Compare commits

..

47 Commits

Author SHA1 Message Date
t895
f44183db9e android: Use utility function for applying view margins 2024-02-09 07:07:06 -05:00
t895
5fa9bc192c android: Add thermal throttling overlay 2024-02-09 07:07:05 -05:00
liamwhite
71e59bdcd8 Merge pull request #12963 from t895/versioning-fix
android: Fix regex for git version
2024-02-08 17:03:32 -05:00
t895
0a1283f94f android: Fix regex for git version 2024-02-08 14:24:15 -05:00
Fernando S
f049453dd6 Merge pull request #12903 from liamwhite/const-offset
shader_recompiler: use only ConstOffset for OpImageFetch
2024-02-08 17:00:45 +01:00
liamwhite
cac37a6f6e Merge pull request #12954 from german77/hidbus-interface
service: hid: Migrate hidbus to new interface
2024-02-08 11:00:11 -05:00
liamwhite
263dfa95e4 Merge pull request #12914 from FernandoS27/vc-refactor
VideoCore Refactor Part 1.
2024-02-08 10:59:59 -05:00
liamwhite
bc9711cb1e Merge pull request #12953 from FernandoS27/zero-fps-mah-ass
SMMU: Ensure the backing address range matches the current
2024-02-08 10:59:52 -05:00
Narr the Reg
b4d88a7bb4 service: hid: Migrate hidbus to new interface 2024-02-07 18:07:32 -06:00
Fernando Sahmkow
ae833aa9c0 SMMU: Ensure the backing address range matches the current 2024-02-07 23:47:42 +01:00
liamwhite
4463ded603 Merge pull request #12939 from german77/wonder
dmnt: cheat: Invalidate cache on memory writes
2024-02-07 15:33:44 -05:00
liamwhite
159dec01ee Merge pull request #12932 from german77/any-key-is-good
yuzu: Make controller keys easier to assign
2024-02-07 15:33:39 -05:00
liamwhite
6319bafafa Merge pull request #12912 from FearlessTobi/ports-feb-24
Port some small changes from Citra (web_backend and translations)
2024-02-07 15:33:28 -05:00
Charles Lombardo
c000a5ff09 Merge pull request #12909 from t895/play-store-automation
ci: android: Play store publishing setup
2024-02-07 15:32:42 -05:00
german77
12f86f89fc yuzu: Make controller keys easier to assign 2024-02-06 16:51:39 -06:00
Narr the Reg
9858ea79fb dmnt: cheat: Invalidate cache on memory writes 2024-02-06 13:49:48 -06:00
liamwhite
c10e720ba9 Merge pull request #12883 from FernandoS27/memory_manager_mem
MemoryManager: Reduce the page table size based on last big page address.
2024-02-06 10:25:03 -05:00
liamwhite
5016de3626 Merge pull request #12928 from german77/motion-mp
service: hid: Add multiprocess support to six axis input
2024-02-06 10:24:46 -05:00
liamwhite
d5fb9fd12c Merge pull request #12933 from german77/irs-interface
service: irs: Migrate service to new interface
2024-02-06 10:24:30 -05:00
liamwhite
c79b3af610 Merge pull request #12934 from german77/hid_debug_interface
service: hid: Migrate hid debug service to new interface
2024-02-06 10:24:20 -05:00
FearlessTobi
c0a383d960 web_backend: Fix compilation 2024-02-06 15:48:04 +01:00
german77
b6106604c4 service: hid: Migrate hid debug service to new interface 2024-02-06 00:38:46 -06:00
german77
12b6162852 service: irs: Migrate service to new interface 2024-02-06 00:14:16 -06:00
german77
8f192b494a service: hid: Add multiprocess support to six axis input 2024-02-05 17:19:31 -06:00
german77
372897aac4 service: hid: Ensure aruid data is initialized 2024-02-05 17:17:21 -06:00
Fernando Sahmkow
fa47ac1c9f Common: Rename SplitRangeSet to OverlapRangeSet 2024-02-05 23:01:17 +01:00
Charles Lombardo
a2f23746c2 Merge pull request #12905 from liamwhite/hwc-release
nvnflinger: release buffers before presentation sleep
2024-02-05 13:43:22 -05:00
Charles Lombardo
215b13f2a2 Merge pull request #12924 from liamwhite/pedantic-unsigned
typed_address: test values are unsigned
2024-02-05 13:43:06 -05:00
liamwhite
35ed9425d7 Merge pull request #12925 from german77/linux-tab
yuzu: Fully hide linux tab
2024-02-05 13:41:31 -05:00
liamwhite
74cc8721c7 Merge pull request #12915 from german77/cheat
dmnt: cheats: Update cheat vm to latest version
2024-02-05 13:41:21 -05:00
liamwhite
8ef1db78b0 Merge pull request #12916 from liamwhite/float-fix
gdb: fix load/save of fp values in a32
2024-02-05 13:41:15 -05:00
Charles Lombardo
18c8f10ff2 Merge pull request #12922 from FearlessTobi/lang-mappins
.tx/config: Use language mappings for android "tx pull"
2024-02-05 13:40:53 -05:00
Liam
0e950baf41 typed_address: test values are unsigned 2024-02-05 12:47:10 -05:00
german77
8113f55f4b dmnt: cheats: Silence memory errors 2024-02-05 11:08:24 -06:00
Liam
f296a9ce9a shader_recompiler: use only ConstOffset for OpImageFetch 2024-02-05 12:01:09 -05:00
FearlessTobi
ddbefc71cb .tx/config: Use language mappings for android "tx pull"
The language names we are using in the android resources differ from those on Transifex.

We need to manually specify mappings for them, so Transifex is able to place the files in the correct folders.
2024-02-05 15:57:13 +01:00
Fernando Sahmkow
0d5a3abeae Buffer Cache: Refactor to use Range sets instead 2024-02-05 11:06:52 +01:00
Liam
85143e8376 gdb: fix load/save of fp values in a32 2024-02-04 20:28:43 -05:00
german77
504abbd6e0 dmnt: cheats: Update cheat vm to latest version 2024-02-04 17:46:20 -06:00
Fernando Sahmkow
accccc0cbf NVDRV: Refactor HeapMapper to use RangeSets 2024-02-04 20:01:50 +01:00
Fernando Sahmkow
01ba6cf610 Common: Introduce Range Sets 2024-02-04 20:01:50 +01:00
Fernando Sahmkow
4841dc0b74 VideoCore: Move Slot Vector to Common 2024-02-04 20:01:47 +01:00
Tobias
185125e4e4 citra_qt/configure_ui: Show country of language in the combobox
This prevents an issue where we had seperate versions of the same language for different regions and they were not distinguishable (e.g. "Chinese (China)" and "Chinese (Taiwan)").

Also makes it so we do not need to hardcode specific languages anymore.
2024-02-04 17:06:44 +01:00
t895
99ea31faa8 ci: android: Play store publishing setup 2024-02-04 10:54:18 -05:00
FearlessTobi
9ade941de1 web_backend: Sync with Citra implementation
While porting https://github.com/citra-emu/citra/pull/7347, I noticed the code of yuzu was not up-to-date with the implementation from Citra.
2024-02-04 16:51:52 +01:00
Liam
5eb5c96750 nvnflinger: release buffers before presentation sleep 2024-02-03 17:14:43 -05:00
Fernando Sahmkow
f740d8b9be MemoryManager: Reduce the page table size based on last big page address. 2024-02-01 13:00:36 +01:00
87 changed files with 2036 additions and 1996 deletions

View File

@@ -0,0 +1,21 @@
#!/bin/bash -ex
# SPDX-FileCopyrightText: 2024 yuzu Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
export NDK_CCACHE="$(which ccache)"
ccache -s
export ANDROID_KEYSTORE_FILE="${GITHUB_WORKSPACE}/ks.jks"
base64 --decode <<< "${EA_PLAY_ANDROID_KEYSTORE_B64}" > "${ANDROID_KEYSTORE_FILE}"
export ANDROID_KEY_ALIAS="${PLAY_ANDROID_KEY_ALIAS}"
export ANDROID_KEYSTORE_PASS="${PLAY_ANDROID_KEYSTORE_PASS}"
export SERVICE_ACCOUNT_KEY_PATH="${GITHUB_WORKSPACE}/sa.json"
base64 --decode <<< "${EA_SERVICE_ACCOUNT_KEY_B64}" > "${SERVICE_ACCOUNT_KEY_PATH}"
./gradlew "publishEaReleaseBundle"
ccache -s
if [ ! -z "${ANDROID_KEYSTORE_B64}" ]; then
rm "${ANDROID_KEYSTORE_FILE}"
fi

View File

@@ -0,0 +1,21 @@
#!/bin/bash -ex
# SPDX-FileCopyrightText: 2024 yuzu Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
export NDK_CCACHE="$(which ccache)"
ccache -s
export ANDROID_KEYSTORE_FILE="${GITHUB_WORKSPACE}/ks.jks"
base64 --decode <<< "${MAINLINE_PLAY_ANDROID_KEYSTORE_B64}" > "${ANDROID_KEYSTORE_FILE}"
export ANDROID_KEY_ALIAS="${PLAY_ANDROID_KEY_ALIAS}"
export ANDROID_KEYSTORE_PASS="${PLAY_ANDROID_KEYSTORE_PASS}"
export SERVICE_ACCOUNT_KEY_PATH="${GITHUB_WORKSPACE}/sa.json"
base64 --decode <<< "${MAINLINE_SERVICE_ACCOUNT_KEY_B64}" > "${SERVICE_ACCOUNT_KEY_PATH}"
./gradlew "publishMainlineReleaseBundle"
ccache -s
if [ ! -z "${ANDROID_KEYSTORE_B64}" ]; then
rm "${ANDROID_KEYSTORE_FILE}"
fi

View File

@@ -0,0 +1,66 @@
# SPDX-FileCopyrightText: 2024 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
name: yuzu-android-ea-play-release
on:
workflow_dispatch:
inputs:
release-track:
description: 'Play store release track (internal/alpha/beta/production)'
required: true
default: 'alpha'
jobs:
android:
runs-on: ubuntu-latest
if: ${{ github.repository == 'yuzu-emu/yuzu' }}
steps:
- uses: actions/checkout@v3
name: Checkout
with:
fetch-depth: 0
submodules: true
token: ${{ secrets.ALT_GITHUB_TOKEN }}
- run: npm install execa@5
- uses: actions/github-script@v5
name: 'Merge and publish Android EA changes'
env:
ALT_GITHUB_TOKEN: ${{ secrets.ALT_GITHUB_TOKEN }}
BUILD_EA: true
with:
script: |
const execa = require("execa");
const mergebot = require('./.github/workflows/android-merge.js').mergebot;
process.chdir('${{ github.workspace }}');
mergebot(github, context, execa);
- name: Get tag name
run: echo "GIT_TAG_NAME=$(cat tag-name.txt)" >> $GITHUB_ENV
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
- 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/eabuild.sh
env:
EA_PLAY_ANDROID_KEYSTORE_B64: ${{ secrets.PLAY_ANDROID_KEYSTORE_B64 }}
PLAY_ANDROID_KEY_ALIAS: ${{ secrets.PLAY_ANDROID_KEY_ALIAS }}
PLAY_ANDROID_KEYSTORE_PASS: ${{ secrets.PLAY_ANDROID_KEYSTORE_PASS }}
EA_SERVICE_ACCOUNT_KEY_B64: ${{ secrets.EA_SERVICE_ACCOUNT_KEY_B64 }}
STORE_TRACK: ${{ github.event.inputs.release-track }}
AUTO_VERSIONED: true
BUILD_EA: true
- name: Create release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ env.EA_TAG_NAME }}
name: ${{ env.EA_TAG_NAME }}
draft: false
prerelease: false
repository: yuzu/yuzu-android
token: ${{ secrets.ALT_GITHUB_TOKEN }}

View File

@@ -0,0 +1,59 @@
# SPDX-FileCopyrightText: 2024 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
name: yuzu-android-mainline-play-release
on:
workflow_dispatch:
inputs:
release-tag:
description: 'Tag # from yuzu-android that you want to build and publish'
required: true
default: '200'
release-track:
description: 'Play store release track (internal/alpha/beta/production)'
required: true
default: 'alpha'
jobs:
android:
runs-on: ubuntu-latest
if: ${{ github.repository == 'yuzu-emu/yuzu' }}
steps:
- uses: actions/checkout@v3
name: Checkout
with:
fetch-depth: 0
submodules: true
token: ${{ secrets.ALT_GITHUB_TOKEN }}
- run: npm install execa@5
- uses: actions/github-script@v5
name: 'Pull mainline tag'
env:
MAINLINE_TAG: ${{ github.event.inputs.release-tag }}
with:
script: |
const execa = require("execa");
const mergebot = require('./.github/workflows/android-merge.js').getMainlineTag;
process.chdir('${{ github.workspace }}');
mergebot(execa);
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y ccache apksigner glslang-dev glslang-tools
- name: Build
run: |
echo "GIT_TAG_NAME=android-${{ github.event.inputs.releast-tag }}" >> $GITHUB_ENV
./.ci/scripts/android/mainlinebuild.sh
env:
MAINLINE_PLAY_ANDROID_KEYSTORE_B64: ${{ secrets.PLAY_ANDROID_KEYSTORE_B64 }}
PLAY_ANDROID_KEY_ALIAS: ${{ secrets.PLAY_ANDROID_KEY_ALIAS }}
PLAY_ANDROID_KEYSTORE_PASS: ${{ secrets.PLAY_ANDROID_KEYSTORE_PASS }}
SERVICE_ACCOUNT_KEY_B64: ${{ secrets.MAINLINE_SERVICE_ACCOUNT_KEY_B64 }}
STORE_TRACK: ${{ github.event.inputs.release-track }}
AUTO_VERSIONED: true

View File

