diff options
Diffstat (limited to 'src')
53 files changed, 637 insertions, 924 deletions
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt index 090dd19b1..e553b8203 100644 --- a/src/audio_core/CMakeLists.txt +++ b/src/audio_core/CMakeLists.txt | |||
| @@ -36,8 +36,6 @@ add_library(audio_core STATIC | |||
| 36 | splitter_context.h | 36 | splitter_context.h |
| 37 | stream.cpp | 37 | stream.cpp |
| 38 | stream.h | 38 | stream.h |
| 39 | time_stretch.cpp | ||
| 40 | time_stretch.h | ||
| 41 | voice_context.cpp | 39 | voice_context.cpp |
| 42 | voice_context.h | 40 | voice_context.h |
| 43 | 41 | ||
| @@ -63,7 +61,6 @@ if (NOT MSVC) | |||
| 63 | endif() | 61 | endif() |
| 64 | 62 | ||
| 65 | target_link_libraries(audio_core PUBLIC common core) | 63 | target_link_libraries(audio_core PUBLIC common core) |
| 66 | target_link_libraries(audio_core PRIVATE SoundTouch) | ||
| 67 | 64 | ||
| 68 | if(ENABLE_CUBEB) | 65 | if(ENABLE_CUBEB) |
| 69 | target_link_libraries(audio_core PRIVATE cubeb) | 66 | target_link_libraries(audio_core PRIVATE cubeb) |
diff --git a/src/audio_core/cubeb_sink.cpp b/src/audio_core/cubeb_sink.cpp index 93c35e785..13de3087c 100644 --- a/src/audio_core/cubeb_sink.cpp +++ b/src/audio_core/cubeb_sink.cpp | |||
| @@ -7,7 +7,6 @@ | |||
| 7 | #include <cstring> | 7 | #include <cstring> |
| 8 | #include "audio_core/cubeb_sink.h" | 8 | #include "audio_core/cubeb_sink.h" |
| 9 | #include "audio_core/stream.h" | 9 | #include "audio_core/stream.h" |
| 10 | #include "audio_core/time_stretch.h" | ||
| 11 | #include "common/assert.h" | 10 | #include "common/assert.h" |
| 12 | #include "common/logging/log.h" | 11 | #include "common/logging/log.h" |
| 13 | #include "common/ring_buffer.h" | 12 | #include "common/ring_buffer.h" |
| @@ -23,8 +22,7 @@ class CubebSinkStream final : public SinkStream { | |||
| 23 | public: | 22 | public: |
| 24 | CubebSinkStream(cubeb* ctx_, u32 sample_rate, u32 num_channels_, cubeb_devid output_device, | 23 | CubebSinkStream(cubeb* ctx_, u32 sample_rate, u32 num_channels_, cubeb_devid output_device, |
| 25 | const std::string& name) | 24 | const std::string& name) |
| 26 | : ctx{ctx_}, num_channels{std::min(num_channels_, 6u)}, time_stretch{sample_rate, | 25 | : ctx{ctx_}, num_channels{std::min(num_channels_, 6u)} { |
| 27 | num_channels} { | ||
| 28 | 26 | ||
| 29 | cubeb_stream_params params{}; | 27 | cubeb_stream_params params{}; |
| 30 | params.rate = sample_rate; | 28 | params.rate = sample_rate; |
| @@ -131,7 +129,6 @@ private: | |||
| 131 | Common::RingBuffer<s16, 0x10000> queue; | 129 | Common::RingBuffer<s16, 0x10000> queue; |
| 132 | std::array<s16, 2> last_frame{}; | 130 | std::array<s16, 2> last_frame{}; |
| 133 | std::atomic<bool> should_flush{}; | 131 | std::atomic<bool> should_flush{}; |
| 134 | TimeStretcher time_stretch; | ||
| 135 | 132 | ||
| 136 | static long DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer, | 133 | static long DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer, |
| 137 | void* output_buffer, long num_frames); | 134 | void* output_buffer, long num_frames); |
| @@ -205,25 +202,7 @@ long CubebSinkStream::DataCallback([[maybe_unused]] cubeb_stream* stream, void* | |||
| 205 | 202 | ||
| 206 | const std::size_t num_channels = impl->GetNumChannels(); | 203 | const std::size_t num_channels = impl->GetNumChannels(); |
| 207 | const std::size_t samples_to_write = num_channels * num_frames; | 204 | const std::size_t samples_to_write = num_channels * num_frames; |
| 208 | std::size_t samples_written; | 205 | const std::size_t samples_written = impl->queue.Pop(buffer, samples_to_write); |
| 209 | |||
| 210 | /* | ||
| 211 | if (Settings::values.enable_audio_stretching.GetValue()) { | ||
| 212 | const std::vector<s16> in{impl->queue.Pop()}; | ||
| 213 | const std::size_t num_in{in.size() / num_channels}; | ||
| 214 | s16* const out{reinterpret_cast<s16*>(buffer)}; | ||
| 215 | const std::size_t out_frames = | ||
| 216 | impl->time_stretch.Process(in.data(), num_in, out, num_frames); | ||
| 217 | samples_written = out_frames * num_channels; | ||
| 218 | |||
| 219 | if (impl->should_flush) { | ||
| 220 | impl->time_stretch.Flush(); | ||
| 221 | impl->should_flush = false; | ||
| 222 | } | ||
| 223 | } else { | ||
| 224 | samples_written = impl->queue.Pop(buffer, samples_to_write); | ||
| 225 | }*/ | ||
| 226 | samples_written = impl->queue.Pop(buffer, samples_to_write); | ||
| 227 | 206 | ||
| 228 | if (samples_written >= num_channels) { | 207 | if (samples_written >= num_channels) { |
| 229 | std::memcpy(&impl->last_frame[0], buffer + (samples_written - num_channels) * sizeof(s16), | 208 | std::memcpy(&impl->last_frame[0], buffer + (samples_written - num_channels) * sizeof(s16), |
diff --git a/src/audio_core/sdl2_sink.cpp b/src/audio_core/sdl2_sink.cpp index 62d3716a6..2d14ce2cb 100644 --- a/src/audio_core/sdl2_sink.cpp +++ b/src/audio_core/sdl2_sink.cpp | |||
| @@ -7,7 +7,6 @@ | |||
| 7 | #include <cstring> | 7 | #include <cstring> |
| 8 | #include "audio_core/sdl2_sink.h" | 8 | #include "audio_core/sdl2_sink.h" |
| 9 | #include "audio_core/stream.h" | 9 | #include "audio_core/stream.h" |
| 10 | #include "audio_core/time_stretch.h" | ||
| 11 | #include "common/assert.h" | 10 | #include "common/assert.h" |
| 12 | #include "common/logging/log.h" | 11 | #include "common/logging/log.h" |
| 13 | //#include "common/settings.h" | 12 | //#include "common/settings.h" |
| @@ -27,7 +26,7 @@ namespace AudioCore { | |||
| 27 | class SDLSinkStream final : public SinkStream { | 26 | class SDLSinkStream final : public SinkStream { |
| 28 | public: | 27 | public: |
| 29 | SDLSinkStream(u32 sample_rate, u32 num_channels_, const std::string& output_device) | 28 | SDLSinkStream(u32 sample_rate, u32 num_channels_, const std::string& output_device) |
| 30 | : num_channels{std::min(num_channels_, 6u)}, time_stretch{sample_rate, num_channels} { | 29 | : num_channels{std::min(num_channels_, 6u)} { |
| 31 | 30 | ||
| 32 | SDL_AudioSpec spec; | 31 | SDL_AudioSpec spec; |
| 33 | spec.freq = sample_rate; | 32 | spec.freq = sample_rate; |
| @@ -116,7 +115,6 @@ private: | |||
| 116 | SDL_AudioDeviceID dev = 0; | 115 | SDL_AudioDeviceID dev = 0; |
| 117 | u32 num_channels{}; | 116 | u32 num_channels{}; |
| 118 | std::atomic<bool> should_flush{}; | 117 | std::atomic<bool> should_flush{}; |
| 119 | TimeStretcher time_stretch; | ||
| 120 | }; | 118 | }; |
| 121 | 119 | ||
| 122 | SDLSink::SDLSink(std::string_view target_device_name) { | 120 | SDLSink::SDLSink(std::string_view target_device_name) { |
diff --git a/src/audio_core/time_stretch.cpp b/src/audio_core/time_stretch.cpp deleted file mode 100644 index 726591fce..000000000 --- a/src/audio_core/time_stretch.cpp +++ /dev/null | |||
| @@ -1,68 +0,0 @@ | |||
| 1 | // Copyright 2018 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <cmath> | ||
| 7 | #include <cstddef> | ||
| 8 | #include "audio_core/time_stretch.h" | ||
| 9 | #include "common/logging/log.h" | ||
| 10 | |||
| 11 | namespace AudioCore { | ||
| 12 | |||
| 13 | TimeStretcher::TimeStretcher(u32 sample_rate, u32 channel_count) : m_sample_rate{sample_rate} { | ||
| 14 | m_sound_touch.setChannels(channel_count); | ||
| 15 | m_sound_touch.setSampleRate(sample_rate); | ||
| 16 | m_sound_touch.setPitch(1.0); | ||
| 17 | m_sound_touch.setTempo(1.0); | ||
| 18 | } | ||
| 19 | |||
| 20 | void TimeStretcher::Clear() { | ||
| 21 | m_sound_touch.clear(); | ||
| 22 | } | ||
| 23 | |||
| 24 | void TimeStretcher::Flush() { | ||
| 25 | m_sound_touch.flush(); | ||
| 26 | } | ||
| 27 | |||
| 28 | std::size_t TimeStretcher::Process(const s16* in, std::size_t num_in, s16* out, | ||
| 29 | std::size_t num_out) { | ||
| 30 | const double time_delta = static_cast<double>(num_out) / m_sample_rate; // seconds | ||
| 31 | |||
| 32 | // We were given actual_samples number of samples, and num_samples were requested from us. | ||
| 33 | double current_ratio = static_cast<double>(num_in) / static_cast<double>(num_out); | ||
| 34 | |||
| 35 | const double max_latency = 0.25; // seconds | ||
| 36 | const double max_backlog = m_sample_rate * max_latency; | ||
| 37 | const double backlog_fullness = m_sound_touch.numSamples() / max_backlog; | ||
| 38 | if (backlog_fullness > 4.0) { | ||
| 39 | // Too many samples in backlog: Don't push anymore on | ||
| 40 | num_in = 0; | ||
| 41 | } | ||
| 42 | |||
| 43 | // We ideally want the backlog to be about 50% full. | ||
| 44 | // This gives some headroom both ways to prevent underflow and overflow. | ||
| 45 | // We tweak current_ratio to encourage this. | ||
| 46 | constexpr double tweak_time_scale = 0.05; // seconds | ||
| 47 | const double tweak_correction = (backlog_fullness - 0.5) * (time_delta / tweak_time_scale); | ||
| 48 | current_ratio *= std::pow(1.0 + 2.0 * tweak_correction, tweak_correction < 0 ? 3.0 : 1.0); | ||
| 49 | |||
| 50 | // This low-pass filter smoothes out variance in the calculated stretch ratio. | ||
| 51 | // The time-scale determines how responsive this filter is. | ||
| 52 | constexpr double lpf_time_scale = 0.712; // seconds | ||
| 53 | const double lpf_gain = 1.0 - std::exp(-time_delta / lpf_time_scale); | ||
| 54 | m_stretch_ratio += lpf_gain * (current_ratio - m_stretch_ratio); | ||
| 55 | |||
| 56 | // Place a lower limit of 5% speed. When a game boots up, there will be | ||
| 57 | // many silence samples. These do not need to be timestretched. | ||
| 58 | m_stretch_ratio = std::max(m_stretch_ratio, 0.05); | ||
| 59 | m_sound_touch.setTempo(m_stretch_ratio); | ||
| 60 | |||
| 61 | LOG_TRACE(Audio, "{:5}/{:5} ratio:{:0.6f} backlog:{:0.6f}", num_in, num_out, m_stretch_ratio, | ||
| 62 | backlog_fullness); | ||
| 63 | |||
| 64 | m_sound_touch.putSamples(in, static_cast<u32>(num_in)); | ||
| 65 | return m_sound_touch.receiveSamples(out, static_cast<u32>(num_out)); | ||
| 66 | } | ||
| 67 | |||
| 68 | } // namespace AudioCore | ||
diff --git a/src/audio_core/time_stretch.h b/src/audio_core/time_stretch.h deleted file mode 100644 index bb2270b96..000000000 --- a/src/audio_core/time_stretch.h +++ /dev/null | |||
| @@ -1,34 +0,0 @@ | |||
| 1 | // Copyright 2018 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <cstddef> | ||
| 8 | #include <SoundTouch.h> | ||
| 9 | #include "common/common_types.h" | ||
| 10 | |||
| 11 | namespace AudioCore { | ||
| 12 | |||
| 13 | class TimeStretcher { | ||
| 14 | public: | ||
| 15 | TimeStretcher(u32 sample_rate, u32 channel_count); | ||
| 16 | |||
| 17 | /// @param in Input sample buffer | ||
| 18 | /// @param num_in Number of input frames in `in` | ||
| 19 | /// @param out Output sample buffer | ||
| 20 | /// @param num_out Desired number of output frames in `out` | ||
| 21 | /// @returns Actual number of frames written to `out` | ||
| 22 | std::size_t Process(const s16* in, std::size_t num_in, s16* out, std::size_t num_out); | ||
| 23 | |||
| 24 | void Clear(); | ||
| 25 | |||
| 26 | void Flush(); | ||
| 27 | |||
| 28 | private: | ||
| 29 | u32 m_sample_rate; | ||
| 30 | soundtouch::SoundTouch m_sound_touch; | ||
| 31 | double m_stretch_ratio = 1.0; | ||
| 32 | }; | ||
| 33 | |||
| 34 | } // namespace AudioCore | ||
diff --git a/src/common/atomic_ops.h b/src/common/atomic_ops.h index b94d73c7a..69fde8421 100644 --- a/src/common/atomic_ops.h +++ b/src/common/atomic_ops.h | |||
| @@ -46,6 +46,50 @@ namespace Common { | |||
| 46 | reinterpret_cast<__int64*>(expected.data())) != 0; | 46 | reinterpret_cast<__int64*>(expected.data())) != 0; |
| 47 | } | 47 | } |
| 48 | 48 | ||
| 49 | [[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected, | ||
| 50 | u8& actual) { | ||
| 51 | actual = | ||
| 52 | _InterlockedCompareExchange8(reinterpret_cast<volatile char*>(pointer), value, expected); | ||
| 53 | return actual == expected; | ||
| 54 | } | ||
| 55 | |||
| 56 | [[nodiscard]] inline bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected, | ||
| 57 | u16& actual) { | ||
| 58 | actual = | ||
| 59 | _InterlockedCompareExchange16(reinterpret_cast<volatile short*>(pointer), value, expected); | ||
| 60 | return actual == expected; | ||
| 61 | } | ||
| 62 | |||
| 63 | [[nodiscard]] inline bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected, | ||
| 64 | u32& actual) { | ||
| 65 | actual = | ||
| 66 | _InterlockedCompareExchange(reinterpret_cast<volatile long*>(pointer), value, expected); | ||
| 67 | return actual == expected; | ||
| 68 | } | ||
| 69 | |||
| 70 | [[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected, | ||
| 71 | u64& actual) { | ||
| 72 | actual = _InterlockedCompareExchange64(reinterpret_cast<volatile __int64*>(pointer), value, | ||
| 73 | expected); | ||
| 74 | return actual == expected; | ||
| 75 | } | ||
| 76 | |||
| 77 | [[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected, | ||
| 78 | u128& actual) { | ||
| 79 | const bool result = | ||
| 80 | _InterlockedCompareExchange128(reinterpret_cast<volatile __int64*>(pointer), value[1], | ||
| 81 | value[0], reinterpret_cast<__int64*>(expected.data())) != 0; | ||
| 82 | actual = expected; | ||
| 83 | return result; | ||
| 84 | } | ||
| 85 | |||
| 86 | [[nodiscard]] inline u128 AtomicLoad128(volatile u64* pointer) { | ||
| 87 | u128 result{}; | ||
| 88 | _InterlockedCompareExchange128(reinterpret_cast<volatile __int64*>(pointer), result[1], | ||
| 89 | result[0], reinterpret_cast<__int64*>(result.data())); | ||
| 90 | return result; | ||
| 91 | } | ||
| 92 | |||
| 49 | #else | 93 | #else |
| 50 | 94 | ||
| 51 | [[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) { | 95 | [[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) { |
| @@ -72,6 +116,52 @@ namespace Common { | |||
| 72 | return __sync_bool_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a); | 116 | return __sync_bool_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a); |
| 73 | } | 117 | } |
| 74 | 118 | ||
| 119 | [[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected, | ||
| 120 | u8& actual) { | ||
| 121 | actual = __sync_val_compare_and_swap(pointer, expected, value); | ||
| 122 | return actual == expected; | ||
| 123 | } | ||
| 124 | |||
| 125 | [[nodiscard]] inline bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected, | ||
| 126 | u16& actual) { | ||
| 127 | actual = __sync_val_compare_and_swap(pointer, expected, value); | ||
| 128 | return actual == expected; | ||
| 129 | } | ||
| 130 | |||
| 131 | [[nodiscard]] inline bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected, | ||
| 132 | u32& actual) { | ||
| 133 | actual = __sync_val_compare_and_swap(pointer, expected, value); | ||
| 134 | return actual == expected; | ||
| 135 | } | ||
| 136 | |||
| 137 | [[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected, | ||
| 138 | u64& actual) { | ||
| 139 | actual = __sync_val_compare_and_swap(pointer, expected, value); | ||
| 140 | return actual == expected; | ||
| 141 | } | ||
| 142 | |||
| 143 | [[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected, | ||
| 144 | u128& actual) { | ||
| 145 | unsigned __int128 value_a; | ||
| 146 | unsigned __int128 expected_a; | ||
| 147 | unsigned __int128 actual_a; | ||
| 148 | std::memcpy(&value_a, value.data(), sizeof(u128)); | ||
| 149 | std::memcpy(&expected_a, expected.data(), sizeof(u128)); | ||
| 150 | actual_a = __sync_val_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a); | ||
| 151 | std::memcpy(actual.data(), &actual_a, sizeof(u128)); | ||
| 152 | return actual_a == expected_a; | ||
| 153 | } | ||
| 154 | |||
| 155 | [[nodiscard]] inline u128 AtomicLoad128(volatile u64* pointer) { | ||
| 156 | unsigned __int128 zeros_a = 0; | ||
| 157 | unsigned __int128 result_a = | ||
| 158 | __sync_val_compare_and_swap((unsigned __int128*)pointer, zeros_a, zeros_a); | ||
| 159 | |||
| 160 | u128 result; | ||
| 161 | std::memcpy(result.data(), &result_a, sizeof(u128)); | ||
| 162 | return result; | ||
| 163 | } | ||
| 164 | |||
| 75 | #endif | 165 | #endif |
| 76 | 166 | ||
| 77 | } // namespace Common | 167 | } // namespace Common |
diff --git a/src/common/x64/native_clock.cpp b/src/common/x64/native_clock.cpp index 347e41efc..7a3f21dcf 100644 --- a/src/common/x64/native_clock.cpp +++ b/src/common/x64/native_clock.cpp | |||
| @@ -55,8 +55,9 @@ NativeClock::NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequen | |||
| 55 | u64 NativeClock::GetRTSC() { | 55 | u64 NativeClock::GetRTSC() { |
| 56 | TimePoint new_time_point{}; | 56 | TimePoint new_time_point{}; |
| 57 | TimePoint current_time_point{}; | 57 | TimePoint current_time_point{}; |
| 58 | |||
| 59 | current_time_point.pack = Common::AtomicLoad128(time_point.pack.data()); | ||
| 58 | do { | 60 | do { |
| 59 | current_time_point.pack = time_point.pack; | ||
| 60 | _mm_mfence(); | 61 | _mm_mfence(); |
| 61 | const u64 current_measure = __rdtsc(); | 62 | const u64 current_measure = __rdtsc(); |
| 62 | u64 diff = current_measure - current_time_point.inner.last_measure; | 63 | u64 diff = current_measure - current_time_point.inner.last_measure; |
| @@ -66,7 +67,7 @@ u64 NativeClock::GetRTSC() { | |||
| 66 | : current_time_point.inner.last_measure; | 67 | : current_time_point.inner.last_measure; |
| 67 | new_time_point.inner.accumulated_ticks = current_time_point.inner.accumulated_ticks + diff; | 68 | new_time_point.inner.accumulated_ticks = current_time_point.inner.accumulated_ticks + diff; |
| 68 | } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack, | 69 | } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack, |
| 69 | current_time_point.pack)); | 70 | current_time_point.pack, current_time_point.pack)); |
| 70 | /// The clock cannot be more precise than the guest timer, remove the lower bits | 71 | /// The clock cannot be more precise than the guest timer, remove the lower bits |
| 71 | return new_time_point.inner.accumulated_ticks & inaccuracy_mask; | 72 | return new_time_point.inner.accumulated_ticks & inaccuracy_mask; |
| 72 | } | 73 | } |
| @@ -75,13 +76,14 @@ void NativeClock::Pause(bool is_paused) { | |||
| 75 | if (!is_paused) { | 76 | if (!is_paused) { |
| 76 | TimePoint current_time_point{}; | 77 | TimePoint current_time_point{}; |
| 77 | TimePoint new_time_point{}; | 78 | TimePoint new_time_point{}; |
| 79 | |||
| 80 | current_time_point.pack = Common::AtomicLoad128(time_point.pack.data()); | ||
| 78 | do { | 81 | do { |
| 79 | current_time_point.pack = time_point.pack; | ||
| 80 | new_time_point.pack = current_time_point.pack; | 82 | new_time_point.pack = current_time_point.pack; |
| 81 | _mm_mfence(); | 83 | _mm_mfence(); |
| 82 | new_time_point.inner.last_measure = __rdtsc(); | 84 | new_time_point.inner.last_measure = __rdtsc(); |
| 83 | } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack, | 85 | } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack, |
| 84 | current_time_point.pack)); | 86 | current_time_point.pack, current_time_point.pack)); |
| 85 | } | 87 | } |
| 86 | } | 88 | } |
| 87 | 89 | ||
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index a2d893450..6d5a1ecfd 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp | |||
| @@ -93,17 +93,19 @@ public: | |||
| 93 | static constexpr u64 ICACHE_LINE_SIZE = 64; | 93 | static constexpr u64 ICACHE_LINE_SIZE = 64; |
| 94 | 94 | ||
| 95 | const u64 cache_line_start = value & ~(ICACHE_LINE_SIZE - 1); | 95 | const u64 cache_line_start = value & ~(ICACHE_LINE_SIZE - 1); |
| 96 | parent.InvalidateCacheRange(cache_line_start, ICACHE_LINE_SIZE); | 96 | parent.system.InvalidateCpuInstructionCacheRange(cache_line_start, ICACHE_LINE_SIZE); |
| 97 | break; | 97 | break; |
| 98 | } | 98 | } |
| 99 | case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoU: | 99 | case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoU: |
| 100 | parent.ClearInstructionCache(); | 100 | parent.system.InvalidateCpuInstructionCaches(); |
| 101 | break; | 101 | break; |
| 102 | case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoUInnerSharable: | 102 | case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoUInnerSharable: |
| 103 | default: | 103 | default: |
| 104 | LOG_DEBUG(Core_ARM, "Unprocesseed instruction cache operation: {}", op); | 104 | LOG_DEBUG(Core_ARM, "Unprocesseed instruction cache operation: {}", op); |
| 105 | break; | 105 | break; |
| 106 | } | 106 | } |
| 107 | |||
| 108 | parent.jit->HaltExecution(); | ||
| 107 | } | 109 | } |
| 108 | 110 | ||
| 109 | void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override { | 111 | void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override { |
diff --git a/src/core/hle/kernel/k_code_memory.cpp b/src/core/hle/kernel/k_code_memory.cpp index b365ce7b7..63bbe02e9 100644 --- a/src/core/hle/kernel/k_code_memory.cpp +++ b/src/core/hle/kernel/k_code_memory.cpp | |||
| @@ -28,7 +28,8 @@ ResultCode KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr | |||
| 28 | auto& page_table = m_owner->PageTable(); | 28 | auto& page_table = m_owner->PageTable(); |
| 29 | 29 | ||
| 30 | // Construct the page group. | 30 | // Construct the page group. |
| 31 | m_page_group = KPageLinkedList(addr, Common::DivideUp(size, PageSize)); | 31 | m_page_group = |
| 32 | KPageLinkedList(page_table.GetPhysicalAddr(addr), Common::DivideUp(size, PageSize)); | ||
| 32 | 33 | ||
| 33 | // Lock the memory. | 34 | // Lock the memory. |
| 34 | R_TRY(page_table.LockForCodeMemory(addr, size)) | 35 | R_TRY(page_table.LockForCodeMemory(addr, size)) |
diff --git a/src/core/hle/kernel/k_page_linked_list.h b/src/core/hle/kernel/k_page_linked_list.h index 0e2ae582a..869228322 100644 --- a/src/core/hle/kernel/k_page_linked_list.h +++ b/src/core/hle/kernel/k_page_linked_list.h | |||
| @@ -89,6 +89,10 @@ public: | |||
| 89 | return ResultSuccess; | 89 | return ResultSuccess; |
| 90 | } | 90 | } |
| 91 | 91 | ||
| 92 | bool Empty() const { | ||
| 93 | return nodes.empty(); | ||
| 94 | } | ||
| 95 | |||
| 92 | private: | 96 | private: |
| 93 | std::list<Node> nodes; | 97 | std::list<Node> nodes; |
| 94 | }; | 98 | }; |
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp index 02d93b12e..599013cf6 100644 --- a/src/core/hle/kernel/k_page_table.cpp +++ b/src/core/hle/kernel/k_page_table.cpp | |||
| @@ -486,6 +486,58 @@ VAddr KPageTable::FindFreeArea(VAddr region_start, std::size_t region_num_pages, | |||
| 486 | return address; | 486 | return address; |
| 487 | } | 487 | } |
| 488 | 488 | ||
| 489 | ResultCode KPageTable::MakePageGroup(KPageLinkedList& pg, VAddr addr, size_t num_pages) { | ||
| 490 | ASSERT(this->IsLockedByCurrentThread()); | ||
| 491 | |||
| 492 | const size_t size = num_pages * PageSize; | ||
| 493 | |||
| 494 | // We're making a new group, not adding to an existing one. | ||
| 495 | R_UNLESS(pg.Empty(), ResultInvalidCurrentMemory); | ||
| 496 | |||
| 497 | // Begin traversal. | ||
| 498 | Common::PageTable::TraversalContext context; | ||
| 499 | Common::PageTable::TraversalEntry next_entry; | ||
| 500 | R_UNLESS(page_table_impl.BeginTraversal(next_entry, context, addr), ResultInvalidCurrentMemory); | ||
| 501 | |||
| 502 | // Prepare tracking variables. | ||
| 503 | PAddr cur_addr = next_entry.phys_addr; | ||
| 504 | size_t cur_size = next_entry.block_size - (cur_addr & (next_entry.block_size - 1)); | ||
| 505 | size_t tot_size = cur_size; | ||
| 506 | |||
| 507 | // Iterate, adding to group as we go. | ||
| 508 | const auto& memory_layout = system.Kernel().MemoryLayout(); | ||
| 509 | while (tot_size < size) { | ||
| 510 | R_UNLESS(page_table_impl.ContinueTraversal(next_entry, context), | ||
| 511 | ResultInvalidCurrentMemory); | ||
| 512 | |||
| 513 | if (next_entry.phys_addr != (cur_addr + cur_size)) { | ||
| 514 | const size_t cur_pages = cur_size / PageSize; | ||
| 515 | |||
| 516 | R_UNLESS(IsHeapPhysicalAddress(memory_layout, cur_addr), ResultInvalidCurrentMemory); | ||
| 517 | R_TRY(pg.AddBlock(cur_addr, cur_pages)); | ||
| 518 | |||
| 519 | cur_addr = next_entry.phys_addr; | ||
| 520 | cur_size = next_entry.block_size; | ||
| 521 | } else { | ||
| 522 | cur_size += next_entry.block_size; | ||
| 523 | } | ||
| 524 | |||
| 525 | tot_size += next_entry.block_size; | ||
| 526 | } | ||
| 527 | |||
| 528 | // Ensure we add the right amount for the last block. | ||
| 529 | if (tot_size > size) { | ||
| 530 | cur_size -= (tot_size - size); | ||
| 531 | } | ||
| 532 | |||
| 533 | // Add the last block. | ||
| 534 | const size_t cur_pages = cur_size / PageSize; | ||
| 535 | R_UNLESS(IsHeapPhysicalAddress(memory_layout, cur_addr), ResultInvalidCurrentMemory); | ||
| 536 | R_TRY(pg.AddBlock(cur_addr, cur_pages)); | ||
| 537 | |||
| 538 | return ResultSuccess; | ||
| 539 | } | ||
| 540 | |||
| 489 | ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size, | 541 | ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size, |
| 490 | KPageTable& src_page_table, VAddr src_addr) { | 542 | KPageTable& src_page_table, VAddr src_addr) { |
| 491 | KScopedLightLock lk(general_lock); | 543 | KScopedLightLock lk(general_lock); |
| @@ -1223,6 +1275,31 @@ ResultCode KPageTable::UnmapPages(VAddr address, std::size_t num_pages, KMemoryS | |||
| 1223 | return ResultSuccess; | 1275 | return ResultSuccess; |
| 1224 | } | 1276 | } |
| 1225 | 1277 | ||
| 1278 | ResultCode KPageTable::MakeAndOpenPageGroup(KPageLinkedList* out, VAddr address, size_t num_pages, | ||
| 1279 | KMemoryState state_mask, KMemoryState state, | ||
| 1280 | KMemoryPermission perm_mask, KMemoryPermission perm, | ||
| 1281 | KMemoryAttribute attr_mask, KMemoryAttribute attr) { | ||
| 1282 | // Ensure that the page group isn't null. | ||
| 1283 | ASSERT(out != nullptr); | ||
| 1284 | |||
| 1285 | // Make sure that the region we're mapping is valid for the table. | ||
| 1286 | const size_t size = num_pages * PageSize; | ||
| 1287 | R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); | ||
| 1288 | |||
| 1289 | // Lock the table. | ||
| 1290 | KScopedLightLock lk(general_lock); | ||
| 1291 | |||
| 1292 | // Check if state allows us to create the group. | ||
| 1293 | R_TRY(this->CheckMemoryState(address, size, state_mask | KMemoryState::FlagReferenceCounted, | ||
| 1294 | state | KMemoryState::FlagReferenceCounted, perm_mask, perm, | ||
| 1295 | attr_mask, attr)); | ||
| 1296 | |||
| 1297 | // Create a new page group for the region. | ||
| 1298 | R_TRY(this->MakePageGroup(*out, address, num_pages)); | ||
| 1299 | |||
| 1300 | return ResultSuccess; | ||
| 1301 | } | ||
| 1302 | |||
| 1226 | ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size, | 1303 | ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size, |
| 1227 | Svc::MemoryPermission svc_perm) { | 1304 | Svc::MemoryPermission svc_perm) { |
| 1228 | const size_t num_pages = size / PageSize; | 1305 | const size_t num_pages = size / PageSize; |
| @@ -1605,57 +1682,21 @@ ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size) | |||
| 1605 | } | 1682 | } |
| 1606 | 1683 | ||
| 1607 | ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) { | 1684 | ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) { |
| 1608 | KScopedLightLock lk(general_lock); | 1685 | return this->LockMemoryAndOpen( |
| 1609 | 1686 | nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, | |
| 1610 | KMemoryPermission new_perm = KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite; | 1687 | KMemoryState::FlagCanCodeMemory, KMemoryPermission::All, KMemoryPermission::UserReadWrite, |
| 1611 | 1688 | KMemoryAttribute::All, KMemoryAttribute::None, | |
| 1612 | KMemoryPermission old_perm{}; | 1689 | static_cast<KMemoryPermission>(KMemoryPermission::NotMapped | |
| 1613 | 1690 | KMemoryPermission::KernelReadWrite), | |
| 1614 | if (const ResultCode result{CheckMemoryState( | 1691 | KMemoryAttribute::Locked); |
| 1615 | nullptr, &old_perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, | ||
| 1616 | KMemoryState::FlagCanCodeMemory, KMemoryPermission::All, | ||
| 1617 | KMemoryPermission::UserReadWrite, KMemoryAttribute::All, KMemoryAttribute::None)}; | ||
| 1618 | result.IsError()) { | ||
| 1619 | return result; | ||
| 1620 | } | ||
| 1621 | |||
| 1622 | new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm; | ||
| 1623 | |||
| 1624 | block_manager->UpdateLock( | ||
| 1625 | addr, size / PageSize, | ||
| 1626 | [](KMemoryBlockManager::iterator block, KMemoryPermission permission) { | ||
| 1627 | block->ShareToDevice(permission); | ||
| 1628 | }, | ||
| 1629 | new_perm); | ||
| 1630 | |||
| 1631 | return ResultSuccess; | ||
| 1632 | } | 1692 | } |
| 1633 | 1693 | ||
| 1634 | ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size) { | 1694 | ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size) { |
| 1635 | KScopedLightLock lk(general_lock); | 1695 | return this->UnlockMemory(addr, size, KMemoryState::FlagCanCodeMemory, |
| 1636 | 1696 | KMemoryState::FlagCanCodeMemory, KMemoryPermission::None, | |
| 1637 | KMemoryPermission new_perm = KMemoryPermission::UserReadWrite; | 1697 | KMemoryPermission::None, KMemoryAttribute::All, |
| 1638 | 1698 | KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite, | |
| 1639 | KMemoryPermission old_perm{}; | 1699 | KMemoryAttribute::Locked, nullptr); |
| 1640 | |||
| 1641 | if (const ResultCode result{CheckMemoryState( | ||
| 1642 | nullptr, &old_perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, | ||
| 1643 | KMemoryState::FlagCanCodeMemory, KMemoryPermission::None, KMemoryPermission::None, | ||
| 1644 | KMemoryAttribute::All, KMemoryAttribute::Locked)}; | ||
| 1645 | result.IsError()) { | ||
| 1646 | return result; | ||
| 1647 | } | ||
| 1648 | |||
| 1649 | new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm; | ||
| 1650 | |||
| 1651 | block_manager->UpdateLock( | ||
| 1652 | addr, size / PageSize, | ||
| 1653 | [](KMemoryBlockManager::iterator block, KMemoryPermission permission) { | ||
| 1654 | block->UnshareToDevice(permission); | ||
| 1655 | }, | ||
| 1656 | new_perm); | ||
| 1657 | |||
| 1658 | return ResultSuccess; | ||
| 1659 | } | 1700 | } |
| 1660 | 1701 | ||
| 1661 | ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) { | 1702 | ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) { |
| @@ -1991,4 +2032,109 @@ ResultCode KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermissi | |||
| 1991 | return ResultSuccess; | 2032 | return ResultSuccess; |
| 1992 | } | 2033 | } |
| 1993 | 2034 | ||
| 2035 | ResultCode KPageTable::LockMemoryAndOpen(KPageLinkedList* out_pg, PAddr* out_paddr, VAddr addr, | ||
| 2036 | size_t size, KMemoryState state_mask, KMemoryState state, | ||
| 2037 | KMemoryPermission perm_mask, KMemoryPermission perm, | ||
| 2038 | KMemoryAttribute attr_mask, KMemoryAttribute attr, | ||
| 2039 | KMemoryPermission new_perm, KMemoryAttribute lock_attr) { | ||
| 2040 | // Validate basic preconditions. | ||
| 2041 | ASSERT((lock_attr & attr) == KMemoryAttribute::None); | ||
| 2042 | ASSERT((lock_attr & (KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared)) == | ||
| 2043 | KMemoryAttribute::None); | ||
| 2044 | |||
| 2045 | // Validate the lock request. | ||
| 2046 | const size_t num_pages = size / PageSize; | ||
| 2047 | R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory); | ||
| 2048 | |||
| 2049 | // Lock the table. | ||
| 2050 | KScopedLightLock lk(general_lock); | ||
| 2051 | |||
| 2052 | // Check that the output page group is empty, if it exists. | ||
| 2053 | if (out_pg) { | ||
| 2054 | ASSERT(out_pg->GetNumPages() == 0); | ||
| 2055 | } | ||
| 2056 | |||
| 2057 | // Check the state. | ||
| 2058 | KMemoryState old_state{}; | ||
| 2059 | KMemoryPermission old_perm{}; | ||
| 2060 | KMemoryAttribute old_attr{}; | ||
| 2061 | size_t num_allocator_blocks{}; | ||
| 2062 | R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm), | ||
| 2063 | std::addressof(old_attr), std::addressof(num_allocator_blocks), | ||
| 2064 | addr, size, state_mask | KMemoryState::FlagReferenceCounted, | ||
| 2065 | state | KMemoryState::FlagReferenceCounted, perm_mask, perm, | ||
| 2066 | attr_mask, attr)); | ||
| 2067 | |||
| 2068 | // Get the physical address, if we're supposed to. | ||
| 2069 | if (out_paddr != nullptr) { | ||
| 2070 | ASSERT(this->GetPhysicalAddressLocked(out_paddr, addr)); | ||
| 2071 | } | ||
| 2072 | |||
| 2073 | // Make the page group, if we're supposed to. | ||
| 2074 | if (out_pg != nullptr) { | ||
| 2075 | R_TRY(this->MakePageGroup(*out_pg, addr, num_pages)); | ||
| 2076 | } | ||
| 2077 | |||
| 2078 | // Decide on new perm and attr. | ||
| 2079 | new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm; | ||
| 2080 | KMemoryAttribute new_attr = static_cast<KMemoryAttribute>(old_attr | lock_attr); | ||
| 2081 | |||
| 2082 | // Update permission, if we need to. | ||
| 2083 | if (new_perm != old_perm) { | ||
| 2084 | R_TRY(Operate(addr, num_pages, new_perm, OperationType::ChangePermissions)); | ||
| 2085 | } | ||
| 2086 | |||
| 2087 | // Apply the memory block updates. | ||
| 2088 | block_manager->Update(addr, num_pages, old_state, new_perm, new_attr); | ||
| 2089 | |||
| 2090 | return ResultSuccess; | ||
| 2091 | } | ||
| 2092 | |||
| 2093 | ResultCode KPageTable::UnlockMemory(VAddr addr, size_t size, KMemoryState state_mask, | ||
| 2094 | KMemoryState state, KMemoryPermission perm_mask, | ||
| 2095 | KMemoryPermission perm, KMemoryAttribute attr_mask, | ||
| 2096 | KMemoryAttribute attr, KMemoryPermission new_perm, | ||
| 2097 | KMemoryAttribute lock_attr, const KPageLinkedList* pg) { | ||
| 2098 | // Validate basic preconditions. | ||
| 2099 | ASSERT((attr_mask & lock_attr) == lock_attr); | ||
| 2100 | ASSERT((attr & lock_attr) == lock_attr); | ||
| 2101 | |||
| 2102 | // Validate the unlock request. | ||
| 2103 | const size_t num_pages = size / PageSize; | ||
| 2104 | R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory); | ||
| 2105 | |||
| 2106 | // Lock the table. | ||
| 2107 | KScopedLightLock lk(general_lock); | ||
| 2108 | |||
| 2109 | // Check the state. | ||
| 2110 | KMemoryState old_state{}; | ||
| 2111 | KMemoryPermission old_perm{}; | ||
| 2112 | KMemoryAttribute old_attr{}; | ||
| 2113 | size_t num_allocator_blocks{}; | ||
| 2114 | R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm), | ||
| 2115 | std::addressof(old_attr), std::addressof(num_allocator_blocks), | ||
| 2116 | addr, size, state_mask | KMemoryState::FlagReferenceCounted, | ||
| 2117 | state | KMemoryState::FlagReferenceCounted, perm_mask, perm, | ||
| 2118 | attr_mask, attr)); | ||
| 2119 | |||
| 2120 | // Check the page group. | ||
| 2121 | if (pg != nullptr) { | ||
| 2122 | UNIMPLEMENTED_MSG("PageGroup support is unimplemented!"); | ||
| 2123 | } | ||
| 2124 | |||
| 2125 | // Decide on new perm and attr. | ||
| 2126 | new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm; | ||
| 2127 | KMemoryAttribute new_attr = static_cast<KMemoryAttribute>(old_attr & ~lock_attr); | ||
| 2128 | |||
| 2129 | // Update permission, if we need to. | ||
| 2130 | if (new_perm != old_perm) { | ||
| 2131 | R_TRY(Operate(addr, num_pages, new_perm, OperationType::ChangePermissions)); | ||
| 2132 | } | ||
| 2133 | |||
| 2134 | // Apply the memory block updates. | ||
| 2135 | block_manager->Update(addr, num_pages, old_state, new_perm, new_attr); | ||
| 2136 | |||
| 2137 | return ResultSuccess; | ||
| 2138 | } | ||
| 2139 | |||
| 1994 | } // namespace Kernel | 2140 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h index 54c6adf8d..bfabdf38c 100644 --- a/src/core/hle/kernel/k_page_table.h +++ b/src/core/hle/kernel/k_page_table.h | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | #include "core/file_sys/program_metadata.h" | 12 | #include "core/file_sys/program_metadata.h" |
| 13 | #include "core/hle/kernel/k_light_lock.h" | 13 | #include "core/hle/kernel/k_light_lock.h" |
| 14 | #include "core/hle/kernel/k_memory_block.h" | 14 | #include "core/hle/kernel/k_memory_block.h" |
| 15 | #include "core/hle/kernel/k_memory_layout.h" | ||
| 15 | #include "core/hle/kernel/k_memory_manager.h" | 16 | #include "core/hle/kernel/k_memory_manager.h" |
| 16 | #include "core/hle/result.h" | 17 | #include "core/hle/result.h" |
| 17 | 18 | ||
| @@ -71,6 +72,10 @@ public: | |||
| 71 | ResultCode UnlockForDeviceAddressSpace(VAddr addr, std::size_t size); | 72 | ResultCode UnlockForDeviceAddressSpace(VAddr addr, std::size_t size); |
| 72 | ResultCode LockForCodeMemory(VAddr addr, std::size_t size); | 73 | ResultCode LockForCodeMemory(VAddr addr, std::size_t size); |
| 73 | ResultCode UnlockForCodeMemory(VAddr addr, std::size_t size); | 74 | ResultCode UnlockForCodeMemory(VAddr addr, std::size_t size); |
| 75 | ResultCode MakeAndOpenPageGroup(KPageLinkedList* out, VAddr address, size_t num_pages, | ||
| 76 | KMemoryState state_mask, KMemoryState state, | ||
| 77 | KMemoryPermission perm_mask, KMemoryPermission perm, | ||
| 78 | KMemoryAttribute attr_mask, KMemoryAttribute attr); | ||
| 74 | 79 | ||
| 75 | Common::PageTable& PageTableImpl() { | 80 | Common::PageTable& PageTableImpl() { |
| 76 | return page_table_impl; | 81 | return page_table_impl; |
| @@ -159,10 +164,37 @@ private: | |||
| 159 | attr_mask, attr, ignore_attr); | 164 | attr_mask, attr, ignore_attr); |
| 160 | } | 165 | } |
| 161 | 166 | ||
| 167 | ResultCode LockMemoryAndOpen(KPageLinkedList* out_pg, PAddr* out_paddr, VAddr addr, size_t size, | ||
| 168 | KMemoryState state_mask, KMemoryState state, | ||
| 169 | KMemoryPermission perm_mask, KMemoryPermission perm, | ||
| 170 | KMemoryAttribute attr_mask, KMemoryAttribute attr, | ||
| 171 | KMemoryPermission new_perm, KMemoryAttribute lock_attr); | ||
| 172 | ResultCode UnlockMemory(VAddr addr, size_t size, KMemoryState state_mask, KMemoryState state, | ||
| 173 | KMemoryPermission perm_mask, KMemoryPermission perm, | ||
| 174 | KMemoryAttribute attr_mask, KMemoryAttribute attr, | ||
| 175 | KMemoryPermission new_perm, KMemoryAttribute lock_attr, | ||
| 176 | const KPageLinkedList* pg); | ||
| 177 | |||
| 178 | ResultCode MakePageGroup(KPageLinkedList& pg, VAddr addr, size_t num_pages); | ||
| 179 | |||
| 162 | bool IsLockedByCurrentThread() const { | 180 | bool IsLockedByCurrentThread() const { |
| 163 | return general_lock.IsLockedByCurrentThread(); | 181 | return general_lock.IsLockedByCurrentThread(); |
| 164 | } | 182 | } |
| 165 | 183 | ||
| 184 | bool IsHeapPhysicalAddress(const KMemoryLayout& layout, PAddr phys_addr) { | ||
| 185 | ASSERT(this->IsLockedByCurrentThread()); | ||
| 186 | |||
| 187 | return layout.IsHeapPhysicalAddress(cached_physical_heap_region, phys_addr); | ||
| 188 | } | ||
| 189 | |||
| 190 | bool GetPhysicalAddressLocked(PAddr* out, VAddr virt_addr) const { | ||
| 191 | ASSERT(this->IsLockedByCurrentThread()); | ||
| 192 | |||
| 193 | *out = GetPhysicalAddr(virt_addr); | ||
| 194 | |||
| 195 | return *out != 0; | ||
| 196 | } | ||
| 197 | |||
| 166 | mutable KLightLock general_lock; | 198 | mutable KLightLock general_lock; |
| 167 | mutable KLightLock map_physical_memory_lock; | 199 | mutable KLightLock map_physical_memory_lock; |
| 168 | 200 | ||
| @@ -322,6 +354,7 @@ private: | |||
| 322 | bool is_aslr_enabled{}; | 354 | bool is_aslr_enabled{}; |
| 323 | 355 | ||
| 324 | u32 heap_fill_value{}; | 356 | u32 heap_fill_value{}; |
| 357 | const KMemoryRegion* cached_physical_heap_region{}; | ||
| 325 | 358 | ||
| 326 | KMemoryManager::Pool memory_pool{KMemoryManager::Pool::Application}; | 359 | KMemoryManager::Pool memory_pool{KMemoryManager::Pool::Application}; |
| 327 | KMemoryManager::Direction allocation_option{KMemoryManager::Direction::FromFront}; | 360 | KMemoryManager::Direction allocation_option{KMemoryManager::Direction::FromFront}; |
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 839171e85..976d63234 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp | |||
| @@ -1362,8 +1362,11 @@ static ResultCode MapProcessMemory(Core::System& system, VAddr dst_address, Hand | |||
| 1362 | ResultInvalidMemoryRegion); | 1362 | ResultInvalidMemoryRegion); |
| 1363 | 1363 | ||
| 1364 | // Create a new page group. | 1364 | // Create a new page group. |
| 1365 | KMemoryInfo kBlockInfo = dst_pt.QueryInfo(dst_address); | 1365 | KPageLinkedList pg; |
| 1366 | KPageLinkedList pg(kBlockInfo.GetAddress(), kBlockInfo.GetNumPages()); | 1366 | R_TRY(src_pt.MakeAndOpenPageGroup( |
| 1367 | std::addressof(pg), src_address, size / PageSize, KMemoryState::FlagCanMapProcess, | ||
| 1368 | KMemoryState::FlagCanMapProcess, KMemoryPermission::None, KMemoryPermission::None, | ||
| 1369 | KMemoryAttribute::All, KMemoryAttribute::None)); | ||
| 1367 | 1370 | ||
| 1368 | // Map the group. | 1371 | // Map the group. |
| 1369 | R_TRY(dst_pt.MapPages(dst_address, pg, KMemoryState::SharedCode, | 1372 | R_TRY(dst_pt.MapPages(dst_address, pg, KMemoryState::SharedCode, |
| @@ -1408,8 +1411,8 @@ static ResultCode UnmapProcessMemory(Core::System& system, VAddr dst_address, Ha | |||
| 1408 | } | 1411 | } |
| 1409 | 1412 | ||
| 1410 | static ResultCode CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) { | 1413 | static ResultCode CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) { |
| 1411 | LOG_TRACE(Kernel_SVC, "called, handle_out={}, address=0x{:X}, size=0x{:X}", | 1414 | LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, size=0x{:X}", address, size); |
| 1412 | static_cast<void*>(out), address, size); | 1415 | |
| 1413 | // Get kernel instance. | 1416 | // Get kernel instance. |
| 1414 | auto& kernel = system.Kernel(); | 1417 | auto& kernel = system.Kernel(); |
| 1415 | 1418 | ||
| @@ -1664,7 +1667,7 @@ static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_ha | |||
| 1664 | return ResultInvalidAddress; | 1667 | return ResultInvalidAddress; |
| 1665 | } | 1668 | } |
| 1666 | 1669 | ||
| 1667 | if (size == 0 || Common::Is4KBAligned(size)) { | 1670 | if (size == 0 || !Common::Is4KBAligned(size)) { |
| 1668 | LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size); | 1671 | LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size); |
| 1669 | return ResultInvalidSize; | 1672 | return ResultInvalidSize; |
| 1670 | } | 1673 | } |
diff --git a/src/core/hle/service/am/applets/applet_web_browser.cpp b/src/core/hle/service/am/applets/applet_web_browser.cpp index bb5cb61be..a4b3fb187 100644 --- a/src/core/hle/service/am/applets/applet_web_browser.cpp +++ b/src/core/hle/service/am/applets/applet_web_browser.cpp | |||
| @@ -446,6 +446,14 @@ void WebBrowser::ExecuteLogin() { | |||
| 446 | } | 446 | } |
| 447 | 447 | ||
| 448 | void WebBrowser::ExecuteOffline() { | 448 | void WebBrowser::ExecuteOffline() { |
| 449 | // TODO (Morph): This is a hack for WebSession foreground web applets such as those used by | ||
| 450 | // Super Mario 3D All-Stars. | ||
| 451 | // TODO (Morph): Implement WebSession. | ||
| 452 | if (applet_mode == LibraryAppletMode::AllForegroundInitiallyHidden) { | ||
| 453 | LOG_WARNING(Service_AM, "WebSession is not implemented"); | ||
| 454 | return; | ||
| 455 | } | ||
| 456 | |||
| 449 | const auto main_url = GetMainURL(Common::FS::PathToUTF8String(offline_document)); | 457 | const auto main_url = GetMainURL(Common::FS::PathToUTF8String(offline_document)); |
| 450 | 458 | ||
| 451 | if (!Common::FS::Exists(main_url)) { | 459 | if (!Common::FS::Exists(main_url)) { |
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index 3703ca4c6..4208337db 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp | |||
| @@ -174,7 +174,7 @@ ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_, | |||
| 174 | ASSERT_MSG(dest != nullptr, "Newly created file with success cannot be found."); | 174 | ASSERT_MSG(dest != nullptr, "Newly created file with success cannot be found."); |
| 175 | 175 | ||
| 176 | ASSERT_MSG(dest->WriteBytes(src->ReadAllBytes()) == src->GetSize(), | 176 | ASSERT_MSG(dest->WriteBytes(src->ReadAllBytes()) == src->GetSize(), |
| 177 | "Could not write all of the bytes but everything else has succeded."); | 177 | "Could not write all of the bytes but everything else has succeeded."); |
| 178 | 178 | ||
| 179 | if (!src->GetContainingDirectory()->DeleteFile(Common::FS::GetFilename(src_path))) { | 179 | if (!src->GetContainingDirectory()->DeleteFile(Common::FS::GetFilename(src_path))) { |
| 180 | // TODO(DarkLordZach): Find a better error code for this | 180 | // TODO(DarkLordZach): Find a better error code for this |
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index e5c951e06..aa6cb34b7 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp | |||
| @@ -262,11 +262,6 @@ void Controller_NPad::OnInit() { | |||
| 262 | service_context.CreateEvent(fmt::format("npad:NpadStyleSetChanged_{}", i)); | 262 | service_context.CreateEvent(fmt::format("npad:NpadStyleSetChanged_{}", i)); |
| 263 | } | 263 | } |
| 264 | 264 | ||
| 265 | if (hid_core.GetSupportedStyleTag().raw == Core::HID::NpadStyleSet::None) { | ||
| 266 | // We want to support all controllers | ||
| 267 | hid_core.SetSupportedStyleTag({Core::HID::NpadStyleSet::All}); | ||
| 268 | } | ||
| 269 | |||
| 270 | supported_npad_id_types.resize(npad_id_list.size()); | 265 | supported_npad_id_types.resize(npad_id_list.size()); |
| 271 | std::memcpy(supported_npad_id_types.data(), npad_id_list.data(), | 266 | std::memcpy(supported_npad_id_types.data(), npad_id_list.data(), |
| 272 | npad_id_list.size() * sizeof(Core::HID::NpadIdType)); | 267 | npad_id_list.size() * sizeof(Core::HID::NpadIdType)); |
| @@ -288,14 +283,6 @@ void Controller_NPad::OnInit() { | |||
| 288 | WriteEmptyEntry(npad); | 283 | WriteEmptyEntry(npad); |
| 289 | } | 284 | } |
| 290 | } | 285 | } |
| 291 | |||
| 292 | // Connect controllers | ||
| 293 | for (auto& controller : controller_data) { | ||
| 294 | const auto& device = controller.device; | ||
| 295 | if (device->IsConnected()) { | ||
| 296 | AddNewControllerAt(device->GetNpadStyleIndex(), device->GetNpadIdType()); | ||
| 297 | } | ||
| 298 | } | ||
| 299 | } | 286 | } |
| 300 | 287 | ||
| 301 | void Controller_NPad::WriteEmptyEntry(NpadInternalState& npad) { | 288 | void Controller_NPad::WriteEmptyEntry(NpadInternalState& npad) { |
| @@ -320,6 +307,7 @@ void Controller_NPad::WriteEmptyEntry(NpadInternalState& npad) { | |||
| 320 | } | 307 | } |
| 321 | 308 | ||
| 322 | void Controller_NPad::OnRelease() { | 309 | void Controller_NPad::OnRelease() { |
| 310 | is_controller_initialized = false; | ||
| 323 | for (std::size_t i = 0; i < controller_data.size(); ++i) { | 311 | for (std::size_t i = 0; i < controller_data.size(); ++i) { |
| 324 | auto& controller = controller_data[i]; | 312 | auto& controller = controller_data[i]; |
| 325 | service_context.CloseEvent(controller.styleset_changed_event); | 313 | service_context.CloseEvent(controller.styleset_changed_event); |
| @@ -651,9 +639,27 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing | |||
| 651 | 639 | ||
| 652 | void Controller_NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) { | 640 | void Controller_NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) { |
| 653 | hid_core.SetSupportedStyleTag(style_set); | 641 | hid_core.SetSupportedStyleTag(style_set); |
| 642 | |||
| 643 | if (is_controller_initialized) { | ||
| 644 | return; | ||
| 645 | } | ||
| 646 | |||
| 647 | // Once SetSupportedStyleSet is called controllers are fully initialized | ||
| 648 | is_controller_initialized = true; | ||
| 649 | |||
| 650 | // Connect all active controllers | ||
| 651 | for (auto& controller : controller_data) { | ||
| 652 | const auto& device = controller.device; | ||
| 653 | if (device->IsConnected()) { | ||
| 654 | AddNewControllerAt(device->GetNpadStyleIndex(), device->GetNpadIdType()); | ||
| 655 | } | ||
| 656 | } | ||
| 654 | } | 657 | } |
| 655 | 658 | ||
| 656 | Core::HID::NpadStyleTag Controller_NPad::GetSupportedStyleSet() const { | 659 | Core::HID::NpadStyleTag Controller_NPad::GetSupportedStyleSet() const { |
| 660 | if (!is_controller_initialized) { | ||
| 661 | return {Core::HID::NpadStyleSet::None}; | ||
| 662 | } | ||
| 657 | return hid_core.GetSupportedStyleTag(); | 663 | return hid_core.GetSupportedStyleTag(); |
| 658 | } | 664 | } |
| 659 | 665 | ||
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index 3287cf435..967379f05 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h | |||
| @@ -191,16 +191,16 @@ private: | |||
| 191 | 191 | ||
| 192 | // This is nn::hid::detail::NpadFullKeyColorState | 192 | // This is nn::hid::detail::NpadFullKeyColorState |
| 193 | struct NpadFullKeyColorState { | 193 | struct NpadFullKeyColorState { |
| 194 | ColorAttribute attribute; | 194 | ColorAttribute attribute{ColorAttribute::NoController}; |
| 195 | Core::HID::NpadControllerColor fullkey; | 195 | Core::HID::NpadControllerColor fullkey{}; |
| 196 | }; | 196 | }; |
| 197 | static_assert(sizeof(NpadFullKeyColorState) == 0xC, "NpadFullKeyColorState is an invalid size"); | 197 | static_assert(sizeof(NpadFullKeyColorState) == 0xC, "NpadFullKeyColorState is an invalid size"); |
| 198 | 198 | ||
| 199 | // This is nn::hid::detail::NpadJoyColorState | 199 | // This is nn::hid::detail::NpadJoyColorState |
| 200 | struct NpadJoyColorState { | 200 | struct NpadJoyColorState { |
| 201 | ColorAttribute attribute; | 201 | ColorAttribute attribute{ColorAttribute::NoController}; |
| 202 | Core::HID::NpadControllerColor left; | 202 | Core::HID::NpadControllerColor left{}; |
| 203 | Core::HID::NpadControllerColor right; | 203 | Core::HID::NpadControllerColor right{}; |
| 204 | }; | 204 | }; |
| 205 | static_assert(sizeof(NpadJoyColorState) == 0x14, "NpadJoyColorState is an invalid size"); | 205 | static_assert(sizeof(NpadJoyColorState) == 0x14, "NpadJoyColorState is an invalid size"); |
| 206 | 206 | ||
| @@ -226,11 +226,11 @@ private: | |||
| 226 | // This is nn::hid::NpadPalmaState | 226 | // This is nn::hid::NpadPalmaState |
| 227 | // This is nn::hid::NpadSystemExtState | 227 | // This is nn::hid::NpadSystemExtState |
| 228 | struct NPadGenericState { | 228 | struct NPadGenericState { |
| 229 | s64_le sampling_number; | 229 | s64_le sampling_number{}; |
| 230 | Core::HID::NpadButtonState npad_buttons; | 230 | Core::HID::NpadButtonState npad_buttons{}; |
| 231 | Core::HID::AnalogStickState l_stick; | 231 | Core::HID::AnalogStickState l_stick{}; |
| 232 | Core::HID::AnalogStickState r_stick; | 232 | Core::HID::AnalogStickState r_stick{}; |
| 233 | NpadAttribute connection_status; | 233 | NpadAttribute connection_status{}; |
| 234 | INSERT_PADDING_BYTES(4); // Reserved | 234 | INSERT_PADDING_BYTES(4); // Reserved |
| 235 | }; | 235 | }; |
| 236 | static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size"); | 236 | static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size"); |
| @@ -253,7 +253,7 @@ private: | |||
| 253 | Common::Vec3f gyro{}; | 253 | Common::Vec3f gyro{}; |
| 254 | Common::Vec3f rotation{}; | 254 | Common::Vec3f rotation{}; |
| 255 | std::array<Common::Vec3f, 3> orientation{}; | 255 | std::array<Common::Vec3f, 3> orientation{}; |
| 256 | SixAxisSensorAttribute attribute; | 256 | SixAxisSensorAttribute attribute{}; |
| 257 | INSERT_PADDING_BYTES(4); // Reserved | 257 | INSERT_PADDING_BYTES(4); // Reserved |
| 258 | }; | 258 | }; |
| 259 | static_assert(sizeof(SixAxisSensorState) == 0x60, "SixAxisSensorState is an invalid size"); | 259 | static_assert(sizeof(SixAxisSensorState) == 0x60, "SixAxisSensorState is an invalid size"); |
| @@ -325,11 +325,11 @@ private: | |||
| 325 | 325 | ||
| 326 | // This is nn::hid::detail::NfcXcdDeviceHandleStateImpl | 326 | // This is nn::hid::detail::NfcXcdDeviceHandleStateImpl |
| 327 | struct NfcXcdDeviceHandleStateImpl { | 327 | struct NfcXcdDeviceHandleStateImpl { |
| 328 | u64 handle; | 328 | u64 handle{}; |
| 329 | bool is_available; | 329 | bool is_available{}; |
| 330 | bool is_activated; | 330 | bool is_activated{}; |
| 331 | INSERT_PADDING_BYTES(0x6); // Reserved | 331 | INSERT_PADDING_BYTES(0x6); // Reserved |
| 332 | u64 sampling_number; | 332 | u64 sampling_number{}; |
| 333 | }; | 333 | }; |
| 334 | static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18, | 334 | static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18, |
| 335 | "NfcXcdDeviceHandleStateImpl is an invalid size"); | 335 | "NfcXcdDeviceHandleStateImpl is an invalid size"); |
| @@ -366,8 +366,8 @@ private: | |||
| 366 | }; | 366 | }; |
| 367 | 367 | ||
| 368 | struct AppletFooterUi { | 368 | struct AppletFooterUi { |
| 369 | AppletFooterUiAttributes attributes; | 369 | AppletFooterUiAttributes attributes{}; |
| 370 | AppletFooterUiType type; | 370 | AppletFooterUiType type{AppletFooterUiType::None}; |
| 371 | INSERT_PADDING_BYTES(0x5B); // Reserved | 371 | INSERT_PADDING_BYTES(0x5B); // Reserved |
| 372 | }; | 372 | }; |
| 373 | static_assert(sizeof(AppletFooterUi) == 0x60, "AppletFooterUi is an invalid size"); | 373 | static_assert(sizeof(AppletFooterUi) == 0x60, "AppletFooterUi is an invalid size"); |
| @@ -404,41 +404,41 @@ private: | |||
| 404 | 404 | ||
| 405 | // This is nn::hid::detail::NpadInternalState | 405 | // This is nn::hid::detail::NpadInternalState |
| 406 | struct NpadInternalState { | 406 | struct NpadInternalState { |
| 407 | Core::HID::NpadStyleTag style_tag; | 407 | Core::HID::NpadStyleTag style_tag{Core::HID::NpadStyleSet::None}; |
| 408 | NpadJoyAssignmentMode assignment_mode; | 408 | NpadJoyAssignmentMode assignment_mode{NpadJoyAssignmentMode::Dual}; |
| 409 | NpadFullKeyColorState fullkey_color; | 409 | NpadFullKeyColorState fullkey_color{}; |
| 410 | NpadJoyColorState joycon_color; | 410 | NpadJoyColorState joycon_color{}; |
| 411 | Lifo<NPadGenericState, hid_entry_count> fullkey_lifo; | 411 | Lifo<NPadGenericState, hid_entry_count> fullkey_lifo{}; |
| 412 | Lifo<NPadGenericState, hid_entry_count> handheld_lifo; | 412 | Lifo<NPadGenericState, hid_entry_count> handheld_lifo{}; |
| 413 | Lifo<NPadGenericState, hid_entry_count> joy_dual_lifo; | 413 | Lifo<NPadGenericState, hid_entry_count> joy_dual_lifo{}; |
| 414 | Lifo<NPadGenericState, hid_entry_count> joy_left_lifo; | 414 | Lifo<NPadGenericState, hid_entry_count> joy_left_lifo{}; |
| 415 | Lifo<NPadGenericState, hid_entry_count> joy_right_lifo; | 415 | Lifo<NPadGenericState, hid_entry_count> joy_right_lifo{}; |
| 416 | Lifo<NPadGenericState, hid_entry_count> palma_lifo; | 416 | Lifo<NPadGenericState, hid_entry_count> palma_lifo{}; |
| 417 | Lifo<NPadGenericState, hid_entry_count> system_ext_lifo; | 417 | Lifo<NPadGenericState, hid_entry_count> system_ext_lifo{}; |
| 418 | Lifo<SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo; | 418 | Lifo<SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo{}; |
| 419 | Lifo<SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo; | 419 | Lifo<SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo{}; |
| 420 | Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo; | 420 | Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo{}; |
| 421 | Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo; | 421 | Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo{}; |
| 422 | Lifo<SixAxisSensorState, hid_entry_count> sixaxis_left_lifo; | 422 | Lifo<SixAxisSensorState, hid_entry_count> sixaxis_left_lifo{}; |
| 423 | Lifo<SixAxisSensorState, hid_entry_count> sixaxis_right_lifo; | 423 | Lifo<SixAxisSensorState, hid_entry_count> sixaxis_right_lifo{}; |
| 424 | DeviceType device_type; | 424 | DeviceType device_type{}; |
| 425 | INSERT_PADDING_BYTES(0x4); // Reserved | 425 | INSERT_PADDING_BYTES(0x4); // Reserved |
| 426 | NPadSystemProperties system_properties; | 426 | NPadSystemProperties system_properties{}; |
| 427 | NpadSystemButtonProperties button_properties; | 427 | NpadSystemButtonProperties button_properties{}; |
| 428 | Core::HID::NpadBatteryLevel battery_level_dual; | 428 | Core::HID::NpadBatteryLevel battery_level_dual{}; |
| 429 | Core::HID::NpadBatteryLevel battery_level_left; | 429 | Core::HID::NpadBatteryLevel battery_level_left{}; |
| 430 | Core::HID::NpadBatteryLevel battery_level_right; | 430 | Core::HID::NpadBatteryLevel battery_level_right{}; |
| 431 | union { | 431 | union { |
| 432 | Lifo<NfcXcdDeviceHandleStateImpl, 0x2> nfc_xcd_device_lifo{}; | 432 | AppletFooterUi applet_footer{}; |
| 433 | AppletFooterUi applet_footer; | 433 | Lifo<NfcXcdDeviceHandleStateImpl, 0x2> nfc_xcd_device_lifo; |
| 434 | }; | 434 | }; |
| 435 | INSERT_PADDING_BYTES(0x20); // Unknown | 435 | INSERT_PADDING_BYTES(0x20); // Unknown |
| 436 | Lifo<NpadGcTriggerState, hid_entry_count> gc_trigger_lifo; | 436 | Lifo<NpadGcTriggerState, hid_entry_count> gc_trigger_lifo{}; |
| 437 | NpadLarkType lark_type_l_and_main; | 437 | NpadLarkType lark_type_l_and_main{}; |
| 438 | NpadLarkType lark_type_r; | 438 | NpadLarkType lark_type_r{}; |
| 439 | NpadLuciaType lucia_type; | 439 | NpadLuciaType lucia_type{}; |
| 440 | NpadLagonType lagon_type; | 440 | NpadLagonType lagon_type{}; |
| 441 | NpadLagerType lager_type; | 441 | NpadLagerType lager_type{}; |
| 442 | // FW 13.x Investigate there is some sort of bitflag related to joycons | 442 | // FW 13.x Investigate there is some sort of bitflag related to joycons |
| 443 | INSERT_PADDING_BYTES(0x4); | 443 | INSERT_PADDING_BYTES(0x4); |
| 444 | INSERT_PADDING_BYTES(0xc08); // Unknown | 444 | INSERT_PADDING_BYTES(0xc08); // Unknown |
| @@ -511,7 +511,8 @@ private: | |||
| 511 | NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual}; | 511 | NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual}; |
| 512 | NpadCommunicationMode communication_mode{NpadCommunicationMode::Default}; | 512 | NpadCommunicationMode communication_mode{NpadCommunicationMode::Default}; |
| 513 | bool permit_vibration_session_enabled{false}; | 513 | bool permit_vibration_session_enabled{false}; |
| 514 | bool analog_stick_use_center_clamp{}; | 514 | bool analog_stick_use_center_clamp{false}; |
| 515 | bool is_in_lr_assignment_mode{false}; | 515 | bool is_in_lr_assignment_mode{false}; |
| 516 | bool is_controller_initialized{false}; | ||
| 516 | }; | 517 | }; |
| 517 | } // namespace Service::HID | 518 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index d9202ea6c..92e6bf889 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp | |||
| @@ -63,10 +63,6 @@ IAppletResource::IAppletResource(Core::System& system_, | |||
| 63 | MakeController<Controller_Gesture>(HidController::Gesture); | 63 | MakeController<Controller_Gesture>(HidController::Gesture); |
| 64 | MakeController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor); | 64 | MakeController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor); |
| 65 | 65 | ||
| 66 | // Homebrew doesn't try to activate some controllers, so we activate them by default | ||
| 67 | GetController<Controller_NPad>(HidController::NPad).ActivateController(); | ||
| 68 | GetController<Controller_Touchscreen>(HidController::Touchscreen).ActivateController(); | ||
| 69 | |||
| 70 | GetController<Controller_Stubbed>(HidController::HomeButton).SetCommonHeaderOffset(0x4C00); | 66 | GetController<Controller_Stubbed>(HidController::HomeButton).SetCommonHeaderOffset(0x4C00); |
| 71 | GetController<Controller_Stubbed>(HidController::SleepButton).SetCommonHeaderOffset(0x4E00); | 67 | GetController<Controller_Stubbed>(HidController::SleepButton).SetCommonHeaderOffset(0x4E00); |
| 72 | GetController<Controller_Stubbed>(HidController::CaptureButton).SetCommonHeaderOffset(0x5000); | 68 | GetController<Controller_Stubbed>(HidController::CaptureButton).SetCommonHeaderOffset(0x5000); |
| @@ -878,6 +874,10 @@ void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) { | |||
| 878 | LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}", | 874 | LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}", |
| 879 | parameters.npad_id, parameters.applet_resource_user_id, parameters.unknown); | 875 | parameters.npad_id, parameters.applet_resource_user_id, parameters.unknown); |
| 880 | 876 | ||
| 877 | // Games expect this event to be signaled after calling this function | ||
| 878 | applet_resource->GetController<Controller_NPad>(HidController::NPad) | ||
| 879 | .SignalStyleSetChangedEvent(parameters.npad_id); | ||
| 880 | |||
| 881 | IPC::ResponseBuilder rb{ctx, 2, 1}; | 881 | IPC::ResponseBuilder rb{ctx, 2, 1}; |
| 882 | rb.Push(ResultSuccess); | 882 | rb.Push(ResultSuccess); |
| 883 | rb.PushCopyObjects(applet_resource->GetController<Controller_NPad>(HidController::NPad) | 883 | rb.PushCopyObjects(applet_resource->GetController<Controller_NPad>(HidController::NPad) |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp index f9b82b504..44c54c665 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp | |||
| @@ -134,7 +134,7 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector | |||
| 134 | } | 134 | } |
| 135 | 135 | ||
| 136 | EventState status = events_interface.status[event_id]; | 136 | EventState status = events_interface.status[event_id]; |
| 137 | const bool bad_parameter = status != EventState::Free && status != EventState::Registered; | 137 | const bool bad_parameter = status == EventState::Busy; |
| 138 | if (bad_parameter) { | 138 | if (bad_parameter) { |
| 139 | std::memcpy(output.data(), ¶ms, sizeof(params)); | 139 | std::memcpy(output.data(), ¶ms, sizeof(params)); |
| 140 | return NvResult::BadParameter; | 140 | return NvResult::BadParameter; |
diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.cpp b/src/core/hle/service/nvdrv/nvdrv_interface.cpp index c16babe14..1ce2a856b 100644 --- a/src/core/hle/service/nvdrv/nvdrv_interface.cpp +++ b/src/core/hle/service/nvdrv/nvdrv_interface.cpp | |||
| @@ -26,7 +26,7 @@ void NVDRV::Open(Kernel::HLERequestContext& ctx) { | |||
| 26 | rb.Push<DeviceFD>(0); | 26 | rb.Push<DeviceFD>(0); |
| 27 | rb.PushEnum(NvResult::NotInitialized); | 27 | rb.PushEnum(NvResult::NotInitialized); |
| 28 | 28 | ||
| 29 | LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); | 29 | LOG_ERROR(Service_NVDRV, "NvServices is not initialized!"); |
| 30 | return; | 30 | return; |
| 31 | } | 31 | } |
| 32 | 32 | ||
| @@ -61,7 +61,7 @@ void NVDRV::Ioctl1(Kernel::HLERequestContext& ctx) { | |||
| 61 | 61 | ||
| 62 | if (!is_initialized) { | 62 | if (!is_initialized) { |
| 63 | ServiceError(ctx, NvResult::NotInitialized); | 63 | ServiceError(ctx, NvResult::NotInitialized); |
| 64 | LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); | 64 | LOG_ERROR(Service_NVDRV, "NvServices is not initialized!"); |
| 65 | return; | 65 | return; |
| 66 | } | 66 | } |
| 67 | 67 | ||
| @@ -87,7 +87,7 @@ void NVDRV::Ioctl2(Kernel::HLERequestContext& ctx) { | |||
| 87 | 87 | ||
| 88 | if (!is_initialized) { | 88 | if (!is_initialized) { |
| 89 | ServiceError(ctx, NvResult::NotInitialized); | 89 | ServiceError(ctx, NvResult::NotInitialized); |
| 90 | LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); | 90 | LOG_ERROR(Service_NVDRV, "NvServices is not initialized!"); |
| 91 | return; | 91 | return; |
| 92 | } | 92 | } |
| 93 | 93 | ||
| @@ -114,7 +114,7 @@ void NVDRV::Ioctl3(Kernel::HLERequestContext& ctx) { | |||
| 114 | 114 | ||
| 115 | if (!is_initialized) { | 115 | if (!is_initialized) { |
| 116 | ServiceError(ctx, NvResult::NotInitialized); | 116 | ServiceError(ctx, NvResult::NotInitialized); |
| 117 | LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); | 117 | LOG_ERROR(Service_NVDRV, "NvServices is not initialized!"); |
| 118 | return; | 118 | return; |
| 119 | } | 119 | } |
| 120 | 120 | ||
| @@ -139,7 +139,7 @@ void NVDRV::Close(Kernel::HLERequestContext& ctx) { | |||
| 139 | 139 | ||
| 140 | if (!is_initialized) { | 140 | if (!is_initialized) { |
| 141 | ServiceError(ctx, NvResult::NotInitialized); | 141 | ServiceError(ctx, NvResult::NotInitialized); |
| 142 | LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); | 142 | LOG_ERROR(Service_NVDRV, "NvServices is not initialized!"); |
| 143 | return; | 143 | return; |
| 144 | } | 144 | } |
| 145 | 145 | ||
| @@ -170,7 +170,7 @@ void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) { | |||
| 170 | 170 | ||
| 171 | if (!is_initialized) { | 171 | if (!is_initialized) { |
| 172 | ServiceError(ctx, NvResult::NotInitialized); | 172 | ServiceError(ctx, NvResult::NotInitialized); |
| 173 | LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); | 173 | LOG_ERROR(Service_NVDRV, "NvServices is not initialized!"); |
| 174 | return; | 174 | return; |
| 175 | } | 175 | } |
| 176 | 176 | ||
diff --git a/src/core/hle/service/nvflinger/buffer_item_consumer.cpp b/src/core/hle/service/nvflinger/buffer_item_consumer.cpp index 7f32c0775..93fa1ec10 100644 --- a/src/core/hle/service/nvflinger/buffer_item_consumer.cpp +++ b/src/core/hle/service/nvflinger/buffer_item_consumer.cpp | |||
| @@ -21,7 +21,7 @@ Status BufferItemConsumer::AcquireBuffer(BufferItem* item, std::chrono::nanoseco | |||
| 21 | return Status::BadValue; | 21 | return Status::BadValue; |
| 22 | } | 22 | } |
| 23 | 23 | ||
| 24 | std::unique_lock lock(mutex); | 24 | std::scoped_lock lock(mutex); |
| 25 | 25 | ||
| 26 | if (const auto status = AcquireBufferLocked(item, present_when); status != Status::NoError) { | 26 | if (const auto status = AcquireBufferLocked(item, present_when); status != Status::NoError) { |
| 27 | if (status != Status::NoBufferAvailable) { | 27 | if (status != Status::NoBufferAvailable) { |
| @@ -40,7 +40,7 @@ Status BufferItemConsumer::AcquireBuffer(BufferItem* item, std::chrono::nanoseco | |||
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | Status BufferItemConsumer::ReleaseBuffer(const BufferItem& item, Fence& release_fence) { | 42 | Status BufferItemConsumer::ReleaseBuffer(const BufferItem& item, Fence& release_fence) { |
| 43 | std::unique_lock lock(mutex); | 43 | std::scoped_lock lock(mutex); |
| 44 | 44 | ||
| 45 | if (const auto status = AddReleaseFenceLocked(item.buf, item.graphic_buffer, release_fence); | 45 | if (const auto status = AddReleaseFenceLocked(item.buf, item.graphic_buffer, release_fence); |
| 46 | status != Status::NoError) { | 46 | status != Status::NoError) { |
diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp deleted file mode 100644 index 5fead6d1b..000000000 --- a/src/core/hle/service/nvflinger/buffer_queue.cpp +++ /dev/null | |||
| @@ -1,206 +0,0 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | |||
| 7 | #include "common/assert.h" | ||
| 8 | #include "common/logging/log.h" | ||
| 9 | #include "core/core.h" | ||
| 10 | #include "core/hle/kernel/k_writable_event.h" | ||
| 11 | #include "core/hle/kernel/kernel.h" | ||
| 12 | #include "core/hle/service/kernel_helpers.h" | ||
| 13 | #include "core/hle/service/nvflinger/buffer_queue.h" | ||
| 14 | |||
| 15 | namespace Service::NVFlinger { | ||
| 16 | |||
| 17 | BufferQueue::BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_, | ||
| 18 | KernelHelpers::ServiceContext& service_context_) | ||
| 19 | : id(id_), layer_id(layer_id_), service_context{service_context_} { | ||
| 20 | buffer_wait_event = service_context.CreateEvent("BufferQueue:WaitEvent"); | ||
| 21 | } | ||
| 22 | |||
| 23 | BufferQueue::~BufferQueue() { | ||
| 24 | service_context.CloseEvent(buffer_wait_event); | ||
| 25 | } | ||
| 26 | |||
| 27 | void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) { | ||
| 28 | ASSERT(slot < buffer_slots); | ||
| 29 | LOG_WARNING(Service, "Adding graphics buffer {}", slot); | ||
| 30 | |||
| 31 | { | ||
| 32 | std::unique_lock lock{free_buffers_mutex}; | ||
| 33 | free_buffers.push_back(slot); | ||
| 34 | } | ||
| 35 | free_buffers_condition.notify_one(); | ||
| 36 | |||
| 37 | buffers[slot] = { | ||
| 38 | .slot = slot, | ||
| 39 | .status = Buffer::Status::Free, | ||
| 40 | .igbp_buffer = igbp_buffer, | ||
| 41 | .transform = {}, | ||
| 42 | .crop_rect = {}, | ||
| 43 | .swap_interval = 0, | ||
| 44 | .multi_fence = {}, | ||
| 45 | }; | ||
| 46 | |||
| 47 | buffer_wait_event->GetWritableEvent().Signal(); | ||
| 48 | } | ||
| 49 | |||
| 50 | std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::DequeueBuffer(u32 width, | ||
| 51 | u32 height) { | ||
| 52 | // Wait for first request before trying to dequeue | ||
| 53 | { | ||
| 54 | std::unique_lock lock{free_buffers_mutex}; | ||
| 55 | free_buffers_condition.wait(lock, [this] { return !free_buffers.empty() || !is_connect; }); | ||
| 56 | } | ||
| 57 | |||
| 58 | if (!is_connect) { | ||
| 59 | // Buffer was disconnected while the thread was blocked, this is most likely due to | ||
| 60 | // emulation being stopped | ||
| 61 | return std::nullopt; | ||
| 62 | } | ||
| 63 | |||
| 64 | std::unique_lock lock{free_buffers_mutex}; | ||
| 65 | |||
| 66 | auto f_itr = free_buffers.begin(); | ||
| 67 | auto slot = buffers.size(); | ||
| 68 | |||
| 69 | while (f_itr != free_buffers.end()) { | ||
| 70 | const Buffer& buffer = buffers[*f_itr]; | ||
| 71 | if (buffer.status == Buffer::Status::Free && buffer.igbp_buffer.width == width && | ||
| 72 | buffer.igbp_buffer.height == height) { | ||
| 73 | slot = *f_itr; | ||
| 74 | free_buffers.erase(f_itr); | ||
| 75 | break; | ||
| 76 | } | ||
| 77 | ++f_itr; | ||
| 78 | } | ||
| 79 | if (slot == buffers.size()) { | ||
| 80 | return std::nullopt; | ||
| 81 | } | ||
| 82 | buffers[slot].status = Buffer::Status::Dequeued; | ||
| 83 | return {{buffers[slot].slot, &buffers[slot].multi_fence}}; | ||
| 84 | } | ||
| 85 | |||
| 86 | const IGBPBuffer& BufferQueue::RequestBuffer(u32 slot) const { | ||
| 87 | ASSERT(slot < buffers.size()); | ||
| 88 | ASSERT(buffers[slot].status == Buffer::Status::Dequeued); | ||
| 89 | ASSERT(buffers[slot].slot == slot); | ||
| 90 | |||
| 91 | return buffers[slot].igbp_buffer; | ||
| 92 | } | ||
| 93 | |||
| 94 | void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform, | ||
| 95 | const Common::Rectangle<int>& crop_rect, u32 swap_interval, | ||
| 96 | Service::Nvidia::MultiFence& multi_fence) { | ||
| 97 | ASSERT(slot < buffers.size()); | ||
| 98 | ASSERT(buffers[slot].status == Buffer::Status::Dequeued); | ||
| 99 | ASSERT(buffers[slot].slot == slot); | ||
| 100 | |||
| 101 | buffers[slot].status = Buffer::Status::Queued; | ||
| 102 | buffers[slot].transform = transform; | ||
| 103 | buffers[slot].crop_rect = crop_rect; | ||
| 104 | buffers[slot].swap_interval = swap_interval; | ||
| 105 | buffers[slot].multi_fence = multi_fence; | ||
| 106 | std::unique_lock lock{queue_sequence_mutex}; | ||
| 107 | queue_sequence.push_back(slot); | ||
| 108 | } | ||
| 109 | |||
| 110 | void BufferQueue::CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& multi_fence) { | ||
| 111 | ASSERT(slot < buffers.size()); | ||
| 112 | ASSERT(buffers[slot].status != Buffer::Status::Free); | ||
| 113 | ASSERT(buffers[slot].slot == slot); | ||
| 114 | |||
| 115 | buffers[slot].status = Buffer::Status::Free; | ||
| 116 | buffers[slot].multi_fence = multi_fence; | ||
| 117 | buffers[slot].swap_interval = 0; | ||
| 118 | |||
| 119 | { | ||
| 120 | std::unique_lock lock{free_buffers_mutex}; | ||
| 121 | free_buffers.push_back(slot); | ||
| 122 | } | ||
| 123 | free_buffers_condition.notify_one(); | ||
| 124 | |||
| 125 | buffer_wait_event->GetWritableEvent().Signal(); | ||
| 126 | } | ||
| 127 | |||
| 128 | std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() { | ||
| 129 | std::unique_lock lock{queue_sequence_mutex}; | ||
| 130 | std::size_t buffer_slot = buffers.size(); | ||
| 131 | // Iterate to find a queued buffer matching the requested slot. | ||
| 132 | while (buffer_slot == buffers.size() && !queue_sequence.empty()) { | ||
| 133 | const auto slot = static_cast<std::size_t>(queue_sequence.front()); | ||
| 134 | ASSERT(slot < buffers.size()); | ||
| 135 | if (buffers[slot].status == Buffer::Status::Queued) { | ||
| 136 | ASSERT(buffers[slot].slot == slot); | ||
| 137 | buffer_slot = slot; | ||
| 138 | } | ||
| 139 | queue_sequence.pop_front(); | ||
| 140 | } | ||
| 141 | if (buffer_slot == buffers.size()) { | ||
| 142 | return std::nullopt; | ||
| 143 | } | ||
| 144 | buffers[buffer_slot].status = Buffer::Status::Acquired; | ||
| 145 | return {{buffers[buffer_slot]}}; | ||
| 146 | } | ||
| 147 | |||
| 148 | void BufferQueue::ReleaseBuffer(u32 slot) { | ||
| 149 | ASSERT(slot < buffers.size()); | ||
| 150 | ASSERT(buffers[slot].status == Buffer::Status::Acquired); | ||
| 151 | ASSERT(buffers[slot].slot == slot); | ||
| 152 | |||
| 153 | buffers[slot].status = Buffer::Status::Free; | ||
| 154 | { | ||
| 155 | std::unique_lock lock{free_buffers_mutex}; | ||
| 156 | free_buffers.push_back(slot); | ||
| 157 | } | ||
| 158 | free_buffers_condition.notify_one(); | ||
| 159 | |||
| 160 | buffer_wait_event->GetWritableEvent().Signal(); | ||
| 161 | } | ||
| 162 | |||
| 163 | void BufferQueue::Connect() { | ||
| 164 | std::unique_lock lock{queue_sequence_mutex}; | ||
| 165 | queue_sequence.clear(); | ||
| 166 | is_connect = true; | ||
| 167 | } | ||
| 168 | |||
| 169 | void BufferQueue::Disconnect() { | ||
| 170 | buffers.fill({}); | ||
| 171 | { | ||
| 172 | std::unique_lock lock{queue_sequence_mutex}; | ||
| 173 | queue_sequence.clear(); | ||
| 174 | } | ||
| 175 | buffer_wait_event->GetWritableEvent().Signal(); | ||
| 176 | is_connect = false; | ||
| 177 | free_buffers_condition.notify_one(); | ||
| 178 | } | ||
| 179 | |||
| 180 | u32 BufferQueue::Query(QueryType type) { | ||
| 181 | LOG_WARNING(Service, "(STUBBED) called type={}", type); | ||
| 182 | |||
| 183 | switch (type) { | ||
| 184 | case QueryType::NativeWindowFormat: | ||
| 185 | return static_cast<u32>(PixelFormat::RGBA8888); | ||
| 186 | case QueryType::NativeWindowWidth: | ||
| 187 | case QueryType::NativeWindowHeight: | ||
| 188 | break; | ||
| 189 | case QueryType::NativeWindowMinUndequeuedBuffers: | ||
| 190 | return 0; | ||
| 191 | case QueryType::NativeWindowConsumerUsageBits: | ||
| 192 | return 0; | ||
| 193 | } | ||
| 194 | UNIMPLEMENTED_MSG("Unimplemented query type={}", type); | ||
| 195 | return 0; | ||
| 196 | } | ||
| 197 | |||
| 198 | Kernel::KWritableEvent& BufferQueue::GetWritableBufferWaitEvent() { | ||
| 199 | return buffer_wait_event->GetWritableEvent(); | ||
| 200 | } | ||
| 201 | |||
| 202 | Kernel::KReadableEvent& BufferQueue::GetBufferWaitEvent() { | ||
| 203 | return buffer_wait_event->GetReadableEvent(); | ||
| 204 | } | ||
| 205 | |||
| 206 | } // namespace Service::NVFlinger | ||
diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h deleted file mode 100644 index f2a579133..000000000 --- a/src/core/hle/service/nvflinger/buffer_queue.h +++ /dev/null | |||
| @@ -1,154 +0,0 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <condition_variable> | ||
| 8 | #include <list> | ||
| 9 | #include <mutex> | ||
| 10 | #include <optional> | ||
| 11 | |||
| 12 | #include "common/common_funcs.h" | ||
| 13 | #include "common/math_util.h" | ||
| 14 | #include "common/swap.h" | ||
| 15 | #include "core/hle/kernel/k_event.h" | ||
| 16 | #include "core/hle/kernel/k_readable_event.h" | ||
| 17 | #include "core/hle/service/nvdrv/nvdata.h" | ||
| 18 | |||
| 19 | namespace Kernel { | ||
| 20 | class KernelCore; | ||
| 21 | class KEvent; | ||
| 22 | class KReadableEvent; | ||
| 23 | class KWritableEvent; | ||
| 24 | } // namespace Kernel | ||
| 25 | |||
| 26 | namespace Service::KernelHelpers { | ||
| 27 | class ServiceContext; | ||
| 28 | } // namespace Service::KernelHelpers | ||
| 29 | |||
| 30 | namespace Service::NVFlinger { | ||
| 31 | |||
| 32 | constexpr u32 buffer_slots = 0x40; | ||
| 33 | struct IGBPBuffer { | ||
| 34 | u32_le magic; | ||
| 35 | u32_le width; | ||
| 36 | u32_le height; | ||
| 37 | u32_le stride; | ||
| 38 | u32_le format; | ||
| 39 | u32_le usage; | ||
| 40 | INSERT_PADDING_WORDS(1); | ||
| 41 | u32_le index; | ||
| 42 | INSERT_PADDING_WORDS(3); | ||
| 43 | u32_le gpu_buffer_id; | ||
| 44 | INSERT_PADDING_WORDS(6); | ||
| 45 | u32_le external_format; | ||
| 46 | INSERT_PADDING_WORDS(10); | ||
| 47 | u32_le nvmap_handle; | ||
| 48 | u32_le offset; | ||
| 49 | INSERT_PADDING_WORDS(60); | ||
| 50 | }; | ||
| 51 | |||
| 52 | static_assert(sizeof(IGBPBuffer) == 0x16C, "IGBPBuffer has wrong size"); | ||
| 53 | |||
| 54 | class BufferQueue final { | ||
| 55 | public: | ||
| 56 | enum class QueryType { | ||
| 57 | NativeWindowWidth = 0, | ||
| 58 | NativeWindowHeight = 1, | ||
| 59 | NativeWindowFormat = 2, | ||
| 60 | /// The minimum number of buffers that must remain un-dequeued after a buffer has been | ||
| 61 | /// queued | ||
| 62 | NativeWindowMinUndequeuedBuffers = 3, | ||
| 63 | /// The consumer gralloc usage bits currently set by the consumer | ||
| 64 | NativeWindowConsumerUsageBits = 10, | ||
| 65 | }; | ||
| 66 | |||
| 67 | explicit BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_, | ||
| 68 | KernelHelpers::ServiceContext& service_context_); | ||
| 69 | ~BufferQueue(); | ||
| 70 | |||
| 71 | enum class BufferTransformFlags : u32 { | ||
| 72 | /// No transform flags are set | ||
| 73 | Unset = 0x00, | ||
| 74 | /// Flip source image horizontally (around the vertical axis) | ||
| 75 | FlipH = 0x01, | ||
| 76 | /// Flip source image vertically (around the horizontal axis) | ||
| 77 | FlipV = 0x02, | ||
| 78 | /// Rotate source image 90 degrees clockwise | ||
| 79 | Rotate90 = 0x04, | ||
| 80 | /// Rotate source image 180 degrees | ||
| 81 | Rotate180 = 0x03, | ||
| 82 | /// Rotate source image 270 degrees clockwise | ||
| 83 | Rotate270 = 0x07, | ||
| 84 | }; | ||
| 85 | |||
| 86 | enum class PixelFormat : u32 { | ||
| 87 | RGBA8888 = 1, | ||
| 88 | RGBX8888 = 2, | ||
| 89 | RGB888 = 3, | ||
| 90 | RGB565 = 4, | ||
| 91 | BGRA8888 = 5, | ||
| 92 | RGBA5551 = 6, | ||
| 93 | RRGBA4444 = 7, | ||
| 94 | }; | ||
| 95 | |||
| 96 | struct Buffer { | ||
| 97 | enum class Status { Free = 0, Queued = 1, Dequeued = 2, Acquired = 3 }; | ||
| 98 | |||
| 99 | u32 slot; | ||
| 100 | Status status = Status::Free; | ||
| 101 | IGBPBuffer igbp_buffer; | ||
| 102 | BufferTransformFlags transform; | ||
| 103 | Common::Rectangle<int> crop_rect; | ||
| 104 | u32 swap_interval; | ||
| 105 | Service::Nvidia::MultiFence multi_fence; | ||
| 106 | }; | ||
| 107 | |||
| 108 | void SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer); | ||
| 109 | std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> DequeueBuffer(u32 width, | ||
| 110 | u32 height); | ||
| 111 | const IGBPBuffer& RequestBuffer(u32 slot) const; | ||
| 112 | void QueueBuffer(u32 slot, BufferTransformFlags transform, | ||
| 113 | const Common::Rectangle<int>& crop_rect, u32 swap_interval, | ||
| 114 | Service::Nvidia::MultiFence& multi_fence); | ||
| 115 | void CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& multi_fence); | ||
| 116 | std::optional<std::reference_wrapper<const Buffer>> AcquireBuffer(); | ||
| 117 | void ReleaseBuffer(u32 slot); | ||
| 118 | void Connect(); | ||
| 119 | void Disconnect(); | ||
| 120 | u32 Query(QueryType type); | ||
| 121 | |||
| 122 | u32 GetId() const { | ||
| 123 | return id; | ||
| 124 | } | ||
| 125 | |||
| 126 | bool IsConnected() const { | ||
| 127 | return is_connect; | ||
| 128 | } | ||
| 129 | |||
| 130 | Kernel::KWritableEvent& GetWritableBufferWaitEvent(); | ||
| 131 | |||
| 132 | Kernel::KReadableEvent& GetBufferWaitEvent(); | ||
| 133 | |||
| 134 | private: | ||
| 135 | BufferQueue(const BufferQueue&) = delete; | ||
| 136 | |||
| 137 | u32 id{}; | ||
| 138 | u64 layer_id{}; | ||
| 139 | std::atomic_bool is_connect{}; | ||
| 140 | |||
| 141 | std::list<u32> free_buffers; | ||
| 142 | std::array<Buffer, buffer_slots> buffers; | ||
| 143 | std::list<u32> queue_sequence; | ||
| 144 | Kernel::KEvent* buffer_wait_event{}; | ||
| 145 | |||
| 146 | std::mutex free_buffers_mutex; | ||
| 147 | std::condition_variable free_buffers_condition; | ||
| 148 | |||
| 149 | std::mutex queue_sequence_mutex; | ||
| 150 | |||
| 151 | KernelHelpers::ServiceContext& service_context; | ||
| 152 | }; | ||
| 153 | |||
| 154 | } // namespace Service::NVFlinger | ||
diff --git a/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp b/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp index 677bec932..41fbba219 100644 --- a/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp | |||
| @@ -20,122 +20,102 @@ BufferQueueConsumer::~BufferQueueConsumer() = default; | |||
| 20 | Status BufferQueueConsumer::AcquireBuffer(BufferItem* out_buffer, | 20 | Status BufferQueueConsumer::AcquireBuffer(BufferItem* out_buffer, |
| 21 | std::chrono::nanoseconds expected_present, | 21 | std::chrono::nanoseconds expected_present, |
| 22 | u64 max_frame_number) { | 22 | u64 max_frame_number) { |
| 23 | s32 num_dropped_buffers{}; | 23 | std::scoped_lock lock(core->mutex); |
| 24 | |||
| 25 | // Check that the consumer doesn't currently have the maximum number of buffers acquired. | ||
| 26 | const s32 num_acquired_buffers{ | ||
| 27 | static_cast<s32>(std::count_if(slots.begin(), slots.end(), [](const auto& slot) { | ||
| 28 | return slot.buffer_state == BufferState::Acquired; | ||
| 29 | }))}; | ||
| 30 | |||
| 31 | if (num_acquired_buffers >= core->max_acquired_buffer_count + 1) { | ||
| 32 | LOG_ERROR(Service_NVFlinger, "max acquired buffer count reached: {} (max {})", | ||
| 33 | num_acquired_buffers, core->max_acquired_buffer_count); | ||
| 34 | return Status::InvalidOperation; | ||
| 35 | } | ||
| 24 | 36 | ||
| 25 | std::shared_ptr<IProducerListener> listener; | 37 | // Check if the queue is empty. |
| 26 | { | 38 | if (core->queue.empty()) { |
| 27 | std::unique_lock lock(core->mutex); | 39 | return Status::NoBufferAvailable; |
| 28 | 40 | } | |
| 29 | // Check that the consumer doesn't currently have the maximum number of buffers acquired. | ||
| 30 | const s32 num_acquired_buffers{ | ||
| 31 | static_cast<s32>(std::count_if(slots.begin(), slots.end(), [](const auto& slot) { | ||
| 32 | return slot.buffer_state == BufferState::Acquired; | ||
| 33 | }))}; | ||
| 34 | |||
| 35 | if (num_acquired_buffers >= core->max_acquired_buffer_count + 1) { | ||
| 36 | LOG_ERROR(Service_NVFlinger, "max acquired buffer count reached: {} (max {})", | ||
| 37 | num_acquired_buffers, core->max_acquired_buffer_count); | ||
| 38 | return Status::InvalidOperation; | ||
| 39 | } | ||
| 40 | 41 | ||
| 41 | // Check if the queue is empty. | 42 | auto front(core->queue.begin()); |
| 42 | if (core->queue.empty()) { | ||
| 43 | return Status::NoBufferAvailable; | ||
| 44 | } | ||
| 45 | 43 | ||
| 46 | auto front(core->queue.begin()); | 44 | // If expected_present is specified, we may not want to return a buffer yet. |
| 47 | 45 | if (expected_present.count() != 0) { | |
| 48 | // If expected_present is specified, we may not want to return a buffer yet. | 46 | constexpr auto MAX_REASONABLE_NSEC = 1000000000LL; // 1 second |
| 49 | if (expected_present.count() != 0) { | ||
| 50 | constexpr auto MAX_REASONABLE_NSEC = 1000000000LL; // 1 second | ||
| 51 | |||
| 52 | // The expected_present argument indicates when the buffer is expected to be | ||
| 53 | // presented on-screen. | ||
| 54 | while (core->queue.size() > 1 && !core->queue[0].is_auto_timestamp) { | ||
| 55 | const auto& buffer_item{core->queue[1]}; | ||
| 56 | |||
| 57 | // If dropping entry[0] would leave us with a buffer that the consumer is not yet | ||
| 58 | // ready for, don't drop it. | ||
| 59 | if (max_frame_number && buffer_item.frame_number > max_frame_number) { | ||
| 60 | break; | ||
| 61 | } | ||
| 62 | |||
| 63 | // If entry[1] is timely, drop entry[0] (and repeat). | ||
| 64 | const auto desired_present = buffer_item.timestamp; | ||
| 65 | if (desired_present < expected_present.count() - MAX_REASONABLE_NSEC || | ||
| 66 | desired_present > expected_present.count()) { | ||
| 67 | // This buffer is set to display in the near future, or desired_present is | ||
| 68 | // garbage. | ||
| 69 | LOG_DEBUG(Service_NVFlinger, "nodrop desire={} expect={}", desired_present, | ||
| 70 | expected_present.count()); | ||
| 71 | break; | ||
| 72 | } | ||
| 73 | |||
| 74 | LOG_DEBUG(Service_NVFlinger, "drop desire={} expect={} size={}", desired_present, | ||
| 75 | expected_present.count(), core->queue.size()); | ||
| 76 | |||
| 77 | if (core->StillTracking(*front)) { | ||
| 78 | // Front buffer is still in mSlots, so mark the slot as free | ||
| 79 | slots[front->slot].buffer_state = BufferState::Free; | ||
| 80 | core->free_buffers.push_back(front->slot); | ||
| 81 | listener = core->connected_producer_listener; | ||
| 82 | ++num_dropped_buffers; | ||
| 83 | } | ||
| 84 | |||
| 85 | core->queue.erase(front); | ||
| 86 | front = core->queue.begin(); | ||
| 87 | } | ||
| 88 | 47 | ||
| 89 | // See if the front buffer is ready to be acquired. | 48 | // The expected_present argument indicates when the buffer is expected to be presented |
| 90 | const auto desired_present = front->timestamp; | 49 | // on-screen. |
| 91 | const auto buffer_is_due = | 50 | while (core->queue.size() > 1 && !core->queue[0].is_auto_timestamp) { |
| 92 | desired_present <= expected_present.count() || | 51 | const auto& buffer_item{core->queue[1]}; |
| 93 | desired_present > expected_present.count() + MAX_REASONABLE_NSEC; | ||
| 94 | const auto consumer_is_ready = | ||
| 95 | max_frame_number > 0 ? front->frame_number <= max_frame_number : true; | ||
| 96 | 52 | ||
| 97 | if (!buffer_is_due || !consumer_is_ready) { | 53 | // If dropping entry[0] would leave us with a buffer that the consumer is not yet ready |
| 98 | LOG_DEBUG(Service_NVFlinger, "defer desire={} expect={}", desired_present, | 54 | // for, don't drop it. |
| 99 | expected_present.count()); | 55 | if (max_frame_number && buffer_item.frame_number > max_frame_number) { |
| 100 | return Status::PresentLater; | 56 | break; |
| 101 | } | 57 | } |
| 102 | 58 | ||
| 103 | LOG_DEBUG(Service_NVFlinger, "accept desire={} expect={}", desired_present, | 59 | // If entry[1] is timely, drop entry[0] (and repeat). |
| 104 | expected_present.count()); | 60 | const auto desired_present = buffer_item.timestamp; |
| 105 | } | 61 | if (desired_present < expected_present.count() - MAX_REASONABLE_NSEC || |
| 62 | desired_present > expected_present.count()) { | ||
| 63 | // This buffer is set to display in the near future, or desired_present is garbage. | ||
| 64 | LOG_DEBUG(Service_NVFlinger, "nodrop desire={} expect={}", desired_present, | ||
| 65 | expected_present.count()); | ||
| 66 | break; | ||
| 67 | } | ||
| 106 | 68 | ||
| 107 | const auto slot = front->slot; | 69 | LOG_DEBUG(Service_NVFlinger, "drop desire={} expect={} size={}", desired_present, |
| 108 | *out_buffer = *front; | 70 | expected_present.count(), core->queue.size()); |
| 109 | 71 | ||
| 110 | LOG_DEBUG(Service_NVFlinger, "acquiring slot={}", slot); | 72 | if (core->StillTracking(*front)) { |
| 73 | // Front buffer is still in mSlots, so mark the slot as free | ||
| 74 | slots[front->slot].buffer_state = BufferState::Free; | ||
| 75 | } | ||
| 111 | 76 | ||
| 112 | // If the front buffer is still being tracked, update its slot state | 77 | core->queue.erase(front); |
| 113 | if (core->StillTracking(*front)) { | 78 | front = core->queue.begin(); |
| 114 | slots[slot].acquire_called = true; | ||
| 115 | slots[slot].needs_cleanup_on_release = false; | ||
| 116 | slots[slot].buffer_state = BufferState::Acquired; | ||
| 117 | slots[slot].fence = Fence::NoFence(); | ||
| 118 | } | 79 | } |
| 119 | 80 | ||
| 120 | // If the buffer has previously been acquired by the consumer, set graphic_buffer to nullptr | 81 | // See if the front buffer is ready to be acquired. |
| 121 | // to avoid unnecessarily remapping this buffer on the consumer side. | 82 | const auto desired_present = front->timestamp; |
| 122 | if (out_buffer->acquire_called) { | 83 | if (desired_present > expected_present.count() && |
| 123 | out_buffer->graphic_buffer = nullptr; | 84 | desired_present < expected_present.count() + MAX_REASONABLE_NSEC) { |
| 85 | LOG_DEBUG(Service_NVFlinger, "defer desire={} expect={}", desired_present, | ||
| 86 | expected_present.count()); | ||
| 87 | return Status::PresentLater; | ||
| 124 | } | 88 | } |
| 125 | 89 | ||
| 126 | core->queue.erase(front); | 90 | LOG_DEBUG(Service_NVFlinger, "accept desire={} expect={}", desired_present, |
| 91 | expected_present.count()); | ||
| 92 | } | ||
| 93 | |||
| 94 | const auto slot = front->slot; | ||
| 95 | *out_buffer = *front; | ||
| 127 | 96 | ||
| 128 | // We might have freed a slot while dropping old buffers, or the producer may be blocked | 97 | LOG_DEBUG(Service_NVFlinger, "acquiring slot={}", slot); |
| 129 | // waiting for the number of buffers in the queue to decrease. | 98 | |
| 130 | core->SignalDequeueCondition(); | 99 | // If the front buffer is still being tracked, update its slot state |
| 100 | if (core->StillTracking(*front)) { | ||
| 101 | slots[slot].acquire_called = true; | ||
| 102 | slots[slot].needs_cleanup_on_release = false; | ||
| 103 | slots[slot].buffer_state = BufferState::Acquired; | ||
| 104 | slots[slot].fence = Fence::NoFence(); | ||
| 131 | } | 105 | } |
| 132 | 106 | ||
| 133 | if (listener != nullptr) { | 107 | // If the buffer has previously been acquired by the consumer, set graphic_buffer to nullptr to |
| 134 | for (s32 i = 0; i < num_dropped_buffers; ++i) { | 108 | // avoid unnecessarily remapping this buffer on the consumer side. |
| 135 | listener->OnBufferReleased(); | 109 | if (out_buffer->acquire_called) { |
| 136 | } | 110 | out_buffer->graphic_buffer = nullptr; |
| 137 | } | 111 | } |
| 138 | 112 | ||
| 113 | core->queue.erase(front); | ||
| 114 | |||
| 115 | // We might have freed a slot while dropping old buffers, or the producer may be blocked | ||
| 116 | // waiting for the number of buffers in the queue to decrease. | ||
| 117 | core->SignalDequeueCondition(); | ||
| 118 | |||
| 139 | return Status::NoError; | 119 | return Status::NoError; |
| 140 | } | 120 | } |
| 141 | 121 | ||
| @@ -147,7 +127,7 @@ Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fenc | |||
| 147 | 127 | ||
| 148 | std::shared_ptr<IProducerListener> listener; | 128 | std::shared_ptr<IProducerListener> listener; |
| 149 | { | 129 | { |
| 150 | std::unique_lock lock(core->mutex); | 130 | std::scoped_lock lock(core->mutex); |
| 151 | 131 | ||
| 152 | // If the frame number has changed because the buffer has been reallocated, we can ignore | 132 | // If the frame number has changed because the buffer has been reallocated, we can ignore |
| 153 | // this ReleaseBuffer for the old buffer. | 133 | // this ReleaseBuffer for the old buffer. |
| @@ -170,8 +150,6 @@ Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fenc | |||
| 170 | slots[slot].fence = release_fence; | 150 | slots[slot].fence = release_fence; |
| 171 | slots[slot].buffer_state = BufferState::Free; | 151 | slots[slot].buffer_state = BufferState::Free; |
| 172 | 152 | ||
| 173 | core->free_buffers.push_back(slot); | ||
| 174 | |||
| 175 | listener = core->connected_producer_listener; | 153 | listener = core->connected_producer_listener; |
| 176 | 154 | ||
| 177 | LOG_DEBUG(Service_NVFlinger, "releasing slot {}", slot); | 155 | LOG_DEBUG(Service_NVFlinger, "releasing slot {}", slot); |
| @@ -189,7 +167,7 @@ Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fenc | |||
| 189 | return Status::BadValue; | 167 | return Status::BadValue; |
| 190 | } | 168 | } |
| 191 | 169 | ||
| 192 | core->dequeue_condition.notify_all(); | 170 | core->SignalDequeueCondition(); |
| 193 | } | 171 | } |
| 194 | 172 | ||
| 195 | // Call back without lock held | 173 | // Call back without lock held |
| @@ -209,7 +187,7 @@ Status BufferQueueConsumer::Connect(std::shared_ptr<IConsumerListener> consumer_ | |||
| 209 | 187 | ||
| 210 | LOG_DEBUG(Service_NVFlinger, "controlled_by_app={}", controlled_by_app); | 188 | LOG_DEBUG(Service_NVFlinger, "controlled_by_app={}", controlled_by_app); |
| 211 | 189 | ||
| 212 | BufferQueueCore::AutoLock lock(core); | 190 | std::scoped_lock lock(core->mutex); |
| 213 | 191 | ||
| 214 | if (core->is_abandoned) { | 192 | if (core->is_abandoned) { |
| 215 | LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); | 193 | LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); |
diff --git a/src/core/hle/service/nvflinger/buffer_queue_core.cpp b/src/core/hle/service/nvflinger/buffer_queue_core.cpp index eb93b43ee..6082610e0 100644 --- a/src/core/hle/service/nvflinger/buffer_queue_core.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue_core.cpp | |||
| @@ -10,16 +10,12 @@ | |||
| 10 | 10 | ||
| 11 | namespace Service::android { | 11 | namespace Service::android { |
| 12 | 12 | ||
| 13 | BufferQueueCore::BufferQueueCore() : lock{mutex, std::defer_lock} { | 13 | BufferQueueCore::BufferQueueCore() = default; |
| 14 | for (s32 slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) { | ||
| 15 | free_slots.insert(slot); | ||
| 16 | } | ||
| 17 | } | ||
| 18 | 14 | ||
| 19 | BufferQueueCore::~BufferQueueCore() = default; | 15 | BufferQueueCore::~BufferQueueCore() = default; |
| 20 | 16 | ||
| 21 | void BufferQueueCore::NotifyShutdown() { | 17 | void BufferQueueCore::NotifyShutdown() { |
| 22 | std::unique_lock lk(mutex); | 18 | std::scoped_lock lock(mutex); |
| 23 | 19 | ||
| 24 | is_shutting_down = true; | 20 | is_shutting_down = true; |
| 25 | 21 | ||
| @@ -35,7 +31,7 @@ bool BufferQueueCore::WaitForDequeueCondition() { | |||
| 35 | return false; | 31 | return false; |
| 36 | } | 32 | } |
| 37 | 33 | ||
| 38 | dequeue_condition.wait(lock); | 34 | dequeue_condition.wait(mutex); |
| 39 | 35 | ||
| 40 | return true; | 36 | return true; |
| 41 | } | 37 | } |
| @@ -86,26 +82,15 @@ s32 BufferQueueCore::GetPreallocatedBufferCountLocked() const { | |||
| 86 | void BufferQueueCore::FreeBufferLocked(s32 slot) { | 82 | void BufferQueueCore::FreeBufferLocked(s32 slot) { |
| 87 | LOG_DEBUG(Service_NVFlinger, "slot {}", slot); | 83 | LOG_DEBUG(Service_NVFlinger, "slot {}", slot); |
| 88 | 84 | ||
| 89 | const auto had_buffer = slots[slot].graphic_buffer != nullptr; | ||
| 90 | |||
| 91 | slots[slot].graphic_buffer.reset(); | 85 | slots[slot].graphic_buffer.reset(); |
| 92 | 86 | ||
| 93 | if (slots[slot].buffer_state == BufferState::Acquired) { | 87 | if (slots[slot].buffer_state == BufferState::Acquired) { |
| 94 | slots[slot].needs_cleanup_on_release = true; | 88 | slots[slot].needs_cleanup_on_release = true; |
| 95 | } | 89 | } |
| 96 | 90 | ||
| 97 | if (slots[slot].buffer_state != BufferState::Free) { | ||
| 98 | free_slots.insert(slot); | ||
| 99 | } else if (had_buffer) { | ||
| 100 | // If the slot was FREE, but we had a buffer, we need to move this slot from the free | ||
| 101 | // buffers list to the the free slots list. | ||
| 102 | free_buffers.remove(slot); | ||
| 103 | free_slots.insert(slot); | ||
| 104 | } | ||
| 105 | |||
| 106 | slots[slot].buffer_state = BufferState::Free; | 91 | slots[slot].buffer_state = BufferState::Free; |
| 92 | slots[slot].frame_number = UINT32_MAX; | ||
| 107 | slots[slot].acquire_called = false; | 93 | slots[slot].acquire_called = false; |
| 108 | slots[slot].frame_number = 0; | ||
| 109 | slots[slot].fence = Fence::NoFence(); | 94 | slots[slot].fence = Fence::NoFence(); |
| 110 | } | 95 | } |
| 111 | 96 | ||
| @@ -126,8 +111,7 @@ bool BufferQueueCore::StillTracking(const BufferItem& item) const { | |||
| 126 | 111 | ||
| 127 | void BufferQueueCore::WaitWhileAllocatingLocked() const { | 112 | void BufferQueueCore::WaitWhileAllocatingLocked() const { |
| 128 | while (is_allocating) { | 113 | while (is_allocating) { |
| 129 | std::unique_lock lk(mutex); | 114 | is_allocating_condition.wait(mutex); |
| 130 | is_allocating_condition.wait(lk); | ||
| 131 | } | 115 | } |
| 132 | } | 116 | } |
| 133 | 117 | ||
diff --git a/src/core/hle/service/nvflinger/buffer_queue_core.h b/src/core/hle/service/nvflinger/buffer_queue_core.h index a3cd89f1c..4dfd53387 100644 --- a/src/core/hle/service/nvflinger/buffer_queue_core.h +++ b/src/core/hle/service/nvflinger/buffer_queue_core.h | |||
| @@ -50,23 +50,7 @@ private: | |||
| 50 | void WaitWhileAllocatingLocked() const; | 50 | void WaitWhileAllocatingLocked() const; |
| 51 | 51 | ||
| 52 | private: | 52 | private: |
| 53 | class AutoLock final { | ||
| 54 | public: | ||
| 55 | AutoLock(std::shared_ptr<BufferQueueCore>& core_) : core{core_} { | ||
| 56 | core->lock.lock(); | ||
| 57 | } | ||
| 58 | |||
| 59 | ~AutoLock() { | ||
| 60 | core->lock.unlock(); | ||
| 61 | } | ||
| 62 | |||
| 63 | private: | ||
| 64 | std::shared_ptr<BufferQueueCore>& core; | ||
| 65 | }; | ||
| 66 | |||
| 67 | private: | ||
| 68 | mutable std::mutex mutex; | 53 | mutable std::mutex mutex; |
| 69 | mutable std::unique_lock<std::mutex> lock; | ||
| 70 | bool is_abandoned{}; | 54 | bool is_abandoned{}; |
| 71 | bool consumer_controlled_by_app{}; | 55 | bool consumer_controlled_by_app{}; |
| 72 | std::shared_ptr<IConsumerListener> consumer_listener; | 56 | std::shared_ptr<IConsumerListener> consumer_listener; |
| @@ -75,10 +59,8 @@ private: | |||
| 75 | std::shared_ptr<IProducerListener> connected_producer_listener; | 59 | std::shared_ptr<IProducerListener> connected_producer_listener; |
| 76 | BufferQueueDefs::SlotsType slots{}; | 60 | BufferQueueDefs::SlotsType slots{}; |
| 77 | std::vector<BufferItem> queue; | 61 | std::vector<BufferItem> queue; |
| 78 | std::set<s32> free_slots; | ||
| 79 | std::list<s32> free_buffers; | ||
| 80 | s32 override_max_buffer_count{}; | 62 | s32 override_max_buffer_count{}; |
| 81 | mutable std::condition_variable dequeue_condition; | 63 | mutable std::condition_variable_any dequeue_condition; |
| 82 | const bool use_async_buffer{}; // This is always disabled on HOS | 64 | const bool use_async_buffer{}; // This is always disabled on HOS |
| 83 | bool dequeue_buffer_cannot_block{}; | 65 | bool dequeue_buffer_cannot_block{}; |
| 84 | PixelFormat default_buffer_format{PixelFormat::Rgba8888}; | 66 | PixelFormat default_buffer_format{PixelFormat::Rgba8888}; |
| @@ -90,7 +72,7 @@ private: | |||
| 90 | u64 frame_counter{}; | 72 | u64 frame_counter{}; |
| 91 | u32 transform_hint{}; | 73 | u32 transform_hint{}; |
| 92 | bool is_allocating{}; | 74 | bool is_allocating{}; |
| 93 | mutable std::condition_variable is_allocating_condition; | 75 | mutable std::condition_variable_any is_allocating_condition; |
| 94 | bool allow_allocation{true}; | 76 | bool allow_allocation{true}; |
| 95 | u64 buffer_age{}; | 77 | u64 buffer_age{}; |
| 96 | bool is_shutting_down{}; | 78 | bool is_shutting_down{}; |
diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp index 078091904..0833be57a 100644 --- a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp | |||
| @@ -38,7 +38,7 @@ BufferQueueProducer::~BufferQueueProducer() { | |||
| 38 | Status BufferQueueProducer::RequestBuffer(s32 slot, std::shared_ptr<GraphicBuffer>* buf) { | 38 | Status BufferQueueProducer::RequestBuffer(s32 slot, std::shared_ptr<GraphicBuffer>* buf) { |
| 39 | LOG_DEBUG(Service_NVFlinger, "slot {}", slot); | 39 | LOG_DEBUG(Service_NVFlinger, "slot {}", slot); |
| 40 | 40 | ||
| 41 | BufferQueueCore::AutoLock lock(core); | 41 | std::scoped_lock lock(core->mutex); |
| 42 | 42 | ||
| 43 | if (core->is_abandoned) { | 43 | if (core->is_abandoned) { |
| 44 | LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); | 44 | LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); |
| @@ -65,7 +65,7 @@ Status BufferQueueProducer::SetBufferCount(s32 buffer_count) { | |||
| 65 | std::shared_ptr<IConsumerListener> listener; | 65 | std::shared_ptr<IConsumerListener> listener; |
| 66 | 66 | ||
| 67 | { | 67 | { |
| 68 | BufferQueueCore::AutoLock lock(core); | 68 | std::scoped_lock lock(core->mutex); |
| 69 | core->WaitWhileAllocatingLocked(); | 69 | core->WaitWhileAllocatingLocked(); |
| 70 | if (core->is_abandoned) { | 70 | if (core->is_abandoned) { |
| 71 | LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); | 71 | LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); |
| @@ -156,6 +156,14 @@ Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found, | |||
| 156 | case BufferState::Acquired: | 156 | case BufferState::Acquired: |
| 157 | ++acquired_count; | 157 | ++acquired_count; |
| 158 | break; | 158 | break; |
| 159 | case BufferState::Free: | ||
| 160 | // We return the oldest of the free buffers to avoid stalling the producer if | ||
| 161 | // possible, since the consumer may still have pending reads of in-flight buffers | ||
| 162 | if (*found == BufferQueueCore::INVALID_BUFFER_SLOT || | ||
| 163 | slots[s].frame_number < slots[*found].frame_number) { | ||
| 164 | *found = s; | ||
| 165 | } | ||
| 166 | break; | ||
| 159 | default: | 167 | default: |
| 160 | break; | 168 | break; |
| 161 | } | 169 | } |
| @@ -183,27 +191,12 @@ Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found, | |||
| 183 | } | 191 | } |
| 184 | } | 192 | } |
| 185 | 193 | ||
| 186 | *found = BufferQueueCore::INVALID_BUFFER_SLOT; | ||
| 187 | |||
| 188 | // If we disconnect and reconnect quickly, we can be in a state where our slots are empty | 194 | // If we disconnect and reconnect quickly, we can be in a state where our slots are empty |
| 189 | // but we have many buffers in the queue. This can cause us to run out of memory if we | 195 | // but we have many buffers in the queue. This can cause us to run out of memory if we |
| 190 | // outrun the consumer. Wait here if it looks like we have too many buffers queued up. | 196 | // outrun the consumer. Wait here if it looks like we have too many buffers queued up. |
| 191 | const bool too_many_buffers = core->queue.size() > static_cast<size_t>(max_buffer_count); | 197 | const bool too_many_buffers = core->queue.size() > static_cast<size_t>(max_buffer_count); |
| 192 | if (too_many_buffers) { | 198 | if (too_many_buffers) { |
| 193 | LOG_ERROR(Service_NVFlinger, "queue size is {}, waiting", core->queue.size()); | 199 | LOG_ERROR(Service_NVFlinger, "queue size is {}, waiting", core->queue.size()); |
| 194 | } else { | ||
| 195 | if (!core->free_buffers.empty()) { | ||
| 196 | auto slot = core->free_buffers.begin(); | ||
| 197 | *found = *slot; | ||
| 198 | core->free_buffers.erase(slot); | ||
| 199 | } else if (core->allow_allocation && !core->free_slots.empty()) { | ||
| 200 | auto slot = core->free_slots.begin(); | ||
| 201 | // Only return free slots up to the max buffer count | ||
| 202 | if (*slot < max_buffer_count) { | ||
| 203 | *found = *slot; | ||
| 204 | core->free_slots.erase(slot); | ||
| 205 | } | ||
| 206 | } | ||
| 207 | } | 200 | } |
| 208 | 201 | ||
| 209 | // If no buffer is found, or if the queue has too many buffers outstanding, wait for a | 202 | // If no buffer is found, or if the queue has too many buffers outstanding, wait for a |
| @@ -240,7 +233,7 @@ Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool | |||
| 240 | Status return_flags = Status::NoError; | 233 | Status return_flags = Status::NoError; |
| 241 | bool attached_by_consumer = false; | 234 | bool attached_by_consumer = false; |
| 242 | { | 235 | { |
| 243 | BufferQueueCore::AutoLock lock(core); | 236 | std::scoped_lock lock(core->mutex); |
| 244 | core->WaitWhileAllocatingLocked(); | 237 | core->WaitWhileAllocatingLocked(); |
| 245 | if (format == PixelFormat::NoFormat) { | 238 | if (format == PixelFormat::NoFormat) { |
| 246 | format = core->default_buffer_format; | 239 | format = core->default_buffer_format; |
| @@ -317,12 +310,13 @@ Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool | |||
| 317 | } | 310 | } |
| 318 | 311 | ||
| 319 | { | 312 | { |
| 320 | BufferQueueCore::AutoLock lock(core); | 313 | std::scoped_lock lock(core->mutex); |
| 321 | if (core->is_abandoned) { | 314 | if (core->is_abandoned) { |
| 322 | LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); | 315 | LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); |
| 323 | return Status::NoInit; | 316 | return Status::NoInit; |
| 324 | } | 317 | } |
| 325 | 318 | ||
| 319 | slots[*out_slot].frame_number = UINT32_MAX; | ||
| 326 | slots[*out_slot].graphic_buffer = graphic_buffer; | 320 | slots[*out_slot].graphic_buffer = graphic_buffer; |
| 327 | } | 321 | } |
| 328 | } | 322 | } |
| @@ -339,7 +333,7 @@ Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool | |||
| 339 | Status BufferQueueProducer::DetachBuffer(s32 slot) { | 333 | Status BufferQueueProducer::DetachBuffer(s32 slot) { |
| 340 | LOG_DEBUG(Service_NVFlinger, "slot {}", slot); | 334 | LOG_DEBUG(Service_NVFlinger, "slot {}", slot); |
| 341 | 335 | ||
| 342 | BufferQueueCore::AutoLock lock(core); | 336 | std::scoped_lock lock(core->mutex); |
| 343 | if (core->is_abandoned) { | 337 | if (core->is_abandoned) { |
| 344 | LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); | 338 | LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); |
| 345 | return Status::NoInit; | 339 | return Status::NoInit; |
| @@ -374,7 +368,7 @@ Status BufferQueueProducer::DetachNextBuffer(std::shared_ptr<GraphicBuffer>* out | |||
| 374 | return Status::BadValue; | 368 | return Status::BadValue; |
| 375 | } | 369 | } |
| 376 | 370 | ||
| 377 | BufferQueueCore::AutoLock lock(core); | 371 | std::scoped_lock lock(core->mutex); |
| 378 | 372 | ||
| 379 | core->WaitWhileAllocatingLocked(); | 373 | core->WaitWhileAllocatingLocked(); |
| 380 | 374 | ||
| @@ -382,12 +376,21 @@ Status BufferQueueProducer::DetachNextBuffer(std::shared_ptr<GraphicBuffer>* out | |||
| 382 | LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); | 376 | LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); |
| 383 | return Status::NoInit; | 377 | return Status::NoInit; |
| 384 | } | 378 | } |
| 385 | if (core->free_buffers.empty()) { | 379 | |
| 386 | return Status::NoMemory; | 380 | // Find the oldest valid slot |
| 381 | int found = BufferQueueCore::INVALID_BUFFER_SLOT; | ||
| 382 | for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { | ||
| 383 | if (slots[s].buffer_state == BufferState::Free && slots[s].graphic_buffer != nullptr) { | ||
| 384 | if (found == BufferQueueCore::INVALID_BUFFER_SLOT || | ||
| 385 | slots[s].frame_number < slots[found].frame_number) { | ||
| 386 | found = s; | ||
| 387 | } | ||
| 388 | } | ||
| 387 | } | 389 | } |
| 388 | 390 | ||
| 389 | const s32 found = core->free_buffers.front(); | 391 | if (found == BufferQueueCore::INVALID_BUFFER_SLOT) { |
| 390 | core->free_buffers.remove(found); | 392 | return Status::NoMemory; |
| 393 | } | ||
| 391 | 394 | ||
| 392 | LOG_DEBUG(Service_NVFlinger, "Detached slot {}", found); | 395 | LOG_DEBUG(Service_NVFlinger, "Detached slot {}", found); |
| 393 | 396 | ||
| @@ -409,7 +412,7 @@ Status BufferQueueProducer::AttachBuffer(s32* out_slot, | |||
| 409 | return Status::BadValue; | 412 | return Status::BadValue; |
| 410 | } | 413 | } |
| 411 | 414 | ||
| 412 | BufferQueueCore::AutoLock lock(core); | 415 | std::scoped_lock lock(core->mutex); |
| 413 | core->WaitWhileAllocatingLocked(); | 416 | core->WaitWhileAllocatingLocked(); |
| 414 | 417 | ||
| 415 | Status return_flags = Status::NoError; | 418 | Status return_flags = Status::NoError; |
| @@ -469,7 +472,7 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input, | |||
| 469 | BufferItem item; | 472 | BufferItem item; |
| 470 | 473 | ||
| 471 | { | 474 | { |
| 472 | BufferQueueCore::AutoLock lock(core); | 475 | std::scoped_lock lock(core->mutex); |
| 473 | 476 | ||
| 474 | if (core->is_abandoned) { | 477 | if (core->is_abandoned) { |
| 475 | LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); | 478 | LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); |
| @@ -554,7 +557,9 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input, | |||
| 554 | // mark it as freed | 557 | // mark it as freed |
| 555 | if (core->StillTracking(*front)) { | 558 | if (core->StillTracking(*front)) { |
| 556 | slots[front->slot].buffer_state = BufferState::Free; | 559 | slots[front->slot].buffer_state = BufferState::Free; |
| 557 | core->free_buffers.push_front(front->slot); | 560 | // Reset the frame number of the freed buffer so that it is the first in line to |
| 561 | // be dequeued again | ||
| 562 | slots[front->slot].frame_number = 0; | ||
| 558 | } | 563 | } |
| 559 | // Overwrite the droppable buffer with the incoming one | 564 | // Overwrite the droppable buffer with the incoming one |
| 560 | *front = item; | 565 | *front = item; |
| @@ -582,10 +587,9 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input, | |||
| 582 | // Call back without the main BufferQueue lock held, but with the callback lock held so we can | 587 | // Call back without the main BufferQueue lock held, but with the callback lock held so we can |
| 583 | // ensure that callbacks occur in order | 588 | // ensure that callbacks occur in order |
| 584 | { | 589 | { |
| 585 | std::unique_lock lock(callback_mutex); | 590 | std::scoped_lock lock(callback_mutex); |
| 586 | while (callback_ticket != current_callback_ticket) { | 591 | while (callback_ticket != current_callback_ticket) { |
| 587 | std::unique_lock<std::mutex> lk(callback_mutex); | 592 | callback_condition.wait(callback_mutex); |
| 588 | callback_condition.wait(lk); | ||
| 589 | } | 593 | } |
| 590 | 594 | ||
| 591 | if (frameAvailableListener != nullptr) { | 595 | if (frameAvailableListener != nullptr) { |
| @@ -604,7 +608,7 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input, | |||
| 604 | void BufferQueueProducer::CancelBuffer(s32 slot, const Fence& fence) { | 608 | void BufferQueueProducer::CancelBuffer(s32 slot, const Fence& fence) { |
| 605 | LOG_DEBUG(Service_NVFlinger, "slot {}", slot); | 609 | LOG_DEBUG(Service_NVFlinger, "slot {}", slot); |
| 606 | 610 | ||
| 607 | BufferQueueCore::AutoLock lock(core); | 611 | std::scoped_lock lock(core->mutex); |
| 608 | 612 | ||
| 609 | if (core->is_abandoned) { | 613 | if (core->is_abandoned) { |
| 610 | LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); | 614 | LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); |
| @@ -621,8 +625,8 @@ void BufferQueueProducer::CancelBuffer(s32 slot, const Fence& fence) { | |||
| 621 | return; | 625 | return; |
| 622 | } | 626 | } |
| 623 | 627 | ||
| 624 | core->free_buffers.push_front(slot); | ||
| 625 | slots[slot].buffer_state = BufferState::Free; | 628 | slots[slot].buffer_state = BufferState::Free; |
| 629 | slots[slot].frame_number = 0; | ||
| 626 | slots[slot].fence = fence; | 630 | slots[slot].fence = fence; |
| 627 | 631 | ||
| 628 | core->SignalDequeueCondition(); | 632 | core->SignalDequeueCondition(); |
| @@ -630,7 +634,7 @@ void BufferQueueProducer::CancelBuffer(s32 slot, const Fence& fence) { | |||
| 630 | } | 634 | } |
| 631 | 635 | ||
| 632 | Status BufferQueueProducer::Query(NativeWindow what, s32* out_value) { | 636 | Status BufferQueueProducer::Query(NativeWindow what, s32* out_value) { |
| 633 | BufferQueueCore::AutoLock lock(core); | 637 | std::scoped_lock lock(core->mutex); |
| 634 | 638 | ||
| 635 | if (out_value == nullptr) { | 639 | if (out_value == nullptr) { |
| 636 | LOG_ERROR(Service_NVFlinger, "outValue was nullptr"); | 640 | LOG_ERROR(Service_NVFlinger, "outValue was nullptr"); |
| @@ -687,7 +691,7 @@ Status BufferQueueProducer::Query(NativeWindow what, s32* out_value) { | |||
| 687 | Status BufferQueueProducer::Connect(const std::shared_ptr<IProducerListener>& listener, | 691 | Status BufferQueueProducer::Connect(const std::shared_ptr<IProducerListener>& listener, |
| 688 | NativeWindowApi api, bool producer_controlled_by_app, | 692 | NativeWindowApi api, bool producer_controlled_by_app, |
| 689 | QueueBufferOutput* output) { | 693 | QueueBufferOutput* output) { |
| 690 | BufferQueueCore::AutoLock lock(core); | 694 | std::scoped_lock lock(core->mutex); |
| 691 | 695 | ||
| 692 | LOG_DEBUG(Service_NVFlinger, "api = {} producer_controlled_by_app = {}", api, | 696 | LOG_DEBUG(Service_NVFlinger, "api = {} producer_controlled_by_app = {}", api, |
| 693 | producer_controlled_by_app); | 697 | producer_controlled_by_app); |
| @@ -745,7 +749,7 @@ Status BufferQueueProducer::Disconnect(NativeWindowApi api) { | |||
| 745 | std::shared_ptr<IConsumerListener> listener; | 749 | std::shared_ptr<IConsumerListener> listener; |
| 746 | 750 | ||
| 747 | { | 751 | { |
| 748 | BufferQueueCore::AutoLock lock(core); | 752 | std::scoped_lock lock(core->mutex); |
| 749 | 753 | ||
| 750 | core->WaitWhileAllocatingLocked(); | 754 | core->WaitWhileAllocatingLocked(); |
| 751 | 755 | ||
| @@ -795,10 +799,11 @@ Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot, | |||
| 795 | return Status::BadValue; | 799 | return Status::BadValue; |
| 796 | } | 800 | } |
| 797 | 801 | ||
| 798 | BufferQueueCore::AutoLock lock(core); | 802 | std::scoped_lock lock(core->mutex); |
| 799 | 803 | ||
| 800 | slots[slot] = {}; | 804 | slots[slot] = {}; |
| 801 | slots[slot].graphic_buffer = buffer; | 805 | slots[slot].graphic_buffer = buffer; |
| 806 | slots[slot].frame_number = 0; | ||
| 802 | 807 | ||
| 803 | // Most games preallocate a buffer and pass a valid buffer here. However, it is possible for | 808 | // Most games preallocate a buffer and pass a valid buffer here. However, it is possible for |
| 804 | // this to be called with an empty buffer, Naruto Ultimate Ninja Storm is a game that does this. | 809 | // this to be called with an empty buffer, Naruto Ultimate Ninja Storm is a game that does this. |
diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.h b/src/core/hle/service/nvflinger/buffer_queue_producer.h index 5ddeebe0c..77fdcae8e 100644 --- a/src/core/hle/service/nvflinger/buffer_queue_producer.h +++ b/src/core/hle/service/nvflinger/buffer_queue_producer.h | |||
| @@ -77,7 +77,7 @@ private: | |||
| 77 | std::mutex callback_mutex; | 77 | std::mutex callback_mutex; |
| 78 | s32 next_callback_ticket{}; | 78 | s32 next_callback_ticket{}; |
| 79 | s32 current_callback_ticket{}; | 79 | s32 current_callback_ticket{}; |
| 80 | std::condition_variable callback_condition; | 80 | std::condition_variable_any callback_condition; |
| 81 | }; | 81 | }; |
| 82 | 82 | ||
| 83 | } // namespace Service::android | 83 | } // namespace Service::android |
diff --git a/src/core/hle/service/nvflinger/consumer_base.cpp b/src/core/hle/service/nvflinger/consumer_base.cpp index 3ccbb7fb8..be65a3f88 100644 --- a/src/core/hle/service/nvflinger/consumer_base.cpp +++ b/src/core/hle/service/nvflinger/consumer_base.cpp | |||
| @@ -18,7 +18,7 @@ ConsumerBase::ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_) | |||
| 18 | : consumer{std::move(consumer_)} {} | 18 | : consumer{std::move(consumer_)} {} |
| 19 | 19 | ||
| 20 | ConsumerBase::~ConsumerBase() { | 20 | ConsumerBase::~ConsumerBase() { |
| 21 | std::unique_lock lock(mutex); | 21 | std::scoped_lock lock(mutex); |
| 22 | 22 | ||
| 23 | ASSERT_MSG(is_abandoned, "consumer is not abandoned!"); | 23 | ASSERT_MSG(is_abandoned, "consumer is not abandoned!"); |
| 24 | } | 24 | } |
| @@ -36,17 +36,17 @@ void ConsumerBase::FreeBufferLocked(s32 slot_index) { | |||
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | void ConsumerBase::OnFrameAvailable(const BufferItem& item) { | 38 | void ConsumerBase::OnFrameAvailable(const BufferItem& item) { |
| 39 | std::unique_lock lock(mutex); | 39 | std::scoped_lock lock(mutex); |
| 40 | LOG_DEBUG(Service_NVFlinger, "called"); | 40 | LOG_DEBUG(Service_NVFlinger, "called"); |
| 41 | } | 41 | } |
| 42 | 42 | ||
| 43 | void ConsumerBase::OnFrameReplaced(const BufferItem& item) { | 43 | void ConsumerBase::OnFrameReplaced(const BufferItem& item) { |
| 44 | std::unique_lock lock(mutex); | 44 | std::scoped_lock lock(mutex); |
| 45 | LOG_DEBUG(Service_NVFlinger, "called"); | 45 | LOG_DEBUG(Service_NVFlinger, "called"); |
| 46 | } | 46 | } |
| 47 | 47 | ||
| 48 | void ConsumerBase::OnBuffersReleased() { | 48 | void ConsumerBase::OnBuffersReleased() { |
| 49 | std::unique_lock lock(mutex); | 49 | std::scoped_lock lock(mutex); |
| 50 | LOG_DEBUG(Service_NVFlinger, "called"); | 50 | LOG_DEBUG(Service_NVFlinger, "called"); |
| 51 | } | 51 | } |
| 52 | 52 | ||
diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 3fed51400..28d30eee2 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp | |||
| @@ -322,7 +322,7 @@ struct Memory::Impl { | |||
| 322 | } | 322 | } |
| 323 | 323 | ||
| 324 | if (Settings::IsFastmemEnabled()) { | 324 | if (Settings::IsFastmemEnabled()) { |
| 325 | const bool is_read_enable = !Settings::IsGPULevelExtreme() || !cached; | 325 | const bool is_read_enable = Settings::IsGPULevelHigh() || !cached; |
| 326 | system.DeviceMemory().buffer.Protect(vaddr, size, is_read_enable, !cached); | 326 | system.DeviceMemory().buffer.Protect(vaddr, size, is_read_enable, !cached); |
| 327 | } | 327 | } |
| 328 | 328 | ||
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp index d3cbb14a9..cb47d253c 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp | |||
| @@ -2,6 +2,8 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <bit> | ||
| 6 | |||
| 5 | #include "shader_recompiler/backend/spirv/emit_spirv.h" | 7 | #include "shader_recompiler/backend/spirv/emit_spirv.h" |
| 6 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" | 8 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" |
| 7 | #include "shader_recompiler/backend/spirv/spirv_emit_context.h" | 9 | #include "shader_recompiler/backend/spirv/spirv_emit_context.h" |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp index 3c2a5e16f..aa7082978 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <bit> | ||
| 5 | #include <tuple> | 6 | #include <tuple> |
| 6 | #include <utility> | 7 | #include <utility> |
| 7 | 8 | ||
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index 53be98ced..28f6a6184 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #include <algorithm> | 5 | #include <algorithm> |
| 6 | #include <array> | 6 | #include <array> |
| 7 | #include <bit> | ||
| 7 | #include <climits> | 8 | #include <climits> |
| 8 | 9 | ||
| 9 | #include <boost/container/static_vector.hpp> | 10 | #include <boost/container/static_vector.hpp> |
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_load.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_load.cpp index 57b4f0eee..60732215b 100644 --- a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_load.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_load.cpp | |||
| @@ -132,7 +132,7 @@ void Impl(TranslatorVisitor& v, u64 insn, bool is_bindless) { | |||
| 132 | multisample = v.X(meta_reg++); | 132 | multisample = v.X(meta_reg++); |
| 133 | } | 133 | } |
| 134 | if (tld.clamp != 0) { | 134 | if (tld.clamp != 0) { |
| 135 | throw NotImplementedException("TLD.CL - CLAMP is not implmented"); | 135 | throw NotImplementedException("TLD.CL - CLAMP is not implemented"); |
| 136 | } | 136 | } |
| 137 | IR::TextureInstInfo info{}; | 137 | IR::TextureInstInfo info{}; |
| 138 | info.type.Assign(GetType(tld.type)); | 138 | info.type.Assign(GetType(tld.type)); |
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_mipmap_level.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_mipmap_level.cpp index 311a9e763..f89ce1b68 100644 --- a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_mipmap_level.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_mipmap_level.cpp | |||
| @@ -81,7 +81,7 @@ void Impl(TranslatorVisitor& v, u64 insn, bool is_bindless) { | |||
| 81 | } const tmml{insn}; | 81 | } const tmml{insn}; |
| 82 | 82 | ||
| 83 | if ((tmml.mask & 0b1100) != 0) { | 83 | if ((tmml.mask & 0b1100) != 0) { |
| 84 | throw NotImplementedException("TMML BA results are not implmented"); | 84 | throw NotImplementedException("TMML BA results are not implemented"); |
| 85 | } | 85 | } |
| 86 | const IR::Value coords{MakeCoords(v, tmml.coord_reg, tmml.type)}; | 86 | const IR::Value coords{MakeCoords(v, tmml.coord_reg, tmml.type)}; |
| 87 | 87 | ||
diff --git a/src/video_core/command_classes/codecs/codec.cpp b/src/video_core/command_classes/codecs/codec.cpp index 81fac94bf..40f7755e8 100644 --- a/src/video_core/command_classes/codecs/codec.cpp +++ b/src/video_core/command_classes/codecs/codec.cpp | |||
| @@ -56,6 +56,18 @@ AVPixelFormat GetGpuFormat(AVCodecContext* av_codec_ctx, const AVPixelFormat* pi | |||
| 56 | av_codec_ctx->pix_fmt = PREFERRED_CPU_FMT; | 56 | av_codec_ctx->pix_fmt = PREFERRED_CPU_FMT; |
| 57 | return PREFERRED_CPU_FMT; | 57 | return PREFERRED_CPU_FMT; |
| 58 | } | 58 | } |
| 59 | |||
| 60 | // List all the currently available hwcontext in ffmpeg | ||
| 61 | std::vector<AVHWDeviceType> ListSupportedContexts() { | ||
| 62 | std::vector<AVHWDeviceType> contexts{}; | ||
| 63 | AVHWDeviceType current_device_type = AV_HWDEVICE_TYPE_NONE; | ||
| 64 | do { | ||
| 65 | current_device_type = av_hwdevice_iterate_types(current_device_type); | ||
| 66 | contexts.push_back(current_device_type); | ||
| 67 | } while (current_device_type != AV_HWDEVICE_TYPE_NONE); | ||
| 68 | return contexts; | ||
| 69 | } | ||
| 70 | |||
| 59 | } // namespace | 71 | } // namespace |
| 60 | 72 | ||
| 61 | void AVFrameDeleter(AVFrame* ptr) { | 73 | void AVFrameDeleter(AVFrame* ptr) { |
| @@ -76,17 +88,6 @@ Codec::~Codec() { | |||
| 76 | av_buffer_unref(&av_gpu_decoder); | 88 | av_buffer_unref(&av_gpu_decoder); |
| 77 | } | 89 | } |
| 78 | 90 | ||
| 79 | // List all the currently available hwcontext in ffmpeg | ||
| 80 | static std::vector<AVHWDeviceType> ListSupportedContexts() { | ||
| 81 | std::vector<AVHWDeviceType> contexts{}; | ||
| 82 | AVHWDeviceType current_device_type = AV_HWDEVICE_TYPE_NONE; | ||
| 83 | do { | ||
| 84 | current_device_type = av_hwdevice_iterate_types(current_device_type); | ||
| 85 | contexts.push_back(current_device_type); | ||
| 86 | } while (current_device_type != AV_HWDEVICE_TYPE_NONE); | ||
| 87 | return contexts; | ||
| 88 | } | ||
| 89 | |||
| 90 | bool Codec::CreateGpuAvDevice() { | 91 | bool Codec::CreateGpuAvDevice() { |
| 91 | static constexpr auto HW_CONFIG_METHOD = AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX; | 92 | static constexpr auto HW_CONFIG_METHOD = AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX; |
| 92 | static const auto supported_contexts = ListSupportedContexts(); | 93 | static const auto supported_contexts = ListSupportedContexts(); |
| @@ -96,6 +97,8 @@ bool Codec::CreateGpuAvDevice() { | |||
| 96 | LOG_DEBUG(Service_NVDRV, "{} explicitly unsupported", av_hwdevice_get_type_name(type)); | 97 | LOG_DEBUG(Service_NVDRV, "{} explicitly unsupported", av_hwdevice_get_type_name(type)); |
| 97 | continue; | 98 | continue; |
| 98 | } | 99 | } |
| 100 | // Avoid memory leak from not cleaning up after av_hwdevice_ctx_create | ||
| 101 | av_buffer_unref(&av_gpu_decoder); | ||
| 99 | const int hwdevice_res = av_hwdevice_ctx_create(&av_gpu_decoder, type, nullptr, nullptr, 0); | 102 | const int hwdevice_res = av_hwdevice_ctx_create(&av_gpu_decoder, type, nullptr, nullptr, 0); |
| 100 | if (hwdevice_res < 0) { | 103 | if (hwdevice_res < 0) { |
| 101 | LOG_DEBUG(Service_NVDRV, "{} av_hwdevice_ctx_create failed {}", | 104 | LOG_DEBUG(Service_NVDRV, "{} av_hwdevice_ctx_create failed {}", |
| @@ -127,15 +130,19 @@ bool Codec::CreateGpuAvDevice() { | |||
| 127 | av_codec->name, av_hwdevice_get_type_name(type)); | 130 | av_codec->name, av_hwdevice_get_type_name(type)); |
| 128 | break; | 131 | break; |
| 129 | } | 132 | } |
| 130 | if (config->methods & HW_CONFIG_METHOD && config->device_type == type) { | 133 | if ((config->methods & HW_CONFIG_METHOD) != 0 && config->device_type == type) { |
| 131 | av_codec_ctx->pix_fmt = config->pix_fmt; | 134 | #if defined(__unix__) |
| 132 | if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX) { | 135 | // Some linux decoding backends are reported to crash with this config method |
| 136 | // TODO(ameerj): Properly support this method | ||
| 137 | if ((config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX) != 0) { | ||
| 133 | // skip zero-copy decoders, we don't currently support them | 138 | // skip zero-copy decoders, we don't currently support them |
| 134 | LOG_DEBUG(Service_NVDRV, "Skipping decoder {} with unsupported capability {}.", | 139 | LOG_DEBUG(Service_NVDRV, "Skipping decoder {} with unsupported capability {}.", |
| 135 | av_hwdevice_get_type_name(type), config->methods); | 140 | av_hwdevice_get_type_name(type), config->methods); |
| 136 | continue; | 141 | continue; |
| 137 | } | 142 | } |
| 143 | #endif | ||
| 138 | LOG_INFO(Service_NVDRV, "Using {} GPU decoder", av_hwdevice_get_type_name(type)); | 144 | LOG_INFO(Service_NVDRV, "Using {} GPU decoder", av_hwdevice_get_type_name(type)); |
| 145 | av_codec_ctx->pix_fmt = config->pix_fmt; | ||
| 139 | return true; | 146 | return true; |
| 140 | } | 147 | } |
| 141 | } | 148 | } |
diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp index 656dd7eb0..597301eeb 100644 --- a/src/video_core/renderer_opengl/gl_device.cpp +++ b/src/video_core/renderer_opengl/gl_device.cpp | |||
| @@ -282,7 +282,7 @@ void main() { | |||
| 282 | 282 | ||
| 283 | u64 Device::GetCurrentDedicatedVideoMemory() const { | 283 | u64 Device::GetCurrentDedicatedVideoMemory() const { |
| 284 | GLint cur_avail_mem_kb = 0; | 284 | GLint cur_avail_mem_kb = 0; |
| 285 | glGetIntegerv(GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX, &cur_avail_mem_kb); | 285 | glGetIntegerv(GL_GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX, &cur_avail_mem_kb); |
| 286 | return static_cast<u64>(cur_avail_mem_kb) * 1_KiB; | 286 | return static_cast<u64>(cur_avail_mem_kb) * 1_KiB; |
| 287 | } | 287 | } |
| 288 | 288 | ||
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 7e06d0069..e6f9ece8b 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -15,8 +15,9 @@ | |||
| 15 | #include "common/logging/log.h" | 15 | #include "common/logging/log.h" |
| 16 | #include "common/math_util.h" | 16 | #include "common/math_util.h" |
| 17 | #include "common/microprofile.h" | 17 | #include "common/microprofile.h" |
| 18 | #include "common/scope_exit.h" | ||
| 18 | #include "common/settings.h" | 19 | #include "common/settings.h" |
| 19 | #include "core/memory.h" | 20 | |
| 20 | #include "video_core/engines/kepler_compute.h" | 21 | #include "video_core/engines/kepler_compute.h" |
| 21 | #include "video_core/engines/maxwell_3d.h" | 22 | #include "video_core/engines/maxwell_3d.h" |
| 22 | #include "video_core/memory_manager.h" | 23 | #include "video_core/memory_manager.h" |
| @@ -210,6 +211,7 @@ void RasterizerOpenGL::Clear() { | |||
| 210 | void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) { | 211 | void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) { |
| 211 | MICROPROFILE_SCOPE(OpenGL_Drawing); | 212 | MICROPROFILE_SCOPE(OpenGL_Drawing); |
| 212 | 213 | ||
| 214 | SCOPE_EXIT({ gpu.TickWork(); }); | ||
| 213 | query_cache.UpdateCounters(); | 215 | query_cache.UpdateCounters(); |
| 214 | 216 | ||
| 215 | GraphicsPipeline* const pipeline{shader_cache.CurrentGraphicsPipeline()}; | 217 | GraphicsPipeline* const pipeline{shader_cache.CurrentGraphicsPipeline()}; |
| @@ -265,8 +267,6 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) { | |||
| 265 | 267 | ||
| 266 | ++num_queued_commands; | 268 | ++num_queued_commands; |
| 267 | has_written_global_memory |= pipeline->WritesGlobalMemory(); | 269 | has_written_global_memory |= pipeline->WritesGlobalMemory(); |
| 268 | |||
| 269 | gpu.TickWork(); | ||
| 270 | } | 270 | } |
| 271 | 271 | ||
| 272 | void RasterizerOpenGL::DispatchCompute() { | 272 | void RasterizerOpenGL::DispatchCompute() { |
| @@ -352,7 +352,7 @@ void RasterizerOpenGL::OnCPUWrite(VAddr addr, u64 size) { | |||
| 352 | shader_cache.OnCPUWrite(addr, size); | 352 | shader_cache.OnCPUWrite(addr, size); |
| 353 | { | 353 | { |
| 354 | std::scoped_lock lock{texture_cache.mutex}; | 354 | std::scoped_lock lock{texture_cache.mutex}; |
| 355 | texture_cache.CachedWriteMemory(addr, size); | 355 | texture_cache.WriteMemory(addr, size); |
| 356 | } | 356 | } |
| 357 | { | 357 | { |
| 358 | std::scoped_lock lock{buffer_cache.mutex}; | 358 | std::scoped_lock lock{buffer_cache.mutex}; |
| @@ -364,10 +364,6 @@ void RasterizerOpenGL::SyncGuestHost() { | |||
| 364 | MICROPROFILE_SCOPE(OpenGL_CacheManagement); | 364 | MICROPROFILE_SCOPE(OpenGL_CacheManagement); |
| 365 | shader_cache.SyncGuestHost(); | 365 | shader_cache.SyncGuestHost(); |
| 366 | { | 366 | { |
| 367 | std::scoped_lock lock{texture_cache.mutex}; | ||
| 368 | texture_cache.FlushCachedWrites(); | ||
| 369 | } | ||
| 370 | { | ||
| 371 | std::scoped_lock lock{buffer_cache.mutex}; | 367 | std::scoped_lock lock{buffer_cache.mutex}; |
| 372 | buffer_cache.FlushCachedWrites(); | 368 | buffer_cache.FlushCachedWrites(); |
| 373 | } | 369 | } |
diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp index ec03cca38..abda1c490 100644 --- a/src/video_core/renderer_vulkan/blit_image.cpp +++ b/src/video_core/renderer_vulkan/blit_image.cpp | |||
| @@ -367,17 +367,14 @@ BlitImageHelper::BlitImageHelper(const Device& device_, VKScheduler& scheduler_, | |||
| 367 | PipelineLayoutCreateInfo(two_textures_set_layout.address()))), | 367 | PipelineLayoutCreateInfo(two_textures_set_layout.address()))), |
| 368 | full_screen_vert(BuildShader(device, FULL_SCREEN_TRIANGLE_VERT_SPV)), | 368 | full_screen_vert(BuildShader(device, FULL_SCREEN_TRIANGLE_VERT_SPV)), |
| 369 | blit_color_to_color_frag(BuildShader(device, VULKAN_BLIT_COLOR_FLOAT_FRAG_SPV)), | 369 | blit_color_to_color_frag(BuildShader(device, VULKAN_BLIT_COLOR_FLOAT_FRAG_SPV)), |
| 370 | blit_depth_stencil_frag(BuildShader(device, VULKAN_BLIT_DEPTH_STENCIL_FRAG_SPV)), | ||
| 370 | convert_depth_to_float_frag(BuildShader(device, CONVERT_DEPTH_TO_FLOAT_FRAG_SPV)), | 371 | convert_depth_to_float_frag(BuildShader(device, CONVERT_DEPTH_TO_FLOAT_FRAG_SPV)), |
| 371 | convert_float_to_depth_frag(BuildShader(device, CONVERT_FLOAT_TO_DEPTH_FRAG_SPV)), | 372 | convert_float_to_depth_frag(BuildShader(device, CONVERT_FLOAT_TO_DEPTH_FRAG_SPV)), |
| 372 | convert_abgr8_to_d24s8_frag(BuildShader(device, CONVERT_ABGR8_TO_D24S8_FRAG_SPV)), | 373 | convert_abgr8_to_d24s8_frag(BuildShader(device, CONVERT_ABGR8_TO_D24S8_FRAG_SPV)), |
| 373 | convert_d24s8_to_abgr8_frag(BuildShader(device, CONVERT_D24S8_TO_ABGR8_FRAG_SPV)), | 374 | convert_d24s8_to_abgr8_frag(BuildShader(device, CONVERT_D24S8_TO_ABGR8_FRAG_SPV)), |
| 374 | convert_s8d24_to_abgr8_frag(BuildShader(device, CONVERT_S8D24_TO_ABGR8_FRAG_SPV)), | 375 | convert_s8d24_to_abgr8_frag(BuildShader(device, CONVERT_S8D24_TO_ABGR8_FRAG_SPV)), |
| 375 | linear_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_LINEAR>)), | 376 | linear_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_LINEAR>)), |
| 376 | nearest_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_NEAREST>)) { | 377 | nearest_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_NEAREST>)) {} |
| 377 | if (device.IsExtShaderStencilExportSupported()) { | ||
| 378 | blit_depth_stencil_frag = BuildShader(device, VULKAN_BLIT_DEPTH_STENCIL_FRAG_SPV); | ||
| 379 | } | ||
| 380 | } | ||
| 381 | 378 | ||
| 382 | BlitImageHelper::~BlitImageHelper() = default; | 379 | BlitImageHelper::~BlitImageHelper() = default; |
| 383 | 380 | ||
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index dd6e0027e..fa87d37f8 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp | |||
| @@ -408,7 +408,7 @@ void RasterizerVulkan::OnCPUWrite(VAddr addr, u64 size) { | |||
| 408 | pipeline_cache.OnCPUWrite(addr, size); | 408 | pipeline_cache.OnCPUWrite(addr, size); |
| 409 | { | 409 | { |
| 410 | std::scoped_lock lock{texture_cache.mutex}; | 410 | std::scoped_lock lock{texture_cache.mutex}; |
| 411 | texture_cache.CachedWriteMemory(addr, size); | 411 | texture_cache.WriteMemory(addr, size); |
| 412 | } | 412 | } |
| 413 | { | 413 | { |
| 414 | std::scoped_lock lock{buffer_cache.mutex}; | 414 | std::scoped_lock lock{buffer_cache.mutex}; |
| @@ -419,10 +419,6 @@ void RasterizerVulkan::OnCPUWrite(VAddr addr, u64 size) { | |||
| 419 | void RasterizerVulkan::SyncGuestHost() { | 419 | void RasterizerVulkan::SyncGuestHost() { |
| 420 | pipeline_cache.SyncGuestHost(); | 420 | pipeline_cache.SyncGuestHost(); |
| 421 | { | 421 | { |
| 422 | std::scoped_lock lock{texture_cache.mutex}; | ||
| 423 | texture_cache.FlushCachedWrites(); | ||
| 424 | } | ||
| 425 | { | ||
| 426 | std::scoped_lock lock{buffer_cache.mutex}; | 422 | std::scoped_lock lock{buffer_cache.mutex}; |
| 427 | buffer_cache.FlushCachedWrites(); | 423 | buffer_cache.FlushCachedWrites(); |
| 428 | } | 424 | } |
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index f2890d263..2c2ccc7c6 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp | |||
| @@ -1451,8 +1451,7 @@ bool Image::BlitScaleHelper(bool scale_up) { | |||
| 1451 | 1451 | ||
| 1452 | runtime->blit_image_helper.BlitColor(blit_framebuffer.get(), color_view, dst_region, | 1452 | runtime->blit_image_helper.BlitColor(blit_framebuffer.get(), color_view, dst_region, |
| 1453 | src_region, operation, BLIT_OPERATION); | 1453 | src_region, operation, BLIT_OPERATION); |
| 1454 | } else if (!runtime->device.IsBlitDepthStencilSupported() && | 1454 | } else if (aspect_mask == (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { |
| 1455 | aspect_mask == (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { | ||
| 1456 | if (!blit_framebuffer) { | 1455 | if (!blit_framebuffer) { |
| 1457 | blit_framebuffer = std::make_unique<Framebuffer>(*runtime, nullptr, view_ptr, extent); | 1456 | blit_framebuffer = std::make_unique<Framebuffer>(*runtime, nullptr, view_ptr, extent); |
| 1458 | } | 1457 | } |
diff --git a/src/video_core/texture_cache/image_base.h b/src/video_core/texture_cache/image_base.h index cc7999027..dd0106432 100644 --- a/src/video_core/texture_cache/image_base.h +++ b/src/video_core/texture_cache/image_base.h | |||
| @@ -39,9 +39,6 @@ enum class ImageFlagBits : u32 { | |||
| 39 | Rescaled = 1 << 13, | 39 | Rescaled = 1 << 13, |
| 40 | CheckingRescalable = 1 << 14, | 40 | CheckingRescalable = 1 << 14, |
| 41 | IsRescalable = 1 << 15, | 41 | IsRescalable = 1 << 15, |
| 42 | |||
| 43 | // Cached CPU | ||
| 44 | CachedCpuModified = 1 << 16, ///< Contents have been modified from the CPU | ||
| 45 | }; | 42 | }; |
| 46 | DECLARE_ENUM_FLAG_OPERATORS(ImageFlagBits) | 43 | DECLARE_ENUM_FLAG_OPERATORS(ImageFlagBits) |
| 47 | 44 | ||
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 099b2ae1b..8fef74117 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h | |||
| @@ -438,23 +438,6 @@ void TextureCache<P>::WriteMemory(VAddr cpu_addr, size_t size) { | |||
| 438 | } | 438 | } |
| 439 | 439 | ||
| 440 | template <class P> | 440 | template <class P> |
| 441 | void TextureCache<P>::CachedWriteMemory(VAddr cpu_addr, size_t size) { | ||
| 442 | const VAddr new_cpu_addr = Common::AlignDown(cpu_addr, CPU_PAGE_SIZE); | ||
| 443 | const size_t new_size = Common::AlignUp(size + cpu_addr - new_cpu_addr, CPU_PAGE_SIZE); | ||
| 444 | ForEachImageInRegion(new_cpu_addr, new_size, [this](ImageId image_id, Image& image) { | ||
| 445 | if (True(image.flags & ImageFlagBits::CachedCpuModified)) { | ||
| 446 | return; | ||
| 447 | } | ||
| 448 | image.flags |= ImageFlagBits::CachedCpuModified; | ||
| 449 | cached_cpu_invalidate.insert(image_id); | ||
| 450 | |||
| 451 | if (True(image.flags & ImageFlagBits::Tracked)) { | ||
| 452 | UntrackImage(image, image_id); | ||
| 453 | } | ||
| 454 | }); | ||
| 455 | } | ||
| 456 | |||
| 457 | template <class P> | ||
| 458 | void TextureCache<P>::DownloadMemory(VAddr cpu_addr, size_t size) { | 441 | void TextureCache<P>::DownloadMemory(VAddr cpu_addr, size_t size) { |
| 459 | std::vector<ImageId> images; | 442 | std::vector<ImageId> images; |
| 460 | ForEachImageInRegion(cpu_addr, size, [this, &images](ImageId image_id, ImageBase& image) { | 443 | ForEachImageInRegion(cpu_addr, size, [this, &images](ImageId image_id, ImageBase& image) { |
| @@ -512,18 +495,6 @@ void TextureCache<P>::UnmapGPUMemory(GPUVAddr gpu_addr, size_t size) { | |||
| 512 | } | 495 | } |
| 513 | 496 | ||
| 514 | template <class P> | 497 | template <class P> |
| 515 | void TextureCache<P>::FlushCachedWrites() { | ||
| 516 | for (ImageId image_id : cached_cpu_invalidate) { | ||
| 517 | Image& image = slot_images[image_id]; | ||
| 518 | if (True(image.flags & ImageFlagBits::CachedCpuModified)) { | ||
| 519 | image.flags &= ~ImageFlagBits::CachedCpuModified; | ||
| 520 | image.flags |= ImageFlagBits::CpuModified; | ||
| 521 | } | ||
| 522 | } | ||
| 523 | cached_cpu_invalidate.clear(); | ||
| 524 | } | ||
| 525 | |||
| 526 | template <class P> | ||
| 527 | void TextureCache<P>::BlitImage(const Tegra::Engines::Fermi2D::Surface& dst, | 498 | void TextureCache<P>::BlitImage(const Tegra::Engines::Fermi2D::Surface& dst, |
| 528 | const Tegra::Engines::Fermi2D::Surface& src, | 499 | const Tegra::Engines::Fermi2D::Surface& src, |
| 529 | const Tegra::Engines::Fermi2D::Config& copy) { | 500 | const Tegra::Engines::Fermi2D::Config& copy) { |
| @@ -1109,8 +1080,6 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA | |||
| 1109 | Image& overlap = slot_images[overlap_id]; | 1080 | Image& overlap = slot_images[overlap_id]; |
| 1110 | if (True(overlap.flags & ImageFlagBits::GpuModified)) { | 1081 | if (True(overlap.flags & ImageFlagBits::GpuModified)) { |
| 1111 | new_image.flags |= ImageFlagBits::GpuModified; | 1082 | new_image.flags |= ImageFlagBits::GpuModified; |
| 1112 | new_image.modification_tick = | ||
| 1113 | std::max(overlap.modification_tick, new_image.modification_tick); | ||
| 1114 | } | 1083 | } |
| 1115 | if (overlap.info.num_samples != new_image.info.num_samples) { | 1084 | if (overlap.info.num_samples != new_image.info.num_samples) { |
| 1116 | LOG_WARNING(HW_GPU, "Copying between images with different samples is not implemented"); | 1085 | LOG_WARNING(HW_GPU, "Copying between images with different samples is not implemented"); |
| @@ -1589,9 +1558,6 @@ void TextureCache<P>::UnregisterImage(ImageId image_id) { | |||
| 1589 | template <class P> | 1558 | template <class P> |
| 1590 | void TextureCache<P>::TrackImage(ImageBase& image, ImageId image_id) { | 1559 | void TextureCache<P>::TrackImage(ImageBase& image, ImageId image_id) { |
| 1591 | ASSERT(False(image.flags & ImageFlagBits::Tracked)); | 1560 | ASSERT(False(image.flags & ImageFlagBits::Tracked)); |
| 1592 | if (True(image.flags & ImageFlagBits::CachedCpuModified)) { | ||
| 1593 | return; | ||
| 1594 | } | ||
| 1595 | image.flags |= ImageFlagBits::Tracked; | 1561 | image.flags |= ImageFlagBits::Tracked; |
| 1596 | if (False(image.flags & ImageFlagBits::Sparse)) { | 1562 | if (False(image.flags & ImageFlagBits::Sparse)) { |
| 1597 | rasterizer.UpdatePagesCachedCount(image.cpu_addr, image.guest_size_bytes, 1); | 1563 | rasterizer.UpdatePagesCachedCount(image.cpu_addr, image.guest_size_bytes, 1); |
| @@ -1648,9 +1614,6 @@ void TextureCache<P>::DeleteImage(ImageId image_id, bool immediate_delete) { | |||
| 1648 | tentative_size = EstimatedDecompressedSize(tentative_size, image.info.format); | 1614 | tentative_size = EstimatedDecompressedSize(tentative_size, image.info.format); |
| 1649 | } | 1615 | } |
| 1650 | total_used_memory -= Common::AlignUp(tentative_size, 1024); | 1616 | total_used_memory -= Common::AlignUp(tentative_size, 1024); |
| 1651 | if (True(image.flags & ImageFlagBits::CachedCpuModified)) { | ||
| 1652 | cached_cpu_invalidate.erase(image_id); | ||
| 1653 | } | ||
| 1654 | const GPUVAddr gpu_addr = image.gpu_addr; | 1617 | const GPUVAddr gpu_addr = image.gpu_addr; |
| 1655 | const auto alloc_it = image_allocs_table.find(gpu_addr); | 1618 | const auto alloc_it = image_allocs_table.find(gpu_addr); |
| 1656 | if (alloc_it == image_allocs_table.end()) { | 1619 | if (alloc_it == image_allocs_table.end()) { |
| @@ -1817,11 +1780,7 @@ template <class P> | |||
| 1817 | void TextureCache<P>::PrepareImage(ImageId image_id, bool is_modification, bool invalidate) { | 1780 | void TextureCache<P>::PrepareImage(ImageId image_id, bool is_modification, bool invalidate) { |
| 1818 | Image& image = slot_images[image_id]; | 1781 | Image& image = slot_images[image_id]; |
| 1819 | if (invalidate) { | 1782 | if (invalidate) { |
| 1820 | if (True(image.flags & ImageFlagBits::CachedCpuModified)) { | 1783 | image.flags &= ~(ImageFlagBits::CpuModified | ImageFlagBits::GpuModified); |
| 1821 | cached_cpu_invalidate.erase(image_id); | ||
| 1822 | } | ||
| 1823 | image.flags &= ~(ImageFlagBits::CpuModified | ImageFlagBits::GpuModified | | ||
| 1824 | ImageFlagBits::CachedCpuModified); | ||
| 1825 | if (False(image.flags & ImageFlagBits::Tracked)) { | 1784 | if (False(image.flags & ImageFlagBits::Tracked)) { |
| 1826 | TrackImage(image, image_id); | 1785 | TrackImage(image, image_id); |
| 1827 | } | 1786 | } |
diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h index ad5978a33..b1324edf3 100644 --- a/src/video_core/texture_cache/texture_cache_base.h +++ b/src/video_core/texture_cache/texture_cache_base.h | |||
| @@ -8,7 +8,6 @@ | |||
| 8 | #include <span> | 8 | #include <span> |
| 9 | #include <type_traits> | 9 | #include <type_traits> |
| 10 | #include <unordered_map> | 10 | #include <unordered_map> |
| 11 | #include <unordered_set> | ||
| 12 | #include <vector> | 11 | #include <vector> |
| 13 | #include <queue> | 12 | #include <queue> |
| 14 | 13 | ||
| @@ -51,9 +50,6 @@ class TextureCache { | |||
| 51 | /// Address shift for caching images into a hash table | 50 | /// Address shift for caching images into a hash table |
| 52 | static constexpr u64 PAGE_BITS = 20; | 51 | static constexpr u64 PAGE_BITS = 20; |
| 53 | 52 | ||
| 54 | static constexpr u64 CPU_PAGE_BITS = 12; | ||
| 55 | static constexpr u64 CPU_PAGE_SIZE = 1ULL << CPU_PAGE_BITS; | ||
| 56 | |||
| 57 | /// Enables debugging features to the texture cache | 53 | /// Enables debugging features to the texture cache |
| 58 | static constexpr bool ENABLE_VALIDATION = P::ENABLE_VALIDATION; | 54 | static constexpr bool ENABLE_VALIDATION = P::ENABLE_VALIDATION; |
| 59 | /// Implement blits as copies between framebuffers | 55 | /// Implement blits as copies between framebuffers |
| @@ -140,9 +136,6 @@ public: | |||
| 140 | /// Mark images in a range as modified from the CPU | 136 | /// Mark images in a range as modified from the CPU |
| 141 | void WriteMemory(VAddr cpu_addr, size_t size); | 137 | void WriteMemory(VAddr cpu_addr, size_t size); |
| 142 | 138 | ||
| 143 | /// Mark images in a range as modified from the CPU | ||
| 144 | void CachedWriteMemory(VAddr cpu_addr, size_t size); | ||
| 145 | |||
| 146 | /// Download contents of host images to guest memory in a region | 139 | /// Download contents of host images to guest memory in a region |
| 147 | void DownloadMemory(VAddr cpu_addr, size_t size); | 140 | void DownloadMemory(VAddr cpu_addr, size_t size); |
| 148 | 141 | ||
| @@ -152,8 +145,6 @@ public: | |||
| 152 | /// Remove images in a region | 145 | /// Remove images in a region |
| 153 | void UnmapGPUMemory(GPUVAddr gpu_addr, size_t size); | 146 | void UnmapGPUMemory(GPUVAddr gpu_addr, size_t size); |
| 154 | 147 | ||
| 155 | void FlushCachedWrites(); | ||
| 156 | |||
| 157 | /// Blit an image with the given parameters | 148 | /// Blit an image with the given parameters |
| 158 | void BlitImage(const Tegra::Engines::Fermi2D::Surface& dst, | 149 | void BlitImage(const Tegra::Engines::Fermi2D::Surface& dst, |
| 159 | const Tegra::Engines::Fermi2D::Surface& src, | 150 | const Tegra::Engines::Fermi2D::Surface& src, |
| @@ -375,8 +366,6 @@ private: | |||
| 375 | 366 | ||
| 376 | std::unordered_map<ImageId, std::vector<ImageViewId>> sparse_views; | 367 | std::unordered_map<ImageId, std::vector<ImageViewId>> sparse_views; |
| 377 | 368 | ||
| 378 | std::unordered_set<ImageId> cached_cpu_invalidate; | ||
| 379 | |||
| 380 | VAddr virtual_invalid_space{}; | 369 | VAddr virtual_invalid_space{}; |
| 381 | 370 | ||
| 382 | bool has_deleted_images = false; | 371 | bool has_deleted_images = false; |
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index e142bee35..f3a05ada9 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp | |||
| @@ -621,6 +621,11 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR | |||
| 621 | khr_push_descriptor = false; | 621 | khr_push_descriptor = false; |
| 622 | break; | 622 | break; |
| 623 | } | 623 | } |
| 624 | const u32 nv_major_version = (properties.driverVersion >> 22) & 0x3ff; | ||
| 625 | if (nv_major_version >= 510) { | ||
| 626 | LOG_WARNING(Render_Vulkan, "NVIDIA Drivers >= 510 do not support MSAA image blits"); | ||
| 627 | cant_blit_msaa = true; | ||
| 628 | } | ||
| 624 | } | 629 | } |
| 625 | const bool is_radv = driver_id == VK_DRIVER_ID_MESA_RADV; | 630 | const bool is_radv = driver_id == VK_DRIVER_ID_MESA_RADV; |
| 626 | if (ext_extended_dynamic_state && is_radv) { | 631 | if (ext_extended_dynamic_state && is_radv) { |
| @@ -731,7 +736,7 @@ VkFormat Device::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFlags | |||
| 731 | } | 736 | } |
| 732 | 737 | ||
| 733 | void Device::ReportLoss() const { | 738 | void Device::ReportLoss() const { |
| 734 | LOG_CRITICAL(Render_Vulkan, "Device loss occured!"); | 739 | LOG_CRITICAL(Render_Vulkan, "Device loss occurred!"); |
| 735 | 740 | ||
| 736 | // Wait for the log to flush and for Nsight Aftermath to dump the results | 741 | // Wait for the log to flush and for Nsight Aftermath to dump the results |
| 737 | std::this_thread::sleep_for(std::chrono::seconds{15}); | 742 | std::this_thread::sleep_for(std::chrono::seconds{15}); |
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 30902101d..b1467d016 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt | |||
| @@ -293,7 +293,7 @@ if (YUZU_USE_QT_WEB_ENGINE) | |||
| 293 | endif () | 293 | endif () |
| 294 | 294 | ||
| 295 | if(UNIX AND NOT APPLE) | 295 | if(UNIX AND NOT APPLE) |
| 296 | install(TARGETS yuzu RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") | 296 | install(TARGETS yuzu) |
| 297 | endif() | 297 | endif() |
| 298 | 298 | ||
| 299 | if (YUZU_USE_BUNDLED_QT) | 299 | if (YUZU_USE_BUNDLED_QT) |
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui index edb525e82..c1d90d588 100644 --- a/src/yuzu/configuration/configure_debug.ui +++ b/src/yuzu/configuration/configure_debug.ui | |||
| @@ -214,14 +214,14 @@ | |||
| 214 | <item row="1" column="1"> | 214 | <item row="1" column="1"> |
| 215 | <widget class="QCheckBox" name="enable_all_controllers"> | 215 | <widget class="QCheckBox" name="enable_all_controllers"> |
| 216 | <property name="text"> | 216 | <property name="text"> |
| 217 | <string>Enable all Controller Types</string> | 217 | <string>Enable All Controller Types</string> |
| 218 | </property> | 218 | </property> |
| 219 | </widget> | 219 | </widget> |
| 220 | </item> | 220 | </item> |
| 221 | <item row="2" column="1"> | 221 | <item row="2" column="1"> |
| 222 | <widget class="QCheckBox" name="disable_web_applet"> | 222 | <widget class="QCheckBox" name="disable_web_applet"> |
| 223 | <property name="text"> | 223 | <property name="text"> |
| 224 | <string>Disable Web Applet**</string> | 224 | <string>Disable Web Applet</string> |
| 225 | </property> | 225 | </property> |
| 226 | </widget> | 226 | </widget> |
| 227 | </item> | 227 | </item> |
diff --git a/src/yuzu/configuration/configure_hotkeys.cpp b/src/yuzu/configuration/configure_hotkeys.cpp index 53e629a5e..6679e9c53 100644 --- a/src/yuzu/configuration/configure_hotkeys.cpp +++ b/src/yuzu/configuration/configure_hotkeys.cpp | |||
| @@ -35,8 +35,9 @@ ConfigureHotkeys::ConfigureHotkeys(Core::HID::HIDCore& hid_core, QWidget* parent | |||
| 35 | ui->hotkey_list->setContextMenuPolicy(Qt::CustomContextMenu); | 35 | ui->hotkey_list->setContextMenuPolicy(Qt::CustomContextMenu); |
| 36 | ui->hotkey_list->setModel(model); | 36 | ui->hotkey_list->setModel(model); |
| 37 | 37 | ||
| 38 | ui->hotkey_list->setColumnWidth(name_column, 200); | 38 | ui->hotkey_list->header()->setStretchLastSection(false); |
| 39 | ui->hotkey_list->resizeColumnToContents(hotkey_column); | 39 | ui->hotkey_list->header()->setSectionResizeMode(name_column, QHeaderView::ResizeMode::Stretch); |
| 40 | ui->hotkey_list->header()->setMinimumSectionSize(150); | ||
| 40 | 41 | ||
| 41 | connect(ui->button_restore_defaults, &QPushButton::clicked, this, | 42 | connect(ui->button_restore_defaults, &QPushButton::clicked, this, |
| 42 | &ConfigureHotkeys::RestoreDefaults); | 43 | &ConfigureHotkeys::RestoreDefaults); |
| @@ -76,8 +77,8 @@ void ConfigureHotkeys::Populate(const HotkeyRegistry& registry) { | |||
| 76 | } | 77 | } |
| 77 | 78 | ||
| 78 | ui->hotkey_list->expandAll(); | 79 | ui->hotkey_list->expandAll(); |
| 79 | ui->hotkey_list->resizeColumnToContents(name_column); | ||
| 80 | ui->hotkey_list->resizeColumnToContents(hotkey_column); | 80 | ui->hotkey_list->resizeColumnToContents(hotkey_column); |
| 81 | ui->hotkey_list->resizeColumnToContents(controller_column); | ||
| 81 | } | 82 | } |
| 82 | 83 | ||
| 83 | void ConfigureHotkeys::changeEvent(QEvent* event) { | 84 | void ConfigureHotkeys::changeEvent(QEvent* event) { |
diff --git a/src/yuzu/configuration/configure_per_game_addons.cpp b/src/yuzu/configuration/configure_per_game_addons.cpp index 21e51d749..7893a85bb 100644 --- a/src/yuzu/configuration/configure_per_game_addons.cpp +++ b/src/yuzu/configuration/configure_per_game_addons.cpp | |||
| @@ -47,6 +47,10 @@ ConfigurePerGameAddons::ConfigurePerGameAddons(Core::System& system_, QWidget* p | |||
| 47 | item_model->setHeaderData(0, Qt::Horizontal, tr("Patch Name")); | 47 | item_model->setHeaderData(0, Qt::Horizontal, tr("Patch Name")); |
| 48 | item_model->setHeaderData(1, Qt::Horizontal, tr("Version")); | 48 | item_model->setHeaderData(1, Qt::Horizontal, tr("Version")); |
| 49 | 49 | ||
| 50 | tree_view->header()->setStretchLastSection(false); | ||
| 51 | tree_view->header()->setSectionResizeMode(0, QHeaderView::ResizeMode::Stretch); | ||
| 52 | tree_view->header()->setMinimumSectionSize(150); | ||
| 53 | |||
| 50 | // We must register all custom types with the Qt Automoc system so that we are able to use it | 54 | // We must register all custom types with the Qt Automoc system so that we are able to use it |
| 51 | // with signals/slots. In this case, QList falls under the umbrella of custom types. | 55 | // with signals/slots. In this case, QList falls under the umbrella of custom types. |
| 52 | qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>"); | 56 | qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>"); |
| @@ -138,5 +142,5 @@ void ConfigurePerGameAddons::LoadConfiguration() { | |||
| 138 | item_model->appendRow(list_items.back()); | 142 | item_model->appendRow(list_items.back()); |
| 139 | } | 143 | } |
| 140 | 144 | ||
| 141 | tree_view->setColumnWidth(0, 5 * tree_view->width() / 16); | 145 | tree_view->resizeColumnToContents(1); |
| 142 | } | 146 | } |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 3b7058a2b..62d15f8cd 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -293,8 +293,6 @@ GMainWindow::GMainWindow() | |||
| 293 | 293 | ||
| 294 | MigrateConfigFiles(); | 294 | MigrateConfigFiles(); |
| 295 | 295 | ||
| 296 | ui->action_Fullscreen->setChecked(false); | ||
| 297 | |||
| 298 | #if defined(HAVE_SDL2) && !defined(_WIN32) | 296 | #if defined(HAVE_SDL2) && !defined(_WIN32) |
| 299 | SDL_InitSubSystem(SDL_INIT_VIDEO); | 297 | SDL_InitSubSystem(SDL_INIT_VIDEO); |
| 300 | // SDL disables the screen saver by default, and setting the hint | 298 | // SDL disables the screen saver by default, and setting the hint |
| @@ -312,17 +310,20 @@ GMainWindow::GMainWindow() | |||
| 312 | } | 310 | } |
| 313 | 311 | ||
| 314 | QString game_path; | 312 | QString game_path; |
| 313 | bool has_gamepath = false; | ||
| 314 | bool is_fullscreen = false; | ||
| 315 | 315 | ||
| 316 | for (int i = 1; i < args.size(); ++i) { | 316 | for (int i = 1; i < args.size(); ++i) { |
| 317 | // Preserves drag/drop functionality | 317 | // Preserves drag/drop functionality |
| 318 | if (args.size() == 2 && !args[1].startsWith(QChar::fromLatin1('-'))) { | 318 | if (args.size() == 2 && !args[1].startsWith(QChar::fromLatin1('-'))) { |
| 319 | game_path = args[1]; | 319 | game_path = args[1]; |
| 320 | has_gamepath = true; | ||
| 320 | break; | 321 | break; |
| 321 | } | 322 | } |
| 322 | 323 | ||
| 323 | // Launch game in fullscreen mode | 324 | // Launch game in fullscreen mode |
| 324 | if (args[i] == QStringLiteral("-f")) { | 325 | if (args[i] == QStringLiteral("-f")) { |
| 325 | ui->action_Fullscreen->setChecked(true); | 326 | is_fullscreen = true; |
| 326 | continue; | 327 | continue; |
| 327 | } | 328 | } |
| 328 | 329 | ||
| @@ -365,9 +366,15 @@ GMainWindow::GMainWindow() | |||
| 365 | } | 366 | } |
| 366 | 367 | ||
| 367 | game_path = args[++i]; | 368 | game_path = args[++i]; |
| 369 | has_gamepath = true; | ||
| 368 | } | 370 | } |
| 369 | } | 371 | } |
| 370 | 372 | ||
| 373 | // Override fullscreen setting if gamepath or argument is provided | ||
| 374 | if (has_gamepath || is_fullscreen) { | ||
| 375 | ui->action_Fullscreen->setChecked(is_fullscreen); | ||
| 376 | } | ||
| 377 | |||
| 371 | if (!game_path.isEmpty()) { | 378 | if (!game_path.isEmpty()) { |
| 372 | BootGame(game_path); | 379 | BootGame(game_path); |
| 373 | } | 380 | } |
diff --git a/src/yuzu_cmd/CMakeLists.txt b/src/yuzu_cmd/CMakeLists.txt index 74fc24972..c8901f2df 100644 --- a/src/yuzu_cmd/CMakeLists.txt +++ b/src/yuzu_cmd/CMakeLists.txt | |||
| @@ -45,7 +45,7 @@ if (YUZU_USE_EXTERNAL_SDL2) | |||
| 45 | endif() | 45 | endif() |
| 46 | 46 | ||
| 47 | if(UNIX AND NOT APPLE) | 47 | if(UNIX AND NOT APPLE) |
| 48 | install(TARGETS yuzu-cmd RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") | 48 | install(TARGETS yuzu-cmd) |
| 49 | endif() | 49 | endif() |
| 50 | 50 | ||
| 51 | if (MSVC) | 51 | if (MSVC) |
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index 34782c378..f34d6b728 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h | |||
| @@ -342,12 +342,6 @@ fps_cap = | |||
| 342 | # null: No audio output | 342 | # null: No audio output |
| 343 | output_engine = | 343 | output_engine = |
| 344 | 344 | ||
| 345 | # Whether or not to enable the audio-stretching post-processing effect. | ||
| 346 | # This effect adjusts audio speed to match emulation speed and helps prevent audio stutter, | ||
| 347 | # at the cost of increasing audio latency. | ||
| 348 | # 0: No, 1 (default): Yes | ||
| 349 | enable_audio_stretching = | ||
| 350 | |||
| 351 | # Which audio device to use. | 345 | # Which audio device to use. |
| 352 | # auto (default): Auto-select | 346 | # auto (default): Auto-select |
| 353 | output_device = | 347 | output_device = |
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index 14bf82f39..ab12dd15d 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp | |||
| @@ -74,6 +74,7 @@ static void PrintVersion() { | |||
| 74 | int main(int argc, char** argv) { | 74 | int main(int argc, char** argv) { |
| 75 | Common::Log::Initialize(); | 75 | Common::Log::Initialize(); |
| 76 | Common::Log::SetColorConsoleBackendEnabled(true); | 76 | Common::Log::SetColorConsoleBackendEnabled(true); |
| 77 | Common::Log::Start(); | ||
| 77 | Common::DetachedTasks detached_tasks; | 78 | Common::DetachedTasks detached_tasks; |
| 78 | 79 | ||
| 79 | int option_index = 0; | 80 | int option_index = 0; |