Compare commits

...
Author SHA1 Message Date
yuzubot fc9cd297b1 "Merge Tagged PR 1340" 2021-03-18 12:04:19 +00:00
yuzubot abe9abef3c "Merge Tagged PR 4836" 2021-03-18 12:04:17 +00:00
yuzubot 7436e56620 "Merge Tagged PR 5896" 2021-03-18 12:04:16 +00:00
bunnei 4aa8189328 Merge pull request #6055 from MerryMage/exceed-the-limit
[testing] fiber: Double default stack size
2021-03-17 20:46:45 -07:00
bunnei e1f7938a3b Merge pull request #6070 from Morph1984/sysver-11.0.1
system_version: Update to 11.0.1
2021-03-16 18:13:12 -07:00
bunnei ab102787fa Merge pull request #6083 from Morph1984/bsd-avoid-writebuffer
bsd: Avoid writing empty buffers
2021-03-16 11:59:05 -07:00
Morph 8441094ba3 bsd: Avoid writing empty buffers
Silences log spam on empty buffer writes
2021-03-16 12:50:44 -04:00
bunnei 0687a8370d Merge pull request #6069 from Morph1984/ngWord
system_archive: Update NgWord archive version
2021-03-15 15:44:04 -07:00
Morph 824e53149d system_version: Update to 11.0.1 2021-03-14 08:47:36 -04:00
Morph 9761618a8d system_archive: Update NgWord archive version 2021-03-14 08:33:48 -04:00
bunnei d3a4a192fe Merge pull request #6054 from Morph1984/time-GetClockSnapshot
time: Assign the current time point to the ClockSnapshot
2021-03-13 23:15:54 -08:00
bunnei 3b85ac2ac4 Merge pull request #6053 from Morph1984/time-CalculateSpanBetween
time: Fix CalculateSpanBetween implementation
2021-03-12 23:31:09 -08:00
bunnei 4735d18bb9 Merge pull request #6028 from bunnei/raster-cache
video_core: rasterizer_accelerated: Use a flat array instead of interval_map for cached pages.
2021-03-12 21:57:27 -08:00
bunnei a9d24b0df3 video_core: rasterizer_accelerated: Fix un/signed mismatch. 2021-03-12 21:52:49 -08:00
bunnei 5dae45b958 Merge pull request #5327 from AniLeo/master
qt: Set DISPLAY env var when not present
2021-03-12 12:10:28 -08:00
bunnei 827dcad26e Merge pull request #6040 from german77/toggleKeyboard
Enable toggle buttons for keyboard and mouse
2021-03-11 11:00:44 -08:00
MerryMage 59173ff7a7 fiber: Double default stack size
Stack overflow occurs with some guest applications
2021-03-10 20:37:56 +00:00
Morph 87cfe5b1da time: Fix CalculateSpanBetween implementation
CalculateSpanBetween passes in the ClockSnapshots through 2 input buffers and not as raw arguments. Fix this by reading the 2 input buffers instead of popping raw arguments.

