From 876d7f90b60e3fed88b6ae8eb2720f1f9a02306c Mon Sep 17 00:00:00 2001 From: Kelebek1 Date: Mon, 25 Mar 2024 19:16:08 +0100 Subject: [PATCH 01/15] 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/15] 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/15] 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/15] 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/15] 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/15] 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/15] 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/15] 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/15] 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/15] 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/15] 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/15] 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/15] 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(); From d3f67d1e9c7562026909a07cb53ea0f20c12a500 Mon Sep 17 00:00:00 2001 From: voidanix Date: Sat, 23 Mar 2024 23:25:52 +0100 Subject: [PATCH 14/15] Fix GCC builds with Debug build type When compiling with -DCMAKE_BUILD_TYPE=Debug, GCC would (correctly) fail to compile intrinsics in stb and host1x due to lack of optimizations. Sadly, the compilation error given is bogus and Clang completing the builds without issues does raise some eyebrows. Therefore, force optimizations for the offending files under GCC when creating Debug builds. Signed-off-by: voidanix --- src/common/CMakeLists.txt | 9 +++++++++ src/video_core/CMakeLists.txt | 5 +++++ 2 files changed, 14 insertions(+) diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index e97a415189..1d2e973841 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -240,6 +240,15 @@ if (MSVC) ) else() set_source_files_properties(stb.cpp PROPERTIES COMPILE_OPTIONS "-Wno-implicit-fallthrough;-Wno-missing-declarations;-Wno-missing-field-initializers") + + # Get around GCC failing with intrinsics in Debug + if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_BUILD_TYPE MATCHES "Debug") + set_property( + SOURCE stb.cpp + APPEND + PROPERTY COMPILE_OPTIONS ";-O2" + ) + endif() endif() if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index e5cd0278fa..0afa3d7036 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -425,6 +425,11 @@ else() # VMA set_source_files_properties(vulkan_common/vma.cpp PROPERTIES COMPILE_OPTIONS "-Wno-conversion;-Wno-unused-variable;-Wno-unused-parameter;-Wno-missing-field-initializers") + + # Get around GCC failing with intrinsics in Debug + if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_BUILD_TYPE MATCHES "Debug") + set_source_files_properties(host1x/vic.cpp PROPERTIES COMPILE_OPTIONS "-O2") + endif() endif() if (ARCHITECTURE_x86_64) From 4015b1d397c5beb1968497e1f8ab6238e743c972 Mon Sep 17 00:00:00 2001 From: Lucas Clemente Vella Date: Sun, 31 Mar 2024 13:40:04 +0100 Subject: [PATCH 15/15] Solves warning about GuestMemory having internal linkage. It is very hard to explain why this public interface class was defined in an anonymous namespace inside a header file. --- src/core/guest_memory.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/core/guest_memory.h b/src/core/guest_memory.h index 83292f7023..a753f51a4e 100644 --- a/src/core/guest_memory.h +++ b/src/core/guest_memory.h @@ -31,7 +31,6 @@ enum GuestMemoryFlags : u32 { UnsafeReadCachedWrite = UnsafeReadWrite | Cached, }; -namespace { template class GuestMemory { using iterator = T*; @@ -219,6 +218,5 @@ public: } } }; -} // namespace } // namespace Core::Memory