Compare commits
7 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
51b2125ae7 | ||
|
|
fae50a3c75 | ||
|
|
8aecf77d77 | ||
|
|
7eaf96eda4 | ||
|
|
4ff304e75b | ||
|
|
b3e844f5cd | ||
|
|
068484206c |
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -553,8 +560,18 @@ find_package(Threads REQUIRED)
|
||||
|
||||
if (APPLE)
|
||||
# Umbrella framework for everything GUI-related
|
||||
find_library(APPKIT_LIBRARY AppKit)
|
||||
find_library(APPSERV_LIBRARY ApplicationServices)
|
||||
find_library(CARBON_LIBRARY Carbon)
|
||||
find_library(COCOA_LIBRARY Cocoa)
|
||||
find_library(COREFOUNDATION_LIBRARY CoreFoundation)
|
||||
find_library(CORESERV_LIBRARY CoreServices)
|
||||
find_library(FOUNDATION_LIBRARY Foundation)
|
||||
find_library(IOK_LIBRARY IOKit)
|
||||
set(PLATFORM_LIBRARIES ${COCOA_LIBRARY} ${IOKIT_LIBRARY} ${COREVIDEO_LIBRARY})
|
||||
set(SDL_FILE ON)
|
||||
|
||||
include_directories("/usr/local/opt/zstd/include")
|
||||
elseif (WIN32)
|
||||
# WSAPoll and SHGetKnownFolderPath (AppData/Roaming) didn't exist before WinNT 6.x (Vista)
|
||||
add_definitions(-D_WIN32_WINNT=0x0600 -DWINVER=0x0600)
|
||||
|
||||
5
externals/CMakeLists.txt
vendored
5
externals/CMakeLists.txt
vendored
@@ -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()
|
||||
|
||||
1
externals/ffmpeg/CMakeLists.txt
vendored
1
externals/ffmpeg/CMakeLists.txt
vendored
@@ -137,6 +137,7 @@ if (NOT WIN32)
|
||||
--disable-network
|
||||
--disable-postproc
|
||||
--disable-swresample
|
||||
--disable-iconv
|
||||
--enable-decoder=h264
|
||||
--enable-decoder=vp8
|
||||
--enable-decoder=vp9
|
||||
|
||||
1
externals/range-v3
vendored
Submodule
1
externals/range-v3
vendored
Submodule
Submodule externals/range-v3 added at d800a03213
@@ -186,3 +186,7 @@ if (MSVC)
|
||||
else()
|
||||
target_link_libraries(common PRIVATE zstd)
|
||||
endif()
|
||||
|
||||
if (APPLE)
|
||||
target_link_libraries(common PUBLIC range_v3)
|
||||
endif()
|
||||
|
||||
108
src/common/apple_compat/appleCompat.h
Normal file
108
src/common/apple_compat/appleCompat.h
Normal file
@@ -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 <bit>
|
||||
#include <concepts>
|
||||
|
||||
#include "common/apple_compat/condition_variable_any2.hpp"
|
||||
#include "common/apple_compat/jthread.hpp"
|
||||
|
||||
#include <range/v3/all.hpp>
|
||||
|
||||
// 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 <std::unsigned_integral T>
|
||||
/* requires !std::custom_same_as<T, bool> && !std::custom_same_as<T, char> &&
|
||||
!std::custom_same_as<T, char8_t> && !std::custom_same_as<T, char16_t> &&
|
||||
!std::custom_same_as<T, char32_t> && !std::custom_same_as<T, wchar_t>*/
|
||||
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 <typename RAIt>
|
||||
constexpr RAIt next(RAIt it, typename std::iterator_traits<RAIt>::difference_type n = 1) {
|
||||
return it + n;
|
||||
}
|
||||
|
||||
template <typename RAIt>
|
||||
constexpr auto distance(RAIt first, RAIt last) {
|
||||
return last - first;
|
||||
}
|
||||
|
||||
template <class ForwardIt1, class ForwardIt2>
|
||||
constexpr void iter_swap(ForwardIt1 a, ForwardIt2 b) {
|
||||
auto temp = std::move(*a);
|
||||
*a = std::move(*b);
|
||||
*b = std::move(temp);
|
||||
}
|
||||
|
||||
template <class InputIt, class UnaryPredicate>
|
||||
constexpr InputIt find_if_not(InputIt first, InputIt last, UnaryPredicate q) {
|
||||
for (; first != last; ++first) {
|
||||
if (!q(*first)) {
|
||||
return first;
|
||||
}
|
||||
}
|
||||
return last;
|
||||
}
|
||||
|
||||
template <class ForwardIt, class UnaryPredicate>
|
||||
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 <class RAIt, class Compare = std::less<>>
|
||||
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 <typename Range>
|
||||
constexpr auto sort(Range&& range) {
|
||||
quick_sort(std::begin(range), std::end(range));
|
||||
return range;
|
||||
}
|
||||
} // namespace constexprSort
|
||||
#endif
|
||||
299
src/common/apple_compat/condition_variable_any2.hpp
Normal file
299
src/common/apple_compat/condition_variable_any2.hpp
Normal file
@@ -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 <condition_variable>
|
||||
#include <iostream>
|
||||
|
||||
namespace std {
|
||||
|
||||
|
||||
//*****************************************
|
||||
//* class condition_variable_any2
|
||||
//* - joining std::thread with interrupt support
|
||||
//*****************************************
|
||||
class condition_variable_any2
|
||||
{
|
||||
template<typename Lockable>
|
||||
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<std::mutex> guard(m);
|
||||
cv.notify_all();
|
||||
}
|
||||
void notify_one(){
|
||||
std::lock_guard<std::mutex> guard(m);
|
||||
cv.notify_one();
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
//*****************************************
|
||||
//* standardized API for condition_variable_any:
|
||||
//*****************************************
|
||||
|
||||
condition_variable_any2()
|
||||
: internals{std::make_shared<cv_internals>()} {
|
||||
}
|
||||
~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<typename Lockable>
|
||||
void wait(Lockable& lock) {
|
||||
auto local_internals=internals;
|
||||
std::unique_lock<std::mutex> first_internal_lock(local_internals->m);
|
||||
unlock_guard<Lockable> unlocker(lock);
|
||||
std::unique_lock<std::mutex> second_internal_lock(std::move(first_internal_lock));
|
||||
local_internals->cv.wait(second_internal_lock);
|
||||
}
|
||||
|
||||
template<class Lockable,class Predicate>
|
||||
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<std::mutex> first_internal_lock(local_internals->m);
|
||||
unlock_guard<Lockable> unlocker(lock);
|
||||
std::unique_lock<std::mutex> second_internal_lock(std::move(first_internal_lock));
|
||||
local_internals->cv.wait(second_internal_lock);
|
||||
}
|
||||
}
|
||||
|
||||
// wait_until()
|
||||
|
||||
template<class Lockable, class Clock, class Duration>
|
||||
cv_status wait_until(Lockable& lock,
|
||||
const chrono::time_point<Clock, Duration>& abs_time) {
|
||||
auto local_internals=internals;
|
||||
std::unique_lock<std::mutex> first_internal_lock(local_internals->m);
|
||||
unlock_guard<Lockable> unlocker(lock);
|
||||
std::unique_lock<std::mutex> second_internal_lock(std::move(first_internal_lock));
|
||||
return local_internals->cv.wait_until(second_internal_lock, abs_time);
|
||||
}
|
||||
|
||||
template<class Lockable,class Clock, class Duration, class Predicate>
|
||||
bool wait_until(Lockable& lock,
|
||||
const chrono::time_point<Clock, Duration>& 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<std::mutex> first_internal_lock(local_internals->m);
|
||||
unlock_guard<Lockable> unlocker(lock);
|
||||
std::unique_lock<std::mutex> 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<class Lockable,class Rep, class Period>
|
||||
cv_status wait_for(Lockable& lock,
|
||||
const chrono::duration<Rep, Period>& rel_time) {
|
||||
return wait_until(lock, std::chrono::steady_clock::now() + rel_time);
|
||||
}
|
||||
|
||||
template<class Lockable,class Rep, class Period, class Predicate>
|
||||
bool wait_for(Lockable& lock,
|
||||
const chrono::duration<Rep, Period>& 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 <class Lockable,class Predicate>
|
||||
bool wait(Lockable& lock,
|
||||
stop_token stoken,
|
||||
Predicate pred);
|
||||
|
||||
// return:
|
||||
// - true if pred() yields true
|
||||
// - false otherwise (i.e. on timeout or interrupt)
|
||||
template <class Lockable, class Clock, class Duration, class Predicate>
|
||||
bool wait_until(Lockable& lock,
|
||||
stop_token stoken,
|
||||
const chrono::time_point<Clock, Duration>& abs_time,
|
||||
Predicate pred);
|
||||
// return:
|
||||
// - true if pred() yields true
|
||||
// - false otherwise (i.e. on timeout or interrupt)
|
||||
template <class Lockable, class Rep, class Period, class Predicate>
|
||||
bool wait_for(Lockable& lock,
|
||||
stop_token stoken,
|
||||
const chrono::duration<Rep, Period>& rel_time,
|
||||
Predicate pred);
|
||||
|
||||
//*****************************************
|
||||
//* implementation:
|
||||
//*****************************************
|
||||
|
||||
private:
|
||||
//*** API for the starting thread:
|
||||
std::shared_ptr<cv_internals> 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<mutex> instead of mutex directly, and the wait functions create
|
||||
// a local shared_ptr<mutex> 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 <class Lockable, class Predicate>
|
||||
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<std::mutex> 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<Lockable> unlocker(lock);
|
||||
std::unique_lock<std::mutex> 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 <class Lockable, class Clock, class Duration, class Predicate>
|
||||
inline bool condition_variable_any2::wait_until(Lockable& lock,
|
||||
stop_token stoken,
|
||||
const chrono::time_point<Clock, Duration>& 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<std::mutex> 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<Lockable> unlocker(lock);
|
||||
std::unique_lock<std::mutex> 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 <class Lockable,class Rep, class Period, class Predicate>
|
||||
inline bool condition_variable_any2::wait_for(Lockable& lock,
|
||||
stop_token stoken,
|
||||
const chrono::duration<Rep, Period>& 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
|
||||
174
src/common/apple_compat/jthread.hpp
Normal file
174
src/common/apple_compat/jthread.hpp
Normal file
@@ -0,0 +1,174 @@
|
||||
// -----------------------------------------------------
|
||||
// cooperative interruptable and joining thread:
|
||||
// -----------------------------------------------------
|
||||
#ifndef JTHREAD_HPP
|
||||
#define JTHREAD_HPP
|
||||
|
||||
#include "common/apple_compat/stop_token.hpp"
|
||||
#include <thread>
|
||||
#include <future>
|
||||
#include <type_traits>
|
||||
#include <functional> // for invoke()
|
||||
#include <iostream> // 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 <typename F, typename... Args> explicit jthread(F&& f, Args&&... args);
|
||||
// THE constructor that starts the thread:
|
||||
// - NOTE: does SFINAE out copy constructor semantics
|
||||
template <typename Callable, typename... Args,
|
||||
typename = ::std::enable_if_t<!::std::is_same_v<::std::decay_t<Callable>, 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 <typename Callable, typename... Args,
|
||||
typename >
|
||||
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<Callable, stop_token, Args...>) {
|
||||
// pass the stop_token as first argument to the started thread:
|
||||
::std::invoke(::std::forward<decltype(cb)>(cb),
|
||||
std::move(st),
|
||||
::std::forward<decltype(args)>(args)...);
|
||||
}
|
||||
else {
|
||||
// started thread does not expect a stop token:
|
||||
::std::invoke(::std::forward<decltype(cb)>(cb),
|
||||
::std::forward<decltype(args)>(args)...);
|
||||
}
|
||||
},
|
||||
_stopSource.get_token(), // not captured due to possible races if immediately set
|
||||
::std::forward<Callable>(cb), // pass callable
|
||||
::std::forward<Args>(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
|
||||
566
src/common/apple_compat/stop_token.hpp
Normal file
566
src/common/apple_compat/stop_token.hpp
Normal file
@@ -0,0 +1,566 @@
|
||||
#pragma once
|
||||
// <stop_token> header
|
||||
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#ifdef SAFE
|
||||
#include <iostream>
|
||||
#endif
|
||||
|
||||
#if defined(__x86_64__) || defined(_M_X64)
|
||||
#include <immintrin.h>
|
||||
#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<bool> __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<std::uint64_t>(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<std::uint64_t> __state_{__source_ref_increment};
|
||||
__stop_callback_base* __head_ = nullptr;
|
||||
std::thread::id __signallingThread_{};
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------
|
||||
// forward declarations
|
||||
//-----------------------------------------------
|
||||
|
||||
class stop_source;
|
||||
template <typename _Callback>
|
||||
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 <typename _Callback>
|
||||
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 <typename _Callback>
|
||||
// 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<std::is_constructible_v<_Callback, _CB>, int> = 0>
|
||||
// requires Constructible<Callback, C>
|
||||
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<stop_callback*>(__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<std::is_constructible_v<_Callback, _CB>, int> = 0>
|
||||
// requires Constructible<Callback, C>
|
||||
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<stop_callback*>(__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<bool> __inExecute_{false};
|
||||
#endif
|
||||
};
|
||||
|
||||
template<typename _Callback>
|
||||
stop_callback(stop_token, _Callback) -> stop_callback<_Callback>;
|
||||
|
||||
} // namespace std
|
||||
@@ -2,6 +2,8 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "common/fs/file.h"
|
||||
#include "common/fs/fs.h"
|
||||
#include "common/logging/log.h"
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#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<const u8> 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<const u8> buffer) {
|
||||
@@ -25,7 +32,11 @@ std::string ToUTF8String(std::u8string_view u8_string) {
|
||||
}
|
||||
|
||||
std::string BufferToUTF8String(std::span<const u8> 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<const u8> buffer) {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "common/fs/fs.h"
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
#include <windows.h>
|
||||
#include "common/dynamic_library.h"
|
||||
|
||||
#elif defined(__linux__) || defined(__FreeBSD__) // ^^^ Windows ^^^ vvv Linux vvv
|
||||
#elif defined(__linux__) || defined(__FreeBSD__) || \
|
||||
defined(__APPLE__) // ^^^ Windows ^^^ vvv Linux vvv
|
||||
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
@@ -347,13 +348,15 @@ private:
|
||||
std::unordered_map<size_t, size_t> placeholder_host_pointers; ///< Placeholder backing offset
|
||||
};
|
||||
|
||||
#elif defined(__linux__) || defined(__FreeBSD__) // ^^^ Windows ^^^ vvv Linux vvv
|
||||
#elif defined(__linux__) || defined(__FreeBSD__) || \
|
||||
defined(__APPLE__) // ^^^ Windows ^^^ vvv Linux vvv
|
||||
|
||||
class HostMemory::Impl {
|
||||
public:
|
||||
explicit Impl(size_t backing_size_, size_t virtual_size_)
|
||||
: backing_size{backing_size_}, virtual_size{virtual_size_} {
|
||||
bool good = false;
|
||||
|
||||
SCOPE_EXIT({
|
||||
if (!good) {
|
||||
Release();
|
||||
@@ -364,6 +367,10 @@ public:
|
||||
#if defined(__FreeBSD__) && __FreeBSD__ < 13
|
||||
// XXX Drop after FreeBSD 12.* reaches EOL on 2024-06-30
|
||||
fd = shm_open(SHM_ANON, O_RDWR, 0600);
|
||||
#elif defined(__APPLE__)
|
||||
// Hack since macOS doesn't have SHM_ANON or memfd_create
|
||||
fd = shm_open("HostMemory", O_RDWR | O_CREAT);
|
||||
shm_unlink("HostMemory");
|
||||
#else
|
||||
fd = memfd_create("HostMemory", 0);
|
||||
#endif
|
||||
|
||||
@@ -5,7 +5,11 @@
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <climits>
|
||||
#ifdef __APPLE__
|
||||
#include "common/apple_compat/appleCompat.h"
|
||||
#else
|
||||
#include <stop_token>
|
||||
#endif
|
||||
#include <thread>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
@@ -8,7 +8,11 @@
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#ifdef __APPLE__
|
||||
#include "common/apple_compat/appleCompat.h"
|
||||
#else
|
||||
#include <stop_token>
|
||||
#endif
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <type_traits>
|
||||
@@ -102,7 +106,11 @@ public:
|
||||
private:
|
||||
std::queue<Task> 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<size_t> work_scheduled{};
|
||||
std::atomic<size_t> work_done{};
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
// single reader, single writer queue
|
||||
|
||||
#include <atomic>
|
||||
#ifdef __APPLE__
|
||||
#include "common/apple_compat/appleCompat.h"
|
||||
#endif
|
||||
#include <condition_variable>
|
||||
#include <cstddef>
|
||||
#include <mutex>
|
||||
@@ -136,7 +139,12 @@ private:
|
||||
ElementPtr* read_ptr;
|
||||
std::atomic_size_t size{0};
|
||||
std::mutex cv_mutex;
|
||||
#ifdef __APPLE__
|
||||
std::conditional_t<with_stop_token, std::condition_variable_any_apple, std::condition_variable>
|
||||
cv;
|
||||
#else
|
||||
std::conditional_t<with_stop_token, std::condition_variable_any, std::condition_variable> cv;
|
||||
#endif
|
||||
};
|
||||
|
||||
// a simple thread-safe,
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include "common/apple_compat/appleCompat.h"
|
||||
#endif
|
||||
#include "common/fiber.h"
|
||||
#include "common/thread.h"
|
||||
#include "core/hardware_properties.h"
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
|
||||
#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"
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
|
||||
@@ -6,6 +6,10 @@
|
||||
// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX.
|
||||
|
||||
#include <bit>
|
||||
#ifdef __APPLE__
|
||||
#include <boost/atomic/atomic_ref.hpp>
|
||||
#include <boost/atomic/capabilities.hpp>
|
||||
#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<KThread*> prev_thread(kernel.Scheduler(static_cast<s32>(i)).prev_thread);
|
||||
#else
|
||||
std::atomic_ref<KThread*> prev_thread(kernel.Scheduler(static_cast<s32>(i)).prev_thread);
|
||||
static_assert(std::atomic_ref<KThread*>::is_always_lock_free);
|
||||
#endif
|
||||
|
||||
// Atomically clear the previous thread if it's our target.
|
||||
KThread* compare = thread;
|
||||
|
||||
@@ -6,6 +6,11 @@
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <boost/atomic/atomic_ref.hpp>
|
||||
#include <boost/atomic/capabilities.hpp>
|
||||
#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<uintptr_t>::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<uintptr_t> peak_ref(m_peak);
|
||||
#else
|
||||
static_assert(std::atomic_ref<uintptr_t>::is_always_lock_free);
|
||||
std::atomic_ref<uintptr_t> peak_ref(m_peak);
|
||||
#endif
|
||||
|
||||
const uintptr_t alloc_peak = obj + this->GetObjectSize();
|
||||
uintptr_t cur_peak = m_peak;
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include "common/apple_compat/appleCompat.h"
|
||||
#endif
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
|
||||
#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<std::jthread> threads;
|
||||
std::queue<std::function<void()>> 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;
|
||||
};
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ void SvcWrap64(Core::System& system) {
|
||||
}
|
||||
|
||||
// Used by ControlCodeMemory
|
||||
template <ResultCode func(Core::System&, Handle, u32, u64, u64, Svc::MemoryPermission)>
|
||||
template <ResultCode func(Core::System&, Handle, u32, u64, size_t, Svc::MemoryPermission)>
|
||||
void SvcWrap64(Core::System& system) {
|
||||
FuncReturn(system, func(system, static_cast<Handle>(Param(system, 0)),
|
||||
static_cast<u32>(Param(system, 1)), Param(system, 2), Param(system, 3),
|
||||
@@ -327,7 +327,7 @@ void SvcWrap64(Core::System& system) {
|
||||
}
|
||||
|
||||
// Used by CreateCodeMemory
|
||||
template <ResultCode func(Core::System&, Handle*, u64, u64)>
|
||||
template <ResultCode func(Core::System&, Handle*, u64, size_t)>
|
||||
void SvcWrap64(Core::System& system) {
|
||||
u32 param_1 = 0;
|
||||
const u32 retval = func(system, ¶m_1, Param(system, 1), Param(system, 2)).raw;
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#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"
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -22,6 +22,10 @@
|
||||
#include <net/if.h>
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include "common/apple_compat/appleCompat.h"
|
||||
#endif
|
||||
|
||||
namespace Network {
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -6,7 +6,11 @@
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#ifdef __APPLE__
|
||||
#include "common/apple_compat/appleCompat.h"
|
||||
#else
|
||||
#include <stop_token>
|
||||
#endif
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
|
||||
@@ -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 <stop_token>
|
||||
#endif
|
||||
#include <thread>
|
||||
#include <fmt/format.h>
|
||||
|
||||
|
||||
@@ -4,7 +4,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include "common/apple_compat/appleCompat.h"
|
||||
#else
|
||||
#include <stop_token>
|
||||
#endif
|
||||
#include <thread>
|
||||
|
||||
#include "common/vector_math.h"
|
||||
|
||||
@@ -419,7 +419,13 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_XBOX, "0");
|
||||
|
||||
// If the frontend is going to manage the event loop, then we don't start one here
|
||||
#ifdef __APPLE__
|
||||
// macOS will crash the application if any thread but the main one
|
||||
// tries to interact with the UX in any way.
|
||||
start_thread = false;
|
||||
#else
|
||||
start_thread = SDL_WasInit(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) == 0;
|
||||
#endif
|
||||
if (start_thread && SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) < 0) {
|
||||
LOG_CRITICAL(Input, "SDL_Init failed with: {}", SDL_GetError());
|
||||
return;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/fs/file.h"
|
||||
|
||||
@@ -267,3 +267,7 @@ else()
|
||||
endif()
|
||||
|
||||
create_target_directory_groups(shader_recompiler)
|
||||
|
||||
if (APPLE)
|
||||
target_link_libraries(shader_recompiler PUBLIC range_v3)
|
||||
endif()
|
||||
|
||||
@@ -7,6 +7,10 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include "common/apple_compat/appleCompat.h"
|
||||
#endif
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "shader_recompiler/frontend/ir/type.h"
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
#include <bit>
|
||||
#include <memory>
|
||||
|
||||
#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()};
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include "common/apple_compat/appleCompat.h"
|
||||
#endif
|
||||
|
||||
namespace Shader {
|
||||
|
||||
template <typename T>
|
||||
|
||||
@@ -277,3 +277,7 @@ else()
|
||||
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable>
|
||||
)
|
||||
endif()
|
||||
|
||||
if (APPLE)
|
||||
target_link_libraries(video_core PUBLIC range_v3)
|
||||
endif()
|
||||
|
||||
@@ -533,7 +533,7 @@ private:
|
||||
const u64* const state_words = Array<type>();
|
||||
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<u64>(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<type>();
|
||||
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<u64>(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<u64>::max();
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
#include <thread>
|
||||
#include <variant>
|
||||
|
||||
#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<u64> signaled_fence{};
|
||||
#ifdef __APPLE__
|
||||
std::condition_variable_any_apple cv;
|
||||
#else
|
||||
std::condition_variable_any cv;
|
||||
#endif
|
||||
};
|
||||
|
||||
/// Class used to manage the GPU thread
|
||||
|
||||
@@ -7,7 +7,11 @@
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#ifdef __APPLE__
|
||||
#include "common/apple_compat/appleCompat.h"
|
||||
#else
|
||||
#include <stop_token>
|
||||
#endif
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/fermi_2d.h"
|
||||
#include "video_core/gpu.h"
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
|
||||
#include <glad/glad.h>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include "common/apple_compat/appleCompat.h"
|
||||
#endif
|
||||
#include "common/literals.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/settings.h"
|
||||
|
||||
@@ -5,7 +5,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#ifdef __APPLE__
|
||||
#include "common/apple_compat/appleCompat.h"
|
||||
#else
|
||||
#include <stop_token>
|
||||
#endif
|
||||
#include <unordered_map>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include "common/apple_compat/appleCompat.h"
|
||||
#endif
|
||||
#include "common/bit_cast.h"
|
||||
#include "common/cityhash.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include "common/apple_compat/appleCompat.h"
|
||||
#endif
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
||||
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
#include <utility>
|
||||
#include <queue>
|
||||
|
||||
#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<std::unique_ptr<CommandChunk>> 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;
|
||||
};
|
||||
|
||||
@@ -11,7 +11,11 @@
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#ifdef __APPLE__
|
||||
#include "common/apple_compat/appleCompat.h"
|
||||
#else
|
||||
#include <stop_token>
|
||||
#endif
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include "common/apple_compat/appleCompat.h"
|
||||
#endif
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
|
||||
@@ -24,6 +24,9 @@
|
||||
|
||||
#include <boost/container/static_vector.hpp>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include "common/apple_compat/appleCompat.h"
|
||||
#endif
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/textures/astc.h"
|
||||
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
#include <array>
|
||||
#include <vector>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include "common/apple_compat/appleCompat.h"
|
||||
#endif
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "shader_recompiler/shader_info.h"
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include "common/apple_compat/appleCompat.h"
|
||||
#endif
|
||||
#include "common/assert.h"
|
||||
#include "common/literals.h"
|
||||
#include "common/settings.h"
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include "common/apple_compat/appleCompat.h"
|
||||
#endif
|
||||
#include "common/common_types.h"
|
||||
#include "common/dynamic_library.h"
|
||||
#include "common/logging/log.h"
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
|
||||
#include <glad/glad.h>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include "common/apple_compat/appleCompat.h"
|
||||
#endif
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -14,6 +14,9 @@
|
||||
#include <QTouchEvent>
|
||||
#include <QWidget>
|
||||
|
||||
#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;
|
||||
|
||||
@@ -296,7 +296,11 @@ GMainWindow::GMainWindow()
|
||||
ui->action_Fullscreen->setChecked(false);
|
||||
|
||||
#if defined(HAVE_SDL2) && !defined(_WIN32)
|
||||
#ifdef __APPLE__
|
||||
SDL_InitSubSystem(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER);
|
||||
#else
|
||||
SDL_InitSubSystem(SDL_INIT_VIDEO);
|
||||
#endif
|
||||
// SDL disables the screen saver by default, and setting the hint
|
||||
// SDL_HINT_VIDEO_ALLOW_SCREENSAVER doesn't seem to work, so we just enable the screen saver
|
||||
// for now.
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user