summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt35
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt120
-rw-r--r--src/android/app/src/main/res/values/strings.xml1
-rw-r--r--src/audio_core/sink/sink_stream.cpp34
-rw-r--r--src/audio_core/sink/sink_stream.h3
-rw-r--r--src/common/ring_buffer.h2
-rw-r--r--src/common/scratch_buffer.h46
-rw-r--r--src/common/settings.h10
-rw-r--r--src/core/core.cpp32
-rw-r--r--src/core/core.h11
-rw-r--r--src/core/file_sys/fsmitm_romfsbuild.cpp38
-rw-r--r--src/core/gpu_dirty_memory_manager.h122
-rw-r--r--src/core/hle/kernel/k_thread.h10
-rw-r--r--src/core/hle/kernel/svc/svc_ipc.cpp37
-rw-r--r--src/core/hle/kernel/svc/svc_synchronization.cpp41
-rw-r--r--src/core/hle/service/audio/audin_u.cpp16
-rw-r--r--src/core/hle/service/audio/audout_u.cpp20
-rw-r--r--src/core/hle/service/audio/audren_u.cpp23
-rw-r--r--src/core/hle/service/audio/hwopus.cpp9
-rw-r--r--src/core/hle/service/nvdrv/nvdrv_interface.cpp25
-rw-r--r--src/core/hle/service/nvdrv/nvdrv_interface.h5
-rw-r--r--src/core/hle/service/nvnflinger/parcel.h24
-rw-r--r--src/core/memory.cpp40
-rw-r--r--src/core/memory.h6
-rw-r--r--src/input_common/drivers/mouse.cpp86
-rw-r--r--src/input_common/drivers/mouse.h2
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h39
-rw-r--r--src/video_core/buffer_cache/buffer_cache_base.h5
-rw-r--r--src/video_core/engines/maxwell_dma.cpp7
-rw-r--r--src/video_core/fence_manager.h2
-rw-r--r--src/video_core/gpu.cpp12
-rw-r--r--src/video_core/gpu.h4
-rw-r--r--src/video_core/gpu_thread.cpp6
-rw-r--r--src/video_core/host1x/codecs/codec.cpp2
-rw-r--r--src/video_core/host1x/codecs/h264.cpp14
-rw-r--r--src/video_core/host1x/codecs/h264.h12
-rw-r--r--src/video_core/host1x/codecs/vp8.cpp2
-rw-r--r--src/video_core/host1x/codecs/vp8.h7
-rw-r--r--src/video_core/host1x/codecs/vp9.cpp1
-rw-r--r--src/video_core/host1x/codecs/vp9.h8
-rw-r--r--src/video_core/host1x/codecs/vp9_types.h1
-rw-r--r--src/video_core/rasterizer_interface.h4
-rw-r--r--src/video_core/renderer_null/null_rasterizer.cpp5
-rw-r--r--src/video_core/renderer_null/null_rasterizer.h3
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp35
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h3
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.cpp3
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp34
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.h3
-rw-r--r--src/video_core/shader_cache.cpp2
-rw-r--r--src/video_core/shader_cache.h2
-rw-r--r--src/video_core/texture_cache/texture_cache.h12
-rw-r--r--src/video_core/vulkan_common/vulkan_instance.cpp6
-rw-r--r--src/video_core/vulkan_common/vulkan_surface.cpp17
-rw-r--r--src/video_core/vulkan_common/vulkan_wrapper.h2
-rw-r--r--src/yuzu/configuration/config.cpp6
-rw-r--r--src/yuzu/configuration/configure_input_player.ui15
-rw-r--r--src/yuzu/configuration/configure_mouse_panning.cpp49
-rw-r--r--src/yuzu/configuration/configure_mouse_panning.ui36
-rw-r--r--src/yuzu/qt_common.cpp9
-rw-r--r--src/yuzu/vk_device_info.cpp5
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
34import androidx.core.view.WindowInsetsCompat 34import androidx.core.view.WindowInsetsCompat
35import androidx.core.view.WindowInsetsControllerCompat 35import androidx.core.view.WindowInsetsControllerCompat
36import androidx.navigation.fragment.NavHostFragment 36import androidx.navigation.fragment.NavHostFragment
37import androidx.preference.PreferenceManager
37import org.yuzu.yuzu_emu.NativeLibrary 38import org.yuzu.yuzu_emu.NativeLibrary
38import org.yuzu.yuzu_emu.R 39import org.yuzu.yuzu_emu.R
40import org.yuzu.yuzu_emu.YuzuApplication
39import org.yuzu.yuzu_emu.databinding.ActivityEmulationBinding 41import org.yuzu.yuzu_emu.databinding.ActivityEmulationBinding
40import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting 42import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
41import org.yuzu.yuzu_emu.features.settings.model.IntSetting 43import org.yuzu.yuzu_emu.features.settings.model.IntSetting
44import org.yuzu.yuzu_emu.features.settings.model.Settings
42import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel 45import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel
43import org.yuzu.yuzu_emu.model.Game 46import org.yuzu.yuzu_emu.model.Game
44import org.yuzu.yuzu_emu.utils.ControllerMappingHelper 47import org.yuzu.yuzu_emu.utils.ControllerMappingHelper
@@ -47,6 +50,7 @@ import org.yuzu.yuzu_emu.utils.InputHandler
47import org.yuzu.yuzu_emu.utils.MemoryUtil 50import org.yuzu.yuzu_emu.utils.MemoryUtil
48import org.yuzu.yuzu_emu.utils.NfcReader 51import org.yuzu.yuzu_emu.utils.NfcReader
49import org.yuzu.yuzu_emu.utils.ThemeHelper 52import org.yuzu.yuzu_emu.utils.ThemeHelper
53import java.text.NumberFormat
50import kotlin.math.roundToInt 54import kotlin.math.roundToInt
51 55
52class EmulationActivity : AppCompatActivity(), SensorEventListener { 56class 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
6import android.app.ActivityManager 6import android.app.ActivityManager
7import android.content.Context 7import android.content.Context
8import android.os.Build
8import org.yuzu.yuzu_emu.R 9import org.yuzu.yuzu_emu.R
10import org.yuzu.yuzu_emu.YuzuApplication
9import java.util.Locale 11import java.util.Locale
12import kotlin.math.ceil
10 13
11class MemoryUtil(val context: Context) { 14object 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 @@
19namespace AudioCore::Sink { 20namespace AudioCore::Sink {
20 21
21void SinkStream::AppendBuffer(SinkBuffer& buffer, std::span<s16> samples) { 22void 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
110std::vector<s16> SinkStream::ReleaseBuffer(u64 num_samples) { 116std::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
22namespace Core { 21namespace 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
263using SinkStreamPtr = std::unique_ptr<SinkStream>; 260using 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
11namespace Common { 10namespace Common {
@@ -19,15 +18,22 @@ namespace Common {
19template <typename T> 18template <typename T>
20class ScratchBuffer { 19class ScratchBuffer {
21public: 20public:
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
108private: 114private:
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,
130struct System::Impl { 131struct 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
545System::System() : impl{std::make_unique<Impl>(*this)} {} 554System::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
641Core::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.
649const 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
632size_t System::GetCurrentHostThreadID() const { 656size_t System::GetCurrentHostThreadID() const {
633 return impl->kernel.GetCurrentHostThreadID(); 657 return impl->kernel.GetCurrentHostThreadID();
634} 658}
635 659
660void 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
636PerfStatsResults System::GetAndResetPerfStats() { 666PerfStatsResults 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;
108class Debugger; 108class Debugger;
109class DeviceMemory; 109class DeviceMemory;
110class ExclusiveMonitor; 110class ExclusiveMonitor;
111class SpeedLimiter; 111class GPUDirtyMemoryManager;
112class PerfStats; 112class PerfStats;
113class Reporter; 113class Reporter;
114class SpeedLimiter;
114class TelemetrySession; 115class TelemetrySession;
115 116
116struct PerfStatsResults; 117struct 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
108void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs, VirtualDir ext_dir, 108void 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
175bool RomFSBuildContext::AddDirectory(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx, 170bool 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>
192bool RomFSBuildContext::AddFile(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx, 188bool 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
15namespace Core {
16
17class GPUDirtyMemoryManager {
18public:
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
77private:
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
39Result ReplyAndReceive(Core::System& system, s32* out_index, uint64_t handles_addr, s32 num_handles, 39Result 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
50static 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) { 51Result 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
84Result 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
101Result CancelSynchronization(Core::System& system, Handle handle) { 98Result 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
201AudInU::AudInU(Core::System& system_) 203AudInU::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
211AudOutU::AudOutU(Core::System& system_) 213AudOutU::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
240class IAudioDevice final : public ServiceFramework<IAudioDevice> { 241class 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
160class IHardwareOpusDecoderManager final : public ServiceFramework<IHardwareOpusDecoderManager> { 161class 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
168private: 169private:
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
179private: 180private:
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
734Memory::Memory(Core::System& system_) : system{system_} { 758Memory::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
903void Memory::SetGPUDirtyManagers(std::span<Core::GPUDirtyMemoryManager> managers) {
904 impl->gpu_dirty_managers = managers;
905}
906
879Result Memory::InvalidateDataCache(Common::ProcessAddress dest_addr, const std::size_t size) { 907Result 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
16namespace Core { 17namespace Core {
17class System; 18class System;
18} 19class GPUDirtyMemoryManager;
20} // namespace Core
19 21
20namespace Kernel { 22namespace Kernel {
21class PhysicalMemory; 23class 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
461private: 465private:
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
13namespace InputCommon { 13namespace InputCommon {
14constexpr int update_time = 10; 14constexpr int update_time = 10;
15constexpr float default_stick_sensitivity = 0.0044f; 15constexpr float default_panning_sensitivity = 0.0010f;
16constexpr float default_motion_sensitivity = 0.0003f; 16constexpr float default_stick_sensitivity = 0.0006f;
17constexpr float default_deadzone_counterweight = 0.01f;
18constexpr float default_motion_panning_sensitivity = 2.5f;
19constexpr float default_motion_sensitivity = 0.416f;
17constexpr float maximum_rotation_speed = 2.0f; 20constexpr float maximum_rotation_speed = 2.0f;
21constexpr float maximum_stick_range = 1.5f;
18constexpr int mouse_axis_x = 0; 22constexpr int mouse_axis_x = 0;
19constexpr int mouse_axis_y = 1; 23constexpr int mouse_axis_y = 1;
20constexpr int wheel_axis_x = 2; 24constexpr int wheel_axis_x = 2;
@@ -81,7 +85,7 @@ void Mouse::UpdateThread(std::stop_token stop_token) {
81} 85}
82 86
83void Mouse::UpdateStickInput() { 87void 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
122void Mouse::UpdateMotionInput() { 113void 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
156void Mouse::Move(int x, int y, int center_x, int center_y) { 146void 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) {
234void Mouse::MouseWheelChange(int x, int y) { 233void 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
246bool Mouse::IsMousePanningEnabled() {
247 // Disable mouse panning when a real mouse is connected
248 return Settings::values.mouse_panning && !Settings::values.mouse_enabled;
249}
250
247std::vector<Common::ParamPackage> Mouse::GetInputDevices() const { 251std::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
116template <class P> 116template <class P>
117void BufferCache<P>::CachedWriteMemory(VAddr cpu_addr, u64 size) { 117void 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
135template <class P>
136bool 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
121template <class P> 148template <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
1588template <class P>
1589void 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
1581template <class P> 1614template <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
570bool GPU::OnCPUWrite(VAddr addr, u64 size) {
571 return impl->OnCPUWrite(addr, size);
572}
573
564void GPU::FlushAndInvalidateRegion(VAddr addr, u64 size) { 574void 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
104void ThreadManager::InvalidateRegion(VAddr addr, u64 size) { 104void ThreadManager::InvalidateRegion(VAddr addr, u64 size) {
105 rasterizer->OnCPUWrite(addr, size); 105 rasterizer->OnCacheInvalidation(addr, size);
106} 106}
107 107
108void ThreadManager::FlushAndInvalidateRegion(VAddr addr, u64 size) { 108void 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
113u64 ThreadManager::PushCommand(CommandData&& command_data, bool block) { 113u64 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
30H264::~H264() = default; 30H264::~H264() = default;
31 31
32const std::vector<u8>& H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state, 32std::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
191void H264BitWriter::WriteScalingList(std::span<const u8> list, s32 start, s32 count) { 191void 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
13namespace Tegra { 15namespace 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
69private: 72private:
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
13VP8::~VP8() = default; 13VP8::~VP8() = default;
14 14
15const std::vector<u8>& VP8::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state) { 15std::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
13namespace Tegra { 14namespace 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
30private: 31private:
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}
49void RasterizerNull::InvalidateRegion(VAddr addr, u64 size, VideoCommon::CacheType) {} 49void RasterizerNull::InvalidateRegion(VAddr addr, u64 size, VideoCommon::CacheType) {}
50void RasterizerNull::OnCPUWrite(VAddr addr, u64 size) {} 50bool RasterizerNull::OnCPUWrite(VAddr addr, u64 size) {
51 return false;
52}
53void RasterizerNull::OnCacheInvalidation(VAddr addr, u64 size) {}
51VideoCore::RasterizerDownloadArea RasterizerNull::GetFlushArea(VAddr addr, u64 size) { 54VideoCore::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
488void RasterizerOpenGL::OnCPUWrite(VAddr addr, u64 size) { 488bool 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
510void 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
504void RasterizerOpenGL::InvalidateGPUCache() { 526void 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
513void RasterizerOpenGL::UnmapMemory(VAddr addr, u64 size) { 530void 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
525void RasterizerOpenGL::ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) { 542void 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
569void RasterizerVulkan::OnCPUWrite(VAddr addr, u64 size) { 569bool 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
590void 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
584void RasterizerVulkan::InvalidateGPUCache() { 606void 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
592void RasterizerVulkan::UnmapMemory(VAddr addr, u64 size) { 610void 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
604void RasterizerVulkan::ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) { 622void 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
27void ShaderCache::OnCPUWrite(VAddr addr, size_t size) { 27void 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() {
865template <class P> 865template <class P>
866ImageId TextureCache<P>::DmaImageId(const Tegra::DMA::ImageOperand& operand, bool is_upload) { 866ImageId 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
889template <class P> 893template <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
48void ConfigureMousePanning::SetDefaultConfiguration() { 54void 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
15namespace QtCommon { 17namespace 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;
26void PopulateRecords(std::vector<Record>& records, QWindow* window) try { 26void 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();