Partially fixes Super Smash Bros. Ultimate's Spirit Board
2021-03-10 11:42:59 -05:00
Morph 2490ffbbce time: Assign the current time point to the ClockSnapshot
Fixes the timer in Super Smash Bros Ultimate's Spirit Board.
2021-03-10 11:40:51 -05:00
Rodrigo Locatti daf5c5060b Merge pull request #5891 from ameerj/bgra-ogl
renderer_opengl: Use compute shaders to swizzle BGR textures on copy
2021-03-09 02:47:51 -03:00
bunnei d1a7b2eca7 Merge pull request #6021 from ReinUsesLisp/skip-cache-heuristic
buffer_cache: Heuristically decide to skip cache on uniform buffers
2021-03-08 17:48:55 -08:00
bunnei 9c4c9f1e7d Merge pull request #5990 from german77/mousePanningV2
InputCommon: Mouse fixes
2021-03-08 14:50:58 -08:00
bunnei 69ce5e41eb Merge pull request #6041 from bunnei/fiber-leaks
common: fiber: Use weak_ptr when yielding.
2021-03-08 13:51:37 -08:00
Morph 9d77ae39de Merge pull request #6047 from lioncash/dynarmic
external: Update dynarmic
2021-03-08 00:52:19 -05:00
Lioncash de21c9e330 external: Update dynarmic 2021-03-08 00:44:54 -05:00
bunnei 8f7eb194af common: Fiber: use a reference for YieldTo.
- Fixes another small leak.
2021-03-07 13:46:53 -08:00
Ani cd7abba1a9 qt: Set DISPLAY env var when not present
Fixes web browser opening (Help > Open Mods Page, Help > Open Quickstart 
Guide)
2021-03-07 15:56:22 +00:00
german77 41e94b7b99 Enable mouse toggle buttons 2021-03-06 13:27:02 -06:00
german 4bcc5bacff Add toggle button option for normal buttons 2021-03-06 07:36:41 -06:00
bunnei 68ffac250a common: fiber: Use weak_ptr when yielding.
- Avoids a memory leak, as taking a strong reference of the fiber here causes a circular reference.
- Supersedes #6006 with a more narrow fix.
2021-03-05 22:10:03 -08:00
german 1f228c51ca Enable button toggle for keyboard in the modifier button 2021-03-05 19:21:04 -06:00
bunnei 4cf5b860bd Merge pull request #6036 from bunnei/thread-leak
hle: kernel: KThread: Rework dummy threads & fix memory leak.
2021-03-05 17:15:35 -08:00
bunnei 47af34003b hle: kernel: KThread: Rework dummy threads & fix memory leak.
- Dummy threads are created on thread local storage for all host threads.
- Fixes a leak by removing creation of fibers, which are not applicable here.
2021-03-05 17:10:57 -08:00
LC 97415ad07a Merge pull request #6029 from Morph1984/compile-utf8
CMakeLists: Add /utf-8 compile option for MSVC
2021-03-05 20:09:37 -05:00
bunnei 7b29a8ce4e Merge pull request #6039 from yuzu-emu/revert-6006-fiber-unique-ptr
Revert "core: Switch to unique_ptr for usage of Common::Fiber."
2021-03-05 17:08:48 -08:00
bunnei a5ab85ac37 Revert "core: Switch to unique_ptr for usage of Common::Fiber." 2021-03-05 17:08:17 -08:00
bunnei 9d010be483 Merge pull request #6034 from Morph1984/mbedtls
externals: Update mbedtls to 2.16.9
2021-03-05 15:48:28 -08:00
bunnei 34a3ee1631 Merge pull request #6006 from bunnei/fiber-unique-ptr
core: Switch to unique_ptr for usage of Common::Fiber.
2021-03-04 23:59:06 -08:00
Morph 96c9e67b1b aes_util: Remove malformed mbedtls_cipher_finish function call 2021-03-05 02:05:05 -05:00
Morph 6faabd6d69 externals: Update mbedtls to 2.16.9
mbedtls 2.16 is the last version which has licensing for GPL 2.0. This updates mbedtls to our own fork of mbedtls 2.16
2021-03-05 02:05:05 -05:00
Morph e7038344aa CMakeLists: Add /utf-8 compile option for MSVC
Ensures that the source and execution character sets are in UTF-8
2021-03-05 01:46:56 -05:00
ameerj 5213f70230 texture_cache: Blacklist BGRA8 copies and views on OpenGL
In order to force the BGRA8 conversion on Nvidia using OpenGL, we need to forbid texture copies and views with other formats.

This commit also adds a boolean relating to this, as this needs to be done only for the OpenGL api, Vulkan must remain unchanged.
2021-03-04 14:14:49 -05:00
ameerj 0639244d85 renderer_opengl: Swizzle BGR textures on copy
OpenGL does not natively support BGR internal formats, which causes many BGR textures to render incorrectly, with Red and Blue channels swapped.

This commit aims to address this by swizzling the blue and red channels on texture copies when a BGR format is encountered.
2021-03-04 14:14:19 -05:00
bunnei b8b5891585 Merge pull request #5989 from ReinUsesLisp/cmdpool
vk_command_pool: Reduce the command pool size from 4096 to 4
2021-03-04 11:07:31 -08:00
bunnei 394475c4e3 Merge pull request #6004 from german77/udprandom
InputCommon: Use an unique client id for each udp socket instance
2021-03-03 15:45:32 -08:00
bunnei 50ee9c46ab video_core: rasterizer_accelerated: Fix delta check ordering. 2021-03-02 17:48:02 -08:00
bunnei 6ab839462c video_core: rasterizer_accelerated: Improve error handling & fix implicit conversion. 2021-03-02 17:44:02 -08:00
bunnei f8bfec3109 Merge pull request #5815 from comex/net-error-reform
Network error handling reform
2021-03-02 17:08:47 -08:00
bunnei 94da1e8a7e video_core: rasterizer_accelerated: Use a flat array instead of interval_map for cached pages.
- Uses a fixed 64MB for the cache instead of an ever growing map.
- Slightly faster by using atomics instead of a single mutex for access.
- Thanks for Rodrigo for the idea.
2021-03-02 16:57:53 -08:00
LC 4a45012f35 Merge pull request #6020 from bunnei/shutdown-crash-2
core: Shutdown: Move kernel cleanup to later in shutdown.
2021-03-02 09:28:28 -05:00
ReinUsesLisp 5ad62e7bfc buffer_cache: Heuristically decide to skip cache on uniform buffers
Some games benefit from skipping caches (Pokémon Sword), and others
don't (Animal Crossing: New Horizons). Add an heuristic to decide this
at runtime.

