mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-15 13:16:43 +00:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0f8effc22a | |||
| b930951254 | |||
| 0a38913d9c | |||
| 481e0e450d | |||
| 4c65780f11 | |||
| a9c4c8aefd | |||
| a769505a45 | |||
| 54c3d10b86 | |||
| bdf60d79a0 | |||
| 2068b5d452 | |||
| 1b482fa99b | |||
| b6ee847947 | |||
| 6c16440996 | |||
| 09c583506b | |||
| 894add43f3 | |||
| d142b5dd6a | |||
| 0c2894eabf | |||
| d8a8169eb2 | |||
| 81c6e56713 | |||
| b4b41ee62c | |||
| 0d6a2158f0 | |||
| 09b6b3b71e |
+3
-1
@@ -31,6 +31,7 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
|
||||
RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"),
|
||||
ENABLE_BUFFER_HISTORY("enable_buffer_history"),
|
||||
USE_OPTIMIZED_VERTEX_BUFFERS("use_optimized_vertex_buffers"),
|
||||
ENABLE_GPU_BUFFER_READBACK("enable_gpu_buffer_readback"),
|
||||
SYNC_MEMORY_OPERATIONS("sync_memory_operations"),
|
||||
BUFFER_REORDER_DISABLE("disable_buffer_reorder"),
|
||||
RENDERER_DEBUG("debug"),
|
||||
@@ -83,9 +84,10 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
|
||||
ENABLE_OVERLAY("enable_overlay"),
|
||||
|
||||
// GPU Logging
|
||||
GPU_LOGGING_ENABLED("gpu_logging_enabled"),
|
||||
GPU_LOG_VULKAN_CALLS("gpu_log_vulkan_calls"),
|
||||
GPU_LOG_SHADER_DUMPS("gpu_log_shader_dumps"),
|
||||
DUMP_GUEST_SHADERS("dump_guest_shaders"),
|
||||
DUMP_MACROS("dump_macros"),
|
||||
GPU_LOG_MEMORY_TRACKING("gpu_log_memory_tracking"),
|
||||
GPU_LOG_DRIVER_DEBUG("gpu_log_driver_debug"),
|
||||
|
||||
|
||||
-89
@@ -1,89 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.features.settings.model.view
|
||||
|
||||
import androidx.annotation.ArrayRes
|
||||
import androidx.annotation.StringRes
|
||||
import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
|
||||
|
||||
class GpuUnswizzleSetting(
|
||||
@StringRes titleId: Int = 0,
|
||||
titleString: String = "",
|
||||
@StringRes descriptionId: Int = 0,
|
||||
descriptionString: String = "",
|
||||
@ArrayRes val textureSizeChoicesId: Int,
|
||||
@ArrayRes val textureSizeValuesId: Int,
|
||||
@ArrayRes val streamSizeChoicesId: Int,
|
||||
@ArrayRes val streamSizeValuesId: Int,
|
||||
@ArrayRes val chunkSizeChoicesId: Int,
|
||||
@ArrayRes val chunkSizeValuesId: Int
|
||||
) : SettingsItem(
|
||||
object : AbstractSetting {
|
||||
override val key: String = SettingsItem.GPU_UNSWIZZLE_COMBINED
|
||||
override val defaultValue: Any = false
|
||||
override val isSaveable = true
|
||||
override val isRuntimeModifiable = true
|
||||
override val isSwitchable = true
|
||||
override val pairedSettingKey: String = ""
|
||||
override var global: Boolean
|
||||
get() {
|
||||
return BooleanSetting.GPU_UNSWIZZLE_ENABLED.global &&
|
||||
IntSetting.GPU_UNSWIZZLE_TEXTURE_SIZE.global &&
|
||||
IntSetting.GPU_UNSWIZZLE_STREAM_SIZE.global &&
|
||||
IntSetting.GPU_UNSWIZZLE_CHUNK_SIZE.global
|
||||
}
|
||||
set(value) {
|
||||
BooleanSetting.GPU_UNSWIZZLE_ENABLED.global = value
|
||||
IntSetting.GPU_UNSWIZZLE_TEXTURE_SIZE.global = value
|
||||
IntSetting.GPU_UNSWIZZLE_STREAM_SIZE.global = value
|
||||
IntSetting.GPU_UNSWIZZLE_CHUNK_SIZE.global = value
|
||||
}
|
||||
override fun getValueAsString(needsGlobal: Boolean): String = "combined"
|
||||
override fun reset() {
|
||||
BooleanSetting.GPU_UNSWIZZLE_ENABLED.reset()
|
||||
IntSetting.GPU_UNSWIZZLE_TEXTURE_SIZE.reset()
|
||||
IntSetting.GPU_UNSWIZZLE_STREAM_SIZE.reset()
|
||||
IntSetting.GPU_UNSWIZZLE_CHUNK_SIZE.reset()
|
||||
}
|
||||
},
|
||||
titleId,
|
||||
titleString,
|
||||
descriptionId,
|
||||
descriptionString
|
||||
) {
|
||||
override val type = SettingsItem.TYPE_GPU_UNSWIZZLE
|
||||
|
||||
// Check if GPU unswizzle is enabled via the dedicated boolean setting
|
||||
fun isEnabled(needsGlobal: Boolean = false): Boolean =
|
||||
BooleanSetting.GPU_UNSWIZZLE_ENABLED.getBoolean(needsGlobal)
|
||||
|
||||
fun setEnabled(value: Boolean) =
|
||||
BooleanSetting.GPU_UNSWIZZLE_ENABLED.setBoolean(value)
|
||||
|
||||
fun enable() = setEnabled(true)
|
||||
|
||||
fun disable() = setEnabled(false)
|
||||
|
||||
fun getTextureSize(needsGlobal: Boolean = false): Int =
|
||||
IntSetting.GPU_UNSWIZZLE_TEXTURE_SIZE.getInt(needsGlobal)
|
||||
|
||||
fun setTextureSize(value: Int) =
|
||||
IntSetting.GPU_UNSWIZZLE_TEXTURE_SIZE.setInt(value)
|
||||
|
||||
fun getStreamSize(needsGlobal: Boolean = false): Int =
|
||||
IntSetting.GPU_UNSWIZZLE_STREAM_SIZE.getInt(needsGlobal)
|
||||
|
||||
fun setStreamSize(value: Int) =
|
||||
IntSetting.GPU_UNSWIZZLE_STREAM_SIZE.setInt(value)
|
||||
|
||||
fun getChunkSize(needsGlobal: Boolean = false): Int =
|
||||
IntSetting.GPU_UNSWIZZLE_CHUNK_SIZE.getInt(needsGlobal)
|
||||
|
||||
fun setChunkSize(value: Int) =
|
||||
IntSetting.GPU_UNSWIZZLE_CHUNK_SIZE.setInt(value)
|
||||
|
||||
fun reset() = setting.reset()
|
||||
}
|
||||
+33
-28
@@ -686,42 +686,33 @@ abstract class SettingsItem(
|
||||
)
|
||||
)
|
||||
put(
|
||||
SingleChoiceSetting(
|
||||
SpinBoxSetting(
|
||||
IntSetting.GPU_UNSWIZZLE_TEXTURE_SIZE,
|
||||
titleId = R.string.gpu_unswizzle_texture_size,
|
||||
descriptionId = R.string.gpu_unswizzle_texture_size_description,
|
||||
choicesId = R.array.gpuTextureSizeSwizzleEntries,
|
||||
valuesId = R.array.gpuTextureSizeSwizzleValues
|
||||
valueHint = R.string.gpu_unswizzle_texture_size,
|
||||
min = 1,
|
||||
max = 9
|
||||
)
|
||||
)
|
||||
put(
|
||||
SingleChoiceSetting(
|
||||
SpinBoxSetting(
|
||||
IntSetting.GPU_UNSWIZZLE_STREAM_SIZE,
|
||||
titleId = R.string.gpu_unswizzle_stream_size,
|
||||
descriptionId = R.string.gpu_unswizzle_stream_size_description,
|
||||
choicesId = R.array.gpuSwizzleEntries,
|
||||
valuesId = R.array.gpuSwizzleValues
|
||||
valueHint = R.string.gpu_unswizzle_stream_size,
|
||||
min = 1,
|
||||
max = 9
|
||||
)
|
||||
)
|
||||
put(
|
||||
SingleChoiceSetting(
|
||||
SpinBoxSetting(
|
||||
IntSetting.GPU_UNSWIZZLE_CHUNK_SIZE,
|
||||
titleId = R.string.gpu_unswizzle_chunk_size,
|
||||
descriptionId = R.string.gpu_unswizzle_chunk_size_description,
|
||||
choicesId = R.array.gpuSwizzleChunkEntries,
|
||||
valuesId = R.array.gpuSwizzleChunkValues
|
||||
)
|
||||
)
|
||||
put(
|
||||
GpuUnswizzleSetting(
|
||||
titleId = R.string.gpu_unswizzle_settings,
|
||||
descriptionId = R.string.gpu_unswizzle_settings_description,
|
||||
textureSizeChoicesId = R.array.gpuTextureSizeSwizzleEntries,
|
||||
textureSizeValuesId = R.array.gpuTextureSizeSwizzleValues,
|
||||
streamSizeChoicesId = R.array.gpuSwizzleEntries,
|
||||
streamSizeValuesId = R.array.gpuSwizzleValues,
|
||||
chunkSizeChoicesId = R.array.gpuSwizzleChunkEntries,
|
||||
chunkSizeValuesId = R.array.gpuSwizzleChunkValues
|
||||
valueHint = R.string.gpu_unswizzle_chunk_size,
|
||||
min = 1,
|
||||
max = 9
|
||||
)
|
||||
)
|
||||
put(
|
||||
@@ -806,6 +797,13 @@ abstract class SettingsItem(
|
||||
descriptionId = R.string.enable_buffer_history_description
|
||||
)
|
||||
)
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.ENABLE_GPU_BUFFER_READBACK,
|
||||
titleId = R.string.enable_gpu_buffer_readback,
|
||||
descriptionId = R.string.enable_gpu_buffer_readback_description
|
||||
)
|
||||
)
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.USE_OPTIMIZED_VERTEX_BUFFERS,
|
||||
@@ -931,13 +929,6 @@ abstract class SettingsItem(
|
||||
)
|
||||
|
||||
// GPU Logging settings
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.GPU_LOGGING_ENABLED,
|
||||
titleId = R.string.gpu_logging_enabled,
|
||||
descriptionId = R.string.gpu_logging_enabled_description
|
||||
)
|
||||
)
|
||||
put(
|
||||
SingleChoiceSetting(
|
||||
ByteSetting.GPU_LOG_LEVEL,
|
||||
@@ -954,6 +945,13 @@ abstract class SettingsItem(
|
||||
descriptionId = R.string.gpu_log_vulkan_calls_description
|
||||
)
|
||||
)
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.DUMP_GUEST_SHADERS,
|
||||
titleId = R.string.dump_guest_shaders,
|
||||
descriptionId = R.string.dump_guest_shaders_description
|
||||
)
|
||||
)
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.GPU_LOG_SHADER_DUMPS,
|
||||
@@ -961,6 +959,13 @@ abstract class SettingsItem(
|
||||
descriptionId = R.string.gpu_log_shader_dumps_description
|
||||
)
|
||||
)
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.DUMP_MACROS,
|
||||
titleId = R.string.dump_macros,
|
||||
descriptionId = R.string.dump_macros_description
|
||||
)
|
||||
)
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.GPU_LOG_MEMORY_TRACKING,
|
||||
|
||||
-206
@@ -1,206 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.features.settings.ui
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.DialogInterface
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.ArrayAdapter
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.databinding.DialogGpuUnswizzleBinding
|
||||
import org.yuzu.yuzu_emu.features.settings.model.view.GpuUnswizzleSetting
|
||||
|
||||
class GpuUnswizzleDialogFragment : DialogFragment() {
|
||||
private var position = 0
|
||||
private val settingsViewModel: SettingsViewModel by activityViewModels()
|
||||
private lateinit var binding: DialogGpuUnswizzleBinding
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
position = requireArguments().getInt(POSITION)
|
||||
|
||||
if (settingsViewModel.clickedItem == null) dismiss()
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
binding = DialogGpuUnswizzleBinding.inflate(LayoutInflater.from(requireContext()))
|
||||
val item = settingsViewModel.clickedItem as GpuUnswizzleSetting
|
||||
|
||||
// Setup texture size dropdown
|
||||
val textureSizeEntries = resources.getStringArray(item.textureSizeChoicesId)
|
||||
val textureSizeValues = resources.getIntArray(item.textureSizeValuesId)
|
||||
val textureSizeAdapter = ArrayAdapter(
|
||||
requireContext(),
|
||||
android.R.layout.simple_dropdown_item_1line,
|
||||
textureSizeEntries.toMutableList()
|
||||
)
|
||||
binding.dropdownTextureSize.setAdapter(textureSizeAdapter)
|
||||
|
||||
// Setup stream size dropdown
|
||||
val streamSizeEntries = resources.getStringArray(item.streamSizeChoicesId)
|
||||
val streamSizeValues = resources.getIntArray(item.streamSizeValuesId)
|
||||
val streamSizeAdapter = ArrayAdapter(
|
||||
requireContext(),
|
||||
android.R.layout.simple_dropdown_item_1line,
|
||||
streamSizeEntries.toMutableList()
|
||||
)
|
||||
binding.dropdownStreamSize.setAdapter(streamSizeAdapter)
|
||||
|
||||
// Setup chunk size dropdown
|
||||
val chunkSizeEntries = resources.getStringArray(item.chunkSizeChoicesId)
|
||||
val chunkSizeValues = resources.getIntArray(item.chunkSizeValuesId)
|
||||
val chunkSizeAdapter = ArrayAdapter(
|
||||
requireContext(),
|
||||
android.R.layout.simple_dropdown_item_1line,
|
||||
chunkSizeEntries.toMutableList()
|
||||
)
|
||||
binding.dropdownChunkSize.setAdapter(chunkSizeAdapter)
|
||||
|
||||
// Load current values
|
||||
val isEnabled = item.isEnabled()
|
||||
binding.switchEnable.isChecked = isEnabled
|
||||
|
||||
if (isEnabled) {
|
||||
val textureSizeIndex = textureSizeValues.indexOf(item.getTextureSize())
|
||||
if (textureSizeIndex >= 0) {
|
||||
binding.dropdownTextureSize.setText(textureSizeEntries[textureSizeIndex], false)
|
||||
}
|
||||
|
||||
val streamSizeIndex = streamSizeValues.indexOf(item.getStreamSize())
|
||||
if (streamSizeIndex >= 0) {
|
||||
binding.dropdownStreamSize.setText(streamSizeEntries[streamSizeIndex], false)
|
||||
}
|
||||
|
||||
val chunkSizeIndex = chunkSizeValues.indexOf(item.getChunkSize())
|
||||
if (chunkSizeIndex >= 0) {
|
||||
binding.dropdownChunkSize.setText(chunkSizeEntries[chunkSizeIndex], false)
|
||||
}
|
||||
} else {
|
||||
// Set default/recommended values when disabling
|
||||
binding.dropdownTextureSize.setText(textureSizeEntries[3], false)
|
||||
binding.dropdownStreamSize.setText(streamSizeEntries[3], false)
|
||||
binding.dropdownChunkSize.setText(chunkSizeEntries[3], false)
|
||||
}
|
||||
|
||||
// Clear adapter filters after setText to fix rotation bug
|
||||
textureSizeAdapter.filter.filter(null)
|
||||
streamSizeAdapter.filter.filter(null)
|
||||
chunkSizeAdapter.filter.filter(null)
|
||||
|
||||
// Enable/disable dropdowns based on switch state
|
||||
updateDropdownsState(isEnabled)
|
||||
binding.switchEnable.setOnCheckedChangeListener { _, checked ->
|
||||
updateDropdownsState(checked)
|
||||
}
|
||||
|
||||
val dialog = MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(item.title)
|
||||
.setView(binding.root)
|
||||
.create()
|
||||
|
||||
// Setup button listeners
|
||||
binding.btnDefault.setOnClickListener {
|
||||
// Reset to defaults
|
||||
item.reset()
|
||||
// Refresh values with adapters reset
|
||||
val textureSizeIndex = textureSizeValues.indexOf(item.getTextureSize())
|
||||
if (textureSizeIndex >= 0) {
|
||||
binding.dropdownTextureSize.setText(textureSizeEntries[textureSizeIndex], false)
|
||||
}
|
||||
val streamSizeIndex = streamSizeValues.indexOf(item.getStreamSize())
|
||||
if (streamSizeIndex >= 0) {
|
||||
binding.dropdownStreamSize.setText(streamSizeEntries[streamSizeIndex], false)
|
||||
}
|
||||
val chunkSizeIndex = chunkSizeValues.indexOf(item.getChunkSize())
|
||||
if (chunkSizeIndex >= 0) {
|
||||
binding.dropdownChunkSize.setText(chunkSizeEntries[chunkSizeIndex], false)
|
||||
}
|
||||
// Clear filters
|
||||
textureSizeAdapter.filter.filter(null)
|
||||
streamSizeAdapter.filter.filter(null)
|
||||
chunkSizeAdapter.filter.filter(null)
|
||||
|
||||
settingsViewModel.setAdapterItemChanged(position)
|
||||
settingsViewModel.setShouldReloadSettingsList(true)
|
||||
}
|
||||
|
||||
binding.btnCancel.setOnClickListener {
|
||||
dialog.dismiss()
|
||||
}
|
||||
|
||||
binding.btnOk.setOnClickListener {
|
||||
if (binding.switchEnable.isChecked) {
|
||||
item.enable()
|
||||
// Save the selected values
|
||||
val selectedTextureIndex = textureSizeEntries.indexOf(
|
||||
binding.dropdownTextureSize.text.toString()
|
||||
)
|
||||
if (selectedTextureIndex >= 0) {
|
||||
item.setTextureSize(textureSizeValues[selectedTextureIndex])
|
||||
}
|
||||
|
||||
val selectedStreamIndex = streamSizeEntries.indexOf(
|
||||
binding.dropdownStreamSize.text.toString()
|
||||
)
|
||||
if (selectedStreamIndex >= 0) {
|
||||
item.setStreamSize(streamSizeValues[selectedStreamIndex])
|
||||
}
|
||||
|
||||
val selectedChunkIndex = chunkSizeEntries.indexOf(
|
||||
binding.dropdownChunkSize.text.toString()
|
||||
)
|
||||
if (selectedChunkIndex >= 0) {
|
||||
item.setChunkSize(chunkSizeValues[selectedChunkIndex])
|
||||
}
|
||||
} else {
|
||||
// Disable GPU unswizzle
|
||||
item.disable()
|
||||
}
|
||||
|
||||
settingsViewModel.setAdapterItemChanged(position)
|
||||
settingsViewModel.setShouldReloadSettingsList(true)
|
||||
dialog.dismiss()
|
||||
}
|
||||
|
||||
// Ensure filters are cleared after dialog is shown
|
||||
binding.root.post {
|
||||
textureSizeAdapter.filter.filter(null)
|
||||
streamSizeAdapter.filter.filter(null)
|
||||
chunkSizeAdapter.filter.filter(null)
|
||||
}
|
||||
|
||||
return dialog
|
||||
}
|
||||
|
||||
private fun updateDropdownsState(enabled: Boolean) {
|
||||
binding.layoutTextureSize.isEnabled = enabled
|
||||
binding.dropdownTextureSize.isEnabled = enabled
|
||||
binding.layoutStreamSize.isEnabled = enabled
|
||||
binding.dropdownStreamSize.isEnabled = enabled
|
||||
binding.layoutChunkSize.isEnabled = enabled
|
||||
binding.dropdownChunkSize.isEnabled = enabled
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TAG = "GpuUnswizzleDialogFragment"
|
||||
const val POSITION = "Position"
|
||||
|
||||
fun newInstance(
|
||||
settingsViewModel: SettingsViewModel,
|
||||
item: GpuUnswizzleSetting,
|
||||
position: Int
|
||||
): GpuUnswizzleDialogFragment {
|
||||
val dialog = GpuUnswizzleDialogFragment()
|
||||
val args = Bundle()
|
||||
args.putInt(POSITION, position)
|
||||
dialog.arguments = args
|
||||
settingsViewModel.clickedItem = item
|
||||
return dialog
|
||||
}
|
||||
}
|
||||
}
|
||||
-12
@@ -102,10 +102,6 @@ class SettingsAdapter(
|
||||
PathViewHolder(ListItemSettingBinding.inflate(inflater), this)
|
||||
}
|
||||
|
||||
SettingsItem.TYPE_GPU_UNSWIZZLE -> {
|
||||
GpuUnswizzleViewHolder(ListItemSettingBinding.inflate(inflater), this)
|
||||
}
|
||||
|
||||
else -> {
|
||||
HeaderViewHolder(ListItemSettingsHeaderBinding.inflate(inflater), this)
|
||||
}
|
||||
@@ -479,14 +475,6 @@ class SettingsAdapter(
|
||||
settingsViewModel.setShouldShowPathResetDialog(true)
|
||||
}
|
||||
|
||||
fun onGpuUnswizzleClick(item: GpuUnswizzleSetting, position: Int) {
|
||||
GpuUnswizzleDialogFragment.newInstance(
|
||||
settingsViewModel,
|
||||
item,
|
||||
position
|
||||
).show(fragment.childFragmentManager, GpuUnswizzleDialogFragment.TAG)
|
||||
}
|
||||
|
||||
private class DiffCallback : DiffUtil.ItemCallback<SettingsItem>() {
|
||||
override fun areItemsTheSame(oldItem: SettingsItem, newItem: SettingsItem): Boolean {
|
||||
return oldItem.setting.key == newItem.setting.key
|
||||
|
||||
+12
-8
@@ -292,6 +292,7 @@ class SettingsFragmentPresenter(
|
||||
add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key)
|
||||
add(BooleanSetting.RENDERER_REACTIVE_FLUSHING.key)
|
||||
add(BooleanSetting.ENABLE_BUFFER_HISTORY.key)
|
||||
add(BooleanSetting.ENABLE_GPU_BUFFER_READBACK.key)
|
||||
add(BooleanSetting.USE_OPTIMIZED_VERTEX_BUFFERS.key)
|
||||
|
||||
add(HeaderSetting(R.string.hacks))
|
||||
@@ -1288,14 +1289,17 @@ class SettingsFragmentPresenter(
|
||||
add(ShortSetting.DEBUG_KNOBS.key)
|
||||
add(StringSetting.PROGRAM_ARGS.key)
|
||||
|
||||
add(HeaderSetting(R.string.gpu_logging_header))
|
||||
add(BooleanSetting.GPU_LOGGING_ENABLED.key)
|
||||
add(ByteSetting.GPU_LOG_LEVEL.key)
|
||||
add(BooleanSetting.GPU_LOG_VULKAN_CALLS.key)
|
||||
add(BooleanSetting.GPU_LOG_SHADER_DUMPS.key)
|
||||
add(BooleanSetting.GPU_LOG_MEMORY_TRACKING.key)
|
||||
add(BooleanSetting.GPU_LOG_DRIVER_DEBUG.key)
|
||||
add(IntSetting.GPU_LOG_RING_BUFFER_SIZE.key)
|
||||
if (!NativeConfig.isPerGameConfigLoaded()) {
|
||||
add(HeaderSetting(R.string.gpu_logging_header))
|
||||
add(ByteSetting.GPU_LOG_LEVEL.key)
|
||||
add(BooleanSetting.GPU_LOG_VULKAN_CALLS.key)
|
||||
add(BooleanSetting.DUMP_GUEST_SHADERS.key)
|
||||
add(BooleanSetting.GPU_LOG_SHADER_DUMPS.key)
|
||||
add(BooleanSetting.DUMP_MACROS.key)
|
||||
add(BooleanSetting.GPU_LOG_MEMORY_TRACKING.key)
|
||||
add(BooleanSetting.GPU_LOG_DRIVER_DEBUG.key)
|
||||
add(IntSetting.GPU_LOG_RING_BUFFER_SIZE.key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-71
@@ -1,71 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.features.settings.ui.viewholder
|
||||
|
||||
import android.view.View
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
|
||||
import org.yuzu.yuzu_emu.features.settings.model.view.GpuUnswizzleSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
|
||||
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
|
||||
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
|
||||
|
||||
class GpuUnswizzleViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
|
||||
SettingViewHolder(binding.root, adapter) {
|
||||
private lateinit var setting: GpuUnswizzleSetting
|
||||
|
||||
override fun bind(item: SettingsItem) {
|
||||
setting = item as GpuUnswizzleSetting
|
||||
binding.textSettingName.text = setting.title
|
||||
binding.textSettingDescription.setVisible(item.description.isNotEmpty())
|
||||
binding.textSettingDescription.text = item.description
|
||||
|
||||
binding.textSettingValue.setVisible(true)
|
||||
val resMgr = binding.root.context.resources
|
||||
|
||||
if (setting.isEnabled()) {
|
||||
// Show a summary of current settings
|
||||
val textureSizeEntries = resMgr.getStringArray(setting.textureSizeChoicesId)
|
||||
val textureSizeValues = resMgr.getIntArray(setting.textureSizeValuesId)
|
||||
val textureSizeIndex = textureSizeValues.indexOf(setting.getTextureSize())
|
||||
val textureSizeLabel = if (textureSizeIndex >= 0) textureSizeEntries[textureSizeIndex] else "?"
|
||||
|
||||
val streamSizeEntries = resMgr.getStringArray(setting.streamSizeChoicesId)
|
||||
val streamSizeValues = resMgr.getIntArray(setting.streamSizeValuesId)
|
||||
val streamSizeIndex = streamSizeValues.indexOf(setting.getStreamSize())
|
||||
val streamSizeLabel = if (streamSizeIndex >= 0) streamSizeEntries[streamSizeIndex] else "?"
|
||||
|
||||
val chunkSizeEntries = resMgr.getStringArray(setting.chunkSizeChoicesId)
|
||||
val chunkSizeValues = resMgr.getIntArray(setting.chunkSizeValuesId)
|
||||
val chunkSizeIndex = chunkSizeValues.indexOf(setting.getChunkSize())
|
||||
val chunkSizeLabel = if (chunkSizeIndex >= 0) chunkSizeEntries[chunkSizeIndex] else "?"
|
||||
|
||||
binding.textSettingValue.text = "$textureSizeLabel ⋅ $streamSizeLabel ⋅ $chunkSizeLabel"
|
||||
} else {
|
||||
binding.textSettingValue.text = resMgr.getString(R.string.gpu_unswizzle_disabled)
|
||||
}
|
||||
|
||||
binding.buttonClear.setVisible(setting.clearable)
|
||||
binding.buttonClear.setOnClickListener {
|
||||
adapter.onClearClick(setting, bindingAdapterPosition)
|
||||
}
|
||||
|
||||
setStyle(setting.isEditable, binding)
|
||||
}
|
||||
|
||||
override fun onClick(clicked: View) {
|
||||
if (!setting.isEditable) {
|
||||
return
|
||||
}
|
||||
|
||||
adapter.onGpuUnswizzleClick(setting, bindingAdapterPosition)
|
||||
}
|
||||
|
||||
override fun onLongClick(clicked: View): Boolean {
|
||||
if (setting.isEditable) {
|
||||
return adapter.onLongClick(setting, bindingAdapterPosition)
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -516,10 +516,7 @@
|
||||
<string name="rescale_hack_description">يُمكّن هذا الخيار من التعامل مع عملية إعادة تحجيم الألعاب بطريقة تقليدية باستخدام مسار إعادة التحجيم السريع</string>
|
||||
<string name="renderer_asynchronous_shaders">استخدم تظليل غير متزامن</string>
|
||||
<string name="renderer_asynchronous_shaders_description">يقوم بتجميع التظليل بشكل غير متزامن. قد يقلل ذلك من التقطعات ولكنه قد يؤدي أيضًا إلى حدوث أخطاء.</string>
|
||||
<string name="gpu_unswizzle_settings">إعدادات إلغاء ترتيب بيانات وحدة معالجة الرسومات</string>
|
||||
<string name="gpu_unswizzle_settings_description">قم بضبط معلمات فكّ تشابك النسيج المستندة إلى وحدة معالجة الرسومات أو تعطيلها تمامًا. اضبط هذه الإعدادات لتحقيق التوازن بين الأداء وجودة تحميل النسيج.</string>
|
||||
<string name="gpu_unswizzle_enable">تفعيل إلغاء ترتيب بيانات وحدة معالجة الرسومات</string>
|
||||
<string name="gpu_unswizzle_disabled">تعطيل</string>
|
||||
<string name="gpu_unswizzle_texture_size">الحد الأقصى لحجم النسيج في وحدة معالجة الرسومات بعد إعادة ترتيب البيانات</string>
|
||||
<string name="gpu_unswizzle_texture_size_description">يُحدد هذا الخيار الحد الأقصى لحجم (ميغابايت) معالجة الصور باستخدام وحدة معالجة الرسومات. مع أن وحدة معالجة الرسومات أسرع في معالجة الصور المتوسطة والكبيرة، إلا أن وحدة المعالجة المركزية قد تكون أكثر كفاءة في معالجة الصور الصغيرة جدًا. اضبط هذا الخيار لتحقيق التوازن الأمثل بين سرعة معالجة الرسومات واستهلاك وحدة المعالجة المركزية.</string>
|
||||
<string name="gpu_unswizzle_stream_size">حجم تدفق إلغاء ترتيب بيانات وحدة معالجة الرسومات</string>
|
||||
@@ -569,8 +566,6 @@
|
||||
|
||||
<!-- GPU Logging strings -->
|
||||
<string name="gpu_logging_header">تسجيل وحدة معالجة الرسومات</string>
|
||||
<string name="gpu_logging_enabled">تمكين تسجيل وحدة معالجة الرسومات</string>
|
||||
<string name="gpu_logging_enabled_description">تسجيل عمليات وحدة معالجة الرسومات في ملف eden_gpu.log لتصحيح أخطاء برامج تشغيل Adreno</string>
|
||||
<string name="gpu_log_level">مستوى السجل</string>
|
||||
<string name="gpu_log_level_description">مستوى التفاصيل لسجلات وحدة معالجة الرسومات (كلما زاد المستوى، زادت التفاصيل وزادت التكاليف الإضافية)</string>
|
||||
<string name="gpu_log_vulkan_calls">تسجيل استدعاءات واجهة برمجة تطبيقات Vulkan</string>
|
||||
@@ -959,25 +954,10 @@
|
||||
<string name="fast_gpu_high">مرتفع (512)</string>
|
||||
|
||||
<!-- GPU swizzle texture size -->
|
||||
<string name="gpu_texturesizeswizzle_verysmall">صغير جدًا (16 ميغابايت)</string>
|
||||
<string name="gpu_texturesizeswizzle_small">صغير (32 ميغابايت)</string>
|
||||
<string name="gpu_texturesizeswizzle_normal">قياسي (128 ميغابايت)</string>
|
||||
<string name="gpu_texturesizeswizzle_large">كبير (256 ميغابايت)</string>
|
||||
<string name="gpu_texturesizeswizzle_verylarge">كبير جدًا (512 ميغابايت)</string>
|
||||
|
||||
<!-- GPU swizzle streams -->
|
||||
<string name="gpu_swizzle_verylow">منخفض جدًا (4 ميغابايت)</string>
|
||||
<string name="gpu_swizzle_low">منخفض (8 ميغابايت)</string>
|
||||
<string name="gpu_swizzle_normal">قياسي (16 ميغابايت)</string>
|
||||
<string name="gpu_swizzle_medium">متوسط (32 ميغابايت)</string>
|
||||
<string name="gpu_swizzle_high">عالي (64 ميغابايت)</string>
|
||||
|
||||
<!-- GPU swizzle chunks -->
|
||||
<string name="gpu_swizzlechunk_verylow">منخفض جدًا (32)</string>
|
||||
<string name="gpu_swizzlechunk_low">منخفض (64)</string>
|
||||
<string name="gpu_swizzlechunk_normal">قياسي (128)</string>
|
||||
<string name="gpu_swizzlechunk_medium">متوسط (256)</string>
|
||||
<string name="gpu_swizzlechunk_high">عالي (512)</string>
|
||||
|
||||
<!-- Temperature Units -->
|
||||
<string name="temperature_celsius">مئوية</string>
|
||||
|
||||
@@ -890,25 +890,10 @@ Wirklich fortfahren?</string>
|
||||
<string name="fast_gpu_high">Hoch (512)</string>
|
||||
|
||||
<!-- GPU swizzle texture size -->
|
||||
<string name="gpu_texturesizeswizzle_verysmall">Sehr klein (16 MB)</string>
|
||||
<string name="gpu_texturesizeswizzle_small">Klein (32 MB)</string>
|
||||
<string name="gpu_texturesizeswizzle_normal">Normal (128 MB)</string>
|
||||
<string name="gpu_texturesizeswizzle_large">Groß (256 MB)</string>
|
||||
<string name="gpu_texturesizeswizzle_verylarge">Sehr groß (512 MB)</string>
|
||||
|
||||
<!-- GPU swizzle streams -->
|
||||
<string name="gpu_swizzle_verylow">Sehr niedrig (4 MB)</string>
|
||||
<string name="gpu_swizzle_low">Niedrig (8 MB)</string>
|
||||
<string name="gpu_swizzle_normal">Normal (16 MB)</string>
|
||||
<string name="gpu_swizzle_medium">Mittel (32 MB)</string>
|
||||
<string name="gpu_swizzle_high">Hoch (64 MB)</string>
|
||||
|
||||
<!-- GPU swizzle chunks -->
|
||||
<string name="gpu_swizzlechunk_verylow">Sehr niedrig (32)</string>
|
||||
<string name="gpu_swizzlechunk_low">Niedrig (64)</string>
|
||||
<string name="gpu_swizzlechunk_normal">Normal (128)</string>
|
||||
<string name="gpu_swizzlechunk_medium">Mittel (256)</string>
|
||||
<string name="gpu_swizzlechunk_high">Hoch (512)</string>
|
||||
|
||||
<!-- Temperature Units -->
|
||||
<string name="temperature_celsius">Celsius</string>
|
||||
|
||||
@@ -510,10 +510,7 @@
|
||||
<string name="rescale_hack_description">Permite el manejo de versiones anteriores para el paso de configuración de reescalado para juegos mediante el uso de una ruta de reescalado rápida.</string>
|
||||
<string name="renderer_asynchronous_shaders">Usar sombreadores asíncronos</string>
|
||||
<string name="renderer_asynchronous_shaders_description">Compila los sombreadores de forma asíncrona. Esto puede reducir los tirones, pero también puede introducir errores gráficos.</string>
|
||||
<string name="gpu_unswizzle_settings">Ajustes de desentrelazado de la GPU</string>
|
||||
<string name="gpu_unswizzle_settings_description">Configura los parámetros de desentrelazado de texturas basadas en la GPU o desactívelos por completo. Modifique estos ajustes para equilibrar el rendimiento y la calidad de las texturas cargadas.</string>
|
||||
<string name="gpu_unswizzle_enable">Activar desentrelazado de la GPU</string>
|
||||
<string name="gpu_unswizzle_disabled">Desactivado</string>
|
||||
<string name="gpu_unswizzle_texture_size">Tamaño máximo de textura de desentrelazado de la GPU</string>
|
||||
<string name="gpu_unswizzle_texture_size_description">Establece el tamaño máximo (en MB) para el desentrelazado de texturas basada en GPU. Aunque la GPU es más rápida para texturas medianas y grandes, la CPU puede ser más eficiente para texturas muy pequeñas. Ajuste este valor para encontrar el equilibrio entre la aceleración de la GPU y la sobrecarga de la CPU.</string>
|
||||
<string name="gpu_unswizzle_stream_size">Tamaño del flujo de desentrelazado de la GPU</string>
|
||||
@@ -563,8 +560,6 @@
|
||||
|
||||
<!-- GPU Logging strings -->
|
||||
<string name="gpu_logging_header">Registros de la GPU</string>
|
||||
<string name="gpu_logging_enabled">Activar los registros de la GPU</string>
|
||||
<string name="gpu_logging_enabled_description">Registra las operaciones de la GPU en eden_gpu.log para la depuración de los controladores de Adreno</string>
|
||||
<string name="gpu_log_level">Nivel de registros</string>
|
||||
<string name="gpu_log_level_description">Nivel de detalle de los registros de la GPU (más alto = más detalles, más sobrecarga)</string>
|
||||
<string name="gpu_log_vulkan_calls">Registros de llamadas del API de Vulkan</string>
|
||||
@@ -953,25 +948,10 @@
|
||||
<string name="fast_gpu_high">Alto (512)</string>
|
||||
|
||||
<!-- GPU swizzle texture size -->
|
||||
<string name="gpu_texturesizeswizzle_verysmall">Muy pequeño (16 MB)</string>
|
||||
<string name="gpu_texturesizeswizzle_small">Pequeño (32 MB)</string>
|
||||
<string name="gpu_texturesizeswizzle_normal">Normal (128 MB)</string>
|
||||
<string name="gpu_texturesizeswizzle_large">Grande (256 MB)</string>
|
||||
<string name="gpu_texturesizeswizzle_verylarge">Muy grande (512 MB)</string>
|
||||
|
||||
<!-- GPU swizzle streams -->
|
||||
<string name="gpu_swizzle_verylow">Muy bajo (4 MB)</string>
|
||||
<string name="gpu_swizzle_low">Bajo (8 MB)</string>
|
||||
<string name="gpu_swizzle_normal">Normal (16 MB)</string>
|
||||
<string name="gpu_swizzle_medium">Medio (32 MB)</string>
|
||||
<string name="gpu_swizzle_high">Alto (64 MB)</string>
|
||||
|
||||
<!-- GPU swizzle chunks -->
|
||||
<string name="gpu_swizzlechunk_verylow">Muy bajo (32)</string>
|
||||
<string name="gpu_swizzlechunk_low">Bajo (64)</string>
|
||||
<string name="gpu_swizzlechunk_normal">Normal (128)</string>
|
||||
<string name="gpu_swizzlechunk_medium">Medio (256)</string>
|
||||
<string name="gpu_swizzlechunk_high">Alto (512)</string>
|
||||
|
||||
<!-- Temperature Units -->
|
||||
<string name="temperature_celsius">Celsius</string>
|
||||
|
||||
@@ -525,7 +525,6 @@
|
||||
|
||||
<!-- GPU Logging strings -->
|
||||
<string name="gpu_logging_header">Journalisation GPU</string>
|
||||
<string name="gpu_logging_enabled">Activer la journalisation GPU</string>
|
||||
<string name="gpu_log_level">Niveau de journalisation</string>
|
||||
<string name="gpu_log_vulkan_calls">Journaliser les appels API Vulkan</string>
|
||||
<string name="gpu_log_shader_dumps">Extraire les shaders</string>
|
||||
|
||||
@@ -509,10 +509,7 @@
|
||||
<string name="rescale_hack_description">Включает старый метод обработки этапа перенастройки масштабирования для игр за счёт использования быстрого алгоритма перемасштабирования.</string>
|
||||
<string name="renderer_asynchronous_shaders">Использовать асинхронные шейдеры</string>
|
||||
<string name="renderer_asynchronous_shaders_description">Компилирует шейдеры асинхронно. Это может уменьшить подтормаживания, но также может вызвать графические артефакты.</string>
|
||||
<string name="gpu_unswizzle_settings">Настройки распаковки текстур (Unswizzle)</string>
|
||||
<string name="gpu_unswizzle_settings_description">Настройте параметры распаковки текстур на стороне ГПУ либо полностью отключите эту функцию. Изменение этих параметров позволяет найти баланс между производительностью и качеством загрузки текстур.</string>
|
||||
<string name="gpu_unswizzle_enable">Включить распаковку текстур (Unswizzle)</string>
|
||||
<string name="gpu_unswizzle_disabled">Отключено</string>
|
||||
<string name="gpu_unswizzle_texture_size">Макс. размер текстуры Unswizzle</string>
|
||||
<string name="gpu_unswizzle_texture_size_description">Задает максимальный размер (в МБ) текстур для преобразования формата (unswizzle) на ГПУ. Хотя ГПУ быстрее работает со средними и большими текстурами, ЦП может быть эффективнее для очень маленьких. Настройте это значение, чтобы найти баланс между ускорением на ГПУ и нагрузкой на ЦП.</string>
|
||||
<string name="gpu_unswizzle_stream_size">Размер потока Unswizzle</string>
|
||||
@@ -562,8 +559,6 @@
|
||||
|
||||
<!-- GPU Logging strings -->
|
||||
<string name="gpu_logging_header">Ведение журнала ГПУ</string>
|
||||
<string name="gpu_logging_enabled">Включить ведение журнала ГПУ</string>
|
||||
<string name="gpu_logging_enabled_description">Записывать операции ГПУ в файл eden_gpu.log для отладки драйверов Adreno</string>
|
||||
<string name="gpu_log_level">Уровень журналирования</string>
|
||||
<string name="gpu_log_level_description">Уровень детализации логов ГПУ (больше значение = больше деталей, выше нагрузка)</string>
|
||||
<string name="gpu_log_vulkan_calls">Записывать вызовы Vulkan API</string>
|
||||
@@ -952,25 +947,10 @@
|
||||
<string name="fast_gpu_high">Высокое (512)</string>
|
||||
|
||||
<!-- GPU swizzle texture size -->
|
||||
<string name="gpu_texturesizeswizzle_verysmall">Очень малый (16 МБ)</string>
|
||||
<string name="gpu_texturesizeswizzle_small">Малый (32 МБ)</string>
|
||||
<string name="gpu_texturesizeswizzle_normal">Обычный (128 МБ)</string>
|
||||
<string name="gpu_texturesizeswizzle_large">Большой (256 МБ)</string>
|
||||
<string name="gpu_texturesizeswizzle_verylarge">Очень большой (512 МБ)</string>
|
||||
|
||||
<!-- GPU swizzle streams -->
|
||||
<string name="gpu_swizzle_verylow">Очень низкий (4 МБ)</string>
|
||||
<string name="gpu_swizzle_low">Низкий (8 МБ)</string>
|
||||
<string name="gpu_swizzle_normal">Обычный (16 МБ)</string>
|
||||
<string name="gpu_swizzle_medium">Средний (32 МБ)</string>
|
||||
<string name="gpu_swizzle_high">Высокий (64 МБ)</string>
|
||||
|
||||
<!-- GPU swizzle chunks -->
|
||||
<string name="gpu_swizzlechunk_verylow">Очень малый (32)</string>
|
||||
<string name="gpu_swizzlechunk_low">Малый (64)</string>
|
||||
<string name="gpu_swizzlechunk_normal">Обычный (128)</string>
|
||||
<string name="gpu_swizzlechunk_medium">Средний (256)</string>
|
||||
<string name="gpu_swizzlechunk_high">Большой (512)</string>
|
||||
|
||||
<!-- Temperature Units -->
|
||||
<string name="temperature_celsius">Цельсий</string>
|
||||
|
||||
@@ -512,10 +512,7 @@
|
||||
<string name="rescale_hack_description">Вмикає застарілу обробку масштабування для ігор, використовуючи швидкий шлях масштабування</string>
|
||||
<string name="renderer_asynchronous_shaders">Асинхронні шейдери</string>
|
||||
<string name="renderer_asynchronous_shaders_description">Компілює шейдери асинхронно. Це може зменшити затримки, але також може спричинити графічні баги.</string>
|
||||
<string name="gpu_unswizzle_settings">Налаштування розпакування за допомогою ГП</string>
|
||||
<string name="gpu_unswizzle_settings_description">Налаштуйте розпакування текстур за допомогою ГП або повністю вимкнути його. Відкоригуйте ці налаштування, щоб урівноважити продуктивність і якість завантаження текстур.</string>
|
||||
<string name="gpu_unswizzle_enable">Увімкнути розпакування за допомогою ГП</string>
|
||||
<string name="gpu_unswizzle_disabled">Вимкнено</string>
|
||||
<string name="gpu_unswizzle_texture_size">Максимальний розмір текстур для розпакування ГП за допомогою ГП</string>
|
||||
<string name="gpu_unswizzle_texture_size_description">Встановлює максимальний розмір (МБ) для розпакування текстур за допомогою ГП. ГП швидше справляється з текстурами середніх і великих розмірів, а ЦП ефективніший для дуже маленьких. Налаштуйте, щоб збалансувати ГП-прискоренням і навантаженням на ЦП.</string>
|
||||
<string name="gpu_unswizzle_stream_size">Розмір потоку розпакування за допомогою ГП</string>
|
||||
@@ -565,8 +562,6 @@
|
||||
|
||||
<!-- GPU Logging strings -->
|
||||
<string name="gpu_logging_header">Журналювання ГП</string>
|
||||
<string name="gpu_logging_enabled">Увімкнути журналювання ГП</string>
|
||||
<string name="gpu_logging_enabled_description">Журналювати операції ГП до eden_gpu.log для зневадження драйверів Adreno</string>
|
||||
<string name="gpu_log_level">Рівень журналювання</string>
|
||||
<string name="gpu_log_level_description">Рівень подробиць у журналі ГП (вищий = більше подробиць, більший вплив на швидкодію)</string>
|
||||
<string name="gpu_log_vulkan_calls">Записувати виклики API Vulkan</string>
|
||||
@@ -955,25 +950,10 @@
|
||||
<string name="fast_gpu_high">Високо (512)</string>
|
||||
|
||||
<!-- GPU swizzle texture size -->
|
||||
<string name="gpu_texturesizeswizzle_verysmall">Дуже малий (16 МБ)</string>
|
||||
<string name="gpu_texturesizeswizzle_small">Малий (32 МБ)</string>
|
||||
<string name="gpu_texturesizeswizzle_normal">Нормальний (128 МБ)</string>
|
||||
<string name="gpu_texturesizeswizzle_large">Великий (256 МБ)</string>
|
||||
<string name="gpu_texturesizeswizzle_verylarge">Дуже великий (512 МБ)</string>
|
||||
|
||||
<!-- GPU swizzle streams -->
|
||||
<string name="gpu_swizzle_verylow">Дуже низький (4 МБ)</string>
|
||||
<string name="gpu_swizzle_low">Низький (8 МБ)</string>
|
||||
<string name="gpu_swizzle_normal">Нормальний (16 МБ)</string>
|
||||
<string name="gpu_swizzle_medium">Середній (32 МБ)</string>
|
||||
<string name="gpu_swizzle_high">Високий (64 МБ)</string>
|
||||
|
||||
<!-- GPU swizzle chunks -->
|
||||
<string name="gpu_swizzlechunk_verylow">Дуже низький (32)</string>
|
||||
<string name="gpu_swizzlechunk_low">Низький (64)</string>
|
||||
<string name="gpu_swizzlechunk_normal">Нормальний (128)</string>
|
||||
<string name="gpu_swizzlechunk_medium">Середній (256)</string>
|
||||
<string name="gpu_swizzlechunk_high">Високий (512)</string>
|
||||
|
||||
<!-- Temperature Units -->
|
||||
<string name="temperature_celsius">Цельсій</string>
|
||||
|
||||
@@ -559,8 +559,6 @@
|
||||
|
||||
<!-- GPU Logging strings -->
|
||||
<string name="gpu_logging_header">GPU 日志</string>
|
||||
<string name="gpu_logging_enabled">启用 GPU 日志</string>
|
||||
<string name="gpu_logging_enabled_description">将 GPU 操作记录至 eden_gpu.log 以供调试 Adreno 驱动</string>
|
||||
<string name="gpu_log_level">日志等级</string>
|
||||
<string name="gpu_log_level_description">GPU 日志的详细级别(数值越高 = 细节越多,开销越大)</string>
|
||||
<string name="gpu_log_vulkan_calls">记录 Vulkan API 调用</string>
|
||||
@@ -949,25 +947,10 @@
|
||||
<string name="fast_gpu_high">高 (512)</string>
|
||||
|
||||
<!-- GPU swizzle texture size -->
|
||||
<string name="gpu_texturesizeswizzle_verysmall">极小 (16 MB)</string>
|
||||
<string name="gpu_texturesizeswizzle_small">较小 (32 MB)</string>
|
||||
<string name="gpu_texturesizeswizzle_normal">正常 (128 MB)</string>
|
||||
<string name="gpu_texturesizeswizzle_large">较大 (256 MB)</string>
|
||||
<string name="gpu_texturesizeswizzle_verylarge">极大 (512 MB)</string>
|
||||
|
||||
<!-- GPU swizzle streams -->
|
||||
<string name="gpu_swizzle_verylow">极低 (4 MB)</string>
|
||||
<string name="gpu_swizzle_low">低 (8 MB)</string>
|
||||
<string name="gpu_swizzle_normal">正常 (16 MB)</string>
|
||||
<string name="gpu_swizzle_medium">中 (32 MB)</string>
|
||||
<string name="gpu_swizzle_high">高 (64 MB)</string>
|
||||
|
||||
<!-- GPU swizzle chunks -->
|
||||
<string name="gpu_swizzlechunk_verylow">极低 (32)</string>
|
||||
<string name="gpu_swizzlechunk_low">低 (64)</string>
|
||||
<string name="gpu_swizzlechunk_normal">正常 (128)</string>
|
||||
<string name="gpu_swizzlechunk_medium">中 (256)</string>
|
||||
<string name="gpu_swizzlechunk_high">高 (512)</string>
|
||||
|
||||
<!-- Temperature Units -->
|
||||
<string name="temperature_celsius">摄氏度</string>
|
||||
|
||||
@@ -545,54 +545,6 @@
|
||||
<item>2</item>
|
||||
</integer-array>
|
||||
|
||||
<string-array name="gpuTextureSizeSwizzleEntries">
|
||||
<item>@string/gpu_texturesizeswizzle_verysmall</item>
|
||||
<item>@string/gpu_texturesizeswizzle_small</item>
|
||||
<item>@string/gpu_texturesizeswizzle_normal</item>
|
||||
<item>@string/gpu_texturesizeswizzle_large</item>
|
||||
<item>@string/gpu_texturesizeswizzle_verylarge</item>
|
||||
</string-array>
|
||||
|
||||
<integer-array name="gpuTextureSizeSwizzleValues">
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
<item>3</item>
|
||||
<item>4</item>
|
||||
</integer-array>
|
||||
|
||||
<string-array name="gpuSwizzleEntries">
|
||||
<item>@string/gpu_swizzle_verylow</item>
|
||||
<item>@string/gpu_swizzle_low</item>
|
||||
<item>@string/gpu_swizzle_normal</item>
|
||||
<item>@string/gpu_swizzle_medium</item>
|
||||
<item>@string/gpu_swizzle_high</item>
|
||||
</string-array>
|
||||
|
||||
<integer-array name="gpuSwizzleValues">
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
<item>3</item>
|
||||
<item>4</item>
|
||||
</integer-array>
|
||||
|
||||
<string-array name="gpuSwizzleChunkEntries">
|
||||
<item>@string/gpu_swizzlechunk_verylow</item>
|
||||
<item>@string/gpu_swizzlechunk_low</item>
|
||||
<item>@string/gpu_swizzlechunk_normal</item>
|
||||
<item>@string/gpu_swizzlechunk_medium</item>
|
||||
<item>@string/gpu_swizzlechunk_high</item>
|
||||
</string-array>
|
||||
|
||||
<integer-array name="gpuSwizzleChunkValues">
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
<item>3</item>
|
||||
<item>4</item>
|
||||
</integer-array>
|
||||
|
||||
<string-array name="temperatureUnitEntries">
|
||||
<item>@string/temperature_celsius</item>
|
||||
<item>@string/temperature_fahrenheit</item>
|
||||
|
||||
@@ -503,6 +503,8 @@
|
||||
<string name="renderer_reactive_flushing_description">Improves rendering accuracy in some games at the cost of performance.</string>
|
||||
<string name="enable_buffer_history">Enable buffer history</string>
|
||||
<string name="enable_buffer_history_description">Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games.</string>
|
||||
<string name="enable_gpu_buffer_readback">Enable GPU Buffer Readback</string>
|
||||
<string name="enable_gpu_buffer_readback_description">Preserves GPU-modified buffer data by reading it back before uploads. Some games require this to render certain effects properly. May cause issues if the hardware cannot handle the additional workload.</string>
|
||||
<string name="use_optimized_vertex_buffers">Optimized Vertex Buffers</string>
|
||||
<string name="use_optimized_vertex_buffers_description">Enables optimized vertex buffer binding for improved performance. Requires Mesa 26.0+ Turnip drivers/ QCOM drivers. Will crash on older Turnip drivers (25.3 and below).</string>
|
||||
|
||||
@@ -522,10 +524,7 @@
|
||||
<string name="rescale_hack_description">Enables a legacy handling for the rescale configuration pass for games by using a quick rescale path</string>
|
||||
<string name="renderer_asynchronous_shaders">Use asynchronous shaders</string>
|
||||
<string name="renderer_asynchronous_shaders_description">Compiles shaders asynchronously. This may reduce stutters but may also introduce glitches.</string>
|
||||
<string name="gpu_unswizzle_settings">GPU Unswizzle Settings</string>
|
||||
<string name="gpu_unswizzle_settings_description">Configure GPU-based texture unswizzling parameters or disable it entirely. Adjust these settings to balance performance and texture loading quality.</string>
|
||||
<string name="gpu_unswizzle_enable">Enable GPU Unswizzle</string>
|
||||
<string name="gpu_unswizzle_disabled">Disabled</string>
|
||||
<string name="gpu_unswizzle_texture_size">GPU Unswizzle Max Texture Size</string>
|
||||
<string name="gpu_unswizzle_texture_size_description">Sets the maximum size (MB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead.</string>
|
||||
<string name="gpu_unswizzle_stream_size">GPU Unswizzle Stream Size</string>
|
||||
@@ -575,14 +574,16 @@
|
||||
|
||||
<!-- GPU Logging strings -->
|
||||
<string name="gpu_logging_header">GPU Logging</string>
|
||||
<string name="gpu_logging_enabled">Enable GPU Logging</string>
|
||||
<string name="gpu_logging_enabled_description">Log GPU operations to eden_gpu.log for debugging Adreno drivers</string>
|
||||
<string name="gpu_log_level">Log Level</string>
|
||||
<string name="gpu_log_level_description">Detail level for GPU logs (higher = more detail, more overhead)</string>
|
||||
<string name="gpu_log_vulkan_calls">Log Vulkan API Calls</string>
|
||||
<string name="gpu_log_vulkan_calls_description">Track all Vulkan API calls in ring buffer</string>
|
||||
<string name="gpu_log_shader_dumps">Dump Shaders</string>
|
||||
<string name="gpu_log_shader_dumps_description">Save compiled shader SPIR-V to files</string>
|
||||
<string name="gpu_log_shader_dumps">Dump SPIR-V Shaders</string>
|
||||
<string name="gpu_log_shader_dumps_description">Save recompiled SPIR-V binaries (.spv) to dump folder. Inspect with spirv-dis/spirv-cross/spirv-val.</string>
|
||||
<string name="dump_guest_shaders">Dump Guest (Maxwell) Shaders</string>
|
||||
<string name="dump_guest_shaders_description">Save Maxwell guest shader bytecode files (*.ash) to dump folder. Inspect with nvdisasm.</string>
|
||||
<string name="dump_macros">Dump Maxwell Macros</string>
|
||||
<string name="dump_macros_description">Save Maxwell macro program files (*.macro) to dump folder. Inspect with envydis.</string>
|
||||
<string name="gpu_log_memory_tracking">Track GPU Memory</string>
|
||||
<string name="gpu_log_memory_tracking_description">Monitor GPU memory allocations and deallocations</string>
|
||||
<string name="gpu_log_driver_debug">Driver Debug Info</string>
|
||||
@@ -974,25 +975,10 @@
|
||||
<string name="fast_gpu_high">High (512)</string>
|
||||
|
||||
<!-- GPU swizzle texture size -->
|
||||
<string name="gpu_texturesizeswizzle_verysmall">Very Small (16 MB)</string>
|
||||
<string name="gpu_texturesizeswizzle_small">Small (32 MB)</string>
|
||||
<string name="gpu_texturesizeswizzle_normal">Normal (128 MB)</string>
|
||||
<string name="gpu_texturesizeswizzle_large">Large (256 MB)</string>
|
||||
<string name="gpu_texturesizeswizzle_verylarge">Very Large (512 MB)</string>
|
||||
|
||||
<!-- GPU swizzle streams -->
|
||||
<string name="gpu_swizzle_verylow">Very Low (4 MB)</string>
|
||||
<string name="gpu_swizzle_low">Low (8 MB)</string>
|
||||
<string name="gpu_swizzle_normal">Normal (16 MB)</string>
|
||||
<string name="gpu_swizzle_medium">Medium (32 MB)</string>
|
||||
<string name="gpu_swizzle_high">High (64 MB)</string>
|
||||
|
||||
<!-- GPU swizzle chunks -->
|
||||
<string name="gpu_swizzlechunk_verylow">Very Low (32)</string>
|
||||
<string name="gpu_swizzlechunk_low">Low (64)</string>
|
||||
<string name="gpu_swizzlechunk_normal">Normal (128)</string>
|
||||
<string name="gpu_swizzlechunk_medium">Medium (256)</string>
|
||||
<string name="gpu_swizzlechunk_high">High (512)</string>
|
||||
|
||||
<!-- Temperature Units -->
|
||||
<string name="temperature_celsius">Celsius</string>
|
||||
|
||||
@@ -50,7 +50,6 @@ add_library(
|
||||
elf.h
|
||||
error.cpp
|
||||
error.h
|
||||
expected.h
|
||||
fiber.cpp
|
||||
fiber.h
|
||||
fixed_point.h
|
||||
|
||||
@@ -1,986 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
// This is based on the proposed implementation of std::expected (P0323)
|
||||
// https://github.com/TartanLlama/expected/blob/master/include/tl/expected.hpp
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace Common {
|
||||
|
||||
template <typename T, typename E>
|
||||
class Expected;
|
||||
|
||||
template <typename E>
|
||||
class Unexpected {
|
||||
public:
|
||||
Unexpected() = delete;
|
||||
|
||||
constexpr explicit Unexpected(const E& e) : m_val{e} {}
|
||||
|
||||
constexpr explicit Unexpected(E&& e) : m_val{std::move(e)} {}
|
||||
|
||||
constexpr E& value() & {
|
||||
return m_val;
|
||||
}
|
||||
|
||||
constexpr const E& value() const& {
|
||||
return m_val;
|
||||
}
|
||||
|
||||
constexpr E&& value() && {
|
||||
return std::move(m_val);
|
||||
}
|
||||
|
||||
constexpr const E&& value() const&& {
|
||||
return std::move(m_val);
|
||||
}
|
||||
|
||||
private:
|
||||
E m_val;
|
||||
};
|
||||
|
||||
template <typename E>
|
||||
constexpr auto operator<=>(const Unexpected<E>& lhs, const Unexpected<E>& rhs) {
|
||||
return lhs.value() <=> rhs.value();
|
||||
}
|
||||
|
||||
struct unexpect_t {
|
||||
constexpr explicit unexpect_t() = default;
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct no_init_t {
|
||||
constexpr explicit no_init_t() = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* This specialization is for when T is not trivially destructible,
|
||||
* so the destructor must be called on destruction of `expected'
|
||||
* Additionally, this requires E to be trivially destructible
|
||||
*/
|
||||
template <typename T, typename E, bool = std::is_trivially_destructible_v<T>>
|
||||
requires std::is_trivially_destructible_v<E>
|
||||
struct expected_storage_base {
|
||||
constexpr expected_storage_base() : m_val{T{}}, m_has_val{true} {}
|
||||
|
||||
constexpr expected_storage_base(no_init_t) : m_has_val{false} {}
|
||||
|
||||
template <typename... Args, std::enable_if_t<std::is_constructible_v<T, Args&&...>>* = nullptr>
|
||||
constexpr expected_storage_base(std::in_place_t, Args&&... args)
|
||||
: m_val{std::forward<Args>(args)...}, m_has_val{true} {}
|
||||
|
||||
template <typename U, typename... Args,
|
||||
std::enable_if_t<std::is_constructible_v<T, std::initializer_list<U>&, Args&&...>>* =
|
||||
nullptr>
|
||||
constexpr expected_storage_base(std::in_place_t, std::initializer_list<U> il, Args&&... args)
|
||||
: m_val{il, std::forward<Args>(args)...}, m_has_val{true} {}
|
||||
|
||||
template <typename... Args, std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
|
||||
constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
|
||||
: m_unexpect{std::forward<Args>(args)...}, m_has_val{false} {}
|
||||
|
||||
template <typename U, typename... Args,
|
||||
std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&, Args&&...>>* =
|
||||
nullptr>
|
||||
constexpr explicit expected_storage_base(unexpect_t, std::initializer_list<U> il,
|
||||
Args&&... args)
|
||||
: m_unexpect{il, std::forward<Args>(args)...}, m_has_val{false} {}
|
||||
|
||||
~expected_storage_base() {
|
||||
if (m_has_val) {
|
||||
m_val.~T();
|
||||
}
|
||||
}
|
||||
|
||||
union {
|
||||
T m_val;
|
||||
Unexpected<E> m_unexpect;
|
||||
};
|
||||
|
||||
bool m_has_val;
|
||||
};
|
||||
|
||||
/**
|
||||
* This specialization is for when T is trivially destructible,
|
||||
* so the destructor of `expected` can be trivial
|
||||
* Additionally, this requires E to be trivially destructible
|
||||
*/
|
||||
template <typename T, typename E>
|
||||
requires std::is_trivially_destructible_v<E>
|
||||
struct expected_storage_base<T, E, true> {
|
||||
constexpr expected_storage_base() : m_val{T{}}, m_has_val{true} {}
|
||||
|
||||
constexpr expected_storage_base(no_init_t) : m_has_val{false} {}
|
||||
|
||||
template <typename... Args, std::enable_if_t<std::is_constructible_v<T, Args&&...>>* = nullptr>
|
||||
constexpr expected_storage_base(std::in_place_t, Args&&... args)
|
||||
: m_val{std::forward<Args>(args)...}, m_has_val{true} {}
|
||||
|
||||
template <typename U, typename... Args,
|
||||
std::enable_if_t<std::is_constructible_v<T, std::initializer_list<U>&, Args&&...>>* =
|
||||
nullptr>
|
||||
constexpr expected_storage_base(std::in_place_t, std::initializer_list<U> il, Args&&... args)
|
||||
: m_val{il, std::forward<Args>(args)...}, m_has_val{true} {}
|
||||
|
||||
template <typename... Args, std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
|
||||
constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
|
||||
: m_unexpect{std::forward<Args>(args)...}, m_has_val{false} {}
|
||||
|
||||
template <typename U, typename... Args,
|
||||
std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&, Args&&...>>* =
|
||||
nullptr>
|
||||
constexpr explicit expected_storage_base(unexpect_t, std::initializer_list<U> il,
|
||||
Args&&... args)
|
||||
: m_unexpect{il, std::forward<Args>(args)...}, m_has_val{false} {}
|
||||
|
||||
~expected_storage_base() = default;
|
||||
|
||||
union {
|
||||
T m_val;
|
||||
Unexpected<E> m_unexpect;
|
||||
};
|
||||
|
||||
bool m_has_val;
|
||||
};
|
||||
|
||||
template <typename T, typename E>
|
||||
struct expected_operations_base : expected_storage_base<T, E> {
|
||||
using expected_storage_base<T, E>::expected_storage_base;
|
||||
|
||||
template <typename... Args>
|
||||
void construct(Args&&... args) noexcept {
|
||||
new (std::addressof(this->m_val)) T{std::forward<Args>(args)...};
|
||||
this->m_has_val = true;
|
||||
}
|
||||
|
||||
template <typename Rhs>
|
||||
void construct_with(Rhs&& rhs) noexcept {
|
||||
new (std::addressof(this->m_val)) T{std::forward<Rhs>(rhs).get()};
|
||||
this->m_has_val = true;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void construct_error(Args&&... args) noexcept {
|
||||
new (std::addressof(this->m_unexpect)) Unexpected<E>{std::forward<Args>(args)...};
|
||||
this->m_has_val = false;
|
||||
}
|
||||
|
||||
void assign(const expected_operations_base& rhs) noexcept {
|
||||
if (!this->m_has_val && rhs.m_has_val) {
|
||||
geterr().~Unexpected<E>();
|
||||
construct(rhs.get());
|
||||
} else {
|
||||
assign_common(rhs);
|
||||
}
|
||||
}
|
||||
|
||||
void assign(expected_operations_base&& rhs) noexcept {
|
||||
if (!this->m_has_val && rhs.m_has_val) {
|
||||
geterr().~Unexpected<E>();
|
||||
construct(std::move(rhs).get());
|
||||
} else {
|
||||
assign_common(rhs);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Rhs>
|
||||
void assign_common(Rhs&& rhs) {
|
||||
if (this->m_has_val) {
|
||||
if (rhs.m_has_val) {
|
||||
get() = std::forward<Rhs>(rhs).get();
|
||||
} else {
|
||||
destroy_val();
|
||||
construct_error(std::forward<Rhs>(rhs).geterr());
|
||||
}
|
||||
} else {
|
||||
if (!rhs.m_has_val) {
|
||||
geterr() = std::forward<Rhs>(rhs).geterr();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool has_value() const {
|
||||
return this->m_has_val;
|
||||
}
|
||||
|
||||
constexpr T& get() & {
|
||||
return this->m_val;
|
||||
}
|
||||
|
||||
constexpr const T& get() const& {
|
||||
return this->m_val;
|
||||
}
|
||||
|
||||
constexpr T&& get() && {
|
||||
return std::move(this->m_val);
|
||||
}
|
||||
|
||||
constexpr const T&& get() const&& {
|
||||
return std::move(this->m_val);
|
||||
}
|
||||
|
||||
constexpr Unexpected<E>& geterr() & {
|
||||
return this->m_unexpect;
|
||||
}
|
||||
|
||||
constexpr const Unexpected<E>& geterr() const& {
|
||||
return this->m_unexpect;
|
||||
}
|
||||
|
||||
constexpr Unexpected<E>&& geterr() && {
|
||||
return std::move(this->m_unexpect);
|
||||
}
|
||||
|
||||
constexpr const Unexpected<E>&& geterr() const&& {
|
||||
return std::move(this->m_unexpect);
|
||||
}
|
||||
|
||||
constexpr void destroy_val() {
|
||||
get().~T();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This manages conditionally having a trivial copy constructor
|
||||
* This specialization is for when T is trivially copy constructible
|
||||
* Additionally, this requires E to be trivially copy constructible
|
||||
*/
|
||||
template <typename T, typename E, bool = std::is_trivially_copy_constructible_v<T>>
|
||||
requires std::is_trivially_copy_constructible_v<E>
|
||||
struct expected_copy_base : expected_operations_base<T, E> {
|
||||
using expected_operations_base<T, E>::expected_operations_base;
|
||||
};
|
||||
|
||||
/**
|
||||
* This specialization is for when T is not trivially copy constructible
|
||||
* Additionally, this requires E to be trivially copy constructible
|
||||
*/
|
||||
template <typename T, typename E>
|
||||
requires std::is_trivially_copy_constructible_v<E>
|
||||
struct expected_copy_base<T, E, false> : expected_operations_base<T, E> {
|
||||
using expected_operations_base<T, E>::expected_operations_base;
|
||||
|
||||
expected_copy_base() = default;
|
||||
|
||||
expected_copy_base(const expected_copy_base& rhs)
|
||||
: expected_operations_base<T, E>{no_init_t{}} {
|
||||
if (rhs.has_value()) {
|
||||
this->construct_with(rhs);
|
||||
} else {
|
||||
this->construct_error(rhs.geterr());
|
||||
}
|
||||
}
|
||||
|
||||
expected_copy_base(expected_copy_base&&) = default;
|
||||
|
||||
expected_copy_base& operator=(const expected_copy_base&) = default;
|
||||
|
||||
expected_copy_base& operator=(expected_copy_base&&) = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* This manages conditionally having a trivial move constructor
|
||||
* This specialization is for when T is trivially move constructible
|
||||
* Additionally, this requires E to be trivially move constructible
|
||||
*/
|
||||
template <typename T, typename E, bool = std::is_trivially_move_constructible_v<T>>
|
||||
requires std::is_trivially_move_constructible_v<E>
|
||||
struct expected_move_base : expected_copy_base<T, E> {
|
||||
using expected_copy_base<T, E>::expected_copy_base;
|
||||
};
|
||||
|
||||
/**
|
||||
* This specialization is for when T is not trivially move constructible
|
||||
* Additionally, this requires E to be trivially move constructible
|
||||
*/
|
||||
template <typename T, typename E>
|
||||
requires std::is_trivially_move_constructible_v<E>
|
||||
struct expected_move_base<T, E, false> : expected_copy_base<T, E> {
|
||||
using expected_copy_base<T, E>::expected_copy_base;
|
||||
|
||||
expected_move_base() = default;
|
||||
|
||||
expected_move_base(const expected_move_base&) = default;
|
||||
|
||||
expected_move_base(expected_move_base&& rhs) noexcept(std::is_nothrow_move_constructible_v<T>)
|
||||
: expected_copy_base<T, E>{no_init_t{}} {
|
||||
if (rhs.has_value()) {
|
||||
this->construct_with(std::move(rhs));
|
||||
} else {
|
||||
this->construct_error(std::move(rhs.geterr()));
|
||||
}
|
||||
}
|
||||
|
||||
expected_move_base& operator=(const expected_move_base&) = default;
|
||||
|
||||
expected_move_base& operator=(expected_move_base&&) = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* This manages conditionally having a trivial copy assignment operator
|
||||
* This specialization is for when T is trivially copy assignable
|
||||
* Additionally, this requires E to be trivially copy assignable
|
||||
*/
|
||||
template <typename T, typename E,
|
||||
bool = std::conjunction_v<std::is_trivially_copy_assignable<T>,
|
||||
std::is_trivially_copy_constructible<T>,
|
||||
std::is_trivially_destructible<T>>>
|
||||
requires std::conjunction_v<std::is_trivially_copy_assignable<E>,
|
||||
std::is_trivially_copy_constructible<E>,
|
||||
std::is_trivially_destructible<E>>
|
||||
struct expected_copy_assign_base : expected_move_base<T, E> {
|
||||
using expected_move_base<T, E>::expected_move_base;
|
||||
};
|
||||
|
||||
/**
|
||||
* This specialization is for when T is not trivially copy assignable
|
||||
* Additionally, this requires E to be trivially copy assignable
|
||||
*/
|
||||
template <typename T, typename E>
|
||||
requires std::conjunction_v<std::is_trivially_copy_assignable<E>,
|
||||
std::is_trivially_copy_constructible<E>,
|
||||
std::is_trivially_destructible<E>>
|
||||
struct expected_copy_assign_base<T, E, false> : expected_move_base<T, E> {
|
||||
using expected_move_base<T, E>::expected_move_base;
|
||||
|
||||
expected_copy_assign_base() = default;
|
||||
|
||||
expected_copy_assign_base(const expected_copy_assign_base&) = default;
|
||||
|
||||
expected_copy_assign_base(expected_copy_assign_base&&) = default;
|
||||
|
||||
expected_copy_assign_base& operator=(const expected_copy_assign_base& rhs) {
|
||||
this->assign(rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
expected_copy_assign_base& operator=(expected_copy_assign_base&&) = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* This manages conditionally having a trivial move assignment operator
|
||||
* This specialization is for when T is trivially move assignable
|
||||
* Additionally, this requires E to be trivially move assignable
|
||||
*/
|
||||
template <typename T, typename E,
|
||||
bool = std::conjunction_v<std::is_trivially_move_assignable<T>,
|
||||
std::is_trivially_move_constructible<T>,
|
||||
std::is_trivially_destructible<T>>>
|
||||
requires std::conjunction_v<std::is_trivially_move_assignable<E>,
|
||||
std::is_trivially_move_constructible<E>,
|
||||
std::is_trivially_destructible<E>>
|
||||
struct expected_move_assign_base : expected_copy_assign_base<T, E> {
|
||||
using expected_copy_assign_base<T, E>::expected_copy_assign_base;
|
||||
};
|
||||
|
||||
/**
|
||||
* This specialization is for when T is not trivially move assignable
|
||||
* Additionally, this requires E to be trivially move assignable
|
||||
*/
|
||||
template <typename T, typename E>
|
||||
requires std::conjunction_v<std::is_trivially_move_assignable<E>,
|
||||
std::is_trivially_move_constructible<E>,
|
||||
std::is_trivially_destructible<E>>
|
||||
struct expected_move_assign_base<T, E, false> : expected_copy_assign_base<T, E> {
|
||||
using expected_copy_assign_base<T, E>::expected_copy_assign_base;
|
||||
|
||||
expected_move_assign_base() = default;
|
||||
|
||||
expected_move_assign_base(const expected_move_assign_base&) = default;
|
||||
|
||||
expected_move_assign_base(expected_move_assign_base&&) = default;
|
||||
|
||||
expected_move_assign_base& operator=(const expected_move_assign_base&) = default;
|
||||
|
||||
expected_move_assign_base& operator=(expected_move_assign_base&& rhs) noexcept(
|
||||
std::conjunction_v<std::is_nothrow_move_constructible<T>,
|
||||
std::is_nothrow_move_assignable<T>>) {
|
||||
this->assign(std::move(rhs));
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* expected_delete_ctor_base will conditionally delete copy and move constructors
|
||||
* depending on whether T is copy/move constructible
|
||||
* Additionally, this requires E to be copy/move constructible
|
||||
*/
|
||||
template <typename T, typename E, bool EnableCopy = std::is_copy_constructible_v<T>,
|
||||
bool EnableMove = std::is_move_constructible_v<T>>
|
||||
requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>>
|
||||
struct expected_delete_ctor_base {
|
||||
expected_delete_ctor_base() = default;
|
||||
expected_delete_ctor_base(const expected_delete_ctor_base&) = default;
|
||||
expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = default;
|
||||
expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) = default;
|
||||
expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept = default;
|
||||
};
|
||||
|
||||
template <typename T, typename E>
|
||||
requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>>
|
||||
struct expected_delete_ctor_base<T, E, true, false> {
|
||||
expected_delete_ctor_base() = default;
|
||||
expected_delete_ctor_base(const expected_delete_ctor_base&) = default;
|
||||
expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = delete;
|
||||
expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) = default;
|
||||
expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept = default;
|
||||
};
|
||||
|
||||
template <typename T, typename E>
|
||||
requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>>
|
||||
struct expected_delete_ctor_base<T, E, false, true> {
|
||||
expected_delete_ctor_base() = default;
|
||||
expected_delete_ctor_base(const expected_delete_ctor_base&) = delete;
|
||||
expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = default;
|
||||
expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) = default;
|
||||
expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept = default;
|
||||
};
|
||||
|
||||
template <typename T, typename E>
|
||||
requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>>
|
||||
struct expected_delete_ctor_base<T, E, false, false> {
|
||||
expected_delete_ctor_base() = default;
|
||||
expected_delete_ctor_base(const expected_delete_ctor_base&) = delete;
|
||||
expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = delete;
|
||||
expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) = default;
|
||||
expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* expected_delete_assign_base will conditionally delete copy and move assignment operators
|
||||
* depending on whether T is copy/move constructible + assignable
|
||||
* Additionally, this requires E to be copy/move constructible + assignable
|
||||
*/
|
||||
template <
|
||||
typename T, typename E,
|
||||
bool EnableCopy = std::conjunction_v<std::is_copy_constructible<T>, std::is_copy_assignable<T>>,
|
||||
bool EnableMove = std::conjunction_v<std::is_move_constructible<T>, std::is_move_assignable<T>>>
|
||||
requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>,
|
||||
std::is_copy_assignable<E>, std::is_move_assignable<E>>
|
||||
struct expected_delete_assign_base {
|
||||
expected_delete_assign_base() = default;
|
||||
expected_delete_assign_base(const expected_delete_assign_base&) = default;
|
||||
expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default;
|
||||
expected_delete_assign_base& operator=(const expected_delete_assign_base&) = default;
|
||||
expected_delete_assign_base& operator=(expected_delete_assign_base&&) noexcept = default;
|
||||
};
|
||||
|
||||
template <typename T, typename E>
|
||||
requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>,
|
||||
std::is_copy_assignable<E>, std::is_move_assignable<E>>
|
||||
struct expected_delete_assign_base<T, E, true, false> {
|
||||
expected_delete_assign_base() = default;
|
||||
expected_delete_assign_base(const expected_delete_assign_base&) = default;
|
||||
expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default;
|
||||
expected_delete_assign_base& operator=(const expected_delete_assign_base&) = default;
|
||||
expected_delete_assign_base& operator=(expected_delete_assign_base&&) noexcept = delete;
|
||||
};
|
||||
|
||||
template <typename T, typename E>
|
||||
requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>,
|
||||
std::is_copy_assignable<E>, std::is_move_assignable<E>>
|
||||
struct expected_delete_assign_base<T, E, false, true> {
|
||||
expected_delete_assign_base() = default;
|
||||
expected_delete_assign_base(const expected_delete_assign_base&) = default;
|
||||
expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default;
|
||||
expected_delete_assign_base& operator=(const expected_delete_assign_base&) = delete;
|
||||
expected_delete_assign_base& operator=(expected_delete_assign_base&&) noexcept = default;
|
||||
};
|
||||
|
||||
template <typename T, typename E>
|
||||
requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>,
|
||||
std::is_copy_assignable<E>, std::is_move_assignable<E>>
|
||||
struct expected_delete_assign_base<T, E, false, false> {
|
||||
expected_delete_assign_base() = default;
|
||||
expected_delete_assign_base(const expected_delete_assign_base&) = default;
|
||||
expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default;
|
||||
expected_delete_assign_base& operator=(const expected_delete_assign_base&) = delete;
|
||||
expected_delete_assign_base& operator=(expected_delete_assign_base&&) noexcept = delete;
|
||||
};
|
||||
|
||||
/**
|
||||
* This is needed to be able to construct the expected_default_ctor_base which follows,
|
||||
* while still conditionally deleting the default constructor.
|
||||
*/
|
||||
struct default_constructor_tag {
|
||||
constexpr explicit default_constructor_tag() = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* expected_default_ctor_base will ensure that expected
|
||||
* has a deleted default constructor if T is not default constructible
|
||||
* This specialization is for when T is default constructible
|
||||
*/
|
||||
template <typename T, typename E, bool Enable = std::is_default_constructible_v<T>>
|
||||
struct expected_default_ctor_base {
|
||||
constexpr expected_default_ctor_base() noexcept = default;
|
||||
constexpr expected_default_ctor_base(expected_default_ctor_base const&) noexcept = default;
|
||||
constexpr expected_default_ctor_base(expected_default_ctor_base&&) noexcept = default;
|
||||
expected_default_ctor_base& operator=(expected_default_ctor_base const&) noexcept = default;
|
||||
expected_default_ctor_base& operator=(expected_default_ctor_base&&) noexcept = default;
|
||||
|
||||
constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
|
||||
};
|
||||
|
||||
template <typename T, typename E>
|
||||
struct expected_default_ctor_base<T, E, false> {
|
||||
constexpr expected_default_ctor_base() noexcept = delete;
|
||||
constexpr expected_default_ctor_base(expected_default_ctor_base const&) noexcept = default;
|
||||
constexpr expected_default_ctor_base(expected_default_ctor_base&&) noexcept = default;
|
||||
expected_default_ctor_base& operator=(expected_default_ctor_base const&) noexcept = default;
|
||||
expected_default_ctor_base& operator=(expected_default_ctor_base&&) noexcept = default;
|
||||
|
||||
constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
|
||||
};
|
||||
|
||||
template <typename T, typename E, typename U>
|
||||
using expected_enable_forward_value =
|
||||
std::enable_if_t<std::is_constructible_v<T, U&&> &&
|
||||
!std::is_same_v<std::remove_cvref_t<U>, std::in_place_t> &&
|
||||
!std::is_same_v<Expected<T, E>, std::remove_cvref_t<U>> &&
|
||||
!std::is_same_v<Unexpected<E>, std::remove_cvref_t<U>>>;
|
||||
|
||||
template <typename T, typename E, typename U, typename G, typename UR, typename GR>
|
||||
using expected_enable_from_other = std::enable_if_t<
|
||||
std::is_constructible_v<T, UR> && std::is_constructible_v<E, GR> &&
|
||||
!std::is_constructible_v<T, Expected<U, G>&> && !std::is_constructible_v<T, Expected<U, G>&&> &&
|
||||
!std::is_constructible_v<T, const Expected<U, G>&> &&
|
||||
!std::is_constructible_v<T, const Expected<U, G>&&> &&
|
||||
!std::is_convertible_v<Expected<U, G>&, T> && !std::is_convertible_v<Expected<U, G>&&, T> &&
|
||||
!std::is_convertible_v<const Expected<U, G>&, T> &&
|
||||
!std::is_convertible_v<const Expected<U, G>&&, T>>;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename T, typename E>
|
||||
class Expected : private detail::expected_move_assign_base<T, E>,
|
||||
private detail::expected_delete_ctor_base<T, E>,
|
||||
private detail::expected_delete_assign_base<T, E>,
|
||||
private detail::expected_default_ctor_base<T, E> {
|
||||
public:
|
||||
using value_type = T;
|
||||
using error_type = E;
|
||||
using unexpected_type = Unexpected<E>;
|
||||
|
||||
constexpr Expected() = default;
|
||||
constexpr Expected(const Expected&) = default;
|
||||
constexpr Expected(Expected&&) = default;
|
||||
Expected& operator=(const Expected&) = default;
|
||||
Expected& operator=(Expected&&) = default;
|
||||
|
||||
template <typename... Args, std::enable_if_t<std::is_constructible_v<T, Args&&...>>* = nullptr>
|
||||
constexpr Expected(std::in_place_t, Args&&... args)
|
||||
: impl_base{std::in_place, std::forward<Args>(args)...},
|
||||
ctor_base{detail::default_constructor_tag{}} {}
|
||||
|
||||
template <typename U, typename... Args,
|
||||
std::enable_if_t<std::is_constructible_v<T, std::initializer_list<U>&, Args&&...>>* =
|
||||
nullptr>
|
||||
constexpr Expected(std::in_place_t, std::initializer_list<U> il, Args&&... args)
|
||||
: impl_base{std::in_place, il, std::forward<Args>(args)...},
|
||||
ctor_base{detail::default_constructor_tag{}} {}
|
||||
|
||||
template <typename G = E, std::enable_if_t<std::is_constructible_v<E, const G&>>* = nullptr,
|
||||
std::enable_if_t<!std::is_convertible_v<const G&, E>>* = nullptr>
|
||||
constexpr explicit Expected(const Unexpected<G>& e)
|
||||
: impl_base{unexpect_t{}, e.value()}, ctor_base{detail::default_constructor_tag{}} {}
|
||||
|
||||
template <typename G = E, std::enable_if_t<std::is_constructible_v<E, const G&>>* = nullptr,
|
||||
std::enable_if_t<std::is_convertible_v<const G&, E>>* = nullptr>
|
||||
constexpr Expected(Unexpected<G> const& e)
|
||||
: impl_base{unexpect_t{}, e.value()}, ctor_base{detail::default_constructor_tag{}} {}
|
||||
|
||||
template <typename G = E, std::enable_if_t<std::is_constructible_v<E, G&&>>* = nullptr,
|
||||
std::enable_if_t<!std::is_convertible_v<G&&, E>>* = nullptr>
|
||||
constexpr explicit Expected(Unexpected<G>&& e) noexcept(std::is_nothrow_constructible_v<E, G&&>)
|
||||
: impl_base{unexpect_t{}, std::move(e.value())}, ctor_base{
|
||||
detail::default_constructor_tag{}} {}
|
||||
|
||||
template <typename G = E, std::enable_if_t<std::is_constructible_v<E, G&&>>* = nullptr,
|
||||
std::enable_if_t<std::is_convertible_v<G&&, E>>* = nullptr>
|
||||
constexpr Expected(Unexpected<G>&& e) noexcept(std::is_nothrow_constructible_v<E, G&&>)
|
||||
: impl_base{unexpect_t{}, std::move(e.value())}, ctor_base{
|
||||
detail::default_constructor_tag{}} {}
|
||||
|
||||
template <typename... Args, std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
|
||||
constexpr explicit Expected(unexpect_t, Args&&... args)
|
||||
: impl_base{unexpect_t{}, std::forward<Args>(args)...},
|
||||
ctor_base{detail::default_constructor_tag{}} {}
|
||||
|
||||
template <typename U, typename... Args,
|
||||
std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&, Args&&...>>* =
|
||||
nullptr>
|
||||
constexpr explicit Expected(unexpect_t, std::initializer_list<U> il, Args&&... args)
|
||||
: impl_base{unexpect_t{}, il, std::forward<Args>(args)...},
|
||||
ctor_base{detail::default_constructor_tag{}} {}
|
||||
|
||||
template <typename U, typename G,
|
||||
std::enable_if_t<!(std::is_convertible_v<U const&, T> &&
|
||||
std::is_convertible_v<G const&, E>)>* = nullptr,
|
||||
detail::expected_enable_from_other<T, E, U, G, const U&, const G&>* = nullptr>
|
||||
constexpr explicit Expected(const Expected<U, G>& rhs)
|
||||
: ctor_base{detail::default_constructor_tag{}} {
|
||||
if (rhs.has_value()) {
|
||||
this->construct(*rhs);
|
||||
} else {
|
||||
this->construct_error(rhs.error());
|
||||
}
|
||||
}
|
||||
|
||||
template <typename U, typename G,
|
||||
std::enable_if_t<(std::is_convertible_v<U const&, T> &&
|
||||
std::is_convertible_v<G const&, E>)>* = nullptr,
|
||||
detail::expected_enable_from_other<T, E, U, G, const U&, const G&>* = nullptr>
|
||||
constexpr Expected(const Expected<U, G>& rhs) : ctor_base{detail::default_constructor_tag{}} {
|
||||
if (rhs.has_value()) {
|
||||
this->construct(*rhs);
|
||||
} else {
|
||||
this->construct_error(rhs.error());
|
||||
}
|
||||
}
|
||||
|
||||
template <typename U, typename G,
|
||||
std::enable_if_t<!(std::is_convertible_v<U&&, T> && std::is_convertible_v<G&&, E>)>* =
|
||||
nullptr,
|
||||
detail::expected_enable_from_other<T, E, U, G, U&&, G&&>* = nullptr>
|
||||
constexpr explicit Expected(Expected<U, G>&& rhs)
|
||||
: ctor_base{detail::default_constructor_tag{}} {
|
||||
if (rhs.has_value()) {
|
||||
this->construct(std::move(*rhs));
|
||||
} else {
|
||||
this->construct_error(std::move(rhs.error()));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename U, typename G,
|
||||
std::enable_if_t<(std::is_convertible_v<U&&, T> && std::is_convertible_v<G&&, E>)>* =
|
||||
nullptr,
|
||||
detail::expected_enable_from_other<T, E, U, G, U&&, G&&>* = nullptr>
|
||||
constexpr Expected(Expected<U, G>&& rhs) : ctor_base{detail::default_constructor_tag{}} {
|
||||
if (rhs.has_value()) {
|
||||
this->construct(std::move(*rhs));
|
||||
} else {
|
||||
this->construct_error(std::move(rhs.error()));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename U = T, std::enable_if_t<!std::is_convertible_v<U&&, T>>* = nullptr,
|
||||
detail::expected_enable_forward_value<T, E, U>* = nullptr>
|
||||
constexpr explicit Expected(U&& v) : Expected{std::in_place, std::forward<U>(v)} {}
|
||||
|
||||
template <typename U = T, std::enable_if_t<std::is_convertible_v<U&&, T>>* = nullptr,
|
||||
detail::expected_enable_forward_value<T, E, U>* = nullptr>
|
||||
constexpr Expected(U&& v) : Expected{std::in_place, std::forward<U>(v)} {}
|
||||
|
||||
template <typename U = T, typename G = T,
|
||||
std::enable_if_t<std::is_nothrow_constructible_v<T, U&&>>* = nullptr,
|
||||
std::enable_if_t<(
|
||||
!std::is_same_v<Expected<T, E>, std::remove_cvref_t<U>> &&
|
||||
!std::conjunction_v<std::is_scalar<T>, std::is_same<T, std::remove_cvref_t<U>>> &&
|
||||
std::is_constructible_v<T, U> && std::is_assignable_v<G&, U> &&
|
||||
std::is_nothrow_move_constructible_v<E>)>* = nullptr>
|
||||
Expected& operator=(U&& v) {
|
||||
if (has_value()) {
|
||||
val() = std::forward<U>(v);
|
||||
} else {
|
||||
err().~Unexpected<E>();
|
||||
new (valptr()) T{std::forward<U>(v)};
|
||||
this->m_has_val = true;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename U = T, typename G = T,
|
||||
std::enable_if_t<!std::is_nothrow_constructible_v<T, U&&>>* = nullptr,
|
||||
std::enable_if_t<(
|
||||
!std::is_same_v<Expected<T, E>, std::remove_cvref_t<U>> &&
|
||||
!std::conjunction_v<std::is_scalar<T>, std::is_same<T, std::remove_cvref_t<U>>> &&
|
||||
std::is_constructible_v<T, U> && std::is_assignable_v<G&, U> &&
|
||||
std::is_nothrow_move_constructible_v<E>)>* = nullptr>
|
||||
Expected& operator=(U&& v) {
|
||||
if (has_value()) {
|
||||
val() = std::forward<U>(v);
|
||||
} else {
|
||||
auto tmp = std::move(err());
|
||||
err().~Unexpected<E>();
|
||||
new (valptr()) T{std::forward<U>(v)};
|
||||
this->m_has_val = true;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename G = E, std::enable_if_t<std::is_nothrow_copy_constructible_v<G> &&
|
||||
std::is_assignable_v<G&, G>>* = nullptr>
|
||||
Expected& operator=(const Unexpected<G>& rhs) {
|
||||
if (!has_value()) {
|
||||
err() = rhs;
|
||||
} else {
|
||||
this->destroy_val();
|
||||
new (errptr()) Unexpected<E>{rhs};
|
||||
this->m_has_val = false;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename G = E, std::enable_if_t<std::is_nothrow_move_constructible_v<G> &&
|
||||
std::is_move_assignable_v<G>>* = nullptr>
|
||||
Expected& operator=(Unexpected<G>&& rhs) noexcept {
|
||||
if (!has_value()) {
|
||||
err() = std::move(rhs);
|
||||
} else {
|
||||
this->destroy_val();
|
||||
new (errptr()) Unexpected<E>{std::move(rhs)};
|
||||
this->m_has_val = false;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename... Args,
|
||||
std::enable_if_t<std::is_nothrow_constructible_v<T, Args&&...>>* = nullptr>
|
||||
void emplace(Args&&... args) {
|
||||
if (has_value()) {
|
||||
val() = T{std::forward<Args>(args)...};
|
||||
} else {
|
||||
err().~Unexpected<E>();
|
||||
new (valptr()) T{std::forward<Args>(args)...};
|
||||
this->m_has_val = true;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... Args,
|
||||
std::enable_if_t<!std::is_nothrow_constructible_v<T, Args&&...>>* = nullptr>
|
||||
void emplace(Args&&... args) {
|
||||
if (has_value()) {
|
||||
val() = T{std::forward<Args>(args)...};
|
||||
} else {
|
||||
auto tmp = std::move(err());
|
||||
err().~Unexpected<E>();
|
||||
new (valptr()) T{std::forward<Args>(args)...};
|
||||
this->m_has_val = true;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename U, typename... Args,
|
||||
std::enable_if_t<std::is_nothrow_constructible_v<T, std::initializer_list<U>&,
|
||||
Args&&...>>* = nullptr>
|
||||
void emplace(std::initializer_list<U> il, Args&&... args) {
|
||||
if (has_value()) {
|
||||
T t{il, std::forward<Args>(args)...};
|
||||
val() = std::move(t);
|
||||
} else {
|
||||
err().~Unexpected<E>();
|
||||
new (valptr()) T{il, std::forward<Args>(args)...};
|
||||
this->m_has_val = true;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename U, typename... Args,
|
||||
std::enable_if_t<!std::is_nothrow_constructible_v<T, std::initializer_list<U>&,
|
||||
Args&&...>>* = nullptr>
|
||||
void emplace(std::initializer_list<U> il, Args&&... args) {
|
||||
if (has_value()) {
|
||||
T t{il, std::forward<Args>(args)...};
|
||||
val() = std::move(t);
|
||||
} else {
|
||||
auto tmp = std::move(err());
|
||||
err().~Unexpected<E>();
|
||||
new (valptr()) T{il, std::forward<Args>(args)...};
|
||||
this->m_has_val = true;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr T* operator->() {
|
||||
return valptr();
|
||||
}
|
||||
|
||||
constexpr const T* operator->() const {
|
||||
return valptr();
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
constexpr U& operator*() & {
|
||||
return val();
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
constexpr const U& operator*() const& {
|
||||
return val();
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
constexpr U&& operator*() && {
|
||||
return std::move(val());
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
constexpr const U&& operator*() const&& {
|
||||
return std::move(val());
|
||||
}
|
||||
|
||||
constexpr bool has_value() const noexcept {
|
||||
return this->m_has_val;
|
||||
}
|
||||
|
||||
constexpr explicit operator bool() const noexcept {
|
||||
return this->m_has_val;
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
constexpr U& value() & {
|
||||
return val();
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
constexpr const U& value() const& {
|
||||
return val();
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
constexpr U&& value() && {
|
||||
return std::move(val());
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
constexpr const U&& value() const&& {
|
||||
return std::move(val());
|
||||
}
|
||||
|
||||
constexpr E& error() & {
|
||||
return err().value();
|
||||
}
|
||||
|
||||
constexpr const E& error() const& {
|
||||
return err().value();
|
||||
}
|
||||
|
||||
constexpr E&& error() && {
|
||||
return std::move(err().value());
|
||||
}
|
||||
|
||||
constexpr const E&& error() const&& {
|
||||
return std::move(err().value());
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
constexpr T value_or(U&& v) const& {
|
||||
static_assert(std::is_copy_constructible_v<T> && std::is_convertible_v<U&&, T>,
|
||||
"T must be copy-constructible and convertible from U&&");
|
||||
return bool(*this) ? **this : static_cast<T>(std::forward<U>(v));
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
constexpr T value_or(U&& v) && {
|
||||
static_assert(std::is_move_constructible_v<T> && std::is_convertible_v<U&&, T>,
|
||||
"T must be move-constructible and convertible from U&&");
|
||||
return bool(*this) ? std::move(**this) : static_cast<T>(std::forward<U>(v));
|
||||
}
|
||||
|
||||
private:
|
||||
static_assert(!std::is_reference_v<T>, "T must not be a reference");
|
||||
static_assert(!std::is_same_v<T, std::remove_cv_t<std::in_place_t>>,
|
||||
"T must not be std::in_place_t");
|
||||
static_assert(!std::is_same_v<T, std::remove_cv_t<unexpect_t>>, "T must not be unexpect_t");
|
||||
static_assert(!std::is_same_v<T, std::remove_cv_t<Unexpected<E>>>,
|
||||
"T must not be Unexpected<E>");
|
||||
static_assert(!std::is_reference_v<E>, "E must not be a reference");
|
||||
|
||||
T* valptr() {
|
||||
return std::addressof(this->m_val);
|
||||
}
|
||||
|
||||
const T* valptr() const {
|
||||
return std::addressof(this->m_val);
|
||||
}
|
||||
|
||||
Unexpected<E>* errptr() {
|
||||
return std::addressof(this->m_unexpect);
|
||||
}
|
||||
|
||||
const Unexpected<E>* errptr() const {
|
||||
return std::addressof(this->m_unexpect);
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
constexpr U& val() {
|
||||
return this->m_val;
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
constexpr const U& val() const {
|
||||
return this->m_val;
|
||||
}
|
||||
|
||||
constexpr Unexpected<E>& err() {
|
||||
return this->m_unexpect;
|
||||
}
|
||||
|
||||
constexpr const Unexpected<E>& err() const {
|
||||
return this->m_unexpect;
|
||||
}
|
||||
|
||||
using impl_base = detail::expected_move_assign_base<T, E>;
|
||||
using ctor_base = detail::expected_default_ctor_base<T, E>;
|
||||
};
|
||||
|
||||
template <typename T, typename E, typename U, typename F>
|
||||
constexpr bool operator==(const Expected<T, E>& lhs, const Expected<U, F>& rhs) {
|
||||
return (lhs.has_value() != rhs.has_value())
|
||||
? false
|
||||
: (!lhs.has_value() ? lhs.error() == rhs.error() : *lhs == *rhs);
|
||||
}
|
||||
|
||||
template <typename T, typename E, typename U, typename F>
|
||||
constexpr bool operator!=(const Expected<T, E>& lhs, const Expected<U, F>& rhs) {
|
||||
return !operator==(lhs, rhs);
|
||||
}
|
||||
|
||||
template <typename T, typename E, typename U>
|
||||
constexpr bool operator==(const Expected<T, E>& x, const U& v) {
|
||||
return x.has_value() ? *x == v : false;
|
||||
}
|
||||
|
||||
template <typename T, typename E, typename U>
|
||||
constexpr bool operator==(const U& v, const Expected<T, E>& x) {
|
||||
return x.has_value() ? *x == v : false;
|
||||
}
|
||||
|
||||
template <typename T, typename E, typename U>
|
||||
constexpr bool operator!=(const Expected<T, E>& x, const U& v) {
|
||||
return !operator==(x, v);
|
||||
}
|
||||
|
||||
template <typename T, typename E, typename U>
|
||||
constexpr bool operator!=(const U& v, const Expected<T, E>& x) {
|
||||
return !operator==(v, x);
|
||||
}
|
||||
|
||||
template <typename T, typename E>
|
||||
constexpr bool operator==(const Expected<T, E>& x, const Unexpected<E>& e) {
|
||||
return x.has_value() ? false : x.error() == e.value();
|
||||
}
|
||||
|
||||
template <typename T, typename E>
|
||||
constexpr bool operator==(const Unexpected<E>& e, const Expected<T, E>& x) {
|
||||
return x.has_value() ? false : x.error() == e.value();
|
||||
}
|
||||
|
||||
template <typename T, typename E>
|
||||
constexpr bool operator!=(const Expected<T, E>& x, const Unexpected<E>& e) {
|
||||
return !operator==(x, e);
|
||||
}
|
||||
|
||||
template <typename T, typename E>
|
||||
constexpr bool operator!=(const Unexpected<E>& e, const Expected<T, E>& x) {
|
||||
return !operator==(e, x);
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
+10
-1
@@ -54,7 +54,6 @@ SWITCHABLE(CpuBackend, true);
|
||||
SWITCHABLE(CpuAccuracy, true);
|
||||
SWITCHABLE(FullscreenMode, true);
|
||||
SWITCHABLE(GpuAccuracy, true);
|
||||
SWITCHABLE(GpuLogLevel, true);
|
||||
SWITCHABLE(Language, true);
|
||||
SWITCHABLE(MemoryLayout, true);
|
||||
SWITCHABLE(NvdecEmulation, false);
|
||||
@@ -213,6 +212,16 @@ bool IsNceEnabled() {
|
||||
return is_nce_enabled;
|
||||
}
|
||||
|
||||
static u64 current_program_id = 0;
|
||||
|
||||
void SetCurrentProgramID(u64 program_id) {
|
||||
current_program_id = program_id;
|
||||
}
|
||||
|
||||
u64 GetCurrentProgramID() {
|
||||
return current_program_id;
|
||||
}
|
||||
|
||||
bool IsDockedMode() {
|
||||
return values.use_docked_mode.GetValue() == Settings::ConsoleMode::Docked;
|
||||
}
|
||||
|
||||
+29
-14
@@ -573,27 +573,40 @@ struct Values {
|
||||
false,
|
||||
#endif
|
||||
"rescale_hack", Category::RendererHacks};
|
||||
SwitchableSetting<bool> enable_gpu_buffer_readback{linkage,
|
||||
false,
|
||||
"enable_gpu_buffer_readback",
|
||||
Category::RendererAdvanced,
|
||||
Specialization::Default,
|
||||
true,
|
||||
true};
|
||||
|
||||
SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders",
|
||||
Category::RendererHacks};
|
||||
|
||||
SwitchableSetting<GpuUnswizzleSize> gpu_unswizzle_texture_size{linkage,
|
||||
GpuUnswizzleSize::Large,
|
||||
SwitchableSetting<u32, true> gpu_unswizzle_texture_size{linkage,
|
||||
6,
|
||||
1,
|
||||
9,
|
||||
"gpu_unswizzle_texture_size",
|
||||
Category::RendererHacks,
|
||||
Specialization::Default};
|
||||
Specialization::Scalar};
|
||||
|
||||
SwitchableSetting<GpuUnswizzle> gpu_unswizzle_stream_size{linkage,
|
||||
GpuUnswizzle::Medium,
|
||||
SwitchableSetting<u32, true> gpu_unswizzle_stream_size{linkage,
|
||||
4,
|
||||
1,
|
||||
9,
|
||||
"gpu_unswizzle_stream_size",
|
||||
Category::RendererHacks,
|
||||
Specialization::Default};
|
||||
Specialization::Scalar};
|
||||
|
||||
SwitchableSetting<GpuUnswizzleChunk> gpu_unswizzle_chunk_size{linkage,
|
||||
GpuUnswizzleChunk::Medium,
|
||||
SwitchableSetting<u32, true> gpu_unswizzle_chunk_size{linkage,
|
||||
7,
|
||||
1,
|
||||
9,
|
||||
"gpu_unswizzle_chunk_size",
|
||||
Category::RendererHacks,
|
||||
Specialization::Default};
|
||||
Specialization::Scalar};
|
||||
|
||||
SwitchableSetting<bool> gpu_unswizzle_enabled{linkage, false, "gpu_unswizzle_enabled",
|
||||
Category::RendererHacks};
|
||||
@@ -788,8 +801,8 @@ struct Values {
|
||||
false}; // runtime_modifiable_ — startup-only
|
||||
Setting<bool> dump_exefs{linkage, false, "dump_exefs", Category::Debugging};
|
||||
Setting<bool> dump_nso{linkage, false, "dump_nso", Category::Debugging};
|
||||
Setting<bool> dump_shaders{
|
||||
linkage, false, "dump_shaders", Category::DebuggingGraphics, Specialization::Default,
|
||||
Setting<bool> dump_guest_shaders{
|
||||
linkage, false, "dump_guest_shaders", Category::DebuggingGraphics, Specialization::Default,
|
||||
false};
|
||||
Setting<bool> dump_macros{
|
||||
linkage, false, "dump_macros", Category::DebuggingGraphics, Specialization::Default, false};
|
||||
@@ -813,9 +826,8 @@ struct Values {
|
||||
Setting<bool> disable_web_applet{linkage, true, "disable_web_applet", Category::Debugging};
|
||||
|
||||
// GPU Logging
|
||||
Setting<bool> gpu_logging_enabled{linkage, false, "gpu_logging_enabled", Category::Debugging};
|
||||
SwitchableSetting<GpuLogLevel> gpu_log_level{linkage, GpuLogLevel::Standard, "gpu_log_level",
|
||||
Category::Debugging};
|
||||
Setting<GpuLogLevel> gpu_log_level{linkage, GpuLogLevel::Off, "gpu_log_level",
|
||||
Category::Debugging};
|
||||
Setting<bool> gpu_log_vulkan_calls{linkage, true, "gpu_log_vulkan_calls", Category::Debugging};
|
||||
Setting<bool> gpu_log_shader_dumps{linkage, false, "gpu_log_shader_dumps", Category::Debugging};
|
||||
Setting<bool> gpu_log_memory_tracking{linkage, true, "gpu_log_memory_tracking",
|
||||
@@ -876,6 +888,9 @@ bool IsFastmemEnabled();
|
||||
void SetNceEnabled(bool is_64bit);
|
||||
bool IsNceEnabled();
|
||||
|
||||
void SetCurrentProgramID(u64 program_id);
|
||||
u64 GetCurrentProgramID();
|
||||
|
||||
bool IsOpenGL();
|
||||
|
||||
bool IsDockedMode();
|
||||
|
||||
@@ -152,9 +152,6 @@ ENUM(ConsoleMode, Handheld, Docked);
|
||||
ENUM(AppletMode, HLE, LLE);
|
||||
ENUM(SpirvOptimizeMode, Never, OnLoad, Always);
|
||||
ENUM(GpuOverclock, Normal, Medium, High)
|
||||
ENUM(GpuUnswizzleSize, VerySmall, Small, Normal, Large, VeryLarge)
|
||||
ENUM(GpuUnswizzle, VeryLow, Low, Normal, Medium, High)
|
||||
ENUM(GpuUnswizzleChunk, VeryLow, Low, Normal, Medium, High)
|
||||
ENUM(TemperatureUnits, Celsius, Fahrenheit)
|
||||
ENUM(ExtendedDynamicState, Disabled, EDS1, EDS2, EDS3);
|
||||
ENUM(GpuLogLevel, Off, Errors, Standard, Verbose, All)
|
||||
|
||||
+4
-1
@@ -326,6 +326,9 @@ struct System::Impl {
|
||||
|
||||
LOG_INFO(Core, "Loading {} ({:016X}) ...", name, params.program_id);
|
||||
|
||||
// Expose program id to dump sites and other global readers.
|
||||
Settings::SetCurrentProgramID(params.program_id);
|
||||
|
||||
// Track launch time for frontend launches
|
||||
LaunchTimestampCache::SaveLaunchTimestamp(params.program_id);
|
||||
|
||||
@@ -347,7 +350,7 @@ struct System::Impl {
|
||||
|
||||
// Register with applet manager
|
||||
// All threads are started, begin main process execution, now that we're in the clear
|
||||
applet_manager.CreateAndInsertByFrontendAppletParameters(std::make_unique<Service::Process>(*std::move(process)), params);
|
||||
applet_manager.CreateAndInsertByFrontendAppletParameters(std::move(process), params);
|
||||
|
||||
if (Settings::values.gamecard_inserted) {
|
||||
if (Settings::values.gamecard_current_game) {
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/expected.h"
|
||||
|
||||
// All the constants in this file come from <https://switchbrew.org/wiki/Error_codes>
|
||||
|
||||
|
||||
@@ -729,7 +729,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface(ensure_token_id);
|
||||
rb.PushIpcInterface(ctx, ensure_token_id);
|
||||
}
|
||||
|
||||
void LoadIdTokenCacheDeprecated(HLERequestContext& ctx) {
|
||||
@@ -921,7 +921,7 @@ void Module::Interface::GetProfile(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IProfile>(system, user_id, *profile_manager);
|
||||
rb.PushIpcInterface<IProfile>(ctx, system, user_id, *profile_manager);
|
||||
}
|
||||
|
||||
void Module::Interface::IsUserRegistrationRequestPermitted(HLERequestContext& ctx) {
|
||||
@@ -993,7 +993,7 @@ void Module::Interface::GetBaasAccountManagerForApplication(HLERequestContext& c
|
||||
LOG_DEBUG(Service_ACC, "called");
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IManagerForApplication>(system, profile_manager);
|
||||
rb.PushIpcInterface<IManagerForApplication>(ctx, system, profile_manager);
|
||||
}
|
||||
|
||||
void Module::Interface::IsUserAccountSwitchLocked(HLERequestContext& ctx) {
|
||||
@@ -1089,7 +1089,7 @@ void Module::Interface::GetProfileEditor(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IProfileEditor>(system, user_id, *profile_manager);
|
||||
rb.PushIpcInterface<IProfileEditor>(ctx, system, user_id, *profile_manager);
|
||||
}
|
||||
|
||||
void Module::Interface::GetBaasAccountAdministrator(HLERequestContext &ctx) {
|
||||
@@ -1100,7 +1100,7 @@ void Module::Interface::GetBaasAccountAdministrator(HLERequestContext &ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IAdministrator>(system, uuid);
|
||||
rb.PushIpcInterface<IAdministrator>(ctx, system, uuid);
|
||||
}
|
||||
|
||||
void Module::Interface::ListQualifiedUsers(HLERequestContext& ctx) {
|
||||
@@ -1143,7 +1143,7 @@ void Module::Interface::GetBaasAccountManagerForSystemService(HLERequestContext&
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IManagerForSystemService>(system, uuid);
|
||||
rb.PushIpcInterface<IManagerForSystemService>(ctx, system, uuid);
|
||||
}
|
||||
|
||||
void Module::Interface::StoreSaveDataThumbnailSystem(HLERequestContext& ctx) {
|
||||
|
||||
@@ -35,7 +35,7 @@ void IAsyncContext::GetSystemEvent(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(completion_event->GetReadableEvent());
|
||||
rb.PushCopyObjects(ctx, completion_event->GetReadableEvent());
|
||||
}
|
||||
|
||||
void IAsyncContext::Cancel(HLERequestContext& ctx) {
|
||||
|
||||
@@ -109,6 +109,10 @@ struct Applet {
|
||||
std::list<std::shared_ptr<Applet>> child_applets{};
|
||||
bool is_completed{};
|
||||
|
||||
std::shared_ptr<Applet> reserved_applet{};
|
||||
bool unwind_after_reserved{};
|
||||
bool is_winding{};
|
||||
|
||||
// Self state
|
||||
bool exit_locked{};
|
||||
s32 fatal_section_count{};
|
||||
|
||||
@@ -25,6 +25,13 @@ void AppletStorageChannel::Push(Kernel::KernelCore& kernel, std::shared_ptr<ISto
|
||||
m_event.Signal(kernel);
|
||||
}
|
||||
|
||||
void AppletStorageChannel::Unpop(Kernel::KernelCore& kernel, std::shared_ptr<IStorage> storage) {
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
m_data.emplace_front(std::move(storage));
|
||||
m_event.Signal(kernel);
|
||||
}
|
||||
|
||||
Result AppletStorageChannel::Pop(Kernel::KernelCore& kernel, std::shared_ptr<IStorage>* out_storage) {
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ public:
|
||||
~AppletStorageChannel();
|
||||
|
||||
void Push(Kernel::KernelCore& kernel, std::shared_ptr<IStorage> storage);
|
||||
void Unpop(Kernel::KernelCore& kernel, std::shared_ptr<IStorage> storage);
|
||||
Result Pop(Kernel::KernelCore& kernel, std::shared_ptr<IStorage>* out_storage);
|
||||
Kernel::KReadableEvent* GetEvent();
|
||||
|
||||
|
||||
@@ -267,7 +267,7 @@ void AppletManager::SetWindowSystem(WindowSystem* window_system) {
|
||||
|
||||
if (Settings::values.enable_overlay && m_window_system->GetOverlayDisplayApplet() == nullptr) {
|
||||
if (auto overlay_process = CreateProcess(m_system, static_cast<u64>(AppletProgramId::OverlayDisplay), 0, 0)) {
|
||||
auto overlay_applet = std::make_shared<Applet>(m_system, std::make_unique<Service::Process>(*std::move(overlay_process)), false);
|
||||
auto overlay_applet = std::make_shared<Applet>(m_system, std::move(overlay_process), false);
|
||||
overlay_applet->program_id = static_cast<u64>(AppletProgramId::OverlayDisplay);
|
||||
overlay_applet->applet_id = AppletId::OverlayDisplay;
|
||||
overlay_applet->type = AppletType::OverlayApplet;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "applet_web_browser_types.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/fs/file.h"
|
||||
#include "common/fs/fs.h"
|
||||
@@ -370,16 +371,13 @@ void WebBrowser::ExtractOfflineRomFS() {
|
||||
|
||||
void WebBrowser::WebBrowserExit(WebExitReason exit_reason, std::string last_url) {
|
||||
const bool use_tlv_output =
|
||||
(web_arg_header.shim_kind == ShimKind::Share &&
|
||||
web_applet_version >= WebAppletVersion::Version196608) ||
|
||||
(web_arg_header.shim_kind == ShimKind::Web &&
|
||||
web_applet_version >= WebAppletVersion::Version524288) ||
|
||||
(web_arg_header.shim_kind == ShimKind::Lhub);
|
||||
(web_arg_header.shim_kind == ShimKind::Share && web_applet_version >= WebAppletVersion::Version196608)
|
||||
|| (web_arg_header.shim_kind == ShimKind::Web && web_applet_version >= WebAppletVersion::Version524288)
|
||||
|| (web_arg_header.shim_kind == ShimKind::Lhub);
|
||||
|
||||
// https://switchbrew.org/wiki/Internet_Browser#TLVs
|
||||
if (use_tlv_output) {
|
||||
LOG_DEBUG(Service_AM, "Using TLV output: exit_reason={}, last_url={}, last_url_size={}",
|
||||
exit_reason, last_url, last_url.size());
|
||||
LOG_DEBUG(Service_AM, "Using TLV output: exit_reason={}, last_url={}, last_url_size={}", exit_reason, last_url, last_url.size());
|
||||
|
||||
// storage size for TLVs is 0x2000 bytes (as per switchbrew documentation)
|
||||
constexpr size_t TLV_STORAGE_SIZE = 0x2000;
|
||||
@@ -600,11 +598,14 @@ void WebBrowser::ExecuteShare() {
|
||||
|
||||
void WebBrowser::ExecuteWeb() {
|
||||
LOG_INFO(Service_AM, "Opening external URL at {}", external_url);
|
||||
|
||||
frontend.OpenExternalWebPage(external_url,
|
||||
[this](WebExitReason exit_reason, std::string last_url) {
|
||||
WebBrowserExit(exit_reason, last_url);
|
||||
});
|
||||
frontend.OpenExternalWebPage(external_url, [this](WebExitReason exit_reason, std::string last_url) {
|
||||
// Offline and web applets must be explicitly exited from because they respect exit state
|
||||
// Unlike the other web stuffs
|
||||
if (exit_reason == WebExitReason::ExitRequested || exit_reason == WebExitReason::EndButtonPressed)
|
||||
exit_reason = (web_arg_header.shim_kind == ShimKind::Web || web_arg_header.shim_kind == ShimKind::Offline)
|
||||
? WebExitReason::ExitRequested : WebExitReason::EndButtonPressed;
|
||||
WebBrowserExit(exit_reason, last_url);
|
||||
});
|
||||
}
|
||||
|
||||
void WebBrowser::ExecuteWifi() {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -12,7 +15,10 @@ namespace Service::AM {
|
||||
|
||||
HidRegistration::HidRegistration(Core::System& system, Process& process) : m_process(process) {
|
||||
m_hid_server = system.ServiceManager().GetService<HID::IHidServer>("hid", true);
|
||||
this->RegisterCurrentProcess();
|
||||
}
|
||||
|
||||
void HidRegistration::RegisterCurrentProcess() {
|
||||
if (m_process.IsInitialized()) {
|
||||
m_hid_server->GetResourceManager()->RegisterAppletResourceUserId(m_process.GetProcessId(),
|
||||
true);
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -24,6 +27,7 @@ public:
|
||||
explicit HidRegistration(Core::System& system, Process& process);
|
||||
~HidRegistration();
|
||||
|
||||
void RegisterCurrentProcess();
|
||||
void EnableAppletToGetInput(bool enable);
|
||||
|
||||
private:
|
||||
|
||||
@@ -171,6 +171,18 @@ void LifecycleManager::SignalSystemEventIfNeeded(Kernel::KernelCore& kernel) {
|
||||
}
|
||||
}
|
||||
|
||||
void LifecycleManager::ResetForRelaunch() {
|
||||
m_unordered_messages.clear();
|
||||
|
||||
m_activity_state = ActivityState::BackgroundVisible;
|
||||
m_requested_focus_state = FocusState{};
|
||||
m_acknowledged_focus_state = FocusState{};
|
||||
m_has_focus_state_changed = true;
|
||||
|
||||
m_suspend_mode = SuspendMode::NoOverride;
|
||||
m_forced_suspend = false;
|
||||
}
|
||||
|
||||
bool LifecycleManager::PopMessage(Kernel::KernelCore& kernel, AppletMessage* out_message) {
|
||||
const auto message = this->PopMessageInOrderOfPriority();
|
||||
this->SignalSystemEventIfNeeded(kernel);
|
||||
|
||||
@@ -138,6 +138,8 @@ public:
|
||||
void PushUnorderedMessage(Kernel::KernelCore& kernel, AppletMessage message);
|
||||
bool PopMessage(Kernel::KernelCore& kernel, AppletMessage* out_message);
|
||||
|
||||
void ResetForRelaunch();
|
||||
|
||||
private:
|
||||
FocusState GetFocusStateWhileForegroundObscured() const;
|
||||
FocusState GetFocusStateWhileBackground(bool is_obscured) const;
|
||||
|
||||
@@ -40,23 +40,23 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] inline std::optional<Process> CreateProcessImpl(std::unique_ptr<Loader::AppLoader>& out_loader, Loader::ResultStatus& out_load_result, Core::System& system, FileSys::VirtualFile file, u64 program_id, u64 program_index) {
|
||||
[[nodiscard]] inline std::unique_ptr<Process> CreateProcessImpl(std::unique_ptr<Loader::AppLoader>& out_loader, Loader::ResultStatus& out_load_result, Core::System& system, FileSys::VirtualFile file, u64 program_id, u64 program_index) {
|
||||
// Get the appropriate loader to parse this NCA.
|
||||
out_loader = Loader::GetLoader(system, file, program_id, program_index);
|
||||
// Ensure we have a loader which can parse the NCA.
|
||||
if (out_loader) {
|
||||
// Try to load the process.
|
||||
auto process = std::make_optional<Process>(system);
|
||||
auto process = std::make_unique<Process>(system);
|
||||
if (process->Initialize(*out_loader, out_load_result)) {
|
||||
return process;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
std::optional<Process> CreateProcess(Core::System& system, u64 program_id, u8 minimum_key_generation, u8 maximum_key_generation) {
|
||||
std::unique_ptr<Process> CreateProcess(Core::System& system, u64 program_id, u8 minimum_key_generation, u8 maximum_key_generation) {
|
||||
// Attempt to load program NCA.
|
||||
FileSys::VirtualFile nca_raw{};
|
||||
|
||||
@@ -66,7 +66,7 @@ std::optional<Process> CreateProcess(Core::System& system, u64 program_id, u8 mi
|
||||
|
||||
// Ensure we retrieved a program NCA.
|
||||
if (!nca_raw) {
|
||||
return std::nullopt;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Ensure we have a suitable version.
|
||||
@@ -76,7 +76,7 @@ std::optional<Process> CreateProcess(Core::System& system, u64 program_id, u8 mi
|
||||
(nca.GetKeyGeneration() < minimum_key_generation ||
|
||||
nca.GetKeyGeneration() > maximum_key_generation)) {
|
||||
LOG_WARNING(Service_LDR, "Skipping program {:016X} with generation {}", program_id, nca.GetKeyGeneration());
|
||||
return std::nullopt;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ std::optional<Process> CreateProcess(Core::System& system, u64 program_id, u8 mi
|
||||
return CreateProcessImpl(loader, status, system, nca_raw, program_id, 0);
|
||||
}
|
||||
|
||||
std::optional<Process> CreateApplicationProcess(std::vector<u8>& out_control, std::unique_ptr<Loader::AppLoader>& out_loader, Loader::ResultStatus& out_load_result, Core::System& system, FileSys::VirtualFile file, u64 program_id, u64 program_index) {
|
||||
std::unique_ptr<Process> CreateApplicationProcess(std::vector<u8>& out_control, std::unique_ptr<Loader::AppLoader>& out_loader, Loader::ResultStatus& out_load_result, Core::System& system, FileSys::VirtualFile file, u64 program_id, u64 program_index) {
|
||||
if (auto process = CreateProcessImpl(out_loader, out_load_result, system, file, program_id, program_index); process) {
|
||||
FileSys::NACP nacp;
|
||||
if (out_loader->ReadControlData(nacp) == Loader::ResultStatus::Success) {
|
||||
@@ -110,7 +110,23 @@ std::optional<Process> CreateApplicationProcess(std::vector<u8>& out_control, st
|
||||
system.GetARPManager().Register(launch.title_id, launch, out_control);
|
||||
return process;
|
||||
}
|
||||
return std::nullopt;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool ReinitializeProcess(Core::System& system, Process& process, u64 program_id) {
|
||||
auto& storage = system.GetContentProviderUnion();
|
||||
const auto nca_raw = storage.GetEntryRaw(program_id, FileSys::ContentRecordType::Program);
|
||||
if (!nca_raw) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto loader = Loader::GetLoader(system, nca_raw, program_id, 0);
|
||||
if (!loader) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Loader::ResultStatus status{};
|
||||
return process.Initialize(*loader, status);
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -27,7 +27,9 @@ class Process;
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
std::optional<Process> CreateProcess(Core::System& system, u64 program_id, u8 minimum_key_generation, u8 maximum_key_generation);
|
||||
std::optional<Process> CreateApplicationProcess(std::vector<u8>& out_control, std::unique_ptr<Loader::AppLoader>& out_loader, Loader::ResultStatus& out_load_result, Core::System& system, FileSys::VirtualFile file, u64 program_id, u64 program_index);
|
||||
std::unique_ptr<Process> CreateProcess(Core::System& system, u64 program_id, u8 minimum_key_generation, u8 maximum_key_generation);
|
||||
std::unique_ptr<Process> CreateApplicationProcess(std::vector<u8>& out_control, std::unique_ptr<Loader::AppLoader>& out_loader, Loader::ResultStatus& out_load_result, Core::System& system, FileSys::VirtualFile file, u64 program_id, u64 program_index);
|
||||
|
||||
bool ReinitializeProcess(Core::System& system, Process& process, u64 program_id);
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -35,9 +35,9 @@ Result CreateGuestApplication(SharedPointer<IApplicationAccessor>* out_applicati
|
||||
std::unique_ptr<Loader::AppLoader> loader;
|
||||
Loader::ResultStatus result;
|
||||
auto process = CreateApplicationProcess(control, loader, result, system, nca_raw, program_id, 0);
|
||||
R_UNLESS(process != std::nullopt, ResultUnknown);
|
||||
R_UNLESS(process != nullptr, ResultUnknown);
|
||||
|
||||
const auto applet = std::make_shared<Applet>(system, std::make_unique<Service::Process>(*std::move(process)), true);
|
||||
const auto applet = std::make_shared<Applet>(system, std::move(process), true);
|
||||
applet->program_id = program_id;
|
||||
applet->applet_id = AppletId::Application;
|
||||
applet->type = AppletType::Application;
|
||||
@@ -88,9 +88,9 @@ Result IApplicationCreator::CreateSystemApplication(
|
||||
std::vector<u8> control;
|
||||
std::unique_ptr<Loader::AppLoader> loader;
|
||||
auto process = CreateProcess(system, application_id, 1, 22);
|
||||
R_UNLESS(process != std::nullopt, ResultUnknown);
|
||||
R_UNLESS(process != nullptr, ResultUnknown);
|
||||
|
||||
const auto applet = std::make_shared<Applet>(system, std::make_unique<Service::Process>(*std::move(process)), true);
|
||||
const auto applet = std::make_shared<Applet>(system, std::move(process), true);
|
||||
applet->program_id = application_id;
|
||||
applet->applet_id = AppletId::Starter;
|
||||
applet->type = AppletType::LibraryApplet;
|
||||
|
||||
@@ -75,7 +75,7 @@ ILibraryAppletAccessor::ILibraryAppletAccessor(Core::System& system_,
|
||||
{105, D<&ILibraryAppletAccessor::GetPopOutDataEvent>, "GetPopOutDataEvent"},
|
||||
{106, D<&ILibraryAppletAccessor::GetPopInteractiveOutDataEvent>, "GetPopInteractiveOutDataEvent"},
|
||||
{110, nullptr, "NeedsToExitProcess"},
|
||||
{120, nullptr, "GetLibraryAppletInfo"},
|
||||
{120, D<&ILibraryAppletAccessor::GetLibraryAppletInfo>, "GetLibraryAppletInfo"},
|
||||
{150, nullptr, "RequestForAppletToGetForeground"},
|
||||
{160, D<&ILibraryAppletAccessor::GetIndirectLayerConsumerHandle>, "GetIndirectLayerConsumerHandle"}, //2.0.0+
|
||||
{170, D<&ILibraryAppletAccessor::Unknown170>, "Unknown170"}, //22.0.0+
|
||||
@@ -218,6 +218,16 @@ Result ILibraryAppletAccessor::GetIndirectLayerConsumerHandle(Out<u64> out_handl
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ILibraryAppletAccessor::GetLibraryAppletInfo(
|
||||
Out<LibraryAppletInfo> out_library_applet_info) {
|
||||
LOG_INFO(Service_AM, "called");
|
||||
*out_library_applet_info = {
|
||||
.applet_id = m_applet->applet_id,
|
||||
.library_applet_mode = m_applet->library_applet_mode,
|
||||
};
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ILibraryAppletAccessor::Unknown170(OutCopyHandle<Kernel::KReadableEvent> out_event) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
*out_event = m_applet->unknown_event.GetHandle();
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/am/service/library_applet_self_accessor.h"
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
@@ -21,6 +22,10 @@ public:
|
||||
std::shared_ptr<Applet> applet);
|
||||
~ILibraryAppletAccessor();
|
||||
|
||||
std::shared_ptr<Applet> GetApplet() const {
|
||||
return m_applet;
|
||||
}
|
||||
|
||||
private:
|
||||
Result GetAppletStateChangedEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||
Result IsCompleted(Out<bool> out_is_completed);
|
||||
@@ -37,6 +42,7 @@ private:
|
||||
Result GetPopOutDataEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||
Result GetPopInteractiveOutDataEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||
Result GetIndirectLayerConsumerHandle(Out<u64> out_handle);
|
||||
Result GetLibraryAppletInfo(Out<LibraryAppletInfo> out_library_applet_info);
|
||||
Result Unknown170(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||
|
||||
void FrontendExecute();
|
||||
|
||||
@@ -123,7 +123,7 @@ std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet(Core::System& system,
|
||||
|
||||
auto process = CreateProcess(system, program_id, Firmware1400, Firmware2200);
|
||||
if (process) {
|
||||
const auto applet = std::make_shared<Applet>(system, std::make_unique<Service::Process>(*std::move(process)), false);
|
||||
const auto applet = std::make_shared<Applet>(system, std::move(process), false);
|
||||
applet->program_id = program_id;
|
||||
applet->applet_id = applet_id;
|
||||
applet->type = AppletType::LibraryApplet;
|
||||
|
||||
@@ -233,8 +233,9 @@ Result ILibraryAppletSelfAccessor::ReportVisibleErrorWithErrorContext(
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ILibraryAppletSelfAccessor::UnpopInData() {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
Result ILibraryAppletSelfAccessor::UnpopInData(SharedPointer<IStorage> storage) {
|
||||
LOG_INFO(Service_AM, "called");
|
||||
m_broker->GetInData().Unpop(system.Kernel(), storage);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ private:
|
||||
Result ReportVisibleError(ErrorCode error_code);
|
||||
Result ReportVisibleErrorWithErrorContext(
|
||||
ErrorCode error_code, InLargeData<ErrorContext, BufferAttr_HipcMapAlias> error_context);
|
||||
Result UnpopInData();
|
||||
Result UnpopInData(SharedPointer<IStorage> storage);
|
||||
Result GetMainAppletApplicationDesiredLanguage(Out<u64> out_desired_language);
|
||||
Result GetCurrentApplicationId(Out<u64> out_application_id);
|
||||
Result GetMainAppletAvailableUsers(Out<bool> out_can_select_any_user, Out<s32> out_users_count,
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/am/applet.h"
|
||||
#include "core/hle/service/am/frontend/applets.h"
|
||||
#include "core/hle/service/am/service/library_applet_accessor.h"
|
||||
#include "core/hle/service/am/service/process_winding_controller.h"
|
||||
@@ -43,6 +45,18 @@ Result IProcessWindingController::OpenCallingLibraryApplet(
|
||||
Out<SharedPointer<ILibraryAppletAccessor>> out_calling_library_applet) {
|
||||
LOG_INFO(Service_AM, "called");
|
||||
|
||||
std::shared_ptr<Applet> reserved_applet;
|
||||
{
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
reserved_applet = std::move(m_applet->reserved_applet);
|
||||
}
|
||||
|
||||
if (reserved_applet != nullptr) {
|
||||
*out_calling_library_applet = std::make_shared<ILibraryAppletAccessor>(
|
||||
system, reserved_applet->caller_applet_broker, reserved_applet);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
const auto caller_applet = m_applet->caller_applet.lock();
|
||||
if (caller_applet == nullptr) {
|
||||
LOG_ERROR(Service_AM, "No caller applet available");
|
||||
@@ -74,22 +88,82 @@ Result IProcessWindingController::PopContext(Out<SharedPointer<IStorage>> out_co
|
||||
}
|
||||
|
||||
Result IProcessWindingController::CancelWindingReservation() {
|
||||
LOG_WARNING(Service_AM, "STUBBED");
|
||||
LOG_INFO(Service_AM, "called");
|
||||
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
m_applet->reserved_applet.reset();
|
||||
m_applet->unwind_after_reserved = false;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IProcessWindingController::WindAndDoReserved() {
|
||||
LOG_WARNING(Service_AM, "STUBBED");
|
||||
LOG_INFO(Service_AM, "called");
|
||||
|
||||
std::shared_ptr<Applet> reserved_applet;
|
||||
{
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
|
||||
reserved_applet = m_applet->reserved_applet;
|
||||
m_applet->display_layer_manager.SetWindowVisibility(false);
|
||||
m_applet->exit_locked = false;
|
||||
system.SetExitLocked(false);
|
||||
}
|
||||
|
||||
if (reserved_applet) {
|
||||
{
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
m_applet->is_winding = true;
|
||||
}
|
||||
|
||||
{
|
||||
std::scoped_lock lk{reserved_applet->lock};
|
||||
reserved_applet->window_visible = true;
|
||||
reserved_applet->process->Run();
|
||||
}
|
||||
|
||||
if (reserved_applet->frontend) {
|
||||
reserved_applet->frontend->Initialize();
|
||||
reserved_applet->frontend->Execute();
|
||||
}
|
||||
} else {
|
||||
LOG_WARNING(Service_AM, "called without a reserved applet to start");
|
||||
}
|
||||
|
||||
m_applet->process->Terminate();
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IProcessWindingController::ReserveToStartAndWaitAndUnwindThis() {
|
||||
LOG_WARNING(Service_AM, "STUBBED");
|
||||
Result IProcessWindingController::ReserveToStartAndWaitAndUnwindThis(
|
||||
SharedPointer<ILibraryAppletAccessor> reserved_applet_accessor) {
|
||||
LOG_INFO(Service_AM, "called");
|
||||
|
||||
if (reserved_applet_accessor == nullptr) {
|
||||
LOG_ERROR(Service_AM, "No applet accessor provided");
|
||||
R_THROW(ResultUnknown);
|
||||
}
|
||||
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
m_applet->reserved_applet = reserved_applet_accessor->GetApplet();
|
||||
m_applet->unwind_after_reserved = true;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IProcessWindingController::ReserveToStartAndWait() {
|
||||
LOG_WARNING(Service_AM, "STUBBED");
|
||||
Result IProcessWindingController::ReserveToStartAndWait(
|
||||
SharedPointer<ILibraryAppletAccessor> reserved_applet_accessor) {
|
||||
LOG_INFO(Service_AM, "called");
|
||||
|
||||
if (reserved_applet_accessor == nullptr) {
|
||||
LOG_ERROR(Service_AM, "No applet accessor provided");
|
||||
R_THROW(ResultUnknown);
|
||||
}
|
||||
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
m_applet->reserved_applet = reserved_applet_accessor->GetApplet();
|
||||
m_applet->unwind_after_reserved = false;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
@@ -29,8 +29,9 @@ private:
|
||||
Result PopContext(Out<SharedPointer<IStorage>> out_context);
|
||||
Result CancelWindingReservation();
|
||||
Result WindAndDoReserved();
|
||||
Result ReserveToStartAndWaitAndUnwindThis();
|
||||
Result ReserveToStartAndWait();
|
||||
Result ReserveToStartAndWaitAndUnwindThis(
|
||||
SharedPointer<ILibraryAppletAccessor> reserved_applet_accessor);
|
||||
Result ReserveToStartAndWait(SharedPointer<ILibraryAppletAccessor> reserved_applet_accessor);
|
||||
|
||||
const std::shared_ptr<Applet> m_applet;
|
||||
};
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "core/hle/service/am/applet.h"
|
||||
#include "core/hle/service/am/applet_manager.h"
|
||||
#include "core/hle/service/am/event_observer.h"
|
||||
#include "core/hle/service/am/process_creation.h"
|
||||
#include "core/hle/service/am/window_system.h"
|
||||
|
||||
namespace Service::AM {
|
||||
@@ -240,6 +241,37 @@ void WindowSystem::PruneTerminatedAppletsLocked() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// A winding applet has had its own process killed but is kept alive as a transparent slot
|
||||
// while the reserved applet running in its place finishes (see WindAndDoReserved()).
|
||||
if (applet->is_winding) {
|
||||
if (!applet->child_applets.empty()) {
|
||||
it = std::next(it);
|
||||
continue;
|
||||
}
|
||||
|
||||
const bool unwind = applet->unwind_after_reserved;
|
||||
applet->is_winding = false;
|
||||
applet->unwind_after_reserved = false;
|
||||
|
||||
if (unwind && this->RestartAppletProcessLocked(applet.get())) {
|
||||
const u64 new_aruid = applet->aruid.pid;
|
||||
const auto next = std::next(it);
|
||||
|
||||
if (new_aruid != aruid) {
|
||||
auto node = m_applets.extract(it);
|
||||
node.key() = new_aruid;
|
||||
m_applets.insert(std::move(node));
|
||||
}
|
||||
|
||||
applet->process->Run();
|
||||
m_event_observer->RequestUpdate();
|
||||
it = next;
|
||||
continue;
|
||||
}
|
||||
|
||||
applet->reserved_applet.reset();
|
||||
}
|
||||
|
||||
// Terminated, so ensure all child applets are terminated.
|
||||
if (!applet->child_applets.empty()) {
|
||||
this->TerminateChildAppletsLocked(applet.get());
|
||||
@@ -301,6 +333,28 @@ void WindowSystem::PruneTerminatedAppletsLocked() {
|
||||
}
|
||||
}
|
||||
|
||||
bool WindowSystem::RestartAppletProcessLocked(Applet* applet) {
|
||||
if (!ReinitializeProcess(m_system, *applet->process, applet->program_id)) {
|
||||
LOG_ERROR(Service_AM, "Failed to restart winding applet_id={}",
|
||||
static_cast<u32>(applet->applet_id));
|
||||
return false;
|
||||
}
|
||||
|
||||
applet->aruid.pid = applet->process->GetProcessId();
|
||||
applet->is_process_running = false;
|
||||
applet->is_completed = false;
|
||||
|
||||
applet->hid_registration.RegisterCurrentProcess();
|
||||
|
||||
applet->lifecycle_manager.ResetForRelaunch();
|
||||
applet->is_activity_runnable = false;
|
||||
|
||||
applet->launch_reason.flag = 1;
|
||||
|
||||
m_event_observer->TrackAppletProcess(*applet);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WindowSystem::LockHomeMenuIntoForegroundLocked() {
|
||||
// If the home menu is not locked into foreground, then there's nothing to do.
|
||||
if (m_home_menu == nullptr || !m_home_menu_foreground_locked) {
|
||||
@@ -352,6 +406,9 @@ void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground, b
|
||||
const bool has_obscuring_child_applets = [&] {
|
||||
for (const auto& child_applet : applet->child_applets) {
|
||||
std::scoped_lock lk2{child_applet->lock};
|
||||
if (child_applet->is_winding) {
|
||||
return true;
|
||||
}
|
||||
const auto mode = child_applet->library_applet_mode;
|
||||
if (child_applet->is_process_running && child_applet->window_visible &&
|
||||
(mode == LibraryAppletMode::AllForeground ||
|
||||
|
||||
@@ -61,6 +61,7 @@ public:
|
||||
|
||||
private:
|
||||
void PruneTerminatedAppletsLocked();
|
||||
bool RestartAppletProcessLocked(Applet* applet);
|
||||
bool LockHomeMenuIntoForegroundLocked();
|
||||
void TerminateChildAppletsLocked(Applet* applet);
|
||||
void UpdateAppletStateLocked(Applet* applet, bool is_foreground, bool overlay_blocking = false);
|
||||
|
||||
@@ -82,7 +82,7 @@ void APM::OpenSession(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<ISession>(system, controller);
|
||||
rb.PushIpcInterface<ISession>(ctx, system, controller);
|
||||
}
|
||||
|
||||
void APM::GetPerformanceMode(HLERequestContext& ctx) {
|
||||
@@ -125,7 +125,7 @@ void APM_Sys::GetPerformanceEvent(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<ISession>(system, controller);
|
||||
rb.PushIpcInterface<ISession>(ctx, system, controller);
|
||||
}
|
||||
|
||||
void APM_Sys::SetCpuBoostMode(HLERequestContext& ctx) {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -45,7 +48,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IRequest>(system);
|
||||
rb.PushIpcInterface<IRequest>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -184,7 +184,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(readable_event.Signal(system.Kernel()));
|
||||
rb.PushCopyObjects(readable_event);
|
||||
rb.PushCopyObjects(ctx, readable_event);
|
||||
}
|
||||
|
||||
void Cancel(HLERequestContext& ctx) {
|
||||
@@ -400,7 +400,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(notification_event->GetReadableEvent());
|
||||
rb.PushCopyObjects(ctx, notification_event->GetReadableEvent());
|
||||
}
|
||||
|
||||
void Clear(HLERequestContext& ctx) {
|
||||
@@ -476,7 +476,7 @@ private:
|
||||
void Module::Interface::CreateFriendService(HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IFriendService>(system);
|
||||
rb.PushIpcInterface<IFriendService>(ctx, system);
|
||||
LOG_DEBUG(Service_Friend, "called");
|
||||
}
|
||||
|
||||
@@ -488,12 +488,12 @@ void Module::Interface::CreateNotificationService(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<INotificationService>(system, uuid);
|
||||
rb.PushIpcInterface<INotificationService>(ctx, system, uuid);
|
||||
}
|
||||
|
||||
Module::Interface::Interface(std::shared_ptr<Module> module_, Core::System& system_,
|
||||
const char* name)
|
||||
: ServiceFramework{system_, name}, module{std::move(module_)} {}
|
||||
Module::Interface::Interface(std::shared_ptr<Module> module_, Core::System& system_, const char* name)
|
||||
: ServiceFramework{system_, name}, module{std::move(module_)}
|
||||
{}
|
||||
|
||||
Module::Interface::~Interface() = default;
|
||||
|
||||
|
||||
@@ -279,7 +279,7 @@ void ARP_W::AcquireRegistrar(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface(registrar);
|
||||
rb.PushIpcInterface(ctx, registrar);
|
||||
}
|
||||
|
||||
void ARP_W::UnregisterApplicationInstance(HLERequestContext& ctx) {
|
||||
|
||||
@@ -28,7 +28,7 @@ void BGTC_T::OpenTaskService(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<ITaskService>(system);
|
||||
rb.PushIpcInterface<ITaskService>(ctx, system);
|
||||
}
|
||||
|
||||
ITaskService::ITaskService(Core::System& system_) : ServiceFramework{system_, "ITaskService"} {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -56,7 +59,7 @@ ECTX_AW::~ECTX_AW() = default;
|
||||
void ECTX_AW::CreateContextRegistrar(HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IContextRegistrar>(std::make_shared<IContextRegistrar>(system));
|
||||
rb.PushIpcInterface<IContextRegistrar>(ctx, system);
|
||||
}
|
||||
|
||||
} // namespace Service::Glue
|
||||
|
||||
@@ -726,7 +726,7 @@ void IHidSystemServer::AcquireConnectionTriggerTimeoutEvent(HLERequestContext& c
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(acquire_device_registered_event->GetReadableEvent());
|
||||
rb.PushCopyObjects(ctx, acquire_device_registered_event->GetReadableEvent());
|
||||
}
|
||||
|
||||
void IHidSystemServer::AcquireDeviceRegisteredEventForControllerSupport(HLERequestContext& ctx) {
|
||||
@@ -734,7 +734,7 @@ void IHidSystemServer::AcquireDeviceRegisteredEventForControllerSupport(HLEReque
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(acquire_device_registered_event->GetReadableEvent());
|
||||
rb.PushCopyObjects(ctx, acquire_device_registered_event->GetReadableEvent());
|
||||
}
|
||||
|
||||
void IHidSystemServer::GetRegisteredDevices(HLERequestContext& ctx) {
|
||||
@@ -759,7 +759,7 @@ void IHidSystemServer::AcquireUniquePadConnectionEventHandle(HLERequestContext&
|
||||
LOG_WARNING(Service_HID, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.PushCopyObjects(unique_pad_connection_event->GetReadableEvent());
|
||||
rb.PushCopyObjects(ctx, unique_pad_connection_event->GetReadableEvent());
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
@@ -776,7 +776,7 @@ void IHidSystemServer::AcquireJoyDetachOnBluetoothOffEventHandle(HLERequestConte
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(joy_detach_event->GetReadableEvent());
|
||||
rb.PushCopyObjects(ctx, joy_detach_event->GetReadableEvent());
|
||||
}
|
||||
|
||||
void IHidSystemServer::IsUsbFullKeyControllerEnabled(HLERequestContext& ctx) {
|
||||
|
||||
@@ -31,7 +31,7 @@ class Memory;
|
||||
}
|
||||
|
||||
namespace IPC {
|
||||
class ResponseBuilder;
|
||||
struct ResponseBuilder;
|
||||
}
|
||||
|
||||
namespace Service {
|
||||
@@ -392,7 +392,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
friend class IPC::ResponseBuilder;
|
||||
friend struct IPC::ResponseBuilder;
|
||||
|
||||
void ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming);
|
||||
|
||||
|
||||
@@ -24,45 +24,7 @@ namespace IPC {
|
||||
|
||||
constexpr Result ResultSessionClosed{ErrorModule::HIPC, 301};
|
||||
|
||||
class RequestHelperBase {
|
||||
protected:
|
||||
Service::HLERequestContext* context = nullptr;
|
||||
u32* cmdbuf;
|
||||
u32 index = 0;
|
||||
|
||||
public:
|
||||
explicit RequestHelperBase(u32* command_buffer) : cmdbuf(command_buffer) {}
|
||||
|
||||
explicit RequestHelperBase(Service::HLERequestContext& ctx)
|
||||
: context(&ctx), cmdbuf(ctx.CommandBuffer()) {}
|
||||
|
||||
void Skip(u32 size_in_words, bool set_to_null) {
|
||||
if (set_to_null) {
|
||||
memset(cmdbuf + index, 0, size_in_words * sizeof(u32));
|
||||
}
|
||||
index += size_in_words;
|
||||
}
|
||||
|
||||
/**
|
||||
* Aligns the current position forward to a 16-byte boundary, padding with zeros.
|
||||
*/
|
||||
void AlignWithPadding() {
|
||||
if (index & 3) {
|
||||
Skip(static_cast<u32>(4 - (index & 3)), true);
|
||||
}
|
||||
}
|
||||
|
||||
u32 GetCurrentOffset() const {
|
||||
return index;
|
||||
}
|
||||
|
||||
void SetCurrentOffset(u32 offset) {
|
||||
index = offset;
|
||||
}
|
||||
};
|
||||
|
||||
class ResponseBuilder : public RequestHelperBase {
|
||||
public:
|
||||
struct ResponseBuilder {
|
||||
/// Flags used for customizing the behavior of ResponseBuilder
|
||||
enum class Flags : u32 {
|
||||
None = 0,
|
||||
@@ -71,14 +33,13 @@ public:
|
||||
AlwaysMoveHandles = 1,
|
||||
};
|
||||
|
||||
explicit ResponseBuilder(Service::HLERequestContext& ctx, u32 normal_params_size_,
|
||||
u32 num_handles_to_copy_ = 0, u32 num_objects_to_move_ = 0,
|
||||
Flags flags = Flags::None)
|
||||
: RequestHelperBase(ctx), normal_params_size(normal_params_size_),
|
||||
num_handles_to_copy(num_handles_to_copy_),
|
||||
num_objects_to_move(num_objects_to_move_), kernel{ctx.kernel} {
|
||||
|
||||
memset(cmdbuf, 0, sizeof(u32) * IPC::COMMAND_BUFFER_LENGTH);
|
||||
inline explicit ResponseBuilder(Service::HLERequestContext& ctx, u32 normal_params_size_, u32 num_handles_to_copy_ = 0, u32 num_objects_to_move_ = 0, Flags flags = Flags::None)
|
||||
: cmdbuf(ctx.CommandBuffer())
|
||||
, normal_params_size(normal_params_size_)
|
||||
, num_handles_to_copy(num_handles_to_copy_)
|
||||
, num_objects_to_move(num_objects_to_move_)
|
||||
{
|
||||
std::memset(cmdbuf, 0, sizeof(u32) * IPC::COMMAND_BUFFER_LENGTH);
|
||||
|
||||
IPC::CommandHeader header{};
|
||||
auto const mgr = ctx.GetManager().get();
|
||||
@@ -117,9 +78,7 @@ public:
|
||||
handle_descriptor_header.num_handles_to_copy.Assign(num_handles_to_copy_);
|
||||
handle_descriptor_header.num_handles_to_move.Assign(num_handles_to_move);
|
||||
PushRaw(handle_descriptor_header);
|
||||
|
||||
ctx.handles_offset = index;
|
||||
|
||||
Skip(num_handles_to_copy + num_handles_to_move, true);
|
||||
}
|
||||
|
||||
@@ -131,7 +90,6 @@ public:
|
||||
domain_header.num_objects = num_domain_objects;
|
||||
PushRaw(domain_header);
|
||||
}
|
||||
|
||||
IPC::DataPayloadHeader data_payload_header{};
|
||||
data_payload_header.magic = Common::MakeMagic('S', 'F', 'C', 'O');
|
||||
PushRaw(data_payload_header);
|
||||
@@ -141,34 +99,39 @@ public:
|
||||
|
||||
ctx.data_payload_offset = index;
|
||||
ctx.write_size += index;
|
||||
ctx.domain_offset = static_cast<u32>(index + raw_data_size / sizeof(u32));
|
||||
ctx.domain_offset = u32(index + raw_data_size / sizeof(u32));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void PushIpcInterface(std::shared_ptr<T> iface) {
|
||||
auto manager{context->GetManager()};
|
||||
inline void Skip(u32 size_in_words, bool set_to_null) {
|
||||
if (set_to_null) std::memset(cmdbuf + index, 0, size_in_words * sizeof(u32));
|
||||
index += size_in_words;
|
||||
}
|
||||
/// @brief Aligns the current position forward to a 16-byte boundary, padding with zeros.
|
||||
inline void AlignWithPadding() { if (index & 3) Skip(u32(4 - (index & 3)), true); }
|
||||
inline u32 GetCurrentOffset() const { return index; }
|
||||
inline void SetCurrentOffset(u32 offset) { index = offset; }
|
||||
|
||||
template <class T> inline void PushIpcInterface(Service::HLERequestContext& ctx, std::shared_ptr<T> iface) {
|
||||
auto manager = ctx.GetManager();
|
||||
if (manager->IsDomain()) {
|
||||
context->AddDomainObject(std::move(iface));
|
||||
ctx.AddDomainObject(std::move(iface));
|
||||
} else {
|
||||
ASSERT(Kernel::GetCurrentProcess(kernel).GetResourceLimit()->Reserve(kernel, Kernel::LimitableResource::SessionCountMax, 1));
|
||||
ASSERT(Kernel::GetCurrentProcess(ctx.kernel).GetResourceLimit()->Reserve(ctx.kernel, Kernel::LimitableResource::SessionCountMax, 1));
|
||||
|
||||
auto* session = Kernel::KSession::Create(kernel);
|
||||
session->Initialize(kernel, nullptr, 0);
|
||||
Kernel::KSession::Register(kernel, session);
|
||||
auto* session = Kernel::KSession::Create(ctx.kernel);
|
||||
session->Initialize(ctx.kernel, nullptr, 0);
|
||||
Kernel::KSession::Register(ctx.kernel, session);
|
||||
|
||||
auto next_manager = std::make_shared<Service::SessionRequestManager>(
|
||||
kernel, manager->GetServerManager());
|
||||
auto next_manager = std::make_shared<Service::SessionRequestManager>(ctx.kernel, manager->GetServerManager());
|
||||
next_manager->SetSessionHandler(iface);
|
||||
manager->GetServerManager().RegisterSession(&session->GetServerSession(), next_manager);
|
||||
|
||||
context->AddMoveObject(&session->GetClientSession());
|
||||
ctx.AddMoveObject(&session->GetClientSession());
|
||||
}
|
||||
}
|
||||
|
||||
template <class T, class... Args>
|
||||
void PushIpcInterface(Args&&... args) {
|
||||
PushIpcInterface<T>(std::make_shared<T>(std::forward<Args>(args)...));
|
||||
template <class T, class... Args> inline void PushIpcInterface(Service::HLERequestContext& ctx, Args&&... args) {
|
||||
PushIpcInterface<T>(ctx, std::make_shared<T>(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
void PushImpl(s8 value);
|
||||
@@ -184,59 +147,38 @@ public:
|
||||
void PushImpl(bool value);
|
||||
void PushImpl(Result value);
|
||||
|
||||
template <typename T>
|
||||
void Push(T value) {
|
||||
template <typename T> inline void Push(T value) {
|
||||
return PushImpl(value);
|
||||
}
|
||||
|
||||
template <typename First, typename... Other>
|
||||
void Push(const First& first_value, const Other&... other_values);
|
||||
|
||||
/**
|
||||
* Helper function for pushing strongly-typed enumeration values.
|
||||
*
|
||||
* @tparam Enum The enumeration type to be pushed
|
||||
*
|
||||
* @param value The value to push.
|
||||
*
|
||||
* @note The underlying size of the enumeration type is the size of the
|
||||
* data that gets pushed. e.g. "enum class SomeEnum : u16" will
|
||||
* push a u16-sized amount of data.
|
||||
*/
|
||||
template <typename Enum>
|
||||
void PushEnum(Enum value) {
|
||||
/// @brief Helper function for pushing strongly-typed enumeration values.
|
||||
/// @tparam Enum The enumeration type to be pushed
|
||||
/// @param value The value to push.
|
||||
/// @note The underlying size of the enumeration type is the size of the data that gets pushed.
|
||||
/// e.g. "enum class SomeEnum : u16" will push a u16-sized amount of data.
|
||||
template <typename Enum> inline void PushEnum(Enum value) {
|
||||
static_assert(std::is_enum_v<Enum>, "T must be an enum type within a PushEnum call.");
|
||||
static_assert(!std::is_convertible_v<Enum, int>,
|
||||
"enum type in PushEnum must be a strongly typed enum.");
|
||||
static_assert(!std::is_convertible_v<Enum, int>, "enum type in PushEnum must be a strongly typed enum.");
|
||||
Push(static_cast<std::underlying_type_t<Enum>>(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copies the content of the given trivially copyable class to the buffer as a normal
|
||||
* param
|
||||
* @note: The input class must be correctly packed/padded to fit hardware layout.
|
||||
*/
|
||||
template <typename T>
|
||||
void PushRaw(const T& value);
|
||||
/// @brief Copies the content of the given trivially copyable class to the buffer as a normal param
|
||||
/// @note: The input class must be correctly packed/padded to fit hardware layout.
|
||||
template <typename T> void PushRaw(const T& value);
|
||||
template <typename... O> void PushMoveObjects(Service::HLERequestContext& ctx, O*... pointers);
|
||||
template <typename... O> void PushMoveObjects(Service::HLERequestContext& ctx, O&... pointers);
|
||||
template <typename... O> void PushCopyObjects(Service::HLERequestContext& ctx, O*... pointers);
|
||||
template <typename... O> void PushCopyObjects(Service::HLERequestContext& ctx, O&... pointers);
|
||||
|
||||
template <typename... O>
|
||||
void PushMoveObjects(O*... pointers);
|
||||
|
||||
template <typename... O>
|
||||
void PushMoveObjects(O&... pointers);
|
||||
|
||||
template <typename... O>
|
||||
void PushCopyObjects(O*... pointers);
|
||||
|
||||
template <typename... O>
|
||||
void PushCopyObjects(O&... pointers);
|
||||
|
||||
private:
|
||||
u32* cmdbuf;
|
||||
u32 index = 0;
|
||||
u32 normal_params_size{};
|
||||
u32 num_handles_to_copy{};
|
||||
u32 num_objects_to_move{}; ///< Domain objects or move handles, context dependent
|
||||
u32 data_payload_index{};
|
||||
Kernel::KernelCore& kernel;
|
||||
};
|
||||
|
||||
/// Push ///
|
||||
@@ -251,8 +193,7 @@ inline void ResponseBuilder::PushImpl(u32 value) {
|
||||
|
||||
template <typename T>
|
||||
void ResponseBuilder::PushRaw(const T& value) {
|
||||
static_assert(std::is_trivially_copyable_v<T>,
|
||||
"It's undefined behavior to use memcpy with non-trivially copyable objects");
|
||||
static_assert(std::is_trivially_copyable_v<T>, "It's undefined behavior to use memcpy with non-trivially copyable objects");
|
||||
std::memcpy(cmdbuf + index, &value, sizeof(T));
|
||||
index += (sizeof(T) + 3) / 4; // round up to word length
|
||||
}
|
||||
@@ -272,8 +213,8 @@ inline void ResponseBuilder::PushImpl(s16 value) {
|
||||
}
|
||||
|
||||
inline void ResponseBuilder::PushImpl(s64 value) {
|
||||
PushImpl(static_cast<u32>(value));
|
||||
PushImpl(static_cast<u32>(value >> 32));
|
||||
PushImpl(u32(value));
|
||||
PushImpl(u32(value >> 32));
|
||||
}
|
||||
|
||||
inline void ResponseBuilder::PushImpl(u8 value) {
|
||||
@@ -285,8 +226,8 @@ inline void ResponseBuilder::PushImpl(u16 value) {
|
||||
}
|
||||
|
||||
inline void ResponseBuilder::PushImpl(u64 value) {
|
||||
PushImpl(static_cast<u32>(value));
|
||||
PushImpl(static_cast<u32>(value >> 32));
|
||||
PushImpl(u32(value));
|
||||
PushImpl(u32(value >> 32));
|
||||
}
|
||||
|
||||
inline void ResponseBuilder::PushImpl(float value) {
|
||||
@@ -312,90 +253,88 @@ void ResponseBuilder::Push(const First& first_value, const Other&... other_value
|
||||
}
|
||||
|
||||
template <typename... O>
|
||||
inline void ResponseBuilder::PushCopyObjects(O*... pointers) {
|
||||
inline void ResponseBuilder::PushCopyObjects(Service::HLERequestContext& ctx, O*... pointers) {
|
||||
auto objects = {pointers...};
|
||||
for (auto& object : objects) {
|
||||
context->AddCopyObject(object);
|
||||
ctx.AddCopyObject(object);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... O>
|
||||
inline void ResponseBuilder::PushCopyObjects(O&... pointers) {
|
||||
inline void ResponseBuilder::PushCopyObjects(Service::HLERequestContext& ctx, O&... pointers) {
|
||||
auto objects = {&pointers...};
|
||||
for (auto& object : objects) {
|
||||
context->AddCopyObject(object);
|
||||
ctx.AddCopyObject(object);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... O>
|
||||
inline void ResponseBuilder::PushMoveObjects(O*... pointers) {
|
||||
inline void ResponseBuilder::PushMoveObjects(Service::HLERequestContext& ctx, O*... pointers) {
|
||||
auto objects = {pointers...};
|
||||
for (auto& object : objects) {
|
||||
context->AddMoveObject(object);
|
||||
ctx.AddMoveObject(object);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... O>
|
||||
inline void ResponseBuilder::PushMoveObjects(O&... pointers) {
|
||||
inline void ResponseBuilder::PushMoveObjects(Service::HLERequestContext& ctx, O&... pointers) {
|
||||
auto objects = {&pointers...};
|
||||
for (auto& object : objects) {
|
||||
context->AddMoveObject(object);
|
||||
ctx.AddMoveObject(object);
|
||||
}
|
||||
}
|
||||
|
||||
class RequestParser : public RequestHelperBase {
|
||||
public:
|
||||
explicit RequestParser(u32* command_buffer) : RequestHelperBase(command_buffer) {}
|
||||
|
||||
explicit RequestParser(Service::HLERequestContext& ctx) : RequestHelperBase(ctx) {
|
||||
struct RequestParser {
|
||||
inline explicit RequestParser(u32* command_buffer) : cmdbuf(command_buffer) {}
|
||||
inline explicit RequestParser(Service::HLERequestContext& ctx)
|
||||
: cmdbuf(ctx.CommandBuffer())
|
||||
{
|
||||
// TIPC does not have data payload offset
|
||||
if (!ctx.IsTipc()) {
|
||||
ASSERT_MSG(ctx.GetDataPayloadOffset(), "context is incomplete");
|
||||
Skip(ctx.GetDataPayloadOffset(), false);
|
||||
}
|
||||
|
||||
// Skip the u64 command id, it's already stored in the context
|
||||
static constexpr u32 CommandIdSize = 2;
|
||||
Skip(CommandIdSize, false);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T Pop();
|
||||
inline void Skip(u32 size_in_words, bool set_to_null) {
|
||||
if (set_to_null) std::memset(cmdbuf + index, 0, size_in_words * sizeof(u32));
|
||||
index += size_in_words;
|
||||
}
|
||||
/// @brief Aligns the current position forward to a 16-byte boundary, padding with zeros.
|
||||
inline void AlignWithPadding() { if (index & 3) Skip(u32(4 - (index & 3)), true); }
|
||||
inline u32 GetCurrentOffset() const { return index; }
|
||||
inline void SetCurrentOffset(u32 offset) { index = offset; }
|
||||
|
||||
template <typename T>
|
||||
void Pop(T& value);
|
||||
|
||||
template <typename First, typename... Other>
|
||||
void Pop(First& first_value, Other&... other_values);
|
||||
template <typename T> T Pop();
|
||||
template <typename T> void Pop(T& value);
|
||||
template <typename First, typename... Other> void Pop(First& first_value, Other&... other_values);
|
||||
|
||||
template <typename T>
|
||||
T PopEnum() {
|
||||
static_assert(std::is_enum_v<T>, "T must be an enum type within a PopEnum call.");
|
||||
static_assert(!std::is_convertible_v<T, int>,
|
||||
"enum type in PopEnum must be a strongly typed enum.");
|
||||
return static_cast<T>(Pop<std::underlying_type_t<T>>());
|
||||
static_assert(!std::is_convertible_v<T, int>, "enum type in PopEnum must be a strongly typed enum.");
|
||||
return T(Pop<std::underlying_type_t<T>>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reads the next normal parameters as a struct, by copying it
|
||||
* @note: The output class must be correctly packed/padded to fit hardware layout.
|
||||
*/
|
||||
template <typename T>
|
||||
void PopRaw(T& value);
|
||||
/// @brief Reads the next normal parameters as a struct, by copying it
|
||||
/// @note: The output class must be correctly packed/padded to fit hardware layout.
|
||||
template <typename T> void PopRaw(T& value);
|
||||
|
||||
/**
|
||||
* @brief Reads the next normal parameters as a struct, by copying it into a new value
|
||||
* @note: The output class must be correctly packed/padded to fit hardware layout.
|
||||
*/
|
||||
template <typename T>
|
||||
T PopRaw();
|
||||
/// @brief Reads the next normal parameters as a struct, by copying it into a new value
|
||||
/// @note: The output class must be correctly packed/padded to fit hardware layout.
|
||||
template <typename T> T PopRaw();
|
||||
|
||||
template <class T>
|
||||
std::weak_ptr<T> PopIpcInterface() {
|
||||
ASSERT(context->GetManager()->IsDomain());
|
||||
ASSERT(context->GetDomainMessageHeader().input_object_count > 0);
|
||||
return context->GetDomainHandler<T>(Pop<u32>() - 1);
|
||||
template <class T> [[nodiscard]] std::weak_ptr<T> PopIpcInterface(Service::HLERequestContext& ctx) {
|
||||
ASSERT(ctx.GetManager()->IsDomain());
|
||||
ASSERT(ctx.GetDomainMessageHeader().input_object_count > 0);
|
||||
return ctx.GetDomainHandler<T>(Pop<u32>() - 1);
|
||||
}
|
||||
|
||||
u32* cmdbuf;
|
||||
u32 index = 0;
|
||||
};
|
||||
|
||||
/// Pop ///
|
||||
@@ -407,7 +346,7 @@ inline u32 RequestParser::Pop() {
|
||||
|
||||
template <>
|
||||
inline s32 RequestParser::Pop() {
|
||||
return static_cast<s32>(Pop<u32>());
|
||||
return s32(Pop<u32>());
|
||||
}
|
||||
|
||||
// Ignore the -Wclass-memaccess warning on memcpy for non-trivially default constructible objects.
|
||||
@@ -417,8 +356,7 @@ inline s32 RequestParser::Pop() {
|
||||
#endif
|
||||
template <typename T>
|
||||
void RequestParser::PopRaw(T& value) {
|
||||
static_assert(std::is_trivially_copyable_v<T>,
|
||||
"It's undefined behavior to use memcpy with non-trivially copyable objects");
|
||||
static_assert(std::is_trivially_copyable_v<T>, "It's undefined behavior to use memcpy with non-trivially copyable objects");
|
||||
std::memcpy(&value, cmdbuf + index, sizeof(T));
|
||||
index += (sizeof(T) + 3) / 4; // round up to word length
|
||||
}
|
||||
|
||||
@@ -353,7 +353,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<ILogger>(system);
|
||||
rb.PushIpcInterface<ILogger>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -151,7 +151,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IAm>(system);
|
||||
rb.PushIpcInterface<IAm>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -173,7 +173,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<MFIUser>(system);
|
||||
rb.PushIpcInterface<MFIUser>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -195,7 +195,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IUser>(system);
|
||||
rb.PushIpcInterface<IUser>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -217,7 +217,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<ISystem>(system);
|
||||
rb.PushIpcInterface<ISystem>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -143,7 +143,7 @@ void NfcInterface::AttachAvailabilityChangeEvent(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(GetManager()->AttachAvailabilityChangeEvent());
|
||||
rb.PushCopyObjects(ctx, GetManager()->AttachAvailabilityChangeEvent());
|
||||
}
|
||||
|
||||
void NfcInterface::StartDetection(HLERequestContext& ctx) {
|
||||
@@ -203,7 +203,7 @@ void NfcInterface::AttachActivateEvent(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(result);
|
||||
rb.PushCopyObjects(out_event);
|
||||
rb.PushCopyObjects(ctx, out_event);
|
||||
}
|
||||
|
||||
void NfcInterface::AttachDeactivateEvent(HLERequestContext& ctx) {
|
||||
@@ -217,7 +217,7 @@ void NfcInterface::AttachDeactivateEvent(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(result);
|
||||
rb.PushCopyObjects(out_event);
|
||||
rb.PushCopyObjects(ctx, out_event);
|
||||
}
|
||||
|
||||
void NfcInterface::SetNfcEnabled(HLERequestContext& ctx) {
|
||||
|
||||
@@ -157,7 +157,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IUser>(system);
|
||||
rb.PushIpcInterface<IUser>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -179,7 +179,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<ISystem>(system);
|
||||
rb.PushIpcInterface<ISystem>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -201,7 +201,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IDebug>(system);
|
||||
rb.PushIpcInterface<IDebug>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -307,7 +307,7 @@ private:
|
||||
void GetSystemEventReadableHandle(HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(evt_scan_complete->GetReadableEvent(),
|
||||
rb.PushCopyObjects(ctx, evt_scan_complete->GetReadableEvent(),
|
||||
evt_processing->GetReadableEvent());
|
||||
}
|
||||
|
||||
@@ -452,7 +452,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(event1->GetReadableEvent(), event2->GetReadableEvent());
|
||||
rb.PushCopyObjects(ctx, event1->GetReadableEvent(), event2->GetReadableEvent());
|
||||
}
|
||||
|
||||
void Cancel(HLERequestContext& ctx) {
|
||||
@@ -528,7 +528,7 @@ void IGeneralService::CreateScanRequest(HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IScanRequest>(system);
|
||||
rb.PushIpcInterface<IScanRequest>(ctx, system);
|
||||
}
|
||||
|
||||
void IGeneralService::CreateRequest(HLERequestContext& ctx) {
|
||||
@@ -537,7 +537,7 @@ void IGeneralService::CreateRequest(HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IRequest>(system);
|
||||
rb.PushIpcInterface<IRequest>(ctx, system);
|
||||
}
|
||||
|
||||
void IGeneralService::GetCurrentNetworkProfile(HLERequestContext& ctx) {
|
||||
@@ -716,7 +716,7 @@ void IGeneralService::GetNetworkProfile(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<INetworkProfile>(system);
|
||||
rb.PushIpcInterface<INetworkProfile>(ctx, system);
|
||||
}
|
||||
|
||||
void IGeneralService::SetNetworkProfile(HLERequestContext& ctx) {
|
||||
@@ -869,7 +869,7 @@ void IGeneralService::CreateTemporaryNetworkProfile(HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 6, 0, 1};
|
||||
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<INetworkProfile>(system);
|
||||
rb.PushIpcInterface<INetworkProfile>(ctx, system);
|
||||
rb.PushRaw<u128>(uuid);
|
||||
}
|
||||
|
||||
@@ -1124,7 +1124,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IGeneralService>(system);
|
||||
rb.PushIpcInterface<IGeneralService>(ctx, system);
|
||||
}
|
||||
|
||||
void CreateGeneralService(HLERequestContext& ctx) {
|
||||
@@ -1132,7 +1132,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IGeneralService>(system);
|
||||
rb.PushIpcInterface<IGeneralService>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ private:
|
||||
LOG_WARNING(Service_NIM, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IShopServiceAsync>(system);
|
||||
rb.PushIpcInterface<IShopServiceAsync>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -75,7 +75,7 @@ private:
|
||||
LOG_WARNING(Service_NIM, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IShopServiceAccessor>(system);
|
||||
rb.PushIpcInterface<IShopServiceAccessor>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -336,7 +336,7 @@ private:
|
||||
LOG_DEBUG(Service_NIM, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IShopServiceAccessServer>(system);
|
||||
rb.PushIpcInterface<IShopServiceAccessServer>(ctx, system);
|
||||
}
|
||||
|
||||
void IsLargeResourceAvailable(HLERequestContext& ctx) {
|
||||
@@ -356,7 +356,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IShopServiceAccessServer>(system);
|
||||
rb.PushIpcInterface<IShopServiceAccessServer>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -439,7 +439,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(finished_event->GetReadableEvent());
|
||||
rb.PushCopyObjects(ctx, finished_event->GetReadableEvent());
|
||||
}
|
||||
|
||||
void GetResult(HLERequestContext& ctx) {
|
||||
@@ -500,7 +500,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IEnsureNetworkClockAvailabilityService>(system);
|
||||
rb.PushIpcInterface<IEnsureNetworkClockAvailabilityService>(ctx, system);
|
||||
}
|
||||
|
||||
// TODO(ogniK): Do we need these?
|
||||
|
||||
@@ -359,8 +359,8 @@ void IReadOnlyApplicationControlDataInterface::ListApplicationTitle(HLERequestCo
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(async_value->ReadableEvent());
|
||||
rb.PushIpcInterface(std::move(async_value));
|
||||
rb.PushCopyObjects(ctx, async_value->ReadableEvent());
|
||||
rb.PushIpcInterface(ctx, std::move(async_value));
|
||||
}
|
||||
|
||||
Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData3(
|
||||
|
||||
@@ -199,7 +199,7 @@ void NVDRV::QueryEvent(HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 3, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
auto& readable_event = event->GetReadableEvent();
|
||||
rb.PushCopyObjects(readable_event);
|
||||
rb.PushCopyObjects(ctx, readable_event);
|
||||
rb.PushEnum(NvResult::Success);
|
||||
} else {
|
||||
LOG_ERROR(Service_NVDRV, "Invalid event request!");
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Core {
|
||||
@@ -28,6 +30,15 @@ public:
|
||||
inline explicit Process(Core::System& system) noexcept : m_system(system) {}
|
||||
inline ~Process() { this->Finalize(); }
|
||||
|
||||
Process(const Process&) = delete;
|
||||
Process& operator=(const Process&) = delete;
|
||||
Process& operator=(Process&&) = delete;
|
||||
inline Process(Process&& other) noexcept
|
||||
: m_system(other.m_system), m_process(std::exchange(other.m_process, nullptr)),
|
||||
m_main_thread_stack_size(std::exchange(other.m_main_thread_stack_size, 0)),
|
||||
m_main_thread_priority(std::exchange(other.m_main_thread_priority, 0)),
|
||||
m_process_started(std::exchange(other.m_process_started, false)) {}
|
||||
|
||||
bool Initialize(Loader::AppLoader& loader, Loader::ResultStatus& out_load_result);
|
||||
void Finalize();
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -124,7 +127,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IClkrstSession>(system, device_code);
|
||||
rb.PushIpcInterface<IClkrstSession>(ctx, system, device_code);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -161,7 +161,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 10, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(*process);
|
||||
rb.PushCopyObjects(ctx, *process);
|
||||
rb.PushRaw(program_location);
|
||||
rb.PushRaw(override_status);
|
||||
}
|
||||
|
||||
@@ -147,7 +147,7 @@ void IAlarmService::CreateWakeupAlarm(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<ISteadyClockAlarm>(system, m_alarms, AlarmType::WakeupAlarm);
|
||||
rb.PushIpcInterface<ISteadyClockAlarm>(ctx, system, m_alarms, AlarmType::WakeupAlarm);
|
||||
}
|
||||
|
||||
void IAlarmService::CreateBackgroundTaskAlarm(HLERequestContext& ctx) {
|
||||
@@ -155,7 +155,7 @@ void IAlarmService::CreateBackgroundTaskAlarm(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<ISteadyClockAlarm>(system, m_alarms, AlarmType::BackgroundTaskAlarm);
|
||||
rb.PushIpcInterface<ISteadyClockAlarm>(ctx, system, m_alarms, AlarmType::BackgroundTaskAlarm);
|
||||
}
|
||||
|
||||
ISteadyClockAlarm::ISteadyClockAlarm(Core::System& system_, Alarms& alarms, AlarmType type)
|
||||
@@ -179,7 +179,7 @@ void ISteadyClockAlarm::GetAlarmEvent(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(m_alarm.GetEventHandle());
|
||||
rb.PushCopyObjects(ctx, m_alarm.GetEventHandle());
|
||||
}
|
||||
|
||||
void ISteadyClockAlarm::Enable(HLERequestContext& ctx) {
|
||||
|
||||
@@ -67,7 +67,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(state_change_event->GetReadableEvent());
|
||||
rb.PushCopyObjects(ctx, state_change_event->GetReadableEvent());
|
||||
}
|
||||
|
||||
void UnbindStateChangeEvent(HLERequestContext& ctx) {
|
||||
@@ -186,7 +186,7 @@ void PSM::OpenSession(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IPsmSession>(system);
|
||||
rb.PushIpcInterface<IPsmSession>(ctx, system);
|
||||
}
|
||||
|
||||
void PSM::GetBatteryVoltageState(HLERequestContext& ctx) {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
@@ -82,7 +85,7 @@ void TS::OpenSession(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<ISession>(system);
|
||||
rb.PushIpcInterface<ISession>(ctx, system);
|
||||
}
|
||||
|
||||
} // namespace Service::PTM
|
||||
|
||||
@@ -140,7 +140,7 @@ void SM::GetServiceCmif(HLERequestContext& ctx) {
|
||||
if (result == ResultSuccess) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
|
||||
rb.Push(result);
|
||||
rb.PushMoveObjects(client_session);
|
||||
rb.PushMoveObjects(ctx, client_session);
|
||||
} else {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
@@ -157,7 +157,7 @@ void SM::GetServiceTipc(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
|
||||
rb.Push(result);
|
||||
rb.PushMoveObjects(result == ResultSuccess ? client_session : nullptr);
|
||||
rb.PushMoveObjects(ctx, result == ResultSuccess ? client_session : nullptr);
|
||||
}
|
||||
|
||||
static std::string PopServiceName(IPC::RequestParser& rp) {
|
||||
@@ -230,8 +230,7 @@ void SM::RegisterServiceImpl(HLERequestContext& ctx, std::string name, u32 max_s
|
||||
max_session_count, is_light);
|
||||
|
||||
Kernel::KServerPort* server_port{};
|
||||
if (const auto result = service_manager.RegisterService(std::addressof(server_port), name,
|
||||
max_session_count, nullptr);
|
||||
if (const auto result = service_manager.RegisterService(std::addressof(server_port), name, max_session_count, nullptr);
|
||||
result.IsError()) {
|
||||
LOG_ERROR(Service_SM, "failed to register service with error_code={:08X}", result.raw);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@@ -241,7 +240,7 @@ void SM::RegisterServiceImpl(HLERequestContext& ctx, std::string name, u32 max_s
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushMoveObjects(server_port);
|
||||
rb.PushMoveObjects(ctx, server_port);
|
||||
}
|
||||
|
||||
void SM::UnregisterService(HLERequestContext& ctx) {
|
||||
|
||||
@@ -60,7 +60,7 @@ void Controller::CloneCurrentObject(HLERequestContext& ctx) {
|
||||
// We succeeded.
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushMoveObjects(session->GetClientSession());
|
||||
rb.PushMoveObjects(ctx, session->GetClientSession());
|
||||
}
|
||||
|
||||
void Controller::CloneCurrentObjectEx(HLERequestContext& ctx) {
|
||||
|
||||
@@ -24,9 +24,6 @@
|
||||
#include "network/network.h"
|
||||
#include <common/settings.h>
|
||||
|
||||
using Common::Expected;
|
||||
using Common::Unexpected;
|
||||
|
||||
namespace Service::Sockets {
|
||||
|
||||
namespace {
|
||||
@@ -464,13 +461,22 @@ void BSD::DuplicateSocket(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto input = rp.PopRaw<InputParameters>();
|
||||
|
||||
Expected<s32, Errno> res = DuplicateSocketImpl(input.fd);
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushRaw(OutputParameters{
|
||||
.ret = res.value_or(0),
|
||||
.bsd_errno = res ? Errno::SUCCESS : res.error(),
|
||||
});
|
||||
|
||||
auto const res_v = DuplicateSocketImpl(input.fd);
|
||||
if (auto* res = std::get_if<s32>(&res_v)) {
|
||||
rb.PushRaw(OutputParameters{
|
||||
.ret = *res,
|
||||
.bsd_errno = Errno::SUCCESS,
|
||||
});
|
||||
} else {
|
||||
auto* err = std::get_if<Errno>(&res_v);
|
||||
rb.PushRaw(OutputParameters{
|
||||
.ret = 0,
|
||||
.bsd_errno = *err,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void BSD::EventFd(HLERequestContext& ctx) {
|
||||
@@ -977,15 +983,15 @@ Errno BSD::CloseImpl(s32 fd) {
|
||||
return bsd_errno;
|
||||
}
|
||||
|
||||
Expected<s32, Errno> BSD::DuplicateSocketImpl(s32 fd) {
|
||||
std::variant<s32, Errno> BSD::DuplicateSocketImpl(s32 fd) {
|
||||
if (!IsFileDescriptorValid(fd)) {
|
||||
return Unexpected(Errno::BADF);
|
||||
return Errno::BADF;
|
||||
}
|
||||
|
||||
const s32 new_fd = FindFreeFileDescriptorHandle();
|
||||
if (new_fd < 0) {
|
||||
LOG_ERROR(Service, "No more file descriptors available");
|
||||
return Unexpected(Errno::MFILE);
|
||||
return Errno::MFILE;
|
||||
}
|
||||
|
||||
file_descriptors[new_fd] = FileDescriptor{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
@@ -8,9 +8,9 @@
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
#include <variant>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/expected.h"
|
||||
#include "common/socket_types.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/sockets/sockets.h"
|
||||
@@ -35,7 +35,7 @@ public:
|
||||
// These methods are called from SSL; the first two are also called from
|
||||
// this class for the corresponding IPC methods.
|
||||
// On the real device, the SSL service makes IPC calls to this service.
|
||||
Common::Expected<s32, Errno> DuplicateSocketImpl(s32 fd);
|
||||
std::variant<s32, Errno> DuplicateSocketImpl(s32 fd);
|
||||
Errno CloseImpl(s32 fd);
|
||||
std::optional<std::shared_ptr<Network::SocketBase>> GetSocket(s32 fd);
|
||||
|
||||
|
||||
@@ -88,11 +88,12 @@ static const constexpr std::array blockedDomains = {
|
||||
"sun.hac.lp1.d4c.nintendo.net",
|
||||
"phoenix-api.wbagora.com", //hogwarts legacy
|
||||
"battle.net",
|
||||
"microsoft.com", //minecraft dungeons + other games
|
||||
"microsoft.com", // Minecraft dungeons + other games
|
||||
"mojang.com",
|
||||
"xboxlive.com",
|
||||
"api.epicgames.dev", // marvel cosmic invasion +?
|
||||
"minecraftservices.com"
|
||||
"minecraftservices.com",
|
||||
"508223012e5a5ff19f30a391b2bdadc0.my.2k.com", // Civilization 5
|
||||
};
|
||||
|
||||
static bool IsBlockedHost(const std::string& host) {
|
||||
@@ -207,16 +208,15 @@ static std::pair<u32, GetAddrInfoError> GetHostByNameRequestImpl(HLERequestConte
|
||||
return {0, GetAddrInfoError::AGAIN};
|
||||
}
|
||||
|
||||
auto res = Network::GetAddressInfo(host, /*service*/ std::nullopt);
|
||||
if (!res.has_value()) {
|
||||
return {0, Translate(res.error())};
|
||||
auto res_v = Network::GetAddressInfo(host, /*service*/ std::nullopt);
|
||||
if (auto* res = std::get_if<std::vector<Network::AddrInfo>>(&res_v)) {
|
||||
const std::vector<u8> data = SerializeAddrInfoAsHostEnt(*res, host);
|
||||
const u32 data_size = u32(data.size());
|
||||
ctx.WriteBuffer(data, 0);
|
||||
return {data_size, GetAddrInfoError::SUCCESS};
|
||||
}
|
||||
|
||||
const std::vector<u8> data = SerializeAddrInfoAsHostEnt(res.value(), host);
|
||||
const u32 data_size = static_cast<u32>(data.size());
|
||||
ctx.WriteBuffer(data, 0);
|
||||
|
||||
return {data_size, GetAddrInfoError::SUCCESS};
|
||||
auto* err = std::get_if<Network::GetAddrInfoError>(&res_v);
|
||||
return {0, Translate(*err)};
|
||||
}
|
||||
|
||||
void SFDNSRES::GetHostByNameRequest(HLERequestContext& ctx) {
|
||||
@@ -332,16 +332,15 @@ static std::pair<u32, GetAddrInfoError> GetAddrInfoRequestImpl(HLERequestContext
|
||||
|
||||
// Serialized hints are also passed in a buffer, but are ignored for now.
|
||||
|
||||
auto res = Network::GetAddressInfo(host, service);
|
||||
if (!res.has_value()) {
|
||||
return {0, Translate(res.error())};
|
||||
auto res_v = Network::GetAddressInfo(host, service);
|
||||
if (auto* res = std::get_if<std::vector<Network::AddrInfo>>(&res_v)) {
|
||||
const std::vector<u8> data = SerializeAddrInfo(*res, host);
|
||||
const u32 data_size = u32(data.size());
|
||||
ctx.WriteBuffer(data, 0);
|
||||
return {data_size, GetAddrInfoError::SUCCESS};
|
||||
}
|
||||
|
||||
const std::vector<u8> data = SerializeAddrInfo(res.value(), host);
|
||||
const u32 data_size = static_cast<u32>(data.size());
|
||||
ctx.WriteBuffer(data, 0);
|
||||
|
||||
return {data_size, GetAddrInfoError::SUCCESS};
|
||||
auto* err = std::get_if<Network::GetAddrInfoError>(&res_v);
|
||||
return {0, Translate(*err)};
|
||||
}
|
||||
|
||||
void SFDNSRES::GetAddrInfoRequest(HLERequestContext& ctx) {
|
||||
|
||||
@@ -160,29 +160,26 @@ private:
|
||||
auto bsd = system.ServiceManager().GetService<Service::Sockets::BSD>("bsd:u");
|
||||
ASSERT_OR_EXECUTE(bsd, { return ResultInternalError; });
|
||||
|
||||
auto res = bsd->DuplicateSocketImpl(fd);
|
||||
if (!res.has_value()) {
|
||||
LOG_ERROR(Service_SSL, "Failed to duplicate socket with fd {}", fd);
|
||||
return ResultInvalidSocket;
|
||||
auto const res_v = bsd->DuplicateSocketImpl(fd);
|
||||
if (auto *res = std::get_if<s32>(&res_v)) {
|
||||
const s32 duplicated_fd = *res;
|
||||
if (do_not_close_socket) {
|
||||
*out_fd = duplicated_fd;
|
||||
} else {
|
||||
*out_fd = -1;
|
||||
fd_to_close = duplicated_fd;
|
||||
}
|
||||
std::optional<std::shared_ptr<Network::SocketBase>> sock = bsd->GetSocket(duplicated_fd);
|
||||
if (!sock.has_value()) {
|
||||
LOG_ERROR(Service_SSL, "invalid socket fd {} after duplication", duplicated_fd);
|
||||
return ResultInvalidSocket;
|
||||
}
|
||||
socket = std::move(*sock);
|
||||
backend->SetSocket(socket);
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
const s32 duplicated_fd = *res;
|
||||
|
||||
if (do_not_close_socket) {
|
||||
*out_fd = duplicated_fd;
|
||||
} else {
|
||||
*out_fd = -1;
|
||||
fd_to_close = duplicated_fd;
|
||||
}
|
||||
|
||||
std::optional<std::shared_ptr<Network::SocketBase>> sock = bsd->GetSocket(duplicated_fd);
|
||||
if (!sock.has_value()) {
|
||||
LOG_ERROR(Service_SSL, "invalid socket fd {} after duplication", duplicated_fd);
|
||||
return ResultInvalidSocket;
|
||||
}
|
||||
socket = std::move(*sock);
|
||||
backend->SetSocket(socket);
|
||||
return ResultSuccess;
|
||||
LOG_ERROR(Service_SSL, "Failed to duplicate socket with fd {}", fd);
|
||||
return ResultInvalidSocket;
|
||||
}
|
||||
|
||||
Result SetHostNameImpl(const std::string& hostname) {
|
||||
@@ -546,8 +543,7 @@ private:
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(res);
|
||||
if (res == ResultSuccess) {
|
||||
rb.PushIpcInterface<ISslConnection>(system, ssl_version, shared_data,
|
||||
std::move(backend));
|
||||
rb.PushIpcInterface<ISslConnection>(ctx, system, ssl_version, shared_data, std::move(backend));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -627,12 +623,11 @@ private:
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto parameters = rp.PopRaw<Parameters>();
|
||||
|
||||
LOG_WARNING(Service_SSL, "(STUBBED) called, api_version={}, pid_placeholder={}",
|
||||
parameters.ssl_version.api_version, parameters.pid_placeholder);
|
||||
LOG_WARNING(Service_SSL, "(STUBBED) called, api_version={}, pid_placeholder={}", parameters.ssl_version.api_version, parameters.pid_placeholder);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<ISslContext>(system, parameters.ssl_version);
|
||||
rb.PushIpcInterface<ISslContext>(ctx, system, parameters.ssl_version);
|
||||
}
|
||||
|
||||
void SetInterfaceVersion(HLERequestContext& ctx) {
|
||||
|
||||
@@ -155,7 +155,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IPdSession>(system);
|
||||
rb.PushIpcInterface<IPdSession>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -199,7 +199,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IPdCradleSession>(system);
|
||||
rb.PushIpcInterface<IPdCradleSession>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/expected.h"
|
||||
#include "common/logging.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/internal_network/network.h"
|
||||
@@ -733,15 +732,14 @@ u32 IPv4AddressToInteger(IPv4Address ip_addr) {
|
||||
static_cast<u32>(ip_addr[2]) << 8 | static_cast<u32>(ip_addr[3]);
|
||||
}
|
||||
|
||||
Common::Expected<std::vector<AddrInfo>, GetAddrInfoError> GetAddressInfo(
|
||||
std::variant<std::vector<AddrInfo>, GetAddrInfoError> GetAddressInfo(
|
||||
const std::string& host, const std::optional<std::string>& service) {
|
||||
addrinfo hints{};
|
||||
hints.ai_family = AF_INET; // Switch only supports IPv4.
|
||||
addrinfo* addrinfo;
|
||||
s32 gai_err = getaddrinfo(host.c_str(), service.has_value() ? service->c_str() : nullptr,
|
||||
&hints, &addrinfo);
|
||||
s32 gai_err = getaddrinfo(host.c_str(), service.has_value() ? service->c_str() : nullptr, &hints, &addrinfo);
|
||||
if (gai_err != 0) {
|
||||
return Common::Unexpected(TranslateGetAddrInfoErrorFromNative(gai_err));
|
||||
return TranslateGetAddrInfoErrorFromNative(gai_err);
|
||||
}
|
||||
std::vector<AddrInfo> ret;
|
||||
for (auto* current = addrinfo; current; current = current->ai_next) {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <array>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
#include <variant>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
@@ -124,7 +125,6 @@ std::string IPv4AddressToString(IPv4Address ip_addr);
|
||||
u32 IPv4AddressToInteger(IPv4Address ip_addr);
|
||||
|
||||
// named to avoid name collision with Windows macro
|
||||
Common::Expected<std::vector<AddrInfo>, GetAddrInfoError> GetAddressInfo(
|
||||
const std::string& host, const std::optional<std::string>& service);
|
||||
std::variant<std::vector<AddrInfo>, GetAddrInfoError> GetAddressInfo(const std::string& host, const std::optional<std::string>& service);
|
||||
|
||||
} // namespace Network
|
||||
|
||||
@@ -50,8 +50,8 @@ NPad::NPad(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service
|
||||
auto& controller = controller_data[aruid_index][i];
|
||||
controller.device = hid_core.GetEmulatedControllerByIndex(i);
|
||||
Core::HID::ControllerUpdateCallback engine_callback{
|
||||
.on_change = [this, i](Core::HID::ControllerTriggerType type) {
|
||||
ControllerUpdate(hid_core.kernel, type, i);
|
||||
.on_change = [this, i, kernel = &hid_core.kernel](Core::HID::ControllerTriggerType type) {
|
||||
ControllerUpdate(*kernel, type, i);
|
||||
},
|
||||
.is_npad_service = true,
|
||||
};
|
||||
|
||||
@@ -229,6 +229,8 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent) {
|
||||
INSERT(Settings, dma_accuracy, tr("DMA Accuracy:"),
|
||||
tr("Controls the DMA precision accuracy. Safe precision fixes issues in some games but "
|
||||
"may degrade performance."));
|
||||
INSERT(Settings, enable_gpu_buffer_readback, tr("Enable GPU buffer readback"),
|
||||
tr("Preserves GPU-modified buffer data by reading it back before uploads.\nSome games require this to render certain effects properly.\nMay cause issues if the hardware cannot handle the additional workload."));
|
||||
INSERT(Settings, use_asynchronous_shaders, tr("Enable asynchronous shader compilation"),
|
||||
tr("May reduce shader stutter."));
|
||||
INSERT(Settings, fast_gpu_time, tr("Fast GPU Time"),
|
||||
@@ -238,16 +240,16 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent) {
|
||||
tr("Accelerates BCn 3D texture decoding using GPU compute.\n"
|
||||
"Disable if experiencing crashes or graphical glitches."));
|
||||
INSERT(Settings, gpu_unswizzle_texture_size, tr("GPU Unswizzle Max Texture Size"),
|
||||
tr("Sets the maximum size (MiB) for GPU-based texture unswizzling.\n"
|
||||
tr("Sets the maximum size (power of 2 * 1MiB) for GPU-based texture unswizzling.\n"
|
||||
"While the GPU is faster for medium and large textures, the CPU may be more "
|
||||
"efficient for very small ones.\n"
|
||||
"Adjust this to find the balance between GPU acceleration and CPU overhead."));
|
||||
INSERT(Settings, gpu_unswizzle_stream_size, tr("GPU Unswizzle Stream Size"),
|
||||
tr("Sets the maximum amount of texture data (in MiB) processed per frame.\n"
|
||||
tr("Sets the maximum amount of texture data (power of 2 * 1MiB) processed per frame.\n"
|
||||
"Higher values can reduce stutter during texture loading but may impact frame "
|
||||
"consistency."));
|
||||
INSERT(Settings, gpu_unswizzle_chunk_size, tr("GPU Unswizzle Chunk Size"),
|
||||
tr("Determines the number of depth slices processed in a single dispatch.\n"
|
||||
tr("Determines the number of depth slices (power of 2) processed in a single dispatch.\n"
|
||||
"Increasing this can improve throughput on high-end GPUs but may cause TDR or driver "
|
||||
"timeouts on weaker hardware."));
|
||||
|
||||
@@ -650,31 +652,6 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QObject* parent) {
|
||||
PAIR(GpuOverclock, Medium, tr("Medium (256)")),
|
||||
PAIR(GpuOverclock, High, tr("High (512)")),
|
||||
}});
|
||||
translations->insert({Settings::EnumMetadata<Settings::GpuUnswizzleSize>::Index(),
|
||||
{
|
||||
PAIR(GpuUnswizzleSize, VerySmall, tr("Very Small (16 MB)")),
|
||||
PAIR(GpuUnswizzleSize, Small, tr("Small (32 MB)")),
|
||||
PAIR(GpuUnswizzleSize, Normal, tr("Normal (128 MB)")),
|
||||
PAIR(GpuUnswizzleSize, Large, tr("Large (256 MB)")),
|
||||
PAIR(GpuUnswizzleSize, VeryLarge, tr("Very Large (512 MB)")),
|
||||
}});
|
||||
translations->insert({Settings::EnumMetadata<Settings::GpuUnswizzle>::Index(),
|
||||
{
|
||||
PAIR(GpuUnswizzle, VeryLow, tr("Very Low (4 MB)")),
|
||||
PAIR(GpuUnswizzle, Low, tr("Low (8 MB)")),
|
||||
PAIR(GpuUnswizzle, Normal, tr("Normal (16 MB)")),
|
||||
PAIR(GpuUnswizzle, Medium, tr("Medium (32 MB)")),
|
||||
PAIR(GpuUnswizzle, High, tr("High (64 MB)")),
|
||||
}});
|
||||
translations->insert({Settings::EnumMetadata<Settings::GpuUnswizzleChunk>::Index(),
|
||||
{
|
||||
PAIR(GpuUnswizzleChunk, VeryLow, tr("Very Low (32)")),
|
||||
PAIR(GpuUnswizzleChunk, Low, tr("Low (64)")),
|
||||
PAIR(GpuUnswizzleChunk, Normal, tr("Normal (128)")),
|
||||
PAIR(GpuUnswizzleChunk, Medium, tr("Medium (256)")),
|
||||
PAIR(GpuUnswizzleChunk, High, tr("High (512)")),
|
||||
}});
|
||||
|
||||
translations->insert({Settings::EnumMetadata<Settings::ExtendedDynamicState>::Index(),
|
||||
{
|
||||
PAIR(ExtendedDynamicState, Disabled, tr("Disabled")),
|
||||
|
||||
@@ -236,8 +236,11 @@ void LowerGeometryPassthrough(const IR::Program& program, const HostTranslateInf
|
||||
|
||||
IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool,
|
||||
Environment& env, Flow::CFG& cfg, const HostTranslateInfo& host_info) {
|
||||
HostTranslateInfo normalized_host_info{host_info};
|
||||
normalized_host_info.ApplyDescriptorLimitPolicy();
|
||||
|
||||
IR::Program program;
|
||||
program.syntax_list = BuildASL(inst_pool, block_pool, env, cfg, host_info);
|
||||
program.syntax_list = BuildASL(inst_pool, block_pool, env, cfg, normalized_host_info);
|
||||
program.blocks = GenerateBlocks(program.syntax_list);
|
||||
program.post_order_blocks = PostOrder(program.syntax_list.front());
|
||||
program.stage = env.ShaderStage();
|
||||
@@ -260,9 +263,9 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
|
||||
program.info.passthrough.mask[i] = ((mask[i / 32] >> (i % 32)) & 1) == 0;
|
||||
}
|
||||
|
||||
if (!host_info.support_geometry_shader_passthrough) {
|
||||
if (!normalized_host_info.support_geometry_shader_passthrough) {
|
||||
program.output_vertices = GetOutputTopologyVertices(program.output_topology);
|
||||
LowerGeometryPassthrough(program, host_info);
|
||||
LowerGeometryPassthrough(program, normalized_host_info);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -277,16 +280,16 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
|
||||
RemoveUnreachableBlocks(program);
|
||||
|
||||
// Replace instructions before the SSA rewrite
|
||||
if (!host_info.support_float64) {
|
||||
if (!normalized_host_info.support_float64) {
|
||||
Optimization::LowerFp64ToFp32(program);
|
||||
}
|
||||
if (!host_info.support_float16) {
|
||||
if (!normalized_host_info.support_float16) {
|
||||
Optimization::LowerFp16ToFp32(program);
|
||||
}
|
||||
if (!host_info.support_int64) {
|
||||
if (!normalized_host_info.support_int64) {
|
||||
Optimization::LowerInt64ToInt32(program);
|
||||
}
|
||||
if (!host_info.support_conditional_barrier) {
|
||||
if (!normalized_host_info.support_conditional_barrier) {
|
||||
Optimization::ConditionalBarrierPass(program);
|
||||
}
|
||||
Optimization::SsaRewritePass(program);
|
||||
@@ -295,8 +298,8 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
|
||||
|
||||
Optimization::PositionPass(env, program);
|
||||
|
||||
Optimization::GlobalMemoryToStorageBufferPass(program, host_info);
|
||||
Optimization::TexturePass(env, program, host_info);
|
||||
Optimization::GlobalMemoryToStorageBufferPass(program, normalized_host_info);
|
||||
Optimization::TexturePass(env, program, normalized_host_info);
|
||||
|
||||
if (Settings::values.resolution_info.active || Settings::values.rescale_hack.GetValue()) {
|
||||
Optimization::RescalingPass(program);
|
||||
@@ -306,7 +309,7 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
|
||||
Optimization::VerificationPass(program);
|
||||
}
|
||||
Optimization::CollectShaderInfoPass(env, program);
|
||||
Optimization::LayerPass(program, host_info);
|
||||
Optimization::LayerPass(program, normalized_host_info);
|
||||
Optimization::VendorWorkaroundPass(program);
|
||||
|
||||
CollectInterpolationInfo(env, program);
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Shader {
|
||||
|
||||
// Try to keep entries here to a minimum
|
||||
@@ -13,20 +15,52 @@ namespace Shader {
|
||||
|
||||
/// Misc information about the host
|
||||
struct HostTranslateInfo {
|
||||
static constexpr u32 DEFAULT_DESCRIPTOR_LIMIT = 1024;
|
||||
|
||||
u64 min_ssbo_alignment{}; ///< Minimum alignment supported by the device for SSBOs
|
||||
u32 max_per_stage_descriptor_sampled_images{}; ///< maximum sampled descriptors per stage
|
||||
u32 max_per_stage_resources{}; ///< maximum resources per stage
|
||||
u32 max_descriptor_set_samplers{};
|
||||
u32 max_descriptor_set_uniform_buffers{};
|
||||
u32 max_descriptor_set_uniform_buffers_dynamic{};
|
||||
u32 max_descriptor_set_storage_buffers{};
|
||||
u32 max_descriptor_set_storage_buffers_dynamic{};
|
||||
u32 max_descriptor_set_sampled_images{};
|
||||
u32 max_descriptor_set_storage_images{};
|
||||
u32 max_descriptor_set_input_attachements{};
|
||||
bool support_float64{}; ///< True when the device supports 64-bit floats
|
||||
bool support_float16{}; ///< True when the device supports 16-bit floats
|
||||
bool support_int64{}; ///< True when the device supports 64-bit integers
|
||||
bool needs_demote_reorder{}; ///< True when the device needs DemoteToHelperInvocation reordered
|
||||
bool support_snorm_render_buffer{}; ///< True when the device supports SNORM render buffers
|
||||
bool support_viewport_index_layer{}; ///< True when the device supports gl_Layer in VS
|
||||
u32 min_ssbo_alignment{}; ///< Minimum alignment supported by the device for SSBOs
|
||||
u32 max_per_stage_descriptor_sampled_images{1024}; ///< maximum sampled descriptors per stage
|
||||
u32 max_per_stage_resources{4096}; ///< maximum resources per stage
|
||||
u32 max_descriptor_set_sampled_images{1024}; ///< maximum sampled descriptors per set
|
||||
bool support_geometry_shader_passthrough{}; ///< True when the device supports geometry
|
||||
///< passthrough shaders
|
||||
bool support_conditional_barrier{}; ///< True when the device supports barriers in conditional
|
||||
///< control flow
|
||||
|
||||
void ApplyDescriptorLimitPolicy() noexcept {
|
||||
if (min_ssbo_alignment == 0) {
|
||||
min_ssbo_alignment = 1;
|
||||
}
|
||||
ApplyDescriptorLimitFallback(max_per_stage_descriptor_sampled_images);
|
||||
ApplyDescriptorLimitFallback(max_per_stage_resources);
|
||||
ApplyDescriptorLimitFallback(max_descriptor_set_samplers);
|
||||
ApplyDescriptorLimitFallback(max_descriptor_set_uniform_buffers);
|
||||
ApplyDescriptorLimitFallback(max_descriptor_set_uniform_buffers_dynamic);
|
||||
ApplyDescriptorLimitFallback(max_descriptor_set_storage_buffers);
|
||||
ApplyDescriptorLimitFallback(max_descriptor_set_storage_buffers_dynamic);
|
||||
ApplyDescriptorLimitFallback(max_descriptor_set_sampled_images);
|
||||
ApplyDescriptorLimitFallback(max_descriptor_set_storage_images);
|
||||
ApplyDescriptorLimitFallback(max_descriptor_set_input_attachements);
|
||||
}
|
||||
|
||||
private:
|
||||
static void ApplyDescriptorLimitFallback(u32& limit) noexcept {
|
||||
if (limit == 0) {
|
||||
limit = DEFAULT_DESCRIPTOR_LIMIT;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Shader
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -545,7 +548,7 @@ void GlobalMemoryToStorageBufferPass(IR::Program& program, const HostTranslateIn
|
||||
IR::Block* const block{storage_inst.block};
|
||||
IR::Inst* const inst{storage_inst.inst};
|
||||
const IR::U32 offset{
|
||||
StorageOffset(*block, *inst, storage_buffer, host_info.min_ssbo_alignment)};
|
||||
StorageOffset(*block, *inst, storage_buffer, u32(host_info.min_ssbo_alignment))};
|
||||
Replace(*block, *inst, index, offset);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,71 +32,62 @@ struct TextureInst {
|
||||
using TextureInstVector = boost::container::small_vector<TextureInst, 24>;
|
||||
|
||||
constexpr u32 DESCRIPTOR_SIZE = 8;
|
||||
constexpr u32 DESCRIPTOR_SIZE_SHIFT = static_cast<u32>(std::countr_zero(DESCRIPTOR_SIZE));
|
||||
constexpr u32 DYNAMIC_DESCRIPTOR_CBUF_BYTES = 16 * 1024;
|
||||
constexpr u32 MAX_DYNAMIC_DESCRIPTOR_COUNT = 1024;
|
||||
constexpr u32 DESCRIPTOR_SIZE_SHIFT = u32(std::countr_zero(DESCRIPTOR_SIZE));
|
||||
constexpr u32 DESCRIPTOR_MAX_COUNT = 1024;
|
||||
|
||||
u32 DynamicDescriptorSizeShift(const IR::U32& dynamic_offset) {
|
||||
const IR::Inst* const inst{dynamic_offset.InstRecursive()};
|
||||
if (!inst || inst->GetOpcode() != IR::Opcode::ShiftLeftLogical32) {
|
||||
const IR::Inst* const inst = dynamic_offset.InstRecursive();
|
||||
if (!inst || inst->GetOpcode() != IR::Opcode::ShiftLeftLogical32)
|
||||
return DESCRIPTOR_SIZE_SHIFT;
|
||||
}
|
||||
const IR::Value shift{inst->Arg(1)};
|
||||
if (!shift.IsImmediate()) {
|
||||
const IR::Value shift = inst->Arg(1);
|
||||
if (!shift.IsImmediate())
|
||||
return DESCRIPTOR_SIZE_SHIFT;
|
||||
}
|
||||
const u32 size_shift{shift.U32()};
|
||||
return size_shift >= DESCRIPTOR_SIZE_SHIFT && size_shift < 31 ? size_shift
|
||||
: DESCRIPTOR_SIZE_SHIFT;
|
||||
const u32 size_shift = shift.U32();
|
||||
return size_shift >= DESCRIPTOR_SIZE_SHIFT && size_shift < 31 ? size_shift : DESCRIPTOR_SIZE_SHIFT;
|
||||
}
|
||||
|
||||
u32 DynamicDescriptorCount(u32 base_offset, u32 size_shift) {
|
||||
if (size_shift >= 31 || base_offset >= DYNAMIC_DESCRIPTOR_CBUF_BYTES) {
|
||||
u32 DynamicDescriptorCount(u32 base_offset, u32 size_shift, u32 max_descriptors) {
|
||||
auto const descriptor_limit = (std::max)(1U, max_descriptors);
|
||||
auto const max_cbuf_bytes = 16 * descriptor_limit;
|
||||
if (size_shift >= 31 || base_offset >= max_cbuf_bytes)
|
||||
return 1;
|
||||
}
|
||||
const u32 stride{1U << size_shift};
|
||||
const u32 available{DYNAMIC_DESCRIPTOR_CBUF_BYTES - base_offset};
|
||||
if (available < DESCRIPTOR_SIZE) {
|
||||
auto const stride = 1U << size_shift;
|
||||
auto const available = max_cbuf_bytes - base_offset;
|
||||
if (available < DESCRIPTOR_SIZE)
|
||||
return 1;
|
||||
}
|
||||
const u32 available_count{1U + (available - DESCRIPTOR_SIZE) / stride};
|
||||
return std::min(MAX_DYNAMIC_DESCRIPTOR_COUNT, available_count);
|
||||
auto const available_count = 1U + (available - DESCRIPTOR_SIZE) / stride;
|
||||
return std::min(descriptor_limit, available_count);
|
||||
}
|
||||
|
||||
u32 SaturatingSub(u32 lhs, u32 rhs) {
|
||||
return lhs > rhs ? lhs - rhs : 0;
|
||||
}
|
||||
|
||||
template <typename Descriptors>
|
||||
u32 StaticDescriptorCount(const Descriptors& descriptors) {
|
||||
u32 count{};
|
||||
for (const auto& desc : descriptors) {
|
||||
if (desc.count <= 1) {
|
||||
count += desc.count;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
template <typename T>
|
||||
[[nodiscard]] u32 StaticDescriptorCount(T const& descriptors) noexcept {
|
||||
return std::accumulate(descriptors.cbegin(), descriptors.cend(), 0U, [](auto const& acc, auto const& e) {
|
||||
return acc + (e.count <= 1 ? e.count : 0);
|
||||
});
|
||||
}
|
||||
|
||||
u32 DynamicSampledTextureCap(const Info& info, const HostTranslateInfo& host_info,
|
||||
u32 dynamic_arrays) {
|
||||
if (dynamic_arrays == 0) {
|
||||
return MAX_DYNAMIC_DESCRIPTOR_COUNT;
|
||||
u32 DynamicSampledTextureCap(const Info& info, const HostTranslateInfo& host_info, u32 dynamic_arrays) {
|
||||
auto const sampled_limit = (std::max)(1U, std::min(host_info.max_per_stage_descriptor_sampled_images,
|
||||
host_info.max_descriptor_set_sampled_images));
|
||||
auto const resource_limit = (std::max)(1U, host_info.max_per_stage_resources);
|
||||
if (dynamic_arrays > 0) {
|
||||
auto const sampled_static_count = StaticDescriptorCount(info.texture_buffer_descriptors) + StaticDescriptorCount(info.texture_descriptors);
|
||||
auto const resource_static_count =
|
||||
NumDescriptors(info.constant_buffer_descriptors)
|
||||
+ NumDescriptors(info.storage_buffers_descriptors)
|
||||
+ sampled_static_count + NumDescriptors(info.image_buffer_descriptors)
|
||||
+ NumDescriptors(info.image_descriptors);
|
||||
auto const sampled_budget = SaturatingSub(sampled_limit, sampled_static_count);
|
||||
auto const resource_budget = SaturatingSub(resource_limit, resource_static_count);
|
||||
auto const sampled_cap = sampled_budget / dynamic_arrays;
|
||||
auto const resource_cap = resource_budget / dynamic_arrays;
|
||||
return (std::max)(1U, (std::min)(sampled_cap, resource_cap));
|
||||
}
|
||||
const u32 sampled_static_count{StaticDescriptorCount(info.texture_buffer_descriptors) +
|
||||
StaticDescriptorCount(info.texture_descriptors)};
|
||||
const u32 resource_static_count{
|
||||
NumDescriptors(info.constant_buffer_descriptors) +
|
||||
NumDescriptors(info.storage_buffers_descriptors) + sampled_static_count +
|
||||
NumDescriptors(info.image_buffer_descriptors) + NumDescriptors(info.image_descriptors)};
|
||||
const u32 sampled_limit{std::min(host_info.max_per_stage_descriptor_sampled_images,
|
||||
host_info.max_descriptor_set_sampled_images)};
|
||||
const u32 sampled_budget{SaturatingSub(sampled_limit, sampled_static_count)};
|
||||
const u32 resource_budget{SaturatingSub(host_info.max_per_stage_resources,
|
||||
resource_static_count)};
|
||||
const u32 sampled_cap{sampled_budget / dynamic_arrays};
|
||||
const u32 resource_cap{resource_budget / dynamic_arrays};
|
||||
return std::max(1U, std::min({MAX_DYNAMIC_DESCRIPTOR_COUNT, sampled_cap, resource_cap}));
|
||||
return (std::min)({DESCRIPTOR_MAX_COUNT, sampled_limit, resource_limit});
|
||||
}
|
||||
|
||||
IR::Opcode IndexedInstruction(const IR::Inst& inst) {
|
||||
@@ -304,21 +295,23 @@ static inline bool IsTexturePixelFormatIntegerCached(Environment& env,
|
||||
}
|
||||
|
||||
|
||||
std::optional<ConstBufferAddr> Track(const IR::Value& value, Environment& env);
|
||||
static inline std::optional<ConstBufferAddr> TrackCached(const IR::Value& v, Environment& env) {
|
||||
std::optional<ConstBufferAddr> Track(const IR::Value& value, Environment& env, const HostTranslateInfo& host_info);
|
||||
static inline std::optional<ConstBufferAddr> TrackCached(const IR::Value& v, Environment& env, const HostTranslateInfo& host_info) {
|
||||
if (const IR::Inst* key = v.InstRecursive()) {
|
||||
if (auto it = env.track_cache.find(key); it != env.track_cache.end()) return it->second;
|
||||
auto found = Track(v, env);
|
||||
auto found = Track(v, env, host_info);
|
||||
if (found) env.track_cache.emplace(key, *found);
|
||||
return found;
|
||||
}
|
||||
return Track(v, env);
|
||||
return Track(v, env, host_info);
|
||||
}
|
||||
|
||||
std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environment& env);
|
||||
std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environment& env, const HostTranslateInfo& host_info);
|
||||
|
||||
std::optional<ConstBufferAddr> Track(const IR::Value& value, Environment& env) {
|
||||
return IR::BreadthFirstSearch(value, [&env](const IR::Inst* inst) { return TryGetConstBuffer(inst, env); });
|
||||
std::optional<ConstBufferAddr> Track(const IR::Value& value, Environment& env, const HostTranslateInfo& host_info) {
|
||||
return IR::BreadthFirstSearch(value, [&env, &host_info](const IR::Inst* inst) {
|
||||
return TryGetConstBuffer(inst, env, host_info);
|
||||
});
|
||||
}
|
||||
|
||||
std::optional<u32> TryGetConstant(IR::Value& value, Environment& env) {
|
||||
@@ -342,13 +335,13 @@ std::optional<u32> TryGetConstant(IR::Value& value, Environment& env) {
|
||||
return ReadCbufCached(env, index_number, offset_number);
|
||||
}
|
||||
|
||||
std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environment& env) {
|
||||
std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environment& env, const HostTranslateInfo& host_info) {
|
||||
switch (inst->GetOpcode()) {
|
||||
default:
|
||||
return std::nullopt;
|
||||
case IR::Opcode::BitwiseOr32: {
|
||||
std::optional lhs{TrackCached(inst->Arg(0), env)};
|
||||
std::optional rhs{TrackCached(inst->Arg(1), env)};
|
||||
std::optional lhs{TrackCached(inst->Arg(0), env, host_info)};
|
||||
std::optional rhs{TrackCached(inst->Arg(1), env, host_info)};
|
||||
if (!lhs || !rhs) {
|
||||
return std::nullopt;
|
||||
}
|
||||
@@ -378,12 +371,11 @@ std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environme
|
||||
if (!shift.IsImmediate()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
std::optional lhs{TrackCached(inst->Arg(0), env)};
|
||||
std::optional lhs{TrackCached(inst->Arg(0), env, host_info)};
|
||||
if (lhs) {
|
||||
lhs->shift_left = shift.U32();
|
||||
}
|
||||
return lhs;
|
||||
break;
|
||||
}
|
||||
case IR::Opcode::BitwiseAnd32: {
|
||||
IR::Value op1{inst->Arg(0)};
|
||||
@@ -407,7 +399,7 @@ std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environme
|
||||
return std::nullopt;
|
||||
} while (false);
|
||||
}
|
||||
std::optional lhs{TrackCached(op1, env)};
|
||||
std::optional lhs{TrackCached(op1, env, host_info)};
|
||||
if (lhs) {
|
||||
lhs->shift_left = static_cast<u32>(std::countr_zero(op2.U32()));
|
||||
}
|
||||
@@ -453,7 +445,10 @@ std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environme
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
const u32 size_shift{DynamicDescriptorSizeShift(dynamic_offset)};
|
||||
auto const size_shift = DynamicDescriptorSizeShift(dynamic_offset);
|
||||
auto const sampled_limit = (std::max)(1U, (std::min)(host_info.max_per_stage_descriptor_sampled_images,
|
||||
host_info.max_descriptor_set_sampled_images));
|
||||
auto const resource_limit = (std::max)(1U, host_info.max_per_stage_resources);
|
||||
return ConstBufferAddr{
|
||||
.index = index.U32(),
|
||||
.offset = base_offset,
|
||||
@@ -462,15 +457,15 @@ std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environme
|
||||
.secondary_offset = 0,
|
||||
.secondary_shift_left = 0,
|
||||
.dynamic_offset = dynamic_offset,
|
||||
.count = DynamicDescriptorCount(base_offset, size_shift),
|
||||
.count = DynamicDescriptorCount(base_offset, size_shift, (std::min)({DESCRIPTOR_MAX_COUNT, sampled_limit, resource_limit})),
|
||||
.has_secondary = false,
|
||||
};
|
||||
}
|
||||
|
||||
TextureInst MakeInst(Environment& env, IR::Block* block, IR::Inst& inst) {
|
||||
TextureInst MakeInst(Environment& env, IR::Block* block, IR::Inst& inst, const HostTranslateInfo& host_info) {
|
||||
ConstBufferAddr addr;
|
||||
if (IsBindless(inst)) {
|
||||
const std::optional<ConstBufferAddr> track_addr{TrackCached(inst.Arg(0), env)};
|
||||
const std::optional<ConstBufferAddr> track_addr{TrackCached(inst.Arg(0), env, host_info)};
|
||||
|
||||
if (!track_addr) {
|
||||
throw NotImplementedException("Failed to track bindless texture constant buffer");
|
||||
@@ -506,15 +501,15 @@ u32 GetTextureHandle(Environment& env, const ConstBufferAddr& cbuf) {
|
||||
return lhs_raw | rhs_raw;
|
||||
}
|
||||
|
||||
[[maybe_unused]]TextureType ReadTextureType(Environment& env, const ConstBufferAddr& cbuf) {
|
||||
[[maybe_unused]] TextureType ReadTextureType(Environment& env, const ConstBufferAddr& cbuf) {
|
||||
return env.ReadTextureType(GetTextureHandle(env, cbuf));
|
||||
}
|
||||
|
||||
[[maybe_unused]]TexturePixelFormat ReadTexturePixelFormat(Environment& env, const ConstBufferAddr& cbuf) {
|
||||
[[maybe_unused]] TexturePixelFormat ReadTexturePixelFormat(Environment& env, const ConstBufferAddr& cbuf) {
|
||||
return env.ReadTexturePixelFormat(GetTextureHandle(env, cbuf));
|
||||
}
|
||||
|
||||
[[maybe_unused]]bool IsTexturePixelFormatInteger(Environment& env, const ConstBufferAddr& cbuf) {
|
||||
[[maybe_unused]] bool IsTexturePixelFormatInteger(Environment& env, const ConstBufferAddr& cbuf) {
|
||||
return env.IsTexturePixelFormatInteger(GetTextureHandle(env, cbuf));
|
||||
}
|
||||
|
||||
@@ -675,7 +670,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
|
||||
if (!IsTextureInstruction(inst)) {
|
||||
continue;
|
||||
}
|
||||
to_replace.push_back(MakeInst(env, block, inst));
|
||||
to_replace.push_back(MakeInst(env, block, inst, host_info));
|
||||
}
|
||||
}
|
||||
// Sort instructions to visit textures by constant buffer index, then by offset
|
||||
@@ -689,8 +684,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
|
||||
program.info.texture_descriptors,
|
||||
program.info.image_descriptors,
|
||||
};
|
||||
const u32 sampled_dynamic_cap{
|
||||
DynamicSampledTextureCap(program.info, host_info, DynamicSampledTextureArrayCount(to_replace))};
|
||||
const u32 sampled_dynamic_cap = DynamicSampledTextureCap(program.info, host_info, DynamicSampledTextureArrayCount(to_replace));
|
||||
for (TextureInst& texture_inst : to_replace) {
|
||||
// TODO: Handle arrays
|
||||
IR::Inst* const inst{texture_inst.inst};
|
||||
|
||||
@@ -92,7 +92,6 @@ struct Profile {
|
||||
bool has_broken_robust{};
|
||||
|
||||
u64 min_ssbo_alignment{};
|
||||
|
||||
u32 max_user_clip_distances{};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -15,12 +18,19 @@ static constexpr auto PERMS = Common::MemoryPermission::ReadWrite;
|
||||
static constexpr auto HEAP = false;
|
||||
|
||||
TEST_CASE("HostMemory: Initialize and deinitialize", "[common]") {
|
||||
{ HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); }
|
||||
{ HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); }
|
||||
{
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
REQUIRE(mem.BackingBasePointer() != nullptr);
|
||||
}
|
||||
{
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
REQUIRE(mem.BackingBasePointer() != nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("HostMemory: Simple map", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
REQUIRE(mem.BackingBasePointer() != nullptr);
|
||||
mem.Map(0x5000, 0x8000, 0x1000, PERMS, HEAP);
|
||||
|
||||
volatile u8* const data = mem.VirtualBasePointer() + 0x5000;
|
||||
@@ -30,6 +40,7 @@ TEST_CASE("HostMemory: Simple map", "[common]") {
|
||||
|
||||
TEST_CASE("HostMemory: Simple mirror map", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
REQUIRE(mem.BackingBasePointer() != nullptr);
|
||||
mem.Map(0x5000, 0x3000, 0x2000, PERMS, HEAP);
|
||||
mem.Map(0x8000, 0x4000, 0x1000, PERMS, HEAP);
|
||||
|
||||
@@ -41,6 +52,7 @@ TEST_CASE("HostMemory: Simple mirror map", "[common]") {
|
||||
|
||||
TEST_CASE("HostMemory: Simple unmap", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
REQUIRE(mem.BackingBasePointer() != nullptr);
|
||||
mem.Map(0x5000, 0x3000, 0x2000, PERMS, HEAP);
|
||||
|
||||
volatile u8* const data = mem.VirtualBasePointer() + 0x5000;
|
||||
@@ -52,6 +64,7 @@ TEST_CASE("HostMemory: Simple unmap", "[common]") {
|
||||
|
||||
TEST_CASE("HostMemory: Simple unmap and remap", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
REQUIRE(mem.BackingBasePointer() != nullptr);
|
||||
mem.Map(0x5000, 0x3000, 0x2000, PERMS, HEAP);
|
||||
|
||||
volatile u8* const data = mem.VirtualBasePointer() + 0x5000;
|
||||
@@ -69,6 +82,7 @@ TEST_CASE("HostMemory: Simple unmap and remap", "[common]") {
|
||||
|
||||
TEST_CASE("HostMemory: Nieche allocation", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
REQUIRE(mem.BackingBasePointer() != nullptr);
|
||||
mem.Map(0x0000, 0, 0x20000, PERMS, HEAP);
|
||||
mem.Unmap(0x0000, 0x4000, HEAP);
|
||||
mem.Map(0x1000, 0, 0x2000, PERMS, HEAP);
|
||||
@@ -78,6 +92,7 @@ TEST_CASE("HostMemory: Nieche allocation", "[common]") {
|
||||
|
||||
TEST_CASE("HostMemory: Full unmap", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
REQUIRE(mem.BackingBasePointer() != nullptr);
|
||||
mem.Map(0x8000, 0, 0x4000, PERMS, HEAP);
|
||||
mem.Unmap(0x8000, 0x4000, HEAP);
|
||||
mem.Map(0x6000, 0, 0x16000, PERMS, HEAP);
|
||||
@@ -85,6 +100,7 @@ TEST_CASE("HostMemory: Full unmap", "[common]") {
|
||||
|
||||
TEST_CASE("HostMemory: Right out of bounds unmap", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
REQUIRE(mem.BackingBasePointer() != nullptr);
|
||||
mem.Map(0x0000, 0, 0x4000, PERMS, HEAP);
|
||||
mem.Unmap(0x2000, 0x4000, HEAP);
|
||||
mem.Map(0x2000, 0x80000, 0x4000, PERMS, HEAP);
|
||||
@@ -92,6 +108,7 @@ TEST_CASE("HostMemory: Right out of bounds unmap", "[common]") {
|
||||
|
||||
TEST_CASE("HostMemory: Left out of bounds unmap", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
|
||||
mem.Map(0x8000, 0, 0x4000, PERMS, HEAP);
|
||||
mem.Unmap(0x6000, 0x4000, HEAP);
|
||||
mem.Map(0x8000, 0, 0x2000, PERMS, HEAP);
|
||||
@@ -99,6 +116,7 @@ TEST_CASE("HostMemory: Left out of bounds unmap", "[common]") {
|
||||
|
||||
TEST_CASE("HostMemory: Multiple placeholder unmap", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
REQUIRE(mem.BackingBasePointer() != nullptr);
|
||||
mem.Map(0x0000, 0, 0x4000, PERMS, HEAP);
|
||||
mem.Map(0x4000, 0, 0x1b000, PERMS, HEAP);
|
||||
mem.Unmap(0x3000, 0x1c000, HEAP);
|
||||
@@ -107,6 +125,7 @@ TEST_CASE("HostMemory: Multiple placeholder unmap", "[common]") {
|
||||
|
||||
TEST_CASE("HostMemory: Unmap between placeholders", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
REQUIRE(mem.BackingBasePointer() != nullptr);
|
||||
mem.Map(0x0000, 0, 0x4000, PERMS, HEAP);
|
||||
mem.Map(0x4000, 0, 0x4000, PERMS, HEAP);
|
||||
mem.Unmap(0x2000, 0x4000, HEAP);
|
||||
@@ -115,6 +134,7 @@ TEST_CASE("HostMemory: Unmap between placeholders", "[common]") {
|
||||
|
||||
TEST_CASE("HostMemory: Unmap to origin", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
REQUIRE(mem.BackingBasePointer() != nullptr);
|
||||
mem.Map(0x4000, 0, 0x4000, PERMS, HEAP);
|
||||
mem.Map(0x8000, 0, 0x4000, PERMS, HEAP);
|
||||
mem.Unmap(0x4000, 0x4000, HEAP);
|
||||
@@ -124,6 +144,7 @@ TEST_CASE("HostMemory: Unmap to origin", "[common]") {
|
||||
|
||||
TEST_CASE("HostMemory: Unmap to right", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
REQUIRE(mem.BackingBasePointer() != nullptr);
|
||||
mem.Map(0x4000, 0, 0x4000, PERMS, HEAP);
|
||||
mem.Map(0x8000, 0, 0x4000, PERMS, HEAP);
|
||||
mem.Unmap(0x8000, 0x4000, HEAP);
|
||||
@@ -132,6 +153,7 @@ TEST_CASE("HostMemory: Unmap to right", "[common]") {
|
||||
|
||||
TEST_CASE("HostMemory: Partial right unmap check bindings", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
REQUIRE(mem.BackingBasePointer() != nullptr);
|
||||
mem.Map(0x4000, 0x10000, 0x4000, PERMS, HEAP);
|
||||
|
||||
volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000;
|
||||
@@ -144,6 +166,7 @@ TEST_CASE("HostMemory: Partial right unmap check bindings", "[common]") {
|
||||
|
||||
TEST_CASE("HostMemory: Partial left unmap check bindings", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
REQUIRE(mem.BackingBasePointer() != nullptr);
|
||||
mem.Map(0x4000, 0x10000, 0x4000, PERMS, HEAP);
|
||||
|
||||
volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000;
|
||||
@@ -158,6 +181,7 @@ TEST_CASE("HostMemory: Partial left unmap check bindings", "[common]") {
|
||||
|
||||
TEST_CASE("HostMemory: Partial middle unmap check bindings", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
REQUIRE(mem.BackingBasePointer() != nullptr);
|
||||
mem.Map(0x4000, 0x10000, 0x4000, PERMS, HEAP);
|
||||
|
||||
volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000;
|
||||
@@ -172,6 +196,7 @@ TEST_CASE("HostMemory: Partial middle unmap check bindings", "[common]") {
|
||||
|
||||
TEST_CASE("HostMemory: Partial sparse middle unmap and check bindings", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
REQUIRE(mem.BackingBasePointer() != nullptr);
|
||||
mem.Map(0x4000, 0x10000, 0x2000, PERMS, HEAP);
|
||||
mem.Map(0x6000, 0x20000, 0x2000, PERMS, HEAP);
|
||||
|
||||
|
||||
@@ -1634,15 +1634,17 @@ bool BufferCache<P>::SynchronizeBuffer(Buffer& buffer, DAddr device_addr, u32 si
|
||||
if (total_size_bytes == 0) {
|
||||
return true;
|
||||
}
|
||||
u64 min_offset = (std::numeric_limits<u64>::max)();
|
||||
u64 max_offset = 0;
|
||||
for (const auto& copy : upload_copies) {
|
||||
min_offset = (std::min)(min_offset, copy.dst_offset);
|
||||
max_offset = (std::max)(max_offset, copy.dst_offset + copy.size);
|
||||
if (Settings::values.enable_gpu_buffer_readback.GetValue()) {
|
||||
u64 min_offset = (std::numeric_limits<u64>::max)();
|
||||
u64 max_offset = 0;
|
||||
for (const auto& copy : upload_copies) {
|
||||
min_offset = (std::min)(min_offset, copy.dst_offset);
|
||||
max_offset = (std::max)(max_offset, copy.dst_offset + copy.size);
|
||||
}
|
||||
const DAddr sync_addr = buffer.CpuAddr() + min_offset;
|
||||
const u64 sync_size = max_offset - min_offset;
|
||||
DownloadBufferMemory(buffer, sync_addr, sync_size);
|
||||
}
|
||||
const DAddr sync_addr = buffer.CpuAddr() + min_offset;
|
||||
const u64 sync_size = max_offset - min_offset;
|
||||
DownloadBufferMemory(buffer, sync_addr, sync_size);
|
||||
const std::span<BufferCopy> copies_span(upload_copies.data(), upload_copies.size());
|
||||
UploadMemory(buffer, total_size_bytes, largest_copy, copies_span);
|
||||
any_buffer_uploaded = true;
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "common/settings.h"
|
||||
#include "video_core/dirty_flags.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
@@ -130,6 +132,44 @@ void Maxwell3D::DrawManager::SetInlineIndexBuffer(Maxwell3D& maxwell3d, u32 inde
|
||||
draw_state.draw_mode = DrawMode::InlineIndex;
|
||||
}
|
||||
|
||||
void Maxwell3D::DrawManager::SetInlineIndexBuffer(Maxwell3D& maxwell3d, u32 method,
|
||||
const u32* base_start, u32 amount) {
|
||||
auto& index_buffer = draw_state.inline_index_draw_indexes;
|
||||
switch (method) {
|
||||
case MAXWELL3D_REG_INDEX(draw_inline_index): {
|
||||
const auto* const bytes = reinterpret_cast<const u8*>(base_start);
|
||||
index_buffer.insert(index_buffer.end(), bytes, bytes + size_t(amount) * sizeof(u32));
|
||||
break;
|
||||
}
|
||||
case MAXWELL3D_REG_INDEX(inline_index_2x16.even): {
|
||||
const size_t offset = index_buffer.size();
|
||||
index_buffer.resize(offset + size_t(amount) * 2 * sizeof(u32));
|
||||
u8* dst = index_buffer.data() + offset;
|
||||
for (u32 i = 0; i < amount; ++i) {
|
||||
const u32 word = base_start[i];
|
||||
const u32 indexes[2]{word & 0xFFFF, word >> 16};
|
||||
std::memcpy(dst, indexes, sizeof(indexes));
|
||||
dst += sizeof(indexes);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MAXWELL3D_REG_INDEX(inline_index_4x8.index0): {
|
||||
const size_t offset = index_buffer.size();
|
||||
index_buffer.resize(offset + size_t(amount) * 4 * sizeof(u32));
|
||||
u8* dst = index_buffer.data() + offset;
|
||||
for (u32 i = 0; i < amount; ++i) {
|
||||
const u32 word = base_start[i];
|
||||
const u32 indexes[4]{word & 0xFF, (word >> 8) & 0xFF, (word >> 16) & 0xFF,
|
||||
word >> 24};
|
||||
std::memcpy(dst, indexes, sizeof(indexes));
|
||||
dst += sizeof(indexes);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
draw_state.draw_mode = DrawMode::InlineIndex;
|
||||
}
|
||||
|
||||
void Maxwell3D::DrawManager::DrawBegin(Maxwell3D& maxwell3d) {
|
||||
auto reset_instance_count = maxwell3d.regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::First;
|
||||
auto increment_instance_count = maxwell3d.regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Subsequent;
|
||||
|
||||
@@ -436,6 +436,14 @@ void Maxwell3D::CallMultiMethod(Core::System& system, u32 method, const u32* bas
|
||||
upload_state.ProcessData(base_start, amount);
|
||||
return;
|
||||
}
|
||||
case MAXWELL3D_REG_INDEX(draw_inline_index):
|
||||
case MAXWELL3D_REG_INDEX(inline_index_2x16.even):
|
||||
case MAXWELL3D_REG_INDEX(inline_index_4x8.index0):
|
||||
if (shadow_state.shadow_ram_control != Regs::ShadowRamControl::Replay) {
|
||||
ProcessInlineIndexMultiData(method, base_start, amount);
|
||||
break;
|
||||
}
|
||||
[[fallthrough]];
|
||||
default:
|
||||
for (u32 i = 0; i < amount; i++) {
|
||||
CallMethod(system, method, base_start[i], methods_pending - i <= 1);
|
||||
@@ -622,6 +630,15 @@ void Maxwell3D::ProcessCBData(u32 value) {
|
||||
ProcessCBMultiData(&value, 1);
|
||||
}
|
||||
|
||||
void Maxwell3D::ProcessInlineIndexMultiData(u32 method, const u32* start_base, u32 amount) {
|
||||
if (amount == 0) {
|
||||
return;
|
||||
}
|
||||
const u32 argument = ProcessShadowRam(method, start_base[amount - 1]);
|
||||
ProcessDirtyRegisters(method, argument);
|
||||
draw_manager.SetInlineIndexBuffer(*this, method, start_base, amount);
|
||||
}
|
||||
|
||||
Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const {
|
||||
const GPUVAddr tic_address_gpu{regs.tex_header.Address() +
|
||||
tic_index * sizeof(Texture::TICEntry)};
|
||||
|
||||
@@ -3077,6 +3077,7 @@ public:
|
||||
void DrawArrayIndirect(Maxwell3D& maxwell3d, Maxwell3D::Regs::PrimitiveTopology topology);
|
||||
void DrawIndexedIndirect(Maxwell3D& maxwell3d, Maxwell3D::Regs::PrimitiveTopology topology, u32 index_first, u32 index_count);
|
||||
void SetInlineIndexBuffer(Maxwell3D& maxwell3d, u32 index);
|
||||
void SetInlineIndexBuffer(Maxwell3D& maxwell3d, u32 method, const u32* base_start, u32 amount);
|
||||
void DrawBegin(Maxwell3D& maxwell3d);
|
||||
void DrawEnd(Maxwell3D& maxwell3d, u32 instance_count = 1, bool force_draw = false);
|
||||
void DrawIndexSmall(Maxwell3D& maxwell3d, u32 argument);
|
||||
@@ -3193,6 +3194,8 @@ public:
|
||||
void ProcessCBData(u32 value);
|
||||
void ProcessCBMultiData(const u32* start_base, u32 amount);
|
||||
|
||||
void ProcessInlineIndexMultiData(u32 method, const u32* start_base, u32 amount);
|
||||
|
||||
private:
|
||||
void InitializeRegisterDefaults();
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "video_core/gpu_logging/gpu_logging.h"
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
#include "common/fs/file.h"
|
||||
@@ -280,13 +281,12 @@ void GPULogger::LogMemoryDeallocation(uintptr_t memory) {
|
||||
}
|
||||
|
||||
void GPULogger::LogShaderCompilation(const std::string& shader_name,
|
||||
const std::string& shader_info,
|
||||
std::span<const u32> spirv_code) {
|
||||
const std::string& shader_info) {
|
||||
if (!initialized || current_level == LogLevel::Off) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!dump_shaders && current_level < LogLevel::Verbose) {
|
||||
if (current_level < LogLevel::Verbose) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -294,38 +294,36 @@ void GPULogger::LogShaderCompilation(const std::string& shader_name,
|
||||
std::chrono::steady_clock::now().time_since_epoch());
|
||||
|
||||
const auto log_entry = fmt::format("[{}] [Shader] Compiled: {} ({})\n",
|
||||
FormatTimestamp(timestamp), shader_name, shader_info);
|
||||
FormatTimestamp(timestamp), shader_name, shader_info);
|
||||
WriteToLog(log_entry);
|
||||
}
|
||||
|
||||
// Dump SPIR-V binary if enabled and we have data
|
||||
if (dump_shaders && !spirv_code.empty()) {
|
||||
using namespace Common::FS;
|
||||
const auto& log_dir = GetEdenPath(EdenPath::LogDir);
|
||||
const auto shaders_dir = log_dir / "shaders";
|
||||
bool IsActive() noexcept {
|
||||
return Settings::values.gpu_log_level.GetValue() != Settings::GpuLogLevel::Off;
|
||||
}
|
||||
|
||||
// Create directory on first dump
|
||||
if (!shader_dump_dir_created) {
|
||||
[[maybe_unused]] const bool created = CreateDir(shaders_dir);
|
||||
shader_dump_dir_created = true;
|
||||
}
|
||||
|
||||
// Write SPIR-V binary file
|
||||
const auto shader_path = shaders_dir / fmt::format("{}.spv", shader_name);
|
||||
auto shader_file = std::make_unique<Common::FS::IOFile>(
|
||||
shader_path, FileAccessMode::Write, FileType::BinaryFile);
|
||||
|
||||
if (shader_file->IsOpen()) {
|
||||
const size_t bytes_to_write = spirv_code.size() * sizeof(u32);
|
||||
static_cast<void>(shader_file->WriteSpan(spirv_code));
|
||||
shader_file->Close();
|
||||
|
||||
const auto dump_log = fmt::format("[{}] [Shader] Dumped SPIR-V: {} ({} bytes)\n",
|
||||
FormatTimestamp(timestamp), shader_path.string(), bytes_to_write);
|
||||
WriteToLog(dump_log);
|
||||
} else {
|
||||
LOG_WARNING(Render_Vulkan, "[GPU Logging] Failed to dump shader: {}", shader_path.string());
|
||||
}
|
||||
void DumpSpirvShader(u64 shader_hash, std::span<const u32> spirv_code) {
|
||||
if (spirv_code.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
using namespace Common::FS;
|
||||
const auto& dump_dir = GetEdenPath(EdenPath::DumpDir);
|
||||
|
||||
// Ensure DumpDir exists once. CreateDir is idempotent, so guarded to skip the syscall.
|
||||
static std::once_flag dump_dir_flag;
|
||||
std::call_once(dump_dir_flag, [&dump_dir]() {
|
||||
[[maybe_unused]] const bool created = CreateDir(dump_dir);
|
||||
});
|
||||
|
||||
const auto shader_path = dump_dir / fmt::format("{:016x}_{:016x}.spv",
|
||||
Settings::GetCurrentProgramID(), shader_hash);
|
||||
Common::FS::IOFile shader_file(shader_path, FileAccessMode::Write, FileType::BinaryFile);
|
||||
if (!shader_file.IsOpen()) {
|
||||
LOG_WARNING(Render_Vulkan, "[Shader Dump] Failed to open {}", shader_path.string());
|
||||
return;
|
||||
}
|
||||
static_cast<void>(shader_file.WriteSpan(spirv_code));
|
||||
}
|
||||
|
||||
void GPULogger::LogPipelineStateChange(const std::string& state_info) {
|
||||
@@ -657,10 +655,6 @@ void GPULogger::EnableVulkanCallTracking(bool enabled) {
|
||||
track_vulkan_calls = enabled;
|
||||
}
|
||||
|
||||
void GPULogger::EnableShaderDumps(bool enabled) {
|
||||
dump_shaders = enabled;
|
||||
}
|
||||
|
||||
void GPULogger::EnableMemoryTracking(bool enabled) {
|
||||
track_memory = enabled;
|
||||
}
|
||||
|
||||
@@ -87,8 +87,7 @@ public:
|
||||
void LogVulkanCall(const std::string& call_name, const std::string& params, int result);
|
||||
void LogMemoryAllocation(uintptr_t memory, u64 size, u32 memory_flags);
|
||||
void LogMemoryDeallocation(uintptr_t memory);
|
||||
void LogShaderCompilation(const std::string& shader_name, const std::string& shader_info,
|
||||
std::span<const u32> spirv_code = {});
|
||||
void LogShaderCompilation(const std::string& shader_name, const std::string& shader_info);
|
||||
void LogPipelineStateChange(const std::string& state_info);
|
||||
void LogDriverDebugInfo(const std::string& debug_info);
|
||||
|
||||
@@ -121,7 +120,6 @@ public:
|
||||
// Settings
|
||||
void SetLogLevel(LogLevel level);
|
||||
void EnableVulkanCallTracking(bool enabled);
|
||||
void EnableShaderDumps(bool enabled);
|
||||
void EnableMemoryTracking(bool enabled);
|
||||
void EnableDriverDebugInfo(bool enabled);
|
||||
void SetRingBufferSize(size_t entries);
|
||||
@@ -171,7 +169,6 @@ private:
|
||||
|
||||
// Feature flags
|
||||
bool track_vulkan_calls = true;
|
||||
bool dump_shaders = false;
|
||||
bool track_memory = false;
|
||||
bool capture_driver_debug = false;
|
||||
|
||||
@@ -179,15 +176,16 @@ private:
|
||||
std::set<std::string> used_extensions;
|
||||
mutable std::mutex extension_mutex;
|
||||
|
||||
// Shader dump directory (created on demand)
|
||||
bool shader_dump_dir_created = false;
|
||||
|
||||
// Stored state for crash dumps
|
||||
std::string stored_driver_debug_info;
|
||||
std::string stored_pipeline_state;
|
||||
mutable std::mutex state_mutex;
|
||||
};
|
||||
|
||||
[[nodiscard]] bool IsActive() noexcept;
|
||||
|
||||
void DumpSpirvShader(u64 shader_hash, std::span<const u32> spirv_code);
|
||||
|
||||
// Helper to get stage name from index
|
||||
inline const char* GetShaderStageName(size_t stage_index) {
|
||||
static constexpr std::array<const char*, 5> stage_names{
|
||||
|
||||
@@ -11,9 +11,8 @@
|
||||
#define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants {
|
||||
#define END_PUSH_CONSTANTS };
|
||||
#define UNIFORM(n)
|
||||
#define BINDING_SWIZZLE_BUFFER 0
|
||||
#define BINDING_INPUT_BUFFER 1
|
||||
#define BINDING_OUTPUT_IMAGE 2
|
||||
#define BINDING_INPUT_BUFFER 0
|
||||
#define BINDING_OUTPUT_IMAGE 1
|
||||
|
||||
#else // ^^^ Vulkan ^^^ // vvv OpenGL vvv
|
||||
|
||||
@@ -26,8 +25,7 @@
|
||||
#define BEGIN_PUSH_CONSTANTS
|
||||
#define END_PUSH_CONSTANTS
|
||||
#define UNIFORM(n) layout (location = n) uniform
|
||||
#define BINDING_SWIZZLE_BUFFER 0
|
||||
#define BINDING_INPUT_BUFFER 1
|
||||
#define BINDING_INPUT_BUFFER 0
|
||||
#define BINDING_OUTPUT_IMAGE 0
|
||||
|
||||
#endif
|
||||
@@ -43,10 +41,6 @@ UNIFORM(6) uint block_height;
|
||||
UNIFORM(7) uint block_height_mask;
|
||||
END_PUSH_CONSTANTS
|
||||
|
||||
layout(binding = BINDING_SWIZZLE_BUFFER, std430) readonly buffer SwizzleTable {
|
||||
uint swizzle_table[];
|
||||
};
|
||||
|
||||
#if HAS_EXTENDED_TYPES
|
||||
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU8 { uint8_t u8data[]; };
|
||||
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU16 { uint16_t u16data[]; };
|
||||
@@ -71,9 +65,19 @@ const uint GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT + GOB_SIZE_Z_SHI
|
||||
|
||||
const uvec2 SWIZZLE_MASK = uvec2(GOB_SIZE_X - 1, GOB_SIZE_Y - 1);
|
||||
|
||||
uint SwizzleTable(uint pos) {
|
||||
const uint t[8] = uint[](
|
||||
0x12100200, 0x13110301, 0x16140604, 0x17150705,
|
||||
0x1a180a08, 0x1b190b09, 0x1e1c0e0c, 0x1f1d0f0d
|
||||
);
|
||||
const uint i = pos >> 4;
|
||||
const uint h = (t[i / 4] >> ((i % 4) * 8)) & 0xff;
|
||||
return (h << 4) | (pos & 0xf);
|
||||
}
|
||||
|
||||
uint SwizzleOffset(uvec2 pos) {
|
||||
pos = pos & SWIZZLE_MASK;
|
||||
return swizzle_table[pos.y * 64 + pos.x];
|
||||
return SwizzleTable(pos.y * 64 + pos.x);
|
||||
}
|
||||
|
||||
uvec4 ReadTexel(uint offset) {
|
||||
|
||||
@@ -11,9 +11,8 @@
|
||||
#define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants {
|
||||
#define END_PUSH_CONSTANTS };
|
||||
#define UNIFORM(n)
|
||||
#define BINDING_SWIZZLE_BUFFER 0
|
||||
#define BINDING_INPUT_BUFFER 1
|
||||
#define BINDING_OUTPUT_IMAGE 2
|
||||
#define BINDING_INPUT_BUFFER 0
|
||||
#define BINDING_OUTPUT_IMAGE 1
|
||||
|
||||
#else // ^^^ Vulkan ^^^ // vvv OpenGL vvv
|
||||
|
||||
@@ -26,8 +25,7 @@
|
||||
#define BEGIN_PUSH_CONSTANTS
|
||||
#define END_PUSH_CONSTANTS
|
||||
#define UNIFORM(n) layout (location = n) uniform
|
||||
#define BINDING_SWIZZLE_BUFFER 0
|
||||
#define BINDING_INPUT_BUFFER 1
|
||||
#define BINDING_INPUT_BUFFER 0
|
||||
#define BINDING_OUTPUT_IMAGE 0
|
||||
|
||||
#endif
|
||||
@@ -45,10 +43,6 @@ UNIFORM(8) uint block_depth;
|
||||
UNIFORM(9) uint block_depth_mask;
|
||||
END_PUSH_CONSTANTS
|
||||
|
||||
layout(binding = BINDING_SWIZZLE_BUFFER, std430) readonly buffer SwizzleTable {
|
||||
uint swizzle_table[];
|
||||
};
|
||||
|
||||
#if HAS_EXTENDED_TYPES
|
||||
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU8 { uint8_t u8data[]; };
|
||||
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU16 { uint16_t u16data[]; };
|
||||
@@ -73,9 +67,19 @@ const uint GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT + GOB_SIZE_Z_SHI
|
||||
|
||||
const uvec2 SWIZZLE_MASK = uvec2(GOB_SIZE_X - 1, GOB_SIZE_Y - 1);
|
||||
|
||||
uint SwizzleTable(uint pos) {
|
||||
const uint t[8] = uint[](
|
||||
0x12100200, 0x13110301, 0x16140604, 0x17150705,
|
||||
0x1a180a08, 0x1b190b09, 0x1e1c0e0c, 0x1f1d0f0d
|
||||
);
|
||||
const uint i = pos >> 4;
|
||||
const uint h = (t[i / 4] >> ((i % 4) * 8)) & 0xff;
|
||||
return (h << 4) | (pos & 0xf);
|
||||
}
|
||||
|
||||
uint SwizzleOffset(uvec2 pos) {
|
||||
pos = pos & SWIZZLE_MASK;
|
||||
return swizzle_table[pos.y * 64 + pos.x];
|
||||
return SwizzleTable(pos.y * 64 + pos.x);
|
||||
}
|
||||
|
||||
uvec4 ReadTexel(uint offset) {
|
||||
|
||||
@@ -10,9 +10,8 @@
|
||||
#define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants {
|
||||
#define END_PUSH_CONSTANTS };
|
||||
#define UNIFORM(n)
|
||||
#define BINDING_SWIZZLE_BUFFER 0
|
||||
#define BINDING_INPUT_BUFFER 1
|
||||
#define BINDING_OUTPUT_BUFFER 2
|
||||
#define BINDING_INPUT_BUFFER 0
|
||||
#define BINDING_OUTPUT_BUFFER 1
|
||||
#else
|
||||
#extension GL_NV_gpu_shader5 : enable
|
||||
#ifdef GL_NV_gpu_shader5
|
||||
@@ -23,7 +22,6 @@
|
||||
#define BEGIN_PUSH_CONSTANTS
|
||||
#define END_PUSH_CONSTANTS
|
||||
#define UNIFORM(n) layout(location = n) uniform
|
||||
#define BINDING_SWIZZLE_BUFFER 0
|
||||
#define BINDING_INPUT_BUFFER 1
|
||||
#define BINDING_OUTPUT_BUFFER 0
|
||||
#endif
|
||||
@@ -66,13 +64,9 @@ END_PUSH_CONSTANTS
|
||||
#endif
|
||||
|
||||
// --- Buffers ---
|
||||
layout(binding = BINDING_SWIZZLE_BUFFER, std430) readonly buffer SwizzleTable {
|
||||
uint swizzle_table[];
|
||||
};
|
||||
|
||||
#if HAS_EXTENDED_TYPES
|
||||
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU8 { uint8_t u8data[]; };
|
||||
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU16 { uint16_t u16data[]; };
|
||||
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU8 { uint8_t u8data[]; };
|
||||
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU16 { uint16_t u16data[]; };
|
||||
#endif
|
||||
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU32 { uint u32data[]; };
|
||||
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU64 { uvec2 u64data[]; };
|
||||
@@ -96,10 +90,20 @@ const uint GOB_SIZE_Z_SHIFT = 0;
|
||||
const uint GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT + GOB_SIZE_Z_SHIFT;
|
||||
const uvec2 SWIZZLE_MASK = uvec2(GOB_SIZE_X - 1u, GOB_SIZE_Y - 1u);
|
||||
|
||||
uint SwizzleTable(uint pos) {
|
||||
const uint t[8] = uint[](
|
||||
0x12100200, 0x13110301, 0x16140604, 0x17150705,
|
||||
0x1a180a08, 0x1b190b09, 0x1e1c0e0c, 0x1f1d0f0d
|
||||
);
|
||||
const uint i = pos >> 4;
|
||||
const uint h = (t[i / 4] >> ((i % 4) * 8)) & 0xff;
|
||||
return (h << 4) | (pos & 0xf);
|
||||
}
|
||||
|
||||
// --- Helpers ---
|
||||
uint SwizzleOffset(uvec2 pos) {
|
||||
pos &= SWIZZLE_MASK;
|
||||
return swizzle_table[pos.y * 64u + pos.x];
|
||||
return SwizzleTable(pos.y * 64u + pos.x);
|
||||
}
|
||||
|
||||
uvec4 ReadTexel(uint offset) {
|
||||
|
||||
@@ -1328,22 +1328,14 @@ Macro::Opcode MacroJITx64Impl::GetOpCode() const {
|
||||
#endif
|
||||
|
||||
static void Dump(u64 hash, std::span<const u32> code, bool decompiled = false) {
|
||||
const auto base_dir{Common::FS::GetEdenPath(Common::FS::EdenPath::DumpDir)};
|
||||
const auto macro_dir{base_dir / "macros"};
|
||||
if (!Common::FS::CreateDir(base_dir) || !Common::FS::CreateDir(macro_dir)) {
|
||||
LOG_ERROR(Common_Filesystem, "Failed to create macro dump directories");
|
||||
const auto dump_dir{Common::FS::GetEdenPath(Common::FS::EdenPath::DumpDir)};
|
||||
if (!Common::FS::CreateDir(dump_dir)) {
|
||||
LOG_ERROR(Common_Filesystem, "Failed to create dump directory");
|
||||
return;
|
||||
}
|
||||
auto name{macro_dir / fmt::format("{:016x}.macro", hash)};
|
||||
|
||||
if (decompiled) {
|
||||
auto new_name{macro_dir / fmt::format("decompiled_{:016x}.macro", hash)};
|
||||
if (Common::FS::Exists(name)) {
|
||||
(void)Common::FS::RenameFile(name, new_name);
|
||||
return;
|
||||
}
|
||||
name = new_name;
|
||||
}
|
||||
const char* const variant_suffix = decompiled ? "jit" : "raw";
|
||||
const auto name{dump_dir / fmt::format("{:016x}_{:016x}_{}.macro",
|
||||
Settings::GetCurrentProgramID(), hash, variant_suffix)};
|
||||
|
||||
std::fstream macro_file(name, std::ios::out | std::ios::binary);
|
||||
if (!macro_file) {
|
||||
|
||||
@@ -245,16 +245,31 @@ ShaderCache::ShaderCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
|
||||
std::min<u32>(device.GetMaxUserClipDistances(), Maxwell::Regs::NumClipDistances),
|
||||
},
|
||||
host_info{
|
||||
.support_float64 = true,
|
||||
.support_float16 = false,
|
||||
.support_int64 = device.HasShaderInt64(),
|
||||
.needs_demote_reorder = device.IsAmd(),
|
||||
.support_snorm_render_buffer = false,
|
||||
.support_viewport_index_layer = device.HasVertexViewportLayer(),
|
||||
.min_ssbo_alignment = static_cast<u32>(device.GetShaderStorageBufferAlignment()),
|
||||
.support_geometry_shader_passthrough = device.HasGeometryShaderPassthrough(),
|
||||
.support_conditional_barrier = device.SupportsConditionalBarriers(),
|
||||
.min_ssbo_alignment = static_cast<u32>(device.GetShaderStorageBufferAlignment()),
|
||||
.max_per_stage_descriptor_sampled_images =
|
||||
Shader::HostTranslateInfo::DEFAULT_DESCRIPTOR_LIMIT,
|
||||
.max_per_stage_resources = Shader::HostTranslateInfo::DEFAULT_DESCRIPTOR_LIMIT,
|
||||
.max_descriptor_set_samplers = Shader::HostTranslateInfo::DEFAULT_DESCRIPTOR_LIMIT,
|
||||
.max_descriptor_set_uniform_buffers = Shader::HostTranslateInfo::DEFAULT_DESCRIPTOR_LIMIT,
|
||||
.max_descriptor_set_uniform_buffers_dynamic =
|
||||
Shader::HostTranslateInfo::DEFAULT_DESCRIPTOR_LIMIT,
|
||||
.max_descriptor_set_storage_buffers = Shader::HostTranslateInfo::DEFAULT_DESCRIPTOR_LIMIT,
|
||||
.max_descriptor_set_storage_buffers_dynamic =
|
||||
Shader::HostTranslateInfo::DEFAULT_DESCRIPTOR_LIMIT,
|
||||
.max_descriptor_set_sampled_images = Shader::HostTranslateInfo::DEFAULT_DESCRIPTOR_LIMIT,
|
||||
.max_descriptor_set_storage_images = Shader::HostTranslateInfo::DEFAULT_DESCRIPTOR_LIMIT,
|
||||
.max_descriptor_set_input_attachements =
|
||||
Shader::HostTranslateInfo::DEFAULT_DESCRIPTOR_LIMIT,
|
||||
.support_float64 = true,
|
||||
.support_float16 = false,
|
||||
.support_int64 = device.HasShaderInt64(),
|
||||
.needs_demote_reorder = device.IsAmd(),
|
||||
.support_snorm_render_buffer = false,
|
||||
.support_viewport_index_layer = device.HasVertexViewportLayer(),
|
||||
.support_geometry_shader_passthrough = device.HasGeometryShaderPassthrough(),
|
||||
.support_conditional_barrier = device.SupportsConditionalBarriers(),
|
||||
} {
|
||||
host_info.ApplyDescriptorLimitPolicy();
|
||||
if (use_asynchronous_shaders) {
|
||||
workers = CreateWorkers();
|
||||
}
|
||||
@@ -481,7 +496,7 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline(
|
||||
const u32 cfg_offset = u32(env.StartAddress() + sizeof(Shader::ProgramHeader));
|
||||
Shader::Maxwell::Flow::CFG cfg(env, pools.flow_block, cfg_offset, index == 0);
|
||||
|
||||
if (Settings::values.dump_shaders) {
|
||||
if (Settings::values.dump_guest_shaders) {
|
||||
env.Dump(hash, key.unique_hashes[index]);
|
||||
}
|
||||
|
||||
@@ -578,7 +593,7 @@ std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline(
|
||||
|
||||
Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()};
|
||||
|
||||
if (Settings::values.dump_shaders) {
|
||||
if (Settings::values.dump_guest_shaders) {
|
||||
env.Dump(hash, key.unique_hash);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -56,10 +59,8 @@ UtilShaders::UtilShaders(ProgramManager& program_manager_)
|
||||
copy_bc4_program(MakeProgram(OPENGL_COPY_BC4_COMP)),
|
||||
convert_s8d24_program(MakeProgram(OPENGL_CONVERT_S8D24_COMP)),
|
||||
convert_ms_to_nonms_program(MakeProgram(CONVERT_MSAA_TO_NON_MSAA_COMP)),
|
||||
convert_nonms_to_ms_program(MakeProgram(CONVERT_NON_MSAA_TO_MSAA_COMP)) {
|
||||
const auto swizzle_table = Tegra::Texture::MakeSwizzleTable();
|
||||
swizzle_table_buffer.Create();
|
||||
glNamedBufferStorage(swizzle_table_buffer.handle, sizeof(swizzle_table), &swizzle_table, 0);
|
||||
convert_nonms_to_ms_program(MakeProgram(CONVERT_NON_MSAA_TO_MSAA_COMP))
|
||||
{
|
||||
}
|
||||
|
||||
UtilShaders::~UtilShaders() = default;
|
||||
@@ -116,13 +117,11 @@ void UtilShaders::ASTCDecode(Image& image, const StagingBufferMap& map,
|
||||
void UtilShaders::BlockLinearUpload2D(Image& image, const StagingBufferMap& map,
|
||||
std::span<const SwizzleParameters> swizzles) {
|
||||
static constexpr Extent3D WORKGROUP_SIZE{32, 32, 1};
|
||||
static constexpr GLuint BINDING_SWIZZLE_BUFFER = 0;
|
||||
static constexpr GLuint BINDING_INPUT_BUFFER = 1;
|
||||
static constexpr GLuint BINDING_INPUT_BUFFER = 0;
|
||||
static constexpr GLuint BINDING_OUTPUT_IMAGE = 0;
|
||||
|
||||
program_manager.BindComputeProgram(block_linear_unswizzle_2d_program.handle);
|
||||
glFlushMappedNamedBufferRange(map.buffer, map.offset, image.guest_size_bytes);
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, BINDING_SWIZZLE_BUFFER, swizzle_table_buffer.handle);
|
||||
|
||||
const GLenum store_format = StoreFormat(BytesPerBlock(image.info.format));
|
||||
for (const SwizzleParameters& swizzle : swizzles) {
|
||||
@@ -153,14 +152,11 @@ void UtilShaders::BlockLinearUpload2D(Image& image, const StagingBufferMap& map,
|
||||
void UtilShaders::BlockLinearUpload3D(Image& image, const StagingBufferMap& map,
|
||||
std::span<const SwizzleParameters> swizzles) {
|
||||
static constexpr Extent3D WORKGROUP_SIZE{16, 8, 8};
|
||||
|
||||
static constexpr GLuint BINDING_SWIZZLE_BUFFER = 0;
|
||||
static constexpr GLuint BINDING_INPUT_BUFFER = 1;
|
||||
static constexpr GLuint BINDING_INPUT_BUFFER = 0;
|
||||
static constexpr GLuint BINDING_OUTPUT_IMAGE = 0;
|
||||
|
||||
glFlushMappedNamedBufferRange(map.buffer, map.offset, image.guest_size_bytes);
|
||||
program_manager.BindComputeProgram(block_linear_unswizzle_3d_program.handle);
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, BINDING_SWIZZLE_BUFFER, swizzle_table_buffer.handle);
|
||||
|
||||
const GLenum store_format = StoreFormat(BytesPerBlock(image.info.format));
|
||||
for (const SwizzleParameters& swizzle : swizzles) {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -45,9 +48,6 @@ public:
|
||||
|
||||
private:
|
||||
ProgramManager& program_manager;
|
||||
|
||||
OGLBuffer swizzle_table_buffer;
|
||||
|
||||
OGLProgram astc_decoder_program;
|
||||
OGLProgram block_linear_unswizzle_2d_program;
|
||||
OGLProgram block_linear_unswizzle_3d_program;
|
||||
|
||||
@@ -22,6 +22,15 @@ namespace Vulkan {
|
||||
|
||||
using Shader::Backend::SPIRV::NUM_TEXTURE_AND_IMAGE_SCALING_WORDS;
|
||||
|
||||
[[nodiscard]] inline u32 NumDescriptorEntries(const Shader::Info& info) {
|
||||
return Shader::NumDescriptors(info.constant_buffer_descriptors) +
|
||||
Shader::NumDescriptors(info.storage_buffers_descriptors) +
|
||||
Shader::NumDescriptors(info.texture_buffer_descriptors) +
|
||||
Shader::NumDescriptors(info.image_buffer_descriptors) +
|
||||
Shader::NumDescriptors(info.texture_descriptors) +
|
||||
Shader::NumDescriptors(info.image_descriptors);
|
||||
}
|
||||
|
||||
class DescriptorLayoutBuilder {
|
||||
public:
|
||||
DescriptorLayoutBuilder(const Device& device_) : device{&device_} {}
|
||||
@@ -194,7 +203,11 @@ inline void PushImageDescriptors(TextureCache& texture_cache,
|
||||
const VideoCommon::ImageViewId image_view_id{(views++)->id};
|
||||
const VideoCommon::SamplerId sampler_id{*(samplers++)};
|
||||
ImageView& image_view{texture_cache.GetImageView(image_view_id)};
|
||||
const VkImageView vk_image_view{image_view.Handle(desc.type)};
|
||||
VkImageView vk_image_view{image_view.Handle(desc.type)};
|
||||
if (vk_image_view == VK_NULL_HANDLE) {
|
||||
const VkImageView null_image_view{texture_cache.GetImageView(VideoCommon::NULL_IMAGE_VIEW_ID).Handle(desc.type)};
|
||||
if (null_image_view != VK_NULL_HANDLE) vk_image_view = null_image_view;
|
||||
}
|
||||
const Sampler& sampler{texture_cache.GetSampler(sampler_id)};
|
||||
const bool use_fallback_sampler{sampler.HasAddedAnisotropy() &&
|
||||
!image_view.SupportsAnisotropy()};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user