diff options
Diffstat (limited to 'src/common')
-rw-r--r-- | src/common/CMakeLists.txt | 8 | ||||
-rw-r--r-- | src/common/atomic_helpers.h | 1 | ||||
-rw-r--r-- | src/common/cache_management.cpp | 59 | ||||
-rw-r--r-- | src/common/cache_management.h | 27 | ||||
-rw-r--r-- | src/common/common_precompiled_headers.h | 14 | ||||
-rw-r--r-- | src/common/fs/file.cpp | 2 | ||||
-rw-r--r-- | src/common/fs/fs_util.cpp | 1 | ||||
-rw-r--r-- | src/common/fs/path_util.cpp | 1 | ||||
-rw-r--r-- | src/common/input.h | 41 | ||||
-rw-r--r-- | src/common/logging/backend.cpp | 2 | ||||
-rw-r--r-- | src/common/polyfill_ranges.h | 530 | ||||
-rw-r--r-- | src/common/polyfill_thread.h | 323 | ||||
-rw-r--r-- | src/common/precompiled_headers.h | 6 | ||||
-rw-r--r-- | src/common/settings.cpp | 2 | ||||
-rw-r--r-- | src/common/settings.h | 6 | ||||
-rw-r--r-- | src/common/string_util.cpp | 4 | ||||
-rw-r--r-- | src/common/thread_worker.h | 5 | ||||
-rw-r--r-- | src/common/threadsafe_queue.h | 4 |
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{}; |