@@ -6,9 +6,12 @@
const fs = require("fs");
// which label to check for changes
const CHANGE_LABEL = 'android-merge';
const CHANGE_LABEL_MAINLINE = 'android-merge';
const CHANGE_LABEL_EA = 'android-ea-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);
const BUILD_EA = process.env.BUILD_EA == 'true';
const MAINLINE_TAG = process.env.MAINLINE_TAG;
async function checkBaseChanges(github) {
// query the commit date of the latest commit on this branch
@@ -40,20 +43,7 @@ async function checkBaseChanges(github) {
async function checkAndroidChanges(github) {
if (checkBaseChanges(github)) 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: 'yuzu-emu',
name: 'yuzu',
label: CHANGE_LABEL,
};
const result = await github.graphql(query, variables);
const pulls = result.repository.pullRequests.nodes;
const pulls = getPulls(github, false);
for (let i = 0; i < pulls.length; i++) {
let pull = pulls[i];
if (new Date() - new Date(pull.headRepository.pushedAt) <= DETECTION_TIME_FRAME) {
@@ -83,7 +73,13 @@ async function tagAndPush(github, owner, repo, execa, commit=false) {
};
const tags = await github.graphql(query, variables);
const tagList = tags.repository.refs.nodes;
const lastTag = tagList[0] ? tagList[0].name : 'dummy-0';
let lastTag = 'android-1';
for (let i = 0; i < tagList.length; ++i) {
if (tagList[i].name.includes('android-')) {
lastTag = tagList[i].name;
break;
}
}
const tagNumber = /\w+-(\d+)/.exec(lastTag)[1] | 0;
const channel = repo.split('-')[1];
const newTag = `${channel}-${tagNumber + 1}`;
@@ -101,6 +97,48 @@ async function tagAndPush(github, owner, repo, execa, commit=false) {
console.info('Successfully pushed new changes.');
}
async function tagAndPushEA(github, owner, repo, execa) {
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;
let lastTag = 'ea-1';
for (let i = 0; i < tagList.length; ++i) {
if (tagList[i].name.includes('ea-')) {
lastTag = tagList[i].name;
break;
}
}
const tagNumber = /\w+-(\d+)/.exec(lastTag)[1] | 0;
const newTag = `ea-${tagNumber + 1}`;
console.log(`New tag: ${newTag}`);
console.info('Pushing tags to GitHub ...');
await execa("git", ["remote", "add", "android", "https://github.com/yuzu-emu/yuzu-android.git"]);
await execa("git", ["fetch", "android"]);
await execa("git", ['tag', newTag]);
await execa("git", ['push', 'android', `${newTag}`]);
fs.writeFile('tag-name.txt', newTag, (err) => {
if (err) throw 'Could not write tag name to file!'
})
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 =
@@ -202,10 +240,7 @@ async function resetBranch(execa) {
}
}
async function mergebot(github, context, execa) {
// Reset our local copy of master to what appears on yuzu-emu/yuzu - master
await resetBranch(execa);
async function getPulls(github) {
const query = `query ($owner:String!, $name:String!, $label:String!) {
repository(name:$name, owner:$owner) {
pullRequests(labels: [$label], states: OPEN, first: 100) {
@@ -215,13 +250,49 @@ async function mergebot(github, context, execa) {
}
}
}`;
const variables = {
const mainlineVariables = {
owner: 'yuzu-emu',
name: 'yuzu',
label: CHANGE_LABEL,
label: CHANGE_LABEL_MAINLINE,
};
const result = await github.graphql(query, variables);
const pulls = result.repository.pullRequests.nodes;
const mainlineResult = await github.graphql(query, mainlineVariables);
const pulls = mainlineResult.repository.pullRequests.nodes;
if (BUILD_EA) {
const eaVariables = {
owner: 'yuzu-emu',
name: 'yuzu',
label: CHANGE_LABEL_EA,
};
const eaResult = await github.graphql(query, eaVariables);
const eaPulls = eaResult.repository.pullRequests.nodes;
return pulls.concat(eaPulls);
}
return pulls;
}
async function getMainlineTag(execa) {
console.log(`::group::Getting mainline tag android-${MAINLINE_TAG}`);
let hasFailed = false;
try {
await execa("git", ["remote", "add", "mainline", "https://github.com/yuzu-emu/yuzu-android.git"]);
await execa("git", ["fetch", "mainline", "--tags"]);
await execa("git", ["checkout", `tags/android-${MAINLINE_TAG}`]);
await execa("git", ["submodule", "update", "--init", "--recursive"]);
} catch (err) {
console.log('::error title=Failed pull tag');
hasFailed = true;
}
console.log("::endgroup::");
if (hasFailed) {
throw 'Failed pull mainline tag. Aborting!';
}
}
async function mergebot(github, context, execa) {
// Reset our local copy of master to what appears on yuzu-emu/yuzu - master
await resetBranch(execa);
const pulls = await getPulls(github);
let displayList = [];
for (let i = 0; i < pulls.length; i++) {
let pull = pulls[i];
@@ -231,11 +302,17 @@ async function mergebot(github, context, execa) {
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, 'yuzu-emu', `yuzu-android`, execa, true);
if (BUILD_EA) {
await tagAndPushEA(github, 'yuzu-emu', `yuzu-android`, execa);
} else {
await generateReadme(pulls, context, mergeResults, execa);
await tagAndPush(github, 'yuzu-emu', `yuzu-android`, execa, true);
}
}
module.exports.mergebot = mergebot;
module.exports.checkAndroidChanges = checkAndroidChanges;
module.exports.tagAndPush = tagAndPush;
module.exports.checkBaseChanges = checkBaseChanges;
module.exports.getMainlineTag = getMainlineTag;

View File

@@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2023 yuzu Emulator Project
# SPDX-FileCopyrightText: 2024 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
name: yuzu-android-publish
@@ -16,7 +16,7 @@ on:
jobs:
android:
runs-on: ubuntu-latest
if: ${{ github.event.inputs.android != 'false' && github.repository == 'yuzu-emu/yuzu-android' }}
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

View File

@@ -11,3 +11,4 @@ type = QT
file_filter = ../../src/android/app/src/main/res/values-<lang>/strings.xml
source_file = ../../src/android/app/src/main/res/values/strings.xml
type = ANDROID
lang_map = ja_JP:ja, ko_KR:ko, pt_BR:pt-rBR, pt_PT:pt-rPT, ru_RU:ru, vi_VN:vi, zh_CN:zh-rCN, zh_TW:zh-rTW

View File

@@ -3,8 +3,8 @@
import android.annotation.SuppressLint
import kotlin.collections.setOf
import org.jetbrains.kotlin.konan.properties.Properties
import org.jlleitschuh.gradle.ktlint.reporter.ReporterType
import com.github.triplet.gradle.androidpublisher.ReleaseStatus
plugins {
id("com.android.application")
@@ -13,6 +13,7 @@ plugins {
kotlin("plugin.serialization") version "1.9.20"
id("androidx.navigation.safeargs.kotlin")
id("org.jlleitschuh.gradle.ktlint") version "11.4.0"
id("com.github.triplet.play") version "3.8.6"
}
/**
@@ -58,15 +59,7 @@ android {
targetSdk = 34
versionName = getGitVersion()
// If you want to use autoVersion for the versionCode, create a property in local.properties
// named "autoVersioned" and set it to "true"
val properties = Properties()
val versionProperty = try {
properties.load(project.rootProject.file("local.properties").inputStream())
properties.getProperty("autoVersioned") ?: ""
} catch (e: Exception) { "" }
versionCode = if (versionProperty == "true") {
versionCode = if (System.getenv("AUTO_VERSIONED") == "true") {
autoVersion
} else {
1
@@ -221,6 +214,15 @@ ktlint {
}
}
play {
val keyPath = System.getenv("SERVICE_ACCOUNT_KEY_PATH")
if (keyPath != null) {
serviceAccountCredentials.set(File(keyPath))
}
track.set(System.getenv("STORE_TRACK") ?: "internal")
releaseStatus.set(ReleaseStatus.COMPLETED)
}
dependencies {
implementation("androidx.core:core-ktx:1.12.0")
implementation("androidx.appcompat:appcompat:1.6.1")
@@ -257,12 +259,18 @@ fun runGitCommand(command: List<String>): String {
}
fun getGitVersion(): String {
val gitVersion = runGitCommand(
listOf(
"git",
"describe",
"--always",
"--long"
)
).replace(Regex("(-0)?-[^-]+$"), "")
val versionName = if (System.getenv("GITHUB_ACTIONS") != null) {
val gitTag = System.getenv("GIT_TAG_NAME") ?: ""
gitTag
System.getenv("GIT_TAG_NAME") ?: gitVersion
} else {
runGitCommand(listOf("git", "describe", "--always", "--long"))
.replace(Regex("(-0)?-[^-]+$"), "")
gitVersion
}
return versionName.ifEmpty { "0.0" }
}

View File

@@ -25,7 +25,8 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
HAPTIC_FEEDBACK("haptic_feedback"),
SHOW_PERFORMANCE_OVERLAY("show_performance_overlay"),
SHOW_INPUT_OVERLAY("show_input_overlay"),
TOUCHSCREEN("touchscreen");
TOUCHSCREEN("touchscreen"),
SHOW_THERMAL_OVERLAY("show_thermal_overlay");
override fun getBoolean(needsGlobal: Boolean): Boolean =
NativeConfig.getBoolean(key, needsGlobal)

View File

@@ -8,7 +8,6 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.MarginLayoutParams
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
@@ -27,6 +26,7 @@ import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.FragmentSettingsBinding
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.model.SettingsViewModel
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
class SettingsFragment : Fragment() {
private lateinit var presenter: SettingsFragmentPresenter
@@ -125,18 +125,10 @@ class SettingsFragment : Fragment() {
val leftInsets = barInsets.left + cutoutInsets.left
val rightInsets = barInsets.right + cutoutInsets.right
val mlpSettingsList = binding.listSettings.layoutParams as MarginLayoutParams
mlpSettingsList.leftMargin = leftInsets
mlpSettingsList.rightMargin = rightInsets
binding.listSettings.layoutParams = mlpSettingsList
binding.listSettings.updatePadding(
bottom = barInsets.bottom
)
binding.listSettings.updateMargins(left = leftInsets, right = rightInsets)
binding.listSettings.updatePadding(bottom = barInsets.bottom)
val mlpAppBar = binding.appbarSettings.layoutParams as MarginLayoutParams
mlpAppBar.leftMargin = leftInsets
mlpAppBar.rightMargin = rightInsets
binding.appbarSettings.layoutParams = mlpAppBar
binding.appbarSettings.updateMargins(left = leftInsets, right = rightInsets)
windowInsets
}
}

View File

@@ -13,7 +13,6 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.MarginLayoutParams
import android.widget.Toast
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
@@ -26,6 +25,7 @@ import org.yuzu.yuzu_emu.BuildConfig
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.FragmentAboutBinding
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
class AboutFragment : Fragment() {
private var _binding: FragmentAboutBinding? = null
@@ -114,15 +114,8 @@ class AboutFragment : Fragment() {
val leftInsets = barInsets.left + cutoutInsets.left
val rightInsets = barInsets.right + cutoutInsets.right
val mlpToolbar = binding.toolbarAbout.layoutParams as MarginLayoutParams
mlpToolbar.leftMargin = leftInsets
mlpToolbar.rightMargin = rightInsets
binding.toolbarAbout.layoutParams = mlpToolbar
val mlpScrollAbout = binding.scrollAbout.layoutParams as MarginLayoutParams
mlpScrollAbout.leftMargin = leftInsets
mlpScrollAbout.rightMargin = rightInsets
binding.scrollAbout.layoutParams = mlpScrollAbout
binding.toolbarAbout.updateMargins(left = leftInsets, right = rightInsets)
binding.scrollAbout.updateMargins(left = leftInsets, right = rightInsets)
binding.contentAbout.updatePadding(bottom = barInsets.bottom)

View File

@@ -31,6 +31,7 @@ import org.yuzu.yuzu_emu.model.AddonViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.AddonUtil
import org.yuzu.yuzu_emu.utils.FileUtil.copyFilesTo
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
import java.io.File
class AddonsFragment : Fragment() {
@@ -202,27 +203,19 @@ class AddonsFragment : Fragment() {
val leftInsets = barInsets.left + cutoutInsets.left
val rightInsets = barInsets.right + cutoutInsets.right
val mlpToolbar = binding.toolbarAddons.layoutParams as ViewGroup.MarginLayoutParams
mlpToolbar.leftMargin = leftInsets
mlpToolbar.rightMargin = rightInsets
binding.toolbarAddons.layoutParams = mlpToolbar
val mlpAddonsList = binding.listAddons.layoutParams as ViewGroup.MarginLayoutParams
mlpAddonsList.leftMargin = leftInsets
mlpAddonsList.rightMargin = rightInsets
binding.listAddons.layoutParams = mlpAddonsList
binding.toolbarAddons.updateMargins(left = leftInsets, right = rightInsets)
binding.listAddons.updateMargins(left = leftInsets, right = rightInsets)
binding.listAddons.updatePadding(
bottom = barInsets.bottom +
resources.getDimensionPixelSize(R.dimen.spacing_bottom_list_fab)
)
val fabSpacing = resources.getDimensionPixelSize(R.dimen.spacing_fab)
val mlpFab =
binding.buttonInstall.layoutParams as ViewGroup.MarginLayoutParams
mlpFab.leftMargin = leftInsets + fabSpacing
mlpFab.rightMargin = rightInsets + fabSpacing
mlpFab.bottomMargin = barInsets.bottom + fabSpacing
binding.buttonInstall.layoutParams = mlpFab
binding.buttonInstall.updateMargins(
left = leftInsets + fabSpacing,
right = rightInsets + fabSpacing,
bottom = barInsets.bottom + fabSpacing
)
windowInsets
}

View File

@@ -21,6 +21,7 @@ import org.yuzu.yuzu_emu.databinding.FragmentAppletLauncherBinding
import org.yuzu.yuzu_emu.model.Applet
import org.yuzu.yuzu_emu.model.AppletInfo
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
class AppletLauncherFragment : Fragment() {
private var _binding: FragmentAppletLauncherBinding? = null
@@ -95,16 +96,8 @@ class AppletLauncherFragment : Fragment() {
val leftInsets = barInsets.left + cutoutInsets.left
val rightInsets = barInsets.right + cutoutInsets.right
val mlpAppBar = binding.toolbarApplets.layoutParams as ViewGroup.MarginLayoutParams
mlpAppBar.leftMargin = leftInsets
mlpAppBar.rightMargin = rightInsets
binding.toolbarApplets.layoutParams = mlpAppBar
val mlpListApplets =
binding.listApplets.layoutParams as ViewGroup.MarginLayoutParams
mlpListApplets.leftMargin = leftInsets
mlpListApplets.rightMargin = rightInsets
binding.listApplets.layoutParams = mlpListApplets
binding.toolbarApplets.updateMargins(left = leftInsets, right = rightInsets)
binding.listApplets.updateMargins(left = leftInsets, right = rightInsets)
binding.listApplets.updatePadding(bottom = barInsets.bottom)

View File

@@ -34,6 +34,7 @@ import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.NativeConfig
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
import java.io.File
import java.io.IOException
@@ -141,23 +142,15 @@ class DriverManagerFragment : Fragment() {
val leftInsets = barInsets.left + cutoutInsets.left
val rightInsets = barInsets.right + cutoutInsets.right
val mlpAppBar = binding.toolbarDrivers.layoutParams as ViewGroup.MarginLayoutParams
mlpAppBar.leftMargin = leftInsets
mlpAppBar.rightMargin = rightInsets
binding.toolbarDrivers.layoutParams = mlpAppBar
val mlplistDrivers = binding.listDrivers.layoutParams as ViewGroup.MarginLayoutParams
mlplistDrivers.leftMargin = leftInsets
mlplistDrivers.rightMargin = rightInsets
binding.listDrivers.layoutParams = mlplistDrivers
binding.toolbarDrivers.updateMargins(left = leftInsets, right = rightInsets)
binding.listDrivers.updateMargins(left = leftInsets, right = rightInsets)
val fabSpacing = resources.getDimensionPixelSize(R.dimen.spacing_fab)
val mlpFab =
binding.buttonInstall.layoutParams as ViewGroup.MarginLayoutParams
mlpFab.leftMargin = leftInsets + fabSpacing
mlpFab.rightMargin = rightInsets + fabSpacing
mlpFab.bottomMargin = barInsets.bottom + fabSpacing
binding.buttonInstall.layoutParams = mlpFab
binding.buttonInstall.updateMargins(
left = leftInsets + fabSpacing,
right = rightInsets + fabSpacing,
bottom = barInsets.bottom + fabSpacing
)
binding.listDrivers.updatePadding(
bottom = barInsets.bottom +

View File

@@ -19,6 +19,7 @@ import com.google.android.material.transition.MaterialSharedAxis
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.FragmentEarlyAccessBinding
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
class EarlyAccessFragment : Fragment() {
private var _binding: FragmentEarlyAccessBinding? = null
@@ -73,10 +74,7 @@ class EarlyAccessFragment : Fragment() {
val leftInsets = barInsets.left + cutoutInsets.left
val rightInsets = barInsets.right + cutoutInsets.right
val mlpAppBar = binding.appbarEa.layoutParams as ViewGroup.MarginLayoutParams
mlpAppBar.leftMargin = leftInsets
mlpAppBar.rightMargin = rightInsets
binding.appbarEa.layoutParams = mlpAppBar
binding.appbarEa.updateMargins(left = leftInsets, right = rightInsets)
binding.scrollEa.updatePadding(
left = leftInsets,

View File

@@ -13,6 +13,7 @@ import android.net.Uri
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.os.PowerManager
import android.os.SystemClock
import android.view.*
import android.widget.TextView
@@ -23,6 +24,7 @@ import androidx.core.content.res.ResourcesCompat
import androidx.core.graphics.Insets
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.drawerlayout.widget.DrawerLayout
import androidx.drawerlayout.widget.DrawerLayout.DrawerListener
import androidx.fragment.app.Fragment
@@ -38,7 +40,6 @@ import androidx.window.layout.WindowLayoutInfo
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.slider.Slider
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.HomeNavigationDirections
@@ -64,6 +65,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
private lateinit var emulationState: EmulationState
private var emulationActivity: EmulationActivity? = null
private var perfStatsUpdater: (() -> Unit)? = null
private var thermalStatsUpdater: (() -> Unit)? = null
private var _binding: FragmentEmulationBinding? = null
private val binding get() = _binding!!
@@ -77,6 +79,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
private var isInFoldableLayout = false
private lateinit var powerManager: PowerManager
override fun onAttach(context: Context) {
super.onAttach(context)
if (context is EmulationActivity) {
@@ -102,6 +106,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
super.onCreate(savedInstanceState)
updateOrientation()
powerManager = requireContext().getSystemService(Context.POWER_SERVICE) as PowerManager
val intentUri: Uri? = requireActivity().intent.data
var intentGame: Game? = null
if (intentUri != null) {
@@ -394,8 +400,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
emulationState.updateSurface()
// Setup overlay
// Setup overlays
updateShowFpsOverlay()
updateThermalOverlay()
}
}
}
@@ -553,6 +560,38 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
}
private fun updateThermalOverlay() {
if (BooleanSetting.SHOW_THERMAL_OVERLAY.getBoolean()) {
thermalStatsUpdater = {
if (emulationViewModel.emulationStarted.value &&
!emulationViewModel.isEmulationStopping.value
) {
val thermalStatus = when (powerManager.currentThermalStatus) {
PowerManager.THERMAL_STATUS_LIGHT -> "😥"
PowerManager.THERMAL_STATUS_MODERATE -> "🥵"
PowerManager.THERMAL_STATUS_SEVERE -> "🔥"
PowerManager.THERMAL_STATUS_CRITICAL,
PowerManager.THERMAL_STATUS_EMERGENCY,
PowerManager.THERMAL_STATUS_SHUTDOWN -> "☢️"
else -> "🙂"
}
if (_binding != null) {
binding.showThermalsText.text = thermalStatus
}
thermalStatsUpdateHandler.postDelayed(thermalStatsUpdater!!, 1000)
}
}
thermalStatsUpdateHandler.post(thermalStatsUpdater!!)
binding.showThermalsText.visibility = View.VISIBLE
} else {
if (thermalStatsUpdater != null) {
thermalStatsUpdateHandler.removeCallbacks(thermalStatsUpdater!!)
}
binding.showThermalsText.visibility = View.GONE
}
}
@SuppressLint("SourceLockedOrientationActivity")
private fun updateOrientation() {
emulationActivity?.let {
@@ -641,6 +680,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
popup.menu.apply {
findItem(R.id.menu_toggle_fps).isChecked =
BooleanSetting.SHOW_PERFORMANCE_OVERLAY.getBoolean()
findItem(R.id.thermal_indicator).isChecked =
BooleanSetting.SHOW_THERMAL_OVERLAY.getBoolean()
findItem(R.id.menu_rel_stick_center).isChecked =
BooleanSetting.JOYSTICK_REL_CENTER.getBoolean()
findItem(R.id.menu_dpad_slide).isChecked = BooleanSetting.DPAD_SLIDE.getBoolean()
@@ -660,6 +701,13 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
true
}
R.id.thermal_indicator -> {
it.isChecked = !it.isChecked
BooleanSetting.SHOW_THERMAL_OVERLAY.setBoolean(it.isChecked)
updateThermalOverlay()
true
}
R.id.menu_edit_overlay -> {
binding.drawerLayout.close()
binding.surfaceInputOverlay.requestFocus()
@@ -850,7 +898,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
right = cutInsets.right
}
v.setPadding(left, cutInsets.top, right, 0)
v.updatePadding(left = left, top = cutInsets.top, right = right)
windowInsets
}
}
@@ -1003,5 +1051,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
companion object {
private val perfStatsUpdateHandler = Handler(Looper.myLooper()!!)
private val thermalStatsUpdateHandler = Handler(Looper.myLooper()!!)
}
}

View File

@@ -26,6 +26,7 @@ import org.yuzu.yuzu_emu.databinding.FragmentFoldersBinding
import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.ui.main.MainActivity
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
class GameFoldersFragment : Fragment() {
private var _binding: FragmentFoldersBinding? = null
@@ -100,23 +101,16 @@ class GameFoldersFragment : Fragment() {
val leftInsets = barInsets.left + cutoutInsets.left
val rightInsets = barInsets.right + cutoutInsets.right
val mlpToolbar = binding.toolbarFolders.layoutParams as ViewGroup.MarginLayoutParams
mlpToolbar.leftMargin = leftInsets
mlpToolbar.rightMargin = rightInsets
binding.toolbarFolders.layoutParams = mlpToolbar
binding.toolbarFolders.updateMargins(left = leftInsets, right = rightInsets)
val fabSpacing = resources.getDimensionPixelSize(R.dimen.spacing_fab)
val mlpFab =
binding.buttonAdd.layoutParams as ViewGroup.MarginLayoutParams
mlpFab.leftMargin = leftInsets + fabSpacing
mlpFab.rightMargin = rightInsets + fabSpacing
mlpFab.bottomMargin = barInsets.bottom + fabSpacing
binding.buttonAdd.layoutParams = mlpFab
binding.buttonAdd.updateMargins(
left = leftInsets + fabSpacing,
right = rightInsets + fabSpacing,
bottom = barInsets.bottom + fabSpacing
)
val mlpListFolders = binding.listFolders.layoutParams as ViewGroup.MarginLayoutParams
mlpListFolders.leftMargin = leftInsets
mlpListFolders.rightMargin = rightInsets
binding.listFolders.layoutParams = mlpListFolders
binding.listFolders.updateMargins(left = leftInsets, right = rightInsets)
binding.listFolders.updatePadding(
bottom = barInsets.bottom +

View File

@@ -27,6 +27,7 @@ import org.yuzu.yuzu_emu.databinding.FragmentGameInfoBinding
import org.yuzu.yuzu_emu.model.GameVerificationResult
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.GameMetadata
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
class GameInfoFragment : Fragment() {
private var _binding: FragmentGameInfoBinding? = null
@@ -122,11 +123,13 @@ class GameInfoFragment : Fragment() {
titleId = R.string.verify_success,
descriptionId = R.string.operation_completed_successfully
)
GameVerificationResult.Failed ->
MessageDialogFragment.newInstance(
titleId = R.string.verify_failure,
descriptionId = R.string.verify_failure_description
)
GameVerificationResult.NotImplemented ->
MessageDialogFragment.newInstance(
titleId = R.string.verify_no_result,
@@ -165,15 +168,8 @@ class GameInfoFragment : Fragment() {
val leftInsets = barInsets.left + cutoutInsets.left
val rightInsets = barInsets.right + cutoutInsets.right
val mlpToolbar = binding.toolbarInfo.layoutParams as ViewGroup.MarginLayoutParams
mlpToolbar.leftMargin = leftInsets
mlpToolbar.rightMargin = rightInsets
binding.toolbarInfo.layoutParams = mlpToolbar
val mlpScrollAbout = binding.scrollInfo.layoutParams as ViewGroup.MarginLayoutParams
mlpScrollAbout.leftMargin = leftInsets
mlpScrollAbout.rightMargin = rightInsets
binding.scrollInfo.layoutParams = mlpScrollAbout
binding.toolbarInfo.updateMargins(left = leftInsets, right = rightInsets)
binding.scrollInfo.updateMargins(left = leftInsets, right = rightInsets)
binding.contentInfo.updatePadding(bottom = barInsets.bottom)

View File

@@ -46,6 +46,7 @@ import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.GameIconUtils
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.MemoryUtil
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
import java.io.BufferedOutputStream
import java.io.File
@@ -320,46 +321,25 @@ class GamePropertiesFragment : Fragment() {
val smallLayout = resources.getBoolean(R.bool.small_layout)
if (smallLayout) {
val mlpListAll =
binding.listAll.layoutParams as ViewGroup.MarginLayoutParams
mlpListAll.leftMargin = leftInsets
mlpListAll.rightMargin = rightInsets
binding.listAll.layoutParams = mlpListAll
binding.listAll.updateMargins(left = leftInsets, right = rightInsets)
} else {
if (ViewCompat.getLayoutDirection(binding.root) ==
ViewCompat.LAYOUT_DIRECTION_LTR
) {
val mlpListAll =
binding.listAll.layoutParams as ViewGroup.MarginLayoutParams
mlpListAll.rightMargin = rightInsets
binding.listAll.layoutParams = mlpListAll
val mlpIconLayout =
binding.iconLayout!!.layoutParams as ViewGroup.MarginLayoutParams
mlpIconLayout.topMargin = barInsets.top
mlpIconLayout.leftMargin = leftInsets
binding.iconLayout!!.layoutParams = mlpIconLayout
binding.listAll.updateMargins(right = rightInsets)
binding.iconLayout!!.updateMargins(top = barInsets.top, left = leftInsets)
} else {
val mlpListAll =
binding.listAll.layoutParams as ViewGroup.MarginLayoutParams
mlpListAll.leftMargin = leftInsets
binding.listAll.layoutParams = mlpListAll
val mlpIconLayout =
binding.iconLayout!!.layoutParams as ViewGroup.MarginLayoutParams
mlpIconLayout.topMargin = barInsets.top
mlpIconLayout.rightMargin = rightInsets
binding.iconLayout!!.layoutParams = mlpIconLayout
binding.listAll.updateMargins(left = leftInsets)
binding.iconLayout!!.updateMargins(top = barInsets.top, right = rightInsets)
}
}
val fabSpacing = resources.getDimensionPixelSize(R.dimen.spacing_fab)
val mlpFab =
binding.buttonStart.layoutParams as ViewGroup.MarginLayoutParams
mlpFab.leftMargin = leftInsets + fabSpacing
mlpFab.rightMargin = rightInsets + fabSpacing
mlpFab.bottomMargin = barInsets.bottom + fabSpacing
binding.buttonStart.layoutParams = mlpFab
binding.buttonStart.updateMargins(
left = leftInsets + fabSpacing,
right = rightInsets + fabSpacing,
bottom = barInsets.bottom + fabSpacing
)
binding.layoutAll.updatePadding(
top = barInsets.top,

View File

@@ -12,7 +12,6 @@ import android.provider.DocumentsContract
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.MarginLayoutParams
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
@@ -44,6 +43,7 @@ import org.yuzu.yuzu_emu.ui.main.MainActivity
import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.Log
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
class HomeSettingsFragment : Fragment() {
private var _binding: FragmentHomeSettingsBinding? = null
@@ -408,10 +408,7 @@ class HomeSettingsFragment : Fragment() {
bottom = barInsets.bottom
)
val mlpScrollSettings = binding.scrollViewSettings.layoutParams as MarginLayoutParams
mlpScrollSettings.leftMargin = leftInsets
mlpScrollSettings.rightMargin = rightInsets
binding.scrollViewSettings.layoutParams = mlpScrollSettings
binding.scrollViewSettings.updateMargins(left = leftInsets, right = rightInsets)
binding.linearLayoutSettings.updatePadding(bottom = spacingNavigation)

View File

@@ -34,6 +34,7 @@ import org.yuzu.yuzu_emu.model.TaskState
import org.yuzu.yuzu_emu.ui.main.MainActivity
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
import java.io.BufferedOutputStream
import java.io.File
import java.math.BigInteger
@@ -172,16 +173,8 @@ class InstallableFragment : Fragment() {
val leftInsets = barInsets.left + cutoutInsets.left
val rightInsets = barInsets.right + cutoutInsets.right
val mlpAppBar = binding.toolbarInstallables.layoutParams as ViewGroup.MarginLayoutParams
mlpAppBar.leftMargin = leftInsets
mlpAppBar.rightMargin = rightInsets
binding.toolbarInstallables.layoutParams = mlpAppBar
val mlpScrollAbout =
binding.listInstallables.layoutParams as ViewGroup.MarginLayoutParams
mlpScrollAbout.leftMargin = leftInsets
mlpScrollAbout.rightMargin = rightInsets
binding.listInstallables.layoutParams = mlpScrollAbout
binding.toolbarInstallables.updateMargins(left = leftInsets, right = rightInsets)
binding.listInstallables.updateMargins(left = leftInsets, right = rightInsets)
binding.listInstallables.updatePadding(bottom = barInsets.bottom)

View File

@@ -7,7 +7,6 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.MarginLayoutParams
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
@@ -22,6 +21,7 @@ import org.yuzu.yuzu_emu.adapters.LicenseAdapter
import org.yuzu.yuzu_emu.databinding.FragmentLicensesBinding
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.model.License
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
class LicensesFragment : Fragment() {
private var _binding: FragmentLicensesBinding? = null
@@ -122,15 +122,8 @@ class LicensesFragment : Fragment() {
val leftInsets = barInsets.left + cutoutInsets.left
val rightInsets = barInsets.right + cutoutInsets.right
val mlpAppBar = binding.appbarLicenses.layoutParams as MarginLayoutParams
mlpAppBar.leftMargin = leftInsets
mlpAppBar.rightMargin = rightInsets
binding.appbarLicenses.layoutParams = mlpAppBar
val mlpScrollAbout = binding.listLicenses.layoutParams as MarginLayoutParams
mlpScrollAbout.leftMargin = leftInsets
mlpScrollAbout.rightMargin = rightInsets
binding.listLicenses.layoutParams = mlpScrollAbout
binding.appbarLicenses.updateMargins(left = leftInsets, right = rightInsets)
binding.listLicenses.updateMargins(left = leftInsets, right = rightInsets)
binding.listLicenses.updatePadding(bottom = barInsets.bottom)

View File

@@ -29,6 +29,7 @@ import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
import org.yuzu.yuzu_emu.model.SettingsViewModel
import org.yuzu.yuzu_emu.utils.NativeConfig
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
class SettingsSearchFragment : Fragment() {
private var _binding: FragmentSettingsSearchBinding? = null
@@ -174,15 +175,14 @@ class SettingsSearchFragment : Fragment() {
bottom = barInsets.bottom
)
val mlpSettingsList = binding.settingsList.layoutParams as ViewGroup.MarginLayoutParams
mlpSettingsList.leftMargin = leftInsets + sideMargin
mlpSettingsList.rightMargin = rightInsets + sideMargin
binding.settingsList.layoutParams = mlpSettingsList
val mlpDivider = binding.divider.layoutParams as ViewGroup.MarginLayoutParams
mlpDivider.leftMargin = leftInsets + sideMargin
mlpDivider.rightMargin = rightInsets + sideMargin
binding.divider.layoutParams = mlpDivider
binding.settingsList.updateMargins(
left = leftInsets + sideMargin,
right = rightInsets + sideMargin
)
binding.divider.updateMargins(
left = leftInsets + sideMargin,
right = rightInsets + sideMargin
)
windowInsets
}

View File

@@ -8,7 +8,6 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.MarginLayoutParams
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
@@ -27,6 +26,7 @@ import org.yuzu.yuzu_emu.databinding.FragmentGamesBinding
import org.yuzu.yuzu_emu.layout.AutofitGridLayoutManager
import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
class GamesFragment : Fragment() {
private var _binding: FragmentGamesBinding? = null
@@ -169,15 +169,16 @@ class GamesFragment : Fragment() {
val leftInsets = barInsets.left + cutoutInsets.left
val rightInsets = barInsets.right + cutoutInsets.right
val mlpSwipe = binding.swipeRefresh.layoutParams as MarginLayoutParams
val left: Int
val right: Int
if (ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_LTR) {
mlpSwipe.leftMargin = leftInsets + spacingNavigationRail
mlpSwipe.rightMargin = rightInsets
left = leftInsets + spacingNavigationRail
right = rightInsets
} else {
mlpSwipe.leftMargin = leftInsets
mlpSwipe.rightMargin = rightInsets + spacingNavigationRail
left = leftInsets
right = rightInsets + spacingNavigationRail
}
binding.swipeRefresh.layoutParams = mlpSwipe
binding.swipeRefresh.updateMargins(left = left, right = right)
binding.noticeText.updatePadding(bottom = spacingNavigation)

View File

@@ -4,6 +4,7 @@
package org.yuzu.yuzu_emu.utils
import android.view.View
import android.view.ViewGroup
object ViewUtils {
fun showView(view: View, length: Long = 300) {
@@ -32,4 +33,28 @@ object ViewUtils {
view.visibility = View.INVISIBLE
}.start()
}
fun View.updateMargins(
left: Int = -1,
top: Int = -1,
right: Int = -1,
bottom: Int = -1
) {
val layoutParams = this.layoutParams as ViewGroup.MarginLayoutParams
layoutParams.apply {
if (left != -1) {
leftMargin = left
}
if (top != -1) {
topMargin = top
}
if (right != -1) {
rightMargin = right
}
if (bottom != -1) {
bottomMargin = bottom
}
}
this.layoutParams = layoutParams
}
}

View File

@@ -60,6 +60,8 @@ struct Values {
Settings::Category::Overlay};
Settings::Setting<bool> show_performance_overlay{linkage, true, "show_performance_overlay",
Settings::Category::Overlay};
Settings::Setting<bool> show_thermal_overlay{linkage, false, "show_thermal_overlay",
Settings::Category::Overlay};
Settings::Setting<bool> show_input_overlay{linkage, true, "show_input_overlay",
Settings::Category::Overlay};
Settings::Setting<bool> touchscreen{linkage, true, "touchscreen", Settings::Category::Overlay};

View File

@@ -140,6 +140,7 @@
android:id="@+id/overlay_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginHorizontal="20dp"
android:fitsSystemWindows="true">
<com.google.android.material.textview.MaterialTextView
@@ -150,7 +151,19 @@
android:layout_gravity="left"
android:clickable="false"
android:focusable="false"
android:paddingHorizontal="20dp"
android:textColor="@android:color/white"
android:shadowColor="@android:color/black"
android:shadowRadius="3"
tools:ignore="RtlHardcoded" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/show_thermals_text"
style="@style/TextAppearance.Material3.BodySmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:clickable="false"
android:focusable="false"
android:textColor="@android:color/white"
android:shadowColor="@android:color/black"
android:shadowRadius="3"

View File

@@ -6,6 +6,11 @@
android:title="@string/emulation_fps_counter"
android:checkable="true" />
<item
android:id="@+id/thermal_indicator"
android:title="@string/emulation_thermal_indicator"
android:checkable="true" />
<item
android:id="@+id/menu_edit_overlay"
android:title="@string/emulation_touch_overlay_edit" />

View File

@@ -380,6 +380,7 @@
<string name="emulation_exit">Exit emulation</string>
<string name="emulation_done">Done</string>
<string name="emulation_fps_counter">FPS counter</string>
<string name="emulation_thermal_indicator">Thermal indicator</string>
<string name="emulation_toggle_controls">Toggle controls</string>
<string name="emulation_rel_stick_center">Relative stick center</string>
<string name="emulation_dpad_slide">D-pad slide</string>

View File

@@ -107,6 +107,8 @@ add_library(common STATIC
quaternion.h
range_map.h
range_mutex.h
range_sets.h
range_sets.inc
reader_writer_queue.h
ring_buffer.h
${CMAKE_CURRENT_BINARY_DIR}/scm_rev.cpp
@@ -121,6 +123,7 @@ add_library(common STATIC
settings_input.cpp
settings_input.h
settings_setting.h
slot_vector.h
socket_types.h
spin_lock.cpp
spin_lock.h

73
src/common/range_sets.h Normal file
View File

@@ -0,0 +1,73 @@
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <memory>
#include "common/common_types.h"
namespace Common {
template <typename AddressType>
class RangeSet {
public:
RangeSet();
~RangeSet();
RangeSet(RangeSet const&) = delete;
RangeSet& operator=(RangeSet const&) = delete;
RangeSet(RangeSet&& other);
RangeSet& operator=(RangeSet&& other);
void Add(AddressType base_address, size_t size);
void Subtract(AddressType base_address, size_t size);
void Clear();
bool Empty() const;
template <typename Func>
void ForEach(Func&& func) const;
template <typename Func>
void ForEachInRange(AddressType device_addr, size_t size, Func&& func) const;
private:
struct RangeSetImpl;
std::unique_ptr<RangeSetImpl> m_impl;
};
template <typename AddressType>
class OverlapRangeSet {
public:
OverlapRangeSet();
~OverlapRangeSet();
OverlapRangeSet(OverlapRangeSet const&) = delete;
OverlapRangeSet& operator=(OverlapRangeSet const&) = delete;
OverlapRangeSet(OverlapRangeSet&& other);
OverlapRangeSet& operator=(OverlapRangeSet&& other);
void Add(AddressType base_address, size_t size);
void Subtract(AddressType base_address, size_t size);
template <typename Func>
void Subtract(AddressType base_address, size_t size, Func&& on_delete);
void DeleteAll(AddressType base_address, size_t size);
void Clear();
bool Empty() const;
template <typename Func>
void ForEach(Func&& func) const;
template <typename Func>
void ForEachInRange(AddressType device_addr, size_t size, Func&& func) const;
private:
struct OverlapRangeSetImpl;
std::unique_ptr<OverlapRangeSetImpl> m_impl;
};
} // namespace Common

304
src/common/range_sets.inc Normal file
View File

@@ -0,0 +1,304 @@
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <limits>
#include <utility>
#include <boost/icl/interval.hpp>
#include <boost/icl/interval_base_set.hpp>
#include <boost/icl/interval_map.hpp>
#include <boost/icl/interval_set.hpp>
#include <boost/icl/split_interval_map.hpp>
#include <boost/pool/pool.hpp>
#include <boost/pool/pool_alloc.hpp>
#include <boost/pool/poolfwd.hpp>
#include "common/range_sets.h"
namespace Common {
namespace {
template <class T>
using RangeSetsAllocator =
boost::fast_pool_allocator<T, boost::default_user_allocator_new_delete,
boost::details::pool::default_mutex, 1024, 2048>;
}
template <typename AddressType>
struct RangeSet<AddressType>::RangeSetImpl {
using IntervalSet = boost::icl::interval_set<
AddressType, std::less, ICL_INTERVAL_INSTANCE(ICL_INTERVAL_DEFAULT, AddressType, std::less),
RangeSetsAllocator>;
using IntervalType = typename IntervalSet::interval_type;
RangeSetImpl() = default;
~RangeSetImpl() = default;
void Add(AddressType base_address, size_t size) {
AddressType end_address = base_address + static_cast<AddressType>(size);
IntervalType interval{base_address, end_address};
m_ranges_set.add(interval);
}
void Subtract(AddressType base_address, size_t size) {
AddressType end_address = base_address + static_cast<AddressType>(size);
IntervalType interval{base_address, end_address};
m_ranges_set.subtract(interval);
}
template <typename Func>
void ForEach(Func&& func) const {
if (m_ranges_set.empty()) {
return;
}
auto it = m_ranges_set.begin();
auto end_it = m_ranges_set.end();
for (; it != end_it; it++) {
const AddressType inter_addr_end = it->upper();
const AddressType inter_addr = it->lower();
func(inter_addr, inter_addr_end);
}
}
template <typename Func>
void ForEachInRange(AddressType base_addr, size_t size, Func&& func) const {
if (m_ranges_set.empty()) {
return;
}
const AddressType start_address = base_addr;
const AddressType end_address = start_address + size;
const RangeSetImpl::IntervalType search_interval{start_address, end_address};
auto it = m_ranges_set.lower_bound(search_interval);
if (it == m_ranges_set.end()) {
return;
}
auto end_it = m_ranges_set.upper_bound(search_interval);
for (; it != end_it; it++) {
AddressType inter_addr_end = it->upper();
AddressType inter_addr = it->lower();
if (inter_addr_end > end_address) {
inter_addr_end = end_address;
}
if (inter_addr < start_address) {
inter_addr = start_address;
}
func(inter_addr, inter_addr_end);
}
}
IntervalSet m_ranges_set;
};
template <typename AddressType>
struct OverlapRangeSet<AddressType>::OverlapRangeSetImpl {
using IntervalSet = boost::icl::split_interval_map<
AddressType, s32, boost::icl::partial_enricher, std::less, boost::icl::inplace_plus,
boost::icl::inter_section,
ICL_INTERVAL_INSTANCE(ICL_INTERVAL_DEFAULT, AddressType, std::less), RangeSetsAllocator>;
using IntervalType = typename IntervalSet::interval_type;
OverlapRangeSetImpl() = default;
~OverlapRangeSetImpl() = default;
void Add(AddressType base_address, size_t size) {
AddressType end_address = base_address + static_cast<AddressType>(size);
IntervalType interval{base_address, end_address};
m_split_ranges_set += std::make_pair(interval, 1);
}
template <bool has_on_delete, typename Func>
void Subtract(AddressType base_address, size_t size, s32 amount,
[[maybe_unused]] Func&& on_delete) {
if (m_split_ranges_set.empty()) {
return;
}
AddressType end_address = base_address + static_cast<AddressType>(size);
IntervalType interval{base_address, end_address};
bool any_removals = false;
m_split_ranges_set += std::make_pair(interval, -amount);
do {
any_removals = false;
auto it = m_split_ranges_set.lower_bound(interval);
if (it == m_split_ranges_set.end()) {
return;
}
auto end_it = m_split_ranges_set.upper_bound(interval);
for (; it != end_it; it++) {
if (it->second <= 0) {
if constexpr (has_on_delete) {
if (it->second == 0) {
on_delete(it->first.lower(), it->first.upper());
}
}
any_removals = true;
m_split_ranges_set.erase(it);
break;
}
}
} while (any_removals);
}
template <typename Func>
void ForEach(Func&& func) const {
if (m_split_ranges_set.empty()) {
return;
}
auto it = m_split_ranges_set.begin();
auto end_it = m_split_ranges_set.end();
for (; it != end_it; it++) {
const AddressType inter_addr_end = it->first.upper();
const AddressType inter_addr = it->first.lower();
func(inter_addr, inter_addr_end, it->second);
}
}
template <typename Func>
void ForEachInRange(AddressType base_address, size_t size, Func&& func) const {
if (m_split_ranges_set.empty()) {
return;
}
const AddressType start_address = base_address;
const AddressType end_address = start_address + size;
const OverlapRangeSetImpl::IntervalType search_interval{start_address, end_address};
auto it = m_split_ranges_set.lower_bound(search_interval);
if (it == m_split_ranges_set.end()) {
return;
}
auto end_it = m_split_ranges_set.upper_bound(search_interval);
for (; it != end_it; it++) {
auto& inter = it->first;
AddressType inter_addr_end = inter.upper();
AddressType inter_addr = inter.lower();
if (inter_addr_end > end_address) {
inter_addr_end = end_address;
}
if (inter_addr < start_address) {
inter_addr = start_address;
}
func(inter_addr, inter_addr_end, it->second);
}
}
IntervalSet m_split_ranges_set;
};
template <typename AddressType>
RangeSet<AddressType>::RangeSet() {
m_impl = std::make_unique<RangeSet<AddressType>::RangeSetImpl>();
}
template <typename AddressType>
RangeSet<AddressType>::~RangeSet() = default;
template <typename AddressType>
RangeSet<AddressType>::RangeSet(RangeSet&& other) {
m_impl = std::make_unique<RangeSet<AddressType>::RangeSetImpl>();
m_impl->m_ranges_set = std::move(other.m_impl->m_ranges_set);
}
template <typename AddressType>
RangeSet<AddressType>& RangeSet<AddressType>::operator=(RangeSet&& other) {
m_impl->m_ranges_set = std::move(other.m_impl->m_ranges_set);
}
template <typename AddressType>
void RangeSet<AddressType>::Add(AddressType base_address, size_t size) {
m_impl->Add(base_address, size);
}
template <typename AddressType>
void RangeSet<AddressType>::Subtract(AddressType base_address, size_t size) {
m_impl->Subtract(base_address, size);
}
template <typename AddressType>
void RangeSet<AddressType>::Clear() {
m_impl->m_ranges_set.clear();
}
template <typename AddressType>
bool RangeSet<AddressType>::Empty() const {
return m_impl->m_ranges_set.empty();
}
template <typename AddressType>
template <typename Func>
void RangeSet<AddressType>::ForEach(Func&& func) const {
m_impl->ForEach(std::move(func));
}
template <typename AddressType>
template <typename Func>
void RangeSet<AddressType>::ForEachInRange(AddressType base_address, size_t size,
Func&& func) const {
m_impl->ForEachInRange(base_address, size, std::move(func));
}
template <typename AddressType>
OverlapRangeSet<AddressType>::OverlapRangeSet() {
m_impl = std::make_unique<OverlapRangeSet<AddressType>::OverlapRangeSetImpl>();
}
template <typename AddressType>
OverlapRangeSet<AddressType>::~OverlapRangeSet() = default;
template <typename AddressType>
OverlapRangeSet<AddressType>::OverlapRangeSet(OverlapRangeSet&& other) {
m_impl = std::make_unique<OverlapRangeSet<AddressType>::OverlapRangeSetImpl>();
m_impl->m_split_ranges_set = std::move(other.m_impl->m_split_ranges_set);
}
template <typename AddressType>
OverlapRangeSet<AddressType>& OverlapRangeSet<AddressType>::operator=(OverlapRangeSet&& other) {
m_impl->m_split_ranges_set = std::move(other.m_impl->m_split_ranges_set);
}
template <typename AddressType>
void OverlapRangeSet<AddressType>::Add(AddressType base_address, size_t size) {
m_impl->Add(base_address, size);
}
template <typename AddressType>
void OverlapRangeSet<AddressType>::Subtract(AddressType base_address, size_t size) {
m_impl->template Subtract<false>(base_address, size, 1, [](AddressType, AddressType) {});
}
template <typename AddressType>
template <typename Func>
void OverlapRangeSet<AddressType>::Subtract(AddressType base_address, size_t size,
Func&& on_delete) {
m_impl->template Subtract<true, Func>(base_address, size, 1, std::move(on_delete));
}
template <typename AddressType>
void OverlapRangeSet<AddressType>::DeleteAll(AddressType base_address, size_t size) {
m_impl->template Subtract<false>(base_address, size, std::numeric_limits<s32>::max(),
[](AddressType, AddressType) {});
}
template <typename AddressType>
void OverlapRangeSet<AddressType>::Clear() {
m_impl->m_split_ranges_set.clear();
}
template <typename AddressType>
bool OverlapRangeSet<AddressType>::Empty() const {
return m_impl->m_split_ranges_set.empty();
}
template <typename AddressType>
template <typename Func>
void OverlapRangeSet<AddressType>::ForEach(Func&& func) const {
m_impl->ForEach(func);
}
template <typename AddressType>
template <typename Func>
void OverlapRangeSet<AddressType>::ForEachInRange(AddressType base_address, size_t size,
Func&& func) const {
m_impl->ForEachInRange(base_address, size, std::move(func));
}
} // namespace Common

View File

@@ -14,7 +14,7 @@
#include "common/common_types.h"
#include "common/polyfill_ranges.h"
namespace VideoCommon {
namespace Common {
struct SlotId {
static constexpr u32 INVALID_INDEX = std::numeric_limits<u32>::max();
@@ -217,11 +217,11 @@ private:
std::vector<u32> free_list;
};
} // namespace VideoCommon
} // namespace Common
template <>
struct std::hash<VideoCommon::SlotId> {
size_t operator()(const VideoCommon::SlotId& id) const noexcept {
struct std::hash<Common::SlotId> {
size_t operator()(const Common::SlotId& id) const noexcept {
return std::hash<u32>{}(id.index);
}
};

View File

@@ -186,68 +186,68 @@ static_assert(std::is_trivially_destructible_v<PhysicalAddress>);
static_assert(std::is_trivially_destructible_v<VirtualAddress>);
static_assert(std::is_trivially_destructible_v<ProcessAddress>);
static_assert(Null<uint64_t> == 0);
static_assert(Null<uint64_t> == 0U);
static_assert(Null<PhysicalAddress> == Null<uint64_t>);
static_assert(Null<VirtualAddress> == Null<uint64_t>);
static_assert(Null<ProcessAddress> == Null<uint64_t>);
// Constructor/assignment validations.
static_assert([] {
const PhysicalAddress a(5);
const PhysicalAddress a(5U);
PhysicalAddress b(a);
return b;
}() == PhysicalAddress(5));
}() == PhysicalAddress(5U));
static_assert([] {
const PhysicalAddress a(5);
PhysicalAddress b(10);
const PhysicalAddress a(5U);
PhysicalAddress b(10U);
b = a;
return b;
}() == PhysicalAddress(5));
}() == PhysicalAddress(5U));
// Arithmetic validations.
static_assert(PhysicalAddress(10) + 5 == PhysicalAddress(15));
static_assert(PhysicalAddress(10) - 5 == PhysicalAddress(5));
static_assert(PhysicalAddress(10U) + 5U == PhysicalAddress(15U));
static_assert(PhysicalAddress(10U) - 5U == PhysicalAddress(5U));
static_assert([] {
PhysicalAddress v(10);
v += 5;
PhysicalAddress v(10U);
v += 5U;
return v;
}() == PhysicalAddress(15));
}() == PhysicalAddress(15U));
static_assert([] {
PhysicalAddress v(10);
v -= 5;
PhysicalAddress v(10U);
v -= 5U;
return v;
}() == PhysicalAddress(5));
static_assert(PhysicalAddress(10)++ == PhysicalAddress(10));
static_assert(++PhysicalAddress(10) == PhysicalAddress(11));
static_assert(PhysicalAddress(10)-- == PhysicalAddress(10));
static_assert(--PhysicalAddress(10) == PhysicalAddress(9));
}() == PhysicalAddress(5U));
static_assert(PhysicalAddress(10U)++ == PhysicalAddress(10U));
static_assert(++PhysicalAddress(10U) == PhysicalAddress(11U));
static_assert(PhysicalAddress(10U)-- == PhysicalAddress(10U));
static_assert(--PhysicalAddress(10U) == PhysicalAddress(9U));
// Logical validations.
static_assert((PhysicalAddress(0b11111111) >> 1) == 0b01111111);
static_assert((PhysicalAddress(0b10101010) >> 1) == 0b01010101);
static_assert((PhysicalAddress(0b11111111) << 1) == 0b111111110);
static_assert((PhysicalAddress(0b01010101) << 1) == 0b10101010);
static_assert((PhysicalAddress(0b11111111) & 0b01010101) == 0b01010101);
static_assert((PhysicalAddress(0b11111111) & 0b10101010) == 0b10101010);
static_assert((PhysicalAddress(0b01010101) & 0b10101010) == 0b00000000);
static_assert((PhysicalAddress(0b00000000) | 0b01010101) == 0b01010101);
static_assert((PhysicalAddress(0b11111111) | 0b01010101) == 0b11111111);
static_assert((PhysicalAddress(0b10101010) | 0b01010101) == 0b11111111);
static_assert((PhysicalAddress(0b11111111U) >> 1) == 0b01111111U);
static_assert((PhysicalAddress(0b10101010U) >> 1) == 0b01010101U);
static_assert((PhysicalAddress(0b11111111U) << 1) == 0b111111110U);
static_assert((PhysicalAddress(0b01010101U) << 1) == 0b10101010U);
static_assert((PhysicalAddress(0b11111111U) & 0b01010101U) == 0b01010101U);
static_assert((PhysicalAddress(0b11111111U) & 0b10101010U) == 0b10101010U);
static_assert((PhysicalAddress(0b01010101U) & 0b10101010U) == 0b00000000U);
static_assert((PhysicalAddress(0b00000000U) | 0b01010101U) == 0b01010101U);
static_assert((PhysicalAddress(0b11111111U) | 0b01010101U) == 0b11111111U);
static_assert((PhysicalAddress(0b10101010U) | 0b01010101U) == 0b11111111U);
// Comparisons.
static_assert(PhysicalAddress(0) == PhysicalAddress(0));
static_assert(PhysicalAddress(0) != PhysicalAddress(1));
static_assert(PhysicalAddress(0) < PhysicalAddress(1));
static_assert(PhysicalAddress(0) <= PhysicalAddress(1));
static_assert(PhysicalAddress(1) > PhysicalAddress(0));
static_assert(PhysicalAddress(1) >= PhysicalAddress(0));
static_assert(PhysicalAddress(0U) == PhysicalAddress(0U));
static_assert(PhysicalAddress(0U) != PhysicalAddress(1U));
static_assert(PhysicalAddress(0U) < PhysicalAddress(1U));
static_assert(PhysicalAddress(0U) <= PhysicalAddress(1U));
static_assert(PhysicalAddress(1U) > PhysicalAddress(0U));
static_assert(PhysicalAddress(1U) >= PhysicalAddress(0U));
static_assert(!(PhysicalAddress(0) == PhysicalAddress(1)));
static_assert(!(PhysicalAddress(0) != PhysicalAddress(0)));
static_assert(!(PhysicalAddress(1) < PhysicalAddress(0)));
static_assert(!(PhysicalAddress(1) <= PhysicalAddress(0)));
static_assert(!(PhysicalAddress(0) > PhysicalAddress(1)));
static_assert(!(PhysicalAddress(0) >= PhysicalAddress(1)));
static_assert(!(PhysicalAddress(0U) == PhysicalAddress(1U)));
static_assert(!(PhysicalAddress(0U) != PhysicalAddress(0U)));
static_assert(!(PhysicalAddress(1U) < PhysicalAddress(0U)));
static_assert(!(PhysicalAddress(1U) <= PhysicalAddress(0U)));
static_assert(!(PhysicalAddress(0U) > PhysicalAddress(1U)));
static_assert(!(PhysicalAddress(0U) >= PhysicalAddress(1U)));
} // namespace Common

View File

@@ -383,7 +383,7 @@ std::string GDBStubA32::RegRead(const Kernel::KThread* thread, size_t id) const
} else if (id == CPSR_REGISTER) {
return ValueToHex(context.pstate);
} else if (id >= D0_REGISTER && id < Q0_REGISTER) {
return ValueToHex(fprs[id - D0_REGISTER][0]);
return ValueToHex(fprs[(id - D0_REGISTER) / 2][(id - D0_REGISTER) % 2]);
} else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) {
return ValueToHex(fprs[id - Q0_REGISTER]);
} else if (id == FPSCR_REGISTER) {
@@ -406,7 +406,7 @@ void GDBStubA32::RegWrite(Kernel::KThread* thread, size_t id, std::string_view v
} else if (id == CPSR_REGISTER) {
context.pstate = HexToValue<u32>(value);
} else if (id >= D0_REGISTER && id < Q0_REGISTER) {
fprs[id - D0_REGISTER] = {HexToValue<u64>(value), 0};
fprs[(id - D0_REGISTER) / 2][(id - D0_REGISTER) % 2] = HexToValue<u64>(value);
} else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) {
fprs[id - Q0_REGISTER] = HexToValue<u128>(value);
} else if (id == FPSCR_REGISTER) {

View File

@@ -532,6 +532,7 @@ void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size
cache_bytes = 0;
}
};
size_t old_vpage = (base_vaddress >> Memory::YUZU_PAGEBITS) - 1;
for (; page != page_end; ++page) {
CounterAtomicType& count = cached_pages->at(page >> subentries_shift).Count(page);
auto [asid_2, vpage] = ExtractCPUBacking(page);
@@ -547,6 +548,12 @@ void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size
memory_device_inter = registered_processes[asid_2.id];
}
if (vpage != old_vpage + 1) [[unlikely]] {
release_pending();
}
old_vpage = vpage;
// Adds or subtracts 1, as count is a unsigned 8-bit value
count.fetch_add(static_cast<CounterType>(delta), std::memory_order_release);

View File

@@ -36,7 +36,7 @@ void LoopProcess(Core::System& system) {
server_manager->RegisterNamedService(
"hid:sys", std::make_shared<IHidSystemServer>(system, resource_manager, firmware_settings));
server_manager->RegisterNamedService("hidbus", std::make_shared<HidBus>(system));
server_manager->RegisterNamedService("hidbus", std::make_shared<Hidbus>(system));
server_manager->RegisterNamedService("irs", std::make_shared<IRS::IRS>(system));
server_manager->RegisterNamedService("irs:sys", std::make_shared<IRS::IRS_SYS>(system));

View File

@@ -3,6 +3,7 @@
#include <algorithm>
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/hid/hid_debug_server.h"
#include "core/hle/service/ipc_helpers.h"
#include "hid_core/hid_types.h"
@@ -11,7 +12,6 @@
#include "hid_core/resources/touch_screen/gesture.h"
#include "hid_core/resources/touch_screen/touch_screen.h"
#include "hid_core/resources/touch_screen/touch_types.h"
namespace Service::HID {
@@ -24,14 +24,14 @@ IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<Resource
{0, nullptr, "DeactivateDebugPad"},
{1, nullptr, "SetDebugPadAutoPilotState"},
{2, nullptr, "UnsetDebugPadAutoPilotState"},
{10, &IHidDebugServer::DeactivateTouchScreen, "DeactivateTouchScreen"},
{11, &IHidDebugServer::SetTouchScreenAutoPilotState, "SetTouchScreenAutoPilotState"},
{12, &IHidDebugServer::UnsetTouchScreenAutoPilotState, "UnsetTouchScreenAutoPilotState"},
{13, &IHidDebugServer::GetTouchScreenConfiguration, "GetTouchScreenConfiguration"},
{14, &IHidDebugServer::ProcessTouchScreenAutoTune, "ProcessTouchScreenAutoTune"},
{15, &IHidDebugServer::ForceStopTouchScreenManagement, "ForceStopTouchScreenManagement"},
{16, &IHidDebugServer::ForceRestartTouchScreenManagement, "ForceRestartTouchScreenManagement"},
{17, &IHidDebugServer::IsTouchScreenManaged, "IsTouchScreenManaged"},
{10, C<&IHidDebugServer::DeactivateTouchScreen>, "DeactivateTouchScreen"},
{11, C<&IHidDebugServer::SetTouchScreenAutoPilotState>, "SetTouchScreenAutoPilotState"},
{12, C<&IHidDebugServer::UnsetTouchScreenAutoPilotState>, "UnsetTouchScreenAutoPilotState"},
{13, C<&IHidDebugServer::GetTouchScreenConfiguration>, "GetTouchScreenConfiguration"},
{14, C<&IHidDebugServer::ProcessTouchScreenAutoTune>, "ProcessTouchScreenAutoTune"},
{15, C<&IHidDebugServer::ForceStopTouchScreenManagement>, "ForceStopTouchScreenManagement"},
{16, C<&IHidDebugServer::ForceRestartTouchScreenManagement>, "ForceRestartTouchScreenManagement"},
{17, C<&IHidDebugServer::IsTouchScreenManaged>, "IsTouchScreenManaged"},
{20, nullptr, "DeactivateMouse"},
{21, nullptr, "SetMouseAutoPilotState"},
{22, nullptr, "UnsetMouseAutoPilotState"},
@@ -47,7 +47,7 @@ IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<Resource
{60, nullptr, "ClearNpadSystemCommonPolicy"},
{61, nullptr, "DeactivateNpad"},
{62, nullptr, "ForceDisconnectNpad"},
{91, &IHidDebugServer::DeactivateGesture, "DeactivateGesture"},
{91, C<&IHidDebugServer::DeactivateGesture>, "DeactivateGesture"},
{110, nullptr, "DeactivateHomeButton"},
{111, nullptr, "SetHomeButtonAutoPilotState"},
{112, nullptr, "UnsetHomeButtonAutoPilotState"},
@@ -160,169 +160,122 @@ IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<Resource
}
IHidDebugServer::~IHidDebugServer() = default;
void IHidDebugServer::DeactivateTouchScreen(HLERequestContext& ctx) {
Result IHidDebugServer::DeactivateTouchScreen() {
LOG_INFO(Service_HID, "called");
Result result = ResultSuccess;
if (!firmware_settings->IsDeviceManaged()) {
result = GetResourceManager()->GetTouchScreen()->Deactivate();
R_RETURN(GetResourceManager()->GetTouchScreen()->Deactivate());
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
R_SUCCEED();
}
void IHidDebugServer::SetTouchScreenAutoPilotState(HLERequestContext& ctx) {
Result IHidDebugServer::SetTouchScreenAutoPilotState(
InArray<TouchState, BufferAttr_HipcMapAlias> auto_pilot_buffer) {
AutoPilotState auto_pilot{};
auto_pilot.count = ctx.GetReadBufferNumElements<TouchState>();
const auto buffer = ctx.ReadBuffer();
auto_pilot.count = std::min(auto_pilot.count, static_cast<u64>(auto_pilot.state.size()));
memcpy(auto_pilot.state.data(), buffer.data(), auto_pilot.count * sizeof(TouchState));
auto_pilot.count =
static_cast<u64>(std::min(auto_pilot_buffer.size(), auto_pilot.state.size()));
memcpy(auto_pilot.state.data(), auto_pilot_buffer.data(),
auto_pilot.count * sizeof(TouchState));
LOG_INFO(Service_HID, "called, auto_pilot_count={}", auto_pilot.count);
const Result result =
GetResourceManager()->GetTouchScreen()->SetTouchScreenAutoPilotState(auto_pilot);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
R_RETURN(GetResourceManager()->GetTouchScreen()->SetTouchScreenAutoPilotState(auto_pilot));
}
void IHidDebugServer::UnsetTouchScreenAutoPilotState(HLERequestContext& ctx) {
Result IHidDebugServer::UnsetTouchScreenAutoPilotState() {
LOG_INFO(Service_HID, "called");
const Result result = GetResourceManager()->GetTouchScreen()->UnsetTouchScreenAutoPilotState();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
R_RETURN(GetResourceManager()->GetTouchScreen()->UnsetTouchScreenAutoPilotState());
}
void IHidDebugServer::GetTouchScreenConfiguration(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
Result IHidDebugServer::GetTouchScreenConfiguration(
Out<Core::HID::TouchScreenConfigurationForNx> out_touchscreen_config,
ClientAppletResourceUserId aruid) {
LOG_INFO(Service_HID, "called, applet_resource_user_id={}", aruid.pid);
LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
R_TRY(GetResourceManager()->GetTouchScreen()->GetTouchScreenConfiguration(
*out_touchscreen_config, aruid.pid));
Core::HID::TouchScreenConfigurationForNx touchscreen_config{};
const Result result = GetResourceManager()->GetTouchScreen()->GetTouchScreenConfiguration(
touchscreen_config, applet_resource_user_id);
if (touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Heat2 &&
touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Finger) {
touchscreen_config.mode = Core::HID::TouchScreenModeForNx::UseSystemSetting;
if (out_touchscreen_config->mode != Core::HID::TouchScreenModeForNx::Heat2 &&
out_touchscreen_config->mode != Core::HID::TouchScreenModeForNx::Finger) {
out_touchscreen_config->mode = Core::HID::TouchScreenModeForNx::UseSystemSetting;
}
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(result);
rb.PushRaw(touchscreen_config);
R_SUCCEED();
}
void IHidDebugServer::ProcessTouchScreenAutoTune(HLERequestContext& ctx) {
Result IHidDebugServer::ProcessTouchScreenAutoTune() {
LOG_INFO(Service_HID, "called");
Result result = GetResourceManager()->GetTouchScreen()->ProcessTouchScreenAutoTune();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
R_RETURN(GetResourceManager()->GetTouchScreen()->ProcessTouchScreenAutoTune());
}
void IHidDebugServer::ForceStopTouchScreenManagement(HLERequestContext& ctx) {
Result IHidDebugServer::ForceStopTouchScreenManagement() {
LOG_INFO(Service_HID, "called");
if (!firmware_settings->IsDeviceManaged()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
return;
R_SUCCEED();
}
Result result = ResultSuccess;
bool is_touch_active{};
bool is_gesture_active{};
auto touch_screen = GetResourceManager()->GetTouchScreen();
auto gesture = GetResourceManager()->GetGesture();
if (firmware_settings->IsTouchI2cManaged()) {
result = touch_screen->IsActive(is_touch_active);
if (result.IsSuccess()) {
result = gesture->IsActive(is_gesture_active);
bool is_touch_active{};
bool is_gesture_active{};
R_TRY(touch_screen->IsActive(is_touch_active));
R_TRY(gesture->IsActive(is_gesture_active));
if (is_touch_active) {
R_TRY(touch_screen->Deactivate());
}
if (result.IsSuccess() && is_touch_active) {
result = touch_screen->Deactivate();
}
if (result.IsSuccess() && is_gesture_active) {
result = gesture->Deactivate();
if (is_gesture_active) {
R_TRY(gesture->Deactivate());
}
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
R_SUCCEED();
}
void IHidDebugServer::ForceRestartTouchScreenManagement(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
u32 basic_gesture_id;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
Result IHidDebugServer::ForceRestartTouchScreenManagement(u32 basic_gesture_id,
ClientAppletResourceUserId aruid) {
LOG_INFO(Service_HID, "called, basic_gesture_id={}, applet_resource_user_id={}",
parameters.basic_gesture_id, parameters.applet_resource_user_id);
basic_gesture_id, aruid.pid);
Result result = ResultSuccess;
auto touch_screen = GetResourceManager()->GetTouchScreen();
auto gesture = GetResourceManager()->GetGesture();
if (firmware_settings->IsDeviceManaged() && firmware_settings->IsTouchI2cManaged()) {
result = gesture->Activate();
if (result.IsSuccess()) {
result =
gesture->Activate(parameters.applet_resource_user_id, parameters.basic_gesture_id);
}
if (result.IsSuccess()) {
result = touch_screen->Activate();
}
if (result.IsSuccess()) {
result = touch_screen->Activate(parameters.applet_resource_user_id);
}
R_TRY(gesture->Activate());
R_TRY(gesture->Activate(aruid.pid, basic_gesture_id));
R_TRY(touch_screen->Activate());
R_TRY(touch_screen->Activate(aruid.pid));
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
R_SUCCEED();
}
void IHidDebugServer::IsTouchScreenManaged(HLERequestContext& ctx) {
Result IHidDebugServer::IsTouchScreenManaged(Out<bool> out_is_managed) {
LOG_INFO(Service_HID, "called");
bool is_touch_active{};
bool is_gesture_active{};
R_TRY(GetResourceManager()->GetTouchScreen()->IsActive(is_touch_active));
R_TRY(GetResourceManager()->GetGesture()->IsActive(is_gesture_active));
Result result = GetResourceManager()->GetTouchScreen()->IsActive(is_touch_active);
if (result.IsSuccess()) {
result = GetResourceManager()->GetGesture()->IsActive(is_gesture_active);
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(result);
rb.Push(is_touch_active | is_gesture_active);
*out_is_managed = is_touch_active || is_gesture_active;
R_SUCCEED();
}
void IHidDebugServer::DeactivateGesture(HLERequestContext& ctx) {
Result IHidDebugServer::DeactivateGesture() {
LOG_INFO(Service_HID, "called");
Result result = ResultSuccess;
if (!firmware_settings->IsDeviceManaged()) {
result = GetResourceManager()->GetGesture()->Deactivate();
R_RETURN(GetResourceManager()->GetGesture()->Deactivate());
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
R_SUCCEED();
}
std::shared_ptr<ResourceManager> IHidDebugServer::GetResourceManager() {

View File

@@ -3,7 +3,9 @@
#pragma once
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h"
#include "hid_core/resources/touch_screen/touch_types.h"
namespace Core {
class System;
@@ -20,15 +22,19 @@ public:
~IHidDebugServer() override;
private:
void DeactivateTouchScreen(HLERequestContext& ctx);
void SetTouchScreenAutoPilotState(HLERequestContext& ctx);
void UnsetTouchScreenAutoPilotState(HLERequestContext& ctx);
void GetTouchScreenConfiguration(HLERequestContext& ctx);
void ProcessTouchScreenAutoTune(HLERequestContext& ctx);
void ForceStopTouchScreenManagement(HLERequestContext& ctx);
void ForceRestartTouchScreenManagement(HLERequestContext& ctx);
void IsTouchScreenManaged(HLERequestContext& ctx);
void DeactivateGesture(HLERequestContext& ctx);
Result DeactivateTouchScreen();
Result SetTouchScreenAutoPilotState(
InArray<TouchState, BufferAttr_HipcMapAlias> auto_pilot_buffer);
Result UnsetTouchScreenAutoPilotState();
Result GetTouchScreenConfiguration(
Out<Core::HID::TouchScreenConfigurationForNx> out_touchscreen_config,
ClientAppletResourceUserId aruid);
Result ProcessTouchScreenAutoTune();
Result ForceStopTouchScreenManagement();
Result ForceRestartTouchScreenManagement(u32 basic_gesture_id,
ClientAppletResourceUserId aruid);
Result IsTouchScreenManaged(Out<bool> out_is_managed);
Result DeactivateGesture();
std::shared_ptr<ResourceManager> GetResourceManager();

View File

@@ -9,6 +9,7 @@
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/hid/hidbus.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/service.h"
@@ -22,25 +23,25 @@ namespace Service::HID {
// (15ms, 66Hz)
constexpr auto hidbus_update_ns = std::chrono::nanoseconds{15 * 1000 * 1000};
HidBus::HidBus(Core::System& system_)
Hidbus::Hidbus(Core::System& system_)
: ServiceFramework{system_, "hidbus"}, service_context{system_, service_name} {
// clang-format off
static const FunctionInfo functions[] = {
{1, &HidBus::GetBusHandle, "GetBusHandle"},
{2, &HidBus::IsExternalDeviceConnected, "IsExternalDeviceConnected"},
{3, &HidBus::Initialize, "Initialize"},
{4, &HidBus::Finalize, "Finalize"},
{5, &HidBus::EnableExternalDevice, "EnableExternalDevice"},
{6, &HidBus::GetExternalDeviceId, "GetExternalDeviceId"},
{7, &HidBus::SendCommandAsync, "SendCommandAsync"},
{8, &HidBus::GetSendCommandAsynceResult, "GetSendCommandAsynceResult"},
{9, &HidBus::SetEventForSendCommandAsycResult, "SetEventForSendCommandAsycResult"},
{10, &HidBus::GetSharedMemoryHandle, "GetSharedMemoryHandle"},
{11, &HidBus::EnableJoyPollingReceiveMode, "EnableJoyPollingReceiveMode"},
{12, &HidBus::DisableJoyPollingReceiveMode, "DisableJoyPollingReceiveMode"},
{1, C<&Hidbus::GetBusHandle>, "GetBusHandle"},
{2, C<&Hidbus::IsExternalDeviceConnected>, "IsExternalDeviceConnected"},
{3, C<&Hidbus::Initialize>, "Initialize"},
{4, C<&Hidbus::Finalize>, "Finalize"},
{5, C<&Hidbus::EnableExternalDevice>, "EnableExternalDevice"},
{6, C<&Hidbus::GetExternalDeviceId>, "GetExternalDeviceId"},
{7, C<&Hidbus::SendCommandAsync>, "SendCommandAsync"},
{8, C<&Hidbus::GetSendCommandAsynceResult>, "GetSendCommandAsynceResult"},
{9, C<&Hidbus::SetEventForSendCommandAsycResult>, "SetEventForSendCommandAsycResult"},
{10, C<&Hidbus::GetSharedMemoryHandle>, "GetSharedMemoryHandle"},
{11, C<&Hidbus::EnableJoyPollingReceiveMode>, "EnableJoyPollingReceiveMode"},
{12, C<&Hidbus::DisableJoyPollingReceiveMode>, "DisableJoyPollingReceiveMode"},
{13, nullptr, "GetPollingData"},
{14, &HidBus::SetStatusManagerType, "SetStatusManagerType"},
{14, C<&Hidbus::SetStatusManagerType>, "SetStatusManagerType"},
};
// clang-format on
@@ -60,11 +61,11 @@ HidBus::HidBus(Core::System& system_)
hidbus_update_event);
}
HidBus::~HidBus() {
Hidbus::~Hidbus() {
system.CoreTiming().UnscheduleEvent(hidbus_update_event);
}
void HidBus::UpdateHidbus(std::chrono::nanoseconds ns_late) {
void Hidbus::UpdateHidbus(std::chrono::nanoseconds ns_late) {
if (is_hidbus_enabled) {
for (std::size_t i = 0; i < devices.size(); ++i) {
if (!devices[i].is_device_initialized) {
@@ -84,7 +85,7 @@ void HidBus::UpdateHidbus(std::chrono::nanoseconds ns_late) {
}
}
std::optional<std::size_t> HidBus::GetDeviceIndexFromHandle(BusHandle handle) const {
std::optional<std::size_t> Hidbus::GetDeviceIndexFromHandle(BusHandle handle) const {
for (std::size_t i = 0; i < devices.size(); ++i) {
const auto& device_handle = devices[i].handle;
if (handle.abstracted_pad_id == device_handle.abstracted_pad_id &&
@@ -98,20 +99,11 @@ std::optional<std::size_t> HidBus::GetDeviceIndexFromHandle(BusHandle handle) co
return std::nullopt;
}
void HidBus::GetBusHandle(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Core::HID::NpadIdType npad_id;
INSERT_PADDING_WORDS_NOINIT(1);
BusType bus_type;
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
LOG_INFO(Service_HID, "called, npad_id={}, bus_type={}, applet_resource_user_id={}",
parameters.npad_id, parameters.bus_type, parameters.applet_resource_user_id);
Result Hidbus::GetBusHandle(Out<bool> out_is_valid, Out<BusHandle> out_bus_handle,
Core::HID::NpadIdType npad_id, BusType bus_type,
AppletResourceUserId aruid) {
LOG_INFO(Service_HID, "called, npad_id={}, bus_type={}, applet_resource_user_id={}", npad_id,
bus_type, aruid.pid);
bool is_handle_found = 0;
std::size_t handle_index = 0;
@@ -121,8 +113,8 @@ void HidBus::GetBusHandle(HLERequestContext& ctx) {
if (!handle.is_valid) {
continue;
}
if (static_cast<Core::HID::NpadIdType>(handle.player_number) == parameters.npad_id &&
handle.bus_type_id == static_cast<u8>(parameters.bus_type)) {
if (handle.player_number.As<Core::HID::NpadIdType>() == npad_id &&
handle.bus_type_id == static_cast<u8>(bus_type)) {
is_handle_found = true;
handle_index = i;
break;
@@ -135,388 +127,231 @@ void HidBus::GetBusHandle(HLERequestContext& ctx) {
if (devices[i].handle.is_valid) {
continue;
}
devices[i].handle = {
.abstracted_pad_id = static_cast<u8>(i),
.internal_index = static_cast<u8>(i),
.player_number = static_cast<u8>(parameters.npad_id),
.bus_type_id = static_cast<u8>(parameters.bus_type),
.is_valid = true,
};
devices[i].handle.raw = 0;
devices[i].handle.abstracted_pad_id.Assign(i);
devices[i].handle.internal_index.Assign(i);
devices[i].handle.player_number.Assign(static_cast<u8>(npad_id));
devices[i].handle.bus_type_id.Assign(static_cast<u8>(bus_type));
devices[i].handle.is_valid.Assign(true);
handle_index = i;
break;
}
}
struct OutData {
bool is_valid;
INSERT_PADDING_BYTES(7);
BusHandle handle;
};
static_assert(sizeof(OutData) == 0x10, "OutData has incorrect size.");
const OutData out_data{
.is_valid = true,
.handle = devices[handle_index].handle,
};
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess);
rb.PushRaw(out_data);
*out_is_valid = true;
*out_bus_handle = devices[handle_index].handle;
R_SUCCEED();
}
void HidBus::IsExternalDeviceConnected(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto bus_handle_{rp.PopRaw<BusHandle>()};
Result Hidbus::IsExternalDeviceConnected(Out<bool> out_is_connected, BusHandle bus_handle) {
LOG_INFO(Service_HID,
"Called, abstracted_pad_id={}, bus_type={}, internal_index={}, "
"player_number={}, is_valid={}",
bus_handle_.abstracted_pad_id, bus_handle_.bus_type_id, bus_handle_.internal_index,
bus_handle_.player_number, bus_handle_.is_valid);
bus_handle.abstracted_pad_id, bus_handle.bus_type_id, bus_handle.internal_index,
bus_handle.player_number, bus_handle.is_valid);
const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
const auto device_index = GetDeviceIndexFromHandle(bus_handle);
if (device_index) {
const auto& device = devices[device_index.value()].device;
const bool is_attached = device->IsDeviceActivated();
R_UNLESS(device_index.has_value(), ResultUnknown);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(is_attached);
return;
}
LOG_ERROR(Service_HID, "Invalid handle");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
*out_is_connected = devices[device_index.value()].device->IsDeviceActivated();
R_SUCCEED();
}
void HidBus::Initialize(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto bus_handle_{rp.PopRaw<BusHandle>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
Result Hidbus::Initialize(BusHandle bus_handle, AppletResourceUserId aruid) {
LOG_INFO(Service_HID,
"called, abstracted_pad_id={} bus_type={} internal_index={} "
"player_number={} is_valid={}, applet_resource_user_id={}",
bus_handle_.abstracted_pad_id, bus_handle_.bus_type_id, bus_handle_.internal_index,
bus_handle_.player_number, bus_handle_.is_valid, applet_resource_user_id);
bus_handle.abstracted_pad_id, bus_handle.bus_type_id, bus_handle.internal_index,
bus_handle.player_number, bus_handle.is_valid, aruid.pid);
is_hidbus_enabled = true;
const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
const auto device_index = GetDeviceIndexFromHandle(bus_handle);
if (device_index) {
const auto entry_index = devices[device_index.value()].handle.internal_index;
auto& cur_entry = hidbus_status.entries[entry_index];
R_UNLESS(device_index.has_value(), ResultUnknown);
if (bus_handle_.internal_index == 0 && Settings::values.enable_ring_controller) {
MakeDevice<RingController>(bus_handle_);
devices[device_index.value()].is_device_initialized = true;
devices[device_index.value()].device->ActivateDevice();
cur_entry.is_in_focus = true;
cur_entry.is_connected = true;
cur_entry.is_connected_result = ResultSuccess;
cur_entry.is_enabled = false;
cur_entry.is_polling_mode = false;
} else {
MakeDevice<HidbusStubbed>(bus_handle_);
devices[device_index.value()].is_device_initialized = true;
cur_entry.is_in_focus = true;
cur_entry.is_connected = false;
cur_entry.is_connected_result = ResultSuccess;
cur_entry.is_enabled = false;
cur_entry.is_polling_mode = false;
}
std::memcpy(system.Kernel().GetHidBusSharedMem().GetPointer(), &hidbus_status,
sizeof(hidbus_status));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
return;
}
LOG_ERROR(Service_HID, "Invalid handle");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
void HidBus::Finalize(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto bus_handle_{rp.PopRaw<BusHandle>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_INFO(Service_HID,
"called, abstracted_pad_id={}, bus_type={}, internal_index={}, "
"player_number={}, is_valid={}, applet_resource_user_id={}",
bus_handle_.abstracted_pad_id, bus_handle_.bus_type_id, bus_handle_.internal_index,
bus_handle_.player_number, bus_handle_.is_valid, applet_resource_user_id);
const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
if (device_index) {
const auto entry_index = devices[device_index.value()].handle.internal_index;
auto& cur_entry = hidbus_status.entries[entry_index];
auto& device = devices[device_index.value()].device;
devices[device_index.value()].is_device_initialized = false;
device->DeactivateDevice();
const auto entry_index = devices[device_index.value()].handle.internal_index;
auto& cur_entry = hidbus_status.entries[entry_index];
if (bus_handle.internal_index == 0 && Settings::values.enable_ring_controller) {
MakeDevice<RingController>(bus_handle);
devices[device_index.value()].is_device_initialized = true;
devices[device_index.value()].device->ActivateDevice();
cur_entry.is_in_focus = true;
cur_entry.is_connected = true;
cur_entry.is_connected_result = ResultSuccess;
cur_entry.is_enabled = false;
cur_entry.is_polling_mode = false;
} else {
MakeDevice<HidbusStubbed>(bus_handle);
devices[device_index.value()].is_device_initialized = true;
cur_entry.is_in_focus = true;
cur_entry.is_connected = false;
cur_entry.is_connected_result = ResultSuccess;
cur_entry.is_enabled = false;
cur_entry.is_polling_mode = false;
std::memcpy(system.Kernel().GetHidBusSharedMem().GetPointer(), &hidbus_status,
sizeof(hidbus_status));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
return;
}
LOG_ERROR(Service_HID, "Invalid handle");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
std::memcpy(system.Kernel().GetHidBusSharedMem().GetPointer(), &hidbus_status,
sizeof(hidbus_status));
R_SUCCEED();
}
void HidBus::EnableExternalDevice(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
bool enable;
INSERT_PADDING_BYTES_NOINIT(7);
BusHandle bus_handle;
u64 inval;
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x20, "Parameters has incorrect size.");
Result Hidbus::Finalize(BusHandle bus_handle, AppletResourceUserId aruid) {
LOG_INFO(Service_HID,
"called, abstracted_pad_id={}, bus_type={}, internal_index={}, "
"player_number={}, is_valid={}, applet_resource_user_id={}",
bus_handle.abstracted_pad_id, bus_handle.bus_type_id, bus_handle.internal_index,
bus_handle.player_number, bus_handle.is_valid, aruid.pid);
const auto parameters{rp.PopRaw<Parameters>()};
const auto device_index = GetDeviceIndexFromHandle(bus_handle);
R_UNLESS(device_index.has_value(), ResultUnknown);
const auto entry_index = devices[device_index.value()].handle.internal_index;
auto& cur_entry = hidbus_status.entries[entry_index];
auto& device = devices[device_index.value()].device;
devices[device_index.value()].is_device_initialized = false;
device->DeactivateDevice();
cur_entry.is_in_focus = true;
cur_entry.is_connected = false;
cur_entry.is_connected_result = ResultSuccess;
cur_entry.is_enabled = false;
cur_entry.is_polling_mode = false;
std::memcpy(system.Kernel().GetHidBusSharedMem().GetPointer(), &hidbus_status,
sizeof(hidbus_status));
R_SUCCEED();
}
Result Hidbus::EnableExternalDevice(bool is_enabled, BusHandle bus_handle, u64 inval,
AppletResourceUserId aruid) {
LOG_DEBUG(Service_HID,
"called, enable={}, abstracted_pad_id={}, bus_type={}, internal_index={}, "
"player_number={}, is_valid={}, inval={}, applet_resource_user_id{}",
parameters.enable, parameters.bus_handle.abstracted_pad_id,
parameters.bus_handle.bus_type_id, parameters.bus_handle.internal_index,
parameters.bus_handle.player_number, parameters.bus_handle.is_valid, parameters.inval,
parameters.applet_resource_user_id);
is_enabled, bus_handle.abstracted_pad_id, bus_handle.bus_type_id,
bus_handle.internal_index, bus_handle.player_number, bus_handle.is_valid, inval,
aruid.pid);
const auto device_index = GetDeviceIndexFromHandle(parameters.bus_handle);
const auto device_index = GetDeviceIndexFromHandle(bus_handle);
if (device_index) {
auto& device = devices[device_index.value()].device;
device->Enable(parameters.enable);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
return;
}
LOG_ERROR(Service_HID, "Invalid handle");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
R_UNLESS(device_index.has_value(), ResultUnknown);
devices[device_index.value()].device->Enable(is_enabled);
R_SUCCEED();
}
void HidBus::GetExternalDeviceId(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto bus_handle_{rp.PopRaw<BusHandle>()};
Result Hidbus::GetExternalDeviceId(Out<u32> out_device_id, BusHandle bus_handle) {
LOG_DEBUG(Service_HID,
"called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
"is_valid={}",
bus_handle_.abstracted_pad_id, bus_handle_.bus_type_id, bus_handle_.internal_index,
bus_handle_.player_number, bus_handle_.is_valid);
bus_handle.abstracted_pad_id, bus_handle.bus_type_id, bus_handle.internal_index,
bus_handle.player_number, bus_handle.is_valid);
const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
const auto device_index = GetDeviceIndexFromHandle(bus_handle);
if (device_index) {
const auto& device = devices[device_index.value()].device;
u32 device_id = device->GetDeviceId();
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push<u32>(device_id);
return;
}
R_UNLESS(device_index.has_value(), ResultUnknown);
LOG_ERROR(Service_HID, "Invalid handle");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
*out_device_id = devices[device_index.value()].device->GetDeviceId();
R_SUCCEED();
}
void HidBus::SendCommandAsync(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto data = ctx.ReadBuffer();
const auto bus_handle_{rp.PopRaw<BusHandle>()};
Result Hidbus::SendCommandAsync(BusHandle bus_handle,
InBuffer<BufferAttr_HipcAutoSelect> buffer_data) {
LOG_DEBUG(Service_HID,
"called, data_size={}, abstracted_pad_id={}, bus_type={}, internal_index={}, "
"player_number={}, is_valid={}",
data.size(), bus_handle_.abstracted_pad_id, bus_handle_.bus_type_id,
bus_handle_.internal_index, bus_handle_.player_number, bus_handle_.is_valid);
buffer_data.size(), bus_handle.abstracted_pad_id, bus_handle.bus_type_id,
bus_handle.internal_index, bus_handle.player_number, bus_handle.is_valid);
const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
const auto device_index = GetDeviceIndexFromHandle(bus_handle);
if (device_index) {
auto& device = devices[device_index.value()].device;
device->SetCommand(data);
R_UNLESS(device_index.has_value(), ResultUnknown);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
return;
}
LOG_ERROR(Service_HID, "Invalid handle");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
devices[device_index.value()].device->SetCommand(buffer_data);
R_SUCCEED();
};
void HidBus::GetSendCommandAsynceResult(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto bus_handle_{rp.PopRaw<BusHandle>()};
Result Hidbus::GetSendCommandAsynceResult(Out<u64> out_data_size, BusHandle bus_handle,
OutBuffer<BufferAttr_HipcAutoSelect> out_buffer_data) {
LOG_DEBUG(Service_HID,
"called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
"is_valid={}",
bus_handle_.abstracted_pad_id, bus_handle_.bus_type_id, bus_handle_.internal_index,
bus_handle_.player_number, bus_handle_.is_valid);
bus_handle.abstracted_pad_id, bus_handle.bus_type_id, bus_handle.internal_index,
bus_handle.player_number, bus_handle.is_valid);
const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
const auto device_index = GetDeviceIndexFromHandle(bus_handle);
if (device_index) {
const auto& device = devices[device_index.value()].device;
const std::vector<u8> data = device->GetReply();
const u64 data_size = ctx.WriteBuffer(data);
R_UNLESS(device_index.has_value(), ResultUnknown);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push<u64>(data_size);
return;
}
LOG_ERROR(Service_HID, "Invalid handle");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
*out_data_size = devices[device_index.value()].device->GetReply(out_buffer_data);
R_SUCCEED();
};
void HidBus::SetEventForSendCommandAsycResult(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto bus_handle_{rp.PopRaw<BusHandle>()};
Result Hidbus::SetEventForSendCommandAsycResult(OutCopyHandle<Kernel::KReadableEvent> out_event,
BusHandle bus_handle) {
LOG_INFO(Service_HID,
"called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
"is_valid={}",
bus_handle_.abstracted_pad_id, bus_handle_.bus_type_id, bus_handle_.internal_index,
bus_handle_.player_number, bus_handle_.is_valid);
bus_handle.abstracted_pad_id, bus_handle.bus_type_id, bus_handle.internal_index,
bus_handle.player_number, bus_handle.is_valid);
const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
const auto device_index = GetDeviceIndexFromHandle(bus_handle);
if (device_index) {
const auto& device = devices[device_index.value()].device;
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(device->GetSendCommandAsycEvent());
return;
}
R_UNLESS(device_index.has_value(), ResultUnknown);
LOG_ERROR(Service_HID, "Invalid handle");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
*out_event = &devices[device_index.value()].device->GetSendCommandAsycEvent();
R_SUCCEED();
};
void HidBus::GetSharedMemoryHandle(HLERequestContext& ctx) {
Result Hidbus::GetSharedMemoryHandle(OutCopyHandle<Kernel::KSharedMemory> out_shared_memory) {
LOG_DEBUG(Service_HID, "called");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(&system.Kernel().GetHidBusSharedMem());
*out_shared_memory = &system.Kernel().GetHidBusSharedMem();
R_SUCCEED();
}
void HidBus::EnableJoyPollingReceiveMode(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto t_mem_size{rp.Pop<u32>()};
const auto t_mem_handle{ctx.GetCopyHandle(0)};
const auto polling_mode_{rp.PopEnum<JoyPollingMode>()};
const auto bus_handle_{rp.PopRaw<BusHandle>()};
Result Hidbus::EnableJoyPollingReceiveMode(u32 t_mem_size, JoyPollingMode polling_mode,
BusHandle bus_handle,
InCopyHandle<Kernel::KTransferMemory> t_mem) {
ASSERT_MSG(t_mem_size == 0x1000, "t_mem_size is not 0x1000 bytes");
auto t_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_handle);
if (t_mem.IsNull()) {
LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
ASSERT_MSG(t_mem->GetSize() == 0x1000, "t_mem has incorrect size");
ASSERT_MSG(t_mem->GetSize() == t_mem_size, "t_mem has incorrect size");
LOG_INFO(Service_HID,
"called, t_mem_handle=0x{:08X}, polling_mode={}, abstracted_pad_id={}, bus_type={}, "
"called, polling_mode={}, abstracted_pad_id={}, bus_type={}, "
"internal_index={}, player_number={}, is_valid={}",
t_mem_handle, polling_mode_, bus_handle_.abstracted_pad_id, bus_handle_.bus_type_id,
bus_handle_.internal_index, bus_handle_.player_number, bus_handle_.is_valid);
polling_mode, bus_handle.abstracted_pad_id, bus_handle.bus_type_id,
bus_handle.internal_index, bus_handle.player_number, bus_handle.is_valid);
const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
const auto device_index = GetDeviceIndexFromHandle(bus_handle);
if (device_index) {
auto& device = devices[device_index.value()].device;
device->SetPollingMode(polling_mode_);
device->SetTransferMemoryAddress(t_mem->GetSourceAddress());
R_UNLESS(device_index.has_value(), ResultUnknown);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
return;
}
LOG_ERROR(Service_HID, "Invalid handle");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
auto& device = devices[device_index.value()].device;
device->SetPollingMode(polling_mode);
device->SetTransferMemoryAddress(t_mem->GetSourceAddress());
R_SUCCEED();
}
void HidBus::DisableJoyPollingReceiveMode(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto bus_handle_{rp.PopRaw<BusHandle>()};
Result Hidbus::DisableJoyPollingReceiveMode(BusHandle bus_handle) {
LOG_INFO(Service_HID,
"called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
"is_valid={}",
bus_handle_.abstracted_pad_id, bus_handle_.bus_type_id, bus_handle_.internal_index,
bus_handle_.player_number, bus_handle_.is_valid);
bus_handle.abstracted_pad_id, bus_handle.bus_type_id, bus_handle.internal_index,
bus_handle.player_number, bus_handle.is_valid);
const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
const auto device_index = GetDeviceIndexFromHandle(bus_handle);
if (device_index) {
auto& device = devices[device_index.value()].device;
device->DisablePollingMode();
R_UNLESS(device_index.has_value(), ResultUnknown);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
return;
}
LOG_ERROR(Service_HID, "Invalid handle");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
auto& device = devices[device_index.value()].device;
device->DisablePollingMode();
R_SUCCEED();
}
void HidBus::SetStatusManagerType(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto manager_type{rp.PopEnum<StatusManagerType>()};
Result Hidbus::SetStatusManagerType(StatusManagerType manager_type) {
LOG_WARNING(Service_HID, "(STUBBED) called, manager_type={}", manager_type);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
R_SUCCEED();
};
} // namespace Service::HID

View File

@@ -5,8 +5,10 @@
#include <functional>
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/service.h"
#include "hid_core/hid_types.h"
#include "hid_core/hidbus/hidbus_base.h"
namespace Core::Timing {
@@ -19,10 +21,10 @@ class System;
namespace Service::HID {
class HidBus final : public ServiceFramework<HidBus> {
class Hidbus final : public ServiceFramework<Hidbus> {
public:
explicit HidBus(Core::System& system_);
~HidBus() override;
explicit Hidbus(Core::System& system_);
~Hidbus() override;
private:
static const std::size_t max_number_of_handles = 0x13;
@@ -41,7 +43,7 @@ private:
};
// This is nn::hidbus::BusType
enum class BusType : u32 {
enum class BusType : u64 {
LeftJoyRail,
RightJoyRail,
InternalBus, // Lark microphone
@@ -51,11 +53,15 @@ private:
// This is nn::hidbus::BusHandle
struct BusHandle {
u32 abstracted_pad_id;
u8 internal_index;
u8 player_number;
u8 bus_type_id;
bool is_valid;
union {
u64 raw{};
BitField<0, 32, u64> abstracted_pad_id;
BitField<32, 8, u64> internal_index;
BitField<40, 8, u64> player_number;
BitField<48, 8, u64> bus_type_id;
BitField<56, 1, u64> is_valid;
};
};
static_assert(sizeof(BusHandle) == 0x8, "BusHandle is an invalid size");
@@ -94,19 +100,38 @@ private:
std::unique_ptr<HidbusBase> device{nullptr};
};
void GetBusHandle(HLERequestContext& ctx);
void IsExternalDeviceConnected(HLERequestContext& ctx);
void Initialize(HLERequestContext& ctx);
void Finalize(HLERequestContext& ctx);
void EnableExternalDevice(HLERequestContext& ctx);
void GetExternalDeviceId(HLERequestContext& ctx);
void SendCommandAsync(HLERequestContext& ctx);
void GetSendCommandAsynceResult(HLERequestContext& ctx);
void SetEventForSendCommandAsycResult(HLERequestContext& ctx);
void GetSharedMemoryHandle(HLERequestContext& ctx);
void EnableJoyPollingReceiveMode(HLERequestContext& ctx);
void DisableJoyPollingReceiveMode(HLERequestContext& ctx);
void SetStatusManagerType(HLERequestContext& ctx);
Result GetBusHandle(Out<bool> out_is_valid, Out<BusHandle> out_bus_handle,
Core::HID::NpadIdType npad_id, BusType bus_type,
AppletResourceUserId aruid);
Result IsExternalDeviceConnected(Out<bool> out_is_connected, BusHandle bus_handle);
Result Initialize(BusHandle bus_handle, AppletResourceUserId aruid);
Result Finalize(BusHandle bus_handle, AppletResourceUserId aruid);
Result EnableExternalDevice(bool is_enabled, BusHandle bus_handle, u64 inval,
AppletResourceUserId aruid);
Result GetExternalDeviceId(Out<u32> out_device_id, BusHandle bus_handle);
Result SendCommandAsync(BusHandle bus_handle, InBuffer<BufferAttr_HipcAutoSelect> buffer_data);
Result GetSendCommandAsynceResult(Out<u64> out_data_size, BusHandle bus_handle,
OutBuffer<BufferAttr_HipcAutoSelect> out_buffer_data);
Result SetEventForSendCommandAsycResult(OutCopyHandle<Kernel::KReadableEvent> out_event,
BusHandle bus_handle);
Result GetSharedMemoryHandle(OutCopyHandle<Kernel::KSharedMemory> out_shared_memory);
Result EnableJoyPollingReceiveMode(u32 t_mem_size, JoyPollingMode polling_mode,
BusHandle bus_handle,
InCopyHandle<Kernel::KTransferMemory> t_mem);
Result DisableJoyPollingReceiveMode(BusHandle bus_handle);
Result SetStatusManagerType(StatusManagerType manager_type);
void UpdateHidbus(std::chrono::nanoseconds ns_late);
std::optional<std::size_t> GetDeviceIndexFromHandle(BusHandle handle) const;

View File

@@ -9,6 +9,7 @@
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/hid/irs.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/memory.h"
@@ -28,24 +29,24 @@ namespace Service::IRS {
IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} {
// clang-format off
static const FunctionInfo functions[] = {
{302, &IRS::ActivateIrsensor, "ActivateIrsensor"},
{303, &IRS::DeactivateIrsensor, "DeactivateIrsensor"},
{304, &IRS::GetIrsensorSharedMemoryHandle, "GetIrsensorSharedMemoryHandle"},
{305, &IRS::StopImageProcessor, "StopImageProcessor"},
{306, &IRS::RunMomentProcessor, "RunMomentProcessor"},
{307, &IRS::RunClusteringProcessor, "RunClusteringProcessor"},
{308, &IRS::RunImageTransferProcessor, "RunImageTransferProcessor"},
{309, &IRS::GetImageTransferProcessorState, "GetImageTransferProcessorState"},
{310, &IRS::RunTeraPluginProcessor, "RunTeraPluginProcessor"},
{311, &IRS::GetNpadIrCameraHandle, "GetNpadIrCameraHandle"},
{312, &IRS::RunPointingProcessor, "RunPointingProcessor"},
{313, &IRS::SuspendImageProcessor, "SuspendImageProcessor"},
{314, &IRS::CheckFirmwareVersion, "CheckFirmwareVersion"},
{315, &IRS::SetFunctionLevel, "SetFunctionLevel"},
{316, &IRS::RunImageTransferExProcessor, "RunImageTransferExProcessor"},
{317, &IRS::RunIrLedProcessor, "RunIrLedProcessor"},
{318, &IRS::StopImageProcessorAsync, "StopImageProcessorAsync"},
{319, &IRS::ActivateIrsensorWithFunctionLevel, "ActivateIrsensorWithFunctionLevel"},
{302, C<&IRS::ActivateIrsensor>, "ActivateIrsensor"},
{303, C<&IRS::DeactivateIrsensor>, "DeactivateIrsensor"},
{304, C<&IRS::GetIrsensorSharedMemoryHandle>, "GetIrsensorSharedMemoryHandle"},
{305, C<&IRS::StopImageProcessor>, "StopImageProcessor"},
{306, C<&IRS::RunMomentProcessor>, "RunMomentProcessor"},
{307, C<&IRS::RunClusteringProcessor>, "RunClusteringProcessor"},
{308, C<&IRS::RunImageTransferProcessor>, "RunImageTransferProcessor"},
{309, C<&IRS::GetImageTransferProcessorState>, "GetImageTransferProcessorState"},
{310, C<&IRS::RunTeraPluginProcessor>, "RunTeraPluginProcessor"},
{311, C<&IRS::GetNpadIrCameraHandle>, "GetNpadIrCameraHandle"},
{312, C<&IRS::RunPointingProcessor>, "RunPointingProcessor"},
{313, C<&IRS::SuspendImageProcessor>, "SuspendImageProcessor"},
{314, C<&IRS::CheckFirmwareVersion>, "CheckFirmwareVersion"},
{315, C<&IRS::SetFunctionLevel>, "SetFunctionLevel"},
{316, C<&IRS::RunImageTransferExProcessor>, "RunImageTransferExProcessor"},
{317, C<&IRS::RunIrLedProcessor>, "RunIrLedProcessor"},
{318, C<&IRS::StopImageProcessorAsync>, "StopImageProcessorAsync"},
{319, C<&IRS::ActivateIrsensorWithFunctionLevel>, "ActivateIrsensorWithFunctionLevel"},
};
// clang-format on
@@ -57,489 +58,292 @@ IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} {
}
IRS::~IRS() = default;
void IRS::ActivateIrsensor(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}",
applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
Result IRS::ActivateIrsensor(ClientAppletResourceUserId aruid) {
LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}", aruid.pid);
R_SUCCEED();
}
void IRS::DeactivateIrsensor(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}",
applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
Result IRS::DeactivateIrsensor(ClientAppletResourceUserId aruid) {
LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}", aruid.pid);
R_SUCCEED();
}
void IRS::GetIrsensorSharedMemoryHandle(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
Result IRS::GetIrsensorSharedMemoryHandle(OutCopyHandle<Kernel::KSharedMemory> out_shared_memory,
ClientAppletResourceUserId aruid) {
LOG_DEBUG(Service_IRS, "called, applet_resource_user_id={}", aruid.pid);
LOG_DEBUG(Service_IRS, "called, applet_resource_user_id={}", applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(&system.Kernel().GetIrsSharedMem());
*out_shared_memory = &system.Kernel().GetIrsSharedMem();
R_SUCCEED();
}
void IRS::StopImageProcessor(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
Result IRS::StopImageProcessor(Core::IrSensor::IrCameraHandle camera_handle,
ClientAppletResourceUserId aruid) {
LOG_WARNING(Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
parameters.applet_resource_user_id);
camera_handle.npad_type, camera_handle.npad_id, aruid.pid);
auto result = IsIrCameraHandleValid(parameters.camera_handle);
if (result.IsSuccess()) {
// TODO: Stop Image processor
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::Active);
result = ResultSuccess;
}
R_TRY(IsIrCameraHandleValid(camera_handle));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
// TODO: Stop Image processor
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::Active);
R_SUCCEED();
}
void IRS::RunMomentProcessor(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
Core::IrSensor::PackedMomentProcessorConfig processor_config;
};
static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
Result IRS::RunMomentProcessor(
Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
const Core::IrSensor::PackedMomentProcessorConfig& processor_config) {
LOG_WARNING(Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
parameters.applet_resource_user_id);
camera_handle.npad_type, camera_handle.npad_id, aruid.pid);
const auto result = IsIrCameraHandleValid(parameters.camera_handle);
R_TRY(IsIrCameraHandleValid(camera_handle));
if (result.IsSuccess()) {
auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
MakeProcessorWithCoreContext<MomentProcessor>(parameters.camera_handle, device);
auto& image_transfer_processor = GetProcessor<MomentProcessor>(parameters.camera_handle);
image_transfer_processor.SetConfig(parameters.processor_config);
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::IR);
}
auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
MakeProcessorWithCoreContext<MomentProcessor>(camera_handle, device);
auto& image_transfer_processor = GetProcessor<MomentProcessor>(camera_handle);
image_transfer_processor.SetConfig(processor_config);
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::IR);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
R_SUCCEED();
}
void IRS::RunClusteringProcessor(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
Core::IrSensor::PackedClusteringProcessorConfig processor_config;
};
static_assert(sizeof(Parameters) == 0x38, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
Result IRS::RunClusteringProcessor(
Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
const Core::IrSensor::PackedClusteringProcessorConfig& processor_config) {
LOG_WARNING(Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
parameters.applet_resource_user_id);
camera_handle.npad_type, camera_handle.npad_id, aruid.pid);
auto result = IsIrCameraHandleValid(parameters.camera_handle);
R_TRY(IsIrCameraHandleValid(camera_handle));
if (result.IsSuccess()) {
auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
MakeProcessorWithCoreContext<ClusteringProcessor>(parameters.camera_handle, device);
auto& image_transfer_processor =
GetProcessor<ClusteringProcessor>(parameters.camera_handle);
image_transfer_processor.SetConfig(parameters.processor_config);
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::IR);
}
auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
MakeProcessorWithCoreContext<ClusteringProcessor>(camera_handle, device);
auto& image_transfer_processor = GetProcessor<ClusteringProcessor>(camera_handle);
image_transfer_processor.SetConfig(processor_config);
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::IR);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
R_SUCCEED();
}
void IRS::RunImageTransferProcessor(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
Core::IrSensor::PackedImageTransferProcessorConfig processor_config;
u32 transfer_memory_size;
};
static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size.");
Result IRS::RunImageTransferProcessor(
Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
const Core::IrSensor::PackedImageTransferProcessorConfig& processor_config,
u64 transfer_memory_size, InCopyHandle<Kernel::KTransferMemory> t_mem) {
const auto parameters{rp.PopRaw<Parameters>()};
const auto t_mem_handle{ctx.GetCopyHandle(0)};
auto t_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_handle);
if (t_mem.IsNull()) {
LOG_ERROR(Service_IRS, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
ASSERT_MSG(t_mem->GetSize() == parameters.transfer_memory_size, "t_mem has incorrect size");
ASSERT_MSG(t_mem->GetSize() == transfer_memory_size, "t_mem has incorrect size");
LOG_INFO(Service_IRS,
"called, npad_type={}, npad_id={}, transfer_memory_size={}, transfer_memory_size={}, "
"applet_resource_user_id={}",
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
parameters.transfer_memory_size, t_mem->GetSize(), parameters.applet_resource_user_id);
camera_handle.npad_type, camera_handle.npad_id, transfer_memory_size, t_mem->GetSize(),
aruid.pid);
const auto result = IsIrCameraHandleValid(parameters.camera_handle);
R_TRY(IsIrCameraHandleValid(camera_handle));
if (result.IsSuccess()) {
auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
MakeProcessorWithCoreContext<ImageTransferProcessor>(parameters.camera_handle, device);
auto& image_transfer_processor =
GetProcessor<ImageTransferProcessor>(parameters.camera_handle);
image_transfer_processor.SetConfig(parameters.processor_config);
image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress());
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::IR);
}
auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
MakeProcessorWithCoreContext<ImageTransferProcessor>(camera_handle, device);
auto& image_transfer_processor = GetProcessor<ImageTransferProcessor>(camera_handle);
image_transfer_processor.SetConfig(processor_config);
image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress());
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::IR);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
R_SUCCEED();
}
void IRS::GetImageTransferProcessorState(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
Result IRS::GetImageTransferProcessorState(
Out<Core::IrSensor::ImageTransferProcessorState> out_state,
Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
OutBuffer<BufferAttr_HipcMapAlias> out_buffer_data) {
LOG_DEBUG(Service_IRS, "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
parameters.applet_resource_user_id);
camera_handle.npad_type, camera_handle.npad_id, aruid.pid);
const auto result = IsIrCameraHandleValid(parameters.camera_handle);
if (result.IsError()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
return;
}
R_TRY(IsIrCameraHandleValid(camera_handle));
const auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
const auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
if (device.mode != Core::IrSensor::IrSensorMode::ImageTransferProcessor) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(InvalidProcessorState);
return;
}
R_TRY(IsIrCameraHandleValid(camera_handle));
R_UNLESS(device.mode == Core::IrSensor::IrSensorMode::ImageTransferProcessor,
InvalidProcessorState);
std::vector<u8> data{};
const auto& image_transfer_processor =
GetProcessor<ImageTransferProcessor>(parameters.camera_handle);
const auto& state = image_transfer_processor.GetState(data);
*out_state = GetProcessor<ImageTransferProcessor>(camera_handle).GetState(out_buffer_data);
ctx.WriteBuffer(data);
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess);
rb.PushRaw(state);
R_SUCCEED();
}
void IRS::RunTeraPluginProcessor(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Core::IrSensor::IrCameraHandle camera_handle;
Core::IrSensor::PackedTeraPluginProcessorConfig processor_config;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
Result IRS::RunTeraPluginProcessor(Core::IrSensor::IrCameraHandle camera_handle,
Core::IrSensor::PackedTeraPluginProcessorConfig processor_config,
ClientAppletResourceUserId aruid) {
LOG_WARNING(Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, mode={}, mcu_version={}.{}, "
"applet_resource_user_id={}",
camera_handle.npad_type, camera_handle.npad_id, processor_config.mode,
processor_config.required_mcu_version.major,
processor_config.required_mcu_version.minor, aruid.pid);
const auto parameters{rp.PopRaw<Parameters>()};
R_TRY(IsIrCameraHandleValid(camera_handle));
LOG_WARNING(
Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, mode={}, mcu_version={}.{}, "
"applet_resource_user_id={}",
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
parameters.processor_config.mode, parameters.processor_config.required_mcu_version.major,
parameters.processor_config.required_mcu_version.minor, parameters.applet_resource_user_id);
auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
MakeProcessor<TeraPluginProcessor>(camera_handle, device);
auto& image_transfer_processor = GetProcessor<TeraPluginProcessor>(camera_handle);
image_transfer_processor.SetConfig(processor_config);
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::IR);
const auto result = IsIrCameraHandleValid(parameters.camera_handle);
if (result.IsSuccess()) {
auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
MakeProcessor<TeraPluginProcessor>(parameters.camera_handle, device);
auto& image_transfer_processor =
GetProcessor<TeraPluginProcessor>(parameters.camera_handle);
image_transfer_processor.SetConfig(parameters.processor_config);
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::IR);
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
R_SUCCEED();
}
void IRS::GetNpadIrCameraHandle(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto npad_id{rp.PopEnum<Core::HID::NpadIdType>()};
Result IRS::GetNpadIrCameraHandle(Out<Core::IrSensor::IrCameraHandle> out_camera_handle,
Core::HID::NpadIdType npad_id) {
R_UNLESS(HID::IsNpadIdValid(npad_id), HID::ResultInvalidNpadId);
if (npad_id > Core::HID::NpadIdType::Player8 && npad_id != Core::HID::NpadIdType::Invalid &&
npad_id != Core::HID::NpadIdType::Handheld) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(Service::HID::ResultInvalidNpadId);
return;
}
Core::IrSensor::IrCameraHandle camera_handle{
*out_camera_handle = {
.npad_id = static_cast<u8>(HID::NpadIdTypeToIndex(npad_id)),
.npad_type = Core::HID::NpadStyleIndex::None,
};
LOG_INFO(Service_IRS, "called, npad_id={}, camera_npad_id={}, camera_npad_type={}", npad_id,
camera_handle.npad_id, camera_handle.npad_type);
out_camera_handle->npad_id, out_camera_handle->npad_type);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushRaw(camera_handle);
R_SUCCEED();
}
void IRS::RunPointingProcessor(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()};
const auto processor_config{rp.PopRaw<Core::IrSensor::PackedPointingProcessorConfig>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
Result IRS::RunPointingProcessor(
Core::IrSensor::IrCameraHandle camera_handle,
const Core::IrSensor::PackedPointingProcessorConfig& processor_config,
ClientAppletResourceUserId aruid) {
LOG_WARNING(
Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, mcu_version={}.{}, applet_resource_user_id={}",
camera_handle.npad_type, camera_handle.npad_id, processor_config.required_mcu_version.major,
processor_config.required_mcu_version.minor, applet_resource_user_id);
processor_config.required_mcu_version.minor, aruid.pid);
auto result = IsIrCameraHandleValid(camera_handle);
R_TRY(IsIrCameraHandleValid(camera_handle));
if (result.IsSuccess()) {
auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
MakeProcessor<PointingProcessor>(camera_handle, device);
auto& image_transfer_processor = GetProcessor<PointingProcessor>(camera_handle);
image_transfer_processor.SetConfig(processor_config);
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::IR);
}
auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
MakeProcessor<PointingProcessor>(camera_handle, device);
auto& image_transfer_processor = GetProcessor<PointingProcessor>(camera_handle);
image_transfer_processor.SetConfig(processor_config);
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::IR);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
R_SUCCEED();
}
void IRS::SuspendImageProcessor(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
Result IRS::SuspendImageProcessor(Core::IrSensor::IrCameraHandle camera_handle,
ClientAppletResourceUserId aruid) {
LOG_WARNING(Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
parameters.applet_resource_user_id);
camera_handle.npad_type, camera_handle.npad_id, aruid.pid);
auto result = IsIrCameraHandleValid(parameters.camera_handle);
if (result.IsSuccess()) {
// TODO: Suspend image processor
result = ResultSuccess;
}
R_TRY(IsIrCameraHandleValid(camera_handle));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
// TODO: Suspend image processor
R_SUCCEED();
}
void IRS::CheckFirmwareVersion(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()};
const auto mcu_version{rp.PopRaw<Core::IrSensor::PackedMcuVersion>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
Result IRS::CheckFirmwareVersion(Core::IrSensor::IrCameraHandle camera_handle,
Core::IrSensor::PackedMcuVersion mcu_version,
ClientAppletResourceUserId aruid) {
LOG_WARNING(
Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}, mcu_version={}.{}",
camera_handle.npad_type, camera_handle.npad_id, applet_resource_user_id, mcu_version.major,
camera_handle.npad_type, camera_handle.npad_id, aruid.pid, mcu_version.major,
mcu_version.minor);
auto result = IsIrCameraHandleValid(camera_handle);
if (result.IsSuccess()) {
// TODO: Check firmware version
result = ResultSuccess;
}
R_TRY(IsIrCameraHandleValid(camera_handle));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
// TODO: Check firmware version
R_SUCCEED();
}
void IRS::SetFunctionLevel(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()};
const auto function_level{rp.PopRaw<Core::IrSensor::PackedFunctionLevel>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
Result IRS::SetFunctionLevel(Core::IrSensor::IrCameraHandle camera_handle,
Core::IrSensor::PackedFunctionLevel function_level,
ClientAppletResourceUserId aruid) {
LOG_WARNING(
Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, function_level={}, applet_resource_user_id={}",
camera_handle.npad_type, camera_handle.npad_id, function_level.function_level,
applet_resource_user_id);
camera_handle.npad_type, camera_handle.npad_id, function_level.function_level, aruid.pid);
auto result = IsIrCameraHandleValid(camera_handle);
if (result.IsSuccess()) {
// TODO: Set Function level
result = ResultSuccess;
}
R_TRY(IsIrCameraHandleValid(camera_handle));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
// TODO: Set Function level
R_SUCCEED();
}
void IRS::RunImageTransferExProcessor(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
Core::IrSensor::PackedImageTransferProcessorExConfig processor_config;
u64 transfer_memory_size;
};
static_assert(sizeof(Parameters) == 0x38, "Parameters has incorrect size.");
Result IRS::RunImageTransferExProcessor(
Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
const Core::IrSensor::PackedImageTransferProcessorExConfig& processor_config,
u64 transfer_memory_size, InCopyHandle<Kernel::KTransferMemory> t_mem) {
const auto parameters{rp.PopRaw<Parameters>()};
const auto t_mem_handle{ctx.GetCopyHandle(0)};
auto t_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_handle);
ASSERT_MSG(t_mem->GetSize() == transfer_memory_size, "t_mem has incorrect size");
LOG_INFO(Service_IRS,
"called, npad_type={}, npad_id={}, transfer_memory_size={}, "
"applet_resource_user_id={}",
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
parameters.transfer_memory_size, parameters.applet_resource_user_id);
camera_handle.npad_type, camera_handle.npad_id, transfer_memory_size, aruid.pid);
auto result = IsIrCameraHandleValid(parameters.camera_handle);
R_TRY(IsIrCameraHandleValid(camera_handle));
if (result.IsSuccess()) {
auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
MakeProcessorWithCoreContext<ImageTransferProcessor>(parameters.camera_handle, device);
auto& image_transfer_processor =
GetProcessor<ImageTransferProcessor>(parameters.camera_handle);
image_transfer_processor.SetConfig(parameters.processor_config);
image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress());
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::IR);
}
auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
MakeProcessorWithCoreContext<ImageTransferProcessor>(camera_handle, device);
auto& image_transfer_processor = GetProcessor<ImageTransferProcessor>(camera_handle);
image_transfer_processor.SetConfig(processor_config);
image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress());
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::IR);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
R_SUCCEED();
}
void IRS::RunIrLedProcessor(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()};
const auto processor_config{rp.PopRaw<Core::IrSensor::PackedIrLedProcessorConfig>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
Result IRS::RunIrLedProcessor(Core::IrSensor::IrCameraHandle camera_handle,
Core::IrSensor::PackedIrLedProcessorConfig processor_config,
ClientAppletResourceUserId aruid) {
LOG_WARNING(Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, light_target={}, mcu_version={}.{} "
"applet_resource_user_id={}",
camera_handle.npad_type, camera_handle.npad_id, processor_config.light_target,
processor_config.required_mcu_version.major,
processor_config.required_mcu_version.minor, applet_resource_user_id);
processor_config.required_mcu_version.minor, aruid.pid);
auto result = IsIrCameraHandleValid(camera_handle);
R_TRY(IsIrCameraHandleValid(camera_handle));
if (result.IsSuccess()) {
auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
MakeProcessor<IrLedProcessor>(camera_handle, device);
auto& image_transfer_processor = GetProcessor<IrLedProcessor>(camera_handle);
image_transfer_processor.SetConfig(processor_config);
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::IR);
}
auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
MakeProcessor<IrLedProcessor>(camera_handle, device);
auto& image_transfer_processor = GetProcessor<IrLedProcessor>(camera_handle);
image_transfer_processor.SetConfig(processor_config);
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::IR);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
R_SUCCEED();
}
void IRS::StopImageProcessorAsync(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
Result IRS::StopImageProcessorAsync(Core::IrSensor::IrCameraHandle camera_handle,
ClientAppletResourceUserId aruid) {
LOG_WARNING(Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
parameters.applet_resource_user_id);
camera_handle.npad_type, camera_handle.npad_id, aruid.pid);
auto result = IsIrCameraHandleValid(parameters.camera_handle);
if (result.IsSuccess()) {
// TODO: Stop image processor async
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::Active);
result = ResultSuccess;
}
R_TRY(IsIrCameraHandleValid(camera_handle));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
// TODO: Stop image processor async
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::Active);
R_SUCCEED();
}
void IRS::ActivateIrsensorWithFunctionLevel(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Core::IrSensor::PackedFunctionLevel function_level;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
Result IRS::ActivateIrsensorWithFunctionLevel(Core::IrSensor::PackedFunctionLevel function_level,
ClientAppletResourceUserId aruid) {
LOG_WARNING(Service_IRS, "(STUBBED) called, function_level={}, applet_resource_user_id={}",
parameters.function_level.function_level, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
function_level.function_level, aruid.pid);
R_SUCCEED();
}
Result IRS::IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const {

View File

@@ -4,6 +4,7 @@
#pragma once
#include "core/core.h"
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h"
#include "hid_core/hid_types.h"
#include "hid_core/irsensor/irs_types.h"
@@ -35,26 +36,73 @@ private:
};
static_assert(sizeof(StatusManager) == 0x8000, "StatusManager is an invalid size");
void ActivateIrsensor(HLERequestContext& ctx);
void DeactivateIrsensor(HLERequestContext& ctx);
void GetIrsensorSharedMemoryHandle(HLERequestContext& ctx);
void StopImageProcessor(HLERequestContext& ctx);
void RunMomentProcessor(HLERequestContext& ctx);
void RunClusteringProcessor(HLERequestContext& ctx);
void RunImageTransferProcessor(HLERequestContext& ctx);
void GetImageTransferProcessorState(HLERequestContext& ctx);
void RunTeraPluginProcessor(HLERequestContext& ctx);
void GetNpadIrCameraHandle(HLERequestContext& ctx);
void RunPointingProcessor(HLERequestContext& ctx);
void SuspendImageProcessor(HLERequestContext& ctx);
void CheckFirmwareVersion(HLERequestContext& ctx);
void SetFunctionLevel(HLERequestContext& ctx);
void RunImageTransferExProcessor(HLERequestContext& ctx);
void RunIrLedProcessor(HLERequestContext& ctx);
void StopImageProcessorAsync(HLERequestContext& ctx);
void ActivateIrsensorWithFunctionLevel(HLERequestContext& ctx);
Result ActivateIrsensor(ClientAppletResourceUserId aruid);
Result DeactivateIrsensor(ClientAppletResourceUserId aruid);
Result GetIrsensorSharedMemoryHandle(OutCopyHandle<Kernel::KSharedMemory> out_shared_memory,
ClientAppletResourceUserId aruid);
Result StopImageProcessor(Core::IrSensor::IrCameraHandle camera_handle,
ClientAppletResourceUserId aruid);
Result RunMomentProcessor(Core::IrSensor::IrCameraHandle camera_handle,
ClientAppletResourceUserId aruid,
const Core::IrSensor::PackedMomentProcessorConfig& processor_config);
Result RunClusteringProcessor(
Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
const Core::IrSensor::PackedClusteringProcessorConfig& processor_config);
Result RunImageTransferProcessor(
Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
const Core::IrSensor::PackedImageTransferProcessorConfig& processor_config,
u64 transfer_memory_size, InCopyHandle<Kernel::KTransferMemory> t_mem);
Result GetImageTransferProcessorState(
Out<Core::IrSensor::ImageTransferProcessorState> out_state,
Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
OutBuffer<BufferAttr_HipcMapAlias> out_buffer_data);
Result RunTeraPluginProcessor(Core::IrSensor::IrCameraHandle camera_handle,
Core::IrSensor::PackedTeraPluginProcessorConfig processor_config,
ClientAppletResourceUserId aruid);
Result GetNpadIrCameraHandle(Out<Core::IrSensor::IrCameraHandle> out_camera_handle,
Core::HID::NpadIdType npad_id);
Result RunPointingProcessor(
Core::IrSensor::IrCameraHandle camera_handle,
const Core::IrSensor::PackedPointingProcessorConfig& processor_config,
ClientAppletResourceUserId aruid);
Result SuspendImageProcessor(Core::IrSensor::IrCameraHandle camera_handle,
ClientAppletResourceUserId aruid);
Result CheckFirmwareVersion(Core::IrSensor::IrCameraHandle camera_handle,
Core::IrSensor::PackedMcuVersion mcu_version,
ClientAppletResourceUserId aruid);
Result SetFunctionLevel(Core::IrSensor::IrCameraHandle camera_handle,
Core::IrSensor::PackedFunctionLevel function_level,
ClientAppletResourceUserId aruid);
Result RunImageTransferExProcessor(
Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
const Core::IrSensor::PackedImageTransferProcessorExConfig& processor_config,
u64 transfer_memory_size, InCopyHandle<Kernel::KTransferMemory> t_mem);
Result RunIrLedProcessor(Core::IrSensor::IrCameraHandle camera_handle,
Core::IrSensor::PackedIrLedProcessorConfig processor_config,
ClientAppletResourceUserId aruid);
Result StopImageProcessorAsync(Core::IrSensor::IrCameraHandle camera_handle,
ClientAppletResourceUserId aruid);
Result ActivateIrsensorWithFunctionLevel(Core::IrSensor::PackedFunctionLevel function_level,
ClientAppletResourceUserId aruid);
Result IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const;
Core::IrSensor::DeviceFormat& GetIrCameraSharedMemoryDeviceEntry(
const Core::IrSensor::IrCameraHandle& camera_handle);

View File

@@ -3,110 +3,21 @@
#include <mutex>
#include <boost/container/small_vector.hpp>
#define BOOST_NO_MT
#include <boost/pool/detail/mutex.hpp>
#undef BOOST_NO_MT
#include <boost/icl/interval.hpp>
#include <boost/icl/interval_base_set.hpp>
#include <boost/icl/interval_set.hpp>
#include <boost/icl/split_interval_map.hpp>
#include <boost/pool/pool.hpp>
#include <boost/pool/pool_alloc.hpp>
#include <boost/pool/poolfwd.hpp>
#include "common/range_sets.h"
#include "common/range_sets.inc"
#include "core/hle/service/nvdrv/core/heap_mapper.h"
#include "video_core/host1x/host1x.h"
namespace boost {
template <typename T>
class fast_pool_allocator<T, default_user_allocator_new_delete, details::pool::null_mutex, 4096, 0>;
}
namespace Service::Nvidia::NvCore {
using IntervalCompare = std::less<DAddr>;
using IntervalInstance = boost::icl::interval_type_default<DAddr, std::less>;
using IntervalAllocator = boost::fast_pool_allocator<DAddr>;
using IntervalSet = boost::icl::interval_set<DAddr>;
using IntervalType = typename IntervalSet::interval_type;
template <typename Type>
struct counter_add_functor : public boost::icl::identity_based_inplace_combine<Type> {
// types
typedef counter_add_functor<Type> type;
typedef boost::icl::identity_based_inplace_combine<Type> base_type;
// public member functions
void operator()(Type& current, const Type& added) const {
current += added;
if (current < base_type::identity_element()) {
current = base_type::identity_element();
}
}
// public static functions
static void version(Type&){};
};
using OverlapCombine = counter_add_functor<int>;
using OverlapSection = boost::icl::inter_section<int>;
using OverlapCounter = boost::icl::split_interval_map<DAddr, int>;
struct HeapMapper::HeapMapperInternal {
HeapMapperInternal(Tegra::Host1x::Host1x& host1x) : device_memory{host1x.MemoryManager()} {}
HeapMapperInternal(Tegra::Host1x::Host1x& host1x) : m_device_memory{host1x.MemoryManager()} {}
~HeapMapperInternal() = default;
template <typename Func>
void ForEachInOverlapCounter(OverlapCounter& current_range, VAddr cpu_addr, u64 size,
Func&& func) {
const DAddr start_address = cpu_addr;
const DAddr end_address = start_address + size;
const IntervalType search_interval{start_address, end_address};
auto it = current_range.lower_bound(search_interval);
if (it == current_range.end()) {
return;
}
auto end_it = current_range.upper_bound(search_interval);
for (; it != end_it; it++) {
auto& inter = it->first;
DAddr inter_addr_end = inter.upper();
DAddr inter_addr = inter.lower();
if (inter_addr_end > end_address) {
inter_addr_end = end_address;
}
if (inter_addr < start_address) {
inter_addr = start_address;
}
func(inter_addr, inter_addr_end, it->second);
}
}
void RemoveEachInOverlapCounter(OverlapCounter& current_range,
const IntervalType search_interval, int subtract_value) {
bool any_removals = false;
current_range.add(std::make_pair(search_interval, subtract_value));
do {
any_removals = false;
auto it = current_range.lower_bound(search_interval);
if (it == current_range.end()) {
return;
}
auto end_it = current_range.upper_bound(search_interval);
for (; it != end_it; it++) {
if (it->second <= 0) {
any_removals = true;
current_range.erase(it);
break;
}
}
} while (any_removals);
}
IntervalSet base_set;
OverlapCounter mapping_overlaps;
Tegra::MaxwellDeviceMemoryManager& device_memory;
std::mutex guard;
Common::RangeSet<VAddr> m_temporary_set;
Common::OverlapRangeSet<VAddr> m_mapped_ranges;
Tegra::MaxwellDeviceMemoryManager& m_device_memory;
std::mutex m_guard;
};
HeapMapper::HeapMapper(VAddr start_vaddress, DAddr start_daddress, size_t size, Core::Asid asid,
@@ -116,60 +27,48 @@ HeapMapper::HeapMapper(VAddr start_vaddress, DAddr start_daddress, size_t size,
}
HeapMapper::~HeapMapper() {
m_internal->device_memory.Unmap(m_daddress, m_size);
// Unmap whatever has been mapped.
m_internal->m_mapped_ranges.ForEach([this](VAddr start_addr, VAddr end_addr, s32 count) {
const size_t sub_size = end_addr - start_addr;
const size_t offset = start_addr - m_vaddress;
m_internal->m_device_memory.Unmap(m_daddress + offset, sub_size);
});
}
DAddr HeapMapper::Map(VAddr start, size_t size) {
std::scoped_lock lk(m_internal->guard);
m_internal->base_set.clear();
const IntervalType interval{start, start + size};
m_internal->base_set.insert(interval);
m_internal->ForEachInOverlapCounter(m_internal->mapping_overlaps, start, size,
[this](VAddr start_addr, VAddr end_addr, int) {
const IntervalType other{start_addr, end_addr};
m_internal->base_set.subtract(other);
});
if (!m_internal->base_set.empty()) {
auto it = m_internal->base_set.begin();
auto end_it = m_internal->base_set.end();
for (; it != end_it; it++) {
const VAddr inter_addr_end = it->upper();
const VAddr inter_addr = it->lower();
const size_t offset = inter_addr - m_vaddress;
const size_t sub_size = inter_addr_end - inter_addr;
m_internal->device_memory.Map(m_daddress + offset, m_vaddress + offset, sub_size,
m_asid);
}
}
m_internal->mapping_overlaps += std::make_pair(interval, 1);
m_internal->base_set.clear();
return m_daddress + (start - m_vaddress);
std::scoped_lock lk(m_internal->m_guard);
// Add the mapping range to a temporary range set.
m_internal->m_temporary_set.Clear();
m_internal->m_temporary_set.Add(start, size);
// Remove anything that's already mapped from the temporary range set.
m_internal->m_mapped_ranges.ForEachInRange(
start, size, [this](VAddr start_addr, VAddr end_addr, s32) {
m_internal->m_temporary_set.Subtract(start_addr, end_addr - start_addr);
});
// Map anything that has not been mapped yet.
m_internal->m_temporary_set.ForEach([this](VAddr start_addr, VAddr end_addr) {
const size_t sub_size = end_addr - start_addr;
const size_t offset = start_addr - m_vaddress;
m_internal->m_device_memory.Map(m_daddress + offset, m_vaddress + offset, sub_size, m_asid);
});
// Add the mapping range to the split map, to register the map and overlaps.
m_internal->m_mapped_ranges.Add(start, size);
m_internal->m_temporary_set.Clear();
return m_daddress + static_cast<DAddr>(start - m_vaddress);
}
void HeapMapper::Unmap(VAddr start, size_t size) {
std::scoped_lock lk(m_internal->guard);
m_internal->base_set.clear();
m_internal->ForEachInOverlapCounter(m_internal->mapping_overlaps, start, size,
[this](VAddr start_addr, VAddr end_addr, int value) {
if (value <= 1) {
const IntervalType other{start_addr, end_addr};
m_internal->base_set.insert(other);
}
});
if (!m_internal->base_set.empty()) {
auto it = m_internal->base_set.begin();
auto end_it = m_internal->base_set.end();
for (; it != end_it; it++) {
const VAddr inter_addr_end = it->upper();
const VAddr inter_addr = it->lower();
const size_t offset = inter_addr - m_vaddress;
const size_t sub_size = inter_addr_end - inter_addr;
m_internal->device_memory.Unmap(m_daddress + offset, sub_size);
}
}
const IntervalType to_remove{start, start + size};
m_internal->RemoveEachInOverlapCounter(m_internal->mapping_overlaps, to_remove, -1);
m_internal->base_set.clear();
std::scoped_lock lk(m_internal->m_guard);
// Just subtract the range and whatever is deleted, unmap it.
m_internal->m_mapped_ranges.Subtract(start, size, [this](VAddr start_addr, VAddr end_addr) {
const size_t sub_size = end_addr - start_addr;
const size_t offset = start_addr - m_vaddress;
m_internal->m_device_memory.Unmap(m_daddress + offset, sub_size);
});
}
} // namespace Service::Nvidia::NvCore

View File

@@ -123,6 +123,8 @@ NvResult nvhost_as_gpu::AllocAsEx(IoctlAllocAsEx& params) {
vm.va_range_end = params.va_range_end;
}
const u64 max_big_page_bits = Common::Log2Ceil64(vm.va_range_end);
const auto start_pages{static_cast<u32>(vm.va_range_start >> VM::PAGE_SIZE_BITS)};
const auto end_pages{static_cast<u32>(vm.va_range_split >> VM::PAGE_SIZE_BITS)};
vm.small_page_allocator = std::make_shared<VM::Allocator>(start_pages, end_pages);
@@ -132,8 +134,8 @@ NvResult nvhost_as_gpu::AllocAsEx(IoctlAllocAsEx& params) {
static_cast<u32>((vm.va_range_end - vm.va_range_split) >> vm.big_page_size_bits)};
vm.big_page_allocator = std::make_unique<VM::Allocator>(start_big_pages, end_big_pages);
gmmu = std::make_shared<Tegra::MemoryManager>(system, 40, vm.big_page_size_bits,
VM::PAGE_SIZE_BITS);
gmmu = std::make_shared<Tegra::MemoryManager>(system, max_big_page_bits, vm.va_range_split,
vm.big_page_size_bits, VM::PAGE_SIZE_BITS);
system.GPU().InitAddressSpace(*gmmu);
vm.initialised = true;

View File

@@ -7,7 +7,6 @@
#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
#include "core/hle/service/nvnflinger/buffer_item.h"
#include "core/hle/service/nvnflinger/buffer_item_consumer.h"
#include "core/hle/service/nvnflinger/buffer_queue_producer.h"
#include "core/hle/service/nvnflinger/hardware_composer.h"
#include "core/hle/service/nvnflinger/hwc_layer.h"
#include "core/hle/service/nvnflinger/ui/graphic_buffer.h"
@@ -46,31 +45,9 @@ HardwareComposer::HardwareComposer() = default;
HardwareComposer::~HardwareComposer() = default;
u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, VI::Display& display,
Nvidia::Devices::nvdisp_disp0& nvdisp, u32 frame_advance) {
Nvidia::Devices::nvdisp_disp0& nvdisp) {
boost::container::small_vector<HwcLayer, 2> composition_stack;
m_frame_number += frame_advance;
// Release any necessary framebuffers.
for (auto& [layer_id, framebuffer] : m_framebuffers) {
if (framebuffer.release_frame_number > m_frame_number) {
// Not yet ready to release this framebuffer.
continue;
}
if (!framebuffer.is_acquired) {
// Already released.
continue;
}
if (auto* layer = display.FindLayer(layer_id); layer != nullptr) {
// TODO: support release fence
// This is needed to prevent screen tearing
layer->GetConsumer().ReleaseBuffer(framebuffer.item, android::Fence::NoFence());
framebuffer.is_acquired = false;
}
}
// Set default speed limit to 100%.
*out_speed_scale = 1.0f;
@@ -142,7 +119,30 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, VI::Display& display,
MicroProfileFlip();
// Advance by at least one frame.
return swap_interval.value_or(1);
const u32 frame_advance = swap_interval.value_or(1);
m_frame_number += frame_advance;
// Release any necessary framebuffers.
for (auto& [layer_id, framebuffer] : m_framebuffers) {
if (framebuffer.release_frame_number > m_frame_number) {
// Not yet ready to release this framebuffer.
continue;
}
if (!framebuffer.is_acquired) {
// Already released.
continue;
}
if (auto* layer = display.FindLayer(layer_id); layer != nullptr) {
// TODO: support release fence
// This is needed to prevent screen tearing
layer->GetConsumer().ReleaseBuffer(framebuffer.item, android::Fence::NoFence());
framebuffer.is_acquired = false;
}
}
return frame_advance;
}
void HardwareComposer::RemoveLayerLocked(VI::Display& display, LayerId layer_id) {

View File

@@ -27,7 +27,7 @@ public:
~HardwareComposer();
u32 ComposeLocked(f32* out_speed_scale, VI::Display& display,
Nvidia::Devices::nvdisp_disp0& nvdisp, u32 frame_advance);
Nvidia::Devices::nvdisp_disp0& nvdisp);
void RemoveLayerLocked(VI::Display& display, LayerId layer_id);
private:

View File

@@ -291,8 +291,7 @@ void Nvnflinger::Compose() {
auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>(disp_fd);
ASSERT(nvdisp);
swap_interval = display.GetComposer().ComposeLocked(&compose_speed_scale, display, *nvdisp,
swap_interval);
swap_interval = display.GetComposer().ComposeLocked(&compose_speed_scale, display, *nvdisp);
}
}

View File

@@ -5,10 +5,12 @@
#include "common/hex_util.h"
#include "common/microprofile.h"
#include "common/swap.h"
#include "core/arm/debug.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_process_page_table.h"
#include "core/hle/service/hid/hid_server.h"
#include "core/hle/service/sm/sm.h"
#include "core/memory.h"
@@ -46,12 +48,25 @@ StandardVmCallbacks::StandardVmCallbacks(System& system_, const CheatProcessMeta
StandardVmCallbacks::~StandardVmCallbacks() = default;
void StandardVmCallbacks::MemoryRead(VAddr address, void* data, u64 size) {
system.ApplicationMemory().ReadBlock(SanitizeAddress(address), data, size);
void StandardVmCallbacks::MemoryReadUnsafe(VAddr address, void* data, u64 size) {
// Return zero on invalid address
if (!IsAddressInRange(address) || !system.ApplicationMemory().IsValidVirtualAddress(address)) {
std::memset(data, 0, size);
return;
}
system.ApplicationMemory().ReadBlock(address, data, size);
}
void StandardVmCallbacks::MemoryWrite(VAddr address, const void* data, u64 size) {
system.ApplicationMemory().WriteBlock(SanitizeAddress(address), data, size);
void StandardVmCallbacks::MemoryWriteUnsafe(VAddr address, const void* data, u64 size) {
// Skip invalid memory write address
if (!IsAddressInRange(address) || !system.ApplicationMemory().IsValidVirtualAddress(address)) {
return;
}
if (system.ApplicationMemory().WriteBlock(address, data, size)) {
Core::InvalidateInstructionCacheRange(system.ApplicationProcess(), address, size);
}
}
u64 StandardVmCallbacks::HidKeysDown() {
@@ -81,21 +96,25 @@ void StandardVmCallbacks::CommandLog(std::string_view data) {
data.back() == '\n' ? data.substr(0, data.size() - 1) : data);
}
VAddr StandardVmCallbacks::SanitizeAddress(VAddr in) const {
bool StandardVmCallbacks::IsAddressInRange(VAddr in) const {
if ((in < metadata.main_nso_extents.base ||
in >= metadata.main_nso_extents.base + metadata.main_nso_extents.size) &&
(in < metadata.heap_extents.base ||
in >= metadata.heap_extents.base + metadata.heap_extents.size)) {
LOG_ERROR(CheatEngine,
in >= metadata.heap_extents.base + metadata.heap_extents.size) &&
(in < metadata.alias_extents.base ||
in >= metadata.heap_extents.base + metadata.alias_extents.size) &&
(in < metadata.aslr_extents.base ||
in >= metadata.heap_extents.base + metadata.aslr_extents.size)) {
LOG_DEBUG(CheatEngine,
"Cheat attempting to access memory at invalid address={:016X}, if this "
"persists, "
"the cheat may be incorrect. However, this may be normal early in execution if "
"the game has not properly set up yet.",
in);
return 0; ///< Invalid addresses will hard crash
return false; ///< Invalid addresses will hard crash
}
return in;
return true;
}
CheatParser::~CheatParser() = default;
@@ -211,16 +230,14 @@ void CheatEngine::Initialize() {
.base = GetInteger(page_table.GetHeapRegionStart()),
.size = page_table.GetHeapRegionSize(),
};
metadata.address_space_extents = {
.base = GetInteger(page_table.GetAddressSpaceStart()),
.size = page_table.GetAddressSpaceSize(),
};
metadata.alias_extents = {
metadata.aslr_extents = {
.base = GetInteger(page_table.GetAliasCodeRegionStart()),
.size = page_table.GetAliasCodeRegionSize(),
};
metadata.alias_extents = {
.base = GetInteger(page_table.GetAliasRegionStart()),
.size = page_table.GetAliasRegionSize(),
};
is_pending_reload.exchange(true);
}

View File

@@ -27,17 +27,17 @@ public:
StandardVmCallbacks(System& system_, const CheatProcessMetadata& metadata_);
~StandardVmCallbacks() override;
void MemoryRead(VAddr address, void* data, u64 size) override;
void MemoryWrite(VAddr address, const void* data, u64 size) override;
void MemoryReadUnsafe(VAddr address, void* data, u64 size) override;
void MemoryWriteUnsafe(VAddr address, const void* data, u64 size) override;
u64 HidKeysDown() override;
void DebugLog(u8 id, u64 value) override;
void CommandLog(std::string_view data) override;
private:
VAddr SanitizeAddress(VAddr address) const;
bool IsAddressInRange(VAddr address) const;
const CheatProcessMetadata& metadata;
System& system;
Core::System& system;
};
// Intermediary class that parses a text file or other disk format for storing cheats into a

View File

@@ -18,7 +18,7 @@ struct CheatProcessMetadata {
MemoryRegionExtents main_nso_extents{};
MemoryRegionExtents heap_extents{};
MemoryRegionExtents alias_extents{};
MemoryRegionExtents address_space_extents{};
MemoryRegionExtents aslr_extents{};
std::array<u8, 0x20> main_nso_build_id{};
};

View File

@@ -322,8 +322,9 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
} break;
case CheatVmOpcodeType::EndConditionalBlock: {
// 20000000
// There's actually nothing left to process here!
opcode.opcode = EndConditionalOpcode{};
opcode.opcode = EndConditionalOpcode{
.is_else = ((first_dword >> 24) & 0xf) == 1,
};
} break;
case CheatVmOpcodeType::ControlLoop: {
// 300R0000 VVVVVVVV
@@ -555,6 +556,18 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
.idx = first_dword & 0xF,
};
} break;
case CheatVmOpcodeType::PauseProcess: {
/* FF0????? */
/* FF0 = opcode 0xFF0 */
/* Pauses the current process. */
opcode.opcode = PauseProcessOpcode{};
} break;
case CheatVmOpcodeType::ResumeProcess: {
/* FF0????? */
/* FF0 = opcode 0xFF0 */
/* Pauses the current process. */
opcode.opcode = ResumeProcessOpcode{};
} break;
case CheatVmOpcodeType::DebugLog: {
// FFFTIX##
// FFFTI0Ma aaaaaaaa
@@ -621,7 +634,7 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
return valid;
}
void DmntCheatVm::SkipConditionalBlock() {
void DmntCheatVm::SkipConditionalBlock(bool is_if) {
if (condition_depth > 0) {
// We want to continue until we're out of the current block.
const std::size_t desired_depth = condition_depth - 1;
@@ -637,8 +650,12 @@ void DmntCheatVm::SkipConditionalBlock() {
// We also support nesting of conditional blocks, and Gateway does not.
if (skip_opcode.begin_conditional_block) {
condition_depth++;
} else if (std::holds_alternative<EndConditionalOpcode>(skip_opcode.opcode)) {
condition_depth--;
} else if (auto end_cond = std::get_if<EndConditionalOpcode>(&skip_opcode.opcode)) {
if (!end_cond->is_else) {
condition_depth--;
} else if (is_if && condition_depth - 1 == desired_depth) {
break;
}
}
}
} else {
@@ -675,6 +692,10 @@ u64 DmntCheatVm::GetCheatProcessAddress(const CheatProcessMetadata& metadata,
return metadata.main_nso_extents.base + rel_address;
case MemoryAccessType::Heap:
return metadata.heap_extents.base + rel_address;
case MemoryAccessType::Alias:
return metadata.alias_extents.base + rel_address;
case MemoryAccessType::Aslr:
return metadata.aslr_extents.base + rel_address;
}
}
@@ -682,7 +703,6 @@ void DmntCheatVm::ResetState() {
registers.fill(0);
saved_values.fill(0);
loop_tops.fill(0);
static_registers.fill(0);
instruction_ptr = 0;
condition_depth = 0;
decode_success = true;
@@ -753,7 +773,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
case 2:
case 4:
case 8:
callbacks->MemoryWrite(dst_address, &dst_value, store_static->bit_width);
callbacks->MemoryWriteUnsafe(dst_address, &dst_value, store_static->bit_width);
break;
}
} else if (auto begin_cond = std::get_if<BeginConditionalOpcode>(&cur_opcode.opcode)) {
@@ -766,7 +786,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
case 2:
case 4:
case 8:
callbacks->MemoryRead(src_address, &src_value, begin_cond->bit_width);
callbacks->MemoryReadUnsafe(src_address, &src_value, begin_cond->bit_width);
break;
}
// Check against condition.
@@ -794,13 +814,18 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
}
// Skip conditional block if condition not met.
if (!cond_met) {
SkipConditionalBlock();
SkipConditionalBlock(true);
}
} else if (std::holds_alternative<EndConditionalOpcode>(cur_opcode.opcode)) {
// Decrement the condition depth.
// We will assume, graciously, that mismatched conditional block ends are a nop.
if (condition_depth > 0) {
condition_depth--;
} else if (auto end_cond = std::get_if<EndConditionalOpcode>(&cur_opcode.opcode)) {
if (end_cond->is_else) {
/* Skip to the end of the conditional block. */
this->SkipConditionalBlock(false);
} else {
/* Decrement the condition depth. */
/* We will assume, graciously, that mismatched conditional block ends are a nop. */
if (condition_depth > 0) {
condition_depth--;
}
}
} else if (auto ctrl_loop = std::get_if<ControlLoopOpcode>(&cur_opcode.opcode)) {
if (ctrl_loop->start_loop) {
@@ -832,8 +857,8 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
case 2:
case 4:
case 8:
callbacks->MemoryRead(src_address, &registers[ldr_memory->reg_index],
ldr_memory->bit_width);
callbacks->MemoryReadUnsafe(src_address, &registers[ldr_memory->reg_index],
ldr_memory->bit_width);
break;
}
} else if (auto str_static = std::get_if<StoreStaticToAddressOpcode>(&cur_opcode.opcode)) {
@@ -849,7 +874,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
case 2:
case 4:
case 8:
callbacks->MemoryWrite(dst_address, &dst_value, str_static->bit_width);
callbacks->MemoryWriteUnsafe(dst_address, &dst_value, str_static->bit_width);
break;
}
// Increment register if relevant.
@@ -908,7 +933,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
// Check for keypress.
if ((begin_keypress_cond->key_mask & kDown) != begin_keypress_cond->key_mask) {
// Keys not pressed. Skip conditional block.
SkipConditionalBlock();
SkipConditionalBlock(true);
}
} else if (auto perform_math_reg =
std::get_if<PerformArithmeticRegisterOpcode>(&cur_opcode.opcode)) {
@@ -1007,7 +1032,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
case 2:
case 4:
case 8:
callbacks->MemoryWrite(dst_address, &dst_value, str_register->bit_width);
callbacks->MemoryWriteUnsafe(dst_address, &dst_value, str_register->bit_width);
break;
}
@@ -1086,7 +1111,8 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
case 2:
case 4:
case 8:
callbacks->MemoryRead(cond_address, &cond_value, begin_reg_cond->bit_width);
callbacks->MemoryReadUnsafe(cond_address, &cond_value,
begin_reg_cond->bit_width);
break;
}
}
@@ -1116,7 +1142,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
// Skip conditional block if condition not met.
if (!cond_met) {
SkipConditionalBlock();
SkipConditionalBlock(true);
}
} else if (auto save_restore_reg =
std::get_if<SaveRestoreRegisterOpcode>(&cur_opcode.opcode)) {
@@ -1178,6 +1204,10 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
// Store a register to a static register.
static_registers[rw_static_reg->static_idx] = registers[rw_static_reg->idx];
}
} else if (std::holds_alternative<PauseProcessOpcode>(cur_opcode.opcode)) {
// TODO: Pause cheat process
} else if (std::holds_alternative<ResumeProcessOpcode>(cur_opcode.opcode)) {
// TODO: Resume cheat process
} else if (auto debug_log = std::get_if<DebugLogOpcode>(&cur_opcode.opcode)) {
// Read value from memory.
u64 log_value = 0;
@@ -1224,7 +1254,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
case 2:
case 4:
case 8:
callbacks->MemoryRead(val_address, &log_value, debug_log->bit_width);
callbacks->MemoryReadUnsafe(val_address, &log_value, debug_log->bit_width);
break;
}
}

View File

@@ -42,12 +42,16 @@ enum class CheatVmOpcodeType : u32 {
DoubleExtendedWidth = 0xF0,
// Double-extended width opcodes.
PauseProcess = 0xFF0,
ResumeProcess = 0xFF1,
DebugLog = 0xFFF,
};
enum class MemoryAccessType : u32 {
MainNso = 0,
Heap = 1,
Alias = 2,
Aslr = 3,
};
enum class ConditionalComparisonType : u32 {
@@ -131,7 +135,9 @@ struct BeginConditionalOpcode {
VmInt value{};
};
struct EndConditionalOpcode {};
struct EndConditionalOpcode {
bool is_else;
};
struct ControlLoopOpcode {
bool start_loop{};
@@ -222,6 +228,10 @@ struct ReadWriteStaticRegisterOpcode {
u32 idx{};
};
struct PauseProcessOpcode {};
struct ResumeProcessOpcode {};
struct DebugLogOpcode {
u32 bit_width{};
u32 log_id{};
@@ -244,8 +254,8 @@ struct CheatVmOpcode {
PerformArithmeticStaticOpcode, BeginKeypressConditionalOpcode,
PerformArithmeticRegisterOpcode, StoreRegisterToAddressOpcode,
BeginRegisterConditionalOpcode, SaveRestoreRegisterOpcode,
SaveRestoreRegisterMaskOpcode, ReadWriteStaticRegisterOpcode, DebugLogOpcode,
UnrecognizedInstruction>
SaveRestoreRegisterMaskOpcode, ReadWriteStaticRegisterOpcode, PauseProcessOpcode,
ResumeProcessOpcode, DebugLogOpcode, UnrecognizedInstruction>
opcode{};
};
@@ -256,8 +266,8 @@ public:
public:
virtual ~Callbacks();
virtual void MemoryRead(VAddr address, void* data, u64 size) = 0;
virtual void MemoryWrite(VAddr address, const void* data, u64 size) = 0;
virtual void MemoryReadUnsafe(VAddr address, void* data, u64 size) = 0;
virtual void MemoryWriteUnsafe(VAddr address, const void* data, u64 size) = 0;
virtual u64 HidKeysDown() = 0;
@@ -296,7 +306,7 @@ private:
std::array<std::size_t, NumRegisters> loop_tops{};
bool DecodeNextOpcode(CheatVmOpcode& out);
void SkipConditionalBlock();
void SkipConditionalBlock(bool is_if);
void ResetState();
// For implementing the DebugLog opcode.

View File

@@ -160,7 +160,7 @@ public:
}
// Returns a reply from a command
virtual std::vector<u8> GetReply() const {
virtual u64 GetReply(std::span<u8> out_data) const {
return {};
}

View File

@@ -90,32 +90,32 @@ u8 RingController::GetDeviceId() const {
return device_id;
}
std::vector<u8> RingController::GetReply() const {
u64 RingController::GetReply(std::span<u8> out_data) const {
const RingConCommands current_command = command;
switch (current_command) {
case RingConCommands::GetFirmwareVersion:
return GetFirmwareVersionReply();
return GetFirmwareVersionReply(out_data);
case RingConCommands::ReadId:
return GetReadIdReply();
return GetReadIdReply(out_data);
case RingConCommands::c20105:
return GetC020105Reply();
return GetC020105Reply(out_data);
case RingConCommands::ReadUnkCal:
return GetReadUnkCalReply();
return GetReadUnkCalReply(out_data);
case RingConCommands::ReadFactoryCal:
return GetReadFactoryCalReply();
return GetReadFactoryCalReply(out_data);
case RingConCommands::ReadUserCal:
return GetReadUserCalReply();
return GetReadUserCalReply(out_data);
case RingConCommands::ReadRepCount:
return GetReadRepCountReply();
return GetReadRepCountReply(out_data);
case RingConCommands::ReadTotalPushCount:
return GetReadTotalPushCountReply();
return GetReadTotalPushCountReply(out_data);
case RingConCommands::ResetRepCount:
return GetResetRepCountReply();
return GetResetRepCountReply(out_data);
case RingConCommands::SaveCalData:
return GetSaveDataReply();
return GetSaveDataReply(out_data);
default:
return GetErrorReply();
return GetErrorReply(out_data);
}
}
@@ -163,16 +163,16 @@ bool RingController::SetCommand(std::span<const u8> data) {
}
}
std::vector<u8> RingController::GetFirmwareVersionReply() const {
u64 RingController::GetFirmwareVersionReply(std::span<u8> out_data) const {
const FirmwareVersionReply reply{
.status = DataValid::Valid,
.firmware = version,
};
return GetDataVector(reply);
return GetData(reply, out_data);
}
std::vector<u8> RingController::GetReadIdReply() const {
u64 RingController::GetReadIdReply(std::span<u8> out_data) const {
// The values are hardcoded from a real joycon
const ReadIdReply reply{
.status = DataValid::Valid,
@@ -184,83 +184,83 @@ std::vector<u8> RingController::GetReadIdReply() const {
.id_h_x4 = 8245,
};
return GetDataVector(reply);
return GetData(reply, out_data);
}
std::vector<u8> RingController::GetC020105Reply() const {
u64 RingController::GetC020105Reply(std::span<u8> out_data) const {
const Cmd020105Reply reply{
.status = DataValid::Valid,
.data = 1,
};
return GetDataVector(reply);
return GetData(reply, out_data);
}
std::vector<u8> RingController::GetReadUnkCalReply() const {
u64 RingController::GetReadUnkCalReply(std::span<u8> out_data) const {
const ReadUnkCalReply reply{
.status = DataValid::Valid,
.data = 0,
};
return GetDataVector(reply);
return GetData(reply, out_data);
}
std::vector<u8> RingController::GetReadFactoryCalReply() const {
u64 RingController::GetReadFactoryCalReply(std::span<u8> out_data) const {
const ReadFactoryCalReply reply{
.status = DataValid::Valid,
.calibration = factory_calibration,
};
return GetDataVector(reply);
return GetData(reply, out_data);
}
std::vector<u8> RingController::GetReadUserCalReply() const {
u64 RingController::GetReadUserCalReply(std::span<u8> out_data) const {
const ReadUserCalReply reply{
.status = DataValid::Valid,
.calibration = user_calibration,
};
return GetDataVector(reply);
return GetData(reply, out_data);
}
std::vector<u8> RingController::GetReadRepCountReply() const {
u64 RingController::GetReadRepCountReply(std::span<u8> out_data) const {
const GetThreeByteReply reply{
.status = DataValid::Valid,
.data = {total_rep_count, 0, 0},
.crc = GetCrcValue({total_rep_count, 0, 0, 0}),
};
return GetDataVector(reply);
return GetData(reply, out_data);
}
std::vector<u8> RingController::GetReadTotalPushCountReply() const {
u64 RingController::GetReadTotalPushCountReply(std::span<u8> out_data) const {
const GetThreeByteReply reply{
.status = DataValid::Valid,
.data = {total_push_count, 0, 0},
.crc = GetCrcValue({total_push_count, 0, 0, 0}),
};
return GetDataVector(reply);
return GetData(reply, out_data);
}
std::vector<u8> RingController::GetResetRepCountReply() const {
return GetReadRepCountReply();
u64 RingController::GetResetRepCountReply(std::span<u8> out_data) const {
return GetReadRepCountReply(out_data);
}
std::vector<u8> RingController::GetSaveDataReply() const {
u64 RingController::GetSaveDataReply(std::span<u8> out_data) const {
const StatusReply reply{
.status = DataValid::Valid,
};
return GetDataVector(reply);
return GetData(reply, out_data);
}
std::vector<u8> RingController::GetErrorReply() const {
u64 RingController::GetErrorReply(std::span<u8> out_data) const {
const ErrorReply reply{
.status = DataValid::BadCRC,
};
return GetDataVector(reply);
return GetData(reply, out_data);
}
u8 RingController::GetCrcValue(const std::vector<u8>& data) const {
@@ -281,12 +281,11 @@ u8 RingController::GetCrcValue(const std::vector<u8>& data) const {
}
template <typename T>
std::vector<u8> RingController::GetDataVector(const T& reply) const {
u64 RingController::GetData(const T& reply, std::span<u8> out_data) const {
static_assert(std::is_trivially_copyable_v<T>);
std::vector<u8> data;
data.resize(sizeof(reply));
std::memcpy(data.data(), &reply, sizeof(reply));
return data;
const auto data_size = static_cast<u64>(std::min(sizeof(reply), out_data.size()));
std::memcpy(out_data.data(), &reply, data_size);
return data_size;
}
} // namespace Service::HID

View File

@@ -34,7 +34,7 @@ public:
bool SetCommand(std::span<const u8> data) override;
// Returns a reply from a command
std::vector<u8> GetReply() const override;
u64 GetReply(std::span<u8> data) const override;
private:
// These values are obtained from a real ring controller
@@ -184,44 +184,44 @@ private:
RingConData GetSensorValue() const;
// Returns 8 byte reply with firmware version
std::vector<u8> GetFirmwareVersionReply() const;
u64 GetFirmwareVersionReply(std::span<u8> out_data) const;
// Returns 16 byte reply with ID values
std::vector<u8> GetReadIdReply() const;
u64 GetReadIdReply(std::span<u8> out_data) const;
// (STUBBED) Returns 8 byte reply
std::vector<u8> GetC020105Reply() const;
u64 GetC020105Reply(std::span<u8> out_data) const;
// (STUBBED) Returns 8 byte empty reply
std::vector<u8> GetReadUnkCalReply() const;
u64 GetReadUnkCalReply(std::span<u8> out_data) const;
// Returns 20 byte reply with factory calibration values
std::vector<u8> GetReadFactoryCalReply() const;
u64 GetReadFactoryCalReply(std::span<u8> out_data) const;
// Returns 20 byte reply with user calibration values
std::vector<u8> GetReadUserCalReply() const;
u64 GetReadUserCalReply(std::span<u8> out_data) const;
// Returns 8 byte reply
std::vector<u8> GetReadRepCountReply() const;
u64 GetReadRepCountReply(std::span<u8> out_data) const;
// Returns 8 byte reply
std::vector<u8> GetReadTotalPushCountReply() const;
u64 GetReadTotalPushCountReply(std::span<u8> out_data) const;
// Returns 8 byte reply
std::vector<u8> GetResetRepCountReply() const;
u64 GetResetRepCountReply(std::span<u8> out_data) const;
// Returns 4 byte save data reply
std::vector<u8> GetSaveDataReply() const;
u64 GetSaveDataReply(std::span<u8> out_data) const;
// Returns 8 byte error reply
std::vector<u8> GetErrorReply() const;
u64 GetErrorReply(std::span<u8> out_data) const;
// Returns 8 bit redundancy check from provided data
u8 GetCrcValue(const std::vector<u8>& data) const;
// Converts structs to an u8 vector equivalent
template <typename T>
std::vector<u8> GetDataVector(const T& reply) const;
u64 GetData(const T& reply, std::span<u8> out_data) const;
RingConCommands command{RingConCommands::Error};

View File

@@ -38,7 +38,7 @@ u8 Starlink::GetDeviceId() const {
return DEVICE_ID;
}
std::vector<u8> Starlink::GetReply() const {
u64 Starlink::GetReply(std::span<u8> out_data) const {
return {};
}

View File

@@ -31,7 +31,7 @@ public:
bool SetCommand(std::span<const u8> data) override;
// Returns a reply from a command
std::vector<u8> GetReply() const override;
u64 GetReply(std::span<u8> out_data) const override;
};
} // namespace Service::HID

View File

@@ -38,7 +38,7 @@ u8 HidbusStubbed::GetDeviceId() const {
return DEVICE_ID;
}
std::vector<u8> HidbusStubbed::GetReply() const {
u64 HidbusStubbed::GetReply(std::span<u8> out_data) const {
return {};
}

View File

@@ -31,7 +31,7 @@ public:
bool SetCommand(std::span<const u8> data) override;
// Returns a reply from a command
std::vector<u8> GetReply() const override;
u64 GetReply(std::span<u8> out_data) const override;
};
} // namespace Service::HID

View File

@@ -145,9 +145,8 @@ void ImageTransferProcessor::SetTransferMemoryAddress(Common::ProcessAddress t_m
}
Core::IrSensor::ImageTransferProcessorState ImageTransferProcessor::GetState(
std::vector<u8>& data) const {
const auto size = GetDataSize(current_config.trimming_format);
data.resize(size);
std::span<u8> data) const {
const auto size = std::min(GetDataSize(current_config.trimming_format), data.size());
system.ApplicationMemory().ReadBlock(transfer_memory, data.data(), size);
return processor_state;
}

View File

@@ -3,6 +3,8 @@
#pragma once
#include <span>
#include "common/typed_address.h"
#include "hid_core/irsensor/irs_types.h"
#include "hid_core/irsensor/processor_base.h"
@@ -39,7 +41,7 @@ public:
// Transfer memory where the image data will be stored
void SetTransferMemoryAddress(Common::ProcessAddress t_mem);
Core::IrSensor::ImageTransferProcessorState GetState(std::vector<u8>& data) const;
Core::IrSensor::ImageTransferProcessorState GetState(std::span<u8> data) const;
private:
// This is nn::irsensor::ImageTransferProcessorConfig

View File

@@ -57,7 +57,7 @@ Result NpadAbstractSixAxisHandler::UpdateSixAxisState() {
Core::HID::NpadIdType npad_id = properties_handler->GetNpadId();
for (std::size_t i = 0; i < AruidIndexMax; i++) {
auto* data = applet_resource_holder->applet_resource->GetAruidDataByIndex(i);
if (data->flag.is_assigned) {
if (data == nullptr || !data->flag.is_assigned) {
continue;
}
auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)];

View File

@@ -131,7 +131,7 @@ void NPad::ControllerUpdate(Core::HID::ControllerTriggerType type, std::size_t c
auto* data = applet_resource_holder.applet_resource->GetAruidDataByIndex(aruid_index);
if (!data->flag.is_assigned) {
if (data == nullptr || !data->flag.is_assigned) {
continue;
}
@@ -463,13 +463,13 @@ void NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
std::scoped_lock lock{*applet_resource_holder.shared_mutex};
for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; ++aruid_index) {
const auto* data = applet_resource_holder.applet_resource->GetAruidDataByIndex(aruid_index);
const auto aruid = data->aruid;
if (!data->flag.is_assigned) {
if (data == nullptr || !data->flag.is_assigned) {
continue;
}
bool is_set{};
const auto aruid = data->aruid;
npad_resource.IsSupportedNpadStyleSet(is_set, aruid);
// Wait until style is defined
if (!is_set) {

View File

@@ -28,142 +28,148 @@ void SixAxis::OnRelease() {}
void SixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
std::scoped_lock shared_lock{*shared_mutex};
const u64 aruid = applet_resource->GetActiveAruid();
auto* data = applet_resource->GetAruidData(aruid);
if (data == nullptr || !data->flag.is_assigned) {
return;
}
for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; ++aruid_index) {
const auto* data = applet_resource->GetAruidDataByIndex(aruid_index);
if (!IsControllerActivated()) {
return;
}
for (std::size_t i = 0; i < controller_data.size(); ++i) {
NpadSharedMemoryEntry& shared_memory = data->shared_memory_format->npad.npad_entry[i];
auto& controller = controller_data[i];
const auto& controller_type = controller.device->GetNpadStyleIndex();
if (controller_type == Core::HID::NpadStyleIndex::None ||
!controller.device->IsConnected()) {
if (data == nullptr || !data->flag.is_assigned) {
continue;
}
const auto& motion_state = controller.device->GetMotions();
auto& sixaxis_fullkey_state = controller.sixaxis_fullkey_state;
auto& sixaxis_handheld_state = controller.sixaxis_handheld_state;
auto& sixaxis_dual_left_state = controller.sixaxis_dual_left_state;
auto& sixaxis_dual_right_state = controller.sixaxis_dual_right_state;
auto& sixaxis_left_lifo_state = controller.sixaxis_left_lifo_state;
auto& sixaxis_right_lifo_state = controller.sixaxis_right_lifo_state;
auto& sixaxis_fullkey_lifo = shared_memory.internal_state.sixaxis_fullkey_lifo;
auto& sixaxis_handheld_lifo = shared_memory.internal_state.sixaxis_handheld_lifo;
auto& sixaxis_dual_left_lifo = shared_memory.internal_state.sixaxis_dual_left_lifo;
auto& sixaxis_dual_right_lifo = shared_memory.internal_state.sixaxis_dual_right_lifo;
auto& sixaxis_left_lifo = shared_memory.internal_state.sixaxis_left_lifo;
auto& sixaxis_right_lifo = shared_memory.internal_state.sixaxis_right_lifo;
// Clear previous state
sixaxis_fullkey_state = {};
sixaxis_handheld_state = {};
sixaxis_dual_left_state = {};
sixaxis_dual_right_state = {};
sixaxis_left_lifo_state = {};
sixaxis_right_lifo_state = {};
if (controller.sixaxis_sensor_enabled && Settings::values.motion_enabled.GetValue()) {
controller.sixaxis_at_rest = true;
for (std::size_t e = 0; e < motion_state.size(); ++e) {
controller.sixaxis_at_rest =
controller.sixaxis_at_rest && motion_state[e].is_at_rest;
}
if (!IsControllerActivated()) {
return;
}
const auto set_motion_state = [&](Core::HID::SixAxisSensorState& state,
const Core::HID::ControllerMotion& hid_state) {
using namespace std::literals::chrono_literals;
static constexpr Core::HID::SixAxisSensorState default_motion_state = {
.delta_time = std::chrono::nanoseconds(5ms).count(),
.accel = {0, 0, -1.0f},
.orientation =
{
Common::Vec3f{1.0f, 0, 0},
Common::Vec3f{0, 1.0f, 0},
Common::Vec3f{0, 0, 1.0f},
},
.attribute = {1},
for (std::size_t i = 0; i < controller_data.size(); ++i) {
NpadSharedMemoryEntry& shared_memory = data->shared_memory_format->npad.npad_entry[i];
auto& controller = controller_data[i];
const auto& controller_type = controller.device->GetNpadStyleIndex();
if (!data->flag.enable_six_axis_sensor) {
continue;
}
if (controller_type == Core::HID::NpadStyleIndex::None ||
!controller.device->IsConnected()) {
continue;
}
const auto& motion_state = controller.device->GetMotions();
auto& sixaxis_fullkey_state = controller.sixaxis_fullkey_state;
auto& sixaxis_handheld_state = controller.sixaxis_handheld_state;
auto& sixaxis_dual_left_state = controller.sixaxis_dual_left_state;
auto& sixaxis_dual_right_state = controller.sixaxis_dual_right_state;
auto& sixaxis_left_lifo_state = controller.sixaxis_left_lifo_state;
auto& sixaxis_right_lifo_state = controller.sixaxis_right_lifo_state;
auto& sixaxis_fullkey_lifo = shared_memory.internal_state.sixaxis_fullkey_lifo;
auto& sixaxis_handheld_lifo = shared_memory.internal_state.sixaxis_handheld_lifo;
auto& sixaxis_dual_left_lifo = shared_memory.internal_state.sixaxis_dual_left_lifo;
auto& sixaxis_dual_right_lifo = shared_memory.internal_state.sixaxis_dual_right_lifo;
auto& sixaxis_left_lifo = shared_memory.internal_state.sixaxis_left_lifo;
auto& sixaxis_right_lifo = shared_memory.internal_state.sixaxis_right_lifo;
// Clear previous state
sixaxis_fullkey_state = {};
sixaxis_handheld_state = {};
sixaxis_dual_left_state = {};
sixaxis_dual_right_state = {};
sixaxis_left_lifo_state = {};
sixaxis_right_lifo_state = {};
if (controller.sixaxis_sensor_enabled && Settings::values.motion_enabled.GetValue()) {
controller.sixaxis_at_rest = true;
for (std::size_t e = 0; e < motion_state.size(); ++e) {
controller.sixaxis_at_rest =
controller.sixaxis_at_rest && motion_state[e].is_at_rest;
}
}
const auto set_motion_state = [&](Core::HID::SixAxisSensorState& state,
const Core::HID::ControllerMotion& hid_state) {
using namespace std::literals::chrono_literals;
static constexpr Core::HID::SixAxisSensorState default_motion_state = {
.delta_time = std::chrono::nanoseconds(5ms).count(),
.accel = {0, 0, -1.0f},
.orientation =
{
Common::Vec3f{1.0f, 0, 0},
Common::Vec3f{0, 1.0f, 0},
Common::Vec3f{0, 0, 1.0f},
},
.attribute = {1},
};
if (!controller.sixaxis_sensor_enabled) {
state = default_motion_state;
return;
}
if (!Settings::values.motion_enabled.GetValue()) {
state = default_motion_state;
return;
}
state.attribute.is_connected.Assign(1);
state.delta_time = std::chrono::nanoseconds(5ms).count();
state.accel = hid_state.accel;
state.gyro = hid_state.gyro;
state.rotation = hid_state.rotation;
state.orientation = hid_state.orientation;
};
if (!controller.sixaxis_sensor_enabled) {
state = default_motion_state;
return;
switch (controller_type) {
case Core::HID::NpadStyleIndex::None:
ASSERT(false);
break;
case Core::HID::NpadStyleIndex::Fullkey:
set_motion_state(sixaxis_fullkey_state, motion_state[0]);
break;
case Core::HID::NpadStyleIndex::Handheld:
set_motion_state(sixaxis_handheld_state, motion_state[0]);
break;
case Core::HID::NpadStyleIndex::JoyconDual:
set_motion_state(sixaxis_dual_left_state, motion_state[0]);
set_motion_state(sixaxis_dual_right_state, motion_state[1]);
break;
case Core::HID::NpadStyleIndex::JoyconLeft:
set_motion_state(sixaxis_left_lifo_state, motion_state[0]);
break;
case Core::HID::NpadStyleIndex::JoyconRight:
set_motion_state(sixaxis_right_lifo_state, motion_state[1]);
break;
case Core::HID::NpadStyleIndex::Pokeball:
using namespace std::literals::chrono_literals;
set_motion_state(sixaxis_fullkey_state, motion_state[0]);
sixaxis_fullkey_state.delta_time = std::chrono::nanoseconds(15ms).count();
break;
default:
break;
}
if (!Settings::values.motion_enabled.GetValue()) {
state = default_motion_state;
return;
sixaxis_fullkey_state.sampling_number =
sixaxis_fullkey_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_handheld_state.sampling_number =
sixaxis_handheld_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_dual_left_state.sampling_number =
sixaxis_dual_left_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_dual_right_state.sampling_number =
sixaxis_dual_right_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_left_lifo_state.sampling_number =
sixaxis_left_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_right_lifo_state.sampling_number =
sixaxis_right_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
if (IndexToNpadIdType(i) == Core::HID::NpadIdType::Handheld) {
// This buffer only is updated on handheld on HW
sixaxis_handheld_lifo.lifo.WriteNextEntry(sixaxis_handheld_state);
} else {
// Handheld doesn't update this buffer on HW
sixaxis_fullkey_lifo.lifo.WriteNextEntry(sixaxis_fullkey_state);
}
state.attribute.is_connected.Assign(1);
state.delta_time = std::chrono::nanoseconds(5ms).count();
state.accel = hid_state.accel;
state.gyro = hid_state.gyro;
state.rotation = hid_state.rotation;
state.orientation = hid_state.orientation;
};
switch (controller_type) {
case Core::HID::NpadStyleIndex::None:
ASSERT(false);
break;
case Core::HID::NpadStyleIndex::Fullkey:
set_motion_state(sixaxis_fullkey_state, motion_state[0]);
break;
case Core::HID::NpadStyleIndex::Handheld:
set_motion_state(sixaxis_handheld_state, motion_state[0]);
break;
case Core::HID::NpadStyleIndex::JoyconDual:
set_motion_state(sixaxis_dual_left_state, motion_state[0]);
set_motion_state(sixaxis_dual_right_state, motion_state[1]);
break;
case Core::HID::NpadStyleIndex::JoyconLeft:
set_motion_state(sixaxis_left_lifo_state, motion_state[0]);
break;
case Core::HID::NpadStyleIndex::JoyconRight:
set_motion_state(sixaxis_right_lifo_state, motion_state[1]);
break;
case Core::HID::NpadStyleIndex::Pokeball:
using namespace std::literals::chrono_literals;
set_motion_state(sixaxis_fullkey_state, motion_state[0]);
sixaxis_fullkey_state.delta_time = std::chrono::nanoseconds(15ms).count();
break;
default:
break;
sixaxis_dual_left_lifo.lifo.WriteNextEntry(sixaxis_dual_left_state);
sixaxis_dual_right_lifo.lifo.WriteNextEntry(sixaxis_dual_right_state);
sixaxis_left_lifo.lifo.WriteNextEntry(sixaxis_left_lifo_state);
sixaxis_right_lifo.lifo.WriteNextEntry(sixaxis_right_lifo_state);
}
sixaxis_fullkey_state.sampling_number =
sixaxis_fullkey_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_handheld_state.sampling_number =
sixaxis_handheld_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_dual_left_state.sampling_number =
sixaxis_dual_left_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_dual_right_state.sampling_number =
sixaxis_dual_right_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_left_lifo_state.sampling_number =
sixaxis_left_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_right_lifo_state.sampling_number =
sixaxis_right_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
if (IndexToNpadIdType(i) == Core::HID::NpadIdType::Handheld) {
// This buffer only is updated on handheld on HW
sixaxis_handheld_lifo.lifo.WriteNextEntry(sixaxis_handheld_state);
} else {
// Handheld doesn't update this buffer on HW
sixaxis_fullkey_lifo.lifo.WriteNextEntry(sixaxis_fullkey_state);
}
sixaxis_dual_left_lifo.lifo.WriteNextEntry(sixaxis_dual_left_state);
sixaxis_dual_right_lifo.lifo.WriteNextEntry(sixaxis_dual_right_state);
sixaxis_left_lifo.lifo.WriteNextEntry(sixaxis_left_lifo_state);
sixaxis_right_lifo.lifo.WriteNextEntry(sixaxis_right_lifo_state);
}
}

View File

@@ -63,7 +63,7 @@ Result TouchResource::ActivateTouch(u64 aruid) {
auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index);
TouchAruidData& touch_data = aruid_data[aruid_index];
if (!applet_data->flag.is_assigned) {
if (applet_data == nullptr || !applet_data->flag.is_assigned) {
touch_data = {};
continue;
}
@@ -124,7 +124,7 @@ Result TouchResource::ActivateGesture(u64 aruid, u32 basic_gesture_id) {
auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index);
TouchAruidData& touch_data = aruid_data[aruid_index];
if (!applet_data->flag.is_assigned) {
if (applet_data == nullptr || !applet_data->flag.is_assigned) {
touch_data = {};
continue;
}
@@ -324,7 +324,7 @@ Result TouchResource::SetTouchScreenConfiguration(
const auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index);
TouchAruidData& data = aruid_data[aruid_index];
if (!applet_data->flag.is_assigned) {
if (applet_data == nullptr || !applet_data->flag.is_assigned) {
continue;
}
if (aruid != data.aruid) {
@@ -344,7 +344,7 @@ Result TouchResource::GetTouchScreenConfiguration(
const auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index);
const TouchAruidData& data = aruid_data[aruid_index];
if (!applet_data->flag.is_assigned) {
if (applet_data == nullptr || !applet_data->flag.is_assigned) {
continue;
}
if (aruid != data.aruid) {

View File

@@ -60,10 +60,11 @@ public:
Add(spv::ImageOperandsMask::ConstOffsets, offsets);
}
explicit ImageOperands(Id lod, Id ms) {
explicit ImageOperands(EmitContext& ctx, const IR::Value& offset, Id lod, Id ms) {
if (Sirit::ValidId(lod)) {
Add(spv::ImageOperandsMask::Lod, lod);
}
AddOffset(ctx, offset, ImageFetchOffsetAllowed);
if (Sirit::ValidId(ms)) {
Add(spv::ImageOperandsMask::Sample, ms);
}
@@ -311,37 +312,6 @@ Id ImageGatherSubpixelOffset(EmitContext& ctx, const IR::TextureInstInfo& info,
return coords;
}
}
void AddOffsetToCoordinates(EmitContext& ctx, const IR::TextureInstInfo& info, Id& coords,
Id offset) {
if (!Sirit::ValidId(offset)) {
return;
}
Id result_type{};
switch (info.type) {
case TextureType::Buffer:
case TextureType::Color1D:
case TextureType::ColorArray1D: {
result_type = ctx.U32[1];
break;
}
case TextureType::Color2D:
case TextureType::Color2DRect:
case TextureType::ColorArray2D: {
result_type = ctx.U32[2];
break;
}
case TextureType::Color3D: {
result_type = ctx.U32[3];
break;
}
case TextureType::ColorCube:
case TextureType::ColorArrayCube:
return;
}
coords = ctx.OpIAdd(result_type, coords, offset);
}
} // Anonymous namespace
Id EmitBindlessImageSampleImplicitLod(EmitContext&) {
@@ -524,10 +494,9 @@ Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
operands.Span());
}
Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset,
Id lod, Id ms) {
Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
const IR::Value& offset, Id lod, Id ms) {
const auto info{inst->Flags<IR::TextureInstInfo>()};
AddOffsetToCoordinates(ctx, info, coords, offset);
if (info.type == TextureType::Buffer) {
lod = Id{};
}
@@ -535,7 +504,7 @@ Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id c
// This image is multisampled, lod must be implicit
lod = Id{};
}
const ImageOperands operands(lod, ms);
const ImageOperands operands(ctx, offset, lod, ms);
return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4],
TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span());
}

View File

@@ -537,8 +537,8 @@ Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id
const IR::Value& offset, const IR::Value& offset2);
Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
const IR::Value& offset, const IR::Value& offset2, Id dref);
Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset,
Id lod, Id ms);
Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
const IR::Value& offset, Id lod, Id ms);
Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod,
const IR::Value& skip_mips);
Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords);

View File

@@ -274,7 +274,6 @@ add_library(video_core STATIC
texture_cache/image_view_info.h
texture_cache/render_targets.h
texture_cache/samples_helper.h
texture_cache/slot_vector.h
texture_cache/texture_cache.cpp
texture_cache/texture_cache.h
texture_cache/texture_cache_base.h

View File

@@ -7,6 +7,7 @@
#include <memory>
#include <numeric>
#include "common/range_sets.inc"
#include "video_core/buffer_cache/buffer_cache_base.h"
#include "video_core/guest_memory.h"
#include "video_core/host1x/gpu_device_memory_manager.h"
@@ -20,7 +21,7 @@ BufferCache<P>::BufferCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, R
: runtime{runtime_}, device_memory{device_memory_}, memory_tracker{device_memory} {
// Ensure the first slot is used for the null buffer
void(slot_buffers.insert(runtime, NullBufferParams{}));
common_ranges.clear();
gpu_modified_ranges.Clear();
inline_buffer_id = NULL_BUFFER_ID;
if (!runtime.CanReportMemoryUsage()) {
@@ -43,6 +44,9 @@ BufferCache<P>::BufferCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, R
DEFAULT_CRITICAL_MEMORY));
}
template <class P>
BufferCache<P>::~BufferCache() = default;
template <class P>
void BufferCache<P>::RunGarbageCollector() {
const bool aggressive_gc = total_used_memory >= critical_memory;
@@ -96,20 +100,17 @@ void BufferCache<P>::TickFrame() {
++frame_tick;
delayed_destruction_ring.Tick();
if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) {
for (auto& buffer : async_buffers_death_ring) {
runtime.FreeDeferredStagingBuffer(buffer);
}
async_buffers_death_ring.clear();
for (auto& buffer : async_buffers_death_ring) {
runtime.FreeDeferredStagingBuffer(buffer);
}
async_buffers_death_ring.clear();
}
template <class P>
void BufferCache<P>::WriteMemory(DAddr device_addr, u64 size) {
if (memory_tracker.IsRegionGpuModified(device_addr, size)) {
const IntervalType subtract_interval{device_addr, device_addr + size};
ClearDownload(subtract_interval);
common_ranges.subtract(subtract_interval);
ClearDownload(device_addr, size);
gpu_modified_ranges.Subtract(device_addr, size);
}
memory_tracker.MarkRegionAsCpuModified(device_addr, size);
}
@@ -174,11 +175,11 @@ void BufferCache<P>::DownloadMemory(DAddr device_addr, u64 size) {
}
template <class P>
void BufferCache<P>::ClearDownload(IntervalType subtract_interval) {
RemoveEachInOverlapCounter(async_downloads, subtract_interval, -1024);
uncommitted_ranges.subtract(subtract_interval);
for (auto& interval_set : committed_ranges) {
interval_set.subtract(subtract_interval);
void BufferCache<P>::ClearDownload(DAddr device_addr, u64 size) {
async_downloads.DeleteAll(device_addr, size);
uncommitted_gpu_modified_ranges.Subtract(device_addr, size);
for (auto& interval_set : committed_gpu_modified_ranges) {
interval_set.Subtract(device_addr, size);
}
}
@@ -195,8 +196,7 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am
return false;
}
const IntervalType subtract_interval{*cpu_dest_address, *cpu_dest_address + amount};
ClearDownload(subtract_interval);
ClearDownload(*cpu_dest_address, amount);
BufferId buffer_a;
BufferId buffer_b;
@@ -215,21 +215,20 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am
.size = amount,
}};
boost::container::small_vector<IntervalType, 4> tmp_intervals;
boost::container::small_vector<std::pair<DAddr, size_t>, 4> tmp_intervals;
auto mirror = [&](DAddr base_address, DAddr base_address_end) {
const u64 size = base_address_end - base_address;
const DAddr diff = base_address - *cpu_src_address;
const DAddr new_base_address = *cpu_dest_address + diff;
const IntervalType add_interval{new_base_address, new_base_address + size};
tmp_intervals.push_back(add_interval);
uncommitted_ranges.add(add_interval);
tmp_intervals.push_back({new_base_address, size});
uncommitted_gpu_modified_ranges.Add(new_base_address, size);
};
ForEachInRangeSet(common_ranges, *cpu_src_address, amount, mirror);
gpu_modified_ranges.ForEachInRange(*cpu_src_address, amount, mirror);
// This subtraction in this order is important for overlapping copies.
common_ranges.subtract(subtract_interval);
gpu_modified_ranges.Subtract(*cpu_dest_address, amount);
const bool has_new_downloads = tmp_intervals.size() != 0;
for (const IntervalType& add_interval : tmp_intervals) {
common_ranges.add(add_interval);
for (const auto& pair : tmp_intervals) {
gpu_modified_ranges.Add(pair.first, pair.second);
}
const auto& copy = copies[0];
src_buffer.MarkUsage(copy.src_offset, copy.size);
@@ -257,9 +256,8 @@ bool BufferCache<P>::DMAClear(GPUVAddr dst_address, u64 amount, u32 value) {
}
const size_t size = amount * sizeof(u32);
const IntervalType subtract_interval{*cpu_dst_address, *cpu_dst_address + size};
ClearDownload(subtract_interval);
common_ranges.subtract(subtract_interval);
ClearDownload(*cpu_dst_address, size);
gpu_modified_ranges.Subtract(*cpu_dst_address, size);
const BufferId buffer = FindBuffer(*cpu_dst_address, static_cast<u32>(size));
Buffer& dest_buffer = slot_buffers[buffer];
@@ -300,11 +298,11 @@ std::pair<typename P::Buffer*, u32> BufferCache<P>::ObtainCPUBuffer(
MarkWrittenBuffer(buffer_id, device_addr, size);
break;
case ObtainBufferOperation::DiscardWrite: {
DAddr device_addr_start = Common::AlignDown(device_addr, 64);
DAddr device_addr_end = Common::AlignUp(device_addr + size, 64);
IntervalType interval{device_addr_start, device_addr_end};
ClearDownload(interval);
common_ranges.subtract(interval);
const DAddr device_addr_start = Common::AlignDown(device_addr, 64);
const DAddr device_addr_end = Common::AlignUp(device_addr + size, 64);
const size_t new_size = device_addr_end - device_addr_start;
ClearDownload(device_addr_start, new_size);
gpu_modified_ranges.Subtract(device_addr_start, new_size);
break;
}
default:
@@ -504,46 +502,40 @@ void BufferCache<P>::FlushCachedWrites() {
template <class P>
bool BufferCache<P>::HasUncommittedFlushes() const noexcept {
return !uncommitted_ranges.empty() || !committed_ranges.empty();
return !uncommitted_gpu_modified_ranges.Empty() || !committed_gpu_modified_ranges.empty();
}
template <class P>
void BufferCache<P>::AccumulateFlushes() {
if (uncommitted_ranges.empty()) {
if (uncommitted_gpu_modified_ranges.Empty()) {
return;
}
committed_ranges.emplace_back(std::move(uncommitted_ranges));
committed_gpu_modified_ranges.emplace_back(std::move(uncommitted_gpu_modified_ranges));
}
template <class P>
bool BufferCache<P>::ShouldWaitAsyncFlushes() const noexcept {
if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) {
return (!async_buffers.empty() && async_buffers.front().has_value());
} else {
return false;
}
return (!async_buffers.empty() && async_buffers.front().has_value());
}
template <class P>
void BufferCache<P>::CommitAsyncFlushesHigh() {
AccumulateFlushes();
if (committed_ranges.empty()) {
if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) {
async_buffers.emplace_back(std::optional<Async_Buffer>{});
}
if (committed_gpu_modified_ranges.empty()) {
async_buffers.emplace_back(std::optional<Async_Buffer>{});
return;
}
MICROPROFILE_SCOPE(GPU_DownloadMemory);
auto it = committed_ranges.begin();
while (it != committed_ranges.end()) {
auto it = committed_gpu_modified_ranges.begin();
while (it != committed_gpu_modified_ranges.end()) {
auto& current_intervals = *it;
auto next_it = std::next(it);
while (next_it != committed_ranges.end()) {
for (auto& interval : *next_it) {
current_intervals.subtract(interval);
}
while (next_it != committed_gpu_modified_ranges.end()) {
next_it->ForEach([&current_intervals](DAddr start, DAddr end) {
current_intervals.Subtract(start, end - start);
});
next_it++;
}
it++;
@@ -552,10 +544,10 @@ void BufferCache<P>::CommitAsyncFlushesHigh() {
boost::container::small_vector<std::pair<BufferCopy, BufferId>, 16> downloads;
u64 total_size_bytes = 0;
u64 largest_copy = 0;
for (const IntervalSet& intervals : committed_ranges) {
for (auto& interval : intervals) {
const std::size_t size = interval.upper() - interval.lower();
const DAddr device_addr = interval.lower();
for (const Common::RangeSet<DAddr>& range_set : committed_gpu_modified_ranges) {
range_set.ForEach([&](DAddr interval_lower, DAddr interval_upper) {
const std::size_t size = interval_upper - interval_lower;
const DAddr device_addr = interval_lower;
ForEachBufferInRange(device_addr, size, [&](BufferId buffer_id, Buffer& buffer) {
const DAddr buffer_start = buffer.CpuAddr();
const DAddr buffer_end = buffer_start + buffer.SizeBytes();
@@ -583,77 +575,35 @@ void BufferCache<P>::CommitAsyncFlushesHigh() {
largest_copy = std::max(largest_copy, new_size);
};
ForEachInRangeSet(common_ranges, device_addr_out, range_size, add_download);
gpu_modified_ranges.ForEachInRange(device_addr_out, range_size,
add_download);
});
});
}
});
}
committed_ranges.clear();
committed_gpu_modified_ranges.clear();
if (downloads.empty()) {
if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) {
async_buffers.emplace_back(std::optional<Async_Buffer>{});
}
async_buffers.emplace_back(std::optional<Async_Buffer>{});
return;
}
if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) {
auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes, true);
boost::container::small_vector<BufferCopy, 4> normalized_copies;
IntervalSet new_async_range{};
runtime.PreCopyBarrier();
for (auto& [copy, buffer_id] : downloads) {
copy.dst_offset += download_staging.offset;
const std::array copies{copy};
BufferCopy second_copy{copy};
Buffer& buffer = slot_buffers[buffer_id];
second_copy.src_offset = static_cast<size_t>(buffer.CpuAddr()) + copy.src_offset;
DAddr orig_device_addr = static_cast<DAddr>(second_copy.src_offset);
const IntervalType base_interval{orig_device_addr, orig_device_addr + copy.size};
async_downloads += std::make_pair(base_interval, 1);
buffer.MarkUsage(copy.src_offset, copy.size);
runtime.CopyBuffer(download_staging.buffer, buffer, copies, false);
normalized_copies.push_back(second_copy);
}
runtime.PostCopyBarrier();
pending_downloads.emplace_back(std::move(normalized_copies));
async_buffers.emplace_back(download_staging);
} else {
if (!Settings::IsGPULevelHigh()) {
committed_ranges.clear();
uncommitted_ranges.clear();
} else {
if constexpr (USE_MEMORY_MAPS) {
auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes);
runtime.PreCopyBarrier();
for (auto& [copy, buffer_id] : downloads) {
// Have in mind the staging buffer offset for the copy
copy.dst_offset += download_staging.offset;
const std::array copies{copy};
Buffer& buffer = slot_buffers[buffer_id];
buffer.MarkUsage(copy.src_offset, copy.size);
runtime.CopyBuffer(download_staging.buffer, buffer, copies, false);
}
runtime.PostCopyBarrier();
runtime.Finish();
for (const auto& [copy, buffer_id] : downloads) {
const Buffer& buffer = slot_buffers[buffer_id];
const DAddr device_addr = buffer.CpuAddr() + copy.src_offset;
// Undo the modified offset
const u64 dst_offset = copy.dst_offset - download_staging.offset;
const u8* read_mapped_memory = download_staging.mapped_span.data() + dst_offset;
device_memory.WriteBlockUnsafe(device_addr, read_mapped_memory, copy.size);
}
} else {
const std::span<u8> immediate_buffer = ImmediateBuffer(largest_copy);
for (const auto& [copy, buffer_id] : downloads) {
Buffer& buffer = slot_buffers[buffer_id];
buffer.ImmediateDownload(copy.src_offset,
immediate_buffer.subspan(0, copy.size));
const DAddr device_addr = buffer.CpuAddr() + copy.src_offset;
device_memory.WriteBlockUnsafe(device_addr, immediate_buffer.data(), copy.size);
}
}
}
auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes, true);
boost::container::small_vector<BufferCopy, 4> normalized_copies;
runtime.PreCopyBarrier();
for (auto& [copy, buffer_id] : downloads) {
copy.dst_offset += download_staging.offset;
const std::array copies{copy};
BufferCopy second_copy{copy};
Buffer& buffer = slot_buffers[buffer_id];
second_copy.src_offset = static_cast<size_t>(buffer.CpuAddr()) + copy.src_offset;
const DAddr orig_device_addr = static_cast<DAddr>(second_copy.src_offset);
async_downloads.Add(orig_device_addr, copy.size);
buffer.MarkUsage(copy.src_offset, copy.size);
runtime.CopyBuffer(download_staging.buffer, buffer, copies, false);
normalized_copies.push_back(second_copy);
}
runtime.PostCopyBarrier();
pending_downloads.emplace_back(std::move(normalized_copies));
async_buffers.emplace_back(download_staging);
}
template <class P>
@@ -676,37 +626,31 @@ void BufferCache<P>::PopAsyncBuffers() {
async_buffers.pop_front();
return;
}
if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) {
auto& downloads = pending_downloads.front();
auto& async_buffer = async_buffers.front();
u8* base = async_buffer->mapped_span.data();
const size_t base_offset = async_buffer->offset;
for (const auto& copy : downloads) {
const DAddr device_addr = static_cast<DAddr>(copy.src_offset);
const u64 dst_offset = copy.dst_offset - base_offset;
const u8* read_mapped_memory = base + dst_offset;
ForEachInOverlapCounter(
async_downloads, device_addr, copy.size, [&](DAddr start, DAddr end, int count) {
device_memory.WriteBlockUnsafe(start, &read_mapped_memory[start - device_addr],
end - start);
if (count == 1) {
const IntervalType base_interval{start, end};
common_ranges.subtract(base_interval);
}
});
const IntervalType subtract_interval{device_addr, device_addr + copy.size};
RemoveEachInOverlapCounter(async_downloads, subtract_interval, -1);
}
async_buffers_death_ring.emplace_back(*async_buffer);
async_buffers.pop_front();
pending_downloads.pop_front();
auto& downloads = pending_downloads.front();
auto& async_buffer = async_buffers.front();
u8* base = async_buffer->mapped_span.data();
const size_t base_offset = async_buffer->offset;
for (const auto& copy : downloads) {
const DAddr device_addr = static_cast<DAddr>(copy.src_offset);
const u64 dst_offset = copy.dst_offset - base_offset;
const u8* read_mapped_memory = base + dst_offset;
async_downloads.ForEachInRange(device_addr, copy.size, [&](DAddr start, DAddr end, s32) {
device_memory.WriteBlockUnsafe(start, &read_mapped_memory[start - device_addr],
end - start);
});
async_downloads.Subtract(device_addr, copy.size, [&](DAddr start, DAddr end) {
gpu_modified_ranges.Subtract(start, end - start);
});
}
async_buffers_death_ring.emplace_back(*async_buffer);
async_buffers.pop_front();
pending_downloads.pop_front();
}
template <class P>
bool BufferCache<P>::IsRegionGpuModified(DAddr addr, size_t size) {
bool is_dirty = false;
ForEachInRangeSet(common_ranges, addr, size, [&](DAddr, DAddr) { is_dirty = true; });
gpu_modified_ranges.ForEachInRange(addr, size, [&](DAddr, DAddr) { is_dirty = true; });
return is_dirty;
}
@@ -1320,10 +1264,8 @@ void BufferCache<P>::UpdateComputeTextureBuffers() {
template <class P>
void BufferCache<P>::MarkWrittenBuffer(BufferId buffer_id, DAddr device_addr, u32 size) {
memory_tracker.MarkRegionAsGpuModified(device_addr, size);
const IntervalType base_interval{device_addr, device_addr + size};
common_ranges.add(base_interval);
uncommitted_ranges.add(base_interval);
gpu_modified_ranges.Add(device_addr, size);
uncommitted_gpu_modified_ranges.Add(device_addr, size);
}
template <class P>
@@ -1600,9 +1542,8 @@ bool BufferCache<P>::InlineMemory(DAddr dest_address, size_t copy_size,
template <class P>
void BufferCache<P>::InlineMemoryImplementation(DAddr dest_address, size_t copy_size,
std::span<const u8> inlined_buffer) {
const IntervalType subtract_interval{dest_address, dest_address + copy_size};
ClearDownload(subtract_interval);
common_ranges.subtract(subtract_interval);
ClearDownload(dest_address, copy_size);
gpu_modified_ranges.Subtract(dest_address, copy_size);
BufferId buffer_id = FindBuffer(dest_address, static_cast<u32>(copy_size));
auto& buffer = slot_buffers[buffer_id];
@@ -1652,12 +1593,9 @@ void BufferCache<P>::DownloadBufferMemory(Buffer& buffer, DAddr device_addr, u64
largest_copy = std::max(largest_copy, new_size);
};
const DAddr start_address = device_addr_out;
const DAddr end_address = start_address + range_size;
ForEachInRangeSet(common_ranges, start_address, range_size, add_download);
const IntervalType subtract_interval{start_address, end_address};
ClearDownload(subtract_interval);
common_ranges.subtract(subtract_interval);
gpu_modified_ranges.ForEachInRange(device_addr_out, range_size, add_download);
ClearDownload(device_addr_out, range_size);
gpu_modified_ranges.Subtract(device_addr_out, range_size);
});
if (total_size_bytes == 0) {
return;

View File

@@ -13,25 +13,15 @@
#include <unordered_map>
#include <vector>
#include <boost/container/small_vector.hpp>
#define BOOST_NO_MT
#include <boost/pool/detail/mutex.hpp>
#undef BOOST_NO_MT
#include <boost/icl/interval.hpp>
#include <boost/icl/interval_base_set.hpp>
#include <boost/icl/interval_set.hpp>
#include <boost/icl/split_interval_map.hpp>
#include <boost/pool/pool.hpp>
#include <boost/pool/pool_alloc.hpp>
#include <boost/pool/poolfwd.hpp>
#include "common/common_types.h"
#include "common/div_ceil.h"
#include "common/literals.h"
#include "common/lru_cache.h"
#include "common/microprofile.h"
#include "common/range_sets.h"
#include "common/scope_exit.h"
#include "common/settings.h"
#include "common/slot_vector.h"
#include "video_core/buffer_cache/buffer_base.h"
#include "video_core/control/channel_state_cache.h"
#include "video_core/delayed_destruction_ring.h"
@@ -41,21 +31,15 @@
#include "video_core/engines/maxwell_3d.h"
#include "video_core/memory_manager.h"
#include "video_core/surface.h"
#include "video_core/texture_cache/slot_vector.h"
#include "video_core/texture_cache/types.h"
namespace boost {
template <typename T>
class fast_pool_allocator<T, default_user_allocator_new_delete, details::pool::null_mutex, 4096, 0>;
}
namespace VideoCommon {
MICROPROFILE_DECLARE(GPU_PrepareBuffers);
MICROPROFILE_DECLARE(GPU_BindUploadBuffers);
MICROPROFILE_DECLARE(GPU_DownloadMemory);
using BufferId = SlotId;
using BufferId = Common::SlotId;
using VideoCore::Surface::PixelFormat;
using namespace Common::Literals;
@@ -184,7 +168,6 @@ class BufferCache : public VideoCommon::ChannelSetupCaches<BufferCacheChannelInf
static constexpr bool NEEDS_BIND_STORAGE_INDEX = P::NEEDS_BIND_STORAGE_INDEX;
static constexpr bool USE_MEMORY_MAPS = P::USE_MEMORY_MAPS;
static constexpr bool SEPARATE_IMAGE_BUFFERS_BINDINGS = P::SEPARATE_IMAGE_BUFFER_BINDINGS;
static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = P::IMPLEMENTS_ASYNC_DOWNLOADS;
static constexpr bool USE_MEMORY_MAPS_FOR_UPLOADS = P::USE_MEMORY_MAPS_FOR_UPLOADS;
static constexpr s64 DEFAULT_EXPECTED_MEMORY = 512_MiB;
@@ -202,34 +185,6 @@ class BufferCache : public VideoCommon::ChannelSetupCaches<BufferCacheChannelInf
using Async_Buffer = typename P::Async_Buffer;
using MemoryTracker = typename P::MemoryTracker;
using IntervalCompare = std::less<DAddr>;
using IntervalInstance = boost::icl::interval_type_default<DAddr, std::less>;
using IntervalAllocator = boost::fast_pool_allocator<DAddr>;
using IntervalSet = boost::icl::interval_set<DAddr>;
using IntervalType = typename IntervalSet::interval_type;
template <typename Type>
struct counter_add_functor : public boost::icl::identity_based_inplace_combine<Type> {
// types
typedef counter_add_functor<Type> type;
typedef boost::icl::identity_based_inplace_combine<Type> base_type;
// public member functions
void operator()(Type& current, const Type& added) const {
current += added;
if (current < base_type::identity_element()) {
current = base_type::identity_element();
}
}
// public static functions
static void version(Type&){};
};
using OverlapCombine = counter_add_functor<int>;
using OverlapSection = boost::icl::inter_section<int>;
using OverlapCounter = boost::icl::split_interval_map<DAddr, int>;
struct OverlapResult {
boost::container::small_vector<BufferId, 16> ids;
DAddr begin;
@@ -240,6 +195,8 @@ class BufferCache : public VideoCommon::ChannelSetupCaches<BufferCacheChannelInf
public:
explicit BufferCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, Runtime& runtime_);
~BufferCache();
void TickFrame();
void WriteMemory(DAddr device_addr, u64 size);
@@ -379,75 +336,6 @@ private:
}
}
template <typename Func>
void ForEachInRangeSet(IntervalSet& current_range, DAddr device_addr, u64 size, Func&& func) {
const DAddr start_address = device_addr;
const DAddr end_address = start_address + size;
const IntervalType search_interval{start_address, end_address};
auto it = current_range.lower_bound(search_interval);
if (it == current_range.end()) {
return;
}
auto end_it = current_range.upper_bound(search_interval);
for (; it != end_it; it++) {
DAddr inter_addr_end = it->upper();
DAddr inter_addr = it->lower();
if (inter_addr_end > end_address) {
inter_addr_end = end_address;
}
if (inter_addr < start_address) {
inter_addr = start_address;
}
func(inter_addr, inter_addr_end);
}
}
template <typename Func>
void ForEachInOverlapCounter(OverlapCounter& current_range, DAddr device_addr, u64 size,
Func&& func) {
const DAddr start_address = device_addr;
const DAddr end_address = start_address + size;
const IntervalType search_interval{start_address, end_address};
auto it = current_range.lower_bound(search_interval);
if (it == current_range.end()) {
return;
}
auto end_it = current_range.upper_bound(search_interval);
for (; it != end_it; it++) {
auto& inter = it->first;
DAddr inter_addr_end = inter.upper();
DAddr inter_addr = inter.lower();
if (inter_addr_end > end_address) {
inter_addr_end = end_address;
}
if (inter_addr < start_address) {
inter_addr = start_address;
}
func(inter_addr, inter_addr_end, it->second);
}
}
void RemoveEachInOverlapCounter(OverlapCounter& current_range,
const IntervalType search_interval, int subtract_value) {
bool any_removals = false;
current_range.add(std::make_pair(search_interval, subtract_value));
do {
any_removals = false;
auto it = current_range.lower_bound(search_interval);
if (it == current_range.end()) {
return;
}
auto end_it = current_range.upper_bound(search_interval);
for (; it != end_it; it++) {
if (it->second <= 0) {
any_removals = true;
current_range.erase(it);
break;
}
}
} while (any_removals);
}
static bool IsRangeGranular(DAddr device_addr, size_t size) {
return (device_addr & ~Core::DEVICE_PAGEMASK) ==
((device_addr + size) & ~Core::DEVICE_PAGEMASK);
@@ -552,14 +440,14 @@ private:
[[nodiscard]] bool HasFastUniformBufferBound(size_t stage, u32 binding_index) const noexcept;
void ClearDownload(IntervalType subtract_interval);
void ClearDownload(DAddr base_addr, u64 size);
void InlineMemoryImplementation(DAddr dest_address, size_t copy_size,
std::span<const u8> inlined_buffer);
Tegra::MaxwellDeviceMemoryManager& device_memory;
SlotVector<Buffer> slot_buffers;
Common::SlotVector<Buffer> slot_buffers;
DelayedDestructionRing<Buffer, 8> delayed_destruction_ring;
const Tegra::Engines::DrawManager::IndirectParams* current_draw_indirect{};
@@ -567,13 +455,12 @@ private:
u32 last_index_count = 0;
MemoryTracker memory_tracker;
IntervalSet uncommitted_ranges;
IntervalSet common_ranges;
IntervalSet cached_ranges;
std::deque<IntervalSet> committed_ranges;
Common::RangeSet<DAddr> uncommitted_gpu_modified_ranges;
Common::RangeSet<DAddr> gpu_modified_ranges;
std::deque<Common::RangeSet<DAddr>> committed_gpu_modified_ranges;
// Async Buffers
OverlapCounter async_downloads;
Common::OverlapRangeSet<DAddr> async_downloads;
std::deque<std::optional<Async_Buffer>> async_buffers;
std::deque<boost::container::small_vector<BufferCopy, 4>> pending_downloads;
std::optional<Async_Buffer> current_buffer;

View File

@@ -10,7 +10,7 @@ namespace Host1x {
Host1x::Host1x(Core::System& system_)
: system{system_}, syncpoint_manager{},
memory_manager(system.DeviceMemory()), gmmu_manager{system, memory_manager, 32, 12},
memory_manager(system.DeviceMemory()), gmmu_manager{system, memory_manager, 32, 0, 12},
allocator{std::make_unique<Common::FlatAllocator<u32, 0, 32>>(1 << 12)} {}
Host1x::~Host1x() = default;

View File

@@ -22,11 +22,12 @@ using Tegra::Memory::GuestMemoryFlags;
std::atomic<size_t> MemoryManager::unique_identifier_generator{};
MemoryManager::MemoryManager(Core::System& system_, MaxwellDeviceMemoryManager& memory_,
u64 address_space_bits_, u64 big_page_bits_, u64 page_bits_)
u64 address_space_bits_, GPUVAddr split_address_, u64 big_page_bits_,
u64 page_bits_)
: system{system_}, memory{memory_}, address_space_bits{address_space_bits_},
page_bits{page_bits_}, big_page_bits{big_page_bits_}, entries{}, big_entries{},
page_table{address_space_bits, address_space_bits + page_bits - 38,
page_bits != big_page_bits ? page_bits : 0},
split_address{split_address_}, page_bits{page_bits_}, big_page_bits{big_page_bits_},
entries{}, big_entries{}, page_table{address_space_bits, address_space_bits + page_bits - 38,
page_bits != big_page_bits ? page_bits : 0},
kind_map{PTEKind::INVALID}, unique_identifier{unique_identifier_generator.fetch_add(
1, std::memory_order_acq_rel)},
accumulator{std::make_unique<VideoCommon::InvalidationAccumulator>()} {
@@ -48,10 +49,10 @@ MemoryManager::MemoryManager(Core::System& system_, MaxwellDeviceMemoryManager&
entries.resize(page_table_size / 32, 0);
}
MemoryManager::MemoryManager(Core::System& system_, u64 address_space_bits_, u64 big_page_bits_,
u64 page_bits_)
: MemoryManager(system_, system_.Host1x().MemoryManager(), address_space_bits_, big_page_bits_,
page_bits_) {}
MemoryManager::MemoryManager(Core::System& system_, u64 address_space_bits_,
GPUVAddr split_address_, u64 big_page_bits_, u64 page_bits_)
: MemoryManager(system_, system_.Host1x().MemoryManager(), address_space_bits_, split_address_,
big_page_bits_, page_bits_) {}
MemoryManager::~MemoryManager() = default;

View File

@@ -36,10 +36,11 @@ namespace Tegra {
class MemoryManager final {
public:
explicit MemoryManager(Core::System& system_, u64 address_space_bits_ = 40,
u64 big_page_bits_ = 16, u64 page_bits_ = 12);
explicit MemoryManager(Core::System& system_, MaxwellDeviceMemoryManager& memory_,
u64 address_space_bits_ = 40, u64 big_page_bits_ = 16,
GPUVAddr split_address = 1ULL << 34, u64 big_page_bits_ = 16,
u64 page_bits_ = 12);
explicit MemoryManager(Core::System& system_, MaxwellDeviceMemoryManager& memory_,
u64 address_space_bits_ = 40, GPUVAddr split_address = 1ULL << 34,
u64 big_page_bits_ = 16, u64 page_bits_ = 12);
~MemoryManager();
size_t GetID() const {
@@ -192,6 +193,7 @@ private:
MaxwellDeviceMemoryManager& memory;
const u64 address_space_bits;
GPUVAddr split_address;
const u64 page_bits;
u64 address_space_size;
u64 page_size;

View File

@@ -18,12 +18,12 @@
#include "common/assert.h"
#include "common/settings.h"
#include "common/slot_vector.h"
#include "video_core/control/channel_state_cache.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/host1x/gpu_device_memory_manager.h"
#include "video_core/memory_manager.h"
#include "video_core/rasterizer_interface.h"
#include "video_core/texture_cache/slot_vector.h"
namespace VideoCore {
enum class QueryType {
@@ -37,7 +37,7 @@ constexpr std::size_t NumQueryTypes = static_cast<size_t>(QueryType::Count);
namespace VideoCommon {
using AsyncJobId = SlotId;
using AsyncJobId = Common::SlotId;
static constexpr AsyncJobId NULL_ASYNC_JOB_ID{0};
@@ -341,7 +341,7 @@ private:
static constexpr std::uintptr_t YUZU_PAGESIZE = 4096;
static constexpr unsigned YUZU_PAGEBITS = 12;
SlotVector<AsyncJob> slot_async_jobs;
Common::SlotVector<AsyncJob> slot_async_jobs;
VideoCore::RasterizerInterface& rasterizer;
Tegra::MaxwellDeviceMemoryManager& device_memory;

View File

@@ -90,7 +90,7 @@ public:
void PostCopyBarrier();
void Finish();
void TickFrame(VideoCommon::SlotVector<Buffer>&) noexcept {}
void TickFrame(Common::SlotVector<Buffer>&) noexcept {}
void ClearBuffer(Buffer& dest_buffer, u32 offset, size_t size, u32 value);
@@ -251,7 +251,6 @@ struct BufferCacheParams {
static constexpr bool NEEDS_BIND_STORAGE_INDEX = true;
static constexpr bool USE_MEMORY_MAPS = true;
static constexpr bool SEPARATE_IMAGE_BUFFER_BINDINGS = true;
static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = true;
// TODO: Investigate why OpenGL seems to perform worse with persistently mapped buffer uploads
static constexpr bool USE_MEMORY_MAPS_FOR_UPLOADS = false;

View File

@@ -30,13 +30,13 @@ class Image;
class ImageView;
class Sampler;
using Common::SlotVector;
using VideoCommon::ImageId;
using VideoCommon::ImageViewId;
using VideoCommon::ImageViewType;
using VideoCommon::NUM_RT;
using VideoCommon::Region2D;
using VideoCommon::RenderTargets;
using VideoCommon::SlotVector;
struct FormatProperties {
GLenum compatibility_class;

View File

@@ -368,7 +368,7 @@ u32 BufferCacheRuntime::GetStorageBufferAlignment() const {
return static_cast<u32>(device.GetStorageBufferAlignment());
}
void BufferCacheRuntime::TickFrame(VideoCommon::SlotVector<Buffer>& slot_buffers) noexcept {
void BufferCacheRuntime::TickFrame(Common::SlotVector<Buffer>& slot_buffers) noexcept {
for (auto it = slot_buffers.begin(); it != slot_buffers.end(); it++) {
it->ResetUsageTracking();
}

View File

@@ -81,7 +81,7 @@ public:
ComputePassDescriptorQueue& compute_pass_descriptor_queue,
DescriptorPool& descriptor_pool);
void TickFrame(VideoCommon::SlotVector<Buffer>& slot_buffers) noexcept;
void TickFrame(Common::SlotVector<Buffer>& slot_buffers) noexcept;
void Finish();
@@ -181,7 +181,6 @@ struct BufferCacheParams {
static constexpr bool NEEDS_BIND_STORAGE_INDEX = false;
static constexpr bool USE_MEMORY_MAPS = true;
static constexpr bool SEPARATE_IMAGE_BUFFER_BINDINGS = false;
static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = true;
static constexpr bool USE_MEMORY_MAPS_FOR_UPLOADS = true;
};

View File

@@ -20,11 +20,11 @@ struct ResolutionScalingInfo;
namespace Vulkan {
using Common::SlotVector;
using VideoCommon::ImageId;
using VideoCommon::NUM_RT;
using VideoCommon::Region2D;
using VideoCommon::RenderTargets;
using VideoCommon::SlotVector;
using VideoCore::Surface::PixelFormat;
class BlitImageHelper;

View File

@@ -21,6 +21,7 @@
#include "common/lru_cache.h"
#include "common/polyfill_ranges.h"
#include "common/scratch_buffer.h"
#include "common/slot_vector.h"
#include "common/thread_worker.h"
#include "video_core/compatible_formats.h"
#include "video_core/control/channel_state_cache.h"
@@ -32,7 +33,6 @@
#include "video_core/texture_cache/image_info.h"
#include "video_core/texture_cache/image_view_base.h"
#include "video_core/texture_cache/render_targets.h"
#include "video_core/texture_cache/slot_vector.h"
#include "video_core/texture_cache/types.h"
#include "video_core/textures/texture.h"
@@ -451,16 +451,16 @@ private:
struct PendingDownload {
bool is_swizzle;
size_t async_buffer_id;
SlotId object_id;
Common::SlotId object_id;
};
SlotVector<Image> slot_images;
SlotVector<ImageMapView> slot_map_views;
SlotVector<ImageView> slot_image_views;
SlotVector<ImageAlloc> slot_image_allocs;
SlotVector<Sampler> slot_samplers;
SlotVector<Framebuffer> slot_framebuffers;
SlotVector<BufferDownload> slot_buffer_downloads;
Common::SlotVector<Image> slot_images;
Common::SlotVector<ImageMapView> slot_map_views;
Common::SlotVector<ImageView> slot_image_views;
Common::SlotVector<ImageAlloc> slot_image_allocs;
Common::SlotVector<Sampler> slot_samplers;
Common::SlotVector<Framebuffer> slot_framebuffers;
Common::SlotVector<BufferDownload> slot_buffer_downloads;
// TODO: This data structure is not optimal and it should be reworked

View File

@@ -5,21 +5,21 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "video_core/texture_cache/slot_vector.h"
#include "common/slot_vector.h"
namespace VideoCommon {
constexpr size_t NUM_RT = 8;
constexpr size_t MAX_MIP_LEVELS = 14;
constexpr SlotId CORRUPT_ID{0xfffffffe};
constexpr Common::SlotId CORRUPT_ID{0xfffffffe};
using ImageId = SlotId;
using ImageMapId = SlotId;
using ImageViewId = SlotId;
using ImageAllocId = SlotId;
using SamplerId = SlotId;
using FramebufferId = SlotId;
using ImageId = Common::SlotId;
using ImageMapId = Common::SlotId;
using ImageViewId = Common::SlotId;
using ImageAllocId = Common::SlotId;
using SamplerId = Common::SlotId;
using FramebufferId = Common::SlotId;
/// Fake image ID for null image views
constexpr ImageId NULL_IMAGE_ID{0};

View File

@@ -32,9 +32,14 @@ struct Client::Impl {
Impl(std::string host_, std::string username_, std::string token_)
: host{std::move(host_)}, username{std::move(username_)}, token{std::move(token_)} {
std::scoped_lock lock{jwt_cache.mutex};
if (username == jwt_cache.username && token == jwt_cache.token) {
if (this->username == jwt_cache.username && this->token == jwt_cache.token) {
jwt = jwt_cache.jwt;
}
// Normalize host expression
if (!this->host.empty() && this->host.back() == '/') {
static_cast<void>(this->host.pop_back());
}
}
/// A generic function handles POST, GET and DELETE request together
@@ -71,18 +76,16 @@ struct Client::Impl {
const std::string& jwt_ = "", const std::string& username_ = "",
const std::string& token_ = "") {
if (cli == nullptr) {
cli = std::make_unique<httplib::Client>(host);
cli = std::make_unique<httplib::Client>(host.c_str());
cli->set_connection_timeout(TIMEOUT_SECONDS);
cli->set_read_timeout(TIMEOUT_SECONDS);
cli->set_write_timeout(TIMEOUT_SECONDS);
}
if (!cli->is_valid()) {
LOG_ERROR(WebService, "Client is invalid, skipping request!");
return {};
LOG_ERROR(WebService, "Invalid URL {}", host + path);
return WebResult{WebResult::Code::InvalidURL, "Invalid URL", ""};
}
cli->set_connection_timeout(TIMEOUT_SECONDS);
cli->set_read_timeout(TIMEOUT_SECONDS);
cli->set_write_timeout(TIMEOUT_SECONDS);
httplib::Headers params;
if (!jwt_.empty()) {
params = {
@@ -107,15 +110,15 @@ struct Client::Impl {
request.headers = params;
request.body = data;
httplib::Response response;
httplib::Error error;
httplib::Result result = cli->send(request);
if (!cli->send(request, response, error)) {
LOG_ERROR(WebService, "{} to {} returned null (httplib Error: {})", method, host + path,
httplib::to_string(error));
if (!result) {
LOG_ERROR(WebService, "{} to {} returned null", method, host + path);
return WebResult{WebResult::Code::LibError, "Null response", ""};
}
httplib::Response response = result.value();
if (response.status >= 400) {
LOG_ERROR(WebService, "{} to {} returned error status code: {}", method, host + path,
response.status);

View File

@@ -45,15 +45,23 @@ ConfigureHotkeys::ConfigureHotkeys(Core::HID::HIDCore& hid_core, QWidget* parent
controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); });
connect(timeout_timer.get(), &QTimer::timeout, [this] {
const bool is_button_pressed = pressed_buttons != Core::HID::NpadButton::None ||
pressed_home_button || pressed_capture_button;
SetPollingResult(!is_button_pressed);
});
connect(poll_timer.get(), &QTimer::timeout, [this] {
const auto buttons = controller->GetNpadButtons();
const auto home_pressed = controller->GetHomeButtons().home != 0;
const auto capture_pressed = controller->GetCaptureButtons().capture != 0;
if (home_pressed || capture_pressed) {
SetPollingResult(buttons.raw, false);
return;
pressed_buttons |= controller->GetNpadButtons().raw;
pressed_home_button |= this->controller->GetHomeButtons().home != 0;
pressed_capture_button |= this->controller->GetCaptureButtons().capture != 0;
if (pressed_buttons != Core::HID::NpadButton::None || pressed_home_button ||
pressed_capture_button) {
const QString button_name =
GetButtonCombinationName(pressed_buttons, pressed_home_button,
pressed_capture_button) +
QStringLiteral("...");
model->setData(button_model_index, button_name);
}
});
RetranslateUI();
@@ -154,16 +162,14 @@ void ConfigureHotkeys::ConfigureController(QModelIndex index) {
const auto previous_key = model->data(index);
input_setter = [this, index, previous_key](const Core::HID::NpadButton button,
const bool cancel) {
input_setter = [this, index, previous_key](const bool cancel) {
if (cancel) {
model->setData(index, previous_key);
return;
}
const auto home_pressed = this->controller->GetHomeButtons().home != 0;
const auto capture_pressed = this->controller->GetCaptureButtons().capture != 0;
const QString button_string =
GetButtonCombinationName(button, home_pressed, capture_pressed);
GetButtonCombinationName(pressed_buttons, pressed_home_button, pressed_capture_button);
const auto [key_sequence_used, used_action] = IsUsedControllerKey(button_string);
@@ -177,17 +183,22 @@ void ConfigureHotkeys::ConfigureController(QModelIndex index) {
}
};
button_model_index = index;
pressed_buttons = Core::HID::NpadButton::None;
pressed_home_button = false;
pressed_capture_button = false;
model->setData(index, tr("[waiting]"));
timeout_timer->start(2500); // Cancel after 2.5 seconds
poll_timer->start(200); // Check for new inputs every 200ms
poll_timer->start(100); // Check for new inputs every 100ms
// We need to disable configuration to be able to read npad buttons
controller->DisableConfiguration();
}
void ConfigureHotkeys::SetPollingResult(Core::HID::NpadButton button, const bool cancel) {
void ConfigureHotkeys::SetPollingResult(const bool cancel) {
timeout_timer->stop();
poll_timer->stop();
(*input_setter)(button, cancel);
(*input_setter)(cancel);
// Re-Enable configuration
controller->EnableConfiguration();

View File

@@ -4,6 +4,7 @@
#pragma once
#include <memory>
#include <QStandardItemModel>
#include <QWidget>
namespace Common {
@@ -54,14 +55,20 @@ private:
void RestoreControllerHotkey(QModelIndex index);
void RestoreHotkey(QModelIndex index);
void SetPollingResult(bool cancel);
QString GetButtonCombinationName(Core::HID::NpadButton button, bool home, bool capture) const;
std::unique_ptr<Ui::ConfigureHotkeys> ui;
QStandardItemModel* model;
void SetPollingResult(Core::HID::NpadButton button, bool cancel);
QString GetButtonCombinationName(Core::HID::NpadButton button, bool home, bool capture) const;
bool pressed_home_button;
bool pressed_capture_button;
QModelIndex button_model_index;
Core::HID::NpadButton pressed_buttons;
Core::HID::EmulatedController* controller;
std::unique_ptr<QTimer> timeout_timer;
std::unique_ptr<QTimer> poll_timer;
std::optional<std::function<void(Core::HID::NpadButton, bool)>> input_setter;
std::optional<std::function<void(bool)>> input_setter;
};

View File

@@ -248,82 +248,22 @@ void ConfigureUi::RetranslateUI() {
}
void ConfigureUi::InitializeLanguageComboBox() {
// This is a list of lexicographically sorted languages, only the available translations are
// shown to the user.
static const struct {
const QString name;
const char* id;
} languages[] = {
// clang-format off
{QStringLiteral(u"Bahasa Indonesia"), "id"}, // Indonesian
{QStringLiteral(u"Bahasa Melayu"), "ms"}, // Malay
{QStringLiteral(u"Catal\u00E0"), "ca"}, // Catalan
{QStringLiteral(u"\u010Ce\u0161tina"), "cs"}, // Czech
{QStringLiteral(u"Dansk"), "da"}, // Danish
{QStringLiteral(u"Deutsch"), "de"}, // German
{QStringLiteral(u"English"), "en"}, // English
{QStringLiteral(u"Espa\u00F1ol"), "es"}, // Spanish
{QStringLiteral(u"Fran\u00E7ais"), "fr"}, // French
{QStringLiteral(u"Hrvatski"), "hr"}, // Croatian
{QStringLiteral(u"Italiano"), "it"}, // Italian
{QStringLiteral(u"Magyar"), "hu"}, // Hungarian
{QStringLiteral(u"Nederlands"), "nl"}, // Dutch
{QStringLiteral(u"Norsk bokm\u00E5l"), "nb"}, // Norwegian
{QStringLiteral(u"Polski"), "pl"}, // Polish
{QStringLiteral(u"Portugu\u00EAs"), "pt_PT"}, // Portuguese
{QStringLiteral(u"Portugu\u00EAs (Brasil)"), "pt_BR"}, // Portuguese (Brazil)
{QStringLiteral(u"Rom\u00E2n\u0103"), "ro"}, // Romanian
{QStringLiteral(u"Srpski"), "sr"}, // Serbian
{QStringLiteral(u"Suomi"), "fi"}, // Finnish
{QStringLiteral(u"Svenska"), "sv"}, // Swedish
{QStringLiteral(u"Ti\u1EBFng Vi\u1EC7t"), "vi"}, // Vietnamese
{QStringLiteral(u"Ti\u1EBFng Vi\u1EC7t (Vi\u1EC7t Nam)"), "vi_VN"}, // Vietnamese
{QStringLiteral(u"T\u00FCrk\u00E7e"), "tr_TR"}, // Turkish
{QStringLiteral(u"\u0395\u03BB\u03BB\u03B7\u03BD\u03B9\u03BA\u03AC"), "el"}, // Greek
{QStringLiteral(u"\u0420\u0443\u0441\u0441\u043A\u0438\u0439"), "ru_RU"}, // Russian
{QStringLiteral(u"\u0423\u043A\u0440\u0430\u0457\u043D\u0441\u044C\u043A\u0430"),
"uk"}, // Ukrainian
{QStringLiteral(u"\u0627\u0644\u0639\u0631\u0628\u064A\u0629"), "ar"}, // Arabic
{QStringLiteral(u"\u0641\u0627\u0631\u0633\u06CC"), "fa"}, // Farsi
{QStringLiteral(u"\uD55C\uAD6D\uC5B4"), "ko_KR"}, // Korean
{QStringLiteral(u"\u65E5\u672C\u8A9E"), "ja_JP"}, // Japanese
{QStringLiteral(u"\u7B80\u4F53\u4E2D\u6587"), "zh_CN"}, // Simplified Chinese
{QStringLiteral(u"\u7E41\u9AD4\u4E2D\u6587"), "zh_TW"}, // Traditional Chinese
// clang-format on
};
ui->language_combobox->addItem(tr("<System>"), QString{});
QDir languages_dir{QStringLiteral(":/languages")};
QStringList language_files = languages_dir.entryList();
for (const auto& lang : languages) {
if (QString::fromLatin1(lang.id) == QStringLiteral("en")) {
ui->language_combobox->addItem(lang.name, QStringLiteral("en"));
language_files.removeOne(QStringLiteral("en.qm"));
continue;
}
for (int i = 0; i < language_files.size(); ++i) {
QString locale = language_files[i];
locale.truncate(locale.lastIndexOf(QLatin1Char{'.'}));
if (QString::fromLatin1(lang.id) == locale) {
ui->language_combobox->addItem(lang.name, locale);
language_files.removeAt(i);
break;
}
}
}
// Anything remaining will be at the bottom
for (const QString& file : language_files) {
LOG_CRITICAL(Frontend, "Unexpected Language File: {}", file.toStdString());
QString locale = file;
ui->language_combobox->addItem(tr("English"), QStringLiteral("en"));
QDirIterator it(QStringLiteral(":/languages"), QDirIterator::NoIteratorFlags);
while (it.hasNext()) {
QString locale = it.next();
locale.truncate(locale.lastIndexOf(QLatin1Char{'.'}));
const QString language_name = QLocale::languageToString(QLocale(locale).language());
const QString lang = QStringLiteral("%1 [%2]").arg(language_name, locale);
ui->language_combobox->addItem(lang, locale);
locale.remove(0, locale.lastIndexOf(QLatin1Char{'/'}) + 1);
const QString lang = QLocale::languageToString(QLocale(locale).language());
const QString country = QLocale::countryToString(QLocale(locale).country());
ui->language_combobox->addItem(QStringLiteral("%1 (%2)").arg(lang, country), locale);
}
// Unlike other configuration changes, interface language changes need to be reflected on the
// interface immediately. This is done by passing a signal to the main window, and then
// retranslating when passing back.
connect(ui->language_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
connect(ui->language_combobox, qOverload<int>(&QComboBox::currentIndexChanged), this,
&ConfigureUi::OnLanguageChanged);
}

View File

@@ -193,8 +193,7 @@ void ControllerShortcut::ControllerUpdateEvent(Core::HID::ControllerTriggerType
if (!Settings::values.controller_navigation) {
return;
}
if (button_sequence.npad.raw == Core::HID::NpadButton::None &&
button_sequence.capture.raw == 0 && button_sequence.home.raw == 0) {
if (button_sequence.npad.raw == Core::HID::NpadButton::None) {
return;
}