summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/CMakeLists.txt2
-rw-r--r--src/common/thread_worker.cpp58
-rw-r--r--src/common/thread_worker.h102
-rw-r--r--src/common/unique_function.h62
-rw-r--r--src/tests/CMakeLists.txt1
-rw-r--r--src/tests/common/unique_function.cpp108
-rw-r--r--src/yuzu/configuration/config.cpp10
7 files changed, 274 insertions, 69 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index a6fa9a85d..e03fffd8d 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -180,7 +180,6 @@ add_library(common STATIC
180 thread.cpp 180 thread.cpp
181 thread.h 181 thread.h
182 thread_queue_list.h 182 thread_queue_list.h
183 thread_worker.cpp
184 thread_worker.h 183 thread_worker.h
185 threadsafe_queue.h 184 threadsafe_queue.h
186 time_zone.cpp 185 time_zone.cpp
@@ -188,6 +187,7 @@ add_library(common STATIC
188 tiny_mt.h 187 tiny_mt.h
189 tree.h 188 tree.h
190 uint128.h 189 uint128.h
190 unique_function.h
191 uuid.cpp 191 uuid.cpp
192 uuid.h 192 uuid.h
193 vector_math.h 193 vector_math.h
diff --git a/src/common/thread_worker.cpp b/src/common/thread_worker.cpp
deleted file mode 100644
index 8f9bf447a..000000000
--- a/src/common/thread_worker.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
1// Copyright 2020 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/thread.h"
6#include "common/thread_worker.h"
7
8namespace Common {
9
10ThreadWorker::ThreadWorker(std::size_t num_workers, const std::string& name) {
11 for (std::size_t i = 0; i < num_workers; ++i)
12 threads.emplace_back([this, thread_name{std::string{name}}] {
13 Common::SetCurrentThreadName(thread_name.c_str());
14
15 // Wait for first request
16 {
17 std::unique_lock lock{queue_mutex};
18 condition.wait(lock, [this] { return stop || !requests.empty(); });
19 }
20
21 while (true) {
22 std::function<void()> task;
23
24 {
25 std::unique_lock lock{queue_mutex};
26 condition.wait(lock, [this] { return stop || !requests.empty(); });
27 if (stop || requests.empty()) {
28 return;
29 }
30 task = std::move(requests.front());
31 requests.pop();
32 }
33
34 task();
35 }
36 });
37}
38
39ThreadWorker::~ThreadWorker() {
40 {
41 std::unique_lock lock{queue_mutex};
42 stop = true;
43 }
44 condition.notify_all();
45 for (std::thread& thread : threads) {
46 thread.join();
47 }
48}
49
50void ThreadWorker::QueueWork(std::function<void()>&& work) {
51 {
52 std::unique_lock lock{queue_mutex};
53 requests.emplace(work);
54 }
55 condition.notify_one();
56}
57
58} // namespace Common
diff --git a/src/common/thread_worker.h b/src/common/thread_worker.h
index f1859971f..8272985ff 100644
--- a/src/common/thread_worker.h
+++ b/src/common/thread_worker.h
@@ -7,24 +7,110 @@
7#include <atomic> 7#include <atomic>
8#include <functional> 8#include <functional>
9#include <mutex> 9#include <mutex>
10#include <stop_token>
10#include <string> 11#include <string>
12#include <thread>
13#include <type_traits>
11#include <vector> 14#include <vector>
12#include <queue> 15#include <queue>
13 16
17#include "common/thread.h"
18#include "common/unique_function.h"
19
14namespace Common { 20namespace Common {
15 21
16class ThreadWorker final { 22template <class StateType = void>
23class StatefulThreadWorker {
24 static constexpr bool with_state = !std::is_same_v<StateType, void>;
25
26 struct DummyCallable {
27 int operator()() const noexcept {
28 return 0;
29 }
30 };
31
32 using Task =
33 std::conditional_t<with_state, UniqueFunction<void, StateType*>, UniqueFunction<void>>;
34 using StateMaker = std::conditional_t<with_state, std::function<StateType()>, DummyCallable>;
35
17public: 36public:
18 explicit ThreadWorker(std::size_t num_workers, const std::string& name); 37 explicit StatefulThreadWorker(size_t num_workers, std::string name, StateMaker func = {})
19 ~ThreadWorker(); 38 : workers_queued{num_workers}, thread_name{std::move(name)} {
20 void QueueWork(std::function<void()>&& work); 39 const auto lambda = [this, func](std::stop_token stop_token) {
40 Common::SetCurrentThreadName(thread_name.c_str());
41 {
42 std::conditional_t<with_state, StateType, int> state{func()};
43 while (!stop_token.stop_requested()) {
44 Task task;
45 {
46 std::unique_lock lock{queue_mutex};
47 if (requests.empty()) {
48 wait_condition.notify_all();
49 }
50 condition.wait(lock, stop_token, [this] { return !requests.empty(); });
51 if (stop_token.stop_requested()) {
52 break;
53 }
54 task = std::move(requests.front());
55 requests.pop();
56 }
57 if constexpr (with_state) {
58 task(&state);
59 } else {
60 task();
61 }
62 ++work_done;
63 }
64 }
65 ++workers_stopped;
66 wait_condition.notify_all();
67 };
68 threads.reserve(num_workers);
69 for (size_t i = 0; i < num_workers; ++i) {
70 threads.emplace_back(lambda);
71 }
72 }
73
74 StatefulThreadWorker& operator=(const StatefulThreadWorker&) = delete;
75 StatefulThreadWorker(const StatefulThreadWorker&) = delete;
76
77 StatefulThreadWorker& operator=(StatefulThreadWorker&&) = delete;
78 StatefulThreadWorker(StatefulThreadWorker&&) = delete;
79
80 void QueueWork(Task work) {
81 {
82 std::unique_lock lock{queue_mutex};
83 requests.emplace(std::move(work));
84 ++work_scheduled;
85 }
86 condition.notify_one();
87 }
88
89 void WaitForRequests(std::stop_token stop_token = {}) {
90 std::stop_callback callback(stop_token, [this] {
91 for (auto& thread : threads) {
92 thread.request_stop();
93 }
94 });
95 std::unique_lock lock{queue_mutex};
96 wait_condition.wait(lock, [this] {
97 return workers_stopped >= workers_queued || work_done >= work_scheduled;
98 });
99 }
21 100
22private: 101private:
23 std::vector<std::thread> threads; 102 std::queue<Task> requests;
24 std::queue<std::function<void()>> requests;
25 std::mutex queue_mutex; 103 std::mutex queue_mutex;
26 std::condition_variable condition; 104 std::condition_variable_any condition;
27 std::atomic_bool stop{}; 105 std::condition_variable wait_condition;
106 std::atomic<size_t> work_scheduled{};
107 std::atomic<size_t> work_done{};
108 std::atomic<size_t> workers_stopped{};
109 std::atomic<size_t> workers_queued{};
110 std::string thread_name;
111 std::vector<std::jthread> threads;
28}; 112};
29 113
114using ThreadWorker = StatefulThreadWorker<>;
115
30} // namespace Common 116} // namespace Common
diff --git a/src/common/unique_function.h b/src/common/unique_function.h
new file mode 100644
index 000000000..ca0559071
--- /dev/null
+++ b/src/common/unique_function.h
@@ -0,0 +1,62 @@
1// Copyright 2021 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <utility>
9
10namespace Common {
11
12/// General purpose function wrapper similar to std::function.
13/// Unlike std::function, the captured values don't have to be copyable.
14/// This class can be moved but not copied.
15template <typename ResultType, typename... Args>
16class UniqueFunction {
17 class CallableBase {
18 public:
19 virtual ~CallableBase() = default;
20 virtual ResultType operator()(Args&&...) = 0;
21 };
22
23 template <typename Functor>
24 class Callable final : public CallableBase {
25 public:
26 Callable(Functor&& functor_) : functor{std::move(functor_)} {}
27 ~Callable() override = default;
28
29 ResultType operator()(Args&&... args) override {
30 return functor(std::forward<Args>(args)...);
31 }
32
33 private:
34 Functor functor;
35 };
36
37public:
38 UniqueFunction() = default;
39
40 template <typename Functor>
41 UniqueFunction(Functor&& functor)
42 : callable{std::make_unique<Callable<Functor>>(std::move(functor))} {}
43
44 UniqueFunction& operator=(UniqueFunction&& rhs) noexcept = default;
45 UniqueFunction(UniqueFunction&& rhs) noexcept = default;
46
47 UniqueFunction& operator=(const UniqueFunction&) = delete;
48 UniqueFunction(const UniqueFunction&) = delete;
49
50 ResultType operator()(Args&&... args) const {
51 return (*callable)(std::forward<Args>(args)...);
52 }
53
54 explicit operator bool() const noexcept {
55 return static_cast<bool>(callable);
56 }
57
58private:
59 std::unique_ptr<CallableBase> callable;
60};
61
62} // namespace Common
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index 96bc30cac..c4c012f3d 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -5,6 +5,7 @@ add_executable(tests
5 common/host_memory.cpp 5 common/host_memory.cpp
6 common/param_package.cpp 6 common/param_package.cpp
7 common/ring_buffer.cpp 7 common/ring_buffer.cpp
8 common/unique_function.cpp
8 core/core_timing.cpp 9 core/core_timing.cpp
9 core/network/network.cpp 10 core/network/network.cpp
10 tests.cpp 11 tests.cpp
diff --git a/src/tests/common/unique_function.cpp b/src/tests/common/unique_function.cpp
new file mode 100644
index 000000000..ac9912738
--- /dev/null
+++ b/src/tests/common/unique_function.cpp
@@ -0,0 +1,108 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <string>
6
7#include <catch2/catch.hpp>
8
9#include "common/unique_function.h"
10
11namespace {
12struct Noisy {
13 Noisy() : state{"Default constructed"} {}
14 Noisy(Noisy&& rhs) noexcept : state{"Move constructed"} {
15 rhs.state = "Moved away";
16 }
17 Noisy& operator=(Noisy&& rhs) noexcept {
18 state = "Move assigned";
19 rhs.state = "Moved away";
20 }
21 Noisy(const Noisy&) : state{"Copied constructed"} {}
22 Noisy& operator=(const Noisy&) {
23 state = "Copied assigned";
24 }
25
26 std::string state;
27};
28} // Anonymous namespace
29
30TEST_CASE("UniqueFunction", "[common]") {
31 SECTION("Capture reference") {
32 int value = 0;
33 Common::UniqueFunction<void> func = [&value] { value = 5; };
34 func();
35 REQUIRE(value == 5);
36 }
37 SECTION("Capture pointer") {
38 int value = 0;
39 int* pointer = &value;
40 Common::UniqueFunction<void> func = [pointer] { *pointer = 5; };
41 func();
42 REQUIRE(value == 5);
43 }
44 SECTION("Move object") {
45 Noisy noisy;
46 REQUIRE(noisy.state == "Default constructed");
47
48 Common::UniqueFunction<void> func = [noisy = std::move(noisy)] {
49 REQUIRE(noisy.state == "Move constructed");
50 };
51 REQUIRE(noisy.state == "Moved away");
52 func();
53 }
54 SECTION("Move construct function") {
55 int value = 0;
56 Common::UniqueFunction<void> func = [&value] { value = 5; };
57 Common::UniqueFunction<void> new_func = std::move(func);
58 new_func();
59 REQUIRE(value == 5);
60 }
61 SECTION("Move assign function") {
62 int value = 0;
63 Common::UniqueFunction<void> func = [&value] { value = 5; };
64 Common::UniqueFunction<void> new_func;
65 new_func = std::move(func);
66 new_func();
67 REQUIRE(value == 5);
68 }
69 SECTION("Default construct then assign function") {
70 int value = 0;
71 Common::UniqueFunction<void> func;
72 func = [&value] { value = 5; };
73 func();
74 REQUIRE(value == 5);
75 }
76 SECTION("Pass arguments") {
77 int result = 0;
78 Common::UniqueFunction<void, int, int> func = [&result](int a, int b) { result = a + b; };
79 func(5, 4);
80 REQUIRE(result == 9);
81 }
82 SECTION("Pass arguments and return value") {
83 Common::UniqueFunction<int, int, int> func = [](int a, int b) { return a + b; };
84 REQUIRE(func(5, 4) == 9);
85 }
86 SECTION("Destructor") {
87 int num_destroyed = 0;
88 struct Foo {
89 Foo(int* num_) : num{num_} {}
90 Foo(Foo&& rhs) : num{std::exchange(rhs.num, nullptr)} {}
91 Foo(const Foo&) = delete;
92
93 ~Foo() {
94 if (num) {
95 ++*num;
96 }
97 }
98
99 int* num = nullptr;
100 };
101 Foo object{&num_destroyed};
102 {
103 Common::UniqueFunction<void> func = [object = std::move(object)] {};
104 REQUIRE(num_destroyed == 0);
105 }
106 REQUIRE(num_destroyed == 1);
107 }
108}
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index e3ed75fe3..87cb9dc93 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -827,7 +827,6 @@ void Config::ReadRendererValues() {
827 qt_config->beginGroup(QStringLiteral("Renderer")); 827 qt_config->beginGroup(QStringLiteral("Renderer"));
828 828
829 ReadGlobalSetting(Settings::values.renderer_backend); 829 ReadGlobalSetting(Settings::values.renderer_backend);
830 ReadBasicSetting(Settings::values.renderer_debug);
831 ReadGlobalSetting(Settings::values.vulkan_device); 830 ReadGlobalSetting(Settings::values.vulkan_device);
832 ReadGlobalSetting(Settings::values.fullscreen_mode); 831 ReadGlobalSetting(Settings::values.fullscreen_mode);
833 ReadGlobalSetting(Settings::values.aspect_ratio); 832 ReadGlobalSetting(Settings::values.aspect_ratio);
@@ -849,6 +848,10 @@ void Config::ReadRendererValues() {
849 ReadGlobalSetting(Settings::values.bg_green); 848 ReadGlobalSetting(Settings::values.bg_green);
850 ReadGlobalSetting(Settings::values.bg_blue); 849 ReadGlobalSetting(Settings::values.bg_blue);
851 850
851 if (global) {
852 ReadBasicSetting(Settings::values.renderer_debug);
853 }
854
852 qt_config->endGroup(); 855 qt_config->endGroup();
853} 856}
854 857
@@ -1351,7 +1354,6 @@ void Config::SaveRendererValues() {
1351 static_cast<u32>(Settings::values.renderer_backend.GetValue(global)), 1354 static_cast<u32>(Settings::values.renderer_backend.GetValue(global)),
1352 static_cast<u32>(Settings::values.renderer_backend.GetDefault()), 1355 static_cast<u32>(Settings::values.renderer_backend.GetDefault()),
1353 Settings::values.renderer_backend.UsingGlobal()); 1356 Settings::values.renderer_backend.UsingGlobal());
1354 WriteBasicSetting(Settings::values.renderer_debug);
1355 WriteGlobalSetting(Settings::values.vulkan_device); 1357 WriteGlobalSetting(Settings::values.vulkan_device);
1356 WriteGlobalSetting(Settings::values.fullscreen_mode); 1358 WriteGlobalSetting(Settings::values.fullscreen_mode);
1357 WriteGlobalSetting(Settings::values.aspect_ratio); 1359 WriteGlobalSetting(Settings::values.aspect_ratio);
@@ -1376,6 +1378,10 @@ void Config::SaveRendererValues() {
1376 WriteGlobalSetting(Settings::values.bg_green); 1378 WriteGlobalSetting(Settings::values.bg_green);
1377 WriteGlobalSetting(Settings::values.bg_blue); 1379 WriteGlobalSetting(Settings::values.bg_blue);
1378 1380
1381 if (global) {
1382 WriteBasicSetting(Settings::values.renderer_debug);
1383 }
1384
1379 qt_config->endGroup(); 1385 qt_config->endGroup();
1380} 1386}
1381 1387