diff options
Diffstat (limited to 'src')
21 files changed, 284 insertions, 108 deletions
diff --git a/src/audio_core/sink/cubeb_sink.cpp b/src/audio_core/sink/cubeb_sink.cpp index 36b115ad6..32c1b1cb3 100644 --- a/src/audio_core/sink/cubeb_sink.cpp +++ b/src/audio_core/sink/cubeb_sink.cpp | |||
| @@ -66,10 +66,10 @@ public: | |||
| 66 | const auto latency_error = cubeb_get_min_latency(ctx, ¶ms, &minimum_latency); | 66 | const auto latency_error = cubeb_get_min_latency(ctx, ¶ms, &minimum_latency); |
| 67 | if (latency_error != CUBEB_OK) { | 67 | if (latency_error != CUBEB_OK) { |
| 68 | LOG_CRITICAL(Audio_Sink, "Error getting minimum latency, error: {}", latency_error); | 68 | LOG_CRITICAL(Audio_Sink, "Error getting minimum latency, error: {}", latency_error); |
| 69 | minimum_latency = 256U; | 69 | minimum_latency = TargetSampleCount * 2; |
| 70 | } | 70 | } |
| 71 | 71 | ||
| 72 | minimum_latency = std::max(minimum_latency, 256u); | 72 | minimum_latency = std::max(minimum_latency, TargetSampleCount * 2); |
| 73 | 73 | ||
| 74 | LOG_INFO(Service_Audio, | 74 | LOG_INFO(Service_Audio, |
| 75 | "Opening cubeb stream {} type {} with: rate {} channels {} (system channels {}) " | 75 | "Opening cubeb stream {} type {} with: rate {} channels {} (system channels {}) " |
| @@ -326,4 +326,31 @@ std::vector<std::string> ListCubebSinkDevices(bool capture) { | |||
| 326 | return device_list; | 326 | return device_list; |
| 327 | } | 327 | } |
| 328 | 328 | ||
| 329 | u32 GetCubebLatency() { | ||
| 330 | cubeb* ctx; | ||
| 331 | |||
| 332 | if (cubeb_init(&ctx, "yuzu Latency Getter", nullptr) != CUBEB_OK) { | ||
| 333 | LOG_CRITICAL(Audio_Sink, "cubeb_init failed"); | ||
| 334 | // Return a large latency so we choose SDL instead. | ||
| 335 | return 10000u; | ||
| 336 | } | ||
| 337 | |||
| 338 | cubeb_stream_params params{}; | ||
| 339 | params.rate = TargetSampleRate; | ||
| 340 | params.channels = 2; | ||
| 341 | params.format = CUBEB_SAMPLE_S16LE; | ||
| 342 | params.prefs = CUBEB_STREAM_PREF_NONE; | ||
| 343 | params.layout = CUBEB_LAYOUT_STEREO; | ||
| 344 | |||
| 345 | u32 latency{0}; | ||
| 346 | const auto latency_error = cubeb_get_min_latency(ctx, ¶ms, &latency); | ||
| 347 | if (latency_error != CUBEB_OK) { | ||
| 348 | LOG_CRITICAL(Audio_Sink, "Error getting minimum latency, error: {}", latency_error); | ||
| 349 | latency = TargetSampleCount * 2; | ||
| 350 | } | ||
| 351 | latency = std::max(latency, TargetSampleCount * 2); | ||
| 352 | cubeb_destroy(ctx); | ||
| 353 | return latency; | ||
| 354 | } | ||
| 355 | |||
| 329 | } // namespace AudioCore::Sink | 356 | } // namespace AudioCore::Sink |
diff --git a/src/audio_core/sink/cubeb_sink.h b/src/audio_core/sink/cubeb_sink.h index 4b0cb160d..3302cb98d 100644 --- a/src/audio_core/sink/cubeb_sink.h +++ b/src/audio_core/sink/cubeb_sink.h | |||
| @@ -96,4 +96,11 @@ private: | |||
| 96 | */ | 96 | */ |
| 97 | std::vector<std::string> ListCubebSinkDevices(bool capture); | 97 | std::vector<std::string> ListCubebSinkDevices(bool capture); |
| 98 | 98 | ||
| 99 | /** | ||
| 100 | * Get the reported latency for this sink. | ||
| 101 | * | ||
| 102 | * @return Minimum latency for this sink. | ||
| 103 | */ | ||
| 104 | u32 GetCubebLatency(); | ||
| 105 | |||
| 99 | } // namespace AudioCore::Sink | 106 | } // namespace AudioCore::Sink |
diff --git a/src/audio_core/sink/sdl2_sink.cpp b/src/audio_core/sink/sdl2_sink.cpp index 1bd001b94..f12ebf7fe 100644 --- a/src/audio_core/sink/sdl2_sink.cpp +++ b/src/audio_core/sink/sdl2_sink.cpp | |||
| @@ -47,11 +47,7 @@ public: | |||
| 47 | spec.freq = TargetSampleRate; | 47 | spec.freq = TargetSampleRate; |
| 48 | spec.channels = static_cast<u8>(device_channels); | 48 | spec.channels = static_cast<u8>(device_channels); |
| 49 | spec.format = AUDIO_S16SYS; | 49 | spec.format = AUDIO_S16SYS; |
| 50 | if (type == StreamType::Render) { | 50 | spec.samples = TargetSampleCount * 2; |
| 51 | spec.samples = TargetSampleCount; | ||
| 52 | } else { | ||
| 53 | spec.samples = 1024; | ||
| 54 | } | ||
| 55 | spec.callback = &SDLSinkStream::DataCallback; | 51 | spec.callback = &SDLSinkStream::DataCallback; |
| 56 | spec.userdata = this; | 52 | spec.userdata = this; |
| 57 | 53 | ||
| @@ -240,4 +236,8 @@ std::vector<std::string> ListSDLSinkDevices(bool capture) { | |||
| 240 | return device_list; | 236 | return device_list; |
| 241 | } | 237 | } |
| 242 | 238 | ||
| 239 | u32 GetSDLLatency() { | ||
| 240 | return TargetSampleCount * 2; | ||
| 241 | } | ||
| 242 | |||
| 243 | } // namespace AudioCore::Sink | 243 | } // namespace AudioCore::Sink |
diff --git a/src/audio_core/sink/sdl2_sink.h b/src/audio_core/sink/sdl2_sink.h index f01eddc1b..27ed1ab94 100644 --- a/src/audio_core/sink/sdl2_sink.h +++ b/src/audio_core/sink/sdl2_sink.h | |||
| @@ -87,4 +87,11 @@ private: | |||
| 87 | */ | 87 | */ |
| 88 | std::vector<std::string> ListSDLSinkDevices(bool capture); | 88 | std::vector<std::string> ListSDLSinkDevices(bool capture); |
| 89 | 89 | ||
| 90 | /** | ||
| 91 | * Get the reported latency for this sink. | ||
| 92 | * | ||
| 93 | * @return Minimum latency for this sink. | ||
| 94 | */ | ||
| 95 | u32 GetSDLLatency(); | ||
| 96 | |||
| 90 | } // namespace AudioCore::Sink | 97 | } // namespace AudioCore::Sink |
diff --git a/src/audio_core/sink/sink_details.cpp b/src/audio_core/sink/sink_details.cpp index 67bdab779..39ea6d91b 100644 --- a/src/audio_core/sink/sink_details.cpp +++ b/src/audio_core/sink/sink_details.cpp | |||
| @@ -21,58 +21,80 @@ namespace { | |||
| 21 | struct SinkDetails { | 21 | struct SinkDetails { |
| 22 | using FactoryFn = std::unique_ptr<Sink> (*)(std::string_view); | 22 | using FactoryFn = std::unique_ptr<Sink> (*)(std::string_view); |
| 23 | using ListDevicesFn = std::vector<std::string> (*)(bool); | 23 | using ListDevicesFn = std::vector<std::string> (*)(bool); |
| 24 | using LatencyFn = u32 (*)(); | ||
| 24 | 25 | ||
| 25 | /// Name for this sink. | 26 | /// Name for this sink. |
| 26 | const char* id; | 27 | std::string_view id; |
| 27 | /// A method to call to construct an instance of this type of sink. | 28 | /// A method to call to construct an instance of this type of sink. |
| 28 | FactoryFn factory; | 29 | FactoryFn factory; |
| 29 | /// A method to call to list available devices. | 30 | /// A method to call to list available devices. |
| 30 | ListDevicesFn list_devices; | 31 | ListDevicesFn list_devices; |
| 32 | /// Method to get the latency of this backend. | ||
| 33 | LatencyFn latency; | ||
| 31 | }; | 34 | }; |
| 32 | 35 | ||
| 33 | // sink_details is ordered in terms of desirability, with the best choice at the top. | 36 | // sink_details is ordered in terms of desirability, with the best choice at the top. |
| 34 | constexpr SinkDetails sink_details[] = { | 37 | constexpr SinkDetails sink_details[] = { |
| 35 | #ifdef HAVE_CUBEB | 38 | #ifdef HAVE_CUBEB |
| 36 | SinkDetails{"cubeb", | 39 | SinkDetails{ |
| 37 | [](std::string_view device_id) -> std::unique_ptr<Sink> { | 40 | "cubeb", |
| 38 | return std::make_unique<CubebSink>(device_id); | 41 | [](std::string_view device_id) -> std::unique_ptr<Sink> { |
| 39 | }, | 42 | return std::make_unique<CubebSink>(device_id); |
| 40 | &ListCubebSinkDevices}, | 43 | }, |
| 44 | &ListCubebSinkDevices, | ||
| 45 | &GetCubebLatency, | ||
| 46 | }, | ||
| 41 | #endif | 47 | #endif |
| 42 | #ifdef HAVE_SDL2 | 48 | #ifdef HAVE_SDL2 |
| 43 | SinkDetails{"sdl2", | 49 | SinkDetails{ |
| 44 | [](std::string_view device_id) -> std::unique_ptr<Sink> { | 50 | "sdl2", |
| 45 | return std::make_unique<SDLSink>(device_id); | 51 | [](std::string_view device_id) -> std::unique_ptr<Sink> { |
| 46 | }, | 52 | return std::make_unique<SDLSink>(device_id); |
| 47 | &ListSDLSinkDevices}, | 53 | }, |
| 54 | &ListSDLSinkDevices, | ||
| 55 | &GetSDLLatency, | ||
| 56 | }, | ||
| 48 | #endif | 57 | #endif |
| 49 | SinkDetails{"null", | 58 | SinkDetails{"null", |
| 50 | [](std::string_view device_id) -> std::unique_ptr<Sink> { | 59 | [](std::string_view device_id) -> std::unique_ptr<Sink> { |
| 51 | return std::make_unique<NullSink>(device_id); | 60 | return std::make_unique<NullSink>(device_id); |
| 52 | }, | 61 | }, |
| 53 | [](bool capture) { return std::vector<std::string>{"null"}; }}, | 62 | [](bool capture) { return std::vector<std::string>{"null"}; }, []() { return 0u; }}, |
| 54 | }; | 63 | }; |
| 55 | 64 | ||
| 56 | const SinkDetails& GetOutputSinkDetails(std::string_view sink_id) { | 65 | const SinkDetails& GetOutputSinkDetails(std::string_view sink_id) { |
| 57 | auto iter = | 66 | const auto find_backend{[](std::string_view id) { |
| 58 | std::find_if(std::begin(sink_details), std::end(sink_details), | 67 | return std::find_if(std::begin(sink_details), std::end(sink_details), |
| 59 | [sink_id](const auto& sink_detail) { return sink_detail.id == sink_id; }); | 68 | [&id](const auto& sink_detail) { return sink_detail.id == id; }); |
| 69 | }}; | ||
| 60 | 70 | ||
| 61 | if (sink_id == "auto" || iter == std::end(sink_details)) { | 71 | auto iter = find_backend(sink_id); |
| 62 | if (sink_id != "auto") { | 72 | |
| 63 | LOG_ERROR(Audio, "Invalid sink_id {}", sink_id); | 73 | if (sink_id == "auto") { |
| 74 | // Auto-select a backend. Prefer CubeB, but it may report a large minimum latency which | ||
| 75 | // causes audio issues, in that case go with SDL. | ||
| 76 | #if defined(HAVE_CUBEB) && defined(HAVE_SDL2) | ||
| 77 | iter = find_backend("cubeb"); | ||
| 78 | if (iter->latency() > TargetSampleCount * 3) { | ||
| 79 | iter = find_backend("sdl2"); | ||
| 64 | } | 80 | } |
| 65 | // Auto-select. | 81 | #else |
| 66 | // sink_details is ordered in terms of desirability, with the best choice at the front. | ||
| 67 | iter = std::begin(sink_details); | 82 | iter = std::begin(sink_details); |
| 83 | #endif | ||
| 84 | LOG_INFO(Service_Audio, "Auto-selecting the {} backend", iter->id); | ||
| 85 | } | ||
| 86 | |||
| 87 | if (iter == std::end(sink_details)) { | ||
| 88 | LOG_ERROR(Audio, "Invalid sink_id {}", sink_id); | ||
| 89 | iter = find_backend("null"); | ||
| 68 | } | 90 | } |
| 69 | 91 | ||
| 70 | return *iter; | 92 | return *iter; |
| 71 | } | 93 | } |
| 72 | } // Anonymous namespace | 94 | } // Anonymous namespace |
| 73 | 95 | ||
| 74 | std::vector<const char*> GetSinkIDs() { | 96 | std::vector<std::string_view> GetSinkIDs() { |
| 75 | std::vector<const char*> sink_ids(std::size(sink_details)); | 97 | std::vector<std::string_view> sink_ids(std::size(sink_details)); |
| 76 | 98 | ||
| 77 | std::transform(std::begin(sink_details), std::end(sink_details), std::begin(sink_ids), | 99 | std::transform(std::begin(sink_details), std::end(sink_details), std::begin(sink_ids), |
| 78 | [](const auto& sink) { return sink.id; }); | 100 | [](const auto& sink) { return sink.id; }); |
diff --git a/src/audio_core/sink/sink_details.h b/src/audio_core/sink/sink_details.h index 3ebdb1e30..e75932898 100644 --- a/src/audio_core/sink/sink_details.h +++ b/src/audio_core/sink/sink_details.h | |||
| @@ -19,7 +19,7 @@ class Sink; | |||
| 19 | * | 19 | * |
| 20 | * @return Vector of available sink names. | 20 | * @return Vector of available sink names. |
| 21 | */ | 21 | */ |
| 22 | std::vector<const char*> GetSinkIDs(); | 22 | std::vector<std::string_view> GetSinkIDs(); |
| 23 | 23 | ||
| 24 | /** | 24 | /** |
| 25 | * Gets the list of devices for a particular sink identified by the given ID. | 25 | * Gets the list of devices for a particular sink identified by the given ID. |
diff --git a/src/common/settings.h b/src/common/settings.h index d2452c93b..0eb98939c 100644 --- a/src/common/settings.h +++ b/src/common/settings.h | |||
| @@ -431,7 +431,7 @@ struct Values { | |||
| 431 | FullscreenMode::Exclusive, | 431 | FullscreenMode::Exclusive, |
| 432 | #endif | 432 | #endif |
| 433 | FullscreenMode::Borderless, FullscreenMode::Exclusive, "fullscreen_mode"}; | 433 | FullscreenMode::Borderless, FullscreenMode::Exclusive, "fullscreen_mode"}; |
| 434 | SwitchableSetting<int, true> aspect_ratio{0, 0, 3, "aspect_ratio"}; | 434 | SwitchableSetting<int, true> aspect_ratio{0, 0, 4, "aspect_ratio"}; |
| 435 | SwitchableSetting<int, true> max_anisotropy{0, 0, 5, "max_anisotropy"}; | 435 | SwitchableSetting<int, true> max_anisotropy{0, 0, 5, "max_anisotropy"}; |
| 436 | SwitchableSetting<bool> use_speed_limit{true, "use_speed_limit"}; | 436 | SwitchableSetting<bool> use_speed_limit{true, "use_speed_limit"}; |
| 437 | SwitchableSetting<u16, true> speed_limit{100, 0, 9999, "speed_limit"}; | 437 | SwitchableSetting<u16, true> speed_limit{100, 0, 9999, "speed_limit"}; |
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index 6c0fcb7b5..2678ce532 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp | |||
| @@ -270,6 +270,7 @@ void CoreTiming::ThreadLoop() { | |||
| 270 | // There are more events left in the queue, wait until the next event. | 270 | // There are more events left in the queue, wait until the next event. |
| 271 | const auto wait_time = *next_time - GetGlobalTimeNs().count(); | 271 | const auto wait_time = *next_time - GetGlobalTimeNs().count(); |
| 272 | if (wait_time > 0) { | 272 | if (wait_time > 0) { |
| 273 | #ifdef _WIN32 | ||
| 273 | // Assume a timer resolution of 1ms. | 274 | // Assume a timer resolution of 1ms. |
| 274 | static constexpr s64 TimerResolutionNS = 1000000; | 275 | static constexpr s64 TimerResolutionNS = 1000000; |
| 275 | 276 | ||
| @@ -287,6 +288,9 @@ void CoreTiming::ThreadLoop() { | |||
| 287 | if (event.IsSet()) { | 288 | if (event.IsSet()) { |
| 288 | event.Reset(); | 289 | event.Reset(); |
| 289 | } | 290 | } |
| 291 | #else | ||
| 292 | event.WaitFor(std::chrono::nanoseconds(wait_time)); | ||
| 293 | #endif | ||
| 290 | } | 294 | } |
| 291 | } else { | 295 | } else { |
| 292 | // Queue is empty, wait until another event is scheduled and signals us to continue. | 296 | // Queue is empty, wait until another event is scheduled and signals us to continue. |
diff --git a/src/core/frontend/framebuffer_layout.cpp b/src/core/frontend/framebuffer_layout.cpp index 90dd68ff1..b4081fc39 100644 --- a/src/core/frontend/framebuffer_layout.cpp +++ b/src/core/frontend/framebuffer_layout.cpp | |||
| @@ -67,6 +67,8 @@ float EmulationAspectRatio(AspectRatio aspect, float window_aspect_ratio) { | |||
| 67 | return 3.0f / 4.0f; | 67 | return 3.0f / 4.0f; |
| 68 | case AspectRatio::R21_9: | 68 | case AspectRatio::R21_9: |
| 69 | return 9.0f / 21.0f; | 69 | return 9.0f / 21.0f; |
| 70 | case AspectRatio::R16_10: | ||
| 71 | return 10.0f / 16.0f; | ||
| 70 | case AspectRatio::StretchToWindow: | 72 | case AspectRatio::StretchToWindow: |
| 71 | return window_aspect_ratio; | 73 | return window_aspect_ratio; |
| 72 | default: | 74 | default: |
diff --git a/src/core/frontend/framebuffer_layout.h b/src/core/frontend/framebuffer_layout.h index 1561d994e..94683b30f 100644 --- a/src/core/frontend/framebuffer_layout.h +++ b/src/core/frontend/framebuffer_layout.h | |||
| @@ -27,6 +27,7 @@ enum class AspectRatio { | |||
| 27 | Default, | 27 | Default, |
| 28 | R4_3, | 28 | R4_3, |
| 29 | R21_9, | 29 | R21_9, |
| 30 | R16_10, | ||
| 30 | StretchToWindow, | 31 | StretchToWindow, |
| 31 | }; | 32 | }; |
| 32 | 33 | ||
diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 47a1b829b..d67e68bae 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | 5 | ||
| 6 | #include "common/assert.h" | 6 | #include "common/assert.h" |
| 7 | #include "common/bit_field.h" | 7 | #include "common/bit_field.h" |
| 8 | #include "common/common_funcs.h" | ||
| 8 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 9 | #include "common/expected.h" | 10 | #include "common/expected.h" |
| 10 | 11 | ||
| @@ -130,6 +131,10 @@ union Result { | |||
| 130 | [[nodiscard]] constexpr bool IsError() const { | 131 | [[nodiscard]] constexpr bool IsError() const { |
| 131 | return !IsSuccess(); | 132 | return !IsSuccess(); |
| 132 | } | 133 | } |
| 134 | |||
| 135 | [[nodiscard]] constexpr bool IsFailure() const { | ||
| 136 | return !IsSuccess(); | ||
| 137 | } | ||
| 133 | }; | 138 | }; |
| 134 | static_assert(std::is_trivial_v<Result>); | 139 | static_assert(std::is_trivial_v<Result>); |
| 135 | 140 | ||
| @@ -349,10 +354,109 @@ private: | |||
| 349 | } \ | 354 | } \ |
| 350 | } while (false) | 355 | } while (false) |
| 351 | 356 | ||
| 352 | #define R_SUCCEEDED(res) (res.IsSuccess()) | 357 | #define R_SUCCEEDED(res) (static_cast<Result>(res).IsSuccess()) |
| 358 | #define R_FAILED(res) (static_cast<Result>(res).IsFailure()) | ||
| 353 | 359 | ||
| 354 | /// Evaluates a boolean expression, and succeeds if that expression is true. | 360 | namespace ResultImpl { |
| 355 | #define R_SUCCEED_IF(expr) R_UNLESS(!(expr), ResultSuccess) | 361 | template <auto EvaluateResult, class F> |
| 362 | class ScopedResultGuard { | ||
| 363 | YUZU_NON_COPYABLE(ScopedResultGuard); | ||
| 364 | YUZU_NON_MOVEABLE(ScopedResultGuard); | ||
| 365 | |||
| 366 | private: | ||
| 367 | Result& m_ref; | ||
| 368 | F m_f; | ||
| 369 | |||
| 370 | public: | ||
| 371 | constexpr ScopedResultGuard(Result& ref, F f) : m_ref(ref), m_f(std::move(f)) {} | ||
| 372 | constexpr ~ScopedResultGuard() { | ||
| 373 | if (EvaluateResult(m_ref)) { | ||
| 374 | m_f(); | ||
| 375 | } | ||
| 376 | } | ||
| 377 | }; | ||
| 378 | |||
| 379 | template <auto EvaluateResult> | ||
| 380 | class ResultReferenceForScopedResultGuard { | ||
| 381 | private: | ||
| 382 | Result& m_ref; | ||
| 383 | |||
| 384 | public: | ||
| 385 | constexpr ResultReferenceForScopedResultGuard(Result& r) : m_ref(r) {} | ||
| 386 | constexpr operator Result&() const { | ||
| 387 | return m_ref; | ||
| 388 | } | ||
| 389 | }; | ||
| 390 | |||
| 391 | template <auto EvaluateResult, typename F> | ||
| 392 | constexpr ScopedResultGuard<EvaluateResult, F> operator+( | ||
| 393 | ResultReferenceForScopedResultGuard<EvaluateResult> ref, F&& f) { | ||
| 394 | return ScopedResultGuard<EvaluateResult, F>(static_cast<Result&>(ref), std::forward<F>(f)); | ||
| 395 | } | ||
| 396 | |||
| 397 | constexpr bool EvaluateResultSuccess(const Result& r) { | ||
| 398 | return R_SUCCEEDED(r); | ||
| 399 | } | ||
| 400 | constexpr bool EvaluateResultFailure(const Result& r) { | ||
| 401 | return R_FAILED(r); | ||
| 402 | } | ||
| 403 | |||
| 404 | template <typename T> | ||
| 405 | constexpr void UpdateCurrentResultReference(T result_reference, Result result) = delete; | ||
| 406 | // Intentionally not defined | ||
| 407 | |||
| 408 | template <> | ||
| 409 | constexpr void UpdateCurrentResultReference<Result&>(Result& result_reference, Result result) { | ||
| 410 | result_reference = result; | ||
| 411 | } | ||
| 412 | |||
| 413 | template <> | ||
| 414 | constexpr void UpdateCurrentResultReference<const Result>(Result result_reference, Result result) {} | ||
| 415 | } // namespace ResultImpl | ||
| 416 | |||
| 417 | #define DECLARE_CURRENT_RESULT_REFERENCE_AND_STORAGE(COUNTER_VALUE) \ | ||
| 418 | [[maybe_unused]] constexpr bool HasPrevRef_##COUNTER_VALUE = \ | ||
| 419 | std::same_as<decltype(__TmpCurrentResultReference), Result&>; \ | ||
| 420 | [[maybe_unused]] auto& PrevRef_##COUNTER_VALUE = __TmpCurrentResultReference; \ | ||
| 421 | [[maybe_unused]] Result __tmp_result_##COUNTER_VALUE = ResultSuccess; \ | ||
| 422 | Result& __TmpCurrentResultReference = \ | ||
| 423 | HasPrevRef_##COUNTER_VALUE ? PrevRef_##COUNTER_VALUE : __tmp_result_##COUNTER_VALUE | ||
| 424 | |||
| 425 | #define ON_RESULT_RETURN_IMPL(...) \ | ||
| 426 | static_assert(std::same_as<decltype(__TmpCurrentResultReference), Result&>); \ | ||
| 427 | auto RESULT_GUARD_STATE_##__COUNTER__ = \ | ||
| 428 | ResultImpl::ResultReferenceForScopedResultGuard<__VA_ARGS__>( \ | ||
| 429 | __TmpCurrentResultReference) + \ | ||
| 430 | [&]() | ||
| 431 | |||
| 432 | #define ON_RESULT_FAILURE_2 ON_RESULT_RETURN_IMPL(ResultImpl::EvaluateResultFailure) | ||
| 433 | |||
| 434 | #define ON_RESULT_FAILURE \ | ||
| 435 | DECLARE_CURRENT_RESULT_REFERENCE_AND_STORAGE(__COUNTER__); \ | ||
| 436 | ON_RESULT_FAILURE_2 | ||
| 437 | |||
| 438 | #define ON_RESULT_SUCCESS_2 ON_RESULT_RETURN_IMPL(ResultImpl::EvaluateResultSuccess) | ||
| 439 | |||
| 440 | #define ON_RESULT_SUCCESS \ | ||
| 441 | DECLARE_CURRENT_RESULT_REFERENCE_AND_STORAGE(__COUNTER__); \ | ||
| 442 | ON_RESULT_SUCCESS_2 | ||
| 443 | |||
| 444 | constexpr inline Result __TmpCurrentResultReference = ResultSuccess; | ||
| 445 | |||
| 446 | /// Returns a result. | ||
| 447 | #define R_RETURN(res_expr) \ | ||
| 448 | { \ | ||
| 449 | const Result _tmp_r_throw_rc = (res_expr); \ | ||
| 450 | ResultImpl::UpdateCurrentResultReference<decltype(__TmpCurrentResultReference)>( \ | ||
| 451 | __TmpCurrentResultReference, _tmp_r_throw_rc); \ | ||
| 452 | return _tmp_r_throw_rc; \ | ||
| 453 | } | ||
| 454 | |||
| 455 | /// Returns ResultSuccess() | ||
| 456 | #define R_SUCCEED() R_RETURN(ResultSuccess) | ||
| 457 | |||
| 458 | /// Throws a result. | ||
| 459 | #define R_THROW(res_expr) R_RETURN(res_expr) | ||
| 356 | 460 | ||
| 357 | /// Evaluates a boolean expression, and returns a result unless that expression is true. | 461 | /// Evaluates a boolean expression, and returns a result unless that expression is true. |
| 358 | #define R_UNLESS(expr, res) \ | 462 | #define R_UNLESS(expr, res) \ |
| @@ -361,7 +465,7 @@ private: | |||
| 361 | if (res.IsError()) { \ | 465 | if (res.IsError()) { \ |
| 362 | LOG_ERROR(Kernel, "Failed with result: {}", res.raw); \ | 466 | LOG_ERROR(Kernel, "Failed with result: {}", res.raw); \ |
| 363 | } \ | 467 | } \ |
| 364 | return res; \ | 468 | R_THROW(res); \ |
| 365 | } \ | 469 | } \ |
| 366 | } | 470 | } |
| 367 | 471 | ||
| @@ -369,7 +473,10 @@ private: | |||
| 369 | #define R_TRY(res_expr) \ | 473 | #define R_TRY(res_expr) \ |
| 370 | { \ | 474 | { \ |
| 371 | const auto _tmp_r_try_rc = (res_expr); \ | 475 | const auto _tmp_r_try_rc = (res_expr); \ |
| 372 | if (_tmp_r_try_rc.IsError()) { \ | 476 | if (R_FAILED(_tmp_r_try_rc)) { \ |
| 373 | return _tmp_r_try_rc; \ | 477 | R_THROW(_tmp_r_try_rc); \ |
| 374 | } \ | 478 | } \ |
| 375 | } | 479 | } |
| 480 | |||
| 481 | /// Evaluates a boolean expression, and succeeds if that expression is true. | ||
| 482 | #define R_SUCCEED_IF(expr) R_UNLESS(!(expr), ResultSuccess) | ||
diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp index e0db787fc..fad532115 100644 --- a/src/core/hle/service/friend/friend.cpp +++ b/src/core/hle/service/friend/friend.cpp | |||
| @@ -26,7 +26,7 @@ public: | |||
| 26 | {10101, &IFriendService::GetFriendList, "GetFriendList"}, | 26 | {10101, &IFriendService::GetFriendList, "GetFriendList"}, |
| 27 | {10102, nullptr, "UpdateFriendInfo"}, | 27 | {10102, nullptr, "UpdateFriendInfo"}, |
| 28 | {10110, nullptr, "GetFriendProfileImage"}, | 28 | {10110, nullptr, "GetFriendProfileImage"}, |
| 29 | {10120, nullptr, "IsFriendListCacheAvailable"}, | 29 | {10120, &IFriendService::CheckFriendListAvailability, "CheckFriendListAvailability"}, |
| 30 | {10121, nullptr, "EnsureFriendListAvailable"}, | 30 | {10121, nullptr, "EnsureFriendListAvailable"}, |
| 31 | {10200, nullptr, "SendFriendRequestForApplication"}, | 31 | {10200, nullptr, "SendFriendRequestForApplication"}, |
| 32 | {10211, nullptr, "AddFacedFriendRequestForApplication"}, | 32 | {10211, nullptr, "AddFacedFriendRequestForApplication"}, |
| @@ -194,6 +194,17 @@ private: | |||
| 194 | // TODO(ogniK): Return a buffer of u64s which are the "NetworkServiceAccountId" | 194 | // TODO(ogniK): Return a buffer of u64s which are the "NetworkServiceAccountId" |
| 195 | } | 195 | } |
| 196 | 196 | ||
| 197 | void CheckFriendListAvailability(Kernel::HLERequestContext& ctx) { | ||
| 198 | IPC::RequestParser rp{ctx}; | ||
| 199 | const auto uuid{rp.PopRaw<Common::UUID>()}; | ||
| 200 | |||
| 201 | LOG_WARNING(Service_Friend, "(STUBBED) called, uuid=0x{}", uuid.RawString()); | ||
| 202 | |||
| 203 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 204 | rb.Push(ResultSuccess); | ||
| 205 | rb.Push(true); | ||
| 206 | } | ||
| 207 | |||
| 197 | KernelHelpers::ServiceContext service_context; | 208 | KernelHelpers::ServiceContext service_context; |
| 198 | 209 | ||
| 199 | Kernel::KEvent* completion_event; | 210 | Kernel::KEvent* completion_event; |
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index 12dbd9cc4..75e3b868d 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h | |||
| @@ -390,7 +390,7 @@ public: | |||
| 390 | FractionalEven = 2, | 390 | FractionalEven = 2, |
| 391 | }; | 391 | }; |
| 392 | 392 | ||
| 393 | enum class OutputPrimitves : u32 { | 393 | enum class OutputPrimitives : u32 { |
| 394 | Points = 0, | 394 | Points = 0, |
| 395 | Lines = 1, | 395 | Lines = 1, |
| 396 | Triangles_CW = 2, | 396 | Triangles_CW = 2, |
| @@ -401,7 +401,7 @@ public: | |||
| 401 | union { | 401 | union { |
| 402 | BitField<0, 2, DomainType> domain_type; | 402 | BitField<0, 2, DomainType> domain_type; |
| 403 | BitField<4, 2, Spacing> spacing; | 403 | BitField<4, 2, Spacing> spacing; |
| 404 | BitField<8, 2, OutputPrimitves> output_primitives; | 404 | BitField<8, 2, OutputPrimitives> output_primitives; |
| 405 | }; | 405 | }; |
| 406 | } params; | 406 | } params; |
| 407 | 407 | ||
diff --git a/src/video_core/host1x/syncpoint_manager.cpp b/src/video_core/host1x/syncpoint_manager.cpp index 326e8355a..a44fc83d3 100644 --- a/src/video_core/host1x/syncpoint_manager.cpp +++ b/src/video_core/host1x/syncpoint_manager.cpp | |||
| @@ -36,7 +36,17 @@ SyncpointManager::ActionHandle SyncpointManager::RegisterAction( | |||
| 36 | void SyncpointManager::DeregisterAction(std::list<RegisteredAction>& action_storage, | 36 | void SyncpointManager::DeregisterAction(std::list<RegisteredAction>& action_storage, |
| 37 | ActionHandle& handle) { | 37 | ActionHandle& handle) { |
| 38 | std::unique_lock lk(guard); | 38 | std::unique_lock lk(guard); |
| 39 | action_storage.erase(handle); | 39 | |
| 40 | // We want to ensure the iterator still exists prior to erasing it | ||
| 41 | // Otherwise, if an invalid iterator was passed in then it could lead to UB | ||
| 42 | // It is important to avoid UB in that case since the deregister isn't called from a locked | ||
| 43 | // context | ||
| 44 | for (auto it = action_storage.begin(); it != action_storage.end(); it++) { | ||
| 45 | if (it == handle) { | ||
| 46 | action_storage.erase(it); | ||
| 47 | return; | ||
| 48 | } | ||
| 49 | } | ||
| 40 | } | 50 | } |
| 41 | 51 | ||
| 42 | void SyncpointManager::DeregisterGuestAction(u32 syncpoint_id, ActionHandle& handle) { | 52 | void SyncpointManager::DeregisterGuestAction(u32 syncpoint_id, ActionHandle& handle) { |
diff --git a/src/video_core/renderer_base.cpp b/src/video_core/renderer_base.cpp index 45791aa75..e8761a747 100644 --- a/src/video_core/renderer_base.cpp +++ b/src/video_core/renderer_base.cpp | |||
| @@ -1,6 +1,8 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2015 Citra Emulator Project | 1 | // SPDX-FileCopyrightText: 2015 Citra Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include <thread> | ||
| 5 | |||
| 4 | #include "common/logging/log.h" | 6 | #include "common/logging/log.h" |
| 5 | #include "core/frontend/emu_window.h" | 7 | #include "core/frontend/emu_window.h" |
| 6 | #include "video_core/renderer_base.h" | 8 | #include "video_core/renderer_base.h" |
| @@ -35,8 +37,12 @@ void RendererBase::RequestScreenshot(void* data, std::function<void(bool)> callb | |||
| 35 | LOG_ERROR(Render, "A screenshot is already requested or in progress, ignoring the request"); | 37 | LOG_ERROR(Render, "A screenshot is already requested or in progress, ignoring the request"); |
| 36 | return; | 38 | return; |
| 37 | } | 39 | } |
| 40 | auto async_callback{[callback = std::move(callback)](bool invert_y) { | ||
| 41 | std::thread t{callback, invert_y}; | ||
| 42 | t.detach(); | ||
| 43 | }}; | ||
| 38 | renderer_settings.screenshot_bits = data; | 44 | renderer_settings.screenshot_bits = data; |
| 39 | renderer_settings.screenshot_complete_callback = std::move(callback); | 45 | renderer_settings.screenshot_complete_callback = async_callback; |
| 40 | renderer_settings.screenshot_framebuffer_layout = layout; | 46 | renderer_settings.screenshot_framebuffer_layout = layout; |
| 41 | renderer_settings.screenshot_requested = true; | 47 | renderer_settings.screenshot_requested = true; |
| 42 | } | 48 | } |
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 6bdb0b645..609f0a772 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp | |||
| @@ -317,8 +317,8 @@ GraphicsPipeline* ShaderCache::CurrentGraphicsPipeline() { | |||
| 317 | graphics_key.tessellation_primitive.Assign(regs.tessellation.params.domain_type.Value()); | 317 | graphics_key.tessellation_primitive.Assign(regs.tessellation.params.domain_type.Value()); |
| 318 | graphics_key.tessellation_spacing.Assign(regs.tessellation.params.spacing.Value()); | 318 | graphics_key.tessellation_spacing.Assign(regs.tessellation.params.spacing.Value()); |
| 319 | graphics_key.tessellation_clockwise.Assign( | 319 | graphics_key.tessellation_clockwise.Assign( |
| 320 | regs.tessellation.params.output_primitives.Value() != | 320 | regs.tessellation.params.output_primitives.Value() == |
| 321 | Maxwell::Tessellation::OutputPrimitves::Triangles_CCW); | 321 | Maxwell::Tessellation::OutputPrimitives::Triangles_CW); |
| 322 | graphics_key.xfb_enabled.Assign(regs.transform_feedback_enabled != 0 ? 1 : 0); | 322 | graphics_key.xfb_enabled.Assign(regs.transform_feedback_enabled != 0 ? 1 : 0); |
| 323 | if (graphics_key.xfb_enabled) { | 323 | if (graphics_key.xfb_enabled) { |
| 324 | SetXfbState(graphics_key.xfb_state, regs); | 324 | SetXfbState(graphics_key.xfb_state, regs); |
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp index eab1b8f93..f85ed8e5b 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp | |||
| @@ -73,8 +73,8 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, | |||
| 73 | patch_control_points_minus_one.Assign(regs.patch_vertices - 1); | 73 | patch_control_points_minus_one.Assign(regs.patch_vertices - 1); |
| 74 | tessellation_primitive.Assign(static_cast<u32>(regs.tessellation.params.domain_type.Value())); | 74 | tessellation_primitive.Assign(static_cast<u32>(regs.tessellation.params.domain_type.Value())); |
| 75 | tessellation_spacing.Assign(static_cast<u32>(regs.tessellation.params.spacing.Value())); | 75 | tessellation_spacing.Assign(static_cast<u32>(regs.tessellation.params.spacing.Value())); |
| 76 | tessellation_clockwise.Assign(regs.tessellation.params.output_primitives.Value() != | 76 | tessellation_clockwise.Assign(regs.tessellation.params.output_primitives.Value() == |
| 77 | Maxwell::Tessellation::OutputPrimitves::Triangles_CCW); | 77 | Maxwell::Tessellation::OutputPrimitives::Triangles_CW); |
| 78 | logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0); | 78 | logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0); |
| 79 | logic_op.Assign(PackLogicOp(regs.logic_op.op)); | 79 | logic_op.Assign(PackLogicOp(regs.logic_op.op)); |
| 80 | topology.Assign(regs.draw.topology); | 80 | topology.Assign(regs.draw.topology); |
diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp index 7fb256953..06f68d09a 100644 --- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp +++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp | |||
| @@ -26,39 +26,20 @@ using namespace Common::Literals; | |||
| 26 | constexpr VkDeviceSize MAX_ALIGNMENT = 256; | 26 | constexpr VkDeviceSize MAX_ALIGNMENT = 256; |
| 27 | // Maximum size to put elements in the stream buffer | 27 | // Maximum size to put elements in the stream buffer |
| 28 | constexpr VkDeviceSize MAX_STREAM_BUFFER_REQUEST_SIZE = 8_MiB; | 28 | constexpr VkDeviceSize MAX_STREAM_BUFFER_REQUEST_SIZE = 8_MiB; |
| 29 | // Stream buffer size in bytes | ||
| 30 | constexpr VkDeviceSize STREAM_BUFFER_SIZE = 128_MiB; | ||
| 31 | constexpr VkDeviceSize REGION_SIZE = STREAM_BUFFER_SIZE / StagingBufferPool::NUM_SYNCS; | ||
| 29 | 32 | ||
| 30 | constexpr VkMemoryPropertyFlags HOST_FLAGS = | 33 | constexpr VkMemoryPropertyFlags HOST_FLAGS = |
| 31 | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; | 34 | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; |
| 32 | constexpr VkMemoryPropertyFlags STREAM_FLAGS = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | HOST_FLAGS; | 35 | constexpr VkMemoryPropertyFlags STREAM_FLAGS = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | HOST_FLAGS; |
| 33 | 36 | ||
| 34 | static bool IsStreamHeap(VkMemoryHeap heap, size_t staging_buffer_size) noexcept { | 37 | bool IsStreamHeap(VkMemoryHeap heap) noexcept { |
| 35 | return staging_buffer_size < (heap.size * 2) / 3; | 38 | return STREAM_BUFFER_SIZE < (heap.size * 2) / 3; |
| 36 | } | ||
| 37 | |||
| 38 | static bool HasLargeDeviceLocalHostVisibleMemory(const VkPhysicalDeviceMemoryProperties& props) { | ||
| 39 | const auto flags{VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT}; | ||
| 40 | |||
| 41 | for (u32 type_index = 0; type_index < props.memoryTypeCount; ++type_index) { | ||
| 42 | const auto& memory_type{props.memoryTypes[type_index]}; | ||
| 43 | |||
| 44 | if ((memory_type.propertyFlags & flags) != flags) { | ||
| 45 | // Memory must be device local and host visible | ||
| 46 | continue; | ||
| 47 | } | ||
| 48 | |||
| 49 | const auto& heap{props.memoryHeaps[memory_type.heapIndex]}; | ||
| 50 | if (heap.size >= 7168_MiB) { | ||
| 51 | // This is the right type of memory | ||
| 52 | return true; | ||
| 53 | } | ||
| 54 | } | ||
| 55 | |||
| 56 | return false; | ||
| 57 | } | 39 | } |
| 58 | 40 | ||
| 59 | std::optional<u32> FindMemoryTypeIndex(const VkPhysicalDeviceMemoryProperties& props, u32 type_mask, | 41 | std::optional<u32> FindMemoryTypeIndex(const VkPhysicalDeviceMemoryProperties& props, u32 type_mask, |
| 60 | VkMemoryPropertyFlags flags, | 42 | VkMemoryPropertyFlags flags) noexcept { |
| 61 | size_t staging_buffer_size) noexcept { | ||
| 62 | for (u32 type_index = 0; type_index < props.memoryTypeCount; ++type_index) { | 43 | for (u32 type_index = 0; type_index < props.memoryTypeCount; ++type_index) { |
| 63 | if (((type_mask >> type_index) & 1) == 0) { | 44 | if (((type_mask >> type_index) & 1) == 0) { |
| 64 | // Memory type is incompatible | 45 | // Memory type is incompatible |
| @@ -69,7 +50,7 @@ std::optional<u32> FindMemoryTypeIndex(const VkPhysicalDeviceMemoryProperties& p | |||
| 69 | // Memory type doesn't have the flags we want | 50 | // Memory type doesn't have the flags we want |
| 70 | continue; | 51 | continue; |
| 71 | } | 52 | } |
| 72 | if (!IsStreamHeap(props.memoryHeaps[memory_type.heapIndex], staging_buffer_size)) { | 53 | if (!IsStreamHeap(props.memoryHeaps[memory_type.heapIndex])) { |
| 73 | // Memory heap is not suitable for streaming | 54 | // Memory heap is not suitable for streaming |
| 74 | continue; | 55 | continue; |
| 75 | } | 56 | } |
| @@ -80,17 +61,17 @@ std::optional<u32> FindMemoryTypeIndex(const VkPhysicalDeviceMemoryProperties& p | |||
| 80 | } | 61 | } |
| 81 | 62 | ||
| 82 | u32 FindMemoryTypeIndex(const VkPhysicalDeviceMemoryProperties& props, u32 type_mask, | 63 | u32 FindMemoryTypeIndex(const VkPhysicalDeviceMemoryProperties& props, u32 type_mask, |
| 83 | bool try_device_local, size_t staging_buffer_size) { | 64 | bool try_device_local) { |
| 84 | std::optional<u32> type; | 65 | std::optional<u32> type; |
| 85 | if (try_device_local) { | 66 | if (try_device_local) { |
| 86 | // Try to find a DEVICE_LOCAL_BIT type, Nvidia and AMD have a dedicated heap for this | 67 | // Try to find a DEVICE_LOCAL_BIT type, Nvidia and AMD have a dedicated heap for this |
| 87 | type = FindMemoryTypeIndex(props, type_mask, STREAM_FLAGS, staging_buffer_size); | 68 | type = FindMemoryTypeIndex(props, type_mask, STREAM_FLAGS); |
| 88 | if (type) { | 69 | if (type) { |
| 89 | return *type; | 70 | return *type; |
| 90 | } | 71 | } |
| 91 | } | 72 | } |
| 92 | // Otherwise try without the DEVICE_LOCAL_BIT | 73 | // Otherwise try without the DEVICE_LOCAL_BIT |
| 93 | type = FindMemoryTypeIndex(props, type_mask, HOST_FLAGS, staging_buffer_size); | 74 | type = FindMemoryTypeIndex(props, type_mask, HOST_FLAGS); |
| 94 | if (type) { | 75 | if (type) { |
| 95 | return *type; | 76 | return *type; |
| 96 | } | 77 | } |
| @@ -98,32 +79,20 @@ u32 FindMemoryTypeIndex(const VkPhysicalDeviceMemoryProperties& props, u32 type_ | |||
| 98 | throw vk::Exception(VK_ERROR_OUT_OF_DEVICE_MEMORY); | 79 | throw vk::Exception(VK_ERROR_OUT_OF_DEVICE_MEMORY); |
| 99 | } | 80 | } |
| 100 | 81 | ||
| 101 | size_t Region(size_t iterator, size_t region_size) noexcept { | 82 | size_t Region(size_t iterator) noexcept { |
| 102 | return iterator / region_size; | 83 | return iterator / REGION_SIZE; |
| 103 | } | 84 | } |
| 104 | } // Anonymous namespace | 85 | } // Anonymous namespace |
| 105 | 86 | ||
| 106 | StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& memory_allocator_, | 87 | StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& memory_allocator_, |
| 107 | Scheduler& scheduler_) | 88 | Scheduler& scheduler_) |
| 108 | : device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_} { | 89 | : device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_} { |
| 109 | |||
| 110 | const auto memory_properties{device.GetPhysical().GetMemoryProperties().memoryProperties}; | ||
| 111 | if (HasLargeDeviceLocalHostVisibleMemory(memory_properties)) { | ||
| 112 | // Possible on many integrated and newer discrete cards | ||
| 113 | staging_buffer_size = 1_GiB; | ||
| 114 | } else { | ||
| 115 | // Well-supported default size used by most Vulkan PC games | ||
| 116 | staging_buffer_size = 256_MiB; | ||
| 117 | } | ||
| 118 | |||
| 119 | region_size = staging_buffer_size / StagingBufferPool::NUM_SYNCS; | ||
| 120 | |||
| 121 | const vk::Device& dev = device.GetLogical(); | 90 | const vk::Device& dev = device.GetLogical(); |
| 122 | stream_buffer = dev.CreateBuffer(VkBufferCreateInfo{ | 91 | stream_buffer = dev.CreateBuffer(VkBufferCreateInfo{ |
| 123 | .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, | 92 | .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, |
| 124 | .pNext = nullptr, | 93 | .pNext = nullptr, |
| 125 | .flags = 0, | 94 | .flags = 0, |
| 126 | .size = staging_buffer_size, | 95 | .size = STREAM_BUFFER_SIZE, |
| 127 | .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | | 96 | .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | |
| 128 | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, | 97 | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, |
| 129 | .sharingMode = VK_SHARING_MODE_EXCLUSIVE, | 98 | .sharingMode = VK_SHARING_MODE_EXCLUSIVE, |
| @@ -148,18 +117,19 @@ StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& mem | |||
| 148 | .image = nullptr, | 117 | .image = nullptr, |
| 149 | .buffer = *stream_buffer, | 118 | .buffer = *stream_buffer, |
| 150 | }; | 119 | }; |
| 120 | const auto memory_properties = device.GetPhysical().GetMemoryProperties().memoryProperties; | ||
| 151 | VkMemoryAllocateInfo stream_memory_info{ | 121 | VkMemoryAllocateInfo stream_memory_info{ |
| 152 | .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, | 122 | .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, |
| 153 | .pNext = make_dedicated ? &dedicated_info : nullptr, | 123 | .pNext = make_dedicated ? &dedicated_info : nullptr, |
| 154 | .allocationSize = requirements.size, | 124 | .allocationSize = requirements.size, |
| 155 | .memoryTypeIndex = FindMemoryTypeIndex(memory_properties, requirements.memoryTypeBits, true, | 125 | .memoryTypeIndex = |
| 156 | staging_buffer_size), | 126 | FindMemoryTypeIndex(memory_properties, requirements.memoryTypeBits, true), |
| 157 | }; | 127 | }; |
| 158 | stream_memory = dev.TryAllocateMemory(stream_memory_info); | 128 | stream_memory = dev.TryAllocateMemory(stream_memory_info); |
| 159 | if (!stream_memory) { | 129 | if (!stream_memory) { |
| 160 | LOG_INFO(Render_Vulkan, "Dynamic memory allocation failed, trying with system memory"); | 130 | LOG_INFO(Render_Vulkan, "Dynamic memory allocation failed, trying with system memory"); |
| 161 | stream_memory_info.memoryTypeIndex = FindMemoryTypeIndex( | 131 | stream_memory_info.memoryTypeIndex = |
| 162 | memory_properties, requirements.memoryTypeBits, false, staging_buffer_size); | 132 | FindMemoryTypeIndex(memory_properties, requirements.memoryTypeBits, false); |
| 163 | stream_memory = dev.AllocateMemory(stream_memory_info); | 133 | stream_memory = dev.AllocateMemory(stream_memory_info); |
| 164 | } | 134 | } |
| 165 | 135 | ||
| @@ -167,7 +137,7 @@ StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& mem | |||
| 167 | stream_memory.SetObjectNameEXT("Stream Buffer Memory"); | 137 | stream_memory.SetObjectNameEXT("Stream Buffer Memory"); |
| 168 | } | 138 | } |
| 169 | stream_buffer.BindMemory(*stream_memory, 0); | 139 | stream_buffer.BindMemory(*stream_memory, 0); |
| 170 | stream_pointer = stream_memory.Map(0, staging_buffer_size); | 140 | stream_pointer = stream_memory.Map(0, STREAM_BUFFER_SIZE); |
| 171 | } | 141 | } |
| 172 | 142 | ||
| 173 | StagingBufferPool::~StagingBufferPool() = default; | 143 | StagingBufferPool::~StagingBufferPool() = default; |
| @@ -188,25 +158,25 @@ void StagingBufferPool::TickFrame() { | |||
| 188 | } | 158 | } |
| 189 | 159 | ||
| 190 | StagingBufferRef StagingBufferPool::GetStreamBuffer(size_t size) { | 160 | StagingBufferRef StagingBufferPool::GetStreamBuffer(size_t size) { |
| 191 | if (AreRegionsActive(Region(free_iterator, region_size) + 1, | 161 | if (AreRegionsActive(Region(free_iterator) + 1, |
| 192 | std::min(Region(iterator + size, region_size) + 1, NUM_SYNCS))) { | 162 | std::min(Region(iterator + size) + 1, NUM_SYNCS))) { |
| 193 | // Avoid waiting for the previous usages to be free | 163 | // Avoid waiting for the previous usages to be free |
| 194 | return GetStagingBuffer(size, MemoryUsage::Upload); | 164 | return GetStagingBuffer(size, MemoryUsage::Upload); |
| 195 | } | 165 | } |
| 196 | const u64 current_tick = scheduler.CurrentTick(); | 166 | const u64 current_tick = scheduler.CurrentTick(); |
| 197 | std::fill(sync_ticks.begin() + Region(used_iterator, region_size), | 167 | std::fill(sync_ticks.begin() + Region(used_iterator), sync_ticks.begin() + Region(iterator), |
| 198 | sync_ticks.begin() + Region(iterator, region_size), current_tick); | 168 | current_tick); |
| 199 | used_iterator = iterator; | 169 | used_iterator = iterator; |
| 200 | free_iterator = std::max(free_iterator, iterator + size); | 170 | free_iterator = std::max(free_iterator, iterator + size); |
| 201 | 171 | ||
| 202 | if (iterator + size >= staging_buffer_size) { | 172 | if (iterator + size >= STREAM_BUFFER_SIZE) { |
| 203 | std::fill(sync_ticks.begin() + Region(used_iterator, region_size), | 173 | std::fill(sync_ticks.begin() + Region(used_iterator), sync_ticks.begin() + NUM_SYNCS, |
| 204 | sync_ticks.begin() + NUM_SYNCS, current_tick); | 174 | current_tick); |
| 205 | used_iterator = 0; | 175 | used_iterator = 0; |
| 206 | iterator = 0; | 176 | iterator = 0; |
| 207 | free_iterator = size; | 177 | free_iterator = size; |
| 208 | 178 | ||
| 209 | if (AreRegionsActive(0, Region(size, region_size) + 1)) { | 179 | if (AreRegionsActive(0, Region(size) + 1)) { |
| 210 | // Avoid waiting for the previous usages to be free | 180 | // Avoid waiting for the previous usages to be free |
| 211 | return GetStagingBuffer(size, MemoryUsage::Upload); | 181 | return GetStagingBuffer(size, MemoryUsage::Upload); |
| 212 | } | 182 | } |
diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h index 90c67177f..91dc84da8 100644 --- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h +++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h | |||
| @@ -93,9 +93,6 @@ private: | |||
| 93 | size_t free_iterator = 0; | 93 | size_t free_iterator = 0; |
| 94 | std::array<u64, NUM_SYNCS> sync_ticks{}; | 94 | std::array<u64, NUM_SYNCS> sync_ticks{}; |
| 95 | 95 | ||
| 96 | size_t staging_buffer_size = 0; | ||
| 97 | size_t region_size = 0; | ||
| 98 | |||
| 99 | StagingBuffersCache device_local_cache; | 96 | StagingBuffersCache device_local_cache; |
| 100 | StagingBuffersCache upload_cache; | 97 | StagingBuffersCache upload_cache; |
| 101 | StagingBuffersCache download_cache; | 98 | StagingBuffersCache download_cache; |
diff --git a/src/yuzu/configuration/configure_audio.cpp b/src/yuzu/configuration/configure_audio.cpp index 19b8b15ef..70cc6f84b 100644 --- a/src/yuzu/configuration/configure_audio.cpp +++ b/src/yuzu/configuration/configure_audio.cpp | |||
| @@ -161,8 +161,8 @@ void ConfigureAudio::InitializeAudioSinkComboBox() { | |||
| 161 | ui->sink_combo_box->clear(); | 161 | ui->sink_combo_box->clear(); |
| 162 | ui->sink_combo_box->addItem(QString::fromUtf8(AudioCore::Sink::auto_device_name)); | 162 | ui->sink_combo_box->addItem(QString::fromUtf8(AudioCore::Sink::auto_device_name)); |
| 163 | 163 | ||
| 164 | for (const char* id : AudioCore::Sink::GetSinkIDs()) { | 164 | for (const auto& id : AudioCore::Sink::GetSinkIDs()) { |
| 165 | ui->sink_combo_box->addItem(QString::fromUtf8(id)); | 165 | ui->sink_combo_box->addItem(QString::fromUtf8(id.data(), static_cast<s32>(id.length()))); |
| 166 | } | 166 | } |
| 167 | } | 167 | } |
| 168 | 168 | ||
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui index 1e4f74704..fdbb33372 100644 --- a/src/yuzu/configuration/configure_graphics.ui +++ b/src/yuzu/configuration/configure_graphics.ui | |||
| @@ -301,6 +301,11 @@ | |||
| 301 | </item> | 301 | </item> |
| 302 | <item> | 302 | <item> |
| 303 | <property name="text"> | 303 | <property name="text"> |
| 304 | <string>Force 16:10</string> | ||
| 305 | </property> | ||
| 306 | </item> | ||
| 307 | <item> | ||
| 308 | <property name="text"> | ||
| 304 | <string>Stretch to Window</string> | 309 | <string>Stretch to Window</string> |
| 305 | </property> | 310 | </property> |
| 306 | </item> | 311 | </item> |