Compare commits
57 Commits
Author | SHA1 | Date | |
---|---|---|---|
9358408798 | |||
d01d998a17 | |||
e6d09620e4 | |||
04868ab9da | |||
28598c9090 | |||
11cb4d88f0 | |||
4b870b28e8 | |||
ce7c418e0c | |||
169b198d08 | |||
e32ce6cc69 | |||
190748546d | |||
79e289404b | |||
a6e467cd55 | |||
8a87a41f2d | |||
f02226283e | |||
82568412f6 | |||
1255196731 | |||
9ce5d39829 | |||
4540bcfaf7 | |||
f80edad109 | |||
aa882cdaa8 | |||
738b37e508 | |||
82a105e2f8 | |||
d6a9ed32e6 | |||
e3937fe8ad | |||
c652e42492 | |||
eacec2ae12 | |||
45ea712d39 | |||
f463ef7dae | |||
95c5b715b1 | |||
8bf46f48f8 | |||
9d7671ec3b | |||
5d0a051abb | |||
4c84bce171 | |||
302a735135 | |||
d8eb37fbec | |||
ef7d44e243 | |||
f71140fbd9 | |||
0792139a5f | |||
f1cfd9c219 | |||
4467fd9993 | |||
1462db4694 | |||
44af2e32a4 | |||
3c88547c74 | |||
1fe003113e | |||
cef7aaa8ec | |||
005f0eb083 | |||
b41006004b | |||
408a9cd50d | |||
d2b62ae401 | |||
68f6f2671b | |||
6f7cb69c94 | |||
ad1946b893 | |||
14ea16e499 | |||
22263787e3 | |||
ddcd89afd4 | |||
dfa040502a |
@ -56,7 +56,6 @@ for i in package/*.exe; do
|
||||
x86_64-w64-mingw32-strip "${i}"
|
||||
done
|
||||
|
||||
pip3 install pefile
|
||||
python3 .ci/scripts/windows/scan_dll.py package/*.exe package/imageformats/*.dll "package/"
|
||||
|
||||
# copy FFmpeg libraries
|
||||
|
33
.ci/scripts/windows/install-vulkan-sdk.ps1
Normal file
33
.ci/scripts/windows/install-vulkan-sdk.ps1
Normal file
@ -0,0 +1,33 @@
|
||||
# SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$VulkanSDKVer = "1.3.250.1"
|
||||
$ExeFile = "VulkanSDK-$VulkanSDKVer-Installer.exe"
|
||||
$Uri = "https://sdk.lunarg.com/sdk/download/$VulkanSDKVer/windows/$ExeFile"
|
||||
$Destination = "./$ExeFile"
|
||||
|
||||
echo "Downloading Vulkan SDK $VulkanSDKVer from $Uri"
|
||||
$WebClient = New-Object System.Net.WebClient
|
||||
$WebClient.DownloadFile($Uri, $Destination)
|
||||
echo "Finished downloading $ExeFile"
|
||||
|
||||
$VULKAN_SDK = "C:/VulkanSDK/$VulkanSDKVer"
|
||||
$Arguments = "--root `"$VULKAN_SDK`" --accept-licenses --default-answer --confirm-command install"
|
||||
|
||||
echo "Installing Vulkan SDK $VulkanSDKVer"
|
||||
$InstallProcess = Start-Process -FilePath $Destination -NoNewWindow -PassThru -Wait -ArgumentList $Arguments
|
||||
$ExitCode = $InstallProcess.ExitCode
|
||||
|
||||
if ($ExitCode -ne 0) {
|
||||
echo "Error installing Vulkan SDK $VulkanSDKVer (Error: $ExitCode)"
|
||||
Exit $ExitCode
|
||||
}
|
||||
|
||||
echo "Finished installing Vulkan SDK $VulkanSDKVer"
|
||||
|
||||
if ("$env:GITHUB_ACTIONS" -eq "true") {
|
||||
echo "VULKAN_SDK=$VULKAN_SDK" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
|
||||
echo "$VULKAN_SDK/Bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||
}
|
@ -7,9 +7,12 @@ parameters:
|
||||
version: ''
|
||||
|
||||
steps:
|
||||
- script: choco install vulkan-sdk
|
||||
displayName: 'Install vulkan-sdk'
|
||||
- script: refreshenv && mkdir build && cd build && cmake -E env CXXFLAGS="/Gw /GA /Gr /Ob2" cmake -G "Visual Studio 17 2022" -A x64 -DCMAKE_POLICY_DEFAULT_CMP0069=NEW -DYUZU_ENABLE_LTO=ON -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DYUZU_TESTS=OFF -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DDISPLAY_VERSION=${{ parameters['version'] }} -DCMAKE_BUILD_TYPE=Release -DYUZU_CRASH_DUMPS=ON .. && cd ..
|
||||
- task: Powershell@2
|
||||
displayName: 'Install Vulkan SDK'
|
||||
inputs:
|
||||
targetType: 'filePath'
|
||||
filePath: './.ci/scripts/windows/install-vulkan-sdk.ps1'
|
||||
- script: refreshenv && glslangValidator --version && mkdir build && cd build && cmake -E env CXXFLAGS="/Gw /GA /Gr /Ob2" cmake -G "Visual Studio 17 2022" -A x64 -DCMAKE_POLICY_DEFAULT_CMP0069=NEW -DYUZU_ENABLE_LTO=ON -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DYUZU_TESTS=OFF -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DDISPLAY_VERSION=${{ parameters['version'] }} -DCMAKE_BUILD_TYPE=Release -DYUZU_CRASH_DUMPS=ON .. && cd ..
|
||||
displayName: 'Configure CMake'
|
||||
- task: MSBuild@1
|
||||
displayName: 'Build'
|
||||
|
80
.github/workflows/android-build.yml
vendored
Normal file
80
.github/workflows/android-build.yml
vendored
Normal file
@ -0,0 +1,80 @@
|
||||
# SPDX-FileCopyrightText: 2022 yuzu Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
name: 'yuzu-android-build'
|
||||
|
||||
on:
|
||||
push:
|
||||
tags: [ "*" ]
|
||||
|
||||
jobs:
|
||||
android:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository == 'yuzu-emu/yuzu-android' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'temurin'
|
||||
- name: Set up cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
~/.ccache
|
||||
key: ${{ runner.os }}-android-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-android-
|
||||
- name: Query tag name
|
||||
uses: olegtarasov/get-tag@v2.1.2
|
||||
id: tagName
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y ccache apksigner glslang-dev glslang-tools
|
||||
- name: Build
|
||||
run: ./.ci/scripts/android/build.sh
|
||||
- name: Copy and sign artifacts
|
||||
env:
|
||||
ANDROID_KEYSTORE_B64: ${{ secrets.ANDROID_KEYSTORE_B64 }}
|
||||
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
|
||||
ANDROID_KEYSTORE_PASS: ${{ secrets.ANDROID_KEYSTORE_PASS }}
|
||||
run: ./.ci/scripts/android/upload.sh
|
||||
- name: Upload
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: android
|
||||
path: artifacts/
|
||||
# release steps
|
||||
release-android:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [android]
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/download-artifact@v3
|
||||
- name: Query tag name
|
||||
uses: olegtarasov/get-tag@v2.1.2
|
||||
id: tagName
|
||||
- name: Create release
|
||||
uses: actions/create-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ steps.tagName.outputs.tag }}
|
||||
release_name: ${{ steps.tagName.outputs.tag }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
- name: Upload artifacts
|
||||
uses: alexellis/upload-assets@0.2.3
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
asset_paths: '["./**/*.apk","./**/*.aab"]'
|
218
.github/workflows/android-merge.js
vendored
Normal file
218
.github/workflows/android-merge.js
vendored
Normal file
@ -0,0 +1,218 @@
|
||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
// Note: This is a GitHub Actions script
|
||||
// It is not meant to be executed directly on your machine without modifications
|
||||
|
||||
const fs = require("fs");
|
||||
// which label to check for changes
|
||||
const CHANGE_LABEL = 'android-merge';
|
||||
// how far back in time should we consider the changes are "recent"? (default: 24 hours)
|
||||
const DETECTION_TIME_FRAME = (parseInt(process.env.DETECTION_TIME_FRAME)) || (24 * 3600 * 1000);
|
||||
|
||||
async function checkBaseChanges(github, context) {
|
||||
// query the commit date of the latest commit on this branch
|
||||
const query = `query($owner:String!, $name:String!, $ref:String!) {
|
||||
repository(name:$name, owner:$owner) {
|
||||
ref(qualifiedName:$ref) {
|
||||
target {
|
||||
... on Commit { id pushedDate oid }
|
||||
}
|
||||
}
|
||||
}
|
||||
}`;
|
||||
const variables = {
|
||||
owner: context.repo.owner,
|
||||
name: context.repo.repo,
|
||||
ref: 'refs/heads/master',
|
||||
};
|
||||
const result = await github.graphql(query, variables);
|
||||
const pushedAt = result.repository.ref.target.pushedDate;
|
||||
console.log(`Last commit pushed at ${pushedAt}.`);
|
||||
const delta = new Date() - new Date(pushedAt);
|
||||
if (delta <= DETECTION_TIME_FRAME) {
|
||||
console.info('New changes detected, triggering a new build.');
|
||||
return true;
|
||||
}
|
||||
console.info('No new changes detected.');
|
||||
return false;
|
||||
}
|
||||
|
||||
async function checkAndroidChanges(github, context) {
|
||||
if (checkBaseChanges(github, context)) return true;
|
||||
const query = `query($owner:String!, $name:String!, $label:String!) {
|
||||
repository(name:$name, owner:$owner) {
|
||||
pullRequests(labels: [$label], states: OPEN, first: 100) {
|
||||
nodes { number headRepository { pushedAt } }
|
||||
}
|
||||
}
|
||||
}`;
|
||||
const variables = {
|
||||
owner: context.repo.owner,
|
||||
name: context.repo.repo,
|
||||
label: CHANGE_LABEL,
|
||||
};
|
||||
const result = await github.graphql(query, variables);
|
||||
const pulls = result.repository.pullRequests.nodes;
|
||||
for (let i = 0; i < pulls.length; i++) {
|
||||
let pull = pulls[i];
|
||||
if (new Date() - new Date(pull.headRepository.pushedAt) <= DETECTION_TIME_FRAME) {
|
||||
console.info(`${pull.number} updated at ${pull.headRepository.pushedAt}`);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
console.info("No changes detected in any tagged pull requests.");
|
||||
return false;
|
||||
}
|
||||
|
||||
async function tagAndPush(github, owner, repo, execa, commit=false) {
|
||||
let altToken = process.env.ALT_GITHUB_TOKEN;
|
||||
if (!altToken) {
|
||||
throw `Please set ALT_GITHUB_TOKEN environment variable. This token should have write access to ${owner}/${repo}.`;
|
||||
}
|
||||
const query = `query ($owner:String!, $name:String!) {
|
||||
repository(name:$name, owner:$owner) {
|
||||
refs(refPrefix: "refs/tags/", orderBy: {field: TAG_COMMIT_DATE, direction: DESC}, first: 10) {
|
||||
nodes { name }
|
||||
}
|
||||
}
|
||||
}`;
|
||||
const variables = {
|
||||
owner: owner,
|
||||
name: repo,
|
||||
};
|
||||
const tags = await github.graphql(query, variables);
|
||||
const tagList = tags.repository.refs.nodes;
|
||||
const lastTag = tagList[0] ? tagList[0].name : 'dummy-0';
|
||||
const tagNumber = /\w+-(\d+)/.exec(lastTag)[1] | 0;
|
||||
const channel = repo.split('-')[1];
|
||||
const newTag = `${channel}-${tagNumber + 1}`;
|
||||
console.log(`New tag: ${newTag}`);
|
||||
if (commit) {
|
||||
let channelName = channel[0].toUpperCase() + channel.slice(1);
|
||||
console.info(`Committing pending commit as ${channelName} #${tagNumber + 1}`);
|
||||
await execa("git", ['commit', '-m', `${channelName} #${tagNumber + 1}`]);
|
||||
}
|
||||
console.info('Pushing tags to GitHub ...');
|
||||
await execa("git", ['tag', newTag]);
|
||||
await execa("git", ['remote', 'add', 'target', `https://${altToken}@github.com/${owner}/${repo}.git`]);
|
||||
await execa("git", ['push', 'target', 'master', '-f']);
|
||||
await execa("git", ['push', 'target', 'master', '--tags']);
|
||||
console.info('Successfully pushed new changes.');
|
||||
}
|
||||
|
||||
async function generateReadme(pulls, context, mergeResults, execa) {
|
||||
let baseUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/`;
|
||||
let output =
|
||||
"| Pull Request | Commit | Title | Author | Merged? |\n|----|----|----|----|----|\n";
|
||||
for (let pull of pulls) {
|
||||
let pr = pull.number;
|
||||
let result = mergeResults[pr];
|
||||
output += `| [${pr}](${baseUrl}/pull/${pr}) | [\`${result.rev || "N/A"}\`](${baseUrl}/pull/${pr}/files) | ${pull.title} | [${pull.author.login}](https://github.com/${pull.author.login}/) | ${result.success ? "Yes" : "No"} |\n`;
|
||||
}
|
||||
output +=
|
||||
"\n\nEnd of merge log. You can find the original README.md below the break.\n\n-----\n\n";
|
||||
output += fs.readFileSync("./README.md");
|
||||
fs.writeFileSync("./README.md", output);
|
||||
await execa("git", ["add", "README.md"]);
|
||||
}
|
||||
|
||||
async function fetchPullRequests(pulls, repoUrl, execa) {
|
||||
console.log("::group::Fetch pull requests");
|
||||
for (let pull of pulls) {
|
||||
let pr = pull.number;
|
||||
console.info(`Fetching PR ${pr} ...`);
|
||||
await execa("git", [
|
||||
"fetch",
|
||||
"-f",
|
||||
"--no-recurse-submodules",
|
||||
repoUrl,
|
||||
`pull/${pr}/head:pr-${pr}`,
|
||||
]);
|
||||
}
|
||||
console.log("::endgroup::");
|
||||
}
|
||||
|
||||
async function mergePullRequests(pulls, execa) {
|
||||
let mergeResults = {};
|
||||
console.log("::group::Merge pull requests");
|
||||
await execa("git", ["config", "--global", "user.name", "yuzubot"]);
|
||||
await execa("git", [
|
||||
"config",
|
||||
"--global",
|
||||
"user.email",
|
||||
"yuzu\x40yuzu-emu\x2eorg", // prevent email harvesters from scraping the address
|
||||
]);
|
||||
let hasFailed = false;
|
||||
for (let pull of pulls) {
|
||||
let pr = pull.number;
|
||||
console.info(`Merging PR ${pr} ...`);
|
||||
try {
|
||||
const process1 = execa("git", [
|
||||
"merge",
|
||||
"--squash",
|
||||
"--no-edit",
|
||||
`pr-${pr}`,
|
||||
]);
|
||||
process1.stdout.pipe(process.stdout);
|
||||
await process1;
|
||||
|
||||
const process2 = execa("git", ["commit", "-m", `Merge PR ${pr}`]);
|
||||
process2.stdout.pipe(process.stdout);
|
||||
await process2;
|
||||
|
||||
const process3 = await execa("git", ["rev-parse", "--short", `pr-${pr}`]);
|
||||
mergeResults[pr] = {
|
||||
success: true,
|
||||
rev: process3.stdout,
|
||||
};
|
||||
} catch (err) {
|
||||
console.log(
|
||||
`::error title=#${pr} not merged::Failed to merge pull request: ${pr}: ${err}`
|
||||
);
|
||||
mergeResults[pr] = { success: false };
|
||||
hasFailed = true;
|
||||
await execa("git", ["reset", "--hard"]);
|
||||
}
|
||||
}
|
||||
console.log("::endgroup::");
|
||||
if (hasFailed) {
|
||||
throw 'There are merge failures. Aborting!';
|
||||
}
|
||||
return mergeResults;
|
||||
}
|
||||
|
||||
async function mergebot(github, context, execa) {
|
||||
const query = `query ($owner:String!, $name:String!, $label:String!) {
|
||||
repository(name:$name, owner:$owner) {
|
||||
pullRequests(labels: [$label], states: OPEN, first: 100) {
|
||||
nodes {
|
||||
number title author { login }
|
||||
}
|
||||
}
|
||||
}
|
||||
}`;
|
||||
const variables = {
|
||||
owner: context.repo.owner,
|
||||
name: context.repo.repo,
|
||||
label: CHANGE_LABEL,
|
||||
};
|
||||
const result = await github.graphql(query, variables);
|
||||
const pulls = result.repository.pullRequests.nodes;
|
||||
let displayList = [];
|
||||
for (let i = 0; i < pulls.length; i++) {
|
||||
let pull = pulls[i];
|
||||
displayList.push({ PR: pull.number, Title: pull.title });
|
||||
}
|
||||
console.info("The following pull requests will be merged:");
|
||||
console.table(displayList);
|
||||
await fetchPullRequests(pulls, "https://github.com/yuzu-emu/yuzu", execa);
|
||||
const mergeResults = await mergePullRequests(pulls, execa);
|
||||
await generateReadme(pulls, context, mergeResults, execa);
|
||||
await tagAndPush(github, context.repo.owner, `${context.repo.repo}-android`, execa, true);
|
||||
}
|
||||
|
||||
module.exports.mergebot = mergebot;
|
||||
module.exports.checkAndroidChanges = checkAndroidChanges;
|
||||
module.exports.tagAndPush = tagAndPush;
|
||||
module.exports.checkBaseChanges = checkBaseChanges;
|
57
.github/workflows/android-publish.yml
vendored
Normal file
57
.github/workflows/android-publish.yml
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
# SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
name: yuzu-android-publish
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '37 0 * * *'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
android:
|
||||
description: 'Whether to trigger an Android build (true/false/auto)'
|
||||
required: false
|
||||
default: 'true'
|
||||
|
||||
jobs:
|
||||
android:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event.inputs.android != 'false' && github.repository == 'yuzu-emu/yuzu' }}
|
||||
steps:
|
||||
# this checkout is required to make sure the GitHub Actions scripts are available
|
||||
- uses: actions/checkout@v3
|
||||
name: Pre-checkout
|
||||
with:
|
||||
submodules: false
|
||||
- uses: actions/github-script@v6
|
||||
id: check-changes
|
||||
name: 'Check for new changes'
|
||||
env:
|
||||
# 24 hours
|
||||
DETECTION_TIME_FRAME: 86400000
|
||||
with:
|
||||
script: |
|
||||
if (context.payload.inputs && context.payload.inputs.android === 'true') return true;
|
||||
const checkAndroidChanges = require('./.github/workflows/android-merge.js').checkAndroidChanges;
|
||||
return checkAndroidChanges(github, context);
|
||||
- run: npm install execa@5
|
||||
if: ${{ steps.check-changes.outputs.result == 'true' }}
|
||||
- uses: actions/checkout@v3
|
||||
name: Checkout
|
||||
if: ${{ steps.check-changes.outputs.result == 'true' }}
|
||||
with:
|
||||
path: 'yuzu-merge'
|
||||
fetch-depth: 0
|
||||
submodules: true
|
||||
token: ${{ secrets.ALT_GITHUB_TOKEN }}
|
||||
- uses: actions/github-script@v5
|
||||
name: 'Check and merge Android changes'
|
||||
if: ${{ steps.check-changes.outputs.result == 'true' }}
|
||||
env:
|
||||
ALT_GITHUB_TOKEN: ${{ secrets.ALT_GITHUB_TOKEN }}
|
||||
with:
|
||||
script: |
|
||||
const execa = require("execa");
|
||||
const mergebot = require('./.github/workflows/android-merge.js').mergebot;
|
||||
process.chdir('${{ github.workspace }}/yuzu-merge');
|
||||
mergebot(github, context, execa);
|
34
.github/workflows/verify.yml
vendored
34
.github/workflows/verify.yml
vendored
@ -73,6 +73,10 @@ jobs:
|
||||
needs: format
|
||||
runs-on: windows-2022
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
- name: Set up cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
@ -81,22 +85,22 @@ jobs:
|
||||
restore-keys: |
|
||||
${{ runner.os }}-msvc-
|
||||
- name: Install dependencies
|
||||
# due to how chocolatey works, only cmd.exe is supported here
|
||||
shell: cmd
|
||||
shell: pwsh
|
||||
run: |
|
||||
choco install vulkan-sdk wget
|
||||
call refreshenv
|
||||
wget https://github.com/mbitsnbites/buildcache/releases/download/v0.27.6/buildcache-windows.zip
|
||||
7z x buildcache-windows.zip
|
||||
copy buildcache\bin\buildcache.exe C:\ProgramData\chocolatey\bin
|
||||
rmdir buildcache
|
||||
echo %PATH% >> %GITHUB_PATH%
|
||||
$ErrorActionPreference = "Stop"
|
||||
$BuildCacheVer = "v0.28.4"
|
||||
$File = "buildcache-windows.zip"
|
||||
$Uri = "https://github.com/mbitsnbites/buildcache/releases/download/$BuildCacheVer/$File"
|
||||
$WebClient = New-Object System.Net.WebClient
|
||||
$WebClient.DownloadFile($Uri, $File)
|
||||
7z x $File
|
||||
$CurrentDir = Convert-Path .
|
||||
echo "$CurrentDir/buildcache/bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||
- name: Install Vulkan SDK
|
||||
shell: pwsh
|
||||
run: .\.ci\scripts\windows\install-vulkan-sdk.ps1
|
||||
- name: Set up MSVC
|
||||
uses: ilammy/msvc-dev-cmd@v1
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
- name: Configure
|
||||
env:
|
||||
CC: cl.exe
|
||||
@ -129,11 +133,12 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
- name: set up JDK 17
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'adopt'
|
||||
distribution: 'temurin'
|
||||
- name: Set up cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
@ -151,7 +156,6 @@ jobs:
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y ccache apksigner glslang-dev glslang-tools
|
||||
git -C ./externals/vcpkg/ fetch --all --unshallow
|
||||
- name: Build
|
||||
run: ./.ci/scripts/android/build.sh
|
||||
- name: Copy and sign artifacts
|
||||
|
@ -285,7 +285,7 @@ find_package(ZLIB 1.2 REQUIRED)
|
||||
find_package(zstd 1.5 REQUIRED)
|
||||
|
||||
if (NOT YUZU_USE_EXTERNAL_VULKAN_HEADERS)
|
||||
find_package(Vulkan 1.3.246 REQUIRED)
|
||||
find_package(Vulkan 1.3.256 REQUIRED)
|
||||
endif()
|
||||
|
||||
if (ENABLE_LIBUSB)
|
||||
@ -489,7 +489,7 @@ if (ENABLE_SDL2)
|
||||
if (YUZU_USE_BUNDLED_SDL2)
|
||||
# Detect toolchain and platform
|
||||
if ((MSVC_VERSION GREATER_EQUAL 1920 AND MSVC_VERSION LESS 1940) AND ARCHITECTURE_x86_64)
|
||||
set(SDL2_VER "SDL2-2.28.0")
|
||||
set(SDL2_VER "SDL2-2.28.1")
|
||||
else()
|
||||
message(FATAL_ERROR "No bundled SDL2 binaries for your toolchain. Disable YUZU_USE_BUNDLED_SDL2 and provide your own.")
|
||||
endif()
|
||||
|
2
externals/SDL
vendored
2
externals/SDL
vendored
Submodule externals/SDL updated: 491fba1d06...116a5344ff
2
externals/Vulkan-Headers
vendored
2
externals/Vulkan-Headers
vendored
Submodule externals/Vulkan-Headers updated: 63af1cf1ee...ed857118e2
@ -22,7 +22,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
android:label="@string/app_name_suffixed"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:allowBackup="true"
|
||||
android:hasFragileUserData="true"
|
||||
android:hasFragileUserData="false"
|
||||
android:supportsRtl="true"
|
||||
android:isGame="true"
|
||||
android:localeConfig="@xml/locales_config"
|
||||
@ -54,6 +54,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
<activity
|
||||
android:name="org.yuzu.yuzu_emu.activities.EmulationActivity"
|
||||
android:theme="@style/Theme.Yuzu.Main"
|
||||
android:launchMode="singleTop"
|
||||
android:screenOrientation="userLandscape"
|
||||
android:supportsPictureInPicture="true"
|
||||
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|uiMode"
|
||||
|
@ -12,6 +12,7 @@ import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.databinding.CardHomeOptionBinding
|
||||
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
|
||||
import org.yuzu.yuzu_emu.model.HomeSetting
|
||||
|
||||
class HomeSettingAdapter(private val activity: AppCompatActivity, var options: List<HomeSetting>) :
|
||||
@ -34,7 +35,14 @@ class HomeSettingAdapter(private val activity: AppCompatActivity, var options: L
|
||||
|
||||
override fun onClick(view: View) {
|
||||
val holder = view.tag as HomeOptionViewHolder
|
||||
holder.option.onClick.invoke()
|
||||
if (holder.option.isEnabled.invoke()) {
|
||||
holder.option.onClick.invoke()
|
||||
} else {
|
||||
MessageDialogFragment.newInstance(
|
||||
holder.option.disabledTitleId,
|
||||
holder.option.disabledMessageId
|
||||
).show(activity.supportFragmentManager, MessageDialogFragment.TAG)
|
||||
}
|
||||
}
|
||||
|
||||
inner class HomeOptionViewHolder(val binding: CardHomeOptionBinding) :
|
||||
@ -65,6 +73,12 @@ class HomeSettingAdapter(private val activity: AppCompatActivity, var options: L
|
||||
R.drawable.premium_background
|
||||
)
|
||||
}
|
||||
|
||||
if (!option.isEnabled.invoke()) {
|
||||
binding.optionTitle.alpha = 0.5f
|
||||
binding.optionDescription.alpha = 0.5f
|
||||
binding.optionIcon.alpha = 0.5f
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -112,25 +112,36 @@ class Settings {
|
||||
|
||||
const val PREF_MEMORY_WARNING_SHOWN = "MemoryWarningShown"
|
||||
|
||||
const val PREF_OVERLAY_INIT = "OverlayInit"
|
||||
const val PREF_OVERLAY_VERSION = "OverlayVersion"
|
||||
const val PREF_LANDSCAPE_OVERLAY_VERSION = "LandscapeOverlayVersion"
|
||||
const val PREF_PORTRAIT_OVERLAY_VERSION = "PortraitOverlayVersion"
|
||||
const val PREF_FOLDABLE_OVERLAY_VERSION = "FoldableOverlayVersion"
|
||||
val overlayLayoutPrefs = listOf(
|
||||
PREF_LANDSCAPE_OVERLAY_VERSION,
|
||||
PREF_PORTRAIT_OVERLAY_VERSION,
|
||||
PREF_FOLDABLE_OVERLAY_VERSION
|
||||
)
|
||||
|
||||
const val PREF_CONTROL_SCALE = "controlScale"
|
||||
const val PREF_CONTROL_OPACITY = "controlOpacity"
|
||||
const val PREF_TOUCH_ENABLED = "isTouchEnabled"
|
||||
const val PREF_BUTTON_TOGGLE_0 = "buttonToggle0"
|
||||
const val PREF_BUTTON_TOGGLE_1 = "buttonToggle1"
|
||||
const val PREF_BUTTON_TOGGLE_2 = "buttonToggle2"
|
||||
const val PREF_BUTTON_TOGGLE_3 = "buttonToggle3"
|
||||
const val PREF_BUTTON_TOGGLE_4 = "buttonToggle4"
|
||||
const val PREF_BUTTON_TOGGLE_5 = "buttonToggle5"
|
||||
const val PREF_BUTTON_TOGGLE_6 = "buttonToggle6"
|
||||
const val PREF_BUTTON_TOGGLE_7 = "buttonToggle7"
|
||||
const val PREF_BUTTON_TOGGLE_8 = "buttonToggle8"
|
||||
const val PREF_BUTTON_TOGGLE_9 = "buttonToggle9"
|
||||
const val PREF_BUTTON_TOGGLE_10 = "buttonToggle10"
|
||||
const val PREF_BUTTON_TOGGLE_11 = "buttonToggle11"
|
||||
const val PREF_BUTTON_TOGGLE_12 = "buttonToggle12"
|
||||
const val PREF_BUTTON_TOGGLE_13 = "buttonToggle13"
|
||||
const val PREF_BUTTON_TOGGLE_14 = "buttonToggle14"
|
||||
const val PREF_BUTTON_A = "buttonToggle0"
|
||||
const val PREF_BUTTON_B = "buttonToggle1"
|
||||
const val PREF_BUTTON_X = "buttonToggle2"
|
||||
const val PREF_BUTTON_Y = "buttonToggle3"
|
||||
const val PREF_BUTTON_L = "buttonToggle4"
|
||||
const val PREF_BUTTON_R = "buttonToggle5"
|
||||
const val PREF_BUTTON_ZL = "buttonToggle6"
|
||||
const val PREF_BUTTON_ZR = "buttonToggle7"
|
||||
const val PREF_BUTTON_PLUS = "buttonToggle8"
|
||||
const val PREF_BUTTON_MINUS = "buttonToggle9"
|
||||
const val PREF_BUTTON_DPAD = "buttonToggle10"
|
||||
const val PREF_STICK_L = "buttonToggle11"
|
||||
const val PREF_STICK_R = "buttonToggle12"
|
||||
const val PREF_BUTTON_STICK_L = "buttonToggle13"
|
||||
const val PREF_BUTTON_STICK_R = "buttonToggle14"
|
||||
const val PREF_BUTTON_HOME = "buttonToggle15"
|
||||
const val PREF_BUTTON_SCREENSHOT = "buttonToggle16"
|
||||
|
||||
const val PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER = "EmulationMenuSettings_JoystickRelCenter"
|
||||
const val PREF_MENU_SETTINGS_DPAD_SLIDE = "EmulationMenuSettings_DpadSlideEnable"
|
||||
@ -145,6 +156,30 @@ class Settings {
|
||||
|
||||
private val configFileSectionsMap: MutableMap<String, List<String>> = HashMap()
|
||||
|
||||
val overlayPreferences = listOf(
|
||||
PREF_OVERLAY_VERSION,
|
||||
PREF_CONTROL_SCALE,
|
||||
PREF_CONTROL_OPACITY,
|
||||
PREF_TOUCH_ENABLED,
|
||||
PREF_BUTTON_A,
|
||||
PREF_BUTTON_B,
|
||||
PREF_BUTTON_X,
|
||||
PREF_BUTTON_Y,
|
||||
PREF_BUTTON_L,
|
||||
PREF_BUTTON_R,
|
||||
PREF_BUTTON_ZL,
|
||||
PREF_BUTTON_ZR,
|
||||
PREF_BUTTON_PLUS,
|
||||
PREF_BUTTON_MINUS,
|
||||
PREF_BUTTON_DPAD,
|
||||
PREF_STICK_L,
|
||||
PREF_STICK_R,
|
||||
PREF_BUTTON_HOME,
|
||||
PREF_BUTTON_SCREENSHOT,
|
||||
PREF_BUTTON_STICK_L,
|
||||
PREF_BUTTON_STICK_R
|
||||
)
|
||||
|
||||
const val LayoutOption_Unspecified = 0
|
||||
const val LayoutOption_MobilePortrait = 4
|
||||
const val LayoutOption_MobileLandscape = 5
|
||||
|
@ -212,9 +212,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
}
|
||||
if (!isInFoldableLayout) {
|
||||
if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
|
||||
binding.surfaceInputOverlay.orientation = InputOverlay.PORTRAIT
|
||||
binding.surfaceInputOverlay.layout = InputOverlay.PORTRAIT
|
||||
} else {
|
||||
binding.surfaceInputOverlay.orientation = InputOverlay.LANDSCAPE
|
||||
binding.surfaceInputOverlay.layout = InputOverlay.LANDSCAPE
|
||||
}
|
||||
}
|
||||
if (!binding.surfaceInputOverlay.isInEditMode) {
|
||||
@ -260,7 +260,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
.remove(Settings.PREF_CONTROL_SCALE)
|
||||
.remove(Settings.PREF_CONTROL_OPACITY)
|
||||
.apply()
|
||||
binding.surfaceInputOverlay.post { binding.surfaceInputOverlay.resetButtonPlacement() }
|
||||
binding.surfaceInputOverlay.post {
|
||||
binding.surfaceInputOverlay.resetLayoutVisibilityAndPlacement()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateShowFpsOverlay() {
|
||||
@ -337,7 +339,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
binding.inGameMenu.layoutParams.height = it.bounds.bottom
|
||||
|
||||
isInFoldableLayout = true
|
||||
binding.surfaceInputOverlay.orientation = InputOverlay.FOLDABLE
|
||||
binding.surfaceInputOverlay.layout = InputOverlay.FOLDABLE
|
||||
refreshInputOverlay()
|
||||
}
|
||||
}
|
||||
@ -410,9 +412,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
R.id.menu_toggle_controls -> {
|
||||
val preferences =
|
||||
PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
|
||||
val optionsArray = BooleanArray(15)
|
||||
for (i in 0..14) {
|
||||
optionsArray[i] = preferences.getBoolean("buttonToggle$i", i < 13)
|
||||
val optionsArray = BooleanArray(Settings.overlayPreferences.size)
|
||||
Settings.overlayPreferences.forEachIndexed { i, _ ->
|
||||
optionsArray[i] = preferences.getBoolean("buttonToggle$i", i < 15)
|
||||
}
|
||||
|
||||
val dialog = MaterialAlertDialogBuilder(requireContext())
|
||||
@ -436,7 +438,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
dialog.getButton(AlertDialog.BUTTON_NEUTRAL)
|
||||
.setOnClickListener {
|
||||
val isChecked = !optionsArray[0]
|
||||
for (i in 0..14) {
|
||||
Settings.overlayPreferences.forEachIndexed { i, _ ->
|
||||
optionsArray[i] = isChecked
|
||||
dialog.listView.setItemChecked(i, isChecked)
|
||||
preferences.edit()
|
||||
|
@ -73,102 +73,113 @@ class HomeSettingsFragment : Fragment() {
|
||||
HomeSetting(
|
||||
R.string.advanced_settings,
|
||||
R.string.settings_description,
|
||||
R.drawable.ic_settings
|
||||
) { SettingsActivity.launch(requireContext(), SettingsFile.FILE_NAME_CONFIG, "") }
|
||||
R.drawable.ic_settings,
|
||||
{ SettingsActivity.launch(requireContext(), SettingsFile.FILE_NAME_CONFIG, "") }
|
||||
)
|
||||
)
|
||||
add(
|
||||
HomeSetting(
|
||||
R.string.open_user_folder,
|
||||
R.string.open_user_folder_description,
|
||||
R.drawable.ic_folder_open
|
||||
) { openFileManager() }
|
||||
R.drawable.ic_folder_open,
|
||||
{ openFileManager() }
|
||||
)
|
||||
)
|
||||
add(
|
||||
HomeSetting(
|
||||
R.string.preferences_theme,
|
||||
R.string.theme_and_color_description,
|
||||
R.drawable.ic_palette
|
||||
) { SettingsActivity.launch(requireContext(), Settings.SECTION_THEME, "") }
|
||||
)
|
||||
|
||||
if (GpuDriverHelper.supportsCustomDriverLoading()) {
|
||||
add(
|
||||
HomeSetting(
|
||||
R.string.install_gpu_driver,
|
||||
R.string.install_gpu_driver_description,
|
||||
R.drawable.ic_exit
|
||||
) { driverInstaller() }
|
||||
R.drawable.ic_palette,
|
||||
{ SettingsActivity.launch(requireContext(), Settings.SECTION_THEME, "") }
|
||||
)
|
||||
}
|
||||
|
||||
)
|
||||
add(
|
||||
HomeSetting(
|
||||
R.string.install_gpu_driver,
|
||||
R.string.install_gpu_driver_description,
|
||||
R.drawable.ic_exit,
|
||||
{ driverInstaller() },
|
||||
{ GpuDriverHelper.supportsCustomDriverLoading() },
|
||||
R.string.custom_driver_not_supported,
|
||||
R.string.custom_driver_not_supported_description
|
||||
)
|
||||
)
|
||||
add(
|
||||
HomeSetting(
|
||||
R.string.install_amiibo_keys,
|
||||
R.string.install_amiibo_keys_description,
|
||||
R.drawable.ic_nfc
|
||||
) { mainActivity.getAmiiboKey.launch(arrayOf("*/*")) }
|
||||
R.drawable.ic_nfc,
|
||||
{ mainActivity.getAmiiboKey.launch(arrayOf("*/*")) }
|
||||
)
|
||||
)
|
||||
add(
|
||||
HomeSetting(
|
||||
R.string.install_game_content,
|
||||
R.string.install_game_content_description,
|
||||
R.drawable.ic_system_update_alt
|
||||
) { mainActivity.installGameUpdate.launch(arrayOf("*/*")) }
|
||||
R.drawable.ic_system_update_alt,
|
||||
{ mainActivity.installGameUpdate.launch(arrayOf("*/*")) }
|
||||
)
|
||||
)
|
||||
add(
|
||||
HomeSetting(
|
||||
R.string.select_games_folder,
|
||||
R.string.select_games_folder_description,
|
||||
R.drawable.ic_add
|
||||
) {
|
||||
mainActivity.getGamesDirectory.launch(
|
||||
Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data
|
||||
)
|
||||
}
|
||||
R.drawable.ic_add,
|
||||
{
|
||||
mainActivity.getGamesDirectory.launch(
|
||||
Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
add(
|
||||
HomeSetting(
|
||||
R.string.manage_save_data,
|
||||
R.string.import_export_saves_description,
|
||||
R.drawable.ic_save
|
||||
) {
|
||||
ImportExportSavesFragment().show(
|
||||
parentFragmentManager,
|
||||
ImportExportSavesFragment.TAG
|
||||
)
|
||||
}
|
||||
R.drawable.ic_save,
|
||||
{
|
||||
ImportExportSavesFragment().show(
|
||||
parentFragmentManager,
|
||||
ImportExportSavesFragment.TAG
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
add(
|
||||
HomeSetting(
|
||||
R.string.install_prod_keys,
|
||||
R.string.install_prod_keys_description,
|
||||
R.drawable.ic_unlock
|
||||
) { mainActivity.getProdKey.launch(arrayOf("*/*")) }
|
||||
R.drawable.ic_unlock,
|
||||
{ mainActivity.getProdKey.launch(arrayOf("*/*")) }
|
||||
)
|
||||
)
|
||||
add(
|
||||
HomeSetting(
|
||||
R.string.install_firmware,
|
||||
R.string.install_firmware_description,
|
||||
R.drawable.ic_firmware
|
||||
) { mainActivity.getFirmware.launch(arrayOf("application/zip")) }
|
||||
R.drawable.ic_firmware,
|
||||
{ mainActivity.getFirmware.launch(arrayOf("application/zip")) }
|
||||
)
|
||||
)
|
||||
add(
|
||||
HomeSetting(
|
||||
R.string.share_log,
|
||||
R.string.share_log_description,
|
||||
R.drawable.ic_log
|
||||
) { shareLog() }
|
||||
R.drawable.ic_log,
|
||||
{ shareLog() }
|
||||
)
|
||||
)
|
||||
add(
|
||||
HomeSetting(
|
||||
R.string.about,
|
||||
R.string.about_description,
|
||||
R.drawable.ic_info_outline
|
||||
) {
|
||||
exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
|
||||
parentFragmentManager.primaryNavigationFragment?.findNavController()
|
||||
?.navigate(R.id.action_homeSettingsFragment_to_aboutFragment)
|
||||
}
|
||||
R.drawable.ic_info_outline,
|
||||
{
|
||||
exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
|
||||
parentFragmentManager.primaryNavigationFragment?.findNavController()
|
||||
?.navigate(R.id.action_homeSettingsFragment_to_aboutFragment)
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@ -178,12 +189,13 @@ class HomeSettingsFragment : Fragment() {
|
||||
HomeSetting(
|
||||
R.string.get_early_access,
|
||||
R.string.get_early_access_description,
|
||||
R.drawable.ic_diamond
|
||||
) {
|
||||
exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
|
||||
parentFragmentManager.primaryNavigationFragment?.findNavController()
|
||||
?.navigate(R.id.action_homeSettingsFragment_to_earlyAccessFragment)
|
||||
}
|
||||
R.drawable.ic_diamond,
|
||||
{
|
||||
exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
|
||||
parentFragmentManager.primaryNavigationFragment?.findNavController()
|
||||
?.navigate(R.id.action_homeSettingsFragment_to_earlyAccessFragment)
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -7,5 +7,8 @@ data class HomeSetting(
|
||||
val titleId: Int,
|
||||
val descriptionId: Int,
|
||||
val iconId: Int,
|
||||
val onClick: () -> Unit
|
||||
val onClick: () -> Unit,
|
||||
val isEnabled: () -> Boolean = { true },
|
||||
val disabledTitleId: Int = 0,
|
||||
val disabledMessageId: Int = 0
|
||||
)
|
||||
|
@ -51,15 +51,23 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
|
||||
|
||||
private lateinit var windowInsets: WindowInsets
|
||||
|
||||
var orientation = LANDSCAPE
|
||||
var layout = LANDSCAPE
|
||||
|
||||
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
||||
super.onLayout(changed, left, top, right, bottom)
|
||||
|
||||
windowInsets = rootWindowInsets
|
||||
|
||||
if (!preferences.getBoolean("${Settings.PREF_OVERLAY_INIT}$orientation", false)) {
|
||||
defaultOverlay()
|
||||
val overlayVersion = preferences.getInt(Settings.PREF_OVERLAY_VERSION, 0)
|
||||
if (overlayVersion != OVERLAY_VERSION) {
|
||||
resetAllLayouts()
|
||||
} else {
|
||||
val layoutIndex = overlayLayouts.indexOf(layout)
|
||||
val currentLayoutVersion =
|
||||
preferences.getInt(Settings.overlayLayoutPrefs[layoutIndex], 0)
|
||||
if (currentLayoutVersion != overlayLayoutVersions[layoutIndex]) {
|
||||
resetCurrentLayout()
|
||||
}
|
||||
}
|
||||
|
||||
// Load the controls.
|
||||
@ -266,10 +274,10 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
|
||||
MotionEvent.ACTION_POINTER_UP -> if (buttonBeingConfigured === button) {
|
||||
// Persist button position by saving new place.
|
||||
saveControlPosition(
|
||||
buttonBeingConfigured!!.buttonId,
|
||||
buttonBeingConfigured!!.prefId,
|
||||
buttonBeingConfigured!!.bounds.centerX(),
|
||||
buttonBeingConfigured!!.bounds.centerY(),
|
||||
orientation
|
||||
layout
|
||||
)
|
||||
buttonBeingConfigured = null
|
||||
}
|
||||
@ -299,10 +307,10 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
|
||||
MotionEvent.ACTION_POINTER_UP -> if (dpadBeingConfigured === dpad) {
|
||||
// Persist button position by saving new place.
|
||||
saveControlPosition(
|
||||
dpadBeingConfigured!!.upId,
|
||||
Settings.PREF_BUTTON_DPAD,
|
||||
dpadBeingConfigured!!.bounds.centerX(),
|
||||
dpadBeingConfigured!!.bounds.centerY(),
|
||||
orientation
|
||||
layout
|
||||
)
|
||||
dpadBeingConfigured = null
|
||||
}
|
||||
@ -330,10 +338,10 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
|
||||
MotionEvent.ACTION_UP,
|
||||
MotionEvent.ACTION_POINTER_UP -> if (joystickBeingConfigured != null) {
|
||||
saveControlPosition(
|
||||
joystickBeingConfigured!!.buttonId,
|
||||
joystickBeingConfigured!!.prefId,
|
||||
joystickBeingConfigured!!.bounds.centerX(),
|
||||
joystickBeingConfigured!!.bounds.centerY(),
|
||||
orientation
|
||||
layout
|
||||
)
|
||||
joystickBeingConfigured = null
|
||||
}
|
||||
@ -343,9 +351,9 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
|
||||
return true
|
||||
}
|
||||
|
||||
private fun addOverlayControls(orientation: String) {
|
||||
private fun addOverlayControls(layout: String) {
|
||||
val windowSize = getSafeScreenSize(context)
|
||||
if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_0, true)) {
|
||||
if (preferences.getBoolean(Settings.PREF_BUTTON_A, true)) {
|
||||
overlayButtons.add(
|
||||
initializeOverlayButton(
|
||||
context,
|
||||
@ -353,11 +361,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
|
||||
R.drawable.facebutton_a,
|
||||
R.drawable.facebutton_a_depressed,
|
||||
ButtonType.BUTTON_A,
|
||||
orientation
|
||||
Settings.PREF_BUTTON_A,
|
||||
layout
|
||||
)
|
||||
)
|
||||
}
|
||||
if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_1, true)) {
|
||||
if (preferences.getBoolean(Settings.PREF_BUTTON_B, true)) {
|
||||
overlayButtons.add(
|
||||
initializeOverlayButton(
|
||||
context,
|
||||
@ -365,11 +374,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
|
||||
R.drawable.facebutton_b,
|
||||
R.drawable.facebutton_b_depressed,
|
||||
ButtonType.BUTTON_B,
|
||||
orientation
|
||||
Settings.PREF_BUTTON_B,
|
||||
layout
|
||||
)
|
||||
)
|
||||
}
|
||||
if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_2, true)) {
|
||||
if (preferences.getBoolean(Settings.PREF_BUTTON_X, true)) {
|
||||
overlayButtons.add(
|
||||
initializeOverlayButton(
|
||||
context,
|
||||
@ -377,11 +387,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
|
||||
R.drawable.facebutton_x,
|
||||
R.drawable.facebutton_x_depressed,
|
||||
ButtonType.BUTTON_X,
|
||||
orientation
|
||||
Settings.PREF_BUTTON_X,
|
||||
layout
|
||||
)
|
||||
)
|
||||
}
|
||||
if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_3, true)) {
|
||||
if (preferences.getBoolean(Settings.PREF_BUTTON_Y, true)) {
|
||||
overlayButtons.add(
|
||||
initializeOverlayButton(
|
||||
context,
|
||||
@ -389,11 +400,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
|
||||
R.drawable.facebutton_y,
|
||||
R.drawable.facebutton_y_depressed,
|
||||
ButtonType.BUTTON_Y,
|
||||
orientation
|
||||
Settings.PREF_BUTTON_Y,
|
||||
layout
|
||||
)
|
||||
)
|
||||
}
|
||||
if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_4, true)) {
|
||||
if (preferences.getBoolean(Settings.PREF_BUTTON_L, true)) {
|
||||
overlayButtons.add(
|
||||
initializeOverlayButton(
|
||||
context,
|
||||
@ -401,11 +413,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
|
||||
R.drawable.l_shoulder,
|
||||
R.drawable.l_shoulder_depressed,
|
||||
ButtonType.TRIGGER_L,
|
||||
orientation
|
||||
Settings.PREF_BUTTON_L,
|
||||
layout
|
||||
)
|
||||
)
|
||||
}
|
||||
if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_5, true)) {
|
||||
if (preferences.getBoolean(Settings.PREF_BUTTON_R, true)) {
|
||||
overlayButtons.add(
|
||||
initializeOverlayButton(
|
||||
context,
|
||||
@ -413,11 +426,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
|
||||
R.drawable.r_shoulder,
|
||||
R.drawable.r_shoulder_depressed,
|
||||
ButtonType.TRIGGER_R,
|
||||
orientation
|
||||
Settings.PREF_BUTTON_R,
|
||||
layout
|
||||
)
|
||||
)
|
||||
}
|
||||
if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_6, true)) {
|
||||
if (preferences.getBoolean(Settings.PREF_BUTTON_ZL, true)) {
|
||||
overlayButtons.add(
|
||||
initializeOverlayButton(
|
||||
context,
|
||||
@ -425,11 +439,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
|
||||
R.drawable.zl_trigger,
|
||||
R.drawable.zl_trigger_depressed,
|
||||
ButtonType.TRIGGER_ZL,
|
||||
orientation
|
||||
Settings.PREF_BUTTON_ZL,
|
||||
layout
|
||||
)
|
||||
)
|
||||
}
|
||||
if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_7, true)) {
|
||||
if (preferences.getBoolean(Settings.PREF_BUTTON_ZR, true)) {
|
||||
overlayButtons.add(
|
||||
initializeOverlayButton(
|
||||
context,
|
||||
@ -437,11 +452,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
|
||||
R.drawable.zr_trigger,
|
||||
R.drawable.zr_trigger_depressed,
|
||||
ButtonType.TRIGGER_ZR,
|
||||
orientation
|
||||
Settings.PREF_BUTTON_ZR,
|
||||
layout
|
||||
)
|
||||
)
|
||||
}
|
||||
if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_8, true)) {
|
||||
if (preferences.getBoolean(Settings.PREF_BUTTON_PLUS, true)) {
|
||||
overlayButtons.add(
|
||||
initializeOverlayButton(
|
||||
context,
|
||||
@ -449,11 +465,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
|
||||
R.drawable.facebutton_plus,
|
||||
R.drawable.facebutton_plus_depressed,
|
||||
ButtonType.BUTTON_PLUS,
|
||||
orientation
|
||||
Settings.PREF_BUTTON_PLUS,
|
||||
layout
|
||||
)
|
||||
)
|
||||
}
|
||||
if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_9, true)) {
|
||||
if (preferences.getBoolean(Settings.PREF_BUTTON_MINUS, true)) {
|
||||
overlayButtons.add(
|
||||
initializeOverlayButton(
|
||||
context,
|
||||
@ -461,11 +478,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
|
||||
R.drawable.facebutton_minus,
|
||||
R.drawable.facebutton_minus_depressed,
|
||||
ButtonType.BUTTON_MINUS,
|
||||
orientation
|
||||
Settings.PREF_BUTTON_MINUS,
|
||||
layout
|
||||
)
|
||||
)
|
||||
}
|
||||
if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_10, true)) {
|
||||
if (preferences.getBoolean(Settings.PREF_BUTTON_DPAD, true)) {
|
||||
overlayDpads.add(
|
||||
initializeOverlayDpad(
|
||||
context,
|
||||
@ -473,11 +491,11 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
|
||||
R.drawable.dpad_standard,
|
||||
R.drawable.dpad_standard_cardinal_depressed,
|
||||
R.drawable.dpad_standard_diagonal_depressed,
|
||||
orientation
|
||||
layout
|
||||
)
|
||||
)
|
||||
}
|
||||
if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_11, true)) {
|
||||
if (preferences.getBoolean(Settings.PREF_STICK_L, true)) {
|
||||
overlayJoysticks.add(
|
||||
initializeOverlayJoystick(
|
||||
context,
|
||||
@ -487,11 +505,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
|
||||
R.drawable.joystick_depressed,
|
||||
StickType.STICK_L,
|
||||
ButtonType.STICK_L,
|
||||
orientation
|
||||
Settings.PREF_STICK_L,
|
||||
layout
|
||||
)
|
||||
)
|
||||
}
|
||||
if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_12, true)) {
|
||||
if (preferences.getBoolean(Settings.PREF_STICK_R, true)) {
|
||||
overlayJoysticks.add(
|
||||
initializeOverlayJoystick(
|
||||
context,
|
||||
@ -501,11 +520,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
|
||||
R.drawable.joystick_depressed,
|
||||
StickType.STICK_R,
|
||||
ButtonType.STICK_R,
|
||||
orientation
|
||||
Settings.PREF_STICK_R,
|
||||
layout
|
||||
)
|
||||
)
|
||||
}
|
||||
if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_13, false)) {
|
||||
if (preferences.getBoolean(Settings.PREF_BUTTON_HOME, false)) {
|
||||
overlayButtons.add(
|
||||
initializeOverlayButton(
|
||||
context,
|
||||
@ -513,11 +533,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
|
||||
R.drawable.facebutton_home,
|
||||
R.drawable.facebutton_home_depressed,
|
||||
ButtonType.BUTTON_HOME,
|
||||
orientation
|
||||
Settings.PREF_BUTTON_HOME,
|
||||
layout
|
||||
)
|
||||
)
|
||||
}
|
||||
if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_14, false)) {
|
||||
if (preferences.getBoolean(Settings.PREF_BUTTON_SCREENSHOT, false)) {
|
||||
overlayButtons.add(
|
||||
initializeOverlayButton(
|
||||
context,
|
||||
@ -525,7 +546,34 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
|
||||
R.drawable.facebutton_screenshot,
|
||||
R.drawable.facebutton_screenshot_depressed,
|
||||
ButtonType.BUTTON_CAPTURE,
|
||||
orientation
|
||||
Settings.PREF_BUTTON_SCREENSHOT,
|
||||
layout
|
||||
)
|
||||
)
|
||||
}
|
||||
if (preferences.getBoolean(Settings.PREF_BUTTON_STICK_L, true)) {
|
||||
overlayButtons.add(
|
||||
initializeOverlayButton(
|
||||
context,
|
||||
windowSize,
|
||||
R.drawable.button_l3,
|
||||
R.drawable.button_l3_depressed,
|
||||
ButtonType.STICK_L,
|
||||
Settings.PREF_BUTTON_STICK_L,
|
||||
layout
|
||||
)
|
||||
)
|
||||
}
|
||||
if (preferences.getBoolean(Settings.PREF_BUTTON_STICK_R, true)) {
|
||||
overlayButtons.add(
|
||||
initializeOverlayButton(
|
||||
context,
|
||||
windowSize,
|
||||
R.drawable.button_r3,
|
||||
R.drawable.button_r3_depressed,
|
||||
ButtonType.STICK_R,
|
||||
Settings.PREF_BUTTON_STICK_R,
|
||||
layout
|
||||
)
|
||||
)
|
||||
}
|
||||
@ -539,18 +587,18 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
|
||||
|
||||
// Add all the enabled overlay items back to the HashSet.
|
||||
if (EmulationMenuSettings.showOverlay) {
|
||||
addOverlayControls(orientation)
|
||||
addOverlayControls(layout)
|
||||
}
|
||||
invalidate()
|
||||
}
|
||||
|
||||
private fun saveControlPosition(sharedPrefsId: Int, x: Int, y: Int, orientation: String) {
|
||||
private fun saveControlPosition(prefId: String, x: Int, y: Int, layout: String) {
|
||||
val windowSize = getSafeScreenSize(context)
|
||||
val min = windowSize.first
|
||||
val max = windowSize.second
|
||||
PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext).edit()
|
||||
.putFloat("$sharedPrefsId-X$orientation", (x - min.x).toFloat() / max.x)
|
||||
.putFloat("$sharedPrefsId-Y$orientation", (y - min.y).toFloat() / max.y)
|
||||
.putFloat("$prefId-X$layout", (x - min.x).toFloat() / max.x)
|
||||
.putFloat("$prefId-Y$layout", (y - min.y).toFloat() / max.y)
|
||||
.apply()
|
||||
}
|
||||
|
||||
@ -558,19 +606,31 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
|
||||
inEditMode = editMode
|
||||
}
|
||||
|
||||
private fun defaultOverlay() {
|
||||
if (!preferences.getBoolean("${Settings.PREF_OVERLAY_INIT}$orientation", false)) {
|
||||
defaultOverlayByLayout(orientation)
|
||||
}
|
||||
|
||||
resetButtonPlacement()
|
||||
private fun resetCurrentLayout() {
|
||||
defaultOverlayByLayout(layout)
|
||||
val layoutIndex = overlayLayouts.indexOf(layout)
|
||||
preferences.edit()
|
||||
.putBoolean("${Settings.PREF_OVERLAY_INIT}$orientation", true)
|
||||
.putInt(Settings.overlayLayoutPrefs[layoutIndex], overlayLayoutVersions[layoutIndex])
|
||||
.apply()
|
||||
}
|
||||
|
||||
fun resetButtonPlacement() {
|
||||
defaultOverlayByLayout(orientation)
|
||||
private fun resetAllLayouts() {
|
||||
val editor = preferences.edit()
|
||||
overlayLayouts.forEachIndexed { i, layout ->
|
||||
defaultOverlayByLayout(layout)
|
||||
editor.putInt(Settings.overlayLayoutPrefs[i], overlayLayoutVersions[i])
|
||||
}
|
||||
editor.putInt(Settings.PREF_OVERLAY_VERSION, OVERLAY_VERSION)
|
||||
editor.apply()
|
||||
}
|
||||
|
||||
fun resetLayoutVisibilityAndPlacement() {
|
||||
defaultOverlayByLayout(layout)
|
||||
val editor = preferences.edit()
|
||||
Settings.overlayPreferences.forEachIndexed { _, pref ->
|
||||
editor.remove(pref)
|
||||
}
|
||||
editor.apply()
|
||||
refreshControls()
|
||||
}
|
||||
|
||||
@ -604,7 +664,11 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
|
||||
R.integer.SWITCH_STICK_R_X,
|
||||
R.integer.SWITCH_STICK_R_Y,
|
||||
R.integer.SWITCH_STICK_L_X,
|
||||
R.integer.SWITCH_STICK_L_Y
|
||||
R.integer.SWITCH_STICK_L_Y,
|
||||
R.integer.SWITCH_BUTTON_STICK_L_X,
|
||||
R.integer.SWITCH_BUTTON_STICK_L_Y,
|
||||
R.integer.SWITCH_BUTTON_STICK_R_X,
|
||||
R.integer.SWITCH_BUTTON_STICK_R_Y
|
||||
)
|
||||
|
||||
private val portraitResources = arrayOf(
|
||||
@ -637,7 +701,11 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
|
||||
R.integer.SWITCH_STICK_R_X_PORTRAIT,
|
||||
R.integer.SWITCH_STICK_R_Y_PORTRAIT,
|
||||
R.integer.SWITCH_STICK_L_X_PORTRAIT,
|
||||
R.integer.SWITCH_STICK_L_Y_PORTRAIT
|
||||
R.integer.SWITCH_STICK_L_Y_PORTRAIT,
|
||||
R.integer.SWITCH_BUTTON_STICK_L_X_PORTRAIT,
|
||||
R.integer.SWITCH_BUTTON_STICK_L_Y_PORTRAIT,
|
||||
R.integer.SWITCH_BUTTON_STICK_R_X_PORTRAIT,
|
||||
R.integer.SWITCH_BUTTON_STICK_R_Y_PORTRAIT
|
||||
)
|
||||
|
||||
private val foldableResources = arrayOf(
|
||||
@ -670,139 +738,159 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
|
||||
R.integer.SWITCH_STICK_R_X_FOLDABLE,
|
||||
R.integer.SWITCH_STICK_R_Y_FOLDABLE,
|
||||
R.integer.SWITCH_STICK_L_X_FOLDABLE,
|
||||
R.integer.SWITCH_STICK_L_Y_FOLDABLE
|
||||
R.integer.SWITCH_STICK_L_Y_FOLDABLE,
|
||||
R.integer.SWITCH_BUTTON_STICK_L_X_FOLDABLE,
|
||||
R.integer.SWITCH_BUTTON_STICK_L_Y_FOLDABLE,
|
||||
R.integer.SWITCH_BUTTON_STICK_R_X_FOLDABLE,
|
||||
R.integer.SWITCH_BUTTON_STICK_R_Y_FOLDABLE
|
||||
)
|
||||
|
||||
private fun getResourceValue(orientation: String, position: Int): Float {
|
||||
return when (orientation) {
|
||||
private fun getResourceValue(layout: String, position: Int): Float {
|
||||
return when (layout) {
|
||||
PORTRAIT -> resources.getInteger(portraitResources[position]).toFloat() / 1000
|
||||
FOLDABLE -> resources.getInteger(foldableResources[position]).toFloat() / 1000
|
||||
else -> resources.getInteger(landscapeResources[position]).toFloat() / 1000
|
||||
}
|
||||
}
|
||||
|
||||
private fun defaultOverlayByLayout(orientation: String) {
|
||||
private fun defaultOverlayByLayout(layout: String) {
|
||||
// Each value represents the position of the button in relation to the screen size without insets.
|
||||
preferences.edit()
|
||||
.putFloat(
|
||||
ButtonType.BUTTON_A.toString() + "-X$orientation",
|
||||
getResourceValue(orientation, 0)
|
||||
"${Settings.PREF_BUTTON_A}-X$layout",
|
||||
getResourceValue(layout, 0)
|
||||
)
|
||||
.putFloat(
|
||||
ButtonType.BUTTON_A.toString() + "-Y$orientation",
|
||||
getResourceValue(orientation, 1)
|
||||
"${Settings.PREF_BUTTON_A}-Y$layout",
|
||||
getResourceValue(layout, 1)
|
||||
)
|
||||
.putFloat(
|
||||
ButtonType.BUTTON_B.toString() + "-X$orientation",
|
||||
getResourceValue(orientation, 2)
|
||||
"${Settings.PREF_BUTTON_B}-X$layout",
|
||||
getResourceValue(layout, 2)
|
||||
)
|
||||
.putFloat(
|
||||
ButtonType.BUTTON_B.toString() + "-Y$orientation",
|
||||
getResourceValue(orientation, 3)
|
||||
"${Settings.PREF_BUTTON_B}-Y$layout",
|
||||
getResourceValue(layout, 3)
|
||||
)
|
||||
.putFloat(
|
||||
ButtonType.BUTTON_X.toString() + "-X$orientation",
|
||||
getResourceValue(orientation, 4)
|
||||
"${Settings.PREF_BUTTON_X}-X$layout",
|
||||
getResourceValue(layout, 4)
|
||||
)
|
||||
.putFloat(
|
||||
ButtonType.BUTTON_X.toString() + "-Y$orientation",
|
||||
getResourceValue(orientation, 5)
|
||||
"${Settings.PREF_BUTTON_X}-Y$layout",
|
||||
getResourceValue(layout, 5)
|
||||
)
|
||||
.putFloat(
|
||||
ButtonType.BUTTON_Y.toString() + "-X$orientation",
|
||||
getResourceValue(orientation, 6)
|
||||
"${Settings.PREF_BUTTON_Y}-X$layout",
|
||||
getResourceValue(layout, 6)
|
||||
)
|
||||
.putFloat(
|
||||
ButtonType.BUTTON_Y.toString() + "-Y$orientation",
|
||||
getResourceValue(orientation, 7)
|
||||
"${Settings.PREF_BUTTON_Y}-Y$layout",
|
||||
getResourceValue(layout, 7)
|
||||
)
|
||||
.putFloat(
|
||||
ButtonType.TRIGGER_ZL.toString() + "-X$orientation",
|
||||
getResourceValue(orientation, 8)
|
||||
"${Settings.PREF_BUTTON_ZL}-X$layout",
|
||||
getResourceValue(layout, 8)
|
||||
)
|
||||
.putFloat(
|
||||
ButtonType.TRIGGER_ZL.toString() + "-Y$orientation",
|
||||
getResourceValue(orientation, 9)
|
||||
"${Settings.PREF_BUTTON_ZL}-Y$layout",
|
||||
getResourceValue(layout, 9)
|
||||
)
|
||||
.putFloat(
|
||||
ButtonType.TRIGGER_ZR.toString() + "-X$orientation",
|
||||
getResourceValue(orientation, 10)
|
||||
"${Settings.PREF_BUTTON_ZR}-X$layout",
|
||||
getResourceValue(layout, 10)
|
||||
)
|
||||
.putFloat(
|
||||
ButtonType.TRIGGER_ZR.toString() + "-Y$orientation",
|
||||
getResourceValue(orientation, 11)
|
||||
"${Settings.PREF_BUTTON_ZR}-Y$layout",
|
||||
getResourceValue(layout, 11)
|
||||
)
|
||||
.putFloat(
|
||||
ButtonType.DPAD_UP.toString() + "-X$orientation",
|
||||
getResourceValue(orientation, 12)
|
||||
"${Settings.PREF_BUTTON_DPAD}-X$layout",
|
||||
getResourceValue(layout, 12)
|
||||
)
|
||||
.putFloat(
|
||||
ButtonType.DPAD_UP.toString() + "-Y$orientation",
|
||||
getResourceValue(orientation, 13)
|
||||
"${Settings.PREF_BUTTON_DPAD}-Y$layout",
|
||||
getResourceValue(layout, 13)
|
||||
)
|
||||
.putFloat(
|
||||
ButtonType.TRIGGER_L.toString() + "-X$orientation",
|
||||
getResourceValue(orientation, 14)
|
||||
"${Settings.PREF_BUTTON_L}-X$layout",
|
||||
getResourceValue(layout, 14)
|
||||
)
|
||||
.putFloat(
|
||||
ButtonType.TRIGGER_L.toString() + "-Y$orientation",
|
||||
getResourceValue(orientation, 15)
|
||||
"${Settings.PREF_BUTTON_L}-Y$layout",
|
||||
getResourceValue(layout, 15)
|
||||
)
|
||||
.putFloat(
|
||||
ButtonType.TRIGGER_R.toString() + "-X$orientation",
|
||||
getResourceValue(orientation, 16)
|
||||
"${Settings.PREF_BUTTON_R}-X$layout",
|
||||
getResourceValue(layout, 16)
|
||||
)
|
||||
.putFloat(
|
||||
ButtonType.TRIGGER_R.toString() + "-Y$orientation",
|
||||
getResourceValue(orientation, 17)
|
||||
"${Settings.PREF_BUTTON_R}-Y$layout",
|
||||
getResourceValue(layout, 17)
|
||||
)
|
||||
.putFloat(
|
||||
ButtonType.BUTTON_PLUS.toString() + "-X$orientation",
|
||||
getResourceValue(orientation, 18)
|
||||
"${Settings.PREF_BUTTON_PLUS}-X$layout",
|
||||
getResourceValue(layout, 18)
|
||||
)
|
||||
.putFloat(
|
||||
ButtonType.BUTTON_PLUS.toString() + "-Y$orientation",
|
||||
getResourceValue(orientation, 19)
|
||||
"${Settings.PREF_BUTTON_PLUS}-Y$layout",
|
||||
getResourceValue(layout, 19)
|
||||
)
|
||||
.putFloat(
|
||||
ButtonType.BUTTON_MINUS.toString() + "-X$orientation",
|
||||
getResourceValue(orientation, 20)
|
||||
"${Settings.PREF_BUTTON_MINUS}-X$layout",
|
||||
getResourceValue(layout, 20)
|
||||
)
|
||||
.putFloat(
|
||||
ButtonType.BUTTON_MINUS.toString() + "-Y$orientation",
|
||||
getResourceValue(orientation, 21)
|
||||
"${Settings.PREF_BUTTON_MINUS}-Y$layout",
|
||||
getResourceValue(layout, 21)
|
||||
)
|
||||
.putFloat(
|
||||
ButtonType.BUTTON_HOME.toString() + "-X$orientation",
|
||||
getResourceValue(orientation, 22)
|
||||
"${Settings.PREF_BUTTON_HOME}-X$layout",
|
||||
getResourceValue(layout, 22)
|
||||
)
|
||||
.putFloat(
|
||||
ButtonType.BUTTON_HOME.toString() + "-Y$orientation",
|
||||
getResourceValue(orientation, 23)
|
||||
"${Settings.PREF_BUTTON_HOME}-Y$layout",
|
||||
getResourceValue(layout, 23)
|
||||
)
|
||||
.putFloat(
|
||||
ButtonType.BUTTON_CAPTURE.toString() + "-X$orientation",
|
||||
getResourceValue(orientation, 24)
|
||||
"${Settings.PREF_BUTTON_SCREENSHOT}-X$layout",
|
||||
getResourceValue(layout, 24)
|
||||
)
|
||||
.putFloat(
|
||||
ButtonType.BUTTON_CAPTURE.toString() + "-Y$orientation",
|
||||
getResourceValue(orientation, 25)
|
||||
"${Settings.PREF_BUTTON_SCREENSHOT}-Y$layout",
|
||||
getResourceValue(layout, 25)
|
||||
)
|
||||
.putFloat(
|
||||
ButtonType.STICK_R.toString() + "-X$orientation",
|
||||
getResourceValue(orientation, 26)
|
||||
"${Settings.PREF_STICK_R}-X$layout",
|
||||
getResourceValue(layout, 26)
|
||||
)
|
||||
.putFloat(
|
||||
ButtonType.STICK_R.toString() + "-Y$orientation",
|
||||
getResourceValue(orientation, 27)
|
||||
"${Settings.PREF_STICK_R}-Y$layout",
|
||||
getResourceValue(layout, 27)
|
||||
)
|
||||
.putFloat(
|
||||
ButtonType.STICK_L.toString() + "-X$orientation",
|
||||
getResourceValue(orientation, 28)
|
||||
"${Settings.PREF_STICK_L}-X$layout",
|
||||
getResourceValue(layout, 28)
|
||||
)
|
||||
.putFloat(
|
||||
ButtonType.STICK_L.toString() + "-Y$orientation",
|
||||
getResourceValue(orientation, 29)
|
||||
"${Settings.PREF_STICK_L}-Y$layout",
|
||||
getResourceValue(layout, 29)
|
||||
)
|
||||
.putFloat(
|
||||
"${Settings.PREF_BUTTON_STICK_L}-X$layout",
|
||||
getResourceValue(layout, 30)
|
||||
)
|
||||
.putFloat(
|
||||
"${Settings.PREF_BUTTON_STICK_L}-Y$layout",
|
||||
getResourceValue(layout, 31)
|
||||
)
|
||||
.putFloat(
|
||||
"${Settings.PREF_BUTTON_STICK_R}-X$layout",
|
||||
getResourceValue(layout, 32)
|
||||
)
|
||||
.putFloat(
|
||||
"${Settings.PREF_BUTTON_STICK_R}-Y$layout",
|
||||
getResourceValue(layout, 33)
|
||||
)
|
||||
.apply()
|
||||
}
|
||||
@ -812,12 +900,30 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
|
||||
}
|
||||
|
||||
companion object {
|
||||
// Increase this number every time there is a breaking change to every overlay layout
|
||||
const val OVERLAY_VERSION = 1
|
||||
|
||||
// Increase the corresponding layout version number whenever that layout has a breaking change
|
||||
private const val LANDSCAPE_OVERLAY_VERSION = 1
|
||||
private const val PORTRAIT_OVERLAY_VERSION = 1
|
||||
private const val FOLDABLE_OVERLAY_VERSION = 1
|
||||
val overlayLayoutVersions = listOf(
|
||||
LANDSCAPE_OVERLAY_VERSION,
|
||||
PORTRAIT_OVERLAY_VERSION,
|
||||
FOLDABLE_OVERLAY_VERSION
|
||||
)
|
||||
|
||||
private val preferences: SharedPreferences =
|
||||
PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
|
||||
|
||||
const val LANDSCAPE = ""
|
||||
const val LANDSCAPE = "_Landscape"
|
||||
const val PORTRAIT = "_Portrait"
|
||||
const val FOLDABLE = "_Foldable"
|
||||
val overlayLayouts = listOf(
|
||||
LANDSCAPE,
|
||||
PORTRAIT,
|
||||
FOLDABLE
|
||||
)
|
||||
|
||||
/**
|
||||
* Resizes a [Bitmap] by a given scale factor
|
||||
@ -948,6 +1054,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
|
||||
* @param defaultResId The resource ID of the [Drawable] to get the [Bitmap] of (Default State).
|
||||
* @param pressedResId The resource ID of the [Drawable] to get the [Bitmap] of (Pressed State).
|
||||
* @param buttonId Identifier for determining what type of button the initialized InputOverlayDrawableButton represents.
|
||||
* @param prefId Identifier for determining where a button appears on screen.
|
||||
* @param layout The current screen layout as determined by [LANDSCAPE], [PORTRAIT], or [FOLDABLE].
|
||||
* @return An [InputOverlayDrawableButton] with the correct drawing bounds set.
|
||||
*/
|
||||
private fun initializeOverlayButton(
|
||||
@ -956,7 +1064,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
|
||||
defaultResId: Int,
|
||||
pressedResId: Int,
|
||||
buttonId: Int,
|
||||
orientation: String
|
||||
prefId: String,
|
||||
layout: String
|
||||
): InputOverlayDrawableButton {
|
||||
// Resources handle for fetching the initial Drawable resource.
|
||||
val res = context.resources
|
||||
@ -964,17 +1073,20 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
|
||||
// SharedPreference to retrieve the X and Y coordinates for the InputOverlayDrawableButton.
|
||||
val sPrefs = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
|
||||
|
||||
// Decide scale based on button ID and user preference
|
||||
var scale: Float = when (buttonId) {
|
||||
ButtonType.BUTTON_HOME,
|
||||
ButtonType.BUTTON_CAPTURE,
|
||||
ButtonType.BUTTON_PLUS,
|
||||
ButtonType.BUTTON_MINUS -> 0.07f
|
||||
// Decide scale based on button preference ID and user preference
|
||||
var scale: Float = when (prefId) {
|
||||
Settings.PREF_BUTTON_HOME,
|
||||
Settings.PREF_BUTTON_SCREENSHOT,
|
||||
Settings.PREF_BUTTON_PLUS,
|
||||
Settings.PREF_BUTTON_MINUS -> 0.07f
|
||||
|
||||
ButtonType.TRIGGER_L,
|
||||
ButtonType.TRIGGER_R,
|
||||
ButtonType.TRIGGER_ZL,
|
||||
ButtonType.TRIGGER_ZR -> 0.26f
|
||||
Settings.PREF_BUTTON_L,
|
||||
Settings.PREF_BUTTON_R,
|
||||
Settings.PREF_BUTTON_ZL,
|
||||
Settings.PREF_BUTTON_ZR -> 0.26f
|
||||
|
||||
Settings.PREF_BUTTON_STICK_L,
|
||||
Settings.PREF_BUTTON_STICK_R -> 0.155f
|
||||
|
||||
else -> 0.11f
|
||||
}
|
||||
@ -984,8 +1096,13 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
|
||||
// Initialize the InputOverlayDrawableButton.
|
||||
val defaultStateBitmap = getBitmap(context, defaultResId, scale)
|
||||
val pressedStateBitmap = getBitmap(context, pressedResId, scale)
|
||||
val overlayDrawable =
|
||||
InputOverlayDrawableButton(res, defaultStateBitmap, pressedStateBitmap, buttonId)
|
||||
val overlayDrawable = InputOverlayDrawableButton(
|
||||
res,
|
||||
defaultStateBitmap,
|
||||
pressedStateBitmap,
|
||||
buttonId,
|
||||
prefId
|
||||
)
|
||||
|
||||
// Get the minimum and maximum coordinates of the screen where the button can be placed.
|
||||
val min = windowSize.first
|
||||
@ -993,8 +1110,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
|
||||
|
||||
// The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay.
|
||||
// These were set in the input overlay configuration menu.
|
||||
val xKey = "$buttonId-X$orientation"
|
||||
val yKey = "$buttonId-Y$orientation"
|
||||
val xKey = "$prefId-X$layout"
|
||||
val yKey = "$prefId-Y$layout"
|
||||
val drawableXPercent = sPrefs.getFloat(xKey, 0f)
|
||||
val drawableYPercent = sPrefs.getFloat(yKey, 0f)
|
||||
val drawableX = (drawableXPercent * max.x + min.x).toInt()
|
||||
@ -1029,7 +1146,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
|
||||
* @param defaultResId The [Bitmap] resource ID of the default state.
|
||||
* @param pressedOneDirectionResId The [Bitmap] resource ID of the pressed state in one direction.
|
||||
* @param pressedTwoDirectionsResId The [Bitmap] resource ID of the pressed state in two directions.
|
||||
* @return the initialized [InputOverlayDrawableDpad]
|
||||
* @param layout The current screen layout as determined by [LANDSCAPE], [PORTRAIT], or [FOLDABLE].
|
||||
* @return The initialized [InputOverlayDrawableDpad]
|
||||
*/
|
||||
private fun initializeOverlayDpad(
|
||||
context: Context,
|
||||
@ -1037,7 +1155,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
|
||||
defaultResId: Int,
|
||||
pressedOneDirectionResId: Int,
|
||||
pressedTwoDirectionsResId: Int,
|
||||
orientation: String
|
||||
layout: String
|
||||
): InputOverlayDrawableDpad {
|
||||
// Resources handle for fetching the initial Drawable resource.
|
||||
val res = context.resources
|
||||
@ -1074,8 +1192,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
|
||||
|
||||
// The X and Y coordinates of the InputOverlayDrawableDpad on the InputOverlay.
|
||||
// These were set in the input overlay configuration menu.
|
||||
val drawableXPercent = sPrefs.getFloat("${ButtonType.DPAD_UP}-X$orientation", 0f)
|
||||
val drawableYPercent = sPrefs.getFloat("${ButtonType.DPAD_UP}-Y$orientation", 0f)
|
||||
val drawableXPercent = sPrefs.getFloat("${Settings.PREF_BUTTON_DPAD}-X$layout", 0f)
|
||||
val drawableYPercent = sPrefs.getFloat("${Settings.PREF_BUTTON_DPAD}-Y$layout", 0f)
|
||||
val drawableX = (drawableXPercent * max.x + min.x).toInt()
|
||||
val drawableY = (drawableYPercent * max.y + min.y).toInt()
|
||||
val width = overlayDrawable.width
|
||||
@ -1107,7 +1225,9 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
|
||||
* @param pressedResInner Resource ID for the pressed inner image of the joystick.
|
||||
* @param joystick Identifier for which joystick this is.
|
||||
* @param button Identifier for which joystick button this is.
|
||||
* @return the initialized [InputOverlayDrawableJoystick].
|
||||
* @param prefId Identifier for determining where a button appears on screen.
|
||||
* @param layout The current screen layout as determined by [LANDSCAPE], [PORTRAIT], or [FOLDABLE].
|
||||
* @return The initialized [InputOverlayDrawableJoystick].
|
||||
*/
|
||||
private fun initializeOverlayJoystick(
|
||||
context: Context,
|
||||
@ -1117,7 +1237,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
|
||||
pressedResInner: Int,
|
||||
joystick: Int,
|
||||
button: Int,
|
||||
orientation: String
|
||||
prefId: String,
|
||||
layout: String
|
||||
): InputOverlayDrawableJoystick {
|
||||
// Resources handle for fetching the initial Drawable resource.
|
||||
val res = context.resources
|
||||
@ -1141,8 +1262,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
|
||||
|
||||
// The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay.
|
||||
// These were set in the input overlay configuration menu.
|
||||
val drawableXPercent = sPrefs.getFloat("$button-X$orientation", 0f)
|
||||
val drawableYPercent = sPrefs.getFloat("$button-Y$orientation", 0f)
|
||||
val drawableXPercent = sPrefs.getFloat("$prefId-X$layout", 0f)
|
||||
val drawableYPercent = sPrefs.getFloat("$prefId-Y$layout", 0f)
|
||||
val drawableX = (drawableXPercent * max.x + min.x).toInt()
|
||||
val drawableY = (drawableYPercent * max.y + min.y).toInt()
|
||||
val outerScale = 1.66f
|
||||
@ -1168,7 +1289,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
|
||||
outerRect,
|
||||
innerRect,
|
||||
joystick,
|
||||
button
|
||||
button,
|
||||
prefId
|
||||
)
|
||||
|
||||
// Need to set the image's position
|
||||
|
@ -24,7 +24,8 @@ class InputOverlayDrawableButton(
|
||||
res: Resources,
|
||||
defaultStateBitmap: Bitmap,
|
||||
pressedStateBitmap: Bitmap,
|
||||
val buttonId: Int
|
||||
val buttonId: Int,
|
||||
val prefId: String
|
||||
) {
|
||||
// The ID value what motion event is tracking
|
||||
var trackId: Int
|
||||
|
@ -37,7 +37,8 @@ class InputOverlayDrawableJoystick(
|
||||
rectOuter: Rect,
|
||||
rectInner: Rect,
|
||||
val joystickId: Int,
|
||||
val buttonId: Int
|
||||
val buttonId: Int,
|
||||
val prefId: String
|
||||
) {
|
||||
// The ID value what motion event is tracking
|
||||
var trackId = -1
|
||||
|
128
src/android/app/src/main/res/drawable/button_l3.xml
Normal file
128
src/android/app/src/main/res/drawable/button_l3.xml
Normal file
@ -0,0 +1,128 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="34.963dp"
|
||||
android:height="37.265dp"
|
||||
android:viewportWidth="34.963"
|
||||
android:viewportHeight="37.265">
|
||||
<path
|
||||
android:fillAlpha="0.5"
|
||||
android:pathData="M19.451,19.024A3.498,3.498 0,0 0,21.165 19.508c1.336,0 1.749,-0.852 1.738,-1.49 0,-1.077 -0.982,-1.537 -1.987,-1.537L20.327,16.481L20.327,15.7L20.901,15.7c0.757,0 1.714,-0.392 1.714,-1.302C22.621,13.785 22.224,13.229 21.271,13.229a2.834,2.834 0,0 0,-1.537 0.529l-0.265,-0.757a3.662,3.662 0,0 1,2.008 -0.59c1.513,0 2.201,0.897 2.201,1.834 0,0.794 -0.474,1.466 -1.421,1.807l0,0.024c0.947,0.19 1.714,0.9 1.714,1.976C23.967,19.27 23.017,20.346 21.165,20.346a3.929,3.929 135,0 1,-1.998 -0.529z"
|
||||
android:strokeAlpha="0.6">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="21.568"
|
||||
android:endY="33.938"
|
||||
android:startX="21.568"
|
||||
android:startY="16.14"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#FFC3C4C5"
|
||||
android:offset="0" />
|
||||
<item
|
||||
android:color="#FFC5C6C6"
|
||||
android:offset="0.03" />
|
||||
<item
|
||||
android:color="#FFC7C7C7"
|
||||
android:offset="0.19" />
|
||||
<item
|
||||
android:color="#DBB5B5B5"
|
||||
android:offset="0.44" />
|
||||
<item
|
||||
android:color="#7F878787"
|
||||
android:offset="1" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillAlpha="0.5"
|
||||
android:pathData="M16.062,9.353 L9.624,3.405A1.963,1.963 0,0 1,10.955 0l12.88,0a1.963,1.963 135,0 1,1.323 3.405L18.726,9.353a1.961,1.961 135,0 1,-2.664 0z"
|
||||
android:strokeAlpha="0.6">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="17.395"
|
||||
android:endY="18.74"
|
||||
android:startX="17.395"
|
||||
android:startY="-1.296"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#FFC3C4C5"
|
||||
android:offset="0" />
|
||||
<item
|
||||
android:color="#FFC5C6C6"
|
||||
android:offset="0.03" />
|
||||
<item
|
||||
android:color="#FFC7C7C7"
|
||||
android:offset="0.19" />
|
||||
<item
|
||||
android:color="#DBB5B5B5"
|
||||
android:offset="0.44" />
|
||||
<item
|
||||
android:color="#7F878787"
|
||||
android:offset="1" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillAlpha="0.5"
|
||||
android:pathData="m25.79,5.657l0,0a2.09,2.09 45,0 0,0.23 3.262c3.522,2.402 4.762,5.927 4.741,10.52A13.279,13.279 135,0 1,4.206 19.365c0,-4.516 0.931,-7.71 4.374,-10.107a2.098,2.098 0,0 0,0.233 -3.265l0,0a2.101,2.101 135,0 0,-2.646 -0.169C1.433,9.133 -0.266,13.941 0.033,20.233a17.468,17.468 0,0 0,34.925 -0.868c0,-6.006 -1.971,-10.771 -6.585,-13.917a2.088,2.088 45,0 0,-2.582 0.209z"
|
||||
android:strokeAlpha="0.6">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:centerX="17.477"
|
||||
android:centerY="19.92"
|
||||
android:gradientRadius="17.201"
|
||||
android:type="radial">
|
||||
<item
|
||||
android:color="#FFC3C4C5"
|
||||
android:offset="0.58" />
|
||||
<item
|
||||
android:color="#FFC6C6C6"
|
||||
android:offset="0.84" />
|
||||
<item
|
||||
android:color="#FFC7C7C7"
|
||||
android:offset="0.88" />
|
||||
<item
|
||||
android:color="#FFC2C2C2"
|
||||
android:offset="0.91" />
|
||||
<item
|
||||
android:color="#FFB5B5B5"
|
||||
android:offset="0.94" />
|
||||
<item
|
||||
android:color="#FF9E9E9E"
|
||||
android:offset="0.98" />
|
||||
<item
|
||||
android:color="#FF8F8F8F"
|
||||
android:offset="1" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillAlpha="0.5"
|
||||
android:pathData="m12.516,12.729l2,0l0,13.822l6.615,0l0,1.68L12.516,28.231Z"
|
||||
android:strokeAlpha="0.6">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="16.829"
|
||||
android:endY="46.882"
|
||||
android:startX="16.829"
|
||||
android:startY="20.479"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#FFC3C4C5"
|
||||
android:offset="0" />
|
||||
<item
|
||||
android:color="#FFC5C6C6"
|
||||
android:offset="0.03" />
|
||||
<item
|
||||
android:color="#FFC7C7C7"
|
||||
android:offset="0.19" />
|
||||
<item
|
||||
android:color="#DBB5B5B5"
|
||||
android:offset="0.44" />
|
||||
<item
|
||||
android:color="#7F878787"
|
||||
android:offset="1" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
</vector>
|
@ -0,0 +1,75 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="34.963dp"
|
||||
android:height="37.265dp"
|
||||
android:viewportWidth="34.963"
|
||||
android:viewportHeight="37.265">
|
||||
<path
|
||||
android:fillAlpha="0.3"
|
||||
android:fillColor="#151515"
|
||||
android:pathData="M16.062,9.353 L9.624,3.405A1.963,1.963 0,0 1,10.955 0l12.88,0a1.963,1.963 135,0 1,1.323 3.405L18.726,9.353a1.961,1.961 135,0 1,-2.664 0z"
|
||||
android:strokeAlpha="0.3" />
|
||||
<path
|
||||
android:fillAlpha="0.6"
|
||||
android:fillColor="#151515"
|
||||
android:pathData="m25.79,5.657l0,0a2.09,2.09 45,0 0,0.23 3.262c3.522,2.402 4.762,5.927 4.741,10.52A13.279,13.279 135,0 1,4.206 19.365c0,-4.516 0.931,-7.71 4.374,-10.107a2.098,2.098 0,0 0,0.233 -3.265l0,0a2.101,2.101 135,0 0,-2.646 -0.169C1.433,9.133 -0.266,13.941 0.033,20.233a17.468,17.468 0,0 0,34.925 -0.868c0,-6.006 -1.971,-10.771 -6.585,-13.917a2.088,2.088 45,0 0,-2.582 0.209z"
|
||||
android:strokeAlpha="0.6" />
|
||||
<path
|
||||
android:fillAlpha="0.6"
|
||||
android:pathData="M19.451,19.024A3.498,3.498 0,0 0,21.165 19.508c1.336,0 1.749,-0.852 1.738,-1.49 0,-1.077 -0.982,-1.537 -1.987,-1.537L20.327,16.481L20.327,15.7L20.901,15.7c0.757,0 1.714,-0.392 1.714,-1.302C22.621,13.785 22.224,13.229 21.271,13.229a2.834,2.834 0,0 0,-1.537 0.529l-0.265,-0.757a3.662,3.662 0,0 1,2.008 -0.59c1.513,0 2.201,0.897 2.201,1.834 0,0.794 -0.474,1.466 -1.421,1.807l0,0.024c0.947,0.19 1.714,0.9 1.714,1.976C23.967,19.27 23.017,20.346 21.165,20.346a3.929,3.929 135,0 1,-1.998 -0.529z"
|
||||
android:strokeAlpha="0.6">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="21.568"
|
||||
android:endY="33.938"
|
||||
android:startX="21.568"
|
||||
android:startY="16.14"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#FFC3C4C5"
|
||||
android:offset="0" />
|
||||
<item
|
||||
android:color="#FFC5C6C6"
|
||||
android:offset="0.03" />
|
||||
<item
|
||||
android:color="#FFC7C7C7"
|
||||
android:offset="0.19" />
|
||||
<item
|
||||
android:color="#DBB5B5B5"
|
||||
android:offset="0.44" />
|
||||
<item
|
||||
android:color="#7F878787"
|
||||
android:offset="1" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillAlpha="0.6"
|
||||
android:pathData="m12.516,12.729l2,0l0,13.822l6.615,0l0,1.68L12.516,28.231Z"
|
||||
android:strokeAlpha="0.6">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="16.829"
|
||||
android:endY="46.882"
|
||||
android:startX="16.829"
|
||||
android:startY="20.479"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#FFC3C4C5"
|
||||
android:offset="0" />
|
||||
<item
|
||||
android:color="#FFC5C6C6"
|
||||
android:offset="0.03" />
|
||||
<item
|
||||
android:color="#FFC7C7C7"
|
||||
android:offset="0.19" />
|
||||
<item
|
||||
android:color="#DBB5B5B5"
|
||||
android:offset="0.44" />
|
||||
<item
|
||||
android:color="#7F878787"
|
||||
android:offset="1" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
</vector>
|
128
src/android/app/src/main/res/drawable/button_r3.xml
Normal file
128
src/android/app/src/main/res/drawable/button_r3.xml
Normal file
@ -0,0 +1,128 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="34.963dp"
|
||||
android:height="37.265dp"
|
||||
android:viewportWidth="34.963"
|
||||
android:viewportHeight="37.265">
|
||||
<path
|
||||
android:fillAlpha="0.5"
|
||||
android:pathData="m10.781,12.65a19.579,19.579 0,0 1,3.596 -0.302c2.003,0 3.294,0.368 4.199,1.185a3.622,3.622 0,0 1,1.14 2.757c0,1.916 -1.206,3.175 -2.733,3.704l0,0.063c1.119,0.386 1.786,1.421 2.117,2.929 0.474,2.024 0.818,3.424 1.119,3.982l-1.924,0c-0.238,-0.407 -0.561,-1.656 -0.968,-3.466 -0.431,-2.003 -1.206,-2.757 -2.91,-2.82l-1.762,0l0,6.286l-1.873,0zM12.654,19.264l1.916,0c2.003,0 3.273,-1.098 3.273,-2.757 0,-1.873 -1.357,-2.691 -3.336,-2.712a7.649,7.649 0,0 0,-1.852 0.172z"
|
||||
android:strokeAlpha="0.6">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="15.506"
|
||||
android:endY="48.977"
|
||||
android:startX="15.506"
|
||||
android:startY="19.659"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#FFC3C4C5"
|
||||
android:offset="0" />
|
||||
<item
|
||||
android:color="#FFC5C6C6"
|
||||
android:offset="0.03" />
|
||||
<item
|
||||
android:color="#FFC7C7C7"
|
||||
android:offset="0.19" />
|
||||
<item
|
||||
android:color="#DBB5B5B5"
|
||||
android:offset="0.44" />
|
||||
<item
|
||||
android:color="#7F878787"
|
||||
android:offset="1" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillAlpha="0.5"
|
||||
android:pathData="M16.062,9.353 L9.624,3.405A1.963,1.963 0,0 1,10.955 0l12.88,0a1.963,1.963 135,0 1,1.323 3.405L18.726,9.353a1.961,1.961 135,0 1,-2.664 0z"
|
||||
android:strokeAlpha="0.6">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="17.395"
|
||||
android:endY="18.74"
|
||||
android:startX="17.395"
|
||||
android:startY="-1.296"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#FFC3C4C5"
|
||||
android:offset="0" />
|
||||
<item
|
||||
android:color="#FFC5C6C6"
|
||||
android:offset="0.03" />
|
||||
<item
|
||||
android:color="#FFC7C7C7"
|
||||
android:offset="0.19" />
|
||||
<item
|
||||
android:color="#DBB5B5B5"
|
||||
android:offset="0.44" />
|
||||
<item
|
||||
android:color="#7F878787"
|
||||
android:offset="1" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillAlpha="0.5"
|
||||
android:pathData="m25.79,5.657l0,0a2.09,2.09 45,0 0,0.23 3.262c3.522,2.402 4.762,5.927 4.741,10.52A13.279,13.279 135,0 1,4.206 19.365c0,-4.516 0.931,-7.71 4.374,-10.107a2.098,2.098 0,0 0,0.233 -3.265l0,0a2.101,2.101 135,0 0,-2.646 -0.169C1.433,9.133 -0.266,13.941 0.033,20.233a17.468,17.468 0,0 0,34.925 -0.868c0,-6.006 -1.971,-10.771 -6.585,-13.917a2.088,2.088 45,0 0,-2.582 0.209z"
|
||||
android:strokeAlpha="0.6">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:centerX="17.477"
|
||||
android:centerY="19.92"
|
||||
android:gradientRadius="17.201"
|
||||
android:type="radial">
|
||||
<item
|
||||
android:color="#FFC3C4C5"
|
||||
android:offset="0.58" />
|
||||
<item
|
||||
android:color="#FFC6C6C6"
|
||||
android:offset="0.84" />
|
||||
<item
|
||||
android:color="#FFC7C7C7"
|
||||
android:offset="0.88" />
|
||||
<item
|
||||
android:color="#FFC2C2C2"
|
||||
android:offset="0.91" />
|
||||
<item
|
||||
android:color="#FFB5B5B5"
|
||||
android:offset="0.94" />
|
||||
<item
|
||||
android:color="#FF9E9E9E"
|
||||
android:offset="0.98" />
|
||||
<item
|
||||
android:color="#FF8F8F8F"
|
||||
android:offset="1" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillAlpha="0.5"
|
||||
android:pathData="M21.832,19.024A3.498,3.498 0,0 0,23.547 19.508c1.336,0 1.749,-0.852 1.738,-1.49 0,-1.077 -0.982,-1.537 -1.987,-1.537L22.708,16.481L22.708,15.7L23.282,15.7c0.757,0 1.714,-0.392 1.714,-1.302C25.002,13.785 24.605,13.229 23.652,13.229a2.834,2.834 0,0 0,-1.537 0.529l-0.265,-0.757a3.662,3.662 0,0 1,2.008 -0.59c1.513,0 2.201,0.897 2.201,1.834 0,0.794 -0.474,1.466 -1.421,1.807l0,0.024c0.947,0.19 1.714,0.9 1.714,1.976C26.349,19.27 25.399,20.346 23.547,20.346a3.929,3.929 135,0 1,-1.998 -0.529z"
|
||||
android:strokeAlpha="0.6">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="23.949"
|
||||
android:endY="33.938"
|
||||
android:startX="23.949"
|
||||
android:startY="16.14"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#FFC3C4C5"
|
||||
android:offset="0" />
|
||||
<item
|
||||
android:color="#FFC5C6C6"
|
||||
android:offset="0.03" />
|
||||
<item
|
||||
android:color="#FFC7C7C7"
|
||||
android:offset="0.19" />
|
||||
<item
|
||||
android:color="#DBB5B5B5"
|
||||
android:offset="0.44" />
|
||||
<item
|
||||
android:color="#7F878787"
|
||||
android:offset="1" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
</vector>
|
@ -0,0 +1,75 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="34.963dp"
|
||||
android:height="37.265dp"
|
||||
android:viewportWidth="34.963"
|
||||
android:viewportHeight="37.265">
|
||||
<path
|
||||
android:fillAlpha="0.3"
|
||||
android:fillColor="#151515"
|
||||
android:pathData="M16.062,9.353 L9.624,3.405A1.963,1.963 0,0 1,10.955 0l12.88,0a1.963,1.963 135,0 1,1.323 3.405L18.726,9.353a1.961,1.961 135,0 1,-2.664 0z"
|
||||
android:strokeAlpha="0.3" />
|
||||
<path
|
||||
android:fillAlpha="0.6"
|
||||
android:fillColor="#151515"
|
||||
android:pathData="m25.79,5.657l0,0a2.09,2.09 45,0 0,0.23 3.262c3.522,2.402 4.762,5.927 4.741,10.52A13.279,13.279 135,0 1,4.206 19.365c0,-4.516 0.931,-7.71 4.374,-10.107a2.098,2.098 0,0 0,0.233 -3.265l0,0a2.101,2.101 135,0 0,-2.646 -0.169C1.433,9.133 -0.266,13.941 0.033,20.233a17.468,17.468 0,0 0,34.925 -0.868c0,-6.006 -1.971,-10.771 -6.585,-13.917a2.088,2.088 45,0 0,-2.582 0.209z"
|
||||
android:strokeAlpha="0.6" />
|
||||
<path
|
||||
android:fillAlpha="0.6"
|
||||
android:pathData="m10.781,12.65a19.579,19.579 0,0 1,3.596 -0.302c2.003,0 3.294,0.368 4.199,1.185a3.622,3.622 0,0 1,1.14 2.757c0,1.916 -1.206,3.175 -2.733,3.704l0,0.063c1.119,0.386 1.786,1.421 2.117,2.929 0.474,2.024 0.818,3.424 1.119,3.982l-1.924,0c-0.238,-0.407 -0.561,-1.656 -0.968,-3.466 -0.431,-2.003 -1.206,-2.757 -2.91,-2.82l-1.762,0l0,6.286l-1.873,0zM12.654,19.264l1.916,0c2.003,0 3.273,-1.098 3.273,-2.757 0,-1.873 -1.357,-2.691 -3.336,-2.712a7.649,7.649 0,0 0,-1.852 0.172z"
|
||||
android:strokeAlpha="0.6">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="15.506"
|
||||
android:endY="48.977"
|
||||
android:startX="15.506"
|
||||
android:startY="19.659"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#FFC3C4C5"
|
||||
android:offset="0" />
|
||||
<item
|
||||
android:color="#FFC5C6C6"
|
||||
android:offset="0.03" />
|
||||
<item
|
||||
android:color="#FFC7C7C7"
|
||||
android:offset="0.19" />
|
||||
<item
|
||||
android:color="#DBB5B5B5"
|
||||
android:offset="0.44" />
|
||||
<item
|
||||
android:color="#7F878787"
|
||||
android:offset="1" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillAlpha="0.6"
|
||||
android:pathData="M21.832,19.024A3.498,3.498 0,0 0,23.547 19.508c1.336,0 1.749,-0.852 1.738,-1.49 0,-1.077 -0.982,-1.537 -1.987,-1.537L22.708,16.481L22.708,15.7L23.282,15.7c0.757,0 1.714,-0.392 1.714,-1.302C25.002,13.785 24.605,13.229 23.652,13.229a2.834,2.834 0,0 0,-1.537 0.529l-0.265,-0.757a3.662,3.662 0,0 1,2.008 -0.59c1.513,0 2.201,0.897 2.201,1.834 0,0.794 -0.474,1.466 -1.421,1.807l0,0.024c0.947,0.19 1.714,0.9 1.714,1.976C26.349,19.27 25.399,20.346 23.547,20.346a3.929,3.929 135,0 1,-1.998 -0.529z"
|
||||
android:strokeAlpha="0.6">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="23.949"
|
||||
android:endY="33.938"
|
||||
android:startX="23.949"
|
||||
android:startY="16.14"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#FFC3C4C5"
|
||||
android:offset="0" />
|
||||
<item
|
||||
android:color="#FFC5C6C6"
|
||||
android:offset="0.03" />
|
||||
<item
|
||||
android:color="#FFC7C7C7"
|
||||
android:offset="0.19" />
|
||||
<item
|
||||
android:color="#DBB5B5B5"
|
||||
android:offset="0.44" />
|
||||
<item
|
||||
android:color="#7F878787"
|
||||
android:offset="1" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
</vector>
|
@ -205,6 +205,8 @@
|
||||
<item>@string/gamepad_d_pad</item>
|
||||
<item>@string/gamepad_left_stick</item>
|
||||
<item>@string/gamepad_right_stick</item>
|
||||
<item>L3</item>
|
||||
<item>R3</item>
|
||||
<item>@string/gamepad_home</item>
|
||||
<item>@string/gamepad_screenshot</item>
|
||||
</string-array>
|
||||
|
@ -33,6 +33,10 @@
|
||||
<integer name="SWITCH_BUTTON_CAPTURE_Y">950</integer>
|
||||
<integer name="SWITCH_BUTTON_DPAD_X">260</integer>
|
||||
<integer name="SWITCH_BUTTON_DPAD_Y">790</integer>
|
||||
<integer name="SWITCH_BUTTON_STICK_L_X">870</integer>
|
||||
<integer name="SWITCH_BUTTON_STICK_L_Y">400</integer>
|
||||
<integer name="SWITCH_BUTTON_STICK_R_X">960</integer>
|
||||
<integer name="SWITCH_BUTTON_STICK_R_Y">430</integer>
|
||||
|
||||
<!-- Default SWITCH portrait layout -->
|
||||
<integer name="SWITCH_BUTTON_A_X_PORTRAIT">840</integer>
|
||||
@ -65,6 +69,10 @@
|
||||
<integer name="SWITCH_BUTTON_CAPTURE_Y_PORTRAIT">950</integer>
|
||||
<integer name="SWITCH_BUTTON_DPAD_X_PORTRAIT">240</integer>
|
||||
<integer name="SWITCH_BUTTON_DPAD_Y_PORTRAIT">840</integer>
|
||||
<integer name="SWITCH_BUTTON_STICK_L_X_PORTRAIT">730</integer>
|
||||
<integer name="SWITCH_BUTTON_STICK_L_Y_PORTRAIT">510</integer>
|
||||
<integer name="SWITCH_BUTTON_STICK_R_X_PORTRAIT">900</integer>
|
||||
<integer name="SWITCH_BUTTON_STICK_R_Y_PORTRAIT">540</integer>
|
||||
|
||||
<!-- Default SWITCH foldable layout -->
|
||||
<integer name="SWITCH_BUTTON_A_X_FOLDABLE">840</integer>
|
||||
@ -97,5 +105,9 @@
|
||||
<integer name="SWITCH_BUTTON_CAPTURE_Y_FOLDABLE">470</integer>
|
||||
<integer name="SWITCH_BUTTON_DPAD_X_FOLDABLE">240</integer>
|
||||
<integer name="SWITCH_BUTTON_DPAD_Y_FOLDABLE">390</integer>
|
||||
<integer name="SWITCH_BUTTON_STICK_L_X_FOLDABLE">550</integer>
|
||||
<integer name="SWITCH_BUTTON_STICK_L_Y_FOLDABLE">210</integer>
|
||||
<integer name="SWITCH_BUTTON_STICK_R_X_FOLDABLE">550</integer>
|
||||
<integer name="SWITCH_BUTTON_STICK_R_Y_FOLDABLE">280</integer>
|
||||
|
||||
</resources>
|
||||
|
@ -113,6 +113,8 @@
|
||||
<string name="install_game_content_success_install">%1$d installed successfully</string>
|
||||
<string name="install_game_content_success_overwrite">%1$d overwritten successfully</string>
|
||||
<string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string>
|
||||
<string name="custom_driver_not_supported">Custom drivers not supported</string>
|
||||
<string name="custom_driver_not_supported_description">Custom driver loading isn\'t currently supported for this device.\nCheck this option again in the future to see if support was added!</string>
|
||||
|
||||
<!-- About screen strings -->
|
||||
<string name="gaia_is_not_real">Gaia isn\'t real</string>
|
||||
|
@ -92,9 +92,9 @@ void DeviceSession::AppendBuffers(std::span<const AudioBuffer> buffers) {
|
||||
if (type == Sink::StreamType::In) {
|
||||
stream->AppendBuffer(new_buffer, tmp_samples);
|
||||
} else {
|
||||
system.ApplicationMemory().ReadBlockUnsafe(buffer.samples, tmp_samples.data(),
|
||||
buffer.size);
|
||||
stream->AppendBuffer(new_buffer, tmp_samples);
|
||||
Core::Memory::CpuGuestMemory<s16, Core::Memory::GuestMemoryFlags::UnsafeRead> samples(
|
||||
system.ApplicationMemory(), buffer.samples, buffer.size / sizeof(s16));
|
||||
stream->AppendBuffer(new_buffer, samples);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,6 @@ constexpr std::array<u8, 3> PitchBySrcQuality = {4, 8, 4};
|
||||
template <typename T>
|
||||
static u32 DecodePcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,
|
||||
const DecodeArg& req) {
|
||||
std::array<T, TempBufferSize> tmp_samples{};
|
||||
constexpr s32 min{std::numeric_limits<s16>::min()};
|
||||
constexpr s32 max{std::numeric_limits<s16>::max()};
|
||||
|
||||
@ -49,19 +48,18 @@ static u32 DecodePcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,
|
||||
const VAddr source{req.buffer +
|
||||
(((req.start_offset + req.offset) * channel_count) * sizeof(T))};
|
||||
const u64 size{channel_count * samples_to_decode};
|
||||
const u64 size_bytes{size * sizeof(T)};
|
||||
|
||||
memory.ReadBlockUnsafe(source, tmp_samples.data(), size_bytes);
|
||||
|
||||
Core::Memory::CpuGuestMemory<T, Core::Memory::GuestMemoryFlags::UnsafeRead> samples(
|
||||
memory, source, size);
|
||||
if constexpr (std::is_floating_point_v<T>) {
|
||||
for (u32 i = 0; i < samples_to_decode; i++) {
|
||||
auto sample{static_cast<s32>(tmp_samples[i * channel_count + req.target_channel] *
|
||||
auto sample{static_cast<s32>(samples[i * channel_count + req.target_channel] *
|
||||
std::numeric_limits<s16>::max())};
|
||||
out_buffer[i] = static_cast<s16>(std::clamp(sample, min, max));
|
||||
}
|
||||
} else {
|
||||
for (u32 i = 0; i < samples_to_decode; i++) {
|
||||
out_buffer[i] = tmp_samples[i * channel_count + req.target_channel];
|
||||
out_buffer[i] = samples[i * channel_count + req.target_channel];
|
||||
}
|
||||
}
|
||||
} break;
|
||||
@ -74,16 +72,17 @@ static u32 DecodePcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,
|
||||
}
|
||||
|
||||
const VAddr source{req.buffer + ((req.start_offset + req.offset) * sizeof(T))};
|
||||
memory.ReadBlockUnsafe(source, tmp_samples.data(), samples_to_decode * sizeof(T));
|
||||
Core::Memory::CpuGuestMemory<T, Core::Memory::GuestMemoryFlags::UnsafeRead> samples(
|
||||
memory, source, samples_to_decode);
|
||||
|
||||
if constexpr (std::is_floating_point_v<T>) {
|
||||
for (u32 i = 0; i < samples_to_decode; i++) {
|
||||
auto sample{static_cast<s32>(tmp_samples[i * channel_count + req.target_channel] *
|
||||
auto sample{static_cast<s32>(samples[i * channel_count + req.target_channel] *
|
||||
std::numeric_limits<s16>::max())};
|
||||
out_buffer[i] = static_cast<s16>(std::clamp(sample, min, max));
|
||||
}
|
||||
} else {
|
||||
std::memcpy(out_buffer.data(), tmp_samples.data(), samples_to_decode * sizeof(s16));
|
||||
std::memcpy(out_buffer.data(), samples.data(), samples_to_decode * sizeof(s16));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -101,7 +100,6 @@ static u32 DecodePcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,
|
||||
*/
|
||||
static u32 DecodeAdpcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,
|
||||
const DecodeArg& req) {
|
||||
std::array<u8, TempBufferSize> wavebuffer{};
|
||||
constexpr u32 SamplesPerFrame{14};
|
||||
constexpr u32 NibblesPerFrame{16};
|
||||
|
||||
@ -139,7 +137,8 @@ static u32 DecodeAdpcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,
|
||||
}
|
||||
|
||||
const auto size{std::max((samples_to_process / 8U) * SamplesPerFrame, 8U)};
|
||||
memory.ReadBlockUnsafe(req.buffer + position_in_frame / 2, wavebuffer.data(), size);
|
||||
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::UnsafeRead> wavebuffer(
|
||||
memory, req.buffer + position_in_frame / 2, size);
|
||||
|
||||
auto context{req.adpcm_context};
|
||||
auto header{context->header};
|
||||
|
@ -21,23 +21,13 @@ static void ResetAuxBufferDsp(Core::Memory::Memory& memory, const CpuAddr aux_in
|
||||
}
|
||||
|
||||
AuxInfo::AuxInfoDsp info{};
|
||||
auto info_ptr{&info};
|
||||
bool host_safe{(aux_info & Core::Memory::YUZU_PAGEMASK) <=
|
||||
(Core::Memory::YUZU_PAGESIZE - sizeof(AuxInfo::AuxInfoDsp))};
|
||||
memory.ReadBlockUnsafe(aux_info, &info, sizeof(AuxInfo::AuxInfoDsp));
|
||||
|
||||
if (host_safe) [[likely]] {
|
||||
info_ptr = memory.GetPointer<AuxInfo::AuxInfoDsp>(aux_info);
|
||||
} else {
|
||||
memory.ReadBlockUnsafe(aux_info, info_ptr, sizeof(AuxInfo::AuxInfoDsp));
|
||||
}
|
||||
info.read_offset = 0;
|
||||
info.write_offset = 0;
|
||||
info.total_sample_count = 0;
|
||||
|
||||
info_ptr->read_offset = 0;
|
||||
info_ptr->write_offset = 0;
|
||||
info_ptr->total_sample_count = 0;
|
||||
|
||||
if (!host_safe) [[unlikely]] {
|
||||
memory.WriteBlockUnsafe(aux_info, info_ptr, sizeof(AuxInfo::AuxInfoDsp));
|
||||
}
|
||||
memory.WriteBlockUnsafe(aux_info, &info, sizeof(AuxInfo::AuxInfoDsp));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -86,17 +76,9 @@ static u32 WriteAuxBufferDsp(Core::Memory::Memory& memory, CpuAddr send_info_,
|
||||
}
|
||||
|
||||
AuxInfo::AuxInfoDsp send_info{};
|
||||
auto send_ptr = &send_info;
|
||||
bool host_safe = (send_info_ & Core::Memory::YUZU_PAGEMASK) <=
|
||||
(Core::Memory::YUZU_PAGESIZE - sizeof(AuxInfo::AuxInfoDsp));
|
||||
memory.ReadBlockUnsafe(send_info_, &send_info, sizeof(AuxInfo::AuxInfoDsp));
|
||||
|
||||
if (host_safe) [[likely]] {
|
||||
send_ptr = memory.GetPointer<AuxInfo::AuxInfoDsp>(send_info_);
|
||||
} else {
|
||||
memory.ReadBlockUnsafe(send_info_, send_ptr, sizeof(AuxInfo::AuxInfoDsp));
|
||||
}
|
||||
|
||||
u32 target_write_offset{send_ptr->write_offset + write_offset};
|
||||
u32 target_write_offset{send_info.write_offset + write_offset};
|
||||
if (target_write_offset > count_max) {
|
||||
return 0;
|
||||
}
|
||||
@ -105,15 +87,9 @@ static u32 WriteAuxBufferDsp(Core::Memory::Memory& memory, CpuAddr send_info_,
|
||||
u32 read_pos{0};
|
||||
while (write_count > 0) {
|
||||
u32 to_write{std::min(count_max - target_write_offset, write_count)};
|
||||
const auto write_addr = send_buffer + target_write_offset * sizeof(s32);
|
||||
bool write_safe{(write_addr & Core::Memory::YUZU_PAGEMASK) <=
|
||||
(Core::Memory::YUZU_PAGESIZE - (write_addr + to_write * sizeof(s32)))};
|
||||
if (write_safe) [[likely]] {
|
||||
auto ptr = memory.GetPointer(write_addr);
|
||||
std::memcpy(ptr, &input[read_pos], to_write * sizeof(s32));
|
||||
} else {
|
||||
memory.WriteBlockUnsafe(send_buffer + target_write_offset * sizeof(s32),
|
||||
&input[read_pos], to_write * sizeof(s32));
|
||||
if (to_write > 0) {
|
||||
const auto write_addr = send_buffer + target_write_offset * sizeof(s32);
|
||||
memory.WriteBlockUnsafe(write_addr, &input[read_pos], to_write * sizeof(s32));
|
||||
}
|
||||
target_write_offset = (target_write_offset + to_write) % count_max;
|
||||
write_count -= to_write;
|
||||
@ -121,13 +97,10 @@ static u32 WriteAuxBufferDsp(Core::Memory::Memory& memory, CpuAddr send_info_,
|
||||
}
|
||||
|
||||
if (update_count) {
|
||||
send_ptr->write_offset = (send_ptr->write_offset + update_count) % count_max;
|
||||
}
|
||||
|
||||
if (!host_safe) [[unlikely]] {
|
||||
memory.WriteBlockUnsafe(send_info_, send_ptr, sizeof(AuxInfo::AuxInfoDsp));
|
||||
send_info.write_offset = (send_info.write_offset + update_count) % count_max;
|
||||
}
|
||||
|
||||
memory.WriteBlockUnsafe(send_info_, &send_info, sizeof(AuxInfo::AuxInfoDsp));
|
||||
return write_count_;
|
||||
}
|
||||
|
||||
@ -174,17 +147,9 @@ static u32 ReadAuxBufferDsp(Core::Memory::Memory& memory, CpuAddr return_info_,
|
||||
}
|
||||
|
||||
AuxInfo::AuxInfoDsp return_info{};
|
||||
auto return_ptr = &return_info;
|
||||
bool host_safe = (return_info_ & Core::Memory::YUZU_PAGEMASK) <=
|
||||
(Core::Memory::YUZU_PAGESIZE - sizeof(AuxInfo::AuxInfoDsp));
|
||||
memory.ReadBlockUnsafe(return_info_, &return_info, sizeof(AuxInfo::AuxInfoDsp));
|
||||
|
||||
if (host_safe) [[likely]] {
|
||||
return_ptr = memory.GetPointer<AuxInfo::AuxInfoDsp>(return_info_);
|
||||
} else {
|
||||
memory.ReadBlockUnsafe(return_info_, return_ptr, sizeof(AuxInfo::AuxInfoDsp));
|
||||
}
|
||||
|
||||
u32 target_read_offset{return_ptr->read_offset + read_offset};
|
||||
u32 target_read_offset{return_info.read_offset + read_offset};
|
||||
if (target_read_offset > count_max) {
|
||||
return 0;
|
||||
}
|
||||
@ -193,15 +158,9 @@ static u32 ReadAuxBufferDsp(Core::Memory::Memory& memory, CpuAddr return_info_,
|
||||
u32 write_pos{0};
|
||||
while (read_count > 0) {
|
||||
u32 to_read{std::min(count_max - target_read_offset, read_count)};
|
||||
const auto read_addr = return_buffer + target_read_offset * sizeof(s32);
|
||||
bool read_safe{(read_addr & Core::Memory::YUZU_PAGEMASK) <=
|
||||
(Core::Memory::YUZU_PAGESIZE - (read_addr + to_read * sizeof(s32)))};
|
||||
if (read_safe) [[likely]] {
|
||||
auto ptr = memory.GetPointer(read_addr);
|
||||
std::memcpy(&output[write_pos], ptr, to_read * sizeof(s32));
|
||||
} else {
|
||||
memory.ReadBlockUnsafe(return_buffer + target_read_offset * sizeof(s32),
|
||||
&output[write_pos], to_read * sizeof(s32));
|
||||
if (to_read > 0) {
|
||||
const auto read_addr = return_buffer + target_read_offset * sizeof(s32);
|
||||
memory.ReadBlockUnsafe(read_addr, &output[write_pos], to_read * sizeof(s32));
|
||||
}
|
||||
target_read_offset = (target_read_offset + to_read) % count_max;
|
||||
read_count -= to_read;
|
||||
@ -209,13 +168,10 @@ static u32 ReadAuxBufferDsp(Core::Memory::Memory& memory, CpuAddr return_info_,
|
||||
}
|
||||
|
||||
if (update_count) {
|
||||
return_ptr->read_offset = (return_ptr->read_offset + update_count) % count_max;
|
||||
}
|
||||
|
||||
if (!host_safe) [[unlikely]] {
|
||||
memory.WriteBlockUnsafe(return_info_, return_ptr, sizeof(AuxInfo::AuxInfoDsp));
|
||||
return_info.read_offset = (return_info.read_offset + update_count) % count_max;
|
||||
}
|
||||
|
||||
memory.WriteBlockUnsafe(return_info_, &return_info, sizeof(AuxInfo::AuxInfoDsp));
|
||||
return read_count_;
|
||||
}
|
||||
|
||||
|
@ -66,6 +66,7 @@ void PageTable::Resize(std::size_t address_space_width_in_bits, std::size_t page
|
||||
<< (address_space_width_in_bits - page_size_in_bits)};
|
||||
pointers.resize(num_page_table_entries);
|
||||
backing_addr.resize(num_page_table_entries);
|
||||
blocks.resize(num_page_table_entries);
|
||||
current_address_space_width_in_bits = address_space_width_in_bits;
|
||||
page_size = 1ULL << page_size_in_bits;
|
||||
}
|
||||
|
@ -122,6 +122,7 @@ struct PageTable {
|
||||
* corresponding attribute element is of type `Memory`.
|
||||
*/
|
||||
VirtualBuffer<PageInfo> pointers;
|
||||
VirtualBuffer<u64> blocks;
|
||||
|
||||
VirtualBuffer<u64> backing_addr;
|
||||
|
||||
|
@ -40,8 +40,21 @@ public:
|
||||
~ScratchBuffer() = default;
|
||||
ScratchBuffer(const ScratchBuffer&) = delete;
|
||||
ScratchBuffer& operator=(const ScratchBuffer&) = delete;
|
||||
ScratchBuffer(ScratchBuffer&&) = default;
|
||||
ScratchBuffer& operator=(ScratchBuffer&&) = default;
|
||||
|
||||
ScratchBuffer(ScratchBuffer&& other) noexcept {
|
||||
swap(other);
|
||||
other.last_requested_size = 0;
|
||||
other.buffer_capacity = 0;
|
||||
other.buffer.reset();
|
||||
}
|
||||
|
||||
ScratchBuffer& operator=(ScratchBuffer&& other) noexcept {
|
||||
swap(other);
|
||||
other.last_requested_size = 0;
|
||||
other.buffer_capacity = 0;
|
||||
other.buffer.reset();
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// This will only grow the buffer's capacity if size is greater than the current capacity.
|
||||
/// The previously held data will remain intact.
|
||||
|
@ -27,8 +27,8 @@ std::string GetTimeZoneString() {
|
||||
std::string location_name;
|
||||
if (time_zone_index == 0) { // Auto
|
||||
#if __cpp_lib_chrono >= 201907L
|
||||
const struct std::chrono::tzdb& time_zone_data = std::chrono::get_tzdb();
|
||||
try {
|
||||
const struct std::chrono::tzdb& time_zone_data = std::chrono::get_tzdb();
|
||||
const std::chrono::time_zone* current_zone = time_zone_data.current_zone();
|
||||
std::string_view current_zone_name = current_zone->name();
|
||||
location_name = current_zone_name;
|
||||
|
@ -285,6 +285,7 @@ add_library(core STATIC
|
||||
hle/kernel/kernel.cpp
|
||||
hle/kernel/kernel.h
|
||||
hle/kernel/memory_types.h
|
||||
hle/kernel/message_buffer.h
|
||||
hle/kernel/physical_core.cpp
|
||||
hle/kernel/physical_core.h
|
||||
hle/kernel/physical_memory.h
|
||||
|
@ -185,7 +185,7 @@ void ARM_Interface::Run() {
|
||||
// Notify the debugger and go to sleep if a breakpoint was hit,
|
||||
// or if the thread is unable to continue for any reason.
|
||||
if (True(hr & HaltReason::InstructionBreakpoint) || True(hr & HaltReason::PrefetchAbort)) {
|
||||
if (!True(hr & HaltReason::InstructionBreakpoint)) {
|
||||
if (!True(hr & HaltReason::PrefetchAbort)) {
|
||||
RewindBreakpointInstruction();
|
||||
}
|
||||
if (system.DebuggerEnabled()) {
|
||||
|
@ -543,6 +543,7 @@ struct System::Impl {
|
||||
|
||||
ExecuteProgramCallback execute_program_callback;
|
||||
ExitCallback exit_callback;
|
||||
GameLogCallback game_log_callback;
|
||||
|
||||
std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{};
|
||||
std::array<MicroProfileToken, Core::Hardware::NUM_CPU_CORES> microprofile_cpu{};
|
||||
@ -1011,6 +1012,18 @@ void System::RegisterExitCallback(ExitCallback&& callback) {
|
||||
impl->exit_callback = std::move(callback);
|
||||
}
|
||||
|
||||
void System::RegisterGameLogCallback(GameLogCallback&& callback) {
|
||||
impl->game_log_callback = std::move(callback);
|
||||
}
|
||||
|
||||
void System::AppendGameMessage(std::string& text) {
|
||||
if (impl->game_log_callback) {
|
||||
impl->game_log_callback(text);
|
||||
} else {
|
||||
LOG_CRITICAL(Core, "game_log_callback must be initialized by the frontend");
|
||||
}
|
||||
}
|
||||
|
||||
void System::Exit() {
|
||||
if (impl->exit_callback) {
|
||||
impl->exit_callback();
|
||||
|
@ -462,6 +462,18 @@ public:
|
||||
*/
|
||||
void RegisterExitCallback(ExitCallback&& callback);
|
||||
|
||||
/// Type used for the frontend to designate a callback for System to display log output.
|
||||
using GameLogCallback = std::function<void(std::string)>;
|
||||
|
||||
/**
|
||||
* Registers a callback from the frontend for System to display log output.
|
||||
* @param callback Callback from the frontend to display log output.
|
||||
*/
|
||||
void RegisterGameLogCallback(GameLogCallback&& callback);
|
||||
|
||||
/// Inserts a new entry on the game log ouput window
|
||||
void AppendGameMessage(std::string& text);
|
||||
|
||||
/// Instructs the frontend to exit the application.
|
||||
void Exit();
|
||||
|
||||
|
@ -70,7 +70,7 @@ void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) {
|
||||
-> std::optional<std::chrono::nanoseconds> { return std::nullopt; };
|
||||
ev_lost = CreateEvent("_lost_event", empty_timed_callback);
|
||||
if (is_multicore) {
|
||||
timer_thread = std::make_unique<std::thread>(ThreadEntry, std::ref(*this));
|
||||
timer_thread = std::make_unique<std::jthread>(ThreadEntry, std::ref(*this));
|
||||
}
|
||||
}
|
||||
|
||||
@ -255,7 +255,6 @@ void CoreTiming::ThreadLoop() {
|
||||
#ifdef _WIN32
|
||||
while (!paused && !event.IsSet() && wait_time > 0) {
|
||||
wait_time = *next_time - GetGlobalTimeNs().count();
|
||||
|
||||
if (wait_time >= timer_resolution_ns) {
|
||||
Common::Windows::SleepForOneTick();
|
||||
} else {
|
||||
|
@ -163,7 +163,7 @@ private:
|
||||
Common::Event pause_event{};
|
||||
std::mutex basic_lock;
|
||||
std::mutex advance_lock;
|
||||
std::unique_ptr<std::thread> timer_thread;
|
||||
std::unique_ptr<std::jthread> timer_thread;
|
||||
std::atomic<bool> paused{};
|
||||
std::atomic<bool> paused_set{};
|
||||
std::atomic<bool> wait_set{};
|
||||
|
@ -57,11 +57,34 @@ struct NCASectionHeaderBlock {
|
||||
};
|
||||
static_assert(sizeof(NCASectionHeaderBlock) == 0x8, "NCASectionHeaderBlock has incorrect size.");
|
||||
|
||||
struct NCABucketInfo {
|
||||
u64 table_offset;
|
||||
u64 table_size;
|
||||
std::array<u8, 0x10> table_header;
|
||||
};
|
||||
static_assert(sizeof(NCABucketInfo) == 0x20, "NCABucketInfo has incorrect size.");
|
||||
|
||||
struct NCASparseInfo {
|
||||
NCABucketInfo bucket;
|
||||
u64 physical_offset;
|
||||
u16 generation;
|
||||
INSERT_PADDING_BYTES_NOINIT(0x6);
|
||||
};
|
||||
static_assert(sizeof(NCASparseInfo) == 0x30, "NCASparseInfo has incorrect size.");
|
||||
|
||||
struct NCACompressionInfo {
|
||||
NCABucketInfo bucket;
|
||||
INSERT_PADDING_BYTES_NOINIT(0x8);
|
||||
};
|
||||
static_assert(sizeof(NCACompressionInfo) == 0x28, "NCACompressionInfo has incorrect size.");
|
||||
|
||||
struct NCASectionRaw {
|
||||
NCASectionHeaderBlock header;
|
||||
std::array<u8, 0x138> block_data;
|
||||
std::array<u8, 0x8> section_ctr;
|
||||
INSERT_PADDING_BYTES_NOINIT(0xB8);
|
||||
NCASparseInfo sparse_info;
|
||||
NCACompressionInfo compression_info;
|
||||
INSERT_PADDING_BYTES_NOINIT(0x60);
|
||||
};
|
||||
static_assert(sizeof(NCASectionRaw) == 0x200, "NCASectionRaw has incorrect size.");
|
||||
|
||||
@ -225,6 +248,20 @@ bool NCA::ReadSections(const std::vector<NCASectionHeader>& sections, u64 bktr_b
|
||||
for (std::size_t i = 0; i < sections.size(); ++i) {
|
||||
const auto& section = sections[i];
|
||||
|
||||
if (section.raw.sparse_info.bucket.table_offset != 0 &&
|
||||
section.raw.sparse_info.bucket.table_size != 0) {
|
||||
LOG_ERROR(Loader, "Sparse NCAs are not supported.");
|
||||
status = Loader::ResultStatus::ErrorSparseNCA;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (section.raw.compression_info.bucket.table_offset != 0 &&
|
||||
section.raw.compression_info.bucket.table_size != 0) {
|
||||
LOG_ERROR(Loader, "Compressed NCAs are not supported.");
|
||||
status = Loader::ResultStatus::ErrorCompressedNCA;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (section.raw.header.filesystem_type == NCASectionFilesystemType::ROMFS) {
|
||||
if (!ReadRomFSSection(section, header.section_tables[i], bktr_base_ivfc_offset)) {
|
||||
return false;
|
||||
|
@ -283,7 +283,8 @@ std::size_t RealVfsFile::GetSize() const {
|
||||
if (size) {
|
||||
return *size;
|
||||
}
|
||||
return FS::GetSize(path);
|
||||
auto lk = base.RefreshReference(path, perms, *reference);
|
||||
return reference->file ? reference->file->GetSize() : 0;
|
||||
}
|
||||
|
||||
bool RealVfsFile::Resize(std::size_t new_size) {
|
||||
|
@ -20,12 +20,132 @@
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/k_thread_queue.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/message_buffer.h"
|
||||
#include "core/hle/service/hle_ipc.h"
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
namespace {
|
||||
|
||||
template <bool MoveHandleAllowed>
|
||||
Result ProcessMessageSpecialData(KProcess& dst_process, KProcess& src_process, KThread& src_thread,
|
||||
MessageBuffer& dst_msg, const MessageBuffer& src_msg,
|
||||
MessageBuffer::SpecialHeader& src_special_header) {
|
||||
// Copy the special header to the destination.
|
||||
s32 offset = dst_msg.Set(src_special_header);
|
||||
|
||||
// Copy the process ID.
|
||||
if (src_special_header.GetHasProcessId()) {
|
||||
offset = dst_msg.SetProcessId(offset, src_process.GetProcessId());
|
||||
}
|
||||
|
||||
// Prepare to process handles.
|
||||
auto& dst_handle_table = dst_process.GetHandleTable();
|
||||
auto& src_handle_table = src_process.GetHandleTable();
|
||||
Result result = ResultSuccess;
|
||||
|
||||
// Process copy handles.
|
||||
for (auto i = 0; i < src_special_header.GetCopyHandleCount(); ++i) {
|
||||
// Get the handles.
|
||||
const Handle src_handle = src_msg.GetHandle(offset);
|
||||
Handle dst_handle = Svc::InvalidHandle;
|
||||
|
||||
// If we're in a success state, try to move the handle to the new table.
|
||||
if (R_SUCCEEDED(result) && src_handle != Svc::InvalidHandle) {
|
||||
KScopedAutoObject obj =
|
||||
src_handle_table.GetObjectForIpc(src_handle, std::addressof(src_thread));
|
||||
if (obj.IsNotNull()) {
|
||||
Result add_result =
|
||||
dst_handle_table.Add(std::addressof(dst_handle), obj.GetPointerUnsafe());
|
||||
if (R_FAILED(add_result)) {
|
||||
result = add_result;
|
||||
dst_handle = Svc::InvalidHandle;
|
||||
}
|
||||
} else {
|
||||
result = ResultInvalidHandle;
|
||||
}
|
||||
}
|
||||
|
||||
// Set the handle.
|
||||
offset = dst_msg.SetHandle(offset, dst_handle);
|
||||
}
|
||||
|
||||
// Process move handles.
|
||||
if constexpr (MoveHandleAllowed) {
|
||||
for (auto i = 0; i < src_special_header.GetMoveHandleCount(); ++i) {
|
||||
// Get the handles.
|
||||
const Handle src_handle = src_msg.GetHandle(offset);
|
||||
Handle dst_handle = Svc::InvalidHandle;
|
||||
|
||||
// Whether or not we've succeeded, we need to remove the handles from the source table.
|
||||
if (src_handle != Svc::InvalidHandle) {
|
||||
if (R_SUCCEEDED(result)) {
|
||||
KScopedAutoObject obj =
|
||||
src_handle_table.GetObjectForIpcWithoutPseudoHandle(src_handle);
|
||||
if (obj.IsNotNull()) {
|
||||
Result add_result = dst_handle_table.Add(std::addressof(dst_handle),
|
||||
obj.GetPointerUnsafe());
|
||||
|
||||
src_handle_table.Remove(src_handle);
|
||||
|
||||
if (R_FAILED(add_result)) {
|
||||
result = add_result;
|
||||
dst_handle = Svc::InvalidHandle;
|
||||
}
|
||||
} else {
|
||||
result = ResultInvalidHandle;
|
||||
}
|
||||
} else {
|
||||
src_handle_table.Remove(src_handle);
|
||||
}
|
||||
}
|
||||
|
||||
// Set the handle.
|
||||
offset = dst_msg.SetHandle(offset, dst_handle);
|
||||
}
|
||||
}
|
||||
|
||||
R_RETURN(result);
|
||||
}
|
||||
|
||||
void CleanupSpecialData(KProcess& dst_process, u32* dst_msg_ptr, size_t dst_buffer_size) {
|
||||
// Parse the message.
|
||||
const MessageBuffer dst_msg(dst_msg_ptr, dst_buffer_size);
|
||||
const MessageBuffer::MessageHeader dst_header(dst_msg);
|
||||
const MessageBuffer::SpecialHeader dst_special_header(dst_msg, dst_header);
|
||||
|
||||
// Check that the size is big enough.
|
||||
if (MessageBuffer::GetMessageBufferSize(dst_header, dst_special_header) > dst_buffer_size) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the special header.
|
||||
int offset = dst_msg.Set(dst_special_header);
|
||||
|
||||
// Clear the process id, if needed.
|
||||
if (dst_special_header.GetHasProcessId()) {
|
||||
offset = dst_msg.SetProcessId(offset, 0);
|
||||
}
|
||||
|
||||
// Clear handles, as relevant.
|
||||
auto& dst_handle_table = dst_process.GetHandleTable();
|
||||
for (auto i = 0;
|
||||
i < (dst_special_header.GetCopyHandleCount() + dst_special_header.GetMoveHandleCount());
|
||||
++i) {
|
||||
const Handle handle = dst_msg.GetHandle(offset);
|
||||
|
||||
if (handle != Svc::InvalidHandle) {
|
||||
dst_handle_table.Remove(handle);
|
||||
}
|
||||
|
||||
offset = dst_msg.SetHandle(offset, Svc::InvalidHandle);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
using ThreadQueueImplForKServerSessionRequest = KThreadQueue;
|
||||
|
||||
KServerSession::KServerSession(KernelCore& kernel)
|
||||
@ -223,12 +343,27 @@ Result KServerSession::SendReply(bool is_hle) {
|
||||
// the reply has already been written in this case.
|
||||
} else {
|
||||
Core::Memory::Memory& memory{client_thread->GetOwnerProcess()->GetMemory()};
|
||||
KThread* server_thread{GetCurrentThreadPointer(m_kernel)};
|
||||
KThread* server_thread = GetCurrentThreadPointer(m_kernel);
|
||||
KProcess& src_process = *client_thread->GetOwnerProcess();
|
||||
KProcess& dst_process = *server_thread->GetOwnerProcess();
|
||||
UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess());
|
||||
|
||||
auto* src_msg_buffer = memory.GetPointer(server_thread->GetTlsAddress());
|
||||
auto* dst_msg_buffer = memory.GetPointer(client_message);
|
||||
auto* src_msg_buffer = memory.GetPointer<u32>(server_thread->GetTlsAddress());
|
||||
auto* dst_msg_buffer = memory.GetPointer<u32>(client_message);
|
||||
std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size);
|
||||
|
||||
// Translate special header ad-hoc.
|
||||
MessageBuffer src_msg(src_msg_buffer, client_buffer_size);
|
||||
MessageBuffer::MessageHeader src_header(src_msg);
|
||||
MessageBuffer::SpecialHeader src_special_header(src_msg, src_header);
|
||||
if (src_header.GetHasSpecialHeader()) {
|
||||
MessageBuffer dst_msg(dst_msg_buffer, client_buffer_size);
|
||||
result = ProcessMessageSpecialData<true>(dst_process, src_process, *server_thread,
|
||||
dst_msg, src_msg, src_special_header);
|
||||
if (R_FAILED(result)) {
|
||||
CleanupSpecialData(dst_process, dst_msg_buffer, client_buffer_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result = ResultSessionClosed;
|
||||
@ -330,12 +465,28 @@ Result KServerSession::ReceiveRequest(std::shared_ptr<Service::HLERequestContext
|
||||
->PopulateFromIncomingCommandBuffer(client_thread->GetOwnerProcess()->GetHandleTable(),
|
||||
cmd_buf);
|
||||
} else {
|
||||
KThread* server_thread{GetCurrentThreadPointer(m_kernel)};
|
||||
UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess());
|
||||
KThread* server_thread = GetCurrentThreadPointer(m_kernel);
|
||||
KProcess& src_process = *client_thread->GetOwnerProcess();
|
||||
KProcess& dst_process = *server_thread->GetOwnerProcess();
|
||||
UNIMPLEMENTED_IF(client_thread->GetOwnerProcess() != server_thread->GetOwnerProcess());
|
||||
|
||||
auto* src_msg_buffer = memory.GetPointer(client_message);
|
||||
auto* dst_msg_buffer = memory.GetPointer(server_thread->GetTlsAddress());
|
||||
auto* src_msg_buffer = memory.GetPointer<u32>(client_message);
|
||||
auto* dst_msg_buffer = memory.GetPointer<u32>(server_thread->GetTlsAddress());
|
||||
std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size);
|
||||
|
||||
// Translate special header ad-hoc.
|
||||
// TODO: fix this mess
|
||||
MessageBuffer src_msg(src_msg_buffer, client_buffer_size);
|
||||
MessageBuffer::MessageHeader src_header(src_msg);
|
||||
MessageBuffer::SpecialHeader src_special_header(src_msg, src_header);
|
||||
if (src_header.GetHasSpecialHeader()) {
|
||||
MessageBuffer dst_msg(dst_msg_buffer, client_buffer_size);
|
||||
Result res = ProcessMessageSpecialData<false>(dst_process, src_process, *client_thread,
|
||||
dst_msg, src_msg, src_special_header);
|
||||
if (R_FAILED(res)) {
|
||||
CleanupSpecialData(dst_process, dst_msg_buffer, client_buffer_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We succeeded.
|
||||
|
612
src/core/hle/kernel/message_buffer.h
Normal file
612
src/core/hle/kernel/message_buffer.h
Normal file
@ -0,0 +1,612 @@
|
||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/bit_field.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
constexpr inline size_t MessageBufferSize = 0x100;
|
||||
|
||||
class MessageBuffer {
|
||||
public:
|
||||
class MessageHeader {
|
||||
private:
|
||||
static constexpr inline u64 NullTag = 0;
|
||||
|
||||
public:
|
||||
enum class ReceiveListCountType : u32 {
|
||||
None = 0,
|
||||
ToMessageBuffer = 1,
|
||||
ToSingleBuffer = 2,
|
||||
|
||||
CountOffset = 2,
|
||||
CountMax = 13,
|
||||
};
|
||||
|
||||
private:
|
||||
union {
|
||||
std::array<u32, 2> raw;
|
||||
|
||||
struct {
|
||||
// Define fields for the first header word.
|
||||
union {
|
||||
BitField<0, 16, u16> tag;
|
||||
BitField<16, 4, u32> pointer_count;
|
||||
BitField<20, 4, u32> send_count;
|
||||
BitField<24, 4, u32> receive_count;
|
||||
BitField<28, 4, u32> exchange_count;
|
||||
};
|
||||
|
||||
// Define fields for the second header word.
|
||||
union {
|
||||
BitField<0, 10, u32> raw_count;
|
||||
BitField<10, 4, ReceiveListCountType> receive_list_count;
|
||||
BitField<14, 6, u32> reserved0;
|
||||
BitField<20, 11, u32> receive_list_offset;
|
||||
BitField<31, 1, u32> has_special_header;
|
||||
};
|
||||
};
|
||||
} m_header;
|
||||
|
||||
public:
|
||||
constexpr MessageHeader() : m_header{} {}
|
||||
|
||||
constexpr MessageHeader(u16 tag, bool special, s32 ptr, s32 send, s32 recv, s32 exch,
|
||||
s32 raw, ReceiveListCountType recv_list)
|
||||
: m_header{} {
|
||||
m_header.raw[0] = 0;
|
||||
m_header.raw[1] = 0;
|
||||
|
||||
m_header.tag.Assign(tag);
|
||||
m_header.pointer_count.Assign(ptr);
|
||||
m_header.send_count.Assign(send);
|
||||
m_header.receive_count.Assign(recv);
|
||||
m_header.exchange_count.Assign(exch);
|
||||
|
||||
m_header.raw_count.Assign(raw);
|
||||
m_header.receive_list_count.Assign(recv_list);
|
||||
m_header.has_special_header.Assign(special);
|
||||
}
|
||||
|
||||
explicit MessageHeader(const MessageBuffer& buf) : m_header{} {
|
||||
buf.Get(0, m_header.raw.data(), 2);
|
||||
}
|
||||
|
||||
explicit MessageHeader(const u32* msg) : m_header{{msg[0], msg[1]}} {}
|
||||
|
||||
constexpr u16 GetTag() const {
|
||||
return m_header.tag;
|
||||
}
|
||||
|
||||
constexpr s32 GetPointerCount() const {
|
||||
return m_header.pointer_count;
|
||||
}
|
||||
|
||||
constexpr s32 GetSendCount() const {
|
||||
return m_header.send_count;
|
||||
}
|
||||
|
||||
constexpr s32 GetReceiveCount() const {
|
||||
return m_header.receive_count;
|
||||
}
|
||||
|
||||
constexpr s32 GetExchangeCount() const {
|
||||
return m_header.exchange_count;
|
||||
}
|
||||
|
||||
constexpr s32 GetMapAliasCount() const {
|
||||
return this->GetSendCount() + this->GetReceiveCount() + this->GetExchangeCount();
|
||||
}
|
||||
|
||||
constexpr s32 GetRawCount() const {
|
||||
return m_header.raw_count;
|
||||
}
|
||||
|
||||
constexpr ReceiveListCountType GetReceiveListCount() const {
|
||||
return m_header.receive_list_count;
|
||||
}
|
||||
|
||||
constexpr s32 GetReceiveListOffset() const {
|
||||
return m_header.receive_list_offset;
|
||||
}
|
||||
|
||||
constexpr bool GetHasSpecialHeader() const {
|
||||
return m_header.has_special_header.Value() != 0;
|
||||
}
|
||||
|
||||
constexpr void SetReceiveListCount(ReceiveListCountType recv_list) {
|
||||
m_header.receive_list_count.Assign(recv_list);
|
||||
}
|
||||
|
||||
constexpr const u32* GetData() const {
|
||||
return m_header.raw.data();
|
||||
}
|
||||
|
||||
static constexpr size_t GetDataSize() {
|
||||
return sizeof(m_header);
|
||||
}
|
||||
};
|
||||
|
||||
class SpecialHeader {
|
||||
private:
|
||||
union {
|
||||
std::array<u32, 1> raw;
|
||||
|
||||
// Define fields for the header word.
|
||||
BitField<0, 1, u32> has_process_id;
|
||||
BitField<1, 4, u32> copy_handle_count;
|
||||
BitField<5, 4, u32> move_handle_count;
|
||||
} m_header;
|
||||
bool m_has_header;
|
||||
|
||||
public:
|
||||
constexpr explicit SpecialHeader(bool pid, s32 copy, s32 move)
|
||||
: m_header{}, m_has_header(true) {
|
||||
m_header.has_process_id.Assign(pid);
|
||||
m_header.copy_handle_count.Assign(copy);
|
||||
m_header.move_handle_count.Assign(move);
|
||||
}
|
||||
|
||||
constexpr explicit SpecialHeader(bool pid, s32 copy, s32 move, bool _has_header)
|
||||
: m_header{}, m_has_header(_has_header) {
|
||||
m_header.has_process_id.Assign(pid);
|
||||
m_header.copy_handle_count.Assign(copy);
|
||||
m_header.move_handle_count.Assign(move);
|
||||
}
|
||||
|
||||
explicit SpecialHeader(const MessageBuffer& buf, const MessageHeader& hdr)
|
||||
: m_header{}, m_has_header(hdr.GetHasSpecialHeader()) {
|
||||
if (m_has_header) {
|
||||
buf.Get(static_cast<s32>(MessageHeader::GetDataSize() / sizeof(u32)),
|
||||
m_header.raw.data(), sizeof(m_header) / sizeof(u32));
|
||||
}
|
||||
}
|
||||
|
||||
constexpr bool GetHasProcessId() const {
|
||||
return m_header.has_process_id.Value() != 0;
|
||||
}
|
||||
|
||||
constexpr s32 GetCopyHandleCount() const {
|
||||
return m_header.copy_handle_count;
|
||||
}
|
||||
|
||||
constexpr s32 GetMoveHandleCount() const {
|
||||
return m_header.move_handle_count;
|
||||
}
|
||||
|
||||
constexpr const u32* GetHeader() const {
|
||||
return m_header.raw.data();
|
||||
}
|
||||
|
||||
constexpr size_t GetHeaderSize() const {
|
||||
if (m_has_header) {
|
||||
return sizeof(m_header);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr size_t GetDataSize() const {
|
||||
if (m_has_header) {
|
||||
return (this->GetHasProcessId() ? sizeof(u64) : 0) +
|
||||
(this->GetCopyHandleCount() * sizeof(Handle)) +
|
||||
(this->GetMoveHandleCount() * sizeof(Handle));
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class MapAliasDescriptor {
|
||||
public:
|
||||
enum class Attribute : u32 {
|
||||
Ipc = 0,
|
||||
NonSecureIpc = 1,
|
||||
NonDeviceIpc = 3,
|
||||
};
|
||||
|
||||
private:
|
||||
static constexpr u32 SizeLowCount = 32;
|
||||
static constexpr u32 SizeHighCount = 4;
|
||||
static constexpr u32 AddressLowCount = 32;
|
||||
static constexpr u32 AddressMidCount = 4;
|
||||
|
||||
constexpr u32 GetAddressMid(u64 address) {
|
||||
return static_cast<u32>(address >> AddressLowCount) & ((1U << AddressMidCount) - 1);
|
||||
}
|
||||
|
||||
constexpr u32 GetAddressHigh(u64 address) {
|
||||
return static_cast<u32>(address >> (AddressLowCount + AddressMidCount));
|
||||
}
|
||||
|
||||
private:
|
||||
union {
|
||||
std::array<u32, 3> raw;
|
||||
|
||||
struct {
|
||||
// Define fields for the first two words.
|
||||
u32 size_low;
|
||||
u32 address_low;
|
||||
|
||||
// Define fields for the packed descriptor word.
|
||||
union {
|
||||
BitField<0, 2, Attribute> attributes;
|
||||
BitField<2, 3, u32> address_high;
|
||||
BitField<5, 19, u32> reserved;
|
||||
BitField<24, 4, u32> size_high;
|
||||
BitField<28, 4, u32> address_mid;
|
||||
};
|
||||
};
|
||||
} m_data;
|
||||
|
||||
public:
|
||||
constexpr MapAliasDescriptor() : m_data{} {}
|
||||
|
||||
MapAliasDescriptor(const void* buffer, size_t _size, Attribute attr = Attribute::Ipc)
|
||||
: m_data{} {
|
||||
const u64 address = reinterpret_cast<u64>(buffer);
|
||||
const u64 size = static_cast<u64>(_size);
|
||||
m_data.size_low = static_cast<u32>(size);
|
||||
m_data.address_low = static_cast<u32>(address);
|
||||
m_data.attributes.Assign(attr);
|
||||
m_data.address_mid.Assign(GetAddressMid(address));
|
||||
m_data.size_high.Assign(static_cast<u32>(size >> SizeLowCount));
|
||||
m_data.address_high.Assign(GetAddressHigh(address));
|
||||
}
|
||||
|
||||
MapAliasDescriptor(const MessageBuffer& buf, s32 index) : m_data{} {
|
||||
buf.Get(index, m_data.raw.data(), 3);
|
||||
}
|
||||
|
||||
constexpr uintptr_t GetAddress() const {
|
||||
return (static_cast<u64>((m_data.address_high << AddressMidCount) | m_data.address_mid)
|
||||
<< AddressLowCount) |
|
||||
m_data.address_low;
|
||||
}
|
||||
|
||||
constexpr uintptr_t GetSize() const {
|
||||
return (static_cast<u64>(m_data.size_high) << SizeLowCount) | m_data.size_low;
|
||||
}
|
||||
|
||||
constexpr Attribute GetAttribute() const {
|
||||
return m_data.attributes;
|
||||
}
|
||||
|
||||
constexpr const u32* GetData() const {
|
||||
return m_data.raw.data();
|
||||
}
|
||||
|
||||
static constexpr size_t GetDataSize() {
|
||||
return sizeof(m_data);
|
||||
}
|
||||
};
|
||||
|
||||
class PointerDescriptor {
|
||||
private:
|
||||
static constexpr u32 AddressLowCount = 32;
|
||||
static constexpr u32 AddressMidCount = 4;
|
||||
|
||||
constexpr u32 GetAddressMid(u64 address) {
|
||||
return static_cast<u32>(address >> AddressLowCount) & ((1u << AddressMidCount) - 1);
|
||||
}
|
||||
|
||||
constexpr u32 GetAddressHigh(u64 address) {
|
||||
return static_cast<u32>(address >> (AddressLowCount + AddressMidCount));
|
||||
}
|
||||
|
||||
private:
|
||||
union {
|
||||
std::array<u32, 2> raw;
|
||||
|
||||
struct {
|
||||
// Define fields for the packed descriptor word.
|
||||
union {
|
||||
BitField<0, 4, u32> index;
|
||||
BitField<4, 2, u32> reserved0;
|
||||
BitField<6, 3, u32> address_high;
|
||||
BitField<9, 3, u32> reserved1;
|
||||
BitField<12, 4, u32> address_mid;
|
||||
BitField<16, 16, u32> size;
|
||||
};
|
||||
|
||||
// Define fields for the second word.
|
||||
u32 address_low;
|
||||
};
|
||||
} m_data;
|
||||
|
||||
public:
|
||||
constexpr PointerDescriptor() : m_data{} {}
|
||||
|
||||
PointerDescriptor(const void* buffer, size_t size, s32 index) : m_data{} {
|
||||
const u64 address = reinterpret_cast<u64>(buffer);
|
||||
|
||||
m_data.index.Assign(index);
|
||||
m_data.address_high.Assign(GetAddressHigh(address));
|
||||
m_data.address_mid.Assign(GetAddressMid(address));
|
||||
m_data.size.Assign(static_cast<u32>(size));
|
||||
|
||||
m_data.address_low = static_cast<u32>(address);
|
||||
}
|
||||
|
||||
PointerDescriptor(const MessageBuffer& buf, s32 index) : m_data{} {
|
||||
buf.Get(index, m_data.raw.data(), 2);
|
||||
}
|
||||
|
||||
constexpr s32 GetIndex() const {
|
||||
return m_data.index;
|
||||
}
|
||||
|
||||
constexpr uintptr_t GetAddress() const {
|
||||
return (static_cast<u64>((m_data.address_high << AddressMidCount) | m_data.address_mid)
|
||||
<< AddressLowCount) |
|
||||
m_data.address_low;
|
||||
}
|
||||
|
||||
constexpr size_t GetSize() const {
|
||||
return m_data.size;
|
||||
}
|
||||
|
||||
constexpr const u32* GetData() const {
|
||||
return m_data.raw.data();
|
||||
}
|
||||
|
||||
static constexpr size_t GetDataSize() {
|
||||
return sizeof(m_data);
|
||||
}
|
||||
};
|
||||
|
||||
class ReceiveListEntry {
|
||||
private:
|
||||
static constexpr u32 AddressLowCount = 32;
|
||||
|
||||
constexpr u32 GetAddressHigh(u64 address) {
|
||||
return static_cast<u32>(address >> (AddressLowCount));
|
||||
}
|
||||
|
||||
private:
|
||||
union {
|
||||
std::array<u32, 2> raw;
|
||||
|
||||
struct {
|
||||
// Define fields for the first word.
|
||||
u32 address_low;
|
||||
|
||||
// Define fields for the packed descriptor word.
|
||||
union {
|
||||
BitField<0, 7, u32> address_high;
|
||||
BitField<7, 9, u32> reserved;
|
||||
BitField<16, 16, u32> size;
|
||||
};
|
||||
};
|
||||
} m_data;
|
||||
|
||||
public:
|
||||
constexpr ReceiveListEntry() : m_data{} {}
|
||||
|
||||
ReceiveListEntry(const void* buffer, size_t size) : m_data{} {
|
||||
const u64 address = reinterpret_cast<u64>(buffer);
|
||||
|
||||
m_data.address_low = static_cast<u32>(address);
|
||||
|
||||
m_data.address_high.Assign(GetAddressHigh(address));
|
||||
m_data.size.Assign(static_cast<u32>(size));
|
||||
}
|
||||
|
||||
ReceiveListEntry(u32 a, u32 b) : m_data{{a, b}} {}
|
||||
|
||||
constexpr uintptr_t GetAddress() const {
|
||||
return (static_cast<u64>(m_data.address_high) << AddressLowCount) | m_data.address_low;
|
||||
}
|
||||
|
||||
constexpr size_t GetSize() const {
|
||||
return m_data.size;
|
||||
}
|
||||
|
||||
constexpr const u32* GetData() const {
|
||||
return m_data.raw.data();
|
||||
}
|
||||
|
||||
static constexpr size_t GetDataSize() {
|
||||
return sizeof(m_data);
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
u32* m_buffer;
|
||||
size_t m_size;
|
||||
|
||||
public:
|
||||
constexpr MessageBuffer(u32* b, size_t sz) : m_buffer(b), m_size(sz) {}
|
||||
constexpr explicit MessageBuffer(u32* b) : m_buffer(b), m_size(MessageBufferSize) {}
|
||||
|
||||
constexpr void* GetBufferForDebug() const {
|
||||
return m_buffer;
|
||||
}
|
||||
|
||||
constexpr size_t GetBufferSize() const {
|
||||
return m_size;
|
||||
}
|
||||
|
||||
void Get(s32 index, u32* dst, size_t count) const {
|
||||
// Ensure that this doesn't get re-ordered.
|
||||
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||
|
||||
// Get the words.
|
||||
static_assert(sizeof(*dst) == sizeof(*m_buffer));
|
||||
|
||||
memcpy(dst, m_buffer + index, count * sizeof(*dst));
|
||||
}
|
||||
|
||||
s32 Set(s32 index, u32* src, size_t count) const {
|
||||
// Ensure that this doesn't get re-ordered.
|
||||
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||
|
||||
// Set the words.
|
||||
memcpy(m_buffer + index, src, count * sizeof(*src));
|
||||
|
||||
// Ensure that this doesn't get re-ordered.
|
||||
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||
|
||||
return static_cast<s32>(index + count);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
const T& GetRaw(s32 index) const {
|
||||
return *reinterpret_cast<const T*>(m_buffer + index);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
s32 SetRaw(s32 index, const T& val) const {
|
||||
*reinterpret_cast<const T*>(m_buffer + index) = val;
|
||||
return index + (Common::AlignUp(sizeof(val), sizeof(*m_buffer)) / sizeof(*m_buffer));
|
||||
}
|
||||
|
||||
void GetRawArray(s32 index, void* dst, size_t len) const {
|
||||
memcpy(dst, m_buffer + index, len);
|
||||
}
|
||||
|
||||
void SetRawArray(s32 index, const void* src, size_t len) const {
|
||||
memcpy(m_buffer + index, src, len);
|
||||
}
|
||||
|
||||
void SetNull() const {
|
||||
this->Set(MessageHeader());
|
||||
}
|
||||
|
||||
s32 Set(const MessageHeader& hdr) const {
|
||||
memcpy(m_buffer, hdr.GetData(), hdr.GetDataSize());
|
||||
return static_cast<s32>(hdr.GetDataSize() / sizeof(*m_buffer));
|
||||
}
|
||||
|
||||
s32 Set(const SpecialHeader& spc) const {
|
||||
const s32 index = static_cast<s32>(MessageHeader::GetDataSize() / sizeof(*m_buffer));
|
||||
memcpy(m_buffer + index, spc.GetHeader(), spc.GetHeaderSize());
|
||||
return static_cast<s32>(index + (spc.GetHeaderSize() / sizeof(*m_buffer)));
|
||||
}
|
||||
|
||||
s32 SetHandle(s32 index, const Handle& hnd) const {
|
||||
memcpy(m_buffer + index, std::addressof(hnd), sizeof(hnd));
|
||||
return static_cast<s32>(index + (sizeof(hnd) / sizeof(*m_buffer)));
|
||||
}
|
||||
|
||||
s32 SetProcessId(s32 index, const u64 pid) const {
|
||||
memcpy(m_buffer + index, std::addressof(pid), sizeof(pid));
|
||||
return static_cast<s32>(index + (sizeof(pid) / sizeof(*m_buffer)));
|
||||
}
|
||||
|
||||
s32 Set(s32 index, const MapAliasDescriptor& desc) const {
|
||||
memcpy(m_buffer + index, desc.GetData(), desc.GetDataSize());
|
||||
return static_cast<s32>(index + (desc.GetDataSize() / sizeof(*m_buffer)));
|
||||
}
|
||||
|
||||
s32 Set(s32 index, const PointerDescriptor& desc) const {
|
||||
memcpy(m_buffer + index, desc.GetData(), desc.GetDataSize());
|
||||
return static_cast<s32>(index + (desc.GetDataSize() / sizeof(*m_buffer)));
|
||||
}
|
||||
|
||||
s32 Set(s32 index, const ReceiveListEntry& desc) const {
|
||||
memcpy(m_buffer + index, desc.GetData(), desc.GetDataSize());
|
||||
return static_cast<s32>(index + (desc.GetDataSize() / sizeof(*m_buffer)));
|
||||
}
|
||||
|
||||
s32 Set(s32 index, const u32 val) const {
|
||||
memcpy(m_buffer + index, std::addressof(val), sizeof(val));
|
||||
return static_cast<s32>(index + (sizeof(val) / sizeof(*m_buffer)));
|
||||
}
|
||||
|
||||
Result GetAsyncResult() const {
|
||||
MessageHeader hdr(m_buffer);
|
||||
MessageHeader null{};
|
||||
if (memcmp(hdr.GetData(), null.GetData(), MessageHeader::GetDataSize()) != 0) [[unlikely]] {
|
||||
R_SUCCEED();
|
||||
}
|
||||
return Result(m_buffer[MessageHeader::GetDataSize() / sizeof(*m_buffer)]);
|
||||
}
|
||||
|
||||
void SetAsyncResult(Result res) const {
|
||||
const s32 index = this->Set(MessageHeader());
|
||||
const auto value = res.raw;
|
||||
memcpy(m_buffer + index, std::addressof(value), sizeof(value));
|
||||
}
|
||||
|
||||
u32 Get32(s32 index) const {
|
||||
return m_buffer[index];
|
||||
}
|
||||
|
||||
u64 Get64(s32 index) const {
|
||||
u64 value;
|
||||
memcpy(std::addressof(value), m_buffer + index, sizeof(value));
|
||||
return value;
|
||||
}
|
||||
|
||||
u64 GetProcessId(s32 index) const {
|
||||
return this->Get64(index);
|
||||
}
|
||||
|
||||
Handle GetHandle(s32 index) const {
|
||||
static_assert(sizeof(Handle) == sizeof(*m_buffer));
|
||||
return Handle(m_buffer[index]);
|
||||
}
|
||||
|
||||
static constexpr s32 GetSpecialDataIndex(const MessageHeader& hdr, const SpecialHeader& spc) {
|
||||
return static_cast<s32>((MessageHeader::GetDataSize() / sizeof(u32)) +
|
||||
(spc.GetHeaderSize() / sizeof(u32)));
|
||||
}
|
||||
|
||||
static constexpr s32 GetPointerDescriptorIndex(const MessageHeader& hdr,
|
||||
const SpecialHeader& spc) {
|
||||
return static_cast<s32>(GetSpecialDataIndex(hdr, spc) + (spc.GetDataSize() / sizeof(u32)));
|
||||
}
|
||||
|
||||
static constexpr s32 GetMapAliasDescriptorIndex(const MessageHeader& hdr,
|
||||
const SpecialHeader& spc) {
|
||||
return GetPointerDescriptorIndex(hdr, spc) +
|
||||
static_cast<s32>(hdr.GetPointerCount() * PointerDescriptor::GetDataSize() /
|
||||
sizeof(u32));
|
||||
}
|
||||
|
||||
static constexpr s32 GetRawDataIndex(const MessageHeader& hdr, const SpecialHeader& spc) {
|
||||
return GetMapAliasDescriptorIndex(hdr, spc) +
|
||||
static_cast<s32>(hdr.GetMapAliasCount() * MapAliasDescriptor::GetDataSize() /
|
||||
sizeof(u32));
|
||||
}
|
||||
|
||||
static constexpr s32 GetReceiveListIndex(const MessageHeader& hdr, const SpecialHeader& spc) {
|
||||
if (const s32 recv_list_index = hdr.GetReceiveListOffset()) {
|
||||
return recv_list_index;
|
||||
} else {
|
||||
return GetRawDataIndex(hdr, spc) + hdr.GetRawCount();
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr size_t GetMessageBufferSize(const MessageHeader& hdr,
|
||||
const SpecialHeader& spc) {
|
||||
// Get the size of the plain message.
|
||||
size_t msg_size = GetReceiveListIndex(hdr, spc) * sizeof(u32);
|
||||
|
||||
// Add the size of the receive list.
|
||||
const auto count = hdr.GetReceiveListCount();
|
||||
switch (count) {
|
||||
case MessageHeader::ReceiveListCountType::None:
|
||||
break;
|
||||
case MessageHeader::ReceiveListCountType::ToMessageBuffer:
|
||||
break;
|
||||
case MessageHeader::ReceiveListCountType::ToSingleBuffer:
|
||||
msg_size += ReceiveListEntry::GetDataSize();
|
||||
break;
|
||||
default:
|
||||
msg_size += (static_cast<s32>(count) -
|
||||
static_cast<s32>(MessageHeader::ReceiveListCountType::CountOffset)) *
|
||||
ReceiveListEntry::GetDataSize();
|
||||
break;
|
||||
}
|
||||
|
||||
return msg_size;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
@ -329,8 +329,22 @@ std::vector<u8> HLERequestContext::ReadBufferCopy(std::size_t buffer_index) cons
|
||||
}
|
||||
|
||||
std::span<const u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const {
|
||||
static thread_local std::array<Common::ScratchBuffer<u8>, 2> read_buffer_a;
|
||||
static thread_local std::array<Common::ScratchBuffer<u8>, 2> read_buffer_x;
|
||||
static thread_local std::array read_buffer_a{
|
||||
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0),
|
||||
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0),
|
||||
};
|
||||
static thread_local std::array read_buffer_data_a{
|
||||
Common::ScratchBuffer<u8>(),
|
||||
Common::ScratchBuffer<u8>(),
|
||||
};
|
||||
static thread_local std::array read_buffer_x{
|
||||
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0),
|
||||
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0),
|
||||
};
|
||||
static thread_local std::array read_buffer_data_x{
|
||||
Common::ScratchBuffer<u8>(),
|
||||
Common::ScratchBuffer<u8>(),
|
||||
};
|
||||
|
||||
const bool is_buffer_a{BufferDescriptorA().size() > buffer_index &&
|
||||
BufferDescriptorA()[buffer_index].Size()};
|
||||
@ -339,19 +353,17 @@ std::span<const u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) cons
|
||||
BufferDescriptorA().size() > buffer_index, { return {}; },
|
||||
"BufferDescriptorA invalid buffer_index {}", buffer_index);
|
||||
auto& read_buffer = read_buffer_a[buffer_index];
|
||||
read_buffer.resize_destructive(BufferDescriptorA()[buffer_index].Size());
|
||||
memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), read_buffer.data(),
|
||||
read_buffer.size());
|
||||
return read_buffer;
|
||||
return read_buffer.Read(BufferDescriptorA()[buffer_index].Address(),
|
||||
BufferDescriptorA()[buffer_index].Size(),
|
||||
&read_buffer_data_a[buffer_index]);
|
||||
} else {
|
||||
ASSERT_OR_EXECUTE_MSG(
|
||||
BufferDescriptorX().size() > buffer_index, { return {}; },
|
||||
"BufferDescriptorX invalid buffer_index {}", buffer_index);
|
||||
auto& read_buffer = read_buffer_x[buffer_index];
|
||||
read_buffer.resize_destructive(BufferDescriptorX()[buffer_index].Size());
|
||||
memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), read_buffer.data(),
|
||||
read_buffer.size());
|
||||
return read_buffer;
|
||||
return read_buffer.Read(BufferDescriptorX()[buffer_index].Address(),
|
||||
BufferDescriptorX()[buffer_index].Size(),
|
||||
&read_buffer_data_x[buffer_index]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -276,6 +276,7 @@ private:
|
||||
|
||||
if (text_log) {
|
||||
output_log += fmt::format("Log Text: {}\n", *text_log);
|
||||
system.AppendGameMessage(*text_log);
|
||||
}
|
||||
LOG_DEBUG(Service_LM, "LogManager {} ({}):\n{}", NameOf(entry.severity),
|
||||
DestinationToString(destination), output_log);
|
||||
|
@ -79,6 +79,8 @@ enum class ResultStatus : u16 {
|
||||
ErrorBadPFSHeader,
|
||||
ErrorIncorrectPFSFileSize,
|
||||
ErrorBadNCAHeader,
|
||||
ErrorCompressedNCA,
|
||||
ErrorSparseNCA,
|
||||
ErrorMissingProductionKeyFile,
|
||||
ErrorMissingHeaderKey,
|
||||
ErrorIncorrectHeaderKey,
|
||||
|
@ -266,6 +266,22 @@ struct Memory::Impl {
|
||||
ReadBlockImpl<true>(*system.ApplicationProcess(), src_addr, dest_buffer, size);
|
||||
}
|
||||
|
||||
const u8* GetSpan(const VAddr src_addr, const std::size_t size) const {
|
||||
if (current_page_table->blocks[src_addr >> YUZU_PAGEBITS] ==
|
||||
current_page_table->blocks[(src_addr + size) >> YUZU_PAGEBITS]) {
|
||||
return GetPointerSilent(src_addr);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
u8* GetSpan(const VAddr src_addr, const std::size_t size) {
|
||||
if (current_page_table->blocks[src_addr >> YUZU_PAGEBITS] ==
|
||||
current_page_table->blocks[(src_addr + size) >> YUZU_PAGEBITS]) {
|
||||
return GetPointerSilent(src_addr);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <bool UNSAFE>
|
||||
void WriteBlockImpl(const Kernel::KProcess& process, const Common::ProcessAddress dest_addr,
|
||||
const void* src_buffer, const std::size_t size) {
|
||||
@ -559,7 +575,7 @@ struct Memory::Impl {
|
||||
}
|
||||
}
|
||||
|
||||
const Common::ProcessAddress end = base + size;
|
||||
const auto end = base + size;
|
||||
ASSERT_MSG(end <= page_table.pointers.size(), "out of range mapping at {:016X}",
|
||||
base + page_table.pointers.size());
|
||||
|
||||
@ -570,14 +586,18 @@ struct Memory::Impl {
|
||||
while (base != end) {
|
||||
page_table.pointers[base].Store(nullptr, type);
|
||||
page_table.backing_addr[base] = 0;
|
||||
|
||||
page_table.blocks[base] = 0;
|
||||
base += 1;
|
||||
}
|
||||
} else {
|
||||
auto orig_base = base;
|
||||
while (base != end) {
|
||||
page_table.pointers[base].Store(
|
||||
system.DeviceMemory().GetPointer<u8>(target) - (base << YUZU_PAGEBITS), type);
|
||||
page_table.backing_addr[base] = GetInteger(target) - (base << YUZU_PAGEBITS);
|
||||
auto host_ptr =
|
||||
system.DeviceMemory().GetPointer<u8>(target) - (base << YUZU_PAGEBITS);
|
||||
auto backing = GetInteger(target) - (base << YUZU_PAGEBITS);
|
||||
page_table.pointers[base].Store(host_ptr, type);
|
||||
page_table.backing_addr[base] = backing;
|
||||
page_table.blocks[base] = orig_base << YUZU_PAGEBITS;
|
||||
|
||||
ASSERT_MSG(page_table.pointers[base].Pointer(),
|
||||
"memory mapping base yield a nullptr within the table");
|
||||
@ -747,6 +767,14 @@ struct Memory::Impl {
|
||||
VAddr last_address;
|
||||
};
|
||||
|
||||
void InvalidateRegion(Common::ProcessAddress dest_addr, size_t size) {
|
||||
system.GPU().InvalidateRegion(GetInteger(dest_addr), size);
|
||||
}
|
||||
|
||||
void FlushRegion(Common::ProcessAddress dest_addr, size_t size) {
|
||||
system.GPU().FlushRegion(GetInteger(dest_addr), size);
|
||||
}
|
||||
|
||||
Core::System& system;
|
||||
Common::PageTable* current_page_table = nullptr;
|
||||
std::array<VideoCore::RasterizerDownloadArea, Core::Hardware::NUM_CPU_CORES>
|
||||
@ -881,6 +909,14 @@ void Memory::ReadBlockUnsafe(const Common::ProcessAddress src_addr, void* dest_b
|
||||
impl->ReadBlockUnsafe(src_addr, dest_buffer, size);
|
||||
}
|
||||
|
||||
const u8* Memory::GetSpan(const VAddr src_addr, const std::size_t size) const {
|
||||
return impl->GetSpan(src_addr, size);
|
||||
}
|
||||
|
||||
u8* Memory::GetSpan(const VAddr src_addr, const std::size_t size) {
|
||||
return impl->GetSpan(src_addr, size);
|
||||
}
|
||||
|
||||
void Memory::WriteBlock(const Common::ProcessAddress dest_addr, const void* src_buffer,
|
||||
const std::size_t size) {
|
||||
impl->WriteBlock(dest_addr, src_buffer, size);
|
||||
@ -924,4 +960,12 @@ void Memory::MarkRegionDebug(Common::ProcessAddress vaddr, u64 size, bool debug)
|
||||
impl->MarkRegionDebug(GetInteger(vaddr), size, debug);
|
||||
}
|
||||
|
||||
void Memory::InvalidateRegion(Common::ProcessAddress dest_addr, size_t size) {
|
||||
impl->InvalidateRegion(dest_addr, size);
|
||||
}
|
||||
|
||||
void Memory::FlushRegion(Common::ProcessAddress dest_addr, size_t size) {
|
||||
impl->FlushRegion(dest_addr, size);
|
||||
}
|
||||
|
||||
} // namespace Core::Memory
|
||||
|
@ -5,8 +5,12 @@
|
||||
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/scratch_buffer.h"
|
||||
#include "common/typed_address.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
@ -24,6 +28,10 @@ class PhysicalMemory;
|
||||
class KProcess;
|
||||
} // namespace Kernel
|
||||
|
||||
namespace Tegra {
|
||||
class MemoryManager;
|
||||
}
|
||||
|
||||
namespace Core::Memory {
|
||||
|
||||
/**
|
||||
@ -343,6 +351,9 @@ public:
|
||||
*/
|
||||
void ReadBlockUnsafe(Common::ProcessAddress src_addr, void* dest_buffer, std::size_t size);
|
||||
|
||||
const u8* GetSpan(const VAddr src_addr, const std::size_t size) const;
|
||||
u8* GetSpan(const VAddr src_addr, const std::size_t size);
|
||||
|
||||
/**
|
||||
* Writes a range of bytes into the current process' address space at the specified
|
||||
* virtual address.
|
||||
@ -461,6 +472,8 @@ public:
|
||||
void MarkRegionDebug(Common::ProcessAddress vaddr, u64 size, bool debug);
|
||||
|
||||
void SetGPUDirtyManagers(std::span<Core::GPUDirtyMemoryManager> managers);
|
||||
void InvalidateRegion(Common::ProcessAddress dest_addr, size_t size);
|
||||
void FlushRegion(Common::ProcessAddress dest_addr, size_t size);
|
||||
|
||||
private:
|
||||
Core::System& system;
|
||||
@ -469,4 +482,203 @@ private:
|
||||
std::unique_ptr<Impl> impl;
|
||||
};
|
||||
|
||||
enum GuestMemoryFlags : u32 {
|
||||
Read = 1 << 0,
|
||||
Write = 1 << 1,
|
||||
Safe = 1 << 2,
|
||||
Cached = 1 << 3,
|
||||
|
||||
SafeRead = Read | Safe,
|
||||
SafeWrite = Write | Safe,
|
||||
SafeReadWrite = SafeRead | SafeWrite,
|
||||
SafeReadCachedWrite = SafeReadWrite | Cached,
|
||||
|
||||
UnsafeRead = Read,
|
||||
UnsafeWrite = Write,
|
||||
UnsafeReadWrite = UnsafeRead | UnsafeWrite,
|
||||
UnsafeReadCachedWrite = UnsafeReadWrite | Cached,
|
||||
};
|
||||
|
||||
namespace {
|
||||
template <typename M, typename T, GuestMemoryFlags FLAGS>
|
||||
class GuestMemory {
|
||||
using iterator = T*;
|
||||
using const_iterator = const T*;
|
||||
using value_type = T;
|
||||
using element_type = T;
|
||||
using iterator_category = std::contiguous_iterator_tag;
|
||||
|
||||
public:
|
||||
GuestMemory() = delete;
|
||||
explicit GuestMemory(M& memory_, u64 addr_, std::size_t size_,
|
||||
Common::ScratchBuffer<T>* backup = nullptr)
|
||||
: memory{memory_}, addr{addr_}, size{size_} {
|
||||
static_assert(FLAGS & GuestMemoryFlags::Read || FLAGS & GuestMemoryFlags::Write);
|
||||
if constexpr (FLAGS & GuestMemoryFlags::Read) {
|
||||
Read(addr, size, backup);
|
||||
}
|
||||
}
|
||||
|
||||
~GuestMemory() = default;
|
||||
|
||||
T* data() noexcept {
|
||||
return data_span.data();
|
||||
}
|
||||
|
||||
const T* data() const noexcept {
|
||||
return data_span.data();
|
||||
}
|
||||
|
||||
[[nodiscard]] T* begin() noexcept {
|
||||
return data();
|
||||
}
|
||||
|
||||
[[nodiscard]] const T* begin() const noexcept {
|
||||
return data();
|
||||
}
|
||||
|
||||
[[nodiscard]] T* end() noexcept {
|
||||
return data() + size;
|
||||
}
|
||||
|
||||
[[nodiscard]] const T* end() const noexcept {
|
||||
return data() + size;
|
||||
}
|
||||
|
||||
T& operator[](size_t index) noexcept {
|
||||
return data_span[index];
|
||||
}
|
||||
|
||||
const T& operator[](size_t index) const noexcept {
|
||||
return data_span[index];
|
||||
}
|
||||
|
||||
void SetAddressAndSize(u64 addr_, std::size_t size_) noexcept {
|
||||
addr = addr_;
|
||||
size = size_;
|
||||
addr_changed = true;
|
||||
}
|
||||
|
||||
std::span<T> Read(u64 addr_, std::size_t size_,
|
||||
Common::ScratchBuffer<T>* backup = nullptr) noexcept {
|
||||
addr = addr_;
|
||||
size = size_;
|
||||
if (size == 0) {
|
||||
is_data_copy = true;
|
||||
return {};
|
||||
}
|
||||
|
||||
if (TrySetSpan()) {
|
||||
if constexpr (FLAGS & GuestMemoryFlags::Safe) {
|
||||
memory.FlushRegion(addr, size * sizeof(T));
|
||||
}
|
||||
} else {
|
||||
if (backup) {
|
||||
backup->resize_destructive(size);
|
||||
data_span = *backup;
|
||||
} else {
|
||||
data_copy.resize(size);
|
||||
data_span = std::span(data_copy);
|
||||
}
|
||||
is_data_copy = true;
|
||||
span_valid = true;
|
||||
if constexpr (FLAGS & GuestMemoryFlags::Safe) {
|
||||
memory.ReadBlock(addr, data_span.data(), size * sizeof(T));
|
||||
} else {
|
||||
memory.ReadBlockUnsafe(addr, data_span.data(), size * sizeof(T));
|
||||
}
|
||||
}
|
||||
return data_span;
|
||||
}
|
||||
|
||||
void Write(std::span<T> write_data) noexcept {
|
||||
if constexpr (FLAGS & GuestMemoryFlags::Cached) {
|
||||
memory.WriteBlockCached(addr, write_data.data(), size * sizeof(T));
|
||||
} else if constexpr (FLAGS & GuestMemoryFlags::Safe) {
|
||||
memory.WriteBlock(addr, write_data.data(), size * sizeof(T));
|
||||
} else {
|
||||
memory.WriteBlockUnsafe(addr, write_data.data(), size * sizeof(T));
|
||||
}
|
||||
}
|
||||
|
||||
bool TrySetSpan() noexcept {
|
||||
if (u8* ptr = memory.GetSpan(addr, size * sizeof(T)); ptr) {
|
||||
data_span = {reinterpret_cast<T*>(ptr), size};
|
||||
span_valid = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool IsDataCopy() const noexcept {
|
||||
return is_data_copy;
|
||||
}
|
||||
|
||||
bool AddressChanged() const noexcept {
|
||||
return addr_changed;
|
||||
}
|
||||
|
||||
M& memory;
|
||||
u64 addr;
|
||||
size_t size;
|
||||
std::span<T> data_span{};
|
||||
std::vector<T> data_copy;
|
||||
bool span_valid{false};
|
||||
bool is_data_copy{false};
|
||||
bool addr_changed{false};
|
||||
};
|
||||
|
||||
template <typename M, typename T, GuestMemoryFlags FLAGS>
|
||||
class GuestMemoryScoped : public GuestMemory<M, T, FLAGS> {
|
||||
public:
|
||||
GuestMemoryScoped() = delete;
|
||||
explicit GuestMemoryScoped(M& memory_, u64 addr_, std::size_t size_,
|
||||
Common::ScratchBuffer<T>* backup = nullptr)
|
||||
: GuestMemory<M, T, FLAGS>(memory_, addr_, size_, backup) {
|
||||
if constexpr (!(FLAGS & GuestMemoryFlags::Read)) {
|
||||
if (!this->TrySetSpan()) {
|
||||
if (backup) {
|
||||
this->data_span = *backup;
|
||||
this->span_valid = true;
|
||||
this->is_data_copy = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~GuestMemoryScoped() {
|
||||
if constexpr (FLAGS & GuestMemoryFlags::Write) {
|
||||
if (this->size == 0) [[unlikely]] {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->AddressChanged() || this->IsDataCopy()) {
|
||||
ASSERT(this->span_valid);
|
||||
if constexpr (FLAGS & GuestMemoryFlags::Cached) {
|
||||
this->memory.WriteBlockCached(this->addr, this->data_span.data(),
|
||||
this->size * sizeof(T));
|
||||
} else if constexpr (FLAGS & GuestMemoryFlags::Safe) {
|
||||
this->memory.WriteBlock(this->addr, this->data_span.data(),
|
||||
this->size * sizeof(T));
|
||||
} else {
|
||||
this->memory.WriteBlockUnsafe(this->addr, this->data_span.data(),
|
||||
this->size * sizeof(T));
|
||||
}
|
||||
} else if constexpr (FLAGS & GuestMemoryFlags::Safe) {
|
||||
this->memory.InvalidateRegion(this->addr, this->size * sizeof(T));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
template <typename T, GuestMemoryFlags FLAGS>
|
||||
using CpuGuestMemory = GuestMemory<Memory, T, FLAGS>;
|
||||
template <typename T, GuestMemoryFlags FLAGS>
|
||||
using CpuGuestMemoryScoped = GuestMemoryScoped<Memory, T, FLAGS>;
|
||||
template <typename T, GuestMemoryFlags FLAGS>
|
||||
using GpuGuestMemory = GuestMemory<Tegra::MemoryManager, T, FLAGS>;
|
||||
template <typename T, GuestMemoryFlags FLAGS>
|
||||
using GpuGuestMemoryScoped = GuestMemoryScoped<Tegra::MemoryManager, T, FLAGS>;
|
||||
} // namespace Core::Memory
|
||||
|
@ -160,8 +160,9 @@ void Mouse::Move(int x, int y, int center_x, int center_y) {
|
||||
last_mouse_change.y += mouse_change.y * y_sensitivity;
|
||||
|
||||
// Bind the mouse change to [0 <= deadzone_counterweight <= 1.0]
|
||||
if (last_mouse_change.Length() < deadzone_counterweight) {
|
||||
last_mouse_change /= last_mouse_change.Length();
|
||||
const float length = last_mouse_change.Length();
|
||||
if (length < deadzone_counterweight && length != 0.0f) {
|
||||
last_mouse_change /= length;
|
||||
last_mouse_change *= deadzone_counterweight;
|
||||
}
|
||||
|
||||
|
@ -523,6 +523,8 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en
|
||||
}
|
||||
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_PLAYER_LED, "1");
|
||||
// Share the same button mapping with non-Nintendo controllers
|
||||
SDL_SetHint(SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS, "0");
|
||||
|
||||
// Disable hidapi driver for xbox. Already default on Windows, this causes conflict with native
|
||||
// driver on Linux.
|
||||
@ -800,16 +802,9 @@ ButtonMapping SDLDriver::GetButtonMappingForDevice(const Common::ParamPackage& p
|
||||
|
||||
// This list is missing ZL/ZR since those are not considered buttons in SDL GameController.
|
||||
// We will add those afterwards
|
||||
// This list also excludes Screenshot since there's not really a mapping for that
|
||||
ButtonBindings switch_to_sdl_button;
|
||||
|
||||
if (SDL_GameControllerGetType(controller) == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO ||
|
||||
SDL_GameControllerGetType(controller) == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT ||
|
||||
SDL_GameControllerGetType(controller) == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT) {
|
||||
switch_to_sdl_button = GetNintendoButtonBinding(joystick);
|
||||
} else {
|
||||
switch_to_sdl_button = GetDefaultButtonBinding();
|
||||
}
|
||||
switch_to_sdl_button = GetDefaultButtonBinding(joystick);
|
||||
|
||||
// Add the missing bindings for ZL/ZR
|
||||
static constexpr ZButtonBindings switch_to_sdl_axis{{
|
||||
@ -830,32 +825,9 @@ ButtonMapping SDLDriver::GetButtonMappingForDevice(const Common::ParamPackage& p
|
||||
return GetSingleControllerMapping(joystick, switch_to_sdl_button, switch_to_sdl_axis);
|
||||
}
|
||||
|
||||
ButtonBindings SDLDriver::GetDefaultButtonBinding() const {
|
||||
return {
|
||||
std::pair{Settings::NativeButton::A, SDL_CONTROLLER_BUTTON_B},
|
||||
{Settings::NativeButton::B, SDL_CONTROLLER_BUTTON_A},
|
||||
{Settings::NativeButton::X, SDL_CONTROLLER_BUTTON_Y},
|
||||
{Settings::NativeButton::Y, SDL_CONTROLLER_BUTTON_X},
|
||||
{Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK},
|
||||
{Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK},
|
||||
{Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
|
||||
{Settings::NativeButton::R, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
|
||||
{Settings::NativeButton::Plus, SDL_CONTROLLER_BUTTON_START},
|
||||
{Settings::NativeButton::Minus, SDL_CONTROLLER_BUTTON_BACK},
|
||||
{Settings::NativeButton::DLeft, SDL_CONTROLLER_BUTTON_DPAD_LEFT},
|
||||
{Settings::NativeButton::DUp, SDL_CONTROLLER_BUTTON_DPAD_UP},
|
||||
{Settings::NativeButton::DRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT},
|
||||
{Settings::NativeButton::DDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN},
|
||||
{Settings::NativeButton::SL, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
|
||||
{Settings::NativeButton::SR, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
|
||||
{Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE},
|
||||
{Settings::NativeButton::Screenshot, SDL_CONTROLLER_BUTTON_MISC1},
|
||||
};
|
||||
}
|
||||
|
||||
ButtonBindings SDLDriver::GetNintendoButtonBinding(
|
||||
ButtonBindings SDLDriver::GetDefaultButtonBinding(
|
||||
const std::shared_ptr<SDLJoystick>& joystick) const {
|
||||
// Default SL/SR mapping for pro controllers
|
||||
// Default SL/SR mapping for other controllers
|
||||
auto sl_button = SDL_CONTROLLER_BUTTON_LEFTSHOULDER;
|
||||
auto sr_button = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER;
|
||||
|
||||
@ -869,10 +841,10 @@ ButtonBindings SDLDriver::GetNintendoButtonBinding(
|
||||
}
|
||||
|
||||
return {
|
||||
std::pair{Settings::NativeButton::A, SDL_CONTROLLER_BUTTON_A},
|
||||
{Settings::NativeButton::B, SDL_CONTROLLER_BUTTON_B},
|
||||
{Settings::NativeButton::X, SDL_CONTROLLER_BUTTON_X},
|
||||
{Settings::NativeButton::Y, SDL_CONTROLLER_BUTTON_Y},
|
||||
std::pair{Settings::NativeButton::A, SDL_CONTROLLER_BUTTON_B},
|
||||
{Settings::NativeButton::B, SDL_CONTROLLER_BUTTON_A},
|
||||
{Settings::NativeButton::X, SDL_CONTROLLER_BUTTON_Y},
|
||||
{Settings::NativeButton::Y, SDL_CONTROLLER_BUTTON_X},
|
||||
{Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK},
|
||||
{Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK},
|
||||
{Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
|
||||
|
@ -100,11 +100,8 @@ private:
|
||||
int axis_y, float offset_x,
|
||||
float offset_y) const;
|
||||
|
||||
/// Returns the default button bindings list for generic controllers
|
||||
ButtonBindings GetDefaultButtonBinding() const;
|
||||
|
||||
/// Returns the default button bindings list for nintendo controllers
|
||||
ButtonBindings GetNintendoButtonBinding(const std::shared_ptr<SDLJoystick>& joystick) const;
|
||||
/// Returns the default button bindings list
|
||||
ButtonBindings GetDefaultButtonBinding(const std::shared_ptr<SDLJoystick>& joystick) const;
|
||||
|
||||
/// Returns the button mappings from a single controller
|
||||
ButtonMapping GetSingleControllerMapping(const std::shared_ptr<SDLJoystick>& joystick,
|
||||
|
@ -234,9 +234,10 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am
|
||||
if (has_new_downloads) {
|
||||
memory_tracker.MarkRegionAsGpuModified(*cpu_dest_address, amount);
|
||||
}
|
||||
tmp_buffer.resize_destructive(amount);
|
||||
cpu_memory.ReadBlockUnsafe(*cpu_src_address, tmp_buffer.data(), amount);
|
||||
cpu_memory.WriteBlockUnsafe(*cpu_dest_address, tmp_buffer.data(), amount);
|
||||
|
||||
Core::Memory::CpuGuestMemoryScoped<u8, Core::Memory::GuestMemoryFlags::UnsafeReadWrite> tmp(
|
||||
cpu_memory, *cpu_src_address, amount, &tmp_buffer);
|
||||
tmp.SetAddressAndSize(*cpu_dest_address, amount);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "common/microprofile.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
#include "core/memory.h"
|
||||
#include "video_core/dma_pusher.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/gpu.h"
|
||||
@ -12,6 +13,8 @@
|
||||
|
||||
namespace Tegra {
|
||||
|
||||
constexpr u32 MacroRegistersStart = 0xE00;
|
||||
|
||||
DmaPusher::DmaPusher(Core::System& system_, GPU& gpu_, MemoryManager& memory_manager_,
|
||||
Control::ChannelState& channel_state_)
|
||||
: gpu{gpu_}, system{system_}, memory_manager{memory_manager_}, puller{gpu_, memory_manager_,
|
||||
@ -74,25 +77,16 @@ bool DmaPusher::Step() {
|
||||
}
|
||||
|
||||
// Push buffer non-empty, read a word
|
||||
command_headers.resize_destructive(command_list_header.size);
|
||||
constexpr u32 MacroRegistersStart = 0xE00;
|
||||
if (dma_state.method < MacroRegistersStart) {
|
||||
if (Settings::IsGPULevelHigh()) {
|
||||
memory_manager.ReadBlock(dma_state.dma_get, command_headers.data(),
|
||||
command_list_header.size * sizeof(u32));
|
||||
} else {
|
||||
memory_manager.ReadBlockUnsafe(dma_state.dma_get, command_headers.data(),
|
||||
command_list_header.size * sizeof(u32));
|
||||
}
|
||||
} else {
|
||||
const size_t copy_size = command_list_header.size * sizeof(u32);
|
||||
if (dma_state.method >= MacroRegistersStart) {
|
||||
if (subchannels[dma_state.subchannel]) {
|
||||
subchannels[dma_state.subchannel]->current_dirty =
|
||||
memory_manager.IsMemoryDirty(dma_state.dma_get, copy_size);
|
||||
subchannels[dma_state.subchannel]->current_dirty = memory_manager.IsMemoryDirty(
|
||||
dma_state.dma_get, command_list_header.size * sizeof(u32));
|
||||
}
|
||||
memory_manager.ReadBlockUnsafe(dma_state.dma_get, command_headers.data(), copy_size);
|
||||
}
|
||||
ProcessCommands(command_headers);
|
||||
Core::Memory::GpuGuestMemory<Tegra::CommandHeader,
|
||||
Core::Memory::GuestMemoryFlags::UnsafeRead>
|
||||
headers(memory_manager, dma_state.dma_get, command_list_header.size, &command_headers);
|
||||
ProcessCommands(headers);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
#include "common/algorithm.h"
|
||||
#include "common/assert.h"
|
||||
#include "core/memory.h"
|
||||
#include "video_core/engines/engine_upload.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
#include "video_core/rasterizer_interface.h"
|
||||
@ -46,15 +47,11 @@ void State::ProcessData(const u32* data, size_t num_data) {
|
||||
void State::ProcessData(std::span<const u8> read_buffer) {
|
||||
const GPUVAddr address{regs.dest.Address()};
|
||||
if (is_linear) {
|
||||
if (regs.line_count == 1) {
|
||||
rasterizer->AccelerateInlineToMemory(address, copy_size, read_buffer);
|
||||
} else {
|
||||
for (size_t line = 0; line < regs.line_count; ++line) {
|
||||
const GPUVAddr dest_line = address + line * regs.dest.pitch;
|
||||
std::span<const u8> buffer(read_buffer.data() + line * regs.line_length_in,
|
||||
regs.line_length_in);
|
||||
rasterizer->AccelerateInlineToMemory(dest_line, regs.line_length_in, buffer);
|
||||
}
|
||||
for (size_t line = 0; line < regs.line_count; ++line) {
|
||||
const GPUVAddr dest_line = address + line * regs.dest.pitch;
|
||||
std::span<const u8> buffer(read_buffer.data() + line * regs.line_length_in,
|
||||
regs.line_length_in);
|
||||
rasterizer->AccelerateInlineToMemory(dest_line, regs.line_length_in, buffer);
|
||||
}
|
||||
} else {
|
||||
u32 width = regs.dest.width;
|
||||
@ -70,13 +67,14 @@ void State::ProcessData(std::span<const u8> read_buffer) {
|
||||
const std::size_t dst_size = Tegra::Texture::CalculateSize(
|
||||
true, bytes_per_pixel, width, regs.dest.height, regs.dest.depth,
|
||||
regs.dest.BlockHeight(), regs.dest.BlockDepth());
|
||||
tmp_buffer.resize_destructive(dst_size);
|
||||
memory_manager.ReadBlock(address, tmp_buffer.data(), dst_size);
|
||||
Tegra::Texture::SwizzleSubrect(tmp_buffer, read_buffer, bytes_per_pixel, width,
|
||||
regs.dest.height, regs.dest.depth, x_offset, regs.dest.y,
|
||||
x_elements, regs.line_count, regs.dest.BlockHeight(),
|
||||
|
||||
Core::Memory::GpuGuestMemoryScoped<u8, Core::Memory::GuestMemoryFlags::SafeReadCachedWrite>
|
||||
tmp(memory_manager, address, dst_size, &tmp_buffer);
|
||||
|
||||
Tegra::Texture::SwizzleSubrect(tmp, read_buffer, bytes_per_pixel, width, regs.dest.height,
|
||||
regs.dest.depth, x_offset, regs.dest.y, x_elements,
|
||||
regs.line_count, regs.dest.BlockHeight(),
|
||||
regs.dest.BlockDepth(), regs.line_length_in);
|
||||
memory_manager.WriteBlockCached(address, tmp_buffer.data(), dst_size);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -84,7 +84,6 @@ Texture::TICEntry KeplerCompute::GetTICEntry(u32 tic_index) const {
|
||||
|
||||
Texture::TICEntry tic_entry;
|
||||
memory_manager.ReadBlockUnsafe(tic_address_gpu, &tic_entry, sizeof(Texture::TICEntry));
|
||||
|
||||
return tic_entry;
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/memory.h"
|
||||
#include "video_core/dirty_flags.h"
|
||||
#include "video_core/engines/draw_manager.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
@ -679,17 +680,14 @@ void Maxwell3D::ProcessCBData(u32 value) {
|
||||
Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const {
|
||||
const GPUVAddr tic_address_gpu{regs.tex_header.Address() +
|
||||
tic_index * sizeof(Texture::TICEntry)};
|
||||
|
||||
Texture::TICEntry tic_entry;
|
||||
memory_manager.ReadBlockUnsafe(tic_address_gpu, &tic_entry, sizeof(Texture::TICEntry));
|
||||
|
||||
return tic_entry;
|
||||
}
|
||||
|
||||
Texture::TSCEntry Maxwell3D::GetTSCEntry(u32 tsc_index) const {
|
||||
const GPUVAddr tsc_address_gpu{regs.tex_sampler.Address() +
|
||||
tsc_index * sizeof(Texture::TSCEntry)};
|
||||
|
||||
Texture::TSCEntry tsc_entry;
|
||||
memory_manager.ReadBlockUnsafe(tsc_address_gpu, &tsc_entry, sizeof(Texture::TSCEntry));
|
||||
return tsc_entry;
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "common/microprofile.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
#include "core/memory.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/engines/maxwell_dma.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
@ -130,11 +131,12 @@ void MaxwellDMA::Launch() {
|
||||
UNIMPLEMENTED_IF(regs.offset_out % 16 != 0);
|
||||
read_buffer.resize_destructive(16);
|
||||
for (u32 offset = 0; offset < regs.line_length_in; offset += 16) {
|
||||
memory_manager.ReadBlock(
|
||||
convert_linear_2_blocklinear_addr(regs.offset_in + offset),
|
||||
read_buffer.data(), read_buffer.size());
|
||||
memory_manager.WriteBlockCached(regs.offset_out + offset, read_buffer.data(),
|
||||
read_buffer.size());
|
||||
Core::Memory::GpuGuestMemoryScoped<
|
||||
u8, Core::Memory::GuestMemoryFlags::SafeReadCachedWrite>
|
||||
tmp_write_buffer(memory_manager,
|
||||
convert_linear_2_blocklinear_addr(regs.offset_in + offset),
|
||||
16, &read_buffer);
|
||||
tmp_write_buffer.SetAddressAndSize(regs.offset_out + offset, 16);
|
||||
}
|
||||
} else if (is_src_pitch && !is_dst_pitch) {
|
||||
UNIMPLEMENTED_IF(regs.line_length_in % 16 != 0);
|
||||
@ -142,20 +144,19 @@ void MaxwellDMA::Launch() {
|
||||
UNIMPLEMENTED_IF(regs.offset_out % 16 != 0);
|
||||
read_buffer.resize_destructive(16);
|
||||
for (u32 offset = 0; offset < regs.line_length_in; offset += 16) {
|
||||
memory_manager.ReadBlock(regs.offset_in + offset, read_buffer.data(),
|
||||
read_buffer.size());
|
||||
memory_manager.WriteBlockCached(
|
||||
convert_linear_2_blocklinear_addr(regs.offset_out + offset),
|
||||
read_buffer.data(), read_buffer.size());
|
||||
Core::Memory::GpuGuestMemoryScoped<
|
||||
u8, Core::Memory::GuestMemoryFlags::SafeReadCachedWrite>
|
||||
tmp_write_buffer(memory_manager, regs.offset_in + offset, 16, &read_buffer);
|
||||
tmp_write_buffer.SetAddressAndSize(
|
||||
convert_linear_2_blocklinear_addr(regs.offset_out + offset), 16);
|
||||
}
|
||||
} else {
|
||||
if (!accelerate.BufferCopy(regs.offset_in, regs.offset_out, regs.line_length_in)) {
|
||||
read_buffer.resize_destructive(regs.line_length_in);
|
||||
memory_manager.ReadBlock(regs.offset_in, read_buffer.data(),
|
||||
regs.line_length_in,
|
||||
VideoCommon::CacheType::NoBufferCache);
|
||||
memory_manager.WriteBlockCached(regs.offset_out, read_buffer.data(),
|
||||
regs.line_length_in);
|
||||
Core::Memory::GpuGuestMemoryScoped<
|
||||
u8, Core::Memory::GuestMemoryFlags::SafeReadCachedWrite>
|
||||
tmp_write_buffer(memory_manager, regs.offset_in, regs.line_length_in,
|
||||
&read_buffer);
|
||||
tmp_write_buffer.SetAddressAndSize(regs.offset_out, regs.line_length_in);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -222,17 +223,15 @@ void MaxwellDMA::CopyBlockLinearToPitch() {
|
||||
CalculateSize(true, bytes_per_pixel, width, height, depth, block_height, block_depth);
|
||||
|
||||
const size_t dst_size = dst_operand.pitch * regs.line_count;
|
||||
read_buffer.resize_destructive(src_size);
|
||||
write_buffer.resize_destructive(dst_size);
|
||||
|
||||
memory_manager.ReadBlock(src_operand.address, read_buffer.data(), src_size);
|
||||
memory_manager.ReadBlock(dst_operand.address, write_buffer.data(), dst_size);
|
||||
Core::Memory::GpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead> tmp_read_buffer(
|
||||
memory_manager, src_operand.address, src_size, &read_buffer);
|
||||
Core::Memory::GpuGuestMemoryScoped<u8, Core::Memory::GuestMemoryFlags::SafeReadCachedWrite>
|
||||
tmp_write_buffer(memory_manager, dst_operand.address, dst_size, &write_buffer);
|
||||
|
||||
UnswizzleSubrect(write_buffer, read_buffer, bytes_per_pixel, width, height, depth, x_offset,
|
||||
src_params.origin.y, x_elements, regs.line_count, block_height, block_depth,
|
||||
dst_operand.pitch);
|
||||
|
||||
memory_manager.WriteBlockCached(regs.offset_out, write_buffer.data(), dst_size);
|
||||
UnswizzleSubrect(tmp_write_buffer, tmp_read_buffer, bytes_per_pixel, width, height, depth,
|
||||
x_offset, src_params.origin.y, x_elements, regs.line_count, block_height,
|
||||
block_depth, dst_operand.pitch);
|
||||
}
|
||||
|
||||
void MaxwellDMA::CopyPitchToBlockLinear() {
|
||||
@ -287,18 +286,17 @@ void MaxwellDMA::CopyPitchToBlockLinear() {
|
||||
CalculateSize(true, bytes_per_pixel, width, height, depth, block_height, block_depth);
|
||||
const size_t src_size = static_cast<size_t>(regs.pitch_in) * regs.line_count;
|
||||
|
||||
read_buffer.resize_destructive(src_size);
|
||||
write_buffer.resize_destructive(dst_size);
|
||||
GPUVAddr src_addr = regs.offset_in;
|
||||
GPUVAddr dst_addr = regs.offset_out;
|
||||
Core::Memory::GpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead> tmp_read_buffer(
|
||||
memory_manager, src_addr, src_size, &read_buffer);
|
||||
Core::Memory::GpuGuestMemoryScoped<u8, Core::Memory::GuestMemoryFlags::SafeReadCachedWrite>
|
||||
tmp_write_buffer(memory_manager, dst_addr, dst_size, &write_buffer);
|
||||
|
||||
memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size);
|
||||
memory_manager.ReadBlockUnsafe(regs.offset_out, write_buffer.data(), dst_size);
|
||||
|
||||
// If the input is linear and the output is tiled, swizzle the input and copy it over.
|
||||
SwizzleSubrect(write_buffer, read_buffer, bytes_per_pixel, width, height, depth, x_offset,
|
||||
dst_params.origin.y, x_elements, regs.line_count, block_height, block_depth,
|
||||
regs.pitch_in);
|
||||
|
||||
memory_manager.WriteBlockCached(regs.offset_out, write_buffer.data(), dst_size);
|
||||
// If the input is linear and the output is tiled, swizzle the input and copy it over.
|
||||
SwizzleSubrect(tmp_write_buffer, tmp_read_buffer, bytes_per_pixel, width, height, depth,
|
||||
x_offset, dst_params.origin.y, x_elements, regs.line_count, block_height,
|
||||
block_depth, regs.pitch_in);
|
||||
}
|
||||
|
||||
void MaxwellDMA::CopyBlockLinearToBlockLinear() {
|
||||
@ -342,23 +340,20 @@ void MaxwellDMA::CopyBlockLinearToBlockLinear() {
|
||||
const u32 pitch = x_elements * bytes_per_pixel;
|
||||
const size_t mid_buffer_size = pitch * regs.line_count;
|
||||
|
||||
read_buffer.resize_destructive(src_size);
|
||||
write_buffer.resize_destructive(dst_size);
|
||||
|
||||
intermediate_buffer.resize_destructive(mid_buffer_size);
|
||||
|
||||
memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size);
|
||||
memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size);
|
||||
Core::Memory::GpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead> tmp_read_buffer(
|
||||
memory_manager, regs.offset_in, src_size, &read_buffer);
|
||||
Core::Memory::GpuGuestMemoryScoped<u8, Core::Memory::GuestMemoryFlags::SafeReadCachedWrite>
|
||||
tmp_write_buffer(memory_manager, regs.offset_out, dst_size, &write_buffer);
|
||||
|
||||
UnswizzleSubrect(intermediate_buffer, read_buffer, bytes_per_pixel, src_width, src.height,
|
||||
UnswizzleSubrect(intermediate_buffer, tmp_read_buffer, bytes_per_pixel, src_width, src.height,
|
||||
src.depth, src_x_offset, src.origin.y, x_elements, regs.line_count,
|
||||
src.block_size.height, src.block_size.depth, pitch);
|
||||
|
||||
SwizzleSubrect(write_buffer, intermediate_buffer, bytes_per_pixel, dst_width, dst.height,
|
||||
SwizzleSubrect(tmp_write_buffer, intermediate_buffer, bytes_per_pixel, dst_width, dst.height,
|
||||
dst.depth, dst_x_offset, dst.origin.y, x_elements, regs.line_count,
|
||||
dst.block_size.height, dst.block_size.depth, pitch);
|
||||
|
||||
memory_manager.WriteBlockCached(regs.offset_out, write_buffer.data(), dst_size);
|
||||
}
|
||||
|
||||
void MaxwellDMA::ReleaseSemaphore() {
|
||||
|
@ -159,11 +159,11 @@ bool SoftwareBlitEngine::Blit(Fermi2D::Surface& src, Fermi2D::Surface& dst,
|
||||
const auto src_bytes_per_pixel = BytesPerBlock(PixelFormatFromRenderTargetFormat(src.format));
|
||||
const auto dst_bytes_per_pixel = BytesPerBlock(PixelFormatFromRenderTargetFormat(dst.format));
|
||||
const size_t src_size = get_surface_size(src, src_bytes_per_pixel);
|
||||
impl->tmp_buffer.resize_destructive(src_size);
|
||||
memory_manager.ReadBlock(src.Address(), impl->tmp_buffer.data(), src_size);
|
||||
|
||||
Core::Memory::GpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead> tmp_buffer(
|
||||
memory_manager, src.Address(), src_size, &impl->tmp_buffer);
|
||||
|
||||
const size_t src_copy_size = src_extent_x * src_extent_y * src_bytes_per_pixel;
|
||||
|
||||
const size_t dst_copy_size = dst_extent_x * dst_extent_y * dst_bytes_per_pixel;
|
||||
|
||||
impl->src_buffer.resize_destructive(src_copy_size);
|
||||
@ -200,12 +200,11 @@ bool SoftwareBlitEngine::Blit(Fermi2D::Surface& src, Fermi2D::Surface& dst,
|
||||
|
||||
impl->dst_buffer.resize_destructive(dst_copy_size);
|
||||
if (src.linear == Fermi2D::MemoryLayout::BlockLinear) {
|
||||
UnswizzleSubrect(impl->src_buffer, impl->tmp_buffer, src_bytes_per_pixel, src.width,
|
||||
src.height, src.depth, config.src_x0, config.src_y0, src_extent_x,
|
||||
src_extent_y, src.block_height, src.block_depth,
|
||||
src_extent_x * src_bytes_per_pixel);
|
||||
UnswizzleSubrect(impl->src_buffer, tmp_buffer, src_bytes_per_pixel, src.width, src.height,
|
||||
src.depth, config.src_x0, config.src_y0, src_extent_x, src_extent_y,
|
||||
src.block_height, src.block_depth, src_extent_x * src_bytes_per_pixel);
|
||||
} else {
|
||||
process_pitch_linear(false, impl->tmp_buffer, impl->src_buffer, src_extent_x, src_extent_y,
|
||||
process_pitch_linear(false, tmp_buffer, impl->src_buffer, src_extent_x, src_extent_y,
|
||||
src.pitch, config.src_x0, config.src_y0, src_bytes_per_pixel);
|
||||
}
|
||||
|
||||
@ -221,20 +220,18 @@ bool SoftwareBlitEngine::Blit(Fermi2D::Surface& src, Fermi2D::Surface& dst,
|
||||
}
|
||||
|
||||
const size_t dst_size = get_surface_size(dst, dst_bytes_per_pixel);
|
||||
impl->tmp_buffer.resize_destructive(dst_size);
|
||||
memory_manager.ReadBlock(dst.Address(), impl->tmp_buffer.data(), dst_size);
|
||||
Core::Memory::GpuGuestMemoryScoped<u8, Core::Memory::GuestMemoryFlags::SafeReadWrite>
|
||||
tmp_buffer2(memory_manager, dst.Address(), dst_size, &impl->tmp_buffer);
|
||||
|
||||
if (dst.linear == Fermi2D::MemoryLayout::BlockLinear) {
|
||||
SwizzleSubrect(impl->tmp_buffer, impl->dst_buffer, dst_bytes_per_pixel, dst.width,
|
||||
dst.height, dst.depth, config.dst_x0, config.dst_y0, dst_extent_x,
|
||||
dst_extent_y, dst.block_height, dst.block_depth,
|
||||
dst_extent_x * dst_bytes_per_pixel);
|
||||
SwizzleSubrect(tmp_buffer2, impl->dst_buffer, dst_bytes_per_pixel, dst.width, dst.height,
|
||||
dst.depth, config.dst_x0, config.dst_y0, dst_extent_x, dst_extent_y,
|
||||
dst.block_height, dst.block_depth, dst_extent_x * dst_bytes_per_pixel);
|
||||
} else {
|
||||
process_pitch_linear(true, impl->dst_buffer, impl->tmp_buffer, dst_extent_x, dst_extent_y,
|
||||
process_pitch_linear(true, impl->dst_buffer, tmp_buffer2, dst_extent_x, dst_extent_y,
|
||||
dst.pitch, config.dst_x0, config.dst_y0,
|
||||
static_cast<size_t>(dst_bytes_per_pixel));
|
||||
}
|
||||
memory_manager.WriteBlock(dst.Address(), impl->tmp_buffer.data(), dst_size);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -10,13 +10,13 @@
|
||||
#include "core/device_memory.h"
|
||||
#include "core/hle/kernel/k_page_table.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/memory.h"
|
||||
#include "video_core/invalidation_accumulator.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
#include "video_core/rasterizer_interface.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
|
||||
namespace Tegra {
|
||||
using Core::Memory::GuestMemoryFlags;
|
||||
|
||||
std::atomic<size_t> MemoryManager::unique_identifier_generator{};
|
||||
|
||||
@ -587,13 +587,10 @@ void MemoryManager::InvalidateRegion(GPUVAddr gpu_addr, size_t size,
|
||||
|
||||
void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size,
|
||||
VideoCommon::CacheType which) {
|
||||
tmp_buffer.resize_destructive(size);
|
||||
ReadBlock(gpu_src_addr, tmp_buffer.data(), size, which);
|
||||
|
||||
// The output block must be flushed in case it has data modified from the GPU.
|
||||
// Fixes NPC geometry in Zombie Panic in Wonderland DX
|
||||
Core::Memory::GpuGuestMemoryScoped<u8, GuestMemoryFlags::SafeReadWrite> data(
|
||||
*this, gpu_src_addr, size);
|
||||
data.SetAddressAndSize(gpu_dest_addr, size);
|
||||
FlushRegion(gpu_dest_addr, size, which);
|
||||
WriteBlock(gpu_dest_addr, tmp_buffer.data(), size, which);
|
||||
}
|
||||
|
||||
bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) const {
|
||||
@ -758,4 +755,23 @@ void MemoryManager::FlushCaching() {
|
||||
accumulator->Clear();
|
||||
}
|
||||
|
||||
const u8* MemoryManager::GetSpan(const GPUVAddr src_addr, const std::size_t size) const {
|
||||
auto cpu_addr = GpuToCpuAddress(src_addr);
|
||||
if (cpu_addr) {
|
||||
return memory.GetSpan(*cpu_addr, size);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
u8* MemoryManager::GetSpan(const GPUVAddr src_addr, const std::size_t size) {
|
||||
if (!IsContinuousRange(src_addr, size)) {
|
||||
return nullptr;
|
||||
}
|
||||
auto cpu_addr = GpuToCpuAddress(src_addr);
|
||||
if (cpu_addr) {
|
||||
return memory.GetSpan(*cpu_addr, size);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace Tegra
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "common/range_map.h"
|
||||
#include "common/scratch_buffer.h"
|
||||
#include "common/virtual_buffer.h"
|
||||
#include "core/memory.h"
|
||||
#include "video_core/cache_types.h"
|
||||
#include "video_core/pte_kind.h"
|
||||
|
||||
@ -62,6 +63,20 @@ public:
|
||||
[[nodiscard]] u8* GetPointer(GPUVAddr addr);
|
||||
[[nodiscard]] const u8* GetPointer(GPUVAddr addr) const;
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] T* GetPointer(GPUVAddr addr) {
|
||||
const auto address{GpuToCpuAddress(addr)};
|
||||
if (!address) {
|
||||
return {};
|
||||
}
|
||||
return memory.GetPointer(*address);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] const T* GetPointer(GPUVAddr addr) const {
|
||||
return GetPointer<T*>(addr);
|
||||
}
|
||||
|
||||
/**
|
||||
* ReadBlock and WriteBlock are full read and write operations over virtual
|
||||
* GPU Memory. It's important to use these when GPU memory may not be continuous
|
||||
@ -139,6 +154,9 @@ public:
|
||||
|
||||
void FlushCaching();
|
||||
|
||||
const u8* GetSpan(const GPUVAddr src_addr, const std::size_t size) const;
|
||||
u8* GetSpan(const GPUVAddr src_addr, const std::size_t size);
|
||||
|
||||
private:
|
||||
template <bool is_big_pages, typename FuncMapped, typename FuncReserved, typename FuncUnmapped>
|
||||
inline void MemoryOperation(GPUVAddr gpu_src_addr, std::size_t size, FuncMapped&& func_mapped,
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/memory.h"
|
||||
#include "video_core/control/channel_state.h"
|
||||
#include "video_core/dirty_flags.h"
|
||||
#include "video_core/engines/kepler_compute.h"
|
||||
@ -598,6 +599,10 @@ void TextureCache<P>::UnmapGPUMemory(size_t as_id, GPUVAddr gpu_addr, size_t siz
|
||||
[&](ImageId id, Image&) { deleted_images.push_back(id); });
|
||||
for (const ImageId id : deleted_images) {
|
||||
Image& image = slot_images[id];
|
||||
if (True(image.flags & ImageFlagBits::CpuModified)) {
|
||||
continue;
|
||||
}
|
||||
image.flags |= ImageFlagBits::CpuModified;
|
||||
if (True(image.flags & ImageFlagBits::Remapped)) {
|
||||
continue;
|
||||
}
|
||||
@ -1022,19 +1027,19 @@ void TextureCache<P>::UploadImageContents(Image& image, StagingBuffer& staging)
|
||||
runtime.AccelerateImageUpload(image, staging, uploads);
|
||||
return;
|
||||
}
|
||||
const size_t guest_size_bytes = image.guest_size_bytes;
|
||||
swizzle_data_buffer.resize_destructive(guest_size_bytes);
|
||||
gpu_memory->ReadBlockUnsafe(gpu_addr, swizzle_data_buffer.data(), guest_size_bytes);
|
||||
|
||||
Core::Memory::GpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::UnsafeRead> swizzle_data(
|
||||
*gpu_memory, gpu_addr, image.guest_size_bytes, &swizzle_data_buffer);
|
||||
|
||||
if (True(image.flags & ImageFlagBits::Converted)) {
|
||||
unswizzle_data_buffer.resize_destructive(image.unswizzled_size_bytes);
|
||||
auto copies = UnswizzleImage(*gpu_memory, gpu_addr, image.info, swizzle_data_buffer,
|
||||
unswizzle_data_buffer);
|
||||
auto copies =
|
||||
UnswizzleImage(*gpu_memory, gpu_addr, image.info, swizzle_data, unswizzle_data_buffer);
|
||||
ConvertImage(unswizzle_data_buffer, image.info, mapped_span, copies);
|
||||
image.UploadMemory(staging, copies);
|
||||
} else {
|
||||
const auto copies =
|
||||
UnswizzleImage(*gpu_memory, gpu_addr, image.info, swizzle_data_buffer, mapped_span);
|
||||
UnswizzleImage(*gpu_memory, gpu_addr, image.info, swizzle_data, mapped_span);
|
||||
image.UploadMemory(staging, copies);
|
||||
}
|
||||
}
|
||||
@ -1227,11 +1232,12 @@ void TextureCache<P>::QueueAsyncDecode(Image& image, ImageId image_id) {
|
||||
decode->image_id = image_id;
|
||||
async_decodes.push_back(std::move(decode));
|
||||
|
||||
Common::ScratchBuffer<u8> local_unswizzle_data_buffer(image.unswizzled_size_bytes);
|
||||
const size_t guest_size_bytes = image.guest_size_bytes;
|
||||
swizzle_data_buffer.resize_destructive(guest_size_bytes);
|
||||
gpu_memory->ReadBlockUnsafe(image.gpu_addr, swizzle_data_buffer.data(), guest_size_bytes);
|
||||
auto copies = UnswizzleImage(*gpu_memory, image.gpu_addr, image.info, swizzle_data_buffer,
|
||||
static Common::ScratchBuffer<u8> local_unswizzle_data_buffer;
|
||||
local_unswizzle_data_buffer.resize_destructive(image.unswizzled_size_bytes);
|
||||
Core::Memory::GpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::UnsafeRead> swizzle_data(
|
||||
*gpu_memory, image.gpu_addr, image.guest_size_bytes, &swizzle_data_buffer);
|
||||
|
||||
auto copies = UnswizzleImage(*gpu_memory, image.gpu_addr, image.info, swizzle_data,
|
||||
local_unswizzle_data_buffer);
|
||||
const size_t out_size = MapSizeBytes(image);
|
||||
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "common/div_ceil.h"
|
||||
#include "common/scratch_buffer.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/memory.h"
|
||||
#include "video_core/compatible_formats.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
@ -544,17 +545,15 @@ void SwizzleBlockLinearImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr
|
||||
tile_size.height, info.tile_width_spacing);
|
||||
const size_t subresource_size = sizes[level];
|
||||
|
||||
tmp_buffer.resize_destructive(subresource_size);
|
||||
const std::span<u8> dst(tmp_buffer);
|
||||
|
||||
for (s32 layer = 0; layer < info.resources.layers; ++layer) {
|
||||
const std::span<const u8> src = input.subspan(host_offset);
|
||||
gpu_memory.ReadBlockUnsafe(gpu_addr + guest_offset, dst.data(), dst.size_bytes());
|
||||
{
|
||||
Core::Memory::GpuGuestMemoryScoped<u8, Core::Memory::GuestMemoryFlags::UnsafeReadWrite>
|
||||
dst(gpu_memory, gpu_addr + guest_offset, subresource_size, &tmp_buffer);
|
||||
|
||||
SwizzleTexture(dst, src, bytes_per_block, num_tiles.width, num_tiles.height,
|
||||
num_tiles.depth, block.height, block.depth);
|
||||
|
||||
gpu_memory.WriteBlockUnsafe(gpu_addr + guest_offset, dst.data(), dst.size_bytes());
|
||||
SwizzleTexture(dst, src, bytes_per_block, num_tiles.width, num_tiles.height,
|
||||
num_tiles.depth, block.height, block.depth);
|
||||
}
|
||||
|
||||
host_offset += host_bytes_per_layer;
|
||||
guest_offset += layer_stride;
|
||||
@ -837,6 +836,7 @@ boost::container::small_vector<BufferImageCopy, 16> UnswizzleImage(Tegra::Memory
|
||||
const Extent3D size = info.size;
|
||||
|
||||
if (info.type == ImageType::Linear) {
|
||||
ASSERT(output.size_bytes() >= guest_size_bytes);
|
||||
gpu_memory.ReadBlockUnsafe(gpu_addr, output.data(), guest_size_bytes);
|
||||
|
||||
ASSERT((info.pitch >> bpp_log2) << bpp_log2 == info.pitch);
|
||||
@ -904,16 +904,6 @@ boost::container::small_vector<BufferImageCopy, 16> UnswizzleImage(Tegra::Memory
|
||||
return copies;
|
||||
}
|
||||
|
||||
BufferCopy UploadBufferCopy(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr,
|
||||
const ImageBase& image, std::span<u8> output) {
|
||||
gpu_memory.ReadBlockUnsafe(gpu_addr, output.data(), image.guest_size_bytes);
|
||||
return BufferCopy{
|
||||
.src_offset = 0,
|
||||
.dst_offset = 0,
|
||||
.size = image.guest_size_bytes,
|
||||
};
|
||||
}
|
||||
|
||||
void ConvertImage(std::span<const u8> input, const ImageInfo& info, std::span<u8> output,
|
||||
std::span<BufferImageCopy> copies) {
|
||||
u32 output_offset = 0;
|
||||
|
@ -66,9 +66,6 @@ struct OverlapResult {
|
||||
Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, const ImageInfo& info,
|
||||
std::span<const u8> input, std::span<u8> output);
|
||||
|
||||
[[nodiscard]] BufferCopy UploadBufferCopy(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr,
|
||||
const ImageBase& image, std::span<u8> output);
|
||||
|
||||
void ConvertImage(std::span<const u8> input, const ImageInfo& info, std::span<u8> output,
|
||||
std::span<BufferImageCopy> copies);
|
||||
|
||||
|
@ -485,7 +485,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
|
||||
}
|
||||
}
|
||||
if (extensions.extended_dynamic_state2 && (is_radv || is_qualcomm)) {
|
||||
if (extensions.extended_dynamic_state2 && is_radv) {
|
||||
const u32 version = (properties.properties.driverVersion << 3) >> 3;
|
||||
if (version < VK_MAKE_API_VERSION(0, 22, 3, 1)) {
|
||||
LOG_WARNING(
|
||||
@ -498,6 +498,20 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
|
||||
}
|
||||
}
|
||||
if (extensions.extended_dynamic_state2 && is_qualcomm) {
|
||||
const u32 version = (properties.properties.driverVersion << 3) >> 3;
|
||||
if (version >= VK_MAKE_API_VERSION(0, 0, 676, 0) &&
|
||||
version < VK_MAKE_API_VERSION(0, 0, 680, 0)) {
|
||||
// Qualcomm Adreno 7xx drivers do not properly support extended_dynamic_state2.
|
||||
LOG_WARNING(Render_Vulkan,
|
||||
"Qualcomm Adreno 7xx drivers have broken VK_EXT_extended_dynamic_state2");
|
||||
features.extended_dynamic_state2.extendedDynamicState2 = false;
|
||||
features.extended_dynamic_state2.extendedDynamicState2LogicOp = false;
|
||||
features.extended_dynamic_state2.extendedDynamicState2PatchControlPoints = false;
|
||||
extensions.extended_dynamic_state2 = false;
|
||||
loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
|
||||
}
|
||||
}
|
||||
if (extensions.extended_dynamic_state3 && is_radv) {
|
||||
LOG_WARNING(Render_Vulkan, "RADV has broken extendedDynamicState3ColorBlendEquation");
|
||||
features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable = false;
|
||||
@ -512,8 +526,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
dynamic_state3_enables = false;
|
||||
}
|
||||
}
|
||||
if (extensions.vertex_input_dynamic_state && (is_radv || is_qualcomm)) {
|
||||
// Qualcomm S8gen2 drivers do not properly support vertex_input_dynamic_state.
|
||||
if (extensions.vertex_input_dynamic_state && is_radv) {
|
||||
// TODO(ameerj): Blacklist only offending driver versions
|
||||
// TODO(ameerj): Confirm if RDNA1 is affected
|
||||
const bool is_rdna2 =
|
||||
@ -526,6 +539,19 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
loaded_extensions.erase(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
|
||||
}
|
||||
}
|
||||
if (extensions.vertex_input_dynamic_state && is_qualcomm) {
|
||||
const u32 version = (properties.properties.driverVersion << 3) >> 3;
|
||||
if (version >= VK_MAKE_API_VERSION(0, 0, 676, 0) &&
|
||||
version < VK_MAKE_API_VERSION(0, 0, 680, 0)) {
|
||||
// Qualcomm Adreno 7xx drivers do not properly support vertex_input_dynamic_state.
|
||||
LOG_WARNING(
|
||||
Render_Vulkan,
|
||||
"Qualcomm Adreno 7xx drivers have broken VK_EXT_vertex_input_dynamic_state");
|
||||
features.vertex_input_dynamic_state.vertexInputDynamicState = false;
|
||||
extensions.vertex_input_dynamic_state = false;
|
||||
loaded_extensions.erase(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
sets_per_pool = 64;
|
||||
if (extensions.extended_dynamic_state3 && is_amd_driver &&
|
||||
@ -774,6 +800,17 @@ bool Device::ShouldBoostClocks() const {
|
||||
return validated_driver && !is_steam_deck && !is_debugging;
|
||||
}
|
||||
|
||||
bool Device::HasTimelineSemaphore() const {
|
||||
if (GetDriverID() == VK_DRIVER_ID_QUALCOMM_PROPRIETARY ||
|
||||
GetDriverID() == VK_DRIVER_ID_MESA_TURNIP) {
|
||||
// Timeline semaphores do not work properly on all Qualcomm drivers.
|
||||
// They generally work properly with Turnip drivers, but are problematic on some devices
|
||||
// (e.g. ZTE handsets with Snapdragon 870).
|
||||
return false;
|
||||
}
|
||||
return features.timeline_semaphore.timelineSemaphore;
|
||||
}
|
||||
|
||||
bool Device::GetSuitability(bool requires_swapchain) {
|
||||
// Assume we will be suitable.
|
||||
bool suitable = true;
|
||||
|
@ -528,13 +528,7 @@ public:
|
||||
return extensions.shader_atomic_int64;
|
||||
}
|
||||
|
||||
bool HasTimelineSemaphore() const {
|
||||
if (GetDriverID() == VK_DRIVER_ID_QUALCOMM_PROPRIETARY) {
|
||||
// Timeline semaphores do not work properly on all Qualcomm drivers.
|
||||
return false;
|
||||
}
|
||||
return features.timeline_semaphore.timelineSemaphore;
|
||||
}
|
||||
bool HasTimelineSemaphore() const;
|
||||
|
||||
/// Returns the minimum supported version of SPIR-V.
|
||||
u32 SupportedSpirvVersion() const {
|
||||
|
@ -221,8 +221,8 @@ vk::Image MemoryAllocator::CreateImage(const VkImageCreateInfo& ci) const {
|
||||
const VmaAllocationCreateInfo alloc_ci = {
|
||||
.flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT,
|
||||
.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE,
|
||||
.requiredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
|
||||
.preferredFlags = 0,
|
||||
.requiredFlags = 0,
|
||||
.preferredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
|
||||
.memoryTypeBits = 0,
|
||||
.pool = VK_NULL_HANDLE,
|
||||
.pUserData = nullptr,
|
||||
|
@ -7,6 +7,11 @@
|
||||
#include <wincon.h>
|
||||
#endif
|
||||
|
||||
#include <QAction>
|
||||
#include <QLayout>
|
||||
#include <QString>
|
||||
#include <QScrollBar>
|
||||
|
||||
#include "common/logging/backend.h"
|
||||
#include "yuzu/debugger/console.h"
|
||||
#include "yuzu/uisettings.h"
|
||||
@ -46,4 +51,66 @@ void ToggleConsole() {
|
||||
SetColorConsoleBackendEnabled(UISettings::values.show_console.GetValue());
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
GameLogOuput::GameLogOuput(QWidget* parent)
|
||||
: QWidget(parent, Qt::Dialog) {
|
||||
setObjectName(QStringLiteral("Game Log Output"));
|
||||
setWindowTitle(tr("Game Log Output"));
|
||||
resize(500, 350);
|
||||
setMinimumSize(500, 350);
|
||||
// Remove the "?" button from the titlebar and enable the maximize button
|
||||
setWindowFlags((windowFlags() & ~Qt::WindowContextHelpButtonHint) |
|
||||
Qt::WindowMaximizeButtonHint);
|
||||
|
||||
log_output = new QPlainTextEdit(this);
|
||||
log_output->setReadOnly(true);
|
||||
QLayout* layout = new QVBoxLayout(this);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout->addWidget(log_output);
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
void GameLogOuput::AppendMessage(const QString& text) {
|
||||
log_queue.Push(text);
|
||||
// Scrolls to the bottom
|
||||
//log_output->verticalScrollBar()->setValue(
|
||||
// log_output->verticalScrollBar()->maximum());
|
||||
}
|
||||
|
||||
|
||||
void GameLogOuput::paintEvent(QPaintEvent* event) {
|
||||
QWidget::paintEvent(event);
|
||||
while (!log_queue.Empty()) {
|
||||
QString text{};
|
||||
log_queue.Pop(text);
|
||||
log_output->appendPlainText(text);
|
||||
}
|
||||
}
|
||||
|
||||
QAction* GameLogOuput::toggleViewAction() {
|
||||
if (toggle_view_action == nullptr) {
|
||||
toggle_view_action = new QAction(tr("&Game Log Ouput"), this);
|
||||
toggle_view_action->setCheckable(true);
|
||||
toggle_view_action->setChecked(isVisible());
|
||||
connect(toggle_view_action, &QAction::toggled, this, &GameLogOuput::setVisible);
|
||||
}
|
||||
|
||||
return toggle_view_action;
|
||||
}
|
||||
|
||||
void GameLogOuput::showEvent(QShowEvent* ev) {
|
||||
if (toggle_view_action) {
|
||||
toggle_view_action->setChecked(isVisible());
|
||||
}
|
||||
QWidget::showEvent(ev);
|
||||
}
|
||||
|
||||
void GameLogOuput::hideEvent(QHideEvent* ev) {
|
||||
if (toggle_view_action) {
|
||||
toggle_view_action->setChecked(isVisible());
|
||||
}
|
||||
QWidget::hideEvent(ev);
|
||||
}
|
||||
|
||||
} // namespace Debugger
|
||||
|
@ -3,6 +3,16 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <QWidget>
|
||||
#include <QPlainTextEdit>
|
||||
|
||||
#include "common/threadsafe_queue.h"
|
||||
|
||||
namespace Core::HID {
|
||||
class HIDCore;
|
||||
} // namespace Core::HID
|
||||
|
||||
namespace Debugger {
|
||||
|
||||
/**
|
||||
@ -10,4 +20,27 @@ namespace Debugger {
|
||||
* get a real qt logging window which would work for all platforms.
|
||||
*/
|
||||
void ToggleConsole();
|
||||
|
||||
class GameLogOuput : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit GameLogOuput(QWidget* parent = nullptr);
|
||||
|
||||
void AppendMessage(const QString& text);
|
||||
|
||||
/// Returns a QAction that can be used to toggle visibility of this dialog.
|
||||
QAction* toggleViewAction();
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent* ev) override;
|
||||
void hideEvent(QHideEvent* ev) override;
|
||||
void paintEvent(QPaintEvent* event) override;
|
||||
|
||||
private:
|
||||
QAction* toggle_view_action = nullptr;
|
||||
QPlainTextEdit* log_output = nullptr;
|
||||
/// Queue of vibration request to controllers
|
||||
Common::SPSCQueue<QString> log_queue;
|
||||
};
|
||||
} // namespace Debugger
|
@ -178,6 +178,8 @@ constexpr int default_mouse_hide_timeout = 2500;
|
||||
constexpr int default_mouse_center_timeout = 10;
|
||||
constexpr int default_input_update_timeout = 1;
|
||||
|
||||
constexpr size_t CopyBufferSize = 1_MiB;
|
||||
|
||||
/**
|
||||
* "Callouts" are one-time instructional messages shown to the user. In the config settings, there
|
||||
* is a bitfield "callout_flags" options, used to track if a message has already been shown to the
|
||||
@ -454,7 +456,7 @@ GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan
|
||||
// the user through their desktop environment.
|
||||
//: TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the
|
||||
//: computer from sleeping
|
||||
QByteArray wakelock_reason = tr("Running a game").toLatin1();
|
||||
QByteArray wakelock_reason = tr("Running a game").toUtf8();
|
||||
SDL_SetHint(SDL_HINT_SCREENSAVER_INHIBIT_ACTIVITY_NAME, wakelock_reason.data());
|
||||
|
||||
// SDL disables the screen saver by default, and setting the hint
|
||||
@ -1246,6 +1248,10 @@ void GMainWindow::InitializeDebugWidgets() {
|
||||
controller_dialog->hide();
|
||||
debug_menu->addAction(controller_dialog->toggleViewAction());
|
||||
|
||||
game_log_ouput_widget = new Debugger::GameLogOuput(this);
|
||||
game_log_ouput_widget->hide();
|
||||
debug_menu->addAction(game_log_ouput_widget->toggleViewAction());
|
||||
|
||||
connect(this, &GMainWindow::EmulationStarting, waitTreeWidget,
|
||||
&WaitTreeWidget::OnEmulationStarting);
|
||||
connect(this, &GMainWindow::EmulationStopping, waitTreeWidget,
|
||||
@ -1874,6 +1880,11 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
|
||||
render_window->Exit();
|
||||
});
|
||||
|
||||
// Register an Exit callback such that Core can exit the currently running application.
|
||||
system->RegisterGameLogCallback([this](std::string text) {
|
||||
game_log_ouput_widget->AppendMessage(QString::fromStdString(text));
|
||||
});
|
||||
|
||||
connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame);
|
||||
connect(render_window, &GRenderWindow::MouseActivity, this, &GMainWindow::OnMouseActivity);
|
||||
// BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views
|
||||
@ -2929,10 +2940,10 @@ void GMainWindow::OnMenuInstallToNAND() {
|
||||
|
||||
int remaining = filenames.size();
|
||||
|
||||
// This would only overflow above 2^43 bytes (8.796 TB)
|
||||
// This would only overflow above 2^51 bytes (2.252 PB)
|
||||
int total_size = 0;
|
||||
for (const QString& file : files) {
|
||||
total_size += static_cast<int>(QFile(file).size() / 0x1000);
|
||||
total_size += static_cast<int>(QFile(file).size() / CopyBufferSize);
|
||||
}
|
||||
if (total_size < 0) {
|
||||
LOG_CRITICAL(Frontend, "Attempting to install too many files, aborting.");
|
||||
@ -3032,7 +3043,7 @@ InstallResult GMainWindow::InstallNSPXCI(const QString& filename) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<u8> buffer(1_MiB);
|
||||
std::vector<u8> buffer(CopyBufferSize);
|
||||
|
||||
for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) {
|
||||
if (install_progress->wasCanceled()) {
|
||||
@ -3088,7 +3099,7 @@ InstallResult GMainWindow::InstallNCA(const QString& filename) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::array<u8, 0x1000> buffer{};
|
||||
std::vector<u8> buffer(CopyBufferSize);
|
||||
|
||||
for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) {
|
||||
if (install_progress->wasCanceled()) {
|
||||
|
@ -72,6 +72,10 @@ struct KeyboardInitializeParameters;
|
||||
struct ProfileSelectParameters;
|
||||
} // namespace Core::Frontend
|
||||
|
||||
namespace Debugger {
|
||||
class GameLogOuput;
|
||||
}
|
||||
|
||||
namespace DiscordRPC {
|
||||
class DiscordInterface;
|
||||
}
|
||||
@ -468,6 +472,7 @@ private:
|
||||
MicroProfileDialog* microProfileDialog;
|
||||
WaitTreeWidget* waitTreeWidget;
|
||||
ControllerDialog* controller_dialog;
|
||||
Debugger::GameLogOuput* game_log_ouput_widget;
|
||||
|
||||
QAction* actions_recent_files[max_recent_files_item];
|
||||
|
||||
|
Reference in New Issue
Block a user