The cache hit ratio has to be ~98% or better to not skip the cache.
There are 16 frames of buffer.
2021-03-02 02:44:19 -03:00
bunnei 925671071c core: Shutdown: Move kernel cleanup to later in shutdown.
- Fixes a shutdown crash due to a race condition with GPU still accessing memory.
2021-03-01 21:42:06 -08:00
german 9b3af0027b inputCommon: Use an unique client id for each socket instance 2021-03-01 09:19:33 -06:00
comex 2910aa77b2 [network] Error handling reform
`network.cpp` has several error paths which either:
- report "Unhandled host socket error=n" and return `SUCCESS`, or
- switch on a few possible errors, log them, and translate them to
  Errno; the same switch statement is copied and pasted in multiple
  places in the code

Convert these paths to use a helper function `GetAndLogLastError`, which
is roughly the equivalent of one of the switch statements, but:
- handling more cases (both ones that were already in `Errno`, and a few
  more I added), and
- using OS functions to convert the error to a string when logging, so
  it'll describe the error even if it's not one of the ones in the
  switch statement.
  - To handle this, refactor the logic in `GetLastErrorMsg` to expose a
    new function `NativeErrorToString` which takes the error number
    explicitly as an argument.  And improve the Windows version a bit.

Also, add a test which exercises two random error paths.
2021-02-28 17:25:31 -05:00
german77 4738e14cb0 inputCommon: Mouse fixes 2021-02-27 17:53:10 -06:00
bunnei 51fb0a6f96 core: Switch to unique_ptr for usage of Common::Fiber.
- With using unique_ptr instead of shared_ptr, we have more explicit ownership of the context.
- Fixes a memory leak due to circular reference of the shared pointer.
2021-02-27 11:56:04 -08:00
ReinUsesLisp aae399c1a8 vk_command_pool: Reduce the command pool size from 4096 to 4
This allows drivers to reuse memory more easily and preallocate less.
The optimal number has been measured booting Pokémon Sword.
2021-02-23 19:08:24 -03:00
84 changed files with 1471 additions and 588 deletions

No files matched your search

+1 -1
View File
@@ -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
+2
View File
@@ -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
View File
@@ -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_)
+5 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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");
+4 -4
View File
@@ -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();
-2
View File
@@ -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>
+2 -2
View File
@@ -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
+6 -11
View File
@@ -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();
}
+31 -19
View File
@@ -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();
}
+20 -4
View File
@@ -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;
}
+5 -6
View File
@@ -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();
}
+3 -2
View File
@@ -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();
+3 -2
View File
@@ -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) {
+6 -2
View File
@@ -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);
+11 -3
View File
@@ -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) {
+78 -95
View File
@@ -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 {
+6
View File
@@ -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
+28 -3
View File
@@ -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;
}
+44 -3
View File
@@ -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;
}
+12 -5
View File
@@ -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};
+15 -5
View File
@@ -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 {
+16 -10
View File
@@ -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();
+3 -3
View File
@@ -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);
+1
View File
@@ -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
View File
@@ -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();
+28
View File
@@ -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);
}
+35 -10
View File
@@ -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>
+42 -7
View File
@@ -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
+3 -2
View File
@@ -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
+4
View File
@@ -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);
}
+19
View File
@@ -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)];
}
+8
View File
@@ -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);
+20 -40
View File
@@ -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);
}
}
}
+19 -6
View File
@@ -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;
};
+6
View File
@@ -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 {
+109 -52
View File
@@ -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
+9 -4
View File
@@ -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
+157 -68
View File
@@ -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);
}
}
+11
View File
@@ -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());
}
+14 -1
View File
@@ -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:
+5
View File
@@ -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);
}
+3
View File
@@ -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);
+70 -25
View File
@@ -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;
+3 -2
View File
@@ -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();
+9 -6
View File
@@ -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);
}
});
+7 -6
View File
@@ -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,
+5 -4
View File
@@ -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);
+29 -4
View File
@@ -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);
+7
View File
@@ -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;
+2 -3
View File
@@ -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
View File
@@ -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);
+21 -3
View File
@@ -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);