From 876d7f90b60e3fed88b6ae8eb2720f1f9a02306c Mon Sep 17 00:00:00 2001 From: Kelebek1 Date: Mon, 25 Mar 2024 19:16:08 +0100 Subject: [PATCH 01/13] Add option to log synchronously, add tooltip to log filter. --- src/common/logging/backend.cpp | 21 +++++++++++++++----- src/common/logging/filter.cpp | 1 + src/common/settings.h | 1 + src/suyu/configuration/configure_debug.cpp | 2 ++ src/suyu/configuration/configure_debug.ui | 23 +++++++++++++++++++++- 5 files changed, 42 insertions(+), 6 deletions(-) diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index fd16197749..2cdd6cf06a 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -231,8 +232,15 @@ public: if (!filter.CheckMessage(log_class, log_level)) { return; } - message_queue.EmplaceWait( - CreateEntry(log_class, log_level, filename, line_num, function, std::move(message))); + + auto entry = + CreateEntry(log_class, log_level, filename, line_num, function, std::move(message)); + if (Settings::values.log_async) { + message_queue.EmplaceWait(entry); + } else { + std::scoped_lock l{sync_mutex}; + ForEachBackend([&entry](Backend& backend) { backend.Write(entry); }); + } } private: @@ -313,6 +321,7 @@ private: #endif MPSCQueue message_queue{}; + std::mutex sync_mutex; std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()}; std::jthread backend_thread; }; @@ -345,9 +354,11 @@ void SetColorConsoleBackendEnabled(bool enabled) { void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename, unsigned int line_num, const char* function, fmt::string_view format, const fmt::format_args& args) { - if (!initialization_in_progress_suppress_logging) { - Impl::Instance().PushEntry(log_class, log_level, filename, line_num, function, - fmt::vformat(format, args)); + if (initialization_in_progress_suppress_logging) { + return; } + + Impl::Instance().PushEntry(log_class, log_level, filename, line_num, function, + fmt::vformat(format, args)); } } // namespace Common::Log diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index 4e3a614a45..c16f4df3aa 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp @@ -171,6 +171,7 @@ const char* GetLogClassName(Class log_class) { #define SUB(x, y) \ case Class::x##_##y: \ return #x "." #y; +// return #x "_" #y; ALL_LOG_CLASSES() #undef CLS #undef SUB diff --git a/src/common/settings.h b/src/common/settings.h index 48f127bde8..829759c94b 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -604,6 +604,7 @@ struct Values { // Miscellaneous Setting log_filter{linkage, "*:Info", "log_filter", Category::Miscellaneous}; + Setting log_async{linkage, true, "log_async", Category::Miscellaneous}; Setting use_dev_keys{linkage, false, "use_dev_keys", Category::Miscellaneous}; // Network diff --git a/src/suyu/configuration/configure_debug.cpp b/src/suyu/configuration/configure_debug.cpp index b4fa4eca09..6f9bb36340 100644 --- a/src/suyu/configuration/configure_debug.cpp +++ b/src/suyu/configuration/configure_debug.cpp @@ -73,6 +73,7 @@ void ConfigureDebug::SetConfiguration() { ui->disable_loop_safety_checks->setChecked( Settings::values.disable_shader_loop_safety_checks.GetValue()); ui->extended_logging->setChecked(Settings::values.extended_logging.GetValue()); + ui->log_async->setChecked(Settings::values.log_async.GetValue()); ui->perform_vulkan_check->setChecked(Settings::values.perform_vulkan_check.GetValue()); #ifdef SUYU_USE_QT_WEB_ENGINE @@ -115,6 +116,7 @@ void ConfigureDebug::ApplyConfiguration() { Common::Log::Filter filter; filter.ParseFilterString(Settings::values.log_filter.GetValue()); Common::Log::SetGlobalFilter(filter); + Settings::values.log_async = ui->log_async->isChecked(); } void ConfigureDebug::changeEvent(QEvent* event) { diff --git a/src/suyu/configuration/configure_debug.ui b/src/suyu/configuration/configure_debug.ui index ed48b2f8f6..e5b76290e6 100644 --- a/src/suyu/configuration/configure_debug.ui +++ b/src/suyu/configuration/configure_debug.ui @@ -164,6 +164,20 @@ + + + + true + + + When checked, logging will run asynchronously. This may cut the log on crashes. +When unchecked, logging will run synchronously. This will slow down the emulator, but allow all logs to be written. Useful for debugging. + + + Log asynchronously + + + @@ -199,7 +213,14 @@ - + + + Log filter in the form <class>:<level>. +Separate multiple filters with a space. +Levels: Trace, Debug, Info, Warning, Error, Critical +Classes: See Common/logging/types.h + + From 040893da00a4d42f973756e26c7f94213d4d7685 Mon Sep 17 00:00:00 2001 From: ilonachan Date: Mon, 25 Mar 2024 19:25:11 +0100 Subject: [PATCH 02/13] formatting --- src/common/logging/filter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index c16f4df3aa..ae866c7a50 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp @@ -171,7 +171,7 @@ const char* GetLogClassName(Class log_class) { #define SUB(x, y) \ case Class::x##_##y: \ return #x "." #y; -// return #x "_" #y; + // return #x "_" #y; ALL_LOG_CLASSES() #undef CLS #undef SUB From ce8f3e802edc216caebfdee40d589f13e8714598 Mon Sep 17 00:00:00 2001 From: Fijxu Date: Fri, 29 Mar 2024 01:04:51 -0300 Subject: [PATCH 03/13] Use proper SPDX-FileCopyrightText for Sudachi. Corrects db647d915d --- .../backend/glasm/emit_glasm_context_get_set.cpp | 3 ++- .../backend/glsl/emit_glsl_context_get_set.cpp | 3 ++- .../backend/spirv/emit_spirv_context_get_set.cpp | 3 ++- src/shader_recompiler/runtime_info.h | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp index d4a6d7f9ad..480649cc82 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp @@ -1,4 +1,5 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project, 2024 sudachi Emulator Project +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: 2024 sudachi Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp index 44ce3242a5..3c0c19961d 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp @@ -1,4 +1,5 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project, 2024 sudachi Emulator Project +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: 2024 sudachi Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include 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 adec9fa46f..5d428d2d68 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 @@ -1,4 +1,5 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project, 2024 sudachi Emulator Project +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: 2024 sudachi Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include diff --git a/src/shader_recompiler/runtime_info.h b/src/shader_recompiler/runtime_info.h index f49c1bdda1..f9ac9fbb42 100644 --- a/src/shader_recompiler/runtime_info.h +++ b/src/shader_recompiler/runtime_info.h @@ -1,4 +1,5 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project, 2024 sudachi Emulator Project +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: 2024 sudachi Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once From 36ede797f3dd48c854021a12349b205366439f0c Mon Sep 17 00:00:00 2001 From: Lucas Clemente Vella Date: Sat, 23 Mar 2024 23:46:02 +0000 Subject: [PATCH 04/13] Vulkan validation error fix. Different image usage flags between image creation and image view creation. --- .../renderer_vulkan/vk_texture_cache.cpp | 14 +++++++------- .../renderer_vulkan/vk_texture_cache.h | 15 +++++++++++---- .../vulkan_common/vulkan_memory_allocator.cpp | 2 +- src/video_core/vulkan_common/vulkan_wrapper.h | 17 ++++++++++++----- 4 files changed, 31 insertions(+), 17 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 8909c77af3..24853ebb9e 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -1406,7 +1406,7 @@ Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu if (runtime->device.HasDebuggingToolAttached()) { original_image.SetObjectNameEXT(VideoCommon::Name(*this).c_str()); } - current_image = *original_image; + current_image = &Image::original_image; storage_image_views.resize(info.resources.levels); if (IsPixelFormatASTC(info.format) && !runtime->device.IsOptimalAstcSupported() && Settings::values.astc_recompression.GetValue() == @@ -1550,8 +1550,8 @@ VkImageView Image::StorageImageView(s32 level) noexcept { if (!view) { const auto format_info = MaxwellToVK::SurfaceFormat(runtime->device, FormatType::Optimal, true, info.format); - view = - MakeStorageView(runtime->device.GetLogical(), level, current_image, format_info.format); + view = MakeStorageView(runtime->device.GetLogical(), level, *(this->*current_image), + format_info.format); } return *view; } @@ -1582,7 +1582,7 @@ bool Image::ScaleUp(bool ignore) { runtime->ViewFormats(info.format)); ignore = false; } - current_image = *scaled_image; + current_image = &Image::scaled_image; if (ignore) { return true; } @@ -1607,7 +1607,7 @@ bool Image::ScaleDown(bool ignore) { } ASSERT(info.type != ImageType::Linear); flags &= ~ImageFlagBits::Rescaled; - current_image = *original_image; + current_image = &Image::original_image; if (ignore) { return true; } @@ -1726,7 +1726,7 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI const VkImageViewUsageCreateInfo image_view_usage{ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO, .pNext = nullptr, - .usage = ImageUsageFlags(format_info, format), + .usage = image.UsageFlags(), }; const VkImageViewCreateInfo create_info{ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, @@ -2087,4 +2087,4 @@ void TextureCacheRuntime::TransitionImageLayout(Image& image) { } } -} // namespace Vulkan \ No newline at end of file +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index 8501ec384b..4161d7ff92 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -24,7 +24,6 @@ using Common::SlotVector; using VideoCommon::ImageId; using VideoCommon::NUM_RT; using VideoCommon::Region2D; -using VideoCommon::RenderTargets; using VideoCore::Surface::PixelFormat; class BlitImageHelper; @@ -157,13 +156,17 @@ public: std::span copies); [[nodiscard]] VkImage Handle() const noexcept { - return current_image; + return *(this->*current_image); } [[nodiscard]] VkImageAspectFlags AspectMask() const noexcept { return aspect_mask; } + [[nodiscard]] VkImageUsageFlags UsageFlags() const noexcept { + return (this->*current_image).UsageFlags(); + } + /// Returns true when the image is already initialized and mark it as initialized [[nodiscard]] bool ExchangeInitialization() noexcept { return std::exchange(initialized, true); @@ -186,11 +189,15 @@ private: TextureCacheRuntime* runtime{}; vk::Image original_image; + vk::Image scaled_image; + + // Use a pointer to field because it is relative, so that the object can be + // moved without breaking the reference. + vk::Image Image::*current_image{}; + std::vector storage_image_views; VkImageAspectFlags aspect_mask = 0; bool initialized = false; - vk::Image scaled_image{}; - VkImage current_image{}; std::unique_ptr scale_framebuffer; std::unique_ptr scale_view; diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp index 8f4a57e3ca..54331688e3 100644 --- a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp +++ b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp @@ -247,7 +247,7 @@ vk::Image MemoryAllocator::CreateImage(const VkImageCreateInfo& ci) const { vk::Check(vmaCreateImage(allocator, &ci, &alloc_ci, &handle, &allocation, nullptr)); - return vk::Image(handle, *device.GetLogical(), allocator, allocation, + return vk::Image(handle, ci.usage, *device.GetLogical(), allocator, allocation, device.GetDispatchLoader()); } diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h index 757f3c8afb..1bb8a47bdf 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.h +++ b/src/video_core/vulkan_common/vulkan_wrapper.h @@ -623,9 +623,10 @@ public: class Image { public: - explicit Image(VkImage handle_, VkDevice owner_, VmaAllocator allocator_, - VmaAllocation allocation_, const DeviceDispatch& dld_) noexcept - : handle{handle_}, owner{owner_}, allocator{allocator_}, + explicit Image(VkImage handle_, VkImageUsageFlags usage_, VkDevice owner_, + VmaAllocator allocator_, VmaAllocation allocation_, + const DeviceDispatch& dld_) noexcept + : handle{handle_}, usage{usage_}, owner{owner_}, allocator{allocator_}, allocation{allocation_}, dld{&dld_} {} Image() = default; @@ -633,12 +634,13 @@ public: Image& operator=(const Image&) = delete; Image(Image&& rhs) noexcept - : handle{std::exchange(rhs.handle, nullptr)}, owner{rhs.owner}, allocator{rhs.allocator}, - allocation{rhs.allocation}, dld{rhs.dld} {} + : handle{std::exchange(rhs.handle, nullptr)}, usage{rhs.usage}, owner{rhs.owner}, + allocator{rhs.allocator}, allocation{rhs.allocation}, dld{rhs.dld} {} Image& operator=(Image&& rhs) noexcept { Release(); handle = std::exchange(rhs.handle, nullptr); + usage = rhs.usage; owner = rhs.owner; allocator = rhs.allocator; allocation = rhs.allocation; @@ -665,10 +667,15 @@ public: void SetObjectNameEXT(const char* name) const; + [[nodiscard]] VkImageUsageFlags UsageFlags() const noexcept { + return usage; + } + private: void Release() const noexcept; VkImage handle = nullptr; + VkImageUsageFlags usage{}; VkDevice owner = nullptr; VmaAllocator allocator = nullptr; VmaAllocation allocation = nullptr; From b5c02fe14a0739add8e7ee0553280f2cd15a8704 Mon Sep 17 00:00:00 2001 From: zqpvr01 Date: Fri, 29 Mar 2024 17:31:07 +0100 Subject: [PATCH 05/13] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 6334f02b2c..2e76388ba5 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,9 @@ It is written in C++ with portability in mind, and we're actively working on bui Pipelines

