Compare commits
50 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cb7f0c2ec3 | ||
|
|
c86e21abe4 | ||
|
|
201733d1b5 | ||
|
|
db15142ac9 | ||
|
|
fa231645f2 | ||
|
|
646656412f | ||
|
|
60ce34aa80 | ||
|
|
ae6015a69b | ||
|
|
053ad04d3f | ||
|
|
1b11e0f0d3 | ||
|
|
fe126f993d | ||
|
|
9f199c8b0b | ||
|
|
6cb6b2da8e | ||
|
|
e44d1fe73c | ||
|
|
b60a93a936 | ||
|
|
42d81aab32 | ||
|
|
864c8e4b2f | ||
|
|
690a4c9438 | ||
|
|
190ded7f48 | ||
|
|
c770f25ccb | ||
|
|
67c0d714c5 | ||
|
|
cf01a507fb | ||
|
|
fcc93a445f | ||
|
|
79f1f326c7 | ||
|
|
2724ffd6e3 | ||
|
|
ee71404d71 | ||
|
|
dcc8abf254 | ||
|
|
56b0f979eb | ||
|
|
c218c7d4da | ||
|
|
f999d268f9 | ||
|
|
c489cbee29 | ||
|
|
dffeca66fa | ||
|
|
fd1ea0fd84 | ||
|
|
48108a8c9b | ||
|
|
92ce241d4d | ||
|
|
d3123079e8 | ||
|
|
5da72a891f | ||
|
|
f8a7d6a0ae | ||
|
|
da31326c17 | ||
|
|
45672d43e3 | ||
|
|
a3bac5550d | ||
|
|
4faea2bbf4 | ||
|
|
fa10374d39 | ||
|
|
98135dee16 | ||
|
|
7bf4bec257 | ||
|
|
f7d95d0a3a | ||
|
|
6a56f42f5d | ||
|
|
b81caf1879 | ||
|
|
b1d633532f | ||
|
|
0104e28fe4 |
@@ -11,6 +11,7 @@ ccache -s
|
||||
mkdir build || true && cd build
|
||||
cmake .. \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_CXX_FLAGS="-march=x86-64-v2" \
|
||||
-DCMAKE_CXX_COMPILER=/usr/lib/ccache/clang++ \
|
||||
-DCMAKE_C_COMPILER=/usr/lib/ccache/clang \
|
||||
-DCMAKE_INSTALL_PREFIX="/usr" \
|
||||
|
||||
@@ -12,6 +12,7 @@ mkdir build || true && cd build
|
||||
cmake .. \
|
||||
-DBoost_USE_STATIC_LIBS=ON \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_CXX_FLAGS="-march=x86-64-v2" \
|
||||
-DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ \
|
||||
-DCMAKE_C_COMPILER=/usr/lib/ccache/gcc \
|
||||
-DCMAKE_INSTALL_PREFIX="/usr" \
|
||||
|
||||
@@ -9,7 +9,7 @@ parameters:
|
||||
steps:
|
||||
- script: choco install vulkan-sdk
|
||||
displayName: 'Install vulkan-sdk'
|
||||
- script: refreshenv && mkdir build && cd build && cmake -G "Visual Studio 17 2022" -A x64 -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DYUZU_TESTS=OFF -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DDISPLAY_VERSION=${{ parameters['version'] }} -DCMAKE_BUILD_TYPE=Release -DYUZU_CRASH_DUMPS=ON .. && cd ..
|
||||
- script: refreshenv && mkdir build && cd build && cmake -E env CXXFLAGS="/Gw /GA /Gr /Ob2" cmake -G "Visual Studio 17 2022" -A x64 -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON -DCMAKE_POLICY_DEFAULT_CMP0069=NEW -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DYUZU_TESTS=OFF -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DDISPLAY_VERSION=${{ parameters['version'] }} -DCMAKE_BUILD_TYPE=Release -DYUZU_CRASH_DUMPS=ON .. && cd ..
|
||||
displayName: 'Configure CMake'
|
||||
- task: MSBuild@1
|
||||
displayName: 'Build'
|
||||
|
||||
@@ -208,7 +208,7 @@ find_package(libusb 1.0.24)
|
||||
find_package(lz4 REQUIRED)
|
||||
find_package(nlohmann_json 3.8 REQUIRED)
|
||||
find_package(Opus 1.3)
|
||||
find_package(Vulkan 1.3.213)
|
||||
find_package(Vulkan 1.3.238)
|
||||
find_package(ZLIB 1.2 REQUIRED)
|
||||
find_package(zstd 1.5 REQUIRED)
|
||||
|
||||
|
||||
2
externals/Vulkan-Headers
vendored
2
externals/Vulkan-Headers
vendored
Submodule externals/Vulkan-Headers updated: 2826791bed...00671c64ba
@@ -91,9 +91,10 @@ public:
|
||||
* @param core_timing - The CoreTiming instance
|
||||
* @param session - The device session
|
||||
*
|
||||
* @return Is the buffer was released.
|
||||
* @return If any buffer was released.
|
||||
*/
|
||||
bool ReleaseBuffers(const Core::Timing::CoreTiming& core_timing, const DeviceSession& session) {
|
||||
bool ReleaseBuffers(const Core::Timing::CoreTiming& core_timing, const DeviceSession& session,
|
||||
bool force) {
|
||||
std::scoped_lock l{lock};
|
||||
bool buffer_released{false};
|
||||
while (registered_count > 0) {
|
||||
@@ -103,7 +104,8 @@ public:
|
||||
}
|
||||
|
||||
// Check with the backend if this buffer can be released yet.
|
||||
if (!session.IsBufferConsumed(buffers[index])) {
|
||||
// If we're shutting down, we don't care if it's been played or not.
|
||||
if (!force && !session.IsBufferConsumed(buffers[index])) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -73,6 +73,12 @@ void DeviceSession::Stop() {
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceSession::ClearBuffers() {
|
||||
if (stream) {
|
||||
stream->ClearQueue();
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceSession::AppendBuffers(std::span<const AudioBuffer> buffers) const {
|
||||
for (const auto& buffer : buffers) {
|
||||
Sink::SinkBuffer new_buffer{
|
||||
|
||||
@@ -90,6 +90,11 @@ public:
|
||||
*/
|
||||
void Stop();
|
||||
|
||||
/**
|
||||
* Clear out the underlying audio buffers in the backend stream.
|
||||
*/
|
||||
void ClearBuffers();
|
||||
|
||||
/**
|
||||
* Set this device session's volume.
|
||||
*
|
||||
|
||||
@@ -23,7 +23,6 @@ System::~System() {
|
||||
void System::Finalize() {
|
||||
Stop();
|
||||
session->Finalize();
|
||||
buffer_event->Signal();
|
||||
}
|
||||
|
||||
void System::StartSession() {
|
||||
@@ -102,6 +101,10 @@ Result System::Stop() {
|
||||
if (state == State::Started) {
|
||||
session->Stop();
|
||||
session->SetVolume(0.0f);
|
||||
session->ClearBuffers();
|
||||
if (buffers.ReleaseBuffers(system.CoreTiming(), *session, true)) {
|
||||
buffer_event->Signal();
|
||||
}
|
||||
state = State::Stopped;
|
||||
}
|
||||
|
||||
@@ -138,7 +141,7 @@ void System::RegisterBuffers() {
|
||||
}
|
||||
|
||||
void System::ReleaseBuffers() {
|
||||
bool signal{buffers.ReleaseBuffers(system.CoreTiming(), *session)};
|
||||
bool signal{buffers.ReleaseBuffers(system.CoreTiming(), *session, false)};
|
||||
|
||||
if (signal) {
|
||||
// Signal if any buffer was released, or if none are registered, we need more.
|
||||
|
||||
@@ -24,7 +24,6 @@ System::~System() {
|
||||
void System::Finalize() {
|
||||
Stop();
|
||||
session->Finalize();
|
||||
buffer_event->Signal();
|
||||
}
|
||||
|
||||
std::string_view System::GetDefaultOutputDeviceName() const {
|
||||
@@ -102,6 +101,10 @@ Result System::Stop() {
|
||||
if (state == State::Started) {
|
||||
session->Stop();
|
||||
session->SetVolume(0.0f);
|
||||
session->ClearBuffers();
|
||||
if (buffers.ReleaseBuffers(system.CoreTiming(), *session, true)) {
|
||||
buffer_event->Signal();
|
||||
}
|
||||
state = State::Stopped;
|
||||
}
|
||||
|
||||
@@ -138,7 +141,7 @@ void System::RegisterBuffers() {
|
||||
}
|
||||
|
||||
void System::ReleaseBuffers() {
|
||||
bool signal{buffers.ReleaseBuffers(system.CoreTiming(), *session)};
|
||||
bool signal{buffers.ReleaseBuffers(system.CoreTiming(), *session, false)};
|
||||
if (signal) {
|
||||
// Signal if any buffer was released, or if none are registered, we need more.
|
||||
buffer_event->Signal();
|
||||
|
||||
@@ -69,7 +69,7 @@ void assert_fail_impl();
|
||||
#define ASSERT_OR_EXECUTE(_a_, _b_) \
|
||||
do { \
|
||||
ASSERT(_a_); \
|
||||
if (!(_a_)) { \
|
||||
if (!(_a_)) [[unlikely]] { \
|
||||
_b_ \
|
||||
} \
|
||||
} while (0)
|
||||
@@ -78,7 +78,7 @@ void assert_fail_impl();
|
||||
#define ASSERT_OR_EXECUTE_MSG(_a_, _b_, ...) \
|
||||
do { \
|
||||
ASSERT_MSG(_a_, __VA_ARGS__); \
|
||||
if (!(_a_)) { \
|
||||
if (!(_a_)) [[unlikely]] { \
|
||||
_b_ \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
@@ -201,6 +201,9 @@ add_library(core STATIC
|
||||
hle/kernel/k_event_info.h
|
||||
hle/kernel/k_handle_table.cpp
|
||||
hle/kernel/k_handle_table.h
|
||||
hle/kernel/k_hardware_timer_base.h
|
||||
hle/kernel/k_hardware_timer.cpp
|
||||
hle/kernel/k_hardware_timer.h
|
||||
hle/kernel/k_interrupt_manager.cpp
|
||||
hle/kernel/k_interrupt_manager.h
|
||||
hle/kernel/k_light_condition_variable.cpp
|
||||
@@ -268,6 +271,7 @@ add_library(core STATIC
|
||||
hle/kernel/k_thread_local_page.h
|
||||
hle/kernel/k_thread_queue.cpp
|
||||
hle/kernel/k_thread_queue.h
|
||||
hle/kernel/k_timer_task.h
|
||||
hle/kernel/k_trace.h
|
||||
hle/kernel/k_transfer_memory.cpp
|
||||
hle/kernel/k_transfer_memory.h
|
||||
@@ -290,8 +294,6 @@ add_library(core STATIC
|
||||
hle/kernel/svc_common.h
|
||||
hle/kernel/svc_types.h
|
||||
hle/kernel/svc_wrap.h
|
||||
hle/kernel/time_manager.cpp
|
||||
hle/kernel/time_manager.h
|
||||
hle/result.h
|
||||
hle/service/acc/acc.cpp
|
||||
hle/service/acc/acc.h
|
||||
|
||||
@@ -183,26 +183,20 @@ struct System::Impl {
|
||||
Initialize(system);
|
||||
}
|
||||
|
||||
SystemResultStatus Run() {
|
||||
void Run() {
|
||||
std::unique_lock<std::mutex> lk(suspend_guard);
|
||||
status = SystemResultStatus::Success;
|
||||
|
||||
kernel.Suspend(false);
|
||||
core_timing.SyncPause(false);
|
||||
is_paused.store(false, std::memory_order_relaxed);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
SystemResultStatus Pause() {
|
||||
void Pause() {
|
||||
std::unique_lock<std::mutex> lk(suspend_guard);
|
||||
status = SystemResultStatus::Success;
|
||||
|
||||
core_timing.SyncPause(true);
|
||||
kernel.Suspend(true);
|
||||
is_paused.store(true, std::memory_order_relaxed);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
bool IsPaused() const {
|
||||
@@ -553,12 +547,12 @@ void System::Initialize() {
|
||||
impl->Initialize(*this);
|
||||
}
|
||||
|
||||
SystemResultStatus System::Run() {
|
||||
return impl->Run();
|
||||
void System::Run() {
|
||||
impl->Run();
|
||||
}
|
||||
|
||||
SystemResultStatus System::Pause() {
|
||||
return impl->Pause();
|
||||
void System::Pause() {
|
||||
impl->Pause();
|
||||
}
|
||||
|
||||
bool System::IsPaused() const {
|
||||
|
||||
@@ -152,13 +152,13 @@ public:
|
||||
* Run the OS and Application
|
||||
* This function will start emulation and run the relevant devices
|
||||
*/
|
||||
[[nodiscard]] SystemResultStatus Run();
|
||||
void Run();
|
||||
|
||||
/**
|
||||
* Pause the OS and Application
|
||||
* This function will pause emulation and stop the relevant devices
|
||||
*/
|
||||
[[nodiscard]] SystemResultStatus Pause();
|
||||
void Pause();
|
||||
|
||||
/// Check if the core is currently paused.
|
||||
[[nodiscard]] bool IsPaused() const;
|
||||
|
||||
@@ -326,25 +326,23 @@ Result HLERequestContext::WriteToOutgoingCommandBuffer(KThread& requesting_threa
|
||||
}
|
||||
|
||||
std::vector<u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const {
|
||||
std::vector<u8> buffer{};
|
||||
const bool is_buffer_a{BufferDescriptorA().size() > buffer_index &&
|
||||
BufferDescriptorA()[buffer_index].Size()};
|
||||
|
||||
if (is_buffer_a) {
|
||||
ASSERT_OR_EXECUTE_MSG(
|
||||
BufferDescriptorA().size() > buffer_index, { return buffer; },
|
||||
BufferDescriptorA().size() > buffer_index, { return {}; },
|
||||
"BufferDescriptorA invalid buffer_index {}", buffer_index);
|
||||
buffer.resize(BufferDescriptorA()[buffer_index].Size());
|
||||
std::vector<u8> buffer(BufferDescriptorA()[buffer_index].Size());
|
||||
memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), buffer.size());
|
||||
return buffer;
|
||||
} else {
|
||||
ASSERT_OR_EXECUTE_MSG(
|
||||
BufferDescriptorX().size() > buffer_index, { return buffer; },
|
||||
BufferDescriptorX().size() > buffer_index, { return {}; },
|
||||
"BufferDescriptorX invalid buffer_index {}", buffer_index);
|
||||
buffer.resize(BufferDescriptorX()[buffer_index].Size());
|
||||
std::vector<u8> buffer(BufferDescriptorX()[buffer_index].Size());
|
||||
memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), buffer.size());
|
||||
return buffer;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size,
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include "core/hle/kernel/k_thread_queue.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/svc_results.h"
|
||||
#include "core/hle/kernel/time_manager.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
74
src/core/hle/kernel/k_hardware_timer.cpp
Normal file
74
src/core/hle/kernel/k_hardware_timer.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/kernel/k_hardware_timer.h"
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
void KHardwareTimer::Initialize() {
|
||||
// Create the timing callback to register with CoreTiming.
|
||||
m_event_type = Core::Timing::CreateEvent(
|
||||
"KHardwareTimer::Callback", [](std::uintptr_t timer_handle, s64, std::chrono::nanoseconds) {
|
||||
reinterpret_cast<KHardwareTimer*>(timer_handle)->DoTask();
|
||||
return std::nullopt;
|
||||
});
|
||||
}
|
||||
|
||||
void KHardwareTimer::Finalize() {
|
||||
this->DisableInterrupt();
|
||||
m_event_type.reset();
|
||||
}
|
||||
|
||||
void KHardwareTimer::DoTask() {
|
||||
// Handle the interrupt.
|
||||
{
|
||||
KScopedSchedulerLock slk{m_kernel};
|
||||
KScopedSpinLock lk(this->GetLock());
|
||||
|
||||
//! Ignore this event if needed.
|
||||
if (!this->GetInterruptEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Disable the timer interrupt while we handle this.
|
||||
this->DisableInterrupt();
|
||||
|
||||
if (const s64 next_time = this->DoInterruptTaskImpl(GetTick());
|
||||
0 < next_time && next_time <= m_wakeup_time) {
|
||||
// We have a next time, so we should set the time to interrupt and turn the interrupt
|
||||
// on.
|
||||
this->EnableInterrupt(next_time);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the timer interrupt.
|
||||
// Kernel::GetInterruptManager().ClearInterrupt(KInterruptName_NonSecurePhysicalTimer,
|
||||
// GetCurrentCoreId());
|
||||
}
|
||||
|
||||
void KHardwareTimer::EnableInterrupt(s64 wakeup_time) {
|
||||
this->DisableInterrupt();
|
||||
|
||||
m_wakeup_time = wakeup_time;
|
||||
m_kernel.System().CoreTiming().ScheduleEvent(std::chrono::nanoseconds{m_wakeup_time},
|
||||
m_event_type, reinterpret_cast<uintptr_t>(this),
|
||||
true);
|
||||
}
|
||||
|
||||
void KHardwareTimer::DisableInterrupt() {
|
||||
m_kernel.System().CoreTiming().UnscheduleEvent(m_event_type, reinterpret_cast<uintptr_t>(this));
|
||||
m_wakeup_time = std::numeric_limits<s64>::max();
|
||||
}
|
||||
|
||||
s64 KHardwareTimer::GetTick() const {
|
||||
return m_kernel.System().CoreTiming().GetGlobalTimeNs().count();
|
||||
}
|
||||
|
||||
bool KHardwareTimer::GetInterruptEnabled() {
|
||||
return m_wakeup_time != std::numeric_limits<s64>::max();
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
54
src/core/hle/kernel/k_hardware_timer.h
Normal file
54
src/core/hle/kernel/k_hardware_timer.h
Normal file
@@ -0,0 +1,54 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/kernel/k_hardware_timer_base.h"
|
||||
|
||||
namespace Core::Timing {
|
||||
struct EventType;
|
||||
} // namespace Core::Timing
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KHardwareTimer : /* public KInterruptTask, */ public KHardwareTimerBase {
|
||||
public:
|
||||
explicit KHardwareTimer(KernelCore& kernel) : KHardwareTimerBase{kernel} {}
|
||||
|
||||
// Public API.
|
||||
void Initialize();
|
||||
void Finalize();
|
||||
|
||||
s64 GetCount() const {
|
||||
return GetTick();
|
||||
}
|
||||
|
||||
void RegisterTask(KTimerTask* task, s64 time_from_now) {
|
||||
this->RegisterAbsoluteTask(task, GetTick() + time_from_now);
|
||||
}
|
||||
|
||||
void RegisterAbsoluteTask(KTimerTask* task, s64 task_time) {
|
||||
KScopedDisableDispatch dd{m_kernel};
|
||||
KScopedSpinLock lk{this->GetLock()};
|
||||
|
||||
if (this->RegisterAbsoluteTaskImpl(task, task_time)) {
|
||||
if (task_time <= m_wakeup_time) {
|
||||
this->EnableInterrupt(task_time);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void EnableInterrupt(s64 wakeup_time);
|
||||
void DisableInterrupt();
|
||||
bool GetInterruptEnabled();
|
||||
s64 GetTick() const;
|
||||
void DoTask();
|
||||
|
||||
private:
|
||||
// Absolute time in nanoseconds
|
||||
s64 m_wakeup_time{std::numeric_limits<s64>::max()};
|
||||
std::shared_ptr<Core::Timing::EventType> m_event_type{};
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
92
src/core/hle/kernel/k_hardware_timer_base.h
Normal file
92
src/core/hle/kernel/k_hardware_timer_base.h
Normal file
@@ -0,0 +1,92 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/kernel/k_spin_lock.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/k_timer_task.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KHardwareTimerBase {
|
||||
public:
|
||||
explicit KHardwareTimerBase(KernelCore& kernel) : m_kernel{kernel} {}
|
||||
|
||||
void CancelTask(KTimerTask* task) {
|
||||
KScopedDisableDispatch dd{m_kernel};
|
||||
KScopedSpinLock lk{m_lock};
|
||||
|
||||
if (const s64 task_time = task->GetTime(); task_time > 0) {
|
||||
this->RemoveTaskFromTree(task);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
KSpinLock& GetLock() {
|
||||
return m_lock;
|
||||
}
|
||||
|
||||
s64 DoInterruptTaskImpl(s64 cur_time) {
|
||||
// We want to handle all tasks, returning the next time that a task is scheduled.
|
||||
while (true) {
|
||||
// Get the next task. If there isn't one, return 0.
|
||||
KTimerTask* task = m_next_task;
|
||||
if (task == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If the task needs to be done in the future, do it in the future and not now.
|
||||
if (const s64 task_time = task->GetTime(); task_time > cur_time) {
|
||||
return task_time;
|
||||
}
|
||||
|
||||
// Remove the task from the tree of tasks, and update our next task.
|
||||
this->RemoveTaskFromTree(task);
|
||||
|
||||
// Handle the task.
|
||||
task->OnTimer();
|
||||
}
|
||||
}
|
||||
|
||||
bool RegisterAbsoluteTaskImpl(KTimerTask* task, s64 task_time) {
|
||||
ASSERT(task_time > 0);
|
||||
|
||||
// Set the task's time, and insert it into our tree.
|
||||
task->SetTime(task_time);
|
||||
m_task_tree.insert(*task);
|
||||
|
||||
// Update our next task if relevant.
|
||||
if (m_next_task != nullptr && m_next_task->GetTime() <= task_time) {
|
||||
return false;
|
||||
}
|
||||
m_next_task = task;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
void RemoveTaskFromTree(KTimerTask* task) {
|
||||
// Erase from the tree.
|
||||
auto it = m_task_tree.erase(m_task_tree.iterator_to(*task));
|
||||
|
||||
// Clear the task's scheduled time.
|
||||
task->SetTime(0);
|
||||
|
||||
// Update our next task if relevant.
|
||||
if (m_next_task == task) {
|
||||
m_next_task = (it != m_task_tree.end()) ? std::addressof(*it) : nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
KernelCore& m_kernel;
|
||||
|
||||
private:
|
||||
using TimerTaskTree = Common::IntrusiveRedBlackTreeBaseTraits<KTimerTask>::TreeType<KTimerTask>;
|
||||
|
||||
KSpinLock m_lock{};
|
||||
TimerTaskTree m_task_tree{};
|
||||
KTimerTask* m_next_task{};
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
@@ -5,9 +5,9 @@
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/global_scheduler_context.h"
|
||||
#include "core/hle/kernel/k_hardware_timer.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/time_manager.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
@@ -22,7 +22,7 @@ public:
|
||||
~KScopedSchedulerLockAndSleep() {
|
||||
// Register the sleep.
|
||||
if (timeout_tick > 0) {
|
||||
kernel.TimeManager().ScheduleTimeEvent(thread, timeout_tick);
|
||||
kernel.HardwareTimer().RegisterTask(thread, timeout_tick);
|
||||
}
|
||||
|
||||
// Unlock the scheduler.
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "core/hle/kernel/k_light_lock.h"
|
||||
#include "core/hle/kernel/k_spin_lock.h"
|
||||
#include "core/hle/kernel/k_synchronization_object.h"
|
||||
#include "core/hle/kernel/k_timer_task.h"
|
||||
#include "core/hle/kernel/k_worker_task.h"
|
||||
#include "core/hle/kernel/slab_helpers.h"
|
||||
#include "core/hle/kernel/svc_common.h"
|
||||
@@ -112,7 +113,8 @@ void SetCurrentThread(KernelCore& kernel, KThread* thread);
|
||||
[[nodiscard]] s32 GetCurrentCoreId(KernelCore& kernel);
|
||||
|
||||
class KThread final : public KAutoObjectWithSlabHeapAndContainer<KThread, KWorkerTask>,
|
||||
public boost::intrusive::list_base_hook<> {
|
||||
public boost::intrusive::list_base_hook<>,
|
||||
public KTimerTask {
|
||||
KERNEL_AUTOOBJECT_TRAITS(KThread, KSynchronizationObject);
|
||||
|
||||
private:
|
||||
@@ -840,4 +842,8 @@ private:
|
||||
KernelCore& kernel;
|
||||
};
|
||||
|
||||
inline void KTimerTask::OnTimer() {
|
||||
static_cast<KThread*>(this)->OnTimer();
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/hle/kernel/k_hardware_timer.h"
|
||||
#include "core/hle/kernel/k_thread_queue.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/time_manager.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
@@ -22,7 +22,7 @@ void KThreadQueue::EndWait(KThread* waiting_thread, Result wait_result) {
|
||||
waiting_thread->ClearWaitQueue();
|
||||
|
||||
// Cancel the thread task.
|
||||
kernel.TimeManager().UnscheduleTimeEvent(waiting_thread);
|
||||
kernel.HardwareTimer().CancelTask(waiting_thread);
|
||||
}
|
||||
|
||||
void KThreadQueue::CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) {
|
||||
@@ -37,7 +37,7 @@ void KThreadQueue::CancelWait(KThread* waiting_thread, Result wait_result, bool
|
||||
|
||||
// Cancel the thread task.
|
||||
if (cancel_timer_task) {
|
||||
kernel.TimeManager().UnscheduleTimeEvent(waiting_thread);
|
||||
kernel.HardwareTimer().CancelTask(waiting_thread);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
40
src/core/hle/kernel/k_timer_task.h
Normal file
40
src/core/hle/kernel/k_timer_task.h
Normal file
@@ -0,0 +1,40 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/intrusive_red_black_tree.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KTimerTask : public Common::IntrusiveRedBlackTreeBaseNode<KTimerTask> {
|
||||
public:
|
||||
static constexpr int Compare(const KTimerTask& lhs, const KTimerTask& rhs) {
|
||||
if (lhs.GetTime() < rhs.GetTime()) {
|
||||
return -1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr explicit KTimerTask() = default;
|
||||
|
||||
constexpr void SetTime(s64 t) {
|
||||
m_time = t;
|
||||
}
|
||||
|
||||
constexpr s64 GetTime() const {
|
||||
return m_time;
|
||||
}
|
||||
|
||||
// NOTE: This is virtual in Nintendo's kernel. Prior to 13.0.0, KWaitObject was also a
|
||||
// TimerTask; this is no longer the case. Since this is now KThread exclusive, we have
|
||||
// devirtualized (see inline declaration for this inside k_thread.h).
|
||||
void OnTimer();
|
||||
|
||||
private:
|
||||
// Absolute time in nanoseconds
|
||||
s64 m_time{};
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "core/hle/kernel/k_client_port.h"
|
||||
#include "core/hle/kernel/k_dynamic_resource_manager.h"
|
||||
#include "core/hle/kernel/k_handle_table.h"
|
||||
#include "core/hle/kernel/k_hardware_timer.h"
|
||||
#include "core/hle/kernel/k_memory_layout.h"
|
||||
#include "core/hle/kernel/k_memory_manager.h"
|
||||
#include "core/hle/kernel/k_page_buffer.h"
|
||||
@@ -39,7 +40,6 @@
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/physical_core.h"
|
||||
#include "core/hle/kernel/service_thread.h"
|
||||
#include "core/hle/kernel/time_manager.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "core/memory.h"
|
||||
@@ -55,7 +55,7 @@ struct KernelCore::Impl {
|
||||
static constexpr size_t ReservedDynamicPageCount = 64;
|
||||
|
||||
explicit Impl(Core::System& system_, KernelCore& kernel_)
|
||||
: time_manager{system_}, service_threads_manager{1, "ServiceThreadsManager"},
|
||||
: service_threads_manager{1, "ServiceThreadsManager"},
|
||||
service_thread_barrier{2}, system{system_} {}
|
||||
|
||||
void SetMulticore(bool is_multi) {
|
||||
@@ -63,6 +63,9 @@ struct KernelCore::Impl {
|
||||
}
|
||||
|
||||
void Initialize(KernelCore& kernel) {
|
||||
hardware_timer = std::make_unique<Kernel::KHardwareTimer>(kernel);
|
||||
hardware_timer->Initialize();
|
||||
|
||||
global_object_list_container = std::make_unique<KAutoObjectWithListContainer>(kernel);
|
||||
global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel);
|
||||
global_handle_table = std::make_unique<Kernel::KHandleTable>(kernel);
|
||||
@@ -193,6 +196,9 @@ struct KernelCore::Impl {
|
||||
// Ensure that the object list container is finalized and properly shutdown.
|
||||
global_object_list_container->Finalize();
|
||||
global_object_list_container.reset();
|
||||
|
||||
hardware_timer->Finalize();
|
||||
hardware_timer.reset();
|
||||
}
|
||||
|
||||
void CloseServices() {
|
||||
@@ -832,7 +838,7 @@ struct KernelCore::Impl {
|
||||
std::vector<KProcess*> process_list;
|
||||
std::atomic<KProcess*> current_process{};
|
||||
std::unique_ptr<Kernel::GlobalSchedulerContext> global_scheduler_context;
|
||||
Kernel::TimeManager time_manager;
|
||||
std::unique_ptr<Kernel::KHardwareTimer> hardware_timer;
|
||||
|
||||
Init::KSlabResourceCounts slab_resource_counts{};
|
||||
KResourceLimit* system_resource_limit{};
|
||||
@@ -1019,12 +1025,8 @@ Kernel::KScheduler* KernelCore::CurrentScheduler() {
|
||||
return impl->schedulers[core_id].get();
|
||||
}
|
||||
|
||||
Kernel::TimeManager& KernelCore::TimeManager() {
|
||||
return impl->time_manager;
|
||||
}
|
||||
|
||||
const Kernel::TimeManager& KernelCore::TimeManager() const {
|
||||
return impl->time_manager;
|
||||
Kernel::KHardwareTimer& KernelCore::HardwareTimer() {
|
||||
return *impl->hardware_timer;
|
||||
}
|
||||
|
||||
Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() {
|
||||
|
||||
@@ -39,6 +39,7 @@ class KDynamicPageManager;
|
||||
class KEvent;
|
||||
class KEventInfo;
|
||||
class KHandleTable;
|
||||
class KHardwareTimer;
|
||||
class KLinkedListNode;
|
||||
class KMemoryLayout;
|
||||
class KMemoryManager;
|
||||
@@ -63,7 +64,6 @@ class KCodeMemory;
|
||||
class PhysicalCore;
|
||||
class ServiceThread;
|
||||
class Synchronization;
|
||||
class TimeManager;
|
||||
|
||||
using ServiceInterfaceFactory =
|
||||
std::function<KClientPort&(Service::SM::ServiceManager&, Core::System&)>;
|
||||
@@ -175,11 +175,8 @@ public:
|
||||
/// Gets the an instance of the current physical CPU core.
|
||||
const Kernel::PhysicalCore& CurrentPhysicalCore() const;
|
||||
|
||||
/// Gets the an instance of the TimeManager Interface.
|
||||
Kernel::TimeManager& TimeManager();
|
||||
|
||||
/// Gets the an instance of the TimeManager Interface.
|
||||
const Kernel::TimeManager& TimeManager() const;
|
||||
/// Gets the an instance of the hardware timer.
|
||||
Kernel::KHardwareTimer& HardwareTimer();
|
||||
|
||||
/// Stops execution of 'id' core, in order to reschedule a new thread.
|
||||
void PrepareReschedule(std::size_t id);
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/time_manager.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
TimeManager::TimeManager(Core::System& system_) : system{system_} {
|
||||
time_manager_event_type = Core::Timing::CreateEvent(
|
||||
"Kernel::TimeManagerCallback",
|
||||
[this](std::uintptr_t thread_handle, s64 time,
|
||||
std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> {
|
||||
KThread* thread = reinterpret_cast<KThread*>(thread_handle);
|
||||
{
|
||||
KScopedSchedulerLock sl(system.Kernel());
|
||||
thread->OnTimer();
|
||||
}
|
||||
return std::nullopt;
|
||||
});
|
||||
}
|
||||
|
||||
void TimeManager::ScheduleTimeEvent(KThread* thread, s64 nanoseconds) {
|
||||
std::scoped_lock lock{mutex};
|
||||
if (nanoseconds > 0) {
|
||||
ASSERT(thread);
|
||||
ASSERT(thread->GetState() != ThreadState::Runnable);
|
||||
system.CoreTiming().ScheduleEvent(std::chrono::nanoseconds{nanoseconds},
|
||||
time_manager_event_type,
|
||||
reinterpret_cast<uintptr_t>(thread));
|
||||
}
|
||||
}
|
||||
|
||||
void TimeManager::UnscheduleTimeEvent(KThread* thread) {
|
||||
std::scoped_lock lock{mutex};
|
||||
system.CoreTiming().UnscheduleEvent(time_manager_event_type,
|
||||
reinterpret_cast<uintptr_t>(thread));
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
@@ -1,41 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
} // namespace Core
|
||||
|
||||
namespace Core::Timing {
|
||||
struct EventType;
|
||||
} // namespace Core::Timing
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KThread;
|
||||
|
||||
/**
|
||||
* The `TimeManager` takes care of scheduling time events on threads and executes their TimeUp
|
||||
* method when the event is triggered.
|
||||
*/
|
||||
class TimeManager {
|
||||
public:
|
||||
explicit TimeManager(Core::System& system);
|
||||
|
||||
/// Schedule a time event on `timetask` thread that will expire in 'nanoseconds'
|
||||
void ScheduleTimeEvent(KThread* time_task, s64 nanoseconds);
|
||||
|
||||
/// Unschedule an existing time event
|
||||
void UnscheduleTimeEvent(KThread* thread);
|
||||
|
||||
private:
|
||||
Core::System& system;
|
||||
std::shared_ptr<Core::Timing::EventType> time_manager_event_type;
|
||||
std::mutex mutex;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
@@ -97,7 +97,7 @@ void IUser::IsNfcEnabled(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
void IUser::ListDevices(Kernel::HLERequestContext& ctx) {
|
||||
LOG_INFO(Service_NFC, "called");
|
||||
LOG_DEBUG(Service_NFC, "called");
|
||||
|
||||
if (state == State::NonInitialized) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
|
||||
@@ -83,7 +83,7 @@ void IUser::Finalize(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
void IUser::ListDevices(Kernel::HLERequestContext& ctx) {
|
||||
LOG_INFO(Service_NFP, "called");
|
||||
LOG_DEBUG(Service_NFP, "called");
|
||||
|
||||
if (state == State::NonInitialized) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
|
||||
@@ -17,7 +17,7 @@ Camera::Camera(std::string input_engine_) : InputEngine(std::move(input_engine_)
|
||||
PreSetController(identifier);
|
||||
}
|
||||
|
||||
void Camera::SetCameraData(std::size_t width, std::size_t height, std::vector<u32> data) {
|
||||
void Camera::SetCameraData(std::size_t width, std::size_t height, std::span<const u32> data) {
|
||||
const std::size_t desired_width = getImageWidth();
|
||||
const std::size_t desired_height = getImageHeight();
|
||||
status.data.resize(desired_width * desired_height);
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <span>
|
||||
|
||||
#include "input_common/input_engine.h"
|
||||
|
||||
namespace InputCommon {
|
||||
@@ -15,7 +17,7 @@ class Camera final : public InputEngine {
|
||||
public:
|
||||
explicit Camera(std::string input_engine_);
|
||||
|
||||
void SetCameraData(std::size_t width, std::size_t height, std::vector<u32> data);
|
||||
void SetCameraData(std::size_t width, std::size_t height, std::span<const u32> data);
|
||||
|
||||
std::size_t getImageWidth() const;
|
||||
std::size_t getImageHeight() const;
|
||||
@@ -23,6 +25,7 @@ public:
|
||||
Common::Input::CameraError SetCameraFormat(const PadIdentifier& identifier_,
|
||||
Common::Input::CameraFormat camera_format) override;
|
||||
|
||||
private:
|
||||
Common::Input::CameraStatus status{};
|
||||
};
|
||||
|
||||
|
||||
@@ -200,12 +200,6 @@ bool MappingFactory::IsDriverValid(const MappingData& data) const {
|
||||
return false;
|
||||
}
|
||||
// The following drivers don't need to be mapped
|
||||
if (data.engine == "tas") {
|
||||
return false;
|
||||
}
|
||||
if (data.engine == "touch") {
|
||||
return false;
|
||||
}
|
||||
if (data.engine == "touch_from_button") {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -26,79 +26,33 @@
|
||||
namespace InputCommon {
|
||||
|
||||
struct InputSubsystem::Impl {
|
||||
void Initialize() {
|
||||
mapping_factory = std::make_shared<MappingFactory>();
|
||||
template <typename Engine>
|
||||
void RegisterEngine(std::string name, std::shared_ptr<Engine>& engine) {
|
||||
MappingCallback mapping_callback{[this](const MappingData& data) { RegisterInput(data); }};
|
||||
|
||||
keyboard = std::make_shared<Keyboard>("keyboard");
|
||||
keyboard->SetMappingCallback(mapping_callback);
|
||||
keyboard_factory = std::make_shared<InputFactory>(keyboard);
|
||||
keyboard_output_factory = std::make_shared<OutputFactory>(keyboard);
|
||||
Common::Input::RegisterInputFactory(keyboard->GetEngineName(), keyboard_factory);
|
||||
Common::Input::RegisterOutputFactory(keyboard->GetEngineName(), keyboard_output_factory);
|
||||
engine = std::make_shared<Engine>(name);
|
||||
engine->SetMappingCallback(mapping_callback);
|
||||
|
||||
mouse = std::make_shared<Mouse>("mouse");
|
||||
mouse->SetMappingCallback(mapping_callback);
|
||||
mouse_factory = std::make_shared<InputFactory>(mouse);
|
||||
mouse_output_factory = std::make_shared<OutputFactory>(mouse);
|
||||
Common::Input::RegisterInputFactory(mouse->GetEngineName(), mouse_factory);
|
||||
Common::Input::RegisterOutputFactory(mouse->GetEngineName(), mouse_output_factory);
|
||||
std::shared_ptr<InputFactory> input_factory = std::make_shared<InputFactory>(engine);
|
||||
std::shared_ptr<OutputFactory> output_factory = std::make_shared<OutputFactory>(engine);
|
||||
Common::Input::RegisterInputFactory(engine->GetEngineName(), std::move(input_factory));
|
||||
Common::Input::RegisterOutputFactory(engine->GetEngineName(), std::move(output_factory));
|
||||
}
|
||||
|
||||
touch_screen = std::make_shared<TouchScreen>("touch");
|
||||
touch_screen_factory = std::make_shared<InputFactory>(touch_screen);
|
||||
Common::Input::RegisterInputFactory(touch_screen->GetEngineName(), touch_screen_factory);
|
||||
|
||||
gcadapter = std::make_shared<GCAdapter>("gcpad");
|
||||
gcadapter->SetMappingCallback(mapping_callback);
|
||||
gcadapter_input_factory = std::make_shared<InputFactory>(gcadapter);
|
||||
gcadapter_output_factory = std::make_shared<OutputFactory>(gcadapter);
|
||||
Common::Input::RegisterInputFactory(gcadapter->GetEngineName(), gcadapter_input_factory);
|
||||
Common::Input::RegisterOutputFactory(gcadapter->GetEngineName(), gcadapter_output_factory);
|
||||
|
||||
udp_client = std::make_shared<CemuhookUDP::UDPClient>("cemuhookudp");
|
||||
udp_client->SetMappingCallback(mapping_callback);
|
||||
udp_client_input_factory = std::make_shared<InputFactory>(udp_client);
|
||||
udp_client_output_factory = std::make_shared<OutputFactory>(udp_client);
|
||||
Common::Input::RegisterInputFactory(udp_client->GetEngineName(), udp_client_input_factory);
|
||||
Common::Input::RegisterOutputFactory(udp_client->GetEngineName(),
|
||||
udp_client_output_factory);
|
||||
|
||||
tas_input = std::make_shared<TasInput::Tas>("tas");
|
||||
tas_input->SetMappingCallback(mapping_callback);
|
||||
tas_input_factory = std::make_shared<InputFactory>(tas_input);
|
||||
tas_output_factory = std::make_shared<OutputFactory>(tas_input);
|
||||
Common::Input::RegisterInputFactory(tas_input->GetEngineName(), tas_input_factory);
|
||||
Common::Input::RegisterOutputFactory(tas_input->GetEngineName(), tas_output_factory);
|
||||
|
||||
camera = std::make_shared<Camera>("camera");
|
||||
camera->SetMappingCallback(mapping_callback);
|
||||
camera_input_factory = std::make_shared<InputFactory>(camera);
|
||||
camera_output_factory = std::make_shared<OutputFactory>(camera);
|
||||
Common::Input::RegisterInputFactory(camera->GetEngineName(), camera_input_factory);
|
||||
Common::Input::RegisterOutputFactory(camera->GetEngineName(), camera_output_factory);
|
||||
|
||||
virtual_amiibo = std::make_shared<VirtualAmiibo>("virtual_amiibo");
|
||||
virtual_amiibo->SetMappingCallback(mapping_callback);
|
||||
virtual_amiibo_input_factory = std::make_shared<InputFactory>(virtual_amiibo);
|
||||
virtual_amiibo_output_factory = std::make_shared<OutputFactory>(virtual_amiibo);
|
||||
Common::Input::RegisterInputFactory(virtual_amiibo->GetEngineName(),
|
||||
virtual_amiibo_input_factory);
|
||||
Common::Input::RegisterOutputFactory(virtual_amiibo->GetEngineName(),
|
||||
virtual_amiibo_output_factory);
|
||||
|
||||
virtual_gamepad = std::make_shared<VirtualGamepad>("virtual_gamepad");
|
||||
virtual_gamepad->SetMappingCallback(mapping_callback);
|
||||
virtual_gamepad_input_factory = std::make_shared<InputFactory>(virtual_gamepad);
|
||||
Common::Input::RegisterInputFactory(virtual_gamepad->GetEngineName(),
|
||||
virtual_gamepad_input_factory);
|
||||
void Initialize() {
|
||||
mapping_factory = std::make_shared<MappingFactory>();
|
||||
|
||||
RegisterEngine("keyboard", keyboard);
|
||||
RegisterEngine("mouse", mouse);
|
||||
RegisterEngine("touch", touch_screen);
|
||||
RegisterEngine("gcpad", gcadapter);
|
||||
RegisterEngine("cemuhookudp", udp_client);
|
||||
RegisterEngine("tas", tas_input);
|
||||
RegisterEngine("camera", camera);
|
||||
RegisterEngine("virtual_amiibo", virtual_amiibo);
|
||||
RegisterEngine("virtual_gamepad", virtual_gamepad);
|
||||
#ifdef HAVE_SDL2
|
||||
sdl = std::make_shared<SDLDriver>("sdl");
|
||||
sdl->SetMappingCallback(mapping_callback);
|
||||
sdl_input_factory = std::make_shared<InputFactory>(sdl);
|
||||
sdl_output_factory = std::make_shared<OutputFactory>(sdl);
|
||||
Common::Input::RegisterInputFactory(sdl->GetEngineName(), sdl_input_factory);
|
||||
Common::Input::RegisterOutputFactory(sdl->GetEngineName(), sdl_output_factory);
|
||||
RegisterEngine("sdl", sdl);
|
||||
#endif
|
||||
|
||||
Common::Input::RegisterInputFactory("touch_from_button",
|
||||
@@ -107,45 +61,25 @@ struct InputSubsystem::Impl {
|
||||
std::make_shared<StickFromButton>());
|
||||
}
|
||||
|
||||
template <typename Engine>
|
||||
void UnregisterEngine(std::shared_ptr<Engine>& engine) {
|
||||
Common::Input::UnregisterInputFactory(engine->GetEngineName());
|
||||
Common::Input::UnregisterOutputFactory(engine->GetEngineName());
|
||||
engine.reset();
|
||||
}
|
||||
|
||||
void Shutdown() {
|
||||
Common::Input::UnregisterInputFactory(keyboard->GetEngineName());
|
||||
Common::Input::UnregisterOutputFactory(keyboard->GetEngineName());
|
||||
keyboard.reset();
|
||||
|
||||
Common::Input::UnregisterInputFactory(mouse->GetEngineName());
|
||||
Common::Input::UnregisterOutputFactory(mouse->GetEngineName());
|
||||
mouse.reset();
|
||||
|
||||
Common::Input::UnregisterInputFactory(touch_screen->GetEngineName());
|
||||
touch_screen.reset();
|
||||
|
||||
Common::Input::UnregisterInputFactory(gcadapter->GetEngineName());
|
||||
Common::Input::UnregisterOutputFactory(gcadapter->GetEngineName());
|
||||
gcadapter.reset();
|
||||
|
||||
Common::Input::UnregisterInputFactory(udp_client->GetEngineName());
|
||||
Common::Input::UnregisterOutputFactory(udp_client->GetEngineName());
|
||||
udp_client.reset();
|
||||
|
||||
Common::Input::UnregisterInputFactory(tas_input->GetEngineName());
|
||||
Common::Input::UnregisterOutputFactory(tas_input->GetEngineName());
|
||||
tas_input.reset();
|
||||
|
||||
Common::Input::UnregisterInputFactory(camera->GetEngineName());
|
||||
Common::Input::UnregisterOutputFactory(camera->GetEngineName());
|
||||
camera.reset();
|
||||
|
||||
Common::Input::UnregisterInputFactory(virtual_amiibo->GetEngineName());
|
||||
Common::Input::UnregisterOutputFactory(virtual_amiibo->GetEngineName());
|
||||
virtual_amiibo.reset();
|
||||
|
||||
Common::Input::UnregisterInputFactory(virtual_gamepad->GetEngineName());
|
||||
virtual_gamepad.reset();
|
||||
|
||||
UnregisterEngine(keyboard);
|
||||
UnregisterEngine(mouse);
|
||||
UnregisterEngine(touch_screen);
|
||||
UnregisterEngine(gcadapter);
|
||||
UnregisterEngine(udp_client);
|
||||
UnregisterEngine(tas_input);
|
||||
UnregisterEngine(camera);
|
||||
UnregisterEngine(virtual_amiibo);
|
||||
UnregisterEngine(virtual_gamepad);
|
||||
#ifdef HAVE_SDL2
|
||||
Common::Input::UnregisterInputFactory(sdl->GetEngineName());
|
||||
Common::Input::UnregisterOutputFactory(sdl->GetEngineName());
|
||||
sdl.reset();
|
||||
UnregisterEngine(sdl);
|
||||
#endif
|
||||
|
||||
Common::Input::UnregisterInputFactory("touch_from_button");
|
||||
@@ -173,117 +107,86 @@ struct InputSubsystem::Impl {
|
||||
return devices;
|
||||
}
|
||||
|
||||
[[nodiscard]] AnalogMapping GetAnalogMappingForDevice(
|
||||
[[nodiscard]] std::shared_ptr<InputEngine> GetInputEngine(
|
||||
const Common::ParamPackage& params) const {
|
||||
if (!params.Has("engine") || params.Get("engine", "") == "any") {
|
||||
return {};
|
||||
return nullptr;
|
||||
}
|
||||
const std::string engine = params.Get("engine", "");
|
||||
if (engine == keyboard->GetEngineName()) {
|
||||
return keyboard;
|
||||
}
|
||||
if (engine == mouse->GetEngineName()) {
|
||||
return mouse->GetAnalogMappingForDevice(params);
|
||||
return mouse;
|
||||
}
|
||||
if (engine == gcadapter->GetEngineName()) {
|
||||
return gcadapter->GetAnalogMappingForDevice(params);
|
||||
return gcadapter;
|
||||
}
|
||||
if (engine == udp_client->GetEngineName()) {
|
||||
return udp_client->GetAnalogMappingForDevice(params);
|
||||
}
|
||||
if (engine == tas_input->GetEngineName()) {
|
||||
return tas_input->GetAnalogMappingForDevice(params);
|
||||
return udp_client;
|
||||
}
|
||||
#ifdef HAVE_SDL2
|
||||
if (engine == sdl->GetEngineName()) {
|
||||
return sdl->GetAnalogMappingForDevice(params);
|
||||
return sdl;
|
||||
}
|
||||
#endif
|
||||
return {};
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] AnalogMapping GetAnalogMappingForDevice(
|
||||
const Common::ParamPackage& params) const {
|
||||
const auto input_engine = GetInputEngine(params);
|
||||
|
||||
if (input_engine == nullptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return input_engine->GetAnalogMappingForDevice(params);
|
||||
}
|
||||
|
||||
[[nodiscard]] ButtonMapping GetButtonMappingForDevice(
|
||||
const Common::ParamPackage& params) const {
|
||||
if (!params.Has("engine") || params.Get("engine", "") == "any") {
|
||||
const auto input_engine = GetInputEngine(params);
|
||||
|
||||
if (input_engine == nullptr) {
|
||||
return {};
|
||||
}
|
||||
const std::string engine = params.Get("engine", "");
|
||||
if (engine == gcadapter->GetEngineName()) {
|
||||
return gcadapter->GetButtonMappingForDevice(params);
|
||||
}
|
||||
if (engine == udp_client->GetEngineName()) {
|
||||
return udp_client->GetButtonMappingForDevice(params);
|
||||
}
|
||||
if (engine == tas_input->GetEngineName()) {
|
||||
return tas_input->GetButtonMappingForDevice(params);
|
||||
}
|
||||
#ifdef HAVE_SDL2
|
||||
if (engine == sdl->GetEngineName()) {
|
||||
return sdl->GetButtonMappingForDevice(params);
|
||||
}
|
||||
#endif
|
||||
return {};
|
||||
|
||||
return input_engine->GetButtonMappingForDevice(params);
|
||||
}
|
||||
|
||||
[[nodiscard]] MotionMapping GetMotionMappingForDevice(
|
||||
const Common::ParamPackage& params) const {
|
||||
if (!params.Has("engine") || params.Get("engine", "") == "any") {
|
||||
const auto input_engine = GetInputEngine(params);
|
||||
|
||||
if (input_engine == nullptr) {
|
||||
return {};
|
||||
}
|
||||
const std::string engine = params.Get("engine", "");
|
||||
if (engine == udp_client->GetEngineName()) {
|
||||
return udp_client->GetMotionMappingForDevice(params);
|
||||
}
|
||||
#ifdef HAVE_SDL2
|
||||
if (engine == sdl->GetEngineName()) {
|
||||
return sdl->GetMotionMappingForDevice(params);
|
||||
}
|
||||
#endif
|
||||
return {};
|
||||
|
||||
return input_engine->GetMotionMappingForDevice(params);
|
||||
}
|
||||
|
||||
Common::Input::ButtonNames GetButtonName(const Common::ParamPackage& params) const {
|
||||
if (!params.Has("engine") || params.Get("engine", "") == "any") {
|
||||
return Common::Input::ButtonNames::Undefined;
|
||||
}
|
||||
const std::string engine = params.Get("engine", "");
|
||||
if (engine == mouse->GetEngineName()) {
|
||||
return mouse->GetUIName(params);
|
||||
const auto input_engine = GetInputEngine(params);
|
||||
|
||||
if (input_engine == nullptr) {
|
||||
return Common::Input::ButtonNames::Invalid;
|
||||
}
|
||||
if (engine == gcadapter->GetEngineName()) {
|
||||
return gcadapter->GetUIName(params);
|
||||
}
|
||||
if (engine == udp_client->GetEngineName()) {
|
||||
return udp_client->GetUIName(params);
|
||||
}
|
||||
if (engine == tas_input->GetEngineName()) {
|
||||
return tas_input->GetUIName(params);
|
||||
}
|
||||
#ifdef HAVE_SDL2
|
||||
if (engine == sdl->GetEngineName()) {
|
||||
return sdl->GetUIName(params);
|
||||
}
|
||||
#endif
|
||||
return Common::Input::ButtonNames::Invalid;
|
||||
|
||||
return input_engine->GetUIName(params);
|
||||
}
|
||||
|
||||
bool IsStickInverted(const Common::ParamPackage& params) {
|
||||
const std::string engine = params.Get("engine", "");
|
||||
if (engine == mouse->GetEngineName()) {
|
||||
return mouse->IsStickInverted(params);
|
||||
const auto input_engine = GetInputEngine(params);
|
||||
|
||||
if (input_engine == nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (engine == gcadapter->GetEngineName()) {
|
||||
return gcadapter->IsStickInverted(params);
|
||||
}
|
||||
if (engine == udp_client->GetEngineName()) {
|
||||
return udp_client->IsStickInverted(params);
|
||||
}
|
||||
if (engine == tas_input->GetEngineName()) {
|
||||
return tas_input->IsStickInverted(params);
|
||||
}
|
||||
#ifdef HAVE_SDL2
|
||||
if (engine == sdl->GetEngineName()) {
|
||||
return sdl->IsStickInverted(params);
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
|
||||
return input_engine->IsStickInverted(params);
|
||||
}
|
||||
|
||||
bool IsController(const Common::ParamPackage& params) {
|
||||
@@ -353,28 +256,8 @@ struct InputSubsystem::Impl {
|
||||
std::shared_ptr<VirtualAmiibo> virtual_amiibo;
|
||||
std::shared_ptr<VirtualGamepad> virtual_gamepad;
|
||||
|
||||
std::shared_ptr<InputFactory> keyboard_factory;
|
||||
std::shared_ptr<InputFactory> mouse_factory;
|
||||
std::shared_ptr<InputFactory> gcadapter_input_factory;
|
||||
std::shared_ptr<InputFactory> touch_screen_factory;
|
||||
std::shared_ptr<InputFactory> udp_client_input_factory;
|
||||
std::shared_ptr<InputFactory> tas_input_factory;
|
||||
std::shared_ptr<InputFactory> camera_input_factory;
|
||||
std::shared_ptr<InputFactory> virtual_amiibo_input_factory;
|
||||
std::shared_ptr<InputFactory> virtual_gamepad_input_factory;
|
||||
|
||||
std::shared_ptr<OutputFactory> keyboard_output_factory;
|
||||
std::shared_ptr<OutputFactory> mouse_output_factory;
|
||||
std::shared_ptr<OutputFactory> gcadapter_output_factory;
|
||||
std::shared_ptr<OutputFactory> udp_client_output_factory;
|
||||
std::shared_ptr<OutputFactory> tas_output_factory;
|
||||
std::shared_ptr<OutputFactory> camera_output_factory;
|
||||
std::shared_ptr<OutputFactory> virtual_amiibo_output_factory;
|
||||
|
||||
#ifdef HAVE_SDL2
|
||||
std::shared_ptr<SDLDriver> sdl;
|
||||
std::shared_ptr<InputFactory> sdl_input_factory;
|
||||
std::shared_ptr<OutputFactory> sdl_output_factory;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
@@ -461,7 +461,7 @@ void EmitSetSampleMask(EmitContext& ctx, Id value) {
|
||||
}
|
||||
|
||||
void EmitSetFragDepth(EmitContext& ctx, Id value) {
|
||||
if (!ctx.runtime_info.convert_depth_mode) {
|
||||
if (!ctx.runtime_info.convert_depth_mode || ctx.profile.support_native_ndc) {
|
||||
ctx.OpStore(ctx.frag_depth, value);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -116,7 +116,8 @@ void EmitPrologue(EmitContext& ctx) {
|
||||
}
|
||||
|
||||
void EmitEpilogue(EmitContext& ctx) {
|
||||
if (ctx.stage == Stage::VertexB && ctx.runtime_info.convert_depth_mode) {
|
||||
if (ctx.stage == Stage::VertexB && ctx.runtime_info.convert_depth_mode &&
|
||||
!ctx.profile.support_native_ndc) {
|
||||
ConvertDepthMode(ctx);
|
||||
}
|
||||
if (ctx.stage == Stage::Fragment) {
|
||||
@@ -125,7 +126,7 @@ void EmitEpilogue(EmitContext& ctx) {
|
||||
}
|
||||
|
||||
void EmitEmitVertex(EmitContext& ctx, const IR::Value& stream) {
|
||||
if (ctx.runtime_info.convert_depth_mode) {
|
||||
if (ctx.runtime_info.convert_depth_mode && !ctx.profile.support_native_ndc) {
|
||||
ConvertDepthMode(ctx);
|
||||
}
|
||||
if (stream.IsImmediate()) {
|
||||
|
||||
@@ -35,6 +35,7 @@ struct Profile {
|
||||
bool support_int64_atomics{};
|
||||
bool support_derivative_control{};
|
||||
bool support_geometry_shader_passthrough{};
|
||||
bool support_native_ndc{};
|
||||
bool support_gl_nv_gpu_shader_5{};
|
||||
bool support_gl_amd_gpu_shader_half_float{};
|
||||
bool support_gl_texture_shadow_lod{};
|
||||
|
||||
@@ -204,6 +204,7 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo
|
||||
.support_int64_atomics = false,
|
||||
.support_derivative_control = device.HasDerivativeControl(),
|
||||
.support_geometry_shader_passthrough = device.HasGeometryShaderPassthrough(),
|
||||
.support_native_ndc = true,
|
||||
.support_gl_nv_gpu_shader_5 = device.HasNvGpuShader5(),
|
||||
.support_gl_amd_gpu_shader_half_float = device.HasAmdShaderHalfFloat(),
|
||||
.support_gl_texture_shadow_lod = device.HasTextureShadowLod(),
|
||||
|
||||
@@ -640,23 +640,33 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
|
||||
};
|
||||
std::array<VkViewportSwizzleNV, Maxwell::NumViewports> swizzles;
|
||||
std::ranges::transform(key.state.viewport_swizzles, swizzles.begin(), UnpackViewportSwizzle);
|
||||
const VkPipelineViewportSwizzleStateCreateInfoNV swizzle_ci{
|
||||
VkPipelineViewportSwizzleStateCreateInfoNV swizzle_ci{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_SWIZZLE_STATE_CREATE_INFO_NV,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.viewportCount = Maxwell::NumViewports,
|
||||
.pViewportSwizzles = swizzles.data(),
|
||||
};
|
||||
const VkPipelineViewportStateCreateInfo viewport_ci{
|
||||
VkPipelineViewportDepthClipControlCreateInfoEXT ndc_info{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_DEPTH_CLIP_CONTROL_CREATE_INFO_EXT,
|
||||
.pNext = nullptr,
|
||||
.negativeOneToOne = key.state.ndc_minus_one_to_one.Value() != 0 ? VK_TRUE : VK_FALSE,
|
||||
};
|
||||
VkPipelineViewportStateCreateInfo viewport_ci{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
|
||||
.pNext = device.IsNvViewportSwizzleSupported() ? &swizzle_ci : nullptr,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.viewportCount = Maxwell::NumViewports,
|
||||
.pViewports = nullptr,
|
||||
.scissorCount = Maxwell::NumViewports,
|
||||
.pScissors = nullptr,
|
||||
};
|
||||
|
||||
if (device.IsNvViewportSwizzleSupported()) {
|
||||
swizzle_ci.pNext = std::exchange(viewport_ci.pNext, &swizzle_ci);
|
||||
}
|
||||
if (device.IsExtDepthClipControlSupported()) {
|
||||
ndc_info.pNext = std::exchange(viewport_ci.pNext, &ndc_info);
|
||||
}
|
||||
VkPipelineRasterizationStateCreateInfo rasterization_ci{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
|
||||
@@ -321,6 +321,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
|
||||
.support_int64_atomics = device.IsExtShaderAtomicInt64Supported(),
|
||||
.support_derivative_control = true,
|
||||
.support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(),
|
||||
.support_native_ndc = device.IsExtDepthClipControlSupported(),
|
||||
|
||||
.warp_size_potentially_larger_than_guest = device.IsWarpSizePotentiallyBiggerThanGuest(),
|
||||
|
||||
|
||||
@@ -658,8 +658,7 @@ void RasterizerVulkan::BeginTransformFeedback() {
|
||||
return;
|
||||
}
|
||||
UNIMPLEMENTED_IF(regs.IsShaderConfigEnabled(Maxwell::ShaderType::TessellationInit) ||
|
||||
regs.IsShaderConfigEnabled(Maxwell::ShaderType::Tessellation) ||
|
||||
regs.IsShaderConfigEnabled(Maxwell::ShaderType::Geometry));
|
||||
regs.IsShaderConfigEnabled(Maxwell::ShaderType::Tessellation));
|
||||
scheduler.Record(
|
||||
[](vk::CommandBuffer cmdbuf) { cmdbuf.BeginTransformFeedbackEXT(0, 0, nullptr, nullptr); });
|
||||
}
|
||||
|
||||
@@ -660,6 +660,16 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
LOG_INFO(Render_Vulkan, "Device doesn't support depth range unrestricted");
|
||||
}
|
||||
|
||||
VkPhysicalDeviceDepthClipControlFeaturesEXT depth_clip_control_features;
|
||||
if (ext_depth_clip_control) {
|
||||
depth_clip_control_features = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_CONTROL_FEATURES_EXT,
|
||||
.pNext = nullptr,
|
||||
.depthClipControl = VK_TRUE,
|
||||
};
|
||||
SetNext(next, depth_clip_control_features);
|
||||
}
|
||||
|
||||
VkDeviceDiagnosticsConfigCreateInfoNV diagnostics_nv;
|
||||
if (Settings::values.enable_nsight_aftermath && nv_device_diagnostics_config) {
|
||||
nsight_aftermath_tracker = std::make_unique<NsightAftermathTracker>();
|
||||
@@ -1084,6 +1094,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
|
||||
bool has_ext_vertex_input_dynamic_state{};
|
||||
bool has_ext_line_rasterization{};
|
||||
bool has_ext_primitive_topology_list_restart{};
|
||||
bool has_ext_depth_clip_control{};
|
||||
for (const std::string& extension : supported_extensions) {
|
||||
const auto test = [&](std::optional<std::reference_wrapper<bool>> status, const char* name,
|
||||
bool push) {
|
||||
@@ -1117,6 +1128,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
|
||||
test(ext_shader_stencil_export, VK_EXT_SHADER_STENCIL_EXPORT_EXTENSION_NAME, true);
|
||||
test(ext_conservative_rasterization, VK_EXT_CONSERVATIVE_RASTERIZATION_EXTENSION_NAME,
|
||||
true);
|
||||
test(has_ext_depth_clip_control, VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME, false);
|
||||
test(has_ext_transform_feedback, VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME, false);
|
||||
test(has_ext_custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME, false);
|
||||
test(has_ext_extended_dynamic_state, VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME, false);
|
||||
@@ -1280,6 +1292,19 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
|
||||
ext_line_rasterization = true;
|
||||
}
|
||||
}
|
||||
if (has_ext_depth_clip_control) {
|
||||
VkPhysicalDeviceDepthClipControlFeaturesEXT depth_clip_control_features;
|
||||
depth_clip_control_features.sType =
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_CONTROL_FEATURES_EXT;
|
||||
depth_clip_control_features.pNext = nullptr;
|
||||
features.pNext = &depth_clip_control_features;
|
||||
physical.GetFeatures2(features);
|
||||
|
||||
if (depth_clip_control_features.depthClipControl) {
|
||||
extensions.push_back(VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME);
|
||||
ext_depth_clip_control = true;
|
||||
}
|
||||
}
|
||||
if (has_khr_workgroup_memory_explicit_layout) {
|
||||
VkPhysicalDeviceWorkgroupMemoryExplicitLayoutFeaturesKHR layout;
|
||||
layout.sType =
|
||||
|
||||
@@ -256,6 +256,11 @@ public:
|
||||
return ext_depth_range_unrestricted;
|
||||
}
|
||||
|
||||
/// Returns true if the device supports VK_EXT_depth_clip_control.
|
||||
bool IsExtDepthClipControlSupported() const {
|
||||
return ext_depth_clip_control;
|
||||
}
|
||||
|
||||
/// Returns true if the device supports VK_EXT_shader_viewport_index_layer.
|
||||
bool IsExtShaderViewportIndexLayerSupported() const {
|
||||
return ext_shader_viewport_index_layer;
|
||||
@@ -454,6 +459,7 @@ private:
|
||||
bool khr_swapchain_mutable_format{}; ///< Support for VK_KHR_swapchain_mutable_format.
|
||||
bool ext_index_type_uint8{}; ///< Support for VK_EXT_index_type_uint8.
|
||||
bool ext_sampler_filter_minmax{}; ///< Support for VK_EXT_sampler_filter_minmax.
|
||||
bool ext_depth_clip_control{}; ///< Support for VK_EXT_depth_clip_control
|
||||
bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted.
|
||||
bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer.
|
||||
bool ext_tooling_info{}; ///< Support for VK_EXT_tooling_info.
|
||||
|
||||
@@ -314,6 +314,18 @@ const char* ToString(VkResult result) noexcept {
|
||||
return "VK_ERROR_VALIDATION_FAILED_EXT";
|
||||
case VkResult::VK_ERROR_INVALID_SHADER_NV:
|
||||
return "VK_ERROR_INVALID_SHADER_NV";
|
||||
case VkResult::VK_ERROR_IMAGE_USAGE_NOT_SUPPORTED_KHR:
|
||||
return "VK_ERROR_IMAGE_USAGE_NOT_SUPPORTED_KHR";
|
||||
case VkResult::VK_ERROR_VIDEO_PICTURE_LAYOUT_NOT_SUPPORTED_KHR:
|
||||
return "VK_ERROR_VIDEO_PICTURE_LAYOUT_NOT_SUPPORTED_KHR";
|
||||
case VkResult::VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR:
|
||||
return "VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR";
|
||||
case VkResult::VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR:
|
||||
return "VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR";
|
||||
case VkResult::VK_ERROR_VIDEO_PROFILE_CODEC_NOT_SUPPORTED_KHR:
|
||||
return "VK_ERROR_VIDEO_PROFILE_CODEC_NOT_SUPPORTED_KHR";
|
||||
case VkResult::VK_ERROR_VIDEO_STD_VERSION_NOT_SUPPORTED_KHR:
|
||||
return "VK_ERROR_VIDEO_STD_VERSION_NOT_SUPPORTED_KHR";
|
||||
case VkResult::VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT:
|
||||
return "VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT";
|
||||
case VkResult::VK_ERROR_FRAGMENTATION_EXT:
|
||||
|
||||
@@ -44,30 +44,30 @@
|
||||
#include "yuzu/bootmanager.h"
|
||||
#include "yuzu/main.h"
|
||||
|
||||
EmuThread::EmuThread(Core::System& system_) : system{system_} {}
|
||||
static Core::Frontend::WindowSystemType GetWindowSystemType();
|
||||
|
||||
EmuThread::EmuThread(Core::System& system) : m_system{system} {}
|
||||
|
||||
EmuThread::~EmuThread() = default;
|
||||
|
||||
void EmuThread::run() {
|
||||
std::string name = "EmuControlThread";
|
||||
MicroProfileOnThreadCreate(name.c_str());
|
||||
Common::SetCurrentThreadName(name.c_str());
|
||||
const char* name = "EmuControlThread";
|
||||
MicroProfileOnThreadCreate(name);
|
||||
Common::SetCurrentThreadName(name);
|
||||
|
||||
auto& gpu = system.GPU();
|
||||
auto stop_token = stop_source.get_token();
|
||||
bool debugger_should_start = system.DebuggerEnabled();
|
||||
auto& gpu = m_system.GPU();
|
||||
auto stop_token = m_stop_source.get_token();
|
||||
|
||||
system.RegisterHostThread();
|
||||
m_system.RegisterHostThread();
|
||||
|
||||
// Main process has been loaded. Make the context current to this thread and begin GPU and CPU
|
||||
// execution.
|
||||
gpu.ObtainContext();
|
||||
|
||||
emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
|
||||
|
||||
if (Settings::values.use_disk_shader_cache.GetValue()) {
|
||||
system.Renderer().ReadRasterizer()->LoadDiskResources(
|
||||
system.GetCurrentProcessProgramID(), stop_token,
|
||||
m_system.Renderer().ReadRasterizer()->LoadDiskResources(
|
||||
m_system.GetCurrentProcessProgramID(), stop_token,
|
||||
[this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) {
|
||||
emit LoadProgress(stage, value, total);
|
||||
});
|
||||
@@ -77,57 +77,34 @@ void EmuThread::run() {
|
||||
gpu.ReleaseContext();
|
||||
gpu.Start();
|
||||
|
||||
system.GetCpuManager().OnGpuReady();
|
||||
m_system.GetCpuManager().OnGpuReady();
|
||||
|
||||
system.RegisterExitCallback([this]() {
|
||||
stop_source.request_stop();
|
||||
SetRunning(false);
|
||||
});
|
||||
if (m_system.DebuggerEnabled()) {
|
||||
m_system.InitializeDebugger();
|
||||
}
|
||||
|
||||
// Holds whether the cpu was running during the last iteration,
|
||||
// so that the DebugModeLeft signal can be emitted before the
|
||||
// next execution step
|
||||
bool was_active = false;
|
||||
while (!stop_token.stop_requested()) {
|
||||
if (running) {
|
||||
if (was_active) {
|
||||
emit DebugModeLeft();
|
||||
}
|
||||
std::unique_lock lk{m_should_run_mutex};
|
||||
if (m_should_run) {
|
||||
m_system.Run();
|
||||
m_is_running.store(true);
|
||||
m_is_running.notify_all();
|
||||
|
||||
running_guard = true;
|
||||
Core::SystemResultStatus result = system.Run();
|
||||
if (result != Core::SystemResultStatus::Success) {
|
||||
running_guard = false;
|
||||
this->SetRunning(false);
|
||||
emit ErrorThrown(result, system.GetStatusDetails());
|
||||
}
|
||||
|
||||
if (debugger_should_start) {
|
||||
system.InitializeDebugger();
|
||||
debugger_should_start = false;
|
||||
}
|
||||
|
||||
running_wait.Wait();
|
||||
result = system.Pause();
|
||||
if (result != Core::SystemResultStatus::Success) {
|
||||
running_guard = false;
|
||||
this->SetRunning(false);
|
||||
emit ErrorThrown(result, system.GetStatusDetails());
|
||||
}
|
||||
running_guard = false;
|
||||
|
||||
if (!stop_token.stop_requested()) {
|
||||
was_active = true;
|
||||
emit DebugModeEntered();
|
||||
}
|
||||
Common::CondvarWait(m_should_run_cv, lk, stop_token, [&] { return !m_should_run; });
|
||||
} else {
|
||||
std::unique_lock lock{running_mutex};
|
||||
Common::CondvarWait(running_cv, lock, stop_token, [&] { return IsRunning(); });
|
||||
m_system.Pause();
|
||||
m_is_running.store(false);
|
||||
m_is_running.notify_all();
|
||||
|
||||
emit DebugModeEntered();
|
||||
Common::CondvarWait(m_should_run_cv, lk, stop_token, [&] { return m_should_run; });
|
||||
emit DebugModeLeft();
|
||||
}
|
||||
}
|
||||
|
||||
// Shutdown the main emulated process
|
||||
system.ShutdownMainProcess();
|
||||
m_system.DetachDebugger();
|
||||
m_system.ShutdownMainProcess();
|
||||
|
||||
#if MICROPROFILE_ENABLED
|
||||
MicroProfileOnThreadExit();
|
||||
@@ -228,8 +205,10 @@ class RenderWidget : public QWidget {
|
||||
public:
|
||||
explicit RenderWidget(GRenderWindow* parent) : QWidget(parent), render_window(parent) {
|
||||
setAttribute(Qt::WA_NativeWindow);
|
||||
setAttribute(Qt::WA_DontCreateNativeAncestors);
|
||||
setAttribute(Qt::WA_PaintOnScreen);
|
||||
if (GetWindowSystemType() == Core::Frontend::WindowSystemType::Wayland) {
|
||||
setAttribute(Qt::WA_DontCreateNativeAncestors);
|
||||
}
|
||||
}
|
||||
|
||||
virtual ~RenderWidget() = default;
|
||||
@@ -274,12 +253,14 @@ static Core::Frontend::WindowSystemType GetWindowSystemType() {
|
||||
return Core::Frontend::WindowSystemType::X11;
|
||||
else if (platform_name == QStringLiteral("wayland"))
|
||||
return Core::Frontend::WindowSystemType::Wayland;
|
||||
else if (platform_name == QStringLiteral("wayland-egl"))
|
||||
return Core::Frontend::WindowSystemType::Wayland;
|
||||
else if (platform_name == QStringLiteral("cocoa"))
|
||||
return Core::Frontend::WindowSystemType::Cocoa;
|
||||
else if (platform_name == QStringLiteral("android"))
|
||||
return Core::Frontend::WindowSystemType::Android;
|
||||
|
||||
LOG_CRITICAL(Frontend, "Unknown Qt platform!");
|
||||
LOG_CRITICAL(Frontend, "Unknown Qt platform {}!", platform_name.toStdString());
|
||||
return Core::Frontend::WindowSystemType::Windows;
|
||||
}
|
||||
|
||||
@@ -319,7 +300,8 @@ GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_,
|
||||
input_subsystem->Initialize();
|
||||
this->setMouseTracking(true);
|
||||
|
||||
strict_context_required = QGuiApplication::platformName() == QStringLiteral("wayland");
|
||||
strict_context_required = QGuiApplication::platformName() == QStringLiteral("wayland") ||
|
||||
QGuiApplication::platformName() == QStringLiteral("wayland-egl");
|
||||
|
||||
connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete);
|
||||
connect(this, &GRenderWindow::ExecuteProgramSignal, parent, &GMainWindow::OnExecuteProgram,
|
||||
@@ -757,6 +739,9 @@ void GRenderWindow::InitializeCamera() {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto camera_width = input_subsystem->GetCamera()->getImageWidth();
|
||||
const auto camera_height = input_subsystem->GetCamera()->getImageHeight();
|
||||
camera_data.resize(camera_width * camera_height);
|
||||
camera_capture->setCaptureDestination(QCameraImageCapture::CaptureDestination::CaptureToBuffer);
|
||||
connect(camera_capture.get(), &QCameraImageCapture::imageCaptured, this,
|
||||
&GRenderWindow::OnCameraCapture);
|
||||
@@ -812,17 +797,22 @@ void GRenderWindow::RequestCameraCapture() {
|
||||
}
|
||||
|
||||
void GRenderWindow::OnCameraCapture(int requestId, const QImage& img) {
|
||||
constexpr std::size_t camera_width = 320;
|
||||
constexpr std::size_t camera_height = 240;
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA
|
||||
// TODO: Capture directly in the format and resolution needed
|
||||
const auto camera_width = input_subsystem->GetCamera()->getImageWidth();
|
||||
const auto camera_height = input_subsystem->GetCamera()->getImageHeight();
|
||||
const auto converted =
|
||||
img.scaled(camera_width, camera_height, Qt::AspectRatioMode::IgnoreAspectRatio,
|
||||
img.scaled(static_cast<int>(camera_width), static_cast<int>(camera_height),
|
||||
Qt::AspectRatioMode::IgnoreAspectRatio,
|
||||
Qt::TransformationMode::SmoothTransformation)
|
||||
.mirrored(false, true);
|
||||
std::vector<u32> camera_data{};
|
||||
camera_data.resize(camera_width * camera_height);
|
||||
if (camera_data.size() != camera_width * camera_height) {
|
||||
camera_data.resize(camera_width * camera_height);
|
||||
}
|
||||
std::memcpy(camera_data.data(), converted.bits(), camera_width * camera_height * sizeof(u32));
|
||||
input_subsystem->GetCamera()->SetCameraData(camera_width, camera_height, camera_data);
|
||||
pending_camera_snapshots = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool GRenderWindow::event(QEvent* event) {
|
||||
|
||||
@@ -47,7 +47,7 @@ class EmuThread final : public QThread {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit EmuThread(Core::System& system_);
|
||||
explicit EmuThread(Core::System& system);
|
||||
~EmuThread() override;
|
||||
|
||||
/**
|
||||
@@ -57,30 +57,30 @@ public:
|
||||
void run() override;
|
||||
|
||||
/**
|
||||
* Sets whether the emulation thread is running or not
|
||||
* @param running_ Boolean value, set the emulation thread to running if true
|
||||
* @note This function is thread-safe
|
||||
* Sets whether the emulation thread should run or not
|
||||
* @param should_run Boolean value, set the emulation thread to running if true
|
||||
*/
|
||||
void SetRunning(bool running_) {
|
||||
std::unique_lock lock{running_mutex};
|
||||
running = running_;
|
||||
lock.unlock();
|
||||
running_cv.notify_all();
|
||||
if (!running) {
|
||||
running_wait.Set();
|
||||
/// Wait until effectively paused
|
||||
while (running_guard)
|
||||
;
|
||||
void SetRunning(bool should_run) {
|
||||
// TODO: Prevent other threads from modifying the state until we finish.
|
||||
{
|
||||
// Notify the running thread to change state.
|
||||
std::unique_lock run_lk{m_should_run_mutex};
|
||||
m_should_run = should_run;
|
||||
m_should_run_cv.notify_one();
|
||||
}
|
||||
|
||||
// Wait until paused, if pausing.
|
||||
if (!should_run) {
|
||||
m_is_running.wait(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the emulation thread is running or not
|
||||
* @return True if the emulation thread is running, otherwise false
|
||||
* @note This function is thread-safe
|
||||
*/
|
||||
bool IsRunning() const {
|
||||
return running;
|
||||
return m_is_running.load() || m_should_run;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -88,18 +88,17 @@ public:
|
||||
*/
|
||||
void ForceStop() {
|
||||
LOG_WARNING(Frontend, "Force stopping EmuThread");
|
||||
stop_source.request_stop();
|
||||
SetRunning(false);
|
||||
m_stop_source.request_stop();
|
||||
}
|
||||
|
||||
private:
|
||||
bool running = false;
|
||||
std::stop_source stop_source;
|
||||
std::mutex running_mutex;
|
||||
std::condition_variable_any running_cv;
|
||||
Common::Event running_wait{};
|
||||
std::atomic_bool running_guard{false};
|
||||
Core::System& system;
|
||||
Core::System& m_system;
|
||||
|
||||
std::stop_source m_stop_source;
|
||||
std::mutex m_should_run_mutex;
|
||||
std::condition_variable_any m_should_run_cv;
|
||||
std::atomic<bool> m_is_running{false};
|
||||
bool m_should_run{true};
|
||||
|
||||
signals:
|
||||
/**
|
||||
@@ -120,8 +119,6 @@ signals:
|
||||
*/
|
||||
void DebugModeLeft();
|
||||
|
||||
void ErrorThrown(Core::SystemResultStatus, std::string);
|
||||
|
||||
void LoadProgress(VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total);
|
||||
};
|
||||
|
||||
@@ -242,13 +239,14 @@ private:
|
||||
bool first_frame = false;
|
||||
InputCommon::TasInput::TasState last_tas_state;
|
||||
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA
|
||||
bool is_virtual_camera;
|
||||
int pending_camera_snapshots;
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA
|
||||
std::vector<u32> camera_data;
|
||||
std::unique_ptr<QCamera> camera;
|
||||
std::unique_ptr<QCameraImageCapture> camera_capture;
|
||||
#endif
|
||||
std::unique_ptr<QTimer> camera_timer;
|
||||
#endif
|
||||
|
||||
Core::System& system;
|
||||
|
||||
|
||||
@@ -783,8 +783,6 @@ void Config::ReadSystemValues() {
|
||||
}
|
||||
}
|
||||
|
||||
ReadBasicSetting(Settings::values.device_name);
|
||||
|
||||
if (global) {
|
||||
ReadBasicSetting(Settings::values.current_user);
|
||||
Settings::values.current_user = std::clamp<int>(Settings::values.current_user.GetValue(), 0,
|
||||
@@ -797,6 +795,7 @@ void Config::ReadSystemValues() {
|
||||
} else {
|
||||
Settings::values.custom_rtc = std::nullopt;
|
||||
}
|
||||
ReadBasicSetting(Settings::values.device_name);
|
||||
}
|
||||
|
||||
ReadGlobalSetting(Settings::values.sound_index);
|
||||
@@ -1407,7 +1406,6 @@ void Config::SaveSystemValues() {
|
||||
Settings::values.rng_seed.UsingGlobal());
|
||||
WriteSetting(QStringLiteral("rng_seed"), Settings::values.rng_seed.GetValue(global).value_or(0),
|
||||
0, Settings::values.rng_seed.UsingGlobal());
|
||||
WriteBasicSetting(Settings::values.device_name);
|
||||
|
||||
if (global) {
|
||||
WriteBasicSetting(Settings::values.current_user);
|
||||
@@ -1416,6 +1414,7 @@ void Config::SaveSystemValues() {
|
||||
false);
|
||||
WriteSetting(QStringLiteral("custom_rtc"),
|
||||
QVariant::fromValue<long long>(Settings::values.custom_rtc.value_or(0)), 0);
|
||||
WriteBasicSetting(Settings::values.device_name);
|
||||
}
|
||||
|
||||
WriteGlobalSetting(Settings::values.sound_index);
|
||||
|
||||
@@ -1498,7 +1498,7 @@ void GMainWindow::SetupSigInterrupts() {
|
||||
|
||||
void GMainWindow::HandleSigInterrupt(int sig) {
|
||||
if (sig == SIGINT) {
|
||||
exit(1);
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
// Calling into Qt directly from a signal handler is not safe,
|
||||
@@ -1550,8 +1550,9 @@ void GMainWindow::AllowOSSleep() {
|
||||
|
||||
bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t program_index) {
|
||||
// Shutdown previous session if the emu thread is still active...
|
||||
if (emu_thread != nullptr)
|
||||
if (emu_thread != nullptr) {
|
||||
ShutdownGame();
|
||||
}
|
||||
|
||||
if (!render_window->InitRenderTarget()) {
|
||||
return false;
|
||||
@@ -1710,6 +1711,11 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
|
||||
system->RegisterExecuteProgramCallback(
|
||||
[this](std::size_t program_index_) { render_window->ExecuteProgram(program_index_); });
|
||||
|
||||
system->RegisterExitCallback([this] {
|
||||
emu_thread->ForceStop();
|
||||
render_window->Exit();
|
||||
});
|
||||
|
||||
connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame);
|
||||
connect(render_window, &GRenderWindow::MouseActivity, this, &GMainWindow::OnMouseActivity);
|
||||
// BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views
|
||||
@@ -1779,9 +1785,9 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
|
||||
OnStartGame();
|
||||
}
|
||||
|
||||
void GMainWindow::ShutdownGame() {
|
||||
bool GMainWindow::OnShutdownBegin() {
|
||||
if (!emulation_running) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ui->action_Fullscreen->isChecked()) {
|
||||
@@ -1793,21 +1799,55 @@ void GMainWindow::ShutdownGame() {
|
||||
// Disable unlimited frame rate
|
||||
Settings::values.use_speed_limit.SetValue(true);
|
||||
|
||||
if (system->IsShuttingDown()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
system->SetShuttingDown(true);
|
||||
system->DetachDebugger();
|
||||
discord_rpc->Pause();
|
||||
|
||||
RequestGameExit();
|
||||
emu_thread->disconnect();
|
||||
emu_thread->SetRunning(true);
|
||||
|
||||
emit EmulationStopping();
|
||||
|
||||
// Wait for emulation thread to complete and delete it
|
||||
if (!emu_thread->wait(5000)) {
|
||||
shutdown_timer.setSingleShot(true);
|
||||
shutdown_timer.start(system->DebuggerEnabled() ? 0 : 5000);
|
||||
connect(&shutdown_timer, &QTimer::timeout, this, &GMainWindow::OnEmulationStopTimeExpired);
|
||||
connect(emu_thread.get(), &QThread::finished, this, &GMainWindow::OnEmulationStopped);
|
||||
|
||||
// Disable everything to prevent anything from being triggered here
|
||||
ui->action_Pause->setEnabled(false);
|
||||
ui->action_Restart->setEnabled(false);
|
||||
ui->action_Stop->setEnabled(false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GMainWindow::OnShutdownBeginDialog() {
|
||||
shutdown_dialog = new OverlayDialog(this, *system, QString{}, tr("Closing software..."),
|
||||
QString{}, QString{}, Qt::AlignHCenter | Qt::AlignVCenter);
|
||||
shutdown_dialog->open();
|
||||
}
|
||||
|
||||
void GMainWindow::OnEmulationStopTimeExpired() {
|
||||
if (emu_thread) {
|
||||
emu_thread->ForceStop();
|
||||
emu_thread->wait();
|
||||
}
|
||||
}
|
||||
|
||||
void GMainWindow::OnEmulationStopped() {
|
||||
shutdown_timer.stop();
|
||||
emu_thread->disconnect();
|
||||
emu_thread->wait();
|
||||
emu_thread = nullptr;
|
||||
|
||||
if (shutdown_dialog) {
|
||||
shutdown_dialog->deleteLater();
|
||||
shutdown_dialog = nullptr;
|
||||
}
|
||||
|
||||
emulation_running = false;
|
||||
|
||||
discord_rpc->Update();
|
||||
@@ -1853,6 +1893,20 @@ void GMainWindow::ShutdownGame() {
|
||||
|
||||
// When closing the game, destroy the GLWindow to clear the context after the game is closed
|
||||
render_window->ReleaseRenderTarget();
|
||||
|
||||
Settings::RestoreGlobalState(system->IsPoweredOn());
|
||||
system->HIDCore().ReloadInputDevices();
|
||||
UpdateStatusButtons();
|
||||
}
|
||||
|
||||
void GMainWindow::ShutdownGame() {
|
||||
if (!emulation_running) {
|
||||
return;
|
||||
}
|
||||
|
||||
OnShutdownBegin();
|
||||
OnEmulationStopTimeExpired();
|
||||
OnEmulationStopped();
|
||||
}
|
||||
|
||||
void GMainWindow::StoreRecentFile(const QString& filename) {
|
||||
@@ -2661,6 +2715,9 @@ void GMainWindow::OnMenuInstallToNAND() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Save folder location of the first selected file
|
||||
UISettings::values.roms_path = QFileInfo(filenames[0]).path();
|
||||
|
||||
int remaining = filenames.size();
|
||||
|
||||
// This would only overflow above 2^43 bytes (8.796 TB)
|
||||
@@ -2916,8 +2973,6 @@ void GMainWindow::OnStartGame() {
|
||||
|
||||
emu_thread->SetRunning(true);
|
||||
|
||||
connect(emu_thread.get(), &EmuThread::ErrorThrown, this, &GMainWindow::OnCoreError);
|
||||
|
||||
UpdateMenuState();
|
||||
OnTasStateChanged();
|
||||
|
||||
@@ -2954,11 +3009,9 @@ void GMainWindow::OnStopGame() {
|
||||
return;
|
||||
}
|
||||
|
||||
ShutdownGame();
|
||||
|
||||
Settings::RestoreGlobalState(system->IsPoweredOn());
|
||||
system->HIDCore().ReloadInputDevices();
|
||||
UpdateStatusButtons();
|
||||
if (OnShutdownBegin()) {
|
||||
OnShutdownBeginDialog();
|
||||
}
|
||||
}
|
||||
|
||||
void GMainWindow::OnLoadComplete() {
|
||||
@@ -3067,7 +3120,8 @@ static QScreen* GuessCurrentScreen(QWidget* window) {
|
||||
|
||||
bool GMainWindow::UsingExclusiveFullscreen() {
|
||||
return Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive ||
|
||||
QGuiApplication::platformName() == QStringLiteral("wayland");
|
||||
QGuiApplication::platformName() == QStringLiteral("wayland") ||
|
||||
QGuiApplication::platformName() == QStringLiteral("wayland-egl");
|
||||
}
|
||||
|
||||
void GMainWindow::ShowFullscreen() {
|
||||
@@ -3900,79 +3954,6 @@ void GMainWindow::OnMouseActivity() {
|
||||
mouse_center_timer.stop();
|
||||
}
|
||||
|
||||
void GMainWindow::OnCoreError(Core::SystemResultStatus result, std::string details) {
|
||||
QMessageBox::StandardButton answer;
|
||||
QString status_message;
|
||||
const QString common_message =
|
||||
tr("The game you are trying to load requires additional files from your Switch to be "
|
||||
"dumped "
|
||||
"before playing.<br/><br/>For more information on dumping these files, please see the "
|
||||
"following wiki page: <a "
|
||||
"href='https://yuzu-emu.org/wiki/"
|
||||
"dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System "
|
||||
"Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to "
|
||||
"quit "
|
||||
"back to the game list? Continuing emulation may result in crashes, corrupted save "
|
||||
"data, or other bugs.");
|
||||
switch (result) {
|
||||
case Core::SystemResultStatus::ErrorSystemFiles: {
|
||||
QString message;
|
||||
if (details.empty()) {
|
||||
message =
|
||||
tr("yuzu was unable to locate a Switch system archive. %1").arg(common_message);
|
||||
} else {
|
||||
message = tr("yuzu was unable to locate a Switch system archive: %1. %2")
|
||||
.arg(QString::fromStdString(details), common_message);
|
||||
}
|
||||
|
||||
answer = QMessageBox::question(this, tr("System Archive Not Found"), message,
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
|
||||
status_message = tr("System Archive Missing");
|
||||
break;
|
||||
}
|
||||
|
||||
case Core::SystemResultStatus::ErrorSharedFont: {
|
||||
const QString message =
|
||||
tr("yuzu was unable to locate the Switch shared fonts. %1").arg(common_message);
|
||||
answer = QMessageBox::question(this, tr("Shared Fonts Not Found"), message,
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
|
||||
status_message = tr("Shared Font Missing");
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
answer = QMessageBox::question(
|
||||
this, tr("Fatal Error"),
|
||||
tr("yuzu has encountered a fatal error, please see the log for more details. "
|
||||
"For more information on accessing the log, please see the following page: "
|
||||
"<a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How "
|
||||
"to "
|
||||
"Upload the Log File</a>.<br/><br/>Would you like to quit back to the game "
|
||||
"list? "
|
||||
"Continuing emulation may result in crashes, corrupted save data, or other "
|
||||
"bugs."),
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
|
||||
status_message = tr("Fatal Error encountered");
|
||||
break;
|
||||
}
|
||||
|
||||
if (answer == QMessageBox::Yes) {
|
||||
if (emu_thread) {
|
||||
ShutdownGame();
|
||||
|
||||
Settings::RestoreGlobalState(system->IsPoweredOn());
|
||||
system->HIDCore().ReloadInputDevices();
|
||||
UpdateStatusButtons();
|
||||
}
|
||||
} else {
|
||||
// Only show the message if the game is still running.
|
||||
if (emu_thread) {
|
||||
emu_thread->SetRunning(true);
|
||||
message_label->setText(status_message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
|
||||
if (behavior == ReinitializeKeyBehavior::Warning) {
|
||||
const auto res = QMessageBox::information(
|
||||
@@ -4117,10 +4098,6 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
|
||||
// Shutdown session if the emu thread is active...
|
||||
if (emu_thread != nullptr) {
|
||||
ShutdownGame();
|
||||
|
||||
Settings::RestoreGlobalState(system->IsPoweredOn());
|
||||
system->HIDCore().ReloadInputDevices();
|
||||
UpdateStatusButtons();
|
||||
}
|
||||
|
||||
render_window->close();
|
||||
@@ -4213,6 +4190,10 @@ bool GMainWindow::ConfirmForceLockedExit() {
|
||||
}
|
||||
|
||||
void GMainWindow::RequestGameExit() {
|
||||
if (!system->IsPoweredOn()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& sm{system->ServiceManager()};
|
||||
auto applet_oe = sm.GetService<Service::AM::AppletOE>("appletOE");
|
||||
auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE");
|
||||
|
||||
@@ -29,6 +29,7 @@ class GImageInfo;
|
||||
class GRenderWindow;
|
||||
class LoadingScreen;
|
||||
class MicroProfileDialog;
|
||||
class OverlayDialog;
|
||||
class ProfilerWidget;
|
||||
class ControllerDialog;
|
||||
class QLabel;
|
||||
@@ -332,10 +333,13 @@ private slots:
|
||||
void ResetWindowSize900();
|
||||
void ResetWindowSize1080();
|
||||
void OnCaptureScreenshot();
|
||||
void OnCoreError(Core::SystemResultStatus, std::string);
|
||||
void OnReinitializeKeys(ReinitializeKeyBehavior behavior);
|
||||
void OnLanguageChanged(const QString& locale);
|
||||
void OnMouseActivity();
|
||||
bool OnShutdownBegin();
|
||||
void OnShutdownBeginDialog();
|
||||
void OnEmulationStopped();
|
||||
void OnEmulationStopTimeExpired();
|
||||
|
||||
private:
|
||||
QString GetGameListErrorRemoving(InstalledEntryType type) const;
|
||||
@@ -385,6 +389,8 @@ private:
|
||||
GRenderWindow* render_window;
|
||||
GameList* game_list;
|
||||
LoadingScreen* loading_screen;
|
||||
QTimer shutdown_timer;
|
||||
OverlayDialog* shutdown_dialog{};
|
||||
|
||||
GameListPlaceholder* game_list_placeholder;
|
||||
|
||||
|
||||
@@ -186,7 +186,7 @@ pid_t SpawnChild(const char* arg0) {
|
||||
return pid;
|
||||
} else if (pid == 0) {
|
||||
// child
|
||||
execl(arg0, arg0, nullptr);
|
||||
execlp(arg0, arg0, nullptr);
|
||||
const int err = errno;
|
||||
fmt::print(stderr, "execl failed with error {}\n", err);
|
||||
_exit(0);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <QKeyEvent>
|
||||
#include <QScreen>
|
||||
#include <QWindow>
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/hid/hid_types.h"
|
||||
@@ -42,7 +43,7 @@ OverlayDialog::OverlayDialog(QWidget* parent, Core::System& system, const QStrin
|
||||
MoveAndResizeWindow();
|
||||
|
||||
// TODO (Morph): Remove this when InputInterpreter no longer relies on the HID backend
|
||||
if (system.IsPoweredOn()) {
|
||||
if (system.IsPoweredOn() && !ui->buttonsDialog->isHidden()) {
|
||||
input_interpreter = std::make_unique<InputInterpreter>(system);
|
||||
|
||||
StartInputThread();
|
||||
@@ -83,6 +84,11 @@ void OverlayDialog::InitializeRegularTextDialog(const QString& title_text, const
|
||||
ui->button_ok_label->setEnabled(false);
|
||||
}
|
||||
|
||||
if (ui->button_cancel->isHidden() && ui->button_ok_label->isHidden()) {
|
||||
ui->buttonsDialog->hide();
|
||||
return;
|
||||
}
|
||||
|
||||
connect(
|
||||
ui->button_cancel, &QPushButton::clicked, this,
|
||||
[this](bool) {
|
||||
@@ -130,6 +136,11 @@ void OverlayDialog::InitializeRichTextDialog(const QString& title_text, const QS
|
||||
ui->button_ok_rich->setEnabled(false);
|
||||
}
|
||||
|
||||
if (ui->button_cancel_rich->isHidden() && ui->button_ok_rich->isHidden()) {
|
||||
ui->buttonsRichDialog->hide();
|
||||
return;
|
||||
}
|
||||
|
||||
connect(
|
||||
ui->button_cancel_rich, &QPushButton::clicked, this,
|
||||
[this](bool) {
|
||||
@@ -152,7 +163,7 @@ void OverlayDialog::MoveAndResizeWindow() {
|
||||
const auto height = static_cast<float>(parentWidget()->height());
|
||||
|
||||
// High DPI
|
||||
const float dpi_scale = qApp->screenAt(pos)->logicalDotsPerInch() / 96.0f;
|
||||
const float dpi_scale = parentWidget()->windowHandle()->screen()->logicalDotsPerInch() / 96.0f;
|
||||
|
||||
const auto title_text_font_size = BASE_TITLE_FONT_SIZE * (height / BASE_HEIGHT) / dpi_scale;
|
||||
const auto body_text_font_size =
|
||||
@@ -249,3 +260,9 @@ void OverlayDialog::InputThread() {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
}
|
||||
}
|
||||
|
||||
void OverlayDialog::keyPressEvent(QKeyEvent* e) {
|
||||
if (!ui->buttonsDialog->isHidden() || e->key() != Qt::Key_Escape) {
|
||||
QDialog::keyPressEvent(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,6 +94,7 @@ private:
|
||||
|
||||
/// The thread where input is being polled and processed.
|
||||
void InputThread();
|
||||
void keyPressEvent(QKeyEvent* e) override;
|
||||
|
||||
std::unique_ptr<Ui::OverlayDialog> ui;
|
||||
|
||||
|
||||
@@ -49,6 +49,15 @@ if(UNIX AND NOT APPLE)
|
||||
install(TARGETS yuzu-cmd)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
# compile as a win32 gui application instead of a console application
|
||||
if(MSVC)
|
||||
set_target_properties(yuzu-cmd PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup")
|
||||
elseif(MINGW)
|
||||
set_target_properties(yuzu-cmd PROPERTIES LINK_FLAGS_RELEASE "-Wl,--subsystem,windows")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (MSVC)
|
||||
include(CopyYuzuSDLDeps)
|
||||
copy_yuzu_SDL_deps(yuzu-cmd)
|
||||
|
||||
@@ -174,6 +174,13 @@ static void OnStatusMessageReceived(const Network::StatusMessageEntry& msg) {
|
||||
|
||||
/// Application entry point
|
||||
int main(int argc, char** argv) {
|
||||
#ifdef _WIN32
|
||||
if (AttachConsole(ATTACH_PARENT_PROCESS)) {
|
||||
freopen("CONOUT$", "wb", stdout);
|
||||
freopen("CONOUT$", "wb", stderr);
|
||||
}
|
||||
#endif
|
||||
|
||||
Common::Log::Initialize();
|
||||
Common::Log::SetColorConsoleBackendEnabled(true);
|
||||
Common::Log::Start();
|
||||
|
||||
Reference in New Issue
Block a user