Compare commits
57
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fc9cd297b1 | ||
|
|
abe9abef3c | ||
|
|
7436e56620 | ||
|
|
4aa8189328 | ||
|
|
e1f7938a3b | ||
|
|
ab102787fa | ||
|
|
8441094ba3 | ||
|
|
0687a8370d | ||
|
|
824e53149d | ||
|
|
9761618a8d | ||
|
|
d3a4a192fe | ||
|
|
3b85ac2ac4 | ||
|
|
4735d18bb9 | ||
|
|
a9d24b0df3 | ||
|
|
5dae45b958 | ||
|
|
827dcad26e | ||
|
|
59173ff7a7 | ||
|
|
87cfe5b1da | ||
|
|
2490ffbbce | ||
|
|
daf5c5060b | ||
|
|
d1a7b2eca7 | ||
|
|
9c4c9f1e7d | ||
|
|
69ce5e41eb | ||
|
|
9d77ae39de | ||
|
|
de21c9e330 | ||
|
|
8f7eb194af | ||
|
|
cd7abba1a9 | ||
|
|
41e94b7b99 | ||
|
|
4bcc5bacff | ||
|
|
68ffac250a | ||
|
|
1f228c51ca | ||
|
|
4cf5b860bd | ||
|
|
47af34003b | ||
|
|
97415ad07a | ||
|
|
7b29a8ce4e | ||
|
|
a5ab85ac37 | ||
|
|
9d010be483 | ||
|
|
34a3ee1631 | ||
|
|
96c9e67b1b | ||
|
|
6faabd6d69 | ||
|
|
e7038344aa | ||
|
|
5213f70230 | ||
|
|
0639244d85 | ||
|
|
b8b5891585 | ||
|
|
394475c4e3 | ||
|
|
50ee9c46ab | ||
|
|
6ab839462c | ||
|
|
f8bfec3109 | ||
|
|
94da1e8a7e | ||
|
|
4a45012f35 | ||
|
|
5ad62e7bfc | ||
|
|
925671071c | ||
|
|
9b3af0027b | ||
|
|
2910aa77b2 | ||
|
|
4738e14cb0 | ||
|
|
51fb0a6f96 | ||
|
|
aae399c1a8 |
No files matched your search
+1
-1
@@ -27,7 +27,7 @@
|
||||
url = https://github.com/ReinUsesLisp/sirit
|
||||
[submodule "mbedtls"]
|
||||
path = externals/mbedtls
|
||||
url = https://github.com/DarkLordZach/mbedtls
|
||||
url = https://github.com/yuzu-emu/mbedtls
|
||||
[submodule "libzip"]
|
||||
path = externals/libzip/libzip
|
||||
url = https://github.com/nih-at/libzip.git
|
||||
|
||||
Vendored
+1
-1
Submodule externals/dynarmic updated: cafa687684...646fd05920.
Vendored
+1
-1
Submodule externals/mbedtls updated: a280e602f3...eac2416b8f.
@@ -27,6 +27,7 @@ if (MSVC)
|
||||
# /Zo - Enhanced debug info for optimized builds
|
||||
# /permissive- - Enables stricter C++ standards conformance checks
|
||||
# /EHsc - C++-only exception handling semantics
|
||||
# /utf-8 - Set source and execution character sets to UTF-8
|
||||
# /volatile:iso - Use strict standards-compliant volatile semantics.
|
||||
# /Zc:externConstexpr - Allow extern constexpr variables to have external linkage, like the standard mandates
|
||||
# /Zc:inline - Let codegen omit inline functions in object files
|
||||
@@ -38,6 +39,7 @@ if (MSVC)
|
||||
/permissive-
|
||||
/EHsc
|
||||
/std:c++latest
|
||||
/utf-8
|
||||
/volatile:iso
|
||||
/Zc:externConstexpr
|
||||
/Zc:inline
|
||||
|
||||
+9
-12
@@ -29,22 +29,19 @@ assert_noinline_call(const Fn& fn) {
|
||||
}
|
||||
|
||||
#define ASSERT(_a_) \
|
||||
do \
|
||||
if (!(_a_)) { \
|
||||
assert_noinline_call([] { LOG_CRITICAL(Debug, "Assertion Failed!"); }); \
|
||||
} \
|
||||
while (0)
|
||||
if (!(_a_)) { \
|
||||
LOG_CRITICAL(Debug, "Assertion Failed!"); \
|
||||
}
|
||||
|
||||
#define ASSERT_MSG(_a_, ...) \
|
||||
do \
|
||||
if (!(_a_)) { \
|
||||
assert_noinline_call([&] { LOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__); }); \
|
||||
} \
|
||||
while (0)
|
||||
if (!(_a_)) { \
|
||||
LOG_CRITICAL(Debug, "Assertion Failed! " __VA_ARGS__); \
|
||||
}
|
||||
|
||||
#define UNREACHABLE() assert_noinline_call([] { LOG_CRITICAL(Debug, "Unreachable code!"); })
|
||||
#define UNREACHABLE() \
|
||||
{ LOG_CRITICAL(Debug, "Unreachable code!"); }
|
||||
#define UNREACHABLE_MSG(...) \
|
||||
assert_noinline_call([&] { LOG_CRITICAL(Debug, "Unreachable code!\n" __VA_ARGS__); })
|
||||
{ LOG_CRITICAL(Debug, "Unreachable code!\n" __VA_ARGS__); }
|
||||
|
||||
#ifdef _DEBUG
|
||||
#define DEBUG_ASSERT(_a_) ASSERT(_a_)
|
||||
|
||||
@@ -52,9 +52,13 @@ __declspec(dllimport) void __stdcall DebugBreak(void);
|
||||
// Generic function to get last error message.
|
||||
// Call directly after the command or use the error num.
|
||||
// This function might change the error code.
|
||||
// Defined in Misc.cpp.
|
||||
// Defined in misc.cpp.
|
||||
[[nodiscard]] std::string GetLastErrorMsg();
|
||||
|
||||
// Like GetLastErrorMsg(), but passing an explicit error code.
|
||||
// Defined in misc.cpp.
|
||||
[[nodiscard]] std::string NativeErrorToString(int e);
|
||||
|
||||
#define DECLARE_ENUM_FLAG_OPERATORS(type) \
|
||||
[[nodiscard]] constexpr type operator|(type a, type b) noexcept { \
|
||||
using T = std::underlying_type_t<type>; \
|
||||
|
||||
+14
-11
@@ -11,7 +11,7 @@
|
||||
|
||||
namespace Common {
|
||||
|
||||
constexpr std::size_t default_stack_size = 256 * 1024;
|
||||
constexpr std::size_t default_stack_size = 512 * 1024;
|
||||
|
||||
struct Fiber::FiberImpl {
|
||||
FiberImpl() : stack{default_stack_size}, rewind_stack{default_stack_size} {}
|
||||
@@ -116,16 +116,19 @@ void Fiber::Rewind() {
|
||||
boost::context::detail::jump_fcontext(impl->rewind_context, this);
|
||||
}
|
||||
|
||||
void Fiber::YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to) {
|
||||
ASSERT_MSG(from != nullptr, "Yielding fiber is null!");
|
||||
ASSERT_MSG(to != nullptr, "Next fiber is null!");
|
||||
to->impl->guard.lock();
|
||||
to->impl->previous_fiber = from;
|
||||
auto transfer = boost::context::detail::jump_fcontext(to->impl->context, to.get());
|
||||
ASSERT(from->impl->previous_fiber != nullptr);
|
||||
from->impl->previous_fiber->impl->context = transfer.fctx;
|
||||
from->impl->previous_fiber->impl->guard.unlock();
|
||||
from->impl->previous_fiber.reset();
|
||||
void Fiber::YieldTo(std::weak_ptr<Fiber> weak_from, Fiber& to) {
|
||||
to.impl->guard.lock();
|
||||
to.impl->previous_fiber = weak_from.lock();
|
||||
|
||||
auto transfer = boost::context::detail::jump_fcontext(to.impl->context, &to);
|
||||
|
||||
// "from" might no longer be valid if the thread was killed
|
||||
if (auto from = weak_from.lock()) {
|
||||
ASSERT(from->impl->previous_fiber != nullptr);
|
||||
from->impl->previous_fiber->impl->context = transfer.fctx;
|
||||
from->impl->previous_fiber->impl->guard.unlock();
|
||||
from->impl->previous_fiber.reset();
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
|
||||
|
||||
+1
-1
@@ -41,7 +41,7 @@ public:
|
||||
|
||||
/// Yields control from Fiber 'from' to Fiber 'to'
|
||||
/// Fiber 'from' must be the currently running fiber.
|
||||
static void YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to);
|
||||
static void YieldTo(std::weak_ptr<Fiber> weak_from, Fiber& to);
|
||||
[[nodiscard]] static std::shared_ptr<Fiber> ThreadToFiber();
|
||||
|
||||
void SetRewindPoint(std::function<void(void*)>&& rewind_func, void* rewind_param);
|
||||
|
||||
+29
-15
@@ -12,27 +12,41 @@
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
|
||||
// Generic function to get last error message.
|
||||
// Call directly after the command or use the error num.
|
||||
// This function might change the error code.
|
||||
std::string GetLastErrorMsg() {
|
||||
static constexpr std::size_t buff_size = 255;
|
||||
char err_str[buff_size];
|
||||
|
||||
std::string NativeErrorToString(int e) {
|
||||
#ifdef _WIN32
|
||||
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, GetLastError(),
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), err_str, buff_size, nullptr);
|
||||
return std::string(err_str, buff_size);
|
||||
#elif defined(__GLIBC__) && (_GNU_SOURCE || (_POSIX_C_SOURCE < 200112L && _XOPEN_SOURCE < 600))
|
||||
LPSTR err_str;
|
||||
|
||||
DWORD res = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
nullptr, e, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
reinterpret_cast<LPSTR>(&err_str), 1, nullptr);
|
||||
if (!res) {
|
||||
return "(FormatMessageA failed to format error)";
|
||||
}
|
||||
std::string ret(err_str);
|
||||
LocalFree(err_str);
|
||||
return ret;
|
||||
#else
|
||||
char err_str[255];
|
||||
#if defined(__GLIBC__) && (_GNU_SOURCE || (_POSIX_C_SOURCE < 200112L && _XOPEN_SOURCE < 600))
|
||||
// Thread safe (GNU-specific)
|
||||
const char* str = strerror_r(errno, err_str, buff_size);
|
||||
const char* str = strerror_r(e, err_str, sizeof(err_str));
|
||||
return std::string(str);
|
||||
#else
|
||||
// Thread safe (XSI-compliant)
|
||||
const int success = strerror_r(errno, err_str, buff_size);
|
||||
if (success != 0) {
|
||||
return {};
|
||||
int second_err = strerror_r(e, err_str, sizeof(err_str));
|
||||
if (second_err != 0) {
|
||||
return "(strerror_r failed to format error)";
|
||||
}
|
||||
return std::string(err_str);
|
||||
#endif // GLIBC etc.
|
||||
#endif // _WIN32
|
||||
}
|
||||
|
||||
std::string GetLastErrorMsg() {
|
||||
#ifdef _WIN32
|
||||
return NativeErrorToString(GetLastError());
|
||||
#else
|
||||
return NativeErrorToString(errno);
|
||||
#endif
|
||||
}
|
||||
+1
-12
@@ -299,28 +299,17 @@ struct System::Impl {
|
||||
gpu_core->WaitIdle();
|
||||
}
|
||||
|
||||
// Shutdown emulation session
|
||||
services.reset();
|
||||
service_manager.reset();
|
||||
cheat_engine.reset();
|
||||
telemetry_session.reset();
|
||||
|
||||
// Close all CPU/threading state
|
||||
cpu_manager.Shutdown();
|
||||
|
||||
// Release the Time Manager's resources
|
||||
time_manager.Shutdown();
|
||||
|
||||
// Shutdown kernel and core timing
|
||||
core_timing.Shutdown();
|
||||
kernel.Shutdown();
|
||||
|
||||
// Close app loader
|
||||
app_loader.reset();
|
||||
gpu_core.reset();
|
||||
perf_stats.reset();
|
||||
|
||||
// Clear all applets
|
||||
kernel.Shutdown();
|
||||
applet_manager.ClearAll();
|
||||
|
||||
LOG_DEBUG(Core, "Shutdown OK");
|
||||
|
||||
@@ -148,7 +148,7 @@ void CpuManager::MultiCoreRunSuspendThread() {
|
||||
auto core = kernel.GetCurrentHostThreadID();
|
||||
auto& scheduler = *kernel.CurrentScheduler();
|
||||
Kernel::KThread* current_thread = scheduler.GetCurrentThread();
|
||||
Common::Fiber::YieldTo(current_thread->GetHostContext(), core_data[core].host_context);
|
||||
Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[core].host_context);
|
||||
ASSERT(scheduler.ContextSwitchPending());
|
||||
ASSERT(core == kernel.GetCurrentHostThreadID());
|
||||
scheduler.RescheduleCurrentCore();
|
||||
@@ -245,7 +245,7 @@ void CpuManager::SingleCoreRunSuspendThread() {
|
||||
auto core = kernel.GetCurrentHostThreadID();
|
||||
auto& scheduler = *kernel.CurrentScheduler();
|
||||
Kernel::KThread* current_thread = scheduler.GetCurrentThread();
|
||||
Common::Fiber::YieldTo(current_thread->GetHostContext(), core_data[0].host_context);
|
||||
Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[0].host_context);
|
||||
ASSERT(scheduler.ContextSwitchPending());
|
||||
ASSERT(core == kernel.GetCurrentHostThreadID());
|
||||
scheduler.RescheduleCurrentCore();
|
||||
@@ -271,7 +271,7 @@ void CpuManager::PreemptSingleCore(bool from_running_enviroment) {
|
||||
scheduler.Unload(scheduler.GetCurrentThread());
|
||||
|
||||
auto& next_scheduler = kernel.Scheduler(current_core);
|
||||
Common::Fiber::YieldTo(current_thread->GetHostContext(), next_scheduler.ControlContext());
|
||||
Common::Fiber::YieldTo(current_thread->GetHostContext(), *next_scheduler.ControlContext());
|
||||
}
|
||||
|
||||
// May have changed scheduler
|
||||
@@ -363,7 +363,7 @@ void CpuManager::RunThread(std::size_t core) {
|
||||
|
||||
auto current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread();
|
||||
data.is_running = true;
|
||||
Common::Fiber::YieldTo(data.host_context, current_thread->GetHostContext());
|
||||
Common::Fiber::YieldTo(data.host_context, *current_thread->GetHostContext());
|
||||
data.is_running = false;
|
||||
data.is_paused = true;
|
||||
data.exit_barrier->Wait();
|
||||
|
||||
@@ -105,8 +105,6 @@ void AESCipher<Key, KeySize>::Transcode(const u8* src, std::size_t size, u8* des
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mbedtls_cipher_finish(context, nullptr, nullptr);
|
||||
}
|
||||
|
||||
template <typename Key, std::size_t KeySize>
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace NgWord1Data {
|
||||
constexpr std::size_t NUMBER_WORD_TXT_FILES = 0x10;
|
||||
|
||||
// Should this archive replacement mysteriously not work on a future game, consider updating.
|
||||
constexpr std::array<u8, 4> VERSION_DAT{0x0, 0x0, 0x0, 0x19}; // 5.1.0 System Version
|
||||
constexpr std::array<u8, 4> VERSION_DAT{0x0, 0x0, 0x0, 0x20}; // 11.0.1 System Version
|
||||
|
||||
constexpr std::array<u8, 30> WORD_TXT{
|
||||
0xFE, 0xFF, 0x00, 0x5E, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 0x79, 0x00, 0x62, 0x00,
|
||||
@@ -43,7 +43,7 @@ namespace NgWord2Data {
|
||||
constexpr std::size_t NUMBER_AC_NX_FILES = 0x10;
|
||||
|
||||
// Should this archive replacement mysteriously not work on a future game, consider updating.
|
||||
constexpr std::array<u8, 4> VERSION_DAT{0x0, 0x0, 0x0, 0x15}; // 5.1.0 System Version
|
||||
constexpr std::array<u8, 4> VERSION_DAT{0x0, 0x0, 0x0, 0x1A}; // 11.0.1 System Version
|
||||
|
||||
constexpr std::array<u8, 0x2C> AC_NX_DATA{
|
||||
0x1F, 0x8B, 0x08, 0x08, 0xD5, 0x2C, 0x09, 0x5C, 0x04, 0x00, 0x61, 0x63, 0x72, 0x61, 0x77,
|
||||
|
||||
@@ -14,15 +14,15 @@ namespace SystemVersionData {
|
||||
|
||||
constexpr u8 VERSION_MAJOR = 11;
|
||||
constexpr u8 VERSION_MINOR = 0;
|
||||
constexpr u8 VERSION_MICRO = 0;
|
||||
constexpr u8 VERSION_MICRO = 1;
|
||||
|
||||
constexpr u8 REVISION_MAJOR = 5;
|
||||
constexpr u8 REVISION_MAJOR = 1;
|
||||
constexpr u8 REVISION_MINOR = 0;
|
||||
|
||||
constexpr char PLATFORM_STRING[] = "NX";
|
||||
constexpr char VERSION_HASH[] = "34197eba8810e2edd5e9dfcfbde7b340882e856d";
|
||||
constexpr char DISPLAY_VERSION[] = "11.0.0";
|
||||
constexpr char DISPLAY_TITLE[] = "NintendoSDK Firmware for NX 11.0.0-5.0";
|
||||
constexpr char VERSION_HASH[] = "69103fcb2004dace877094c2f8c29e6113be5dbf";
|
||||
constexpr char DISPLAY_VERSION[] = "11.0.1";
|
||||
constexpr char DISPLAY_TITLE[] = "NintendoSDK Firmware for NX 11.0.1-1.0";
|
||||
|
||||
} // namespace SystemVersionData
|
||||
|
||||
|
||||
@@ -734,7 +734,7 @@ void KScheduler::ScheduleImpl() {
|
||||
}
|
||||
guard.unlock();
|
||||
|
||||
Common::Fiber::YieldTo(*old_context, switch_fiber);
|
||||
Common::Fiber::YieldTo(*old_context, *switch_fiber);
|
||||
/// When a thread wakes up, the scheduler may have changed to other in another core.
|
||||
auto& next_scheduler = *system.Kernel().CurrentScheduler();
|
||||
next_scheduler.SwitchContextStep2();
|
||||
@@ -769,13 +769,8 @@ void KScheduler::SwitchToCurrent() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::shared_ptr<Common::Fiber>* next_context;
|
||||
if (next_thread != nullptr) {
|
||||
next_context = &next_thread->GetHostContext();
|
||||
} else {
|
||||
next_context = &idle_thread->GetHostContext();
|
||||
}
|
||||
Common::Fiber::YieldTo(switch_fiber, *next_context);
|
||||
auto thread = next_thread ? next_thread : idle_thread;
|
||||
Common::Fiber::YieldTo(switch_fiber, *thread->GetHostContext());
|
||||
} while (!is_switch_pending());
|
||||
}
|
||||
}
|
||||
@@ -800,9 +795,9 @@ void KScheduler::Initialize() {
|
||||
std::string name = "Idle Thread Id:" + std::to_string(core_id);
|
||||
std::function<void(void*)> init_func = Core::CpuManager::GetIdleThreadStartFunc();
|
||||
void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
|
||||
auto thread_res = KThread::Create(system, ThreadType::Main, name, 0,
|
||||
KThread::IdleThreadPriority, 0, static_cast<u32>(core_id), 0,
|
||||
nullptr, std::move(init_func), init_func_parameter);
|
||||
auto thread_res = KThread::CreateThread(
|
||||
system, ThreadType::Main, name, 0, KThread::IdleThreadPriority, 0,
|
||||
static_cast<u32>(core_id), 0, nullptr, std::move(init_func), init_func_parameter);
|
||||
idle_thread = thread_res.Unwrap().get();
|
||||
}
|
||||
|
||||
|
||||
@@ -995,22 +995,11 @@ std::shared_ptr<Common::Fiber>& KThread::GetHostContext() {
|
||||
return host_context;
|
||||
}
|
||||
|
||||
ResultVal<std::shared_ptr<KThread>> KThread::Create(Core::System& system, ThreadType type_flags,
|
||||
std::string name, VAddr entry_point,
|
||||
u32 priority, u64 arg, s32 processor_id,
|
||||
VAddr stack_top, Process* owner_process) {
|
||||
std::function<void(void*)> init_func = Core::CpuManager::GetGuestThreadStartFunc();
|
||||
void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
|
||||
return Create(system, type_flags, name, entry_point, priority, arg, processor_id, stack_top,
|
||||
owner_process, std::move(init_func), init_func_parameter);
|
||||
}
|
||||
|
||||
ResultVal<std::shared_ptr<KThread>> KThread::Create(Core::System& system, ThreadType type_flags,
|
||||
std::string name, VAddr entry_point,
|
||||
u32 priority, u64 arg, s32 processor_id,
|
||||
VAddr stack_top, Process* owner_process,
|
||||
std::function<void(void*)>&& thread_start_func,
|
||||
void* thread_start_parameter) {
|
||||
ResultVal<std::shared_ptr<KThread>> KThread::CreateThread(Core::System& system,
|
||||
ThreadType type_flags, std::string name,
|
||||
VAddr entry_point, u32 priority, u64 arg,
|
||||
s32 processor_id, VAddr stack_top,
|
||||
Process* owner_process) {
|
||||
auto& kernel = system.Kernel();
|
||||
|
||||
std::shared_ptr<KThread> thread = std::make_shared<KThread>(kernel);
|
||||
@@ -1027,12 +1016,35 @@ ResultVal<std::shared_ptr<KThread>> KThread::Create(Core::System& system, Thread
|
||||
auto& scheduler = kernel.GlobalSchedulerContext();
|
||||
scheduler.AddThread(thread);
|
||||
|
||||
thread->host_context =
|
||||
std::make_shared<Common::Fiber>(std::move(thread_start_func), thread_start_parameter);
|
||||
|
||||
return MakeResult<std::shared_ptr<KThread>>(std::move(thread));
|
||||
}
|
||||
|
||||
ResultVal<std::shared_ptr<KThread>> KThread::CreateThread(
|
||||
Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point, u32 priority,
|
||||
u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process,
|
||||
std::function<void(void*)>&& thread_start_func, void* thread_start_parameter) {
|
||||
auto thread_result = CreateThread(system, type_flags, name, entry_point, priority, arg,
|
||||
processor_id, stack_top, owner_process);
|
||||
|
||||
if (thread_result.Succeeded()) {
|
||||
(*thread_result)->host_context =
|
||||
std::make_shared<Common::Fiber>(std::move(thread_start_func), thread_start_parameter);
|
||||
}
|
||||
|
||||
return thread_result;
|
||||
}
|
||||
|
||||
ResultVal<std::shared_ptr<KThread>> KThread::CreateUserThread(
|
||||
Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point, u32 priority,
|
||||
u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process) {
|
||||
std::function<void(void*)> init_func = Core::CpuManager::GetGuestThreadStartFunc();
|
||||
|
||||
void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
|
||||
|
||||
return CreateThread(system, type_flags, name, entry_point, priority, arg, processor_id,
|
||||
stack_top, owner_process, std::move(init_func), init_func_parameter);
|
||||
}
|
||||
|
||||
KThread* GetCurrentThreadPointer(KernelCore& kernel) {
|
||||
return kernel.GetCurrentEmuThread();
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ public:
|
||||
using WaiterList = boost::intrusive::list<KThread>;
|
||||
|
||||
/**
|
||||
* Creates and returns a new thread. The new thread is immediately scheduled
|
||||
* Creates and returns a new thread.
|
||||
* @param system The instance of the whole system
|
||||
* @param name The friendly name desired for the thread
|
||||
* @param entry_point The address at which the thread should start execution
|
||||
@@ -127,12 +127,12 @@ public:
|
||||
* @param owner_process The parent process for the thread, if null, it's a kernel thread
|
||||
* @return A shared pointer to the newly created thread
|
||||
*/
|
||||
[[nodiscard]] static ResultVal<std::shared_ptr<KThread>> Create(
|
||||
[[nodiscard]] static ResultVal<std::shared_ptr<KThread>> CreateThread(
|
||||
Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point,
|
||||
u32 priority, u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process);
|
||||
|
||||
/**
|
||||
* Creates and returns a new thread. The new thread is immediately scheduled
|
||||
* Creates and returns a new thread, with a specified entry point.
|
||||
* @param system The instance of the whole system
|
||||
* @param name The friendly name desired for the thread
|
||||
* @param entry_point The address at which the thread should start execution
|
||||
@@ -145,11 +145,27 @@ public:
|
||||
* @param thread_start_parameter The parameter which will passed to host context on init
|
||||
* @return A shared pointer to the newly created thread
|
||||
*/
|
||||
[[nodiscard]] static ResultVal<std::shared_ptr<KThread>> Create(
|
||||
[[nodiscard]] static ResultVal<std::shared_ptr<KThread>> CreateThread(
|
||||
Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point,
|
||||
u32 priority, u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process,
|
||||
std::function<void(void*)>&& thread_start_func, void* thread_start_parameter);
|
||||
|
||||
/**
|
||||
* Creates and returns a new thread for the emulated "user" process.
|
||||
* @param system The instance of the whole system
|
||||
* @param name The friendly name desired for the thread
|
||||
* @param entry_point The address at which the thread should start execution
|
||||
* @param priority The thread's priority
|
||||
* @param arg User data to pass to the thread
|
||||
* @param processor_id The ID(s) of the processors on which the thread is desired to be run
|
||||
* @param stack_top The address of the thread's stack top
|
||||
* @param owner_process The parent process for the thread, if null, it's a kernel thread
|
||||
* @return A shared pointer to the newly created thread
|
||||
*/
|
||||
[[nodiscard]] static ResultVal<std::shared_ptr<KThread>> CreateUserThread(
|
||||
Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point,
|
||||
u32 priority, u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process);
|
||||
|
||||
[[nodiscard]] std::string GetName() const override {
|
||||
return name;
|
||||
}
|
||||
|
||||
@@ -181,9 +181,9 @@ struct KernelCore::Impl {
|
||||
std::string name = "Suspend Thread Id:" + std::to_string(i);
|
||||
std::function<void(void*)> init_func = Core::CpuManager::GetSuspendThreadStartFunc();
|
||||
void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
|
||||
auto thread_res = KThread::Create(system, ThreadType::HighPriority, std::move(name), 0,
|
||||
0, 0, static_cast<u32>(i), 0, nullptr,
|
||||
std::move(init_func), init_func_parameter);
|
||||
auto thread_res = KThread::CreateThread(
|
||||
system, ThreadType::HighPriority, std::move(name), 0, 0, 0, static_cast<u32>(i), 0,
|
||||
nullptr, std::move(init_func), init_func_parameter);
|
||||
|
||||
suspend_threads[i] = std::move(thread_res).Unwrap();
|
||||
}
|
||||
@@ -221,10 +221,9 @@ struct KernelCore::Impl {
|
||||
// Gets the dummy KThread for the caller, allocating a new one if this is the first time
|
||||
KThread* GetHostDummyThread() {
|
||||
const thread_local auto thread =
|
||||
KThread::Create(
|
||||
KThread::CreateThread(
|
||||
system, ThreadType::Main, fmt::format("DummyThread:{}", GetHostThreadId()), 0,
|
||||
KThread::DefaultThreadPriority, 0, static_cast<u32>(3), 0, nullptr,
|
||||
[]([[maybe_unused]] void* arg) { UNREACHABLE(); }, nullptr)
|
||||
KThread::DefaultThreadPriority, 0, static_cast<u32>(3), 0, nullptr)
|
||||
.Unwrap();
|
||||
return thread.get();
|
||||
}
|
||||
|
||||
@@ -40,8 +40,9 @@ namespace {
|
||||
void SetupMainThread(Core::System& system, Process& owner_process, u32 priority, VAddr stack_top) {
|
||||
const VAddr entry_point = owner_process.PageTable().GetCodeRegionStart();
|
||||
ASSERT(owner_process.GetResourceLimit()->Reserve(LimitableResource::Threads, 1));
|
||||
auto thread_res = KThread::Create(system, ThreadType::User, "main", entry_point, priority, 0,
|
||||
owner_process.GetIdealCoreId(), stack_top, &owner_process);
|
||||
auto thread_res =
|
||||
KThread::CreateUserThread(system, ThreadType::User, "main", entry_point, priority, 0,
|
||||
owner_process.GetIdealCoreId(), stack_top, &owner_process);
|
||||
|
||||
std::shared_ptr<KThread> thread = std::move(thread_res).Unwrap();
|
||||
|
||||
|
||||
@@ -1532,8 +1532,9 @@ static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr e
|
||||
std::shared_ptr<KThread> thread;
|
||||
{
|
||||
KScopedLightLock lk{process.GetStateLock()};
|
||||
CASCADE_RESULT(thread, KThread::Create(system, ThreadType::User, "", entry_point, priority,
|
||||
arg, core_id, stack_bottom, &process));
|
||||
CASCADE_RESULT(thread,
|
||||
KThread::CreateUserThread(system, ThreadType::User, "", entry_point,
|
||||
priority, arg, core_id, stack_bottom, &process));
|
||||
}
|
||||
|
||||
const auto new_thread_handle = process.GetHandleTable().Create(thread);
|
||||
|
||||
@@ -36,6 +36,7 @@ NvResult nvhost_nvdec::Ioctl1(Ioctl command, const std::vector<u8>& input,
|
||||
LOG_INFO(Service_NVDRV, "NVDEC video stream ended");
|
||||
Tegra::ChCommandHeaderList cmdlist{{0xDEADB33F}};
|
||||
system.GPU().PushCommandBuffer(cmdlist);
|
||||
system.GPU().MemoryManager().InvalidateQueuedCaches();
|
||||
}
|
||||
return UnmapBuffer(input, output);
|
||||
}
|
||||
|
||||
@@ -193,7 +193,13 @@ NvResult nvhost_nvdec_common::UnmapBuffer(const std::vector<u8>& input, std::vec
|
||||
return NvResult::InvalidState;
|
||||
}
|
||||
if (const auto size{RemoveBufferMap(object->dma_map_addr)}; size) {
|
||||
gpu.MemoryManager().Unmap(object->dma_map_addr, *size);
|
||||
if (vic_device) {
|
||||
// UnmapVicFrame defers texture_cache invalidation of the frame address until
|
||||
// the stream is over
|
||||
gpu.MemoryManager().UnmapVicFrame(object->dma_map_addr, *size);
|
||||
} else {
|
||||
gpu.MemoryManager().Unmap(object->dma_map_addr, *size);
|
||||
}
|
||||
} else {
|
||||
// This occurs quite frequently, however does not seem to impact functionality
|
||||
LOG_DEBUG(Service_NVDRV, "invalid offset=0x{:X} dma=0x{:X}", object->addr,
|
||||
|
||||
@@ -160,6 +160,7 @@ protected:
|
||||
|
||||
s32_le nvmap_fd{};
|
||||
u32_le submit_timeout{};
|
||||
bool vic_device{};
|
||||
std::shared_ptr<nvmap> nvmap_dev;
|
||||
SyncpointManager& syncpoint_manager;
|
||||
std::array<u32, MaxSyncPoints> device_syncpoints{};
|
||||
|
||||
@@ -12,8 +12,9 @@
|
||||
namespace Service::Nvidia::Devices {
|
||||
nvhost_vic::nvhost_vic(Core::System& system, std::shared_ptr<nvmap> nvmap_dev,
|
||||
SyncpointManager& syncpoint_manager)
|
||||
: nvhost_nvdec_common(system, std::move(nvmap_dev), syncpoint_manager) {}
|
||||
|
||||
: nvhost_nvdec_common(system, std::move(nvmap_dev), syncpoint_manager) {
|
||||
vic_device = true;
|
||||
}
|
||||
nvhost_vic::~nvhost_vic() = default;
|
||||
|
||||
NvResult nvhost_vic::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
|
||||
@@ -42,7 +42,9 @@ void BSD::PollWork::Execute(BSD* bsd) {
|
||||
}
|
||||
|
||||
void BSD::PollWork::Response(Kernel::HLERequestContext& ctx) {
|
||||
ctx.WriteBuffer(write_buffer);
|
||||
if (write_buffer.size() > 0) {
|
||||
ctx.WriteBuffer(write_buffer);
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -55,7 +57,9 @@ void BSD::AcceptWork::Execute(BSD* bsd) {
|
||||
}
|
||||
|
||||
void BSD::AcceptWork::Response(Kernel::HLERequestContext& ctx) {
|
||||
ctx.WriteBuffer(write_buffer);
|
||||
if (write_buffer.size() > 0) {
|
||||
ctx.WriteBuffer(write_buffer);
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 5};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
@@ -140,6 +140,8 @@ ResultCode Module::Interface::GetClockSnapshotFromSystemClockContextInternal(
|
||||
|
||||
const auto current_time_point{
|
||||
time_manager.GetStandardSteadyClockCore().GetCurrentTimePoint(system)};
|
||||
clock_snapshot.steady_clock_time_point = current_time_point;
|
||||
|
||||
if (const ResultCode result{Clock::ClockSnapshot::GetCurrentTime(
|
||||
clock_snapshot.user_time, current_time_point, clock_snapshot.user_context)};
|
||||
result != RESULT_SUCCESS) {
|
||||
@@ -341,12 +343,18 @@ void Module::Interface::CalculateStandardUserSystemClockDifferenceByUser(
|
||||
void Module::Interface::CalculateSpanBetween(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto snapshot_a = rp.PopRaw<Clock::ClockSnapshot>();
|
||||
const auto snapshot_b = rp.PopRaw<Clock::ClockSnapshot>();
|
||||
Clock::ClockSnapshot snapshot_a;
|
||||
Clock::ClockSnapshot snapshot_b;
|
||||
|
||||
const auto snapshot_a_data = ctx.ReadBuffer(0);
|
||||
const auto snapshot_b_data = ctx.ReadBuffer(1);
|
||||
|
||||
std::memcpy(&snapshot_a, snapshot_a_data.data(), sizeof(Clock::ClockSnapshot));
|
||||
std::memcpy(&snapshot_b, snapshot_b_data.data(), sizeof(Clock::ClockSnapshot));
|
||||
|
||||
Clock::TimeSpanType time_span_type{};
|
||||
s64 span{};
|
||||
|
||||
if (const ResultCode result{snapshot_a.steady_clock_time_point.GetSpanBetween(
|
||||
snapshot_b.steady_clock_time_point, span)};
|
||||
result != RESULT_SUCCESS) {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <limits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "common/common_funcs.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define _WINSOCK_DEPRECATED_NO_WARNINGS // gethostname
|
||||
@@ -90,15 +91,36 @@ LINGER MakeLinger(bool enable, u32 linger_value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
int LastError() {
|
||||
return WSAGetLastError();
|
||||
}
|
||||
|
||||
bool EnableNonBlock(SOCKET fd, bool enable) {
|
||||
u_long value = enable ? 1 : 0;
|
||||
return ioctlsocket(fd, FIONBIO, &value) != SOCKET_ERROR;
|
||||
}
|
||||
|
||||
Errno TranslateNativeError(int e) {
|
||||
switch (e) {
|
||||
case WSAEBADF:
|
||||
return Errno::BADF;
|
||||
case WSAEINVAL:
|
||||
return Errno::INVAL;
|
||||
case WSAEMFILE:
|
||||
return Errno::MFILE;
|
||||
case WSAENOTCONN:
|
||||
return Errno::NOTCONN;
|
||||
case WSAEWOULDBLOCK:
|
||||
return Errno::AGAIN;
|
||||
case WSAECONNREFUSED:
|
||||
return Errno::CONNREFUSED;
|
||||
case WSAEHOSTUNREACH:
|
||||
return Errno::HOSTUNREACH;
|
||||
case WSAENETDOWN:
|
||||
return Errno::NETDOWN;
|
||||
case WSAENETUNREACH:
|
||||
return Errno::NETUNREACH;
|
||||
default:
|
||||
return Errno::OTHER;
|
||||
}
|
||||
}
|
||||
|
||||
#elif YUZU_UNIX // ^ _WIN32 v YUZU_UNIX
|
||||
|
||||
using SOCKET = int;
|
||||
@@ -108,9 +130,6 @@ using ULONG = u64;
|
||||
constexpr SOCKET INVALID_SOCKET = -1;
|
||||
constexpr SOCKET SOCKET_ERROR = -1;
|
||||
|
||||
constexpr int WSAEWOULDBLOCK = EAGAIN;
|
||||
constexpr int WSAENOTCONN = ENOTCONN;
|
||||
|
||||
constexpr int SD_RECEIVE = SHUT_RD;
|
||||
constexpr int SD_SEND = SHUT_WR;
|
||||
constexpr int SD_BOTH = SHUT_RDWR;
|
||||
@@ -162,10 +181,6 @@ linger MakeLinger(bool enable, u32 linger_value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
int LastError() {
|
||||
return errno;
|
||||
}
|
||||
|
||||
bool EnableNonBlock(int fd, bool enable) {
|
||||
int flags = fcntl(fd, F_GETFD);
|
||||
if (flags == -1) {
|
||||
@@ -179,8 +194,43 @@ bool EnableNonBlock(int fd, bool enable) {
|
||||
return fcntl(fd, F_SETFD, flags) == 0;
|
||||
}
|
||||
|
||||
Errno TranslateNativeError(int e) {
|
||||
switch (e) {
|
||||
case EBADF:
|
||||
return Errno::BADF;
|
||||
case EINVAL:
|
||||
return Errno::INVAL;
|
||||
case EMFILE:
|
||||
return Errno::MFILE;
|
||||
case ENOTCONN:
|
||||
return Errno::NOTCONN;
|
||||
case EAGAIN:
|
||||
return Errno::AGAIN;
|
||||
case ECONNREFUSED:
|
||||
return Errno::CONNREFUSED;
|
||||
case EHOSTUNREACH:
|
||||
return Errno::HOSTUNREACH;
|
||||
case ENETDOWN:
|
||||
return Errno::NETDOWN;
|
||||
case ENETUNREACH:
|
||||
return Errno::NETUNREACH;
|
||||
default:
|
||||
return Errno::OTHER;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
Errno GetAndLogLastError() {
|
||||
#ifdef _WIN32
|
||||
int e = WSAGetLastError();
|
||||
#else
|
||||
int e = errno;
|
||||
#endif
|
||||
LOG_ERROR(Network, "Socket operation error: {}", NativeErrorToString(e));
|
||||
return TranslateNativeError(e);
|
||||
}
|
||||
|
||||
int TranslateDomain(Domain domain) {
|
||||
switch (domain) {
|
||||
case Domain::INET:
|
||||
@@ -290,9 +340,7 @@ Errno SetSockOpt(SOCKET fd, int option, T value) {
|
||||
if (result != SOCKET_ERROR) {
|
||||
return Errno::SUCCESS;
|
||||
}
|
||||
const int ec = LastError();
|
||||
UNREACHABLE_MSG("Unhandled host socket error={}", ec);
|
||||
return Errno::SUCCESS;
|
||||
return GetAndLogLastError();
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
@@ -308,14 +356,12 @@ NetworkInstance::~NetworkInstance() {
|
||||
std::pair<IPv4Address, Errno> GetHostIPv4Address() {
|
||||
std::array<char, 256> name{};
|
||||
if (gethostname(name.data(), static_cast<int>(name.size()) - 1) == SOCKET_ERROR) {
|
||||
UNIMPLEMENTED_MSG("Unhandled gethostname error");
|
||||
return {IPv4Address{}, Errno::SUCCESS};
|
||||
return {IPv4Address{}, GetAndLogLastError()};
|
||||
}
|
||||
|
||||
hostent* const ent = gethostbyname(name.data());
|
||||
if (!ent) {
|
||||
UNIMPLEMENTED_MSG("Unhandled gethostbyname error");
|
||||
return {IPv4Address{}, Errno::SUCCESS};
|
||||
return {IPv4Address{}, GetAndLogLastError()};
|
||||
}
|
||||
if (ent->h_addr_list == nullptr) {
|
||||
UNIMPLEMENTED_MSG("No addr provided in hostent->h_addr_list");
|
||||
@@ -359,9 +405,7 @@ std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) {
|
||||
|
||||
ASSERT(result == SOCKET_ERROR);
|
||||
|
||||
const int ec = LastError();
|
||||
UNREACHABLE_MSG("Unhandled host socket error={}", ec);
|
||||
return {-1, Errno::SUCCESS};
|
||||
return {-1, GetAndLogLastError()};
|
||||
}
|
||||
|
||||
Socket::~Socket() {
|
||||
@@ -380,9 +424,7 @@ Errno Socket::Initialize(Domain domain, Type type, Protocol protocol) {
|
||||
return Errno::SUCCESS;
|
||||
}
|
||||
|
||||
const int ec = LastError();
|
||||
UNREACHABLE_MSG("Unhandled host socket error={}", ec);
|
||||
return Errno::SUCCESS;
|
||||
return GetAndLogLastError();
|
||||
}
|
||||
|
||||
std::pair<Socket::AcceptResult, Errno> Socket::Accept() {
|
||||
@@ -391,9 +433,7 @@ std::pair<Socket::AcceptResult, Errno> Socket::Accept() {
|
||||
const SOCKET new_socket = accept(fd, &addr, &addrlen);
|
||||
|
||||
if (new_socket == INVALID_SOCKET) {
|
||||
const int ec = LastError();
|
||||
UNREACHABLE_MSG("Unhandled host socket error={}", ec);
|
||||
return {AcceptResult{}, Errno::SUCCESS};
|
||||
return {AcceptResult{}, GetAndLogLastError()};
|
||||
}
|
||||
|
||||
AcceptResult result;
|
||||
@@ -412,23 +452,14 @@ Errno Socket::Connect(SockAddrIn addr_in) {
|
||||
return Errno::SUCCESS;
|
||||
}
|
||||
|
||||
switch (const int ec = LastError()) {
|
||||
case WSAEWOULDBLOCK:
|
||||
LOG_DEBUG(Service, "EAGAIN generated");
|
||||
return Errno::AGAIN;
|
||||
default:
|
||||
UNREACHABLE_MSG("Unhandled host socket error={}", ec);
|
||||
return Errno::SUCCESS;
|
||||
}
|
||||
return GetAndLogLastError();
|
||||
}
|
||||
|
||||
std::pair<SockAddrIn, Errno> Socket::GetPeerName() {
|
||||
sockaddr addr;
|
||||
socklen_t addrlen = sizeof(addr);
|
||||
if (getpeername(fd, &addr, &addrlen) == SOCKET_ERROR) {
|
||||
const int ec = LastError();
|
||||
UNREACHABLE_MSG("Unhandled host socket error={}", ec);
|
||||
return {SockAddrIn{}, Errno::SUCCESS};
|
||||
return {SockAddrIn{}, GetAndLogLastError()};
|
||||
}
|
||||
|
||||
ASSERT(addrlen == sizeof(sockaddr_in));
|
||||
@@ -439,9 +470,7 @@ std::pair<SockAddrIn, Errno> Socket::GetSockName() {
|
||||
sockaddr addr;
|
||||
socklen_t addrlen = sizeof(addr);
|
||||
if (getsockname(fd, &addr, &addrlen) == SOCKET_ERROR) {
|
||||
const int ec = LastError();
|
||||
UNREACHABLE_MSG("Unhandled host socket error={}", ec);
|
||||
return {SockAddrIn{}, Errno::SUCCESS};
|
||||
return {SockAddrIn{}, GetAndLogLastError()};
|
||||
}
|
||||
|
||||
ASSERT(addrlen == sizeof(sockaddr_in));
|
||||
@@ -454,9 +483,7 @@ Errno Socket::Bind(SockAddrIn addr) {
|
||||
return Errno::SUCCESS;
|
||||
}
|
||||
|
||||
const int ec = LastError();
|
||||
UNREACHABLE_MSG("Unhandled host socket error={}", ec);
|
||||
return Errno::SUCCESS;
|
||||
return GetAndLogLastError();
|
||||
}
|
||||
|
||||
Errno Socket::Listen(s32 backlog) {
|
||||
@@ -464,9 +491,7 @@ Errno Socket::Listen(s32 backlog) {
|
||||
return Errno::SUCCESS;
|
||||
}
|
||||
|
||||
const int ec = LastError();
|
||||
UNREACHABLE_MSG("Unhandled host socket error={}", ec);
|
||||
return Errno::SUCCESS;
|
||||
return GetAndLogLastError();
|
||||
}
|
||||
|
||||
Errno Socket::Shutdown(ShutdownHow how) {
|
||||
@@ -489,14 +514,7 @@ Errno Socket::Shutdown(ShutdownHow how) {
|
||||
return Errno::SUCCESS;
|
||||
}
|
||||
|
||||
switch (const int ec = LastError()) {
|
||||
case WSAENOTCONN:
|
||||
LOG_ERROR(Service, "ENOTCONN generated");
|
||||
return Errno::NOTCONN;
|
||||
default:
|
||||
UNREACHABLE_MSG("Unhandled host socket error={}", ec);
|
||||
return Errno::SUCCESS;
|
||||
}
|
||||
return GetAndLogLastError();
|
||||
}
|
||||
|
||||
std::pair<s32, Errno> Socket::Recv(int flags, std::vector<u8>& message) {
|
||||
@@ -509,17 +527,7 @@ std::pair<s32, Errno> Socket::Recv(int flags, std::vector<u8>& message) {
|
||||
return {static_cast<s32>(result), Errno::SUCCESS};
|
||||
}
|
||||
|
||||
switch (const int ec = LastError()) {
|
||||
case WSAEWOULDBLOCK:
|
||||
LOG_DEBUG(Service, "EAGAIN generated");
|
||||
return {-1, Errno::AGAIN};
|
||||
case WSAENOTCONN:
|
||||
LOG_ERROR(Service, "ENOTCONN generated");
|
||||
return {-1, Errno::NOTCONN};
|
||||
default:
|
||||
UNREACHABLE_MSG("Unhandled host socket error={}", ec);
|
||||
return {0, Errno::SUCCESS};
|
||||
}
|
||||
return {-1, GetAndLogLastError()};
|
||||
}
|
||||
|
||||
std::pair<s32, Errno> Socket::RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) {
|
||||
@@ -541,17 +549,7 @@ std::pair<s32, Errno> Socket::RecvFrom(int flags, std::vector<u8>& message, Sock
|
||||
return {static_cast<s32>(result), Errno::SUCCESS};
|
||||
}
|
||||
|
||||
switch (const int ec = LastError()) {
|
||||
case WSAEWOULDBLOCK:
|
||||
LOG_DEBUG(Service, "EAGAIN generated");
|
||||
return {-1, Errno::AGAIN};
|
||||
case WSAENOTCONN:
|
||||
LOG_ERROR(Service, "ENOTCONN generated");
|
||||
return {-1, Errno::NOTCONN};
|
||||
default:
|
||||
UNREACHABLE_MSG("Unhandled host socket error={}", ec);
|
||||
return {-1, Errno::SUCCESS};
|
||||
}
|
||||
return {-1, GetAndLogLastError()};
|
||||
}
|
||||
|
||||
std::pair<s32, Errno> Socket::Send(const std::vector<u8>& message, int flags) {
|
||||
@@ -564,18 +562,7 @@ std::pair<s32, Errno> Socket::Send(const std::vector<u8>& message, int flags) {
|
||||
return {static_cast<s32>(result), Errno::SUCCESS};
|
||||
}
|
||||
|
||||
const int ec = LastError();
|
||||
switch (ec) {
|
||||
case WSAEWOULDBLOCK:
|
||||
LOG_DEBUG(Service, "EAGAIN generated");
|
||||
return {-1, Errno::AGAIN};
|
||||
case WSAENOTCONN:
|
||||
LOG_ERROR(Service, "ENOTCONN generated");
|
||||
return {-1, Errno::NOTCONN};
|
||||
default:
|
||||
UNREACHABLE_MSG("Unhandled host socket error={}", ec);
|
||||
return {-1, Errno::SUCCESS};
|
||||
}
|
||||
return {-1, GetAndLogLastError()};
|
||||
}
|
||||
|
||||
std::pair<s32, Errno> Socket::SendTo(u32 flags, const std::vector<u8>& message,
|
||||
@@ -597,9 +584,7 @@ std::pair<s32, Errno> Socket::SendTo(u32 flags, const std::vector<u8>& message,
|
||||
return {static_cast<s32>(result), Errno::SUCCESS};
|
||||
}
|
||||
|
||||
const int ec = LastError();
|
||||
UNREACHABLE_MSG("Unhandled host socket error={}", ec);
|
||||
return {-1, Errno::SUCCESS};
|
||||
return {-1, GetAndLogLastError()};
|
||||
}
|
||||
|
||||
Errno Socket::Close() {
|
||||
@@ -642,9 +627,7 @@ Errno Socket::SetNonBlock(bool enable) {
|
||||
if (EnableNonBlock(fd, enable)) {
|
||||
return Errno::SUCCESS;
|
||||
}
|
||||
const int ec = LastError();
|
||||
UNREACHABLE_MSG("Unhandled host socket error={}", ec);
|
||||
return Errno::SUCCESS;
|
||||
return GetAndLogLastError();
|
||||
}
|
||||
|
||||
bool Socket::IsOpened() const {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <array>
|
||||
#include <utility>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Network {
|
||||
@@ -21,6 +22,11 @@ enum class Errno {
|
||||
MFILE,
|
||||
NOTCONN,
|
||||
AGAIN,
|
||||
CONNREFUSED,
|
||||
HOSTUNREACH,
|
||||
NETDOWN,
|
||||
NETUNREACH,
|
||||
OTHER,
|
||||
};
|
||||
|
||||
/// Address families
|
||||
|
||||
@@ -12,20 +12,39 @@ namespace InputCommon {
|
||||
|
||||
class KeyButton final : public Input::ButtonDevice {
|
||||
public:
|
||||
explicit KeyButton(std::shared_ptr<KeyButtonList> key_button_list_)
|
||||
: key_button_list(std::move(key_button_list_)) {}
|
||||
explicit KeyButton(std::shared_ptr<KeyButtonList> key_button_list_, bool toggle_)
|
||||
: key_button_list(std::move(key_button_list_)), toggle(toggle_) {}
|
||||
|
||||
~KeyButton() override;
|
||||
|
||||
bool GetStatus() const override {
|
||||
if (toggle) {
|
||||
return toggled_status.load(std::memory_order_relaxed);
|
||||
}
|
||||
return status.load();
|
||||
}
|
||||
|
||||
void ToggleButton() {
|
||||
if (lock) {
|
||||
return;
|
||||
}
|
||||
lock = true;
|
||||
const bool old_toggle_status = toggled_status.load();
|
||||
toggled_status.store(!old_toggle_status);
|
||||
}
|
||||
|
||||
void UnlockButton() {
|
||||
lock = false;
|
||||
}
|
||||
|
||||
friend class KeyButtonList;
|
||||
|
||||
private:
|
||||
std::shared_ptr<KeyButtonList> key_button_list;
|
||||
std::atomic<bool> status{false};
|
||||
std::atomic<bool> toggled_status{false};
|
||||
bool lock{false};
|
||||
const bool toggle;
|
||||
};
|
||||
|
||||
struct KeyButtonPair {
|
||||
@@ -51,6 +70,11 @@ public:
|
||||
for (const KeyButtonPair& pair : list) {
|
||||
if (pair.key_code == key_code) {
|
||||
pair.key_button->status.store(pressed);
|
||||
if (pressed) {
|
||||
pair.key_button->ToggleButton();
|
||||
} else {
|
||||
pair.key_button->UnlockButton();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -75,7 +99,8 @@ KeyButton::~KeyButton() {
|
||||
|
||||
std::unique_ptr<Input::ButtonDevice> Keyboard::Create(const Common::ParamPackage& params) {
|
||||
const int key_code = params.Get("code", 0);
|
||||
std::unique_ptr<KeyButton> button = std::make_unique<KeyButton>(key_button_list);
|
||||
const bool toggle = params.Get("toggle", false);
|
||||
std::unique_ptr<KeyButton> button = std::make_unique<KeyButton>(key_button_list, toggle);
|
||||
key_button_list->AddKeyButton(key_code, button.get());
|
||||
return button;
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ void Mouse::UpdateYuzuSettings() {
|
||||
});
|
||||
}
|
||||
|
||||
void Mouse::PressButton(int x, int y, int button_) {
|
||||
void Mouse::PressButton(int x, int y, MouseButton button_) {
|
||||
const auto button_index = static_cast<std::size_t>(button_);
|
||||
if (button_index >= mouse_info.size()) {
|
||||
return;
|
||||
@@ -67,7 +67,7 @@ void Mouse::PressButton(int x, int y, int button_) {
|
||||
|
||||
const auto button = 1U << button_index;
|
||||
buttons |= static_cast<u16>(button);
|
||||
last_button = static_cast<MouseButton>(button_index);
|
||||
last_button = button_;
|
||||
|
||||
mouse_info[button_index].mouse_origin = Common::MakeVec(x, y);
|
||||
mouse_info[button_index].last_mouse_position = Common::MakeVec(x, y);
|
||||
@@ -129,7 +129,7 @@ void Mouse::MouseMove(int x, int y, int center_x, int center_y) {
|
||||
}
|
||||
}
|
||||
|
||||
void Mouse::ReleaseButton(int button_) {
|
||||
void Mouse::ReleaseButton(MouseButton button_) {
|
||||
const auto button_index = static_cast<std::size_t>(button_);
|
||||
if (button_index >= mouse_info.size()) {
|
||||
return;
|
||||
@@ -152,11 +152,52 @@ void Mouse::BeginConfiguration() {
|
||||
|
||||
void Mouse::EndConfiguration() {
|
||||
buttons = 0;
|
||||
for (MouseInfo& info : mouse_info) {
|
||||
info.tilt_speed = 0;
|
||||
info.data.pressed = false;
|
||||
info.data.axis = {0, 0};
|
||||
}
|
||||
last_button = MouseButton::Undefined;
|
||||
mouse_queue.Clear();
|
||||
configuring = false;
|
||||
}
|
||||
|
||||
bool Mouse::ToggleButton(std::size_t button_) {
|
||||
if (button_ >= mouse_info.size()) {
|
||||
return false;
|
||||
}
|
||||
const auto button = 1U << button_;
|
||||
const bool button_state = (toggle_buttons & button) != 0;
|
||||
const bool button_lock = (lock_buttons & button) != 0;
|
||||
|
||||
if (button_lock) {
|
||||
return button_state;
|
||||
}
|
||||
|
||||
lock_buttons |= static_cast<u16>(button);
|
||||
|
||||
if (button_state) {
|
||||
toggle_buttons &= static_cast<u16>(0xFF - button);
|
||||
} else {
|
||||
toggle_buttons |= static_cast<u16>(button);
|
||||
}
|
||||
|
||||
return !button_state;
|
||||
}
|
||||
|
||||
bool Mouse::UnlockButton(std::size_t button_) {
|
||||
if (button_ >= mouse_info.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto button = 1U << button_;
|
||||
const bool button_state = (toggle_buttons & button) != 0;
|
||||
|
||||
lock_buttons &= static_cast<u16>(0xFF - button);
|
||||
|
||||
return button_state;
|
||||
}
|
||||
|
||||
Common::SPSCQueue<MouseStatus>& Mouse::GetMouseQueue() {
|
||||
return mouse_queue;
|
||||
}
|
||||
|
||||
@@ -18,10 +18,12 @@ namespace MouseInput {
|
||||
|
||||
enum class MouseButton {
|
||||
Left,
|
||||
Wheel,
|
||||
Right,
|
||||
Forward,
|
||||
Wheel,
|
||||
Backward,
|
||||
Forward,
|
||||
Task,
|
||||
Extra,
|
||||
Undefined,
|
||||
};
|
||||
|
||||
@@ -51,7 +53,7 @@ public:
|
||||
* @param y the y-coordinate of the cursor
|
||||
* @param button_ the button pressed
|
||||
*/
|
||||
void PressButton(int x, int y, int button_);
|
||||
void PressButton(int x, int y, MouseButton button_);
|
||||
|
||||
/**
|
||||
* Signals that mouse has moved.
|
||||
@@ -65,7 +67,10 @@ public:
|
||||
/**
|
||||
* Signals that a motion sensor tilt has ended.
|
||||
*/
|
||||
void ReleaseButton(int button_);
|
||||
void ReleaseButton(MouseButton button_);
|
||||
|
||||
[[nodiscard]] bool ToggleButton(std::size_t button_);
|
||||
[[nodiscard]] bool UnlockButton(std::size_t button_);
|
||||
|
||||
[[nodiscard]] Common::SPSCQueue<MouseStatus>& GetMouseQueue();
|
||||
[[nodiscard]] const Common::SPSCQueue<MouseStatus>& GetMouseQueue() const;
|
||||
@@ -92,9 +97,11 @@ private:
|
||||
};
|
||||
|
||||
u16 buttons{};
|
||||
u16 toggle_buttons{};
|
||||
u16 lock_buttons{};
|
||||
std::thread update_thread;
|
||||
MouseButton last_button{MouseButton::Undefined};
|
||||
std::array<MouseInfo, 5> mouse_info;
|
||||
std::array<MouseInfo, 7> mouse_info;
|
||||
Common::SPSCQueue<MouseStatus> mouse_queue;
|
||||
bool configuring{false};
|
||||
bool update_thread_running{true};
|
||||
|
||||
@@ -14,16 +14,25 @@ namespace InputCommon {
|
||||
|
||||
class MouseButton final : public Input::ButtonDevice {
|
||||
public:
|
||||
explicit MouseButton(u32 button_, const MouseInput::Mouse* mouse_input_)
|
||||
: button(button_), mouse_input(mouse_input_) {}
|
||||
explicit MouseButton(u32 button_, bool toggle_, MouseInput::Mouse* mouse_input_)
|
||||
: button(button_), toggle(toggle_), mouse_input(mouse_input_) {}
|
||||
|
||||
bool GetStatus() const override {
|
||||
return mouse_input->GetMouseState(button).pressed;
|
||||
const bool button_state = mouse_input->GetMouseState(button).pressed;
|
||||
if (!toggle) {
|
||||
return button_state;
|
||||
}
|
||||
|
||||
if (button_state) {
|
||||
return mouse_input->ToggleButton(button);
|
||||
}
|
||||
return mouse_input->UnlockButton(button);
|
||||
}
|
||||
|
||||
private:
|
||||
const u32 button;
|
||||
const MouseInput::Mouse* mouse_input;
|
||||
const bool toggle;
|
||||
MouseInput::Mouse* mouse_input;
|
||||
};
|
||||
|
||||
MouseButtonFactory::MouseButtonFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_)
|
||||
@@ -32,8 +41,9 @@ MouseButtonFactory::MouseButtonFactory(std::shared_ptr<MouseInput::Mouse> mouse_
|
||||
std::unique_ptr<Input::ButtonDevice> MouseButtonFactory::Create(
|
||||
const Common::ParamPackage& params) {
|
||||
const auto button_id = params.Get("button", 0);
|
||||
const auto toggle = params.Get("toggle", false);
|
||||
|
||||
return std::make_unique<MouseButton>(button_id, mouse_input.get());
|
||||
return std::make_unique<MouseButton>(button_id, toggle, mouse_input.get());
|
||||
}
|
||||
|
||||
Common::ParamPackage MouseButtonFactory::GetNextInput() const {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <random>
|
||||
#include <thread>
|
||||
#include <boost/asio.hpp>
|
||||
#include "common/logging/log.h"
|
||||
@@ -26,10 +27,10 @@ class Socket {
|
||||
public:
|
||||
using clock = std::chrono::system_clock;
|
||||
|
||||
explicit Socket(const std::string& host, u16 port, std::size_t pad_index_, u32 client_id_,
|
||||
explicit Socket(const std::string& host, u16 port, std::size_t pad_index_,
|
||||
SocketCallback callback_)
|
||||
: callback(std::move(callback_)), timer(io_service),
|
||||
socket(io_service, udp::endpoint(udp::v4(), 0)), client_id(client_id_),
|
||||
socket(io_service, udp::endpoint(udp::v4(), 0)), client_id(GenerateRandomClientId()),
|
||||
pad_index(pad_index_) {
|
||||
boost::system::error_code ec{};
|
||||
auto ipv4 = boost::asio::ip::make_address_v4(host, ec);
|
||||
@@ -63,6 +64,11 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
u32 GenerateRandomClientId() const {
|
||||
std::random_device device;
|
||||
return device();
|
||||
}
|
||||
|
||||
void HandleReceive(const boost::system::error_code&, std::size_t bytes_transferred) {
|
||||
if (auto type = Response::Validate(receive_buffer.data(), bytes_transferred)) {
|
||||
switch (*type) {
|
||||
@@ -115,7 +121,7 @@ private:
|
||||
boost::asio::basic_waitable_timer<clock> timer;
|
||||
udp::socket socket;
|
||||
|
||||
u32 client_id{};
|
||||
const u32 client_id;
|
||||
std::size_t pad_index{};
|
||||
|
||||
static constexpr std::size_t PORT_INFO_SIZE = sizeof(Message<Request::PortInfo>);
|
||||
@@ -203,7 +209,7 @@ void Client::ReloadSockets() {
|
||||
LOG_ERROR(Input, "Duplicated UDP servers found");
|
||||
continue;
|
||||
}
|
||||
StartCommunication(client++, udp_input_address, udp_input_port, pad, 24872);
|
||||
StartCommunication(client++, udp_input_address, udp_input_port, pad);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -277,7 +283,7 @@ void Client::OnPadData(Response::PadData data, std::size_t client) {
|
||||
}
|
||||
|
||||
void Client::StartCommunication(std::size_t client, const std::string& host, u16 port,
|
||||
std::size_t pad_index, u32 client_id) {
|
||||
std::size_t pad_index) {
|
||||
SocketCallback callback{[this](Response::Version version) { OnVersion(version); },
|
||||
[this](Response::PortInfo info) { OnPortInfo(info); },
|
||||
[this, client](Response::PadData data) { OnPadData(data, client); }};
|
||||
@@ -287,7 +293,7 @@ void Client::StartCommunication(std::size_t client, const std::string& host, u16
|
||||
clients[client].port = port;
|
||||
clients[client].pad_index = pad_index;
|
||||
clients[client].active = 0;
|
||||
clients[client].socket = std::make_unique<Socket>(host, port, pad_index, client_id, callback);
|
||||
clients[client].socket = std::make_unique<Socket>(host, port, pad_index, callback);
|
||||
clients[client].thread = std::thread{SocketLoop, clients[client].socket.get()};
|
||||
// Set motion parameters
|
||||
// SetGyroThreshold value should be dependent on GyroscopeZeroDriftMode
|
||||
@@ -416,7 +422,7 @@ const Common::SPSCQueue<UDPPadStatus>& Client::GetPadQueue() const {
|
||||
return pad_queue;
|
||||
}
|
||||
|
||||
void TestCommunication(const std::string& host, u16 port, std::size_t pad_index, u32 client_id,
|
||||
void TestCommunication(const std::string& host, u16 port, std::size_t pad_index,
|
||||
const std::function<void()>& success_callback,
|
||||
const std::function<void()>& failure_callback) {
|
||||
std::thread([=] {
|
||||
@@ -426,7 +432,7 @@ void TestCommunication(const std::string& host, u16 port, std::size_t pad_index,
|
||||
.port_info = [](Response::PortInfo) {},
|
||||
.pad_data = [&](Response::PadData) { success_event.Set(); },
|
||||
};
|
||||
Socket socket{host, port, pad_index, client_id, std::move(callback)};
|
||||
Socket socket{host, port, pad_index, std::move(callback)};
|
||||
std::thread worker_thread{SocketLoop, &socket};
|
||||
const bool result = success_event.WaitFor(std::chrono::seconds(5));
|
||||
socket.Stop();
|
||||
@@ -440,7 +446,7 @@ void TestCommunication(const std::string& host, u16 port, std::size_t pad_index,
|
||||
}
|
||||
|
||||
CalibrationConfigurationJob::CalibrationConfigurationJob(
|
||||
const std::string& host, u16 port, std::size_t pad_index, u32 client_id,
|
||||
const std::string& host, u16 port, std::size_t pad_index,
|
||||
std::function<void(Status)> status_callback,
|
||||
std::function<void(u16, u16, u16, u16)> data_callback) {
|
||||
|
||||
@@ -485,7 +491,7 @@ CalibrationConfigurationJob::CalibrationConfigurationJob(
|
||||
complete_event.Set();
|
||||
}
|
||||
}};
|
||||
Socket socket{host, port, pad_index, client_id, std::move(callback)};
|
||||
Socket socket{host, port, pad_index, std::move(callback)};
|
||||
std::thread worker_thread{SocketLoop, &socket};
|
||||
complete_event.Wait();
|
||||
socket.Stop();
|
||||
|
||||
@@ -126,7 +126,7 @@ private:
|
||||
void OnPortInfo(Response::PortInfo);
|
||||
void OnPadData(Response::PadData, std::size_t client);
|
||||
void StartCommunication(std::size_t client, const std::string& host, u16 port,
|
||||
std::size_t pad_index, u32 client_id);
|
||||
std::size_t pad_index);
|
||||
void UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& acc,
|
||||
const Common::Vec3<float>& gyro);
|
||||
|
||||
@@ -165,7 +165,7 @@ public:
|
||||
* @param data_callback Called when calibration data is ready
|
||||
*/
|
||||
explicit CalibrationConfigurationJob(const std::string& host, u16 port, std::size_t pad_index,
|
||||
u32 client_id, std::function<void(Status)> status_callback,
|
||||
std::function<void(Status)> status_callback,
|
||||
std::function<void(u16, u16, u16, u16)> data_callback);
|
||||
~CalibrationConfigurationJob();
|
||||
void Stop();
|
||||
@@ -174,7 +174,7 @@ private:
|
||||
Common::Event complete_event;
|
||||
};
|
||||
|
||||
void TestCommunication(const std::string& host, u16 port, std::size_t pad_index, u32 client_id,
|
||||
void TestCommunication(const std::string& host, u16 port, std::size_t pad_index,
|
||||
const std::function<void()>& success_callback,
|
||||
const std::function<void()>& failure_callback);
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ add_executable(tests
|
||||
common/param_package.cpp
|
||||
common/ring_buffer.cpp
|
||||
core/core_timing.cpp
|
||||
core/network/network.cpp
|
||||
tests.cpp
|
||||
video_core/buffer_base.cpp
|
||||
)
|
||||
|
||||
+14
-14
@@ -67,7 +67,7 @@ void TestControl1::DoWork() {
|
||||
value++;
|
||||
}
|
||||
results[id] = value;
|
||||
Fiber::YieldTo(work_fibers[id], thread_fibers[id]);
|
||||
Fiber::YieldTo(work_fibers[id], *thread_fibers[id]);
|
||||
}
|
||||
|
||||
void TestControl1::ExecuteThread(u32 id) {
|
||||
@@ -76,7 +76,7 @@ void TestControl1::ExecuteThread(u32 id) {
|
||||
thread_fibers[id] = thread_fiber;
|
||||
work_fibers[id] = std::make_shared<Fiber>(std::function<void(void*)>{WorkControl1}, this);
|
||||
items[id] = rand() % 256;
|
||||
Fiber::YieldTo(thread_fibers[id], work_fibers[id]);
|
||||
Fiber::YieldTo(thread_fibers[id], *work_fibers[id]);
|
||||
thread_fibers[id]->Exit();
|
||||
}
|
||||
|
||||
@@ -117,11 +117,11 @@ public:
|
||||
for (u32 i = 0; i < 12000; i++) {
|
||||
value1 += i;
|
||||
}
|
||||
Fiber::YieldTo(fiber1, fiber3);
|
||||
Fiber::YieldTo(fiber1, *fiber3);
|
||||
const u32 id = thread_ids.Get();
|
||||
assert1 = id == 1;
|
||||
value2 += 5000;
|
||||
Fiber::YieldTo(fiber1, thread_fibers[id]);
|
||||
Fiber::YieldTo(fiber1, *thread_fibers[id]);
|
||||
}
|
||||
|
||||
void DoWork2() {
|
||||
@@ -129,7 +129,7 @@ public:
|
||||
;
|
||||
value2 = 2000;
|
||||
trap = false;
|
||||
Fiber::YieldTo(fiber2, fiber1);
|
||||
Fiber::YieldTo(fiber2, *fiber1);
|
||||
assert3 = false;
|
||||
}
|
||||
|
||||
@@ -137,19 +137,19 @@ public:
|
||||
const u32 id = thread_ids.Get();
|
||||
assert2 = id == 0;
|
||||
value1 += 1000;
|
||||
Fiber::YieldTo(fiber3, thread_fibers[id]);
|
||||
Fiber::YieldTo(fiber3, *thread_fibers[id]);
|
||||
}
|
||||
|
||||
void ExecuteThread(u32 id);
|
||||
|
||||
void CallFiber1() {
|
||||
const u32 id = thread_ids.Get();
|
||||
Fiber::YieldTo(thread_fibers[id], fiber1);
|
||||
Fiber::YieldTo(thread_fibers[id], *fiber1);
|
||||
}
|
||||
|
||||
void CallFiber2() {
|
||||
const u32 id = thread_ids.Get();
|
||||
Fiber::YieldTo(thread_fibers[id], fiber2);
|
||||
Fiber::YieldTo(thread_fibers[id], *fiber2);
|
||||
}
|
||||
|
||||
void Exit();
|
||||
@@ -241,23 +241,23 @@ public:
|
||||
|
||||
void DoWork1() {
|
||||
value1 += 1;
|
||||
Fiber::YieldTo(fiber1, fiber2);
|
||||
Fiber::YieldTo(fiber1, *fiber2);
|
||||
const u32 id = thread_ids.Get();
|
||||
value3 += 1;
|
||||
Fiber::YieldTo(fiber1, thread_fibers[id]);
|
||||
Fiber::YieldTo(fiber1, *thread_fibers[id]);
|
||||
}
|
||||
|
||||
void DoWork2() {
|
||||
value2 += 1;
|
||||
const u32 id = thread_ids.Get();
|
||||
Fiber::YieldTo(fiber2, thread_fibers[id]);
|
||||
Fiber::YieldTo(fiber2, *thread_fibers[id]);
|
||||
}
|
||||
|
||||
void ExecuteThread(u32 id);
|
||||
|
||||
void CallFiber1() {
|
||||
const u32 id = thread_ids.Get();
|
||||
Fiber::YieldTo(thread_fibers[id], fiber1);
|
||||
Fiber::YieldTo(thread_fibers[id], *fiber1);
|
||||
}
|
||||
|
||||
void Exit();
|
||||
@@ -332,7 +332,7 @@ public:
|
||||
|
||||
void Execute() {
|
||||
thread_fiber = Fiber::ThreadToFiber();
|
||||
Fiber::YieldTo(thread_fiber, fiber1);
|
||||
Fiber::YieldTo(thread_fiber, *fiber1);
|
||||
thread_fiber->Exit();
|
||||
}
|
||||
|
||||
@@ -340,7 +340,7 @@ public:
|
||||
fiber1->SetRewindPoint(std::function<void(void*)>{WorkControl4}, this);
|
||||
if (rewinded) {
|
||||
goal_reached = true;
|
||||
Fiber::YieldTo(fiber1, thread_fiber);
|
||||
Fiber::YieldTo(fiber1, *thread_fiber);
|
||||
}
|
||||
rewinded = true;
|
||||
fiber1->Rewind();
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <catch2/catch.hpp>
|
||||
|
||||
#include "core/network/network.h"
|
||||
#include "core/network/sockets.h"
|
||||
|
||||
TEST_CASE("Network::Errors", "[core]") {
|
||||
Network::NetworkInstance network_instance; // initialize network
|
||||
|
||||
Network::Socket socks[2];
|
||||
for (Network::Socket& sock : socks) {
|
||||
REQUIRE(sock.Initialize(Network::Domain::INET, Network::Type::STREAM,
|
||||
Network::Protocol::TCP) == Network::Errno::SUCCESS);
|
||||
}
|
||||
|
||||
Network::SockAddrIn addr{
|
||||
Network::Domain::INET,
|
||||
{127, 0, 0, 1},
|
||||
1, // hopefully nobody running this test has something listening on port 1
|
||||
};
|
||||
REQUIRE(socks[0].Connect(addr) == Network::Errno::CONNREFUSED);
|
||||
|
||||
std::vector<u8> message{1, 2, 3, 4};
|
||||
REQUIRE(socks[1].Recv(0, message).second == Network::Errno::NOTCONN);
|
||||
}
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <numeric>
|
||||
#include <span>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
@@ -91,7 +92,7 @@ class BufferCache {
|
||||
};
|
||||
|
||||
public:
|
||||
static constexpr u32 SKIP_CACHE_SIZE = 4096;
|
||||
static constexpr u32 DEFAULT_SKIP_CACHE_SIZE = 4096;
|
||||
|
||||
explicit BufferCache(VideoCore::RasterizerInterface& rasterizer_,
|
||||
Tegra::Engines::Maxwell3D& maxwell3d_,
|
||||
@@ -240,9 +241,9 @@ private:
|
||||
template <bool insert>
|
||||
void ChangeRegister(BufferId buffer_id);
|
||||
|
||||
void SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size);
|
||||
bool SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size);
|
||||
|
||||
void SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 size);
|
||||
bool SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 size);
|
||||
|
||||
void UploadMemory(Buffer& buffer, u64 total_size_bytes, u64 largest_copy,
|
||||
std::span<BufferCopy> copies);
|
||||
@@ -297,6 +298,11 @@ private:
|
||||
|
||||
std::array<u32, NUM_STAGES> fast_bound_uniform_buffers{};
|
||||
|
||||
std::array<u32, 16> uniform_cache_hits{};
|
||||
std::array<u32, 16> uniform_cache_shots{};
|
||||
|
||||
u32 uniform_buffer_skip_cache_size = DEFAULT_SKIP_CACHE_SIZE;
|
||||
|
||||
bool has_deleted_buffers = false;
|
||||
|
||||
std::conditional_t<HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS, std::array<u32, NUM_STAGES>, Empty>
|
||||
@@ -328,6 +334,19 @@ BufferCache<P>::BufferCache(VideoCore::RasterizerInterface& rasterizer_,
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::TickFrame() {
|
||||
// Calculate hits and shots and move hit bits to the right
|
||||
const u32 hits = std::reduce(uniform_cache_hits.begin(), uniform_cache_hits.end());
|
||||
const u32 shots = std::reduce(uniform_cache_shots.begin(), uniform_cache_shots.end());
|
||||
std::copy_n(uniform_cache_hits.begin(), uniform_cache_hits.size() - 1,
|
||||
uniform_cache_hits.begin() + 1);
|
||||
std::copy_n(uniform_cache_shots.begin(), uniform_cache_shots.size() - 1,
|
||||
uniform_cache_shots.begin() + 1);
|
||||
uniform_cache_hits[0] = 0;
|
||||
uniform_cache_shots[0] = 0;
|
||||
|
||||
const bool skip_preferred = hits * 256 < shots * 251;
|
||||
uniform_buffer_skip_cache_size = skip_preferred ? DEFAULT_SKIP_CACHE_SIZE : 0;
|
||||
|
||||
delayed_destruction_ring.Tick();
|
||||
}
|
||||
|
||||
@@ -671,7 +690,7 @@ void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32
|
||||
const VAddr cpu_addr = binding.cpu_addr;
|
||||
const u32 size = binding.size;
|
||||
Buffer& buffer = slot_buffers[binding.buffer_id];
|
||||
if (size <= SKIP_CACHE_SIZE && !buffer.IsRegionGpuModified(cpu_addr, size)) {
|
||||
if (size <= uniform_buffer_skip_cache_size && !buffer.IsRegionGpuModified(cpu_addr, size)) {
|
||||
if constexpr (IS_OPENGL) {
|
||||
if (runtime.HasFastBufferSubData()) {
|
||||
// Fast path for Nvidia
|
||||
@@ -692,7 +711,12 @@ void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32
|
||||
return;
|
||||
}
|
||||
// Classic cached path
|
||||
SynchronizeBuffer(buffer, cpu_addr, size);
|
||||
const bool sync_cached = SynchronizeBuffer(buffer, cpu_addr, size);
|
||||
if (sync_cached) {
|
||||
++uniform_cache_hits[0];
|
||||
}
|
||||
++uniform_cache_shots[0];
|
||||
|
||||
if (!needs_bind && !HasFastUniformBufferBound(stage, binding_index)) {
|
||||
// Skip binding if it's not needed and if the bound buffer is not the fast version
|
||||
// This exists to avoid instances where the fast buffer is bound and a GPU write happens
|
||||
@@ -1106,15 +1130,15 @@ void BufferCache<P>::ChangeRegister(BufferId buffer_id) {
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size) {
|
||||
bool BufferCache<P>::SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size) {
|
||||
if (buffer.CpuAddr() == 0) {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
SynchronizeBufferImpl(buffer, cpu_addr, size);
|
||||
return SynchronizeBufferImpl(buffer, cpu_addr, size);
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 size) {
|
||||
bool BufferCache<P>::SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 size) {
|
||||
boost::container::small_vector<BufferCopy, 4> copies;
|
||||
u64 total_size_bytes = 0;
|
||||
u64 largest_copy = 0;
|
||||
@@ -1128,10 +1152,11 @@ void BufferCache<P>::SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 s
|
||||
largest_copy = std::max(largest_copy, range_size);
|
||||
});
|
||||
if (total_size_bytes == 0) {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
const std::span<BufferCopy> copies_span(copies.data(), copies.size());
|
||||
UploadMemory(buffer, total_size_bytes, largest_copy, copies_span);
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class P>
|
||||
|
||||
@@ -48,6 +48,15 @@ constexpr std::array VIEW_CLASS_32_BITS{
|
||||
PixelFormat::A2B10G10R10_UINT,
|
||||
};
|
||||
|
||||
constexpr std::array VIEW_CLASS_32_BITS_NO_BGR{
|
||||
PixelFormat::R16G16_FLOAT, PixelFormat::B10G11R11_FLOAT, PixelFormat::R32_FLOAT,
|
||||
PixelFormat::A2B10G10R10_UNORM, PixelFormat::R16G16_UINT, PixelFormat::R32_UINT,
|
||||
PixelFormat::R16G16_SINT, PixelFormat::R32_SINT, PixelFormat::A8B8G8R8_UNORM,
|
||||
PixelFormat::R16G16_UNORM, PixelFormat::A8B8G8R8_SNORM, PixelFormat::R16G16_SNORM,
|
||||
PixelFormat::A8B8G8R8_SRGB, PixelFormat::E5B9G9R9_FLOAT, PixelFormat::A8B8G8R8_UINT,
|
||||
PixelFormat::A8B8G8R8_SINT, PixelFormat::A2B10G10R10_UINT,
|
||||
};
|
||||
|
||||
// TODO: How should we handle 24 bits?
|
||||
|
||||
constexpr std::array VIEW_CLASS_16_BITS{
|
||||
@@ -205,7 +214,6 @@ constexpr Table MakeViewTable() {
|
||||
EnableRange(view, VIEW_CLASS_128_BITS);
|
||||
EnableRange(view, VIEW_CLASS_96_BITS);
|
||||
EnableRange(view, VIEW_CLASS_64_BITS);
|
||||
EnableRange(view, VIEW_CLASS_32_BITS);
|
||||
EnableRange(view, VIEW_CLASS_16_BITS);
|
||||
EnableRange(view, VIEW_CLASS_8_BITS);
|
||||
EnableRange(view, VIEW_CLASS_RGTC1_RED);
|
||||
@@ -231,20 +239,47 @@ constexpr Table MakeCopyTable() {
|
||||
EnableRange(copy, COPY_CLASS_64_BITS);
|
||||
return copy;
|
||||
}
|
||||
|
||||
constexpr Table MakeNativeBgrViewTable() {
|
||||
Table copy = MakeViewTable();
|
||||
EnableRange(copy, VIEW_CLASS_32_BITS);
|
||||
return copy;
|
||||
}
|
||||
|
||||
constexpr Table MakeNonNativeBgrViewTable() {
|
||||
Table copy = MakeViewTable();
|
||||
EnableRange(copy, VIEW_CLASS_32_BITS_NO_BGR);
|
||||
return copy;
|
||||
}
|
||||
|
||||
constexpr Table MakeNativeBgrCopyTable() {
|
||||
Table copy = MakeCopyTable();
|
||||
EnableRange(copy, VIEW_CLASS_32_BITS);
|
||||
return copy;
|
||||
}
|
||||
|
||||
constexpr Table MakeNonNativeBgrCopyTable() {
|
||||
Table copy = MakeCopyTable();
|
||||
EnableRange(copy, VIEW_CLASS_32_BITS);
|
||||
return copy;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
bool IsViewCompatible(PixelFormat format_a, PixelFormat format_b, bool broken_views) {
|
||||
bool IsViewCompatible(PixelFormat format_a, PixelFormat format_b, bool broken_views,
|
||||
bool native_bgr) {
|
||||
if (broken_views) {
|
||||
// If format views are broken, only accept formats that are identical.
|
||||
return format_a == format_b;
|
||||
}
|
||||
static constexpr Table TABLE = MakeViewTable();
|
||||
return IsSupported(TABLE, format_a, format_b);
|
||||
static constexpr Table BGR_TABLE = MakeNativeBgrViewTable();
|
||||
static constexpr Table NO_BGR_TABLE = MakeNonNativeBgrViewTable();
|
||||
return IsSupported(native_bgr ? BGR_TABLE : NO_BGR_TABLE, format_a, format_b);
|
||||
}
|
||||
|
||||
bool IsCopyCompatible(PixelFormat format_a, PixelFormat format_b) {
|
||||
static constexpr Table TABLE = MakeCopyTable();
|
||||
return IsSupported(TABLE, format_a, format_b);
|
||||
bool IsCopyCompatible(PixelFormat format_a, PixelFormat format_b, bool native_bgr) {
|
||||
static constexpr Table BGR_TABLE = MakeNativeBgrCopyTable();
|
||||
static constexpr Table NO_BGR_TABLE = MakeNonNativeBgrCopyTable();
|
||||
return IsSupported(native_bgr ? BGR_TABLE : NO_BGR_TABLE, format_a, format_b);
|
||||
}
|
||||
|
||||
} // namespace VideoCore::Surface
|
||||
@@ -8,8 +8,9 @@
|
||||
|
||||
namespace VideoCore::Surface {
|
||||
|
||||
bool IsViewCompatible(PixelFormat format_a, PixelFormat format_b, bool broken_views);
|
||||
bool IsViewCompatible(PixelFormat format_a, PixelFormat format_b, bool broken_views,
|
||||
bool native_bgr);
|
||||
|
||||
bool IsCopyCompatible(PixelFormat format_a, PixelFormat format_b);
|
||||
bool IsCopyCompatible(PixelFormat format_a, PixelFormat format_b, bool native_bgr);
|
||||
|
||||
} // namespace VideoCore::Surface
|
||||
@@ -1785,6 +1785,8 @@ public:
|
||||
SSY,
|
||||
SYNC,
|
||||
BRK,
|
||||
CAL,
|
||||
RET,
|
||||
DEPBAR,
|
||||
VOTE,
|
||||
VOTE_VTG,
|
||||
@@ -2108,6 +2110,8 @@ private:
|
||||
INST("1111000011111---", Id::SYNC, Type::Flow, "SYNC"),
|
||||
INST("111000110100----", Id::BRK, Type::Flow, "BRK"),
|
||||
INST("111000110000----", Id::EXIT, Type::Flow, "EXIT"),
|
||||
INST("111000100110----", Id::CAL, Type::Flow, "CAL"),
|
||||
INST("111000110010----", Id::RET, Type::Flow, "RET"),
|
||||
INST("1111000011110---", Id::DEPBAR, Type::Synch, "DEPBAR"),
|
||||
INST("0101000011011---", Id::VOTE, Type::Warp, "VOTE"),
|
||||
INST("0101000011100---", Id::VOTE_VTG, Type::Warp, "VOTE_VTG"),
|
||||
|
||||
@@ -5,6 +5,7 @@ set(SHADER_FILES
|
||||
convert_float_to_depth.frag
|
||||
full_screen_triangle.vert
|
||||
opengl_copy_bc4.comp
|
||||
opengl_copy_bgra.comp
|
||||
opengl_present.frag
|
||||
opengl_present.vert
|
||||
pitch_unswizzle.comp
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#version 430 core
|
||||
|
||||
layout (local_size_x = 4, local_size_y = 4) in;
|
||||
|
||||
layout(binding = 0, rgba8) readonly uniform image2DArray bgr_input;
|
||||
layout(binding = 1, rgba8) writeonly uniform image2DArray bgr_output;
|
||||
|
||||
void main() {
|
||||
vec4 color = imageLoad(bgr_input, ivec3(gl_GlobalInvocationID));
|
||||
imageStore(bgr_output, ivec3(gl_GlobalInvocationID), color.bgra);
|
||||
}
|
||||
@@ -114,6 +114,25 @@ void MemoryManager::TryUnlockPage(PageEntry page_entry, std::size_t size) {
|
||||
.IsSuccess());
|
||||
}
|
||||
|
||||
void MemoryManager::UnmapVicFrame(GPUVAddr gpu_addr, std::size_t size) {
|
||||
if (!size) {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::optional<VAddr> cpu_addr = GpuToCpuAddress(gpu_addr);
|
||||
ASSERT(cpu_addr);
|
||||
rasterizer->InvalidateExceptTextureCache(*cpu_addr, size);
|
||||
cache_invalidate_queue.push_back({*cpu_addr, size});
|
||||
|
||||
UpdateRange(gpu_addr, PageEntry::State::Unmapped, size);
|
||||
}
|
||||
|
||||
void MemoryManager::InvalidateQueuedCaches() {
|
||||
for (const auto& entry : cache_invalidate_queue) {
|
||||
rasterizer->InvalidateTextureCache(entry.first, entry.second);
|
||||
}
|
||||
cache_invalidate_queue.clear();
|
||||
}
|
||||
PageEntry MemoryManager::GetPageEntry(GPUVAddr gpu_addr) const {
|
||||
return page_table[PageEntryIndex(gpu_addr)];
|
||||
}
|
||||
|
||||
@@ -123,6 +123,14 @@ public:
|
||||
[[nodiscard]] GPUVAddr Allocate(std::size_t size, std::size_t align);
|
||||
void Unmap(GPUVAddr gpu_addr, std::size_t size);
|
||||
|
||||
/**
|
||||
* Some Decoded NVDEC frames require that texture cache does not get invalidated.
|
||||
* UnmapVicFrame defers the texture cache invalidation until the stream ends
|
||||
* by invoking InvalidateQueuedCaches to invalidate all frame texture caches.
|
||||
*/
|
||||
void UnmapVicFrame(GPUVAddr gpu_addr, std::size_t size);
|
||||
void InvalidateQueuedCaches();
|
||||
|
||||
private:
|
||||
[[nodiscard]] PageEntry GetPageEntry(GPUVAddr gpu_addr) const;
|
||||
void SetPageEntry(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size = page_size);
|
||||
|
||||
@@ -2,63 +2,43 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include <boost/icl/interval_map.hpp>
|
||||
#include <boost/range/iterator_range.hpp>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/div_ceil.h"
|
||||
#include "core/memory.h"
|
||||
#include "video_core/rasterizer_accelerated.h"
|
||||
|
||||
namespace VideoCore {
|
||||
|
||||
namespace {
|
||||
|
||||
template <typename Map, typename Interval>
|
||||
constexpr auto RangeFromInterval(Map& map, const Interval& interval) {
|
||||
return boost::make_iterator_range(map.equal_range(interval));
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
RasterizerAccelerated::RasterizerAccelerated(Core::Memory::Memory& cpu_memory_)
|
||||
: cpu_memory{cpu_memory_} {}
|
||||
|
||||
RasterizerAccelerated::~RasterizerAccelerated() = default;
|
||||
|
||||
void RasterizerAccelerated::UpdatePagesCachedCount(VAddr addr, u64 size, int delta) {
|
||||
std::lock_guard lock{pages_mutex};
|
||||
const u64 page_start{addr >> Core::Memory::PAGE_BITS};
|
||||
const u64 page_end{(addr + size + Core::Memory::PAGE_SIZE - 1) >> Core::Memory::PAGE_BITS};
|
||||
const auto page_end = Common::DivCeil(addr + size, Core::Memory::PAGE_SIZE);
|
||||
for (auto page = addr >> Core::Memory::PAGE_BITS; page != page_end; ++page) {
|
||||
auto& count = cached_pages.at(page >> 3).Count(page);
|
||||
|
||||
// Interval maps will erase segments if count reaches 0, so if delta is negative we have to
|
||||
// subtract after iterating
|
||||
const auto pages_interval = CachedPageMap::interval_type::right_open(page_start, page_end);
|
||||
if (delta > 0) {
|
||||
cached_pages.add({pages_interval, delta});
|
||||
}
|
||||
|
||||
for (const auto& pair : RangeFromInterval(cached_pages, pages_interval)) {
|
||||
const auto interval = pair.first & pages_interval;
|
||||
const int count = pair.second;
|
||||
|
||||
const VAddr interval_start_addr = boost::icl::first(interval) << Core::Memory::PAGE_BITS;
|
||||
const VAddr interval_end_addr = boost::icl::last_next(interval) << Core::Memory::PAGE_BITS;
|
||||
const u64 interval_size = interval_end_addr - interval_start_addr;
|
||||
|
||||
if (delta > 0 && count == delta) {
|
||||
cpu_memory.RasterizerMarkRegionCached(interval_start_addr, interval_size, true);
|
||||
} else if (delta < 0 && count == -delta) {
|
||||
cpu_memory.RasterizerMarkRegionCached(interval_start_addr, interval_size, false);
|
||||
if (delta > 0) {
|
||||
ASSERT_MSG(count < UINT8_MAX, "Count may overflow!");
|
||||
} else if (delta < 0) {
|
||||
ASSERT_MSG(count > 0, "Count may underflow!");
|
||||
} else {
|
||||
ASSERT(count >= 0);
|
||||
ASSERT_MSG(true, "Delta must be non-zero!");
|
||||
}
|
||||
}
|
||||
|
||||
if (delta < 0) {
|
||||
cached_pages.add({pages_interval, delta});
|
||||
// Adds or subtracts 1, as count is a unsigned 8-bit value
|
||||
count += static_cast<u8>(delta);
|
||||
|
||||
// Assume delta is either -1 or 1
|
||||
if (count == 0) {
|
||||
cpu_memory.RasterizerMarkRegionCached(page << Core::Memory::PAGE_BITS,
|
||||
Core::Memory::PAGE_SIZE, false);
|
||||
} else if (count == 1 && delta > 0) {
|
||||
cpu_memory.RasterizerMarkRegionCached(page << Core::Memory::PAGE_BITS,
|
||||
Core::Memory::PAGE_SIZE, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,9 +4,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include <boost/icl/interval_map.hpp>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/rasterizer_interface.h"
|
||||
@@ -26,10 +25,24 @@ public:
|
||||
void UpdatePagesCachedCount(VAddr addr, u64 size, int delta) override;
|
||||
|
||||
private:
|
||||
using CachedPageMap = boost::icl::interval_map<u64, int>;
|
||||
CachedPageMap cached_pages;
|
||||
std::mutex pages_mutex;
|
||||
class CacheEntry final {
|
||||
public:
|
||||
CacheEntry() = default;
|
||||
|
||||
std::atomic_uint8_t& Count(std::size_t page) {
|
||||
return values[page & 7];
|
||||
}
|
||||
|
||||
const std::atomic_uint8_t& Count(std::size_t page) const {
|
||||
return values[page & 7];
|
||||
}
|
||||
|
||||
private:
|
||||
std::array<std::atomic_uint8_t, 8> values{};
|
||||
};
|
||||
static_assert(sizeof(CacheEntry) == 8, "CacheEntry should be 8 bytes!");
|
||||
|
||||
std::array<CacheEntry, 0x800000> cached_pages;
|
||||
Core::Memory::Memory& cpu_memory;
|
||||
};
|
||||
|
||||
|
||||
@@ -69,6 +69,12 @@ public:
|
||||
/// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
|
||||
virtual void FlushRegion(VAddr addr, u64 size) = 0;
|
||||
|
||||
/// Notify rasterizer to flush the texture cache to Switch memory
|
||||
virtual void InvalidateExceptTextureCache(VAddr addr, u64 size) = 0;
|
||||
|
||||
/// Notify rasterizer to invalidate the texture cache
|
||||
virtual void InvalidateTextureCache(VAddr addr, u64 size) = 0;
|
||||
|
||||
/// Check if the the specified memory area requires flushing to CPU Memory.
|
||||
virtual bool MustFlushRegion(VAddr addr, u64 size) = 0;
|
||||
|
||||
|
||||
@@ -491,6 +491,9 @@ private:
|
||||
const Registry& registry;
|
||||
const ShaderType stage;
|
||||
|
||||
std::shared_ptr<ShaderFunctionIR> context_func;
|
||||
u32 ast_var_base{};
|
||||
|
||||
std::size_t num_temporaries = 0;
|
||||
std::size_t max_temporaries = 0;
|
||||
|
||||
@@ -807,13 +810,33 @@ ARBDecompiler::ARBDecompiler(const Device& device_, const ShaderIR& ir_, const R
|
||||
: device{device_}, ir{ir_}, registry{registry_}, stage{stage_} {
|
||||
DefineGlobalMemory();
|
||||
|
||||
context_func = ir.GetMainFunction();
|
||||
ast_var_base = 0;
|
||||
|
||||
AddLine("TEMP RC;");
|
||||
AddLine("TEMP FSWZA[4];");
|
||||
AddLine("TEMP FSWZB[4];");
|
||||
if (ir.IsDecompiled()) {
|
||||
InitializeVariables();
|
||||
AddLine("main:");
|
||||
if (context_func->IsDecompiled()) {
|
||||
DecompileAST();
|
||||
} else {
|
||||
DecompileBranchMode();
|
||||
AddLine("RET;");
|
||||
}
|
||||
|
||||
const auto& subfunctions = ir.GetSubFunctions();
|
||||
auto it = subfunctions.begin();
|
||||
while (it != subfunctions.end()) {
|
||||
context_func = *it;
|
||||
AddLine("func_{}:", context_func->GetId());
|
||||
if (context_func->IsDecompiled()) {
|
||||
DecompileAST();
|
||||
} else {
|
||||
DecompileBranchMode();
|
||||
AddLine("RET;");
|
||||
}
|
||||
it++;
|
||||
}
|
||||
AddLine("END");
|
||||
|
||||
@@ -1060,41 +1083,38 @@ void ARBDecompiler::InitializeVariables() {
|
||||
}
|
||||
|
||||
void ARBDecompiler::DecompileAST() {
|
||||
const u32 num_flow_variables = ir.GetASTNumVariables();
|
||||
const u32 num_flow_variables = context_func->GetASTNumVariables();
|
||||
for (u32 i = 0; i < num_flow_variables; ++i) {
|
||||
AddLine("TEMP F{};", i);
|
||||
AddLine("TEMP F{};", i + ast_var_base);
|
||||
}
|
||||
for (u32 i = 0; i < num_flow_variables; ++i) {
|
||||
AddLine("MOV.U F{}, {{0, 0, 0, 0}};", i);
|
||||
AddLine("MOV.U F{}, {{0, 0, 0, 0}};", i + ast_var_base);
|
||||
}
|
||||
|
||||
InitializeVariables();
|
||||
|
||||
VisitAST(ir.GetASTProgram());
|
||||
VisitAST(context_func->GetASTProgram());
|
||||
ast_var_base += num_flow_variables;
|
||||
}
|
||||
|
||||
void ARBDecompiler::DecompileBranchMode() {
|
||||
static constexpr u32 FLOW_STACK_SIZE = 20;
|
||||
if (!ir.IsFlowStackDisabled()) {
|
||||
if (!context_func->IsFlowStackDisabled()) {
|
||||
AddLine("TEMP SSY[{}];", FLOW_STACK_SIZE);
|
||||
AddLine("TEMP PBK[{}];", FLOW_STACK_SIZE);
|
||||
AddLine("TEMP SSY_TOP;");
|
||||
AddLine("TEMP PBK_TOP;");
|
||||
}
|
||||
|
||||
AddLine("TEMP PC;");
|
||||
AddLine("TEMP PC{};", context_func->GetId());
|
||||
|
||||
if (!ir.IsFlowStackDisabled()) {
|
||||
if (!context_func->IsFlowStackDisabled()) {
|
||||
AddLine("MOV.U SSY_TOP.x, 0;");
|
||||
AddLine("MOV.U PBK_TOP.x, 0;");
|
||||
}
|
||||
|
||||
InitializeVariables();
|
||||
|
||||
const auto basic_block_end = ir.GetBasicBlocks().end();
|
||||
auto basic_block_it = ir.GetBasicBlocks().begin();
|
||||
const auto basic_block_end = context_func->GetBasicBlocks().end();
|
||||
auto basic_block_it = context_func->GetBasicBlocks().begin();
|
||||
const u32 first_address = basic_block_it->first;
|
||||
AddLine("MOV.U PC.x, {};", first_address);
|
||||
AddLine("MOV.U PC{}.x, {};", context_func->GetId(), first_address);
|
||||
|
||||
AddLine("REP;");
|
||||
|
||||
@@ -1103,7 +1123,7 @@ void ARBDecompiler::DecompileBranchMode() {
|
||||
const auto& [address, bb] = *basic_block_it;
|
||||
++num_blocks;
|
||||
|
||||
AddLine("SEQ.S.CC RC.x, PC.x, {};", address);
|
||||
AddLine("SEQ.S.CC RC.x, PC{}.x, {};", context_func->GetId(), address);
|
||||
AddLine("IF NE.x;");
|
||||
|
||||
VisitBlock(bb);
|
||||
@@ -1114,7 +1134,7 @@ void ARBDecompiler::DecompileBranchMode() {
|
||||
const auto op = std::get_if<OperationNode>(&*bb[bb.size() - 1]);
|
||||
if (!op || op->GetCode() != OperationCode::Branch) {
|
||||
const u32 next_address = basic_block_it->first;
|
||||
AddLine("MOV.U PC.x, {};", next_address);
|
||||
AddLine("MOV.U PC{}.x, {};", context_func->GetId(), next_address);
|
||||
AddLine("CONT;");
|
||||
}
|
||||
}
|
||||
@@ -1152,7 +1172,8 @@ void ARBDecompiler::VisitAST(const ASTNode& node) {
|
||||
} else if (const auto decoded = std::get_if<ASTBlockDecoded>(&*node->GetInnerData())) {
|
||||
VisitBlock(decoded->nodes);
|
||||
} else if (const auto var_set = std::get_if<ASTVarSet>(&*node->GetInnerData())) {
|
||||
AddLine("MOV.U F{}, {};", var_set->index, VisitExpression(var_set->condition));
|
||||
AddLine("MOV.U F{}, {};", var_set->index + ast_var_base,
|
||||
VisitExpression(var_set->condition));
|
||||
ResetTemporaries();
|
||||
} else if (const auto do_while = std::get_if<ASTDoWhile>(&*node->GetInnerData())) {
|
||||
const std::string condition = VisitExpression(do_while->condition);
|
||||
@@ -1172,7 +1193,11 @@ void ARBDecompiler::VisitAST(const ASTNode& node) {
|
||||
ResetTemporaries();
|
||||
}
|
||||
if (ast_return->kills) {
|
||||
AddLine("KIL TR;");
|
||||
if (stage == ShaderType::Fragment) {
|
||||
AddLine("KIL TR;");
|
||||
} else {
|
||||
AddLine("RET;");
|
||||
}
|
||||
} else {
|
||||
Exit();
|
||||
}
|
||||
@@ -1219,7 +1244,7 @@ std::string ARBDecompiler::VisitExpression(const Expr& node) {
|
||||
return Visit(ir.GetConditionCode(expr->cc));
|
||||
}
|
||||
if (const auto expr = std::get_if<ExprVar>(&*node)) {
|
||||
return fmt::format("F{}.x", expr->var_index);
|
||||
return fmt::format("F{}.x", expr->var_index + ast_var_base);
|
||||
}
|
||||
if (const auto expr = std::get_if<ExprBoolean>(&*node)) {
|
||||
return expr->value ? "0xffffffff" : "0";
|
||||
@@ -1406,6 +1431,11 @@ std::string ARBDecompiler::Visit(const Node& node) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (const auto func_call = std::get_if<FunctionCallNode>(&*node)) {
|
||||
AddLine("CAL func_{};", func_call->GetFuncId());
|
||||
return {};
|
||||
}
|
||||
|
||||
if ([[maybe_unused]] const auto cmt = std::get_if<CommentNode>(&*node)) {
|
||||
// Uncommenting this will generate invalid code. GLASM lacks comments.
|
||||
// AddLine("// {}", cmt->GetText());
|
||||
@@ -1479,7 +1509,7 @@ std::string ARBDecompiler::GlobalMemoryPointer(const GmemNode& gmem) {
|
||||
}
|
||||
|
||||
void ARBDecompiler::Exit() {
|
||||
if (stage != ShaderType::Fragment) {
|
||||
if (!context_func->IsMain() || stage != ShaderType::Fragment) {
|
||||
AddLine("RET;");
|
||||
return;
|
||||
}
|
||||
@@ -2021,13 +2051,13 @@ std::string ARBDecompiler::ImageStore(Operation operation) {
|
||||
|
||||
std::string ARBDecompiler::Branch(Operation operation) {
|
||||
const auto target = std::get<ImmediateNode>(*operation[0]);
|
||||
AddLine("MOV.U PC.x, {};", target.GetValue());
|
||||
AddLine("MOV.U PC{}.x, {};", context_func->GetId(), target.GetValue());
|
||||
AddLine("CONT;");
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string ARBDecompiler::BranchIndirect(Operation operation) {
|
||||
AddLine("MOV.U PC.x, {};", Visit(operation[0]));
|
||||
AddLine("MOV.U PC{}.x, {};", context_func->GetId(), Visit(operation[0]));
|
||||
AddLine("CONT;");
|
||||
return {};
|
||||
}
|
||||
@@ -2045,7 +2075,7 @@ std::string ARBDecompiler::PopFlowStack(Operation operation) {
|
||||
const auto stack = std::get<MetaStackClass>(operation.GetMeta());
|
||||
const std::string_view stack_name = StackName(stack);
|
||||
AddLine("SUB.S {}_TOP.x, {}_TOP.x, 1;", stack_name, stack_name);
|
||||
AddLine("MOV.U PC.x, {}[{}_TOP.x].x;", stack_name, stack_name);
|
||||
AddLine("MOV.U PC{}.x, {}[{}_TOP.x].x;", context_func->GetId(), stack_name, stack_name);
|
||||
AddLine("CONT;");
|
||||
return {};
|
||||
}
|
||||
@@ -2056,6 +2086,10 @@ std::string ARBDecompiler::Exit(Operation) {
|
||||
}
|
||||
|
||||
std::string ARBDecompiler::Discard(Operation) {
|
||||
if (stage != ShaderType::Fragment) {
|
||||
AddLine("RET;");
|
||||
return {};
|
||||
}
|
||||
AddLine("KIL TR;");
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -73,7 +73,8 @@ BufferCacheRuntime::BufferCacheRuntime(const Device& device_)
|
||||
for (auto& stage_uniforms : fast_uniforms) {
|
||||
for (OGLBuffer& buffer : stage_uniforms) {
|
||||
buffer.Create();
|
||||
glNamedBufferData(buffer.handle, BufferCache::SKIP_CACHE_SIZE, nullptr, GL_STREAM_DRAW);
|
||||
glNamedBufferData(buffer.handle, BufferCache::DEFAULT_SKIP_CACHE_SIZE, nullptr,
|
||||
GL_STREAM_DRAW);
|
||||
}
|
||||
}
|
||||
for (auto& stage_uniforms : copy_uniforms) {
|
||||
|
||||
@@ -544,6 +544,26 @@ void RasterizerOpenGL::FlushRegion(VAddr addr, u64 size) {
|
||||
query_cache.FlushRegion(addr, size);
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::InvalidateExceptTextureCache(VAddr addr, u64 size) {
|
||||
if (addr == 0 || size == 0) {
|
||||
return;
|
||||
}
|
||||
shader_cache.InvalidateRegion(addr, size);
|
||||
{
|
||||
std::scoped_lock lock{buffer_cache.mutex};
|
||||
buffer_cache.WriteMemory(addr, size);
|
||||
}
|
||||
query_cache.InvalidateRegion(addr, size);
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::InvalidateTextureCache(VAddr addr, u64 size) {
|
||||
if (addr == 0 || size == 0) {
|
||||
return;
|
||||
}
|
||||
std::scoped_lock lock{texture_cache.mutex};
|
||||
texture_cache.UnmapMemory(addr, size);
|
||||
}
|
||||
|
||||
bool RasterizerOpenGL::MustFlushRegion(VAddr addr, u64 size) {
|
||||
std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
|
||||
if (!Settings::IsGPULevelHigh()) {
|
||||
|
||||
@@ -74,6 +74,8 @@ public:
|
||||
void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size) override;
|
||||
void FlushAll() override;
|
||||
void FlushRegion(VAddr addr, u64 size) override;
|
||||
void InvalidateExceptTextureCache(VAddr addr, u64 size) override;
|
||||
void InvalidateTextureCache(VAddr addr, u64 size) override;
|
||||
bool MustFlushRegion(VAddr addr, u64 size) override;
|
||||
void InvalidateRegion(VAddr addr, u64 size) override;
|
||||
void OnCPUWrite(VAddr addr, u64 size) override;
|
||||
|
||||
@@ -435,6 +435,27 @@ public:
|
||||
DeclareCustomVariables();
|
||||
DeclarePhysicalAttributeReader();
|
||||
|
||||
const auto& subfunctions = ir.GetSubFunctions();
|
||||
auto it = subfunctions.rbegin();
|
||||
while (it != subfunctions.rend()) {
|
||||
context_func = *it;
|
||||
code.AddLine("void func_{}() {{", context_func->GetId());
|
||||
++code.scope;
|
||||
|
||||
if (context_func->IsDecompiled()) {
|
||||
DecompileAST();
|
||||
} else {
|
||||
DecompileBranchMode();
|
||||
}
|
||||
|
||||
--code.scope;
|
||||
code.AddLine("}}");
|
||||
|
||||
it++;
|
||||
}
|
||||
|
||||
context_func = ir.GetMainFunction();
|
||||
|
||||
code.AddLine("void main() {{");
|
||||
++code.scope;
|
||||
|
||||
@@ -442,7 +463,7 @@ public:
|
||||
code.AddLine("gl_Position = vec4(0.0f, 0.0f, 0.0f, 1.0f);");
|
||||
}
|
||||
|
||||
if (ir.IsDecompiled()) {
|
||||
if (context_func->IsDecompiled()) {
|
||||
DecompileAST();
|
||||
} else {
|
||||
DecompileBranchMode();
|
||||
@@ -462,13 +483,13 @@ private:
|
||||
|
||||
void DecompileBranchMode() {
|
||||
// VM's program counter
|
||||
const auto first_address = ir.GetBasicBlocks().begin()->first;
|
||||
const auto first_address = context_func->GetBasicBlocks().begin()->first;
|
||||
code.AddLine("uint jmp_to = {}U;", first_address);
|
||||
|
||||
// TODO(Subv): Figure out the actual depth of the flow stack, for now it seems
|
||||
// unlikely that shaders will use 20 nested SSYs and PBKs.
|
||||
constexpr u32 FLOW_STACK_SIZE = 20;
|
||||
if (!ir.IsFlowStackDisabled()) {
|
||||
if (!context_func->IsFlowStackDisabled()) {
|
||||
for (const auto stack : std::array{MetaStackClass::Ssy, MetaStackClass::Pbk}) {
|
||||
code.AddLine("uint {}[{}];", FlowStackName(stack), FLOW_STACK_SIZE);
|
||||
code.AddLine("uint {} = 0U;", FlowStackTopName(stack));
|
||||
@@ -480,7 +501,7 @@ private:
|
||||
|
||||
code.AddLine("switch (jmp_to) {{");
|
||||
|
||||
for (const auto& pair : ir.GetBasicBlocks()) {
|
||||
for (const auto& pair : context_func->GetBasicBlocks()) {
|
||||
const auto& [address, bb] = pair;
|
||||
code.AddLine("case 0x{:X}U: {{", address);
|
||||
++code.scope;
|
||||
@@ -1131,6 +1152,11 @@ private:
|
||||
return {};
|
||||
}
|
||||
|
||||
if (const auto func_call = std::get_if<FunctionCallNode>(&*node)) {
|
||||
code.AddLine("func_{}();", func_call->GetFuncId());
|
||||
return {};
|
||||
}
|
||||
|
||||
if (const auto comment = std::get_if<CommentNode>(&*node)) {
|
||||
code.AddLine("// " + comment->GetText());
|
||||
return {};
|
||||
@@ -2267,7 +2293,9 @@ private:
|
||||
}
|
||||
|
||||
Expression Exit(Operation operation) {
|
||||
PreExit();
|
||||
if (context_func->IsMain()) {
|
||||
PreExit();
|
||||
}
|
||||
code.AddLine("return;");
|
||||
return {};
|
||||
}
|
||||
@@ -2277,7 +2305,11 @@ private:
|
||||
// about unexecuted instructions that may follow this.
|
||||
code.AddLine("if (true) {{");
|
||||
++code.scope;
|
||||
code.AddLine("discard;");
|
||||
if (stage != ShaderType::Fragment) {
|
||||
code.AddLine("return;");
|
||||
} else {
|
||||
code.AddLine("discard;");
|
||||
}
|
||||
--code.scope;
|
||||
code.AddLine("}}");
|
||||
return {};
|
||||
@@ -2388,7 +2420,7 @@ private:
|
||||
}
|
||||
|
||||
Expression Barrier(Operation) {
|
||||
if (!ir.IsDecompiled()) {
|
||||
if (!context_func->IsDecompiled()) {
|
||||
LOG_ERROR(Render_OpenGL, "barrier() used but shader is not decompiled");
|
||||
return {};
|
||||
}
|
||||
@@ -2755,6 +2787,8 @@ private:
|
||||
const Header header;
|
||||
std::unordered_map<u8, VaryingTFB> transform_feedback;
|
||||
|
||||
std::shared_ptr<ShaderFunctionIR> context_func;
|
||||
|
||||
ShaderWriter code;
|
||||
|
||||
std::optional<u32> max_input_vertices;
|
||||
@@ -2902,9 +2936,15 @@ public:
|
||||
decomp.code.scope++;
|
||||
}
|
||||
if (ast.kills) {
|
||||
decomp.code.AddLine("discard;");
|
||||
if (decomp.stage != ShaderType::Fragment) {
|
||||
decomp.code.AddLine("return;");
|
||||
} else {
|
||||
decomp.code.AddLine("discard;");
|
||||
}
|
||||
} else {
|
||||
decomp.PreExit();
|
||||
if (decomp.context_func->IsMain()) {
|
||||
decomp.PreExit();
|
||||
}
|
||||
decomp.code.AddLine("return;");
|
||||
}
|
||||
if (!is_true) {
|
||||
@@ -2937,13 +2977,13 @@ private:
|
||||
};
|
||||
|
||||
void GLSLDecompiler::DecompileAST() {
|
||||
const u32 num_flow_variables = ir.GetASTNumVariables();
|
||||
const u32 num_flow_variables = context_func->GetASTNumVariables();
|
||||
for (u32 i = 0; i < num_flow_variables; i++) {
|
||||
code.AddLine("bool {} = false;", GetFlowVariable(i));
|
||||
}
|
||||
|
||||
ASTDecompiler decompiler{*this};
|
||||
decompiler.Visit(ir.GetASTProgram());
|
||||
decompiler.Visit(context_func->GetASTProgram());
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
@@ -96,7 +96,7 @@ constexpr std::array<FormatTuple, MaxPixelFormat> FORMAT_TABLE = {{
|
||||
{GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT}, // BC6H_UFLOAT
|
||||
{GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT}, // BC6H_SFLOAT
|
||||
{GL_COMPRESSED_RGBA_ASTC_4x4_KHR}, // ASTC_2D_4X4_UNORM
|
||||
{GL_RGBA8, GL_BGRA, GL_UNSIGNED_BYTE}, // B8G8R8A8_UNORM
|
||||
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE}, // B8G8R8A8_UNORM
|
||||
{GL_RGBA32F, GL_RGBA, GL_FLOAT}, // R32G32B32A32_FLOAT
|
||||
{GL_RGBA32I, GL_RGBA_INTEGER, GL_INT}, // R32G32B32A32_SINT
|
||||
{GL_RG32F, GL_RG, GL_FLOAT}, // R32G32_FLOAT
|
||||
@@ -125,7 +125,7 @@ constexpr std::array<FormatTuple, MaxPixelFormat> FORMAT_TABLE = {{
|
||||
{GL_COMPRESSED_RGBA_ASTC_8x8_KHR}, // ASTC_2D_8X8_UNORM
|
||||
{GL_COMPRESSED_RGBA_ASTC_8x5_KHR}, // ASTC_2D_8X5_UNORM
|
||||
{GL_COMPRESSED_RGBA_ASTC_5x4_KHR}, // ASTC_2D_5X4_UNORM
|
||||
{GL_SRGB8_ALPHA8, GL_BGRA, GL_UNSIGNED_BYTE}, // B8G8R8A8_UNORM
|
||||
{GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE}, // B8G8R8A8_SRGB
|
||||
{GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT}, // BC1_RGBA_SRGB
|
||||
{GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT}, // BC2_SRGB
|
||||
{GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT}, // BC3_SRGB
|
||||
@@ -396,6 +396,17 @@ void AttachTexture(GLuint fbo, GLenum attachment, const ImageView* image_view) {
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsPixelFormatBGR(PixelFormat format) {
|
||||
switch (format) {
|
||||
case PixelFormat::B5G6R5_UNORM:
|
||||
case PixelFormat::B8G8R8A8_UNORM:
|
||||
case PixelFormat::B8G8R8A8_SRGB:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
ImageBufferMap::~ImageBufferMap() {
|
||||
@@ -512,6 +523,9 @@ bool TextureCacheRuntime::CanImageBeCopied(const Image& dst, const Image& src) {
|
||||
if (dst.info.type == ImageType::e3D && dst.info.format == PixelFormat::BC4_UNORM) {
|
||||
return false;
|
||||
}
|
||||
if (IsPixelFormatBGR(dst.info.format) || IsPixelFormatBGR(src.info.format)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -520,6 +534,8 @@ void TextureCacheRuntime::EmulateCopyImage(Image& dst, Image& src,
|
||||
if (dst.info.type == ImageType::e3D && dst.info.format == PixelFormat::BC4_UNORM) {
|
||||
ASSERT(src.info.type == ImageType::e3D);
|
||||
util_shaders.CopyBC4(dst, src, copies);
|
||||
} else if (IsPixelFormatBGR(dst.info.format) || IsPixelFormatBGR(src.info.format)) {
|
||||
util_shaders.CopyBGR(dst, src, copies);
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
@@ -86,6 +86,11 @@ public:
|
||||
|
||||
FormatProperties FormatInfo(VideoCommon::ImageType type, GLenum internal_format) const;
|
||||
|
||||
bool HasNativeBgr() const noexcept {
|
||||
// OpenGL does not have native support for the BGR internal format
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HasBrokenTextureViewFormats() const noexcept {
|
||||
return has_broken_texture_view_formats;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "video_core/host_shaders/block_linear_unswizzle_2d_comp.h"
|
||||
#include "video_core/host_shaders/block_linear_unswizzle_3d_comp.h"
|
||||
#include "video_core/host_shaders/opengl_copy_bc4_comp.h"
|
||||
#include "video_core/host_shaders/opengl_copy_bgra_comp.h"
|
||||
#include "video_core/host_shaders/pitch_unswizzle_comp.h"
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_manager.h"
|
||||
@@ -48,6 +49,11 @@ OGLProgram MakeProgram(std::string_view source) {
|
||||
return program;
|
||||
}
|
||||
|
||||
size_t NumPixelsInCopy(const VideoCommon::ImageCopy& copy) {
|
||||
return static_cast<size_t>(copy.extent.width * copy.extent.height *
|
||||
copy.src_subresource.num_layers);
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
UtilShaders::UtilShaders(ProgramManager& program_manager_)
|
||||
@@ -55,6 +61,7 @@ UtilShaders::UtilShaders(ProgramManager& program_manager_)
|
||||
block_linear_unswizzle_2d_program(MakeProgram(BLOCK_LINEAR_UNSWIZZLE_2D_COMP)),
|
||||
block_linear_unswizzle_3d_program(MakeProgram(BLOCK_LINEAR_UNSWIZZLE_3D_COMP)),
|
||||
pitch_unswizzle_program(MakeProgram(PITCH_UNSWIZZLE_COMP)),
|
||||
copy_bgra_program(MakeProgram(OPENGL_COPY_BGRA_COMP)),
|
||||
copy_bc4_program(MakeProgram(OPENGL_COPY_BC4_COMP)) {
|
||||
const auto swizzle_table = Tegra::Texture::MakeSwizzleTable();
|
||||
swizzle_table_buffer.Create();
|
||||
@@ -205,6 +212,43 @@ void UtilShaders::CopyBC4(Image& dst_image, Image& src_image, std::span<const Im
|
||||
program_manager.RestoreGuestCompute();
|
||||
}
|
||||
|
||||
void UtilShaders::CopyBGR(Image& dst_image, Image& src_image,
|
||||
std::span<const VideoCommon::ImageCopy> copies) {
|
||||
static constexpr GLuint BINDING_INPUT_IMAGE = 0;
|
||||
static constexpr GLuint BINDING_OUTPUT_IMAGE = 1;
|
||||
static constexpr VideoCommon::Offset3D zero_offset{0, 0, 0};
|
||||
const u32 bytes_per_block = BytesPerBlock(dst_image.info.format);
|
||||
switch (bytes_per_block) {
|
||||
case 2:
|
||||
// BGR565 copy
|
||||
for (const ImageCopy& copy : copies) {
|
||||
ASSERT(copy.src_offset == zero_offset);
|
||||
ASSERT(copy.dst_offset == zero_offset);
|
||||
bgr_copy_pass.Execute(dst_image, src_image, copy);
|
||||
}
|
||||
break;
|
||||
case 4: {
|
||||
// BGRA8 copy
|
||||
program_manager.BindHostCompute(copy_bgra_program.handle);
|
||||
constexpr GLenum FORMAT = GL_RGBA8;
|
||||
for (const ImageCopy& copy : copies) {
|
||||
ASSERT(copy.src_offset == zero_offset);
|
||||
ASSERT(copy.dst_offset == zero_offset);
|
||||
glBindImageTexture(BINDING_INPUT_IMAGE, src_image.StorageHandle(),
|
||||
copy.src_subresource.base_level, GL_FALSE, 0, GL_READ_ONLY, FORMAT);
|
||||
glBindImageTexture(BINDING_OUTPUT_IMAGE, dst_image.StorageHandle(),
|
||||
copy.dst_subresource.base_level, GL_FALSE, 0, GL_WRITE_ONLY, FORMAT);
|
||||
glDispatchCompute(copy.extent.width, copy.extent.height, copy.extent.depth);
|
||||
}
|
||||
program_manager.RestoreGuestCompute();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
GLenum StoreFormat(u32 bytes_per_block) {
|
||||
switch (bytes_per_block) {
|
||||
case 1:
|
||||
@@ -222,4 +266,36 @@ GLenum StoreFormat(u32 bytes_per_block) {
|
||||
return GL_R8UI;
|
||||
}
|
||||
|
||||
void Bgr565CopyPass::Execute(const Image& dst_image, const Image& src_image,
|
||||
const ImageCopy& copy) {
|
||||
if (CopyBufferCreationNeeded(copy)) {
|
||||
CreateNewCopyBuffer(copy, GL_TEXTURE_2D_ARRAY, GL_RGB565);
|
||||
}
|
||||
// Copy from source to PBO
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
||||
glPixelStorei(GL_PACK_ROW_LENGTH, copy.extent.width);
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, bgr16_pbo.handle);
|
||||
glGetTextureSubImage(src_image.Handle(), 0, 0, 0, 0, copy.extent.width, copy.extent.height,
|
||||
copy.src_subresource.num_layers, GL_RGB, GL_UNSIGNED_SHORT_5_6_5,
|
||||
static_cast<GLsizei>(bgr16_pbo_size), nullptr);
|
||||
|
||||
// Copy from PBO to destination in reverse order
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, copy.extent.width);
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, bgr16_pbo.handle);
|
||||
glTextureSubImage3D(dst_image.Handle(), 0, 0, 0, 0, copy.extent.width, copy.extent.height,
|
||||
copy.dst_subresource.num_layers, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
bool Bgr565CopyPass::CopyBufferCreationNeeded(const ImageCopy& copy) {
|
||||
return bgr16_pbo_size < NumPixelsInCopy(copy) * sizeof(u16);
|
||||
}
|
||||
|
||||
void Bgr565CopyPass::CreateNewCopyBuffer(const ImageCopy& copy, GLenum target, GLuint format) {
|
||||
bgr16_pbo.Create();
|
||||
bgr16_pbo_size = NumPixelsInCopy(copy) * sizeof(u16);
|
||||
glNamedBufferData(bgr16_pbo.handle, bgr16_pbo_size, nullptr, GL_STREAM_COPY);
|
||||
}
|
||||
|
||||
} // namespace OpenGL
|
||||
@@ -19,6 +19,22 @@ class ProgramManager;
|
||||
|
||||
struct ImageBufferMap;
|
||||
|
||||
class Bgr565CopyPass {
|
||||
public:
|
||||
Bgr565CopyPass() = default;
|
||||
~Bgr565CopyPass() = default;
|
||||
|
||||
void Execute(const Image& dst_image, const Image& src_image,
|
||||
const VideoCommon::ImageCopy& copy);
|
||||
|
||||
private:
|
||||
[[nodiscard]] bool CopyBufferCreationNeeded(const VideoCommon::ImageCopy& copy);
|
||||
void CreateNewCopyBuffer(const VideoCommon::ImageCopy& copy, GLenum target, GLuint format);
|
||||
|
||||
OGLBuffer bgr16_pbo;
|
||||
size_t bgr16_pbo_size{};
|
||||
};
|
||||
|
||||
class UtilShaders {
|
||||
public:
|
||||
explicit UtilShaders(ProgramManager& program_manager);
|
||||
@@ -36,6 +52,9 @@ public:
|
||||
void CopyBC4(Image& dst_image, Image& src_image,
|
||||
std::span<const VideoCommon::ImageCopy> copies);
|
||||
|
||||
void CopyBGR(Image& dst_image, Image& src_image,
|
||||
std::span<const VideoCommon::ImageCopy> copies);
|
||||
|
||||
private:
|
||||
ProgramManager& program_manager;
|
||||
|
||||
@@ -44,7 +63,10 @@ private:
|
||||
OGLProgram block_linear_unswizzle_2d_program;
|
||||
OGLProgram block_linear_unswizzle_3d_program;
|
||||
OGLProgram pitch_unswizzle_program;
|
||||
OGLProgram copy_bgra_program;
|
||||
OGLProgram copy_bc4_program;
|
||||
|
||||
Bgr565CopyPass bgr_copy_pass;
|
||||
};
|
||||
|
||||
GLenum StoreFormat(u32 bytes_per_block);
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
constexpr size_t COMMAND_BUFFER_POOL_SIZE = 0x1000;
|
||||
constexpr size_t COMMAND_BUFFER_POOL_SIZE = 4;
|
||||
|
||||
struct CommandPool::Pool {
|
||||
vk::CommandPool handle;
|
||||
|
||||
@@ -490,6 +490,26 @@ void RasterizerVulkan::FlushRegion(VAddr addr, u64 size) {
|
||||
query_cache.FlushRegion(addr, size);
|
||||
}
|
||||
|
||||
void Vulkan::RasterizerVulkan::InvalidateExceptTextureCache(VAddr addr, u64 size) {
|
||||
if (addr == 0 || size == 0) {
|
||||
return;
|
||||
}
|
||||
pipeline_cache.InvalidateRegion(addr, size);
|
||||
{
|
||||
std::scoped_lock lock{buffer_cache.mutex};
|
||||
buffer_cache.WriteMemory(addr, size);
|
||||
}
|
||||
query_cache.InvalidateRegion(addr, size);
|
||||
}
|
||||
|
||||
void Vulkan::RasterizerVulkan::InvalidateTextureCache(VAddr addr, u64 size) {
|
||||
if (addr == 0 || size == 0) {
|
||||
return;
|
||||
}
|
||||
std::scoped_lock lock{texture_cache.mutex};
|
||||
texture_cache.UnmapMemory(addr, size);
|
||||
}
|
||||
|
||||
bool RasterizerVulkan::MustFlushRegion(VAddr addr, u64 size) {
|
||||
std::scoped_lock lock{texture_cache.mutex, buffer_cache.mutex};
|
||||
if (!Settings::IsGPULevelHigh()) {
|
||||
|
||||
@@ -66,6 +66,8 @@ public:
|
||||
void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size) override;
|
||||
void FlushAll() override;
|
||||
void FlushRegion(VAddr addr, u64 size) override;
|
||||
void InvalidateExceptTextureCache(VAddr addr, u64 size) override;
|
||||
void InvalidateTextureCache(VAddr addr, u64 size) override;
|
||||
bool MustFlushRegion(VAddr addr, u64 size) override;
|
||||
void InvalidateRegion(VAddr addr, u64 size) override;
|
||||
void OnCPUWrite(VAddr addr, u64 size) override;
|
||||
|
||||
@@ -406,10 +406,38 @@ private:
|
||||
binding = DeclareStorageTexels(binding);
|
||||
binding = DeclareImages(binding);
|
||||
|
||||
const auto& subfunctions = ir.GetSubFunctions();
|
||||
|
||||
labels.resize(subfunctions.size() + 1);
|
||||
other_functions.resize(subfunctions.size());
|
||||
|
||||
auto it = subfunctions.rbegin();
|
||||
while (it != subfunctions.rend()) {
|
||||
context_func = *it;
|
||||
other_functions[context_func->GetId() - 1] =
|
||||
OpFunction(t_void, {}, TypeFunction(t_void));
|
||||
AddLabel();
|
||||
|
||||
if (context_func->IsDecompiled()) {
|
||||
DeclareFlowVariables();
|
||||
DecompileAST();
|
||||
} else {
|
||||
AllocateLabels();
|
||||
DecompileBranchMode();
|
||||
}
|
||||
|
||||
OpReturn();
|
||||
OpFunctionEnd();
|
||||
|
||||
it++;
|
||||
}
|
||||
|
||||
context_func = ir.GetMainFunction();
|
||||
|
||||
const Id main = OpFunction(t_void, {}, TypeFunction(t_void));
|
||||
AddLabel();
|
||||
|
||||
if (ir.IsDecompiled()) {
|
||||
if (context_func->IsDecompiled()) {
|
||||
DeclareFlowVariables();
|
||||
DecompileAST();
|
||||
} else {
|
||||
@@ -441,16 +469,18 @@ private:
|
||||
void DecompileAST();
|
||||
|
||||
void DecompileBranchMode() {
|
||||
const u32 first_address = ir.GetBasicBlocks().begin()->first;
|
||||
const Id loop_label = OpLabel("loop");
|
||||
const Id merge_label = OpLabel("merge");
|
||||
const u32 first_address = context_func->GetBasicBlocks().begin()->first;
|
||||
const u32 func_id = context_func->GetId();
|
||||
const std::string func_id_msg = std::to_string(func_id);
|
||||
const Id loop_label = OpLabel("loop_" + func_id_msg);
|
||||
const Id merge_label = OpLabel("merge_" + func_id_msg);
|
||||
const Id dummy_label = OpLabel();
|
||||
const Id jump_label = OpLabel();
|
||||
continue_label = OpLabel("continue");
|
||||
continue_label = OpLabel("continue_" + func_id_msg);
|
||||
|
||||
std::vector<Sirit::Literal> literals;
|
||||
std::vector<Id> branch_labels;
|
||||
for (const auto& [literal, label] : labels) {
|
||||
for (const auto& [literal, label] : labels[func_id]) {
|
||||
literals.push_back(literal);
|
||||
branch_labels.push_back(label);
|
||||
}
|
||||
@@ -462,11 +492,11 @@ private:
|
||||
std::tie(ssy_flow_stack, ssy_flow_stack_top) = CreateFlowStack();
|
||||
std::tie(pbk_flow_stack, pbk_flow_stack_top) = CreateFlowStack();
|
||||
|
||||
Name(jmp_to, "jmp_to");
|
||||
Name(ssy_flow_stack, "ssy_flow_stack");
|
||||
Name(ssy_flow_stack_top, "ssy_flow_stack_top");
|
||||
Name(pbk_flow_stack, "pbk_flow_stack");
|
||||
Name(pbk_flow_stack_top, "pbk_flow_stack_top");
|
||||
Name(jmp_to, "jmp_to_" + func_id_msg);
|
||||
Name(ssy_flow_stack, "ssy_flow_stack_" + func_id_msg);
|
||||
Name(ssy_flow_stack_top, "ssy_flow_stack_top_" + func_id_msg);
|
||||
Name(pbk_flow_stack, "pbk_flow_stack_" + func_id_msg);
|
||||
Name(pbk_flow_stack_top, "pbk_flow_stack_top_" + func_id_msg);
|
||||
|
||||
DefinePrologue();
|
||||
|
||||
@@ -484,13 +514,14 @@ private:
|
||||
AddLabel(default_branch);
|
||||
OpReturn();
|
||||
|
||||
for (const auto& [address, bb] : ir.GetBasicBlocks()) {
|
||||
AddLabel(labels.at(address));
|
||||
for (const auto& [address, bb] : context_func->GetBasicBlocks()) {
|
||||
AddLabel(labels[func_id].at(address));
|
||||
|
||||
VisitBasicBlock(bb);
|
||||
|
||||
const auto next_it = labels.lower_bound(address + 1);
|
||||
const Id next_label = next_it != labels.end() ? next_it->second : default_branch;
|
||||
const auto next_it = labels[func_id].lower_bound(address + 1);
|
||||
const Id next_label =
|
||||
next_it != labels[func_id].end() ? next_it->second : default_branch;
|
||||
OpBranch(next_label);
|
||||
}
|
||||
|
||||
@@ -508,9 +539,10 @@ private:
|
||||
static constexpr auto INTERNAL_FLAGS_COUNT = static_cast<std::size_t>(InternalFlag::Amount);
|
||||
|
||||
void AllocateLabels() {
|
||||
for (const auto& pair : ir.GetBasicBlocks()) {
|
||||
const u32 func_id = context_func->GetId();
|
||||
for (const auto& pair : context_func->GetBasicBlocks()) {
|
||||
const u32 address = pair.first;
|
||||
labels.emplace(address, OpLabel(fmt::format("label_0x{:x}", address)));
|
||||
labels[func_id].emplace(address, OpLabel(fmt::format("label_0x{:x}", address)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -589,6 +621,14 @@ private:
|
||||
DeclareOutputVertex();
|
||||
}
|
||||
|
||||
void SafeKill() {
|
||||
if (stage != ShaderType::Fragment) {
|
||||
OpReturn();
|
||||
return;
|
||||
}
|
||||
OpKill();
|
||||
}
|
||||
|
||||
void DeclareFragment() {
|
||||
if (stage != ShaderType::Fragment) {
|
||||
return;
|
||||
@@ -656,7 +696,7 @@ private:
|
||||
}
|
||||
|
||||
void DeclareFlowVariables() {
|
||||
for (u32 i = 0; i < ir.GetASTNumVariables(); i++) {
|
||||
for (u32 i = 0; i < context_func->GetASTNumVariables(); i++) {
|
||||
const Id id = OpVariable(t_prv_bool, spv::StorageClass::Private, v_false);
|
||||
Name(id, fmt::format("flow_var_{}", static_cast<u32>(i)));
|
||||
flow_variables.emplace(i, AddGlobalVariable(id));
|
||||
@@ -1333,6 +1373,12 @@ private:
|
||||
return {};
|
||||
}
|
||||
|
||||
if (const auto func_call = std::get_if<FunctionCallNode>(&*node)) {
|
||||
const u32 func_id = func_call->GetFuncId();
|
||||
OpFunctionCall(t_void, other_functions[func_id - 1]);
|
||||
return {};
|
||||
}
|
||||
|
||||
if (const auto comment = std::get_if<CommentNode>(&*node)) {
|
||||
if (device.HasDebuggingToolAttached()) {
|
||||
// We should insert comments with OpString instead of using named variables
|
||||
@@ -2124,7 +2170,7 @@ private:
|
||||
|
||||
OpBranchConditional(condition, true_label, discard_label);
|
||||
AddLabel(discard_label);
|
||||
OpKill();
|
||||
SafeKill();
|
||||
AddLabel(true_label);
|
||||
}
|
||||
|
||||
@@ -2175,7 +2221,9 @@ private:
|
||||
}
|
||||
|
||||
Expression Exit(Operation operation) {
|
||||
PreExit();
|
||||
if (context_func->IsMain()) {
|
||||
PreExit();
|
||||
}
|
||||
inside_branch = true;
|
||||
if (conditional_branch_set) {
|
||||
OpReturn();
|
||||
@@ -2192,12 +2240,12 @@ private:
|
||||
Expression Discard(Operation operation) {
|
||||
inside_branch = true;
|
||||
if (conditional_branch_set) {
|
||||
OpKill();
|
||||
SafeKill();
|
||||
} else {
|
||||
const Id dummy = OpLabel();
|
||||
OpBranch(dummy);
|
||||
AddLabel(dummy);
|
||||
OpKill();
|
||||
SafeKill();
|
||||
AddLabel();
|
||||
}
|
||||
return {};
|
||||
@@ -2276,7 +2324,7 @@ private:
|
||||
}
|
||||
|
||||
Expression Barrier(Operation) {
|
||||
if (!ir.IsDecompiled()) {
|
||||
if (!context_func->IsDecompiled()) {
|
||||
LOG_ERROR(Render_Vulkan, "OpBarrier used by shader is not decompiled");
|
||||
return {};
|
||||
}
|
||||
@@ -2770,6 +2818,8 @@ private:
|
||||
const Specialization& specialization;
|
||||
std::unordered_map<u8, VaryingTFB> transform_feedback;
|
||||
|
||||
std::shared_ptr<ShaderFunctionIR> context_func;
|
||||
|
||||
const Id t_void = Name(TypeVoid(), "void");
|
||||
|
||||
const Id t_bool = Name(TypeBool(), "bool");
|
||||
@@ -2896,7 +2946,8 @@ private:
|
||||
Id ssy_flow_stack{};
|
||||
Id pbk_flow_stack{};
|
||||
Id continue_label{};
|
||||
std::map<u32, Id> labels;
|
||||
std::vector<std::map<u32, Id>> labels;
|
||||
std::vector<Id> other_functions;
|
||||
|
||||
bool conditional_branch_set{};
|
||||
bool inside_branch{};
|
||||
@@ -3047,9 +3098,11 @@ public:
|
||||
decomp.OpBranchConditional(condition, then_label, endif_label);
|
||||
decomp.AddLabel(then_label);
|
||||
if (ast.kills) {
|
||||
decomp.OpKill();
|
||||
decomp.SafeKill();
|
||||
} else {
|
||||
decomp.PreExit();
|
||||
if (decomp.context_func->IsMain()) {
|
||||
decomp.PreExit();
|
||||
}
|
||||
decomp.OpReturn();
|
||||
}
|
||||
decomp.AddLabel(endif_label);
|
||||
@@ -3058,9 +3111,11 @@ public:
|
||||
decomp.OpBranch(next_block);
|
||||
decomp.AddLabel(next_block);
|
||||
if (ast.kills) {
|
||||
decomp.OpKill();
|
||||
decomp.SafeKill();
|
||||
} else {
|
||||
decomp.PreExit();
|
||||
if (decomp.context_func->IsMain()) {
|
||||
decomp.PreExit();
|
||||
}
|
||||
decomp.OpReturn();
|
||||
}
|
||||
decomp.AddLabel(decomp.OpLabel());
|
||||
@@ -3097,7 +3152,7 @@ private:
|
||||
};
|
||||
|
||||
void SPIRVDecompiler::DecompileAST() {
|
||||
const u32 num_flow_variables = ir.GetASTNumVariables();
|
||||
const u32 num_flow_variables = context_func->GetASTNumVariables();
|
||||
for (u32 i = 0; i < num_flow_variables; i++) {
|
||||
const Id id = OpVariable(t_prv_bool, spv::StorageClass::Private, v_false);
|
||||
Name(id, fmt::format("flow_var_{}", i));
|
||||
@@ -3106,7 +3161,7 @@ void SPIRVDecompiler::DecompileAST() {
|
||||
|
||||
DefinePrologue();
|
||||
|
||||
const ASTNode program = ir.GetASTProgram();
|
||||
const ASTNode program = context_func->GetASTProgram();
|
||||
ASTDecompiler decompiler{*this};
|
||||
decompiler.Visit(program);
|
||||
|
||||
|
||||
@@ -93,6 +93,11 @@ struct TextureCacheRuntime {
|
||||
// No known Vulkan driver has broken image views
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HasNativeBgr() const noexcept {
|
||||
// All known Vulkan drivers can natively handle BGR textures
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class Image : public VideoCommon::ImageBase {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <set>
|
||||
#include <stack>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "common/assert.h"
|
||||
@@ -26,17 +27,29 @@ using Tegra::Shader::OpCode;
|
||||
|
||||
constexpr s32 unassigned_branch = -2;
|
||||
|
||||
enum class JumpLabel : u32 {
|
||||
SSYClass = 0,
|
||||
PBKClass = 1,
|
||||
};
|
||||
|
||||
struct JumpItem {
|
||||
JumpLabel type;
|
||||
u32 address;
|
||||
|
||||
bool operator==(const JumpItem& other) const {
|
||||
return std::tie(type, address) == std::tie(other.type, other.address);
|
||||
}
|
||||
};
|
||||
|
||||
struct Query {
|
||||
u32 address{};
|
||||
std::stack<u32> ssy_stack{};
|
||||
std::stack<u32> pbk_stack{};
|
||||
std::stack<JumpItem> stack{};
|
||||
};
|
||||
|
||||
struct BlockStack {
|
||||
BlockStack() = default;
|
||||
explicit BlockStack(const Query& q) : ssy_stack{q.ssy_stack}, pbk_stack{q.pbk_stack} {}
|
||||
std::stack<u32> ssy_stack{};
|
||||
std::stack<u32> pbk_stack{};
|
||||
explicit BlockStack(const Query& q) : stack{q.stack} {}
|
||||
std::stack<JumpItem> stack{};
|
||||
};
|
||||
|
||||
template <typename T, typename... Args>
|
||||
@@ -65,20 +78,36 @@ struct BlockInfo {
|
||||
}
|
||||
};
|
||||
|
||||
struct CFGRebuildState {
|
||||
explicit CFGRebuildState(const ProgramCode& program_code_, u32 start_, Registry& registry_)
|
||||
: program_code{program_code_}, registry{registry_}, start{start_} {}
|
||||
struct ProgramControl {
|
||||
std::unordered_set<u32> found_functions{};
|
||||
std::list<u32> pending_functions{};
|
||||
|
||||
void RegisterFunction(u32 address) {
|
||||
if (found_functions.count(address) != 0) {
|
||||
return;
|
||||
}
|
||||
found_functions.insert(address);
|
||||
pending_functions.emplace_back(address);
|
||||
}
|
||||
};
|
||||
|
||||
struct CFGRebuildState {
|
||||
explicit CFGRebuildState(ProgramControl& control_, const ProgramCode& program_code_, u32 start_,
|
||||
u32 base_start_, Registry& registry_)
|
||||
: control{control_}, program_code{program_code_}, registry{registry_}, start{start_},
|
||||
base_start{base_start_} {}
|
||||
|
||||
ProgramControl& control;
|
||||
const ProgramCode& program_code;
|
||||
Registry& registry;
|
||||
u32 start{};
|
||||
u32 base_start{};
|
||||
std::vector<BlockInfo> block_info;
|
||||
std::list<u32> inspect_queries;
|
||||
std::list<Query> queries;
|
||||
std::unordered_map<u32, u32> registered;
|
||||
std::set<u32> labels;
|
||||
std::map<u32, u32> ssy_labels;
|
||||
std::map<u32, u32> pbk_labels;
|
||||
std::map<u32, JumpItem> jump_labels;
|
||||
std::unordered_map<u32, BlockStack> stacks;
|
||||
ASTManager* manager{};
|
||||
};
|
||||
@@ -153,7 +182,7 @@ template <typename Result, typename TestCallable, typename PackCallable>
|
||||
std::optional<Result> TrackInstruction(const CFGRebuildState& state, u32& pos, TestCallable test,
|
||||
PackCallable pack) {
|
||||
for (; pos >= state.start; --pos) {
|
||||
if (IsSchedInstruction(pos, state.start)) {
|
||||
if (IsSchedInstruction(pos, state.base_start)) {
|
||||
continue;
|
||||
}
|
||||
const Instruction instr = state.program_code[pos];
|
||||
@@ -262,7 +291,7 @@ std::pair<ParseResult, ParseInfo> ParseCode(CFGRebuildState& state, u32 address)
|
||||
single_branch.ignore = true;
|
||||
break;
|
||||
}
|
||||
if (IsSchedInstruction(offset, state.start)) {
|
||||
if (IsSchedInstruction(offset, state.base_start)) {
|
||||
offset++;
|
||||
continue;
|
||||
}
|
||||
@@ -274,6 +303,7 @@ std::pair<ParseResult, ParseInfo> ParseCode(CFGRebuildState& state, u32 address)
|
||||
}
|
||||
|
||||
switch (opcode->get().GetId()) {
|
||||
case OpCode::Id::RET:
|
||||
case OpCode::Id::EXIT: {
|
||||
const auto pred_index = static_cast<u32>(instr.pred.pred_index);
|
||||
single_branch.condition.predicate = GetPredicate(pred_index, instr.negate_pred != 0);
|
||||
@@ -411,13 +441,20 @@ std::pair<ParseResult, ParseInfo> ParseCode(CFGRebuildState& state, u32 address)
|
||||
case OpCode::Id::SSY: {
|
||||
const u32 target = offset + instr.bra.GetBranchTarget();
|
||||
insert_label(state, target);
|
||||
state.ssy_labels.emplace(offset, target);
|
||||
JumpItem it = {JumpLabel::SSYClass, target};
|
||||
state.jump_labels.emplace(offset, it);
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::PBK: {
|
||||
const u32 target = offset + instr.bra.GetBranchTarget();
|
||||
insert_label(state, target);
|
||||
state.pbk_labels.emplace(offset, target);
|
||||
JumpItem it = {JumpLabel::PBKClass, target};
|
||||
state.jump_labels.emplace(offset, it);
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::CAL: {
|
||||
const u32 target = offset + instr.bra.GetBranchTarget();
|
||||
state.control.RegisterFunction(target);
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::BRX: {
|
||||
@@ -513,7 +550,7 @@ bool TryInspectAddress(CFGRebuildState& state) {
|
||||
}
|
||||
|
||||
bool TryQuery(CFGRebuildState& state) {
|
||||
const auto gather_labels = [](std::stack<u32>& cc, std::map<u32, u32>& labels,
|
||||
const auto gather_labels = [](std::stack<JumpItem>& cc, std::map<u32, JumpItem>& labels,
|
||||
BlockInfo& block) {
|
||||
auto gather_start = labels.lower_bound(block.start);
|
||||
const auto gather_end = labels.upper_bound(block.end);
|
||||
@@ -522,6 +559,19 @@ bool TryQuery(CFGRebuildState& state) {
|
||||
++gather_start;
|
||||
}
|
||||
};
|
||||
const auto pop_labels = [](JumpLabel type, SingleBranch* branch, Query& query) -> bool {
|
||||
while (!query.stack.empty() && query.stack.top().type != type) {
|
||||
query.stack.pop();
|
||||
}
|
||||
if (query.stack.empty()) {
|
||||
return false;
|
||||
}
|
||||
if (branch->address == unassigned_branch) {
|
||||
branch->address = query.stack.top().address;
|
||||
}
|
||||
query.stack.pop();
|
||||
return true;
|
||||
};
|
||||
if (state.queries.empty()) {
|
||||
return false;
|
||||
}
|
||||
@@ -534,8 +584,7 @@ bool TryQuery(CFGRebuildState& state) {
|
||||
// consumes a label. Schedule new queries accordingly
|
||||
if (block.visited) {
|
||||
BlockStack& stack = state.stacks[q.address];
|
||||
const bool all_okay = (stack.ssy_stack.empty() || q.ssy_stack == stack.ssy_stack) &&
|
||||
(stack.pbk_stack.empty() || q.pbk_stack == stack.pbk_stack);
|
||||
const bool all_okay = (stack.stack.empty() || q.stack == stack.stack);
|
||||
state.queries.pop_front();
|
||||
return all_okay;
|
||||
}
|
||||
@@ -544,8 +593,7 @@ bool TryQuery(CFGRebuildState& state) {
|
||||
|
||||
Query q2(q);
|
||||
state.queries.pop_front();
|
||||
gather_labels(q2.ssy_stack, state.ssy_labels, block);
|
||||
gather_labels(q2.pbk_stack, state.pbk_labels, block);
|
||||
gather_labels(q2.stack, state.jump_labels, block);
|
||||
if (std::holds_alternative<SingleBranch>(*block.branch)) {
|
||||
auto* branch = std::get_if<SingleBranch>(block.branch.get());
|
||||
if (!branch->condition.IsUnconditional()) {
|
||||
@@ -555,16 +603,10 @@ bool TryQuery(CFGRebuildState& state) {
|
||||
|
||||
auto& conditional_query = state.queries.emplace_back(q2);
|
||||
if (branch->is_sync) {
|
||||
if (branch->address == unassigned_branch) {
|
||||
branch->address = conditional_query.ssy_stack.top();
|
||||
}
|
||||
conditional_query.ssy_stack.pop();
|
||||
pop_labels(JumpLabel::SSYClass, branch, conditional_query);
|
||||
}
|
||||
if (branch->is_brk) {
|
||||
if (branch->address == unassigned_branch) {
|
||||
branch->address = conditional_query.pbk_stack.top();
|
||||
}
|
||||
conditional_query.pbk_stack.pop();
|
||||
pop_labels(JumpLabel::PBKClass, branch, conditional_query);
|
||||
}
|
||||
conditional_query.address = branch->address;
|
||||
return true;
|
||||
@@ -646,25 +688,23 @@ void DecompileShader(CFGRebuildState& state) {
|
||||
state.manager->Decompile();
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
std::unique_ptr<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code, u32 start_address,
|
||||
const CompilerSettings& settings,
|
||||
Registry& registry) {
|
||||
auto result_out = std::make_unique<ShaderCharacteristics>();
|
||||
ShaderFunction ScanFunction(ProgramControl& control, const ProgramCode& program_code,
|
||||
u32 start_address, u32 base_start, const CompilerSettings& settings,
|
||||
Registry& registry) {
|
||||
ShaderFunction result_out{};
|
||||
if (settings.depth == CompileDepth::BruteForce) {
|
||||
result_out->settings.depth = CompileDepth::BruteForce;
|
||||
result_out.settings.depth = CompileDepth::BruteForce;
|
||||
return result_out;
|
||||
}
|
||||
|
||||
CFGRebuildState state{program_code, start_address, registry};
|
||||
CFGRebuildState state{control, program_code, start_address, base_start, registry};
|
||||
// Inspect Code and generate blocks
|
||||
state.labels.clear();
|
||||
state.labels.emplace(start_address);
|
||||
state.inspect_queries.push_back(state.start);
|
||||
while (!state.inspect_queries.empty()) {
|
||||
if (!TryInspectAddress(state)) {
|
||||
result_out->settings.depth = CompileDepth::BruteForce;
|
||||
result_out.settings.depth = CompileDepth::BruteForce;
|
||||
return result_out;
|
||||
}
|
||||
}
|
||||
@@ -675,7 +715,7 @@ std::unique_ptr<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code,
|
||||
|
||||
if (settings.depth != CompileDepth::FlowStack) {
|
||||
// Decompile Stacks
|
||||
state.queries.push_back(Query{state.start, {}, {}});
|
||||
state.queries.push_back(Query{state.start, {}});
|
||||
decompiled = true;
|
||||
while (!state.queries.empty()) {
|
||||
if (!TryQuery(state)) {
|
||||
@@ -705,19 +745,18 @@ std::unique_ptr<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code,
|
||||
state.manager->ShowCurrentState("Of Shader");
|
||||
state.manager->Clear();
|
||||
} else {
|
||||
auto characteristics = std::make_unique<ShaderCharacteristics>();
|
||||
characteristics->start = start_address;
|
||||
characteristics->settings.depth = settings.depth;
|
||||
characteristics->manager = std::move(manager);
|
||||
characteristics->end = state.block_info.back().end + 1;
|
||||
return characteristics;
|
||||
result_out.start = start_address;
|
||||
result_out.settings.depth = settings.depth;
|
||||
result_out.manager = std::move(manager);
|
||||
result_out.end = state.block_info.back().end + 1;
|
||||
return result_out;
|
||||
}
|
||||
}
|
||||
|
||||
result_out->start = start_address;
|
||||
result_out->settings.depth =
|
||||
result_out.start = start_address;
|
||||
result_out.settings.depth =
|
||||
use_flow_stack ? CompileDepth::FlowStack : CompileDepth::NoFlowStack;
|
||||
result_out->blocks.clear();
|
||||
result_out.blocks.clear();
|
||||
for (auto& block : state.block_info) {
|
||||
ShaderBlock new_block{};
|
||||
new_block.start = block.start;
|
||||
@@ -726,20 +765,20 @@ std::unique_ptr<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code,
|
||||
if (!new_block.ignore_branch) {
|
||||
new_block.branch = block.branch;
|
||||
}
|
||||
result_out->end = std::max(result_out->end, block.end);
|
||||
result_out->blocks.push_back(new_block);
|
||||
result_out.end = std::max(result_out.end, block.end);
|
||||
result_out.blocks.push_back(new_block);
|
||||
}
|
||||
if (!use_flow_stack) {
|
||||
result_out->labels = std::move(state.labels);
|
||||
result_out.labels = std::move(state.labels);
|
||||
return result_out;
|
||||
}
|
||||
|
||||
auto back = result_out->blocks.begin();
|
||||
auto back = result_out.blocks.begin();
|
||||
auto next = std::next(back);
|
||||
while (next != result_out->blocks.end()) {
|
||||
while (next != result_out.blocks.end()) {
|
||||
if (!state.labels.contains(next->start) && next->start == back->end + 1) {
|
||||
back->end = next->end;
|
||||
next = result_out->blocks.erase(next);
|
||||
next = result_out.blocks.erase(next);
|
||||
continue;
|
||||
}
|
||||
back = next;
|
||||
@@ -748,4 +787,22 @@ std::unique_ptr<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code,
|
||||
|
||||
return result_out;
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
std::unique_ptr<ShaderProgram> ScanFlow(const ProgramCode& program_code, u32 start_address,
|
||||
const CompilerSettings& settings, Registry& registry) {
|
||||
ProgramControl control{};
|
||||
auto result_out = std::make_unique<ShaderProgram>();
|
||||
result_out->main =
|
||||
ScanFunction(control, program_code, start_address, start_address, settings, registry);
|
||||
while (!control.pending_functions.empty()) {
|
||||
u32 address = control.pending_functions.front();
|
||||
auto fun = ScanFunction(control, program_code, address, start_address, settings, registry);
|
||||
result_out->subfunctions.emplace(address, std::move(fun));
|
||||
control.pending_functions.pop_front();
|
||||
}
|
||||
return result_out;
|
||||
}
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
@@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <variant>
|
||||
@@ -101,7 +102,7 @@ struct ShaderBlock {
|
||||
}
|
||||
};
|
||||
|
||||
struct ShaderCharacteristics {
|
||||
struct ShaderFunction {
|
||||
std::list<ShaderBlock> blocks{};
|
||||
std::set<u32> labels{};
|
||||
u32 start{};
|
||||
@@ -110,8 +111,12 @@ struct ShaderCharacteristics {
|
||||
CompilerSettings settings{};
|
||||
};
|
||||
|
||||
std::unique_ptr<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code, u32 start_address,
|
||||
const CompilerSettings& settings,
|
||||
Registry& registry);
|
||||
struct ShaderProgram {
|
||||
ShaderFunction main;
|
||||
std::map<u32, ShaderFunction> subfunctions;
|
||||
};
|
||||
|
||||
std::unique_ptr<ShaderProgram> ScanFlow(const ProgramCode& program_code, u32 start_address,
|
||||
const CompilerSettings& settings, Registry& registry);
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
@@ -64,9 +64,52 @@ std::optional<u32> TryDeduceSamplerSize(const SamplerEntry& sampler_to_deduce,
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
class ExprDecoder {
|
||||
public:
|
||||
explicit ExprDecoder(ShaderIR& ir_) : ir(ir_) {}
|
||||
|
||||
void operator()(const ExprAnd& expr) {
|
||||
Visit(expr.operand1);
|
||||
Visit(expr.operand2);
|
||||
}
|
||||
|
||||
void operator()(const ExprOr& expr) {
|
||||
Visit(expr.operand1);
|
||||
Visit(expr.operand2);
|
||||
}
|
||||
|
||||
void operator()(const ExprNot& expr) {
|
||||
Visit(expr.operand1);
|
||||
}
|
||||
|
||||
void operator()(const ExprPredicate& expr) {
|
||||
const auto pred = static_cast<Tegra::Shader::Pred>(expr.predicate);
|
||||
if (pred != Pred::UnusedIndex && pred != Pred::NeverExecute) {
|
||||
ir.used_predicates.insert(pred);
|
||||
}
|
||||
}
|
||||
|
||||
void operator()(const ExprCondCode& expr) {}
|
||||
|
||||
void operator()(const ExprVar& expr) {}
|
||||
|
||||
void operator()(const ExprBoolean& expr) {}
|
||||
|
||||
void operator()(const ExprGprEqual& expr) {
|
||||
ir.used_registers.insert(expr.gpr);
|
||||
}
|
||||
|
||||
void Visit(const Expr& node) {
|
||||
return std::visit(*this, *node);
|
||||
}
|
||||
|
||||
private:
|
||||
ShaderIR& ir;
|
||||
};
|
||||
|
||||
class ASTDecoder {
|
||||
public:
|
||||
explicit ASTDecoder(ShaderIR& ir_) : ir(ir_) {}
|
||||
explicit ASTDecoder(ShaderIR& ir_) : ir(ir_), decoder(ir_) {}
|
||||
|
||||
void operator()(ASTProgram& ast) {
|
||||
ASTNode current = ast.nodes.GetFirst();
|
||||
@@ -77,6 +120,7 @@ public:
|
||||
}
|
||||
|
||||
void operator()(ASTIfThen& ast) {
|
||||
decoder.Visit(ast.condition);
|
||||
ASTNode current = ast.nodes.GetFirst();
|
||||
while (current) {
|
||||
Visit(current);
|
||||
@@ -96,13 +140,18 @@ public:
|
||||
|
||||
void operator()(ASTBlockDecoded& ast) {}
|
||||
|
||||
void operator()(ASTVarSet& ast) {}
|
||||
void operator()(ASTVarSet& ast) {
|
||||
decoder.Visit(ast.condition);
|
||||
}
|
||||
|
||||
void operator()(ASTLabel& ast) {}
|
||||
|
||||
void operator()(ASTGoto& ast) {}
|
||||
void operator()(ASTGoto& ast) {
|
||||
decoder.Visit(ast.condition);
|
||||
}
|
||||
|
||||
void operator()(ASTDoWhile& ast) {
|
||||
decoder.Visit(ast.condition);
|
||||
ASTNode current = ast.nodes.GetFirst();
|
||||
while (current) {
|
||||
Visit(current);
|
||||
@@ -110,9 +159,13 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void operator()(ASTReturn& ast) {}
|
||||
void operator()(ASTReturn& ast) {
|
||||
decoder.Visit(ast.condition);
|
||||
}
|
||||
|
||||
void operator()(ASTBreak& ast) {}
|
||||
void operator()(ASTBreak& ast) {
|
||||
decoder.Visit(ast.condition);
|
||||
}
|
||||
|
||||
void Visit(ASTNode& node) {
|
||||
std::visit(*this, *node->GetInnerData());
|
||||
@@ -125,77 +178,113 @@ public:
|
||||
|
||||
private:
|
||||
ShaderIR& ir;
|
||||
ExprDecoder decoder;
|
||||
};
|
||||
|
||||
void ShaderIR::Decode() {
|
||||
const auto decode_function = ([this](ShaderFunction& shader_info) {
|
||||
coverage_end = std::max<u32>(0, shader_info.end);
|
||||
switch (shader_info.settings.depth) {
|
||||
case CompileDepth::FlowStack: {
|
||||
for (const auto& block : shader_info.blocks) {
|
||||
basic_blocks.insert({block.start, DecodeRange(block.start, block.end + 1)});
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CompileDepth::NoFlowStack: {
|
||||
disable_flow_stack = true;
|
||||
const auto insert_block = [this](NodeBlock& nodes, u32 label) {
|
||||
if (label == static_cast<u32>(exit_branch)) {
|
||||
return;
|
||||
}
|
||||
basic_blocks.insert({label, nodes});
|
||||
};
|
||||
const auto& blocks = shader_info.blocks;
|
||||
NodeBlock current_block;
|
||||
u32 current_label = static_cast<u32>(exit_branch);
|
||||
for (const auto& block : blocks) {
|
||||
if (shader_info.labels.contains(block.start)) {
|
||||
insert_block(current_block, current_label);
|
||||
current_block.clear();
|
||||
current_label = block.start;
|
||||
}
|
||||
if (!block.ignore_branch) {
|
||||
DecodeRangeInner(current_block, block.start, block.end);
|
||||
InsertControlFlow(current_block, block);
|
||||
} else {
|
||||
DecodeRangeInner(current_block, block.start, block.end + 1);
|
||||
}
|
||||
}
|
||||
insert_block(current_block, current_label);
|
||||
break;
|
||||
}
|
||||
case CompileDepth::DecompileBackwards:
|
||||
case CompileDepth::FullDecompile: {
|
||||
program_manager = std::move(shader_info.manager);
|
||||
disable_flow_stack = true;
|
||||
decompiled = true;
|
||||
ASTDecoder decoder{*this};
|
||||
ASTNode program = program_manager.GetProgram();
|
||||
decoder.Visit(program);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOG_CRITICAL(HW_GPU, "Unknown decompilation mode!");
|
||||
[[fallthrough]];
|
||||
case CompileDepth::BruteForce: {
|
||||
const auto shader_end = static_cast<u32>(program_code.size());
|
||||
coverage_begin = main_offset;
|
||||
coverage_end = shader_end;
|
||||
for (u32 label = main_offset; label < shader_end; ++label) {
|
||||
basic_blocks.insert({label, DecodeRange(label, label + 1)});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (settings.depth != shader_info.settings.depth) {
|
||||
LOG_WARNING(
|
||||
HW_GPU,
|
||||
"Decompiling to this setting \"{}\" failed, downgrading to this setting \"{}\"",
|
||||
CompileDepthAsString(settings.depth),
|
||||
CompileDepthAsString(shader_info.settings.depth));
|
||||
}
|
||||
});
|
||||
const auto gen_function =
|
||||
([this](ShaderFunction& shader_info, u32 id) -> std::shared_ptr<ShaderFunctionIR> {
|
||||
std::shared_ptr<ShaderFunctionIR> result;
|
||||
if (decompiled) {
|
||||
result = std::make_shared<ShaderFunctionIR>(std::move(program_manager), id,
|
||||
shader_info.start, shader_info.end);
|
||||
} else {
|
||||
result =
|
||||
std::make_shared<ShaderFunctionIR>(std::move(basic_blocks), disable_flow_stack,
|
||||
id, shader_info.start, shader_info.end);
|
||||
}
|
||||
decompiled = false;
|
||||
disable_flow_stack = false;
|
||||
basic_blocks.clear();
|
||||
program_manager.Clear();
|
||||
return result;
|
||||
});
|
||||
std::memcpy(&header, program_code.data(), sizeof(Tegra::Shader::Header));
|
||||
|
||||
decompiled = false;
|
||||
auto info = ScanFlow(program_code, main_offset, settings, registry);
|
||||
auto& shader_info = *info;
|
||||
coverage_begin = shader_info.start;
|
||||
coverage_end = shader_info.end;
|
||||
switch (shader_info.settings.depth) {
|
||||
case CompileDepth::FlowStack: {
|
||||
for (const auto& block : shader_info.blocks) {
|
||||
basic_blocks.insert({block.start, DecodeRange(block.start, block.end + 1)});
|
||||
}
|
||||
break;
|
||||
u32 id_start = 1;
|
||||
for (auto& pair : info->subfunctions) {
|
||||
func_map.emplace(pair.first, id_start);
|
||||
id_start++;
|
||||
}
|
||||
case CompileDepth::NoFlowStack: {
|
||||
disable_flow_stack = true;
|
||||
const auto insert_block = [this](NodeBlock& nodes, u32 label) {
|
||||
if (label == static_cast<u32>(exit_branch)) {
|
||||
return;
|
||||
}
|
||||
basic_blocks.insert({label, nodes});
|
||||
};
|
||||
const auto& blocks = shader_info.blocks;
|
||||
NodeBlock current_block;
|
||||
u32 current_label = static_cast<u32>(exit_branch);
|
||||
for (const auto& block : blocks) {
|
||||
if (shader_info.labels.contains(block.start)) {
|
||||
insert_block(current_block, current_label);
|
||||
current_block.clear();
|
||||
current_label = block.start;
|
||||
}
|
||||
if (!block.ignore_branch) {
|
||||
DecodeRangeInner(current_block, block.start, block.end);
|
||||
InsertControlFlow(current_block, block);
|
||||
} else {
|
||||
DecodeRangeInner(current_block, block.start, block.end + 1);
|
||||
}
|
||||
}
|
||||
insert_block(current_block, current_label);
|
||||
break;
|
||||
}
|
||||
case CompileDepth::DecompileBackwards:
|
||||
case CompileDepth::FullDecompile: {
|
||||
program_manager = std::move(shader_info.manager);
|
||||
disable_flow_stack = true;
|
||||
decompiled = true;
|
||||
ASTDecoder decoder{*this};
|
||||
ASTNode program = GetASTProgram();
|
||||
decoder.Visit(program);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOG_CRITICAL(HW_GPU, "Unknown decompilation mode!");
|
||||
[[fallthrough]];
|
||||
case CompileDepth::BruteForce: {
|
||||
const auto shader_end = static_cast<u32>(program_code.size());
|
||||
coverage_begin = main_offset;
|
||||
coverage_end = shader_end;
|
||||
for (u32 label = main_offset; label < shader_end; ++label) {
|
||||
basic_blocks.insert({label, DecodeRange(label, label + 1)});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (settings.depth != shader_info.settings.depth) {
|
||||
LOG_WARNING(
|
||||
HW_GPU, "Decompiling to this setting \"{}\" failed, downgrading to this setting \"{}\"",
|
||||
CompileDepthAsString(settings.depth), CompileDepthAsString(shader_info.settings.depth));
|
||||
coverage_begin = info->main.start;
|
||||
coverage_end = 0;
|
||||
decode_function(info->main);
|
||||
main_function = gen_function(info->main, 0);
|
||||
subfunctions.resize(info->subfunctions.size());
|
||||
for (auto& pair : info->subfunctions) {
|
||||
auto& func_info = pair.second;
|
||||
decode_function(func_info);
|
||||
u32 id = func_map[pair.first];
|
||||
subfunctions[id - 1] = gen_function(func_info, id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) {
|
||||
// With the previous preconditions, this instruction is a no-operation.
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::RET:
|
||||
case OpCode::Id::EXIT: {
|
||||
const ConditionCode cc = instr.flow_condition_code;
|
||||
UNIMPLEMENTED_IF_MSG(cc != ConditionCode::T, "EXIT condition code used: {}", cc);
|
||||
@@ -312,6 +313,16 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) {
|
||||
LOG_DEBUG(HW_GPU, "DEPBAR instruction is stubbed");
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::CAL: {
|
||||
const u32 target = pc + instr.bra.GetBranchTarget();
|
||||
const auto it = func_map.find(target);
|
||||
if (it == func_map.end()) {
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
bb.push_back(FunctionCall(it->second));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unhandled instruction: {}", opcode->get().GetName());
|
||||
}
|
||||
|
||||
@@ -267,10 +267,11 @@ class PatchNode;
|
||||
class SmemNode;
|
||||
class GmemNode;
|
||||
class CommentNode;
|
||||
class FunctionCallNode;
|
||||
|
||||
using NodeData = std::variant<OperationNode, ConditionalNode, GprNode, CustomVarNode, ImmediateNode,
|
||||
InternalFlagNode, PredicateNode, AbufNode, PatchNode, CbufNode,
|
||||
LmemNode, SmemNode, GmemNode, CommentNode>;
|
||||
LmemNode, SmemNode, GmemNode, FunctionCallNode, CommentNode>;
|
||||
using Node = std::shared_ptr<NodeData>;
|
||||
using Node4 = std::array<Node, 4>;
|
||||
using NodeBlock = std::vector<Node>;
|
||||
@@ -494,6 +495,18 @@ private:
|
||||
std::vector<Node> code; ///< Code to execute
|
||||
};
|
||||
|
||||
class FunctionCallNode final : public AmendNode {
|
||||
public:
|
||||
explicit FunctionCallNode(u32 func_id_) : func_id{func_id_} {}
|
||||
|
||||
[[nodiscard]] u32 GetFuncId() const {
|
||||
return func_id;
|
||||
}
|
||||
|
||||
private:
|
||||
u32 func_id; ///< Id of the function to call
|
||||
};
|
||||
|
||||
/// A general purpose register
|
||||
class GprNode final {
|
||||
public:
|
||||
|
||||
@@ -19,6 +19,11 @@ Node Comment(std::string text) {
|
||||
return MakeNode<CommentNode>(std::move(text));
|
||||
}
|
||||
|
||||
/// Creates a function call
|
||||
Node FunctionCall(u32 func_id) {
|
||||
return MakeNode<FunctionCallNode>(func_id);
|
||||
}
|
||||
|
||||
Node Immediate(u32 value) {
|
||||
return MakeNode<ImmediateNode>(value);
|
||||
}
|
||||
|
||||
@@ -27,6 +27,9 @@ Node Conditional(Node condition, std::vector<Node> code);
|
||||
/// Creates a commentary node
|
||||
Node Comment(std::string text);
|
||||
|
||||
/// Creates a function call
|
||||
Node FunctionCall(u32 func_id);
|
||||
|
||||
/// Creates an u32 immediate
|
||||
Node Immediate(u32 value);
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace VideoCommon::Shader {
|
||||
|
||||
struct ShaderBlock;
|
||||
|
||||
constexpr u32 MAX_PROGRAM_LENGTH = 0x1000;
|
||||
constexpr u32 MAX_PROGRAM_LENGTH = 0x2000;
|
||||
|
||||
struct ConstBuffer {
|
||||
constexpr explicit ConstBuffer(u32 max_offset_, bool is_indirect_)
|
||||
@@ -64,16 +64,68 @@ struct GlobalMemoryUsage {
|
||||
bool is_written{};
|
||||
};
|
||||
|
||||
class ShaderFunctionIR final {
|
||||
public:
|
||||
explicit ShaderFunctionIR(std::map<u32, NodeBlock>&& basic_blocks_, bool disable_flow_stack_,
|
||||
u32 id_, u32 coverage_begin_, u32 coverage_end_)
|
||||
: basic_blocks{std::move(basic_blocks_)}, decompiled{false},
|
||||
disable_flow_stack{disable_flow_stack_}, id{id_}, coverage_begin{coverage_begin_},
|
||||
coverage_end{coverage_end_} {}
|
||||
explicit ShaderFunctionIR(ASTManager&& program_manager_, u32 id_, u32 coverage_begin_,
|
||||
u32 coverage_end_)
|
||||
: program_manager{std::move(program_manager_)}, decompiled{true}, disable_flow_stack{true},
|
||||
id{id_}, coverage_begin{coverage_begin_}, coverage_end{coverage_end_} {}
|
||||
|
||||
const std::map<u32, NodeBlock>& GetBasicBlocks() const {
|
||||
return basic_blocks;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsFlowStackDisabled() const {
|
||||
return disable_flow_stack;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsDecompiled() const {
|
||||
return decompiled;
|
||||
}
|
||||
|
||||
const ASTManager& GetASTManager() const {
|
||||
return program_manager;
|
||||
}
|
||||
|
||||
[[nodiscard]] ASTNode GetASTProgram() const {
|
||||
return program_manager.GetProgram();
|
||||
}
|
||||
|
||||
[[nodiscard]] u32 GetASTNumVariables() const {
|
||||
return program_manager.GetVariables();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsMain() const {
|
||||
return id == 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] u32 GetId() const {
|
||||
return id;
|
||||
}
|
||||
|
||||
private:
|
||||
std::map<u32, NodeBlock> basic_blocks;
|
||||
ASTManager program_manager{true, true};
|
||||
|
||||
bool decompiled{};
|
||||
bool disable_flow_stack{};
|
||||
u32 id{};
|
||||
|
||||
u32 coverage_begin{};
|
||||
u32 coverage_end{};
|
||||
};
|
||||
|
||||
class ShaderIR final {
|
||||
public:
|
||||
explicit ShaderIR(const ProgramCode& program_code_, u32 main_offset_,
|
||||
CompilerSettings settings_, Registry& registry_);
|
||||
~ShaderIR();
|
||||
|
||||
const std::map<u32, NodeBlock>& GetBasicBlocks() const {
|
||||
return basic_blocks;
|
||||
}
|
||||
|
||||
const std::set<u32>& GetRegisters() const {
|
||||
return used_registers;
|
||||
}
|
||||
@@ -155,26 +207,6 @@ public:
|
||||
return header;
|
||||
}
|
||||
|
||||
bool IsFlowStackDisabled() const {
|
||||
return disable_flow_stack;
|
||||
}
|
||||
|
||||
bool IsDecompiled() const {
|
||||
return decompiled;
|
||||
}
|
||||
|
||||
const ASTManager& GetASTManager() const {
|
||||
return program_manager;
|
||||
}
|
||||
|
||||
ASTNode GetASTProgram() const {
|
||||
return program_manager.GetProgram();
|
||||
}
|
||||
|
||||
u32 GetASTNumVariables() const {
|
||||
return program_manager.GetVariables();
|
||||
}
|
||||
|
||||
u32 ConvertAddressToNvidiaSpace(u32 address) const {
|
||||
return (address - main_offset) * static_cast<u32>(sizeof(Tegra::Shader::Instruction));
|
||||
}
|
||||
@@ -190,7 +222,16 @@ public:
|
||||
return num_custom_variables;
|
||||
}
|
||||
|
||||
std::shared_ptr<ShaderFunctionIR> GetMainFunction() const {
|
||||
return main_function;
|
||||
}
|
||||
|
||||
const std::vector<std::shared_ptr<ShaderFunctionIR>>& GetSubFunctions() const {
|
||||
return subfunctions;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class ExprDecoder;
|
||||
friend class ASTDecoder;
|
||||
|
||||
struct SamplerInfo {
|
||||
@@ -453,6 +494,10 @@ private:
|
||||
std::vector<Node> amend_code;
|
||||
u32 num_custom_variables{};
|
||||
|
||||
std::shared_ptr<ShaderFunctionIR> main_function;
|
||||
std::vector<std::shared_ptr<ShaderFunctionIR>> subfunctions;
|
||||
std::unordered_map<u32, u32> func_map;
|
||||
|
||||
std::set<u32> used_registers;
|
||||
std::set<Tegra::Shader::Pred> used_predicates;
|
||||
std::set<Tegra::Shader::Attribute::Index> used_input_attributes;
|
||||
|
||||
@@ -120,9 +120,10 @@ void AddImageAlias(ImageBase& lhs, ImageBase& rhs, ImageId lhs_id, ImageId rhs_i
|
||||
if (lhs.info.type == ImageType::Linear) {
|
||||
base = SubresourceBase{.level = 0, .layer = 0};
|
||||
} else {
|
||||
// We are passing relaxed formats as an option, having broken views or not won't matter
|
||||
// We are passing relaxed formats as an option, having broken views/bgr or not won't matter
|
||||
static constexpr bool broken_views = false;
|
||||
base = FindSubresource(rhs.info, lhs, rhs.gpu_addr, OPTIONS, broken_views);
|
||||
static constexpr bool native_bgr = true;
|
||||
base = FindSubresource(rhs.info, lhs, rhs.gpu_addr, OPTIONS, broken_views, native_bgr);
|
||||
}
|
||||
if (!base) {
|
||||
LOG_ERROR(HW_GPU, "Image alias should have been flipped");
|
||||
|
||||
@@ -24,7 +24,7 @@ ImageViewBase::ImageViewBase(const ImageViewInfo& info, const ImageInfo& image_i
|
||||
.height = std::max(image_info.size.height >> range.base.level, 1u),
|
||||
.depth = std::max(image_info.size.depth >> range.base.level, 1u),
|
||||
} {
|
||||
ASSERT_MSG(VideoCore::Surface::IsViewCompatible(image_info.format, info.format, false),
|
||||
ASSERT_MSG(VideoCore::Surface::IsViewCompatible(image_info.format, info.format, false, true),
|
||||
"Image view format {} is incompatible with image format {}", info.format,
|
||||
image_info.format);
|
||||
const bool is_async = Settings::values.use_asynchronous_gpu_emulation.GetValue();
|
||||
|
||||
@@ -876,6 +876,7 @@ ImageId TextureCache<P>::FindImage(const ImageInfo& info, GPUVAddr gpu_addr,
|
||||
return ImageId{};
|
||||
}
|
||||
const bool broken_views = runtime.HasBrokenTextureViewFormats();
|
||||
const bool native_bgr = runtime.HasNativeBgr();
|
||||
ImageId image_id;
|
||||
const auto lambda = [&](ImageId existing_image_id, ImageBase& existing_image) {
|
||||
if (info.type == ImageType::Linear || existing_image.info.type == ImageType::Linear) {
|
||||
@@ -885,11 +886,12 @@ ImageId TextureCache<P>::FindImage(const ImageInfo& info, GPUVAddr gpu_addr,
|
||||
if (existing_image.gpu_addr == gpu_addr && existing.type == info.type &&
|
||||
existing.pitch == info.pitch &&
|
||||
IsPitchLinearSameSize(existing, info, strict_size) &&
|
||||
IsViewCompatible(existing.format, info.format, broken_views)) {
|
||||
IsViewCompatible(existing.format, info.format, broken_views, native_bgr)) {
|
||||
image_id = existing_image_id;
|
||||
return true;
|
||||
}
|
||||
} else if (IsSubresource(info, existing_image, gpu_addr, options, broken_views)) {
|
||||
} else if (IsSubresource(info, existing_image, gpu_addr, options, broken_views,
|
||||
native_bgr)) {
|
||||
image_id = existing_image_id;
|
||||
return true;
|
||||
}
|
||||
@@ -920,6 +922,7 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
|
||||
ImageInfo new_info = info;
|
||||
const size_t size_bytes = CalculateGuestSizeInBytes(new_info);
|
||||
const bool broken_views = runtime.HasBrokenTextureViewFormats();
|
||||
const bool native_bgr = runtime.HasNativeBgr();
|
||||
std::vector<ImageId> overlap_ids;
|
||||
std::vector<ImageId> left_aliased_ids;
|
||||
std::vector<ImageId> right_aliased_ids;
|
||||
@@ -935,8 +938,8 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
|
||||
return;
|
||||
}
|
||||
static constexpr bool strict_size = true;
|
||||
const std::optional<OverlapResult> solution =
|
||||
ResolveOverlap(new_info, gpu_addr, cpu_addr, overlap, strict_size, broken_views);
|
||||
const std::optional<OverlapResult> solution = ResolveOverlap(
|
||||
new_info, gpu_addr, cpu_addr, overlap, strict_size, broken_views, native_bgr);
|
||||
if (solution) {
|
||||
gpu_addr = solution->gpu_addr;
|
||||
cpu_addr = solution->cpu_addr;
|
||||
@@ -946,10 +949,10 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
|
||||
}
|
||||
static constexpr auto options = RelaxedOptions::Size | RelaxedOptions::Format;
|
||||
const ImageBase new_image_base(new_info, gpu_addr, cpu_addr);
|
||||
if (IsSubresource(new_info, overlap, gpu_addr, options, broken_views)) {
|
||||
if (IsSubresource(new_info, overlap, gpu_addr, options, broken_views, native_bgr)) {
|
||||
left_aliased_ids.push_back(overlap_id);
|
||||
} else if (IsSubresource(overlap.info, new_image_base, overlap.gpu_addr, options,
|
||||
broken_views)) {
|
||||
broken_views, native_bgr)) {
|
||||
right_aliased_ids.push_back(overlap_id);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1035,13 +1035,13 @@ bool IsPitchLinearSameSize(const ImageInfo& lhs, const ImageInfo& rhs, bool stri
|
||||
|
||||
std::optional<OverlapResult> ResolveOverlap(const ImageInfo& new_info, GPUVAddr gpu_addr,
|
||||
VAddr cpu_addr, const ImageBase& overlap,
|
||||
bool strict_size, bool broken_views) {
|
||||
bool strict_size, bool broken_views, bool native_bgr) {
|
||||
ASSERT(new_info.type != ImageType::Linear);
|
||||
ASSERT(overlap.info.type != ImageType::Linear);
|
||||
if (!IsLayerStrideCompatible(new_info, overlap.info)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
if (!IsViewCompatible(overlap.info.format, new_info.format, broken_views)) {
|
||||
if (!IsViewCompatible(overlap.info.format, new_info.format, broken_views, native_bgr)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
if (gpu_addr == overlap.gpu_addr) {
|
||||
@@ -1085,14 +1085,14 @@ bool IsLayerStrideCompatible(const ImageInfo& lhs, const ImageInfo& rhs) {
|
||||
|
||||
std::optional<SubresourceBase> FindSubresource(const ImageInfo& candidate, const ImageBase& image,
|
||||
GPUVAddr candidate_addr, RelaxedOptions options,
|
||||
bool broken_views) {
|
||||
bool broken_views, bool native_bgr) {
|
||||
const std::optional<SubresourceBase> base = image.TryFindBase(candidate_addr);
|
||||
if (!base) {
|
||||
return std::nullopt;
|
||||
}
|
||||
const ImageInfo& existing = image.info;
|
||||
if (False(options & RelaxedOptions::Format)) {
|
||||
if (!IsViewCompatible(existing.format, candidate.format, broken_views)) {
|
||||
if (!IsViewCompatible(existing.format, candidate.format, broken_views, native_bgr)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
@@ -1129,8 +1129,9 @@ std::optional<SubresourceBase> FindSubresource(const ImageInfo& candidate, const
|
||||
}
|
||||
|
||||
bool IsSubresource(const ImageInfo& candidate, const ImageBase& image, GPUVAddr candidate_addr,
|
||||
RelaxedOptions options, bool broken_views) {
|
||||
return FindSubresource(candidate, image, candidate_addr, options, broken_views).has_value();
|
||||
RelaxedOptions options, bool broken_views, bool native_bgr) {
|
||||
return FindSubresource(candidate, image, candidate_addr, options, broken_views, native_bgr)
|
||||
.has_value();
|
||||
}
|
||||
|
||||
void DeduceBlitImages(ImageInfo& dst_info, ImageInfo& src_info, const ImageBase* dst,
|
||||
|
||||
@@ -87,7 +87,8 @@ void SwizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, const Ima
|
||||
[[nodiscard]] std::optional<OverlapResult> ResolveOverlap(const ImageInfo& new_info,
|
||||
GPUVAddr gpu_addr, VAddr cpu_addr,
|
||||
const ImageBase& overlap,
|
||||
bool strict_size, bool broken_views);
|
||||
bool strict_size, bool broken_views,
|
||||
bool native_bgr);
|
||||
|
||||
[[nodiscard]] bool IsLayerStrideCompatible(const ImageInfo& lhs, const ImageInfo& rhs);
|
||||
|
||||
@@ -95,11 +96,11 @@ void SwizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, const Ima
|
||||
const ImageBase& image,
|
||||
GPUVAddr candidate_addr,
|
||||
RelaxedOptions options,
|
||||
bool broken_views);
|
||||
bool broken_views, bool native_bgr);
|
||||
|
||||
[[nodiscard]] bool IsSubresource(const ImageInfo& candidate, const ImageBase& image,
|
||||
GPUVAddr candidate_addr, RelaxedOptions options,
|
||||
bool broken_views);
|
||||
GPUVAddr candidate_addr, RelaxedOptions options, bool broken_views,
|
||||
bool native_bgr);
|
||||
|
||||
void DeduceBlitImages(ImageInfo& dst_info, ImageInfo& src_info, const ImageBase* dst,
|
||||
const ImageBase* src);
|
||||
|
||||
@@ -376,11 +376,34 @@ void GRenderWindow::closeEvent(QCloseEvent* event) {
|
||||
}
|
||||
|
||||
void GRenderWindow::keyPressEvent(QKeyEvent* event) {
|
||||
input_subsystem->GetKeyboard()->PressKey(event->key());
|
||||
if (!event->isAutoRepeat()) {
|
||||
input_subsystem->GetKeyboard()->PressKey(event->key());
|
||||
}
|
||||
}
|
||||
|
||||
void GRenderWindow::keyReleaseEvent(QKeyEvent* event) {
|
||||
input_subsystem->GetKeyboard()->ReleaseKey(event->key());
|
||||
if (!event->isAutoRepeat()) {
|
||||
input_subsystem->GetKeyboard()->ReleaseKey(event->key());
|
||||
}
|
||||
}
|
||||
|
||||
MouseInput::MouseButton GRenderWindow::QtButtonToMouseButton(Qt::MouseButton button) {
|
||||
switch (button) {
|
||||
case Qt::LeftButton:
|
||||
return MouseInput::MouseButton::Left;
|
||||
case Qt::RightButton:
|
||||
return MouseInput::MouseButton::Right;
|
||||
case Qt::MiddleButton:
|
||||
return MouseInput::MouseButton::Wheel;
|
||||
case Qt::BackButton:
|
||||
return MouseInput::MouseButton::Backward;
|
||||
case Qt::ForwardButton:
|
||||
return MouseInput::MouseButton::Forward;
|
||||
case Qt::TaskButton:
|
||||
return MouseInput::MouseButton::Task;
|
||||
default:
|
||||
return MouseInput::MouseButton::Extra;
|
||||
}
|
||||
}
|
||||
|
||||
void GRenderWindow::mousePressEvent(QMouseEvent* event) {
|
||||
@@ -391,7 +414,8 @@ void GRenderWindow::mousePressEvent(QMouseEvent* event) {
|
||||
|
||||
auto pos = event->pos();
|
||||
const auto [x, y] = ScaleTouch(pos);
|
||||
input_subsystem->GetMouse()->PressButton(x, y, event->button());
|
||||
const auto button = QtButtonToMouseButton(event->button());
|
||||
input_subsystem->GetMouse()->PressButton(x, y, button);
|
||||
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
this->TouchPressed(x, y, 0);
|
||||
@@ -425,7 +449,8 @@ void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
|
||||
return;
|
||||
}
|
||||
|
||||
input_subsystem->GetMouse()->ReleaseButton(event->button());
|
||||
const auto button = QtButtonToMouseButton(event->button());
|
||||
input_subsystem->GetMouse()->ReleaseButton(button);
|
||||
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
this->TouchReleased(0);
|
||||
|
||||
@@ -28,6 +28,10 @@ namespace InputCommon {
|
||||
class InputSubsystem;
|
||||
}
|
||||
|
||||
namespace MouseInput {
|
||||
enum class MouseButton;
|
||||
}
|
||||
|
||||
namespace VideoCore {
|
||||
enum class LoadCallbackStage;
|
||||
}
|
||||
@@ -149,6 +153,9 @@ public:
|
||||
void keyPressEvent(QKeyEvent* event) override;
|
||||
void keyReleaseEvent(QKeyEvent* event) override;
|
||||
|
||||
/// Converts a Qt mouse button into MouseInput mouse button
|
||||
static MouseInput::MouseButton QtButtonToMouseButton(Qt::MouseButton button);
|
||||
|
||||
void mousePressEvent(QMouseEvent* event) override;
|
||||
void mouseMoveEvent(QMouseEvent* event) override;
|
||||
void mouseReleaseEvent(QMouseEvent* event) override;
|
||||
|
||||
@@ -235,7 +235,7 @@ const std::array<UISettings::Shortcut, 17> Config::default_hotkeys{{
|
||||
{QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Toggle Mouse Panning"), QStringLiteral("Main Window"), {QStringLiteral("F9"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Toggle Mouse Panning"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F9"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Toggle Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Z"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Toggle Status Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+S"), Qt::WindowShortcut}},
|
||||
}};
|
||||
@@ -508,7 +508,7 @@ void Config::ReadControlValues() {
|
||||
|
||||
Settings::values.emulate_analog_keyboard =
|
||||
ReadSetting(QStringLiteral("emulate_analog_keyboard"), false).toBool();
|
||||
Settings::values.mouse_panning = ReadSetting(QStringLiteral("mouse_panning"), false).toBool();
|
||||
Settings::values.mouse_panning = false;
|
||||
Settings::values.mouse_panning_sensitivity =
|
||||
ReadSetting(QStringLiteral("mouse_panning_sensitivity"), 1).toFloat();
|
||||
|
||||
@@ -1182,7 +1182,6 @@ void Config::SaveControlValues() {
|
||||
WriteSetting(QStringLiteral("keyboard_enabled"), Settings::values.keyboard_enabled, false);
|
||||
WriteSetting(QStringLiteral("emulate_analog_keyboard"),
|
||||
Settings::values.emulate_analog_keyboard, false);
|
||||
WriteSetting(QStringLiteral("mouse_panning"), Settings::values.mouse_panning, false);
|
||||
WriteSetting(QStringLiteral("mouse_panning_sensitivity"),
|
||||
Settings::values.mouse_panning_sensitivity, 1.0f);
|
||||
qt_config->endGroup();
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "input_common/mouse/mouse_poller.h"
|
||||
#include "input_common/udp/udp.h"
|
||||
#include "ui_configure_input_player.h"
|
||||
#include "yuzu/bootmanager.h"
|
||||
#include "yuzu/configuration/config.h"
|
||||
#include "yuzu/configuration/configure_input_player.h"
|
||||
#include "yuzu/configuration/configure_input_player_widget.h"
|
||||
@@ -104,7 +105,9 @@ QString ButtonToText(const Common::ParamPackage& param) {
|
||||
}
|
||||
|
||||
if (param.Get("engine", "") == "keyboard") {
|
||||
return GetKeyName(param.Get("code", 0));
|
||||
const QString button_str = GetKeyName(param.Get("code", 0));
|
||||
const QString toggle = QString::fromStdString(param.Get("toggle", false) ? "~" : "");
|
||||
return QObject::tr("%1%2").arg(toggle, button_str);
|
||||
}
|
||||
|
||||
if (param.Get("engine", "") == "gcpad") {
|
||||
@@ -156,7 +159,8 @@ QString ButtonToText(const Common::ParamPackage& param) {
|
||||
if (param.Get("engine", "") == "mouse") {
|
||||
if (param.Has("button")) {
|
||||
const QString button_str = QString::number(int(param.Get("button", 0)));
|
||||
return QObject::tr("Click %1").arg(button_str);
|
||||
const QString toggle = QString::fromStdString(param.Get("toggle", false) ? "~" : "");
|
||||
return QObject::tr("%1Click %2").arg(toggle, button_str);
|
||||
}
|
||||
return GetKeyName(param.Get("code", 0));
|
||||
}
|
||||
@@ -300,6 +304,11 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
|
||||
buttons_param[button_id].Clear();
|
||||
button_map[button_id]->setText(tr("[not set]"));
|
||||
});
|
||||
context_menu.addAction(tr("Toggle button"), [&] {
|
||||
const bool toggle_value = !buttons_param[button_id].Get("toggle", false);
|
||||
buttons_param[button_id].Set("toggle", toggle_value);
|
||||
button_map[button_id]->setText(ButtonToText(buttons_param[button_id]));
|
||||
});
|
||||
context_menu.exec(button_map[button_id]->mapToGlobal(menu_location));
|
||||
ui->controllerFrame->SetPlayerInput(player_index, buttons_param, analogs_param);
|
||||
});
|
||||
@@ -412,6 +421,15 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
|
||||
analogs_param[analog_id].Set("modifier", "");
|
||||
analog_map_modifier_button[analog_id]->setText(tr("[not set]"));
|
||||
});
|
||||
context_menu.addAction(tr("Toggle button"), [&] {
|
||||
Common::ParamPackage modifier_param =
|
||||
Common::ParamPackage{analogs_param[analog_id].Get("modifier", "")};
|
||||
const bool toggle_value = !modifier_param.Get("toggle", false);
|
||||
modifier_param.Set("toggle", toggle_value);
|
||||
analogs_param[analog_id].Set("modifier", modifier_param.Serialize());
|
||||
analog_map_modifier_button[analog_id]->setText(
|
||||
ButtonToText(modifier_param));
|
||||
});
|
||||
context_menu.exec(
|
||||
analog_map_modifier_button[analog_id]->mapToGlobal(menu_location));
|
||||
});
|
||||
@@ -1345,7 +1363,8 @@ void ConfigureInputPlayer::mousePressEvent(QMouseEvent* event) {
|
||||
return;
|
||||
}
|
||||
|
||||
input_subsystem->GetMouse()->PressButton(0, 0, event->button());
|
||||
const auto button = GRenderWindow::QtButtonToMouseButton(event->button());
|
||||
input_subsystem->GetMouse()->PressButton(0, 0, button);
|
||||
}
|
||||
|
||||
void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) {
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
CalibrationConfigurationDialog::CalibrationConfigurationDialog(QWidget* parent,
|
||||
const std::string& host, u16 port,
|
||||
u8 pad_index, u16 client_id)
|
||||
u8 pad_index)
|
||||
: QDialog(parent) {
|
||||
layout = new QVBoxLayout;
|
||||
status_label = new QLabel(tr("Communicating with the server..."));
|
||||
@@ -41,7 +41,7 @@ CalibrationConfigurationDialog::CalibrationConfigurationDialog(QWidget* parent,
|
||||
|
||||
using namespace InputCommon::CemuhookUDP;
|
||||
job = std::make_unique<CalibrationConfigurationJob>(
|
||||
host, port, pad_index, client_id,
|
||||
host, port, pad_index,
|
||||
[this](CalibrationConfigurationJob::Status status) {
|
||||
QString text;
|
||||
switch (status) {
|
||||
@@ -218,7 +218,6 @@ void ConfigureMotionTouch::OnCemuhookUDPTest() {
|
||||
udp_test_in_progress = true;
|
||||
InputCommon::CemuhookUDP::TestCommunication(
|
||||
ui->udp_server->text().toStdString(), static_cast<u16>(ui->udp_port->text().toInt()), 0,
|
||||
24872,
|
||||
[this] {
|
||||
LOG_INFO(Frontend, "UDP input test success");
|
||||
QMetaObject::invokeMethod(this, "ShowUDPTestResult", Q_ARG(bool, true));
|
||||
@@ -233,8 +232,7 @@ void ConfigureMotionTouch::OnConfigureTouchCalibration() {
|
||||
ui->touch_calibration_config->setEnabled(false);
|
||||
ui->touch_calibration_config->setText(tr("Configuring"));
|
||||
CalibrationConfigurationDialog dialog(this, ui->udp_server->text().toStdString(),
|
||||
static_cast<u16>(ui->udp_port->text().toUInt()), 0,
|
||||
24872);
|
||||
static_cast<u16>(ui->udp_port->text().toUInt()), 0);
|
||||
dialog.exec();
|
||||
if (dialog.completed) {
|
||||
min_x = dialog.min_x;
|
||||
|
||||
@@ -30,7 +30,7 @@ class CalibrationConfigurationDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit CalibrationConfigurationDialog(QWidget* parent, const std::string& host, u16 port,
|
||||
u8 pad_index, u16 client_id);
|
||||
u8 pad_index);
|
||||
~CalibrationConfigurationDialog() override;
|
||||
|
||||
private:
|
||||
|
||||
+20
-8
@@ -60,6 +60,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
|
||||
#include <QPushButton>
|
||||
#include <QShortcut>
|
||||
#include <QStatusBar>
|
||||
#include <QString>
|
||||
#include <QSysInfo>
|
||||
#include <QUrl>
|
||||
#include <QtConcurrent/QtConcurrent>
|
||||
@@ -854,8 +855,7 @@ void GMainWindow::InitializeHotkeys() {
|
||||
connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Toggle Mouse Panning"), this),
|
||||
&QShortcut::activated, this, [&] {
|
||||
Settings::values.mouse_panning = !Settings::values.mouse_panning;
|
||||
if (UISettings::values.hide_mouse || Settings::values.mouse_panning) {
|
||||
mouse_hide_timer.start();
|
||||
if (Settings::values.mouse_panning) {
|
||||
render_window->installEventFilter(render_window);
|
||||
render_window->setAttribute(Qt::WA_Hover, true);
|
||||
}
|
||||
@@ -1208,11 +1208,14 @@ void GMainWindow::BootGame(const QString& filename, std::size_t program_index) {
|
||||
renderer_status_button->setDisabled(true);
|
||||
|
||||
if (UISettings::values.hide_mouse || Settings::values.mouse_panning) {
|
||||
mouse_hide_timer.start();
|
||||
render_window->installEventFilter(render_window);
|
||||
render_window->setAttribute(Qt::WA_Hover, true);
|
||||
}
|
||||
|
||||
if (UISettings::values.hide_mouse) {
|
||||
mouse_hide_timer.start();
|
||||
}
|
||||
|
||||
std::string title_name;
|
||||
std::string title_version;
|
||||
const auto res = system.GetGameName(title_name);
|
||||
@@ -2372,12 +2375,15 @@ void GMainWindow::OnConfigure() {
|
||||
if ((UISettings::values.hide_mouse || Settings::values.mouse_panning) && emulation_running) {
|
||||
render_window->installEventFilter(render_window);
|
||||
render_window->setAttribute(Qt::WA_Hover, true);
|
||||
mouse_hide_timer.start();
|
||||
} else {
|
||||
render_window->removeEventFilter(render_window);
|
||||
render_window->setAttribute(Qt::WA_Hover, false);
|
||||
}
|
||||
|
||||
if (UISettings::values.hide_mouse) {
|
||||
mouse_hide_timer.start();
|
||||
}
|
||||
|
||||
UpdateStatusButtons();
|
||||
}
|
||||
|
||||
@@ -2615,8 +2621,7 @@ void GMainWindow::UpdateUISettings() {
|
||||
}
|
||||
|
||||
void GMainWindow::HideMouseCursor() {
|
||||
if (emu_thread == nullptr ||
|
||||
(!UISettings::values.hide_mouse && !Settings::values.mouse_panning)) {
|
||||
if (emu_thread == nullptr && UISettings::values.hide_mouse) {
|
||||
mouse_hide_timer.stop();
|
||||
ShowMouseCursor();
|
||||
return;
|
||||
@@ -2626,8 +2631,7 @@ void GMainWindow::HideMouseCursor() {
|
||||
|
||||
void GMainWindow::ShowMouseCursor() {
|
||||
render_window->unsetCursor();
|
||||
if (emu_thread != nullptr &&
|
||||
(UISettings::values.hide_mouse || Settings::values.mouse_panning)) {
|
||||
if (emu_thread != nullptr && UISettings::values.hide_mouse) {
|
||||
mouse_hide_timer.start();
|
||||
}
|
||||
}
|
||||
@@ -3058,6 +3062,14 @@ int main(int argc, char* argv[]) {
|
||||
chdir(bin_path.c_str());
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
// Set the DISPLAY variable in order to open web browsers
|
||||
// TODO (lat9nq): Find a better solution for AppImages to start external applications
|
||||
if (QString::fromLocal8Bit(qgetenv("DISPLAY")).isEmpty()) {
|
||||
qputenv("DISPLAY", ":0");
|
||||
}
|
||||
#endif
|
||||
|
||||
// Enables the core to make the qt created contexts current on std::threads
|
||||
QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity);
|
||||
QApplication app(argc, argv);
|
||||
|
||||
@@ -35,18 +35,36 @@ void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
|
||||
input_subsystem->GetMouse()->MouseMove(x, y, 0, 0);
|
||||
}
|
||||
|
||||
MouseInput::MouseButton EmuWindow_SDL2::SDLButtonToMouseButton(u32 button) const {
|
||||
switch (button) {
|
||||
case SDL_BUTTON_LEFT:
|
||||
return MouseInput::MouseButton::Left;
|
||||
case SDL_BUTTON_RIGHT:
|
||||
return MouseInput::MouseButton::Right;
|
||||
case SDL_BUTTON_MIDDLE:
|
||||
return MouseInput::MouseButton::Wheel;
|
||||
case SDL_BUTTON_X1:
|
||||
return MouseInput::MouseButton::Backward;
|
||||
case SDL_BUTTON_X2:
|
||||
return MouseInput::MouseButton::Forward;
|
||||
default:
|
||||
return MouseInput::MouseButton::Undefined;
|
||||
}
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
|
||||
const auto mouse_button = SDLButtonToMouseButton(button);
|
||||
if (button == SDL_BUTTON_LEFT) {
|
||||
if (state == SDL_PRESSED) {
|
||||
TouchPressed((unsigned)std::max(x, 0), (unsigned)std::max(y, 0), 0);
|
||||
} else {
|
||||
TouchReleased(0);
|
||||
}
|
||||
} else if (button == SDL_BUTTON_RIGHT) {
|
||||
} else {
|
||||
if (state == SDL_PRESSED) {
|
||||
input_subsystem->GetMouse()->PressButton(x, y, button);
|
||||
input_subsystem->GetMouse()->PressButton(x, y, mouse_button);
|
||||
} else {
|
||||
input_subsystem->GetMouse()->ReleaseButton(button);
|
||||
input_subsystem->GetMouse()->ReleaseButton(mouse_button);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,10 @@ namespace InputCommon {
|
||||
class InputSubsystem;
|
||||
}
|
||||
|
||||
namespace MouseInput {
|
||||
enum class MouseButton;
|
||||
}
|
||||
|
||||
class EmuWindow_SDL2 : public Core::Frontend::EmuWindow {
|
||||
public:
|
||||
explicit EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem);
|
||||
@@ -42,6 +46,9 @@ protected:
|
||||
/// Called by WaitEvent when the mouse moves.
|
||||
void OnMouseMotion(s32 x, s32 y);
|
||||
|
||||
/// Converts a SDL mouse button into MouseInput mouse button
|
||||
MouseInput::MouseButton SDLButtonToMouseButton(u32 button) const;
|
||||
|
||||
/// Called by WaitEvent when a mouse button is pressed or released
|
||||
void OnMouseButton(u32 button, u8 state, s32 x, s32 y);
|
||||
|
||||
|
||||
Reference in new issue
Block a user