+## Hardware Requirements +[Click here to see the Hardware Requirements](https://git.suyu.dev/suyu/suyu/wiki/Hardware-Requirements) + ## Status Although we're able to make builds, we don't have a version ready for distribution yet. But we can always use more help! You can make a merge request if you'd like to see something changed, or you can [chat with other developers to find out what needs work](https://discord.gg/suyu). From 9e223759e0e804514c318f282038fc0e12c8a180 Mon Sep 17 00:00:00 2001 From: zqpvr01 Date: Fri, 29 Mar 2024 19:18:38 +0100 Subject: [PATCH 06/13] Add img --- img | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 img diff --git a/img b/img new file mode 100644 index 0000000000..e69de29bb2 From 2a672fac30cd3a7b578088c02795d641ccbc93c8 Mon Sep 17 00:00:00 2001 From: zqpvr01 Date: Fri, 29 Mar 2024 19:19:11 +0100 Subject: [PATCH 07/13] revert 9e223759e0e804514c318f282038fc0e12c8a180 revert Add img --- img | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 img diff --git a/img b/img deleted file mode 100644 index e69de29bb2..0000000000 From 444a200eef33193403ab8d1ec3ee81742f611156 Mon Sep 17 00:00:00 2001 From: zqpvr01 Date: Fri, 29 Mar 2024 19:19:28 +0100 Subject: [PATCH 08/13] Upload files to "img" --- img/maximum-clocks.png | Bin 0 -> 30758 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 img/maximum-clocks.png diff --git a/img/maximum-clocks.png b/img/maximum-clocks.png new file mode 100644 index 0000000000000000000000000000000000000000..0353c0bf83796ea720b4841784e90595142e6637 GIT binary patch literal 30758 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 XQCfY7gzR62=+q4No9rvlBVYM{Sf;j4 literal 0 HcmV?d00001 From 66cf0c1b0c9452637266f4faf7077fd5d2daa88d Mon Sep 17 00:00:00 2001 From: Hustler One Date: Mon, 25 Mar 2024 20:48:12 +0100 Subject: [PATCH 09/13] Clarify that we indeed have builds Cherry-picked from #65 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2e76388ba5..3448576d3f 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ It is written in C++ with portability in mind, and we're actively working on bui ## Status -Although we're able to make builds, we don't have a version ready for distribution yet. But we can always use more help! You can make a merge request if you'd like to see something changed, or you can [chat with other developers to find out what needs work](https://discord.gg/suyu). +We currently have builds over at the [Releases](https://git.suyu.dev/suyu/suyu/releases) page. **Note**: We try to update this README whenever we can, but some links might be broken, and some information may be outdated or irrelevant. From ba3539c5179913fd32f172d4002cb47b775ddb57 Mon Sep 17 00:00:00 2001 From: Fijxu Date: Fri, 29 Mar 2024 21:31:40 -0300 Subject: [PATCH 10/13] Use CC0-1.0 for images under ./img --- .reuse/dep5 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.reuse/dep5 b/.reuse/dep5 index b39bc42fb7..baf1354bf6 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -180,3 +180,7 @@ License: GPL-3.0-or-later Files: dist/icns_generator.sh Copyright: 2024 suyu Emulator Project License: GPL-3.0-or-later + +Files: img/* +Copyright: 2024 suyu Emulator Project +License: CC0-1.0 From 48e86d6e84c0027ccade556e90fef170c7e3d803 Mon Sep 17 00:00:00 2001 From: Lucas Clemente Vella Date: Sat, 30 Mar 2024 15:13:23 +0000 Subject: [PATCH 11/13] Fixes issue #94: setting Vulkan::Headers before Qt6 can do it --- CMakeLists.txt | 126 +++++++++++++++++++++++++------------------------ 1 file changed, 64 insertions(+), 62 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 42e8615e57..9406dd64a1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -396,7 +396,71 @@ function(set_suyu_qt_components) set(SUYU_QT_COMPONENTS ${SUYU_QT_COMPONENTS2} PARENT_SCOPE) endfunction(set_suyu_qt_components) +# find SDL2 exports a bunch of variables that are needed, so its easier to do this outside of the suyu_find_package +if (ENABLE_SDL2) + if (SUYU_USE_BUNDLED_SDL2) + # Detect toolchain and platform + if ((MSVC_VERSION GREATER_EQUAL 1920 AND MSVC_VERSION LESS 1940) AND ARCHITECTURE_x86_64) + set(SDL2_VER "SDL2-2.28.2") + else() + message(FATAL_ERROR "No bundled SDL2 binaries for your toolchain. Disable SUYU_USE_BUNDLED_SDL2 and provide your own.") + endif() + + if (DEFINED SDL2_VER) + download_bundled_external("sdl2/" ${SDL2_VER} SDL2_PREFIX) + endif() + + set(SDL2_FOUND YES) + set(SDL2_INCLUDE_DIR "${SDL2_PREFIX}/include" CACHE PATH "Path to SDL2 headers") + set(SDL2_LIBRARY "${SDL2_PREFIX}/lib/x64/SDL2.lib" CACHE PATH "Path to SDL2 library") + set(SDL2_DLL_DIR "${SDL2_PREFIX}/lib/x64/" CACHE PATH "Path to SDL2.dll") + + add_library(SDL2::SDL2 INTERFACE IMPORTED) + target_link_libraries(SDL2::SDL2 INTERFACE "${SDL2_LIBRARY}") + target_include_directories(SDL2::SDL2 INTERFACE "${SDL2_INCLUDE_DIR}") + elseif (SUYU_USE_EXTERNAL_SDL2) + message(STATUS "Using SDL2 from externals.") + else() + find_package(SDL2 2.26.4 REQUIRED) + endif() +endif() + +# List of all FFmpeg components required +set(FFmpeg_COMPONENTS + avcodec + avfilter + avutil + swscale) + +if (UNIX AND NOT APPLE AND NOT ANDROID) + find_package(PkgConfig REQUIRED) + pkg_check_modules(LIBVA libva) +endif() +if (NOT SUYU_USE_BUNDLED_FFMPEG) + # Use system installed FFmpeg + find_package(FFmpeg 4.3 REQUIRED QUIET COMPONENTS ${FFmpeg_COMPONENTS}) +endif() + +if (WIN32 AND SUYU_CRASH_DUMPS) + set(BREAKPAD_VER "breakpad-c89f9dd") + download_bundled_external("breakpad/" ${BREAKPAD_VER} BREAKPAD_PREFIX) + + set(BREAKPAD_CLIENT_INCLUDE_DIR "${BREAKPAD_PREFIX}/include") + set(BREAKPAD_CLIENT_LIBRARY "${BREAKPAD_PREFIX}/lib/libbreakpad_client.lib") + + add_library(libbreakpad_client INTERFACE IMPORTED) + target_link_libraries(libbreakpad_client INTERFACE "${BREAKPAD_CLIENT_LIBRARY}") + target_include_directories(libbreakpad_client INTERFACE "${BREAKPAD_CLIENT_INCLUDE_DIR}") +endif() + +# Prefer the -pthread flag on Linux. +set(THREADS_PREFER_PTHREAD_FLAG ON) +find_package(Threads REQUIRED) + +add_subdirectory(externals) + # Qt5 requires that we find components, so it doesn't fit our pretty little find package function +# Qt6 sets Vulkan::Headers, so Qt search has to come after externals, so it doesn't get to do it. if(ENABLE_QT) set(QT_VERSION 5.15) # These are used to specify minimum versions @@ -535,67 +599,6 @@ if(ENABLE_QT) endif() -# find SDL2 exports a bunch of variables that are needed, so its easier to do this outside of the suyu_find_package -if (ENABLE_SDL2) - if (SUYU_USE_BUNDLED_SDL2) - # Detect toolchain and platform - if ((MSVC_VERSION GREATER_EQUAL 1920 AND MSVC_VERSION LESS 1940) AND ARCHITECTURE_x86_64) - set(SDL2_VER "SDL2-2.28.2") - else() - message(FATAL_ERROR "No bundled SDL2 binaries for your toolchain. Disable SUYU_USE_BUNDLED_SDL2 and provide your own.") - endif() - - if (DEFINED SDL2_VER) - download_bundled_external("sdl2/" ${SDL2_VER} SDL2_PREFIX) - endif() - - set(SDL2_FOUND YES) - set(SDL2_INCLUDE_DIR "${SDL2_PREFIX}/include" CACHE PATH "Path to SDL2 headers") - set(SDL2_LIBRARY "${SDL2_PREFIX}/lib/x64/SDL2.lib" CACHE PATH "Path to SDL2 library") - set(SDL2_DLL_DIR "${SDL2_PREFIX}/lib/x64/" CACHE PATH "Path to SDL2.dll") - - add_library(SDL2::SDL2 INTERFACE IMPORTED) - target_link_libraries(SDL2::SDL2 INTERFACE "${SDL2_LIBRARY}") - target_include_directories(SDL2::SDL2 INTERFACE "${SDL2_INCLUDE_DIR}") - elseif (SUYU_USE_EXTERNAL_SDL2) - message(STATUS "Using SDL2 from externals.") - else() - find_package(SDL2 2.26.4 REQUIRED) - endif() -endif() - -# List of all FFmpeg components required -set(FFmpeg_COMPONENTS - avcodec - avfilter - avutil - swscale) - -if (UNIX AND NOT APPLE AND NOT ANDROID) - find_package(PkgConfig REQUIRED) - pkg_check_modules(LIBVA libva) -endif() -if (NOT SUYU_USE_BUNDLED_FFMPEG) - # Use system installed FFmpeg - find_package(FFmpeg 4.3 REQUIRED QUIET COMPONENTS ${FFmpeg_COMPONENTS}) -endif() - -if (WIN32 AND SUYU_CRASH_DUMPS) - set(BREAKPAD_VER "breakpad-c89f9dd") - download_bundled_external("breakpad/" ${BREAKPAD_VER} BREAKPAD_PREFIX) - - set(BREAKPAD_CLIENT_INCLUDE_DIR "${BREAKPAD_PREFIX}/include") - set(BREAKPAD_CLIENT_LIBRARY "${BREAKPAD_PREFIX}/lib/libbreakpad_client.lib") - - add_library(libbreakpad_client INTERFACE IMPORTED) - target_link_libraries(libbreakpad_client INTERFACE "${BREAKPAD_CLIENT_LIBRARY}") - target_include_directories(libbreakpad_client INTERFACE "${BREAKPAD_CLIENT_INCLUDE_DIR}") -endif() - -# Prefer the -pthread flag on Linux. -set(THREADS_PREFER_PTHREAD_FLAG ON) -find_package(Threads REQUIRED) - # Platform-specific library requirements # ====================================== @@ -710,7 +713,6 @@ if (SUYU_USE_FASTER_LD AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU") endif() endif() -add_subdirectory(externals) add_subdirectory(src) # Set suyu project or suyu-cmd project as default StartUp Project in Visual Studio depending on whether QT is enabled or not From 39eea71e62535c72eef0b41b35c5305f86df4971 Mon Sep 17 00:00:00 2001 From: Exverge Date: Sun, 24 Mar 2024 19:51:18 -0400 Subject: [PATCH 12/13] fix: resume application when library applets are closed --- .../am/service/library_applet_accessor.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/core/hle/service/am/service/library_applet_accessor.cpp b/src/core/hle/service/am/service/library_applet_accessor.cpp index cda8c3eb87..b7ea464ff2 100644 --- a/src/core/hle/service/am/service/library_applet_accessor.cpp +++ b/src/core/hle/service/am/service/library_applet_accessor.cpp @@ -1,4 +1,5 @@ // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2024 suyu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/am/applet_data_broker.h" @@ -101,6 +102,22 @@ Result ILibraryAppletAccessor::PushInData(SharedPointer storage) { Result ILibraryAppletAccessor::PopOutData(Out> out_storage) { LOG_DEBUG(Service_AM, "called"); + // suyu todo: move library applet fix to another function + // since this function is only called for applets that give a result, + // applets that don't (e.g. info applets in 1st party games) simply freeze + if (auto caller = m_applet->caller_applet.lock(); caller != nullptr) { + caller->SetInteractibleLocked(true); + + caller->lifecycle_manager.SetFocusState(FocusState::InFocus); + caller->lifecycle_manager.UpdateRequestedFocusState(); + + caller->lifecycle_manager.SetResumeNotificationEnabled(true); + caller->lifecycle_manager.RequestResumeNotification(); + caller->UpdateSuspensionStateLocked(true); + } else { + LOG_CRITICAL(Service_AM, "Caller applet pointer is invalid."); + LOG_CRITICAL(Service_AM, "The emulator will freeze!"); + } R_RETURN(m_broker->GetOutData().Pop(out_storage.Get())); } From 7215ac95437dd041fc402faf7daad6f995724f0b Mon Sep 17 00:00:00 2001 From: Belal Ashraf Date: Fri, 29 Mar 2024 13:41:34 +0100 Subject: [PATCH 13/13] Fix NROs crashing and loading infinitely --- src/core/hle/service/am/process_creation.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/hle/service/am/process_creation.cpp b/src/core/hle/service/am/process_creation.cpp index 237151d061..e6e2fad2c6 100644 --- a/src/core/hle/service/am/process_creation.cpp +++ b/src/core/hle/service/am/process_creation.cpp @@ -106,6 +106,7 @@ std::unique_ptr CreateApplicationProcess(std::vector& out_control, out_control = nacp.GetRawBytes(); } else { out_control.resize(sizeof(FileSys::RawNACP)); + memset(out_control.data(), 0, sizeof(u8) * out_control.size()); } auto& storage = system.GetContentProviderUnion();