Compare commits
22 Commits
android-22
...
android-22
Author | SHA1 | Date | |
---|---|---|---|
b52f26a094 | |||
e957bc2b4c | |||
363c96bdc3 | |||
514c3a75e1 | |||
bef9ee5000 | |||
78ce7f00f9 | |||
9b9634454a | |||
a5cdf0aed1 | |||
4cccbe7989 | |||
5da55cbac9 | |||
81cc4df1f9 | |||
25f3d358b1 | |||
a3c8bb251d | |||
327533be1f | |||
61ea2115c7 | |||
108a72ea8a | |||
fb3ef957bb | |||
78f72b3bf5 | |||
818721d12d | |||
442aad9b27 | |||
8e0f97ac96 | |||
345d691328 |
@ -2,9 +2,11 @@
|
|||||||
|----|----|----|----|----|
|
|----|----|----|----|----|
|
||||||
| [12461](https://github.com/yuzu-emu/yuzu-android//pull/12461) | [`4c08a0e6d`](https://github.com/yuzu-emu/yuzu-android//pull/12461/files) | Rework Nvdec and VIC to fix out-of-order videos, and speed up decoding. | [Kelebek1](https://github.com/Kelebek1/) | Yes |
|
| [12461](https://github.com/yuzu-emu/yuzu-android//pull/12461) | [`4c08a0e6d`](https://github.com/yuzu-emu/yuzu-android//pull/12461/files) | Rework Nvdec and VIC to fix out-of-order videos, and speed up decoding. | [Kelebek1](https://github.com/Kelebek1/) | Yes |
|
||||||
| [12749](https://github.com/yuzu-emu/yuzu-android//pull/12749) | [`aad4b0d6f`](https://github.com/yuzu-emu/yuzu-android//pull/12749/files) | general: workarounds for SMMU syncing issues | [liamwhite](https://github.com/liamwhite/) | Yes |
|
| [12749](https://github.com/yuzu-emu/yuzu-android//pull/12749) | [`aad4b0d6f`](https://github.com/yuzu-emu/yuzu-android//pull/12749/files) | general: workarounds for SMMU syncing issues | [liamwhite](https://github.com/liamwhite/) | Yes |
|
||||||
| [12756](https://github.com/yuzu-emu/yuzu-android//pull/12756) | [`f6e8080cb`](https://github.com/yuzu-emu/yuzu-android//pull/12756/files) | general: applet multiprocess | [liamwhite](https://github.com/liamwhite/) | Yes |
|
| [12756](https://github.com/yuzu-emu/yuzu-android//pull/12756) | [`cd3de0848`](https://github.com/yuzu-emu/yuzu-android//pull/12756/files) | general: applet multiprocess | [liamwhite](https://github.com/liamwhite/) | Yes |
|
||||||
| [12874](https://github.com/yuzu-emu/yuzu-android//pull/12874) | [`f410cf681`](https://github.com/yuzu-emu/yuzu-android//pull/12874/files) | Revert "shader_recompiler: fix Offset operand usage for non-OpImage*Gather" | [liamwhite](https://github.com/liamwhite/) | Yes |
|
| [12873](https://github.com/yuzu-emu/yuzu-android//pull/12873) | [`a8488952f`](https://github.com/yuzu-emu/yuzu-android//pull/12873/files) | GPU: Implement channel scheduling. | [FernandoS27](https://github.com/FernandoS27/) | Yes |
|
||||||
| [12877](https://github.com/yuzu-emu/yuzu-android//pull/12877) | [`818721d12`](https://github.com/yuzu-emu/yuzu-android//pull/12877/files) | service: hid: Multiple fixes | [german77](https://github.com/german77/) | Yes |
|
| [12903](https://github.com/yuzu-emu/yuzu-android//pull/12903) | [`5be8121af`](https://github.com/yuzu-emu/yuzu-android//pull/12903/files) | shader_recompiler: use only ConstOffset for OpImageFetch | [liamwhite](https://github.com/liamwhite/) | Yes |
|
||||||
|
| [12905](https://github.com/yuzu-emu/yuzu-android//pull/12905) | [`5eb5c9675`](https://github.com/yuzu-emu/yuzu-android//pull/12905/files) | nvnflinger: release buffers before presentation sleep | [liamwhite](https://github.com/liamwhite/) | Yes |
|
||||||
|
| [12915](https://github.com/yuzu-emu/yuzu-android//pull/12915) | [`504abbd6e`](https://github.com/yuzu-emu/yuzu-android//pull/12915/files) | dmnt: cheats: Update cheat vm to latest version | [german77](https://github.com/german77/) | Yes |
|
||||||
|
|
||||||
|
|
||||||
End of merge log. You can find the original README.md below the break.
|
End of merge log. You can find the original README.md below the break.
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
#include "common/assert.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
@ -29,6 +30,8 @@ namespace Common {
|
|||||||
|
|
||||||
template <std::size_t Size, bool le = false>
|
template <std::size_t Size, bool le = false>
|
||||||
[[nodiscard]] constexpr std::array<u8, Size> HexStringToArray(std::string_view str) {
|
[[nodiscard]] constexpr std::array<u8, Size> HexStringToArray(std::string_view str) {
|
||||||
|
ASSERT_MSG(Size * 2 <= str.size(), "Invalid string size");
|
||||||
|
|
||||||
std::array<u8, Size> out{};
|
std::array<u8, Size> out{};
|
||||||
if constexpr (le) {
|
if constexpr (le) {
|
||||||
for (std::size_t i = 2 * Size - 2; i <= 2 * Size; i -= 2) {
|
for (std::size_t i = 2 * Size - 2; i <= 2 * Size; i -= 2) {
|
||||||
|
@ -118,23 +118,17 @@ std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet(Core::System& system,
|
|||||||
switch (mode) {
|
switch (mode) {
|
||||||
case LibraryAppletMode::AllForeground:
|
case LibraryAppletMode::AllForeground:
|
||||||
case LibraryAppletMode::NoUi:
|
case LibraryAppletMode::NoUi:
|
||||||
applet->focus_state = FocusState::InFocus;
|
|
||||||
applet->hid_registration.EnableAppletToGetInput(true);
|
|
||||||
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground);
|
|
||||||
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
|
|
||||||
break;
|
|
||||||
case LibraryAppletMode::AllForegroundInitiallyHidden:
|
|
||||||
applet->system_buffer_manager.SetWindowVisibility(false);
|
|
||||||
applet->focus_state = FocusState::NotInFocus;
|
|
||||||
applet->hid_registration.EnableAppletToGetInput(false);
|
|
||||||
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
|
|
||||||
break;
|
|
||||||
case LibraryAppletMode::PartialForeground:
|
case LibraryAppletMode::PartialForeground:
|
||||||
case LibraryAppletMode::PartialForegroundIndirectDisplay:
|
case LibraryAppletMode::PartialForegroundIndirectDisplay:
|
||||||
default:
|
|
||||||
applet->focus_state = FocusState::Background;
|
|
||||||
applet->hid_registration.EnableAppletToGetInput(true);
|
applet->hid_registration.EnableAppletToGetInput(true);
|
||||||
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
|
applet->focus_state = FocusState::InFocus;
|
||||||
|
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground);
|
||||||
|
break;
|
||||||
|
case LibraryAppletMode::AllForegroundInitiallyHidden:
|
||||||
|
applet->hid_registration.EnableAppletToGetInput(false);
|
||||||
|
applet->focus_state = FocusState::NotInFocus;
|
||||||
|
applet->system_buffer_manager.SetWindowVisibility(false);
|
||||||
|
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoBackground);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include "common/logging/log.h"
|
||||||
|
#include "core/hle/result.h"
|
||||||
#include "core/hle/service/am/am_results.h"
|
#include "core/hle/service/am/am_results.h"
|
||||||
#include "core/hle/service/am/frontend/applets.h"
|
#include "core/hle/service/am/frontend/applets.h"
|
||||||
#include "core/hle/service/am/self_controller.h"
|
#include "core/hle/service/am/self_controller.h"
|
||||||
#include "core/hle/service/caps/caps_su.h"
|
#include "core/hle/service/caps/caps_su.h"
|
||||||
|
#include "core/hle/service/hle_ipc.h"
|
||||||
#include "core/hle/service/ipc_helpers.h"
|
#include "core/hle/service/ipc_helpers.h"
|
||||||
#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h"
|
#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h"
|
||||||
#include "core/hle/service/nvnflinger/nvnflinger.h"
|
#include "core/hle/service/nvnflinger/nvnflinger.h"
|
||||||
@ -47,7 +50,7 @@ ISelfController::ISelfController(Core::System& system_, std::shared_ptr<Applet>
|
|||||||
{50, &ISelfController::SetHandlesRequestToDisplay, "SetHandlesRequestToDisplay"},
|
{50, &ISelfController::SetHandlesRequestToDisplay, "SetHandlesRequestToDisplay"},
|
||||||
{51, &ISelfController::ApproveToDisplay, "ApproveToDisplay"},
|
{51, &ISelfController::ApproveToDisplay, "ApproveToDisplay"},
|
||||||
{60, nullptr, "OverrideAutoSleepTimeAndDimmingTime"},
|
{60, nullptr, "OverrideAutoSleepTimeAndDimmingTime"},
|
||||||
{61, nullptr, "SetMediaPlaybackState"},
|
{61, &ISelfController::SetMediaPlaybackState, "SetMediaPlaybackState"},
|
||||||
{62, &ISelfController::SetIdleTimeDetectionExtension, "SetIdleTimeDetectionExtension"},
|
{62, &ISelfController::SetIdleTimeDetectionExtension, "SetIdleTimeDetectionExtension"},
|
||||||
{63, &ISelfController::GetIdleTimeDetectionExtension, "GetIdleTimeDetectionExtension"},
|
{63, &ISelfController::GetIdleTimeDetectionExtension, "GetIdleTimeDetectionExtension"},
|
||||||
{64, nullptr, "SetInputDetectionSourceSet"},
|
{64, nullptr, "SetInputDetectionSourceSet"},
|
||||||
@ -324,6 +327,16 @@ void ISelfController::ApproveToDisplay(HLERequestContext& ctx) {
|
|||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ISelfController::SetMediaPlaybackState(HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp{ctx};
|
||||||
|
const u8 state = rp.Pop<u8>();
|
||||||
|
|
||||||
|
LOG_WARNING(Service_AM, "(STUBBED) called, state={}", state);
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
}
|
||||||
|
|
||||||
void ISelfController::SetIdleTimeDetectionExtension(HLERequestContext& ctx) {
|
void ISelfController::SetIdleTimeDetectionExtension(HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "core/hle/service/hle_ipc.h"
|
||||||
#include "core/hle/service/kernel_helpers.h"
|
#include "core/hle/service/kernel_helpers.h"
|
||||||
#include "core/hle/service/service.h"
|
#include "core/hle/service/service.h"
|
||||||
|
|
||||||
@ -38,6 +39,7 @@ private:
|
|||||||
void CreateManagedDisplaySeparableLayer(HLERequestContext& ctx);
|
void CreateManagedDisplaySeparableLayer(HLERequestContext& ctx);
|
||||||
void SetHandlesRequestToDisplay(HLERequestContext& ctx);
|
void SetHandlesRequestToDisplay(HLERequestContext& ctx);
|
||||||
void ApproveToDisplay(HLERequestContext& ctx);
|
void ApproveToDisplay(HLERequestContext& ctx);
|
||||||
|
void SetMediaPlaybackState(HLERequestContext& ctx);
|
||||||
void SetIdleTimeDetectionExtension(HLERequestContext& ctx);
|
void SetIdleTimeDetectionExtension(HLERequestContext& ctx);
|
||||||
void GetIdleTimeDetectionExtension(HLERequestContext& ctx);
|
void GetIdleTimeDetectionExtension(HLERequestContext& ctx);
|
||||||
void ReportUserIsActive(HLERequestContext& ctx);
|
void ReportUserIsActive(HLERequestContext& ctx);
|
||||||
|
@ -38,7 +38,8 @@ bool SystemBufferManager::Initialize(Nvnflinger::Nvnflinger* nvnflinger, Kernel:
|
|||||||
}
|
}
|
||||||
|
|
||||||
Nvnflinger::LayerBlending blending = Nvnflinger::LayerBlending::None;
|
Nvnflinger::LayerBlending blending = Nvnflinger::LayerBlending::None;
|
||||||
if (mode == LibraryAppletMode::PartialForeground) {
|
if (mode == LibraryAppletMode::PartialForeground ||
|
||||||
|
mode == LibraryAppletMode::PartialForegroundIndirectDisplay) {
|
||||||
blending = Nvnflinger::LayerBlending::Coverage;
|
blending = Nvnflinger::LayerBlending::Coverage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,12 +62,12 @@ void IWindowController::SetAppletWindowVisibility(HLERequestContext& ctx) {
|
|||||||
applet->hid_registration.EnableAppletToGetInput(visible);
|
applet->hid_registration.EnableAppletToGetInput(visible);
|
||||||
|
|
||||||
if (visible) {
|
if (visible) {
|
||||||
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground);
|
|
||||||
applet->focus_state = FocusState::InFocus;
|
applet->focus_state = FocusState::InFocus;
|
||||||
|
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground);
|
||||||
} else {
|
} else {
|
||||||
applet->focus_state = FocusState::NotInFocus;
|
applet->focus_state = FocusState::NotInFocus;
|
||||||
|
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoBackground);
|
||||||
}
|
}
|
||||||
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
|
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
|
@ -115,6 +115,11 @@ struct ArgumentTraits {
|
|||||||
static constexpr ArgumentType Type = ArgumentType::InData;
|
static constexpr ArgumentType Type = ArgumentType::InData;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename... Ts>
|
||||||
|
consteval bool ConstIfReference() {
|
||||||
|
return ((!std::is_reference_v<Ts> || std::is_const_v<std::remove_reference_t<Ts>>) && ... && true);
|
||||||
|
}
|
||||||
|
|
||||||
struct RequestLayout {
|
struct RequestLayout {
|
||||||
u32 copy_handle_count;
|
u32 copy_handle_count;
|
||||||
u32 move_handle_count;
|
u32 move_handle_count;
|
||||||
@ -435,6 +440,7 @@ void CmifReplyWrapImpl(HLERequestContext& ctx, T& t, Result (T::*f)(A...)) {
|
|||||||
}
|
}
|
||||||
const bool is_domain = Domain ? ctx.GetManager()->IsDomain() : false;
|
const bool is_domain = Domain ? ctx.GetManager()->IsDomain() : false;
|
||||||
|
|
||||||
|
static_assert(ConstIfReference<A...>(), "Arguments taken by reference must be const");
|
||||||
using MethodArguments = std::tuple<std::remove_cvref_t<A>...>;
|
using MethodArguments = std::tuple<std::remove_cvref_t<A>...>;
|
||||||
|
|
||||||
OutTemporaryBuffers buffers{};
|
OutTemporaryBuffers buffers{};
|
||||||
|
@ -4,10 +4,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <span>
|
||||||
|
|
||||||
#include "common/common_funcs.h"
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/hle/service/hle_ipc.h"
|
|
||||||
|
|
||||||
namespace Service {
|
namespace Service {
|
||||||
|
|
||||||
@ -22,8 +21,10 @@ class Out {
|
|||||||
public:
|
public:
|
||||||
using Type = T;
|
using Type = T;
|
||||||
|
|
||||||
|
/* implicit */ Out(const Out& t) : raw(t.raw) {}
|
||||||
/* implicit */ Out(AutoOut<Type>& t) : raw(&t.raw) {}
|
/* implicit */ Out(AutoOut<Type>& t) : raw(&t.raw) {}
|
||||||
/* implicit */ Out(Type* t) : raw(t) {}
|
/* implicit */ Out(Type* t) : raw(t) {}
|
||||||
|
Out& operator=(const Out&) = delete;
|
||||||
|
|
||||||
Type* Get() const {
|
Type* Get() const {
|
||||||
return raw;
|
return raw;
|
||||||
@ -37,6 +38,10 @@ public:
|
|||||||
return raw;
|
return raw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
operator Type*() const {
|
||||||
|
return raw;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Type* raw;
|
Type* raw;
|
||||||
};
|
};
|
||||||
@ -113,8 +118,10 @@ class OutCopyHandle {
|
|||||||
public:
|
public:
|
||||||
using Type = T*;
|
using Type = T*;
|
||||||
|
|
||||||
|
/* implicit */ OutCopyHandle(const OutCopyHandle& t) : raw(t.raw) {}
|
||||||
/* implicit */ OutCopyHandle(AutoOut<Type>& t) : raw(&t.raw) {}
|
/* implicit */ OutCopyHandle(AutoOut<Type>& t) : raw(&t.raw) {}
|
||||||
/* implicit */ OutCopyHandle(Type* t) : raw(t) {}
|
/* implicit */ OutCopyHandle(Type* t) : raw(t) {}
|
||||||
|
OutCopyHandle& operator=(const OutCopyHandle&) = delete;
|
||||||
|
|
||||||
Type* Get() const {
|
Type* Get() const {
|
||||||
return raw;
|
return raw;
|
||||||
@ -128,6 +135,10 @@ public:
|
|||||||
return raw;
|
return raw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
operator Type*() const {
|
||||||
|
return raw;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Type* raw;
|
Type* raw;
|
||||||
};
|
};
|
||||||
@ -137,8 +148,10 @@ class OutMoveHandle {
|
|||||||
public:
|
public:
|
||||||
using Type = T*;
|
using Type = T*;
|
||||||
|
|
||||||
|
/* implicit */ OutMoveHandle(const OutMoveHandle& t) : raw(t.raw) {}
|
||||||
/* implicit */ OutMoveHandle(AutoOut<Type>& t) : raw(&t.raw) {}
|
/* implicit */ OutMoveHandle(AutoOut<Type>& t) : raw(&t.raw) {}
|
||||||
/* implicit */ OutMoveHandle(Type* t) : raw(t) {}
|
/* implicit */ OutMoveHandle(Type* t) : raw(t) {}
|
||||||
|
OutMoveHandle& operator=(const OutMoveHandle&) = delete;
|
||||||
|
|
||||||
Type* Get() const {
|
Type* Get() const {
|
||||||
return raw;
|
return raw;
|
||||||
@ -152,6 +165,10 @@ public:
|
|||||||
return raw;
|
return raw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
operator Type*() const {
|
||||||
|
return raw;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Type* raw;
|
Type* raw;
|
||||||
};
|
};
|
||||||
@ -248,8 +265,10 @@ public:
|
|||||||
static constexpr BufferAttr Attr = static_cast<BufferAttr>(A | BufferAttr_In | BufferAttr_FixedSize);
|
static constexpr BufferAttr Attr = static_cast<BufferAttr>(A | BufferAttr_In | BufferAttr_FixedSize);
|
||||||
using Type = T;
|
using Type = T;
|
||||||
|
|
||||||
|
/* implicit */ OutLargeData(const OutLargeData& t) : raw(t.raw) {}
|
||||||
/* implicit */ OutLargeData(Type* t) : raw(t) {}
|
/* implicit */ OutLargeData(Type* t) : raw(t) {}
|
||||||
/* implicit */ OutLargeData(AutoOut<T>& t) : raw(&t.raw) {}
|
/* implicit */ OutLargeData(AutoOut<T>& t) : raw(&t.raw) {}
|
||||||
|
OutLargeData& operator=(const OutLargeData&) = delete;
|
||||||
|
|
||||||
Type* Get() const {
|
Type* Get() const {
|
||||||
return raw;
|
return raw;
|
||||||
@ -263,6 +282,10 @@ public:
|
|||||||
return raw;
|
return raw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
operator Type*() const {
|
||||||
|
return raw;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Type* raw;
|
Type* raw;
|
||||||
};
|
};
|
||||||
|
@ -115,6 +115,11 @@ private:
|
|||||||
if (type->GetName() == "save") {
|
if (type->GetName() == "save") {
|
||||||
for (const auto& save_id : type->GetSubdirectories()) {
|
for (const auto& save_id : type->GetSubdirectories()) {
|
||||||
for (const auto& user_id : save_id->GetSubdirectories()) {
|
for (const auto& user_id : save_id->GetSubdirectories()) {
|
||||||
|
// Skip non user id subdirectories
|
||||||
|
if (user_id->GetName().size() != 0x20) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const auto save_id_numeric = stoull_be(save_id->GetName());
|
const auto save_id_numeric = stoull_be(save_id->GetName());
|
||||||
auto user_id_numeric = Common::HexStringToArray<0x10>(user_id->GetName());
|
auto user_id_numeric = Common::HexStringToArray<0x10>(user_id->GetName());
|
||||||
std::reverse(user_id_numeric.begin(), user_id_numeric.end());
|
std::reverse(user_id_numeric.begin(), user_id_numeric.end());
|
||||||
@ -160,6 +165,10 @@ private:
|
|||||||
} else if (space == FileSys::SaveDataSpaceId::TemporaryStorage) {
|
} else if (space == FileSys::SaveDataSpaceId::TemporaryStorage) {
|
||||||
// Temporary Storage
|
// Temporary Storage
|
||||||
for (const auto& user_id : type->GetSubdirectories()) {
|
for (const auto& user_id : type->GetSubdirectories()) {
|
||||||
|
// Skip non user id subdirectories
|
||||||
|
if (user_id->GetName().size() != 0x20) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
for (const auto& title_id : user_id->GetSubdirectories()) {
|
for (const auto& title_id : user_id->GetSubdirectories()) {
|
||||||
if (!title_id->GetFiles().empty() ||
|
if (!title_id->GetFiles().empty() ||
|
||||||
!title_id->GetSubdirectories().empty()) {
|
!title_id->GetSubdirectories().empty()) {
|
||||||
|
@ -65,6 +65,7 @@ Result MountTimeZoneBinary(Core::System& system) {
|
|||||||
// Validate that the romfs is readable, using invalid firmware keys can cause this to get
|
// Validate that the romfs is readable, using invalid firmware keys can cause this to get
|
||||||
// set but the files to be garbage. In that case, we want to hit the next path and
|
// set but the files to be garbage. In that case, we want to hit the next path and
|
||||||
// synthesise them instead.
|
// synthesise them instead.
|
||||||
|
g_time_zone_binary_mount_result = ResultSuccess;
|
||||||
Service::PSC::Time::LocationName name{"Etc/GMT"};
|
Service::PSC::Time::LocationName name{"Etc/GMT"};
|
||||||
if (!IsTimeZoneBinaryValid(name)) {
|
if (!IsTimeZoneBinaryValid(name)) {
|
||||||
ResetTimeZoneBinary();
|
ResetTimeZoneBinary();
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
|
#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
|
||||||
#include "core/hle/service/nvnflinger/buffer_item.h"
|
#include "core/hle/service/nvnflinger/buffer_item.h"
|
||||||
#include "core/hle/service/nvnflinger/buffer_item_consumer.h"
|
#include "core/hle/service/nvnflinger/buffer_item_consumer.h"
|
||||||
#include "core/hle/service/nvnflinger/buffer_queue_producer.h"
|
|
||||||
#include "core/hle/service/nvnflinger/hardware_composer.h"
|
#include "core/hle/service/nvnflinger/hardware_composer.h"
|
||||||
#include "core/hle/service/nvnflinger/hwc_layer.h"
|
#include "core/hle/service/nvnflinger/hwc_layer.h"
|
||||||
#include "core/hle/service/nvnflinger/ui/graphic_buffer.h"
|
#include "core/hle/service/nvnflinger/ui/graphic_buffer.h"
|
||||||
@ -46,31 +45,9 @@ HardwareComposer::HardwareComposer() = default;
|
|||||||
HardwareComposer::~HardwareComposer() = default;
|
HardwareComposer::~HardwareComposer() = default;
|
||||||
|
|
||||||
u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, VI::Display& display,
|
u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, VI::Display& display,
|
||||||
Nvidia::Devices::nvdisp_disp0& nvdisp, u32 frame_advance) {
|
Nvidia::Devices::nvdisp_disp0& nvdisp) {
|
||||||
boost::container::small_vector<HwcLayer, 2> composition_stack;
|
boost::container::small_vector<HwcLayer, 2> composition_stack;
|
||||||
|
|
||||||
m_frame_number += frame_advance;
|
|
||||||
|
|
||||||
// Release any necessary framebuffers.
|
|
||||||
for (auto& [layer_id, framebuffer] : m_framebuffers) {
|
|
||||||
if (framebuffer.release_frame_number > m_frame_number) {
|
|
||||||
// Not yet ready to release this framebuffer.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!framebuffer.is_acquired) {
|
|
||||||
// Already released.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (auto* layer = display.FindLayer(layer_id); layer != nullptr) {
|
|
||||||
// TODO: support release fence
|
|
||||||
// This is needed to prevent screen tearing
|
|
||||||
layer->GetConsumer().ReleaseBuffer(framebuffer.item, android::Fence::NoFence());
|
|
||||||
framebuffer.is_acquired = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set default speed limit to 100%.
|
// Set default speed limit to 100%.
|
||||||
*out_speed_scale = 1.0f;
|
*out_speed_scale = 1.0f;
|
||||||
|
|
||||||
@ -143,7 +120,30 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, VI::Display& display,
|
|||||||
MicroProfileFlip();
|
MicroProfileFlip();
|
||||||
|
|
||||||
// Advance by at least one frame.
|
// Advance by at least one frame.
|
||||||
return swap_interval.value_or(1);
|
const u32 frame_advance = swap_interval.value_or(1);
|
||||||
|
m_frame_number += frame_advance;
|
||||||
|
|
||||||
|
// Release any necessary framebuffers.
|
||||||
|
for (auto& [layer_id, framebuffer] : m_framebuffers) {
|
||||||
|
if (framebuffer.release_frame_number > m_frame_number) {
|
||||||
|
// Not yet ready to release this framebuffer.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!framebuffer.is_acquired) {
|
||||||
|
// Already released.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto* layer = display.FindLayer(layer_id); layer != nullptr) {
|
||||||
|
// TODO: support release fence
|
||||||
|
// This is needed to prevent screen tearing
|
||||||
|
layer->GetConsumer().ReleaseBuffer(framebuffer.item, android::Fence::NoFence());
|
||||||
|
framebuffer.is_acquired = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return frame_advance;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HardwareComposer::RemoveLayerLocked(VI::Display& display, LayerId layer_id) {
|
void HardwareComposer::RemoveLayerLocked(VI::Display& display, LayerId layer_id) {
|
||||||
|
@ -27,7 +27,7 @@ public:
|
|||||||
~HardwareComposer();
|
~HardwareComposer();
|
||||||
|
|
||||||
u32 ComposeLocked(f32* out_speed_scale, VI::Display& display,
|
u32 ComposeLocked(f32* out_speed_scale, VI::Display& display,
|
||||||
Nvidia::Devices::nvdisp_disp0& nvdisp, u32 frame_advance);
|
Nvidia::Devices::nvdisp_disp0& nvdisp);
|
||||||
void RemoveLayerLocked(VI::Display& display, LayerId layer_id);
|
void RemoveLayerLocked(VI::Display& display, LayerId layer_id);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -292,8 +292,7 @@ void Nvnflinger::Compose() {
|
|||||||
auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>(disp_fd);
|
auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>(disp_fd);
|
||||||
ASSERT(nvdisp);
|
ASSERT(nvdisp);
|
||||||
|
|
||||||
swap_interval = display.GetComposer().ComposeLocked(&compose_speed_scale, display, *nvdisp,
|
swap_interval = display.GetComposer().ComposeLocked(&compose_speed_scale, display, *nvdisp);
|
||||||
swap_interval);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include "core/core_timing.h"
|
#include "core/core_timing.h"
|
||||||
#include "core/hle/kernel/k_page_table.h"
|
#include "core/hle/kernel/k_page_table.h"
|
||||||
#include "core/hle/kernel/k_process.h"
|
#include "core/hle/kernel/k_process.h"
|
||||||
|
#include "core/hle/kernel/k_process_page_table.h"
|
||||||
#include "core/hle/service/hid/hid_server.h"
|
#include "core/hle/service/hid/hid_server.h"
|
||||||
#include "core/hle/service/sm/sm.h"
|
#include "core/hle/service/sm/sm.h"
|
||||||
#include "core/memory.h"
|
#include "core/memory.h"
|
||||||
@ -85,8 +86,12 @@ VAddr StandardVmCallbacks::SanitizeAddress(VAddr in) const {
|
|||||||
if ((in < metadata.main_nso_extents.base ||
|
if ((in < metadata.main_nso_extents.base ||
|
||||||
in >= metadata.main_nso_extents.base + metadata.main_nso_extents.size) &&
|
in >= metadata.main_nso_extents.base + metadata.main_nso_extents.size) &&
|
||||||
(in < metadata.heap_extents.base ||
|
(in < metadata.heap_extents.base ||
|
||||||
in >= metadata.heap_extents.base + metadata.heap_extents.size)) {
|
in >= metadata.heap_extents.base + metadata.heap_extents.size) &&
|
||||||
LOG_ERROR(CheatEngine,
|
(in < metadata.alias_extents.base ||
|
||||||
|
in >= metadata.heap_extents.base + metadata.alias_extents.size) &&
|
||||||
|
(in < metadata.aslr_extents.base ||
|
||||||
|
in >= metadata.heap_extents.base + metadata.aslr_extents.size)) {
|
||||||
|
LOG_DEBUG(CheatEngine,
|
||||||
"Cheat attempting to access memory at invalid address={:016X}, if this "
|
"Cheat attempting to access memory at invalid address={:016X}, if this "
|
||||||
"persists, "
|
"persists, "
|
||||||
"the cheat may be incorrect. However, this may be normal early in execution if "
|
"the cheat may be incorrect. However, this may be normal early in execution if "
|
||||||
@ -211,16 +216,14 @@ void CheatEngine::Initialize() {
|
|||||||
.base = GetInteger(page_table.GetHeapRegionStart()),
|
.base = GetInteger(page_table.GetHeapRegionStart()),
|
||||||
.size = page_table.GetHeapRegionSize(),
|
.size = page_table.GetHeapRegionSize(),
|
||||||
};
|
};
|
||||||
|
metadata.aslr_extents = {
|
||||||
metadata.address_space_extents = {
|
|
||||||
.base = GetInteger(page_table.GetAddressSpaceStart()),
|
|
||||||
.size = page_table.GetAddressSpaceSize(),
|
|
||||||
};
|
|
||||||
|
|
||||||
metadata.alias_extents = {
|
|
||||||
.base = GetInteger(page_table.GetAliasCodeRegionStart()),
|
.base = GetInteger(page_table.GetAliasCodeRegionStart()),
|
||||||
.size = page_table.GetAliasCodeRegionSize(),
|
.size = page_table.GetAliasCodeRegionSize(),
|
||||||
};
|
};
|
||||||
|
metadata.alias_extents = {
|
||||||
|
.base = GetInteger(page_table.GetAliasRegionStart()),
|
||||||
|
.size = page_table.GetAliasRegionSize(),
|
||||||
|
};
|
||||||
|
|
||||||
is_pending_reload.exchange(true);
|
is_pending_reload.exchange(true);
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ private:
|
|||||||
VAddr SanitizeAddress(VAddr address) const;
|
VAddr SanitizeAddress(VAddr address) const;
|
||||||
|
|
||||||
const CheatProcessMetadata& metadata;
|
const CheatProcessMetadata& metadata;
|
||||||
System& system;
|
Core::System& system;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Intermediary class that parses a text file or other disk format for storing cheats into a
|
// Intermediary class that parses a text file or other disk format for storing cheats into a
|
||||||
|
@ -18,7 +18,7 @@ struct CheatProcessMetadata {
|
|||||||
MemoryRegionExtents main_nso_extents{};
|
MemoryRegionExtents main_nso_extents{};
|
||||||
MemoryRegionExtents heap_extents{};
|
MemoryRegionExtents heap_extents{};
|
||||||
MemoryRegionExtents alias_extents{};
|
MemoryRegionExtents alias_extents{};
|
||||||
MemoryRegionExtents address_space_extents{};
|
MemoryRegionExtents aslr_extents{};
|
||||||
std::array<u8, 0x20> main_nso_build_id{};
|
std::array<u8, 0x20> main_nso_build_id{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -322,8 +322,9 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
|
|||||||
} break;
|
} break;
|
||||||
case CheatVmOpcodeType::EndConditionalBlock: {
|
case CheatVmOpcodeType::EndConditionalBlock: {
|
||||||
// 20000000
|
// 20000000
|
||||||
// There's actually nothing left to process here!
|
opcode.opcode = EndConditionalOpcode{
|
||||||
opcode.opcode = EndConditionalOpcode{};
|
.is_else = ((first_dword >> 24) & 0xf) == 1,
|
||||||
|
};
|
||||||
} break;
|
} break;
|
||||||
case CheatVmOpcodeType::ControlLoop: {
|
case CheatVmOpcodeType::ControlLoop: {
|
||||||
// 300R0000 VVVVVVVV
|
// 300R0000 VVVVVVVV
|
||||||
@ -555,6 +556,18 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
|
|||||||
.idx = first_dword & 0xF,
|
.idx = first_dword & 0xF,
|
||||||
};
|
};
|
||||||
} break;
|
} break;
|
||||||
|
case CheatVmOpcodeType::PauseProcess: {
|
||||||
|
/* FF0????? */
|
||||||
|
/* FF0 = opcode 0xFF0 */
|
||||||
|
/* Pauses the current process. */
|
||||||
|
opcode.opcode = PauseProcessOpcode{};
|
||||||
|
} break;
|
||||||
|
case CheatVmOpcodeType::ResumeProcess: {
|
||||||
|
/* FF0????? */
|
||||||
|
/* FF0 = opcode 0xFF0 */
|
||||||
|
/* Pauses the current process. */
|
||||||
|
opcode.opcode = ResumeProcessOpcode{};
|
||||||
|
} break;
|
||||||
case CheatVmOpcodeType::DebugLog: {
|
case CheatVmOpcodeType::DebugLog: {
|
||||||
// FFFTIX##
|
// FFFTIX##
|
||||||
// FFFTI0Ma aaaaaaaa
|
// FFFTI0Ma aaaaaaaa
|
||||||
@ -621,7 +634,7 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
|
|||||||
return valid;
|
return valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DmntCheatVm::SkipConditionalBlock() {
|
void DmntCheatVm::SkipConditionalBlock(bool is_if) {
|
||||||
if (condition_depth > 0) {
|
if (condition_depth > 0) {
|
||||||
// We want to continue until we're out of the current block.
|
// We want to continue until we're out of the current block.
|
||||||
const std::size_t desired_depth = condition_depth - 1;
|
const std::size_t desired_depth = condition_depth - 1;
|
||||||
@ -637,8 +650,12 @@ void DmntCheatVm::SkipConditionalBlock() {
|
|||||||
// We also support nesting of conditional blocks, and Gateway does not.
|
// We also support nesting of conditional blocks, and Gateway does not.
|
||||||
if (skip_opcode.begin_conditional_block) {
|
if (skip_opcode.begin_conditional_block) {
|
||||||
condition_depth++;
|
condition_depth++;
|
||||||
} else if (std::holds_alternative<EndConditionalOpcode>(skip_opcode.opcode)) {
|
} else if (auto end_cond = std::get_if<EndConditionalOpcode>(&skip_opcode.opcode)) {
|
||||||
condition_depth--;
|
if (!end_cond->is_else) {
|
||||||
|
condition_depth--;
|
||||||
|
} else if (is_if && condition_depth - 1 == desired_depth) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -675,6 +692,10 @@ u64 DmntCheatVm::GetCheatProcessAddress(const CheatProcessMetadata& metadata,
|
|||||||
return metadata.main_nso_extents.base + rel_address;
|
return metadata.main_nso_extents.base + rel_address;
|
||||||
case MemoryAccessType::Heap:
|
case MemoryAccessType::Heap:
|
||||||
return metadata.heap_extents.base + rel_address;
|
return metadata.heap_extents.base + rel_address;
|
||||||
|
case MemoryAccessType::Alias:
|
||||||
|
return metadata.alias_extents.base + rel_address;
|
||||||
|
case MemoryAccessType::Aslr:
|
||||||
|
return metadata.aslr_extents.base + rel_address;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -682,7 +703,6 @@ void DmntCheatVm::ResetState() {
|
|||||||
registers.fill(0);
|
registers.fill(0);
|
||||||
saved_values.fill(0);
|
saved_values.fill(0);
|
||||||
loop_tops.fill(0);
|
loop_tops.fill(0);
|
||||||
static_registers.fill(0);
|
|
||||||
instruction_ptr = 0;
|
instruction_ptr = 0;
|
||||||
condition_depth = 0;
|
condition_depth = 0;
|
||||||
decode_success = true;
|
decode_success = true;
|
||||||
@ -794,13 +814,18 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
|
|||||||
}
|
}
|
||||||
// Skip conditional block if condition not met.
|
// Skip conditional block if condition not met.
|
||||||
if (!cond_met) {
|
if (!cond_met) {
|
||||||
SkipConditionalBlock();
|
SkipConditionalBlock(true);
|
||||||
}
|
}
|
||||||
} else if (std::holds_alternative<EndConditionalOpcode>(cur_opcode.opcode)) {
|
} else if (auto end_cond = std::get_if<EndConditionalOpcode>(&cur_opcode.opcode)) {
|
||||||
// Decrement the condition depth.
|
if (end_cond->is_else) {
|
||||||
// We will assume, graciously, that mismatched conditional block ends are a nop.
|
/* Skip to the end of the conditional block. */
|
||||||
if (condition_depth > 0) {
|
this->SkipConditionalBlock(false);
|
||||||
condition_depth--;
|
} else {
|
||||||
|
/* Decrement the condition depth. */
|
||||||
|
/* We will assume, graciously, that mismatched conditional block ends are a nop. */
|
||||||
|
if (condition_depth > 0) {
|
||||||
|
condition_depth--;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (auto ctrl_loop = std::get_if<ControlLoopOpcode>(&cur_opcode.opcode)) {
|
} else if (auto ctrl_loop = std::get_if<ControlLoopOpcode>(&cur_opcode.opcode)) {
|
||||||
if (ctrl_loop->start_loop) {
|
if (ctrl_loop->start_loop) {
|
||||||
@ -908,7 +933,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
|
|||||||
// Check for keypress.
|
// Check for keypress.
|
||||||
if ((begin_keypress_cond->key_mask & kDown) != begin_keypress_cond->key_mask) {
|
if ((begin_keypress_cond->key_mask & kDown) != begin_keypress_cond->key_mask) {
|
||||||
// Keys not pressed. Skip conditional block.
|
// Keys not pressed. Skip conditional block.
|
||||||
SkipConditionalBlock();
|
SkipConditionalBlock(true);
|
||||||
}
|
}
|
||||||
} else if (auto perform_math_reg =
|
} else if (auto perform_math_reg =
|
||||||
std::get_if<PerformArithmeticRegisterOpcode>(&cur_opcode.opcode)) {
|
std::get_if<PerformArithmeticRegisterOpcode>(&cur_opcode.opcode)) {
|
||||||
@ -1116,7 +1141,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
|
|||||||
|
|
||||||
// Skip conditional block if condition not met.
|
// Skip conditional block if condition not met.
|
||||||
if (!cond_met) {
|
if (!cond_met) {
|
||||||
SkipConditionalBlock();
|
SkipConditionalBlock(true);
|
||||||
}
|
}
|
||||||
} else if (auto save_restore_reg =
|
} else if (auto save_restore_reg =
|
||||||
std::get_if<SaveRestoreRegisterOpcode>(&cur_opcode.opcode)) {
|
std::get_if<SaveRestoreRegisterOpcode>(&cur_opcode.opcode)) {
|
||||||
@ -1178,6 +1203,10 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
|
|||||||
// Store a register to a static register.
|
// Store a register to a static register.
|
||||||
static_registers[rw_static_reg->static_idx] = registers[rw_static_reg->idx];
|
static_registers[rw_static_reg->static_idx] = registers[rw_static_reg->idx];
|
||||||
}
|
}
|
||||||
|
} else if (std::holds_alternative<PauseProcessOpcode>(cur_opcode.opcode)) {
|
||||||
|
// TODO: Pause cheat process
|
||||||
|
} else if (std::holds_alternative<ResumeProcessOpcode>(cur_opcode.opcode)) {
|
||||||
|
// TODO: Resume cheat process
|
||||||
} else if (auto debug_log = std::get_if<DebugLogOpcode>(&cur_opcode.opcode)) {
|
} else if (auto debug_log = std::get_if<DebugLogOpcode>(&cur_opcode.opcode)) {
|
||||||
// Read value from memory.
|
// Read value from memory.
|
||||||
u64 log_value = 0;
|
u64 log_value = 0;
|
||||||
|
@ -42,12 +42,16 @@ enum class CheatVmOpcodeType : u32 {
|
|||||||
DoubleExtendedWidth = 0xF0,
|
DoubleExtendedWidth = 0xF0,
|
||||||
|
|
||||||
// Double-extended width opcodes.
|
// Double-extended width opcodes.
|
||||||
|
PauseProcess = 0xFF0,
|
||||||
|
ResumeProcess = 0xFF1,
|
||||||
DebugLog = 0xFFF,
|
DebugLog = 0xFFF,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class MemoryAccessType : u32 {
|
enum class MemoryAccessType : u32 {
|
||||||
MainNso = 0,
|
MainNso = 0,
|
||||||
Heap = 1,
|
Heap = 1,
|
||||||
|
Alias = 2,
|
||||||
|
Aslr = 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class ConditionalComparisonType : u32 {
|
enum class ConditionalComparisonType : u32 {
|
||||||
@ -131,7 +135,9 @@ struct BeginConditionalOpcode {
|
|||||||
VmInt value{};
|
VmInt value{};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct EndConditionalOpcode {};
|
struct EndConditionalOpcode {
|
||||||
|
bool is_else;
|
||||||
|
};
|
||||||
|
|
||||||
struct ControlLoopOpcode {
|
struct ControlLoopOpcode {
|
||||||
bool start_loop{};
|
bool start_loop{};
|
||||||
@ -222,6 +228,10 @@ struct ReadWriteStaticRegisterOpcode {
|
|||||||
u32 idx{};
|
u32 idx{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct PauseProcessOpcode {};
|
||||||
|
|
||||||
|
struct ResumeProcessOpcode {};
|
||||||
|
|
||||||
struct DebugLogOpcode {
|
struct DebugLogOpcode {
|
||||||
u32 bit_width{};
|
u32 bit_width{};
|
||||||
u32 log_id{};
|
u32 log_id{};
|
||||||
@ -244,8 +254,8 @@ struct CheatVmOpcode {
|
|||||||
PerformArithmeticStaticOpcode, BeginKeypressConditionalOpcode,
|
PerformArithmeticStaticOpcode, BeginKeypressConditionalOpcode,
|
||||||
PerformArithmeticRegisterOpcode, StoreRegisterToAddressOpcode,
|
PerformArithmeticRegisterOpcode, StoreRegisterToAddressOpcode,
|
||||||
BeginRegisterConditionalOpcode, SaveRestoreRegisterOpcode,
|
BeginRegisterConditionalOpcode, SaveRestoreRegisterOpcode,
|
||||||
SaveRestoreRegisterMaskOpcode, ReadWriteStaticRegisterOpcode, DebugLogOpcode,
|
SaveRestoreRegisterMaskOpcode, ReadWriteStaticRegisterOpcode, PauseProcessOpcode,
|
||||||
UnrecognizedInstruction>
|
ResumeProcessOpcode, DebugLogOpcode, UnrecognizedInstruction>
|
||||||
opcode{};
|
opcode{};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -296,7 +306,7 @@ private:
|
|||||||
std::array<std::size_t, NumRegisters> loop_tops{};
|
std::array<std::size_t, NumRegisters> loop_tops{};
|
||||||
|
|
||||||
bool DecodeNextOpcode(CheatVmOpcode& out);
|
bool DecodeNextOpcode(CheatVmOpcode& out);
|
||||||
void SkipConditionalBlock();
|
void SkipConditionalBlock(bool is_if);
|
||||||
void ResetState();
|
void ResetState();
|
||||||
|
|
||||||
// For implementing the DebugLog opcode.
|
// For implementing the DebugLog opcode.
|
||||||
|
@ -12,6 +12,11 @@ namespace Shader::Backend::SPIRV {
|
|||||||
namespace {
|
namespace {
|
||||||
class ImageOperands {
|
class ImageOperands {
|
||||||
public:
|
public:
|
||||||
|
[[maybe_unused]] static constexpr bool ImageSampleOffsetAllowed = false;
|
||||||
|
[[maybe_unused]] static constexpr bool ImageGatherOffsetAllowed = true;
|
||||||
|
[[maybe_unused]] static constexpr bool ImageFetchOffsetAllowed = false;
|
||||||
|
[[maybe_unused]] static constexpr bool ImageGradientOffsetAllowed = false;
|
||||||
|
|
||||||
explicit ImageOperands(EmitContext& ctx, bool has_bias, bool has_lod, bool has_lod_clamp,
|
explicit ImageOperands(EmitContext& ctx, bool has_bias, bool has_lod, bool has_lod_clamp,
|
||||||
Id lod, const IR::Value& offset) {
|
Id lod, const IR::Value& offset) {
|
||||||
if (has_bias) {
|
if (has_bias) {
|
||||||
@ -22,7 +27,7 @@ public:
|
|||||||
const Id lod_value{has_lod_clamp ? ctx.OpCompositeExtract(ctx.F32[1], lod, 0) : lod};
|
const Id lod_value{has_lod_clamp ? ctx.OpCompositeExtract(ctx.F32[1], lod, 0) : lod};
|
||||||
Add(spv::ImageOperandsMask::Lod, lod_value);
|
Add(spv::ImageOperandsMask::Lod, lod_value);
|
||||||
}
|
}
|
||||||
AddOffset(ctx, offset);
|
AddOffset(ctx, offset, ImageSampleOffsetAllowed);
|
||||||
if (has_lod_clamp) {
|
if (has_lod_clamp) {
|
||||||
const Id lod_clamp{has_bias ? ctx.OpCompositeExtract(ctx.F32[1], lod, 1) : lod};
|
const Id lod_clamp{has_bias ? ctx.OpCompositeExtract(ctx.F32[1], lod, 1) : lod};
|
||||||
Add(spv::ImageOperandsMask::MinLod, lod_clamp);
|
Add(spv::ImageOperandsMask::MinLod, lod_clamp);
|
||||||
@ -55,20 +60,18 @@ public:
|
|||||||
Add(spv::ImageOperandsMask::ConstOffsets, offsets);
|
Add(spv::ImageOperandsMask::ConstOffsets, offsets);
|
||||||
}
|
}
|
||||||
|
|
||||||
explicit ImageOperands(Id offset, Id lod, Id ms) {
|
explicit ImageOperands(EmitContext& ctx, const IR::Value& offset, Id lod, Id ms) {
|
||||||
|
AddOffset(ctx, offset, ImageFetchOffsetAllowed);
|
||||||
if (Sirit::ValidId(lod)) {
|
if (Sirit::ValidId(lod)) {
|
||||||
Add(spv::ImageOperandsMask::Lod, lod);
|
Add(spv::ImageOperandsMask::Lod, lod);
|
||||||
}
|
}
|
||||||
if (Sirit::ValidId(offset)) {
|
|
||||||
Add(spv::ImageOperandsMask::Offset, offset);
|
|
||||||
}
|
|
||||||
if (Sirit::ValidId(ms)) {
|
if (Sirit::ValidId(ms)) {
|
||||||
Add(spv::ImageOperandsMask::Sample, ms);
|
Add(spv::ImageOperandsMask::Sample, ms);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
explicit ImageOperands(EmitContext& ctx, bool has_lod_clamp, Id derivatives,
|
explicit ImageOperands(EmitContext& ctx, bool has_lod_clamp, Id derivatives,
|
||||||
u32 num_derivatives, Id offset, Id lod_clamp) {
|
u32 num_derivatives, const IR::Value& offset, Id lod_clamp) {
|
||||||
if (!Sirit::ValidId(derivatives)) {
|
if (!Sirit::ValidId(derivatives)) {
|
||||||
throw LogicError("Derivatives must be present");
|
throw LogicError("Derivatives must be present");
|
||||||
}
|
}
|
||||||
@ -83,16 +86,14 @@ public:
|
|||||||
const Id derivatives_Y{ctx.OpCompositeConstruct(
|
const Id derivatives_Y{ctx.OpCompositeConstruct(
|
||||||
ctx.F32[num_derivatives], std::span{deriv_y_accum.data(), deriv_y_accum.size()})};
|
ctx.F32[num_derivatives], std::span{deriv_y_accum.data(), deriv_y_accum.size()})};
|
||||||
Add(spv::ImageOperandsMask::Grad, derivatives_X, derivatives_Y);
|
Add(spv::ImageOperandsMask::Grad, derivatives_X, derivatives_Y);
|
||||||
if (Sirit::ValidId(offset)) {
|
AddOffset(ctx, offset, ImageGradientOffsetAllowed);
|
||||||
Add(spv::ImageOperandsMask::Offset, offset);
|
|
||||||
}
|
|
||||||
if (has_lod_clamp) {
|
if (has_lod_clamp) {
|
||||||
Add(spv::ImageOperandsMask::MinLod, lod_clamp);
|
Add(spv::ImageOperandsMask::MinLod, lod_clamp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
explicit ImageOperands(EmitContext& ctx, bool has_lod_clamp, Id derivatives_1, Id derivatives_2,
|
explicit ImageOperands(EmitContext& ctx, bool has_lod_clamp, Id derivatives_1, Id derivatives_2,
|
||||||
Id offset, Id lod_clamp) {
|
const IR::Value& offset, Id lod_clamp) {
|
||||||
if (!Sirit::ValidId(derivatives_1) || !Sirit::ValidId(derivatives_2)) {
|
if (!Sirit::ValidId(derivatives_1) || !Sirit::ValidId(derivatives_2)) {
|
||||||
throw LogicError("Derivatives must be present");
|
throw LogicError("Derivatives must be present");
|
||||||
}
|
}
|
||||||
@ -111,9 +112,7 @@ public:
|
|||||||
const Id derivatives_id2{ctx.OpCompositeConstruct(
|
const Id derivatives_id2{ctx.OpCompositeConstruct(
|
||||||
ctx.F32[3], std::span{deriv_2_accum.data(), deriv_2_accum.size()})};
|
ctx.F32[3], std::span{deriv_2_accum.data(), deriv_2_accum.size()})};
|
||||||
Add(spv::ImageOperandsMask::Grad, derivatives_id1, derivatives_id2);
|
Add(spv::ImageOperandsMask::Grad, derivatives_id1, derivatives_id2);
|
||||||
if (Sirit::ValidId(offset)) {
|
AddOffset(ctx, offset, ImageGradientOffsetAllowed);
|
||||||
Add(spv::ImageOperandsMask::Offset, offset);
|
|
||||||
}
|
|
||||||
if (has_lod_clamp) {
|
if (has_lod_clamp) {
|
||||||
Add(spv::ImageOperandsMask::MinLod, lod_clamp);
|
Add(spv::ImageOperandsMask::MinLod, lod_clamp);
|
||||||
}
|
}
|
||||||
@ -132,7 +131,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void AddOffset(EmitContext& ctx, const IR::Value& offset) {
|
void AddOffset(EmitContext& ctx, const IR::Value& offset, bool runtime_offset_allowed) {
|
||||||
if (offset.IsEmpty()) {
|
if (offset.IsEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -165,7 +164,9 @@ private:
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Add(spv::ImageOperandsMask::Offset, ctx.Def(offset));
|
if (runtime_offset_allowed) {
|
||||||
|
Add(spv::ImageOperandsMask::Offset, ctx.Def(offset));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Add(spv::ImageOperandsMask new_mask, Id value) {
|
void Add(spv::ImageOperandsMask new_mask, Id value) {
|
||||||
@ -493,8 +494,8 @@ Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
|
|||||||
operands.Span());
|
operands.Span());
|
||||||
}
|
}
|
||||||
|
|
||||||
Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset,
|
Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||||
Id lod, Id ms) {
|
const IR::Value& offset, Id lod, Id ms) {
|
||||||
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
||||||
if (info.type == TextureType::Buffer) {
|
if (info.type == TextureType::Buffer) {
|
||||||
lod = Id{};
|
lod = Id{};
|
||||||
@ -503,7 +504,7 @@ Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id c
|
|||||||
// This image is multisampled, lod must be implicit
|
// This image is multisampled, lod must be implicit
|
||||||
lod = Id{};
|
lod = Id{};
|
||||||
}
|
}
|
||||||
const ImageOperands operands(offset, lod, ms);
|
const ImageOperands operands(ctx, offset, lod, ms);
|
||||||
return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4],
|
return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4],
|
||||||
TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span());
|
TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span());
|
||||||
}
|
}
|
||||||
@ -548,13 +549,13 @@ Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, I
|
|||||||
}
|
}
|
||||||
|
|
||||||
Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||||
Id derivatives, Id offset, Id lod_clamp) {
|
Id derivatives, const IR::Value& offset, Id lod_clamp) {
|
||||||
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
||||||
const auto operands =
|
const auto operands = info.num_derivatives == 3
|
||||||
info.num_derivatives == 3
|
? ImageOperands(ctx, info.has_lod_clamp != 0, derivatives,
|
||||||
? ImageOperands(ctx, info.has_lod_clamp != 0, derivatives, offset, {}, lod_clamp)
|
ctx.Def(offset), {}, lod_clamp)
|
||||||
: ImageOperands(ctx, info.has_lod_clamp != 0, derivatives, info.num_derivatives, offset,
|
: ImageOperands(ctx, info.has_lod_clamp != 0, derivatives,
|
||||||
lod_clamp);
|
info.num_derivatives, offset, lod_clamp);
|
||||||
return Emit(&EmitContext::OpImageSparseSampleExplicitLod,
|
return Emit(&EmitContext::OpImageSparseSampleExplicitLod,
|
||||||
&EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4],
|
&EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4],
|
||||||
Texture(ctx, info, index), coords, operands.Mask(), operands.Span());
|
Texture(ctx, info, index), coords, operands.Mask(), operands.Span());
|
||||||
|
@ -537,13 +537,13 @@ Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id
|
|||||||
const IR::Value& offset, const IR::Value& offset2);
|
const IR::Value& offset, const IR::Value& offset2);
|
||||||
Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||||
const IR::Value& offset, const IR::Value& offset2, Id dref);
|
const IR::Value& offset, const IR::Value& offset2, Id dref);
|
||||||
Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset,
|
Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||||
Id lod, Id ms);
|
const IR::Value& offset, Id lod, Id ms);
|
||||||
Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod,
|
Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod,
|
||||||
const IR::Value& skip_mips);
|
const IR::Value& skip_mips);
|
||||||
Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords);
|
Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords);
|
||||||
Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||||
Id derivatives, Id offset, Id lod_clamp);
|
Id derivatives, const IR::Value& offset, Id lod_clamp);
|
||||||
Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords);
|
Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords);
|
||||||
void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id color);
|
void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id color);
|
||||||
Id EmitIsTextureScaled(EmitContext& ctx, const IR::Value& index);
|
Id EmitIsTextureScaled(EmitContext& ctx, const IR::Value& index);
|
||||||
|
@ -13,20 +13,101 @@ Scheduler::Scheduler(GPU& gpu_) : gpu{gpu_} {}
|
|||||||
|
|
||||||
Scheduler::~Scheduler() = default;
|
Scheduler::~Scheduler() = default;
|
||||||
|
|
||||||
|
void Scheduler::Init() {
|
||||||
|
master_control = Common::Fiber::ThreadToFiber();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Scheduler::Resume() {
|
||||||
|
bool nothing_pending;
|
||||||
|
do {
|
||||||
|
nothing_pending = true;
|
||||||
|
current_fifo = nullptr;
|
||||||
|
{
|
||||||
|
std::unique_lock lk(scheduling_guard);
|
||||||
|
size_t num_iters = gpfifos.size();
|
||||||
|
for (size_t i = 0; i < num_iters; i++) {
|
||||||
|
size_t current_id = (current_fifo_rotation_id + i) % gpfifos.size();
|
||||||
|
auto& fifo = gpfifos[current_id];
|
||||||
|
if (!fifo.is_active) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
std::scoped_lock lk2(fifo.guard);
|
||||||
|
if (!fifo.pending_work.empty() || fifo.working.load(std::memory_order_acquire)) {
|
||||||
|
current_fifo = &fifo;
|
||||||
|
current_fifo_rotation_id = current_id;
|
||||||
|
nothing_pending = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (current_fifo) {
|
||||||
|
Common::Fiber::YieldTo(master_control, *current_fifo->context);
|
||||||
|
current_fifo = nullptr;
|
||||||
|
}
|
||||||
|
} while (!nothing_pending);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Scheduler::Yield() {
|
||||||
|
ASSERT(current_fifo != nullptr);
|
||||||
|
Common::Fiber::YieldTo(current_fifo->context, *master_control);
|
||||||
|
}
|
||||||
|
|
||||||
void Scheduler::Push(s32 channel, CommandList&& entries) {
|
void Scheduler::Push(s32 channel, CommandList&& entries) {
|
||||||
std::unique_lock lk(scheduling_guard);
|
std::unique_lock lk(scheduling_guard);
|
||||||
auto it = channels.find(channel);
|
auto it = channel_gpfifo_ids.find(channel);
|
||||||
ASSERT(it != channels.end());
|
ASSERT(it != channel_gpfifo_ids.end());
|
||||||
auto channel_state = it->second;
|
auto gpfifo_id = it->second;
|
||||||
gpu.BindChannel(channel_state->bind_id);
|
auto& fifo = gpfifos[gpfifo_id];
|
||||||
channel_state->dma_pusher->Push(std::move(entries));
|
{
|
||||||
channel_state->dma_pusher->DispatchCalls();
|
std::scoped_lock lk2(fifo.guard);
|
||||||
|
fifo.pending_work.emplace_back(std::move(entries));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Scheduler::ChannelLoop(size_t gpfifo_id, s32 channel_id) {
|
||||||
|
gpu.BindChannel(channel_id);
|
||||||
|
auto& fifo = gpfifos[gpfifo_id];
|
||||||
|
while (true) {
|
||||||
|
auto* channel_state = channels[channel_id].get();
|
||||||
|
fifo.guard.lock();
|
||||||
|
while (!fifo.pending_work.empty()) {
|
||||||
|
{
|
||||||
|
|
||||||
|
fifo.working.store(true, std::memory_order_release);
|
||||||
|
CommandList&& entries = std::move(fifo.pending_work.front());
|
||||||
|
channel_state->dma_pusher->Push(std::move(entries));
|
||||||
|
fifo.pending_work.pop_front();
|
||||||
|
}
|
||||||
|
fifo.guard.unlock();
|
||||||
|
channel_state->dma_pusher->DispatchCalls();
|
||||||
|
fifo.guard.lock();
|
||||||
|
}
|
||||||
|
fifo.working.store(false, std::memory_order_relaxed);
|
||||||
|
fifo.guard.unlock();
|
||||||
|
Common::Fiber::YieldTo(fifo.context, *master_control);
|
||||||
|
gpu.BindChannel(channel_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scheduler::DeclareChannel(std::shared_ptr<ChannelState> new_channel) {
|
void Scheduler::DeclareChannel(std::shared_ptr<ChannelState> new_channel) {
|
||||||
s32 channel = new_channel->bind_id;
|
s32 channel = new_channel->bind_id;
|
||||||
std::unique_lock lk(scheduling_guard);
|
std::unique_lock lk(scheduling_guard);
|
||||||
channels.emplace(channel, new_channel);
|
channels.emplace(channel, new_channel);
|
||||||
|
size_t new_fifo_id;
|
||||||
|
if (!free_fifos.empty()) {
|
||||||
|
new_fifo_id = free_fifos.front();
|
||||||
|
free_fifos.pop_front();
|
||||||
|
} else {
|
||||||
|
new_fifo_id = gpfifos.size();
|
||||||
|
gpfifos.emplace_back();
|
||||||
|
}
|
||||||
|
auto& new_fifo = gpfifos[new_fifo_id];
|
||||||
|
channel_gpfifo_ids[channel] = new_fifo_id;
|
||||||
|
new_fifo.is_active = true;
|
||||||
|
new_fifo.bind_id = channel;
|
||||||
|
new_fifo.pending_work.clear();
|
||||||
|
std::function<void()> callback = std::bind(&Scheduler::ChannelLoop, this, new_fifo_id, channel);
|
||||||
|
new_fifo.context = std::make_shared<Common::Fiber>(std::move(callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Tegra::Control
|
} // namespace Tegra::Control
|
||||||
|
@ -3,10 +3,13 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <deque>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#include "common/fiber.h"
|
||||||
#include "video_core/dma_pusher.h"
|
#include "video_core/dma_pusher.h"
|
||||||
|
|
||||||
namespace Tegra {
|
namespace Tegra {
|
||||||
@ -22,14 +25,36 @@ public:
|
|||||||
explicit Scheduler(GPU& gpu_);
|
explicit Scheduler(GPU& gpu_);
|
||||||
~Scheduler();
|
~Scheduler();
|
||||||
|
|
||||||
|
void Init();
|
||||||
|
|
||||||
|
void Resume();
|
||||||
|
|
||||||
|
void Yield();
|
||||||
|
|
||||||
void Push(s32 channel, CommandList&& entries);
|
void Push(s32 channel, CommandList&& entries);
|
||||||
|
|
||||||
void DeclareChannel(std::shared_ptr<ChannelState> new_channel);
|
void DeclareChannel(std::shared_ptr<ChannelState> new_channel);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void ChannelLoop(size_t gpfifo_id, s32 channel_id);
|
||||||
|
|
||||||
std::unordered_map<s32, std::shared_ptr<ChannelState>> channels;
|
std::unordered_map<s32, std::shared_ptr<ChannelState>> channels;
|
||||||
|
std::unordered_map<s32, size_t> channel_gpfifo_ids;
|
||||||
std::mutex scheduling_guard;
|
std::mutex scheduling_guard;
|
||||||
|
std::shared_ptr<Common::Fiber> master_control;
|
||||||
|
struct GPFifoContext {
|
||||||
|
bool is_active;
|
||||||
|
std::shared_ptr<Common::Fiber> context;
|
||||||
|
std::deque<CommandList> pending_work;
|
||||||
|
std::atomic<bool> working{};
|
||||||
|
std::mutex guard;
|
||||||
|
s32 bind_id;
|
||||||
|
};
|
||||||
|
std::deque<GPFifoContext> gpfifos;
|
||||||
|
std::deque<size_t> free_fifos;
|
||||||
GPU& gpu;
|
GPU& gpu;
|
||||||
|
size_t current_fifo_rotation_id{};
|
||||||
|
GPFifoContext* current_fifo{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Control
|
} // namespace Control
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "video_core/control/channel_state.h"
|
#include "video_core/control/channel_state.h"
|
||||||
|
#include "video_core/control/scheduler.h"
|
||||||
#include "video_core/dma_pusher.h"
|
#include "video_core/dma_pusher.h"
|
||||||
#include "video_core/engines/fermi_2d.h"
|
#include "video_core/engines/fermi_2d.h"
|
||||||
#include "video_core/engines/kepler_compute.h"
|
#include "video_core/engines/kepler_compute.h"
|
||||||
@ -14,6 +15,8 @@
|
|||||||
#include "video_core/engines/maxwell_dma.h"
|
#include "video_core/engines/maxwell_dma.h"
|
||||||
#include "video_core/engines/puller.h"
|
#include "video_core/engines/puller.h"
|
||||||
#include "video_core/gpu.h"
|
#include "video_core/gpu.h"
|
||||||
|
#include "video_core/host1x/host1x.h"
|
||||||
|
#include "video_core/host1x/syncpoint_manager.h"
|
||||||
#include "video_core/memory_manager.h"
|
#include "video_core/memory_manager.h"
|
||||||
#include "video_core/rasterizer_interface.h"
|
#include "video_core/rasterizer_interface.h"
|
||||||
|
|
||||||
@ -60,11 +63,14 @@ void Puller::ProcessBindMethod(const MethodCall& method_call) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Puller::ProcessFenceActionMethod() {
|
void Puller::ProcessFenceActionMethod() {
|
||||||
|
auto& syncpoint_manager = gpu.Host1x().GetSyncpointManager();
|
||||||
switch (regs.fence_action.op) {
|
switch (regs.fence_action.op) {
|
||||||
case Puller::FenceOperation::Acquire:
|
case Puller::FenceOperation::Acquire:
|
||||||
// UNIMPLEMENTED_MSG("Channel Scheduling pending.");
|
while (regs.fence_value >
|
||||||
// WaitFence(regs.fence_action.syncpoint_id, regs.fence_value);
|
syncpoint_manager.GetGuestSyncpointValue(regs.fence_action.syncpoint_id)) {
|
||||||
rasterizer->ReleaseFences();
|
rasterizer->ReleaseFences();
|
||||||
|
gpu.Scheduler().Yield();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case Puller::FenceOperation::Increment:
|
case Puller::FenceOperation::Increment:
|
||||||
rasterizer->SignalSyncPoint(regs.fence_action.syncpoint_id);
|
rasterizer->SignalSyncPoint(regs.fence_action.syncpoint_id);
|
||||||
|
@ -387,6 +387,14 @@ std::shared_ptr<Control::ChannelState> GPU::AllocateChannel() {
|
|||||||
return impl->AllocateChannel();
|
return impl->AllocateChannel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Tegra::Control::Scheduler& GPU::Scheduler() {
|
||||||
|
return *impl->scheduler;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Tegra::Control::Scheduler& GPU::Scheduler() const {
|
||||||
|
return *impl->scheduler;
|
||||||
|
}
|
||||||
|
|
||||||
void GPU::InitChannel(Control::ChannelState& to_init) {
|
void GPU::InitChannel(Control::ChannelState& to_init) {
|
||||||
impl->InitChannel(to_init);
|
impl->InitChannel(to_init);
|
||||||
}
|
}
|
||||||
|
@ -124,7 +124,8 @@ class KeplerCompute;
|
|||||||
|
|
||||||
namespace Control {
|
namespace Control {
|
||||||
struct ChannelState;
|
struct ChannelState;
|
||||||
}
|
class Scheduler;
|
||||||
|
} // namespace Control
|
||||||
|
|
||||||
namespace Host1x {
|
namespace Host1x {
|
||||||
class Host1x;
|
class Host1x;
|
||||||
@ -204,6 +205,12 @@ public:
|
|||||||
/// Returns a const reference to the shader notifier.
|
/// Returns a const reference to the shader notifier.
|
||||||
[[nodiscard]] const VideoCore::ShaderNotify& ShaderNotify() const;
|
[[nodiscard]] const VideoCore::ShaderNotify& ShaderNotify() const;
|
||||||
|
|
||||||
|
/// Returns GPU Channel Scheduler.
|
||||||
|
[[nodiscard]] Tegra::Control::Scheduler& Scheduler();
|
||||||
|
|
||||||
|
/// Returns GPU Channel Scheduler.
|
||||||
|
[[nodiscard]] const Tegra::Control::Scheduler& Scheduler() const;
|
||||||
|
|
||||||
[[nodiscard]] u64 GetTicks() const;
|
[[nodiscard]] u64 GetTicks() const;
|
||||||
|
|
||||||
[[nodiscard]] bool IsAsync() const;
|
[[nodiscard]] bool IsAsync() const;
|
||||||
|
@ -34,13 +34,15 @@ static void RunThread(std::stop_token stop_token, Core::System& system,
|
|||||||
|
|
||||||
CommandDataContainer next;
|
CommandDataContainer next;
|
||||||
|
|
||||||
|
scheduler.Init();
|
||||||
|
|
||||||
while (!stop_token.stop_requested()) {
|
while (!stop_token.stop_requested()) {
|
||||||
state.queue.PopWait(next, stop_token);
|
state.queue.PopWait(next, stop_token);
|
||||||
if (stop_token.stop_requested()) {
|
if (stop_token.stop_requested()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (auto* submit_list = std::get_if<SubmitListCommand>(&next.data)) {
|
if (std::holds_alternative<SubmitListCommand>(next.data)) {
|
||||||
scheduler.Push(submit_list->channel, std::move(submit_list->entries));
|
scheduler.Resume();
|
||||||
} else if (std::holds_alternative<GPUTickCommand>(next.data)) {
|
} else if (std::holds_alternative<GPUTickCommand>(next.data)) {
|
||||||
system.GPU().TickWork();
|
system.GPU().TickWork();
|
||||||
} else if (const auto* flush = std::get_if<FlushRegionCommand>(&next.data)) {
|
} else if (const auto* flush = std::get_if<FlushRegionCommand>(&next.data)) {
|
||||||
@ -67,14 +69,16 @@ ThreadManager::~ThreadManager() = default;
|
|||||||
|
|
||||||
void ThreadManager::StartThread(VideoCore::RendererBase& renderer,
|
void ThreadManager::StartThread(VideoCore::RendererBase& renderer,
|
||||||
Core::Frontend::GraphicsContext& context,
|
Core::Frontend::GraphicsContext& context,
|
||||||
Tegra::Control::Scheduler& scheduler) {
|
Tegra::Control::Scheduler& scheduler_) {
|
||||||
rasterizer = renderer.ReadRasterizer();
|
rasterizer = renderer.ReadRasterizer();
|
||||||
|
scheduler = &scheduler_;
|
||||||
thread = std::jthread(RunThread, std::ref(system), std::ref(renderer), std::ref(context),
|
thread = std::jthread(RunThread, std::ref(system), std::ref(renderer), std::ref(context),
|
||||||
std::ref(scheduler), std::ref(state));
|
std::ref(scheduler_), std::ref(state));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThreadManager::SubmitList(s32 channel, Tegra::CommandList&& entries) {
|
void ThreadManager::SubmitList(s32 channel, Tegra::CommandList&& entries) {
|
||||||
PushCommand(SubmitListCommand(channel, std::move(entries)));
|
scheduler->Push(channel, std::move(entries));
|
||||||
|
PushCommand(SubmitListCommand());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThreadManager::FlushRegion(DAddr addr, u64 size) {
|
void ThreadManager::FlushRegion(DAddr addr, u64 size) {
|
||||||
|
@ -36,13 +36,7 @@ class RendererBase;
|
|||||||
namespace VideoCommon::GPUThread {
|
namespace VideoCommon::GPUThread {
|
||||||
|
|
||||||
/// Command to signal to the GPU thread that a command list is ready for processing
|
/// Command to signal to the GPU thread that a command list is ready for processing
|
||||||
struct SubmitListCommand final {
|
struct SubmitListCommand final {};
|
||||||
explicit SubmitListCommand(s32 channel_, Tegra::CommandList&& entries_)
|
|
||||||
: channel{channel_}, entries{std::move(entries_)} {}
|
|
||||||
|
|
||||||
s32 channel;
|
|
||||||
Tegra::CommandList entries;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Command to signal to the GPU thread to flush a region
|
/// Command to signal to the GPU thread to flush a region
|
||||||
struct FlushRegionCommand final {
|
struct FlushRegionCommand final {
|
||||||
@ -124,6 +118,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
/// Pushes a command to be executed by the GPU thread
|
/// Pushes a command to be executed by the GPU thread
|
||||||
u64 PushCommand(CommandData&& command_data, bool block = false);
|
u64 PushCommand(CommandData&& command_data, bool block = false);
|
||||||
|
Tegra::Control::Scheduler* scheduler;
|
||||||
|
|
||||||
Core::System& system;
|
Core::System& system;
|
||||||
const bool is_async;
|
const bool is_async;
|
||||||
|
@ -1353,6 +1353,13 @@ void GMainWindow::InitializeHotkeys() {
|
|||||||
LinkActionShortcut(ui->action_TAS_Start, QStringLiteral("TAS Start/Stop"), true);
|
LinkActionShortcut(ui->action_TAS_Start, QStringLiteral("TAS Start/Stop"), true);
|
||||||
LinkActionShortcut(ui->action_TAS_Record, QStringLiteral("TAS Record"), true);
|
LinkActionShortcut(ui->action_TAS_Record, QStringLiteral("TAS Record"), true);
|
||||||
LinkActionShortcut(ui->action_TAS_Reset, QStringLiteral("TAS Reset"), true);
|
LinkActionShortcut(ui->action_TAS_Reset, QStringLiteral("TAS Reset"), true);
|
||||||
|
LinkActionShortcut(ui->action_View_Lobby,
|
||||||
|
QStringLiteral("Multiplayer Browse Public Game Lobby"));
|
||||||
|
LinkActionShortcut(ui->action_Start_Room, QStringLiteral("Multiplayer Create Room"));
|
||||||
|
LinkActionShortcut(ui->action_Connect_To_Room,
|
||||||
|
QStringLiteral("Multiplayer Direct Connect to Room"));
|
||||||
|
LinkActionShortcut(ui->action_Show_Room, QStringLiteral("Multiplayer Show Current Room"));
|
||||||
|
LinkActionShortcut(ui->action_Leave_Room, QStringLiteral("Multiplayer Leave Room"));
|
||||||
|
|
||||||
static const QString main_window = QStringLiteral("Main Window");
|
static const QString main_window = QStringLiteral("Main Window");
|
||||||
const auto connect_shortcut = [&]<typename Fn>(const QString& action_name, const Fn& function) {
|
const auto connect_shortcut = [&]<typename Fn>(const QString& action_name, const Fn& function) {
|
||||||
|
@ -77,16 +77,23 @@ Lobby::Lobby(QWidget* parent, QStandardItemModel* list,
|
|||||||
|
|
||||||
// UI Buttons
|
// UI Buttons
|
||||||
connect(ui->refresh_list, &QPushButton::clicked, this, &Lobby::RefreshLobby);
|
connect(ui->refresh_list, &QPushButton::clicked, this, &Lobby::RefreshLobby);
|
||||||
|
connect(ui->search, &QLineEdit::textChanged, proxy, &LobbyFilterProxyModel::SetFilterSearch);
|
||||||
connect(ui->games_owned, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterOwned);
|
connect(ui->games_owned, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterOwned);
|
||||||
connect(ui->hide_empty, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterEmpty);
|
connect(ui->hide_empty, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterEmpty);
|
||||||
connect(ui->hide_full, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterFull);
|
connect(ui->hide_full, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterFull);
|
||||||
connect(ui->search, &QLineEdit::textChanged, proxy, &LobbyFilterProxyModel::SetFilterSearch);
|
|
||||||
connect(ui->room_list, &QTreeView::doubleClicked, this, &Lobby::OnJoinRoom);
|
connect(ui->room_list, &QTreeView::doubleClicked, this, &Lobby::OnJoinRoom);
|
||||||
connect(ui->room_list, &QTreeView::clicked, this, &Lobby::OnExpandRoom);
|
connect(ui->room_list, &QTreeView::clicked, this, &Lobby::OnExpandRoom);
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
connect(&room_list_watcher, &QFutureWatcher<AnnounceMultiplayerRoom::RoomList>::finished, this,
|
connect(&room_list_watcher, &QFutureWatcher<AnnounceMultiplayerRoom::RoomList>::finished, this,
|
||||||
&Lobby::OnRefreshLobby);
|
&Lobby::OnRefreshLobby);
|
||||||
|
|
||||||
|
// Load persistent filters after events are connected to make sure they apply
|
||||||
|
ui->search->setText(
|
||||||
|
QString::fromStdString(UISettings::values.multiplayer_filter_text.GetValue()));
|
||||||
|
ui->games_owned->setChecked(UISettings::values.multiplayer_filter_games_owned.GetValue());
|
||||||
|
ui->hide_empty->setChecked(UISettings::values.multiplayer_filter_hide_empty.GetValue());
|
||||||
|
ui->hide_full->setChecked(UISettings::values.multiplayer_filter_hide_full.GetValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
Lobby::~Lobby() = default;
|
Lobby::~Lobby() = default;
|
||||||
@ -204,6 +211,10 @@ void Lobby::OnJoinRoom(const QModelIndex& source) {
|
|||||||
|
|
||||||
// Save settings
|
// Save settings
|
||||||
UISettings::values.multiplayer_nickname = ui->nickname->text().toStdString();
|
UISettings::values.multiplayer_nickname = ui->nickname->text().toStdString();
|
||||||
|
UISettings::values.multiplayer_filter_text = ui->search->text().toStdString();
|
||||||
|
UISettings::values.multiplayer_filter_games_owned = ui->games_owned->isChecked();
|
||||||
|
UISettings::values.multiplayer_filter_hide_empty = ui->hide_empty->isChecked();
|
||||||
|
UISettings::values.multiplayer_filter_hide_full = ui->hide_full->isChecked();
|
||||||
UISettings::values.multiplayer_ip =
|
UISettings::values.multiplayer_ip =
|
||||||
proxy->data(connection_index, LobbyItemHost::HostIPRole).value<QString>().toStdString();
|
proxy->data(connection_index, LobbyItemHost::HostIPRole).value<QString>().toStdString();
|
||||||
UISettings::values.multiplayer_port =
|
UISettings::values.multiplayer_port =
|
||||||
|
@ -193,12 +193,29 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
QVariant data(int role) const override {
|
QVariant data(int role) const override {
|
||||||
if (role != Qt::DisplayRole) {
|
switch (role) {
|
||||||
|
case Qt::DisplayRole: {
|
||||||
|
auto members = data(MemberListRole).toList();
|
||||||
|
return QStringLiteral("%1 / %2 ")
|
||||||
|
.arg(QString::number(members.size()), data(MaxPlayerRole).toString());
|
||||||
|
}
|
||||||
|
case Qt::ForegroundRole: {
|
||||||
|
auto members = data(MemberListRole).toList();
|
||||||
|
auto max_players = data(MaxPlayerRole).toInt();
|
||||||
|
if (members.size() >= max_players) {
|
||||||
|
return QBrush(QColor(255, 48, 32));
|
||||||
|
} else if (members.size() == (max_players - 1)) {
|
||||||
|
return QBrush(QColor(255, 140, 32));
|
||||||
|
} else if (members.size() == 0) {
|
||||||
|
return QBrush(QColor(128, 128, 128));
|
||||||
|
}
|
||||||
|
// FIXME: How to return a value that tells Qt not to modify the
|
||||||
|
// text color from the default (as if Qt::ForegroundRole wasn't overridden)?
|
||||||
|
return QBrush(nullptr);
|
||||||
|
}
|
||||||
|
default:
|
||||||
return LobbyItem::data(role);
|
return LobbyItem::data(role);
|
||||||
}
|
}
|
||||||
auto members = data(MemberListRole).toList();
|
|
||||||
return QStringLiteral("%1 / %2 ")
|
|
||||||
.arg(QString::number(members.size()), data(MaxPlayerRole).toString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator<(const QStandardItem& other) const override {
|
bool operator<(const QStandardItem& other) const override {
|
||||||
|
@ -169,6 +169,13 @@ struct Values {
|
|||||||
|
|
||||||
// multiplayer settings
|
// multiplayer settings
|
||||||
Setting<std::string> multiplayer_nickname{linkage, {}, "nickname", Category::Multiplayer};
|
Setting<std::string> multiplayer_nickname{linkage, {}, "nickname", Category::Multiplayer};
|
||||||
|
Setting<std::string> multiplayer_filter_text{linkage, {}, "filter_text", Category::Multiplayer};
|
||||||
|
Setting<bool> multiplayer_filter_games_owned{linkage, false, "filter_games_owned",
|
||||||
|
Category::Multiplayer};
|
||||||
|
Setting<bool> multiplayer_filter_hide_empty{linkage, false, "filter_games_hide_empty",
|
||||||
|
Category::Multiplayer};
|
||||||
|
Setting<bool> multiplayer_filter_hide_full{linkage, false, "filter_games_hide_full",
|
||||||
|
Category::Multiplayer};
|
||||||
Setting<std::string> multiplayer_ip{linkage, {}, "ip", Category::Multiplayer};
|
Setting<std::string> multiplayer_ip{linkage, {}, "ip", Category::Multiplayer};
|
||||||
Setting<u16, true> multiplayer_port{linkage, 24872, 0,
|
Setting<u16, true> multiplayer_port{linkage, 24872, 0,
|
||||||
UINT16_MAX, "port", Category::Multiplayer};
|
UINT16_MAX, "port", Category::Multiplayer};
|
||||||
@ -222,7 +229,7 @@ void RestoreWindowState(std::unique_ptr<QtConfig>& qtConfig);
|
|||||||
// This must be in alphabetical order according to action name as it must have the same order as
|
// This must be in alphabetical order according to action name as it must have the same order as
|
||||||
// UISetting::values.shortcuts, which is alphabetically ordered.
|
// UISetting::values.shortcuts, which is alphabetically ordered.
|
||||||
// clang-format off
|
// clang-format off
|
||||||
const std::array<Shortcut, 23> default_hotkeys{{
|
const std::array<Shortcut, 28> default_hotkeys{{
|
||||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Mute/Unmute")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+M"), std::string("Home+Dpad_Right"), Qt::WindowShortcut, false}},
|
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Mute/Unmute")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+M"), std::string("Home+Dpad_Right"), Qt::WindowShortcut, false}},
|
||||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Down")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("-"), std::string("Home+Dpad_Down"), Qt::ApplicationShortcut, true}},
|
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Down")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("-"), std::string("Home+Dpad_Down"), Qt::ApplicationShortcut, true}},
|
||||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Up")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("="), std::string("Home+Dpad_Up"), Qt::ApplicationShortcut, true}},
|
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Up")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("="), std::string("Home+Dpad_Up"), Qt::ApplicationShortcut, true}},
|
||||||
@ -236,6 +243,11 @@ const std::array<Shortcut, 23> default_hotkeys{{
|
|||||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Fullscreen")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F11"), std::string("Home+B"), Qt::WindowShortcut, false}},
|
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Fullscreen")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F11"), std::string("Home+B"), Qt::WindowShortcut, false}},
|
||||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load File")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+O"), std::string(""), Qt::WidgetWithChildrenShortcut, false}},
|
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load File")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+O"), std::string(""), Qt::WidgetWithChildrenShortcut, false}},
|
||||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load/Remove Amiibo")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F2"), std::string("Home+A"), Qt::WidgetWithChildrenShortcut, false}},
|
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load/Remove Amiibo")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F2"), std::string("Home+A"), Qt::WidgetWithChildrenShortcut, false}},
|
||||||
|
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Multiplayer Browse Public Game Lobby")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+B"), std::string(""), Qt::ApplicationShortcut, false}},
|
||||||
|
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Multiplayer Create Room")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+N"), std::string(""), Qt::ApplicationShortcut, false}},
|
||||||
|
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Multiplayer Direct Connect to Room")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+C"), std::string(""), Qt::ApplicationShortcut, false}},
|
||||||
|
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Multiplayer Leave Room")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+L"), std::string(""), Qt::ApplicationShortcut, false}},
|
||||||
|
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Multiplayer Show Current Room")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+R"), std::string(""), Qt::ApplicationShortcut, false}},
|
||||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Restart Emulation")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F6"), std::string("R+Plus+Minus"), Qt::WindowShortcut, false}},
|
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Restart Emulation")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F6"), std::string("R+Plus+Minus"), Qt::WindowShortcut, false}},
|
||||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Stop Emulation")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F5"), std::string("L+Plus+Minus"), Qt::WindowShortcut, false}},
|
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Stop Emulation")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F5"), std::string("L+Plus+Minus"), Qt::WindowShortcut, false}},
|
||||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Record")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F7"), std::string(""), Qt::ApplicationShortcut, false}},
|
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Record")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F7"), std::string(""), Qt::ApplicationShortcut, false}},
|
||||||
|
Reference in New Issue
Block a user