summaryrefslogtreecommitdiffstats
path: root/src/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/common')
-rw-r--r--src/common/CMakeLists.txt8
-rw-r--r--src/common/atomic_helpers.h1
-rw-r--r--src/common/cache_management.cpp59
-rw-r--r--src/common/cache_management.h27
-rw-r--r--src/common/common_precompiled_headers.h14
-rw-r--r--src/common/fs/file.cpp2
-rw-r--r--src/common/fs/fs_util.cpp1
-rw-r--r--src/common/fs/path_util.cpp1
-rw-r--r--src/common/input.h41
-rw-r--r--src/common/logging/backend.cpp2
-rw-r--r--src/common/polyfill_ranges.h530
-rw-r--r--src/common/polyfill_thread.h323
-rw-r--r--src/common/precompiled_headers.h6
-rw-r--r--src/common/settings.cpp2
-rw-r--r--src/common/settings.h6
-rw-r--r--src/common/string_util.cpp4
-rw-r--r--src/common/thread_worker.h5
-rw-r--r--src/common/threadsafe_queue.h4
18 files changed, 1025 insertions, 11 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index c0555f840..a12edc584 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -34,7 +34,10 @@ add_library(common STATIC
bit_util.h
cityhash.cpp
cityhash.h
+ cache_management.cpp
+ cache_management.h
common_funcs.h
+ common_precompiled_headers.h
common_types.h
concepts.h
div_ceil.h
@@ -93,6 +96,7 @@ add_library(common STATIC
param_package.h
parent_of_member.h
point.h
+ precompiled_headers.h
quaternion.h
reader_writer_queue.h
ring_buffer.h
@@ -181,3 +185,7 @@ else()
target_link_libraries(common PRIVATE
$<IF:$<TARGET_EXISTS:zstd::libzstd_shared>,zstd::libzstd_shared,zstd::libzstd_static>)
endif()
+
+if (YUZU_USE_PRECOMPILED_HEADERS)
+ target_precompile_headers(common PRIVATE precompiled_headers.h)
+endif()
diff --git a/src/common/atomic_helpers.h b/src/common/atomic_helpers.h
index bef5015c1..aef3b66a4 100644
--- a/src/common/atomic_helpers.h
+++ b/src/common/atomic_helpers.h
@@ -156,6 +156,7 @@ AE_FORCEINLINE void compiler_fence(memory_order order) AE_NO_TSAN {
break;
default:
assert(false);
+ break;
}
}
diff --git a/src/common/cache_management.cpp b/src/common/cache_management.cpp
new file mode 100644
index 000000000..ed353828a
--- /dev/null
+++ b/src/common/cache_management.cpp
@@ -0,0 +1,59 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <cstdint>
+#include <cstring>
+
+#include "common/cache_management.h"
+
+namespace Common {
+
+#if defined(ARCHITECTURE_x86_64)
+
+// Most cache operations are no-ops on x86
+
+void DataCacheLineCleanByVAToPoU(void* start, size_t size) {}
+void DataCacheLineCleanAndInvalidateByVAToPoC(void* start, size_t size) {}
+void DataCacheLineCleanByVAToPoC(void* start, size_t size) {}
+void DataCacheZeroByVA(void* start, size_t size) {
+ std::memset(start, 0, size);
+}
+
+#elif defined(ARCHITECTURE_arm64)
+
+// BS/DminLine is log2(cache size in words), we want size in bytes
+#define EXTRACT_DMINLINE(ctr_el0) (1 << ((((ctr_el0) >> 16) & 0xf) + 2))
+#define EXTRACT_BS(dczid_el0) (1 << (((dczid_el0)&0xf) + 2))
+
+#define DEFINE_DC_OP(op_name, function_name) \
+ void function_name(void* start, size_t size) { \
+ size_t ctr_el0; \
+ asm volatile("mrs %[ctr_el0], ctr_el0\n\t" : [ctr_el0] "=r"(ctr_el0)); \
+ size_t cacheline_size = EXTRACT_DMINLINE(ctr_el0); \
+ uintptr_t va_start = reinterpret_cast<uintptr_t>(start); \
+ uintptr_t va_end = va_start + size; \
+ for (uintptr_t va = va_start; va < va_end; va += cacheline_size) { \
+ asm volatile("dc " #op_name ", %[va]\n\t" : : [va] "r"(va) : "memory"); \
+ } \
+ }
+
+#define DEFINE_DC_OP_DCZID(op_name, function_name) \
+ void function_name(void* start, size_t size) { \
+ size_t dczid_el0; \
+ asm volatile("mrs %[dczid_el0], dczid_el0\n\t" : [dczid_el0] "=r"(dczid_el0)); \
+ size_t cacheline_size = EXTRACT_BS(dczid_el0); \
+ uintptr_t va_start = reinterpret_cast<uintptr_t>(start); \
+ uintptr_t va_end = va_start + size; \
+ for (uintptr_t va = va_start; va < va_end; va += cacheline_size) { \
+ asm volatile("dc " #op_name ", %[va]\n\t" : : [va] "r"(va) : "memory"); \
+ } \
+ }
+
+DEFINE_DC_OP(cvau, DataCacheLineCleanByVAToPoU);
+DEFINE_DC_OP(civac, DataCacheLineCleanAndInvalidateByVAToPoC);
+DEFINE_DC_OP(cvac, DataCacheLineCleanByVAToPoC);
+DEFINE_DC_OP_DCZID(zva, DataCacheZeroByVA);
+
+#endif
+
+} // namespace Common
diff --git a/src/common/cache_management.h b/src/common/cache_management.h
new file mode 100644
index 000000000..038323e95
--- /dev/null
+++ b/src/common/cache_management.h
@@ -0,0 +1,27 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <cstddef>
+
+namespace Common {
+
+// Data cache instructions enabled at EL0 by SCTLR_EL1.UCI.
+// VA = virtual address
+// PoC = point of coherency
+// PoU = point of unification
+
+// dc cvau
+void DataCacheLineCleanByVAToPoU(void* start, size_t size);
+
+// dc civac
+void DataCacheLineCleanAndInvalidateByVAToPoC(void* start, size_t size);
+
+// dc cvac
+void DataCacheLineCleanByVAToPoC(void* start, size_t size);
+
+// dc zva
+void DataCacheZeroByVA(void* start, size_t size);
+
+} // namespace Common
diff --git a/src/common/common_precompiled_headers.h b/src/common/common_precompiled_headers.h
new file mode 100644
index 000000000..be7e5b5f9
--- /dev/null
+++ b/src/common/common_precompiled_headers.h
@@ -0,0 +1,14 @@
+// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <algorithm>
+#include <array>
+#include <chrono>
+#include <memory>
+
+#include <fmt/format.h>
+
+#include "common/assert.h"
+#include "common/common_types.h"
diff --git a/src/common/fs/file.cpp b/src/common/fs/file.cpp
index fa8422c41..656b03cc5 100644
--- a/src/common/fs/file.cpp
+++ b/src/common/fs/file.cpp
@@ -1,6 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+#include <vector>
+
#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 eb4ac1deb..813a713c3 100644
--- a/src/common/fs/fs_util.cpp
+++ b/src/common/fs/fs_util.cpp
@@ -4,6 +4,7 @@
#include <algorithm>
#include "common/fs/fs_util.h"
+#include "common/polyfill_ranges.h"
namespace Common::FS {
diff --git a/src/common/fs/path_util.cpp b/src/common/fs/path_util.cpp
index 1074f2421..defa3e918 100644
--- a/src/common/fs/path_util.cpp
+++ b/src/common/fs/path_util.cpp
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
+#include <sstream>
#include <unordered_map>
#include "common/fs/fs.h"
diff --git a/src/common/input.h b/src/common/input.h
index cb30b7254..fc14fd7bf 100644
--- a/src/common/input.h
+++ b/src/common/input.h
@@ -8,6 +8,7 @@
#include <string>
#include <unordered_map>
#include <utility>
+#include <vector>
#include "common/logging/log.h"
#include "common/param_package.h"
#include "common/uuid.h"
@@ -383,6 +384,16 @@ void RegisterFactory(const std::string& name, std::shared_ptr<Factory<InputDevic
}
}
+inline void RegisterInputFactory(const std::string& name,
+ std::shared_ptr<Factory<InputDevice>> factory) {
+ RegisterFactory<InputDevice>(name, std::move(factory));
+}
+
+inline void RegisterOutputFactory(const std::string& name,
+ std::shared_ptr<Factory<OutputDevice>> factory) {
+ RegisterFactory<OutputDevice>(name, std::move(factory));
+}
+
/**
* Unregisters an input device factory.
* @tparam InputDeviceType the type of input devices the factory can create
@@ -395,6 +406,14 @@ void UnregisterFactory(const std::string& name) {
}
}
+inline void UnregisterInputFactory(const std::string& name) {
+ UnregisterFactory<InputDevice>(name);
+}
+
+inline void UnregisterOutputFactory(const std::string& name) {
+ UnregisterFactory<OutputDevice>(name);
+}
+
/**
* Create an input device from given paramters.
* @tparam InputDeviceType the type of input devices to create
@@ -416,13 +435,21 @@ std::unique_ptr<InputDeviceType> CreateDeviceFromString(const std::string& param
return pair->second->Create(package);
}
+inline std::unique_ptr<InputDevice> CreateInputDeviceFromString(const std::string& params) {
+ return CreateDeviceFromString<InputDevice>(params);
+}
+
+inline std::unique_ptr<OutputDevice> CreateOutputDeviceFromString(const std::string& params) {
+ return CreateDeviceFromString<OutputDevice>(params);
+}
+
/**
- * Create an input device from given paramters.
+ * Create an input device from given parameters.
* @tparam InputDeviceType the type of input devices to create
- * @param A ParamPackage that contains all parameters for creating the device
+ * @param package A ParamPackage that contains all parameters for creating the device
*/
template <typename InputDeviceType>
-std::unique_ptr<InputDeviceType> CreateDevice(const Common::ParamPackage package) {
+std::unique_ptr<InputDeviceType> CreateDevice(const ParamPackage& package) {
const std::string engine = package.Get("engine", "null");
const auto& factory_list = Impl::FactoryList<InputDeviceType>::list;
const auto pair = factory_list.find(engine);
@@ -435,4 +462,12 @@ std::unique_ptr<InputDeviceType> CreateDevice(const Common::ParamPackage package
return pair->second->Create(package);
}
+inline std::unique_ptr<InputDevice> CreateInputDevice(const ParamPackage& package) {
+ return CreateDevice<InputDevice>(package);
+}
+
+inline std::unique_ptr<OutputDevice> CreateOutputDevice(const ParamPackage& package) {
+ return CreateDevice<OutputDevice>(package);
+}
+
} // namespace Common::Input
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 15d92505e..2a3bded40 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -4,7 +4,6 @@
#include <atomic>
#include <chrono>
#include <climits>
-#include <stop_token>
#include <thread>
#include <fmt/format.h>
@@ -18,6 +17,7 @@
#include "common/fs/fs_paths.h"
#include "common/fs/path_util.h"
#include "common/literals.h"
+#include "common/polyfill_thread.h"
#include "common/thread.h"
#include "common/logging/backend.h"
diff --git a/src/common/polyfill_ranges.h b/src/common/polyfill_ranges.h
new file mode 100644
index 000000000..ca44bfaef
--- /dev/null
+++ b/src/common/polyfill_ranges.h
@@ -0,0 +1,530 @@
+// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+//
+// TODO: remove this file when ranges are supported by all compilation targets
+//
+
+#pragma once
+
+#include <algorithm>
+#include <utility>
+#include <version>
+
+#ifndef __cpp_lib_ranges
+
+namespace std {
+namespace ranges {
+
+template <typename T>
+concept range = requires(T& t) {
+ begin(t);
+ end(t);
+};
+
+template <typename T>
+concept input_range = range<T>;
+
+template <typename T>
+concept output_range = range<T>;
+
+template <range R>
+using range_difference_t = ptrdiff_t;
+
+//
+// find, find_if, find_if_not
+//
+
+struct find_fn {
+ template <typename Iterator, typename T, typename Proj = std::identity>
+ constexpr Iterator operator()(Iterator first, Iterator last, const T& value,
+ Proj proj = {}) const {
+ for (; first != last; ++first) {
+ if (std::invoke(proj, *first) == value) {
+ return first;
+ }
+ }
+ return first;
+ }
+
+ template <ranges::input_range R, typename T, typename Proj = std::identity>
+ constexpr ranges::iterator_t<R> operator()(R&& r, const T& value, Proj proj = {}) const {
+ return operator()(ranges::begin(r), ranges::end(r), value, std::ref(proj));
+ }
+};
+
+struct find_if_fn {
+ template <typename Iterator, typename Proj = std::identity, typename Pred>
+ constexpr Iterator operator()(Iterator first, Iterator last, Pred pred, Proj proj = {}) const {
+ for (; first != last; ++first) {
+ if (std::invoke(pred, std::invoke(proj, *first))) {
+ return first;
+ }
+ }
+ return first;
+ }
+
+ template <ranges::input_range R, typename Proj = std::identity, typename Pred>
+ constexpr ranges::iterator_t<R> operator()(R&& r, Pred pred, Proj proj = {}) const {
+ return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj));
+ }
+};
+
+struct find_if_not_fn {
+ template <typename Iterator, typename Proj = std::identity, typename Pred>
+ constexpr Iterator operator()(Iterator first, Iterator last, Pred pred, Proj proj = {}) const {
+ for (; first != last; ++first) {
+ if (!std::invoke(pred, std::invoke(proj, *first))) {
+ return first;
+ }
+ }
+ return first;
+ }
+
+ template <ranges::input_range R, typename Proj = std::identity, typename Pred>
+ constexpr ranges::iterator_t<R> operator()(R&& r, Pred pred, Proj proj = {}) const {
+ return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj));
+ }
+};
+
+inline constexpr find_fn find;
+inline constexpr find_if_fn find_if;
+inline constexpr find_if_not_fn find_if_not;
+
+//
+// any_of, all_of, none_of
+//
+
+struct all_of_fn {
+ template <typename Iterator, typename Proj = std::identity, typename Pred>
+ constexpr bool operator()(Iterator first, Iterator last, Pred pred, Proj proj = {}) const {
+ return ranges::find_if_not(first, last, std::ref(pred), std::ref(proj)) == last;
+ }
+
+ template <ranges::input_range R, typename Proj = std::identity, typename Pred>
+ constexpr bool operator()(R&& r, Pred pred, Proj proj = {}) const {
+ return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj));
+ }
+};
+
+struct any_of_fn {
+ template <typename Iterator, typename Proj = std::identity, typename Pred>
+ constexpr bool operator()(Iterator first, Iterator last, Pred pred, Proj proj = {}) const {
+ return ranges::find_if(first, last, std::ref(pred), std::ref(proj)) != last;
+ }
+
+ template <ranges::input_range R, typename Proj = std::identity, typename Pred>
+ constexpr bool operator()(R&& r, Pred pred, Proj proj = {}) const {
+ return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj));
+ }
+};
+
+struct none_of_fn {
+ template <typename Iterator, typename Proj = std::identity, typename Pred>
+ constexpr bool operator()(Iterator first, Iterator last, Pred pred, Proj proj = {}) const {
+ return ranges::find_if(first, last, std::ref(pred), std::ref(proj)) == last;
+ }
+
+ template <ranges::input_range R, typename Proj = std::identity, typename Pred>
+ constexpr bool operator()(R&& r, Pred pred, Proj proj = {}) const {
+ return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj));
+ }
+};
+
+inline constexpr any_of_fn any_of;
+inline constexpr all_of_fn all_of;
+inline constexpr none_of_fn none_of;
+
+//
+// count, count_if
+//
+
+struct count_fn {
+ template <typename Iterator, typename T, typename Proj = std::identity>
+ constexpr ptrdiff_t operator()(Iterator first, Iterator last, const T& value,
+ Proj proj = {}) const {
+ ptrdiff_t counter = 0;
+ for (; first != last; ++first)
+ if (std::invoke(proj, *first) == value)
+ ++counter;
+ return counter;
+ }
+
+ template <ranges::input_range R, typename T, typename Proj = std::identity>
+ constexpr ptrdiff_t operator()(R&& r, const T& value, Proj proj = {}) const {
+ return operator()(ranges::begin(r), ranges::end(r), value, std::ref(proj));
+ }
+};
+
+struct count_if_fn {
+ template <typename Iterator, typename Proj = std::identity, typename Pred>
+ constexpr ptrdiff_t operator()(Iterator first, Iterator last, Pred pred, Proj proj = {}) const {
+ ptrdiff_t counter = 0;
+ for (; first != last; ++first)
+ if (std::invoke(pred, std::invoke(proj, *first)))
+ ++counter;
+ return counter;
+ }
+
+ template <ranges::input_range R, typename Proj = std::identity, typename Pred>
+ constexpr ptrdiff_t operator()(R&& r, Pred pred, Proj proj = {}) const {
+ return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj));
+ }
+};
+
+inline constexpr count_fn count;
+inline constexpr count_if_fn count_if;
+
+//
+// transform
+//
+
+struct transform_fn {
+ template <typename InputIterator, typename OutputIterator, typename F,
+ typename Proj = std::identity>
+ constexpr void operator()(InputIterator first1, InputIterator last1, OutputIterator result,
+ F op, Proj proj = {}) const {
+ for (; first1 != last1; ++first1, (void)++result) {
+ *result = std::invoke(op, std::invoke(proj, *first1));
+ }
+ }
+
+ template <ranges::input_range R, typename OutputIterator, typename F,
+ typename Proj = std::identity>
+ constexpr void operator()(R&& r, OutputIterator result, F op, Proj proj = {}) const {
+ return operator()(ranges::begin(r), ranges::end(r), result, std::ref(op), std::ref(proj));
+ }
+};
+
+inline constexpr transform_fn transform;
+
+//
+// sort
+//
+
+struct sort_fn {
+ template <typename Iterator, typename Comp = ranges::less, typename Proj = std::identity>
+ constexpr void operator()(Iterator first, Iterator last, Comp comp = {}, Proj proj = {}) const {
+ if (first == last)
+ return;
+
+ Iterator last_iter = ranges::next(first, last);
+ std::sort(first, last_iter,
+ [&](auto& lhs, auto& rhs) { return comp(proj(lhs), proj(rhs)); });
+ }
+
+ template <ranges::input_range R, typename Comp = ranges::less, typename Proj = std::identity>
+ constexpr void operator()(R&& r, Comp comp = {}, Proj proj = {}) const {
+ return operator()(ranges::begin(r), ranges::end(r), std::move(comp), std::move(proj));
+ }
+};
+
+inline constexpr sort_fn sort;
+
+//
+// fill
+//
+
+struct fill_fn {
+ template <typename T, typename OutputIterator>
+ constexpr OutputIterator operator()(OutputIterator first, OutputIterator last,
+ const T& value) const {
+ while (first != last) {
+ *first++ = value;
+ }
+
+ return first;
+ }
+
+ template <typename T, ranges::output_range R>
+ constexpr ranges::iterator_t<R> operator()(R&& r, const T& value) const {
+ return operator()(ranges::begin(r), ranges::end(r), value);
+ }
+};
+
+inline constexpr fill_fn fill;
+
+//
+// for_each
+//
+
+struct for_each_fn {
+ template <typename Iterator, typename Proj = std::identity, typename Fun>
+ constexpr void operator()(Iterator first, Iterator last, Fun f, Proj proj = {}) const {
+ for (; first != last; ++first) {
+ std::invoke(f, std::invoke(proj, *first));
+ }
+ }
+
+ template <ranges::input_range R, typename Proj = std::identity, typename Fun>
+ constexpr void operator()(R&& r, Fun f, Proj proj = {}) const {
+ return operator()(ranges::begin(r), ranges::end(r), std::move(f), std::ref(proj));
+ }
+};
+
+inline constexpr for_each_fn for_each;
+
+//
+// min_element, max_element
+//
+
+struct min_element_fn {
+ template <typename Iterator, typename Proj = std::identity, typename Comp = ranges::less>
+ constexpr Iterator operator()(Iterator first, Iterator last, Comp comp = {},
+ Proj proj = {}) const {
+ if (first == last) {
+ return last;
+ }
+
+ auto smallest = first;
+ ++first;
+ for (; first != last; ++first) {
+ if (!std::invoke(comp, std::invoke(proj, *smallest), std::invoke(proj, *first))) {
+ smallest = first;
+ }
+ }
+ return smallest;
+ }
+
+ template <ranges::input_range R, typename Proj = std::identity, typename Comp = ranges::less>
+ constexpr ranges::iterator_t<R> operator()(R&& r, Comp comp = {}, Proj proj = {}) const {
+ return operator()(ranges::begin(r), ranges::end(r), std::ref(comp), std::ref(proj));
+ }
+};
+
+struct max_element_fn {
+ template <typename Iterator, typename Proj = std::identity, typename Comp = ranges::less>
+ constexpr Iterator operator()(Iterator first, Iterator last, Comp comp = {},
+ Proj proj = {}) const {
+ if (first == last) {
+ return last;
+ }
+
+ auto largest = first;
+ ++first;
+ for (; first != last; ++first) {
+ if (std::invoke(comp, std::invoke(proj, *largest), std::invoke(proj, *first))) {
+ largest = first;
+ }
+ }
+ return largest;
+ }
+
+ template <ranges::input_range R, typename Proj = std::identity, typename Comp = ranges::less>
+ constexpr ranges::iterator_t<R> operator()(R&& r, Comp comp = {}, Proj proj = {}) const {
+ return operator()(ranges::begin(r), ranges::end(r), std::ref(comp), std::ref(proj));
+ }
+};
+
+inline constexpr min_element_fn min_element;
+inline constexpr max_element_fn max_element;
+
+//
+// replace, replace_if
+//
+
+struct replace_fn {
+ template <typename Iterator, typename T1, typename T2, typename Proj = std::identity>
+ constexpr Iterator operator()(Iterator first, Iterator last, const T1& old_value,
+ const T2& new_value, Proj proj = {}) const {
+ for (; first != last; ++first) {
+ if (old_value == std::invoke(proj, *first)) {
+ *first = new_value;
+ }
+ }
+ return first;
+ }
+
+ template <ranges::input_range R, typename T1, typename T2, typename Proj = std::identity>
+ constexpr ranges::iterator_t<R> operator()(R&& r, const T1& old_value, const T2& new_value,
+ Proj proj = {}) const {
+ return operator()(ranges::begin(r), ranges::end(r), old_value, new_value, std::move(proj));
+ }
+};
+
+struct replace_if_fn {
+ template <typename Iterator, typename T, typename Proj = std::identity, typename Pred>
+ constexpr Iterator operator()(Iterator first, Iterator last, Pred pred, const T& new_value,
+ Proj proj = {}) const {
+ for (; first != last; ++first) {
+ if (!!std::invoke(pred, std::invoke(proj, *first))) {
+ *first = new_value;
+ }
+ }
+ return std::move(first);
+ }
+
+ template <ranges::input_range R, typename T, typename Proj = std::identity, typename Pred>
+ constexpr ranges::iterator_t<R> operator()(R&& r, Pred pred, const T& new_value,
+ Proj proj = {}) const {
+ return operator()(ranges::begin(r), ranges::end(r), std::move(pred), new_value,
+ std::move(proj));
+ }
+};
+
+inline constexpr replace_fn replace;
+inline constexpr replace_if_fn replace_if;
+
+//
+// copy, copy_if
+//
+
+struct copy_fn {
+ template <typename InputIterator, typename OutputIterator>
+ constexpr void operator()(InputIterator first, InputIterator last,
+ OutputIterator result) const {
+ for (; first != last; ++first, (void)++result) {
+ *result = *first;
+ }
+ }
+
+ template <ranges::input_range R, typename OutputIterator>
+ constexpr void operator()(R&& r, OutputIterator result) const {
+ return operator()(ranges::begin(r), ranges::end(r), std::move(result));
+ }
+};
+
+struct copy_if_fn {
+ template <typename InputIterator, typename OutputIterator, typename Proj = std::identity,
+ typename Pred>
+ constexpr void operator()(InputIterator first, InputIterator last, OutputIterator result,
+ Pred pred, Proj proj = {}) const {
+ for (; first != last; ++first) {
+ if (std::invoke(pred, std::invoke(proj, *first))) {
+ *result = *first;
+ ++result;
+ }
+ }
+ }
+
+ template <ranges::input_range R, typename OutputIterator, typename Proj = std::identity,
+ typename Pred>
+ constexpr void operator()(R&& r, OutputIterator result, Pred pred, Proj proj = {}) const {
+ return operator()(ranges::begin(r), ranges::end(r), std::move(result), std::ref(pred),
+ std::ref(proj));
+ }
+};
+
+inline constexpr copy_fn copy;
+inline constexpr copy_if_fn copy_if;
+
+//
+// generate
+//
+
+struct generate_fn {
+ template <typename Iterator, typename F>
+ constexpr Iterator operator()(Iterator first, Iterator last, F gen) const {
+ for (; first != last; *first = std::invoke(gen), ++first)
+ ;
+ return first;
+ }
+
+ template <typename R, std::copy_constructible F>
+ requires std::invocable<F&> && ranges::output_range<R>
+ constexpr ranges::iterator_t<R> operator()(R&& r, F gen) const {
+ return operator()(ranges::begin(r), ranges::end(r), std::move(gen));
+ }
+};
+
+inline constexpr generate_fn generate;
+
+//
+// lower_bound, upper_bound
+//
+
+struct lower_bound_fn {
+ template <typename Iterator, typename T, typename Proj = std::identity,
+ typename Comp = ranges::less>
+ constexpr Iterator operator()(Iterator first, Iterator last, const T& value, Comp comp = {},
+ Proj proj = {}) const {
+ Iterator it;
+ std::ptrdiff_t _count, _step;
+ _count = std::distance(first, last);
+
+ while (_count > 0) {
+ it = first;
+ _step = _count / 2;
+ ranges::advance(it, _step, last);
+ if (comp(std::invoke(proj, *it), value)) {
+ first = ++it;
+ _count -= _step + 1;
+ } else {
+ _count = _step;
+ }
+ }
+ return first;
+ }
+
+ template <ranges::input_range R, typename T, typename Proj = std::identity,
+ typename Comp = ranges::less>
+ constexpr ranges::iterator_t<R> operator()(R&& r, const T& value, Comp comp = {},
+ Proj proj = {}) const {
+ return operator()(ranges::begin(r), ranges::end(r), value, std::ref(comp), std::ref(proj));
+ }
+};
+
+struct upper_bound_fn {
+ template <typename Iterator, typename T, typename Proj = std::identity,
+ typename Comp = ranges::less>
+ constexpr Iterator operator()(Iterator first, Iterator last, const T& value, Comp comp = {},
+ Proj proj = {}) const {
+ Iterator it;
+ std::ptrdiff_t _count, _step;
+ _count = std::distance(first, last);
+
+ while (_count > 0) {
+ it = first;
+ _step = _count / 2;
+ ranges::advance(it, _step, last);
+ if (!comp(value, std::invoke(proj, *it))) {
+ first = ++it;
+ _count -= _step + 1;
+ } else {
+ _count = _step;
+ }
+ }
+ return first;
+ }
+
+ template <ranges::input_range R, typename T, typename Proj = std::identity,
+ typename Comp = ranges::less>
+ constexpr ranges::iterator_t<R> operator()(R&& r, const T& value, Comp comp = {},
+ Proj proj = {}) const {
+ return operator()(ranges::begin(r), ranges::end(r), value, std::ref(comp), std::ref(proj));
+ }
+};
+
+inline constexpr lower_bound_fn lower_bound;
+inline constexpr upper_bound_fn upper_bound;
+
+//
+// adjacent_find
+//
+
+struct adjacent_find_fn {
+ template <typename Iterator, typename Proj = std::identity, typename Pred = ranges::equal_to>
+ constexpr Iterator operator()(Iterator first, Iterator last, Pred pred = {},
+ Proj proj = {}) const {
+ if (first == last)
+ return first;
+ auto _next = ranges::next(first);
+ for (; _next != last; ++_next, ++first)
+ if (std::invoke(pred, std::invoke(proj, *first), std::invoke(proj, *_next)))
+ return first;
+ return _next;
+ }
+
+ template <ranges::input_range R, typename Proj = std::identity,
+ typename Pred = ranges::equal_to>
+ constexpr ranges::iterator_t<R> operator()(R&& r, Pred pred = {}, Proj proj = {}) const {
+ return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj));
+ }
+};
+
+inline constexpr adjacent_find_fn adjacent_find;
+
+} // namespace ranges
+} // namespace std
+
+#endif
diff --git a/src/common/polyfill_thread.h b/src/common/polyfill_thread.h
new file mode 100644
index 000000000..5a8d1ce08
--- /dev/null
+++ b/src/common/polyfill_thread.h
@@ -0,0 +1,323 @@
+// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+//
+// TODO: remove this file when jthread is supported by all compilation targets
+//
+
+#pragma once
+
+#include <version>
+
+#ifdef __cpp_lib_jthread
+
+#include <stop_token>
+#include <thread>
+
+namespace Common {
+
+template <typename Condvar, typename Lock, typename Pred>
+void CondvarWait(Condvar& cv, Lock& lock, std::stop_token token, Pred&& pred) {
+ cv.wait(lock, token, std::move(pred));
+}
+
+} // namespace Common
+
+#else
+
+#include <atomic>
+#include <functional>
+#include <list>
+#include <memory>
+#include <mutex>
+#include <optional>
+#include <thread>
+#include <type_traits>
+
+namespace std {
+namespace polyfill {
+
+using stop_state_callbacks = list<function<void()>>;
+
+class stop_state {
+public:
+ stop_state() = default;
+ ~stop_state() = default;
+
+ bool request_stop() {
+ stop_state_callbacks callbacks;
+
+ {
+ scoped_lock lk{m_lock};
+
+ if (m_stop_requested.load()) {
+ // Already set, nothing to do
+ return false;
+ }
+
+ // Set as requested
+ m_stop_requested = true;
+
+ // Copy callback list
+ callbacks = m_callbacks;
+ }
+
+ for (auto callback : callbacks) {
+ callback();
+ }
+
+ return true;
+ }
+
+ bool stop_requested() const {
+ return m_stop_requested.load();
+ }
+
+ stop_state_callbacks::const_iterator insert_callback(function<void()> f) {
+ stop_state_callbacks::const_iterator ret{};
+ bool should_run{};
+
+ {
+ scoped_lock lk{m_lock};
+ should_run = m_stop_requested.load();
+ m_callbacks.push_front(f);
+ ret = m_callbacks.begin();
+ }
+
+ if (should_run) {
+ f();
+ }
+
+ return ret;
+ }
+
+ void remove_callback(stop_state_callbacks::const_iterator it) {
+ scoped_lock lk{m_lock};
+ m_callbacks.erase(it);
+ }
+
+private:
+ mutex m_lock;
+ atomic<bool> m_stop_requested;
+ stop_state_callbacks m_callbacks;
+};
+
+} // namespace polyfill
+
+class stop_token;
+class stop_source;
+struct nostopstate_t {
+ explicit nostopstate_t() = default;
+};
+inline constexpr nostopstate_t nostopstate{};
+
+template <class Callback>
+class stop_callback;
+
+class stop_token {
+public:
+ stop_token() noexcept = default;
+
+ stop_token(const stop_token&) noexcept = default;
+ stop_token(stop_token&&) noexcept = default;
+ stop_token& operator=(const stop_token&) noexcept = default;
+ stop_token& operator=(stop_token&&) noexcept = default;
+ ~stop_token() = default;
+
+ void swap(stop_token& other) noexcept {
+ m_stop_state.swap(other.m_stop_state);
+ }
+
+ [[nodiscard]] bool stop_requested() const noexcept {
+ return m_stop_state && m_stop_state->stop_requested();
+ }
+ [[nodiscard]] bool stop_possible() const noexcept {
+ return m_stop_state != nullptr;
+ }
+
+private:
+ friend class stop_source;
+ template <typename Callback>
+ friend class stop_callback;
+ stop_token(shared_ptr<polyfill::stop_state> stop_state) : m_stop_state(move(stop_state)) {}
+
+private:
+ shared_ptr<polyfill::stop_state> m_stop_state;
+};
+
+class stop_source {
+public:
+ stop_source() : m_stop_state(make_shared<polyfill::stop_state>()) {}
+ explicit stop_source(nostopstate_t) noexcept {}
+
+ stop_source(const stop_source&) noexcept = default;
+ stop_source(stop_source&&) noexcept = default;
+ stop_source& operator=(const stop_source&) noexcept = default;
+ stop_source& operator=(stop_source&&) noexcept = default;
+ ~stop_source() = default;
+ void swap(stop_source& other) noexcept {
+ m_stop_state.swap(other.m_stop_state);
+ }
+
+ [[nodiscard]] stop_token get_token() const noexcept {
+ return stop_token(m_stop_state);
+ }
+ [[nodiscard]] bool stop_possible() const noexcept {
+ return m_stop_state != nullptr;
+ }
+ [[nodiscard]] bool stop_requested() const noexcept {
+ return m_stop_state && m_stop_state->stop_requested();
+ }
+ bool request_stop() noexcept {
+ return m_stop_state && m_stop_state->request_stop();
+ }
+
+private:
+ friend class jthread;
+ explicit stop_source(shared_ptr<polyfill::stop_state> stop_state)
+ : m_stop_state(move(stop_state)) {}
+
+private:
+ shared_ptr<polyfill::stop_state> m_stop_state;
+};
+
+template <typename Callback>
+class stop_callback {
+ static_assert(is_nothrow_destructible_v<Callback>);
+ static_assert(is_invocable_v<Callback>);
+
+public:
+ using callback_type = Callback;
+
+ template <typename C>
+ requires constructible_from<Callback, C>
+ explicit stop_callback(const stop_token& st,
+ C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>)
+ : m_stop_state(st.m_stop_state) {
+ if (m_stop_state) {
+ m_callback = m_stop_state->insert_callback(move(cb));
+ }
+ }
+ template <typename C>
+ requires constructible_from<Callback, C>
+ explicit stop_callback(stop_token&& st,
+ C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>)
+ : m_stop_state(move(st.m_stop_state)) {
+ if (m_stop_state) {
+ m_callback = m_stop_state->insert_callback(move(cb));
+ }
+ }
+ ~stop_callback() {
+ if (m_stop_state && m_callback) {
+ m_stop_state->remove_callback(*m_callback);
+ }
+ }
+
+ stop_callback(const stop_callback&) = delete;
+ stop_callback(stop_callback&&) = delete;
+ stop_callback& operator=(const stop_callback&) = delete;
+ stop_callback& operator=(stop_callback&&) = delete;
+
+private:
+ shared_ptr<polyfill::stop_state> m_stop_state;
+ optional<polyfill::stop_state_callbacks::const_iterator> m_callback;
+};
+
+template <typename Callback>
+stop_callback(stop_token, Callback) -> stop_callback<Callback>;
+
+class jthread {
+public:
+ using id = thread::id;
+ using native_handle_type = thread::native_handle_type;
+
+ jthread() noexcept = default;
+
+ template <typename F, typename... Args,
+ typename = enable_if_t<!is_same_v<remove_cvref_t<F>, jthread>>>
+ explicit jthread(F&& f, Args&&... args)
+ : m_stop_state(make_shared<polyfill::stop_state>()),
+ m_thread(make_thread(move(f), move(args)...)) {}
+
+ ~jthread() {
+ if (joinable()) {
+ request_stop();
+ join();
+ }
+ }
+
+ jthread(const jthread&) = delete;
+ jthread(jthread&&) noexcept = default;
+ jthread& operator=(const jthread&) = delete;
+
+ jthread& operator=(jthread&& other) noexcept {
+ m_thread.swap(other.m_thread);
+ m_stop_state.swap(other.m_stop_state);
+ return *this;
+ }
+
+ void swap(jthread& other) noexcept {
+ m_thread.swap(other.m_thread);
+ m_stop_state.swap(other.m_stop_state);
+ }
+ [[nodiscard]] bool joinable() const noexcept {
+ return m_thread.joinable();
+ }
+ void join() {
+ m_thread.join();
+ }
+ void detach() {
+ m_thread.detach();
+ m_stop_state.reset();
+ }
+
+ [[nodiscard]] id get_id() const noexcept {
+ return m_thread.get_id();
+ }
+ [[nodiscard]] native_handle_type native_handle() {
+ return m_thread.native_handle();
+ }
+ [[nodiscard]] stop_source get_stop_source() noexcept {
+ return stop_source(m_stop_state);
+ }
+ [[nodiscard]] stop_token get_stop_token() const noexcept {
+ return stop_source(m_stop_state).get_token();
+ }
+ bool request_stop() noexcept {
+ return get_stop_source().request_stop();
+ }
+ [[nodiscard]] static unsigned int hardware_concurrency() noexcept {
+ return thread::hardware_concurrency();
+ }
+
+private:
+ template <typename F, typename... Args>
+ thread make_thread(F&& f, Args&&... args) {
+ if constexpr (is_invocable_v<decay_t<F>, stop_token, decay_t<Args>...>) {
+ return thread(move(f), get_stop_token(), move(args)...);
+ } else {
+ return thread(move(f), move(args)...);
+ }
+ }
+
+ shared_ptr<polyfill::stop_state> m_stop_state;
+ thread m_thread;
+};
+
+} // namespace std
+
+namespace Common {
+
+template <typename Condvar, typename Lock, typename Pred>
+void CondvarWait(Condvar& cv, Lock& lock, std::stop_token token, Pred pred) {
+ if (token.stop_requested()) {
+ return;
+ }
+
+ std::stop_callback callback(token, [&] { cv.notify_all(); });
+ cv.wait(lock, [&] { return pred() || token.stop_requested(); });
+}
+
+} // namespace Common
+
+#endif
diff --git a/src/common/precompiled_headers.h b/src/common/precompiled_headers.h
new file mode 100644
index 000000000..aabae730b
--- /dev/null
+++ b/src/common/precompiled_headers.h
@@ -0,0 +1,6 @@
+// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "common/common_precompiled_headers.h"
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index 8173462cb..d8ffe34c3 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -48,6 +48,7 @@ void LogSettings() {
log_setting("CPU_Accuracy", values.cpu_accuracy.GetValue());
log_setting("Renderer_UseResolutionScaling", values.resolution_setup.GetValue());
log_setting("Renderer_ScalingFilter", values.scaling_filter.GetValue());
+ log_setting("Renderer_FSRSlider", values.fsr_sharpening_slider.GetValue());
log_setting("Renderer_AntiAliasing", values.anti_aliasing.GetValue());
log_setting("Renderer_UseSpeedLimit", values.use_speed_limit.GetValue());
log_setting("Renderer_SpeedLimit", values.speed_limit.GetValue());
@@ -181,6 +182,7 @@ void RestoreGlobalState(bool is_powered_on) {
values.cpuopt_unsafe_ignore_global_monitor.SetGlobal(true);
// Renderer
+ values.fsr_sharpening_slider.SetGlobal(true);
values.renderer_backend.SetGlobal(true);
values.vulkan_device.SetGlobal(true);
values.aspect_ratio.SetGlobal(true);
diff --git a/src/common/settings.h b/src/common/settings.h
index 0eb98939c..c0620066c 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -19,6 +19,7 @@ namespace Settings {
enum class RendererBackend : u32 {
OpenGL = 0,
Vulkan = 1,
+ Null = 2,
};
enum class ShaderBackend : u32 {
@@ -411,7 +412,7 @@ struct Values {
// Renderer
SwitchableSetting<RendererBackend, true> renderer_backend{
- RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Vulkan, "backend"};
+ RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Null, "backend"};
Setting<bool> renderer_debug{false, "debug"};
Setting<bool> renderer_shader_feedback{false, "shader_feedback"};
Setting<bool> enable_nsight_aftermath{false, "nsight_aftermath"};
@@ -421,6 +422,7 @@ struct Values {
ResolutionScalingInfo resolution_info{};
SwitchableSetting<ResolutionSetup> resolution_setup{ResolutionSetup::Res1X, "resolution_setup"};
SwitchableSetting<ScalingFilter> scaling_filter{ScalingFilter::Bilinear, "scaling_filter"};
+ SwitchableSetting<int, true> fsr_sharpening_slider{25, 0, 200, "fsr_sharpening_slider"};
SwitchableSetting<AntiAliasing> anti_aliasing{AntiAliasing::None, "anti_aliasing"};
// *nix platforms may have issues with the borderless windowed fullscreen mode.
// Default to exclusive fullscreen on these platforms for now.
@@ -442,7 +444,7 @@ struct Values {
SwitchableSetting<NvdecEmulation> nvdec_emulation{NvdecEmulation::GPU, "nvdec_emulation"};
SwitchableSetting<bool> accelerate_astc{true, "accelerate_astc"};
SwitchableSetting<bool> use_vsync{true, "use_vsync"};
- SwitchableSetting<ShaderBackend, true> shader_backend{ShaderBackend::GLASM, ShaderBackend::GLSL,
+ SwitchableSetting<ShaderBackend, true> shader_backend{ShaderBackend::GLSL, ShaderBackend::GLSL,
ShaderBackend::SPIRV, "shader_backend"};
SwitchableSetting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"};
SwitchableSetting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"};
diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp
index 7a495bc79..b26db4796 100644
--- a/src/common/string_util.cpp
+++ b/src/common/string_util.cpp
@@ -141,7 +141,7 @@ static std::wstring CPToUTF16(u32 code_page, const std::string& input) {
MultiByteToWideChar(code_page, 0, input.data(), static_cast<int>(input.size()), nullptr, 0);
if (size == 0) {
- return L"";
+ return {};
}
std::wstring output(size, L'\0');
@@ -158,7 +158,7 @@ std::string UTF16ToUTF8(const std::wstring& input) {
const auto size = WideCharToMultiByte(CP_UTF8, 0, input.data(), static_cast<int>(input.size()),
nullptr, 0, nullptr, nullptr);
if (size == 0) {
- return "";
+ return {};
}
std::string output(size, '\0');
diff --git a/src/common/thread_worker.h b/src/common/thread_worker.h
index 62c60f724..260ad44e4 100644
--- a/src/common/thread_worker.h
+++ b/src/common/thread_worker.h
@@ -7,13 +7,13 @@
#include <condition_variable>
#include <functional>
#include <mutex>
-#include <stop_token>
#include <string>
#include <thread>
#include <type_traits>
#include <vector>
#include <queue>
+#include "common/polyfill_thread.h"
#include "common/thread.h"
#include "common/unique_function.h"
@@ -47,7 +47,8 @@ public:
if (requests.empty()) {
wait_condition.notify_all();
}
- condition.wait(lock, stop_token, [this] { return !requests.empty(); });
+ Common::CondvarWait(condition, lock, stop_token,
+ [this] { return !requests.empty(); });
if (stop_token.stop_requested()) {
break;
}
diff --git a/src/common/threadsafe_queue.h b/src/common/threadsafe_queue.h
index 053798e79..2ef1da064 100644
--- a/src/common/threadsafe_queue.h
+++ b/src/common/threadsafe_queue.h
@@ -12,6 +12,8 @@
#include <mutex>
#include <utility>
+#include "common/polyfill_thread.h"
+
namespace Common {
template <typename T, bool with_stop_token = false>
class SPSCQueue {
@@ -97,7 +99,7 @@ public:
T PopWait(std::stop_token stop_token) {
if (Empty()) {
std::unique_lock lock{cv_mutex};
- cv.wait(lock, stop_token, [this] { return !Empty(); });
+ Common::CondvarWait(cv, lock, stop_token, [this] { return !Empty(); });
}
if (stop_token.stop_requested()) {
return T{};