diff options
62 files changed, 776 insertions, 397 deletions
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt index ae665ed2e..7461fb093 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt | |||
| @@ -34,11 +34,14 @@ import androidx.core.view.WindowCompat | |||
| 34 | import androidx.core.view.WindowInsetsCompat | 34 | import androidx.core.view.WindowInsetsCompat |
| 35 | import androidx.core.view.WindowInsetsControllerCompat | 35 | import androidx.core.view.WindowInsetsControllerCompat |
| 36 | import androidx.navigation.fragment.NavHostFragment | 36 | import androidx.navigation.fragment.NavHostFragment |
| 37 | import androidx.preference.PreferenceManager | ||
| 37 | import org.yuzu.yuzu_emu.NativeLibrary | 38 | import org.yuzu.yuzu_emu.NativeLibrary |
| 38 | import org.yuzu.yuzu_emu.R | 39 | import org.yuzu.yuzu_emu.R |
| 40 | import org.yuzu.yuzu_emu.YuzuApplication | ||
| 39 | import org.yuzu.yuzu_emu.databinding.ActivityEmulationBinding | 41 | import org.yuzu.yuzu_emu.databinding.ActivityEmulationBinding |
| 40 | import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting | 42 | import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting |
| 41 | import org.yuzu.yuzu_emu.features.settings.model.IntSetting | 43 | import org.yuzu.yuzu_emu.features.settings.model.IntSetting |
| 44 | import org.yuzu.yuzu_emu.features.settings.model.Settings | ||
| 42 | import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel | 45 | import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel |
| 43 | import org.yuzu.yuzu_emu.model.Game | 46 | import org.yuzu.yuzu_emu.model.Game |
| 44 | import org.yuzu.yuzu_emu.utils.ControllerMappingHelper | 47 | import org.yuzu.yuzu_emu.utils.ControllerMappingHelper |
| @@ -47,6 +50,7 @@ import org.yuzu.yuzu_emu.utils.InputHandler | |||
| 47 | import org.yuzu.yuzu_emu.utils.MemoryUtil | 50 | import org.yuzu.yuzu_emu.utils.MemoryUtil |
| 48 | import org.yuzu.yuzu_emu.utils.NfcReader | 51 | import org.yuzu.yuzu_emu.utils.NfcReader |
| 49 | import org.yuzu.yuzu_emu.utils.ThemeHelper | 52 | import org.yuzu.yuzu_emu.utils.ThemeHelper |
| 53 | import java.text.NumberFormat | ||
| 50 | import kotlin.math.roundToInt | 54 | import kotlin.math.roundToInt |
| 51 | 55 | ||
| 52 | class EmulationActivity : AppCompatActivity(), SensorEventListener { | 56 | class EmulationActivity : AppCompatActivity(), SensorEventListener { |
| @@ -106,17 +110,26 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { | |||
| 106 | inputHandler = InputHandler() | 110 | inputHandler = InputHandler() |
| 107 | inputHandler.initialize() | 111 | inputHandler.initialize() |
| 108 | 112 | ||
| 109 | val memoryUtil = MemoryUtil(this) | 113 | val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) |
| 110 | if (memoryUtil.isLessThan(8, MemoryUtil.Gb)) { | 114 | if (!preferences.getBoolean(Settings.PREF_MEMORY_WARNING_SHOWN, false)) { |
| 111 | Toast.makeText( | 115 | if (MemoryUtil.isLessThan(MemoryUtil.REQUIRED_MEMORY, MemoryUtil.Gb)) { |
| 112 | this, | 116 | Toast.makeText( |
| 113 | getString( | 117 | this, |
| 114 | R.string.device_memory_inadequate, | 118 | getString( |
| 115 | memoryUtil.getDeviceRAM(), | 119 | R.string.device_memory_inadequate, |
| 116 | "8 ${getString(R.string.memory_gigabyte)}" | 120 | MemoryUtil.getDeviceRAM(), |
| 117 | ), | 121 | getString( |
| 118 | Toast.LENGTH_LONG | 122 | R.string.memory_formatted, |
| 119 | ).show() | 123 | NumberFormat.getInstance().format(MemoryUtil.REQUIRED_MEMORY), |
| 124 | getString(R.string.memory_gigabyte) | ||
| 125 | ) | ||
| 126 | ), | ||
| 127 | Toast.LENGTH_LONG | ||
| 128 | ).show() | ||
| 129 | preferences.edit() | ||
| 130 | .putBoolean(Settings.PREF_MEMORY_WARNING_SHOWN, true) | ||
| 131 | .apply() | ||
| 132 | } | ||
| 120 | } | 133 | } |
| 121 | 134 | ||
| 122 | // Start a foreground service to prevent the app from getting killed in the background | 135 | // Start a foreground service to prevent the app from getting killed in the background |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt index 88afb2223..be6e17e65 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt | |||
| @@ -110,6 +110,8 @@ class Settings { | |||
| 110 | const val SECTION_THEME = "Theme" | 110 | const val SECTION_THEME = "Theme" |
| 111 | const val SECTION_DEBUG = "Debug" | 111 | const val SECTION_DEBUG = "Debug" |
| 112 | 112 | ||
| 113 | const val PREF_MEMORY_WARNING_SHOWN = "MemoryWarningShown" | ||
| 114 | |||
| 113 | const val PREF_OVERLAY_INIT = "OverlayInit" | 115 | const val PREF_OVERLAY_INIT = "OverlayInit" |
| 114 | const val PREF_CONTROL_SCALE = "controlScale" | 116 | const val PREF_CONTROL_SCALE = "controlScale" |
| 115 | const val PREF_CONTROL_OPACITY = "controlOpacity" | 117 | const val PREF_CONTROL_OPACITY = "controlOpacity" |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt index 18e5fa0b0..aa4a5539a 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt | |||
| @@ -5,35 +5,101 @@ package org.yuzu.yuzu_emu.utils | |||
| 5 | 5 | ||
| 6 | import android.app.ActivityManager | 6 | import android.app.ActivityManager |
| 7 | import android.content.Context | 7 | import android.content.Context |
| 8 | import android.os.Build | ||
| 8 | import org.yuzu.yuzu_emu.R | 9 | import org.yuzu.yuzu_emu.R |
| 10 | import org.yuzu.yuzu_emu.YuzuApplication | ||
| 9 | import java.util.Locale | 11 | import java.util.Locale |
| 12 | import kotlin.math.ceil | ||
| 10 | 13 | ||
| 11 | class MemoryUtil(val context: Context) { | 14 | object MemoryUtil { |
| 15 | private val context get() = YuzuApplication.appContext | ||
| 12 | 16 | ||
| 13 | private val Long.floatForm: String | 17 | private val Float.hundredths: String |
| 14 | get() = String.format(Locale.ROOT, "%.2f", this.toDouble()) | 18 | get() = String.format(Locale.ROOT, "%.2f", this) |
| 15 | 19 | ||
| 16 | private fun bytesToSizeUnit(size: Long): String { | 20 | // Required total system memory |
| 17 | return when { | 21 | const val REQUIRED_MEMORY = 8 |
| 18 | size < Kb -> "${size.floatForm} ${context.getString(R.string.memory_byte)}" | 22 | |
| 19 | size < Mb -> "${(size / Kb).floatForm} ${context.getString(R.string.memory_kilobyte)}" | 23 | const val Kb: Float = 1024F |
| 20 | size < Gb -> "${(size / Mb).floatForm} ${context.getString(R.string.memory_megabyte)}" | 24 | const val Mb = Kb * 1024 |
| 21 | size < Tb -> "${(size / Gb).floatForm} ${context.getString(R.string.memory_gigabyte)}" | 25 | const val Gb = Mb * 1024 |
| 22 | size < Pb -> "${(size / Tb).floatForm} ${context.getString(R.string.memory_terabyte)}" | 26 | const val Tb = Gb * 1024 |
| 23 | size < Eb -> "${(size / Pb).floatForm} ${context.getString(R.string.memory_petabyte)}" | 27 | const val Pb = Tb * 1024 |
| 24 | else -> "${(size / Eb).floatForm} ${context.getString(R.string.memory_exabyte)}" | 28 | const val Eb = Pb * 1024 |
| 29 | |||
| 30 | private fun bytesToSizeUnit(size: Float): String = | ||
| 31 | when { | ||
| 32 | size < Kb -> { | ||
| 33 | context.getString( | ||
| 34 | R.string.memory_formatted, | ||
| 35 | size.hundredths, | ||
| 36 | context.getString(R.string.memory_byte) | ||
| 37 | ) | ||
| 38 | } | ||
| 39 | size < Mb -> { | ||
| 40 | context.getString( | ||
| 41 | R.string.memory_formatted, | ||
| 42 | (size / Kb).hundredths, | ||
| 43 | context.getString(R.string.memory_kilobyte) | ||
| 44 | ) | ||
| 45 | } | ||
| 46 | size < Gb -> { | ||
| 47 | context.getString( | ||
| 48 | R.string.memory_formatted, | ||
| 49 | (size / Mb).hundredths, | ||
| 50 | context.getString(R.string.memory_megabyte) | ||
| 51 | ) | ||
| 52 | } | ||
| 53 | size < Tb -> { | ||
| 54 | context.getString( | ||
| 55 | R.string.memory_formatted, | ||
| 56 | (size / Gb).hundredths, | ||
| 57 | context.getString(R.string.memory_gigabyte) | ||
| 58 | ) | ||
| 59 | } | ||
| 60 | size < Pb -> { | ||
| 61 | context.getString( | ||
| 62 | R.string.memory_formatted, | ||
| 63 | (size / Tb).hundredths, | ||
| 64 | context.getString(R.string.memory_terabyte) | ||
| 65 | ) | ||
| 66 | } | ||
| 67 | size < Eb -> { | ||
| 68 | context.getString( | ||
| 69 | R.string.memory_formatted, | ||
| 70 | (size / Pb).hundredths, | ||
| 71 | context.getString(R.string.memory_petabyte) | ||
| 72 | ) | ||
| 73 | } | ||
| 74 | else -> { | ||
| 75 | context.getString( | ||
| 76 | R.string.memory_formatted, | ||
| 77 | (size / Eb).hundredths, | ||
| 78 | context.getString(R.string.memory_exabyte) | ||
| 79 | ) | ||
| 80 | } | ||
| 25 | } | 81 | } |
| 26 | } | ||
| 27 | 82 | ||
| 28 | private val totalMemory = | 83 | // Devices are unlikely to have 0.5GB increments of memory so we'll just round up to account for |
| 29 | with(context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager) { | 84 | // the potential error created by memInfo.totalMem |
| 85 | private val totalMemory: Float | ||
| 86 | get() { | ||
| 30 | val memInfo = ActivityManager.MemoryInfo() | 87 | val memInfo = ActivityManager.MemoryInfo() |
| 31 | getMemoryInfo(memInfo) | 88 | with(context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager) { |
| 32 | memInfo.totalMem | 89 | getMemoryInfo(memInfo) |
| 90 | } | ||
| 91 | |||
| 92 | return ceil( | ||
| 93 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { | ||
| 94 | memInfo.advertisedMem.toFloat() | ||
| 95 | } else { | ||
| 96 | memInfo.totalMem.toFloat() | ||
| 97 | } | ||
| 98 | ) | ||
| 33 | } | 99 | } |
| 34 | 100 | ||
| 35 | fun isLessThan(minimum: Int, size: Long): Boolean { | 101 | fun isLessThan(minimum: Int, size: Float): Boolean = |
| 36 | return when (size) { | 102 | when (size) { |
| 37 | Kb -> totalMemory < Mb && totalMemory < minimum | 103 | Kb -> totalMemory < Mb && totalMemory < minimum |
| 38 | Mb -> totalMemory < Gb && (totalMemory / Mb) < minimum | 104 | Mb -> totalMemory < Gb && (totalMemory / Mb) < minimum |
| 39 | Gb -> totalMemory < Tb && (totalMemory / Gb) < minimum | 105 | Gb -> totalMemory < Tb && (totalMemory / Gb) < minimum |
| @@ -42,18 +108,6 @@ class MemoryUtil(val context: Context) { | |||
| 42 | Eb -> totalMemory / Eb < minimum | 108 | Eb -> totalMemory / Eb < minimum |
| 43 | else -> totalMemory < Kb && totalMemory < minimum | 109 | else -> totalMemory < Kb && totalMemory < minimum |
| 44 | } | 110 | } |
| 45 | } | 111 | |
| 46 | 112 | fun getDeviceRAM(): String = bytesToSizeUnit(totalMemory) | |
| 47 | fun getDeviceRAM(): String { | ||
| 48 | return bytesToSizeUnit(totalMemory) | ||
| 49 | } | ||
| 50 | |||
| 51 | companion object { | ||
| 52 | const val Kb: Long = 1024 | ||
| 53 | const val Mb = Kb * 1024 | ||
| 54 | const val Gb = Mb * 1024 | ||
| 55 | const val Tb = Gb * 1024 | ||
| 56 | const val Pb = Tb * 1024 | ||
| 57 | const val Eb = Pb * 1024 | ||
| 58 | } | ||
| 59 | } | 113 | } |
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index af7450619..b3c737979 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml | |||
| @@ -273,6 +273,7 @@ | |||
| 273 | <string name="fatal_error_message">A fatal error occurred. Check the log for details.\nContinuing emulation may result in crashes and bugs.</string> | 273 | <string name="fatal_error_message">A fatal error occurred. Check the log for details.\nContinuing emulation may result in crashes and bugs.</string> |
| 274 | <string name="performance_warning">Turning off this setting will significantly reduce emulation performance! For the best experience, it is recommended that you leave this setting enabled.</string> | 274 | <string name="performance_warning">Turning off this setting will significantly reduce emulation performance! For the best experience, it is recommended that you leave this setting enabled.</string> |
| 275 | <string name="device_memory_inadequate">Device RAM: %1$s\nRecommended: %2$s</string> | 275 | <string name="device_memory_inadequate">Device RAM: %1$s\nRecommended: %2$s</string> |
| 276 | <string name="memory_formatted">%1$s %2$s</string> | ||
| 276 | 277 | ||
| 277 | <!-- Region Names --> | 278 | <!-- Region Names --> |
| 278 | <string name="region_japan">Japan</string> | 279 | <string name="region_japan">Japan</string> |
diff --git a/src/audio_core/sink/sink_stream.cpp b/src/audio_core/sink/sink_stream.cpp index 404dcd0e9..6081352a2 100644 --- a/src/audio_core/sink/sink_stream.cpp +++ b/src/audio_core/sink/sink_stream.cpp | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | #include "audio_core/sink/sink_stream.h" | 12 | #include "audio_core/sink/sink_stream.h" |
| 13 | #include "common/common_types.h" | 13 | #include "common/common_types.h" |
| 14 | #include "common/fixed_point.h" | 14 | #include "common/fixed_point.h" |
| 15 | #include "common/scope_exit.h" | ||
| 15 | #include "common/settings.h" | 16 | #include "common/settings.h" |
| 16 | #include "core/core.h" | 17 | #include "core/core.h" |
| 17 | #include "core/core_timing.h" | 18 | #include "core/core_timing.h" |
| @@ -19,9 +20,12 @@ | |||
| 19 | namespace AudioCore::Sink { | 20 | namespace AudioCore::Sink { |
| 20 | 21 | ||
| 21 | void SinkStream::AppendBuffer(SinkBuffer& buffer, std::span<s16> samples) { | 22 | void SinkStream::AppendBuffer(SinkBuffer& buffer, std::span<s16> samples) { |
| 22 | if (type == StreamType::In) { | 23 | SCOPE_EXIT({ |
| 23 | queue.enqueue(buffer); | 24 | queue.enqueue(buffer); |
| 24 | queued_buffers++; | 25 | ++queued_buffers; |
| 26 | }); | ||
| 27 | |||
| 28 | if (type == StreamType::In) { | ||
| 25 | return; | 29 | return; |
| 26 | } | 30 | } |
| 27 | 31 | ||
| @@ -66,16 +70,17 @@ void SinkStream::AppendBuffer(SinkBuffer& buffer, std::span<s16> samples) { | |||
| 66 | static_cast<s16>(std::clamp(right_sample, min, max)); | 70 | static_cast<s16>(std::clamp(right_sample, min, max)); |
| 67 | } | 71 | } |
| 68 | 72 | ||
| 69 | samples = samples.subspan(0, samples.size() / system_channels * device_channels); | 73 | samples_buffer.Push(samples.subspan(0, samples.size() / system_channels * device_channels)); |
| 74 | return; | ||
| 75 | } | ||
| 70 | 76 | ||
| 71 | } else if (system_channels == 2 && device_channels == 6) { | 77 | if (system_channels == 2 && device_channels == 6) { |
| 72 | // We need moar samples! Not all games will provide 6 channel audio. | 78 | // We need moar samples! Not all games will provide 6 channel audio. |
| 73 | // TODO: Implement some upmixing here. Currently just passthrough, with other | 79 | // TODO: Implement some upmixing here. Currently just passthrough, with other |
| 74 | // channels left as silence. | 80 | // channels left as silence. |
| 75 | auto new_size = samples.size() / system_channels * device_channels; | 81 | std::vector<s16> new_samples(samples.size() / system_channels * device_channels); |
| 76 | tmp_samples.resize_destructive(new_size); | ||
| 77 | 82 | ||
| 78 | for (u32 read_index = 0, write_index = 0; read_index < new_size; | 83 | for (u32 read_index = 0, write_index = 0; read_index < samples.size(); |
| 79 | read_index += system_channels, write_index += device_channels) { | 84 | read_index += system_channels, write_index += device_channels) { |
| 80 | const auto left_sample{static_cast<s16>(std::clamp( | 85 | const auto left_sample{static_cast<s16>(std::clamp( |
| 81 | static_cast<s32>( | 86 | static_cast<s32>( |
| @@ -83,7 +88,7 @@ void SinkStream::AppendBuffer(SinkBuffer& buffer, std::span<s16> samples) { | |||
| 83 | volume), | 88 | volume), |
| 84 | min, max))}; | 89 | min, max))}; |
| 85 | 90 | ||
| 86 | tmp_samples[write_index + static_cast<u32>(Channels::FrontLeft)] = left_sample; | 91 | new_samples[write_index + static_cast<u32>(Channels::FrontLeft)] = left_sample; |
| 87 | 92 | ||
| 88 | const auto right_sample{static_cast<s16>(std::clamp( | 93 | const auto right_sample{static_cast<s16>(std::clamp( |
| 89 | static_cast<s32>( | 94 | static_cast<s32>( |
| @@ -91,20 +96,21 @@ void SinkStream::AppendBuffer(SinkBuffer& buffer, std::span<s16> samples) { | |||
| 91 | volume), | 96 | volume), |
| 92 | min, max))}; | 97 | min, max))}; |
| 93 | 98 | ||
| 94 | tmp_samples[write_index + static_cast<u32>(Channels::FrontRight)] = right_sample; | 99 | new_samples[write_index + static_cast<u32>(Channels::FrontRight)] = right_sample; |
| 95 | } | 100 | } |
| 96 | samples = std::span<s16>(tmp_samples); | ||
| 97 | 101 | ||
| 98 | } else if (volume != 1.0f) { | 102 | samples_buffer.Push(new_samples); |
| 99 | for (u32 i = 0; i < samples.size(); i++) { | 103 | return; |
| 104 | } | ||
| 105 | |||
| 106 | if (volume != 1.0f) { | ||
| 107 | for (u32 i = 0; i < samples.size(); ++i) { | ||
| 100 | samples[i] = static_cast<s16>( | 108 | samples[i] = static_cast<s16>( |
| 101 | std::clamp(static_cast<s32>(static_cast<f32>(samples[i]) * volume), min, max)); | 109 | std::clamp(static_cast<s32>(static_cast<f32>(samples[i]) * volume), min, max)); |
| 102 | } | 110 | } |
| 103 | } | 111 | } |
| 104 | 112 | ||
| 105 | samples_buffer.Push(samples); | 113 | samples_buffer.Push(samples); |
| 106 | queue.enqueue(buffer); | ||
| 107 | queued_buffers++; | ||
| 108 | } | 114 | } |
| 109 | 115 | ||
| 110 | std::vector<s16> SinkStream::ReleaseBuffer(u64 num_samples) { | 116 | std::vector<s16> SinkStream::ReleaseBuffer(u64 num_samples) { |
diff --git a/src/audio_core/sink/sink_stream.h b/src/audio_core/sink/sink_stream.h index 98d72ace1..6a4996ca3 100644 --- a/src/audio_core/sink/sink_stream.h +++ b/src/audio_core/sink/sink_stream.h | |||
| @@ -16,7 +16,6 @@ | |||
| 16 | #include "common/polyfill_thread.h" | 16 | #include "common/polyfill_thread.h" |
| 17 | #include "common/reader_writer_queue.h" | 17 | #include "common/reader_writer_queue.h" |
| 18 | #include "common/ring_buffer.h" | 18 | #include "common/ring_buffer.h" |
| 19 | #include "common/scratch_buffer.h" | ||
| 20 | #include "common/thread.h" | 19 | #include "common/thread.h" |
| 21 | 20 | ||
| 22 | namespace Core { | 21 | namespace Core { |
| @@ -256,8 +255,6 @@ private: | |||
| 256 | /// Signalled when ring buffer entries are consumed | 255 | /// Signalled when ring buffer entries are consumed |
| 257 | std::condition_variable_any release_cv; | 256 | std::condition_variable_any release_cv; |
| 258 | std::mutex release_mutex; | 257 | std::mutex release_mutex; |
| 259 | /// Temporary buffer for appending samples when upmixing | ||
| 260 | Common::ScratchBuffer<s16> tmp_samples{}; | ||
| 261 | }; | 258 | }; |
| 262 | 259 | ||
| 263 | using SinkStreamPtr = std::unique_ptr<SinkStream>; | 260 | using SinkStreamPtr = std::unique_ptr<SinkStream>; |
diff --git a/src/common/ring_buffer.h b/src/common/ring_buffer.h index 416680d44..5c961b202 100644 --- a/src/common/ring_buffer.h +++ b/src/common/ring_buffer.h | |||
| @@ -54,7 +54,7 @@ public: | |||
| 54 | return push_count; | 54 | return push_count; |
| 55 | } | 55 | } |
| 56 | 56 | ||
| 57 | std::size_t Push(const std::span<T> input) { | 57 | std::size_t Push(std::span<const T> input) { |
| 58 | return Push(input.data(), input.size()); | 58 | return Push(input.data(), input.size()); |
| 59 | } | 59 | } |
| 60 | 60 | ||
diff --git a/src/common/scratch_buffer.h b/src/common/scratch_buffer.h index 6fe907953..d5961b020 100644 --- a/src/common/scratch_buffer.h +++ b/src/common/scratch_buffer.h | |||
| @@ -5,7 +5,6 @@ | |||
| 5 | 5 | ||
| 6 | #include <iterator> | 6 | #include <iterator> |
| 7 | 7 | ||
| 8 | #include "common/concepts.h" | ||
| 9 | #include "common/make_unique_for_overwrite.h" | 8 | #include "common/make_unique_for_overwrite.h" |
| 10 | 9 | ||
| 11 | namespace Common { | 10 | namespace Common { |
| @@ -19,15 +18,22 @@ namespace Common { | |||
| 19 | template <typename T> | 18 | template <typename T> |
| 20 | class ScratchBuffer { | 19 | class ScratchBuffer { |
| 21 | public: | 20 | public: |
| 22 | using iterator = T*; | ||
| 23 | using const_iterator = const T*; | ||
| 24 | using value_type = T; | ||
| 25 | using element_type = T; | 21 | using element_type = T; |
| 26 | using iterator_category = std::contiguous_iterator_tag; | 22 | using value_type = T; |
| 23 | using size_type = size_t; | ||
| 24 | using difference_type = std::ptrdiff_t; | ||
| 25 | using pointer = T*; | ||
| 26 | using const_pointer = const T*; | ||
| 27 | using reference = T&; | ||
| 28 | using const_reference = const T&; | ||
| 29 | using iterator = pointer; | ||
| 30 | using const_iterator = const_pointer; | ||
| 31 | using iterator_category = std::random_access_iterator_tag; | ||
| 32 | using iterator_concept = std::contiguous_iterator_tag; | ||
| 27 | 33 | ||
| 28 | ScratchBuffer() = default; | 34 | ScratchBuffer() = default; |
| 29 | 35 | ||
| 30 | explicit ScratchBuffer(size_t initial_capacity) | 36 | explicit ScratchBuffer(size_type initial_capacity) |
| 31 | : last_requested_size{initial_capacity}, buffer_capacity{initial_capacity}, | 37 | : last_requested_size{initial_capacity}, buffer_capacity{initial_capacity}, |
| 32 | buffer{Common::make_unique_for_overwrite<T[]>(initial_capacity)} {} | 38 | buffer{Common::make_unique_for_overwrite<T[]>(initial_capacity)} {} |
| 33 | 39 | ||
| @@ -39,7 +45,7 @@ public: | |||
| 39 | 45 | ||
| 40 | /// This will only grow the buffer's capacity if size is greater than the current capacity. | 46 | /// This will only grow the buffer's capacity if size is greater than the current capacity. |
| 41 | /// The previously held data will remain intact. | 47 | /// The previously held data will remain intact. |
| 42 | void resize(size_t size) { | 48 | void resize(size_type size) { |
| 43 | if (size > buffer_capacity) { | 49 | if (size > buffer_capacity) { |
| 44 | auto new_buffer = Common::make_unique_for_overwrite<T[]>(size); | 50 | auto new_buffer = Common::make_unique_for_overwrite<T[]>(size); |
| 45 | std::move(buffer.get(), buffer.get() + buffer_capacity, new_buffer.get()); | 51 | std::move(buffer.get(), buffer.get() + buffer_capacity, new_buffer.get()); |
| @@ -51,7 +57,7 @@ public: | |||
| 51 | 57 | ||
| 52 | /// This will only grow the buffer's capacity if size is greater than the current capacity. | 58 | /// This will only grow the buffer's capacity if size is greater than the current capacity. |
| 53 | /// The previously held data will be destroyed if a reallocation occurs. | 59 | /// The previously held data will be destroyed if a reallocation occurs. |
| 54 | void resize_destructive(size_t size) { | 60 | void resize_destructive(size_type size) { |
| 55 | if (size > buffer_capacity) { | 61 | if (size > buffer_capacity) { |
| 56 | buffer_capacity = size; | 62 | buffer_capacity = size; |
| 57 | buffer = Common::make_unique_for_overwrite<T[]>(buffer_capacity); | 63 | buffer = Common::make_unique_for_overwrite<T[]>(buffer_capacity); |
| @@ -59,43 +65,43 @@ public: | |||
| 59 | last_requested_size = size; | 65 | last_requested_size = size; |
| 60 | } | 66 | } |
| 61 | 67 | ||
| 62 | [[nodiscard]] T* data() noexcept { | 68 | [[nodiscard]] pointer data() noexcept { |
| 63 | return buffer.get(); | 69 | return buffer.get(); |
| 64 | } | 70 | } |
| 65 | 71 | ||
| 66 | [[nodiscard]] const T* data() const noexcept { | 72 | [[nodiscard]] const_pointer data() const noexcept { |
| 67 | return buffer.get(); | 73 | return buffer.get(); |
| 68 | } | 74 | } |
| 69 | 75 | ||
| 70 | [[nodiscard]] T* begin() noexcept { | 76 | [[nodiscard]] iterator begin() noexcept { |
| 71 | return data(); | 77 | return data(); |
| 72 | } | 78 | } |
| 73 | 79 | ||
| 74 | [[nodiscard]] const T* begin() const noexcept { | 80 | [[nodiscard]] const_iterator begin() const noexcept { |
| 75 | return data(); | 81 | return data(); |
| 76 | } | 82 | } |
| 77 | 83 | ||
| 78 | [[nodiscard]] T* end() noexcept { | 84 | [[nodiscard]] iterator end() noexcept { |
| 79 | return data() + last_requested_size; | 85 | return data() + last_requested_size; |
| 80 | } | 86 | } |
| 81 | 87 | ||
| 82 | [[nodiscard]] const T* end() const noexcept { | 88 | [[nodiscard]] const_iterator end() const noexcept { |
| 83 | return data() + last_requested_size; | 89 | return data() + last_requested_size; |
| 84 | } | 90 | } |
| 85 | 91 | ||
| 86 | [[nodiscard]] T& operator[](size_t i) { | 92 | [[nodiscard]] reference operator[](size_type i) { |
| 87 | return buffer[i]; | 93 | return buffer[i]; |
| 88 | } | 94 | } |
| 89 | 95 | ||
| 90 | [[nodiscard]] const T& operator[](size_t i) const { | 96 | [[nodiscard]] const_reference operator[](size_type i) const { |
| 91 | return buffer[i]; | 97 | return buffer[i]; |
| 92 | } | 98 | } |
| 93 | 99 | ||
| 94 | [[nodiscard]] size_t size() const noexcept { | 100 | [[nodiscard]] size_type size() const noexcept { |
| 95 | return last_requested_size; | 101 | return last_requested_size; |
| 96 | } | 102 | } |
| 97 | 103 | ||
| 98 | [[nodiscard]] size_t capacity() const noexcept { | 104 | [[nodiscard]] size_type capacity() const noexcept { |
| 99 | return buffer_capacity; | 105 | return buffer_capacity; |
| 100 | } | 106 | } |
| 101 | 107 | ||
| @@ -106,8 +112,8 @@ public: | |||
| 106 | } | 112 | } |
| 107 | 113 | ||
| 108 | private: | 114 | private: |
| 109 | size_t last_requested_size{}; | 115 | size_type last_requested_size{}; |
| 110 | size_t buffer_capacity{}; | 116 | size_type buffer_capacity{}; |
| 111 | std::unique_ptr<T[]> buffer{}; | 117 | std::unique_ptr<T[]> buffer{}; |
| 112 | }; | 118 | }; |
| 113 | 119 | ||
diff --git a/src/common/settings.h b/src/common/settings.h index ae5ed93d8..59e96e74f 100644 --- a/src/common/settings.h +++ b/src/common/settings.h | |||
| @@ -527,12 +527,10 @@ struct Values { | |||
| 527 | Setting<bool> mouse_panning{false, "mouse_panning"}; | 527 | Setting<bool> mouse_panning{false, "mouse_panning"}; |
| 528 | Setting<u8, true> mouse_panning_x_sensitivity{50, 1, 100, "mouse_panning_x_sensitivity"}; | 528 | Setting<u8, true> mouse_panning_x_sensitivity{50, 1, 100, "mouse_panning_x_sensitivity"}; |
| 529 | Setting<u8, true> mouse_panning_y_sensitivity{50, 1, 100, "mouse_panning_y_sensitivity"}; | 529 | Setting<u8, true> mouse_panning_y_sensitivity{50, 1, 100, "mouse_panning_y_sensitivity"}; |
| 530 | Setting<u8, true> mouse_panning_deadzone_x_counterweight{ | 530 | Setting<u8, true> mouse_panning_deadzone_counterweight{20, 0, 100, |
| 531 | 0, 0, 100, "mouse_panning_deadzone_x_counterweight"}; | 531 | "mouse_panning_deadzone_counterweight"}; |
| 532 | Setting<u8, true> mouse_panning_deadzone_y_counterweight{ | 532 | Setting<u8, true> mouse_panning_decay_strength{18, 0, 100, "mouse_panning_decay_strength"}; |
| 533 | 0, 0, 100, "mouse_panning_deadzone_y_counterweight"}; | 533 | Setting<u8, true> mouse_panning_min_decay{6, 0, 100, "mouse_panning_min_decay"}; |
| 534 | Setting<u8, true> mouse_panning_decay_strength{22, 0, 100, "mouse_panning_decay_strength"}; | ||
| 535 | Setting<u8, true> mouse_panning_min_decay{5, 0, 100, "mouse_panning_min_decay"}; | ||
| 536 | 534 | ||
| 537 | Setting<bool> mouse_enabled{false, "mouse_enabled"}; | 535 | Setting<bool> mouse_enabled{false, "mouse_enabled"}; |
| 538 | Setting<bool> emulate_analog_keyboard{false, "emulate_analog_keyboard"}; | 536 | Setting<bool> emulate_analog_keyboard{false, "emulate_analog_keyboard"}; |
diff --git a/src/core/core.cpp b/src/core/core.cpp index b74fd0a58..9e3eb3795 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -27,6 +27,7 @@ | |||
| 27 | #include "core/file_sys/savedata_factory.h" | 27 | #include "core/file_sys/savedata_factory.h" |
| 28 | #include "core/file_sys/vfs_concat.h" | 28 | #include "core/file_sys/vfs_concat.h" |
| 29 | #include "core/file_sys/vfs_real.h" | 29 | #include "core/file_sys/vfs_real.h" |
| 30 | #include "core/gpu_dirty_memory_manager.h" | ||
| 30 | #include "core/hid/hid_core.h" | 31 | #include "core/hid/hid_core.h" |
| 31 | #include "core/hle/kernel/k_memory_manager.h" | 32 | #include "core/hle/kernel/k_memory_manager.h" |
| 32 | #include "core/hle/kernel/k_process.h" | 33 | #include "core/hle/kernel/k_process.h" |
| @@ -130,7 +131,10 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, | |||
| 130 | struct System::Impl { | 131 | struct System::Impl { |
| 131 | explicit Impl(System& system) | 132 | explicit Impl(System& system) |
| 132 | : kernel{system}, fs_controller{system}, memory{system}, hid_core{}, room_network{}, | 133 | : kernel{system}, fs_controller{system}, memory{system}, hid_core{}, room_network{}, |
| 133 | cpu_manager{system}, reporter{system}, applet_manager{system}, time_manager{system} {} | 134 | cpu_manager{system}, reporter{system}, applet_manager{system}, time_manager{system}, |
| 135 | gpu_dirty_memory_write_manager{} { | ||
| 136 | memory.SetGPUDirtyManagers(gpu_dirty_memory_write_manager); | ||
| 137 | } | ||
| 134 | 138 | ||
| 135 | void Initialize(System& system) { | 139 | void Initialize(System& system) { |
| 136 | device_memory = std::make_unique<Core::DeviceMemory>(); | 140 | device_memory = std::make_unique<Core::DeviceMemory>(); |
| @@ -234,6 +238,8 @@ struct System::Impl { | |||
| 234 | // Setting changes may require a full system reinitialization (e.g., disabling multicore). | 238 | // Setting changes may require a full system reinitialization (e.g., disabling multicore). |
| 235 | ReinitializeIfNecessary(system); | 239 | ReinitializeIfNecessary(system); |
| 236 | 240 | ||
| 241 | memory.SetGPUDirtyManagers(gpu_dirty_memory_write_manager); | ||
| 242 | |||
| 237 | kernel.Initialize(); | 243 | kernel.Initialize(); |
| 238 | cpu_manager.Initialize(); | 244 | cpu_manager.Initialize(); |
| 239 | 245 | ||
| @@ -540,6 +546,9 @@ struct System::Impl { | |||
| 540 | 546 | ||
| 541 | std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{}; | 547 | std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{}; |
| 542 | std::array<MicroProfileToken, Core::Hardware::NUM_CPU_CORES> microprofile_cpu{}; | 548 | std::array<MicroProfileToken, Core::Hardware::NUM_CPU_CORES> microprofile_cpu{}; |
| 549 | |||
| 550 | std::array<Core::GPUDirtyMemoryManager, Core::Hardware::NUM_CPU_CORES> | ||
| 551 | gpu_dirty_memory_write_manager{}; | ||
| 543 | }; | 552 | }; |
| 544 | 553 | ||
| 545 | System::System() : impl{std::make_unique<Impl>(*this)} {} | 554 | System::System() : impl{std::make_unique<Impl>(*this)} {} |
| @@ -629,10 +638,31 @@ void System::PrepareReschedule(const u32 core_index) { | |||
| 629 | impl->kernel.PrepareReschedule(core_index); | 638 | impl->kernel.PrepareReschedule(core_index); |
| 630 | } | 639 | } |
| 631 | 640 | ||
| 641 | Core::GPUDirtyMemoryManager& System::CurrentGPUDirtyMemoryManager() { | ||
| 642 | const std::size_t core = impl->kernel.GetCurrentHostThreadID(); | ||
| 643 | return impl->gpu_dirty_memory_write_manager[core < Core::Hardware::NUM_CPU_CORES | ||
| 644 | ? core | ||
| 645 | : Core::Hardware::NUM_CPU_CORES - 1]; | ||
| 646 | } | ||
| 647 | |||
| 648 | /// Provides a constant reference to the current gou dirty memory manager. | ||
| 649 | const Core::GPUDirtyMemoryManager& System::CurrentGPUDirtyMemoryManager() const { | ||
| 650 | const std::size_t core = impl->kernel.GetCurrentHostThreadID(); | ||
| 651 | return impl->gpu_dirty_memory_write_manager[core < Core::Hardware::NUM_CPU_CORES | ||
| 652 | ? core | ||
| 653 | : Core::Hardware::NUM_CPU_CORES - 1]; | ||
| 654 | } | ||
| 655 | |||
| 632 | size_t System::GetCurrentHostThreadID() const { | 656 | size_t System::GetCurrentHostThreadID() const { |
| 633 | return impl->kernel.GetCurrentHostThreadID(); | 657 | return impl->kernel.GetCurrentHostThreadID(); |
| 634 | } | 658 | } |
| 635 | 659 | ||
| 660 | void System::GatherGPUDirtyMemory(std::function<void(VAddr, size_t)>& callback) { | ||
| 661 | for (auto& manager : impl->gpu_dirty_memory_write_manager) { | ||
| 662 | manager.Gather(callback); | ||
| 663 | } | ||
| 664 | } | ||
| 665 | |||
| 636 | PerfStatsResults System::GetAndResetPerfStats() { | 666 | PerfStatsResults System::GetAndResetPerfStats() { |
| 637 | return impl->GetAndResetPerfStats(); | 667 | return impl->GetAndResetPerfStats(); |
| 638 | } | 668 | } |
diff --git a/src/core/core.h b/src/core/core.h index 93afc9303..14b2f7785 100644 --- a/src/core/core.h +++ b/src/core/core.h | |||
| @@ -108,9 +108,10 @@ class CpuManager; | |||
| 108 | class Debugger; | 108 | class Debugger; |
| 109 | class DeviceMemory; | 109 | class DeviceMemory; |
| 110 | class ExclusiveMonitor; | 110 | class ExclusiveMonitor; |
| 111 | class SpeedLimiter; | 111 | class GPUDirtyMemoryManager; |
| 112 | class PerfStats; | 112 | class PerfStats; |
| 113 | class Reporter; | 113 | class Reporter; |
| 114 | class SpeedLimiter; | ||
| 114 | class TelemetrySession; | 115 | class TelemetrySession; |
| 115 | 116 | ||
| 116 | struct PerfStatsResults; | 117 | struct PerfStatsResults; |
| @@ -225,6 +226,14 @@ public: | |||
| 225 | /// Prepare the core emulation for a reschedule | 226 | /// Prepare the core emulation for a reschedule |
| 226 | void PrepareReschedule(u32 core_index); | 227 | void PrepareReschedule(u32 core_index); |
| 227 | 228 | ||
| 229 | /// Provides a reference to the gou dirty memory manager. | ||
| 230 | [[nodiscard]] Core::GPUDirtyMemoryManager& CurrentGPUDirtyMemoryManager(); | ||
| 231 | |||
| 232 | /// Provides a constant reference to the current gou dirty memory manager. | ||
| 233 | [[nodiscard]] const Core::GPUDirtyMemoryManager& CurrentGPUDirtyMemoryManager() const; | ||
| 234 | |||
| 235 | void GatherGPUDirtyMemory(std::function<void(VAddr, size_t)>& callback); | ||
| 236 | |||
| 228 | [[nodiscard]] size_t GetCurrentHostThreadID() const; | 237 | [[nodiscard]] size_t GetCurrentHostThreadID() const; |
| 229 | 238 | ||
| 230 | /// Gets and resets core performance statistics | 239 | /// Gets and resets core performance statistics |
diff --git a/src/core/file_sys/fsmitm_romfsbuild.cpp b/src/core/file_sys/fsmitm_romfsbuild.cpp index 1ff83c08c..e39c7b62b 100644 --- a/src/core/file_sys/fsmitm_romfsbuild.cpp +++ b/src/core/file_sys/fsmitm_romfsbuild.cpp | |||
| @@ -105,19 +105,11 @@ static u64 romfs_get_hash_table_count(u64 num_entries) { | |||
| 105 | return count; | 105 | return count; |
| 106 | } | 106 | } |
| 107 | 107 | ||
| 108 | void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs, VirtualDir ext_dir, | 108 | void RomFSBuildContext::VisitDirectory(VirtualDir romfs_dir, VirtualDir ext_dir, |
| 109 | std::shared_ptr<RomFSBuildDirectoryContext> parent) { | 109 | std::shared_ptr<RomFSBuildDirectoryContext> parent) { |
| 110 | std::vector<std::shared_ptr<RomFSBuildDirectoryContext>> child_dirs; | 110 | std::vector<std::shared_ptr<RomFSBuildDirectoryContext>> child_dirs; |
| 111 | 111 | ||
| 112 | VirtualDir dir; | 112 | const auto entries = romfs_dir->GetEntries(); |
| 113 | |||
| 114 | if (parent->path_len == 0) { | ||
| 115 | dir = root_romfs; | ||
| 116 | } else { | ||
| 117 | dir = root_romfs->GetDirectoryRelative(parent->path); | ||
| 118 | } | ||
| 119 | |||
| 120 | const auto entries = dir->GetEntries(); | ||
| 121 | 113 | ||
| 122 | for (const auto& kv : entries) { | 114 | for (const auto& kv : entries) { |
| 123 | if (kv.second == VfsEntryType::Directory) { | 115 | if (kv.second == VfsEntryType::Directory) { |
| @@ -127,7 +119,7 @@ void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs, VirtualDir ext_dir | |||
| 127 | child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size()); | 119 | child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size()); |
| 128 | child->path = parent->path + "/" + kv.first; | 120 | child->path = parent->path + "/" + kv.first; |
| 129 | 121 | ||
| 130 | if (ext_dir != nullptr && ext_dir->GetFileRelative(child->path + ".stub") != nullptr) { | 122 | if (ext_dir != nullptr && ext_dir->GetFile(kv.first + ".stub") != nullptr) { |
| 131 | continue; | 123 | continue; |
| 132 | } | 124 | } |
| 133 | 125 | ||
| @@ -144,17 +136,17 @@ void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs, VirtualDir ext_dir | |||
| 144 | child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size()); | 136 | child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size()); |
| 145 | child->path = parent->path + "/" + kv.first; | 137 | child->path = parent->path + "/" + kv.first; |
| 146 | 138 | ||
| 147 | if (ext_dir != nullptr && ext_dir->GetFileRelative(child->path + ".stub") != nullptr) { | 139 | if (ext_dir != nullptr && ext_dir->GetFile(kv.first + ".stub") != nullptr) { |
| 148 | continue; | 140 | continue; |
| 149 | } | 141 | } |
| 150 | 142 | ||
| 151 | // Sanity check on path_len | 143 | // Sanity check on path_len |
| 152 | ASSERT(child->path_len < FS_MAX_PATH); | 144 | ASSERT(child->path_len < FS_MAX_PATH); |
| 153 | 145 | ||
| 154 | child->source = root_romfs->GetFileRelative(child->path); | 146 | child->source = romfs_dir->GetFile(kv.first); |
| 155 | 147 | ||
| 156 | if (ext_dir != nullptr) { | 148 | if (ext_dir != nullptr) { |
| 157 | if (const auto ips = ext_dir->GetFileRelative(child->path + ".ips")) { | 149 | if (const auto ips = ext_dir->GetFile(kv.first + ".ips")) { |
| 158 | if (auto patched = PatchIPS(child->source, ips)) { | 150 | if (auto patched = PatchIPS(child->source, ips)) { |
| 159 | child->source = std::move(patched); | 151 | child->source = std::move(patched); |
| 160 | } | 152 | } |
| @@ -168,23 +160,27 @@ void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs, VirtualDir ext_dir | |||
| 168 | } | 160 | } |
| 169 | 161 | ||
| 170 | for (auto& child : child_dirs) { | 162 | for (auto& child : child_dirs) { |
| 171 | this->VisitDirectory(root_romfs, ext_dir, child); | 163 | auto subdir_name = std::string_view(child->path).substr(child->cur_path_ofs); |
| 164 | auto child_romfs_dir = romfs_dir->GetSubdirectory(subdir_name); | ||
| 165 | auto child_ext_dir = ext_dir != nullptr ? ext_dir->GetSubdirectory(subdir_name) : nullptr; | ||
| 166 | this->VisitDirectory(child_romfs_dir, child_ext_dir, child); | ||
| 172 | } | 167 | } |
| 173 | } | 168 | } |
| 174 | 169 | ||
| 175 | bool RomFSBuildContext::AddDirectory(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx, | 170 | bool RomFSBuildContext::AddDirectory(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx, |
| 176 | std::shared_ptr<RomFSBuildDirectoryContext> dir_ctx) { | 171 | std::shared_ptr<RomFSBuildDirectoryContext> dir_ctx) { |
| 177 | // Check whether it's already in the known directories. | 172 | // Check whether it's already in the known directories. |
| 178 | const auto existing = directories.find(dir_ctx->path); | 173 | const auto [it, is_new] = directories.emplace(dir_ctx->path, nullptr); |
| 179 | if (existing != directories.end()) | 174 | if (!is_new) { |
| 180 | return false; | 175 | return false; |
| 176 | } | ||
| 181 | 177 | ||
| 182 | // Add a new directory. | 178 | // Add a new directory. |
| 183 | num_dirs++; | 179 | num_dirs++; |
| 184 | dir_table_size += | 180 | dir_table_size += |
| 185 | sizeof(RomFSDirectoryEntry) + Common::AlignUp(dir_ctx->path_len - dir_ctx->cur_path_ofs, 4); | 181 | sizeof(RomFSDirectoryEntry) + Common::AlignUp(dir_ctx->path_len - dir_ctx->cur_path_ofs, 4); |
| 186 | dir_ctx->parent = parent_dir_ctx; | 182 | dir_ctx->parent = parent_dir_ctx; |
| 187 | directories.emplace(dir_ctx->path, dir_ctx); | 183 | it->second = dir_ctx; |
| 188 | 184 | ||
| 189 | return true; | 185 | return true; |
| 190 | } | 186 | } |
| @@ -192,8 +188,8 @@ bool RomFSBuildContext::AddDirectory(std::shared_ptr<RomFSBuildDirectoryContext> | |||
| 192 | bool RomFSBuildContext::AddFile(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx, | 188 | bool RomFSBuildContext::AddFile(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx, |
| 193 | std::shared_ptr<RomFSBuildFileContext> file_ctx) { | 189 | std::shared_ptr<RomFSBuildFileContext> file_ctx) { |
| 194 | // Check whether it's already in the known files. | 190 | // Check whether it's already in the known files. |
| 195 | const auto existing = files.find(file_ctx->path); | 191 | const auto [it, is_new] = files.emplace(file_ctx->path, nullptr); |
| 196 | if (existing != files.end()) { | 192 | if (!is_new) { |
| 197 | return false; | 193 | return false; |
| 198 | } | 194 | } |
| 199 | 195 | ||
| @@ -202,7 +198,7 @@ bool RomFSBuildContext::AddFile(std::shared_ptr<RomFSBuildDirectoryContext> pare | |||
| 202 | file_table_size += | 198 | file_table_size += |
| 203 | sizeof(RomFSFileEntry) + Common::AlignUp(file_ctx->path_len - file_ctx->cur_path_ofs, 4); | 199 | sizeof(RomFSFileEntry) + Common::AlignUp(file_ctx->path_len - file_ctx->cur_path_ofs, 4); |
| 204 | file_ctx->parent = parent_dir_ctx; | 200 | file_ctx->parent = parent_dir_ctx; |
| 205 | files.emplace(file_ctx->path, file_ctx); | 201 | it->second = file_ctx; |
| 206 | 202 | ||
| 207 | return true; | 203 | return true; |
| 208 | } | 204 | } |
diff --git a/src/core/gpu_dirty_memory_manager.h b/src/core/gpu_dirty_memory_manager.h new file mode 100644 index 000000000..9687531e8 --- /dev/null +++ b/src/core/gpu_dirty_memory_manager.h | |||
| @@ -0,0 +1,122 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <atomic> | ||
| 7 | #include <bit> | ||
| 8 | #include <functional> | ||
| 9 | #include <mutex> | ||
| 10 | #include <utility> | ||
| 11 | #include <vector> | ||
| 12 | |||
| 13 | #include "core/memory.h" | ||
| 14 | |||
| 15 | namespace Core { | ||
| 16 | |||
| 17 | class GPUDirtyMemoryManager { | ||
| 18 | public: | ||
| 19 | GPUDirtyMemoryManager() : current{default_transform} { | ||
| 20 | back_buffer.reserve(256); | ||
| 21 | front_buffer.reserve(256); | ||
| 22 | } | ||
| 23 | |||
| 24 | ~GPUDirtyMemoryManager() = default; | ||
| 25 | |||
| 26 | void Collect(VAddr address, size_t size) { | ||
| 27 | TransformAddress t = BuildTransform(address, size); | ||
| 28 | TransformAddress tmp, original; | ||
| 29 | do { | ||
| 30 | tmp = current.load(std::memory_order_acquire); | ||
| 31 | original = tmp; | ||
| 32 | if (tmp.address != t.address) { | ||
| 33 | if (IsValid(tmp.address)) { | ||
| 34 | std::scoped_lock lk(guard); | ||
| 35 | back_buffer.emplace_back(tmp); | ||
| 36 | current.exchange(t, std::memory_order_relaxed); | ||
| 37 | return; | ||
| 38 | } | ||
| 39 | tmp.address = t.address; | ||
| 40 | tmp.mask = 0; | ||
| 41 | } | ||
| 42 | if ((tmp.mask | t.mask) == tmp.mask) { | ||
| 43 | return; | ||
| 44 | } | ||
| 45 | tmp.mask |= t.mask; | ||
| 46 | } while (!current.compare_exchange_weak(original, tmp, std::memory_order_release, | ||
| 47 | std::memory_order_relaxed)); | ||
| 48 | } | ||
| 49 | |||
| 50 | void Gather(std::function<void(VAddr, size_t)>& callback) { | ||
| 51 | { | ||
| 52 | std::scoped_lock lk(guard); | ||
| 53 | TransformAddress t = current.exchange(default_transform, std::memory_order_relaxed); | ||
| 54 | front_buffer.swap(back_buffer); | ||
| 55 | if (IsValid(t.address)) { | ||
| 56 | front_buffer.emplace_back(t); | ||
| 57 | } | ||
| 58 | } | ||
| 59 | for (auto& transform : front_buffer) { | ||
| 60 | size_t offset = 0; | ||
| 61 | u64 mask = transform.mask; | ||
| 62 | while (mask != 0) { | ||
| 63 | const size_t empty_bits = std::countr_zero(mask); | ||
| 64 | offset += empty_bits << align_bits; | ||
| 65 | mask = mask >> empty_bits; | ||
| 66 | |||
| 67 | const size_t continuous_bits = std::countr_one(mask); | ||
| 68 | callback((static_cast<VAddr>(transform.address) << page_bits) + offset, | ||
| 69 | continuous_bits << align_bits); | ||
| 70 | mask = continuous_bits < align_size ? (mask >> continuous_bits) : 0; | ||
| 71 | offset += continuous_bits << align_bits; | ||
| 72 | } | ||
| 73 | } | ||
| 74 | front_buffer.clear(); | ||
| 75 | } | ||
| 76 | |||
| 77 | private: | ||
| 78 | struct alignas(8) TransformAddress { | ||
| 79 | u32 address; | ||
| 80 | u32 mask; | ||
| 81 | }; | ||
| 82 | |||
| 83 | constexpr static size_t page_bits = Memory::YUZU_PAGEBITS - 1; | ||
| 84 | constexpr static size_t page_size = 1ULL << page_bits; | ||
| 85 | constexpr static size_t page_mask = page_size - 1; | ||
| 86 | |||
| 87 | constexpr static size_t align_bits = 6U; | ||
| 88 | constexpr static size_t align_size = 1U << align_bits; | ||
| 89 | constexpr static size_t align_mask = align_size - 1; | ||
| 90 | constexpr static TransformAddress default_transform = {.address = ~0U, .mask = 0U}; | ||
| 91 | |||
| 92 | bool IsValid(VAddr address) { | ||
| 93 | return address < (1ULL << 39); | ||
| 94 | } | ||
| 95 | |||
| 96 | template <typename T> | ||
| 97 | T CreateMask(size_t top_bit, size_t minor_bit) { | ||
| 98 | T mask = ~T(0); | ||
| 99 | mask <<= (sizeof(T) * 8 - top_bit); | ||
| 100 | mask >>= (sizeof(T) * 8 - top_bit); | ||
| 101 | mask >>= minor_bit; | ||
| 102 | mask <<= minor_bit; | ||
| 103 | return mask; | ||
| 104 | } | ||
| 105 | |||
| 106 | TransformAddress BuildTransform(VAddr address, size_t size) { | ||
| 107 | const size_t minor_address = address & page_mask; | ||
| 108 | const size_t minor_bit = minor_address >> align_bits; | ||
| 109 | const size_t top_bit = (minor_address + size + align_mask) >> align_bits; | ||
| 110 | TransformAddress result{}; | ||
| 111 | result.address = static_cast<u32>(address >> page_bits); | ||
| 112 | result.mask = CreateMask<u32>(top_bit, minor_bit); | ||
| 113 | return result; | ||
| 114 | } | ||
| 115 | |||
| 116 | std::atomic<TransformAddress> current{}; | ||
| 117 | std::mutex guard; | ||
| 118 | std::vector<TransformAddress> back_buffer; | ||
| 119 | std::vector<TransformAddress> front_buffer; | ||
| 120 | }; | ||
| 121 | |||
| 122 | } // namespace Core | ||
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h index dd662b3f8..d178c2453 100644 --- a/src/core/hle/kernel/k_thread.h +++ b/src/core/hle/kernel/k_thread.h | |||
| @@ -338,6 +338,15 @@ public: | |||
| 338 | return m_parent != nullptr; | 338 | return m_parent != nullptr; |
| 339 | } | 339 | } |
| 340 | 340 | ||
| 341 | std::span<KSynchronizationObject*> GetSynchronizationObjectBuffer() { | ||
| 342 | return m_sync_object_buffer.sync_objects; | ||
| 343 | } | ||
| 344 | |||
| 345 | std::span<Handle> GetHandleBuffer() { | ||
| 346 | return {m_sync_object_buffer.handles.data() + Svc::ArgumentHandleCountMax, | ||
| 347 | Svc::ArgumentHandleCountMax}; | ||
| 348 | } | ||
| 349 | |||
| 341 | u16 GetUserDisableCount() const; | 350 | u16 GetUserDisableCount() const; |
| 342 | void SetInterruptFlag(); | 351 | void SetInterruptFlag(); |
| 343 | void ClearInterruptFlag(); | 352 | void ClearInterruptFlag(); |
| @@ -855,6 +864,7 @@ private: | |||
| 855 | u32* m_light_ipc_data{}; | 864 | u32* m_light_ipc_data{}; |
| 856 | KProcessAddress m_tls_address{}; | 865 | KProcessAddress m_tls_address{}; |
| 857 | KLightLock m_activity_pause_lock; | 866 | KLightLock m_activity_pause_lock; |
| 867 | SyncObjectBuffer m_sync_object_buffer{}; | ||
| 858 | s64 m_schedule_count{}; | 868 | s64 m_schedule_count{}; |
| 859 | s64 m_last_scheduled_tick{}; | 869 | s64 m_last_scheduled_tick{}; |
| 860 | std::array<QueueEntry, Core::Hardware::NUM_CPU_CORES> m_per_core_priority_queue_entry{}; | 870 | std::array<QueueEntry, Core::Hardware::NUM_CPU_CORES> m_per_core_priority_queue_entry{}; |
diff --git a/src/core/hle/kernel/svc/svc_ipc.cpp b/src/core/hle/kernel/svc/svc_ipc.cpp index 60247df2e..bb94f6934 100644 --- a/src/core/hle/kernel/svc/svc_ipc.cpp +++ b/src/core/hle/kernel/svc/svc_ipc.cpp | |||
| @@ -38,22 +38,31 @@ Result SendAsyncRequestWithUserBuffer(Core::System& system, Handle* out_event_ha | |||
| 38 | 38 | ||
| 39 | Result ReplyAndReceive(Core::System& system, s32* out_index, uint64_t handles_addr, s32 num_handles, | 39 | Result ReplyAndReceive(Core::System& system, s32* out_index, uint64_t handles_addr, s32 num_handles, |
| 40 | Handle reply_target, s64 timeout_ns) { | 40 | Handle reply_target, s64 timeout_ns) { |
| 41 | // Ensure number of handles is valid. | ||
| 42 | R_UNLESS(0 <= num_handles && num_handles <= ArgumentHandleCountMax, ResultOutOfRange); | ||
| 43 | |||
| 44 | // Get the synchronization context. | ||
| 41 | auto& kernel = system.Kernel(); | 45 | auto& kernel = system.Kernel(); |
| 42 | auto& handle_table = GetCurrentProcess(kernel).GetHandleTable(); | 46 | auto& handle_table = GetCurrentProcess(kernel).GetHandleTable(); |
| 43 | 47 | auto objs = GetCurrentThread(kernel).GetSynchronizationObjectBuffer(); | |
| 44 | R_UNLESS(0 <= num_handles && num_handles <= ArgumentHandleCountMax, ResultOutOfRange); | 48 | auto handles = GetCurrentThread(kernel).GetHandleBuffer(); |
| 45 | R_UNLESS(GetCurrentMemory(kernel).IsValidVirtualAddressRange( | 49 | |
| 46 | handles_addr, static_cast<u64>(sizeof(Handle) * num_handles)), | 50 | // Copy user handles. |
| 47 | ResultInvalidPointer); | 51 | if (num_handles > 0) { |
| 48 | 52 | // Ensure we can try to get the handles. | |
| 49 | std::array<Handle, Svc::ArgumentHandleCountMax> handles; | 53 | R_UNLESS(GetCurrentMemory(kernel).IsValidVirtualAddressRange( |
| 50 | GetCurrentMemory(kernel).ReadBlock(handles_addr, handles.data(), sizeof(Handle) * num_handles); | 54 | handles_addr, static_cast<u64>(sizeof(Handle) * num_handles)), |
| 51 | 55 | ResultInvalidPointer); | |
| 52 | // Convert handle list to object table. | 56 | |
| 53 | std::array<KSynchronizationObject*, Svc::ArgumentHandleCountMax> objs; | 57 | // Get the handles. |
| 54 | R_UNLESS(handle_table.GetMultipleObjects<KSynchronizationObject>(objs.data(), handles.data(), | 58 | GetCurrentMemory(kernel).ReadBlock(handles_addr, handles.data(), |
| 55 | num_handles), | 59 | sizeof(Handle) * num_handles); |
| 56 | ResultInvalidHandle); | 60 | |
| 61 | // Convert the handles to objects. | ||
| 62 | R_UNLESS(handle_table.GetMultipleObjects<KSynchronizationObject>( | ||
| 63 | objs.data(), handles.data(), num_handles), | ||
| 64 | ResultInvalidHandle); | ||
| 65 | } | ||
| 57 | 66 | ||
| 58 | // Ensure handles are closed when we're done. | 67 | // Ensure handles are closed when we're done. |
| 59 | SCOPE_EXIT({ | 68 | SCOPE_EXIT({ |
diff --git a/src/core/hle/kernel/svc/svc_synchronization.cpp b/src/core/hle/kernel/svc/svc_synchronization.cpp index 53df5bcd8..f02d03f30 100644 --- a/src/core/hle/kernel/svc/svc_synchronization.cpp +++ b/src/core/hle/kernel/svc/svc_synchronization.cpp | |||
| @@ -47,21 +47,35 @@ Result ResetSignal(Core::System& system, Handle handle) { | |||
| 47 | R_THROW(ResultInvalidHandle); | 47 | R_THROW(ResultInvalidHandle); |
| 48 | } | 48 | } |
| 49 | 49 | ||
| 50 | static Result WaitSynchronization(Core::System& system, int32_t* out_index, const Handle* handles, | 50 | /// Wait for the given handles to synchronize, timeout after the specified nanoseconds |
| 51 | int32_t num_handles, int64_t timeout_ns) { | 51 | Result WaitSynchronization(Core::System& system, int32_t* out_index, u64 user_handles, |
| 52 | int32_t num_handles, int64_t timeout_ns) { | ||
| 53 | LOG_TRACE(Kernel_SVC, "called user_handles={:#x}, num_handles={}, timeout_ns={}", user_handles, | ||
| 54 | num_handles, timeout_ns); | ||
| 55 | |||
| 52 | // Ensure number of handles is valid. | 56 | // Ensure number of handles is valid. |
| 53 | R_UNLESS(0 <= num_handles && num_handles <= Svc::ArgumentHandleCountMax, ResultOutOfRange); | 57 | R_UNLESS(0 <= num_handles && num_handles <= Svc::ArgumentHandleCountMax, ResultOutOfRange); |
| 54 | 58 | ||
| 55 | // Get the synchronization context. | 59 | // Get the synchronization context. |
| 56 | auto& kernel = system.Kernel(); | 60 | auto& kernel = system.Kernel(); |
| 57 | auto& handle_table = GetCurrentProcess(kernel).GetHandleTable(); | 61 | auto& handle_table = GetCurrentProcess(kernel).GetHandleTable(); |
| 58 | std::array<KSynchronizationObject*, Svc::ArgumentHandleCountMax> objs; | 62 | auto objs = GetCurrentThread(kernel).GetSynchronizationObjectBuffer(); |
| 63 | auto handles = GetCurrentThread(kernel).GetHandleBuffer(); | ||
| 59 | 64 | ||
| 60 | // Copy user handles. | 65 | // Copy user handles. |
| 61 | if (num_handles > 0) { | 66 | if (num_handles > 0) { |
| 67 | // Ensure we can try to get the handles. | ||
| 68 | R_UNLESS(GetCurrentMemory(kernel).IsValidVirtualAddressRange( | ||
| 69 | user_handles, static_cast<u64>(sizeof(Handle) * num_handles)), | ||
| 70 | ResultInvalidPointer); | ||
| 71 | |||
| 72 | // Get the handles. | ||
| 73 | GetCurrentMemory(kernel).ReadBlock(user_handles, handles.data(), | ||
| 74 | sizeof(Handle) * num_handles); | ||
| 75 | |||
| 62 | // Convert the handles to objects. | 76 | // Convert the handles to objects. |
| 63 | R_UNLESS(handle_table.GetMultipleObjects<KSynchronizationObject>(objs.data(), handles, | 77 | R_UNLESS(handle_table.GetMultipleObjects<KSynchronizationObject>( |
| 64 | num_handles), | 78 | objs.data(), handles.data(), num_handles), |
| 65 | ResultInvalidHandle); | 79 | ResultInvalidHandle); |
| 66 | } | 80 | } |
| 67 | 81 | ||
| @@ -80,23 +94,6 @@ static Result WaitSynchronization(Core::System& system, int32_t* out_index, cons | |||
| 80 | R_RETURN(res); | 94 | R_RETURN(res); |
| 81 | } | 95 | } |
| 82 | 96 | ||
| 83 | /// Wait for the given handles to synchronize, timeout after the specified nanoseconds | ||
| 84 | Result WaitSynchronization(Core::System& system, int32_t* out_index, u64 user_handles, | ||
| 85 | int32_t num_handles, int64_t timeout_ns) { | ||
| 86 | LOG_TRACE(Kernel_SVC, "called user_handles={:#x}, num_handles={}, timeout_ns={}", user_handles, | ||
| 87 | num_handles, timeout_ns); | ||
| 88 | |||
| 89 | // Ensure number of handles is valid. | ||
| 90 | R_UNLESS(0 <= num_handles && num_handles <= Svc::ArgumentHandleCountMax, ResultOutOfRange); | ||
| 91 | std::array<Handle, Svc::ArgumentHandleCountMax> handles; | ||
| 92 | if (num_handles > 0) { | ||
| 93 | GetCurrentMemory(system.Kernel()) | ||
| 94 | .ReadBlock(user_handles, handles.data(), num_handles * sizeof(Handle)); | ||
| 95 | } | ||
| 96 | |||
| 97 | R_RETURN(WaitSynchronization(system, out_index, handles.data(), num_handles, timeout_ns)); | ||
| 98 | } | ||
| 99 | |||
| 100 | /// Resumes a thread waiting on WaitSynchronization | 97 | /// Resumes a thread waiting on WaitSynchronization |
| 101 | Result CancelSynchronization(Core::System& system, Handle handle) { | 98 | Result CancelSynchronization(Core::System& system, Handle handle) { |
| 102 | LOG_TRACE(Kernel_SVC, "called handle=0x{:X}", handle); | 99 | LOG_TRACE(Kernel_SVC, "called handle=0x{:X}", handle); |
diff --git a/src/core/hle/service/audio/audin_u.cpp b/src/core/hle/service/audio/audin_u.cpp index c8d574993..526a39130 100644 --- a/src/core/hle/service/audio/audin_u.cpp +++ b/src/core/hle/service/audio/audin_u.cpp | |||
| @@ -5,7 +5,7 @@ | |||
| 5 | #include "audio_core/renderer/audio_device.h" | 5 | #include "audio_core/renderer/audio_device.h" |
| 6 | #include "common/common_funcs.h" | 6 | #include "common/common_funcs.h" |
| 7 | #include "common/logging/log.h" | 7 | #include "common/logging/log.h" |
| 8 | #include "common/settings.h" | 8 | #include "common/scratch_buffer.h" |
| 9 | #include "common/string_util.h" | 9 | #include "common/string_util.h" |
| 10 | #include "core/core.h" | 10 | #include "core/core.h" |
| 11 | #include "core/hle/kernel/k_event.h" | 11 | #include "core/hle/kernel/k_event.h" |
| @@ -124,12 +124,15 @@ private: | |||
| 124 | 124 | ||
| 125 | void GetReleasedAudioInBuffer(HLERequestContext& ctx) { | 125 | void GetReleasedAudioInBuffer(HLERequestContext& ctx) { |
| 126 | const auto write_buffer_size = ctx.GetWriteBufferNumElements<u64>(); | 126 | const auto write_buffer_size = ctx.GetWriteBufferNumElements<u64>(); |
| 127 | tmp_buffer.resize_destructive(write_buffer_size); | 127 | released_buffer.resize_destructive(write_buffer_size); |
| 128 | tmp_buffer[0] = 0; | 128 | released_buffer[0] = 0; |
| 129 | 129 | ||
| 130 | const auto count = impl->GetReleasedBuffers(tmp_buffer); | 130 | const auto count = impl->GetReleasedBuffers(released_buffer); |
| 131 | 131 | ||
| 132 | ctx.WriteBuffer(tmp_buffer); | 132 | LOG_TRACE(Service_Audio, "called. Session {} released {} buffers", |
| 133 | impl->GetSystem().GetSessionId(), count); | ||
| 134 | |||
| 135 | ctx.WriteBuffer(released_buffer); | ||
| 133 | 136 | ||
| 134 | IPC::ResponseBuilder rb{ctx, 3}; | 137 | IPC::ResponseBuilder rb{ctx, 3}; |
| 135 | rb.Push(ResultSuccess); | 138 | rb.Push(ResultSuccess); |
| @@ -155,7 +158,6 @@ private: | |||
| 155 | LOG_DEBUG(Service_Audio, "called. Buffer count={}", buffer_count); | 158 | LOG_DEBUG(Service_Audio, "called. Buffer count={}", buffer_count); |
| 156 | 159 | ||
| 157 | IPC::ResponseBuilder rb{ctx, 3}; | 160 | IPC::ResponseBuilder rb{ctx, 3}; |
| 158 | |||
| 159 | rb.Push(ResultSuccess); | 161 | rb.Push(ResultSuccess); |
| 160 | rb.Push(buffer_count); | 162 | rb.Push(buffer_count); |
| 161 | } | 163 | } |
| @@ -195,7 +197,7 @@ private: | |||
| 195 | KernelHelpers::ServiceContext service_context; | 197 | KernelHelpers::ServiceContext service_context; |
| 196 | Kernel::KEvent* event; | 198 | Kernel::KEvent* event; |
| 197 | std::shared_ptr<AudioCore::AudioIn::In> impl; | 199 | std::shared_ptr<AudioCore::AudioIn::In> impl; |
| 198 | Common::ScratchBuffer<u64> tmp_buffer; | 200 | Common::ScratchBuffer<u64> released_buffer; |
| 199 | }; | 201 | }; |
| 200 | 202 | ||
| 201 | AudInU::AudInU(Core::System& system_) | 203 | AudInU::AudInU(Core::System& system_) |
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index 032c8c11f..23f84a29f 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include "audio_core/renderer/audio_device.h" | 9 | #include "audio_core/renderer/audio_device.h" |
| 10 | #include "common/common_funcs.h" | 10 | #include "common/common_funcs.h" |
| 11 | #include "common/logging/log.h" | 11 | #include "common/logging/log.h" |
| 12 | #include "common/scratch_buffer.h" | ||
| 12 | #include "common/string_util.h" | 13 | #include "common/string_util.h" |
| 13 | #include "common/swap.h" | 14 | #include "common/swap.h" |
| 14 | #include "core/core.h" | 15 | #include "core/core.h" |
| @@ -102,8 +103,8 @@ private: | |||
| 102 | AudioOutBuffer buffer{}; | 103 | AudioOutBuffer buffer{}; |
| 103 | std::memcpy(&buffer, in_buffer.data(), sizeof(AudioOutBuffer)); | 104 | std::memcpy(&buffer, in_buffer.data(), sizeof(AudioOutBuffer)); |
| 104 | 105 | ||
| 105 | [[maybe_unused]] auto sessionid{impl->GetSystem().GetSessionId()}; | 106 | LOG_TRACE(Service_Audio, "called. Session {} Appending buffer {:08X}", |
| 106 | LOG_TRACE(Service_Audio, "called. Session {} Appending buffer {:08X}", sessionid, tag); | 107 | impl->GetSystem().GetSessionId(), tag); |
| 107 | 108 | ||
| 108 | auto result = impl->AppendBuffer(buffer, tag); | 109 | auto result = impl->AppendBuffer(buffer, tag); |
| 109 | 110 | ||
| @@ -123,12 +124,15 @@ private: | |||
| 123 | 124 | ||
| 124 | void GetReleasedAudioOutBuffers(HLERequestContext& ctx) { | 125 | void GetReleasedAudioOutBuffers(HLERequestContext& ctx) { |
| 125 | const auto write_buffer_size = ctx.GetWriteBufferNumElements<u64>(); | 126 | const auto write_buffer_size = ctx.GetWriteBufferNumElements<u64>(); |
| 126 | tmp_buffer.resize_destructive(write_buffer_size); | 127 | released_buffer.resize_destructive(write_buffer_size); |
| 127 | tmp_buffer[0] = 0; | 128 | released_buffer[0] = 0; |
| 128 | 129 | ||
| 129 | const auto count = impl->GetReleasedBuffers(tmp_buffer); | 130 | const auto count = impl->GetReleasedBuffers(released_buffer); |
| 130 | 131 | ||
| 131 | ctx.WriteBuffer(tmp_buffer); | 132 | ctx.WriteBuffer(released_buffer); |
| 133 | |||
| 134 | LOG_TRACE(Service_Audio, "called. Session {} released {} buffers", | ||
| 135 | impl->GetSystem().GetSessionId(), count); | ||
| 132 | 136 | ||
| 133 | IPC::ResponseBuilder rb{ctx, 3}; | 137 | IPC::ResponseBuilder rb{ctx, 3}; |
| 134 | rb.Push(ResultSuccess); | 138 | rb.Push(ResultSuccess); |
| @@ -154,7 +158,6 @@ private: | |||
| 154 | LOG_DEBUG(Service_Audio, "called. Buffer count={}", buffer_count); | 158 | LOG_DEBUG(Service_Audio, "called. Buffer count={}", buffer_count); |
| 155 | 159 | ||
| 156 | IPC::ResponseBuilder rb{ctx, 3}; | 160 | IPC::ResponseBuilder rb{ctx, 3}; |
| 157 | |||
| 158 | rb.Push(ResultSuccess); | 161 | rb.Push(ResultSuccess); |
| 159 | rb.Push(buffer_count); | 162 | rb.Push(buffer_count); |
| 160 | } | 163 | } |
| @@ -165,7 +168,6 @@ private: | |||
| 165 | LOG_DEBUG(Service_Audio, "called. Played samples={}", samples_played); | 168 | LOG_DEBUG(Service_Audio, "called. Played samples={}", samples_played); |
| 166 | 169 | ||
| 167 | IPC::ResponseBuilder rb{ctx, 4}; | 170 | IPC::ResponseBuilder rb{ctx, 4}; |
| 168 | |||
| 169 | rb.Push(ResultSuccess); | 171 | rb.Push(ResultSuccess); |
| 170 | rb.Push(samples_played); | 172 | rb.Push(samples_played); |
| 171 | } | 173 | } |
| @@ -205,7 +207,7 @@ private: | |||
| 205 | KernelHelpers::ServiceContext service_context; | 207 | KernelHelpers::ServiceContext service_context; |
| 206 | Kernel::KEvent* event; | 208 | Kernel::KEvent* event; |
| 207 | std::shared_ptr<AudioCore::AudioOut::Out> impl; | 209 | std::shared_ptr<AudioCore::AudioOut::Out> impl; |
| 208 | Common::ScratchBuffer<u64> tmp_buffer; | 210 | Common::ScratchBuffer<u64> released_buffer; |
| 209 | }; | 211 | }; |
| 210 | 212 | ||
| 211 | AudOutU::AudOutU(Core::System& system_) | 213 | AudOutU::AudOutU(Core::System& system_) |
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index 12845c23a..003870176 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp | |||
| @@ -15,6 +15,7 @@ | |||
| 15 | #include "common/common_funcs.h" | 15 | #include "common/common_funcs.h" |
| 16 | #include "common/logging/log.h" | 16 | #include "common/logging/log.h" |
| 17 | #include "common/polyfill_ranges.h" | 17 | #include "common/polyfill_ranges.h" |
| 18 | #include "common/scratch_buffer.h" | ||
| 18 | #include "common/string_util.h" | 19 | #include "common/string_util.h" |
| 19 | #include "core/core.h" | 20 | #include "core/core.h" |
| 20 | #include "core/hle/kernel/k_event.h" | 21 | #include "core/hle/kernel/k_event.h" |
| @@ -119,23 +120,23 @@ private: | |||
| 119 | auto is_buffer_b{ctx.BufferDescriptorB()[0].Size() != 0}; | 120 | auto is_buffer_b{ctx.BufferDescriptorB()[0].Size() != 0}; |
| 120 | if (is_buffer_b) { | 121 | if (is_buffer_b) { |
| 121 | const auto buffersB{ctx.BufferDescriptorB()}; | 122 | const auto buffersB{ctx.BufferDescriptorB()}; |
| 122 | tmp_output.resize_destructive(buffersB[0].Size()); | 123 | output_buffer.resize_destructive(buffersB[0].Size()); |
| 123 | tmp_performance.resize_destructive(buffersB[1].Size()); | 124 | performance_buffer.resize_destructive(buffersB[1].Size()); |
| 124 | } else { | 125 | } else { |
| 125 | const auto buffersC{ctx.BufferDescriptorC()}; | 126 | const auto buffersC{ctx.BufferDescriptorC()}; |
| 126 | tmp_output.resize_destructive(buffersC[0].Size()); | 127 | output_buffer.resize_destructive(buffersC[0].Size()); |
| 127 | tmp_performance.resize_destructive(buffersC[1].Size()); | 128 | performance_buffer.resize_destructive(buffersC[1].Size()); |
| 128 | } | 129 | } |
| 129 | 130 | ||
| 130 | auto result = impl->RequestUpdate(input, tmp_performance, tmp_output); | 131 | auto result = impl->RequestUpdate(input, performance_buffer, output_buffer); |
| 131 | 132 | ||
| 132 | if (result.IsSuccess()) { | 133 | if (result.IsSuccess()) { |
| 133 | if (is_buffer_b) { | 134 | if (is_buffer_b) { |
| 134 | ctx.WriteBufferB(tmp_output.data(), tmp_output.size(), 0); | 135 | ctx.WriteBufferB(output_buffer.data(), output_buffer.size(), 0); |
| 135 | ctx.WriteBufferB(tmp_performance.data(), tmp_performance.size(), 1); | 136 | ctx.WriteBufferB(performance_buffer.data(), performance_buffer.size(), 1); |
| 136 | } else { | 137 | } else { |
| 137 | ctx.WriteBufferC(tmp_output.data(), tmp_output.size(), 0); | 138 | ctx.WriteBufferC(output_buffer.data(), output_buffer.size(), 0); |
| 138 | ctx.WriteBufferC(tmp_performance.data(), tmp_performance.size(), 1); | 139 | ctx.WriteBufferC(performance_buffer.data(), performance_buffer.size(), 1); |
| 139 | } | 140 | } |
| 140 | } else { | 141 | } else { |
| 141 | LOG_ERROR(Service_Audio, "RequestUpdate failed error 0x{:02X}!", result.description); | 142 | LOG_ERROR(Service_Audio, "RequestUpdate failed error 0x{:02X}!", result.description); |
| @@ -233,8 +234,8 @@ private: | |||
| 233 | Kernel::KEvent* rendered_event; | 234 | Kernel::KEvent* rendered_event; |
| 234 | Manager& manager; | 235 | Manager& manager; |
| 235 | std::unique_ptr<Renderer> impl; | 236 | std::unique_ptr<Renderer> impl; |
| 236 | Common::ScratchBuffer<u8> tmp_output; | 237 | Common::ScratchBuffer<u8> output_buffer; |
| 237 | Common::ScratchBuffer<u8> tmp_performance; | 238 | Common::ScratchBuffer<u8> performance_buffer; |
| 238 | }; | 239 | }; |
| 239 | 240 | ||
| 240 | class IAudioDevice final : public ServiceFramework<IAudioDevice> { | 241 | class IAudioDevice final : public ServiceFramework<IAudioDevice> { |
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp index c835f6cb7..fa77007f3 100644 --- a/src/core/hle/service/audio/hwopus.cpp +++ b/src/core/hle/service/audio/hwopus.cpp | |||
| @@ -11,6 +11,7 @@ | |||
| 11 | 11 | ||
| 12 | #include "common/assert.h" | 12 | #include "common/assert.h" |
| 13 | #include "common/logging/log.h" | 13 | #include "common/logging/log.h" |
| 14 | #include "common/scratch_buffer.h" | ||
| 14 | #include "core/hle/service/audio/hwopus.h" | 15 | #include "core/hle/service/audio/hwopus.h" |
| 15 | #include "core/hle/service/ipc_helpers.h" | 16 | #include "core/hle/service/ipc_helpers.h" |
| 16 | 17 | ||
| @@ -68,13 +69,13 @@ private: | |||
| 68 | ExtraBehavior extra_behavior) { | 69 | ExtraBehavior extra_behavior) { |
| 69 | u32 consumed = 0; | 70 | u32 consumed = 0; |
| 70 | u32 sample_count = 0; | 71 | u32 sample_count = 0; |
| 71 | tmp_samples.resize_destructive(ctx.GetWriteBufferNumElements<opus_int16>()); | 72 | samples.resize_destructive(ctx.GetWriteBufferNumElements<opus_int16>()); |
| 72 | 73 | ||
| 73 | if (extra_behavior == ExtraBehavior::ResetContext) { | 74 | if (extra_behavior == ExtraBehavior::ResetContext) { |
| 74 | ResetDecoderContext(); | 75 | ResetDecoderContext(); |
| 75 | } | 76 | } |
| 76 | 77 | ||
| 77 | if (!DecodeOpusData(consumed, sample_count, ctx.ReadBuffer(), tmp_samples, performance)) { | 78 | if (!DecodeOpusData(consumed, sample_count, ctx.ReadBuffer(), samples, performance)) { |
| 78 | LOG_ERROR(Audio, "Failed to decode opus data"); | 79 | LOG_ERROR(Audio, "Failed to decode opus data"); |
| 79 | IPC::ResponseBuilder rb{ctx, 2}; | 80 | IPC::ResponseBuilder rb{ctx, 2}; |
| 80 | // TODO(ogniK): Use correct error code | 81 | // TODO(ogniK): Use correct error code |
| @@ -90,7 +91,7 @@ private: | |||
| 90 | if (performance) { | 91 | if (performance) { |
| 91 | rb.Push<u64>(*performance); | 92 | rb.Push<u64>(*performance); |
| 92 | } | 93 | } |
| 93 | ctx.WriteBuffer(tmp_samples); | 94 | ctx.WriteBuffer(samples); |
| 94 | } | 95 | } |
| 95 | 96 | ||
| 96 | bool DecodeOpusData(u32& consumed, u32& sample_count, std::span<const u8> input, | 97 | bool DecodeOpusData(u32& consumed, u32& sample_count, std::span<const u8> input, |
| @@ -154,7 +155,7 @@ private: | |||
| 154 | OpusDecoderPtr decoder; | 155 | OpusDecoderPtr decoder; |
| 155 | u32 sample_rate; | 156 | u32 sample_rate; |
| 156 | u32 channel_count; | 157 | u32 channel_count; |
| 157 | Common::ScratchBuffer<opus_int16> tmp_samples; | 158 | Common::ScratchBuffer<opus_int16> samples; |
| 158 | }; | 159 | }; |
| 159 | 160 | ||
| 160 | class IHardwareOpusDecoderManager final : public ServiceFramework<IHardwareOpusDecoderManager> { | 161 | class IHardwareOpusDecoderManager final : public ServiceFramework<IHardwareOpusDecoderManager> { |
diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.cpp b/src/core/hle/service/nvdrv/nvdrv_interface.cpp index 348207e25..c8a880e84 100644 --- a/src/core/hle/service/nvdrv/nvdrv_interface.cpp +++ b/src/core/hle/service/nvdrv/nvdrv_interface.cpp | |||
| @@ -2,7 +2,6 @@ | |||
| 2 | // SPDX-FileCopyrightText: 2021 Skyline Team and Contributors | 2 | // SPDX-FileCopyrightText: 2021 Skyline Team and Contributors |
| 3 | // SPDX-License-Identifier: GPL-3.0-or-later | 3 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 4 | 4 | ||
| 5 | #include <cinttypes> | ||
| 6 | #include "common/logging/log.h" | 5 | #include "common/logging/log.h" |
| 7 | #include "core/core.h" | 6 | #include "core/core.h" |
| 8 | #include "core/hle/kernel/k_event.h" | 7 | #include "core/hle/kernel/k_event.h" |
| @@ -63,12 +62,12 @@ void NVDRV::Ioctl1(HLERequestContext& ctx) { | |||
| 63 | } | 62 | } |
| 64 | 63 | ||
| 65 | // Check device | 64 | // Check device |
| 66 | tmp_output.resize_destructive(ctx.GetWriteBufferSize(0)); | 65 | output_buffer.resize_destructive(ctx.GetWriteBufferSize(0)); |
| 67 | const auto input_buffer = ctx.ReadBuffer(0); | 66 | const auto input_buffer = ctx.ReadBuffer(0); |
| 68 | 67 | ||
| 69 | const auto nv_result = nvdrv->Ioctl1(fd, command, input_buffer, tmp_output); | 68 | const auto nv_result = nvdrv->Ioctl1(fd, command, input_buffer, output_buffer); |
| 70 | if (command.is_out != 0) { | 69 | if (command.is_out != 0) { |
| 71 | ctx.WriteBuffer(tmp_output); | 70 | ctx.WriteBuffer(output_buffer); |
| 72 | } | 71 | } |
| 73 | 72 | ||
| 74 | IPC::ResponseBuilder rb{ctx, 3}; | 73 | IPC::ResponseBuilder rb{ctx, 3}; |
| @@ -90,12 +89,12 @@ void NVDRV::Ioctl2(HLERequestContext& ctx) { | |||
| 90 | 89 | ||
| 91 | const auto input_buffer = ctx.ReadBuffer(0); | 90 | const auto input_buffer = ctx.ReadBuffer(0); |
| 92 | const auto input_inlined_buffer = ctx.ReadBuffer(1); | 91 | const auto input_inlined_buffer = ctx.ReadBuffer(1); |
| 93 | tmp_output.resize_destructive(ctx.GetWriteBufferSize(0)); | 92 | output_buffer.resize_destructive(ctx.GetWriteBufferSize(0)); |
| 94 | 93 | ||
| 95 | const auto nv_result = | 94 | const auto nv_result = |
| 96 | nvdrv->Ioctl2(fd, command, input_buffer, input_inlined_buffer, tmp_output); | 95 | nvdrv->Ioctl2(fd, command, input_buffer, input_inlined_buffer, output_buffer); |
| 97 | if (command.is_out != 0) { | 96 | if (command.is_out != 0) { |
| 98 | ctx.WriteBuffer(tmp_output); | 97 | ctx.WriteBuffer(output_buffer); |
| 99 | } | 98 | } |
| 100 | 99 | ||
| 101 | IPC::ResponseBuilder rb{ctx, 3}; | 100 | IPC::ResponseBuilder rb{ctx, 3}; |
| @@ -116,12 +115,14 @@ void NVDRV::Ioctl3(HLERequestContext& ctx) { | |||
| 116 | } | 115 | } |
| 117 | 116 | ||
| 118 | const auto input_buffer = ctx.ReadBuffer(0); | 117 | const auto input_buffer = ctx.ReadBuffer(0); |
| 119 | tmp_output.resize_destructive(ctx.GetWriteBufferSize(0)); | 118 | output_buffer.resize_destructive(ctx.GetWriteBufferSize(0)); |
| 120 | tmp_output_inline.resize_destructive(ctx.GetWriteBufferSize(1)); | 119 | inline_output_buffer.resize_destructive(ctx.GetWriteBufferSize(1)); |
| 121 | const auto nv_result = nvdrv->Ioctl3(fd, command, input_buffer, tmp_output, tmp_output_inline); | 120 | |
| 121 | const auto nv_result = | ||
| 122 | nvdrv->Ioctl3(fd, command, input_buffer, output_buffer, inline_output_buffer); | ||
| 122 | if (command.is_out != 0) { | 123 | if (command.is_out != 0) { |
| 123 | ctx.WriteBuffer(tmp_output, 0); | 124 | ctx.WriteBuffer(output_buffer, 0); |
| 124 | ctx.WriteBuffer(tmp_output_inline, 1); | 125 | ctx.WriteBuffer(inline_output_buffer, 1); |
| 125 | } | 126 | } |
| 126 | 127 | ||
| 127 | IPC::ResponseBuilder rb{ctx, 3}; | 128 | IPC::ResponseBuilder rb{ctx, 3}; |
diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.h b/src/core/hle/service/nvdrv/nvdrv_interface.h index 4b593ff90..6e98115dc 100644 --- a/src/core/hle/service/nvdrv/nvdrv_interface.h +++ b/src/core/hle/service/nvdrv/nvdrv_interface.h | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <memory> | 6 | #include <memory> |
| 7 | |||
| 7 | #include "common/scratch_buffer.h" | 8 | #include "common/scratch_buffer.h" |
| 8 | #include "core/hle/service/nvdrv/nvdrv.h" | 9 | #include "core/hle/service/nvdrv/nvdrv.h" |
| 9 | #include "core/hle/service/service.h" | 10 | #include "core/hle/service/service.h" |
| @@ -34,8 +35,8 @@ private: | |||
| 34 | 35 | ||
| 35 | u64 pid{}; | 36 | u64 pid{}; |
| 36 | bool is_initialized{}; | 37 | bool is_initialized{}; |
| 37 | Common::ScratchBuffer<u8> tmp_output; | 38 | Common::ScratchBuffer<u8> output_buffer; |
| 38 | Common::ScratchBuffer<u8> tmp_output_inline; | 39 | Common::ScratchBuffer<u8> inline_output_buffer; |
| 39 | }; | 40 | }; |
| 40 | 41 | ||
| 41 | } // namespace Service::Nvidia | 42 | } // namespace Service::Nvidia |
diff --git a/src/core/hle/service/nvnflinger/parcel.h b/src/core/hle/service/nvnflinger/parcel.h index 23ba315a0..e2c9bbd50 100644 --- a/src/core/hle/service/nvnflinger/parcel.h +++ b/src/core/hle/service/nvnflinger/parcel.h | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | #include <memory> | 6 | #include <memory> |
| 7 | #include <span> | 7 | #include <span> |
| 8 | #include <vector> | 8 | #include <vector> |
| 9 | |||
| 9 | #include <boost/container/small_vector.hpp> | 10 | #include <boost/container/small_vector.hpp> |
| 10 | 11 | ||
| 11 | #include "common/alignment.h" | 12 | #include "common/alignment.h" |
| @@ -148,9 +149,9 @@ public: | |||
| 148 | this->WriteImpl(0U, m_object_buffer); | 149 | this->WriteImpl(0U, m_object_buffer); |
| 149 | } | 150 | } |
| 150 | 151 | ||
| 151 | std::vector<u8> Serialize() const { | 152 | std::span<u8> Serialize() { |
| 152 | std::vector<u8> output_buffer(sizeof(ParcelHeader) + m_data_buffer.size() + | 153 | m_output_buffer.resize(sizeof(ParcelHeader) + m_data_buffer.size() + |
| 153 | m_object_buffer.size()); | 154 | m_object_buffer.size()); |
| 154 | 155 | ||
| 155 | ParcelHeader header{}; | 156 | ParcelHeader header{}; |
| 156 | header.data_size = static_cast<u32>(m_data_buffer.size()); | 157 | header.data_size = static_cast<u32>(m_data_buffer.size()); |
| @@ -158,17 +159,17 @@ public: | |||
| 158 | header.objects_size = static_cast<u32>(m_object_buffer.size()); | 159 | header.objects_size = static_cast<u32>(m_object_buffer.size()); |
| 159 | header.objects_offset = header.data_offset + header.data_size; | 160 | header.objects_offset = header.data_offset + header.data_size; |
| 160 | 161 | ||
| 161 | std::memcpy(output_buffer.data(), &header, sizeof(header)); | 162 | std::memcpy(m_output_buffer.data(), &header, sizeof(ParcelHeader)); |
| 162 | std::ranges::copy(m_data_buffer, output_buffer.data() + header.data_offset); | 163 | std::ranges::copy(m_data_buffer, m_output_buffer.data() + header.data_offset); |
| 163 | std::ranges::copy(m_object_buffer, output_buffer.data() + header.objects_offset); | 164 | std::ranges::copy(m_object_buffer, m_output_buffer.data() + header.objects_offset); |
| 164 | 165 | ||
| 165 | return output_buffer; | 166 | return m_output_buffer; |
| 166 | } | 167 | } |
| 167 | 168 | ||
| 168 | private: | 169 | private: |
| 169 | template <typename T> | 170 | template <typename T, size_t BufferSize> |
| 170 | requires(std::is_trivially_copyable_v<T>) | 171 | requires(std::is_trivially_copyable_v<T>) |
| 171 | void WriteImpl(const T& val, boost::container::small_vector<u8, 0x200>& buffer) { | 172 | void WriteImpl(const T& val, boost::container::small_vector<u8, BufferSize>& buffer) { |
| 172 | const size_t aligned_size = Common::AlignUp(sizeof(T), 4); | 173 | const size_t aligned_size = Common::AlignUp(sizeof(T), 4); |
| 173 | const size_t old_size = buffer.size(); | 174 | const size_t old_size = buffer.size(); |
| 174 | buffer.resize(old_size + aligned_size); | 175 | buffer.resize(old_size + aligned_size); |
| @@ -177,8 +178,9 @@ private: | |||
| 177 | } | 178 | } |
| 178 | 179 | ||
| 179 | private: | 180 | private: |
| 180 | boost::container::small_vector<u8, 0x200> m_data_buffer; | 181 | boost::container::small_vector<u8, 0x1B0> m_data_buffer; |
| 181 | boost::container::small_vector<u8, 0x200> m_object_buffer; | 182 | boost::container::small_vector<u8, 0x40> m_object_buffer; |
| 183 | boost::container::small_vector<u8, 0x200> m_output_buffer; | ||
| 182 | }; | 184 | }; |
| 183 | 185 | ||
| 184 | } // namespace Service::android | 186 | } // namespace Service::android |
diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 514ba0d66..257406f09 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | #include <algorithm> | 4 | #include <algorithm> |
| 5 | #include <cstring> | 5 | #include <cstring> |
| 6 | #include <span> | ||
| 6 | 7 | ||
| 7 | #include "common/assert.h" | 8 | #include "common/assert.h" |
| 8 | #include "common/atomic_ops.h" | 9 | #include "common/atomic_ops.h" |
| @@ -13,6 +14,7 @@ | |||
| 13 | #include "common/swap.h" | 14 | #include "common/swap.h" |
| 14 | #include "core/core.h" | 15 | #include "core/core.h" |
| 15 | #include "core/device_memory.h" | 16 | #include "core/device_memory.h" |
| 17 | #include "core/gpu_dirty_memory_manager.h" | ||
| 16 | #include "core/hardware_properties.h" | 18 | #include "core/hardware_properties.h" |
| 17 | #include "core/hle/kernel/k_page_table.h" | 19 | #include "core/hle/kernel/k_page_table.h" |
| 18 | #include "core/hle/kernel/k_process.h" | 20 | #include "core/hle/kernel/k_process.h" |
| @@ -678,7 +680,7 @@ struct Memory::Impl { | |||
| 678 | LOG_ERROR(HW_Memory, "Unmapped Write{} @ 0x{:016X} = 0x{:016X}", sizeof(T) * 8, | 680 | LOG_ERROR(HW_Memory, "Unmapped Write{} @ 0x{:016X} = 0x{:016X}", sizeof(T) * 8, |
| 679 | GetInteger(vaddr), static_cast<u64>(data)); | 681 | GetInteger(vaddr), static_cast<u64>(data)); |
| 680 | }, | 682 | }, |
| 681 | [&]() { system.GPU().InvalidateRegion(GetInteger(vaddr), sizeof(T)); }); | 683 | [&]() { HandleRasterizerWrite(GetInteger(vaddr), sizeof(T)); }); |
| 682 | if (ptr) { | 684 | if (ptr) { |
| 683 | std::memcpy(ptr, &data, sizeof(T)); | 685 | std::memcpy(ptr, &data, sizeof(T)); |
| 684 | } | 686 | } |
| @@ -692,7 +694,7 @@ struct Memory::Impl { | |||
| 692 | LOG_ERROR(HW_Memory, "Unmapped WriteExclusive{} @ 0x{:016X} = 0x{:016X}", | 694 | LOG_ERROR(HW_Memory, "Unmapped WriteExclusive{} @ 0x{:016X} = 0x{:016X}", |
| 693 | sizeof(T) * 8, GetInteger(vaddr), static_cast<u64>(data)); | 695 | sizeof(T) * 8, GetInteger(vaddr), static_cast<u64>(data)); |
| 694 | }, | 696 | }, |
| 695 | [&]() { system.GPU().InvalidateRegion(GetInteger(vaddr), sizeof(T)); }); | 697 | [&]() { HandleRasterizerWrite(GetInteger(vaddr), sizeof(T)); }); |
| 696 | if (ptr) { | 698 | if (ptr) { |
| 697 | const auto volatile_pointer = reinterpret_cast<volatile T*>(ptr); | 699 | const auto volatile_pointer = reinterpret_cast<volatile T*>(ptr); |
| 698 | return Common::AtomicCompareAndSwap(volatile_pointer, data, expected); | 700 | return Common::AtomicCompareAndSwap(volatile_pointer, data, expected); |
| @@ -707,7 +709,7 @@ struct Memory::Impl { | |||
| 707 | LOG_ERROR(HW_Memory, "Unmapped WriteExclusive128 @ 0x{:016X} = 0x{:016X}{:016X}", | 709 | LOG_ERROR(HW_Memory, "Unmapped WriteExclusive128 @ 0x{:016X} = 0x{:016X}{:016X}", |
| 708 | GetInteger(vaddr), static_cast<u64>(data[1]), static_cast<u64>(data[0])); | 710 | GetInteger(vaddr), static_cast<u64>(data[1]), static_cast<u64>(data[0])); |
| 709 | }, | 711 | }, |
| 710 | [&]() { system.GPU().InvalidateRegion(GetInteger(vaddr), sizeof(u128)); }); | 712 | [&]() { HandleRasterizerWrite(GetInteger(vaddr), sizeof(u128)); }); |
| 711 | if (ptr) { | 713 | if (ptr) { |
| 712 | const auto volatile_pointer = reinterpret_cast<volatile u64*>(ptr); | 714 | const auto volatile_pointer = reinterpret_cast<volatile u64*>(ptr); |
| 713 | return Common::AtomicCompareAndSwap(volatile_pointer, data, expected); | 715 | return Common::AtomicCompareAndSwap(volatile_pointer, data, expected); |
| @@ -717,7 +719,7 @@ struct Memory::Impl { | |||
| 717 | 719 | ||
| 718 | void HandleRasterizerDownload(VAddr address, size_t size) { | 720 | void HandleRasterizerDownload(VAddr address, size_t size) { |
| 719 | const size_t core = system.GetCurrentHostThreadID(); | 721 | const size_t core = system.GetCurrentHostThreadID(); |
| 720 | auto& current_area = rasterizer_areas[core]; | 722 | auto& current_area = rasterizer_read_areas[core]; |
| 721 | const VAddr end_address = address + size; | 723 | const VAddr end_address = address + size; |
| 722 | if (current_area.start_address <= address && end_address <= current_area.end_address) | 724 | if (current_area.start_address <= address && end_address <= current_area.end_address) |
| 723 | [[likely]] { | 725 | [[likely]] { |
| @@ -726,9 +728,31 @@ struct Memory::Impl { | |||
| 726 | current_area = system.GPU().OnCPURead(address, size); | 728 | current_area = system.GPU().OnCPURead(address, size); |
| 727 | } | 729 | } |
| 728 | 730 | ||
| 729 | Common::PageTable* current_page_table = nullptr; | 731 | void HandleRasterizerWrite(VAddr address, size_t size) { |
| 730 | std::array<VideoCore::RasterizerDownloadArea, Core::Hardware::NUM_CPU_CORES> rasterizer_areas{}; | 732 | const size_t core = system.GetCurrentHostThreadID(); |
| 733 | auto& current_area = rasterizer_write_areas[core]; | ||
| 734 | VAddr subaddress = address >> YUZU_PAGEBITS; | ||
| 735 | bool do_collection = current_area.last_address == subaddress; | ||
| 736 | if (!do_collection) [[unlikely]] { | ||
| 737 | do_collection = system.GPU().OnCPUWrite(address, size); | ||
| 738 | if (!do_collection) { | ||
| 739 | return; | ||
| 740 | } | ||
| 741 | current_area.last_address = subaddress; | ||
| 742 | } | ||
| 743 | gpu_dirty_managers[core].Collect(address, size); | ||
| 744 | } | ||
| 745 | |||
| 746 | struct GPUDirtyState { | ||
| 747 | VAddr last_address; | ||
| 748 | }; | ||
| 749 | |||
| 731 | Core::System& system; | 750 | Core::System& system; |
| 751 | Common::PageTable* current_page_table = nullptr; | ||
| 752 | std::array<VideoCore::RasterizerDownloadArea, Core::Hardware::NUM_CPU_CORES> | ||
| 753 | rasterizer_read_areas{}; | ||
| 754 | std::array<GPUDirtyState, Core::Hardware::NUM_CPU_CORES> rasterizer_write_areas{}; | ||
| 755 | std::span<Core::GPUDirtyMemoryManager> gpu_dirty_managers; | ||
| 732 | }; | 756 | }; |
| 733 | 757 | ||
| 734 | Memory::Memory(Core::System& system_) : system{system_} { | 758 | Memory::Memory(Core::System& system_) : system{system_} { |
| @@ -876,6 +900,10 @@ void Memory::ZeroBlock(Common::ProcessAddress dest_addr, const std::size_t size) | |||
| 876 | impl->ZeroBlock(*system.ApplicationProcess(), dest_addr, size); | 900 | impl->ZeroBlock(*system.ApplicationProcess(), dest_addr, size); |
| 877 | } | 901 | } |
| 878 | 902 | ||
| 903 | void Memory::SetGPUDirtyManagers(std::span<Core::GPUDirtyMemoryManager> managers) { | ||
| 904 | impl->gpu_dirty_managers = managers; | ||
| 905 | } | ||
| 906 | |||
| 879 | Result Memory::InvalidateDataCache(Common::ProcessAddress dest_addr, const std::size_t size) { | 907 | Result Memory::InvalidateDataCache(Common::ProcessAddress dest_addr, const std::size_t size) { |
| 880 | return impl->InvalidateDataCache(*system.ApplicationProcess(), dest_addr, size); | 908 | return impl->InvalidateDataCache(*system.ApplicationProcess(), dest_addr, size); |
| 881 | } | 909 | } |
diff --git a/src/core/memory.h b/src/core/memory.h index 72a0be813..ea01824f8 100644 --- a/src/core/memory.h +++ b/src/core/memory.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | 5 | ||
| 6 | #include <cstddef> | 6 | #include <cstddef> |
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | #include <span> | ||
| 8 | #include <string> | 9 | #include <string> |
| 9 | #include "common/typed_address.h" | 10 | #include "common/typed_address.h" |
| 10 | #include "core/hle/result.h" | 11 | #include "core/hle/result.h" |
| @@ -15,7 +16,8 @@ struct PageTable; | |||
| 15 | 16 | ||
| 16 | namespace Core { | 17 | namespace Core { |
| 17 | class System; | 18 | class System; |
| 18 | } | 19 | class GPUDirtyMemoryManager; |
| 20 | } // namespace Core | ||
| 19 | 21 | ||
| 20 | namespace Kernel { | 22 | namespace Kernel { |
| 21 | class PhysicalMemory; | 23 | class PhysicalMemory; |
| @@ -458,6 +460,8 @@ public: | |||
| 458 | */ | 460 | */ |
| 459 | void MarkRegionDebug(Common::ProcessAddress vaddr, u64 size, bool debug); | 461 | void MarkRegionDebug(Common::ProcessAddress vaddr, u64 size, bool debug); |
| 460 | 462 | ||
| 463 | void SetGPUDirtyManagers(std::span<Core::GPUDirtyMemoryManager> managers); | ||
| 464 | |||
| 461 | private: | 465 | private: |
| 462 | Core::System& system; | 466 | Core::System& system; |
| 463 | 467 | ||
diff --git a/src/input_common/drivers/mouse.cpp b/src/input_common/drivers/mouse.cpp index f07cf8a0e..dac29c78f 100644 --- a/src/input_common/drivers/mouse.cpp +++ b/src/input_common/drivers/mouse.cpp | |||
| @@ -12,9 +12,13 @@ | |||
| 12 | 12 | ||
| 13 | namespace InputCommon { | 13 | namespace InputCommon { |
| 14 | constexpr int update_time = 10; | 14 | constexpr int update_time = 10; |
| 15 | constexpr float default_stick_sensitivity = 0.0044f; | 15 | constexpr float default_panning_sensitivity = 0.0010f; |
| 16 | constexpr float default_motion_sensitivity = 0.0003f; | 16 | constexpr float default_stick_sensitivity = 0.0006f; |
| 17 | constexpr float default_deadzone_counterweight = 0.01f; | ||
| 18 | constexpr float default_motion_panning_sensitivity = 2.5f; | ||
| 19 | constexpr float default_motion_sensitivity = 0.416f; | ||
| 17 | constexpr float maximum_rotation_speed = 2.0f; | 20 | constexpr float maximum_rotation_speed = 2.0f; |
| 21 | constexpr float maximum_stick_range = 1.5f; | ||
| 18 | constexpr int mouse_axis_x = 0; | 22 | constexpr int mouse_axis_x = 0; |
| 19 | constexpr int mouse_axis_y = 1; | 23 | constexpr int mouse_axis_y = 1; |
| 20 | constexpr int wheel_axis_x = 2; | 24 | constexpr int wheel_axis_x = 2; |
| @@ -81,7 +85,7 @@ void Mouse::UpdateThread(std::stop_token stop_token) { | |||
| 81 | } | 85 | } |
| 82 | 86 | ||
| 83 | void Mouse::UpdateStickInput() { | 87 | void Mouse::UpdateStickInput() { |
| 84 | if (!Settings::values.mouse_panning) { | 88 | if (!IsMousePanningEnabled()) { |
| 85 | return; | 89 | return; |
| 86 | } | 90 | } |
| 87 | 91 | ||
| @@ -89,26 +93,13 @@ void Mouse::UpdateStickInput() { | |||
| 89 | 93 | ||
| 90 | // Prevent input from exceeding the max range (1.0f) too much, | 94 | // Prevent input from exceeding the max range (1.0f) too much, |
| 91 | // but allow some room to make it easier to sustain | 95 | // but allow some room to make it easier to sustain |
| 92 | if (length > 1.2f) { | 96 | if (length > maximum_stick_range) { |
| 93 | last_mouse_change /= length; | 97 | last_mouse_change /= length; |
| 94 | last_mouse_change *= 1.2f; | 98 | last_mouse_change *= maximum_stick_range; |
| 95 | } | 99 | } |
| 96 | 100 | ||
| 97 | auto mouse_change = last_mouse_change; | 101 | SetAxis(identifier, mouse_axis_x, last_mouse_change.x); |
| 98 | 102 | SetAxis(identifier, mouse_axis_y, -last_mouse_change.y); | |
| 99 | // Bind the mouse change to [0 <= deadzone_counterweight <= 1,1] | ||
| 100 | if (length < 1.0f) { | ||
| 101 | const float deadzone_h_counterweight = | ||
| 102 | Settings::values.mouse_panning_deadzone_x_counterweight.GetValue(); | ||
| 103 | const float deadzone_v_counterweight = | ||
| 104 | Settings::values.mouse_panning_deadzone_y_counterweight.GetValue(); | ||
| 105 | mouse_change /= length; | ||
| 106 | mouse_change.x *= length + (1 - length) * deadzone_h_counterweight * 0.01f; | ||
| 107 | mouse_change.y *= length + (1 - length) * deadzone_v_counterweight * 0.01f; | ||
| 108 | } | ||
| 109 | |||
| 110 | SetAxis(identifier, mouse_axis_x, mouse_change.x); | ||
| 111 | SetAxis(identifier, mouse_axis_y, -mouse_change.y); | ||
| 112 | 103 | ||
| 113 | // Decay input over time | 104 | // Decay input over time |
| 114 | const float clamped_length = std::min(1.0f, length); | 105 | const float clamped_length = std::min(1.0f, length); |
| @@ -120,14 +111,13 @@ void Mouse::UpdateStickInput() { | |||
| 120 | } | 111 | } |
| 121 | 112 | ||
| 122 | void Mouse::UpdateMotionInput() { | 113 | void Mouse::UpdateMotionInput() { |
| 123 | // This may need its own sensitivity instead of using the average | 114 | const float sensitivity = |
| 124 | const float sensitivity = (Settings::values.mouse_panning_x_sensitivity.GetValue() + | 115 | IsMousePanningEnabled() ? default_motion_panning_sensitivity : default_motion_sensitivity; |
| 125 | Settings::values.mouse_panning_y_sensitivity.GetValue()) / | ||
| 126 | 2.0f * default_motion_sensitivity; | ||
| 127 | 116 | ||
| 128 | const float rotation_velocity = std::sqrt(last_motion_change.x * last_motion_change.x + | 117 | const float rotation_velocity = std::sqrt(last_motion_change.x * last_motion_change.x + |
| 129 | last_motion_change.y * last_motion_change.y); | 118 | last_motion_change.y * last_motion_change.y); |
| 130 | 119 | ||
| 120 | // Clamp rotation speed | ||
| 131 | if (rotation_velocity > maximum_rotation_speed / sensitivity) { | 121 | if (rotation_velocity > maximum_rotation_speed / sensitivity) { |
| 132 | const float multiplier = maximum_rotation_speed / rotation_velocity / sensitivity; | 122 | const float multiplier = maximum_rotation_speed / rotation_velocity / sensitivity; |
| 133 | last_motion_change.x = last_motion_change.x * multiplier; | 123 | last_motion_change.x = last_motion_change.x * multiplier; |
| @@ -144,7 +134,7 @@ void Mouse::UpdateMotionInput() { | |||
| 144 | .delta_timestamp = update_time * 1000, | 134 | .delta_timestamp = update_time * 1000, |
| 145 | }; | 135 | }; |
| 146 | 136 | ||
| 147 | if (Settings::values.mouse_panning) { | 137 | if (IsMousePanningEnabled()) { |
| 148 | last_motion_change.x = 0; | 138 | last_motion_change.x = 0; |
| 149 | last_motion_change.y = 0; | 139 | last_motion_change.y = 0; |
| 150 | } | 140 | } |
| @@ -154,33 +144,42 @@ void Mouse::UpdateMotionInput() { | |||
| 154 | } | 144 | } |
| 155 | 145 | ||
| 156 | void Mouse::Move(int x, int y, int center_x, int center_y) { | 146 | void Mouse::Move(int x, int y, int center_x, int center_y) { |
| 157 | if (Settings::values.mouse_panning) { | 147 | if (IsMousePanningEnabled()) { |
| 158 | const auto mouse_change = | 148 | const auto mouse_change = |
| 159 | (Common::MakeVec(x, y) - Common::MakeVec(center_x, center_y)).Cast<float>(); | 149 | (Common::MakeVec(x, y) - Common::MakeVec(center_x, center_y)).Cast<float>(); |
| 160 | const float x_sensitivity = | 150 | const float x_sensitivity = |
| 161 | Settings::values.mouse_panning_x_sensitivity.GetValue() * default_stick_sensitivity; | 151 | Settings::values.mouse_panning_x_sensitivity.GetValue() * default_panning_sensitivity; |
| 162 | const float y_sensitivity = | 152 | const float y_sensitivity = |
| 163 | Settings::values.mouse_panning_y_sensitivity.GetValue() * default_stick_sensitivity; | 153 | Settings::values.mouse_panning_y_sensitivity.GetValue() * default_panning_sensitivity; |
| 154 | const float deadzone_counterweight = | ||
| 155 | Settings::values.mouse_panning_deadzone_counterweight.GetValue() * | ||
| 156 | default_deadzone_counterweight; | ||
| 157 | |||
| 158 | last_motion_change += {-mouse_change.y * x_sensitivity, -mouse_change.x * y_sensitivity, 0}; | ||
| 159 | last_mouse_change.x += mouse_change.x * x_sensitivity; | ||
| 160 | last_mouse_change.y += mouse_change.y * y_sensitivity; | ||
| 164 | 161 | ||
| 165 | last_motion_change += {-mouse_change.y, -mouse_change.x, 0}; | 162 | // Bind the mouse change to [0 <= deadzone_counterweight <= 1.0] |
| 166 | last_mouse_change.x += mouse_change.x * x_sensitivity * 0.09f; | 163 | if (last_mouse_change.Length() < deadzone_counterweight) { |
| 167 | last_mouse_change.y += mouse_change.y * y_sensitivity * 0.09f; | 164 | last_mouse_change /= last_mouse_change.Length(); |
| 165 | last_mouse_change *= deadzone_counterweight; | ||
| 166 | } | ||
| 168 | 167 | ||
| 169 | return; | 168 | return; |
| 170 | } | 169 | } |
| 171 | 170 | ||
| 172 | if (button_pressed) { | 171 | if (button_pressed) { |
| 173 | const auto mouse_move = Common::MakeVec<int>(x, y) - mouse_origin; | 172 | const auto mouse_move = Common::MakeVec<int>(x, y) - mouse_origin; |
| 174 | const float x_sensitivity = Settings::values.mouse_panning_x_sensitivity.GetValue(); | 173 | const float x_sensitivity = |
| 175 | const float y_sensitivity = Settings::values.mouse_panning_y_sensitivity.GetValue(); | 174 | Settings::values.mouse_panning_x_sensitivity.GetValue() * default_stick_sensitivity; |
| 176 | SetAxis(identifier, mouse_axis_x, | 175 | const float y_sensitivity = |
| 177 | static_cast<float>(mouse_move.x) * x_sensitivity * 0.0012f); | 176 | Settings::values.mouse_panning_y_sensitivity.GetValue() * default_stick_sensitivity; |
| 178 | SetAxis(identifier, mouse_axis_y, | 177 | SetAxis(identifier, mouse_axis_x, static_cast<float>(mouse_move.x) * x_sensitivity); |
| 179 | static_cast<float>(-mouse_move.y) * y_sensitivity * 0.0012f); | 178 | SetAxis(identifier, mouse_axis_y, static_cast<float>(-mouse_move.y) * y_sensitivity); |
| 180 | 179 | ||
| 181 | last_motion_change = { | 180 | last_motion_change = { |
| 182 | static_cast<float>(-mouse_move.y) / 50.0f, | 181 | static_cast<float>(-mouse_move.y) * x_sensitivity, |
| 183 | static_cast<float>(-mouse_move.x) / 50.0f, | 182 | static_cast<float>(-mouse_move.x) * y_sensitivity, |
| 184 | last_motion_change.z, | 183 | last_motion_change.z, |
| 185 | }; | 184 | }; |
| 186 | } | 185 | } |
| @@ -220,7 +219,7 @@ void Mouse::ReleaseButton(MouseButton button) { | |||
| 220 | SetButton(real_mouse_identifier, static_cast<int>(button), false); | 219 | SetButton(real_mouse_identifier, static_cast<int>(button), false); |
| 221 | SetButton(touch_identifier, static_cast<int>(button), false); | 220 | SetButton(touch_identifier, static_cast<int>(button), false); |
| 222 | 221 | ||
| 223 | if (!Settings::values.mouse_panning) { | 222 | if (!IsMousePanningEnabled()) { |
| 224 | SetAxis(identifier, mouse_axis_x, 0); | 223 | SetAxis(identifier, mouse_axis_x, 0); |
| 225 | SetAxis(identifier, mouse_axis_y, 0); | 224 | SetAxis(identifier, mouse_axis_y, 0); |
| 226 | } | 225 | } |
| @@ -234,7 +233,7 @@ void Mouse::ReleaseButton(MouseButton button) { | |||
| 234 | void Mouse::MouseWheelChange(int x, int y) { | 233 | void Mouse::MouseWheelChange(int x, int y) { |
| 235 | wheel_position.x += x; | 234 | wheel_position.x += x; |
| 236 | wheel_position.y += y; | 235 | wheel_position.y += y; |
| 237 | last_motion_change.z += static_cast<f32>(y) / 100.0f; | 236 | last_motion_change.z += static_cast<f32>(y); |
| 238 | SetAxis(identifier, wheel_axis_x, static_cast<f32>(wheel_position.x)); | 237 | SetAxis(identifier, wheel_axis_x, static_cast<f32>(wheel_position.x)); |
| 239 | SetAxis(identifier, wheel_axis_y, static_cast<f32>(wheel_position.y)); | 238 | SetAxis(identifier, wheel_axis_y, static_cast<f32>(wheel_position.y)); |
| 240 | } | 239 | } |
| @@ -244,6 +243,11 @@ void Mouse::ReleaseAllButtons() { | |||
| 244 | button_pressed = false; | 243 | button_pressed = false; |
| 245 | } | 244 | } |
| 246 | 245 | ||
| 246 | bool Mouse::IsMousePanningEnabled() { | ||
| 247 | // Disable mouse panning when a real mouse is connected | ||
| 248 | return Settings::values.mouse_panning && !Settings::values.mouse_enabled; | ||
| 249 | } | ||
| 250 | |||
| 247 | std::vector<Common::ParamPackage> Mouse::GetInputDevices() const { | 251 | std::vector<Common::ParamPackage> Mouse::GetInputDevices() const { |
| 248 | std::vector<Common::ParamPackage> devices; | 252 | std::vector<Common::ParamPackage> devices; |
| 249 | devices.emplace_back(Common::ParamPackage{ | 253 | devices.emplace_back(Common::ParamPackage{ |
diff --git a/src/input_common/drivers/mouse.h b/src/input_common/drivers/mouse.h index 0e8edcce1..2b93a40b9 100644 --- a/src/input_common/drivers/mouse.h +++ b/src/input_common/drivers/mouse.h | |||
| @@ -99,6 +99,8 @@ private: | |||
| 99 | void UpdateStickInput(); | 99 | void UpdateStickInput(); |
| 100 | void UpdateMotionInput(); | 100 | void UpdateMotionInput(); |
| 101 | 101 | ||
| 102 | bool IsMousePanningEnabled(); | ||
| 103 | |||
| 102 | Common::Input::ButtonNames GetUIButtonName(const Common::ParamPackage& params) const; | 104 | Common::Input::ButtonNames GetUIButtonName(const Common::ParamPackage& params) const; |
| 103 | 105 | ||
| 104 | Common::Vec2<int> mouse_origin; | 106 | Common::Vec2<int> mouse_origin; |
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 58a45ab67..b5ed3380f 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h | |||
| @@ -115,7 +115,34 @@ void BufferCache<P>::WriteMemory(VAddr cpu_addr, u64 size) { | |||
| 115 | 115 | ||
| 116 | template <class P> | 116 | template <class P> |
| 117 | void BufferCache<P>::CachedWriteMemory(VAddr cpu_addr, u64 size) { | 117 | void BufferCache<P>::CachedWriteMemory(VAddr cpu_addr, u64 size) { |
| 118 | memory_tracker.CachedCpuWrite(cpu_addr, size); | 118 | const bool is_dirty = IsRegionRegistered(cpu_addr, size); |
| 119 | if (!is_dirty) { | ||
| 120 | return; | ||
| 121 | } | ||
| 122 | VAddr aligned_start = Common::AlignDown(cpu_addr, YUZU_PAGESIZE); | ||
| 123 | VAddr aligned_end = Common::AlignUp(cpu_addr + size, YUZU_PAGESIZE); | ||
| 124 | if (!IsRegionGpuModified(aligned_start, aligned_end - aligned_start)) { | ||
| 125 | WriteMemory(cpu_addr, size); | ||
| 126 | return; | ||
| 127 | } | ||
| 128 | |||
| 129 | tmp_buffer.resize_destructive(size); | ||
| 130 | cpu_memory.ReadBlockUnsafe(cpu_addr, tmp_buffer.data(), size); | ||
| 131 | |||
| 132 | InlineMemoryImplementation(cpu_addr, size, tmp_buffer); | ||
| 133 | } | ||
| 134 | |||
| 135 | template <class P> | ||
| 136 | bool BufferCache<P>::OnCPUWrite(VAddr cpu_addr, u64 size) { | ||
| 137 | const bool is_dirty = IsRegionRegistered(cpu_addr, size); | ||
| 138 | if (!is_dirty) { | ||
| 139 | return false; | ||
| 140 | } | ||
| 141 | if (memory_tracker.IsRegionGpuModified(cpu_addr, size)) { | ||
| 142 | return true; | ||
| 143 | } | ||
| 144 | WriteMemory(cpu_addr, size); | ||
| 145 | return false; | ||
| 119 | } | 146 | } |
| 120 | 147 | ||
| 121 | template <class P> | 148 | template <class P> |
| @@ -1553,6 +1580,14 @@ bool BufferCache<P>::InlineMemory(VAddr dest_address, size_t copy_size, | |||
| 1553 | return false; | 1580 | return false; |
| 1554 | } | 1581 | } |
| 1555 | 1582 | ||
| 1583 | InlineMemoryImplementation(dest_address, copy_size, inlined_buffer); | ||
| 1584 | |||
| 1585 | return true; | ||
| 1586 | } | ||
| 1587 | |||
| 1588 | template <class P> | ||
| 1589 | void BufferCache<P>::InlineMemoryImplementation(VAddr dest_address, size_t copy_size, | ||
| 1590 | std::span<const u8> inlined_buffer) { | ||
| 1556 | const IntervalType subtract_interval{dest_address, dest_address + copy_size}; | 1591 | const IntervalType subtract_interval{dest_address, dest_address + copy_size}; |
| 1557 | ClearDownload(subtract_interval); | 1592 | ClearDownload(subtract_interval); |
| 1558 | common_ranges.subtract(subtract_interval); | 1593 | common_ranges.subtract(subtract_interval); |
| @@ -1574,8 +1609,6 @@ bool BufferCache<P>::InlineMemory(VAddr dest_address, size_t copy_size, | |||
| 1574 | } else { | 1609 | } else { |
| 1575 | buffer.ImmediateUpload(buffer.Offset(dest_address), inlined_buffer.first(copy_size)); | 1610 | buffer.ImmediateUpload(buffer.Offset(dest_address), inlined_buffer.first(copy_size)); |
| 1576 | } | 1611 | } |
| 1577 | |||
| 1578 | return true; | ||
| 1579 | } | 1612 | } |
| 1580 | 1613 | ||
| 1581 | template <class P> | 1614 | template <class P> |
diff --git a/src/video_core/buffer_cache/buffer_cache_base.h b/src/video_core/buffer_cache/buffer_cache_base.h index fe6068cfe..460fc7551 100644 --- a/src/video_core/buffer_cache/buffer_cache_base.h +++ b/src/video_core/buffer_cache/buffer_cache_base.h | |||
| @@ -245,6 +245,8 @@ public: | |||
| 245 | 245 | ||
| 246 | void CachedWriteMemory(VAddr cpu_addr, u64 size); | 246 | void CachedWriteMemory(VAddr cpu_addr, u64 size); |
| 247 | 247 | ||
| 248 | bool OnCPUWrite(VAddr cpu_addr, u64 size); | ||
| 249 | |||
| 248 | void DownloadMemory(VAddr cpu_addr, u64 size); | 250 | void DownloadMemory(VAddr cpu_addr, u64 size); |
| 249 | 251 | ||
| 250 | std::optional<VideoCore::RasterizerDownloadArea> GetFlushArea(VAddr cpu_addr, u64 size); | 252 | std::optional<VideoCore::RasterizerDownloadArea> GetFlushArea(VAddr cpu_addr, u64 size); |
| @@ -543,6 +545,9 @@ private: | |||
| 543 | 545 | ||
| 544 | void ClearDownload(IntervalType subtract_interval); | 546 | void ClearDownload(IntervalType subtract_interval); |
| 545 | 547 | ||
| 548 | void InlineMemoryImplementation(VAddr dest_address, size_t copy_size, | ||
| 549 | std::span<const u8> inlined_buffer); | ||
| 550 | |||
| 546 | VideoCore::RasterizerInterface& rasterizer; | 551 | VideoCore::RasterizerInterface& rasterizer; |
| 547 | Core::Memory::Memory& cpu_memory; | 552 | Core::Memory::Memory& cpu_memory; |
| 548 | 553 | ||
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp index a290d6ea7..f8598fd98 100644 --- a/src/video_core/engines/maxwell_dma.cpp +++ b/src/video_core/engines/maxwell_dma.cpp | |||
| @@ -174,8 +174,7 @@ void MaxwellDMA::CopyBlockLinearToPitch() { | |||
| 174 | src_operand.address = regs.offset_in; | 174 | src_operand.address = regs.offset_in; |
| 175 | 175 | ||
| 176 | DMA::BufferOperand dst_operand; | 176 | DMA::BufferOperand dst_operand; |
| 177 | u32 abs_pitch_out = std::abs(static_cast<s32>(regs.pitch_out)); | 177 | dst_operand.pitch = static_cast<u32>(std::abs(regs.pitch_out)); |
| 178 | dst_operand.pitch = abs_pitch_out; | ||
| 179 | dst_operand.width = regs.line_length_in; | 178 | dst_operand.width = regs.line_length_in; |
| 180 | dst_operand.height = regs.line_count; | 179 | dst_operand.height = regs.line_count; |
| 181 | dst_operand.address = regs.offset_out; | 180 | dst_operand.address = regs.offset_out; |
| @@ -222,7 +221,7 @@ void MaxwellDMA::CopyBlockLinearToPitch() { | |||
| 222 | const size_t src_size = | 221 | const size_t src_size = |
| 223 | CalculateSize(true, bytes_per_pixel, width, height, depth, block_height, block_depth); | 222 | CalculateSize(true, bytes_per_pixel, width, height, depth, block_height, block_depth); |
| 224 | 223 | ||
| 225 | const size_t dst_size = static_cast<size_t>(abs_pitch_out) * regs.line_count; | 224 | const size_t dst_size = dst_operand.pitch * regs.line_count; |
| 226 | read_buffer.resize_destructive(src_size); | 225 | read_buffer.resize_destructive(src_size); |
| 227 | write_buffer.resize_destructive(dst_size); | 226 | write_buffer.resize_destructive(dst_size); |
| 228 | 227 | ||
| @@ -231,7 +230,7 @@ void MaxwellDMA::CopyBlockLinearToPitch() { | |||
| 231 | 230 | ||
| 232 | UnswizzleSubrect(write_buffer, read_buffer, bytes_per_pixel, width, height, depth, x_offset, | 231 | UnswizzleSubrect(write_buffer, read_buffer, bytes_per_pixel, width, height, depth, x_offset, |
| 233 | src_params.origin.y, x_elements, regs.line_count, block_height, block_depth, | 232 | src_params.origin.y, x_elements, regs.line_count, block_height, block_depth, |
| 234 | abs_pitch_out); | 233 | dst_operand.pitch); |
| 235 | 234 | ||
| 236 | memory_manager.WriteBlockCached(regs.offset_out, write_buffer.data(), dst_size); | 235 | memory_manager.WriteBlockCached(regs.offset_out, write_buffer.data(), dst_size); |
| 237 | } | 236 | } |
diff --git a/src/video_core/fence_manager.h b/src/video_core/fence_manager.h index 35d699bbf..ab20ff30f 100644 --- a/src/video_core/fence_manager.h +++ b/src/video_core/fence_manager.h | |||
| @@ -69,7 +69,6 @@ public: | |||
| 69 | } | 69 | } |
| 70 | 70 | ||
| 71 | void SignalFence(std::function<void()>&& func) { | 71 | void SignalFence(std::function<void()>&& func) { |
| 72 | rasterizer.InvalidateGPUCache(); | ||
| 73 | bool delay_fence = Settings::IsGPULevelHigh(); | 72 | bool delay_fence = Settings::IsGPULevelHigh(); |
| 74 | if constexpr (!can_async_check) { | 73 | if constexpr (!can_async_check) { |
| 75 | TryReleasePendingFences<false>(); | 74 | TryReleasePendingFences<false>(); |
| @@ -96,6 +95,7 @@ public: | |||
| 96 | guard.unlock(); | 95 | guard.unlock(); |
| 97 | cv.notify_all(); | 96 | cv.notify_all(); |
| 98 | } | 97 | } |
| 98 | rasterizer.InvalidateGPUCache(); | ||
| 99 | } | 99 | } |
| 100 | 100 | ||
| 101 | void SignalSyncPoint(u32 value) { | 101 | void SignalSyncPoint(u32 value) { |
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index db385076d..c192e33b2 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp | |||
| @@ -95,7 +95,9 @@ struct GPU::Impl { | |||
| 95 | 95 | ||
| 96 | /// Synchronizes CPU writes with Host GPU memory. | 96 | /// Synchronizes CPU writes with Host GPU memory. |
| 97 | void InvalidateGPUCache() { | 97 | void InvalidateGPUCache() { |
| 98 | rasterizer->InvalidateGPUCache(); | 98 | std::function<void(VAddr, size_t)> callback_writes( |
| 99 | [this](VAddr address, size_t size) { rasterizer->OnCacheInvalidation(address, size); }); | ||
| 100 | system.GatherGPUDirtyMemory(callback_writes); | ||
| 99 | } | 101 | } |
| 100 | 102 | ||
| 101 | /// Signal the ending of command list. | 103 | /// Signal the ending of command list. |
| @@ -299,6 +301,10 @@ struct GPU::Impl { | |||
| 299 | gpu_thread.InvalidateRegion(addr, size); | 301 | gpu_thread.InvalidateRegion(addr, size); |
| 300 | } | 302 | } |
| 301 | 303 | ||
| 304 | bool OnCPUWrite(VAddr addr, u64 size) { | ||
| 305 | return rasterizer->OnCPUWrite(addr, size); | ||
| 306 | } | ||
| 307 | |||
| 302 | /// Notify rasterizer that any caches of the specified region should be flushed and invalidated | 308 | /// Notify rasterizer that any caches of the specified region should be flushed and invalidated |
| 303 | void FlushAndInvalidateRegion(VAddr addr, u64 size) { | 309 | void FlushAndInvalidateRegion(VAddr addr, u64 size) { |
| 304 | gpu_thread.FlushAndInvalidateRegion(addr, size); | 310 | gpu_thread.FlushAndInvalidateRegion(addr, size); |
| @@ -561,6 +567,10 @@ void GPU::InvalidateRegion(VAddr addr, u64 size) { | |||
| 561 | impl->InvalidateRegion(addr, size); | 567 | impl->InvalidateRegion(addr, size); |
| 562 | } | 568 | } |
| 563 | 569 | ||
| 570 | bool GPU::OnCPUWrite(VAddr addr, u64 size) { | ||
| 571 | return impl->OnCPUWrite(addr, size); | ||
| 572 | } | ||
| 573 | |||
| 564 | void GPU::FlushAndInvalidateRegion(VAddr addr, u64 size) { | 574 | void GPU::FlushAndInvalidateRegion(VAddr addr, u64 size) { |
| 565 | impl->FlushAndInvalidateRegion(addr, size); | 575 | impl->FlushAndInvalidateRegion(addr, size); |
| 566 | } | 576 | } |
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index e49c40cf2..ba2838b89 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h | |||
| @@ -250,6 +250,10 @@ public: | |||
| 250 | /// Notify rasterizer that any caches of the specified region should be invalidated | 250 | /// Notify rasterizer that any caches of the specified region should be invalidated |
| 251 | void InvalidateRegion(VAddr addr, u64 size); | 251 | void InvalidateRegion(VAddr addr, u64 size); |
| 252 | 252 | ||
| 253 | /// Notify rasterizer that CPU is trying to write this area. It returns true if the area is | ||
| 254 | /// sensible, false otherwise | ||
| 255 | bool OnCPUWrite(VAddr addr, u64 size); | ||
| 256 | |||
| 253 | /// Notify rasterizer that any caches of the specified region should be flushed and invalidated | 257 | /// Notify rasterizer that any caches of the specified region should be flushed and invalidated |
| 254 | void FlushAndInvalidateRegion(VAddr addr, u64 size); | 258 | void FlushAndInvalidateRegion(VAddr addr, u64 size); |
| 255 | 259 | ||
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp index 889144f38..2f0f9f593 100644 --- a/src/video_core/gpu_thread.cpp +++ b/src/video_core/gpu_thread.cpp | |||
| @@ -47,7 +47,7 @@ static void RunThread(std::stop_token stop_token, Core::System& system, | |||
| 47 | } else if (const auto* flush = std::get_if<FlushRegionCommand>(&next.data)) { | 47 | } else if (const auto* flush = std::get_if<FlushRegionCommand>(&next.data)) { |
| 48 | rasterizer->FlushRegion(flush->addr, flush->size); | 48 | rasterizer->FlushRegion(flush->addr, flush->size); |
| 49 | } else if (const auto* invalidate = std::get_if<InvalidateRegionCommand>(&next.data)) { | 49 | } else if (const auto* invalidate = std::get_if<InvalidateRegionCommand>(&next.data)) { |
| 50 | rasterizer->OnCPUWrite(invalidate->addr, invalidate->size); | 50 | rasterizer->OnCacheInvalidation(invalidate->addr, invalidate->size); |
| 51 | } else { | 51 | } else { |
| 52 | ASSERT(false); | 52 | ASSERT(false); |
| 53 | } | 53 | } |
| @@ -102,12 +102,12 @@ void ThreadManager::TickGPU() { | |||
| 102 | } | 102 | } |
| 103 | 103 | ||
| 104 | void ThreadManager::InvalidateRegion(VAddr addr, u64 size) { | 104 | void ThreadManager::InvalidateRegion(VAddr addr, u64 size) { |
| 105 | rasterizer->OnCPUWrite(addr, size); | 105 | rasterizer->OnCacheInvalidation(addr, size); |
| 106 | } | 106 | } |
| 107 | 107 | ||
| 108 | void ThreadManager::FlushAndInvalidateRegion(VAddr addr, u64 size) { | 108 | void ThreadManager::FlushAndInvalidateRegion(VAddr addr, u64 size) { |
| 109 | // Skip flush on asynch mode, as FlushAndInvalidateRegion is not used for anything too important | 109 | // Skip flush on asynch mode, as FlushAndInvalidateRegion is not used for anything too important |
| 110 | rasterizer->OnCPUWrite(addr, size); | 110 | rasterizer->OnCacheInvalidation(addr, size); |
| 111 | } | 111 | } |
| 112 | 112 | ||
| 113 | u64 ThreadManager::PushCommand(CommandData&& command_data, bool block) { | 113 | u64 ThreadManager::PushCommand(CommandData&& command_data, bool block) { |
diff --git a/src/video_core/host1x/codecs/codec.cpp b/src/video_core/host1x/codecs/codec.cpp index cd6a3a9b8..da07a556f 100644 --- a/src/video_core/host1x/codecs/codec.cpp +++ b/src/video_core/host1x/codecs/codec.cpp | |||
| @@ -290,7 +290,7 @@ void Codec::Decode() { | |||
| 290 | return vp9_decoder->GetFrameBytes(); | 290 | return vp9_decoder->GetFrameBytes(); |
| 291 | default: | 291 | default: |
| 292 | ASSERT(false); | 292 | ASSERT(false); |
| 293 | return std::vector<u8>{}; | 293 | return std::span<const u8>{}; |
| 294 | } | 294 | } |
| 295 | }(); | 295 | }(); |
| 296 | AVPacketPtr packet{av_packet_alloc(), AVPacketDeleter}; | 296 | AVPacketPtr packet{av_packet_alloc(), AVPacketDeleter}; |
diff --git a/src/video_core/host1x/codecs/h264.cpp b/src/video_core/host1x/codecs/h264.cpp index ce827eb6c..862904e39 100644 --- a/src/video_core/host1x/codecs/h264.cpp +++ b/src/video_core/host1x/codecs/h264.cpp | |||
| @@ -29,15 +29,15 @@ H264::H264(Host1x::Host1x& host1x_) : host1x{host1x_} {} | |||
| 29 | 29 | ||
| 30 | H264::~H264() = default; | 30 | H264::~H264() = default; |
| 31 | 31 | ||
| 32 | const std::vector<u8>& H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state, | 32 | std::span<const u8> H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state, |
| 33 | bool is_first_frame) { | 33 | bool is_first_frame) { |
| 34 | H264DecoderContext context; | 34 | H264DecoderContext context; |
| 35 | host1x.MemoryManager().ReadBlock(state.picture_info_offset, &context, | 35 | host1x.MemoryManager().ReadBlock(state.picture_info_offset, &context, |
| 36 | sizeof(H264DecoderContext)); | 36 | sizeof(H264DecoderContext)); |
| 37 | 37 | ||
| 38 | const s64 frame_number = context.h264_parameter_set.frame_number.Value(); | 38 | const s64 frame_number = context.h264_parameter_set.frame_number.Value(); |
| 39 | if (!is_first_frame && frame_number != 0) { | 39 | if (!is_first_frame && frame_number != 0) { |
| 40 | frame.resize(context.stream_len); | 40 | frame.resize_destructive(context.stream_len); |
| 41 | host1x.MemoryManager().ReadBlock(state.frame_bitstream_offset, frame.data(), frame.size()); | 41 | host1x.MemoryManager().ReadBlock(state.frame_bitstream_offset, frame.data(), frame.size()); |
| 42 | return frame; | 42 | return frame; |
| 43 | } | 43 | } |
| @@ -135,14 +135,14 @@ const std::vector<u8>& H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegist | |||
| 135 | for (s32 index = 0; index < 6; index++) { | 135 | for (s32 index = 0; index < 6; index++) { |
| 136 | writer.WriteBit(true); | 136 | writer.WriteBit(true); |
| 137 | std::span<const u8> matrix{context.weight_scale}; | 137 | std::span<const u8> matrix{context.weight_scale}; |
| 138 | writer.WriteScalingList(matrix, index * 16, 16); | 138 | writer.WriteScalingList(scan, matrix, index * 16, 16); |
| 139 | } | 139 | } |
| 140 | 140 | ||
| 141 | if (context.h264_parameter_set.transform_8x8_mode_flag) { | 141 | if (context.h264_parameter_set.transform_8x8_mode_flag) { |
| 142 | for (s32 index = 0; index < 2; index++) { | 142 | for (s32 index = 0; index < 2; index++) { |
| 143 | writer.WriteBit(true); | 143 | writer.WriteBit(true); |
| 144 | std::span<const u8> matrix{context.weight_scale_8x8}; | 144 | std::span<const u8> matrix{context.weight_scale_8x8}; |
| 145 | writer.WriteScalingList(matrix, index * 64, 64); | 145 | writer.WriteScalingList(scan, matrix, index * 64, 64); |
| 146 | } | 146 | } |
| 147 | } | 147 | } |
| 148 | 148 | ||
| @@ -188,8 +188,8 @@ void H264BitWriter::WriteBit(bool state) { | |||
| 188 | WriteBits(state ? 1 : 0, 1); | 188 | WriteBits(state ? 1 : 0, 1); |
| 189 | } | 189 | } |
| 190 | 190 | ||
| 191 | void H264BitWriter::WriteScalingList(std::span<const u8> list, s32 start, s32 count) { | 191 | void H264BitWriter::WriteScalingList(Common::ScratchBuffer<u8>& scan, std::span<const u8> list, |
| 192 | static Common::ScratchBuffer<u8> scan{}; | 192 | s32 start, s32 count) { |
| 193 | scan.resize_destructive(count); | 193 | scan.resize_destructive(count); |
| 194 | if (count == 16) { | 194 | if (count == 16) { |
| 195 | std::memcpy(scan.data(), zig_zag_scan.data(), scan.size()); | 195 | std::memcpy(scan.data(), zig_zag_scan.data(), scan.size()); |
diff --git a/src/video_core/host1x/codecs/h264.h b/src/video_core/host1x/codecs/h264.h index 5cc86454e..d6b556322 100644 --- a/src/video_core/host1x/codecs/h264.h +++ b/src/video_core/host1x/codecs/h264.h | |||
| @@ -5,9 +5,11 @@ | |||
| 5 | 5 | ||
| 6 | #include <span> | 6 | #include <span> |
| 7 | #include <vector> | 7 | #include <vector> |
| 8 | |||
| 8 | #include "common/bit_field.h" | 9 | #include "common/bit_field.h" |
| 9 | #include "common/common_funcs.h" | 10 | #include "common/common_funcs.h" |
| 10 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | #include "common/scratch_buffer.h" | ||
| 11 | #include "video_core/host1x/nvdec_common.h" | 13 | #include "video_core/host1x/nvdec_common.h" |
| 12 | 14 | ||
| 13 | namespace Tegra { | 15 | namespace Tegra { |
| @@ -37,7 +39,8 @@ public: | |||
| 37 | 39 | ||
| 38 | /// Based on section 7.3.2.1.1.1 and Table 7-4 in the H.264 specification | 40 | /// Based on section 7.3.2.1.1.1 and Table 7-4 in the H.264 specification |
| 39 | /// Writes the scaling matrices of the sream | 41 | /// Writes the scaling matrices of the sream |
| 40 | void WriteScalingList(std::span<const u8> list, s32 start, s32 count); | 42 | void WriteScalingList(Common::ScratchBuffer<u8>& scan, std::span<const u8> list, s32 start, |
| 43 | s32 count); | ||
| 41 | 44 | ||
| 42 | /// Return the bitstream as a vector. | 45 | /// Return the bitstream as a vector. |
| 43 | [[nodiscard]] std::vector<u8>& GetByteArray(); | 46 | [[nodiscard]] std::vector<u8>& GetByteArray(); |
| @@ -63,11 +66,12 @@ public: | |||
| 63 | ~H264(); | 66 | ~H264(); |
| 64 | 67 | ||
| 65 | /// Compose the H264 frame for FFmpeg decoding | 68 | /// Compose the H264 frame for FFmpeg decoding |
| 66 | [[nodiscard]] const std::vector<u8>& ComposeFrame( | 69 | [[nodiscard]] std::span<const u8> ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state, |
| 67 | const Host1x::NvdecCommon::NvdecRegisters& state, bool is_first_frame = false); | 70 | bool is_first_frame = false); |
| 68 | 71 | ||
| 69 | private: | 72 | private: |
| 70 | std::vector<u8> frame; | 73 | Common::ScratchBuffer<u8> frame; |
| 74 | Common::ScratchBuffer<u8> scan; | ||
| 71 | Host1x::Host1x& host1x; | 75 | Host1x::Host1x& host1x; |
| 72 | 76 | ||
| 73 | struct H264ParameterSet { | 77 | struct H264ParameterSet { |
diff --git a/src/video_core/host1x/codecs/vp8.cpp b/src/video_core/host1x/codecs/vp8.cpp index 28fb12cb8..ee6392ff9 100644 --- a/src/video_core/host1x/codecs/vp8.cpp +++ b/src/video_core/host1x/codecs/vp8.cpp | |||
| @@ -12,7 +12,7 @@ VP8::VP8(Host1x::Host1x& host1x_) : host1x{host1x_} {} | |||
| 12 | 12 | ||
| 13 | VP8::~VP8() = default; | 13 | VP8::~VP8() = default; |
| 14 | 14 | ||
| 15 | const std::vector<u8>& VP8::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state) { | 15 | std::span<const u8> VP8::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state) { |
| 16 | VP8PictureInfo info; | 16 | VP8PictureInfo info; |
| 17 | host1x.MemoryManager().ReadBlock(state.picture_info_offset, &info, sizeof(VP8PictureInfo)); | 17 | host1x.MemoryManager().ReadBlock(state.picture_info_offset, &info, sizeof(VP8PictureInfo)); |
| 18 | 18 | ||
diff --git a/src/video_core/host1x/codecs/vp8.h b/src/video_core/host1x/codecs/vp8.h index 5bf07ecab..7926b73f3 100644 --- a/src/video_core/host1x/codecs/vp8.h +++ b/src/video_core/host1x/codecs/vp8.h | |||
| @@ -4,10 +4,11 @@ | |||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <array> | 6 | #include <array> |
| 7 | #include <vector> | 7 | #include <span> |
| 8 | 8 | ||
| 9 | #include "common/common_funcs.h" | 9 | #include "common/common_funcs.h" |
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "common/scratch_buffer.h" | ||
| 11 | #include "video_core/host1x/nvdec_common.h" | 12 | #include "video_core/host1x/nvdec_common.h" |
| 12 | 13 | ||
| 13 | namespace Tegra { | 14 | namespace Tegra { |
| @@ -24,11 +25,11 @@ public: | |||
| 24 | ~VP8(); | 25 | ~VP8(); |
| 25 | 26 | ||
| 26 | /// Compose the VP8 frame for FFmpeg decoding | 27 | /// Compose the VP8 frame for FFmpeg decoding |
| 27 | [[nodiscard]] const std::vector<u8>& ComposeFrame( | 28 | [[nodiscard]] std::span<const u8> ComposeFrame( |
| 28 | const Host1x::NvdecCommon::NvdecRegisters& state); | 29 | const Host1x::NvdecCommon::NvdecRegisters& state); |
| 29 | 30 | ||
| 30 | private: | 31 | private: |
| 31 | std::vector<u8> frame; | 32 | Common::ScratchBuffer<u8> frame; |
| 32 | Host1x::Host1x& host1x; | 33 | Host1x::Host1x& host1x; |
| 33 | 34 | ||
| 34 | struct VP8PictureInfo { | 35 | struct VP8PictureInfo { |
diff --git a/src/video_core/host1x/codecs/vp9.cpp b/src/video_core/host1x/codecs/vp9.cpp index cf40c9012..306c3d0e8 100644 --- a/src/video_core/host1x/codecs/vp9.cpp +++ b/src/video_core/host1x/codecs/vp9.cpp | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | #include <algorithm> // for std::copy | 4 | #include <algorithm> // for std::copy |
| 5 | #include <numeric> | 5 | #include <numeric> |
| 6 | |||
| 6 | #include "common/assert.h" | 7 | #include "common/assert.h" |
| 7 | #include "video_core/host1x/codecs/vp9.h" | 8 | #include "video_core/host1x/codecs/vp9.h" |
| 8 | #include "video_core/host1x/host1x.h" | 9 | #include "video_core/host1x/host1x.h" |
diff --git a/src/video_core/host1x/codecs/vp9.h b/src/video_core/host1x/codecs/vp9.h index d4083e8d3..f1ed19508 100644 --- a/src/video_core/host1x/codecs/vp9.h +++ b/src/video_core/host1x/codecs/vp9.h | |||
| @@ -4,9 +4,11 @@ | |||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <array> | 6 | #include <array> |
| 7 | #include <span> | ||
| 7 | #include <vector> | 8 | #include <vector> |
| 8 | 9 | ||
| 9 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "common/scratch_buffer.h" | ||
| 10 | #include "common/stream.h" | 12 | #include "common/stream.h" |
| 11 | #include "video_core/host1x/codecs/vp9_types.h" | 13 | #include "video_core/host1x/codecs/vp9_types.h" |
| 12 | #include "video_core/host1x/nvdec_common.h" | 14 | #include "video_core/host1x/nvdec_common.h" |
| @@ -128,8 +130,8 @@ public: | |||
| 128 | return !current_frame_info.show_frame; | 130 | return !current_frame_info.show_frame; |
| 129 | } | 131 | } |
| 130 | 132 | ||
| 131 | /// Returns a const reference to the composed frame data. | 133 | /// Returns a const span to the composed frame data. |
| 132 | [[nodiscard]] const std::vector<u8>& GetFrameBytes() const { | 134 | [[nodiscard]] std::span<const u8> GetFrameBytes() const { |
| 133 | return frame; | 135 | return frame; |
| 134 | } | 136 | } |
| 135 | 137 | ||
| @@ -181,7 +183,7 @@ private: | |||
| 181 | [[nodiscard]] VpxBitStreamWriter ComposeUncompressedHeader(); | 183 | [[nodiscard]] VpxBitStreamWriter ComposeUncompressedHeader(); |
| 182 | 184 | ||
| 183 | Host1x::Host1x& host1x; | 185 | Host1x::Host1x& host1x; |
| 184 | std::vector<u8> frame; | 186 | Common::ScratchBuffer<u8> frame; |
| 185 | 187 | ||
| 186 | std::array<s8, 4> loop_filter_ref_deltas{}; | 188 | std::array<s8, 4> loop_filter_ref_deltas{}; |
| 187 | std::array<s8, 2> loop_filter_mode_deltas{}; | 189 | std::array<s8, 2> loop_filter_mode_deltas{}; |
diff --git a/src/video_core/host1x/codecs/vp9_types.h b/src/video_core/host1x/codecs/vp9_types.h index adad8ed7e..cc9b25690 100644 --- a/src/video_core/host1x/codecs/vp9_types.h +++ b/src/video_core/host1x/codecs/vp9_types.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | 5 | ||
| 6 | #include <array> | 6 | #include <array> |
| 7 | #include <vector> | 7 | #include <vector> |
| 8 | |||
| 8 | #include "common/common_funcs.h" | 9 | #include "common/common_funcs.h" |
| 9 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 10 | 11 | ||
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h index 7566a8c4e..cb8029a4f 100644 --- a/src/video_core/rasterizer_interface.h +++ b/src/video_core/rasterizer_interface.h | |||
| @@ -109,7 +109,9 @@ public: | |||
| 109 | } | 109 | } |
| 110 | 110 | ||
| 111 | /// Notify rasterizer that any caches of the specified region are desync with guest | 111 | /// Notify rasterizer that any caches of the specified region are desync with guest |
| 112 | virtual void OnCPUWrite(VAddr addr, u64 size) = 0; | 112 | virtual void OnCacheInvalidation(VAddr addr, u64 size) = 0; |
| 113 | |||
| 114 | virtual bool OnCPUWrite(VAddr addr, u64 size) = 0; | ||
| 113 | 115 | ||
| 114 | /// Sync memory between guest and host. | 116 | /// Sync memory between guest and host. |
| 115 | virtual void InvalidateGPUCache() = 0; | 117 | virtual void InvalidateGPUCache() = 0; |
diff --git a/src/video_core/renderer_null/null_rasterizer.cpp b/src/video_core/renderer_null/null_rasterizer.cpp index bf2ce4c49..92ecf6682 100644 --- a/src/video_core/renderer_null/null_rasterizer.cpp +++ b/src/video_core/renderer_null/null_rasterizer.cpp | |||
| @@ -47,7 +47,10 @@ bool RasterizerNull::MustFlushRegion(VAddr addr, u64 size, VideoCommon::CacheTyp | |||
| 47 | return false; | 47 | return false; |
| 48 | } | 48 | } |
| 49 | void RasterizerNull::InvalidateRegion(VAddr addr, u64 size, VideoCommon::CacheType) {} | 49 | void RasterizerNull::InvalidateRegion(VAddr addr, u64 size, VideoCommon::CacheType) {} |
| 50 | void RasterizerNull::OnCPUWrite(VAddr addr, u64 size) {} | 50 | bool RasterizerNull::OnCPUWrite(VAddr addr, u64 size) { |
| 51 | return false; | ||
| 52 | } | ||
| 53 | void RasterizerNull::OnCacheInvalidation(VAddr addr, u64 size) {} | ||
| 51 | VideoCore::RasterizerDownloadArea RasterizerNull::GetFlushArea(VAddr addr, u64 size) { | 54 | VideoCore::RasterizerDownloadArea RasterizerNull::GetFlushArea(VAddr addr, u64 size) { |
| 52 | VideoCore::RasterizerDownloadArea new_area{ | 55 | VideoCore::RasterizerDownloadArea new_area{ |
| 53 | .start_address = Common::AlignDown(addr, Core::Memory::YUZU_PAGESIZE), | 56 | .start_address = Common::AlignDown(addr, Core::Memory::YUZU_PAGESIZE), |
diff --git a/src/video_core/renderer_null/null_rasterizer.h b/src/video_core/renderer_null/null_rasterizer.h index a8d35d2c1..93b9a6971 100644 --- a/src/video_core/renderer_null/null_rasterizer.h +++ b/src/video_core/renderer_null/null_rasterizer.h | |||
| @@ -53,7 +53,8 @@ public: | |||
| 53 | VideoCommon::CacheType which = VideoCommon::CacheType::All) override; | 53 | VideoCommon::CacheType which = VideoCommon::CacheType::All) override; |
| 54 | void InvalidateRegion(VAddr addr, u64 size, | 54 | void InvalidateRegion(VAddr addr, u64 size, |
| 55 | VideoCommon::CacheType which = VideoCommon::CacheType::All) override; | 55 | VideoCommon::CacheType which = VideoCommon::CacheType::All) override; |
| 56 | void OnCPUWrite(VAddr addr, u64 size) override; | 56 | void OnCacheInvalidation(VAddr addr, u64 size) override; |
| 57 | bool OnCPUWrite(VAddr addr, u64 size) override; | ||
| 57 | VideoCore::RasterizerDownloadArea GetFlushArea(VAddr addr, u64 size) override; | 58 | VideoCore::RasterizerDownloadArea GetFlushArea(VAddr addr, u64 size) override; |
| 58 | void InvalidateGPUCache() override; | 59 | void InvalidateGPUCache() override; |
| 59 | void UnmapMemory(VAddr addr, u64 size) override; | 60 | void UnmapMemory(VAddr addr, u64 size) override; |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index edf527f2d..aadd6967c 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -485,12 +485,33 @@ void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size, VideoCommon::Cache | |||
| 485 | } | 485 | } |
| 486 | } | 486 | } |
| 487 | 487 | ||
| 488 | void RasterizerOpenGL::OnCPUWrite(VAddr addr, u64 size) { | 488 | bool RasterizerOpenGL::OnCPUWrite(VAddr addr, u64 size) { |
| 489 | MICROPROFILE_SCOPE(OpenGL_CacheManagement); | ||
| 490 | if (addr == 0 || size == 0) { | ||
| 491 | return false; | ||
| 492 | } | ||
| 493 | |||
| 494 | { | ||
| 495 | std::scoped_lock lock{buffer_cache.mutex}; | ||
| 496 | if (buffer_cache.OnCPUWrite(addr, size)) { | ||
| 497 | return true; | ||
| 498 | } | ||
| 499 | } | ||
| 500 | |||
| 501 | { | ||
| 502 | std::scoped_lock lock{texture_cache.mutex}; | ||
| 503 | texture_cache.WriteMemory(addr, size); | ||
| 504 | } | ||
| 505 | |||
| 506 | shader_cache.InvalidateRegion(addr, size); | ||
| 507 | return false; | ||
| 508 | } | ||
| 509 | |||
| 510 | void RasterizerOpenGL::OnCacheInvalidation(VAddr addr, u64 size) { | ||
| 489 | MICROPROFILE_SCOPE(OpenGL_CacheManagement); | 511 | MICROPROFILE_SCOPE(OpenGL_CacheManagement); |
| 490 | if (addr == 0 || size == 0) { | 512 | if (addr == 0 || size == 0) { |
| 491 | return; | 513 | return; |
| 492 | } | 514 | } |
| 493 | shader_cache.OnCPUWrite(addr, size); | ||
| 494 | { | 515 | { |
| 495 | std::scoped_lock lock{texture_cache.mutex}; | 516 | std::scoped_lock lock{texture_cache.mutex}; |
| 496 | texture_cache.WriteMemory(addr, size); | 517 | texture_cache.WriteMemory(addr, size); |
| @@ -499,15 +520,11 @@ void RasterizerOpenGL::OnCPUWrite(VAddr addr, u64 size) { | |||
| 499 | std::scoped_lock lock{buffer_cache.mutex}; | 520 | std::scoped_lock lock{buffer_cache.mutex}; |
| 500 | buffer_cache.CachedWriteMemory(addr, size); | 521 | buffer_cache.CachedWriteMemory(addr, size); |
| 501 | } | 522 | } |
| 523 | shader_cache.InvalidateRegion(addr, size); | ||
| 502 | } | 524 | } |
| 503 | 525 | ||
| 504 | void RasterizerOpenGL::InvalidateGPUCache() { | 526 | void RasterizerOpenGL::InvalidateGPUCache() { |
| 505 | MICROPROFILE_SCOPE(OpenGL_CacheManagement); | 527 | gpu.InvalidateGPUCache(); |
| 506 | shader_cache.SyncGuestHost(); | ||
| 507 | { | ||
| 508 | std::scoped_lock lock{buffer_cache.mutex}; | ||
| 509 | buffer_cache.FlushCachedWrites(); | ||
| 510 | } | ||
| 511 | } | 528 | } |
| 512 | 529 | ||
| 513 | void RasterizerOpenGL::UnmapMemory(VAddr addr, u64 size) { | 530 | void RasterizerOpenGL::UnmapMemory(VAddr addr, u64 size) { |
| @@ -519,7 +536,7 @@ void RasterizerOpenGL::UnmapMemory(VAddr addr, u64 size) { | |||
| 519 | std::scoped_lock lock{buffer_cache.mutex}; | 536 | std::scoped_lock lock{buffer_cache.mutex}; |
| 520 | buffer_cache.WriteMemory(addr, size); | 537 | buffer_cache.WriteMemory(addr, size); |
| 521 | } | 538 | } |
| 522 | shader_cache.OnCPUWrite(addr, size); | 539 | shader_cache.OnCacheInvalidation(addr, size); |
| 523 | } | 540 | } |
| 524 | 541 | ||
| 525 | void RasterizerOpenGL::ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) { | 542 | void RasterizerOpenGL::ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) { |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index a73ad15c1..8eda2ddba 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h | |||
| @@ -98,7 +98,8 @@ public: | |||
| 98 | VideoCore::RasterizerDownloadArea GetFlushArea(VAddr addr, u64 size) override; | 98 | VideoCore::RasterizerDownloadArea GetFlushArea(VAddr addr, u64 size) override; |
| 99 | void InvalidateRegion(VAddr addr, u64 size, | 99 | void InvalidateRegion(VAddr addr, u64 size, |
| 100 | VideoCommon::CacheType which = VideoCommon::CacheType::All) override; | 100 | VideoCommon::CacheType which = VideoCommon::CacheType::All) override; |
| 101 | void OnCPUWrite(VAddr addr, u64 size) override; | 101 | void OnCacheInvalidation(VAddr addr, u64 size) override; |
| 102 | bool OnCPUWrite(VAddr addr, u64 size) override; | ||
| 102 | void InvalidateGPUCache() override; | 103 | void InvalidateGPUCache() override; |
| 103 | void UnmapMemory(VAddr addr, u64 size) override; | 104 | void UnmapMemory(VAddr addr, u64 size) override; |
| 104 | void ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) override; | 105 | void ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) override; |
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index b72f95235..51df18ec3 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp | |||
| @@ -591,7 +591,7 @@ void BufferCacheRuntime::ReserveNullBuffer() { | |||
| 591 | .flags = 0, | 591 | .flags = 0, |
| 592 | .size = 4, | 592 | .size = 4, |
| 593 | .usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | | 593 | .usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | |
| 594 | VK_BUFFER_USAGE_TRANSFER_DST_BIT, | 594 | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT, |
| 595 | .sharingMode = VK_SHARING_MODE_EXCLUSIVE, | 595 | .sharingMode = VK_SHARING_MODE_EXCLUSIVE, |
| 596 | .queueFamilyIndexCount = 0, | 596 | .queueFamilyIndexCount = 0, |
| 597 | .pQueueFamilyIndices = nullptr, | 597 | .pQueueFamilyIndices = nullptr, |
| @@ -599,7 +599,6 @@ void BufferCacheRuntime::ReserveNullBuffer() { | |||
| 599 | if (device.IsExtTransformFeedbackSupported()) { | 599 | if (device.IsExtTransformFeedbackSupported()) { |
| 600 | create_info.usage |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT; | 600 | create_info.usage |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT; |
| 601 | } | 601 | } |
| 602 | create_info.usage |= VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT; | ||
| 603 | null_buffer = memory_allocator.CreateBuffer(create_info, MemoryUsage::DeviceLocal); | 602 | null_buffer = memory_allocator.CreateBuffer(create_info, MemoryUsage::DeviceLocal); |
| 604 | if (device.HasDebuggingToolAttached()) { | 603 | if (device.HasDebuggingToolAttached()) { |
| 605 | null_buffer.SetObjectNameEXT("Null buffer"); | 604 | null_buffer.SetObjectNameEXT("Null buffer"); |
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index f7c0d939a..456bb040e 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp | |||
| @@ -566,11 +566,32 @@ void RasterizerVulkan::InnerInvalidation(std::span<const std::pair<VAddr, std::s | |||
| 566 | } | 566 | } |
| 567 | } | 567 | } |
| 568 | 568 | ||
| 569 | void RasterizerVulkan::OnCPUWrite(VAddr addr, u64 size) { | 569 | bool RasterizerVulkan::OnCPUWrite(VAddr addr, u64 size) { |
| 570 | if (addr == 0 || size == 0) { | ||
| 571 | return false; | ||
| 572 | } | ||
| 573 | |||
| 574 | { | ||
| 575 | std::scoped_lock lock{buffer_cache.mutex}; | ||
| 576 | if (buffer_cache.OnCPUWrite(addr, size)) { | ||
| 577 | return true; | ||
| 578 | } | ||
| 579 | } | ||
| 580 | |||
| 581 | { | ||
| 582 | std::scoped_lock lock{texture_cache.mutex}; | ||
| 583 | texture_cache.WriteMemory(addr, size); | ||
| 584 | } | ||
| 585 | |||
| 586 | pipeline_cache.InvalidateRegion(addr, size); | ||
| 587 | return false; | ||
| 588 | } | ||
| 589 | |||
| 590 | void RasterizerVulkan::OnCacheInvalidation(VAddr addr, u64 size) { | ||
| 570 | if (addr == 0 || size == 0) { | 591 | if (addr == 0 || size == 0) { |
| 571 | return; | 592 | return; |
| 572 | } | 593 | } |
| 573 | pipeline_cache.OnCPUWrite(addr, size); | 594 | |
| 574 | { | 595 | { |
| 575 | std::scoped_lock lock{texture_cache.mutex}; | 596 | std::scoped_lock lock{texture_cache.mutex}; |
| 576 | texture_cache.WriteMemory(addr, size); | 597 | texture_cache.WriteMemory(addr, size); |
| @@ -579,14 +600,11 @@ void RasterizerVulkan::OnCPUWrite(VAddr addr, u64 size) { | |||
| 579 | std::scoped_lock lock{buffer_cache.mutex}; | 600 | std::scoped_lock lock{buffer_cache.mutex}; |
| 580 | buffer_cache.CachedWriteMemory(addr, size); | 601 | buffer_cache.CachedWriteMemory(addr, size); |
| 581 | } | 602 | } |
| 603 | pipeline_cache.InvalidateRegion(addr, size); | ||
| 582 | } | 604 | } |
| 583 | 605 | ||
| 584 | void RasterizerVulkan::InvalidateGPUCache() { | 606 | void RasterizerVulkan::InvalidateGPUCache() { |
| 585 | pipeline_cache.SyncGuestHost(); | 607 | gpu.InvalidateGPUCache(); |
| 586 | { | ||
| 587 | std::scoped_lock lock{buffer_cache.mutex}; | ||
| 588 | buffer_cache.FlushCachedWrites(); | ||
| 589 | } | ||
| 590 | } | 608 | } |
| 591 | 609 | ||
| 592 | void RasterizerVulkan::UnmapMemory(VAddr addr, u64 size) { | 610 | void RasterizerVulkan::UnmapMemory(VAddr addr, u64 size) { |
| @@ -598,7 +616,7 @@ void RasterizerVulkan::UnmapMemory(VAddr addr, u64 size) { | |||
| 598 | std::scoped_lock lock{buffer_cache.mutex}; | 616 | std::scoped_lock lock{buffer_cache.mutex}; |
| 599 | buffer_cache.WriteMemory(addr, size); | 617 | buffer_cache.WriteMemory(addr, size); |
| 600 | } | 618 | } |
| 601 | pipeline_cache.OnCPUWrite(addr, size); | 619 | pipeline_cache.OnCacheInvalidation(addr, size); |
| 602 | } | 620 | } |
| 603 | 621 | ||
| 604 | void RasterizerVulkan::ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) { | 622 | void RasterizerVulkan::ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) { |
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index b39710b3c..73257d964 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h | |||
| @@ -96,7 +96,8 @@ public: | |||
| 96 | void InvalidateRegion(VAddr addr, u64 size, | 96 | void InvalidateRegion(VAddr addr, u64 size, |
| 97 | VideoCommon::CacheType which = VideoCommon::CacheType::All) override; | 97 | VideoCommon::CacheType which = VideoCommon::CacheType::All) override; |
| 98 | void InnerInvalidation(std::span<const std::pair<VAddr, std::size_t>> sequences) override; | 98 | void InnerInvalidation(std::span<const std::pair<VAddr, std::size_t>> sequences) override; |
| 99 | void OnCPUWrite(VAddr addr, u64 size) override; | 99 | void OnCacheInvalidation(VAddr addr, u64 size) override; |
| 100 | bool OnCPUWrite(VAddr addr, u64 size) override; | ||
| 100 | void InvalidateGPUCache() override; | 101 | void InvalidateGPUCache() override; |
| 101 | void UnmapMemory(VAddr addr, u64 size) override; | 102 | void UnmapMemory(VAddr addr, u64 size) override; |
| 102 | void ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) override; | 103 | void ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) override; |
diff --git a/src/video_core/shader_cache.cpp b/src/video_core/shader_cache.cpp index 4db948b6d..01701201d 100644 --- a/src/video_core/shader_cache.cpp +++ b/src/video_core/shader_cache.cpp | |||
| @@ -24,7 +24,7 @@ void ShaderCache::InvalidateRegion(VAddr addr, size_t size) { | |||
| 24 | RemovePendingShaders(); | 24 | RemovePendingShaders(); |
| 25 | } | 25 | } |
| 26 | 26 | ||
| 27 | void ShaderCache::OnCPUWrite(VAddr addr, size_t size) { | 27 | void ShaderCache::OnCacheInvalidation(VAddr addr, size_t size) { |
| 28 | std::scoped_lock lock{invalidation_mutex}; | 28 | std::scoped_lock lock{invalidation_mutex}; |
| 29 | InvalidatePagesInRegion(addr, size); | 29 | InvalidatePagesInRegion(addr, size); |
| 30 | } | 30 | } |
diff --git a/src/video_core/shader_cache.h b/src/video_core/shader_cache.h index f3cc4c70b..de8e08002 100644 --- a/src/video_core/shader_cache.h +++ b/src/video_core/shader_cache.h | |||
| @@ -62,7 +62,7 @@ public: | |||
| 62 | /// @brief Unmarks a memory region as cached and marks it for removal | 62 | /// @brief Unmarks a memory region as cached and marks it for removal |
| 63 | /// @param addr Start address of the CPU write operation | 63 | /// @param addr Start address of the CPU write operation |
| 64 | /// @param size Number of bytes of the CPU write operation | 64 | /// @param size Number of bytes of the CPU write operation |
| 65 | void OnCPUWrite(VAddr addr, size_t size); | 65 | void OnCacheInvalidation(VAddr addr, size_t size); |
| 66 | 66 | ||
| 67 | /// @brief Flushes delayed removal operations | 67 | /// @brief Flushes delayed removal operations |
| 68 | void SyncGuestHost(); | 68 | void SyncGuestHost(); |
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 8190f3ba1..79f158db4 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h | |||
| @@ -865,11 +865,15 @@ void TextureCache<P>::PopAsyncFlushes() { | |||
| 865 | template <class P> | 865 | template <class P> |
| 866 | ImageId TextureCache<P>::DmaImageId(const Tegra::DMA::ImageOperand& operand, bool is_upload) { | 866 | ImageId TextureCache<P>::DmaImageId(const Tegra::DMA::ImageOperand& operand, bool is_upload) { |
| 867 | const ImageInfo dst_info(operand); | 867 | const ImageInfo dst_info(operand); |
| 868 | const ImageId image_id = FindDMAImage(dst_info, operand.address); | 868 | const ImageId dst_id = FindDMAImage(dst_info, operand.address); |
| 869 | if (!image_id) { | 869 | if (!dst_id) { |
| 870 | return NULL_IMAGE_ID; | ||
| 871 | } | ||
| 872 | auto& image = slot_images[dst_id]; | ||
| 873 | if (False(image.flags & ImageFlagBits::GpuModified)) { | ||
| 874 | // No need to waste time on an image that's synced with guest | ||
| 870 | return NULL_IMAGE_ID; | 875 | return NULL_IMAGE_ID; |
| 871 | } | 876 | } |
| 872 | auto& image = slot_images[image_id]; | ||
| 873 | if (image.info.type == ImageType::e3D) { | 877 | if (image.info.type == ImageType::e3D) { |
| 874 | // Don't accelerate 3D images. | 878 | // Don't accelerate 3D images. |
| 875 | return NULL_IMAGE_ID; | 879 | return NULL_IMAGE_ID; |
| @@ -883,7 +887,7 @@ ImageId TextureCache<P>::DmaImageId(const Tegra::DMA::ImageOperand& operand, boo | |||
| 883 | if (!base) { | 887 | if (!base) { |
| 884 | return NULL_IMAGE_ID; | 888 | return NULL_IMAGE_ID; |
| 885 | } | 889 | } |
| 886 | return image_id; | 890 | return dst_id; |
| 887 | } | 891 | } |
| 888 | 892 | ||
| 889 | template <class P> | 893 | template <class P> |
diff --git a/src/video_core/vulkan_common/vulkan_instance.cpp b/src/video_core/vulkan_common/vulkan_instance.cpp index 7624a9b32..6a294c1da 100644 --- a/src/video_core/vulkan_common/vulkan_instance.cpp +++ b/src/video_core/vulkan_common/vulkan_instance.cpp | |||
| @@ -19,11 +19,9 @@ | |||
| 19 | #include <windows.h> | 19 | #include <windows.h> |
| 20 | // ensure include order | 20 | // ensure include order |
| 21 | #include <vulkan/vulkan_win32.h> | 21 | #include <vulkan/vulkan_win32.h> |
| 22 | #elif defined(__APPLE__) | ||
| 23 | #include <vulkan/vulkan_macos.h> | ||
| 24 | #elif defined(__ANDROID__) | 22 | #elif defined(__ANDROID__) |
| 25 | #include <vulkan/vulkan_android.h> | 23 | #include <vulkan/vulkan_android.h> |
| 26 | #else | 24 | #elif !defined(__APPLE__) |
| 27 | #include <X11/Xlib.h> | 25 | #include <X11/Xlib.h> |
| 28 | #include <vulkan/vulkan_wayland.h> | 26 | #include <vulkan/vulkan_wayland.h> |
| 29 | #include <vulkan/vulkan_xlib.h> | 27 | #include <vulkan/vulkan_xlib.h> |
| @@ -68,7 +66,7 @@ namespace { | |||
| 68 | break; | 66 | break; |
| 69 | #elif defined(__APPLE__) | 67 | #elif defined(__APPLE__) |
| 70 | case Core::Frontend::WindowSystemType::Cocoa: | 68 | case Core::Frontend::WindowSystemType::Cocoa: |
| 71 | extensions.push_back(VK_MVK_MACOS_SURFACE_EXTENSION_NAME); | 69 | extensions.push_back(VK_EXT_METAL_SURFACE_EXTENSION_NAME); |
| 72 | break; | 70 | break; |
| 73 | #elif defined(__ANDROID__) | 71 | #elif defined(__ANDROID__) |
| 74 | case Core::Frontend::WindowSystemType::Android: | 72 | case Core::Frontend::WindowSystemType::Android: |
diff --git a/src/video_core/vulkan_common/vulkan_surface.cpp b/src/video_core/vulkan_common/vulkan_surface.cpp index c34599365..cfea4cd7b 100644 --- a/src/video_core/vulkan_common/vulkan_surface.cpp +++ b/src/video_core/vulkan_common/vulkan_surface.cpp | |||
| @@ -11,11 +11,9 @@ | |||
| 11 | #include <windows.h> | 11 | #include <windows.h> |
| 12 | // ensure include order | 12 | // ensure include order |
| 13 | #include <vulkan/vulkan_win32.h> | 13 | #include <vulkan/vulkan_win32.h> |
| 14 | #elif defined(__APPLE__) | ||
| 15 | #include <vulkan/vulkan_macos.h> | ||
| 16 | #elif defined(__ANDROID__) | 14 | #elif defined(__ANDROID__) |
| 17 | #include <vulkan/vulkan_android.h> | 15 | #include <vulkan/vulkan_android.h> |
| 18 | #else | 16 | #elif !defined(__APPLE__) |
| 19 | #include <X11/Xlib.h> | 17 | #include <X11/Xlib.h> |
| 20 | #include <vulkan/vulkan_wayland.h> | 18 | #include <vulkan/vulkan_wayland.h> |
| 21 | #include <vulkan/vulkan_xlib.h> | 19 | #include <vulkan/vulkan_xlib.h> |
| @@ -44,12 +42,13 @@ vk::SurfaceKHR CreateSurface( | |||
| 44 | } | 42 | } |
| 45 | #elif defined(__APPLE__) | 43 | #elif defined(__APPLE__) |
| 46 | if (window_info.type == Core::Frontend::WindowSystemType::Cocoa) { | 44 | if (window_info.type == Core::Frontend::WindowSystemType::Cocoa) { |
| 47 | const VkMacOSSurfaceCreateInfoMVK mvk_ci{VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK, | 45 | const VkMetalSurfaceCreateInfoEXT macos_ci = { |
| 48 | nullptr, 0, window_info.render_surface}; | 46 | .pLayer = static_cast<const CAMetalLayer*>(window_info.render_surface), |
| 49 | const auto vkCreateMacOSSurfaceMVK = reinterpret_cast<PFN_vkCreateMacOSSurfaceMVK>( | 47 | }; |
| 50 | dld.vkGetInstanceProcAddr(*instance, "vkCreateMacOSSurfaceMVK")); | 48 | const auto vkCreateMetalSurfaceEXT = reinterpret_cast<PFN_vkCreateMetalSurfaceEXT>( |
| 51 | if (!vkCreateMacOSSurfaceMVK || | 49 | dld.vkGetInstanceProcAddr(*instance, "vkCreateMetalSurfaceEXT")); |
| 52 | vkCreateMacOSSurfaceMVK(*instance, &mvk_ci, nullptr, &unsafe_surface) != VK_SUCCESS) { | 50 | if (!vkCreateMetalSurfaceEXT || |
| 51 | vkCreateMetalSurfaceEXT(*instance, &macos_ci, nullptr, &unsafe_surface) != VK_SUCCESS) { | ||
| 53 | LOG_ERROR(Render_Vulkan, "Failed to initialize Metal surface"); | 52 | LOG_ERROR(Render_Vulkan, "Failed to initialize Metal surface"); |
| 54 | throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); | 53 | throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); |
| 55 | } | 54 | } |
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h index b5e70fcd4..32bd75ad8 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.h +++ b/src/video_core/vulkan_common/vulkan_wrapper.h | |||
| @@ -15,6 +15,8 @@ | |||
| 15 | #define VK_NO_PROTOTYPES | 15 | #define VK_NO_PROTOTYPES |
| 16 | #ifdef _WIN32 | 16 | #ifdef _WIN32 |
| 17 | #define VK_USE_PLATFORM_WIN32_KHR | 17 | #define VK_USE_PLATFORM_WIN32_KHR |
| 18 | #elif defined(__APPLE__) | ||
| 19 | #define VK_USE_PLATFORM_METAL_EXT | ||
| 18 | #endif | 20 | #endif |
| 19 | #include <vulkan/vulkan.h> | 21 | #include <vulkan/vulkan.h> |
| 20 | 22 | ||
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 29467d380..195d3556c 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -503,8 +503,7 @@ void Config::ReadMousePanningValues() { | |||
| 503 | ReadBasicSetting(Settings::values.mouse_panning); | 503 | ReadBasicSetting(Settings::values.mouse_panning); |
| 504 | ReadBasicSetting(Settings::values.mouse_panning_x_sensitivity); | 504 | ReadBasicSetting(Settings::values.mouse_panning_x_sensitivity); |
| 505 | ReadBasicSetting(Settings::values.mouse_panning_y_sensitivity); | 505 | ReadBasicSetting(Settings::values.mouse_panning_y_sensitivity); |
| 506 | ReadBasicSetting(Settings::values.mouse_panning_deadzone_x_counterweight); | 506 | ReadBasicSetting(Settings::values.mouse_panning_deadzone_counterweight); |
| 507 | ReadBasicSetting(Settings::values.mouse_panning_deadzone_y_counterweight); | ||
| 508 | ReadBasicSetting(Settings::values.mouse_panning_decay_strength); | 507 | ReadBasicSetting(Settings::values.mouse_panning_decay_strength); |
| 509 | ReadBasicSetting(Settings::values.mouse_panning_min_decay); | 508 | ReadBasicSetting(Settings::values.mouse_panning_min_decay); |
| 510 | } | 509 | } |
| @@ -1122,8 +1121,7 @@ void Config::SaveMousePanningValues() { | |||
| 1122 | // Don't overwrite values.mouse_panning | 1121 | // Don't overwrite values.mouse_panning |
| 1123 | WriteBasicSetting(Settings::values.mouse_panning_x_sensitivity); | 1122 | WriteBasicSetting(Settings::values.mouse_panning_x_sensitivity); |
| 1124 | WriteBasicSetting(Settings::values.mouse_panning_y_sensitivity); | 1123 | WriteBasicSetting(Settings::values.mouse_panning_y_sensitivity); |
| 1125 | WriteBasicSetting(Settings::values.mouse_panning_deadzone_x_counterweight); | 1124 | WriteBasicSetting(Settings::values.mouse_panning_deadzone_counterweight); |
| 1126 | WriteBasicSetting(Settings::values.mouse_panning_deadzone_y_counterweight); | ||
| 1127 | WriteBasicSetting(Settings::values.mouse_panning_decay_strength); | 1125 | WriteBasicSetting(Settings::values.mouse_panning_decay_strength); |
| 1128 | WriteBasicSetting(Settings::values.mouse_panning_min_decay); | 1126 | WriteBasicSetting(Settings::values.mouse_panning_min_decay); |
| 1129 | } | 1127 | } |
diff --git a/src/yuzu/configuration/configure_input_player.ui b/src/yuzu/configuration/configure_input_player.ui index 43f6c7b50..611a79477 100644 --- a/src/yuzu/configuration/configure_input_player.ui +++ b/src/yuzu/configuration/configure_input_player.ui | |||
| @@ -3105,21 +3105,6 @@ | |||
| 3105 | </property> | 3105 | </property> |
| 3106 | <item> | 3106 | <item> |
| 3107 | <widget class="QPushButton" name="mousePanningButton"> | 3107 | <widget class="QPushButton" name="mousePanningButton"> |
| 3108 | <property name="minimumSize"> | ||
| 3109 | <size> | ||
| 3110 | <width>68</width> | ||
| 3111 | <height>0</height> | ||
| 3112 | </size> | ||
| 3113 | </property> | ||
| 3114 | <property name="maximumSize"> | ||
| 3115 | <size> | ||
| 3116 | <width>68</width> | ||
| 3117 | <height>16777215</height> | ||
| 3118 | </size> | ||
| 3119 | </property> | ||
| 3120 | <property name="styleSheet"> | ||
| 3121 | <string notr="true">min-width: 68px;</string> | ||
| 3122 | </property> | ||
| 3123 | <property name="text"> | 3108 | <property name="text"> |
| 3124 | <string>Configure</string> | 3109 | <string>Configure</string> |
| 3125 | </property> | 3110 | </property> |
diff --git a/src/yuzu/configuration/configure_mouse_panning.cpp b/src/yuzu/configuration/configure_mouse_panning.cpp index f183d2740..e37c546b0 100644 --- a/src/yuzu/configuration/configure_mouse_panning.cpp +++ b/src/yuzu/configuration/configure_mouse_panning.cpp | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include <QCloseEvent> | 4 | #include <QCloseEvent> |
| 5 | #include <QMessageBox> | ||
| 5 | 6 | ||
| 6 | #include "common/settings.h" | 7 | #include "common/settings.h" |
| 7 | #include "ui_configure_mouse_panning.h" | 8 | #include "ui_configure_mouse_panning.h" |
| @@ -27,31 +28,34 @@ void ConfigureMousePanning::SetConfiguration(float right_stick_deadzone, float r | |||
| 27 | ui->enable->setChecked(Settings::values.mouse_panning.GetValue()); | 28 | ui->enable->setChecked(Settings::values.mouse_panning.GetValue()); |
| 28 | ui->x_sensitivity->setValue(Settings::values.mouse_panning_x_sensitivity.GetValue()); | 29 | ui->x_sensitivity->setValue(Settings::values.mouse_panning_x_sensitivity.GetValue()); |
| 29 | ui->y_sensitivity->setValue(Settings::values.mouse_panning_y_sensitivity.GetValue()); | 30 | ui->y_sensitivity->setValue(Settings::values.mouse_panning_y_sensitivity.GetValue()); |
| 30 | ui->deadzone_x_counterweight->setValue( | 31 | ui->deadzone_counterweight->setValue( |
| 31 | Settings::values.mouse_panning_deadzone_x_counterweight.GetValue()); | 32 | Settings::values.mouse_panning_deadzone_counterweight.GetValue()); |
| 32 | ui->deadzone_y_counterweight->setValue( | ||
| 33 | Settings::values.mouse_panning_deadzone_y_counterweight.GetValue()); | ||
| 34 | ui->decay_strength->setValue(Settings::values.mouse_panning_decay_strength.GetValue()); | 33 | ui->decay_strength->setValue(Settings::values.mouse_panning_decay_strength.GetValue()); |
| 35 | ui->min_decay->setValue(Settings::values.mouse_panning_min_decay.GetValue()); | 34 | ui->min_decay->setValue(Settings::values.mouse_panning_min_decay.GetValue()); |
| 36 | 35 | ||
| 37 | if (right_stick_deadzone > 0.0f || right_stick_range != 1.0f) { | 36 | if (right_stick_deadzone > 0.0f || right_stick_range != 1.0f) { |
| 38 | ui->warning_label->setText(QString::fromStdString( | 37 | const QString right_stick_deadzone_str = |
| 39 | "Mouse panning works better with a deadzone of 0% and a range of 100%.\n" | 38 | QString::fromStdString(std::to_string(static_cast<int>(right_stick_deadzone * 100.0f))); |
| 40 | "Current values are " + | 39 | const QString right_stick_range_str = |
| 41 | std::to_string(static_cast<int>(right_stick_deadzone * 100.0f)) + "% and " + | 40 | QString::fromStdString(std::to_string(static_cast<int>(right_stick_range * 100.0f))); |
| 42 | std::to_string(static_cast<int>(right_stick_range * 100.0f)) + "% respectively.")); | 41 | |
| 43 | } else { | 42 | ui->warning_label->setText( |
| 44 | ui->warning_label->hide(); | 43 | tr("Mouse panning works better with a deadzone of 0% and a range of 100%.\nCurrent " |
| 44 | "values are %1% and %2% respectively.") | ||
| 45 | .arg(right_stick_deadzone_str, right_stick_range_str)); | ||
| 46 | } | ||
| 47 | |||
| 48 | if (Settings::values.mouse_enabled) { | ||
| 49 | ui->warning_label->setText( | ||
| 50 | tr("Emulated mouse is enabled. This is incompatible with mouse panning.")); | ||
| 45 | } | 51 | } |
| 46 | } | 52 | } |
| 47 | 53 | ||
| 48 | void ConfigureMousePanning::SetDefaultConfiguration() { | 54 | void ConfigureMousePanning::SetDefaultConfiguration() { |
| 49 | ui->x_sensitivity->setValue(Settings::values.mouse_panning_x_sensitivity.GetDefault()); | 55 | ui->x_sensitivity->setValue(Settings::values.mouse_panning_x_sensitivity.GetDefault()); |
| 50 | ui->y_sensitivity->setValue(Settings::values.mouse_panning_y_sensitivity.GetDefault()); | 56 | ui->y_sensitivity->setValue(Settings::values.mouse_panning_y_sensitivity.GetDefault()); |
| 51 | ui->deadzone_x_counterweight->setValue( | 57 | ui->deadzone_counterweight->setValue( |
| 52 | Settings::values.mouse_panning_deadzone_x_counterweight.GetDefault()); | 58 | Settings::values.mouse_panning_deadzone_counterweight.GetDefault()); |
| 53 | ui->deadzone_y_counterweight->setValue( | ||
| 54 | Settings::values.mouse_panning_deadzone_y_counterweight.GetDefault()); | ||
| 55 | ui->decay_strength->setValue(Settings::values.mouse_panning_decay_strength.GetDefault()); | 59 | ui->decay_strength->setValue(Settings::values.mouse_panning_decay_strength.GetDefault()); |
| 56 | ui->min_decay->setValue(Settings::values.mouse_panning_min_decay.GetDefault()); | 60 | ui->min_decay->setValue(Settings::values.mouse_panning_min_decay.GetDefault()); |
| 57 | } | 61 | } |
| @@ -68,12 +72,19 @@ void ConfigureMousePanning::ApplyConfiguration() { | |||
| 68 | Settings::values.mouse_panning = ui->enable->isChecked(); | 72 | Settings::values.mouse_panning = ui->enable->isChecked(); |
| 69 | Settings::values.mouse_panning_x_sensitivity = static_cast<float>(ui->x_sensitivity->value()); | 73 | Settings::values.mouse_panning_x_sensitivity = static_cast<float>(ui->x_sensitivity->value()); |
| 70 | Settings::values.mouse_panning_y_sensitivity = static_cast<float>(ui->y_sensitivity->value()); | 74 | Settings::values.mouse_panning_y_sensitivity = static_cast<float>(ui->y_sensitivity->value()); |
| 71 | Settings::values.mouse_panning_deadzone_x_counterweight = | 75 | Settings::values.mouse_panning_deadzone_counterweight = |
| 72 | static_cast<float>(ui->deadzone_x_counterweight->value()); | 76 | static_cast<float>(ui->deadzone_counterweight->value()); |
| 73 | Settings::values.mouse_panning_deadzone_y_counterweight = | ||
| 74 | static_cast<float>(ui->deadzone_y_counterweight->value()); | ||
| 75 | Settings::values.mouse_panning_decay_strength = static_cast<float>(ui->decay_strength->value()); | 77 | Settings::values.mouse_panning_decay_strength = static_cast<float>(ui->decay_strength->value()); |
| 76 | Settings::values.mouse_panning_min_decay = static_cast<float>(ui->min_decay->value()); | 78 | Settings::values.mouse_panning_min_decay = static_cast<float>(ui->min_decay->value()); |
| 77 | 79 | ||
| 80 | if (Settings::values.mouse_enabled && Settings::values.mouse_panning) { | ||
| 81 | Settings::values.mouse_panning = false; | ||
| 82 | QMessageBox::critical( | ||
| 83 | this, tr("Emulated mouse is enabled"), | ||
| 84 | tr("Real mouse input and mouse panning are incompatible. Please disable the " | ||
| 85 | "emulated mouse in input advanced settings to allow mouse panning.")); | ||
| 86 | return; | ||
| 87 | } | ||
| 88 | |||
| 78 | accept(); | 89 | accept(); |
| 79 | } | 90 | } |
diff --git a/src/yuzu/configuration/configure_mouse_panning.ui b/src/yuzu/configuration/configure_mouse_panning.ui index 75795b727..84fb7ee80 100644 --- a/src/yuzu/configuration/configure_mouse_panning.ui +++ b/src/yuzu/configuration/configure_mouse_panning.ui | |||
| @@ -9,10 +9,10 @@ | |||
| 9 | <item> | 9 | <item> |
| 10 | <widget class="QCheckBox" name="enable"> | 10 | <widget class="QCheckBox" name="enable"> |
| 11 | <property name="text"> | 11 | <property name="text"> |
| 12 | <string>Enable</string> | 12 | <string>Enable mouse panning</string> |
| 13 | </property> | 13 | </property> |
| 14 | <property name="toolTip"> | 14 | <property name="toolTip"> |
| 15 | <string>Can be toggled via a hotkey</string> | 15 | <string>Can be toggled via a hotkey. Default hotkey is Ctrl + F9</string> |
| 16 | </property> | 16 | </property> |
| 17 | </widget> | 17 | </widget> |
| 18 | </item> | 18 | </item> |
| @@ -89,40 +89,14 @@ | |||
| 89 | </property> | 89 | </property> |
| 90 | <layout class="QGridLayout"> | 90 | <layout class="QGridLayout"> |
| 91 | <item row="0" column="0"> | 91 | <item row="0" column="0"> |
| 92 | <widget class="QLabel" name="deadzone_x_counterweight_label"> | 92 | <widget class="QLabel" name="deadzone_counterweight_label"> |
| 93 | <property name="text"> | 93 | <property name="text"> |
| 94 | <string>Horizontal</string> | 94 | <string>Deadzone</string> |
| 95 | </property> | 95 | </property> |
| 96 | </widget> | 96 | </widget> |
| 97 | </item> | 97 | </item> |
| 98 | <item row="0" column="1"> | 98 | <item row="0" column="1"> |
| 99 | <widget class="QSpinBox" name="deadzone_x_counterweight"> | 99 | <widget class="QSpinBox" name="deadzone_counterweight"> |
| 100 | <property name="alignment"> | ||
| 101 | <set>Qt::AlignCenter</set> | ||
| 102 | </property> | ||
| 103 | <property name="suffix"> | ||
| 104 | <string>%</string> | ||
| 105 | </property> | ||
| 106 | <property name="minimum"> | ||
| 107 | <number>0</number> | ||
| 108 | </property> | ||
| 109 | <property name="maximum"> | ||
| 110 | <number>100</number> | ||
| 111 | </property> | ||
| 112 | <property name="value"> | ||
| 113 | <number>0</number> | ||
| 114 | </property> | ||
| 115 | </widget> | ||
| 116 | </item> | ||
| 117 | <item row="1" column="0"> | ||
| 118 | <widget class="QLabel" name="deadzone_y_counterweight_label"> | ||
| 119 | <property name="text"> | ||
| 120 | <string>Vertical</string> | ||
| 121 | </property> | ||
| 122 | </widget> | ||
| 123 | </item> | ||
| 124 | <item row="1" column="1"> | ||
| 125 | <widget class="QSpinBox" name="deadzone_y_counterweight"> | ||
| 126 | <property name="alignment"> | 100 | <property name="alignment"> |
| 127 | <set>Qt::AlignCenter</set> | 101 | <set>Qt::AlignCenter</set> |
| 128 | </property> | 102 | </property> |
diff --git a/src/yuzu/qt_common.cpp b/src/yuzu/qt_common.cpp index 5d0fd7674..413402165 100644 --- a/src/yuzu/qt_common.cpp +++ b/src/yuzu/qt_common.cpp | |||
| @@ -10,6 +10,8 @@ | |||
| 10 | 10 | ||
| 11 | #if !defined(WIN32) && !defined(__APPLE__) | 11 | #if !defined(WIN32) && !defined(__APPLE__) |
| 12 | #include <qpa/qplatformnativeinterface.h> | 12 | #include <qpa/qplatformnativeinterface.h> |
| 13 | #elif defined(__APPLE__) | ||
| 14 | #include <objc/message.h> | ||
| 13 | #endif | 15 | #endif |
| 14 | 16 | ||
| 15 | namespace QtCommon { | 17 | namespace QtCommon { |
| @@ -37,9 +39,12 @@ Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow* window) | |||
| 37 | Core::Frontend::EmuWindow::WindowSystemInfo wsi; | 39 | Core::Frontend::EmuWindow::WindowSystemInfo wsi; |
| 38 | wsi.type = GetWindowSystemType(); | 40 | wsi.type = GetWindowSystemType(); |
| 39 | 41 | ||
| 42 | #if defined(WIN32) | ||
| 40 | // Our Win32 Qt external doesn't have the private API. | 43 | // Our Win32 Qt external doesn't have the private API. |
| 41 | #if defined(WIN32) || defined(__APPLE__) | 44 | wsi.render_surface = reinterpret_cast<void*>(window->winId()); |
| 42 | wsi.render_surface = window ? reinterpret_cast<void*>(window->winId()) : nullptr; | 45 | #elif defined(__APPLE__) |
| 46 | wsi.render_surface = reinterpret_cast<void* (*)(id, SEL)>(objc_msgSend)( | ||
| 47 | reinterpret_cast<id>(window->winId()), sel_registerName("layer")); | ||
| 43 | #else | 48 | #else |
| 44 | QPlatformNativeInterface* pni = QGuiApplication::platformNativeInterface(); | 49 | QPlatformNativeInterface* pni = QGuiApplication::platformNativeInterface(); |
| 45 | wsi.display_connection = pni->nativeResourceForWindow("display", window); | 50 | wsi.display_connection = pni->nativeResourceForWindow("display", window); |
diff --git a/src/yuzu/vk_device_info.cpp b/src/yuzu/vk_device_info.cpp index 7c26a3dc7..e1a0e6a2a 100644 --- a/src/yuzu/vk_device_info.cpp +++ b/src/yuzu/vk_device_info.cpp | |||
| @@ -26,7 +26,10 @@ Record::~Record() = default; | |||
| 26 | void PopulateRecords(std::vector<Record>& records, QWindow* window) try { | 26 | void PopulateRecords(std::vector<Record>& records, QWindow* window) try { |
| 27 | using namespace Vulkan; | 27 | using namespace Vulkan; |
| 28 | 28 | ||
| 29 | auto wsi = QtCommon::GetWindowSystemInfo(window); | 29 | // Create a test window with a Vulkan surface type for checking present modes. |
| 30 | QWindow test_window(window); | ||
| 31 | test_window.setSurfaceType(QWindow::VulkanSurface); | ||
| 32 | auto wsi = QtCommon::GetWindowSystemInfo(&test_window); | ||
| 30 | 33 | ||
| 31 | vk::InstanceDispatch dld; | 34 | vk::InstanceDispatch dld; |
| 32 | const auto library = OpenLibrary(); | 35 | const auto library = OpenLibrary(); |