Compare commits
56 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
25429998e3 | ||
|
|
bd3bfe411d | ||
|
|
963ed37fd6 | ||
|
|
741da9c8bf | ||
|
|
a0407a8e64 | ||
|
|
7582717c9d | ||
|
|
ec85eac3c9 | ||
|
|
fb4b507ba4 | ||
|
|
7ea78699a1 | ||
|
|
80ad90651e | ||
|
|
b94739cfa7 | ||
|
|
89e00c442d | ||
|
|
d796341d33 | ||
|
|
5282efac1b | ||
|
|
ae83d5c6d3 | ||
|
|
3370546a7a | ||
|
|
2ff606628c | ||
|
|
20576ebb43 | ||
|
|
6f81160160 | ||
|
|
266e086706 | ||
|
|
9561a2f5b1 | ||
|
|
bc8699a9fa | ||
|
|
c3cc65a11e | ||
|
|
1f0fee33ed | ||
|
|
de6c0defb3 | ||
|
|
6c659c3a16 | ||
|
|
af022294dd | ||
|
|
073714a762 | ||
|
|
4ae75bec50 | ||
|
|
31527ccd25 | ||
|
|
268878f895 | ||
|
|
d00b7be2d6 | ||
|
|
941b663352 | ||
|
|
708e5b027f | ||
|
|
c33c9c76bf | ||
|
|
888e814130 | ||
|
|
cad53179ed | ||
|
|
45bdbf538c | ||
|
|
4544407af6 | ||
|
|
2f2e443858 | ||
|
|
14db101148 | ||
|
|
4dd6bcd206 | ||
|
|
ea89cf8639 | ||
|
|
054732210e | ||
|
|
e609bc1c6a | ||
|
|
422525e3fb | ||
|
|
2dafb27055 | ||
|
|
500b01076e | ||
|
|
b43ae9d5ed | ||
|
|
f22867efc5 | ||
|
|
67fa743414 | ||
|
|
5799fa4d7d | ||
|
|
499c89790b | ||
|
|
75bf2c20eb | ||
|
|
017a18f42e | ||
|
|
669a9a644d |
13
dist/qt_themes/default/style.qss
vendored
13
dist/qt_themes/default/style.qss
vendored
@@ -58,6 +58,19 @@ QPushButton#GPUStatusBarButton:!checked {
|
||||
color: #109010;
|
||||
}
|
||||
|
||||
QPushButton#DockingStatusBarButton {
|
||||
min-width: 0px;
|
||||
color: #000000;
|
||||
border: 1px solid transparent;
|
||||
background-color: transparent;
|
||||
padding: 0px 3px 0px 3px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
QPushButton#DockingStatusBarButton:hover {
|
||||
border: 1px solid #76797C;
|
||||
}
|
||||
|
||||
QPushButton#buttonRefreshDevices {
|
||||
min-width: 21px;
|
||||
min-height: 21px;
|
||||
|
||||
13
dist/qt_themes/qdarkstyle/style.qss
vendored
13
dist/qt_themes/qdarkstyle/style.qss
vendored
@@ -1304,6 +1304,19 @@ QPushButton#GPUStatusBarButton:!checked {
|
||||
color: #40dd40;
|
||||
}
|
||||
|
||||
QPushButton#DockingStatusBarButton {
|
||||
min-width: 0px;
|
||||
color: #ffffff;
|
||||
border: 1px solid transparent;
|
||||
background-color: transparent;
|
||||
padding: 0px 3px 0px 3px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
QPushButton#DockingStatusBarButton:hover {
|
||||
border: 1px solid #76797C;
|
||||
}
|
||||
|
||||
QPushButton#buttonRefreshDevices {
|
||||
min-width: 23px;
|
||||
min-height: 23px;
|
||||
|
||||
@@ -2207,6 +2207,19 @@ QPushButton#GPUStatusBarButton:!checked {
|
||||
color: #40dd40;
|
||||
}
|
||||
|
||||
QPushButton#DockingStatusBarButton {
|
||||
min-width: 0px;
|
||||
color: #ffffff;
|
||||
border: 1px solid transparent;
|
||||
background-color: transparent;
|
||||
padding: 0px 3px 0px 3px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
QPushButton#DockingStatusBarButton:hover {
|
||||
border: 1px solid #76797C;
|
||||
}
|
||||
|
||||
QPushButton#buttonRefreshDevices {
|
||||
min-width: 19px;
|
||||
min-height: 19px;
|
||||
|
||||
167
src/common/bounded_threadsafe_queue.h
Normal file
167
src/common/bounded_threadsafe_queue.h
Normal file
@@ -0,0 +1,167 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2020 Erik Rigtorp <erik@rigtorp.se>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <bit>
|
||||
#include <condition_variable>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <new>
|
||||
#include <stop_token>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace Common {
|
||||
|
||||
#if defined(__cpp_lib_hardware_interference_size)
|
||||
constexpr size_t hardware_interference_size = std::hardware_destructive_interference_size;
|
||||
#else
|
||||
constexpr size_t hardware_interference_size = 64;
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4324)
|
||||
#endif
|
||||
|
||||
template <typename T, size_t capacity = 0x400>
|
||||
class MPSCQueue {
|
||||
public:
|
||||
explicit MPSCQueue() : allocator{std::allocator<Slot<T>>()} {
|
||||
// Allocate one extra slot to prevent false sharing on the last slot
|
||||
slots = allocator.allocate(capacity + 1);
|
||||
// Allocators are not required to honor alignment for over-aligned types
|
||||
// (see http://eel.is/c++draft/allocator.requirements#10) so we verify
|
||||
// alignment here
|
||||
if (reinterpret_cast<uintptr_t>(slots) % alignof(Slot<T>) != 0) {
|
||||
allocator.deallocate(slots, capacity + 1);
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
for (size_t i = 0; i < capacity; ++i) {
|
||||
std::construct_at(&slots[i]);
|
||||
}
|
||||
static_assert(std::has_single_bit(capacity), "capacity must be an integer power of 2");
|
||||
static_assert(alignof(Slot<T>) == hardware_interference_size,
|
||||
"Slot must be aligned to cache line boundary to prevent false sharing");
|
||||
static_assert(sizeof(Slot<T>) % hardware_interference_size == 0,
|
||||
"Slot size must be a multiple of cache line size to prevent "
|
||||
"false sharing between adjacent slots");
|
||||
static_assert(sizeof(MPSCQueue) % hardware_interference_size == 0,
|
||||
"Queue size must be a multiple of cache line size to "
|
||||
"prevent false sharing between adjacent queues");
|
||||
}
|
||||
|
||||
~MPSCQueue() noexcept {
|
||||
for (size_t i = 0; i < capacity; ++i) {
|
||||
std::destroy_at(&slots[i]);
|
||||
}
|
||||
allocator.deallocate(slots, capacity + 1);
|
||||
}
|
||||
|
||||
// The queue must be both non-copyable and non-movable
|
||||
MPSCQueue(const MPSCQueue&) = delete;
|
||||
MPSCQueue& operator=(const MPSCQueue&) = delete;
|
||||
|
||||
MPSCQueue(MPSCQueue&&) = delete;
|
||||
MPSCQueue& operator=(MPSCQueue&&) = delete;
|
||||
|
||||
void Push(const T& v) noexcept {
|
||||
static_assert(std::is_nothrow_copy_constructible_v<T>,
|
||||
"T must be nothrow copy constructible");
|
||||
emplace(v);
|
||||
}
|
||||
|
||||
template <typename P, typename = std::enable_if_t<std::is_nothrow_constructible_v<T, P&&>>>
|
||||
void Push(P&& v) noexcept {
|
||||
emplace(std::forward<P>(v));
|
||||
}
|
||||
|
||||
void Pop(T& v, std::stop_token stop) noexcept {
|
||||
auto const tail = tail_.fetch_add(1);
|
||||
auto& slot = slots[idx(tail)];
|
||||
if (!slot.turn.test()) {
|
||||
std::unique_lock lock{cv_mutex};
|
||||
cv.wait(lock, stop, [&slot] { return slot.turn.test(); });
|
||||
}
|
||||
v = slot.move();
|
||||
slot.destroy();
|
||||
slot.turn.clear();
|
||||
slot.turn.notify_one();
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename U = T>
|
||||
struct Slot {
|
||||
~Slot() noexcept {
|
||||
if (turn.test()) {
|
||||
destroy();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void construct(Args&&... args) noexcept {
|
||||
static_assert(std::is_nothrow_constructible_v<U, Args&&...>,
|
||||
"T must be nothrow constructible with Args&&...");
|
||||
std::construct_at(reinterpret_cast<U*>(&storage), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
void destroy() noexcept {
|
||||
static_assert(std::is_nothrow_destructible_v<U>, "T must be nothrow destructible");
|
||||
std::destroy_at(reinterpret_cast<U*>(&storage));
|
||||
}
|
||||
|
||||
U&& move() noexcept {
|
||||
return reinterpret_cast<U&&>(storage);
|
||||
}
|
||||
|
||||
// Align to avoid false sharing between adjacent slots
|
||||
alignas(hardware_interference_size) std::atomic_flag turn{};
|
||||
struct aligned_store {
|
||||
struct type {
|
||||
alignas(U) unsigned char data[sizeof(U)];
|
||||
};
|
||||
};
|
||||
typename aligned_store::type storage;
|
||||
};
|
||||
|
||||
template <typename... Args>
|
||||
void emplace(Args&&... args) noexcept {
|
||||
static_assert(std::is_nothrow_constructible_v<T, Args&&...>,
|
||||
"T must be nothrow constructible with Args&&...");
|
||||
auto const head = head_.fetch_add(1);
|
||||
auto& slot = slots[idx(head)];
|
||||
slot.turn.wait(true);
|
||||
slot.construct(std::forward<Args>(args)...);
|
||||
slot.turn.test_and_set();
|
||||
cv.notify_one();
|
||||
}
|
||||
|
||||
constexpr size_t idx(size_t i) const noexcept {
|
||||
return i & mask;
|
||||
}
|
||||
|
||||
static constexpr size_t mask = capacity - 1;
|
||||
|
||||
// Align to avoid false sharing between head_ and tail_
|
||||
alignas(hardware_interference_size) std::atomic<size_t> head_{0};
|
||||
alignas(hardware_interference_size) std::atomic<size_t> tail_{0};
|
||||
|
||||
std::mutex cv_mutex;
|
||||
std::condition_variable_any cv;
|
||||
|
||||
Slot<T>* slots;
|
||||
[[no_unique_address]] std::allocator<Slot<T>> allocator;
|
||||
|
||||
static_assert(std::is_nothrow_copy_assignable_v<T> || std::is_nothrow_move_assignable_v<T>,
|
||||
"T must be nothrow copy or move assignable");
|
||||
|
||||
static_assert(std::is_nothrow_destructible_v<T>, "T must be nothrow destructible");
|
||||
};
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
} // namespace Common
|
||||
@@ -496,7 +496,7 @@ struct Values {
|
||||
|
||||
// Renderer
|
||||
RangedSetting<RendererBackend> renderer_backend{
|
||||
RendererBackend::OpenGL, RendererBackend::OpenGL, RendererBackend::Vulkan, "backend"};
|
||||
RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Vulkan, "backend"};
|
||||
BasicSetting<bool> renderer_debug{false, "debug"};
|
||||
BasicSetting<bool> renderer_shader_feedback{false, "shader_feedback"};
|
||||
BasicSetting<bool> enable_nsight_aftermath{false, "nsight_aftermath"};
|
||||
|
||||
@@ -493,6 +493,12 @@ void System::Shutdown() {
|
||||
impl->Shutdown();
|
||||
}
|
||||
|
||||
void System::DetachDebugger() {
|
||||
if (impl->debugger) {
|
||||
impl->debugger->NotifyShutdown();
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> System::StallCPU() {
|
||||
return impl->StallCPU();
|
||||
}
|
||||
|
||||
@@ -160,6 +160,9 @@ public:
|
||||
/// Shutdown the emulated system.
|
||||
void Shutdown();
|
||||
|
||||
/// Forcibly detach the debugger if it is running.
|
||||
void DetachDebugger();
|
||||
|
||||
std::unique_lock<std::mutex> StallCPU();
|
||||
void UnstallCPU();
|
||||
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
|
||||
namespace Core {
|
||||
|
||||
CpuManager::CpuManager(System& system_) : system{system_} {}
|
||||
CpuManager::CpuManager(System& system_)
|
||||
: pause_barrier{std::make_unique<Common::Barrier>(1)}, system{system_} {}
|
||||
CpuManager::~CpuManager() = default;
|
||||
|
||||
void CpuManager::ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager,
|
||||
@@ -30,8 +31,10 @@ void CpuManager::Initialize() {
|
||||
for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
|
||||
core_data[core].host_thread = std::jthread(ThreadStart, std::ref(*this), core);
|
||||
}
|
||||
pause_barrier = std::make_unique<Common::Barrier>(Core::Hardware::NUM_CPU_CORES + 1);
|
||||
} else {
|
||||
core_data[0].host_thread = std::jthread(ThreadStart, std::ref(*this), 0);
|
||||
pause_barrier = std::make_unique<Common::Barrier>(2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,51 +141,14 @@ void CpuManager::MultiCoreRunSuspendThread() {
|
||||
auto core = kernel.CurrentPhysicalCoreIndex();
|
||||
auto& scheduler = *kernel.CurrentScheduler();
|
||||
Kernel::KThread* current_thread = scheduler.GetCurrentThread();
|
||||
current_thread->DisableDispatch();
|
||||
|
||||
Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[core].host_context);
|
||||
ASSERT(scheduler.ContextSwitchPending());
|
||||
ASSERT(core == kernel.CurrentPhysicalCoreIndex());
|
||||
scheduler.RescheduleCurrentCore();
|
||||
}
|
||||
}
|
||||
|
||||
void CpuManager::MultiCorePause(bool paused) {
|
||||
if (!paused) {
|
||||
bool all_not_barrier = false;
|
||||
while (!all_not_barrier) {
|
||||
all_not_barrier = true;
|
||||
for (const auto& data : core_data) {
|
||||
all_not_barrier &= !data.is_running.load() && data.initialized.load();
|
||||
}
|
||||
}
|
||||
for (auto& data : core_data) {
|
||||
data.enter_barrier->Set();
|
||||
}
|
||||
if (paused_state.load()) {
|
||||
bool all_barrier = false;
|
||||
while (!all_barrier) {
|
||||
all_barrier = true;
|
||||
for (const auto& data : core_data) {
|
||||
all_barrier &= data.is_paused.load() && data.initialized.load();
|
||||
}
|
||||
}
|
||||
for (auto& data : core_data) {
|
||||
data.exit_barrier->Set();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/// Wait until all cores are paused.
|
||||
bool all_barrier = false;
|
||||
while (!all_barrier) {
|
||||
all_barrier = true;
|
||||
for (const auto& data : core_data) {
|
||||
all_barrier &= data.is_paused.load() && data.initialized.load();
|
||||
}
|
||||
}
|
||||
/// Don't release the barrier
|
||||
}
|
||||
paused_state = paused;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// SingleCore ///
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
@@ -235,8 +201,9 @@ void CpuManager::SingleCoreRunSuspendThread() {
|
||||
auto core = kernel.GetCurrentHostThreadID();
|
||||
auto& scheduler = *kernel.CurrentScheduler();
|
||||
Kernel::KThread* current_thread = scheduler.GetCurrentThread();
|
||||
current_thread->DisableDispatch();
|
||||
|
||||
Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[0].host_context);
|
||||
ASSERT(scheduler.ContextSwitchPending());
|
||||
ASSERT(core == kernel.GetCurrentHostThreadID());
|
||||
scheduler.RescheduleCurrentCore();
|
||||
}
|
||||
@@ -274,37 +241,21 @@ void CpuManager::PreemptSingleCore(bool from_running_enviroment) {
|
||||
}
|
||||
}
|
||||
|
||||
void CpuManager::SingleCorePause(bool paused) {
|
||||
if (!paused) {
|
||||
bool all_not_barrier = false;
|
||||
while (!all_not_barrier) {
|
||||
all_not_barrier = !core_data[0].is_running.load() && core_data[0].initialized.load();
|
||||
}
|
||||
core_data[0].enter_barrier->Set();
|
||||
if (paused_state.load()) {
|
||||
bool all_barrier = false;
|
||||
while (!all_barrier) {
|
||||
all_barrier = core_data[0].is_paused.load() && core_data[0].initialized.load();
|
||||
}
|
||||
core_data[0].exit_barrier->Set();
|
||||
}
|
||||
} else {
|
||||
/// Wait until all cores are paused.
|
||||
bool all_barrier = false;
|
||||
while (!all_barrier) {
|
||||
all_barrier = core_data[0].is_paused.load() && core_data[0].initialized.load();
|
||||
}
|
||||
/// Don't release the barrier
|
||||
}
|
||||
paused_state = paused;
|
||||
}
|
||||
|
||||
void CpuManager::Pause(bool paused) {
|
||||
if (is_multicore) {
|
||||
MultiCorePause(paused);
|
||||
} else {
|
||||
SingleCorePause(paused);
|
||||
std::scoped_lock lk{pause_lock};
|
||||
|
||||
if (pause_state == paused) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the new state
|
||||
pause_state.store(paused);
|
||||
|
||||
// Wake up any waiting threads
|
||||
pause_state.notify_all();
|
||||
|
||||
// Wait for all threads to successfully change state before returning
|
||||
pause_barrier->Sync();
|
||||
}
|
||||
|
||||
void CpuManager::RunThread(std::stop_token stop_token, std::size_t core) {
|
||||
@@ -320,27 +271,29 @@ void CpuManager::RunThread(std::stop_token stop_token, std::size_t core) {
|
||||
Common::SetCurrentThreadName(name.c_str());
|
||||
Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
|
||||
auto& data = core_data[core];
|
||||
data.enter_barrier = std::make_unique<Common::Event>();
|
||||
data.exit_barrier = std::make_unique<Common::Event>();
|
||||
data.host_context = Common::Fiber::ThreadToFiber();
|
||||
data.is_running = false;
|
||||
data.initialized = true;
|
||||
const bool sc_sync = !is_async_gpu && !is_multicore;
|
||||
bool sc_sync_first_use = sc_sync;
|
||||
|
||||
// Cleanup
|
||||
SCOPE_EXIT({
|
||||
data.host_context->Exit();
|
||||
data.enter_barrier.reset();
|
||||
data.exit_barrier.reset();
|
||||
data.initialized = false;
|
||||
MicroProfileOnThreadExit();
|
||||
});
|
||||
|
||||
/// Running
|
||||
while (running_mode) {
|
||||
data.is_running = false;
|
||||
data.enter_barrier->Wait();
|
||||
if (pause_state.load(std::memory_order_relaxed)) {
|
||||
// Wait for caller to acknowledge pausing
|
||||
pause_barrier->Sync();
|
||||
|
||||
// Wait until unpaused
|
||||
pause_state.wait(true, std::memory_order_relaxed);
|
||||
|
||||
// Wait for caller to acknowledge unpausing
|
||||
pause_barrier->Sync();
|
||||
}
|
||||
|
||||
if (sc_sync_first_use) {
|
||||
system.GPU().ObtainContext();
|
||||
sc_sync_first_use = false;
|
||||
@@ -352,12 +305,7 @@ void CpuManager::RunThread(std::stop_token stop_token, std::size_t core) {
|
||||
}
|
||||
|
||||
auto current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread();
|
||||
data.is_running = true;
|
||||
Common::Fiber::YieldTo(data.host_context, *current_thread->GetHostContext());
|
||||
data.is_running = false;
|
||||
data.is_paused = true;
|
||||
data.exit_barrier->Wait();
|
||||
data.is_paused = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -69,13 +69,11 @@ private:
|
||||
void MultiCoreRunGuestLoop();
|
||||
void MultiCoreRunIdleThread();
|
||||
void MultiCoreRunSuspendThread();
|
||||
void MultiCorePause(bool paused);
|
||||
|
||||
void SingleCoreRunGuestThread();
|
||||
void SingleCoreRunGuestLoop();
|
||||
void SingleCoreRunIdleThread();
|
||||
void SingleCoreRunSuspendThread();
|
||||
void SingleCorePause(bool paused);
|
||||
|
||||
static void ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager, std::size_t core);
|
||||
|
||||
@@ -83,16 +81,13 @@ private:
|
||||
|
||||
struct CoreData {
|
||||
std::shared_ptr<Common::Fiber> host_context;
|
||||
std::unique_ptr<Common::Event> enter_barrier;
|
||||
std::unique_ptr<Common::Event> exit_barrier;
|
||||
std::atomic<bool> is_running;
|
||||
std::atomic<bool> is_paused;
|
||||
std::atomic<bool> initialized;
|
||||
std::jthread host_thread;
|
||||
};
|
||||
|
||||
std::atomic<bool> running_mode{};
|
||||
std::atomic<bool> paused_state{};
|
||||
std::atomic<bool> pause_state{};
|
||||
std::unique_ptr<Common::Barrier> pause_barrier{};
|
||||
std::mutex pause_lock{};
|
||||
|
||||
std::array<CoreData, Core::Hardware::NUM_CPU_CORES> core_data{};
|
||||
|
||||
|
||||
@@ -20,15 +20,16 @@ template <typename Readable, typename Buffer, typename Callback>
|
||||
static void AsyncReceiveInto(Readable& r, Buffer& buffer, Callback&& c) {
|
||||
static_assert(std::is_trivial_v<Buffer>);
|
||||
auto boost_buffer{boost::asio::buffer(&buffer, sizeof(Buffer))};
|
||||
r.async_read_some(boost_buffer, [&](const boost::system::error_code& error, size_t bytes_read) {
|
||||
if (!error.failed()) {
|
||||
const u8* buffer_start = reinterpret_cast<const u8*>(&buffer);
|
||||
std::span<const u8> received_data{buffer_start, buffer_start + bytes_read};
|
||||
c(received_data);
|
||||
}
|
||||
r.async_read_some(
|
||||
boost_buffer, [&, c](const boost::system::error_code& error, size_t bytes_read) {
|
||||
if (!error.failed()) {
|
||||
const u8* buffer_start = reinterpret_cast<const u8*>(&buffer);
|
||||
std::span<const u8> received_data{buffer_start, buffer_start + bytes_read};
|
||||
c(received_data);
|
||||
}
|
||||
|
||||
AsyncReceiveInto(r, buffer, c);
|
||||
});
|
||||
AsyncReceiveInto(r, buffer, c);
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Readable, typename Buffer>
|
||||
@@ -41,6 +42,16 @@ static std::span<const u8> ReceiveInto(Readable& r, Buffer& buffer) {
|
||||
return received_data;
|
||||
}
|
||||
|
||||
enum class SignalType {
|
||||
Stopped,
|
||||
ShuttingDown,
|
||||
};
|
||||
|
||||
struct SignalInfo {
|
||||
SignalType type;
|
||||
Kernel::KThread* thread;
|
||||
};
|
||||
|
||||
namespace Core {
|
||||
|
||||
class DebuggerImpl : public DebuggerBackend {
|
||||
@@ -55,7 +66,7 @@ public:
|
||||
ShutdownServer();
|
||||
}
|
||||
|
||||
bool NotifyThreadStopped(Kernel::KThread* thread) {
|
||||
bool SignalDebugger(SignalInfo signal_info) {
|
||||
std::scoped_lock lk{connection_lock};
|
||||
|
||||
if (stopped) {
|
||||
@@ -63,9 +74,13 @@ public:
|
||||
// It should be ignored.
|
||||
return false;
|
||||
}
|
||||
stopped = true;
|
||||
|
||||
signal_pipe.write_some(boost::asio::buffer(&thread, sizeof(thread)));
|
||||
// Set up the state.
|
||||
stopped = true;
|
||||
info = signal_info;
|
||||
|
||||
// Write a single byte into the pipe to wake up the debug interface.
|
||||
boost::asio::write(signal_pipe, boost::asio::buffer(&stopped, sizeof(stopped)));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -74,7 +89,7 @@ public:
|
||||
}
|
||||
|
||||
void WriteToClient(std::span<const u8> data) override {
|
||||
client_socket.write_some(boost::asio::buffer(data.data(), data.size_bytes()));
|
||||
boost::asio::write(client_socket, boost::asio::buffer(data.data(), data.size_bytes()));
|
||||
}
|
||||
|
||||
void SetActiveThread(Kernel::KThread* thread) override {
|
||||
@@ -95,7 +110,7 @@ private:
|
||||
connection_thread = std::jthread([&, port](std::stop_token stop_token) {
|
||||
try {
|
||||
// Initialize the listening socket and accept a new client.
|
||||
tcp::endpoint endpoint{boost::asio::ip::address_v4::loopback(), port};
|
||||
tcp::endpoint endpoint{boost::asio::ip::address_v4::any(), port};
|
||||
tcp::acceptor acceptor{io_context, endpoint};
|
||||
|
||||
acceptor.async_accept(client_socket, [](const auto&) {});
|
||||
@@ -123,7 +138,7 @@ private:
|
||||
Common::SetCurrentThreadName("yuzu:Debugger");
|
||||
|
||||
// Set up the client signals for new data.
|
||||
AsyncReceiveInto(signal_pipe, active_thread, [&](auto d) { PipeData(d); });
|
||||
AsyncReceiveInto(signal_pipe, pipe_data, [&](auto d) { PipeData(d); });
|
||||
AsyncReceiveInto(client_socket, client_data, [&](auto d) { ClientData(d); });
|
||||
|
||||
// Stop the emulated CPU.
|
||||
@@ -141,9 +156,28 @@ private:
|
||||
}
|
||||
|
||||
void PipeData(std::span<const u8> data) {
|
||||
AllCoreStop();
|
||||
UpdateActiveThread();
|
||||
frontend->Stopped(active_thread);
|
||||
switch (info.type) {
|
||||
case SignalType::Stopped:
|
||||
// Stop emulation.
|
||||
AllCoreStop();
|
||||
|
||||
// Notify the client.
|
||||
active_thread = info.thread;
|
||||
UpdateActiveThread();
|
||||
frontend->Stopped(active_thread);
|
||||
|
||||
break;
|
||||
case SignalType::ShuttingDown:
|
||||
frontend->ShuttingDown();
|
||||
|
||||
// Wait for emulation to shut down gracefully now.
|
||||
suspend.reset();
|
||||
signal_pipe.close();
|
||||
client_socket.shutdown(boost::asio::socket_base::shutdown_both);
|
||||
LOG_INFO(Debug_GDBStub, "Shut down server");
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ClientData(std::span<const u8> data) {
|
||||
@@ -245,7 +279,9 @@ private:
|
||||
boost::asio::ip::tcp::socket client_socket;
|
||||
std::optional<std::unique_lock<std::mutex>> suspend;
|
||||
|
||||
SignalInfo info;
|
||||
Kernel::KThread* active_thread;
|
||||
bool pipe_data;
|
||||
bool stopped;
|
||||
|
||||
std::array<u8, 4096> client_data;
|
||||
@@ -262,7 +298,13 @@ Debugger::Debugger(Core::System& system, u16 port) {
|
||||
Debugger::~Debugger() = default;
|
||||
|
||||
bool Debugger::NotifyThreadStopped(Kernel::KThread* thread) {
|
||||
return impl && impl->NotifyThreadStopped(thread);
|
||||
return impl && impl->SignalDebugger(SignalInfo{SignalType::Stopped, thread});
|
||||
}
|
||||
|
||||
void Debugger::NotifyShutdown() {
|
||||
if (impl) {
|
||||
impl->SignalDebugger(SignalInfo{SignalType::ShuttingDown, nullptr});
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -35,6 +35,11 @@ public:
|
||||
*/
|
||||
bool NotifyThreadStopped(Kernel::KThread* thread);
|
||||
|
||||
/**
|
||||
* Notify the debugger that a shutdown is being performed now and disconnect.
|
||||
*/
|
||||
void NotifyShutdown();
|
||||
|
||||
private:
|
||||
std::unique_ptr<DebuggerImpl> impl;
|
||||
};
|
||||
|
||||
@@ -66,6 +66,11 @@ public:
|
||||
*/
|
||||
virtual void Stopped(Kernel::KThread* thread) = 0;
|
||||
|
||||
/**
|
||||
* Called when emulation is shutting down.
|
||||
*/
|
||||
virtual void ShuttingDown() = 0;
|
||||
|
||||
/**
|
||||
* Called when new data is asynchronously received on the client socket.
|
||||
* A list of actions to perform is returned.
|
||||
|
||||
@@ -106,6 +106,8 @@ GDBStub::~GDBStub() = default;
|
||||
|
||||
void GDBStub::Connected() {}
|
||||
|
||||
void GDBStub::ShuttingDown() {}
|
||||
|
||||
void GDBStub::Stopped(Kernel::KThread* thread) {
|
||||
SendReply(arch->ThreadStatus(thread, GDB_STUB_SIGTRAP));
|
||||
}
|
||||
@@ -422,6 +424,18 @@ static std::string GetThreadState(const Kernel::KThread* thread) {
|
||||
}
|
||||
}
|
||||
|
||||
static std::string PaginateBuffer(std::string_view buffer, std::string_view request) {
|
||||
const auto amount{request.substr(request.find(',') + 1)};
|
||||
const auto offset_val{static_cast<u64>(strtoll(request.data(), nullptr, 16))};
|
||||
const auto amount_val{static_cast<u64>(strtoll(amount.data(), nullptr, 16))};
|
||||
|
||||
if (offset_val + amount_val > buffer.size()) {
|
||||
return fmt::format("l{}", buffer.substr(offset_val));
|
||||
} else {
|
||||
return fmt::format("m{}", buffer.substr(offset_val, amount_val));
|
||||
}
|
||||
}
|
||||
|
||||
void GDBStub::HandleQuery(std::string_view command) {
|
||||
if (command.starts_with("TStatus")) {
|
||||
// no tracepoint support
|
||||
@@ -430,18 +444,8 @@ void GDBStub::HandleQuery(std::string_view command) {
|
||||
SendReply("PacketSize=4000;qXfer:features:read+;qXfer:threads:read+;qXfer:libraries:read+;"
|
||||
"vContSupported+;QStartNoAckMode+");
|
||||
} else if (command.starts_with("Xfer:features:read:target.xml:")) {
|
||||
const auto offset{command.substr(30)};
|
||||
const auto amount{command.substr(command.find(',') + 1)};
|
||||
|
||||
const auto offset_val{static_cast<u64>(strtoll(offset.data(), nullptr, 16))};
|
||||
const auto amount_val{static_cast<u64>(strtoll(amount.data(), nullptr, 16))};
|
||||
const auto target_xml{arch->GetTargetXML()};
|
||||
|
||||
if (offset_val + amount_val > target_xml.size()) {
|
||||
SendReply("l" + target_xml.substr(offset_val));
|
||||
} else {
|
||||
SendReply("m" + target_xml.substr(offset_val, amount_val));
|
||||
}
|
||||
SendReply(PaginateBuffer(target_xml, command.substr(30)));
|
||||
} else if (command.starts_with("Offsets")) {
|
||||
Loader::AppLoader::Modules modules;
|
||||
system.GetAppLoader().ReadNSOModules(modules);
|
||||
@@ -454,6 +458,20 @@ void GDBStub::HandleQuery(std::string_view command) {
|
||||
SendReply(fmt::format("TextSeg={:x}",
|
||||
system.CurrentProcess()->PageTable().GetCodeRegionStart()));
|
||||
}
|
||||
} else if (command.starts_with("Xfer:libraries:read::")) {
|
||||
Loader::AppLoader::Modules modules;
|
||||
system.GetAppLoader().ReadNSOModules(modules);
|
||||
|
||||
std::string buffer;
|
||||
buffer += R"(<?xml version="1.0"?>)";
|
||||
buffer += "<library-list>";
|
||||
for (const auto& [base, name] : modules) {
|
||||
buffer += fmt::format(R"(<library name="{}"><segment address="{:#x}"/></library>)",
|
||||
EscapeXML(name), base);
|
||||
}
|
||||
buffer += "</library-list>";
|
||||
|
||||
SendReply(PaginateBuffer(buffer, command.substr(21)));
|
||||
} else if (command.starts_with("fThreadInfo")) {
|
||||
// beginning of list
|
||||
const auto& threads = system.GlobalSchedulerContext().GetThreadList();
|
||||
@@ -484,17 +502,7 @@ void GDBStub::HandleQuery(std::string_view command) {
|
||||
|
||||
buffer += "</threads>";
|
||||
|
||||
const auto offset{command.substr(19)};
|
||||
const auto amount{command.substr(command.find(',') + 1)};
|
||||
|
||||
const auto offset_val{static_cast<u64>(strtoll(offset.data(), nullptr, 16))};
|
||||
const auto amount_val{static_cast<u64>(strtoll(amount.data(), nullptr, 16))};
|
||||
|
||||
if (offset_val + amount_val > buffer.size()) {
|
||||
SendReply("l" + buffer.substr(offset_val));
|
||||
} else {
|
||||
SendReply("m" + buffer.substr(offset_val, amount_val));
|
||||
}
|
||||
SendReply(PaginateBuffer(buffer, command.substr(19)));
|
||||
} else if (command.starts_with("Attached")) {
|
||||
SendReply("0");
|
||||
} else if (command.starts_with("StartNoAckMode")) {
|
||||
|
||||
@@ -23,6 +23,7 @@ public:
|
||||
|
||||
void Connected() override;
|
||||
void Stopped(Kernel::KThread* thread) override;
|
||||
void ShuttingDown() override;
|
||||
std::vector<DebuggerAction> ClientData(std::span<const u8> data) override;
|
||||
|
||||
private:
|
||||
|
||||
@@ -15,6 +15,7 @@ namespace Core {
|
||||
|
||||
class GDBStubArch {
|
||||
public:
|
||||
virtual ~GDBStubArch() = default;
|
||||
virtual std::string GetTargetXML() const = 0;
|
||||
virtual std::string RegRead(const Kernel::KThread* thread, size_t id) const = 0;
|
||||
virtual void RegWrite(Kernel::KThread* thread, size_t id, std::string_view value) const = 0;
|
||||
|
||||
@@ -27,23 +27,18 @@ ResultCode KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr
|
||||
auto& page_table = m_owner->PageTable();
|
||||
|
||||
// Construct the page group.
|
||||
m_page_group =
|
||||
KPageLinkedList(page_table.GetPhysicalAddr(addr), Common::DivideUp(size, PageSize));
|
||||
m_page_group = {};
|
||||
|
||||
// Lock the memory.
|
||||
R_TRY(page_table.LockForCodeMemory(addr, size))
|
||||
R_TRY(page_table.LockForCodeMemory(&m_page_group, addr, size))
|
||||
|
||||
// Clear the memory.
|
||||
//
|
||||
// FIXME: this ends up clobbering address ranges outside the scope of the mapping within
|
||||
// guest memory, and is not specifically required if the guest program is correctly
|
||||
// written, so disable until this is further investigated.
|
||||
//
|
||||
// for (const auto& block : m_page_group.Nodes()) {
|
||||
// std::memset(device_memory.GetPointer(block.GetAddress()), 0xFF, block.GetSize());
|
||||
// }
|
||||
for (const auto& block : m_page_group.Nodes()) {
|
||||
std::memset(device_memory.GetPointer(block.GetAddress()), 0xFF, block.GetSize());
|
||||
}
|
||||
|
||||
// Set remaining tracking members.
|
||||
m_owner->Open();
|
||||
m_address = addr;
|
||||
m_is_initialized = true;
|
||||
m_is_owner_mapped = false;
|
||||
@@ -57,8 +52,14 @@ void KCodeMemory::Finalize() {
|
||||
// Unlock.
|
||||
if (!m_is_mapped && !m_is_owner_mapped) {
|
||||
const size_t size = m_page_group.GetNumPages() * PageSize;
|
||||
m_owner->PageTable().UnlockForCodeMemory(m_address, size);
|
||||
m_owner->PageTable().UnlockForCodeMemory(m_address, size, m_page_group);
|
||||
}
|
||||
|
||||
// Close the page group.
|
||||
m_page_group = {};
|
||||
|
||||
// Close our reference to our owner.
|
||||
m_owner->Close();
|
||||
}
|
||||
|
||||
ResultCode KCodeMemory::Map(VAddr address, size_t size) {
|
||||
@@ -118,7 +119,8 @@ ResultCode KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermis
|
||||
k_perm = KMemoryPermission::UserReadExecute;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
// Already validated by ControlCodeMemory svc
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
// Map the memory.
|
||||
|
||||
@@ -542,6 +542,95 @@ ResultCode KPageTable::MakePageGroup(KPageLinkedList& pg, VAddr addr, size_t num
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
bool KPageTable::IsValidPageGroup(const KPageLinkedList& pg_ll, VAddr addr, size_t num_pages) {
|
||||
ASSERT(this->IsLockedByCurrentThread());
|
||||
|
||||
const size_t size = num_pages * PageSize;
|
||||
const auto& pg = pg_ll.Nodes();
|
||||
const auto& memory_layout = system.Kernel().MemoryLayout();
|
||||
|
||||
// Empty groups are necessarily invalid.
|
||||
if (pg.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We're going to validate that the group we'd expect is the group we see.
|
||||
auto cur_it = pg.begin();
|
||||
PAddr cur_block_address = cur_it->GetAddress();
|
||||
size_t cur_block_pages = cur_it->GetNumPages();
|
||||
|
||||
auto UpdateCurrentIterator = [&]() {
|
||||
if (cur_block_pages == 0) {
|
||||
if ((++cur_it) == pg.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
cur_block_address = cur_it->GetAddress();
|
||||
cur_block_pages = cur_it->GetNumPages();
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
// Begin traversal.
|
||||
Common::PageTable::TraversalContext context;
|
||||
Common::PageTable::TraversalEntry next_entry;
|
||||
if (!page_table_impl.BeginTraversal(next_entry, context, addr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prepare tracking variables.
|
||||
PAddr cur_addr = next_entry.phys_addr;
|
||||
size_t cur_size = next_entry.block_size - (cur_addr & (next_entry.block_size - 1));
|
||||
size_t tot_size = cur_size;
|
||||
|
||||
// Iterate, comparing expected to actual.
|
||||
while (tot_size < size) {
|
||||
if (!page_table_impl.ContinueTraversal(next_entry, context)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (next_entry.phys_addr != (cur_addr + cur_size)) {
|
||||
const size_t cur_pages = cur_size / PageSize;
|
||||
|
||||
if (!IsHeapPhysicalAddress(memory_layout, cur_addr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!UpdateCurrentIterator()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cur_block_address != cur_addr || cur_block_pages < cur_pages) {
|
||||
return false;
|
||||
}
|
||||
|
||||
cur_block_address += cur_size;
|
||||
cur_block_pages -= cur_pages;
|
||||
cur_addr = next_entry.phys_addr;
|
||||
cur_size = next_entry.block_size;
|
||||
} else {
|
||||
cur_size += next_entry.block_size;
|
||||
}
|
||||
|
||||
tot_size += next_entry.block_size;
|
||||
}
|
||||
|
||||
// Ensure we compare the right amount for the last block.
|
||||
if (tot_size > size) {
|
||||
cur_size -= (tot_size - size);
|
||||
}
|
||||
|
||||
if (!IsHeapPhysicalAddress(memory_layout, cur_addr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!UpdateCurrentIterator()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return cur_block_address == cur_addr && cur_block_pages == (cur_size / PageSize);
|
||||
}
|
||||
|
||||
ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size,
|
||||
KPageTable& src_page_table, VAddr src_addr) {
|
||||
KScopedLightLock lk(general_lock);
|
||||
@@ -1687,22 +1776,22 @@ ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size)
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) {
|
||||
ResultCode KPageTable::LockForCodeMemory(KPageLinkedList* out, VAddr addr, std::size_t size) {
|
||||
return this->LockMemoryAndOpen(
|
||||
nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
|
||||
KMemoryState::FlagCanCodeMemory, KMemoryPermission::All, KMemoryPermission::UserReadWrite,
|
||||
KMemoryAttribute::All, KMemoryAttribute::None,
|
||||
out, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, KMemoryState::FlagCanCodeMemory,
|
||||
KMemoryPermission::All, KMemoryPermission::UserReadWrite, KMemoryAttribute::All,
|
||||
KMemoryAttribute::None,
|
||||
static_cast<KMemoryPermission>(KMemoryPermission::NotMapped |
|
||||
KMemoryPermission::KernelReadWrite),
|
||||
KMemoryAttribute::Locked);
|
||||
}
|
||||
|
||||
ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size) {
|
||||
return this->UnlockMemory(addr, size, KMemoryState::FlagCanCodeMemory,
|
||||
KMemoryState::FlagCanCodeMemory, KMemoryPermission::None,
|
||||
KMemoryPermission::None, KMemoryAttribute::All,
|
||||
KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite,
|
||||
KMemoryAttribute::Locked, nullptr);
|
||||
ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size,
|
||||
const KPageLinkedList& pg) {
|
||||
return this->UnlockMemory(
|
||||
addr, size, KMemoryState::FlagCanCodeMemory, KMemoryState::FlagCanCodeMemory,
|
||||
KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::All,
|
||||
KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite, KMemoryAttribute::Locked, &pg);
|
||||
}
|
||||
|
||||
ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) {
|
||||
@@ -2125,7 +2214,7 @@ ResultCode KPageTable::UnlockMemory(VAddr addr, size_t size, KMemoryState state_
|
||||
|
||||
// Check the page group.
|
||||
if (pg != nullptr) {
|
||||
UNIMPLEMENTED_MSG("PageGroup support is unimplemented!");
|
||||
R_UNLESS(this->IsValidPageGroup(*pg, addr, num_pages), ResultInvalidMemoryRegion);
|
||||
}
|
||||
|
||||
// Decide on new perm and attr.
|
||||
|
||||
@@ -72,8 +72,8 @@ public:
|
||||
KMemoryPermission perm, PAddr map_addr = 0);
|
||||
ResultCode LockForDeviceAddressSpace(VAddr addr, std::size_t size);
|
||||
ResultCode UnlockForDeviceAddressSpace(VAddr addr, std::size_t size);
|
||||
ResultCode LockForCodeMemory(VAddr addr, std::size_t size);
|
||||
ResultCode UnlockForCodeMemory(VAddr addr, std::size_t size);
|
||||
ResultCode LockForCodeMemory(KPageLinkedList* out, VAddr addr, std::size_t size);
|
||||
ResultCode UnlockForCodeMemory(VAddr addr, std::size_t size, const KPageLinkedList& pg);
|
||||
ResultCode MakeAndOpenPageGroup(KPageLinkedList* out, VAddr address, size_t num_pages,
|
||||
KMemoryState state_mask, KMemoryState state,
|
||||
KMemoryPermission perm_mask, KMemoryPermission perm,
|
||||
@@ -178,6 +178,7 @@ private:
|
||||
const KPageLinkedList* pg);
|
||||
|
||||
ResultCode MakePageGroup(KPageLinkedList& pg, VAddr addr, size_t num_pages);
|
||||
bool IsValidPageGroup(const KPageLinkedList& pg, VAddr addr, size_t num_pages);
|
||||
|
||||
bool IsLockedByCurrentThread() const {
|
||||
return general_lock.IsLockedByCurrentThread();
|
||||
|
||||
@@ -252,6 +252,7 @@ struct KernelCore::Impl {
|
||||
core_id)
|
||||
.IsSuccess());
|
||||
suspend_threads[core_id]->SetName(fmt::format("SuspendThread:{}", core_id));
|
||||
suspend_threads[core_id]->DisableDispatch();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1073,9 +1074,6 @@ void KernelCore::Suspend(bool in_suspention) {
|
||||
impl->suspend_threads[core_id]->SetState(state);
|
||||
impl->suspend_threads[core_id]->SetWaitReasonForDebugging(
|
||||
ThreadWaitReasonForDebugging::Suspended);
|
||||
if (!should_suspend) {
|
||||
impl->suspend_threads[core_id]->DisableDispatch();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
|
||||
}
|
||||
|
||||
last_update_timestamp = shared_memory->gesture_lifo.timestamp;
|
||||
UpdateGestureSharedMemory(gesture, time_difference);
|
||||
}
|
||||
|
||||
void Controller_Gesture::ReadTouchInput() {
|
||||
@@ -94,8 +95,7 @@ bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture,
|
||||
return false;
|
||||
}
|
||||
|
||||
void Controller_Gesture::UpdateGestureSharedMemory(u8* data, std::size_t size,
|
||||
GestureProperties& gesture,
|
||||
void Controller_Gesture::UpdateGestureSharedMemory(GestureProperties& gesture,
|
||||
f32 time_difference) {
|
||||
GestureType type = GestureType::Idle;
|
||||
GestureAttribute attributes{};
|
||||
|
||||
@@ -107,8 +107,7 @@ private:
|
||||
bool ShouldUpdateGesture(const GestureProperties& gesture, f32 time_difference);
|
||||
|
||||
// Updates the shared memory to the next state
|
||||
void UpdateGestureSharedMemory(u8* data, std::size_t size, GestureProperties& gesture,
|
||||
f32 time_difference);
|
||||
void UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_difference);
|
||||
|
||||
// Initializes new gesture
|
||||
void NewGesture(GestureProperties& gesture, GestureType& type, GestureAttribute& attributes);
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/k_shared_memory.h"
|
||||
#include "core/hle/kernel/k_transfer_memory.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/service/hid/errors.h"
|
||||
#include "core/hle/service/hid/irs.h"
|
||||
|
||||
namespace Service::HID {
|
||||
@@ -38,21 +40,32 @@ IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} {
|
||||
}
|
||||
|
||||
void IRS::ActivateIrsensor(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_IRS, "(STUBBED) called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||
|
||||
LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
|
||||
applet_resource_user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void IRS::DeactivateIrsensor(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_IRS, "(STUBBED) called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||
|
||||
LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
|
||||
applet_resource_user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void IRS::GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_IRS, "called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||
|
||||
LOG_DEBUG(Service_IRS, "called, applet_resource_user_id={}", applet_resource_user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
@@ -60,35 +73,109 @@ void IRS::GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
void IRS::StopImageProcessor(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_IRS, "(STUBBED) called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
IrCameraHandle camera_handle;
|
||||
INSERT_PADDING_WORDS_NOINIT(1);
|
||||
u64 applet_resource_user_id;
|
||||
};
|
||||
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
|
||||
LOG_WARNING(Service_IRS,
|
||||
"(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
|
||||
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
|
||||
parameters.applet_resource_user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void IRS::RunMomentProcessor(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_IRS, "(STUBBED) called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
IrCameraHandle camera_handle;
|
||||
INSERT_PADDING_WORDS_NOINIT(1);
|
||||
u64 applet_resource_user_id;
|
||||
PackedMomentProcessorConfig processor_config;
|
||||
};
|
||||
static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size.");
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
|
||||
LOG_WARNING(Service_IRS,
|
||||
"(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
|
||||
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
|
||||
parameters.applet_resource_user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void IRS::RunClusteringProcessor(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_IRS, "(STUBBED) called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
IrCameraHandle camera_handle;
|
||||
INSERT_PADDING_WORDS_NOINIT(1);
|
||||
u64 applet_resource_user_id;
|
||||
PackedClusteringProcessorConfig processor_config;
|
||||
};
|
||||
static_assert(sizeof(Parameters) == 0x40, "Parameters has incorrect size.");
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
|
||||
LOG_WARNING(Service_IRS,
|
||||
"(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
|
||||
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
|
||||
parameters.applet_resource_user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void IRS::RunImageTransferProcessor(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_IRS, "(STUBBED) called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
IrCameraHandle camera_handle;
|
||||
INSERT_PADDING_WORDS_NOINIT(1);
|
||||
u64 applet_resource_user_id;
|
||||
PackedImageTransferProcessorConfig processor_config;
|
||||
u32 transfer_memory_size;
|
||||
};
|
||||
static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size.");
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
const auto t_mem_handle{ctx.GetCopyHandle(0)};
|
||||
|
||||
auto t_mem =
|
||||
system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle);
|
||||
|
||||
LOG_WARNING(Service_IRS,
|
||||
"(STUBBED) called, npad_type={}, npad_id={}, transfer_memory_size={}, "
|
||||
"applet_resource_user_id={}",
|
||||
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
|
||||
parameters.transfer_memory_size, parameters.applet_resource_user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_IRS, "(STUBBED) called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
IrCameraHandle camera_handle;
|
||||
INSERT_PADDING_WORDS_NOINIT(1);
|
||||
u64 applet_resource_user_id;
|
||||
};
|
||||
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
|
||||
LOG_WARNING(Service_IRS,
|
||||
"(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
|
||||
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
|
||||
parameters.applet_resource_user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 5};
|
||||
rb.Push(ResultSuccess);
|
||||
@@ -97,71 +184,195 @@ void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
void IRS::RunTeraPluginProcessor(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_IRS, "(STUBBED) called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto camera_handle{rp.PopRaw<IrCameraHandle>()};
|
||||
const auto processor_config{rp.PopRaw<PackedTeraPluginProcessorConfig>()};
|
||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||
|
||||
LOG_WARNING(Service_IRS,
|
||||
"(STUBBED) called, npad_type={}, npad_id={}, mode={}, mcu_version={}.{}, "
|
||||
"applet_resource_user_id={}",
|
||||
camera_handle.npad_type, camera_handle.npad_id, processor_config.mode,
|
||||
processor_config.required_mcu_version.major,
|
||||
processor_config.required_mcu_version.minor, applet_resource_user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void IRS::GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_IRS, "(STUBBED) called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto npad_id{rp.PopEnum<Core::HID::NpadIdType>()};
|
||||
|
||||
if (npad_id > Core::HID::NpadIdType::Player8 && npad_id != Core::HID::NpadIdType::Invalid &&
|
||||
npad_id != Core::HID::NpadIdType::Handheld) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(InvalidNpadId);
|
||||
return;
|
||||
}
|
||||
|
||||
IrCameraHandle camera_handle{
|
||||
.npad_id = static_cast<u8>(NpadIdTypeToIndex(npad_id)),
|
||||
.npad_type = Core::HID::NpadStyleIndex::None,
|
||||
};
|
||||
|
||||
LOG_WARNING(Service_IRS, "(STUBBED) called, npad_id={}, camera_npad_id={}, camera_npad_type={}",
|
||||
npad_id, camera_handle.npad_id, camera_handle.npad_type);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushRaw<u32>(device_handle);
|
||||
rb.PushRaw(camera_handle);
|
||||
}
|
||||
|
||||
void IRS::RunPointingProcessor(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_IRS, "(STUBBED) called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto camera_handle{rp.PopRaw<IrCameraHandle>()};
|
||||
const auto processor_config{rp.PopRaw<PackedPointingProcessorConfig>()};
|
||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||
|
||||
LOG_WARNING(
|
||||
Service_IRS,
|
||||
"(STUBBED) called, npad_type={}, npad_id={}, mcu_version={}.{}, applet_resource_user_id={}",
|
||||
camera_handle.npad_type, camera_handle.npad_id, processor_config.required_mcu_version.major,
|
||||
processor_config.required_mcu_version.minor, applet_resource_user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void IRS::SuspendImageProcessor(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_IRS, "(STUBBED) called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
IrCameraHandle camera_handle;
|
||||
INSERT_PADDING_WORDS_NOINIT(1);
|
||||
u64 applet_resource_user_id;
|
||||
};
|
||||
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
|
||||
LOG_WARNING(Service_IRS,
|
||||
"(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
|
||||
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
|
||||
parameters.applet_resource_user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void IRS::CheckFirmwareVersion(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_IRS, "(STUBBED) called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto camera_handle{rp.PopRaw<IrCameraHandle>()};
|
||||
const auto mcu_version{rp.PopRaw<PackedMcuVersion>()};
|
||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||
|
||||
LOG_WARNING(
|
||||
Service_IRS,
|
||||
"(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}, mcu_version={}.{}",
|
||||
camera_handle.npad_type, camera_handle.npad_id, applet_resource_user_id, mcu_version.major,
|
||||
mcu_version.minor);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void IRS::SetFunctionLevel(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_IRS, "(STUBBED) called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
IrCameraHandle camera_handle;
|
||||
PackedFunctionLevel function_level;
|
||||
u64 applet_resource_user_id;
|
||||
};
|
||||
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
|
||||
LOG_WARNING(Service_IRS,
|
||||
"(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
|
||||
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
|
||||
parameters.applet_resource_user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void IRS::RunImageTransferExProcessor(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_IRS, "(STUBBED) called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
IrCameraHandle camera_handle;
|
||||
INSERT_PADDING_WORDS_NOINIT(1);
|
||||
u64 applet_resource_user_id;
|
||||
PackedImageTransferProcessorExConfig processor_config;
|
||||
u64 transfer_memory_size;
|
||||
};
|
||||
static_assert(sizeof(Parameters) == 0x38, "Parameters has incorrect size.");
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
const auto t_mem_handle{ctx.GetCopyHandle(0)};
|
||||
|
||||
auto t_mem =
|
||||
system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle);
|
||||
|
||||
LOG_WARNING(Service_IRS,
|
||||
"(STUBBED) called, npad_type={}, npad_id={}, transfer_memory_size={}, "
|
||||
"applet_resource_user_id={}",
|
||||
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
|
||||
parameters.transfer_memory_size, parameters.applet_resource_user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void IRS::RunIrLedProcessor(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_IRS, "(STUBBED) called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto camera_handle{rp.PopRaw<IrCameraHandle>()};
|
||||
const auto processor_config{rp.PopRaw<PackedIrLedProcessorConfig>()};
|
||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||
|
||||
LOG_WARNING(Service_IRS,
|
||||
"(STUBBED) called, npad_type={}, npad_id={}, light_target={}, mcu_version={}.{} "
|
||||
"applet_resource_user_id={}",
|
||||
camera_handle.npad_type, camera_handle.npad_id, processor_config.light_target,
|
||||
processor_config.required_mcu_version.major,
|
||||
processor_config.required_mcu_version.minor, applet_resource_user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void IRS::StopImageProcessorAsync(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_IRS, "(STUBBED) called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
IrCameraHandle camera_handle;
|
||||
INSERT_PADDING_WORDS_NOINIT(1);
|
||||
u64 applet_resource_user_id;
|
||||
};
|
||||
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
|
||||
LOG_WARNING(Service_IRS,
|
||||
"(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
|
||||
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
|
||||
parameters.applet_resource_user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void IRS::ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_IRS, "(STUBBED) called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
PackedFunctionLevel function_level;
|
||||
INSERT_PADDING_WORDS_NOINIT(1);
|
||||
u64 applet_resource_user_id;
|
||||
};
|
||||
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
|
||||
LOG_WARNING(Service_IRS, "(STUBBED) called, function_level={}, applet_resource_user_id={}",
|
||||
parameters.function_level.function_level, parameters.applet_resource_user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hid/hid_types.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Core {
|
||||
@@ -17,6 +18,235 @@ public:
|
||||
~IRS() override;
|
||||
|
||||
private:
|
||||
// This is nn::irsensor::IrCameraStatus
|
||||
enum IrCameraStatus : u32 {
|
||||
Available,
|
||||
Unsupported,
|
||||
Unconnected,
|
||||
};
|
||||
|
||||
// This is nn::irsensor::IrCameraInternalStatus
|
||||
enum IrCameraInternalStatus : u32 {
|
||||
Stopped,
|
||||
FirmwareUpdateNeeded,
|
||||
Unkown2,
|
||||
Unkown3,
|
||||
Unkown4,
|
||||
FirmwareVersionRequested,
|
||||
FirmwareVersionIsInvalid,
|
||||
Ready,
|
||||
Setting,
|
||||
};
|
||||
|
||||
// This is nn::irsensor::detail::StatusManager::IrSensorMode
|
||||
enum IrSensorMode : u64 {
|
||||
None,
|
||||
MomentProcessor,
|
||||
ClusteringProcessor,
|
||||
ImageTransferProcessor,
|
||||
PointingProcessorMarker,
|
||||
TeraPluginProcessor,
|
||||
IrLedProcessor,
|
||||
};
|
||||
|
||||
// This is nn::irsensor::ImageProcessorStatus
|
||||
enum ImageProcessorStatus : u8 {
|
||||
stopped,
|
||||
running,
|
||||
};
|
||||
|
||||
// This is nn::irsensor::ImageTransferProcessorFormat
|
||||
enum ImageTransferProcessorFormat : u8 {
|
||||
Size320x240,
|
||||
Size160x120,
|
||||
Size80x60,
|
||||
Size40x30,
|
||||
Size20x15,
|
||||
};
|
||||
|
||||
// This is nn::irsensor::AdaptiveClusteringMode
|
||||
enum AdaptiveClusteringMode : u8 {
|
||||
StaticFov,
|
||||
DynamicFov,
|
||||
};
|
||||
|
||||
// This is nn::irsensor::AdaptiveClusteringTargetDistance
|
||||
enum AdaptiveClusteringTargetDistance : u8 {
|
||||
Near,
|
||||
Middle,
|
||||
Far,
|
||||
};
|
||||
|
||||
// This is nn::irsensor::IrsHandAnalysisMode
|
||||
enum IrsHandAnalysisMode : u8 {
|
||||
Silhouette,
|
||||
Image,
|
||||
SilhoueteAndImage,
|
||||
SilhuetteOnly,
|
||||
};
|
||||
|
||||
// This is nn::irsensor::IrSensorFunctionLevel
|
||||
enum IrSensorFunctionLevel : u8 {
|
||||
unknown0,
|
||||
unknown1,
|
||||
unknown2,
|
||||
unknown3,
|
||||
unknown4,
|
||||
};
|
||||
|
||||
// This is nn::irsensor::IrCameraHandle
|
||||
struct IrCameraHandle {
|
||||
u8 npad_id{};
|
||||
Core::HID::NpadStyleIndex npad_type{Core::HID::NpadStyleIndex::None};
|
||||
INSERT_PADDING_BYTES(2);
|
||||
};
|
||||
static_assert(sizeof(IrCameraHandle) == 4, "IrCameraHandle is an invalid size");
|
||||
|
||||
struct IrsRect {
|
||||
s16 x;
|
||||
s16 y;
|
||||
s16 width;
|
||||
s16 height;
|
||||
};
|
||||
|
||||
// This is nn::irsensor::PackedMcuVersion
|
||||
struct PackedMcuVersion {
|
||||
u16 major;
|
||||
u16 minor;
|
||||
};
|
||||
static_assert(sizeof(PackedMcuVersion) == 4, "PackedMcuVersion is an invalid size");
|
||||
|
||||
// This is nn::irsensor::MomentProcessorConfig
|
||||
struct MomentProcessorConfig {
|
||||
u64 exposire_time;
|
||||
u8 light_target;
|
||||
u8 gain;
|
||||
u8 is_negative_used;
|
||||
INSERT_PADDING_BYTES(7);
|
||||
IrsRect window_of_interest;
|
||||
u8 preprocess;
|
||||
u8 preprocess_intensity_threshold;
|
||||
INSERT_PADDING_BYTES(5);
|
||||
};
|
||||
static_assert(sizeof(MomentProcessorConfig) == 0x28,
|
||||
"MomentProcessorConfig is an invalid size");
|
||||
|
||||
// This is nn::irsensor::PackedMomentProcessorConfig
|
||||
struct PackedMomentProcessorConfig {
|
||||
u64 exposire_time;
|
||||
u8 light_target;
|
||||
u8 gain;
|
||||
u8 is_negative_used;
|
||||
INSERT_PADDING_BYTES(5);
|
||||
IrsRect window_of_interest;
|
||||
PackedMcuVersion required_mcu_version;
|
||||
u8 preprocess;
|
||||
u8 preprocess_intensity_threshold;
|
||||
INSERT_PADDING_BYTES(2);
|
||||
};
|
||||
static_assert(sizeof(PackedMomentProcessorConfig) == 0x20,
|
||||
"PackedMomentProcessorConfig is an invalid size");
|
||||
|
||||
// This is nn::irsensor::ClusteringProcessorConfig
|
||||
struct ClusteringProcessorConfig {
|
||||
u64 exposire_time;
|
||||
u32 light_target;
|
||||
u32 gain;
|
||||
u8 is_negative_used;
|
||||
INSERT_PADDING_BYTES(7);
|
||||
IrsRect window_of_interest;
|
||||
u32 pixel_count_min;
|
||||
u32 pixel_count_max;
|
||||
u32 object_intensity_min;
|
||||
u8 is_external_light_filter_enabled;
|
||||
INSERT_PADDING_BYTES(3);
|
||||
};
|
||||
static_assert(sizeof(ClusteringProcessorConfig) == 0x30,
|
||||
"ClusteringProcessorConfig is an invalid size");
|
||||
|
||||
// This is nn::irsensor::PackedClusteringProcessorConfig
|
||||
struct PackedClusteringProcessorConfig {
|
||||
u64 exposire_time;
|
||||
u8 light_target;
|
||||
u8 gain;
|
||||
u8 is_negative_used;
|
||||
INSERT_PADDING_BYTES(5);
|
||||
IrsRect window_of_interest;
|
||||
PackedMcuVersion required_mcu_version;
|
||||
u32 pixel_count_min;
|
||||
u32 pixel_count_max;
|
||||
u32 object_intensity_min;
|
||||
u8 is_external_light_filter_enabled;
|
||||
INSERT_PADDING_BYTES(2);
|
||||
};
|
||||
static_assert(sizeof(PackedClusteringProcessorConfig) == 0x30,
|
||||
"PackedClusteringProcessorConfig is an invalid size");
|
||||
|
||||
// This is nn::irsensor::PackedImageTransferProcessorConfig
|
||||
struct PackedImageTransferProcessorConfig {
|
||||
u64 exposire_time;
|
||||
u8 light_target;
|
||||
u8 gain;
|
||||
u8 is_negative_used;
|
||||
INSERT_PADDING_BYTES(5);
|
||||
PackedMcuVersion required_mcu_version;
|
||||
u8 format;
|
||||
INSERT_PADDING_BYTES(3);
|
||||
};
|
||||
static_assert(sizeof(PackedImageTransferProcessorConfig) == 0x18,
|
||||
"PackedImageTransferProcessorConfig is an invalid size");
|
||||
|
||||
// This is nn::irsensor::PackedTeraPluginProcessorConfig
|
||||
struct PackedTeraPluginProcessorConfig {
|
||||
PackedMcuVersion required_mcu_version;
|
||||
u8 mode;
|
||||
INSERT_PADDING_BYTES(3);
|
||||
};
|
||||
static_assert(sizeof(PackedTeraPluginProcessorConfig) == 0x8,
|
||||
"PackedTeraPluginProcessorConfig is an invalid size");
|
||||
|
||||
// This is nn::irsensor::PackedPointingProcessorConfig
|
||||
struct PackedPointingProcessorConfig {
|
||||
IrsRect window_of_interest;
|
||||
PackedMcuVersion required_mcu_version;
|
||||
};
|
||||
static_assert(sizeof(PackedPointingProcessorConfig) == 0xC,
|
||||
"PackedPointingProcessorConfig is an invalid size");
|
||||
|
||||
// This is nn::irsensor::PackedFunctionLevel
|
||||
struct PackedFunctionLevel {
|
||||
IrSensorFunctionLevel function_level;
|
||||
INSERT_PADDING_BYTES(3);
|
||||
};
|
||||
static_assert(sizeof(PackedFunctionLevel) == 0x4, "PackedFunctionLevel is an invalid size");
|
||||
|
||||
// This is nn::irsensor::PackedImageTransferProcessorExConfig
|
||||
struct PackedImageTransferProcessorExConfig {
|
||||
u64 exposire_time;
|
||||
u8 light_target;
|
||||
u8 gain;
|
||||
u8 is_negative_used;
|
||||
INSERT_PADDING_BYTES(5);
|
||||
PackedMcuVersion required_mcu_version;
|
||||
ImageTransferProcessorFormat origin_format;
|
||||
ImageTransferProcessorFormat trimming_format;
|
||||
u16 trimming_start_x;
|
||||
u16 trimming_start_y;
|
||||
u8 is_external_light_filter_enabled;
|
||||
INSERT_PADDING_BYTES(3);
|
||||
};
|
||||
static_assert(sizeof(PackedImageTransferProcessorExConfig) == 0x20,
|
||||
"PackedImageTransferProcessorExConfig is an invalid size");
|
||||
|
||||
// This is nn::irsensor::PackedIrLedProcessorConfig
|
||||
struct PackedIrLedProcessorConfig {
|
||||
PackedMcuVersion required_mcu_version;
|
||||
u8 light_target;
|
||||
INSERT_PADDING_BYTES(3);
|
||||
};
|
||||
static_assert(sizeof(PackedIrLedProcessorConfig) == 0x8,
|
||||
"PackedIrLedProcessorConfig is an invalid size");
|
||||
|
||||
void ActivateIrsensor(Kernel::HLERequestContext& ctx);
|
||||
void DeactivateIrsensor(Kernel::HLERequestContext& ctx);
|
||||
void GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx);
|
||||
@@ -35,8 +265,6 @@ private:
|
||||
void RunIrLedProcessor(Kernel::HLERequestContext& ctx);
|
||||
void StopImageProcessorAsync(Kernel::HLERequestContext& ctx);
|
||||
void ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx);
|
||||
|
||||
const u32 device_handle{0xABCD};
|
||||
};
|
||||
|
||||
class IRS_SYS final : public ServiceFramework<IRS_SYS> {
|
||||
|
||||
@@ -89,14 +89,6 @@ Status BufferQueueConsumer::AcquireBuffer(BufferItem* out_buffer,
|
||||
|
||||
LOG_DEBUG(Service_NVFlinger, "acquiring slot={}", slot);
|
||||
|
||||
// If the front buffer is still being tracked, update its slot state
|
||||
if (core->StillTracking(*front)) {
|
||||
slots[slot].acquire_called = true;
|
||||
slots[slot].needs_cleanup_on_release = false;
|
||||
slots[slot].buffer_state = BufferState::Acquired;
|
||||
slots[slot].fence = Fence::NoFence();
|
||||
}
|
||||
|
||||
// If the buffer has previously been acquired by the consumer, set graphic_buffer to nullptr to
|
||||
// avoid unnecessarily remapping this buffer on the consumer side.
|
||||
if (out_buffer->acquire_called) {
|
||||
@@ -139,26 +131,11 @@ Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fenc
|
||||
++current;
|
||||
}
|
||||
|
||||
if (slots[slot].buffer_state == BufferState::Acquired) {
|
||||
slots[slot].fence = release_fence;
|
||||
slots[slot].buffer_state = BufferState::Free;
|
||||
slots[slot].buffer_state = BufferState::Free;
|
||||
|
||||
listener = core->connected_producer_listener;
|
||||
listener = core->connected_producer_listener;
|
||||
|
||||
LOG_DEBUG(Service_NVFlinger, "releasing slot {}", slot);
|
||||
} else if (slots[slot].needs_cleanup_on_release) {
|
||||
LOG_DEBUG(Service_NVFlinger, "releasing a stale buffer slot {} (state = {})", slot,
|
||||
slots[slot].buffer_state);
|
||||
|
||||
slots[slot].needs_cleanup_on_release = false;
|
||||
|
||||
return Status::StaleBufferSlot;
|
||||
} else {
|
||||
LOG_ERROR(Service_NVFlinger, "attempted to release buffer slot {} but its state was {}",
|
||||
slot, slots[slot].buffer_state);
|
||||
|
||||
return Status::BadValue;
|
||||
}
|
||||
LOG_DEBUG(Service_NVFlinger, "releasing slot {}", slot);
|
||||
|
||||
core->SignalDequeueCondition();
|
||||
}
|
||||
|
||||
@@ -84,10 +84,6 @@ void BufferQueueCore::FreeBufferLocked(s32 slot) {
|
||||
|
||||
slots[slot].graphic_buffer.reset();
|
||||
|
||||
if (slots[slot].buffer_state == BufferState::Acquired) {
|
||||
slots[slot].needs_cleanup_on_release = true;
|
||||
}
|
||||
|
||||
slots[slot].buffer_state = BufferState::Free;
|
||||
slots[slot].frame_number = UINT32_MAX;
|
||||
slots[slot].acquire_called = false;
|
||||
|
||||
@@ -31,7 +31,6 @@ struct BufferSlot final {
|
||||
u64 frame_number{};
|
||||
Fence fence;
|
||||
bool acquire_called{};
|
||||
bool needs_cleanup_on_release{};
|
||||
bool attached_by_consumer{};
|
||||
bool is_preallocated{};
|
||||
};
|
||||
|
||||
@@ -975,13 +975,7 @@ private:
|
||||
Environment& env;
|
||||
IR::AbstractSyntaxList& syntax_list;
|
||||
bool uses_demote_to_helper{};
|
||||
|
||||
// TODO: C++20 Remove this when all compilers support constexpr std::vector
|
||||
#if __cpp_lib_constexpr_vector >= 201907
|
||||
static constexpr Flow::Block dummy_flow_block;
|
||||
#else
|
||||
const Flow::Block dummy_flow_block;
|
||||
#endif
|
||||
};
|
||||
} // Anonymous namespace
|
||||
|
||||
|
||||
@@ -595,8 +595,8 @@ void Maxwell3D::DrawArrays() {
|
||||
|
||||
std::optional<u64> Maxwell3D::GetQueryResult() {
|
||||
switch (regs.query.query_get.select) {
|
||||
case Regs::QuerySelect::Zero:
|
||||
return 0;
|
||||
case Regs::QuerySelect::Payload:
|
||||
return regs.query.query_sequence;
|
||||
case Regs::QuerySelect::SamplesPassed:
|
||||
// Deferred.
|
||||
rasterizer->Query(regs.query.QueryAddress(), QueryType::SamplesPassed,
|
||||
|
||||
@@ -93,7 +93,7 @@ public:
|
||||
};
|
||||
|
||||
enum class QuerySelect : u32 {
|
||||
Zero = 0,
|
||||
Payload = 0,
|
||||
TimeElapsed = 2,
|
||||
TransformFeedbackPrimitivesGenerated = 11,
|
||||
PrimitivesGenerated = 18,
|
||||
|
||||
@@ -31,7 +31,8 @@ static void RunThread(std::stop_token stop_token, Core::System& system,
|
||||
VideoCore::RasterizerInterface* const rasterizer = renderer.ReadRasterizer();
|
||||
|
||||
while (!stop_token.stop_requested()) {
|
||||
CommandDataContainer next = state.queue.PopWait(stop_token);
|
||||
CommandDataContainer next;
|
||||
state.queue.Pop(next, stop_token);
|
||||
if (stop_token.stop_requested()) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include <thread>
|
||||
#include <variant>
|
||||
|
||||
#include "common/threadsafe_queue.h"
|
||||
#include "common/bounded_threadsafe_queue.h"
|
||||
#include "video_core/framebuffer_config.h"
|
||||
|
||||
namespace Tegra {
|
||||
@@ -96,7 +96,7 @@ struct CommandDataContainer {
|
||||
|
||||
/// Struct used to synchronize the GPU thread
|
||||
struct SynchState final {
|
||||
using CommandQueue = Common::SPSCQueue<CommandDataContainer, true>;
|
||||
using CommandQueue = Common::MPSCQueue<CommandDataContainer>;
|
||||
std::mutex write_lock;
|
||||
CommandQueue queue;
|
||||
u64 last_fence{};
|
||||
|
||||
@@ -5,11 +5,13 @@
|
||||
|
||||
#include "common/dynamic_library.h"
|
||||
#include "common/fs/path_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "video_core/vulkan_common/vulkan_library.h"
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
Common::DynamicLibrary OpenLibrary() {
|
||||
LOG_DEBUG(Render_Vulkan, "Looking for a Vulkan library");
|
||||
Common::DynamicLibrary library;
|
||||
#ifdef __APPLE__
|
||||
// Check if a path to a specific Vulkan library has been specified.
|
||||
@@ -22,9 +24,11 @@ Common::DynamicLibrary OpenLibrary() {
|
||||
}
|
||||
#else
|
||||
std::string filename = Common::DynamicLibrary::GetVersionedFilename("vulkan", 1);
|
||||
LOG_DEBUG(Render_Vulkan, "Trying Vulkan library: {}", filename);
|
||||
if (!library.Open(filename.c_str())) {
|
||||
// Android devices may not have libvulkan.so.1, only libvulkan.so.
|
||||
filename = Common::DynamicLibrary::GetVersionedFilename("vulkan");
|
||||
LOG_DEBUG(Render_Vulkan, "Trying Vulkan library (second attempt): {}", filename);
|
||||
void(library.Open(filename.c_str()));
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -30,6 +30,8 @@ add_executable(yuzu
|
||||
applets/qt_web_browser_scripts.h
|
||||
bootmanager.cpp
|
||||
bootmanager.h
|
||||
check_vulkan.cpp
|
||||
check_vulkan.h
|
||||
compatdb.ui
|
||||
compatibility_list.cpp
|
||||
compatibility_list.h
|
||||
@@ -187,7 +189,7 @@ if (ENABLE_QT_TRANSLATION)
|
||||
# Update source TS file if enabled
|
||||
if (GENERATE_QT_TRANSLATION)
|
||||
get_target_property(SRCS yuzu SOURCES)
|
||||
qt5_create_translation(QM_FILES
|
||||
qt_create_translation(QM_FILES
|
||||
${SRCS}
|
||||
${UIS}
|
||||
${YUZU_QT_LANGUAGES}/en.ts
|
||||
@@ -203,7 +205,7 @@ if (ENABLE_QT_TRANSLATION)
|
||||
list(REMOVE_ITEM LANGUAGES_TS ${YUZU_QT_LANGUAGES}/en.ts)
|
||||
|
||||
# Compile TS files to QM files
|
||||
qt5_add_translation(LANGUAGES_QM ${LANGUAGES_TS})
|
||||
qt_add_translation(LANGUAGES_QM ${LANGUAGES_TS})
|
||||
|
||||
# Build a QRC file from the QM file list
|
||||
set(LANGUAGES_QRC ${CMAKE_CURRENT_BINARY_DIR}/languages.qrc)
|
||||
@@ -215,7 +217,7 @@ if (ENABLE_QT_TRANSLATION)
|
||||
file(APPEND ${LANGUAGES_QRC} "</qresource></RCC>")
|
||||
|
||||
# Add the QRC file to package in all QM files
|
||||
qt5_add_resources(LANGUAGES ${LANGUAGES_QRC})
|
||||
qt_add_resources(LANGUAGES ${LANGUAGES_QRC})
|
||||
else()
|
||||
set(LANGUAGES)
|
||||
endif()
|
||||
@@ -236,8 +238,13 @@ if (APPLE)
|
||||
set_target_properties(yuzu PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist)
|
||||
elseif(WIN32)
|
||||
# compile as a win32 gui application instead of a console application
|
||||
target_link_libraries(yuzu PRIVATE Qt5::WinMain)
|
||||
if (QT_VERSION VERSION_GREATER 6)
|
||||
target_link_libraries(yuzu PRIVATE Qt6::EntryPointPrivate)
|
||||
else()
|
||||
target_link_libraries(yuzu PRIVATE Qt5::WinMain)
|
||||
endif()
|
||||
if(MSVC)
|
||||
target_link_libraries(yuzu PRIVATE version.lib)
|
||||
set_target_properties(yuzu PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS")
|
||||
elseif(MINGW)
|
||||
set_target_properties(yuzu PROPERTIES LINK_FLAGS_RELEASE "-Wl,--subsystem,windows")
|
||||
@@ -247,7 +254,7 @@ endif()
|
||||
create_target_directory_groups(yuzu)
|
||||
|
||||
target_link_libraries(yuzu PRIVATE common core input_common video_core)
|
||||
target_link_libraries(yuzu PRIVATE Boost::boost glad Qt5::Widgets)
|
||||
target_link_libraries(yuzu PRIVATE Boost::boost glad Qt::Widgets)
|
||||
target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
|
||||
|
||||
target_include_directories(yuzu PRIVATE ../../externals/Vulkan-Headers/include)
|
||||
@@ -255,7 +262,7 @@ if (NOT WIN32)
|
||||
target_include_directories(yuzu PRIVATE ${Qt5Gui_PRIVATE_INCLUDE_DIRS})
|
||||
endif()
|
||||
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
target_link_libraries(yuzu PRIVATE Qt5::DBus)
|
||||
target_link_libraries(yuzu PRIVATE Qt::DBus)
|
||||
endif()
|
||||
|
||||
target_compile_definitions(yuzu PRIVATE
|
||||
@@ -291,7 +298,7 @@ if (USE_DISCORD_PRESENCE)
|
||||
endif()
|
||||
|
||||
if (YUZU_USE_QT_WEB_ENGINE)
|
||||
target_link_libraries(yuzu PRIVATE Qt5::WebEngineCore Qt5::WebEngineWidgets)
|
||||
target_link_libraries(yuzu PRIVATE Qt::WebEngineCore Qt::WebEngineWidgets)
|
||||
target_compile_definitions(yuzu PRIVATE -DYUZU_USE_QT_WEB_ENGINE)
|
||||
endif ()
|
||||
|
||||
|
||||
@@ -752,7 +752,7 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
|
||||
input_subsystem->GetMouse()->MouseMove(x, y, touch_x, touch_y, center_x, center_y);
|
||||
|
||||
if (Settings::values.mouse_panning && !Settings::values.mouse_enabled) {
|
||||
QCursor::setPos(mapToGlobal({center_x, center_y}));
|
||||
QCursor::setPos(mapToGlobal(QPoint{center_x, center_y}));
|
||||
}
|
||||
|
||||
emit MouseActivity();
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <mutex>
|
||||
|
||||
#include <QImage>
|
||||
#include <QStringList>
|
||||
#include <QThread>
|
||||
#include <QTouchEvent>
|
||||
#include <QWidget>
|
||||
@@ -20,7 +21,6 @@
|
||||
class GRenderWindow;
|
||||
class GMainWindow;
|
||||
class QKeyEvent;
|
||||
class QStringList;
|
||||
|
||||
namespace Core {
|
||||
enum class SystemResultStatus : u32;
|
||||
|
||||
53
src/yuzu/check_vulkan.cpp
Normal file
53
src/yuzu/check_vulkan.cpp
Normal file
@@ -0,0 +1,53 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include "common/fs/fs.h"
|
||||
#include "common/fs/path_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "video_core/vulkan_common/vulkan_instance.h"
|
||||
#include "video_core/vulkan_common/vulkan_library.h"
|
||||
#include "yuzu/check_vulkan.h"
|
||||
#include "yuzu/uisettings.h"
|
||||
|
||||
constexpr char TEMP_FILE_NAME[] = "vulkan_check";
|
||||
|
||||
bool CheckVulkan() {
|
||||
if (UISettings::values.has_broken_vulkan) {
|
||||
return true;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Frontend, "Checking presence of Vulkan");
|
||||
|
||||
const auto fs_config_loc = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir);
|
||||
const auto temp_file_loc = fs_config_loc / TEMP_FILE_NAME;
|
||||
|
||||
if (std::filesystem::exists(temp_file_loc)) {
|
||||
LOG_WARNING(Frontend, "Detected recovery from previous failed Vulkan initialization");
|
||||
|
||||
UISettings::values.has_broken_vulkan = true;
|
||||
std::filesystem::remove(temp_file_loc);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::ofstream temp_file_handle(temp_file_loc);
|
||||
temp_file_handle.close();
|
||||
|
||||
try {
|
||||
Vulkan::vk::InstanceDispatch dld;
|
||||
const Common::DynamicLibrary library = Vulkan::OpenLibrary();
|
||||
const Vulkan::vk::Instance instance =
|
||||
Vulkan::CreateInstance(library, dld, VK_API_VERSION_1_0);
|
||||
|
||||
} catch (const Vulkan::vk::Exception& exception) {
|
||||
LOG_ERROR(Frontend, "Failed to initialize Vulkan: {}", exception.what());
|
||||
// Don't set has_broken_vulkan to true here: we care when loading Vulkan crashes the
|
||||
// application, not when we can handle it.
|
||||
}
|
||||
|
||||
std::filesystem::remove(temp_file_loc);
|
||||
return true;
|
||||
}
|
||||
6
src/yuzu/check_vulkan.h
Normal file
6
src/yuzu/check_vulkan.h
Normal file
@@ -0,0 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
bool CheckVulkan();
|
||||
@@ -71,28 +71,28 @@ const std::array<int, 2> Config::default_ringcon_analogs{{
|
||||
// UISetting::values.shortcuts, which is alphabetically ordered.
|
||||
// clang-format off
|
||||
const std::array<UISettings::Shortcut, 22> Config::default_hotkeys{{
|
||||
{QStringLiteral("Audio Mute/Unmute"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), QStringLiteral("Home+Dpad_Right"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Audio Volume Down"), QStringLiteral("Main Window"), {QStringLiteral("-"), QStringLiteral("Home+Dpad_Down"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Audio Volume Up"), QStringLiteral("Main Window"), {QStringLiteral("+"), QStringLiteral("Home+Dpad_Up"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), QStringLiteral("Screenshot"), Qt::WidgetWithChildrenShortcut}},
|
||||
{QStringLiteral("Change Adapting Filter"), QStringLiteral("Main Window"), {QStringLiteral("F8"), QStringLiteral("Home+L"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Change Docked Mode"), QStringLiteral("Main Window"), {QStringLiteral("F10"), QStringLiteral("Home+X"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Change GPU Accuracy"), QStringLiteral("Main Window"), {QStringLiteral("F9"), QStringLiteral("Home+R"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), QStringLiteral("Home+Plus"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Exit Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("Esc"), QStringLiteral(""), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Exit yuzu"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), QStringLiteral("Home+Minus"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), QStringLiteral("Home+B"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut}},
|
||||
{QStringLiteral("Load/Remove Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut}},
|
||||
{QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), QStringLiteral(""), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), QStringLiteral(""), Qt::WindowShortcut}},
|
||||
{QStringLiteral("TAS Record"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("TAS Reset"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("TAS Start/Stop"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), QStringLiteral(""), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Toggle Framerate Limit"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+U"), QStringLiteral("Home+Y"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Toggle Mouse Panning"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F9"), QStringLiteral(""), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Toggle Status Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+S"), QStringLiteral(""), Qt::WindowShortcut}},
|
||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Mute/Unmute")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+M"), QStringLiteral("Home+Dpad_Right"), Qt::WindowShortcut}},
|
||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Down")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("-"), QStringLiteral("Home+Dpad_Down"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Up")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("+"), QStringLiteral("Home+Dpad_Up"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Capture Screenshot")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+P"), QStringLiteral("Screenshot"), Qt::WidgetWithChildrenShortcut}},
|
||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Adapting Filter")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F8"), QStringLiteral("Home+L"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Docked Mode")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F10"), QStringLiteral("Home+X"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change GPU Accuracy")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F9"), QStringLiteral("Home+R"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Continue/Pause Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F4"), QStringLiteral("Home+Plus"), Qt::WindowShortcut}},
|
||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Esc"), QStringLiteral(""), Qt::WindowShortcut}},
|
||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit yuzu")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+Q"), QStringLiteral("Home+Minus"), Qt::WindowShortcut}},
|
||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F11"), QStringLiteral("Home+B"), Qt::WindowShortcut}},
|
||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load File")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut}},
|
||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load/Remove Amiibo")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut}},
|
||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Restart Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F6"), QStringLiteral(""), Qt::WindowShortcut}},
|
||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Stop Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F5"), QStringLiteral(""), Qt::WindowShortcut}},
|
||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Record")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Reset")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Start/Stop")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Filter Bar")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F"), QStringLiteral(""), Qt::WindowShortcut}},
|
||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Framerate Limit")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+U"), QStringLiteral("Home+Y"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Mouse Panning")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F9"), QStringLiteral(""), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Status Bar")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+S"), QStringLiteral(""), Qt::WindowShortcut}},
|
||||
}};
|
||||
// clang-format on
|
||||
|
||||
@@ -682,6 +682,12 @@ void Config::ReadRendererValues() {
|
||||
ReadGlobalSetting(Settings::values.bg_green);
|
||||
ReadGlobalSetting(Settings::values.bg_blue);
|
||||
|
||||
if (!global && UISettings::values.has_broken_vulkan &&
|
||||
Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::Vulkan &&
|
||||
!Settings::values.renderer_backend.UsingGlobal()) {
|
||||
Settings::values.renderer_backend.SetGlobal(true);
|
||||
}
|
||||
|
||||
if (global) {
|
||||
ReadBasicSetting(Settings::values.renderer_debug);
|
||||
ReadBasicSetting(Settings::values.renderer_shader_feedback);
|
||||
@@ -801,6 +807,7 @@ void Config::ReadUIValues() {
|
||||
ReadBasicSetting(UISettings::values.pause_when_in_background);
|
||||
ReadBasicSetting(UISettings::values.mute_when_in_background);
|
||||
ReadBasicSetting(UISettings::values.hide_mouse);
|
||||
ReadBasicSetting(UISettings::values.has_broken_vulkan);
|
||||
ReadBasicSetting(UISettings::values.disable_web_applet);
|
||||
|
||||
qt_config->endGroup();
|
||||
@@ -1348,6 +1355,7 @@ void Config::SaveUIValues() {
|
||||
WriteBasicSetting(UISettings::values.pause_when_in_background);
|
||||
WriteBasicSetting(UISettings::values.mute_when_in_background);
|
||||
WriteBasicSetting(UISettings::values.hide_mouse);
|
||||
WriteBasicSetting(UISettings::values.has_broken_vulkan);
|
||||
WriteBasicSetting(UISettings::values.disable_web_applet);
|
||||
|
||||
qt_config->endGroup();
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "video_core/vulkan_common/vulkan_library.h"
|
||||
#include "yuzu/configuration/configuration_shared.h"
|
||||
#include "yuzu/configuration/configure_graphics.h"
|
||||
#include "yuzu/uisettings.h"
|
||||
|
||||
ConfigureGraphics::ConfigureGraphics(const Core::System& system_, QWidget* parent)
|
||||
: QWidget(parent), ui{std::make_unique<Ui::ConfigureGraphics>()}, system{system_} {
|
||||
@@ -57,6 +58,24 @@ ConfigureGraphics::ConfigureGraphics(const Core::System& system_, QWidget* paren
|
||||
UpdateBackgroundColorButton(new_bg_color);
|
||||
});
|
||||
|
||||
connect(ui->button_check_vulkan, &QAbstractButton::clicked, this, [this] {
|
||||
UISettings::values.has_broken_vulkan = false;
|
||||
|
||||
if (RetrieveVulkanDevices()) {
|
||||
ui->api->setEnabled(true);
|
||||
ui->button_check_vulkan->hide();
|
||||
|
||||
for (const auto& device : vulkan_devices) {
|
||||
ui->device->addItem(device);
|
||||
}
|
||||
} else {
|
||||
UISettings::values.has_broken_vulkan = true;
|
||||
}
|
||||
});
|
||||
|
||||
ui->api->setEnabled(!UISettings::values.has_broken_vulkan.GetValue());
|
||||
ui->button_check_vulkan->setVisible(UISettings::values.has_broken_vulkan.GetValue());
|
||||
|
||||
ui->bg_label->setVisible(Settings::IsConfiguringGlobal());
|
||||
ui->bg_combobox->setVisible(!Settings::IsConfiguringGlobal());
|
||||
}
|
||||
@@ -296,7 +315,7 @@ void ConfigureGraphics::UpdateAPILayout() {
|
||||
vulkan_device = Settings::values.vulkan_device.GetValue(true);
|
||||
shader_backend = Settings::values.shader_backend.GetValue(true);
|
||||
ui->device_widget->setEnabled(false);
|
||||
ui->backend_widget->setEnabled(false);
|
||||
ui->backend_widget->setEnabled(UISettings::values.has_broken_vulkan.GetValue());
|
||||
} else {
|
||||
vulkan_device = Settings::values.vulkan_device.GetValue();
|
||||
shader_backend = Settings::values.shader_backend.GetValue();
|
||||
@@ -318,7 +337,11 @@ void ConfigureGraphics::UpdateAPILayout() {
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigureGraphics::RetrieveVulkanDevices() try {
|
||||
bool ConfigureGraphics::RetrieveVulkanDevices() try {
|
||||
if (UISettings::values.has_broken_vulkan) {
|
||||
return false;
|
||||
}
|
||||
|
||||
using namespace Vulkan;
|
||||
|
||||
vk::InstanceDispatch dld;
|
||||
@@ -333,8 +356,10 @@ void ConfigureGraphics::RetrieveVulkanDevices() try {
|
||||
vulkan_devices.push_back(QString::fromStdString(name));
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (const Vulkan::vk::Exception& exception) {
|
||||
LOG_ERROR(Frontend, "Failed to enumerate devices with error: {}", exception.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const {
|
||||
@@ -415,4 +440,11 @@ void ConfigureGraphics::SetupPerGameUI() {
|
||||
ui->api, static_cast<int>(Settings::values.renderer_backend.GetValue(true)));
|
||||
ConfigurationShared::InsertGlobalItem(
|
||||
ui->nvdec_emulation, static_cast<int>(Settings::values.nvdec_emulation.GetValue(true)));
|
||||
|
||||
if (UISettings::values.has_broken_vulkan) {
|
||||
ui->backend_widget->setEnabled(true);
|
||||
ConfigurationShared::SetColoredComboBox(
|
||||
ui->backend, ui->backend_widget,
|
||||
static_cast<int>(Settings::values.shader_backend.GetValue(true)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ private:
|
||||
void UpdateDeviceSelection(int device);
|
||||
void UpdateShaderBackendSelection(int backend);
|
||||
|
||||
void RetrieveVulkanDevices();
|
||||
bool RetrieveVulkanDevices();
|
||||
|
||||
void SetupPerGameUI();
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>437</width>
|
||||
<height>482</height>
|
||||
<width>471</width>
|
||||
<height>759</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -171,11 +171,11 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="accelerate_astc">
|
||||
<property name="text">
|
||||
<string>Accelerate ASTC texture decoding</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QCheckBox" name="accelerate_astc">
|
||||
<property name="text">
|
||||
<string>Accelerate ASTC texture decoding</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="nvdec_emulation_widget" native="true">
|
||||
@@ -438,43 +438,43 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="anti_aliasing_layout" native="true">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_7">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
<widget class="QWidget" name="anti_aliasing_layout" native="true">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_7">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="anti_aliasing_label">
|
||||
<property name="text">
|
||||
<string>Anti-Aliasing Method:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="anti_aliasing_combobox">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>None</string>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>FXAA</string>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="anti_aliasing_label">
|
||||
<property name="text">
|
||||
<string>Anti-Aliasing Method:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="anti_aliasing_combobox">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>None</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>FXAA</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="bg_layout" native="true">
|
||||
@@ -574,6 +574,13 @@
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="button_check_vulkan">
|
||||
<property name="text">
|
||||
<string>Check for Working Vulkan</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
|
||||
@@ -61,14 +61,18 @@ ConfigureHotkeys::~ConfigureHotkeys() = default;
|
||||
|
||||
void ConfigureHotkeys::Populate(const HotkeyRegistry& registry) {
|
||||
for (const auto& group : registry.hotkey_groups) {
|
||||
auto* parent_item = new QStandardItem(group.first);
|
||||
auto* parent_item =
|
||||
new QStandardItem(QCoreApplication::translate("Hotkeys", qPrintable(group.first)));
|
||||
parent_item->setEditable(false);
|
||||
parent_item->setData(group.first);
|
||||
for (const auto& hotkey : group.second) {
|
||||
auto* action = new QStandardItem(hotkey.first);
|
||||
auto* action =
|
||||
new QStandardItem(QCoreApplication::translate("Hotkeys", qPrintable(hotkey.first)));
|
||||
auto* keyseq =
|
||||
new QStandardItem(hotkey.second.keyseq.toString(QKeySequence::NativeText));
|
||||
auto* controller_keyseq = new QStandardItem(hotkey.second.controller_keyseq);
|
||||
action->setEditable(false);
|
||||
action->setData(hotkey.first);
|
||||
keyseq->setEditable(false);
|
||||
controller_keyseq->setEditable(false);
|
||||
parent_item->appendRow({action, keyseq, controller_keyseq});
|
||||
@@ -93,6 +97,16 @@ void ConfigureHotkeys::RetranslateUI() {
|
||||
ui->retranslateUi(this);
|
||||
|
||||
model->setHorizontalHeaderLabels({tr("Action"), tr("Hotkey"), tr("Controller Hotkey")});
|
||||
for (int key_id = 0; key_id < model->rowCount(); key_id++) {
|
||||
QStandardItem* parent = model->item(key_id, 0);
|
||||
parent->setText(
|
||||
QCoreApplication::translate("Hotkeys", qPrintable(parent->data().toString())));
|
||||
for (int key_column_id = 0; key_column_id < parent->rowCount(); key_column_id++) {
|
||||
QStandardItem* action = parent->child(key_column_id, name_column);
|
||||
action->setText(
|
||||
QCoreApplication::translate("Hotkeys", qPrintable(action->data().toString())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigureHotkeys::Configure(QModelIndex index) {
|
||||
@@ -273,10 +287,10 @@ void ConfigureHotkeys::ApplyConfiguration(HotkeyRegistry& registry) {
|
||||
const QStandardItem* controller_keyseq =
|
||||
parent->child(key_column_id, controller_column);
|
||||
for (auto& [group, sub_actions] : registry.hotkey_groups) {
|
||||
if (group != parent->text())
|
||||
if (group != parent->data())
|
||||
continue;
|
||||
for (auto& [action_name, hotkey] : sub_actions) {
|
||||
if (action_name != action->text())
|
||||
if (action_name != action->data())
|
||||
continue;
|
||||
hotkey.keyseq = QKeySequence(keyseq->text());
|
||||
hotkey.controller_keyseq = controller_keyseq->text();
|
||||
|
||||
@@ -151,6 +151,8 @@ void ConfigureMotionTouch::ConnectEvents() {
|
||||
&ConfigureMotionTouch::OnConfigureTouchCalibration);
|
||||
connect(ui->touch_from_button_config_btn, &QPushButton::clicked, this,
|
||||
&ConfigureMotionTouch::OnConfigureTouchFromButton);
|
||||
connect(ui->buttonBox, &QDialogButtonBox::accepted, this,
|
||||
&ConfigureMotionTouch::ApplyConfiguration);
|
||||
connect(ui->buttonBox, &QDialogButtonBox::rejected, this, [this] {
|
||||
if (CanCloseDialog()) {
|
||||
reject();
|
||||
|
||||
@@ -293,22 +293,5 @@
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>ConfigureMotionTouch</receiver>
|
||||
<slot>ApplyConfiguration()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>20</x>
|
||||
<y>20</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>20</x>
|
||||
<y>20</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
||||
@@ -130,8 +130,7 @@ void ConfigureSystem::ApplyConfiguration() {
|
||||
// Guard if during game and set to game-specific value
|
||||
if (Settings::values.rng_seed.UsingGlobal()) {
|
||||
if (ui->rng_seed_checkbox->isChecked()) {
|
||||
Settings::values.rng_seed.SetValue(
|
||||
ui->rng_seed_edit->text().toULongLong(nullptr, 16));
|
||||
Settings::values.rng_seed.SetValue(ui->rng_seed_edit->text().toUInt(nullptr, 16));
|
||||
} else {
|
||||
Settings::values.rng_seed.SetValue(std::nullopt);
|
||||
}
|
||||
@@ -142,8 +141,7 @@ void ConfigureSystem::ApplyConfiguration() {
|
||||
case ConfigurationShared::CheckState::Off:
|
||||
Settings::values.rng_seed.SetGlobal(false);
|
||||
if (ui->rng_seed_checkbox->isChecked()) {
|
||||
Settings::values.rng_seed.SetValue(
|
||||
ui->rng_seed_edit->text().toULongLong(nullptr, 16));
|
||||
Settings::values.rng_seed.SetValue(ui->rng_seed_edit->text().toUInt(nullptr, 16));
|
||||
} else {
|
||||
Settings::values.rng_seed.SetValue(std::nullopt);
|
||||
}
|
||||
|
||||
@@ -483,7 +483,7 @@ void GameList::DonePopulating(const QStringList& watch_list) {
|
||||
// Also artificially caps the watcher to a certain number of directories
|
||||
constexpr int LIMIT_WATCH_DIRECTORIES = 5000;
|
||||
constexpr int SLICE_SIZE = 25;
|
||||
int len = std::min(watch_list.length(), LIMIT_WATCH_DIRECTORIES);
|
||||
int len = std::min(static_cast<int>(watch_list.size()), LIMIT_WATCH_DIRECTORIES);
|
||||
for (int i = 0; i < len; i += SLICE_SIZE) {
|
||||
watcher->addPaths(watch_list.mid(i, i + SLICE_SIZE));
|
||||
QCoreApplication::processEvents();
|
||||
@@ -870,7 +870,7 @@ GameListPlaceholder::GameListPlaceholder(GMainWindow* parent) : QWidget{parent}
|
||||
layout->setAlignment(Qt::AlignCenter);
|
||||
image->setPixmap(QIcon::fromTheme(QStringLiteral("plus_folder")).pixmap(200));
|
||||
|
||||
text->setText(tr("Double-click to add a new folder to the game list"));
|
||||
RetranslateUI();
|
||||
QFont font = text->font();
|
||||
font.setPointSize(20);
|
||||
text->setFont(font);
|
||||
@@ -891,3 +891,15 @@ void GameListPlaceholder::onUpdateThemedIcons() {
|
||||
void GameListPlaceholder::mouseDoubleClickEvent(QMouseEvent* event) {
|
||||
emit GameListPlaceholder::AddDirectory();
|
||||
}
|
||||
|
||||
void GameListPlaceholder::changeEvent(QEvent* event) {
|
||||
if (event->type() == QEvent::LanguageChange) {
|
||||
RetranslateUI();
|
||||
}
|
||||
|
||||
QWidget::changeEvent(event);
|
||||
}
|
||||
|
||||
void GameListPlaceholder::RetranslateUI() {
|
||||
text->setText(tr("Double-click to add a new folder to the game list"));
|
||||
}
|
||||
|
||||
@@ -166,6 +166,9 @@ protected:
|
||||
void mouseDoubleClickEvent(QMouseEvent* event) override;
|
||||
|
||||
private:
|
||||
void changeEvent(QEvent* event) override;
|
||||
void RetranslateUI();
|
||||
|
||||
QVBoxLayout* layout = nullptr;
|
||||
QLabel* image = nullptr;
|
||||
QLabel* text = nullptr;
|
||||
|
||||
@@ -183,7 +183,7 @@ void LoadingScreen::OnLoadProgress(VideoCore::LoadCallbackStage stage, std::size
|
||||
|
||||
void LoadingScreen::paintEvent(QPaintEvent* event) {
|
||||
QStyleOption opt;
|
||||
opt.init(this);
|
||||
opt.initFrom(this);
|
||||
QPainter p(this);
|
||||
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
|
||||
QWidget::paintEvent(event);
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <memory>
|
||||
#include <QString>
|
||||
#include <QWidget>
|
||||
#include <QtGlobal>
|
||||
|
||||
#if !QT_CONFIG(movie)
|
||||
#define YUZU_QT_MOVIE_MISSING 1
|
||||
@@ -88,4 +89,6 @@ private:
|
||||
std::size_t slow_shader_first_value = 0;
|
||||
};
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
Q_DECLARE_METATYPE(VideoCore::LoadCallbackStage);
|
||||
#endif
|
||||
|
||||
@@ -52,7 +52,6 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
|
||||
#define QT_NO_OPENGL
|
||||
#include <QClipboard>
|
||||
#include <QDesktopServices>
|
||||
#include <QDesktopWidget>
|
||||
#include <QFile>
|
||||
#include <QFileDialog>
|
||||
#include <QInputDialog>
|
||||
@@ -60,6 +59,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
|
||||
#include <QProgressBar>
|
||||
#include <QProgressDialog>
|
||||
#include <QPushButton>
|
||||
#include <QScreen>
|
||||
#include <QShortcut>
|
||||
#include <QStatusBar>
|
||||
#include <QString>
|
||||
@@ -115,6 +115,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
|
||||
#include "video_core/shader_notify.h"
|
||||
#include "yuzu/about_dialog.h"
|
||||
#include "yuzu/bootmanager.h"
|
||||
#include "yuzu/check_vulkan.h"
|
||||
#include "yuzu/compatdb.h"
|
||||
#include "yuzu/compatibility_list.h"
|
||||
#include "yuzu/configuration/config.h"
|
||||
@@ -198,6 +199,59 @@ static void RemoveCachedContents() {
|
||||
Common::FS::RemoveDirRecursively(offline_system_data);
|
||||
}
|
||||
|
||||
static void LogRuntimes() {
|
||||
#ifdef _MSC_VER
|
||||
// It is possible that the name of the dll will change.
|
||||
// vcruntime140.dll is for 2015 and onwards
|
||||
constexpr char runtime_dll_name[] = "vcruntime140.dll";
|
||||
UINT sz = GetFileVersionInfoSizeA(runtime_dll_name, nullptr);
|
||||
bool runtime_version_inspection_worked = false;
|
||||
if (sz > 0) {
|
||||
std::vector<u8> buf(sz);
|
||||
if (GetFileVersionInfoA(runtime_dll_name, 0, sz, buf.data())) {
|
||||
VS_FIXEDFILEINFO* pvi;
|
||||
sz = sizeof(VS_FIXEDFILEINFO);
|
||||
if (VerQueryValueA(buf.data(), "\\", reinterpret_cast<LPVOID*>(&pvi), &sz)) {
|
||||
if (pvi->dwSignature == VS_FFI_SIGNATURE) {
|
||||
runtime_version_inspection_worked = true;
|
||||
LOG_INFO(Frontend, "MSVC Compiler: {} Runtime: {}.{}.{}.{}", _MSC_VER,
|
||||
pvi->dwProductVersionMS >> 16, pvi->dwProductVersionMS & 0xFFFF,
|
||||
pvi->dwProductVersionLS >> 16, pvi->dwProductVersionLS & 0xFFFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!runtime_version_inspection_worked) {
|
||||
LOG_INFO(Frontend, "Unable to inspect {}", runtime_dll_name);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static QString PrettyProductName() {
|
||||
#ifdef _WIN32
|
||||
// After Windows 10 Version 2004, Microsoft decided to switch to a different notation: 20H2
|
||||
// With that notation change they changed the registry key used to denote the current version
|
||||
QSettings windows_registry(
|
||||
QStringLiteral("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"),
|
||||
QSettings::NativeFormat);
|
||||
const QString release_id = windows_registry.value(QStringLiteral("ReleaseId")).toString();
|
||||
if (release_id == QStringLiteral("2009")) {
|
||||
const u32 current_build = windows_registry.value(QStringLiteral("CurrentBuild")).toUInt();
|
||||
const QString display_version =
|
||||
windows_registry.value(QStringLiteral("DisplayVersion")).toString();
|
||||
const u32 ubr = windows_registry.value(QStringLiteral("UBR")).toUInt();
|
||||
u32 version = 10;
|
||||
if (current_build >= 22000) {
|
||||
version = 11;
|
||||
}
|
||||
return QStringLiteral("Windows %1 Version %2 (Build %3.%4)")
|
||||
.arg(QString::number(version), display_version, QString::number(current_build),
|
||||
QString::number(ubr));
|
||||
}
|
||||
#endif
|
||||
return QSysInfo::prettyProductName();
|
||||
}
|
||||
|
||||
GMainWindow::GMainWindow()
|
||||
: ui{std::make_unique<Ui::MainWindow>()}, system{std::make_unique<Core::System>()},
|
||||
input_subsystem{std::make_shared<InputCommon::InputSubsystem>()},
|
||||
@@ -243,6 +297,7 @@ GMainWindow::GMainWindow()
|
||||
const auto yuzu_build_version = override_build.empty() ? yuzu_build : override_build;
|
||||
|
||||
LOG_INFO(Frontend, "yuzu Version: {}", yuzu_build_version);
|
||||
LogRuntimes();
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
const auto& caps = Common::GetCPUCaps();
|
||||
std::string cpu_string = caps.cpu_string;
|
||||
@@ -259,7 +314,7 @@ GMainWindow::GMainWindow()
|
||||
}
|
||||
LOG_INFO(Frontend, "Host CPU: {}", cpu_string);
|
||||
#endif
|
||||
LOG_INFO(Frontend, "Host OS: {}", QSysInfo::prettyProductName().toStdString());
|
||||
LOG_INFO(Frontend, "Host OS: {}", PrettyProductName().toStdString());
|
||||
LOG_INFO(Frontend, "Host RAM: {:.2f} GiB",
|
||||
Common::GetMemInfo().TotalPhysicalMemory / f64{1_GiB});
|
||||
LOG_INFO(Frontend, "Host Swap: {:.2f} GiB", Common::GetMemInfo().TotalSwapMemory / f64{1_GiB});
|
||||
@@ -297,6 +352,23 @@ GMainWindow::GMainWindow()
|
||||
|
||||
MigrateConfigFiles();
|
||||
|
||||
if (!CheckVulkan()) {
|
||||
config->Save();
|
||||
|
||||
QMessageBox::warning(
|
||||
this, tr("Broken Vulkan Installation Detected"),
|
||||
tr("Vulkan initialization failed on the previous boot.<br><br>Click <a "
|
||||
"href='https://yuzu-emu.org/wiki/faq/"
|
||||
"#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for "
|
||||
"instructions to fix the issue</a>."));
|
||||
}
|
||||
if (UISettings::values.has_broken_vulkan) {
|
||||
Settings::values.renderer_backend = Settings::RendererBackend::OpenGL;
|
||||
|
||||
renderer_status_button->setDisabled(true);
|
||||
renderer_status_button->setChecked(false);
|
||||
}
|
||||
|
||||
#if defined(HAVE_SDL2) && !defined(_WIN32)
|
||||
SDL_InitSubSystem(SDL_INIT_VIDEO);
|
||||
// SDL disables the screen saver by default, and setting the hint
|
||||
@@ -827,12 +899,11 @@ void GMainWindow::InitializeWidgets() {
|
||||
|
||||
// Setup Dock button
|
||||
dock_status_button = new QPushButton();
|
||||
dock_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton"));
|
||||
dock_status_button->setObjectName(QStringLiteral("DockingStatusBarButton"));
|
||||
dock_status_button->setFocusPolicy(Qt::NoFocus);
|
||||
connect(dock_status_button, &QPushButton::clicked, this, &GMainWindow::OnToggleDockedMode);
|
||||
dock_status_button->setText(tr("DOCK"));
|
||||
dock_status_button->setCheckable(true);
|
||||
dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue());
|
||||
UpdateDockedButton();
|
||||
statusBar()->insertPermanentWidget(0, dock_status_button);
|
||||
|
||||
gpu_accuracy_button = new QPushButton();
|
||||
@@ -1002,7 +1073,7 @@ void GMainWindow::InitializeHotkeys() {
|
||||
|
||||
void GMainWindow::SetDefaultUIGeometry() {
|
||||
// geometry: 53% of the window contents are in the upper screen half, 47% in the lower half
|
||||
const QRect screenRect = QApplication::desktop()->screenGeometry(this);
|
||||
const QRect screenRect = QGuiApplication::primaryScreen()->geometry();
|
||||
|
||||
const int w = screenRect.width() * 2 / 3;
|
||||
const int h = screenRect.height() * 2 / 3;
|
||||
@@ -1520,6 +1591,7 @@ void GMainWindow::ShutdownGame() {
|
||||
|
||||
AllowOSSleep();
|
||||
|
||||
system->DetachDebugger();
|
||||
discord_rpc->Pause();
|
||||
emu_thread->RequestStop();
|
||||
|
||||
@@ -1567,7 +1639,7 @@ void GMainWindow::ShutdownGame() {
|
||||
emu_speed_label->setVisible(false);
|
||||
game_fps_label->setVisible(false);
|
||||
emu_frametime_label->setVisible(false);
|
||||
renderer_status_button->setEnabled(true);
|
||||
renderer_status_button->setEnabled(!UISettings::values.has_broken_vulkan);
|
||||
|
||||
game_path.clear();
|
||||
|
||||
@@ -1587,7 +1659,7 @@ void GMainWindow::StoreRecentFile(const QString& filename) {
|
||||
|
||||
void GMainWindow::UpdateRecentFiles() {
|
||||
const int num_recent_files =
|
||||
std::min(UISettings::values.recent_files.size(), max_recent_files_item);
|
||||
std::min(static_cast<int>(UISettings::values.recent_files.size()), max_recent_files_item);
|
||||
|
||||
for (int i = 0; i < num_recent_files; i++) {
|
||||
const QString text = QStringLiteral("&%1. %2").arg(i + 1).arg(
|
||||
@@ -2585,6 +2657,18 @@ void GMainWindow::ToggleFullscreen() {
|
||||
}
|
||||
}
|
||||
|
||||
// We're going to return the screen that the given window has the most pixels on
|
||||
static QScreen* GuessCurrentScreen(QWidget* window) {
|
||||
const QList<QScreen*> screens = QGuiApplication::screens();
|
||||
return *std::max_element(
|
||||
screens.cbegin(), screens.cend(), [window](const QScreen* left, const QScreen* right) {
|
||||
const QSize left_size = left->geometry().intersected(window->geometry()).size();
|
||||
const QSize right_size = right->geometry().intersected(window->geometry()).size();
|
||||
return (left_size.height() * left_size.width()) <
|
||||
(right_size.height() * right_size.width());
|
||||
});
|
||||
}
|
||||
|
||||
void GMainWindow::ShowFullscreen() {
|
||||
const auto show_fullscreen = [](QWidget* window) {
|
||||
if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive) {
|
||||
@@ -2593,7 +2677,7 @@ void GMainWindow::ShowFullscreen() {
|
||||
}
|
||||
window->hide();
|
||||
window->setWindowFlags(window->windowFlags() | Qt::FramelessWindowHint);
|
||||
const auto screen_geometry = QApplication::desktop()->screenGeometry(window);
|
||||
const auto screen_geometry = GuessCurrentScreen(window)->geometry();
|
||||
window->setGeometry(screen_geometry.x(), screen_geometry.y(), screen_geometry.width(),
|
||||
screen_geometry.height() + 1);
|
||||
window->raise();
|
||||
@@ -2777,6 +2861,10 @@ void GMainWindow::OnConfigure() {
|
||||
mouse_hide_timer.start();
|
||||
}
|
||||
|
||||
if (!UISettings::values.has_broken_vulkan) {
|
||||
renderer_status_button->setEnabled(!emulation_running);
|
||||
}
|
||||
|
||||
UpdateStatusButtons();
|
||||
controller_dialog->refreshConfiguration();
|
||||
}
|
||||
@@ -2862,7 +2950,7 @@ void GMainWindow::OnToggleDockedMode() {
|
||||
}
|
||||
|
||||
Settings::values.use_docked_mode.SetValue(!is_docked);
|
||||
dock_status_button->setChecked(!is_docked);
|
||||
UpdateDockedButton();
|
||||
OnDockedModeChanged(is_docked, !is_docked, *system);
|
||||
}
|
||||
|
||||
@@ -3228,6 +3316,12 @@ void GMainWindow::UpdateGPUAccuracyButton() {
|
||||
}
|
||||
}
|
||||
|
||||
void GMainWindow::UpdateDockedButton() {
|
||||
const bool is_docked = Settings::values.use_docked_mode.GetValue();
|
||||
dock_status_button->setChecked(is_docked);
|
||||
dock_status_button->setText(is_docked ? tr("DOCKED") : tr("HANDHELD"));
|
||||
}
|
||||
|
||||
void GMainWindow::UpdateFilterText() {
|
||||
const auto filter = Settings::values.scaling_filter.GetValue();
|
||||
switch (filter) {
|
||||
@@ -3271,10 +3365,10 @@ void GMainWindow::UpdateAAText() {
|
||||
}
|
||||
|
||||
void GMainWindow::UpdateStatusButtons() {
|
||||
dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue());
|
||||
renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() ==
|
||||
Settings::RendererBackend::Vulkan);
|
||||
UpdateGPUAccuracyButton();
|
||||
UpdateDockedButton();
|
||||
UpdateFilterText();
|
||||
UpdateAAText();
|
||||
}
|
||||
@@ -3325,7 +3419,7 @@ void GMainWindow::CenterMouseCursor() {
|
||||
const int center_x = render_window->width() / 2;
|
||||
const int center_y = render_window->height() / 2;
|
||||
|
||||
QCursor::setPos(mapToGlobal({center_x, center_y}));
|
||||
QCursor::setPos(mapToGlobal(QPoint{center_x, center_y}));
|
||||
}
|
||||
|
||||
void GMainWindow::OnMouseActivity() {
|
||||
|
||||
@@ -320,6 +320,7 @@ private:
|
||||
void MigrateConfigFiles();
|
||||
void UpdateWindowTitle(std::string_view title_name = {}, std::string_view title_version = {},
|
||||
std::string_view gpu_vendor = {});
|
||||
void UpdateDockedButton();
|
||||
void UpdateFilterText();
|
||||
void UpdateAAText();
|
||||
void UpdateStatusBar();
|
||||
|
||||
@@ -77,6 +77,8 @@ struct Values {
|
||||
Settings::BasicSetting<bool> pause_when_in_background{false, "pauseWhenInBackground"};
|
||||
Settings::BasicSetting<bool> mute_when_in_background{false, "muteWhenInBackground"};
|
||||
Settings::BasicSetting<bool> hide_mouse{true, "hideInactiveMouse"};
|
||||
// Set when Vulkan is known to crash the application
|
||||
Settings::BasicSetting<bool> has_broken_vulkan{false, "has_broken_vulkan"};
|
||||
|
||||
Settings::BasicSetting<bool> select_user_on_boot{false, "select_user_on_boot"};
|
||||
|
||||
|
||||
@@ -344,6 +344,8 @@ void Config::ReadValues() {
|
||||
ReadSetting("Debugging", Settings::values.use_debug_asserts);
|
||||
ReadSetting("Debugging", Settings::values.use_auto_stub);
|
||||
ReadSetting("Debugging", Settings::values.disable_macro_jit);
|
||||
ReadSetting("Debugging", Settings::values.use_gdbstub);
|
||||
ReadSetting("Debugging", Settings::values.gdbstub_port);
|
||||
|
||||
const auto title_list = sdl2_config->Get("AddOns", "title_ids", "");
|
||||
std::stringstream ss(title_list);
|
||||
|
||||
@@ -218,7 +218,7 @@ cpuopt_unsafe_ignore_global_monitor =
|
||||
|
||||
[Renderer]
|
||||
# Which backend API to use.
|
||||
# 0 (default): OpenGL, 1: Vulkan
|
||||
# 0: OpenGL, 1 (default): Vulkan
|
||||
backend =
|
||||
|
||||
# Enable graphics API debugging mode.
|
||||
@@ -437,6 +437,11 @@ disable_macro_jit=false
|
||||
# Presents guest frames as they become available. Experimental.
|
||||
# false: Disabled (default), true: Enabled
|
||||
disable_fps_limit=false
|
||||
# Determines whether to enable the GDB stub and wait for the debugger to attach before running.
|
||||
# false: Disabled (default), true: Enabled
|
||||
use_gdbstub=false
|
||||
# The port to use for the GDB server, if it is enabled.
|
||||
gdbstub_port=6543
|
||||
|
||||
[WebService]
|
||||
# Whether or not to enable telemetry
|
||||
|
||||
@@ -162,7 +162,15 @@ void EmuWindow_SDL2::WaitEvent() {
|
||||
SDL_Event event;
|
||||
|
||||
if (!SDL_WaitEvent(&event)) {
|
||||
LOG_CRITICAL(Frontend, "SDL_WaitEvent failed: {}", SDL_GetError());
|
||||
const char* error = SDL_GetError();
|
||||
if (!error || strcmp(error, "") == 0) {
|
||||
// https://github.com/libsdl-org/SDL/issues/5780
|
||||
// Sometimes SDL will return without actually having hit an error condition;
|
||||
// just ignore it in this case.
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_CRITICAL(Frontend, "SDL_WaitEvent failed: {}", error);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
@@ -217,10 +217,19 @@ int main(int argc, char** argv) {
|
||||
[](VideoCore::LoadCallbackStage, size_t value, size_t total) {});
|
||||
}
|
||||
|
||||
system.RegisterExitCallback([&] {
|
||||
// Just exit right away.
|
||||
exit(0);
|
||||
});
|
||||
|
||||
void(system.Run());
|
||||
if (system.DebuggerEnabled()) {
|
||||
system.InitializeDebugger();
|
||||
}
|
||||
while (emu_window->IsOpen()) {
|
||||
emu_window->WaitEvent();
|
||||
}
|
||||
system.DetachDebugger();
|
||||
void(system.Pause());
|
||||
system.Shutdown();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user