Compare commits

..

28 Commits

Author SHA1 Message Date
4634c092d1 Android 182 2024-01-04 21:22:55 +00:00
8b27dd4442 Merge yuzu-emu#12576 2024-01-04 21:22:54 +00:00
797970093f Merge yuzu-emu#12560 2024-01-04 21:22:54 +00:00
92a331af76 Merge pull request #12437 from ameerj/gl-amd-fixes
OpenGL: Fixes and workaround updates for AMD
2024-01-04 15:53:44 -05:00
a8f62bff43 Merge pull request #12575 from t895/inconsistent-settings-application
frontend_common: config: Only write setting related to opened config file
2024-01-04 15:51:24 -05:00
519904e8a8 Merge pull request #12571 from t895/apply-orientation-on-start
android: Expose more orientation options
2024-01-04 15:51:08 -05:00
8d3463dbdd Merge pull request #12570 from t895/generic-config-pain
frontend_common: config: Move WriteIntegerSetting definition to header
2024-01-04 15:51:00 -05:00
b125cb97a2 Merge pull request #12568 from t895/actions-button
actions: android: Allow for manually triggering Android builds
2024-01-04 15:50:54 -05:00
d7e7a69e00 Merge pull request #12558 from t895/zip-storage-method
android: Disable compression for zip exports
2024-01-04 15:50:47 -05:00
246cffb624 Merge pull request #12557 from merryhime/termination-ipi
KThread: Send termination interrupt to all cores a thread has affinity to
2024-01-04 15:50:40 -05:00
0e93cad4f0 Merge pull request #12549 from german77/npadresource
service: hid: Implement NpadResource and NpadData
2024-01-04 15:50:33 -05:00
39d28a5131 android: Save global config synchronously in onCloseGameFoldersFragment
Could cause multiple global saves at once that went untracked previously
2024-01-03 22:50:21 -05:00
fa04dea7c4 frontend_common: config: Only write setting related to opened config file
If we tried to write a switchable setting to config that was not using global in the global config instance, we could write the per-game setting accidentally. This ensures that we always use the global setting for global config and the currently applied setting for custom config.
2024-01-03 22:49:41 -05:00
1c278974a8 android: Don't save settings on config destruction
Android doesn't have a proper way of destroying its config object so it's best to stick to the built-in methods to control saving config
2024-01-03 22:12:15 -05:00
2b838b6d06 android: Update orientation on emulation fragment start 2024-01-03 21:35:45 -05:00
82ea082997 android: Expose all orientation locking options 2024-01-03 21:35:45 -05:00
5562322290 service: hid: Remove data races when handling shared memory 2024-01-03 20:21:16 -06:00
6a244465ce service: hid: Implement NpadResource and NpadData 2024-01-03 20:21:14 -06:00
bdf87ba0f8 frontend_common: config: Move WriteIntegerSetting definition to header
Fixes a compiler error where the compiler could not see the definition of the method in qt_config and others.
2024-01-03 17:57:20 -05:00
3b314a68a1 actions: android: Prevent resolving tag commit to PR link 2024-01-03 17:01:31 -05:00
06c68fb196 actions: android: Resolve PR commits to link 2024-01-03 17:00:49 -05:00
9a31122c82 actions: android: Move trigger logic to be yuzu-android specific 2024-01-03 03:15:07 -05:00
dace726d08 android: Add internal option to disable compression for zip exports
Disables compression for user data and save exports
2024-01-02 18:11:22 -05:00
0f7fc94111 KThread: Send termination interrupt to all cores a thread has affinity to
KThread::RequestTerminate may run from a thread which is not the CurrentCore, and thus
masking this out is erroneous.
2024-01-02 21:34:34 +00:00
139b4cc9ea Settings: Indicate AMD's compatibility with SPIR-V on OGL 2023-12-21 22:00:49 -05:00
d5d0d2cb0e spirv_emit_context: Fix BaseInstance for OGL spirv 2023-12-21 21:53:24 -05:00
a5b2b8b91b emit_glsl_image: Use inlined texelFetch offsets 2023-12-20 19:24:11 -05:00
b4b301d22e gl_device: Remove AMD blacklists that are no longer applicable 2023-12-20 18:19:15 -05:00
26 changed files with 405 additions and 80 deletions

View File

