diff options
260 files changed, 5334 insertions, 3750 deletions
diff --git a/.ci/scripts/android/eabuild.sh b/.ci/scripts/android/eabuild.sh new file mode 100644 index 000000000..1672f2948 --- /dev/null +++ b/.ci/scripts/android/eabuild.sh | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | #!/bin/bash -ex | ||
| 2 | |||
| 3 | # SPDX-FileCopyrightText: 2024 yuzu Emulator Project | ||
| 4 | # SPDX-License-Identifier: GPL-3.0-or-later | ||
| 5 | |||
| 6 | export NDK_CCACHE="$(which ccache)" | ||
| 7 | ccache -s | ||
| 8 | |||
| 9 | export ANDROID_KEYSTORE_FILE="${GITHUB_WORKSPACE}/ks.jks" | ||
| 10 | base64 --decode <<< "${EA_PLAY_ANDROID_KEYSTORE_B64}" > "${ANDROID_KEYSTORE_FILE}" | ||
| 11 | export ANDROID_KEY_ALIAS="${PLAY_ANDROID_KEY_ALIAS}" | ||
| 12 | export ANDROID_KEYSTORE_PASS="${PLAY_ANDROID_KEYSTORE_PASS}" | ||
| 13 | export SERVICE_ACCOUNT_KEY_PATH="${GITHUB_WORKSPACE}/sa.json" | ||
| 14 | base64 --decode <<< "${EA_SERVICE_ACCOUNT_KEY_B64}" > "${SERVICE_ACCOUNT_KEY_PATH}" | ||
| 15 | ./gradlew "publishEaReleaseBundle" | ||
| 16 | |||
| 17 | ccache -s | ||
| 18 | |||
| 19 | if [ ! -z "${ANDROID_KEYSTORE_B64}" ]; then | ||
| 20 | rm "${ANDROID_KEYSTORE_FILE}" | ||
| 21 | fi | ||
diff --git a/.ci/scripts/android/mainlinebuild.sh b/.ci/scripts/android/mainlinebuild.sh new file mode 100644 index 000000000..f3b89ed1c --- /dev/null +++ b/.ci/scripts/android/mainlinebuild.sh | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | #!/bin/bash -ex | ||
| 2 | |||
| 3 | # SPDX-FileCopyrightText: 2024 yuzu Emulator Project | ||
| 4 | # SPDX-License-Identifier: GPL-3.0-or-later | ||
| 5 | |||
| 6 | export NDK_CCACHE="$(which ccache)" | ||
| 7 | ccache -s | ||
| 8 | |||
| 9 | export ANDROID_KEYSTORE_FILE="${GITHUB_WORKSPACE}/ks.jks" | ||
| 10 | base64 --decode <<< "${MAINLINE_PLAY_ANDROID_KEYSTORE_B64}" > "${ANDROID_KEYSTORE_FILE}" | ||
| 11 | export ANDROID_KEY_ALIAS="${PLAY_ANDROID_KEY_ALIAS}" | ||
| 12 | export ANDROID_KEYSTORE_PASS="${PLAY_ANDROID_KEYSTORE_PASS}" | ||
| 13 | export SERVICE_ACCOUNT_KEY_PATH="${GITHUB_WORKSPACE}/sa.json" | ||
| 14 | base64 --decode <<< "${MAINLINE_SERVICE_ACCOUNT_KEY_B64}" > "${SERVICE_ACCOUNT_KEY_PATH}" | ||
| 15 | ./gradlew "publishMainlineReleaseBundle" | ||
| 16 | |||
| 17 | ccache -s | ||
| 18 | |||
| 19 | if [ ! -z "${ANDROID_KEYSTORE_B64}" ]; then | ||
| 20 | rm "${ANDROID_KEYSTORE_FILE}" | ||
| 21 | fi | ||
diff --git a/.github/workflows/android-ea-play-release.yml b/.github/workflows/android-ea-play-release.yml new file mode 100644 index 000000000..0cf78279c --- /dev/null +++ b/.github/workflows/android-ea-play-release.yml | |||
| @@ -0,0 +1,66 @@ | |||
| 1 | # SPDX-FileCopyrightText: 2024 yuzu Emulator Project | ||
| 2 | # SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | name: yuzu-android-ea-play-release | ||
| 5 | |||
| 6 | on: | ||
| 7 | workflow_dispatch: | ||
| 8 | inputs: | ||
| 9 | release-track: | ||
| 10 | description: 'Play store release track (internal/alpha/beta/production)' | ||
| 11 | required: true | ||
| 12 | default: 'alpha' | ||
| 13 | |||
| 14 | jobs: | ||
| 15 | android: | ||
| 16 | runs-on: ubuntu-latest | ||
| 17 | if: ${{ github.repository == 'yuzu-emu/yuzu' }} | ||
| 18 | steps: | ||
| 19 | - uses: actions/checkout@v3 | ||
| 20 | name: Checkout | ||
| 21 | with: | ||
| 22 | fetch-depth: 0 | ||
| 23 | submodules: true | ||
| 24 | token: ${{ secrets.ALT_GITHUB_TOKEN }} | ||
| 25 | - run: npm install execa@5 | ||
| 26 | - uses: actions/github-script@v5 | ||
| 27 | name: 'Merge and publish Android EA changes' | ||
| 28 | env: | ||
| 29 | ALT_GITHUB_TOKEN: ${{ secrets.ALT_GITHUB_TOKEN }} | ||
| 30 | BUILD_EA: true | ||
| 31 | with: | ||
| 32 | script: | | ||
| 33 | const execa = require("execa"); | ||
| 34 | const mergebot = require('./.github/workflows/android-merge.js').mergebot; | ||
| 35 | process.chdir('${{ github.workspace }}'); | ||
| 36 | mergebot(github, context, execa); | ||
| 37 | - name: Get tag name | ||
| 38 | run: echo "GIT_TAG_NAME=$(cat tag-name.txt)" >> $GITHUB_ENV | ||
| 39 | - name: Set up JDK 17 | ||
| 40 | uses: actions/setup-java@v3 | ||
| 41 | with: | ||
| 42 | java-version: '17' | ||
| 43 | distribution: 'temurin' | ||
| 44 | - name: Install dependencies | ||
| 45 | run: | | ||
| 46 | sudo apt-get update | ||
| 47 | sudo apt-get install -y ccache apksigner glslang-dev glslang-tools | ||
| 48 | - name: Build | ||
| 49 | run: ./.ci/scripts/android/eabuild.sh | ||
| 50 | env: | ||
| 51 | EA_PLAY_ANDROID_KEYSTORE_B64: ${{ secrets.PLAY_ANDROID_KEYSTORE_B64 }} | ||
| 52 | PLAY_ANDROID_KEY_ALIAS: ${{ secrets.PLAY_ANDROID_KEY_ALIAS }} | ||
| 53 | PLAY_ANDROID_KEYSTORE_PASS: ${{ secrets.PLAY_ANDROID_KEYSTORE_PASS }} | ||
| 54 | EA_SERVICE_ACCOUNT_KEY_B64: ${{ secrets.EA_SERVICE_ACCOUNT_KEY_B64 }} | ||
| 55 | STORE_TRACK: ${{ github.event.inputs.release-track }} | ||
| 56 | AUTO_VERSIONED: true | ||
| 57 | BUILD_EA: true | ||
| 58 | - name: Create release | ||
| 59 | uses: softprops/action-gh-release@v1 | ||
| 60 | with: | ||
| 61 | tag_name: ${{ env.EA_TAG_NAME }} | ||
| 62 | name: ${{ env.EA_TAG_NAME }} | ||
| 63 | draft: false | ||
| 64 | prerelease: false | ||
| 65 | repository: yuzu/yuzu-android | ||
| 66 | token: ${{ secrets.ALT_GITHUB_TOKEN }} | ||
diff --git a/.github/workflows/android-mainline-play-release.yml b/.github/workflows/android-mainline-play-release.yml new file mode 100644 index 000000000..8255e0a40 --- /dev/null +++ b/.github/workflows/android-mainline-play-release.yml | |||
| @@ -0,0 +1,59 @@ | |||
| 1 | # SPDX-FileCopyrightText: 2024 yuzu Emulator Project | ||
| 2 | # SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | name: yuzu-android-mainline-play-release | ||
| 5 | |||
| 6 | on: | ||
| 7 | workflow_dispatch: | ||
| 8 | inputs: | ||
| 9 | release-tag: | ||
| 10 | description: 'Tag # from yuzu-android that you want to build and publish' | ||
| 11 | required: true | ||
| 12 | default: '200' | ||
| 13 | release-track: | ||
| 14 | description: 'Play store release track (internal/alpha/beta/production)' | ||
| 15 | required: true | ||
| 16 | default: 'alpha' | ||
| 17 | |||
| 18 | jobs: | ||
| 19 | android: | ||
| 20 | runs-on: ubuntu-latest | ||
| 21 | if: ${{ github.repository == 'yuzu-emu/yuzu' }} | ||
| 22 | steps: | ||
| 23 | - uses: actions/checkout@v3 | ||
| 24 | name: Checkout | ||
| 25 | with: | ||
| 26 | fetch-depth: 0 | ||
| 27 | submodules: true | ||
| 28 | token: ${{ secrets.ALT_GITHUB_TOKEN }} | ||
| 29 | - run: npm install execa@5 | ||
| 30 | - uses: actions/github-script@v5 | ||
| 31 | name: 'Pull mainline tag' | ||
| 32 | env: | ||
| 33 | MAINLINE_TAG: ${{ github.event.inputs.release-tag }} | ||
| 34 | with: | ||
| 35 | script: | | ||
| 36 | const execa = require("execa"); | ||
| 37 | const mergebot = require('./.github/workflows/android-merge.js').getMainlineTag; | ||
| 38 | process.chdir('${{ github.workspace }}'); | ||
| 39 | mergebot(execa); | ||
| 40 | - name: Set up JDK 17 | ||
| 41 | uses: actions/setup-java@v3 | ||
| 42 | with: | ||
| 43 | java-version: '17' | ||
| 44 | distribution: 'temurin' | ||
| 45 | - name: Install dependencies | ||
| 46 | run: | | ||
| 47 | sudo apt-get update | ||
| 48 | sudo apt-get install -y ccache apksigner glslang-dev glslang-tools | ||
| 49 | - name: Build | ||
| 50 | run: | | ||
| 51 | echo "GIT_TAG_NAME=android-${{ github.event.inputs.releast-tag }}" >> $GITHUB_ENV | ||
| 52 | ./.ci/scripts/android/mainlinebuild.sh | ||
| 53 | env: | ||
| 54 | MAINLINE_PLAY_ANDROID_KEYSTORE_B64: ${{ secrets.PLAY_ANDROID_KEYSTORE_B64 }} | ||
| 55 | PLAY_ANDROID_KEY_ALIAS: ${{ secrets.PLAY_ANDROID_KEY_ALIAS }} | ||
| 56 | PLAY_ANDROID_KEYSTORE_PASS: ${{ secrets.PLAY_ANDROID_KEYSTORE_PASS }} | ||
| 57 | SERVICE_ACCOUNT_KEY_B64: ${{ secrets.MAINLINE_SERVICE_ACCOUNT_KEY_B64 }} | ||
| 58 | STORE_TRACK: ${{ github.event.inputs.release-track }} | ||
| 59 | AUTO_VERSIONED: true | ||
diff --git a/.github/workflows/android-merge.js b/.github/workflows/android-merge.js index 44ab56e44..315f81ba0 100644 --- a/.github/workflows/android-merge.js +++ b/.github/workflows/android-merge.js | |||
| @@ -6,9 +6,12 @@ | |||
| 6 | 6 | ||
| 7 | const fs = require("fs"); | 7 | const fs = require("fs"); |
| 8 | // which label to check for changes | 8 | // which label to check for changes |
| 9 | const CHANGE_LABEL = 'android-merge'; | 9 | const CHANGE_LABEL_MAINLINE = 'android-merge'; |
| 10 | const CHANGE_LABEL_EA = 'android-ea-merge'; | ||
| 10 | // how far back in time should we consider the changes are "recent"? (default: 24 hours) | 11 | // how far back in time should we consider the changes are "recent"? (default: 24 hours) |
| 11 | const DETECTION_TIME_FRAME = (parseInt(process.env.DETECTION_TIME_FRAME)) || (24 * 3600 * 1000); | 12 | const DETECTION_TIME_FRAME = (parseInt(process.env.DETECTION_TIME_FRAME)) || (24 * 3600 * 1000); |
| 13 | const BUILD_EA = process.env.BUILD_EA == 'true'; | ||
| 14 | const MAINLINE_TAG = process.env.MAINLINE_TAG; | ||
| 12 | 15 | ||
| 13 | async function checkBaseChanges(github) { | 16 | async function checkBaseChanges(github) { |
| 14 | // query the commit date of the latest commit on this branch | 17 | // query the commit date of the latest commit on this branch |
| @@ -40,20 +43,7 @@ async function checkBaseChanges(github) { | |||
| 40 | 43 | ||
| 41 | async function checkAndroidChanges(github) { | 44 | async function checkAndroidChanges(github) { |
| 42 | if (checkBaseChanges(github)) return true; | 45 | if (checkBaseChanges(github)) return true; |
| 43 | const query = `query($owner:String!, $name:String!, $label:String!) { | 46 | const pulls = getPulls(github, false); |
| 44 | repository(name:$name, owner:$owner) { | ||
| 45 | pullRequests(labels: [$label], states: OPEN, first: 100) { | ||
| 46 | nodes { number headRepository { pushedAt } } | ||
| 47 | } | ||
| 48 | } | ||
| 49 | }`; | ||
| 50 | const variables = { | ||
| 51 | owner: 'yuzu-emu', | ||
| 52 | name: 'yuzu', | ||
| 53 | label: CHANGE_LABEL, | ||
| 54 | }; | ||
| 55 | const result = await github.graphql(query, variables); | ||
| 56 | const pulls = result.repository.pullRequests.nodes; | ||
| 57 | for (let i = 0; i < pulls.length; i++) { | 47 | for (let i = 0; i < pulls.length; i++) { |
| 58 | let pull = pulls[i]; | 48 | let pull = pulls[i]; |
| 59 | if (new Date() - new Date(pull.headRepository.pushedAt) <= DETECTION_TIME_FRAME) { | 49 | if (new Date() - new Date(pull.headRepository.pushedAt) <= DETECTION_TIME_FRAME) { |
| @@ -83,7 +73,13 @@ async function tagAndPush(github, owner, repo, execa, commit=false) { | |||
| 83 | }; | 73 | }; |
| 84 | const tags = await github.graphql(query, variables); | 74 | const tags = await github.graphql(query, variables); |
| 85 | const tagList = tags.repository.refs.nodes; | 75 | const tagList = tags.repository.refs.nodes; |
| 86 | const lastTag = tagList[0] ? tagList[0].name : 'dummy-0'; | 76 | let lastTag = 'android-1'; |
| 77 | for (let i = 0; i < tagList.length; ++i) { | ||
| 78 | if (tagList[i].name.includes('android-')) { | ||
| 79 | lastTag = tagList[i].name; | ||
| 80 | break; | ||
| 81 | } | ||
| 82 | } | ||
| 87 | const tagNumber = /\w+-(\d+)/.exec(lastTag)[1] | 0; | 83 | const tagNumber = /\w+-(\d+)/.exec(lastTag)[1] | 0; |
| 88 | const channel = repo.split('-')[1]; | 84 | const channel = repo.split('-')[1]; |
| 89 | const newTag = `${channel}-${tagNumber + 1}`; | 85 | const newTag = `${channel}-${tagNumber + 1}`; |
| @@ -101,6 +97,48 @@ async function tagAndPush(github, owner, repo, execa, commit=false) { | |||
| 101 | console.info('Successfully pushed new changes.'); | 97 | console.info('Successfully pushed new changes.'); |
| 102 | } | 98 | } |
| 103 | 99 | ||
| 100 | async function tagAndPushEA(github, owner, repo, execa) { | ||
| 101 | let altToken = process.env.ALT_GITHUB_TOKEN; | ||
| 102 | if (!altToken) { | ||
| 103 | throw `Please set ALT_GITHUB_TOKEN environment variable. This token should have write access to ${owner}/${repo}.`; | ||
| 104 | } | ||
| 105 | const query = `query ($owner:String!, $name:String!) { | ||
| 106 | repository(name:$name, owner:$owner) { | ||
| 107 | refs(refPrefix: "refs/tags/", orderBy: {field: TAG_COMMIT_DATE, direction: DESC}, first: 10) { | ||
| 108 | nodes { name } | ||
| 109 | } | ||
| 110 | } | ||
| 111 | }`; | ||
| 112 | const variables = { | ||
| 113 | owner: owner, | ||
| 114 | name: repo, | ||
| 115 | }; | ||
| 116 | const tags = await github.graphql(query, variables); | ||
| 117 | const tagList = tags.repository.refs.nodes; | ||
| 118 | let lastTag = 'ea-1'; | ||
| 119 | for (let i = 0; i < tagList.length; ++i) { | ||
| 120 | if (tagList[i].name.includes('ea-')) { | ||
| 121 | lastTag = tagList[i].name; | ||
| 122 | break; | ||
| 123 | } | ||
| 124 | } | ||
| 125 | const tagNumber = /\w+-(\d+)/.exec(lastTag)[1] | 0; | ||
| 126 | const newTag = `ea-${tagNumber + 1}`; | ||
| 127 | console.log(`New tag: ${newTag}`); | ||
| 128 | console.info('Pushing tags to GitHub ...'); | ||
| 129 | await execa("git", ["remote", "add", "android", "https://github.com/yuzu-emu/yuzu-android.git"]); | ||
| 130 | await execa("git", ["fetch", "android"]); | ||
| 131 | |||
| 132 | await execa("git", ['tag', newTag]); | ||
| 133 | await execa("git", ['push', 'android', `${newTag}`]); | ||
| 134 | |||
| 135 | fs.writeFile('tag-name.txt', newTag, (err) => { | ||
| 136 | if (err) throw 'Could not write tag name to file!' | ||
| 137 | }) | ||
| 138 | |||
| 139 | console.info('Successfully pushed new changes.'); | ||
| 140 | } | ||
| 141 | |||
| 104 | async function generateReadme(pulls, context, mergeResults, execa) { | 142 | async function generateReadme(pulls, context, mergeResults, execa) { |
| 105 | let baseUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/`; | 143 | let baseUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/`; |
| 106 | let output = | 144 | let output = |
| @@ -202,10 +240,7 @@ async function resetBranch(execa) { | |||
| 202 | } | 240 | } |
| 203 | } | 241 | } |
| 204 | 242 | ||
| 205 | async function mergebot(github, context, execa) { | 243 | async function getPulls(github) { |
| 206 | // Reset our local copy of master to what appears on yuzu-emu/yuzu - master | ||
| 207 | await resetBranch(execa); | ||
| 208 | |||
| 209 | const query = `query ($owner:String!, $name:String!, $label:String!) { | 244 | const query = `query ($owner:String!, $name:String!, $label:String!) { |
| 210 | repository(name:$name, owner:$owner) { | 245 | repository(name:$name, owner:$owner) { |
| 211 | pullRequests(labels: [$label], states: OPEN, first: 100) { | 246 | pullRequests(labels: [$label], states: OPEN, first: 100) { |
| @@ -215,13 +250,49 @@ async function mergebot(github, context, execa) { | |||
| 215 | } | 250 | } |
| 216 | } | 251 | } |
| 217 | }`; | 252 | }`; |
| 218 | const variables = { | 253 | const mainlineVariables = { |
| 219 | owner: 'yuzu-emu', | 254 | owner: 'yuzu-emu', |
| 220 | name: 'yuzu', | 255 | name: 'yuzu', |
| 221 | label: CHANGE_LABEL, | 256 | label: CHANGE_LABEL_MAINLINE, |
| 222 | }; | 257 | }; |
| 223 | const result = await github.graphql(query, variables); | 258 | const mainlineResult = await github.graphql(query, mainlineVariables); |
| 224 | const pulls = result.repository.pullRequests.nodes; | 259 | const pulls = mainlineResult.repository.pullRequests.nodes; |
| 260 | if (BUILD_EA) { | ||
| 261 | const eaVariables = { | ||
| 262 | owner: 'yuzu-emu', | ||
| 263 | name: 'yuzu', | ||
| 264 | label: CHANGE_LABEL_EA, | ||
| 265 | }; | ||
| 266 | const eaResult = await github.graphql(query, eaVariables); | ||
| 267 | const eaPulls = eaResult.repository.pullRequests.nodes; | ||
| 268 | return pulls.concat(eaPulls); | ||
| 269 | } | ||
| 270 | return pulls; | ||
| 271 | } | ||
| 272 | |||
| 273 | async function getMainlineTag(execa) { | ||
| 274 | console.log(`::group::Getting mainline tag android-${MAINLINE_TAG}`); | ||
| 275 | let hasFailed = false; | ||
| 276 | try { | ||
| 277 | await execa("git", ["remote", "add", "mainline", "https://github.com/yuzu-emu/yuzu-android.git"]); | ||
| 278 | await execa("git", ["fetch", "mainline", "--tags"]); | ||
| 279 | await execa("git", ["checkout", `tags/android-${MAINLINE_TAG}`]); | ||
| 280 | await execa("git", ["submodule", "update", "--init", "--recursive"]); | ||
| 281 | } catch (err) { | ||
| 282 | console.log('::error title=Failed pull tag'); | ||
| 283 | hasFailed = true; | ||
| 284 | } | ||
| 285 | console.log("::endgroup::"); | ||
| 286 | if (hasFailed) { | ||
| 287 | throw 'Failed pull mainline tag. Aborting!'; | ||
| 288 | } | ||
| 289 | } | ||
| 290 | |||
| 291 | async function mergebot(github, context, execa) { | ||
| 292 | // Reset our local copy of master to what appears on yuzu-emu/yuzu - master | ||
| 293 | await resetBranch(execa); | ||
| 294 | |||
| 295 | const pulls = await getPulls(github); | ||
| 225 | let displayList = []; | 296 | let displayList = []; |
| 226 | for (let i = 0; i < pulls.length; i++) { | 297 | for (let i = 0; i < pulls.length; i++) { |
| 227 | let pull = pulls[i]; | 298 | let pull = pulls[i]; |
| @@ -231,11 +302,17 @@ async function mergebot(github, context, execa) { | |||
| 231 | console.table(displayList); | 302 | console.table(displayList); |
| 232 | await fetchPullRequests(pulls, "https://github.com/yuzu-emu/yuzu", execa); | 303 | await fetchPullRequests(pulls, "https://github.com/yuzu-emu/yuzu", execa); |
| 233 | const mergeResults = await mergePullRequests(pulls, execa); | 304 | const mergeResults = await mergePullRequests(pulls, execa); |
| 234 | await generateReadme(pulls, context, mergeResults, execa); | 305 | |
| 235 | await tagAndPush(github, 'yuzu-emu', `yuzu-android`, execa, true); | 306 | if (BUILD_EA) { |
| 307 | await tagAndPushEA(github, 'yuzu-emu', `yuzu-android`, execa); | ||
| 308 | } else { | ||
| 309 | await generateReadme(pulls, context, mergeResults, execa); | ||
| 310 | await tagAndPush(github, 'yuzu-emu', `yuzu-android`, execa, true); | ||
| 311 | } | ||
| 236 | } | 312 | } |
| 237 | 313 | ||
| 238 | module.exports.mergebot = mergebot; | 314 | module.exports.mergebot = mergebot; |
| 239 | module.exports.checkAndroidChanges = checkAndroidChanges; | 315 | module.exports.checkAndroidChanges = checkAndroidChanges; |
| 240 | module.exports.tagAndPush = tagAndPush; | 316 | module.exports.tagAndPush = tagAndPush; |
| 241 | module.exports.checkBaseChanges = checkBaseChanges; | 317 | module.exports.checkBaseChanges = checkBaseChanges; |
| 318 | module.exports.getMainlineTag = getMainlineTag; | ||
diff --git a/.github/workflows/android-publish.yml b/.github/workflows/android-publish.yml index 68e21c2f2..61f739e96 100644 --- a/.github/workflows/android-publish.yml +++ b/.github/workflows/android-publish.yml | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | # SPDX-FileCopyrightText: 2023 yuzu Emulator Project | 1 | # SPDX-FileCopyrightText: 2024 yuzu Emulator Project |
| 2 | # SPDX-License-Identifier: GPL-2.0-or-later | 2 | # SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | name: yuzu-android-publish | 4 | name: yuzu-android-publish |
| @@ -16,7 +16,7 @@ on: | |||
| 16 | jobs: | 16 | jobs: |
| 17 | android: | 17 | android: |
| 18 | runs-on: ubuntu-latest | 18 | runs-on: ubuntu-latest |
| 19 | if: ${{ github.event.inputs.android != 'false' && github.repository == 'yuzu-emu/yuzu-android' }} | 19 | if: ${{ github.event.inputs.android != 'false' && github.repository == 'yuzu-emu/yuzu' }} |
| 20 | steps: | 20 | steps: |
| 21 | # this checkout is required to make sure the GitHub Actions scripts are available | 21 | # this checkout is required to make sure the GitHub Actions scripts are available |
| 22 | - uses: actions/checkout@v3 | 22 | - uses: actions/checkout@v3 |
diff --git a/externals/dynarmic b/externals/dynarmic | |||
| Subproject ca0e264f4f962e29baa23a3282ce484625866b9 | Subproject ba8192d89078af51ae6f97c9352e3683612cdff | ||
diff --git a/externals/nx_tzdb/tzdb_to_nx b/externals/nx_tzdb/tzdb_to_nx | |||
| Subproject 404d39004570a26c734a9d1fa29ab4d63089c59 | Subproject 97929690234f2b4add36b33657fe3fe09bd57df | ||
diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts index 188ef9469..cb026211c 100644 --- a/src/android/app/build.gradle.kts +++ b/src/android/app/build.gradle.kts | |||
| @@ -3,8 +3,8 @@ | |||
| 3 | 3 | ||
| 4 | import android.annotation.SuppressLint | 4 | import android.annotation.SuppressLint |
| 5 | import kotlin.collections.setOf | 5 | import kotlin.collections.setOf |
| 6 | import org.jetbrains.kotlin.konan.properties.Properties | ||
| 7 | import org.jlleitschuh.gradle.ktlint.reporter.ReporterType | 6 | import org.jlleitschuh.gradle.ktlint.reporter.ReporterType |
| 7 | import com.github.triplet.gradle.androidpublisher.ReleaseStatus | ||
| 8 | 8 | ||
| 9 | plugins { | 9 | plugins { |
| 10 | id("com.android.application") | 10 | id("com.android.application") |
| @@ -13,6 +13,7 @@ plugins { | |||
| 13 | kotlin("plugin.serialization") version "1.9.20" | 13 | kotlin("plugin.serialization") version "1.9.20" |
| 14 | id("androidx.navigation.safeargs.kotlin") | 14 | id("androidx.navigation.safeargs.kotlin") |
| 15 | id("org.jlleitschuh.gradle.ktlint") version "11.4.0" | 15 | id("org.jlleitschuh.gradle.ktlint") version "11.4.0" |
| 16 | id("com.github.triplet.play") version "3.8.6" | ||
| 16 | } | 17 | } |
| 17 | 18 | ||
| 18 | /** | 19 | /** |
| @@ -58,15 +59,7 @@ android { | |||
| 58 | targetSdk = 34 | 59 | targetSdk = 34 |
| 59 | versionName = getGitVersion() | 60 | versionName = getGitVersion() |
| 60 | 61 | ||
| 61 | // If you want to use autoVersion for the versionCode, create a property in local.properties | 62 | versionCode = if (System.getenv("AUTO_VERSIONED") == "true") { |
| 62 | // named "autoVersioned" and set it to "true" | ||
| 63 | val properties = Properties() | ||
| 64 | val versionProperty = try { | ||
| 65 | properties.load(project.rootProject.file("local.properties").inputStream()) | ||
| 66 | properties.getProperty("autoVersioned") ?: "" | ||
| 67 | } catch (e: Exception) { "" } | ||
| 68 | |||
| 69 | versionCode = if (versionProperty == "true") { | ||
| 70 | autoVersion | 63 | autoVersion |
| 71 | } else { | 64 | } else { |
| 72 | 1 | 65 | 1 |
| @@ -221,6 +214,15 @@ ktlint { | |||
| 221 | } | 214 | } |
| 222 | } | 215 | } |
| 223 | 216 | ||
| 217 | play { | ||
| 218 | val keyPath = System.getenv("SERVICE_ACCOUNT_KEY_PATH") | ||
| 219 | if (keyPath != null) { | ||
| 220 | serviceAccountCredentials.set(File(keyPath)) | ||
| 221 | } | ||
| 222 | track.set(System.getenv("STORE_TRACK") ?: "internal") | ||
| 223 | releaseStatus.set(ReleaseStatus.COMPLETED) | ||
| 224 | } | ||
| 225 | |||
| 224 | dependencies { | 226 | dependencies { |
| 225 | implementation("androidx.core:core-ktx:1.12.0") | 227 | implementation("androidx.core:core-ktx:1.12.0") |
| 226 | implementation("androidx.appcompat:appcompat:1.6.1") | 228 | implementation("androidx.appcompat:appcompat:1.6.1") |
| @@ -257,12 +259,18 @@ fun runGitCommand(command: List<String>): String { | |||
| 257 | } | 259 | } |
| 258 | 260 | ||
| 259 | fun getGitVersion(): String { | 261 | fun getGitVersion(): String { |
| 262 | val gitVersion = runGitCommand( | ||
| 263 | listOf( | ||
| 264 | "git", | ||
| 265 | "describe", | ||
| 266 | "--always", | ||
| 267 | "--long" | ||
| 268 | ) | ||
| 269 | ).replace(Regex("(-0)?-[^-]+$"), "") | ||
| 260 | val versionName = if (System.getenv("GITHUB_ACTIONS") != null) { | 270 | val versionName = if (System.getenv("GITHUB_ACTIONS") != null) { |
| 261 | val gitTag = System.getenv("GIT_TAG_NAME") ?: "" | 271 | System.getenv("GIT_TAG_NAME") ?: gitVersion |
| 262 | gitTag | ||
| 263 | } else { | 272 | } else { |
| 264 | runGitCommand(listOf("git", "describe", "--always", "--long")) | 273 | gitVersion |
| 265 | .replace(Regex("(-0)?-[^-]+$"), "") | ||
| 266 | } | 274 | } |
| 267 | return versionName.ifEmpty { "0.0" } | 275 | return versionName.ifEmpty { "0.0" } |
| 268 | } | 276 | } |
diff --git a/src/android/app/src/main/AndroidManifest.xml b/src/android/app/src/main/AndroidManifest.xml index f011bd696..7890b30ca 100644 --- a/src/android/app/src/main/AndroidManifest.xml +++ b/src/android/app/src/main/AndroidManifest.xml | |||
| @@ -12,8 +12,6 @@ SPDX-License-Identifier: GPL-3.0-or-later | |||
| 12 | <uses-feature android:name="android.hardware.vulkan.version" android:version="0x401000" android:required="true" /> | 12 | <uses-feature android:name="android.hardware.vulkan.version" android:version="0x401000" android:required="true" /> |
| 13 | 13 | ||
| 14 | <uses-permission android:name="android.permission.INTERNET" /> | 14 | <uses-permission android:name="android.permission.INTERNET" /> |
| 15 | <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> | ||
| 16 | <uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" /> | ||
| 17 | <uses-permission android:name="android.permission.NFC" /> | 15 | <uses-permission android:name="android.permission.NFC" /> |
| 18 | <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> | 16 | <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> |
| 19 | 17 | ||
| @@ -80,10 +78,6 @@ SPDX-License-Identifier: GPL-3.0-or-later | |||
| 80 | android:resource="@xml/nfc_tech_filter" /> | 78 | android:resource="@xml/nfc_tech_filter" /> |
| 81 | </activity> | 79 | </activity> |
| 82 | 80 | ||
| 83 | <service android:name="org.yuzu.yuzu_emu.utils.ForegroundService" android:foregroundServiceType="specialUse"> | ||
| 84 | <property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE" android:value="Keep emulation running in background"/> | ||
| 85 | </service> | ||
| 86 | |||
| 87 | <provider | 81 | <provider |
| 88 | android:name=".features.DocumentProvider" | 82 | android:name=".features.DocumentProvider" |
| 89 | android:authorities="${applicationId}.user" | 83 | android:authorities="${applicationId}.user" |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt index d114bd53d..76778c10a 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt | |||
| @@ -17,17 +17,6 @@ fun Context.getPublicFilesDir(): File = getExternalFilesDir(null) ?: filesDir | |||
| 17 | 17 | ||
| 18 | class YuzuApplication : Application() { | 18 | class YuzuApplication : Application() { |
| 19 | private fun createNotificationChannels() { | 19 | private fun createNotificationChannels() { |
| 20 | val emulationChannel = NotificationChannel( | ||
| 21 | getString(R.string.emulation_notification_channel_id), | ||
| 22 | getString(R.string.emulation_notification_channel_name), | ||
| 23 | NotificationManager.IMPORTANCE_LOW | ||
| 24 | ) | ||
| 25 | emulationChannel.description = getString( | ||
| 26 | R.string.emulation_notification_channel_description | ||
| 27 | ) | ||
| 28 | emulationChannel.setSound(null, null) | ||
| 29 | emulationChannel.vibrationPattern = null | ||
| 30 | |||
| 31 | val noticeChannel = NotificationChannel( | 20 | val noticeChannel = NotificationChannel( |
| 32 | getString(R.string.notice_notification_channel_id), | 21 | getString(R.string.notice_notification_channel_id), |
| 33 | getString(R.string.notice_notification_channel_name), | 22 | getString(R.string.notice_notification_channel_name), |
| @@ -39,7 +28,6 @@ class YuzuApplication : Application() { | |||
| 39 | // Register the channel with the system; you can't change the importance | 28 | // Register the channel with the system; you can't change the importance |
| 40 | // or other notification behaviors after this | 29 | // or other notification behaviors after this |
| 41 | val notificationManager = getSystemService(NotificationManager::class.java) | 30 | val notificationManager = getSystemService(NotificationManager::class.java) |
| 42 | notificationManager.createNotificationChannel(emulationChannel) | ||
| 43 | notificationManager.createNotificationChannel(noticeChannel) | 31 | notificationManager.createNotificationChannel(noticeChannel) |
| 44 | } | 32 | } |
| 45 | 33 | ||
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt index 564aaf305..7a8d03610 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt | |||
| @@ -4,7 +4,6 @@ | |||
| 4 | package org.yuzu.yuzu_emu.activities | 4 | package org.yuzu.yuzu_emu.activities |
| 5 | 5 | ||
| 6 | import android.annotation.SuppressLint | 6 | import android.annotation.SuppressLint |
| 7 | import android.app.Activity | ||
| 8 | import android.app.PendingIntent | 7 | import android.app.PendingIntent |
| 9 | import android.app.PictureInPictureParams | 8 | import android.app.PictureInPictureParams |
| 10 | import android.app.RemoteAction | 9 | import android.app.RemoteAction |
| @@ -45,7 +44,6 @@ import org.yuzu.yuzu_emu.features.settings.model.IntSetting | |||
| 45 | import org.yuzu.yuzu_emu.features.settings.model.Settings | 44 | import org.yuzu.yuzu_emu.features.settings.model.Settings |
| 46 | import org.yuzu.yuzu_emu.model.EmulationViewModel | 45 | import org.yuzu.yuzu_emu.model.EmulationViewModel |
| 47 | import org.yuzu.yuzu_emu.model.Game | 46 | import org.yuzu.yuzu_emu.model.Game |
| 48 | import org.yuzu.yuzu_emu.utils.ForegroundService | ||
| 49 | import org.yuzu.yuzu_emu.utils.InputHandler | 47 | import org.yuzu.yuzu_emu.utils.InputHandler |
| 50 | import org.yuzu.yuzu_emu.utils.Log | 48 | import org.yuzu.yuzu_emu.utils.Log |
| 51 | import org.yuzu.yuzu_emu.utils.MemoryUtil | 49 | import org.yuzu.yuzu_emu.utils.MemoryUtil |
| @@ -74,11 +72,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { | |||
| 74 | 72 | ||
| 75 | private val emulationViewModel: EmulationViewModel by viewModels() | 73 | private val emulationViewModel: EmulationViewModel by viewModels() |
| 76 | 74 | ||
| 77 | override fun onDestroy() { | ||
| 78 | stopForegroundService(this) | ||
| 79 | super.onDestroy() | ||
| 80 | } | ||
| 81 | |||
| 82 | override fun onCreate(savedInstanceState: Bundle?) { | 75 | override fun onCreate(savedInstanceState: Bundle?) { |
| 83 | Log.gameLaunched = true | 76 | Log.gameLaunched = true |
| 84 | ThemeHelper.setTheme(this) | 77 | ThemeHelper.setTheme(this) |
| @@ -125,10 +118,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { | |||
| 125 | .apply() | 118 | .apply() |
| 126 | } | 119 | } |
| 127 | } | 120 | } |
| 128 | |||
| 129 | // Start a foreground service to prevent the app from getting killed in the background | ||
| 130 | val startIntent = Intent(this, ForegroundService::class.java) | ||
| 131 | startForegroundService(startIntent) | ||
| 132 | } | 121 | } |
| 133 | 122 | ||
| 134 | override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean { | 123 | override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean { |
| @@ -481,12 +470,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { | |||
| 481 | activity.startActivity(launcher) | 470 | activity.startActivity(launcher) |
| 482 | } | 471 | } |
| 483 | 472 | ||
| 484 | fun stopForegroundService(activity: Activity) { | ||
| 485 | val startIntent = Intent(activity, ForegroundService::class.java) | ||
| 486 | startIntent.action = ForegroundService.ACTION_STOP | ||
| 487 | activity.startForegroundService(startIntent) | ||
| 488 | } | ||
| 489 | |||
| 490 | private fun areCoordinatesOutside(view: View?, x: Float, y: Float): Boolean { | 473 | private fun areCoordinatesOutside(view: View?, x: Float, y: Float): Boolean { |
| 491 | if (view == null) { | 474 | if (view == null) { |
| 492 | return true | 475 | return true |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt index 86bd33672..664478472 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt | |||
| @@ -25,7 +25,8 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting { | |||
| 25 | HAPTIC_FEEDBACK("haptic_feedback"), | 25 | HAPTIC_FEEDBACK("haptic_feedback"), |
| 26 | SHOW_PERFORMANCE_OVERLAY("show_performance_overlay"), | 26 | SHOW_PERFORMANCE_OVERLAY("show_performance_overlay"), |
| 27 | SHOW_INPUT_OVERLAY("show_input_overlay"), | 27 | SHOW_INPUT_OVERLAY("show_input_overlay"), |
| 28 | TOUCHSCREEN("touchscreen"); | 28 | TOUCHSCREEN("touchscreen"), |
| 29 | SHOW_THERMAL_OVERLAY("show_thermal_overlay"); | ||
| 29 | 30 | ||
| 30 | override fun getBoolean(needsGlobal: Boolean): Boolean = | 31 | override fun getBoolean(needsGlobal: Boolean): Boolean = |
| 31 | NativeConfig.getBoolean(key, needsGlobal) | 32 | NativeConfig.getBoolean(key, needsGlobal) |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt index d7ab0b5d9..6f6e7be10 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt | |||
| @@ -8,7 +8,6 @@ import android.os.Bundle | |||
| 8 | import android.view.LayoutInflater | 8 | import android.view.LayoutInflater |
| 9 | import android.view.View | 9 | import android.view.View |
| 10 | import android.view.ViewGroup | 10 | import android.view.ViewGroup |
| 11 | import android.view.ViewGroup.MarginLayoutParams | ||
| 12 | import androidx.core.view.ViewCompat | 11 | import androidx.core.view.ViewCompat |
| 13 | import androidx.core.view.WindowInsetsCompat | 12 | import androidx.core.view.WindowInsetsCompat |
| 14 | import androidx.core.view.updatePadding | 13 | import androidx.core.view.updatePadding |
| @@ -27,6 +26,7 @@ import org.yuzu.yuzu_emu.R | |||
| 27 | import org.yuzu.yuzu_emu.databinding.FragmentSettingsBinding | 26 | import org.yuzu.yuzu_emu.databinding.FragmentSettingsBinding |
| 28 | import org.yuzu.yuzu_emu.features.settings.model.Settings | 27 | import org.yuzu.yuzu_emu.features.settings.model.Settings |
| 29 | import org.yuzu.yuzu_emu.model.SettingsViewModel | 28 | import org.yuzu.yuzu_emu.model.SettingsViewModel |
| 29 | import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins | ||
| 30 | 30 | ||
| 31 | class SettingsFragment : Fragment() { | 31 | class SettingsFragment : Fragment() { |
| 32 | private lateinit var presenter: SettingsFragmentPresenter | 32 | private lateinit var presenter: SettingsFragmentPresenter |
| @@ -125,18 +125,10 @@ class SettingsFragment : Fragment() { | |||
| 125 | val leftInsets = barInsets.left + cutoutInsets.left | 125 | val leftInsets = barInsets.left + cutoutInsets.left |
| 126 | val rightInsets = barInsets.right + cutoutInsets.right | 126 | val rightInsets = barInsets.right + cutoutInsets.right |
| 127 | 127 | ||
| 128 | val mlpSettingsList = binding.listSettings.layoutParams as MarginLayoutParams | 128 | binding.listSettings.updateMargins(left = leftInsets, right = rightInsets) |
| 129 | mlpSettingsList.leftMargin = leftInsets | 129 | binding.listSettings.updatePadding(bottom = barInsets.bottom) |
| 130 | mlpSettingsList.rightMargin = rightInsets | 130 | |
| 131 | binding.listSettings.layoutParams = mlpSettingsList | 131 | binding.appbarSettings.updateMargins(left = leftInsets, right = rightInsets) |
| 132 | binding.listSettings.updatePadding( | ||
| 133 | bottom = barInsets.bottom | ||
| 134 | ) | ||
| 135 | |||
| 136 | val mlpAppBar = binding.appbarSettings.layoutParams as MarginLayoutParams | ||
| 137 | mlpAppBar.leftMargin = leftInsets | ||
| 138 | mlpAppBar.rightMargin = rightInsets | ||
| 139 | binding.appbarSettings.layoutParams = mlpAppBar | ||
| 140 | windowInsets | 132 | windowInsets |
| 141 | } | 133 | } |
| 142 | } | 134 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt index 5ab38ffda..ff4f0e5df 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt | |||
| @@ -13,7 +13,6 @@ import android.os.Bundle | |||
| 13 | import android.view.LayoutInflater | 13 | import android.view.LayoutInflater |
| 14 | import android.view.View | 14 | import android.view.View |
| 15 | import android.view.ViewGroup | 15 | import android.view.ViewGroup |
| 16 | import android.view.ViewGroup.MarginLayoutParams | ||
| 17 | import android.widget.Toast | 16 | import android.widget.Toast |
| 18 | import androidx.core.view.ViewCompat | 17 | import androidx.core.view.ViewCompat |
| 19 | import androidx.core.view.WindowInsetsCompat | 18 | import androidx.core.view.WindowInsetsCompat |
| @@ -26,6 +25,7 @@ import org.yuzu.yuzu_emu.BuildConfig | |||
| 26 | import org.yuzu.yuzu_emu.R | 25 | import org.yuzu.yuzu_emu.R |
| 27 | import org.yuzu.yuzu_emu.databinding.FragmentAboutBinding | 26 | import org.yuzu.yuzu_emu.databinding.FragmentAboutBinding |
| 28 | import org.yuzu.yuzu_emu.model.HomeViewModel | 27 | import org.yuzu.yuzu_emu.model.HomeViewModel |
| 28 | import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins | ||
| 29 | 29 | ||
| 30 | class AboutFragment : Fragment() { | 30 | class AboutFragment : Fragment() { |
| 31 | private var _binding: FragmentAboutBinding? = null | 31 | private var _binding: FragmentAboutBinding? = null |
| @@ -114,15 +114,8 @@ class AboutFragment : Fragment() { | |||
| 114 | val leftInsets = barInsets.left + cutoutInsets.left | 114 | val leftInsets = barInsets.left + cutoutInsets.left |
| 115 | val rightInsets = barInsets.right + cutoutInsets.right | 115 | val rightInsets = barInsets.right + cutoutInsets.right |
| 116 | 116 | ||
| 117 | val mlpToolbar = binding.toolbarAbout.layoutParams as MarginLayoutParams | 117 | binding.toolbarAbout.updateMargins(left = leftInsets, right = rightInsets) |
| 118 | mlpToolbar.leftMargin = leftInsets | 118 | binding.scrollAbout.updateMargins(left = leftInsets, right = rightInsets) |
| 119 | mlpToolbar.rightMargin = rightInsets | ||
| 120 | binding.toolbarAbout.layoutParams = mlpToolbar | ||
| 121 | |||
| 122 | val mlpScrollAbout = binding.scrollAbout.layoutParams as MarginLayoutParams | ||
| 123 | mlpScrollAbout.leftMargin = leftInsets | ||
| 124 | mlpScrollAbout.rightMargin = rightInsets | ||
| 125 | binding.scrollAbout.layoutParams = mlpScrollAbout | ||
| 126 | 119 | ||
| 127 | binding.contentAbout.updatePadding(bottom = barInsets.bottom) | 120 | binding.contentAbout.updatePadding(bottom = barInsets.bottom) |
| 128 | 121 | ||
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt index adb65812c..f5647fa95 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt | |||
| @@ -31,6 +31,7 @@ import org.yuzu.yuzu_emu.model.AddonViewModel | |||
| 31 | import org.yuzu.yuzu_emu.model.HomeViewModel | 31 | import org.yuzu.yuzu_emu.model.HomeViewModel |
| 32 | import org.yuzu.yuzu_emu.utils.AddonUtil | 32 | import org.yuzu.yuzu_emu.utils.AddonUtil |
| 33 | import org.yuzu.yuzu_emu.utils.FileUtil.copyFilesTo | 33 | import org.yuzu.yuzu_emu.utils.FileUtil.copyFilesTo |
| 34 | import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins | ||
| 34 | import java.io.File | 35 | import java.io.File |
| 35 | 36 | ||
| 36 | class AddonsFragment : Fragment() { | 37 | class AddonsFragment : Fragment() { |
| @@ -202,27 +203,19 @@ class AddonsFragment : Fragment() { | |||
| 202 | val leftInsets = barInsets.left + cutoutInsets.left | 203 | val leftInsets = barInsets.left + cutoutInsets.left |
| 203 | val rightInsets = barInsets.right + cutoutInsets.right | 204 | val rightInsets = barInsets.right + cutoutInsets.right |
| 204 | 205 | ||
| 205 | val mlpToolbar = binding.toolbarAddons.layoutParams as ViewGroup.MarginLayoutParams | 206 | binding.toolbarAddons.updateMargins(left = leftInsets, right = rightInsets) |
| 206 | mlpToolbar.leftMargin = leftInsets | 207 | binding.listAddons.updateMargins(left = leftInsets, right = rightInsets) |
| 207 | mlpToolbar.rightMargin = rightInsets | ||
| 208 | binding.toolbarAddons.layoutParams = mlpToolbar | ||
| 209 | |||
| 210 | val mlpAddonsList = binding.listAddons.layoutParams as ViewGroup.MarginLayoutParams | ||
| 211 | mlpAddonsList.leftMargin = leftInsets | ||
| 212 | mlpAddonsList.rightMargin = rightInsets | ||
| 213 | binding.listAddons.layoutParams = mlpAddonsList | ||
| 214 | binding.listAddons.updatePadding( | 208 | binding.listAddons.updatePadding( |
| 215 | bottom = barInsets.bottom + | 209 | bottom = barInsets.bottom + |
| 216 | resources.getDimensionPixelSize(R.dimen.spacing_bottom_list_fab) | 210 | resources.getDimensionPixelSize(R.dimen.spacing_bottom_list_fab) |
| 217 | ) | 211 | ) |
| 218 | 212 | ||
| 219 | val fabSpacing = resources.getDimensionPixelSize(R.dimen.spacing_fab) | 213 | val fabSpacing = resources.getDimensionPixelSize(R.dimen.spacing_fab) |
| 220 | val mlpFab = | 214 | binding.buttonInstall.updateMargins( |
| 221 | binding.buttonInstall.layoutParams as ViewGroup.MarginLayoutParams | 215 | left = leftInsets + fabSpacing, |
| 222 | mlpFab.leftMargin = leftInsets + fabSpacing | 216 | right = rightInsets + fabSpacing, |
| 223 | mlpFab.rightMargin = rightInsets + fabSpacing | 217 | bottom = barInsets.bottom + fabSpacing |
| 224 | mlpFab.bottomMargin = barInsets.bottom + fabSpacing | 218 | ) |
| 225 | binding.buttonInstall.layoutParams = mlpFab | ||
| 226 | 219 | ||
| 227 | windowInsets | 220 | windowInsets |
| 228 | } | 221 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AppletLauncherFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AppletLauncherFragment.kt index 1f66b440d..73ca40484 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AppletLauncherFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AppletLauncherFragment.kt | |||
| @@ -21,6 +21,7 @@ import org.yuzu.yuzu_emu.databinding.FragmentAppletLauncherBinding | |||
| 21 | import org.yuzu.yuzu_emu.model.Applet | 21 | import org.yuzu.yuzu_emu.model.Applet |
| 22 | import org.yuzu.yuzu_emu.model.AppletInfo | 22 | import org.yuzu.yuzu_emu.model.AppletInfo |
| 23 | import org.yuzu.yuzu_emu.model.HomeViewModel | 23 | import org.yuzu.yuzu_emu.model.HomeViewModel |
| 24 | import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins | ||
| 24 | 25 | ||
| 25 | class AppletLauncherFragment : Fragment() { | 26 | class AppletLauncherFragment : Fragment() { |
| 26 | private var _binding: FragmentAppletLauncherBinding? = null | 27 | private var _binding: FragmentAppletLauncherBinding? = null |
| @@ -95,16 +96,8 @@ class AppletLauncherFragment : Fragment() { | |||
| 95 | val leftInsets = barInsets.left + cutoutInsets.left | 96 | val leftInsets = barInsets.left + cutoutInsets.left |
| 96 | val rightInsets = barInsets.right + cutoutInsets.right | 97 | val rightInsets = barInsets.right + cutoutInsets.right |
| 97 | 98 | ||
| 98 | val mlpAppBar = binding.toolbarApplets.layoutParams as ViewGroup.MarginLayoutParams | 99 | binding.toolbarApplets.updateMargins(left = leftInsets, right = rightInsets) |
| 99 | mlpAppBar.leftMargin = leftInsets | 100 | binding.listApplets.updateMargins(left = leftInsets, right = rightInsets) |
| 100 | mlpAppBar.rightMargin = rightInsets | ||
| 101 | binding.toolbarApplets.layoutParams = mlpAppBar | ||
| 102 | |||
| 103 | val mlpListApplets = | ||
| 104 | binding.listApplets.layoutParams as ViewGroup.MarginLayoutParams | ||
| 105 | mlpListApplets.leftMargin = leftInsets | ||
| 106 | mlpListApplets.rightMargin = rightInsets | ||
| 107 | binding.listApplets.layoutParams = mlpListApplets | ||
| 108 | 101 | ||
| 109 | binding.listApplets.updatePadding(bottom = barInsets.bottom) | 102 | binding.listApplets.updatePadding(bottom = barInsets.bottom) |
| 110 | 103 | ||
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt index bf017cd7c..41cff46c1 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt | |||
| @@ -34,6 +34,7 @@ import org.yuzu.yuzu_emu.model.HomeViewModel | |||
| 34 | import org.yuzu.yuzu_emu.utils.FileUtil | 34 | import org.yuzu.yuzu_emu.utils.FileUtil |
| 35 | import org.yuzu.yuzu_emu.utils.GpuDriverHelper | 35 | import org.yuzu.yuzu_emu.utils.GpuDriverHelper |
| 36 | import org.yuzu.yuzu_emu.utils.NativeConfig | 36 | import org.yuzu.yuzu_emu.utils.NativeConfig |
| 37 | import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins | ||
| 37 | import java.io.File | 38 | import java.io.File |
| 38 | import java.io.IOException | 39 | import java.io.IOException |
| 39 | 40 | ||
| @@ -141,23 +142,15 @@ class DriverManagerFragment : Fragment() { | |||
| 141 | val leftInsets = barInsets.left + cutoutInsets.left | 142 | val leftInsets = barInsets.left + cutoutInsets.left |
| 142 | val rightInsets = barInsets.right + cutoutInsets.right | 143 | val rightInsets = barInsets.right + cutoutInsets.right |
| 143 | 144 | ||
| 144 | val mlpAppBar = binding.toolbarDrivers.layoutParams as ViewGroup.MarginLayoutParams | 145 | binding.toolbarDrivers.updateMargins(left = leftInsets, right = rightInsets) |
| 145 | mlpAppBar.leftMargin = leftInsets | 146 | binding.listDrivers.updateMargins(left = leftInsets, right = rightInsets) |
| 146 | mlpAppBar.rightMargin = rightInsets | ||
| 147 | binding.toolbarDrivers.layoutParams = mlpAppBar | ||
| 148 | |||
| 149 | val mlplistDrivers = binding.listDrivers.layoutParams as ViewGroup.MarginLayoutParams | ||
| 150 | mlplistDrivers.leftMargin = leftInsets | ||
| 151 | mlplistDrivers.rightMargin = rightInsets | ||
| 152 | binding.listDrivers.layoutParams = mlplistDrivers | ||
| 153 | 147 | ||
| 154 | val fabSpacing = resources.getDimensionPixelSize(R.dimen.spacing_fab) | 148 | val fabSpacing = resources.getDimensionPixelSize(R.dimen.spacing_fab) |
| 155 | val mlpFab = | 149 | binding.buttonInstall.updateMargins( |
| 156 | binding.buttonInstall.layoutParams as ViewGroup.MarginLayoutParams | 150 | left = leftInsets + fabSpacing, |
| 157 | mlpFab.leftMargin = leftInsets + fabSpacing | 151 | right = rightInsets + fabSpacing, |
| 158 | mlpFab.rightMargin = rightInsets + fabSpacing | 152 | bottom = barInsets.bottom + fabSpacing |
| 159 | mlpFab.bottomMargin = barInsets.bottom + fabSpacing | 153 | ) |
| 160 | binding.buttonInstall.layoutParams = mlpFab | ||
| 161 | 154 | ||
| 162 | binding.listDrivers.updatePadding( | 155 | binding.listDrivers.updatePadding( |
| 163 | bottom = barInsets.bottom + | 156 | bottom = barInsets.bottom + |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EarlyAccessFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EarlyAccessFragment.kt index dbc16da4a..0534b68ce 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EarlyAccessFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EarlyAccessFragment.kt | |||
| @@ -19,6 +19,7 @@ import com.google.android.material.transition.MaterialSharedAxis | |||
| 19 | import org.yuzu.yuzu_emu.R | 19 | import org.yuzu.yuzu_emu.R |
| 20 | import org.yuzu.yuzu_emu.databinding.FragmentEarlyAccessBinding | 20 | import org.yuzu.yuzu_emu.databinding.FragmentEarlyAccessBinding |
| 21 | import org.yuzu.yuzu_emu.model.HomeViewModel | 21 | import org.yuzu.yuzu_emu.model.HomeViewModel |
| 22 | import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins | ||
| 22 | 23 | ||
| 23 | class EarlyAccessFragment : Fragment() { | 24 | class EarlyAccessFragment : Fragment() { |
| 24 | private var _binding: FragmentEarlyAccessBinding? = null | 25 | private var _binding: FragmentEarlyAccessBinding? = null |
| @@ -73,10 +74,7 @@ class EarlyAccessFragment : Fragment() { | |||
| 73 | val leftInsets = barInsets.left + cutoutInsets.left | 74 | val leftInsets = barInsets.left + cutoutInsets.left |
| 74 | val rightInsets = barInsets.right + cutoutInsets.right | 75 | val rightInsets = barInsets.right + cutoutInsets.right |
| 75 | 76 | ||
| 76 | val mlpAppBar = binding.appbarEa.layoutParams as ViewGroup.MarginLayoutParams | 77 | binding.appbarEa.updateMargins(left = leftInsets, right = rightInsets) |
| 77 | mlpAppBar.leftMargin = leftInsets | ||
| 78 | mlpAppBar.rightMargin = rightInsets | ||
| 79 | binding.appbarEa.layoutParams = mlpAppBar | ||
| 80 | 78 | ||
| 81 | binding.scrollEa.updatePadding( | 79 | binding.scrollEa.updatePadding( |
| 82 | left = leftInsets, | 80 | left = leftInsets, |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt index 937b8faf1..44af896da 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt | |||
| @@ -13,6 +13,7 @@ import android.net.Uri | |||
| 13 | import android.os.Bundle | 13 | import android.os.Bundle |
| 14 | import android.os.Handler | 14 | import android.os.Handler |
| 15 | import android.os.Looper | 15 | import android.os.Looper |
| 16 | import android.os.PowerManager | ||
| 16 | import android.os.SystemClock | 17 | import android.os.SystemClock |
| 17 | import android.view.* | 18 | import android.view.* |
| 18 | import android.widget.TextView | 19 | import android.widget.TextView |
| @@ -23,6 +24,7 @@ import androidx.core.content.res.ResourcesCompat | |||
| 23 | import androidx.core.graphics.Insets | 24 | import androidx.core.graphics.Insets |
| 24 | import androidx.core.view.ViewCompat | 25 | import androidx.core.view.ViewCompat |
| 25 | import androidx.core.view.WindowInsetsCompat | 26 | import androidx.core.view.WindowInsetsCompat |
| 27 | import androidx.core.view.updatePadding | ||
| 26 | import androidx.drawerlayout.widget.DrawerLayout | 28 | import androidx.drawerlayout.widget.DrawerLayout |
| 27 | import androidx.drawerlayout.widget.DrawerLayout.DrawerListener | 29 | import androidx.drawerlayout.widget.DrawerLayout.DrawerListener |
| 28 | import androidx.fragment.app.Fragment | 30 | import androidx.fragment.app.Fragment |
| @@ -38,7 +40,6 @@ import androidx.window.layout.WindowLayoutInfo | |||
| 38 | import com.google.android.material.dialog.MaterialAlertDialogBuilder | 40 | import com.google.android.material.dialog.MaterialAlertDialogBuilder |
| 39 | import com.google.android.material.slider.Slider | 41 | import com.google.android.material.slider.Slider |
| 40 | import kotlinx.coroutines.Dispatchers | 42 | import kotlinx.coroutines.Dispatchers |
| 41 | import kotlinx.coroutines.flow.collect | ||
| 42 | import kotlinx.coroutines.flow.collectLatest | 43 | import kotlinx.coroutines.flow.collectLatest |
| 43 | import kotlinx.coroutines.launch | 44 | import kotlinx.coroutines.launch |
| 44 | import org.yuzu.yuzu_emu.HomeNavigationDirections | 45 | import org.yuzu.yuzu_emu.HomeNavigationDirections |
| @@ -64,6 +65,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 64 | private lateinit var emulationState: EmulationState | 65 | private lateinit var emulationState: EmulationState |
| 65 | private var emulationActivity: EmulationActivity? = null | 66 | private var emulationActivity: EmulationActivity? = null |
| 66 | private var perfStatsUpdater: (() -> Unit)? = null | 67 | private var perfStatsUpdater: (() -> Unit)? = null |
| 68 | private var thermalStatsUpdater: (() -> Unit)? = null | ||
| 67 | 69 | ||
| 68 | private var _binding: FragmentEmulationBinding? = null | 70 | private var _binding: FragmentEmulationBinding? = null |
| 69 | private val binding get() = _binding!! | 71 | private val binding get() = _binding!! |
| @@ -77,6 +79,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 77 | 79 | ||
| 78 | private var isInFoldableLayout = false | 80 | private var isInFoldableLayout = false |
| 79 | 81 | ||
| 82 | private lateinit var powerManager: PowerManager | ||
| 83 | |||
| 80 | override fun onAttach(context: Context) { | 84 | override fun onAttach(context: Context) { |
| 81 | super.onAttach(context) | 85 | super.onAttach(context) |
| 82 | if (context is EmulationActivity) { | 86 | if (context is EmulationActivity) { |
| @@ -102,6 +106,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 102 | super.onCreate(savedInstanceState) | 106 | super.onCreate(savedInstanceState) |
| 103 | updateOrientation() | 107 | updateOrientation() |
| 104 | 108 | ||
| 109 | powerManager = requireContext().getSystemService(Context.POWER_SERVICE) as PowerManager | ||
| 110 | |||
| 105 | val intentUri: Uri? = requireActivity().intent.data | 111 | val intentUri: Uri? = requireActivity().intent.data |
| 106 | var intentGame: Game? = null | 112 | var intentGame: Game? = null |
| 107 | if (intentUri != null) { | 113 | if (intentUri != null) { |
| @@ -394,8 +400,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 394 | 400 | ||
| 395 | emulationState.updateSurface() | 401 | emulationState.updateSurface() |
| 396 | 402 | ||
| 397 | // Setup overlay | 403 | // Setup overlays |
| 398 | updateShowFpsOverlay() | 404 | updateShowFpsOverlay() |
| 405 | updateThermalOverlay() | ||
| 399 | } | 406 | } |
| 400 | } | 407 | } |
| 401 | } | 408 | } |
| @@ -553,6 +560,38 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 553 | } | 560 | } |
| 554 | } | 561 | } |
| 555 | 562 | ||
| 563 | private fun updateThermalOverlay() { | ||
| 564 | if (BooleanSetting.SHOW_THERMAL_OVERLAY.getBoolean()) { | ||
| 565 | thermalStatsUpdater = { | ||
| 566 | if (emulationViewModel.emulationStarted.value && | ||
| 567 | !emulationViewModel.isEmulationStopping.value | ||
| 568 | ) { | ||
| 569 | val thermalStatus = when (powerManager.currentThermalStatus) { | ||
| 570 | PowerManager.THERMAL_STATUS_LIGHT -> "😥" | ||
| 571 | PowerManager.THERMAL_STATUS_MODERATE -> "🥵" | ||
| 572 | PowerManager.THERMAL_STATUS_SEVERE -> "🔥" | ||
| 573 | PowerManager.THERMAL_STATUS_CRITICAL, | ||
| 574 | PowerManager.THERMAL_STATUS_EMERGENCY, | ||
| 575 | PowerManager.THERMAL_STATUS_SHUTDOWN -> "☢️" | ||
| 576 | |||
| 577 | else -> "🙂" | ||
| 578 | } | ||
| 579 | if (_binding != null) { | ||
| 580 | binding.showThermalsText.text = thermalStatus | ||
| 581 | } | ||
| 582 | thermalStatsUpdateHandler.postDelayed(thermalStatsUpdater!!, 1000) | ||
| 583 | } | ||
| 584 | } | ||
| 585 | thermalStatsUpdateHandler.post(thermalStatsUpdater!!) | ||
| 586 | binding.showThermalsText.visibility = View.VISIBLE | ||
| 587 | } else { | ||
| 588 | if (thermalStatsUpdater != null) { | ||
| 589 | thermalStatsUpdateHandler.removeCallbacks(thermalStatsUpdater!!) | ||
| 590 | } | ||
| 591 | binding.showThermalsText.visibility = View.GONE | ||
| 592 | } | ||
| 593 | } | ||
| 594 | |||
| 556 | @SuppressLint("SourceLockedOrientationActivity") | 595 | @SuppressLint("SourceLockedOrientationActivity") |
| 557 | private fun updateOrientation() { | 596 | private fun updateOrientation() { |
| 558 | emulationActivity?.let { | 597 | emulationActivity?.let { |
| @@ -641,6 +680,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 641 | popup.menu.apply { | 680 | popup.menu.apply { |
| 642 | findItem(R.id.menu_toggle_fps).isChecked = | 681 | findItem(R.id.menu_toggle_fps).isChecked = |
| 643 | BooleanSetting.SHOW_PERFORMANCE_OVERLAY.getBoolean() | 682 | BooleanSetting.SHOW_PERFORMANCE_OVERLAY.getBoolean() |
| 683 | findItem(R.id.thermal_indicator).isChecked = | ||
| 684 | BooleanSetting.SHOW_THERMAL_OVERLAY.getBoolean() | ||
| 644 | findItem(R.id.menu_rel_stick_center).isChecked = | 685 | findItem(R.id.menu_rel_stick_center).isChecked = |
| 645 | BooleanSetting.JOYSTICK_REL_CENTER.getBoolean() | 686 | BooleanSetting.JOYSTICK_REL_CENTER.getBoolean() |
| 646 | findItem(R.id.menu_dpad_slide).isChecked = BooleanSetting.DPAD_SLIDE.getBoolean() | 687 | findItem(R.id.menu_dpad_slide).isChecked = BooleanSetting.DPAD_SLIDE.getBoolean() |
| @@ -660,6 +701,13 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 660 | true | 701 | true |
| 661 | } | 702 | } |
| 662 | 703 | ||
| 704 | R.id.thermal_indicator -> { | ||
| 705 | it.isChecked = !it.isChecked | ||
| 706 | BooleanSetting.SHOW_THERMAL_OVERLAY.setBoolean(it.isChecked) | ||
| 707 | updateThermalOverlay() | ||
| 708 | true | ||
| 709 | } | ||
| 710 | |||
| 663 | R.id.menu_edit_overlay -> { | 711 | R.id.menu_edit_overlay -> { |
| 664 | binding.drawerLayout.close() | 712 | binding.drawerLayout.close() |
| 665 | binding.surfaceInputOverlay.requestFocus() | 713 | binding.surfaceInputOverlay.requestFocus() |
| @@ -850,7 +898,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 850 | right = cutInsets.right | 898 | right = cutInsets.right |
| 851 | } | 899 | } |
| 852 | 900 | ||
| 853 | v.setPadding(left, cutInsets.top, right, 0) | 901 | v.updatePadding(left = left, top = cutInsets.top, right = right) |
| 854 | windowInsets | 902 | windowInsets |
| 855 | } | 903 | } |
| 856 | } | 904 | } |
| @@ -1003,5 +1051,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 1003 | 1051 | ||
| 1004 | companion object { | 1052 | companion object { |
| 1005 | private val perfStatsUpdateHandler = Handler(Looper.myLooper()!!) | 1053 | private val perfStatsUpdateHandler = Handler(Looper.myLooper()!!) |
| 1054 | private val thermalStatsUpdateHandler = Handler(Looper.myLooper()!!) | ||
| 1006 | } | 1055 | } |
| 1007 | } | 1056 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFoldersFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFoldersFragment.kt index 341a37fdb..5c558b1a5 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFoldersFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFoldersFragment.kt | |||
| @@ -26,6 +26,7 @@ import org.yuzu.yuzu_emu.databinding.FragmentFoldersBinding | |||
| 26 | import org.yuzu.yuzu_emu.model.GamesViewModel | 26 | import org.yuzu.yuzu_emu.model.GamesViewModel |
| 27 | import org.yuzu.yuzu_emu.model.HomeViewModel | 27 | import org.yuzu.yuzu_emu.model.HomeViewModel |
| 28 | import org.yuzu.yuzu_emu.ui.main.MainActivity | 28 | import org.yuzu.yuzu_emu.ui.main.MainActivity |
| 29 | import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins | ||
| 29 | 30 | ||
| 30 | class GameFoldersFragment : Fragment() { | 31 | class GameFoldersFragment : Fragment() { |
| 31 | private var _binding: FragmentFoldersBinding? = null | 32 | private var _binding: FragmentFoldersBinding? = null |
| @@ -100,23 +101,16 @@ class GameFoldersFragment : Fragment() { | |||
| 100 | val leftInsets = barInsets.left + cutoutInsets.left | 101 | val leftInsets = barInsets.left + cutoutInsets.left |
| 101 | val rightInsets = barInsets.right + cutoutInsets.right | 102 | val rightInsets = barInsets.right + cutoutInsets.right |
| 102 | 103 | ||
| 103 | val mlpToolbar = binding.toolbarFolders.layoutParams as ViewGroup.MarginLayoutParams | 104 | binding.toolbarFolders.updateMargins(left = leftInsets, right = rightInsets) |
| 104 | mlpToolbar.leftMargin = leftInsets | ||
| 105 | mlpToolbar.rightMargin = rightInsets | ||
| 106 | binding.toolbarFolders.layoutParams = mlpToolbar | ||
| 107 | 105 | ||
| 108 | val fabSpacing = resources.getDimensionPixelSize(R.dimen.spacing_fab) | 106 | val fabSpacing = resources.getDimensionPixelSize(R.dimen.spacing_fab) |
| 109 | val mlpFab = | 107 | binding.buttonAdd.updateMargins( |
| 110 | binding.buttonAdd.layoutParams as ViewGroup.MarginLayoutParams | 108 | left = leftInsets + fabSpacing, |
| 111 | mlpFab.leftMargin = leftInsets + fabSpacing | 109 | right = rightInsets + fabSpacing, |
| 112 | mlpFab.rightMargin = rightInsets + fabSpacing | 110 | bottom = barInsets.bottom + fabSpacing |
| 113 | mlpFab.bottomMargin = barInsets.bottom + fabSpacing | 111 | ) |
| 114 | binding.buttonAdd.layoutParams = mlpFab | 112 | |
| 115 | 113 | binding.listFolders.updateMargins(left = leftInsets, right = rightInsets) | |
| 116 | val mlpListFolders = binding.listFolders.layoutParams as ViewGroup.MarginLayoutParams | ||
| 117 | mlpListFolders.leftMargin = leftInsets | ||
| 118 | mlpListFolders.rightMargin = rightInsets | ||
| 119 | binding.listFolders.layoutParams = mlpListFolders | ||
| 120 | 114 | ||
| 121 | binding.listFolders.updatePadding( | 115 | binding.listFolders.updatePadding( |
| 122 | bottom = barInsets.bottom + | 116 | bottom = barInsets.bottom + |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameInfoFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameInfoFragment.kt index 5aa3f453f..dbd56e84f 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameInfoFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameInfoFragment.kt | |||
| @@ -27,6 +27,7 @@ import org.yuzu.yuzu_emu.databinding.FragmentGameInfoBinding | |||
| 27 | import org.yuzu.yuzu_emu.model.GameVerificationResult | 27 | import org.yuzu.yuzu_emu.model.GameVerificationResult |
| 28 | import org.yuzu.yuzu_emu.model.HomeViewModel | 28 | import org.yuzu.yuzu_emu.model.HomeViewModel |
| 29 | import org.yuzu.yuzu_emu.utils.GameMetadata | 29 | import org.yuzu.yuzu_emu.utils.GameMetadata |
| 30 | import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins | ||
| 30 | 31 | ||
| 31 | class GameInfoFragment : Fragment() { | 32 | class GameInfoFragment : Fragment() { |
| 32 | private var _binding: FragmentGameInfoBinding? = null | 33 | private var _binding: FragmentGameInfoBinding? = null |
| @@ -122,11 +123,13 @@ class GameInfoFragment : Fragment() { | |||
| 122 | titleId = R.string.verify_success, | 123 | titleId = R.string.verify_success, |
| 123 | descriptionId = R.string.operation_completed_successfully | 124 | descriptionId = R.string.operation_completed_successfully |
| 124 | ) | 125 | ) |
| 126 | |||
| 125 | GameVerificationResult.Failed -> | 127 | GameVerificationResult.Failed -> |
| 126 | MessageDialogFragment.newInstance( | 128 | MessageDialogFragment.newInstance( |
| 127 | titleId = R.string.verify_failure, | 129 | titleId = R.string.verify_failure, |
| 128 | descriptionId = R.string.verify_failure_description | 130 | descriptionId = R.string.verify_failure_description |
| 129 | ) | 131 | ) |
| 132 | |||
| 130 | GameVerificationResult.NotImplemented -> | 133 | GameVerificationResult.NotImplemented -> |
| 131 | MessageDialogFragment.newInstance( | 134 | MessageDialogFragment.newInstance( |
| 132 | titleId = R.string.verify_no_result, | 135 | titleId = R.string.verify_no_result, |
| @@ -165,15 +168,8 @@ class GameInfoFragment : Fragment() { | |||
| 165 | val leftInsets = barInsets.left + cutoutInsets.left | 168 | val leftInsets = barInsets.left + cutoutInsets.left |
| 166 | val rightInsets = barInsets.right + cutoutInsets.right | 169 | val rightInsets = barInsets.right + cutoutInsets.right |
| 167 | 170 | ||
| 168 | val mlpToolbar = binding.toolbarInfo.layoutParams as ViewGroup.MarginLayoutParams | 171 | binding.toolbarInfo.updateMargins(left = leftInsets, right = rightInsets) |
| 169 | mlpToolbar.leftMargin = leftInsets | 172 | binding.scrollInfo.updateMargins(left = leftInsets, right = rightInsets) |
| 170 | mlpToolbar.rightMargin = rightInsets | ||
| 171 | binding.toolbarInfo.layoutParams = mlpToolbar | ||
| 172 | |||
| 173 | val mlpScrollAbout = binding.scrollInfo.layoutParams as ViewGroup.MarginLayoutParams | ||
| 174 | mlpScrollAbout.leftMargin = leftInsets | ||
| 175 | mlpScrollAbout.rightMargin = rightInsets | ||
| 176 | binding.scrollInfo.layoutParams = mlpScrollAbout | ||
| 177 | 173 | ||
| 178 | binding.contentInfo.updatePadding(bottom = barInsets.bottom) | 174 | binding.contentInfo.updatePadding(bottom = barInsets.bottom) |
| 179 | 175 | ||
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt index 582df0133..d14b2c634 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt | |||
| @@ -46,6 +46,7 @@ import org.yuzu.yuzu_emu.utils.FileUtil | |||
| 46 | import org.yuzu.yuzu_emu.utils.GameIconUtils | 46 | import org.yuzu.yuzu_emu.utils.GameIconUtils |
| 47 | import org.yuzu.yuzu_emu.utils.GpuDriverHelper | 47 | import org.yuzu.yuzu_emu.utils.GpuDriverHelper |
| 48 | import org.yuzu.yuzu_emu.utils.MemoryUtil | 48 | import org.yuzu.yuzu_emu.utils.MemoryUtil |
| 49 | import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins | ||
| 49 | import java.io.BufferedOutputStream | 50 | import java.io.BufferedOutputStream |
| 50 | import java.io.File | 51 | import java.io.File |
| 51 | 52 | ||
| @@ -320,46 +321,25 @@ class GamePropertiesFragment : Fragment() { | |||
| 320 | 321 | ||
| 321 | val smallLayout = resources.getBoolean(R.bool.small_layout) | 322 | val smallLayout = resources.getBoolean(R.bool.small_layout) |
| 322 | if (smallLayout) { | 323 | if (smallLayout) { |
| 323 | val mlpListAll = | 324 | binding.listAll.updateMargins(left = leftInsets, right = rightInsets) |
| 324 | binding.listAll.layoutParams as ViewGroup.MarginLayoutParams | ||
| 325 | mlpListAll.leftMargin = leftInsets | ||
| 326 | mlpListAll.rightMargin = rightInsets | ||
| 327 | binding.listAll.layoutParams = mlpListAll | ||
| 328 | } else { | 325 | } else { |
| 329 | if (ViewCompat.getLayoutDirection(binding.root) == | 326 | if (ViewCompat.getLayoutDirection(binding.root) == |
| 330 | ViewCompat.LAYOUT_DIRECTION_LTR | 327 | ViewCompat.LAYOUT_DIRECTION_LTR |
| 331 | ) { | 328 | ) { |
| 332 | val mlpListAll = | 329 | binding.listAll.updateMargins(right = rightInsets) |
| 333 | binding.listAll.layoutParams as ViewGroup.MarginLayoutParams | 330 | binding.iconLayout!!.updateMargins(top = barInsets.top, left = leftInsets) |
| 334 | mlpListAll.rightMargin = rightInsets | ||
| 335 | binding.listAll.layoutParams = mlpListAll | ||
| 336 | |||
| 337 | val mlpIconLayout = | ||
| 338 | binding.iconLayout!!.layoutParams as ViewGroup.MarginLayoutParams | ||
| 339 | mlpIconLayout.topMargin = barInsets.top | ||
| 340 | mlpIconLayout.leftMargin = leftInsets | ||
| 341 | binding.iconLayout!!.layoutParams = mlpIconLayout | ||
| 342 | } else { | 331 | } else { |
| 343 | val mlpListAll = | 332 | binding.listAll.updateMargins(left = leftInsets) |
| 344 | binding.listAll.layoutParams as ViewGroup.MarginLayoutParams | 333 | binding.iconLayout!!.updateMargins(top = barInsets.top, right = rightInsets) |
| 345 | mlpListAll.leftMargin = leftInsets | ||
| 346 | binding.listAll.layoutParams = mlpListAll | ||
| 347 | |||
| 348 | val mlpIconLayout = | ||
| 349 | binding.iconLayout!!.layoutParams as ViewGroup.MarginLayoutParams | ||
| 350 | mlpIconLayout.topMargin = barInsets.top | ||
| 351 | mlpIconLayout.rightMargin = rightInsets | ||
| 352 | binding.iconLayout!!.layoutParams = mlpIconLayout | ||
| 353 | } | 334 | } |
| 354 | } | 335 | } |
| 355 | 336 | ||
| 356 | val fabSpacing = resources.getDimensionPixelSize(R.dimen.spacing_fab) | 337 | val fabSpacing = resources.getDimensionPixelSize(R.dimen.spacing_fab) |
| 357 | val mlpFab = | 338 | binding.buttonStart.updateMargins( |
| 358 | binding.buttonStart.layoutParams as ViewGroup.MarginLayoutParams | 339 | left = leftInsets + fabSpacing, |
| 359 | mlpFab.leftMargin = leftInsets + fabSpacing | 340 | right = rightInsets + fabSpacing, |
| 360 | mlpFab.rightMargin = rightInsets + fabSpacing | 341 | bottom = barInsets.bottom + fabSpacing |
| 361 | mlpFab.bottomMargin = barInsets.bottom + fabSpacing | 342 | ) |
| 362 | binding.buttonStart.layoutParams = mlpFab | ||
| 363 | 343 | ||
| 364 | binding.layoutAll.updatePadding( | 344 | binding.layoutAll.updatePadding( |
| 365 | top = barInsets.top, | 345 | top = barInsets.top, |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt index 1f3578b22..87e130d3e 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt | |||
| @@ -12,7 +12,6 @@ import android.provider.DocumentsContract | |||
| 12 | import android.view.LayoutInflater | 12 | import android.view.LayoutInflater |
| 13 | import android.view.View | 13 | import android.view.View |
| 14 | import android.view.ViewGroup | 14 | import android.view.ViewGroup |
| 15 | import android.view.ViewGroup.MarginLayoutParams | ||
| 16 | import android.widget.Toast | 15 | import android.widget.Toast |
| 17 | import androidx.appcompat.app.AppCompatActivity | 16 | import androidx.appcompat.app.AppCompatActivity |
| 18 | import androidx.core.app.ActivityCompat | 17 | import androidx.core.app.ActivityCompat |
| @@ -44,6 +43,7 @@ import org.yuzu.yuzu_emu.ui.main.MainActivity | |||
| 44 | import org.yuzu.yuzu_emu.utils.FileUtil | 43 | import org.yuzu.yuzu_emu.utils.FileUtil |
| 45 | import org.yuzu.yuzu_emu.utils.GpuDriverHelper | 44 | import org.yuzu.yuzu_emu.utils.GpuDriverHelper |
| 46 | import org.yuzu.yuzu_emu.utils.Log | 45 | import org.yuzu.yuzu_emu.utils.Log |
| 46 | import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins | ||
| 47 | 47 | ||
| 48 | class HomeSettingsFragment : Fragment() { | 48 | class HomeSettingsFragment : Fragment() { |
| 49 | private var _binding: FragmentHomeSettingsBinding? = null | 49 | private var _binding: FragmentHomeSettingsBinding? = null |
| @@ -408,10 +408,7 @@ class HomeSettingsFragment : Fragment() { | |||
| 408 | bottom = barInsets.bottom | 408 | bottom = barInsets.bottom |
| 409 | ) | 409 | ) |
| 410 | 410 | ||
| 411 | val mlpScrollSettings = binding.scrollViewSettings.layoutParams as MarginLayoutParams | 411 | binding.scrollViewSettings.updateMargins(left = leftInsets, right = rightInsets) |
| 412 | mlpScrollSettings.leftMargin = leftInsets | ||
| 413 | mlpScrollSettings.rightMargin = rightInsets | ||
| 414 | binding.scrollViewSettings.layoutParams = mlpScrollSettings | ||
| 415 | 412 | ||
| 416 | binding.linearLayoutSettings.updatePadding(bottom = spacingNavigation) | 413 | binding.linearLayoutSettings.updatePadding(bottom = spacingNavigation) |
| 417 | 414 | ||
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt index 7df8e6bf4..63112dc6f 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt | |||
| @@ -34,6 +34,7 @@ import org.yuzu.yuzu_emu.model.TaskState | |||
| 34 | import org.yuzu.yuzu_emu.ui.main.MainActivity | 34 | import org.yuzu.yuzu_emu.ui.main.MainActivity |
| 35 | import org.yuzu.yuzu_emu.utils.DirectoryInitialization | 35 | import org.yuzu.yuzu_emu.utils.DirectoryInitialization |
| 36 | import org.yuzu.yuzu_emu.utils.FileUtil | 36 | import org.yuzu.yuzu_emu.utils.FileUtil |
| 37 | import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins | ||
| 37 | import java.io.BufferedOutputStream | 38 | import java.io.BufferedOutputStream |
| 38 | import java.io.File | 39 | import java.io.File |
| 39 | import java.math.BigInteger | 40 | import java.math.BigInteger |
| @@ -172,16 +173,8 @@ class InstallableFragment : Fragment() { | |||
| 172 | val leftInsets = barInsets.left + cutoutInsets.left | 173 | val leftInsets = barInsets.left + cutoutInsets.left |
| 173 | val rightInsets = barInsets.right + cutoutInsets.right | 174 | val rightInsets = barInsets.right + cutoutInsets.right |
| 174 | 175 | ||
| 175 | val mlpAppBar = binding.toolbarInstallables.layoutParams as ViewGroup.MarginLayoutParams | 176 | binding.toolbarInstallables.updateMargins(left = leftInsets, right = rightInsets) |
| 176 | mlpAppBar.leftMargin = leftInsets | 177 | binding.listInstallables.updateMargins(left = leftInsets, right = rightInsets) |
| 177 | mlpAppBar.rightMargin = rightInsets | ||
| 178 | binding.toolbarInstallables.layoutParams = mlpAppBar | ||
| 179 | |||
| 180 | val mlpScrollAbout = | ||
| 181 | binding.listInstallables.layoutParams as ViewGroup.MarginLayoutParams | ||
| 182 | mlpScrollAbout.leftMargin = leftInsets | ||
| 183 | mlpScrollAbout.rightMargin = rightInsets | ||
| 184 | binding.listInstallables.layoutParams = mlpScrollAbout | ||
| 185 | 178 | ||
| 186 | binding.listInstallables.updatePadding(bottom = barInsets.bottom) | 179 | binding.listInstallables.updatePadding(bottom = barInsets.bottom) |
| 187 | 180 | ||
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LicensesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LicensesFragment.kt index b6e9129f7..f17f621f8 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LicensesFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LicensesFragment.kt | |||
| @@ -7,7 +7,6 @@ import android.os.Bundle | |||
| 7 | import android.view.LayoutInflater | 7 | import android.view.LayoutInflater |
| 8 | import android.view.View | 8 | import android.view.View |
| 9 | import android.view.ViewGroup | 9 | import android.view.ViewGroup |
| 10 | import android.view.ViewGroup.MarginLayoutParams | ||
| 11 | import androidx.appcompat.app.AppCompatActivity | 10 | import androidx.appcompat.app.AppCompatActivity |
| 12 | import androidx.core.view.ViewCompat | 11 | import androidx.core.view.ViewCompat |
| 13 | import androidx.core.view.WindowInsetsCompat | 12 | import androidx.core.view.WindowInsetsCompat |
| @@ -22,6 +21,7 @@ import org.yuzu.yuzu_emu.adapters.LicenseAdapter | |||
| 22 | import org.yuzu.yuzu_emu.databinding.FragmentLicensesBinding | 21 | import org.yuzu.yuzu_emu.databinding.FragmentLicensesBinding |
| 23 | import org.yuzu.yuzu_emu.model.HomeViewModel | 22 | import org.yuzu.yuzu_emu.model.HomeViewModel |
| 24 | import org.yuzu.yuzu_emu.model.License | 23 | import org.yuzu.yuzu_emu.model.License |
| 24 | import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins | ||
| 25 | 25 | ||
| 26 | class LicensesFragment : Fragment() { | 26 | class LicensesFragment : Fragment() { |
| 27 | private var _binding: FragmentLicensesBinding? = null | 27 | private var _binding: FragmentLicensesBinding? = null |
| @@ -122,15 +122,8 @@ class LicensesFragment : Fragment() { | |||
| 122 | val leftInsets = barInsets.left + cutoutInsets.left | 122 | val leftInsets = barInsets.left + cutoutInsets.left |
| 123 | val rightInsets = barInsets.right + cutoutInsets.right | 123 | val rightInsets = barInsets.right + cutoutInsets.right |
| 124 | 124 | ||
| 125 | val mlpAppBar = binding.appbarLicenses.layoutParams as MarginLayoutParams | 125 | binding.appbarLicenses.updateMargins(left = leftInsets, right = rightInsets) |
| 126 | mlpAppBar.leftMargin = leftInsets | 126 | binding.listLicenses.updateMargins(left = leftInsets, right = rightInsets) |
| 127 | mlpAppBar.rightMargin = rightInsets | ||
| 128 | binding.appbarLicenses.layoutParams = mlpAppBar | ||
| 129 | |||
| 130 | val mlpScrollAbout = binding.listLicenses.layoutParams as MarginLayoutParams | ||
| 131 | mlpScrollAbout.leftMargin = leftInsets | ||
| 132 | mlpScrollAbout.rightMargin = rightInsets | ||
| 133 | binding.listLicenses.layoutParams = mlpScrollAbout | ||
| 134 | 127 | ||
| 135 | binding.listLicenses.updatePadding(bottom = barInsets.bottom) | 128 | binding.listLicenses.updatePadding(bottom = barInsets.bottom) |
| 136 | 129 | ||
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt index f95d545bf..a135b80b4 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt | |||
| @@ -29,6 +29,7 @@ import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem | |||
| 29 | import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter | 29 | import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter |
| 30 | import org.yuzu.yuzu_emu.model.SettingsViewModel | 30 | import org.yuzu.yuzu_emu.model.SettingsViewModel |
| 31 | import org.yuzu.yuzu_emu.utils.NativeConfig | 31 | import org.yuzu.yuzu_emu.utils.NativeConfig |
| 32 | import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins | ||
| 32 | 33 | ||
| 33 | class SettingsSearchFragment : Fragment() { | 34 | class SettingsSearchFragment : Fragment() { |
| 34 | private var _binding: FragmentSettingsSearchBinding? = null | 35 | private var _binding: FragmentSettingsSearchBinding? = null |
| @@ -174,15 +175,14 @@ class SettingsSearchFragment : Fragment() { | |||
| 174 | bottom = barInsets.bottom | 175 | bottom = barInsets.bottom |
| 175 | ) | 176 | ) |
| 176 | 177 | ||
| 177 | val mlpSettingsList = binding.settingsList.layoutParams as ViewGroup.MarginLayoutParams | 178 | binding.settingsList.updateMargins( |
| 178 | mlpSettingsList.leftMargin = leftInsets + sideMargin | 179 | left = leftInsets + sideMargin, |
| 179 | mlpSettingsList.rightMargin = rightInsets + sideMargin | 180 | right = rightInsets + sideMargin |
| 180 | binding.settingsList.layoutParams = mlpSettingsList | 181 | ) |
| 181 | 182 | binding.divider.updateMargins( | |
| 182 | val mlpDivider = binding.divider.layoutParams as ViewGroup.MarginLayoutParams | 183 | left = leftInsets + sideMargin, |
| 183 | mlpDivider.leftMargin = leftInsets + sideMargin | 184 | right = rightInsets + sideMargin |
| 184 | mlpDivider.rightMargin = rightInsets + sideMargin | 185 | ) |
| 185 | binding.divider.layoutParams = mlpDivider | ||
| 186 | 186 | ||
| 187 | windowInsets | 187 | windowInsets |
| 188 | } | 188 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt index 54380323e..23ca49b53 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt | |||
| @@ -8,7 +8,6 @@ import android.os.Bundle | |||
| 8 | import android.view.LayoutInflater | 8 | import android.view.LayoutInflater |
| 9 | import android.view.View | 9 | import android.view.View |
| 10 | import android.view.ViewGroup | 10 | import android.view.ViewGroup |
| 11 | import android.view.ViewGroup.MarginLayoutParams | ||
| 12 | import androidx.appcompat.app.AppCompatActivity | 11 | import androidx.appcompat.app.AppCompatActivity |
| 13 | import androidx.core.view.ViewCompat | 12 | import androidx.core.view.ViewCompat |
| 14 | import androidx.core.view.WindowInsetsCompat | 13 | import androidx.core.view.WindowInsetsCompat |
| @@ -27,6 +26,7 @@ import org.yuzu.yuzu_emu.databinding.FragmentGamesBinding | |||
| 27 | import org.yuzu.yuzu_emu.layout.AutofitGridLayoutManager | 26 | import org.yuzu.yuzu_emu.layout.AutofitGridLayoutManager |
| 28 | import org.yuzu.yuzu_emu.model.GamesViewModel | 27 | import org.yuzu.yuzu_emu.model.GamesViewModel |
| 29 | import org.yuzu.yuzu_emu.model.HomeViewModel | 28 | import org.yuzu.yuzu_emu.model.HomeViewModel |
| 29 | import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins | ||
| 30 | 30 | ||
| 31 | class GamesFragment : Fragment() { | 31 | class GamesFragment : Fragment() { |
| 32 | private var _binding: FragmentGamesBinding? = null | 32 | private var _binding: FragmentGamesBinding? = null |
| @@ -169,15 +169,16 @@ class GamesFragment : Fragment() { | |||
| 169 | 169 | ||
| 170 | val leftInsets = barInsets.left + cutoutInsets.left | 170 | val leftInsets = barInsets.left + cutoutInsets.left |
| 171 | val rightInsets = barInsets.right + cutoutInsets.right | 171 | val rightInsets = barInsets.right + cutoutInsets.right |
| 172 | val mlpSwipe = binding.swipeRefresh.layoutParams as MarginLayoutParams | 172 | val left: Int |
| 173 | val right: Int | ||
| 173 | if (ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_LTR) { | 174 | if (ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_LTR) { |
| 174 | mlpSwipe.leftMargin = leftInsets + spacingNavigationRail | 175 | left = leftInsets + spacingNavigationRail |
| 175 | mlpSwipe.rightMargin = rightInsets | 176 | right = rightInsets |
| 176 | } else { | 177 | } else { |
| 177 | mlpSwipe.leftMargin = leftInsets | 178 | left = leftInsets |
| 178 | mlpSwipe.rightMargin = rightInsets + spacingNavigationRail | 179 | right = rightInsets + spacingNavigationRail |
| 179 | } | 180 | } |
| 180 | binding.swipeRefresh.layoutParams = mlpSwipe | 181 | binding.swipeRefresh.updateMargins(left = left, right = right) |
| 181 | 182 | ||
| 182 | binding.noticeText.updatePadding(bottom = spacingNavigation) | 183 | binding.noticeText.updatePadding(bottom = spacingNavigation) |
| 183 | 184 | ||
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt index b3967d294..4df4ac4c6 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt | |||
| @@ -34,7 +34,6 @@ import kotlinx.coroutines.launch | |||
| 34 | import org.yuzu.yuzu_emu.HomeNavigationDirections | 34 | import org.yuzu.yuzu_emu.HomeNavigationDirections |
| 35 | import org.yuzu.yuzu_emu.NativeLibrary | 35 | import org.yuzu.yuzu_emu.NativeLibrary |
| 36 | import org.yuzu.yuzu_emu.R | 36 | import org.yuzu.yuzu_emu.R |
| 37 | import org.yuzu.yuzu_emu.activities.EmulationActivity | ||
| 38 | import org.yuzu.yuzu_emu.databinding.ActivityMainBinding | 37 | import org.yuzu.yuzu_emu.databinding.ActivityMainBinding |
| 39 | import org.yuzu.yuzu_emu.features.settings.model.Settings | 38 | import org.yuzu.yuzu_emu.features.settings.model.Settings |
| 40 | import org.yuzu.yuzu_emu.fragments.AddGameFolderDialogFragment | 39 | import org.yuzu.yuzu_emu.fragments.AddGameFolderDialogFragment |
| @@ -177,9 +176,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider { | |||
| 177 | } | 176 | } |
| 178 | } | 177 | } |
| 179 | 178 | ||
| 180 | // Dismiss previous notifications (should not happen unless a crash occurred) | ||
| 181 | EmulationActivity.stopForegroundService(this) | ||
| 182 | |||
| 183 | setInsets() | 179 | setInsets() |
| 184 | } | 180 | } |
| 185 | 181 | ||
| @@ -298,11 +294,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider { | |||
| 298 | super.onResume() | 294 | super.onResume() |
| 299 | } | 295 | } |
| 300 | 296 | ||
| 301 | override fun onDestroy() { | ||
| 302 | EmulationActivity.stopForegroundService(this) | ||
| 303 | super.onDestroy() | ||
| 304 | } | ||
| 305 | |||
| 306 | private fun setInsets() = | 297 | private fun setInsets() = |
| 307 | ViewCompat.setOnApplyWindowInsetsListener( | 298 | ViewCompat.setOnApplyWindowInsetsListener( |
| 308 | binding.root | 299 | binding.root |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ForegroundService.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ForegroundService.kt deleted file mode 100644 index 086d17606..000000000 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ForegroundService.kt +++ /dev/null | |||
| @@ -1,70 +0,0 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | package org.yuzu.yuzu_emu.utils | ||
| 5 | |||
| 6 | import android.app.PendingIntent | ||
| 7 | import android.app.Service | ||
| 8 | import android.content.Intent | ||
| 9 | import android.os.IBinder | ||
| 10 | import androidx.core.app.NotificationCompat | ||
| 11 | import androidx.core.app.NotificationManagerCompat | ||
| 12 | import org.yuzu.yuzu_emu.R | ||
| 13 | import org.yuzu.yuzu_emu.activities.EmulationActivity | ||
| 14 | |||
| 15 | /** | ||
| 16 | * A service that shows a permanent notification in the background to avoid the app getting | ||
| 17 | * cleared from memory by the system. | ||
| 18 | */ | ||
| 19 | class ForegroundService : Service() { | ||
| 20 | companion object { | ||
| 21 | const val EMULATION_RUNNING_NOTIFICATION = 0x1000 | ||
| 22 | |||
| 23 | const val ACTION_STOP = "stop" | ||
| 24 | } | ||
| 25 | |||
| 26 | private fun showRunningNotification() { | ||
| 27 | // Intent is used to resume emulation if the notification is clicked | ||
| 28 | val contentIntent = PendingIntent.getActivity( | ||
| 29 | this, | ||
| 30 | 0, | ||
| 31 | Intent(this, EmulationActivity::class.java), | ||
| 32 | PendingIntent.FLAG_IMMUTABLE | ||
| 33 | ) | ||
| 34 | val builder = | ||
| 35 | NotificationCompat.Builder(this, getString(R.string.emulation_notification_channel_id)) | ||
| 36 | .setSmallIcon(R.drawable.ic_stat_notification_logo) | ||
| 37 | .setContentTitle(getString(R.string.app_name)) | ||
| 38 | .setContentText(getString(R.string.emulation_notification_running)) | ||
| 39 | .setPriority(NotificationCompat.PRIORITY_LOW) | ||
| 40 | .setOngoing(true) | ||
| 41 | .setVibrate(null) | ||
| 42 | .setSound(null) | ||
| 43 | .setContentIntent(contentIntent) | ||
| 44 | startForeground(EMULATION_RUNNING_NOTIFICATION, builder.build()) | ||
| 45 | } | ||
| 46 | |||
| 47 | override fun onBind(intent: Intent): IBinder? { | ||
| 48 | return null | ||
| 49 | } | ||
| 50 | |||
| 51 | override fun onCreate() { | ||
| 52 | showRunningNotification() | ||
| 53 | } | ||
| 54 | |||
| 55 | override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { | ||
| 56 | if (intent == null) { | ||
| 57 | return START_NOT_STICKY | ||
| 58 | } | ||
| 59 | if (intent.action == ACTION_STOP) { | ||
| 60 | NotificationManagerCompat.from(this).cancel(EMULATION_RUNNING_NOTIFICATION) | ||
| 61 | stopForeground(STOP_FOREGROUND_REMOVE) | ||
| 62 | stopSelfResult(startId) | ||
| 63 | } | ||
| 64 | return START_STICKY | ||
| 65 | } | ||
| 66 | |||
| 67 | override fun onDestroy() { | ||
| 68 | NotificationManagerCompat.from(this).cancel(EMULATION_RUNNING_NOTIFICATION) | ||
| 69 | } | ||
| 70 | } | ||
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ViewUtils.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ViewUtils.kt index f9a3e4126..ffbfa9337 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ViewUtils.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ViewUtils.kt | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | package org.yuzu.yuzu_emu.utils | 4 | package org.yuzu.yuzu_emu.utils |
| 5 | 5 | ||
| 6 | import android.view.View | 6 | import android.view.View |
| 7 | import android.view.ViewGroup | ||
| 7 | 8 | ||
| 8 | object ViewUtils { | 9 | object ViewUtils { |
| 9 | fun showView(view: View, length: Long = 300) { | 10 | fun showView(view: View, length: Long = 300) { |
| @@ -32,4 +33,28 @@ object ViewUtils { | |||
| 32 | view.visibility = View.INVISIBLE | 33 | view.visibility = View.INVISIBLE |
| 33 | }.start() | 34 | }.start() |
| 34 | } | 35 | } |
| 36 | |||
| 37 | fun View.updateMargins( | ||
| 38 | left: Int = -1, | ||
| 39 | top: Int = -1, | ||
| 40 | right: Int = -1, | ||
| 41 | bottom: Int = -1 | ||
| 42 | ) { | ||
| 43 | val layoutParams = this.layoutParams as ViewGroup.MarginLayoutParams | ||
| 44 | layoutParams.apply { | ||
| 45 | if (left != -1) { | ||
| 46 | leftMargin = left | ||
| 47 | } | ||
| 48 | if (top != -1) { | ||
| 49 | topMargin = top | ||
| 50 | } | ||
| 51 | if (right != -1) { | ||
| 52 | rightMargin = right | ||
| 53 | } | ||
| 54 | if (bottom != -1) { | ||
| 55 | bottomMargin = bottom | ||
| 56 | } | ||
| 57 | } | ||
| 58 | this.layoutParams = layoutParams | ||
| 59 | } | ||
| 35 | } | 60 | } |
diff --git a/src/android/app/src/main/jni/CMakeLists.txt b/src/android/app/src/main/jni/CMakeLists.txt index abc6055ab..20b319c12 100644 --- a/src/android/app/src/main/jni/CMakeLists.txt +++ b/src/android/app/src/main/jni/CMakeLists.txt | |||
| @@ -2,14 +2,8 @@ | |||
| 2 | # SPDX-License-Identifier: GPL-3.0-or-later | 2 | # SPDX-License-Identifier: GPL-3.0-or-later |
| 3 | 3 | ||
| 4 | add_library(yuzu-android SHARED | 4 | add_library(yuzu-android SHARED |
| 5 | android_common/android_common.cpp | ||
| 6 | android_common/android_common.h | ||
| 7 | applets/software_keyboard.cpp | ||
| 8 | applets/software_keyboard.h | ||
| 9 | emu_window/emu_window.cpp | 5 | emu_window/emu_window.cpp |
| 10 | emu_window/emu_window.h | 6 | emu_window/emu_window.h |
| 11 | id_cache.cpp | ||
| 12 | id_cache.h | ||
| 13 | native.cpp | 7 | native.cpp |
| 14 | native.h | 8 | native.h |
| 15 | native_config.cpp | 9 | native_config.cpp |
diff --git a/src/android/app/src/main/jni/android_settings.h b/src/android/app/src/main/jni/android_settings.h index cf93304da..4a3bc8e53 100644 --- a/src/android/app/src/main/jni/android_settings.h +++ b/src/android/app/src/main/jni/android_settings.h | |||
| @@ -60,6 +60,8 @@ struct Values { | |||
| 60 | Settings::Category::Overlay}; | 60 | Settings::Category::Overlay}; |
| 61 | Settings::Setting<bool> show_performance_overlay{linkage, true, "show_performance_overlay", | 61 | Settings::Setting<bool> show_performance_overlay{linkage, true, "show_performance_overlay", |
| 62 | Settings::Category::Overlay}; | 62 | Settings::Category::Overlay}; |
| 63 | Settings::Setting<bool> show_thermal_overlay{linkage, false, "show_thermal_overlay", | ||
| 64 | Settings::Category::Overlay}; | ||
| 63 | Settings::Setting<bool> show_input_overlay{linkage, true, "show_input_overlay", | 65 | Settings::Setting<bool> show_input_overlay{linkage, true, "show_input_overlay", |
| 64 | Settings::Category::Overlay}; | 66 | Settings::Category::Overlay}; |
| 65 | Settings::Setting<bool> touchscreen{linkage, true, "touchscreen", Settings::Category::Overlay}; | 67 | Settings::Setting<bool> touchscreen{linkage, true, "touchscreen", Settings::Category::Overlay}; |
diff --git a/src/android/app/src/main/jni/emu_window/emu_window.cpp b/src/android/app/src/main/jni/emu_window/emu_window.cpp index c4f631924..c927cddda 100644 --- a/src/android/app/src/main/jni/emu_window/emu_window.cpp +++ b/src/android/app/src/main/jni/emu_window/emu_window.cpp | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | #include <android/native_window_jni.h> | 4 | #include <android/native_window_jni.h> |
| 5 | 5 | ||
| 6 | #include "common/android/id_cache.h" | ||
| 6 | #include "common/logging/log.h" | 7 | #include "common/logging/log.h" |
| 7 | #include "input_common/drivers/touch_screen.h" | 8 | #include "input_common/drivers/touch_screen.h" |
| 8 | #include "input_common/drivers/virtual_amiibo.h" | 9 | #include "input_common/drivers/virtual_amiibo.h" |
| @@ -60,7 +61,8 @@ void EmuWindow_Android::OnRemoveNfcTag() { | |||
| 60 | 61 | ||
| 61 | void EmuWindow_Android::OnFrameDisplayed() { | 62 | void EmuWindow_Android::OnFrameDisplayed() { |
| 62 | if (!m_first_frame) { | 63 | if (!m_first_frame) { |
| 63 | EmulationSession::GetInstance().OnEmulationStarted(); | 64 | Common::Android::RunJNIOnFiber<void>( |
| 65 | [&](JNIEnv* env) { EmulationSession::GetInstance().OnEmulationStarted(); }); | ||
| 64 | m_first_frame = true; | 66 | m_first_frame = true; |
| 65 | } | 67 | } |
| 66 | } | 68 | } |
diff --git a/src/android/app/src/main/jni/game_metadata.cpp b/src/android/app/src/main/jni/game_metadata.cpp index 8f0da1413..c33763b47 100644 --- a/src/android/app/src/main/jni/game_metadata.cpp +++ b/src/android/app/src/main/jni/game_metadata.cpp | |||
| @@ -1,13 +1,12 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include "common/android/android_common.h" | ||
| 4 | #include "core/core.h" | 5 | #include "core/core.h" |
| 5 | #include "core/file_sys/fs_filesystem.h" | 6 | #include "core/file_sys/fs_filesystem.h" |
| 6 | #include "core/file_sys/patch_manager.h" | 7 | #include "core/file_sys/patch_manager.h" |
| 7 | #include "core/loader/loader.h" | 8 | #include "core/loader/loader.h" |
| 8 | #include "core/loader/nro.h" | 9 | #include "core/loader/nro.h" |
| 9 | #include "jni.h" | ||
| 10 | #include "jni/android_common/android_common.h" | ||
| 11 | #include "native.h" | 10 | #include "native.h" |
| 12 | 11 | ||
| 13 | struct RomMetadata { | 12 | struct RomMetadata { |
| @@ -79,7 +78,7 @@ extern "C" { | |||
| 79 | jboolean Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIsValid(JNIEnv* env, jobject obj, | 78 | jboolean Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIsValid(JNIEnv* env, jobject obj, |
| 80 | jstring jpath) { | 79 | jstring jpath) { |
| 81 | const auto file = EmulationSession::GetInstance().System().GetFilesystem()->OpenFile( | 80 | const auto file = EmulationSession::GetInstance().System().GetFilesystem()->OpenFile( |
| 82 | GetJString(env, jpath), FileSys::OpenMode::Read); | 81 | Common::Android::GetJString(env, jpath), FileSys::OpenMode::Read); |
| 83 | if (!file) { | 82 | if (!file) { |
| 84 | return false; | 83 | return false; |
| 85 | } | 84 | } |
| @@ -104,27 +103,31 @@ jboolean Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIsValid(JNIEnv* env, jobj | |||
| 104 | 103 | ||
| 105 | jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getTitle(JNIEnv* env, jobject obj, | 104 | jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getTitle(JNIEnv* env, jobject obj, |
| 106 | jstring jpath) { | 105 | jstring jpath) { |
| 107 | return ToJString(env, GetRomMetadata(GetJString(env, jpath)).title); | 106 | return Common::Android::ToJString( |
| 107 | env, GetRomMetadata(Common::Android::GetJString(env, jpath)).title); | ||
| 108 | } | 108 | } |
| 109 | 109 | ||
| 110 | jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getProgramId(JNIEnv* env, jobject obj, | 110 | jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getProgramId(JNIEnv* env, jobject obj, |
| 111 | jstring jpath) { | 111 | jstring jpath) { |
| 112 | return ToJString(env, std::to_string(GetRomMetadata(GetJString(env, jpath)).programId)); | 112 | return Common::Android::ToJString( |
| 113 | env, std::to_string(GetRomMetadata(Common::Android::GetJString(env, jpath)).programId)); | ||
| 113 | } | 114 | } |
| 114 | 115 | ||
| 115 | jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getDeveloper(JNIEnv* env, jobject obj, | 116 | jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getDeveloper(JNIEnv* env, jobject obj, |
| 116 | jstring jpath) { | 117 | jstring jpath) { |
| 117 | return ToJString(env, GetRomMetadata(GetJString(env, jpath)).developer); | 118 | return Common::Android::ToJString( |
| 119 | env, GetRomMetadata(Common::Android::GetJString(env, jpath)).developer); | ||
| 118 | } | 120 | } |
| 119 | 121 | ||
| 120 | jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getVersion(JNIEnv* env, jobject obj, | 122 | jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getVersion(JNIEnv* env, jobject obj, |
| 121 | jstring jpath, jboolean jreload) { | 123 | jstring jpath, jboolean jreload) { |
| 122 | return ToJString(env, GetRomMetadata(GetJString(env, jpath), jreload).version); | 124 | return Common::Android::ToJString( |
| 125 | env, GetRomMetadata(Common::Android::GetJString(env, jpath), jreload).version); | ||
| 123 | } | 126 | } |
| 124 | 127 | ||
| 125 | jbyteArray Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIcon(JNIEnv* env, jobject obj, | 128 | jbyteArray Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIcon(JNIEnv* env, jobject obj, |
| 126 | jstring jpath) { | 129 | jstring jpath) { |
| 127 | auto icon_data = GetRomMetadata(GetJString(env, jpath)).icon; | 130 | auto icon_data = GetRomMetadata(Common::Android::GetJString(env, jpath)).icon; |
| 128 | jbyteArray icon = env->NewByteArray(static_cast<jsize>(icon_data.size())); | 131 | jbyteArray icon = env->NewByteArray(static_cast<jsize>(icon_data.size())); |
| 129 | env->SetByteArrayRegion(icon, 0, env->GetArrayLength(icon), | 132 | env->SetByteArrayRegion(icon, 0, env->GetArrayLength(icon), |
| 130 | reinterpret_cast<jbyte*>(icon_data.data())); | 133 | reinterpret_cast<jbyte*>(icon_data.data())); |
| @@ -133,7 +136,8 @@ jbyteArray Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIcon(JNIEnv* env, jobje | |||
| 133 | 136 | ||
| 134 | jboolean Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIsHomebrew(JNIEnv* env, jobject obj, | 137 | jboolean Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIsHomebrew(JNIEnv* env, jobject obj, |
| 135 | jstring jpath) { | 138 | jstring jpath) { |
| 136 | return static_cast<jboolean>(GetRomMetadata(GetJString(env, jpath)).isHomebrew); | 139 | return static_cast<jboolean>( |
| 140 | GetRomMetadata(Common::Android::GetJString(env, jpath)).isHomebrew); | ||
| 137 | } | 141 | } |
| 138 | 142 | ||
| 139 | void Java_org_yuzu_yuzu_1emu_utils_GameMetadata_resetMetadata(JNIEnv* env, jobject obj) { | 143 | void Java_org_yuzu_yuzu_1emu_utils_GameMetadata_resetMetadata(JNIEnv* env, jobject obj) { |
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 654510129..4acc60956 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp | |||
| @@ -20,6 +20,8 @@ | |||
| 20 | #include <frontend_common/content_manager.h> | 20 | #include <frontend_common/content_manager.h> |
| 21 | #include <jni.h> | 21 | #include <jni.h> |
| 22 | 22 | ||
| 23 | #include "common/android/android_common.h" | ||
| 24 | #include "common/android/id_cache.h" | ||
| 23 | #include "common/detached_tasks.h" | 25 | #include "common/detached_tasks.h" |
| 24 | #include "common/dynamic_library.h" | 26 | #include "common/dynamic_library.h" |
| 25 | #include "common/fs/path_util.h" | 27 | #include "common/fs/path_util.h" |
| @@ -57,8 +59,6 @@ | |||
| 57 | #include "hid_core/frontend/emulated_controller.h" | 59 | #include "hid_core/frontend/emulated_controller.h" |
| 58 | #include "hid_core/hid_core.h" | 60 | #include "hid_core/hid_core.h" |
| 59 | #include "hid_core/hid_types.h" | 61 | #include "hid_core/hid_types.h" |
| 60 | #include "jni/android_common/android_common.h" | ||
| 61 | #include "jni/id_cache.h" | ||
| 62 | #include "jni/native.h" | 62 | #include "jni/native.h" |
| 63 | #include "video_core/renderer_base.h" | 63 | #include "video_core/renderer_base.h" |
| 64 | #include "video_core/renderer_vulkan/renderer_vulkan.h" | 64 | #include "video_core/renderer_vulkan/renderer_vulkan.h" |
| @@ -228,7 +228,7 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string | |||
| 228 | std::make_unique<EmuWindow_Android>(&m_input_subsystem, m_native_window, m_vulkan_library); | 228 | std::make_unique<EmuWindow_Android>(&m_input_subsystem, m_native_window, m_vulkan_library); |
| 229 | 229 | ||
| 230 | // Initialize system. | 230 | // Initialize system. |
| 231 | jauto android_keyboard = std::make_unique<SoftwareKeyboard::AndroidKeyboard>(); | 231 | jauto android_keyboard = std::make_unique<Common::Android::SoftwareKeyboard::AndroidKeyboard>(); |
| 232 | m_software_keyboard = android_keyboard.get(); | 232 | m_software_keyboard = android_keyboard.get(); |
| 233 | m_system.SetShuttingDown(false); | 233 | m_system.SetShuttingDown(false); |
| 234 | m_system.ApplySettings(); | 234 | m_system.ApplySettings(); |
| @@ -411,37 +411,39 @@ void EmulationSession::OnGamepadDisconnectEvent([[maybe_unused]] int index) { | |||
| 411 | controller->Disconnect(); | 411 | controller->Disconnect(); |
| 412 | } | 412 | } |
| 413 | 413 | ||
| 414 | SoftwareKeyboard::AndroidKeyboard* EmulationSession::SoftwareKeyboard() { | 414 | Common::Android::SoftwareKeyboard::AndroidKeyboard* EmulationSession::SoftwareKeyboard() { |
| 415 | return m_software_keyboard; | 415 | return m_software_keyboard; |
| 416 | } | 416 | } |
| 417 | 417 | ||
| 418 | void EmulationSession::LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, | 418 | void EmulationSession::LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, |
| 419 | int max) { | 419 | int max) { |
| 420 | JNIEnv* env = IDCache::GetEnvForThread(); | 420 | JNIEnv* env = Common::Android::GetEnvForThread(); |
| 421 | env->CallStaticVoidMethod(IDCache::GetDiskCacheProgressClass(), | 421 | env->CallStaticVoidMethod(Common::Android::GetDiskCacheProgressClass(), |
| 422 | IDCache::GetDiskCacheLoadProgress(), static_cast<jint>(stage), | 422 | Common::Android::GetDiskCacheLoadProgress(), static_cast<jint>(stage), |
| 423 | static_cast<jint>(progress), static_cast<jint>(max)); | 423 | static_cast<jint>(progress), static_cast<jint>(max)); |
| 424 | } | 424 | } |
| 425 | 425 | ||
| 426 | void EmulationSession::OnEmulationStarted() { | 426 | void EmulationSession::OnEmulationStarted() { |
| 427 | JNIEnv* env = IDCache::GetEnvForThread(); | 427 | JNIEnv* env = Common::Android::GetEnvForThread(); |
| 428 | env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), IDCache::GetOnEmulationStarted()); | 428 | env->CallStaticVoidMethod(Common::Android::GetNativeLibraryClass(), |
| 429 | Common::Android::GetOnEmulationStarted()); | ||
| 429 | } | 430 | } |
| 430 | 431 | ||
| 431 | void EmulationSession::OnEmulationStopped(Core::SystemResultStatus result) { | 432 | void EmulationSession::OnEmulationStopped(Core::SystemResultStatus result) { |
| 432 | JNIEnv* env = IDCache::GetEnvForThread(); | 433 | JNIEnv* env = Common::Android::GetEnvForThread(); |
| 433 | env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), IDCache::GetOnEmulationStopped(), | 434 | env->CallStaticVoidMethod(Common::Android::GetNativeLibraryClass(), |
| 434 | static_cast<jint>(result)); | 435 | Common::Android::GetOnEmulationStopped(), static_cast<jint>(result)); |
| 435 | } | 436 | } |
| 436 | 437 | ||
| 437 | void EmulationSession::ChangeProgram(std::size_t program_index) { | 438 | void EmulationSession::ChangeProgram(std::size_t program_index) { |
| 438 | JNIEnv* env = IDCache::GetEnvForThread(); | 439 | JNIEnv* env = Common::Android::GetEnvForThread(); |
| 439 | env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), IDCache::GetOnProgramChanged(), | 440 | env->CallStaticVoidMethod(Common::Android::GetNativeLibraryClass(), |
| 441 | Common::Android::GetOnProgramChanged(), | ||
| 440 | static_cast<jint>(program_index)); | 442 | static_cast<jint>(program_index)); |
| 441 | } | 443 | } |
| 442 | 444 | ||
| 443 | u64 EmulationSession::GetProgramId(JNIEnv* env, jstring jprogramId) { | 445 | u64 EmulationSession::GetProgramId(JNIEnv* env, jstring jprogramId) { |
| 444 | auto program_id_string = GetJString(env, jprogramId); | 446 | auto program_id_string = Common::Android::GetJString(env, jprogramId); |
| 445 | try { | 447 | try { |
| 446 | return std::stoull(program_id_string); | 448 | return std::stoull(program_id_string); |
| 447 | } catch (...) { | 449 | } catch (...) { |
| @@ -491,7 +493,7 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceDestroyed(JNIEnv* env, jobject | |||
| 491 | 493 | ||
| 492 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_setAppDirectory(JNIEnv* env, jobject instance, | 494 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_setAppDirectory(JNIEnv* env, jobject instance, |
| 493 | [[maybe_unused]] jstring j_directory) { | 495 | [[maybe_unused]] jstring j_directory) { |
| 494 | Common::FS::SetAppDirectory(GetJString(env, j_directory)); | 496 | Common::FS::SetAppDirectory(Common::Android::GetJString(env, j_directory)); |
| 495 | } | 497 | } |
| 496 | 498 | ||
| 497 | int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject instance, | 499 | int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject instance, |
| @@ -501,21 +503,22 @@ int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject | |||
| 501 | jlambdaClass, "invoke", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); | 503 | jlambdaClass, "invoke", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); |
| 502 | const auto callback = [env, jcallback, jlambdaInvokeMethod](size_t max, size_t progress) { | 504 | const auto callback = [env, jcallback, jlambdaInvokeMethod](size_t max, size_t progress) { |
| 503 | auto jwasCancelled = env->CallObjectMethod(jcallback, jlambdaInvokeMethod, | 505 | auto jwasCancelled = env->CallObjectMethod(jcallback, jlambdaInvokeMethod, |
| 504 | ToJDouble(env, max), ToJDouble(env, progress)); | 506 | Common::Android::ToJDouble(env, max), |
| 505 | return GetJBoolean(env, jwasCancelled); | 507 | Common::Android::ToJDouble(env, progress)); |
| 508 | return Common::Android::GetJBoolean(env, jwasCancelled); | ||
| 506 | }; | 509 | }; |
| 507 | 510 | ||
| 508 | return static_cast<int>( | 511 | return static_cast<int>( |
| 509 | ContentManager::InstallNSP(EmulationSession::GetInstance().System(), | 512 | ContentManager::InstallNSP(EmulationSession::GetInstance().System(), |
| 510 | *EmulationSession::GetInstance().System().GetFilesystem(), | 513 | *EmulationSession::GetInstance().System().GetFilesystem(), |
| 511 | GetJString(env, j_file), callback)); | 514 | Common::Android::GetJString(env, j_file), callback)); |
| 512 | } | 515 | } |
| 513 | 516 | ||
| 514 | jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* env, jobject jobj, | 517 | jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* env, jobject jobj, |
| 515 | jstring jprogramId, | 518 | jstring jprogramId, |
| 516 | jstring jupdatePath) { | 519 | jstring jupdatePath) { |
| 517 | u64 program_id = EmulationSession::GetProgramId(env, jprogramId); | 520 | u64 program_id = EmulationSession::GetProgramId(env, jprogramId); |
| 518 | std::string updatePath = GetJString(env, jupdatePath); | 521 | std::string updatePath = Common::Android::GetJString(env, jupdatePath); |
| 519 | std::shared_ptr<FileSys::NSP> nsp = std::make_shared<FileSys::NSP>( | 522 | std::shared_ptr<FileSys::NSP> nsp = std::make_shared<FileSys::NSP>( |
| 520 | EmulationSession::GetInstance().System().GetFilesystem()->OpenFile( | 523 | EmulationSession::GetInstance().System().GetFilesystem()->OpenFile( |
| 521 | updatePath, FileSys::OpenMode::Read)); | 524 | updatePath, FileSys::OpenMode::Read)); |
| @@ -538,8 +541,10 @@ void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeGpuDriver(JNIEnv* e | |||
| 538 | jstring custom_driver_name, | 541 | jstring custom_driver_name, |
| 539 | jstring file_redirect_dir) { | 542 | jstring file_redirect_dir) { |
| 540 | EmulationSession::GetInstance().InitializeGpuDriver( | 543 | EmulationSession::GetInstance().InitializeGpuDriver( |
| 541 | GetJString(env, hook_lib_dir), GetJString(env, custom_driver_dir), | 544 | Common::Android::GetJString(env, hook_lib_dir), |
| 542 | GetJString(env, custom_driver_name), GetJString(env, file_redirect_dir)); | 545 | Common::Android::GetJString(env, custom_driver_dir), |
| 546 | Common::Android::GetJString(env, custom_driver_name), | ||
| 547 | Common::Android::GetJString(env, file_redirect_dir)); | ||
| 543 | } | 548 | } |
| 544 | 549 | ||
| 545 | [[maybe_unused]] static bool CheckKgslPresent() { | 550 | [[maybe_unused]] static bool CheckKgslPresent() { |
| @@ -566,7 +571,7 @@ jobjectArray Java_org_yuzu_yuzu_1emu_utils_GpuDriverHelper_getSystemDriverInfo( | |||
| 566 | JNIEnv* env, jobject j_obj, jobject j_surf, jstring j_hook_lib_dir) { | 571 | JNIEnv* env, jobject j_obj, jobject j_surf, jstring j_hook_lib_dir) { |
| 567 | const char* file_redirect_dir_{}; | 572 | const char* file_redirect_dir_{}; |
| 568 | int featureFlags{}; | 573 | int featureFlags{}; |
| 569 | std::string hook_lib_dir = GetJString(env, j_hook_lib_dir); | 574 | std::string hook_lib_dir = Common::Android::GetJString(env, j_hook_lib_dir); |
| 570 | auto handle = adrenotools_open_libvulkan(RTLD_NOW, featureFlags, nullptr, hook_lib_dir.c_str(), | 575 | auto handle = adrenotools_open_libvulkan(RTLD_NOW, featureFlags, nullptr, hook_lib_dir.c_str(), |
| 571 | nullptr, nullptr, file_redirect_dir_, nullptr); | 576 | nullptr, nullptr, file_redirect_dir_, nullptr); |
| 572 | auto driver_library = std::make_shared<Common::DynamicLibrary>(handle); | 577 | auto driver_library = std::make_shared<Common::DynamicLibrary>(handle); |
| @@ -587,9 +592,10 @@ jobjectArray Java_org_yuzu_yuzu_1emu_utils_GpuDriverHelper_getSystemDriverInfo( | |||
| 587 | fmt::format("{}.{}.{}", VK_API_VERSION_MAJOR(driver_version), | 592 | fmt::format("{}.{}.{}", VK_API_VERSION_MAJOR(driver_version), |
| 588 | VK_API_VERSION_MINOR(driver_version), VK_API_VERSION_PATCH(driver_version)); | 593 | VK_API_VERSION_MINOR(driver_version), VK_API_VERSION_PATCH(driver_version)); |
| 589 | 594 | ||
| 590 | jobjectArray j_driver_info = | 595 | jobjectArray j_driver_info = env->NewObjectArray( |
| 591 | env->NewObjectArray(2, IDCache::GetStringClass(), ToJString(env, version_string)); | 596 | 2, Common::Android::GetStringClass(), Common::Android::ToJString(env, version_string)); |
| 592 | env->SetObjectArrayElement(j_driver_info, 1, ToJString(env, device.GetDriverName())); | 597 | env->SetObjectArrayElement(j_driver_info, 1, |
| 598 | Common::Android::ToJString(env, device.GetDriverName())); | ||
| 593 | return j_driver_info; | 599 | return j_driver_info; |
| 594 | } | 600 | } |
| 595 | 601 | ||
| @@ -742,15 +748,15 @@ jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats(JNIEnv* env, jcl | |||
| 742 | 748 | ||
| 743 | jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCpuBackend(JNIEnv* env, jclass clazz) { | 749 | jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCpuBackend(JNIEnv* env, jclass clazz) { |
| 744 | if (Settings::IsNceEnabled()) { | 750 | if (Settings::IsNceEnabled()) { |
| 745 | return ToJString(env, "NCE"); | 751 | return Common::Android::ToJString(env, "NCE"); |
| 746 | } | 752 | } |
| 747 | 753 | ||
| 748 | return ToJString(env, "JIT"); | 754 | return Common::Android::ToJString(env, "JIT"); |
| 749 | } | 755 | } |
| 750 | 756 | ||
| 751 | jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getGpuDriver(JNIEnv* env, jobject jobj) { | 757 | jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getGpuDriver(JNIEnv* env, jobject jobj) { |
| 752 | return ToJString(env, | 758 | return Common::Android::ToJString( |
| 753 | EmulationSession::GetInstance().System().GPU().Renderer().GetDeviceVendor()); | 759 | env, EmulationSession::GetInstance().System().GPU().Renderer().GetDeviceVendor()); |
| 754 | } | 760 | } |
| 755 | 761 | ||
| 756 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_applySettings(JNIEnv* env, jobject jobj) { | 762 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_applySettings(JNIEnv* env, jobject jobj) { |
| @@ -764,13 +770,14 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_logSettings(JNIEnv* env, jobject jobj | |||
| 764 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_run(JNIEnv* env, jobject jobj, jstring j_path, | 770 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_run(JNIEnv* env, jobject jobj, jstring j_path, |
| 765 | jint j_program_index, | 771 | jint j_program_index, |
| 766 | jboolean j_frontend_initiated) { | 772 | jboolean j_frontend_initiated) { |
| 767 | const std::string path = GetJString(env, j_path); | 773 | const std::string path = Common::Android::GetJString(env, j_path); |
| 768 | 774 | ||
| 769 | const Core::SystemResultStatus result{ | 775 | const Core::SystemResultStatus result{ |
| 770 | RunEmulation(path, j_program_index, j_frontend_initiated)}; | 776 | RunEmulation(path, j_program_index, j_frontend_initiated)}; |
| 771 | if (result != Core::SystemResultStatus::Success) { | 777 | if (result != Core::SystemResultStatus::Success) { |
| 772 | env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), | 778 | env->CallStaticVoidMethod(Common::Android::GetNativeLibraryClass(), |
| 773 | IDCache::GetExitEmulationActivity(), static_cast<int>(result)); | 779 | Common::Android::GetExitEmulationActivity(), |
| 780 | static_cast<int>(result)); | ||
| 774 | } | 781 | } |
| 775 | } | 782 | } |
| 776 | 783 | ||
| @@ -781,7 +788,7 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_logDeviceInfo(JNIEnv* env, jclass cla | |||
| 781 | 788 | ||
| 782 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_submitInlineKeyboardText(JNIEnv* env, jclass clazz, | 789 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_submitInlineKeyboardText(JNIEnv* env, jclass clazz, |
| 783 | jstring j_text) { | 790 | jstring j_text) { |
| 784 | const std::u16string input = Common::UTF8ToUTF16(GetJString(env, j_text)); | 791 | const std::u16string input = Common::UTF8ToUTF16(Common::Android::GetJString(env, j_text)); |
| 785 | EmulationSession::GetInstance().SoftwareKeyboard()->SubmitInlineKeyboardText(input); | 792 | EmulationSession::GetInstance().SoftwareKeyboard()->SubmitInlineKeyboardText(input); |
| 786 | } | 793 | } |
| 787 | 794 | ||
| @@ -815,16 +822,16 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getAppletLaunchPath(JNIEnv* env, j | |||
| 815 | auto bis_system = | 822 | auto bis_system = |
| 816 | EmulationSession::GetInstance().System().GetFileSystemController().GetSystemNANDContents(); | 823 | EmulationSession::GetInstance().System().GetFileSystemController().GetSystemNANDContents(); |
| 817 | if (!bis_system) { | 824 | if (!bis_system) { |
| 818 | return ToJString(env, ""); | 825 | return Common::Android::ToJString(env, ""); |
| 819 | } | 826 | } |
| 820 | 827 | ||
| 821 | auto applet_nca = | 828 | auto applet_nca = |
| 822 | bis_system->GetEntry(static_cast<u64>(jid), FileSys::ContentRecordType::Program); | 829 | bis_system->GetEntry(static_cast<u64>(jid), FileSys::ContentRecordType::Program); |
| 823 | if (!applet_nca) { | 830 | if (!applet_nca) { |
| 824 | return ToJString(env, ""); | 831 | return Common::Android::ToJString(env, ""); |
| 825 | } | 832 | } |
| 826 | 833 | ||
| 827 | return ToJString(env, applet_nca->GetFullPath()); | 834 | return Common::Android::ToJString(env, applet_nca->GetFullPath()); |
| 828 | } | 835 | } |
| 829 | 836 | ||
| 830 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_setCurrentAppletId(JNIEnv* env, jclass clazz, | 837 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_setCurrentAppletId(JNIEnv* env, jclass clazz, |
| @@ -857,7 +864,7 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isFirmwareAvailable(JNIEnv* env, | |||
| 857 | jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPatchesForFile(JNIEnv* env, jobject jobj, | 864 | jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPatchesForFile(JNIEnv* env, jobject jobj, |
| 858 | jstring jpath, | 865 | jstring jpath, |
| 859 | jstring jprogramId) { | 866 | jstring jprogramId) { |
| 860 | const auto path = GetJString(env, jpath); | 867 | const auto path = Common::Android::GetJString(env, jpath); |
| 861 | const auto vFile = | 868 | const auto vFile = |
| 862 | Core::GetGameFileFromPath(EmulationSession::GetInstance().System().GetFilesystem(), path); | 869 | Core::GetGameFileFromPath(EmulationSession::GetInstance().System().GetFilesystem(), path); |
| 863 | if (vFile == nullptr) { | 870 | if (vFile == nullptr) { |
| @@ -875,14 +882,15 @@ jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPatchesForFile(JNIEnv* env | |||
| 875 | 882 | ||
| 876 | auto patches = pm.GetPatches(update_raw); | 883 | auto patches = pm.GetPatches(update_raw); |
| 877 | jobjectArray jpatchArray = | 884 | jobjectArray jpatchArray = |
| 878 | env->NewObjectArray(patches.size(), IDCache::GetPatchClass(), nullptr); | 885 | env->NewObjectArray(patches.size(), Common::Android::GetPatchClass(), nullptr); |
| 879 | int i = 0; | 886 | int i = 0; |
| 880 | for (const auto& patch : patches) { | 887 | for (const auto& patch : patches) { |
| 881 | jobject jpatch = env->NewObject( | 888 | jobject jpatch = env->NewObject( |
| 882 | IDCache::GetPatchClass(), IDCache::GetPatchConstructor(), patch.enabled, | 889 | Common::Android::GetPatchClass(), Common::Android::GetPatchConstructor(), patch.enabled, |
| 883 | ToJString(env, patch.name), ToJString(env, patch.version), | 890 | Common::Android::ToJString(env, patch.name), |
| 884 | static_cast<jint>(patch.type), ToJString(env, std::to_string(patch.program_id)), | 891 | Common::Android::ToJString(env, patch.version), static_cast<jint>(patch.type), |
| 885 | ToJString(env, std::to_string(patch.title_id))); | 892 | Common::Android::ToJString(env, std::to_string(patch.program_id)), |
| 893 | Common::Android::ToJString(env, std::to_string(patch.title_id))); | ||
| 886 | env->SetObjectArrayElement(jpatchArray, i, jpatch); | 894 | env->SetObjectArrayElement(jpatchArray, i, jpatch); |
| 887 | ++i; | 895 | ++i; |
| 888 | } | 896 | } |
| @@ -906,7 +914,7 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeMod(JNIEnv* env, jobject jobj, | |||
| 906 | jstring jname) { | 914 | jstring jname) { |
| 907 | auto program_id = EmulationSession::GetProgramId(env, jprogramId); | 915 | auto program_id = EmulationSession::GetProgramId(env, jprogramId); |
| 908 | ContentManager::RemoveMod(EmulationSession::GetInstance().System().GetFileSystemController(), | 916 | ContentManager::RemoveMod(EmulationSession::GetInstance().System().GetFileSystemController(), |
| 909 | program_id, GetJString(env, jname)); | 917 | program_id, Common::Android::GetJString(env, jname)); |
| 910 | } | 918 | } |
| 911 | 919 | ||
| 912 | jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyInstalledContents(JNIEnv* env, | 920 | jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyInstalledContents(JNIEnv* env, |
| @@ -917,17 +925,18 @@ jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyInstalledContents(JNIEn | |||
| 917 | jlambdaClass, "invoke", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); | 925 | jlambdaClass, "invoke", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); |
| 918 | const auto callback = [env, jcallback, jlambdaInvokeMethod](size_t max, size_t progress) { | 926 | const auto callback = [env, jcallback, jlambdaInvokeMethod](size_t max, size_t progress) { |
| 919 | auto jwasCancelled = env->CallObjectMethod(jcallback, jlambdaInvokeMethod, | 927 | auto jwasCancelled = env->CallObjectMethod(jcallback, jlambdaInvokeMethod, |
| 920 | ToJDouble(env, max), ToJDouble(env, progress)); | 928 | Common::Android::ToJDouble(env, max), |
| 921 | return GetJBoolean(env, jwasCancelled); | 929 | Common::Android::ToJDouble(env, progress)); |
| 930 | return Common::Android::GetJBoolean(env, jwasCancelled); | ||
| 922 | }; | 931 | }; |
| 923 | 932 | ||
| 924 | auto& session = EmulationSession::GetInstance(); | 933 | auto& session = EmulationSession::GetInstance(); |
| 925 | std::vector<std::string> result = ContentManager::VerifyInstalledContents( | 934 | std::vector<std::string> result = ContentManager::VerifyInstalledContents( |
| 926 | session.System(), *session.GetContentProvider(), callback); | 935 | session.System(), *session.GetContentProvider(), callback); |
| 927 | jobjectArray jresult = | 936 | jobjectArray jresult = env->NewObjectArray(result.size(), Common::Android::GetStringClass(), |
| 928 | env->NewObjectArray(result.size(), IDCache::GetStringClass(), ToJString(env, "")); | 937 | Common::Android::ToJString(env, "")); |
| 929 | for (size_t i = 0; i < result.size(); ++i) { | 938 | for (size_t i = 0; i < result.size(); ++i) { |
| 930 | env->SetObjectArrayElement(jresult, i, ToJString(env, result[i])); | 939 | env->SetObjectArrayElement(jresult, i, Common::Android::ToJString(env, result[i])); |
| 931 | } | 940 | } |
| 932 | return jresult; | 941 | return jresult; |
| 933 | } | 942 | } |
| @@ -939,19 +948,20 @@ jint Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyGameContents(JNIEnv* env, jobje | |||
| 939 | jlambdaClass, "invoke", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); | 948 | jlambdaClass, "invoke", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); |
| 940 | const auto callback = [env, jcallback, jlambdaInvokeMethod](size_t max, size_t progress) { | 949 | const auto callback = [env, jcallback, jlambdaInvokeMethod](size_t max, size_t progress) { |
| 941 | auto jwasCancelled = env->CallObjectMethod(jcallback, jlambdaInvokeMethod, | 950 | auto jwasCancelled = env->CallObjectMethod(jcallback, jlambdaInvokeMethod, |
| 942 | ToJDouble(env, max), ToJDouble(env, progress)); | 951 | Common::Android::ToJDouble(env, max), |
| 943 | return GetJBoolean(env, jwasCancelled); | 952 | Common::Android::ToJDouble(env, progress)); |
| 953 | return Common::Android::GetJBoolean(env, jwasCancelled); | ||
| 944 | }; | 954 | }; |
| 945 | auto& session = EmulationSession::GetInstance(); | 955 | auto& session = EmulationSession::GetInstance(); |
| 946 | return static_cast<jint>( | 956 | return static_cast<jint>(ContentManager::VerifyGameContents( |
| 947 | ContentManager::VerifyGameContents(session.System(), GetJString(env, jpath), callback)); | 957 | session.System(), Common::Android::GetJString(env, jpath), callback)); |
| 948 | } | 958 | } |
| 949 | 959 | ||
| 950 | jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject jobj, | 960 | jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject jobj, |
| 951 | jstring jprogramId) { | 961 | jstring jprogramId) { |
| 952 | auto program_id = EmulationSession::GetProgramId(env, jprogramId); | 962 | auto program_id = EmulationSession::GetProgramId(env, jprogramId); |
| 953 | if (program_id == 0) { | 963 | if (program_id == 0) { |
| 954 | return ToJString(env, ""); | 964 | return Common::Android::ToJString(env, ""); |
| 955 | } | 965 | } |
| 956 | 966 | ||
| 957 | auto& system = EmulationSession::GetInstance().System(); | 967 | auto& system = EmulationSession::GetInstance().System(); |
| @@ -968,7 +978,7 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject j | |||
| 968 | const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath( | 978 | const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath( |
| 969 | {}, vfsNandDir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData, | 979 | {}, vfsNandDir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData, |
| 970 | program_id, user_id->AsU128(), 0); | 980 | program_id, user_id->AsU128(), 0); |
| 971 | return ToJString(env, user_save_data_path); | 981 | return Common::Android::ToJString(env, user_save_data_path); |
| 972 | } | 982 | } |
| 973 | 983 | ||
| 974 | jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getDefaultProfileSaveDataRoot(JNIEnv* env, | 984 | jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getDefaultProfileSaveDataRoot(JNIEnv* env, |
| @@ -981,12 +991,13 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getDefaultProfileSaveDataRoot(JNIE | |||
| 981 | 991 | ||
| 982 | const auto user_save_data_root = | 992 | const auto user_save_data_root = |
| 983 | FileSys::SaveDataFactory::GetUserGameSaveDataRoot(user_id->AsU128(), jfuture); | 993 | FileSys::SaveDataFactory::GetUserGameSaveDataRoot(user_id->AsU128(), jfuture); |
| 984 | return ToJString(env, user_save_data_root); | 994 | return Common::Android::ToJString(env, user_save_data_root); |
| 985 | } | 995 | } |
| 986 | 996 | ||
| 987 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_addFileToFilesystemProvider(JNIEnv* env, jobject jobj, | 997 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_addFileToFilesystemProvider(JNIEnv* env, jobject jobj, |
| 988 | jstring jpath) { | 998 | jstring jpath) { |
| 989 | EmulationSession::GetInstance().ConfigureFilesystemProvider(GetJString(env, jpath)); | 999 | EmulationSession::GetInstance().ConfigureFilesystemProvider( |
| 1000 | Common::Android::GetJString(env, jpath)); | ||
| 990 | } | 1001 | } |
| 991 | 1002 | ||
| 992 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_clearFilesystemProvider(JNIEnv* env, jobject jobj) { | 1003 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_clearFilesystemProvider(JNIEnv* env, jobject jobj) { |
diff --git a/src/android/app/src/main/jni/native.h b/src/android/app/src/main/jni/native.h index e49d4e015..47936e305 100644 --- a/src/android/app/src/main/jni/native.h +++ b/src/android/app/src/main/jni/native.h | |||
| @@ -2,13 +2,13 @@ | |||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include <android/native_window_jni.h> | 4 | #include <android/native_window_jni.h> |
| 5 | #include "common/android/applets/software_keyboard.h" | ||
| 5 | #include "common/detached_tasks.h" | 6 | #include "common/detached_tasks.h" |
| 6 | #include "core/core.h" | 7 | #include "core/core.h" |
| 7 | #include "core/file_sys/registered_cache.h" | 8 | #include "core/file_sys/registered_cache.h" |
| 8 | #include "core/hle/service/acc/profile_manager.h" | 9 | #include "core/hle/service/acc/profile_manager.h" |
| 9 | #include "core/perf_stats.h" | 10 | #include "core/perf_stats.h" |
| 10 | #include "frontend_common/content_manager.h" | 11 | #include "frontend_common/content_manager.h" |
| 11 | #include "jni/applets/software_keyboard.h" | ||
| 12 | #include "jni/emu_window/emu_window.h" | 12 | #include "jni/emu_window/emu_window.h" |
| 13 | #include "video_core/rasterizer_interface.h" | 13 | #include "video_core/rasterizer_interface.h" |
| 14 | 14 | ||
| @@ -54,7 +54,7 @@ public: | |||
| 54 | void SetDeviceType([[maybe_unused]] int index, int type); | 54 | void SetDeviceType([[maybe_unused]] int index, int type); |
| 55 | void OnGamepadConnectEvent([[maybe_unused]] int index); | 55 | void OnGamepadConnectEvent([[maybe_unused]] int index); |
| 56 | void OnGamepadDisconnectEvent([[maybe_unused]] int index); | 56 | void OnGamepadDisconnectEvent([[maybe_unused]] int index); |
| 57 | SoftwareKeyboard::AndroidKeyboard* SoftwareKeyboard(); | 57 | Common::Android::SoftwareKeyboard::AndroidKeyboard* SoftwareKeyboard(); |
| 58 | 58 | ||
| 59 | static void OnEmulationStarted(); | 59 | static void OnEmulationStarted(); |
| 60 | 60 | ||
| @@ -79,7 +79,7 @@ private: | |||
| 79 | Core::SystemResultStatus m_load_result{Core::SystemResultStatus::ErrorNotInitialized}; | 79 | Core::SystemResultStatus m_load_result{Core::SystemResultStatus::ErrorNotInitialized}; |
| 80 | std::atomic<bool> m_is_running = false; | 80 | std::atomic<bool> m_is_running = false; |
| 81 | std::atomic<bool> m_is_paused = false; | 81 | std::atomic<bool> m_is_paused = false; |
| 82 | SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{}; | 82 | Common::Android::SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{}; |
| 83 | std::unique_ptr<FileSys::ManualContentProvider> m_manual_provider; | 83 | std::unique_ptr<FileSys::ManualContentProvider> m_manual_provider; |
| 84 | int m_applet_id{1}; | 84 | int m_applet_id{1}; |
| 85 | 85 | ||
diff --git a/src/android/app/src/main/jni/native_config.cpp b/src/android/app/src/main/jni/native_config.cpp index c6c3343dc..8ae10fbc7 100644 --- a/src/android/app/src/main/jni/native_config.cpp +++ b/src/android/app/src/main/jni/native_config.cpp | |||
| @@ -8,11 +8,11 @@ | |||
| 8 | 8 | ||
| 9 | #include "android_config.h" | 9 | #include "android_config.h" |
| 10 | #include "android_settings.h" | 10 | #include "android_settings.h" |
| 11 | #include "common/android/android_common.h" | ||
| 12 | #include "common/android/id_cache.h" | ||
| 11 | #include "common/logging/log.h" | 13 | #include "common/logging/log.h" |
| 12 | #include "common/settings.h" | 14 | #include "common/settings.h" |
| 13 | #include "frontend_common/config.h" | 15 | #include "frontend_common/config.h" |
| 14 | #include "jni/android_common/android_common.h" | ||
| 15 | #include "jni/id_cache.h" | ||
| 16 | #include "native.h" | 16 | #include "native.h" |
| 17 | 17 | ||
| 18 | std::unique_ptr<AndroidConfig> global_config; | 18 | std::unique_ptr<AndroidConfig> global_config; |
| @@ -20,7 +20,7 @@ std::unique_ptr<AndroidConfig> per_game_config; | |||
| 20 | 20 | ||
| 21 | template <typename T> | 21 | template <typename T> |
| 22 | Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) { | 22 | Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) { |
| 23 | auto key = GetJString(env, jkey); | 23 | auto key = Common::Android::GetJString(env, jkey); |
| 24 | auto basic_setting = Settings::values.linkage.by_key[key]; | 24 | auto basic_setting = Settings::values.linkage.by_key[key]; |
| 25 | if (basic_setting != 0) { | 25 | if (basic_setting != 0) { |
| 26 | return static_cast<Settings::Setting<T>*>(basic_setting); | 26 | return static_cast<Settings::Setting<T>*>(basic_setting); |
| @@ -55,7 +55,7 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_initializePerGameConfig(JNIEnv* | |||
| 55 | jstring jprogramId, | 55 | jstring jprogramId, |
| 56 | jstring jfileName) { | 56 | jstring jfileName) { |
| 57 | auto program_id = EmulationSession::GetProgramId(env, jprogramId); | 57 | auto program_id = EmulationSession::GetProgramId(env, jprogramId); |
| 58 | auto file_name = GetJString(env, jfileName); | 58 | auto file_name = Common::Android::GetJString(env, jfileName); |
| 59 | const auto config_file_name = program_id == 0 ? file_name : fmt::format("{:016X}", program_id); | 59 | const auto config_file_name = program_id == 0 ? file_name : fmt::format("{:016X}", program_id); |
| 60 | per_game_config = | 60 | per_game_config = |
| 61 | std::make_unique<AndroidConfig>(config_file_name, Config::ConfigType::PerGameConfig); | 61 | std::make_unique<AndroidConfig>(config_file_name, Config::ConfigType::PerGameConfig); |
| @@ -186,9 +186,9 @@ jstring Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getString(JNIEnv* env, jobjec | |||
| 186 | jboolean needGlobal) { | 186 | jboolean needGlobal) { |
| 187 | auto setting = getSetting<std::string>(env, jkey); | 187 | auto setting = getSetting<std::string>(env, jkey); |
| 188 | if (setting == nullptr) { | 188 | if (setting == nullptr) { |
| 189 | return ToJString(env, ""); | 189 | return Common::Android::ToJString(env, ""); |
| 190 | } | 190 | } |
| 191 | return ToJString(env, setting->GetValue(static_cast<bool>(needGlobal))); | 191 | return Common::Android::ToJString(env, setting->GetValue(static_cast<bool>(needGlobal))); |
| 192 | } | 192 | } |
| 193 | 193 | ||
| 194 | void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setString(JNIEnv* env, jobject obj, jstring jkey, | 194 | void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setString(JNIEnv* env, jobject obj, jstring jkey, |
| @@ -198,7 +198,7 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setString(JNIEnv* env, jobject o | |||
| 198 | return; | 198 | return; |
| 199 | } | 199 | } |
| 200 | 200 | ||
| 201 | setting->SetValue(GetJString(env, value)); | 201 | setting->SetValue(Common::Android::GetJString(env, value)); |
| 202 | } | 202 | } |
| 203 | 203 | ||
| 204 | jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getIsRuntimeModifiable(JNIEnv* env, jobject obj, | 204 | jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getIsRuntimeModifiable(JNIEnv* env, jobject obj, |
| @@ -214,13 +214,13 @@ jstring Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getPairedSettingKey(JNIEnv* e | |||
| 214 | jstring jkey) { | 214 | jstring jkey) { |
| 215 | auto setting = getSetting<std::string>(env, jkey); | 215 | auto setting = getSetting<std::string>(env, jkey); |
| 216 | if (setting == nullptr) { | 216 | if (setting == nullptr) { |
| 217 | return ToJString(env, ""); | 217 | return Common::Android::ToJString(env, ""); |
| 218 | } | 218 | } |
| 219 | if (setting->PairedSetting() == nullptr) { | 219 | if (setting->PairedSetting() == nullptr) { |
| 220 | return ToJString(env, ""); | 220 | return Common::Android::ToJString(env, ""); |
| 221 | } | 221 | } |
| 222 | 222 | ||
| 223 | return ToJString(env, setting->PairedSetting()->GetLabel()); | 223 | return Common::Android::ToJString(env, setting->PairedSetting()->GetLabel()); |
| 224 | } | 224 | } |
| 225 | 225 | ||
| 226 | jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getIsSwitchable(JNIEnv* env, jobject obj, | 226 | jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getIsSwitchable(JNIEnv* env, jobject obj, |
| @@ -262,21 +262,21 @@ jstring Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getDefaultToString(JNIEnv* en | |||
| 262 | jstring jkey) { | 262 | jstring jkey) { |
| 263 | auto setting = getSetting<std::string>(env, jkey); | 263 | auto setting = getSetting<std::string>(env, jkey); |
| 264 | if (setting != nullptr) { | 264 | if (setting != nullptr) { |
| 265 | return ToJString(env, setting->DefaultToString()); | 265 | return Common::Android::ToJString(env, setting->DefaultToString()); |
| 266 | } | 266 | } |
| 267 | return ToJString(env, ""); | 267 | return Common::Android::ToJString(env, ""); |
| 268 | } | 268 | } |
| 269 | 269 | ||
| 270 | jobjectArray Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getGameDirs(JNIEnv* env, jobject obj) { | 270 | jobjectArray Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getGameDirs(JNIEnv* env, jobject obj) { |
| 271 | jclass gameDirClass = IDCache::GetGameDirClass(); | 271 | jclass gameDirClass = Common::Android::GetGameDirClass(); |
| 272 | jmethodID gameDirConstructor = IDCache::GetGameDirConstructor(); | 272 | jmethodID gameDirConstructor = Common::Android::GetGameDirConstructor(); |
| 273 | jobjectArray jgameDirArray = | 273 | jobjectArray jgameDirArray = |
| 274 | env->NewObjectArray(AndroidSettings::values.game_dirs.size(), gameDirClass, nullptr); | 274 | env->NewObjectArray(AndroidSettings::values.game_dirs.size(), gameDirClass, nullptr); |
| 275 | for (size_t i = 0; i < AndroidSettings::values.game_dirs.size(); ++i) { | 275 | for (size_t i = 0; i < AndroidSettings::values.game_dirs.size(); ++i) { |
| 276 | jobject jgameDir = | 276 | jobject jgameDir = env->NewObject( |
| 277 | env->NewObject(gameDirClass, gameDirConstructor, | 277 | gameDirClass, gameDirConstructor, |
| 278 | ToJString(env, AndroidSettings::values.game_dirs[i].path), | 278 | Common::Android::ToJString(env, AndroidSettings::values.game_dirs[i].path), |
| 279 | static_cast<jboolean>(AndroidSettings::values.game_dirs[i].deep_scan)); | 279 | static_cast<jboolean>(AndroidSettings::values.game_dirs[i].deep_scan)); |
| 280 | env->SetObjectArrayElement(jgameDirArray, i, jgameDir); | 280 | env->SetObjectArrayElement(jgameDirArray, i, jgameDir); |
| 281 | } | 281 | } |
| 282 | return jgameDirArray; | 282 | return jgameDirArray; |
| @@ -292,14 +292,14 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setGameDirs(JNIEnv* env, jobject | |||
| 292 | } | 292 | } |
| 293 | 293 | ||
| 294 | jobject dir = env->GetObjectArrayElement(gameDirs, 0); | 294 | jobject dir = env->GetObjectArrayElement(gameDirs, 0); |
| 295 | jclass gameDirClass = IDCache::GetGameDirClass(); | 295 | jclass gameDirClass = Common::Android::GetGameDirClass(); |
| 296 | jfieldID uriStringField = env->GetFieldID(gameDirClass, "uriString", "Ljava/lang/String;"); | 296 | jfieldID uriStringField = env->GetFieldID(gameDirClass, "uriString", "Ljava/lang/String;"); |
| 297 | jfieldID deepScanBooleanField = env->GetFieldID(gameDirClass, "deepScan", "Z"); | 297 | jfieldID deepScanBooleanField = env->GetFieldID(gameDirClass, "deepScan", "Z"); |
| 298 | for (int i = 0; i < size; ++i) { | 298 | for (int i = 0; i < size; ++i) { |
| 299 | dir = env->GetObjectArrayElement(gameDirs, i); | 299 | dir = env->GetObjectArrayElement(gameDirs, i); |
| 300 | jstring juriString = static_cast<jstring>(env->GetObjectField(dir, uriStringField)); | 300 | jstring juriString = static_cast<jstring>(env->GetObjectField(dir, uriStringField)); |
| 301 | jboolean jdeepScanBoolean = env->GetBooleanField(dir, deepScanBooleanField); | 301 | jboolean jdeepScanBoolean = env->GetBooleanField(dir, deepScanBooleanField); |
| 302 | std::string uriString = GetJString(env, juriString); | 302 | std::string uriString = Common::Android::GetJString(env, juriString); |
| 303 | AndroidSettings::values.game_dirs.push_back( | 303 | AndroidSettings::values.game_dirs.push_back( |
| 304 | AndroidSettings::GameDir{uriString, static_cast<bool>(jdeepScanBoolean)}); | 304 | AndroidSettings::GameDir{uriString, static_cast<bool>(jdeepScanBoolean)}); |
| 305 | } | 305 | } |
| @@ -307,13 +307,13 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setGameDirs(JNIEnv* env, jobject | |||
| 307 | 307 | ||
| 308 | void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_addGameDir(JNIEnv* env, jobject obj, | 308 | void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_addGameDir(JNIEnv* env, jobject obj, |
| 309 | jobject gameDir) { | 309 | jobject gameDir) { |
| 310 | jclass gameDirClass = IDCache::GetGameDirClass(); | 310 | jclass gameDirClass = Common::Android::GetGameDirClass(); |
| 311 | jfieldID uriStringField = env->GetFieldID(gameDirClass, "uriString", "Ljava/lang/String;"); | 311 | jfieldID uriStringField = env->GetFieldID(gameDirClass, "uriString", "Ljava/lang/String;"); |
| 312 | jfieldID deepScanBooleanField = env->GetFieldID(gameDirClass, "deepScan", "Z"); | 312 | jfieldID deepScanBooleanField = env->GetFieldID(gameDirClass, "deepScan", "Z"); |
| 313 | 313 | ||
| 314 | jstring juriString = static_cast<jstring>(env->GetObjectField(gameDir, uriStringField)); | 314 | jstring juriString = static_cast<jstring>(env->GetObjectField(gameDir, uriStringField)); |
| 315 | jboolean jdeepScanBoolean = env->GetBooleanField(gameDir, deepScanBooleanField); | 315 | jboolean jdeepScanBoolean = env->GetBooleanField(gameDir, deepScanBooleanField); |
| 316 | std::string uriString = GetJString(env, juriString); | 316 | std::string uriString = Common::Android::GetJString(env, juriString); |
| 317 | AndroidSettings::values.game_dirs.push_back( | 317 | AndroidSettings::values.game_dirs.push_back( |
| 318 | AndroidSettings::GameDir{uriString, static_cast<bool>(jdeepScanBoolean)}); | 318 | AndroidSettings::GameDir{uriString, static_cast<bool>(jdeepScanBoolean)}); |
| 319 | } | 319 | } |
| @@ -323,9 +323,11 @@ jobjectArray Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getDisabledAddons(JNIEnv | |||
| 323 | auto program_id = EmulationSession::GetProgramId(env, jprogramId); | 323 | auto program_id = EmulationSession::GetProgramId(env, jprogramId); |
| 324 | auto& disabledAddons = Settings::values.disabled_addons[program_id]; | 324 | auto& disabledAddons = Settings::values.disabled_addons[program_id]; |
| 325 | jobjectArray jdisabledAddonsArray = | 325 | jobjectArray jdisabledAddonsArray = |
| 326 | env->NewObjectArray(disabledAddons.size(), IDCache::GetStringClass(), ToJString(env, "")); | 326 | env->NewObjectArray(disabledAddons.size(), Common::Android::GetStringClass(), |
| 327 | Common::Android::ToJString(env, "")); | ||
| 327 | for (size_t i = 0; i < disabledAddons.size(); ++i) { | 328 | for (size_t i = 0; i < disabledAddons.size(); ++i) { |
| 328 | env->SetObjectArrayElement(jdisabledAddonsArray, i, ToJString(env, disabledAddons[i])); | 329 | env->SetObjectArrayElement(jdisabledAddonsArray, i, |
| 330 | Common::Android::ToJString(env, disabledAddons[i])); | ||
| 329 | } | 331 | } |
| 330 | return jdisabledAddonsArray; | 332 | return jdisabledAddonsArray; |
| 331 | } | 333 | } |
| @@ -339,7 +341,7 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setDisabledAddons(JNIEnv* env, j | |||
| 339 | const int size = env->GetArrayLength(jdisabledAddons); | 341 | const int size = env->GetArrayLength(jdisabledAddons); |
| 340 | for (int i = 0; i < size; ++i) { | 342 | for (int i = 0; i < size; ++i) { |
| 341 | auto jaddon = static_cast<jstring>(env->GetObjectArrayElement(jdisabledAddons, i)); | 343 | auto jaddon = static_cast<jstring>(env->GetObjectArrayElement(jdisabledAddons, i)); |
| 342 | disabled_addons.push_back(GetJString(env, jaddon)); | 344 | disabled_addons.push_back(Common::Android::GetJString(env, jaddon)); |
| 343 | } | 345 | } |
| 344 | Settings::values.disabled_addons[program_id] = disabled_addons; | 346 | Settings::values.disabled_addons[program_id] = disabled_addons; |
| 345 | } | 347 | } |
| @@ -348,26 +350,27 @@ jobjectArray Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getOverlayControlData(JN | |||
| 348 | jobject obj) { | 350 | jobject obj) { |
| 349 | jobjectArray joverlayControlDataArray = | 351 | jobjectArray joverlayControlDataArray = |
| 350 | env->NewObjectArray(AndroidSettings::values.overlay_control_data.size(), | 352 | env->NewObjectArray(AndroidSettings::values.overlay_control_data.size(), |
| 351 | IDCache::GetOverlayControlDataClass(), nullptr); | 353 | Common::Android::GetOverlayControlDataClass(), nullptr); |
| 352 | for (size_t i = 0; i < AndroidSettings::values.overlay_control_data.size(); ++i) { | 354 | for (size_t i = 0; i < AndroidSettings::values.overlay_control_data.size(); ++i) { |
| 353 | const auto& control_data = AndroidSettings::values.overlay_control_data[i]; | 355 | const auto& control_data = AndroidSettings::values.overlay_control_data[i]; |
| 354 | jobject jlandscapePosition = | 356 | jobject jlandscapePosition = |
| 355 | env->NewObject(IDCache::GetPairClass(), IDCache::GetPairConstructor(), | 357 | env->NewObject(Common::Android::GetPairClass(), Common::Android::GetPairConstructor(), |
| 356 | ToJDouble(env, control_data.landscape_position.first), | 358 | Common::Android::ToJDouble(env, control_data.landscape_position.first), |
| 357 | ToJDouble(env, control_data.landscape_position.second)); | 359 | Common::Android::ToJDouble(env, control_data.landscape_position.second)); |
| 358 | jobject jportraitPosition = | 360 | jobject jportraitPosition = |
| 359 | env->NewObject(IDCache::GetPairClass(), IDCache::GetPairConstructor(), | 361 | env->NewObject(Common::Android::GetPairClass(), Common::Android::GetPairConstructor(), |
| 360 | ToJDouble(env, control_data.portrait_position.first), | 362 | Common::Android::ToJDouble(env, control_data.portrait_position.first), |
| 361 | ToJDouble(env, control_data.portrait_position.second)); | 363 | Common::Android::ToJDouble(env, control_data.portrait_position.second)); |
| 362 | jobject jfoldablePosition = | 364 | jobject jfoldablePosition = |
| 363 | env->NewObject(IDCache::GetPairClass(), IDCache::GetPairConstructor(), | 365 | env->NewObject(Common::Android::GetPairClass(), Common::Android::GetPairConstructor(), |
| 364 | ToJDouble(env, control_data.foldable_position.first), | 366 | Common::Android::ToJDouble(env, control_data.foldable_position.first), |
| 365 | ToJDouble(env, control_data.foldable_position.second)); | 367 | Common::Android::ToJDouble(env, control_data.foldable_position.second)); |
| 366 | 368 | ||
| 367 | jobject jcontrolData = env->NewObject( | 369 | jobject jcontrolData = |
| 368 | IDCache::GetOverlayControlDataClass(), IDCache::GetOverlayControlDataConstructor(), | 370 | env->NewObject(Common::Android::GetOverlayControlDataClass(), |
| 369 | ToJString(env, control_data.id), control_data.enabled, jlandscapePosition, | 371 | Common::Android::GetOverlayControlDataConstructor(), |
| 370 | jportraitPosition, jfoldablePosition); | 372 | Common::Android::ToJString(env, control_data.id), control_data.enabled, |
| 373 | jlandscapePosition, jportraitPosition, jfoldablePosition); | ||
| 371 | env->SetObjectArrayElement(joverlayControlDataArray, i, jcontrolData); | 374 | env->SetObjectArrayElement(joverlayControlDataArray, i, jcontrolData); |
| 372 | } | 375 | } |
| 373 | return joverlayControlDataArray; | 376 | return joverlayControlDataArray; |
| @@ -384,33 +387,41 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setOverlayControlData( | |||
| 384 | 387 | ||
| 385 | for (int i = 0; i < size; ++i) { | 388 | for (int i = 0; i < size; ++i) { |
| 386 | jobject joverlayControlData = env->GetObjectArrayElement(joverlayControlDataArray, i); | 389 | jobject joverlayControlData = env->GetObjectArrayElement(joverlayControlDataArray, i); |
| 387 | jstring jidString = static_cast<jstring>( | 390 | jstring jidString = static_cast<jstring>(env->GetObjectField( |
| 388 | env->GetObjectField(joverlayControlData, IDCache::GetOverlayControlDataIdField())); | 391 | joverlayControlData, Common::Android::GetOverlayControlDataIdField())); |
| 389 | bool enabled = static_cast<bool>(env->GetBooleanField( | 392 | bool enabled = static_cast<bool>(env->GetBooleanField( |
| 390 | joverlayControlData, IDCache::GetOverlayControlDataEnabledField())); | 393 | joverlayControlData, Common::Android::GetOverlayControlDataEnabledField())); |
| 391 | 394 | ||
| 392 | jobject jlandscapePosition = env->GetObjectField( | 395 | jobject jlandscapePosition = env->GetObjectField( |
| 393 | joverlayControlData, IDCache::GetOverlayControlDataLandscapePositionField()); | 396 | joverlayControlData, Common::Android::GetOverlayControlDataLandscapePositionField()); |
| 394 | std::pair<double, double> landscape_position = std::make_pair( | 397 | std::pair<double, double> landscape_position = std::make_pair( |
| 395 | GetJDouble(env, env->GetObjectField(jlandscapePosition, IDCache::GetPairFirstField())), | 398 | Common::Android::GetJDouble( |
| 396 | GetJDouble(env, | 399 | env, env->GetObjectField(jlandscapePosition, Common::Android::GetPairFirstField())), |
| 397 | env->GetObjectField(jlandscapePosition, IDCache::GetPairSecondField()))); | 400 | Common::Android::GetJDouble( |
| 401 | env, | ||
| 402 | env->GetObjectField(jlandscapePosition, Common::Android::GetPairSecondField()))); | ||
| 398 | 403 | ||
| 399 | jobject jportraitPosition = env->GetObjectField( | 404 | jobject jportraitPosition = env->GetObjectField( |
| 400 | joverlayControlData, IDCache::GetOverlayControlDataPortraitPositionField()); | 405 | joverlayControlData, Common::Android::GetOverlayControlDataPortraitPositionField()); |
| 401 | std::pair<double, double> portrait_position = std::make_pair( | 406 | std::pair<double, double> portrait_position = std::make_pair( |
| 402 | GetJDouble(env, env->GetObjectField(jportraitPosition, IDCache::GetPairFirstField())), | 407 | Common::Android::GetJDouble( |
| 403 | GetJDouble(env, env->GetObjectField(jportraitPosition, IDCache::GetPairSecondField()))); | 408 | env, env->GetObjectField(jportraitPosition, Common::Android::GetPairFirstField())), |
| 409 | Common::Android::GetJDouble( | ||
| 410 | env, | ||
| 411 | env->GetObjectField(jportraitPosition, Common::Android::GetPairSecondField()))); | ||
| 404 | 412 | ||
| 405 | jobject jfoldablePosition = env->GetObjectField( | 413 | jobject jfoldablePosition = env->GetObjectField( |
| 406 | joverlayControlData, IDCache::GetOverlayControlDataFoldablePositionField()); | 414 | joverlayControlData, Common::Android::GetOverlayControlDataFoldablePositionField()); |
| 407 | std::pair<double, double> foldable_position = std::make_pair( | 415 | std::pair<double, double> foldable_position = std::make_pair( |
| 408 | GetJDouble(env, env->GetObjectField(jfoldablePosition, IDCache::GetPairFirstField())), | 416 | Common::Android::GetJDouble( |
| 409 | GetJDouble(env, env->GetObjectField(jfoldablePosition, IDCache::GetPairSecondField()))); | 417 | env, env->GetObjectField(jfoldablePosition, Common::Android::GetPairFirstField())), |
| 418 | Common::Android::GetJDouble( | ||
| 419 | env, | ||
| 420 | env->GetObjectField(jfoldablePosition, Common::Android::GetPairSecondField()))); | ||
| 410 | 421 | ||
| 411 | AndroidSettings::values.overlay_control_data.push_back(AndroidSettings::OverlayControlData{ | 422 | AndroidSettings::values.overlay_control_data.push_back(AndroidSettings::OverlayControlData{ |
| 412 | GetJString(env, jidString), enabled, landscape_position, portrait_position, | 423 | Common::Android::GetJString(env, jidString), enabled, landscape_position, |
| 413 | foldable_position}); | 424 | portrait_position, foldable_position}); |
| 414 | } | 425 | } |
| 415 | } | 426 | } |
| 416 | 427 | ||
diff --git a/src/android/app/src/main/jni/native_log.cpp b/src/android/app/src/main/jni/native_log.cpp index 33d691dc8..95dd1f057 100644 --- a/src/android/app/src/main/jni/native_log.cpp +++ b/src/android/app/src/main/jni/native_log.cpp | |||
| @@ -1,31 +1,30 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include <common/android/android_common.h> | ||
| 4 | #include <common/logging/log.h> | 5 | #include <common/logging/log.h> |
| 5 | #include <jni.h> | 6 | #include <jni.h> |
| 6 | 7 | ||
| 7 | #include "android_common/android_common.h" | ||
| 8 | |||
| 9 | extern "C" { | 8 | extern "C" { |
| 10 | 9 | ||
| 11 | void Java_org_yuzu_yuzu_1emu_utils_Log_debug(JNIEnv* env, jobject obj, jstring jmessage) { | 10 | void Java_org_yuzu_yuzu_1emu_utils_Log_debug(JNIEnv* env, jobject obj, jstring jmessage) { |
| 12 | LOG_DEBUG(Frontend, "{}", GetJString(env, jmessage)); | 11 | LOG_DEBUG(Frontend, "{}", Common::Android::GetJString(env, jmessage)); |
| 13 | } | 12 | } |
| 14 | 13 | ||
| 15 | void Java_org_yuzu_yuzu_1emu_utils_Log_warning(JNIEnv* env, jobject obj, jstring jmessage) { | 14 | void Java_org_yuzu_yuzu_1emu_utils_Log_warning(JNIEnv* env, jobject obj, jstring jmessage) { |
| 16 | LOG_WARNING(Frontend, "{}", GetJString(env, jmessage)); | 15 | LOG_WARNING(Frontend, "{}", Common::Android::GetJString(env, jmessage)); |
| 17 | } | 16 | } |
| 18 | 17 | ||
| 19 | void Java_org_yuzu_yuzu_1emu_utils_Log_info(JNIEnv* env, jobject obj, jstring jmessage) { | 18 | void Java_org_yuzu_yuzu_1emu_utils_Log_info(JNIEnv* env, jobject obj, jstring jmessage) { |
| 20 | LOG_INFO(Frontend, "{}", GetJString(env, jmessage)); | 19 | LOG_INFO(Frontend, "{}", Common::Android::GetJString(env, jmessage)); |
| 21 | } | 20 | } |
| 22 | 21 | ||
| 23 | void Java_org_yuzu_yuzu_1emu_utils_Log_error(JNIEnv* env, jobject obj, jstring jmessage) { | 22 | void Java_org_yuzu_yuzu_1emu_utils_Log_error(JNIEnv* env, jobject obj, jstring jmessage) { |
| 24 | LOG_ERROR(Frontend, "{}", GetJString(env, jmessage)); | 23 | LOG_ERROR(Frontend, "{}", Common::Android::GetJString(env, jmessage)); |
| 25 | } | 24 | } |
| 26 | 25 | ||
| 27 | void Java_org_yuzu_yuzu_1emu_utils_Log_critical(JNIEnv* env, jobject obj, jstring jmessage) { | 26 | void Java_org_yuzu_yuzu_1emu_utils_Log_critical(JNIEnv* env, jobject obj, jstring jmessage) { |
| 28 | LOG_CRITICAL(Frontend, "{}", GetJString(env, jmessage)); | 27 | LOG_CRITICAL(Frontend, "{}", Common::Android::GetJString(env, jmessage)); |
| 29 | } | 28 | } |
| 30 | 29 | ||
| 31 | } // extern "C" | 30 | } // extern "C" |
diff --git a/src/android/app/src/main/res/layout/fragment_emulation.xml b/src/android/app/src/main/res/layout/fragment_emulation.xml index 0d2bfe8d6..e99a15783 100644 --- a/src/android/app/src/main/res/layout/fragment_emulation.xml +++ b/src/android/app/src/main/res/layout/fragment_emulation.xml | |||
| @@ -140,6 +140,7 @@ | |||
| 140 | android:id="@+id/overlay_container" | 140 | android:id="@+id/overlay_container" |
| 141 | android:layout_width="match_parent" | 141 | android:layout_width="match_parent" |
| 142 | android:layout_height="match_parent" | 142 | android:layout_height="match_parent" |
| 143 | android:layout_marginHorizontal="20dp" | ||
| 143 | android:fitsSystemWindows="true"> | 144 | android:fitsSystemWindows="true"> |
| 144 | 145 | ||
| 145 | <com.google.android.material.textview.MaterialTextView | 146 | <com.google.android.material.textview.MaterialTextView |
| @@ -150,7 +151,19 @@ | |||
| 150 | android:layout_gravity="left" | 151 | android:layout_gravity="left" |
| 151 | android:clickable="false" | 152 | android:clickable="false" |
| 152 | android:focusable="false" | 153 | android:focusable="false" |
| 153 | android:paddingHorizontal="20dp" | 154 | android:textColor="@android:color/white" |
| 155 | android:shadowColor="@android:color/black" | ||
| 156 | android:shadowRadius="3" | ||
| 157 | tools:ignore="RtlHardcoded" /> | ||
| 158 | |||
| 159 | <com.google.android.material.textview.MaterialTextView | ||
| 160 | android:id="@+id/show_thermals_text" | ||
| 161 | style="@style/TextAppearance.Material3.BodySmall" | ||
| 162 | android:layout_width="wrap_content" | ||
| 163 | android:layout_height="wrap_content" | ||
| 164 | android:layout_gravity="right" | ||
| 165 | android:clickable="false" | ||
| 166 | android:focusable="false" | ||
| 154 | android:textColor="@android:color/white" | 167 | android:textColor="@android:color/white" |
| 155 | android:shadowColor="@android:color/black" | 168 | android:shadowColor="@android:color/black" |
| 156 | android:shadowRadius="3" | 169 | android:shadowRadius="3" |
diff --git a/src/android/app/src/main/res/menu/menu_overlay_options.xml b/src/android/app/src/main/res/menu/menu_overlay_options.xml index 363781652..a9e807427 100644 --- a/src/android/app/src/main/res/menu/menu_overlay_options.xml +++ b/src/android/app/src/main/res/menu/menu_overlay_options.xml | |||
| @@ -7,6 +7,11 @@ | |||
| 7 | android:checkable="true" /> | 7 | android:checkable="true" /> |
| 8 | 8 | ||
| 9 | <item | 9 | <item |
| 10 | android:id="@+id/thermal_indicator" | ||
| 11 | android:title="@string/emulation_thermal_indicator" | ||
| 12 | android:checkable="true" /> | ||
| 13 | |||
| 14 | <item | ||
| 10 | android:id="@+id/menu_edit_overlay" | 15 | android:id="@+id/menu_edit_overlay" |
| 11 | android:title="@string/emulation_touch_overlay_edit" /> | 16 | android:title="@string/emulation_touch_overlay_edit" /> |
| 12 | 17 | ||
diff --git a/src/android/app/src/main/res/values-ar/strings.xml b/src/android/app/src/main/res/values-ar/strings.xml index 53678f465..41d741847 100644 --- a/src/android/app/src/main/res/values-ar/strings.xml +++ b/src/android/app/src/main/res/values-ar/strings.xml | |||
| @@ -1,9 +1,6 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | 1 | <?xml version="1.0" encoding="utf-8"?> |
| 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> | 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> |
| 3 | 3 | ||
| 4 | <string name="emulation_notification_channel_name">المحاكي نشط</string> | ||
| 5 | <string name="emulation_notification_channel_description">اظهار اشعار دائم عندما يكون المحاكي نشطاً</string> | ||
| 6 | <string name="emulation_notification_running">يوزو قيد التشغيل</string> | ||
| 7 | <string name="notice_notification_channel_name">الإشعارات والأخطاء</string> | 4 | <string name="notice_notification_channel_name">الإشعارات والأخطاء</string> |
| 8 | <string name="notice_notification_channel_description">اظهار اشعار عند حصول اي مشكلة.</string> | 5 | <string name="notice_notification_channel_description">اظهار اشعار عند حصول اي مشكلة.</string> |
| 9 | <string name="notification_permission_not_granted">لم يتم منح إذن الإشعار</string> | 6 | <string name="notification_permission_not_granted">لم يتم منح إذن الإشعار</string> |
diff --git a/src/android/app/src/main/res/values-ckb/strings.xml b/src/android/app/src/main/res/values-ckb/strings.xml index 7e1eb2b8d..827339505 100644 --- a/src/android/app/src/main/res/values-ckb/strings.xml +++ b/src/android/app/src/main/res/values-ckb/strings.xml | |||
| @@ -2,9 +2,6 @@ | |||
| 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> | 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> |
| 3 | 3 | ||
| 4 | <string name="app_disclaimer">ئەم نەرمەکاڵایە یارییەکانی کۆنسۆلی نینتێندۆ سویچ کارپێدەکات. هیچ ناونیشانێکی یاری و کلیلی تێدا نییە..<br /><br />پێش ئەوەی دەست پێ بکەیت، تکایە شوێنی فایلی <![CDATA[<b> prod.keys </b>]]> دیاریبکە لە نێو کۆگای ئامێرەکەت.<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">زیاتر فێربە</a>]]></string> | 4 | <string name="app_disclaimer">ئەم نەرمەکاڵایە یارییەکانی کۆنسۆلی نینتێندۆ سویچ کارپێدەکات. هیچ ناونیشانێکی یاری و کلیلی تێدا نییە..<br /><br />پێش ئەوەی دەست پێ بکەیت، تکایە شوێنی فایلی <![CDATA[<b> prod.keys </b>]]> دیاریبکە لە نێو کۆگای ئامێرەکەت.<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">زیاتر فێربە</a>]]></string> |
| 5 | <string name="emulation_notification_channel_name">ئیمولەیشن کارایە</string> | ||
| 6 | <string name="emulation_notification_channel_description">ئاگادارکردنەوەیەکی بەردەوام نیشان دەدات کاتێک ئیمولەیشن کاردەکات.</string> | ||
| 7 | <string name="emulation_notification_running">یوزو کاردەکات</string> | ||
| 8 | <string name="notice_notification_channel_name">ئاگاداری و هەڵەکان</string> | 5 | <string name="notice_notification_channel_name">ئاگاداری و هەڵەکان</string> |
| 9 | <string name="notice_notification_channel_description">ئاگادارکردنەوەکان پیشان دەدات کاتێک شتێک بە هەڵەدا دەچێت.</string> | 6 | <string name="notice_notification_channel_description">ئاگادارکردنەوەکان پیشان دەدات کاتێک شتێک بە هەڵەدا دەچێت.</string> |
| 10 | <string name="notification_permission_not_granted">مۆڵەتی ئاگادارکردنەوە نەدراوە!</string> | 7 | <string name="notification_permission_not_granted">مۆڵەتی ئاگادارکردنەوە نەدراوە!</string> |
diff --git a/src/android/app/src/main/res/values-cs/strings.xml b/src/android/app/src/main/res/values-cs/strings.xml index b9a4a11e4..8f8e2848d 100644 --- a/src/android/app/src/main/res/values-cs/strings.xml +++ b/src/android/app/src/main/res/values-cs/strings.xml | |||
| @@ -1,7 +1,6 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | 1 | <?xml version="1.0" encoding="utf-8"?> |
| 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> | 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> |
| 3 | 3 | ||
| 4 | <string name="emulation_notification_channel_name">Emulace je aktivní</string> | ||
| 5 | <string name="notice_notification_channel_name">Upozornění a chyby</string> | 4 | <string name="notice_notification_channel_name">Upozornění a chyby</string> |
| 6 | <string name="notice_notification_channel_description">Ukáže oznámení v případě chyby.</string> | 5 | <string name="notice_notification_channel_description">Ukáže oznámení v případě chyby.</string> |
| 7 | <string name="notification_permission_not_granted">Oznámení nejsou oprávněna!</string> | 6 | <string name="notification_permission_not_granted">Oznámení nejsou oprávněna!</string> |
diff --git a/src/android/app/src/main/res/values-de/strings.xml b/src/android/app/src/main/res/values-de/strings.xml index 483ea8c88..fb25b3c93 100644 --- a/src/android/app/src/main/res/values-de/strings.xml +++ b/src/android/app/src/main/res/values-de/strings.xml | |||
| @@ -2,9 +2,6 @@ | |||
| 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> | 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> |
| 3 | 3 | ||
| 4 | <string name="app_disclaimer">Diese Software kann Spiele für die Nintendo Switch abspielen. Keine Spiele oder Spielekeys sind enthalten.<br /><br />Bevor du beginnst, bitte halte deine <![CDATA[<b> prod.keys </b>]]> auf deinem Gerät bereit. .<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Mehr Infos</a>]]></string> | 4 | <string name="app_disclaimer">Diese Software kann Spiele für die Nintendo Switch abspielen. Keine Spiele oder Spielekeys sind enthalten.<br /><br />Bevor du beginnst, bitte halte deine <![CDATA[<b> prod.keys </b>]]> auf deinem Gerät bereit. .<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Mehr Infos</a>]]></string> |
| 5 | <string name="emulation_notification_channel_name">Emulation ist aktiv</string> | ||
| 6 | <string name="emulation_notification_channel_description">Zeigt eine dauerhafte Benachrichtigung an, wenn die Emulation läuft.</string> | ||
| 7 | <string name="emulation_notification_running">yuzu läuft</string> | ||
| 8 | <string name="notice_notification_channel_name">Hinweise und Fehler</string> | 5 | <string name="notice_notification_channel_name">Hinweise und Fehler</string> |
| 9 | <string name="notice_notification_channel_description">Zeigt Benachrichtigungen an, wenn etwas schief läuft.</string> | 6 | <string name="notice_notification_channel_description">Zeigt Benachrichtigungen an, wenn etwas schief läuft.</string> |
| 10 | <string name="notification_permission_not_granted">Berechtigung für Benachrichtigungen nicht erlaubt!</string> | 7 | <string name="notification_permission_not_granted">Berechtigung für Benachrichtigungen nicht erlaubt!</string> |
diff --git a/src/android/app/src/main/res/values-es/strings.xml b/src/android/app/src/main/res/values-es/strings.xml index c3825710b..7ecbeaba4 100644 --- a/src/android/app/src/main/res/values-es/strings.xml +++ b/src/android/app/src/main/res/values-es/strings.xml | |||
| @@ -2,9 +2,6 @@ | |||
| 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> | 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> |
| 3 | 3 | ||
| 4 | <string name="app_disclaimer">Este software ejecuta juegos para la videoconsola Nintendo Switch. Los videojuegos o claves no vienen incluidos.<br /><br />Antes de empezar, por favor, localice el archivo <![CDATA[<b> prod.keys </b>]]>en el almacenamiento de su dispositivo..<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Saber más</a>]]></string> | 4 | <string name="app_disclaimer">Este software ejecuta juegos para la videoconsola Nintendo Switch. Los videojuegos o claves no vienen incluidos.<br /><br />Antes de empezar, por favor, localice el archivo <![CDATA[<b> prod.keys </b>]]>en el almacenamiento de su dispositivo..<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Saber más</a>]]></string> |
| 5 | <string name="emulation_notification_channel_name">Emulación activa</string> | ||
| 6 | <string name="emulation_notification_channel_description">Muestra una notificación persistente cuando la emulación está activa.</string> | ||
| 7 | <string name="emulation_notification_running">yuzu está ejecutándose</string> | ||
| 8 | <string name="notice_notification_channel_name">Avisos y errores</string> | 5 | <string name="notice_notification_channel_name">Avisos y errores</string> |
| 9 | <string name="notice_notification_channel_description">Mostrar notificaciones cuándo algo vaya mal.</string> | 6 | <string name="notice_notification_channel_description">Mostrar notificaciones cuándo algo vaya mal.</string> |
| 10 | <string name="notification_permission_not_granted">¡Permisos de notificación no concedidos!</string> | 7 | <string name="notification_permission_not_granted">¡Permisos de notificación no concedidos!</string> |
diff --git a/src/android/app/src/main/res/values-fr/strings.xml b/src/android/app/src/main/res/values-fr/strings.xml index 667fe33cb..a848b9163 100644 --- a/src/android/app/src/main/res/values-fr/strings.xml +++ b/src/android/app/src/main/res/values-fr/strings.xml | |||
| @@ -2,9 +2,6 @@ | |||
| 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> | 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> |
| 3 | 3 | ||
| 4 | <string name="app_disclaimer">Ce logiciel exécutera des jeux pour la console de jeu Nintendo Switch. Aucun jeux ou clés n\'est inclus.<br /><br />Avant de commencer, veuillez localiser votre fichier <![CDATA[<b> prod.keys </b>]]> sur le stockage de votre appareil.<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">En savoir plus</a>]]></string> | 4 | <string name="app_disclaimer">Ce logiciel exécutera des jeux pour la console de jeu Nintendo Switch. Aucun jeux ou clés n\'est inclus.<br /><br />Avant de commencer, veuillez localiser votre fichier <![CDATA[<b> prod.keys </b>]]> sur le stockage de votre appareil.<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">En savoir plus</a>]]></string> |
| 5 | <string name="emulation_notification_channel_name">L\'émulation est active</string> | ||
| 6 | <string name="emulation_notification_channel_description">Affiche une notification persistante lorsque l\'émulation est en cours d\'exécution.</string> | ||
| 7 | <string name="emulation_notification_running">yuzu est en cours d\'exécution</string> | ||
| 8 | <string name="notice_notification_channel_name">Avis et erreurs</string> | 5 | <string name="notice_notification_channel_name">Avis et erreurs</string> |
| 9 | <string name="notice_notification_channel_description">Affiche des notifications en cas de problème.</string> | 6 | <string name="notice_notification_channel_description">Affiche des notifications en cas de problème.</string> |
| 10 | <string name="notification_permission_not_granted">Permission de notification non accordée !</string> | 7 | <string name="notification_permission_not_granted">Permission de notification non accordée !</string> |
diff --git a/src/android/app/src/main/res/values-he/strings.xml b/src/android/app/src/main/res/values-he/strings.xml index 41e4450c6..6096605a9 100644 --- a/src/android/app/src/main/res/values-he/strings.xml +++ b/src/android/app/src/main/res/values-he/strings.xml | |||
| @@ -2,9 +2,6 @@ | |||
| 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> | 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> |
| 3 | 3 | ||
| 4 | <string name="app_disclaimer">התוכנה תריץ משחקים לקונסולת ה Nintendo Switch. אף משחק או קבצים בעלי זכויות יוצרים נכללים.<br /><br /> לפני שאת/ה מתחיל בבקשה מצא את קובץ <![CDATA[<b>prod.keys</b>]]> על המכשיר.<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">קרא עוד</a>]]></string> | 4 | <string name="app_disclaimer">התוכנה תריץ משחקים לקונסולת ה Nintendo Switch. אף משחק או קבצים בעלי זכויות יוצרים נכללים.<br /><br /> לפני שאת/ה מתחיל בבקשה מצא את קובץ <![CDATA[<b>prod.keys</b>]]> על המכשיר.<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">קרא עוד</a>]]></string> |
| 5 | <string name="emulation_notification_channel_name">אמולציה פעילה</string> | ||
| 6 | <string name="emulation_notification_channel_description">מציג התראה מתמשכת כאשר האמולציה פועלת.</string> | ||
| 7 | <string name="emulation_notification_running">yuzu רץ</string> | ||
| 8 | <string name="notice_notification_channel_name">התראות ותקלות</string> | 5 | <string name="notice_notification_channel_name">התראות ותקלות</string> |
| 9 | <string name="notice_notification_channel_description">מציג התראות כאשר משהו הולך לא כשורה.</string> | 6 | <string name="notice_notification_channel_description">מציג התראות כאשר משהו הולך לא כשורה.</string> |
| 10 | <string name="notification_permission_not_granted">הרשאות התראות לא ניתנה!</string> | 7 | <string name="notification_permission_not_granted">הרשאות התראות לא ניתנה!</string> |
diff --git a/src/android/app/src/main/res/values-hu/strings.xml b/src/android/app/src/main/res/values-hu/strings.xml index 554da0816..f3a29e0c3 100644 --- a/src/android/app/src/main/res/values-hu/strings.xml +++ b/src/android/app/src/main/res/values-hu/strings.xml | |||
| @@ -2,9 +2,6 @@ | |||
| 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> | 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> |
| 3 | 3 | ||
| 4 | <string name="app_disclaimer">Ez a szoftver Nintendo Switch játékkonzolhoz készült játékokat futtat. Nem tartalmaz játékokat vagy kulcsokat. .<br /><br />Mielőtt hozzákezdenél, kérjük, válaszd ki a <![CDATA[<b>prod.keys</b>]]> fájl helyét a készülék tárhelyén<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Tudj meg többet</a>]]></string> | 4 | <string name="app_disclaimer">Ez a szoftver Nintendo Switch játékkonzolhoz készült játékokat futtat. Nem tartalmaz játékokat vagy kulcsokat. .<br /><br />Mielőtt hozzákezdenél, kérjük, válaszd ki a <![CDATA[<b>prod.keys</b>]]> fájl helyét a készülék tárhelyén<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Tudj meg többet</a>]]></string> |
| 5 | <string name="emulation_notification_channel_name">Emuláció aktív</string> | ||
| 6 | <string name="emulation_notification_channel_description">Állandó értesítést jelenít meg, amíg az emuláció fut.</string> | ||
| 7 | <string name="emulation_notification_running">A yuzu fut</string> | ||
| 8 | <string name="notice_notification_channel_name">Megjegyzések és hibák</string> | 5 | <string name="notice_notification_channel_name">Megjegyzések és hibák</string> |
| 9 | <string name="notice_notification_channel_description">Értesítések megjelenítése, ha valami rosszul sül el.</string> | 6 | <string name="notice_notification_channel_description">Értesítések megjelenítése, ha valami rosszul sül el.</string> |
| 10 | <string name="notification_permission_not_granted">Nincs engedély az értesítés megjelenítéséhez!</string> | 7 | <string name="notification_permission_not_granted">Nincs engedély az értesítés megjelenítéséhez!</string> |
diff --git a/src/android/app/src/main/res/values-it/strings.xml b/src/android/app/src/main/res/values-it/strings.xml index 61b39f57f..433d84f5c 100644 --- a/src/android/app/src/main/res/values-it/strings.xml +++ b/src/android/app/src/main/res/values-it/strings.xml | |||
| @@ -2,9 +2,6 @@ | |||
| 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> | 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> |
| 3 | 3 | ||
| 4 | <string name="app_disclaimer">Questo software permette di giocare ai giochi della console Nintendo Switch. Nessun gioco o chiave è inclusa.<br /><br />Prima di iniziare, perfavore individua il file <![CDATA[<b>prod.keys </b>]]> nella memoria del tuo dispositivo.<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Scopri di più</a>]]></string> | 4 | <string name="app_disclaimer">Questo software permette di giocare ai giochi della console Nintendo Switch. Nessun gioco o chiave è inclusa.<br /><br />Prima di iniziare, perfavore individua il file <![CDATA[<b>prod.keys </b>]]> nella memoria del tuo dispositivo.<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Scopri di più</a>]]></string> |
| 5 | <string name="emulation_notification_channel_name">L\'emulatore è attivo</string> | ||
| 6 | <string name="emulation_notification_channel_description">Mostra una notifica persistente quando l\'emulatore è in esecuzione.</string> | ||
| 7 | <string name="emulation_notification_running">yuzu è in esecuzione</string> | ||
| 8 | <string name="notice_notification_channel_name">Avvisi ed errori</string> | 5 | <string name="notice_notification_channel_name">Avvisi ed errori</string> |
| 9 | <string name="notice_notification_channel_description">Mostra le notifiche quando qualcosa va storto.</string> | 6 | <string name="notice_notification_channel_description">Mostra le notifiche quando qualcosa va storto.</string> |
| 10 | <string name="notification_permission_not_granted">Autorizzazione di notifica non concessa!</string> | 7 | <string name="notification_permission_not_granted">Autorizzazione di notifica non concessa!</string> |
diff --git a/src/android/app/src/main/res/values-ja/strings.xml b/src/android/app/src/main/res/values-ja/strings.xml index 0cff40bb6..da73ad651 100644 --- a/src/android/app/src/main/res/values-ja/strings.xml +++ b/src/android/app/src/main/res/values-ja/strings.xml | |||
| @@ -2,9 +2,6 @@ | |||
| 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> | 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> |
| 3 | 3 | ||
| 4 | <string name="app_disclaimer">このソフトウェアでは、Nintendo Switchのゲームを実行できます。 ゲームソフトやキーは含まれません。<br /><br />事前に、 <![CDATA[<b> prod.keys </b>]]> ファイルをストレージに配置しておいてください。<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">詳細</a>]]></string> | 4 | <string name="app_disclaimer">このソフトウェアでは、Nintendo Switchのゲームを実行できます。 ゲームソフトやキーは含まれません。<br /><br />事前に、 <![CDATA[<b> prod.keys </b>]]> ファイルをストレージに配置しておいてください。<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">詳細</a>]]></string> |
| 5 | <string name="emulation_notification_channel_name">エミュレーションが有効です</string> | ||
| 6 | <string name="emulation_notification_channel_description">エミュレーションの実行中に常設通知を表示します。</string> | ||
| 7 | <string name="emulation_notification_running">yuzu は実行中です</string> | ||
| 8 | <string name="notice_notification_channel_name">通知とエラー</string> | 5 | <string name="notice_notification_channel_name">通知とエラー</string> |
| 9 | <string name="notice_notification_channel_description">問題の発生時に通知を表示します。</string> | 6 | <string name="notice_notification_channel_description">問題の発生時に通知を表示します。</string> |
| 10 | <string name="notification_permission_not_granted">通知が許可されていません!</string> | 7 | <string name="notification_permission_not_granted">通知が許可されていません!</string> |
diff --git a/src/android/app/src/main/res/values-ko/strings.xml b/src/android/app/src/main/res/values-ko/strings.xml index eaa6c23ce..904353d34 100644 --- a/src/android/app/src/main/res/values-ko/strings.xml +++ b/src/android/app/src/main/res/values-ko/strings.xml | |||
| @@ -2,9 +2,6 @@ | |||
| 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> | 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> |
| 3 | 3 | ||
| 4 | <string name="app_disclaimer">이 소프트웨어는 Nintendo Switch 게임을 실행합니다. 게임 타이틀이나 키는 포함되어 있지 않습니다.<br /><br />시작하기 전에 장치 저장소에서 <![CDATA[<b> prod.keys </b>]]> 파일을 찾아주세요.<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">자세히 알아보기</a>]]></string> | 4 | <string name="app_disclaimer">이 소프트웨어는 Nintendo Switch 게임을 실행합니다. 게임 타이틀이나 키는 포함되어 있지 않습니다.<br /><br />시작하기 전에 장치 저장소에서 <![CDATA[<b> prod.keys </b>]]> 파일을 찾아주세요.<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">자세히 알아보기</a>]]></string> |
| 5 | <string name="emulation_notification_channel_name">에뮬레이션이 활성화됨</string> | ||
| 6 | <string name="emulation_notification_channel_description">에뮬레이션이 실행 중일 때 지속적으로 알림을 표시합니다.</string> | ||
| 7 | <string name="emulation_notification_running">yuzu가 실행 중입니다.</string> | ||
| 8 | <string name="notice_notification_channel_name">알림 및 오류</string> | 5 | <string name="notice_notification_channel_name">알림 및 오류</string> |
| 9 | <string name="notice_notification_channel_description">문제가 발생하면 알림을 표시합니다.</string> | 6 | <string name="notice_notification_channel_description">문제가 발생하면 알림을 표시합니다.</string> |
| 10 | <string name="notification_permission_not_granted">알림 권한이 부여되지 않았습니다!</string> | 7 | <string name="notification_permission_not_granted">알림 권한이 부여되지 않았습니다!</string> |
diff --git a/src/android/app/src/main/res/values-nb/strings.xml b/src/android/app/src/main/res/values-nb/strings.xml index e92dc62d9..fe3af5920 100644 --- a/src/android/app/src/main/res/values-nb/strings.xml +++ b/src/android/app/src/main/res/values-nb/strings.xml | |||
| @@ -2,9 +2,6 @@ | |||
| 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> | 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> |
| 3 | 3 | ||
| 4 | <string name="app_disclaimer">Denne programvaren vil kjøre spill for Nintendo Switch-spillkonsollen. Ingen spilltitler eller nøkler er inkludert.<br /><br />Før du begynner, må du finne <![CDATA[<b> prod.keys </b>]]> filen din på enhetslagringen.<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Lær mer</a>]]></string> | 4 | <string name="app_disclaimer">Denne programvaren vil kjøre spill for Nintendo Switch-spillkonsollen. Ingen spilltitler eller nøkler er inkludert.<br /><br />Før du begynner, må du finne <![CDATA[<b> prod.keys </b>]]> filen din på enhetslagringen.<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Lær mer</a>]]></string> |
| 5 | <string name="emulation_notification_channel_name">Emulering er aktiv</string> | ||
| 6 | <string name="emulation_notification_channel_description">Viser et vedvarende varsel når emuleringen kjører.</string> | ||
| 7 | <string name="emulation_notification_running">Yuzu kjører</string> | ||
| 8 | <string name="notice_notification_channel_name">Merknader og feil</string> | 5 | <string name="notice_notification_channel_name">Merknader og feil</string> |
| 9 | <string name="notice_notification_channel_description">Viser varsler når noe går galt.</string> | 6 | <string name="notice_notification_channel_description">Viser varsler når noe går galt.</string> |
| 10 | <string name="notification_permission_not_granted">Varslingstillatelse ikke gitt!</string> | 7 | <string name="notification_permission_not_granted">Varslingstillatelse ikke gitt!</string> |
diff --git a/src/android/app/src/main/res/values-pl/strings.xml b/src/android/app/src/main/res/values-pl/strings.xml index fbd0ad7e9..2af7fd7b4 100644 --- a/src/android/app/src/main/res/values-pl/strings.xml +++ b/src/android/app/src/main/res/values-pl/strings.xml | |||
| @@ -2,9 +2,6 @@ | |||
| 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> | 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> |
| 3 | 3 | ||
| 4 | <string name="app_disclaimer">To oprogramowanie umożliwia uruchomienie gier z konsoli Nintendo Switch. Nie zawiera gier ani wymaganych kluczy.<br /><br />Zanim zaczniesz, wybierz plik kluczy <![CDATA[<b> prod.keys </b>]]> z katalogu w pamięci masowej.<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Dowiedz się więcej</a>]]></string> | 4 | <string name="app_disclaimer">To oprogramowanie umożliwia uruchomienie gier z konsoli Nintendo Switch. Nie zawiera gier ani wymaganych kluczy.<br /><br />Zanim zaczniesz, wybierz plik kluczy <![CDATA[<b> prod.keys </b>]]> z katalogu w pamięci masowej.<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Dowiedz się więcej</a>]]></string> |
| 5 | <string name="emulation_notification_channel_name">Emulacja jest uruchomiona</string> | ||
| 6 | <string name="emulation_notification_channel_description">Pokaż trwałe powiadomienie gdy emulacja jest uruchomiona.</string> | ||
| 7 | <string name="emulation_notification_running">yuzu jest uruchomiony</string> | ||
| 8 | <string name="notice_notification_channel_name">Powiadomienia błędy</string> | 5 | <string name="notice_notification_channel_name">Powiadomienia błędy</string> |
| 9 | <string name="notice_notification_channel_description">Pokaż powiadomienie gdy coś pójdzie źle</string> | 6 | <string name="notice_notification_channel_description">Pokaż powiadomienie gdy coś pójdzie źle</string> |
| 10 | <string name="notification_permission_not_granted">Nie zezwolono na powiadomienia!</string> | 7 | <string name="notification_permission_not_granted">Nie zezwolono na powiadomienia!</string> |
diff --git a/src/android/app/src/main/res/values-pt-rBR/strings.xml b/src/android/app/src/main/res/values-pt-rBR/strings.xml index a87eb11e4..130252590 100644 --- a/src/android/app/src/main/res/values-pt-rBR/strings.xml +++ b/src/android/app/src/main/res/values-pt-rBR/strings.xml | |||
| @@ -2,9 +2,6 @@ | |||
| 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> | 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> |
| 3 | 3 | ||
| 4 | <string name="app_disclaimer">Este software executa jogos do console Nintendo Switch. Não estão inclusos nem jogos ou chaves.<br /><br />Antes de começar, por favor localize o arquivo <![CDATA[<b> prod.keys </b>]]> no armazenamento de seu dispositivo.<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Saiba mais</a>]]></string> | 4 | <string name="app_disclaimer">Este software executa jogos do console Nintendo Switch. Não estão inclusos nem jogos ou chaves.<br /><br />Antes de começar, por favor localize o arquivo <![CDATA[<b> prod.keys </b>]]> no armazenamento de seu dispositivo.<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Saiba mais</a>]]></string> |
| 5 | <string name="emulation_notification_channel_name">A emulação está Ativa</string> | ||
| 6 | <string name="emulation_notification_channel_description">Mostra uma notificação permanente enquanto a emulação estiver em andamento.</string> | ||
| 7 | <string name="emulation_notification_running">O Yuzu está em execução </string> | ||
| 8 | <string name="notice_notification_channel_name">Notificações e erros</string> | 5 | <string name="notice_notification_channel_name">Notificações e erros</string> |
| 9 | <string name="notice_notification_channel_description">Mostra notificações quando algo dá errado.</string> | 6 | <string name="notice_notification_channel_description">Mostra notificações quando algo dá errado.</string> |
| 10 | <string name="notification_permission_not_granted">Acesso às notificações não concedido!</string> | 7 | <string name="notification_permission_not_granted">Acesso às notificações não concedido!</string> |
diff --git a/src/android/app/src/main/res/values-pt-rPT/strings.xml b/src/android/app/src/main/res/values-pt-rPT/strings.xml index 684a71616..0fdbae4f8 100644 --- a/src/android/app/src/main/res/values-pt-rPT/strings.xml +++ b/src/android/app/src/main/res/values-pt-rPT/strings.xml | |||
| @@ -2,9 +2,6 @@ | |||
| 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> | 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> |
| 3 | 3 | ||
| 4 | <string name="app_disclaimer">Este software corre jogos para a consola Nintendo Switch. Não estão incluídas nem jogos ou chaves. <br /><br />Antes de começares, por favor localiza o ficheiro <![CDATA[1 prod.keys 1]]> no armazenamento do teu dispositivo.<br /><br /><![CDATA[2Learn more2]]></string> | 4 | <string name="app_disclaimer">Este software corre jogos para a consola Nintendo Switch. Não estão incluídas nem jogos ou chaves. <br /><br />Antes de começares, por favor localiza o ficheiro <![CDATA[1 prod.keys 1]]> no armazenamento do teu dispositivo.<br /><br /><![CDATA[2Learn more2]]></string> |
| 5 | <string name="emulation_notification_channel_name">Emulação está Ativa</string> | ||
| 6 | <string name="emulation_notification_channel_description">Mostra uma notificação permanente enquanto a emulação está a correr.</string> | ||
| 7 | <string name="emulation_notification_running">Yuzu está em execução </string> | ||
| 8 | <string name="notice_notification_channel_name">Notificações e erros</string> | 5 | <string name="notice_notification_channel_name">Notificações e erros</string> |
| 9 | <string name="notice_notification_channel_description">Mostra notificações quendo algo corre mal.</string> | 6 | <string name="notice_notification_channel_description">Mostra notificações quendo algo corre mal.</string> |
| 10 | <string name="notification_permission_not_granted">Permissões de notificação não permitidas </string> | 7 | <string name="notification_permission_not_granted">Permissões de notificação não permitidas </string> |
diff --git a/src/android/app/src/main/res/values-ru/strings.xml b/src/android/app/src/main/res/values-ru/strings.xml index 099b2c9eb..2dfd4a824 100644 --- a/src/android/app/src/main/res/values-ru/strings.xml +++ b/src/android/app/src/main/res/values-ru/strings.xml | |||
| @@ -2,9 +2,6 @@ | |||
| 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> | 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> |
| 3 | 3 | ||
| 4 | <string name="app_disclaimer">Это программное обеспечение позволяет запускать игры для игровой консоли Nintendo Switch. Мы не предоставляем сами игры или ключи.<br /><br />Перед началом работы найдите файл <![CDATA[<b> prod.keys </b>]]> в хранилище устройства..<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Узнать больше</a>]]></string> | 4 | <string name="app_disclaimer">Это программное обеспечение позволяет запускать игры для игровой консоли Nintendo Switch. Мы не предоставляем сами игры или ключи.<br /><br />Перед началом работы найдите файл <![CDATA[<b> prod.keys </b>]]> в хранилище устройства..<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Узнать больше</a>]]></string> |
| 5 | <string name="emulation_notification_channel_name">Эмуляция активна</string> | ||
| 6 | <string name="emulation_notification_channel_description">Показывает постоянное уведомление, когда запущена эмуляция.</string> | ||
| 7 | <string name="emulation_notification_running">yuzu запущен</string> | ||
| 8 | <string name="notice_notification_channel_name">Уведомления и ошибки</string> | 5 | <string name="notice_notification_channel_name">Уведомления и ошибки</string> |
| 9 | <string name="notice_notification_channel_description">Показывать уведомления, когда что-то пошло не так</string> | 6 | <string name="notice_notification_channel_description">Показывать уведомления, когда что-то пошло не так</string> |
| 10 | <string name="notification_permission_not_granted">Вы не предоставили разрешение на уведомления!</string> | 7 | <string name="notification_permission_not_granted">Вы не предоставили разрешение на уведомления!</string> |
diff --git a/src/android/app/src/main/res/values-uk/strings.xml b/src/android/app/src/main/res/values-uk/strings.xml index 361f0b726..9a2804a93 100644 --- a/src/android/app/src/main/res/values-uk/strings.xml +++ b/src/android/app/src/main/res/values-uk/strings.xml | |||
| @@ -2,9 +2,6 @@ | |||
| 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> | 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> |
| 3 | 3 | ||
| 4 | <string name="app_disclaimer">Це програмне забезпечення дозволяє запускати ігри для ігрової консолі Nintendo Switch. Ми не надаємо самі ігри або ключі.<br /><br />Перед початком роботи знайдіть ваш файл <![CDATA[<b> prod.keys </b>]]> у сховищі пристрою.<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Дізнатися більше</a>]]></string> | 4 | <string name="app_disclaimer">Це програмне забезпечення дозволяє запускати ігри для ігрової консолі Nintendo Switch. Ми не надаємо самі ігри або ключі.<br /><br />Перед початком роботи знайдіть ваш файл <![CDATA[<b> prod.keys </b>]]> у сховищі пристрою.<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Дізнатися більше</a>]]></string> |
| 5 | <string name="emulation_notification_channel_name">Емуляція активна</string> | ||
| 6 | <string name="emulation_notification_channel_description">Показує постійне сповіщення, коли запущено емуляцію.</string> | ||
| 7 | <string name="emulation_notification_running">yuzu запущено</string> | ||
| 8 | <string name="notice_notification_channel_name">Сповіщення та помилки</string> | 5 | <string name="notice_notification_channel_name">Сповіщення та помилки</string> |
| 9 | <string name="notice_notification_channel_description">Показувати сповіщення, коли щось пішло не так</string> | 6 | <string name="notice_notification_channel_description">Показувати сповіщення, коли щось пішло не так</string> |
| 10 | <string name="notification_permission_not_granted">Ви не надали дозвіл сповіщень!</string> | 7 | <string name="notification_permission_not_granted">Ви не надали дозвіл сповіщень!</string> |
diff --git a/src/android/app/src/main/res/values-vi/strings.xml b/src/android/app/src/main/res/values-vi/strings.xml index 0a722f329..dc06610c7 100644 --- a/src/android/app/src/main/res/values-vi/strings.xml +++ b/src/android/app/src/main/res/values-vi/strings.xml | |||
| @@ -2,9 +2,6 @@ | |||
| 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> | 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> |
| 3 | 3 | ||
| 4 | <string name="app_disclaimer">Phần mềm này sẽ chạy các game cho máy chơi game Nintendo Switch. Không có title games hoặc keys được bao gồm.<br /><br />Trước khi bạn bắt đầu, hãy tìm tập tin <![CDATA[<b> prod.keys </b>]]> trên bộ nhớ thiết bị của bạn.<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Tìm hiểu thêm</a>]]></string> | 4 | <string name="app_disclaimer">Phần mềm này sẽ chạy các game cho máy chơi game Nintendo Switch. Không có title games hoặc keys được bao gồm.<br /><br />Trước khi bạn bắt đầu, hãy tìm tập tin <![CDATA[<b> prod.keys </b>]]> trên bộ nhớ thiết bị của bạn.<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Tìm hiểu thêm</a>]]></string> |
| 5 | <string name="emulation_notification_channel_name">Giả lập đang chạy</string> | ||
| 6 | <string name="emulation_notification_channel_description">Hiển thị thông báo liên tục khi giả lập đang chạy.</string> | ||
| 7 | <string name="emulation_notification_running">yuzu đang chạy</string> | ||
| 8 | <string name="notice_notification_channel_name">Thông báo và lỗi</string> | 5 | <string name="notice_notification_channel_name">Thông báo và lỗi</string> |
| 9 | <string name="notice_notification_channel_description">Hiển thị thông báo khi có sự cố xảy ra.</string> | 6 | <string name="notice_notification_channel_description">Hiển thị thông báo khi có sự cố xảy ra.</string> |
| 10 | <string name="notification_permission_not_granted">Ứng dụng không được cấp quyền thông báo!</string> | 7 | <string name="notification_permission_not_granted">Ứng dụng không được cấp quyền thông báo!</string> |
diff --git a/src/android/app/src/main/res/values-zh-rCN/strings.xml b/src/android/app/src/main/res/values-zh-rCN/strings.xml index b840591a4..6acf6f391 100644 --- a/src/android/app/src/main/res/values-zh-rCN/strings.xml +++ b/src/android/app/src/main/res/values-zh-rCN/strings.xml | |||
| @@ -2,9 +2,6 @@ | |||
| 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> | 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> |
| 3 | 3 | ||
| 4 | <string name="app_disclaimer">此软件可以运行 Nintendo Switch 游戏,但不包含任何游戏和密钥文件。<br /><br />在开始前,请找到放置于设备存储中的 <![CDATA[<b> prod.keys </b>]]> 文件。<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">了解更多</a>]]></string> | 4 | <string name="app_disclaimer">此软件可以运行 Nintendo Switch 游戏,但不包含任何游戏和密钥文件。<br /><br />在开始前,请找到放置于设备存储中的 <![CDATA[<b> prod.keys </b>]]> 文件。<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">了解更多</a>]]></string> |
| 5 | <string name="emulation_notification_channel_name">正在进行模拟</string> | ||
| 6 | <string name="emulation_notification_channel_description">在模拟运行时显示持久通知。</string> | ||
| 7 | <string name="emulation_notification_running">yuzu 正在运行</string> | ||
| 8 | <string name="notice_notification_channel_name">通知及错误提醒</string> | 5 | <string name="notice_notification_channel_name">通知及错误提醒</string> |
| 9 | <string name="notice_notification_channel_description">当发生错误时显示通知。</string> | 6 | <string name="notice_notification_channel_description">当发生错误时显示通知。</string> |
| 10 | <string name="notification_permission_not_granted">未授予通知权限!</string> | 7 | <string name="notification_permission_not_granted">未授予通知权限!</string> |
diff --git a/src/android/app/src/main/res/values-zh-rTW/strings.xml b/src/android/app/src/main/res/values-zh-rTW/strings.xml index d39255714..411fc5947 100644 --- a/src/android/app/src/main/res/values-zh-rTW/strings.xml +++ b/src/android/app/src/main/res/values-zh-rTW/strings.xml | |||
| @@ -2,9 +2,6 @@ | |||
| 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> | 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> |
| 3 | 3 | ||
| 4 | <string name="app_disclaimer">此軟體可以執行 Nintendo Switch 主機遊戲,但不包含任何遊戲和金鑰。<br /><br />在您開始前,請找到放置於您的裝置儲存空間的 <![CDATA[<b> prod.keys </b>]]> 檔案。<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">深入瞭解</a>]]></string> | 4 | <string name="app_disclaimer">此軟體可以執行 Nintendo Switch 主機遊戲,但不包含任何遊戲和金鑰。<br /><br />在您開始前,請找到放置於您的裝置儲存空間的 <![CDATA[<b> prod.keys </b>]]> 檔案。<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">深入瞭解</a>]]></string> |
| 5 | <string name="emulation_notification_channel_name">模擬進行中</string> | ||
| 6 | <string name="emulation_notification_channel_description">在模擬執行時顯示持續通知。</string> | ||
| 7 | <string name="emulation_notification_running">yuzu 正在執行</string> | ||
| 8 | <string name="notice_notification_channel_name">通知和錯誤</string> | 5 | <string name="notice_notification_channel_name">通知和錯誤</string> |
| 9 | <string name="notice_notification_channel_description">發生錯誤時顯示通知。</string> | 6 | <string name="notice_notification_channel_description">發生錯誤時顯示通知。</string> |
| 10 | <string name="notification_permission_not_granted">未授予通知權限!</string> | 7 | <string name="notification_permission_not_granted">未授予通知權限!</string> |
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 3cd1586fd..489e00107 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml | |||
| @@ -4,10 +4,6 @@ | |||
| 4 | <!-- General application strings --> | 4 | <!-- General application strings --> |
| 5 | <string name="app_name" translatable="false">yuzu</string> | 5 | <string name="app_name" translatable="false">yuzu</string> |
| 6 | <string name="app_disclaimer">This software will run games for the Nintendo Switch game console. No game titles or keys are included.<br /><br />Before you begin, please locate your <![CDATA[<b> prod.keys </b>]]> file on your device storage.<br /><br /><![CDATA[<a href="https://yuzu-emu.org/help/quickstart">Learn more</a>]]></string> | 6 | <string name="app_disclaimer">This software will run games for the Nintendo Switch game console. No game titles or keys are included.<br /><br />Before you begin, please locate your <![CDATA[<b> prod.keys </b>]]> file on your device storage.<br /><br /><![CDATA[<a href="https://yuzu-emu.org/help/quickstart">Learn more</a>]]></string> |
| 7 | <string name="emulation_notification_channel_name">Emulation is Active</string> | ||
| 8 | <string name="emulation_notification_channel_id" translatable="false">emulationIsActive</string> | ||
| 9 | <string name="emulation_notification_channel_description">Shows a persistent notification when emulation is running.</string> | ||
| 10 | <string name="emulation_notification_running">yuzu is running</string> | ||
| 11 | <string name="notice_notification_channel_name">Notices and errors</string> | 7 | <string name="notice_notification_channel_name">Notices and errors</string> |
| 12 | <string name="notice_notification_channel_id" translatable="false">noticesAndErrors</string> | 8 | <string name="notice_notification_channel_id" translatable="false">noticesAndErrors</string> |
| 13 | <string name="notice_notification_channel_description">Shows notifications when something goes wrong.</string> | 9 | <string name="notice_notification_channel_description">Shows notifications when something goes wrong.</string> |
| @@ -380,6 +376,7 @@ | |||
| 380 | <string name="emulation_exit">Exit emulation</string> | 376 | <string name="emulation_exit">Exit emulation</string> |
| 381 | <string name="emulation_done">Done</string> | 377 | <string name="emulation_done">Done</string> |
| 382 | <string name="emulation_fps_counter">FPS counter</string> | 378 | <string name="emulation_fps_counter">FPS counter</string> |
| 379 | <string name="emulation_thermal_indicator">Thermal indicator</string> | ||
| 383 | <string name="emulation_toggle_controls">Toggle controls</string> | 380 | <string name="emulation_toggle_controls">Toggle controls</string> |
| 384 | <string name="emulation_rel_stick_center">Relative stick center</string> | 381 | <string name="emulation_rel_stick_center">Relative stick center</string> |
| 385 | <string name="emulation_dpad_slide">D-pad slide</string> | 382 | <string name="emulation_dpad_slide">D-pad slide</string> |
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 85926fc8f..779be211e 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt | |||
| @@ -107,6 +107,8 @@ add_library(common STATIC | |||
| 107 | quaternion.h | 107 | quaternion.h |
| 108 | range_map.h | 108 | range_map.h |
| 109 | range_mutex.h | 109 | range_mutex.h |
| 110 | range_sets.h | ||
| 111 | range_sets.inc | ||
| 110 | reader_writer_queue.h | 112 | reader_writer_queue.h |
| 111 | ring_buffer.h | 113 | ring_buffer.h |
| 112 | ${CMAKE_CURRENT_BINARY_DIR}/scm_rev.cpp | 114 | ${CMAKE_CURRENT_BINARY_DIR}/scm_rev.cpp |
| @@ -121,6 +123,7 @@ add_library(common STATIC | |||
| 121 | settings_input.cpp | 123 | settings_input.cpp |
| 122 | settings_input.h | 124 | settings_input.h |
| 123 | settings_setting.h | 125 | settings_setting.h |
| 126 | slot_vector.h | ||
| 124 | socket_types.h | 127 | socket_types.h |
| 125 | spin_lock.cpp | 128 | spin_lock.cpp |
| 126 | spin_lock.h | 129 | spin_lock.h |
| @@ -179,9 +182,15 @@ endif() | |||
| 179 | 182 | ||
| 180 | if(ANDROID) | 183 | if(ANDROID) |
| 181 | target_sources(common | 184 | target_sources(common |
| 182 | PRIVATE | 185 | PUBLIC |
| 183 | fs/fs_android.cpp | 186 | fs/fs_android.cpp |
| 184 | fs/fs_android.h | 187 | fs/fs_android.h |
| 188 | android/android_common.cpp | ||
| 189 | android/android_common.h | ||
| 190 | android/id_cache.cpp | ||
| 191 | android/id_cache.h | ||
| 192 | android/applets/software_keyboard.cpp | ||
| 193 | android/applets/software_keyboard.h | ||
| 185 | ) | 194 | ) |
| 186 | endif() | 195 | endif() |
| 187 | 196 | ||
diff --git a/src/android/app/src/main/jni/android_common/android_common.cpp b/src/common/android/android_common.cpp index 7018a52af..e79005658 100644 --- a/src/android/app/src/main/jni/android_common/android_common.cpp +++ b/src/common/android/android_common.cpp | |||
| @@ -1,15 +1,17 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include "jni/android_common/android_common.h" | 4 | #include "android_common.h" |
| 5 | 5 | ||
| 6 | #include <string> | 6 | #include <string> |
| 7 | #include <string_view> | 7 | #include <string_view> |
| 8 | 8 | ||
| 9 | #include <jni.h> | 9 | #include <jni.h> |
| 10 | 10 | ||
| 11 | #include "common/android/id_cache.h" | ||
| 11 | #include "common/string_util.h" | 12 | #include "common/string_util.h" |
| 12 | #include "jni/id_cache.h" | 13 | |
| 14 | namespace Common::Android { | ||
| 13 | 15 | ||
| 14 | std::string GetJString(JNIEnv* env, jstring jstr) { | 16 | std::string GetJString(JNIEnv* env, jstring jstr) { |
| 15 | if (!jstr) { | 17 | if (!jstr) { |
| @@ -18,7 +20,8 @@ std::string GetJString(JNIEnv* env, jstring jstr) { | |||
| 18 | 20 | ||
| 19 | const jchar* jchars = env->GetStringChars(jstr, nullptr); | 21 | const jchar* jchars = env->GetStringChars(jstr, nullptr); |
| 20 | const jsize length = env->GetStringLength(jstr); | 22 | const jsize length = env->GetStringLength(jstr); |
| 21 | const std::u16string_view string_view(reinterpret_cast<const char16_t*>(jchars), length); | 23 | const std::u16string_view string_view(reinterpret_cast<const char16_t*>(jchars), |
| 24 | static_cast<u32>(length)); | ||
| 22 | const std::string converted_string = Common::UTF16ToUTF8(string_view); | 25 | const std::string converted_string = Common::UTF16ToUTF8(string_view); |
| 23 | env->ReleaseStringChars(jstr, jchars); | 26 | env->ReleaseStringChars(jstr, jchars); |
| 24 | 27 | ||
| @@ -36,25 +39,27 @@ jstring ToJString(JNIEnv* env, std::u16string_view str) { | |||
| 36 | } | 39 | } |
| 37 | 40 | ||
| 38 | double GetJDouble(JNIEnv* env, jobject jdouble) { | 41 | double GetJDouble(JNIEnv* env, jobject jdouble) { |
| 39 | return env->GetDoubleField(jdouble, IDCache::GetDoubleValueField()); | 42 | return env->GetDoubleField(jdouble, GetDoubleValueField()); |
| 40 | } | 43 | } |
| 41 | 44 | ||
| 42 | jobject ToJDouble(JNIEnv* env, double value) { | 45 | jobject ToJDouble(JNIEnv* env, double value) { |
| 43 | return env->NewObject(IDCache::GetDoubleClass(), IDCache::GetDoubleConstructor(), value); | 46 | return env->NewObject(GetDoubleClass(), GetDoubleConstructor(), value); |
| 44 | } | 47 | } |
| 45 | 48 | ||
| 46 | s32 GetJInteger(JNIEnv* env, jobject jinteger) { | 49 | s32 GetJInteger(JNIEnv* env, jobject jinteger) { |
| 47 | return env->GetIntField(jinteger, IDCache::GetIntegerValueField()); | 50 | return env->GetIntField(jinteger, GetIntegerValueField()); |
| 48 | } | 51 | } |
| 49 | 52 | ||
| 50 | jobject ToJInteger(JNIEnv* env, s32 value) { | 53 | jobject ToJInteger(JNIEnv* env, s32 value) { |
| 51 | return env->NewObject(IDCache::GetIntegerClass(), IDCache::GetIntegerConstructor(), value); | 54 | return env->NewObject(GetIntegerClass(), GetIntegerConstructor(), value); |
| 52 | } | 55 | } |
| 53 | 56 | ||
| 54 | bool GetJBoolean(JNIEnv* env, jobject jboolean) { | 57 | bool GetJBoolean(JNIEnv* env, jobject jboolean) { |
| 55 | return env->GetBooleanField(jboolean, IDCache::GetBooleanValueField()); | 58 | return env->GetBooleanField(jboolean, GetBooleanValueField()); |
| 56 | } | 59 | } |
| 57 | 60 | ||
| 58 | jobject ToJBoolean(JNIEnv* env, bool value) { | 61 | jobject ToJBoolean(JNIEnv* env, bool value) { |
| 59 | return env->NewObject(IDCache::GetBooleanClass(), IDCache::GetBooleanConstructor(), value); | 62 | return env->NewObject(GetBooleanClass(), GetBooleanConstructor(), value); |
| 60 | } | 63 | } |
| 64 | |||
| 65 | } // namespace Common::Android | ||
diff --git a/src/android/app/src/main/jni/android_common/android_common.h b/src/common/android/android_common.h index 29a338c0a..d0ccb4ec2 100644 --- a/src/android/app/src/main/jni/android_common/android_common.h +++ b/src/common/android/android_common.h | |||
| @@ -8,6 +8,8 @@ | |||
| 8 | #include <jni.h> | 8 | #include <jni.h> |
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | 10 | ||
| 11 | namespace Common::Android { | ||
| 12 | |||
| 11 | std::string GetJString(JNIEnv* env, jstring jstr); | 13 | std::string GetJString(JNIEnv* env, jstring jstr); |
| 12 | jstring ToJString(JNIEnv* env, std::string_view str); | 14 | jstring ToJString(JNIEnv* env, std::string_view str); |
| 13 | jstring ToJString(JNIEnv* env, std::u16string_view str); | 15 | jstring ToJString(JNIEnv* env, std::u16string_view str); |
| @@ -20,3 +22,5 @@ jobject ToJInteger(JNIEnv* env, s32 value); | |||
| 20 | 22 | ||
| 21 | bool GetJBoolean(JNIEnv* env, jobject jboolean); | 23 | bool GetJBoolean(JNIEnv* env, jobject jboolean); |
| 22 | jobject ToJBoolean(JNIEnv* env, bool value); | 24 | jobject ToJBoolean(JNIEnv* env, bool value); |
| 25 | |||
| 26 | } // namespace Common::Android | ||
diff --git a/src/android/app/src/main/jni/applets/software_keyboard.cpp b/src/common/android/applets/software_keyboard.cpp index 9943483e8..477e62b16 100644 --- a/src/android/app/src/main/jni/applets/software_keyboard.cpp +++ b/src/common/android/applets/software_keyboard.cpp | |||
| @@ -6,12 +6,12 @@ | |||
| 6 | 6 | ||
| 7 | #include <jni.h> | 7 | #include <jni.h> |
| 8 | 8 | ||
| 9 | #include "common/android/android_common.h" | ||
| 10 | #include "common/android/applets/software_keyboard.h" | ||
| 11 | #include "common/android/id_cache.h" | ||
| 9 | #include "common/logging/log.h" | 12 | #include "common/logging/log.h" |
| 10 | #include "common/string_util.h" | 13 | #include "common/string_util.h" |
| 11 | #include "core/core.h" | 14 | #include "core/core.h" |
| 12 | #include "jni/android_common/android_common.h" | ||
| 13 | #include "jni/applets/software_keyboard.h" | ||
| 14 | #include "jni/id_cache.h" | ||
| 15 | 15 | ||
| 16 | static jclass s_software_keyboard_class; | 16 | static jclass s_software_keyboard_class; |
| 17 | static jclass s_keyboard_config_class; | 17 | static jclass s_keyboard_config_class; |
| @@ -19,10 +19,10 @@ static jclass s_keyboard_data_class; | |||
| 19 | static jmethodID s_swkbd_execute_normal; | 19 | static jmethodID s_swkbd_execute_normal; |
| 20 | static jmethodID s_swkbd_execute_inline; | 20 | static jmethodID s_swkbd_execute_inline; |
| 21 | 21 | ||
| 22 | namespace SoftwareKeyboard { | 22 | namespace Common::Android::SoftwareKeyboard { |
| 23 | 23 | ||
| 24 | static jobject ToJKeyboardParams(const Core::Frontend::KeyboardInitializeParameters& config) { | 24 | static jobject ToJKeyboardParams(const Core::Frontend::KeyboardInitializeParameters& config) { |
| 25 | JNIEnv* env = IDCache::GetEnvForThread(); | 25 | JNIEnv* env = GetEnvForThread(); |
| 26 | jobject object = env->AllocObject(s_keyboard_config_class); | 26 | jobject object = env->AllocObject(s_keyboard_config_class); |
| 27 | 27 | ||
| 28 | env->SetObjectField(object, | 28 | env->SetObjectField(object, |
| @@ -78,7 +78,7 @@ static jobject ToJKeyboardParams(const Core::Frontend::KeyboardInitializeParamet | |||
| 78 | } | 78 | } |
| 79 | 79 | ||
| 80 | AndroidKeyboard::ResultData AndroidKeyboard::ResultData::CreateFromFrontend(jobject object) { | 80 | AndroidKeyboard::ResultData AndroidKeyboard::ResultData::CreateFromFrontend(jobject object) { |
| 81 | JNIEnv* env = IDCache::GetEnvForThread(); | 81 | JNIEnv* env = GetEnvForThread(); |
| 82 | const jstring string = reinterpret_cast<jstring>(env->GetObjectField( | 82 | const jstring string = reinterpret_cast<jstring>(env->GetObjectField( |
| 83 | object, env->GetFieldID(s_keyboard_data_class, "text", "Ljava/lang/String;"))); | 83 | object, env->GetFieldID(s_keyboard_data_class, "text", "Ljava/lang/String;"))); |
| 84 | return ResultData{GetJString(env, string), | 84 | return ResultData{GetJString(env, string), |
| @@ -141,7 +141,7 @@ void AndroidKeyboard::ShowNormalKeyboard() const { | |||
| 141 | 141 | ||
| 142 | // Pivot to a new thread, as we cannot call GetEnvForThread() from a Fiber. | 142 | // Pivot to a new thread, as we cannot call GetEnvForThread() from a Fiber. |
| 143 | std::thread([&] { | 143 | std::thread([&] { |
| 144 | data = ResultData::CreateFromFrontend(IDCache::GetEnvForThread()->CallStaticObjectMethod( | 144 | data = ResultData::CreateFromFrontend(GetEnvForThread()->CallStaticObjectMethod( |
| 145 | s_software_keyboard_class, s_swkbd_execute_normal, ToJKeyboardParams(parameters))); | 145 | s_software_keyboard_class, s_swkbd_execute_normal, ToJKeyboardParams(parameters))); |
| 146 | }).join(); | 146 | }).join(); |
| 147 | 147 | ||
| @@ -183,8 +183,8 @@ void AndroidKeyboard::ShowInlineKeyboard( | |||
| 183 | // Pivot to a new thread, as we cannot call GetEnvForThread() from a Fiber. | 183 | // Pivot to a new thread, as we cannot call GetEnvForThread() from a Fiber. |
| 184 | m_is_inline_active = true; | 184 | m_is_inline_active = true; |
| 185 | std::thread([&] { | 185 | std::thread([&] { |
| 186 | IDCache::GetEnvForThread()->CallStaticVoidMethod( | 186 | GetEnvForThread()->CallStaticVoidMethod(s_software_keyboard_class, s_swkbd_execute_inline, |
| 187 | s_software_keyboard_class, s_swkbd_execute_inline, ToJKeyboardParams(parameters)); | 187 | ToJKeyboardParams(parameters)); |
| 188 | }).join(); | 188 | }).join(); |
| 189 | } | 189 | } |
| 190 | 190 | ||
| @@ -220,7 +220,7 @@ void AndroidKeyboard::SubmitInlineKeyboardText(std::u16string submitted_text) { | |||
| 220 | m_current_text += submitted_text; | 220 | m_current_text += submitted_text; |
| 221 | 221 | ||
| 222 | submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::ChangedString, m_current_text, | 222 | submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::ChangedString, m_current_text, |
| 223 | m_current_text.size()); | 223 | static_cast<int>(m_current_text.size())); |
| 224 | } | 224 | } |
| 225 | 225 | ||
| 226 | void AndroidKeyboard::SubmitInlineKeyboardInput(int key_code) { | 226 | void AndroidKeyboard::SubmitInlineKeyboardInput(int key_code) { |
| @@ -242,7 +242,7 @@ void AndroidKeyboard::SubmitInlineKeyboardInput(int key_code) { | |||
| 242 | case KEYCODE_DEL: | 242 | case KEYCODE_DEL: |
| 243 | m_current_text.pop_back(); | 243 | m_current_text.pop_back(); |
| 244 | submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::ChangedString, m_current_text, | 244 | submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::ChangedString, m_current_text, |
| 245 | m_current_text.size()); | 245 | static_cast<int>(m_current_text.size())); |
| 246 | break; | 246 | break; |
| 247 | } | 247 | } |
| 248 | } | 248 | } |
| @@ -274,4 +274,4 @@ void CleanupJNI(JNIEnv* env) { | |||
| 274 | env->DeleteGlobalRef(s_keyboard_data_class); | 274 | env->DeleteGlobalRef(s_keyboard_data_class); |
| 275 | } | 275 | } |
| 276 | 276 | ||
| 277 | } // namespace SoftwareKeyboard | 277 | } // namespace Common::Android::SoftwareKeyboard |
diff --git a/src/android/app/src/main/jni/applets/software_keyboard.h b/src/common/android/applets/software_keyboard.h index 2affc01f6..9fd09d27c 100644 --- a/src/android/app/src/main/jni/applets/software_keyboard.h +++ b/src/common/android/applets/software_keyboard.h | |||
| @@ -7,7 +7,7 @@ | |||
| 7 | 7 | ||
| 8 | #include "core/frontend/applets/software_keyboard.h" | 8 | #include "core/frontend/applets/software_keyboard.h" |
| 9 | 9 | ||
| 10 | namespace SoftwareKeyboard { | 10 | namespace Common::Android::SoftwareKeyboard { |
| 11 | 11 | ||
| 12 | class AndroidKeyboard final : public Core::Frontend::SoftwareKeyboardApplet { | 12 | class AndroidKeyboard final : public Core::Frontend::SoftwareKeyboardApplet { |
| 13 | public: | 13 | public: |
| @@ -66,7 +66,7 @@ void InitJNI(JNIEnv* env); | |||
| 66 | // Should be called in JNI_Unload | 66 | // Should be called in JNI_Unload |
| 67 | void CleanupJNI(JNIEnv* env); | 67 | void CleanupJNI(JNIEnv* env); |
| 68 | 68 | ||
| 69 | } // namespace SoftwareKeyboard | 69 | } // namespace Common::Android::SoftwareKeyboard |
| 70 | 70 | ||
| 71 | // Native function calls | 71 | // Native function calls |
| 72 | extern "C" { | 72 | extern "C" { |
diff --git a/src/android/app/src/main/jni/id_cache.cpp b/src/common/android/id_cache.cpp index f30100bd8..f39262db9 100644 --- a/src/android/app/src/main/jni/id_cache.cpp +++ b/src/common/android/id_cache.cpp | |||
| @@ -3,10 +3,10 @@ | |||
| 3 | 3 | ||
| 4 | #include <jni.h> | 4 | #include <jni.h> |
| 5 | 5 | ||
| 6 | #include "applets/software_keyboard.h" | ||
| 7 | #include "common/android/id_cache.h" | ||
| 6 | #include "common/assert.h" | 8 | #include "common/assert.h" |
| 7 | #include "common/fs/fs_android.h" | 9 | #include "common/fs/fs_android.h" |
| 8 | #include "jni/applets/software_keyboard.h" | ||
| 9 | #include "jni/id_cache.h" | ||
| 10 | #include "video_core/rasterizer_interface.h" | 10 | #include "video_core/rasterizer_interface.h" |
| 11 | 11 | ||
| 12 | static JavaVM* s_java_vm; | 12 | static JavaVM* s_java_vm; |
| @@ -67,7 +67,7 @@ static jfieldID s_boolean_value_field; | |||
| 67 | 67 | ||
| 68 | static constexpr jint JNI_VERSION = JNI_VERSION_1_6; | 68 | static constexpr jint JNI_VERSION = JNI_VERSION_1_6; |
| 69 | 69 | ||
| 70 | namespace IDCache { | 70 | namespace Common::Android { |
| 71 | 71 | ||
| 72 | JNIEnv* GetEnvForThread() { | 72 | JNIEnv* GetEnvForThread() { |
| 73 | thread_local static struct OwnedEnv { | 73 | thread_local static struct OwnedEnv { |
| @@ -276,8 +276,6 @@ jfieldID GetBooleanValueField() { | |||
| 276 | return s_boolean_value_field; | 276 | return s_boolean_value_field; |
| 277 | } | 277 | } |
| 278 | 278 | ||
| 279 | } // namespace IDCache | ||
| 280 | |||
| 281 | #ifdef __cplusplus | 279 | #ifdef __cplusplus |
| 282 | extern "C" { | 280 | extern "C" { |
| 283 | #endif | 281 | #endif |
| @@ -393,7 +391,7 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) { | |||
| 393 | Common::FS::Android::RegisterCallbacks(env, s_native_library_class); | 391 | Common::FS::Android::RegisterCallbacks(env, s_native_library_class); |
| 394 | 392 | ||
| 395 | // Initialize applets | 393 | // Initialize applets |
| 396 | SoftwareKeyboard::InitJNI(env); | 394 | Common::Android::SoftwareKeyboard::InitJNI(env); |
| 397 | 395 | ||
| 398 | return JNI_VERSION; | 396 | return JNI_VERSION; |
| 399 | } | 397 | } |
| @@ -426,3 +424,5 @@ void JNI_OnUnload(JavaVM* vm, void* reserved) { | |||
| 426 | #ifdef __cplusplus | 424 | #ifdef __cplusplus |
| 427 | } | 425 | } |
| 428 | #endif | 426 | #endif |
| 427 | |||
| 428 | } // namespace Common::Android | ||
diff --git a/src/android/app/src/main/jni/id_cache.h b/src/common/android/id_cache.h index 00e48afc0..47802f96c 100644 --- a/src/android/app/src/main/jni/id_cache.h +++ b/src/common/android/id_cache.h | |||
| @@ -3,20 +3,40 @@ | |||
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <future> | ||
| 6 | #include <jni.h> | 7 | #include <jni.h> |
| 7 | 8 | ||
| 8 | #include "video_core/rasterizer_interface.h" | 9 | #include "video_core/rasterizer_interface.h" |
| 9 | 10 | ||
| 10 | namespace IDCache { | 11 | namespace Common::Android { |
| 11 | 12 | ||
| 12 | JNIEnv* GetEnvForThread(); | 13 | JNIEnv* GetEnvForThread(); |
| 14 | |||
| 15 | /** | ||
| 16 | * Starts a new thread to run JNI. Intended to be used when you must run JNI from a fiber. | ||
| 17 | * @tparam T Typename of the return value for the work param | ||
| 18 | * @param work Lambda that runs JNI code. This function will take care of attaching this thread to | ||
| 19 | * the JVM | ||
| 20 | * @return The result from the work lambda param | ||
| 21 | */ | ||
| 22 | template <typename T = void> | ||
| 23 | T RunJNIOnFiber(const std::function<T(JNIEnv*)>& work) { | ||
| 24 | std::future<T> j_result = std::async(std::launch::async, [&] { | ||
| 25 | auto env = GetEnvForThread(); | ||
| 26 | return work(env); | ||
| 27 | }); | ||
| 28 | return j_result.get(); | ||
| 29 | } | ||
| 30 | |||
| 13 | jclass GetNativeLibraryClass(); | 31 | jclass GetNativeLibraryClass(); |
| 32 | |||
| 14 | jclass GetDiskCacheProgressClass(); | 33 | jclass GetDiskCacheProgressClass(); |
| 15 | jclass GetDiskCacheLoadCallbackStageClass(); | 34 | jclass GetDiskCacheLoadCallbackStageClass(); |
| 16 | jclass GetGameDirClass(); | 35 | jclass GetGameDirClass(); |
| 17 | jmethodID GetGameDirConstructor(); | 36 | jmethodID GetGameDirConstructor(); |
| 18 | jmethodID GetExitEmulationActivity(); | ||
| 19 | jmethodID GetDiskCacheLoadProgress(); | 37 | jmethodID GetDiskCacheLoadProgress(); |
| 38 | |||
| 39 | jmethodID GetExitEmulationActivity(); | ||
| 20 | jmethodID GetOnEmulationStarted(); | 40 | jmethodID GetOnEmulationStarted(); |
| 21 | jmethodID GetOnEmulationStopped(); | 41 | jmethodID GetOnEmulationStopped(); |
| 22 | jmethodID GetOnProgramChanged(); | 42 | jmethodID GetOnProgramChanged(); |
| @@ -65,4 +85,4 @@ jclass GetBooleanClass(); | |||
| 65 | jmethodID GetBooleanConstructor(); | 85 | jmethodID GetBooleanConstructor(); |
| 66 | jfieldID GetBooleanValueField(); | 86 | jfieldID GetBooleanValueField(); |
| 67 | 87 | ||
| 68 | } // namespace IDCache | 88 | } // namespace Common::Android |
diff --git a/src/common/fs/fs_android.cpp b/src/common/fs/fs_android.cpp index 1dd826a4a..9a8053222 100644 --- a/src/common/fs/fs_android.cpp +++ b/src/common/fs/fs_android.cpp | |||
| @@ -1,63 +1,38 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include "common/android/android_common.h" | ||
| 5 | #include "common/android/id_cache.h" | ||
| 6 | #include "common/assert.h" | ||
| 4 | #include "common/fs/fs_android.h" | 7 | #include "common/fs/fs_android.h" |
| 5 | #include "common/string_util.h" | 8 | #include "common/string_util.h" |
| 6 | 9 | ||
| 7 | namespace Common::FS::Android { | 10 | namespace Common::FS::Android { |
| 8 | 11 | ||
| 9 | JNIEnv* GetEnvForThread() { | ||
| 10 | thread_local static struct OwnedEnv { | ||
| 11 | OwnedEnv() { | ||
| 12 | status = g_jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6); | ||
| 13 | if (status == JNI_EDETACHED) | ||
| 14 | g_jvm->AttachCurrentThread(&env, nullptr); | ||
| 15 | } | ||
| 16 | |||
| 17 | ~OwnedEnv() { | ||
| 18 | if (status == JNI_EDETACHED) | ||
| 19 | g_jvm->DetachCurrentThread(); | ||
| 20 | } | ||
| 21 | |||
| 22 | int status; | ||
| 23 | JNIEnv* env = nullptr; | ||
| 24 | } owned; | ||
| 25 | return owned.env; | ||
| 26 | } | ||
| 27 | |||
| 28 | void RegisterCallbacks(JNIEnv* env, jclass clazz) { | 12 | void RegisterCallbacks(JNIEnv* env, jclass clazz) { |
| 29 | env->GetJavaVM(&g_jvm); | 13 | env->GetJavaVM(&g_jvm); |
| 30 | native_library = clazz; | 14 | native_library = clazz; |
| 31 | 15 | ||
| 32 | #define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) \ | 16 | s_get_parent_directory = env->GetStaticMethodID(native_library, "getParentDirectory", |
| 33 | F(JMethodID, JMethodName, Signature) | 17 | "(Ljava/lang/String;)Ljava/lang/String;"); |
| 34 | #define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) \ | 18 | s_get_filename = env->GetStaticMethodID(native_library, "getFilename", |
| 35 | F(JMethodID, JMethodName, Signature) | 19 | "(Ljava/lang/String;)Ljava/lang/String;"); |
| 36 | #define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) \ | 20 | s_get_size = env->GetStaticMethodID(native_library, "getSize", "(Ljava/lang/String;)J"); |
| 37 | F(JMethodID, JMethodName, Signature) | 21 | s_is_directory = env->GetStaticMethodID(native_library, "isDirectory", "(Ljava/lang/String;)Z"); |
| 38 | #define F(JMethodID, JMethodName, Signature) \ | 22 | s_file_exists = env->GetStaticMethodID(native_library, "exists", "(Ljava/lang/String;)Z"); |
| 39 | JMethodID = env->GetStaticMethodID(native_library, JMethodName, Signature); | 23 | s_open_content_uri = env->GetStaticMethodID(native_library, "openContentUri", |
| 40 | ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH) | 24 | "(Ljava/lang/String;Ljava/lang/String;)I"); |
| 41 | ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR) | ||
| 42 | ANDROID_STORAGE_FUNCTIONS(FS) | ||
| 43 | #undef F | ||
| 44 | #undef FS | ||
| 45 | #undef FR | ||
| 46 | #undef FH | ||
| 47 | } | 25 | } |
| 48 | 26 | ||
| 49 | void UnRegisterCallbacks() { | 27 | void UnRegisterCallbacks() { |
| 50 | #define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) F(JMethodID) | 28 | s_get_parent_directory = nullptr; |
| 51 | #define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) F(JMethodID) | 29 | s_get_filename = nullptr; |
| 52 | #define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) F(JMethodID) | 30 | |
| 53 | #define F(JMethodID) JMethodID = nullptr; | 31 | s_get_size = nullptr; |
| 54 | ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH) | 32 | s_is_directory = nullptr; |
| 55 | ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR) | 33 | s_file_exists = nullptr; |
| 56 | ANDROID_STORAGE_FUNCTIONS(FS) | 34 | |
| 57 | #undef F | 35 | s_open_content_uri = nullptr; |
| 58 | #undef FS | ||
| 59 | #undef FR | ||
| 60 | #undef FH | ||
| 61 | } | 36 | } |
| 62 | 37 | ||
| 63 | bool IsContentUri(const std::string& path) { | 38 | bool IsContentUri(const std::string& path) { |
| @@ -69,8 +44,8 @@ bool IsContentUri(const std::string& path) { | |||
| 69 | return path.find(prefix) == 0; | 44 | return path.find(prefix) == 0; |
| 70 | } | 45 | } |
| 71 | 46 | ||
| 72 | int OpenContentUri(const std::string& filepath, OpenMode openmode) { | 47 | s32 OpenContentUri(const std::string& filepath, OpenMode openmode) { |
| 73 | if (open_content_uri == nullptr) | 48 | if (s_open_content_uri == nullptr) |
| 74 | return -1; | 49 | return -1; |
| 75 | 50 | ||
| 76 | const char* mode = ""; | 51 | const char* mode = ""; |
| @@ -82,50 +57,66 @@ int OpenContentUri(const std::string& filepath, OpenMode openmode) { | |||
| 82 | UNIMPLEMENTED(); | 57 | UNIMPLEMENTED(); |
| 83 | return -1; | 58 | return -1; |
| 84 | } | 59 | } |
| 85 | auto env = GetEnvForThread(); | 60 | auto env = Common::Android::GetEnvForThread(); |
| 86 | jstring j_filepath = env->NewStringUTF(filepath.c_str()); | 61 | jstring j_filepath = Common::Android::ToJString(env, filepath); |
| 87 | jstring j_mode = env->NewStringUTF(mode); | 62 | jstring j_mode = Common::Android::ToJString(env, mode); |
| 88 | return env->CallStaticIntMethod(native_library, open_content_uri, j_filepath, j_mode); | 63 | return env->CallStaticIntMethod(native_library, s_open_content_uri, j_filepath, j_mode); |
| 64 | } | ||
| 65 | |||
| 66 | u64 GetSize(const std::string& filepath) { | ||
| 67 | if (s_get_size == nullptr) { | ||
| 68 | return 0; | ||
| 69 | } | ||
| 70 | auto env = Common::Android::GetEnvForThread(); | ||
| 71 | return static_cast<u64>(env->CallStaticLongMethod( | ||
| 72 | native_library, s_get_size, | ||
| 73 | Common::Android::ToJString(Common::Android::GetEnvForThread(), filepath))); | ||
| 74 | } | ||
| 75 | |||
| 76 | bool IsDirectory(const std::string& filepath) { | ||
| 77 | if (s_is_directory == nullptr) { | ||
| 78 | return 0; | ||
| 79 | } | ||
| 80 | auto env = Common::Android::GetEnvForThread(); | ||
| 81 | return env->CallStaticBooleanMethod( | ||
| 82 | native_library, s_is_directory, | ||
| 83 | Common::Android::ToJString(Common::Android::GetEnvForThread(), filepath)); | ||
| 89 | } | 84 | } |
| 90 | 85 | ||
| 91 | #define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) \ | 86 | bool Exists(const std::string& filepath) { |
| 92 | F(FunctionName, ReturnValue, JMethodID, Caller) | 87 | if (s_file_exists == nullptr) { |
| 93 | #define F(FunctionName, ReturnValue, JMethodID, Caller) \ | 88 | return 0; |
| 94 | ReturnValue FunctionName(const std::string& filepath) { \ | ||
| 95 | if (JMethodID == nullptr) { \ | ||
| 96 | return 0; \ | ||
| 97 | } \ | ||
| 98 | auto env = GetEnvForThread(); \ | ||
| 99 | jstring j_filepath = env->NewStringUTF(filepath.c_str()); \ | ||
| 100 | return env->Caller(native_library, JMethodID, j_filepath); \ | ||
| 101 | } | 89 | } |
| 102 | ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR) | 90 | auto env = Common::Android::GetEnvForThread(); |
| 103 | #undef F | 91 | return env->CallStaticBooleanMethod( |
| 104 | #undef FR | 92 | native_library, s_file_exists, |
| 105 | 93 | Common::Android::ToJString(Common::Android::GetEnvForThread(), filepath)); | |
| 106 | #define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) \ | 94 | } |
| 107 | F(FunctionName, JMethodID, Caller) | 95 | |
| 108 | #define F(FunctionName, JMethodID, Caller) \ | 96 | std::string GetParentDirectory(const std::string& filepath) { |
| 109 | std::string FunctionName(const std::string& filepath) { \ | 97 | if (s_get_parent_directory == nullptr) { |
| 110 | if (JMethodID == nullptr) { \ | 98 | return 0; |
| 111 | return 0; \ | ||
| 112 | } \ | ||
| 113 | auto env = GetEnvForThread(); \ | ||
| 114 | jstring j_filepath = env->NewStringUTF(filepath.c_str()); \ | ||
| 115 | jstring j_return = \ | ||
| 116 | static_cast<jstring>(env->Caller(native_library, JMethodID, j_filepath)); \ | ||
| 117 | if (!j_return) { \ | ||
| 118 | return {}; \ | ||
| 119 | } \ | ||
| 120 | const jchar* jchars = env->GetStringChars(j_return, nullptr); \ | ||
| 121 | const jsize length = env->GetStringLength(j_return); \ | ||
| 122 | const std::u16string_view string_view(reinterpret_cast<const char16_t*>(jchars), length); \ | ||
| 123 | const std::string converted_string = Common::UTF16ToUTF8(string_view); \ | ||
| 124 | env->ReleaseStringChars(j_return, jchars); \ | ||
| 125 | return converted_string; \ | ||
| 126 | } | 99 | } |
| 127 | ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH) | 100 | auto env = Common::Android::GetEnvForThread(); |
| 128 | #undef F | 101 | jstring j_return = static_cast<jstring>(env->CallStaticObjectMethod( |
| 129 | #undef FH | 102 | native_library, s_get_parent_directory, Common::Android::ToJString(env, filepath))); |
| 103 | if (!j_return) { | ||
| 104 | return {}; | ||
| 105 | } | ||
| 106 | return Common::Android::GetJString(env, j_return); | ||
| 107 | } | ||
| 108 | |||
| 109 | std::string GetFilename(const std::string& filepath) { | ||
| 110 | if (s_get_filename == nullptr) { | ||
| 111 | return 0; | ||
| 112 | } | ||
| 113 | auto env = Common::Android::GetEnvForThread(); | ||
| 114 | jstring j_return = static_cast<jstring>(env->CallStaticObjectMethod( | ||
| 115 | native_library, s_get_filename, Common::Android::ToJString(env, filepath))); | ||
| 116 | if (!j_return) { | ||
| 117 | return {}; | ||
| 118 | } | ||
| 119 | return Common::Android::GetJString(env, j_return); | ||
| 120 | } | ||
| 130 | 121 | ||
| 131 | } // namespace Common::FS::Android | 122 | } // namespace Common::FS::Android |
diff --git a/src/common/fs/fs_android.h b/src/common/fs/fs_android.h index 2c9234313..b33f4beb8 100644 --- a/src/common/fs/fs_android.h +++ b/src/common/fs/fs_android.h | |||
| @@ -7,38 +7,17 @@ | |||
| 7 | #include <vector> | 7 | #include <vector> |
| 8 | #include <jni.h> | 8 | #include <jni.h> |
| 9 | 9 | ||
| 10 | #define ANDROID_STORAGE_FUNCTIONS(V) \ | ||
| 11 | V(OpenContentUri, int, (const std::string& filepath, OpenMode openmode), open_content_uri, \ | ||
| 12 | "openContentUri", "(Ljava/lang/String;Ljava/lang/String;)I") | ||
| 13 | |||
| 14 | #define ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(V) \ | ||
| 15 | V(GetSize, std::uint64_t, get_size, CallStaticLongMethod, "getSize", "(Ljava/lang/String;)J") \ | ||
| 16 | V(IsDirectory, bool, is_directory, CallStaticBooleanMethod, "isDirectory", \ | ||
| 17 | "(Ljava/lang/String;)Z") \ | ||
| 18 | V(Exists, bool, file_exists, CallStaticBooleanMethod, "exists", "(Ljava/lang/String;)Z") | ||
| 19 | |||
| 20 | #define ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(V) \ | ||
| 21 | V(GetParentDirectory, get_parent_directory, CallStaticObjectMethod, "getParentDirectory", \ | ||
| 22 | "(Ljava/lang/String;)Ljava/lang/String;") \ | ||
| 23 | V(GetFilename, get_filename, CallStaticObjectMethod, "getFilename", \ | ||
| 24 | "(Ljava/lang/String;)Ljava/lang/String;") | ||
| 25 | |||
| 26 | namespace Common::FS::Android { | 10 | namespace Common::FS::Android { |
| 27 | 11 | ||
| 28 | static JavaVM* g_jvm = nullptr; | 12 | static JavaVM* g_jvm = nullptr; |
| 29 | static jclass native_library = nullptr; | 13 | static jclass native_library = nullptr; |
| 30 | 14 | ||
| 31 | #define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) F(JMethodID) | 15 | static jmethodID s_get_parent_directory; |
| 32 | #define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) F(JMethodID) | 16 | static jmethodID s_get_filename; |
| 33 | #define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) F(JMethodID) | 17 | static jmethodID s_get_size; |
| 34 | #define F(JMethodID) static jmethodID JMethodID = nullptr; | 18 | static jmethodID s_is_directory; |
| 35 | ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH) | 19 | static jmethodID s_file_exists; |
| 36 | ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR) | 20 | static jmethodID s_open_content_uri; |
| 37 | ANDROID_STORAGE_FUNCTIONS(FS) | ||
| 38 | #undef F | ||
| 39 | #undef FS | ||
| 40 | #undef FR | ||
| 41 | #undef FH | ||
| 42 | 21 | ||
| 43 | enum class OpenMode { | 22 | enum class OpenMode { |
| 44 | Read, | 23 | Read, |
| @@ -57,24 +36,11 @@ void UnRegisterCallbacks(); | |||
| 57 | 36 | ||
| 58 | bool IsContentUri(const std::string& path); | 37 | bool IsContentUri(const std::string& path); |
| 59 | 38 | ||
| 60 | #define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) \ | 39 | int OpenContentUri(const std::string& filepath, OpenMode openmode); |
| 61 | F(FunctionName, Parameters, ReturnValue) | 40 | std::uint64_t GetSize(const std::string& filepath); |
| 62 | #define F(FunctionName, Parameters, ReturnValue) ReturnValue FunctionName Parameters; | 41 | bool IsDirectory(const std::string& filepath); |
| 63 | ANDROID_STORAGE_FUNCTIONS(FS) | 42 | bool Exists(const std::string& filepath); |
| 64 | #undef F | 43 | std::string GetParentDirectory(const std::string& filepath); |
| 65 | #undef FS | 44 | std::string GetFilename(const std::string& filepath); |
| 66 | |||
| 67 | #define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) \ | ||
| 68 | F(FunctionName, ReturnValue) | ||
| 69 | #define F(FunctionName, ReturnValue) ReturnValue FunctionName(const std::string& filepath); | ||
| 70 | ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR) | ||
| 71 | #undef F | ||
| 72 | #undef FR | ||
| 73 | |||
| 74 | #define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) F(FunctionName) | ||
| 75 | #define F(FunctionName) std::string FunctionName(const std::string& filepath); | ||
| 76 | ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH) | ||
| 77 | #undef F | ||
| 78 | #undef FH | ||
| 79 | 45 | ||
| 80 | } // namespace Common::FS::Android | 46 | } // namespace Common::FS::Android |
diff --git a/src/common/range_sets.h b/src/common/range_sets.h new file mode 100644 index 000000000..f8fcee483 --- /dev/null +++ b/src/common/range_sets.h | |||
| @@ -0,0 +1,73 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <memory> | ||
| 7 | |||
| 8 | #include "common/common_types.h" | ||
| 9 | |||
| 10 | namespace Common { | ||
| 11 | |||
| 12 | template <typename AddressType> | ||
| 13 | class RangeSet { | ||
| 14 | public: | ||
| 15 | RangeSet(); | ||
| 16 | ~RangeSet(); | ||
| 17 | |||
| 18 | RangeSet(RangeSet const&) = delete; | ||
| 19 | RangeSet& operator=(RangeSet const&) = delete; | ||
| 20 | |||
| 21 | RangeSet(RangeSet&& other); | ||
| 22 | RangeSet& operator=(RangeSet&& other); | ||
| 23 | |||
| 24 | void Add(AddressType base_address, size_t size); | ||
| 25 | void Subtract(AddressType base_address, size_t size); | ||
| 26 | void Clear(); | ||
| 27 | bool Empty() const; | ||
| 28 | |||
| 29 | template <typename Func> | ||
| 30 | void ForEach(Func&& func) const; | ||
| 31 | |||
| 32 | template <typename Func> | ||
| 33 | void ForEachInRange(AddressType device_addr, size_t size, Func&& func) const; | ||
| 34 | |||
| 35 | private: | ||
| 36 | struct RangeSetImpl; | ||
| 37 | std::unique_ptr<RangeSetImpl> m_impl; | ||
| 38 | }; | ||
| 39 | |||
| 40 | template <typename AddressType> | ||
| 41 | class OverlapRangeSet { | ||
| 42 | public: | ||
| 43 | OverlapRangeSet(); | ||
| 44 | ~OverlapRangeSet(); | ||
| 45 | |||
| 46 | OverlapRangeSet(OverlapRangeSet const&) = delete; | ||
| 47 | OverlapRangeSet& operator=(OverlapRangeSet const&) = delete; | ||
| 48 | |||
| 49 | OverlapRangeSet(OverlapRangeSet&& other); | ||
| 50 | OverlapRangeSet& operator=(OverlapRangeSet&& other); | ||
| 51 | |||
| 52 | void Add(AddressType base_address, size_t size); | ||
| 53 | void Subtract(AddressType base_address, size_t size); | ||
| 54 | |||
| 55 | template <typename Func> | ||
| 56 | void Subtract(AddressType base_address, size_t size, Func&& on_delete); | ||
| 57 | |||
| 58 | void DeleteAll(AddressType base_address, size_t size); | ||
| 59 | void Clear(); | ||
| 60 | bool Empty() const; | ||
| 61 | |||
| 62 | template <typename Func> | ||
| 63 | void ForEach(Func&& func) const; | ||
| 64 | |||
| 65 | template <typename Func> | ||
| 66 | void ForEachInRange(AddressType device_addr, size_t size, Func&& func) const; | ||
| 67 | |||
| 68 | private: | ||
| 69 | struct OverlapRangeSetImpl; | ||
| 70 | std::unique_ptr<OverlapRangeSetImpl> m_impl; | ||
| 71 | }; | ||
| 72 | |||
| 73 | } // namespace Common | ||
diff --git a/src/common/range_sets.inc b/src/common/range_sets.inc new file mode 100644 index 000000000..b83eceb7b --- /dev/null +++ b/src/common/range_sets.inc | |||
| @@ -0,0 +1,304 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <limits> | ||
| 7 | #include <utility> | ||
| 8 | |||
| 9 | #include <boost/icl/interval.hpp> | ||
| 10 | #include <boost/icl/interval_base_set.hpp> | ||
| 11 | #include <boost/icl/interval_map.hpp> | ||
| 12 | #include <boost/icl/interval_set.hpp> | ||
| 13 | #include <boost/icl/split_interval_map.hpp> | ||
| 14 | #include <boost/pool/pool.hpp> | ||
| 15 | #include <boost/pool/pool_alloc.hpp> | ||
| 16 | #include <boost/pool/poolfwd.hpp> | ||
| 17 | |||
| 18 | #include "common/range_sets.h" | ||
| 19 | |||
| 20 | namespace Common { | ||
| 21 | |||
| 22 | namespace { | ||
| 23 | template <class T> | ||
| 24 | using RangeSetsAllocator = | ||
| 25 | boost::fast_pool_allocator<T, boost::default_user_allocator_new_delete, | ||
| 26 | boost::details::pool::default_mutex, 1024, 2048>; | ||
| 27 | } | ||
| 28 | |||
| 29 | template <typename AddressType> | ||
| 30 | struct RangeSet<AddressType>::RangeSetImpl { | ||
| 31 | using IntervalSet = boost::icl::interval_set< | ||
| 32 | AddressType, std::less, ICL_INTERVAL_INSTANCE(ICL_INTERVAL_DEFAULT, AddressType, std::less), | ||
| 33 | RangeSetsAllocator>; | ||
| 34 | using IntervalType = typename IntervalSet::interval_type; | ||
| 35 | |||
| 36 | RangeSetImpl() = default; | ||
| 37 | ~RangeSetImpl() = default; | ||
| 38 | |||
| 39 | void Add(AddressType base_address, size_t size) { | ||
| 40 | AddressType end_address = base_address + static_cast<AddressType>(size); | ||
| 41 | IntervalType interval{base_address, end_address}; | ||
| 42 | m_ranges_set.add(interval); | ||
| 43 | } | ||
| 44 | |||
| 45 | void Subtract(AddressType base_address, size_t size) { | ||
| 46 | AddressType end_address = base_address + static_cast<AddressType>(size); | ||
| 47 | IntervalType interval{base_address, end_address}; | ||
| 48 | m_ranges_set.subtract(interval); | ||
| 49 | } | ||
| 50 | |||
| 51 | template <typename Func> | ||
| 52 | void ForEach(Func&& func) const { | ||
| 53 | if (m_ranges_set.empty()) { | ||
| 54 | return; | ||
| 55 | } | ||
| 56 | auto it = m_ranges_set.begin(); | ||
| 57 | auto end_it = m_ranges_set.end(); | ||
| 58 | for (; it != end_it; it++) { | ||
| 59 | const AddressType inter_addr_end = it->upper(); | ||
| 60 | const AddressType inter_addr = it->lower(); | ||
| 61 | func(inter_addr, inter_addr_end); | ||
| 62 | } | ||
| 63 | } | ||
| 64 | |||
| 65 | template <typename Func> | ||
| 66 | void ForEachInRange(AddressType base_addr, size_t size, Func&& func) const { | ||
| 67 | if (m_ranges_set.empty()) { | ||
| 68 | return; | ||
| 69 | } | ||
| 70 | const AddressType start_address = base_addr; | ||
| 71 | const AddressType end_address = start_address + size; | ||
| 72 | const RangeSetImpl::IntervalType search_interval{start_address, end_address}; | ||
| 73 | auto it = m_ranges_set.lower_bound(search_interval); | ||
| 74 | if (it == m_ranges_set.end()) { | ||
| 75 | return; | ||
| 76 | } | ||
| 77 | auto end_it = m_ranges_set.upper_bound(search_interval); | ||
| 78 | for (; it != end_it; it++) { | ||
| 79 | AddressType inter_addr_end = it->upper(); | ||
| 80 | AddressType inter_addr = it->lower(); | ||
| 81 | if (inter_addr_end > end_address) { | ||
| 82 | inter_addr_end = end_address; | ||
| 83 | } | ||
| 84 | if (inter_addr < start_address) { | ||
| 85 | inter_addr = start_address; | ||
| 86 | } | ||
| 87 | func(inter_addr, inter_addr_end); | ||
| 88 | } | ||
| 89 | } | ||
| 90 | |||
| 91 | IntervalSet m_ranges_set; | ||
| 92 | }; | ||
| 93 | |||
| 94 | template <typename AddressType> | ||
| 95 | struct OverlapRangeSet<AddressType>::OverlapRangeSetImpl { | ||
| 96 | using IntervalSet = boost::icl::split_interval_map< | ||
| 97 | AddressType, s32, boost::icl::partial_enricher, std::less, boost::icl::inplace_plus, | ||
| 98 | boost::icl::inter_section, | ||
| 99 | ICL_INTERVAL_INSTANCE(ICL_INTERVAL_DEFAULT, AddressType, std::less), RangeSetsAllocator>; | ||
| 100 | using IntervalType = typename IntervalSet::interval_type; | ||
| 101 | |||
| 102 | OverlapRangeSetImpl() = default; | ||
| 103 | ~OverlapRangeSetImpl() = default; | ||
| 104 | |||
| 105 | void Add(AddressType base_address, size_t size) { | ||
| 106 | AddressType end_address = base_address + static_cast<AddressType>(size); | ||
| 107 | IntervalType interval{base_address, end_address}; | ||
| 108 | m_split_ranges_set += std::make_pair(interval, 1); | ||
| 109 | } | ||
| 110 | |||
| 111 | template <bool has_on_delete, typename Func> | ||
| 112 | void Subtract(AddressType base_address, size_t size, s32 amount, | ||
| 113 | [[maybe_unused]] Func&& on_delete) { | ||
| 114 | if (m_split_ranges_set.empty()) { | ||
| 115 | return; | ||
| 116 | } | ||
| 117 | AddressType end_address = base_address + static_cast<AddressType>(size); | ||
| 118 | IntervalType interval{base_address, end_address}; | ||
| 119 | bool any_removals = false; | ||
| 120 | m_split_ranges_set += std::make_pair(interval, -amount); | ||
| 121 | do { | ||
| 122 | any_removals = false; | ||
| 123 | auto it = m_split_ranges_set.lower_bound(interval); | ||
| 124 | if (it == m_split_ranges_set.end()) { | ||
| 125 | return; | ||
| 126 | } | ||
| 127 | auto end_it = m_split_ranges_set.upper_bound(interval); | ||
| 128 | for (; it != end_it; it++) { | ||
| 129 | if (it->second <= 0) { | ||
| 130 | if constexpr (has_on_delete) { | ||
| 131 | if (it->second == 0) { | ||
| 132 | on_delete(it->first.lower(), it->first.upper()); | ||
| 133 | } | ||
| 134 | } | ||
| 135 | any_removals = true; | ||
| 136 | m_split_ranges_set.erase(it); | ||
| 137 | break; | ||
| 138 | } | ||
| 139 | } | ||
| 140 | } while (any_removals); | ||
| 141 | } | ||
| 142 | |||
| 143 | template <typename Func> | ||
| 144 | void ForEach(Func&& func) const { | ||
| 145 | if (m_split_ranges_set.empty()) { | ||
| 146 | return; | ||
| 147 | } | ||
| 148 | auto it = m_split_ranges_set.begin(); | ||
| 149 | auto end_it = m_split_ranges_set.end(); | ||
| 150 | for (; it != end_it; it++) { | ||
| 151 | const AddressType inter_addr_end = it->first.upper(); | ||
| 152 | const AddressType inter_addr = it->first.lower(); | ||
| 153 | func(inter_addr, inter_addr_end, it->second); | ||
| 154 | } | ||
| 155 | } | ||
| 156 | |||
| 157 | template <typename Func> | ||
| 158 | void ForEachInRange(AddressType base_address, size_t size, Func&& func) const { | ||
| 159 | if (m_split_ranges_set.empty()) { | ||
| 160 | return; | ||
| 161 | } | ||
| 162 | const AddressType start_address = base_address; | ||
| 163 | const AddressType end_address = start_address + size; | ||
| 164 | const OverlapRangeSetImpl::IntervalType search_interval{start_address, end_address}; | ||
| 165 | auto it = m_split_ranges_set.lower_bound(search_interval); | ||
| 166 | if (it == m_split_ranges_set.end()) { | ||
| 167 | return; | ||
| 168 | } | ||
| 169 | auto end_it = m_split_ranges_set.upper_bound(search_interval); | ||
| 170 | for (; it != end_it; it++) { | ||
| 171 | auto& inter = it->first; | ||
| 172 | AddressType inter_addr_end = inter.upper(); | ||
| 173 | AddressType inter_addr = inter.lower(); | ||
| 174 | if (inter_addr_end > end_address) { | ||
| 175 | inter_addr_end = end_address; | ||
| 176 | } | ||
| 177 | if (inter_addr < start_address) { | ||
| 178 | inter_addr = start_address; | ||
| 179 | } | ||
| 180 | func(inter_addr, inter_addr_end, it->second); | ||
| 181 | } | ||
| 182 | } | ||
| 183 | |||
| 184 | IntervalSet m_split_ranges_set; | ||
| 185 | }; | ||
| 186 | |||
| 187 | template <typename AddressType> | ||
| 188 | RangeSet<AddressType>::RangeSet() { | ||
| 189 | m_impl = std::make_unique<RangeSet<AddressType>::RangeSetImpl>(); | ||
| 190 | } | ||
| 191 | |||
| 192 | template <typename AddressType> | ||
| 193 | RangeSet<AddressType>::~RangeSet() = default; | ||
| 194 | |||
| 195 | template <typename AddressType> | ||
| 196 | RangeSet<AddressType>::RangeSet(RangeSet&& other) { | ||
| 197 | m_impl = std::make_unique<RangeSet<AddressType>::RangeSetImpl>(); | ||
| 198 | m_impl->m_ranges_set = std::move(other.m_impl->m_ranges_set); | ||
| 199 | } | ||
| 200 | |||
| 201 | template <typename AddressType> | ||
| 202 | RangeSet<AddressType>& RangeSet<AddressType>::operator=(RangeSet&& other) { | ||
| 203 | m_impl->m_ranges_set = std::move(other.m_impl->m_ranges_set); | ||
| 204 | } | ||
| 205 | |||
| 206 | template <typename AddressType> | ||
| 207 | void RangeSet<AddressType>::Add(AddressType base_address, size_t size) { | ||
| 208 | m_impl->Add(base_address, size); | ||
| 209 | } | ||
| 210 | |||
| 211 | template <typename AddressType> | ||
| 212 | void RangeSet<AddressType>::Subtract(AddressType base_address, size_t size) { | ||
| 213 | m_impl->Subtract(base_address, size); | ||
| 214 | } | ||
| 215 | |||
| 216 | template <typename AddressType> | ||
| 217 | void RangeSet<AddressType>::Clear() { | ||
| 218 | m_impl->m_ranges_set.clear(); | ||
| 219 | } | ||
| 220 | |||
| 221 | template <typename AddressType> | ||
| 222 | bool RangeSet<AddressType>::Empty() const { | ||
| 223 | return m_impl->m_ranges_set.empty(); | ||
| 224 | } | ||
| 225 | |||
| 226 | template <typename AddressType> | ||
| 227 | template <typename Func> | ||
| 228 | void RangeSet<AddressType>::ForEach(Func&& func) const { | ||
| 229 | m_impl->ForEach(std::move(func)); | ||
| 230 | } | ||
| 231 | |||
| 232 | template <typename AddressType> | ||
| 233 | template <typename Func> | ||
| 234 | void RangeSet<AddressType>::ForEachInRange(AddressType base_address, size_t size, | ||
| 235 | Func&& func) const { | ||
| 236 | m_impl->ForEachInRange(base_address, size, std::move(func)); | ||
| 237 | } | ||
| 238 | |||
| 239 | template <typename AddressType> | ||
| 240 | OverlapRangeSet<AddressType>::OverlapRangeSet() { | ||
| 241 | m_impl = std::make_unique<OverlapRangeSet<AddressType>::OverlapRangeSetImpl>(); | ||
| 242 | } | ||
| 243 | |||
| 244 | template <typename AddressType> | ||
| 245 | OverlapRangeSet<AddressType>::~OverlapRangeSet() = default; | ||
| 246 | |||
| 247 | template <typename AddressType> | ||
| 248 | OverlapRangeSet<AddressType>::OverlapRangeSet(OverlapRangeSet&& other) { | ||
| 249 | m_impl = std::make_unique<OverlapRangeSet<AddressType>::OverlapRangeSetImpl>(); | ||
| 250 | m_impl->m_split_ranges_set = std::move(other.m_impl->m_split_ranges_set); | ||
| 251 | } | ||
| 252 | |||
| 253 | template <typename AddressType> | ||
| 254 | OverlapRangeSet<AddressType>& OverlapRangeSet<AddressType>::operator=(OverlapRangeSet&& other) { | ||
| 255 | m_impl->m_split_ranges_set = std::move(other.m_impl->m_split_ranges_set); | ||
| 256 | } | ||
| 257 | |||
| 258 | template <typename AddressType> | ||
| 259 | void OverlapRangeSet<AddressType>::Add(AddressType base_address, size_t size) { | ||
| 260 | m_impl->Add(base_address, size); | ||
| 261 | } | ||
| 262 | |||
| 263 | template <typename AddressType> | ||
| 264 | void OverlapRangeSet<AddressType>::Subtract(AddressType base_address, size_t size) { | ||
| 265 | m_impl->template Subtract<false>(base_address, size, 1, [](AddressType, AddressType) {}); | ||
| 266 | } | ||
| 267 | |||
| 268 | template <typename AddressType> | ||
| 269 | template <typename Func> | ||
| 270 | void OverlapRangeSet<AddressType>::Subtract(AddressType base_address, size_t size, | ||
| 271 | Func&& on_delete) { | ||
| 272 | m_impl->template Subtract<true, Func>(base_address, size, 1, std::move(on_delete)); | ||
| 273 | } | ||
| 274 | |||
| 275 | template <typename AddressType> | ||
| 276 | void OverlapRangeSet<AddressType>::DeleteAll(AddressType base_address, size_t size) { | ||
| 277 | m_impl->template Subtract<false>(base_address, size, std::numeric_limits<s32>::max(), | ||
| 278 | [](AddressType, AddressType) {}); | ||
| 279 | } | ||
| 280 | |||
| 281 | template <typename AddressType> | ||
| 282 | void OverlapRangeSet<AddressType>::Clear() { | ||
| 283 | m_impl->m_split_ranges_set.clear(); | ||
| 284 | } | ||
| 285 | |||
| 286 | template <typename AddressType> | ||
| 287 | bool OverlapRangeSet<AddressType>::Empty() const { | ||
| 288 | return m_impl->m_split_ranges_set.empty(); | ||
| 289 | } | ||
| 290 | |||
| 291 | template <typename AddressType> | ||
| 292 | template <typename Func> | ||
| 293 | void OverlapRangeSet<AddressType>::ForEach(Func&& func) const { | ||
| 294 | m_impl->ForEach(func); | ||
| 295 | } | ||
| 296 | |||
| 297 | template <typename AddressType> | ||
| 298 | template <typename Func> | ||
| 299 | void OverlapRangeSet<AddressType>::ForEachInRange(AddressType base_address, size_t size, | ||
| 300 | Func&& func) const { | ||
| 301 | m_impl->ForEachInRange(base_address, size, std::move(func)); | ||
| 302 | } | ||
| 303 | |||
| 304 | } // namespace Common | ||
diff --git a/src/common/settings.cpp b/src/common/settings.cpp index 07709d4e5..80d388fe8 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp | |||
| @@ -30,6 +30,7 @@ namespace Settings { | |||
| 30 | #define SETTING(TYPE, RANGED) template class Setting<TYPE, RANGED> | 30 | #define SETTING(TYPE, RANGED) template class Setting<TYPE, RANGED> |
| 31 | #define SWITCHABLE(TYPE, RANGED) template class SwitchableSetting<TYPE, RANGED> | 31 | #define SWITCHABLE(TYPE, RANGED) template class SwitchableSetting<TYPE, RANGED> |
| 32 | 32 | ||
| 33 | SETTING(AppletMode, false); | ||
| 33 | SETTING(AudioEngine, false); | 34 | SETTING(AudioEngine, false); |
| 34 | SETTING(bool, false); | 35 | SETTING(bool, false); |
| 35 | SETTING(int, false); | 36 | SETTING(int, false); |
| @@ -215,6 +216,8 @@ const char* TranslateCategory(Category category) { | |||
| 215 | return "Debugging"; | 216 | return "Debugging"; |
| 216 | case Category::GpuDriver: | 217 | case Category::GpuDriver: |
| 217 | return "GpuDriver"; | 218 | return "GpuDriver"; |
| 219 | case Category::LibraryApplet: | ||
| 220 | return "LibraryApplet"; | ||
| 218 | case Category::Miscellaneous: | 221 | case Category::Miscellaneous: |
| 219 | return "Miscellaneous"; | 222 | return "Miscellaneous"; |
| 220 | case Category::Network: | 223 | case Category::Network: |
diff --git a/src/common/settings.h b/src/common/settings.h index f1b1add56..aa054dc24 100644 --- a/src/common/settings.h +++ b/src/common/settings.h | |||
| @@ -133,6 +133,38 @@ struct TouchFromButtonMap { | |||
| 133 | struct Values { | 133 | struct Values { |
| 134 | Linkage linkage{}; | 134 | Linkage linkage{}; |
| 135 | 135 | ||
| 136 | // Applet | ||
| 137 | Setting<AppletMode> cabinet_applet_mode{linkage, AppletMode::LLE, "cabinet_applet_mode", | ||
| 138 | Category::LibraryApplet}; | ||
| 139 | Setting<AppletMode> controller_applet_mode{linkage, AppletMode::HLE, "controller_applet_mode", | ||
| 140 | Category::LibraryApplet}; | ||
| 141 | Setting<AppletMode> data_erase_applet_mode{linkage, AppletMode::HLE, "data_erase_applet_mode", | ||
| 142 | Category::LibraryApplet}; | ||
| 143 | Setting<AppletMode> error_applet_mode{linkage, AppletMode::HLE, "error_applet_mode", | ||
| 144 | Category::LibraryApplet}; | ||
| 145 | Setting<AppletMode> net_connect_applet_mode{linkage, AppletMode::HLE, "net_connect_applet_mode", | ||
| 146 | Category::LibraryApplet}; | ||
| 147 | Setting<AppletMode> player_select_applet_mode{ | ||
| 148 | linkage, AppletMode::HLE, "player_select_applet_mode", Category::LibraryApplet}; | ||
| 149 | Setting<AppletMode> swkbd_applet_mode{linkage, AppletMode::LLE, "swkbd_applet_mode", | ||
| 150 | Category::LibraryApplet}; | ||
| 151 | Setting<AppletMode> mii_edit_applet_mode{linkage, AppletMode::LLE, "mii_edit_applet_mode", | ||
| 152 | Category::LibraryApplet}; | ||
| 153 | Setting<AppletMode> web_applet_mode{linkage, AppletMode::HLE, "web_applet_mode", | ||
| 154 | Category::LibraryApplet}; | ||
| 155 | Setting<AppletMode> shop_applet_mode{linkage, AppletMode::HLE, "shop_applet_mode", | ||
| 156 | Category::LibraryApplet}; | ||
| 157 | Setting<AppletMode> photo_viewer_applet_mode{ | ||
| 158 | linkage, AppletMode::LLE, "photo_viewer_applet_mode", Category::LibraryApplet}; | ||
| 159 | Setting<AppletMode> offline_web_applet_mode{linkage, AppletMode::LLE, "offline_web_applet_mode", | ||
| 160 | Category::LibraryApplet}; | ||
| 161 | Setting<AppletMode> login_share_applet_mode{linkage, AppletMode::HLE, "login_share_applet_mode", | ||
| 162 | Category::LibraryApplet}; | ||
| 163 | Setting<AppletMode> wifi_web_auth_applet_mode{ | ||
| 164 | linkage, AppletMode::HLE, "wifi_web_auth_applet_mode", Category::LibraryApplet}; | ||
| 165 | Setting<AppletMode> my_page_applet_mode{linkage, AppletMode::LLE, "my_page_applet_mode", | ||
| 166 | Category::LibraryApplet}; | ||
| 167 | |||
| 136 | // Audio | 168 | // Audio |
| 137 | SwitchableSetting<AudioEngine> sink_id{linkage, AudioEngine::Auto, "output_engine", | 169 | SwitchableSetting<AudioEngine> sink_id{linkage, AudioEngine::Auto, "output_engine", |
| 138 | Category::Audio, Specialization::RuntimeList}; | 170 | Category::Audio, Specialization::RuntimeList}; |
diff --git a/src/common/settings_common.h b/src/common/settings_common.h index 987489e8a..2df3f0809 100644 --- a/src/common/settings_common.h +++ b/src/common/settings_common.h | |||
| @@ -44,6 +44,7 @@ enum class Category : u32 { | |||
| 44 | Services, | 44 | Services, |
| 45 | Paths, | 45 | Paths, |
| 46 | Linux, | 46 | Linux, |
| 47 | LibraryApplet, | ||
| 47 | MaxEnum, | 48 | MaxEnum, |
| 48 | }; | 49 | }; |
| 49 | 50 | ||
diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h index 617036588..f42367e67 100644 --- a/src/common/settings_enums.h +++ b/src/common/settings_enums.h | |||
| @@ -151,6 +151,8 @@ ENUM(AspectRatio, R16_9, R4_3, R21_9, R16_10, Stretch); | |||
| 151 | 151 | ||
| 152 | ENUM(ConsoleMode, Handheld, Docked); | 152 | ENUM(ConsoleMode, Handheld, Docked); |
| 153 | 153 | ||
| 154 | ENUM(AppletMode, HLE, LLE); | ||
| 155 | |||
| 154 | template <typename Type> | 156 | template <typename Type> |
| 155 | inline std::string CanonicalizeEnum(Type id) { | 157 | inline std::string CanonicalizeEnum(Type id) { |
| 156 | const auto group = EnumMetadata<Type>::Canonicalizations(); | 158 | const auto group = EnumMetadata<Type>::Canonicalizations(); |
diff --git a/src/video_core/texture_cache/slot_vector.h b/src/common/slot_vector.h index 3ffa2a661..34ff7de94 100644 --- a/src/video_core/texture_cache/slot_vector.h +++ b/src/common/slot_vector.h | |||
| @@ -14,7 +14,7 @@ | |||
| 14 | #include "common/common_types.h" | 14 | #include "common/common_types.h" |
| 15 | #include "common/polyfill_ranges.h" | 15 | #include "common/polyfill_ranges.h" |
| 16 | 16 | ||
| 17 | namespace VideoCommon { | 17 | namespace Common { |
| 18 | 18 | ||
| 19 | struct SlotId { | 19 | struct SlotId { |
| 20 | static constexpr u32 INVALID_INDEX = std::numeric_limits<u32>::max(); | 20 | static constexpr u32 INVALID_INDEX = std::numeric_limits<u32>::max(); |
| @@ -217,11 +217,11 @@ private: | |||
| 217 | std::vector<u32> free_list; | 217 | std::vector<u32> free_list; |
| 218 | }; | 218 | }; |
| 219 | 219 | ||
| 220 | } // namespace VideoCommon | 220 | } // namespace Common |
| 221 | 221 | ||
| 222 | template <> | 222 | template <> |
| 223 | struct std::hash<VideoCommon::SlotId> { | 223 | struct std::hash<Common::SlotId> { |
| 224 | size_t operator()(const VideoCommon::SlotId& id) const noexcept { | 224 | size_t operator()(const Common::SlotId& id) const noexcept { |
| 225 | return std::hash<u32>{}(id.index); | 225 | return std::hash<u32>{}(id.index); |
| 226 | } | 226 | } |
| 227 | }; | 227 | }; |
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index eb8f643a2..2d5490968 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -512,10 +512,35 @@ add_library(core STATIC | |||
| 512 | hle/service/audio/hwopus.h | 512 | hle/service/audio/hwopus.h |
| 513 | hle/service/bcat/backend/backend.cpp | 513 | hle/service/bcat/backend/backend.cpp |
| 514 | hle/service/bcat/backend/backend.h | 514 | hle/service/bcat/backend/backend.h |
| 515 | hle/service/bcat/news/newly_arrived_event_holder.cpp | ||
| 516 | hle/service/bcat/news/newly_arrived_event_holder.h | ||
| 517 | hle/service/bcat/news/news_data_service.cpp | ||
| 518 | hle/service/bcat/news/news_data_service.h | ||
| 519 | hle/service/bcat/news/news_database_service.cpp | ||
| 520 | hle/service/bcat/news/news_database_service.h | ||
| 521 | hle/service/bcat/news/news_service.cpp | ||
| 522 | hle/service/bcat/news/news_service.h | ||
| 523 | hle/service/bcat/news/overwrite_event_holder.cpp | ||
| 524 | hle/service/bcat/news/overwrite_event_holder.h | ||
| 525 | hle/service/bcat/news/service_creator.cpp | ||
| 526 | hle/service/bcat/news/service_creator.h | ||
| 515 | hle/service/bcat/bcat.cpp | 527 | hle/service/bcat/bcat.cpp |
| 516 | hle/service/bcat/bcat.h | 528 | hle/service/bcat/bcat.h |
| 517 | hle/service/bcat/bcat_module.cpp | 529 | hle/service/bcat/bcat_result.h |
| 518 | hle/service/bcat/bcat_module.h | 530 | hle/service/bcat/bcat_service.cpp |
| 531 | hle/service/bcat/bcat_service.h | ||
| 532 | hle/service/bcat/bcat_types.h | ||
| 533 | hle/service/bcat/bcat_util.h | ||
| 534 | hle/service/bcat/delivery_cache_directory_service.cpp | ||
| 535 | hle/service/bcat/delivery_cache_directory_service.h | ||
| 536 | hle/service/bcat/delivery_cache_file_service.cpp | ||
| 537 | hle/service/bcat/delivery_cache_file_service.h | ||
| 538 | hle/service/bcat/delivery_cache_progress_service.cpp | ||
| 539 | hle/service/bcat/delivery_cache_progress_service.h | ||
| 540 | hle/service/bcat/delivery_cache_storage_service.cpp | ||
| 541 | hle/service/bcat/delivery_cache_storage_service.h | ||
| 542 | hle/service/bcat/service_creator.cpp | ||
| 543 | hle/service/bcat/service_creator.h | ||
| 519 | hle/service/bpc/bpc.cpp | 544 | hle/service/bpc/bpc.cpp |
| 520 | hle/service/bpc/bpc.h | 545 | hle/service/bpc/bpc.h |
| 521 | hle/service/btdrv/btdrv.cpp | 546 | hle/service/btdrv/btdrv.cpp |
| @@ -548,8 +573,6 @@ add_library(core STATIC | |||
| 548 | hle/service/es/es.h | 573 | hle/service/es/es.h |
| 549 | hle/service/eupld/eupld.cpp | 574 | hle/service/eupld/eupld.cpp |
| 550 | hle/service/eupld/eupld.h | 575 | hle/service/eupld/eupld.h |
| 551 | hle/service/event.cpp | ||
| 552 | hle/service/event.h | ||
| 553 | hle/service/fatal/fatal.cpp | 576 | hle/service/fatal/fatal.cpp |
| 554 | hle/service/fatal/fatal.h | 577 | hle/service/fatal/fatal.h |
| 555 | hle/service/fatal/fatal_p.cpp | 578 | hle/service/fatal/fatal_p.cpp |
| @@ -676,8 +699,6 @@ add_library(core STATIC | |||
| 676 | hle/service/mm/mm_u.h | 699 | hle/service/mm/mm_u.h |
| 677 | hle/service/mnpp/mnpp_app.cpp | 700 | hle/service/mnpp/mnpp_app.cpp |
| 678 | hle/service/mnpp/mnpp_app.h | 701 | hle/service/mnpp/mnpp_app.h |
| 679 | hle/service/mutex.cpp | ||
| 680 | hle/service/mutex.h | ||
| 681 | hle/service/ncm/ncm.cpp | 702 | hle/service/ncm/ncm.cpp |
| 682 | hle/service/ncm/ncm.h | 703 | hle/service/ncm/ncm.h |
| 683 | hle/service/nfc/common/amiibo_crypto.cpp | 704 | hle/service/nfc/common/amiibo_crypto.cpp |
| @@ -790,6 +811,15 @@ add_library(core STATIC | |||
| 790 | hle/service/nvnflinger/window.h | 811 | hle/service/nvnflinger/window.h |
| 791 | hle/service/olsc/olsc.cpp | 812 | hle/service/olsc/olsc.cpp |
| 792 | hle/service/olsc/olsc.h | 813 | hle/service/olsc/olsc.h |
| 814 | hle/service/os/event.cpp | ||
| 815 | hle/service/os/event.h | ||
| 816 | hle/service/os/multi_wait_holder.cpp | ||
| 817 | hle/service/os/multi_wait_holder.h | ||
| 818 | hle/service/os/multi_wait_utils.h | ||
| 819 | hle/service/os/multi_wait.cpp | ||
| 820 | hle/service/os/multi_wait.h | ||
| 821 | hle/service/os/mutex.cpp | ||
| 822 | hle/service/os/mutex.h | ||
| 793 | hle/service/pcie/pcie.cpp | 823 | hle/service/pcie/pcie.cpp |
| 794 | hle/service/pcie/pcie.h | 824 | hle/service/pcie/pcie.h |
| 795 | hle/service/pctl/pctl.cpp | 825 | hle/service/pctl/pctl.cpp |
diff --git a/src/core/device_memory_manager.inc b/src/core/device_memory_manager.inc index b026f4220..6dfee806c 100644 --- a/src/core/device_memory_manager.inc +++ b/src/core/device_memory_manager.inc | |||
| @@ -532,6 +532,7 @@ void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size | |||
| 532 | cache_bytes = 0; | 532 | cache_bytes = 0; |
| 533 | } | 533 | } |
| 534 | }; | 534 | }; |
| 535 | size_t old_vpage = (base_vaddress >> Memory::YUZU_PAGEBITS) - 1; | ||
| 535 | for (; page != page_end; ++page) { | 536 | for (; page != page_end; ++page) { |
| 536 | CounterAtomicType& count = cached_pages->at(page >> subentries_shift).Count(page); | 537 | CounterAtomicType& count = cached_pages->at(page >> subentries_shift).Count(page); |
| 537 | auto [asid_2, vpage] = ExtractCPUBacking(page); | 538 | auto [asid_2, vpage] = ExtractCPUBacking(page); |
| @@ -547,6 +548,12 @@ void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size | |||
| 547 | memory_device_inter = registered_processes[asid_2.id]; | 548 | memory_device_inter = registered_processes[asid_2.id]; |
| 548 | } | 549 | } |
| 549 | 550 | ||
| 551 | if (vpage != old_vpage + 1) [[unlikely]] { | ||
| 552 | release_pending(); | ||
| 553 | } | ||
| 554 | |||
| 555 | old_vpage = vpage; | ||
| 556 | |||
| 550 | // Adds or subtracts 1, as count is a unsigned 8-bit value | 557 | // Adds or subtracts 1, as count is a unsigned 8-bit value |
| 551 | count.fetch_add(static_cast<CounterType>(delta), std::memory_order_release); | 558 | count.fetch_add(static_cast<CounterType>(delta), std::memory_order_release); |
| 552 | 559 | ||
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index 285fe4db6..665252358 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp | |||
| @@ -172,6 +172,10 @@ u32 NCA::GetSDKVersion() const { | |||
| 172 | return reader->GetSdkAddonVersion(); | 172 | return reader->GetSdkAddonVersion(); |
| 173 | } | 173 | } |
| 174 | 174 | ||
| 175 | u8 NCA::GetKeyGeneration() const { | ||
| 176 | return reader->GetKeyGeneration(); | ||
| 177 | } | ||
| 178 | |||
| 175 | bool NCA::IsUpdate() const { | 179 | bool NCA::IsUpdate() const { |
| 176 | return is_update; | 180 | return is_update; |
| 177 | } | 181 | } |
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h index f68464eb0..8560617f5 100644 --- a/src/core/file_sys/content_archive.h +++ b/src/core/file_sys/content_archive.h | |||
| @@ -77,6 +77,7 @@ public: | |||
| 77 | u64 GetTitleId() const; | 77 | u64 GetTitleId() const; |
| 78 | RightsId GetRightsId() const; | 78 | RightsId GetRightsId() const; |
| 79 | u32 GetSDKVersion() const; | 79 | u32 GetSDKVersion() const; |
| 80 | u8 GetKeyGeneration() const; | ||
| 80 | bool IsUpdate() const; | 81 | bool IsUpdate() const; |
| 81 | 82 | ||
| 82 | VirtualFile GetRomFS() const; | 83 | VirtualFile GetRomFS() const; |
diff --git a/src/core/file_sys/errors.h b/src/core/file_sys/errors.h index d4e0eb6f4..b22767bf5 100644 --- a/src/core/file_sys/errors.h +++ b/src/core/file_sys/errors.h | |||
| @@ -91,6 +91,7 @@ constexpr Result ResultWriteNotPermitted{ErrorModule::FS, 6203}; | |||
| 91 | constexpr Result ResultUnsupportedSetSizeForIndirectStorage{ErrorModule::FS, 6325}; | 91 | constexpr Result ResultUnsupportedSetSizeForIndirectStorage{ErrorModule::FS, 6325}; |
| 92 | constexpr Result ResultUnsupportedWriteForCompressedStorage{ErrorModule::FS, 6387}; | 92 | constexpr Result ResultUnsupportedWriteForCompressedStorage{ErrorModule::FS, 6387}; |
| 93 | constexpr Result ResultUnsupportedOperateRangeForCompressedStorage{ErrorModule::FS, 6388}; | 93 | constexpr Result ResultUnsupportedOperateRangeForCompressedStorage{ErrorModule::FS, 6388}; |
| 94 | constexpr Result ResultPermissionDenied{ErrorModule::FS, 6400}; | ||
| 94 | constexpr Result ResultBufferAllocationFailed{ErrorModule::FS, 6705}; | 95 | constexpr Result ResultBufferAllocationFailed{ErrorModule::FS, 6705}; |
| 95 | 96 | ||
| 96 | } // namespace FileSys | 97 | } // namespace FileSys |
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp index 0b08e877e..1bcc42890 100644 --- a/src/core/hle/kernel/k_process.cpp +++ b/src/core/hle/kernel/k_process.cpp | |||
| @@ -4,8 +4,9 @@ | |||
| 4 | #include <random> | 4 | #include <random> |
| 5 | #include "common/scope_exit.h" | 5 | #include "common/scope_exit.h" |
| 6 | #include "common/settings.h" | 6 | #include "common/settings.h" |
| 7 | #include "core/arm/dynarmic/arm_dynarmic.h" | ||
| 8 | #include "core/arm/dynarmic/dynarmic_exclusive_monitor.h" | ||
| 7 | #include "core/core.h" | 9 | #include "core/core.h" |
| 8 | #include "core/gpu_dirty_memory_manager.h" | ||
| 9 | #include "core/hle/kernel/k_process.h" | 10 | #include "core/hle/kernel/k_process.h" |
| 10 | #include "core/hle/kernel/k_scoped_resource_reservation.h" | 11 | #include "core/hle/kernel/k_scoped_resource_reservation.h" |
| 11 | #include "core/hle/kernel/k_shared_memory.h" | 12 | #include "core/hle/kernel/k_shared_memory.h" |
| @@ -1258,6 +1259,10 @@ void KProcess::InitializeInterfaces() { | |||
| 1258 | 1259 | ||
| 1259 | #ifdef HAS_NCE | 1260 | #ifdef HAS_NCE |
| 1260 | if (this->IsApplication() && Settings::IsNceEnabled()) { | 1261 | if (this->IsApplication() && Settings::IsNceEnabled()) { |
| 1262 | // Register the scoped JIT handler before creating any NCE instances | ||
| 1263 | // so that its signal handler will appear first in the signal chain. | ||
| 1264 | Core::ScopedJitExecution::RegisterHandler(); | ||
| 1265 | |||
| 1261 | for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { | 1266 | for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { |
| 1262 | m_arm_interfaces[i] = std::make_unique<Core::ArmNce>(m_kernel.System(), true, i); | 1267 | m_arm_interfaces[i] = std::make_unique<Core::ArmNce>(m_kernel.System(), true, i); |
| 1263 | } | 1268 | } |
diff --git a/src/core/hle/service/am/am_types.h b/src/core/hle/service/am/am_types.h index a2b852b12..8c33feb15 100644 --- a/src/core/hle/service/am/am_types.h +++ b/src/core/hle/service/am/am_types.h | |||
| @@ -130,9 +130,9 @@ enum class AppletProgramId : u64 { | |||
| 130 | 130 | ||
| 131 | enum class LibraryAppletMode : u32 { | 131 | enum class LibraryAppletMode : u32 { |
| 132 | AllForeground = 0, | 132 | AllForeground = 0, |
| 133 | Background = 1, | 133 | PartialForeground = 1, |
| 134 | NoUI = 2, | 134 | NoUi = 2, |
| 135 | BackgroundIndirectDisplay = 3, | 135 | PartialForegroundIndirectDisplay = 3, |
| 136 | AllForegroundInitiallyHidden = 4, | 136 | AllForegroundInitiallyHidden = 4, |
| 137 | }; | 137 | }; |
| 138 | 138 | ||
diff --git a/src/core/hle/service/am/applet.h b/src/core/hle/service/am/applet.h index bce6f9050..b29ecdfed 100644 --- a/src/core/hle/service/am/applet.h +++ b/src/core/hle/service/am/applet.h | |||
| @@ -9,8 +9,8 @@ | |||
| 9 | #include "common/math_util.h" | 9 | #include "common/math_util.h" |
| 10 | #include "core/hle/service/apm/apm_controller.h" | 10 | #include "core/hle/service/apm/apm_controller.h" |
| 11 | #include "core/hle/service/caps/caps_types.h" | 11 | #include "core/hle/service/caps/caps_types.h" |
| 12 | #include "core/hle/service/event.h" | ||
| 13 | #include "core/hle/service/kernel_helpers.h" | 12 | #include "core/hle/service/kernel_helpers.h" |
| 13 | #include "core/hle/service/os/event.h" | ||
| 14 | #include "core/hle/service/service.h" | 14 | #include "core/hle/service/service.h" |
| 15 | 15 | ||
| 16 | #include "core/hle/service/am/am_types.h" | 16 | #include "core/hle/service/am/am_types.h" |
diff --git a/src/core/hle/service/am/applet_data_broker.h b/src/core/hle/service/am/applet_data_broker.h index 12326fd04..5a1d43c11 100644 --- a/src/core/hle/service/am/applet_data_broker.h +++ b/src/core/hle/service/am/applet_data_broker.h | |||
| @@ -7,8 +7,8 @@ | |||
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | #include <mutex> | 8 | #include <mutex> |
| 9 | 9 | ||
| 10 | #include "core/hle/service/event.h" | ||
| 11 | #include "core/hle/service/kernel_helpers.h" | 10 | #include "core/hle/service/kernel_helpers.h" |
| 11 | #include "core/hle/service/os/event.h" | ||
| 12 | 12 | ||
| 13 | union Result; | 13 | union Result; |
| 14 | 14 | ||
diff --git a/src/core/hle/service/am/frontend/applet_software_keyboard.cpp b/src/core/hle/service/am/frontend/applet_software_keyboard.cpp index fbf75d379..034c62f32 100644 --- a/src/core/hle/service/am/frontend/applet_software_keyboard.cpp +++ b/src/core/hle/service/am/frontend/applet_software_keyboard.cpp | |||
| @@ -68,9 +68,9 @@ void SoftwareKeyboard::Initialize() { | |||
| 68 | case LibraryAppletMode::AllForeground: | 68 | case LibraryAppletMode::AllForeground: |
| 69 | InitializeForeground(); | 69 | InitializeForeground(); |
| 70 | break; | 70 | break; |
| 71 | case LibraryAppletMode::Background: | 71 | case LibraryAppletMode::PartialForeground: |
| 72 | case LibraryAppletMode::BackgroundIndirectDisplay: | 72 | case LibraryAppletMode::PartialForegroundIndirectDisplay: |
| 73 | InitializeBackground(applet_mode); | 73 | InitializePartialForeground(applet_mode); |
| 74 | break; | 74 | break; |
| 75 | default: | 75 | default: |
| 76 | ASSERT_MSG(false, "Invalid LibraryAppletMode={}", applet_mode); | 76 | ASSERT_MSG(false, "Invalid LibraryAppletMode={}", applet_mode); |
| @@ -243,7 +243,7 @@ void SoftwareKeyboard::InitializeForeground() { | |||
| 243 | InitializeFrontendNormalKeyboard(); | 243 | InitializeFrontendNormalKeyboard(); |
| 244 | } | 244 | } |
| 245 | 245 | ||
| 246 | void SoftwareKeyboard::InitializeBackground(LibraryAppletMode library_applet_mode) { | 246 | void SoftwareKeyboard::InitializePartialForeground(LibraryAppletMode library_applet_mode) { |
| 247 | LOG_INFO(Service_AM, "Initializing Inline Software Keyboard Applet."); | 247 | LOG_INFO(Service_AM, "Initializing Inline Software Keyboard Applet."); |
| 248 | 248 | ||
| 249 | is_background = true; | 249 | is_background = true; |
| @@ -258,9 +258,9 @@ void SoftwareKeyboard::InitializeBackground(LibraryAppletMode library_applet_mod | |||
| 258 | swkbd_inline_initialize_arg.size()); | 258 | swkbd_inline_initialize_arg.size()); |
| 259 | 259 | ||
| 260 | if (swkbd_initialize_arg.library_applet_mode_flag) { | 260 | if (swkbd_initialize_arg.library_applet_mode_flag) { |
| 261 | ASSERT(library_applet_mode == LibraryAppletMode::Background); | 261 | ASSERT(library_applet_mode == LibraryAppletMode::PartialForeground); |
| 262 | } else { | 262 | } else { |
| 263 | ASSERT(library_applet_mode == LibraryAppletMode::BackgroundIndirectDisplay); | 263 | ASSERT(library_applet_mode == LibraryAppletMode::PartialForegroundIndirectDisplay); |
| 264 | } | 264 | } |
| 265 | } | 265 | } |
| 266 | 266 | ||
diff --git a/src/core/hle/service/am/frontend/applet_software_keyboard.h b/src/core/hle/service/am/frontend/applet_software_keyboard.h index f464b7e15..2a7d01b96 100644 --- a/src/core/hle/service/am/frontend/applet_software_keyboard.h +++ b/src/core/hle/service/am/frontend/applet_software_keyboard.h | |||
| @@ -62,7 +62,7 @@ private: | |||
| 62 | void InitializeForeground(); | 62 | void InitializeForeground(); |
| 63 | 63 | ||
| 64 | /// Initializes the inline software keyboard. | 64 | /// Initializes the inline software keyboard. |
| 65 | void InitializeBackground(LibraryAppletMode library_applet_mode); | 65 | void InitializePartialForeground(LibraryAppletMode library_applet_mode); |
| 66 | 66 | ||
| 67 | /// Processes the text check sent by the application. | 67 | /// Processes the text check sent by the application. |
| 68 | void ProcessTextCheck(); | 68 | void ProcessTextCheck(); |
diff --git a/src/core/hle/service/am/library_applet_creator.cpp b/src/core/hle/service/am/library_applet_creator.cpp index 47bab7528..00d5a0705 100644 --- a/src/core/hle/service/am/library_applet_creator.cpp +++ b/src/core/hle/service/am/library_applet_creator.cpp | |||
| @@ -1,6 +1,7 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include "common/settings.h" | ||
| 4 | #include "core/hle/kernel/k_transfer_memory.h" | 5 | #include "core/hle/kernel/k_transfer_memory.h" |
| 5 | #include "core/hle/service/am/applet_data_broker.h" | 6 | #include "core/hle/service/am/applet_data_broker.h" |
| 6 | #include "core/hle/service/am/applet_manager.h" | 7 | #include "core/hle/service/am/applet_manager.h" |
| @@ -16,6 +17,34 @@ namespace Service::AM { | |||
| 16 | 17 | ||
| 17 | namespace { | 18 | namespace { |
| 18 | 19 | ||
| 20 | bool ShouldCreateGuestApplet(AppletId applet_id) { | ||
| 21 | #define X(Name, name) \ | ||
| 22 | if (applet_id == AppletId::Name && \ | ||
| 23 | Settings::values.name##_applet_mode.GetValue() != Settings::AppletMode::LLE) { \ | ||
| 24 | return false; \ | ||
| 25 | } | ||
| 26 | |||
| 27 | X(Cabinet, cabinet) | ||
| 28 | X(Controller, controller) | ||
| 29 | X(DataErase, data_erase) | ||
| 30 | X(Error, error) | ||
| 31 | X(NetConnect, net_connect) | ||
| 32 | X(ProfileSelect, player_select) | ||
| 33 | X(SoftwareKeyboard, swkbd) | ||
| 34 | X(MiiEdit, mii_edit) | ||
| 35 | X(Web, web) | ||
| 36 | X(Shop, shop) | ||
| 37 | X(PhotoViewer, photo_viewer) | ||
| 38 | X(OfflineWeb, offline_web) | ||
| 39 | X(LoginShare, login_share) | ||
| 40 | X(WebAuth, wifi_web_auth) | ||
| 41 | X(MyPage, my_page) | ||
| 42 | |||
| 43 | #undef X | ||
| 44 | |||
| 45 | return true; | ||
| 46 | } | ||
| 47 | |||
| 19 | AppletProgramId AppletIdToProgramId(AppletId applet_id) { | 48 | AppletProgramId AppletIdToProgramId(AppletId applet_id) { |
| 20 | switch (applet_id) { | 49 | switch (applet_id) { |
| 21 | case AppletId::OverlayDisplay: | 50 | case AppletId::OverlayDisplay: |
| @@ -63,17 +92,26 @@ AppletProgramId AppletIdToProgramId(AppletId applet_id) { | |||
| 63 | } | 92 | } |
| 64 | } | 93 | } |
| 65 | 94 | ||
| 66 | [[maybe_unused]] std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet( | 95 | std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet(Core::System& system, |
| 67 | Core::System& system, std::shared_ptr<Applet> caller_applet, AppletId applet_id, | 96 | std::shared_ptr<Applet> caller_applet, |
| 68 | LibraryAppletMode mode) { | 97 | AppletId applet_id, |
| 98 | LibraryAppletMode mode) { | ||
| 69 | const auto program_id = static_cast<u64>(AppletIdToProgramId(applet_id)); | 99 | const auto program_id = static_cast<u64>(AppletIdToProgramId(applet_id)); |
| 70 | if (program_id == 0) { | 100 | if (program_id == 0) { |
| 71 | // Unknown applet | 101 | // Unknown applet |
| 72 | return {}; | 102 | return {}; |
| 73 | } | 103 | } |
| 74 | 104 | ||
| 105 | // TODO: enable other versions of applets | ||
| 106 | enum : u8 { | ||
| 107 | Firmware1400 = 14, | ||
| 108 | Firmware1500 = 15, | ||
| 109 | Firmware1600 = 16, | ||
| 110 | Firmware1700 = 17, | ||
| 111 | }; | ||
| 112 | |||
| 75 | auto process = std::make_unique<Process>(system); | 113 | auto process = std::make_unique<Process>(system); |
| 76 | if (!process->Initialize(program_id)) { | 114 | if (!process->Initialize(program_id, Firmware1400, Firmware1700)) { |
| 77 | // Couldn't initialize the guest process | 115 | // Couldn't initialize the guest process |
| 78 | return {}; | 116 | return {}; |
| 79 | } | 117 | } |
| @@ -87,24 +125,18 @@ AppletProgramId AppletIdToProgramId(AppletId applet_id) { | |||
| 87 | // Set focus state | 125 | // Set focus state |
| 88 | switch (mode) { | 126 | switch (mode) { |
| 89 | case LibraryAppletMode::AllForeground: | 127 | case LibraryAppletMode::AllForeground: |
| 90 | case LibraryAppletMode::NoUI: | 128 | case LibraryAppletMode::NoUi: |
| 91 | applet->focus_state = FocusState::InFocus; | 129 | case LibraryAppletMode::PartialForeground: |
| 130 | case LibraryAppletMode::PartialForegroundIndirectDisplay: | ||
| 92 | applet->hid_registration.EnableAppletToGetInput(true); | 131 | applet->hid_registration.EnableAppletToGetInput(true); |
| 132 | applet->focus_state = FocusState::InFocus; | ||
| 93 | applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground); | 133 | applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground); |
| 94 | applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); | ||
| 95 | break; | 134 | break; |
| 96 | case LibraryAppletMode::AllForegroundInitiallyHidden: | 135 | case LibraryAppletMode::AllForegroundInitiallyHidden: |
| 97 | applet->system_buffer_manager.SetWindowVisibility(false); | ||
| 98 | applet->focus_state = FocusState::NotInFocus; | ||
| 99 | applet->hid_registration.EnableAppletToGetInput(false); | 136 | applet->hid_registration.EnableAppletToGetInput(false); |
| 100 | applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); | 137 | applet->focus_state = FocusState::NotInFocus; |
| 101 | break; | 138 | applet->system_buffer_manager.SetWindowVisibility(false); |
| 102 | case LibraryAppletMode::Background: | 139 | applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoBackground); |
| 103 | case LibraryAppletMode::BackgroundIndirectDisplay: | ||
| 104 | default: | ||
| 105 | applet->focus_state = FocusState::Background; | ||
| 106 | applet->hid_registration.EnableAppletToGetInput(true); | ||
| 107 | applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); | ||
| 108 | break; | 140 | break; |
| 109 | } | 141 | } |
| 110 | 142 | ||
| @@ -117,9 +149,10 @@ AppletProgramId AppletIdToProgramId(AppletId applet_id) { | |||
| 117 | return std::make_shared<ILibraryAppletAccessor>(system, broker, applet); | 149 | return std::make_shared<ILibraryAppletAccessor>(system, broker, applet); |
| 118 | } | 150 | } |
| 119 | 151 | ||
| 120 | [[maybe_unused]] std::shared_ptr<ILibraryAppletAccessor> CreateFrontendApplet( | 152 | std::shared_ptr<ILibraryAppletAccessor> CreateFrontendApplet(Core::System& system, |
| 121 | Core::System& system, std::shared_ptr<Applet> caller_applet, AppletId applet_id, | 153 | std::shared_ptr<Applet> caller_applet, |
| 122 | LibraryAppletMode mode) { | 154 | AppletId applet_id, |
| 155 | LibraryAppletMode mode) { | ||
| 123 | const auto program_id = static_cast<u64>(AppletIdToProgramId(applet_id)); | 156 | const auto program_id = static_cast<u64>(AppletIdToProgramId(applet_id)); |
| 124 | 157 | ||
| 125 | auto process = std::make_unique<Process>(system); | 158 | auto process = std::make_unique<Process>(system); |
| @@ -163,7 +196,13 @@ void ILibraryAppletCreator::CreateLibraryApplet(HLERequestContext& ctx) { | |||
| 163 | LOG_DEBUG(Service_AM, "called with applet_id={:08X}, applet_mode={:08X}", applet_id, | 196 | LOG_DEBUG(Service_AM, "called with applet_id={:08X}, applet_mode={:08X}", applet_id, |
| 164 | applet_mode); | 197 | applet_mode); |
| 165 | 198 | ||
| 166 | auto library_applet = CreateFrontendApplet(system, applet, applet_id, applet_mode); | 199 | std::shared_ptr<ILibraryAppletAccessor> library_applet; |
| 200 | if (ShouldCreateGuestApplet(applet_id)) { | ||
| 201 | library_applet = CreateGuestApplet(system, applet, applet_id, applet_mode); | ||
| 202 | } | ||
| 203 | if (!library_applet) { | ||
| 204 | library_applet = CreateFrontendApplet(system, applet, applet_id, applet_mode); | ||
| 205 | } | ||
| 167 | if (!library_applet) { | 206 | if (!library_applet) { |
| 168 | LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", applet_id); | 207 | LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", applet_id); |
| 169 | 208 | ||
diff --git a/src/core/hle/service/am/process.cpp b/src/core/hle/service/am/process.cpp index 16b685f86..992c50713 100644 --- a/src/core/hle/service/am/process.cpp +++ b/src/core/hle/service/am/process.cpp | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | #include "common/scope_exit.h" | 4 | #include "common/scope_exit.h" |
| 5 | 5 | ||
| 6 | #include "core/file_sys/content_archive.h" | ||
| 6 | #include "core/file_sys/nca_metadata.h" | 7 | #include "core/file_sys/nca_metadata.h" |
| 7 | #include "core/file_sys/registered_cache.h" | 8 | #include "core/file_sys/registered_cache.h" |
| 8 | #include "core/hle/kernel/k_process.h" | 9 | #include "core/hle/kernel/k_process.h" |
| @@ -20,7 +21,7 @@ Process::~Process() { | |||
| 20 | this->Finalize(); | 21 | this->Finalize(); |
| 21 | } | 22 | } |
| 22 | 23 | ||
| 23 | bool Process::Initialize(u64 program_id) { | 24 | bool Process::Initialize(u64 program_id, u8 minimum_key_generation, u8 maximum_key_generation) { |
| 24 | // First, ensure we are not holding another process. | 25 | // First, ensure we are not holding another process. |
| 25 | this->Finalize(); | 26 | this->Finalize(); |
| 26 | 27 | ||
| @@ -29,21 +30,33 @@ bool Process::Initialize(u64 program_id) { | |||
| 29 | 30 | ||
| 30 | // Attempt to load program NCA. | 31 | // Attempt to load program NCA. |
| 31 | const FileSys::RegisteredCache* bis_system{}; | 32 | const FileSys::RegisteredCache* bis_system{}; |
| 32 | FileSys::VirtualFile nca{}; | 33 | FileSys::VirtualFile nca_raw{}; |
| 33 | 34 | ||
| 34 | // Get the program NCA from built-in storage. | 35 | // Get the program NCA from built-in storage. |
| 35 | bis_system = fsc.GetSystemNANDContents(); | 36 | bis_system = fsc.GetSystemNANDContents(); |
| 36 | if (bis_system) { | 37 | if (bis_system) { |
| 37 | nca = bis_system->GetEntryRaw(program_id, FileSys::ContentRecordType::Program); | 38 | nca_raw = bis_system->GetEntryRaw(program_id, FileSys::ContentRecordType::Program); |
| 38 | } | 39 | } |
| 39 | 40 | ||
| 40 | // Ensure we retrieved a program NCA. | 41 | // Ensure we retrieved a program NCA. |
| 41 | if (!nca) { | 42 | if (!nca_raw) { |
| 42 | return false; | 43 | return false; |
| 43 | } | 44 | } |
| 44 | 45 | ||
| 46 | // Ensure we have a suitable version. | ||
| 47 | if (minimum_key_generation > 0) { | ||
| 48 | FileSys::NCA nca(nca_raw); | ||
| 49 | if (nca.GetStatus() == Loader::ResultStatus::Success && | ||
| 50 | (nca.GetKeyGeneration() < minimum_key_generation || | ||
| 51 | nca.GetKeyGeneration() > maximum_key_generation)) { | ||
| 52 | LOG_WARNING(Service_LDR, "Skipping program {:016X} with generation {}", program_id, | ||
| 53 | nca.GetKeyGeneration()); | ||
| 54 | return false; | ||
| 55 | } | ||
| 56 | } | ||
| 57 | |||
| 45 | // Get the appropriate loader to parse this NCA. | 58 | // Get the appropriate loader to parse this NCA. |
| 46 | auto app_loader = Loader::GetLoader(m_system, nca, program_id, 0); | 59 | auto app_loader = Loader::GetLoader(m_system, nca_raw, program_id, 0); |
| 47 | 60 | ||
| 48 | // Ensure we have a loader which can parse the NCA. | 61 | // Ensure we have a loader which can parse the NCA. |
| 49 | if (!app_loader) { | 62 | if (!app_loader) { |
diff --git a/src/core/hle/service/am/process.h b/src/core/hle/service/am/process.h index 4b908ade4..4b8102fb6 100644 --- a/src/core/hle/service/am/process.h +++ b/src/core/hle/service/am/process.h | |||
| @@ -21,7 +21,7 @@ public: | |||
| 21 | explicit Process(Core::System& system); | 21 | explicit Process(Core::System& system); |
| 22 | ~Process(); | 22 | ~Process(); |
| 23 | 23 | ||
| 24 | bool Initialize(u64 program_id); | 24 | bool Initialize(u64 program_id, u8 minimum_key_generation, u8 maximum_key_generation); |
| 25 | void Finalize(); | 25 | void Finalize(); |
| 26 | 26 | ||
| 27 | bool Run(); | 27 | bool Run(); |
diff --git a/src/core/hle/service/am/self_controller.cpp b/src/core/hle/service/am/self_controller.cpp index 0289f5cf1..65e249c0c 100644 --- a/src/core/hle/service/am/self_controller.cpp +++ b/src/core/hle/service/am/self_controller.cpp | |||
| @@ -1,10 +1,13 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include "common/logging/log.h" | ||
| 5 | #include "core/hle/result.h" | ||
| 4 | #include "core/hle/service/am/am_results.h" | 6 | #include "core/hle/service/am/am_results.h" |
| 5 | #include "core/hle/service/am/frontend/applets.h" | 7 | #include "core/hle/service/am/frontend/applets.h" |
| 6 | #include "core/hle/service/am/self_controller.h" | 8 | #include "core/hle/service/am/self_controller.h" |
| 7 | #include "core/hle/service/caps/caps_su.h" | 9 | #include "core/hle/service/caps/caps_su.h" |
| 10 | #include "core/hle/service/hle_ipc.h" | ||
| 8 | #include "core/hle/service/ipc_helpers.h" | 11 | #include "core/hle/service/ipc_helpers.h" |
| 9 | #include "core/hle/service/nvnflinger/fb_share_buffer_manager.h" | 12 | #include "core/hle/service/nvnflinger/fb_share_buffer_manager.h" |
| 10 | #include "core/hle/service/nvnflinger/nvnflinger.h" | 13 | #include "core/hle/service/nvnflinger/nvnflinger.h" |
| @@ -47,7 +50,7 @@ ISelfController::ISelfController(Core::System& system_, std::shared_ptr<Applet> | |||
| 47 | {50, &ISelfController::SetHandlesRequestToDisplay, "SetHandlesRequestToDisplay"}, | 50 | {50, &ISelfController::SetHandlesRequestToDisplay, "SetHandlesRequestToDisplay"}, |
| 48 | {51, &ISelfController::ApproveToDisplay, "ApproveToDisplay"}, | 51 | {51, &ISelfController::ApproveToDisplay, "ApproveToDisplay"}, |
| 49 | {60, nullptr, "OverrideAutoSleepTimeAndDimmingTime"}, | 52 | {60, nullptr, "OverrideAutoSleepTimeAndDimmingTime"}, |
| 50 | {61, nullptr, "SetMediaPlaybackState"}, | 53 | {61, &ISelfController::SetMediaPlaybackState, "SetMediaPlaybackState"}, |
| 51 | {62, &ISelfController::SetIdleTimeDetectionExtension, "SetIdleTimeDetectionExtension"}, | 54 | {62, &ISelfController::SetIdleTimeDetectionExtension, "SetIdleTimeDetectionExtension"}, |
| 52 | {63, &ISelfController::GetIdleTimeDetectionExtension, "GetIdleTimeDetectionExtension"}, | 55 | {63, &ISelfController::GetIdleTimeDetectionExtension, "GetIdleTimeDetectionExtension"}, |
| 53 | {64, nullptr, "SetInputDetectionSourceSet"}, | 56 | {64, nullptr, "SetInputDetectionSourceSet"}, |
| @@ -288,7 +291,8 @@ void ISelfController::GetSystemSharedBufferHandle(HLERequestContext& ctx) { | |||
| 288 | } | 291 | } |
| 289 | 292 | ||
| 290 | Result ISelfController::EnsureBufferSharingEnabled(Kernel::KProcess* process) { | 293 | Result ISelfController::EnsureBufferSharingEnabled(Kernel::KProcess* process) { |
| 291 | if (applet->system_buffer_manager.Initialize(&nvnflinger, process, applet->applet_id)) { | 294 | if (applet->system_buffer_manager.Initialize(&nvnflinger, process, applet->applet_id, |
| 295 | applet->library_applet_mode)) { | ||
| 292 | return ResultSuccess; | 296 | return ResultSuccess; |
| 293 | } | 297 | } |
| 294 | 298 | ||
| @@ -323,6 +327,16 @@ void ISelfController::ApproveToDisplay(HLERequestContext& ctx) { | |||
| 323 | rb.Push(ResultSuccess); | 327 | rb.Push(ResultSuccess); |
| 324 | } | 328 | } |
| 325 | 329 | ||
| 330 | void ISelfController::SetMediaPlaybackState(HLERequestContext& ctx) { | ||
| 331 | IPC::RequestParser rp{ctx}; | ||
| 332 | const u8 state = rp.Pop<u8>(); | ||
| 333 | |||
| 334 | LOG_WARNING(Service_AM, "(STUBBED) called, state={}", state); | ||
| 335 | |||
| 336 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 337 | rb.Push(ResultSuccess); | ||
| 338 | } | ||
| 339 | |||
| 326 | void ISelfController::SetIdleTimeDetectionExtension(HLERequestContext& ctx) { | 340 | void ISelfController::SetIdleTimeDetectionExtension(HLERequestContext& ctx) { |
| 327 | IPC::RequestParser rp{ctx}; | 341 | IPC::RequestParser rp{ctx}; |
| 328 | 342 | ||
diff --git a/src/core/hle/service/am/self_controller.h b/src/core/hle/service/am/self_controller.h index a63bc2e74..ab21a1881 100644 --- a/src/core/hle/service/am/self_controller.h +++ b/src/core/hle/service/am/self_controller.h | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include "core/hle/service/hle_ipc.h" | ||
| 6 | #include "core/hle/service/kernel_helpers.h" | 7 | #include "core/hle/service/kernel_helpers.h" |
| 7 | #include "core/hle/service/service.h" | 8 | #include "core/hle/service/service.h" |
| 8 | 9 | ||
| @@ -38,6 +39,7 @@ private: | |||
| 38 | void CreateManagedDisplaySeparableLayer(HLERequestContext& ctx); | 39 | void CreateManagedDisplaySeparableLayer(HLERequestContext& ctx); |
| 39 | void SetHandlesRequestToDisplay(HLERequestContext& ctx); | 40 | void SetHandlesRequestToDisplay(HLERequestContext& ctx); |
| 40 | void ApproveToDisplay(HLERequestContext& ctx); | 41 | void ApproveToDisplay(HLERequestContext& ctx); |
| 42 | void SetMediaPlaybackState(HLERequestContext& ctx); | ||
| 41 | void SetIdleTimeDetectionExtension(HLERequestContext& ctx); | 43 | void SetIdleTimeDetectionExtension(HLERequestContext& ctx); |
| 42 | void GetIdleTimeDetectionExtension(HLERequestContext& ctx); | 44 | void GetIdleTimeDetectionExtension(HLERequestContext& ctx); |
| 43 | void ReportUserIsActive(HLERequestContext& ctx); | 45 | void ReportUserIsActive(HLERequestContext& ctx); |
diff --git a/src/core/hle/service/am/system_buffer_manager.cpp b/src/core/hle/service/am/system_buffer_manager.cpp index 60a9afc9d..48923fe41 100644 --- a/src/core/hle/service/am/system_buffer_manager.cpp +++ b/src/core/hle/service/am/system_buffer_manager.cpp | |||
| @@ -17,11 +17,12 @@ SystemBufferManager::~SystemBufferManager() { | |||
| 17 | 17 | ||
| 18 | // Clean up shared layers. | 18 | // Clean up shared layers. |
| 19 | if (m_buffer_sharing_enabled) { | 19 | if (m_buffer_sharing_enabled) { |
| 20 | m_nvnflinger->GetSystemBufferManager().Finalize(m_process); | ||
| 20 | } | 21 | } |
| 21 | } | 22 | } |
| 22 | 23 | ||
| 23 | bool SystemBufferManager::Initialize(Nvnflinger::Nvnflinger* nvnflinger, Kernel::KProcess* process, | 24 | bool SystemBufferManager::Initialize(Nvnflinger::Nvnflinger* nvnflinger, Kernel::KProcess* process, |
| 24 | AppletId applet_id) { | 25 | AppletId applet_id, LibraryAppletMode mode) { |
| 25 | if (m_nvnflinger) { | 26 | if (m_nvnflinger) { |
| 26 | return m_buffer_sharing_enabled; | 27 | return m_buffer_sharing_enabled; |
| 27 | } | 28 | } |
| @@ -36,9 +37,15 @@ bool SystemBufferManager::Initialize(Nvnflinger::Nvnflinger* nvnflinger, Kernel: | |||
| 36 | return false; | 37 | return false; |
| 37 | } | 38 | } |
| 38 | 39 | ||
| 40 | Nvnflinger::LayerBlending blending = Nvnflinger::LayerBlending::None; | ||
| 41 | if (mode == LibraryAppletMode::PartialForeground || | ||
| 42 | mode == LibraryAppletMode::PartialForegroundIndirectDisplay) { | ||
| 43 | blending = Nvnflinger::LayerBlending::Coverage; | ||
| 44 | } | ||
| 45 | |||
| 39 | const auto display_id = m_nvnflinger->OpenDisplay("Default").value(); | 46 | const auto display_id = m_nvnflinger->OpenDisplay("Default").value(); |
| 40 | const auto res = m_nvnflinger->GetSystemBufferManager().Initialize( | 47 | const auto res = m_nvnflinger->GetSystemBufferManager().Initialize( |
| 41 | &m_system_shared_buffer_id, &m_system_shared_layer_id, display_id); | 48 | m_process, &m_system_shared_buffer_id, &m_system_shared_layer_id, display_id, blending); |
| 42 | 49 | ||
| 43 | if (res.IsSuccess()) { | 50 | if (res.IsSuccess()) { |
| 44 | m_buffer_sharing_enabled = true; | 51 | m_buffer_sharing_enabled = true; |
| @@ -62,8 +69,12 @@ void SystemBufferManager::SetWindowVisibility(bool visible) { | |||
| 62 | 69 | ||
| 63 | Result SystemBufferManager::WriteAppletCaptureBuffer(bool* out_was_written, | 70 | Result SystemBufferManager::WriteAppletCaptureBuffer(bool* out_was_written, |
| 64 | s32* out_fbshare_layer_index) { | 71 | s32* out_fbshare_layer_index) { |
| 65 | // TODO | 72 | if (!m_buffer_sharing_enabled) { |
| 66 | R_SUCCEED(); | 73 | return VI::ResultPermissionDenied; |
| 74 | } | ||
| 75 | |||
| 76 | return m_nvnflinger->GetSystemBufferManager().WriteAppletCaptureBuffer(out_was_written, | ||
| 77 | out_fbshare_layer_index); | ||
| 67 | } | 78 | } |
| 68 | 79 | ||
| 69 | } // namespace Service::AM | 80 | } // namespace Service::AM |
diff --git a/src/core/hle/service/am/system_buffer_manager.h b/src/core/hle/service/am/system_buffer_manager.h index 98c3cf055..0690f68b6 100644 --- a/src/core/hle/service/am/system_buffer_manager.h +++ b/src/core/hle/service/am/system_buffer_manager.h | |||
| @@ -27,7 +27,8 @@ public: | |||
| 27 | SystemBufferManager(); | 27 | SystemBufferManager(); |
| 28 | ~SystemBufferManager(); | 28 | ~SystemBufferManager(); |
| 29 | 29 | ||
| 30 | bool Initialize(Nvnflinger::Nvnflinger* flinger, Kernel::KProcess* process, AppletId applet_id); | 30 | bool Initialize(Nvnflinger::Nvnflinger* flinger, Kernel::KProcess* process, AppletId applet_id, |
| 31 | LibraryAppletMode mode); | ||
| 31 | 32 | ||
| 32 | void GetSystemSharedLayerHandle(u64* out_system_shared_buffer_id, | 33 | void GetSystemSharedLayerHandle(u64* out_system_shared_buffer_id, |
| 33 | u64* out_system_shared_layer_id) { | 34 | u64* out_system_shared_layer_id) { |
diff --git a/src/core/hle/service/am/window_controller.cpp b/src/core/hle/service/am/window_controller.cpp index f00957f83..c07ef228b 100644 --- a/src/core/hle/service/am/window_controller.cpp +++ b/src/core/hle/service/am/window_controller.cpp | |||
| @@ -62,12 +62,12 @@ void IWindowController::SetAppletWindowVisibility(HLERequestContext& ctx) { | |||
| 62 | applet->hid_registration.EnableAppletToGetInput(visible); | 62 | applet->hid_registration.EnableAppletToGetInput(visible); |
| 63 | 63 | ||
| 64 | if (visible) { | 64 | if (visible) { |
| 65 | applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground); | ||
| 66 | applet->focus_state = FocusState::InFocus; | 65 | applet->focus_state = FocusState::InFocus; |
| 66 | applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground); | ||
| 67 | } else { | 67 | } else { |
| 68 | applet->focus_state = FocusState::NotInFocus; | 68 | applet->focus_state = FocusState::NotInFocus; |
| 69 | applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoBackground); | ||
| 69 | } | 70 | } |
| 70 | applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); | ||
| 71 | 71 | ||
| 72 | IPC::ResponseBuilder rb{ctx, 2}; | 72 | IPC::ResponseBuilder rb{ctx, 2}; |
| 73 | rb.Push(ResultSuccess); | 73 | rb.Push(ResultSuccess); |
diff --git a/src/core/hle/service/bcat/backend/backend.cpp b/src/core/hle/service/bcat/backend/backend.cpp index 847f76987..1993493a9 100644 --- a/src/core/hle/service/bcat/backend/backend.cpp +++ b/src/core/hle/service/bcat/backend/backend.cpp | |||
| @@ -33,18 +33,18 @@ void ProgressServiceBackend::SetTotalSize(u64 size) { | |||
| 33 | } | 33 | } |
| 34 | 34 | ||
| 35 | void ProgressServiceBackend::StartConnecting() { | 35 | void ProgressServiceBackend::StartConnecting() { |
| 36 | impl.status = DeliveryCacheProgressImpl::Status::Connecting; | 36 | impl.status = DeliveryCacheProgressStatus::Connecting; |
| 37 | SignalUpdate(); | 37 | SignalUpdate(); |
| 38 | } | 38 | } |
| 39 | 39 | ||
| 40 | void ProgressServiceBackend::StartProcessingDataList() { | 40 | void ProgressServiceBackend::StartProcessingDataList() { |
| 41 | impl.status = DeliveryCacheProgressImpl::Status::ProcessingDataList; | 41 | impl.status = DeliveryCacheProgressStatus::ProcessingDataList; |
| 42 | SignalUpdate(); | 42 | SignalUpdate(); |
| 43 | } | 43 | } |
| 44 | 44 | ||
| 45 | void ProgressServiceBackend::StartDownloadingFile(std::string_view dir_name, | 45 | void ProgressServiceBackend::StartDownloadingFile(std::string_view dir_name, |
| 46 | std::string_view file_name, u64 file_size) { | 46 | std::string_view file_name, u64 file_size) { |
| 47 | impl.status = DeliveryCacheProgressImpl::Status::Downloading; | 47 | impl.status = DeliveryCacheProgressStatus::Downloading; |
| 48 | impl.current_downloaded_bytes = 0; | 48 | impl.current_downloaded_bytes = 0; |
| 49 | impl.current_total_bytes = file_size; | 49 | impl.current_total_bytes = file_size; |
| 50 | std::memcpy(impl.current_directory.data(), dir_name.data(), | 50 | std::memcpy(impl.current_directory.data(), dir_name.data(), |
| @@ -65,7 +65,7 @@ void ProgressServiceBackend::FinishDownloadingFile() { | |||
| 65 | } | 65 | } |
| 66 | 66 | ||
| 67 | void ProgressServiceBackend::CommitDirectory(std::string_view dir_name) { | 67 | void ProgressServiceBackend::CommitDirectory(std::string_view dir_name) { |
| 68 | impl.status = DeliveryCacheProgressImpl::Status::Committing; | 68 | impl.status = DeliveryCacheProgressStatus::Committing; |
| 69 | impl.current_file.fill(0); | 69 | impl.current_file.fill(0); |
| 70 | impl.current_downloaded_bytes = 0; | 70 | impl.current_downloaded_bytes = 0; |
| 71 | impl.current_total_bytes = 0; | 71 | impl.current_total_bytes = 0; |
| @@ -76,7 +76,7 @@ void ProgressServiceBackend::CommitDirectory(std::string_view dir_name) { | |||
| 76 | 76 | ||
| 77 | void ProgressServiceBackend::FinishDownload(Result result) { | 77 | void ProgressServiceBackend::FinishDownload(Result result) { |
| 78 | impl.total_downloaded_bytes = impl.total_bytes; | 78 | impl.total_downloaded_bytes = impl.total_bytes; |
| 79 | impl.status = DeliveryCacheProgressImpl::Status::Done; | 79 | impl.status = DeliveryCacheProgressStatus::Done; |
| 80 | impl.result = result; | 80 | impl.result = result; |
| 81 | SignalUpdate(); | 81 | SignalUpdate(); |
| 82 | } | 82 | } |
| @@ -85,15 +85,15 @@ void ProgressServiceBackend::SignalUpdate() { | |||
| 85 | update_event->Signal(); | 85 | update_event->Signal(); |
| 86 | } | 86 | } |
| 87 | 87 | ||
| 88 | Backend::Backend(DirectoryGetter getter) : dir_getter(std::move(getter)) {} | 88 | BcatBackend::BcatBackend(DirectoryGetter getter) : dir_getter(std::move(getter)) {} |
| 89 | 89 | ||
| 90 | Backend::~Backend() = default; | 90 | BcatBackend::~BcatBackend() = default; |
| 91 | 91 | ||
| 92 | NullBackend::NullBackend(DirectoryGetter getter) : Backend(std::move(getter)) {} | 92 | NullBcatBackend::NullBcatBackend(DirectoryGetter getter) : BcatBackend(std::move(getter)) {} |
| 93 | 93 | ||
| 94 | NullBackend::~NullBackend() = default; | 94 | NullBcatBackend::~NullBcatBackend() = default; |
| 95 | 95 | ||
| 96 | bool NullBackend::Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) { | 96 | bool NullBcatBackend::Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) { |
| 97 | LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}", title.title_id, | 97 | LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}", title.title_id, |
| 98 | title.build_id); | 98 | title.build_id); |
| 99 | 99 | ||
| @@ -101,8 +101,8 @@ bool NullBackend::Synchronize(TitleIDVersion title, ProgressServiceBackend& prog | |||
| 101 | return true; | 101 | return true; |
| 102 | } | 102 | } |
| 103 | 103 | ||
| 104 | bool NullBackend::SynchronizeDirectory(TitleIDVersion title, std::string name, | 104 | bool NullBcatBackend::SynchronizeDirectory(TitleIDVersion title, std::string name, |
| 105 | ProgressServiceBackend& progress) { | 105 | ProgressServiceBackend& progress) { |
| 106 | LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}, name={}", title.title_id, | 106 | LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}, name={}", title.title_id, |
| 107 | title.build_id, name); | 107 | title.build_id, name); |
| 108 | 108 | ||
| @@ -110,18 +110,18 @@ bool NullBackend::SynchronizeDirectory(TitleIDVersion title, std::string name, | |||
| 110 | return true; | 110 | return true; |
| 111 | } | 111 | } |
| 112 | 112 | ||
| 113 | bool NullBackend::Clear(u64 title_id) { | 113 | bool NullBcatBackend::Clear(u64 title_id) { |
| 114 | LOG_DEBUG(Service_BCAT, "called, title_id={:016X}", title_id); | 114 | LOG_DEBUG(Service_BCAT, "called, title_id={:016X}", title_id); |
| 115 | 115 | ||
| 116 | return true; | 116 | return true; |
| 117 | } | 117 | } |
| 118 | 118 | ||
| 119 | void NullBackend::SetPassphrase(u64 title_id, const Passphrase& passphrase) { | 119 | void NullBcatBackend::SetPassphrase(u64 title_id, const Passphrase& passphrase) { |
| 120 | LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, passphrase={}", title_id, | 120 | LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, passphrase={}", title_id, |
| 121 | Common::HexToString(passphrase)); | 121 | Common::HexToString(passphrase)); |
| 122 | } | 122 | } |
| 123 | 123 | ||
| 124 | std::optional<std::vector<u8>> NullBackend::GetLaunchParameter(TitleIDVersion title) { | 124 | std::optional<std::vector<u8>> NullBcatBackend::GetLaunchParameter(TitleIDVersion title) { |
| 125 | LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}", title.title_id, | 125 | LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}", title.title_id, |
| 126 | title.build_id); | 126 | title.build_id); |
| 127 | return std::nullopt; | 127 | return std::nullopt; |
diff --git a/src/core/hle/service/bcat/backend/backend.h b/src/core/hle/service/bcat/backend/backend.h index aa36d29d5..3680f6c9c 100644 --- a/src/core/hle/service/bcat/backend/backend.h +++ b/src/core/hle/service/bcat/backend/backend.h | |||
| @@ -10,6 +10,7 @@ | |||
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "core/file_sys/vfs/vfs_types.h" | 11 | #include "core/file_sys/vfs/vfs_types.h" |
| 12 | #include "core/hle/result.h" | 12 | #include "core/hle/result.h" |
| 13 | #include "core/hle/service/bcat/bcat_types.h" | ||
| 13 | #include "core/hle/service/kernel_helpers.h" | 14 | #include "core/hle/service/kernel_helpers.h" |
| 14 | 15 | ||
| 15 | namespace Core { | 16 | namespace Core { |
| @@ -24,44 +25,6 @@ class KReadableEvent; | |||
| 24 | 25 | ||
| 25 | namespace Service::BCAT { | 26 | namespace Service::BCAT { |
| 26 | 27 | ||
| 27 | struct DeliveryCacheProgressImpl; | ||
| 28 | |||
| 29 | using DirectoryGetter = std::function<FileSys::VirtualDir(u64)>; | ||
| 30 | using Passphrase = std::array<u8, 0x20>; | ||
| 31 | |||
| 32 | struct TitleIDVersion { | ||
| 33 | u64 title_id; | ||
| 34 | u64 build_id; | ||
| 35 | }; | ||
| 36 | |||
| 37 | using DirectoryName = std::array<char, 0x20>; | ||
| 38 | using FileName = std::array<char, 0x20>; | ||
| 39 | |||
| 40 | struct DeliveryCacheProgressImpl { | ||
| 41 | enum class Status : s32 { | ||
| 42 | None = 0x0, | ||
| 43 | Queued = 0x1, | ||
| 44 | Connecting = 0x2, | ||
| 45 | ProcessingDataList = 0x3, | ||
| 46 | Downloading = 0x4, | ||
| 47 | Committing = 0x5, | ||
| 48 | Done = 0x9, | ||
| 49 | }; | ||
| 50 | |||
| 51 | Status status; | ||
| 52 | Result result = ResultSuccess; | ||
| 53 | DirectoryName current_directory; | ||
| 54 | FileName current_file; | ||
| 55 | s64 current_downloaded_bytes; ///< Bytes downloaded on current file. | ||
| 56 | s64 current_total_bytes; ///< Bytes total on current file. | ||
| 57 | s64 total_downloaded_bytes; ///< Bytes downloaded on overall download. | ||
| 58 | s64 total_bytes; ///< Bytes total on overall download. | ||
| 59 | INSERT_PADDING_BYTES( | ||
| 60 | 0x198); ///< Appears to be unused in official code, possibly reserved for future use. | ||
| 61 | }; | ||
| 62 | static_assert(sizeof(DeliveryCacheProgressImpl) == 0x200, | ||
| 63 | "DeliveryCacheProgressImpl has incorrect size."); | ||
| 64 | |||
| 65 | // A class to manage the signalling to the game about BCAT download progress. | 28 | // A class to manage the signalling to the game about BCAT download progress. |
| 66 | // Some of this class is implemented in module.cpp to avoid exposing the implementation structure. | 29 | // Some of this class is implemented in module.cpp to avoid exposing the implementation structure. |
| 67 | class ProgressServiceBackend { | 30 | class ProgressServiceBackend { |
| @@ -107,10 +70,10 @@ private: | |||
| 107 | }; | 70 | }; |
| 108 | 71 | ||
| 109 | // A class representing an abstract backend for BCAT functionality. | 72 | // A class representing an abstract backend for BCAT functionality. |
| 110 | class Backend { | 73 | class BcatBackend { |
| 111 | public: | 74 | public: |
| 112 | explicit Backend(DirectoryGetter getter); | 75 | explicit BcatBackend(DirectoryGetter getter); |
| 113 | virtual ~Backend(); | 76 | virtual ~BcatBackend(); |
| 114 | 77 | ||
| 115 | // Called when the backend is needed to synchronize the data for the game with title ID and | 78 | // Called when the backend is needed to synchronize the data for the game with title ID and |
| 116 | // version in title. A ProgressServiceBackend object is provided to alert the application of | 79 | // version in title. A ProgressServiceBackend object is provided to alert the application of |
| @@ -135,10 +98,10 @@ protected: | |||
| 135 | }; | 98 | }; |
| 136 | 99 | ||
| 137 | // A backend of BCAT that provides no operation. | 100 | // A backend of BCAT that provides no operation. |
| 138 | class NullBackend : public Backend { | 101 | class NullBcatBackend : public BcatBackend { |
| 139 | public: | 102 | public: |
| 140 | explicit NullBackend(DirectoryGetter getter); | 103 | explicit NullBcatBackend(DirectoryGetter getter); |
| 141 | ~NullBackend() override; | 104 | ~NullBcatBackend() override; |
| 142 | 105 | ||
| 143 | bool Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) override; | 106 | bool Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) override; |
| 144 | bool SynchronizeDirectory(TitleIDVersion title, std::string name, | 107 | bool SynchronizeDirectory(TitleIDVersion title, std::string name, |
| @@ -151,6 +114,7 @@ public: | |||
| 151 | std::optional<std::vector<u8>> GetLaunchParameter(TitleIDVersion title) override; | 114 | std::optional<std::vector<u8>> GetLaunchParameter(TitleIDVersion title) override; |
| 152 | }; | 115 | }; |
| 153 | 116 | ||
| 154 | std::unique_ptr<Backend> CreateBackendFromSettings(Core::System& system, DirectoryGetter getter); | 117 | std::unique_ptr<BcatBackend> CreateBackendFromSettings(Core::System& system, |
| 118 | DirectoryGetter getter); | ||
| 155 | 119 | ||
| 156 | } // namespace Service::BCAT | 120 | } // namespace Service::BCAT |
diff --git a/src/core/hle/service/bcat/bcat.cpp b/src/core/hle/service/bcat/bcat.cpp index d0ac17324..ea8b15998 100644 --- a/src/core/hle/service/bcat/bcat.cpp +++ b/src/core/hle/service/bcat/bcat.cpp | |||
| @@ -1,24 +1,38 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include "core/hle/service/bcat/backend/backend.h" | ||
| 4 | #include "core/hle/service/bcat/bcat.h" | 5 | #include "core/hle/service/bcat/bcat.h" |
| 6 | #include "core/hle/service/bcat/news/service_creator.h" | ||
| 7 | #include "core/hle/service/bcat/service_creator.h" | ||
| 8 | #include "core/hle/service/server_manager.h" | ||
| 5 | 9 | ||
| 6 | namespace Service::BCAT { | 10 | namespace Service::BCAT { |
| 7 | 11 | ||
| 8 | BCAT::BCAT(Core::System& system_, std::shared_ptr<Module> module_, | 12 | void LoopProcess(Core::System& system) { |
| 9 | FileSystem::FileSystemController& fsc_, const char* name_) | 13 | auto server_manager = std::make_unique<ServerManager>(system); |
| 10 | : Interface(system_, std::move(module_), fsc_, name_) { | 14 | |
| 11 | // clang-format off | 15 | server_manager->RegisterNamedService("bcat:a", |
| 12 | static const FunctionInfo functions[] = { | 16 | std::make_shared<IServiceCreator>(system, "bcat:a")); |
| 13 | {0, &BCAT::CreateBcatService, "CreateBcatService"}, | 17 | server_manager->RegisterNamedService("bcat:m", |
| 14 | {1, &BCAT::CreateDeliveryCacheStorageService, "CreateDeliveryCacheStorageService"}, | 18 | std::make_shared<IServiceCreator>(system, "bcat:m")); |
| 15 | {2, &BCAT::CreateDeliveryCacheStorageServiceWithApplicationId, "CreateDeliveryCacheStorageServiceWithApplicationId"}, | 19 | server_manager->RegisterNamedService("bcat:u", |
| 16 | {3, nullptr, "CreateDeliveryCacheProgressService"}, | 20 | std::make_shared<IServiceCreator>(system, "bcat:u")); |
| 17 | {4, nullptr, "CreateDeliveryCacheProgressServiceWithApplicationId"}, | 21 | server_manager->RegisterNamedService("bcat:s", |
| 18 | }; | 22 | std::make_shared<IServiceCreator>(system, "bcat:s")); |
| 19 | // clang-format on | 23 | |
| 20 | RegisterHandlers(functions); | 24 | server_manager->RegisterNamedService( |
| 25 | "news:a", std::make_shared<News::IServiceCreator>(system, 0xffffffff, "news:a")); | ||
| 26 | server_manager->RegisterNamedService( | ||
| 27 | "news:p", std::make_shared<News::IServiceCreator>(system, 0x1, "news:p")); | ||
| 28 | server_manager->RegisterNamedService( | ||
| 29 | "news:c", std::make_shared<News::IServiceCreator>(system, 0x2, "news:c")); | ||
| 30 | server_manager->RegisterNamedService( | ||
| 31 | "news:v", std::make_shared<News::IServiceCreator>(system, 0x4, "news:v")); | ||
| 32 | server_manager->RegisterNamedService( | ||
| 33 | "news:m", std::make_shared<News::IServiceCreator>(system, 0xd, "news:m")); | ||
| 34 | |||
| 35 | ServerManager::RunServer(std::move(server_manager)); | ||
| 21 | } | 36 | } |
| 22 | 37 | ||
| 23 | BCAT::~BCAT() = default; | ||
| 24 | } // namespace Service::BCAT | 38 | } // namespace Service::BCAT |
diff --git a/src/core/hle/service/bcat/bcat.h b/src/core/hle/service/bcat/bcat.h index db9d3c8c5..2aaffc693 100644 --- a/src/core/hle/service/bcat/bcat.h +++ b/src/core/hle/service/bcat/bcat.h | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include "core/hle/service/bcat/bcat_module.h" | 6 | #include "core/hle/service/service.h" |
| 7 | 7 | ||
| 8 | namespace Core { | 8 | namespace Core { |
| 9 | class System; | 9 | class System; |
| @@ -11,11 +11,6 @@ class System; | |||
| 11 | 11 | ||
| 12 | namespace Service::BCAT { | 12 | namespace Service::BCAT { |
| 13 | 13 | ||
| 14 | class BCAT final : public Module::Interface { | 14 | void LoopProcess(Core::System& system); |
| 15 | public: | ||
| 16 | explicit BCAT(Core::System& system_, std::shared_ptr<Module> module_, | ||
| 17 | FileSystem::FileSystemController& fsc_, const char* name_); | ||
| 18 | ~BCAT() override; | ||
| 19 | }; | ||
| 20 | 15 | ||
| 21 | } // namespace Service::BCAT | 16 | } // namespace Service::BCAT |
diff --git a/src/core/hle/service/bcat/bcat_module.cpp b/src/core/hle/service/bcat/bcat_module.cpp deleted file mode 100644 index 76d7bb139..000000000 --- a/src/core/hle/service/bcat/bcat_module.cpp +++ /dev/null | |||
| @@ -1,606 +0,0 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <cctype> | ||
| 5 | #include <mbedtls/md5.h> | ||
| 6 | #include "common/hex_util.h" | ||
| 7 | #include "common/logging/log.h" | ||
| 8 | #include "common/settings.h" | ||
| 9 | #include "common/string_util.h" | ||
| 10 | #include "core/core.h" | ||
| 11 | #include "core/file_sys/vfs/vfs.h" | ||
| 12 | #include "core/hle/kernel/k_readable_event.h" | ||
| 13 | #include "core/hle/service/bcat/backend/backend.h" | ||
| 14 | #include "core/hle/service/bcat/bcat.h" | ||
| 15 | #include "core/hle/service/bcat/bcat_module.h" | ||
| 16 | #include "core/hle/service/filesystem/filesystem.h" | ||
| 17 | #include "core/hle/service/ipc_helpers.h" | ||
| 18 | #include "core/hle/service/server_manager.h" | ||
| 19 | |||
| 20 | namespace Service::BCAT { | ||
| 21 | |||
| 22 | constexpr Result ERROR_INVALID_ARGUMENT{ErrorModule::BCAT, 1}; | ||
| 23 | constexpr Result ERROR_FAILED_OPEN_ENTITY{ErrorModule::BCAT, 2}; | ||
| 24 | constexpr Result ERROR_ENTITY_ALREADY_OPEN{ErrorModule::BCAT, 6}; | ||
| 25 | constexpr Result ERROR_NO_OPEN_ENTITY{ErrorModule::BCAT, 7}; | ||
| 26 | |||
| 27 | // The command to clear the delivery cache just calls fs IFileSystem DeleteFile on all of the files | ||
| 28 | // and if any of them have a non-zero result it just forwards that result. This is the FS error code | ||
| 29 | // for permission denied, which is the closest approximation of this scenario. | ||
| 30 | constexpr Result ERROR_FAILED_CLEAR_CACHE{ErrorModule::FS, 6400}; | ||
| 31 | |||
| 32 | using BCATDigest = std::array<u8, 0x10>; | ||
| 33 | |||
| 34 | namespace { | ||
| 35 | |||
| 36 | u64 GetCurrentBuildID(const Core::System::CurrentBuildProcessID& id) { | ||
| 37 | u64 out{}; | ||
| 38 | std::memcpy(&out, id.data(), sizeof(u64)); | ||
| 39 | return out; | ||
| 40 | } | ||
| 41 | |||
| 42 | // The digest is only used to determine if a file is unique compared to others of the same name. | ||
| 43 | // Since the algorithm isn't ever checked in game, MD5 is safe. | ||
| 44 | BCATDigest DigestFile(const FileSys::VirtualFile& file) { | ||
| 45 | BCATDigest out{}; | ||
| 46 | const auto bytes = file->ReadAllBytes(); | ||
| 47 | mbedtls_md5_ret(bytes.data(), bytes.size(), out.data()); | ||
| 48 | return out; | ||
| 49 | } | ||
| 50 | |||
| 51 | // For a name to be valid it must be non-empty, must have a null terminating character as the final | ||
| 52 | // char, can only contain numbers, letters, underscores and a hyphen if directory and a period if | ||
| 53 | // file. | ||
| 54 | bool VerifyNameValidInternal(HLERequestContext& ctx, std::array<char, 0x20> name, char match_char) { | ||
| 55 | const auto null_chars = std::count(name.begin(), name.end(), 0); | ||
| 56 | const auto bad_chars = std::count_if(name.begin(), name.end(), [match_char](char c) { | ||
| 57 | return !std::isalnum(static_cast<u8>(c)) && c != '_' && c != match_char && c != '\0'; | ||
| 58 | }); | ||
| 59 | if (null_chars == 0x20 || null_chars == 0 || bad_chars != 0 || name[0x1F] != '\0') { | ||
| 60 | LOG_ERROR(Service_BCAT, "Name passed was invalid!"); | ||
| 61 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 62 | rb.Push(ERROR_INVALID_ARGUMENT); | ||
| 63 | return false; | ||
| 64 | } | ||
| 65 | |||
| 66 | return true; | ||
| 67 | } | ||
| 68 | |||
| 69 | bool VerifyNameValidDir(HLERequestContext& ctx, DirectoryName name) { | ||
| 70 | return VerifyNameValidInternal(ctx, name, '-'); | ||
| 71 | } | ||
| 72 | |||
| 73 | bool VerifyNameValidFile(HLERequestContext& ctx, FileName name) { | ||
| 74 | return VerifyNameValidInternal(ctx, name, '.'); | ||
| 75 | } | ||
| 76 | |||
| 77 | } // Anonymous namespace | ||
| 78 | |||
| 79 | struct DeliveryCacheDirectoryEntry { | ||
| 80 | FileName name; | ||
| 81 | u64 size; | ||
| 82 | BCATDigest digest; | ||
| 83 | }; | ||
| 84 | |||
| 85 | class IDeliveryCacheProgressService final : public ServiceFramework<IDeliveryCacheProgressService> { | ||
| 86 | public: | ||
| 87 | explicit IDeliveryCacheProgressService(Core::System& system_, Kernel::KReadableEvent& event_, | ||
| 88 | const DeliveryCacheProgressImpl& impl_) | ||
| 89 | : ServiceFramework{system_, "IDeliveryCacheProgressService"}, event{event_}, impl{impl_} { | ||
| 90 | // clang-format off | ||
| 91 | static const FunctionInfo functions[] = { | ||
| 92 | {0, &IDeliveryCacheProgressService::GetEvent, "GetEvent"}, | ||
| 93 | {1, &IDeliveryCacheProgressService::GetImpl, "GetImpl"}, | ||
| 94 | }; | ||
| 95 | // clang-format on | ||
| 96 | |||
| 97 | RegisterHandlers(functions); | ||
| 98 | } | ||
| 99 | |||
| 100 | private: | ||
| 101 | void GetEvent(HLERequestContext& ctx) { | ||
| 102 | LOG_DEBUG(Service_BCAT, "called"); | ||
| 103 | |||
| 104 | IPC::ResponseBuilder rb{ctx, 2, 1}; | ||
| 105 | rb.Push(ResultSuccess); | ||
| 106 | rb.PushCopyObjects(event); | ||
| 107 | } | ||
| 108 | |||
| 109 | void GetImpl(HLERequestContext& ctx) { | ||
| 110 | LOG_DEBUG(Service_BCAT, "called"); | ||
| 111 | |||
| 112 | ctx.WriteBuffer(impl); | ||
| 113 | |||
| 114 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 115 | rb.Push(ResultSuccess); | ||
| 116 | } | ||
| 117 | |||
| 118 | Kernel::KReadableEvent& event; | ||
| 119 | const DeliveryCacheProgressImpl& impl; | ||
| 120 | }; | ||
| 121 | |||
| 122 | class IBcatService final : public ServiceFramework<IBcatService> { | ||
| 123 | public: | ||
| 124 | explicit IBcatService(Core::System& system_, Backend& backend_) | ||
| 125 | : ServiceFramework{system_, "IBcatService"}, backend{backend_}, | ||
| 126 | progress{{ | ||
| 127 | ProgressServiceBackend{system_, "Normal"}, | ||
| 128 | ProgressServiceBackend{system_, "Directory"}, | ||
| 129 | }} { | ||
| 130 | // clang-format off | ||
| 131 | static const FunctionInfo functions[] = { | ||
| 132 | {10100, &IBcatService::RequestSyncDeliveryCache, "RequestSyncDeliveryCache"}, | ||
| 133 | {10101, &IBcatService::RequestSyncDeliveryCacheWithDirectoryName, "RequestSyncDeliveryCacheWithDirectoryName"}, | ||
| 134 | {10200, nullptr, "CancelSyncDeliveryCacheRequest"}, | ||
| 135 | {20100, nullptr, "RequestSyncDeliveryCacheWithApplicationId"}, | ||
| 136 | {20101, nullptr, "RequestSyncDeliveryCacheWithApplicationIdAndDirectoryName"}, | ||
| 137 | {20300, nullptr, "GetDeliveryCacheStorageUpdateNotifier"}, | ||
| 138 | {20301, nullptr, "RequestSuspendDeliveryTask"}, | ||
| 139 | {20400, nullptr, "RegisterSystemApplicationDeliveryTask"}, | ||
| 140 | {20401, nullptr, "UnregisterSystemApplicationDeliveryTask"}, | ||
| 141 | {20410, nullptr, "SetSystemApplicationDeliveryTaskTimer"}, | ||
| 142 | {30100, &IBcatService::SetPassphrase, "SetPassphrase"}, | ||
| 143 | {30101, nullptr, "Unknown30101"}, | ||
| 144 | {30102, nullptr, "Unknown30102"}, | ||
| 145 | {30200, nullptr, "RegisterBackgroundDeliveryTask"}, | ||
| 146 | {30201, nullptr, "UnregisterBackgroundDeliveryTask"}, | ||
| 147 | {30202, nullptr, "BlockDeliveryTask"}, | ||
| 148 | {30203, nullptr, "UnblockDeliveryTask"}, | ||
| 149 | {30210, nullptr, "SetDeliveryTaskTimer"}, | ||
| 150 | {30300, nullptr, "RegisterSystemApplicationDeliveryTasks"}, | ||
| 151 | {90100, nullptr, "EnumerateBackgroundDeliveryTask"}, | ||
| 152 | {90101, nullptr, "Unknown90101"}, | ||
| 153 | {90200, nullptr, "GetDeliveryList"}, | ||
| 154 | {90201, &IBcatService::ClearDeliveryCacheStorage, "ClearDeliveryCacheStorage"}, | ||
| 155 | {90202, nullptr, "ClearDeliveryTaskSubscriptionStatus"}, | ||
| 156 | {90300, nullptr, "GetPushNotificationLog"}, | ||
| 157 | {90301, nullptr, "Unknown90301"}, | ||
| 158 | }; | ||
| 159 | // clang-format on | ||
| 160 | RegisterHandlers(functions); | ||
| 161 | } | ||
| 162 | |||
| 163 | private: | ||
| 164 | enum class SyncType { | ||
| 165 | Normal, | ||
| 166 | Directory, | ||
| 167 | Count, | ||
| 168 | }; | ||
| 169 | |||
| 170 | std::shared_ptr<IDeliveryCacheProgressService> CreateProgressService(SyncType type) { | ||
| 171 | auto& progress_backend{GetProgressBackend(type)}; | ||
| 172 | return std::make_shared<IDeliveryCacheProgressService>(system, progress_backend.GetEvent(), | ||
| 173 | progress_backend.GetImpl()); | ||
| 174 | } | ||
| 175 | |||
| 176 | void RequestSyncDeliveryCache(HLERequestContext& ctx) { | ||
| 177 | LOG_DEBUG(Service_BCAT, "called"); | ||
| 178 | |||
| 179 | backend.Synchronize({system.GetApplicationProcessProgramID(), | ||
| 180 | GetCurrentBuildID(system.GetApplicationProcessBuildID())}, | ||
| 181 | GetProgressBackend(SyncType::Normal)); | ||
| 182 | |||
| 183 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||
| 184 | rb.Push(ResultSuccess); | ||
| 185 | rb.PushIpcInterface(CreateProgressService(SyncType::Normal)); | ||
| 186 | } | ||
| 187 | |||
| 188 | void RequestSyncDeliveryCacheWithDirectoryName(HLERequestContext& ctx) { | ||
| 189 | IPC::RequestParser rp{ctx}; | ||
| 190 | const auto name_raw = rp.PopRaw<DirectoryName>(); | ||
| 191 | const auto name = | ||
| 192 | Common::StringFromFixedZeroTerminatedBuffer(name_raw.data(), name_raw.size()); | ||
| 193 | |||
| 194 | LOG_DEBUG(Service_BCAT, "called, name={}", name); | ||
| 195 | |||
| 196 | backend.SynchronizeDirectory({system.GetApplicationProcessProgramID(), | ||
| 197 | GetCurrentBuildID(system.GetApplicationProcessBuildID())}, | ||
| 198 | name, GetProgressBackend(SyncType::Directory)); | ||
| 199 | |||
| 200 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||
| 201 | rb.Push(ResultSuccess); | ||
| 202 | rb.PushIpcInterface(CreateProgressService(SyncType::Directory)); | ||
| 203 | } | ||
| 204 | |||
| 205 | void SetPassphrase(HLERequestContext& ctx) { | ||
| 206 | IPC::RequestParser rp{ctx}; | ||
| 207 | const auto title_id = rp.PopRaw<u64>(); | ||
| 208 | |||
| 209 | const auto passphrase_raw = ctx.ReadBuffer(); | ||
| 210 | |||
| 211 | LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, passphrase={}", title_id, | ||
| 212 | Common::HexToString(passphrase_raw)); | ||
| 213 | |||
| 214 | if (title_id == 0) { | ||
| 215 | LOG_ERROR(Service_BCAT, "Invalid title ID!"); | ||
| 216 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 217 | rb.Push(ERROR_INVALID_ARGUMENT); | ||
| 218 | } | ||
| 219 | |||
| 220 | if (passphrase_raw.size() > 0x40) { | ||
| 221 | LOG_ERROR(Service_BCAT, "Passphrase too large!"); | ||
| 222 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 223 | rb.Push(ERROR_INVALID_ARGUMENT); | ||
| 224 | return; | ||
| 225 | } | ||
| 226 | |||
| 227 | Passphrase passphrase{}; | ||
| 228 | std::memcpy(passphrase.data(), passphrase_raw.data(), | ||
| 229 | std::min(passphrase.size(), passphrase_raw.size())); | ||
| 230 | |||
| 231 | backend.SetPassphrase(title_id, passphrase); | ||
| 232 | |||
| 233 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 234 | rb.Push(ResultSuccess); | ||
| 235 | } | ||
| 236 | |||
| 237 | void ClearDeliveryCacheStorage(HLERequestContext& ctx) { | ||
| 238 | IPC::RequestParser rp{ctx}; | ||
| 239 | const auto title_id = rp.PopRaw<u64>(); | ||
| 240 | |||
| 241 | LOG_DEBUG(Service_BCAT, "called, title_id={:016X}", title_id); | ||
| 242 | |||
| 243 | if (title_id == 0) { | ||
| 244 | LOG_ERROR(Service_BCAT, "Invalid title ID!"); | ||
| 245 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 246 | rb.Push(ERROR_INVALID_ARGUMENT); | ||
| 247 | return; | ||
| 248 | } | ||
| 249 | |||
| 250 | if (!backend.Clear(title_id)) { | ||
| 251 | LOG_ERROR(Service_BCAT, "Could not clear the directory successfully!"); | ||
| 252 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 253 | rb.Push(ERROR_FAILED_CLEAR_CACHE); | ||
| 254 | return; | ||
| 255 | } | ||
| 256 | |||
| 257 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 258 | rb.Push(ResultSuccess); | ||
| 259 | } | ||
| 260 | |||
| 261 | ProgressServiceBackend& GetProgressBackend(SyncType type) { | ||
| 262 | return progress.at(static_cast<size_t>(type)); | ||
| 263 | } | ||
| 264 | |||
| 265 | const ProgressServiceBackend& GetProgressBackend(SyncType type) const { | ||
| 266 | return progress.at(static_cast<size_t>(type)); | ||
| 267 | } | ||
| 268 | |||
| 269 | Backend& backend; | ||
| 270 | std::array<ProgressServiceBackend, static_cast<size_t>(SyncType::Count)> progress; | ||
| 271 | }; | ||
| 272 | |||
| 273 | void Module::Interface::CreateBcatService(HLERequestContext& ctx) { | ||
| 274 | LOG_DEBUG(Service_BCAT, "called"); | ||
| 275 | |||
| 276 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||
| 277 | rb.Push(ResultSuccess); | ||
| 278 | rb.PushIpcInterface<IBcatService>(system, *backend); | ||
| 279 | } | ||
| 280 | |||
| 281 | class IDeliveryCacheFileService final : public ServiceFramework<IDeliveryCacheFileService> { | ||
| 282 | public: | ||
| 283 | explicit IDeliveryCacheFileService(Core::System& system_, FileSys::VirtualDir root_) | ||
| 284 | : ServiceFramework{system_, "IDeliveryCacheFileService"}, root(std::move(root_)) { | ||
| 285 | // clang-format off | ||
| 286 | static const FunctionInfo functions[] = { | ||
| 287 | {0, &IDeliveryCacheFileService::Open, "Open"}, | ||
| 288 | {1, &IDeliveryCacheFileService::Read, "Read"}, | ||
| 289 | {2, &IDeliveryCacheFileService::GetSize, "GetSize"}, | ||
| 290 | {3, &IDeliveryCacheFileService::GetDigest, "GetDigest"}, | ||
| 291 | }; | ||
| 292 | // clang-format on | ||
| 293 | |||
| 294 | RegisterHandlers(functions); | ||
| 295 | } | ||
| 296 | |||
| 297 | private: | ||
| 298 | void Open(HLERequestContext& ctx) { | ||
| 299 | IPC::RequestParser rp{ctx}; | ||
| 300 | const auto dir_name_raw = rp.PopRaw<DirectoryName>(); | ||
| 301 | const auto file_name_raw = rp.PopRaw<FileName>(); | ||
| 302 | |||
| 303 | const auto dir_name = | ||
| 304 | Common::StringFromFixedZeroTerminatedBuffer(dir_name_raw.data(), dir_name_raw.size()); | ||
| 305 | const auto file_name = | ||
| 306 | Common::StringFromFixedZeroTerminatedBuffer(file_name_raw.data(), file_name_raw.size()); | ||
| 307 | |||
| 308 | LOG_DEBUG(Service_BCAT, "called, dir_name={}, file_name={}", dir_name, file_name); | ||
| 309 | |||
| 310 | if (!VerifyNameValidDir(ctx, dir_name_raw) || !VerifyNameValidFile(ctx, file_name_raw)) | ||
| 311 | return; | ||
| 312 | |||
| 313 | if (current_file != nullptr) { | ||
| 314 | LOG_ERROR(Service_BCAT, "A file has already been opened on this interface!"); | ||
| 315 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 316 | rb.Push(ERROR_ENTITY_ALREADY_OPEN); | ||
| 317 | return; | ||
| 318 | } | ||
| 319 | |||
| 320 | const auto dir = root->GetSubdirectory(dir_name); | ||
| 321 | |||
| 322 | if (dir == nullptr) { | ||
| 323 | LOG_ERROR(Service_BCAT, "The directory of name={} couldn't be opened!", dir_name); | ||
| 324 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 325 | rb.Push(ERROR_FAILED_OPEN_ENTITY); | ||
| 326 | return; | ||
| 327 | } | ||
| 328 | |||
| 329 | current_file = dir->GetFile(file_name); | ||
| 330 | |||
| 331 | if (current_file == nullptr) { | ||
| 332 | LOG_ERROR(Service_BCAT, "The file of name={} couldn't be opened!", file_name); | ||
| 333 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 334 | rb.Push(ERROR_FAILED_OPEN_ENTITY); | ||
| 335 | return; | ||
| 336 | } | ||
| 337 | |||
| 338 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 339 | rb.Push(ResultSuccess); | ||
| 340 | } | ||
| 341 | |||
| 342 | void Read(HLERequestContext& ctx) { | ||
| 343 | IPC::RequestParser rp{ctx}; | ||
| 344 | const auto offset{rp.PopRaw<u64>()}; | ||
| 345 | |||
| 346 | auto size = ctx.GetWriteBufferSize(); | ||
| 347 | |||
| 348 | LOG_DEBUG(Service_BCAT, "called, offset={:016X}, size={:016X}", offset, size); | ||
| 349 | |||
| 350 | if (current_file == nullptr) { | ||
| 351 | LOG_ERROR(Service_BCAT, "There is no file currently open!"); | ||
| 352 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 353 | rb.Push(ERROR_NO_OPEN_ENTITY); | ||
| 354 | } | ||
| 355 | |||
| 356 | size = std::min<u64>(current_file->GetSize() - offset, size); | ||
| 357 | const auto buffer = current_file->ReadBytes(size, offset); | ||
| 358 | ctx.WriteBuffer(buffer); | ||
| 359 | |||
| 360 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 361 | rb.Push(ResultSuccess); | ||
| 362 | rb.Push<u64>(buffer.size()); | ||
| 363 | } | ||
| 364 | |||
| 365 | void GetSize(HLERequestContext& ctx) { | ||
| 366 | LOG_DEBUG(Service_BCAT, "called"); | ||
| 367 | |||
| 368 | if (current_file == nullptr) { | ||
| 369 | LOG_ERROR(Service_BCAT, "There is no file currently open!"); | ||
| 370 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 371 | rb.Push(ERROR_NO_OPEN_ENTITY); | ||
| 372 | } | ||
| 373 | |||
| 374 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 375 | rb.Push(ResultSuccess); | ||
| 376 | rb.Push<u64>(current_file->GetSize()); | ||
| 377 | } | ||
| 378 | |||
| 379 | void GetDigest(HLERequestContext& ctx) { | ||
| 380 | LOG_DEBUG(Service_BCAT, "called"); | ||
| 381 | |||
| 382 | if (current_file == nullptr) { | ||
| 383 | LOG_ERROR(Service_BCAT, "There is no file currently open!"); | ||
| 384 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 385 | rb.Push(ERROR_NO_OPEN_ENTITY); | ||
| 386 | } | ||
| 387 | |||
| 388 | IPC::ResponseBuilder rb{ctx, 6}; | ||
| 389 | rb.Push(ResultSuccess); | ||
| 390 | rb.PushRaw(DigestFile(current_file)); | ||
| 391 | } | ||
| 392 | |||
| 393 | FileSys::VirtualDir root; | ||
| 394 | FileSys::VirtualFile current_file; | ||
| 395 | }; | ||
| 396 | |||
| 397 | class IDeliveryCacheDirectoryService final | ||
| 398 | : public ServiceFramework<IDeliveryCacheDirectoryService> { | ||
| 399 | public: | ||
| 400 | explicit IDeliveryCacheDirectoryService(Core::System& system_, FileSys::VirtualDir root_) | ||
| 401 | : ServiceFramework{system_, "IDeliveryCacheDirectoryService"}, root(std::move(root_)) { | ||
| 402 | // clang-format off | ||
| 403 | static const FunctionInfo functions[] = { | ||
| 404 | {0, &IDeliveryCacheDirectoryService::Open, "Open"}, | ||
| 405 | {1, &IDeliveryCacheDirectoryService::Read, "Read"}, | ||
| 406 | {2, &IDeliveryCacheDirectoryService::GetCount, "GetCount"}, | ||
| 407 | }; | ||
| 408 | // clang-format on | ||
| 409 | |||
| 410 | RegisterHandlers(functions); | ||
| 411 | } | ||
| 412 | |||
| 413 | private: | ||
| 414 | void Open(HLERequestContext& ctx) { | ||
| 415 | IPC::RequestParser rp{ctx}; | ||
| 416 | const auto name_raw = rp.PopRaw<DirectoryName>(); | ||
| 417 | const auto name = | ||
| 418 | Common::StringFromFixedZeroTerminatedBuffer(name_raw.data(), name_raw.size()); | ||
| 419 | |||
| 420 | LOG_DEBUG(Service_BCAT, "called, name={}", name); | ||
| 421 | |||
| 422 | if (!VerifyNameValidDir(ctx, name_raw)) | ||
| 423 | return; | ||
| 424 | |||
| 425 | if (current_dir != nullptr) { | ||
| 426 | LOG_ERROR(Service_BCAT, "A file has already been opened on this interface!"); | ||
| 427 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 428 | rb.Push(ERROR_ENTITY_ALREADY_OPEN); | ||
| 429 | return; | ||
| 430 | } | ||
| 431 | |||
| 432 | current_dir = root->GetSubdirectory(name); | ||
| 433 | |||
| 434 | if (current_dir == nullptr) { | ||
| 435 | LOG_ERROR(Service_BCAT, "Failed to open the directory name={}!", name); | ||
| 436 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 437 | rb.Push(ERROR_FAILED_OPEN_ENTITY); | ||
| 438 | return; | ||
| 439 | } | ||
| 440 | |||
| 441 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 442 | rb.Push(ResultSuccess); | ||
| 443 | } | ||
| 444 | |||
| 445 | void Read(HLERequestContext& ctx) { | ||
| 446 | auto write_size = ctx.GetWriteBufferNumElements<DeliveryCacheDirectoryEntry>(); | ||
| 447 | |||
| 448 | LOG_DEBUG(Service_BCAT, "called, write_size={:016X}", write_size); | ||
| 449 | |||
| 450 | if (current_dir == nullptr) { | ||
| 451 | LOG_ERROR(Service_BCAT, "There is no open directory!"); | ||
| 452 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 453 | rb.Push(ERROR_NO_OPEN_ENTITY); | ||
| 454 | return; | ||
| 455 | } | ||
| 456 | |||
| 457 | const auto files = current_dir->GetFiles(); | ||
| 458 | write_size = std::min<u64>(write_size, files.size()); | ||
| 459 | std::vector<DeliveryCacheDirectoryEntry> entries(write_size); | ||
| 460 | std::transform( | ||
| 461 | files.begin(), files.begin() + write_size, entries.begin(), [](const auto& file) { | ||
| 462 | FileName name{}; | ||
| 463 | std::memcpy(name.data(), file->GetName().data(), | ||
| 464 | std::min(file->GetName().size(), name.size())); | ||
| 465 | return DeliveryCacheDirectoryEntry{name, file->GetSize(), DigestFile(file)}; | ||
| 466 | }); | ||
| 467 | |||
| 468 | ctx.WriteBuffer(entries); | ||
| 469 | |||
| 470 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 471 | rb.Push(ResultSuccess); | ||
| 472 | rb.Push(static_cast<u32>(write_size * sizeof(DeliveryCacheDirectoryEntry))); | ||
| 473 | } | ||
| 474 | |||
| 475 | void GetCount(HLERequestContext& ctx) { | ||
| 476 | LOG_DEBUG(Service_BCAT, "called"); | ||
| 477 | |||
| 478 | if (current_dir == nullptr) { | ||
| 479 | LOG_ERROR(Service_BCAT, "There is no open directory!"); | ||
| 480 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 481 | rb.Push(ERROR_NO_OPEN_ENTITY); | ||
| 482 | return; | ||
| 483 | } | ||
| 484 | |||
| 485 | const auto files = current_dir->GetFiles(); | ||
| 486 | |||
| 487 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 488 | rb.Push(ResultSuccess); | ||
| 489 | rb.Push(static_cast<u32>(files.size())); | ||
| 490 | } | ||
| 491 | |||
| 492 | FileSys::VirtualDir root; | ||
| 493 | FileSys::VirtualDir current_dir; | ||
| 494 | }; | ||
| 495 | |||
| 496 | class IDeliveryCacheStorageService final : public ServiceFramework<IDeliveryCacheStorageService> { | ||
| 497 | public: | ||
| 498 | explicit IDeliveryCacheStorageService(Core::System& system_, FileSys::VirtualDir root_) | ||
| 499 | : ServiceFramework{system_, "IDeliveryCacheStorageService"}, root(std::move(root_)) { | ||
| 500 | // clang-format off | ||
| 501 | static const FunctionInfo functions[] = { | ||
| 502 | {0, &IDeliveryCacheStorageService::CreateFileService, "CreateFileService"}, | ||
| 503 | {1, &IDeliveryCacheStorageService::CreateDirectoryService, "CreateDirectoryService"}, | ||
| 504 | {10, &IDeliveryCacheStorageService::EnumerateDeliveryCacheDirectory, "EnumerateDeliveryCacheDirectory"}, | ||
| 505 | }; | ||
| 506 | // clang-format on | ||
| 507 | |||
| 508 | RegisterHandlers(functions); | ||
| 509 | |||
| 510 | for (const auto& subdir : root->GetSubdirectories()) { | ||
| 511 | DirectoryName name{}; | ||
| 512 | std::memcpy(name.data(), subdir->GetName().data(), | ||
| 513 | std::min(sizeof(DirectoryName) - 1, subdir->GetName().size())); | ||
| 514 | entries.push_back(name); | ||
| 515 | } | ||
| 516 | } | ||
| 517 | |||
| 518 | private: | ||
| 519 | void CreateFileService(HLERequestContext& ctx) { | ||
| 520 | LOG_DEBUG(Service_BCAT, "called"); | ||
| 521 | |||
| 522 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||
| 523 | rb.Push(ResultSuccess); | ||
| 524 | rb.PushIpcInterface<IDeliveryCacheFileService>(system, root); | ||
| 525 | } | ||
| 526 | |||
| 527 | void CreateDirectoryService(HLERequestContext& ctx) { | ||
| 528 | LOG_DEBUG(Service_BCAT, "called"); | ||
| 529 | |||
| 530 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||
| 531 | rb.Push(ResultSuccess); | ||
| 532 | rb.PushIpcInterface<IDeliveryCacheDirectoryService>(system, root); | ||
| 533 | } | ||
| 534 | |||
| 535 | void EnumerateDeliveryCacheDirectory(HLERequestContext& ctx) { | ||
| 536 | auto size = ctx.GetWriteBufferNumElements<DirectoryName>(); | ||
| 537 | |||
| 538 | LOG_DEBUG(Service_BCAT, "called, size={:016X}", size); | ||
| 539 | |||
| 540 | size = std::min<u64>(size, entries.size() - next_read_index); | ||
| 541 | ctx.WriteBuffer(entries.data() + next_read_index, size * sizeof(DirectoryName)); | ||
| 542 | next_read_index += size; | ||
| 543 | |||
| 544 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 545 | rb.Push(ResultSuccess); | ||
| 546 | rb.Push(static_cast<u32>(size)); | ||
| 547 | } | ||
| 548 | |||
| 549 | FileSys::VirtualDir root; | ||
| 550 | std::vector<DirectoryName> entries; | ||
| 551 | u64 next_read_index = 0; | ||
| 552 | }; | ||
| 553 | |||
| 554 | void Module::Interface::CreateDeliveryCacheStorageService(HLERequestContext& ctx) { | ||
| 555 | LOG_DEBUG(Service_BCAT, "called"); | ||
| 556 | |||
| 557 | const auto title_id = system.GetApplicationProcessProgramID(); | ||
| 558 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||
| 559 | rb.Push(ResultSuccess); | ||
| 560 | rb.PushIpcInterface<IDeliveryCacheStorageService>(system, fsc.GetBCATDirectory(title_id)); | ||
| 561 | } | ||
| 562 | |||
| 563 | void Module::Interface::CreateDeliveryCacheStorageServiceWithApplicationId(HLERequestContext& ctx) { | ||
| 564 | IPC::RequestParser rp{ctx}; | ||
| 565 | const auto title_id = rp.PopRaw<u64>(); | ||
| 566 | |||
| 567 | LOG_DEBUG(Service_BCAT, "called, title_id={:016X}", title_id); | ||
| 568 | |||
| 569 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||
| 570 | rb.Push(ResultSuccess); | ||
| 571 | rb.PushIpcInterface<IDeliveryCacheStorageService>(system, fsc.GetBCATDirectory(title_id)); | ||
| 572 | } | ||
| 573 | |||
| 574 | std::unique_ptr<Backend> CreateBackendFromSettings([[maybe_unused]] Core::System& system, | ||
| 575 | DirectoryGetter getter) { | ||
| 576 | return std::make_unique<NullBackend>(std::move(getter)); | ||
| 577 | } | ||
| 578 | |||
| 579 | Module::Interface::Interface(Core::System& system_, std::shared_ptr<Module> module_, | ||
| 580 | FileSystem::FileSystemController& fsc_, const char* name) | ||
| 581 | : ServiceFramework{system_, name}, fsc{fsc_}, module{std::move(module_)}, | ||
| 582 | backend{CreateBackendFromSettings(system_, | ||
| 583 | [&fsc_](u64 tid) { return fsc_.GetBCATDirectory(tid); })} {} | ||
| 584 | |||
| 585 | Module::Interface::~Interface() = default; | ||
| 586 | |||
| 587 | void LoopProcess(Core::System& system) { | ||
| 588 | auto server_manager = std::make_unique<ServerManager>(system); | ||
| 589 | auto module = std::make_shared<Module>(); | ||
| 590 | |||
| 591 | server_manager->RegisterNamedService( | ||
| 592 | "bcat:a", | ||
| 593 | std::make_shared<BCAT>(system, module, system.GetFileSystemController(), "bcat:a")); | ||
| 594 | server_manager->RegisterNamedService( | ||
| 595 | "bcat:m", | ||
| 596 | std::make_shared<BCAT>(system, module, system.GetFileSystemController(), "bcat:m")); | ||
| 597 | server_manager->RegisterNamedService( | ||
| 598 | "bcat:u", | ||
| 599 | std::make_shared<BCAT>(system, module, system.GetFileSystemController(), "bcat:u")); | ||
| 600 | server_manager->RegisterNamedService( | ||
| 601 | "bcat:s", | ||
| 602 | std::make_shared<BCAT>(system, module, system.GetFileSystemController(), "bcat:s")); | ||
| 603 | ServerManager::RunServer(std::move(server_manager)); | ||
| 604 | } | ||
| 605 | |||
| 606 | } // namespace Service::BCAT | ||
diff --git a/src/core/hle/service/bcat/bcat_module.h b/src/core/hle/service/bcat/bcat_module.h deleted file mode 100644 index 87576288b..000000000 --- a/src/core/hle/service/bcat/bcat_module.h +++ /dev/null | |||
| @@ -1,46 +0,0 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "core/hle/service/service.h" | ||
| 7 | |||
| 8 | namespace Core { | ||
| 9 | class System; | ||
| 10 | } | ||
| 11 | |||
| 12 | namespace Service { | ||
| 13 | |||
| 14 | namespace FileSystem { | ||
| 15 | class FileSystemController; | ||
| 16 | } // namespace FileSystem | ||
| 17 | |||
| 18 | namespace BCAT { | ||
| 19 | |||
| 20 | class Backend; | ||
| 21 | |||
| 22 | class Module final { | ||
| 23 | public: | ||
| 24 | class Interface : public ServiceFramework<Interface> { | ||
| 25 | public: | ||
| 26 | explicit Interface(Core::System& system_, std::shared_ptr<Module> module_, | ||
| 27 | FileSystem::FileSystemController& fsc_, const char* name); | ||
| 28 | ~Interface() override; | ||
| 29 | |||
| 30 | void CreateBcatService(HLERequestContext& ctx); | ||
| 31 | void CreateDeliveryCacheStorageService(HLERequestContext& ctx); | ||
| 32 | void CreateDeliveryCacheStorageServiceWithApplicationId(HLERequestContext& ctx); | ||
| 33 | |||
| 34 | protected: | ||
| 35 | FileSystem::FileSystemController& fsc; | ||
| 36 | |||
| 37 | std::shared_ptr<Module> module; | ||
| 38 | std::unique_ptr<Backend> backend; | ||
| 39 | }; | ||
| 40 | }; | ||
| 41 | |||
| 42 | void LoopProcess(Core::System& system); | ||
| 43 | |||
| 44 | } // namespace BCAT | ||
| 45 | |||
| 46 | } // namespace Service | ||
diff --git a/src/core/hle/service/bcat/bcat_result.h b/src/core/hle/service/bcat/bcat_result.h new file mode 100644 index 000000000..edf8a6564 --- /dev/null +++ b/src/core/hle/service/bcat/bcat_result.h | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "core/hle/result.h" | ||
| 7 | |||
| 8 | namespace Service::BCAT { | ||
| 9 | |||
| 10 | constexpr Result ResultInvalidArgument{ErrorModule::BCAT, 1}; | ||
| 11 | constexpr Result ResultFailedOpenEntity{ErrorModule::BCAT, 2}; | ||
| 12 | constexpr Result ResultEntityAlreadyOpen{ErrorModule::BCAT, 6}; | ||
| 13 | constexpr Result ResultNoOpenEntry{ErrorModule::BCAT, 7}; | ||
| 14 | |||
| 15 | } // namespace Service::BCAT | ||
diff --git a/src/core/hle/service/bcat/bcat_service.cpp b/src/core/hle/service/bcat/bcat_service.cpp new file mode 100644 index 000000000..63b1072d2 --- /dev/null +++ b/src/core/hle/service/bcat/bcat_service.cpp | |||
| @@ -0,0 +1,132 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "common/hex_util.h" | ||
| 5 | #include "common/string_util.h" | ||
| 6 | #include "core/core.h" | ||
| 7 | #include "core/file_sys/errors.h" | ||
| 8 | #include "core/hle/service/bcat/backend/backend.h" | ||
| 9 | #include "core/hle/service/bcat/bcat_result.h" | ||
| 10 | #include "core/hle/service/bcat/bcat_service.h" | ||
| 11 | #include "core/hle/service/bcat/bcat_util.h" | ||
| 12 | #include "core/hle/service/bcat/delivery_cache_progress_service.h" | ||
| 13 | #include "core/hle/service/bcat/delivery_cache_storage_service.h" | ||
| 14 | #include "core/hle/service/cmif_serialization.h" | ||
| 15 | |||
| 16 | namespace Service::BCAT { | ||
| 17 | |||
| 18 | static u64 GetCurrentBuildID(const Core::System::CurrentBuildProcessID& id) { | ||
| 19 | u64 out{}; | ||
| 20 | std::memcpy(&out, id.data(), sizeof(u64)); | ||
| 21 | return out; | ||
| 22 | } | ||
| 23 | |||
| 24 | IBcatService::IBcatService(Core::System& system_, BcatBackend& backend_) | ||
| 25 | : ServiceFramework{system_, "IBcatService"}, backend{backend_}, | ||
| 26 | progress{{ | ||
| 27 | ProgressServiceBackend{system_, "Normal"}, | ||
| 28 | ProgressServiceBackend{system_, "Directory"}, | ||
| 29 | }} { | ||
| 30 | // clang-format off | ||
| 31 | static const FunctionInfo functions[] = { | ||
| 32 | {10100, D<&IBcatService::RequestSyncDeliveryCache>, "RequestSyncDeliveryCache"}, | ||
| 33 | {10101, D<&IBcatService::RequestSyncDeliveryCacheWithDirectoryName>, "RequestSyncDeliveryCacheWithDirectoryName"}, | ||
| 34 | {10200, nullptr, "CancelSyncDeliveryCacheRequest"}, | ||
| 35 | {20100, nullptr, "RequestSyncDeliveryCacheWithApplicationId"}, | ||
| 36 | {20101, nullptr, "RequestSyncDeliveryCacheWithApplicationIdAndDirectoryName"}, | ||
| 37 | {20300, nullptr, "GetDeliveryCacheStorageUpdateNotifier"}, | ||
| 38 | {20301, nullptr, "RequestSuspendDeliveryTask"}, | ||
| 39 | {20400, nullptr, "RegisterSystemApplicationDeliveryTask"}, | ||
| 40 | {20401, nullptr, "UnregisterSystemApplicationDeliveryTask"}, | ||
| 41 | {20410, nullptr, "SetSystemApplicationDeliveryTaskTimer"}, | ||
| 42 | {30100, D<&IBcatService::SetPassphrase>, "SetPassphrase"}, | ||
| 43 | {30101, nullptr, "Unknown30101"}, | ||
| 44 | {30102, nullptr, "Unknown30102"}, | ||
| 45 | {30200, nullptr, "RegisterBackgroundDeliveryTask"}, | ||
| 46 | {30201, nullptr, "UnregisterBackgroundDeliveryTask"}, | ||
| 47 | {30202, nullptr, "BlockDeliveryTask"}, | ||
| 48 | {30203, nullptr, "UnblockDeliveryTask"}, | ||
| 49 | {30210, nullptr, "SetDeliveryTaskTimer"}, | ||
| 50 | {30300, D<&IBcatService::RegisterSystemApplicationDeliveryTasks>, "RegisterSystemApplicationDeliveryTasks"}, | ||
| 51 | {90100, nullptr, "EnumerateBackgroundDeliveryTask"}, | ||
| 52 | {90101, nullptr, "Unknown90101"}, | ||
| 53 | {90200, nullptr, "GetDeliveryList"}, | ||
| 54 | {90201, D<&IBcatService::ClearDeliveryCacheStorage>, "ClearDeliveryCacheStorage"}, | ||
| 55 | {90202, nullptr, "ClearDeliveryTaskSubscriptionStatus"}, | ||
| 56 | {90300, nullptr, "GetPushNotificationLog"}, | ||
| 57 | {90301, nullptr, "Unknown90301"}, | ||
| 58 | }; | ||
| 59 | // clang-format on | ||
| 60 | RegisterHandlers(functions); | ||
| 61 | } | ||
| 62 | |||
| 63 | IBcatService::~IBcatService() = default; | ||
| 64 | |||
| 65 | Result IBcatService::RequestSyncDeliveryCache( | ||
| 66 | OutInterface<IDeliveryCacheProgressService> out_interface) { | ||
| 67 | LOG_DEBUG(Service_BCAT, "called"); | ||
| 68 | |||
| 69 | auto& progress_backend{GetProgressBackend(SyncType::Normal)}; | ||
| 70 | backend.Synchronize({system.GetApplicationProcessProgramID(), | ||
| 71 | GetCurrentBuildID(system.GetApplicationProcessBuildID())}, | ||
| 72 | GetProgressBackend(SyncType::Normal)); | ||
| 73 | |||
| 74 | *out_interface = std::make_shared<IDeliveryCacheProgressService>( | ||
| 75 | system, progress_backend.GetEvent(), progress_backend.GetImpl()); | ||
| 76 | R_SUCCEED(); | ||
| 77 | } | ||
| 78 | |||
| 79 | Result IBcatService::RequestSyncDeliveryCacheWithDirectoryName( | ||
| 80 | const DirectoryName& name_raw, OutInterface<IDeliveryCacheProgressService> out_interface) { | ||
| 81 | const auto name = Common::StringFromFixedZeroTerminatedBuffer(name_raw.data(), name_raw.size()); | ||
| 82 | |||
| 83 | LOG_DEBUG(Service_BCAT, "called, name={}", name); | ||
| 84 | |||
| 85 | auto& progress_backend{GetProgressBackend(SyncType::Directory)}; | ||
| 86 | backend.SynchronizeDirectory({system.GetApplicationProcessProgramID(), | ||
| 87 | GetCurrentBuildID(system.GetApplicationProcessBuildID())}, | ||
| 88 | name, progress_backend); | ||
| 89 | |||
| 90 | *out_interface = std::make_shared<IDeliveryCacheProgressService>( | ||
| 91 | system, progress_backend.GetEvent(), progress_backend.GetImpl()); | ||
| 92 | R_SUCCEED(); | ||
| 93 | } | ||
| 94 | |||
| 95 | Result IBcatService::SetPassphrase(u64 application_id, | ||
| 96 | InBuffer<BufferAttr_HipcPointer> passphrase_buffer) { | ||
| 97 | LOG_DEBUG(Service_BCAT, "called, application_id={:016X}, passphrase={}", application_id, | ||
| 98 | Common::HexToString(passphrase_buffer)); | ||
| 99 | |||
| 100 | R_UNLESS(application_id != 0, ResultInvalidArgument); | ||
| 101 | R_UNLESS(passphrase_buffer.size() <= 0x40, ResultInvalidArgument); | ||
| 102 | |||
| 103 | Passphrase passphrase{}; | ||
| 104 | std::memcpy(passphrase.data(), passphrase_buffer.data(), | ||
| 105 | std::min(passphrase.size(), passphrase_buffer.size())); | ||
| 106 | |||
| 107 | backend.SetPassphrase(application_id, passphrase); | ||
| 108 | R_SUCCEED(); | ||
| 109 | } | ||
| 110 | |||
| 111 | Result IBcatService::RegisterSystemApplicationDeliveryTasks() { | ||
| 112 | LOG_WARNING(Service_BCAT, "(STUBBED) called"); | ||
| 113 | R_SUCCEED(); | ||
| 114 | } | ||
| 115 | |||
| 116 | Result IBcatService::ClearDeliveryCacheStorage(u64 application_id) { | ||
| 117 | LOG_DEBUG(Service_BCAT, "called, title_id={:016X}", application_id); | ||
| 118 | |||
| 119 | R_UNLESS(application_id != 0, ResultInvalidArgument); | ||
| 120 | R_UNLESS(backend.Clear(application_id), FileSys::ResultPermissionDenied); | ||
| 121 | R_SUCCEED(); | ||
| 122 | } | ||
| 123 | |||
| 124 | ProgressServiceBackend& IBcatService::GetProgressBackend(SyncType type) { | ||
| 125 | return progress.at(static_cast<size_t>(type)); | ||
| 126 | } | ||
| 127 | |||
| 128 | const ProgressServiceBackend& IBcatService::GetProgressBackend(SyncType type) const { | ||
| 129 | return progress.at(static_cast<size_t>(type)); | ||
| 130 | } | ||
| 131 | |||
| 132 | } // namespace Service::BCAT | ||
diff --git a/src/core/hle/service/bcat/bcat_service.h b/src/core/hle/service/bcat/bcat_service.h new file mode 100644 index 000000000..dda5a2d5f --- /dev/null +++ b/src/core/hle/service/bcat/bcat_service.h | |||
| @@ -0,0 +1,45 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "core/hle/service/bcat/backend/backend.h" | ||
| 7 | #include "core/hle/service/bcat/bcat_types.h" | ||
| 8 | #include "core/hle/service/cmif_types.h" | ||
| 9 | #include "core/hle/service/service.h" | ||
| 10 | |||
| 11 | namespace Core { | ||
| 12 | class System; | ||
| 13 | } | ||
| 14 | |||
| 15 | namespace Service::BCAT { | ||
| 16 | class BcatBackend; | ||
| 17 | class IDeliveryCacheStorageService; | ||
| 18 | class IDeliveryCacheProgressService; | ||
| 19 | |||
| 20 | class IBcatService final : public ServiceFramework<IBcatService> { | ||
| 21 | public: | ||
| 22 | explicit IBcatService(Core::System& system_, BcatBackend& backend_); | ||
| 23 | ~IBcatService() override; | ||
| 24 | |||
| 25 | private: | ||
| 26 | Result RequestSyncDeliveryCache(OutInterface<IDeliveryCacheProgressService> out_interface); | ||
| 27 | |||
| 28 | Result RequestSyncDeliveryCacheWithDirectoryName( | ||
| 29 | const DirectoryName& name, OutInterface<IDeliveryCacheProgressService> out_interface); | ||
| 30 | |||
| 31 | Result SetPassphrase(u64 application_id, InBuffer<BufferAttr_HipcPointer> passphrase_buffer); | ||
| 32 | |||
| 33 | Result RegisterSystemApplicationDeliveryTasks(); | ||
| 34 | |||
| 35 | Result ClearDeliveryCacheStorage(u64 application_id); | ||
| 36 | |||
| 37 | private: | ||
| 38 | ProgressServiceBackend& GetProgressBackend(SyncType type); | ||
| 39 | const ProgressServiceBackend& GetProgressBackend(SyncType type) const; | ||
| 40 | |||
| 41 | BcatBackend& backend; | ||
| 42 | std::array<ProgressServiceBackend, static_cast<size_t>(SyncType::Count)> progress; | ||
| 43 | }; | ||
| 44 | |||
| 45 | } // namespace Service::BCAT | ||
diff --git a/src/core/hle/service/bcat/bcat_types.h b/src/core/hle/service/bcat/bcat_types.h new file mode 100644 index 000000000..b35dab7c5 --- /dev/null +++ b/src/core/hle/service/bcat/bcat_types.h | |||
| @@ -0,0 +1,66 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <array> | ||
| 7 | #include <functional> | ||
| 8 | |||
| 9 | #include "common/common_funcs.h" | ||
| 10 | #include "common/common_types.h" | ||
| 11 | #include "core/file_sys/vfs/vfs_types.h" | ||
| 12 | #include "core/hle/result.h" | ||
| 13 | |||
| 14 | namespace Service::BCAT { | ||
| 15 | |||
| 16 | using DirectoryName = std::array<char, 0x20>; | ||
| 17 | using FileName = std::array<char, 0x20>; | ||
| 18 | using BcatDigest = std::array<u8, 0x10>; | ||
| 19 | using Passphrase = std::array<u8, 0x20>; | ||
| 20 | using DirectoryGetter = std::function<FileSys::VirtualDir(u64)>; | ||
| 21 | |||
| 22 | enum class SyncType { | ||
| 23 | Normal, | ||
| 24 | Directory, | ||
| 25 | Count, | ||
| 26 | }; | ||
| 27 | |||
| 28 | enum class DeliveryCacheProgressStatus : s32 { | ||
| 29 | None = 0x0, | ||
| 30 | Queued = 0x1, | ||
| 31 | Connecting = 0x2, | ||
| 32 | ProcessingDataList = 0x3, | ||
| 33 | Downloading = 0x4, | ||
| 34 | Committing = 0x5, | ||
| 35 | Done = 0x9, | ||
| 36 | }; | ||
| 37 | |||
| 38 | struct DeliveryCacheDirectoryEntry { | ||
| 39 | FileName name; | ||
| 40 | u64 size; | ||
| 41 | BcatDigest digest; | ||
| 42 | }; | ||
| 43 | |||
| 44 | struct TitleIDVersion { | ||
| 45 | u64 title_id; | ||
| 46 | u64 build_id; | ||
| 47 | }; | ||
| 48 | |||
| 49 | struct DeliveryCacheProgressImpl { | ||
| 50 | DeliveryCacheProgressStatus status; | ||
| 51 | Result result; | ||
| 52 | DirectoryName current_directory; | ||
| 53 | FileName current_file; | ||
| 54 | s64 current_downloaded_bytes; ///< Bytes downloaded on current file. | ||
| 55 | s64 current_total_bytes; ///< Bytes total on current file. | ||
| 56 | s64 total_downloaded_bytes; ///< Bytes downloaded on overall download. | ||
| 57 | s64 total_bytes; ///< Bytes total on overall download. | ||
| 58 | INSERT_PADDING_BYTES_NOINIT( | ||
| 59 | 0x198); ///< Appears to be unused in official code, possibly reserved for future use. | ||
| 60 | }; | ||
| 61 | static_assert(sizeof(DeliveryCacheProgressImpl) == 0x200, | ||
| 62 | "DeliveryCacheProgressImpl has incorrect size."); | ||
| 63 | static_assert(std::is_trivial_v<DeliveryCacheProgressImpl>, | ||
| 64 | "DeliveryCacheProgressImpl type must be trivially copyable."); | ||
| 65 | |||
| 66 | } // namespace Service::BCAT | ||
diff --git a/src/core/hle/service/bcat/bcat_util.h b/src/core/hle/service/bcat/bcat_util.h new file mode 100644 index 000000000..6bf2657ee --- /dev/null +++ b/src/core/hle/service/bcat/bcat_util.h | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <array> | ||
| 7 | #include <cctype> | ||
| 8 | #include <mbedtls/md5.h> | ||
| 9 | |||
| 10 | #include "core/hle/service/bcat/bcat_result.h" | ||
| 11 | #include "core/hle/service/bcat/bcat_types.h" | ||
| 12 | |||
| 13 | namespace Service::BCAT { | ||
| 14 | |||
| 15 | // For a name to be valid it must be non-empty, must have a null terminating character as the final | ||
| 16 | // char, can only contain numbers, letters, underscores and a hyphen if directory and a period if | ||
| 17 | // file. | ||
| 18 | constexpr Result VerifyNameValidInternal(std::array<char, 0x20> name, char match_char) { | ||
| 19 | const auto null_chars = std::count(name.begin(), name.end(), 0); | ||
| 20 | const auto bad_chars = std::count_if(name.begin(), name.end(), [match_char](char c) { | ||
| 21 | return !std::isalnum(static_cast<u8>(c)) && c != '_' && c != match_char && c != '\0'; | ||
| 22 | }); | ||
| 23 | if (null_chars == 0x20 || null_chars == 0 || bad_chars != 0 || name[0x1F] != '\0') { | ||
| 24 | LOG_ERROR(Service_BCAT, "Name passed was invalid!"); | ||
| 25 | return ResultInvalidArgument; | ||
| 26 | } | ||
| 27 | |||
| 28 | return ResultSuccess; | ||
| 29 | } | ||
| 30 | |||
| 31 | constexpr Result VerifyNameValidDir(DirectoryName name) { | ||
| 32 | return VerifyNameValidInternal(name, '-'); | ||
| 33 | } | ||
| 34 | |||
| 35 | constexpr Result VerifyNameValidFile(FileName name) { | ||
| 36 | return VerifyNameValidInternal(name, '.'); | ||
| 37 | } | ||
| 38 | |||
| 39 | } // namespace Service::BCAT | ||
diff --git a/src/core/hle/service/bcat/delivery_cache_directory_service.cpp b/src/core/hle/service/bcat/delivery_cache_directory_service.cpp new file mode 100644 index 000000000..01f08a2fc --- /dev/null +++ b/src/core/hle/service/bcat/delivery_cache_directory_service.cpp | |||
| @@ -0,0 +1,80 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #include "common/string_util.h" | ||
| 5 | #include "core/file_sys/vfs/vfs_types.h" | ||
| 6 | #include "core/hle/service/bcat/bcat_result.h" | ||
| 7 | #include "core/hle/service/bcat/bcat_util.h" | ||
| 8 | #include "core/hle/service/bcat/delivery_cache_directory_service.h" | ||
| 9 | #include "core/hle/service/cmif_serialization.h" | ||
| 10 | |||
| 11 | namespace Service::BCAT { | ||
| 12 | |||
| 13 | // The digest is only used to determine if a file is unique compared to others of the same name. | ||
| 14 | // Since the algorithm isn't ever checked in game, MD5 is safe. | ||
| 15 | static BcatDigest DigestFile(const FileSys::VirtualFile& file) { | ||
| 16 | BcatDigest out{}; | ||
| 17 | const auto bytes = file->ReadAllBytes(); | ||
| 18 | mbedtls_md5_ret(bytes.data(), bytes.size(), out.data()); | ||
| 19 | return out; | ||
| 20 | } | ||
| 21 | |||
| 22 | IDeliveryCacheDirectoryService::IDeliveryCacheDirectoryService(Core::System& system_, | ||
| 23 | FileSys::VirtualDir root_) | ||
| 24 | : ServiceFramework{system_, "IDeliveryCacheDirectoryService"}, root(std::move(root_)) { | ||
| 25 | // clang-format off | ||
| 26 | static const FunctionInfo functions[] = { | ||
| 27 | {0, D<&IDeliveryCacheDirectoryService::Open>, "Open"}, | ||
| 28 | {1, D<&IDeliveryCacheDirectoryService::Read>, "Read"}, | ||
| 29 | {2, D<&IDeliveryCacheDirectoryService::GetCount>, "GetCount"}, | ||
| 30 | }; | ||
| 31 | // clang-format on | ||
| 32 | |||
| 33 | RegisterHandlers(functions); | ||
| 34 | } | ||
| 35 | |||
| 36 | IDeliveryCacheDirectoryService::~IDeliveryCacheDirectoryService() = default; | ||
| 37 | |||
| 38 | Result IDeliveryCacheDirectoryService::Open(const DirectoryName& dir_name_raw) { | ||
| 39 | const auto dir_name = | ||
| 40 | Common::StringFromFixedZeroTerminatedBuffer(dir_name_raw.data(), dir_name_raw.size()); | ||
| 41 | |||
| 42 | LOG_DEBUG(Service_BCAT, "called, dir_name={}", dir_name); | ||
| 43 | |||
| 44 | R_TRY(VerifyNameValidDir(dir_name_raw)); | ||
| 45 | R_UNLESS(current_dir == nullptr, ResultEntityAlreadyOpen); | ||
| 46 | |||
| 47 | const auto dir = root->GetSubdirectory(dir_name); | ||
| 48 | R_UNLESS(dir != nullptr, ResultFailedOpenEntity); | ||
| 49 | |||
| 50 | R_SUCCEED(); | ||
| 51 | } | ||
| 52 | |||
| 53 | Result IDeliveryCacheDirectoryService::Read( | ||
| 54 | Out<s32> out_count, OutArray<DeliveryCacheDirectoryEntry, BufferAttr_HipcMapAlias> out_buffer) { | ||
| 55 | LOG_DEBUG(Service_BCAT, "called, write_size={:016X}", out_buffer.size()); | ||
| 56 | |||
| 57 | R_UNLESS(current_dir != nullptr, ResultNoOpenEntry); | ||
| 58 | |||
| 59 | const auto files = current_dir->GetFiles(); | ||
| 60 | *out_count = static_cast<s32>(std::min(files.size(), out_buffer.size())); | ||
| 61 | std::transform(files.begin(), files.begin() + *out_count, out_buffer.begin(), | ||
| 62 | [](const auto& file) { | ||
| 63 | FileName name{}; | ||
| 64 | std::memcpy(name.data(), file->GetName().data(), | ||
| 65 | std::min(file->GetName().size(), name.size())); | ||
| 66 | return DeliveryCacheDirectoryEntry{name, file->GetSize(), DigestFile(file)}; | ||
| 67 | }); | ||
| 68 | R_SUCCEED(); | ||
| 69 | } | ||
| 70 | |||
| 71 | Result IDeliveryCacheDirectoryService::GetCount(Out<s32> out_count) { | ||
| 72 | LOG_DEBUG(Service_BCAT, "called"); | ||
| 73 | |||
| 74 | R_UNLESS(current_dir != nullptr, ResultNoOpenEntry); | ||
| 75 | |||
| 76 | *out_count = static_cast<s32>(current_dir->GetFiles().size()); | ||
| 77 | R_SUCCEED(); | ||
| 78 | } | ||
| 79 | |||
| 80 | } // namespace Service::BCAT | ||
diff --git a/src/core/hle/service/bcat/delivery_cache_directory_service.h b/src/core/hle/service/bcat/delivery_cache_directory_service.h new file mode 100644 index 000000000..b902c6495 --- /dev/null +++ b/src/core/hle/service/bcat/delivery_cache_directory_service.h | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "core/file_sys/vfs/vfs.h" | ||
| 7 | #include "core/hle/service/bcat/bcat_types.h" | ||
| 8 | #include "core/hle/service/cmif_types.h" | ||
| 9 | #include "core/hle/service/service.h" | ||
| 10 | |||
| 11 | namespace Core { | ||
| 12 | class System; | ||
| 13 | } | ||
| 14 | |||
| 15 | namespace Service::BCAT { | ||
| 16 | |||
| 17 | class IDeliveryCacheDirectoryService final | ||
| 18 | : public ServiceFramework<IDeliveryCacheDirectoryService> { | ||
| 19 | public: | ||
| 20 | explicit IDeliveryCacheDirectoryService(Core::System& system_, FileSys::VirtualDir root_); | ||
| 21 | ~IDeliveryCacheDirectoryService() override; | ||
| 22 | |||
| 23 | private: | ||
| 24 | Result Open(const DirectoryName& dir_name_raw); | ||
| 25 | Result Read(Out<s32> out_count, | ||
| 26 | OutArray<DeliveryCacheDirectoryEntry, BufferAttr_HipcMapAlias> out_buffer); | ||
| 27 | Result GetCount(Out<s32> out_count); | ||
| 28 | |||
| 29 | FileSys::VirtualDir root; | ||
| 30 | FileSys::VirtualDir current_dir; | ||
| 31 | }; | ||
| 32 | |||
| 33 | } // namespace Service::BCAT | ||
diff --git a/src/core/hle/service/bcat/delivery_cache_file_service.cpp b/src/core/hle/service/bcat/delivery_cache_file_service.cpp new file mode 100644 index 000000000..b75fac4bf --- /dev/null +++ b/src/core/hle/service/bcat/delivery_cache_file_service.cpp | |||
| @@ -0,0 +1,82 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #include "common/string_util.h" | ||
| 5 | #include "core/hle/service/bcat/bcat_result.h" | ||
| 6 | #include "core/hle/service/bcat/bcat_util.h" | ||
| 7 | #include "core/hle/service/bcat/delivery_cache_file_service.h" | ||
| 8 | #include "core/hle/service/cmif_serialization.h" | ||
| 9 | |||
| 10 | namespace Service::BCAT { | ||
| 11 | |||
| 12 | IDeliveryCacheFileService::IDeliveryCacheFileService(Core::System& system_, | ||
| 13 | FileSys::VirtualDir root_) | ||
| 14 | : ServiceFramework{system_, "IDeliveryCacheFileService"}, root(std::move(root_)) { | ||
| 15 | // clang-format off | ||
| 16 | static const FunctionInfo functions[] = { | ||
| 17 | {0, D<&IDeliveryCacheFileService::Open>, "Open"}, | ||
| 18 | {1, D<&IDeliveryCacheFileService::Read>, "Read"}, | ||
| 19 | {2, D<&IDeliveryCacheFileService::GetSize>, "GetSize"}, | ||
| 20 | {3, D<&IDeliveryCacheFileService::GetDigest>, "GetDigest"}, | ||
| 21 | }; | ||
| 22 | // clang-format on | ||
| 23 | |||
| 24 | RegisterHandlers(functions); | ||
| 25 | } | ||
| 26 | |||
| 27 | IDeliveryCacheFileService::~IDeliveryCacheFileService() = default; | ||
| 28 | |||
| 29 | Result IDeliveryCacheFileService::Open(const DirectoryName& dir_name_raw, | ||
| 30 | const FileName& file_name_raw) { | ||
| 31 | const auto dir_name = | ||
| 32 | Common::StringFromFixedZeroTerminatedBuffer(dir_name_raw.data(), dir_name_raw.size()); | ||
| 33 | const auto file_name = | ||
| 34 | Common::StringFromFixedZeroTerminatedBuffer(file_name_raw.data(), file_name_raw.size()); | ||
| 35 | |||
| 36 | LOG_DEBUG(Service_BCAT, "called, dir_name={}, file_name={}", dir_name, file_name); | ||
| 37 | |||
| 38 | R_TRY(VerifyNameValidDir(dir_name_raw)); | ||
| 39 | R_TRY(VerifyNameValidDir(file_name_raw)); | ||
| 40 | R_UNLESS(current_file == nullptr, ResultEntityAlreadyOpen); | ||
| 41 | |||
| 42 | const auto dir = root->GetSubdirectory(dir_name); | ||
| 43 | R_UNLESS(dir != nullptr, ResultFailedOpenEntity); | ||
| 44 | |||
| 45 | current_file = dir->GetFile(file_name); | ||
| 46 | R_UNLESS(current_file != nullptr, ResultFailedOpenEntity); | ||
| 47 | |||
| 48 | R_SUCCEED(); | ||
| 49 | } | ||
| 50 | |||
| 51 | Result IDeliveryCacheFileService::Read(Out<u64> out_buffer_size, u64 offset, | ||
| 52 | OutBuffer<BufferAttr_HipcMapAlias> out_buffer) { | ||
| 53 | LOG_DEBUG(Service_BCAT, "called, offset={:016X}, size={:016X}", offset, out_buffer.size()); | ||
| 54 | |||
| 55 | R_UNLESS(current_file != nullptr, ResultNoOpenEntry); | ||
| 56 | |||
| 57 | *out_buffer_size = std::min<u64>(current_file->GetSize() - offset, out_buffer.size()); | ||
| 58 | const auto buffer = current_file->ReadBytes(*out_buffer_size, offset); | ||
| 59 | memcpy(out_buffer.data(), buffer.data(), buffer.size()); | ||
| 60 | |||
| 61 | R_SUCCEED(); | ||
| 62 | } | ||
| 63 | |||
| 64 | Result IDeliveryCacheFileService::GetSize(Out<u64> out_size) { | ||
| 65 | LOG_DEBUG(Service_BCAT, "called"); | ||
| 66 | |||
| 67 | R_UNLESS(current_file != nullptr, ResultNoOpenEntry); | ||
| 68 | |||
| 69 | *out_size = current_file->GetSize(); | ||
| 70 | R_SUCCEED(); | ||
| 71 | } | ||
| 72 | |||
| 73 | Result IDeliveryCacheFileService::GetDigest(Out<BcatDigest> out_digest) { | ||
| 74 | LOG_DEBUG(Service_BCAT, "called"); | ||
| 75 | |||
| 76 | R_UNLESS(current_file != nullptr, ResultNoOpenEntry); | ||
| 77 | |||
| 78 | //*out_digest = DigestFile(current_file); | ||
| 79 | R_SUCCEED(); | ||
| 80 | } | ||
| 81 | |||
| 82 | } // namespace Service::BCAT | ||
diff --git a/src/core/hle/service/bcat/delivery_cache_file_service.h b/src/core/hle/service/bcat/delivery_cache_file_service.h new file mode 100644 index 000000000..e1012e687 --- /dev/null +++ b/src/core/hle/service/bcat/delivery_cache_file_service.h | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "core/file_sys/vfs/vfs.h" | ||
| 7 | #include "core/hle/service/bcat/bcat_types.h" | ||
| 8 | #include "core/hle/service/cmif_types.h" | ||
| 9 | #include "core/hle/service/service.h" | ||
| 10 | |||
| 11 | namespace Core { | ||
| 12 | class System; | ||
| 13 | } | ||
| 14 | |||
| 15 | namespace Service::BCAT { | ||
| 16 | |||
| 17 | class IDeliveryCacheFileService final : public ServiceFramework<IDeliveryCacheFileService> { | ||
| 18 | public: | ||
| 19 | explicit IDeliveryCacheFileService(Core::System& system_, FileSys::VirtualDir root_); | ||
| 20 | ~IDeliveryCacheFileService() override; | ||
| 21 | |||
| 22 | private: | ||
| 23 | Result Open(const DirectoryName& dir_name_raw, const FileName& file_name_raw); | ||
| 24 | Result Read(Out<u64> out_buffer_size, u64 offset, | ||
| 25 | OutBuffer<BufferAttr_HipcMapAlias> out_buffer); | ||
| 26 | Result GetSize(Out<u64> out_size); | ||
| 27 | Result GetDigest(Out<BcatDigest> out_digest); | ||
| 28 | |||
| 29 | FileSys::VirtualDir root; | ||
| 30 | FileSys::VirtualFile current_file; | ||
| 31 | }; | ||
| 32 | |||
| 33 | } // namespace Service::BCAT | ||
diff --git a/src/core/hle/service/bcat/delivery_cache_progress_service.cpp b/src/core/hle/service/bcat/delivery_cache_progress_service.cpp new file mode 100644 index 000000000..79e7e0d95 --- /dev/null +++ b/src/core/hle/service/bcat/delivery_cache_progress_service.cpp | |||
| @@ -0,0 +1,41 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #include "core/hle/service/bcat/bcat_types.h" | ||
| 5 | #include "core/hle/service/bcat/delivery_cache_progress_service.h" | ||
| 6 | #include "core/hle/service/cmif_serialization.h" | ||
| 7 | |||
| 8 | namespace Service::BCAT { | ||
| 9 | |||
| 10 | IDeliveryCacheProgressService::IDeliveryCacheProgressService(Core::System& system_, | ||
| 11 | Kernel::KReadableEvent& event_, | ||
| 12 | const DeliveryCacheProgressImpl& impl_) | ||
| 13 | : ServiceFramework{system_, "IDeliveryCacheProgressService"}, event{event_}, impl{impl_} { | ||
| 14 | // clang-format off | ||
| 15 | static const FunctionInfo functions[] = { | ||
| 16 | {0, D<&IDeliveryCacheProgressService::GetEvent>, "Get"}, | ||
| 17 | {1, D<&IDeliveryCacheProgressService::GetImpl>, "Get"}, | ||
| 18 | }; | ||
| 19 | // clang-format on | ||
| 20 | |||
| 21 | RegisterHandlers(functions); | ||
| 22 | } | ||
| 23 | |||
| 24 | IDeliveryCacheProgressService::~IDeliveryCacheProgressService() = default; | ||
| 25 | |||
| 26 | Result IDeliveryCacheProgressService::GetEvent(OutCopyHandle<Kernel::KReadableEvent> out_event) { | ||
| 27 | LOG_DEBUG(Service_BCAT, "called"); | ||
| 28 | |||
| 29 | *out_event = &event; | ||
| 30 | R_SUCCEED(); | ||
| 31 | } | ||
| 32 | |||
| 33 | Result IDeliveryCacheProgressService::GetImpl( | ||
| 34 | OutLargeData<DeliveryCacheProgressImpl, BufferAttr_HipcPointer> out_impl) { | ||
| 35 | LOG_DEBUG(Service_BCAT, "called"); | ||
| 36 | |||
| 37 | *out_impl = impl; | ||
| 38 | R_SUCCEED(); | ||
| 39 | } | ||
| 40 | |||
| 41 | } // namespace Service::BCAT | ||
diff --git a/src/core/hle/service/bcat/delivery_cache_progress_service.h b/src/core/hle/service/bcat/delivery_cache_progress_service.h new file mode 100644 index 000000000..f81a13980 --- /dev/null +++ b/src/core/hle/service/bcat/delivery_cache_progress_service.h | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "core/hle/service/cmif_types.h" | ||
| 7 | #include "core/hle/service/service.h" | ||
| 8 | |||
| 9 | namespace Core { | ||
| 10 | class System; | ||
| 11 | } | ||
| 12 | |||
| 13 | namespace Kernel { | ||
| 14 | class KEvent; | ||
| 15 | class KReadableEvent; | ||
| 16 | } // namespace Kernel | ||
| 17 | |||
| 18 | namespace Service::BCAT { | ||
| 19 | struct DeliveryCacheProgressImpl; | ||
| 20 | |||
| 21 | class IDeliveryCacheProgressService final : public ServiceFramework<IDeliveryCacheProgressService> { | ||
| 22 | public: | ||
| 23 | explicit IDeliveryCacheProgressService(Core::System& system_, Kernel::KReadableEvent& event_, | ||
| 24 | const DeliveryCacheProgressImpl& impl_); | ||
| 25 | ~IDeliveryCacheProgressService() override; | ||
| 26 | |||
| 27 | private: | ||
| 28 | Result GetEvent(OutCopyHandle<Kernel::KReadableEvent> out_event); | ||
| 29 | Result GetImpl(OutLargeData<DeliveryCacheProgressImpl, BufferAttr_HipcPointer> out_impl); | ||
| 30 | |||
| 31 | Kernel::KReadableEvent& event; | ||
| 32 | const DeliveryCacheProgressImpl& impl; | ||
| 33 | }; | ||
| 34 | |||
| 35 | } // namespace Service::BCAT | ||
diff --git a/src/core/hle/service/bcat/delivery_cache_storage_service.cpp b/src/core/hle/service/bcat/delivery_cache_storage_service.cpp new file mode 100644 index 000000000..4c79d71f4 --- /dev/null +++ b/src/core/hle/service/bcat/delivery_cache_storage_service.cpp | |||
| @@ -0,0 +1,57 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #include "core/hle/service/bcat/bcat_result.h" | ||
| 5 | #include "core/hle/service/bcat/delivery_cache_directory_service.h" | ||
| 6 | #include "core/hle/service/bcat/delivery_cache_file_service.h" | ||
| 7 | #include "core/hle/service/bcat/delivery_cache_storage_service.h" | ||
| 8 | #include "core/hle/service/cmif_serialization.h" | ||
| 9 | |||
| 10 | namespace Service::BCAT { | ||
| 11 | |||
| 12 | IDeliveryCacheStorageService::IDeliveryCacheStorageService(Core::System& system_, | ||
| 13 | FileSys::VirtualDir root_) | ||
| 14 | : ServiceFramework{system_, "IDeliveryCacheStorageService"}, root(std::move(root_)) { | ||
| 15 | // clang-format off | ||
| 16 | static const FunctionInfo functions[] = { | ||
| 17 | {0, D<&IDeliveryCacheStorageService::CreateFileService>, "CreateFileService"}, | ||
| 18 | {1, D<&IDeliveryCacheStorageService::CreateDirectoryService>, "CreateDirectoryService"}, | ||
| 19 | {10, D<&IDeliveryCacheStorageService::EnumerateDeliveryCacheDirectory>, "EnumerateDeliveryCacheDirectory"}, | ||
| 20 | }; | ||
| 21 | // clang-format on | ||
| 22 | |||
| 23 | RegisterHandlers(functions); | ||
| 24 | } | ||
| 25 | |||
| 26 | IDeliveryCacheStorageService::~IDeliveryCacheStorageService() = default; | ||
| 27 | |||
| 28 | Result IDeliveryCacheStorageService::CreateFileService( | ||
| 29 | OutInterface<IDeliveryCacheFileService> out_interface) { | ||
| 30 | LOG_DEBUG(Service_BCAT, "called"); | ||
| 31 | |||
| 32 | *out_interface = std::make_shared<IDeliveryCacheFileService>(system, root); | ||
| 33 | R_SUCCEED(); | ||
| 34 | } | ||
| 35 | |||
| 36 | Result IDeliveryCacheStorageService::CreateDirectoryService( | ||
| 37 | OutInterface<IDeliveryCacheDirectoryService> out_interface) { | ||
| 38 | LOG_DEBUG(Service_BCAT, "called"); | ||
| 39 | |||
| 40 | *out_interface = std::make_shared<IDeliveryCacheDirectoryService>(system, root); | ||
| 41 | R_SUCCEED(); | ||
| 42 | } | ||
| 43 | |||
| 44 | Result IDeliveryCacheStorageService::EnumerateDeliveryCacheDirectory( | ||
| 45 | Out<s32> out_directory_count, | ||
| 46 | OutArray<DirectoryName, BufferAttr_HipcMapAlias> out_directories) { | ||
| 47 | LOG_DEBUG(Service_BCAT, "called, size={:016X}", out_directories.size()); | ||
| 48 | |||
| 49 | *out_directory_count = | ||
| 50 | static_cast<s32>(std::min(out_directories.size(), entries.size() - next_read_index)); | ||
| 51 | memcpy(out_directories.data(), entries.data() + next_read_index, | ||
| 52 | *out_directory_count * sizeof(DirectoryName)); | ||
| 53 | next_read_index += *out_directory_count; | ||
| 54 | R_SUCCEED(); | ||
| 55 | } | ||
| 56 | |||
| 57 | } // namespace Service::BCAT | ||
diff --git a/src/core/hle/service/bcat/delivery_cache_storage_service.h b/src/core/hle/service/bcat/delivery_cache_storage_service.h new file mode 100644 index 000000000..3b8dfb1a3 --- /dev/null +++ b/src/core/hle/service/bcat/delivery_cache_storage_service.h | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "core/file_sys/vfs/vfs.h" | ||
| 7 | #include "core/hle/service/bcat/bcat_types.h" | ||
| 8 | #include "core/hle/service/cmif_types.h" | ||
| 9 | #include "core/hle/service/service.h" | ||
| 10 | |||
| 11 | namespace Core { | ||
| 12 | class System; | ||
| 13 | } | ||
| 14 | |||
| 15 | namespace Service::BCAT { | ||
| 16 | class IDeliveryCacheFileService; | ||
| 17 | class IDeliveryCacheDirectoryService; | ||
| 18 | |||
| 19 | class IDeliveryCacheStorageService final : public ServiceFramework<IDeliveryCacheStorageService> { | ||
| 20 | public: | ||
| 21 | explicit IDeliveryCacheStorageService(Core::System& system_, FileSys::VirtualDir root_); | ||
| 22 | ~IDeliveryCacheStorageService() override; | ||
| 23 | |||
| 24 | private: | ||
| 25 | Result CreateFileService(OutInterface<IDeliveryCacheFileService> out_interface); | ||
| 26 | Result CreateDirectoryService(OutInterface<IDeliveryCacheDirectoryService> out_interface); | ||
| 27 | Result EnumerateDeliveryCacheDirectory( | ||
| 28 | Out<s32> out_directory_count, | ||
| 29 | OutArray<DirectoryName, BufferAttr_HipcMapAlias> out_directories); | ||
| 30 | |||
| 31 | FileSys::VirtualDir root; | ||
| 32 | std::vector<DirectoryName> entries; | ||
| 33 | std::size_t next_read_index = 0; | ||
| 34 | }; | ||
| 35 | |||
| 36 | } // namespace Service::BCAT | ||
diff --git a/src/core/hle/service/bcat/news/newly_arrived_event_holder.cpp b/src/core/hle/service/bcat/news/newly_arrived_event_holder.cpp new file mode 100644 index 000000000..ed393f7a2 --- /dev/null +++ b/src/core/hle/service/bcat/news/newly_arrived_event_holder.cpp | |||
| @@ -0,0 +1,34 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #include "core/hle/service/bcat/news/newly_arrived_event_holder.h" | ||
| 5 | #include "core/hle/service/cmif_serialization.h" | ||
| 6 | |||
| 7 | namespace Service::News { | ||
| 8 | |||
| 9 | INewlyArrivedEventHolder::INewlyArrivedEventHolder(Core::System& system_) | ||
| 10 | : ServiceFramework{system_, "INewlyArrivedEventHolder"}, service_context{ | ||
| 11 | system_, | ||
| 12 | "INewlyArrivedEventHolder"} { | ||
| 13 | // clang-format off | ||
| 14 | static const FunctionInfo functions[] = { | ||
| 15 | {0, D<&INewlyArrivedEventHolder::Get>, "Get"}, | ||
| 16 | }; | ||
| 17 | // clang-format on | ||
| 18 | |||
| 19 | RegisterHandlers(functions); | ||
| 20 | arrived_event = service_context.CreateEvent("INewlyArrivedEventHolder::ArrivedEvent"); | ||
| 21 | } | ||
| 22 | |||
| 23 | INewlyArrivedEventHolder::~INewlyArrivedEventHolder() { | ||
| 24 | service_context.CloseEvent(arrived_event); | ||
| 25 | } | ||
| 26 | |||
| 27 | Result INewlyArrivedEventHolder::Get(OutCopyHandle<Kernel::KReadableEvent> out_event) { | ||
| 28 | LOG_INFO(Service_BCAT, "called"); | ||
| 29 | |||
| 30 | *out_event = &arrived_event->GetReadableEvent(); | ||
| 31 | R_SUCCEED(); | ||
| 32 | } | ||
| 33 | |||
| 34 | } // namespace Service::News | ||
diff --git a/src/core/hle/service/bcat/news/newly_arrived_event_holder.h b/src/core/hle/service/bcat/news/newly_arrived_event_holder.h new file mode 100644 index 000000000..6cc9ae099 --- /dev/null +++ b/src/core/hle/service/bcat/news/newly_arrived_event_holder.h | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "core/hle/service/cmif_types.h" | ||
| 7 | #include "core/hle/service/kernel_helpers.h" | ||
| 8 | #include "core/hle/service/service.h" | ||
| 9 | |||
| 10 | namespace Core { | ||
| 11 | class System; | ||
| 12 | } | ||
| 13 | |||
| 14 | namespace Kernel { | ||
| 15 | class KEvent; | ||
| 16 | class KReadableEvent; | ||
| 17 | } // namespace Kernel | ||
| 18 | |||
| 19 | namespace Service::News { | ||
| 20 | |||
| 21 | class INewlyArrivedEventHolder final : public ServiceFramework<INewlyArrivedEventHolder> { | ||
| 22 | public: | ||
| 23 | explicit INewlyArrivedEventHolder(Core::System& system_); | ||
| 24 | ~INewlyArrivedEventHolder() override; | ||
| 25 | |||
| 26 | private: | ||
| 27 | Result Get(OutCopyHandle<Kernel::KReadableEvent> out_event); | ||
| 28 | |||
| 29 | Kernel::KEvent* arrived_event; | ||
| 30 | KernelHelpers::ServiceContext service_context; | ||
| 31 | }; | ||
| 32 | |||
| 33 | } // namespace Service::News | ||
diff --git a/src/core/hle/service/bcat/news/news_data_service.cpp b/src/core/hle/service/bcat/news/news_data_service.cpp new file mode 100644 index 000000000..08103c9c3 --- /dev/null +++ b/src/core/hle/service/bcat/news/news_data_service.cpp | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #include "core/hle/service/bcat/news/news_data_service.h" | ||
| 5 | |||
| 6 | namespace Service::News { | ||
| 7 | |||
| 8 | INewsDataService::INewsDataService(Core::System& system_) | ||
| 9 | : ServiceFramework{system_, "INewsDataService"} { | ||
| 10 | // clang-format off | ||
| 11 | static const FunctionInfo functions[] = { | ||
| 12 | {0, nullptr, "Open"}, | ||
| 13 | {1, nullptr, "OpenWithNewsRecordV1"}, | ||
| 14 | {2, nullptr, "Read"}, | ||
| 15 | {3, nullptr, "GetSize"}, | ||
| 16 | {1001, nullptr, "OpenWithNewsRecord"}, | ||
| 17 | }; | ||
| 18 | // clang-format on | ||
| 19 | |||
| 20 | RegisterHandlers(functions); | ||
| 21 | } | ||
| 22 | |||
| 23 | INewsDataService::~INewsDataService() = default; | ||
| 24 | |||
| 25 | } // namespace Service::News | ||
diff --git a/src/core/hle/service/bcat/news/news_data_service.h b/src/core/hle/service/bcat/news/news_data_service.h new file mode 100644 index 000000000..12082ada4 --- /dev/null +++ b/src/core/hle/service/bcat/news/news_data_service.h | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "core/hle/service/service.h" | ||
| 7 | |||
| 8 | namespace Core { | ||
| 9 | class System; | ||
| 10 | } | ||
| 11 | |||
| 12 | namespace Service::News { | ||
| 13 | |||
| 14 | class INewsDataService final : public ServiceFramework<INewsDataService> { | ||
| 15 | public: | ||
| 16 | explicit INewsDataService(Core::System& system_); | ||
| 17 | ~INewsDataService() override; | ||
| 18 | }; | ||
| 19 | |||
| 20 | } // namespace Service::News | ||
diff --git a/src/core/hle/service/bcat/news/news_database_service.cpp b/src/core/hle/service/bcat/news/news_database_service.cpp new file mode 100644 index 000000000..b94ef0636 --- /dev/null +++ b/src/core/hle/service/bcat/news/news_database_service.cpp | |||
| @@ -0,0 +1,53 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #include "core/hle/service/bcat/news/news_database_service.h" | ||
| 5 | #include "core/hle/service/cmif_serialization.h" | ||
| 6 | |||
| 7 | namespace Service::News { | ||
| 8 | |||
| 9 | INewsDatabaseService::INewsDatabaseService(Core::System& system_) | ||
| 10 | : ServiceFramework{system_, "INewsDatabaseService"} { | ||
| 11 | // clang-format off | ||
| 12 | static const FunctionInfo functions[] = { | ||
| 13 | {0, nullptr, "GetListV1"}, | ||
| 14 | {1, D<&INewsDatabaseService::Count>, "Count"}, | ||
| 15 | {2, nullptr, "CountWithKey"}, | ||
| 16 | {3, nullptr, "UpdateIntegerValue"}, | ||
| 17 | {4, D<&INewsDatabaseService::UpdateIntegerValueWithAddition>, "UpdateIntegerValueWithAddition"}, | ||
| 18 | {5, nullptr, "UpdateStringValue"}, | ||
| 19 | {1000, D<&INewsDatabaseService::GetList>, "GetList"}, | ||
| 20 | }; | ||
| 21 | // clang-format on | ||
| 22 | |||
| 23 | RegisterHandlers(functions); | ||
| 24 | } | ||
| 25 | |||
| 26 | INewsDatabaseService::~INewsDatabaseService() = default; | ||
| 27 | |||
| 28 | Result INewsDatabaseService::Count(Out<s32> out_count, | ||
| 29 | InBuffer<BufferAttr_HipcPointer> buffer_data) { | ||
| 30 | LOG_WARNING(Service_BCAT, "(STUBBED) called, buffer_size={}", buffer_data.size()); | ||
| 31 | *out_count = 0; | ||
| 32 | R_SUCCEED(); | ||
| 33 | } | ||
| 34 | |||
| 35 | Result INewsDatabaseService::UpdateIntegerValueWithAddition( | ||
| 36 | u32 value, InBuffer<BufferAttr_HipcPointer> buffer_data_1, | ||
| 37 | InBuffer<BufferAttr_HipcPointer> buffer_data_2) { | ||
| 38 | LOG_WARNING(Service_BCAT, "(STUBBED) called, value={}, buffer_size_1={}, buffer_data_2={}", | ||
| 39 | value, buffer_data_1.size(), buffer_data_2.size()); | ||
| 40 | R_SUCCEED(); | ||
| 41 | } | ||
| 42 | |||
| 43 | Result INewsDatabaseService::GetList(Out<s32> out_count, u32 value, | ||
| 44 | OutBuffer<BufferAttr_HipcMapAlias> out_buffer_data, | ||
| 45 | InBuffer<BufferAttr_HipcPointer> buffer_data_1, | ||
| 46 | InBuffer<BufferAttr_HipcPointer> buffer_data_2) { | ||
| 47 | LOG_WARNING(Service_BCAT, "(STUBBED) called, value={}, buffer_size_1={}, buffer_data_2={}", | ||
| 48 | value, buffer_data_1.size(), buffer_data_2.size()); | ||
| 49 | *out_count = 0; | ||
| 50 | R_SUCCEED(); | ||
| 51 | } | ||
| 52 | |||
| 53 | } // namespace Service::News | ||
diff --git a/src/core/hle/service/bcat/news/news_database_service.h b/src/core/hle/service/bcat/news/news_database_service.h new file mode 100644 index 000000000..860b7074c --- /dev/null +++ b/src/core/hle/service/bcat/news/news_database_service.h | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "core/hle/service/cmif_types.h" | ||
| 7 | #include "core/hle/service/service.h" | ||
| 8 | |||
| 9 | namespace Core { | ||
| 10 | class System; | ||
| 11 | } | ||
| 12 | |||
| 13 | namespace Service::News { | ||
| 14 | |||
| 15 | class INewsDatabaseService final : public ServiceFramework<INewsDatabaseService> { | ||
| 16 | public: | ||
| 17 | explicit INewsDatabaseService(Core::System& system_); | ||
| 18 | ~INewsDatabaseService() override; | ||
| 19 | |||
| 20 | private: | ||
| 21 | Result Count(Out<s32> out_count, InBuffer<BufferAttr_HipcPointer> buffer_data); | ||
| 22 | |||
| 23 | Result UpdateIntegerValueWithAddition(u32 value, InBuffer<BufferAttr_HipcPointer> buffer_data_1, | ||
| 24 | InBuffer<BufferAttr_HipcPointer> buffer_data_2); | ||
| 25 | |||
| 26 | Result GetList(Out<s32> out_count, u32 value, | ||
| 27 | OutBuffer<BufferAttr_HipcMapAlias> out_buffer_data, | ||
| 28 | InBuffer<BufferAttr_HipcPointer> buffer_data_1, | ||
| 29 | InBuffer<BufferAttr_HipcPointer> buffer_data_2); | ||
| 30 | }; | ||
| 31 | |||
| 32 | } // namespace Service::News | ||
diff --git a/src/core/hle/service/bcat/news/news_service.cpp b/src/core/hle/service/bcat/news/news_service.cpp new file mode 100644 index 000000000..bc6c2afd2 --- /dev/null +++ b/src/core/hle/service/bcat/news/news_service.cpp | |||
| @@ -0,0 +1,57 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #include "core/hle/service/bcat/news/news_service.h" | ||
| 5 | #include "core/hle/service/cmif_serialization.h" | ||
| 6 | |||
| 7 | namespace Service::News { | ||
| 8 | |||
| 9 | INewsService::INewsService(Core::System& system_) : ServiceFramework{system_, "INewsService"} { | ||
| 10 | // clang-format off | ||
| 11 | static const FunctionInfo functions[] = { | ||
| 12 | {10100, nullptr, "PostLocalNews"}, | ||
| 13 | {20100, nullptr, "SetPassphrase"}, | ||
| 14 | {30100, D<&INewsService::GetSubscriptionStatus>, "GetSubscriptionStatus"}, | ||
| 15 | {30101, nullptr, "GetTopicList"}, | ||
| 16 | {30110, nullptr, "Unknown30110"}, | ||
| 17 | {30200, D<&INewsService::IsSystemUpdateRequired>, "IsSystemUpdateRequired"}, | ||
| 18 | {30201, nullptr, "Unknown30201"}, | ||
| 19 | {30210, nullptr, "Unknown30210"}, | ||
| 20 | {30300, nullptr, "RequestImmediateReception"}, | ||
| 21 | {30400, nullptr, "DecodeArchiveFile"}, | ||
| 22 | {30500, nullptr, "Unknown30500"}, | ||
| 23 | {30900, nullptr, "Unknown30900"}, | ||
| 24 | {30901, nullptr, "Unknown30901"}, | ||
| 25 | {30902, nullptr, "Unknown30902"}, | ||
| 26 | {40100, nullptr, "SetSubscriptionStatus"}, | ||
| 27 | {40101, D<&INewsService::RequestAutoSubscription>, "RequestAutoSubscription"}, | ||
| 28 | {40200, nullptr, "ClearStorage"}, | ||
| 29 | {40201, nullptr, "ClearSubscriptionStatusAll"}, | ||
| 30 | {90100, nullptr, "GetNewsDatabaseDump"}, | ||
| 31 | }; | ||
| 32 | // clang-format on | ||
| 33 | |||
| 34 | RegisterHandlers(functions); | ||
| 35 | } | ||
| 36 | |||
| 37 | INewsService::~INewsService() = default; | ||
| 38 | |||
| 39 | Result INewsService::GetSubscriptionStatus(Out<u32> out_status, | ||
| 40 | InBuffer<BufferAttr_HipcPointer> buffer_data) { | ||
| 41 | LOG_WARNING(Service_BCAT, "(STUBBED) called, buffer_size={}", buffer_data.size()); | ||
| 42 | *out_status = 0; | ||
| 43 | R_SUCCEED(); | ||
| 44 | } | ||
| 45 | |||
| 46 | Result INewsService::IsSystemUpdateRequired(Out<bool> out_is_system_update_required) { | ||
| 47 | LOG_WARNING(Service_BCAT, "(STUBBED) called"); | ||
| 48 | *out_is_system_update_required = false; | ||
| 49 | R_SUCCEED(); | ||
| 50 | } | ||
| 51 | |||
| 52 | Result INewsService::RequestAutoSubscription(u64 value) { | ||
| 53 | LOG_WARNING(Service_BCAT, "(STUBBED) called"); | ||
| 54 | R_SUCCEED(); | ||
| 55 | } | ||
| 56 | |||
| 57 | } // namespace Service::News | ||
diff --git a/src/core/hle/service/bcat/news/news_service.h b/src/core/hle/service/bcat/news/news_service.h new file mode 100644 index 000000000..f1716a302 --- /dev/null +++ b/src/core/hle/service/bcat/news/news_service.h | |||
| @@ -0,0 +1,28 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "core/hle/service/cmif_types.h" | ||
| 7 | #include "core/hle/service/service.h" | ||
| 8 | |||
| 9 | namespace Core { | ||
| 10 | class System; | ||
| 11 | } | ||
| 12 | |||
| 13 | namespace Service::News { | ||
| 14 | |||
| 15 | class INewsService final : public ServiceFramework<INewsService> { | ||
| 16 | public: | ||
| 17 | explicit INewsService(Core::System& system_); | ||
| 18 | ~INewsService() override; | ||
| 19 | |||
| 20 | private: | ||
| 21 | Result GetSubscriptionStatus(Out<u32> out_status, InBuffer<BufferAttr_HipcPointer> buffer_data); | ||
| 22 | |||
| 23 | Result IsSystemUpdateRequired(Out<bool> out_is_system_update_required); | ||
| 24 | |||
| 25 | Result RequestAutoSubscription(u64 value); | ||
| 26 | }; | ||
| 27 | |||
| 28 | } // namespace Service::News | ||
diff --git a/src/core/hle/service/bcat/news/overwrite_event_holder.cpp b/src/core/hle/service/bcat/news/overwrite_event_holder.cpp new file mode 100644 index 000000000..1712971e4 --- /dev/null +++ b/src/core/hle/service/bcat/news/overwrite_event_holder.cpp | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #include "core/hle/service/bcat/news/overwrite_event_holder.h" | ||
| 5 | #include "core/hle/service/cmif_serialization.h" | ||
| 6 | |||
| 7 | namespace Service::News { | ||
| 8 | |||
| 9 | IOverwriteEventHolder::IOverwriteEventHolder(Core::System& system_) | ||
| 10 | : ServiceFramework{system_, "IOverwriteEventHolder"}, service_context{system_, | ||
| 11 | "IOverwriteEventHolder"} { | ||
| 12 | // clang-format off | ||
| 13 | static const FunctionInfo functions[] = { | ||
| 14 | {0, D<&IOverwriteEventHolder::Get>, "Get"}, | ||
| 15 | }; | ||
| 16 | // clang-format on | ||
| 17 | |||
| 18 | RegisterHandlers(functions); | ||
| 19 | overwrite_event = service_context.CreateEvent("IOverwriteEventHolder::OverwriteEvent"); | ||
| 20 | } | ||
| 21 | |||
| 22 | IOverwriteEventHolder::~IOverwriteEventHolder() { | ||
| 23 | service_context.CloseEvent(overwrite_event); | ||
| 24 | } | ||
| 25 | |||
| 26 | Result IOverwriteEventHolder::Get(OutCopyHandle<Kernel::KReadableEvent> out_event) { | ||
| 27 | LOG_INFO(Service_BCAT, "called"); | ||
| 28 | |||
| 29 | *out_event = &overwrite_event->GetReadableEvent(); | ||
| 30 | R_SUCCEED(); | ||
| 31 | } | ||
| 32 | |||
| 33 | } // namespace Service::News | ||
diff --git a/src/core/hle/service/bcat/news/overwrite_event_holder.h b/src/core/hle/service/bcat/news/overwrite_event_holder.h new file mode 100644 index 000000000..cdc87d782 --- /dev/null +++ b/src/core/hle/service/bcat/news/overwrite_event_holder.h | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "core/hle/service/cmif_types.h" | ||
| 7 | #include "core/hle/service/kernel_helpers.h" | ||
| 8 | #include "core/hle/service/service.h" | ||
| 9 | |||
| 10 | namespace Core { | ||
| 11 | class System; | ||
| 12 | } | ||
| 13 | |||
| 14 | namespace Kernel { | ||
| 15 | class KEvent; | ||
| 16 | class KReadableEvent; | ||
| 17 | } // namespace Kernel | ||
| 18 | |||
| 19 | namespace Service::News { | ||
| 20 | |||
| 21 | class IOverwriteEventHolder final : public ServiceFramework<IOverwriteEventHolder> { | ||
| 22 | public: | ||
| 23 | explicit IOverwriteEventHolder(Core::System& system_); | ||
| 24 | ~IOverwriteEventHolder() override; | ||
| 25 | |||
| 26 | private: | ||
| 27 | Result Get(OutCopyHandle<Kernel::KReadableEvent> out_event); | ||
| 28 | |||
| 29 | Kernel::KEvent* overwrite_event; | ||
| 30 | KernelHelpers::ServiceContext service_context; | ||
| 31 | }; | ||
| 32 | |||
| 33 | } // namespace Service::News | ||
diff --git a/src/core/hle/service/bcat/news/service_creator.cpp b/src/core/hle/service/bcat/news/service_creator.cpp new file mode 100644 index 000000000..a1b22c004 --- /dev/null +++ b/src/core/hle/service/bcat/news/service_creator.cpp | |||
| @@ -0,0 +1,64 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #include "core/hle/service/bcat/news/newly_arrived_event_holder.h" | ||
| 5 | #include "core/hle/service/bcat/news/news_data_service.h" | ||
| 6 | #include "core/hle/service/bcat/news/news_database_service.h" | ||
| 7 | #include "core/hle/service/bcat/news/news_service.h" | ||
| 8 | #include "core/hle/service/bcat/news/overwrite_event_holder.h" | ||
| 9 | #include "core/hle/service/bcat/news/service_creator.h" | ||
| 10 | #include "core/hle/service/cmif_serialization.h" | ||
| 11 | |||
| 12 | namespace Service::News { | ||
| 13 | |||
| 14 | IServiceCreator::IServiceCreator(Core::System& system_, u32 permissions_, const char* name_) | ||
| 15 | : ServiceFramework{system_, name_}, permissions{permissions_} { | ||
| 16 | // clang-format off | ||
| 17 | static const FunctionInfo functions[] = { | ||
| 18 | {0, D<&IServiceCreator::CreateNewsService>, "CreateNewsService"}, | ||
| 19 | {1, D<&IServiceCreator::CreateNewlyArrivedEventHolder>, "CreateNewlyArrivedEventHolder"}, | ||
| 20 | {2, D<&IServiceCreator::CreateNewsDataService>, "CreateNewsDataService"}, | ||
| 21 | {3, D<&IServiceCreator::CreateNewsDatabaseService>, "CreateNewsDatabaseService"}, | ||
| 22 | {4, D<&IServiceCreator::CreateOverwriteEventHolder>, "CreateOverwriteEventHolder"}, | ||
| 23 | }; | ||
| 24 | // clang-format on | ||
| 25 | |||
| 26 | RegisterHandlers(functions); | ||
| 27 | } | ||
| 28 | |||
| 29 | IServiceCreator::~IServiceCreator() = default; | ||
| 30 | |||
| 31 | Result IServiceCreator::CreateNewsService(OutInterface<INewsService> out_interface) { | ||
| 32 | LOG_INFO(Service_BCAT, "called"); | ||
| 33 | *out_interface = std::make_shared<INewsService>(system); | ||
| 34 | R_SUCCEED(); | ||
| 35 | } | ||
| 36 | |||
| 37 | Result IServiceCreator::CreateNewlyArrivedEventHolder( | ||
| 38 | OutInterface<INewlyArrivedEventHolder> out_interface) { | ||
| 39 | LOG_INFO(Service_BCAT, "called"); | ||
| 40 | *out_interface = std::make_shared<INewlyArrivedEventHolder>(system); | ||
| 41 | R_SUCCEED(); | ||
| 42 | } | ||
| 43 | |||
| 44 | Result IServiceCreator::CreateNewsDataService(OutInterface<INewsDataService> out_interface) { | ||
| 45 | LOG_INFO(Service_BCAT, "called"); | ||
| 46 | *out_interface = std::make_shared<INewsDataService>(system); | ||
| 47 | R_SUCCEED(); | ||
| 48 | } | ||
| 49 | |||
| 50 | Result IServiceCreator::CreateNewsDatabaseService( | ||
| 51 | OutInterface<INewsDatabaseService> out_interface) { | ||
| 52 | LOG_INFO(Service_BCAT, "called"); | ||
| 53 | *out_interface = std::make_shared<INewsDatabaseService>(system); | ||
| 54 | R_SUCCEED(); | ||
| 55 | } | ||
| 56 | |||
| 57 | Result IServiceCreator::CreateOverwriteEventHolder( | ||
| 58 | OutInterface<IOverwriteEventHolder> out_interface) { | ||
| 59 | LOG_INFO(Service_BCAT, "called"); | ||
| 60 | *out_interface = std::make_shared<IOverwriteEventHolder>(system); | ||
| 61 | R_SUCCEED(); | ||
| 62 | } | ||
| 63 | |||
| 64 | } // namespace Service::News | ||
diff --git a/src/core/hle/service/bcat/news/service_creator.h b/src/core/hle/service/bcat/news/service_creator.h new file mode 100644 index 000000000..5a62e7c1a --- /dev/null +++ b/src/core/hle/service/bcat/news/service_creator.h | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "core/hle/service/cmif_types.h" | ||
| 7 | #include "core/hle/service/service.h" | ||
| 8 | |||
| 9 | namespace Core { | ||
| 10 | class System; | ||
| 11 | } | ||
| 12 | |||
| 13 | namespace Service::News { | ||
| 14 | class INewsService; | ||
| 15 | class INewlyArrivedEventHolder; | ||
| 16 | class INewsDataService; | ||
| 17 | class INewsDatabaseService; | ||
| 18 | class IOverwriteEventHolder; | ||
| 19 | |||
| 20 | class IServiceCreator final : public ServiceFramework<IServiceCreator> { | ||
| 21 | public: | ||
| 22 | explicit IServiceCreator(Core::System& system_, u32 permissions_, const char* name_); | ||
| 23 | ~IServiceCreator() override; | ||
| 24 | |||
| 25 | private: | ||
| 26 | Result CreateNewsService(OutInterface<INewsService> out_interface); | ||
| 27 | Result CreateNewlyArrivedEventHolder(OutInterface<INewlyArrivedEventHolder> out_interface); | ||
| 28 | Result CreateNewsDataService(OutInterface<INewsDataService> out_interface); | ||
| 29 | Result CreateNewsDatabaseService(OutInterface<INewsDatabaseService> out_interface); | ||
| 30 | Result CreateOverwriteEventHolder(OutInterface<IOverwriteEventHolder> out_interface); | ||
| 31 | |||
| 32 | u32 permissions; | ||
| 33 | }; | ||
| 34 | |||
| 35 | } // namespace Service::News | ||
diff --git a/src/core/hle/service/bcat/service_creator.cpp b/src/core/hle/service/bcat/service_creator.cpp new file mode 100644 index 000000000..ca339e5a6 --- /dev/null +++ b/src/core/hle/service/bcat/service_creator.cpp | |||
| @@ -0,0 +1,62 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/hle/service/bcat/bcat_service.h" | ||
| 5 | #include "core/hle/service/bcat/delivery_cache_storage_service.h" | ||
| 6 | #include "core/hle/service/bcat/service_creator.h" | ||
| 7 | #include "core/hle/service/cmif_serialization.h" | ||
| 8 | #include "core/hle/service/filesystem/filesystem.h" | ||
| 9 | |||
| 10 | namespace Service::BCAT { | ||
| 11 | |||
| 12 | std::unique_ptr<BcatBackend> CreateBackendFromSettings([[maybe_unused]] Core::System& system, | ||
| 13 | DirectoryGetter getter) { | ||
| 14 | return std::make_unique<NullBcatBackend>(std::move(getter)); | ||
| 15 | } | ||
| 16 | |||
| 17 | IServiceCreator::IServiceCreator(Core::System& system_, const char* name_) | ||
| 18 | : ServiceFramework{system_, name_}, fsc{system.GetFileSystemController()} { | ||
| 19 | // clang-format off | ||
| 20 | static const FunctionInfo functions[] = { | ||
| 21 | {0, D<&IServiceCreator::CreateBcatService>, "CreateBcatService"}, | ||
| 22 | {1, D<&IServiceCreator::CreateDeliveryCacheStorageService>, "CreateDeliveryCacheStorageService"}, | ||
| 23 | {2, D<&IServiceCreator::CreateDeliveryCacheStorageServiceWithApplicationId>, "CreateDeliveryCacheStorageServiceWithApplicationId"}, | ||
| 24 | {3, nullptr, "CreateDeliveryCacheProgressService"}, | ||
| 25 | {4, nullptr, "CreateDeliveryCacheProgressServiceWithApplicationId"}, | ||
| 26 | }; | ||
| 27 | // clang-format on | ||
| 28 | |||
| 29 | RegisterHandlers(functions); | ||
| 30 | |||
| 31 | backend = | ||
| 32 | CreateBackendFromSettings(system_, [this](u64 tid) { return fsc.GetBCATDirectory(tid); }); | ||
| 33 | } | ||
| 34 | |||
| 35 | IServiceCreator::~IServiceCreator() = default; | ||
| 36 | |||
| 37 | Result IServiceCreator::CreateBcatService(ClientProcessId process_id, | ||
| 38 | OutInterface<IBcatService> out_interface) { | ||
| 39 | LOG_INFO(Service_BCAT, "called, process_id={}", process_id.pid); | ||
| 40 | *out_interface = std::make_shared<IBcatService>(system, *backend); | ||
| 41 | R_SUCCEED(); | ||
| 42 | } | ||
| 43 | |||
| 44 | Result IServiceCreator::CreateDeliveryCacheStorageService( | ||
| 45 | ClientProcessId process_id, OutInterface<IDeliveryCacheStorageService> out_interface) { | ||
| 46 | LOG_INFO(Service_BCAT, "called, process_id={}", process_id.pid); | ||
| 47 | |||
| 48 | const auto title_id = system.GetApplicationProcessProgramID(); | ||
| 49 | *out_interface = | ||
| 50 | std::make_shared<IDeliveryCacheStorageService>(system, fsc.GetBCATDirectory(title_id)); | ||
| 51 | R_SUCCEED(); | ||
| 52 | } | ||
| 53 | |||
| 54 | Result IServiceCreator::CreateDeliveryCacheStorageServiceWithApplicationId( | ||
| 55 | u64 application_id, OutInterface<IDeliveryCacheStorageService> out_interface) { | ||
| 56 | LOG_DEBUG(Service_BCAT, "called, application_id={:016X}", application_id); | ||
| 57 | *out_interface = std::make_shared<IDeliveryCacheStorageService>( | ||
| 58 | system, fsc.GetBCATDirectory(application_id)); | ||
| 59 | R_SUCCEED(); | ||
| 60 | } | ||
| 61 | |||
| 62 | } // namespace Service::BCAT | ||
diff --git a/src/core/hle/service/bcat/service_creator.h b/src/core/hle/service/bcat/service_creator.h new file mode 100644 index 000000000..50e663324 --- /dev/null +++ b/src/core/hle/service/bcat/service_creator.h | |||
| @@ -0,0 +1,40 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "core/hle/service/cmif_types.h" | ||
| 7 | #include "core/hle/service/service.h" | ||
| 8 | |||
| 9 | namespace Core { | ||
| 10 | class System; | ||
| 11 | } | ||
| 12 | |||
| 13 | namespace Service::FileSystem { | ||
| 14 | class FileSystemController; | ||
| 15 | } | ||
| 16 | |||
| 17 | namespace Service::BCAT { | ||
| 18 | class BcatBackend; | ||
| 19 | class IBcatService; | ||
| 20 | class IDeliveryCacheStorageService; | ||
| 21 | |||
| 22 | class IServiceCreator final : public ServiceFramework<IServiceCreator> { | ||
| 23 | public: | ||
| 24 | explicit IServiceCreator(Core::System& system_, const char* name_); | ||
| 25 | ~IServiceCreator() override; | ||
| 26 | |||
| 27 | private: | ||
| 28 | Result CreateBcatService(ClientProcessId process_id, OutInterface<IBcatService> out_interface); | ||
| 29 | |||
| 30 | Result CreateDeliveryCacheStorageService( | ||
| 31 | ClientProcessId process_id, OutInterface<IDeliveryCacheStorageService> out_interface); | ||
| 32 | |||
| 33 | Result CreateDeliveryCacheStorageServiceWithApplicationId( | ||
| 34 | u64 application_id, OutInterface<IDeliveryCacheStorageService> out_interface); | ||
| 35 | |||
| 36 | std::unique_ptr<BcatBackend> backend; | ||
| 37 | Service::FileSystem::FileSystemController& fsc; | ||
| 38 | }; | ||
| 39 | |||
| 40 | } // namespace Service::BCAT | ||
diff --git a/src/core/hle/service/cmif_serialization.h b/src/core/hle/service/cmif_serialization.h index e985fe317..f24682c34 100644 --- a/src/core/hle/service/cmif_serialization.h +++ b/src/core/hle/service/cmif_serialization.h | |||
| @@ -280,7 +280,7 @@ void ReadInArgument(bool is_domain, CallArguments& args, const u8* raw_data, HLE | |||
| 280 | 280 | ||
| 281 | u32 value{}; | 281 | u32 value{}; |
| 282 | std::memcpy(&value, raw_data + ArgOffset, ArgSize); | 282 | std::memcpy(&value, raw_data + ArgOffset, ArgSize); |
| 283 | std::get<ArgIndex>(args) = ctx.GetDomainHandler<ArgType::Type>(value - 1); | 283 | std::get<ArgIndex>(args) = ctx.GetDomainHandler<typename ArgType::element_type>(value - 1); |
| 284 | 284 | ||
| 285 | return ReadInArgument<MethodArguments, CallArguments, ArgAlign, ArgEnd, HandleIndex, InBufferIndex, OutBufferIndex, true, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp); | 285 | return ReadInArgument<MethodArguments, CallArguments, ArgAlign, ArgEnd, HandleIndex, InBufferIndex, OutBufferIndex, true, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp); |
| 286 | } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InCopyHandle) { | 286 | } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InCopyHandle) { |
diff --git a/src/core/hle/service/cmif_types.h b/src/core/hle/service/cmif_types.h index 84f4c2456..dad358b87 100644 --- a/src/core/hle/service/cmif_types.h +++ b/src/core/hle/service/cmif_types.h | |||
| @@ -65,6 +65,14 @@ struct ClientProcessId { | |||
| 65 | }; | 65 | }; |
| 66 | 66 | ||
| 67 | struct ProcessId { | 67 | struct ProcessId { |
| 68 | explicit ProcessId() : pid() {} | ||
| 69 | explicit ProcessId(u64 p) : pid(p) {} | ||
| 70 | /* implicit */ ProcessId(const ClientProcessId& c) : pid(c.pid) {} | ||
| 71 | |||
| 72 | bool operator==(const ProcessId& rhs) const { | ||
| 73 | return pid == rhs.pid; | ||
| 74 | } | ||
| 75 | |||
| 68 | explicit operator bool() const { | 76 | explicit operator bool() const { |
| 69 | return pid != 0; | 77 | return pid != 0; |
| 70 | } | 78 | } |
| @@ -262,7 +270,7 @@ class OutLargeData { | |||
| 262 | public: | 270 | public: |
| 263 | static_assert(std::is_trivially_copyable_v<T>, "LargeData type must be trivially copyable"); | 271 | static_assert(std::is_trivially_copyable_v<T>, "LargeData type must be trivially copyable"); |
| 264 | static_assert((A & BufferAttr_In) == 0, "OutLargeData attr must not be In"); | 272 | static_assert((A & BufferAttr_In) == 0, "OutLargeData attr must not be In"); |
| 265 | static constexpr BufferAttr Attr = static_cast<BufferAttr>(A | BufferAttr_In | BufferAttr_FixedSize); | 273 | static constexpr BufferAttr Attr = static_cast<BufferAttr>(A | BufferAttr_Out | BufferAttr_FixedSize); |
| 266 | using Type = T; | 274 | using Type = T; |
| 267 | 275 | ||
| 268 | /* implicit */ OutLargeData(const OutLargeData& t) : raw(t.raw) {} | 276 | /* implicit */ OutLargeData(const OutLargeData& t) : raw(t.raw) {} |
| @@ -291,4 +299,4 @@ private: | |||
| 291 | }; | 299 | }; |
| 292 | // clang-format on | 300 | // clang-format on |
| 293 | 301 | ||
| 294 | } // namespace Service \ No newline at end of file | 302 | } // namespace Service |
diff --git a/src/core/hle/service/glue/time/worker.cpp b/src/core/hle/service/glue/time/worker.cpp index f44f3077e..8787f2dcd 100644 --- a/src/core/hle/service/glue/time/worker.cpp +++ b/src/core/hle/service/glue/time/worker.cpp | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include "core/hle/service/glue/time/file_timestamp_worker.h" | 7 | #include "core/hle/service/glue/time/file_timestamp_worker.h" |
| 8 | #include "core/hle/service/glue/time/standard_steady_clock_resource.h" | 8 | #include "core/hle/service/glue/time/standard_steady_clock_resource.h" |
| 9 | #include "core/hle/service/glue/time/worker.h" | 9 | #include "core/hle/service/glue/time/worker.h" |
| 10 | #include "core/hle/service/os/multi_wait_utils.h" | ||
| 10 | #include "core/hle/service/psc/time/common.h" | 11 | #include "core/hle/service/psc/time/common.h" |
| 11 | #include "core/hle/service/psc/time/service_manager.h" | 12 | #include "core/hle/service/psc/time/service_manager.h" |
| 12 | #include "core/hle/service/psc/time/static.h" | 13 | #include "core/hle/service/psc/time/static.h" |
| @@ -143,82 +144,46 @@ void TimeWorker::ThreadFunc(std::stop_token stop_token) { | |||
| 143 | Common::SetCurrentThreadName("TimeWorker"); | 144 | Common::SetCurrentThreadName("TimeWorker"); |
| 144 | Common::SetCurrentThreadPriority(Common::ThreadPriority::Low); | 145 | Common::SetCurrentThreadPriority(Common::ThreadPriority::Low); |
| 145 | 146 | ||
| 146 | enum class EventType { | ||
| 147 | Exit = 0, | ||
| 148 | IpmModuleService_GetEvent = 1, | ||
| 149 | PowerStateChange = 2, | ||
| 150 | SignalAlarms = 3, | ||
| 151 | UpdateLocalSystemClock = 4, | ||
| 152 | UpdateNetworkSystemClock = 5, | ||
| 153 | UpdateEphemeralSystemClock = 6, | ||
| 154 | UpdateSteadyClock = 7, | ||
| 155 | UpdateFileTimestamp = 8, | ||
| 156 | AutoCorrect = 9, | ||
| 157 | Max = 10, | ||
| 158 | }; | ||
| 159 | |||
| 160 | s32 num_objs{}; | ||
| 161 | std::array<Kernel::KSynchronizationObject*, static_cast<u32>(EventType::Max)> wait_objs{}; | ||
| 162 | std::array<EventType, static_cast<u32>(EventType::Max)> wait_indices{}; | ||
| 163 | |||
| 164 | const auto AddWaiter{ | ||
| 165 | [&](Kernel::KSynchronizationObject* synchronization_object, EventType type) { | ||
| 166 | // Open a new reference to the object. | ||
| 167 | synchronization_object->Open(); | ||
| 168 | |||
| 169 | // Insert into the list. | ||
| 170 | wait_indices[num_objs] = type; | ||
| 171 | wait_objs[num_objs++] = synchronization_object; | ||
| 172 | }}; | ||
| 173 | |||
| 174 | while (!stop_token.stop_requested()) { | 147 | while (!stop_token.stop_requested()) { |
| 175 | SCOPE_EXIT({ | 148 | enum class EventType : s32 { |
| 176 | for (s32 i = 0; i < num_objs; i++) { | 149 | Exit = 0, |
| 177 | wait_objs[i]->Close(); | 150 | PowerStateChange = 1, |
| 178 | } | 151 | SignalAlarms = 2, |
| 179 | }); | 152 | UpdateLocalSystemClock = 3, |
| 153 | UpdateNetworkSystemClock = 4, | ||
| 154 | UpdateEphemeralSystemClock = 5, | ||
| 155 | UpdateSteadyClock = 6, | ||
| 156 | UpdateFileTimestamp = 7, | ||
| 157 | AutoCorrect = 8, | ||
| 158 | }; | ||
| 159 | |||
| 160 | s32 index{}; | ||
| 180 | 161 | ||
| 181 | num_objs = {}; | ||
| 182 | wait_objs = {}; | ||
| 183 | if (m_pm_state_change_handler.m_priority != 0) { | 162 | if (m_pm_state_change_handler.m_priority != 0) { |
| 184 | AddWaiter(&m_event->GetReadableEvent(), EventType::Exit); | 163 | // TODO: gIPmModuleService::GetEvent() 1 |
| 185 | // TODO | 164 | index = WaitAny(m_system.Kernel(), |
| 186 | // AddWaiter(gIPmModuleService::GetEvent(), 1); | 165 | &m_event->GetReadableEvent(), // 0 |
| 187 | AddWaiter(&m_alarm_worker.GetEvent(), EventType::PowerStateChange); | 166 | &m_alarm_worker.GetEvent() // 1 |
| 167 | ); | ||
| 188 | } else { | 168 | } else { |
| 189 | AddWaiter(&m_event->GetReadableEvent(), EventType::Exit); | 169 | // TODO: gIPmModuleService::GetEvent() 1 |
| 190 | // TODO | 170 | index = WaitAny(m_system.Kernel(), |
| 191 | // AddWaiter(gIPmModuleService::GetEvent(), 1); | 171 | &m_event->GetReadableEvent(), // 0 |
| 192 | AddWaiter(&m_alarm_worker.GetEvent(), EventType::PowerStateChange); | 172 | &m_alarm_worker.GetEvent(), // 1 |
| 193 | AddWaiter(&m_alarm_worker.GetTimerEvent().GetReadableEvent(), EventType::SignalAlarms); | 173 | &m_alarm_worker.GetTimerEvent().GetReadableEvent(), // 2 |
| 194 | AddWaiter(m_local_clock_event, EventType::UpdateLocalSystemClock); | 174 | m_local_clock_event, // 3 |
| 195 | AddWaiter(m_network_clock_event, EventType::UpdateNetworkSystemClock); | 175 | m_network_clock_event, // 4 |
| 196 | AddWaiter(m_ephemeral_clock_event, EventType::UpdateEphemeralSystemClock); | 176 | m_ephemeral_clock_event, // 5 |
| 197 | AddWaiter(&m_timer_steady_clock->GetReadableEvent(), EventType::UpdateSteadyClock); | 177 | &m_timer_steady_clock->GetReadableEvent(), // 6 |
| 198 | AddWaiter(&m_timer_file_system->GetReadableEvent(), EventType::UpdateFileTimestamp); | 178 | &m_timer_file_system->GetReadableEvent(), // 7 |
| 199 | AddWaiter(m_standard_user_auto_correct_clock_event, EventType::AutoCorrect); | 179 | m_standard_user_auto_correct_clock_event // 8 |
| 180 | ); | ||
| 200 | } | 181 | } |
| 201 | 182 | ||
| 202 | s32 out_index{-1}; | 183 | switch (static_cast<EventType>(index)) { |
| 203 | Kernel::KSynchronizationObject::Wait(m_system.Kernel(), &out_index, wait_objs.data(), | ||
| 204 | num_objs, -1); | ||
| 205 | ASSERT(out_index >= 0 && out_index < num_objs); | ||
| 206 | |||
| 207 | if (stop_token.stop_requested()) { | ||
| 208 | return; | ||
| 209 | } | ||
| 210 | |||
| 211 | switch (wait_indices[out_index]) { | ||
| 212 | case EventType::Exit: | 184 | case EventType::Exit: |
| 213 | return; | 185 | return; |
| 214 | 186 | ||
| 215 | case EventType::IpmModuleService_GetEvent: | ||
| 216 | // TODO | ||
| 217 | // IPmModuleService::GetEvent() | ||
| 218 | // clear the event | ||
| 219 | // Handle power state change event | ||
| 220 | break; | ||
| 221 | |||
| 222 | case EventType::PowerStateChange: | 187 | case EventType::PowerStateChange: |
| 223 | m_alarm_worker.GetEvent().Clear(); | 188 | m_alarm_worker.GetEvent().Clear(); |
| 224 | if (m_pm_state_change_handler.m_priority <= 1) { | 189 | if (m_pm_state_change_handler.m_priority <= 1) { |
| @@ -235,19 +200,19 @@ void TimeWorker::ThreadFunc(std::stop_token stop_token) { | |||
| 235 | m_local_clock_event->Clear(); | 200 | m_local_clock_event->Clear(); |
| 236 | 201 | ||
| 237 | Service::PSC::Time::SystemClockContext context{}; | 202 | Service::PSC::Time::SystemClockContext context{}; |
| 238 | auto res = m_local_clock->GetSystemClockContext(&context); | 203 | R_ASSERT(m_local_clock->GetSystemClockContext(&context)); |
| 239 | ASSERT(res == ResultSuccess); | ||
| 240 | 204 | ||
| 241 | m_set_sys->SetUserSystemClockContext(context); | 205 | m_set_sys->SetUserSystemClockContext(context); |
| 242 | |||
| 243 | m_file_timestamp_worker.SetFilesystemPosixTime(); | 206 | m_file_timestamp_worker.SetFilesystemPosixTime(); |
| 244 | } break; | 207 | break; |
| 208 | } | ||
| 245 | 209 | ||
| 246 | case EventType::UpdateNetworkSystemClock: { | 210 | case EventType::UpdateNetworkSystemClock: { |
| 247 | m_network_clock_event->Clear(); | 211 | m_network_clock_event->Clear(); |
| 212 | |||
| 248 | Service::PSC::Time::SystemClockContext context{}; | 213 | Service::PSC::Time::SystemClockContext context{}; |
| 249 | auto res = m_network_clock->GetSystemClockContext(&context); | 214 | R_ASSERT(m_network_clock->GetSystemClockContext(&context)); |
| 250 | ASSERT(res == ResultSuccess); | 215 | |
| 251 | m_set_sys->SetNetworkSystemClockContext(context); | 216 | m_set_sys->SetNetworkSystemClockContext(context); |
| 252 | 217 | ||
| 253 | s64 time{}; | 218 | s64 time{}; |
| @@ -267,7 +232,8 @@ void TimeWorker::ThreadFunc(std::stop_token stop_token) { | |||
| 267 | } | 232 | } |
| 268 | 233 | ||
| 269 | m_file_timestamp_worker.SetFilesystemPosixTime(); | 234 | m_file_timestamp_worker.SetFilesystemPosixTime(); |
| 270 | } break; | 235 | break; |
| 236 | } | ||
| 271 | 237 | ||
| 272 | case EventType::UpdateEphemeralSystemClock: { | 238 | case EventType::UpdateEphemeralSystemClock: { |
| 273 | m_ephemeral_clock_event->Clear(); | 239 | m_ephemeral_clock_event->Clear(); |
| @@ -295,7 +261,8 @@ void TimeWorker::ThreadFunc(std::stop_token stop_token) { | |||
| 295 | if (!g_ig_report_ephemeral_clock_context_set) { | 261 | if (!g_ig_report_ephemeral_clock_context_set) { |
| 296 | g_ig_report_ephemeral_clock_context_set = true; | 262 | g_ig_report_ephemeral_clock_context_set = true; |
| 297 | } | 263 | } |
| 298 | } break; | 264 | break; |
| 265 | } | ||
| 299 | 266 | ||
| 300 | case EventType::UpdateSteadyClock: | 267 | case EventType::UpdateSteadyClock: |
| 301 | m_timer_steady_clock->Clear(); | 268 | m_timer_steady_clock->Clear(); |
| @@ -314,21 +281,20 @@ void TimeWorker::ThreadFunc(std::stop_token stop_token) { | |||
| 314 | m_standard_user_auto_correct_clock_event->Clear(); | 281 | m_standard_user_auto_correct_clock_event->Clear(); |
| 315 | 282 | ||
| 316 | bool automatic_correction{}; | 283 | bool automatic_correction{}; |
| 317 | auto res = m_time_sm->IsStandardUserSystemClockAutomaticCorrectionEnabled( | 284 | R_ASSERT(m_time_sm->IsStandardUserSystemClockAutomaticCorrectionEnabled( |
| 318 | &automatic_correction); | 285 | &automatic_correction)); |
| 319 | ASSERT(res == ResultSuccess); | ||
| 320 | 286 | ||
| 321 | Service::PSC::Time::SteadyClockTimePoint time_point{}; | 287 | Service::PSC::Time::SteadyClockTimePoint time_point{}; |
| 322 | res = m_time_sm->GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(&time_point); | 288 | R_ASSERT( |
| 323 | ASSERT(res == ResultSuccess); | 289 | m_time_sm->GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(&time_point)); |
| 324 | 290 | ||
| 325 | m_set_sys->SetUserSystemClockAutomaticCorrectionEnabled(automatic_correction); | 291 | m_set_sys->SetUserSystemClockAutomaticCorrectionEnabled(automatic_correction); |
| 326 | m_set_sys->SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point); | 292 | m_set_sys->SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point); |
| 327 | } break; | 293 | break; |
| 294 | } | ||
| 328 | 295 | ||
| 329 | default: | 296 | default: |
| 330 | UNREACHABLE(); | 297 | UNREACHABLE(); |
| 331 | break; | ||
| 332 | } | 298 | } |
| 333 | } | 299 | } |
| 334 | } | 300 | } |
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index b60fb9139..1fa9cfbfb 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp | |||
| @@ -36,7 +36,7 @@ void LoopProcess(Core::System& system) { | |||
| 36 | server_manager->RegisterNamedService( | 36 | server_manager->RegisterNamedService( |
| 37 | "hid:sys", std::make_shared<IHidSystemServer>(system, resource_manager, firmware_settings)); | 37 | "hid:sys", std::make_shared<IHidSystemServer>(system, resource_manager, firmware_settings)); |
| 38 | 38 | ||
| 39 | server_manager->RegisterNamedService("hidbus", std::make_shared<HidBus>(system)); | 39 | server_manager->RegisterNamedService("hidbus", std::make_shared<Hidbus>(system)); |
| 40 | 40 | ||
| 41 | server_manager->RegisterNamedService("irs", std::make_shared<IRS::IRS>(system)); | 41 | server_manager->RegisterNamedService("irs", std::make_shared<IRS::IRS>(system)); |
| 42 | server_manager->RegisterNamedService("irs:sys", std::make_shared<IRS::IRS_SYS>(system)); | 42 | server_manager->RegisterNamedService("irs:sys", std::make_shared<IRS::IRS_SYS>(system)); |
diff --git a/src/core/hle/service/hid/hid_debug_server.cpp b/src/core/hle/service/hid/hid_debug_server.cpp index 610af34dd..4e2663672 100644 --- a/src/core/hle/service/hid/hid_debug_server.cpp +++ b/src/core/hle/service/hid/hid_debug_server.cpp | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | #include <algorithm> | 4 | #include <algorithm> |
| 5 | 5 | ||
| 6 | #include "core/hle/service/cmif_serialization.h" | ||
| 6 | #include "core/hle/service/hid/hid_debug_server.h" | 7 | #include "core/hle/service/hid/hid_debug_server.h" |
| 7 | #include "core/hle/service/ipc_helpers.h" | 8 | #include "core/hle/service/ipc_helpers.h" |
| 8 | #include "hid_core/hid_types.h" | 9 | #include "hid_core/hid_types.h" |
| @@ -11,7 +12,6 @@ | |||
| 11 | 12 | ||
| 12 | #include "hid_core/resources/touch_screen/gesture.h" | 13 | #include "hid_core/resources/touch_screen/gesture.h" |
| 13 | #include "hid_core/resources/touch_screen/touch_screen.h" | 14 | #include "hid_core/resources/touch_screen/touch_screen.h" |
| 14 | #include "hid_core/resources/touch_screen/touch_types.h" | ||
| 15 | 15 | ||
| 16 | namespace Service::HID { | 16 | namespace Service::HID { |
| 17 | 17 | ||
| @@ -24,14 +24,14 @@ IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<Resource | |||
| 24 | {0, nullptr, "DeactivateDebugPad"}, | 24 | {0, nullptr, "DeactivateDebugPad"}, |
| 25 | {1, nullptr, "SetDebugPadAutoPilotState"}, | 25 | {1, nullptr, "SetDebugPadAutoPilotState"}, |
| 26 | {2, nullptr, "UnsetDebugPadAutoPilotState"}, | 26 | {2, nullptr, "UnsetDebugPadAutoPilotState"}, |
| 27 | {10, &IHidDebugServer::DeactivateTouchScreen, "DeactivateTouchScreen"}, | 27 | {10, C<&IHidDebugServer::DeactivateTouchScreen>, "DeactivateTouchScreen"}, |
| 28 | {11, &IHidDebugServer::SetTouchScreenAutoPilotState, "SetTouchScreenAutoPilotState"}, | 28 | {11, C<&IHidDebugServer::SetTouchScreenAutoPilotState>, "SetTouchScreenAutoPilotState"}, |
| 29 | {12, &IHidDebugServer::UnsetTouchScreenAutoPilotState, "UnsetTouchScreenAutoPilotState"}, | 29 | {12, C<&IHidDebugServer::UnsetTouchScreenAutoPilotState>, "UnsetTouchScreenAutoPilotState"}, |
| 30 | {13, &IHidDebugServer::GetTouchScreenConfiguration, "GetTouchScreenConfiguration"}, | 30 | {13, C<&IHidDebugServer::GetTouchScreenConfiguration>, "GetTouchScreenConfiguration"}, |
| 31 | {14, &IHidDebugServer::ProcessTouchScreenAutoTune, "ProcessTouchScreenAutoTune"}, | 31 | {14, C<&IHidDebugServer::ProcessTouchScreenAutoTune>, "ProcessTouchScreenAutoTune"}, |
| 32 | {15, &IHidDebugServer::ForceStopTouchScreenManagement, "ForceStopTouchScreenManagement"}, | 32 | {15, C<&IHidDebugServer::ForceStopTouchScreenManagement>, "ForceStopTouchScreenManagement"}, |
| 33 | {16, &IHidDebugServer::ForceRestartTouchScreenManagement, "ForceRestartTouchScreenManagement"}, | 33 | {16, C<&IHidDebugServer::ForceRestartTouchScreenManagement>, "ForceRestartTouchScreenManagement"}, |
| 34 | {17, &IHidDebugServer::IsTouchScreenManaged, "IsTouchScreenManaged"}, | 34 | {17, C<&IHidDebugServer::IsTouchScreenManaged>, "IsTouchScreenManaged"}, |
| 35 | {20, nullptr, "DeactivateMouse"}, | 35 | {20, nullptr, "DeactivateMouse"}, |
| 36 | {21, nullptr, "SetMouseAutoPilotState"}, | 36 | {21, nullptr, "SetMouseAutoPilotState"}, |
| 37 | {22, nullptr, "UnsetMouseAutoPilotState"}, | 37 | {22, nullptr, "UnsetMouseAutoPilotState"}, |
| @@ -47,7 +47,7 @@ IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<Resource | |||
| 47 | {60, nullptr, "ClearNpadSystemCommonPolicy"}, | 47 | {60, nullptr, "ClearNpadSystemCommonPolicy"}, |
| 48 | {61, nullptr, "DeactivateNpad"}, | 48 | {61, nullptr, "DeactivateNpad"}, |
| 49 | {62, nullptr, "ForceDisconnectNpad"}, | 49 | {62, nullptr, "ForceDisconnectNpad"}, |
| 50 | {91, &IHidDebugServer::DeactivateGesture, "DeactivateGesture"}, | 50 | {91, C<&IHidDebugServer::DeactivateGesture>, "DeactivateGesture"}, |
| 51 | {110, nullptr, "DeactivateHomeButton"}, | 51 | {110, nullptr, "DeactivateHomeButton"}, |
| 52 | {111, nullptr, "SetHomeButtonAutoPilotState"}, | 52 | {111, nullptr, "SetHomeButtonAutoPilotState"}, |
| 53 | {112, nullptr, "UnsetHomeButtonAutoPilotState"}, | 53 | {112, nullptr, "UnsetHomeButtonAutoPilotState"}, |
| @@ -160,169 +160,122 @@ IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<Resource | |||
| 160 | } | 160 | } |
| 161 | 161 | ||
| 162 | IHidDebugServer::~IHidDebugServer() = default; | 162 | IHidDebugServer::~IHidDebugServer() = default; |
| 163 | void IHidDebugServer::DeactivateTouchScreen(HLERequestContext& ctx) { | ||
| 164 | LOG_INFO(Service_HID, "called"); | ||
| 165 | 163 | ||
| 166 | Result result = ResultSuccess; | 164 | Result IHidDebugServer::DeactivateTouchScreen() { |
| 165 | LOG_INFO(Service_HID, "called"); | ||
| 167 | 166 | ||
| 168 | if (!firmware_settings->IsDeviceManaged()) { | 167 | if (!firmware_settings->IsDeviceManaged()) { |
| 169 | result = GetResourceManager()->GetTouchScreen()->Deactivate(); | 168 | R_RETURN(GetResourceManager()->GetTouchScreen()->Deactivate()); |
| 170 | } | 169 | } |
| 171 | 170 | ||
| 172 | IPC::ResponseBuilder rb{ctx, 2}; | 171 | R_SUCCEED(); |
| 173 | rb.Push(result); | ||
| 174 | } | 172 | } |
| 175 | 173 | ||
| 176 | void IHidDebugServer::SetTouchScreenAutoPilotState(HLERequestContext& ctx) { | 174 | Result IHidDebugServer::SetTouchScreenAutoPilotState( |
| 175 | InArray<TouchState, BufferAttr_HipcMapAlias> auto_pilot_buffer) { | ||
| 177 | AutoPilotState auto_pilot{}; | 176 | AutoPilotState auto_pilot{}; |
| 178 | auto_pilot.count = ctx.GetReadBufferNumElements<TouchState>(); | ||
| 179 | const auto buffer = ctx.ReadBuffer(); | ||
| 180 | 177 | ||
| 181 | auto_pilot.count = std::min(auto_pilot.count, static_cast<u64>(auto_pilot.state.size())); | 178 | auto_pilot.count = |
| 182 | memcpy(auto_pilot.state.data(), buffer.data(), auto_pilot.count * sizeof(TouchState)); | 179 | static_cast<u64>(std::min(auto_pilot_buffer.size(), auto_pilot.state.size())); |
| 180 | memcpy(auto_pilot.state.data(), auto_pilot_buffer.data(), | ||
| 181 | auto_pilot.count * sizeof(TouchState)); | ||
| 183 | 182 | ||
| 184 | LOG_INFO(Service_HID, "called, auto_pilot_count={}", auto_pilot.count); | 183 | LOG_INFO(Service_HID, "called, auto_pilot_count={}", auto_pilot.count); |
| 185 | 184 | ||
| 186 | const Result result = | 185 | R_RETURN(GetResourceManager()->GetTouchScreen()->SetTouchScreenAutoPilotState(auto_pilot)); |
| 187 | GetResourceManager()->GetTouchScreen()->SetTouchScreenAutoPilotState(auto_pilot); | ||
| 188 | |||
| 189 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 190 | rb.Push(result); | ||
| 191 | } | 186 | } |
| 192 | 187 | ||
| 193 | void IHidDebugServer::UnsetTouchScreenAutoPilotState(HLERequestContext& ctx) { | 188 | Result IHidDebugServer::UnsetTouchScreenAutoPilotState() { |
| 194 | LOG_INFO(Service_HID, "called"); | 189 | LOG_INFO(Service_HID, "called"); |
| 195 | 190 | R_RETURN(GetResourceManager()->GetTouchScreen()->UnsetTouchScreenAutoPilotState()); | |
| 196 | const Result result = GetResourceManager()->GetTouchScreen()->UnsetTouchScreenAutoPilotState(); | ||
| 197 | |||
| 198 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 199 | rb.Push(result); | ||
| 200 | } | 191 | } |
| 201 | 192 | ||
| 202 | void IHidDebugServer::GetTouchScreenConfiguration(HLERequestContext& ctx) { | 193 | Result IHidDebugServer::GetTouchScreenConfiguration( |
| 203 | IPC::RequestParser rp{ctx}; | 194 | Out<Core::HID::TouchScreenConfigurationForNx> out_touchscreen_config, |
| 204 | const auto applet_resource_user_id{rp.Pop<u64>()}; | 195 | ClientAppletResourceUserId aruid) { |
| 205 | 196 | LOG_INFO(Service_HID, "called, applet_resource_user_id={}", aruid.pid); | |
| 206 | LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | ||
| 207 | 197 | ||
| 208 | Core::HID::TouchScreenConfigurationForNx touchscreen_config{}; | 198 | R_TRY(GetResourceManager()->GetTouchScreen()->GetTouchScreenConfiguration( |
| 209 | const Result result = GetResourceManager()->GetTouchScreen()->GetTouchScreenConfiguration( | 199 | *out_touchscreen_config, aruid.pid)); |
| 210 | touchscreen_config, applet_resource_user_id); | ||
| 211 | 200 | ||
| 212 | if (touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Heat2 && | 201 | if (out_touchscreen_config->mode != Core::HID::TouchScreenModeForNx::Heat2 && |
| 213 | touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Finger) { | 202 | out_touchscreen_config->mode != Core::HID::TouchScreenModeForNx::Finger) { |
| 214 | touchscreen_config.mode = Core::HID::TouchScreenModeForNx::UseSystemSetting; | 203 | out_touchscreen_config->mode = Core::HID::TouchScreenModeForNx::UseSystemSetting; |
| 215 | } | 204 | } |
| 216 | 205 | ||
| 217 | IPC::ResponseBuilder rb{ctx, 6}; | 206 | R_SUCCEED(); |
| 218 | rb.Push(result); | ||
| 219 | rb.PushRaw(touchscreen_config); | ||
| 220 | } | 207 | } |
| 221 | 208 | ||
| 222 | void IHidDebugServer::ProcessTouchScreenAutoTune(HLERequestContext& ctx) { | 209 | Result IHidDebugServer::ProcessTouchScreenAutoTune() { |
| 223 | LOG_INFO(Service_HID, "called"); | 210 | LOG_INFO(Service_HID, "called"); |
| 224 | 211 | R_RETURN(GetResourceManager()->GetTouchScreen()->ProcessTouchScreenAutoTune()); | |
| 225 | Result result = GetResourceManager()->GetTouchScreen()->ProcessTouchScreenAutoTune(); | ||
| 226 | |||
| 227 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 228 | rb.Push(result); | ||
| 229 | } | 212 | } |
| 230 | 213 | ||
| 231 | void IHidDebugServer::ForceStopTouchScreenManagement(HLERequestContext& ctx) { | 214 | Result IHidDebugServer::ForceStopTouchScreenManagement() { |
| 232 | LOG_INFO(Service_HID, "called"); | 215 | LOG_INFO(Service_HID, "called"); |
| 233 | 216 | ||
| 234 | if (!firmware_settings->IsDeviceManaged()) { | 217 | if (!firmware_settings->IsDeviceManaged()) { |
| 235 | IPC::ResponseBuilder rb{ctx, 2}; | 218 | R_SUCCEED(); |
| 236 | rb.Push(ResultSuccess); | ||
| 237 | return; | ||
| 238 | } | 219 | } |
| 239 | 220 | ||
| 240 | Result result = ResultSuccess; | ||
| 241 | bool is_touch_active{}; | ||
| 242 | bool is_gesture_active{}; | ||
| 243 | auto touch_screen = GetResourceManager()->GetTouchScreen(); | 221 | auto touch_screen = GetResourceManager()->GetTouchScreen(); |
| 244 | auto gesture = GetResourceManager()->GetGesture(); | 222 | auto gesture = GetResourceManager()->GetGesture(); |
| 245 | 223 | ||
| 246 | if (firmware_settings->IsTouchI2cManaged()) { | 224 | if (firmware_settings->IsTouchI2cManaged()) { |
| 247 | result = touch_screen->IsActive(is_touch_active); | 225 | bool is_touch_active{}; |
| 248 | if (result.IsSuccess()) { | 226 | bool is_gesture_active{}; |
| 249 | result = gesture->IsActive(is_gesture_active); | 227 | R_TRY(touch_screen->IsActive(is_touch_active)); |
| 250 | } | 228 | R_TRY(gesture->IsActive(is_gesture_active)); |
| 251 | if (result.IsSuccess() && is_touch_active) { | 229 | |
| 252 | result = touch_screen->Deactivate(); | 230 | if (is_touch_active) { |
| 231 | R_TRY(touch_screen->Deactivate()); | ||
| 253 | } | 232 | } |
| 254 | if (result.IsSuccess() && is_gesture_active) { | 233 | if (is_gesture_active) { |
| 255 | result = gesture->Deactivate(); | 234 | R_TRY(gesture->Deactivate()); |
| 256 | } | 235 | } |
| 257 | } | 236 | } |
| 258 | 237 | ||
| 259 | IPC::ResponseBuilder rb{ctx, 2}; | 238 | R_SUCCEED(); |
| 260 | rb.Push(result); | ||
| 261 | } | 239 | } |
| 262 | 240 | ||
| 263 | void IHidDebugServer::ForceRestartTouchScreenManagement(HLERequestContext& ctx) { | 241 | Result IHidDebugServer::ForceRestartTouchScreenManagement(u32 basic_gesture_id, |
| 264 | IPC::RequestParser rp{ctx}; | 242 | ClientAppletResourceUserId aruid) { |
| 265 | struct Parameters { | ||
| 266 | u32 basic_gesture_id; | ||
| 267 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 268 | u64 applet_resource_user_id; | ||
| 269 | }; | ||
| 270 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 271 | |||
| 272 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 273 | |||
| 274 | LOG_INFO(Service_HID, "called, basic_gesture_id={}, applet_resource_user_id={}", | 243 | LOG_INFO(Service_HID, "called, basic_gesture_id={}, applet_resource_user_id={}", |
| 275 | parameters.basic_gesture_id, parameters.applet_resource_user_id); | 244 | basic_gesture_id, aruid.pid); |
| 276 | 245 | ||
| 277 | Result result = ResultSuccess; | ||
| 278 | auto touch_screen = GetResourceManager()->GetTouchScreen(); | 246 | auto touch_screen = GetResourceManager()->GetTouchScreen(); |
| 279 | auto gesture = GetResourceManager()->GetGesture(); | 247 | auto gesture = GetResourceManager()->GetGesture(); |
| 280 | 248 | ||
| 281 | if (firmware_settings->IsDeviceManaged() && firmware_settings->IsTouchI2cManaged()) { | 249 | if (firmware_settings->IsDeviceManaged() && firmware_settings->IsTouchI2cManaged()) { |
| 282 | result = gesture->Activate(); | 250 | R_TRY(gesture->Activate()); |
| 283 | if (result.IsSuccess()) { | 251 | R_TRY(gesture->Activate(aruid.pid, basic_gesture_id)); |
| 284 | result = | 252 | R_TRY(touch_screen->Activate()); |
| 285 | gesture->Activate(parameters.applet_resource_user_id, parameters.basic_gesture_id); | 253 | R_TRY(touch_screen->Activate(aruid.pid)); |
| 286 | } | ||
| 287 | if (result.IsSuccess()) { | ||
| 288 | result = touch_screen->Activate(); | ||
| 289 | } | ||
| 290 | if (result.IsSuccess()) { | ||
| 291 | result = touch_screen->Activate(parameters.applet_resource_user_id); | ||
| 292 | } | ||
| 293 | } | 254 | } |
| 294 | 255 | ||
| 295 | IPC::ResponseBuilder rb{ctx, 2}; | 256 | R_SUCCEED(); |
| 296 | rb.Push(result); | ||
| 297 | } | 257 | } |
| 298 | 258 | ||
| 299 | void IHidDebugServer::IsTouchScreenManaged(HLERequestContext& ctx) { | 259 | Result IHidDebugServer::IsTouchScreenManaged(Out<bool> out_is_managed) { |
| 300 | LOG_INFO(Service_HID, "called"); | 260 | LOG_INFO(Service_HID, "called"); |
| 301 | 261 | ||
| 302 | bool is_touch_active{}; | 262 | bool is_touch_active{}; |
| 303 | bool is_gesture_active{}; | 263 | bool is_gesture_active{}; |
| 264 | R_TRY(GetResourceManager()->GetTouchScreen()->IsActive(is_touch_active)); | ||
| 265 | R_TRY(GetResourceManager()->GetGesture()->IsActive(is_gesture_active)); | ||
| 304 | 266 | ||
| 305 | Result result = GetResourceManager()->GetTouchScreen()->IsActive(is_touch_active); | 267 | *out_is_managed = is_touch_active || is_gesture_active; |
| 306 | if (result.IsSuccess()) { | 268 | R_SUCCEED(); |
| 307 | result = GetResourceManager()->GetGesture()->IsActive(is_gesture_active); | ||
| 308 | } | ||
| 309 | |||
| 310 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 311 | rb.Push(result); | ||
| 312 | rb.Push(is_touch_active | is_gesture_active); | ||
| 313 | } | 269 | } |
| 314 | 270 | ||
| 315 | void IHidDebugServer::DeactivateGesture(HLERequestContext& ctx) { | 271 | Result IHidDebugServer::DeactivateGesture() { |
| 316 | LOG_INFO(Service_HID, "called"); | 272 | LOG_INFO(Service_HID, "called"); |
| 317 | 273 | ||
| 318 | Result result = ResultSuccess; | ||
| 319 | |||
| 320 | if (!firmware_settings->IsDeviceManaged()) { | 274 | if (!firmware_settings->IsDeviceManaged()) { |
| 321 | result = GetResourceManager()->GetGesture()->Deactivate(); | 275 | R_RETURN(GetResourceManager()->GetGesture()->Deactivate()); |
| 322 | } | 276 | } |
| 323 | 277 | ||
| 324 | IPC::ResponseBuilder rb{ctx, 2}; | 278 | R_SUCCEED(); |
| 325 | rb.Push(result); | ||
| 326 | } | 279 | } |
| 327 | 280 | ||
| 328 | std::shared_ptr<ResourceManager> IHidDebugServer::GetResourceManager() { | 281 | std::shared_ptr<ResourceManager> IHidDebugServer::GetResourceManager() { |
diff --git a/src/core/hle/service/hid/hid_debug_server.h b/src/core/hle/service/hid/hid_debug_server.h index 7d5b082b3..3a483f07e 100644 --- a/src/core/hle/service/hid/hid_debug_server.h +++ b/src/core/hle/service/hid/hid_debug_server.h | |||
| @@ -3,7 +3,9 @@ | |||
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include "core/hle/service/cmif_types.h" | ||
| 6 | #include "core/hle/service/service.h" | 7 | #include "core/hle/service/service.h" |
| 8 | #include "hid_core/resources/touch_screen/touch_types.h" | ||
| 7 | 9 | ||
| 8 | namespace Core { | 10 | namespace Core { |
| 9 | class System; | 11 | class System; |
| @@ -20,15 +22,19 @@ public: | |||
| 20 | ~IHidDebugServer() override; | 22 | ~IHidDebugServer() override; |
| 21 | 23 | ||
| 22 | private: | 24 | private: |
| 23 | void DeactivateTouchScreen(HLERequestContext& ctx); | 25 | Result DeactivateTouchScreen(); |
| 24 | void SetTouchScreenAutoPilotState(HLERequestContext& ctx); | 26 | Result SetTouchScreenAutoPilotState( |
| 25 | void UnsetTouchScreenAutoPilotState(HLERequestContext& ctx); | 27 | InArray<TouchState, BufferAttr_HipcMapAlias> auto_pilot_buffer); |
| 26 | void GetTouchScreenConfiguration(HLERequestContext& ctx); | 28 | Result UnsetTouchScreenAutoPilotState(); |
| 27 | void ProcessTouchScreenAutoTune(HLERequestContext& ctx); | 29 | Result GetTouchScreenConfiguration( |
| 28 | void ForceStopTouchScreenManagement(HLERequestContext& ctx); | 30 | Out<Core::HID::TouchScreenConfigurationForNx> out_touchscreen_config, |
| 29 | void ForceRestartTouchScreenManagement(HLERequestContext& ctx); | 31 | ClientAppletResourceUserId aruid); |
| 30 | void IsTouchScreenManaged(HLERequestContext& ctx); | 32 | Result ProcessTouchScreenAutoTune(); |
| 31 | void DeactivateGesture(HLERequestContext& ctx); | 33 | Result ForceStopTouchScreenManagement(); |
| 34 | Result ForceRestartTouchScreenManagement(u32 basic_gesture_id, | ||
| 35 | ClientAppletResourceUserId aruid); | ||
| 36 | Result IsTouchScreenManaged(Out<bool> out_is_managed); | ||
| 37 | Result DeactivateGesture(); | ||
| 32 | 38 | ||
| 33 | std::shared_ptr<ResourceManager> GetResourceManager(); | 39 | std::shared_ptr<ResourceManager> GetResourceManager(); |
| 34 | 40 | ||
diff --git a/src/core/hle/service/hid/hid_system_server.cpp b/src/core/hle/service/hid/hid_system_server.cpp index 22471e9e2..7126a1dcd 100644 --- a/src/core/hle/service/hid/hid_system_server.cpp +++ b/src/core/hle/service/hid/hid_system_server.cpp | |||
| @@ -508,13 +508,8 @@ void IHidSystemServer::RegisterAppletResourceUserId(HLERequestContext& ctx) { | |||
| 508 | Result result = GetResourceManager()->RegisterAppletResourceUserId( | 508 | Result result = GetResourceManager()->RegisterAppletResourceUserId( |
| 509 | parameters.applet_resource_user_id, parameters.enable_input); | 509 | parameters.applet_resource_user_id, parameters.enable_input); |
| 510 | 510 | ||
| 511 | if (result.IsSuccess()) { | ||
| 512 | // result = GetResourceManager()->GetNpad()->RegisterAppletResourceUserId( | ||
| 513 | // parameters.applet_resource_user_id); | ||
| 514 | } | ||
| 515 | |||
| 516 | IPC::ResponseBuilder rb{ctx, 2}; | 511 | IPC::ResponseBuilder rb{ctx, 2}; |
| 517 | rb.Push(ResultSuccess); | 512 | rb.Push(result); |
| 518 | } | 513 | } |
| 519 | 514 | ||
| 520 | void IHidSystemServer::UnregisterAppletResourceUserId(HLERequestContext& ctx) { | 515 | void IHidSystemServer::UnregisterAppletResourceUserId(HLERequestContext& ctx) { |
| @@ -524,8 +519,6 @@ void IHidSystemServer::UnregisterAppletResourceUserId(HLERequestContext& ctx) { | |||
| 524 | LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | 519 | LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); |
| 525 | 520 | ||
| 526 | GetResourceManager()->UnregisterAppletResourceUserId(applet_resource_user_id); | 521 | GetResourceManager()->UnregisterAppletResourceUserId(applet_resource_user_id); |
| 527 | // GetResourceManager()->GetNpad()->UnregisterAppletResourceUserId(applet_resource_user_id); | ||
| 528 | // GetResourceManager()->GetPalma()->UnregisterAppletResourceUserId(applet_resource_user_id); | ||
| 529 | 522 | ||
| 530 | IPC::ResponseBuilder rb{ctx, 2}; | 523 | IPC::ResponseBuilder rb{ctx, 2}; |
| 531 | rb.Push(ResultSuccess); | 524 | rb.Push(ResultSuccess); |
diff --git a/src/core/hle/service/hid/hidbus.cpp b/src/core/hle/service/hid/hidbus.cpp index c903ee8b8..4fb002bc4 100644 --- a/src/core/hle/service/hid/hidbus.cpp +++ b/src/core/hle/service/hid/hidbus.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include "core/hle/kernel/k_readable_event.h" | 9 | #include "core/hle/kernel/k_readable_event.h" |
| 10 | #include "core/hle/kernel/k_shared_memory.h" | 10 | #include "core/hle/kernel/k_shared_memory.h" |
| 11 | #include "core/hle/kernel/k_transfer_memory.h" | 11 | #include "core/hle/kernel/k_transfer_memory.h" |
| 12 | #include "core/hle/service/cmif_serialization.h" | ||
| 12 | #include "core/hle/service/hid/hidbus.h" | 13 | #include "core/hle/service/hid/hidbus.h" |
| 13 | #include "core/hle/service/ipc_helpers.h" | 14 | #include "core/hle/service/ipc_helpers.h" |
| 14 | #include "core/hle/service/service.h" | 15 | #include "core/hle/service/service.h" |
| @@ -22,25 +23,25 @@ namespace Service::HID { | |||
| 22 | // (15ms, 66Hz) | 23 | // (15ms, 66Hz) |
| 23 | constexpr auto hidbus_update_ns = std::chrono::nanoseconds{15 * 1000 * 1000}; | 24 | constexpr auto hidbus_update_ns = std::chrono::nanoseconds{15 * 1000 * 1000}; |
| 24 | 25 | ||
| 25 | HidBus::HidBus(Core::System& system_) | 26 | Hidbus::Hidbus(Core::System& system_) |
| 26 | : ServiceFramework{system_, "hidbus"}, service_context{system_, service_name} { | 27 | : ServiceFramework{system_, "hidbus"}, service_context{system_, service_name} { |
| 27 | 28 | ||
| 28 | // clang-format off | 29 | // clang-format off |
| 29 | static const FunctionInfo functions[] = { | 30 | static const FunctionInfo functions[] = { |
| 30 | {1, &HidBus::GetBusHandle, "GetBusHandle"}, | 31 | {1, C<&Hidbus::GetBusHandle>, "GetBusHandle"}, |
| 31 | {2, &HidBus::IsExternalDeviceConnected, "IsExternalDeviceConnected"}, | 32 | {2, C<&Hidbus::IsExternalDeviceConnected>, "IsExternalDeviceConnected"}, |
| 32 | {3, &HidBus::Initialize, "Initialize"}, | 33 | {3, C<&Hidbus::Initialize>, "Initialize"}, |
| 33 | {4, &HidBus::Finalize, "Finalize"}, | 34 | {4, C<&Hidbus::Finalize>, "Finalize"}, |
| 34 | {5, &HidBus::EnableExternalDevice, "EnableExternalDevice"}, | 35 | {5, C<&Hidbus::EnableExternalDevice>, "EnableExternalDevice"}, |
| 35 | {6, &HidBus::GetExternalDeviceId, "GetExternalDeviceId"}, | 36 | {6, C<&Hidbus::GetExternalDeviceId>, "GetExternalDeviceId"}, |
| 36 | {7, &HidBus::SendCommandAsync, "SendCommandAsync"}, | 37 | {7, C<&Hidbus::SendCommandAsync>, "SendCommandAsync"}, |
| 37 | {8, &HidBus::GetSendCommandAsynceResult, "GetSendCommandAsynceResult"}, | 38 | {8, C<&Hidbus::GetSendCommandAsynceResult>, "GetSendCommandAsynceResult"}, |
| 38 | {9, &HidBus::SetEventForSendCommandAsycResult, "SetEventForSendCommandAsycResult"}, | 39 | {9, C<&Hidbus::SetEventForSendCommandAsycResult>, "SetEventForSendCommandAsycResult"}, |
| 39 | {10, &HidBus::GetSharedMemoryHandle, "GetSharedMemoryHandle"}, | 40 | {10, C<&Hidbus::GetSharedMemoryHandle>, "GetSharedMemoryHandle"}, |
| 40 | {11, &HidBus::EnableJoyPollingReceiveMode, "EnableJoyPollingReceiveMode"}, | 41 | {11, C<&Hidbus::EnableJoyPollingReceiveMode>, "EnableJoyPollingReceiveMode"}, |
| 41 | {12, &HidBus::DisableJoyPollingReceiveMode, "DisableJoyPollingReceiveMode"}, | 42 | {12, C<&Hidbus::DisableJoyPollingReceiveMode>, "DisableJoyPollingReceiveMode"}, |
| 42 | {13, nullptr, "GetPollingData"}, | 43 | {13, nullptr, "GetPollingData"}, |
| 43 | {14, &HidBus::SetStatusManagerType, "SetStatusManagerType"}, | 44 | {14, C<&Hidbus::SetStatusManagerType>, "SetStatusManagerType"}, |
| 44 | }; | 45 | }; |
| 45 | // clang-format on | 46 | // clang-format on |
| 46 | 47 | ||
| @@ -60,11 +61,11 @@ HidBus::HidBus(Core::System& system_) | |||
| 60 | hidbus_update_event); | 61 | hidbus_update_event); |
| 61 | } | 62 | } |
| 62 | 63 | ||
| 63 | HidBus::~HidBus() { | 64 | Hidbus::~Hidbus() { |
| 64 | system.CoreTiming().UnscheduleEvent(hidbus_update_event); | 65 | system.CoreTiming().UnscheduleEvent(hidbus_update_event); |
| 65 | } | 66 | } |
| 66 | 67 | ||
| 67 | void HidBus::UpdateHidbus(std::chrono::nanoseconds ns_late) { | 68 | void Hidbus::UpdateHidbus(std::chrono::nanoseconds ns_late) { |
| 68 | if (is_hidbus_enabled) { | 69 | if (is_hidbus_enabled) { |
| 69 | for (std::size_t i = 0; i < devices.size(); ++i) { | 70 | for (std::size_t i = 0; i < devices.size(); ++i) { |
| 70 | if (!devices[i].is_device_initialized) { | 71 | if (!devices[i].is_device_initialized) { |
| @@ -84,7 +85,7 @@ void HidBus::UpdateHidbus(std::chrono::nanoseconds ns_late) { | |||
| 84 | } | 85 | } |
| 85 | } | 86 | } |
| 86 | 87 | ||
| 87 | std::optional<std::size_t> HidBus::GetDeviceIndexFromHandle(BusHandle handle) const { | 88 | std::optional<std::size_t> Hidbus::GetDeviceIndexFromHandle(BusHandle handle) const { |
| 88 | for (std::size_t i = 0; i < devices.size(); ++i) { | 89 | for (std::size_t i = 0; i < devices.size(); ++i) { |
| 89 | const auto& device_handle = devices[i].handle; | 90 | const auto& device_handle = devices[i].handle; |
| 90 | if (handle.abstracted_pad_id == device_handle.abstracted_pad_id && | 91 | if (handle.abstracted_pad_id == device_handle.abstracted_pad_id && |
| @@ -98,20 +99,11 @@ std::optional<std::size_t> HidBus::GetDeviceIndexFromHandle(BusHandle handle) co | |||
| 98 | return std::nullopt; | 99 | return std::nullopt; |
| 99 | } | 100 | } |
| 100 | 101 | ||
| 101 | void HidBus::GetBusHandle(HLERequestContext& ctx) { | 102 | Result Hidbus::GetBusHandle(Out<bool> out_is_valid, Out<BusHandle> out_bus_handle, |
| 102 | IPC::RequestParser rp{ctx}; | 103 | Core::HID::NpadIdType npad_id, BusType bus_type, |
| 103 | struct Parameters { | 104 | AppletResourceUserId aruid) { |
| 104 | Core::HID::NpadIdType npad_id; | 105 | LOG_INFO(Service_HID, "called, npad_id={}, bus_type={}, applet_resource_user_id={}", npad_id, |
| 105 | INSERT_PADDING_WORDS_NOINIT(1); | 106 | bus_type, aruid.pid); |
| 106 | BusType bus_type; | ||
| 107 | u64 applet_resource_user_id; | ||
| 108 | }; | ||
| 109 | static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); | ||
| 110 | |||
| 111 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 112 | |||
| 113 | LOG_INFO(Service_HID, "called, npad_id={}, bus_type={}, applet_resource_user_id={}", | ||
| 114 | parameters.npad_id, parameters.bus_type, parameters.applet_resource_user_id); | ||
| 115 | 107 | ||
| 116 | bool is_handle_found = 0; | 108 | bool is_handle_found = 0; |
| 117 | std::size_t handle_index = 0; | 109 | std::size_t handle_index = 0; |
| @@ -121,8 +113,8 @@ void HidBus::GetBusHandle(HLERequestContext& ctx) { | |||
| 121 | if (!handle.is_valid) { | 113 | if (!handle.is_valid) { |
| 122 | continue; | 114 | continue; |
| 123 | } | 115 | } |
| 124 | if (static_cast<Core::HID::NpadIdType>(handle.player_number) == parameters.npad_id && | 116 | if (handle.player_number.As<Core::HID::NpadIdType>() == npad_id && |
| 125 | handle.bus_type_id == static_cast<u8>(parameters.bus_type)) { | 117 | handle.bus_type_id == static_cast<u8>(bus_type)) { |
| 126 | is_handle_found = true; | 118 | is_handle_found = true; |
| 127 | handle_index = i; | 119 | handle_index = i; |
| 128 | break; | 120 | break; |
| @@ -135,388 +127,231 @@ void HidBus::GetBusHandle(HLERequestContext& ctx) { | |||
| 135 | if (devices[i].handle.is_valid) { | 127 | if (devices[i].handle.is_valid) { |
| 136 | continue; | 128 | continue; |
| 137 | } | 129 | } |
| 138 | devices[i].handle = { | 130 | devices[i].handle.raw = 0; |
| 139 | .abstracted_pad_id = static_cast<u8>(i), | 131 | devices[i].handle.abstracted_pad_id.Assign(i); |
| 140 | .internal_index = static_cast<u8>(i), | 132 | devices[i].handle.internal_index.Assign(i); |
| 141 | .player_number = static_cast<u8>(parameters.npad_id), | 133 | devices[i].handle.player_number.Assign(static_cast<u8>(npad_id)); |
| 142 | .bus_type_id = static_cast<u8>(parameters.bus_type), | 134 | devices[i].handle.bus_type_id.Assign(static_cast<u8>(bus_type)); |
| 143 | .is_valid = true, | 135 | devices[i].handle.is_valid.Assign(true); |
| 144 | }; | ||
| 145 | handle_index = i; | 136 | handle_index = i; |
| 146 | break; | 137 | break; |
| 147 | } | 138 | } |
| 148 | } | 139 | } |
| 149 | 140 | ||
| 150 | struct OutData { | 141 | *out_is_valid = true; |
| 151 | bool is_valid; | 142 | *out_bus_handle = devices[handle_index].handle; |
| 152 | INSERT_PADDING_BYTES(7); | 143 | R_SUCCEED(); |
| 153 | BusHandle handle; | ||
| 154 | }; | ||
| 155 | static_assert(sizeof(OutData) == 0x10, "OutData has incorrect size."); | ||
| 156 | |||
| 157 | const OutData out_data{ | ||
| 158 | .is_valid = true, | ||
| 159 | .handle = devices[handle_index].handle, | ||
| 160 | }; | ||
| 161 | |||
| 162 | IPC::ResponseBuilder rb{ctx, 6}; | ||
| 163 | rb.Push(ResultSuccess); | ||
| 164 | rb.PushRaw(out_data); | ||
| 165 | } | 144 | } |
| 166 | 145 | ||
| 167 | void HidBus::IsExternalDeviceConnected(HLERequestContext& ctx) { | 146 | Result Hidbus::IsExternalDeviceConnected(Out<bool> out_is_connected, BusHandle bus_handle) { |
| 168 | IPC::RequestParser rp{ctx}; | ||
| 169 | const auto bus_handle_{rp.PopRaw<BusHandle>()}; | ||
| 170 | |||
| 171 | LOG_INFO(Service_HID, | 147 | LOG_INFO(Service_HID, |
| 172 | "Called, abstracted_pad_id={}, bus_type={}, internal_index={}, " | 148 | "Called, abstracted_pad_id={}, bus_type={}, internal_index={}, " |
| 173 | "player_number={}, is_valid={}", | 149 | "player_number={}, is_valid={}", |
| 174 | bus_handle_.abstracted_pad_id, bus_handle_.bus_type_id, bus_handle_.internal_index, | 150 | bus_handle.abstracted_pad_id, bus_handle.bus_type_id, bus_handle.internal_index, |
| 175 | bus_handle_.player_number, bus_handle_.is_valid); | 151 | bus_handle.player_number, bus_handle.is_valid); |
| 176 | 152 | ||
| 177 | const auto device_index = GetDeviceIndexFromHandle(bus_handle_); | 153 | const auto device_index = GetDeviceIndexFromHandle(bus_handle); |
| 178 | 154 | ||
| 179 | if (device_index) { | 155 | R_UNLESS(device_index.has_value(), ResultUnknown); |
| 180 | const auto& device = devices[device_index.value()].device; | ||
| 181 | const bool is_attached = device->IsDeviceActivated(); | ||
| 182 | 156 | ||
| 183 | IPC::ResponseBuilder rb{ctx, 3}; | 157 | *out_is_connected = devices[device_index.value()].device->IsDeviceActivated(); |
| 184 | rb.Push(ResultSuccess); | 158 | R_SUCCEED(); |
| 185 | rb.Push(is_attached); | ||
| 186 | return; | ||
| 187 | } | ||
| 188 | |||
| 189 | LOG_ERROR(Service_HID, "Invalid handle"); | ||
| 190 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 191 | rb.Push(ResultUnknown); | ||
| 192 | return; | ||
| 193 | } | 159 | } |
| 194 | 160 | ||
| 195 | void HidBus::Initialize(HLERequestContext& ctx) { | 161 | Result Hidbus::Initialize(BusHandle bus_handle, AppletResourceUserId aruid) { |
| 196 | IPC::RequestParser rp{ctx}; | ||
| 197 | const auto bus_handle_{rp.PopRaw<BusHandle>()}; | ||
| 198 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 199 | |||
| 200 | LOG_INFO(Service_HID, | 162 | LOG_INFO(Service_HID, |
| 201 | "called, abstracted_pad_id={} bus_type={} internal_index={} " | 163 | "called, abstracted_pad_id={} bus_type={} internal_index={} " |
| 202 | "player_number={} is_valid={}, applet_resource_user_id={}", | 164 | "player_number={} is_valid={}, applet_resource_user_id={}", |
| 203 | bus_handle_.abstracted_pad_id, bus_handle_.bus_type_id, bus_handle_.internal_index, | 165 | bus_handle.abstracted_pad_id, bus_handle.bus_type_id, bus_handle.internal_index, |
| 204 | bus_handle_.player_number, bus_handle_.is_valid, applet_resource_user_id); | 166 | bus_handle.player_number, bus_handle.is_valid, aruid.pid); |
| 205 | 167 | ||
| 206 | is_hidbus_enabled = true; | 168 | is_hidbus_enabled = true; |
| 207 | 169 | ||
| 208 | const auto device_index = GetDeviceIndexFromHandle(bus_handle_); | 170 | const auto device_index = GetDeviceIndexFromHandle(bus_handle); |
| 209 | |||
| 210 | if (device_index) { | ||
| 211 | const auto entry_index = devices[device_index.value()].handle.internal_index; | ||
| 212 | auto& cur_entry = hidbus_status.entries[entry_index]; | ||
| 213 | |||
| 214 | if (bus_handle_.internal_index == 0 && Settings::values.enable_ring_controller) { | ||
| 215 | MakeDevice<RingController>(bus_handle_); | ||
| 216 | devices[device_index.value()].is_device_initialized = true; | ||
| 217 | devices[device_index.value()].device->ActivateDevice(); | ||
| 218 | cur_entry.is_in_focus = true; | ||
| 219 | cur_entry.is_connected = true; | ||
| 220 | cur_entry.is_connected_result = ResultSuccess; | ||
| 221 | cur_entry.is_enabled = false; | ||
| 222 | cur_entry.is_polling_mode = false; | ||
| 223 | } else { | ||
| 224 | MakeDevice<HidbusStubbed>(bus_handle_); | ||
| 225 | devices[device_index.value()].is_device_initialized = true; | ||
| 226 | cur_entry.is_in_focus = true; | ||
| 227 | cur_entry.is_connected = false; | ||
| 228 | cur_entry.is_connected_result = ResultSuccess; | ||
| 229 | cur_entry.is_enabled = false; | ||
| 230 | cur_entry.is_polling_mode = false; | ||
| 231 | } | ||
| 232 | |||
| 233 | std::memcpy(system.Kernel().GetHidBusSharedMem().GetPointer(), &hidbus_status, | ||
| 234 | sizeof(hidbus_status)); | ||
| 235 | |||
| 236 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 237 | rb.Push(ResultSuccess); | ||
| 238 | return; | ||
| 239 | } | ||
| 240 | 171 | ||
| 241 | LOG_ERROR(Service_HID, "Invalid handle"); | 172 | R_UNLESS(device_index.has_value(), ResultUnknown); |
| 242 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 243 | rb.Push(ResultUnknown); | ||
| 244 | return; | ||
| 245 | } | ||
| 246 | 173 | ||
| 247 | void HidBus::Finalize(HLERequestContext& ctx) { | 174 | const auto entry_index = devices[device_index.value()].handle.internal_index; |
| 248 | IPC::RequestParser rp{ctx}; | 175 | auto& cur_entry = hidbus_status.entries[entry_index]; |
| 249 | const auto bus_handle_{rp.PopRaw<BusHandle>()}; | ||
| 250 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 251 | |||
| 252 | LOG_INFO(Service_HID, | ||
| 253 | "called, abstracted_pad_id={}, bus_type={}, internal_index={}, " | ||
| 254 | "player_number={}, is_valid={}, applet_resource_user_id={}", | ||
| 255 | bus_handle_.abstracted_pad_id, bus_handle_.bus_type_id, bus_handle_.internal_index, | ||
| 256 | bus_handle_.player_number, bus_handle_.is_valid, applet_resource_user_id); | ||
| 257 | |||
| 258 | const auto device_index = GetDeviceIndexFromHandle(bus_handle_); | ||
| 259 | |||
| 260 | if (device_index) { | ||
| 261 | const auto entry_index = devices[device_index.value()].handle.internal_index; | ||
| 262 | auto& cur_entry = hidbus_status.entries[entry_index]; | ||
| 263 | auto& device = devices[device_index.value()].device; | ||
| 264 | devices[device_index.value()].is_device_initialized = false; | ||
| 265 | device->DeactivateDevice(); | ||
| 266 | 176 | ||
| 177 | if (bus_handle.internal_index == 0 && Settings::values.enable_ring_controller) { | ||
| 178 | MakeDevice<RingController>(bus_handle); | ||
| 179 | devices[device_index.value()].is_device_initialized = true; | ||
| 180 | devices[device_index.value()].device->ActivateDevice(); | ||
| 181 | cur_entry.is_in_focus = true; | ||
| 182 | cur_entry.is_connected = true; | ||
| 183 | cur_entry.is_connected_result = ResultSuccess; | ||
| 184 | cur_entry.is_enabled = false; | ||
| 185 | cur_entry.is_polling_mode = false; | ||
| 186 | } else { | ||
| 187 | MakeDevice<HidbusStubbed>(bus_handle); | ||
| 188 | devices[device_index.value()].is_device_initialized = true; | ||
| 267 | cur_entry.is_in_focus = true; | 189 | cur_entry.is_in_focus = true; |
| 268 | cur_entry.is_connected = false; | 190 | cur_entry.is_connected = false; |
| 269 | cur_entry.is_connected_result = ResultSuccess; | 191 | cur_entry.is_connected_result = ResultSuccess; |
| 270 | cur_entry.is_enabled = false; | 192 | cur_entry.is_enabled = false; |
| 271 | cur_entry.is_polling_mode = false; | 193 | cur_entry.is_polling_mode = false; |
| 272 | std::memcpy(system.Kernel().GetHidBusSharedMem().GetPointer(), &hidbus_status, | ||
| 273 | sizeof(hidbus_status)); | ||
| 274 | |||
| 275 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 276 | rb.Push(ResultSuccess); | ||
| 277 | return; | ||
| 278 | } | 194 | } |
| 279 | 195 | ||
| 280 | LOG_ERROR(Service_HID, "Invalid handle"); | 196 | std::memcpy(system.Kernel().GetHidBusSharedMem().GetPointer(), &hidbus_status, |
| 281 | IPC::ResponseBuilder rb{ctx, 2}; | 197 | sizeof(hidbus_status)); |
| 282 | rb.Push(ResultUnknown); | 198 | R_SUCCEED(); |
| 283 | return; | ||
| 284 | } | 199 | } |
| 285 | 200 | ||
| 286 | void HidBus::EnableExternalDevice(HLERequestContext& ctx) { | 201 | Result Hidbus::Finalize(BusHandle bus_handle, AppletResourceUserId aruid) { |
| 287 | IPC::RequestParser rp{ctx}; | 202 | LOG_INFO(Service_HID, |
| 288 | struct Parameters { | 203 | "called, abstracted_pad_id={}, bus_type={}, internal_index={}, " |
| 289 | bool enable; | 204 | "player_number={}, is_valid={}, applet_resource_user_id={}", |
| 290 | INSERT_PADDING_BYTES_NOINIT(7); | 205 | bus_handle.abstracted_pad_id, bus_handle.bus_type_id, bus_handle.internal_index, |
| 291 | BusHandle bus_handle; | 206 | bus_handle.player_number, bus_handle.is_valid, aruid.pid); |
| 292 | u64 inval; | 207 | |
| 293 | u64 applet_resource_user_id; | 208 | const auto device_index = GetDeviceIndexFromHandle(bus_handle); |
| 294 | }; | 209 | |
| 295 | static_assert(sizeof(Parameters) == 0x20, "Parameters has incorrect size."); | 210 | R_UNLESS(device_index.has_value(), ResultUnknown); |
| 296 | 211 | ||
| 297 | const auto parameters{rp.PopRaw<Parameters>()}; | 212 | const auto entry_index = devices[device_index.value()].handle.internal_index; |
| 213 | auto& cur_entry = hidbus_status.entries[entry_index]; | ||
| 214 | auto& device = devices[device_index.value()].device; | ||
| 215 | devices[device_index.value()].is_device_initialized = false; | ||
| 216 | device->DeactivateDevice(); | ||
| 217 | |||
| 218 | cur_entry.is_in_focus = true; | ||
| 219 | cur_entry.is_connected = false; | ||
| 220 | cur_entry.is_connected_result = ResultSuccess; | ||
| 221 | cur_entry.is_enabled = false; | ||
| 222 | cur_entry.is_polling_mode = false; | ||
| 223 | std::memcpy(system.Kernel().GetHidBusSharedMem().GetPointer(), &hidbus_status, | ||
| 224 | sizeof(hidbus_status)); | ||
| 225 | R_SUCCEED(); | ||
| 226 | } | ||
| 298 | 227 | ||
| 228 | Result Hidbus::EnableExternalDevice(bool is_enabled, BusHandle bus_handle, u64 inval, | ||
| 229 | AppletResourceUserId aruid) { | ||
| 299 | LOG_DEBUG(Service_HID, | 230 | LOG_DEBUG(Service_HID, |
| 300 | "called, enable={}, abstracted_pad_id={}, bus_type={}, internal_index={}, " | 231 | "called, enable={}, abstracted_pad_id={}, bus_type={}, internal_index={}, " |
| 301 | "player_number={}, is_valid={}, inval={}, applet_resource_user_id{}", | 232 | "player_number={}, is_valid={}, inval={}, applet_resource_user_id{}", |
| 302 | parameters.enable, parameters.bus_handle.abstracted_pad_id, | 233 | is_enabled, bus_handle.abstracted_pad_id, bus_handle.bus_type_id, |
| 303 | parameters.bus_handle.bus_type_id, parameters.bus_handle.internal_index, | 234 | bus_handle.internal_index, bus_handle.player_number, bus_handle.is_valid, inval, |
| 304 | parameters.bus_handle.player_number, parameters.bus_handle.is_valid, parameters.inval, | 235 | aruid.pid); |
| 305 | parameters.applet_resource_user_id); | ||
| 306 | |||
| 307 | const auto device_index = GetDeviceIndexFromHandle(parameters.bus_handle); | ||
| 308 | |||
| 309 | if (device_index) { | ||
| 310 | auto& device = devices[device_index.value()].device; | ||
| 311 | device->Enable(parameters.enable); | ||
| 312 | 236 | ||
| 313 | IPC::ResponseBuilder rb{ctx, 2}; | 237 | const auto device_index = GetDeviceIndexFromHandle(bus_handle); |
| 314 | rb.Push(ResultSuccess); | ||
| 315 | return; | ||
| 316 | } | ||
| 317 | 238 | ||
| 318 | LOG_ERROR(Service_HID, "Invalid handle"); | 239 | R_UNLESS(device_index.has_value(), ResultUnknown); |
| 319 | IPC::ResponseBuilder rb{ctx, 2}; | 240 | devices[device_index.value()].device->Enable(is_enabled); |
| 320 | rb.Push(ResultUnknown); | 241 | R_SUCCEED(); |
| 321 | return; | ||
| 322 | } | 242 | } |
| 323 | 243 | ||
| 324 | void HidBus::GetExternalDeviceId(HLERequestContext& ctx) { | 244 | Result Hidbus::GetExternalDeviceId(Out<u32> out_device_id, BusHandle bus_handle) { |
| 325 | IPC::RequestParser rp{ctx}; | ||
| 326 | const auto bus_handle_{rp.PopRaw<BusHandle>()}; | ||
| 327 | |||
| 328 | LOG_DEBUG(Service_HID, | 245 | LOG_DEBUG(Service_HID, |
| 329 | "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, " | 246 | "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, " |
| 330 | "is_valid={}", | 247 | "is_valid={}", |
| 331 | bus_handle_.abstracted_pad_id, bus_handle_.bus_type_id, bus_handle_.internal_index, | 248 | bus_handle.abstracted_pad_id, bus_handle.bus_type_id, bus_handle.internal_index, |
| 332 | bus_handle_.player_number, bus_handle_.is_valid); | 249 | bus_handle.player_number, bus_handle.is_valid); |
| 333 | |||
| 334 | const auto device_index = GetDeviceIndexFromHandle(bus_handle_); | ||
| 335 | |||
| 336 | if (device_index) { | ||
| 337 | const auto& device = devices[device_index.value()].device; | ||
| 338 | u32 device_id = device->GetDeviceId(); | ||
| 339 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 340 | rb.Push(ResultSuccess); | ||
| 341 | rb.Push<u32>(device_id); | ||
| 342 | return; | ||
| 343 | } | ||
| 344 | 250 | ||
| 345 | LOG_ERROR(Service_HID, "Invalid handle"); | 251 | const auto device_index = GetDeviceIndexFromHandle(bus_handle); |
| 346 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 347 | rb.Push(ResultUnknown); | ||
| 348 | return; | ||
| 349 | } | ||
| 350 | 252 | ||
| 351 | void HidBus::SendCommandAsync(HLERequestContext& ctx) { | 253 | R_UNLESS(device_index.has_value(), ResultUnknown); |
| 352 | IPC::RequestParser rp{ctx}; | ||
| 353 | const auto data = ctx.ReadBuffer(); | ||
| 354 | const auto bus_handle_{rp.PopRaw<BusHandle>()}; | ||
| 355 | 254 | ||
| 255 | *out_device_id = devices[device_index.value()].device->GetDeviceId(); | ||
| 256 | R_SUCCEED(); | ||
| 257 | } | ||
| 258 | |||
| 259 | Result Hidbus::SendCommandAsync(BusHandle bus_handle, | ||
| 260 | InBuffer<BufferAttr_HipcAutoSelect> buffer_data) { | ||
| 356 | LOG_DEBUG(Service_HID, | 261 | LOG_DEBUG(Service_HID, |
| 357 | "called, data_size={}, abstracted_pad_id={}, bus_type={}, internal_index={}, " | 262 | "called, data_size={}, abstracted_pad_id={}, bus_type={}, internal_index={}, " |
| 358 | "player_number={}, is_valid={}", | 263 | "player_number={}, is_valid={}", |
| 359 | data.size(), bus_handle_.abstracted_pad_id, bus_handle_.bus_type_id, | 264 | buffer_data.size(), bus_handle.abstracted_pad_id, bus_handle.bus_type_id, |
| 360 | bus_handle_.internal_index, bus_handle_.player_number, bus_handle_.is_valid); | 265 | bus_handle.internal_index, bus_handle.player_number, bus_handle.is_valid); |
| 361 | 266 | ||
| 362 | const auto device_index = GetDeviceIndexFromHandle(bus_handle_); | 267 | const auto device_index = GetDeviceIndexFromHandle(bus_handle); |
| 363 | 268 | ||
| 364 | if (device_index) { | 269 | R_UNLESS(device_index.has_value(), ResultUnknown); |
| 365 | auto& device = devices[device_index.value()].device; | ||
| 366 | device->SetCommand(data); | ||
| 367 | |||
| 368 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 369 | rb.Push(ResultSuccess); | ||
| 370 | return; | ||
| 371 | } | ||
| 372 | 270 | ||
| 373 | LOG_ERROR(Service_HID, "Invalid handle"); | 271 | devices[device_index.value()].device->SetCommand(buffer_data); |
| 374 | IPC::ResponseBuilder rb{ctx, 2}; | 272 | R_SUCCEED(); |
| 375 | rb.Push(ResultUnknown); | ||
| 376 | return; | ||
| 377 | }; | 273 | }; |
| 378 | 274 | ||
| 379 | void HidBus::GetSendCommandAsynceResult(HLERequestContext& ctx) { | 275 | Result Hidbus::GetSendCommandAsynceResult(Out<u64> out_data_size, BusHandle bus_handle, |
| 380 | IPC::RequestParser rp{ctx}; | 276 | OutBuffer<BufferAttr_HipcAutoSelect> out_buffer_data) { |
| 381 | const auto bus_handle_{rp.PopRaw<BusHandle>()}; | ||
| 382 | |||
| 383 | LOG_DEBUG(Service_HID, | 277 | LOG_DEBUG(Service_HID, |
| 384 | "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, " | 278 | "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, " |
| 385 | "is_valid={}", | 279 | "is_valid={}", |
| 386 | bus_handle_.abstracted_pad_id, bus_handle_.bus_type_id, bus_handle_.internal_index, | 280 | bus_handle.abstracted_pad_id, bus_handle.bus_type_id, bus_handle.internal_index, |
| 387 | bus_handle_.player_number, bus_handle_.is_valid); | 281 | bus_handle.player_number, bus_handle.is_valid); |
| 388 | |||
| 389 | const auto device_index = GetDeviceIndexFromHandle(bus_handle_); | ||
| 390 | 282 | ||
| 391 | if (device_index) { | 283 | const auto device_index = GetDeviceIndexFromHandle(bus_handle); |
| 392 | const auto& device = devices[device_index.value()].device; | ||
| 393 | const std::vector<u8> data = device->GetReply(); | ||
| 394 | const u64 data_size = ctx.WriteBuffer(data); | ||
| 395 | 284 | ||
| 396 | IPC::ResponseBuilder rb{ctx, 4}; | 285 | R_UNLESS(device_index.has_value(), ResultUnknown); |
| 397 | rb.Push(ResultSuccess); | ||
| 398 | rb.Push<u64>(data_size); | ||
| 399 | return; | ||
| 400 | } | ||
| 401 | 286 | ||
| 402 | LOG_ERROR(Service_HID, "Invalid handle"); | 287 | *out_data_size = devices[device_index.value()].device->GetReply(out_buffer_data); |
| 403 | IPC::ResponseBuilder rb{ctx, 2}; | 288 | R_SUCCEED(); |
| 404 | rb.Push(ResultUnknown); | ||
| 405 | return; | ||
| 406 | }; | 289 | }; |
| 407 | 290 | ||
| 408 | void HidBus::SetEventForSendCommandAsycResult(HLERequestContext& ctx) { | 291 | Result Hidbus::SetEventForSendCommandAsycResult(OutCopyHandle<Kernel::KReadableEvent> out_event, |
| 409 | IPC::RequestParser rp{ctx}; | 292 | BusHandle bus_handle) { |
| 410 | const auto bus_handle_{rp.PopRaw<BusHandle>()}; | ||
| 411 | |||
| 412 | LOG_INFO(Service_HID, | 293 | LOG_INFO(Service_HID, |
| 413 | "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, " | 294 | "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, " |
| 414 | "is_valid={}", | 295 | "is_valid={}", |
| 415 | bus_handle_.abstracted_pad_id, bus_handle_.bus_type_id, bus_handle_.internal_index, | 296 | bus_handle.abstracted_pad_id, bus_handle.bus_type_id, bus_handle.internal_index, |
| 416 | bus_handle_.player_number, bus_handle_.is_valid); | 297 | bus_handle.player_number, bus_handle.is_valid); |
| 417 | 298 | ||
| 418 | const auto device_index = GetDeviceIndexFromHandle(bus_handle_); | 299 | const auto device_index = GetDeviceIndexFromHandle(bus_handle); |
| 419 | 300 | ||
| 420 | if (device_index) { | 301 | R_UNLESS(device_index.has_value(), ResultUnknown); |
| 421 | const auto& device = devices[device_index.value()].device; | ||
| 422 | IPC::ResponseBuilder rb{ctx, 2, 1}; | ||
| 423 | rb.Push(ResultSuccess); | ||
| 424 | rb.PushCopyObjects(device->GetSendCommandAsycEvent()); | ||
| 425 | return; | ||
| 426 | } | ||
| 427 | 302 | ||
| 428 | LOG_ERROR(Service_HID, "Invalid handle"); | 303 | *out_event = &devices[device_index.value()].device->GetSendCommandAsycEvent(); |
| 429 | IPC::ResponseBuilder rb{ctx, 2}; | 304 | R_SUCCEED(); |
| 430 | rb.Push(ResultUnknown); | ||
| 431 | return; | ||
| 432 | }; | 305 | }; |
| 433 | 306 | ||
| 434 | void HidBus::GetSharedMemoryHandle(HLERequestContext& ctx) { | 307 | Result Hidbus::GetSharedMemoryHandle(OutCopyHandle<Kernel::KSharedMemory> out_shared_memory) { |
| 435 | LOG_DEBUG(Service_HID, "called"); | 308 | LOG_DEBUG(Service_HID, "called"); |
| 436 | 309 | ||
| 437 | IPC::ResponseBuilder rb{ctx, 2, 1}; | 310 | *out_shared_memory = &system.Kernel().GetHidBusSharedMem(); |
| 438 | rb.Push(ResultSuccess); | 311 | R_SUCCEED(); |
| 439 | rb.PushCopyObjects(&system.Kernel().GetHidBusSharedMem()); | ||
| 440 | } | 312 | } |
| 441 | 313 | ||
| 442 | void HidBus::EnableJoyPollingReceiveMode(HLERequestContext& ctx) { | 314 | Result Hidbus::EnableJoyPollingReceiveMode(u32 t_mem_size, JoyPollingMode polling_mode, |
| 443 | IPC::RequestParser rp{ctx}; | 315 | BusHandle bus_handle, |
| 444 | const auto t_mem_size{rp.Pop<u32>()}; | 316 | InCopyHandle<Kernel::KTransferMemory> t_mem) { |
| 445 | const auto t_mem_handle{ctx.GetCopyHandle(0)}; | ||
| 446 | const auto polling_mode_{rp.PopEnum<JoyPollingMode>()}; | ||
| 447 | const auto bus_handle_{rp.PopRaw<BusHandle>()}; | ||
| 448 | |||
| 449 | ASSERT_MSG(t_mem_size == 0x1000, "t_mem_size is not 0x1000 bytes"); | 317 | ASSERT_MSG(t_mem_size == 0x1000, "t_mem_size is not 0x1000 bytes"); |
| 450 | 318 | ASSERT_MSG(t_mem->GetSize() == t_mem_size, "t_mem has incorrect size"); | |
| 451 | auto t_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_handle); | ||
| 452 | |||
| 453 | if (t_mem.IsNull()) { | ||
| 454 | LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle); | ||
| 455 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 456 | rb.Push(ResultUnknown); | ||
| 457 | return; | ||
| 458 | } | ||
| 459 | |||
| 460 | ASSERT_MSG(t_mem->GetSize() == 0x1000, "t_mem has incorrect size"); | ||
| 461 | 319 | ||
| 462 | LOG_INFO(Service_HID, | 320 | LOG_INFO(Service_HID, |
| 463 | "called, t_mem_handle=0x{:08X}, polling_mode={}, abstracted_pad_id={}, bus_type={}, " | 321 | "called, polling_mode={}, abstracted_pad_id={}, bus_type={}, " |
| 464 | "internal_index={}, player_number={}, is_valid={}", | 322 | "internal_index={}, player_number={}, is_valid={}", |
| 465 | t_mem_handle, polling_mode_, bus_handle_.abstracted_pad_id, bus_handle_.bus_type_id, | 323 | polling_mode, bus_handle.abstracted_pad_id, bus_handle.bus_type_id, |
| 466 | bus_handle_.internal_index, bus_handle_.player_number, bus_handle_.is_valid); | 324 | bus_handle.internal_index, bus_handle.player_number, bus_handle.is_valid); |
| 467 | 325 | ||
| 468 | const auto device_index = GetDeviceIndexFromHandle(bus_handle_); | 326 | const auto device_index = GetDeviceIndexFromHandle(bus_handle); |
| 469 | 327 | ||
| 470 | if (device_index) { | 328 | R_UNLESS(device_index.has_value(), ResultUnknown); |
| 471 | auto& device = devices[device_index.value()].device; | ||
| 472 | device->SetPollingMode(polling_mode_); | ||
| 473 | device->SetTransferMemoryAddress(t_mem->GetSourceAddress()); | ||
| 474 | 329 | ||
| 475 | IPC::ResponseBuilder rb{ctx, 2}; | 330 | auto& device = devices[device_index.value()].device; |
| 476 | rb.Push(ResultSuccess); | 331 | device->SetPollingMode(polling_mode); |
| 477 | return; | 332 | device->SetTransferMemoryAddress(t_mem->GetSourceAddress()); |
| 478 | } | 333 | R_SUCCEED(); |
| 479 | |||
| 480 | LOG_ERROR(Service_HID, "Invalid handle"); | ||
| 481 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 482 | rb.Push(ResultUnknown); | ||
| 483 | return; | ||
| 484 | } | 334 | } |
| 485 | 335 | ||
| 486 | void HidBus::DisableJoyPollingReceiveMode(HLERequestContext& ctx) { | 336 | Result Hidbus::DisableJoyPollingReceiveMode(BusHandle bus_handle) { |
| 487 | IPC::RequestParser rp{ctx}; | ||
| 488 | const auto bus_handle_{rp.PopRaw<BusHandle>()}; | ||
| 489 | |||
| 490 | LOG_INFO(Service_HID, | 337 | LOG_INFO(Service_HID, |
| 491 | "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, " | 338 | "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, " |
| 492 | "is_valid={}", | 339 | "is_valid={}", |
| 493 | bus_handle_.abstracted_pad_id, bus_handle_.bus_type_id, bus_handle_.internal_index, | 340 | bus_handle.abstracted_pad_id, bus_handle.bus_type_id, bus_handle.internal_index, |
| 494 | bus_handle_.player_number, bus_handle_.is_valid); | 341 | bus_handle.player_number, bus_handle.is_valid); |
| 495 | 342 | ||
| 496 | const auto device_index = GetDeviceIndexFromHandle(bus_handle_); | 343 | const auto device_index = GetDeviceIndexFromHandle(bus_handle); |
| 497 | 344 | ||
| 498 | if (device_index) { | 345 | R_UNLESS(device_index.has_value(), ResultUnknown); |
| 499 | auto& device = devices[device_index.value()].device; | ||
| 500 | device->DisablePollingMode(); | ||
| 501 | |||
| 502 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 503 | rb.Push(ResultSuccess); | ||
| 504 | return; | ||
| 505 | } | ||
| 506 | 346 | ||
| 507 | LOG_ERROR(Service_HID, "Invalid handle"); | 347 | auto& device = devices[device_index.value()].device; |
| 508 | IPC::ResponseBuilder rb{ctx, 2}; | 348 | device->DisablePollingMode(); |
| 509 | rb.Push(ResultUnknown); | 349 | R_SUCCEED(); |
| 510 | return; | ||
| 511 | } | 350 | } |
| 512 | 351 | ||
| 513 | void HidBus::SetStatusManagerType(HLERequestContext& ctx) { | 352 | Result Hidbus::SetStatusManagerType(StatusManagerType manager_type) { |
| 514 | IPC::RequestParser rp{ctx}; | ||
| 515 | const auto manager_type{rp.PopEnum<StatusManagerType>()}; | ||
| 516 | |||
| 517 | LOG_WARNING(Service_HID, "(STUBBED) called, manager_type={}", manager_type); | 353 | LOG_WARNING(Service_HID, "(STUBBED) called, manager_type={}", manager_type); |
| 518 | 354 | R_SUCCEED(); | |
| 519 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 520 | rb.Push(ResultSuccess); | ||
| 521 | }; | 355 | }; |
| 356 | |||
| 522 | } // namespace Service::HID | 357 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/hidbus.h b/src/core/hle/service/hid/hidbus.h index 03d9f6863..af4d95667 100644 --- a/src/core/hle/service/hid/hidbus.h +++ b/src/core/hle/service/hid/hidbus.h | |||
| @@ -5,8 +5,10 @@ | |||
| 5 | 5 | ||
| 6 | #include <functional> | 6 | #include <functional> |
| 7 | 7 | ||
| 8 | #include "core/hle/service/cmif_types.h" | ||
| 8 | #include "core/hle/service/kernel_helpers.h" | 9 | #include "core/hle/service/kernel_helpers.h" |
| 9 | #include "core/hle/service/service.h" | 10 | #include "core/hle/service/service.h" |
| 11 | #include "hid_core/hid_types.h" | ||
| 10 | #include "hid_core/hidbus/hidbus_base.h" | 12 | #include "hid_core/hidbus/hidbus_base.h" |
| 11 | 13 | ||
| 12 | namespace Core::Timing { | 14 | namespace Core::Timing { |
| @@ -19,10 +21,10 @@ class System; | |||
| 19 | 21 | ||
| 20 | namespace Service::HID { | 22 | namespace Service::HID { |
| 21 | 23 | ||
| 22 | class HidBus final : public ServiceFramework<HidBus> { | 24 | class Hidbus final : public ServiceFramework<Hidbus> { |
| 23 | public: | 25 | public: |
| 24 | explicit HidBus(Core::System& system_); | 26 | explicit Hidbus(Core::System& system_); |
| 25 | ~HidBus() override; | 27 | ~Hidbus() override; |
| 26 | 28 | ||
| 27 | private: | 29 | private: |
| 28 | static const std::size_t max_number_of_handles = 0x13; | 30 | static const std::size_t max_number_of_handles = 0x13; |
| @@ -41,7 +43,7 @@ private: | |||
| 41 | }; | 43 | }; |
| 42 | 44 | ||
| 43 | // This is nn::hidbus::BusType | 45 | // This is nn::hidbus::BusType |
| 44 | enum class BusType : u32 { | 46 | enum class BusType : u64 { |
| 45 | LeftJoyRail, | 47 | LeftJoyRail, |
| 46 | RightJoyRail, | 48 | RightJoyRail, |
| 47 | InternalBus, // Lark microphone | 49 | InternalBus, // Lark microphone |
| @@ -51,11 +53,15 @@ private: | |||
| 51 | 53 | ||
| 52 | // This is nn::hidbus::BusHandle | 54 | // This is nn::hidbus::BusHandle |
| 53 | struct BusHandle { | 55 | struct BusHandle { |
| 54 | u32 abstracted_pad_id; | 56 | union { |
| 55 | u8 internal_index; | 57 | u64 raw{}; |
| 56 | u8 player_number; | 58 | |
| 57 | u8 bus_type_id; | 59 | BitField<0, 32, u64> abstracted_pad_id; |
| 58 | bool is_valid; | 60 | BitField<32, 8, u64> internal_index; |
| 61 | BitField<40, 8, u64> player_number; | ||
| 62 | BitField<48, 8, u64> bus_type_id; | ||
| 63 | BitField<56, 1, u64> is_valid; | ||
| 64 | }; | ||
| 59 | }; | 65 | }; |
| 60 | static_assert(sizeof(BusHandle) == 0x8, "BusHandle is an invalid size"); | 66 | static_assert(sizeof(BusHandle) == 0x8, "BusHandle is an invalid size"); |
| 61 | 67 | ||
| @@ -94,19 +100,38 @@ private: | |||
| 94 | std::unique_ptr<HidbusBase> device{nullptr}; | 100 | std::unique_ptr<HidbusBase> device{nullptr}; |
| 95 | }; | 101 | }; |
| 96 | 102 | ||
| 97 | void GetBusHandle(HLERequestContext& ctx); | 103 | Result GetBusHandle(Out<bool> out_is_valid, Out<BusHandle> out_bus_handle, |
| 98 | void IsExternalDeviceConnected(HLERequestContext& ctx); | 104 | Core::HID::NpadIdType npad_id, BusType bus_type, |
| 99 | void Initialize(HLERequestContext& ctx); | 105 | AppletResourceUserId aruid); |
| 100 | void Finalize(HLERequestContext& ctx); | 106 | |
| 101 | void EnableExternalDevice(HLERequestContext& ctx); | 107 | Result IsExternalDeviceConnected(Out<bool> out_is_connected, BusHandle bus_handle); |
| 102 | void GetExternalDeviceId(HLERequestContext& ctx); | 108 | |
| 103 | void SendCommandAsync(HLERequestContext& ctx); | 109 | Result Initialize(BusHandle bus_handle, AppletResourceUserId aruid); |
| 104 | void GetSendCommandAsynceResult(HLERequestContext& ctx); | 110 | |
| 105 | void SetEventForSendCommandAsycResult(HLERequestContext& ctx); | 111 | Result Finalize(BusHandle bus_handle, AppletResourceUserId aruid); |
| 106 | void GetSharedMemoryHandle(HLERequestContext& ctx); | 112 | |
| 107 | void EnableJoyPollingReceiveMode(HLERequestContext& ctx); | 113 | Result EnableExternalDevice(bool is_enabled, BusHandle bus_handle, u64 inval, |
| 108 | void DisableJoyPollingReceiveMode(HLERequestContext& ctx); | 114 | AppletResourceUserId aruid); |
| 109 | void SetStatusManagerType(HLERequestContext& ctx); | 115 | |
| 116 | Result GetExternalDeviceId(Out<u32> out_device_id, BusHandle bus_handle); | ||
| 117 | |||
| 118 | Result SendCommandAsync(BusHandle bus_handle, InBuffer<BufferAttr_HipcAutoSelect> buffer_data); | ||
| 119 | |||
| 120 | Result GetSendCommandAsynceResult(Out<u64> out_data_size, BusHandle bus_handle, | ||
| 121 | OutBuffer<BufferAttr_HipcAutoSelect> out_buffer_data); | ||
| 122 | |||
| 123 | Result SetEventForSendCommandAsycResult(OutCopyHandle<Kernel::KReadableEvent> out_event, | ||
| 124 | BusHandle bus_handle); | ||
| 125 | |||
| 126 | Result GetSharedMemoryHandle(OutCopyHandle<Kernel::KSharedMemory> out_shared_memory); | ||
| 127 | |||
| 128 | Result EnableJoyPollingReceiveMode(u32 t_mem_size, JoyPollingMode polling_mode, | ||
| 129 | BusHandle bus_handle, | ||
| 130 | InCopyHandle<Kernel::KTransferMemory> t_mem); | ||
| 131 | |||
| 132 | Result DisableJoyPollingReceiveMode(BusHandle bus_handle); | ||
| 133 | |||
| 134 | Result SetStatusManagerType(StatusManagerType manager_type); | ||
| 110 | 135 | ||
| 111 | void UpdateHidbus(std::chrono::nanoseconds ns_late); | 136 | void UpdateHidbus(std::chrono::nanoseconds ns_late); |
| 112 | std::optional<std::size_t> GetDeviceIndexFromHandle(BusHandle handle) const; | 137 | std::optional<std::size_t> GetDeviceIndexFromHandle(BusHandle handle) const; |
diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp index 18e544f2f..7d7368ff9 100644 --- a/src/core/hle/service/hid/irs.cpp +++ b/src/core/hle/service/hid/irs.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include "core/hle/kernel/k_shared_memory.h" | 9 | #include "core/hle/kernel/k_shared_memory.h" |
| 10 | #include "core/hle/kernel/k_transfer_memory.h" | 10 | #include "core/hle/kernel/k_transfer_memory.h" |
| 11 | #include "core/hle/kernel/kernel.h" | 11 | #include "core/hle/kernel/kernel.h" |
| 12 | #include "core/hle/service/cmif_serialization.h" | ||
| 12 | #include "core/hle/service/hid/irs.h" | 13 | #include "core/hle/service/hid/irs.h" |
| 13 | #include "core/hle/service/ipc_helpers.h" | 14 | #include "core/hle/service/ipc_helpers.h" |
| 14 | #include "core/memory.h" | 15 | #include "core/memory.h" |
| @@ -28,24 +29,24 @@ namespace Service::IRS { | |||
| 28 | IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} { | 29 | IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} { |
| 29 | // clang-format off | 30 | // clang-format off |
| 30 | static const FunctionInfo functions[] = { | 31 | static const FunctionInfo functions[] = { |
| 31 | {302, &IRS::ActivateIrsensor, "ActivateIrsensor"}, | 32 | {302, C<&IRS::ActivateIrsensor>, "ActivateIrsensor"}, |
| 32 | {303, &IRS::DeactivateIrsensor, "DeactivateIrsensor"}, | 33 | {303, C<&IRS::DeactivateIrsensor>, "DeactivateIrsensor"}, |
| 33 | {304, &IRS::GetIrsensorSharedMemoryHandle, "GetIrsensorSharedMemoryHandle"}, | 34 | {304, C<&IRS::GetIrsensorSharedMemoryHandle>, "GetIrsensorSharedMemoryHandle"}, |
| 34 | {305, &IRS::StopImageProcessor, "StopImageProcessor"}, | 35 | {305, C<&IRS::StopImageProcessor>, "StopImageProcessor"}, |
| 35 | {306, &IRS::RunMomentProcessor, "RunMomentProcessor"}, | 36 | {306, C<&IRS::RunMomentProcessor>, "RunMomentProcessor"}, |
| 36 | {307, &IRS::RunClusteringProcessor, "RunClusteringProcessor"}, | 37 | {307, C<&IRS::RunClusteringProcessor>, "RunClusteringProcessor"}, |
| 37 | {308, &IRS::RunImageTransferProcessor, "RunImageTransferProcessor"}, | 38 | {308, C<&IRS::RunImageTransferProcessor>, "RunImageTransferProcessor"}, |
| 38 | {309, &IRS::GetImageTransferProcessorState, "GetImageTransferProcessorState"}, | 39 | {309, C<&IRS::GetImageTransferProcessorState>, "GetImageTransferProcessorState"}, |
| 39 | {310, &IRS::RunTeraPluginProcessor, "RunTeraPluginProcessor"}, | 40 | {310, C<&IRS::RunTeraPluginProcessor>, "RunTeraPluginProcessor"}, |
| 40 | {311, &IRS::GetNpadIrCameraHandle, "GetNpadIrCameraHandle"}, | 41 | {311, C<&IRS::GetNpadIrCameraHandle>, "GetNpadIrCameraHandle"}, |
| 41 | {312, &IRS::RunPointingProcessor, "RunPointingProcessor"}, | 42 | {312, C<&IRS::RunPointingProcessor>, "RunPointingProcessor"}, |
| 42 | {313, &IRS::SuspendImageProcessor, "SuspendImageProcessor"}, | 43 | {313, C<&IRS::SuspendImageProcessor>, "SuspendImageProcessor"}, |
| 43 | {314, &IRS::CheckFirmwareVersion, "CheckFirmwareVersion"}, | 44 | {314, C<&IRS::CheckFirmwareVersion>, "CheckFirmwareVersion"}, |
| 44 | {315, &IRS::SetFunctionLevel, "SetFunctionLevel"}, | 45 | {315, C<&IRS::SetFunctionLevel>, "SetFunctionLevel"}, |
| 45 | {316, &IRS::RunImageTransferExProcessor, "RunImageTransferExProcessor"}, | 46 | {316, C<&IRS::RunImageTransferExProcessor>, "RunImageTransferExProcessor"}, |
| 46 | {317, &IRS::RunIrLedProcessor, "RunIrLedProcessor"}, | 47 | {317, C<&IRS::RunIrLedProcessor>, "RunIrLedProcessor"}, |
| 47 | {318, &IRS::StopImageProcessorAsync, "StopImageProcessorAsync"}, | 48 | {318, C<&IRS::StopImageProcessorAsync>, "StopImageProcessorAsync"}, |
| 48 | {319, &IRS::ActivateIrsensorWithFunctionLevel, "ActivateIrsensorWithFunctionLevel"}, | 49 | {319, C<&IRS::ActivateIrsensorWithFunctionLevel>, "ActivateIrsensorWithFunctionLevel"}, |
| 49 | }; | 50 | }; |
| 50 | // clang-format on | 51 | // clang-format on |
| 51 | 52 | ||
| @@ -57,489 +58,292 @@ IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} { | |||
| 57 | } | 58 | } |
| 58 | IRS::~IRS() = default; | 59 | IRS::~IRS() = default; |
| 59 | 60 | ||
| 60 | void IRS::ActivateIrsensor(HLERequestContext& ctx) { | 61 | Result IRS::ActivateIrsensor(ClientAppletResourceUserId aruid) { |
| 61 | IPC::RequestParser rp{ctx}; | 62 | LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}", aruid.pid); |
| 62 | const auto applet_resource_user_id{rp.Pop<u64>()}; | 63 | R_SUCCEED(); |
| 63 | |||
| 64 | LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}", | ||
| 65 | applet_resource_user_id); | ||
| 66 | |||
| 67 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 68 | rb.Push(ResultSuccess); | ||
| 69 | } | 64 | } |
| 70 | 65 | ||
| 71 | void IRS::DeactivateIrsensor(HLERequestContext& ctx) { | 66 | Result IRS::DeactivateIrsensor(ClientAppletResourceUserId aruid) { |
| 72 | IPC::RequestParser rp{ctx}; | 67 | LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}", aruid.pid); |
| 73 | const auto applet_resource_user_id{rp.Pop<u64>()}; | 68 | R_SUCCEED(); |
| 74 | |||
| 75 | LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}", | ||
| 76 | applet_resource_user_id); | ||
| 77 | |||
| 78 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 79 | rb.Push(ResultSuccess); | ||
| 80 | } | 69 | } |
| 81 | 70 | ||
| 82 | void IRS::GetIrsensorSharedMemoryHandle(HLERequestContext& ctx) { | 71 | Result IRS::GetIrsensorSharedMemoryHandle(OutCopyHandle<Kernel::KSharedMemory> out_shared_memory, |
| 83 | IPC::RequestParser rp{ctx}; | 72 | ClientAppletResourceUserId aruid) { |
| 84 | const auto applet_resource_user_id{rp.Pop<u64>()}; | 73 | LOG_DEBUG(Service_IRS, "called, applet_resource_user_id={}", aruid.pid); |
| 85 | |||
| 86 | LOG_DEBUG(Service_IRS, "called, applet_resource_user_id={}", applet_resource_user_id); | ||
| 87 | 74 | ||
| 88 | IPC::ResponseBuilder rb{ctx, 2, 1}; | 75 | *out_shared_memory = &system.Kernel().GetIrsSharedMem(); |
| 89 | rb.Push(ResultSuccess); | 76 | R_SUCCEED(); |
| 90 | rb.PushCopyObjects(&system.Kernel().GetIrsSharedMem()); | ||
| 91 | } | 77 | } |
| 92 | 78 | ||
| 93 | void IRS::StopImageProcessor(HLERequestContext& ctx) { | 79 | Result IRS::StopImageProcessor(Core::IrSensor::IrCameraHandle camera_handle, |
| 94 | IPC::RequestParser rp{ctx}; | 80 | ClientAppletResourceUserId aruid) { |
| 95 | struct Parameters { | ||
| 96 | Core::IrSensor::IrCameraHandle camera_handle; | ||
| 97 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 98 | u64 applet_resource_user_id; | ||
| 99 | }; | ||
| 100 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 101 | |||
| 102 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 103 | |||
| 104 | LOG_WARNING(Service_IRS, | 81 | LOG_WARNING(Service_IRS, |
| 105 | "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", | 82 | "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", |
| 106 | parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, | 83 | camera_handle.npad_type, camera_handle.npad_id, aruid.pid); |
| 107 | parameters.applet_resource_user_id); | ||
| 108 | |||
| 109 | auto result = IsIrCameraHandleValid(parameters.camera_handle); | ||
| 110 | if (result.IsSuccess()) { | ||
| 111 | // TODO: Stop Image processor | ||
| 112 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, | ||
| 113 | Common::Input::PollingMode::Active); | ||
| 114 | result = ResultSuccess; | ||
| 115 | } | ||
| 116 | 84 | ||
| 117 | IPC::ResponseBuilder rb{ctx, 2}; | 85 | R_TRY(IsIrCameraHandleValid(camera_handle)); |
| 118 | rb.Push(result); | ||
| 119 | } | ||
| 120 | |||
| 121 | void IRS::RunMomentProcessor(HLERequestContext& ctx) { | ||
| 122 | IPC::RequestParser rp{ctx}; | ||
| 123 | struct Parameters { | ||
| 124 | Core::IrSensor::IrCameraHandle camera_handle; | ||
| 125 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 126 | u64 applet_resource_user_id; | ||
| 127 | Core::IrSensor::PackedMomentProcessorConfig processor_config; | ||
| 128 | }; | ||
| 129 | static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size."); | ||
| 130 | 86 | ||
| 131 | const auto parameters{rp.PopRaw<Parameters>()}; | 87 | // TODO: Stop Image processor |
| 88 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, | ||
| 89 | Common::Input::PollingMode::Active); | ||
| 90 | R_SUCCEED(); | ||
| 91 | } | ||
| 132 | 92 | ||
| 93 | Result IRS::RunMomentProcessor( | ||
| 94 | Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid, | ||
| 95 | const Core::IrSensor::PackedMomentProcessorConfig& processor_config) { | ||
| 133 | LOG_WARNING(Service_IRS, | 96 | LOG_WARNING(Service_IRS, |
| 134 | "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", | 97 | "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", |
| 135 | parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, | 98 | camera_handle.npad_type, camera_handle.npad_id, aruid.pid); |
| 136 | parameters.applet_resource_user_id); | ||
| 137 | |||
| 138 | const auto result = IsIrCameraHandleValid(parameters.camera_handle); | ||
| 139 | |||
| 140 | if (result.IsSuccess()) { | ||
| 141 | auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); | ||
| 142 | MakeProcessorWithCoreContext<MomentProcessor>(parameters.camera_handle, device); | ||
| 143 | auto& image_transfer_processor = GetProcessor<MomentProcessor>(parameters.camera_handle); | ||
| 144 | image_transfer_processor.SetConfig(parameters.processor_config); | ||
| 145 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, | ||
| 146 | Common::Input::PollingMode::IR); | ||
| 147 | } | ||
| 148 | 99 | ||
| 149 | IPC::ResponseBuilder rb{ctx, 2}; | 100 | R_TRY(IsIrCameraHandleValid(camera_handle)); |
| 150 | rb.Push(result); | ||
| 151 | } | ||
| 152 | 101 | ||
| 153 | void IRS::RunClusteringProcessor(HLERequestContext& ctx) { | 102 | auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle); |
| 154 | IPC::RequestParser rp{ctx}; | 103 | MakeProcessorWithCoreContext<MomentProcessor>(camera_handle, device); |
| 155 | struct Parameters { | 104 | auto& image_transfer_processor = GetProcessor<MomentProcessor>(camera_handle); |
| 156 | Core::IrSensor::IrCameraHandle camera_handle; | 105 | image_transfer_processor.SetConfig(processor_config); |
| 157 | INSERT_PADDING_WORDS_NOINIT(1); | 106 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, |
| 158 | u64 applet_resource_user_id; | 107 | Common::Input::PollingMode::IR); |
| 159 | Core::IrSensor::PackedClusteringProcessorConfig processor_config; | ||
| 160 | }; | ||
| 161 | static_assert(sizeof(Parameters) == 0x38, "Parameters has incorrect size."); | ||
| 162 | 108 | ||
| 163 | const auto parameters{rp.PopRaw<Parameters>()}; | 109 | R_SUCCEED(); |
| 110 | } | ||
| 164 | 111 | ||
| 112 | Result IRS::RunClusteringProcessor( | ||
| 113 | Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid, | ||
| 114 | const Core::IrSensor::PackedClusteringProcessorConfig& processor_config) { | ||
| 165 | LOG_WARNING(Service_IRS, | 115 | LOG_WARNING(Service_IRS, |
| 166 | "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", | 116 | "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", |
| 167 | parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, | 117 | camera_handle.npad_type, camera_handle.npad_id, aruid.pid); |
| 168 | parameters.applet_resource_user_id); | ||
| 169 | |||
| 170 | auto result = IsIrCameraHandleValid(parameters.camera_handle); | ||
| 171 | |||
| 172 | if (result.IsSuccess()) { | ||
| 173 | auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); | ||
| 174 | MakeProcessorWithCoreContext<ClusteringProcessor>(parameters.camera_handle, device); | ||
| 175 | auto& image_transfer_processor = | ||
| 176 | GetProcessor<ClusteringProcessor>(parameters.camera_handle); | ||
| 177 | image_transfer_processor.SetConfig(parameters.processor_config); | ||
| 178 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, | ||
| 179 | Common::Input::PollingMode::IR); | ||
| 180 | } | ||
| 181 | 118 | ||
| 182 | IPC::ResponseBuilder rb{ctx, 2}; | 119 | R_TRY(IsIrCameraHandleValid(camera_handle)); |
| 183 | rb.Push(result); | ||
| 184 | } | ||
| 185 | 120 | ||
| 186 | void IRS::RunImageTransferProcessor(HLERequestContext& ctx) { | 121 | auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle); |
| 187 | IPC::RequestParser rp{ctx}; | 122 | MakeProcessorWithCoreContext<ClusteringProcessor>(camera_handle, device); |
| 188 | struct Parameters { | 123 | auto& image_transfer_processor = GetProcessor<ClusteringProcessor>(camera_handle); |
| 189 | Core::IrSensor::IrCameraHandle camera_handle; | 124 | image_transfer_processor.SetConfig(processor_config); |
| 190 | INSERT_PADDING_WORDS_NOINIT(1); | 125 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, |
| 191 | u64 applet_resource_user_id; | 126 | Common::Input::PollingMode::IR); |
| 192 | Core::IrSensor::PackedImageTransferProcessorConfig processor_config; | ||
| 193 | u32 transfer_memory_size; | ||
| 194 | }; | ||
| 195 | static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size."); | ||
| 196 | 127 | ||
| 197 | const auto parameters{rp.PopRaw<Parameters>()}; | 128 | R_SUCCEED(); |
| 198 | const auto t_mem_handle{ctx.GetCopyHandle(0)}; | 129 | } |
| 199 | 130 | ||
| 200 | auto t_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_handle); | 131 | Result IRS::RunImageTransferProcessor( |
| 132 | Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid, | ||
| 133 | const Core::IrSensor::PackedImageTransferProcessorConfig& processor_config, | ||
| 134 | u64 transfer_memory_size, InCopyHandle<Kernel::KTransferMemory> t_mem) { | ||
| 201 | 135 | ||
| 202 | if (t_mem.IsNull()) { | 136 | ASSERT_MSG(t_mem->GetSize() == transfer_memory_size, "t_mem has incorrect size"); |
| 203 | LOG_ERROR(Service_IRS, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle); | ||
| 204 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 205 | rb.Push(ResultUnknown); | ||
| 206 | return; | ||
| 207 | } | ||
| 208 | |||
| 209 | ASSERT_MSG(t_mem->GetSize() == parameters.transfer_memory_size, "t_mem has incorrect size"); | ||
| 210 | 137 | ||
| 211 | LOG_INFO(Service_IRS, | 138 | LOG_INFO(Service_IRS, |
| 212 | "called, npad_type={}, npad_id={}, transfer_memory_size={}, transfer_memory_size={}, " | 139 | "called, npad_type={}, npad_id={}, transfer_memory_size={}, transfer_memory_size={}, " |
| 213 | "applet_resource_user_id={}", | 140 | "applet_resource_user_id={}", |
| 214 | parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, | 141 | camera_handle.npad_type, camera_handle.npad_id, transfer_memory_size, t_mem->GetSize(), |
| 215 | parameters.transfer_memory_size, t_mem->GetSize(), parameters.applet_resource_user_id); | 142 | aruid.pid); |
| 216 | |||
| 217 | const auto result = IsIrCameraHandleValid(parameters.camera_handle); | ||
| 218 | |||
| 219 | if (result.IsSuccess()) { | ||
| 220 | auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); | ||
| 221 | MakeProcessorWithCoreContext<ImageTransferProcessor>(parameters.camera_handle, device); | ||
| 222 | auto& image_transfer_processor = | ||
| 223 | GetProcessor<ImageTransferProcessor>(parameters.camera_handle); | ||
| 224 | image_transfer_processor.SetConfig(parameters.processor_config); | ||
| 225 | image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress()); | ||
| 226 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, | ||
| 227 | Common::Input::PollingMode::IR); | ||
| 228 | } | ||
| 229 | 143 | ||
| 230 | IPC::ResponseBuilder rb{ctx, 2}; | 144 | R_TRY(IsIrCameraHandleValid(camera_handle)); |
| 231 | rb.Push(result); | ||
| 232 | } | ||
| 233 | 145 | ||
| 234 | void IRS::GetImageTransferProcessorState(HLERequestContext& ctx) { | 146 | auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle); |
| 235 | IPC::RequestParser rp{ctx}; | 147 | MakeProcessorWithCoreContext<ImageTransferProcessor>(camera_handle, device); |
| 236 | struct Parameters { | 148 | auto& image_transfer_processor = GetProcessor<ImageTransferProcessor>(camera_handle); |
| 237 | Core::IrSensor::IrCameraHandle camera_handle; | 149 | image_transfer_processor.SetConfig(processor_config); |
| 238 | INSERT_PADDING_WORDS_NOINIT(1); | 150 | image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress()); |
| 239 | u64 applet_resource_user_id; | 151 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, |
| 240 | }; | 152 | Common::Input::PollingMode::IR); |
| 241 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 242 | 153 | ||
| 243 | const auto parameters{rp.PopRaw<Parameters>()}; | 154 | R_SUCCEED(); |
| 155 | } | ||
| 244 | 156 | ||
| 157 | Result IRS::GetImageTransferProcessorState( | ||
| 158 | Out<Core::IrSensor::ImageTransferProcessorState> out_state, | ||
| 159 | Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid, | ||
| 160 | OutBuffer<BufferAttr_HipcMapAlias> out_buffer_data) { | ||
| 245 | LOG_DEBUG(Service_IRS, "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", | 161 | LOG_DEBUG(Service_IRS, "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", |
| 246 | parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, | 162 | camera_handle.npad_type, camera_handle.npad_id, aruid.pid); |
| 247 | parameters.applet_resource_user_id); | ||
| 248 | |||
| 249 | const auto result = IsIrCameraHandleValid(parameters.camera_handle); | ||
| 250 | if (result.IsError()) { | ||
| 251 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 252 | rb.Push(result); | ||
| 253 | return; | ||
| 254 | } | ||
| 255 | 163 | ||
| 256 | const auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); | 164 | R_TRY(IsIrCameraHandleValid(camera_handle)); |
| 257 | 165 | ||
| 258 | if (device.mode != Core::IrSensor::IrSensorMode::ImageTransferProcessor) { | 166 | const auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle); |
| 259 | IPC::ResponseBuilder rb{ctx, 2}; | 167 | |
| 260 | rb.Push(InvalidProcessorState); | 168 | R_TRY(IsIrCameraHandleValid(camera_handle)); |
| 261 | return; | 169 | R_UNLESS(device.mode == Core::IrSensor::IrSensorMode::ImageTransferProcessor, |
| 262 | } | 170 | InvalidProcessorState); |
| 263 | 171 | ||
| 264 | std::vector<u8> data{}; | 172 | *out_state = GetProcessor<ImageTransferProcessor>(camera_handle).GetState(out_buffer_data); |
| 265 | const auto& image_transfer_processor = | ||
| 266 | GetProcessor<ImageTransferProcessor>(parameters.camera_handle); | ||
| 267 | const auto& state = image_transfer_processor.GetState(data); | ||
| 268 | 173 | ||
| 269 | ctx.WriteBuffer(data); | 174 | R_SUCCEED(); |
| 270 | IPC::ResponseBuilder rb{ctx, 6}; | ||
| 271 | rb.Push(ResultSuccess); | ||
| 272 | rb.PushRaw(state); | ||
| 273 | } | 175 | } |
| 274 | 176 | ||
| 275 | void IRS::RunTeraPluginProcessor(HLERequestContext& ctx) { | 177 | Result IRS::RunTeraPluginProcessor(Core::IrSensor::IrCameraHandle camera_handle, |
| 276 | IPC::RequestParser rp{ctx}; | 178 | Core::IrSensor::PackedTeraPluginProcessorConfig processor_config, |
| 277 | struct Parameters { | 179 | ClientAppletResourceUserId aruid) { |
| 278 | Core::IrSensor::IrCameraHandle camera_handle; | 180 | LOG_WARNING(Service_IRS, |
| 279 | Core::IrSensor::PackedTeraPluginProcessorConfig processor_config; | 181 | "(STUBBED) called, npad_type={}, npad_id={}, mode={}, mcu_version={}.{}, " |
| 280 | INSERT_PADDING_WORDS_NOINIT(1); | 182 | "applet_resource_user_id={}", |
| 281 | u64 applet_resource_user_id; | 183 | camera_handle.npad_type, camera_handle.npad_id, processor_config.mode, |
| 282 | }; | 184 | processor_config.required_mcu_version.major, |
| 283 | static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); | 185 | processor_config.required_mcu_version.minor, aruid.pid); |
| 284 | 186 | ||
| 285 | const auto parameters{rp.PopRaw<Parameters>()}; | 187 | R_TRY(IsIrCameraHandleValid(camera_handle)); |
| 286 | 188 | ||
| 287 | LOG_WARNING( | 189 | auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle); |
| 288 | Service_IRS, | 190 | MakeProcessor<TeraPluginProcessor>(camera_handle, device); |
| 289 | "(STUBBED) called, npad_type={}, npad_id={}, mode={}, mcu_version={}.{}, " | 191 | auto& image_transfer_processor = GetProcessor<TeraPluginProcessor>(camera_handle); |
| 290 | "applet_resource_user_id={}", | 192 | image_transfer_processor.SetConfig(processor_config); |
| 291 | parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, | 193 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, |
| 292 | parameters.processor_config.mode, parameters.processor_config.required_mcu_version.major, | 194 | Common::Input::PollingMode::IR); |
| 293 | parameters.processor_config.required_mcu_version.minor, parameters.applet_resource_user_id); | ||
| 294 | |||
| 295 | const auto result = IsIrCameraHandleValid(parameters.camera_handle); | ||
| 296 | |||
| 297 | if (result.IsSuccess()) { | ||
| 298 | auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); | ||
| 299 | MakeProcessor<TeraPluginProcessor>(parameters.camera_handle, device); | ||
| 300 | auto& image_transfer_processor = | ||
| 301 | GetProcessor<TeraPluginProcessor>(parameters.camera_handle); | ||
| 302 | image_transfer_processor.SetConfig(parameters.processor_config); | ||
| 303 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, | ||
| 304 | Common::Input::PollingMode::IR); | ||
| 305 | } | ||
| 306 | 195 | ||
| 307 | IPC::ResponseBuilder rb{ctx, 2}; | 196 | R_SUCCEED(); |
| 308 | rb.Push(result); | ||
| 309 | } | 197 | } |
| 310 | 198 | ||
| 311 | void IRS::GetNpadIrCameraHandle(HLERequestContext& ctx) { | 199 | Result IRS::GetNpadIrCameraHandle(Out<Core::IrSensor::IrCameraHandle> out_camera_handle, |
| 312 | IPC::RequestParser rp{ctx}; | 200 | Core::HID::NpadIdType npad_id) { |
| 313 | const auto npad_id{rp.PopEnum<Core::HID::NpadIdType>()}; | 201 | R_UNLESS(HID::IsNpadIdValid(npad_id), HID::ResultInvalidNpadId); |
| 314 | |||
| 315 | if (npad_id > Core::HID::NpadIdType::Player8 && npad_id != Core::HID::NpadIdType::Invalid && | ||
| 316 | npad_id != Core::HID::NpadIdType::Handheld) { | ||
| 317 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 318 | rb.Push(Service::HID::ResultInvalidNpadId); | ||
| 319 | return; | ||
| 320 | } | ||
| 321 | 202 | ||
| 322 | Core::IrSensor::IrCameraHandle camera_handle{ | 203 | *out_camera_handle = { |
| 323 | .npad_id = static_cast<u8>(HID::NpadIdTypeToIndex(npad_id)), | 204 | .npad_id = static_cast<u8>(HID::NpadIdTypeToIndex(npad_id)), |
| 324 | .npad_type = Core::HID::NpadStyleIndex::None, | 205 | .npad_type = Core::HID::NpadStyleIndex::None, |
| 325 | }; | 206 | }; |
| 326 | 207 | ||
| 327 | LOG_INFO(Service_IRS, "called, npad_id={}, camera_npad_id={}, camera_npad_type={}", npad_id, | 208 | LOG_INFO(Service_IRS, "called, npad_id={}, camera_npad_id={}, camera_npad_type={}", npad_id, |
| 328 | camera_handle.npad_id, camera_handle.npad_type); | 209 | out_camera_handle->npad_id, out_camera_handle->npad_type); |
| 329 | 210 | ||
| 330 | IPC::ResponseBuilder rb{ctx, 3}; | 211 | R_SUCCEED(); |
| 331 | rb.Push(ResultSuccess); | ||
| 332 | rb.PushRaw(camera_handle); | ||
| 333 | } | 212 | } |
| 334 | 213 | ||
| 335 | void IRS::RunPointingProcessor(HLERequestContext& ctx) { | 214 | Result IRS::RunPointingProcessor( |
| 336 | IPC::RequestParser rp{ctx}; | 215 | Core::IrSensor::IrCameraHandle camera_handle, |
| 337 | const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()}; | 216 | const Core::IrSensor::PackedPointingProcessorConfig& processor_config, |
| 338 | const auto processor_config{rp.PopRaw<Core::IrSensor::PackedPointingProcessorConfig>()}; | 217 | ClientAppletResourceUserId aruid) { |
| 339 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 340 | |||
| 341 | LOG_WARNING( | 218 | LOG_WARNING( |
| 342 | Service_IRS, | 219 | Service_IRS, |
| 343 | "(STUBBED) called, npad_type={}, npad_id={}, mcu_version={}.{}, applet_resource_user_id={}", | 220 | "(STUBBED) called, npad_type={}, npad_id={}, mcu_version={}.{}, applet_resource_user_id={}", |
| 344 | camera_handle.npad_type, camera_handle.npad_id, processor_config.required_mcu_version.major, | 221 | camera_handle.npad_type, camera_handle.npad_id, processor_config.required_mcu_version.major, |
| 345 | processor_config.required_mcu_version.minor, applet_resource_user_id); | 222 | processor_config.required_mcu_version.minor, aruid.pid); |
| 346 | 223 | ||
| 347 | auto result = IsIrCameraHandleValid(camera_handle); | 224 | R_TRY(IsIrCameraHandleValid(camera_handle)); |
| 348 | 225 | ||
| 349 | if (result.IsSuccess()) { | 226 | auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle); |
| 350 | auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle); | 227 | MakeProcessor<PointingProcessor>(camera_handle, device); |
| 351 | MakeProcessor<PointingProcessor>(camera_handle, device); | 228 | auto& image_transfer_processor = GetProcessor<PointingProcessor>(camera_handle); |
| 352 | auto& image_transfer_processor = GetProcessor<PointingProcessor>(camera_handle); | 229 | image_transfer_processor.SetConfig(processor_config); |
| 353 | image_transfer_processor.SetConfig(processor_config); | 230 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, |
| 354 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, | 231 | Common::Input::PollingMode::IR); |
| 355 | Common::Input::PollingMode::IR); | ||
| 356 | } | ||
| 357 | 232 | ||
| 358 | IPC::ResponseBuilder rb{ctx, 2}; | 233 | R_SUCCEED(); |
| 359 | rb.Push(result); | ||
| 360 | } | 234 | } |
| 361 | 235 | ||
| 362 | void IRS::SuspendImageProcessor(HLERequestContext& ctx) { | 236 | Result IRS::SuspendImageProcessor(Core::IrSensor::IrCameraHandle camera_handle, |
| 363 | IPC::RequestParser rp{ctx}; | 237 | ClientAppletResourceUserId aruid) { |
| 364 | struct Parameters { | ||
| 365 | Core::IrSensor::IrCameraHandle camera_handle; | ||
| 366 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 367 | u64 applet_resource_user_id; | ||
| 368 | }; | ||
| 369 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 370 | |||
| 371 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 372 | |||
| 373 | LOG_WARNING(Service_IRS, | 238 | LOG_WARNING(Service_IRS, |
| 374 | "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", | 239 | "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", |
| 375 | parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, | 240 | camera_handle.npad_type, camera_handle.npad_id, aruid.pid); |
| 376 | parameters.applet_resource_user_id); | ||
| 377 | 241 | ||
| 378 | auto result = IsIrCameraHandleValid(parameters.camera_handle); | 242 | R_TRY(IsIrCameraHandleValid(camera_handle)); |
| 379 | if (result.IsSuccess()) { | ||
| 380 | // TODO: Suspend image processor | ||
| 381 | result = ResultSuccess; | ||
| 382 | } | ||
| 383 | 243 | ||
| 384 | IPC::ResponseBuilder rb{ctx, 2}; | 244 | // TODO: Suspend image processor |
| 385 | rb.Push(result); | ||
| 386 | } | ||
| 387 | 245 | ||
| 388 | void IRS::CheckFirmwareVersion(HLERequestContext& ctx) { | 246 | R_SUCCEED(); |
| 389 | IPC::RequestParser rp{ctx}; | 247 | } |
| 390 | const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()}; | ||
| 391 | const auto mcu_version{rp.PopRaw<Core::IrSensor::PackedMcuVersion>()}; | ||
| 392 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 393 | 248 | ||
| 249 | Result IRS::CheckFirmwareVersion(Core::IrSensor::IrCameraHandle camera_handle, | ||
| 250 | Core::IrSensor::PackedMcuVersion mcu_version, | ||
| 251 | ClientAppletResourceUserId aruid) { | ||
| 394 | LOG_WARNING( | 252 | LOG_WARNING( |
| 395 | Service_IRS, | 253 | Service_IRS, |
| 396 | "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}, mcu_version={}.{}", | 254 | "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}, mcu_version={}.{}", |
| 397 | camera_handle.npad_type, camera_handle.npad_id, applet_resource_user_id, mcu_version.major, | 255 | camera_handle.npad_type, camera_handle.npad_id, aruid.pid, mcu_version.major, |
| 398 | mcu_version.minor); | 256 | mcu_version.minor); |
| 399 | 257 | ||
| 400 | auto result = IsIrCameraHandleValid(camera_handle); | 258 | R_TRY(IsIrCameraHandleValid(camera_handle)); |
| 401 | if (result.IsSuccess()) { | ||
| 402 | // TODO: Check firmware version | ||
| 403 | result = ResultSuccess; | ||
| 404 | } | ||
| 405 | 259 | ||
| 406 | IPC::ResponseBuilder rb{ctx, 2}; | 260 | // TODO: Check firmware version |
| 407 | rb.Push(result); | ||
| 408 | } | ||
| 409 | 261 | ||
| 410 | void IRS::SetFunctionLevel(HLERequestContext& ctx) { | 262 | R_SUCCEED(); |
| 411 | IPC::RequestParser rp{ctx}; | 263 | } |
| 412 | const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()}; | ||
| 413 | const auto function_level{rp.PopRaw<Core::IrSensor::PackedFunctionLevel>()}; | ||
| 414 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 415 | 264 | ||
| 265 | Result IRS::SetFunctionLevel(Core::IrSensor::IrCameraHandle camera_handle, | ||
| 266 | Core::IrSensor::PackedFunctionLevel function_level, | ||
| 267 | ClientAppletResourceUserId aruid) { | ||
| 416 | LOG_WARNING( | 268 | LOG_WARNING( |
| 417 | Service_IRS, | 269 | Service_IRS, |
| 418 | "(STUBBED) called, npad_type={}, npad_id={}, function_level={}, applet_resource_user_id={}", | 270 | "(STUBBED) called, npad_type={}, npad_id={}, function_level={}, applet_resource_user_id={}", |
| 419 | camera_handle.npad_type, camera_handle.npad_id, function_level.function_level, | 271 | camera_handle.npad_type, camera_handle.npad_id, function_level.function_level, aruid.pid); |
| 420 | applet_resource_user_id); | ||
| 421 | 272 | ||
| 422 | auto result = IsIrCameraHandleValid(camera_handle); | 273 | R_TRY(IsIrCameraHandleValid(camera_handle)); |
| 423 | if (result.IsSuccess()) { | ||
| 424 | // TODO: Set Function level | ||
| 425 | result = ResultSuccess; | ||
| 426 | } | ||
| 427 | 274 | ||
| 428 | IPC::ResponseBuilder rb{ctx, 2}; | 275 | // TODO: Set Function level |
| 429 | rb.Push(result); | ||
| 430 | } | ||
| 431 | 276 | ||
| 432 | void IRS::RunImageTransferExProcessor(HLERequestContext& ctx) { | 277 | R_SUCCEED(); |
| 433 | IPC::RequestParser rp{ctx}; | 278 | } |
| 434 | struct Parameters { | ||
| 435 | Core::IrSensor::IrCameraHandle camera_handle; | ||
| 436 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 437 | u64 applet_resource_user_id; | ||
| 438 | Core::IrSensor::PackedImageTransferProcessorExConfig processor_config; | ||
| 439 | u64 transfer_memory_size; | ||
| 440 | }; | ||
| 441 | static_assert(sizeof(Parameters) == 0x38, "Parameters has incorrect size."); | ||
| 442 | 279 | ||
| 443 | const auto parameters{rp.PopRaw<Parameters>()}; | 280 | Result IRS::RunImageTransferExProcessor( |
| 444 | const auto t_mem_handle{ctx.GetCopyHandle(0)}; | 281 | Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid, |
| 282 | const Core::IrSensor::PackedImageTransferProcessorExConfig& processor_config, | ||
| 283 | u64 transfer_memory_size, InCopyHandle<Kernel::KTransferMemory> t_mem) { | ||
| 445 | 284 | ||
| 446 | auto t_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_handle); | 285 | ASSERT_MSG(t_mem->GetSize() == transfer_memory_size, "t_mem has incorrect size"); |
| 447 | 286 | ||
| 448 | LOG_INFO(Service_IRS, | 287 | LOG_INFO(Service_IRS, |
| 449 | "called, npad_type={}, npad_id={}, transfer_memory_size={}, " | 288 | "called, npad_type={}, npad_id={}, transfer_memory_size={}, " |
| 450 | "applet_resource_user_id={}", | 289 | "applet_resource_user_id={}", |
| 451 | parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, | 290 | camera_handle.npad_type, camera_handle.npad_id, transfer_memory_size, aruid.pid); |
| 452 | parameters.transfer_memory_size, parameters.applet_resource_user_id); | ||
| 453 | |||
| 454 | auto result = IsIrCameraHandleValid(parameters.camera_handle); | ||
| 455 | |||
| 456 | if (result.IsSuccess()) { | ||
| 457 | auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); | ||
| 458 | MakeProcessorWithCoreContext<ImageTransferProcessor>(parameters.camera_handle, device); | ||
| 459 | auto& image_transfer_processor = | ||
| 460 | GetProcessor<ImageTransferProcessor>(parameters.camera_handle); | ||
| 461 | image_transfer_processor.SetConfig(parameters.processor_config); | ||
| 462 | image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress()); | ||
| 463 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, | ||
| 464 | Common::Input::PollingMode::IR); | ||
| 465 | } | ||
| 466 | 291 | ||
| 467 | IPC::ResponseBuilder rb{ctx, 2}; | 292 | R_TRY(IsIrCameraHandleValid(camera_handle)); |
| 468 | rb.Push(result); | 293 | |
| 469 | } | 294 | auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle); |
| 295 | MakeProcessorWithCoreContext<ImageTransferProcessor>(camera_handle, device); | ||
| 296 | auto& image_transfer_processor = GetProcessor<ImageTransferProcessor>(camera_handle); | ||
| 297 | image_transfer_processor.SetConfig(processor_config); | ||
| 298 | image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress()); | ||
| 299 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, | ||
| 300 | Common::Input::PollingMode::IR); | ||
| 470 | 301 | ||
| 471 | void IRS::RunIrLedProcessor(HLERequestContext& ctx) { | 302 | R_SUCCEED(); |
| 472 | IPC::RequestParser rp{ctx}; | 303 | } |
| 473 | const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()}; | ||
| 474 | const auto processor_config{rp.PopRaw<Core::IrSensor::PackedIrLedProcessorConfig>()}; | ||
| 475 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 476 | 304 | ||
| 305 | Result IRS::RunIrLedProcessor(Core::IrSensor::IrCameraHandle camera_handle, | ||
| 306 | Core::IrSensor::PackedIrLedProcessorConfig processor_config, | ||
| 307 | ClientAppletResourceUserId aruid) { | ||
| 477 | LOG_WARNING(Service_IRS, | 308 | LOG_WARNING(Service_IRS, |
| 478 | "(STUBBED) called, npad_type={}, npad_id={}, light_target={}, mcu_version={}.{} " | 309 | "(STUBBED) called, npad_type={}, npad_id={}, light_target={}, mcu_version={}.{} " |
| 479 | "applet_resource_user_id={}", | 310 | "applet_resource_user_id={}", |
| 480 | camera_handle.npad_type, camera_handle.npad_id, processor_config.light_target, | 311 | camera_handle.npad_type, camera_handle.npad_id, processor_config.light_target, |
| 481 | processor_config.required_mcu_version.major, | 312 | processor_config.required_mcu_version.major, |
| 482 | processor_config.required_mcu_version.minor, applet_resource_user_id); | 313 | processor_config.required_mcu_version.minor, aruid.pid); |
| 483 | 314 | ||
| 484 | auto result = IsIrCameraHandleValid(camera_handle); | 315 | R_TRY(IsIrCameraHandleValid(camera_handle)); |
| 485 | 316 | ||
| 486 | if (result.IsSuccess()) { | 317 | auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle); |
| 487 | auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle); | 318 | MakeProcessor<IrLedProcessor>(camera_handle, device); |
| 488 | MakeProcessor<IrLedProcessor>(camera_handle, device); | 319 | auto& image_transfer_processor = GetProcessor<IrLedProcessor>(camera_handle); |
| 489 | auto& image_transfer_processor = GetProcessor<IrLedProcessor>(camera_handle); | 320 | image_transfer_processor.SetConfig(processor_config); |
| 490 | image_transfer_processor.SetConfig(processor_config); | 321 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, |
| 491 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, | 322 | Common::Input::PollingMode::IR); |
| 492 | Common::Input::PollingMode::IR); | ||
| 493 | } | ||
| 494 | 323 | ||
| 495 | IPC::ResponseBuilder rb{ctx, 2}; | 324 | R_SUCCEED(); |
| 496 | rb.Push(result); | ||
| 497 | } | 325 | } |
| 498 | 326 | ||
| 499 | void IRS::StopImageProcessorAsync(HLERequestContext& ctx) { | 327 | Result IRS::StopImageProcessorAsync(Core::IrSensor::IrCameraHandle camera_handle, |
| 500 | IPC::RequestParser rp{ctx}; | 328 | ClientAppletResourceUserId aruid) { |
| 501 | struct Parameters { | ||
| 502 | Core::IrSensor::IrCameraHandle camera_handle; | ||
| 503 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 504 | u64 applet_resource_user_id; | ||
| 505 | }; | ||
| 506 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 507 | |||
| 508 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 509 | |||
| 510 | LOG_WARNING(Service_IRS, | 329 | LOG_WARNING(Service_IRS, |
| 511 | "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", | 330 | "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", |
| 512 | parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, | 331 | camera_handle.npad_type, camera_handle.npad_id, aruid.pid); |
| 513 | parameters.applet_resource_user_id); | ||
| 514 | |||
| 515 | auto result = IsIrCameraHandleValid(parameters.camera_handle); | ||
| 516 | if (result.IsSuccess()) { | ||
| 517 | // TODO: Stop image processor async | ||
| 518 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, | ||
| 519 | Common::Input::PollingMode::Active); | ||
| 520 | result = ResultSuccess; | ||
| 521 | } | ||
| 522 | 332 | ||
| 523 | IPC::ResponseBuilder rb{ctx, 2}; | 333 | R_TRY(IsIrCameraHandleValid(camera_handle)); |
| 524 | rb.Push(result); | ||
| 525 | } | ||
| 526 | 334 | ||
| 527 | void IRS::ActivateIrsensorWithFunctionLevel(HLERequestContext& ctx) { | 335 | // TODO: Stop image processor async |
| 528 | IPC::RequestParser rp{ctx}; | 336 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, |
| 529 | struct Parameters { | 337 | Common::Input::PollingMode::Active); |
| 530 | Core::IrSensor::PackedFunctionLevel function_level; | ||
| 531 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 532 | u64 applet_resource_user_id; | ||
| 533 | }; | ||
| 534 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 535 | 338 | ||
| 536 | const auto parameters{rp.PopRaw<Parameters>()}; | 339 | R_SUCCEED(); |
| 340 | } | ||
| 537 | 341 | ||
| 342 | Result IRS::ActivateIrsensorWithFunctionLevel(Core::IrSensor::PackedFunctionLevel function_level, | ||
| 343 | ClientAppletResourceUserId aruid) { | ||
| 538 | LOG_WARNING(Service_IRS, "(STUBBED) called, function_level={}, applet_resource_user_id={}", | 344 | LOG_WARNING(Service_IRS, "(STUBBED) called, function_level={}, applet_resource_user_id={}", |
| 539 | parameters.function_level.function_level, parameters.applet_resource_user_id); | 345 | function_level.function_level, aruid.pid); |
| 540 | 346 | R_SUCCEED(); | |
| 541 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 542 | rb.Push(ResultSuccess); | ||
| 543 | } | 347 | } |
| 544 | 348 | ||
| 545 | Result IRS::IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const { | 349 | Result IRS::IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const { |
diff --git a/src/core/hle/service/hid/irs.h b/src/core/hle/service/hid/irs.h index 06b7279ee..58dfee6c3 100644 --- a/src/core/hle/service/hid/irs.h +++ b/src/core/hle/service/hid/irs.h | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include "core/core.h" | 6 | #include "core/core.h" |
| 7 | #include "core/hle/service/cmif_types.h" | ||
| 7 | #include "core/hle/service/service.h" | 8 | #include "core/hle/service/service.h" |
| 8 | #include "hid_core/hid_types.h" | 9 | #include "hid_core/hid_types.h" |
| 9 | #include "hid_core/irsensor/irs_types.h" | 10 | #include "hid_core/irsensor/irs_types.h" |
| @@ -35,26 +36,73 @@ private: | |||
| 35 | }; | 36 | }; |
| 36 | static_assert(sizeof(StatusManager) == 0x8000, "StatusManager is an invalid size"); | 37 | static_assert(sizeof(StatusManager) == 0x8000, "StatusManager is an invalid size"); |
| 37 | 38 | ||
| 38 | void ActivateIrsensor(HLERequestContext& ctx); | 39 | Result ActivateIrsensor(ClientAppletResourceUserId aruid); |
| 39 | void DeactivateIrsensor(HLERequestContext& ctx); | 40 | |
| 40 | void GetIrsensorSharedMemoryHandle(HLERequestContext& ctx); | 41 | Result DeactivateIrsensor(ClientAppletResourceUserId aruid); |
| 41 | void StopImageProcessor(HLERequestContext& ctx); | 42 | |
| 42 | void RunMomentProcessor(HLERequestContext& ctx); | 43 | Result GetIrsensorSharedMemoryHandle(OutCopyHandle<Kernel::KSharedMemory> out_shared_memory, |
| 43 | void RunClusteringProcessor(HLERequestContext& ctx); | 44 | ClientAppletResourceUserId aruid); |
| 44 | void RunImageTransferProcessor(HLERequestContext& ctx); | 45 | Result StopImageProcessor(Core::IrSensor::IrCameraHandle camera_handle, |
| 45 | void GetImageTransferProcessorState(HLERequestContext& ctx); | 46 | ClientAppletResourceUserId aruid); |
| 46 | void RunTeraPluginProcessor(HLERequestContext& ctx); | 47 | |
| 47 | void GetNpadIrCameraHandle(HLERequestContext& ctx); | 48 | Result RunMomentProcessor(Core::IrSensor::IrCameraHandle camera_handle, |
| 48 | void RunPointingProcessor(HLERequestContext& ctx); | 49 | ClientAppletResourceUserId aruid, |
| 49 | void SuspendImageProcessor(HLERequestContext& ctx); | 50 | const Core::IrSensor::PackedMomentProcessorConfig& processor_config); |
| 50 | void CheckFirmwareVersion(HLERequestContext& ctx); | 51 | |
| 51 | void SetFunctionLevel(HLERequestContext& ctx); | 52 | Result RunClusteringProcessor( |
| 52 | void RunImageTransferExProcessor(HLERequestContext& ctx); | 53 | Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid, |
| 53 | void RunIrLedProcessor(HLERequestContext& ctx); | 54 | const Core::IrSensor::PackedClusteringProcessorConfig& processor_config); |
| 54 | void StopImageProcessorAsync(HLERequestContext& ctx); | 55 | |
| 55 | void ActivateIrsensorWithFunctionLevel(HLERequestContext& ctx); | 56 | Result RunImageTransferProcessor( |
| 57 | Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid, | ||
| 58 | const Core::IrSensor::PackedImageTransferProcessorConfig& processor_config, | ||
| 59 | u64 transfer_memory_size, InCopyHandle<Kernel::KTransferMemory> t_mem); | ||
| 60 | |||
| 61 | Result GetImageTransferProcessorState( | ||
| 62 | Out<Core::IrSensor::ImageTransferProcessorState> out_state, | ||
| 63 | Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid, | ||
| 64 | OutBuffer<BufferAttr_HipcMapAlias> out_buffer_data); | ||
| 65 | |||
| 66 | Result RunTeraPluginProcessor(Core::IrSensor::IrCameraHandle camera_handle, | ||
| 67 | Core::IrSensor::PackedTeraPluginProcessorConfig processor_config, | ||
| 68 | ClientAppletResourceUserId aruid); | ||
| 69 | |||
| 70 | Result GetNpadIrCameraHandle(Out<Core::IrSensor::IrCameraHandle> out_camera_handle, | ||
| 71 | Core::HID::NpadIdType npad_id); | ||
| 72 | |||
| 73 | Result RunPointingProcessor( | ||
| 74 | Core::IrSensor::IrCameraHandle camera_handle, | ||
| 75 | const Core::IrSensor::PackedPointingProcessorConfig& processor_config, | ||
| 76 | ClientAppletResourceUserId aruid); | ||
| 77 | |||
| 78 | Result SuspendImageProcessor(Core::IrSensor::IrCameraHandle camera_handle, | ||
| 79 | ClientAppletResourceUserId aruid); | ||
| 80 | |||
| 81 | Result CheckFirmwareVersion(Core::IrSensor::IrCameraHandle camera_handle, | ||
| 82 | Core::IrSensor::PackedMcuVersion mcu_version, | ||
| 83 | ClientAppletResourceUserId aruid); | ||
| 84 | |||
| 85 | Result SetFunctionLevel(Core::IrSensor::IrCameraHandle camera_handle, | ||
| 86 | Core::IrSensor::PackedFunctionLevel function_level, | ||
| 87 | ClientAppletResourceUserId aruid); | ||
| 88 | |||
| 89 | Result RunImageTransferExProcessor( | ||
| 90 | Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid, | ||
| 91 | const Core::IrSensor::PackedImageTransferProcessorExConfig& processor_config, | ||
| 92 | u64 transfer_memory_size, InCopyHandle<Kernel::KTransferMemory> t_mem); | ||
| 93 | |||
| 94 | Result RunIrLedProcessor(Core::IrSensor::IrCameraHandle camera_handle, | ||
| 95 | Core::IrSensor::PackedIrLedProcessorConfig processor_config, | ||
| 96 | ClientAppletResourceUserId aruid); | ||
| 97 | |||
| 98 | Result StopImageProcessorAsync(Core::IrSensor::IrCameraHandle camera_handle, | ||
| 99 | ClientAppletResourceUserId aruid); | ||
| 100 | |||
| 101 | Result ActivateIrsensorWithFunctionLevel(Core::IrSensor::PackedFunctionLevel function_level, | ||
| 102 | ClientAppletResourceUserId aruid); | ||
| 56 | 103 | ||
| 57 | Result IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const; | 104 | Result IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const; |
| 105 | |||
| 58 | Core::IrSensor::DeviceFormat& GetIrCameraSharedMemoryDeviceEntry( | 106 | Core::IrSensor::DeviceFormat& GetIrCameraSharedMemoryDeviceEntry( |
| 59 | const Core::IrSensor::IrCameraHandle& camera_handle); | 107 | const Core::IrSensor::IrCameraHandle& camera_handle); |
| 60 | 108 | ||
diff --git a/src/core/hle/service/hle_ipc.cpp b/src/core/hle/service/hle_ipc.cpp index 50e1ed756..e0367e774 100644 --- a/src/core/hle/service/hle_ipc.cpp +++ b/src/core/hle/service/hle_ipc.cpp | |||
| @@ -299,8 +299,12 @@ Result HLERequestContext::WriteToOutgoingCommandBuffer() { | |||
| 299 | if (GetManager()->IsDomain()) { | 299 | if (GetManager()->IsDomain()) { |
| 300 | current_offset = domain_offset - static_cast<u32>(outgoing_domain_objects.size()); | 300 | current_offset = domain_offset - static_cast<u32>(outgoing_domain_objects.size()); |
| 301 | for (auto& object : outgoing_domain_objects) { | 301 | for (auto& object : outgoing_domain_objects) { |
| 302 | GetManager()->AppendDomainHandler(std::move(object)); | 302 | if (object) { |
| 303 | cmd_buf[current_offset++] = static_cast<u32_le>(GetManager()->DomainHandlerCount()); | 303 | GetManager()->AppendDomainHandler(std::move(object)); |
| 304 | cmd_buf[current_offset++] = static_cast<u32_le>(GetManager()->DomainHandlerCount()); | ||
| 305 | } else { | ||
| 306 | cmd_buf[current_offset++] = 0; | ||
| 307 | } | ||
| 304 | } | 308 | } |
| 305 | } | 309 | } |
| 306 | 310 | ||
diff --git a/src/core/hle/service/nvdrv/core/container.cpp b/src/core/hle/service/nvdrv/core/container.cpp index e89cca6f2..9edce03f6 100644 --- a/src/core/hle/service/nvdrv/core/container.cpp +++ b/src/core/hle/service/nvdrv/core/container.cpp | |||
| @@ -49,6 +49,7 @@ SessionId Container::OpenSession(Kernel::KProcess* process) { | |||
| 49 | continue; | 49 | continue; |
| 50 | } | 50 | } |
| 51 | if (session.process == process) { | 51 | if (session.process == process) { |
| 52 | session.ref_count++; | ||
| 52 | return session.id; | 53 | return session.id; |
| 53 | } | 54 | } |
| 54 | } | 55 | } |
| @@ -66,6 +67,7 @@ SessionId Container::OpenSession(Kernel::KProcess* process) { | |||
| 66 | } | 67 | } |
| 67 | auto& session = impl->sessions[new_id]; | 68 | auto& session = impl->sessions[new_id]; |
| 68 | session.is_active = true; | 69 | session.is_active = true; |
| 70 | session.ref_count = 1; | ||
| 69 | // Optimization | 71 | // Optimization |
| 70 | if (process->IsApplication()) { | 72 | if (process->IsApplication()) { |
| 71 | auto& page_table = process->GetPageTable().GetBasePageTable(); | 73 | auto& page_table = process->GetPageTable().GetBasePageTable(); |
| @@ -114,8 +116,11 @@ SessionId Container::OpenSession(Kernel::KProcess* process) { | |||
| 114 | 116 | ||
| 115 | void Container::CloseSession(SessionId session_id) { | 117 | void Container::CloseSession(SessionId session_id) { |
| 116 | std::scoped_lock lk(impl->session_guard); | 118 | std::scoped_lock lk(impl->session_guard); |
| 117 | impl->file.UnmapAllHandles(session_id); | ||
| 118 | auto& session = impl->sessions[session_id.id]; | 119 | auto& session = impl->sessions[session_id.id]; |
| 120 | if (--session.ref_count > 0) { | ||
| 121 | return; | ||
| 122 | } | ||
| 123 | impl->file.UnmapAllHandles(session_id); | ||
| 119 | auto& smmu = impl->host1x.MemoryManager(); | 124 | auto& smmu = impl->host1x.MemoryManager(); |
| 120 | if (session.has_preallocated_area) { | 125 | if (session.has_preallocated_area) { |
| 121 | const DAddr region_start = session.mapper->GetRegionStart(); | 126 | const DAddr region_start = session.mapper->GetRegionStart(); |
diff --git a/src/core/hle/service/nvdrv/core/container.h b/src/core/hle/service/nvdrv/core/container.h index b4d3938a8..f159ced09 100644 --- a/src/core/hle/service/nvdrv/core/container.h +++ b/src/core/hle/service/nvdrv/core/container.h | |||
| @@ -46,6 +46,7 @@ struct Session { | |||
| 46 | bool has_preallocated_area{}; | 46 | bool has_preallocated_area{}; |
| 47 | std::unique_ptr<HeapMapper> mapper{}; | 47 | std::unique_ptr<HeapMapper> mapper{}; |
| 48 | bool is_active{}; | 48 | bool is_active{}; |
| 49 | s32 ref_count{}; | ||
| 49 | }; | 50 | }; |
| 50 | 51 | ||
| 51 | class Container { | 52 | class Container { |
diff --git a/src/core/hle/service/nvdrv/core/heap_mapper.cpp b/src/core/hle/service/nvdrv/core/heap_mapper.cpp index 096dc5deb..af17e3e85 100644 --- a/src/core/hle/service/nvdrv/core/heap_mapper.cpp +++ b/src/core/hle/service/nvdrv/core/heap_mapper.cpp | |||
| @@ -3,110 +3,21 @@ | |||
| 3 | 3 | ||
| 4 | #include <mutex> | 4 | #include <mutex> |
| 5 | 5 | ||
| 6 | #include <boost/container/small_vector.hpp> | 6 | #include "common/range_sets.h" |
| 7 | #define BOOST_NO_MT | 7 | #include "common/range_sets.inc" |
| 8 | #include <boost/pool/detail/mutex.hpp> | ||
| 9 | #undef BOOST_NO_MT | ||
| 10 | #include <boost/icl/interval.hpp> | ||
| 11 | #include <boost/icl/interval_base_set.hpp> | ||
| 12 | #include <boost/icl/interval_set.hpp> | ||
| 13 | #include <boost/icl/split_interval_map.hpp> | ||
| 14 | #include <boost/pool/pool.hpp> | ||
| 15 | #include <boost/pool/pool_alloc.hpp> | ||
| 16 | #include <boost/pool/poolfwd.hpp> | ||
| 17 | |||
| 18 | #include "core/hle/service/nvdrv/core/heap_mapper.h" | 8 | #include "core/hle/service/nvdrv/core/heap_mapper.h" |
| 19 | #include "video_core/host1x/host1x.h" | 9 | #include "video_core/host1x/host1x.h" |
| 20 | 10 | ||
| 21 | namespace boost { | ||
| 22 | template <typename T> | ||
| 23 | class fast_pool_allocator<T, default_user_allocator_new_delete, details::pool::null_mutex, 4096, 0>; | ||
| 24 | } | ||
| 25 | |||
| 26 | namespace Service::Nvidia::NvCore { | 11 | namespace Service::Nvidia::NvCore { |
| 27 | 12 | ||
| 28 | using IntervalCompare = std::less<DAddr>; | ||
| 29 | using IntervalInstance = boost::icl::interval_type_default<DAddr, std::less>; | ||
| 30 | using IntervalAllocator = boost::fast_pool_allocator<DAddr>; | ||
| 31 | using IntervalSet = boost::icl::interval_set<DAddr>; | ||
| 32 | using IntervalType = typename IntervalSet::interval_type; | ||
| 33 | |||
| 34 | template <typename Type> | ||
| 35 | struct counter_add_functor : public boost::icl::identity_based_inplace_combine<Type> { | ||
| 36 | // types | ||
| 37 | typedef counter_add_functor<Type> type; | ||
| 38 | typedef boost::icl::identity_based_inplace_combine<Type> base_type; | ||
| 39 | |||
| 40 | // public member functions | ||
| 41 | void operator()(Type& current, const Type& added) const { | ||
| 42 | current += added; | ||
| 43 | if (current < base_type::identity_element()) { | ||
| 44 | current = base_type::identity_element(); | ||
| 45 | } | ||
| 46 | } | ||
| 47 | |||
| 48 | // public static functions | ||
| 49 | static void version(Type&){}; | ||
| 50 | }; | ||
| 51 | |||
| 52 | using OverlapCombine = counter_add_functor<int>; | ||
| 53 | using OverlapSection = boost::icl::inter_section<int>; | ||
| 54 | using OverlapCounter = boost::icl::split_interval_map<DAddr, int>; | ||
| 55 | |||
| 56 | struct HeapMapper::HeapMapperInternal { | 13 | struct HeapMapper::HeapMapperInternal { |
| 57 | HeapMapperInternal(Tegra::Host1x::Host1x& host1x) : device_memory{host1x.MemoryManager()} {} | 14 | HeapMapperInternal(Tegra::Host1x::Host1x& host1x) : m_device_memory{host1x.MemoryManager()} {} |
| 58 | ~HeapMapperInternal() = default; | 15 | ~HeapMapperInternal() = default; |
| 59 | 16 | ||
| 60 | template <typename Func> | 17 | Common::RangeSet<VAddr> m_temporary_set; |
| 61 | void ForEachInOverlapCounter(OverlapCounter& current_range, VAddr cpu_addr, u64 size, | 18 | Common::OverlapRangeSet<VAddr> m_mapped_ranges; |
| 62 | Func&& func) { | 19 | Tegra::MaxwellDeviceMemoryManager& m_device_memory; |
| 63 | const DAddr start_address = cpu_addr; | 20 | std::mutex m_guard; |
| 64 | const DAddr end_address = start_address + size; | ||
| 65 | const IntervalType search_interval{start_address, end_address}; | ||
| 66 | auto it = current_range.lower_bound(search_interval); | ||
| 67 | if (it == current_range.end()) { | ||
| 68 | return; | ||
| 69 | } | ||
| 70 | auto end_it = current_range.upper_bound(search_interval); | ||
| 71 | for (; it != end_it; it++) { | ||
| 72 | auto& inter = it->first; | ||
| 73 | DAddr inter_addr_end = inter.upper(); | ||
| 74 | DAddr inter_addr = inter.lower(); | ||
| 75 | if (inter_addr_end > end_address) { | ||
| 76 | inter_addr_end = end_address; | ||
| 77 | } | ||
| 78 | if (inter_addr < start_address) { | ||
| 79 | inter_addr = start_address; | ||
| 80 | } | ||
| 81 | func(inter_addr, inter_addr_end, it->second); | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | void RemoveEachInOverlapCounter(OverlapCounter& current_range, | ||
| 86 | const IntervalType search_interval, int subtract_value) { | ||
| 87 | bool any_removals = false; | ||
| 88 | current_range.add(std::make_pair(search_interval, subtract_value)); | ||
| 89 | do { | ||
| 90 | any_removals = false; | ||
| 91 | auto it = current_range.lower_bound(search_interval); | ||
| 92 | if (it == current_range.end()) { | ||
| 93 | return; | ||
| 94 | } | ||
| 95 | auto end_it = current_range.upper_bound(search_interval); | ||
| 96 | for (; it != end_it; it++) { | ||
| 97 | if (it->second <= 0) { | ||
| 98 | any_removals = true; | ||
| 99 | current_range.erase(it); | ||
| 100 | break; | ||
| 101 | } | ||
| 102 | } | ||
| 103 | } while (any_removals); | ||
| 104 | } | ||
| 105 | |||
| 106 | IntervalSet base_set; | ||
| 107 | OverlapCounter mapping_overlaps; | ||
| 108 | Tegra::MaxwellDeviceMemoryManager& device_memory; | ||
| 109 | std::mutex guard; | ||
| 110 | }; | 21 | }; |
| 111 | 22 | ||
| 112 | HeapMapper::HeapMapper(VAddr start_vaddress, DAddr start_daddress, size_t size, Core::Asid asid, | 23 | 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, | |||
| 116 | } | 27 | } |
| 117 | 28 | ||
| 118 | HeapMapper::~HeapMapper() { | 29 | HeapMapper::~HeapMapper() { |
| 119 | m_internal->device_memory.Unmap(m_daddress, m_size); | 30 | // Unmap whatever has been mapped. |
| 31 | m_internal->m_mapped_ranges.ForEach([this](VAddr start_addr, VAddr end_addr, s32 count) { | ||
| 32 | const size_t sub_size = end_addr - start_addr; | ||
| 33 | const size_t offset = start_addr - m_vaddress; | ||
| 34 | m_internal->m_device_memory.Unmap(m_daddress + offset, sub_size); | ||
| 35 | }); | ||
| 120 | } | 36 | } |
| 121 | 37 | ||
| 122 | DAddr HeapMapper::Map(VAddr start, size_t size) { | 38 | DAddr HeapMapper::Map(VAddr start, size_t size) { |
| 123 | std::scoped_lock lk(m_internal->guard); | 39 | std::scoped_lock lk(m_internal->m_guard); |
| 124 | m_internal->base_set.clear(); | 40 | // Add the mapping range to a temporary range set. |
| 125 | const IntervalType interval{start, start + size}; | 41 | m_internal->m_temporary_set.Clear(); |
| 126 | m_internal->base_set.insert(interval); | 42 | m_internal->m_temporary_set.Add(start, size); |
| 127 | m_internal->ForEachInOverlapCounter(m_internal->mapping_overlaps, start, size, | 43 | |
| 128 | [this](VAddr start_addr, VAddr end_addr, int) { | 44 | // Remove anything that's already mapped from the temporary range set. |
| 129 | const IntervalType other{start_addr, end_addr}; | 45 | m_internal->m_mapped_ranges.ForEachInRange( |
| 130 | m_internal->base_set.subtract(other); | 46 | start, size, [this](VAddr start_addr, VAddr end_addr, s32) { |
| 131 | }); | 47 | m_internal->m_temporary_set.Subtract(start_addr, end_addr - start_addr); |
| 132 | if (!m_internal->base_set.empty()) { | 48 | }); |
| 133 | auto it = m_internal->base_set.begin(); | 49 | |
| 134 | auto end_it = m_internal->base_set.end(); | 50 | // Map anything that has not been mapped yet. |
| 135 | for (; it != end_it; it++) { | 51 | m_internal->m_temporary_set.ForEach([this](VAddr start_addr, VAddr end_addr) { |
| 136 | const VAddr inter_addr_end = it->upper(); | 52 | const size_t sub_size = end_addr - start_addr; |
| 137 | const VAddr inter_addr = it->lower(); | 53 | const size_t offset = start_addr - m_vaddress; |
| 138 | const size_t offset = inter_addr - m_vaddress; | 54 | m_internal->m_device_memory.Map(m_daddress + offset, m_vaddress + offset, sub_size, m_asid); |
| 139 | const size_t sub_size = inter_addr_end - inter_addr; | 55 | }); |
| 140 | m_internal->device_memory.Map(m_daddress + offset, m_vaddress + offset, sub_size, | 56 | |
| 141 | m_asid); | 57 | // Add the mapping range to the split map, to register the map and overlaps. |
| 142 | } | 58 | m_internal->m_mapped_ranges.Add(start, size); |
| 143 | } | 59 | m_internal->m_temporary_set.Clear(); |
| 144 | m_internal->mapping_overlaps += std::make_pair(interval, 1); | 60 | return m_daddress + static_cast<DAddr>(start - m_vaddress); |
| 145 | m_internal->base_set.clear(); | ||
| 146 | return m_daddress + (start - m_vaddress); | ||
| 147 | } | 61 | } |
| 148 | 62 | ||
| 149 | void HeapMapper::Unmap(VAddr start, size_t size) { | 63 | void HeapMapper::Unmap(VAddr start, size_t size) { |
| 150 | std::scoped_lock lk(m_internal->guard); | 64 | std::scoped_lock lk(m_internal->m_guard); |
| 151 | m_internal->base_set.clear(); | 65 | |
| 152 | m_internal->ForEachInOverlapCounter(m_internal->mapping_overlaps, start, size, | 66 | // Just subtract the range and whatever is deleted, unmap it. |
| 153 | [this](VAddr start_addr, VAddr end_addr, int value) { | 67 | m_internal->m_mapped_ranges.Subtract(start, size, [this](VAddr start_addr, VAddr end_addr) { |
| 154 | if (value <= 1) { | 68 | const size_t sub_size = end_addr - start_addr; |
| 155 | const IntervalType other{start_addr, end_addr}; | 69 | const size_t offset = start_addr - m_vaddress; |
| 156 | m_internal->base_set.insert(other); | 70 | m_internal->m_device_memory.Unmap(m_daddress + offset, sub_size); |
| 157 | } | 71 | }); |
| 158 | }); | ||
| 159 | if (!m_internal->base_set.empty()) { | ||
| 160 | auto it = m_internal->base_set.begin(); | ||
| 161 | auto end_it = m_internal->base_set.end(); | ||
| 162 | for (; it != end_it; it++) { | ||
| 163 | const VAddr inter_addr_end = it->upper(); | ||
| 164 | const VAddr inter_addr = it->lower(); | ||
| 165 | const size_t offset = inter_addr - m_vaddress; | ||
| 166 | const size_t sub_size = inter_addr_end - inter_addr; | ||
| 167 | m_internal->device_memory.Unmap(m_daddress + offset, sub_size); | ||
| 168 | } | ||
| 169 | } | ||
| 170 | const IntervalType to_remove{start, start + size}; | ||
| 171 | m_internal->RemoveEachInOverlapCounter(m_internal->mapping_overlaps, to_remove, -1); | ||
| 172 | m_internal->base_set.clear(); | ||
| 173 | } | 72 | } |
| 174 | 73 | ||
| 175 | } // namespace Service::Nvidia::NvCore | 74 | } // namespace Service::Nvidia::NvCore |
diff --git a/src/core/hle/service/nvdrv/core/nvmap.cpp b/src/core/hle/service/nvdrv/core/nvmap.cpp index bc1c033c6..453cb5831 100644 --- a/src/core/hle/service/nvdrv/core/nvmap.cpp +++ b/src/core/hle/service/nvdrv/core/nvmap.cpp | |||
| @@ -333,9 +333,13 @@ void NvMap::UnmapAllHandles(NvCore::SessionId session_id) { | |||
| 333 | }(); | 333 | }(); |
| 334 | 334 | ||
| 335 | for (auto& [id, handle] : handles_copy) { | 335 | for (auto& [id, handle] : handles_copy) { |
| 336 | if (handle->session_id.id == session_id.id) { | 336 | { |
| 337 | FreeHandle(id, false); | 337 | std::scoped_lock lk{handle->mutex}; |
| 338 | if (handle->session_id.id != session_id.id || handle->dupes <= 0) { | ||
| 339 | continue; | ||
| 340 | } | ||
| 338 | } | 341 | } |
| 342 | FreeHandle(id, false); | ||
| 339 | } | 343 | } |
| 340 | } | 344 | } |
| 341 | 345 | ||
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp index abe95303e..995646e25 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp | |||
| @@ -15,6 +15,22 @@ | |||
| 15 | 15 | ||
| 16 | namespace Service::Nvidia::Devices { | 16 | namespace Service::Nvidia::Devices { |
| 17 | 17 | ||
| 18 | namespace { | ||
| 19 | |||
| 20 | Tegra::BlendMode ConvertBlending(Service::Nvnflinger::LayerBlending blending) { | ||
| 21 | switch (blending) { | ||
| 22 | case Service::Nvnflinger::LayerBlending::None: | ||
| 23 | default: | ||
| 24 | return Tegra::BlendMode::Opaque; | ||
| 25 | case Service::Nvnflinger::LayerBlending::Premultiplied: | ||
| 26 | return Tegra::BlendMode::Premultiplied; | ||
| 27 | case Service::Nvnflinger::LayerBlending::Coverage: | ||
| 28 | return Tegra::BlendMode::Coverage; | ||
| 29 | } | ||
| 30 | } | ||
| 31 | |||
| 32 | } // namespace | ||
| 33 | |||
| 18 | nvdisp_disp0::nvdisp_disp0(Core::System& system_, NvCore::Container& core) | 34 | nvdisp_disp0::nvdisp_disp0(Core::System& system_, NvCore::Container& core) |
| 19 | : nvdevice{system_}, container{core}, nvmap{core.GetNvMapFile()} {} | 35 | : nvdevice{system_}, container{core}, nvmap{core.GetNvMapFile()} {} |
| 20 | nvdisp_disp0::~nvdisp_disp0() = default; | 36 | nvdisp_disp0::~nvdisp_disp0() = default; |
| @@ -56,6 +72,7 @@ void nvdisp_disp0::Composite(std::span<const Nvnflinger::HwcLayer> sorted_layers | |||
| 56 | .pixel_format = layer.format, | 72 | .pixel_format = layer.format, |
| 57 | .transform_flags = layer.transform, | 73 | .transform_flags = layer.transform, |
| 58 | .crop_rect = layer.crop_rect, | 74 | .crop_rect = layer.crop_rect, |
| 75 | .blending = ConvertBlending(layer.blending), | ||
| 59 | }); | 76 | }); |
| 60 | 77 | ||
| 61 | for (size_t i = 0; i < layer.acquire_fence.num_fences; i++) { | 78 | for (size_t i = 0; i < layer.acquire_fence.num_fences; i++) { |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp index e6646ba04..68fe38874 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp | |||
| @@ -123,6 +123,8 @@ NvResult nvhost_as_gpu::AllocAsEx(IoctlAllocAsEx& params) { | |||
| 123 | vm.va_range_end = params.va_range_end; | 123 | vm.va_range_end = params.va_range_end; |
| 124 | } | 124 | } |
| 125 | 125 | ||
| 126 | const u64 max_big_page_bits = Common::Log2Ceil64(vm.va_range_end); | ||
| 127 | |||
| 126 | const auto start_pages{static_cast<u32>(vm.va_range_start >> VM::PAGE_SIZE_BITS)}; | 128 | const auto start_pages{static_cast<u32>(vm.va_range_start >> VM::PAGE_SIZE_BITS)}; |
| 127 | const auto end_pages{static_cast<u32>(vm.va_range_split >> VM::PAGE_SIZE_BITS)}; | 129 | const auto end_pages{static_cast<u32>(vm.va_range_split >> VM::PAGE_SIZE_BITS)}; |
| 128 | vm.small_page_allocator = std::make_shared<VM::Allocator>(start_pages, end_pages); | 130 | vm.small_page_allocator = std::make_shared<VM::Allocator>(start_pages, end_pages); |
| @@ -132,8 +134,8 @@ NvResult nvhost_as_gpu::AllocAsEx(IoctlAllocAsEx& params) { | |||
| 132 | static_cast<u32>((vm.va_range_end - vm.va_range_split) >> vm.big_page_size_bits)}; | 134 | static_cast<u32>((vm.va_range_end - vm.va_range_split) >> vm.big_page_size_bits)}; |
| 133 | vm.big_page_allocator = std::make_unique<VM::Allocator>(start_big_pages, end_big_pages); | 135 | vm.big_page_allocator = std::make_unique<VM::Allocator>(start_big_pages, end_big_pages); |
| 134 | 136 | ||
| 135 | gmmu = std::make_shared<Tegra::MemoryManager>(system, 40, vm.big_page_size_bits, | 137 | gmmu = std::make_shared<Tegra::MemoryManager>(system, max_big_page_bits, vm.va_range_split, |
| 136 | VM::PAGE_SIZE_BITS); | 138 | vm.big_page_size_bits, VM::PAGE_SIZE_BITS); |
| 137 | system.GPU().InitAddressSpace(*gmmu); | 139 | system.GPU().InitAddressSpace(*gmmu); |
| 138 | vm.initialised = true; | 140 | vm.initialised = true; |
| 139 | 141 | ||
diff --git a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp index e71652cdf..90f7248a0 100644 --- a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp +++ b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp | |||
| @@ -14,24 +14,20 @@ | |||
| 14 | #include "core/hle/service/nvnflinger/ui/graphic_buffer.h" | 14 | #include "core/hle/service/nvnflinger/ui/graphic_buffer.h" |
| 15 | #include "core/hle/service/vi/layer/vi_layer.h" | 15 | #include "core/hle/service/vi/layer/vi_layer.h" |
| 16 | #include "core/hle/service/vi/vi_results.h" | 16 | #include "core/hle/service/vi/vi_results.h" |
| 17 | #include "video_core/gpu.h" | ||
| 18 | #include "video_core/host1x/host1x.h" | ||
| 17 | 19 | ||
| 18 | namespace Service::Nvnflinger { | 20 | namespace Service::Nvnflinger { |
| 19 | 21 | ||
| 20 | namespace { | 22 | namespace { |
| 21 | 23 | ||
| 22 | Result AllocateIoForProcessAddressSpace(Common::ProcessAddress* out_map_address, | 24 | Result AllocateSharedBufferMemory(std::unique_ptr<Kernel::KPageGroup>* out_page_group, |
| 23 | std::unique_ptr<Kernel::KPageGroup>* out_page_group, | 25 | Core::System& system, u32 size) { |
| 24 | Core::System& system, u32 size) { | ||
| 25 | using Core::Memory::YUZU_PAGESIZE; | 26 | using Core::Memory::YUZU_PAGESIZE; |
| 26 | 27 | ||
| 27 | // Allocate memory for the system shared buffer. | 28 | // Allocate memory for the system shared buffer. |
| 28 | // FIXME: Because the gmmu can only point to cpu addresses, we need | ||
| 29 | // to map this in the application space to allow it to be used. | ||
| 30 | // FIXME: Add proper smmu emulation. | ||
| 31 | // FIXME: This memory belongs to vi's .data section. | 29 | // FIXME: This memory belongs to vi's .data section. |
| 32 | auto& kernel = system.Kernel(); | 30 | auto& kernel = system.Kernel(); |
| 33 | auto* process = system.ApplicationProcess(); | ||
| 34 | auto& page_table = process->GetPageTable(); | ||
| 35 | 31 | ||
| 36 | // Hold a temporary page group reference while we try to map it. | 32 | // Hold a temporary page group reference while we try to map it. |
| 37 | auto pg = std::make_unique<Kernel::KPageGroup>( | 33 | auto pg = std::make_unique<Kernel::KPageGroup>( |
| @@ -43,6 +39,30 @@ Result AllocateIoForProcessAddressSpace(Common::ProcessAddress* out_map_address, | |||
| 43 | Kernel::KMemoryManager::EncodeOption(Kernel::KMemoryManager::Pool::Secure, | 39 | Kernel::KMemoryManager::EncodeOption(Kernel::KMemoryManager::Pool::Secure, |
| 44 | Kernel::KMemoryManager::Direction::FromBack))); | 40 | Kernel::KMemoryManager::Direction::FromBack))); |
| 45 | 41 | ||
| 42 | // Fill the output data with red. | ||
| 43 | for (auto& block : *pg) { | ||
| 44 | u32* start = system.DeviceMemory().GetPointer<u32>(block.GetAddress()); | ||
| 45 | u32* end = system.DeviceMemory().GetPointer<u32>(block.GetAddress() + block.GetSize()); | ||
| 46 | |||
| 47 | for (; start < end; start++) { | ||
| 48 | *start = 0xFF0000FF; | ||
| 49 | } | ||
| 50 | } | ||
| 51 | |||
| 52 | // Return the mapped page group. | ||
| 53 | *out_page_group = std::move(pg); | ||
| 54 | |||
| 55 | // We succeeded. | ||
| 56 | R_SUCCEED(); | ||
| 57 | } | ||
| 58 | |||
| 59 | Result MapSharedBufferIntoProcessAddressSpace(Common::ProcessAddress* out_map_address, | ||
| 60 | std::unique_ptr<Kernel::KPageGroup>& pg, | ||
| 61 | Kernel::KProcess* process, Core::System& system) { | ||
| 62 | using Core::Memory::YUZU_PAGESIZE; | ||
| 63 | |||
| 64 | auto& page_table = process->GetPageTable(); | ||
| 65 | |||
| 46 | // Get bounds of where mapping is possible. | 66 | // Get bounds of where mapping is possible. |
| 47 | const VAddr alias_code_begin = GetInteger(page_table.GetAliasCodeRegionStart()); | 67 | const VAddr alias_code_begin = GetInteger(page_table.GetAliasCodeRegionStart()); |
| 48 | const VAddr alias_code_size = page_table.GetAliasCodeRegionSize() / YUZU_PAGESIZE; | 68 | const VAddr alias_code_size = page_table.GetAliasCodeRegionSize() / YUZU_PAGESIZE; |
| @@ -64,9 +84,6 @@ Result AllocateIoForProcessAddressSpace(Common::ProcessAddress* out_map_address, | |||
| 64 | // Return failure, if necessary | 84 | // Return failure, if necessary |
| 65 | R_UNLESS(i < 64, res); | 85 | R_UNLESS(i < 64, res); |
| 66 | 86 | ||
| 67 | // Return the mapped page group. | ||
| 68 | *out_page_group = std::move(pg); | ||
| 69 | |||
| 70 | // We succeeded. | 87 | // We succeeded. |
| 71 | R_SUCCEED(); | 88 | R_SUCCEED(); |
| 72 | } | 89 | } |
| @@ -135,6 +152,13 @@ Result AllocateHandleForBuffer(u32* out_handle, Nvidia::Module& nvdrv, Nvidia::D | |||
| 135 | R_RETURN(AllocNvMapHandle(*nvmap, *out_handle, buffer, size, nvmap_fd)); | 152 | R_RETURN(AllocNvMapHandle(*nvmap, *out_handle, buffer, size, nvmap_fd)); |
| 136 | } | 153 | } |
| 137 | 154 | ||
| 155 | void FreeHandle(u32 handle, Nvidia::Module& nvdrv, Nvidia::DeviceFD nvmap_fd) { | ||
| 156 | auto nvmap = nvdrv.GetDevice<Nvidia::Devices::nvmap>(nvmap_fd); | ||
| 157 | ASSERT(nvmap != nullptr); | ||
| 158 | |||
| 159 | R_ASSERT(FreeNvMapHandle(*nvmap, handle, nvmap_fd)); | ||
| 160 | } | ||
| 161 | |||
| 138 | constexpr auto SharedBufferBlockLinearFormat = android::PixelFormat::Rgba8888; | 162 | constexpr auto SharedBufferBlockLinearFormat = android::PixelFormat::Rgba8888; |
| 139 | constexpr u32 SharedBufferBlockLinearBpp = 4; | 163 | constexpr u32 SharedBufferBlockLinearBpp = 4; |
| 140 | 164 | ||
| @@ -186,53 +210,97 @@ FbShareBufferManager::FbShareBufferManager(Core::System& system, Nvnflinger& fli | |||
| 186 | 210 | ||
| 187 | FbShareBufferManager::~FbShareBufferManager() = default; | 211 | FbShareBufferManager::~FbShareBufferManager() = default; |
| 188 | 212 | ||
| 189 | Result FbShareBufferManager::Initialize(u64* out_buffer_id, u64* out_layer_id, u64 display_id) { | 213 | Result FbShareBufferManager::Initialize(Kernel::KProcess* owner_process, u64* out_buffer_id, |
| 214 | u64* out_layer_handle, u64 display_id, | ||
| 215 | LayerBlending blending) { | ||
| 190 | std::scoped_lock lk{m_guard}; | 216 | std::scoped_lock lk{m_guard}; |
| 191 | 217 | ||
| 192 | // Ensure we have not already created a buffer. | 218 | // Ensure we haven't already created. |
| 193 | R_UNLESS(m_buffer_id == 0, VI::ResultOperationFailed); | 219 | const u64 aruid = owner_process->GetProcessId(); |
| 220 | R_UNLESS(!m_sessions.contains(aruid), VI::ResultPermissionDenied); | ||
| 221 | |||
| 222 | // Allocate memory for the shared buffer if needed. | ||
| 223 | if (!m_buffer_page_group) { | ||
| 224 | R_TRY(AllocateSharedBufferMemory(std::addressof(m_buffer_page_group), m_system, | ||
| 225 | SharedBufferSize)); | ||
| 226 | |||
| 227 | // Record buffer id. | ||
| 228 | m_buffer_id = m_next_buffer_id++; | ||
| 229 | |||
| 230 | // Record display id. | ||
| 231 | m_display_id = display_id; | ||
| 232 | } | ||
| 233 | |||
| 234 | // Map into process. | ||
| 235 | Common::ProcessAddress map_address{}; | ||
| 236 | R_TRY(MapSharedBufferIntoProcessAddressSpace(std::addressof(map_address), m_buffer_page_group, | ||
| 237 | owner_process, m_system)); | ||
| 194 | 238 | ||
| 195 | // Allocate memory and space for the shared buffer. | 239 | // Create new session. |
| 196 | Common::ProcessAddress map_address; | 240 | auto [it, was_emplaced] = m_sessions.emplace(aruid, FbShareSession{}); |
| 197 | R_TRY(AllocateIoForProcessAddressSpace(std::addressof(map_address), | 241 | auto& session = it->second; |
| 198 | std::addressof(m_buffer_page_group), m_system, | ||
| 199 | SharedBufferSize)); | ||
| 200 | 242 | ||
| 201 | auto& container = m_nvdrv->GetContainer(); | 243 | auto& container = m_nvdrv->GetContainer(); |
| 202 | m_session_id = container.OpenSession(m_system.ApplicationProcess()); | 244 | session.session_id = container.OpenSession(owner_process); |
| 203 | m_nvmap_fd = m_nvdrv->Open("/dev/nvmap", m_session_id); | 245 | session.nvmap_fd = m_nvdrv->Open("/dev/nvmap", session.session_id); |
| 204 | 246 | ||
| 205 | // Create an nvmap handle for the buffer and assign the memory to it. | 247 | // Create an nvmap handle for the buffer and assign the memory to it. |
| 206 | R_TRY(AllocateHandleForBuffer(std::addressof(m_buffer_nvmap_handle), *m_nvdrv, m_nvmap_fd, | 248 | R_TRY(AllocateHandleForBuffer(std::addressof(session.buffer_nvmap_handle), *m_nvdrv, |
| 207 | map_address, SharedBufferSize)); | 249 | session.nvmap_fd, map_address, SharedBufferSize)); |
| 208 | |||
| 209 | // Record the display id. | ||
| 210 | m_display_id = display_id; | ||
| 211 | 250 | ||
| 212 | // Create and open a layer for the display. | 251 | // Create and open a layer for the display. |
| 213 | m_layer_id = m_flinger.CreateLayer(m_display_id).value(); | 252 | session.layer_id = m_flinger.CreateLayer(m_display_id, blending).value(); |
| 214 | m_flinger.OpenLayer(m_layer_id); | 253 | m_flinger.OpenLayer(session.layer_id); |
| 215 | |||
| 216 | // Set up the buffer. | ||
| 217 | m_buffer_id = m_next_buffer_id++; | ||
| 218 | 254 | ||
| 219 | // Get the layer. | 255 | // Get the layer. |
| 220 | VI::Layer* layer = m_flinger.FindLayer(m_display_id, m_layer_id); | 256 | VI::Layer* layer = m_flinger.FindLayer(m_display_id, session.layer_id); |
| 221 | ASSERT(layer != nullptr); | 257 | ASSERT(layer != nullptr); |
| 222 | 258 | ||
| 223 | // Get the producer and set preallocated buffers. | 259 | // Get the producer and set preallocated buffers. |
| 224 | auto& producer = layer->GetBufferQueue(); | 260 | auto& producer = layer->GetBufferQueue(); |
| 225 | MakeGraphicBuffer(producer, 0, m_buffer_nvmap_handle); | 261 | MakeGraphicBuffer(producer, 0, session.buffer_nvmap_handle); |
| 226 | MakeGraphicBuffer(producer, 1, m_buffer_nvmap_handle); | 262 | MakeGraphicBuffer(producer, 1, session.buffer_nvmap_handle); |
| 227 | 263 | ||
| 228 | // Assign outputs. | 264 | // Assign outputs. |
| 229 | *out_buffer_id = m_buffer_id; | 265 | *out_buffer_id = m_buffer_id; |
| 230 | *out_layer_id = m_layer_id; | 266 | *out_layer_handle = session.layer_id; |
| 231 | 267 | ||
| 232 | // We succeeded. | 268 | // We succeeded. |
| 233 | R_SUCCEED(); | 269 | R_SUCCEED(); |
| 234 | } | 270 | } |
| 235 | 271 | ||
| 272 | void FbShareBufferManager::Finalize(Kernel::KProcess* owner_process) { | ||
| 273 | std::scoped_lock lk{m_guard}; | ||
| 274 | |||
| 275 | if (m_buffer_id == 0) { | ||
| 276 | return; | ||
| 277 | } | ||
| 278 | |||
| 279 | const u64 aruid = owner_process->GetProcessId(); | ||
| 280 | const auto it = m_sessions.find(aruid); | ||
| 281 | if (it == m_sessions.end()) { | ||
| 282 | return; | ||
| 283 | } | ||
| 284 | |||
| 285 | auto& session = it->second; | ||
| 286 | |||
| 287 | // Destroy the layer. | ||
| 288 | m_flinger.DestroyLayer(session.layer_id); | ||
| 289 | |||
| 290 | // Close nvmap handle. | ||
| 291 | FreeHandle(session.buffer_nvmap_handle, *m_nvdrv, session.nvmap_fd); | ||
| 292 | |||
| 293 | // Close nvmap device. | ||
| 294 | m_nvdrv->Close(session.nvmap_fd); | ||
| 295 | |||
| 296 | // Close session. | ||
| 297 | auto& container = m_nvdrv->GetContainer(); | ||
| 298 | container.CloseSession(session.session_id); | ||
| 299 | |||
| 300 | // Erase. | ||
| 301 | m_sessions.erase(it); | ||
| 302 | } | ||
| 303 | |||
| 236 | Result FbShareBufferManager::GetSharedBufferMemoryHandleId(u64* out_buffer_size, | 304 | Result FbShareBufferManager::GetSharedBufferMemoryHandleId(u64* out_buffer_size, |
| 237 | s32* out_nvmap_handle, | 305 | s32* out_nvmap_handle, |
| 238 | SharedMemoryPoolLayout* out_pool_layout, | 306 | SharedMemoryPoolLayout* out_pool_layout, |
| @@ -242,17 +310,18 @@ Result FbShareBufferManager::GetSharedBufferMemoryHandleId(u64* out_buffer_size, | |||
| 242 | 310 | ||
| 243 | R_UNLESS(m_buffer_id > 0, VI::ResultNotFound); | 311 | R_UNLESS(m_buffer_id > 0, VI::ResultNotFound); |
| 244 | R_UNLESS(buffer_id == m_buffer_id, VI::ResultNotFound); | 312 | R_UNLESS(buffer_id == m_buffer_id, VI::ResultNotFound); |
| 313 | R_UNLESS(m_sessions.contains(applet_resource_user_id), VI::ResultNotFound); | ||
| 245 | 314 | ||
| 246 | *out_pool_layout = SharedBufferPoolLayout; | 315 | *out_pool_layout = SharedBufferPoolLayout; |
| 247 | *out_buffer_size = SharedBufferSize; | 316 | *out_buffer_size = SharedBufferSize; |
| 248 | *out_nvmap_handle = m_buffer_nvmap_handle; | 317 | *out_nvmap_handle = m_sessions[applet_resource_user_id].buffer_nvmap_handle; |
| 249 | 318 | ||
| 250 | R_SUCCEED(); | 319 | R_SUCCEED(); |
| 251 | } | 320 | } |
| 252 | 321 | ||
| 253 | Result FbShareBufferManager::GetLayerFromId(VI::Layer** out_layer, u64 layer_id) { | 322 | Result FbShareBufferManager::GetLayerFromId(VI::Layer** out_layer, u64 layer_id) { |
| 254 | // Ensure the layer id is valid. | 323 | // Ensure the layer id is valid. |
| 255 | R_UNLESS(m_layer_id > 0 && layer_id == m_layer_id, VI::ResultNotFound); | 324 | R_UNLESS(layer_id > 0, VI::ResultNotFound); |
| 256 | 325 | ||
| 257 | // Get the layer. | 326 | // Get the layer. |
| 258 | VI::Layer* layer = m_flinger.FindLayer(m_display_id, layer_id); | 327 | VI::Layer* layer = m_flinger.FindLayer(m_display_id, layer_id); |
| @@ -309,6 +378,10 @@ Result FbShareBufferManager::PresentSharedFrameBuffer(android::Fence fence, | |||
| 309 | android::Status::NoError, | 378 | android::Status::NoError, |
| 310 | VI::ResultOperationFailed); | 379 | VI::ResultOperationFailed); |
| 311 | 380 | ||
| 381 | ON_RESULT_FAILURE { | ||
| 382 | producer.CancelBuffer(static_cast<s32>(slot), fence); | ||
| 383 | }; | ||
| 384 | |||
| 312 | // Queue the buffer to the producer. | 385 | // Queue the buffer to the producer. |
| 313 | android::QueueBufferInput input{}; | 386 | android::QueueBufferInput input{}; |
| 314 | android::QueueBufferOutput output{}; | 387 | android::QueueBufferOutput output{}; |
| @@ -342,4 +415,33 @@ Result FbShareBufferManager::GetSharedFrameBufferAcquirableEvent(Kernel::KReadab | |||
| 342 | R_SUCCEED(); | 415 | R_SUCCEED(); |
| 343 | } | 416 | } |
| 344 | 417 | ||
| 418 | Result FbShareBufferManager::WriteAppletCaptureBuffer(bool* out_was_written, s32* out_layer_index) { | ||
| 419 | std::vector<u8> capture_buffer(m_system.GPU().GetAppletCaptureBuffer()); | ||
| 420 | Common::ScratchBuffer<u32> scratch; | ||
| 421 | |||
| 422 | // TODO: this could be optimized | ||
| 423 | s64 e = -1280 * 768 * 4; | ||
| 424 | for (auto& block : *m_buffer_page_group) { | ||
| 425 | u8* start = m_system.DeviceMemory().GetPointer<u8>(block.GetAddress()); | ||
| 426 | u8* end = m_system.DeviceMemory().GetPointer<u8>(block.GetAddress() + block.GetSize()); | ||
| 427 | |||
| 428 | for (; start < end; start++) { | ||
| 429 | *start = 0; | ||
| 430 | |||
| 431 | if (e >= 0 && e < static_cast<s64>(capture_buffer.size())) { | ||
| 432 | *start = capture_buffer[e]; | ||
| 433 | } | ||
| 434 | e++; | ||
| 435 | } | ||
| 436 | |||
| 437 | m_system.GPU().Host1x().MemoryManager().ApplyOpOnPointer(start, scratch, [&](DAddr addr) { | ||
| 438 | m_system.GPU().InvalidateRegion(addr, end - start); | ||
| 439 | }); | ||
| 440 | } | ||
| 441 | |||
| 442 | *out_was_written = true; | ||
| 443 | *out_layer_index = 1; | ||
| 444 | R_SUCCEED(); | ||
| 445 | } | ||
| 446 | |||
| 345 | } // namespace Service::Nvnflinger | 447 | } // namespace Service::Nvnflinger |
diff --git a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.h b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.h index 033bf4bbe..b79a7d23a 100644 --- a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.h +++ b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.h | |||
| @@ -3,9 +3,12 @@ | |||
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <map> | ||
| 7 | |||
| 6 | #include "common/math_util.h" | 8 | #include "common/math_util.h" |
| 7 | #include "core/hle/service/nvdrv/core/container.h" | 9 | #include "core/hle/service/nvdrv/core/container.h" |
| 8 | #include "core/hle/service/nvdrv/nvdata.h" | 10 | #include "core/hle/service/nvdrv/nvdata.h" |
| 11 | #include "core/hle/service/nvnflinger/hwc_layer.h" | ||
| 9 | #include "core/hle/service/nvnflinger/nvnflinger.h" | 12 | #include "core/hle/service/nvnflinger/nvnflinger.h" |
| 10 | #include "core/hle/service/nvnflinger/ui/fence.h" | 13 | #include "core/hle/service/nvnflinger/ui/fence.h" |
| 11 | 14 | ||
| @@ -29,13 +32,18 @@ struct SharedMemoryPoolLayout { | |||
| 29 | }; | 32 | }; |
| 30 | static_assert(sizeof(SharedMemoryPoolLayout) == 0x188, "SharedMemoryPoolLayout has wrong size"); | 33 | static_assert(sizeof(SharedMemoryPoolLayout) == 0x188, "SharedMemoryPoolLayout has wrong size"); |
| 31 | 34 | ||
| 35 | struct FbShareSession; | ||
| 36 | |||
| 32 | class FbShareBufferManager final { | 37 | class FbShareBufferManager final { |
| 33 | public: | 38 | public: |
| 34 | explicit FbShareBufferManager(Core::System& system, Nvnflinger& flinger, | 39 | explicit FbShareBufferManager(Core::System& system, Nvnflinger& flinger, |
| 35 | std::shared_ptr<Nvidia::Module> nvdrv); | 40 | std::shared_ptr<Nvidia::Module> nvdrv); |
| 36 | ~FbShareBufferManager(); | 41 | ~FbShareBufferManager(); |
| 37 | 42 | ||
| 38 | Result Initialize(u64* out_buffer_id, u64* out_layer_handle, u64 display_id); | 43 | Result Initialize(Kernel::KProcess* owner_process, u64* out_buffer_id, u64* out_layer_handle, |
| 44 | u64 display_id, LayerBlending blending); | ||
| 45 | void Finalize(Kernel::KProcess* owner_process); | ||
| 46 | |||
| 39 | Result GetSharedBufferMemoryHandleId(u64* out_buffer_size, s32* out_nvmap_handle, | 47 | Result GetSharedBufferMemoryHandleId(u64* out_buffer_size, s32* out_nvmap_handle, |
| 40 | SharedMemoryPoolLayout* out_pool_layout, u64 buffer_id, | 48 | SharedMemoryPoolLayout* out_pool_layout, u64 buffer_id, |
| 41 | u64 applet_resource_user_id); | 49 | u64 applet_resource_user_id); |
| @@ -45,6 +53,8 @@ public: | |||
| 45 | u32 transform, s32 swap_interval, u64 layer_id, s64 slot); | 53 | u32 transform, s32 swap_interval, u64 layer_id, s64 slot); |
| 46 | Result GetSharedFrameBufferAcquirableEvent(Kernel::KReadableEvent** out_event, u64 layer_id); | 54 | Result GetSharedFrameBufferAcquirableEvent(Kernel::KReadableEvent** out_event, u64 layer_id); |
| 47 | 55 | ||
| 56 | Result WriteAppletCaptureBuffer(bool* out_was_written, s32* out_layer_index); | ||
| 57 | |||
| 48 | private: | 58 | private: |
| 49 | Result GetLayerFromId(VI::Layer** out_layer, u64 layer_id); | 59 | Result GetLayerFromId(VI::Layer** out_layer, u64 layer_id); |
| 50 | 60 | ||
| @@ -52,11 +62,8 @@ private: | |||
| 52 | u64 m_next_buffer_id = 1; | 62 | u64 m_next_buffer_id = 1; |
| 53 | u64 m_display_id = 0; | 63 | u64 m_display_id = 0; |
| 54 | u64 m_buffer_id = 0; | 64 | u64 m_buffer_id = 0; |
| 55 | u64 m_layer_id = 0; | ||
| 56 | u32 m_buffer_nvmap_handle = 0; | ||
| 57 | SharedMemoryPoolLayout m_pool_layout = {}; | 65 | SharedMemoryPoolLayout m_pool_layout = {}; |
| 58 | Nvidia::DeviceFD m_nvmap_fd = {}; | 66 | std::map<u64, FbShareSession> m_sessions; |
| 59 | Nvidia::NvCore::SessionId m_session_id = {}; | ||
| 60 | std::unique_ptr<Kernel::KPageGroup> m_buffer_page_group; | 67 | std::unique_ptr<Kernel::KPageGroup> m_buffer_page_group; |
| 61 | 68 | ||
| 62 | std::mutex m_guard; | 69 | std::mutex m_guard; |
| @@ -65,4 +72,11 @@ private: | |||
| 65 | std::shared_ptr<Nvidia::Module> m_nvdrv; | 72 | std::shared_ptr<Nvidia::Module> m_nvdrv; |
| 66 | }; | 73 | }; |
| 67 | 74 | ||
| 75 | struct FbShareSession { | ||
| 76 | Nvidia::DeviceFD nvmap_fd = {}; | ||
| 77 | Nvidia::NvCore::SessionId session_id = {}; | ||
| 78 | u64 layer_id = {}; | ||
| 79 | u32 buffer_nvmap_handle = 0; | ||
| 80 | }; | ||
| 81 | |||
| 68 | } // namespace Service::Nvnflinger | 82 | } // namespace Service::Nvnflinger |
diff --git a/src/core/hle/service/nvnflinger/hardware_composer.cpp b/src/core/hle/service/nvnflinger/hardware_composer.cpp index ba2b5c28c..be7eb97a3 100644 --- a/src/core/hle/service/nvnflinger/hardware_composer.cpp +++ b/src/core/hle/service/nvnflinger/hardware_composer.cpp | |||
| @@ -86,6 +86,7 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, VI::Display& display, | |||
| 86 | .height = igbp_buffer.Height(), | 86 | .height = igbp_buffer.Height(), |
| 87 | .stride = igbp_buffer.Stride(), | 87 | .stride = igbp_buffer.Stride(), |
| 88 | .z_index = 0, | 88 | .z_index = 0, |
| 89 | .blending = layer.GetBlending(), | ||
| 89 | .transform = static_cast<android::BufferTransformFlags>(item.transform), | 90 | .transform = static_cast<android::BufferTransformFlags>(item.transform), |
| 90 | .crop_rect = item.crop, | 91 | .crop_rect = item.crop, |
| 91 | .acquire_fence = item.fence, | 92 | .acquire_fence = item.fence, |
diff --git a/src/core/hle/service/nvnflinger/hwc_layer.h b/src/core/hle/service/nvnflinger/hwc_layer.h index 3af668a25..f71a5d822 100644 --- a/src/core/hle/service/nvnflinger/hwc_layer.h +++ b/src/core/hle/service/nvnflinger/hwc_layer.h | |||
| @@ -11,6 +11,18 @@ | |||
| 11 | 11 | ||
| 12 | namespace Service::Nvnflinger { | 12 | namespace Service::Nvnflinger { |
| 13 | 13 | ||
| 14 | // hwc_layer_t::blending values | ||
| 15 | enum class LayerBlending : u32 { | ||
| 16 | // No blending | ||
| 17 | None = 0x100, | ||
| 18 | |||
| 19 | // ONE / ONE_MINUS_SRC_ALPHA | ||
| 20 | Premultiplied = 0x105, | ||
| 21 | |||
| 22 | // SRC_ALPHA / ONE_MINUS_SRC_ALPHA | ||
| 23 | Coverage = 0x405, | ||
| 24 | }; | ||
| 25 | |||
| 14 | struct HwcLayer { | 26 | struct HwcLayer { |
| 15 | u32 buffer_handle; | 27 | u32 buffer_handle; |
| 16 | u32 offset; | 28 | u32 offset; |
| @@ -19,6 +31,7 @@ struct HwcLayer { | |||
| 19 | u32 height; | 31 | u32 height; |
| 20 | u32 stride; | 32 | u32 stride; |
| 21 | s32 z_index; | 33 | s32 z_index; |
| 34 | LayerBlending blending; | ||
| 22 | android::BufferTransformFlags transform; | 35 | android::BufferTransformFlags transform; |
| 23 | Common::Rectangle<int> crop_rect; | 36 | Common::Rectangle<int> crop_rect; |
| 24 | android::Fence acquire_fence; | 37 | android::Fence acquire_fence; |
diff --git a/src/core/hle/service/nvnflinger/nvnflinger.cpp b/src/core/hle/service/nvnflinger/nvnflinger.cpp index d8ba89d43..687ccc9f9 100644 --- a/src/core/hle/service/nvnflinger/nvnflinger.cpp +++ b/src/core/hle/service/nvnflinger/nvnflinger.cpp | |||
| @@ -157,7 +157,7 @@ bool Nvnflinger::CloseDisplay(u64 display_id) { | |||
| 157 | return true; | 157 | return true; |
| 158 | } | 158 | } |
| 159 | 159 | ||
| 160 | std::optional<u64> Nvnflinger::CreateLayer(u64 display_id) { | 160 | std::optional<u64> Nvnflinger::CreateLayer(u64 display_id, LayerBlending blending) { |
| 161 | const auto lock_guard = Lock(); | 161 | const auto lock_guard = Lock(); |
| 162 | auto* const display = FindDisplay(display_id); | 162 | auto* const display = FindDisplay(display_id); |
| 163 | 163 | ||
| @@ -166,13 +166,14 @@ std::optional<u64> Nvnflinger::CreateLayer(u64 display_id) { | |||
| 166 | } | 166 | } |
| 167 | 167 | ||
| 168 | const u64 layer_id = next_layer_id++; | 168 | const u64 layer_id = next_layer_id++; |
| 169 | CreateLayerAtId(*display, layer_id); | 169 | CreateLayerAtId(*display, layer_id, blending); |
| 170 | return layer_id; | 170 | return layer_id; |
| 171 | } | 171 | } |
| 172 | 172 | ||
| 173 | void Nvnflinger::CreateLayerAtId(VI::Display& display, u64 layer_id) { | 173 | void Nvnflinger::CreateLayerAtId(VI::Display& display, u64 layer_id, LayerBlending blending) { |
| 174 | const auto buffer_id = next_buffer_queue_id++; | 174 | const auto buffer_id = next_buffer_queue_id++; |
| 175 | display.CreateLayer(layer_id, buffer_id, nvdrv->container); | 175 | display.CreateLayer(layer_id, buffer_id, nvdrv->container); |
| 176 | display.FindLayer(layer_id)->SetBlending(blending); | ||
| 176 | } | 177 | } |
| 177 | 178 | ||
| 178 | bool Nvnflinger::OpenLayer(u64 layer_id) { | 179 | bool Nvnflinger::OpenLayer(u64 layer_id) { |
diff --git a/src/core/hle/service/nvnflinger/nvnflinger.h b/src/core/hle/service/nvnflinger/nvnflinger.h index c984d55a0..4cf4f069d 100644 --- a/src/core/hle/service/nvnflinger/nvnflinger.h +++ b/src/core/hle/service/nvnflinger/nvnflinger.h | |||
| @@ -15,6 +15,7 @@ | |||
| 15 | #include "common/thread.h" | 15 | #include "common/thread.h" |
| 16 | #include "core/hle/result.h" | 16 | #include "core/hle/result.h" |
| 17 | #include "core/hle/service/kernel_helpers.h" | 17 | #include "core/hle/service/kernel_helpers.h" |
| 18 | #include "core/hle/service/nvnflinger/hwc_layer.h" | ||
| 18 | 19 | ||
| 19 | namespace Common { | 20 | namespace Common { |
| 20 | class Event; | 21 | class Event; |
| @@ -72,7 +73,8 @@ public: | |||
| 72 | /// Creates a layer on the specified display and returns the layer ID. | 73 | /// Creates a layer on the specified display and returns the layer ID. |
| 73 | /// | 74 | /// |
| 74 | /// If an invalid display ID is specified, then an empty optional is returned. | 75 | /// If an invalid display ID is specified, then an empty optional is returned. |
| 75 | [[nodiscard]] std::optional<u64> CreateLayer(u64 display_id); | 76 | [[nodiscard]] std::optional<u64> CreateLayer(u64 display_id, |
| 77 | LayerBlending blending = LayerBlending::None); | ||
| 76 | 78 | ||
| 77 | /// Opens a layer on all displays for the given layer ID. | 79 | /// Opens a layer on all displays for the given layer ID. |
| 78 | bool OpenLayer(u64 layer_id); | 80 | bool OpenLayer(u64 layer_id); |
| @@ -128,7 +130,7 @@ private: | |||
| 128 | [[nodiscard]] VI::Layer* FindLayer(u64 display_id, u64 layer_id); | 130 | [[nodiscard]] VI::Layer* FindLayer(u64 display_id, u64 layer_id); |
| 129 | 131 | ||
| 130 | /// Creates a layer with the specified layer ID in the desired display. | 132 | /// Creates a layer with the specified layer ID in the desired display. |
| 131 | void CreateLayerAtId(VI::Display& display, u64 layer_id); | 133 | void CreateLayerAtId(VI::Display& display, u64 layer_id, LayerBlending blending); |
| 132 | 134 | ||
| 133 | void SplitVSync(std::stop_token stop_token); | 135 | void SplitVSync(std::stop_token stop_token); |
| 134 | 136 | ||
diff --git a/src/core/hle/service/event.cpp b/src/core/hle/service/os/event.cpp index 375660d72..ec52c17fd 100644 --- a/src/core/hle/service/event.cpp +++ b/src/core/hle/service/os/event.cpp | |||
| @@ -2,8 +2,8 @@ | |||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include "core/hle/kernel/k_event.h" | 4 | #include "core/hle/kernel/k_event.h" |
| 5 | #include "core/hle/service/event.h" | ||
| 6 | #include "core/hle/service/kernel_helpers.h" | 5 | #include "core/hle/service/kernel_helpers.h" |
| 6 | #include "core/hle/service/os/event.h" | ||
| 7 | 7 | ||
| 8 | namespace Service { | 8 | namespace Service { |
| 9 | 9 | ||
diff --git a/src/core/hle/service/event.h b/src/core/hle/service/os/event.h index cdbc4635a..cdbc4635a 100644 --- a/src/core/hle/service/event.h +++ b/src/core/hle/service/os/event.h | |||
diff --git a/src/core/hle/service/os/multi_wait.cpp b/src/core/hle/service/os/multi_wait.cpp new file mode 100644 index 000000000..7b80d28be --- /dev/null +++ b/src/core/hle/service/os/multi_wait.cpp | |||
| @@ -0,0 +1,59 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/hle/kernel/k_hardware_timer.h" | ||
| 5 | #include "core/hle/kernel/k_synchronization_object.h" | ||
| 6 | #include "core/hle/kernel/kernel.h" | ||
| 7 | #include "core/hle/kernel/svc_common.h" | ||
| 8 | #include "core/hle/service/os/multi_wait.h" | ||
| 9 | |||
| 10 | namespace Service { | ||
| 11 | |||
| 12 | MultiWait::MultiWait() = default; | ||
| 13 | MultiWait::~MultiWait() = default; | ||
| 14 | |||
| 15 | MultiWaitHolder* MultiWait::WaitAny(Kernel::KernelCore& kernel) { | ||
| 16 | return this->TimedWaitImpl(kernel, -1); | ||
| 17 | } | ||
| 18 | |||
| 19 | MultiWaitHolder* MultiWait::TryWaitAny(Kernel::KernelCore& kernel) { | ||
| 20 | return this->TimedWaitImpl(kernel, 0); | ||
| 21 | } | ||
| 22 | |||
| 23 | MultiWaitHolder* MultiWait::TimedWaitAny(Kernel::KernelCore& kernel, s64 timeout_ns) { | ||
| 24 | return this->TimedWaitImpl(kernel, kernel.HardwareTimer().GetTick() + timeout_ns); | ||
| 25 | } | ||
| 26 | |||
| 27 | MultiWaitHolder* MultiWait::TimedWaitImpl(Kernel::KernelCore& kernel, s64 timeout_tick) { | ||
| 28 | std::array<MultiWaitHolder*, Kernel::Svc::ArgumentHandleCountMax> holders{}; | ||
| 29 | std::array<Kernel::KSynchronizationObject*, Kernel::Svc::ArgumentHandleCountMax> objects{}; | ||
| 30 | |||
| 31 | s32 out_index = -1; | ||
| 32 | s32 num_objects = 0; | ||
| 33 | |||
| 34 | for (auto it = m_wait_list.begin(); it != m_wait_list.end(); it++) { | ||
| 35 | ASSERT(num_objects < Kernel::Svc::ArgumentHandleCountMax); | ||
| 36 | holders[num_objects] = std::addressof(*it); | ||
| 37 | objects[num_objects] = it->GetNativeHandle(); | ||
| 38 | num_objects++; | ||
| 39 | } | ||
| 40 | |||
| 41 | Kernel::KSynchronizationObject::Wait(kernel, std::addressof(out_index), objects.data(), | ||
| 42 | num_objects, timeout_tick); | ||
| 43 | |||
| 44 | if (out_index == -1) { | ||
| 45 | return nullptr; | ||
| 46 | } else { | ||
| 47 | return holders[out_index]; | ||
| 48 | } | ||
| 49 | } | ||
| 50 | |||
| 51 | void MultiWait::MoveAll(MultiWait* other) { | ||
| 52 | while (!other->m_wait_list.empty()) { | ||
| 53 | MultiWaitHolder& holder = other->m_wait_list.front(); | ||
| 54 | holder.UnlinkFromMultiWait(); | ||
| 55 | holder.LinkToMultiWait(this); | ||
| 56 | } | ||
| 57 | } | ||
| 58 | |||
| 59 | } // namespace Service | ||
diff --git a/src/core/hle/service/os/multi_wait.h b/src/core/hle/service/os/multi_wait.h new file mode 100644 index 000000000..340c611b5 --- /dev/null +++ b/src/core/hle/service/os/multi_wait.h | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "core/hle/service/os/multi_wait_holder.h" | ||
| 7 | |||
| 8 | namespace Kernel { | ||
| 9 | class KernelCore; | ||
| 10 | } | ||
| 11 | |||
| 12 | namespace Service { | ||
| 13 | |||
| 14 | class MultiWait final { | ||
| 15 | public: | ||
| 16 | explicit MultiWait(); | ||
| 17 | ~MultiWait(); | ||
| 18 | |||
| 19 | public: | ||
| 20 | MultiWaitHolder* WaitAny(Kernel::KernelCore& kernel); | ||
| 21 | MultiWaitHolder* TryWaitAny(Kernel::KernelCore& kernel); | ||
| 22 | MultiWaitHolder* TimedWaitAny(Kernel::KernelCore& kernel, s64 timeout_ns); | ||
| 23 | // TODO: SdkReplyAndReceive? | ||
| 24 | |||
| 25 | void MoveAll(MultiWait* other); | ||
| 26 | |||
| 27 | private: | ||
| 28 | MultiWaitHolder* TimedWaitImpl(Kernel::KernelCore& kernel, s64 timeout_tick); | ||
| 29 | |||
| 30 | private: | ||
| 31 | friend class MultiWaitHolder; | ||
| 32 | using ListType = Common::IntrusiveListMemberTraits<&MultiWaitHolder::m_list_node>::ListType; | ||
| 33 | ListType m_wait_list{}; | ||
| 34 | }; | ||
| 35 | |||
| 36 | } // namespace Service | ||
diff --git a/src/core/hle/service/os/multi_wait_holder.cpp b/src/core/hle/service/os/multi_wait_holder.cpp new file mode 100644 index 000000000..01efa045b --- /dev/null +++ b/src/core/hle/service/os/multi_wait_holder.cpp | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/hle/service/os/multi_wait.h" | ||
| 5 | #include "core/hle/service/os/multi_wait_holder.h" | ||
| 6 | |||
| 7 | namespace Service { | ||
| 8 | |||
| 9 | void MultiWaitHolder::LinkToMultiWait(MultiWait* multi_wait) { | ||
| 10 | if (m_multi_wait != nullptr) { | ||
| 11 | UNREACHABLE(); | ||
| 12 | } | ||
| 13 | |||
| 14 | m_multi_wait = multi_wait; | ||
| 15 | m_multi_wait->m_wait_list.push_back(*this); | ||
| 16 | } | ||
| 17 | |||
| 18 | void MultiWaitHolder::UnlinkFromMultiWait() { | ||
| 19 | if (m_multi_wait) { | ||
| 20 | m_multi_wait->m_wait_list.erase(m_multi_wait->m_wait_list.iterator_to(*this)); | ||
| 21 | m_multi_wait = nullptr; | ||
| 22 | } | ||
| 23 | } | ||
| 24 | |||
| 25 | } // namespace Service | ||
diff --git a/src/core/hle/service/os/multi_wait_holder.h b/src/core/hle/service/os/multi_wait_holder.h new file mode 100644 index 000000000..646395a3f --- /dev/null +++ b/src/core/hle/service/os/multi_wait_holder.h | |||
| @@ -0,0 +1,44 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "common/intrusive_list.h" | ||
| 7 | |||
| 8 | namespace Kernel { | ||
| 9 | class KSynchronizationObject; | ||
| 10 | } // namespace Kernel | ||
| 11 | |||
| 12 | namespace Service { | ||
| 13 | |||
| 14 | class MultiWait; | ||
| 15 | |||
| 16 | class MultiWaitHolder { | ||
| 17 | public: | ||
| 18 | explicit MultiWaitHolder(Kernel::KSynchronizationObject* native_handle) | ||
| 19 | : m_native_handle(native_handle) {} | ||
| 20 | |||
| 21 | void LinkToMultiWait(MultiWait* multi_wait); | ||
| 22 | void UnlinkFromMultiWait(); | ||
| 23 | |||
| 24 | void SetUserData(uintptr_t user_data) { | ||
| 25 | m_user_data = user_data; | ||
| 26 | } | ||
| 27 | |||
| 28 | uintptr_t GetUserData() const { | ||
| 29 | return m_user_data; | ||
| 30 | } | ||
| 31 | |||
| 32 | Kernel::KSynchronizationObject* GetNativeHandle() const { | ||
| 33 | return m_native_handle; | ||
| 34 | } | ||
| 35 | |||
| 36 | private: | ||
| 37 | friend class MultiWait; | ||
| 38 | Common::IntrusiveListNode m_list_node{}; | ||
| 39 | MultiWait* m_multi_wait{}; | ||
| 40 | Kernel::KSynchronizationObject* m_native_handle{}; | ||
| 41 | uintptr_t m_user_data{}; | ||
| 42 | }; | ||
| 43 | |||
| 44 | } // namespace Service | ||
diff --git a/src/core/hle/service/os/multi_wait_utils.h b/src/core/hle/service/os/multi_wait_utils.h new file mode 100644 index 000000000..96d3a10f3 --- /dev/null +++ b/src/core/hle/service/os/multi_wait_utils.h | |||
| @@ -0,0 +1,109 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "core/hle/service/os/multi_wait.h" | ||
| 7 | |||
| 8 | namespace Service { | ||
| 9 | |||
| 10 | namespace impl { | ||
| 11 | |||
| 12 | class AutoMultiWaitHolder { | ||
| 13 | private: | ||
| 14 | MultiWaitHolder m_holder; | ||
| 15 | |||
| 16 | public: | ||
| 17 | template <typename T> | ||
| 18 | explicit AutoMultiWaitHolder(MultiWait* multi_wait, T&& arg) : m_holder(arg) { | ||
| 19 | m_holder.LinkToMultiWait(multi_wait); | ||
| 20 | } | ||
| 21 | |||
| 22 | ~AutoMultiWaitHolder() { | ||
| 23 | m_holder.UnlinkFromMultiWait(); | ||
| 24 | } | ||
| 25 | |||
| 26 | std::pair<MultiWaitHolder*, int> ConvertResult(const std::pair<MultiWaitHolder*, int> result, | ||
| 27 | int index) { | ||
| 28 | if (result.first == std::addressof(m_holder)) { | ||
| 29 | return std::make_pair(static_cast<MultiWaitHolder*>(nullptr), index); | ||
| 30 | } else { | ||
| 31 | return result; | ||
| 32 | } | ||
| 33 | } | ||
| 34 | }; | ||
| 35 | |||
| 36 | using WaitAnyFunction = decltype(&MultiWait::WaitAny); | ||
| 37 | |||
| 38 | inline std::pair<MultiWaitHolder*, int> WaitAnyImpl(Kernel::KernelCore& kernel, | ||
| 39 | MultiWait* multi_wait, WaitAnyFunction func, | ||
| 40 | int) { | ||
| 41 | return std::pair<MultiWaitHolder*, int>((multi_wait->*func)(kernel), -1); | ||
| 42 | } | ||
| 43 | |||
| 44 | template <typename T, typename... Args> | ||
| 45 | inline std::pair<MultiWaitHolder*, int> WaitAnyImpl(Kernel::KernelCore& kernel, | ||
| 46 | MultiWait* multi_wait, WaitAnyFunction func, | ||
| 47 | int index, T&& x, Args&&... args) { | ||
| 48 | AutoMultiWaitHolder holder(multi_wait, std::forward<T>(x)); | ||
| 49 | return holder.ConvertResult( | ||
| 50 | WaitAnyImpl(kernel, multi_wait, func, index + 1, std::forward<Args>(args)...), index); | ||
| 51 | } | ||
| 52 | |||
| 53 | template <typename... Args> | ||
| 54 | inline std::pair<MultiWaitHolder*, int> WaitAnyImpl(Kernel::KernelCore& kernel, | ||
| 55 | MultiWait* multi_wait, WaitAnyFunction func, | ||
| 56 | Args&&... args) { | ||
| 57 | return WaitAnyImpl(kernel, multi_wait, func, 0, std::forward<Args>(args)...); | ||
| 58 | } | ||
| 59 | |||
| 60 | template <typename... Args> | ||
| 61 | inline std::pair<MultiWaitHolder*, int> WaitAnyImpl(Kernel::KernelCore& kernel, | ||
| 62 | WaitAnyFunction func, Args&&... args) { | ||
| 63 | MultiWait temp_multi_wait; | ||
| 64 | return WaitAnyImpl(kernel, std::addressof(temp_multi_wait), func, 0, | ||
| 65 | std::forward<Args>(args)...); | ||
| 66 | } | ||
| 67 | |||
| 68 | class NotBoolButInt { | ||
| 69 | public: | ||
| 70 | constexpr NotBoolButInt(int v) : m_value(v) {} | ||
| 71 | constexpr operator int() const { | ||
| 72 | return m_value; | ||
| 73 | } | ||
| 74 | explicit operator bool() const = delete; | ||
| 75 | |||
| 76 | private: | ||
| 77 | int m_value; | ||
| 78 | }; | ||
| 79 | |||
| 80 | } // namespace impl | ||
| 81 | |||
| 82 | template <typename... Args> | ||
| 83 | requires(sizeof...(Args) > 0) | ||
| 84 | inline std::pair<MultiWaitHolder*, int> WaitAny(Kernel::KernelCore& kernel, MultiWait* multi_wait, | ||
| 85 | Args&&... args) { | ||
| 86 | return impl::WaitAnyImpl(kernel, &MultiWait::WaitAny, multi_wait, std::forward<Args>(args)...); | ||
| 87 | } | ||
| 88 | |||
| 89 | template <typename... Args> | ||
| 90 | requires(sizeof...(Args) > 0) | ||
| 91 | inline int WaitAny(Kernel::KernelCore& kernel, Args&&... args) { | ||
| 92 | return impl::WaitAnyImpl(kernel, &MultiWait::WaitAny, std::forward<Args>(args)...).second; | ||
| 93 | } | ||
| 94 | |||
| 95 | template <typename... Args> | ||
| 96 | requires(sizeof...(Args) > 0) | ||
| 97 | inline std::pair<MultiWaitHolder*, int> TryWaitAny(Kernel::KernelCore& kernel, | ||
| 98 | MultiWait* multi_wait, Args&&... args) { | ||
| 99 | return impl::WaitAnyImpl(kernel, &MultiWait::TryWaitAny, multi_wait, | ||
| 100 | std::forward<Args>(args)...); | ||
| 101 | } | ||
| 102 | |||
| 103 | template <typename... Args> | ||
| 104 | requires(sizeof...(Args) > 0) | ||
| 105 | inline impl::NotBoolButInt TryWaitAny(Kernel::KernelCore& kernel, Args&&... args) { | ||
| 106 | return impl::WaitAnyImpl(kernel, &MultiWait::TryWaitAny, std::forward<Args>(args)...).second; | ||
| 107 | } | ||
| 108 | |||
| 109 | } // namespace Service | ||
diff --git a/src/core/hle/service/mutex.cpp b/src/core/hle/service/os/mutex.cpp index b0ff71d1b..6009f4866 100644 --- a/src/core/hle/service/mutex.cpp +++ b/src/core/hle/service/os/mutex.cpp | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | #include "core/core.h" | 4 | #include "core/core.h" |
| 5 | #include "core/hle/kernel/k_event.h" | 5 | #include "core/hle/kernel/k_event.h" |
| 6 | #include "core/hle/kernel/k_synchronization_object.h" | 6 | #include "core/hle/kernel/k_synchronization_object.h" |
| 7 | #include "core/hle/service/mutex.h" | 7 | #include "core/hle/service/os/mutex.h" |
| 8 | 8 | ||
| 9 | namespace Service { | 9 | namespace Service { |
| 10 | 10 | ||
diff --git a/src/core/hle/service/mutex.h b/src/core/hle/service/os/mutex.h index 95ac9b117..95ac9b117 100644 --- a/src/core/hle/service/mutex.h +++ b/src/core/hle/service/os/mutex.h | |||
diff --git a/src/core/hle/service/server_manager.cpp b/src/core/hle/service/server_manager.cpp index 8ef49387d..8c7f94c8c 100644 --- a/src/core/hle/service/server_manager.cpp +++ b/src/core/hle/service/server_manager.cpp | |||
| @@ -20,50 +20,91 @@ | |||
| 20 | 20 | ||
| 21 | namespace Service { | 21 | namespace Service { |
| 22 | 22 | ||
| 23 | constexpr size_t MaximumWaitObjects = 0x40; | 23 | enum class UserDataTag { |
| 24 | |||
| 25 | enum HandleType { | ||
| 26 | Port, | 24 | Port, |
| 27 | Session, | 25 | Session, |
| 28 | DeferEvent, | 26 | DeferEvent, |
| 29 | Event, | ||
| 30 | }; | 27 | }; |
| 31 | 28 | ||
| 32 | ServerManager::ServerManager(Core::System& system) : m_system{system}, m_serve_mutex{system} { | 29 | class Port : public MultiWaitHolder, public Common::IntrusiveListBaseNode<Port> { |
| 30 | public: | ||
| 31 | explicit Port(Kernel::KServerPort* server_port, SessionRequestHandlerFactory&& handler_factory) | ||
| 32 | : MultiWaitHolder(server_port), m_handler_factory(std::move(handler_factory)) { | ||
| 33 | this->SetUserData(static_cast<uintptr_t>(UserDataTag::Port)); | ||
| 34 | } | ||
| 35 | |||
| 36 | ~Port() { | ||
| 37 | this->GetNativeHandle()->Close(); | ||
| 38 | } | ||
| 39 | |||
| 40 | SessionRequestHandlerPtr CreateHandler() { | ||
| 41 | return m_handler_factory(); | ||
| 42 | } | ||
| 43 | |||
| 44 | private: | ||
| 45 | const SessionRequestHandlerFactory m_handler_factory; | ||
| 46 | }; | ||
| 47 | |||
| 48 | class Session : public MultiWaitHolder, public Common::IntrusiveListBaseNode<Session> { | ||
| 49 | public: | ||
| 50 | explicit Session(Kernel::KServerSession* server_session, | ||
| 51 | std::shared_ptr<SessionRequestManager>&& manager) | ||
| 52 | : MultiWaitHolder(server_session), m_manager(std::move(manager)) { | ||
| 53 | this->SetUserData(static_cast<uintptr_t>(UserDataTag::Session)); | ||
| 54 | } | ||
| 55 | |||
| 56 | ~Session() { | ||
| 57 | this->GetNativeHandle()->Close(); | ||
| 58 | } | ||
| 59 | |||
| 60 | std::shared_ptr<SessionRequestManager>& GetManager() { | ||
| 61 | return m_manager; | ||
| 62 | } | ||
| 63 | |||
| 64 | std::shared_ptr<HLERequestContext>& GetContext() { | ||
| 65 | return m_context; | ||
| 66 | } | ||
| 67 | |||
| 68 | private: | ||
| 69 | std::shared_ptr<SessionRequestManager> m_manager; | ||
| 70 | std::shared_ptr<HLERequestContext> m_context; | ||
| 71 | }; | ||
| 72 | |||
| 73 | ServerManager::ServerManager(Core::System& system) : m_system{system}, m_selection_mutex{system} { | ||
| 33 | // Initialize event. | 74 | // Initialize event. |
| 34 | m_event = Kernel::KEvent::Create(system.Kernel()); | 75 | m_wakeup_event = Kernel::KEvent::Create(system.Kernel()); |
| 35 | m_event->Initialize(nullptr); | 76 | m_wakeup_event->Initialize(nullptr); |
| 36 | 77 | ||
| 37 | // Register event. | 78 | // Register event. |
| 38 | Kernel::KEvent::Register(system.Kernel(), m_event); | 79 | Kernel::KEvent::Register(system.Kernel(), m_wakeup_event); |
| 80 | |||
| 81 | // Link to holder. | ||
| 82 | m_wakeup_holder.emplace(std::addressof(m_wakeup_event->GetReadableEvent())); | ||
| 83 | m_wakeup_holder->LinkToMultiWait(std::addressof(m_deferred_list)); | ||
| 39 | } | 84 | } |
| 40 | 85 | ||
| 41 | ServerManager::~ServerManager() { | 86 | ServerManager::~ServerManager() { |
| 42 | // Signal stop. | 87 | // Signal stop. |
| 43 | m_stop_source.request_stop(); | 88 | m_stop_source.request_stop(); |
| 44 | m_event->Signal(); | 89 | m_wakeup_event->Signal(); |
| 45 | 90 | ||
| 46 | // Wait for processing to stop. | 91 | // Wait for processing to stop. |
| 47 | m_stopped.Wait(); | 92 | m_stopped.Wait(); |
| 48 | m_threads.clear(); | 93 | m_threads.clear(); |
| 49 | 94 | ||
| 50 | // Clean up server ports. | 95 | // Clean up ports. |
| 51 | for (const auto& [port, handler] : m_ports) { | 96 | for (auto it = m_servers.begin(); it != m_servers.end(); it = m_servers.erase(it)) { |
| 52 | port->Close(); | 97 | delete std::addressof(*it); |
| 53 | } | 98 | } |
| 54 | 99 | ||
| 55 | // Clean up sessions. | 100 | // Clean up sessions. |
| 56 | for (const auto& [session, manager] : m_sessions) { | 101 | for (auto it = m_sessions.begin(); it != m_sessions.end(); it = m_sessions.erase(it)) { |
| 57 | session->Close(); | 102 | delete std::addressof(*it); |
| 58 | } | ||
| 59 | |||
| 60 | for (const auto& request : m_deferrals) { | ||
| 61 | request.session->Close(); | ||
| 62 | } | 103 | } |
| 63 | 104 | ||
| 64 | // Close event. | 105 | // Close wakeup event. |
| 65 | m_event->GetReadableEvent().Close(); | 106 | m_wakeup_event->GetReadableEvent().Close(); |
| 66 | m_event->Close(); | 107 | m_wakeup_event->Close(); |
| 67 | 108 | ||
| 68 | if (m_deferral_event) { | 109 | if (m_deferral_event) { |
| 69 | m_deferral_event->GetReadableEvent().Close(); | 110 | m_deferral_event->GetReadableEvent().Close(); |
| @@ -75,19 +116,19 @@ void ServerManager::RunServer(std::unique_ptr<ServerManager>&& server_manager) { | |||
| 75 | server_manager->m_system.RunServer(std::move(server_manager)); | 116 | server_manager->m_system.RunServer(std::move(server_manager)); |
| 76 | } | 117 | } |
| 77 | 118 | ||
| 78 | Result ServerManager::RegisterSession(Kernel::KServerSession* session, | 119 | Result ServerManager::RegisterSession(Kernel::KServerSession* server_session, |
| 79 | std::shared_ptr<SessionRequestManager> manager) { | 120 | std::shared_ptr<SessionRequestManager> manager) { |
| 80 | ASSERT(m_sessions.size() + m_ports.size() < MaximumWaitObjects); | ||
| 81 | |||
| 82 | // We are taking ownership of the server session, so don't open it. | 121 | // We are taking ownership of the server session, so don't open it. |
| 122 | auto* session = new Session(server_session, std::move(manager)); | ||
| 123 | |||
| 83 | // Begin tracking the server session. | 124 | // Begin tracking the server session. |
| 84 | { | 125 | { |
| 85 | std::scoped_lock ll{m_list_mutex}; | 126 | std::scoped_lock ll{m_deferred_list_mutex}; |
| 86 | m_sessions.emplace(session, std::move(manager)); | 127 | m_sessions.push_back(*session); |
| 87 | } | 128 | } |
| 88 | 129 | ||
| 89 | // Signal the wakeup event. | 130 | // Register to wait on the session. |
| 90 | m_event->Signal(); | 131 | this->LinkToDeferredList(session); |
| 91 | 132 | ||
| 92 | R_SUCCEED(); | 133 | R_SUCCEED(); |
| 93 | } | 134 | } |
| @@ -95,21 +136,22 @@ Result ServerManager::RegisterSession(Kernel::KServerSession* session, | |||
| 95 | Result ServerManager::RegisterNamedService(const std::string& service_name, | 136 | Result ServerManager::RegisterNamedService(const std::string& service_name, |
| 96 | SessionRequestHandlerFactory&& handler_factory, | 137 | SessionRequestHandlerFactory&& handler_factory, |
| 97 | u32 max_sessions) { | 138 | u32 max_sessions) { |
| 98 | ASSERT(m_sessions.size() + m_ports.size() < MaximumWaitObjects); | ||
| 99 | |||
| 100 | // Add the new server to sm: and get the moved server port. | 139 | // Add the new server to sm: and get the moved server port. |
| 101 | Kernel::KServerPort* server_port{}; | 140 | Kernel::KServerPort* server_port{}; |
| 102 | R_ASSERT(m_system.ServiceManager().RegisterService(std::addressof(server_port), service_name, | 141 | R_ASSERT(m_system.ServiceManager().RegisterService(std::addressof(server_port), service_name, |
| 103 | max_sessions, handler_factory)); | 142 | max_sessions, handler_factory)); |
| 104 | 143 | ||
| 144 | // We are taking ownership of the server port, so don't open it. | ||
| 145 | auto* server = new Port(server_port, std::move(handler_factory)); | ||
| 146 | |||
| 105 | // Begin tracking the server port. | 147 | // Begin tracking the server port. |
| 106 | { | 148 | { |
| 107 | std::scoped_lock ll{m_list_mutex}; | 149 | std::scoped_lock ll{m_deferred_list_mutex}; |
| 108 | m_ports.emplace(server_port, std::move(handler_factory)); | 150 | m_servers.push_back(*server); |
| 109 | } | 151 | } |
| 110 | 152 | ||
| 111 | // Signal the wakeup event. | 153 | // Register to wait on the server port. |
| 112 | m_event->Signal(); | 154 | this->LinkToDeferredList(server); |
| 113 | 155 | ||
| 114 | R_SUCCEED(); | 156 | R_SUCCEED(); |
| 115 | } | 157 | } |
| @@ -127,8 +169,6 @@ Result ServerManager::RegisterNamedService(const std::string& service_name, | |||
| 127 | Result ServerManager::ManageNamedPort(const std::string& service_name, | 169 | Result ServerManager::ManageNamedPort(const std::string& service_name, |
| 128 | SessionRequestHandlerFactory&& handler_factory, | 170 | SessionRequestHandlerFactory&& handler_factory, |
| 129 | u32 max_sessions) { | 171 | u32 max_sessions) { |
| 130 | ASSERT(m_sessions.size() + m_ports.size() < MaximumWaitObjects); | ||
| 131 | |||
| 132 | // Create a new port. | 172 | // Create a new port. |
| 133 | auto* port = Kernel::KPort::Create(m_system.Kernel()); | 173 | auto* port = Kernel::KPort::Create(m_system.Kernel()); |
| 134 | port->Initialize(max_sessions, false, 0); | 174 | port->Initialize(max_sessions, false, 0); |
| @@ -149,12 +189,18 @@ Result ServerManager::ManageNamedPort(const std::string& service_name, | |||
| 149 | // Open a new reference to the server port. | 189 | // Open a new reference to the server port. |
| 150 | port->GetServerPort().Open(); | 190 | port->GetServerPort().Open(); |
| 151 | 191 | ||
| 152 | // Begin tracking the server port. | 192 | // Transfer ownership into a new port object. |
| 193 | auto* server = new Port(std::addressof(port->GetServerPort()), std::move(handler_factory)); | ||
| 194 | |||
| 195 | // Begin tracking the port. | ||
| 153 | { | 196 | { |
| 154 | std::scoped_lock ll{m_list_mutex}; | 197 | std::scoped_lock ll{m_deferred_list_mutex}; |
| 155 | m_ports.emplace(std::addressof(port->GetServerPort()), std::move(handler_factory)); | 198 | m_servers.push_back(*server); |
| 156 | } | 199 | } |
| 157 | 200 | ||
| 201 | // Register to wait on the port. | ||
| 202 | this->LinkToDeferredList(server); | ||
| 203 | |||
| 158 | // We succeeded. | 204 | // We succeeded. |
| 159 | R_SUCCEED(); | 205 | R_SUCCEED(); |
| 160 | } | 206 | } |
| @@ -173,6 +219,11 @@ Result ServerManager::ManageDeferral(Kernel::KEvent** out_event) { | |||
| 173 | // Set the output. | 219 | // Set the output. |
| 174 | *out_event = m_deferral_event; | 220 | *out_event = m_deferral_event; |
| 175 | 221 | ||
| 222 | // Register to wait on the event. | ||
| 223 | m_deferral_holder.emplace(std::addressof(m_deferral_event->GetReadableEvent())); | ||
| 224 | m_deferral_holder->SetUserData(static_cast<uintptr_t>(UserDataTag::DeferEvent)); | ||
| 225 | this->LinkToDeferredList(std::addressof(*m_deferral_holder)); | ||
| 226 | |||
| 176 | // We succeeded. | 227 | // We succeeded. |
| 177 | R_SUCCEED(); | 228 | R_SUCCEED(); |
| 178 | } | 229 | } |
| @@ -191,270 +242,185 @@ Result ServerManager::LoopProcess() { | |||
| 191 | R_RETURN(this->LoopProcessImpl()); | 242 | R_RETURN(this->LoopProcessImpl()); |
| 192 | } | 243 | } |
| 193 | 244 | ||
| 194 | Result ServerManager::LoopProcessImpl() { | 245 | void ServerManager::LinkToDeferredList(MultiWaitHolder* holder) { |
| 195 | while (!m_stop_source.stop_requested()) { | 246 | // Link. |
| 196 | R_TRY(this->WaitAndProcessImpl()); | 247 | { |
| 248 | std::scoped_lock lk{m_deferred_list_mutex}; | ||
| 249 | holder->LinkToMultiWait(std::addressof(m_deferred_list)); | ||
| 197 | } | 250 | } |
| 198 | 251 | ||
| 199 | R_SUCCEED(); | 252 | // Signal the wakeup event. |
| 253 | m_wakeup_event->Signal(); | ||
| 200 | } | 254 | } |
| 201 | 255 | ||
| 202 | Result ServerManager::WaitAndProcessImpl() { | 256 | void ServerManager::LinkDeferred() { |
| 203 | Kernel::KScopedAutoObject<Kernel::KSynchronizationObject> wait_obj; | 257 | std::scoped_lock lk{m_deferred_list_mutex}; |
| 204 | HandleType wait_type{}; | 258 | m_multi_wait.MoveAll(std::addressof(m_deferred_list)); |
| 259 | } | ||
| 205 | 260 | ||
| 261 | MultiWaitHolder* ServerManager::WaitSignaled() { | ||
| 206 | // Ensure we are the only thread waiting for this server. | 262 | // Ensure we are the only thread waiting for this server. |
| 207 | std::unique_lock sl{m_serve_mutex}; | 263 | std::scoped_lock lk{m_selection_mutex}; |
| 208 | 264 | ||
| 209 | // If we're done, return before we start waiting. | 265 | while (true) { |
| 210 | R_SUCCEED_IF(m_stop_source.stop_requested()); | 266 | this->LinkDeferred(); |
| 211 | 267 | ||
| 212 | // Wait for a tracked object to become signaled. | 268 | // If we're done, return before we start waiting. |
| 213 | { | 269 | if (m_stop_source.stop_requested()) { |
| 214 | s32 num_objs{}; | 270 | return nullptr; |
| 215 | std::array<HandleType, MaximumWaitObjects> wait_types{}; | ||
| 216 | std::array<Kernel::KSynchronizationObject*, MaximumWaitObjects> wait_objs{}; | ||
| 217 | |||
| 218 | const auto AddWaiter{ | ||
| 219 | [&](Kernel::KSynchronizationObject* synchronization_object, HandleType type) { | ||
| 220 | // Open a new reference to the object. | ||
| 221 | synchronization_object->Open(); | ||
| 222 | |||
| 223 | // Insert into the list. | ||
| 224 | wait_types[num_objs] = type; | ||
| 225 | wait_objs[num_objs++] = synchronization_object; | ||
| 226 | }}; | ||
| 227 | |||
| 228 | { | ||
| 229 | std::scoped_lock ll{m_list_mutex}; | ||
| 230 | |||
| 231 | // Add all of our ports. | ||
| 232 | for (const auto& [port, handler] : m_ports) { | ||
| 233 | AddWaiter(port, HandleType::Port); | ||
| 234 | } | ||
| 235 | |||
| 236 | // Add all of our sessions. | ||
| 237 | for (const auto& [session, manager] : m_sessions) { | ||
| 238 | AddWaiter(session, HandleType::Session); | ||
| 239 | } | ||
| 240 | } | 271 | } |
| 241 | 272 | ||
| 242 | // Add the deferral wakeup event. | 273 | auto* selected = m_multi_wait.WaitAny(m_system.Kernel()); |
| 243 | if (m_deferral_event != nullptr) { | 274 | if (selected == std::addressof(*m_wakeup_holder)) { |
| 244 | AddWaiter(std::addressof(m_deferral_event->GetReadableEvent()), HandleType::DeferEvent); | 275 | // Clear and restart if we were woken up. |
| 276 | m_wakeup_event->Clear(); | ||
| 277 | } else { | ||
| 278 | // Unlink and handle the event. | ||
| 279 | selected->UnlinkFromMultiWait(); | ||
| 280 | return selected; | ||
| 245 | } | 281 | } |
| 282 | } | ||
| 283 | } | ||
| 246 | 284 | ||
| 247 | // Add the wakeup event. | 285 | Result ServerManager::Process(MultiWaitHolder* holder) { |
| 248 | AddWaiter(std::addressof(m_event->GetReadableEvent()), HandleType::Event); | 286 | switch (static_cast<UserDataTag>(holder->GetUserData())) { |
| 249 | 287 | case UserDataTag::Session: | |
| 250 | // Clean up extra references on exit. | 288 | R_RETURN(this->OnSessionEvent(static_cast<Session*>(holder))); |
| 251 | SCOPE_EXIT({ | 289 | case UserDataTag::Port: |
| 252 | for (s32 i = 0; i < num_objs; i++) { | 290 | R_RETURN(this->OnPortEvent(static_cast<Port*>(holder))); |
| 253 | wait_objs[i]->Close(); | 291 | case UserDataTag::DeferEvent: |
| 254 | } | 292 | R_RETURN(this->OnDeferralEvent()); |
| 255 | }); | 293 | default: |
| 256 | 294 | UNREACHABLE(); | |
| 257 | // Wait for a signal. | 295 | } |
| 258 | s32 out_index{-1}; | 296 | } |
| 259 | R_TRY_CATCH(Kernel::KSynchronizationObject::Wait(m_system.Kernel(), &out_index, | ||
| 260 | wait_objs.data(), num_objs, -1)) { | ||
| 261 | R_CATCH(Kernel::ResultSessionClosed) { | ||
| 262 | // On session closed, index is updated and we don't want to return an error. | ||
| 263 | } | ||
| 264 | } | ||
| 265 | R_END_TRY_CATCH; | ||
| 266 | ASSERT(out_index >= 0 && out_index < num_objs); | ||
| 267 | 297 | ||
| 268 | // Set the output index. | 298 | bool ServerManager::WaitAndProcessImpl() { |
| 269 | wait_obj = wait_objs[out_index]; | 299 | if (auto* signaled_holder = this->WaitSignaled(); signaled_holder != nullptr) { |
| 270 | wait_type = wait_types[out_index]; | 300 | R_ASSERT(this->Process(signaled_holder)); |
| 301 | return true; | ||
| 302 | } else { | ||
| 303 | return false; | ||
| 271 | } | 304 | } |
| 305 | } | ||
| 272 | 306 | ||
| 273 | // Process what we just received, temporarily removing the object so it is | 307 | Result ServerManager::LoopProcessImpl() { |
| 274 | // not processed concurrently by another thread. | 308 | while (!m_stop_source.stop_requested()) { |
| 275 | { | 309 | this->WaitAndProcessImpl(); |
| 276 | switch (wait_type) { | ||
| 277 | case HandleType::Port: { | ||
| 278 | // Port signaled. | ||
| 279 | auto* port = wait_obj->DynamicCast<Kernel::KServerPort*>(); | ||
| 280 | SessionRequestHandlerFactory handler_factory; | ||
| 281 | |||
| 282 | // Remove from tracking. | ||
| 283 | { | ||
| 284 | std::scoped_lock ll{m_list_mutex}; | ||
| 285 | ASSERT(m_ports.contains(port)); | ||
| 286 | m_ports.at(port).swap(handler_factory); | ||
| 287 | m_ports.erase(port); | ||
| 288 | } | ||
| 289 | |||
| 290 | // Allow other threads to serve. | ||
| 291 | sl.unlock(); | ||
| 292 | |||
| 293 | // Finish. | ||
| 294 | R_RETURN(this->OnPortEvent(port, std::move(handler_factory))); | ||
| 295 | } | ||
| 296 | case HandleType::Session: { | ||
| 297 | // Session signaled. | ||
| 298 | auto* session = wait_obj->DynamicCast<Kernel::KServerSession*>(); | ||
| 299 | std::shared_ptr<SessionRequestManager> manager; | ||
| 300 | |||
| 301 | // Remove from tracking. | ||
| 302 | { | ||
| 303 | std::scoped_lock ll{m_list_mutex}; | ||
| 304 | ASSERT(m_sessions.contains(session)); | ||
| 305 | m_sessions.at(session).swap(manager); | ||
| 306 | m_sessions.erase(session); | ||
| 307 | } | ||
| 308 | |||
| 309 | // Allow other threads to serve. | ||
| 310 | sl.unlock(); | ||
| 311 | |||
| 312 | // Finish. | ||
| 313 | R_RETURN(this->OnSessionEvent(session, std::move(manager))); | ||
| 314 | } | ||
| 315 | case HandleType::DeferEvent: { | ||
| 316 | // Clear event. | ||
| 317 | ASSERT(R_SUCCEEDED(m_deferral_event->Clear())); | ||
| 318 | |||
| 319 | // Drain the list of deferrals while we process. | ||
| 320 | std::list<RequestState> deferrals; | ||
| 321 | { | ||
| 322 | std::scoped_lock ll{m_list_mutex}; | ||
| 323 | m_deferrals.swap(deferrals); | ||
| 324 | } | ||
| 325 | |||
| 326 | // Allow other threads to serve. | ||
| 327 | sl.unlock(); | ||
| 328 | |||
| 329 | // Finish. | ||
| 330 | R_RETURN(this->OnDeferralEvent(std::move(deferrals))); | ||
| 331 | } | ||
| 332 | case HandleType::Event: { | ||
| 333 | // Clear event and finish. | ||
| 334 | R_RETURN(m_event->Clear()); | ||
| 335 | } | ||
| 336 | default: { | ||
| 337 | UNREACHABLE(); | ||
| 338 | } | ||
| 339 | } | ||
| 340 | } | 310 | } |
| 311 | |||
| 312 | R_SUCCEED(); | ||
| 341 | } | 313 | } |
| 342 | 314 | ||
| 343 | Result ServerManager::OnPortEvent(Kernel::KServerPort* port, | 315 | Result ServerManager::OnPortEvent(Port* server) { |
| 344 | SessionRequestHandlerFactory&& handler_factory) { | ||
| 345 | // Accept a new server session. | 316 | // Accept a new server session. |
| 346 | Kernel::KServerSession* session = port->AcceptSession(); | 317 | auto* server_port = static_cast<Kernel::KServerPort*>(server->GetNativeHandle()); |
| 347 | ASSERT(session != nullptr); | 318 | Kernel::KServerSession* server_session = server_port->AcceptSession(); |
| 319 | ASSERT(server_session != nullptr); | ||
| 348 | 320 | ||
| 349 | // Create the session manager and install the handler. | 321 | // Create the session manager and install the handler. |
| 350 | auto manager = std::make_shared<SessionRequestManager>(m_system.Kernel(), *this); | 322 | auto manager = std::make_shared<SessionRequestManager>(m_system.Kernel(), *this); |
| 351 | manager->SetSessionHandler(handler_factory()); | 323 | manager->SetSessionHandler(server->CreateHandler()); |
| 352 | 324 | ||
| 353 | // Track the server session. | 325 | // Create and register the new session. |
| 354 | { | 326 | this->RegisterSession(server_session, std::move(manager)); |
| 355 | std::scoped_lock ll{m_list_mutex}; | ||
| 356 | m_ports.emplace(port, std::move(handler_factory)); | ||
| 357 | m_sessions.emplace(session, std::move(manager)); | ||
| 358 | } | ||
| 359 | 327 | ||
| 360 | // Signal the wakeup event. | 328 | // Resume tracking the port. |
| 361 | m_event->Signal(); | 329 | this->LinkToDeferredList(server); |
| 362 | 330 | ||
| 363 | // We succeeded. | 331 | // We succeeded. |
| 364 | R_SUCCEED(); | 332 | R_SUCCEED(); |
| 365 | } | 333 | } |
| 366 | 334 | ||
| 367 | Result ServerManager::OnSessionEvent(Kernel::KServerSession* session, | 335 | Result ServerManager::OnSessionEvent(Session* session) { |
| 368 | std::shared_ptr<SessionRequestManager>&& manager) { | 336 | Result res = ResultSuccess; |
| 369 | Result rc{ResultSuccess}; | ||
| 370 | 337 | ||
| 371 | // Try to receive a message. | 338 | // Try to receive a message. |
| 372 | std::shared_ptr<HLERequestContext> context; | 339 | auto* server_session = static_cast<Kernel::KServerSession*>(session->GetNativeHandle()); |
| 373 | rc = session->ReceiveRequestHLE(&context, manager); | 340 | res = server_session->ReceiveRequestHLE(&session->GetContext(), session->GetManager()); |
| 374 | 341 | ||
| 375 | // If the session has been closed, we're done. | 342 | // If the session has been closed, we're done. |
| 376 | if (rc == Kernel::ResultSessionClosed) { | 343 | if (res == Kernel::ResultSessionClosed) { |
| 377 | // Close the session. | 344 | this->DestroySession(session); |
| 378 | session->Close(); | ||
| 379 | |||
| 380 | // Finish. | ||
| 381 | R_SUCCEED(); | 345 | R_SUCCEED(); |
| 382 | } | 346 | } |
| 383 | ASSERT(R_SUCCEEDED(rc)); | ||
| 384 | 347 | ||
| 385 | RequestState request{ | 348 | R_ASSERT(res); |
| 386 | .session = session, | ||
| 387 | .context = std::move(context), | ||
| 388 | .manager = std::move(manager), | ||
| 389 | }; | ||
| 390 | 349 | ||
| 391 | // Complete the sync request with deferral handling. | 350 | // Complete the sync request with deferral handling. |
| 392 | R_RETURN(this->CompleteSyncRequest(std::move(request))); | 351 | R_RETURN(this->CompleteSyncRequest(session)); |
| 393 | } | 352 | } |
| 394 | 353 | ||
| 395 | Result ServerManager::CompleteSyncRequest(RequestState&& request) { | 354 | Result ServerManager::CompleteSyncRequest(Session* session) { |
| 396 | Result rc{ResultSuccess}; | 355 | Result res = ResultSuccess; |
| 397 | Result service_rc{ResultSuccess}; | 356 | Result service_res = ResultSuccess; |
| 398 | 357 | ||
| 399 | // Mark the request as not deferred. | 358 | // Mark the request as not deferred. |
| 400 | request.context->SetIsDeferred(false); | 359 | session->GetContext()->SetIsDeferred(false); |
| 401 | 360 | ||
| 402 | // Complete the request. We have exclusive access to this session. | 361 | // Complete the request. We have exclusive access to this session. |
| 403 | service_rc = request.manager->CompleteSyncRequest(request.session, *request.context); | 362 | auto* server_session = static_cast<Kernel::KServerSession*>(session->GetNativeHandle()); |
| 363 | service_res = | ||
| 364 | session->GetManager()->CompleteSyncRequest(server_session, *session->GetContext()); | ||
| 404 | 365 | ||
| 405 | // If we've been deferred, we're done. | 366 | // If we've been deferred, we're done. |
| 406 | if (request.context->GetIsDeferred()) { | 367 | if (session->GetContext()->GetIsDeferred()) { |
| 407 | // Insert into deferral list. | 368 | // Insert into deferred session list. |
| 408 | std::scoped_lock ll{m_list_mutex}; | 369 | std::scoped_lock ll{m_deferred_list_mutex}; |
| 409 | m_deferrals.emplace_back(std::move(request)); | 370 | m_deferred_sessions.push_back(session); |
| 410 | 371 | ||
| 411 | // Finish. | 372 | // Finish. |
| 412 | R_SUCCEED(); | 373 | R_SUCCEED(); |
| 413 | } | 374 | } |
| 414 | 375 | ||
| 415 | // Send the reply. | 376 | // Send the reply. |
| 416 | rc = request.session->SendReplyHLE(); | 377 | res = server_session->SendReplyHLE(); |
| 417 | 378 | ||
| 418 | // If the session has been closed, we're done. | 379 | // If the session has been closed, we're done. |
| 419 | if (rc == Kernel::ResultSessionClosed || service_rc == IPC::ResultSessionClosed) { | 380 | if (res == Kernel::ResultSessionClosed || service_res == IPC::ResultSessionClosed) { |
| 420 | // Close the session. | 381 | this->DestroySession(session); |
| 421 | request.session->Close(); | ||
| 422 | |||
| 423 | // Finish. | ||
| 424 | R_SUCCEED(); | 382 | R_SUCCEED(); |
| 425 | } | 383 | } |
| 426 | 384 | ||
| 427 | ASSERT(R_SUCCEEDED(rc)); | 385 | R_ASSERT(res); |
| 428 | ASSERT(R_SUCCEEDED(service_rc)); | 386 | R_ASSERT(service_res); |
| 429 | |||
| 430 | // Reinsert the session. | ||
| 431 | { | ||
| 432 | std::scoped_lock ll{m_list_mutex}; | ||
| 433 | m_sessions.emplace(request.session, std::move(request.manager)); | ||
| 434 | } | ||
| 435 | 387 | ||
| 436 | // Signal the wakeup event. | 388 | // We succeeded, so we can process future messages on this session. |
| 437 | m_event->Signal(); | 389 | this->LinkToDeferredList(session); |
| 438 | 390 | ||
| 439 | // We succeeded. | ||
| 440 | R_SUCCEED(); | 391 | R_SUCCEED(); |
| 441 | } | 392 | } |
| 442 | 393 | ||
| 443 | Result ServerManager::OnDeferralEvent(std::list<RequestState>&& deferrals) { | 394 | Result ServerManager::OnDeferralEvent() { |
| 444 | ON_RESULT_FAILURE { | 395 | // Clear event before grabbing the list. |
| 445 | std::scoped_lock ll{m_list_mutex}; | 396 | m_deferral_event->Clear(); |
| 446 | m_deferrals.splice(m_deferrals.end(), deferrals); | ||
| 447 | }; | ||
| 448 | 397 | ||
| 449 | while (!deferrals.empty()) { | 398 | // Get and clear list. |
| 450 | RequestState request = deferrals.front(); | 399 | const auto deferrals = [&] { |
| 451 | deferrals.pop_front(); | 400 | std::scoped_lock lk{m_deferred_list_mutex}; |
| 401 | return std::move(m_deferred_sessions); | ||
| 402 | }(); | ||
| 452 | 403 | ||
| 453 | // Try again to complete the request. | 404 | // Relink deferral event. |
| 454 | R_TRY(this->CompleteSyncRequest(std::move(request))); | 405 | this->LinkToDeferredList(std::addressof(*m_deferral_holder)); |
| 406 | |||
| 407 | // For each session, try again to complete the request. | ||
| 408 | for (auto* session : deferrals) { | ||
| 409 | R_ASSERT(this->CompleteSyncRequest(session)); | ||
| 455 | } | 410 | } |
| 456 | 411 | ||
| 457 | R_SUCCEED(); | 412 | R_SUCCEED(); |
| 458 | } | 413 | } |
| 459 | 414 | ||
| 415 | void ServerManager::DestroySession(Session* session) { | ||
| 416 | // Unlink. | ||
| 417 | { | ||
| 418 | std::scoped_lock lk{m_deferred_list_mutex}; | ||
| 419 | m_sessions.erase(m_sessions.iterator_to(*session)); | ||
| 420 | } | ||
| 421 | |||
| 422 | // Free the session. | ||
| 423 | delete session; | ||
| 424 | } | ||
| 425 | |||
| 460 | } // namespace Service | 426 | } // namespace Service |
diff --git a/src/core/hle/service/server_manager.h b/src/core/hle/service/server_manager.h index c4bc07262..5173ce46e 100644 --- a/src/core/hle/service/server_manager.h +++ b/src/core/hle/service/server_manager.h | |||
| @@ -3,18 +3,17 @@ | |||
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <functional> | ||
| 7 | #include <list> | 6 | #include <list> |
| 8 | #include <map> | ||
| 9 | #include <mutex> | 7 | #include <mutex> |
| 10 | #include <string_view> | 8 | #include <optional> |
| 11 | #include <vector> | 9 | #include <vector> |
| 12 | 10 | ||
| 13 | #include "common/polyfill_thread.h" | 11 | #include "common/polyfill_thread.h" |
| 14 | #include "common/thread.h" | 12 | #include "common/thread.h" |
| 15 | #include "core/hle/result.h" | 13 | #include "core/hle/result.h" |
| 16 | #include "core/hle/service/hle_ipc.h" | 14 | #include "core/hle/service/hle_ipc.h" |
| 17 | #include "core/hle/service/mutex.h" | 15 | #include "core/hle/service/os/multi_wait.h" |
| 16 | #include "core/hle/service/os/mutex.h" | ||
| 18 | 17 | ||
| 19 | namespace Core { | 18 | namespace Core { |
| 20 | class System; | 19 | class System; |
| @@ -24,11 +23,13 @@ namespace Kernel { | |||
| 24 | class KEvent; | 23 | class KEvent; |
| 25 | class KServerPort; | 24 | class KServerPort; |
| 26 | class KServerSession; | 25 | class KServerSession; |
| 27 | class KSynchronizationObject; | ||
| 28 | } // namespace Kernel | 26 | } // namespace Kernel |
| 29 | 27 | ||
| 30 | namespace Service { | 28 | namespace Service { |
| 31 | 29 | ||
| 30 | class Port; | ||
| 31 | class Session; | ||
| 32 | |||
| 32 | class ServerManager { | 33 | class ServerManager { |
| 33 | public: | 34 | public: |
| 34 | explicit ServerManager(Core::System& system); | 35 | explicit ServerManager(Core::System& system); |
| @@ -52,34 +53,40 @@ public: | |||
| 52 | static void RunServer(std::unique_ptr<ServerManager>&& server); | 53 | static void RunServer(std::unique_ptr<ServerManager>&& server); |
| 53 | 54 | ||
| 54 | private: | 55 | private: |
| 55 | struct RequestState; | 56 | void LinkToDeferredList(MultiWaitHolder* holder); |
| 56 | 57 | void LinkDeferred(); | |
| 58 | MultiWaitHolder* WaitSignaled(); | ||
| 59 | Result Process(MultiWaitHolder* holder); | ||
| 60 | bool WaitAndProcessImpl(); | ||
| 57 | Result LoopProcessImpl(); | 61 | Result LoopProcessImpl(); |
| 58 | Result WaitAndProcessImpl(); | 62 | |
| 59 | Result OnPortEvent(Kernel::KServerPort* port, SessionRequestHandlerFactory&& handler_factory); | 63 | Result OnPortEvent(Port* port); |
| 60 | Result OnSessionEvent(Kernel::KServerSession* session, | 64 | Result OnSessionEvent(Session* session); |
| 61 | std::shared_ptr<SessionRequestManager>&& manager); | 65 | Result OnDeferralEvent(); |
| 62 | Result OnDeferralEvent(std::list<RequestState>&& deferrals); | 66 | Result CompleteSyncRequest(Session* session); |
| 63 | Result CompleteSyncRequest(RequestState&& state); | 67 | |
| 68 | private: | ||
| 69 | void DestroySession(Session* session); | ||
| 64 | 70 | ||
| 65 | private: | 71 | private: |
| 66 | Core::System& m_system; | 72 | Core::System& m_system; |
| 67 | Mutex m_serve_mutex; | 73 | Mutex m_selection_mutex; |
| 68 | std::mutex m_list_mutex; | ||
| 69 | 74 | ||
| 70 | // Guest state tracking | 75 | // Events |
| 71 | std::map<Kernel::KServerPort*, SessionRequestHandlerFactory> m_ports{}; | 76 | Kernel::KEvent* m_wakeup_event{}; |
| 72 | std::map<Kernel::KServerSession*, std::shared_ptr<SessionRequestManager>> m_sessions{}; | ||
| 73 | Kernel::KEvent* m_event{}; | ||
| 74 | Kernel::KEvent* m_deferral_event{}; | 77 | Kernel::KEvent* m_deferral_event{}; |
| 75 | 78 | ||
| 76 | // Deferral tracking | 79 | // Deferred wait list |
| 77 | struct RequestState { | 80 | std::mutex m_deferred_list_mutex{}; |
| 78 | Kernel::KServerSession* session; | 81 | MultiWait m_deferred_list{}; |
| 79 | std::shared_ptr<HLERequestContext> context; | 82 | |
| 80 | std::shared_ptr<SessionRequestManager> manager; | 83 | // Guest state tracking |
| 81 | }; | 84 | MultiWait m_multi_wait{}; |
| 82 | std::list<RequestState> m_deferrals{}; | 85 | Common::IntrusiveListBaseTraits<Port>::ListType m_servers{}; |
| 86 | Common::IntrusiveListBaseTraits<Session>::ListType m_sessions{}; | ||
| 87 | std::list<Session*> m_deferred_sessions{}; | ||
| 88 | std::optional<MultiWaitHolder> m_wakeup_holder{}; | ||
| 89 | std::optional<MultiWaitHolder> m_deferral_holder{}; | ||
| 83 | 90 | ||
| 84 | // Host state tracking | 91 | // Host state tracking |
| 85 | Common::Event m_stopped{}; | 92 | Common::Event m_stopped{}; |
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 06cbad268..f68c3c686 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp | |||
| @@ -15,7 +15,7 @@ | |||
| 15 | #include "core/hle/service/aoc/aoc_u.h" | 15 | #include "core/hle/service/aoc/aoc_u.h" |
| 16 | #include "core/hle/service/apm/apm.h" | 16 | #include "core/hle/service/apm/apm.h" |
| 17 | #include "core/hle/service/audio/audio.h" | 17 | #include "core/hle/service/audio/audio.h" |
| 18 | #include "core/hle/service/bcat/bcat_module.h" | 18 | #include "core/hle/service/bcat/bcat.h" |
| 19 | #include "core/hle/service/bpc/bpc.h" | 19 | #include "core/hle/service/bpc/bpc.h" |
| 20 | #include "core/hle/service/btdrv/btdrv.h" | 20 | #include "core/hle/service/btdrv/btdrv.h" |
| 21 | #include "core/hle/service/btm/btm.h" | 21 | #include "core/hle/service/btm/btm.h" |
diff --git a/src/core/hle/service/vi/layer/vi_layer.cpp b/src/core/hle/service/vi/layer/vi_layer.cpp index 493bd6e9e..eca35d82a 100644 --- a/src/core/hle/service/vi/layer/vi_layer.cpp +++ b/src/core/hle/service/vi/layer/vi_layer.cpp | |||
| @@ -1,6 +1,7 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include "core/hle/service/nvnflinger/hwc_layer.h" | ||
| 4 | #include "core/hle/service/vi/layer/vi_layer.h" | 5 | #include "core/hle/service/vi/layer/vi_layer.h" |
| 5 | 6 | ||
| 6 | namespace Service::VI { | 7 | namespace Service::VI { |
| @@ -8,8 +9,9 @@ namespace Service::VI { | |||
| 8 | Layer::Layer(u64 layer_id_, u32 binder_id_, android::BufferQueueCore& core_, | 9 | Layer::Layer(u64 layer_id_, u32 binder_id_, android::BufferQueueCore& core_, |
| 9 | android::BufferQueueProducer& binder_, | 10 | android::BufferQueueProducer& binder_, |
| 10 | std::shared_ptr<android::BufferItemConsumer>&& consumer_) | 11 | std::shared_ptr<android::BufferItemConsumer>&& consumer_) |
| 11 | : layer_id{layer_id_}, binder_id{binder_id_}, core{core_}, binder{binder_}, | 12 | : layer_id{layer_id_}, binder_id{binder_id_}, core{core_}, binder{binder_}, consumer{std::move( |
| 12 | consumer{std::move(consumer_)}, open{false}, visible{true} {} | 13 | consumer_)}, |
| 14 | blending{Nvnflinger::LayerBlending::None}, open{false}, visible{true} {} | ||
| 13 | 15 | ||
| 14 | Layer::~Layer() = default; | 16 | Layer::~Layer() = default; |
| 15 | 17 | ||
diff --git a/src/core/hle/service/vi/layer/vi_layer.h b/src/core/hle/service/vi/layer/vi_layer.h index b4b031ee7..14e229903 100644 --- a/src/core/hle/service/vi/layer/vi_layer.h +++ b/src/core/hle/service/vi/layer/vi_layer.h | |||
| @@ -14,6 +14,10 @@ class BufferQueueCore; | |||
| 14 | class BufferQueueProducer; | 14 | class BufferQueueProducer; |
| 15 | } // namespace Service::android | 15 | } // namespace Service::android |
| 16 | 16 | ||
| 17 | namespace Service::Nvnflinger { | ||
| 18 | enum class LayerBlending : u32; | ||
| 19 | } | ||
| 20 | |||
| 17 | namespace Service::VI { | 21 | namespace Service::VI { |
| 18 | 22 | ||
| 19 | /// Represents a single display layer. | 23 | /// Represents a single display layer. |
| @@ -92,12 +96,21 @@ public: | |||
| 92 | return !std::exchange(open, true); | 96 | return !std::exchange(open, true); |
| 93 | } | 97 | } |
| 94 | 98 | ||
| 99 | Nvnflinger::LayerBlending GetBlending() { | ||
| 100 | return blending; | ||
| 101 | } | ||
| 102 | |||
| 103 | void SetBlending(Nvnflinger::LayerBlending b) { | ||
| 104 | blending = b; | ||
| 105 | } | ||
| 106 | |||
| 95 | private: | 107 | private: |
| 96 | const u64 layer_id; | 108 | const u64 layer_id; |
| 97 | const u32 binder_id; | 109 | const u32 binder_id; |
| 98 | android::BufferQueueCore& core; | 110 | android::BufferQueueCore& core; |
| 99 | android::BufferQueueProducer& binder; | 111 | android::BufferQueueProducer& binder; |
| 100 | std::shared_ptr<android::BufferItemConsumer> consumer; | 112 | std::shared_ptr<android::BufferItemConsumer> consumer; |
| 113 | Service::Nvnflinger::LayerBlending blending; | ||
| 101 | bool open; | 114 | bool open; |
| 102 | bool visible; | 115 | bool visible; |
| 103 | }; | 116 | }; |
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp index 14d1a3840..b84b57d92 100644 --- a/src/core/memory/cheat_engine.cpp +++ b/src/core/memory/cheat_engine.cpp | |||
| @@ -5,11 +5,13 @@ | |||
| 5 | #include "common/hex_util.h" | 5 | #include "common/hex_util.h" |
| 6 | #include "common/microprofile.h" | 6 | #include "common/microprofile.h" |
| 7 | #include "common/swap.h" | 7 | #include "common/swap.h" |
| 8 | #include "core/arm/debug.h" | ||
| 8 | #include "core/core.h" | 9 | #include "core/core.h" |
| 9 | #include "core/core_timing.h" | 10 | #include "core/core_timing.h" |
| 10 | #include "core/hle/kernel/k_page_table.h" | 11 | #include "core/hle/kernel/k_page_table.h" |
| 11 | #include "core/hle/kernel/k_process.h" | 12 | #include "core/hle/kernel/k_process.h" |
| 12 | #include "core/hle/kernel/k_process_page_table.h" | 13 | #include "core/hle/kernel/k_process_page_table.h" |
| 14 | #include "core/hle/kernel/svc_types.h" | ||
| 13 | #include "core/hle/service/hid/hid_server.h" | 15 | #include "core/hle/service/hid/hid_server.h" |
| 14 | #include "core/hle/service/sm/sm.h" | 16 | #include "core/hle/service/sm/sm.h" |
| 15 | #include "core/memory.h" | 17 | #include "core/memory.h" |
| @@ -63,7 +65,9 @@ void StandardVmCallbacks::MemoryWriteUnsafe(VAddr address, const void* data, u64 | |||
| 63 | return; | 65 | return; |
| 64 | } | 66 | } |
| 65 | 67 | ||
| 66 | system.ApplicationMemory().WriteBlock(address, data, size); | 68 | if (system.ApplicationMemory().WriteBlock(address, data, size)) { |
| 69 | Core::InvalidateInstructionCacheRange(system.ApplicationProcess(), address, size); | ||
| 70 | } | ||
| 67 | } | 71 | } |
| 68 | 72 | ||
| 69 | u64 StandardVmCallbacks::HidKeysDown() { | 73 | u64 StandardVmCallbacks::HidKeysDown() { |
| @@ -84,6 +88,20 @@ u64 StandardVmCallbacks::HidKeysDown() { | |||
| 84 | return static_cast<u64>(press_state & HID::NpadButton::All); | 88 | return static_cast<u64>(press_state & HID::NpadButton::All); |
| 85 | } | 89 | } |
| 86 | 90 | ||
| 91 | void StandardVmCallbacks::PauseProcess() { | ||
| 92 | if (system.ApplicationProcess()->IsSuspended()) { | ||
| 93 | return; | ||
| 94 | } | ||
| 95 | system.ApplicationProcess()->SetActivity(Kernel::Svc::ProcessActivity::Paused); | ||
| 96 | } | ||
| 97 | |||
| 98 | void StandardVmCallbacks::ResumeProcess() { | ||
| 99 | if (!system.ApplicationProcess()->IsSuspended()) { | ||
| 100 | return; | ||
| 101 | } | ||
| 102 | system.ApplicationProcess()->SetActivity(Kernel::Svc::ProcessActivity::Runnable); | ||
| 103 | } | ||
| 104 | |||
| 87 | void StandardVmCallbacks::DebugLog(u8 id, u64 value) { | 105 | void StandardVmCallbacks::DebugLog(u8 id, u64 value) { |
| 88 | LOG_INFO(CheatEngine, "Cheat triggered DebugLog: ID '{:01X}' Value '{:016X}'", id, value); | 106 | LOG_INFO(CheatEngine, "Cheat triggered DebugLog: ID '{:01X}' Value '{:016X}'", id, value); |
| 89 | } | 107 | } |
diff --git a/src/core/memory/cheat_engine.h b/src/core/memory/cheat_engine.h index 619cabaa2..f52f2be7c 100644 --- a/src/core/memory/cheat_engine.h +++ b/src/core/memory/cheat_engine.h | |||
| @@ -30,6 +30,8 @@ public: | |||
| 30 | void MemoryReadUnsafe(VAddr address, void* data, u64 size) override; | 30 | void MemoryReadUnsafe(VAddr address, void* data, u64 size) override; |
| 31 | void MemoryWriteUnsafe(VAddr address, const void* data, u64 size) override; | 31 | void MemoryWriteUnsafe(VAddr address, const void* data, u64 size) override; |
| 32 | u64 HidKeysDown() override; | 32 | u64 HidKeysDown() override; |
| 33 | void PauseProcess() override; | ||
| 34 | void ResumeProcess() override; | ||
| 33 | void DebugLog(u8 id, u64 value) override; | 35 | void DebugLog(u8 id, u64 value) override; |
| 34 | void CommandLog(std::string_view data) override; | 36 | void CommandLog(std::string_view data) override; |
| 35 | 37 | ||
diff --git a/src/core/memory/dmnt_cheat_vm.cpp b/src/core/memory/dmnt_cheat_vm.cpp index 8bc81e72d..f7097d01d 100644 --- a/src/core/memory/dmnt_cheat_vm.cpp +++ b/src/core/memory/dmnt_cheat_vm.cpp | |||
| @@ -1205,9 +1205,9 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) { | |||
| 1205 | static_registers[rw_static_reg->static_idx] = registers[rw_static_reg->idx]; | 1205 | static_registers[rw_static_reg->static_idx] = registers[rw_static_reg->idx]; |
| 1206 | } | 1206 | } |
| 1207 | } else if (std::holds_alternative<PauseProcessOpcode>(cur_opcode.opcode)) { | 1207 | } else if (std::holds_alternative<PauseProcessOpcode>(cur_opcode.opcode)) { |
| 1208 | // TODO: Pause cheat process | 1208 | callbacks->PauseProcess(); |
| 1209 | } else if (std::holds_alternative<ResumeProcessOpcode>(cur_opcode.opcode)) { | 1209 | } else if (std::holds_alternative<ResumeProcessOpcode>(cur_opcode.opcode)) { |
| 1210 | // TODO: Resume cheat process | 1210 | callbacks->ResumeProcess(); |
| 1211 | } else if (auto debug_log = std::get_if<DebugLogOpcode>(&cur_opcode.opcode)) { | 1211 | } else if (auto debug_log = std::get_if<DebugLogOpcode>(&cur_opcode.opcode)) { |
| 1212 | // Read value from memory. | 1212 | // Read value from memory. |
| 1213 | u64 log_value = 0; | 1213 | u64 log_value = 0; |
diff --git a/src/core/memory/dmnt_cheat_vm.h b/src/core/memory/dmnt_cheat_vm.h index fed6a24ad..1c1ed1259 100644 --- a/src/core/memory/dmnt_cheat_vm.h +++ b/src/core/memory/dmnt_cheat_vm.h | |||
| @@ -271,6 +271,9 @@ public: | |||
| 271 | 271 | ||
| 272 | virtual u64 HidKeysDown() = 0; | 272 | virtual u64 HidKeysDown() = 0; |
| 273 | 273 | ||
| 274 | virtual void PauseProcess() = 0; | ||
| 275 | virtual void ResumeProcess() = 0; | ||
| 276 | |||
| 274 | virtual void DebugLog(u8 id, u64 value) = 0; | 277 | virtual void DebugLog(u8 id, u64 value) = 0; |
| 275 | virtual void CommandLog(std::string_view data) = 0; | 278 | virtual void CommandLog(std::string_view data) = 0; |
| 276 | }; | 279 | }; |
diff --git a/src/frontend_common/config.cpp b/src/frontend_common/config.cpp index d34624d28..2bebfeef9 100644 --- a/src/frontend_common/config.cpp +++ b/src/frontend_common/config.cpp | |||
| @@ -401,6 +401,14 @@ void Config::ReadNetworkValues() { | |||
| 401 | EndGroup(); | 401 | EndGroup(); |
| 402 | } | 402 | } |
| 403 | 403 | ||
| 404 | void Config::ReadLibraryAppletValues() { | ||
| 405 | BeginGroup(Settings::TranslateCategory(Settings::Category::LibraryApplet)); | ||
| 406 | |||
| 407 | ReadCategory(Settings::Category::LibraryApplet); | ||
| 408 | |||
| 409 | EndGroup(); | ||
| 410 | } | ||
| 411 | |||
| 404 | void Config::ReadValues() { | 412 | void Config::ReadValues() { |
| 405 | if (global) { | 413 | if (global) { |
| 406 | ReadDataStorageValues(); | 414 | ReadDataStorageValues(); |
| @@ -410,6 +418,7 @@ void Config::ReadValues() { | |||
| 410 | ReadServiceValues(); | 418 | ReadServiceValues(); |
| 411 | ReadWebServiceValues(); | 419 | ReadWebServiceValues(); |
| 412 | ReadMiscellaneousValues(); | 420 | ReadMiscellaneousValues(); |
| 421 | ReadLibraryAppletValues(); | ||
| 413 | } | 422 | } |
| 414 | ReadControlValues(); | 423 | ReadControlValues(); |
| 415 | ReadCoreValues(); | 424 | ReadCoreValues(); |
| @@ -511,6 +520,7 @@ void Config::SaveValues() { | |||
| 511 | SaveNetworkValues(); | 520 | SaveNetworkValues(); |
| 512 | SaveWebServiceValues(); | 521 | SaveWebServiceValues(); |
| 513 | SaveMiscellaneousValues(); | 522 | SaveMiscellaneousValues(); |
| 523 | SaveLibraryAppletValues(); | ||
| 514 | } else { | 524 | } else { |
| 515 | LOG_DEBUG(Config, "Saving only generic configuration values"); | 525 | LOG_DEBUG(Config, "Saving only generic configuration values"); |
| 516 | } | 526 | } |
| @@ -691,6 +701,14 @@ void Config::SaveWebServiceValues() { | |||
| 691 | EndGroup(); | 701 | EndGroup(); |
| 692 | } | 702 | } |
| 693 | 703 | ||
| 704 | void Config::SaveLibraryAppletValues() { | ||
| 705 | BeginGroup(Settings::TranslateCategory(Settings::Category::LibraryApplet)); | ||
| 706 | |||
| 707 | WriteCategory(Settings::Category::LibraryApplet); | ||
| 708 | |||
| 709 | EndGroup(); | ||
| 710 | } | ||
| 711 | |||
| 694 | bool Config::ReadBooleanSetting(const std::string& key, const std::optional<bool> default_value) { | 712 | bool Config::ReadBooleanSetting(const std::string& key, const std::optional<bool> default_value) { |
| 695 | std::string full_key = GetFullKey(key, false); | 713 | std::string full_key = GetFullKey(key, false); |
| 696 | if (!default_value.has_value()) { | 714 | if (!default_value.has_value()) { |
| @@ -867,15 +885,9 @@ void Config::Reload() { | |||
| 867 | } | 885 | } |
| 868 | 886 | ||
| 869 | void Config::ClearControlPlayerValues() const { | 887 | void Config::ClearControlPlayerValues() const { |
| 870 | // If key is an empty string, all keys in the current group() are removed. | 888 | // Removes the entire [Controls] section |
| 871 | const char* section = Settings::TranslateCategory(Settings::Category::Controls); | 889 | const char* section = Settings::TranslateCategory(Settings::Category::Controls); |
| 872 | CSimpleIniA::TNamesDepend keys; | 890 | config->Delete(section, nullptr, true); |
| 873 | config->GetAllKeys(section, keys); | ||
| 874 | for (const auto& key : keys) { | ||
| 875 | if (std::string(config->GetValue(section, key.pItem)).empty()) { | ||
| 876 | config->Delete(section, key.pItem); | ||
| 877 | } | ||
| 878 | } | ||
| 879 | } | 891 | } |
| 880 | 892 | ||
| 881 | const std::string& Config::GetConfigFilePath() const { | 893 | const std::string& Config::GetConfigFilePath() const { |
diff --git a/src/frontend_common/config.h b/src/frontend_common/config.h index 4ecb97044..8b0599cc3 100644 --- a/src/frontend_common/config.h +++ b/src/frontend_common/config.h | |||
| @@ -88,6 +88,7 @@ protected: | |||
| 88 | void ReadSystemValues(); | 88 | void ReadSystemValues(); |
| 89 | void ReadWebServiceValues(); | 89 | void ReadWebServiceValues(); |
| 90 | void ReadNetworkValues(); | 90 | void ReadNetworkValues(); |
| 91 | void ReadLibraryAppletValues(); | ||
| 91 | 92 | ||
| 92 | // Read platform specific sections | 93 | // Read platform specific sections |
| 93 | virtual void ReadHidbusValues() = 0; | 94 | virtual void ReadHidbusValues() = 0; |
| @@ -121,6 +122,7 @@ protected: | |||
| 121 | void SaveScreenshotValues(); | 122 | void SaveScreenshotValues(); |
| 122 | void SaveSystemValues(); | 123 | void SaveSystemValues(); |
| 123 | void SaveWebServiceValues(); | 124 | void SaveWebServiceValues(); |
| 125 | void SaveLibraryAppletValues(); | ||
| 124 | 126 | ||
| 125 | // Save platform specific sections | 127 | // Save platform specific sections |
| 126 | virtual void SaveHidbusValues() = 0; | 128 | virtual void SaveHidbusValues() = 0; |
diff --git a/src/hid_core/hidbus/hidbus_base.h b/src/hid_core/hidbus/hidbus_base.h index ec41684e1..c948606e4 100644 --- a/src/hid_core/hidbus/hidbus_base.h +++ b/src/hid_core/hidbus/hidbus_base.h | |||
| @@ -160,7 +160,7 @@ public: | |||
| 160 | } | 160 | } |
| 161 | 161 | ||
| 162 | // Returns a reply from a command | 162 | // Returns a reply from a command |
| 163 | virtual std::vector<u8> GetReply() const { | 163 | virtual u64 GetReply(std::span<u8> out_data) const { |
| 164 | return {}; | 164 | return {}; |
| 165 | } | 165 | } |
| 166 | 166 | ||
diff --git a/src/hid_core/hidbus/ringcon.cpp b/src/hid_core/hidbus/ringcon.cpp index cedf25c16..4f5eaa505 100644 --- a/src/hid_core/hidbus/ringcon.cpp +++ b/src/hid_core/hidbus/ringcon.cpp | |||
| @@ -90,32 +90,32 @@ u8 RingController::GetDeviceId() const { | |||
| 90 | return device_id; | 90 | return device_id; |
| 91 | } | 91 | } |
| 92 | 92 | ||
| 93 | std::vector<u8> RingController::GetReply() const { | 93 | u64 RingController::GetReply(std::span<u8> out_data) const { |
| 94 | const RingConCommands current_command = command; | 94 | const RingConCommands current_command = command; |
| 95 | 95 | ||
| 96 | switch (current_command) { | 96 | switch (current_command) { |
| 97 | case RingConCommands::GetFirmwareVersion: | 97 | case RingConCommands::GetFirmwareVersion: |
| 98 | return GetFirmwareVersionReply(); | 98 | return GetFirmwareVersionReply(out_data); |
| 99 | case RingConCommands::ReadId: | 99 | case RingConCommands::ReadId: |
| 100 | return GetReadIdReply(); | 100 | return GetReadIdReply(out_data); |
| 101 | case RingConCommands::c20105: | 101 | case RingConCommands::c20105: |
| 102 | return GetC020105Reply(); | 102 | return GetC020105Reply(out_data); |
| 103 | case RingConCommands::ReadUnkCal: | 103 | case RingConCommands::ReadUnkCal: |
| 104 | return GetReadUnkCalReply(); | 104 | return GetReadUnkCalReply(out_data); |
| 105 | case RingConCommands::ReadFactoryCal: | 105 | case RingConCommands::ReadFactoryCal: |
| 106 | return GetReadFactoryCalReply(); | 106 | return GetReadFactoryCalReply(out_data); |
| 107 | case RingConCommands::ReadUserCal: | 107 | case RingConCommands::ReadUserCal: |
| 108 | return GetReadUserCalReply(); | 108 | return GetReadUserCalReply(out_data); |
| 109 | case RingConCommands::ReadRepCount: | 109 | case RingConCommands::ReadRepCount: |
| 110 | return GetReadRepCountReply(); | 110 | return GetReadRepCountReply(out_data); |
| 111 | case RingConCommands::ReadTotalPushCount: | 111 | case RingConCommands::ReadTotalPushCount: |
| 112 | return GetReadTotalPushCountReply(); | 112 | return GetReadTotalPushCountReply(out_data); |
| 113 | case RingConCommands::ResetRepCount: | 113 | case RingConCommands::ResetRepCount: |
| 114 | return GetResetRepCountReply(); | 114 | return GetResetRepCountReply(out_data); |
| 115 | case RingConCommands::SaveCalData: | 115 | case RingConCommands::SaveCalData: |
| 116 | return GetSaveDataReply(); | 116 | return GetSaveDataReply(out_data); |
| 117 | default: | 117 | default: |
| 118 | return GetErrorReply(); | 118 | return GetErrorReply(out_data); |
| 119 | } | 119 | } |
| 120 | } | 120 | } |
| 121 | 121 | ||
| @@ -163,16 +163,16 @@ bool RingController::SetCommand(std::span<const u8> data) { | |||
| 163 | } | 163 | } |
| 164 | } | 164 | } |
| 165 | 165 | ||
| 166 | std::vector<u8> RingController::GetFirmwareVersionReply() const { | 166 | u64 RingController::GetFirmwareVersionReply(std::span<u8> out_data) const { |
| 167 | const FirmwareVersionReply reply{ | 167 | const FirmwareVersionReply reply{ |
| 168 | .status = DataValid::Valid, | 168 | .status = DataValid::Valid, |
| 169 | .firmware = version, | 169 | .firmware = version, |
| 170 | }; | 170 | }; |
| 171 | 171 | ||
| 172 | return GetDataVector(reply); | 172 | return GetData(reply, out_data); |
| 173 | } | 173 | } |
| 174 | 174 | ||
| 175 | std::vector<u8> RingController::GetReadIdReply() const { | 175 | u64 RingController::GetReadIdReply(std::span<u8> out_data) const { |
| 176 | // The values are hardcoded from a real joycon | 176 | // The values are hardcoded from a real joycon |
| 177 | const ReadIdReply reply{ | 177 | const ReadIdReply reply{ |
| 178 | .status = DataValid::Valid, | 178 | .status = DataValid::Valid, |
| @@ -184,83 +184,83 @@ std::vector<u8> RingController::GetReadIdReply() const { | |||
| 184 | .id_h_x4 = 8245, | 184 | .id_h_x4 = 8245, |
| 185 | }; | 185 | }; |
| 186 | 186 | ||
| 187 | return GetDataVector(reply); | 187 | return GetData(reply, out_data); |
| 188 | } | 188 | } |
| 189 | 189 | ||
| 190 | std::vector<u8> RingController::GetC020105Reply() const { | 190 | u64 RingController::GetC020105Reply(std::span<u8> out_data) const { |
| 191 | const Cmd020105Reply reply{ | 191 | const Cmd020105Reply reply{ |
| 192 | .status = DataValid::Valid, | 192 | .status = DataValid::Valid, |
| 193 | .data = 1, | 193 | .data = 1, |
| 194 | }; | 194 | }; |
| 195 | 195 | ||
| 196 | return GetDataVector(reply); | 196 | return GetData(reply, out_data); |
| 197 | } | 197 | } |
| 198 | 198 | ||
| 199 | std::vector<u8> RingController::GetReadUnkCalReply() const { | 199 | u64 RingController::GetReadUnkCalReply(std::span<u8> out_data) const { |
| 200 | const ReadUnkCalReply reply{ | 200 | const ReadUnkCalReply reply{ |
| 201 | .status = DataValid::Valid, | 201 | .status = DataValid::Valid, |
| 202 | .data = 0, | 202 | .data = 0, |
| 203 | }; | 203 | }; |
| 204 | 204 | ||
| 205 | return GetDataVector(reply); | 205 | return GetData(reply, out_data); |
| 206 | } | 206 | } |
| 207 | 207 | ||
| 208 | std::vector<u8> RingController::GetReadFactoryCalReply() const { | 208 | u64 RingController::GetReadFactoryCalReply(std::span<u8> out_data) const { |
| 209 | const ReadFactoryCalReply reply{ | 209 | const ReadFactoryCalReply reply{ |
| 210 | .status = DataValid::Valid, | 210 | .status = DataValid::Valid, |
| 211 | .calibration = factory_calibration, | 211 | .calibration = factory_calibration, |
| 212 | }; | 212 | }; |
| 213 | 213 | ||
| 214 | return GetDataVector(reply); | 214 | return GetData(reply, out_data); |
| 215 | } | 215 | } |
| 216 | 216 | ||
| 217 | std::vector<u8> RingController::GetReadUserCalReply() const { | 217 | u64 RingController::GetReadUserCalReply(std::span<u8> out_data) const { |
| 218 | const ReadUserCalReply reply{ | 218 | const ReadUserCalReply reply{ |
| 219 | .status = DataValid::Valid, | 219 | .status = DataValid::Valid, |
| 220 | .calibration = user_calibration, | 220 | .calibration = user_calibration, |
| 221 | }; | 221 | }; |
| 222 | 222 | ||
| 223 | return GetDataVector(reply); | 223 | return GetData(reply, out_data); |
| 224 | } | 224 | } |
| 225 | 225 | ||
| 226 | std::vector<u8> RingController::GetReadRepCountReply() const { | 226 | u64 RingController::GetReadRepCountReply(std::span<u8> out_data) const { |
| 227 | const GetThreeByteReply reply{ | 227 | const GetThreeByteReply reply{ |
| 228 | .status = DataValid::Valid, | 228 | .status = DataValid::Valid, |
| 229 | .data = {total_rep_count, 0, 0}, | 229 | .data = {total_rep_count, 0, 0}, |
| 230 | .crc = GetCrcValue({total_rep_count, 0, 0, 0}), | 230 | .crc = GetCrcValue({total_rep_count, 0, 0, 0}), |
| 231 | }; | 231 | }; |
| 232 | 232 | ||
| 233 | return GetDataVector(reply); | 233 | return GetData(reply, out_data); |
| 234 | } | 234 | } |
| 235 | 235 | ||
| 236 | std::vector<u8> RingController::GetReadTotalPushCountReply() const { | 236 | u64 RingController::GetReadTotalPushCountReply(std::span<u8> out_data) const { |
| 237 | const GetThreeByteReply reply{ | 237 | const GetThreeByteReply reply{ |
| 238 | .status = DataValid::Valid, | 238 | .status = DataValid::Valid, |
| 239 | .data = {total_push_count, 0, 0}, | 239 | .data = {total_push_count, 0, 0}, |
| 240 | .crc = GetCrcValue({total_push_count, 0, 0, 0}), | 240 | .crc = GetCrcValue({total_push_count, 0, 0, 0}), |
| 241 | }; | 241 | }; |
| 242 | 242 | ||
| 243 | return GetDataVector(reply); | 243 | return GetData(reply, out_data); |
| 244 | } | 244 | } |
| 245 | 245 | ||
| 246 | std::vector<u8> RingController::GetResetRepCountReply() const { | 246 | u64 RingController::GetResetRepCountReply(std::span<u8> out_data) const { |
| 247 | return GetReadRepCountReply(); | 247 | return GetReadRepCountReply(out_data); |
| 248 | } | 248 | } |
| 249 | 249 | ||
| 250 | std::vector<u8> RingController::GetSaveDataReply() const { | 250 | u64 RingController::GetSaveDataReply(std::span<u8> out_data) const { |
| 251 | const StatusReply reply{ | 251 | const StatusReply reply{ |
| 252 | .status = DataValid::Valid, | 252 | .status = DataValid::Valid, |
| 253 | }; | 253 | }; |
| 254 | 254 | ||
| 255 | return GetDataVector(reply); | 255 | return GetData(reply, out_data); |
| 256 | } | 256 | } |
| 257 | 257 | ||
| 258 | std::vector<u8> RingController::GetErrorReply() const { | 258 | u64 RingController::GetErrorReply(std::span<u8> out_data) const { |
| 259 | const ErrorReply reply{ | 259 | const ErrorReply reply{ |
| 260 | .status = DataValid::BadCRC, | 260 | .status = DataValid::BadCRC, |
| 261 | }; | 261 | }; |
| 262 | 262 | ||
| 263 | return GetDataVector(reply); | 263 | return GetData(reply, out_data); |
| 264 | } | 264 | } |
| 265 | 265 | ||
| 266 | u8 RingController::GetCrcValue(const std::vector<u8>& data) const { | 266 | u8 RingController::GetCrcValue(const std::vector<u8>& data) const { |
| @@ -281,12 +281,11 @@ u8 RingController::GetCrcValue(const std::vector<u8>& data) const { | |||
| 281 | } | 281 | } |
| 282 | 282 | ||
| 283 | template <typename T> | 283 | template <typename T> |
| 284 | std::vector<u8> RingController::GetDataVector(const T& reply) const { | 284 | u64 RingController::GetData(const T& reply, std::span<u8> out_data) const { |
| 285 | static_assert(std::is_trivially_copyable_v<T>); | 285 | static_assert(std::is_trivially_copyable_v<T>); |
| 286 | std::vector<u8> data; | 286 | const auto data_size = static_cast<u64>(std::min(sizeof(reply), out_data.size())); |
| 287 | data.resize(sizeof(reply)); | 287 | std::memcpy(out_data.data(), &reply, data_size); |
| 288 | std::memcpy(data.data(), &reply, sizeof(reply)); | 288 | return data_size; |
| 289 | return data; | ||
| 290 | } | 289 | } |
| 291 | 290 | ||
| 292 | } // namespace Service::HID | 291 | } // namespace Service::HID |
diff --git a/src/hid_core/hidbus/ringcon.h b/src/hid_core/hidbus/ringcon.h index 0953e8100..a48eeed45 100644 --- a/src/hid_core/hidbus/ringcon.h +++ b/src/hid_core/hidbus/ringcon.h | |||
| @@ -34,7 +34,7 @@ public: | |||
| 34 | bool SetCommand(std::span<const u8> data) override; | 34 | bool SetCommand(std::span<const u8> data) override; |
| 35 | 35 | ||
| 36 | // Returns a reply from a command | 36 | // Returns a reply from a command |
| 37 | std::vector<u8> GetReply() const override; | 37 | u64 GetReply(std::span<u8> data) const override; |
| 38 | 38 | ||
| 39 | private: | 39 | private: |
| 40 | // These values are obtained from a real ring controller | 40 | // These values are obtained from a real ring controller |
| @@ -184,44 +184,44 @@ private: | |||
| 184 | RingConData GetSensorValue() const; | 184 | RingConData GetSensorValue() const; |
| 185 | 185 | ||
| 186 | // Returns 8 byte reply with firmware version | 186 | // Returns 8 byte reply with firmware version |
| 187 | std::vector<u8> GetFirmwareVersionReply() const; | 187 | u64 GetFirmwareVersionReply(std::span<u8> out_data) const; |
| 188 | 188 | ||
| 189 | // Returns 16 byte reply with ID values | 189 | // Returns 16 byte reply with ID values |
| 190 | std::vector<u8> GetReadIdReply() const; | 190 | u64 GetReadIdReply(std::span<u8> out_data) const; |
| 191 | 191 | ||
| 192 | // (STUBBED) Returns 8 byte reply | 192 | // (STUBBED) Returns 8 byte reply |
| 193 | std::vector<u8> GetC020105Reply() const; | 193 | u64 GetC020105Reply(std::span<u8> out_data) const; |
| 194 | 194 | ||
| 195 | // (STUBBED) Returns 8 byte empty reply | 195 | // (STUBBED) Returns 8 byte empty reply |
| 196 | std::vector<u8> GetReadUnkCalReply() const; | 196 | u64 GetReadUnkCalReply(std::span<u8> out_data) const; |
| 197 | 197 | ||
| 198 | // Returns 20 byte reply with factory calibration values | 198 | // Returns 20 byte reply with factory calibration values |
| 199 | std::vector<u8> GetReadFactoryCalReply() const; | 199 | u64 GetReadFactoryCalReply(std::span<u8> out_data) const; |
| 200 | 200 | ||
| 201 | // Returns 20 byte reply with user calibration values | 201 | // Returns 20 byte reply with user calibration values |
| 202 | std::vector<u8> GetReadUserCalReply() const; | 202 | u64 GetReadUserCalReply(std::span<u8> out_data) const; |
| 203 | 203 | ||
| 204 | // Returns 8 byte reply | 204 | // Returns 8 byte reply |
| 205 | std::vector<u8> GetReadRepCountReply() const; | 205 | u64 GetReadRepCountReply(std::span<u8> out_data) const; |
| 206 | 206 | ||
| 207 | // Returns 8 byte reply | 207 | // Returns 8 byte reply |
| 208 | std::vector<u8> GetReadTotalPushCountReply() const; | 208 | u64 GetReadTotalPushCountReply(std::span<u8> out_data) const; |
| 209 | 209 | ||
| 210 | // Returns 8 byte reply | 210 | // Returns 8 byte reply |
| 211 | std::vector<u8> GetResetRepCountReply() const; | 211 | u64 GetResetRepCountReply(std::span<u8> out_data) const; |
| 212 | 212 | ||
| 213 | // Returns 4 byte save data reply | 213 | // Returns 4 byte save data reply |
| 214 | std::vector<u8> GetSaveDataReply() const; | 214 | u64 GetSaveDataReply(std::span<u8> out_data) const; |
| 215 | 215 | ||
| 216 | // Returns 8 byte error reply | 216 | // Returns 8 byte error reply |
| 217 | std::vector<u8> GetErrorReply() const; | 217 | u64 GetErrorReply(std::span<u8> out_data) const; |
| 218 | 218 | ||
| 219 | // Returns 8 bit redundancy check from provided data | 219 | // Returns 8 bit redundancy check from provided data |
| 220 | u8 GetCrcValue(const std::vector<u8>& data) const; | 220 | u8 GetCrcValue(const std::vector<u8>& data) const; |
| 221 | 221 | ||
| 222 | // Converts structs to an u8 vector equivalent | 222 | // Converts structs to an u8 vector equivalent |
| 223 | template <typename T> | 223 | template <typename T> |
| 224 | std::vector<u8> GetDataVector(const T& reply) const; | 224 | u64 GetData(const T& reply, std::span<u8> out_data) const; |
| 225 | 225 | ||
| 226 | RingConCommands command{RingConCommands::Error}; | 226 | RingConCommands command{RingConCommands::Error}; |
| 227 | 227 | ||
diff --git a/src/hid_core/hidbus/starlink.cpp b/src/hid_core/hidbus/starlink.cpp index 31b263aa1..1c4df1d8c 100644 --- a/src/hid_core/hidbus/starlink.cpp +++ b/src/hid_core/hidbus/starlink.cpp | |||
| @@ -38,7 +38,7 @@ u8 Starlink::GetDeviceId() const { | |||
| 38 | return DEVICE_ID; | 38 | return DEVICE_ID; |
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | std::vector<u8> Starlink::GetReply() const { | 41 | u64 Starlink::GetReply(std::span<u8> out_data) const { |
| 42 | return {}; | 42 | return {}; |
| 43 | } | 43 | } |
| 44 | 44 | ||
diff --git a/src/hid_core/hidbus/starlink.h b/src/hid_core/hidbus/starlink.h index ee37763b4..695b69748 100644 --- a/src/hid_core/hidbus/starlink.h +++ b/src/hid_core/hidbus/starlink.h | |||
| @@ -31,7 +31,7 @@ public: | |||
| 31 | bool SetCommand(std::span<const u8> data) override; | 31 | bool SetCommand(std::span<const u8> data) override; |
| 32 | 32 | ||
| 33 | // Returns a reply from a command | 33 | // Returns a reply from a command |
| 34 | std::vector<u8> GetReply() const override; | 34 | u64 GetReply(std::span<u8> out_data) const override; |
| 35 | }; | 35 | }; |
| 36 | 36 | ||
| 37 | } // namespace Service::HID | 37 | } // namespace Service::HID |
diff --git a/src/hid_core/hidbus/stubbed.cpp b/src/hid_core/hidbus/stubbed.cpp index f16051aa9..658922a4f 100644 --- a/src/hid_core/hidbus/stubbed.cpp +++ b/src/hid_core/hidbus/stubbed.cpp | |||
| @@ -38,7 +38,7 @@ u8 HidbusStubbed::GetDeviceId() const { | |||
| 38 | return DEVICE_ID; | 38 | return DEVICE_ID; |
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | std::vector<u8> HidbusStubbed::GetReply() const { | 41 | u64 HidbusStubbed::GetReply(std::span<u8> out_data) const { |
| 42 | return {}; | 42 | return {}; |
| 43 | } | 43 | } |
| 44 | 44 | ||
diff --git a/src/hid_core/hidbus/stubbed.h b/src/hid_core/hidbus/stubbed.h index 7a711cea0..f05280b3a 100644 --- a/src/hid_core/hidbus/stubbed.h +++ b/src/hid_core/hidbus/stubbed.h | |||
| @@ -31,7 +31,7 @@ public: | |||
| 31 | bool SetCommand(std::span<const u8> data) override; | 31 | bool SetCommand(std::span<const u8> data) override; |
| 32 | 32 | ||
| 33 | // Returns a reply from a command | 33 | // Returns a reply from a command |
| 34 | std::vector<u8> GetReply() const override; | 34 | u64 GetReply(std::span<u8> out_data) const override; |
| 35 | }; | 35 | }; |
| 36 | 36 | ||
| 37 | } // namespace Service::HID | 37 | } // namespace Service::HID |
diff --git a/src/hid_core/irsensor/image_transfer_processor.cpp b/src/hid_core/irsensor/image_transfer_processor.cpp index d6573f8dc..2b5a50ef6 100644 --- a/src/hid_core/irsensor/image_transfer_processor.cpp +++ b/src/hid_core/irsensor/image_transfer_processor.cpp | |||
| @@ -145,9 +145,8 @@ void ImageTransferProcessor::SetTransferMemoryAddress(Common::ProcessAddress t_m | |||
| 145 | } | 145 | } |
| 146 | 146 | ||
| 147 | Core::IrSensor::ImageTransferProcessorState ImageTransferProcessor::GetState( | 147 | Core::IrSensor::ImageTransferProcessorState ImageTransferProcessor::GetState( |
| 148 | std::vector<u8>& data) const { | 148 | std::span<u8> data) const { |
| 149 | const auto size = GetDataSize(current_config.trimming_format); | 149 | const auto size = std::min(GetDataSize(current_config.trimming_format), data.size()); |
| 150 | data.resize(size); | ||
| 151 | system.ApplicationMemory().ReadBlock(transfer_memory, data.data(), size); | 150 | system.ApplicationMemory().ReadBlock(transfer_memory, data.data(), size); |
| 152 | return processor_state; | 151 | return processor_state; |
| 153 | } | 152 | } |
diff --git a/src/hid_core/irsensor/image_transfer_processor.h b/src/hid_core/irsensor/image_transfer_processor.h index 4e0117084..df1c9d920 100644 --- a/src/hid_core/irsensor/image_transfer_processor.h +++ b/src/hid_core/irsensor/image_transfer_processor.h | |||
| @@ -3,6 +3,8 @@ | |||
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <span> | ||
| 7 | |||
| 6 | #include "common/typed_address.h" | 8 | #include "common/typed_address.h" |
| 7 | #include "hid_core/irsensor/irs_types.h" | 9 | #include "hid_core/irsensor/irs_types.h" |
| 8 | #include "hid_core/irsensor/processor_base.h" | 10 | #include "hid_core/irsensor/processor_base.h" |
| @@ -39,7 +41,7 @@ public: | |||
| 39 | // Transfer memory where the image data will be stored | 41 | // Transfer memory where the image data will be stored |
| 40 | void SetTransferMemoryAddress(Common::ProcessAddress t_mem); | 42 | void SetTransferMemoryAddress(Common::ProcessAddress t_mem); |
| 41 | 43 | ||
| 42 | Core::IrSensor::ImageTransferProcessorState GetState(std::vector<u8>& data) const; | 44 | Core::IrSensor::ImageTransferProcessorState GetState(std::span<u8> data) const; |
| 43 | 45 | ||
| 44 | private: | 46 | private: |
| 45 | // This is nn::irsensor::ImageTransferProcessorConfig | 47 | // This is nn::irsensor::ImageTransferProcessorConfig |
diff --git a/src/hid_core/resource_manager.cpp b/src/hid_core/resource_manager.cpp index 245da582e..01261ba97 100644 --- a/src/hid_core/resource_manager.cpp +++ b/src/hid_core/resource_manager.cpp | |||
| @@ -314,6 +314,7 @@ void ResourceManager::UnregisterAppletResourceUserId(u64 aruid) { | |||
| 314 | std::scoped_lock lock{shared_mutex}; | 314 | std::scoped_lock lock{shared_mutex}; |
| 315 | applet_resource->UnregisterAppletResourceUserId(aruid); | 315 | applet_resource->UnregisterAppletResourceUserId(aruid); |
| 316 | npad->UnregisterAppletResourceUserId(aruid); | 316 | npad->UnregisterAppletResourceUserId(aruid); |
| 317 | // palma->UnregisterAppletResourceUserId(aruid); | ||
| 317 | } | 318 | } |
| 318 | 319 | ||
| 319 | Result ResourceManager::GetSharedMemoryHandle(Kernel::KSharedMemory** out_handle, u64 aruid) { | 320 | Result ResourceManager::GetSharedMemoryHandle(Kernel::KSharedMemory** out_handle, u64 aruid) { |
| @@ -324,6 +325,7 @@ Result ResourceManager::GetSharedMemoryHandle(Kernel::KSharedMemory** out_handle | |||
| 324 | void ResourceManager::FreeAppletResourceId(u64 aruid) { | 325 | void ResourceManager::FreeAppletResourceId(u64 aruid) { |
| 325 | std::scoped_lock lock{shared_mutex}; | 326 | std::scoped_lock lock{shared_mutex}; |
| 326 | applet_resource->FreeAppletResourceId(aruid); | 327 | applet_resource->FreeAppletResourceId(aruid); |
| 328 | npad->FreeAppletResourceId(aruid); | ||
| 327 | } | 329 | } |
| 328 | 330 | ||
| 329 | void ResourceManager::EnableInput(u64 aruid, bool is_enabled) { | 331 | void ResourceManager::EnableInput(u64 aruid, bool is_enabled) { |
diff --git a/src/hid_core/resources/abstracted_pad/abstract_sixaxis_handler.cpp b/src/hid_core/resources/abstracted_pad/abstract_sixaxis_handler.cpp index 6d759298e..0dde244ef 100644 --- a/src/hid_core/resources/abstracted_pad/abstract_sixaxis_handler.cpp +++ b/src/hid_core/resources/abstracted_pad/abstract_sixaxis_handler.cpp | |||
| @@ -57,7 +57,7 @@ Result NpadAbstractSixAxisHandler::UpdateSixAxisState() { | |||
| 57 | Core::HID::NpadIdType npad_id = properties_handler->GetNpadId(); | 57 | Core::HID::NpadIdType npad_id = properties_handler->GetNpadId(); |
| 58 | for (std::size_t i = 0; i < AruidIndexMax; i++) { | 58 | for (std::size_t i = 0; i < AruidIndexMax; i++) { |
| 59 | auto* data = applet_resource_holder->applet_resource->GetAruidDataByIndex(i); | 59 | auto* data = applet_resource_holder->applet_resource->GetAruidDataByIndex(i); |
| 60 | if (data->flag.is_assigned) { | 60 | if (data == nullptr || !data->flag.is_assigned) { |
| 61 | continue; | 61 | continue; |
| 62 | } | 62 | } |
| 63 | auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)]; | 63 | auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)]; |
diff --git a/src/hid_core/resources/npad/npad.cpp b/src/hid_core/resources/npad/npad.cpp index fe3fdc5cd..e10e97e1c 100644 --- a/src/hid_core/resources/npad/npad.cpp +++ b/src/hid_core/resources/npad/npad.cpp | |||
| @@ -117,6 +117,10 @@ Result NPad::ActivateNpadResource(u64 aruid) { | |||
| 117 | return npad_resource.Activate(aruid); | 117 | return npad_resource.Activate(aruid); |
| 118 | } | 118 | } |
| 119 | 119 | ||
| 120 | void NPad::FreeAppletResourceId(u64 aruid) { | ||
| 121 | return npad_resource.FreeAppletResourceId(aruid); | ||
| 122 | } | ||
| 123 | |||
| 120 | void NPad::ControllerUpdate(Core::HID::ControllerTriggerType type, std::size_t controller_idx) { | 124 | void NPad::ControllerUpdate(Core::HID::ControllerTriggerType type, std::size_t controller_idx) { |
| 121 | if (type == Core::HID::ControllerTriggerType::All) { | 125 | if (type == Core::HID::ControllerTriggerType::All) { |
| 122 | ControllerUpdate(Core::HID::ControllerTriggerType::Connected, controller_idx); | 126 | ControllerUpdate(Core::HID::ControllerTriggerType::Connected, controller_idx); |
| @@ -131,7 +135,7 @@ void NPad::ControllerUpdate(Core::HID::ControllerTriggerType type, std::size_t c | |||
| 131 | 135 | ||
| 132 | auto* data = applet_resource_holder.applet_resource->GetAruidDataByIndex(aruid_index); | 136 | auto* data = applet_resource_holder.applet_resource->GetAruidDataByIndex(aruid_index); |
| 133 | 137 | ||
| 134 | if (!data->flag.is_assigned) { | 138 | if (data == nullptr || !data->flag.is_assigned) { |
| 135 | continue; | 139 | continue; |
| 136 | } | 140 | } |
| 137 | 141 | ||
| @@ -463,13 +467,13 @@ void NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { | |||
| 463 | std::scoped_lock lock{*applet_resource_holder.shared_mutex}; | 467 | std::scoped_lock lock{*applet_resource_holder.shared_mutex}; |
| 464 | for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; ++aruid_index) { | 468 | for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; ++aruid_index) { |
| 465 | const auto* data = applet_resource_holder.applet_resource->GetAruidDataByIndex(aruid_index); | 469 | const auto* data = applet_resource_holder.applet_resource->GetAruidDataByIndex(aruid_index); |
| 466 | const auto aruid = data->aruid; | ||
| 467 | 470 | ||
| 468 | if (!data->flag.is_assigned) { | 471 | if (data == nullptr || !data->flag.is_assigned) { |
| 469 | continue; | 472 | continue; |
| 470 | } | 473 | } |
| 471 | 474 | ||
| 472 | bool is_set{}; | 475 | bool is_set{}; |
| 476 | const auto aruid = data->aruid; | ||
| 473 | npad_resource.IsSupportedNpadStyleSet(is_set, aruid); | 477 | npad_resource.IsSupportedNpadStyleSet(is_set, aruid); |
| 474 | // Wait until style is defined | 478 | // Wait until style is defined |
| 475 | if (!is_set) { | 479 | if (!is_set) { |
diff --git a/src/hid_core/resources/npad/npad.h b/src/hid_core/resources/npad/npad.h index c63488346..99e761127 100644 --- a/src/hid_core/resources/npad/npad.h +++ b/src/hid_core/resources/npad/npad.h | |||
| @@ -58,6 +58,8 @@ public: | |||
| 58 | Result ActivateNpadResource(); | 58 | Result ActivateNpadResource(); |
| 59 | Result ActivateNpadResource(u64 aruid); | 59 | Result ActivateNpadResource(u64 aruid); |
| 60 | 60 | ||
| 61 | void FreeAppletResourceId(u64 aruid); | ||
| 62 | |||
| 61 | // When the controller is requesting an update for the shared memory | 63 | // When the controller is requesting an update for the shared memory |
| 62 | void OnUpdate(const Core::Timing::CoreTiming& core_timing); | 64 | void OnUpdate(const Core::Timing::CoreTiming& core_timing); |
| 63 | 65 | ||
diff --git a/src/hid_core/resources/npad/npad_resource.cpp b/src/hid_core/resources/npad/npad_resource.cpp index 8dd86b58e..79f7d74c0 100644 --- a/src/hid_core/resources/npad/npad_resource.cpp +++ b/src/hid_core/resources/npad/npad_resource.cpp | |||
| @@ -67,7 +67,7 @@ Result NPadResource::RegisterAppletResourceUserId(u64 aruid) { | |||
| 67 | void NPadResource::UnregisterAppletResourceUserId(u64 aruid) { | 67 | void NPadResource::UnregisterAppletResourceUserId(u64 aruid) { |
| 68 | const u64 aruid_index = GetIndexFromAruid(aruid); | 68 | const u64 aruid_index = GetIndexFromAruid(aruid); |
| 69 | 69 | ||
| 70 | DestroyStyleSetUpdateEvents(aruid); | 70 | FreeAppletResourceId(aruid); |
| 71 | if (aruid_index < AruidIndexMax) { | 71 | if (aruid_index < AruidIndexMax) { |
| 72 | state[aruid_index] = {}; | 72 | state[aruid_index] = {}; |
| 73 | registration_list.flag[aruid_index] = RegistrationStatus::PendingDelete; | 73 | registration_list.flag[aruid_index] = RegistrationStatus::PendingDelete; |
| @@ -80,14 +80,18 @@ void NPadResource::UnregisterAppletResourceUserId(u64 aruid) { | |||
| 80 | } | 80 | } |
| 81 | } | 81 | } |
| 82 | 82 | ||
| 83 | void NPadResource::DestroyStyleSetUpdateEvents(u64 aruid) { | 83 | void NPadResource::FreeAppletResourceId(u64 aruid) { |
| 84 | const u64 aruid_index = GetIndexFromAruid(aruid); | 84 | const u64 aruid_index = GetIndexFromAruid(aruid); |
| 85 | 85 | ||
| 86 | if (aruid_index >= AruidIndexMax) { | 86 | if (aruid_index >= AruidIndexMax) { |
| 87 | return; | 87 | return; |
| 88 | } | 88 | } |
| 89 | 89 | ||
| 90 | for (auto& controller_state : state[aruid_index].controller_state) { | 90 | auto& aruid_data = state[aruid_index]; |
| 91 | |||
| 92 | aruid_data.flag.is_assigned.Assign(false); | ||
| 93 | |||
| 94 | for (auto& controller_state : aruid_data.controller_state) { | ||
| 91 | if (!controller_state.is_styleset_update_event_initialized) { | 95 | if (!controller_state.is_styleset_update_event_initialized) { |
| 92 | continue; | 96 | continue; |
| 93 | } | 97 | } |
diff --git a/src/hid_core/resources/npad/npad_resource.h b/src/hid_core/resources/npad/npad_resource.h index aed89eec6..8ee5702fd 100644 --- a/src/hid_core/resources/npad/npad_resource.h +++ b/src/hid_core/resources/npad/npad_resource.h | |||
| @@ -55,7 +55,7 @@ public: | |||
| 55 | Result RegisterAppletResourceUserId(u64 aruid); | 55 | Result RegisterAppletResourceUserId(u64 aruid); |
| 56 | void UnregisterAppletResourceUserId(u64 aruid); | 56 | void UnregisterAppletResourceUserId(u64 aruid); |
| 57 | 57 | ||
| 58 | void DestroyStyleSetUpdateEvents(u64 aruid); | 58 | void FreeAppletResourceId(u64 aruid); |
| 59 | 59 | ||
| 60 | Result Activate(u64 aruid); | 60 | Result Activate(u64 aruid); |
| 61 | Result Activate(); | 61 | Result Activate(); |
diff --git a/src/hid_core/resources/six_axis/six_axis.cpp b/src/hid_core/resources/six_axis/six_axis.cpp index abb6fd152..b407a5c76 100644 --- a/src/hid_core/resources/six_axis/six_axis.cpp +++ b/src/hid_core/resources/six_axis/six_axis.cpp | |||
| @@ -28,142 +28,148 @@ void SixAxis::OnRelease() {} | |||
| 28 | 28 | ||
| 29 | void SixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) { | 29 | void SixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) { |
| 30 | std::scoped_lock shared_lock{*shared_mutex}; | 30 | std::scoped_lock shared_lock{*shared_mutex}; |
| 31 | const u64 aruid = applet_resource->GetActiveAruid(); | ||
| 32 | auto* data = applet_resource->GetAruidData(aruid); | ||
| 33 | 31 | ||
| 34 | if (data == nullptr || !data->flag.is_assigned) { | 32 | for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; ++aruid_index) { |
| 35 | return; | 33 | const auto* data = applet_resource->GetAruidDataByIndex(aruid_index); |
| 36 | } | ||
| 37 | |||
| 38 | if (!IsControllerActivated()) { | ||
| 39 | return; | ||
| 40 | } | ||
| 41 | 34 | ||
| 42 | for (std::size_t i = 0; i < controller_data.size(); ++i) { | 35 | if (data == nullptr || !data->flag.is_assigned) { |
| 43 | NpadSharedMemoryEntry& shared_memory = data->shared_memory_format->npad.npad_entry[i]; | ||
| 44 | auto& controller = controller_data[i]; | ||
| 45 | const auto& controller_type = controller.device->GetNpadStyleIndex(); | ||
| 46 | |||
| 47 | if (controller_type == Core::HID::NpadStyleIndex::None || | ||
| 48 | !controller.device->IsConnected()) { | ||
| 49 | continue; | 36 | continue; |
| 50 | } | 37 | } |
| 51 | 38 | ||
| 52 | const auto& motion_state = controller.device->GetMotions(); | 39 | if (!IsControllerActivated()) { |
| 53 | auto& sixaxis_fullkey_state = controller.sixaxis_fullkey_state; | 40 | return; |
| 54 | auto& sixaxis_handheld_state = controller.sixaxis_handheld_state; | ||
| 55 | auto& sixaxis_dual_left_state = controller.sixaxis_dual_left_state; | ||
| 56 | auto& sixaxis_dual_right_state = controller.sixaxis_dual_right_state; | ||
| 57 | auto& sixaxis_left_lifo_state = controller.sixaxis_left_lifo_state; | ||
| 58 | auto& sixaxis_right_lifo_state = controller.sixaxis_right_lifo_state; | ||
| 59 | |||
| 60 | auto& sixaxis_fullkey_lifo = shared_memory.internal_state.sixaxis_fullkey_lifo; | ||
| 61 | auto& sixaxis_handheld_lifo = shared_memory.internal_state.sixaxis_handheld_lifo; | ||
| 62 | auto& sixaxis_dual_left_lifo = shared_memory.internal_state.sixaxis_dual_left_lifo; | ||
| 63 | auto& sixaxis_dual_right_lifo = shared_memory.internal_state.sixaxis_dual_right_lifo; | ||
| 64 | auto& sixaxis_left_lifo = shared_memory.internal_state.sixaxis_left_lifo; | ||
| 65 | auto& sixaxis_right_lifo = shared_memory.internal_state.sixaxis_right_lifo; | ||
| 66 | |||
| 67 | // Clear previous state | ||
| 68 | sixaxis_fullkey_state = {}; | ||
| 69 | sixaxis_handheld_state = {}; | ||
| 70 | sixaxis_dual_left_state = {}; | ||
| 71 | sixaxis_dual_right_state = {}; | ||
| 72 | sixaxis_left_lifo_state = {}; | ||
| 73 | sixaxis_right_lifo_state = {}; | ||
| 74 | |||
| 75 | if (controller.sixaxis_sensor_enabled && Settings::values.motion_enabled.GetValue()) { | ||
| 76 | controller.sixaxis_at_rest = true; | ||
| 77 | for (std::size_t e = 0; e < motion_state.size(); ++e) { | ||
| 78 | controller.sixaxis_at_rest = | ||
| 79 | controller.sixaxis_at_rest && motion_state[e].is_at_rest; | ||
| 80 | } | ||
| 81 | } | 41 | } |
| 82 | 42 | ||
| 83 | const auto set_motion_state = [&](Core::HID::SixAxisSensorState& state, | 43 | for (std::size_t i = 0; i < controller_data.size(); ++i) { |
| 84 | const Core::HID::ControllerMotion& hid_state) { | 44 | NpadSharedMemoryEntry& shared_memory = data->shared_memory_format->npad.npad_entry[i]; |
| 85 | using namespace std::literals::chrono_literals; | 45 | auto& controller = controller_data[i]; |
| 86 | static constexpr Core::HID::SixAxisSensorState default_motion_state = { | 46 | const auto& controller_type = controller.device->GetNpadStyleIndex(); |
| 87 | .delta_time = std::chrono::nanoseconds(5ms).count(), | 47 | |
| 88 | .accel = {0, 0, -1.0f}, | 48 | if (!data->flag.enable_six_axis_sensor) { |
| 89 | .orientation = | 49 | continue; |
| 90 | { | 50 | } |
| 91 | Common::Vec3f{1.0f, 0, 0}, | 51 | |
| 92 | Common::Vec3f{0, 1.0f, 0}, | 52 | if (controller_type == Core::HID::NpadStyleIndex::None || |
| 93 | Common::Vec3f{0, 0, 1.0f}, | 53 | !controller.device->IsConnected()) { |
| 94 | }, | 54 | continue; |
| 95 | .attribute = {1}, | 55 | } |
| 56 | |||
| 57 | const auto& motion_state = controller.device->GetMotions(); | ||
| 58 | auto& sixaxis_fullkey_state = controller.sixaxis_fullkey_state; | ||
| 59 | auto& sixaxis_handheld_state = controller.sixaxis_handheld_state; | ||
| 60 | auto& sixaxis_dual_left_state = controller.sixaxis_dual_left_state; | ||
| 61 | auto& sixaxis_dual_right_state = controller.sixaxis_dual_right_state; | ||
| 62 | auto& sixaxis_left_lifo_state = controller.sixaxis_left_lifo_state; | ||
| 63 | auto& sixaxis_right_lifo_state = controller.sixaxis_right_lifo_state; | ||
| 64 | |||
| 65 | auto& sixaxis_fullkey_lifo = shared_memory.internal_state.sixaxis_fullkey_lifo; | ||
| 66 | auto& sixaxis_handheld_lifo = shared_memory.internal_state.sixaxis_handheld_lifo; | ||
| 67 | auto& sixaxis_dual_left_lifo = shared_memory.internal_state.sixaxis_dual_left_lifo; | ||
| 68 | auto& sixaxis_dual_right_lifo = shared_memory.internal_state.sixaxis_dual_right_lifo; | ||
| 69 | auto& sixaxis_left_lifo = shared_memory.internal_state.sixaxis_left_lifo; | ||
| 70 | auto& sixaxis_right_lifo = shared_memory.internal_state.sixaxis_right_lifo; | ||
| 71 | |||
| 72 | // Clear previous state | ||
| 73 | sixaxis_fullkey_state = {}; | ||
| 74 | sixaxis_handheld_state = {}; | ||
| 75 | sixaxis_dual_left_state = {}; | ||
| 76 | sixaxis_dual_right_state = {}; | ||
| 77 | sixaxis_left_lifo_state = {}; | ||
| 78 | sixaxis_right_lifo_state = {}; | ||
| 79 | |||
| 80 | if (controller.sixaxis_sensor_enabled && Settings::values.motion_enabled.GetValue()) { | ||
| 81 | controller.sixaxis_at_rest = true; | ||
| 82 | for (std::size_t e = 0; e < motion_state.size(); ++e) { | ||
| 83 | controller.sixaxis_at_rest = | ||
| 84 | controller.sixaxis_at_rest && motion_state[e].is_at_rest; | ||
| 85 | } | ||
| 86 | } | ||
| 87 | |||
| 88 | const auto set_motion_state = [&](Core::HID::SixAxisSensorState& state, | ||
| 89 | const Core::HID::ControllerMotion& hid_state) { | ||
| 90 | using namespace std::literals::chrono_literals; | ||
| 91 | static constexpr Core::HID::SixAxisSensorState default_motion_state = { | ||
| 92 | .delta_time = std::chrono::nanoseconds(5ms).count(), | ||
| 93 | .accel = {0, 0, -1.0f}, | ||
| 94 | .orientation = | ||
| 95 | { | ||
| 96 | Common::Vec3f{1.0f, 0, 0}, | ||
| 97 | Common::Vec3f{0, 1.0f, 0}, | ||
| 98 | Common::Vec3f{0, 0, 1.0f}, | ||
| 99 | }, | ||
| 100 | .attribute = {1}, | ||
| 101 | }; | ||
| 102 | if (!controller.sixaxis_sensor_enabled) { | ||
| 103 | state = default_motion_state; | ||
| 104 | return; | ||
| 105 | } | ||
| 106 | if (!Settings::values.motion_enabled.GetValue()) { | ||
| 107 | state = default_motion_state; | ||
| 108 | return; | ||
| 109 | } | ||
| 110 | state.attribute.is_connected.Assign(1); | ||
| 111 | state.delta_time = std::chrono::nanoseconds(5ms).count(); | ||
| 112 | state.accel = hid_state.accel; | ||
| 113 | state.gyro = hid_state.gyro; | ||
| 114 | state.rotation = hid_state.rotation; | ||
| 115 | state.orientation = hid_state.orientation; | ||
| 96 | }; | 116 | }; |
| 97 | if (!controller.sixaxis_sensor_enabled) { | 117 | |
| 98 | state = default_motion_state; | 118 | switch (controller_type) { |
| 99 | return; | 119 | case Core::HID::NpadStyleIndex::None: |
| 120 | ASSERT(false); | ||
| 121 | break; | ||
| 122 | case Core::HID::NpadStyleIndex::Fullkey: | ||
| 123 | set_motion_state(sixaxis_fullkey_state, motion_state[0]); | ||
| 124 | break; | ||
| 125 | case Core::HID::NpadStyleIndex::Handheld: | ||
| 126 | set_motion_state(sixaxis_handheld_state, motion_state[0]); | ||
| 127 | break; | ||
| 128 | case Core::HID::NpadStyleIndex::JoyconDual: | ||
| 129 | set_motion_state(sixaxis_dual_left_state, motion_state[0]); | ||
| 130 | set_motion_state(sixaxis_dual_right_state, motion_state[1]); | ||
| 131 | break; | ||
| 132 | case Core::HID::NpadStyleIndex::JoyconLeft: | ||
| 133 | set_motion_state(sixaxis_left_lifo_state, motion_state[0]); | ||
| 134 | break; | ||
| 135 | case Core::HID::NpadStyleIndex::JoyconRight: | ||
| 136 | set_motion_state(sixaxis_right_lifo_state, motion_state[1]); | ||
| 137 | break; | ||
| 138 | case Core::HID::NpadStyleIndex::Pokeball: | ||
| 139 | using namespace std::literals::chrono_literals; | ||
| 140 | set_motion_state(sixaxis_fullkey_state, motion_state[0]); | ||
| 141 | sixaxis_fullkey_state.delta_time = std::chrono::nanoseconds(15ms).count(); | ||
| 142 | break; | ||
| 143 | default: | ||
| 144 | break; | ||
| 100 | } | 145 | } |
| 101 | if (!Settings::values.motion_enabled.GetValue()) { | 146 | |
| 102 | state = default_motion_state; | 147 | sixaxis_fullkey_state.sampling_number = |
| 103 | return; | 148 | sixaxis_fullkey_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1; |
| 149 | sixaxis_handheld_state.sampling_number = | ||
| 150 | sixaxis_handheld_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 151 | sixaxis_dual_left_state.sampling_number = | ||
| 152 | sixaxis_dual_left_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 153 | sixaxis_dual_right_state.sampling_number = | ||
| 154 | sixaxis_dual_right_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 155 | sixaxis_left_lifo_state.sampling_number = | ||
| 156 | sixaxis_left_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 157 | sixaxis_right_lifo_state.sampling_number = | ||
| 158 | sixaxis_right_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 159 | |||
| 160 | if (IndexToNpadIdType(i) == Core::HID::NpadIdType::Handheld) { | ||
| 161 | // This buffer only is updated on handheld on HW | ||
| 162 | sixaxis_handheld_lifo.lifo.WriteNextEntry(sixaxis_handheld_state); | ||
| 163 | } else { | ||
| 164 | // Handheld doesn't update this buffer on HW | ||
| 165 | sixaxis_fullkey_lifo.lifo.WriteNextEntry(sixaxis_fullkey_state); | ||
| 104 | } | 166 | } |
| 105 | state.attribute.is_connected.Assign(1); | ||
| 106 | state.delta_time = std::chrono::nanoseconds(5ms).count(); | ||
| 107 | state.accel = hid_state.accel; | ||
| 108 | state.gyro = hid_state.gyro; | ||
| 109 | state.rotation = hid_state.rotation; | ||
| 110 | state.orientation = hid_state.orientation; | ||
| 111 | }; | ||
| 112 | |||
| 113 | switch (controller_type) { | ||
| 114 | case Core::HID::NpadStyleIndex::None: | ||
| 115 | ASSERT(false); | ||
| 116 | break; | ||
| 117 | case Core::HID::NpadStyleIndex::Fullkey: | ||
| 118 | set_motion_state(sixaxis_fullkey_state, motion_state[0]); | ||
| 119 | break; | ||
| 120 | case Core::HID::NpadStyleIndex::Handheld: | ||
| 121 | set_motion_state(sixaxis_handheld_state, motion_state[0]); | ||
| 122 | break; | ||
| 123 | case Core::HID::NpadStyleIndex::JoyconDual: | ||
| 124 | set_motion_state(sixaxis_dual_left_state, motion_state[0]); | ||
| 125 | set_motion_state(sixaxis_dual_right_state, motion_state[1]); | ||
| 126 | break; | ||
| 127 | case Core::HID::NpadStyleIndex::JoyconLeft: | ||
| 128 | set_motion_state(sixaxis_left_lifo_state, motion_state[0]); | ||
| 129 | break; | ||
| 130 | case Core::HID::NpadStyleIndex::JoyconRight: | ||
| 131 | set_motion_state(sixaxis_right_lifo_state, motion_state[1]); | ||
| 132 | break; | ||
| 133 | case Core::HID::NpadStyleIndex::Pokeball: | ||
| 134 | using namespace std::literals::chrono_literals; | ||
| 135 | set_motion_state(sixaxis_fullkey_state, motion_state[0]); | ||
| 136 | sixaxis_fullkey_state.delta_time = std::chrono::nanoseconds(15ms).count(); | ||
| 137 | break; | ||
| 138 | default: | ||
| 139 | break; | ||
| 140 | } | ||
| 141 | 167 | ||
| 142 | sixaxis_fullkey_state.sampling_number = | 168 | sixaxis_dual_left_lifo.lifo.WriteNextEntry(sixaxis_dual_left_state); |
| 143 | sixaxis_fullkey_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1; | 169 | sixaxis_dual_right_lifo.lifo.WriteNextEntry(sixaxis_dual_right_state); |
| 144 | sixaxis_handheld_state.sampling_number = | 170 | sixaxis_left_lifo.lifo.WriteNextEntry(sixaxis_left_lifo_state); |
| 145 | sixaxis_handheld_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1; | 171 | sixaxis_right_lifo.lifo.WriteNextEntry(sixaxis_right_lifo_state); |
| 146 | sixaxis_dual_left_state.sampling_number = | ||
| 147 | sixaxis_dual_left_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 148 | sixaxis_dual_right_state.sampling_number = | ||
| 149 | sixaxis_dual_right_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 150 | sixaxis_left_lifo_state.sampling_number = | ||
| 151 | sixaxis_left_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 152 | sixaxis_right_lifo_state.sampling_number = | ||
| 153 | sixaxis_right_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 154 | |||
| 155 | if (IndexToNpadIdType(i) == Core::HID::NpadIdType::Handheld) { | ||
| 156 | // This buffer only is updated on handheld on HW | ||
| 157 | sixaxis_handheld_lifo.lifo.WriteNextEntry(sixaxis_handheld_state); | ||
| 158 | } else { | ||
| 159 | // Handheld doesn't update this buffer on HW | ||
| 160 | sixaxis_fullkey_lifo.lifo.WriteNextEntry(sixaxis_fullkey_state); | ||
| 161 | } | 172 | } |
| 162 | |||
| 163 | sixaxis_dual_left_lifo.lifo.WriteNextEntry(sixaxis_dual_left_state); | ||
| 164 | sixaxis_dual_right_lifo.lifo.WriteNextEntry(sixaxis_dual_right_state); | ||
| 165 | sixaxis_left_lifo.lifo.WriteNextEntry(sixaxis_left_lifo_state); | ||
| 166 | sixaxis_right_lifo.lifo.WriteNextEntry(sixaxis_right_lifo_state); | ||
| 167 | } | 173 | } |
| 168 | } | 174 | } |
| 169 | 175 | ||
diff --git a/src/hid_core/resources/touch_screen/touch_screen_resource.cpp b/src/hid_core/resources/touch_screen/touch_screen_resource.cpp index 56e8e8e51..c39321915 100644 --- a/src/hid_core/resources/touch_screen/touch_screen_resource.cpp +++ b/src/hid_core/resources/touch_screen/touch_screen_resource.cpp | |||
| @@ -63,7 +63,7 @@ Result TouchResource::ActivateTouch(u64 aruid) { | |||
| 63 | auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index); | 63 | auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index); |
| 64 | TouchAruidData& touch_data = aruid_data[aruid_index]; | 64 | TouchAruidData& touch_data = aruid_data[aruid_index]; |
| 65 | 65 | ||
| 66 | if (!applet_data->flag.is_assigned) { | 66 | if (applet_data == nullptr || !applet_data->flag.is_assigned) { |
| 67 | touch_data = {}; | 67 | touch_data = {}; |
| 68 | continue; | 68 | continue; |
| 69 | } | 69 | } |
| @@ -124,7 +124,7 @@ Result TouchResource::ActivateGesture(u64 aruid, u32 basic_gesture_id) { | |||
| 124 | auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index); | 124 | auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index); |
| 125 | TouchAruidData& touch_data = aruid_data[aruid_index]; | 125 | TouchAruidData& touch_data = aruid_data[aruid_index]; |
| 126 | 126 | ||
| 127 | if (!applet_data->flag.is_assigned) { | 127 | if (applet_data == nullptr || !applet_data->flag.is_assigned) { |
| 128 | touch_data = {}; | 128 | touch_data = {}; |
| 129 | continue; | 129 | continue; |
| 130 | } | 130 | } |
| @@ -324,7 +324,7 @@ Result TouchResource::SetTouchScreenConfiguration( | |||
| 324 | const auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index); | 324 | const auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index); |
| 325 | TouchAruidData& data = aruid_data[aruid_index]; | 325 | TouchAruidData& data = aruid_data[aruid_index]; |
| 326 | 326 | ||
| 327 | if (!applet_data->flag.is_assigned) { | 327 | if (applet_data == nullptr || !applet_data->flag.is_assigned) { |
| 328 | continue; | 328 | continue; |
| 329 | } | 329 | } |
| 330 | if (aruid != data.aruid) { | 330 | if (aruid != data.aruid) { |
| @@ -344,7 +344,7 @@ Result TouchResource::GetTouchScreenConfiguration( | |||
| 344 | const auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index); | 344 | const auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index); |
| 345 | const TouchAruidData& data = aruid_data[aruid_index]; | 345 | const TouchAruidData& data = aruid_data[aruid_index]; |
| 346 | 346 | ||
| 347 | if (!applet_data->flag.is_assigned) { | 347 | if (applet_data == nullptr || !applet_data->flag.is_assigned) { |
| 348 | continue; | 348 | continue; |
| 349 | } | 349 | } |
| 350 | if (aruid != data.aruid) { | 350 | if (aruid != data.aruid) { |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp index 64a4e0e55..44281e407 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp | |||
| @@ -60,10 +60,11 @@ public: | |||
| 60 | Add(spv::ImageOperandsMask::ConstOffsets, offsets); | 60 | Add(spv::ImageOperandsMask::ConstOffsets, offsets); |
| 61 | } | 61 | } |
| 62 | 62 | ||
| 63 | explicit ImageOperands(Id lod, Id ms) { | 63 | explicit ImageOperands(EmitContext& ctx, const IR::Value& offset, Id lod, Id ms) { |
| 64 | if (Sirit::ValidId(lod)) { | 64 | if (Sirit::ValidId(lod)) { |
| 65 | Add(spv::ImageOperandsMask::Lod, lod); | 65 | Add(spv::ImageOperandsMask::Lod, lod); |
| 66 | } | 66 | } |
| 67 | AddOffset(ctx, offset, ImageFetchOffsetAllowed); | ||
| 67 | if (Sirit::ValidId(ms)) { | 68 | if (Sirit::ValidId(ms)) { |
| 68 | Add(spv::ImageOperandsMask::Sample, ms); | 69 | Add(spv::ImageOperandsMask::Sample, ms); |
| 69 | } | 70 | } |
| @@ -311,37 +312,6 @@ Id ImageGatherSubpixelOffset(EmitContext& ctx, const IR::TextureInstInfo& info, | |||
| 311 | return coords; | 312 | return coords; |
| 312 | } | 313 | } |
| 313 | } | 314 | } |
| 314 | |||
| 315 | void AddOffsetToCoordinates(EmitContext& ctx, const IR::TextureInstInfo& info, Id& coords, | ||
| 316 | Id offset) { | ||
| 317 | if (!Sirit::ValidId(offset)) { | ||
| 318 | return; | ||
| 319 | } | ||
| 320 | |||
| 321 | Id result_type{}; | ||
| 322 | switch (info.type) { | ||
| 323 | case TextureType::Buffer: | ||
| 324 | case TextureType::Color1D: | ||
| 325 | case TextureType::ColorArray1D: { | ||
| 326 | result_type = ctx.U32[1]; | ||
| 327 | break; | ||
| 328 | } | ||
| 329 | case TextureType::Color2D: | ||
| 330 | case TextureType::Color2DRect: | ||
| 331 | case TextureType::ColorArray2D: { | ||
| 332 | result_type = ctx.U32[2]; | ||
| 333 | break; | ||
| 334 | } | ||
| 335 | case TextureType::Color3D: { | ||
| 336 | result_type = ctx.U32[3]; | ||
| 337 | break; | ||
| 338 | } | ||
| 339 | case TextureType::ColorCube: | ||
| 340 | case TextureType::ColorArrayCube: | ||
| 341 | return; | ||
| 342 | } | ||
| 343 | coords = ctx.OpIAdd(result_type, coords, offset); | ||
| 344 | } | ||
| 345 | } // Anonymous namespace | 315 | } // Anonymous namespace |
| 346 | 316 | ||
| 347 | Id EmitBindlessImageSampleImplicitLod(EmitContext&) { | 317 | Id EmitBindlessImageSampleImplicitLod(EmitContext&) { |
| @@ -524,10 +494,9 @@ Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, | |||
| 524 | operands.Span()); | 494 | operands.Span()); |
| 525 | } | 495 | } |
| 526 | 496 | ||
| 527 | Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset, | 497 | Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, |
| 528 | Id lod, Id ms) { | 498 | const IR::Value& offset, Id lod, Id ms) { |
| 529 | const auto info{inst->Flags<IR::TextureInstInfo>()}; | 499 | const auto info{inst->Flags<IR::TextureInstInfo>()}; |
| 530 | AddOffsetToCoordinates(ctx, info, coords, offset); | ||
| 531 | if (info.type == TextureType::Buffer) { | 500 | if (info.type == TextureType::Buffer) { |
| 532 | lod = Id{}; | 501 | lod = Id{}; |
| 533 | } | 502 | } |
| @@ -535,7 +504,7 @@ Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id c | |||
| 535 | // This image is multisampled, lod must be implicit | 504 | // This image is multisampled, lod must be implicit |
| 536 | lod = Id{}; | 505 | lod = Id{}; |
| 537 | } | 506 | } |
| 538 | const ImageOperands operands(lod, ms); | 507 | const ImageOperands operands(ctx, offset, lod, ms); |
| 539 | return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4], | 508 | return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4], |
| 540 | TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span()); | 509 | TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span()); |
| 541 | } | 510 | } |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h index 5c01b1012..08fcabd58 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h | |||
| @@ -537,8 +537,8 @@ Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id | |||
| 537 | const IR::Value& offset, const IR::Value& offset2); | 537 | const IR::Value& offset, const IR::Value& offset2); |
| 538 | Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, | 538 | Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, |
| 539 | const IR::Value& offset, const IR::Value& offset2, Id dref); | 539 | const IR::Value& offset, const IR::Value& offset2, Id dref); |
| 540 | Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset, | 540 | Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, |
| 541 | Id lod, Id ms); | 541 | const IR::Value& offset, Id lod, Id ms); |
| 542 | Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod, | 542 | Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod, |
| 543 | const IR::Value& skip_mips); | 543 | const IR::Value& skip_mips); |
| 544 | Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords); | 544 | Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords); |
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 16c905db9..2de2beb6e 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt | |||
| @@ -18,6 +18,7 @@ add_library(video_core STATIC | |||
| 18 | buffer_cache/usage_tracker.h | 18 | buffer_cache/usage_tracker.h |
| 19 | buffer_cache/word_manager.h | 19 | buffer_cache/word_manager.h |
| 20 | cache_types.h | 20 | cache_types.h |
| 21 | capture.h | ||
| 21 | cdma_pusher.cpp | 22 | cdma_pusher.cpp |
| 22 | cdma_pusher.h | 23 | cdma_pusher.h |
| 23 | compatible_formats.cpp | 24 | compatible_formats.cpp |
| @@ -101,6 +102,7 @@ add_library(video_core STATIC | |||
| 101 | memory_manager.cpp | 102 | memory_manager.cpp |
| 102 | memory_manager.h | 103 | memory_manager.h |
| 103 | precompiled_headers.h | 104 | precompiled_headers.h |
| 105 | present.h | ||
| 104 | pte_kind.h | 106 | pte_kind.h |
| 105 | query_cache/bank_base.h | 107 | query_cache/bank_base.h |
| 106 | query_cache/query_base.h | 108 | query_cache/query_base.h |
| @@ -274,7 +276,6 @@ add_library(video_core STATIC | |||
| 274 | texture_cache/image_view_info.h | 276 | texture_cache/image_view_info.h |
| 275 | texture_cache/render_targets.h | 277 | texture_cache/render_targets.h |
| 276 | texture_cache/samples_helper.h | 278 | texture_cache/samples_helper.h |
| 277 | texture_cache/slot_vector.h | ||
| 278 | texture_cache/texture_cache.cpp | 279 | texture_cache/texture_cache.cpp |
| 279 | texture_cache/texture_cache.h | 280 | texture_cache/texture_cache.h |
| 280 | texture_cache/texture_cache_base.h | 281 | texture_cache/texture_cache_base.h |
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index b4bf369d1..6d3d933c5 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | #include <numeric> | 8 | #include <numeric> |
| 9 | 9 | ||
| 10 | #include "common/range_sets.inc" | ||
| 10 | #include "video_core/buffer_cache/buffer_cache_base.h" | 11 | #include "video_core/buffer_cache/buffer_cache_base.h" |
| 11 | #include "video_core/guest_memory.h" | 12 | #include "video_core/guest_memory.h" |
| 12 | #include "video_core/host1x/gpu_device_memory_manager.h" | 13 | #include "video_core/host1x/gpu_device_memory_manager.h" |
| @@ -20,7 +21,7 @@ BufferCache<P>::BufferCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, R | |||
| 20 | : runtime{runtime_}, device_memory{device_memory_}, memory_tracker{device_memory} { | 21 | : runtime{runtime_}, device_memory{device_memory_}, memory_tracker{device_memory} { |
| 21 | // Ensure the first slot is used for the null buffer | 22 | // Ensure the first slot is used for the null buffer |
| 22 | void(slot_buffers.insert(runtime, NullBufferParams{})); | 23 | void(slot_buffers.insert(runtime, NullBufferParams{})); |
| 23 | common_ranges.clear(); | 24 | gpu_modified_ranges.Clear(); |
| 24 | inline_buffer_id = NULL_BUFFER_ID; | 25 | inline_buffer_id = NULL_BUFFER_ID; |
| 25 | 26 | ||
| 26 | if (!runtime.CanReportMemoryUsage()) { | 27 | if (!runtime.CanReportMemoryUsage()) { |
| @@ -44,6 +45,9 @@ BufferCache<P>::BufferCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, R | |||
| 44 | } | 45 | } |
| 45 | 46 | ||
| 46 | template <class P> | 47 | template <class P> |
| 48 | BufferCache<P>::~BufferCache() = default; | ||
| 49 | |||
| 50 | template <class P> | ||
| 47 | void BufferCache<P>::RunGarbageCollector() { | 51 | void BufferCache<P>::RunGarbageCollector() { |
| 48 | const bool aggressive_gc = total_used_memory >= critical_memory; | 52 | const bool aggressive_gc = total_used_memory >= critical_memory; |
| 49 | const u64 ticks_to_destroy = aggressive_gc ? 60 : 120; | 53 | const u64 ticks_to_destroy = aggressive_gc ? 60 : 120; |
| @@ -96,20 +100,17 @@ void BufferCache<P>::TickFrame() { | |||
| 96 | ++frame_tick; | 100 | ++frame_tick; |
| 97 | delayed_destruction_ring.Tick(); | 101 | delayed_destruction_ring.Tick(); |
| 98 | 102 | ||
| 99 | if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) { | 103 | for (auto& buffer : async_buffers_death_ring) { |
| 100 | for (auto& buffer : async_buffers_death_ring) { | 104 | runtime.FreeDeferredStagingBuffer(buffer); |
| 101 | runtime.FreeDeferredStagingBuffer(buffer); | ||
| 102 | } | ||
| 103 | async_buffers_death_ring.clear(); | ||
| 104 | } | 105 | } |
| 106 | async_buffers_death_ring.clear(); | ||
| 105 | } | 107 | } |
| 106 | 108 | ||
| 107 | template <class P> | 109 | template <class P> |
| 108 | void BufferCache<P>::WriteMemory(DAddr device_addr, u64 size) { | 110 | void BufferCache<P>::WriteMemory(DAddr device_addr, u64 size) { |
| 109 | if (memory_tracker.IsRegionGpuModified(device_addr, size)) { | 111 | if (memory_tracker.IsRegionGpuModified(device_addr, size)) { |
| 110 | const IntervalType subtract_interval{device_addr, device_addr + size}; | 112 | ClearDownload(device_addr, size); |
| 111 | ClearDownload(subtract_interval); | 113 | gpu_modified_ranges.Subtract(device_addr, size); |
| 112 | common_ranges.subtract(subtract_interval); | ||
| 113 | } | 114 | } |
| 114 | memory_tracker.MarkRegionAsCpuModified(device_addr, size); | 115 | memory_tracker.MarkRegionAsCpuModified(device_addr, size); |
| 115 | } | 116 | } |
| @@ -174,11 +175,11 @@ void BufferCache<P>::DownloadMemory(DAddr device_addr, u64 size) { | |||
| 174 | } | 175 | } |
| 175 | 176 | ||
| 176 | template <class P> | 177 | template <class P> |
| 177 | void BufferCache<P>::ClearDownload(IntervalType subtract_interval) { | 178 | void BufferCache<P>::ClearDownload(DAddr device_addr, u64 size) { |
| 178 | RemoveEachInOverlapCounter(async_downloads, subtract_interval, -1024); | 179 | async_downloads.DeleteAll(device_addr, size); |
| 179 | uncommitted_ranges.subtract(subtract_interval); | 180 | uncommitted_gpu_modified_ranges.Subtract(device_addr, size); |
| 180 | for (auto& interval_set : committed_ranges) { | 181 | for (auto& interval_set : committed_gpu_modified_ranges) { |
| 181 | interval_set.subtract(subtract_interval); | 182 | interval_set.Subtract(device_addr, size); |
| 182 | } | 183 | } |
| 183 | } | 184 | } |
| 184 | 185 | ||
| @@ -195,8 +196,7 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am | |||
| 195 | return false; | 196 | return false; |
| 196 | } | 197 | } |
| 197 | 198 | ||
| 198 | const IntervalType subtract_interval{*cpu_dest_address, *cpu_dest_address + amount}; | 199 | ClearDownload(*cpu_dest_address, amount); |
| 199 | ClearDownload(subtract_interval); | ||
| 200 | 200 | ||
| 201 | BufferId buffer_a; | 201 | BufferId buffer_a; |
| 202 | BufferId buffer_b; | 202 | BufferId buffer_b; |
| @@ -215,21 +215,20 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am | |||
| 215 | .size = amount, | 215 | .size = amount, |
| 216 | }}; | 216 | }}; |
| 217 | 217 | ||
| 218 | boost::container::small_vector<IntervalType, 4> tmp_intervals; | 218 | boost::container::small_vector<std::pair<DAddr, size_t>, 4> tmp_intervals; |
| 219 | auto mirror = [&](DAddr base_address, DAddr base_address_end) { | 219 | auto mirror = [&](DAddr base_address, DAddr base_address_end) { |
| 220 | const u64 size = base_address_end - base_address; | 220 | const u64 size = base_address_end - base_address; |
| 221 | const DAddr diff = base_address - *cpu_src_address; | 221 | const DAddr diff = base_address - *cpu_src_address; |
| 222 | const DAddr new_base_address = *cpu_dest_address + diff; | 222 | const DAddr new_base_address = *cpu_dest_address + diff; |
| 223 | const IntervalType add_interval{new_base_address, new_base_address + size}; | 223 | tmp_intervals.push_back({new_base_address, size}); |
| 224 | tmp_intervals.push_back(add_interval); | 224 | uncommitted_gpu_modified_ranges.Add(new_base_address, size); |
| 225 | uncommitted_ranges.add(add_interval); | ||
| 226 | }; | 225 | }; |
| 227 | ForEachInRangeSet(common_ranges, *cpu_src_address, amount, mirror); | 226 | gpu_modified_ranges.ForEachInRange(*cpu_src_address, amount, mirror); |
| 228 | // This subtraction in this order is important for overlapping copies. | 227 | // This subtraction in this order is important for overlapping copies. |
| 229 | common_ranges.subtract(subtract_interval); | 228 | gpu_modified_ranges.Subtract(*cpu_dest_address, amount); |
| 230 | const bool has_new_downloads = tmp_intervals.size() != 0; | 229 | const bool has_new_downloads = tmp_intervals.size() != 0; |
| 231 | for (const IntervalType& add_interval : tmp_intervals) { | 230 | for (const auto& pair : tmp_intervals) { |
| 232 | common_ranges.add(add_interval); | 231 | gpu_modified_ranges.Add(pair.first, pair.second); |
| 233 | } | 232 | } |
| 234 | const auto& copy = copies[0]; | 233 | const auto& copy = copies[0]; |
| 235 | src_buffer.MarkUsage(copy.src_offset, copy.size); | 234 | src_buffer.MarkUsage(copy.src_offset, copy.size); |
| @@ -257,9 +256,8 @@ bool BufferCache<P>::DMAClear(GPUVAddr dst_address, u64 amount, u32 value) { | |||
| 257 | } | 256 | } |
| 258 | 257 | ||
| 259 | const size_t size = amount * sizeof(u32); | 258 | const size_t size = amount * sizeof(u32); |
| 260 | const IntervalType subtract_interval{*cpu_dst_address, *cpu_dst_address + size}; | 259 | ClearDownload(*cpu_dst_address, size); |
| 261 | ClearDownload(subtract_interval); | 260 | gpu_modified_ranges.Subtract(*cpu_dst_address, size); |
| 262 | common_ranges.subtract(subtract_interval); | ||
| 263 | 261 | ||
| 264 | const BufferId buffer = FindBuffer(*cpu_dst_address, static_cast<u32>(size)); | 262 | const BufferId buffer = FindBuffer(*cpu_dst_address, static_cast<u32>(size)); |
| 265 | Buffer& dest_buffer = slot_buffers[buffer]; | 263 | Buffer& dest_buffer = slot_buffers[buffer]; |
| @@ -300,11 +298,11 @@ std::pair<typename P::Buffer*, u32> BufferCache<P>::ObtainCPUBuffer( | |||
| 300 | MarkWrittenBuffer(buffer_id, device_addr, size); | 298 | MarkWrittenBuffer(buffer_id, device_addr, size); |
| 301 | break; | 299 | break; |
| 302 | case ObtainBufferOperation::DiscardWrite: { | 300 | case ObtainBufferOperation::DiscardWrite: { |
| 303 | DAddr device_addr_start = Common::AlignDown(device_addr, 64); | 301 | const DAddr device_addr_start = Common::AlignDown(device_addr, 64); |
| 304 | DAddr device_addr_end = Common::AlignUp(device_addr + size, 64); | 302 | const DAddr device_addr_end = Common::AlignUp(device_addr + size, 64); |
| 305 | IntervalType interval{device_addr_start, device_addr_end}; | 303 | const size_t new_size = device_addr_end - device_addr_start; |
| 306 | ClearDownload(interval); | 304 | ClearDownload(device_addr_start, new_size); |
| 307 | common_ranges.subtract(interval); | 305 | gpu_modified_ranges.Subtract(device_addr_start, new_size); |
| 308 | break; | 306 | break; |
| 309 | } | 307 | } |
| 310 | default: | 308 | default: |
| @@ -504,46 +502,40 @@ void BufferCache<P>::FlushCachedWrites() { | |||
| 504 | 502 | ||
| 505 | template <class P> | 503 | template <class P> |
| 506 | bool BufferCache<P>::HasUncommittedFlushes() const noexcept { | 504 | bool BufferCache<P>::HasUncommittedFlushes() const noexcept { |
| 507 | return !uncommitted_ranges.empty() || !committed_ranges.empty(); | 505 | return !uncommitted_gpu_modified_ranges.Empty() || !committed_gpu_modified_ranges.empty(); |
| 508 | } | 506 | } |
| 509 | 507 | ||
| 510 | template <class P> | 508 | template <class P> |
| 511 | void BufferCache<P>::AccumulateFlushes() { | 509 | void BufferCache<P>::AccumulateFlushes() { |
| 512 | if (uncommitted_ranges.empty()) { | 510 | if (uncommitted_gpu_modified_ranges.Empty()) { |
| 513 | return; | 511 | return; |
| 514 | } | 512 | } |
| 515 | committed_ranges.emplace_back(std::move(uncommitted_ranges)); | 513 | committed_gpu_modified_ranges.emplace_back(std::move(uncommitted_gpu_modified_ranges)); |
| 516 | } | 514 | } |
| 517 | 515 | ||
| 518 | template <class P> | 516 | template <class P> |
| 519 | bool BufferCache<P>::ShouldWaitAsyncFlushes() const noexcept { | 517 | bool BufferCache<P>::ShouldWaitAsyncFlushes() const noexcept { |
| 520 | if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) { | 518 | return (!async_buffers.empty() && async_buffers.front().has_value()); |
| 521 | return (!async_buffers.empty() && async_buffers.front().has_value()); | ||
| 522 | } else { | ||
| 523 | return false; | ||
| 524 | } | ||
| 525 | } | 519 | } |
| 526 | 520 | ||
| 527 | template <class P> | 521 | template <class P> |
| 528 | void BufferCache<P>::CommitAsyncFlushesHigh() { | 522 | void BufferCache<P>::CommitAsyncFlushesHigh() { |
| 529 | AccumulateFlushes(); | 523 | AccumulateFlushes(); |
| 530 | 524 | ||
| 531 | if (committed_ranges.empty()) { | 525 | if (committed_gpu_modified_ranges.empty()) { |
| 532 | if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) { | 526 | async_buffers.emplace_back(std::optional<Async_Buffer>{}); |
| 533 | async_buffers.emplace_back(std::optional<Async_Buffer>{}); | ||
| 534 | } | ||
| 535 | return; | 527 | return; |
| 536 | } | 528 | } |
| 537 | MICROPROFILE_SCOPE(GPU_DownloadMemory); | 529 | MICROPROFILE_SCOPE(GPU_DownloadMemory); |
| 538 | 530 | ||
| 539 | auto it = committed_ranges.begin(); | 531 | auto it = committed_gpu_modified_ranges.begin(); |
| 540 | while (it != committed_ranges.end()) { | 532 | while (it != committed_gpu_modified_ranges.end()) { |
| 541 | auto& current_intervals = *it; | 533 | auto& current_intervals = *it; |
| 542 | auto next_it = std::next(it); | 534 | auto next_it = std::next(it); |
| 543 | while (next_it != committed_ranges.end()) { | 535 | while (next_it != committed_gpu_modified_ranges.end()) { |
| 544 | for (auto& interval : *next_it) { | 536 | next_it->ForEach([¤t_intervals](DAddr start, DAddr end) { |
| 545 | current_intervals.subtract(interval); | 537 | current_intervals.Subtract(start, end - start); |
| 546 | } | 538 | }); |
| 547 | next_it++; | 539 | next_it++; |
| 548 | } | 540 | } |
| 549 | it++; | 541 | it++; |
| @@ -552,10 +544,10 @@ void BufferCache<P>::CommitAsyncFlushesHigh() { | |||
| 552 | boost::container::small_vector<std::pair<BufferCopy, BufferId>, 16> downloads; | 544 | boost::container::small_vector<std::pair<BufferCopy, BufferId>, 16> downloads; |
| 553 | u64 total_size_bytes = 0; | 545 | u64 total_size_bytes = 0; |
| 554 | u64 largest_copy = 0; | 546 | u64 largest_copy = 0; |
| 555 | for (const IntervalSet& intervals : committed_ranges) { | 547 | for (const Common::RangeSet<DAddr>& range_set : committed_gpu_modified_ranges) { |
| 556 | for (auto& interval : intervals) { | 548 | range_set.ForEach([&](DAddr interval_lower, DAddr interval_upper) { |
| 557 | const std::size_t size = interval.upper() - interval.lower(); | 549 | const std::size_t size = interval_upper - interval_lower; |
| 558 | const DAddr device_addr = interval.lower(); | 550 | const DAddr device_addr = interval_lower; |
| 559 | ForEachBufferInRange(device_addr, size, [&](BufferId buffer_id, Buffer& buffer) { | 551 | ForEachBufferInRange(device_addr, size, [&](BufferId buffer_id, Buffer& buffer) { |
| 560 | const DAddr buffer_start = buffer.CpuAddr(); | 552 | const DAddr buffer_start = buffer.CpuAddr(); |
| 561 | const DAddr buffer_end = buffer_start + buffer.SizeBytes(); | 553 | const DAddr buffer_end = buffer_start + buffer.SizeBytes(); |
| @@ -583,77 +575,35 @@ void BufferCache<P>::CommitAsyncFlushesHigh() { | |||
| 583 | largest_copy = std::max(largest_copy, new_size); | 575 | largest_copy = std::max(largest_copy, new_size); |
| 584 | }; | 576 | }; |
| 585 | 577 | ||
| 586 | ForEachInRangeSet(common_ranges, device_addr_out, range_size, add_download); | 578 | gpu_modified_ranges.ForEachInRange(device_addr_out, range_size, |
| 579 | add_download); | ||
| 587 | }); | 580 | }); |
| 588 | }); | 581 | }); |
| 589 | } | 582 | }); |
| 590 | } | 583 | } |
| 591 | committed_ranges.clear(); | 584 | committed_gpu_modified_ranges.clear(); |
| 592 | if (downloads.empty()) { | 585 | if (downloads.empty()) { |
| 593 | if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) { | 586 | async_buffers.emplace_back(std::optional<Async_Buffer>{}); |
| 594 | async_buffers.emplace_back(std::optional<Async_Buffer>{}); | ||
| 595 | } | ||
| 596 | return; | 587 | return; |
| 597 | } | 588 | } |
| 598 | if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) { | 589 | auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes, true); |
| 599 | auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes, true); | 590 | boost::container::small_vector<BufferCopy, 4> normalized_copies; |
| 600 | boost::container::small_vector<BufferCopy, 4> normalized_copies; | 591 | runtime.PreCopyBarrier(); |
| 601 | IntervalSet new_async_range{}; | 592 | for (auto& [copy, buffer_id] : downloads) { |
| 602 | runtime.PreCopyBarrier(); | 593 | copy.dst_offset += download_staging.offset; |
| 603 | for (auto& [copy, buffer_id] : downloads) { | 594 | const std::array copies{copy}; |
| 604 | copy.dst_offset += download_staging.offset; | 595 | BufferCopy second_copy{copy}; |
| 605 | const std::array copies{copy}; | 596 | Buffer& buffer = slot_buffers[buffer_id]; |
| 606 | BufferCopy second_copy{copy}; | 597 | second_copy.src_offset = static_cast<size_t>(buffer.CpuAddr()) + copy.src_offset; |
| 607 | Buffer& buffer = slot_buffers[buffer_id]; | 598 | const DAddr orig_device_addr = static_cast<DAddr>(second_copy.src_offset); |
| 608 | second_copy.src_offset = static_cast<size_t>(buffer.CpuAddr()) + copy.src_offset; | 599 | async_downloads.Add(orig_device_addr, copy.size); |
| 609 | DAddr orig_device_addr = static_cast<DAddr>(second_copy.src_offset); | 600 | buffer.MarkUsage(copy.src_offset, copy.size); |
| 610 | const IntervalType base_interval{orig_device_addr, orig_device_addr + copy.size}; | 601 | runtime.CopyBuffer(download_staging.buffer, buffer, copies, false); |
| 611 | async_downloads += std::make_pair(base_interval, 1); | 602 | normalized_copies.push_back(second_copy); |
| 612 | buffer.MarkUsage(copy.src_offset, copy.size); | ||
| 613 | runtime.CopyBuffer(download_staging.buffer, buffer, copies, false); | ||
| 614 | normalized_copies.push_back(second_copy); | ||
| 615 | } | ||
| 616 | runtime.PostCopyBarrier(); | ||
| 617 | pending_downloads.emplace_back(std::move(normalized_copies)); | ||
| 618 | async_buffers.emplace_back(download_staging); | ||
| 619 | } else { | ||
| 620 | if (!Settings::IsGPULevelHigh()) { | ||
| 621 | committed_ranges.clear(); | ||
| 622 | uncommitted_ranges.clear(); | ||
| 623 | } else { | ||
| 624 | if constexpr (USE_MEMORY_MAPS) { | ||
| 625 | auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes); | ||
| 626 | runtime.PreCopyBarrier(); | ||
| 627 | for (auto& [copy, buffer_id] : downloads) { | ||
| 628 | // Have in mind the staging buffer offset for the copy | ||
| 629 | copy.dst_offset += download_staging.offset; | ||
| 630 | const std::array copies{copy}; | ||
| 631 | Buffer& buffer = slot_buffers[buffer_id]; | ||
| 632 | buffer.MarkUsage(copy.src_offset, copy.size); | ||
| 633 | runtime.CopyBuffer(download_staging.buffer, buffer, copies, false); | ||
| 634 | } | ||
| 635 | runtime.PostCopyBarrier(); | ||
| 636 | runtime.Finish(); | ||
| 637 | for (const auto& [copy, buffer_id] : downloads) { | ||
| 638 | const Buffer& buffer = slot_buffers[buffer_id]; | ||
| 639 | const DAddr device_addr = buffer.CpuAddr() + copy.src_offset; | ||
| 640 | // Undo the modified offset | ||
| 641 | const u64 dst_offset = copy.dst_offset - download_staging.offset; | ||
| 642 | const u8* read_mapped_memory = download_staging.mapped_span.data() + dst_offset; | ||
| 643 | device_memory.WriteBlockUnsafe(device_addr, read_mapped_memory, copy.size); | ||
| 644 | } | ||
| 645 | } else { | ||
| 646 | const std::span<u8> immediate_buffer = ImmediateBuffer(largest_copy); | ||
| 647 | for (const auto& [copy, buffer_id] : downloads) { | ||
| 648 | Buffer& buffer = slot_buffers[buffer_id]; | ||
| 649 | buffer.ImmediateDownload(copy.src_offset, | ||
| 650 | immediate_buffer.subspan(0, copy.size)); | ||
| 651 | const DAddr device_addr = buffer.CpuAddr() + copy.src_offset; | ||
| 652 | device_memory.WriteBlockUnsafe(device_addr, immediate_buffer.data(), copy.size); | ||
| 653 | } | ||
| 654 | } | ||
| 655 | } | ||
| 656 | } | 603 | } |
| 604 | runtime.PostCopyBarrier(); | ||
| 605 | pending_downloads.emplace_back(std::move(normalized_copies)); | ||
| 606 | async_buffers.emplace_back(download_staging); | ||
| 657 | } | 607 | } |
| 658 | 608 | ||
| 659 | template <class P> | 609 | template <class P> |
| @@ -676,37 +626,31 @@ void BufferCache<P>::PopAsyncBuffers() { | |||
| 676 | async_buffers.pop_front(); | 626 | async_buffers.pop_front(); |
| 677 | return; | 627 | return; |
| 678 | } | 628 | } |
| 679 | if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) { | 629 | auto& downloads = pending_downloads.front(); |
| 680 | auto& downloads = pending_downloads.front(); | 630 | auto& async_buffer = async_buffers.front(); |
| 681 | auto& async_buffer = async_buffers.front(); | 631 | u8* base = async_buffer->mapped_span.data(); |
| 682 | u8* base = async_buffer->mapped_span.data(); | 632 | const size_t base_offset = async_buffer->offset; |
| 683 | const size_t base_offset = async_buffer->offset; | 633 | for (const auto& copy : downloads) { |
| 684 | for (const auto& copy : downloads) { | 634 | const DAddr device_addr = static_cast<DAddr>(copy.src_offset); |
| 685 | const DAddr device_addr = static_cast<DAddr>(copy.src_offset); | 635 | const u64 dst_offset = copy.dst_offset - base_offset; |
| 686 | const u64 dst_offset = copy.dst_offset - base_offset; | 636 | const u8* read_mapped_memory = base + dst_offset; |
| 687 | const u8* read_mapped_memory = base + dst_offset; | 637 | async_downloads.ForEachInRange(device_addr, copy.size, [&](DAddr start, DAddr end, s32) { |
| 688 | ForEachInOverlapCounter( | 638 | device_memory.WriteBlockUnsafe(start, &read_mapped_memory[start - device_addr], |
| 689 | async_downloads, device_addr, copy.size, [&](DAddr start, DAddr end, int count) { | 639 | end - start); |
| 690 | device_memory.WriteBlockUnsafe(start, &read_mapped_memory[start - device_addr], | 640 | }); |
| 691 | end - start); | 641 | async_downloads.Subtract(device_addr, copy.size, [&](DAddr start, DAddr end) { |
| 692 | if (count == 1) { | 642 | gpu_modified_ranges.Subtract(start, end - start); |
| 693 | const IntervalType base_interval{start, end}; | 643 | }); |
| 694 | common_ranges.subtract(base_interval); | ||
| 695 | } | ||
| 696 | }); | ||
| 697 | const IntervalType subtract_interval{device_addr, device_addr + copy.size}; | ||
| 698 | RemoveEachInOverlapCounter(async_downloads, subtract_interval, -1); | ||
| 699 | } | ||
| 700 | async_buffers_death_ring.emplace_back(*async_buffer); | ||
| 701 | async_buffers.pop_front(); | ||
| 702 | pending_downloads.pop_front(); | ||
| 703 | } | 644 | } |
| 645 | async_buffers_death_ring.emplace_back(*async_buffer); | ||
| 646 | async_buffers.pop_front(); | ||
| 647 | pending_downloads.pop_front(); | ||
| 704 | } | 648 | } |
| 705 | 649 | ||
| 706 | template <class P> | 650 | template <class P> |
| 707 | bool BufferCache<P>::IsRegionGpuModified(DAddr addr, size_t size) { | 651 | bool BufferCache<P>::IsRegionGpuModified(DAddr addr, size_t size) { |
| 708 | bool is_dirty = false; | 652 | bool is_dirty = false; |
| 709 | ForEachInRangeSet(common_ranges, addr, size, [&](DAddr, DAddr) { is_dirty = true; }); | 653 | gpu_modified_ranges.ForEachInRange(addr, size, [&](DAddr, DAddr) { is_dirty = true; }); |
| 710 | return is_dirty; | 654 | return is_dirty; |
| 711 | } | 655 | } |
| 712 | 656 | ||
| @@ -1320,10 +1264,8 @@ void BufferCache<P>::UpdateComputeTextureBuffers() { | |||
| 1320 | template <class P> | 1264 | template <class P> |
| 1321 | void BufferCache<P>::MarkWrittenBuffer(BufferId buffer_id, DAddr device_addr, u32 size) { | 1265 | void BufferCache<P>::MarkWrittenBuffer(BufferId buffer_id, DAddr device_addr, u32 size) { |
| 1322 | memory_tracker.MarkRegionAsGpuModified(device_addr, size); | 1266 | memory_tracker.MarkRegionAsGpuModified(device_addr, size); |
| 1323 | 1267 | gpu_modified_ranges.Add(device_addr, size); | |
| 1324 | const IntervalType base_interval{device_addr, device_addr + size}; | 1268 | uncommitted_gpu_modified_ranges.Add(device_addr, size); |
| 1325 | common_ranges.add(base_interval); | ||
| 1326 | uncommitted_ranges.add(base_interval); | ||
| 1327 | } | 1269 | } |
| 1328 | 1270 | ||
| 1329 | template <class P> | 1271 | template <class P> |
| @@ -1600,9 +1542,8 @@ bool BufferCache<P>::InlineMemory(DAddr dest_address, size_t copy_size, | |||
| 1600 | template <class P> | 1542 | template <class P> |
| 1601 | void BufferCache<P>::InlineMemoryImplementation(DAddr dest_address, size_t copy_size, | 1543 | void BufferCache<P>::InlineMemoryImplementation(DAddr dest_address, size_t copy_size, |
| 1602 | std::span<const u8> inlined_buffer) { | 1544 | std::span<const u8> inlined_buffer) { |
| 1603 | const IntervalType subtract_interval{dest_address, dest_address + copy_size}; | 1545 | ClearDownload(dest_address, copy_size); |
| 1604 | ClearDownload(subtract_interval); | 1546 | gpu_modified_ranges.Subtract(dest_address, copy_size); |
| 1605 | common_ranges.subtract(subtract_interval); | ||
| 1606 | 1547 | ||
| 1607 | BufferId buffer_id = FindBuffer(dest_address, static_cast<u32>(copy_size)); | 1548 | BufferId buffer_id = FindBuffer(dest_address, static_cast<u32>(copy_size)); |
| 1608 | auto& buffer = slot_buffers[buffer_id]; | 1549 | auto& buffer = slot_buffers[buffer_id]; |
| @@ -1652,12 +1593,9 @@ void BufferCache<P>::DownloadBufferMemory(Buffer& buffer, DAddr device_addr, u64 | |||
| 1652 | largest_copy = std::max(largest_copy, new_size); | 1593 | largest_copy = std::max(largest_copy, new_size); |
| 1653 | }; | 1594 | }; |
| 1654 | 1595 | ||
| 1655 | const DAddr start_address = device_addr_out; | 1596 | gpu_modified_ranges.ForEachInRange(device_addr_out, range_size, add_download); |
| 1656 | const DAddr end_address = start_address + range_size; | 1597 | ClearDownload(device_addr_out, range_size); |
| 1657 | ForEachInRangeSet(common_ranges, start_address, range_size, add_download); | 1598 | gpu_modified_ranges.Subtract(device_addr_out, range_size); |
| 1658 | const IntervalType subtract_interval{start_address, end_address}; | ||
| 1659 | ClearDownload(subtract_interval); | ||
| 1660 | common_ranges.subtract(subtract_interval); | ||
| 1661 | }); | 1599 | }); |
| 1662 | if (total_size_bytes == 0) { | 1600 | if (total_size_bytes == 0) { |
| 1663 | return; | 1601 | return; |
diff --git a/src/video_core/buffer_cache/buffer_cache_base.h b/src/video_core/buffer_cache/buffer_cache_base.h index 80dbb81e7..240e9f015 100644 --- a/src/video_core/buffer_cache/buffer_cache_base.h +++ b/src/video_core/buffer_cache/buffer_cache_base.h | |||
| @@ -13,25 +13,15 @@ | |||
| 13 | #include <unordered_map> | 13 | #include <unordered_map> |
| 14 | #include <vector> | 14 | #include <vector> |
| 15 | 15 | ||
| 16 | #include <boost/container/small_vector.hpp> | ||
| 17 | #define BOOST_NO_MT | ||
| 18 | #include <boost/pool/detail/mutex.hpp> | ||
| 19 | #undef BOOST_NO_MT | ||
| 20 | #include <boost/icl/interval.hpp> | ||
| 21 | #include <boost/icl/interval_base_set.hpp> | ||
| 22 | #include <boost/icl/interval_set.hpp> | ||
| 23 | #include <boost/icl/split_interval_map.hpp> | ||
| 24 | #include <boost/pool/pool.hpp> | ||
| 25 | #include <boost/pool/pool_alloc.hpp> | ||
| 26 | #include <boost/pool/poolfwd.hpp> | ||
| 27 | |||
| 28 | #include "common/common_types.h" | 16 | #include "common/common_types.h" |
| 29 | #include "common/div_ceil.h" | 17 | #include "common/div_ceil.h" |
| 30 | #include "common/literals.h" | 18 | #include "common/literals.h" |
| 31 | #include "common/lru_cache.h" | 19 | #include "common/lru_cache.h" |
| 32 | #include "common/microprofile.h" | 20 | #include "common/microprofile.h" |
| 21 | #include "common/range_sets.h" | ||
| 33 | #include "common/scope_exit.h" | 22 | #include "common/scope_exit.h" |
| 34 | #include "common/settings.h" | 23 | #include "common/settings.h" |
| 24 | #include "common/slot_vector.h" | ||
| 35 | #include "video_core/buffer_cache/buffer_base.h" | 25 | #include "video_core/buffer_cache/buffer_base.h" |
| 36 | #include "video_core/control/channel_state_cache.h" | 26 | #include "video_core/control/channel_state_cache.h" |
| 37 | #include "video_core/delayed_destruction_ring.h" | 27 | #include "video_core/delayed_destruction_ring.h" |
| @@ -41,21 +31,15 @@ | |||
| 41 | #include "video_core/engines/maxwell_3d.h" | 31 | #include "video_core/engines/maxwell_3d.h" |
| 42 | #include "video_core/memory_manager.h" | 32 | #include "video_core/memory_manager.h" |
| 43 | #include "video_core/surface.h" | 33 | #include "video_core/surface.h" |
| 44 | #include "video_core/texture_cache/slot_vector.h" | ||
| 45 | #include "video_core/texture_cache/types.h" | 34 | #include "video_core/texture_cache/types.h" |
| 46 | 35 | ||
| 47 | namespace boost { | ||
| 48 | template <typename T> | ||
| 49 | class fast_pool_allocator<T, default_user_allocator_new_delete, details::pool::null_mutex, 4096, 0>; | ||
| 50 | } | ||
| 51 | |||
| 52 | namespace VideoCommon { | 36 | namespace VideoCommon { |
| 53 | 37 | ||
| 54 | MICROPROFILE_DECLARE(GPU_PrepareBuffers); | 38 | MICROPROFILE_DECLARE(GPU_PrepareBuffers); |
| 55 | MICROPROFILE_DECLARE(GPU_BindUploadBuffers); | 39 | MICROPROFILE_DECLARE(GPU_BindUploadBuffers); |
| 56 | MICROPROFILE_DECLARE(GPU_DownloadMemory); | 40 | MICROPROFILE_DECLARE(GPU_DownloadMemory); |
| 57 | 41 | ||
| 58 | using BufferId = SlotId; | 42 | using BufferId = Common::SlotId; |
| 59 | 43 | ||
| 60 | using VideoCore::Surface::PixelFormat; | 44 | using VideoCore::Surface::PixelFormat; |
| 61 | using namespace Common::Literals; | 45 | using namespace Common::Literals; |
| @@ -184,7 +168,6 @@ class BufferCache : public VideoCommon::ChannelSetupCaches<BufferCacheChannelInf | |||
| 184 | static constexpr bool NEEDS_BIND_STORAGE_INDEX = P::NEEDS_BIND_STORAGE_INDEX; | 168 | static constexpr bool NEEDS_BIND_STORAGE_INDEX = P::NEEDS_BIND_STORAGE_INDEX; |
| 185 | static constexpr bool USE_MEMORY_MAPS = P::USE_MEMORY_MAPS; | 169 | static constexpr bool USE_MEMORY_MAPS = P::USE_MEMORY_MAPS; |
| 186 | static constexpr bool SEPARATE_IMAGE_BUFFERS_BINDINGS = P::SEPARATE_IMAGE_BUFFER_BINDINGS; | 170 | static constexpr bool SEPARATE_IMAGE_BUFFERS_BINDINGS = P::SEPARATE_IMAGE_BUFFER_BINDINGS; |
| 187 | static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = P::IMPLEMENTS_ASYNC_DOWNLOADS; | ||
| 188 | static constexpr bool USE_MEMORY_MAPS_FOR_UPLOADS = P::USE_MEMORY_MAPS_FOR_UPLOADS; | 171 | static constexpr bool USE_MEMORY_MAPS_FOR_UPLOADS = P::USE_MEMORY_MAPS_FOR_UPLOADS; |
| 189 | 172 | ||
| 190 | static constexpr s64 DEFAULT_EXPECTED_MEMORY = 512_MiB; | 173 | static constexpr s64 DEFAULT_EXPECTED_MEMORY = 512_MiB; |
| @@ -202,34 +185,6 @@ class BufferCache : public VideoCommon::ChannelSetupCaches<BufferCacheChannelInf | |||
| 202 | using Async_Buffer = typename P::Async_Buffer; | 185 | using Async_Buffer = typename P::Async_Buffer; |
| 203 | using MemoryTracker = typename P::MemoryTracker; | 186 | using MemoryTracker = typename P::MemoryTracker; |
| 204 | 187 | ||
| 205 | using IntervalCompare = std::less<DAddr>; | ||
| 206 | using IntervalInstance = boost::icl::interval_type_default<DAddr, std::less>; | ||
| 207 | using IntervalAllocator = boost::fast_pool_allocator<DAddr>; | ||
| 208 | using IntervalSet = boost::icl::interval_set<DAddr>; | ||
| 209 | using IntervalType = typename IntervalSet::interval_type; | ||
| 210 | |||
| 211 | template <typename Type> | ||
| 212 | struct counter_add_functor : public boost::icl::identity_based_inplace_combine<Type> { | ||
| 213 | // types | ||
| 214 | typedef counter_add_functor<Type> type; | ||
| 215 | typedef boost::icl::identity_based_inplace_combine<Type> base_type; | ||
| 216 | |||
| 217 | // public member functions | ||
| 218 | void operator()(Type& current, const Type& added) const { | ||
| 219 | current += added; | ||
| 220 | if (current < base_type::identity_element()) { | ||
| 221 | current = base_type::identity_element(); | ||
| 222 | } | ||
| 223 | } | ||
| 224 | |||
| 225 | // public static functions | ||
| 226 | static void version(Type&){}; | ||
| 227 | }; | ||
| 228 | |||
| 229 | using OverlapCombine = counter_add_functor<int>; | ||
| 230 | using OverlapSection = boost::icl::inter_section<int>; | ||
| 231 | using OverlapCounter = boost::icl::split_interval_map<DAddr, int>; | ||
| 232 | |||
| 233 | struct OverlapResult { | 188 | struct OverlapResult { |
| 234 | boost::container::small_vector<BufferId, 16> ids; | 189 | boost::container::small_vector<BufferId, 16> ids; |
| 235 | DAddr begin; | 190 | DAddr begin; |
| @@ -240,6 +195,8 @@ class BufferCache : public VideoCommon::ChannelSetupCaches<BufferCacheChannelInf | |||
| 240 | public: | 195 | public: |
| 241 | explicit BufferCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, Runtime& runtime_); | 196 | explicit BufferCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, Runtime& runtime_); |
| 242 | 197 | ||
| 198 | ~BufferCache(); | ||
| 199 | |||
| 243 | void TickFrame(); | 200 | void TickFrame(); |
| 244 | 201 | ||
| 245 | void WriteMemory(DAddr device_addr, u64 size); | 202 | void WriteMemory(DAddr device_addr, u64 size); |
| @@ -379,75 +336,6 @@ private: | |||
| 379 | } | 336 | } |
| 380 | } | 337 | } |
| 381 | 338 | ||
| 382 | template <typename Func> | ||
| 383 | void ForEachInRangeSet(IntervalSet& current_range, DAddr device_addr, u64 size, Func&& func) { | ||
| 384 | const DAddr start_address = device_addr; | ||
| 385 | const DAddr end_address = start_address + size; | ||
| 386 | const IntervalType search_interval{start_address, end_address}; | ||
| 387 | auto it = current_range.lower_bound(search_interval); | ||
| 388 | if (it == current_range.end()) { | ||
| 389 | return; | ||
| 390 | } | ||
| 391 | auto end_it = current_range.upper_bound(search_interval); | ||
| 392 | for (; it != end_it; it++) { | ||
| 393 | DAddr inter_addr_end = it->upper(); | ||
| 394 | DAddr inter_addr = it->lower(); | ||
| 395 | if (inter_addr_end > end_address) { | ||
| 396 | inter_addr_end = end_address; | ||
| 397 | } | ||
| 398 | if (inter_addr < start_address) { | ||
| 399 | inter_addr = start_address; | ||
| 400 | } | ||
| 401 | func(inter_addr, inter_addr_end); | ||
| 402 | } | ||
| 403 | } | ||
| 404 | |||
| 405 | template <typename Func> | ||
| 406 | void ForEachInOverlapCounter(OverlapCounter& current_range, DAddr device_addr, u64 size, | ||
| 407 | Func&& func) { | ||
| 408 | const DAddr start_address = device_addr; | ||
| 409 | const DAddr end_address = start_address + size; | ||
| 410 | const IntervalType search_interval{start_address, end_address}; | ||
| 411 | auto it = current_range.lower_bound(search_interval); | ||
| 412 | if (it == current_range.end()) { | ||
| 413 | return; | ||
| 414 | } | ||
| 415 | auto end_it = current_range.upper_bound(search_interval); | ||
| 416 | for (; it != end_it; it++) { | ||
| 417 | auto& inter = it->first; | ||
| 418 | DAddr inter_addr_end = inter.upper(); | ||
| 419 | DAddr inter_addr = inter.lower(); | ||
| 420 | if (inter_addr_end > end_address) { | ||
| 421 | inter_addr_end = end_address; | ||
| 422 | } | ||
| 423 | if (inter_addr < start_address) { | ||
| 424 | inter_addr = start_address; | ||
| 425 | } | ||
| 426 | func(inter_addr, inter_addr_end, it->second); | ||
| 427 | } | ||
| 428 | } | ||
| 429 | |||
| 430 | void RemoveEachInOverlapCounter(OverlapCounter& current_range, | ||
| 431 | const IntervalType search_interval, int subtract_value) { | ||
| 432 | bool any_removals = false; | ||
| 433 | current_range.add(std::make_pair(search_interval, subtract_value)); | ||
| 434 | do { | ||
| 435 | any_removals = false; | ||
| 436 | auto it = current_range.lower_bound(search_interval); | ||
| 437 | if (it == current_range.end()) { | ||
| 438 | return; | ||
| 439 | } | ||
| 440 | auto end_it = current_range.upper_bound(search_interval); | ||
| 441 | for (; it != end_it; it++) { | ||
| 442 | if (it->second <= 0) { | ||
| 443 | any_removals = true; | ||
| 444 | current_range.erase(it); | ||
| 445 | break; | ||
| 446 | } | ||
| 447 | } | ||
| 448 | } while (any_removals); | ||
| 449 | } | ||
| 450 | |||
| 451 | static bool IsRangeGranular(DAddr device_addr, size_t size) { | 339 | static bool IsRangeGranular(DAddr device_addr, size_t size) { |
| 452 | return (device_addr & ~Core::DEVICE_PAGEMASK) == | 340 | return (device_addr & ~Core::DEVICE_PAGEMASK) == |
| 453 | ((device_addr + size) & ~Core::DEVICE_PAGEMASK); | 341 | ((device_addr + size) & ~Core::DEVICE_PAGEMASK); |
| @@ -552,14 +440,14 @@ private: | |||
| 552 | 440 | ||
| 553 | [[nodiscard]] bool HasFastUniformBufferBound(size_t stage, u32 binding_index) const noexcept; | 441 | [[nodiscard]] bool HasFastUniformBufferBound(size_t stage, u32 binding_index) const noexcept; |
| 554 | 442 | ||
| 555 | void ClearDownload(IntervalType subtract_interval); | 443 | void ClearDownload(DAddr base_addr, u64 size); |
| 556 | 444 | ||
| 557 | void InlineMemoryImplementation(DAddr dest_address, size_t copy_size, | 445 | void InlineMemoryImplementation(DAddr dest_address, size_t copy_size, |
| 558 | std::span<const u8> inlined_buffer); | 446 | std::span<const u8> inlined_buffer); |
| 559 | 447 | ||
| 560 | Tegra::MaxwellDeviceMemoryManager& device_memory; | 448 | Tegra::MaxwellDeviceMemoryManager& device_memory; |
| 561 | 449 | ||
| 562 | SlotVector<Buffer> slot_buffers; | 450 | Common::SlotVector<Buffer> slot_buffers; |
| 563 | DelayedDestructionRing<Buffer, 8> delayed_destruction_ring; | 451 | DelayedDestructionRing<Buffer, 8> delayed_destruction_ring; |
| 564 | 452 | ||
| 565 | const Tegra::Engines::DrawManager::IndirectParams* current_draw_indirect{}; | 453 | const Tegra::Engines::DrawManager::IndirectParams* current_draw_indirect{}; |
| @@ -567,13 +455,12 @@ private: | |||
| 567 | u32 last_index_count = 0; | 455 | u32 last_index_count = 0; |
| 568 | 456 | ||
| 569 | MemoryTracker memory_tracker; | 457 | MemoryTracker memory_tracker; |
| 570 | IntervalSet uncommitted_ranges; | 458 | Common::RangeSet<DAddr> uncommitted_gpu_modified_ranges; |
| 571 | IntervalSet common_ranges; | 459 | Common::RangeSet<DAddr> gpu_modified_ranges; |
| 572 | IntervalSet cached_ranges; | 460 | std::deque<Common::RangeSet<DAddr>> committed_gpu_modified_ranges; |
| 573 | std::deque<IntervalSet> committed_ranges; | ||
| 574 | 461 | ||
| 575 | // Async Buffers | 462 | // Async Buffers |
| 576 | OverlapCounter async_downloads; | 463 | Common::OverlapRangeSet<DAddr> async_downloads; |
| 577 | std::deque<std::optional<Async_Buffer>> async_buffers; | 464 | std::deque<std::optional<Async_Buffer>> async_buffers; |
| 578 | std::deque<boost::container::small_vector<BufferCopy, 4>> pending_downloads; | 465 | std::deque<boost::container::small_vector<BufferCopy, 4>> pending_downloads; |
| 579 | std::optional<Async_Buffer> current_buffer; | 466 | std::optional<Async_Buffer> current_buffer; |
diff --git a/src/video_core/capture.h b/src/video_core/capture.h new file mode 100644 index 000000000..8db14a8ec --- /dev/null +++ b/src/video_core/capture.h | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "common/alignment.h" | ||
| 7 | #include "common/bit_util.h" | ||
| 8 | #include "common/common_types.h" | ||
| 9 | #include "core/frontend/framebuffer_layout.h" | ||
| 10 | #include "video_core/surface.h" | ||
| 11 | |||
| 12 | namespace VideoCore::Capture { | ||
| 13 | |||
| 14 | constexpr u32 BlockHeight = 4; | ||
| 15 | constexpr u32 BlockDepth = 0; | ||
| 16 | constexpr u32 BppLog2 = 2; | ||
| 17 | |||
| 18 | constexpr auto PixelFormat = Surface::PixelFormat::B8G8R8A8_UNORM; | ||
| 19 | |||
| 20 | constexpr auto LinearWidth = Layout::ScreenUndocked::Width; | ||
| 21 | constexpr auto LinearHeight = Layout::ScreenUndocked::Height; | ||
| 22 | constexpr auto LinearDepth = 1U; | ||
| 23 | constexpr auto BytesPerPixel = 4U; | ||
| 24 | |||
| 25 | constexpr auto TiledWidth = LinearWidth; | ||
| 26 | constexpr auto TiledHeight = Common::AlignUpLog2(LinearHeight, BlockHeight + BlockDepth + BppLog2); | ||
| 27 | constexpr auto TiledSize = TiledWidth * TiledHeight * (1 << BppLog2); | ||
| 28 | |||
| 29 | constexpr Layout::FramebufferLayout Layout{ | ||
| 30 | .width = LinearWidth, | ||
| 31 | .height = LinearHeight, | ||
| 32 | .screen = {0, 0, LinearWidth, LinearHeight}, | ||
| 33 | .is_srgb = false, | ||
| 34 | }; | ||
| 35 | |||
| 36 | } // namespace VideoCore::Capture | ||
diff --git a/src/video_core/framebuffer_config.h b/src/video_core/framebuffer_config.h index 6a18b76fb..8b2a49de5 100644 --- a/src/video_core/framebuffer_config.h +++ b/src/video_core/framebuffer_config.h | |||
| @@ -11,6 +11,12 @@ | |||
| 11 | 11 | ||
| 12 | namespace Tegra { | 12 | namespace Tegra { |
| 13 | 13 | ||
| 14 | enum class BlendMode { | ||
| 15 | Opaque, | ||
| 16 | Premultiplied, | ||
| 17 | Coverage, | ||
| 18 | }; | ||
| 19 | |||
| 14 | /** | 20 | /** |
| 15 | * Struct describing framebuffer configuration | 21 | * Struct describing framebuffer configuration |
| 16 | */ | 22 | */ |
| @@ -23,6 +29,7 @@ struct FramebufferConfig { | |||
| 23 | Service::android::PixelFormat pixel_format{}; | 29 | Service::android::PixelFormat pixel_format{}; |
| 24 | Service::android::BufferTransformFlags transform_flags{}; | 30 | Service::android::BufferTransformFlags transform_flags{}; |
| 25 | Common::Rectangle<int> crop_rect{}; | 31 | Common::Rectangle<int> crop_rect{}; |
| 32 | BlendMode blending{}; | ||
| 26 | }; | 33 | }; |
| 27 | 34 | ||
| 28 | Common::Rectangle<f32> NormalizeCrop(const FramebufferConfig& framebuffer, u32 texture_width, | 35 | Common::Rectangle<f32> NormalizeCrop(const FramebufferConfig& framebuffer, u32 texture_width, |
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index f4a5d831c..8e663f2a8 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp | |||
| @@ -347,6 +347,17 @@ struct GPU::Impl { | |||
| 347 | WaitForSyncOperation(wait_fence); | 347 | WaitForSyncOperation(wait_fence); |
| 348 | } | 348 | } |
| 349 | 349 | ||
| 350 | std::vector<u8> GetAppletCaptureBuffer() { | ||
| 351 | std::vector<u8> out; | ||
| 352 | |||
| 353 | const auto wait_fence = | ||
| 354 | RequestSyncOperation([&] { out = renderer->GetAppletCaptureBuffer(); }); | ||
| 355 | gpu_thread.TickGPU(); | ||
| 356 | WaitForSyncOperation(wait_fence); | ||
| 357 | |||
| 358 | return out; | ||
| 359 | } | ||
| 360 | |||
| 350 | GPU& gpu; | 361 | GPU& gpu; |
| 351 | Core::System& system; | 362 | Core::System& system; |
| 352 | Host1x::Host1x& host1x; | 363 | Host1x::Host1x& host1x; |
| @@ -505,6 +516,10 @@ void GPU::RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers, | |||
| 505 | impl->RequestComposite(std::move(layers), std::move(fences)); | 516 | impl->RequestComposite(std::move(layers), std::move(fences)); |
| 506 | } | 517 | } |
| 507 | 518 | ||
| 519 | std::vector<u8> GPU::GetAppletCaptureBuffer() { | ||
| 520 | return impl->GetAppletCaptureBuffer(); | ||
| 521 | } | ||
| 522 | |||
| 508 | u64 GPU::GetTicks() const { | 523 | u64 GPU::GetTicks() const { |
| 509 | return impl->GetTicks(); | 524 | return impl->GetTicks(); |
| 510 | } | 525 | } |
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index c4602ca37..ad535512c 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h | |||
| @@ -215,6 +215,8 @@ public: | |||
| 215 | void RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers, | 215 | void RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers, |
| 216 | std::vector<Service::Nvidia::NvFence>&& fences); | 216 | std::vector<Service::Nvidia::NvFence>&& fences); |
| 217 | 217 | ||
| 218 | std::vector<u8> GetAppletCaptureBuffer(); | ||
| 219 | |||
| 218 | /// Performs any additional setup necessary in order to begin GPU emulation. | 220 | /// Performs any additional setup necessary in order to begin GPU emulation. |
| 219 | /// This can be used to launch any necessary threads and register any necessary | 221 | /// This can be used to launch any necessary threads and register any necessary |
| 220 | /// core timing events. | 222 | /// core timing events. |
diff --git a/src/video_core/host1x/host1x.cpp b/src/video_core/host1x/host1x.cpp index c4c7a5883..e923bfa22 100644 --- a/src/video_core/host1x/host1x.cpp +++ b/src/video_core/host1x/host1x.cpp | |||
| @@ -10,7 +10,7 @@ namespace Host1x { | |||
| 10 | 10 | ||
| 11 | Host1x::Host1x(Core::System& system_) | 11 | Host1x::Host1x(Core::System& system_) |
| 12 | : system{system_}, syncpoint_manager{}, | 12 | : system{system_}, syncpoint_manager{}, |
| 13 | memory_manager(system.DeviceMemory()), gmmu_manager{system, memory_manager, 32, 12}, | 13 | memory_manager(system.DeviceMemory()), gmmu_manager{system, memory_manager, 32, 0, 12}, |
| 14 | allocator{std::make_unique<Common::FlatAllocator<u32, 0, 32>>(1 << 12)} {} | 14 | allocator{std::make_unique<Common::FlatAllocator<u32, 0, 32>>(1 << 12)} {} |
| 15 | 15 | ||
| 16 | Host1x::~Host1x() = default; | 16 | Host1x::~Host1x() = default; |
diff --git a/src/video_core/host_shaders/fidelityfx_fsr.frag b/src/video_core/host_shaders/fidelityfx_fsr.frag index a266e1c4e..54eedb450 100644 --- a/src/video_core/host_shaders/fidelityfx_fsr.frag +++ b/src/video_core/host_shaders/fidelityfx_fsr.frag | |||
| @@ -37,6 +37,7 @@ layout(set=0,binding=0) uniform sampler2D InputTexture; | |||
| 37 | 37 | ||
| 38 | #define A_GPU 1 | 38 | #define A_GPU 1 |
| 39 | #define A_GLSL 1 | 39 | #define A_GLSL 1 |
| 40 | #define FSR_RCAS_PASSTHROUGH_ALPHA 1 | ||
| 40 | 41 | ||
| 41 | #ifndef YUZU_USE_FP16 | 42 | #ifndef YUZU_USE_FP16 |
| 42 | #include "ffx_a.h" | 43 | #include "ffx_a.h" |
| @@ -71,9 +72,7 @@ layout(set=0,binding=0) uniform sampler2D InputTexture; | |||
| 71 | 72 | ||
| 72 | #include "ffx_fsr1.h" | 73 | #include "ffx_fsr1.h" |
| 73 | 74 | ||
| 74 | #if USE_RCAS | 75 | layout (location = 0) in vec2 frag_texcoord; |
| 75 | layout(location = 0) in vec2 frag_texcoord; | ||
| 76 | #endif | ||
| 77 | layout (location = 0) out vec4 frag_color; | 76 | layout (location = 0) out vec4 frag_color; |
| 78 | 77 | ||
| 79 | void CurrFilter(AU2 pos) { | 78 | void CurrFilter(AU2 pos) { |
| @@ -81,22 +80,22 @@ void CurrFilter(AU2 pos) { | |||
| 81 | #ifndef YUZU_USE_FP16 | 80 | #ifndef YUZU_USE_FP16 |
| 82 | AF3 c; | 81 | AF3 c; |
| 83 | FsrEasuF(c, pos, Const0, Const1, Const2, Const3); | 82 | FsrEasuF(c, pos, Const0, Const1, Const2, Const3); |
| 84 | frag_color = AF4(c, 1.0); | 83 | frag_color = AF4(c, texture(InputTexture, frag_texcoord).a); |
| 85 | #else | 84 | #else |
| 86 | AH3 c; | 85 | AH3 c; |
| 87 | FsrEasuH(c, pos, Const0, Const1, Const2, Const3); | 86 | FsrEasuH(c, pos, Const0, Const1, Const2, Const3); |
| 88 | frag_color = AH4(c, 1.0); | 87 | frag_color = AH4(c, texture(InputTexture, frag_texcoord).a); |
| 89 | #endif | 88 | #endif |
| 90 | #endif | 89 | #endif |
| 91 | #if USE_RCAS | 90 | #if USE_RCAS |
| 92 | #ifndef YUZU_USE_FP16 | 91 | #ifndef YUZU_USE_FP16 |
| 93 | AF3 c; | 92 | AF4 c; |
| 94 | FsrRcasF(c.r, c.g, c.b, pos, Const0); | 93 | FsrRcasF(c.r, c.g, c.b, c.a, pos, Const0); |
| 95 | frag_color = AF4(c, 1.0); | 94 | frag_color = c; |
| 96 | #else | 95 | #else |
| 97 | AH3 c; | 96 | AH4 c; |
| 98 | FsrRcasH(c.r, c.g, c.b, pos, Const0); | 97 | FsrRcasH(c.r, c.g, c.b, c.a, pos, Const0); |
| 99 | frag_color = AH4(c, 1.0); | 98 | frag_color = c; |
| 100 | #endif | 99 | #endif |
| 101 | #endif | 100 | #endif |
| 102 | } | 101 | } |
diff --git a/src/video_core/host_shaders/fxaa.frag b/src/video_core/host_shaders/fxaa.frag index 9bffc20d5..192a602c1 100644 --- a/src/video_core/host_shaders/fxaa.frag +++ b/src/video_core/host_shaders/fxaa.frag | |||
| @@ -71,5 +71,5 @@ vec3 FxaaPixelShader(vec4 posPos, sampler2D tex) { | |||
| 71 | } | 71 | } |
| 72 | 72 | ||
| 73 | void main() { | 73 | void main() { |
| 74 | frag_color = vec4(FxaaPixelShader(posPos, input_texture), 1.0); | 74 | frag_color = vec4(FxaaPixelShader(posPos, input_texture), texture(input_texture, posPos.xy).a); |
| 75 | } | 75 | } |
diff --git a/src/video_core/host_shaders/opengl_fidelityfx_fsr.frag b/src/video_core/host_shaders/opengl_fidelityfx_fsr.frag index 16d22f58e..fc47d3810 100644 --- a/src/video_core/host_shaders/opengl_fidelityfx_fsr.frag +++ b/src/video_core/host_shaders/opengl_fidelityfx_fsr.frag | |||
| @@ -31,6 +31,7 @@ layout (location = 0) uniform uvec4 constants[4]; | |||
| 31 | 31 | ||
| 32 | #define A_GPU 1 | 32 | #define A_GPU 1 |
| 33 | #define A_GLSL 1 | 33 | #define A_GLSL 1 |
| 34 | #define FSR_RCAS_PASSTHROUGH_ALPHA 1 | ||
| 34 | 35 | ||
| 35 | #ifdef YUZU_USE_FP16 | 36 | #ifdef YUZU_USE_FP16 |
| 36 | #define A_HALF | 37 | #define A_HALF |
| @@ -67,9 +68,7 @@ layout (location = 0) uniform uvec4 constants[4]; | |||
| 67 | 68 | ||
| 68 | #include "ffx_fsr1.h" | 69 | #include "ffx_fsr1.h" |
| 69 | 70 | ||
| 70 | #if USE_RCAS | 71 | layout (location = 0) in vec2 frag_texcoord; |
| 71 | layout(location = 0) in vec2 frag_texcoord; | ||
| 72 | #endif | ||
| 73 | layout (location = 0) out vec4 frag_color; | 72 | layout (location = 0) out vec4 frag_color; |
| 74 | 73 | ||
| 75 | void CurrFilter(AU2 pos) | 74 | void CurrFilter(AU2 pos) |
| @@ -78,22 +77,22 @@ void CurrFilter(AU2 pos) | |||
| 78 | #ifndef YUZU_USE_FP16 | 77 | #ifndef YUZU_USE_FP16 |
| 79 | AF3 c; | 78 | AF3 c; |
| 80 | FsrEasuF(c, pos, constants[0], constants[1], constants[2], constants[3]); | 79 | FsrEasuF(c, pos, constants[0], constants[1], constants[2], constants[3]); |
| 81 | frag_color = AF4(c, 1.0); | 80 | frag_color = AF4(c, texture(InputTexture, frag_texcoord).a); |
| 82 | #else | 81 | #else |
| 83 | AH3 c; | 82 | AH3 c; |
| 84 | FsrEasuH(c, pos, constants[0], constants[1], constants[2], constants[3]); | 83 | FsrEasuH(c, pos, constants[0], constants[1], constants[2], constants[3]); |
| 85 | frag_color = AH4(c, 1.0); | 84 | frag_color = AH4(c, texture(InputTexture, frag_texcoord).a); |
| 86 | #endif | 85 | #endif |
| 87 | #endif | 86 | #endif |
| 88 | #if USE_RCAS | 87 | #if USE_RCAS |
| 89 | #ifndef YUZU_USE_FP16 | 88 | #ifndef YUZU_USE_FP16 |
| 90 | AF3 c; | 89 | AF4 c; |
| 91 | FsrRcasF(c.r, c.g, c.b, pos, constants[0]); | 90 | FsrRcasF(c.r, c.g, c.b, c.a, pos, constants[0]); |
| 92 | frag_color = AF4(c, 1.0); | 91 | frag_color = c; |
| 93 | #else | 92 | #else |
| 94 | AH3 c; | 93 | AH3 c; |
| 95 | FsrRcasH(c.r, c.g, c.b, pos, constants[0]); | 94 | FsrRcasH(c.r, c.g, c.b, c.a, pos, constants[0]); |
| 96 | frag_color = AH4(c, 1.0); | 95 | frag_color = c; |
| 97 | #endif | 96 | #endif |
| 98 | #endif | 97 | #endif |
| 99 | } | 98 | } |
diff --git a/src/video_core/host_shaders/opengl_present.frag b/src/video_core/host_shaders/opengl_present.frag index 5fd7ad297..096b4e4db 100644 --- a/src/video_core/host_shaders/opengl_present.frag +++ b/src/video_core/host_shaders/opengl_present.frag | |||
| @@ -9,5 +9,5 @@ layout (location = 0) out vec4 color; | |||
| 9 | layout (binding = 0) uniform sampler2D color_texture; | 9 | layout (binding = 0) uniform sampler2D color_texture; |
| 10 | 10 | ||
| 11 | void main() { | 11 | void main() { |
| 12 | color = vec4(texture(color_texture, frag_tex_coord).rgb, 1.0f); | 12 | color = vec4(texture(color_texture, frag_tex_coord)); |
| 13 | } | 13 | } |
diff --git a/src/video_core/host_shaders/present_bicubic.frag b/src/video_core/host_shaders/present_bicubic.frag index c814629cf..a9d9d40a3 100644 --- a/src/video_core/host_shaders/present_bicubic.frag +++ b/src/video_core/host_shaders/present_bicubic.frag | |||
| @@ -52,5 +52,5 @@ vec4 textureBicubic( sampler2D textureSampler, vec2 texCoords ) { | |||
| 52 | } | 52 | } |
| 53 | 53 | ||
| 54 | void main() { | 54 | void main() { |
| 55 | color = vec4(textureBicubic(color_texture, frag_tex_coord).rgb, 1.0f); | 55 | color = textureBicubic(color_texture, frag_tex_coord); |
| 56 | } | 56 | } |
diff --git a/src/video_core/host_shaders/present_gaussian.frag b/src/video_core/host_shaders/present_gaussian.frag index ad9bb76a4..78edeb9b4 100644 --- a/src/video_core/host_shaders/present_gaussian.frag +++ b/src/video_core/host_shaders/present_gaussian.frag | |||
| @@ -46,14 +46,14 @@ vec4 blurDiagonal(sampler2D textureSampler, vec2 coord, vec2 norm) { | |||
| 46 | } | 46 | } |
| 47 | 47 | ||
| 48 | void main() { | 48 | void main() { |
| 49 | vec3 base = texture(color_texture, vec2(frag_tex_coord)).rgb * weight[0]; | 49 | vec4 base = texture(color_texture, vec2(frag_tex_coord)) * weight[0]; |
| 50 | vec2 tex_offset = 1.0f / textureSize(color_texture, 0); | 50 | vec2 tex_offset = 1.0f / textureSize(color_texture, 0); |
| 51 | 51 | ||
| 52 | // TODO(Blinkhawk): This code can be optimized through shader group instructions. | 52 | // TODO(Blinkhawk): This code can be optimized through shader group instructions. |
| 53 | vec3 horizontal = blurHorizontal(color_texture, frag_tex_coord, tex_offset).rgb; | 53 | vec4 horizontal = blurHorizontal(color_texture, frag_tex_coord, tex_offset); |
| 54 | vec3 vertical = blurVertical(color_texture, frag_tex_coord, tex_offset).rgb; | 54 | vec4 vertical = blurVertical(color_texture, frag_tex_coord, tex_offset); |
| 55 | vec3 diagonalA = blurDiagonal(color_texture, frag_tex_coord, tex_offset).rgb; | 55 | vec4 diagonalA = blurDiagonal(color_texture, frag_tex_coord, tex_offset); |
| 56 | vec3 diagonalB = blurDiagonal(color_texture, frag_tex_coord, tex_offset * vec2(1.0, -1.0)).rgb; | 56 | vec4 diagonalB = blurDiagonal(color_texture, frag_tex_coord, tex_offset * vec2(1.0, -1.0)); |
| 57 | vec3 combination = mix(mix(horizontal, vertical, 0.5f), mix(diagonalA, diagonalB, 0.5f), 0.5f); | 57 | vec4 combination = mix(mix(horizontal, vertical, 0.5f), mix(diagonalA, diagonalB, 0.5f), 0.5f); |
| 58 | color = vec4(combination + base, 1.0f); | 58 | color = combination + base; |
| 59 | } | 59 | } |
diff --git a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16.frag b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16.frag index d369bef06..05d033310 100644 --- a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16.frag +++ b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16.frag | |||
| @@ -6,5 +6,6 @@ | |||
| 6 | 6 | ||
| 7 | #define YUZU_USE_FP16 | 7 | #define YUZU_USE_FP16 |
| 8 | #define USE_EASU 1 | 8 | #define USE_EASU 1 |
| 9 | #define VERSION 1 | ||
| 9 | 10 | ||
| 10 | #include "fidelityfx_fsr.frag" | 11 | #include "fidelityfx_fsr.frag" |
diff --git a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32.frag b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32.frag index 6f25ef00f..7ae11dd66 100644 --- a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32.frag +++ b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32.frag | |||
| @@ -5,5 +5,6 @@ | |||
| 5 | #extension GL_GOOGLE_include_directive : enable | 5 | #extension GL_GOOGLE_include_directive : enable |
| 6 | 6 | ||
| 7 | #define USE_EASU 1 | 7 | #define USE_EASU 1 |
| 8 | #define VERSION 1 | ||
| 8 | 9 | ||
| 9 | #include "fidelityfx_fsr.frag" | 10 | #include "fidelityfx_fsr.frag" |
diff --git a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16.frag b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16.frag index 0c953a900..c017214a5 100644 --- a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16.frag +++ b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16.frag | |||
| @@ -6,5 +6,6 @@ | |||
| 6 | 6 | ||
| 7 | #define YUZU_USE_FP16 | 7 | #define YUZU_USE_FP16 |
| 8 | #define USE_RCAS 1 | 8 | #define USE_RCAS 1 |
| 9 | #define VERSION 1 | ||
| 9 | 10 | ||
| 10 | #include "fidelityfx_fsr.frag" | 11 | #include "fidelityfx_fsr.frag" |
diff --git a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp32.frag b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp32.frag index 02e9a27c6..976825f4b 100644 --- a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp32.frag +++ b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp32.frag | |||
| @@ -5,5 +5,6 @@ | |||
| 5 | #extension GL_GOOGLE_include_directive : enable | 5 | #extension GL_GOOGLE_include_directive : enable |
| 6 | 6 | ||
| 7 | #define USE_RCAS 1 | 7 | #define USE_RCAS 1 |
| 8 | #define VERSION 1 | ||
| 8 | 9 | ||
| 9 | #include "fidelityfx_fsr.frag" | 10 | #include "fidelityfx_fsr.frag" |
diff --git a/src/video_core/host_shaders/vulkan_present.vert b/src/video_core/host_shaders/vulkan_present.vert index 249c9675a..c0e6e8537 100644 --- a/src/video_core/host_shaders/vulkan_present.vert +++ b/src/video_core/host_shaders/vulkan_present.vert | |||
| @@ -19,15 +19,13 @@ layout (push_constant) uniform PushConstants { | |||
| 19 | // Any member of a push constant block that is declared as an | 19 | // Any member of a push constant block that is declared as an |
| 20 | // array must only be accessed with dynamically uniform indices. | 20 | // array must only be accessed with dynamically uniform indices. |
| 21 | ScreenRectVertex GetVertex(int index) { | 21 | ScreenRectVertex GetVertex(int index) { |
| 22 | switch (index) { | 22 | if (index < 1) { |
| 23 | case 0: | ||
| 24 | default: | ||
| 25 | return vertices[0]; | 23 | return vertices[0]; |
| 26 | case 1: | 24 | } else if (index < 2) { |
| 27 | return vertices[1]; | 25 | return vertices[1]; |
| 28 | case 2: | 26 | } else if (index < 3) { |
| 29 | return vertices[2]; | 27 | return vertices[2]; |
| 30 | case 3: | 28 | } else { |
| 31 | return vertices[3]; | 29 | return vertices[3]; |
| 32 | } | 30 | } |
| 33 | } | 31 | } |
diff --git a/src/video_core/host_shaders/vulkan_present_scaleforce_fp16.frag b/src/video_core/host_shaders/vulkan_present_scaleforce_fp16.frag index 79ea817c2..cea5dac9d 100644 --- a/src/video_core/host_shaders/vulkan_present_scaleforce_fp16.frag +++ b/src/video_core/host_shaders/vulkan_present_scaleforce_fp16.frag | |||
| @@ -5,7 +5,7 @@ | |||
| 5 | 5 | ||
| 6 | #extension GL_GOOGLE_include_directive : enable | 6 | #extension GL_GOOGLE_include_directive : enable |
| 7 | 7 | ||
| 8 | #define VERSION 1 | 8 | #define VERSION 2 |
| 9 | #define YUZU_USE_FP16 | 9 | #define YUZU_USE_FP16 |
| 10 | 10 | ||
| 11 | #include "opengl_present_scaleforce.frag" | 11 | #include "opengl_present_scaleforce.frag" |
diff --git a/src/video_core/host_shaders/vulkan_present_scaleforce_fp32.frag b/src/video_core/host_shaders/vulkan_present_scaleforce_fp32.frag index 9605bb58b..10ddf0401 100644 --- a/src/video_core/host_shaders/vulkan_present_scaleforce_fp32.frag +++ b/src/video_core/host_shaders/vulkan_present_scaleforce_fp32.frag | |||
| @@ -5,6 +5,6 @@ | |||
| 5 | 5 | ||
| 6 | #extension GL_GOOGLE_include_directive : enable | 6 | #extension GL_GOOGLE_include_directive : enable |
| 7 | 7 | ||
| 8 | #define VERSION 1 | 8 | #define VERSION 2 |
| 9 | 9 | ||
| 10 | #include "opengl_present_scaleforce.frag" | 10 | #include "opengl_present_scaleforce.frag" |
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index a52f8e486..ffafc48ef 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp | |||
| @@ -22,11 +22,12 @@ using Tegra::Memory::GuestMemoryFlags; | |||
| 22 | std::atomic<size_t> MemoryManager::unique_identifier_generator{}; | 22 | std::atomic<size_t> MemoryManager::unique_identifier_generator{}; |
| 23 | 23 | ||
| 24 | MemoryManager::MemoryManager(Core::System& system_, MaxwellDeviceMemoryManager& memory_, | 24 | MemoryManager::MemoryManager(Core::System& system_, MaxwellDeviceMemoryManager& memory_, |
| 25 | u64 address_space_bits_, u64 big_page_bits_, u64 page_bits_) | 25 | u64 address_space_bits_, GPUVAddr split_address_, u64 big_page_bits_, |
| 26 | u64 page_bits_) | ||
| 26 | : system{system_}, memory{memory_}, address_space_bits{address_space_bits_}, | 27 | : system{system_}, memory{memory_}, address_space_bits{address_space_bits_}, |
| 27 | page_bits{page_bits_}, big_page_bits{big_page_bits_}, entries{}, big_entries{}, | 28 | split_address{split_address_}, page_bits{page_bits_}, big_page_bits{big_page_bits_}, |
| 28 | page_table{address_space_bits, address_space_bits + page_bits - 38, | 29 | entries{}, big_entries{}, page_table{address_space_bits, address_space_bits + page_bits - 38, |
| 29 | page_bits != big_page_bits ? page_bits : 0}, | 30 | page_bits != big_page_bits ? page_bits : 0}, |
| 30 | kind_map{PTEKind::INVALID}, unique_identifier{unique_identifier_generator.fetch_add( | 31 | kind_map{PTEKind::INVALID}, unique_identifier{unique_identifier_generator.fetch_add( |
| 31 | 1, std::memory_order_acq_rel)}, | 32 | 1, std::memory_order_acq_rel)}, |
| 32 | accumulator{std::make_unique<VideoCommon::InvalidationAccumulator>()} { | 33 | accumulator{std::make_unique<VideoCommon::InvalidationAccumulator>()} { |
| @@ -48,10 +49,10 @@ MemoryManager::MemoryManager(Core::System& system_, MaxwellDeviceMemoryManager& | |||
| 48 | entries.resize(page_table_size / 32, 0); | 49 | entries.resize(page_table_size / 32, 0); |
| 49 | } | 50 | } |
| 50 | 51 | ||
| 51 | MemoryManager::MemoryManager(Core::System& system_, u64 address_space_bits_, u64 big_page_bits_, | 52 | MemoryManager::MemoryManager(Core::System& system_, u64 address_space_bits_, |
| 52 | u64 page_bits_) | 53 | GPUVAddr split_address_, u64 big_page_bits_, u64 page_bits_) |
| 53 | : MemoryManager(system_, system_.Host1x().MemoryManager(), address_space_bits_, big_page_bits_, | 54 | : MemoryManager(system_, system_.Host1x().MemoryManager(), address_space_bits_, split_address_, |
| 54 | page_bits_) {} | 55 | big_page_bits_, page_bits_) {} |
| 55 | 56 | ||
| 56 | MemoryManager::~MemoryManager() = default; | 57 | MemoryManager::~MemoryManager() = default; |
| 57 | 58 | ||
diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h index c5255f36c..ac7c1472a 100644 --- a/src/video_core/memory_manager.h +++ b/src/video_core/memory_manager.h | |||
| @@ -36,10 +36,11 @@ namespace Tegra { | |||
| 36 | class MemoryManager final { | 36 | class MemoryManager final { |
| 37 | public: | 37 | public: |
| 38 | explicit MemoryManager(Core::System& system_, u64 address_space_bits_ = 40, | 38 | explicit MemoryManager(Core::System& system_, u64 address_space_bits_ = 40, |
| 39 | u64 big_page_bits_ = 16, u64 page_bits_ = 12); | 39 | GPUVAddr split_address = 1ULL << 34, u64 big_page_bits_ = 16, |
| 40 | explicit MemoryManager(Core::System& system_, MaxwellDeviceMemoryManager& memory_, | ||
| 41 | u64 address_space_bits_ = 40, u64 big_page_bits_ = 16, | ||
| 42 | u64 page_bits_ = 12); | 40 | u64 page_bits_ = 12); |
| 41 | explicit MemoryManager(Core::System& system_, MaxwellDeviceMemoryManager& memory_, | ||
| 42 | u64 address_space_bits_ = 40, GPUVAddr split_address = 1ULL << 34, | ||
| 43 | u64 big_page_bits_ = 16, u64 page_bits_ = 12); | ||
| 43 | ~MemoryManager(); | 44 | ~MemoryManager(); |
| 44 | 45 | ||
| 45 | size_t GetID() const { | 46 | size_t GetID() const { |
| @@ -192,6 +193,7 @@ private: | |||
| 192 | MaxwellDeviceMemoryManager& memory; | 193 | MaxwellDeviceMemoryManager& memory; |
| 193 | 194 | ||
| 194 | const u64 address_space_bits; | 195 | const u64 address_space_bits; |
| 196 | GPUVAddr split_address; | ||
| 195 | const u64 page_bits; | 197 | const u64 page_bits; |
| 196 | u64 address_space_size; | 198 | u64 address_space_size; |
| 197 | u64 page_size; | 199 | u64 page_size; |
diff --git a/src/video_core/present.h b/src/video_core/present.h new file mode 100644 index 000000000..4fdfcca68 --- /dev/null +++ b/src/video_core/present.h | |||
| @@ -0,0 +1,37 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "common/settings.h" | ||
| 7 | |||
| 8 | static inline Settings::ScalingFilter GetScalingFilter() { | ||
| 9 | return Settings::values.scaling_filter.GetValue(); | ||
| 10 | } | ||
| 11 | |||
| 12 | static inline Settings::AntiAliasing GetAntiAliasing() { | ||
| 13 | return Settings::values.anti_aliasing.GetValue(); | ||
| 14 | } | ||
| 15 | |||
| 16 | static inline Settings::ScalingFilter GetScalingFilterForAppletCapture() { | ||
| 17 | return Settings::ScalingFilter::Bilinear; | ||
| 18 | } | ||
| 19 | |||
| 20 | static inline Settings::AntiAliasing GetAntiAliasingForAppletCapture() { | ||
| 21 | return Settings::AntiAliasing::None; | ||
| 22 | } | ||
| 23 | |||
| 24 | struct PresentFilters { | ||
| 25 | Settings::ScalingFilter (*get_scaling_filter)(); | ||
| 26 | Settings::AntiAliasing (*get_anti_aliasing)(); | ||
| 27 | }; | ||
| 28 | |||
| 29 | constexpr PresentFilters PresentFiltersForDisplay{ | ||
| 30 | .get_scaling_filter = &GetScalingFilter, | ||
| 31 | .get_anti_aliasing = &GetAntiAliasing, | ||
| 32 | }; | ||
| 33 | |||
| 34 | constexpr PresentFilters PresentFiltersForAppletCapture{ | ||
| 35 | .get_scaling_filter = &GetScalingFilterForAppletCapture, | ||
| 36 | .get_anti_aliasing = &GetAntiAliasingForAppletCapture, | ||
| 37 | }; | ||
diff --git a/src/video_core/query_cache.h b/src/video_core/query_cache.h index 4861b123a..e1019f228 100644 --- a/src/video_core/query_cache.h +++ b/src/video_core/query_cache.h | |||
| @@ -18,12 +18,12 @@ | |||
| 18 | 18 | ||
| 19 | #include "common/assert.h" | 19 | #include "common/assert.h" |
| 20 | #include "common/settings.h" | 20 | #include "common/settings.h" |
| 21 | #include "common/slot_vector.h" | ||
| 21 | #include "video_core/control/channel_state_cache.h" | 22 | #include "video_core/control/channel_state_cache.h" |
| 22 | #include "video_core/engines/maxwell_3d.h" | 23 | #include "video_core/engines/maxwell_3d.h" |
| 23 | #include "video_core/host1x/gpu_device_memory_manager.h" | 24 | #include "video_core/host1x/gpu_device_memory_manager.h" |
| 24 | #include "video_core/memory_manager.h" | 25 | #include "video_core/memory_manager.h" |
| 25 | #include "video_core/rasterizer_interface.h" | 26 | #include "video_core/rasterizer_interface.h" |
| 26 | #include "video_core/texture_cache/slot_vector.h" | ||
| 27 | 27 | ||
| 28 | namespace VideoCore { | 28 | namespace VideoCore { |
| 29 | enum class QueryType { | 29 | enum class QueryType { |
| @@ -37,7 +37,7 @@ constexpr std::size_t NumQueryTypes = static_cast<size_t>(QueryType::Count); | |||
| 37 | 37 | ||
| 38 | namespace VideoCommon { | 38 | namespace VideoCommon { |
| 39 | 39 | ||
| 40 | using AsyncJobId = SlotId; | 40 | using AsyncJobId = Common::SlotId; |
| 41 | 41 | ||
| 42 | static constexpr AsyncJobId NULL_ASYNC_JOB_ID{0}; | 42 | static constexpr AsyncJobId NULL_ASYNC_JOB_ID{0}; |
| 43 | 43 | ||
| @@ -341,7 +341,7 @@ private: | |||
| 341 | static constexpr std::uintptr_t YUZU_PAGESIZE = 4096; | 341 | static constexpr std::uintptr_t YUZU_PAGESIZE = 4096; |
| 342 | static constexpr unsigned YUZU_PAGEBITS = 12; | 342 | static constexpr unsigned YUZU_PAGEBITS = 12; |
| 343 | 343 | ||
| 344 | SlotVector<AsyncJob> slot_async_jobs; | 344 | Common::SlotVector<AsyncJob> slot_async_jobs; |
| 345 | 345 | ||
| 346 | VideoCore::RasterizerInterface& rasterizer; | 346 | VideoCore::RasterizerInterface& rasterizer; |
| 347 | Tegra::MaxwellDeviceMemoryManager& device_memory; | 347 | Tegra::MaxwellDeviceMemoryManager& device_memory; |
diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h index 3ad180f67..67427f937 100644 --- a/src/video_core/renderer_base.h +++ b/src/video_core/renderer_base.h | |||
| @@ -40,6 +40,9 @@ public: | |||
| 40 | /// Finalize rendering the guest frame and draw into the presentation texture | 40 | /// Finalize rendering the guest frame and draw into the presentation texture |
| 41 | virtual void Composite(std::span<const Tegra::FramebufferConfig> layers) = 0; | 41 | virtual void Composite(std::span<const Tegra::FramebufferConfig> layers) = 0; |
| 42 | 42 | ||
| 43 | /// Get the tiled applet layer capture buffer | ||
| 44 | virtual std::vector<u8> GetAppletCaptureBuffer() = 0; | ||
| 45 | |||
| 43 | [[nodiscard]] virtual RasterizerInterface* ReadRasterizer() = 0; | 46 | [[nodiscard]] virtual RasterizerInterface* ReadRasterizer() = 0; |
| 44 | 47 | ||
| 45 | [[nodiscard]] virtual std::string GetDeviceVendor() const = 0; | 48 | [[nodiscard]] virtual std::string GetDeviceVendor() const = 0; |
diff --git a/src/video_core/renderer_null/renderer_null.cpp b/src/video_core/renderer_null/renderer_null.cpp index c89daff53..e6147d66c 100644 --- a/src/video_core/renderer_null/renderer_null.cpp +++ b/src/video_core/renderer_null/renderer_null.cpp | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | #include "core/frontend/emu_window.h" | 4 | #include "core/frontend/emu_window.h" |
| 5 | #include "core/frontend/graphics_context.h" | 5 | #include "core/frontend/graphics_context.h" |
| 6 | #include "video_core/capture.h" | ||
| 6 | #include "video_core/renderer_null/renderer_null.h" | 7 | #include "video_core/renderer_null/renderer_null.h" |
| 7 | 8 | ||
| 8 | namespace Null { | 9 | namespace Null { |
| @@ -22,4 +23,8 @@ void RendererNull::Composite(std::span<const Tegra::FramebufferConfig> framebuff | |||
| 22 | render_window.OnFrameDisplayed(); | 23 | render_window.OnFrameDisplayed(); |
| 23 | } | 24 | } |
| 24 | 25 | ||
| 26 | std::vector<u8> RendererNull::GetAppletCaptureBuffer() { | ||
| 27 | return std::vector<u8>(VideoCore::Capture::TiledSize); | ||
| 28 | } | ||
| 29 | |||
| 25 | } // namespace Null | 30 | } // namespace Null |
diff --git a/src/video_core/renderer_null/renderer_null.h b/src/video_core/renderer_null/renderer_null.h index 063b476bb..34dbe1e4f 100644 --- a/src/video_core/renderer_null/renderer_null.h +++ b/src/video_core/renderer_null/renderer_null.h | |||
| @@ -19,6 +19,8 @@ public: | |||
| 19 | 19 | ||
| 20 | void Composite(std::span<const Tegra::FramebufferConfig> framebuffer) override; | 20 | void Composite(std::span<const Tegra::FramebufferConfig> framebuffer) override; |
| 21 | 21 | ||
| 22 | std::vector<u8> GetAppletCaptureBuffer() override; | ||
| 23 | |||
| 22 | VideoCore::RasterizerInterface* ReadRasterizer() override { | 24 | VideoCore::RasterizerInterface* ReadRasterizer() override { |
| 23 | return &m_rasterizer; | 25 | return &m_rasterizer; |
| 24 | } | 26 | } |
diff --git a/src/video_core/renderer_opengl/gl_blit_screen.cpp b/src/video_core/renderer_opengl/gl_blit_screen.cpp index 6ba8b214b..9260a4dc4 100644 --- a/src/video_core/renderer_opengl/gl_blit_screen.cpp +++ b/src/video_core/renderer_opengl/gl_blit_screen.cpp | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include "common/settings.h" | 4 | #include "common/settings.h" |
| 5 | #include "video_core/present.h" | ||
| 5 | #include "video_core/renderer_opengl/gl_blit_screen.h" | 6 | #include "video_core/renderer_opengl/gl_blit_screen.h" |
| 6 | #include "video_core/renderer_opengl/gl_state_tracker.h" | 7 | #include "video_core/renderer_opengl/gl_state_tracker.h" |
| 7 | #include "video_core/renderer_opengl/present/filters.h" | 8 | #include "video_core/renderer_opengl/present/filters.h" |
| @@ -13,14 +14,14 @@ namespace OpenGL { | |||
| 13 | BlitScreen::BlitScreen(RasterizerOpenGL& rasterizer_, | 14 | BlitScreen::BlitScreen(RasterizerOpenGL& rasterizer_, |
| 14 | Tegra::MaxwellDeviceMemoryManager& device_memory_, | 15 | Tegra::MaxwellDeviceMemoryManager& device_memory_, |
| 15 | StateTracker& state_tracker_, ProgramManager& program_manager_, | 16 | StateTracker& state_tracker_, ProgramManager& program_manager_, |
| 16 | Device& device_) | 17 | Device& device_, const PresentFilters& filters_) |
| 17 | : rasterizer(rasterizer_), device_memory(device_memory_), state_tracker(state_tracker_), | 18 | : rasterizer(rasterizer_), device_memory(device_memory_), state_tracker(state_tracker_), |
| 18 | program_manager(program_manager_), device(device_) {} | 19 | program_manager(program_manager_), device(device_), filters(filters_) {} |
| 19 | 20 | ||
| 20 | BlitScreen::~BlitScreen() = default; | 21 | BlitScreen::~BlitScreen() = default; |
| 21 | 22 | ||
| 22 | void BlitScreen::DrawScreen(std::span<const Tegra::FramebufferConfig> framebuffers, | 23 | void BlitScreen::DrawScreen(std::span<const Tegra::FramebufferConfig> framebuffers, |
| 23 | const Layout::FramebufferLayout& layout) { | 24 | const Layout::FramebufferLayout& layout, bool invert_y) { |
| 24 | // TODO: Signal state tracker about these changes | 25 | // TODO: Signal state tracker about these changes |
| 25 | state_tracker.NotifyScreenDrawVertexArray(); | 26 | state_tracker.NotifyScreenDrawVertexArray(); |
| 26 | state_tracker.NotifyPolygonModes(); | 27 | state_tracker.NotifyPolygonModes(); |
| @@ -56,22 +57,22 @@ void BlitScreen::DrawScreen(std::span<const Tegra::FramebufferConfig> framebuffe | |||
| 56 | glDepthRangeIndexed(0, 0.0, 0.0); | 57 | glDepthRangeIndexed(0, 0.0, 0.0); |
| 57 | 58 | ||
| 58 | while (layers.size() < framebuffers.size()) { | 59 | while (layers.size() < framebuffers.size()) { |
| 59 | layers.emplace_back(rasterizer, device_memory); | 60 | layers.emplace_back(rasterizer, device_memory, filters); |
| 60 | } | 61 | } |
| 61 | 62 | ||
| 62 | CreateWindowAdapt(); | 63 | CreateWindowAdapt(); |
| 63 | window_adapt->DrawToFramebuffer(program_manager, layers, framebuffers, layout); | 64 | window_adapt->DrawToFramebuffer(program_manager, layers, framebuffers, layout, invert_y); |
| 64 | 65 | ||
| 65 | // TODO | 66 | // TODO |
| 66 | // program_manager.RestoreGuestPipeline(); | 67 | // program_manager.RestoreGuestPipeline(); |
| 67 | } | 68 | } |
| 68 | 69 | ||
| 69 | void BlitScreen::CreateWindowAdapt() { | 70 | void BlitScreen::CreateWindowAdapt() { |
| 70 | if (window_adapt && Settings::values.scaling_filter.GetValue() == current_window_adapt) { | 71 | if (window_adapt && filters.get_scaling_filter() == current_window_adapt) { |
| 71 | return; | 72 | return; |
| 72 | } | 73 | } |
| 73 | 74 | ||
| 74 | current_window_adapt = Settings::values.scaling_filter.GetValue(); | 75 | current_window_adapt = filters.get_scaling_filter(); |
| 75 | switch (current_window_adapt) { | 76 | switch (current_window_adapt) { |
| 76 | case Settings::ScalingFilter::NearestNeighbor: | 77 | case Settings::ScalingFilter::NearestNeighbor: |
| 77 | window_adapt = MakeNearestNeighbor(device); | 78 | window_adapt = MakeNearestNeighbor(device); |
diff --git a/src/video_core/renderer_opengl/gl_blit_screen.h b/src/video_core/renderer_opengl/gl_blit_screen.h index 0c3d838f1..df2da9424 100644 --- a/src/video_core/renderer_opengl/gl_blit_screen.h +++ b/src/video_core/renderer_opengl/gl_blit_screen.h | |||
| @@ -15,6 +15,8 @@ namespace Layout { | |||
| 15 | struct FramebufferLayout; | 15 | struct FramebufferLayout; |
| 16 | } | 16 | } |
| 17 | 17 | ||
| 18 | struct PresentFilters; | ||
| 19 | |||
| 18 | namespace Tegra { | 20 | namespace Tegra { |
| 19 | struct FramebufferConfig; | 21 | struct FramebufferConfig; |
| 20 | } | 22 | } |
| @@ -46,12 +48,12 @@ public: | |||
| 46 | explicit BlitScreen(RasterizerOpenGL& rasterizer, | 48 | explicit BlitScreen(RasterizerOpenGL& rasterizer, |
| 47 | Tegra::MaxwellDeviceMemoryManager& device_memory, | 49 | Tegra::MaxwellDeviceMemoryManager& device_memory, |
| 48 | StateTracker& state_tracker, ProgramManager& program_manager, | 50 | StateTracker& state_tracker, ProgramManager& program_manager, |
| 49 | Device& device); | 51 | Device& device, const PresentFilters& filters); |
| 50 | ~BlitScreen(); | 52 | ~BlitScreen(); |
| 51 | 53 | ||
| 52 | /// Draws the emulated screens to the emulator window. | 54 | /// Draws the emulated screens to the emulator window. |
| 53 | void DrawScreen(std::span<const Tegra::FramebufferConfig> framebuffers, | 55 | void DrawScreen(std::span<const Tegra::FramebufferConfig> framebuffers, |
| 54 | const Layout::FramebufferLayout& layout); | 56 | const Layout::FramebufferLayout& layout, bool invert_y); |
| 55 | 57 | ||
| 56 | private: | 58 | private: |
| 57 | void CreateWindowAdapt(); | 59 | void CreateWindowAdapt(); |
| @@ -61,6 +63,7 @@ private: | |||
| 61 | StateTracker& state_tracker; | 63 | StateTracker& state_tracker; |
| 62 | ProgramManager& program_manager; | 64 | ProgramManager& program_manager; |
| 63 | Device& device; | 65 | Device& device; |
| 66 | const PresentFilters& filters; | ||
| 64 | 67 | ||
| 65 | Settings::ScalingFilter current_window_adapt{}; | 68 | Settings::ScalingFilter current_window_adapt{}; |
| 66 | std::unique_ptr<WindowAdaptPass> window_adapt; | 69 | std::unique_ptr<WindowAdaptPass> window_adapt; |
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h index af34c272b..fd471e979 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.h +++ b/src/video_core/renderer_opengl/gl_buffer_cache.h | |||
| @@ -90,7 +90,7 @@ public: | |||
| 90 | void PostCopyBarrier(); | 90 | void PostCopyBarrier(); |
| 91 | void Finish(); | 91 | void Finish(); |
| 92 | 92 | ||
| 93 | void TickFrame(VideoCommon::SlotVector<Buffer>&) noexcept {} | 93 | void TickFrame(Common::SlotVector<Buffer>&) noexcept {} |
| 94 | 94 | ||
| 95 | void ClearBuffer(Buffer& dest_buffer, u32 offset, size_t size, u32 value); | 95 | void ClearBuffer(Buffer& dest_buffer, u32 offset, size_t size, u32 value); |
| 96 | 96 | ||
| @@ -251,7 +251,6 @@ struct BufferCacheParams { | |||
| 251 | static constexpr bool NEEDS_BIND_STORAGE_INDEX = true; | 251 | static constexpr bool NEEDS_BIND_STORAGE_INDEX = true; |
| 252 | static constexpr bool USE_MEMORY_MAPS = true; | 252 | static constexpr bool USE_MEMORY_MAPS = true; |
| 253 | static constexpr bool SEPARATE_IMAGE_BUFFER_BINDINGS = true; | 253 | static constexpr bool SEPARATE_IMAGE_BUFFER_BINDINGS = true; |
| 254 | static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = true; | ||
| 255 | 254 | ||
| 256 | // TODO: Investigate why OpenGL seems to perform worse with persistently mapped buffer uploads | 255 | // TODO: Investigate why OpenGL seems to perform worse with persistently mapped buffer uploads |
| 257 | static constexpr bool USE_MEMORY_MAPS_FOR_UPLOADS = false; | 256 | static constexpr bool USE_MEMORY_MAPS_FOR_UPLOADS = false; |
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h index 3e54edcc2..d4165d8e4 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.h +++ b/src/video_core/renderer_opengl/gl_texture_cache.h | |||
| @@ -30,13 +30,13 @@ class Image; | |||
| 30 | class ImageView; | 30 | class ImageView; |
| 31 | class Sampler; | 31 | class Sampler; |
| 32 | 32 | ||
| 33 | using Common::SlotVector; | ||
| 33 | using VideoCommon::ImageId; | 34 | using VideoCommon::ImageId; |
| 34 | using VideoCommon::ImageViewId; | 35 | using VideoCommon::ImageViewId; |
| 35 | using VideoCommon::ImageViewType; | 36 | using VideoCommon::ImageViewType; |
| 36 | using VideoCommon::NUM_RT; | 37 | using VideoCommon::NUM_RT; |
| 37 | using VideoCommon::Region2D; | 38 | using VideoCommon::Region2D; |
| 38 | using VideoCommon::RenderTargets; | 39 | using VideoCommon::RenderTargets; |
| 39 | using VideoCommon::SlotVector; | ||
| 40 | 40 | ||
| 41 | struct FormatProperties { | 41 | struct FormatProperties { |
| 42 | GLenum compatibility_class; | 42 | GLenum compatibility_class; |
diff --git a/src/video_core/renderer_opengl/present/layer.cpp b/src/video_core/renderer_opengl/present/layer.cpp index 8643e07c6..6c7092d22 100644 --- a/src/video_core/renderer_opengl/present/layer.cpp +++ b/src/video_core/renderer_opengl/present/layer.cpp | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include "video_core/framebuffer_config.h" | 4 | #include "video_core/framebuffer_config.h" |
| 5 | #include "video_core/present.h" | ||
| 5 | #include "video_core/renderer_opengl/gl_blit_screen.h" | 6 | #include "video_core/renderer_opengl/gl_blit_screen.h" |
| 6 | #include "video_core/renderer_opengl/gl_rasterizer.h" | 7 | #include "video_core/renderer_opengl/gl_rasterizer.h" |
| 7 | #include "video_core/renderer_opengl/present/fsr.h" | 8 | #include "video_core/renderer_opengl/present/fsr.h" |
| @@ -14,8 +15,9 @@ | |||
| 14 | 15 | ||
| 15 | namespace OpenGL { | 16 | namespace OpenGL { |
| 16 | 17 | ||
| 17 | Layer::Layer(RasterizerOpenGL& rasterizer_, Tegra::MaxwellDeviceMemoryManager& device_memory_) | 18 | Layer::Layer(RasterizerOpenGL& rasterizer_, Tegra::MaxwellDeviceMemoryManager& device_memory_, |
| 18 | : rasterizer(rasterizer_), device_memory(device_memory_) { | 19 | const PresentFilters& filters_) |
| 20 | : rasterizer(rasterizer_), device_memory(device_memory_), filters(filters_) { | ||
| 19 | // Allocate textures for the screen | 21 | // Allocate textures for the screen |
| 20 | framebuffer_texture.resource.Create(GL_TEXTURE_2D); | 22 | framebuffer_texture.resource.Create(GL_TEXTURE_2D); |
| 21 | 23 | ||
| @@ -34,12 +36,12 @@ GLuint Layer::ConfigureDraw(std::array<GLfloat, 3 * 2>& out_matrix, | |||
| 34 | std::array<ScreenRectVertex, 4>& out_vertices, | 36 | std::array<ScreenRectVertex, 4>& out_vertices, |
| 35 | ProgramManager& program_manager, | 37 | ProgramManager& program_manager, |
| 36 | const Tegra::FramebufferConfig& framebuffer, | 38 | const Tegra::FramebufferConfig& framebuffer, |
| 37 | const Layout::FramebufferLayout& layout) { | 39 | const Layout::FramebufferLayout& layout, bool invert_y) { |
| 38 | FramebufferTextureInfo info = PrepareRenderTarget(framebuffer); | 40 | FramebufferTextureInfo info = PrepareRenderTarget(framebuffer); |
| 39 | auto crop = Tegra::NormalizeCrop(framebuffer, info.width, info.height); | 41 | auto crop = Tegra::NormalizeCrop(framebuffer, info.width, info.height); |
| 40 | GLuint texture = info.display_texture; | 42 | GLuint texture = info.display_texture; |
| 41 | 43 | ||
| 42 | auto anti_aliasing = Settings::values.anti_aliasing.GetValue(); | 44 | auto anti_aliasing = filters.get_anti_aliasing(); |
| 43 | if (anti_aliasing != Settings::AntiAliasing::None) { | 45 | if (anti_aliasing != Settings::AntiAliasing::None) { |
| 44 | glEnablei(GL_SCISSOR_TEST, 0); | 46 | glEnablei(GL_SCISSOR_TEST, 0); |
| 45 | auto viewport_width = Settings::values.resolution_info.ScaleUp(framebuffer_texture.width); | 47 | auto viewport_width = Settings::values.resolution_info.ScaleUp(framebuffer_texture.width); |
| @@ -64,7 +66,7 @@ GLuint Layer::ConfigureDraw(std::array<GLfloat, 3 * 2>& out_matrix, | |||
| 64 | 66 | ||
| 65 | glDisablei(GL_SCISSOR_TEST, 0); | 67 | glDisablei(GL_SCISSOR_TEST, 0); |
| 66 | 68 | ||
| 67 | if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) { | 69 | if (filters.get_scaling_filter() == Settings::ScalingFilter::Fsr) { |
| 68 | if (!fsr || fsr->NeedsRecreation(layout.screen)) { | 70 | if (!fsr || fsr->NeedsRecreation(layout.screen)) { |
| 69 | fsr = std::make_unique<FSR>(layout.screen.GetWidth(), layout.screen.GetHeight()); | 71 | fsr = std::make_unique<FSR>(layout.screen.GetWidth(), layout.screen.GetHeight()); |
| 70 | } | 72 | } |
| @@ -83,10 +85,15 @@ GLuint Layer::ConfigureDraw(std::array<GLfloat, 3 * 2>& out_matrix, | |||
| 83 | const auto w = screen.GetWidth(); | 85 | const auto w = screen.GetWidth(); |
| 84 | const auto h = screen.GetHeight(); | 86 | const auto h = screen.GetHeight(); |
| 85 | 87 | ||
| 86 | out_vertices[0] = ScreenRectVertex(x, y, crop.left, crop.top); | 88 | const auto left = crop.left; |
| 87 | out_vertices[1] = ScreenRectVertex(x + w, y, crop.right, crop.top); | 89 | const auto right = crop.right; |
| 88 | out_vertices[2] = ScreenRectVertex(x, y + h, crop.left, crop.bottom); | 90 | const auto top = invert_y ? crop.bottom : crop.top; |
| 89 | out_vertices[3] = ScreenRectVertex(x + w, y + h, crop.right, crop.bottom); | 91 | const auto bottom = invert_y ? crop.top : crop.bottom; |
| 92 | |||
| 93 | out_vertices[0] = ScreenRectVertex(x, y, left, top); | ||
| 94 | out_vertices[1] = ScreenRectVertex(x + w, y, right, top); | ||
| 95 | out_vertices[2] = ScreenRectVertex(x, y + h, left, bottom); | ||
| 96 | out_vertices[3] = ScreenRectVertex(x + w, y + h, right, bottom); | ||
| 90 | 97 | ||
| 91 | return texture; | 98 | return texture; |
| 92 | } | 99 | } |
| @@ -131,10 +138,12 @@ FramebufferTextureInfo Layer::LoadFBToScreenInfo(const Tegra::FramebufferConfig& | |||
| 131 | const u64 size_in_bytes{Tegra::Texture::CalculateSize( | 138 | const u64 size_in_bytes{Tegra::Texture::CalculateSize( |
| 132 | true, bytes_per_pixel, framebuffer.stride, framebuffer.height, 1, block_height_log2, 0)}; | 139 | true, bytes_per_pixel, framebuffer.stride, framebuffer.height, 1, block_height_log2, 0)}; |
| 133 | const u8* const host_ptr{device_memory.GetPointer<u8>(framebuffer_addr)}; | 140 | const u8* const host_ptr{device_memory.GetPointer<u8>(framebuffer_addr)}; |
| 134 | const std::span<const u8> input_data(host_ptr, size_in_bytes); | 141 | if (host_ptr) { |
| 135 | Tegra::Texture::UnswizzleTexture(gl_framebuffer_data, input_data, bytes_per_pixel, | 142 | const std::span<const u8> input_data(host_ptr, size_in_bytes); |
| 136 | framebuffer.width, framebuffer.height, 1, block_height_log2, | 143 | Tegra::Texture::UnswizzleTexture(gl_framebuffer_data, input_data, bytes_per_pixel, |
| 137 | 0); | 144 | framebuffer.width, framebuffer.height, 1, |
| 145 | block_height_log2, 0); | ||
| 146 | } | ||
| 138 | 147 | ||
| 139 | glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); | 148 | glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); |
| 140 | glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(framebuffer.stride)); | 149 | glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(framebuffer.stride)); |
diff --git a/src/video_core/renderer_opengl/present/layer.h b/src/video_core/renderer_opengl/present/layer.h index ef1055abf..5b15b730f 100644 --- a/src/video_core/renderer_opengl/present/layer.h +++ b/src/video_core/renderer_opengl/present/layer.h | |||
| @@ -13,6 +13,8 @@ namespace Layout { | |||
| 13 | struct FramebufferLayout; | 13 | struct FramebufferLayout; |
| 14 | } | 14 | } |
| 15 | 15 | ||
| 16 | struct PresentFilters; | ||
| 17 | |||
| 16 | namespace Service::android { | 18 | namespace Service::android { |
| 17 | enum class PixelFormat : u32; | 19 | enum class PixelFormat : u32; |
| 18 | }; | 20 | }; |
| @@ -44,14 +46,15 @@ struct ScreenRectVertex; | |||
| 44 | 46 | ||
| 45 | class Layer { | 47 | class Layer { |
| 46 | public: | 48 | public: |
| 47 | explicit Layer(RasterizerOpenGL& rasterizer, Tegra::MaxwellDeviceMemoryManager& device_memory); | 49 | explicit Layer(RasterizerOpenGL& rasterizer, Tegra::MaxwellDeviceMemoryManager& device_memory, |
| 50 | const PresentFilters& filters); | ||
| 48 | ~Layer(); | 51 | ~Layer(); |
| 49 | 52 | ||
| 50 | GLuint ConfigureDraw(std::array<GLfloat, 3 * 2>& out_matrix, | 53 | GLuint ConfigureDraw(std::array<GLfloat, 3 * 2>& out_matrix, |
| 51 | std::array<ScreenRectVertex, 4>& out_vertices, | 54 | std::array<ScreenRectVertex, 4>& out_vertices, |
| 52 | ProgramManager& program_manager, | 55 | ProgramManager& program_manager, |
| 53 | const Tegra::FramebufferConfig& framebuffer, | 56 | const Tegra::FramebufferConfig& framebuffer, |
| 54 | const Layout::FramebufferLayout& layout); | 57 | const Layout::FramebufferLayout& layout, bool invert_y); |
| 55 | 58 | ||
| 56 | private: | 59 | private: |
| 57 | /// Loads framebuffer from emulated memory into the active OpenGL texture. | 60 | /// Loads framebuffer from emulated memory into the active OpenGL texture. |
| @@ -65,6 +68,7 @@ private: | |||
| 65 | private: | 68 | private: |
| 66 | RasterizerOpenGL& rasterizer; | 69 | RasterizerOpenGL& rasterizer; |
| 67 | Tegra::MaxwellDeviceMemoryManager& device_memory; | 70 | Tegra::MaxwellDeviceMemoryManager& device_memory; |
| 71 | const PresentFilters& filters; | ||
| 68 | 72 | ||
| 69 | /// OpenGL framebuffer data | 73 | /// OpenGL framebuffer data |
| 70 | std::vector<u8> gl_framebuffer_data; | 74 | std::vector<u8> gl_framebuffer_data; |
diff --git a/src/video_core/renderer_opengl/present/window_adapt_pass.cpp b/src/video_core/renderer_opengl/present/window_adapt_pass.cpp index 4d681606b..d8b6a11cb 100644 --- a/src/video_core/renderer_opengl/present/window_adapt_pass.cpp +++ b/src/video_core/renderer_opengl/present/window_adapt_pass.cpp | |||
| @@ -37,7 +37,7 @@ WindowAdaptPass::~WindowAdaptPass() = default; | |||
| 37 | 37 | ||
| 38 | void WindowAdaptPass::DrawToFramebuffer(ProgramManager& program_manager, std::list<Layer>& layers, | 38 | void WindowAdaptPass::DrawToFramebuffer(ProgramManager& program_manager, std::list<Layer>& layers, |
| 39 | std::span<const Tegra::FramebufferConfig> framebuffers, | 39 | std::span<const Tegra::FramebufferConfig> framebuffers, |
| 40 | const Layout::FramebufferLayout& layout) { | 40 | const Layout::FramebufferLayout& layout, bool invert_y) { |
| 41 | GLint old_read_fb; | 41 | GLint old_read_fb; |
| 42 | GLint old_draw_fb; | 42 | GLint old_draw_fb; |
| 43 | glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb); | 43 | glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb); |
| @@ -51,7 +51,7 @@ void WindowAdaptPass::DrawToFramebuffer(ProgramManager& program_manager, std::li | |||
| 51 | auto layer_it = layers.begin(); | 51 | auto layer_it = layers.begin(); |
| 52 | for (size_t i = 0; i < layer_count; i++) { | 52 | for (size_t i = 0; i < layer_count; i++) { |
| 53 | textures[i] = layer_it->ConfigureDraw(matrices[i], vertices[i], program_manager, | 53 | textures[i] = layer_it->ConfigureDraw(matrices[i], vertices[i], program_manager, |
| 54 | framebuffers[i], layout); | 54 | framebuffers[i], layout, invert_y); |
| 55 | layer_it++; | 55 | layer_it++; |
| 56 | } | 56 | } |
| 57 | 57 | ||
| @@ -92,6 +92,21 @@ void WindowAdaptPass::DrawToFramebuffer(ProgramManager& program_manager, std::li | |||
| 92 | glClear(GL_COLOR_BUFFER_BIT); | 92 | glClear(GL_COLOR_BUFFER_BIT); |
| 93 | 93 | ||
| 94 | for (size_t i = 0; i < layer_count; i++) { | 94 | for (size_t i = 0; i < layer_count; i++) { |
| 95 | switch (framebuffers[i].blending) { | ||
| 96 | case Tegra::BlendMode::Opaque: | ||
| 97 | default: | ||
| 98 | glDisablei(GL_BLEND, 0); | ||
| 99 | break; | ||
| 100 | case Tegra::BlendMode::Premultiplied: | ||
| 101 | glEnablei(GL_BLEND, 0); | ||
| 102 | glBlendFuncSeparatei(0, GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO); | ||
| 103 | break; | ||
| 104 | case Tegra::BlendMode::Coverage: | ||
| 105 | glEnablei(GL_BLEND, 0); | ||
| 106 | glBlendFuncSeparatei(0, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO); | ||
| 107 | break; | ||
| 108 | } | ||
| 109 | |||
| 95 | glBindTextureUnit(0, textures[i]); | 110 | glBindTextureUnit(0, textures[i]); |
| 96 | glProgramUniformMatrix3x2fv(vert.handle, ModelViewMatrixLocation, 1, GL_FALSE, | 111 | glProgramUniformMatrix3x2fv(vert.handle, ModelViewMatrixLocation, 1, GL_FALSE, |
| 97 | matrices[i].data()); | 112 | matrices[i].data()); |
diff --git a/src/video_core/renderer_opengl/present/window_adapt_pass.h b/src/video_core/renderer_opengl/present/window_adapt_pass.h index 00975a9c6..0a8bcef2f 100644 --- a/src/video_core/renderer_opengl/present/window_adapt_pass.h +++ b/src/video_core/renderer_opengl/present/window_adapt_pass.h | |||
| @@ -31,7 +31,7 @@ public: | |||
| 31 | 31 | ||
| 32 | void DrawToFramebuffer(ProgramManager& program_manager, std::list<Layer>& layers, | 32 | void DrawToFramebuffer(ProgramManager& program_manager, std::list<Layer>& layers, |
| 33 | std::span<const Tegra::FramebufferConfig> framebuffers, | 33 | std::span<const Tegra::FramebufferConfig> framebuffers, |
| 34 | const Layout::FramebufferLayout& layout); | 34 | const Layout::FramebufferLayout& layout, bool invert_y); |
| 35 | 35 | ||
| 36 | private: | 36 | private: |
| 37 | const Device& device; | 37 | const Device& device; |
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index e33a32592..5fb54635d 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp | |||
| @@ -16,6 +16,8 @@ | |||
| 16 | #include "core/core_timing.h" | 16 | #include "core/core_timing.h" |
| 17 | #include "core/frontend/emu_window.h" | 17 | #include "core/frontend/emu_window.h" |
| 18 | #include "core/telemetry_session.h" | 18 | #include "core/telemetry_session.h" |
| 19 | #include "video_core/capture.h" | ||
| 20 | #include "video_core/present.h" | ||
| 19 | #include "video_core/renderer_opengl/gl_blit_screen.h" | 21 | #include "video_core/renderer_opengl/gl_blit_screen.h" |
| 20 | #include "video_core/renderer_opengl/gl_rasterizer.h" | 22 | #include "video_core/renderer_opengl/gl_rasterizer.h" |
| 21 | #include "video_core/renderer_opengl/gl_shader_manager.h" | 23 | #include "video_core/renderer_opengl/gl_shader_manager.h" |
| @@ -120,7 +122,15 @@ RendererOpenGL::RendererOpenGL(Core::TelemetrySession& telemetry_session_, | |||
| 120 | glEnableClientState(GL_ELEMENT_ARRAY_UNIFIED_NV); | 122 | glEnableClientState(GL_ELEMENT_ARRAY_UNIFIED_NV); |
| 121 | } | 123 | } |
| 122 | blit_screen = std::make_unique<BlitScreen>(rasterizer, device_memory, state_tracker, | 124 | blit_screen = std::make_unique<BlitScreen>(rasterizer, device_memory, state_tracker, |
| 123 | program_manager, device); | 125 | program_manager, device, PresentFiltersForDisplay); |
| 126 | blit_applet = | ||
| 127 | std::make_unique<BlitScreen>(rasterizer, device_memory, state_tracker, program_manager, | ||
| 128 | device, PresentFiltersForAppletCapture); | ||
| 129 | capture_framebuffer.Create(); | ||
| 130 | capture_renderbuffer.Create(); | ||
| 131 | glBindRenderbuffer(GL_RENDERBUFFER, capture_renderbuffer.handle); | ||
| 132 | glRenderbufferStorage(GL_RENDERBUFFER, GL_SRGB8, VideoCore::Capture::LinearWidth, | ||
| 133 | VideoCore::Capture::LinearHeight); | ||
| 124 | } | 134 | } |
| 125 | 135 | ||
| 126 | RendererOpenGL::~RendererOpenGL() = default; | 136 | RendererOpenGL::~RendererOpenGL() = default; |
| @@ -130,10 +140,11 @@ void RendererOpenGL::Composite(std::span<const Tegra::FramebufferConfig> framebu | |||
| 130 | return; | 140 | return; |
| 131 | } | 141 | } |
| 132 | 142 | ||
| 143 | RenderAppletCaptureLayer(framebuffers); | ||
| 133 | RenderScreenshot(framebuffers); | 144 | RenderScreenshot(framebuffers); |
| 134 | 145 | ||
| 135 | state_tracker.BindFramebuffer(0); | 146 | state_tracker.BindFramebuffer(0); |
| 136 | blit_screen->DrawScreen(framebuffers, emu_window.GetFramebufferLayout()); | 147 | blit_screen->DrawScreen(framebuffers, emu_window.GetFramebufferLayout(), false); |
| 137 | 148 | ||
| 138 | ++m_current_frame; | 149 | ++m_current_frame; |
| 139 | 150 | ||
| @@ -159,11 +170,8 @@ void RendererOpenGL::AddTelemetryFields() { | |||
| 159 | telemetry_session.AddField(user_system, "GPU_OpenGL_Version", std::string(gl_version)); | 170 | telemetry_session.AddField(user_system, "GPU_OpenGL_Version", std::string(gl_version)); |
| 160 | } | 171 | } |
| 161 | 172 | ||
| 162 | void RendererOpenGL::RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers) { | 173 | void RendererOpenGL::RenderToBuffer(std::span<const Tegra::FramebufferConfig> framebuffers, |
| 163 | if (!renderer_settings.screenshot_requested) { | 174 | const Layout::FramebufferLayout& layout, void* dst) { |
| 164 | return; | ||
| 165 | } | ||
| 166 | |||
| 167 | GLint old_read_fb; | 175 | GLint old_read_fb; |
| 168 | GLint old_draw_fb; | 176 | GLint old_draw_fb; |
| 169 | glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb); | 177 | glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb); |
| @@ -173,29 +181,86 @@ void RendererOpenGL::RenderScreenshot(std::span<const Tegra::FramebufferConfig> | |||
| 173 | screenshot_framebuffer.Create(); | 181 | screenshot_framebuffer.Create(); |
| 174 | glBindFramebuffer(GL_FRAMEBUFFER, screenshot_framebuffer.handle); | 182 | glBindFramebuffer(GL_FRAMEBUFFER, screenshot_framebuffer.handle); |
| 175 | 183 | ||
| 176 | const Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout}; | ||
| 177 | |||
| 178 | GLuint renderbuffer; | 184 | GLuint renderbuffer; |
| 179 | glGenRenderbuffers(1, &renderbuffer); | 185 | glGenRenderbuffers(1, &renderbuffer); |
| 180 | glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer); | 186 | glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer); |
| 181 | glRenderbufferStorage(GL_RENDERBUFFER, GL_SRGB8, layout.width, layout.height); | 187 | glRenderbufferStorage(GL_RENDERBUFFER, GL_SRGB8, layout.width, layout.height); |
| 182 | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer); | 188 | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer); |
| 183 | 189 | ||
| 184 | blit_screen->DrawScreen(framebuffers, layout); | 190 | blit_screen->DrawScreen(framebuffers, layout, false); |
| 185 | 191 | ||
| 186 | glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); | 192 | glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); |
| 187 | glPixelStorei(GL_PACK_ROW_LENGTH, 0); | 193 | glPixelStorei(GL_PACK_ROW_LENGTH, 0); |
| 188 | glReadPixels(0, 0, layout.width, layout.height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, | 194 | glReadPixels(0, 0, layout.width, layout.height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, dst); |
| 189 | renderer_settings.screenshot_bits); | ||
| 190 | 195 | ||
| 191 | screenshot_framebuffer.Release(); | 196 | screenshot_framebuffer.Release(); |
| 192 | glDeleteRenderbuffers(1, &renderbuffer); | 197 | glDeleteRenderbuffers(1, &renderbuffer); |
| 193 | 198 | ||
| 194 | glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb); | 199 | glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb); |
| 195 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb); | 200 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb); |
| 201 | } | ||
| 202 | |||
| 203 | void RendererOpenGL::RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers) { | ||
| 204 | if (!renderer_settings.screenshot_requested) { | ||
| 205 | return; | ||
| 206 | } | ||
| 207 | |||
| 208 | RenderToBuffer(framebuffers, renderer_settings.screenshot_framebuffer_layout, | ||
| 209 | renderer_settings.screenshot_bits); | ||
| 196 | 210 | ||
| 197 | renderer_settings.screenshot_complete_callback(true); | 211 | renderer_settings.screenshot_complete_callback(true); |
| 198 | renderer_settings.screenshot_requested = false; | 212 | renderer_settings.screenshot_requested = false; |
| 199 | } | 213 | } |
| 200 | 214 | ||
| 215 | void RendererOpenGL::RenderAppletCaptureLayer( | ||
| 216 | std::span<const Tegra::FramebufferConfig> framebuffers) { | ||
| 217 | GLint old_read_fb; | ||
| 218 | GLint old_draw_fb; | ||
| 219 | glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb); | ||
| 220 | glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb); | ||
| 221 | |||
| 222 | glBindFramebuffer(GL_FRAMEBUFFER, capture_framebuffer.handle); | ||
| 223 | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, | ||
| 224 | capture_renderbuffer.handle); | ||
| 225 | |||
| 226 | blit_applet->DrawScreen(framebuffers, VideoCore::Capture::Layout, true); | ||
| 227 | |||
| 228 | glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb); | ||
| 229 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb); | ||
| 230 | } | ||
| 231 | |||
| 232 | std::vector<u8> RendererOpenGL::GetAppletCaptureBuffer() { | ||
| 233 | using namespace VideoCore::Capture; | ||
| 234 | |||
| 235 | std::vector<u8> linear(TiledSize); | ||
| 236 | std::vector<u8> out(TiledSize); | ||
| 237 | |||
| 238 | GLint old_read_fb; | ||
| 239 | GLint old_draw_fb; | ||
| 240 | GLint old_pixel_pack_buffer; | ||
| 241 | GLint old_pack_row_length; | ||
| 242 | glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb); | ||
| 243 | glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb); | ||
| 244 | glGetIntegerv(GL_PIXEL_PACK_BUFFER_BINDING, &old_pixel_pack_buffer); | ||
| 245 | glGetIntegerv(GL_PACK_ROW_LENGTH, &old_pack_row_length); | ||
| 246 | |||
| 247 | glBindFramebuffer(GL_FRAMEBUFFER, capture_framebuffer.handle); | ||
| 248 | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, | ||
| 249 | capture_renderbuffer.handle); | ||
| 250 | glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); | ||
| 251 | glPixelStorei(GL_PACK_ROW_LENGTH, 0); | ||
| 252 | glReadPixels(0, 0, LinearWidth, LinearHeight, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, | ||
| 253 | linear.data()); | ||
| 254 | |||
| 255 | glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb); | ||
| 256 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb); | ||
| 257 | glBindBuffer(GL_PIXEL_PACK_BUFFER, old_pixel_pack_buffer); | ||
| 258 | glPixelStorei(GL_PACK_ROW_LENGTH, old_pack_row_length); | ||
| 259 | |||
| 260 | Tegra::Texture::SwizzleTexture(out, linear, BytesPerPixel, LinearWidth, LinearHeight, | ||
| 261 | LinearDepth, BlockHeight, BlockDepth); | ||
| 262 | |||
| 263 | return out; | ||
| 264 | } | ||
| 265 | |||
| 201 | } // namespace OpenGL | 266 | } // namespace OpenGL |
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index c4625c96e..60d6a1477 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h | |||
| @@ -42,6 +42,8 @@ public: | |||
| 42 | 42 | ||
| 43 | void Composite(std::span<const Tegra::FramebufferConfig> framebuffers) override; | 43 | void Composite(std::span<const Tegra::FramebufferConfig> framebuffers) override; |
| 44 | 44 | ||
| 45 | std::vector<u8> GetAppletCaptureBuffer() override; | ||
| 46 | |||
| 45 | VideoCore::RasterizerInterface* ReadRasterizer() override { | 47 | VideoCore::RasterizerInterface* ReadRasterizer() override { |
| 46 | return &rasterizer; | 48 | return &rasterizer; |
| 47 | } | 49 | } |
| @@ -52,7 +54,11 @@ public: | |||
| 52 | 54 | ||
| 53 | private: | 55 | private: |
| 54 | void AddTelemetryFields(); | 56 | void AddTelemetryFields(); |
| 57 | |||
| 58 | void RenderToBuffer(std::span<const Tegra::FramebufferConfig> framebuffers, | ||
| 59 | const Layout::FramebufferLayout& layout, void* dst); | ||
| 55 | void RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers); | 60 | void RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers); |
| 61 | void RenderAppletCaptureLayer(std::span<const Tegra::FramebufferConfig> framebuffers); | ||
| 56 | 62 | ||
| 57 | Core::TelemetrySession& telemetry_session; | 63 | Core::TelemetrySession& telemetry_session; |
| 58 | Core::Frontend::EmuWindow& emu_window; | 64 | Core::Frontend::EmuWindow& emu_window; |
| @@ -64,8 +70,11 @@ private: | |||
| 64 | ProgramManager program_manager; | 70 | ProgramManager program_manager; |
| 65 | RasterizerOpenGL rasterizer; | 71 | RasterizerOpenGL rasterizer; |
| 66 | OGLFramebuffer screenshot_framebuffer; | 72 | OGLFramebuffer screenshot_framebuffer; |
| 73 | OGLFramebuffer capture_framebuffer; | ||
| 74 | OGLRenderbuffer capture_renderbuffer; | ||
| 67 | 75 | ||
| 68 | std::unique_ptr<BlitScreen> blit_screen; | 76 | std::unique_ptr<BlitScreen> blit_screen; |
| 77 | std::unique_ptr<BlitScreen> blit_applet; | ||
| 69 | }; | 78 | }; |
| 70 | 79 | ||
| 71 | } // namespace OpenGL | 80 | } // namespace OpenGL |
diff --git a/src/video_core/renderer_vulkan/present/layer.cpp b/src/video_core/renderer_vulkan/present/layer.cpp index cfc04be44..3847a9a13 100644 --- a/src/video_core/renderer_vulkan/present/layer.cpp +++ b/src/video_core/renderer_vulkan/present/layer.cpp | |||
| @@ -1,6 +1,7 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include "video_core/present.h" | ||
| 4 | #include "video_core/renderer_vulkan/vk_rasterizer.h" | 5 | #include "video_core/renderer_vulkan/vk_rasterizer.h" |
| 5 | 6 | ||
| 6 | #include "common/settings.h" | 7 | #include "common/settings.h" |
| @@ -48,12 +49,12 @@ VkFormat GetFormat(const Tegra::FramebufferConfig& framebuffer) { | |||
| 48 | 49 | ||
| 49 | Layer::Layer(const Device& device_, MemoryAllocator& memory_allocator_, Scheduler& scheduler_, | 50 | Layer::Layer(const Device& device_, MemoryAllocator& memory_allocator_, Scheduler& scheduler_, |
| 50 | Tegra::MaxwellDeviceMemoryManager& device_memory_, size_t image_count_, | 51 | Tegra::MaxwellDeviceMemoryManager& device_memory_, size_t image_count_, |
| 51 | VkExtent2D output_size, VkDescriptorSetLayout layout) | 52 | VkExtent2D output_size, VkDescriptorSetLayout layout, const PresentFilters& filters_) |
| 52 | : device(device_), memory_allocator(memory_allocator_), scheduler(scheduler_), | 53 | : device(device_), memory_allocator(memory_allocator_), scheduler(scheduler_), |
| 53 | device_memory(device_memory_), image_count(image_count_) { | 54 | device_memory(device_memory_), filters(filters_), image_count(image_count_) { |
| 54 | CreateDescriptorPool(); | 55 | CreateDescriptorPool(); |
| 55 | CreateDescriptorSets(layout); | 56 | CreateDescriptorSets(layout); |
| 56 | if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) { | 57 | if (filters.get_scaling_filter() == Settings::ScalingFilter::Fsr) { |
| 57 | CreateFSR(output_size); | 58 | CreateFSR(output_size); |
| 58 | } | 59 | } |
| 59 | } | 60 | } |
| @@ -171,11 +172,11 @@ void Layer::RefreshResources(const Tegra::FramebufferConfig& framebuffer) { | |||
| 171 | } | 172 | } |
| 172 | 173 | ||
| 173 | void Layer::SetAntiAliasPass() { | 174 | void Layer::SetAntiAliasPass() { |
| 174 | if (anti_alias && anti_alias_setting == Settings::values.anti_aliasing.GetValue()) { | 175 | if (anti_alias && anti_alias_setting == filters.get_anti_aliasing()) { |
| 175 | return; | 176 | return; |
| 176 | } | 177 | } |
| 177 | 178 | ||
| 178 | anti_alias_setting = Settings::values.anti_aliasing.GetValue(); | 179 | anti_alias_setting = filters.get_anti_aliasing(); |
| 179 | 180 | ||
| 180 | const VkExtent2D render_area{ | 181 | const VkExtent2D render_area{ |
| 181 | .width = Settings::values.resolution_info.ScaleUp(raw_width), | 182 | .width = Settings::values.resolution_info.ScaleUp(raw_width), |
| @@ -270,9 +271,11 @@ void Layer::UpdateRawImage(const Tegra::FramebufferConfig& framebuffer, size_t i | |||
| 270 | const u64 linear_size{GetSizeInBytes(framebuffer)}; | 271 | const u64 linear_size{GetSizeInBytes(framebuffer)}; |
| 271 | const u64 tiled_size{Tegra::Texture::CalculateSize( | 272 | const u64 tiled_size{Tegra::Texture::CalculateSize( |
| 272 | true, bytes_per_pixel, framebuffer.stride, framebuffer.height, 1, block_height_log2, 0)}; | 273 | true, bytes_per_pixel, framebuffer.stride, framebuffer.height, 1, block_height_log2, 0)}; |
| 273 | Tegra::Texture::UnswizzleTexture( | 274 | if (host_ptr) { |
| 274 | mapped_span.subspan(image_offset, linear_size), std::span(host_ptr, tiled_size), | 275 | Tegra::Texture::UnswizzleTexture( |
| 275 | bytes_per_pixel, framebuffer.width, framebuffer.height, 1, block_height_log2, 0); | 276 | mapped_span.subspan(image_offset, linear_size), std::span(host_ptr, tiled_size), |
| 277 | bytes_per_pixel, framebuffer.width, framebuffer.height, 1, block_height_log2, 0); | ||
| 278 | } | ||
| 276 | 279 | ||
| 277 | const VkBufferImageCopy copy{ | 280 | const VkBufferImageCopy copy{ |
| 278 | .bufferOffset = image_offset, | 281 | .bufferOffset = image_offset, |
diff --git a/src/video_core/renderer_vulkan/present/layer.h b/src/video_core/renderer_vulkan/present/layer.h index 88d43fc5f..f5effdcd7 100644 --- a/src/video_core/renderer_vulkan/present/layer.h +++ b/src/video_core/renderer_vulkan/present/layer.h | |||
| @@ -11,6 +11,8 @@ namespace Layout { | |||
| 11 | struct FramebufferLayout; | 11 | struct FramebufferLayout; |
| 12 | } | 12 | } |
| 13 | 13 | ||
| 14 | struct PresentFilters; | ||
| 15 | |||
| 14 | namespace Tegra { | 16 | namespace Tegra { |
| 15 | struct FramebufferConfig; | 17 | struct FramebufferConfig; |
| 16 | } | 18 | } |
| @@ -37,7 +39,8 @@ class Layer final { | |||
| 37 | public: | 39 | public: |
| 38 | explicit Layer(const Device& device, MemoryAllocator& memory_allocator, Scheduler& scheduler, | 40 | explicit Layer(const Device& device, MemoryAllocator& memory_allocator, Scheduler& scheduler, |
| 39 | Tegra::MaxwellDeviceMemoryManager& device_memory, size_t image_count, | 41 | Tegra::MaxwellDeviceMemoryManager& device_memory, size_t image_count, |
| 40 | VkExtent2D output_size, VkDescriptorSetLayout layout); | 42 | VkExtent2D output_size, VkDescriptorSetLayout layout, |
| 43 | const PresentFilters& filters); | ||
| 41 | ~Layer(); | 44 | ~Layer(); |
| 42 | 45 | ||
| 43 | void ConfigureDraw(PresentPushConstants* out_push_constants, | 46 | void ConfigureDraw(PresentPushConstants* out_push_constants, |
| @@ -71,6 +74,7 @@ private: | |||
| 71 | MemoryAllocator& memory_allocator; | 74 | MemoryAllocator& memory_allocator; |
| 72 | Scheduler& scheduler; | 75 | Scheduler& scheduler; |
| 73 | Tegra::MaxwellDeviceMemoryManager& device_memory; | 76 | Tegra::MaxwellDeviceMemoryManager& device_memory; |
| 77 | const PresentFilters& filters; | ||
| 74 | const size_t image_count{}; | 78 | const size_t image_count{}; |
| 75 | vk::DescriptorPool descriptor_pool{}; | 79 | vk::DescriptorPool descriptor_pool{}; |
| 76 | vk::DescriptorSets descriptor_sets{}; | 80 | vk::DescriptorSets descriptor_sets{}; |
diff --git a/src/video_core/renderer_vulkan/present/util.cpp b/src/video_core/renderer_vulkan/present/util.cpp index 6ee16595d..7f27c7c1b 100644 --- a/src/video_core/renderer_vulkan/present/util.cpp +++ b/src/video_core/renderer_vulkan/present/util.cpp | |||
| @@ -362,10 +362,10 @@ vk::PipelineLayout CreateWrappedPipelineLayout(const Device& device, | |||
| 362 | }); | 362 | }); |
| 363 | } | 363 | } |
| 364 | 364 | ||
| 365 | vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass, | 365 | static vk::Pipeline CreateWrappedPipelineImpl( |
| 366 | vk::PipelineLayout& layout, | 366 | const Device& device, vk::RenderPass& renderpass, vk::PipelineLayout& layout, |
| 367 | std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders, | 367 | std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders, |
| 368 | bool enable_blending) { | 368 | VkPipelineColorBlendAttachmentState blending) { |
| 369 | const std::array<VkPipelineShaderStageCreateInfo, 2> shader_stages{{ | 369 | const std::array<VkPipelineShaderStageCreateInfo, 2> shader_stages{{ |
| 370 | { | 370 | { |
| 371 | .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, | 371 | .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, |
| @@ -443,30 +443,6 @@ vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderp | |||
| 443 | .alphaToOneEnable = VK_FALSE, | 443 | .alphaToOneEnable = VK_FALSE, |
| 444 | }; | 444 | }; |
| 445 | 445 | ||
| 446 | constexpr VkPipelineColorBlendAttachmentState color_blend_attachment_disabled{ | ||
| 447 | .blendEnable = VK_FALSE, | ||
| 448 | .srcColorBlendFactor = VK_BLEND_FACTOR_ZERO, | ||
| 449 | .dstColorBlendFactor = VK_BLEND_FACTOR_ZERO, | ||
| 450 | .colorBlendOp = VK_BLEND_OP_ADD, | ||
| 451 | .srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, | ||
| 452 | .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, | ||
| 453 | .alphaBlendOp = VK_BLEND_OP_ADD, | ||
| 454 | .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | | ||
| 455 | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, | ||
| 456 | }; | ||
| 457 | |||
| 458 | constexpr VkPipelineColorBlendAttachmentState color_blend_attachment_enabled{ | ||
| 459 | .blendEnable = VK_TRUE, | ||
| 460 | .srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA, | ||
| 461 | .dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, | ||
| 462 | .colorBlendOp = VK_BLEND_OP_ADD, | ||
| 463 | .srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE, | ||
| 464 | .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, | ||
| 465 | .alphaBlendOp = VK_BLEND_OP_ADD, | ||
| 466 | .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | | ||
| 467 | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, | ||
| 468 | }; | ||
| 469 | |||
| 470 | const VkPipelineColorBlendStateCreateInfo color_blend_ci{ | 446 | const VkPipelineColorBlendStateCreateInfo color_blend_ci{ |
| 471 | .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, | 447 | .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, |
| 472 | .pNext = nullptr, | 448 | .pNext = nullptr, |
| @@ -474,8 +450,7 @@ vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderp | |||
| 474 | .logicOpEnable = VK_FALSE, | 450 | .logicOpEnable = VK_FALSE, |
| 475 | .logicOp = VK_LOGIC_OP_COPY, | 451 | .logicOp = VK_LOGIC_OP_COPY, |
| 476 | .attachmentCount = 1, | 452 | .attachmentCount = 1, |
| 477 | .pAttachments = | 453 | .pAttachments = &blending, |
| 478 | enable_blending ? &color_blend_attachment_enabled : &color_blend_attachment_disabled, | ||
| 479 | .blendConstants = {0.0f, 0.0f, 0.0f, 0.0f}, | 454 | .blendConstants = {0.0f, 0.0f, 0.0f, 0.0f}, |
| 480 | }; | 455 | }; |
| 481 | 456 | ||
| @@ -515,6 +490,63 @@ vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderp | |||
| 515 | }); | 490 | }); |
| 516 | } | 491 | } |
| 517 | 492 | ||
| 493 | vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass, | ||
| 494 | vk::PipelineLayout& layout, | ||
| 495 | std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders) { | ||
| 496 | constexpr VkPipelineColorBlendAttachmentState color_blend_attachment_disabled{ | ||
| 497 | .blendEnable = VK_FALSE, | ||
| 498 | .srcColorBlendFactor = VK_BLEND_FACTOR_ZERO, | ||
| 499 | .dstColorBlendFactor = VK_BLEND_FACTOR_ZERO, | ||
| 500 | .colorBlendOp = VK_BLEND_OP_ADD, | ||
| 501 | .srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, | ||
| 502 | .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, | ||
| 503 | .alphaBlendOp = VK_BLEND_OP_ADD, | ||
| 504 | .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | | ||
| 505 | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, | ||
| 506 | }; | ||
| 507 | |||
| 508 | return CreateWrappedPipelineImpl(device, renderpass, layout, shaders, | ||
| 509 | color_blend_attachment_disabled); | ||
| 510 | } | ||
| 511 | |||
| 512 | vk::Pipeline CreateWrappedPremultipliedBlendingPipeline( | ||
| 513 | const Device& device, vk::RenderPass& renderpass, vk::PipelineLayout& layout, | ||
| 514 | std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders) { | ||
| 515 | constexpr VkPipelineColorBlendAttachmentState color_blend_attachment_premultiplied{ | ||
| 516 | .blendEnable = VK_TRUE, | ||
| 517 | .srcColorBlendFactor = VK_BLEND_FACTOR_ONE, | ||
| 518 | .dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, | ||
| 519 | .colorBlendOp = VK_BLEND_OP_ADD, | ||
| 520 | .srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE, | ||
| 521 | .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, | ||
| 522 | .alphaBlendOp = VK_BLEND_OP_ADD, | ||
| 523 | .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | | ||
| 524 | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, | ||
| 525 | }; | ||
| 526 | |||
| 527 | return CreateWrappedPipelineImpl(device, renderpass, layout, shaders, | ||
| 528 | color_blend_attachment_premultiplied); | ||
| 529 | } | ||
| 530 | |||
| 531 | vk::Pipeline CreateWrappedCoverageBlendingPipeline( | ||
| 532 | const Device& device, vk::RenderPass& renderpass, vk::PipelineLayout& layout, | ||
| 533 | std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders) { | ||
| 534 | constexpr VkPipelineColorBlendAttachmentState color_blend_attachment_coverage{ | ||
| 535 | .blendEnable = VK_TRUE, | ||
| 536 | .srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA, | ||
| 537 | .dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, | ||
| 538 | .colorBlendOp = VK_BLEND_OP_ADD, | ||
| 539 | .srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE, | ||
| 540 | .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, | ||
| 541 | .alphaBlendOp = VK_BLEND_OP_ADD, | ||
| 542 | .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | | ||
| 543 | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, | ||
| 544 | }; | ||
| 545 | |||
| 546 | return CreateWrappedPipelineImpl(device, renderpass, layout, shaders, | ||
| 547 | color_blend_attachment_coverage); | ||
| 548 | } | ||
| 549 | |||
| 518 | VkWriteDescriptorSet CreateWriteDescriptorSet(std::vector<VkDescriptorImageInfo>& images, | 550 | VkWriteDescriptorSet CreateWriteDescriptorSet(std::vector<VkDescriptorImageInfo>& images, |
| 519 | VkSampler sampler, VkImageView view, | 551 | VkSampler sampler, VkImageView view, |
| 520 | VkDescriptorSet set, u32 binding) { | 552 | VkDescriptorSet set, u32 binding) { |
diff --git a/src/video_core/renderer_vulkan/present/util.h b/src/video_core/renderer_vulkan/present/util.h index 1104aaa15..5b22f0fa8 100644 --- a/src/video_core/renderer_vulkan/present/util.h +++ b/src/video_core/renderer_vulkan/present/util.h | |||
| @@ -42,8 +42,13 @@ vk::PipelineLayout CreateWrappedPipelineLayout(const Device& device, | |||
| 42 | vk::DescriptorSetLayout& layout); | 42 | vk::DescriptorSetLayout& layout); |
| 43 | vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass, | 43 | vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass, |
| 44 | vk::PipelineLayout& layout, | 44 | vk::PipelineLayout& layout, |
| 45 | std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders, | 45 | std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders); |
| 46 | bool enable_blending = false); | 46 | vk::Pipeline CreateWrappedPremultipliedBlendingPipeline( |
| 47 | const Device& device, vk::RenderPass& renderpass, vk::PipelineLayout& layout, | ||
| 48 | std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders); | ||
| 49 | vk::Pipeline CreateWrappedCoverageBlendingPipeline( | ||
| 50 | const Device& device, vk::RenderPass& renderpass, vk::PipelineLayout& layout, | ||
| 51 | std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders); | ||
| 47 | VkWriteDescriptorSet CreateWriteDescriptorSet(std::vector<VkDescriptorImageInfo>& images, | 52 | VkWriteDescriptorSet CreateWriteDescriptorSet(std::vector<VkDescriptorImageInfo>& images, |
| 48 | VkSampler sampler, VkImageView view, | 53 | VkSampler sampler, VkImageView view, |
| 49 | VkDescriptorSet set, u32 binding); | 54 | VkDescriptorSet set, u32 binding); |
diff --git a/src/video_core/renderer_vulkan/present/window_adapt_pass.cpp b/src/video_core/renderer_vulkan/present/window_adapt_pass.cpp index c5db0230d..22ffacf11 100644 --- a/src/video_core/renderer_vulkan/present/window_adapt_pass.cpp +++ b/src/video_core/renderer_vulkan/present/window_adapt_pass.cpp | |||
| @@ -22,7 +22,7 @@ WindowAdaptPass::WindowAdaptPass(const Device& device_, VkFormat frame_format, | |||
| 22 | CreatePipelineLayout(); | 22 | CreatePipelineLayout(); |
| 23 | CreateVertexShader(); | 23 | CreateVertexShader(); |
| 24 | CreateRenderPass(frame_format); | 24 | CreateRenderPass(frame_format); |
| 25 | CreatePipeline(); | 25 | CreatePipelines(); |
| 26 | } | 26 | } |
| 27 | 27 | ||
| 28 | WindowAdaptPass::~WindowAdaptPass() = default; | 28 | WindowAdaptPass::~WindowAdaptPass() = default; |
| @@ -34,7 +34,6 @@ void WindowAdaptPass::Draw(RasterizerVulkan& rasterizer, Scheduler& scheduler, s | |||
| 34 | 34 | ||
| 35 | const VkFramebuffer host_framebuffer{*dst->framebuffer}; | 35 | const VkFramebuffer host_framebuffer{*dst->framebuffer}; |
| 36 | const VkRenderPass renderpass{*render_pass}; | 36 | const VkRenderPass renderpass{*render_pass}; |
| 37 | const VkPipeline graphics_pipeline{*pipeline}; | ||
| 38 | const VkPipelineLayout graphics_pipeline_layout{*pipeline_layout}; | 37 | const VkPipelineLayout graphics_pipeline_layout{*pipeline_layout}; |
| 39 | const VkExtent2D render_area{ | 38 | const VkExtent2D render_area{ |
| 40 | .width = dst->width, | 39 | .width = dst->width, |
| @@ -44,9 +43,23 @@ void WindowAdaptPass::Draw(RasterizerVulkan& rasterizer, Scheduler& scheduler, s | |||
| 44 | const size_t layer_count = configs.size(); | 43 | const size_t layer_count = configs.size(); |
| 45 | std::vector<PresentPushConstants> push_constants(layer_count); | 44 | std::vector<PresentPushConstants> push_constants(layer_count); |
| 46 | std::vector<VkDescriptorSet> descriptor_sets(layer_count); | 45 | std::vector<VkDescriptorSet> descriptor_sets(layer_count); |
| 46 | std::vector<VkPipeline> graphics_pipelines(layer_count); | ||
| 47 | 47 | ||
| 48 | auto layer_it = layers.begin(); | 48 | auto layer_it = layers.begin(); |
| 49 | for (size_t i = 0; i < layer_count; i++) { | 49 | for (size_t i = 0; i < layer_count; i++) { |
| 50 | switch (configs[i].blending) { | ||
| 51 | case Tegra::BlendMode::Opaque: | ||
| 52 | default: | ||
| 53 | graphics_pipelines[i] = *opaque_pipeline; | ||
| 54 | break; | ||
| 55 | case Tegra::BlendMode::Premultiplied: | ||
| 56 | graphics_pipelines[i] = *premultiplied_pipeline; | ||
| 57 | break; | ||
| 58 | case Tegra::BlendMode::Coverage: | ||
| 59 | graphics_pipelines[i] = *coverage_pipeline; | ||
| 60 | break; | ||
| 61 | } | ||
| 62 | |||
| 50 | layer_it->ConfigureDraw(&push_constants[i], &descriptor_sets[i], rasterizer, *sampler, | 63 | layer_it->ConfigureDraw(&push_constants[i], &descriptor_sets[i], rasterizer, *sampler, |
| 51 | image_index, configs[i], layout); | 64 | image_index, configs[i], layout); |
| 52 | layer_it++; | 65 | layer_it++; |
| @@ -77,8 +90,8 @@ void WindowAdaptPass::Draw(RasterizerVulkan& rasterizer, Scheduler& scheduler, s | |||
| 77 | BeginRenderPass(cmdbuf, renderpass, host_framebuffer, render_area); | 90 | BeginRenderPass(cmdbuf, renderpass, host_framebuffer, render_area); |
| 78 | cmdbuf.ClearAttachments({clear_attachment}, {clear_rect}); | 91 | cmdbuf.ClearAttachments({clear_attachment}, {clear_rect}); |
| 79 | 92 | ||
| 80 | cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline); | ||
| 81 | for (size_t i = 0; i < layer_count; i++) { | 93 | for (size_t i = 0; i < layer_count; i++) { |
| 94 | cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipelines[i]); | ||
| 82 | cmdbuf.PushConstants(graphics_pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT, | 95 | cmdbuf.PushConstants(graphics_pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT, |
| 83 | push_constants[i]); | 96 | push_constants[i]); |
| 84 | cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline_layout, 0, | 97 | cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline_layout, 0, |
| @@ -129,9 +142,13 @@ void WindowAdaptPass::CreateRenderPass(VkFormat frame_format) { | |||
| 129 | render_pass = CreateWrappedRenderPass(device, frame_format, VK_IMAGE_LAYOUT_UNDEFINED); | 142 | render_pass = CreateWrappedRenderPass(device, frame_format, VK_IMAGE_LAYOUT_UNDEFINED); |
| 130 | } | 143 | } |
| 131 | 144 | ||
| 132 | void WindowAdaptPass::CreatePipeline() { | 145 | void WindowAdaptPass::CreatePipelines() { |
| 133 | pipeline = CreateWrappedPipeline(device, render_pass, pipeline_layout, | 146 | opaque_pipeline = CreateWrappedPipeline(device, render_pass, pipeline_layout, |
| 134 | std::tie(vertex_shader, fragment_shader), false); | 147 | std::tie(vertex_shader, fragment_shader)); |
| 148 | premultiplied_pipeline = CreateWrappedPremultipliedBlendingPipeline( | ||
| 149 | device, render_pass, pipeline_layout, std::tie(vertex_shader, fragment_shader)); | ||
| 150 | coverage_pipeline = CreateWrappedCoverageBlendingPipeline( | ||
| 151 | device, render_pass, pipeline_layout, std::tie(vertex_shader, fragment_shader)); | ||
| 135 | } | 152 | } |
| 136 | 153 | ||
| 137 | } // namespace Vulkan | 154 | } // namespace Vulkan |
diff --git a/src/video_core/renderer_vulkan/present/window_adapt_pass.h b/src/video_core/renderer_vulkan/present/window_adapt_pass.h index 0e2edfc31..cf667a4fc 100644 --- a/src/video_core/renderer_vulkan/present/window_adapt_pass.h +++ b/src/video_core/renderer_vulkan/present/window_adapt_pass.h | |||
| @@ -42,7 +42,7 @@ private: | |||
| 42 | void CreatePipelineLayout(); | 42 | void CreatePipelineLayout(); |
| 43 | void CreateVertexShader(); | 43 | void CreateVertexShader(); |
| 44 | void CreateRenderPass(VkFormat frame_format); | 44 | void CreateRenderPass(VkFormat frame_format); |
| 45 | void CreatePipeline(); | 45 | void CreatePipelines(); |
| 46 | 46 | ||
| 47 | private: | 47 | private: |
| 48 | const Device& device; | 48 | const Device& device; |
| @@ -52,7 +52,9 @@ private: | |||
| 52 | vk::ShaderModule vertex_shader; | 52 | vk::ShaderModule vertex_shader; |
| 53 | vk::ShaderModule fragment_shader; | 53 | vk::ShaderModule fragment_shader; |
| 54 | vk::RenderPass render_pass; | 54 | vk::RenderPass render_pass; |
| 55 | vk::Pipeline pipeline; | 55 | vk::Pipeline opaque_pipeline; |
| 56 | vk::Pipeline premultiplied_pipeline; | ||
| 57 | vk::Pipeline coverage_pipeline; | ||
| 56 | }; | 58 | }; |
| 57 | 59 | ||
| 58 | } // namespace Vulkan | 60 | } // namespace Vulkan |
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 48a105327..d50417116 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp | |||
| @@ -19,7 +19,9 @@ | |||
| 19 | #include "core/core_timing.h" | 19 | #include "core/core_timing.h" |
| 20 | #include "core/frontend/graphics_context.h" | 20 | #include "core/frontend/graphics_context.h" |
| 21 | #include "core/telemetry_session.h" | 21 | #include "core/telemetry_session.h" |
| 22 | #include "video_core/capture.h" | ||
| 22 | #include "video_core/gpu.h" | 23 | #include "video_core/gpu.h" |
| 24 | #include "video_core/present.h" | ||
| 23 | #include "video_core/renderer_vulkan/present/util.h" | 25 | #include "video_core/renderer_vulkan/present/util.h" |
| 24 | #include "video_core/renderer_vulkan/renderer_vulkan.h" | 26 | #include "video_core/renderer_vulkan/renderer_vulkan.h" |
| 25 | #include "video_core/renderer_vulkan/vk_blit_screen.h" | 27 | #include "video_core/renderer_vulkan/vk_blit_screen.h" |
| @@ -38,6 +40,20 @@ | |||
| 38 | 40 | ||
| 39 | namespace Vulkan { | 41 | namespace Vulkan { |
| 40 | namespace { | 42 | namespace { |
| 43 | |||
| 44 | constexpr VkExtent2D CaptureImageSize{ | ||
| 45 | .width = VideoCore::Capture::LinearWidth, | ||
| 46 | .height = VideoCore::Capture::LinearHeight, | ||
| 47 | }; | ||
| 48 | |||
| 49 | constexpr VkExtent3D CaptureImageExtent{ | ||
| 50 | .width = VideoCore::Capture::LinearWidth, | ||
| 51 | .height = VideoCore::Capture::LinearHeight, | ||
| 52 | .depth = VideoCore::Capture::LinearDepth, | ||
| 53 | }; | ||
| 54 | |||
| 55 | constexpr VkFormat CaptureFormat = VK_FORMAT_A8B8G8R8_UNORM_PACK32; | ||
| 56 | |||
| 41 | std::string GetReadableVersion(u32 version) { | 57 | std::string GetReadableVersion(u32 version) { |
| 42 | return fmt::format("{}.{}.{}", VK_VERSION_MAJOR(version), VK_VERSION_MINOR(version), | 58 | return fmt::format("{}.{}.{}", VK_VERSION_MAJOR(version), VK_VERSION_MINOR(version), |
| 43 | VK_VERSION_PATCH(version)); | 59 | VK_VERSION_PATCH(version)); |
| @@ -99,10 +115,15 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_, | |||
| 99 | render_window.GetFramebufferLayout().height), | 115 | render_window.GetFramebufferLayout().height), |
| 100 | present_manager(instance, render_window, device, memory_allocator, scheduler, swapchain, | 116 | present_manager(instance, render_window, device, memory_allocator, scheduler, swapchain, |
| 101 | surface), | 117 | surface), |
| 102 | blit_swapchain(device_memory, device, memory_allocator, present_manager, scheduler), | 118 | blit_swapchain(device_memory, device, memory_allocator, present_manager, scheduler, |
| 103 | blit_screenshot(device_memory, device, memory_allocator, present_manager, scheduler), | 119 | PresentFiltersForDisplay), |
| 120 | blit_capture(device_memory, device, memory_allocator, present_manager, scheduler, | ||
| 121 | PresentFiltersForDisplay), | ||
| 122 | blit_applet(device_memory, device, memory_allocator, present_manager, scheduler, | ||
| 123 | PresentFiltersForAppletCapture), | ||
| 104 | rasterizer(render_window, gpu, device_memory, device, memory_allocator, state_tracker, | 124 | rasterizer(render_window, gpu, device_memory, device, memory_allocator, state_tracker, |
| 105 | scheduler) { | 125 | scheduler), |
| 126 | applet_frame() { | ||
| 106 | if (Settings::values.renderer_force_max_clock.GetValue() && device.ShouldBoostClocks()) { | 127 | if (Settings::values.renderer_force_max_clock.GetValue() && device.ShouldBoostClocks()) { |
| 107 | turbo_mode.emplace(instance, dld); | 128 | turbo_mode.emplace(instance, dld); |
| 108 | scheduler.RegisterOnSubmit([this] { turbo_mode->QueueSubmitted(); }); | 129 | scheduler.RegisterOnSubmit([this] { turbo_mode->QueueSubmitted(); }); |
| @@ -125,6 +146,8 @@ void RendererVulkan::Composite(std::span<const Tegra::FramebufferConfig> framebu | |||
| 125 | 146 | ||
| 126 | SCOPE_EXIT({ render_window.OnFrameDisplayed(); }); | 147 | SCOPE_EXIT({ render_window.OnFrameDisplayed(); }); |
| 127 | 148 | ||
| 149 | RenderAppletCaptureLayer(framebuffers); | ||
| 150 | |||
| 128 | if (!render_window.IsShown()) { | 151 | if (!render_window.IsShown()) { |
| 129 | return; | 152 | return; |
| 130 | } | 153 | } |
| @@ -167,30 +190,20 @@ void RendererVulkan::Report() const { | |||
| 167 | telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions); | 190 | telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions); |
| 168 | } | 191 | } |
| 169 | 192 | ||
| 170 | void Vulkan::RendererVulkan::RenderScreenshot( | 193 | vk::Buffer RendererVulkan::RenderToBuffer(std::span<const Tegra::FramebufferConfig> framebuffers, |
| 171 | std::span<const Tegra::FramebufferConfig> framebuffers) { | 194 | const Layout::FramebufferLayout& layout, VkFormat format, |
| 172 | if (!renderer_settings.screenshot_requested) { | 195 | VkDeviceSize buffer_size) { |
| 173 | return; | ||
| 174 | } | ||
| 175 | |||
| 176 | constexpr VkFormat ScreenshotFormat{VK_FORMAT_B8G8R8A8_UNORM}; | ||
| 177 | const Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout}; | ||
| 178 | |||
| 179 | auto frame = [&]() { | 196 | auto frame = [&]() { |
| 180 | Frame f{}; | 197 | Frame f{}; |
| 181 | f.image = CreateWrappedImage(memory_allocator, VkExtent2D{layout.width, layout.height}, | 198 | f.image = |
| 182 | ScreenshotFormat); | 199 | CreateWrappedImage(memory_allocator, VkExtent2D{layout.width, layout.height}, format); |
| 183 | f.image_view = CreateWrappedImageView(device, f.image, ScreenshotFormat); | 200 | f.image_view = CreateWrappedImageView(device, f.image, format); |
| 184 | f.framebuffer = blit_screenshot.CreateFramebuffer(layout, *f.image_view, ScreenshotFormat); | 201 | f.framebuffer = blit_capture.CreateFramebuffer(layout, *f.image_view, format); |
| 185 | return f; | 202 | return f; |
| 186 | }(); | 203 | }(); |
| 187 | 204 | ||
| 188 | blit_screenshot.DrawToFrame(rasterizer, &frame, framebuffers, layout, 1, | 205 | auto dst_buffer = CreateWrappedBuffer(memory_allocator, buffer_size, MemoryUsage::Download); |
| 189 | VK_FORMAT_B8G8R8A8_UNORM); | 206 | blit_capture.DrawToFrame(rasterizer, &frame, framebuffers, layout, 1, format); |
| 190 | |||
| 191 | const auto dst_buffer = CreateWrappedBuffer( | ||
| 192 | memory_allocator, static_cast<VkDeviceSize>(layout.width * layout.height * 4), | ||
| 193 | MemoryUsage::Download); | ||
| 194 | 207 | ||
| 195 | scheduler.RequestOutsideRenderPassOperationContext(); | 208 | scheduler.RequestOutsideRenderPassOperationContext(); |
| 196 | scheduler.Record([&](vk::CommandBuffer cmdbuf) { | 209 | scheduler.Record([&](vk::CommandBuffer cmdbuf) { |
| @@ -198,15 +211,68 @@ void Vulkan::RendererVulkan::RenderScreenshot( | |||
| 198 | VkExtent3D{layout.width, layout.height, 1}); | 211 | VkExtent3D{layout.width, layout.height, 1}); |
| 199 | }); | 212 | }); |
| 200 | 213 | ||
| 201 | // Ensure the copy is fully completed before saving the screenshot | 214 | // Ensure the copy is fully completed before saving the capture |
| 202 | scheduler.Finish(); | 215 | scheduler.Finish(); |
| 203 | 216 | ||
| 204 | // Copy backing image data to the QImage screenshot buffer | 217 | // Copy backing image data to the capture buffer |
| 205 | dst_buffer.Invalidate(); | 218 | dst_buffer.Invalidate(); |
| 219 | return dst_buffer; | ||
| 220 | } | ||
| 221 | |||
| 222 | void RendererVulkan::RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers) { | ||
| 223 | if (!renderer_settings.screenshot_requested) { | ||
| 224 | return; | ||
| 225 | } | ||
| 226 | |||
| 227 | const auto& layout{renderer_settings.screenshot_framebuffer_layout}; | ||
| 228 | const auto dst_buffer = RenderToBuffer(framebuffers, layout, VK_FORMAT_B8G8R8A8_UNORM, | ||
| 229 | layout.width * layout.height * 4); | ||
| 230 | |||
| 206 | std::memcpy(renderer_settings.screenshot_bits, dst_buffer.Mapped().data(), | 231 | std::memcpy(renderer_settings.screenshot_bits, dst_buffer.Mapped().data(), |
| 207 | dst_buffer.Mapped().size()); | 232 | dst_buffer.Mapped().size()); |
| 208 | renderer_settings.screenshot_complete_callback(false); | 233 | renderer_settings.screenshot_complete_callback(false); |
| 209 | renderer_settings.screenshot_requested = false; | 234 | renderer_settings.screenshot_requested = false; |
| 210 | } | 235 | } |
| 211 | 236 | ||
| 237 | std::vector<u8> RendererVulkan::GetAppletCaptureBuffer() { | ||
| 238 | using namespace VideoCore::Capture; | ||
| 239 | |||
| 240 | std::vector<u8> out(VideoCore::Capture::TiledSize); | ||
| 241 | |||
| 242 | if (!applet_frame.image) { | ||
| 243 | return out; | ||
| 244 | } | ||
| 245 | |||
| 246 | const auto dst_buffer = | ||
| 247 | CreateWrappedBuffer(memory_allocator, VideoCore::Capture::TiledSize, MemoryUsage::Download); | ||
| 248 | |||
| 249 | scheduler.RequestOutsideRenderPassOperationContext(); | ||
| 250 | scheduler.Record([&](vk::CommandBuffer cmdbuf) { | ||
| 251 | DownloadColorImage(cmdbuf, *applet_frame.image, *dst_buffer, CaptureImageExtent); | ||
| 252 | }); | ||
| 253 | |||
| 254 | // Ensure the copy is fully completed before writing the capture | ||
| 255 | scheduler.Finish(); | ||
| 256 | |||
| 257 | // Swizzle image data to the capture buffer | ||
| 258 | dst_buffer.Invalidate(); | ||
| 259 | Tegra::Texture::SwizzleTexture(out, dst_buffer.Mapped(), BytesPerPixel, LinearWidth, | ||
| 260 | LinearHeight, LinearDepth, BlockHeight, BlockDepth); | ||
| 261 | |||
| 262 | return out; | ||
| 263 | } | ||
| 264 | |||
| 265 | void RendererVulkan::RenderAppletCaptureLayer( | ||
| 266 | std::span<const Tegra::FramebufferConfig> framebuffers) { | ||
| 267 | if (!applet_frame.image) { | ||
| 268 | applet_frame.image = CreateWrappedImage(memory_allocator, CaptureImageSize, CaptureFormat); | ||
| 269 | applet_frame.image_view = CreateWrappedImageView(device, applet_frame.image, CaptureFormat); | ||
| 270 | applet_frame.framebuffer = blit_applet.CreateFramebuffer( | ||
| 271 | VideoCore::Capture::Layout, *applet_frame.image_view, CaptureFormat); | ||
| 272 | } | ||
| 273 | |||
| 274 | blit_applet.DrawToFrame(rasterizer, &applet_frame, framebuffers, VideoCore::Capture::Layout, 1, | ||
| 275 | CaptureFormat); | ||
| 276 | } | ||
| 277 | |||
| 212 | } // namespace Vulkan | 278 | } // namespace Vulkan |
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h index c6d8a0f21..fb9d83412 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.h +++ b/src/video_core/renderer_vulkan/renderer_vulkan.h | |||
| @@ -48,6 +48,8 @@ public: | |||
| 48 | 48 | ||
| 49 | void Composite(std::span<const Tegra::FramebufferConfig> framebuffers) override; | 49 | void Composite(std::span<const Tegra::FramebufferConfig> framebuffers) override; |
| 50 | 50 | ||
| 51 | std::vector<u8> GetAppletCaptureBuffer() override; | ||
| 52 | |||
| 51 | VideoCore::RasterizerInterface* ReadRasterizer() override { | 53 | VideoCore::RasterizerInterface* ReadRasterizer() override { |
| 52 | return &rasterizer; | 54 | return &rasterizer; |
| 53 | } | 55 | } |
| @@ -59,7 +61,11 @@ public: | |||
| 59 | private: | 61 | private: |
| 60 | void Report() const; | 62 | void Report() const; |
| 61 | 63 | ||
| 64 | vk::Buffer RenderToBuffer(std::span<const Tegra::FramebufferConfig> framebuffers, | ||
| 65 | const Layout::FramebufferLayout& layout, VkFormat format, | ||
| 66 | VkDeviceSize buffer_size); | ||
| 62 | void RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers); | 67 | void RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers); |
| 68 | void RenderAppletCaptureLayer(std::span<const Tegra::FramebufferConfig> framebuffers); | ||
| 63 | 69 | ||
| 64 | Core::TelemetrySession& telemetry_session; | 70 | Core::TelemetrySession& telemetry_session; |
| 65 | Tegra::MaxwellDeviceMemoryManager& device_memory; | 71 | Tegra::MaxwellDeviceMemoryManager& device_memory; |
| @@ -79,9 +85,12 @@ private: | |||
| 79 | Swapchain swapchain; | 85 | Swapchain swapchain; |
| 80 | PresentManager present_manager; | 86 | PresentManager present_manager; |
| 81 | BlitScreen blit_swapchain; | 87 | BlitScreen blit_swapchain; |
| 82 | BlitScreen blit_screenshot; | 88 | BlitScreen blit_capture; |
| 89 | BlitScreen blit_applet; | ||
| 83 | RasterizerVulkan rasterizer; | 90 | RasterizerVulkan rasterizer; |
| 84 | std::optional<TurboMode> turbo_mode; | 91 | std::optional<TurboMode> turbo_mode; |
| 92 | |||
| 93 | Frame applet_frame; | ||
| 85 | }; | 94 | }; |
| 86 | 95 | ||
| 87 | } // namespace Vulkan | 96 | } // namespace Vulkan |
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp index 2275fcc46..b7797f833 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include "video_core/framebuffer_config.h" | 4 | #include "video_core/framebuffer_config.h" |
| 5 | #include "video_core/present.h" | ||
| 5 | #include "video_core/renderer_vulkan/present/filters.h" | 6 | #include "video_core/renderer_vulkan/present/filters.h" |
| 6 | #include "video_core/renderer_vulkan/present/layer.h" | 7 | #include "video_core/renderer_vulkan/present/layer.h" |
| 7 | #include "video_core/renderer_vulkan/vk_blit_screen.h" | 8 | #include "video_core/renderer_vulkan/vk_blit_screen.h" |
| @@ -12,9 +13,9 @@ namespace Vulkan { | |||
| 12 | 13 | ||
| 13 | BlitScreen::BlitScreen(Tegra::MaxwellDeviceMemoryManager& device_memory_, const Device& device_, | 14 | BlitScreen::BlitScreen(Tegra::MaxwellDeviceMemoryManager& device_memory_, const Device& device_, |
| 14 | MemoryAllocator& memory_allocator_, PresentManager& present_manager_, | 15 | MemoryAllocator& memory_allocator_, PresentManager& present_manager_, |
| 15 | Scheduler& scheduler_) | 16 | Scheduler& scheduler_, const PresentFilters& filters_) |
| 16 | : device_memory{device_memory_}, device{device_}, memory_allocator{memory_allocator_}, | 17 | : device_memory{device_memory_}, device{device_}, memory_allocator{memory_allocator_}, |
| 17 | present_manager{present_manager_}, scheduler{scheduler_}, image_count{1}, | 18 | present_manager{present_manager_}, scheduler{scheduler_}, filters{filters_}, image_count{1}, |
| 18 | swapchain_view_format{VK_FORMAT_B8G8R8A8_UNORM} {} | 19 | swapchain_view_format{VK_FORMAT_B8G8R8A8_UNORM} {} |
| 19 | 20 | ||
| 20 | BlitScreen::~BlitScreen() = default; | 21 | BlitScreen::~BlitScreen() = default; |
| @@ -27,7 +28,7 @@ void BlitScreen::WaitIdle() { | |||
| 27 | 28 | ||
| 28 | void BlitScreen::SetWindowAdaptPass() { | 29 | void BlitScreen::SetWindowAdaptPass() { |
| 29 | layers.clear(); | 30 | layers.clear(); |
| 30 | scaling_filter = Settings::values.scaling_filter.GetValue(); | 31 | scaling_filter = filters.get_scaling_filter(); |
| 31 | 32 | ||
| 32 | switch (scaling_filter) { | 33 | switch (scaling_filter) { |
| 33 | case Settings::ScalingFilter::NearestNeighbor: | 34 | case Settings::ScalingFilter::NearestNeighbor: |
| @@ -59,7 +60,7 @@ void BlitScreen::DrawToFrame(RasterizerVulkan& rasterizer, Frame* frame, | |||
| 59 | bool presentation_recreate_required = false; | 60 | bool presentation_recreate_required = false; |
| 60 | 61 | ||
| 61 | // Recreate dynamic resources if the adapting filter changed | 62 | // Recreate dynamic resources if the adapting filter changed |
| 62 | if (!window_adapt || scaling_filter != Settings::values.scaling_filter.GetValue()) { | 63 | if (!window_adapt || scaling_filter != filters.get_scaling_filter()) { |
| 63 | resource_update_required = true; | 64 | resource_update_required = true; |
| 64 | } | 65 | } |
| 65 | 66 | ||
| @@ -102,7 +103,7 @@ void BlitScreen::DrawToFrame(RasterizerVulkan& rasterizer, Frame* frame, | |||
| 102 | 103 | ||
| 103 | while (layers.size() < framebuffers.size()) { | 104 | while (layers.size() < framebuffers.size()) { |
| 104 | layers.emplace_back(device, memory_allocator, scheduler, device_memory, image_count, | 105 | layers.emplace_back(device, memory_allocator, scheduler, device_memory, image_count, |
| 105 | window_size, window_adapt->GetDescriptorSetLayout()); | 106 | window_size, window_adapt->GetDescriptorSetLayout(), filters); |
| 106 | } | 107 | } |
| 107 | 108 | ||
| 108 | // Perform the draw | 109 | // Perform the draw |
| @@ -119,8 +120,7 @@ vk::Framebuffer BlitScreen::CreateFramebuffer(const Layout::FramebufferLayout& l | |||
| 119 | VkFormat current_view_format) { | 120 | VkFormat current_view_format) { |
| 120 | const bool format_updated = | 121 | const bool format_updated = |
| 121 | std::exchange(swapchain_view_format, current_view_format) != current_view_format; | 122 | std::exchange(swapchain_view_format, current_view_format) != current_view_format; |
| 122 | if (!window_adapt || scaling_filter != Settings::values.scaling_filter.GetValue() || | 123 | if (!window_adapt || scaling_filter != filters.get_scaling_filter() || format_updated) { |
| 123 | format_updated) { | ||
| 124 | WaitIdle(); | 124 | WaitIdle(); |
| 125 | SetWindowAdaptPass(); | 125 | SetWindowAdaptPass(); |
| 126 | } | 126 | } |
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.h b/src/video_core/renderer_vulkan/vk_blit_screen.h index cbdf2d5d0..531c57fc5 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.h +++ b/src/video_core/renderer_vulkan/vk_blit_screen.h | |||
| @@ -16,6 +16,8 @@ namespace Core { | |||
| 16 | class System; | 16 | class System; |
| 17 | } | 17 | } |
| 18 | 18 | ||
| 19 | struct PresentFilters; | ||
| 20 | |||
| 19 | namespace Tegra { | 21 | namespace Tegra { |
| 20 | struct FramebufferConfig; | 22 | struct FramebufferConfig; |
| 21 | } | 23 | } |
| @@ -47,7 +49,7 @@ class BlitScreen { | |||
| 47 | public: | 49 | public: |
| 48 | explicit BlitScreen(Tegra::MaxwellDeviceMemoryManager& device_memory, const Device& device, | 50 | explicit BlitScreen(Tegra::MaxwellDeviceMemoryManager& device_memory, const Device& device, |
| 49 | MemoryAllocator& memory_allocator, PresentManager& present_manager, | 51 | MemoryAllocator& memory_allocator, PresentManager& present_manager, |
| 50 | Scheduler& scheduler); | 52 | Scheduler& scheduler, const PresentFilters& filters); |
| 51 | ~BlitScreen(); | 53 | ~BlitScreen(); |
| 52 | 54 | ||
| 53 | void DrawToFrame(RasterizerVulkan& rasterizer, Frame* frame, | 55 | void DrawToFrame(RasterizerVulkan& rasterizer, Frame* frame, |
| @@ -70,6 +72,7 @@ private: | |||
| 70 | MemoryAllocator& memory_allocator; | 72 | MemoryAllocator& memory_allocator; |
| 71 | PresentManager& present_manager; | 73 | PresentManager& present_manager; |
| 72 | Scheduler& scheduler; | 74 | Scheduler& scheduler; |
| 75 | const PresentFilters& filters; | ||
| 73 | std::size_t image_count{}; | 76 | std::size_t image_count{}; |
| 74 | std::size_t image_index{}; | 77 | std::size_t image_index{}; |
| 75 | VkFormat swapchain_view_format{}; | 78 | VkFormat swapchain_view_format{}; |
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index 31001d142..e5e1e3ab6 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp | |||
| @@ -368,7 +368,7 @@ u32 BufferCacheRuntime::GetStorageBufferAlignment() const { | |||
| 368 | return static_cast<u32>(device.GetStorageBufferAlignment()); | 368 | return static_cast<u32>(device.GetStorageBufferAlignment()); |
| 369 | } | 369 | } |
| 370 | 370 | ||
| 371 | void BufferCacheRuntime::TickFrame(VideoCommon::SlotVector<Buffer>& slot_buffers) noexcept { | 371 | void BufferCacheRuntime::TickFrame(Common::SlotVector<Buffer>& slot_buffers) noexcept { |
| 372 | for (auto it = slot_buffers.begin(); it != slot_buffers.end(); it++) { | 372 | for (auto it = slot_buffers.begin(); it != slot_buffers.end(); it++) { |
| 373 | it->ResetUsageTracking(); | 373 | it->ResetUsageTracking(); |
| 374 | } | 374 | } |
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h index e273f4988..efe960258 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.h +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h | |||
| @@ -81,7 +81,7 @@ public: | |||
| 81 | ComputePassDescriptorQueue& compute_pass_descriptor_queue, | 81 | ComputePassDescriptorQueue& compute_pass_descriptor_queue, |
| 82 | DescriptorPool& descriptor_pool); | 82 | DescriptorPool& descriptor_pool); |
| 83 | 83 | ||
| 84 | void TickFrame(VideoCommon::SlotVector<Buffer>& slot_buffers) noexcept; | 84 | void TickFrame(Common::SlotVector<Buffer>& slot_buffers) noexcept; |
| 85 | 85 | ||
| 86 | void Finish(); | 86 | void Finish(); |
| 87 | 87 | ||
| @@ -181,7 +181,6 @@ struct BufferCacheParams { | |||
| 181 | static constexpr bool NEEDS_BIND_STORAGE_INDEX = false; | 181 | static constexpr bool NEEDS_BIND_STORAGE_INDEX = false; |
| 182 | static constexpr bool USE_MEMORY_MAPS = true; | 182 | static constexpr bool USE_MEMORY_MAPS = true; |
| 183 | static constexpr bool SEPARATE_IMAGE_BUFFER_BINDINGS = false; | 183 | static constexpr bool SEPARATE_IMAGE_BUFFER_BINDINGS = false; |
| 184 | static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = true; | ||
| 185 | static constexpr bool USE_MEMORY_MAPS_FOR_UPLOADS = true; | 184 | static constexpr bool USE_MEMORY_MAPS_FOR_UPLOADS = true; |
| 186 | }; | 185 | }; |
| 187 | 186 | ||
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index 0dbde65d6..aaeb5ef93 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h | |||
| @@ -20,11 +20,11 @@ struct ResolutionScalingInfo; | |||
| 20 | 20 | ||
| 21 | namespace Vulkan { | 21 | namespace Vulkan { |
| 22 | 22 | ||
| 23 | using Common::SlotVector; | ||
| 23 | using VideoCommon::ImageId; | 24 | using VideoCommon::ImageId; |
| 24 | using VideoCommon::NUM_RT; | 25 | using VideoCommon::NUM_RT; |
| 25 | using VideoCommon::Region2D; | 26 | using VideoCommon::Region2D; |
| 26 | using VideoCommon::RenderTargets; | 27 | using VideoCommon::RenderTargets; |
| 27 | using VideoCommon::SlotVector; | ||
| 28 | using VideoCore::Surface::PixelFormat; | 28 | using VideoCore::Surface::PixelFormat; |
| 29 | 29 | ||
| 30 | class BlitImageHelper; | 30 | class BlitImageHelper; |
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index a20c956ff..3a1cc060e 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h | |||
| @@ -746,7 +746,13 @@ std::pair<typename P::ImageView*, bool> TextureCache<P>::TryFindFramebufferImage | |||
| 746 | }(); | 746 | }(); |
| 747 | 747 | ||
| 748 | const auto GetImageViewForFramebuffer = [&](ImageId image_id) { | 748 | const auto GetImageViewForFramebuffer = [&](ImageId image_id) { |
| 749 | const ImageViewInfo info{ImageViewType::e2D, view_format}; | 749 | ImageViewInfo info{ImageViewType::e2D, view_format}; |
| 750 | if (config.blending == Tegra::BlendMode::Opaque) { | ||
| 751 | info.x_source = static_cast<u8>(SwizzleSource::R); | ||
| 752 | info.y_source = static_cast<u8>(SwizzleSource::G); | ||
| 753 | info.z_source = static_cast<u8>(SwizzleSource::B); | ||
| 754 | info.w_source = static_cast<u8>(SwizzleSource::OneFloat); | ||
| 755 | } | ||
| 750 | return std::make_pair(&slot_image_views[FindOrEmplaceImageView(image_id, info)], | 756 | return std::make_pair(&slot_image_views[FindOrEmplaceImageView(image_id, info)], |
| 751 | slot_images[image_id].IsRescaled()); | 757 | slot_images[image_id].IsRescaled()); |
| 752 | }; | 758 | }; |
diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h index e7b910121..da98a634b 100644 --- a/src/video_core/texture_cache/texture_cache_base.h +++ b/src/video_core/texture_cache/texture_cache_base.h | |||
| @@ -21,6 +21,7 @@ | |||
| 21 | #include "common/lru_cache.h" | 21 | #include "common/lru_cache.h" |
| 22 | #include "common/polyfill_ranges.h" | 22 | #include "common/polyfill_ranges.h" |
| 23 | #include "common/scratch_buffer.h" | 23 | #include "common/scratch_buffer.h" |
| 24 | #include "common/slot_vector.h" | ||
| 24 | #include "common/thread_worker.h" | 25 | #include "common/thread_worker.h" |
| 25 | #include "video_core/compatible_formats.h" | 26 | #include "video_core/compatible_formats.h" |
| 26 | #include "video_core/control/channel_state_cache.h" | 27 | #include "video_core/control/channel_state_cache.h" |
| @@ -32,7 +33,6 @@ | |||
| 32 | #include "video_core/texture_cache/image_info.h" | 33 | #include "video_core/texture_cache/image_info.h" |
| 33 | #include "video_core/texture_cache/image_view_base.h" | 34 | #include "video_core/texture_cache/image_view_base.h" |
| 34 | #include "video_core/texture_cache/render_targets.h" | 35 | #include "video_core/texture_cache/render_targets.h" |
| 35 | #include "video_core/texture_cache/slot_vector.h" | ||
| 36 | #include "video_core/texture_cache/types.h" | 36 | #include "video_core/texture_cache/types.h" |
| 37 | #include "video_core/textures/texture.h" | 37 | #include "video_core/textures/texture.h" |
| 38 | 38 | ||
| @@ -451,16 +451,16 @@ private: | |||
| 451 | struct PendingDownload { | 451 | struct PendingDownload { |
| 452 | bool is_swizzle; | 452 | bool is_swizzle; |
| 453 | size_t async_buffer_id; | 453 | size_t async_buffer_id; |
| 454 | SlotId object_id; | 454 | Common::SlotId object_id; |
| 455 | }; | 455 | }; |
| 456 | 456 | ||
| 457 | SlotVector<Image> slot_images; | 457 | Common::SlotVector<Image> slot_images; |
| 458 | SlotVector<ImageMapView> slot_map_views; | 458 | Common::SlotVector<ImageMapView> slot_map_views; |
| 459 | SlotVector<ImageView> slot_image_views; | 459 | Common::SlotVector<ImageView> slot_image_views; |
| 460 | SlotVector<ImageAlloc> slot_image_allocs; | 460 | Common::SlotVector<ImageAlloc> slot_image_allocs; |
| 461 | SlotVector<Sampler> slot_samplers; | 461 | Common::SlotVector<Sampler> slot_samplers; |
| 462 | SlotVector<Framebuffer> slot_framebuffers; | 462 | Common::SlotVector<Framebuffer> slot_framebuffers; |
| 463 | SlotVector<BufferDownload> slot_buffer_downloads; | 463 | Common::SlotVector<BufferDownload> slot_buffer_downloads; |
| 464 | 464 | ||
| 465 | // TODO: This data structure is not optimal and it should be reworked | 465 | // TODO: This data structure is not optimal and it should be reworked |
| 466 | 466 | ||
diff --git a/src/video_core/texture_cache/types.h b/src/video_core/texture_cache/types.h index 0453456b4..07c304386 100644 --- a/src/video_core/texture_cache/types.h +++ b/src/video_core/texture_cache/types.h | |||
| @@ -5,21 +5,21 @@ | |||
| 5 | 5 | ||
| 6 | #include "common/common_funcs.h" | 6 | #include "common/common_funcs.h" |
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | #include "video_core/texture_cache/slot_vector.h" | 8 | #include "common/slot_vector.h" |
| 9 | 9 | ||
| 10 | namespace VideoCommon { | 10 | namespace VideoCommon { |
| 11 | 11 | ||
| 12 | constexpr size_t NUM_RT = 8; | 12 | constexpr size_t NUM_RT = 8; |
| 13 | constexpr size_t MAX_MIP_LEVELS = 14; | 13 | constexpr size_t MAX_MIP_LEVELS = 14; |
| 14 | 14 | ||
| 15 | constexpr SlotId CORRUPT_ID{0xfffffffe}; | 15 | constexpr Common::SlotId CORRUPT_ID{0xfffffffe}; |
| 16 | 16 | ||
| 17 | using ImageId = SlotId; | 17 | using ImageId = Common::SlotId; |
| 18 | using ImageMapId = SlotId; | 18 | using ImageMapId = Common::SlotId; |
| 19 | using ImageViewId = SlotId; | 19 | using ImageViewId = Common::SlotId; |
| 20 | using ImageAllocId = SlotId; | 20 | using ImageAllocId = Common::SlotId; |
| 21 | using SamplerId = SlotId; | 21 | using SamplerId = Common::SlotId; |
| 22 | using FramebufferId = SlotId; | 22 | using FramebufferId = Common::SlotId; |
| 23 | 23 | ||
| 24 | /// Fake image ID for null image views | 24 | /// Fake image ID for null image views |
| 25 | constexpr ImageId NULL_IMAGE_ID{0}; | 25 | constexpr ImageId NULL_IMAGE_ID{0}; |
diff --git a/src/web_service/web_backend.cpp b/src/web_service/web_backend.cpp index dff380cca..fdf3ac846 100644 --- a/src/web_service/web_backend.cpp +++ b/src/web_service/web_backend.cpp | |||
| @@ -32,9 +32,14 @@ struct Client::Impl { | |||
| 32 | Impl(std::string host_, std::string username_, std::string token_) | 32 | Impl(std::string host_, std::string username_, std::string token_) |
| 33 | : host{std::move(host_)}, username{std::move(username_)}, token{std::move(token_)} { | 33 | : host{std::move(host_)}, username{std::move(username_)}, token{std::move(token_)} { |
| 34 | std::scoped_lock lock{jwt_cache.mutex}; | 34 | std::scoped_lock lock{jwt_cache.mutex}; |
| 35 | if (username == jwt_cache.username && token == jwt_cache.token) { | 35 | if (this->username == jwt_cache.username && this->token == jwt_cache.token) { |
| 36 | jwt = jwt_cache.jwt; | 36 | jwt = jwt_cache.jwt; |
| 37 | } | 37 | } |
| 38 | |||
| 39 | // Normalize host expression | ||
| 40 | if (!this->host.empty() && this->host.back() == '/') { | ||
| 41 | static_cast<void>(this->host.pop_back()); | ||
| 42 | } | ||
| 38 | } | 43 | } |
| 39 | 44 | ||
| 40 | /// A generic function handles POST, GET and DELETE request together | 45 | /// A generic function handles POST, GET and DELETE request together |
| @@ -71,18 +76,16 @@ struct Client::Impl { | |||
| 71 | const std::string& jwt_ = "", const std::string& username_ = "", | 76 | const std::string& jwt_ = "", const std::string& username_ = "", |
| 72 | const std::string& token_ = "") { | 77 | const std::string& token_ = "") { |
| 73 | if (cli == nullptr) { | 78 | if (cli == nullptr) { |
| 74 | cli = std::make_unique<httplib::Client>(host); | 79 | cli = std::make_unique<httplib::Client>(host.c_str()); |
| 80 | cli->set_connection_timeout(TIMEOUT_SECONDS); | ||
| 81 | cli->set_read_timeout(TIMEOUT_SECONDS); | ||
| 82 | cli->set_write_timeout(TIMEOUT_SECONDS); | ||
| 75 | } | 83 | } |
| 76 | |||
| 77 | if (!cli->is_valid()) { | 84 | if (!cli->is_valid()) { |
| 78 | LOG_ERROR(WebService, "Client is invalid, skipping request!"); | 85 | LOG_ERROR(WebService, "Invalid URL {}", host + path); |
| 79 | return {}; | 86 | return WebResult{WebResult::Code::InvalidURL, "Invalid URL", ""}; |
| 80 | } | 87 | } |
| 81 | 88 | ||
| 82 | cli->set_connection_timeout(TIMEOUT_SECONDS); | ||
| 83 | cli->set_read_timeout(TIMEOUT_SECONDS); | ||
| 84 | cli->set_write_timeout(TIMEOUT_SECONDS); | ||
| 85 | |||
| 86 | httplib::Headers params; | 89 | httplib::Headers params; |
| 87 | if (!jwt_.empty()) { | 90 | if (!jwt_.empty()) { |
| 88 | params = { | 91 | params = { |
| @@ -107,15 +110,15 @@ struct Client::Impl { | |||
| 107 | request.headers = params; | 110 | request.headers = params; |
| 108 | request.body = data; | 111 | request.body = data; |
| 109 | 112 | ||
| 110 | httplib::Response response; | 113 | httplib::Result result = cli->send(request); |
| 111 | httplib::Error error; | ||
| 112 | 114 | ||
| 113 | if (!cli->send(request, response, error)) { | 115 | if (!result) { |
| 114 | LOG_ERROR(WebService, "{} to {} returned null (httplib Error: {})", method, host + path, | 116 | LOG_ERROR(WebService, "{} to {} returned null", method, host + path); |
| 115 | httplib::to_string(error)); | ||
| 116 | return WebResult{WebResult::Code::LibError, "Null response", ""}; | 117 | return WebResult{WebResult::Code::LibError, "Null response", ""}; |
| 117 | } | 118 | } |
| 118 | 119 | ||
| 120 | httplib::Response response = result.value(); | ||
| 121 | |||
| 119 | if (response.status >= 400) { | 122 | if (response.status >= 400) { |
| 120 | LOG_ERROR(WebService, "{} to {} returned error status code: {}", method, host + path, | 123 | LOG_ERROR(WebService, "{} to {} returned error status code: {}", method, host + path, |
| 121 | response.status); | 124 | response.status); |
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 76f06da12..0259a8c29 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt | |||
| @@ -41,6 +41,9 @@ add_executable(yuzu | |||
| 41 | configuration/configuration_shared.cpp | 41 | configuration/configuration_shared.cpp |
| 42 | configuration/configuration_shared.h | 42 | configuration/configuration_shared.h |
| 43 | configuration/configure.ui | 43 | configuration/configure.ui |
| 44 | configuration/configure_applets.cpp | ||
| 45 | configuration/configure_applets.h | ||
| 46 | configuration/configure_applets.ui | ||
| 44 | configuration/configure_audio.cpp | 47 | configuration/configure_audio.cpp |
| 45 | configuration/configure_audio.h | 48 | configuration/configure_audio.h |
| 46 | configuration/configure_audio.ui | 49 | configuration/configure_audio.ui |
diff --git a/src/yuzu/configuration/configure_applets.cpp b/src/yuzu/configuration/configure_applets.cpp new file mode 100644 index 000000000..513ecb548 --- /dev/null +++ b/src/yuzu/configuration/configure_applets.cpp | |||
| @@ -0,0 +1,86 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "common/settings.h" | ||
| 5 | #include "core/core.h" | ||
| 6 | #include "ui_configure_applets.h" | ||
| 7 | #include "yuzu/configuration/configuration_shared.h" | ||
| 8 | #include "yuzu/configuration/configure_applets.h" | ||
| 9 | #include "yuzu/configuration/shared_widget.h" | ||
| 10 | |||
| 11 | ConfigureApplets::ConfigureApplets(Core::System& system_, | ||
| 12 | std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group_, | ||
| 13 | const ConfigurationShared::Builder& builder, QWidget* parent) | ||
| 14 | : Tab(group_, parent), ui{std::make_unique<Ui::ConfigureApplets>()}, system{system_} { | ||
| 15 | ui->setupUi(this); | ||
| 16 | |||
| 17 | Setup(builder); | ||
| 18 | |||
| 19 | SetConfiguration(); | ||
| 20 | } | ||
| 21 | |||
| 22 | ConfigureApplets::~ConfigureApplets() = default; | ||
| 23 | |||
| 24 | void ConfigureApplets::changeEvent(QEvent* event) { | ||
| 25 | if (event->type() == QEvent::LanguageChange) { | ||
| 26 | RetranslateUI(); | ||
| 27 | } | ||
| 28 | |||
| 29 | QWidget::changeEvent(event); | ||
| 30 | } | ||
| 31 | |||
| 32 | void ConfigureApplets::RetranslateUI() { | ||
| 33 | ui->retranslateUi(this); | ||
| 34 | } | ||
| 35 | |||
| 36 | void ConfigureApplets::Setup(const ConfigurationShared::Builder& builder) { | ||
| 37 | auto& library_applets_layout = *ui->group_library_applet_modes->layout(); | ||
| 38 | std::map<u32, QWidget*> applets_hold{}; | ||
| 39 | |||
| 40 | std::vector<Settings::BasicSetting*> settings; | ||
| 41 | auto push = [&settings](auto& list) { | ||
| 42 | for (auto setting : list) { | ||
| 43 | settings.push_back(setting); | ||
| 44 | } | ||
| 45 | }; | ||
| 46 | |||
| 47 | push(Settings::values.linkage.by_category[Settings::Category::LibraryApplet]); | ||
| 48 | |||
| 49 | for (auto setting : settings) { | ||
| 50 | ConfigurationShared::Widget* widget = builder.BuildWidget(setting, apply_funcs); | ||
| 51 | |||
| 52 | if (widget == nullptr) { | ||
| 53 | continue; | ||
| 54 | } | ||
| 55 | if (!widget->Valid()) { | ||
| 56 | widget->deleteLater(); | ||
| 57 | continue; | ||
| 58 | } | ||
| 59 | |||
| 60 | // Untested applets | ||
| 61 | if (setting->Id() == Settings::values.data_erase_applet_mode.Id() || | ||
| 62 | setting->Id() == Settings::values.error_applet_mode.Id() || | ||
| 63 | setting->Id() == Settings::values.net_connect_applet_mode.Id() || | ||
| 64 | setting->Id() == Settings::values.web_applet_mode.Id() || | ||
| 65 | setting->Id() == Settings::values.shop_applet_mode.Id() || | ||
| 66 | setting->Id() == Settings::values.login_share_applet_mode.Id() || | ||
| 67 | setting->Id() == Settings::values.wifi_web_auth_applet_mode.Id() || | ||
| 68 | setting->Id() == Settings::values.my_page_applet_mode.Id()) { | ||
| 69 | widget->setHidden(true); | ||
| 70 | } | ||
| 71 | |||
| 72 | applets_hold.emplace(setting->Id(), widget); | ||
| 73 | } | ||
| 74 | for (const auto& [label, widget] : applets_hold) { | ||
| 75 | library_applets_layout.addWidget(widget); | ||
| 76 | } | ||
| 77 | } | ||
| 78 | |||
| 79 | void ConfigureApplets::SetConfiguration() {} | ||
| 80 | |||
| 81 | void ConfigureApplets::ApplyConfiguration() { | ||
| 82 | const bool powered_on = system.IsPoweredOn(); | ||
| 83 | for (const auto& func : apply_funcs) { | ||
| 84 | func(powered_on); | ||
| 85 | } | ||
| 86 | } | ||
diff --git a/src/yuzu/configuration/configure_applets.h b/src/yuzu/configuration/configure_applets.h new file mode 100644 index 000000000..54f494d2f --- /dev/null +++ b/src/yuzu/configuration/configure_applets.h | |||
| @@ -0,0 +1,48 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <QWidget> | ||
| 7 | #include "yuzu/configuration/configuration_shared.h" | ||
| 8 | |||
| 9 | class QCheckBox; | ||
| 10 | class QLineEdit; | ||
| 11 | class QComboBox; | ||
| 12 | class QDateTimeEdit; | ||
| 13 | namespace Core { | ||
| 14 | class System; | ||
| 15 | } | ||
| 16 | |||
| 17 | namespace Ui { | ||
| 18 | class ConfigureApplets; | ||
| 19 | } | ||
| 20 | |||
| 21 | namespace ConfigurationShared { | ||
| 22 | class Builder; | ||
| 23 | } | ||
| 24 | |||
| 25 | class ConfigureApplets : public ConfigurationShared::Tab { | ||
| 26 | public: | ||
| 27 | explicit ConfigureApplets(Core::System& system_, | ||
| 28 | std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group, | ||
| 29 | const ConfigurationShared::Builder& builder, | ||
| 30 | QWidget* parent = nullptr); | ||
| 31 | ~ConfigureApplets() override; | ||
| 32 | |||
| 33 | void ApplyConfiguration() override; | ||
| 34 | void SetConfiguration() override; | ||
| 35 | |||
| 36 | private: | ||
| 37 | void changeEvent(QEvent* event) override; | ||
| 38 | void RetranslateUI(); | ||
| 39 | |||
| 40 | void Setup(const ConfigurationShared::Builder& builder); | ||
| 41 | |||
| 42 | std::vector<std::function<void(bool)>> apply_funcs{}; | ||
| 43 | |||
| 44 | std::unique_ptr<Ui::ConfigureApplets> ui; | ||
| 45 | bool enabled = false; | ||
| 46 | |||
| 47 | Core::System& system; | ||
| 48 | }; | ||
diff --git a/src/yuzu/configuration/configure_applets.ui b/src/yuzu/configuration/configure_applets.ui new file mode 100644 index 000000000..6f2ca66bd --- /dev/null +++ b/src/yuzu/configuration/configure_applets.ui | |||
| @@ -0,0 +1,65 @@ | |||
| 1 | <?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | <ui version="4.0"> | ||
| 3 | <class>ConfigureApplets</class> | ||
| 4 | <widget class="QWidget" name="ConfigureApplets"> | ||
| 5 | <property name="geometry"> | ||
| 6 | <rect> | ||
| 7 | <x>0</x> | ||
| 8 | <y>0</y> | ||
| 9 | <width>605</width> | ||
| 10 | <height>300</height> | ||
| 11 | </rect> | ||
| 12 | </property> | ||
| 13 | <property name="windowTitle"> | ||
| 14 | <string>Form</string> | ||
| 15 | </property> | ||
| 16 | <property name="accessibleName"> | ||
| 17 | <string>Applets</string> | ||
| 18 | </property> | ||
| 19 | <layout class="QVBoxLayout" name="verticalLayout_1"> | ||
| 20 | <item> | ||
| 21 | <layout class="QVBoxLayout" name="verticalLayout"> | ||
| 22 | <item> | ||
| 23 | <widget class="QGroupBox" name="group_library_applet_modes"> | ||
| 24 | <property name="title"> | ||
| 25 | <string>Applet mode preference</string> | ||
| 26 | </property> | ||
| 27 | <layout class="QVBoxLayout"> | ||
| 28 | <item> | ||
| 29 | <widget class="QWidget" name="applets_widget" native="true"> | ||
| 30 | <layout class="QVBoxLayout" name="verticalLayout_3"> | ||
| 31 | <property name="leftMargin"> | ||
| 32 | <number>0</number> | ||
| 33 | </property> | ||
| 34 | <property name="topMargin"> | ||
| 35 | <number>0</number> | ||
| 36 | </property> | ||
| 37 | <property name="rightMargin"> | ||
| 38 | <number>0</number> | ||
| 39 | </property> | ||
| 40 | </layout> | ||
| 41 | </widget> | ||
| 42 | </item> | ||
| 43 | </layout> | ||
| 44 | </widget> | ||
| 45 | </item> | ||
| 46 | </layout> | ||
| 47 | </item> | ||
| 48 | <item> | ||
| 49 | <spacer name="verticalSpacer"> | ||
| 50 | <property name="orientation"> | ||
| 51 | <enum>Qt::Vertical</enum> | ||
| 52 | </property> | ||
| 53 | <property name="sizeHint" stdset="0"> | ||
| 54 | <size> | ||
| 55 | <width>20</width> | ||
| 56 | <height>40</height> | ||
| 57 | </size> | ||
| 58 | </property> | ||
| 59 | </spacer> | ||
| 60 | </item> | ||
| 61 | </layout> | ||
| 62 | </widget> | ||
| 63 | <resources/> | ||
| 64 | <connections/> | ||
| 65 | </ui> | ||
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp index aab54a1cc..37f23388e 100644 --- a/src/yuzu/configuration/configure_dialog.cpp +++ b/src/yuzu/configuration/configure_dialog.cpp | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | #include "core/core.h" | 8 | #include "core/core.h" |
| 9 | #include "ui_configure.h" | 9 | #include "ui_configure.h" |
| 10 | #include "vk_device_info.h" | 10 | #include "vk_device_info.h" |
| 11 | #include "yuzu/configuration/configure_applets.h" | ||
| 11 | #include "yuzu/configuration/configure_audio.h" | 12 | #include "yuzu/configuration/configure_audio.h" |
| 12 | #include "yuzu/configuration/configure_cpu.h" | 13 | #include "yuzu/configuration/configure_cpu.h" |
| 13 | #include "yuzu/configuration/configure_debug_tab.h" | 14 | #include "yuzu/configuration/configure_debug_tab.h" |
| @@ -34,6 +35,7 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_, | |||
| 34 | : QDialog(parent), ui{std::make_unique<Ui::ConfigureDialog>()}, | 35 | : QDialog(parent), ui{std::make_unique<Ui::ConfigureDialog>()}, |
| 35 | registry(registry_), system{system_}, builder{std::make_unique<ConfigurationShared::Builder>( | 36 | registry(registry_), system{system_}, builder{std::make_unique<ConfigurationShared::Builder>( |
| 36 | this, !system_.IsPoweredOn())}, | 37 | this, !system_.IsPoweredOn())}, |
| 38 | applets_tab{std::make_unique<ConfigureApplets>(system_, nullptr, *builder, this)}, | ||
| 37 | audio_tab{std::make_unique<ConfigureAudio>(system_, nullptr, *builder, this)}, | 39 | audio_tab{std::make_unique<ConfigureAudio>(system_, nullptr, *builder, this)}, |
| 38 | cpu_tab{std::make_unique<ConfigureCpu>(system_, nullptr, *builder, this)}, | 40 | cpu_tab{std::make_unique<ConfigureCpu>(system_, nullptr, *builder, this)}, |
| 39 | debug_tab_tab{std::make_unique<ConfigureDebugTab>(system_, this)}, | 41 | debug_tab_tab{std::make_unique<ConfigureDebugTab>(system_, this)}, |
| @@ -58,6 +60,7 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_, | |||
| 58 | 60 | ||
| 59 | ui->setupUi(this); | 61 | ui->setupUi(this); |
| 60 | 62 | ||
| 63 | ui->tabWidget->addTab(applets_tab.get(), tr("Applets")); | ||
| 61 | ui->tabWidget->addTab(audio_tab.get(), tr("Audio")); | 64 | ui->tabWidget->addTab(audio_tab.get(), tr("Audio")); |
| 62 | ui->tabWidget->addTab(cpu_tab.get(), tr("CPU")); | 65 | ui->tabWidget->addTab(cpu_tab.get(), tr("CPU")); |
| 63 | ui->tabWidget->addTab(debug_tab_tab.get(), tr("Debug")); | 66 | ui->tabWidget->addTab(debug_tab_tab.get(), tr("Debug")); |
| @@ -124,6 +127,7 @@ void ConfigureDialog::ApplyConfiguration() { | |||
| 124 | debug_tab_tab->ApplyConfiguration(); | 127 | debug_tab_tab->ApplyConfiguration(); |
| 125 | web_tab->ApplyConfiguration(); | 128 | web_tab->ApplyConfiguration(); |
| 126 | network_tab->ApplyConfiguration(); | 129 | network_tab->ApplyConfiguration(); |
| 130 | applets_tab->ApplyConfiguration(); | ||
| 127 | system.ApplySettings(); | 131 | system.ApplySettings(); |
| 128 | Settings::LogSettings(); | 132 | Settings::LogSettings(); |
| 129 | } | 133 | } |
| @@ -161,7 +165,8 @@ void ConfigureDialog::PopulateSelectionList() { | |||
| 161 | {{tr("General"), | 165 | {{tr("General"), |
| 162 | {general_tab.get(), hotkeys_tab.get(), ui_tab.get(), web_tab.get(), debug_tab_tab.get()}}, | 166 | {general_tab.get(), hotkeys_tab.get(), ui_tab.get(), web_tab.get(), debug_tab_tab.get()}}, |
| 163 | {tr("System"), | 167 | {tr("System"), |
| 164 | {system_tab.get(), profile_tab.get(), network_tab.get(), filesystem_tab.get()}}, | 168 | {system_tab.get(), profile_tab.get(), network_tab.get(), filesystem_tab.get(), |
| 169 | applets_tab.get()}}, | ||
| 165 | {tr("CPU"), {cpu_tab.get()}}, | 170 | {tr("CPU"), {cpu_tab.get()}}, |
| 166 | {tr("Graphics"), {graphics_tab.get(), graphics_advanced_tab.get()}}, | 171 | {tr("Graphics"), {graphics_tab.get(), graphics_advanced_tab.get()}}, |
| 167 | {tr("Audio"), {audio_tab.get()}}, | 172 | {tr("Audio"), {audio_tab.get()}}, |
diff --git a/src/yuzu/configuration/configure_dialog.h b/src/yuzu/configuration/configure_dialog.h index b28ce288c..d0a24a07b 100644 --- a/src/yuzu/configuration/configure_dialog.h +++ b/src/yuzu/configuration/configure_dialog.h | |||
| @@ -15,6 +15,7 @@ namespace Core { | |||
| 15 | class System; | 15 | class System; |
| 16 | } | 16 | } |
| 17 | 17 | ||
| 18 | class ConfigureApplets; | ||
| 18 | class ConfigureAudio; | 19 | class ConfigureAudio; |
| 19 | class ConfigureCpu; | 20 | class ConfigureCpu; |
| 20 | class ConfigureDebugTab; | 21 | class ConfigureDebugTab; |
| @@ -75,6 +76,7 @@ private: | |||
| 75 | std::unique_ptr<ConfigurationShared::Builder> builder; | 76 | std::unique_ptr<ConfigurationShared::Builder> builder; |
| 76 | std::vector<ConfigurationShared::Tab*> tab_group; | 77 | std::vector<ConfigurationShared::Tab*> tab_group; |
| 77 | 78 | ||
| 79 | std::unique_ptr<ConfigureApplets> applets_tab; | ||
| 78 | std::unique_ptr<ConfigureAudio> audio_tab; | 80 | std::unique_ptr<ConfigureAudio> audio_tab; |
| 79 | std::unique_ptr<ConfigureCpu> cpu_tab; | 81 | std::unique_ptr<ConfigureCpu> cpu_tab; |
| 80 | std::unique_ptr<ConfigureDebugTab> debug_tab_tab; | 82 | std::unique_ptr<ConfigureDebugTab> debug_tab_tab; |
diff --git a/src/yuzu/configuration/configure_hotkeys.cpp b/src/yuzu/configuration/configure_hotkeys.cpp index 3d18670ce..3f68de12d 100644 --- a/src/yuzu/configuration/configure_hotkeys.cpp +++ b/src/yuzu/configuration/configure_hotkeys.cpp | |||
| @@ -45,15 +45,23 @@ ConfigureHotkeys::ConfigureHotkeys(Core::HID::HIDCore& hid_core, QWidget* parent | |||
| 45 | 45 | ||
| 46 | controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1); | 46 | controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1); |
| 47 | 47 | ||
| 48 | connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); }); | 48 | connect(timeout_timer.get(), &QTimer::timeout, [this] { |
| 49 | const bool is_button_pressed = pressed_buttons != Core::HID::NpadButton::None || | ||
| 50 | pressed_home_button || pressed_capture_button; | ||
| 51 | SetPollingResult(!is_button_pressed); | ||
| 52 | }); | ||
| 49 | 53 | ||
| 50 | connect(poll_timer.get(), &QTimer::timeout, [this] { | 54 | connect(poll_timer.get(), &QTimer::timeout, [this] { |
| 51 | const auto buttons = controller->GetNpadButtons(); | 55 | pressed_buttons |= controller->GetNpadButtons().raw; |
| 52 | const auto home_pressed = controller->GetHomeButtons().home != 0; | 56 | pressed_home_button |= this->controller->GetHomeButtons().home != 0; |
| 53 | const auto capture_pressed = controller->GetCaptureButtons().capture != 0; | 57 | pressed_capture_button |= this->controller->GetCaptureButtons().capture != 0; |
| 54 | if (home_pressed || capture_pressed) { | 58 | if (pressed_buttons != Core::HID::NpadButton::None || pressed_home_button || |
| 55 | SetPollingResult(buttons.raw, false); | 59 | pressed_capture_button) { |
| 56 | return; | 60 | const QString button_name = |
| 61 | GetButtonCombinationName(pressed_buttons, pressed_home_button, | ||
| 62 | pressed_capture_button) + | ||
| 63 | QStringLiteral("..."); | ||
| 64 | model->setData(button_model_index, button_name); | ||
| 57 | } | 65 | } |
| 58 | }); | 66 | }); |
| 59 | RetranslateUI(); | 67 | RetranslateUI(); |
| @@ -154,16 +162,14 @@ void ConfigureHotkeys::ConfigureController(QModelIndex index) { | |||
| 154 | 162 | ||
| 155 | const auto previous_key = model->data(index); | 163 | const auto previous_key = model->data(index); |
| 156 | 164 | ||
| 157 | input_setter = [this, index, previous_key](const Core::HID::NpadButton button, | 165 | input_setter = [this, index, previous_key](const bool cancel) { |
| 158 | const bool cancel) { | ||
| 159 | if (cancel) { | 166 | if (cancel) { |
| 160 | model->setData(index, previous_key); | 167 | model->setData(index, previous_key); |
| 161 | return; | 168 | return; |
| 162 | } | 169 | } |
| 163 | const auto home_pressed = this->controller->GetHomeButtons().home != 0; | 170 | |
| 164 | const auto capture_pressed = this->controller->GetCaptureButtons().capture != 0; | ||
| 165 | const QString button_string = | 171 | const QString button_string = |
| 166 | GetButtonCombinationName(button, home_pressed, capture_pressed); | 172 | GetButtonCombinationName(pressed_buttons, pressed_home_button, pressed_capture_button); |
| 167 | 173 | ||
| 168 | const auto [key_sequence_used, used_action] = IsUsedControllerKey(button_string); | 174 | const auto [key_sequence_used, used_action] = IsUsedControllerKey(button_string); |
| 169 | 175 | ||
| @@ -177,17 +183,22 @@ void ConfigureHotkeys::ConfigureController(QModelIndex index) { | |||
| 177 | } | 183 | } |
| 178 | }; | 184 | }; |
| 179 | 185 | ||
| 186 | button_model_index = index; | ||
| 187 | pressed_buttons = Core::HID::NpadButton::None; | ||
| 188 | pressed_home_button = false; | ||
| 189 | pressed_capture_button = false; | ||
| 190 | |||
| 180 | model->setData(index, tr("[waiting]")); | 191 | model->setData(index, tr("[waiting]")); |
| 181 | timeout_timer->start(2500); // Cancel after 2.5 seconds | 192 | timeout_timer->start(2500); // Cancel after 2.5 seconds |
| 182 | poll_timer->start(200); // Check for new inputs every 200ms | 193 | poll_timer->start(100); // Check for new inputs every 100ms |
| 183 | // We need to disable configuration to be able to read npad buttons | 194 | // We need to disable configuration to be able to read npad buttons |
| 184 | controller->DisableConfiguration(); | 195 | controller->DisableConfiguration(); |
| 185 | } | 196 | } |
| 186 | 197 | ||
| 187 | void ConfigureHotkeys::SetPollingResult(Core::HID::NpadButton button, const bool cancel) { | 198 | void ConfigureHotkeys::SetPollingResult(const bool cancel) { |
| 188 | timeout_timer->stop(); | 199 | timeout_timer->stop(); |
| 189 | poll_timer->stop(); | 200 | poll_timer->stop(); |
| 190 | (*input_setter)(button, cancel); | 201 | (*input_setter)(cancel); |
| 191 | // Re-Enable configuration | 202 | // Re-Enable configuration |
| 192 | controller->EnableConfiguration(); | 203 | controller->EnableConfiguration(); |
| 193 | 204 | ||
diff --git a/src/yuzu/configuration/configure_hotkeys.h b/src/yuzu/configuration/configure_hotkeys.h index 5fd1bcbfe..20ea3b515 100644 --- a/src/yuzu/configuration/configure_hotkeys.h +++ b/src/yuzu/configuration/configure_hotkeys.h | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <memory> | 6 | #include <memory> |
| 7 | #include <QStandardItemModel> | ||
| 7 | #include <QWidget> | 8 | #include <QWidget> |
| 8 | 9 | ||
| 9 | namespace Common { | 10 | namespace Common { |
| @@ -54,14 +55,20 @@ private: | |||
| 54 | void RestoreControllerHotkey(QModelIndex index); | 55 | void RestoreControllerHotkey(QModelIndex index); |
| 55 | void RestoreHotkey(QModelIndex index); | 56 | void RestoreHotkey(QModelIndex index); |
| 56 | 57 | ||
| 58 | void SetPollingResult(bool cancel); | ||
| 59 | QString GetButtonCombinationName(Core::HID::NpadButton button, bool home, bool capture) const; | ||
| 60 | |||
| 57 | std::unique_ptr<Ui::ConfigureHotkeys> ui; | 61 | std::unique_ptr<Ui::ConfigureHotkeys> ui; |
| 58 | 62 | ||
| 59 | QStandardItemModel* model; | 63 | QStandardItemModel* model; |
| 60 | 64 | ||
| 61 | void SetPollingResult(Core::HID::NpadButton button, bool cancel); | 65 | bool pressed_home_button; |
| 62 | QString GetButtonCombinationName(Core::HID::NpadButton button, bool home, bool capture) const; | 66 | bool pressed_capture_button; |
| 67 | QModelIndex button_model_index; | ||
| 68 | Core::HID::NpadButton pressed_buttons; | ||
| 69 | |||
| 63 | Core::HID::EmulatedController* controller; | 70 | Core::HID::EmulatedController* controller; |
| 64 | std::unique_ptr<QTimer> timeout_timer; | 71 | std::unique_ptr<QTimer> timeout_timer; |
| 65 | std::unique_ptr<QTimer> poll_timer; | 72 | std::unique_ptr<QTimer> poll_timer; |
| 66 | std::optional<std::function<void(Core::HID::NpadButton, bool)>> input_setter; | 73 | std::optional<std::function<void(bool)>> input_setter; |
| 67 | }; | 74 | }; |
diff --git a/src/yuzu/configuration/configure_ui.cpp b/src/yuzu/configuration/configure_ui.cpp index c8e871151..f3c91586c 100644 --- a/src/yuzu/configuration/configure_ui.cpp +++ b/src/yuzu/configuration/configure_ui.cpp | |||
| @@ -248,82 +248,22 @@ void ConfigureUi::RetranslateUI() { | |||
| 248 | } | 248 | } |
| 249 | 249 | ||
| 250 | void ConfigureUi::InitializeLanguageComboBox() { | 250 | void ConfigureUi::InitializeLanguageComboBox() { |
| 251 | // This is a list of lexicographically sorted languages, only the available translations are | ||
| 252 | // shown to the user. | ||
| 253 | static const struct { | ||
| 254 | const QString name; | ||
| 255 | const char* id; | ||
| 256 | } languages[] = { | ||
| 257 | // clang-format off | ||
| 258 | {QStringLiteral(u"Bahasa Indonesia"), "id"}, // Indonesian | ||
| 259 | {QStringLiteral(u"Bahasa Melayu"), "ms"}, // Malay | ||
| 260 | {QStringLiteral(u"Catal\u00E0"), "ca"}, // Catalan | ||
| 261 | {QStringLiteral(u"\u010Ce\u0161tina"), "cs"}, // Czech | ||
| 262 | {QStringLiteral(u"Dansk"), "da"}, // Danish | ||
| 263 | {QStringLiteral(u"Deutsch"), "de"}, // German | ||
| 264 | {QStringLiteral(u"English"), "en"}, // English | ||
| 265 | {QStringLiteral(u"Espa\u00F1ol"), "es"}, // Spanish | ||
| 266 | {QStringLiteral(u"Fran\u00E7ais"), "fr"}, // French | ||
| 267 | {QStringLiteral(u"Hrvatski"), "hr"}, // Croatian | ||
| 268 | {QStringLiteral(u"Italiano"), "it"}, // Italian | ||
| 269 | {QStringLiteral(u"Magyar"), "hu"}, // Hungarian | ||
| 270 | {QStringLiteral(u"Nederlands"), "nl"}, // Dutch | ||
| 271 | {QStringLiteral(u"Norsk bokm\u00E5l"), "nb"}, // Norwegian | ||
| 272 | {QStringLiteral(u"Polski"), "pl"}, // Polish | ||
| 273 | {QStringLiteral(u"Portugu\u00EAs"), "pt_PT"}, // Portuguese | ||
| 274 | {QStringLiteral(u"Portugu\u00EAs (Brasil)"), "pt_BR"}, // Portuguese (Brazil) | ||
| 275 | {QStringLiteral(u"Rom\u00E2n\u0103"), "ro"}, // Romanian | ||
| 276 | {QStringLiteral(u"Srpski"), "sr"}, // Serbian | ||
| 277 | {QStringLiteral(u"Suomi"), "fi"}, // Finnish | ||
| 278 | {QStringLiteral(u"Svenska"), "sv"}, // Swedish | ||
| 279 | {QStringLiteral(u"Ti\u1EBFng Vi\u1EC7t"), "vi"}, // Vietnamese | ||
| 280 | {QStringLiteral(u"Ti\u1EBFng Vi\u1EC7t (Vi\u1EC7t Nam)"), "vi_VN"}, // Vietnamese | ||
| 281 | {QStringLiteral(u"T\u00FCrk\u00E7e"), "tr_TR"}, // Turkish | ||
| 282 | {QStringLiteral(u"\u0395\u03BB\u03BB\u03B7\u03BD\u03B9\u03BA\u03AC"), "el"}, // Greek | ||
| 283 | {QStringLiteral(u"\u0420\u0443\u0441\u0441\u043A\u0438\u0439"), "ru_RU"}, // Russian | ||
| 284 | {QStringLiteral(u"\u0423\u043A\u0440\u0430\u0457\u043D\u0441\u044C\u043A\u0430"), | ||
| 285 | "uk"}, // Ukrainian | ||
| 286 | {QStringLiteral(u"\u0627\u0644\u0639\u0631\u0628\u064A\u0629"), "ar"}, // Arabic | ||
| 287 | {QStringLiteral(u"\u0641\u0627\u0631\u0633\u06CC"), "fa"}, // Farsi | ||
| 288 | {QStringLiteral(u"\uD55C\uAD6D\uC5B4"), "ko_KR"}, // Korean | ||
| 289 | {QStringLiteral(u"\u65E5\u672C\u8A9E"), "ja_JP"}, // Japanese | ||
| 290 | {QStringLiteral(u"\u7B80\u4F53\u4E2D\u6587"), "zh_CN"}, // Simplified Chinese | ||
| 291 | {QStringLiteral(u"\u7E41\u9AD4\u4E2D\u6587"), "zh_TW"}, // Traditional Chinese | ||
| 292 | // clang-format on | ||
| 293 | }; | ||
| 294 | ui->language_combobox->addItem(tr("<System>"), QString{}); | 251 | ui->language_combobox->addItem(tr("<System>"), QString{}); |
| 295 | QDir languages_dir{QStringLiteral(":/languages")}; | 252 | ui->language_combobox->addItem(tr("English"), QStringLiteral("en")); |
| 296 | QStringList language_files = languages_dir.entryList(); | 253 | QDirIterator it(QStringLiteral(":/languages"), QDirIterator::NoIteratorFlags); |
| 297 | for (const auto& lang : languages) { | 254 | while (it.hasNext()) { |
| 298 | if (QString::fromLatin1(lang.id) == QStringLiteral("en")) { | 255 | QString locale = it.next(); |
| 299 | ui->language_combobox->addItem(lang.name, QStringLiteral("en")); | ||
| 300 | language_files.removeOne(QStringLiteral("en.qm")); | ||
| 301 | continue; | ||
| 302 | } | ||
| 303 | for (int i = 0; i < language_files.size(); ++i) { | ||
| 304 | QString locale = language_files[i]; | ||
| 305 | locale.truncate(locale.lastIndexOf(QLatin1Char{'.'})); | ||
| 306 | if (QString::fromLatin1(lang.id) == locale) { | ||
| 307 | ui->language_combobox->addItem(lang.name, locale); | ||
| 308 | language_files.removeAt(i); | ||
| 309 | break; | ||
| 310 | } | ||
| 311 | } | ||
| 312 | } | ||
| 313 | // Anything remaining will be at the bottom | ||
| 314 | for (const QString& file : language_files) { | ||
| 315 | LOG_CRITICAL(Frontend, "Unexpected Language File: {}", file.toStdString()); | ||
| 316 | QString locale = file; | ||
| 317 | locale.truncate(locale.lastIndexOf(QLatin1Char{'.'})); | 256 | locale.truncate(locale.lastIndexOf(QLatin1Char{'.'})); |
| 318 | const QString language_name = QLocale::languageToString(QLocale(locale).language()); | 257 | locale.remove(0, locale.lastIndexOf(QLatin1Char{'/'}) + 1); |
| 319 | const QString lang = QStringLiteral("%1 [%2]").arg(language_name, locale); | 258 | const QString lang = QLocale::languageToString(QLocale(locale).language()); |
| 320 | ui->language_combobox->addItem(lang, locale); | 259 | const QString country = QLocale::countryToString(QLocale(locale).country()); |
| 260 | ui->language_combobox->addItem(QStringLiteral("%1 (%2)").arg(lang, country), locale); | ||
| 321 | } | 261 | } |
| 322 | 262 | ||
| 323 | // Unlike other configuration changes, interface language changes need to be reflected on the | 263 | // Unlike other configuration changes, interface language changes need to be reflected on the |
| 324 | // interface immediately. This is done by passing a signal to the main window, and then | 264 | // interface immediately. This is done by passing a signal to the main window, and then |
| 325 | // retranslating when passing back. | 265 | // retranslating when passing back. |
| 326 | connect(ui->language_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, | 266 | connect(ui->language_combobox, qOverload<int>(&QComboBox::currentIndexChanged), this, |
| 327 | &ConfigureUi::OnLanguageChanged); | 267 | &ConfigureUi::OnLanguageChanged); |
| 328 | } | 268 | } |
| 329 | 269 | ||
diff --git a/src/yuzu/configuration/shared_translation.cpp b/src/yuzu/configuration/shared_translation.cpp index 2f274c0e5..d138b53c8 100644 --- a/src/yuzu/configuration/shared_translation.cpp +++ b/src/yuzu/configuration/shared_translation.cpp | |||
| @@ -26,6 +26,23 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) { | |||
| 26 | 26 | ||
| 27 | // A setting can be ignored by giving it a blank name | 27 | // A setting can be ignored by giving it a blank name |
| 28 | 28 | ||
| 29 | // Applets | ||
| 30 | INSERT(Settings, cabinet_applet_mode, tr("Amiibo editor"), QStringLiteral()); | ||
| 31 | INSERT(Settings, controller_applet_mode, tr("Controller configuration"), QStringLiteral()); | ||
| 32 | INSERT(Settings, data_erase_applet_mode, tr("Data erase"), QStringLiteral()); | ||
| 33 | INSERT(Settings, error_applet_mode, tr("Error"), QStringLiteral()); | ||
| 34 | INSERT(Settings, net_connect_applet_mode, tr("Net connect"), QStringLiteral()); | ||
| 35 | INSERT(Settings, player_select_applet_mode, tr("Player select"), QStringLiteral()); | ||
| 36 | INSERT(Settings, swkbd_applet_mode, tr("Software keyboard"), QStringLiteral()); | ||
| 37 | INSERT(Settings, mii_edit_applet_mode, tr("Mii Edit"), QStringLiteral()); | ||
| 38 | INSERT(Settings, web_applet_mode, tr("Online web"), QStringLiteral()); | ||
| 39 | INSERT(Settings, shop_applet_mode, tr("Shop"), QStringLiteral()); | ||
| 40 | INSERT(Settings, photo_viewer_applet_mode, tr("Photo viewer"), QStringLiteral()); | ||
| 41 | INSERT(Settings, offline_web_applet_mode, tr("Offline web"), QStringLiteral()); | ||
| 42 | INSERT(Settings, login_share_applet_mode, tr("Login share"), QStringLiteral()); | ||
| 43 | INSERT(Settings, wifi_web_auth_applet_mode, tr("Wifi web auth"), QStringLiteral()); | ||
| 44 | INSERT(Settings, my_page_applet_mode, tr("My page"), QStringLiteral()); | ||
| 45 | |||
| 29 | // Audio | 46 | // Audio |
| 30 | INSERT(Settings, sink_id, tr("Output Engine:"), QStringLiteral()); | 47 | INSERT(Settings, sink_id, tr("Output Engine:"), QStringLiteral()); |
| 31 | INSERT(Settings, audio_output_device_id, tr("Output Device:"), QStringLiteral()); | 48 | INSERT(Settings, audio_output_device_id, tr("Output Device:"), QStringLiteral()); |
| @@ -279,6 +296,11 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent) { | |||
| 279 | #define PAIR(ENUM, VALUE, TRANSLATION) {static_cast<u32>(Settings::ENUM::VALUE), (TRANSLATION)} | 296 | #define PAIR(ENUM, VALUE, TRANSLATION) {static_cast<u32>(Settings::ENUM::VALUE), (TRANSLATION)} |
| 280 | 297 | ||
| 281 | // Intentionally skipping VSyncMode to let the UI fill that one out | 298 | // Intentionally skipping VSyncMode to let the UI fill that one out |
| 299 | translations->insert({Settings::EnumMetadata<Settings::AppletMode>::Index(), | ||
| 300 | { | ||
| 301 | PAIR(AppletMode, HLE, tr("Custom frontend")), | ||
| 302 | PAIR(AppletMode, LLE, tr("Real applet")), | ||
| 303 | }}); | ||
| 282 | 304 | ||
| 283 | translations->insert({Settings::EnumMetadata<Settings::AstcDecodeMode>::Index(), | 305 | translations->insert({Settings::EnumMetadata<Settings::AstcDecodeMode>::Index(), |
| 284 | { | 306 | { |
diff --git a/src/yuzu/hotkeys.cpp b/src/yuzu/hotkeys.cpp index b7693ad0d..170f14684 100644 --- a/src/yuzu/hotkeys.cpp +++ b/src/yuzu/hotkeys.cpp | |||
| @@ -193,8 +193,7 @@ void ControllerShortcut::ControllerUpdateEvent(Core::HID::ControllerTriggerType | |||
| 193 | if (!Settings::values.controller_navigation) { | 193 | if (!Settings::values.controller_navigation) { |
| 194 | return; | 194 | return; |
| 195 | } | 195 | } |
| 196 | if (button_sequence.npad.raw == Core::HID::NpadButton::None && | 196 | if (button_sequence.npad.raw == Core::HID::NpadButton::None) { |
| 197 | button_sequence.capture.raw == 0 && button_sequence.home.raw == 0) { | ||
| 198 | return; | 197 | return; |
| 199 | } | 198 | } |
| 200 | 199 | ||
diff --git a/src/yuzu/multiplayer/lobby_p.h b/src/yuzu/multiplayer/lobby_p.h index 398833e7a..77ec1fcde 100644 --- a/src/yuzu/multiplayer/lobby_p.h +++ b/src/yuzu/multiplayer/lobby_p.h | |||
| @@ -202,12 +202,19 @@ public: | |||
| 202 | case Qt::ForegroundRole: { | 202 | case Qt::ForegroundRole: { |
| 203 | auto members = data(MemberListRole).toList(); | 203 | auto members = data(MemberListRole).toList(); |
| 204 | auto max_players = data(MaxPlayerRole).toInt(); | 204 | auto max_players = data(MaxPlayerRole).toInt(); |
| 205 | const QColor room_full_color(255, 48, 32); | ||
| 206 | const QColor room_almost_full_color(255, 140, 32); | ||
| 207 | const QColor room_has_players_color(32, 160, 32); | ||
| 208 | const QColor room_empty_color(128, 128, 128); | ||
| 209 | |||
| 205 | if (members.size() >= max_players) { | 210 | if (members.size() >= max_players) { |
| 206 | return QBrush(QColor(255, 48, 32)); | 211 | return QBrush(room_full_color); |
| 207 | } else if (members.size() == (max_players - 1)) { | 212 | } else if (members.size() == (max_players - 1)) { |
| 208 | return QBrush(QColor(255, 140, 32)); | 213 | return QBrush(room_almost_full_color); |
| 209 | } else if (members.size() == 0) { | 214 | } else if (members.size() == 0) { |
| 210 | return QBrush(QColor(128, 128, 128)); | 215 | return QBrush(room_empty_color); |
| 216 | } else if (members.size() > 0 && members.size() < (max_players - 1)) { | ||
| 217 | return QBrush(room_has_players_color); | ||
| 211 | } | 218 | } |
| 212 | // FIXME: How to return a value that tells Qt not to modify the | 219 | // FIXME: How to return a value that tells Qt not to modify the |
| 213 | // text color from the default (as if Qt::ForegroundRole wasn't overridden)? | 220 | // text color from the default (as if Qt::ForegroundRole wasn't overridden)? |