mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-10-06 22:10:49 +00:00
Merge pull request '[arm, vulkan] 5th Vulkan Global Maintenance + NCE maintenance' (#1) from cleanup-vk into master
Reviewed-on: https://git.eden-emu.dev/CamilleLaVey/Eden/pulls/1
This commit is contained in:
-2
@@ -25,7 +25,6 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
|
||||
RENDERER_USE_DISK_SHADER_CACHE("use_disk_shader_cache"),
|
||||
RENDERER_FORCE_MAX_CLOCK("force_max_clock"),
|
||||
RENDERER_ASYNCHRONOUS_GPU_EMULATION("use_asynchronous_gpu_emulation"),
|
||||
RENDERER_ASYNC_PRESENTATION("async_presentation"),
|
||||
RENDERER_ASYNCHRONOUS_SHADERS("use_asynchronous_shaders"),
|
||||
RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"),
|
||||
ENABLE_BUFFER_HISTORY("enable_buffer_history"),
|
||||
@@ -39,7 +38,6 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
|
||||
RENDERER_SAMPLE_SHADING("sample_shading"),
|
||||
RENDERER_FRAME_GEN("frame_gen"),
|
||||
RENDERER_FRAME_GEN_FLOW_SCALE_AUTO("frame_gen_flow_scale_auto"),
|
||||
GPU_UNSWIZZLE_ENABLED("gpu_unswizzle_enabled"),
|
||||
PICTURE_IN_PICTURE("picture_in_picture"),
|
||||
USE_CUSTOM_RTC("custom_rtc_enabled"),
|
||||
BLACK_BACKGROUNDS("black_backgrounds"),
|
||||
|
||||
@@ -51,9 +51,6 @@ enum class IntSetting(override val key: String) : AbstractIntSetting {
|
||||
FAST_CPU_TIME("fast_cpu_time"),
|
||||
CPU_TICKS("cpu_ticks"),
|
||||
FAST_GPU_TIME("fast_gpu_time"),
|
||||
GPU_UNSWIZZLE_TEXTURE_SIZE("gpu_unswizzle_texture_size"),
|
||||
GPU_UNSWIZZLE_STREAM_SIZE("gpu_unswizzle_stream_size"),
|
||||
GPU_UNSWIZZLE_CHUNK_SIZE("gpu_unswizzle_chunk_size"),
|
||||
BAT_TEMPERATURE_UNIT("bat_temperature_unit"),
|
||||
CABINET_APPLET("cabinet_applet_mode"),
|
||||
CONTROLLER_APPLET("controller_applet_mode"),
|
||||
|
||||
-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()
|
||||
}
|
||||
-48
@@ -141,14 +141,12 @@ abstract class SettingsItem(
|
||||
const val TYPE_SPINBOX = 12
|
||||
const val TYPE_LAUNCHABLE = 13
|
||||
const val TYPE_PATH = 14
|
||||
const val TYPE_GPU_UNSWIZZLE = 15
|
||||
const val TYPE_FX_TOOLBAR = 16
|
||||
const val TYPE_FX_PRESET = 17
|
||||
const val TYPE_FX_SHADER = 18
|
||||
const val TYPE_FX_BUTTON = 19
|
||||
|
||||
const val FASTMEM_COMBINED = "fastmem_combined"
|
||||
const val GPU_UNSWIZZLE_COMBINED = "gpu_unswizzle_combined"
|
||||
|
||||
val emptySetting = object : AbstractSetting {
|
||||
override val key: String = ""
|
||||
@@ -753,13 +751,6 @@ abstract class SettingsItem(
|
||||
descriptionId = R.string.renderer_asynchronous_gpu_emulation_description
|
||||
)
|
||||
)
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.RENDERER_ASYNC_PRESENTATION,
|
||||
titleId = R.string.renderer_async_presentation,
|
||||
descriptionId = R.string.renderer_async_presentation_description
|
||||
)
|
||||
)
|
||||
put(
|
||||
SingleChoiceSetting(
|
||||
IntSetting.DMA_ACCURACY,
|
||||
@@ -794,45 +785,6 @@ abstract class SettingsItem(
|
||||
valuesId = R.array.gpuValues
|
||||
)
|
||||
)
|
||||
put(
|
||||
SingleChoiceSetting(
|
||||
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
|
||||
)
|
||||
)
|
||||
put(
|
||||
SingleChoiceSetting(
|
||||
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
|
||||
)
|
||||
)
|
||||
put(
|
||||
SingleChoiceSetting(
|
||||
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
|
||||
)
|
||||
)
|
||||
put(
|
||||
SingleChoiceSetting(
|
||||
IntSetting.FAST_CPU_TIME,
|
||||
|
||||
-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
@@ -106,10 +106,6 @@ class SettingsAdapter(
|
||||
PathViewHolder(ListItemSettingBinding.inflate(inflater), this)
|
||||
}
|
||||
|
||||
SettingsItem.TYPE_GPU_UNSWIZZLE -> {
|
||||
GpuUnswizzleViewHolder(ListItemSettingBinding.inflate(inflater), this)
|
||||
}
|
||||
|
||||
SettingsItem.TYPE_FX_TOOLBAR -> {
|
||||
FxToolbarViewHolder(
|
||||
ListItemSettingFxToolbarBinding.inflate(inflater, parent, false),
|
||||
@@ -511,14 +507,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
|
||||
|
||||
-2
@@ -560,8 +560,6 @@ class SettingsFragmentPresenter(
|
||||
add(BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key)
|
||||
add(IntSetting.ANDROID_PIPELINE_WORKERS.key)
|
||||
add(BooleanSetting.RENDERER_ASYNCHRONOUS_GPU_EMULATION.key)
|
||||
add(BooleanSetting.RENDERER_ASYNC_PRESENTATION.key)
|
||||
add(SettingsItem.GPU_UNSWIZZLE_COMBINED)
|
||||
|
||||
add(HeaderSetting(R.string.extensions))
|
||||
|
||||
|
||||
-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
|
||||
}
|
||||
}
|
||||
@@ -738,6 +738,35 @@ const char* fallback_cpu_detection() {
|
||||
return s_result.c_str();
|
||||
}
|
||||
|
||||
#ifdef ARCHITECTURE_arm64
|
||||
struct SystemDriverInfo {
|
||||
VkPhysicalDeviceProperties properties{};
|
||||
VkPhysicalDeviceDriverProperties driver{};
|
||||
};
|
||||
|
||||
SystemDriverInfo QuerySystemDriverInfo(JNIEnv* env, jstring j_hook_lib_dir) {
|
||||
const std::string hook_lib_dir = Common::Android::GetJString(env, j_hook_lib_dir);
|
||||
const Common::DynamicLibrary library{adrenotools_open_libvulkan(
|
||||
RTLD_NOW, 0, nullptr, hook_lib_dir.c_str(), nullptr, nullptr, nullptr, nullptr)};
|
||||
Vulkan::vk::InstanceDispatch dld;
|
||||
const Vulkan::vk::Instance instance = Vulkan::CreateInstance(library, dld, VK_API_VERSION_1_1);
|
||||
const std::vector<VkPhysicalDevice> devices = instance.EnumeratePhysicalDevices();
|
||||
if (devices.empty()) {
|
||||
throw Vulkan::vk::Exception(VK_ERROR_INITIALIZATION_FAILED);
|
||||
}
|
||||
SystemDriverInfo info{};
|
||||
info.driver.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES;
|
||||
VkPhysicalDeviceProperties2 properties2{
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
|
||||
.pNext = &info.driver,
|
||||
.properties = {},
|
||||
};
|
||||
Vulkan::vk::PhysicalDevice(devices[0], dld).GetProperties2(properties2);
|
||||
info.properties = properties2.properties;
|
||||
return info;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C" {
|
||||
@@ -857,34 +886,18 @@ jboolean JNICALL Java_org_yuzu_yuzu_1emu_utils_GpuDriverHelper_supportsCustomDri
|
||||
|
||||
jobjectArray Java_org_yuzu_yuzu_1emu_utils_GpuDriverHelper_getSystemDriverInfo(
|
||||
JNIEnv* env, jobject j_obj, jobject j_surf, jstring j_hook_lib_dir) {
|
||||
std::string version_string{"1.1.0"};
|
||||
std::string driver_name{"generic"};
|
||||
#ifdef ARCHITECTURE_arm64
|
||||
const char* file_redirect_dir_{};
|
||||
int featureFlags{};
|
||||
std::string hook_lib_dir = Common::Android::GetJString(env, j_hook_lib_dir);
|
||||
auto handle = adrenotools_open_libvulkan(RTLD_NOW, featureFlags, nullptr, hook_lib_dir.c_str(),
|
||||
nullptr, nullptr, file_redirect_dir_, nullptr);
|
||||
auto driver_library = std::make_shared<Common::DynamicLibrary>(handle);
|
||||
InputCommon::InputSubsystem input_subsystem;
|
||||
auto window =
|
||||
std::make_unique<EmuWindow_Android>(ANativeWindow_fromSurface(env, j_surf), driver_library);
|
||||
|
||||
Vulkan::vk::InstanceDispatch dld;
|
||||
Vulkan::vk::Instance vk_instance = Vulkan::CreateInstance(
|
||||
*driver_library, dld, VK_API_VERSION_1_1, Core::Frontend::WindowSystemType::Android);
|
||||
|
||||
auto surface = Vulkan::CreateSurface(vk_instance, window->GetWindowInfo());
|
||||
|
||||
auto device = Vulkan::CreateDevice(vk_instance, dld, *surface);
|
||||
|
||||
auto driver_version = device.GetDriverVersion();
|
||||
auto version_string =
|
||||
fmt::format("{}.{}.{}", VK_API_VERSION_MAJOR(driver_version),
|
||||
VK_API_VERSION_MINOR(driver_version), VK_API_VERSION_PATCH(driver_version));
|
||||
auto driver_name = device.GetDriverName();
|
||||
#else
|
||||
auto driver_version = "1.0.0";
|
||||
auto version_string = "1.1.0"; //Assume lowest Vulkan level
|
||||
auto driver_name = "generic";
|
||||
try {
|
||||
const SystemDriverInfo info = QuerySystemDriverInfo(env, j_hook_lib_dir);
|
||||
const u32 driver_version = info.properties.driverVersion;
|
||||
version_string =
|
||||
fmt::format("{}.{}.{}", VK_API_VERSION_MAJOR(driver_version),
|
||||
VK_API_VERSION_MINOR(driver_version), VK_API_VERSION_PATCH(driver_version));
|
||||
driver_name = Vulkan::vk::GetDriverName(info.driver);
|
||||
} catch (...) {
|
||||
}
|
||||
#endif
|
||||
jobjectArray j_driver_info = env->NewObjectArray(2, Common::Android::GetStringClass(), Common::Android::ToJString(env, version_string));
|
||||
env->SetObjectArrayElement(j_driver_info, 1, Common::Android::ToJString(env, driver_name));
|
||||
@@ -893,32 +906,12 @@ jobjectArray Java_org_yuzu_yuzu_1emu_utils_GpuDriverHelper_getSystemDriverInfo(
|
||||
|
||||
jstring Java_org_yuzu_yuzu_1emu_utils_GpuDriverHelper_getGpuModel(JNIEnv *env, jobject j_obj, jobject j_surf, jstring j_hook_lib_dir) {
|
||||
#ifdef ARCHITECTURE_arm64
|
||||
const char* file_redirect_dir_{};
|
||||
int featureFlags{};
|
||||
std::string hook_lib_dir = Common::Android::GetJString(env, j_hook_lib_dir);
|
||||
auto handle = adrenotools_open_libvulkan(RTLD_NOW, featureFlags, nullptr, hook_lib_dir.c_str(),
|
||||
nullptr, nullptr, file_redirect_dir_, nullptr);
|
||||
auto driver_library = std::make_shared<Common::DynamicLibrary>(handle);
|
||||
InputCommon::InputSubsystem input_subsystem;
|
||||
auto window =
|
||||
std::make_unique<EmuWindow_Android>(ANativeWindow_fromSurface(env, j_surf), driver_library);
|
||||
|
||||
Vulkan::vk::InstanceDispatch dld;
|
||||
Vulkan::vk::Instance vk_instance = Vulkan::CreateInstance(
|
||||
*driver_library, dld, VK_API_VERSION_1_1, Core::Frontend::WindowSystemType::Android);
|
||||
|
||||
auto surface = Vulkan::CreateSurface(vk_instance, window->GetWindowInfo());
|
||||
|
||||
auto device = Vulkan::CreateDevice(vk_instance, dld, *surface);
|
||||
|
||||
const std::string model_name{device.GetModelName()};
|
||||
|
||||
window.release();
|
||||
|
||||
return Common::Android::ToJString(env, model_name);
|
||||
#else
|
||||
return Common::Android::ToJString(env, "no-info");
|
||||
try {
|
||||
return Common::Android::ToJString(env, QuerySystemDriverInfo(env, j_hook_lib_dir).properties.deviceName);
|
||||
} catch (...) {
|
||||
}
|
||||
#endif
|
||||
return Common::Android::ToJString(env, "no-info");
|
||||
}
|
||||
|
||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadKeys(JNIEnv* env, jclass clazz) {
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:scrollbars="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="24dp">
|
||||
|
||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
||||
android:id="@+id/switch_enable"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:text="@string/gpu_unswizzle_enable" />
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/layout_texture_size"
|
||||
style="@style/Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:hint="@string/gpu_unswizzle_texture_size">
|
||||
|
||||
<com.google.android.material.textfield.MaterialAutoCompleteTextView
|
||||
android:id="@+id/dropdown_texture_size"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="none" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/layout_stream_size"
|
||||
style="@style/Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:hint="@string/gpu_unswizzle_stream_size">
|
||||
|
||||
<com.google.android.material.textfield.MaterialAutoCompleteTextView
|
||||
android:id="@+id/dropdown_stream_size"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="none" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/layout_chunk_size"
|
||||
style="@style/Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/gpu_unswizzle_chunk_size">
|
||||
|
||||
<com.google.android.material.textfield.MaterialAutoCompleteTextView
|
||||
android:id="@+id/dropdown_chunk_size"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="none" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="end"
|
||||
android:spacing="8dp">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_default"
|
||||
style="@style/Widget.Material3.Button.TextButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/gpu_unswizzle_default_button" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_cancel"
|
||||
style="@style/Widget.Material3.Button.TextButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@android:string/cancel" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_ok"
|
||||
style="@style/Widget.Material3.Button.TextButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@android:string/ok" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
@@ -574,8 +574,6 @@
|
||||
<string name="renderer_force_max_clock_description">يجبر وحدة معالجة الرسومات على العمل بأقصى سرعة ممكنة (سيظل يتم تطبيق القيود الحرارية).</string>
|
||||
<string name="renderer_asynchronous_gpu_emulation">محاكاة غير متزامنة لوحدة معالجة الرسومات</string>
|
||||
<string name="renderer_asynchronous_gpu_emulation_description">يمكن لهذه الحيلة أن تزيد الأداء عن طريق تشغيل محاكاة وحدة معالجة الرسومات بشكل غير متزامن على حساب مشاكل الرسومات وزيادة معدلات الأعطال بسبب العمليات المتعلقة بالتوقيت.</string>
|
||||
<string name="renderer_async_presentation">عرض غير متزامن</string>
|
||||
<string name="renderer_async_presentation_description">يمكن لهذه الحيلة أن تزيد من الأداء عن طريق نقل عملية العرض إلى خيط معالجة منفصل على حساب مشاكل الرسوميات.</string>
|
||||
<string name="renderer_reactive_flushing">استخدم التنظيف التفاعلي</string>
|
||||
<string name="renderer_reactive_flushing_description">يحسن دقة العرض في بعض الألعاب على حساب الأداء.</string>
|
||||
<string name="enable_buffer_history">تمكين سجل التخزين المؤقت</string>
|
||||
@@ -599,17 +597,6 @@
|
||||
<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>
|
||||
<string name="gpu_unswizzle_stream_size_description">يحدد هذا الخيار حد البيانات لكل إطار لمعالجة الصور الكبيرة. القيم الأعلى تُسرّع تحميل الصور على حساب زيادة زمن استجابة الإطارات؛ أما القيم الأقل فتُقلل من الحمل الزائد على وحدة معالجة الرسومات، ولكنها قد تتسبب في ظهور الصور بشكل مفاجئ.</string>
|
||||
<string name="gpu_unswizzle_chunk_size">حجم كتلة إلغاء ترتيب بيانات وحدة معالجة الرسومات</string>
|
||||
<string name="gpu_unswizzle_chunk_size_description">يُحدد هذا الخيار عدد شرائح العمق التي تتم معالجتها لكل دفعة من الصور ثلاثية الأبعاد (3D). زيادة هذا العدد تُحسّن كفاءة الإنتاجية على وحدات معالجة الرسومات القوية، ولكنها قد تُسبب تقطعًا أو انقطاعًا في عمل برنامج التشغيل على الأجهزة ذات المواصفات الأقل قوة.</string>
|
||||
<string name="gpu_unswizzle_default_button">افتراضي</string>
|
||||
|
||||
|
||||
<string name="extensions">إضافات</string>
|
||||
@@ -1050,25 +1037,10 @@
|
||||
<string name="fast_gpu_high">كسر السرعة</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>
|
||||
|
||||
@@ -508,7 +508,6 @@ Wird der Handheld-Modus verwendet, verringert es die Auflösung und erhöht die
|
||||
<string name="skip_cpu_inner_invalidation_description">Überspringt bestimmte Cache-Invalidierungen auf CPU-Seite während Speicherupdates, reduziert die CPU-Auslastung und verbessert die Leistung. Kann in einigen Spielen zu Fehlern oder Abstürzen führen.</string>
|
||||
<string name="renderer_asynchronous_shaders">Asynchrone Shader</string>
|
||||
<string name="renderer_asynchronous_shaders_description">Kompiliert Shader asynchron. Dies kann Ruckler reduzieren, aber auch Grafikfehler verursachen.</string>
|
||||
<string name="gpu_unswizzle_default_button">Standard</string>
|
||||
|
||||
|
||||
<string name="extensions">Erweiterungen</string>
|
||||
@@ -918,25 +917,10 @@ Wirklich fortfahren?</string>
|
||||
<string name="fast_gpu_high">Übertaktung</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>
|
||||
|
||||
@@ -517,8 +517,6 @@
|
||||
<string name="renderer_force_max_clock_description">Fuerza a la GPU a ejecutarse a la velocidad máxima de reloj posible (se seguirán aplicando restricciones térmicas).</string>
|
||||
<string name="renderer_asynchronous_gpu_emulation">Emulación de GPU asíncrona</string>
|
||||
<string name="renderer_asynchronous_gpu_emulation_description">Este hack puede aumentar el rendimiento ejecutando la emulación de la GPU de forma asíncrona, a costa de problemas gráficos y un aumento en la tasa de fallos debido a operaciones relacionadas con la sincronización.</string>
|
||||
<string name="renderer_async_presentation">Presentación asíncrona</string>
|
||||
<string name="renderer_async_presentation_description">Este hack puede aumentar el rendimiento al mover la presentación a un hilo independiente de la CPU a costa de problemas gráficos.</string>
|
||||
<string name="renderer_reactive_flushing">Usar limpieza reactiva</string>
|
||||
<string name="renderer_reactive_flushing_description">Mejora la precisión de renderizado en algunos juegos, pero reduce el rendimiento.</string>
|
||||
<string name="enable_buffer_history">Activar el historial del búfer</string>
|
||||
@@ -541,17 +539,6 @@
|
||||
<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>
|
||||
<string name="gpu_unswizzle_stream_size_description">Establece el límite de datos por fotograma para desentrelazar texturas grandes. Los valores altos aceleran la carga de texturas, a coste de una mayor latencia por fotograma; los valores bajos reducen la carga de la GPU, pero pueden causar parpadeos visibles en las texturas.</string>
|
||||
<string name="gpu_unswizzle_chunk_size">Tamaño del trozo de desentrelazado de la GPU</string>
|
||||
<string name="gpu_unswizzle_chunk_size_description">Determina la cantidad de cortes de profundidad procesados en un solo envío de texturas 3D. Aumentar este valor puede mejorar el rendimiento en una GPU de gama alta, pero puede causar tirones y problemas en los tiempos de respuesta en hardware más modesto.</string>
|
||||
<string name="gpu_unswizzle_default_button">Por defecto</string>
|
||||
|
||||
|
||||
<string name="extensions">Extensiones</string>
|
||||
@@ -988,25 +975,10 @@
|
||||
<string name="fast_gpu_high">Overclock</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>
|
||||
|
||||
@@ -482,8 +482,6 @@
|
||||
<string name="emulate_bgr565">Emuler BGR565</string>
|
||||
<string name="renderer_asynchronous_shaders">Utiliser les shaders asynchrones</string>
|
||||
<string name="renderer_asynchronous_shaders_description">Compile les shaders de manière asynchrone. Cela peut réduire les saccades mais peut aussi provoquer des problèmes graphiques.</string>
|
||||
<string name="gpu_unswizzle_disabled">Désactivé</string>
|
||||
<string name="gpu_unswizzle_default_button">Par défaut</string>
|
||||
|
||||
|
||||
<string name="extensions">Extensions</string>
|
||||
@@ -884,25 +882,10 @@
|
||||
<string name="memory_8gb">8 Go (Dangereux)</string>
|
||||
|
||||
<!-- GPU swizzle texture size -->
|
||||
<string name="gpu_texturesizeswizzle_verysmall">Très petit (16 Mo)</string>
|
||||
<string name="gpu_texturesizeswizzle_small">Petit (32 Mo)</string>
|
||||
<string name="gpu_texturesizeswizzle_normal">Normal (128 Mo)</string>
|
||||
<string name="gpu_texturesizeswizzle_large">Large (256 Mo)</string>
|
||||
<string name="gpu_texturesizeswizzle_verylarge">Très large (512 Mo)</string>
|
||||
|
||||
<!-- GPU swizzle streams -->
|
||||
<string name="gpu_swizzle_verylow">Très faible (4 Mo)</string>
|
||||
<string name="gpu_swizzle_low">Faible (8 Mo)</string>
|
||||
<string name="gpu_swizzle_normal">Normal (16 Mo)</string>
|
||||
<string name="gpu_swizzle_medium">Moyen (32 Mo)</string>
|
||||
<string name="gpu_swizzle_high">Élevé (64 Mo)</string>
|
||||
|
||||
<!-- GPU swizzle chunks -->
|
||||
<string name="gpu_swizzlechunk_verylow">Très faible (32)</string>
|
||||
<string name="gpu_swizzlechunk_low">Faible (64)</string>
|
||||
<string name="gpu_swizzlechunk_normal">Normal (128)</string>
|
||||
<string name="gpu_swizzlechunk_medium">Moyen (256)</string>
|
||||
<string name="gpu_swizzlechunk_high">Élevé (512)</string>
|
||||
|
||||
<!-- Temperature Units -->
|
||||
<string name="temperature_celsius">Celsius</string>
|
||||
|
||||
@@ -554,8 +554,6 @@
|
||||
<string name="renderer_force_max_clock_description">Заставляет ГПУ работать на максимально возможных тактовых частотах (тепловые ограничения все равно будут применяться).</string>
|
||||
<string name="renderer_asynchronous_gpu_emulation">Асинхронная эмуляция ГПУ</string>
|
||||
<string name="renderer_asynchronous_gpu_emulation_description">Может повысить производительность за счёт асинхронного запуска эмуляции ГПУ, но ценой появления графических ошибок и увеличения частоты вылетов из-за операций, зависящих от синхронизации.</string>
|
||||
<string name="renderer_async_presentation">Асинхронная презентация</string>
|
||||
<string name="renderer_async_presentation_description">Может повысить производительность за счёт перемещения вывода кадров в отдельный поток ЦП, но ценой возникновения графических проблем.</string>
|
||||
<string name="renderer_reactive_flushing">Реактивная очистка</string>
|
||||
<string name="renderer_reactive_flushing_description">Повышение точности рендеринга в некоторых играх за счет снижения производительности.</string>
|
||||
<string name="enable_buffer_history">Включить историю буфера</string>
|
||||
@@ -579,17 +577,6 @@
|
||||
<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>
|
||||
<string name="gpu_unswizzle_stream_size_description">Задает лимит данных на кадр для преобразования крупных текстур (unswizzle). Высокие значения ускоряют загрузку текстур ценой увеличения задержки кадра; низкие значения снижают нагрузку на ГПУ, но могут вызывать заметную постепенную подгрузку текстур.</string>
|
||||
<string name="gpu_unswizzle_chunk_size">Размер блока Unswizzle</string>
|
||||
<string name="gpu_unswizzle_chunk_size_description">Задает количество слоёв глубины, обрабатываемых за одну пачку для 3D-текстур. Увеличение этого значения улучшает пропускную способность на мощных ГПУ, но может вызывать подтормаживания или таймауты драйвера на слабом железе.</string>
|
||||
<string name="gpu_unswizzle_default_button">По умолчанию</string>
|
||||
|
||||
|
||||
<string name="extensions">Расширения</string>
|
||||
@@ -1030,25 +1017,10 @@
|
||||
<string name="fast_gpu_high">Разгон</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>
|
||||
|
||||
@@ -481,8 +481,6 @@
|
||||
<string name="renderer_force_max_clock_description">Змушує GPU працювати на максимальній тактовій частоті.</string>
|
||||
<string name="renderer_asynchronous_gpu_emulation">Асинхронна емуляція ГП</string>
|
||||
<string name="renderer_asynchronous_gpu_emulation_description">Це обхідне рішення може покращити продуктивність завдяки асинхронному виконанню емуляції ГП, але спричинить проблеми з графікою та збільшить частоту збоїв чутливих до таймінгів операцій.</string>
|
||||
<string name="renderer_async_presentation">Асинхронне подання</string>
|
||||
<string name="renderer_async_presentation_description">Це обхідне рішення може покращити продуктивність завдяки переміщенню подання на окремий потік ЦП, але спричинить проблеми з графікою.</string>
|
||||
<string name="renderer_reactive_flushing">Реактивне очищення</string>
|
||||
<string name="renderer_reactive_flushing_description">Покращує точність рендерингу в деяких іграх.</string>
|
||||
<string name="enable_buffer_history">Увімкнути історію буфера</string>
|
||||
@@ -502,17 +500,6 @@
|
||||
<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>
|
||||
<string name="gpu_unswizzle_stream_size_description">Встановлює обмеження даних на кадр для розпакування великих текстур. Вищі значення пришвидшують завантаження текстур за рахунок більших кадрових затримок; менші значення зменшують перевантаження ГП але може спричинити помітні появи текстур.</string>
|
||||
<string name="gpu_unswizzle_chunk_size">Розмір блоків розпакування за допомогою ГП</string>
|
||||
<string name="gpu_unswizzle_chunk_size_description">Визначає кількість зрізів глибини, оброблених за партію 3D-текстур. Збільшення здатне покращити пропускну здатність на потужних ГП, але може призвести до затримок або затримок драйвера зі слабшим устаткуванням.</string>
|
||||
<string name="gpu_unswizzle_default_button">Стандартно</string>
|
||||
|
||||
|
||||
<string name="extensions">Розширення</string>
|
||||
@@ -929,25 +916,10 @@
|
||||
<string name="memory_8gb">8 ГБ (Небезпечно)</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>
|
||||
|
||||
@@ -535,8 +535,6 @@
|
||||
<string name="renderer_force_max_clock_description">强制 GPU 以最大时钟运行 (温控依然生效)。</string>
|
||||
<string name="renderer_asynchronous_gpu_emulation">GPU 异步模拟</string>
|
||||
<string name="renderer_asynchronous_gpu_emulation_description">此技巧可通过异步运行 GPU 模拟来提升性能,但在执行与时序相关的操作时,可能带来图形显示问题以及增加崩溃概率。</string>
|
||||
<string name="renderer_async_presentation">异步呈现</string>
|
||||
<string name="renderer_async_presentation_description">此技巧通过将图形呈现移至独立的 CPU 线程来提升性能,但可能会带来图形显示问题。</string>
|
||||
<string name="renderer_reactive_flushing">启用反应性刷新</string>
|
||||
<string name="renderer_reactive_flushing_description">通过牺牲性能来提升某些游戏的渲染精度。</string>
|
||||
<string name="enable_buffer_history">启用缓冲区历史</string>
|
||||
@@ -560,17 +558,6 @@
|
||||
<string name="rescale_hack_description">启用通过使用快速缩放路径,来为游戏提供缩放配置处理的传统处理方式</string>
|
||||
<string name="renderer_asynchronous_shaders">使用异步着色器</string>
|
||||
<string name="renderer_asynchronous_shaders_description">以异步方式编译着色器。采用此方式或可减少卡顿,但也可能引入故障点。</string>
|
||||
<string name="gpu_unswizzle_settings">GPU Unswizzle 设置</string>
|
||||
<string name="gpu_unswizzle_settings_description">配置基于 GPU 的纹理 unswizzling 参数,或完全禁用该功能。通过调整这些设置,以尝试在性能与纹理加载质量之间取得平衡。</string>
|
||||
<string name="gpu_unswizzle_enable">启用 GPU Unswizzle</string>
|
||||
<string name="gpu_unswizzle_disabled">禁用</string>
|
||||
<string name="gpu_unswizzle_texture_size">GPU Unswizzle 最大纹理尺寸</string>
|
||||
<string name="gpu_unswizzle_texture_size_description">设置基于 GPU 的纹理 unswizzling 的最大尺寸(MB)。虽然 GPU 处理中等和大型纹理的速度更快,但对于非常小的纹理,CPU 可能更为高效。通过调节此项设置,以平衡GPU 加速与 CPU 开销。</string>
|
||||
<string name="gpu_unswizzle_stream_size">GPU Unswizzle 流大小</string>
|
||||
<string name="gpu_unswizzle_stream_size_description">设置用于 unswizzling 大型纹理时的每帧数据限制。较高的数值可以加速纹理的加载过程,但会带来更高的帧延迟。而较低的数值则可以降低 GPU 的开销,但也可能会导致可见的纹理闪现。</string>
|
||||
<string name="gpu_unswizzle_chunk_size">GPU Unswizzle 块大小</string>
|
||||
<string name="gpu_unswizzle_chunk_size_description">定义了 3D 纹理每批次处理的深度切片数量。增加此数值可在高性能 GPU 上提升吞吐效率,但在性能较弱的硬件上可能会导致卡顿或驱动超时。</string>
|
||||
<string name="gpu_unswizzle_default_button">默认</string>
|
||||
|
||||
|
||||
<string name="extensions">扩展</string>
|
||||
@@ -1004,25 +991,10 @@
|
||||
<string name="fast_gpu_high">超频</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>
|
||||
|
||||
@@ -555,8 +555,6 @@
|
||||
<string name="renderer_force_max_clock_description">強制 GPU 以可能的最大時脈執行 (熱溫限制仍會被套用)</string>
|
||||
<string name="renderer_asynchronous_gpu_emulation">GPU 非同步模擬</string>
|
||||
<string name="renderer_asynchronous_gpu_emulation_description">此修改可透過使用 GPU 非同步模擬來提升性能,不過可能會導致圖形問題且會提高在進行時序相關操作時當機的機率</string>
|
||||
<string name="renderer_async_presentation">非同步呈現</string>
|
||||
<string name="renderer_async_presentation_description">此修改可以透過將渲染移至單獨的 CPU 執行緒來提升性能,但可能會導致圖形問題</string>
|
||||
<string name="renderer_reactive_flushing">使用重新啟用排清</string>
|
||||
<string name="renderer_reactive_flushing_description">犧牲效能,以改善部分遊戲的轉譯準確度</string>
|
||||
<string name="enable_buffer_history">啟用緩衝區歷史</string>
|
||||
@@ -580,17 +578,6 @@
|
||||
<string name="rescale_hack_description">透過快速重新縮放處理來啟用遊戲重新縮放設定階段的舊版處理方式</string>
|
||||
<string name="renderer_asynchronous_shaders">使用非同步著色器</string>
|
||||
<string name="renderer_asynchronous_shaders_description">使用非同步編譯著色器。這可能會減少卡頓,但也可能導致圖形錯誤。</string>
|
||||
<string name="gpu_unswizzle_settings">GPU Unswizzle 設定</string>
|
||||
<string name="gpu_unswizzle_settings_description">設定基於 GPU 的 Unswizzling 參數或完全停用此功能。調整此設定以在性能與紋理載入品質中取得平衡</string>
|
||||
<string name="gpu_unswizzle_enable">啟用 GPU Unswizzle</string>
|
||||
<string name="gpu_unswizzle_disabled">停用</string>
|
||||
<string name="gpu_unswizzle_texture_size">GPU Unswizzle 最大紋理尺寸</string>
|
||||
<string name="gpu_unswizzle_texture_size_description">設定 GPU 紋理 Unswizzling 的最大值 (MB)。雖然 GPU 處理中型和大型紋理的速度較快,但 CPU 處理非常小的紋理可能更有效率。整此設定以在 GPU 加速與 CPU 負載之間取得平衡</string>
|
||||
<string name="gpu_unswizzle_stream_size">GPU Unswizzle 流大小</string>
|
||||
<string name="gpu_unswizzle_stream_size_description">設定每個影格處理大型紋理 Unswizzling 的資料上限。數值越高載入紋理的速度越快,但會增加畫面延遲;數值較低則能降低 GPU 負載,不過有可能導致至紋理載入不完全甚至突然出現</string>
|
||||
<string name="gpu_unswizzle_chunk_size">GPU Unswizzle 塊大小</string>
|
||||
<string name="gpu_unswizzle_chunk_size_description">設定 3D 紋理每批次處理的深度切片數量。增加此數值可提升規格較佳 GPU 的處理效率,但在較低規格的硬體上可能導致卡頓或驅動程式逾時</string>
|
||||
<string name="gpu_unswizzle_default_button">預設</string>
|
||||
|
||||
|
||||
<string name="extensions">擴充功能</string>
|
||||
|
||||
@@ -591,54 +591,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>
|
||||
|
||||
@@ -580,8 +580,6 @@
|
||||
<string name="renderer_force_max_clock_description">Forces the GPU to run at the maximum possible clocks (thermal constraints will still be applied).</string>
|
||||
<string name="renderer_asynchronous_gpu_emulation">GPU async emulation</string>
|
||||
<string name="renderer_asynchronous_gpu_emulation_description">This hack can increase performance by running GPU emulation asynchronously at the cost of graphical issues and increased crash rates by timing-related operations.</string>
|
||||
<string name="renderer_async_presentation">Asynchronous presentation</string>
|
||||
<string name="renderer_async_presentation_description">This hack can increase performance by moving presentation to a separate CPU thread at the cost of graphical issues.</string>
|
||||
<string name="renderer_reactive_flushing">Use reactive flushing</string>
|
||||
<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>
|
||||
@@ -605,17 +603,6 @@
|
||||
<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>
|
||||
<string name="gpu_unswizzle_stream_size_description">Sets the data limit per frame for unswizzling large textures. Higher values speed up texture loading at the cost of higher frame latency; lower values reduce GPU overhead but may cause visible texture pop-in.</string>
|
||||
<string name="gpu_unswizzle_chunk_size">GPU Unswizzle Chunk Size</string>
|
||||
<string name="gpu_unswizzle_chunk_size_description">Defines the number of depth slices processed per batch for 3D textures. Increasing this improves throughput efficiency on powerful GPUs but may cause stuttering or driver timeouts on weaker hardware.</string>
|
||||
<string name="gpu_unswizzle_default_button">Default</string>
|
||||
|
||||
|
||||
<string name="extensions">Extensions</string>
|
||||
@@ -1065,25 +1052,10 @@
|
||||
<string name="fast_gpu_high">Overclock</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>
|
||||
|
||||
@@ -108,8 +108,6 @@ add_library(
|
||||
settings_setting.h
|
||||
slot_vector.h
|
||||
socket_types.h
|
||||
sparse_large_vector.cpp
|
||||
sparse_large_vector.h
|
||||
spin_lock.h
|
||||
stb.cpp
|
||||
stb.h
|
||||
@@ -139,6 +137,8 @@ add_library(
|
||||
uuid.cpp
|
||||
uuid.h
|
||||
vector_math.h
|
||||
virtual_buffer.cpp
|
||||
virtual_buffer.h
|
||||
zstd_compression.cpp
|
||||
zstd_compression.h
|
||||
fs/ryujinx_compat.h fs/ryujinx_compat.cpp
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/fiber.h"
|
||||
#include "common/virtual_buffer.h"
|
||||
|
||||
#include <boost/context/detail/fcontext.hpp>
|
||||
|
||||
|
||||
+13
-41
@@ -178,14 +178,6 @@ public:
|
||||
Release();
|
||||
}
|
||||
|
||||
void* Allocate(size_t size) {
|
||||
auto* ptr = VirtualAlloc(nullptr, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
|
||||
if (ptr == nullptr) {
|
||||
LOG_CRITICAL(HW_Memory, "Failed to allocate fallback buffer with size {:#x}, error {}", size, GetLastError());
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms) {
|
||||
std::unique_lock lock{placeholder_mutex};
|
||||
if (!IsNiechePlaceholder(virtual_offset, length)) {
|
||||
@@ -406,10 +398,6 @@ private:
|
||||
// For managarm: see https://github.com/managarm/managarm/issues/1370
|
||||
#else // ^^^ Windows ^^^ vvv POSIX vvv
|
||||
|
||||
#ifndef MAP_NOCORE
|
||||
#define MAP_NOCORE 0
|
||||
#endif
|
||||
|
||||
#ifdef ARCHITECTURE_arm64
|
||||
|
||||
static void* ChooseVirtualBase(size_t virtual_size) {
|
||||
@@ -434,7 +422,7 @@ static void* ChooseVirtualBase(size_t virtual_size) {
|
||||
// Note: we may be able to take advantage of MAP_FIXED_NOREPLACE here.
|
||||
void* map_pointer =
|
||||
mmap(reinterpret_cast<void*>(hint_address), virtual_size, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_NOCORE, -1, 0);
|
||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
|
||||
|
||||
// If we successfully mapped, we're done.
|
||||
if (reinterpret_cast<uintptr_t>(map_pointer) == hint_address) {
|
||||
@@ -454,11 +442,11 @@ static void* ChooseVirtualBase(size_t virtual_size) {
|
||||
|
||||
static void* ChooseVirtualBase(size_t virtual_size) {
|
||||
#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__) || defined(__sun__) || defined(__HAIKU__) || defined(__managarm__) || defined(__AIX__)
|
||||
void* virtual_base = mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_ALIGNED_SUPER | MAP_NOCORE, -1, 0);
|
||||
void* virtual_base = mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_ALIGNED_SUPER, -1, 0);
|
||||
if (virtual_base != MAP_FAILED)
|
||||
return virtual_base;
|
||||
#endif
|
||||
return mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_NOCORE, -1, 0);
|
||||
return mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -552,13 +540,13 @@ public:
|
||||
}
|
||||
if (use_anon) {
|
||||
LOG_WARNING(Common_Memory, "Using private mappings instead of shared ones");
|
||||
backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_NOCORE, -1, 0));
|
||||
backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0));
|
||||
if (fd > 0) {
|
||||
fd = -1;
|
||||
close(fd);
|
||||
}
|
||||
} else {
|
||||
backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_NOCORE, fd, 0));
|
||||
backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
|
||||
}
|
||||
if (backing_base == MAP_FAILED) {
|
||||
LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno));
|
||||
@@ -582,14 +570,6 @@ public:
|
||||
Release();
|
||||
}
|
||||
|
||||
void* Allocate(size_t size) {
|
||||
auto* ptr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||
if (ptr == MAP_FAILED) {
|
||||
LOG_CRITICAL(HW_Memory, "Failed to allocate fallback buffer with size {:#x}, {}", size, strerror(errno));
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms) {
|
||||
// Intersect the range with our address space.
|
||||
AdjustMap(&virtual_offset, &length);
|
||||
@@ -710,10 +690,12 @@ HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_)
|
||||
{
|
||||
#if defined(__OPENORBIS__) || defined(__managarm__)
|
||||
LOG_WARNING(HW_Memory, "Platform doesn't support fastmem");
|
||||
backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
|
||||
fallback_buffer.emplace(backing_size);
|
||||
backing_base = fallback_buffer->data();
|
||||
virtual_base = nullptr;
|
||||
#else
|
||||
// Try to allocate a fastmem arena.
|
||||
// The implementation will fail with std::bad_alloc on errors.
|
||||
impl = std::make_unique<HostMemory::Impl>(AlignUp(backing_size, PageAlignment), AlignUp(virtual_size, PageAlignment) + HugePageSize);
|
||||
if (impl->Init()) {
|
||||
backing_base = impl->backing_base;
|
||||
@@ -724,26 +706,16 @@ HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_)
|
||||
virtual_base_offset = virtual_base - impl->virtual_base;
|
||||
}
|
||||
} else {
|
||||
LOG_WARNING(HW_Memory, "Platform can support fastmem, but can't create it");
|
||||
fallback_buffer = true;
|
||||
backing_base = static_cast<u8*>(impl->Allocate(backing_size));
|
||||
virtual_base = nullptr;
|
||||
impl.reset();
|
||||
LOG_WARNING(HW_Memory, "Platform can support fastmem, but can't create it");
|
||||
fallback_buffer.emplace(backing_size);
|
||||
backing_base = fallback_buffer->data();
|
||||
virtual_base = nullptr;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
HostMemory::~HostMemory() {
|
||||
#ifdef _WIN32
|
||||
if (fallback_buffer) {
|
||||
VirtualFree(backing_base, backing_size, MEM_RELEASE);
|
||||
}
|
||||
#else
|
||||
if (fallback_buffer) {
|
||||
munmap(backing_base, backing_size);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
HostMemory::~HostMemory() = default;
|
||||
|
||||
HostMemory::HostMemory(HostMemory&&) noexcept = default;
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <optional>
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/virtual_buffer.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
@@ -85,7 +86,7 @@ private:
|
||||
u8* virtual_base{};
|
||||
size_t virtual_base_offset{};
|
||||
// Windows requires it for kernels whom lack proper support for some functions!
|
||||
bool fallback_buffer{false};
|
||||
std::optional<Common::VirtualBuffer<u8>> fallback_buffer;
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -13,11 +13,39 @@ PageTable::PageTable() = default;
|
||||
|
||||
PageTable::~PageTable() noexcept = default;
|
||||
|
||||
void PageTable::Resize(std::size_t address_space_width_in_bits, std::size_t page_bits) {
|
||||
auto const num_page_table_entries = 1ULL << (address_space_width_in_bits - page_bits);
|
||||
entries.ResizeAndClear(num_page_table_entries);
|
||||
bool PageTable::BeginTraversal(TraversalEntry* out_entry, TraversalContext* out_context,
|
||||
Common::ProcessAddress address) const {
|
||||
out_context->next_offset = GetInteger(address);
|
||||
out_context->next_page = address / page_size;
|
||||
|
||||
return this->ContinueTraversal(out_entry, out_context);
|
||||
}
|
||||
|
||||
bool PageTable::ContinueTraversal(TraversalEntry* out_entry, TraversalContext* context) const {
|
||||
// Setup invalid defaults.
|
||||
out_entry->phys_addr = 0;
|
||||
out_entry->block_size = page_size;
|
||||
// Validate that we can read the actual entry.
|
||||
if (auto const page = context->next_page; page < entries.size()) {
|
||||
// Validate that the entry is mapped.
|
||||
if (auto const paddr = entries[page].addr; paddr != 0) {
|
||||
// Populate the results.
|
||||
out_entry->phys_addr = paddr + context->next_offset;
|
||||
context->next_page += 1;
|
||||
context->next_offset += page_size;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
context->next_page += 1;
|
||||
context->next_offset += page_size;
|
||||
return false;
|
||||
}
|
||||
|
||||
void PageTable::Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits) {
|
||||
auto const num_page_table_entries = 1ULL << (address_space_width_in_bits - page_size_in_bits);
|
||||
entries.resize(num_page_table_entries);
|
||||
current_address_space_width_in_bits = address_space_width_in_bits;
|
||||
current_page_bits = page_bits;
|
||||
page_size = 1ULL << page_size_in_bits;
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
||||
+56
-64
@@ -9,22 +9,22 @@
|
||||
#include <atomic>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/sparse_large_vector.h"
|
||||
#include "common/typed_address.h"
|
||||
#include "common/virtual_buffer.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
enum class PageType : u8 {
|
||||
/// Page is unmapped and should cause an access error.
|
||||
Unmapped = 0b00,
|
||||
Unmapped,
|
||||
/// Page is mapped to regular memory. This is the only type you can get pointers to.
|
||||
Memory = 0b01,
|
||||
Memory,
|
||||
/// Page is mapped to regular memory, but inaccessible from CPU fastmem and must use
|
||||
/// the callbacks.
|
||||
DebugMemory = 0b10,
|
||||
DebugMemory,
|
||||
/// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
|
||||
/// invalidation
|
||||
RasterizerCachedMemory = 0b11,
|
||||
RasterizerCachedMemory,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -42,86 +42,57 @@ struct PageTable {
|
||||
u64 next_offset{};
|
||||
};
|
||||
|
||||
/// Masks out bits reserved for attribute tagging.
|
||||
static constexpr u64 ATTRIBUTE_MASK = ((1ULL << 44) - 1) << 12;
|
||||
|
||||
/// Specifies sign bit for page table entries.
|
||||
static constexpr u64 SIGN_BIT = 45 + 12; // 44 bits of data + page offset
|
||||
/// Number of bits reserved for attribute tagging.
|
||||
/// This can be at most the guaranteed alignment of the pointers in the page table.
|
||||
static constexpr int ATTRIBUTE_BITS = 2;
|
||||
|
||||
/**
|
||||
* Atomic tuple of host pointer, page type, and block id.
|
||||
* This uses the lower bits of a given pointer to store the attributes.
|
||||
* Pair of host pointer and page type attribute.
|
||||
* This uses the lower bits of a given pointer to store the attribute tag.
|
||||
* Writing and reading the pointer attribute pair is guaranteed to be atomic for the same method
|
||||
* call. In other words, they are guaranteed to be synchronized at all times.
|
||||
*/
|
||||
class PageEntryData {
|
||||
class PageInfo {
|
||||
public:
|
||||
struct Data {
|
||||
Data(bool marked_, PageType type_, u16 block_, u64 page_)
|
||||
: marked(static_cast<u64>(marked_) & 0b1)
|
||||
, type(static_cast<u64>(type_) & ((1ULL << 2) - 1))
|
||||
, block(static_cast<u64>(block_) & ((1ULL << 9) - 1))
|
||||
, page((page_ >> 12) & ((1ULL << 45) - 1))
|
||||
, block2((static_cast<u64>(block_) >> 9) & ((1ULL << 7) - 1)) {}
|
||||
u64 marked : 1;
|
||||
u64 type : 2;
|
||||
u64 block : 9;
|
||||
u64 page : 45; // 44 bits of actual data (64 - page offset (12) - reserved (8)) + a sign bit
|
||||
u64 block2 : 7;
|
||||
};
|
||||
|
||||
[[nodiscard]] Data Raw() const noexcept {
|
||||
return std::bit_cast<Data>(data_raw.load(std::memory_order_relaxed));
|
||||
}
|
||||
|
||||
/// Returns the page pointer
|
||||
[[nodiscard]] uintptr_t Pointer(bool ignored_marked = false) const noexcept {
|
||||
return ExtractPointer(std::bit_cast<Data>(data_raw.load(std::memory_order_relaxed)), ignored_marked);
|
||||
[[nodiscard]] uintptr_t Pointer() const noexcept {
|
||||
return ExtractPointer(raw.load(std::memory_order_relaxed));
|
||||
}
|
||||
|
||||
/// Returns the page type attribute
|
||||
[[nodiscard]] PageType Type() const noexcept {
|
||||
return static_cast<PageType>(std::bit_cast<Data>(data_raw.load(std::memory_order_relaxed)).type);
|
||||
}
|
||||
|
||||
/// Returns the block identifier.
|
||||
[[nodiscard]] u16 Block() const noexcept {
|
||||
return ExtractBlock(std::bit_cast<Data>(data_raw.load(std::memory_order_relaxed)));
|
||||
return ExtractType(raw.load(std::memory_order_relaxed));
|
||||
}
|
||||
|
||||
/// Returns the page pointer and attribute pair, extracted from the same atomic read
|
||||
[[nodiscard]] std::tuple<uintptr_t, PageType, u16> PointerTypeBlock(bool ignore_marked = false) const noexcept {
|
||||
const auto non_atomic_raw = std::bit_cast<Data>(data_raw.load(std::memory_order_relaxed));
|
||||
return {ExtractPointer(non_atomic_raw, ignore_marked), static_cast<PageType>(non_atomic_raw.type), ExtractBlock(non_atomic_raw)};
|
||||
[[nodiscard]] std::pair<uintptr_t, PageType> PointerType() const noexcept {
|
||||
const uintptr_t non_atomic_raw = raw.load(std::memory_order_relaxed);
|
||||
return {ExtractPointer(non_atomic_raw), ExtractType(non_atomic_raw)};
|
||||
}
|
||||
|
||||
/// Write page info atomically
|
||||
constexpr void Store(bool marked, PageType type, u16 block, uintptr_t pointer) noexcept {
|
||||
data_raw.store(std::bit_cast<u64>(Data{marked, type, block, pointer}));
|
||||
/// Returns the raw representation of the page information.
|
||||
/// Use ExtractPointer and ExtractType to unpack the value.
|
||||
[[nodiscard]] uintptr_t Raw() const noexcept {
|
||||
return raw.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
constexpr void MarkRasterizerCached() noexcept {
|
||||
data_raw.fetch_or(0b111);
|
||||
}
|
||||
|
||||
constexpr void MarkDebug(u64 ptr, u16 block) noexcept {
|
||||
Store(true, PageType::DebugMemory, block, ptr);
|
||||
/// Write a page pointer and type pair atomically
|
||||
void Store(uintptr_t pointer, PageType type) noexcept {
|
||||
raw.store(pointer | uintptr_t(type));
|
||||
}
|
||||
|
||||
/// Unpack a pointer from a page info raw representation
|
||||
[[nodiscard]] static uintptr_t ExtractPointer(Data raw, bool ignore_marked = false) noexcept {
|
||||
return raw.marked && !ignore_marked ? 0
|
||||
// shift raw.page's fake sign bit to the actual sign bit, then sign extend
|
||||
: ((s64)(raw.page << (64 - 44))) >> (64 - 44 - 12);
|
||||
[[nodiscard]] static uintptr_t ExtractPointer(uintptr_t raw) noexcept {
|
||||
return raw & (~uintptr_t{0} << ATTRIBUTE_BITS);
|
||||
}
|
||||
|
||||
[[nodiscard]] static u16 ExtractBlock(Data raw) noexcept {
|
||||
return static_cast<u16>(raw.block | (raw.block2 << 9));
|
||||
/// Unpack a page type from a page info raw representation
|
||||
[[nodiscard]] static PageType ExtractType(uintptr_t raw) noexcept {
|
||||
return static_cast<PageType>(raw & ((uintptr_t{1} << ATTRIBUTE_BITS) - 1));
|
||||
}
|
||||
|
||||
private:
|
||||
std::atomic<u64> data_raw;
|
||||
static_assert(sizeof(Data) == sizeof(std::atomic<u64>));
|
||||
std::atomic<uintptr_t> raw;
|
||||
};
|
||||
|
||||
PageTable();
|
||||
@@ -129,8 +100,13 @@ struct PageTable {
|
||||
|
||||
PageTable(const PageTable&) = delete;
|
||||
PageTable& operator=(const PageTable&) = delete;
|
||||
PageTable(PageTable&&) noexcept = delete;
|
||||
PageTable& operator=(PageTable&&) noexcept = delete;
|
||||
|
||||
PageTable(PageTable&&) noexcept = default;
|
||||
PageTable& operator=(PageTable&&) noexcept = default;
|
||||
|
||||
bool BeginTraversal(TraversalEntry* out_entry, TraversalContext* out_context,
|
||||
Common::ProcessAddress address) const;
|
||||
bool ContinueTraversal(TraversalEntry* out_entry, TraversalContext* context) const;
|
||||
|
||||
/**
|
||||
* Resizes the page table to be able to accommodate enough pages within
|
||||
@@ -145,14 +121,30 @@ struct PageTable {
|
||||
return current_address_space_width_in_bits;
|
||||
}
|
||||
|
||||
bool GetPhysicalAddress(Common::PhysicalAddress* out_phys_addr,
|
||||
Common::ProcessAddress virt_addr) const {
|
||||
if (virt_addr > (1ULL << this->GetAddressSpaceBits())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*out_phys_addr = entries[virt_addr / page_size].addr + GetInteger(virt_addr);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Vector of memory pointers backing each page. An entry can only be non-null if the
|
||||
/// corresponding attribute element is of type `Memory`.
|
||||
SparseLargeVector<PageEntryData> entries;
|
||||
static_assert(sizeof(PageEntryData) == 8);
|
||||
struct PageEntryData {
|
||||
PageInfo ptr;
|
||||
u64 block;
|
||||
u64 addr;
|
||||
u64 padding;
|
||||
};
|
||||
VirtualBuffer<PageEntryData> entries;
|
||||
static_assert(sizeof(PageEntryData) == 32);
|
||||
|
||||
u8* fastmem_arena{};
|
||||
std::size_t current_address_space_width_in_bits{};
|
||||
std::size_t current_page_bits{};
|
||||
std::size_t page_size{};
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -48,7 +48,6 @@ SWITCHABLE(AnisotropyMode, true);
|
||||
SWITCHABLE(AntiAliasing, false);
|
||||
SWITCHABLE(AspectRatio, true);
|
||||
SWITCHABLE(AstcDecodeMode, true);
|
||||
SWITCHABLE(AstcRecompression, true);
|
||||
SWITCHABLE(AudioMode, true);
|
||||
SWITCHABLE(CpuBackend, true);
|
||||
SWITCHABLE(CpuAccuracy, true);
|
||||
|
||||
+1
-29
@@ -65,7 +65,6 @@ SWITCHABLE(AnisotropyMode, true);
|
||||
SWITCHABLE(AntiAliasing, false);
|
||||
SWITCHABLE(AspectRatio, true);
|
||||
SWITCHABLE(AstcDecodeMode, true);
|
||||
SWITCHABLE(AstcRecompression, true);
|
||||
SWITCHABLE(AudioMode, true);
|
||||
SWITCHABLE(CpuBackend, true);
|
||||
SWITCHABLE(CpuAccuracy, true);
|
||||
@@ -563,12 +562,6 @@ struct Values {
|
||||
true,
|
||||
true};
|
||||
|
||||
SwitchableSetting<AstcRecompression, true> astc_recompression{linkage,
|
||||
AstcRecompression::Uncompressed,
|
||||
"astc_recompression",
|
||||
Category::RendererAdvanced};
|
||||
|
||||
|
||||
SwitchableSetting<bool> sync_memory_operations{linkage,
|
||||
false,
|
||||
"sync_memory_operations",
|
||||
@@ -641,7 +634,7 @@ struct Values {
|
||||
true};
|
||||
SwitchableSetting<bool> async_presentation{linkage,
|
||||
#ifdef __ANDROID__
|
||||
false,
|
||||
true,
|
||||
#else
|
||||
false,
|
||||
#endif
|
||||
@@ -666,27 +659,6 @@ struct Values {
|
||||
SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders",
|
||||
Category::RendererHacks};
|
||||
|
||||
SwitchableSetting<GpuUnswizzleSize> gpu_unswizzle_texture_size{linkage,
|
||||
GpuUnswizzleSize::Large,
|
||||
"gpu_unswizzle_texture_size",
|
||||
Category::RendererHacks,
|
||||
Specialization::Default};
|
||||
|
||||
SwitchableSetting<GpuUnswizzle> gpu_unswizzle_stream_size{linkage,
|
||||
GpuUnswizzle::Medium,
|
||||
"gpu_unswizzle_stream_size",
|
||||
Category::RendererHacks,
|
||||
Specialization::Default};
|
||||
|
||||
SwitchableSetting<GpuUnswizzleChunk> gpu_unswizzle_chunk_size{linkage,
|
||||
GpuUnswizzleChunk::Medium,
|
||||
"gpu_unswizzle_chunk_size",
|
||||
Category::RendererHacks,
|
||||
Specialization::Default};
|
||||
|
||||
SwitchableSetting<bool> gpu_unswizzle_enabled{linkage, false, "gpu_unswizzle_enabled",
|
||||
Category::RendererHacks};
|
||||
|
||||
SwitchableSetting<ExtendedDynamicState> dyna_state{linkage,
|
||||
#if defined(__ANDROID__)
|
||||
ExtendedDynamicState::Disabled,
|
||||
|
||||
@@ -130,7 +130,6 @@ ENUM(TimeZone, Auto, Default, Cet, Cst6Cdt, Cuba, Eet, Egypt, Eire, Est, Est5Edt
|
||||
Roc, Rok, Singapore, Turkey, Uct, Universal, Utc, WSu, Wet, Zulu);
|
||||
ENUM(AnisotropyMode, Automatic, Default, X2, X4, X8, X16);
|
||||
ENUM(AstcDecodeMode, Cpu, Gpu, CpuAsynchronous);
|
||||
ENUM(AstcRecompression, Uncompressed, Bc1, Bc3);
|
||||
ENUM(FramePacingMode, Target_Auto, Target_30, Target_60, Target_90, Target_120);
|
||||
ENUM(VSyncMode, Immediate, Mailbox, Fifo, FifoRelaxed);
|
||||
ENUM(VramUsageMode, Conservative, Aggressive);
|
||||
@@ -153,9 +152,6 @@ ENUM(ConsoleMode, Handheld, Docked);
|
||||
ENUM(AppletMode, HLE, LLE);
|
||||
ENUM(SpirvOptimizeMode, Never, OnLoad, Always);
|
||||
ENUM(GpuClock, Normal, Boost, Overclock)
|
||||
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)
|
||||
|
||||
@@ -1,143 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* virtual_buffer.cpp */
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <mutex>
|
||||
#else
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/sparse_large_vector.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
#ifdef _WIN32
|
||||
static std::vector<std::pair<u64, u64>> vector_regions {};
|
||||
|
||||
// Workaround for handling non-commited memory accessed by Dynarmic; usually result of an error
|
||||
static LONG WINAPI FakePageFaultHandler(PEXCEPTION_POINTERS info) {
|
||||
DWORD code = info->ExceptionRecord->ExceptionCode;
|
||||
u64 exception_addr = reinterpret_cast<u64>(info->ExceptionRecord->ExceptionAddress);
|
||||
|
||||
if (code != EXCEPTION_ACCESS_VIOLATION) {
|
||||
// Not our problem
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
u64 addr = 0, addr2 = 0;
|
||||
|
||||
for (auto region: vector_regions) {
|
||||
auto addr_shifted = exception_addr >> HostPageBits;
|
||||
if (region.first <= addr_shifted && addr_shifted <= region.second) {
|
||||
addr = addr_shifted;
|
||||
}
|
||||
|
||||
// Page-boundary accesses
|
||||
if (auto addr_ = (exception_addr + 0x40) >> HostPageBits; addr_ != addr_shifted && region.first <= addr_ && addr_ <= region.second) {
|
||||
addr2 = addr_;
|
||||
}
|
||||
|
||||
if (addr != 0 || addr2 != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (addr == 0 && addr2 == 0) {
|
||||
// Not our problem
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
LOG_ERROR(HW_Memory, "Accessing an unallocated region of a SparseLargeVector at {:#x}; this shouldn't happen and is likely a Dynarmic error!", exception_addr);
|
||||
|
||||
// Commit this region
|
||||
if (addr != 0) {
|
||||
if (!CommitVectorPage(addr << HostPageBits, false)) {
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
}
|
||||
// Commit next region if needed
|
||||
if (addr2 != 0) {
|
||||
if (!CommitVectorPage(addr2 << HostPageBits, false)) {
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
}
|
||||
|
||||
return EXCEPTION_CONTINUE_EXECUTION;
|
||||
}
|
||||
|
||||
bool CommitVectorPage(uintptr_t addr, bool write) noexcept {
|
||||
MEMORY_BASIC_INFORMATION info {};
|
||||
auto res = VirtualQuery(reinterpret_cast<void*>(addr), &info, sizeof(info));
|
||||
if (res == 0) {
|
||||
LOG_CRITICAL(HW_Memory, "Failed to query large buffer region at {:#x} with error {}, will try committing anyway", addr, GetLastError());
|
||||
} else if (info.State != MEM_RESERVE) {
|
||||
LOG_ERROR(HW_Memory, "Tried to commit an unreserved large buffer region at {:#x} that is not mapped or is already committed (state {:#x})", addr, info.State);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto perm = write ? PAGE_READWRITE : PAGE_READONLY;
|
||||
void* res2 = VirtualAlloc(reinterpret_cast<LPVOID>(addr), HostPageSize, MEM_COMMIT, perm);
|
||||
if (res2 == nullptr) {
|
||||
LOG_ERROR(HW_Memory, "Failed to commit large buffer region at {:#x}, error {}", addr, GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef MAP_NOCORE
|
||||
#define MAP_NOCORE 0
|
||||
#endif
|
||||
|
||||
void* AllocateMemoryPages(std::size_t size) noexcept {
|
||||
if (auto page = HostPageSize; size % page != 0) {
|
||||
LOG_WARNING(HW_Memory, "Allocating unaligned large vector with size {:#x}; aligning to {} page size", size, page);
|
||||
size = AlignUp(size, page);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// We will never use this memory entirely so instead of committing it up front let's just reserve it and commit each page individually
|
||||
void* base = VirtualAlloc(nullptr, size, MEM_RESERVE, PAGE_READWRITE);
|
||||
|
||||
if (base != nullptr) {
|
||||
vector_regions.emplace_back(reinterpret_cast<u64>(base), reinterpret_cast<u64>(base) + size);
|
||||
|
||||
static std::once_flag flag;
|
||||
std::call_once(flag, []() { AddVectoredExceptionHandler(1, FakePageFaultHandler); });
|
||||
} else {
|
||||
// Try committing everything instead??
|
||||
LOG_WARNING(HW_Memory, "Failed to reserve large vector region with error {}, trying to commit instead..", GetLastError());
|
||||
base = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE);
|
||||
}
|
||||
ASSERT_MSG(base, "Failed to reserve {:#x} sized region with error {}", size, GetLastError());
|
||||
#else
|
||||
void* base = mmap(nullptr, size, PROT_READ, MAP_ANON | MAP_PRIVATE | MAP_NOCORE, -1, 0);
|
||||
if (base == MAP_FAILED)
|
||||
base = nullptr;
|
||||
ASSERT_MSG(base, "Failed to allocate {:#x} sized region with error {}", size, strerror(errno));
|
||||
#endif
|
||||
return base;
|
||||
}
|
||||
|
||||
void FreeMemoryPages(void* base, [[maybe_unused]] std::size_t size) noexcept {
|
||||
if (auto page = HostPageSize; size % page != 0) {
|
||||
size = AlignUp(size, page);
|
||||
}
|
||||
if (!base)
|
||||
return;
|
||||
#ifdef _WIN32
|
||||
ASSERT(VirtualFree(base, 0, MEM_RELEASE));
|
||||
#else
|
||||
ASSERT(munmap(base, size) == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
@@ -1,194 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* virtual_buffer.h */
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <bit>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
#ifdef _WIN32
|
||||
constexpr u64 HostPageSize = 0x1000;
|
||||
constexpr u64 HostPageBits = 12;
|
||||
constexpr u64 HostPageMask = ~(HostPageSize - 1);
|
||||
bool CommitVectorPage(uintptr_t addr, bool write) noexcept;
|
||||
#else
|
||||
const u64 HostPageSize = sysconf(_SC_PAGESIZE);
|
||||
const u64 HostPageBits = std::countr_zero(HostPageSize);
|
||||
const u64 HostPageMask = ~(HostPageSize - 1);
|
||||
#endif
|
||||
|
||||
void* AllocateMemoryPages(std::size_t size) noexcept;
|
||||
void FreeMemoryPages(void* base, std::size_t size) noexcept;
|
||||
|
||||
/// A large page-aligned buffer that has optimized memory usage for zero-writes.
|
||||
template <typename T>
|
||||
requires std::is_trivially_copyable_v<T>
|
||||
class SparseLargeVector final {
|
||||
public:
|
||||
constexpr SparseLargeVector() = default;
|
||||
|
||||
explicit SparseLargeVector(std::size_t count) noexcept
|
||||
: alloc_size{count * sizeof(T)}
|
||||
{
|
||||
base_ptr = static_cast<T*>(AllocateMemoryPages(alloc_size));
|
||||
|
||||
// each item in vector holds information for 64 pages
|
||||
auto denom = HostPageSize * 64;
|
||||
committed_pages = std::vector<std::atomic<u64>>((alloc_size + denom - 1) / denom);
|
||||
}
|
||||
|
||||
~SparseLargeVector() noexcept {
|
||||
FreeMemoryPages(base_ptr, alloc_size);
|
||||
}
|
||||
|
||||
SparseLargeVector(const SparseLargeVector&) = delete;
|
||||
SparseLargeVector& operator=(const SparseLargeVector&) = delete;
|
||||
SparseLargeVector(SparseLargeVector&& other) = delete;
|
||||
SparseLargeVector& operator=(SparseLargeVector&& other) = delete;
|
||||
|
||||
void ResizeAndClear(std::size_t count) noexcept {
|
||||
if (auto const new_size = count * sizeof(T); new_size != alloc_size) {
|
||||
FreeMemoryPages(base_ptr, alloc_size);
|
||||
alloc_size = new_size;
|
||||
base_ptr = static_cast<T*>(AllocateMemoryPages(alloc_size));
|
||||
|
||||
auto denom = HostPageSize * 64;
|
||||
committed_pages = std::vector<std::atomic<u64>>((alloc_size + denom - 1) / denom);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a reference to the value of the requested index and allocates memory if needed.
|
||||
T& GetAndFault(std::size_t index) noexcept {
|
||||
if (index > alloc_size / sizeof(T)) {
|
||||
UNREACHABLE_MSG("Out of bounds RW access on SparseLargeVector @ {}", index);
|
||||
}
|
||||
|
||||
if (!IsCommittedPage(index)) {
|
||||
CommitPage(index);
|
||||
}
|
||||
return base_ptr[index];
|
||||
}
|
||||
|
||||
/// Returns a reference to the value of the requested index if initialized, or will otherwise return a zero-initialized object.
|
||||
const T& GetOrDefault(std::size_t index) const {
|
||||
#ifdef _WIN32
|
||||
if (!IsCommittedPage(index)) {
|
||||
return *reinterpret_cast<const T*>(&default_val);
|
||||
}
|
||||
#endif
|
||||
// On non-Windows, OS page table should optimize this by pointing to a zero page if unallocated.
|
||||
return base_ptr[index];
|
||||
}
|
||||
|
||||
void Set(std::size_t index, const T& value) noexcept {
|
||||
if (index > alloc_size / sizeof(T)) {
|
||||
LOG_CRITICAL(Common_Memory, "Out of bounds write on SparseLargeVector @ {}", index);
|
||||
return;
|
||||
}
|
||||
if (!IsCommittedPage(index))
|
||||
CommitPage(index);
|
||||
base_ptr[index] = value;
|
||||
}
|
||||
|
||||
void ZeroRegion(std::size_t start, std::size_t end_) noexcept {
|
||||
u64 base = reinterpret_cast<u64>(&base_ptr[start]);
|
||||
const u64 end = reinterpret_cast<u64>(&base_ptr[end_]);
|
||||
|
||||
const u64 end_page = AlignUp(base, HostPageSize);
|
||||
const u64 first_size = (std::min)(end_page, end) - base;
|
||||
|
||||
if (IsCommittedPage(start / sizeof(T))) {
|
||||
std::memset(reinterpret_cast<void*>(base), 0, first_size);
|
||||
}
|
||||
|
||||
if (end <= end_page)
|
||||
return;
|
||||
|
||||
base = end_page;
|
||||
|
||||
for (u64 page = base; page < end; page += HostPageSize) {
|
||||
if (!IsCommittedPage((page - reinterpret_cast<u64>(base_ptr)) / sizeof(T))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::memset(reinterpret_cast<void*>(page), 0, (std::min)( HostPageSize, end - page));
|
||||
}
|
||||
}
|
||||
|
||||
constexpr void CommitRegion(size_t index, size_t end_) {
|
||||
const u64 base = static_cast<u64>(index) * sizeof(T);
|
||||
const u64 end = static_cast<u64>(end_) * sizeof(T);
|
||||
|
||||
for (u64 page = AlignDown(base, HostPageSize); page < end; page += HostPageSize) {
|
||||
if (!IsCommittedPage(page / sizeof(T))) {
|
||||
CommitPage(page / sizeof(T));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constexpr T& GetUnchecked(size_t index) {
|
||||
return base_ptr[index];
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr const T& operator[](std::size_t index) const noexcept {
|
||||
return GetOrDefault(index);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr const T* data() const noexcept {
|
||||
return base_ptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr std::size_t size() const noexcept {
|
||||
return alloc_size / sizeof(T);
|
||||
}
|
||||
|
||||
private:
|
||||
[[nodiscard]] constexpr bool IsCommittedPage(std::size_t index) const noexcept {
|
||||
if (index > alloc_size / sizeof(T)) {
|
||||
LOG_CRITICAL(Common_Memory, "Out of bounds access on large vector @ {}", index);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto page = (index * sizeof(T)) >> HostPageBits;
|
||||
auto val = committed_pages[page >> 6].load(std::memory_order_acquire);
|
||||
return (val >> (page & 63)) & 1;
|
||||
}
|
||||
|
||||
constexpr void CommitPage(std::size_t index) noexcept {
|
||||
auto page_index = (index * sizeof(T)) >> HostPageBits;
|
||||
auto page = reinterpret_cast<uintptr_t>(base_ptr + index) & HostPageMask;
|
||||
#if defined(_WIN32)
|
||||
CommitVectorPage(page, true);
|
||||
#else
|
||||
mprotect(reinterpret_cast<void*>(page), HostPageSize, PROT_READ | PROT_WRITE);
|
||||
#endif
|
||||
|
||||
committed_pages[page_index >> 6].fetch_or(1ULL << (page_index & 63), std::memory_order_release);
|
||||
}
|
||||
|
||||
std::size_t alloc_size{};
|
||||
T* base_ptr{};
|
||||
|
||||
std::vector<std::atomic<u64>> committed_pages{};
|
||||
#ifdef _WIN32
|
||||
const std::array<u8, sizeof(T)> default_val{};
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
@@ -0,0 +1,44 @@
|
||||
// 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
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/virtual_buffer.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
void* AllocateMemoryPages(std::size_t size) noexcept {
|
||||
#ifdef _WIN32
|
||||
void* base = VirtualAlloc(nullptr, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||||
if (base == nullptr) {
|
||||
// Probably failing to reserve is less likely than failing to commit
|
||||
base = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE);
|
||||
}
|
||||
#else
|
||||
void* base = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
if (base == MAP_FAILED)
|
||||
base = nullptr;
|
||||
#endif
|
||||
ASSERT(base);
|
||||
return base;
|
||||
}
|
||||
|
||||
void FreeMemoryPages(void* base, [[maybe_unused]] std::size_t size) noexcept {
|
||||
if (!base)
|
||||
return;
|
||||
#ifdef _WIN32
|
||||
ASSERT(VirtualFree(base, 0, MEM_RELEASE));
|
||||
#else
|
||||
ASSERT(munmap(base, size) == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
@@ -0,0 +1,84 @@
|
||||
// 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
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace Common {
|
||||
|
||||
void* AllocateMemoryPages(std::size_t size) noexcept;
|
||||
void FreeMemoryPages(void* base, std::size_t size) noexcept;
|
||||
|
||||
template <typename T>
|
||||
class VirtualBuffer final {
|
||||
public:
|
||||
// TODO: Uncomment this and change Common::PageTable::PageInfo to be trivially constructible
|
||||
// using std::atomic_ref once libc++ has support for it
|
||||
// static_assert(
|
||||
// std::is_trivially_constructible_v<T>,
|
||||
// "T must be trivially constructible, as non-trivial constructors will not be executed "
|
||||
// "with the current allocator");
|
||||
|
||||
constexpr VirtualBuffer() = default;
|
||||
explicit VirtualBuffer(std::size_t count) noexcept
|
||||
: alloc_size{count * sizeof(T)}
|
||||
{
|
||||
base_ptr = reinterpret_cast<T*>(AllocateMemoryPages(alloc_size));
|
||||
}
|
||||
|
||||
~VirtualBuffer() noexcept {
|
||||
FreeMemoryPages(base_ptr, alloc_size);
|
||||
}
|
||||
|
||||
VirtualBuffer(const VirtualBuffer&) = delete;
|
||||
VirtualBuffer& operator=(const VirtualBuffer&) = delete;
|
||||
|
||||
VirtualBuffer(VirtualBuffer&& other) noexcept
|
||||
: alloc_size{std::exchange(other.alloc_size, 0)}
|
||||
, base_ptr{std::exchange(other.base_ptr, nullptr)}
|
||||
{}
|
||||
|
||||
VirtualBuffer& operator=(VirtualBuffer&& other) noexcept {
|
||||
alloc_size = std::exchange(other.alloc_size, 0);
|
||||
base_ptr = std::exchange(other.base_ptr, nullptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void resize(std::size_t count) noexcept {
|
||||
if (auto const new_size = count * sizeof(T); new_size != alloc_size) {
|
||||
FreeMemoryPages(base_ptr, alloc_size);
|
||||
alloc_size = new_size;
|
||||
base_ptr = reinterpret_cast<T*>(AllocateMemoryPages(alloc_size));
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr const T& operator[](std::size_t index) const noexcept {
|
||||
return base_ptr[index];
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr T& operator[](std::size_t index) noexcept {
|
||||
return base_ptr[index];
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr T* data() noexcept {
|
||||
return base_ptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr const T* data() const noexcept {
|
||||
return base_ptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr std::size_t size() const noexcept {
|
||||
return alloc_size / sizeof(T);
|
||||
}
|
||||
|
||||
private:
|
||||
std::size_t alloc_size{};
|
||||
T* base_ptr{};
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
@@ -1238,8 +1238,7 @@ if (HAS_NCE)
|
||||
arm/nce/interpreter_visitor.cpp
|
||||
arm/nce/interpreter_visitor.h
|
||||
arm/nce/patcher.cpp
|
||||
arm/nce/patcher.h
|
||||
arm/nce/visitor_base.h)
|
||||
arm/nce/patcher.h)
|
||||
target_link_libraries(core PRIVATE merry::oaknut)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -172,12 +172,12 @@ void ArmDynarmic32::MakeJit(Common::PageTable* page_table) {
|
||||
if (page_table) {
|
||||
constexpr size_t PageBits = 12;
|
||||
constexpr size_t NumPageTableEntries = 1 << (32 - PageBits);
|
||||
constexpr size_t PageLog2Stride = 5;
|
||||
static_assert(1 << PageLog2Stride == sizeof(Common::PageTable::PageEntryData));
|
||||
|
||||
// Dynarmic will not write to the page table, const_cast is safe here
|
||||
config.page_table = reinterpret_cast<std::array<std::uint8_t*, NumPageTableEntries>*>(
|
||||
const_cast<Common::PageTable::PageEntryData*>(page_table->entries.data()));
|
||||
config.page_table_pointer_mask = Common::PageTable::ATTRIBUTE_MASK;
|
||||
config.page_table_marked_bit = 0;
|
||||
config.page_table = reinterpret_cast<std::array<std::uint8_t*, NumPageTableEntries>*>(page_table->entries.data());
|
||||
config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS;
|
||||
config.page_table_log2_stride = PageLog2Stride;
|
||||
config.absolute_offset_page_table = true;
|
||||
config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
|
||||
config.only_detect_misalignment_via_page_table_on_page_boundary = true;
|
||||
@@ -188,13 +188,6 @@ void ArmDynarmic32::MakeJit(Common::PageTable* page_table) {
|
||||
|
||||
config.fastmem_exclusive_access = config.fastmem_pointer != std::nullopt;
|
||||
config.recompile_on_exclusive_fastmem_failure = true;
|
||||
|
||||
if (reinterpret_cast<u64>(m_system.DeviceMemory().buffer.BackingBasePointer() +
|
||||
Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize()) < (1ULL << 39)) {
|
||||
// Systems like FreeBSD allocate memory really low by default, and since we pack our page table entries,
|
||||
// we have to manually sign extend when our actual pointer is negative.
|
||||
config.page_table_sign_extension = Common::PageTable::SIGN_BIT;
|
||||
}
|
||||
}
|
||||
|
||||
// Multi-process state
|
||||
@@ -427,7 +420,6 @@ void ArmDynarmic32::SignalInterrupt(Kernel::KThread* thread) {
|
||||
}
|
||||
|
||||
void ArmDynarmic32::ClearInstructionCache() {
|
||||
m_cb->last_code_addr = u64(-1);
|
||||
m_jit->ClearCache();
|
||||
}
|
||||
|
||||
|
||||
@@ -211,12 +211,13 @@ void ArmDynarmic64::MakeJit(Common::PageTable* page_table, std::size_t address_s
|
||||
|
||||
// Memory
|
||||
if (page_table) {
|
||||
// Dynarmic will not write to the page table, const_cast is safe here
|
||||
config.page_table = reinterpret_cast<void**>(
|
||||
const_cast<Common::PageTable::PageEntryData*>(page_table->entries.data()));
|
||||
constexpr size_t PageLog2Stride = 5;
|
||||
static_assert(1 << PageLog2Stride == sizeof(Common::PageTable::PageEntryData));
|
||||
|
||||
config.page_table = reinterpret_cast<void**>(page_table->entries.data());
|
||||
config.page_table_address_space_bits = std::uint32_t(address_space_bits);
|
||||
config.page_table_pointer_mask = Common::PageTable::ATTRIBUTE_MASK;
|
||||
config.page_table_marked_bit = 0;
|
||||
config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS;
|
||||
config.page_table_log2_stride = PageLog2Stride;
|
||||
config.silently_mirror_page_table = false;
|
||||
config.absolute_offset_page_table = true;
|
||||
config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
|
||||
@@ -230,13 +231,6 @@ void ArmDynarmic64::MakeJit(Common::PageTable* page_table, std::size_t address_s
|
||||
|
||||
config.fastmem_exclusive_access = config.fastmem_pointer != std::nullopt;
|
||||
config.recompile_on_exclusive_fastmem_failure = true;
|
||||
|
||||
if (reinterpret_cast<u64>(m_system.DeviceMemory().buffer.BackingBasePointer() +
|
||||
Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize()) < (1ULL << 39)) {
|
||||
// Systems like FreeBSD allocate memory really low by default, and since we pack our page table entries,
|
||||
// we have to manually sign extend when our actual pointer is negative.
|
||||
config.page_table_sign_extension = Common::PageTable::SIGN_BIT;
|
||||
}
|
||||
}
|
||||
|
||||
// Multi-process state
|
||||
@@ -453,7 +447,6 @@ void ArmDynarmic64::SignalInterrupt(Kernel::KThread* thread) {
|
||||
}
|
||||
|
||||
void ArmDynarmic64::ClearInstructionCache() {
|
||||
m_cb->last_code_addr = u64(-1);
|
||||
m_jit->ClearCache();
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,97 +7,77 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <signal.h>
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wshadow"
|
||||
#include <dynarmic/frontend/A64/a64_types.h>
|
||||
#include <dynarmic/frontend/imm.h>
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/memory.h"
|
||||
#include "common/logging.h"
|
||||
#include "core/arm/nce/visitor_base.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
namespace Memory {
|
||||
class Memory;
|
||||
}
|
||||
|
||||
class InterpreterVisitor final : public VisitorBase {
|
||||
class InterpreterVisitor {
|
||||
public:
|
||||
explicit InterpreterVisitor(Core::Memory::Memory& memory, std::span<u64, 31> regs,
|
||||
std::span<u128, 32> fpsimd_regs, u64& sp, const u64& pc)
|
||||
: m_memory(memory), m_regs(regs), m_fpsimd_regs(fpsimd_regs), m_sp(sp), m_pc(pc) {}
|
||||
~InterpreterVisitor() override = default;
|
||||
|
||||
enum class MemOp {
|
||||
Load,
|
||||
Store,
|
||||
Prefetch,
|
||||
};
|
||||
|
||||
u128 GetVec(Vec v);
|
||||
u64 GetReg(Reg r);
|
||||
u64 GetSp();
|
||||
u64 GetPc();
|
||||
|
||||
void SetVec(Vec v, u128 value);
|
||||
void SetReg(Reg r, u64 value);
|
||||
void SetSp(u64 value);
|
||||
|
||||
u64 ExtendReg(size_t bitsize, Reg reg, Imm<3> option, u8 shift);
|
||||
|
||||
// Loads and stores - Load/Store Exclusive
|
||||
bool Ordered(size_t size, bool L, bool o0, Reg Rn, Reg Rt);
|
||||
bool STLLR(Imm<2> size, Reg Rn, Reg Rt) override;
|
||||
bool STLR(Imm<2> size, Reg Rn, Reg Rt) override;
|
||||
bool LDLAR(Imm<2> size, Reg Rn, Reg Rt) override;
|
||||
bool LDAR(Imm<2> size, Reg Rn, Reg Rt) override;
|
||||
|
||||
// Loads and stores - Load register (literal)
|
||||
bool LDR_lit_gen(bool opc_0, Imm<19> imm19, Reg Rt) override;
|
||||
bool LDR_lit_fpsimd(Imm<2> opc, Imm<19> imm19, Vec Vt) override;
|
||||
|
||||
// Loads and stores - Load/Store register pair
|
||||
bool STP_LDP_gen(Imm<2> opc, bool not_postindex, bool wback, Imm<1> L, Imm<7> imm7, Reg Rt2,
|
||||
Reg Rn, Reg Rt) override;
|
||||
bool STP_LDP_fpsimd(Imm<2> opc, bool not_postindex, bool wback, Imm<1> L, Imm<7> imm7, Vec Vt2,
|
||||
Reg Rn, Vec Vt) override;
|
||||
|
||||
// Loads and stores - Load/Store register (immediate)
|
||||
bool RegisterImmediate(bool wback, bool postindex, size_t scale, u64 offset, Imm<2> size,
|
||||
Imm<2> opc, Reg Rn, Reg Rt);
|
||||
bool STRx_LDRx_imm_1(Imm<2> size, Imm<2> opc, Imm<9> imm9, bool not_postindex, Reg Rn,
|
||||
Reg Rt) override;
|
||||
bool STRx_LDRx_imm_2(Imm<2> size, Imm<2> opc, Imm<12> imm12, Reg Rn, Reg Rt) override;
|
||||
bool STURx_LDURx(Imm<2> size, Imm<2> opc, Imm<9> imm9, Reg Rn, Reg Rt) override;
|
||||
|
||||
bool SIMDImmediate(bool wback, bool postindex, size_t scale, u64 offset, MemOp memop, Reg Rn,
|
||||
Vec Vt);
|
||||
bool STR_imm_fpsimd_1(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, bool not_postindex, Reg Rn,
|
||||
Vec Vt) override;
|
||||
bool STR_imm_fpsimd_2(Imm<2> size, Imm<1> opc_1, Imm<12> imm12, Reg Rn, Vec Vt) override;
|
||||
bool LDR_imm_fpsimd_1(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, bool not_postindex, Reg Rn,
|
||||
Vec Vt) override;
|
||||
bool LDR_imm_fpsimd_2(Imm<2> size, Imm<1> opc_1, Imm<12> imm12, Reg Rn, Vec Vt) override;
|
||||
bool STUR_fpsimd(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, Reg Rn, Vec Vt) override;
|
||||
bool LDUR_fpsimd(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, Reg Rn, Vec Vt) override;
|
||||
|
||||
// Loads and stores - Load/Store register (register offset)
|
||||
bool RegisterOffset(size_t scale, u8 shift, Imm<2> size, Imm<1> opc_1, Imm<1> opc_0, Reg Rm,
|
||||
Imm<3> option, Reg Rn, Reg Rt);
|
||||
bool STRx_reg(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn,
|
||||
Reg Rt) override;
|
||||
bool LDRx_reg(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn,
|
||||
Reg Rt) override;
|
||||
|
||||
bool SIMDOffset(size_t scale, u8 shift, Imm<1> opc_0, Reg Rm, Imm<3> option, Reg Rn, Vec Vt);
|
||||
bool STR_reg_fpsimd(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn,
|
||||
Vec Vt) override;
|
||||
bool LDR_reg_fpsimd(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn,
|
||||
Vec Vt) override;
|
||||
bool Execute(u32 inst);
|
||||
|
||||
private:
|
||||
template <size_t BitSize>
|
||||
using Imm = Dynarmic::Imm<BitSize>;
|
||||
using Reg = Dynarmic::A64::Reg;
|
||||
using Vec = Dynarmic::A64::Vec;
|
||||
|
||||
u128 GetVec(Vec v) const {
|
||||
return m_fpsimd_regs[static_cast<u32>(v)];
|
||||
}
|
||||
void SetVec(Vec v, u128 value) {
|
||||
m_fpsimd_regs[static_cast<u32>(v)] = value;
|
||||
}
|
||||
u64 GetReg(Reg r) const {
|
||||
return m_regs[static_cast<u32>(r)];
|
||||
}
|
||||
void SetReg(Reg r, u64 value) {
|
||||
m_regs[static_cast<u32>(r)] = value;
|
||||
}
|
||||
u64 GetRegSp(Reg r) const {
|
||||
if (r == Reg::SP) {
|
||||
return m_sp;
|
||||
}
|
||||
return m_regs[static_cast<u32>(r)];
|
||||
}
|
||||
void SetRegSp(Reg r, u64 value) {
|
||||
if (r == Reg::SP) {
|
||||
m_sp = value;
|
||||
return;
|
||||
}
|
||||
m_regs[static_cast<u32>(r)] = value;
|
||||
}
|
||||
|
||||
u64 ExtendReg(Reg reg, Imm<3> option, u8 shift);
|
||||
|
||||
bool Ordered(size_t size, bool load, Reg Rn, Reg Rt);
|
||||
bool LoadLiteral(bool wide, Imm<19> imm19, Reg Rt);
|
||||
bool LoadLiteralSimd(Imm<2> opc, Imm<19> imm19, Vec Vt);
|
||||
bool Pair(Imm<2> opc, bool not_postindex, bool wback, bool load, Imm<7> imm7, Reg Rt2, Reg Rn,
|
||||
Reg Rt);
|
||||
bool PairSimd(Imm<2> opc, bool not_postindex, bool wback, bool load, Imm<7> imm7, Vec Vt2,
|
||||
Reg Rn, Vec Vt);
|
||||
bool RegisterImmediate(bool wback, bool postindex, u64 offset, Imm<2> size, Imm<2> opc, Reg Rn,
|
||||
Reg Rt);
|
||||
bool RegisterOffset(bool S, Imm<2> size, Imm<1> opc_1, Imm<1> opc_0, Reg Rm, Imm<3> option,
|
||||
Reg Rn, Reg Rt);
|
||||
bool SimdImmediate(bool wback, bool postindex, size_t scale, u64 offset, bool load, Reg Rn,
|
||||
Vec Vt);
|
||||
bool SimdOffset(size_t scale, bool S, bool load, Reg Rm, Imm<3> option, Reg Rn, Vec Vt);
|
||||
|
||||
Core::Memory::Memory& m_memory;
|
||||
std::span<u64, 31> m_regs;
|
||||
std::span<u128, 32> m_fpsimd_regs;
|
||||
|
||||
@@ -371,14 +371,9 @@ size_t Patcher::GetPreSectionSize() const noexcept {
|
||||
}
|
||||
|
||||
void Patcher::WriteLoadContext(oaknut::VectorCodeGenerator& cg) {
|
||||
// This function was called, which modifies X30, so use that as a scratch register.
|
||||
// SP contains the guest X30, so save our return X30 to SP + 8, since we have allocated 16 bytes
|
||||
// of stack.
|
||||
cg.STR(X30, SP, 8);
|
||||
cg.MRS(X30, oaknut::SystemReg::TPIDR_EL0);
|
||||
cg.LDR(X30, X30, offsetof(NativeExecutionParameters, native_context));
|
||||
cg.LDR(X30, SP, 0);
|
||||
|
||||
// Load system registers.
|
||||
cg.LDR(W0, X30, offsetof(GuestContext, fpsr));
|
||||
cg.MSR(oaknut::SystemReg::FPSR, X0);
|
||||
cg.LDR(W0, X30, offsetof(GuestContext, fpcr));
|
||||
@@ -386,7 +381,9 @@ void Patcher::WriteLoadContext(oaknut::VectorCodeGenerator& cg) {
|
||||
cg.LDR(W0, X30, offsetof(GuestContext, nzcv));
|
||||
cg.MSR(oaknut::SystemReg::NZCV, X0);
|
||||
|
||||
// Load all vector registers.
|
||||
cg.LDR(X0, X30, 8 * 30);
|
||||
cg.STR(X0, SP, 0);
|
||||
|
||||
static constexpr size_t VEC_OFF = offsetof(GuestContext, vector_registers);
|
||||
for (int i = 0; i <= 30; i += 2) {
|
||||
cg.LDP(oaknut::QReg{i}, oaknut::QReg{i + 1}, X30, VEC_OFF + 16 * i);
|
||||
@@ -436,7 +433,7 @@ void Patcher::WriteSaveContext(oaknut::VectorCodeGenerator& cg) {
|
||||
cg.STR(W0, X30, offsetof(GuestContext, nzcv));
|
||||
cg.LDR(X0, SP, POST_INDEXED, 16);
|
||||
|
||||
// Reload our return X30 from the stack, and return.
|
||||
cg.MOV(X1, X30);
|
||||
cg.LDR(X30, SP, 8);
|
||||
cg.RET();
|
||||
}
|
||||
@@ -457,8 +454,6 @@ void Patcher::WriteSvcTrampoline(ModuleDestLabel module_dest, u32 svc_id, oaknut
|
||||
// Now that we've saved all registers, we can use any registers as scratch.
|
||||
// Store PC + 4 to arm interface, since we know the instruction offset from the entry point.
|
||||
oaknut::Label pc_after_svc;
|
||||
cg.MRS(X1, oaknut::SystemReg::TPIDR_EL0);
|
||||
cg.LDR(X1, X1, offsetof(NativeExecutionParameters, native_context));
|
||||
cg.LDR(X2, pc_after_svc);
|
||||
cg.STR(X2, X1, offsetof(GuestContext, pc));
|
||||
|
||||
@@ -514,24 +509,13 @@ void Patcher::WriteSvcTrampoline(ModuleDestLabel module_dest, u32 svc_id, oaknut
|
||||
|
||||
// Host called this location. Save the return address so we can
|
||||
// unwind the stack properly when jumping back.
|
||||
cg.MRS(X2, oaknut::SystemReg::TPIDR_EL0);
|
||||
cg.LDR(X2, X2, offsetof(NativeExecutionParameters, native_context));
|
||||
cg.ADD(X0, X2, offsetof(GuestContext, host_ctx));
|
||||
cg.ADD(X0, X1, offsetof(GuestContext, host_ctx));
|
||||
cg.STR(X30, X0, offsetof(HostContext, host_saved_regs) + 11 * sizeof(u64));
|
||||
|
||||
// Reload all guest registers except X30 and PC.
|
||||
// The function also expects 16 bytes of stack already allocated.
|
||||
cg.STR(X30, SP, PRE_INDEXED, -16);
|
||||
cg.STR(X1, SP, PRE_INDEXED, -16);
|
||||
cg.BL(load_ctx);
|
||||
cg.LDR(X30, SP, POST_INDEXED, 16);
|
||||
|
||||
// Use X1 as a scratch register to restore X30.
|
||||
cg.STR(X1, SP, PRE_INDEXED, -16);
|
||||
cg.MRS(X1, oaknut::SystemReg::TPIDR_EL0);
|
||||
cg.LDR(X1, X1, offsetof(NativeExecutionParameters, native_context));
|
||||
cg.LDR(X30, X1, offsetof(GuestContext, cpu_registers) + sizeof(u64) * 30);
|
||||
cg.LDR(X1, SP, POST_INDEXED, 16);
|
||||
|
||||
// Unlock the context.
|
||||
this->UnlockContext(cg);
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -29,11 +29,8 @@ public:
|
||||
|
||||
template <typename T>
|
||||
Common::PhysicalAddress GetPhysicalAddr(const T* ptr) const {
|
||||
return GetPhysicalAddr(reinterpret_cast<uintptr_t>(ptr));
|
||||
}
|
||||
|
||||
Common::PhysicalAddress GetPhysicalAddr(uintptr_t ptr) const {
|
||||
return (ptr - reinterpret_cast<uintptr_t>(buffer.BackingBasePointer())) +
|
||||
return (reinterpret_cast<uintptr_t>(ptr) -
|
||||
reinterpret_cast<uintptr_t>(buffer.BackingBasePointer())) +
|
||||
DramMemoryMap::Base;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/range_mutex.h"
|
||||
#include "common/scratch_buffer.h"
|
||||
#include "common/sparse_large_vector.h"
|
||||
#include "common/virtual_buffer.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
@@ -178,8 +178,8 @@ private:
|
||||
u32 continuity_tracker;
|
||||
u32 compressed_physical_ptr;
|
||||
};
|
||||
Common::SparseLargeVector<u32> compressed_device_addr;
|
||||
Common::SparseLargeVector<TrackedEntry> tracked_entries;
|
||||
Common::VirtualBuffer<u32> compressed_device_addr;
|
||||
Common::VirtualBuffer<TrackedEntry> tracked_entries;
|
||||
|
||||
// Process memory interfaces
|
||||
|
||||
@@ -200,8 +200,8 @@ private:
|
||||
return std::make_pair(asid, address);
|
||||
}
|
||||
|
||||
constexpr void InsertCPUBacking(size_t page_index, VAddr address, Asid asid) {
|
||||
tracked_entries.GetUnchecked(page_index).cpu_backing_address = address | (asid.id << asid_start_bit);
|
||||
void InsertCPUBacking(size_t page_index, VAddr address, Asid asid) {
|
||||
tracked_entries[page_index].cpu_backing_address = address | (asid.id << asid_start_bit);
|
||||
}
|
||||
|
||||
std::array<TranslationEntry, 4> t_slot{};
|
||||
|
||||
@@ -177,6 +177,17 @@ DeviceMemoryManager<Traits>::DeviceMemoryManager(const DeviceMemory& device_memo
|
||||
{
|
||||
impl = std::make_unique<DeviceMemoryManagerAllocator<Traits>>();
|
||||
cached_pages = std::make_unique<CachedPages>();
|
||||
|
||||
const size_t total_virtual = device_as_size >> Memory::YUZU_PAGEBITS;
|
||||
for (size_t i = 0; i < total_virtual; i++) {
|
||||
tracked_entries[i].compressed_physical_ptr = 0;
|
||||
tracked_entries[i].continuity_tracker = 1;
|
||||
tracked_entries[i].cpu_backing_address = 0;
|
||||
}
|
||||
const size_t total_phys = 1ULL << ((Settings::values.memory_layout_mode.GetValue() == Settings::MemoryLayout::Memory_4Gb ? physical_min_bits : physical_max_bits) - Memory::YUZU_PAGEBITS);
|
||||
for (size_t i = 0; i < total_phys; i++) {
|
||||
compressed_device_addr[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Traits>
|
||||
@@ -209,28 +220,26 @@ void DeviceMemoryManager<Traits>::Map(DAddr address, VAddr virtual_address, size
|
||||
size_t start_page_d = address >> Memory::YUZU_PAGEBITS;
|
||||
size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS;
|
||||
std::scoped_lock lk(mapping_guard);
|
||||
|
||||
tracked_entries.CommitRegion(start_page_d, start_page_d + num_pages);
|
||||
for (size_t i = 0; i < num_pages; i++) {
|
||||
const VAddr new_vaddress = virtual_address + i * Memory::YUZU_PAGESIZE;
|
||||
auto* ptr = process_memory->GetPointerSilent(Common::ProcessAddress(new_vaddress));
|
||||
if (ptr == nullptr) [[unlikely]] {
|
||||
tracked_entries.GetUnchecked(start_page_d + i).compressed_physical_ptr = 0;
|
||||
tracked_entries[start_page_d + i].compressed_physical_ptr = 0;
|
||||
continue;
|
||||
}
|
||||
auto phys_addr = static_cast<u32>(GetRawPhysicalAddr(ptr) >> Memory::YUZU_PAGEBITS) + 1U;
|
||||
tracked_entries.GetUnchecked(start_page_d + i).compressed_physical_ptr = phys_addr;
|
||||
tracked_entries[start_page_d + i].compressed_physical_ptr = phys_addr;
|
||||
InsertCPUBacking(start_page_d + i, new_vaddress, asid);
|
||||
const u32 base_dev = compressed_device_addr[phys_addr - 1U];
|
||||
const u32 new_dev = static_cast<u32>(start_page_d + i);
|
||||
if (base_dev == 0) [[likely]] {
|
||||
compressed_device_addr.GetAndFault(phys_addr - 1U) = new_dev;
|
||||
compressed_device_addr[phys_addr - 1U] = new_dev;
|
||||
continue;
|
||||
}
|
||||
u32 start_id = base_dev & MULTI_MASK;
|
||||
if ((base_dev >> MULTI_FLAG_BITS) == 0) {
|
||||
start_id = impl->multi_dev_address.Register(base_dev);
|
||||
compressed_device_addr.GetAndFault(phys_addr - 1U) = MULTI_FLAG | start_id;
|
||||
compressed_device_addr[phys_addr - 1U] = MULTI_FLAG | start_id;
|
||||
}
|
||||
impl->multi_dev_address.Register(new_dev, start_id);
|
||||
}
|
||||
@@ -246,26 +255,24 @@ void DeviceMemoryManager<Traits>::Unmap(DAddr address, size_t size) {
|
||||
size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS;
|
||||
device_inter->InvalidateRegion(address, size);
|
||||
std::scoped_lock lk(mapping_guard);
|
||||
|
||||
tracked_entries.CommitRegion(start_page_d, start_page_d + num_pages); // should already be committed, but just in case
|
||||
for (size_t i = 0; i < num_pages; i++) {
|
||||
auto& entry = tracked_entries.GetUnchecked(start_page_d + i);
|
||||
auto phys_addr = entry.compressed_physical_ptr;
|
||||
entry.compressed_physical_ptr = 0;
|
||||
entry.cpu_backing_address = 0;
|
||||
auto phys_addr = tracked_entries[start_page_d + i].compressed_physical_ptr;
|
||||
tracked_entries[start_page_d + i].compressed_physical_ptr = 0;
|
||||
tracked_entries[start_page_d + i].cpu_backing_address = 0;
|
||||
if (phys_addr != 0) [[likely]] {
|
||||
u32& base_dev = compressed_device_addr.GetAndFault(phys_addr - 1U);
|
||||
const u32 base_dev = compressed_device_addr[phys_addr - 1U];
|
||||
if ((base_dev >> MULTI_FLAG_BITS) == 0) [[likely]] {
|
||||
base_dev = 0;
|
||||
compressed_device_addr[phys_addr - 1] = 0;
|
||||
continue;
|
||||
}
|
||||
const auto [more_entries, new_start] = impl->multi_dev_address.Unregister(
|
||||
static_cast<u32>(start_page_d + i), base_dev & MULTI_MASK);
|
||||
if (!more_entries) {
|
||||
base_dev = impl->multi_dev_address.ReleaseEntry(new_start);
|
||||
compressed_device_addr[phys_addr - 1] =
|
||||
impl->multi_dev_address.ReleaseEntry(new_start);
|
||||
continue;
|
||||
}
|
||||
base_dev = new_start | MULTI_FLAG;
|
||||
compressed_device_addr[phys_addr - 1] = new_start | MULTI_FLAG;
|
||||
}
|
||||
}
|
||||
t_slot = {};
|
||||
@@ -278,8 +285,6 @@ void DeviceMemoryManager<Traits>::TrackContinuityImpl(DAddr address, VAddr virtu
|
||||
size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS;
|
||||
uintptr_t last_ptr = 0;
|
||||
size_t page_count = 1;
|
||||
|
||||
tracked_entries.CommitRegion(start_page_d, start_page_d + num_pages);
|
||||
for (size_t i = num_pages; i > 0; i--) {
|
||||
size_t index = i - 1;
|
||||
const VAddr new_vaddress = virtual_address + index * Memory::YUZU_PAGESIZE;
|
||||
@@ -291,14 +296,14 @@ void DeviceMemoryManager<Traits>::TrackContinuityImpl(DAddr address, VAddr virtu
|
||||
page_count = 1;
|
||||
}
|
||||
last_ptr = new_ptr;
|
||||
tracked_entries.GetUnchecked(start_page_d + index).continuity_tracker = static_cast<u32>(page_count) - 1;
|
||||
tracked_entries[start_page_d + index].continuity_tracker = static_cast<u32>(page_count);
|
||||
}
|
||||
}
|
||||
template <typename Traits>
|
||||
u8* DeviceMemoryManager<Traits>::GetSpan(const DAddr src_addr, const std::size_t size) {
|
||||
size_t page_index = src_addr >> page_bits;
|
||||
size_t subbits = src_addr & page_mask;
|
||||
if ((static_cast<size_t>(tracked_entries[page_index].continuity_tracker+1) << page_bits) >= size + subbits) {
|
||||
if ((static_cast<size_t>(tracked_entries[page_index].continuity_tracker) << page_bits) >= size + subbits) {
|
||||
return GetPointer<u8>(src_addr);
|
||||
}
|
||||
return nullptr;
|
||||
@@ -308,7 +313,7 @@ template <typename Traits>
|
||||
const u8* DeviceMemoryManager<Traits>::GetSpan(const DAddr src_addr, const std::size_t size) const {
|
||||
size_t page_index = src_addr >> page_bits;
|
||||
size_t subbits = src_addr & page_mask;
|
||||
if ((static_cast<size_t>(tracked_entries[page_index].continuity_tracker+1) << page_bits) >= size + subbits) {
|
||||
if ((static_cast<size_t>(tracked_entries[page_index].continuity_tracker) << page_bits) >= size + subbits) {
|
||||
return GetPointer<u8>(src_addr);
|
||||
}
|
||||
return nullptr;
|
||||
@@ -378,7 +383,7 @@ void DeviceMemoryManager<Traits>::WalkBlock(DAddr addr, std::size_t size, auto o
|
||||
std::size_t page_index = addr >> Memory::YUZU_PAGEBITS;
|
||||
std::size_t page_offset = addr & Memory::YUZU_PAGEMASK;
|
||||
while (remaining_size) {
|
||||
const size_t next_pages = std::size_t(tracked_entries[page_index].continuity_tracker+1);
|
||||
const size_t next_pages = std::size_t(tracked_entries[page_index].continuity_tracker);
|
||||
const std::size_t copy_amount = (std::min)((next_pages << Memory::YUZU_PAGEBITS) - page_offset, remaining_size);
|
||||
const auto current_vaddr = u64((page_index << Memory::YUZU_PAGEBITS) + page_offset);
|
||||
SCOPE_EXIT{
|
||||
|
||||
@@ -635,36 +635,6 @@ Result KPageTableBase::CheckMemoryState(const KMemoryInfo& info, KMemoryState st
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
bool KPageTableBase::BeginTraversal(const Common::PageTable &impl, TraversalEntry *out_entry, TraversalContext *out_context,
|
||||
Common::ProcessAddress address) const {
|
||||
out_context->next_offset = GetInteger(address);
|
||||
out_context->next_page = GetInteger(address) >> PageBits;
|
||||
|
||||
return ContinueTraversal(impl, out_entry, out_context);
|
||||
}
|
||||
|
||||
bool KPageTableBase::ContinueTraversal(const Common::PageTable &impl, TraversalEntry *out_entry,
|
||||
TraversalContext *context) const {
|
||||
// Setup invalid defaults.
|
||||
out_entry->phys_addr = 0;
|
||||
out_entry->block_size = PageSize;
|
||||
// Validate that we can read the actual entry.
|
||||
if (auto const page = context->next_page; page < impl.entries.size()) {
|
||||
// Validate that the entry is mapped.
|
||||
if (auto const paddr = impl.entries[page].Pointer(true); paddr != 0) {
|
||||
// Populate the results and return true
|
||||
out_entry->phys_addr = GetInteger(m_system.DeviceMemory().GetPhysicalAddr(paddr + context->next_offset));
|
||||
context->next_page += 1;
|
||||
context->next_offset += PageSize;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
context->next_page += 1;
|
||||
context->next_offset += PageSize;
|
||||
// Otherwise return false
|
||||
return false;
|
||||
}
|
||||
|
||||
Result KPageTableBase::CheckMemoryStateContiguous(size_t* out_blocks_needed, KProcessAddress addr,
|
||||
size_t size, KMemoryState state_mask,
|
||||
KMemoryState state, KMemoryPermission perm_mask,
|
||||
@@ -970,7 +940,7 @@ Result KPageTableBase::QueryMappingImpl(KProcessAddress* out, KPhysicalAddress a
|
||||
size_t tot_size = 0;
|
||||
|
||||
next_valid =
|
||||
BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), region_start);
|
||||
impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), region_start);
|
||||
next_entry.block_size =
|
||||
(next_entry.block_size - (GetInteger(region_start) & (next_entry.block_size - 1)));
|
||||
|
||||
@@ -1006,7 +976,7 @@ Result KPageTableBase::QueryMappingImpl(KProcessAddress* out, KPhysicalAddress a
|
||||
break;
|
||||
}
|
||||
|
||||
next_valid = ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context));
|
||||
next_valid = impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
|
||||
}
|
||||
|
||||
// Check the last entry.
|
||||
@@ -1784,7 +1754,7 @@ Result KPageTableBase::MakePageGroup(KPageGroup& pg, KProcessAddress addr, size_
|
||||
// Begin traversal.
|
||||
TraversalContext context;
|
||||
TraversalEntry next_entry;
|
||||
R_UNLESS(BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), addr),
|
||||
R_UNLESS(impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), addr),
|
||||
ResultInvalidCurrentMemory);
|
||||
|
||||
// Prepare tracking variables.
|
||||
@@ -1794,7 +1764,7 @@ Result KPageTableBase::MakePageGroup(KPageGroup& pg, KProcessAddress addr, size_
|
||||
|
||||
// Iterate, adding to group as we go.
|
||||
while (tot_size < size) {
|
||||
R_UNLESS(ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context)),
|
||||
R_UNLESS(impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)),
|
||||
ResultInvalidCurrentMemory);
|
||||
|
||||
if (next_entry.phys_addr != (cur_addr + cur_size)) {
|
||||
@@ -1858,7 +1828,7 @@ bool KPageTableBase::IsValidPageGroup(const KPageGroup& pg, KProcessAddress addr
|
||||
// Begin traversal.
|
||||
TraversalContext context;
|
||||
TraversalEntry next_entry;
|
||||
if (!BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), addr)) {
|
||||
if (!impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), addr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1869,7 +1839,7 @@ bool KPageTableBase::IsValidPageGroup(const KPageGroup& pg, KProcessAddress addr
|
||||
|
||||
// Iterate, comparing expected to actual.
|
||||
while (tot_size < size) {
|
||||
if (!ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context))) {
|
||||
if (!impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1926,7 +1896,7 @@ Result KPageTableBase::GetContiguousMemoryRangeWithState(
|
||||
// Begin a traversal.
|
||||
TraversalContext context;
|
||||
TraversalEntry cur_entry = {.phys_addr = 0, .block_size = 0};
|
||||
R_UNLESS(BeginTraversal(impl, std::addressof(cur_entry), std::addressof(context), address),
|
||||
R_UNLESS(impl.BeginTraversal(std::addressof(cur_entry), std::addressof(context), address),
|
||||
ResultInvalidCurrentMemory);
|
||||
|
||||
// Traverse until we have enough size or we aren't contiguous any more.
|
||||
@@ -1935,7 +1905,7 @@ Result KPageTableBase::GetContiguousMemoryRangeWithState(
|
||||
for (contig_size =
|
||||
cur_entry.block_size - (GetInteger(phys_address) & (cur_entry.block_size - 1));
|
||||
contig_size < size; contig_size += cur_entry.block_size) {
|
||||
if (!ContinueTraversal(impl, std::addressof(cur_entry), std::addressof(context))) {
|
||||
if (!impl.ContinueTraversal(std::addressof(cur_entry), std::addressof(context))) {
|
||||
break;
|
||||
}
|
||||
if (cur_entry.phys_addr != phys_address + contig_size) {
|
||||
@@ -2364,7 +2334,7 @@ Result KPageTableBase::QueryPhysicalAddress(Svc::lp64::PhysicalMemoryInfo* out,
|
||||
TraversalContext context;
|
||||
TraversalEntry next_entry;
|
||||
bool traverse_valid =
|
||||
BeginTraversal(m_impl, std::addressof(next_entry), std::addressof(context), virt_addr);
|
||||
m_impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), virt_addr);
|
||||
R_UNLESS(traverse_valid, ResultInvalidCurrentMemory);
|
||||
|
||||
// Set tracking variables.
|
||||
@@ -2375,7 +2345,7 @@ Result KPageTableBase::QueryPhysicalAddress(Svc::lp64::PhysicalMemoryInfo* out,
|
||||
while (true) {
|
||||
// Continue the traversal.
|
||||
traverse_valid =
|
||||
ContinueTraversal(m_impl, std::addressof(next_entry), std::addressof(context));
|
||||
m_impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
|
||||
if (!traverse_valid) {
|
||||
break;
|
||||
}
|
||||
@@ -2597,7 +2567,7 @@ Result KPageTableBase::UnmapIoRegion(KProcessAddress dst_address, KPhysicalAddre
|
||||
TraversalContext context;
|
||||
TraversalEntry next_entry;
|
||||
ASSERT(
|
||||
BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), dst_address));
|
||||
impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), dst_address));
|
||||
|
||||
// Check that the physical region matches.
|
||||
R_UNLESS(next_entry.phys_addr == phys_addr, ResultInvalidMemoryRegion);
|
||||
@@ -2607,7 +2577,7 @@ Result KPageTableBase::UnmapIoRegion(KProcessAddress dst_address, KPhysicalAddre
|
||||
next_entry.block_size - (GetInteger(phys_addr) & (next_entry.block_size - 1));
|
||||
checked_size < size; checked_size += next_entry.block_size) {
|
||||
// Continue the traversal.
|
||||
ASSERT(ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context)));
|
||||
ASSERT(impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)));
|
||||
|
||||
// Check that the physical region matches.
|
||||
R_UNLESS(next_entry.phys_addr == phys_addr + checked_size, ResultInvalidMemoryRegion);
|
||||
@@ -3059,7 +3029,7 @@ Result KPageTableBase::InvalidateProcessDataCache(KProcessAddress address, size_
|
||||
TraversalContext context;
|
||||
TraversalEntry next_entry;
|
||||
bool traverse_valid =
|
||||
BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), address);
|
||||
impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), address);
|
||||
R_UNLESS(traverse_valid, ResultInvalidCurrentMemory);
|
||||
|
||||
// Prepare tracking variables.
|
||||
@@ -3071,7 +3041,7 @@ Result KPageTableBase::InvalidateProcessDataCache(KProcessAddress address, size_
|
||||
while (tot_size < size) {
|
||||
// Continue the traversal.
|
||||
traverse_valid =
|
||||
ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context));
|
||||
impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
|
||||
R_UNLESS(traverse_valid, ResultInvalidCurrentMemory);
|
||||
|
||||
if (next_entry.phys_addr != (cur_addr + cur_size)) {
|
||||
@@ -3159,7 +3129,7 @@ Result KPageTableBase::ReadDebugMemory(KProcessAddress dst_address, KProcessAddr
|
||||
TraversalContext context;
|
||||
TraversalEntry next_entry;
|
||||
bool traverse_valid =
|
||||
BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), src_address);
|
||||
impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), src_address);
|
||||
R_UNLESS(traverse_valid, ResultInvalidCurrentMemory);
|
||||
|
||||
// Prepare tracking variables.
|
||||
@@ -3197,7 +3167,7 @@ Result KPageTableBase::ReadDebugMemory(KProcessAddress dst_address, KProcessAddr
|
||||
while (tot_size < size) {
|
||||
// Continue the traversal.
|
||||
traverse_valid =
|
||||
ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context));
|
||||
impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
if (next_entry.phys_addr != (cur_addr + cur_size)) {
|
||||
@@ -3255,7 +3225,7 @@ Result KPageTableBase::WriteDebugMemory(KProcessAddress dst_address, KProcessAdd
|
||||
TraversalContext context;
|
||||
TraversalEntry next_entry;
|
||||
bool traverse_valid =
|
||||
BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), dst_address);
|
||||
impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), dst_address);
|
||||
R_UNLESS(traverse_valid, ResultInvalidCurrentMemory);
|
||||
|
||||
// Prepare tracking variables.
|
||||
@@ -3297,7 +3267,7 @@ Result KPageTableBase::WriteDebugMemory(KProcessAddress dst_address, KProcessAdd
|
||||
while (tot_size < size) {
|
||||
// Continue the traversal.
|
||||
traverse_valid =
|
||||
ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context));
|
||||
impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
if (next_entry.phys_addr != (cur_addr + cur_size)) {
|
||||
@@ -3758,7 +3728,7 @@ Result KPageTableBase::CopyMemoryFromLinearToUser(
|
||||
TraversalContext context;
|
||||
TraversalEntry next_entry;
|
||||
bool traverse_valid =
|
||||
BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), src_addr);
|
||||
impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), src_addr);
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
// Prepare tracking variables.
|
||||
@@ -3798,7 +3768,7 @@ Result KPageTableBase::CopyMemoryFromLinearToUser(
|
||||
while (tot_size < size) {
|
||||
// Continue the traversal.
|
||||
traverse_valid =
|
||||
ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context));
|
||||
impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
if (next_entry.phys_addr != (cur_addr + cur_size)) {
|
||||
@@ -3852,7 +3822,7 @@ Result KPageTableBase::CopyMemoryFromLinearToKernel(
|
||||
TraversalContext context;
|
||||
TraversalEntry next_entry;
|
||||
bool traverse_valid =
|
||||
BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), src_addr);
|
||||
impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), src_addr);
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
// Prepare tracking variables.
|
||||
@@ -3875,7 +3845,7 @@ Result KPageTableBase::CopyMemoryFromLinearToKernel(
|
||||
while (tot_size < size) {
|
||||
// Continue the traversal.
|
||||
traverse_valid =
|
||||
ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context));
|
||||
impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
if (next_entry.phys_addr != (cur_addr + cur_size)) {
|
||||
@@ -3932,7 +3902,7 @@ Result KPageTableBase::CopyMemoryFromUserToLinear(
|
||||
TraversalContext context;
|
||||
TraversalEntry next_entry;
|
||||
bool traverse_valid =
|
||||
BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), dst_addr);
|
||||
impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), dst_addr);
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
// Prepare tracking variables.
|
||||
@@ -3971,7 +3941,7 @@ Result KPageTableBase::CopyMemoryFromUserToLinear(
|
||||
while (tot_size < size) {
|
||||
// Continue the traversal.
|
||||
traverse_valid =
|
||||
ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context));
|
||||
impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
if (next_entry.phys_addr != (cur_addr + cur_size)) {
|
||||
@@ -4027,7 +3997,7 @@ Result KPageTableBase::CopyMemoryFromKernelToLinear(KProcessAddress dst_addr, si
|
||||
TraversalContext context;
|
||||
TraversalEntry next_entry;
|
||||
bool traverse_valid =
|
||||
BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), dst_addr);
|
||||
impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), dst_addr);
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
// Prepare tracking variables.
|
||||
@@ -4050,7 +4020,7 @@ Result KPageTableBase::CopyMemoryFromKernelToLinear(KProcessAddress dst_addr, si
|
||||
while (tot_size < size) {
|
||||
// Continue the traversal.
|
||||
traverse_valid =
|
||||
ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context));
|
||||
impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
if (next_entry.phys_addr != (cur_addr + cur_size)) {
|
||||
@@ -4119,10 +4089,10 @@ Result KPageTableBase::CopyMemoryFromHeapToHeap(
|
||||
bool traverse_valid;
|
||||
|
||||
// Begin traversal.
|
||||
traverse_valid = BeginTraversal(src_impl, std::addressof(src_next_entry),
|
||||
traverse_valid = src_impl.BeginTraversal(std::addressof(src_next_entry),
|
||||
std::addressof(src_context), src_addr);
|
||||
ASSERT(traverse_valid);
|
||||
traverse_valid = BeginTraversal(dst_impl, std::addressof(dst_next_entry),
|
||||
traverse_valid = dst_impl.BeginTraversal(std::addressof(dst_next_entry),
|
||||
std::addressof(dst_context), dst_addr);
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
@@ -4157,7 +4127,7 @@ Result KPageTableBase::CopyMemoryFromHeapToHeap(
|
||||
if (ofs + cur_copy_size != size) {
|
||||
if (cur_src_addr + cur_min_size == cur_src_block_addr + cur_src_size) {
|
||||
// Continue the src traversal.
|
||||
traverse_valid = ContinueTraversal(src_impl, std::addressof(src_next_entry),
|
||||
traverse_valid = src_impl.ContinueTraversal(std::addressof(src_next_entry),
|
||||
std::addressof(src_context));
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
@@ -4168,7 +4138,7 @@ Result KPageTableBase::CopyMemoryFromHeapToHeap(
|
||||
if (cur_dst_addr + cur_min_size ==
|
||||
dst_next_entry.phys_addr + dst_next_entry.block_size) {
|
||||
// Continue the dst traversal.
|
||||
traverse_valid = ContinueTraversal(dst_impl, std::addressof(dst_next_entry),
|
||||
traverse_valid = dst_impl.ContinueTraversal(std::addressof(dst_next_entry),
|
||||
std::addressof(dst_context));
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
@@ -4253,10 +4223,10 @@ Result KPageTableBase::CopyMemoryFromHeapToHeapWithoutCheckDestination(
|
||||
bool traverse_valid;
|
||||
|
||||
// Begin traversal.
|
||||
traverse_valid = BeginTraversal(src_impl, std::addressof(src_next_entry),
|
||||
traverse_valid = src_impl.BeginTraversal(std::addressof(src_next_entry),
|
||||
std::addressof(src_context), src_addr);
|
||||
ASSERT(traverse_valid);
|
||||
traverse_valid = BeginTraversal(dst_impl, std::addressof(dst_next_entry),
|
||||
traverse_valid = dst_impl.BeginTraversal(std::addressof(dst_next_entry),
|
||||
std::addressof(dst_context), dst_addr);
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
@@ -4291,7 +4261,7 @@ Result KPageTableBase::CopyMemoryFromHeapToHeapWithoutCheckDestination(
|
||||
if (ofs + cur_copy_size != size) {
|
||||
if (cur_src_addr + cur_min_size == cur_src_block_addr + cur_src_size) {
|
||||
// Continue the src traversal.
|
||||
traverse_valid = ContinueTraversal(src_impl, std::addressof(src_next_entry),
|
||||
traverse_valid = src_impl.ContinueTraversal(std::addressof(src_next_entry),
|
||||
std::addressof(src_context));
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
@@ -4302,7 +4272,7 @@ Result KPageTableBase::CopyMemoryFromHeapToHeapWithoutCheckDestination(
|
||||
if (cur_dst_addr + cur_min_size ==
|
||||
dst_next_entry.phys_addr + dst_next_entry.block_size) {
|
||||
// Continue the dst traversal.
|
||||
traverse_valid = ContinueTraversal(dst_impl, std::addressof(dst_next_entry),
|
||||
traverse_valid = dst_impl.ContinueTraversal(std::addressof(dst_next_entry),
|
||||
std::addressof(dst_context));
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
@@ -4577,7 +4547,7 @@ Result KPageTableBase::SetupForIpcServer(KProcessAddress* out_addr, size_t size,
|
||||
// Begin traversal.
|
||||
TraversalContext context;
|
||||
TraversalEntry next_entry;
|
||||
bool traverse_valid = BeginTraversal(src_impl, std::addressof(next_entry),
|
||||
bool traverse_valid = src_impl.BeginTraversal(std::addressof(next_entry),
|
||||
std::addressof(context), aligned_src_start);
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
@@ -4627,7 +4597,7 @@ Result KPageTableBase::SetupForIpcServer(KProcessAddress* out_addr, size_t size,
|
||||
// If the block's size was one page, we may need to continue traversal.
|
||||
if (cur_block_size == 0 && aligned_src_size > PageSize) {
|
||||
traverse_valid =
|
||||
ContinueTraversal(src_impl, std::addressof(next_entry), std::addressof(context));
|
||||
src_impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
cur_block_addr = next_entry.phys_addr;
|
||||
@@ -4640,7 +4610,7 @@ Result KPageTableBase::SetupForIpcServer(KProcessAddress* out_addr, size_t size,
|
||||
while (aligned_src_start + tot_block_size < mapping_src_end) {
|
||||
// Continue the traversal.
|
||||
traverse_valid =
|
||||
ContinueTraversal(src_impl, std::addressof(next_entry), std::addressof(context));
|
||||
src_impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
// Process the block.
|
||||
@@ -4683,7 +4653,7 @@ Result KPageTableBase::SetupForIpcServer(KProcessAddress* out_addr, size_t size,
|
||||
if (mapped_block_end + cur_block_size < aligned_src_end &&
|
||||
cur_block_size == last_block_size) {
|
||||
traverse_valid =
|
||||
ContinueTraversal(src_impl, std::addressof(next_entry), std::addressof(context));
|
||||
src_impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
cur_block_addr = next_entry.phys_addr;
|
||||
@@ -5631,7 +5601,7 @@ Result KPageTableBase::UnmapProcessMemory(KProcessAddress dst_address, size_t si
|
||||
ContiguousRangeInfo(KPageTableBase& pt, KProcessAddress address, size_t size)
|
||||
: m_pt(pt), m_remaining_size(size) {
|
||||
// Begin a traversal.
|
||||
ASSERT(m_pt.BeginTraversal(m_pt.GetImpl(), std::addressof(m_entry),
|
||||
ASSERT(m_pt.GetImpl().BeginTraversal(std::addressof(m_entry),
|
||||
std::addressof(m_context), address));
|
||||
|
||||
// Setup tracking fields.
|
||||
@@ -5662,7 +5632,7 @@ Result KPageTableBase::UnmapProcessMemory(KProcessAddress dst_address, size_t si
|
||||
void DetermineContiguousBlockExtents() {
|
||||
// Continue traversing until we're not contiguous, or we have enough.
|
||||
while (m_cur_size < m_remaining_size) {
|
||||
ASSERT(m_pt.ContinueTraversal(m_pt.GetImpl(), std::addressof(m_entry),
|
||||
ASSERT(m_pt.GetImpl().ContinueTraversal(std::addressof(m_entry),
|
||||
std::addressof(m_context)));
|
||||
|
||||
// If we're not contiguous, we're done.
|
||||
|
||||
@@ -370,10 +370,6 @@ private:
|
||||
size_t num_pages, size_t alignment, size_t offset,
|
||||
size_t guard_pages) const;
|
||||
|
||||
bool BeginTraversal(const Common::PageTable& impl, TraversalEntry* out_entry, TraversalContext* out_context,
|
||||
Common::ProcessAddress address) const;
|
||||
bool ContinueTraversal(const Common::PageTable& impl, TraversalEntry* out_entry, TraversalContext* context) const;
|
||||
|
||||
Result CheckMemoryStateContiguous(size_t* out_blocks_needed, KProcessAddress addr, size_t size,
|
||||
KMemoryState state_mask, KMemoryState state,
|
||||
KMemoryPermission perm_mask, KMemoryPermission perm,
|
||||
@@ -478,14 +474,7 @@ private:
|
||||
// Validate pre-conditions.
|
||||
ASSERT(this->IsLockedByCurrentThread());
|
||||
|
||||
if (virt_addr > (1ULL << m_address_space_width)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*out = m_system.DeviceMemory().GetPhysicalAddr(
|
||||
this->GetImpl().entries[GetInteger(virt_addr) >> PageBits].Pointer(true) + GetInteger(virt_addr));
|
||||
|
||||
return true;
|
||||
return this->GetImpl().GetPhysicalAddress(out, virt_addr);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
+39
-42
@@ -101,10 +101,8 @@ struct Memory::Impl {
|
||||
}
|
||||
|
||||
u64 protect_bytes = 0, protect_begin = 0;
|
||||
|
||||
current_page_table->entries.CommitRegion(vaddr >> YUZU_PAGEBITS, (vaddr + size) >> YUZU_PAGEBITS);
|
||||
for (u64 addr = vaddr; addr < vaddr + size; addr += YUZU_PAGESIZE) {
|
||||
const Common::PageType page_type = current_page_table->entries.GetUnchecked(addr >> YUZU_PAGEBITS).Type();
|
||||
const Common::PageType page_type = current_page_table->entries[addr >> YUZU_PAGEBITS].ptr.Type();
|
||||
switch (page_type) {
|
||||
case Common::PageType::RasterizerCachedMemory:
|
||||
if (protect_bytes > 0) {
|
||||
@@ -125,14 +123,16 @@ struct Memory::Impl {
|
||||
}
|
||||
|
||||
[[nodiscard]] u8* GetPointerFromRasterizerCachedMemory(u64 vaddr) const {
|
||||
if (u64 paddr = current_page_table->entries[vaddr >> YUZU_PAGEBITS].Pointer(true); paddr)
|
||||
return reinterpret_cast<u8*>(paddr) + vaddr;
|
||||
Common::PhysicalAddress const paddr = current_page_table->entries[vaddr >> YUZU_PAGEBITS].addr;
|
||||
if (paddr)
|
||||
return system.DeviceMemory().GetPointer<u8>(paddr + vaddr);
|
||||
return {};
|
||||
}
|
||||
|
||||
[[nodiscard]] u8* GetPointerFromDebugMemory(u64 vaddr) const {
|
||||
if (u64 paddr = current_page_table->entries[vaddr >> YUZU_PAGEBITS].Pointer(true); paddr)
|
||||
return reinterpret_cast<u8*>(paddr) + vaddr;
|
||||
const Common::PhysicalAddress paddr = current_page_table->entries[vaddr >> YUZU_PAGEBITS].addr;
|
||||
if (paddr != 0)
|
||||
return system.DeviceMemory().GetPointer<u8>(paddr + vaddr);
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -243,12 +243,10 @@ struct Memory::Impl {
|
||||
std::size_t page_index = addr >> YUZU_PAGEBITS;
|
||||
std::size_t page_offset = addr & YUZU_PAGEMASK;
|
||||
bool user_accessible = true;
|
||||
|
||||
current_page_table->entries.CommitRegion(page_index, page_index + (size >> YUZU_PAGEBITS) + 1);
|
||||
while (remaining_size != 0) {
|
||||
const std::size_t copy_amount = (std::min)(std::size_t(YUZU_PAGESIZE) - page_offset, remaining_size);
|
||||
const auto current_vaddr = u64((page_index << YUZU_PAGEBITS) + page_offset);
|
||||
const auto [pointer, type, _] = current_page_table->entries.GetUnchecked(page_index).PointerTypeBlock();
|
||||
const auto [pointer, type] = current_page_table->entries[page_index].ptr.PointerType();
|
||||
switch (type) {
|
||||
case Common::PageType::Unmapped: {
|
||||
user_accessible = false;
|
||||
@@ -299,10 +297,10 @@ struct Memory::Impl {
|
||||
}
|
||||
|
||||
[[nodiscard]] inline const u8* GetSpan(const VAddr addr, const std::size_t size) const noexcept {
|
||||
return (current_page_table->entries[addr >> YUZU_PAGEBITS].Block() == current_page_table->entries[(addr + size) >> YUZU_PAGEBITS].Block()) ? GetPointerSilent(addr) : nullptr;
|
||||
return (current_page_table->entries[addr >> YUZU_PAGEBITS].block == current_page_table->entries[(addr + size) >> YUZU_PAGEBITS].block) ? GetPointerSilent(addr) : nullptr;
|
||||
}
|
||||
[[nodiscard]] inline u8* GetSpan(const VAddr addr, const std::size_t size) noexcept {
|
||||
return (current_page_table->entries[addr >> YUZU_PAGEBITS].Block() == current_page_table->entries[(addr + size) >> YUZU_PAGEBITS].Block()) ? GetPointerSilent(addr) : nullptr;
|
||||
return (current_page_table->entries[addr >> YUZU_PAGEBITS].block == current_page_table->entries[(addr + size) >> YUZU_PAGEBITS].block) ? GetPointerSilent(addr) : nullptr;
|
||||
}
|
||||
|
||||
bool WriteBlockImpl(const Common::ProcessAddress addr, const void* buffer, const std::size_t size, bool unsafe) {
|
||||
@@ -406,14 +404,11 @@ struct Memory::Impl {
|
||||
// The region is at a granularity of CPU pages.
|
||||
|
||||
const u64 num_pages = ((vaddr + size - 1) >> YUZU_PAGEBITS) - (vaddr >> YUZU_PAGEBITS) + 1;
|
||||
|
||||
current_page_table->entries.CommitRegion(vaddr >> YUZU_PAGEBITS, (vaddr >> YUZU_PAGEBITS) + num_pages);
|
||||
for (u64 i = 0; i < num_pages; ++i, vaddr += YUZU_PAGESIZE) {
|
||||
auto& entry = current_page_table->entries.GetUnchecked(vaddr >> YUZU_PAGEBITS);
|
||||
const auto [pointer, type, block] = entry.PointerTypeBlock(true);
|
||||
const Common::PageType page_type = current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Type();
|
||||
if (debug) {
|
||||
// Switch page type to debug if now debug
|
||||
switch (type) {
|
||||
switch (page_type) {
|
||||
case Common::PageType::Unmapped:
|
||||
ASSERT(false && "Attempted to mark unmapped pages as debug");
|
||||
break;
|
||||
@@ -422,14 +417,14 @@ struct Memory::Impl {
|
||||
// Page is already marked.
|
||||
break;
|
||||
case Common::PageType::Memory:
|
||||
entry.MarkDebug(pointer, block);
|
||||
current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Store(0, Common::PageType::DebugMemory);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
} else {
|
||||
// Switch page type to non-debug if now non-debug
|
||||
switch (type) {
|
||||
switch (page_type) {
|
||||
case Common::PageType::Unmapped:
|
||||
ASSERT(false && "Attempted to mark unmapped pages as non-debug");
|
||||
break;
|
||||
@@ -438,7 +433,8 @@ struct Memory::Impl {
|
||||
// Don't mess with already non-debug or rasterizer memory.
|
||||
break;
|
||||
case Common::PageType::DebugMemory: {
|
||||
entry.Store(false, Common::PageType::Memory, block, pointer);
|
||||
u8* const pointer = GetPointerFromDebugMemory(vaddr & ~YUZU_PAGEMASK);
|
||||
current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Store(uintptr_t(pointer) - (vaddr & ~YUZU_PAGEMASK), Common::PageType::Memory);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -470,10 +466,8 @@ struct Memory::Impl {
|
||||
// is different). This assumes the specified GPU address region is contiguous as well.
|
||||
|
||||
const u64 num_pages = ((vaddr + size - 1) >> YUZU_PAGEBITS) - (vaddr >> YUZU_PAGEBITS) + 1;
|
||||
current_page_table->entries.CommitRegion(vaddr >> YUZU_PAGEBITS, (vaddr >> YUZU_PAGEBITS) + num_pages);
|
||||
for (u64 i = 0; i < num_pages; ++i, vaddr += YUZU_PAGESIZE) {
|
||||
auto& entry = current_page_table->entries.GetUnchecked(vaddr >> YUZU_PAGEBITS);
|
||||
const Common::PageType page_type = entry.Type();
|
||||
const Common::PageType page_type= current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Type();
|
||||
if (cached) {
|
||||
// Switch page type to cached if now cached
|
||||
switch (page_type) {
|
||||
@@ -483,7 +477,7 @@ struct Memory::Impl {
|
||||
break;
|
||||
case Common::PageType::DebugMemory:
|
||||
case Common::PageType::Memory:
|
||||
entry.MarkRasterizerCached();
|
||||
current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Store(0, Common::PageType::RasterizerCachedMemory);
|
||||
break;
|
||||
case Common::PageType::RasterizerCachedMemory:
|
||||
// There can be more than one GPU region mapped per CPU region, so it's common
|
||||
@@ -505,13 +499,13 @@ struct Memory::Impl {
|
||||
// that this area is already unmarked as cached.
|
||||
break;
|
||||
case Common::PageType::RasterizerCachedMemory: {
|
||||
if (auto [ptr, _, block] = entry.PointerTypeBlock(true); ptr == 0) {
|
||||
if (u8* const pointer = GetPointerFromRasterizerCachedMemory(vaddr & ~YUZU_PAGEMASK); pointer == nullptr) {
|
||||
// It's possible that this function has been called while updating the
|
||||
// pagetable after unmapping a VMA. In that case the underlying VMA will no
|
||||
// longer exist, and we should just leave the pagetable entry blank.
|
||||
entry.Store(false, Common::PageType::Unmapped, block, 0);
|
||||
current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Store(0, Common::PageType::Unmapped);
|
||||
} else {
|
||||
entry.Store(false, Common::PageType::Memory, block, ptr);
|
||||
current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Store(uintptr_t(pointer) - (vaddr & ~YUZU_PAGEMASK), Common::PageType::Memory);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -545,18 +539,22 @@ struct Memory::Impl {
|
||||
ASSERT_MSG(type != Common::PageType::Memory,
|
||||
"Mapping memory page without a pointer @ {:016x}", base * YUZU_PAGESIZE);
|
||||
|
||||
page_table.entries.ZeroRegion(base, end);
|
||||
} else {
|
||||
auto current_block = block_count.fetch_add(1, std::memory_order_relaxed);
|
||||
ASSERT(current_block != 65535);
|
||||
|
||||
page_table.entries.CommitRegion(base, end);
|
||||
while (base != end) {
|
||||
auto host_ptr = reinterpret_cast<u64>(system.DeviceMemory().GetPointer<u8>(target)) - (base << YUZU_PAGEBITS);;
|
||||
auto& entry = page_table.entries.GetUnchecked(base);
|
||||
page_table.entries[base].ptr.Store(0, type);
|
||||
page_table.entries[base].addr = 0;
|
||||
page_table.entries[base].block = 0;
|
||||
base += 1;
|
||||
}
|
||||
} else {
|
||||
auto orig_base = base;
|
||||
while (base != end) {
|
||||
auto host_ptr = uintptr_t(system.DeviceMemory().GetPointer<u8>(target)) - (base << YUZU_PAGEBITS);
|
||||
auto backing = GetInteger(target) - (base << YUZU_PAGEBITS);
|
||||
page_table.entries[base].ptr.Store(host_ptr, type);
|
||||
page_table.entries[base].addr = backing;
|
||||
page_table.entries[base].block = orig_base << YUZU_PAGEBITS;
|
||||
|
||||
entry.Store(false, type, current_block, host_ptr);
|
||||
ASSERT_MSG(page_table.entries[base].Pointer(),
|
||||
ASSERT_MSG(page_table.entries[base].ptr.Pointer(),
|
||||
"memory mapping base yield a nullptr within the table");
|
||||
|
||||
base += 1;
|
||||
@@ -571,11 +569,11 @@ struct Memory::Impl {
|
||||
vaddr &= 0xffffffffffffULL;
|
||||
if (AddressSpaceContains(*current_page_table, vaddr, 1)) [[likely]] {
|
||||
// Avoid adding any extra logic to this fast-path block
|
||||
const auto raw = current_page_table->entries[vaddr >> YUZU_PAGEBITS].Raw();
|
||||
if (auto pointer = Common::PageTable::PageEntryData::ExtractPointer(raw); pointer) [[likely]] {
|
||||
const uintptr_t raw_pointer = current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Raw();
|
||||
if (const uintptr_t pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) [[likely]] {
|
||||
return reinterpret_cast<u8*>(pointer + vaddr);
|
||||
} else {
|
||||
switch (static_cast<Common::PageType>(raw.type)) {
|
||||
switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) {
|
||||
case Common::PageType::Memory:
|
||||
ASSERT_MSG(false, "Mapped memory page without a pointer @ {:#016x}", vaddr);
|
||||
return nullptr;
|
||||
@@ -775,7 +773,6 @@ struct Memory::Impl {
|
||||
#else
|
||||
Common::HostMemory* host_buffer{};
|
||||
#endif
|
||||
std::atomic<u16> block_count = 0;
|
||||
};
|
||||
|
||||
Memory::Memory(Core::System& system_) : system{system_} {
|
||||
@@ -814,7 +811,7 @@ bool Memory::IsValidVirtualAddress(const Common::ProcessAddress vaddr) const {
|
||||
if (page >= page_table.entries.size()) {
|
||||
return false;
|
||||
}
|
||||
const auto [pointer, type, _] = page_table.entries[page].PointerTypeBlock();
|
||||
const auto [pointer, type] = page_table.entries[page].ptr.PointerType();
|
||||
return pointer != 0 || type == Common::PageType::RasterizerCachedMemory ||
|
||||
type == Common::PageType::DebugMemory;
|
||||
}
|
||||
|
||||
@@ -371,10 +371,8 @@ EmitConfig A32AddressSpace::GetEmitConfig() {
|
||||
|
||||
.page_table_pointer = std::bit_cast<u64>(conf.page_table),
|
||||
.page_table_address_space_bits = 32,
|
||||
.page_table_pointer_mask = conf.page_table_pointer_mask,
|
||||
.page_table_pointer_mask_bits = conf.page_table_pointer_mask_bits,
|
||||
.page_table_log2_stride = conf.page_table_log2_stride,
|
||||
.page_table_marked_bit = conf.page_table_marked_bit,
|
||||
.page_table_sign_extension = conf.page_table_sign_extension,
|
||||
.silently_mirror_page_table = true,
|
||||
.absolute_offset_page_table = conf.absolute_offset_page_table,
|
||||
.detect_misaligned_access_via_page_table = conf.detect_misaligned_access_via_page_table,
|
||||
|
||||
@@ -545,10 +545,8 @@ EmitConfig A64AddressSpace::GetEmitConfig() {
|
||||
|
||||
.page_table_pointer = std::bit_cast<u64>(conf.page_table),
|
||||
.page_table_address_space_bits = conf.page_table_address_space_bits,
|
||||
.page_table_pointer_mask = conf.page_table_pointer_mask,
|
||||
.page_table_pointer_mask_bits = conf.page_table_pointer_mask_bits,
|
||||
.page_table_log2_stride = conf.page_table_log2_stride,
|
||||
.page_table_marked_bit = conf.page_table_marked_bit,
|
||||
.page_table_sign_extension = conf.page_table_sign_extension,
|
||||
.silently_mirror_page_table = conf.silently_mirror_page_table,
|
||||
.absolute_offset_page_table = conf.absolute_offset_page_table,
|
||||
.detect_misaligned_access_via_page_table = conf.detect_misaligned_access_via_page_table,
|
||||
|
||||
@@ -128,10 +128,8 @@ struct EmitConfig {
|
||||
// Page table
|
||||
u64 page_table_pointer;
|
||||
std::size_t page_table_address_space_bits;
|
||||
u64 page_table_pointer_mask;
|
||||
int page_table_pointer_mask_bits;
|
||||
std::size_t page_table_log2_stride;
|
||||
std::optional<std::uint8_t> page_table_marked_bit;
|
||||
std::optional<std::uint8_t> page_table_sign_extension;
|
||||
bool silently_mirror_page_table;
|
||||
bool absolute_offset_page_table;
|
||||
u8 detect_misaligned_access_via_page_table;
|
||||
|
||||
@@ -273,18 +273,9 @@ std::pair<oaknut::XReg, oaknut::XReg> InlinePageTableEmitVAddrLookup(oaknut::Cod
|
||||
// load x0 = *<(u8*)pagetable + index>
|
||||
code.LDR(Xscratch0, Xpagetable, Xscratch0);
|
||||
|
||||
if (ctx.conf.page_table_marked_bit) {
|
||||
code.TST(Xscratch0, 1ULL << *ctx.conf.page_table_marked_bit);
|
||||
code.B(NE, *fallback);
|
||||
}
|
||||
|
||||
if (ctx.conf.page_table_pointer_mask != 0) {
|
||||
code.AND(Xscratch0, Xscratch0, ctx.conf.page_table_pointer_mask);
|
||||
}
|
||||
|
||||
// TODO: combine this with page_table_pointer_mask
|
||||
if (ctx.conf.page_table_sign_extension) {
|
||||
code.SBFM(Xscratch0, Xscratch0, 0, *ctx.conf.page_table_sign_extension);
|
||||
if (ctx.conf.page_table_pointer_mask_bits != 0) {
|
||||
const u64 mask = u64(~u64(0)) << ctx.conf.page_table_pointer_mask_bits;
|
||||
code.AND(Xscratch0, Xscratch0, mask);
|
||||
}
|
||||
|
||||
code.CBZ(Xscratch0, *fallback);
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <bit>
|
||||
#include <utility>
|
||||
#include "dynarmic/backend/x64/xbyak.h"
|
||||
|
||||
#include "dynarmic/backend/x64/a32_emit_x64.h"
|
||||
@@ -79,46 +78,27 @@ Xbyak::RegExp EmitVAddrLookup(BlockOfCode& code, EmitContext& ctx, size_t bitsiz
|
||||
template<>
|
||||
[[maybe_unused]] Xbyak::RegExp EmitVAddrLookup<A32EmitContext>(BlockOfCode& code, A32EmitContext& ctx, size_t bitsize, Xbyak::Label& abort, Xbyak::Reg64 vaddr) {
|
||||
const Xbyak::Reg64 page = ctx.reg_alloc.ScratchGpr(code);
|
||||
const Xbyak::Reg64 tmp = ctx.conf.absolute_offset_page_table && ctx.conf.page_table_pointer_mask == 0 ? page : ctx.reg_alloc.ScratchGpr(code);
|
||||
const Xbyak::Reg32 tmp = ctx.conf.absolute_offset_page_table ? page.cvt32() : ctx.reg_alloc.ScratchGpr(code).cvt32();
|
||||
|
||||
EmitDetectMisalignedVAddr(code, ctx, bitsize, abort, vaddr, tmp);
|
||||
EmitDetectMisalignedVAddr(code, ctx, bitsize, abort, vaddr, tmp.cvt64());
|
||||
|
||||
code.mov(tmp, vaddr);
|
||||
// TODO: This code assumes vaddr has been zext from 32-bits to 64-bits.
|
||||
|
||||
code.mov(tmp, vaddr.cvt32());
|
||||
code.shr(tmp, int(page_table_const_bits));
|
||||
|
||||
if (ctx.conf.page_table_log2_stride > 3) {
|
||||
code.shl(tmp, int(ctx.conf.page_table_log2_stride));
|
||||
code.mov(page, qword[r14 + tmp.cvt64()]);
|
||||
} else {
|
||||
code.mov(page, qword[r14 + tmp.cvt64() * int(ctx.conf.page_table_log2_stride)]);
|
||||
}
|
||||
|
||||
// check for marked bit, use as unmapped if marked
|
||||
if (ctx.conf.page_table_marked_bit) {
|
||||
code.bt(page, *ctx.conf.page_table_marked_bit);
|
||||
code.jc(abort, code.T_NEAR);
|
||||
}
|
||||
// mask away attributes
|
||||
if (ctx.conf.page_table_pointer_mask == 0) {
|
||||
code.shl(tmp, int(ctx.conf.page_table_log2_stride));
|
||||
code.mov(page, qword[r14 + tmp.cvt64()]);
|
||||
if (ctx.conf.page_table_pointer_mask_bits == 0) {
|
||||
code.test(page, page);
|
||||
} else if (std::in_range<s32>(ctx.conf.page_table_pointer_mask)) {
|
||||
code.and_(page, ctx.conf.page_table_pointer_mask);
|
||||
} else {
|
||||
code.mov(tmp, ctx.conf.page_table_pointer_mask);
|
||||
code.and_(page, tmp);
|
||||
code.and_(page, ~u32(0) << ctx.conf.page_table_pointer_mask_bits);
|
||||
}
|
||||
// check for sign bit, apply sign extension as needed
|
||||
if (ctx.conf.page_table_sign_extension) {
|
||||
code.shl(page, 63 - int(*ctx.conf.page_table_sign_extension));
|
||||
code.sar(page, 63 - int(*ctx.conf.page_table_sign_extension));
|
||||
}
|
||||
|
||||
code.jz(abort, code.T_NEAR);
|
||||
if (ctx.conf.absolute_offset_page_table) {
|
||||
return page + vaddr;
|
||||
}
|
||||
code.mov(tmp, vaddr);
|
||||
code.and_(tmp, u32(page_table_const_mask));
|
||||
code.mov(tmp, vaddr.cvt32());
|
||||
code.and_(tmp, static_cast<u32>(page_table_const_mask));
|
||||
return page + tmp.cvt64();
|
||||
}
|
||||
|
||||
@@ -128,7 +108,7 @@ template<>
|
||||
const size_t unused_top_bits = 64 - ctx.conf.page_table_address_space_bits;
|
||||
|
||||
const Xbyak::Reg64 page = ctx.reg_alloc.ScratchGpr(code);
|
||||
const Xbyak::Reg64 tmp = ctx.conf.absolute_offset_page_table && ctx.conf.page_table_pointer_mask == 0 ? page : ctx.reg_alloc.ScratchGpr(code);
|
||||
const Xbyak::Reg64 tmp = ctx.conf.absolute_offset_page_table ? page : ctx.reg_alloc.ScratchGpr(code);
|
||||
|
||||
EmitDetectMisalignedVAddr(code, ctx, bitsize, abort, vaddr, tmp);
|
||||
|
||||
@@ -163,26 +143,11 @@ template<>
|
||||
|
||||
code.shl(tmp, int(ctx.conf.page_table_log2_stride));
|
||||
code.mov(page, qword[r14 + tmp]);
|
||||
|
||||
// check for marked bit, use as unmapped if marked
|
||||
if (ctx.conf.page_table_marked_bit) {
|
||||
code.bt(page, *ctx.conf.page_table_marked_bit);
|
||||
code.jc(abort, code.T_NEAR);
|
||||
}
|
||||
// mask away attributes
|
||||
if (ctx.conf.page_table_pointer_mask == 0) {
|
||||
if (ctx.conf.page_table_pointer_mask_bits == 0) {
|
||||
code.test(page, page);
|
||||
} else if (std::in_range<s32>(ctx.conf.page_table_pointer_mask)) {
|
||||
code.and_(page, ctx.conf.page_table_pointer_mask);
|
||||
} else {
|
||||
code.mov(tmp, ctx.conf.page_table_pointer_mask);
|
||||
code.and_(page, tmp);
|
||||
code.and_(page, ~u32(0) << ctx.conf.page_table_pointer_mask_bits);
|
||||
}
|
||||
if (ctx.conf.page_table_sign_extension) {
|
||||
code.shl(page, *ctx.conf.page_table_sign_extension);
|
||||
code.sar(page, *ctx.conf.page_table_sign_extension);
|
||||
}
|
||||
|
||||
code.jz(abort, code.T_NEAR);
|
||||
if (ctx.conf.absolute_offset_page_table) {
|
||||
return page + vaddr;
|
||||
|
||||
@@ -159,23 +159,14 @@ struct UserConfig {
|
||||
/// Maximum size is limited by the maximum length of a x86_64 / arm64 jump.
|
||||
std::uint32_t code_cache_size = 128 * 1024 * 1024; // bytes
|
||||
|
||||
/// Applies a bit mask to the bits in host pointers from the page table.
|
||||
/// Masks out the first N bits in host pointers from the page table.
|
||||
/// The intention behind this is to allow users of Dynarmic to pack attributes in the
|
||||
/// same integer and update the pointer attribute pair atomically.
|
||||
/// If the configured value is ~(0b111ULL), all pointers will be forcefully aligned to 8 bytes.
|
||||
std::uint64_t page_table_pointer_mask = 0;
|
||||
/// If the configured value is 3, all pointers will be forcefully aligned to 8 bytes.
|
||||
std::int32_t page_table_pointer_mask_bits = 0;
|
||||
|
||||
/// Log2 of the size per page entry, value should be either 3 or 4
|
||||
std::uint32_t page_table_log2_stride = 3;
|
||||
|
||||
/// Setting this value has Dynarmic check the specified bit of the page pointer provided by page table.
|
||||
/// If the bit is set to 1, Dynarmic will treat it as unmapped.
|
||||
/// This bit should be included as part of `page_table_pointer_mask_bits`.
|
||||
std::optional<std::uint8_t> page_table_marked_bit = std::nullopt;
|
||||
|
||||
/// If this value is set, Dynarmic will sign extend the page table pointer by this bit.
|
||||
/// Useful for compacting bits into the page table and should be used as part of `page_table_pointer_mask`.
|
||||
std::optional<std::uint8_t> page_table_sign_extension = std::nullopt;
|
||||
// Log2 of the size per page entry, value should be either 3 or 4
|
||||
std::size_t page_table_log2_stride = 3;
|
||||
|
||||
/// Select the architecture version to use.
|
||||
/// There are minor behavioural differences between versions.
|
||||
|
||||
@@ -173,23 +173,14 @@ struct UserConfig {
|
||||
/// This is only used if page_table is not nullptr.
|
||||
std::uint32_t page_table_address_space_bits = 36;
|
||||
|
||||
/// Applies a bit mask to the bits in host pointers from the page table.
|
||||
/// Masks out the first N bits in host pointers from the page table.
|
||||
/// The intention behind this is to allow users of Dynarmic to pack attributes in the
|
||||
/// same integer and update the pointer attribute pair atomically.
|
||||
/// If the configured value is ~(0b111ULL), all pointers will be forcefully aligned to 8 bytes.
|
||||
std::uint64_t page_table_pointer_mask = 0;
|
||||
/// If the configured value is 3, all pointers will be forcefully aligned to 8 bytes.
|
||||
std::int32_t page_table_pointer_mask_bits = 0;
|
||||
|
||||
/// Log2 of the size per page entry, value should be either 3 or 4
|
||||
std::uint32_t page_table_log2_stride = 3;
|
||||
|
||||
/// Setting this value has Dynarmic check the specified bit of the page pointer provided by page table.
|
||||
/// If the bit is set to 1, Dynarmic will treat it as unmapped.
|
||||
/// This bit should be included as part of `page_table_pointer_mask`.
|
||||
std::optional<std::uint8_t> page_table_marked_bit = std::nullopt;
|
||||
|
||||
/// If this value is set, Dynarmic will sign extend the page table pointer by this bit.
|
||||
/// Useful for compacting bits into the page table and should be used as part of `page_table_pointer_mask`.
|
||||
std::optional<std::uint8_t> page_table_sign_extension = std::nullopt;
|
||||
// Log2 of the size per page entry, value should be either 3 or 4
|
||||
std::size_t page_table_log2_stride = 3;
|
||||
|
||||
/// Counter-timer frequency register. The value of the register is not interpreted by
|
||||
/// dynarmic.
|
||||
|
||||
@@ -179,11 +179,6 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent) {
|
||||
"GPU: Use the GPU's compute shaders to decode ASTC textures (recommended).\n"
|
||||
"CPU Asynchronously: Use the CPU to decode ASTC textures on demand. Eliminates"
|
||||
"ASTC decoding\nstuttering but may present artifacts."));
|
||||
INSERT(Settings, astc_recompression, tr("ASTC Recompression Method:"),
|
||||
tr("Most GPUs lack support for ASTC textures and must decompress to an"
|
||||
"intermediate format: RGBA8.\n"
|
||||
"BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format,\n"
|
||||
" saving VRAM but degrading image quality."));
|
||||
INSERT(Settings, frame_pacing_mode, tr("Frame Pacing Mode (Vulkan only)"),
|
||||
tr("Controls how the emulator manages frame pacing to reduce stuttering and make the "
|
||||
"frame rate smoother and more consistent."));
|
||||
@@ -234,23 +229,6 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent) {
|
||||
INSERT(Settings, gpu_clock, tr("GPU Clocks"),
|
||||
tr("Makes the game believe GPU work finishes faster than it does, so it stops lowering "
|
||||
"resolution and render distance to fit the Switch's clocks."));
|
||||
INSERT(Settings, gpu_unswizzle_enabled, tr("GPU Unswizzle"),
|
||||
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"
|
||||
"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"
|
||||
"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"
|
||||
"Increasing this can improve throughput on high-end GPUs but may cause TDR or driver "
|
||||
"timeouts on weaker hardware."));
|
||||
|
||||
INSERT(Settings, use_vulkan_driver_pipeline_cache, tr("Use Vulkan pipeline cache"),
|
||||
tr("Enables GPU vendor-specific pipeline cache.\nThis option can improve shader loading "
|
||||
"time significantly in cases where the Vulkan driver does not store pipeline cache "
|
||||
@@ -396,13 +374,6 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QObject* parent) {
|
||||
PAIR(AstcDecodeMode, Gpu, tr("GPU")),
|
||||
PAIR(AstcDecodeMode, CpuAsynchronous, tr("CPU Asynchronous")),
|
||||
}});
|
||||
translations->insert(
|
||||
{Settings::EnumMetadata<Settings::AstcRecompression>::Index(),
|
||||
{
|
||||
PAIR(AstcRecompression, Uncompressed, tr("Uncompressed (Best quality)")),
|
||||
PAIR(AstcRecompression, Bc1, tr("BC1 (Low quality)")),
|
||||
PAIR(AstcRecompression, Bc3, tr("BC3 (Medium quality)")),
|
||||
}});
|
||||
translations->insert({Settings::EnumMetadata<Settings::FramePacingMode>::Index(),
|
||||
{
|
||||
PAIR(FramePacingMode, Target_Auto, tr("Auto")),
|
||||
@@ -654,30 +625,6 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QObject* parent) {
|
||||
PAIR(GpuClock, Boost, tr("Boost")),
|
||||
PAIR(GpuClock, Overclock, tr("Overclock")),
|
||||
}});
|
||||
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(),
|
||||
{
|
||||
|
||||
@@ -262,6 +262,5 @@ Q_DECLARE_METATYPE(Settings::ResolutionSetup);
|
||||
Q_DECLARE_METATYPE(Settings::ScalingFilter);
|
||||
Q_DECLARE_METATYPE(Settings::AntiAliasing);
|
||||
Q_DECLARE_METATYPE(Settings::RendererBackend);
|
||||
Q_DECLARE_METATYPE(Settings::AstcRecompression);
|
||||
Q_DECLARE_METATYPE(Settings::AstcDecodeMode);
|
||||
Q_DECLARE_METATYPE(Settings::SpirvOptimizeMode);
|
||||
|
||||
@@ -172,6 +172,7 @@ add_library(shader_recompiler STATIC
|
||||
ir_opt/constant_propagation_pass.cpp
|
||||
ir_opt/dead_code_elimination_pass.cpp
|
||||
ir_opt/dual_vertex_pass.cpp
|
||||
ir_opt/geometry_compaction_pass.cpp
|
||||
ir_opt/global_memory_to_storage_buffer_pass.cpp
|
||||
ir_opt/identity_removal_pass.cpp
|
||||
ir_opt/layer_pass.cpp
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <span>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
@@ -439,10 +440,13 @@ void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ct
|
||||
ctx.AddCapability(spv::Capability::DrawParameters);
|
||||
}
|
||||
if ((info.uses_subgroup_vote || info.uses_subgroup_invocation_id ||
|
||||
info.uses_subgroup_shuffles) &&
|
||||
info.uses_subgroup_shuffles || info.uses_subgroup_mask) &&
|
||||
profile.support_vote && profile.SupportsSubgroupStage(ctx.stage)) {
|
||||
ctx.AddCapability(spv::Capability::GroupNonUniformBallot);
|
||||
ctx.AddCapability(spv::Capability::GroupNonUniformShuffle);
|
||||
if (info.uses_subgroup_shuffles && profile.support_shuffle_relative) {
|
||||
ctx.AddCapability(spv::Capability::GroupNonUniformShuffleRelative);
|
||||
}
|
||||
if (!profile.warp_size_potentially_larger_than_guest) {
|
||||
// vote ops are only used when not taking the long path
|
||||
ctx.AddCapability(spv::Capability::GroupNonUniformVote);
|
||||
@@ -521,6 +525,29 @@ void PatchPhiNodes(IR::Program& program, EmitContext& ctx) {
|
||||
return { ctx.Def(phi->Arg(phi_arg)), parent };
|
||||
});
|
||||
}
|
||||
|
||||
void RewriteOpcodes(std::vector<u32>& code, std::span<const std::pair<u32, spv::Op>> rewrites) {
|
||||
if (rewrites.empty()) {
|
||||
return;
|
||||
}
|
||||
size_t offset = 5;
|
||||
while (offset + 2 < code.size()) {
|
||||
const u32 word_count{code[offset] >> 16};
|
||||
const auto opcode{static_cast<spv::Op>(code[offset] & 0xFFFFu)};
|
||||
if (word_count == 0) {
|
||||
return;
|
||||
}
|
||||
if (opcode == spv::Op::OpGroupNonUniformShuffleXor ||
|
||||
opcode == spv::Op::OpGroupNonUniformQuadBroadcast) {
|
||||
const auto it{std::ranges::find(rewrites, code[offset + 2],
|
||||
&std::pair<u32, spv::Op>::first)};
|
||||
if (it != rewrites.end()) {
|
||||
code[offset] = (code[offset] & 0xFFFF0000u) | static_cast<u32>(it->second);
|
||||
}
|
||||
}
|
||||
offset += word_count;
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
std::vector<u32> EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_info, IR::Program& program, Bindings& bindings) {
|
||||
@@ -535,7 +562,9 @@ std::vector<u32> EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_in
|
||||
SetupCapabilities(profile, program.info, ctx);
|
||||
SetupTransformFeedbackCapabilities(ctx, main);
|
||||
PatchPhiNodes(program, ctx);
|
||||
return ctx.Assemble();
|
||||
std::vector<u32> code{ctx.Assemble()};
|
||||
RewriteOpcodes(code, ctx.opcode_rewrites);
|
||||
return code;
|
||||
}
|
||||
|
||||
Id EmitPhi(EmitContext& ctx, IR::Inst* inst) {
|
||||
|
||||
@@ -77,20 +77,57 @@ Id GetMaxThreadId(EmitContext& ctx, Id thread_id, Id clamp, Id segmentation_mask
|
||||
return ComputeMaxThreadId(ctx, min_thread_id, clamp, not_seg_mask);
|
||||
}
|
||||
|
||||
Id SelectValue(EmitContext& ctx, Id in_range, Id value, Id src_thread_id) {
|
||||
if (!StageSupportsSubgroups(ctx)) {
|
||||
return value;
|
||||
Id HostThreadId(EmitContext& ctx, Id thread_id) {
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
return thread_id;
|
||||
}
|
||||
return ctx.OpSelect(
|
||||
ctx.U32[1], in_range,
|
||||
ctx.OpGroupNonUniformShuffle(ctx.U32[1], SubgroupScope(ctx), value, src_thread_id), value);
|
||||
}
|
||||
|
||||
Id AddPartitionBase(EmitContext& ctx, Id thread_id) {
|
||||
const Id partition_idx{ctx.OpShiftRightLogical(ctx.U32[1], GetThreadId(ctx), ctx.Const(5u))};
|
||||
const Id partition_base{ctx.OpShiftLeftLogical(ctx.U32[1], partition_idx, ctx.Const(5u))};
|
||||
return ctx.OpIAdd(ctx.U32[1], thread_id, partition_base);
|
||||
}
|
||||
|
||||
Id GuestLane(EmitContext& ctx, Id index) {
|
||||
return ctx.OpBitwiseAnd(ctx.U32[1], index, ctx.Const(31U));
|
||||
}
|
||||
|
||||
Id ShuffleAbsolute(EmitContext& ctx, Id value, Id src_thread_id) {
|
||||
if (!ctx.profile.has_broken_spirv_subgroup_shuffle) {
|
||||
return ctx.OpGroupNonUniformShuffle(ctx.U32[1], SubgroupScope(ctx), value, src_thread_id);
|
||||
}
|
||||
Id result{ctx.u32_zero_value};
|
||||
for (u32 lane = 0; lane < ctx.profile.max_subgroup_size; ++lane) {
|
||||
const Id read{
|
||||
ctx.OpGroupNonUniformBroadcast(ctx.U32[1], SubgroupScope(ctx), value, ctx.Const(lane))};
|
||||
const Id matches{ctx.OpIEqual(ctx.U1, src_thread_id, ctx.Const(lane))};
|
||||
result = ctx.OpSelect(ctx.U32[1], matches, read, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Id ShuffleRelative(EmitContext& ctx, Id value, Id delta, Id src_thread_id, spv::Op op) {
|
||||
if (!ctx.profile.support_shuffle_relative) {
|
||||
return ShuffleAbsolute(ctx, value, HostThreadId(ctx, src_thread_id));
|
||||
}
|
||||
const Id result{ctx.OpGroupNonUniformShuffleXor(ctx.U32[1], SubgroupScope(ctx), value, delta)};
|
||||
ctx.opcode_rewrites.emplace_back(result.value, op);
|
||||
return result;
|
||||
}
|
||||
|
||||
Id BroadcastLane(EmitContext& ctx, Id value, u32 lane) {
|
||||
Id result{
|
||||
ctx.OpGroupNonUniformBroadcast(ctx.U32[1], SubgroupScope(ctx), value, ctx.Const(lane))};
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
return result;
|
||||
}
|
||||
const Id partition_idx{ctx.OpShiftRightLogical(ctx.U32[1], GetThreadId(ctx), ctx.Const(5u))};
|
||||
for (u32 base = 32; base < ctx.profile.max_subgroup_size; base += 32) {
|
||||
const Id read{ctx.OpGroupNonUniformBroadcast(ctx.U32[1], SubgroupScope(ctx), value,
|
||||
ctx.Const(base + lane))};
|
||||
const Id matches{ctx.OpIEqual(ctx.U1, partition_idx, ctx.Const(base >> 5))};
|
||||
result = ctx.OpSelect(ctx.U32[1], matches, read, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
Id EmitLaneId(EmitContext& ctx) {
|
||||
@@ -203,61 +240,75 @@ Id EmitShuffleIndex(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id cla
|
||||
const Id min_thread_id{ComputeMinThreadId(ctx, thread_id, segmentation_mask)};
|
||||
const Id max_thread_id{ComputeMaxThreadId(ctx, min_thread_id, clamp, not_seg_mask)};
|
||||
|
||||
const Id lhs{ctx.OpBitwiseAnd(ctx.U32[1], index, not_seg_mask)};
|
||||
Id src_thread_id{ctx.OpBitwiseOr(ctx.U32[1], lhs, min_thread_id)};
|
||||
const Id lhs{ctx.OpBitwiseAnd(ctx.U32[1], GuestLane(ctx, index), not_seg_mask)};
|
||||
const Id src_thread_id{ctx.OpBitwiseOr(ctx.U32[1], lhs, min_thread_id)};
|
||||
const Id in_range{ctx.OpSLessThanEqual(ctx.U1, src_thread_id, max_thread_id)};
|
||||
|
||||
if (ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
src_thread_id = AddPartitionBase(ctx, src_thread_id);
|
||||
}
|
||||
|
||||
SetInBoundsFlag(inst, in_range);
|
||||
return SelectValue(ctx, in_range, value, src_thread_id);
|
||||
if (!StageSupportsSubgroups(ctx)) {
|
||||
return value;
|
||||
}
|
||||
const IR::Value lane{inst->Arg(1).Resolve()};
|
||||
const IR::Value segment{inst->Arg(3).Resolve()};
|
||||
if (lane.IsImmediate() && segment.IsImmediate() && segment.U32() == 0) {
|
||||
return ctx.OpSelect(ctx.U32[1], in_range, BroadcastLane(ctx, value, lane.U32() & 31),
|
||||
value);
|
||||
}
|
||||
const Id shuffled{ShuffleAbsolute(ctx, value, HostThreadId(ctx, src_thread_id))};
|
||||
return ctx.OpSelect(ctx.U32[1], in_range, shuffled, value);
|
||||
}
|
||||
|
||||
Id EmitShuffleUp(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
|
||||
Id segmentation_mask) {
|
||||
if (!StageSupportsSubgroups(ctx)) {
|
||||
SetInBoundsFlag(inst, ctx.false_value);
|
||||
return value;
|
||||
}
|
||||
const Id delta{GuestLane(ctx, index)};
|
||||
const Id thread_id{EmitLaneId(ctx)};
|
||||
const Id max_thread_id{GetMaxThreadId(ctx, thread_id, clamp, segmentation_mask)};
|
||||
Id src_thread_id{ctx.OpISub(ctx.U32[1], thread_id, index)};
|
||||
const Id src_thread_id{ctx.OpISub(ctx.U32[1], thread_id, delta)};
|
||||
const Id in_range{ctx.OpSGreaterThanEqual(ctx.U1, src_thread_id, max_thread_id)};
|
||||
|
||||
if (ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
src_thread_id = AddPartitionBase(ctx, src_thread_id);
|
||||
}
|
||||
|
||||
SetInBoundsFlag(inst, in_range);
|
||||
return SelectValue(ctx, in_range, value, src_thread_id);
|
||||
const Id shuffled{ShuffleRelative(ctx, value, delta, src_thread_id,
|
||||
spv::Op::OpGroupNonUniformShuffleUp)};
|
||||
return ctx.OpSelect(ctx.U32[1], in_range, shuffled, value);
|
||||
}
|
||||
|
||||
Id EmitShuffleDown(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
|
||||
Id segmentation_mask) {
|
||||
if (!StageSupportsSubgroups(ctx)) {
|
||||
SetInBoundsFlag(inst, ctx.false_value);
|
||||
return value;
|
||||
}
|
||||
const Id delta{GuestLane(ctx, index)};
|
||||
const Id thread_id{EmitLaneId(ctx)};
|
||||
const Id max_thread_id{GetMaxThreadId(ctx, thread_id, clamp, segmentation_mask)};
|
||||
Id src_thread_id{ctx.OpIAdd(ctx.U32[1], thread_id, index)};
|
||||
const Id src_thread_id{ctx.OpIAdd(ctx.U32[1], thread_id, delta)};
|
||||
const Id in_range{ctx.OpSLessThanEqual(ctx.U1, src_thread_id, max_thread_id)};
|
||||
|
||||
if (ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
src_thread_id = AddPartitionBase(ctx, src_thread_id);
|
||||
}
|
||||
|
||||
SetInBoundsFlag(inst, in_range);
|
||||
return SelectValue(ctx, in_range, value, src_thread_id);
|
||||
const Id shuffled{ShuffleRelative(ctx, value, delta, src_thread_id,
|
||||
spv::Op::OpGroupNonUniformShuffleDown)};
|
||||
return ctx.OpSelect(ctx.U32[1], in_range, shuffled, value);
|
||||
}
|
||||
|
||||
Id EmitShuffleButterfly(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
|
||||
Id segmentation_mask) {
|
||||
if (!StageSupportsSubgroups(ctx)) {
|
||||
SetInBoundsFlag(inst, ctx.false_value);
|
||||
return value;
|
||||
}
|
||||
const Id mask{GuestLane(ctx, index)};
|
||||
const Id thread_id{EmitLaneId(ctx)};
|
||||
const Id max_thread_id{GetMaxThreadId(ctx, thread_id, clamp, segmentation_mask)};
|
||||
Id src_thread_id{ctx.OpBitwiseXor(ctx.U32[1], thread_id, index)};
|
||||
const Id src_thread_id{ctx.OpBitwiseXor(ctx.U32[1], thread_id, mask)};
|
||||
const Id in_range{ctx.OpSLessThanEqual(ctx.U1, src_thread_id, max_thread_id)};
|
||||
|
||||
if (ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
src_thread_id = AddPartitionBase(ctx, src_thread_id);
|
||||
}
|
||||
|
||||
SetInBoundsFlag(inst, in_range);
|
||||
return SelectValue(ctx, in_range, value, src_thread_id);
|
||||
const Id shuffled{ctx.OpGroupNonUniformShuffleXor(ctx.U32[1], SubgroupScope(ctx), value, mask)};
|
||||
return ctx.OpSelect(ctx.U32[1], in_range, shuffled, value);
|
||||
}
|
||||
|
||||
Id EmitQuadBroadcast(EmitContext& ctx, Id value, Id lane) {
|
||||
@@ -267,10 +318,16 @@ Id EmitQuadBroadcast(EmitContext& ctx, Id value, Id lane) {
|
||||
const Id base{ctx.OpBitwiseAnd(ctx.U32[1], GetThreadId(ctx), ctx.Const(~3u))};
|
||||
const Id local_lane{ctx.OpBitwiseAnd(ctx.U32[1], lane, ctx.Const(3u))};
|
||||
const Id src_thread_id{ctx.OpBitwiseOr(ctx.U32[1], base, local_lane)};
|
||||
return ctx.OpGroupNonUniformShuffle(ctx.U32[1], SubgroupScope(ctx), value, src_thread_id);
|
||||
return ShuffleAbsolute(ctx, value, src_thread_id);
|
||||
}
|
||||
|
||||
Id EmitQuadSwap(EmitContext& ctx, Id value, Id direction) {
|
||||
if (ctx.profile.support_quad_shuffles) {
|
||||
const Id result{
|
||||
ctx.OpGroupNonUniformQuadBroadcast(ctx.U32[1], SubgroupScope(ctx), value, direction)};
|
||||
ctx.opcode_rewrites.emplace_back(result.value, spv::Op::OpGroupNonUniformQuadSwap);
|
||||
return result;
|
||||
}
|
||||
const Id xor_mask{ctx.OpIAdd(ctx.U32[1], direction, ctx.Const(1u))};
|
||||
return ctx.OpGroupNonUniformShuffleXor(ctx.U32[1], SubgroupScope(ctx), value, xor_mask);
|
||||
}
|
||||
|
||||
@@ -361,6 +361,7 @@ public:
|
||||
Id frag_depth{};
|
||||
|
||||
std::vector<Id> interfaces;
|
||||
std::vector<std::pair<u32, spv::Op>> opcode_rewrites;
|
||||
|
||||
Id load_const_func_u8{};
|
||||
Id load_const_func_u16{};
|
||||
|
||||
@@ -299,6 +299,7 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
|
||||
Optimization::PositionPass(env, program);
|
||||
|
||||
Optimization::GlobalMemoryToStorageBufferPass(program, normalized_host_info);
|
||||
Optimization::GeometryCompactionPass(program, normalized_host_info);
|
||||
Optimization::TexturePass(env, program, normalized_host_info);
|
||||
|
||||
if (Settings::values.resolution_info.active || Settings::values.rescale_hack.GetValue()) {
|
||||
|
||||
@@ -38,6 +38,7 @@ struct HostTranslateInfo {
|
||||
///< passthrough shaders
|
||||
bool support_conditional_barrier{}; ///< True when the device supports barriers in conditional
|
||||
///< control flow
|
||||
bool single_lane_geometry_subgroups{};
|
||||
|
||||
void ApplyDescriptorLimitPolicy() noexcept {
|
||||
if (min_ssbo_alignment == 0) {
|
||||
|
||||
@@ -651,6 +651,29 @@ IR::Value GetThroughCast(IR::Value value, IR::Opcode expected_cast) {
|
||||
return value;
|
||||
}
|
||||
|
||||
u32 QuadButterflyMask(const IR::Inst& inst) {
|
||||
if (inst.GetOpcode() == IR::Opcode::QuadSwap) {
|
||||
const IR::Value direction{inst.Arg(1)};
|
||||
if (!direction.IsImmediate()) {
|
||||
return 0;
|
||||
}
|
||||
return direction.U32() + 1;
|
||||
}
|
||||
if (inst.GetOpcode() != IR::Opcode::ShuffleButterfly) {
|
||||
return 0;
|
||||
}
|
||||
const IR::Value index{inst.Arg(1)};
|
||||
const IR::Value clamp{inst.Arg(2)};
|
||||
const IR::Value segmentation_mask{inst.Arg(3)};
|
||||
if (!index.IsImmediate() || !clamp.IsImmediate() || !segmentation_mask.IsImmediate()) {
|
||||
return 0;
|
||||
}
|
||||
if (clamp.U32() != 3 || segmentation_mask.U32() != 28) {
|
||||
return 0;
|
||||
}
|
||||
return index.U32();
|
||||
}
|
||||
|
||||
void FoldFSwizzleAdd(IR::Block& block, IR::Inst& inst) {
|
||||
const IR::Value swizzle{inst.Arg(2)};
|
||||
if (!swizzle.IsImmediate()) {
|
||||
@@ -666,7 +689,8 @@ void FoldFSwizzleAdd(IR::Block& block, IR::Inst& inst) {
|
||||
return;
|
||||
}
|
||||
IR::Inst* const inst2{value_1.InstRecursive()};
|
||||
if (inst2->GetOpcode() != IR::Opcode::ShuffleButterfly) {
|
||||
const u32 lane_mask{QuadButterflyMask(*inst2)};
|
||||
if (lane_mask == 0) {
|
||||
return;
|
||||
}
|
||||
const IR::Value value_3{GetThroughCast(inst2->Arg(0).Resolve(), IR::Opcode::BitCastU32F32)};
|
||||
@@ -678,24 +702,15 @@ void FoldFSwizzleAdd(IR::Block& block, IR::Inst& inst) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
const IR::Value index{inst2->Arg(1)};
|
||||
const IR::Value clamp{inst2->Arg(2)};
|
||||
const IR::Value segmentation_mask{inst2->Arg(3)};
|
||||
if (!index.IsImmediate() || !clamp.IsImmediate() || !segmentation_mask.IsImmediate()) {
|
||||
return;
|
||||
}
|
||||
if (clamp.U32() != 3 || segmentation_mask.U32() != 28) {
|
||||
return;
|
||||
}
|
||||
if (swizzle_value == 0x99) {
|
||||
// DPdxFine
|
||||
if (index.U32() == 1) {
|
||||
if (lane_mask == 1) {
|
||||
IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
|
||||
inst.ReplaceUsesWith(ir.DPdxFine(IR::F32{inst.Arg(1)}));
|
||||
}
|
||||
} else if (swizzle_value == 0xA5) {
|
||||
// DPdyFine
|
||||
if (index.U32() == 2) {
|
||||
if (lane_mask == 2) {
|
||||
IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
|
||||
inst.ReplaceUsesWith(ir.DPdyFine(IR::F32{inst.Arg(1)}));
|
||||
}
|
||||
@@ -709,6 +724,13 @@ bool FindGradient3DDerivatives(std::array<IR::Value, 3>& results, IR::Value coor
|
||||
const auto check_through_shuffle = [](IR::Value input, IR::Value& result) {
|
||||
const IR::Value value_1{GetThroughCast(input.Resolve(), IR::Opcode::BitCastF32U32)};
|
||||
IR::Inst* const inst2{value_1.InstRecursive()};
|
||||
if (inst2->GetOpcode() == IR::Opcode::QuadBroadcast) {
|
||||
if (!inst2->Arg(1).Resolve().IsImmediate()) {
|
||||
return false;
|
||||
}
|
||||
result = GetThroughCast(inst2->Arg(0).Resolve(), IR::Opcode::BitCastU32F32);
|
||||
return true;
|
||||
}
|
||||
if (inst2->GetOpcode() != IR::Opcode::ShuffleIndex) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <boost/container/small_vector.hpp>
|
||||
|
||||
#include "shader_recompiler/frontend/ir/ir_emitter.h"
|
||||
#include "shader_recompiler/host_translate_info.h"
|
||||
#include "shader_recompiler/ir_opt/passes.h"
|
||||
|
||||
namespace Shader::Optimization {
|
||||
namespace {
|
||||
constexpr int MAX_BALLOT_DEPTH = 8;
|
||||
|
||||
struct SlotAtomic {
|
||||
IR::Block* block;
|
||||
IR::Inst* atomic;
|
||||
IR::Inst* ballot;
|
||||
};
|
||||
|
||||
IR::Inst* FindBallot(const IR::Value& value, int depth) {
|
||||
if (depth > MAX_BALLOT_DEPTH || value.IsImmediate()) {
|
||||
return nullptr;
|
||||
}
|
||||
IR::Inst* const inst{value.InstRecursive()};
|
||||
if (inst->GetOpcode() == IR::Opcode::SubgroupBallot) {
|
||||
return inst;
|
||||
}
|
||||
if (inst->GetOpcode() == IR::Opcode::Phi) {
|
||||
return nullptr;
|
||||
}
|
||||
for (size_t index = 0; index < inst->NumArgs(); ++index) {
|
||||
if (IR::Inst* const ballot{FindBallot(inst->Arg(index), depth + 1)}) {
|
||||
return ballot;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool IsShuffle(IR::Opcode opcode) {
|
||||
switch (opcode) {
|
||||
case IR::Opcode::ShuffleIndex:
|
||||
case IR::Opcode::ShuffleUp:
|
||||
case IR::Opcode::ShuffleDown:
|
||||
case IR::Opcode::ShuffleButterfly:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
IR::U32 PrimitiveSlot(IR::IREmitter& ir, u32 invocations, const IR::U32& amount) {
|
||||
IR::U32 key{ir.GetAttributeU32(IR::Attribute::PrimitiveId)};
|
||||
if (invocations > 1) {
|
||||
key = ir.IAdd(ir.IMul(key, ir.Imm32(invocations)), ir.InvocationId());
|
||||
}
|
||||
return ir.IMul(key, amount);
|
||||
}
|
||||
|
||||
bool MergesAtomic(const IR::Inst& phi, const IR::Block& block, const IR::Inst& atomic) {
|
||||
for (size_t index = 0; index < phi.NumArgs(); ++index) {
|
||||
const IR::Value arg{phi.Arg(index)};
|
||||
if (phi.PhiBlock(index) == &block && !arg.IsImmediate() &&
|
||||
arg.InstRecursive() == &atomic) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void RewriteMergedSlots(IR::Block& block, IR::Inst& atomic, u32 invocations) {
|
||||
for (IR::Block* const successor : block.ImmSuccessors()) {
|
||||
for (IR::Inst& phi : successor->Instructions()) {
|
||||
if (phi.GetOpcode() != IR::Opcode::Phi || !MergesAtomic(phi, block, atomic)) {
|
||||
continue;
|
||||
}
|
||||
for (size_t index = 0; index < phi.NumArgs(); ++index) {
|
||||
const IR::Value arg{phi.Arg(index)};
|
||||
if (arg.IsImmediate() || !IsShuffle(arg.InstRecursive()->GetOpcode())) {
|
||||
continue;
|
||||
}
|
||||
IR::IREmitter ir{*phi.PhiBlock(index)};
|
||||
const IR::U32 amount{arg.InstRecursive()->Arg(0)};
|
||||
phi.SetArg(index, PrimitiveSlot(ir, invocations, amount));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RewriteAtomic(IR::Block& block, IR::Inst& atomic, u32 invocations) {
|
||||
const auto insert_point{IR::Block::InstructionList::s_iterator_to(atomic)};
|
||||
IR::IREmitter ir{block, insert_point};
|
||||
const IR::U32 amount{atomic.Arg(2)};
|
||||
const IR::U32 slot{PrimitiveSlot(ir, invocations, amount)};
|
||||
block.PrependNewInst(insert_point, IR::Opcode::StorageAtomicUMax32,
|
||||
{atomic.Arg(0), atomic.Arg(1), ir.IAdd(slot, amount)});
|
||||
atomic.ReplaceUsesWith(slot);
|
||||
}
|
||||
|
||||
void NeutralizePredicate(IR::Inst& ballot) {
|
||||
const IR::Value pred{ballot.Arg(0)};
|
||||
if (!pred.IsImmediate()) {
|
||||
pred.InstRecursive()->ReplaceUsesWith(IR::Value{true});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GeometryCompactionPass(IR::Program& program, const HostTranslateInfo& host_info) {
|
||||
if (program.stage != Stage::Geometry || !host_info.single_lane_geometry_subgroups) {
|
||||
return;
|
||||
}
|
||||
boost::container::small_vector<SlotAtomic, 4> slot_atomics;
|
||||
for (IR::Block* const block : program.post_order_blocks) {
|
||||
for (IR::Inst& inst : block->Instructions()) {
|
||||
if (inst.GetOpcode() != IR::Opcode::StorageAtomicIAdd32) {
|
||||
continue;
|
||||
}
|
||||
if (IR::Inst* const ballot{FindBallot(inst.Arg(2), 0)}) {
|
||||
slot_atomics.push_back({block, &inst, ballot});
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const SlotAtomic& slot_atomic : slot_atomics) {
|
||||
RewriteMergedSlots(*slot_atomic.block, *slot_atomic.atomic, program.invocations);
|
||||
RewriteAtomic(*slot_atomic.block, *slot_atomic.atomic, program.invocations);
|
||||
NeutralizePredicate(*slot_atomic.ballot);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,6 +16,7 @@ void CollectShaderInfoPass(Environment& env, IR::Program& program);
|
||||
void ConditionalBarrierPass(IR::Program& program);
|
||||
void ConstantPropagationPass(Environment& env, IR::Program& program);
|
||||
void DeadCodeEliminationPass(IR::Program& program);
|
||||
void GeometryCompactionPass(IR::Program& program, const HostTranslateInfo& host_info);
|
||||
void GlobalMemoryToStorageBufferPass(IR::Program& program, const HostTranslateInfo& host_info);
|
||||
void IdentityRemovalPass(IR::Program& program);
|
||||
void LowerFp64ToFp32(IR::Program& program);
|
||||
|
||||
@@ -33,7 +33,8 @@ using TextureInstVector = boost::container::small_vector<TextureInst, 24>;
|
||||
|
||||
constexpr u32 DESCRIPTOR_SIZE = 8;
|
||||
constexpr u32 DESCRIPTOR_SIZE_SHIFT = u32(std::countr_zero(DESCRIPTOR_SIZE));
|
||||
constexpr u32 DESCRIPTOR_MAX_COUNT = 1024;
|
||||
constexpr u32 DESCRIPTOR_MAX_COUNT = 128;
|
||||
constexpr u32 DESCRIPTOR_CBUF_BYTES = 16 * 1024;
|
||||
|
||||
u32 DynamicDescriptorSizeShift(const IR::U32& dynamic_offset) {
|
||||
const IR::Inst* const inst = dynamic_offset.InstRecursive();
|
||||
@@ -48,11 +49,10 @@ u32 DynamicDescriptorSizeShift(const IR::U32& dynamic_offset) {
|
||||
|
||||
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)
|
||||
if (size_shift >= 31 || base_offset >= DESCRIPTOR_CBUF_BYTES)
|
||||
return 1;
|
||||
auto const stride = 1U << size_shift;
|
||||
auto const available = max_cbuf_bytes - base_offset;
|
||||
auto const available = DESCRIPTOR_CBUF_BYTES - base_offset;
|
||||
if (available < DESCRIPTOR_SIZE)
|
||||
return 1;
|
||||
auto const available_count = 1U + (available - DESCRIPTOR_SIZE) / stride;
|
||||
|
||||
@@ -40,6 +40,7 @@ struct Profile {
|
||||
bool support_shader_quad_control{};
|
||||
bool support_quad_shuffles{};
|
||||
bool support_vote{};
|
||||
bool support_shuffle_relative{};
|
||||
u32 supported_subgroup_stages{0x7F};
|
||||
bool support_viewport_index_layer_non_geometry{};
|
||||
bool support_viewport_mask{};
|
||||
@@ -102,6 +103,8 @@ struct Profile {
|
||||
bool ignore_nan_fp_comparisons{};
|
||||
/// Some drivers have broken support for OpVectorExtractDynamic on subgroup mask inputs
|
||||
bool has_broken_spirv_subgroup_mask_vector_extract_dynamic{};
|
||||
bool has_broken_spirv_subgroup_shuffle{};
|
||||
u32 max_subgroup_size{};
|
||||
|
||||
u32 gl_max_compute_smem_size{};
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ add_library(video_core STATIC
|
||||
buffer_cache/usage_tracker.h
|
||||
buffer_cache/virtual_range_cache.h
|
||||
buffer_cache/word_manager.h
|
||||
cache_reclaim.h
|
||||
cache_types.h
|
||||
capture.h
|
||||
cdma_pusher.cpp
|
||||
@@ -256,8 +257,6 @@ add_library(video_core STATIC
|
||||
texture_cache/util.h
|
||||
textures/astc.h
|
||||
textures/astc.cpp
|
||||
textures/bcn.cpp
|
||||
textures/bcn.h
|
||||
textures/decoders.cpp
|
||||
textures/decoders.h
|
||||
textures/texture.cpp
|
||||
@@ -406,7 +405,7 @@ if (ENABLE_OPENGL)
|
||||
endif()
|
||||
|
||||
target_link_libraries(video_core PUBLIC common core)
|
||||
target_link_libraries(video_core PUBLIC shader_recompiler stb bc_decoder gpu_logging)
|
||||
target_link_libraries(video_core PUBLIC shader_recompiler bc_decoder gpu_logging)
|
||||
if (ENABLE_OPENGL)
|
||||
target_link_libraries(video_core PUBLIC glad)
|
||||
endif()
|
||||
|
||||
@@ -129,13 +129,44 @@ public:
|
||||
write_tick = write_tick_;
|
||||
}
|
||||
|
||||
u64 ContentSerial() const noexcept {
|
||||
return content_serial;
|
||||
}
|
||||
|
||||
void MarkContentModified() noexcept {
|
||||
content_serial = ++next_content_serial;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool HasDrawHazard(u64 pass, u64 wfi, DAddr addr, u64 size) const noexcept {
|
||||
return draw_write_pass == pass && draw_write_wfi < wfi && addr < draw_write_end &&
|
||||
addr + size > draw_write_begin;
|
||||
}
|
||||
|
||||
void MarkDrawWrite(u64 pass, u64 wfi, DAddr addr, u64 size) noexcept {
|
||||
if (draw_write_pass != pass) {
|
||||
draw_write_pass = pass;
|
||||
draw_write_begin = addr;
|
||||
draw_write_end = addr + size;
|
||||
}
|
||||
draw_write_wfi = wfi;
|
||||
draw_write_begin = (std::min)(draw_write_begin, addr);
|
||||
draw_write_end = (std::max)(draw_write_end, addr + size);
|
||||
}
|
||||
|
||||
private:
|
||||
static inline u64 next_content_serial = 0;
|
||||
|
||||
VAddr cpu_addr = 0;
|
||||
BufferFlagBits flags{};
|
||||
int stream_score = 0;
|
||||
size_t lru_id = SIZE_MAX;
|
||||
size_t size_bytes = 0;
|
||||
u64 write_tick = 0;
|
||||
u64 content_serial = ++next_content_serial;
|
||||
u64 draw_write_pass = 0;
|
||||
u64 draw_write_wfi = 0;
|
||||
DAddr draw_write_begin = 0;
|
||||
DAddr draw_write_end = 0;
|
||||
};
|
||||
|
||||
} // namespace VideoCommon
|
||||
|
||||
@@ -30,34 +30,54 @@ BufferCache<P>::BufferCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, R
|
||||
#ifdef YUZU_LEGACY
|
||||
immediately_free = (Settings::values.vram_usage_mode.GetValue() == Settings::VramUsageMode::Aggressive);
|
||||
#endif
|
||||
if (!runtime.CanReportMemoryUsage()) {
|
||||
minimum_memory = DEFAULT_EXPECTED_MEMORY;
|
||||
critical_memory = DEFAULT_CRITICAL_MEMORY;
|
||||
return;
|
||||
}
|
||||
|
||||
const s64 device_local_memory = static_cast<s64>(runtime.GetDeviceLocalMemory());
|
||||
const s64 min_spacing_expected = device_local_memory - 1_GiB;
|
||||
const s64 min_spacing_critical = device_local_memory - 512_MiB;
|
||||
const s64 mem_threshold = (std::min)(device_local_memory, TARGET_THRESHOLD);
|
||||
const s64 min_vacancy_expected = (6 * mem_threshold) / 10;
|
||||
const s64 min_vacancy_critical = (2 * mem_threshold) / 10;
|
||||
minimum_memory = static_cast<u64>(
|
||||
(std::max)((std::min)(device_local_memory - min_vacancy_expected, min_spacing_expected),
|
||||
DEFAULT_EXPECTED_MEMORY));
|
||||
critical_memory = static_cast<u64>(
|
||||
(std::max)((std::min)(device_local_memory - min_vacancy_critical, min_spacing_critical),
|
||||
DEFAULT_CRITICAL_MEMORY));
|
||||
device_local_memory = runtime.GetDeviceLocalMemory();
|
||||
const auto thresholds = VideoCommon::MakeReclaimThresholds(
|
||||
device_local_memory, static_cast<u64>(TARGET_THRESHOLD),
|
||||
static_cast<u64>(DEFAULT_EXPECTED_MEMORY), static_cast<u64>(DEFAULT_CRITICAL_MEMORY),
|
||||
HEAP_PRESSURE_HEADROOM);
|
||||
minimum_memory = thresholds.minimum;
|
||||
expected_memory = thresholds.expected;
|
||||
critical_memory = thresholds.critical;
|
||||
heap_headroom = thresholds.headroom;
|
||||
}
|
||||
|
||||
template <class P>
|
||||
BufferCache<P>::~BufferCache() = default;
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::ReclaimInline() {
|
||||
if (total_used_memory < minimum_memory) {
|
||||
return;
|
||||
}
|
||||
int num_iterations = 8;
|
||||
const auto clean_up = [this, &num_iterations](BufferId buffer_id) {
|
||||
if (num_iterations == 0) {
|
||||
return true;
|
||||
}
|
||||
--num_iterations;
|
||||
Buffer& buffer = slot_buffers[buffer_id];
|
||||
if (memory_tracker.IsRegionGpuModified(buffer.CpuAddr(), buffer.SizeBytes())) {
|
||||
return false;
|
||||
}
|
||||
DeleteBuffer(buffer_id);
|
||||
return false;
|
||||
};
|
||||
lru_cache.ForEachItemBelow(frame_tick - INLINE_TICKS_TO_DESTROY, clean_up);
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::RunGarbageCollector() {
|
||||
const bool aggressive_gc = total_used_memory >= critical_memory;
|
||||
const u64 ticks_to_destroy = aggressive_gc ? 60 : 120;
|
||||
int num_iterations = aggressive_gc ? 64 : 32;
|
||||
const bool aggressive_gc = heap_pressure || total_used_memory >= critical_memory;
|
||||
const bool priority_gc = aggressive_gc || total_used_memory >= expected_memory;
|
||||
u64 ticks_to_destroy = 120;
|
||||
int num_iterations = 32;
|
||||
if (aggressive_gc) {
|
||||
ticks_to_destroy = 30;
|
||||
num_iterations = 64;
|
||||
} else if (priority_gc) {
|
||||
ticks_to_destroy = 60;
|
||||
num_iterations = 48;
|
||||
}
|
||||
const auto clean_up = [this, &num_iterations](BufferId buffer_id) {
|
||||
if (num_iterations == 0) {
|
||||
return true;
|
||||
@@ -96,11 +116,11 @@ void BufferCache<P>::TickFrame() {
|
||||
const bool skip_preferred = hits * 256 < shots * 251;
|
||||
channel_state->uniform_buffer_skip_cache_size = skip_preferred ? DEFAULT_SKIP_CACHE_SIZE : 0;
|
||||
|
||||
// If we can obtain the memory info, use it instead of the estimate.
|
||||
if (runtime.CanReportMemoryUsage()) {
|
||||
total_used_memory = runtime.GetDeviceMemoryUsage();
|
||||
heap_pressure = false;
|
||||
if (device_local_memory != 0 && runtime.CanReportMemoryUsage()) {
|
||||
heap_pressure = runtime.GetDeviceMemoryUsage() + heap_headroom >= device_local_memory;
|
||||
}
|
||||
if (total_used_memory >= minimum_memory) {
|
||||
if (total_used_memory >= minimum_memory || heap_pressure) {
|
||||
RunGarbageCollector();
|
||||
}
|
||||
++frame_tick;
|
||||
@@ -246,6 +266,7 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am
|
||||
const auto& copy = copies[0];
|
||||
src_buffer.MarkUsage(copy.src_offset, copy.size);
|
||||
dest_buffer.MarkUsage(copy.dst_offset, copy.size);
|
||||
dest_buffer.MarkContentModified();
|
||||
runtime.CopyBuffer(dest_buffer, src_buffer, copies, true);
|
||||
if (has_new_downloads) {
|
||||
memory_tracker.MarkRegionAsGpuModified(*cpu_dest_address, amount);
|
||||
@@ -277,6 +298,7 @@ bool BufferCache<P>::DMAClear(GPUVAddr dst_address, u64 amount, u32 value) {
|
||||
const u32 offset = dest_buffer.Offset(*cpu_dst_address);
|
||||
runtime.ClearBuffer(dest_buffer, offset, size, value);
|
||||
dest_buffer.MarkUsage(offset, size);
|
||||
dest_buffer.MarkContentModified();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -344,14 +366,44 @@ void BufferCache<P>::DisableGraphicsUniformBuffer(size_t stage, u32 index) {
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::UpdateGraphicsBuffers(bool is_indexed) {
|
||||
if constexpr (!IS_OPENGL) {
|
||||
draw_writes.clear();
|
||||
draw_pass = runtime.RenderPassSerial();
|
||||
draw_wfi = runtime.WaitForIdleSerial();
|
||||
draw_hazard = false;
|
||||
recording_draw = true;
|
||||
}
|
||||
ReclaimInline();
|
||||
do {
|
||||
channel_state->has_deleted_buffers = false;
|
||||
DoUpdateGraphicsBuffers(is_indexed);
|
||||
} while (channel_state->has_deleted_buffers);
|
||||
}
|
||||
|
||||
template <class P>
|
||||
bool BufferCache<P>::TakeDrawHazard() noexcept {
|
||||
return std::exchange(draw_hazard, false);
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::CommitDrawWrites() {
|
||||
if constexpr (!IS_OPENGL) {
|
||||
recording_draw = false;
|
||||
if (draw_writes.empty()) {
|
||||
return;
|
||||
}
|
||||
const u64 pass = runtime.RenderPassSerial();
|
||||
for (const DrawWrite& write : draw_writes) {
|
||||
slot_buffers[write.buffer_id].MarkDrawWrite(pass, draw_wfi, write.device_addr,
|
||||
write.size);
|
||||
}
|
||||
runtime.MarkRenderPassWrites();
|
||||
}
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::UpdateComputeBuffers() {
|
||||
ReclaimInline();
|
||||
do {
|
||||
channel_state->has_deleted_buffers = false;
|
||||
DoUpdateComputeBuffers();
|
||||
@@ -751,6 +803,7 @@ void BufferCache<P>::BindHostIndexBuffer() {
|
||||
if (draw_state.inline_index_draw_indexes.empty()) {
|
||||
SynchronizeBuffer(buffer, channel_state->index_buffer.device_addr, size);
|
||||
} else {
|
||||
buffer.MarkContentModified();
|
||||
if constexpr (USE_MEMORY_MAPS_FOR_UPLOADS) {
|
||||
auto upload_staging = runtime.UploadStagingBuffer(size);
|
||||
std::array<BufferCopy, 1> copies{{BufferCopy{.src_offset = upload_staging.offset, .dst_offset = 0, .size = size}}};
|
||||
@@ -1006,15 +1059,14 @@ void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::ResolveMultiRangeStorage(Binding& binding, bool is_written,
|
||||
std::vector<MultiRangeSegment>& pool) {
|
||||
void BufferCache<P>::ResolveMultiRangeStorage(Binding& binding,
|
||||
std::vector<MultiRangeSegment>& pool,
|
||||
bool is_written) {
|
||||
binding.segment_first = 0;
|
||||
binding.segment_count = 0;
|
||||
if constexpr (requires { runtime.BindMultiRangeStorageBuffer(u64{}, bool{}); }) {
|
||||
if (binding.gpu_addr == 0 || binding.size == 0) {
|
||||
return;
|
||||
}
|
||||
if (is_written && !runtime.PrefersSparseSources()) {
|
||||
if (binding.gpu_addr == 0 || binding.size == 0 ||
|
||||
(is_written && !runtime.PrefersSparseSources())) {
|
||||
return;
|
||||
}
|
||||
const VirtualSegments* found =
|
||||
@@ -1022,7 +1074,7 @@ void BufferCache<P>::ResolveMultiRangeStorage(Binding& binding, bool is_written,
|
||||
if (!found || found->size() < 2) {
|
||||
return;
|
||||
}
|
||||
const VirtualSegments segments = *found;
|
||||
const VirtualSegments& segments = *found;
|
||||
const u32 first = static_cast<u32>(pool.size());
|
||||
const bool prefer_sparse = runtime.PrefersSparseSources();
|
||||
for (const VirtualSegment& segment : segments) {
|
||||
@@ -1059,9 +1111,7 @@ bool BufferCache<P>::BindMultiRangeStorage(const Binding& binding, bool is_writt
|
||||
const MultiRangeSegment& segment = pool[binding.segment_first + index];
|
||||
Buffer& buffer = slot_buffers[segment.buffer_id];
|
||||
TouchBuffer(buffer, segment.buffer_id);
|
||||
if (SynchronizeBuffer(buffer, segment.device_addr, segment.size)) {
|
||||
runtime.InvalidateMultiRange(key);
|
||||
}
|
||||
SynchronizeBuffer(buffer, segment.device_addr, segment.size);
|
||||
const u32 offset = buffer.Offset(segment.device_addr);
|
||||
buffer.MarkUsage(offset, segment.size);
|
||||
if (is_written) {
|
||||
@@ -1322,7 +1372,7 @@ void BufferCache<P>::UpdateIndexBuffer() {
|
||||
inline_buffer_id = CreateBuffer(0, buffer_size, false);
|
||||
}
|
||||
if (slot_buffers[inline_buffer_id].SizeBytes() < buffer_size) [[unlikely]] {
|
||||
slot_buffers.erase(inline_buffer_id);
|
||||
DeleteBuffer(inline_buffer_id, true);
|
||||
inline_buffer_id = CreateBuffer(0, buffer_size, false);
|
||||
}
|
||||
channel_state->index_buffer = Binding{
|
||||
@@ -1437,10 +1487,12 @@ void BufferCache<P>::UpdateStorageBuffers(size_t stage) {
|
||||
ForEachEnabledBit(channel_state->enabled_storage_buffers[stage], [&](u32 index) {
|
||||
// Resolve buffer
|
||||
Binding& binding = channel_state->storage_buffers[stage][index];
|
||||
const BufferId buffer_id = FindBuffer(binding.device_addr, binding.size, false);
|
||||
binding.buffer_id = buffer_id;
|
||||
const bool is_written = ((channel_state->written_storage_buffers[stage] >> index) & 1) != 0;
|
||||
ResolveMultiRangeStorage(binding, is_written, graphics_segments);
|
||||
ResolveMultiRangeStorage(binding, graphics_segments, is_written);
|
||||
binding.buffer_id = NULL_BUFFER_ID;
|
||||
if (binding.segment_count == 0 || is_written) {
|
||||
binding.buffer_id = FindBuffer(binding.device_addr, binding.size, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1503,10 +1555,12 @@ void BufferCache<P>::UpdateComputeStorageBuffers() {
|
||||
ForEachEnabledBit(channel_state->enabled_compute_storage_buffers, [&](u32 index) {
|
||||
// Resolve buffer
|
||||
Binding& binding = channel_state->compute_storage_buffers[index];
|
||||
binding.buffer_id = FindBuffer(binding.device_addr, binding.size, false);
|
||||
const bool is_written =
|
||||
((channel_state->written_compute_storage_buffers >> index) & 1) != 0;
|
||||
ResolveMultiRangeStorage(binding, is_written, compute_segments);
|
||||
const bool is_written = ((channel_state->written_compute_storage_buffers >> index) & 1) != 0;
|
||||
ResolveMultiRangeStorage(binding, compute_segments, is_written);
|
||||
binding.buffer_id = NULL_BUFFER_ID;
|
||||
if (binding.segment_count == 0 || is_written) {
|
||||
binding.buffer_id = FindBuffer(binding.device_addr, binding.size, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1523,6 +1577,10 @@ void BufferCache<P>::MarkWrittenBuffer(BufferId buffer_id, DAddr device_addr, u3
|
||||
if constexpr (!IS_OPENGL) {
|
||||
Buffer& buffer = slot_buffers[buffer_id];
|
||||
buffer.setWriteTick(runtime.CurrentTick());
|
||||
buffer.MarkContentModified();
|
||||
if (recording_draw) {
|
||||
draw_writes.push_back({buffer_id, device_addr, size});
|
||||
}
|
||||
}
|
||||
memory_tracker.MarkRegionAsGpuModified(device_addr, size);
|
||||
gpu_modified_ranges.Add(device_addr, size);
|
||||
@@ -1680,6 +1738,11 @@ BufferId BufferCache<P>::CreateBuffer(DAddr device_addr, u32 wanted_size,
|
||||
wanted_size = static_cast<u32>(device_addr_end - device_addr);
|
||||
const OverlapResult overlap = ResolveOverlaps(device_addr, wanted_size);
|
||||
const u32 size = static_cast<u32>(overlap.end - overlap.begin);
|
||||
if constexpr (requires(Buffer& buffer) { buffer.IsSparseCompatible(); }) {
|
||||
for (const BufferId overlap_id : overlap.ids) {
|
||||
sparse_compatible |= slot_buffers[overlap_id].IsSparseCompatible();
|
||||
}
|
||||
}
|
||||
const BufferId new_buffer_id =
|
||||
slot_buffers.insert(runtime, overlap.begin, size, sparse_compatible);
|
||||
auto& new_buffer = slot_buffers[new_buffer_id];
|
||||
@@ -1738,6 +1801,9 @@ void BufferCache<P>::TouchBuffer(Buffer& buffer, BufferId buffer_id) noexcept {
|
||||
|
||||
template <class P>
|
||||
bool BufferCache<P>::SynchronizeBuffer(Buffer& buffer, DAddr device_addr, u32 size) {
|
||||
if constexpr (!IS_OPENGL) {
|
||||
draw_hazard |= buffer.HasDrawHazard(draw_pass, draw_wfi, device_addr, size);
|
||||
}
|
||||
upload_copies.clear();
|
||||
u64 total_size_bytes = 0;
|
||||
u64 largest_copy = 0;
|
||||
@@ -1763,6 +1829,7 @@ bool BufferCache<P>::SynchronizeBuffer(Buffer& buffer, DAddr device_addr, u32 si
|
||||
template <class P>
|
||||
void BufferCache<P>::UploadMemory(Buffer& buffer, u64 total_size_bytes, u64 largest_copy,
|
||||
std::span<BufferCopy> copies) {
|
||||
buffer.MarkContentModified();
|
||||
if constexpr (USE_MEMORY_MAPS_FOR_UPLOADS) {
|
||||
MappedUploadMemory(buffer, total_size_bytes, copies);
|
||||
} else {
|
||||
@@ -1848,6 +1915,7 @@ void BufferCache<P>::InlineMemoryImplementation(DAddr dest_address, size_t copy_
|
||||
BufferId buffer_id = FindBuffer(dest_address, static_cast<u32>(copy_size), false);
|
||||
auto& buffer = slot_buffers[buffer_id];
|
||||
SynchronizeBuffer(buffer, dest_address, static_cast<u32>(copy_size));
|
||||
buffer.MarkContentModified();
|
||||
|
||||
if constexpr (USE_MEMORY_MAPS_FOR_UPLOADS) {
|
||||
auto upload_staging = runtime.UploadStagingBuffer(copy_size);
|
||||
@@ -1970,6 +2038,10 @@ void BufferCache<P>::DeleteBuffer(BufferId buffer_id, bool do_not_mark) {
|
||||
memory_tracker.MarkRegionAsCpuModified(buffer.CpuAddr(), buffer.SizeBytes());
|
||||
}
|
||||
|
||||
if (inline_buffer_id == buffer_id) {
|
||||
inline_buffer_id = NULL_BUFFER_ID;
|
||||
}
|
||||
|
||||
Unregister(buffer_id);
|
||||
|
||||
#ifdef YUZU_LEGACY
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "video_core/buffer_cache/buffer_base.h"
|
||||
#include "video_core/buffer_cache/virtual_range_cache.h"
|
||||
#include "video_core/control/channel_state_cache.h"
|
||||
#include "video_core/cache_reclaim.h"
|
||||
#include "video_core/delayed_destruction_ring.h"
|
||||
#include "video_core/dirty_flags.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
@@ -199,6 +200,8 @@ class BufferCache : public VideoCommon::ChannelSetupCaches<BufferCacheChannelInf
|
||||
|
||||
static constexpr s64 DEFAULT_EXPECTED_MEMORY = 512_MiB;
|
||||
static constexpr s64 DEFAULT_CRITICAL_MEMORY = 1_GiB;
|
||||
static constexpr u64 HEAP_PRESSURE_HEADROOM = 512_MiB;
|
||||
static constexpr u64 INLINE_TICKS_TO_DESTROY = 240;
|
||||
|
||||
// Debug Flags.
|
||||
|
||||
@@ -228,8 +231,12 @@ public:
|
||||
bool BindMultiRangeStorage(const Binding& binding, bool is_written,
|
||||
std::span<const MultiRangeSegment> pool);
|
||||
|
||||
void ResolveMultiRangeStorage(Binding& binding, bool is_written,
|
||||
std::vector<MultiRangeSegment>& pool);
|
||||
|
||||
|
||||
|
||||
|
||||
void ResolveMultiRangeStorage(Binding& binding, std::vector<MultiRangeSegment>& pool,
|
||||
bool is_written);
|
||||
|
||||
void UnmapGPUMemory(size_t as_id, GPUVAddr gpu_addr, size_t size);
|
||||
|
||||
@@ -253,6 +260,10 @@ public:
|
||||
|
||||
void UpdateComputeBuffers();
|
||||
|
||||
[[nodiscard]] bool TakeDrawHazard() noexcept;
|
||||
|
||||
void CommitDrawWrites();
|
||||
|
||||
void BindHostGeometryBuffers(bool is_indexed);
|
||||
|
||||
void BindHostStageBuffers(size_t stage);
|
||||
@@ -378,6 +389,8 @@ private:
|
||||
|
||||
void RunGarbageCollector();
|
||||
|
||||
void ReclaimInline();
|
||||
|
||||
void BindHostIndexBuffer();
|
||||
|
||||
void BindHostVertexBuffers();
|
||||
@@ -511,6 +524,17 @@ private:
|
||||
|
||||
boost::container::small_vector<BufferCopy, 4> upload_copies;
|
||||
|
||||
struct DrawWrite {
|
||||
BufferId buffer_id;
|
||||
DAddr device_addr;
|
||||
u32 size;
|
||||
};
|
||||
boost::container::small_vector<DrawWrite, 8> draw_writes;
|
||||
u64 draw_pass = 0;
|
||||
u64 draw_wfi = 0;
|
||||
bool draw_hazard = false;
|
||||
bool recording_draw = false;
|
||||
|
||||
MemoryTracker memory_tracker;
|
||||
Common::RangeSet<DAddr> uncommitted_gpu_modified_ranges;
|
||||
Common::RangeSet<DAddr> gpu_modified_ranges;
|
||||
@@ -537,8 +561,12 @@ private:
|
||||
std::vector<MultiRangeSegment> compute_segments;
|
||||
u64 frame_tick = 0;
|
||||
u64 total_used_memory = 0;
|
||||
u64 device_local_memory = 0;
|
||||
u64 minimum_memory = 0;
|
||||
u64 expected_memory = 0;
|
||||
u64 critical_memory = 0;
|
||||
u64 heap_headroom = 0;
|
||||
bool heap_pressure = false;
|
||||
BufferId inline_buffer_id;
|
||||
#ifdef YUZU_LEGACY
|
||||
bool immediately_free = false;
|
||||
|
||||
@@ -126,11 +126,11 @@ private:
|
||||
}
|
||||
|
||||
void ApplyDeferred() {
|
||||
std::vector<DeferredUnmap> pending;
|
||||
bool overflow = false;
|
||||
{
|
||||
std::scoped_lock lock{deferred_mutex};
|
||||
has_deferred.store(false, std::memory_order_release);
|
||||
pending.clear();
|
||||
pending.swap(deferred);
|
||||
overflow = deferred_overflow;
|
||||
deferred_overflow = false;
|
||||
@@ -165,6 +165,7 @@ private:
|
||||
|
||||
::Common::unordered_map<u64, Entry> entries;
|
||||
std::vector<DeferredUnmap> deferred;
|
||||
std::vector<DeferredUnmap> pending;
|
||||
std::mutex deferred_mutex;
|
||||
std::atomic<bool> has_deferred{false};
|
||||
bool deferred_overflow{};
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace VideoCommon {
|
||||
|
||||
struct ReclaimThresholds {
|
||||
u64 minimum{};
|
||||
u64 expected{};
|
||||
u64 critical{};
|
||||
u64 headroom{};
|
||||
};
|
||||
|
||||
[[nodiscard]] constexpr ReclaimThresholds MakeReclaimThresholds(u64 device_local_memory,
|
||||
u64 target_threshold,
|
||||
u64 default_expected,
|
||||
u64 default_critical,
|
||||
u64 default_headroom) {
|
||||
u64 critical = default_critical;
|
||||
if (device_local_memory != 0) {
|
||||
const u64 budget = (std::min)(device_local_memory, target_threshold);
|
||||
critical = (std::min)(critical, budget / 2);
|
||||
}
|
||||
const u64 expected = (std::min)(default_expected, (critical * 3) / 4);
|
||||
return ReclaimThresholds{
|
||||
.minimum = (expected * 3) / 4,
|
||||
.expected = expected,
|
||||
.critical = critical,
|
||||
.headroom = (std::clamp)(device_local_memory / 4, default_headroom, default_headroom * 2),
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace VideoCommon
|
||||
@@ -14,6 +14,10 @@
|
||||
namespace Tegra::Engines {
|
||||
|
||||
void Maxwell3D::DrawManager::ProcessMethodCall(Maxwell3D& maxwell3d, u32 method, u32 argument) {
|
||||
if (draw_state.draw_mode == DrawMode::InstanceArray &&
|
||||
method != MAXWELL3D_REG_INDEX(vertex_array_instance_subsequent)) {
|
||||
DrawDeferred(maxwell3d);
|
||||
}
|
||||
switch (method) {
|
||||
case MAXWELL3D_REG_INDEX(clear_surface):
|
||||
return Clear(maxwell3d, 1);
|
||||
@@ -72,7 +76,20 @@ void Maxwell3D::DrawManager::Clear(Maxwell3D& maxwell3d, u32 layer_count) {
|
||||
}
|
||||
}
|
||||
|
||||
void Maxwell3D::DrawManager::FlushInstanceArray(Maxwell3D& maxwell3d) {
|
||||
if (draw_state.draw_mode != DrawMode::InstanceArray) {
|
||||
return;
|
||||
}
|
||||
const u32 instance_count = draw_state.instance_count + 1;
|
||||
draw_state.draw_mode = DrawMode::General;
|
||||
draw_state.instance_count = 0;
|
||||
if (maxwell3d.ShouldExecute()) {
|
||||
maxwell3d.rasterizer->Draw(false, instance_count);
|
||||
}
|
||||
}
|
||||
|
||||
void Maxwell3D::DrawManager::DrawDeferred(Maxwell3D& maxwell3d) {
|
||||
FlushInstanceArray(maxwell3d);
|
||||
if (draw_state.draw_mode != DrawMode::Instance || draw_state.instance_count == 0) {
|
||||
return;
|
||||
}
|
||||
@@ -81,6 +98,7 @@ void Maxwell3D::DrawManager::DrawDeferred(Maxwell3D& maxwell3d) {
|
||||
}
|
||||
|
||||
void Maxwell3D::DrawManager::DrawArray(Maxwell3D& maxwell3d, Maxwell3D::Regs::PrimitiveTopology topology, u32 vertex_first, u32 vertex_count, u32 base_instance, u32 num_instances) {
|
||||
FlushInstanceArray(maxwell3d);
|
||||
draw_state.topology = topology;
|
||||
draw_state.vertex_buffer.first = vertex_first;
|
||||
draw_state.vertex_buffer.count = vertex_count;
|
||||
@@ -89,19 +107,29 @@ void Maxwell3D::DrawManager::DrawArray(Maxwell3D& maxwell3d, Maxwell3D::Regs::Pr
|
||||
}
|
||||
|
||||
void Maxwell3D::DrawManager::DrawArrayInstanced(Maxwell3D& maxwell3d, Maxwell3D::Regs::PrimitiveTopology topology, u32 vertex_first, u32 vertex_count, bool subsequent) {
|
||||
if (subsequent && draw_state.draw_mode == DrawMode::InstanceArray &&
|
||||
instance_topology == topology && draw_state.vertex_buffer.first == vertex_first &&
|
||||
draw_state.vertex_buffer.count == vertex_count) {
|
||||
++draw_state.instance_count;
|
||||
return;
|
||||
}
|
||||
u32 base_instance = 0;
|
||||
if (subsequent) {
|
||||
base_instance = draw_state.base_instance + draw_state.instance_count + 1;
|
||||
}
|
||||
DrawDeferred(maxwell3d);
|
||||
instance_topology = topology;
|
||||
draw_state.topology = topology;
|
||||
draw_state.vertex_buffer.first = vertex_first;
|
||||
draw_state.vertex_buffer.count = vertex_count;
|
||||
if (!subsequent) {
|
||||
draw_state.instance_count = 1;
|
||||
}
|
||||
draw_state.base_instance = draw_state.instance_count - 1;
|
||||
draw_state.draw_mode = DrawMode::Instance;
|
||||
draw_state.instance_count++;
|
||||
ProcessDraw(maxwell3d, false, 1);
|
||||
draw_state.base_instance = base_instance;
|
||||
draw_state.instance_count = 0;
|
||||
draw_state.draw_mode = DrawMode::InstanceArray;
|
||||
UpdateTopology(maxwell3d);
|
||||
}
|
||||
|
||||
void Maxwell3D::DrawManager::DrawIndex(Maxwell3D& maxwell3d, Maxwell3D::Regs::PrimitiveTopology topology, u32 index_first, u32 index_count, u32 base_index, u32 base_instance, u32 num_instances) {
|
||||
FlushInstanceArray(maxwell3d);
|
||||
draw_state.topology = topology;
|
||||
draw_state.index_buffer = maxwell3d.regs.index_buffer;
|
||||
draw_state.index_buffer.first = index_first;
|
||||
@@ -112,11 +140,13 @@ void Maxwell3D::DrawManager::DrawIndex(Maxwell3D& maxwell3d, Maxwell3D::Regs::Pr
|
||||
}
|
||||
|
||||
void Maxwell3D::DrawManager::DrawArrayIndirect(Maxwell3D& maxwell3d, Maxwell3D::Regs::PrimitiveTopology topology) {
|
||||
FlushInstanceArray(maxwell3d);
|
||||
draw_state.topology = topology;
|
||||
ProcessDrawIndirect(maxwell3d);
|
||||
}
|
||||
|
||||
void Maxwell3D::DrawManager::DrawIndexedIndirect(Maxwell3D& maxwell3d, Maxwell3D::Regs::PrimitiveTopology topology, u32 index_first, u32 index_count) {
|
||||
FlushInstanceArray(maxwell3d);
|
||||
draw_state.topology = topology;
|
||||
draw_state.index_buffer = maxwell3d.regs.index_buffer;
|
||||
draw_state.index_buffer.first = index_first;
|
||||
@@ -186,6 +216,9 @@ void Maxwell3D::DrawManager::DrawBegin(Maxwell3D& maxwell3d) {
|
||||
|
||||
void Maxwell3D::DrawManager::DrawEnd(Maxwell3D& maxwell3d, u32 instance_count, bool force_draw) {
|
||||
switch (draw_state.draw_mode) {
|
||||
case DrawMode::InstanceArray:
|
||||
FlushInstanceArray(maxwell3d);
|
||||
break;
|
||||
case DrawMode::Instance:
|
||||
if (!force_draw) {
|
||||
break;
|
||||
@@ -249,28 +282,68 @@ void Maxwell3D::DrawManager::DrawTexture(Maxwell3D& maxwell3d) {
|
||||
}
|
||||
|
||||
void Maxwell3D::DrawManager::UpdateTopology(Maxwell3D& maxwell3d) {
|
||||
using Topology = Maxwell3D::Regs::PrimitiveTopology;
|
||||
using Override = Maxwell3D::Regs::PrimitiveTopologyOverride;
|
||||
switch (maxwell3d.regs.primitive_topology_control) {
|
||||
case Maxwell3D::Regs::PrimitiveTopologyControl::UseInBeginMethods:
|
||||
break;
|
||||
case Maxwell3D::Regs::PrimitiveTopologyControl::UseSeparateState:
|
||||
switch (maxwell3d.regs.topology_override) {
|
||||
case Maxwell3D::Regs::PrimitiveTopologyOverride::None:
|
||||
case Override::None:
|
||||
break;
|
||||
case Maxwell3D::Regs::PrimitiveTopologyOverride::Points:
|
||||
draw_state.topology = Maxwell3D::Regs::PrimitiveTopology::Points;
|
||||
case Override::Points:
|
||||
case Override::LegacyPoints:
|
||||
draw_state.topology = Topology::Points;
|
||||
break;
|
||||
case Maxwell3D::Regs::PrimitiveTopologyOverride::Lines:
|
||||
draw_state.topology = Maxwell3D::Regs::PrimitiveTopology::Lines;
|
||||
case Override::Lines:
|
||||
case Override::LegacyLines:
|
||||
case Override::LegacyLinesImm:
|
||||
case Override::LegacyIndexedLines:
|
||||
case Override::LegacyIndexedLines2:
|
||||
draw_state.topology = Topology::Lines;
|
||||
break;
|
||||
case Maxwell3D::Regs::PrimitiveTopologyOverride::LineStrip:
|
||||
draw_state.topology = Maxwell3D::Regs::PrimitiveTopology::LineStrip;
|
||||
case Override::LineStrip:
|
||||
case Override::LegacyLineStrip:
|
||||
case Override::LegacyIndexedLineStrip:
|
||||
draw_state.topology = Topology::LineStrip;
|
||||
break;
|
||||
default:
|
||||
draw_state.topology = Maxwell3D::Regs::PrimitiveTopology(maxwell3d.regs.topology_override);
|
||||
case Override::Triangles:
|
||||
case Override::LegacyTriangles:
|
||||
case Override::LegacyIndexedTriangles:
|
||||
case Override::LegacyIndexedTriangles2:
|
||||
draw_state.topology = Topology::Triangles;
|
||||
break;
|
||||
case Override::TriangleStrip:
|
||||
case Override::LegacyTriangleStrip:
|
||||
case Override::LegacyIndexedTriangleStrip:
|
||||
draw_state.topology = Topology::TriangleStrip;
|
||||
break;
|
||||
case Override::LegacyTriangleFan:
|
||||
case Override::LegacyTriangleFanImm:
|
||||
case Override::LegacyIndexedTriangleFan:
|
||||
draw_state.topology = Topology::TriangleFan;
|
||||
break;
|
||||
case Override::LinesAdjacency:
|
||||
draw_state.topology = Topology::LinesAdjacency;
|
||||
break;
|
||||
case Override::LineStripAdjacency:
|
||||
draw_state.topology = Topology::LineStripAdjacency;
|
||||
break;
|
||||
case Override::TrianglesAdjacency:
|
||||
draw_state.topology = Topology::TrianglesAdjacency;
|
||||
break;
|
||||
case Override::TriangleStripAdjacency:
|
||||
draw_state.topology = Topology::TriangleStripAdjacency;
|
||||
break;
|
||||
case Override::Patches:
|
||||
draw_state.topology = Topology::Patches;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (u32(draw_state.topology) > u32(Topology::Patches)) {
|
||||
draw_state.topology = Topology::Triangles;
|
||||
}
|
||||
}
|
||||
|
||||
void Maxwell3D::DrawManager::ProcessDraw(Maxwell3D& maxwell3d, bool draw_indexed, u32 instance_count) {
|
||||
|
||||
@@ -3034,7 +3034,7 @@ public:
|
||||
// clang-format on
|
||||
|
||||
struct DrawManager {
|
||||
enum class DrawMode : u32 { General = 0, Instance, InlineIndex };
|
||||
enum class DrawMode : u32 { General = 0, Instance, InlineIndex, InstanceArray };
|
||||
struct State {
|
||||
Maxwell3D::Regs::PrimitiveTopology topology{};
|
||||
DrawMode draw_mode{};
|
||||
@@ -3071,6 +3071,7 @@ public:
|
||||
void ProcessMethodCall(Maxwell3D& maxwell3d, u32 method, u32 argument);
|
||||
void Clear(Maxwell3D& maxwell3d, u32 layer_count);
|
||||
void DrawDeferred(Maxwell3D& maxwell3d);
|
||||
void FlushInstanceArray(Maxwell3D& maxwell3d);
|
||||
void DrawArray(Maxwell3D& maxwell3d, Maxwell3D::Regs::PrimitiveTopology topology, u32 vertex_first, u32 vertex_count, u32 base_instance, u32 num_instances);
|
||||
void DrawArrayInstanced(Maxwell3D& maxwell3d, Maxwell3D::Regs::PrimitiveTopology topology, u32 vertex_first, u32 vertex_count, bool subsequent);
|
||||
void DrawIndex(Maxwell3D& maxwell3d, Maxwell3D::Regs::PrimitiveTopology topology, u32 index_first, u32 index_count, u32 base_index, u32 base_instance, u32 num_instances);
|
||||
@@ -3086,6 +3087,7 @@ public:
|
||||
void ProcessDraw(Maxwell3D& maxwell3d, bool draw_indexed, u32 instance_count);
|
||||
void ProcessDrawIndirect(Maxwell3D& maxwell3d);
|
||||
State draw_state{};
|
||||
Maxwell3D::Regs::PrimitiveTopology instance_topology{};
|
||||
DrawTextureState draw_texture_state{};
|
||||
IndirectParams indirect_state{};
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -92,7 +95,8 @@ void Bilinear(std::span<const f32> input, std::span<f32> output, size_t src_widt
|
||||
|
||||
const auto read_src = [&](f32 in_x, f32 in_y) {
|
||||
const size_t read_from =
|
||||
((static_cast<size_t>(in_x) * src_width + static_cast<size_t>(in_y)) >> 32) *
|
||||
((std::min)(static_cast<size_t>(in_y), src_height - 1) * src_width +
|
||||
(std::min)(static_cast<size_t>(in_x), src_width - 1)) *
|
||||
ir_components;
|
||||
return std::span<const f32>(&input[read_from], ir_components);
|
||||
};
|
||||
|
||||
@@ -152,6 +152,10 @@ struct GPU::Impl {
|
||||
|
||||
/// Tick pending requests within the GPU.
|
||||
void TickWork() {
|
||||
if (last_sync_fence.load(std::memory_order_relaxed) ==
|
||||
current_sync_fence.load(std::memory_order_relaxed)) {
|
||||
return;
|
||||
}
|
||||
std::unique_lock lck{sync_request_mutex};
|
||||
while (!sync_requests.empty()) {
|
||||
auto request = std::move(sync_requests.front());
|
||||
@@ -328,7 +332,7 @@ struct GPU::Impl {
|
||||
|
||||
std::list<std::function<void()>> sync_requests;
|
||||
std::atomic<u64> current_sync_fence{};
|
||||
u64 last_sync_fence{};
|
||||
std::atomic<u64> last_sync_fence{};
|
||||
std::mutex sync_request_mutex;
|
||||
std::condition_variable sync_request_cv;
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ set(SHADER_FILES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/blit_depth_msaa.frag
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/blit_depth_stencil_msaa.frag
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_3d.comp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_3d_bcn.comp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/convert_abgr8_to_d24s8.frag
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/convert_abgr8_to_d32f.frag
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/convert_d32f_to_abgr8.frag
|
||||
@@ -79,6 +78,7 @@ set(SHADER_FILES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/vulkan_fidelityfx_fsr_easu_fp32.frag
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/vulkan_fidelityfx_fsr_rcas_fp16.frag
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/vulkan_fidelityfx_fsr_rcas_fp32.frag
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/vulkan_indirect_quads.comp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/vulkan_present.frag
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/vulkan_present.vert
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/vulkan_present_scaleforce_fp16.frag
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -51,7 +51,7 @@ layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU128 { uvec4 u1
|
||||
|
||||
layout(binding = BINDING_OUTPUT_IMAGE) uniform writeonly uimage2DArray output_image;
|
||||
|
||||
layout(local_size_x = 32, local_size_y = 32, local_size_z = 1) in;
|
||||
layout(local_size_x = 32, local_size_y = 8, local_size_z = 1) in;
|
||||
|
||||
const uint GOB_SIZE_X = 64;
|
||||
const uint GOB_SIZE_Y = 8;
|
||||
@@ -65,19 +65,11 @@ 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 SwizzleTable(pos.y * 64 + pos.x);
|
||||
return ((pos.x & 32u) << 3u) | ((pos.y & 6u) << 5u) |
|
||||
((pos.x & 16u) << 1u) | ((pos.y & 1u) << 4u) |
|
||||
(pos.x & 15u);
|
||||
}
|
||||
|
||||
uvec4 ReadTexel(uint offset) {
|
||||
|
||||
@@ -53,7 +53,7 @@ layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU128 { uvec4 u1
|
||||
|
||||
layout(binding = BINDING_OUTPUT_IMAGE) uniform writeonly uimage3D output_image;
|
||||
|
||||
layout(local_size_x = 16, local_size_y = 8, local_size_z = 8) in;
|
||||
layout(local_size_x = 16, local_size_y = 8, local_size_z = 2) in;
|
||||
|
||||
const uint GOB_SIZE_X = 64;
|
||||
const uint GOB_SIZE_Y = 8;
|
||||
@@ -67,19 +67,11 @@ 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 SwizzleTable(pos.y * 64 + pos.x);
|
||||
return ((pos.x & 32u) << 3u) | ((pos.y & 6u) << 5u) |
|
||||
((pos.x & 16u) << 1u) | ((pos.y & 1u) << 4u) |
|
||||
(pos.x & 15u);
|
||||
}
|
||||
|
||||
uvec4 ReadTexel(uint offset) {
|
||||
|
||||
@@ -1,164 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#version 430
|
||||
|
||||
#ifdef VULKAN
|
||||
#extension GL_EXT_shader_16bit_storage : require
|
||||
#extension GL_EXT_shader_8bit_storage : require
|
||||
#define HAS_EXTENDED_TYPES 1
|
||||
#define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants {
|
||||
#define END_PUSH_CONSTANTS };
|
||||
#define UNIFORM(n)
|
||||
#define BINDING_INPUT_BUFFER 0
|
||||
#define BINDING_OUTPUT_BUFFER 1
|
||||
#else
|
||||
#extension GL_NV_gpu_shader5 : enable
|
||||
#ifdef GL_NV_gpu_shader5
|
||||
#define HAS_EXTENDED_TYPES 1
|
||||
#else
|
||||
#define HAS_EXTENDED_TYPES 0
|
||||
#endif
|
||||
#define BEGIN_PUSH_CONSTANTS
|
||||
#define END_PUSH_CONSTANTS
|
||||
#define UNIFORM(n) layout(location = n) uniform
|
||||
#define BINDING_INPUT_BUFFER 1
|
||||
#define BINDING_OUTPUT_BUFFER 0
|
||||
#endif
|
||||
|
||||
// --- Push Constants / Uniforms ---
|
||||
#ifdef VULKAN
|
||||
layout(push_constant) uniform PushConstants {
|
||||
uvec3 blocks_dim; // Offset 0
|
||||
uint bytes_per_block_log2; // Offset 12
|
||||
|
||||
uvec3 origin; // Offset 16
|
||||
uint slice_size; // Offset 28
|
||||
|
||||
uint block_size; // Offset 32
|
||||
uint x_shift; // Offset 36
|
||||
uint block_height; // Offset 40
|
||||
uint block_height_mask; // Offset 44
|
||||
|
||||
uint block_depth; // Offset 48
|
||||
uint block_depth_mask; // Offset 52
|
||||
int _pad; // Offset 56
|
||||
|
||||
ivec3 destination; // Offset 60
|
||||
} pc;
|
||||
#else
|
||||
BEGIN_PUSH_CONSTANTS
|
||||
UNIFORM(0) uvec3 origin;
|
||||
UNIFORM(1) ivec3 destination;
|
||||
UNIFORM(2) uint bytes_per_block_log2;
|
||||
UNIFORM(3) uint slice_size;
|
||||
UNIFORM(4) uint block_size;
|
||||
UNIFORM(5) uint x_shift;
|
||||
UNIFORM(6) uint block_height;
|
||||
UNIFORM(7) uint block_height_mask;
|
||||
UNIFORM(8) uint block_depth;
|
||||
UNIFORM(9) uint block_depth_mask;
|
||||
UNIFORM(10) uvec3 blocks_dim;
|
||||
END_PUSH_CONSTANTS
|
||||
#define pc // Map pc prefix to nothing for OpenGL compatibility
|
||||
#endif
|
||||
|
||||
// --- Buffers ---
|
||||
#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[]; };
|
||||
#endif
|
||||
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU32 { uint u32data[]; };
|
||||
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU64 { uvec2 u64data[]; };
|
||||
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU128 { uvec4 u128data[]; };
|
||||
|
||||
layout(binding = BINDING_OUTPUT_BUFFER, std430) writeonly buffer OutputBuffer {
|
||||
uint out_u32[];
|
||||
};
|
||||
|
||||
// --- Constants ---
|
||||
layout(local_size_x = 8, local_size_y = 8, local_size_z = 4) in;
|
||||
|
||||
const uint GOB_SIZE_X = 64;
|
||||
const uint GOB_SIZE_Y = 8;
|
||||
const uint GOB_SIZE_Z = 1;
|
||||
const uint GOB_SIZE = GOB_SIZE_X * GOB_SIZE_Y * GOB_SIZE_Z;
|
||||
|
||||
const uint GOB_SIZE_X_SHIFT = 6;
|
||||
const uint GOB_SIZE_Y_SHIFT = 3;
|
||||
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 SwizzleTable(pos.y * 64u + pos.x);
|
||||
}
|
||||
|
||||
uvec4 ReadTexel(uint offset) {
|
||||
uint bpl2 = pc.bytes_per_block_log2;
|
||||
switch (bpl2) {
|
||||
#if HAS_EXTENDED_TYPES
|
||||
case 0u: return uvec4(u8data[offset], 0u, 0u, 0u);
|
||||
case 1u: return uvec4(u16data[offset / 2u], 0u, 0u, 0u);
|
||||
#else
|
||||
case 0u: return uvec4(bitfieldExtract(u32data[offset / 4u], int((offset * 8u) & 24u), 8), 0u, 0u, 0u);
|
||||
case 1u: return uvec4(bitfieldExtract(u32data[offset / 4u], int((offset * 8u) & 16u), 16), 0u, 0u, 0u);
|
||||
#endif
|
||||
case 2u: return uvec4(u32data[offset / 4u], 0u, 0u, 0u);
|
||||
case 3u: return uvec4(u64data[offset / 8u], 0u, 0u);
|
||||
case 4u: return u128data[offset / 16u];
|
||||
}
|
||||
return uvec4(0u);
|
||||
}
|
||||
|
||||
void main() {
|
||||
uvec3 block_coord = gl_GlobalInvocationID;
|
||||
if (any(greaterThanEqual(block_coord, pc.blocks_dim))) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint bytes_per_block = 1u << pc.bytes_per_block_log2;
|
||||
// Origin is in pixels, divide by 4 for block-space (e.g. BCn formats)
|
||||
uvec3 pos;
|
||||
pos.x = (block_coord.x + (pc.origin.x >> 2u)) * bytes_per_block;
|
||||
pos.y = block_coord.y + (pc.origin.y >> 2u);
|
||||
pos.z = block_coord.z + pc.origin.z;
|
||||
|
||||
uint swizzle = SwizzleOffset(pos.xy);
|
||||
uint block_y = pos.y >> GOB_SIZE_Y_SHIFT;
|
||||
uint offset = 0u;
|
||||
// Apply block-linear offsets
|
||||
offset += (pos.z >> pc.block_depth) * pc.slice_size;
|
||||
offset += (pos.z & pc.block_depth_mask) << (GOB_SIZE_SHIFT + pc.block_height);
|
||||
offset += (block_y >> pc.block_height) * pc.block_size;
|
||||
offset += (block_y & pc.block_height_mask) << GOB_SIZE_SHIFT;
|
||||
offset += (pos.x >> GOB_SIZE_X_SHIFT) << pc.x_shift;
|
||||
offset += swizzle;
|
||||
|
||||
uvec4 texel = ReadTexel(offset);
|
||||
|
||||
// Calculate linear output index
|
||||
uint block_index = block_coord.x +
|
||||
(block_coord.y * pc.blocks_dim.x) +
|
||||
(block_coord.z * pc.blocks_dim.x * pc.blocks_dim.y);
|
||||
uint out_idx = block_index * (bytes_per_block >> 2u);
|
||||
|
||||
out_u32[out_idx] = texel.x;
|
||||
out_u32[out_idx + 1u] = texel.y;
|
||||
if (pc.bytes_per_block_log2 == 4u) {
|
||||
out_u32[out_idx + 2u] = texel.z;
|
||||
out_u32[out_idx + 3u] = texel.w;
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,7 @@
|
||||
BEGIN_PUSH_CONSTANTS
|
||||
UNIFORM(0) uvec2 origin;
|
||||
UNIFORM(1) ivec2 destination;
|
||||
UNIFORM(2) uint bytes_per_block;
|
||||
UNIFORM(2) uint bytes_per_block_log2;
|
||||
UNIFORM(3) uint pitch;
|
||||
END_PUSH_CONSTANTS
|
||||
|
||||
@@ -47,26 +47,26 @@ layout(binding = BINDING_INPUT_BUFFER, std430) readonly buffer InputBufferU128 {
|
||||
|
||||
layout(binding = BINDING_OUTPUT_IMAGE) writeonly uniform uimage2D output_image;
|
||||
|
||||
layout(local_size_x = 32, local_size_y = 32, local_size_z = 1) in;
|
||||
layout(local_size_x = 32, local_size_y = 8, local_size_z = 1) in;
|
||||
|
||||
uvec4 ReadTexel(uint offset) {
|
||||
switch (bytes_per_block) {
|
||||
switch (bytes_per_block_log2) {
|
||||
#if HAS_EXTENDED_TYPES
|
||||
case 1:
|
||||
case 0:
|
||||
return uvec4(u8data[offset], 0, 0, 0);
|
||||
case 2:
|
||||
case 1:
|
||||
return uvec4(u16data[offset / 2], 0, 0, 0);
|
||||
#else
|
||||
case 1:
|
||||
case 0:
|
||||
return uvec4(bitfieldExtract(u32data[offset / 4], int((offset * 8) & 24), 8), 0, 0, 0);
|
||||
case 2:
|
||||
case 1:
|
||||
return uvec4(bitfieldExtract(u32data[offset / 4], int((offset * 8) & 16), 16), 0, 0, 0);
|
||||
#endif
|
||||
case 4:
|
||||
case 2:
|
||||
return uvec4(u32data[offset / 4], 0, 0, 0);
|
||||
case 8:
|
||||
case 3:
|
||||
return uvec4(u64data[offset / 8], 0, 0);
|
||||
case 16:
|
||||
case 4:
|
||||
return u128data[offset / 16];
|
||||
}
|
||||
return uvec4(0);
|
||||
@@ -76,7 +76,7 @@ void main() {
|
||||
uvec2 pos = gl_GlobalInvocationID.xy + origin;
|
||||
|
||||
uint offset = 0;
|
||||
offset += pos.x * bytes_per_block;
|
||||
offset += pos.x << bytes_per_block_log2;
|
||||
offset += pos.y * pitch;
|
||||
|
||||
const uvec4 texel = ReadTexel(offset);
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#version 460 core
|
||||
|
||||
layout (local_size_x = 32) in;
|
||||
|
||||
layout (std430, set = 0, binding = 0) readonly buffer InputBuffer {
|
||||
uint input_commands[];
|
||||
};
|
||||
|
||||
layout (std430, set = 0, binding = 1) writeonly buffer OutputBuffer {
|
||||
uint output_commands[];
|
||||
};
|
||||
|
||||
layout (push_constant) uniform PushConstants {
|
||||
uint num_draws;
|
||||
uint src_stride;
|
||||
};
|
||||
|
||||
void main() {
|
||||
uint draw = gl_GlobalInvocationID.x;
|
||||
if (draw >= num_draws) {
|
||||
return;
|
||||
}
|
||||
uint src = draw * src_stride;
|
||||
uint dst = draw * 5;
|
||||
output_commands[dst + 0] = input_commands[src + 0] / 4 * 6;
|
||||
output_commands[dst + 1] = input_commands[src + 1];
|
||||
output_commands[dst + 2] = input_commands[src + 2] / 4 * 6;
|
||||
output_commands[dst + 3] = input_commands[src + 3];
|
||||
output_commands[dst + 4] = input_commands[src + 4];
|
||||
}
|
||||
@@ -46,8 +46,8 @@ MemoryManager::MemoryManager(Core::System& system_, MaxwellDeviceMemoryManager&
|
||||
page_table_mask = page_table_size - 1;
|
||||
big_page_table_mask = big_page_table_size - 1;
|
||||
|
||||
big_page_table_dev.ResizeAndClear(big_page_table_size);
|
||||
big_entries.resize(big_page_table_size / 32, 0);
|
||||
big_page_table_dev.resize(big_page_table_size);
|
||||
big_page_continuous.resize(big_page_table_size / continuous_bits, 0);
|
||||
entries.resize(page_table_size / 32, 0);
|
||||
}
|
||||
@@ -143,7 +143,7 @@ GPUVAddr MemoryManager::BigPageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] DAddr
|
||||
const DAddr current_dev_addr = dev_addr + offset;
|
||||
const auto index = PageEntryIndex(current_gpu_addr, true);
|
||||
const u32 sub_value = static_cast<u32>(current_dev_addr >> cpu_page_bits);
|
||||
big_page_table_dev.Set(index, sub_value);
|
||||
big_page_table_dev[index] = sub_value;
|
||||
const bool is_continuous = ([&] {
|
||||
uintptr_t base_ptr{
|
||||
reinterpret_cast<uintptr_t>(memory.GetPointer<u8>(current_dev_addr))};
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#include "common/multi_level_page_table.h"
|
||||
#include "common/range_map.h"
|
||||
#include "common/scratch_buffer.h"
|
||||
#include "common/sparse_large_vector.h"
|
||||
#include "common/virtual_buffer.h"
|
||||
#include "video_core/invalidation_accumulator.h"
|
||||
#include "video_core/cache_types.h"
|
||||
#include "video_core/host1x/gpu_device_memory_manager.h"
|
||||
@@ -214,7 +214,7 @@ private:
|
||||
|
||||
Common::MultiLevelPageTable<u32> page_table;
|
||||
Common::RangeMap<GPUVAddr, PTEKind> kind_map;
|
||||
Common::SparseLargeVector<u32> big_page_table_dev;
|
||||
Common::VirtualBuffer<u32> big_page_table_dev;
|
||||
|
||||
std::vector<u64> big_page_continuous;
|
||||
boost::container::small_vector<std::pair<DAddr, std::size_t>, 32> page_stash{};
|
||||
|
||||
@@ -235,9 +235,7 @@ void ApplySwizzle(GLuint handle, PixelFormat format, std::array<SwizzleSource, 4
|
||||
[[nodiscard]] bool CanBeAccelerated(const TextureCacheRuntime& runtime,
|
||||
const VideoCommon::ImageInfo& info) {
|
||||
if (IsPixelFormatASTC(info.format) && info.size.depth == 1 && !runtime.HasNativeASTC()) {
|
||||
return Settings::values.accelerate_astc.GetValue() == Settings::AstcDecodeMode::Gpu &&
|
||||
Settings::values.astc_recompression.GetValue() ==
|
||||
Settings::AstcRecompression::Uncompressed;
|
||||
return Settings::values.accelerate_astc.GetValue() == Settings::AstcDecodeMode::Gpu;
|
||||
}
|
||||
// Disable other accelerated uploads for now as they don't implement swizzled uploads
|
||||
return false;
|
||||
@@ -442,23 +440,6 @@ OGLTexture MakeImage(const VideoCommon::ImageInfo& info, GLenum gl_internal_form
|
||||
return GL_R32UI;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsAstcRecompressionEnabled() {
|
||||
return Settings::values.astc_recompression.GetValue() !=
|
||||
Settings::AstcRecompression::Uncompressed;
|
||||
}
|
||||
|
||||
[[nodiscard]] GLenum SelectAstcFormat(PixelFormat format, bool is_srgb) {
|
||||
switch (Settings::values.astc_recompression.GetValue()) {
|
||||
case Settings::AstcRecompression::Bc1:
|
||||
return is_srgb ? GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT : GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
|
||||
break;
|
||||
case Settings::AstcRecompression::Bc3:
|
||||
return is_srgb ? GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT : GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
|
||||
break;
|
||||
default:
|
||||
return is_srgb ? GL_SRGB8_ALPHA8 : GL_RGBA8;
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
TextureCacheRuntime::TextureCacheRuntime(const Device& device_, ProgramManager& program_manager,
|
||||
@@ -556,7 +537,7 @@ void TextureCacheRuntime::Finish() {
|
||||
glFinish();
|
||||
}
|
||||
|
||||
StagingBufferMap TextureCacheRuntime::UploadStagingBuffer(size_t size, bool deferred) {
|
||||
StagingBufferMap TextureCacheRuntime::UploadStagingBuffer(size_t size) {
|
||||
return staging_buffer_pool.RequestUploadBuffer(size);
|
||||
}
|
||||
|
||||
@@ -651,8 +632,7 @@ void TextureCacheRuntime::BlitFramebuffer(Framebuffer* dst, Framebuffer* src,
|
||||
}
|
||||
|
||||
void TextureCacheRuntime::AccelerateImageUpload(Image& image, const StagingBufferMap& map,
|
||||
std::span<const SwizzleParameters> swizzles,
|
||||
u32 z_start, u32 z_count) {
|
||||
std::span<const SwizzleParameters> swizzles) {
|
||||
switch (image.info.type) {
|
||||
case ImageType::e2D:
|
||||
if (IsPixelFormatASTC(image.info.format)) {
|
||||
@@ -709,11 +689,6 @@ Image::Image(TextureCacheRuntime& runtime_, const VideoCommon::ImageInfo& info_,
|
||||
gl_internal_format = is_srgb ? GL_SRGB8_ALPHA8 : GL_RGBA8;
|
||||
gl_format = GL_RGBA;
|
||||
gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
|
||||
|
||||
if (IsPixelFormatASTC(info.format) && IsAstcRecompressionEnabled()) {
|
||||
gl_internal_format = SelectAstcFormat(info.format, is_srgb);
|
||||
gl_format = GL_NONE;
|
||||
}
|
||||
} else {
|
||||
const auto& tuple = MaxwellToGL::GetFormatTuple(info.format);
|
||||
gl_internal_format = tuple.internal_format;
|
||||
@@ -1110,10 +1085,6 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
|
||||
if (True(image.flags & ImageFlagBits::Converted)) {
|
||||
const bool is_srgb = IsPixelFormatSRGB(info.format);
|
||||
internal_format = is_srgb ? GL_SRGB8_ALPHA8 : GL_RGBA8;
|
||||
|
||||
if (IsPixelFormatASTC(info.format) && IsAstcRecompressionEnabled()) {
|
||||
internal_format = SelectAstcFormat(info.format, is_srgb);
|
||||
}
|
||||
} else {
|
||||
internal_format = MaxwellToGL::GetFormatTuple(format).internal_format;
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ public:
|
||||
|
||||
void FlushDeferredClear() {}
|
||||
|
||||
StagingBufferMap UploadStagingBuffer(size_t size, bool deferred = false);
|
||||
StagingBufferMap UploadStagingBuffer(size_t size);
|
||||
|
||||
StagingBufferMap DownloadStagingBuffer(size_t size, bool deferred = false);
|
||||
|
||||
@@ -121,8 +121,7 @@ public:
|
||||
Tegra::Engines::Fermi2D::Operation operation);
|
||||
|
||||
void AccelerateImageUpload(Image& image, const StagingBufferMap& map,
|
||||
std::span<const VideoCommon::SwizzleParameters> swizzles,
|
||||
u32 z_start, u32 z_count);
|
||||
std::span<const VideoCommon::SwizzleParameters> swizzles);
|
||||
|
||||
void InsertUploadMemoryBarrier();
|
||||
|
||||
@@ -229,8 +228,6 @@ public:
|
||||
|
||||
bool ScaleDown(bool ignore = false);
|
||||
|
||||
u64 allocation_tick;
|
||||
|
||||
private:
|
||||
void CopyBufferToImage(const VideoCommon::BufferImageCopy& copy, size_t buffer_offset);
|
||||
|
||||
@@ -367,7 +364,6 @@ private:
|
||||
};
|
||||
|
||||
struct TextureCacheParams {
|
||||
static constexpr bool ENABLE_VALIDATION = true;
|
||||
static constexpr bool FRAMEBUFFER_BLITS = true;
|
||||
static constexpr bool HAS_EMULATED_COPIES = true;
|
||||
static constexpr bool HAS_DEVICE_MEMORY_INFO = true;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <bit>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
|
||||
@@ -116,7 +117,7 @@ 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 Extent3D WORKGROUP_SIZE{32, 8, 1};
|
||||
static constexpr GLuint BINDING_INPUT_BUFFER = 0;
|
||||
static constexpr GLuint BINDING_OUTPUT_IMAGE = 0;
|
||||
|
||||
@@ -151,7 +152,7 @@ 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 Extent3D WORKGROUP_SIZE{16, 8, 2};
|
||||
static constexpr GLuint BINDING_INPUT_BUFFER = 0;
|
||||
static constexpr GLuint BINDING_OUTPUT_IMAGE = 0;
|
||||
|
||||
@@ -189,12 +190,12 @@ void UtilShaders::BlockLinearUpload3D(Image& image, const StagingBufferMap& map,
|
||||
|
||||
void UtilShaders::PitchUpload(Image& image, const StagingBufferMap& map,
|
||||
std::span<const SwizzleParameters> swizzles) {
|
||||
static constexpr Extent3D WORKGROUP_SIZE{32, 32, 1};
|
||||
static constexpr Extent3D WORKGROUP_SIZE{32, 8, 1};
|
||||
static constexpr GLuint BINDING_INPUT_BUFFER = 0;
|
||||
static constexpr GLuint BINDING_OUTPUT_IMAGE = 0;
|
||||
static constexpr GLuint LOC_ORIGIN = 0;
|
||||
static constexpr GLuint LOC_DESTINATION = 1;
|
||||
static constexpr GLuint LOC_BYTES_PER_BLOCK = 2;
|
||||
static constexpr GLuint LOC_BYTES_PER_BLOCK_LOG2 = 2;
|
||||
static constexpr GLuint LOC_PITCH = 3;
|
||||
|
||||
const u32 bytes_per_block = BytesPerBlock(image.info.format);
|
||||
@@ -208,7 +209,7 @@ void UtilShaders::PitchUpload(Image& image, const StagingBufferMap& map,
|
||||
glFlushMappedNamedBufferRange(map.buffer, map.offset, image.guest_size_bytes);
|
||||
glUniform2ui(LOC_ORIGIN, 0, 0);
|
||||
glUniform2i(LOC_DESTINATION, 0, 0);
|
||||
glUniform1ui(LOC_BYTES_PER_BLOCK, bytes_per_block);
|
||||
glUniform1ui(LOC_BYTES_PER_BLOCK_LOG2, static_cast<GLuint>(std::countr_zero(bytes_per_block)));
|
||||
glUniform1ui(LOC_PITCH, pitch);
|
||||
glBindImageTexture(BINDING_OUTPUT_IMAGE, image.StorageHandle(), 0, GL_FALSE, 0, GL_WRITE_ONLY,
|
||||
format);
|
||||
|
||||
@@ -452,45 +452,19 @@ VkExtent2D GetConversionExtent(const ImageView& src_image_view) {
|
||||
};
|
||||
}
|
||||
|
||||
void TransitionImageLayout(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayout target_layout,
|
||||
VkImageLayout source_layout = VK_IMAGE_LAYOUT_GENERAL) {
|
||||
constexpr VkFlags flags{VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
|
||||
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT};
|
||||
const VkImageMemoryBarrier barrier{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.pNext = nullptr,
|
||||
.srcAccessMask = flags,
|
||||
.dstAccessMask = flags,
|
||||
.oldLayout = source_layout,
|
||||
.newLayout = target_layout,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = image,
|
||||
.subresourceRange{
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1,
|
||||
},
|
||||
};
|
||||
cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE,
|
||||
0, barrier);
|
||||
}
|
||||
|
||||
void RecordShaderReadBarrier(Scheduler& scheduler, const ImageView& image_view) {
|
||||
const VkImage image = image_view.ImageHandle();
|
||||
const VkImageSubresourceRange subresource_range = SubresourceRangeFromView(image_view);
|
||||
scheduler.RequestOutsideRenderPassOperationContext();
|
||||
scheduler.Record([image, subresource_range](vk::CommandBuffer cmdbuf) {
|
||||
const VkImageMemoryBarrier barrier{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
const VkImageMemoryBarrier2 barrier{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
|
||||
.pNext = nullptr,
|
||||
.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
|
||||
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT |
|
||||
VK_ACCESS_SHADER_WRITE_BIT |
|
||||
VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
|
||||
.srcStageMask = vk::PIPELINE_STAGE_IMAGE_USERS,
|
||||
.srcAccessMask = vk::ACCESS_IMAGE_WRITES,
|
||||
.dstStageMask = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT |
|
||||
VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
|
||||
.dstAccessMask = VK_ACCESS_2_SHADER_READ_BIT,
|
||||
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
@@ -498,17 +472,7 @@ void RecordShaderReadBarrier(Scheduler& scheduler, const ImageView& image_view)
|
||||
.image = image,
|
||||
.subresourceRange = subresource_range,
|
||||
};
|
||||
cmdbuf.PipelineBarrier(
|
||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
|
||||
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT |
|
||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT |
|
||||
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
|
||||
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
|
||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
|
||||
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
|
||||
0,
|
||||
barrier);
|
||||
cmdbuf.PipelineBarrier(barrier);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -562,25 +526,6 @@ void RecordShaderReadBarrier(Scheduler& scheduler, const ImageView& image_view)
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
void BeginRenderPass(vk::CommandBuffer& cmdbuf, const Framebuffer* framebuffer) {
|
||||
const VkRenderPass render_pass = framebuffer->RenderPass();
|
||||
const VkFramebuffer framebuffer_handle = framebuffer->Handle();
|
||||
const VkExtent2D render_area = framebuffer->RenderArea();
|
||||
const VkRenderPassBeginInfo renderpass_bi{
|
||||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
|
||||
.pNext = nullptr,
|
||||
.renderPass = render_pass,
|
||||
.framebuffer = framebuffer_handle,
|
||||
.renderArea{
|
||||
.offset{},
|
||||
.extent = render_area,
|
||||
},
|
||||
.clearValueCount = 0,
|
||||
.pClearValues = nullptr,
|
||||
};
|
||||
cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_,
|
||||
@@ -686,11 +631,14 @@ void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, VkImageView
|
||||
};
|
||||
const VkPipelineLayout layout = *one_texture_pipeline_layout;
|
||||
const VkPipeline pipeline = FindOrEmplaceColorPipeline(key);
|
||||
scheduler.RequestOutsideRenderPassOperationContext();
|
||||
scheduler.Record([this, dst_framebuffer, src_image_view, src_image, src_sampler, dst_region,
|
||||
src_region, src_size, pipeline, layout](vk::CommandBuffer cmdbuf) {
|
||||
TransitionImageLayout(cmdbuf, src_image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
BeginRenderPass(cmdbuf, dst_framebuffer);
|
||||
const auto attachments =
|
||||
std::span(dst_framebuffer->Images()).first(dst_framebuffer->NumImages());
|
||||
if (std::ranges::find(attachments, src_image) != attachments.end()) {
|
||||
scheduler.RequestOutsideRenderPassOperationContext();
|
||||
}
|
||||
scheduler.RequestRenderpass(dst_framebuffer);
|
||||
scheduler.Record([this, src_image_view, src_sampler, dst_region, src_region, src_size,
|
||||
pipeline, layout](vk::CommandBuffer cmdbuf) {
|
||||
const VkDescriptorSet descriptor_set = one_texture_descriptor_allocator.Commit();
|
||||
UpdateOneTextureDescriptorSet(device, descriptor_set, src_sampler, src_image_view);
|
||||
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
||||
@@ -698,8 +646,8 @@ void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, VkImageView
|
||||
nullptr);
|
||||
BindBlitState(cmdbuf, layout, dst_region, src_region, src_size);
|
||||
cmdbuf.Draw(3, 1, 0, 0);
|
||||
cmdbuf.EndRenderPass();
|
||||
});
|
||||
scheduler.InvalidateState();
|
||||
}
|
||||
|
||||
void BlitImageHelper::BlitImpl(const Framebuffer* dst_framebuffer,
|
||||
@@ -991,10 +939,12 @@ void BlitImageHelper::CopyMSAAImpl(VkRenderPass renderpass, VkPipeline pipeline,
|
||||
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
||||
};
|
||||
const std::array pre_barriers{
|
||||
VkImageMemoryBarrier{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
VkImageMemoryBarrier2{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
|
||||
.pNext = nullptr,
|
||||
.srcAccessMask = aspect_info.pre_src_access,
|
||||
.srcStageMask = vk::PIPELINE_STAGE_IMAGE_USERS,
|
||||
.srcAccessMask = vk::ACCESS_IMAGE_WRITES,
|
||||
.dstStageMask = aspect_info.pre_dst_stages,
|
||||
.dstAccessMask = aspect_info.pre_src_dst_access,
|
||||
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
@@ -1003,10 +953,12 @@ void BlitImageHelper::CopyMSAAImpl(VkRenderPass renderpass, VkPipeline pipeline,
|
||||
.image = src,
|
||||
.subresourceRange = barrier_range,
|
||||
},
|
||||
VkImageMemoryBarrier{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
VkImageMemoryBarrier2{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
|
||||
.pNext = nullptr,
|
||||
.srcAccessMask = aspect_info.pre_src_access,
|
||||
.srcStageMask = vk::PIPELINE_STAGE_IMAGE_USERS,
|
||||
.srcAccessMask = vk::ACCESS_IMAGE_WRITES,
|
||||
.dstStageMask = aspect_info.pre_dst_stages,
|
||||
.dstAccessMask = aspect_info.pre_dst_dst_access,
|
||||
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
@@ -1016,8 +968,7 @@ void BlitImageHelper::CopyMSAAImpl(VkRenderPass renderpass, VkPipeline pipeline,
|
||||
.subresourceRange = barrier_range,
|
||||
},
|
||||
};
|
||||
cmdbuf.PipelineBarrier(aspect_info.pre_src_stages, aspect_info.pre_dst_stages, 0,
|
||||
nullptr, nullptr, pre_barriers);
|
||||
cmdbuf.PipelineBarrier(0, {}, {}, pre_barriers);
|
||||
const VkRenderPassBeginInfo renderpass_bi{
|
||||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
|
||||
.pNext = nullptr,
|
||||
@@ -1053,11 +1004,13 @@ void BlitImageHelper::CopyMSAAImpl(VkRenderPass renderpass, VkPipeline pipeline,
|
||||
cmdbuf.PushConstants(layout, VK_SHADER_STAGE_FRAGMENT_BIT, push_constants);
|
||||
cmdbuf.Draw(3, 1, 0, 0);
|
||||
cmdbuf.EndRenderPass();
|
||||
const VkImageMemoryBarrier post_barrier{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
const VkImageMemoryBarrier2 post_barrier{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
|
||||
.pNext = nullptr,
|
||||
.srcStageMask = aspect_info.post_src_stages,
|
||||
.srcAccessMask = aspect_info.post_src_access,
|
||||
.dstAccessMask = aspect_info.post_dst_access,
|
||||
.dstStageMask = vk::PIPELINE_STAGE_IMAGE_USERS,
|
||||
.dstAccessMask = vk::ACCESS_IMAGE_USERS,
|
||||
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
@@ -1065,8 +1018,7 @@ void BlitImageHelper::CopyMSAAImpl(VkRenderPass renderpass, VkPipeline pipeline,
|
||||
.image = dst,
|
||||
.subresourceRange = barrier_range,
|
||||
};
|
||||
cmdbuf.PipelineBarrier(aspect_info.post_src_stages, aspect_info.post_dst_stages, 0,
|
||||
post_barrier);
|
||||
cmdbuf.PipelineBarrier(post_barrier);
|
||||
});
|
||||
msaa_copy_resources.push_back(MSAACopyResources{
|
||||
.tick = scheduler.CurrentTick(),
|
||||
@@ -1115,21 +1067,13 @@ void BlitImageHelper::CopyMSAA(RenderPassCache& render_pass_cache, VkImage dst_i
|
||||
.src_view_aspect = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.attachment_aspect = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.barrier_aspect = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.pre_src_access = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_WRITE_BIT |
|
||||
VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
.pre_src_dst_access = VK_ACCESS_SHADER_READ_BIT,
|
||||
.pre_src_dst_access = VK_ACCESS_2_SHADER_READ_BIT,
|
||||
.pre_dst_dst_access =
|
||||
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||||
.pre_src_stages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
|
||||
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT |
|
||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VK_ACCESS_2_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT,
|
||||
.pre_dst_stages =
|
||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
.post_src_access = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||||
.post_dst_access = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT,
|
||||
.post_src_stages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
.post_dst_stages = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
|
||||
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
.post_src_access = VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT,
|
||||
.post_src_stages = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
};
|
||||
const VkFormat src_vk_format =
|
||||
MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, true, src_format).format;
|
||||
@@ -1281,7 +1225,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceColorPipeline(const BlitImagePipelineKe
|
||||
.subpass = 0,
|
||||
.basePipelineHandle = VK_NULL_HANDLE,
|
||||
.basePipelineIndex = 0,
|
||||
}, device.StaticPipelineCache()));
|
||||
}));
|
||||
return *blit_color_pipelines.back();
|
||||
}
|
||||
|
||||
@@ -1313,7 +1257,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceDepthStencilPipeline(const BlitImagePip
|
||||
.subpass = 0,
|
||||
.basePipelineHandle = VK_NULL_HANDLE,
|
||||
.basePipelineIndex = 0,
|
||||
}, device.StaticPipelineCache()));
|
||||
}));
|
||||
return *blit_depth_stencil_pipelines.back();
|
||||
}
|
||||
|
||||
@@ -1366,7 +1310,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearColorPipeline(const BlitImagePipel
|
||||
.subpass = 0,
|
||||
.basePipelineHandle = VK_NULL_HANDLE,
|
||||
.basePipelineIndex = 0,
|
||||
}, device.StaticPipelineCache()));
|
||||
}));
|
||||
return *clear_color_pipelines.back();
|
||||
}
|
||||
|
||||
@@ -1422,7 +1366,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearStencilPipeline(
|
||||
.subpass = 0,
|
||||
.basePipelineHandle = VK_NULL_HANDLE,
|
||||
.basePipelineIndex = 0,
|
||||
}, device.StaticPipelineCache()));
|
||||
}));
|
||||
return *clear_stencil_pipelines.back();
|
||||
}
|
||||
|
||||
@@ -1465,7 +1409,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceBlitColorMSAAPipeline(const BlitMSAAPip
|
||||
.subpass = 0,
|
||||
.basePipelineHandle = VK_NULL_HANDLE,
|
||||
.basePipelineIndex = 0,
|
||||
}, device.StaticPipelineCache()));
|
||||
}));
|
||||
return *blit_msaa_color_pipelines.back();
|
||||
}
|
||||
|
||||
@@ -1584,7 +1528,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceResolveDepthStencilPipeline(VkRenderPas
|
||||
.subpass = 0,
|
||||
.basePipelineHandle = VK_NULL_HANDLE,
|
||||
.basePipelineIndex = 0,
|
||||
}, device.StaticPipelineCache()));
|
||||
}));
|
||||
return *pipelines.back();
|
||||
}
|
||||
|
||||
@@ -1624,21 +1568,13 @@ void BlitImageHelper::CopyMSAADepth(RenderPassCache& render_pass_cache, VkImage
|
||||
.src_view_aspect = VK_IMAGE_ASPECT_DEPTH_BIT,
|
||||
.attachment_aspect = attachment_aspect,
|
||||
.barrier_aspect = attachment_aspect,
|
||||
.pre_src_access =
|
||||
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
.pre_src_dst_access = VK_ACCESS_SHADER_READ_BIT,
|
||||
.pre_dst_dst_access = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
|
||||
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
|
||||
.pre_src_stages = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
|
||||
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
.pre_src_dst_access = VK_ACCESS_2_SHADER_READ_BIT,
|
||||
.pre_dst_dst_access = VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
|
||||
VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
|
||||
.pre_dst_stages =
|
||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT,
|
||||
.post_src_access = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
|
||||
.post_dst_access = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT |
|
||||
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT,
|
||||
.post_src_stages = VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
|
||||
.post_dst_stages = vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER,
|
||||
VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT,
|
||||
.post_src_access = VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
|
||||
.post_src_stages = VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT,
|
||||
};
|
||||
const VkFormat src_vk_format =
|
||||
MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, true, src_format).format;
|
||||
@@ -1697,7 +1633,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceMSAACopyPipeline(const MSAACopyPipeline
|
||||
.subpass = 0,
|
||||
.basePipelineHandle = VK_NULL_HANDLE,
|
||||
.basePipelineIndex = 0,
|
||||
}, device.StaticPipelineCache()));
|
||||
}));
|
||||
return *msaa_copy_pipelines.back();
|
||||
}
|
||||
|
||||
@@ -1817,7 +1753,7 @@ void BlitImageHelper::ConvertPipelineEx(vk::Pipeline& pipeline, VkRenderPass ren
|
||||
.subpass = 0,
|
||||
.basePipelineHandle = VK_NULL_HANDLE,
|
||||
.basePipelineIndex = 0,
|
||||
}, device.StaticPipelineCache());
|
||||
});
|
||||
}
|
||||
|
||||
void BlitImageHelper::ConvertPipelineColorTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
|
||||
@@ -1860,7 +1796,7 @@ void BlitImageHelper::ConvertPipeline(vk::Pipeline& pipeline, VkRenderPass rende
|
||||
.subpass = 0,
|
||||
.basePipelineHandle = VK_NULL_HANDLE,
|
||||
.basePipelineIndex = 0,
|
||||
}, device.StaticPipelineCache());
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace Vulkan
|
||||
|
||||
@@ -140,15 +140,11 @@ private:
|
||||
VkImageAspectFlags src_view_aspect;
|
||||
VkImageAspectFlags attachment_aspect;
|
||||
VkImageAspectFlags barrier_aspect;
|
||||
VkAccessFlags pre_src_access;
|
||||
VkAccessFlags pre_src_dst_access;
|
||||
VkAccessFlags pre_dst_dst_access;
|
||||
VkPipelineStageFlags pre_src_stages;
|
||||
VkPipelineStageFlags pre_dst_stages;
|
||||
VkAccessFlags post_src_access;
|
||||
VkAccessFlags post_dst_access;
|
||||
VkPipelineStageFlags post_src_stages;
|
||||
VkPipelineStageFlags post_dst_stages;
|
||||
VkAccessFlags2 pre_src_dst_access;
|
||||
VkAccessFlags2 pre_dst_dst_access;
|
||||
VkPipelineStageFlags2 pre_dst_stages;
|
||||
VkAccessFlags2 post_src_access;
|
||||
VkPipelineStageFlags2 post_src_stages;
|
||||
};
|
||||
|
||||
void BlitImpl(const Framebuffer* dst_framebuffer, const ImageView& src_image_view,
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging.h"
|
||||
#include "common/settings.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/renderer_vulkan/maxwell_to_vk.h"
|
||||
#include "video_core/surface.h"
|
||||
@@ -71,12 +70,14 @@ VkSamplerAddressMode WrapMode(const Device& device,
|
||||
}
|
||||
ASSERT(false);
|
||||
return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
||||
case Tegra::Texture::WrapMode::MirrorOnceClampToEdge:
|
||||
return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE;
|
||||
case Tegra::Texture::WrapMode::MirrorOnceBorder:
|
||||
UNIMPLEMENTED();
|
||||
return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE;
|
||||
[[fallthrough]];
|
||||
case Tegra::Texture::WrapMode::MirrorOnceClampToEdge:
|
||||
case Tegra::Texture::WrapMode::MirrorOnceClampOGL:
|
||||
if (!device.IsKhrSamplerMirrorClampToEdgeSupported()) {
|
||||
return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
||||
}
|
||||
return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented wrap mode={}", wrap_mode);
|
||||
@@ -223,10 +224,6 @@ FormatInfo SurfaceFormat(const Device& device, FormatType format_type, bool with
|
||||
SURFACE_FORMAT_ELEM(VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK, 0, ETC2_RGB_SRGB) \
|
||||
SURFACE_FORMAT_ELEM(VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK, 0, ETC2_RGBA_SRGB) \
|
||||
SURFACE_FORMAT_ELEM(VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK, 0, ETC2_RGB_PTA_SRGB) \
|
||||
SURFACE_FORMAT_ELEM(VK_FORMAT_EAC_R11_UNORM_BLOCK, 0, EAC_R11_UNORM) \
|
||||
SURFACE_FORMAT_ELEM(VK_FORMAT_EAC_R11_SNORM_BLOCK, 0, EAC_R11_SNORM) \
|
||||
SURFACE_FORMAT_ELEM(VK_FORMAT_EAC_R11G11_UNORM_BLOCK, 0, EAC_R11G11_UNORM) \
|
||||
SURFACE_FORMAT_ELEM(VK_FORMAT_EAC_R11G11_SNORM_BLOCK, 0, EAC_R11G11_SNORM) \
|
||||
/* Depth formats */ \
|
||||
SURFACE_FORMAT_ELEM(VK_FORMAT_D32_SFLOAT, usage_attachable, D32_FLOAT) \
|
||||
SURFACE_FORMAT_ELEM(VK_FORMAT_D16_UNORM, usage_attachable, D16_UNORM) \
|
||||
@@ -246,21 +243,11 @@ FormatInfo SurfaceFormat(const Device& device, FormatType format_type, bool with
|
||||
bool const is_srgb = with_srgb && VideoCore::Surface::IsPixelFormatSRGB(pixel_format);
|
||||
// Transcode on hardware that doesn't support ASTC natively
|
||||
if (!device.IsOptimalAstcSupported() && VideoCore::Surface::IsPixelFormatASTC(pixel_format)) {
|
||||
switch (Settings::values.astc_recompression.GetValue()) {
|
||||
case Settings::AstcRecompression::Uncompressed:
|
||||
if (is_srgb) {
|
||||
tuple.format = VK_FORMAT_A8B8G8R8_SRGB_PACK32;
|
||||
} else {
|
||||
tuple.format = VK_FORMAT_A8B8G8R8_UNORM_PACK32;
|
||||
tuple.usage |= usage_storage;
|
||||
}
|
||||
break;
|
||||
case Settings::AstcRecompression::Bc1:
|
||||
tuple.format = is_srgb ? VK_FORMAT_BC1_RGBA_SRGB_BLOCK : VK_FORMAT_BC1_RGBA_UNORM_BLOCK;
|
||||
break;
|
||||
case Settings::AstcRecompression::Bc3:
|
||||
tuple.format = is_srgb ? VK_FORMAT_BC3_SRGB_BLOCK : VK_FORMAT_BC3_UNORM_BLOCK;
|
||||
break;
|
||||
if (is_srgb) {
|
||||
tuple.format = VK_FORMAT_A8B8G8R8_SRGB_PACK32;
|
||||
} else {
|
||||
tuple.format = VK_FORMAT_A8B8G8R8_UNORM_PACK32;
|
||||
tuple.usage |= usage_storage;
|
||||
}
|
||||
}
|
||||
if (!device.IsOptimalBcnSupported() && VideoCore::Surface::IsPixelFormatBCn(pixel_format)) {
|
||||
@@ -282,17 +269,7 @@ FormatInfo SurfaceFormat(const Device& device, FormatType format_type, bool with
|
||||
}
|
||||
} else if (!device.IsOptimalEtc2Supported() && VideoCore::Surface::IsPixelFormatETC2(pixel_format)) {
|
||||
// Transcode on hardware that doesn't support ETC2 natively
|
||||
if (pixel_format == PixelFormat::EAC_R11_SNORM) {
|
||||
tuple.format = VK_FORMAT_R8_SNORM;
|
||||
} else if (pixel_format == PixelFormat::EAC_R11_UNORM) {
|
||||
tuple.format = VK_FORMAT_R8_UNORM;
|
||||
} else if (pixel_format == PixelFormat::EAC_R11G11_SNORM) {
|
||||
tuple.format = VK_FORMAT_R8G8_SNORM;
|
||||
} else if (pixel_format == PixelFormat::EAC_R11G11_UNORM) {
|
||||
tuple.format = VK_FORMAT_R8G8_UNORM;
|
||||
} else {
|
||||
tuple.format = is_srgb ? VK_FORMAT_A8B8G8R8_SRGB_PACK32 : VK_FORMAT_A8B8G8R8_UNORM_PACK32;
|
||||
}
|
||||
tuple.format = is_srgb ? VK_FORMAT_A8B8G8R8_SRGB_PACK32 : VK_FORMAT_A8B8G8R8_UNORM_PACK32;
|
||||
}
|
||||
bool const attachable = (tuple.usage & usage_attachable) != 0;
|
||||
bool const storage = (tuple.usage & usage_storage) != 0;
|
||||
|
||||
@@ -113,13 +113,17 @@ void WriteColorPpm(const std::filesystem::path& path, VkExtent2D extent,
|
||||
WritePortablePixmap(path, "P6", extent, rgb);
|
||||
}
|
||||
|
||||
VkImageMemoryBarrier MakeTransitionBarrier(VkImage image, VkAccessFlags src_access,
|
||||
VkAccessFlags dst_access, VkImageLayout old_layout,
|
||||
VkImageLayout new_layout) {
|
||||
return VkImageMemoryBarrier{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
VkImageMemoryBarrier2 MakeTransitionBarrier(VkImage image, VkPipelineStageFlags2 src_stage,
|
||||
VkAccessFlags2 src_access,
|
||||
VkPipelineStageFlags2 dst_stage,
|
||||
VkAccessFlags2 dst_access, VkImageLayout old_layout,
|
||||
VkImageLayout new_layout) {
|
||||
return VkImageMemoryBarrier2{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
|
||||
.pNext = nullptr,
|
||||
.srcStageMask = src_stage,
|
||||
.srcAccessMask = src_access,
|
||||
.dstStageMask = dst_stage,
|
||||
.dstAccessMask = dst_access,
|
||||
.oldLayout = old_layout,
|
||||
.newLayout = new_layout,
|
||||
@@ -160,29 +164,31 @@ void CopyPresentedFrame(vk::CommandBuffer cmdbuf, VkImage source, LsfgImage& des
|
||||
VkExtent2D extent) {
|
||||
const auto make_barrier = MakeTransitionBarrier;
|
||||
|
||||
static constexpr VkPipelineStageFlags2 present_stage =
|
||||
VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT;
|
||||
|
||||
const std::array before{
|
||||
make_barrier(source, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
|
||||
make_barrier(source, present_stage, VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT,
|
||||
VK_PIPELINE_STAGE_2_TRANSFER_BIT, VK_ACCESS_2_TRANSFER_READ_BIT,
|
||||
VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL),
|
||||
make_barrier(destination.Handle(), VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
make_barrier(destination.Handle(), present_stage, VK_ACCESS_2_SHADER_READ_BIT,
|
||||
VK_PIPELINE_STAGE_2_TRANSFER_BIT, VK_ACCESS_2_TRANSFER_WRITE_BIT,
|
||||
destination.Layout(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL),
|
||||
};
|
||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
|
||||
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, {}, {}, before);
|
||||
cmdbuf.PipelineBarrier(0, {}, {}, before);
|
||||
|
||||
cmdbuf.CopyImage(source, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, destination.Handle(),
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, MakeCopyRegion(extent));
|
||||
|
||||
const std::array after{
|
||||
make_barrier(source, VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||||
make_barrier(source, VK_PIPELINE_STAGE_2_TRANSFER_BIT, VK_ACCESS_2_TRANSFER_READ_BIT,
|
||||
present_stage, VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL),
|
||||
make_barrier(destination.Handle(), VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT,
|
||||
make_barrier(destination.Handle(), VK_PIPELINE_STAGE_2_TRANSFER_BIT,
|
||||
VK_ACCESS_2_TRANSFER_WRITE_BIT, present_stage, VK_ACCESS_2_SHADER_READ_BIT,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL),
|
||||
};
|
||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
|
||||
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
|
||||
0, {}, {}, after);
|
||||
cmdbuf.PipelineBarrier(0, {}, {}, after);
|
||||
|
||||
destination.SetLayout(VK_IMAGE_LAYOUT_GENERAL);
|
||||
}
|
||||
|
||||
@@ -364,11 +364,13 @@ void Layer::UpdateRawImage(const Tegra::FramebufferConfig& framebuffer, size_t i
|
||||
};
|
||||
scheduler.Record([this, copy, index = image_index](vk::CommandBuffer cmdbuf) {
|
||||
const VkImage image = *raw_images[index];
|
||||
const VkImageMemoryBarrier base_barrier{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
const VkImageMemoryBarrier2 base_barrier{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
|
||||
.pNext = nullptr,
|
||||
.srcAccessMask = 0,
|
||||
.dstAccessMask = 0,
|
||||
.srcStageMask = VK_PIPELINE_STAGE_2_NONE,
|
||||
.srcAccessMask = VK_ACCESS_2_NONE,
|
||||
.dstStageMask = VK_PIPELINE_STAGE_2_NONE,
|
||||
.dstAccessMask = VK_ACCESS_2_NONE,
|
||||
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
@@ -382,23 +384,24 @@ void Layer::UpdateRawImage(const Tegra::FramebufferConfig& framebuffer, size_t i
|
||||
.layerCount = 1,
|
||||
},
|
||||
};
|
||||
VkImageMemoryBarrier read_barrier = base_barrier;
|
||||
read_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
VkImageMemoryBarrier2 read_barrier = base_barrier;
|
||||
read_barrier.srcStageMask = VK_PIPELINE_STAGE_2_HOST_BIT;
|
||||
read_barrier.dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT;
|
||||
read_barrier.dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT;
|
||||
read_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
read_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||
|
||||
VkImageMemoryBarrier write_barrier = base_barrier;
|
||||
write_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
write_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
||||
VkImageMemoryBarrier2 write_barrier = base_barrier;
|
||||
write_barrier.srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT;
|
||||
write_barrier.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT;
|
||||
write_barrier.dstStageMask = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT |
|
||||
VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT;
|
||||
write_barrier.dstAccessMask = VK_ACCESS_2_SHADER_READ_BIT;
|
||||
write_barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||
|
||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
|
||||
read_barrier);
|
||||
cmdbuf.PipelineBarrier(read_barrier);
|
||||
cmdbuf.CopyBufferToImage(*buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copy);
|
||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
|
||||
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
|
||||
0, write_barrier);
|
||||
cmdbuf.PipelineBarrier(write_barrier);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -67,12 +67,14 @@ vk::Buffer CreateUniformBuffer(MemoryAllocator& memory_allocator, VkDeviceSize s
|
||||
return memory_allocator.CreateBuffer(buffer_ci, MemoryUsage::Upload);
|
||||
}
|
||||
|
||||
VkImageMemoryBarrier MakeBarrier(const LsfgImage& image, VkAccessFlags src_access,
|
||||
VkAccessFlags dst_access) {
|
||||
return VkImageMemoryBarrier{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
VkImageMemoryBarrier2 MakeBarrier(const LsfgImage& image, VkAccessFlags2 src_access,
|
||||
VkAccessFlags2 dst_access) {
|
||||
return VkImageMemoryBarrier2{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
|
||||
.pNext = nullptr,
|
||||
.srcStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
|
||||
.srcAccessMask = src_access,
|
||||
.dstStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
|
||||
.dstAccessMask = dst_access,
|
||||
.oldLayout = image.Layout(),
|
||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
@@ -98,19 +100,19 @@ LsfgImage::LsfgImage(const Device& device, MemoryAllocator& memory_allocator, Vk
|
||||
view = CreateWrappedImageView(device, image, format);
|
||||
}
|
||||
|
||||
LsfgBarriers& LsfgBarriers::Push(LsfgImage& image, VkAccessFlags src_access,
|
||||
VkAccessFlags dst_access) {
|
||||
LsfgBarriers& LsfgBarriers::Push(LsfgImage& image, VkAccessFlags2 src_access,
|
||||
VkAccessFlags2 dst_access) {
|
||||
barriers.push_back(MakeBarrier(image, src_access, dst_access));
|
||||
image.SetLayout(VK_IMAGE_LAYOUT_GENERAL);
|
||||
return *this;
|
||||
}
|
||||
|
||||
LsfgBarriers& LsfgBarriers::WriteToRead(LsfgImage& image) {
|
||||
return Push(image, VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT);
|
||||
return Push(image, VK_ACCESS_2_SHADER_WRITE_BIT, VK_ACCESS_2_SHADER_READ_BIT);
|
||||
}
|
||||
|
||||
LsfgBarriers& LsfgBarriers::ReadToWrite(LsfgImage& image) {
|
||||
return Push(image, VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_SHADER_WRITE_BIT);
|
||||
return Push(image, VK_ACCESS_2_SHADER_READ_BIT, VK_ACCESS_2_SHADER_WRITE_BIT);
|
||||
}
|
||||
|
||||
LsfgBarriers& LsfgBarriers::WriteToRead(LsfgImage* image) {
|
||||
@@ -122,11 +124,13 @@ LsfgBarriers& LsfgBarriers::ReadToWrite(LsfgImage* image) {
|
||||
}
|
||||
|
||||
LsfgBarriers& LsfgBarriers::DiscardToWrite(VkImage image) {
|
||||
barriers.push_back(VkImageMemoryBarrier{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
barriers.push_back(VkImageMemoryBarrier2{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
|
||||
.pNext = nullptr,
|
||||
.srcAccessMask = 0,
|
||||
.dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
|
||||
.srcStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
|
||||
.srcAccessMask = VK_ACCESS_2_NONE,
|
||||
.dstStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
|
||||
.dstAccessMask = VK_ACCESS_2_SHADER_WRITE_BIT,
|
||||
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
@@ -199,8 +203,7 @@ void LsfgBarriers::Build() {
|
||||
if (barriers.empty()) {
|
||||
return;
|
||||
}
|
||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
|
||||
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, {}, {}, barriers);
|
||||
cmdbuf.PipelineBarrier(0, {}, {}, barriers);
|
||||
barriers.clear();
|
||||
}
|
||||
|
||||
@@ -283,7 +286,6 @@ LsfgPass::LsfgPass(const Device& device, const LsfgShaders& shaders, u32 shader_
|
||||
for (const auto& [count, type] : bindings) {
|
||||
types.insert(types.end(), count, type);
|
||||
}
|
||||
descriptor_count = static_cast<u32>(types.size());
|
||||
|
||||
descriptor_set_layout = CreateWrappedDescriptorSetLayout(
|
||||
device, std::span<const VkDescriptorType>{types}, VK_SHADER_STAGE_COMPUTE_BIT);
|
||||
|
||||
@@ -147,10 +147,10 @@ public:
|
||||
void Build();
|
||||
|
||||
private:
|
||||
LsfgBarriers& Push(LsfgImage& image, VkAccessFlags src_access, VkAccessFlags dst_access);
|
||||
LsfgBarriers& Push(LsfgImage& image, VkAccessFlags2 src_access, VkAccessFlags2 dst_access);
|
||||
|
||||
vk::CommandBuffer cmdbuf;
|
||||
std::vector<VkImageMemoryBarrier> barriers;
|
||||
std::vector<VkImageMemoryBarrier2> barriers;
|
||||
};
|
||||
|
||||
class LsfgDescriptorWriter {
|
||||
@@ -204,10 +204,6 @@ public:
|
||||
return *descriptor_set_layout;
|
||||
}
|
||||
|
||||
[[nodiscard]] u32 DescriptorCount() const {
|
||||
return descriptor_count;
|
||||
}
|
||||
|
||||
void Bind(vk::CommandBuffer cmdbuf, VkDescriptorSet set) const;
|
||||
void BindPipeline(vk::CommandBuffer cmdbuf) const;
|
||||
void BindSet(vk::CommandBuffer cmdbuf, VkDescriptorSet set) const;
|
||||
@@ -216,7 +212,6 @@ private:
|
||||
vk::DescriptorSetLayout descriptor_set_layout;
|
||||
vk::PipelineLayout pipeline_layout;
|
||||
vk::Pipeline pipeline;
|
||||
u32 descriptor_count{};
|
||||
};
|
||||
|
||||
[[nodiscard]] vk::DescriptorPool CreateLsfgDescriptorPool(const Device& device, u32 max_sets);
|
||||
|
||||
@@ -22,12 +22,15 @@ constexpr u32 DISPATCH_TILE_SHIFT = 4;
|
||||
return (size + (1u << DISPATCH_TILE_SHIFT) - 1) >> DISPATCH_TILE_SHIFT;
|
||||
}
|
||||
|
||||
VkImageMemoryBarrier MakeTargetBarrier(VkImage image, VkAccessFlags src_access,
|
||||
VkAccessFlags dst_access, VkImageLayout old_layout) {
|
||||
return VkImageMemoryBarrier{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
VkImageMemoryBarrier2 MakeTargetBarrier(VkImage image, VkPipelineStageFlags2 src_stage,
|
||||
VkAccessFlags2 src_access, VkPipelineStageFlags2 dst_stage,
|
||||
VkAccessFlags2 dst_access, VkImageLayout old_layout) {
|
||||
return VkImageMemoryBarrier2{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
|
||||
.pNext = nullptr,
|
||||
.srcStageMask = src_stage,
|
||||
.srcAccessMask = src_access,
|
||||
.dstStageMask = dst_stage,
|
||||
.dstAccessMask = dst_access,
|
||||
.oldLayout = old_layout,
|
||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
@@ -118,13 +121,11 @@ void LsfgGenerate::Dispatch(vk::CommandBuffer cmdbuf, u64 frame_count, size_t sl
|
||||
cmdbuf.Dispatch(GroupCount(extent.width), GroupCount(extent.height), 1);
|
||||
|
||||
const std::array after{MakeTargetBarrier(
|
||||
image, VK_ACCESS_SHADER_WRITE_BIT,
|
||||
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_READ_BIT,
|
||||
image, VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, VK_ACCESS_2_SHADER_WRITE_BIT,
|
||||
VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_2_TRANSFER_BIT,
|
||||
VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_2_TRANSFER_READ_BIT,
|
||||
VK_IMAGE_LAYOUT_GENERAL)};
|
||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
|
||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
0, {}, {}, after);
|
||||
cmdbuf.PipelineBarrier(0, {}, {}, after);
|
||||
}
|
||||
|
||||
} // namespace Vulkan
|
||||
|
||||
@@ -134,7 +134,9 @@ vk::RenderPass CreateFxRenderPass(const Device& device, VkFormat format, bool cl
|
||||
initial_layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
}
|
||||
|
||||
const VkAttachmentDescription attachment{
|
||||
const VkAttachmentDescription2 attachment{
|
||||
.sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.format = format,
|
||||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||||
@@ -146,14 +148,20 @@ vk::RenderPass CreateFxRenderPass(const Device& device, VkFormat format, bool cl
|
||||
.finalLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
};
|
||||
|
||||
const VkAttachmentReference reference{
|
||||
static constexpr VkAttachmentReference2 reference{
|
||||
.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2,
|
||||
.pNext = nullptr,
|
||||
.attachment = 0,
|
||||
.layout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.aspectMask = 0,
|
||||
};
|
||||
|
||||
const VkSubpassDescription subpass{
|
||||
const VkSubpassDescription2 subpass{
|
||||
.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
.viewMask = 0,
|
||||
.inputAttachmentCount = 0,
|
||||
.pInputAttachments = nullptr,
|
||||
.colorAttachmentCount = 1,
|
||||
@@ -164,8 +172,8 @@ vk::RenderPass CreateFxRenderPass(const Device& device, VkFormat format, bool cl
|
||||
.pPreserveAttachments = nullptr,
|
||||
};
|
||||
|
||||
return device.GetLogical().CreateRenderPass(VkRenderPassCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
|
||||
return device.GetLogical().CreateRenderPass2(VkRenderPassCreateInfo2{
|
||||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.attachmentCount = 1,
|
||||
@@ -174,6 +182,8 @@ vk::RenderPass CreateFxRenderPass(const Device& device, VkFormat format, bool cl
|
||||
.pSubpasses = &subpass,
|
||||
.dependencyCount = 0,
|
||||
.pDependencies = nullptr,
|
||||
.correlatedViewMaskCount = 0,
|
||||
.pCorrelatedViewMasks = nullptr,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -601,8 +611,6 @@ bool PostProcessChain::BuildEffects(const Device& device, MemoryAllocator& alloc
|
||||
*vertex_shader->second, *fragment_shader->second, pass);
|
||||
|
||||
if (out.writes_backbuffer) {
|
||||
out.backbuffer_slot = static_cast<u32>(effect.backbuffer_pass_count % 2);
|
||||
++effect.backbuffer_pass_count;
|
||||
for (u32 image = 0; image < m_image_count; ++image) {
|
||||
for (size_t slot = 0; slot < 2; ++slot) {
|
||||
out.framebuffers.push_back(CreateWrappedFramebuffer(
|
||||
|
||||
@@ -89,7 +89,6 @@ private:
|
||||
u32 num_vertices{3};
|
||||
bool clear{};
|
||||
bool writes_backbuffer{};
|
||||
u32 backbuffer_slot{};
|
||||
};
|
||||
|
||||
struct Effect {
|
||||
@@ -105,8 +104,6 @@ private:
|
||||
vk::DescriptorSetLayout uniform_layout{};
|
||||
vk::DescriptorPool descriptor_pool{};
|
||||
vk::DescriptorSets uniform_sets{};
|
||||
size_t backbuffer_pass_count{};
|
||||
u32 backbuffer_slots{1};
|
||||
};
|
||||
|
||||
struct FrameImages {
|
||||
|
||||
@@ -49,13 +49,13 @@ vk::Image CreateWrappedImage(MemoryAllocator& allocator, VkExtent2D dimensions,
|
||||
|
||||
void TransitionImageLayout(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayout target_layout,
|
||||
VkImageLayout source_layout) {
|
||||
constexpr VkFlags flags{VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
|
||||
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT};
|
||||
const VkImageMemoryBarrier barrier{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
const VkImageMemoryBarrier2 barrier{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
|
||||
.pNext = nullptr,
|
||||
.srcAccessMask = flags,
|
||||
.dstAccessMask = flags,
|
||||
.srcStageMask = vk::PIPELINE_STAGE_IMAGE_USERS,
|
||||
.srcAccessMask = vk::ACCESS_IMAGE_WRITES,
|
||||
.dstStageMask = vk::PIPELINE_STAGE_IMAGE_USERS,
|
||||
.dstAccessMask = vk::ACCESS_IMAGE_USERS,
|
||||
.oldLayout = source_layout,
|
||||
.newLayout = target_layout,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
@@ -69,8 +69,7 @@ void TransitionImageLayout(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayo
|
||||
.layerCount = 1,
|
||||
},
|
||||
};
|
||||
cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE,
|
||||
0, barrier);
|
||||
cmdbuf.PipelineBarrier(barrier);
|
||||
}
|
||||
|
||||
void UploadImage(const Device& device, MemoryAllocator& allocator, Scheduler& scheduler,
|
||||
@@ -116,11 +115,13 @@ void UploadImage(const Device& device, MemoryAllocator& allocator, Scheduler& sc
|
||||
|
||||
void DownloadColorImage(vk::CommandBuffer& cmdbuf, VkImage image, VkBuffer buffer,
|
||||
VkExtent3D extent) {
|
||||
const VkImageMemoryBarrier read_barrier{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
const VkImageMemoryBarrier2 read_barrier{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
|
||||
.pNext = nullptr,
|
||||
.srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT,
|
||||
.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
|
||||
.srcStageMask = vk::PIPELINE_STAGE_IMAGE_USERS,
|
||||
.srcAccessMask = vk::ACCESS_IMAGE_WRITES,
|
||||
.dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT,
|
||||
.dstAccessMask = VK_ACCESS_2_TRANSFER_READ_BIT,
|
||||
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
@@ -134,11 +135,13 @@ void DownloadColorImage(vk::CommandBuffer& cmdbuf, VkImage image, VkBuffer buffe
|
||||
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
||||
},
|
||||
};
|
||||
const VkImageMemoryBarrier image_write_barrier{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
const VkImageMemoryBarrier2 image_write_barrier{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
|
||||
.pNext = nullptr,
|
||||
.srcAccessMask = 0,
|
||||
.dstAccessMask = VK_ACCESS_MEMORY_WRITE_BIT,
|
||||
.srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT,
|
||||
.srcAccessMask = VK_ACCESS_2_NONE,
|
||||
.dstStageMask = vk::PIPELINE_STAGE_IMAGE_USERS,
|
||||
.dstAccessMask = vk::ACCESS_IMAGE_USERS,
|
||||
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
@@ -152,11 +155,13 @@ void DownloadColorImage(vk::CommandBuffer& cmdbuf, VkImage image, VkBuffer buffe
|
||||
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
||||
},
|
||||
};
|
||||
static constexpr VkMemoryBarrier memory_write_barrier{
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
|
||||
static constexpr VkMemoryBarrier2 memory_write_barrier{
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2,
|
||||
.pNext = nullptr,
|
||||
.srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT,
|
||||
.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT,
|
||||
.srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT,
|
||||
.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT,
|
||||
.dstStageMask = vk::PIPELINE_STAGE_HOST,
|
||||
.dstAccessMask = VK_ACCESS_2_HOST_READ_BIT,
|
||||
};
|
||||
const VkBufferImageCopy copy{
|
||||
.bufferOffset = 0,
|
||||
@@ -171,11 +176,9 @@ void DownloadColorImage(vk::CommandBuffer& cmdbuf, VkImage image, VkBuffer buffe
|
||||
.imageOffset{.x = 0, .y = 0, .z = 0},
|
||||
.imageExtent{extent},
|
||||
};
|
||||
cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
|
||||
read_barrier);
|
||||
cmdbuf.PipelineBarrier(read_barrier);
|
||||
cmdbuf.CopyImageToBuffer(image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, buffer, copy);
|
||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, 0,
|
||||
memory_write_barrier, nullptr, image_write_barrier);
|
||||
cmdbuf.PipelineBarrier(0, memory_write_barrier, {}, image_write_barrier);
|
||||
}
|
||||
|
||||
vk::ImageView CreateWrappedImageView(const Device& device, vk::Image& image, VkFormat format) {
|
||||
@@ -197,7 +200,9 @@ vk::ImageView CreateWrappedImageView(const Device& device, vk::Image& image, VkF
|
||||
|
||||
vk::RenderPass CreateWrappedRenderPass(const Device& device, VkFormat format,
|
||||
VkImageLayout initial_layout) {
|
||||
const VkAttachmentDescription attachment{
|
||||
const VkAttachmentDescription2 attachment{
|
||||
.sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2,
|
||||
.pNext = nullptr,
|
||||
.flags = VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT,
|
||||
.format = format,
|
||||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||||
@@ -210,14 +215,20 @@ vk::RenderPass CreateWrappedRenderPass(const Device& device, VkFormat format,
|
||||
.finalLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
};
|
||||
|
||||
constexpr VkAttachmentReference color_attachment_ref{
|
||||
static constexpr VkAttachmentReference2 color_attachment_ref{
|
||||
.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2,
|
||||
.pNext = nullptr,
|
||||
.attachment = 0,
|
||||
.layout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.aspectMask = 0,
|
||||
};
|
||||
|
||||
const VkSubpassDescription subpass_description{
|
||||
const VkSubpassDescription2 subpass_description{
|
||||
.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
.viewMask = 0,
|
||||
.inputAttachmentCount = 0,
|
||||
.pInputAttachments = nullptr,
|
||||
.colorAttachmentCount = 1,
|
||||
@@ -228,18 +239,31 @@ vk::RenderPass CreateWrappedRenderPass(const Device& device, VkFormat format,
|
||||
.pPreserveAttachments = nullptr,
|
||||
};
|
||||
|
||||
constexpr VkSubpassDependency dependency{
|
||||
.srcSubpass = VK_SUBPASS_EXTERNAL,
|
||||
.dstSubpass = 0,
|
||||
.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
static constexpr VkMemoryBarrier2 dependency_barrier{
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2,
|
||||
.pNext = nullptr,
|
||||
.srcStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
.srcAccessMask = 0,
|
||||
.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||||
.dependencyFlags = 0,
|
||||
.dstStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
.dstAccessMask =
|
||||
VK_ACCESS_2_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT,
|
||||
};
|
||||
|
||||
return device.GetLogical().CreateRenderPass(VkRenderPassCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
|
||||
static constexpr VkSubpassDependency2 dependency{
|
||||
.sType = VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2,
|
||||
.pNext = &dependency_barrier,
|
||||
.srcSubpass = VK_SUBPASS_EXTERNAL,
|
||||
.dstSubpass = 0,
|
||||
.srcStageMask = 0,
|
||||
.dstStageMask = 0,
|
||||
.srcAccessMask = 0,
|
||||
.dstAccessMask = 0,
|
||||
.dependencyFlags = 0,
|
||||
.viewOffset = 0,
|
||||
};
|
||||
|
||||
return device.GetLogical().CreateRenderPass2(VkRenderPassCreateInfo2{
|
||||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.attachmentCount = 1,
|
||||
@@ -248,6 +272,8 @@ vk::RenderPass CreateWrappedRenderPass(const Device& device, VkFormat format,
|
||||
.pSubpasses = &subpass_description,
|
||||
.dependencyCount = 1,
|
||||
.pDependencies = &dependency,
|
||||
.correlatedViewMaskCount = 0,
|
||||
.pCorrelatedViewMasks = nullptr,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -521,7 +547,7 @@ static vk::Pipeline CreateWrappedPipelineImpl(
|
||||
.subpass = 0,
|
||||
.basePipelineHandle = 0,
|
||||
.basePipelineIndex = 0,
|
||||
}, device.StaticPipelineCache());
|
||||
});
|
||||
}
|
||||
|
||||
vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass,
|
||||
|
||||
@@ -217,13 +217,15 @@ void RendererVulkan::Composite(std::span<const Tegra::FramebufferConfig> framebu
|
||||
|
||||
scheduler.RequestOutsideRenderPassOperationContext();
|
||||
blit_swapchain.DrawToFrame(device, rasterizer, frame, framebuffers,
|
||||
render_window.GetFramebufferLayout(), swapchain.GetImageCount(),
|
||||
render_window.GetFramebufferLayout(),
|
||||
present_manager.SwapchainImageCount(),
|
||||
swapchain.GetImageViewFormat());
|
||||
|
||||
#ifdef HAS_LSFG
|
||||
void(frame_gen.WantedGenerations(present_manager.MaxExtraFrames()));
|
||||
|
||||
frame_gen.Process(device, frame, swapchain.GetImageFormat(), GuestExtent(framebuffers));
|
||||
frame_gen.Process(device, frame, present_manager.SwapchainImageFormat(),
|
||||
GuestExtent(framebuffers));
|
||||
|
||||
const size_t generated_frames = frame_gen.GeneratedFrameCount();
|
||||
for (size_t generation = 0; generation < generated_frames; ++generation) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user