@ -10,7 +10,7 @@ const CHANGE_LABEL = 'android-merge';
// how far back in time should we consider the changes are "recent"? (default: 24 hours) // how far back in time should we consider the changes are "recent"? (default: 24 hours)
const DETECTION_TIME_FRAME = (parseInt(process.env.DETECTION_TIME_FRAME)) || (24 * 3600 * 1000); const DETECTION_TIME_FRAME = (parseInt(process.env.DETECTION_TIME_FRAME)) || (24 * 3600 * 1000);
async function checkBaseChanges(github, context) { async function checkBaseChanges(github) {
// query the commit date of the latest commit on this branch // query the commit date of the latest commit on this branch
const query = `query($owner:String!, $name:String!, $ref:String!) { const query = `query($owner:String!, $name:String!, $ref:String!) {
repository(name:$name, owner:$owner) { repository(name:$name, owner:$owner) {
@ -22,8 +22,8 @@ async function checkBaseChanges(github, context) {
} }
}`; }`;
const variables = { const variables = {
owner: context.repo.owner, owner: 'yuzu-emu',
name: context.repo.repo, name: 'yuzu',
ref: 'refs/heads/master', ref: 'refs/heads/master',
}; };
const result = await github.graphql(query, variables); const result = await github.graphql(query, variables);
@ -38,8 +38,8 @@ async function checkBaseChanges(github, context) {
return false; return false;
} }
async function checkAndroidChanges(github, context) { async function checkAndroidChanges(github) {
if (checkBaseChanges(github, context)) return true; if (checkBaseChanges(github)) return true;
const query = `query($owner:String!, $name:String!, $label:String!) { const query = `query($owner:String!, $name:String!, $label:String!) {
repository(name:$name, owner:$owner) { repository(name:$name, owner:$owner) {
pullRequests(labels: [$label], states: OPEN, first: 100) { pullRequests(labels: [$label], states: OPEN, first: 100) {
@ -48,8 +48,8 @@ async function checkAndroidChanges(github, context) {
} }
}`; }`;
const variables = { const variables = {
owner: context.repo.owner, owner: 'yuzu-emu',
name: context.repo.repo, name: 'yuzu',
label: CHANGE_LABEL, label: CHANGE_LABEL,
}; };
const result = await github.graphql(query, variables); const result = await github.graphql(query, variables);
@ -90,8 +90,8 @@ async function tagAndPush(github, owner, repo, execa, commit=false) {
console.log(`New tag: ${newTag}`); console.log(`New tag: ${newTag}`);
if (commit) { if (commit) {
let channelName = channel[0].toUpperCase() + channel.slice(1); let channelName = channel[0].toUpperCase() + channel.slice(1);
console.info(`Committing pending commit as ${channelName} #${tagNumber + 1}`); console.info(`Committing pending commit as ${channelName} ${tagNumber + 1}`);
await execa("git", ['commit', '-m', `${channelName} #${tagNumber + 1}`]); await execa("git", ['commit', '-m', `${channelName} ${tagNumber + 1}`]);
} }
console.info('Pushing tags to GitHub ...'); console.info('Pushing tags to GitHub ...');
await execa("git", ['tag', newTag]); await execa("git", ['tag', newTag]);
@ -157,7 +157,7 @@ async function mergePullRequests(pulls, execa) {
process1.stdout.pipe(process.stdout); process1.stdout.pipe(process.stdout);
await process1; await process1;
const process2 = execa("git", ["commit", "-m", `Merge PR ${pr}`]); const process2 = execa("git", ["commit", "-m", `Merge yuzu-emu#${pr}`]);
process2.stdout.pipe(process.stdout); process2.stdout.pipe(process.stdout);
await process2; await process2;
@ -182,7 +182,30 @@ async function mergePullRequests(pulls, execa) {
return mergeResults; return mergeResults;
} }
async function resetBranch(execa) {
console.log("::group::Reset master branch");
let hasFailed = false;
try {
await execa("git", ["remote", "add", "source", "https://github.com/yuzu-emu/yuzu.git"]);
await execa("git", ["fetch", "source"]);
const process1 = await execa("git", ["rev-parse", "source/master"]);
const headCommit = process1.stdout;
await execa("git", ["reset", "--hard", headCommit]);
} catch (err) {
console.log(`::error title=Failed to reset master branch`);
hasFailed = true;
}
console.log("::endgroup::");
if (hasFailed) {
throw 'Failed to reset the master branch. Aborting!';
}
}
async function mergebot(github, context, execa) { async function mergebot(github, context, execa) {
// Reset our local copy of master to what appears on yuzu-emu/yuzu - master
await resetBranch(execa);
const query = `query ($owner:String!, $name:String!, $label:String!) { const query = `query ($owner:String!, $name:String!, $label:String!) {
repository(name:$name, owner:$owner) { repository(name:$name, owner:$owner) {
pullRequests(labels: [$label], states: OPEN, first: 100) { pullRequests(labels: [$label], states: OPEN, first: 100) {
@ -193,8 +216,8 @@ async function mergebot(github, context, execa) {
} }
}`; }`;
const variables = { const variables = {
owner: context.repo.owner, owner: 'yuzu-emu',
name: context.repo.repo, name: 'yuzu',
label: CHANGE_LABEL, label: CHANGE_LABEL,
}; };
const result = await github.graphql(query, variables); const result = await github.graphql(query, variables);
@ -209,7 +232,7 @@ async function mergebot(github, context, execa) {
await fetchPullRequests(pulls, "https://github.com/yuzu-emu/yuzu", execa); await fetchPullRequests(pulls, "https://github.com/yuzu-emu/yuzu", execa);
const mergeResults = await mergePullRequests(pulls, execa); const mergeResults = await mergePullRequests(pulls, execa);
await generateReadme(pulls, context, mergeResults, execa); await generateReadme(pulls, context, mergeResults, execa);
await tagAndPush(github, context.repo.owner, `${context.repo.repo}-android`, execa, true); await tagAndPush(github, 'yuzu-emu', `yuzu-android`, execa, true);
} }
module.exports.mergebot = mergebot; module.exports.mergebot = mergebot;

View File

@ -16,7 +16,7 @@ on:
jobs: jobs:
android: android:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ github.event.inputs.android != 'false' && github.repository == 'yuzu-emu/yuzu' }} if: ${{ github.event.inputs.android != 'false' && github.repository == 'yuzu-emu/yuzu-android' }}
steps: steps:
# this checkout is required to make sure the GitHub Actions scripts are available # this checkout is required to make sure the GitHub Actions scripts are available
- uses: actions/checkout@v3 - uses: actions/checkout@v3
@ -33,7 +33,7 @@ jobs:
script: | script: |
if (context.payload.inputs && context.payload.inputs.android === 'true') return true; if (context.payload.inputs && context.payload.inputs.android === 'true') return true;
const checkAndroidChanges = require('./.github/workflows/android-merge.js').checkAndroidChanges; const checkAndroidChanges = require('./.github/workflows/android-merge.js').checkAndroidChanges;
return checkAndroidChanges(github, context); return checkAndroidChanges(github);
- run: npm install execa@5 - run: npm install execa@5
if: ${{ steps.check-changes.outputs.result == 'true' }} if: ${{ steps.check-changes.outputs.result == 'true' }}
- uses: actions/checkout@v3 - uses: actions/checkout@v3

View File

@ -1,8 +1,7 @@
| Pull Request | Commit | Title | Author | Merged? | | Pull Request | Commit | Title | Author | Merged? |
|----|----|----|----|----| |----|----|----|----|----|
| [12549](https://github.com/yuzu-emu/yuzu//pull/12549) | [`27259ff10`](https://github.com/yuzu-emu/yuzu//pull/12549/files) | service: hid: Implement NpadResource and NpadData | [german77](https://github.com/german77/) | Yes | | [12560](https://github.com/yuzu-emu/yuzu-android//pull/12560) | [`e5de3d5a7`](https://github.com/yuzu-emu/yuzu-android//pull/12560/files) | android: add basic support for google game dashboard | [GayPotatoEmma](https://github.com/GayPotatoEmma/) | Yes |
| [12557](https://github.com/yuzu-emu/yuzu//pull/12557) | [`0f7fc9411`](https://github.com/yuzu-emu/yuzu//pull/12557/files) | KThread: Send termination interrupt to all cores a thread has affinity to | [merryhime](https://github.com/merryhime/) | Yes | | [12576](https://github.com/yuzu-emu/yuzu-android//pull/12576) | [`53d4dbacf`](https://github.com/yuzu-emu/yuzu-android//pull/12576/files) | android: Re-add global save manager | [t895](https://github.com/t895/) | Yes |
| [12558](https://github.com/yuzu-emu/yuzu//pull/12558) | [`dace726d0`](https://github.com/yuzu-emu/yuzu//pull/12558/files) | android: Disable compression for zip exports | [t895](https://github.com/t895/) | Yes |
End of merge log. You can find the original README.md below the break. End of merge log. You can find the original README.md below the break.

View File

@ -31,6 +31,9 @@ SPDX-License-Identifier: GPL-3.0-or-later
android:dataExtractionRules="@xml/data_extraction_rules_api_31" android:dataExtractionRules="@xml/data_extraction_rules_api_31"
android:enableOnBackInvokedCallback="true"> android:enableOnBackInvokedCallback="true">
<meta-data android:name="android.game_mode_config"
android:resource="@xml/game_mode_config" />
<activity <activity
android:name="org.yuzu.yuzu_emu.ui.main.MainActivity" android:name="org.yuzu.yuzu_emu.ui.main.MainActivity"
android:exported="true" android:exported="true"

View File

@ -547,6 +547,15 @@ object NativeLibrary {
*/ */
external fun getSavePath(programId: String): String external fun getSavePath(programId: String): String
/**
* Gets the root save directory for the default profile as either
* /user/save/account/<user id raw string> or /user/save/000...000/<user id>
*
* @param future If true, returns the /user/save/account/... directory
* @return Save data path that may not exist yet
*/
external fun getDefaultProfileSaveDataRoot(future: Boolean): String
/** /**
* Adds a file to the manual filesystem provider in our EmulationSession instance * Adds a file to the manual filesystem provider in our EmulationSession instance
* @param path Path to the file we're adding. Can be a string representation of a [Uri] or * @param path Path to the file we're adding. Can be a string representation of a [Uri] or

View File

@ -79,7 +79,18 @@ object Settings {
const val PREF_THEME_MODE = "ThemeMode" const val PREF_THEME_MODE = "ThemeMode"
const val PREF_BLACK_BACKGROUNDS = "BlackBackgrounds" const val PREF_BLACK_BACKGROUNDS = "BlackBackgrounds"
const val LayoutOption_Unspecified = 0 enum class EmulationOrientation(val int: Int) {
const val LayoutOption_MobilePortrait = 4 Unspecified(0),
const val LayoutOption_MobileLandscape = 5 SensorLandscape(5),
Landscape(1),
ReverseLandscape(2),
SensorPortrait(6),
Portrait(4),
ReversePortrait(3);
companion object {
fun from(int: Int): EmulationOrientation =
entries.firstOrNull { it.int == int } ?: Unspecified
}
}
} }

View File

@ -50,6 +50,7 @@ import org.yuzu.yuzu_emu.databinding.FragmentEmulationBinding
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
import org.yuzu.yuzu_emu.features.settings.model.IntSetting import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.features.settings.model.Settings import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.features.settings.model.Settings.EmulationOrientation
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
import org.yuzu.yuzu_emu.model.DriverViewModel import org.yuzu.yuzu_emu.model.DriverViewModel
import org.yuzu.yuzu_emu.model.Game import org.yuzu.yuzu_emu.model.Game
@ -99,6 +100,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
*/ */
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
updateOrientation()
val intentUri: Uri? = requireActivity().intent.data val intentUri: Uri? = requireActivity().intent.data
var intentGame: Game? = null var intentGame: Game? = null
@ -458,13 +460,23 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
@SuppressLint("SourceLockedOrientationActivity") @SuppressLint("SourceLockedOrientationActivity")
private fun updateOrientation() { private fun updateOrientation() {
emulationActivity?.let { emulationActivity?.let {
it.requestedOrientation = when (IntSetting.RENDERER_SCREEN_LAYOUT.getInt()) { val orientationSetting =
Settings.LayoutOption_MobileLandscape -> EmulationOrientation.from(IntSetting.RENDERER_SCREEN_LAYOUT.getInt())
it.requestedOrientation = when (orientationSetting) {
EmulationOrientation.Unspecified -> ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
EmulationOrientation.SensorLandscape ->
ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
Settings.LayoutOption_MobilePortrait ->
ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT EmulationOrientation.Landscape -> ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
Settings.LayoutOption_Unspecified -> ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED EmulationOrientation.ReverseLandscape ->
else -> ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE
EmulationOrientation.SensorPortrait ->
ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT
EmulationOrientation.Portrait -> ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
EmulationOrientation.ReversePortrait ->
ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT
} }
} }
} }
@ -651,7 +663,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
@SuppressLint("SourceLockedOrientationActivity") @SuppressLint("SourceLockedOrientationActivity")
private fun startConfiguringControls() { private fun startConfiguringControls() {
// Lock the current orientation to prevent editing inconsistencies // Lock the current orientation to prevent editing inconsistencies
if (IntSetting.RENDERER_SCREEN_LAYOUT.getInt() == Settings.LayoutOption_Unspecified) { if (IntSetting.RENDERER_SCREEN_LAYOUT.getInt() == EmulationOrientation.Unspecified.int) {
emulationActivity?.let { emulationActivity?.let {
it.requestedOrientation = it.requestedOrientation =
if (resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT) { if (resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT) {
@ -669,7 +681,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
binding.doneControlConfig.visibility = View.GONE binding.doneControlConfig.visibility = View.GONE
binding.surfaceInputOverlay.setIsInEditMode(false) binding.surfaceInputOverlay.setIsInEditMode(false)
// Unlock the orientation if it was locked for editing // Unlock the orientation if it was locked for editing
if (IntSetting.RENDERER_SCREEN_LAYOUT.getInt() == Settings.LayoutOption_Unspecified) { if (IntSetting.RENDERER_SCREEN_LAYOUT.getInt() == EmulationOrientation.Unspecified.int) {
emulationActivity?.let { emulationActivity?.let {
it.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED it.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
} }

View File

@ -7,20 +7,39 @@ import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.findNavController import androidx.navigation.findNavController
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import com.google.android.material.transition.MaterialSharedAxis import com.google.android.material.transition.MaterialSharedAxis
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.adapters.InstallableAdapter import org.yuzu.yuzu_emu.adapters.InstallableAdapter
import org.yuzu.yuzu_emu.databinding.FragmentInstallablesBinding import org.yuzu.yuzu_emu.databinding.FragmentInstallablesBinding
import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.model.Installable import org.yuzu.yuzu_emu.model.Installable
import org.yuzu.yuzu_emu.model.TaskState
import org.yuzu.yuzu_emu.ui.main.MainActivity import org.yuzu.yuzu_emu.ui.main.MainActivity
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.FileUtil
import java.io.BufferedInputStream
import java.io.BufferedOutputStream
import java.io.File
import java.math.BigInteger
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
class InstallableFragment : Fragment() { class InstallableFragment : Fragment() {
private var _binding: FragmentInstallablesBinding? = null private var _binding: FragmentInstallablesBinding? = null
@ -56,6 +75,17 @@ class InstallableFragment : Fragment() {
binding.root.findNavController().popBackStack() binding.root.findNavController().popBackStack()
} }
viewLifecycleOwner.lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
homeViewModel.openImportSaves.collect {
if (it) {
importSaves.launch(arrayOf("application/zip"))
homeViewModel.setOpenImportSaves(false)
}
}
}
}
val installables = listOf( val installables = listOf(
Installable( Installable(
R.string.user_data, R.string.user_data,
@ -63,6 +93,43 @@ class InstallableFragment : Fragment() {
install = { mainActivity.importUserData.launch(arrayOf("application/zip")) }, install = { mainActivity.importUserData.launch(arrayOf("application/zip")) },
export = { mainActivity.exportUserData.launch("export.zip") } export = { mainActivity.exportUserData.launch("export.zip") }
), ),
Installable(
R.string.manage_save_data,
R.string.manage_save_data_description,
install = {
MessageDialogFragment.newInstance(
requireActivity(),
titleId = R.string.import_save_warning,
descriptionId = R.string.import_save_warning_description,
positiveAction = { homeViewModel.setOpenImportSaves(true) }
).show(parentFragmentManager, MessageDialogFragment.TAG)
},
export = {
val oldSaveDataFolder = File(
"${DirectoryInitialization.userDirectory}/nand" +
NativeLibrary.getDefaultProfileSaveDataRoot(false)
)
val futureSaveDataFolder = File(
"${DirectoryInitialization.userDirectory}/nand" +
NativeLibrary.getDefaultProfileSaveDataRoot(true)
)
if (!oldSaveDataFolder.exists() && !futureSaveDataFolder.exists()) {
Toast.makeText(
YuzuApplication.appContext,
R.string.no_save_data_found,
Toast.LENGTH_SHORT
).show()
return@Installable
} else {
exportSaves.launch(
"${getString(R.string.save_data)} " +
LocalDateTime.now().format(
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")
)
)
}
}
),
Installable( Installable(
R.string.install_game_content, R.string.install_game_content,
R.string.install_game_content_description, R.string.install_game_content_description,
@ -121,4 +188,156 @@ class InstallableFragment : Fragment() {
windowInsets windowInsets
} }
private val importSaves =
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
if (result == null) {
return@registerForActivityResult
}
val inputZip = requireContext().contentResolver.openInputStream(result)
val cacheSaveDir = File("${requireContext().cacheDir.path}/saves/")
cacheSaveDir.mkdir()
if (inputZip == null) {
Toast.makeText(
YuzuApplication.appContext,
getString(R.string.fatal_error),
Toast.LENGTH_LONG
).show()
return@registerForActivityResult
}
IndeterminateProgressDialogFragment.newInstance(
requireActivity(),
R.string.save_files_importing,
false
) {
try {
FileUtil.unzipToInternalStorage(BufferedInputStream(inputZip), cacheSaveDir)
val files = cacheSaveDir.listFiles()
var successfulImports = 0
var failedImports = 0
if (files != null) {
for (file in files) {
if (file.isDirectory) {
val baseSaveDir =
NativeLibrary.getSavePath(BigInteger(file.name, 16).toString())
if (baseSaveDir.isEmpty()) {
failedImports++
continue
}
val internalSaveFolder = File(
"${DirectoryInitialization.userDirectory}/nand$baseSaveDir"
)
internalSaveFolder.deleteRecursively()
internalSaveFolder.mkdir()
file.copyRecursively(target = internalSaveFolder, overwrite = true)
successfulImports++
}
}
}
withContext(Dispatchers.Main) {
if (successfulImports == 0) {
MessageDialogFragment.newInstance(
requireActivity(),
titleId = R.string.save_file_invalid_zip_structure,
descriptionId = R.string.save_file_invalid_zip_structure_description
).show(parentFragmentManager, MessageDialogFragment.TAG)
return@withContext
}
val successString = if (failedImports > 0) {
"""
${
requireContext().resources.getQuantityString(
R.plurals.saves_import_success,
successfulImports,
successfulImports
)
}
${
requireContext().resources.getQuantityString(
R.plurals.saves_import_failed,
failedImports,
failedImports
)
}
"""
} else {
requireContext().resources.getQuantityString(
R.plurals.saves_import_success,
successfulImports,
successfulImports
)
}
MessageDialogFragment.newInstance(
requireActivity(),
titleId = R.string.import_complete,
descriptionString = successString
).show(parentFragmentManager, MessageDialogFragment.TAG)
}
cacheSaveDir.deleteRecursively()
} catch (e: Exception) {
Toast.makeText(
YuzuApplication.appContext,
getString(R.string.fatal_error),
Toast.LENGTH_LONG
).show()
}
}.show(parentFragmentManager, IndeterminateProgressDialogFragment.TAG)
}
private val exportSaves = registerForActivityResult(
ActivityResultContracts.CreateDocument("application/zip")
) { result ->
if (result == null) {
return@registerForActivityResult
}
IndeterminateProgressDialogFragment.newInstance(
requireActivity(),
R.string.save_files_exporting,
false
) {
val cacheSaveDir = File("${requireContext().cacheDir.path}/saves/")
cacheSaveDir.mkdir()
val oldSaveDataFolder = File(
"${DirectoryInitialization.userDirectory}/nand" +
NativeLibrary.getDefaultProfileSaveDataRoot(false)
)
if (oldSaveDataFolder.exists()) {
oldSaveDataFolder.copyRecursively(cacheSaveDir)
}
val futureSaveDataFolder = File(
"${DirectoryInitialization.userDirectory}/nand" +
NativeLibrary.getDefaultProfileSaveDataRoot(true)
)
if (futureSaveDataFolder.exists()) {
futureSaveDataFolder.copyRecursively(cacheSaveDir)
}
val saveFilesTotal = cacheSaveDir.listFiles()?.size ?: 0
if (saveFilesTotal == 0) {
cacheSaveDir.deleteRecursively()
return@newInstance getString(R.string.no_save_data_found)
}
val zipResult = FileUtil.zipFromInternalStorage(
cacheSaveDir,
cacheSaveDir.path,
BufferedOutputStream(requireContext().contentResolver.openOutputStream(result))
)
cacheSaveDir.deleteRecursively()
return@newInstance when (zipResult) {
TaskState.Completed -> getString(R.string.export_success)
TaskState.Cancelled, TaskState.Failed -> getString(R.string.export_failed)
}
}.show(parentFragmentManager, IndeterminateProgressDialogFragment.TAG)
}
} }

View File

@ -167,13 +167,14 @@ class GamesViewModel : ViewModel() {
} }
} }
fun onCloseGameFoldersFragment() = fun onCloseGameFoldersFragment() {
NativeConfig.saveGlobalConfig()
viewModelScope.launch { viewModelScope.launch {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
NativeConfig.saveGlobalConfig()
getGameDirs(true) getGameDirs(true)
} }
} }
}
private fun getGameDirs(reloadList: Boolean = false) { private fun getGameDirs(reloadList: Boolean = false) {
val gameDirs = NativeConfig.getGameDirs() val gameDirs = NativeConfig.getGameDirs()

View File

@ -14,12 +14,6 @@ AndroidConfig::AndroidConfig(const std::string& config_name, ConfigType config_t
} }
} }
AndroidConfig::~AndroidConfig() {
if (global) {
AndroidConfig::SaveAllValues();
}
}
void AndroidConfig::ReloadAllValues() { void AndroidConfig::ReloadAllValues() {
Reload(); Reload();
ReadAndroidValues(); ReadAndroidValues();

View File

@ -9,7 +9,6 @@ class AndroidConfig final : public Config {
public: public:
explicit AndroidConfig(const std::string& config_name = "config", explicit AndroidConfig(const std::string& config_name = "config",
ConfigType config_type = ConfigType::GlobalConfig); ConfigType config_type = ConfigType::GlobalConfig);
~AndroidConfig() override;
void ReloadAllValues() override; void ReloadAllValues() override;
void SaveAllValues() override; void SaveAllValues() override;

View File

@ -862,6 +862,9 @@ jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getAddonsForFile(JNIEnv* env,
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject jobj, jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject jobj,
jstring jprogramId) { jstring jprogramId) {
auto program_id = EmulationSession::GetProgramId(env, jprogramId); auto program_id = EmulationSession::GetProgramId(env, jprogramId);
if (program_id == 0) {
return ToJString(env, "");
}
auto& system = EmulationSession::GetInstance().System(); auto& system = EmulationSession::GetInstance().System();
@ -880,6 +883,19 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject j
return ToJString(env, user_save_data_path); return ToJString(env, user_save_data_path);
} }
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getDefaultProfileSaveDataRoot(JNIEnv* env,
jobject jobj,
jboolean jfuture) {
Service::Account::ProfileManager manager;
// TODO: Pass in a selected user once we get the relevant UI working
const auto user_id = manager.GetUser(static_cast<std::size_t>(0));
ASSERT(user_id);
const auto user_save_data_root =
FileSys::SaveDataFactory::GetUserGameSaveDataRoot(user_id->AsU128(), jfuture);
return ToJString(env, user_save_data_root);
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_addFileToFilesystemProvider(JNIEnv* env, jobject jobj, void Java_org_yuzu_yuzu_1emu_NativeLibrary_addFileToFilesystemProvider(JNIEnv* env, jobject jobj,
jstring jpath) { jstring jpath) {
EmulationSession::GetInstance().ConfigureFilesystemProvider(GetJString(env, jpath)); EmulationSession::GetInstance().ConfigureFilesystemProvider(GetJString(env, jpath));

View File

@ -118,15 +118,23 @@
</integer-array> </integer-array>
<string-array name="rendererScreenLayoutNames"> <string-array name="rendererScreenLayoutNames">
<item>@string/screen_layout_landscape</item>
<item>@string/screen_layout_portrait</item>
<item>@string/screen_layout_auto</item> <item>@string/screen_layout_auto</item>
<item>@string/screen_layout_sensor_landscape</item>
<item>@string/screen_layout_landscape</item>
<item>@string/screen_layout_reverse_landscape</item>
<item>@string/screen_layout_sensor_portrait</item>
<item>@string/screen_layout_portrait</item>
<item>@string/screen_layout_reverse_portrait</item>
</string-array> </string-array>
<integer-array name="rendererScreenLayoutValues"> <integer-array name="rendererScreenLayoutValues">
<item>5</item>
<item>4</item>
<item>0</item> <item>0</item>
<item>5</item>
<item>1</item>
<item>2</item>
<item>6</item>
<item>4</item>
<item>3</item>
</integer-array> </integer-array>
<string-array name="rendererAspectRatioNames"> <string-array name="rendererAspectRatioNames">

View File

@ -133,6 +133,15 @@
<string name="add_game_folder">Add game folder</string> <string name="add_game_folder">Add game folder</string>
<string name="folder_already_added">This folder was already added!</string> <string name="folder_already_added">This folder was already added!</string>
<string name="game_folder_properties">Game folder properties</string> <string name="game_folder_properties">Game folder properties</string>
<plurals name="saves_import_failed">
<item quantity="one">Failed to import %d save</item>
<item quantity="other">Failed to import %d saves</item>
</plurals>
<plurals name="saves_import_success">
<item quantity="one">Successfully imported %d save</item>
<item quantity="other">Successfully imported %d saves</item>
</plurals>
<string name="no_save_data_found">No save data found</string>
<!-- Applet launcher strings --> <!-- Applet launcher strings -->
<string name="applets">Applet launcher</string> <string name="applets">Applet launcher</string>
@ -276,6 +285,7 @@
<string name="global">Global</string> <string name="global">Global</string>
<string name="custom">Custom</string> <string name="custom">Custom</string>
<string name="notice">Notice</string> <string name="notice">Notice</string>
<string name="import_complete">Import complete</string>
<!-- GPU driver installation --> <!-- GPU driver installation -->
<string name="select_gpu_driver">Select GPU driver</string> <string name="select_gpu_driver">Select GPU driver</string>
@ -463,9 +473,13 @@
<string name="anti_aliasing_smaa">SMAA</string> <string name="anti_aliasing_smaa">SMAA</string>
<!-- Screen Layouts --> <!-- Screen Layouts -->
<string name="screen_layout_landscape">Landscape</string>
<string name="screen_layout_portrait">Portrait</string>
<string name="screen_layout_auto">Auto</string> <string name="screen_layout_auto">Auto</string>
<string name="screen_layout_sensor_landscape">Sensor landscape</string>
<string name="screen_layout_landscape">Landscape</string>
<string name="screen_layout_reverse_landscape">Reverse landscape</string>
<string name="screen_layout_sensor_portrait">Sensor portrait</string>
<string name="screen_layout_portrait">Portrait</string>
<string name="screen_layout_reverse_portrait">Reverse portrait</string>
<!-- Aspect Ratios --> <!-- Aspect Ratios -->
<string name="ratio_default">Default (16:9)</string> <string name="ratio_default">Default (16:9)</string>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<game-mode-config
xmlns:android="http://schemas.android.com/apk/res/android"
android:supportsBatteryGameMode="true"
android:supportsPerformanceGameMode="true"
android:allowGameDownscaling="false"
android:allowGameFpsOverride="false"/>

View File

@ -189,6 +189,15 @@ std::string SaveDataFactory::GetFullPath(Core::System& system, VirtualDir dir,
} }
} }
std::string SaveDataFactory::GetUserGameSaveDataRoot(u128 user_id, bool future) {
if (future) {
Common::UUID uuid;
std::memcpy(uuid.uuid.data(), user_id.data(), sizeof(Common::UUID));
return fmt::format("/user/save/account/{}", uuid.RawString());
}
return fmt::format("/user/save/{:016X}/{:016X}{:016X}", 0, user_id[1], user_id[0]);
}
SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id, SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id,
u128 user_id) const { u128 user_id) const {
const auto path = const auto path =

View File

@ -101,6 +101,7 @@ public:
static std::string GetSaveDataSpaceIdPath(SaveDataSpaceId space); static std::string GetSaveDataSpaceIdPath(SaveDataSpaceId space);
static std::string GetFullPath(Core::System& system, VirtualDir dir, SaveDataSpaceId space, static std::string GetFullPath(Core::System& system, VirtualDir dir, SaveDataSpaceId space,
SaveDataType type, u64 title_id, u128 user_id, u64 save_id); SaveDataType type, u64 title_id, u128 user_id, u64 save_id);
static std::string GetUserGameSaveDataRoot(u128 user_id, bool future);
SaveDataSize ReadSaveDataSize(SaveDataType type, u64 title_id, u128 user_id) const; SaveDataSize ReadSaveDataSize(SaveDataType type, u64 title_id, u128 user_id) const;
void WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id, void WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id,

View File

@ -120,11 +120,12 @@ void NPad::ControllerUpdate(Core::HID::ControllerTriggerType type, std::size_t c
ControllerUpdate(Core::HID::ControllerTriggerType::Battery, controller_idx); ControllerUpdate(Core::HID::ControllerTriggerType::Battery, controller_idx);
return; return;
} }
if (controller_idx >= controller_data.size()) {
for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) {
if (controller_idx >= controller_data[aruid_index].size()) {
return; return;
} }
for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) {
auto* data = applet_resource_holder.applet_resource->GetAruidDataByIndex(aruid_index); auto* data = applet_resource_holder.applet_resource->GetAruidDataByIndex(aruid_index);
if (!data->flag.is_assigned) { if (!data->flag.is_assigned) {
@ -464,7 +465,7 @@ void NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
continue; continue;
} }
for (std::size_t i = 0; i < controller_data.size(); ++i) { for (std::size_t i = 0; i < controller_data[aruid_index].size(); ++i) {
auto& controller = controller_data[aruid_index][i]; auto& controller = controller_data[aruid_index][i];
controller.shared_memory = controller.shared_memory =
&data->shared_memory_format->npad.npad_entry[i].internal_state; &data->shared_memory_format->npad.npad_entry[i].internal_state;

View File

@ -153,6 +153,8 @@ bool NPadData::IsNpadStyleIndexSupported(Core::HID::NpadStyleIndex style_index)
switch (style_index) { switch (style_index) {
case Core::HID::NpadStyleIndex::ProController: case Core::HID::NpadStyleIndex::ProController:
return style.fullkey.As<bool>(); return style.fullkey.As<bool>();
case Core::HID::NpadStyleIndex::Handheld:
return style.handheld.As<bool>();
case Core::HID::NpadStyleIndex::JoyconDual: case Core::HID::NpadStyleIndex::JoyconDual:
return style.joycon_dual.As<bool>(); return style.joycon_dual.As<bool>();
case Core::HID::NpadStyleIndex::JoyconLeft: case Core::HID::NpadStyleIndex::JoyconLeft:

View File

@ -762,17 +762,6 @@ void Config::WriteBooleanSetting(const std::string& key, const bool& value,
WritePreparedSetting(key, AdjustOutputString(ToString(value)), string_default, use_global); WritePreparedSetting(key, AdjustOutputString(ToString(value)), string_default, use_global);
} }
template <typename T>
std::enable_if_t<std::is_integral_v<T>> Config::WriteIntegerSetting(
const std::string& key, const T& value, const std::optional<T>& default_value,
const std::optional<bool>& use_global) {
std::optional<std::string> string_default = std::nullopt;
if (default_value.has_value()) {
string_default = std::make_optional(ToString(default_value.value()));
}
WritePreparedSetting(key, AdjustOutputString(ToString(value)), string_default, use_global);
}
void Config::WriteDoubleSetting(const std::string& key, const double& value, void Config::WriteDoubleSetting(const std::string& key, const double& value,
const std::optional<double>& default_value, const std::optional<double>& default_value,
const std::optional<bool>& use_global) { const std::optional<bool>& use_global) {
@ -894,9 +883,10 @@ void Config::WriteSettingGeneric(const Settings::BasicSetting* const setting) {
WriteBooleanSetting(std::string(key).append("\\use_global"), setting->UsingGlobal()); WriteBooleanSetting(std::string(key).append("\\use_global"), setting->UsingGlobal());
} }
if (global || !setting->UsingGlobal()) { if (global || !setting->UsingGlobal()) {
auto value = global ? setting->ToStringGlobal() : setting->ToString();
WriteBooleanSetting(std::string(key).append("\\default"), WriteBooleanSetting(std::string(key).append("\\default"),
setting->ToString() == setting->DefaultToString()); value == setting->DefaultToString());
WriteStringSetting(key, setting->ToString()); WriteStringSetting(key, value);
} }
} else if (global) { } else if (global) {
WriteBooleanSetting(std::string(key).append("\\default"), WriteBooleanSetting(std::string(key).append("\\default"),

View File

@ -157,17 +157,23 @@ protected:
void WriteBooleanSetting(const std::string& key, const bool& value, void WriteBooleanSetting(const std::string& key, const bool& value,
const std::optional<bool>& default_value = std::nullopt, const std::optional<bool>& default_value = std::nullopt,
const std::optional<bool>& use_global = std::nullopt); const std::optional<bool>& use_global = std::nullopt);
template <typename T>
std::enable_if_t<std::is_integral_v<T>> WriteIntegerSetting(
const std::string& key, const T& value,
const std::optional<T>& default_value = std::nullopt,
const std::optional<bool>& use_global = std::nullopt);
void WriteDoubleSetting(const std::string& key, const double& value, void WriteDoubleSetting(const std::string& key, const double& value,
const std::optional<double>& default_value = std::nullopt, const std::optional<double>& default_value = std::nullopt,
const std::optional<bool>& use_global = std::nullopt); const std::optional<bool>& use_global = std::nullopt);
void WriteStringSetting(const std::string& key, const std::string& value, void WriteStringSetting(const std::string& key, const std::string& value,
const std::optional<std::string>& default_value = std::nullopt, const std::optional<std::string>& default_value = std::nullopt,
const std::optional<bool>& use_global = std::nullopt); const std::optional<bool>& use_global = std::nullopt);
template <typename T>
std::enable_if_t<std::is_integral_v<T>> WriteIntegerSetting(
const std::string& key, const T& value,
const std::optional<T>& default_value = std::nullopt,
const std::optional<bool>& use_global = std::nullopt) {
std::optional<std::string> string_default = std::nullopt;
if (default_value.has_value()) {
string_default = std::make_optional(ToString(default_value.value()));
}
WritePreparedSetting(key, AdjustOutputString(ToString(value)), string_default, use_global);
}
void ReadCategory(Settings::Category category); void ReadCategory(Settings::Category category);
void WriteCategory(Settings::Category category); void WriteCategory(Settings::Category category);

View File

@ -449,7 +449,7 @@ void EmitImageGatherDref(EmitContext& ctx, IR::Inst& inst, const IR::Value& inde
} }
void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
std::string_view coords, std::string_view offset, std::string_view lod, std::string_view coords, const IR::Value& offset, std::string_view lod,
std::string_view ms) { std::string_view ms) {
const auto info{inst.Flags<IR::TextureInstInfo>()}; const auto info{inst.Flags<IR::TextureInstInfo>()};
if (info.has_bias) { if (info.has_bias) {
@ -470,9 +470,9 @@ void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
const auto int_coords{CoordsCastToInt(coords, info)}; const auto int_coords{CoordsCastToInt(coords, info)};
if (!ms.empty()) { if (!ms.empty()) {
ctx.Add("{}=texelFetch({},{},int({}));", texel, texture, int_coords, ms); ctx.Add("{}=texelFetch({},{},int({}));", texel, texture, int_coords, ms);
} else if (!offset.empty()) { } else if (!offset.IsEmpty()) {
ctx.Add("{}=texelFetchOffset({},{},int({}),{});", texel, texture, int_coords, lod, ctx.Add("{}=texelFetchOffset({},{},int({}),{});", texel, texture, int_coords, lod,
CoordsCastToInt(offset, info)); GetOffsetVec(ctx, offset));
} else { } else {
if (info.type == TextureType::Buffer) { if (info.type == TextureType::Buffer) {
ctx.Add("{}=texelFetch({},int({}));", texel, texture, coords); ctx.Add("{}=texelFetch({},int({}));", texel, texture, coords);
@ -485,10 +485,10 @@ void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
if (!ms.empty()) { if (!ms.empty()) {
throw NotImplementedException("EmitImageFetch Sparse MSAA samples"); throw NotImplementedException("EmitImageFetch Sparse MSAA samples");
} }
if (!offset.empty()) { if (!offset.IsEmpty()) {
ctx.AddU1("{}=sparseTexelsResidentARB(sparseTexelFetchOffsetARB({},{},int({}),{},{}));", ctx.AddU1("{}=sparseTexelsResidentARB(sparseTexelFetchOffsetARB({},{},int({}),{},{}));",
*sparse_inst, texture, CastToIntVec(coords, info), lod, *sparse_inst, texture, CastToIntVec(coords, info), lod, GetOffsetVec(ctx, offset),
CastToIntVec(offset, info), texel); texel);
} else { } else {
ctx.AddU1("{}=sparseTexelsResidentARB(sparseTexelFetchARB({},{},int({}),{}));", ctx.AddU1("{}=sparseTexelsResidentARB(sparseTexelFetchARB({},{},int({}),{}));",
*sparse_inst, texture, CastToIntVec(coords, info), lod, texel); *sparse_inst, texture, CastToIntVec(coords, info), lod, texel);

View File

@ -651,7 +651,7 @@ void EmitImageGatherDref(EmitContext& ctx, IR::Inst& inst, const IR::Value& inde
std::string_view coords, const IR::Value& offset, const IR::Value& offset2, std::string_view coords, const IR::Value& offset, const IR::Value& offset2,
std::string_view dref); std::string_view dref);
void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
std::string_view coords, std::string_view offset, std::string_view lod, std::string_view coords, const IR::Value& offset, std::string_view lod,
std::string_view ms); std::string_view ms);
void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
std::string_view lod, const IR::Value& skip_mips); std::string_view lod, const IR::Value& skip_mips);

View File

@ -1440,7 +1440,7 @@ void EmitContext::DefineInputs(const IR::Program& program) {
if (profile.support_vertex_instance_id) { if (profile.support_vertex_instance_id) {
instance_id = DefineInput(*this, U32[1], true, spv::BuiltIn::InstanceId); instance_id = DefineInput(*this, U32[1], true, spv::BuiltIn::InstanceId);
if (loads[IR::Attribute::BaseInstance]) { if (loads[IR::Attribute::BaseInstance]) {
base_instance = DefineInput(*this, U32[1], true, spv::BuiltIn::BaseVertex); base_instance = DefineInput(*this, U32[1], true, spv::BuiltIn::BaseInstance);
} }
} else { } else {
instance_index = DefineInput(*this, U32[1], true, spv::BuiltIn::InstanceIndex); instance_index = DefineInput(*this, U32[1], true, spv::BuiltIn::InstanceIndex);

View File

@ -195,9 +195,9 @@ Device::Device(Core::Frontend::EmuWindow& emu_window) {
has_texture_shadow_lod = HasExtension(extensions, "GL_EXT_texture_shadow_lod"); has_texture_shadow_lod = HasExtension(extensions, "GL_EXT_texture_shadow_lod");
has_astc = !has_slow_software_astc && IsASTCSupported(); has_astc = !has_slow_software_astc && IsASTCSupported();
has_variable_aoffi = TestVariableAoffi(); has_variable_aoffi = TestVariableAoffi();
has_component_indexing_bug = is_amd; has_component_indexing_bug = false;
has_precise_bug = TestPreciseBug(); has_precise_bug = TestPreciseBug();
has_broken_texture_view_formats = is_amd || (!is_linux && is_intel); has_broken_texture_view_formats = (!is_linux && is_intel);
has_nv_viewport_array2 = GLAD_GL_NV_viewport_array2; has_nv_viewport_array2 = GLAD_GL_NV_viewport_array2;
has_derivative_control = GLAD_GL_ARB_derivative_control; has_derivative_control = GLAD_GL_ARB_derivative_control;
has_vertex_buffer_unified_memory = GLAD_GL_NV_vertex_buffer_unified_memory; has_vertex_buffer_unified_memory = GLAD_GL_NV_vertex_buffer_unified_memory;
@ -238,10 +238,11 @@ Device::Device(Core::Frontend::EmuWindow& emu_window) {
has_lmem_perf_bug = is_nvidia; has_lmem_perf_bug = is_nvidia;
strict_context_required = emu_window.StrictContextRequired(); strict_context_required = emu_window.StrictContextRequired();
// Blocks AMD and Intel OpenGL drivers on Windows from using asynchronous shader compilation. // Blocks Intel OpenGL drivers on Windows from using asynchronous shader compilation.
// Blocks EGL on Wayland from using asynchronous shader compilation. // Blocks EGL on Wayland from using asynchronous shader compilation.
use_asynchronous_shaders = Settings::values.use_asynchronous_shaders.GetValue() && const bool blacklist_async_shaders = (is_intel && !is_linux) || strict_context_required;
!(is_amd || (is_intel && !is_linux)) && !strict_context_required; use_asynchronous_shaders =
Settings::values.use_asynchronous_shaders.GetValue() && !blacklist_async_shaders;
use_driver_cache = is_nvidia; use_driver_cache = is_nvidia;
supports_conditional_barriers = !is_intel; supports_conditional_barriers = !is_intel;

View File

@ -228,7 +228,7 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent) {
{ {
PAIR(ShaderBackend, Glsl, tr("GLSL")), PAIR(ShaderBackend, Glsl, tr("GLSL")),
PAIR(ShaderBackend, Glasm, tr("GLASM (Assembly Shaders, NVIDIA Only)")), PAIR(ShaderBackend, Glasm, tr("GLASM (Assembly Shaders, NVIDIA Only)")),
PAIR(ShaderBackend, SpirV, tr("SPIR-V (Experimental, Mesa Only)")), PAIR(ShaderBackend, SpirV, tr("SPIR-V (Experimental, AMD/Mesa Only)")),
}}); }});
translations->insert({Settings::EnumMetadata<Settings::GpuAccuracy>::Index(), translations->insert({Settings::EnumMetadata<Settings::GpuAccuracy>::Index(),
{ {