From 7eaf96eda4f7abb4ac6a992a5f9babbbdf5e934f Mon Sep 17 00:00:00 2001 From: sguo35 Date: Sun, 27 Mar 2022 22:51:46 -0700 Subject: [PATCH] build-macos: fix C++20 compatibility for Clang13.1 Fixes compilation on Clang13.1 by backporting some C++20 features that aren't available in Clang and fixing the CMake config. Note that this doesn't yet properly package the app nor does anything work at this point. jthread, stop_token, and condition_variable_any impls taken from josuttis/jthread and appleCompat.h taken from Citra and modified by me. --- .gitmodules | 3 + CMakeLists.txt | 11 +- externals/CMakeLists.txt | 5 + externals/ffmpeg/CMakeLists.txt | 1 + externals/range-v3 | 1 + src/common/CMakeLists.txt | 4 + src/common/apple_compat/appleCompat.h | 108 ++++ .../apple_compat/condition_variable_any2.hpp | 299 +++++++++ src/common/apple_compat/jthread.hpp | 174 ++++++ src/common/apple_compat/stop_token.hpp | 566 ++++++++++++++++++ src/common/fs/file.cpp | 2 + src/common/fs/fs_util.cpp | 11 + src/common/fs/path_util.cpp | 1 + src/common/logging/backend.cpp | 4 + src/common/thread_worker.h | 8 + src/common/threadsafe_queue.h | 8 + src/core/CMakeLists.txt | 4 + src/core/cpu_manager.h | 3 + src/core/file_sys/content_archive.cpp | 3 + src/core/frontend/applets/controller.h | 1 + src/core/hle/kernel/k_scheduler.cpp | 9 + src/core/hle/kernel/k_slab_heap.h | 17 +- src/core/hle/kernel/k_thread_local_page.h | 3 + src/core/hle/kernel/service_thread.cpp | 7 + src/core/hle/kernel/svc_wrap.h | 4 +- src/core/hle/service/acc/profile_manager.cpp | 3 + .../nvdrv/devices/nvhost_nvdec_common.h | 1 + src/core/network/network.cpp | 3 + src/core/network/network_interface.cpp | 4 + src/input_common/CMakeLists.txt | 4 + src/input_common/drivers/gc_adapter.h | 4 + src/input_common/drivers/mouse.cpp | 4 + src/input_common/drivers/mouse.h | 4 + src/input_common/drivers/tas_input.cpp | 1 + src/shader_recompiler/CMakeLists.txt | 4 + src/shader_recompiler/frontend/ir/opcodes.h | 4 + .../frontend/maxwell/decode.cpp | 10 + .../floating_point_conversion_integer.cpp | 5 +- src/shader_recompiler/object_pool.h | 4 + src/video_core/CMakeLists.txt | 4 + src/video_core/buffer_cache/buffer_base.h | 4 +- src/video_core/gpu_thread.h | 7 + src/video_core/rasterizer_interface.h | 4 + src/video_core/renderer_opengl/gl_device.cpp | 3 + .../renderer_opengl/gl_shader_cache.h | 4 + .../renderer_vulkan/fixed_pipeline_state.cpp | 3 + .../renderer_vulkan/vk_master_semaphore.h | 3 + src/video_core/renderer_vulkan/vk_scheduler.h | 7 + src/video_core/shader_environment.h | 4 + src/video_core/texture_cache/slot_vector.h | 3 + src/video_core/textures/astc.cpp | 3 + src/video_core/transform_feedback.cpp | 3 + .../vulkan_common/vulkan_device.cpp | 3 + .../vulkan_common/vulkan_instance.cpp | 3 + .../vulkan_common/vulkan_memory_allocator.cpp | 3 + src/yuzu/CMakeLists.txt | 1 + src/yuzu/bootmanager.h | 7 + src/yuzu_cmd/CMakeLists.txt | 4 + 58 files changed, 1376 insertions(+), 9 deletions(-) create mode 160000 externals/range-v3 create mode 100644 src/common/apple_compat/appleCompat.h create mode 100644 src/common/apple_compat/condition_variable_any2.hpp create mode 100644 src/common/apple_compat/jthread.hpp create mode 100644 src/common/apple_compat/stop_token.hpp diff --git a/.gitmodules b/.gitmodules index a9cf9a24ad..3d943ff834 100644 --- a/.gitmodules +++ b/.gitmodules @@ -43,3 +43,6 @@ [submodule "externals/ffmpeg/ffmpeg"] path = externals/ffmpeg/ffmpeg url = https://git.ffmpeg.org/ffmpeg.git +[submodule "externals/range-v3"] + path = externals/range-v3 + url = https://github.com/ericniebler/range-v3.git diff --git a/CMakeLists.txt b/CMakeLists.txt index af093113be..da5df2eb45 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,14 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externals/find-module include(DownloadExternals) include(CMakeDependentOption) -project(yuzu) +if (APPLE) + # Hack for M1. Currently fails when compiling for arm64 + set(CMAKE_OSX_ARCHITECTURES "x86_64" CACHE STRING "") + set(CMAKE_CROSSCOMPILING TRUE) + project(yuzu) +else() + project(yuzu) +endif() # Set bundled sdl2/qt as dependent options. # OFF by default, but if ENABLE_SDL2 and MSVC are true then ON @@ -492,7 +499,7 @@ if (TARGET Boost::Boost) add_library(boost ALIAS Boost::Boost) elseif (TARGET Boost::boost) set_target_properties(Boost::boost PROPERTIES IMPORTED_GLOBAL TRUE) - add_library(boost ALIAS Boost::boost src/common/apple_compat/appleCompat.h) + add_library(boost ALIAS Boost::boost) endif() # Ensure libusb is properly configured (based on dolphin libusb include) diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 82e8ef18cf..c801f175c5 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -127,3 +127,8 @@ if (YUZU_USE_BUNDLED_FFMPEG) set(FFmpeg_LIBRARIES "${FFmpeg_LIBRARIES}" PARENT_SCOPE) set(FFmpeg_INCLUDE_DIR "${FFmpeg_INCLUDE_DIR}" PARENT_SCOPE) endif() + +if (APPLE) + add_library(range_v3 INTERFACE) + set_target_properties(range_v3 PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${PROJECT_SOURCE_DIR}/externals/range-v3/include) +endif() diff --git a/externals/ffmpeg/CMakeLists.txt b/externals/ffmpeg/CMakeLists.txt index dcc9788273..f41c99cb17 100644 --- a/externals/ffmpeg/CMakeLists.txt +++ b/externals/ffmpeg/CMakeLists.txt @@ -137,6 +137,7 @@ if (NOT WIN32) --disable-network --disable-postproc --disable-swresample + --disable-iconv --enable-decoder=h264 --enable-decoder=vp8 --enable-decoder=vp9 diff --git a/externals/range-v3 b/externals/range-v3 new file mode 160000 index 0000000000..d800a03213 --- /dev/null +++ b/externals/range-v3 @@ -0,0 +1 @@ +Subproject commit d800a032132512a54c291ce55a2a43e0460591c7 diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index adf70eb8ba..4f0957eebd 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -186,3 +186,7 @@ if (MSVC) else() target_link_libraries(common PRIVATE zstd) endif() + +if (APPLE) + target_link_libraries(common PUBLIC range_v3) +endif() diff --git a/src/common/apple_compat/appleCompat.h b/src/common/apple_compat/appleCompat.h new file mode 100644 index 0000000000..da1029a9dd --- /dev/null +++ b/src/common/apple_compat/appleCompat.h @@ -0,0 +1,108 @@ +// Copyright 2018 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +// Very hacky :) +#ifdef __APPLE__ + +#include +#include + +#include "common/apple_compat/condition_variable_any2.hpp" +#include "common/apple_compat/jthread.hpp" + +#include + +// use an external library range-v3 as std's one +namespace std::ranges { +using namespace ::ranges; +} + +// adaptation of new features in c++20 +namespace std { +constexpr unsigned long long min(const unsigned long long a, const unsigned long long b) { + return (b < a) ? b : a; +} + +template +/* requires !std::custom_same_as && !std::custom_same_as && + !std::custom_same_as && !std::custom_same_as && + !std::custom_same_as && !std::custom_same_as*/ +constexpr bool has_single_bit(T x) noexcept { + return x != 0 && (x & (x - 1)) == 0; +} + +using condition_variable_any_apple = std::condition_variable_any2; +} // namespace std + +// fix sorting with constexpr funcs +// thanks to https://tristanbrindle.com/posts/a-more-useful-compile-time-quicksort +namespace constexprSort { +namespace cstd { +template +constexpr RAIt next(RAIt it, typename std::iterator_traits::difference_type n = 1) { + return it + n; +} + +template +constexpr auto distance(RAIt first, RAIt last) { + return last - first; +} + +template +constexpr void iter_swap(ForwardIt1 a, ForwardIt2 b) { + auto temp = std::move(*a); + *a = std::move(*b); + *b = std::move(temp); +} + +template +constexpr InputIt find_if_not(InputIt first, InputIt last, UnaryPredicate q) { + for (; first != last; ++first) { + if (!q(*first)) { + return first; + } + } + return last; +} + +template +constexpr ForwardIt partition(ForwardIt first, ForwardIt last, UnaryPredicate p) { + first = cstd::find_if_not(first, last, p); + if (first == last) + return first; + + for (ForwardIt i = cstd::next(first); i != last; ++i) { + if (p(*i)) { + cstd::iter_swap(i, first); + ++first; + } + } + return first; +} + +} // namespace cstd + +template > +constexpr void quick_sort(RAIt first, RAIt last, Compare cmp = Compare{}) { + auto const N = cstd::distance(first, last); + if (N <= 1) + return; + auto const pivot = *cstd::next(first, N / 2); + auto const middle1 = + cstd::partition(first, last, [=](auto const& elem) { return cmp(elem, pivot); }); + auto const middle2 = + cstd::partition(middle1, last, [=](auto const& elem) { return !cmp(pivot, elem); }); + quick_sort(first, middle1, cmp); // assert(std::is_sorted(first, middle1, cmp)); + quick_sort(middle2, last, cmp); // assert(std::is_sorted(middle2, last, cmp)); +} + +template +constexpr auto sort(Range&& range) { + quick_sort(std::begin(range), std::end(range)); + return range; +} +} // namespace constexprSort +#endif \ No newline at end of file diff --git a/src/common/apple_compat/condition_variable_any2.hpp b/src/common/apple_compat/condition_variable_any2.hpp new file mode 100644 index 0000000000..ce52a12687 --- /dev/null +++ b/src/common/apple_compat/condition_variable_any2.hpp @@ -0,0 +1,299 @@ +// extended standard condition_variable to deal with +// interrupt tokens and jthread +// ----------------------------------------------------- +#ifndef CONDITION_VARIABLE2_HPP +#define CONDITION_VARIABLE2_HPP + +//***************************************************************************** +// forward declarations are in separate header due to cyclic type dependencies: +//***************************************************************************** +#include "common/apple_compat/stop_token.hpp" +#include +#include + +namespace std { + + +//***************************************** +//* class condition_variable_any2 +//* - joining std::thread with interrupt support +//***************************************** + class condition_variable_any2 + { + template + struct unlock_guard{ + unlock_guard(Lockable& mtx_): + mtx(mtx_){ + mtx.unlock(); + } + ~unlock_guard(){ + mtx.lock(); + } + unlock_guard(unlock_guard const&)=delete; + unlock_guard(unlock_guard&&)=delete; + unlock_guard& operator=(unlock_guard const&)=delete; + unlock_guard& operator=(unlock_guard&&)=delete; + + private: + Lockable& mtx; + }; + + struct cv_internals{ + std::mutex m = {}; + std::condition_variable cv = {}; + + void notify_all(){ + std::lock_guard guard(m); + cv.notify_all(); + } + void notify_one(){ + std::lock_guard guard(m); + cv.notify_one(); + } + }; + + public: + //***************************************** + //* standardized API for condition_variable_any: + //***************************************** + + condition_variable_any2() + : internals{std::make_shared()} { + } + ~condition_variable_any2() { + } + condition_variable_any2(const condition_variable_any2&) = delete; + condition_variable_any2& operator=(const condition_variable_any2&) = delete; + + void notify_one() noexcept { + internals->notify_one(); + } + void notify_all() noexcept { + internals->notify_all(); + } + + // wait() + + template + void wait(Lockable& lock) { + auto local_internals=internals; + std::unique_lock first_internal_lock(local_internals->m); + unlock_guard unlocker(lock); + std::unique_lock second_internal_lock(std::move(first_internal_lock)); + local_internals->cv.wait(second_internal_lock); + } + + template + void wait(Lockable& lock, Predicate pred) { + // have to manually implement the loop so that the user-provided lock is reacquired before calling pred(). + // (otherwise the test_cvrace_pred test case fails) + auto local_internals=internals; + while (!pred()) { + std::unique_lock first_internal_lock(local_internals->m); + unlock_guard unlocker(lock); + std::unique_lock second_internal_lock(std::move(first_internal_lock)); + local_internals->cv.wait(second_internal_lock); + } + } + + // wait_until() + + template + cv_status wait_until(Lockable& lock, + const chrono::time_point& abs_time) { + auto local_internals=internals; + std::unique_lock first_internal_lock(local_internals->m); + unlock_guard unlocker(lock); + std::unique_lock second_internal_lock(std::move(first_internal_lock)); + return local_internals->cv.wait_until(second_internal_lock, abs_time); + } + + template + bool wait_until(Lockable& lock, + const chrono::time_point& abs_time, + Predicate pred) { + // have to manually implement the loop so that the user-provided lock is reacquired before calling pred(). + // (otherwise the test_cvrace_pred test case fails) + auto local_internals=internals; + while (!pred()) { + bool shouldStop; + { + std::unique_lock first_internal_lock(local_internals->m); + unlock_guard unlocker(lock); + std::unique_lock second_internal_lock(std::move(first_internal_lock)); + shouldStop = (local_internals->cv.wait_until(second_internal_lock, abs_time) == std::cv_status::timeout); + } + if (shouldStop) { + return pred(); + } + } + return true; + } + + // wait_for() + + template + cv_status wait_for(Lockable& lock, + const chrono::duration& rel_time) { + return wait_until(lock, std::chrono::steady_clock::now() + rel_time); + } + + template + bool wait_for(Lockable& lock, + const chrono::duration& rel_time, + Predicate pred) { + return wait_until(lock, std::chrono::steady_clock::now() + rel_time, std::move(pred)); + } + + //***************************************** + //* supplementary API: + //***************************************** + + // x.6.2.1 dealing with interrupts: + + // return: + // - true if pred() yields true + // - false otherwise (i.e. on interrupt) + template + bool wait(Lockable& lock, + stop_token stoken, + Predicate pred); + + // return: + // - true if pred() yields true + // - false otherwise (i.e. on timeout or interrupt) + template + bool wait_until(Lockable& lock, + stop_token stoken, + const chrono::time_point& abs_time, + Predicate pred); + // return: + // - true if pred() yields true + // - false otherwise (i.e. on timeout or interrupt) + template + bool wait_for(Lockable& lock, + stop_token stoken, + const chrono::duration& rel_time, + Predicate pred); + + //***************************************** + //* implementation: + //***************************************** + + private: + //*** API for the starting thread: + std::shared_ptr internals; + // NOTE (as Howard Hinnant pointed out): + // std::~condition_variable_any() says: + // Requires: There shall be no thread blocked on *this. [Note: That is, all threads shall have been notified; + // they may subsequently block on the lock specified in the wait. + // This relaxes the usual rules, which would have required all wait calls to happen before destruction. + // Only the notification to unblock the wait needs to happen before destruction. + // The user should take care to ensure that no threads wait on *this once the destructor has been started, + // especially when the waiting threads are calling the wait functions in a loop or using the overloads of + // wait, wait_for, or wait_until that take a predicate. ] + // That big long note means ~condition_variable_any() can execute before a signaled thread returns from a wait. + // If this happens with condition_variable_any2, that waiting thread will attempt to lock the destructed mutex mut. + // To fix this, there must be shared ownership of the data member mut between the condition_variable_any object + // and the member functions wait (wait_for, etc.). + // (libc++'s implementation gets this right: https://github.com/llvm-mirror/libcxx/blob/master/include/condition_variable + // It holds the data member mutex with a shared_ptr instead of mutex directly, and the wait functions create + // a local shared_ptr copy on entry so that if *this destructs out from under the thread executing the wait function, + // the mutex stays alive until the wait function returns.) + }; + + + +//***************************************************************************** +//* implementation of class condition_variable_any2 +//***************************************************************************** + +// wait_until(): wait with interrupt handling +// - returns on interrupt +// return value: +// - true if pred() yields true +// - false otherwise (i.e. on interrupt) + template + inline bool condition_variable_any2::wait(Lockable& lock, + stop_token stoken, + Predicate pred) + { + if (stoken.stop_requested()) { + return pred(); + } + auto local_internals=internals; + stop_callback cb(stoken, [&local_internals] { local_internals->notify_all(); }); + while (!pred()) { + std::unique_lock first_internal_lock(local_internals->m); + if (stoken.stop_requested()) { + // pred() has already evaluated to 'false' since we last a acquired 'lock' + return false; + } + unlock_guard unlocker(lock); + std::unique_lock second_internal_lock(std::move(first_internal_lock)); + local_internals->cv.wait(second_internal_lock); + } + + return true; + } + +// wait_until(): timed wait with interrupt handling +// - returns on interrupt +// return: +// - true if pred() yields true +// - false otherwise (i.e. on timeout or interrupt) + template + inline bool condition_variable_any2::wait_until(Lockable& lock, + stop_token stoken, + const chrono::time_point& abs_time, + Predicate pred) + { + if (stoken.stop_requested()) { + return pred(); + } + // have to manually implement the loop so that the user-provided lock is reacquired before calling pred(). + // (otherwise the test_cvrace_pred test case fails) + auto local_internals=internals; + stop_callback cb(stoken, [&local_internals] { local_internals->notify_all(); }); + while (!pred()) { + bool shouldStop; + { + std::unique_lock first_internal_lock(local_internals->m); + if (stoken.stop_requested()) { + // pred() has already evaluated to 'false' since we last acquired 'lock'. + return false; + } + unlock_guard unlocker(lock); + std::unique_lock second_internal_lock(std::move(first_internal_lock)); + const auto status = local_internals->cv.wait_until(second_internal_lock, abs_time); + shouldStop = (status == std::cv_status::timeout) || stoken.stop_requested(); + } + if (shouldStop) { + return pred(); + } + } + return true; + } + +// wait_for(): timed wait with interrupt handling +// - returns on interrupt +// return: +// - true if pred() yields true +// - false otherwise (i.e. on timeout or interrupt) + template + inline bool condition_variable_any2::wait_for(Lockable& lock, + stop_token stoken, + const chrono::duration& rel_time, + Predicate pred) + { + auto abs_time = std::chrono::steady_clock::now() + rel_time; + return wait_until(lock, + std::move(stoken), + abs_time, + std::move(pred)); + } + + +} // std + +#endif // CONDITION_VARIABLE2_HPP \ No newline at end of file diff --git a/src/common/apple_compat/jthread.hpp b/src/common/apple_compat/jthread.hpp new file mode 100644 index 0000000000..460d77ae8f --- /dev/null +++ b/src/common/apple_compat/jthread.hpp @@ -0,0 +1,174 @@ +// ----------------------------------------------------- +// cooperative interruptable and joining thread: +// ----------------------------------------------------- +#ifndef JTHREAD_HPP +#define JTHREAD_HPP + +#include "common/apple_compat/stop_token.hpp" +#include +#include +#include +#include // for invoke() +#include // for debugging output + +namespace std { + +//***************************************** +//* class jthread +//* - joining std::thread with signaling stop/end support +//***************************************** + class jthread + { + public: + //***************************************** + //* standardized API: + //***************************************** + // - cover full API of std::thread + // to be able to switch from std::thread to std::jthread + + // types are those from std::thread: + using id = ::std::thread::id; + using native_handle_type = ::std::thread::native_handle_type; + + // construct/copy/destroy: + jthread() noexcept; + //template explicit jthread(F&& f, Args&&... args); + // THE constructor that starts the thread: + // - NOTE: does SFINAE out copy constructor semantics + template , jthread>>> + explicit jthread(Callable&& cb, Args&&... args); + ~jthread(); + + jthread(const jthread&) = delete; + jthread(jthread&&) noexcept = default; + jthread& operator=(const jthread&) = delete; + jthread& operator=(jthread&&) noexcept; + + // members: + void swap(jthread&) noexcept; + bool joinable() const noexcept; + void join(); + void detach(); + + id get_id() const noexcept; + native_handle_type native_handle(); + + // static members: + static unsigned hardware_concurrency() noexcept { + return ::std::thread::hardware_concurrency(); + }; + + //***************************************** + // - supplementary API: + // - for the calling thread: + [[nodiscard]] stop_source get_stop_source() noexcept; + [[nodiscard]] stop_token get_stop_token() const noexcept; + bool request_stop() noexcept { + return get_stop_source().request_stop(); + } + + + //***************************************** + //* implementation: + //***************************************** + + private: + //*** API for the starting thread: + stop_source _stopSource; // stop_source for started thread + ::std::thread _thread{}; // started thread (if any) + }; + + +//********************************************************************** + +//***************************************** +//* implementation of class jthread +//***************************************** + +// default constructor: + inline jthread::jthread() noexcept + : _stopSource{nostopstate} { +} + +// THE constructor that starts the thread: +// - NOTE: declaration does SFINAE out copy constructor semantics +template +inline jthread::jthread(Callable&& cb, Args&&... args) + : _stopSource{}, // initialize stop_source + _thread{[] (stop_token st, auto&& cb, auto&&... args) { // called lambda in the thread + // perform tasks of the thread: + if constexpr(std::is_invocable_v) { + // pass the stop_token as first argument to the started thread: + ::std::invoke(::std::forward(cb), + std::move(st), + ::std::forward(args)...); + } + else { + // started thread does not expect a stop token: + ::std::invoke(::std::forward(cb), + ::std::forward(args)...); + } + }, + _stopSource.get_token(), // not captured due to possible races if immediately set + ::std::forward(cb), // pass callable + ::std::forward(args)... // pass arguments for callable + } +{ +} + +// move assignment operator: +inline jthread& jthread::operator=(jthread&& t) noexcept { +if (joinable()) { // if not joined/detached, signal stop and wait for end: +request_stop(); +join(); +} + +_thread = std::move(t._thread); +_stopSource = std::move(t._stopSource); +return *this; +} + +// destructor: +inline jthread::~jthread() { + if (joinable()) { // if not joined/detached, signal stop and wait for end: + request_stop(); + join(); + } +} + + +// others: +inline bool jthread::joinable() const noexcept { +return _thread.joinable(); +} +inline void jthread::join() { + _thread.join(); +} +inline void jthread::detach() { + _thread.detach(); +} +inline typename jthread::id jthread::get_id() const noexcept { +return _thread.get_id(); +} +inline typename jthread::native_handle_type jthread::native_handle() { + return _thread.native_handle(); +} + +inline stop_source jthread::get_stop_source() noexcept { +return _stopSource; +} +inline stop_token jthread::get_stop_token() const noexcept { +return _stopSource.get_token(); +} + +inline void jthread::swap(jthread& t) noexcept { +std::swap(_stopSource, t._stopSource); +std::swap(_thread, t._thread); +} + + +} // std + +#endif // JTHREAD_HPP \ No newline at end of file diff --git a/src/common/apple_compat/stop_token.hpp b/src/common/apple_compat/stop_token.hpp new file mode 100644 index 0000000000..6f48b23dde --- /dev/null +++ b/src/common/apple_compat/stop_token.hpp @@ -0,0 +1,566 @@ +#pragma once +// header + +#include +#include +#include +#include +#ifdef SAFE +#include +#endif + +#if defined(__x86_64__) || defined(_M_X64) +#include +#endif + +namespace std { + inline void __spin_yield() noexcept { + // TODO: Platform-specific code here +#if defined(__x86_64__) || defined(_M_X64) + _mm_pause(); +#endif +} + + +//----------------------------------------------- +// internal types for shared stop state +//----------------------------------------------- + +struct __stop_callback_base { + void(*__callback_)(__stop_callback_base*) = nullptr; + + __stop_callback_base* __next_ = nullptr; + __stop_callback_base** __prev_ = nullptr; + bool* __isRemoved_ = nullptr; + std::atomic __callbackFinishedExecuting_{false}; + + void __execute() noexcept { + __callback_(this); + } + +protected: + // it shall only by us who deletes this + // (workaround for virtual __execute() and destructor) + ~__stop_callback_base() = default; +}; + +struct __stop_state { +public: + void __add_token_reference() noexcept { + __state_.fetch_add(__token_ref_increment, std::memory_order_relaxed); + } + + void __remove_token_reference() noexcept { + auto __oldState = + __state_.fetch_sub(__token_ref_increment, std::memory_order_acq_rel); + if (__oldState < (__token_ref_increment + __source_ref_increment)) { + delete this; + } + } + + void __add_source_reference() noexcept { + __state_.fetch_add(__source_ref_increment, std::memory_order_relaxed); + } + + void __remove_source_reference() noexcept { + auto __oldState = + __state_.fetch_sub(__source_ref_increment, std::memory_order_acq_rel); + if (__oldState < (__token_ref_increment + __source_ref_increment)) { + delete this; + } + } + + bool __request_stop() noexcept { + + if (!__try_lock_and_signal_until_signalled()) { + // Stop has already been requested. + return false; + } + + // Set the 'stop_requested' signal and acquired the lock. + + __signallingThread_ = std::this_thread::get_id(); + + while (__head_ != nullptr) { + // Dequeue the head of the queue + auto* __cb = __head_; + __head_ = __cb->__next_; + const bool anyMore = __head_ != nullptr; + if (anyMore) { + __head_->__prev_ = &__head_; + } + // Mark this item as removed from the list. + __cb->__prev_ = nullptr; + + // Don't hold lock while executing callback + // so we don't block other threads from deregistering callbacks. + __unlock(); + + // TRICKY: Need to store a flag on the stack here that the callback + // can use to signal that the destructor was executed inline + // during the call. If the destructor was executed inline then + // it's not safe to dereference __cb after __execute() returns. + // If the destructor runs on some other thread then the other + // thread will block waiting for this thread to signal that the + // callback has finished executing. + bool __isRemoved = false; + __cb->__isRemoved_ = &__isRemoved; + + __cb->__execute(); + + if (!__isRemoved) { + __cb->__isRemoved_ = nullptr; + __cb->__callbackFinishedExecuting_.store( + true, std::memory_order_release); + } + + if (!anyMore) { + // This was the last item in the queue when we dequeued it. + // No more items should be added to the queue after we have + // marked the state as interrupted, only removed from the queue. + // Avoid acquring/releasing the lock in this case. + return true; + } + + __lock(); + } + + __unlock(); + + return true; + } + + bool __is_stop_requested() noexcept { + return __is_stop_requested(__state_.load(std::memory_order_acquire)); + } + + bool __is_stop_requestable() noexcept { + return __is_stop_requestable(__state_.load(std::memory_order_acquire)); + } + + bool __try_add_callback( + __stop_callback_base* __cb, + bool __incrementRefCountIfSuccessful) noexcept { + std::uint64_t __oldState; + goto __load_state; + do { + goto __check_state; + do { + __spin_yield(); + __load_state: + __oldState = __state_.load(std::memory_order_acquire); + __check_state: + if (__is_stop_requested(__oldState)) { + __cb->__execute(); + return false; + } else if (!__is_stop_requestable(__oldState)) { + return false; + } + } while (__is_locked(__oldState)); + } while (!__state_.compare_exchange_weak( + __oldState, __oldState | __locked_flag, std::memory_order_acquire)); + + // Push callback onto callback list. + __cb->__next_ = __head_; + if (__cb->__next_ != nullptr) { + __cb->__next_->__prev_ = &__cb->__next_; + } + __cb->__prev_ = &__head_; + __head_ = __cb; + + if (__incrementRefCountIfSuccessful) { + __unlock_and_increment_token_ref_count(); + } else { + __unlock(); + } + + // Successfully added the callback. + return true; + } + + void __remove_callback(__stop_callback_base* __cb) noexcept { + __lock(); + + if (__cb->__prev_ != nullptr) { + // Still registered, not yet executed + // Just remove from the list. + *__cb->__prev_ = __cb->__next_; + if (__cb->__next_ != nullptr) { + __cb->__next_->__prev_ = __cb->__prev_; + } + + __unlock_and_decrement_token_ref_count(); + + return; + } + + __unlock(); + + // Callback has either already executed or is executing + // concurrently on another thread. + + if (__signallingThread_ == std::this_thread::get_id()) { + // Callback executed on this thread or is still currently executing + // and is deregistering itself from within the callback. + if (__cb->__isRemoved_ != nullptr) { + // Currently inside the callback, let the __request_stop() method + // know the object is about to be destructed and that it should + // not try to access the object when the callback returns. + *__cb->__isRemoved_ = true; + } + } else { + // Callback is currently executing on another thread, + // block until it finishes executing. + while ( + !__cb->__callbackFinishedExecuting_.load(std::memory_order_acquire)) { + __spin_yield(); + } + } + + __remove_token_reference(); + } + +private: + static bool __is_locked(std::uint64_t __state) noexcept { + return (__state & __locked_flag) != 0; + } + + static bool __is_stop_requested(std::uint64_t __state) noexcept { + return (__state & __stop_requested_flag) != 0; + } + + static bool __is_stop_requestable(std::uint64_t __state) noexcept { + // Interruptible if it has already been interrupted or if there are + // still interrupt_source instances in existence. + return __is_stop_requested(__state) || (__state >= __source_ref_increment); + } + + bool __try_lock_and_signal_until_signalled() noexcept { + std::uint64_t __oldState = __state_.load(std::memory_order_acquire); + do { + if (__is_stop_requested(__oldState)) + return false; + while (__is_locked(__oldState)) { + __spin_yield(); + __oldState = __state_.load(std::memory_order_acquire); + if (__is_stop_requested(__oldState)) + return false; + } + } while (!__state_.compare_exchange_weak( + __oldState, + __oldState | __stop_requested_flag | __locked_flag, + std::memory_order_acq_rel, + std::memory_order_acquire)); + return true; + } + + void __lock() noexcept { + auto __oldState = __state_.load(std::memory_order_relaxed); + do { + while (__is_locked(__oldState)) { + __spin_yield(); + __oldState = __state_.load(std::memory_order_relaxed); + } + } while (!__state_.compare_exchange_weak( + __oldState, + __oldState | __locked_flag, + std::memory_order_acquire, + std::memory_order_relaxed)); + } + + void __unlock() noexcept { + __state_.fetch_sub(__locked_flag, std::memory_order_release); + } + + void __unlock_and_increment_token_ref_count() noexcept { + __state_.fetch_sub( + __locked_flag - __token_ref_increment, std::memory_order_release); + } + + void __unlock_and_decrement_token_ref_count() noexcept { + auto __oldState = __state_.fetch_sub( + __locked_flag + __token_ref_increment, std::memory_order_acq_rel); + // Check if new state is less than __token_ref_increment which would + // indicate that this was the last reference. + if (__oldState < + (__locked_flag + __token_ref_increment + __token_ref_increment)) { + delete this; + } + } + + static constexpr std::uint64_t __stop_requested_flag = 1u; + static constexpr std::uint64_t __locked_flag = 2u; + static constexpr std::uint64_t __token_ref_increment = 4u; + static constexpr std::uint64_t __source_ref_increment = + static_cast(1u) << 33u; + + // bit 0 - stop-requested + // bit 1 - locked + // bits 2-32 - token ref count (31 bits) + // bits 33-63 - source ref count (31 bits) + std::atomic __state_{__source_ref_increment}; + __stop_callback_base* __head_ = nullptr; + std::thread::id __signallingThread_{}; +}; + + +//----------------------------------------------- +// forward declarations +//----------------------------------------------- + +class stop_source; +template +class stop_callback; + +// std::nostopstate +// - to initialize a stop_source without shared stop state +struct nostopstate_t { explicit nostopstate_t() = default; }; +inline constexpr nostopstate_t nostopstate{}; + + +//----------------------------------------------- +// stop_token +//----------------------------------------------- + +class stop_token { +public: + // construct: + // - TODO: explicit? + stop_token() noexcept + : __state_(nullptr) { + } + + // copy/move/assign/destroy: + stop_token(const stop_token& __it) noexcept + : __state_(__it.__state_) { + if (__state_ != nullptr) { + __state_->__add_token_reference(); + } + } + + stop_token(stop_token&& __it) noexcept + : __state_(std::exchange(__it.__state_, nullptr)) { + } + + ~stop_token() { + if (__state_ != nullptr) { + __state_->__remove_token_reference(); + } + } + + stop_token& operator=(const stop_token& __it) noexcept { + if (__state_ != __it.__state_) { + stop_token __tmp{__it}; + swap(__tmp); + } + return *this; + } + + stop_token& operator=(stop_token&& __it) noexcept { + stop_token __tmp{std::move(__it)}; + swap(__tmp); + return *this; + } + + void swap(stop_token& __it) noexcept { + std::swap(__state_, __it.__state_); + } + + // stop handling: + [[nodiscard]] bool stop_requested() const noexcept { + return __state_ != nullptr && __state_->__is_stop_requested(); + } + + [[nodiscard]] bool stop_possible() const noexcept { + return __state_ != nullptr && __state_->__is_stop_requestable(); + } + + [[nodiscard]] friend bool operator==( + const stop_token& __a, + const stop_token& __b) noexcept { + return __a.__state_ == __b.__state_; + } + [[nodiscard]] friend bool operator!=( + const stop_token& __a, + const stop_token& __b) noexcept { + return __a.__state_ != __b.__state_; + } + +private: + friend class stop_source; + template + friend class stop_callback; + + explicit stop_token(__stop_state* __state) noexcept : __state_(__state) { + if (__state_ != nullptr) { + __state_->__add_token_reference(); + } + } + + __stop_state* __state_; +}; + + +//----------------------------------------------- +// stop_source +//----------------------------------------------- + +class stop_source { +public: + stop_source() : __state_(new __stop_state()) {} + + explicit stop_source(nostopstate_t) noexcept : __state_(nullptr) {} + + ~stop_source() { + if (__state_ != nullptr) { + __state_->__remove_source_reference(); + } + } + + stop_source(const stop_source& __other) noexcept + : __state_(__other.__state_) { + if (__state_ != nullptr) { + __state_->__add_source_reference(); + } + } + + stop_source(stop_source&& __other) noexcept + : __state_(std::exchange(__other.__state_, nullptr)) {} + + stop_source& operator=(stop_source&& __other) noexcept { + stop_source __tmp{std::move(__other)}; + swap(__tmp); + return *this; + } + + stop_source& operator=(const stop_source& __other) noexcept { + if (__state_ != __other.__state_) { + stop_source __tmp{__other}; + swap(__tmp); + } + return *this; + } + + [[nodiscard]] bool stop_requested() const noexcept { + return __state_ != nullptr && __state_->__is_stop_requested(); + } + + [[nodiscard]] bool stop_possible() const noexcept { + return __state_ != nullptr; + } + + bool request_stop() noexcept { + if (__state_ != nullptr) { + return __state_->__request_stop(); + } + return false; + } + + [[nodiscard]] stop_token get_token() const noexcept { + return stop_token{__state_}; + } + + void swap(stop_source& __other) noexcept { + std::swap(__state_, __other.__state_); + } + + [[nodiscard]] friend bool operator==( + const stop_source& __a, + const stop_source& __b) noexcept { + return __a.__state_ == __b.__state_; + } + [[nodiscard]] friend bool operator!=( + const stop_source& __a, + const stop_source& __b) noexcept { + return __a.__state_ != __b.__state_; + } + +private: + __stop_state* __state_; +}; + + +//----------------------------------------------- +// stop_callback +//----------------------------------------------- + +template +// requires Destructible<_Callback> && Invocable<_Callback> +class [[nodiscard]] stop_callback : private __stop_callback_base { +public: + using callback_type = _Callback; + + template < + typename _CB, + std::enable_if_t, int> = 0> + // requires Constructible + explicit stop_callback(const stop_token& __token, _CB&& __cb) noexcept( + std::is_nothrow_constructible_v<_Callback, _CB>) + : __stop_callback_base{[](__stop_callback_base *__that) noexcept { + static_cast(__that)->__execute(); + }}, + __state_(nullptr), + __cb_(static_cast<_CB&&>(__cb)) { + if (__token.__state_ != nullptr && + __token.__state_->__try_add_callback(this, true)) { + __state_ = __token.__state_; + } + } + + template < + typename _CB, + std::enable_if_t, int> = 0> + // requires Constructible + explicit stop_callback(stop_token&& __token, _CB&& __cb) noexcept( + std::is_nothrow_constructible_v<_Callback, _CB>) + : __stop_callback_base{[](__stop_callback_base *__that) noexcept { + static_cast(__that)->__execute(); + }}, + __state_(nullptr), + __cb_(static_cast<_CB&&>(__cb)) { + if (__token.__state_ != nullptr && + __token.__state_->__try_add_callback(this, false)) { + __state_ = std::exchange(__token.__state_, nullptr); + } + } + + ~stop_callback() { +#ifdef SAFE + if (__inExecute_.load()) { + std::cerr << "*** OOPS: ~stop_callback() while callback executed\n"; + } +#endif + if (__state_ != nullptr) { + __state_->__remove_callback(this); + } + } + + stop_callback& operator=(const stop_callback&) = delete; + stop_callback& operator=(stop_callback&&) = delete; + stop_callback(const stop_callback&) = delete; + stop_callback(stop_callback&&) = delete; + +private: + void __execute() noexcept { + // Executed in a noexcept context + // If it throws then we call std::terminate(). +#ifdef SAFE + __inExecute_.store(true); + __cb_(); + __inExecute_.store(false); +#else + __cb_(); +#endif + } + + __stop_state* __state_; + _Callback __cb_; +#ifdef SAFE + std::atomic __inExecute_{false}; +#endif +}; + +template +stop_callback(stop_token, _Callback) -> stop_callback<_Callback>; + +} // namespace std \ No newline at end of file diff --git a/src/common/fs/file.cpp b/src/common/fs/file.cpp index 5d71275ef2..03dab240ca 100644 --- a/src/common/fs/file.cpp +++ b/src/common/fs/file.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include + #include "common/fs/file.h" #include "common/fs/fs.h" #include "common/logging/log.h" diff --git a/src/common/fs/fs_util.cpp b/src/common/fs/fs_util.cpp index 0068112e63..e161407f06 100644 --- a/src/common/fs/fs_util.cpp +++ b/src/common/fs/fs_util.cpp @@ -4,6 +4,9 @@ #include +#ifdef __APPLE__ +#include "common/apple_compat/appleCompat.h" +#endif #include "common/fs/fs_util.h" namespace Common::FS { @@ -13,7 +16,11 @@ std::u8string ToU8String(std::string_view utf8_string) { } std::u8string BufferToU8String(std::span buffer) { +#ifdef __APPLE__ + return std::u8string{buffer.begin(), ranges::find(buffer, u8{0})}; +#else return std::u8string{buffer.begin(), std::ranges::find(buffer, u8{0})}; +#endif } std::u8string_view BufferToU8StringView(std::span buffer) { @@ -25,7 +32,11 @@ std::string ToUTF8String(std::u8string_view u8_string) { } std::string BufferToUTF8String(std::span buffer) { +#ifdef __APPLE__ + return std::string{buffer.begin(), ranges::find(buffer, u8{0})}; +#else return std::string{buffer.begin(), std::ranges::find(buffer, u8{0})}; +#endif } std::string_view BufferToUTF8StringView(std::span buffer) { diff --git a/src/common/fs/path_util.cpp b/src/common/fs/path_util.cpp index 1bcb897b57..3e014b84c5 100644 --- a/src/common/fs/path_util.cpp +++ b/src/common/fs/path_util.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include +#include #include #include "common/fs/fs.h" diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index b3793106da..7650997862 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -5,7 +5,11 @@ #include #include #include +#ifdef __APPLE__ +#include "common/apple_compat/appleCompat.h" +#else #include +#endif #include #include diff --git a/src/common/thread_worker.h b/src/common/thread_worker.h index cd0017726f..f8c4a81dbf 100644 --- a/src/common/thread_worker.h +++ b/src/common/thread_worker.h @@ -8,7 +8,11 @@ #include #include #include +#ifdef __APPLE__ +#include "common/apple_compat/appleCompat.h" +#else #include +#endif #include #include #include @@ -102,7 +106,11 @@ public: private: std::queue requests; std::mutex queue_mutex; +#ifdef __APPLE__ + std::condition_variable_any_apple condition; +#else std::condition_variable_any condition; +#endif std::condition_variable wait_condition; std::atomic work_scheduled{}; std::atomic work_done{}; diff --git a/src/common/threadsafe_queue.h b/src/common/threadsafe_queue.h index 2c8c2b90e9..71f08417d8 100644 --- a/src/common/threadsafe_queue.h +++ b/src/common/threadsafe_queue.h @@ -8,6 +8,9 @@ // single reader, single writer queue #include +#ifdef __APPLE__ +#include "common/apple_compat/appleCompat.h" +#endif #include #include #include @@ -136,7 +139,12 @@ private: ElementPtr* read_ptr; std::atomic_size_t size{0}; std::mutex cv_mutex; +#ifdef __APPLE__ + std::conditional_t + cv; +#else std::conditional_t cv; +#endif }; // a simple thread-safe, diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 6536d05440..dcc3ac8c35 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -762,3 +762,7 @@ if (ARCHITECTURE_x86_64) ) target_link_libraries(core PRIVATE dynarmic) endif() + +if (APPLE) + target_link_libraries(core PUBLIC range_v3) +endif() diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h index 9d92d4af00..ab8f27fd53 100644 --- a/src/core/cpu_manager.h +++ b/src/core/cpu_manager.h @@ -10,6 +10,9 @@ #include #include +#ifdef __APPLE__ +#include "common/apple_compat/appleCompat.h" +#endif #include "common/fiber.h" #include "common/thread.h" #include "core/hardware_properties.h" diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index 7019a7a684..78b2629109 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp @@ -7,6 +7,9 @@ #include #include +#ifdef __APPLE__ +#include "common/apple_compat/appleCompat.h" +#endif #include "common/logging/log.h" #include "core/crypto/aes_util.h" #include "core/crypto/ctr_encryption_layer.h" diff --git a/src/core/frontend/applets/controller.h b/src/core/frontend/applets/controller.h index 014bc89011..6e293c5798 100644 --- a/src/core/frontend/applets/controller.h +++ b/src/core/frontend/applets/controller.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include "common/common_types.h" diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp index 6c0bb16725..be42719fa2 100644 --- a/src/core/hle/kernel/k_scheduler.cpp +++ b/src/core/hle/kernel/k_scheduler.cpp @@ -6,6 +6,10 @@ // the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX. #include +#ifdef __APPLE__ +#include +#include +#endif #include "common/assert.h" #include "common/bit_util.h" @@ -208,8 +212,13 @@ void KScheduler::ClearPreviousThread(KernelCore& kernel, KThread* thread) { ASSERT(kernel.GlobalSchedulerContext().IsLocked()); for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; ++i) { // Get an atomic reference to the core scheduler's previous thread. +#ifdef __APPLE__ + static_assert(BOOST_ATOMIC_ADDRESS_LOCK_FREE); + boost::atomic_ref prev_thread(kernel.Scheduler(static_cast(i)).prev_thread); +#else std::atomic_ref prev_thread(kernel.Scheduler(static_cast(i)).prev_thread); static_assert(std::atomic_ref::is_always_lock_free); +#endif // Atomically clear the previous thread if it's our target. KThread* compare = thread; diff --git a/src/core/hle/kernel/k_slab_heap.h b/src/core/hle/kernel/k_slab_heap.h index 5690cc757a..2dcab4063c 100644 --- a/src/core/hle/kernel/k_slab_heap.h +++ b/src/core/hle/kernel/k_slab_heap.h @@ -6,6 +6,11 @@ #include +#ifdef __APPLE__ +#include +#include +#include "common/apple_compat/appleCompat.h" +#endif #include "common/assert.h" #include "common/common_funcs.h" #include "common/common_types.h" @@ -70,14 +75,24 @@ class KSlabHeapBase : protected impl::KSlabHeapImpl { private: size_t m_obj_size{}; - uintptr_t m_peak{}; + uintptr_t m_peak {} +#ifdef __APPLE + alignas(boost::atomic_ref::required_alignment); +#else + ; +#endif uintptr_t m_start{}; uintptr_t m_end{}; private: void UpdatePeakImpl(uintptr_t obj) { +#ifdef __APPLE__ + static_assert(BOOST_ATOMIC_ADDRESS_LOCK_FREE); + boost::atomic_ref peak_ref(m_peak); +#else static_assert(std::atomic_ref::is_always_lock_free); std::atomic_ref peak_ref(m_peak); +#endif const uintptr_t alloc_peak = obj + this->GetObjectSize(); uintptr_t cur_peak = m_peak; diff --git a/src/core/hle/kernel/k_thread_local_page.h b/src/core/hle/kernel/k_thread_local_page.h index 74b565a71c..2a42fe107c 100644 --- a/src/core/hle/kernel/k_thread_local_page.h +++ b/src/core/hle/kernel/k_thread_local_page.h @@ -7,6 +7,9 @@ #include #include +#ifdef __APPLE__ +#include "common/apple_compat/appleCompat.h" +#endif #include "common/alignment.h" #include "common/assert.h" #include "common/common_types.h" diff --git a/src/core/hle/kernel/service_thread.cpp b/src/core/hle/kernel/service_thread.cpp index 52d25b837b..a2c19ebf69 100644 --- a/src/core/hle/kernel/service_thread.cpp +++ b/src/core/hle/kernel/service_thread.cpp @@ -9,6 +9,9 @@ #include #include +#ifdef __APPLE__ +#include "common/apple_compat/appleCompat.h" +#endif #include "common/scope_exit.h" #include "common/thread.h" #include "core/hle/kernel/k_session.h" @@ -29,7 +32,11 @@ private: std::vector threads; std::queue> requests; std::mutex queue_mutex; +#ifdef __APPLE__ + std::condition_variable_any_apple condition; +#else std::condition_variable_any condition; +#endif const std::string service_name; }; diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h index d309f166c2..97a6576d11 100644 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h @@ -82,7 +82,7 @@ void SvcWrap64(Core::System& system) { } // Used by ControlCodeMemory -template +template void SvcWrap64(Core::System& system) { FuncReturn(system, func(system, static_cast(Param(system, 0)), static_cast(Param(system, 1)), Param(system, 2), Param(system, 3), @@ -327,7 +327,7 @@ void SvcWrap64(Core::System& system) { } // Used by CreateCodeMemory -template +template void SvcWrap64(Core::System& system) { u32 param_1 = 0; const u32 retval = func(system, ¶m_1, Param(system, 1), Param(system, 2)).raw; diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp index fba8471423..5adec56154 100644 --- a/src/core/hle/service/acc/profile_manager.cpp +++ b/src/core/hle/service/acc/profile_manager.cpp @@ -7,6 +7,9 @@ #include +#ifdef __APPLE__ +#include "common/apple_compat/appleCompat.h" +#endif #include "common/fs/file.h" #include "common/fs/fs.h" #include "common/fs/path_util.h" diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h index e28c54df61..0d227cef30 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include "common/common_types.h" #include "common/swap.h" diff --git a/src/core/network/network.cpp b/src/core/network/network.cpp index a3e0664b92..47e96cdc11 100644 --- a/src/core/network/network.cpp +++ b/src/core/network/network.cpp @@ -26,6 +26,9 @@ #error "Unimplemented platform" #endif +#ifdef __APPLE__ +#include "common/apple_compat/appleCompat.h" +#endif #include "common/assert.h" #include "common/common_types.h" #include "common/logging/log.h" diff --git a/src/core/network/network_interface.cpp b/src/core/network/network_interface.cpp index 6811f21b14..ee9f0d7562 100644 --- a/src/core/network/network_interface.cpp +++ b/src/core/network/network_interface.cpp @@ -22,6 +22,10 @@ #include #endif +#ifdef __APPLE__ +#include "common/apple_compat/appleCompat.h" +#endif + namespace Network { #ifdef _WIN32 diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt index d4fa69a775..04513c59e0 100644 --- a/src/input_common/CMakeLists.txt +++ b/src/input_common/CMakeLists.txt @@ -64,3 +64,7 @@ target_link_libraries(input_common PRIVATE usb) create_target_directory_groups(input_common) target_link_libraries(input_common PUBLIC core PRIVATE common Boost::boost) + +if (APPLE) + target_link_libraries(input_common PUBLIC range_v3) +endif() diff --git a/src/input_common/drivers/gc_adapter.h b/src/input_common/drivers/gc_adapter.h index 43ad58c85f..3af30c0776 100644 --- a/src/input_common/drivers/gc_adapter.h +++ b/src/input_common/drivers/gc_adapter.h @@ -6,7 +6,11 @@ #include #include +#ifdef __APPLE__ +#include "common/apple_compat/appleCompat.h" +#else #include +#endif #include #include diff --git a/src/input_common/drivers/mouse.cpp b/src/input_common/drivers/mouse.cpp index 3c9a4e7475..54ec238ed9 100644 --- a/src/input_common/drivers/mouse.cpp +++ b/src/input_common/drivers/mouse.cpp @@ -2,7 +2,11 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included +#ifdef __APPLE__ +#include "common/apple_compat/appleCompat.h" +#else #include +#endif #include #include diff --git a/src/input_common/drivers/mouse.h b/src/input_common/drivers/mouse.h index c5833b8ede..475a48c55d 100644 --- a/src/input_common/drivers/mouse.h +++ b/src/input_common/drivers/mouse.h @@ -4,7 +4,11 @@ #pragma once +#ifdef __APPLE__ +#include "common/apple_compat/appleCompat.h" +#else #include +#endif #include #include "common/vector_math.h" diff --git a/src/input_common/drivers/tas_input.cpp b/src/input_common/drivers/tas_input.cpp index 944e141bfe..c8a946be8e 100644 --- a/src/input_common/drivers/tas_input.cpp +++ b/src/input_common/drivers/tas_input.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include +#include #include #include "common/fs/file.h" diff --git a/src/shader_recompiler/CMakeLists.txt b/src/shader_recompiler/CMakeLists.txt index 4c76ce1ea8..a347d4bba1 100644 --- a/src/shader_recompiler/CMakeLists.txt +++ b/src/shader_recompiler/CMakeLists.txt @@ -267,3 +267,7 @@ else() endif() create_target_directory_groups(shader_recompiler) + +if (APPLE) + target_link_libraries(shader_recompiler PUBLIC range_v3) +endif() diff --git a/src/shader_recompiler/frontend/ir/opcodes.h b/src/shader_recompiler/frontend/ir/opcodes.h index 85f7aac02e..a6c006cc60 100644 --- a/src/shader_recompiler/frontend/ir/opcodes.h +++ b/src/shader_recompiler/frontend/ir/opcodes.h @@ -7,6 +7,10 @@ #include #include +#ifdef __APPLE__ +#include "common/apple_compat/appleCompat.h" +#endif + #include #include "shader_recompiler/frontend/ir/type.h" diff --git a/src/shader_recompiler/frontend/maxwell/decode.cpp b/src/shader_recompiler/frontend/maxwell/decode.cpp index e688e648bf..856cab83d6 100644 --- a/src/shader_recompiler/frontend/maxwell/decode.cpp +++ b/src/shader_recompiler/frontend/maxwell/decode.cpp @@ -7,6 +7,9 @@ #include #include +#ifdef __APPLE__ +#include "common/apple_compat/appleCompat.h" +#endif #include "common/common_types.h" #include "shader_recompiler/exception.h" #include "shader_recompiler/frontend/maxwell/decode.h" @@ -63,9 +66,16 @@ constexpr std::array UNORDERED_ENCODINGS{ constexpr auto SortedEncodings() { std::array encodings{UNORDERED_ENCODINGS}; +#ifdef __APPLE__ + std::sort(encodings.begin(), encodings.end(), + [](const InstEncoding& lhs, const InstEncoding& rhs) { + return std::popcount(lhs.mask_value.mask) > std::popcount(rhs.mask_value.mask); + }); +#else std::ranges::sort(encodings, [](const InstEncoding& lhs, const InstEncoding& rhs) { return std::popcount(lhs.mask_value.mask) > std::popcount(rhs.mask_value.mask); }); +#endif return encodings; } constexpr auto ENCODINGS{SortedEncodings()}; diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_conversion_integer.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_conversion_integer.cpp index 92b1ce015a..1380ea9315 100644 --- a/src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_conversion_integer.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_conversion_integer.cpp @@ -181,8 +181,9 @@ void TranslateF2I(TranslatorVisitor& v, u64 insn, const IR::F16F32F64& src_a) { result = IR::U32{v.ir.Select(v.ir.FPIsNan(op_a), v.ir.Imm32(0x8000'0000U), result)}; } else if (f2i.dest_format == DestFormat::I64) { handled_special_case = true; - result = IR::U64{ - v.ir.Select(v.ir.FPIsNan(op_a), v.ir.Imm64(0x8000'0000'0000'0000UL), result)}; + // fix clang overload resolution on macOS by explicitly defining this + u64 immediate = 0x8000'0000'0000'0000UL; + result = IR::U64{v.ir.Select(v.ir.FPIsNan(op_a), v.ir.Imm64(immediate), result)}; } } if (!handled_special_case && is_signed) { diff --git a/src/shader_recompiler/object_pool.h b/src/shader_recompiler/object_pool.h index a12ddcc8fc..b7883a280f 100644 --- a/src/shader_recompiler/object_pool.h +++ b/src/shader_recompiler/object_pool.h @@ -8,6 +8,10 @@ #include #include +#ifdef __APPLE__ +#include "common/apple_compat/appleCompat.h" +#endif + namespace Shader { template diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 6a6325e38e..22c23ec81e 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -277,3 +277,7 @@ else() $<$:-Werror=unused-but-set-variable> ) endif() + +if (APPLE) + target_link_libraries(video_core PUBLIC range_v3) +endif() diff --git a/src/video_core/buffer_cache/buffer_base.h b/src/video_core/buffer_cache/buffer_base.h index 10975884bc..e295e0c90d 100644 --- a/src/video_core/buffer_cache/buffer_base.h +++ b/src/video_core/buffer_cache/buffer_base.h @@ -533,7 +533,7 @@ private: const u64* const state_words = Array(); const u64 num_query_words = size / BYTES_PER_WORD + 1; const u64 word_begin = offset / BYTES_PER_WORD; - const u64 word_end = std::min(word_begin + num_query_words, NumWords()); + const u64 word_end = std::min(word_begin + num_query_words, static_cast(NumWords())); const u64 page_limit = Common::DivCeil(offset + size, BYTES_PER_PAGE); u64 page_index = (offset / BYTES_PER_PAGE) % PAGES_PER_WORD; for (u64 word_index = word_begin; word_index < word_end; ++word_index, page_index = 0) { @@ -566,7 +566,7 @@ private: const u64* const state_words = Array(); const u64 num_query_words = size / BYTES_PER_WORD + 1; const u64 word_begin = offset / BYTES_PER_WORD; - const u64 word_end = std::min(word_begin + num_query_words, NumWords()); + const u64 word_end = std::min(word_begin + num_query_words, static_cast(NumWords())); const u64 page_base = offset / BYTES_PER_PAGE; const u64 page_limit = Common::DivCeil(offset + size, BYTES_PER_PAGE); u64 begin = std::numeric_limits::max(); diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h index 00984188ee..6d987b6f16 100644 --- a/src/video_core/gpu_thread.h +++ b/src/video_core/gpu_thread.h @@ -11,6 +11,9 @@ #include #include +#ifdef __APPLE__ +#include "common/apple_compat/appleCompat.h" +#endif #include "common/threadsafe_queue.h" #include "video_core/framebuffer_config.h" @@ -102,7 +105,11 @@ struct SynchState final { CommandQueue queue; u64 last_fence{}; std::atomic signaled_fence{}; +#ifdef __APPLE__ + std::condition_variable_any_apple cv; +#else std::condition_variable_any cv; +#endif }; /// Class used to manage the GPU thread diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h index 1f1f122910..79e5523f02 100644 --- a/src/video_core/rasterizer_interface.h +++ b/src/video_core/rasterizer_interface.h @@ -7,7 +7,11 @@ #include #include #include +#ifdef __APPLE__ +#include "common/apple_compat/appleCompat.h" +#else #include +#endif #include "common/common_types.h" #include "video_core/engines/fermi_2d.h" #include "video_core/gpu.h" diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp index 656dd7eb00..5ad7252910 100644 --- a/src/video_core/renderer_opengl/gl_device.cpp +++ b/src/video_core/renderer_opengl/gl_device.cpp @@ -13,6 +13,9 @@ #include +#ifdef __APPLE__ +#include "common/apple_compat/appleCompat.h" +#endif #include "common/literals.h" #include "common/logging/log.h" #include "common/settings.h" diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h index 06d4b38bb7..c313ab31ec 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.h +++ b/src/video_core/renderer_opengl/gl_shader_cache.h @@ -5,7 +5,11 @@ #pragma once #include +#ifdef __APPLE__ +#include "common/apple_compat/appleCompat.h" +#else #include +#endif #include #include "common/common_types.h" diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp index c2259ac5f0..b57f61881c 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp @@ -5,6 +5,9 @@ #include #include +#ifdef __APPLE__ +#include "common/apple_compat/appleCompat.h" +#endif #include "common/bit_cast.h" #include "common/cityhash.h" #include "common/common_types.h" diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.h b/src/video_core/renderer_vulkan/vk_master_semaphore.h index 9be9c9bed2..5c84ee1aa0 100644 --- a/src/video_core/renderer_vulkan/vk_master_semaphore.h +++ b/src/video_core/renderer_vulkan/vk_master_semaphore.h @@ -7,6 +7,9 @@ #include #include +#ifdef __APPLE__ +#include "common/apple_compat/appleCompat.h" +#endif #include "common/common_types.h" #include "video_core/vulkan_common/vulkan_wrapper.h" diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h index 25c5e6ca1d..4700e92962 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_scheduler.h @@ -11,6 +11,9 @@ #include #include +#ifdef __APPLE__ +#include "common/apple_compat/appleCompat.h" +#endif #include "common/alignment.h" #include "common/common_types.h" #include "video_core/renderer_vulkan/vk_master_semaphore.h" @@ -229,7 +232,11 @@ private: std::vector> chunk_reserve; std::mutex reserve_mutex; std::mutex work_mutex; +#ifdef __APPLE__ + std::condition_variable_any_apple work_cv; +#else std::condition_variable_any work_cv; +#endif std::condition_variable wait_cv; std::jthread worker_thread; }; diff --git a/src/video_core/shader_environment.h b/src/video_core/shader_environment.h index aae762b270..f5e9bcb562 100644 --- a/src/video_core/shader_environment.h +++ b/src/video_core/shader_environment.h @@ -11,7 +11,11 @@ #include #include #include +#ifdef __APPLE__ +#include "common/apple_compat/appleCompat.h" +#else #include +#endif #include #include #include diff --git a/src/video_core/texture_cache/slot_vector.h b/src/video_core/texture_cache/slot_vector.h index 6aabaef7b0..1b63fbb3eb 100644 --- a/src/video_core/texture_cache/slot_vector.h +++ b/src/video_core/texture_cache/slot_vector.h @@ -11,6 +11,9 @@ #include #include +#ifdef __APPLE__ +#include "common/apple_compat/appleCompat.h" +#endif #include "common/assert.h" #include "common/common_types.h" diff --git a/src/video_core/textures/astc.cpp b/src/video_core/textures/astc.cpp index 28e4beafde..7ac102b125 100644 --- a/src/video_core/textures/astc.cpp +++ b/src/video_core/textures/astc.cpp @@ -24,6 +24,9 @@ #include +#ifdef __APPLE__ +#include "common/apple_compat/appleCompat.h" +#endif #include "common/common_types.h" #include "video_core/textures/astc.h" diff --git a/src/video_core/transform_feedback.cpp b/src/video_core/transform_feedback.cpp index ba26ac3f14..49c98696ee 100644 --- a/src/video_core/transform_feedback.cpp +++ b/src/video_core/transform_feedback.cpp @@ -6,6 +6,9 @@ #include #include +#ifdef __APPLE__ +#include "common/apple_compat/appleCompat.h" +#endif #include "common/alignment.h" #include "common/assert.h" #include "shader_recompiler/shader_info.h" diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index e142bee35c..b6f55c2702 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -11,6 +11,9 @@ #include #include +#ifdef __APPLE__ +#include "common/apple_compat/appleCompat.h" +#endif #include "common/assert.h" #include "common/literals.h" #include "common/settings.h" diff --git a/src/video_core/vulkan_common/vulkan_instance.cpp b/src/video_core/vulkan_common/vulkan_instance.cpp index 662694f169..36ae31de2b 100644 --- a/src/video_core/vulkan_common/vulkan_instance.cpp +++ b/src/video_core/vulkan_common/vulkan_instance.cpp @@ -7,6 +7,9 @@ #include #include +#ifdef __APPLE__ +#include "common/apple_compat/appleCompat.h" +#endif #include "common/common_types.h" #include "common/dynamic_library.h" #include "common/logging/log.h" diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp index e6e97b3320..ac7e4a69a9 100644 --- a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp +++ b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp @@ -9,6 +9,9 @@ #include +#ifdef __APPLE__ +#include "common/apple_compat/appleCompat.h" +#endif #include "common/alignment.h" #include "common/assert.h" #include "common/common_types.h" diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 30902101df..6483e715ca 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -231,6 +231,7 @@ if (APPLE) target_sources(yuzu PRIVATE ${MACOSX_ICON}) set_target_properties(yuzu PROPERTIES MACOSX_BUNDLE TRUE) set_target_properties(yuzu PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist) + target_link_libraries(yuzu PUBLIC range_v3) elseif(WIN32) # compile as a win32 gui application instead of a console application target_link_libraries(yuzu PRIVATE Qt5::WinMain) diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index 4b0ce0293d..50b1ab3394 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h @@ -14,6 +14,9 @@ #include #include +#ifdef __APPLE__ +#include "common/apple_compat/appleCompat.h" +#endif #include "common/thread.h" #include "core/frontend/emu_window.h" @@ -103,7 +106,11 @@ private: bool running = false; std::stop_source stop_source; std::mutex running_mutex; +#ifdef __APPLE__ + std::condition_variable_any_apple running_cv; +#else std::condition_variable_any running_cv; +#endif Common::Event running_wait{}; std::atomic_bool running_guard{false}; Core::System& system; diff --git a/src/yuzu_cmd/CMakeLists.txt b/src/yuzu_cmd/CMakeLists.txt index 74fc249725..0915e33b63 100644 --- a/src/yuzu_cmd/CMakeLists.txt +++ b/src/yuzu_cmd/CMakeLists.txt @@ -52,3 +52,7 @@ if (MSVC) include(CopyYuzuSDLDeps) copy_yuzu_SDL_deps(yuzu-cmd) endif() + +if (APPLE) + target_link_libraries(yuzu-cmd PUBLIC range_v3) +endif()