Compare commits
63 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
87cfe5b1da | ||
|
|
daf5c5060b | ||
|
|
d1a7b2eca7 | ||
|
|
9c4c9f1e7d | ||
|
|
69ce5e41eb | ||
|
|
9d77ae39de | ||
|
|
de21c9e330 | ||
|
|
8f7eb194af | ||
|
|
68ffac250a | ||
|
|
4cf5b860bd | ||
|
|
47af34003b | ||
|
|
97415ad07a | ||
|
|
7b29a8ce4e | ||
|
|
a5ab85ac37 | ||
|
|
9d010be483 | ||
|
|
34a3ee1631 | ||
|
|
96c9e67b1b | ||
|
|
6faabd6d69 | ||
|
|
e7038344aa | ||
|
|
5213f70230 | ||
|
|
0639244d85 | ||
|
|
b8b5891585 | ||
|
|
394475c4e3 | ||
|
|
f8bfec3109 | ||
|
|
4a45012f35 | ||
|
|
5ad62e7bfc | ||
|
|
925671071c | ||
|
|
cd25817938 | ||
|
|
c7a7e47615 | ||
|
|
9b3af0027b | ||
|
|
ac8b1445ff | ||
|
|
52e9d7fa49 | ||
|
|
2910aa77b2 | ||
|
|
9e9341f4b4 | ||
|
|
ee9ebeeb80 | ||
|
|
e895ab7d6f | ||
|
|
4738e14cb0 | ||
|
|
55f556c53e | ||
|
|
ab65cb499d | ||
|
|
51fb0a6f96 | ||
|
|
09f7c355c6 | ||
|
|
bfa1644464 | ||
|
|
272bc4c3d6 | ||
|
|
1ba578c4aa | ||
|
|
d31dbb1bc1 | ||
|
|
aae399c1a8 | ||
|
|
1841ca4b9b | ||
|
|
71526ecfc7 | ||
|
|
ae876ed047 | ||
|
|
fb0b4c7e27 | ||
|
|
20245e660f | ||
|
|
ec19a85890 | ||
|
|
3de8e7a8f2 | ||
|
|
3d0394681c | ||
|
|
8e4c9c9852 | ||
|
|
2807a98168 | ||
|
|
1a5d4d7840 | ||
|
|
def03d4075 | ||
|
|
3acb265c9e | ||
|
|
728ee181eb | ||
|
|
9d8f793969 | ||
|
|
fb54c38631 | ||
|
|
21b40de318 |
18
.ci/scripts/clang/docker.sh
Executable file
18
.ci/scripts/clang/docker.sh
Executable file
@@ -0,0 +1,18 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
# Exit on error, rather than continuing with the rest of the script.
|
||||
set -e
|
||||
|
||||
cd /yuzu
|
||||
|
||||
ccache -s
|
||||
|
||||
mkdir build || true && cd build
|
||||
cmake .. -DDISPLAY_VERSION=$1 -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/clang -DCMAKE_CXX_COMPILER=/usr/lib/ccache/clang++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DCMAKE_INSTALL_PREFIX="/usr"
|
||||
|
||||
make -j$(nproc)
|
||||
|
||||
ccache -s
|
||||
|
||||
ctest -VV -C Release
|
||||
|
||||
8
.ci/scripts/clang/exec.sh
Normal file
8
.ci/scripts/clang/exec.sh
Normal file
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
mkdir -p "ccache" || true
|
||||
chmod a+x ./.ci/scripts/clang/docker.sh
|
||||
# the UID for the container yuzu user is 1027
|
||||
sudo chown -R 1027 ./
|
||||
docker run -e ENABLE_COMPATIBILITY_REPORTING -e CCACHE_DIR=/yuzu/ccache -v $(pwd):/yuzu yuzuemu/build-environments:linux-fresh /bin/bash /yuzu/.ci/scripts/clang/docker.sh $1
|
||||
sudo chown -R $UID ./
|
||||
20
.ci/scripts/clang/upload.sh
Normal file
20
.ci/scripts/clang/upload.sh
Normal file
@@ -0,0 +1,20 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
. .ci/scripts/common/pre-upload.sh
|
||||
|
||||
REV_NAME="yuzu-linux-${GITDATE}-${GITREV}"
|
||||
ARCHIVE_NAME="${REV_NAME}.tar.xz"
|
||||
COMPRESSION_FLAGS="-cJvf"
|
||||
|
||||
if [ "${RELEASE_NAME}" = "mainline" ]; then
|
||||
DIR_NAME="${REV_NAME}"
|
||||
else
|
||||
DIR_NAME="${REV_NAME}_${RELEASE_NAME}"
|
||||
fi
|
||||
|
||||
mkdir "$DIR_NAME"
|
||||
|
||||
cp build/bin/yuzu-cmd "$DIR_NAME"
|
||||
cp build/bin/yuzu "$DIR_NAME"
|
||||
|
||||
. .ci/scripts/common/post-upload.sh
|
||||
@@ -12,6 +12,9 @@ jobs:
|
||||
windows:
|
||||
BuildSuffix: 'windows-mingw'
|
||||
ScriptFolder: 'windows'
|
||||
clang:
|
||||
BuildSuffix: 'clang'
|
||||
ScriptFolder: 'clang'
|
||||
linux:
|
||||
BuildSuffix: 'linux'
|
||||
ScriptFolder: 'linux'
|
||||
@@ -24,4 +27,4 @@ jobs:
|
||||
parameters:
|
||||
artifactSource: 'false'
|
||||
cache: $(parameters.cache)
|
||||
version: $(parameters.version)
|
||||
version: $(parameters.version)
|
||||
|
||||
2
.gitmodules
vendored
2
.gitmodules
vendored
@@ -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
externals/dynarmic
vendored
2
externals/dynarmic
vendored
Submodule externals/dynarmic updated: cafa687684...646fd05920
3
externals/glad/include/glad/glad.h
vendored
3
externals/glad/include/glad/glad.h
vendored
@@ -5156,6 +5156,9 @@ GLAPI PFNGLDEPTHRANGEARRAYVPROC glad_glDepthRangeArrayv;
|
||||
typedef void (APIENTRYP PFNGLDEPTHRANGEINDEXEDPROC)(GLuint index, GLdouble n, GLdouble f);
|
||||
GLAPI PFNGLDEPTHRANGEINDEXEDPROC glad_glDepthRangeIndexed;
|
||||
#define glDepthRangeIndexed glad_glDepthRangeIndexed
|
||||
typedef void (APIENTRYP PFNGLDEPTHRANGEINDEXEDDNVPROC)(GLuint index, GLdouble n, GLdouble f);
|
||||
GLAPI PFNGLDEPTHRANGEINDEXEDDNVPROC glad_glDepthRangeIndexeddNV;
|
||||
#define glDepthRangeIndexeddNV glad_glDepthRangeIndexeddNV
|
||||
typedef void (APIENTRYP PFNGLGETFLOATI_VPROC)(GLenum target, GLuint index, GLfloat *data);
|
||||
GLAPI PFNGLGETFLOATI_VPROC glad_glGetFloati_v;
|
||||
#define glGetFloati_v glad_glGetFloati_v
|
||||
|
||||
2
externals/glad/src/glad.c
vendored
2
externals/glad/src/glad.c
vendored
@@ -1044,6 +1044,7 @@ PFNGLDEPTHMASKPROC glad_glDepthMask = NULL;
|
||||
PFNGLDEPTHRANGEPROC glad_glDepthRange = NULL;
|
||||
PFNGLDEPTHRANGEARRAYVPROC glad_glDepthRangeArrayv = NULL;
|
||||
PFNGLDEPTHRANGEINDEXEDPROC glad_glDepthRangeIndexed = NULL;
|
||||
PFNGLDEPTHRANGEINDEXEDDNVPROC glad_glDepthRangeIndexeddNV = NULL;
|
||||
PFNGLDEPTHRANGEFPROC glad_glDepthRangef = NULL;
|
||||
PFNGLDETACHSHADERPROC glad_glDetachShader = NULL;
|
||||
PFNGLDISABLEPROC glad_glDisable = NULL;
|
||||
@@ -7971,6 +7972,7 @@ static void load_GL_NV_depth_buffer_float(GLADloadproc load) {
|
||||
glad_glDepthRangedNV = (PFNGLDEPTHRANGEDNVPROC)load("glDepthRangedNV");
|
||||
glad_glClearDepthdNV = (PFNGLCLEARDEPTHDNVPROC)load("glClearDepthdNV");
|
||||
glad_glDepthBoundsdNV = (PFNGLDEPTHBOUNDSDNVPROC)load("glDepthBoundsdNV");
|
||||
glad_glDepthRangeIndexeddNV = (PFNGLDEPTHRANGEINDEXEDDNVPROC)load("glDepthRangeIndexeddNV");
|
||||
}
|
||||
static void load_GL_NV_draw_texture(GLADloadproc load) {
|
||||
if(!GLAD_GL_NV_draw_texture) return;
|
||||
|
||||
2
externals/mbedtls
vendored
2
externals/mbedtls
vendored
Submodule externals/mbedtls updated: a280e602f3...eac2416b8f
@@ -27,6 +27,7 @@ if (MSVC)
|
||||
# /Zo - Enhanced debug info for optimized builds
|
||||
# /permissive- - Enables stricter C++ standards conformance checks
|
||||
# /EHsc - C++-only exception handling semantics
|
||||
# /utf-8 - Set source and execution character sets to UTF-8
|
||||
# /volatile:iso - Use strict standards-compliant volatile semantics.
|
||||
# /Zc:externConstexpr - Allow extern constexpr variables to have external linkage, like the standard mandates
|
||||
# /Zc:inline - Let codegen omit inline functions in object files
|
||||
@@ -38,6 +39,7 @@ if (MSVC)
|
||||
/permissive-
|
||||
/EHsc
|
||||
/std:c++latest
|
||||
/utf-8
|
||||
/volatile:iso
|
||||
/Zc:externConstexpr
|
||||
/Zc:inline
|
||||
|
||||
@@ -61,6 +61,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
@@ -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>; \
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -98,4 +98,24 @@ namespace Common {
|
||||
#endif
|
||||
}
|
||||
|
||||
// This function divides a u128 by a u32 value and produces two u64 values:
|
||||
// the result of division and the remainder
|
||||
[[nodiscard]] static inline std::pair<u64, u64> Divide128On32(u128 dividend, u32 divisor) {
|
||||
u64 remainder = dividend[0] % divisor;
|
||||
u64 accum = dividend[0] / divisor;
|
||||
if (dividend[1] == 0)
|
||||
return {accum, remainder};
|
||||
// We ignore dividend[1] / divisor as that overflows
|
||||
const u64 first_segment = (dividend[1] % divisor) << 32;
|
||||
accum += (first_segment / divisor) << 32;
|
||||
const u64 second_segment = (first_segment % divisor) << 32;
|
||||
accum += (second_segment / divisor);
|
||||
remainder += second_segment % divisor;
|
||||
if (remainder >= divisor) {
|
||||
accum++;
|
||||
remainder -= divisor;
|
||||
}
|
||||
return {accum, remainder};
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -20,9 +20,7 @@ using base_time_point = std::chrono::time_point<base_timer>;
|
||||
class StandardWallClock final : public WallClock {
|
||||
public:
|
||||
explicit StandardWallClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequency_)
|
||||
: WallClock(emulated_cpu_frequency_, emulated_clock_frequency_, false),
|
||||
emulated_clock_factor{GetFixedPoint64Factor(emulated_clock_frequency, 1000000000)},
|
||||
emulated_cpu_factor{GetFixedPoint64Factor(emulated_cpu_frequency, 1000000000)} {
|
||||
: WallClock(emulated_cpu_frequency_, emulated_clock_frequency_, false) {
|
||||
start_time = base_timer::now();
|
||||
}
|
||||
|
||||
@@ -45,11 +43,16 @@ public:
|
||||
}
|
||||
|
||||
u64 GetClockCycles() override {
|
||||
return MultiplyHigh(GetTimeNS().count(), emulated_clock_factor);
|
||||
std::chrono::nanoseconds time_now = GetTimeNS();
|
||||
const u128 temporary =
|
||||
Common::Multiply64Into128(time_now.count(), emulated_clock_frequency);
|
||||
return Common::Divide128On32(temporary, 1000000000).first;
|
||||
}
|
||||
|
||||
u64 GetCPUCycles() override {
|
||||
return MultiplyHigh(GetTimeNS().count(), emulated_cpu_factor);
|
||||
std::chrono::nanoseconds time_now = GetTimeNS();
|
||||
const u128 temporary = Common::Multiply64Into128(time_now.count(), emulated_cpu_frequency);
|
||||
return Common::Divide128On32(temporary, 1000000000).first;
|
||||
}
|
||||
|
||||
void Pause([[maybe_unused]] bool is_paused) override {
|
||||
@@ -58,8 +61,6 @@ public:
|
||||
|
||||
private:
|
||||
base_time_point start_time;
|
||||
const u64 emulated_clock_factor;
|
||||
const u64 emulated_cpu_factor;
|
||||
};
|
||||
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
|
||||
@@ -299,25 +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();
|
||||
|
||||
// Shutdown kernel and core timing
|
||||
time_manager.Shutdown();
|
||||
core_timing.Shutdown();
|
||||
kernel.Shutdown();
|
||||
|
||||
// Close app loader
|
||||
app_loader.reset();
|
||||
gpu_core.reset();
|
||||
perf_stats.reset();
|
||||
|
||||
// Clear all applets
|
||||
kernel.Shutdown();
|
||||
applet_manager.ClearAll();
|
||||
|
||||
LOG_DEBUG(Core, "Shutdown OK");
|
||||
|
||||
@@ -148,7 +148,7 @@ void CpuManager::MultiCoreRunSuspendThread() {
|
||||
auto core = kernel.GetCurrentHostThreadID();
|
||||
auto& scheduler = *kernel.CurrentScheduler();
|
||||
Kernel::KThread* current_thread = scheduler.GetCurrentThread();
|
||||
Common::Fiber::YieldTo(current_thread->GetHostContext(), core_data[core].host_context);
|
||||
Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[core].host_context);
|
||||
ASSERT(scheduler.ContextSwitchPending());
|
||||
ASSERT(core == kernel.GetCurrentHostThreadID());
|
||||
scheduler.RescheduleCurrentCore();
|
||||
@@ -245,7 +245,7 @@ void CpuManager::SingleCoreRunSuspendThread() {
|
||||
auto core = kernel.GetCurrentHostThreadID();
|
||||
auto& scheduler = *kernel.CurrentScheduler();
|
||||
Kernel::KThread* current_thread = scheduler.GetCurrentThread();
|
||||
Common::Fiber::YieldTo(current_thread->GetHostContext(), core_data[0].host_context);
|
||||
Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[0].host_context);
|
||||
ASSERT(scheduler.ContextSwitchPending());
|
||||
ASSERT(core == kernel.GetCurrentHostThreadID());
|
||||
scheduler.RescheduleCurrentCore();
|
||||
@@ -271,7 +271,7 @@ void CpuManager::PreemptSingleCore(bool from_running_enviroment) {
|
||||
scheduler.Unload(scheduler.GetCurrentThread());
|
||||
|
||||
auto& next_scheduler = kernel.Scheduler(current_core);
|
||||
Common::Fiber::YieldTo(current_thread->GetHostContext(), next_scheduler.ControlContext());
|
||||
Common::Fiber::YieldTo(current_thread->GetHostContext(), *next_scheduler.ControlContext());
|
||||
}
|
||||
|
||||
// May have changed scheduler
|
||||
@@ -363,7 +363,7 @@ void CpuManager::RunThread(std::size_t core) {
|
||||
|
||||
auto current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread();
|
||||
data.is_running = true;
|
||||
Common::Fiber::YieldTo(data.host_context, current_thread->GetHostContext());
|
||||
Common::Fiber::YieldTo(data.host_context, *current_thread->GetHostContext());
|
||||
data.is_running = false;
|
||||
data.is_paused = true;
|
||||
data.exit_barrier->Wait();
|
||||
|
||||
@@ -105,8 +105,6 @@ void AESCipher<Key, KeySize>::Transcode(const u8* src, std::size_t size, u8* des
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mbedtls_cipher_finish(context, nullptr, nullptr);
|
||||
}
|
||||
|
||||
template <typename Key, std::size_t KeySize>
|
||||
|
||||
@@ -734,7 +734,7 @@ void KScheduler::ScheduleImpl() {
|
||||
}
|
||||
guard.unlock();
|
||||
|
||||
Common::Fiber::YieldTo(*old_context, switch_fiber);
|
||||
Common::Fiber::YieldTo(*old_context, *switch_fiber);
|
||||
/// When a thread wakes up, the scheduler may have changed to other in another core.
|
||||
auto& next_scheduler = *system.Kernel().CurrentScheduler();
|
||||
next_scheduler.SwitchContextStep2();
|
||||
@@ -769,13 +769,8 @@ void KScheduler::SwitchToCurrent() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::shared_ptr<Common::Fiber>* next_context;
|
||||
if (next_thread != nullptr) {
|
||||
next_context = &next_thread->GetHostContext();
|
||||
} else {
|
||||
next_context = &idle_thread->GetHostContext();
|
||||
}
|
||||
Common::Fiber::YieldTo(switch_fiber, *next_context);
|
||||
auto thread = next_thread ? next_thread : idle_thread;
|
||||
Common::Fiber::YieldTo(switch_fiber, *thread->GetHostContext());
|
||||
} while (!is_switch_pending());
|
||||
}
|
||||
}
|
||||
@@ -800,9 +795,9 @@ void KScheduler::Initialize() {
|
||||
std::string name = "Idle Thread Id:" + std::to_string(core_id);
|
||||
std::function<void(void*)> init_func = Core::CpuManager::GetIdleThreadStartFunc();
|
||||
void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
|
||||
auto thread_res = KThread::Create(system, ThreadType::Main, name, 0,
|
||||
KThread::IdleThreadPriority, 0, static_cast<u32>(core_id), 0,
|
||||
nullptr, std::move(init_func), init_func_parameter);
|
||||
auto thread_res = KThread::CreateThread(
|
||||
system, ThreadType::Main, name, 0, KThread::IdleThreadPriority, 0,
|
||||
static_cast<u32>(core_id), 0, nullptr, std::move(init_func), init_func_parameter);
|
||||
idle_thread = thread_res.Unwrap().get();
|
||||
}
|
||||
|
||||
|
||||
@@ -995,22 +995,11 @@ std::shared_ptr<Common::Fiber>& KThread::GetHostContext() {
|
||||
return host_context;
|
||||
}
|
||||
|
||||
ResultVal<std::shared_ptr<KThread>> KThread::Create(Core::System& system, ThreadType type_flags,
|
||||
std::string name, VAddr entry_point,
|
||||
u32 priority, u64 arg, s32 processor_id,
|
||||
VAddr stack_top, Process* owner_process) {
|
||||
std::function<void(void*)> init_func = Core::CpuManager::GetGuestThreadStartFunc();
|
||||
void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
|
||||
return Create(system, type_flags, name, entry_point, priority, arg, processor_id, stack_top,
|
||||
owner_process, std::move(init_func), init_func_parameter);
|
||||
}
|
||||
|
||||
ResultVal<std::shared_ptr<KThread>> KThread::Create(Core::System& system, ThreadType type_flags,
|
||||
std::string name, VAddr entry_point,
|
||||
u32 priority, u64 arg, s32 processor_id,
|
||||
VAddr stack_top, Process* owner_process,
|
||||
std::function<void(void*)>&& thread_start_func,
|
||||
void* thread_start_parameter) {
|
||||
ResultVal<std::shared_ptr<KThread>> KThread::CreateThread(Core::System& system,
|
||||
ThreadType type_flags, std::string name,
|
||||
VAddr entry_point, u32 priority, u64 arg,
|
||||
s32 processor_id, VAddr stack_top,
|
||||
Process* owner_process) {
|
||||
auto& kernel = system.Kernel();
|
||||
|
||||
std::shared_ptr<KThread> thread = std::make_shared<KThread>(kernel);
|
||||
@@ -1027,12 +1016,35 @@ ResultVal<std::shared_ptr<KThread>> KThread::Create(Core::System& system, Thread
|
||||
auto& scheduler = kernel.GlobalSchedulerContext();
|
||||
scheduler.AddThread(thread);
|
||||
|
||||
thread->host_context =
|
||||
std::make_shared<Common::Fiber>(std::move(thread_start_func), thread_start_parameter);
|
||||
|
||||
return MakeResult<std::shared_ptr<KThread>>(std::move(thread));
|
||||
}
|
||||
|
||||
ResultVal<std::shared_ptr<KThread>> KThread::CreateThread(
|
||||
Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point, u32 priority,
|
||||
u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process,
|
||||
std::function<void(void*)>&& thread_start_func, void* thread_start_parameter) {
|
||||
auto thread_result = CreateThread(system, type_flags, name, entry_point, priority, arg,
|
||||
processor_id, stack_top, owner_process);
|
||||
|
||||
if (thread_result.Succeeded()) {
|
||||
(*thread_result)->host_context =
|
||||
std::make_shared<Common::Fiber>(std::move(thread_start_func), thread_start_parameter);
|
||||
}
|
||||
|
||||
return thread_result;
|
||||
}
|
||||
|
||||
ResultVal<std::shared_ptr<KThread>> KThread::CreateUserThread(
|
||||
Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point, u32 priority,
|
||||
u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process) {
|
||||
std::function<void(void*)> init_func = Core::CpuManager::GetGuestThreadStartFunc();
|
||||
|
||||
void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
|
||||
|
||||
return CreateThread(system, type_flags, name, entry_point, priority, arg, processor_id,
|
||||
stack_top, owner_process, std::move(init_func), init_func_parameter);
|
||||
}
|
||||
|
||||
KThread* GetCurrentThreadPointer(KernelCore& kernel) {
|
||||
return kernel.GetCurrentEmuThread();
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ public:
|
||||
using WaiterList = boost::intrusive::list<KThread>;
|
||||
|
||||
/**
|
||||
* Creates and returns a new thread. The new thread is immediately scheduled
|
||||
* Creates and returns a new thread.
|
||||
* @param system The instance of the whole system
|
||||
* @param name The friendly name desired for the thread
|
||||
* @param entry_point The address at which the thread should start execution
|
||||
@@ -127,12 +127,12 @@ public:
|
||||
* @param owner_process The parent process for the thread, if null, it's a kernel thread
|
||||
* @return A shared pointer to the newly created thread
|
||||
*/
|
||||
[[nodiscard]] static ResultVal<std::shared_ptr<KThread>> Create(
|
||||
[[nodiscard]] static ResultVal<std::shared_ptr<KThread>> CreateThread(
|
||||
Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point,
|
||||
u32 priority, u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process);
|
||||
|
||||
/**
|
||||
* Creates and returns a new thread. The new thread is immediately scheduled
|
||||
* Creates and returns a new thread, with a specified entry point.
|
||||
* @param system The instance of the whole system
|
||||
* @param name The friendly name desired for the thread
|
||||
* @param entry_point The address at which the thread should start execution
|
||||
@@ -145,11 +145,27 @@ public:
|
||||
* @param thread_start_parameter The parameter which will passed to host context on init
|
||||
* @return A shared pointer to the newly created thread
|
||||
*/
|
||||
[[nodiscard]] static ResultVal<std::shared_ptr<KThread>> Create(
|
||||
[[nodiscard]] static ResultVal<std::shared_ptr<KThread>> CreateThread(
|
||||
Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point,
|
||||
u32 priority, u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process,
|
||||
std::function<void(void*)>&& thread_start_func, void* thread_start_parameter);
|
||||
|
||||
/**
|
||||
* Creates and returns a new thread for the emulated "user" process.
|
||||
* @param system The instance of the whole system
|
||||
* @param name The friendly name desired for the thread
|
||||
* @param entry_point The address at which the thread should start execution
|
||||
* @param priority The thread's priority
|
||||
* @param arg User data to pass to the thread
|
||||
* @param processor_id The ID(s) of the processors on which the thread is desired to be run
|
||||
* @param stack_top The address of the thread's stack top
|
||||
* @param owner_process The parent process for the thread, if null, it's a kernel thread
|
||||
* @return A shared pointer to the newly created thread
|
||||
*/
|
||||
[[nodiscard]] static ResultVal<std::shared_ptr<KThread>> CreateUserThread(
|
||||
Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point,
|
||||
u32 priority, u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process);
|
||||
|
||||
[[nodiscard]] std::string GetName() const override {
|
||||
return name;
|
||||
}
|
||||
|
||||
@@ -101,8 +101,6 @@ struct KernelCore::Impl {
|
||||
|
||||
current_process = nullptr;
|
||||
|
||||
system_resource_limit = nullptr;
|
||||
|
||||
global_handle_table.Clear();
|
||||
|
||||
preemption_event = nullptr;
|
||||
@@ -111,6 +109,13 @@ struct KernelCore::Impl {
|
||||
|
||||
exclusive_monitor.reset();
|
||||
|
||||
hid_shared_mem = nullptr;
|
||||
font_shared_mem = nullptr;
|
||||
irs_shared_mem = nullptr;
|
||||
time_shared_mem = nullptr;
|
||||
|
||||
system_resource_limit = nullptr;
|
||||
|
||||
// Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others
|
||||
next_host_thread_id = Core::Hardware::NUM_CPU_CORES;
|
||||
}
|
||||
@@ -176,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();
|
||||
}
|
||||
@@ -216,10 +221,9 @@ struct KernelCore::Impl {
|
||||
// Gets the dummy KThread for the caller, allocating a new one if this is the first time
|
||||
KThread* GetHostDummyThread() {
|
||||
const thread_local auto thread =
|
||||
KThread::Create(
|
||||
KThread::CreateThread(
|
||||
system, ThreadType::Main, fmt::format("DummyThread:{}", GetHostThreadId()), 0,
|
||||
KThread::DefaultThreadPriority, 0, static_cast<u32>(3), 0, nullptr,
|
||||
[]([[maybe_unused]] void* arg) { UNREACHABLE(); }, nullptr)
|
||||
KThread::DefaultThreadPriority, 0, static_cast<u32>(3), 0, nullptr)
|
||||
.Unwrap();
|
||||
return thread.get();
|
||||
}
|
||||
|
||||
@@ -40,8 +40,9 @@ namespace {
|
||||
void SetupMainThread(Core::System& system, Process& owner_process, u32 priority, VAddr stack_top) {
|
||||
const VAddr entry_point = owner_process.PageTable().GetCodeRegionStart();
|
||||
ASSERT(owner_process.GetResourceLimit()->Reserve(LimitableResource::Threads, 1));
|
||||
auto thread_res = KThread::Create(system, ThreadType::User, "main", entry_point, priority, 0,
|
||||
owner_process.GetIdealCoreId(), stack_top, &owner_process);
|
||||
auto thread_res =
|
||||
KThread::CreateUserThread(system, ThreadType::User, "main", entry_point, priority, 0,
|
||||
owner_process.GetIdealCoreId(), stack_top, &owner_process);
|
||||
|
||||
std::shared_ptr<KThread> thread = std::move(thread_res).Unwrap();
|
||||
|
||||
|
||||
@@ -1532,8 +1532,9 @@ static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr e
|
||||
std::shared_ptr<KThread> thread;
|
||||
{
|
||||
KScopedLightLock lk{process.GetStateLock()};
|
||||
CASCADE_RESULT(thread, KThread::Create(system, ThreadType::User, "", entry_point, priority,
|
||||
arg, core_id, stack_bottom, &process));
|
||||
CASCADE_RESULT(thread,
|
||||
KThread::CreateUserThread(system, ThreadType::User, "", entry_point,
|
||||
priority, arg, core_id, stack_bottom, &process));
|
||||
}
|
||||
|
||||
const auto new_thread_handle = process.GetHandleTable().Create(thread);
|
||||
|
||||
@@ -508,7 +508,7 @@ public:
|
||||
{1, &IManagerForApplication::GetAccountId, "GetAccountId"},
|
||||
{2, nullptr, "EnsureIdTokenCacheAsync"},
|
||||
{3, nullptr, "LoadIdTokenCache"},
|
||||
{130, nullptr, "GetNintendoAccountUserResourceCacheForApplication"},
|
||||
{130, &IManagerForApplication::GetNintendoAccountUserResourceCacheForApplication, "GetNintendoAccountUserResourceCacheForApplication"},
|
||||
{150, nullptr, "CreateAuthorizationRequest"},
|
||||
{160, &IManagerForApplication::StoreOpenContext, "StoreOpenContext"},
|
||||
{170, nullptr, "LoadNetworkServiceLicenseKindAsync"},
|
||||
@@ -534,6 +534,22 @@ private:
|
||||
rb.PushRaw<u64>(user_id.GetNintendoID());
|
||||
}
|
||||
|
||||
void GetNintendoAccountUserResourceCacheForApplication(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called");
|
||||
|
||||
std::vector<u8> nas_user_base_for_application(0x68);
|
||||
ctx.WriteBuffer(nas_user_base_for_application, 0);
|
||||
|
||||
if (ctx.CanWriteBuffer(1)) {
|
||||
std::vector<u8> unknown_out_buffer(ctx.GetWriteBufferSize(1));
|
||||
ctx.WriteBuffer(unknown_out_buffer, 1);
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw<u64>(user_id.GetNintendoID());
|
||||
}
|
||||
|
||||
void StoreOpenContext(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
|
||||
@@ -5,15 +5,25 @@
|
||||
#include <cstring>
|
||||
#include "common/common_types.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/frontend/emu_window.h"
|
||||
#include "core/hle/service/hid/controllers/gesture.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace Service::HID {
|
||||
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3BA00;
|
||||
constexpr f32 angle_threshold = 0.08f;
|
||||
constexpr f32 pinch_threshold = 100.0f;
|
||||
|
||||
Controller_Gesture::Controller_Gesture(Core::System& system) : ControllerBase(system) {}
|
||||
Controller_Gesture::~Controller_Gesture() = default;
|
||||
|
||||
void Controller_Gesture::OnInit() {}
|
||||
void Controller_Gesture::OnInit() {
|
||||
for (std::size_t id = 0; id < MAX_FINGERS; ++id) {
|
||||
mouse_finger_id[id] = MAX_FINGERS;
|
||||
keyboard_finger_id[id] = MAX_FINGERS;
|
||||
udp_finger_id[id] = MAX_FINGERS;
|
||||
}
|
||||
}
|
||||
|
||||
void Controller_Gesture::OnRelease() {}
|
||||
|
||||
@@ -35,10 +45,153 @@ void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing, u
|
||||
|
||||
cur_entry.sampling_number = last_entry.sampling_number + 1;
|
||||
cur_entry.sampling_number2 = cur_entry.sampling_number;
|
||||
// TODO(ogniK): Update gesture states
|
||||
|
||||
// TODO(german77): Implement all gesture types
|
||||
|
||||
const Input::TouchStatus& mouse_status = touch_mouse_device->GetStatus();
|
||||
const Input::TouchStatus& udp_status = touch_udp_device->GetStatus();
|
||||
for (std::size_t id = 0; id < mouse_status.size(); ++id) {
|
||||
mouse_finger_id[id] = UpdateTouchInputEvent(mouse_status[id], mouse_finger_id[id]);
|
||||
udp_finger_id[id] = UpdateTouchInputEvent(udp_status[id], udp_finger_id[id]);
|
||||
}
|
||||
|
||||
if (Settings::values.use_touch_from_button) {
|
||||
const Input::TouchStatus& keyboard_status = touch_btn_device->GetStatus();
|
||||
for (std::size_t id = 0; id < mouse_status.size(); ++id) {
|
||||
keyboard_finger_id[id] =
|
||||
UpdateTouchInputEvent(keyboard_status[id], keyboard_finger_id[id]);
|
||||
}
|
||||
}
|
||||
|
||||
TouchType type = TouchType::Idle;
|
||||
Attribute attributes{};
|
||||
GestureProperties gesture = GetGestureProperties();
|
||||
if (last_gesture.active_points != gesture.active_points) {
|
||||
++last_gesture.detection_count;
|
||||
}
|
||||
if (gesture.active_points > 0) {
|
||||
if (last_gesture.active_points == 0) {
|
||||
attributes.is_new_touch.Assign(true);
|
||||
last_gesture.average_distance = gesture.average_distance;
|
||||
last_gesture.angle = gesture.angle;
|
||||
}
|
||||
|
||||
type = TouchType::Touch;
|
||||
if (gesture.mid_point.x != last_entry.x || gesture.mid_point.y != last_entry.y) {
|
||||
type = TouchType::Pan;
|
||||
}
|
||||
if (std::abs(gesture.average_distance - last_gesture.average_distance) > pinch_threshold) {
|
||||
type = TouchType::Pinch;
|
||||
}
|
||||
if (std::abs(gesture.angle - last_gesture.angle) > angle_threshold) {
|
||||
type = TouchType::Rotate;
|
||||
}
|
||||
|
||||
cur_entry.delta_x = gesture.mid_point.x - last_entry.x;
|
||||
cur_entry.delta_y = gesture.mid_point.y - last_entry.y;
|
||||
// TODO: Find how velocities are calculated
|
||||
cur_entry.vel_x = static_cast<float>(cur_entry.delta_x) * 150.1f;
|
||||
cur_entry.vel_y = static_cast<float>(cur_entry.delta_y) * 150.1f;
|
||||
|
||||
// Slowdown the rate of change for less flapping
|
||||
last_gesture.average_distance =
|
||||
(last_gesture.average_distance * 0.9f) + (gesture.average_distance * 0.1f);
|
||||
last_gesture.angle = (last_gesture.angle * 0.9f) + (gesture.angle * 0.1f);
|
||||
|
||||
} else {
|
||||
cur_entry.delta_x = 0;
|
||||
cur_entry.delta_y = 0;
|
||||
cur_entry.vel_x = 0;
|
||||
cur_entry.vel_y = 0;
|
||||
}
|
||||
last_gesture.active_points = gesture.active_points;
|
||||
cur_entry.detection_count = last_gesture.detection_count;
|
||||
cur_entry.type = type;
|
||||
cur_entry.attributes = attributes;
|
||||
cur_entry.x = gesture.mid_point.x;
|
||||
cur_entry.y = gesture.mid_point.y;
|
||||
cur_entry.point_count = static_cast<s32>(gesture.active_points);
|
||||
for (size_t id = 0; id < MAX_POINTS; id++) {
|
||||
cur_entry.points[id].x = gesture.points[id].x;
|
||||
cur_entry.points[id].y = gesture.points[id].y;
|
||||
}
|
||||
cur_entry.rotation_angle = 0;
|
||||
cur_entry.scale = 0;
|
||||
|
||||
std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
|
||||
}
|
||||
|
||||
void Controller_Gesture::OnLoadInputDevices() {}
|
||||
void Controller_Gesture::OnLoadInputDevices() {
|
||||
touch_mouse_device = Input::CreateDevice<Input::TouchDevice>("engine:emu_window");
|
||||
touch_udp_device = Input::CreateDevice<Input::TouchDevice>("engine:cemuhookudp");
|
||||
touch_btn_device = Input::CreateDevice<Input::TouchDevice>("engine:touch_from_button");
|
||||
}
|
||||
|
||||
std::optional<std::size_t> Controller_Gesture::GetUnusedFingerID() const {
|
||||
std::size_t first_free_id = 0;
|
||||
while (first_free_id < MAX_POINTS) {
|
||||
if (!fingers[first_free_id].pressed) {
|
||||
return first_free_id;
|
||||
} else {
|
||||
first_free_id++;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::size_t Controller_Gesture::UpdateTouchInputEvent(
|
||||
const std::tuple<float, float, bool>& touch_input, std::size_t finger_id) {
|
||||
const auto& [x, y, pressed] = touch_input;
|
||||
if (pressed) {
|
||||
if (finger_id == MAX_POINTS) {
|
||||
const auto first_free_id = GetUnusedFingerID();
|
||||
if (!first_free_id) {
|
||||
// Invalid finger id do nothing
|
||||
return MAX_POINTS;
|
||||
}
|
||||
finger_id = first_free_id.value();
|
||||
fingers[finger_id].pressed = true;
|
||||
}
|
||||
fingers[finger_id].x = x;
|
||||
fingers[finger_id].y = y;
|
||||
return finger_id;
|
||||
}
|
||||
|
||||
if (finger_id != MAX_POINTS) {
|
||||
fingers[finger_id].pressed = false;
|
||||
}
|
||||
|
||||
return MAX_POINTS;
|
||||
}
|
||||
|
||||
Controller_Gesture::GestureProperties Controller_Gesture::GetGestureProperties() {
|
||||
GestureProperties gesture;
|
||||
std::array<Finger, MAX_POINTS> active_fingers;
|
||||
const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(),
|
||||
[](const auto& finger) { return finger.pressed; });
|
||||
gesture.active_points =
|
||||
static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter));
|
||||
|
||||
for (size_t id = 0; id < gesture.active_points; ++id) {
|
||||
gesture.points[id].x =
|
||||
static_cast<int>(active_fingers[id].x * Layout::ScreenUndocked::Width);
|
||||
gesture.points[id].y =
|
||||
static_cast<int>(active_fingers[id].y * Layout::ScreenUndocked::Height);
|
||||
gesture.mid_point.x += static_cast<int>(gesture.points[id].x / gesture.active_points);
|
||||
gesture.mid_point.y += static_cast<int>(gesture.points[id].y / gesture.active_points);
|
||||
}
|
||||
|
||||
for (size_t id = 0; id < gesture.active_points; ++id) {
|
||||
const double distance =
|
||||
std::pow(static_cast<float>(gesture.mid_point.x - gesture.points[id].x), 2) +
|
||||
std::pow(static_cast<float>(gesture.mid_point.y - gesture.points[id].y), 2);
|
||||
gesture.average_distance +=
|
||||
static_cast<float>(distance) / static_cast<float>(gesture.active_points);
|
||||
}
|
||||
|
||||
gesture.angle = std::atan2(static_cast<float>(gesture.mid_point.y - gesture.points[0].y),
|
||||
static_cast<float>(gesture.mid_point.x - gesture.points[0].x));
|
||||
return gesture;
|
||||
}
|
||||
|
||||
} // namespace Service::HID
|
||||
|
||||
@@ -5,8 +5,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/frontend/input.h"
|
||||
#include "core/hle/service/hid/controllers/controller_base.h"
|
||||
|
||||
namespace Service::HID {
|
||||
@@ -28,29 +30,64 @@ public:
|
||||
void OnLoadInputDevices() override;
|
||||
|
||||
private:
|
||||
struct Locations {
|
||||
static constexpr size_t MAX_FINGERS = 16;
|
||||
static constexpr size_t MAX_POINTS = 4;
|
||||
|
||||
enum class TouchType : u32 {
|
||||
Idle, // Nothing touching the screen
|
||||
Complete, // Unknown. End of touch?
|
||||
Cancel, // Never triggered
|
||||
Touch, // Pressing without movement
|
||||
Press, // Never triggered
|
||||
Tap, // Fast press then release
|
||||
Pan, // All points moving together across the screen
|
||||
Swipe, // Fast press movement and release of a single point
|
||||
Pinch, // All points moving away/closer to the midpoint
|
||||
Rotate, // All points rotating from the midpoint
|
||||
};
|
||||
|
||||
enum class Direction : u32 {
|
||||
None,
|
||||
Left,
|
||||
Up,
|
||||
Right,
|
||||
Down,
|
||||
};
|
||||
|
||||
struct Attribute {
|
||||
union {
|
||||
u32_le raw{};
|
||||
|
||||
BitField<0, 1, u32> is_new_touch;
|
||||
BitField<1, 1, u32> is_double_tap;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(Attribute) == 4, "Attribute is an invalid size");
|
||||
|
||||
struct Points {
|
||||
s32_le x;
|
||||
s32_le y;
|
||||
};
|
||||
static_assert(sizeof(Points) == 8, "Points is an invalid size");
|
||||
|
||||
struct GestureState {
|
||||
s64_le sampling_number;
|
||||
s64_le sampling_number2;
|
||||
|
||||
s64_le detection_count;
|
||||
s32_le type;
|
||||
s32_le dir;
|
||||
TouchType type;
|
||||
Direction dir;
|
||||
s32_le x;
|
||||
s32_le y;
|
||||
s32_le delta_x;
|
||||
s32_le delta_y;
|
||||
f32 vel_x;
|
||||
f32 vel_y;
|
||||
s32_le attributes;
|
||||
f32 scale;
|
||||
f32 rotation;
|
||||
s32_le location_count;
|
||||
std::array<Locations, 4> locations;
|
||||
Attribute attributes;
|
||||
u32 scale;
|
||||
u32 rotation_angle;
|
||||
s32_le point_count;
|
||||
std::array<Points, 4> points;
|
||||
};
|
||||
static_assert(sizeof(GestureState) == 0x68, "GestureState is an invalid size");
|
||||
|
||||
@@ -58,6 +95,45 @@ private:
|
||||
CommonHeader header;
|
||||
std::array<GestureState, 17> gesture_states;
|
||||
};
|
||||
static_assert(sizeof(SharedMemory) == 0x708, "SharedMemory is an invalid size");
|
||||
|
||||
struct Finger {
|
||||
f32 x{};
|
||||
f32 y{};
|
||||
bool pressed{};
|
||||
};
|
||||
|
||||
struct GestureProperties {
|
||||
std::array<Points, MAX_POINTS> points{};
|
||||
std::size_t active_points{};
|
||||
Points mid_point{};
|
||||
s64_le detection_count{};
|
||||
u64_le delta_time{};
|
||||
float average_distance{};
|
||||
float angle{};
|
||||
};
|
||||
|
||||
// Returns an unused finger id, if there is no fingers avaliable MAX_FINGERS will be returned
|
||||
std::optional<size_t> GetUnusedFingerID() const;
|
||||
|
||||
/** If the touch is new it tries to assing a new finger id, if there is no fingers avaliable no
|
||||
* changes will be made. Updates the coordinates if the finger id it's already set. If the touch
|
||||
* ends delays the output by one frame to set the end_touch flag before finally freeing the
|
||||
* finger id */
|
||||
size_t UpdateTouchInputEvent(const std::tuple<float, float, bool>& touch_input,
|
||||
size_t finger_id);
|
||||
|
||||
// Returns the average distance, angle and middle point of the active fingers
|
||||
GestureProperties GetGestureProperties();
|
||||
|
||||
SharedMemory shared_memory{};
|
||||
std::unique_ptr<Input::TouchDevice> touch_mouse_device;
|
||||
std::unique_ptr<Input::TouchDevice> touch_udp_device;
|
||||
std::unique_ptr<Input::TouchDevice> touch_btn_device;
|
||||
std::array<size_t, MAX_FINGERS> mouse_finger_id;
|
||||
std::array<size_t, MAX_FINGERS> keyboard_finger_id;
|
||||
std::array<size_t, MAX_FINGERS> udp_finger_id;
|
||||
std::array<Finger, MAX_POINTS> fingers;
|
||||
GestureProperties last_gesture;
|
||||
};
|
||||
} // namespace Service::HID
|
||||
|
||||
@@ -273,8 +273,8 @@ Hid::Hid(Core::System& system_) : ServiceFramework{system_, "hid"} {
|
||||
{204, &Hid::PermitVibration, "PermitVibration"},
|
||||
{205, &Hid::IsVibrationPermitted, "IsVibrationPermitted"},
|
||||
{206, &Hid::SendVibrationValues, "SendVibrationValues"},
|
||||
{207, nullptr, "SendVibrationGcErmCommand"},
|
||||
{208, nullptr, "GetActualVibrationGcErmCommand"},
|
||||
{207, &Hid::SendVibrationGcErmCommand, "SendVibrationGcErmCommand"},
|
||||
{208, &Hid::GetActualVibrationGcErmCommand, "GetActualVibrationGcErmCommand"},
|
||||
{209, &Hid::BeginPermitVibrationSession, "BeginPermitVibrationSession"},
|
||||
{210, &Hid::EndPermitVibrationSession, "EndPermitVibrationSession"},
|
||||
{211, &Hid::IsVibrationDeviceMounted, "IsVibrationDeviceMounted"},
|
||||
@@ -1093,7 +1093,22 @@ void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
VibrationDeviceInfo vibration_device_info;
|
||||
|
||||
vibration_device_info.type = VibrationDeviceType::LinearResonantActuator;
|
||||
switch (vibration_device_handle.npad_type) {
|
||||
case Controller_NPad::NpadType::ProController:
|
||||
case Controller_NPad::NpadType::Handheld:
|
||||
case Controller_NPad::NpadType::JoyconDual:
|
||||
case Controller_NPad::NpadType::JoyconLeft:
|
||||
case Controller_NPad::NpadType::JoyconRight:
|
||||
default:
|
||||
vibration_device_info.type = VibrationDeviceType::LinearResonantActuator;
|
||||
break;
|
||||
case Controller_NPad::NpadType::GameCube:
|
||||
vibration_device_info.type = VibrationDeviceType::GcErm;
|
||||
break;
|
||||
case Controller_NPad::NpadType::Pokeball:
|
||||
vibration_device_info.type = VibrationDeviceType::Unknown;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (vibration_device_handle.device_index) {
|
||||
case Controller_NPad::DeviceIndex::Left:
|
||||
@@ -1215,6 +1230,108 @@ void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Hid::SendVibrationGcErmCommand(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
Controller_NPad::DeviceHandle vibration_device_handle;
|
||||
u64 applet_resource_user_id;
|
||||
VibrationGcErmCommand gc_erm_command;
|
||||
};
|
||||
static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
|
||||
/**
|
||||
* Note: This uses yuzu-specific behavior such that the StopHard command produces
|
||||
* vibrations where freq_low == 0.0f and freq_high == 0.0f, as defined below,
|
||||
* in order to differentiate between Stop and StopHard commands.
|
||||
* This is done to reuse the controller vibration functions made for regular controllers.
|
||||
*/
|
||||
const auto vibration_value = [parameters] {
|
||||
switch (parameters.gc_erm_command) {
|
||||
case VibrationGcErmCommand::Stop:
|
||||
return Controller_NPad::VibrationValue{
|
||||
.amp_low = 0.0f,
|
||||
.freq_low = 160.0f,
|
||||
.amp_high = 0.0f,
|
||||
.freq_high = 320.0f,
|
||||
};
|
||||
case VibrationGcErmCommand::Start:
|
||||
return Controller_NPad::VibrationValue{
|
||||
.amp_low = 1.0f,
|
||||
.freq_low = 160.0f,
|
||||
.amp_high = 1.0f,
|
||||
.freq_high = 320.0f,
|
||||
};
|
||||
case VibrationGcErmCommand::StopHard:
|
||||
return Controller_NPad::VibrationValue{
|
||||
.amp_low = 0.0f,
|
||||
.freq_low = 0.0f,
|
||||
.amp_high = 0.0f,
|
||||
.freq_high = 0.0f,
|
||||
};
|
||||
default:
|
||||
return Controller_NPad::DEFAULT_VIBRATION_VALUE;
|
||||
}
|
||||
}();
|
||||
|
||||
applet_resource->GetController<Controller_NPad>(HidController::NPad)
|
||||
.VibrateController(parameters.vibration_device_handle, vibration_value);
|
||||
|
||||
LOG_DEBUG(Service_HID,
|
||||
"called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}, "
|
||||
"gc_erm_command={}",
|
||||
parameters.vibration_device_handle.npad_type,
|
||||
parameters.vibration_device_handle.npad_id,
|
||||
parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id,
|
||||
parameters.gc_erm_command);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Hid::GetActualVibrationGcErmCommand(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
Controller_NPad::DeviceHandle vibration_device_handle;
|
||||
INSERT_PADDING_WORDS_NOINIT(1);
|
||||
u64 applet_resource_user_id;
|
||||
};
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
|
||||
const auto last_vibration = applet_resource->GetController<Controller_NPad>(HidController::NPad)
|
||||
.GetLastVibration(parameters.vibration_device_handle);
|
||||
|
||||
const auto gc_erm_command = [last_vibration] {
|
||||
if (last_vibration.amp_low != 0.0f || last_vibration.amp_high != 0.0f) {
|
||||
return VibrationGcErmCommand::Start;
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: This uses yuzu-specific behavior such that the StopHard command produces
|
||||
* vibrations where freq_low == 0.0f and freq_high == 0.0f, as defined in the HID function
|
||||
* SendVibrationGcErmCommand, in order to differentiate between Stop and StopHard commands.
|
||||
* This is done to reuse the controller vibration functions made for regular controllers.
|
||||
*/
|
||||
if (last_vibration.freq_low == 0.0f && last_vibration.freq_high == 0.0f) {
|
||||
return VibrationGcErmCommand::StopHard;
|
||||
}
|
||||
|
||||
return VibrationGcErmCommand::Stop;
|
||||
}();
|
||||
|
||||
LOG_DEBUG(Service_HID,
|
||||
"called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
|
||||
parameters.vibration_device_handle.npad_type,
|
||||
parameters.vibration_device_handle.npad_id,
|
||||
parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushEnum(gc_erm_command);
|
||||
}
|
||||
|
||||
void Hid::BeginPermitVibrationSession(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||
|
||||
@@ -136,6 +136,8 @@ private:
|
||||
void PermitVibration(Kernel::HLERequestContext& ctx);
|
||||
void IsVibrationPermitted(Kernel::HLERequestContext& ctx);
|
||||
void SendVibrationValues(Kernel::HLERequestContext& ctx);
|
||||
void SendVibrationGcErmCommand(Kernel::HLERequestContext& ctx);
|
||||
void GetActualVibrationGcErmCommand(Kernel::HLERequestContext& ctx);
|
||||
void BeginPermitVibrationSession(Kernel::HLERequestContext& ctx);
|
||||
void EndPermitVibrationSession(Kernel::HLERequestContext& ctx);
|
||||
void IsVibrationDeviceMounted(Kernel::HLERequestContext& ctx);
|
||||
@@ -154,7 +156,9 @@ private:
|
||||
void GetNpadCommunicationMode(Kernel::HLERequestContext& ctx);
|
||||
|
||||
enum class VibrationDeviceType : u32 {
|
||||
Unknown = 0,
|
||||
LinearResonantActuator = 1,
|
||||
GcErm = 2,
|
||||
};
|
||||
|
||||
enum class VibrationDevicePosition : u32 {
|
||||
@@ -163,6 +167,12 @@ private:
|
||||
Right = 2,
|
||||
};
|
||||
|
||||
enum class VibrationGcErmCommand : u64 {
|
||||
Stop = 0,
|
||||
Start = 1,
|
||||
StopHard = 2,
|
||||
};
|
||||
|
||||
struct VibrationDeviceInfo {
|
||||
VibrationDeviceType type{};
|
||||
VibrationDevicePosition position{};
|
||||
|
||||
@@ -156,7 +156,7 @@ public:
|
||||
is_initialized = true;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(ERROR_DISABLED);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -341,12 +341,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) {
|
||||
|
||||
@@ -279,6 +279,10 @@ const SharedMemory& TimeManager::GetSharedMemory() const {
|
||||
return impl->shared_memory;
|
||||
}
|
||||
|
||||
void TimeManager::Shutdown() {
|
||||
impl.reset();
|
||||
}
|
||||
|
||||
void TimeManager::UpdateLocalSystemClockTime(s64 posix_time) {
|
||||
impl->UpdateLocalSystemClockTime(system, posix_time);
|
||||
}
|
||||
|
||||
@@ -61,6 +61,8 @@ public:
|
||||
|
||||
const SharedMemory& GetSharedMemory() const;
|
||||
|
||||
void Shutdown();
|
||||
|
||||
void SetupTimeZoneManager(std::string location_name,
|
||||
Clock::SteadyClockTimePoint time_zone_updated_time_point,
|
||||
std::size_t total_location_name_count, u128 time_zone_rule_version,
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <limits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "common/common_funcs.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define _WINSOCK_DEPRECATED_NO_WARNINGS // gethostname
|
||||
@@ -90,15 +91,36 @@ LINGER MakeLinger(bool enable, u32 linger_value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
int LastError() {
|
||||
return WSAGetLastError();
|
||||
}
|
||||
|
||||
bool EnableNonBlock(SOCKET fd, bool enable) {
|
||||
u_long value = enable ? 1 : 0;
|
||||
return ioctlsocket(fd, FIONBIO, &value) != SOCKET_ERROR;
|
||||
}
|
||||
|
||||
Errno TranslateNativeError(int e) {
|
||||
switch (e) {
|
||||
case WSAEBADF:
|
||||
return Errno::BADF;
|
||||
case WSAEINVAL:
|
||||
return Errno::INVAL;
|
||||
case WSAEMFILE:
|
||||
return Errno::MFILE;
|
||||
case WSAENOTCONN:
|
||||
return Errno::NOTCONN;
|
||||
case WSAEWOULDBLOCK:
|
||||
return Errno::AGAIN;
|
||||
case WSAECONNREFUSED:
|
||||
return Errno::CONNREFUSED;
|
||||
case WSAEHOSTUNREACH:
|
||||
return Errno::HOSTUNREACH;
|
||||
case WSAENETDOWN:
|
||||
return Errno::NETDOWN;
|
||||
case WSAENETUNREACH:
|
||||
return Errno::NETUNREACH;
|
||||
default:
|
||||
return Errno::OTHER;
|
||||
}
|
||||
}
|
||||
|
||||
#elif YUZU_UNIX // ^ _WIN32 v YUZU_UNIX
|
||||
|
||||
using SOCKET = int;
|
||||
@@ -108,9 +130,6 @@ using ULONG = u64;
|
||||
constexpr SOCKET INVALID_SOCKET = -1;
|
||||
constexpr SOCKET SOCKET_ERROR = -1;
|
||||
|
||||
constexpr int WSAEWOULDBLOCK = EAGAIN;
|
||||
constexpr int WSAENOTCONN = ENOTCONN;
|
||||
|
||||
constexpr int SD_RECEIVE = SHUT_RD;
|
||||
constexpr int SD_SEND = SHUT_WR;
|
||||
constexpr int SD_BOTH = SHUT_RDWR;
|
||||
@@ -162,10 +181,6 @@ linger MakeLinger(bool enable, u32 linger_value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
int LastError() {
|
||||
return errno;
|
||||
}
|
||||
|
||||
bool EnableNonBlock(int fd, bool enable) {
|
||||
int flags = fcntl(fd, F_GETFD);
|
||||
if (flags == -1) {
|
||||
@@ -179,8 +194,43 @@ bool EnableNonBlock(int fd, bool enable) {
|
||||
return fcntl(fd, F_SETFD, flags) == 0;
|
||||
}
|
||||
|
||||
Errno TranslateNativeError(int e) {
|
||||
switch (e) {
|
||||
case EBADF:
|
||||
return Errno::BADF;
|
||||
case EINVAL:
|
||||
return Errno::INVAL;
|
||||
case EMFILE:
|
||||
return Errno::MFILE;
|
||||
case ENOTCONN:
|
||||
return Errno::NOTCONN;
|
||||
case EAGAIN:
|
||||
return Errno::AGAIN;
|
||||
case ECONNREFUSED:
|
||||
return Errno::CONNREFUSED;
|
||||
case EHOSTUNREACH:
|
||||
return Errno::HOSTUNREACH;
|
||||
case ENETDOWN:
|
||||
return Errno::NETDOWN;
|
||||
case ENETUNREACH:
|
||||
return Errno::NETUNREACH;
|
||||
default:
|
||||
return Errno::OTHER;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
Errno GetAndLogLastError() {
|
||||
#ifdef _WIN32
|
||||
int e = WSAGetLastError();
|
||||
#else
|
||||
int e = errno;
|
||||
#endif
|
||||
LOG_ERROR(Network, "Socket operation error: {}", NativeErrorToString(e));
|
||||
return TranslateNativeError(e);
|
||||
}
|
||||
|
||||
int TranslateDomain(Domain domain) {
|
||||
switch (domain) {
|
||||
case Domain::INET:
|
||||
@@ -290,9 +340,7 @@ Errno SetSockOpt(SOCKET fd, int option, T value) {
|
||||
if (result != SOCKET_ERROR) {
|
||||
return Errno::SUCCESS;
|
||||
}
|
||||
const int ec = LastError();
|
||||
UNREACHABLE_MSG("Unhandled host socket error={}", ec);
|
||||
return Errno::SUCCESS;
|
||||
return GetAndLogLastError();
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
@@ -308,14 +356,12 @@ NetworkInstance::~NetworkInstance() {
|
||||
std::pair<IPv4Address, Errno> GetHostIPv4Address() {
|
||||
std::array<char, 256> name{};
|
||||
if (gethostname(name.data(), static_cast<int>(name.size()) - 1) == SOCKET_ERROR) {
|
||||
UNIMPLEMENTED_MSG("Unhandled gethostname error");
|
||||
return {IPv4Address{}, Errno::SUCCESS};
|
||||
return {IPv4Address{}, GetAndLogLastError()};
|
||||
}
|
||||
|
||||
hostent* const ent = gethostbyname(name.data());
|
||||
if (!ent) {
|
||||
UNIMPLEMENTED_MSG("Unhandled gethostbyname error");
|
||||
return {IPv4Address{}, Errno::SUCCESS};
|
||||
return {IPv4Address{}, GetAndLogLastError()};
|
||||
}
|
||||
if (ent->h_addr_list == nullptr) {
|
||||
UNIMPLEMENTED_MSG("No addr provided in hostent->h_addr_list");
|
||||
@@ -359,9 +405,7 @@ std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) {
|
||||
|
||||
ASSERT(result == SOCKET_ERROR);
|
||||
|
||||
const int ec = LastError();
|
||||
UNREACHABLE_MSG("Unhandled host socket error={}", ec);
|
||||
return {-1, Errno::SUCCESS};
|
||||
return {-1, GetAndLogLastError()};
|
||||
}
|
||||
|
||||
Socket::~Socket() {
|
||||
@@ -380,9 +424,7 @@ Errno Socket::Initialize(Domain domain, Type type, Protocol protocol) {
|
||||
return Errno::SUCCESS;
|
||||
}
|
||||
|
||||
const int ec = LastError();
|
||||
UNREACHABLE_MSG("Unhandled host socket error={}", ec);
|
||||
return Errno::SUCCESS;
|
||||
return GetAndLogLastError();
|
||||
}
|
||||
|
||||
std::pair<Socket::AcceptResult, Errno> Socket::Accept() {
|
||||
@@ -391,9 +433,7 @@ std::pair<Socket::AcceptResult, Errno> Socket::Accept() {
|
||||
const SOCKET new_socket = accept(fd, &addr, &addrlen);
|
||||
|
||||
if (new_socket == INVALID_SOCKET) {
|
||||
const int ec = LastError();
|
||||
UNREACHABLE_MSG("Unhandled host socket error={}", ec);
|
||||
return {AcceptResult{}, Errno::SUCCESS};
|
||||
return {AcceptResult{}, GetAndLogLastError()};
|
||||
}
|
||||
|
||||
AcceptResult result;
|
||||
@@ -412,23 +452,14 @@ Errno Socket::Connect(SockAddrIn addr_in) {
|
||||
return Errno::SUCCESS;
|
||||
}
|
||||
|
||||
switch (const int ec = LastError()) {
|
||||
case WSAEWOULDBLOCK:
|
||||
LOG_DEBUG(Service, "EAGAIN generated");
|
||||
return Errno::AGAIN;
|
||||
default:
|
||||
UNREACHABLE_MSG("Unhandled host socket error={}", ec);
|
||||
return Errno::SUCCESS;
|
||||
}
|
||||
return GetAndLogLastError();
|
||||
}
|
||||
|
||||
std::pair<SockAddrIn, Errno> Socket::GetPeerName() {
|
||||
sockaddr addr;
|
||||
socklen_t addrlen = sizeof(addr);
|
||||
if (getpeername(fd, &addr, &addrlen) == SOCKET_ERROR) {
|
||||
const int ec = LastError();
|
||||
UNREACHABLE_MSG("Unhandled host socket error={}", ec);
|
||||
return {SockAddrIn{}, Errno::SUCCESS};
|
||||
return {SockAddrIn{}, GetAndLogLastError()};
|
||||
}
|
||||
|
||||
ASSERT(addrlen == sizeof(sockaddr_in));
|
||||
@@ -439,9 +470,7 @@ std::pair<SockAddrIn, Errno> Socket::GetSockName() {
|
||||
sockaddr addr;
|
||||
socklen_t addrlen = sizeof(addr);
|
||||
if (getsockname(fd, &addr, &addrlen) == SOCKET_ERROR) {
|
||||
const int ec = LastError();
|
||||
UNREACHABLE_MSG("Unhandled host socket error={}", ec);
|
||||
return {SockAddrIn{}, Errno::SUCCESS};
|
||||
return {SockAddrIn{}, GetAndLogLastError()};
|
||||
}
|
||||
|
||||
ASSERT(addrlen == sizeof(sockaddr_in));
|
||||
@@ -454,9 +483,7 @@ Errno Socket::Bind(SockAddrIn addr) {
|
||||
return Errno::SUCCESS;
|
||||
}
|
||||
|
||||
const int ec = LastError();
|
||||
UNREACHABLE_MSG("Unhandled host socket error={}", ec);
|
||||
return Errno::SUCCESS;
|
||||
return GetAndLogLastError();
|
||||
}
|
||||
|
||||
Errno Socket::Listen(s32 backlog) {
|
||||
@@ -464,9 +491,7 @@ Errno Socket::Listen(s32 backlog) {
|
||||
return Errno::SUCCESS;
|
||||
}
|
||||
|
||||
const int ec = LastError();
|
||||
UNREACHABLE_MSG("Unhandled host socket error={}", ec);
|
||||
return Errno::SUCCESS;
|
||||
return GetAndLogLastError();
|
||||
}
|
||||
|
||||
Errno Socket::Shutdown(ShutdownHow how) {
|
||||
@@ -489,14 +514,7 @@ Errno Socket::Shutdown(ShutdownHow how) {
|
||||
return Errno::SUCCESS;
|
||||
}
|
||||
|
||||
switch (const int ec = LastError()) {
|
||||
case WSAENOTCONN:
|
||||
LOG_ERROR(Service, "ENOTCONN generated");
|
||||
return Errno::NOTCONN;
|
||||
default:
|
||||
UNREACHABLE_MSG("Unhandled host socket error={}", ec);
|
||||
return Errno::SUCCESS;
|
||||
}
|
||||
return GetAndLogLastError();
|
||||
}
|
||||
|
||||
std::pair<s32, Errno> Socket::Recv(int flags, std::vector<u8>& message) {
|
||||
@@ -509,17 +527,7 @@ std::pair<s32, Errno> Socket::Recv(int flags, std::vector<u8>& message) {
|
||||
return {static_cast<s32>(result), Errno::SUCCESS};
|
||||
}
|
||||
|
||||
switch (const int ec = LastError()) {
|
||||
case WSAEWOULDBLOCK:
|
||||
LOG_DEBUG(Service, "EAGAIN generated");
|
||||
return {-1, Errno::AGAIN};
|
||||
case WSAENOTCONN:
|
||||
LOG_ERROR(Service, "ENOTCONN generated");
|
||||
return {-1, Errno::NOTCONN};
|
||||
default:
|
||||
UNREACHABLE_MSG("Unhandled host socket error={}", ec);
|
||||
return {0, Errno::SUCCESS};
|
||||
}
|
||||
return {-1, GetAndLogLastError()};
|
||||
}
|
||||
|
||||
std::pair<s32, Errno> Socket::RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) {
|
||||
@@ -541,17 +549,7 @@ std::pair<s32, Errno> Socket::RecvFrom(int flags, std::vector<u8>& message, Sock
|
||||
return {static_cast<s32>(result), Errno::SUCCESS};
|
||||
}
|
||||
|
||||
switch (const int ec = LastError()) {
|
||||
case WSAEWOULDBLOCK:
|
||||
LOG_DEBUG(Service, "EAGAIN generated");
|
||||
return {-1, Errno::AGAIN};
|
||||
case WSAENOTCONN:
|
||||
LOG_ERROR(Service, "ENOTCONN generated");
|
||||
return {-1, Errno::NOTCONN};
|
||||
default:
|
||||
UNREACHABLE_MSG("Unhandled host socket error={}", ec);
|
||||
return {-1, Errno::SUCCESS};
|
||||
}
|
||||
return {-1, GetAndLogLastError()};
|
||||
}
|
||||
|
||||
std::pair<s32, Errno> Socket::Send(const std::vector<u8>& message, int flags) {
|
||||
@@ -564,18 +562,7 @@ std::pair<s32, Errno> Socket::Send(const std::vector<u8>& message, int flags) {
|
||||
return {static_cast<s32>(result), Errno::SUCCESS};
|
||||
}
|
||||
|
||||
const int ec = LastError();
|
||||
switch (ec) {
|
||||
case WSAEWOULDBLOCK:
|
||||
LOG_DEBUG(Service, "EAGAIN generated");
|
||||
return {-1, Errno::AGAIN};
|
||||
case WSAENOTCONN:
|
||||
LOG_ERROR(Service, "ENOTCONN generated");
|
||||
return {-1, Errno::NOTCONN};
|
||||
default:
|
||||
UNREACHABLE_MSG("Unhandled host socket error={}", ec);
|
||||
return {-1, Errno::SUCCESS};
|
||||
}
|
||||
return {-1, GetAndLogLastError()};
|
||||
}
|
||||
|
||||
std::pair<s32, Errno> Socket::SendTo(u32 flags, const std::vector<u8>& message,
|
||||
@@ -597,9 +584,7 @@ std::pair<s32, Errno> Socket::SendTo(u32 flags, const std::vector<u8>& message,
|
||||
return {static_cast<s32>(result), Errno::SUCCESS};
|
||||
}
|
||||
|
||||
const int ec = LastError();
|
||||
UNREACHABLE_MSG("Unhandled host socket error={}", ec);
|
||||
return {-1, Errno::SUCCESS};
|
||||
return {-1, GetAndLogLastError()};
|
||||
}
|
||||
|
||||
Errno Socket::Close() {
|
||||
@@ -642,9 +627,7 @@ Errno Socket::SetNonBlock(bool enable) {
|
||||
if (EnableNonBlock(fd, enable)) {
|
||||
return Errno::SUCCESS;
|
||||
}
|
||||
const int ec = LastError();
|
||||
UNREACHABLE_MSG("Unhandled host socket error={}", ec);
|
||||
return Errno::SUCCESS;
|
||||
return GetAndLogLastError();
|
||||
}
|
||||
|
||||
bool Socket::IsOpened() const {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <array>
|
||||
#include <utility>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Network {
|
||||
@@ -21,6 +22,11 @@ enum class Errno {
|
||||
MFILE,
|
||||
NOTCONN,
|
||||
AGAIN,
|
||||
CONNREFUSED,
|
||||
HOSTUNREACH,
|
||||
NETDOWN,
|
||||
NETUNREACH,
|
||||
OTHER,
|
||||
};
|
||||
|
||||
/// Address families
|
||||
|
||||
@@ -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,6 +152,11 @@ 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;
|
||||
|
||||
@@ -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,7 @@ public:
|
||||
/**
|
||||
* Signals that a motion sensor tilt has ended.
|
||||
*/
|
||||
void ReleaseButton(int button_);
|
||||
void ReleaseButton(MouseButton button_);
|
||||
|
||||
[[nodiscard]] Common::SPSCQueue<MouseStatus>& GetMouseQueue();
|
||||
[[nodiscard]] const Common::SPSCQueue<MouseStatus>& GetMouseQueue() const;
|
||||
@@ -94,7 +96,7 @@ private:
|
||||
u16 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};
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <random>
|
||||
#include <thread>
|
||||
#include <boost/asio.hpp>
|
||||
#include "common/logging/log.h"
|
||||
@@ -26,10 +27,10 @@ class Socket {
|
||||
public:
|
||||
using clock = std::chrono::system_clock;
|
||||
|
||||
explicit Socket(const std::string& host, u16 port, std::size_t pad_index_, u32 client_id_,
|
||||
explicit Socket(const std::string& host, u16 port, std::size_t pad_index_,
|
||||
SocketCallback callback_)
|
||||
: callback(std::move(callback_)), timer(io_service),
|
||||
socket(io_service, udp::endpoint(udp::v4(), 0)), client_id(client_id_),
|
||||
socket(io_service, udp::endpoint(udp::v4(), 0)), client_id(GenerateRandomClientId()),
|
||||
pad_index(pad_index_) {
|
||||
boost::system::error_code ec{};
|
||||
auto ipv4 = boost::asio::ip::make_address_v4(host, ec);
|
||||
@@ -63,6 +64,11 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
u32 GenerateRandomClientId() const {
|
||||
std::random_device device;
|
||||
return device();
|
||||
}
|
||||
|
||||
void HandleReceive(const boost::system::error_code&, std::size_t bytes_transferred) {
|
||||
if (auto type = Response::Validate(receive_buffer.data(), bytes_transferred)) {
|
||||
switch (*type) {
|
||||
@@ -115,7 +121,7 @@ private:
|
||||
boost::asio::basic_waitable_timer<clock> timer;
|
||||
udp::socket socket;
|
||||
|
||||
u32 client_id{};
|
||||
const u32 client_id;
|
||||
std::size_t pad_index{};
|
||||
|
||||
static constexpr std::size_t PORT_INFO_SIZE = sizeof(Message<Request::PortInfo>);
|
||||
@@ -203,7 +209,7 @@ void Client::ReloadSockets() {
|
||||
LOG_ERROR(Input, "Duplicated UDP servers found");
|
||||
continue;
|
||||
}
|
||||
StartCommunication(client++, udp_input_address, udp_input_port, pad, 24872);
|
||||
StartCommunication(client++, udp_input_address, udp_input_port, pad);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -277,7 +283,7 @@ void Client::OnPadData(Response::PadData data, std::size_t client) {
|
||||
}
|
||||
|
||||
void Client::StartCommunication(std::size_t client, const std::string& host, u16 port,
|
||||
std::size_t pad_index, u32 client_id) {
|
||||
std::size_t pad_index) {
|
||||
SocketCallback callback{[this](Response::Version version) { OnVersion(version); },
|
||||
[this](Response::PortInfo info) { OnPortInfo(info); },
|
||||
[this, client](Response::PadData data) { OnPadData(data, client); }};
|
||||
@@ -287,7 +293,7 @@ void Client::StartCommunication(std::size_t client, const std::string& host, u16
|
||||
clients[client].port = port;
|
||||
clients[client].pad_index = pad_index;
|
||||
clients[client].active = 0;
|
||||
clients[client].socket = std::make_unique<Socket>(host, port, pad_index, client_id, callback);
|
||||
clients[client].socket = std::make_unique<Socket>(host, port, pad_index, callback);
|
||||
clients[client].thread = std::thread{SocketLoop, clients[client].socket.get()};
|
||||
// Set motion parameters
|
||||
// SetGyroThreshold value should be dependent on GyroscopeZeroDriftMode
|
||||
@@ -416,7 +422,7 @@ const Common::SPSCQueue<UDPPadStatus>& Client::GetPadQueue() const {
|
||||
return pad_queue;
|
||||
}
|
||||
|
||||
void TestCommunication(const std::string& host, u16 port, std::size_t pad_index, u32 client_id,
|
||||
void TestCommunication(const std::string& host, u16 port, std::size_t pad_index,
|
||||
const std::function<void()>& success_callback,
|
||||
const std::function<void()>& failure_callback) {
|
||||
std::thread([=] {
|
||||
@@ -426,7 +432,7 @@ void TestCommunication(const std::string& host, u16 port, std::size_t pad_index,
|
||||
.port_info = [](Response::PortInfo) {},
|
||||
.pad_data = [&](Response::PadData) { success_event.Set(); },
|
||||
};
|
||||
Socket socket{host, port, pad_index, client_id, std::move(callback)};
|
||||
Socket socket{host, port, pad_index, std::move(callback)};
|
||||
std::thread worker_thread{SocketLoop, &socket};
|
||||
const bool result = success_event.WaitFor(std::chrono::seconds(5));
|
||||
socket.Stop();
|
||||
@@ -440,7 +446,7 @@ void TestCommunication(const std::string& host, u16 port, std::size_t pad_index,
|
||||
}
|
||||
|
||||
CalibrationConfigurationJob::CalibrationConfigurationJob(
|
||||
const std::string& host, u16 port, std::size_t pad_index, u32 client_id,
|
||||
const std::string& host, u16 port, std::size_t pad_index,
|
||||
std::function<void(Status)> status_callback,
|
||||
std::function<void(u16, u16, u16, u16)> data_callback) {
|
||||
|
||||
@@ -485,7 +491,7 @@ CalibrationConfigurationJob::CalibrationConfigurationJob(
|
||||
complete_event.Set();
|
||||
}
|
||||
}};
|
||||
Socket socket{host, port, pad_index, client_id, std::move(callback)};
|
||||
Socket socket{host, port, pad_index, std::move(callback)};
|
||||
std::thread worker_thread{SocketLoop, &socket};
|
||||
complete_event.Wait();
|
||||
socket.Stop();
|
||||
|
||||
@@ -126,7 +126,7 @@ private:
|
||||
void OnPortInfo(Response::PortInfo);
|
||||
void OnPadData(Response::PadData, std::size_t client);
|
||||
void StartCommunication(std::size_t client, const std::string& host, u16 port,
|
||||
std::size_t pad_index, u32 client_id);
|
||||
std::size_t pad_index);
|
||||
void UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& acc,
|
||||
const Common::Vec3<float>& gyro);
|
||||
|
||||
@@ -165,7 +165,7 @@ public:
|
||||
* @param data_callback Called when calibration data is ready
|
||||
*/
|
||||
explicit CalibrationConfigurationJob(const std::string& host, u16 port, std::size_t pad_index,
|
||||
u32 client_id, std::function<void(Status)> status_callback,
|
||||
std::function<void(Status)> status_callback,
|
||||
std::function<void(u16, u16, u16, u16)> data_callback);
|
||||
~CalibrationConfigurationJob();
|
||||
void Stop();
|
||||
@@ -174,7 +174,7 @@ private:
|
||||
Common::Event complete_event;
|
||||
};
|
||||
|
||||
void TestCommunication(const std::string& host, u16 port, std::size_t pad_index, u32 client_id,
|
||||
void TestCommunication(const std::string& host, u16 port, std::size_t pad_index,
|
||||
const std::function<void()>& success_callback,
|
||||
const std::function<void()>& failure_callback);
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ add_executable(tests
|
||||
common/param_package.cpp
|
||||
common/ring_buffer.cpp
|
||||
core/core_timing.cpp
|
||||
core/network/network.cpp
|
||||
tests.cpp
|
||||
video_core/buffer_base.cpp
|
||||
)
|
||||
|
||||
@@ -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
src/tests/core/network/network.cpp
Normal file
28
src/tests/core/network/network.cpp
Normal 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);
|
||||
}
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <numeric>
|
||||
#include <span>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
@@ -91,7 +92,7 @@ class BufferCache {
|
||||
};
|
||||
|
||||
public:
|
||||
static constexpr u32 SKIP_CACHE_SIZE = 4096;
|
||||
static constexpr u32 DEFAULT_SKIP_CACHE_SIZE = 4096;
|
||||
|
||||
explicit BufferCache(VideoCore::RasterizerInterface& rasterizer_,
|
||||
Tegra::Engines::Maxwell3D& maxwell3d_,
|
||||
@@ -240,9 +241,9 @@ private:
|
||||
template <bool insert>
|
||||
void ChangeRegister(BufferId buffer_id);
|
||||
|
||||
void SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size);
|
||||
bool SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size);
|
||||
|
||||
void SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 size);
|
||||
bool SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 size);
|
||||
|
||||
void UploadMemory(Buffer& buffer, u64 total_size_bytes, u64 largest_copy,
|
||||
std::span<BufferCopy> copies);
|
||||
@@ -297,6 +298,11 @@ private:
|
||||
|
||||
std::array<u32, NUM_STAGES> fast_bound_uniform_buffers{};
|
||||
|
||||
std::array<u32, 16> uniform_cache_hits{};
|
||||
std::array<u32, 16> uniform_cache_shots{};
|
||||
|
||||
u32 uniform_buffer_skip_cache_size = DEFAULT_SKIP_CACHE_SIZE;
|
||||
|
||||
bool has_deleted_buffers = false;
|
||||
|
||||
std::conditional_t<HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS, std::array<u32, NUM_STAGES>, Empty>
|
||||
@@ -328,6 +334,19 @@ BufferCache<P>::BufferCache(VideoCore::RasterizerInterface& rasterizer_,
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::TickFrame() {
|
||||
// Calculate hits and shots and move hit bits to the right
|
||||
const u32 hits = std::reduce(uniform_cache_hits.begin(), uniform_cache_hits.end());
|
||||
const u32 shots = std::reduce(uniform_cache_shots.begin(), uniform_cache_shots.end());
|
||||
std::copy_n(uniform_cache_hits.begin(), uniform_cache_hits.size() - 1,
|
||||
uniform_cache_hits.begin() + 1);
|
||||
std::copy_n(uniform_cache_shots.begin(), uniform_cache_shots.size() - 1,
|
||||
uniform_cache_shots.begin() + 1);
|
||||
uniform_cache_hits[0] = 0;
|
||||
uniform_cache_shots[0] = 0;
|
||||
|
||||
const bool skip_preferred = hits * 256 < shots * 251;
|
||||
uniform_buffer_skip_cache_size = skip_preferred ? DEFAULT_SKIP_CACHE_SIZE : 0;
|
||||
|
||||
delayed_destruction_ring.Tick();
|
||||
}
|
||||
|
||||
@@ -671,7 +690,7 @@ void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32
|
||||
const VAddr cpu_addr = binding.cpu_addr;
|
||||
const u32 size = binding.size;
|
||||
Buffer& buffer = slot_buffers[binding.buffer_id];
|
||||
if (size <= SKIP_CACHE_SIZE && !buffer.IsRegionGpuModified(cpu_addr, size)) {
|
||||
if (size <= uniform_buffer_skip_cache_size && !buffer.IsRegionGpuModified(cpu_addr, size)) {
|
||||
if constexpr (IS_OPENGL) {
|
||||
if (runtime.HasFastBufferSubData()) {
|
||||
// Fast path for Nvidia
|
||||
@@ -692,7 +711,12 @@ void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32
|
||||
return;
|
||||
}
|
||||
// Classic cached path
|
||||
SynchronizeBuffer(buffer, cpu_addr, size);
|
||||
const bool sync_cached = SynchronizeBuffer(buffer, cpu_addr, size);
|
||||
if (sync_cached) {
|
||||
++uniform_cache_hits[0];
|
||||
}
|
||||
++uniform_cache_shots[0];
|
||||
|
||||
if (!needs_bind && !HasFastUniformBufferBound(stage, binding_index)) {
|
||||
// Skip binding if it's not needed and if the bound buffer is not the fast version
|
||||
// This exists to avoid instances where the fast buffer is bound and a GPU write happens
|
||||
@@ -1106,15 +1130,15 @@ void BufferCache<P>::ChangeRegister(BufferId buffer_id) {
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size) {
|
||||
bool BufferCache<P>::SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size) {
|
||||
if (buffer.CpuAddr() == 0) {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
SynchronizeBufferImpl(buffer, cpu_addr, size);
|
||||
return SynchronizeBufferImpl(buffer, cpu_addr, size);
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 size) {
|
||||
bool BufferCache<P>::SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 size) {
|
||||
boost::container::small_vector<BufferCopy, 4> copies;
|
||||
u64 total_size_bytes = 0;
|
||||
u64 largest_copy = 0;
|
||||
@@ -1128,10 +1152,11 @@ void BufferCache<P>::SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 s
|
||||
largest_copy = std::max(largest_copy, range_size);
|
||||
});
|
||||
if (total_size_bytes == 0) {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
const std::span<BufferCopy> copies_span(copies.data(), copies.size());
|
||||
UploadMemory(buffer, total_size_bytes, largest_copy, copies_span);
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class P>
|
||||
|
||||
@@ -48,6 +48,15 @@ constexpr std::array VIEW_CLASS_32_BITS{
|
||||
PixelFormat::A2B10G10R10_UINT,
|
||||
};
|
||||
|
||||
constexpr std::array VIEW_CLASS_32_BITS_NO_BGR{
|
||||
PixelFormat::R16G16_FLOAT, PixelFormat::B10G11R11_FLOAT, PixelFormat::R32_FLOAT,
|
||||
PixelFormat::A2B10G10R10_UNORM, PixelFormat::R16G16_UINT, PixelFormat::R32_UINT,
|
||||
PixelFormat::R16G16_SINT, PixelFormat::R32_SINT, PixelFormat::A8B8G8R8_UNORM,
|
||||
PixelFormat::R16G16_UNORM, PixelFormat::A8B8G8R8_SNORM, PixelFormat::R16G16_SNORM,
|
||||
PixelFormat::A8B8G8R8_SRGB, PixelFormat::E5B9G9R9_FLOAT, PixelFormat::A8B8G8R8_UINT,
|
||||
PixelFormat::A8B8G8R8_SINT, PixelFormat::A2B10G10R10_UINT,
|
||||
};
|
||||
|
||||
// TODO: How should we handle 24 bits?
|
||||
|
||||
constexpr std::array VIEW_CLASS_16_BITS{
|
||||
@@ -205,7 +214,6 @@ constexpr Table MakeViewTable() {
|
||||
EnableRange(view, VIEW_CLASS_128_BITS);
|
||||
EnableRange(view, VIEW_CLASS_96_BITS);
|
||||
EnableRange(view, VIEW_CLASS_64_BITS);
|
||||
EnableRange(view, VIEW_CLASS_32_BITS);
|
||||
EnableRange(view, VIEW_CLASS_16_BITS);
|
||||
EnableRange(view, VIEW_CLASS_8_BITS);
|
||||
EnableRange(view, VIEW_CLASS_RGTC1_RED);
|
||||
@@ -231,20 +239,47 @@ constexpr Table MakeCopyTable() {
|
||||
EnableRange(copy, COPY_CLASS_64_BITS);
|
||||
return copy;
|
||||
}
|
||||
|
||||
constexpr Table MakeNativeBgrViewTable() {
|
||||
Table copy = MakeViewTable();
|
||||
EnableRange(copy, VIEW_CLASS_32_BITS);
|
||||
return copy;
|
||||
}
|
||||
|
||||
constexpr Table MakeNonNativeBgrViewTable() {
|
||||
Table copy = MakeViewTable();
|
||||
EnableRange(copy, VIEW_CLASS_32_BITS_NO_BGR);
|
||||
return copy;
|
||||
}
|
||||
|
||||
constexpr Table MakeNativeBgrCopyTable() {
|
||||
Table copy = MakeCopyTable();
|
||||
EnableRange(copy, VIEW_CLASS_32_BITS);
|
||||
return copy;
|
||||
}
|
||||
|
||||
constexpr Table MakeNonNativeBgrCopyTable() {
|
||||
Table copy = MakeCopyTable();
|
||||
EnableRange(copy, VIEW_CLASS_32_BITS);
|
||||
return copy;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
bool IsViewCompatible(PixelFormat format_a, PixelFormat format_b, bool broken_views) {
|
||||
bool IsViewCompatible(PixelFormat format_a, PixelFormat format_b, bool broken_views,
|
||||
bool native_bgr) {
|
||||
if (broken_views) {
|
||||
// If format views are broken, only accept formats that are identical.
|
||||
return format_a == format_b;
|
||||
}
|
||||
static constexpr Table TABLE = MakeViewTable();
|
||||
return IsSupported(TABLE, format_a, format_b);
|
||||
static constexpr Table BGR_TABLE = MakeNativeBgrViewTable();
|
||||
static constexpr Table NO_BGR_TABLE = MakeNonNativeBgrViewTable();
|
||||
return IsSupported(native_bgr ? BGR_TABLE : NO_BGR_TABLE, format_a, format_b);
|
||||
}
|
||||
|
||||
bool IsCopyCompatible(PixelFormat format_a, PixelFormat format_b) {
|
||||
static constexpr Table TABLE = MakeCopyTable();
|
||||
return IsSupported(TABLE, format_a, format_b);
|
||||
bool IsCopyCompatible(PixelFormat format_a, PixelFormat format_b, bool native_bgr) {
|
||||
static constexpr Table BGR_TABLE = MakeNativeBgrCopyTable();
|
||||
static constexpr Table NO_BGR_TABLE = MakeNonNativeBgrCopyTable();
|
||||
return IsSupported(native_bgr ? BGR_TABLE : NO_BGR_TABLE, format_a, format_b);
|
||||
}
|
||||
|
||||
} // namespace VideoCore::Surface
|
||||
|
||||
@@ -8,8 +8,9 @@
|
||||
|
||||
namespace VideoCore::Surface {
|
||||
|
||||
bool IsViewCompatible(PixelFormat format_a, PixelFormat format_b, bool broken_views);
|
||||
bool IsViewCompatible(PixelFormat format_a, PixelFormat format_b, bool broken_views,
|
||||
bool native_bgr);
|
||||
|
||||
bool IsCopyCompatible(PixelFormat format_a, PixelFormat format_b);
|
||||
bool IsCopyCompatible(PixelFormat format_a, PixelFormat format_b, bool native_bgr);
|
||||
|
||||
} // namespace VideoCore::Surface
|
||||
|
||||
@@ -459,7 +459,7 @@ void GPU::ProcessSemaphoreAcquire() {
|
||||
}
|
||||
|
||||
void GPU::Start() {
|
||||
gpu_thread.StartThread(*renderer, renderer->Context(), *dma_pusher, *cdma_pusher);
|
||||
gpu_thread.StartThread(*renderer, renderer->Context(), *dma_pusher);
|
||||
cpu_context = renderer->GetRenderWindow().CreateSharedContext();
|
||||
cpu_context->MakeCurrent();
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace VideoCommon::GPUThread {
|
||||
/// Runs the GPU thread
|
||||
static void RunThread(Core::System& system, VideoCore::RendererBase& renderer,
|
||||
Core::Frontend::GraphicsContext& context, Tegra::DmaPusher& dma_pusher,
|
||||
SynchState& state, Tegra::CDmaPusher& cdma_pusher) {
|
||||
SynchState& state) {
|
||||
std::string name = "yuzu:GPU";
|
||||
MicroProfileOnThreadCreate(name.c_str());
|
||||
SCOPE_EXIT({ MicroProfileOnThreadExit(); });
|
||||
@@ -46,9 +46,6 @@ static void RunThread(Core::System& system, VideoCore::RendererBase& renderer,
|
||||
if (auto* submit_list = std::get_if<SubmitListCommand>(&next.data)) {
|
||||
dma_pusher.Push(std::move(submit_list->entries));
|
||||
dma_pusher.DispatchCalls();
|
||||
} else if (auto* command_list = std::get_if<SubmitChCommandEntries>(&next.data)) {
|
||||
// NVDEC
|
||||
cdma_pusher.ProcessEntries(std::move(command_list->entries));
|
||||
} else if (const auto* data = std::get_if<SwapBuffersCommand>(&next.data)) {
|
||||
renderer.SwapBuffers(data->framebuffer ? &*data->framebuffer : nullptr);
|
||||
} else if (std::holds_alternative<OnCommandListEndCommand>(next.data)) {
|
||||
@@ -83,20 +80,16 @@ ThreadManager::~ThreadManager() {
|
||||
|
||||
void ThreadManager::StartThread(VideoCore::RendererBase& renderer,
|
||||
Core::Frontend::GraphicsContext& context,
|
||||
Tegra::DmaPusher& dma_pusher, Tegra::CDmaPusher& cdma_pusher) {
|
||||
Tegra::DmaPusher& dma_pusher) {
|
||||
rasterizer = renderer.ReadRasterizer();
|
||||
thread = std::thread(RunThread, std::ref(system), std::ref(renderer), std::ref(context),
|
||||
std::ref(dma_pusher), std::ref(state), std::ref(cdma_pusher));
|
||||
std::ref(dma_pusher), std::ref(state));
|
||||
}
|
||||
|
||||
void ThreadManager::SubmitList(Tegra::CommandList&& entries) {
|
||||
PushCommand(SubmitListCommand(std::move(entries)));
|
||||
}
|
||||
|
||||
void ThreadManager::SubmitCommandBuffer(Tegra::ChCommandHeaderList&& entries) {
|
||||
PushCommand(SubmitChCommandEntries(std::move(entries)));
|
||||
}
|
||||
|
||||
void ThreadManager::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
|
||||
PushCommand(SwapBuffersCommand(framebuffer ? std::make_optional(*framebuffer) : std::nullopt));
|
||||
}
|
||||
|
||||
@@ -43,14 +43,6 @@ struct SubmitListCommand final {
|
||||
Tegra::CommandList entries;
|
||||
};
|
||||
|
||||
/// Command to signal to the GPU thread that a cdma command list is ready for processing
|
||||
struct SubmitChCommandEntries final {
|
||||
explicit SubmitChCommandEntries(Tegra::ChCommandHeaderList&& entries_)
|
||||
: entries{std::move(entries_)} {}
|
||||
|
||||
Tegra::ChCommandHeaderList entries;
|
||||
};
|
||||
|
||||
/// Command to signal to the GPU thread that a swap buffers is pending
|
||||
struct SwapBuffersCommand final {
|
||||
explicit SwapBuffersCommand(std::optional<const Tegra::FramebufferConfig> framebuffer_)
|
||||
@@ -91,9 +83,9 @@ struct OnCommandListEndCommand final {};
|
||||
struct GPUTickCommand final {};
|
||||
|
||||
using CommandData =
|
||||
std::variant<EndProcessingCommand, SubmitListCommand, SubmitChCommandEntries,
|
||||
SwapBuffersCommand, FlushRegionCommand, InvalidateRegionCommand,
|
||||
FlushAndInvalidateRegionCommand, OnCommandListEndCommand, GPUTickCommand>;
|
||||
std::variant<EndProcessingCommand, SubmitListCommand, SwapBuffersCommand, FlushRegionCommand,
|
||||
InvalidateRegionCommand, FlushAndInvalidateRegionCommand, OnCommandListEndCommand,
|
||||
GPUTickCommand>;
|
||||
|
||||
struct CommandDataContainer {
|
||||
CommandDataContainer() = default;
|
||||
@@ -123,14 +115,11 @@ public:
|
||||
|
||||
/// Creates and starts the GPU thread.
|
||||
void StartThread(VideoCore::RendererBase& renderer, Core::Frontend::GraphicsContext& context,
|
||||
Tegra::DmaPusher& dma_pusher, Tegra::CDmaPusher& cdma_pusher);
|
||||
Tegra::DmaPusher& dma_pusher);
|
||||
|
||||
/// Push GPU command entries to be processed
|
||||
void SubmitList(Tegra::CommandList&& entries);
|
||||
|
||||
/// Push GPU CDMA command buffer entries to be processed
|
||||
void SubmitCommandBuffer(Tegra::ChCommandHeaderList&& entries);
|
||||
|
||||
/// Swap buffers (render frame)
|
||||
void SwapBuffers(const Tegra::FramebufferConfig* framebuffer);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
15
src/video_core/host_shaders/opengl_copy_bgra.comp
Normal file
15
src/video_core/host_shaders/opengl_copy_bgra.comp
Normal file
@@ -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);
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -239,6 +239,7 @@ Device::Device() {
|
||||
has_nv_viewport_array2 = GLAD_GL_NV_viewport_array2;
|
||||
has_vertex_buffer_unified_memory = GLAD_GL_NV_vertex_buffer_unified_memory;
|
||||
has_debugging_tool_attached = IsDebugToolAttached(extensions);
|
||||
has_depth_buffer_float = HasExtension(extensions, "GL_NV_depth_buffer_float");
|
||||
|
||||
// At the moment of writing this, only Nvidia's driver optimizes BufferSubData on exclusive
|
||||
// uniform buffers as "push constants"
|
||||
@@ -275,6 +276,7 @@ Device::Device(std::nullptr_t) {
|
||||
has_image_load_formatted = true;
|
||||
has_texture_shadow_lod = true;
|
||||
has_variable_aoffi = true;
|
||||
has_depth_buffer_float = true;
|
||||
}
|
||||
|
||||
bool Device::TestVariableAoffi() {
|
||||
|
||||
@@ -122,6 +122,10 @@ public:
|
||||
return use_driver_cache;
|
||||
}
|
||||
|
||||
bool HasDepthBufferFloat() const {
|
||||
return has_depth_buffer_float;
|
||||
}
|
||||
|
||||
private:
|
||||
static bool TestVariableAoffi();
|
||||
static bool TestPreciseBug();
|
||||
@@ -150,6 +154,7 @@ private:
|
||||
bool use_assembly_shaders{};
|
||||
bool use_asynchronous_shaders{};
|
||||
bool use_driver_cache{};
|
||||
bool has_depth_buffer_float{};
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
||||
|
||||
@@ -889,7 +889,11 @@ void RasterizerOpenGL::SyncViewport() {
|
||||
const GLdouble reduce_z = regs.depth_mode == Maxwell::DepthMode::MinusOneToOne;
|
||||
const GLdouble near_depth = src.translate_z - src.scale_z * reduce_z;
|
||||
const GLdouble far_depth = src.translate_z + src.scale_z;
|
||||
glDepthRangeIndexed(static_cast<GLuint>(i), near_depth, far_depth);
|
||||
if (device.HasDepthBufferFloat()) {
|
||||
glDepthRangeIndexeddNV(static_cast<GLuint>(i), near_depth, far_depth);
|
||||
} else {
|
||||
glDepthRangeIndexed(static_cast<GLuint>(i), near_depth, far_depth);
|
||||
}
|
||||
|
||||
if (!GLAD_GL_NV_viewport_swizzle) {
|
||||
continue;
|
||||
|
||||
@@ -335,6 +335,10 @@ void ShaderCacheOpenGL::LoadDiskCache(u64 title_id, const std::atomic_bool& stop
|
||||
const VideoCore::DiskResourceLoadCallback& callback) {
|
||||
disk_cache.BindTitleID(title_id);
|
||||
const std::optional transferable = disk_cache.LoadTransferable();
|
||||
|
||||
LOG_INFO(Render_OpenGL, "Total Shader Count: {}",
|
||||
transferable.has_value() ? transferable->size() : 0);
|
||||
|
||||
if (!transferable) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -201,10 +201,6 @@ void BufferCacheRuntime::BindTransformFeedbackBuffer(u32 index, VkBuffer buffer,
|
||||
});
|
||||
}
|
||||
|
||||
void BufferCacheRuntime::BindBuffer(VkBuffer buffer, u32 offset, u32 size) {
|
||||
update_descriptor_queue.AddBuffer(buffer, offset, size);
|
||||
}
|
||||
|
||||
void BufferCacheRuntime::ReserveQuadArrayLUT(u32 num_indices, bool wait_for_idle) {
|
||||
if (num_indices <= current_num_indices) {
|
||||
return;
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/renderer_vulkan/vk_compute_pass.h"
|
||||
#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
|
||||
#include "video_core/renderer_vulkan/vk_update_descriptor.h"
|
||||
#include "video_core/vulkan_common/vulkan_memory_allocator.h"
|
||||
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
||||
|
||||
@@ -16,7 +17,6 @@ namespace Vulkan {
|
||||
class Device;
|
||||
class VKDescriptorPool;
|
||||
class VKScheduler;
|
||||
class VKUpdateDescriptorQueue;
|
||||
|
||||
class BufferCacheRuntime;
|
||||
|
||||
@@ -86,7 +86,9 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
void BindBuffer(VkBuffer buffer, u32 offset, u32 size);
|
||||
void BindBuffer(VkBuffer buffer, u32 offset, u32 size) {
|
||||
update_descriptor_queue.AddBuffer(buffer, offset, size);
|
||||
}
|
||||
|
||||
void ReserveQuadArrayLUT(u32 num_indices, bool wait_for_idle);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1845,13 +1845,21 @@ private:
|
||||
|
||||
Expression TextureGather(Operation operation) {
|
||||
const auto& meta = std::get<MetaTexture>(operation.GetMeta());
|
||||
UNIMPLEMENTED_IF(!meta.aoffi.empty());
|
||||
|
||||
const Id coords = GetCoordinates(operation, Type::Float);
|
||||
|
||||
spv::ImageOperandsMask mask = spv::ImageOperandsMask::MaskNone;
|
||||
std::vector<Id> operands;
|
||||
Id texture{};
|
||||
|
||||
if (!meta.aoffi.empty()) {
|
||||
mask = mask | spv::ImageOperandsMask::Offset;
|
||||
operands.push_back(GetOffsetCoordinates(operation));
|
||||
}
|
||||
|
||||
if (meta.sampler.is_shadow) {
|
||||
texture = OpImageDrefGather(t_float4, GetTextureSampler(operation), coords,
|
||||
AsFloat(Visit(meta.depth_compare)));
|
||||
AsFloat(Visit(meta.depth_compare)), mask, operands);
|
||||
} else {
|
||||
u32 component_value = 0;
|
||||
if (meta.component) {
|
||||
@@ -1860,7 +1868,7 @@ private:
|
||||
component_value = component->GetValue();
|
||||
}
|
||||
texture = OpImageGather(t_float4, GetTextureSampler(operation), coords,
|
||||
Constant(t_uint, component_value));
|
||||
Constant(t_uint, component_value), mask, operands);
|
||||
}
|
||||
return GetTextureElement(operation, texture, Type::Float);
|
||||
}
|
||||
@@ -1928,13 +1936,22 @@ private:
|
||||
|
||||
const Id image = GetTextureImage(operation);
|
||||
const Id coords = GetCoordinates(operation, Type::Int);
|
||||
|
||||
spv::ImageOperandsMask mask = spv::ImageOperandsMask::MaskNone;
|
||||
std::vector<Id> operands;
|
||||
Id fetch;
|
||||
|
||||
if (meta.lod && !meta.sampler.is_buffer) {
|
||||
fetch = OpImageFetch(t_float4, image, coords, spv::ImageOperandsMask::Lod,
|
||||
AsInt(Visit(meta.lod)));
|
||||
} else {
|
||||
fetch = OpImageFetch(t_float4, image, coords);
|
||||
mask = mask | spv::ImageOperandsMask::Lod;
|
||||
operands.push_back(AsInt(Visit(meta.lod)));
|
||||
}
|
||||
|
||||
if (!meta.aoffi.empty()) {
|
||||
mask = mask | spv::ImageOperandsMask::Offset;
|
||||
operands.push_back(GetOffsetCoordinates(operation));
|
||||
}
|
||||
|
||||
fetch = OpImageFetch(t_float4, image, coords, mask, operands);
|
||||
return GetTextureElement(operation, fetch, Type::Float);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -20,20 +20,20 @@ VKUpdateDescriptorQueue::VKUpdateDescriptorQueue(const Device& device_, VKSchedu
|
||||
VKUpdateDescriptorQueue::~VKUpdateDescriptorQueue() = default;
|
||||
|
||||
void VKUpdateDescriptorQueue::TickFrame() {
|
||||
payload.clear();
|
||||
payload_cursor = payload.data();
|
||||
}
|
||||
|
||||
void VKUpdateDescriptorQueue::Acquire() {
|
||||
// Minimum number of entries required.
|
||||
// This is the maximum number of entries a single draw call migth use.
|
||||
static constexpr std::size_t MIN_ENTRIES = 0x400;
|
||||
static constexpr size_t MIN_ENTRIES = 0x400;
|
||||
|
||||
if (payload.size() + MIN_ENTRIES >= payload.max_size()) {
|
||||
if (std::distance(payload.data(), payload_cursor) + MIN_ENTRIES >= payload.max_size()) {
|
||||
LOG_WARNING(Render_Vulkan, "Payload overflow, waiting for worker thread");
|
||||
scheduler.WaitWorker();
|
||||
payload.clear();
|
||||
payload_cursor = payload.data();
|
||||
}
|
||||
upload_start = &*payload.end();
|
||||
upload_start = payload_cursor;
|
||||
}
|
||||
|
||||
void VKUpdateDescriptorQueue::Send(VkDescriptorUpdateTemplateKHR update_template,
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <variant>
|
||||
#include <boost/container/static_vector.hpp>
|
||||
#include <array>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
||||
@@ -16,13 +15,15 @@ class Device;
|
||||
class VKScheduler;
|
||||
|
||||
struct DescriptorUpdateEntry {
|
||||
struct Empty {};
|
||||
|
||||
DescriptorUpdateEntry() = default;
|
||||
DescriptorUpdateEntry(VkDescriptorImageInfo image_) : image{image_} {}
|
||||
|
||||
DescriptorUpdateEntry(VkDescriptorBufferInfo buffer_) : buffer{buffer_} {}
|
||||
|
||||
DescriptorUpdateEntry(VkBufferView texel_buffer_) : texel_buffer{texel_buffer_} {}
|
||||
|
||||
union {
|
||||
Empty empty{};
|
||||
VkDescriptorImageInfo image;
|
||||
VkDescriptorBufferInfo buffer;
|
||||
VkBufferView texel_buffer;
|
||||
@@ -41,39 +42,40 @@ public:
|
||||
void Send(VkDescriptorUpdateTemplateKHR update_template, VkDescriptorSet set);
|
||||
|
||||
void AddSampledImage(VkImageView image_view, VkSampler sampler) {
|
||||
payload.emplace_back(VkDescriptorImageInfo{
|
||||
*(payload_cursor++) = VkDescriptorImageInfo{
|
||||
.sampler = sampler,
|
||||
.imageView = image_view,
|
||||
.imageLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
void AddImage(VkImageView image_view) {
|
||||
payload.emplace_back(VkDescriptorImageInfo{
|
||||
*(payload_cursor++) = VkDescriptorImageInfo{
|
||||
.sampler = VK_NULL_HANDLE,
|
||||
.imageView = image_view,
|
||||
.imageLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
void AddBuffer(VkBuffer buffer, u64 offset, size_t size) {
|
||||
payload.emplace_back(VkDescriptorBufferInfo{
|
||||
void AddBuffer(VkBuffer buffer, VkDeviceSize offset, VkDeviceSize size) {
|
||||
*(payload_cursor++) = VkDescriptorBufferInfo{
|
||||
.buffer = buffer,
|
||||
.offset = offset,
|
||||
.range = size,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
void AddTexelBuffer(VkBufferView texel_buffer) {
|
||||
payload.emplace_back(texel_buffer);
|
||||
*(payload_cursor++) = texel_buffer;
|
||||
}
|
||||
|
||||
private:
|
||||
const Device& device;
|
||||
VKScheduler& scheduler;
|
||||
|
||||
DescriptorUpdateEntry* payload_cursor = nullptr;
|
||||
const DescriptorUpdateEntry* upload_start = nullptr;
|
||||
boost::container::static_vector<DescriptorUpdateEntry, 0x10000> payload;
|
||||
std::array<DescriptorUpdateEntry, 0x10000> payload;
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
||||
|
||||
@@ -330,6 +330,7 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
|
||||
case StoreType::Bits32:
|
||||
(this->*set_memory)(bb, GetAddress(0), GetRegister(instr.gpr0));
|
||||
break;
|
||||
case StoreType::Unsigned16:
|
||||
case StoreType::Signed16: {
|
||||
Node address = GetAddress(0);
|
||||
Node memory = (this->*get_memory)(address);
|
||||
|
||||
@@ -806,6 +806,7 @@ Node4 ShaderIR::GetTldsCode(Instruction instr, TextureType texture_type, bool is
|
||||
|
||||
const std::size_t type_coord_count = GetCoordCount(texture_type);
|
||||
const bool lod_enabled = instr.tlds.GetTextureProcessMode() == TextureProcessMode::LL;
|
||||
const bool aoffi_enabled = instr.tlds.UsesMiscMode(TextureMiscMode::AOFFI);
|
||||
|
||||
// If enabled arrays index is always stored in the gpr8 field
|
||||
const u64 array_register = instr.gpr8.Value();
|
||||
@@ -820,17 +821,23 @@ Node4 ShaderIR::GetTldsCode(Instruction instr, TextureType texture_type, bool is
|
||||
std::vector<Node> coords;
|
||||
for (std::size_t i = 0; i < type_coord_count; ++i) {
|
||||
const bool last = (i == (type_coord_count - 1)) && (type_coord_count > 1);
|
||||
coords.push_back(GetRegister(last ? last_coord_register : coord_register + i));
|
||||
coords.push_back(
|
||||
GetRegister(last && !aoffi_enabled ? last_coord_register : coord_register + i));
|
||||
}
|
||||
|
||||
const Node array = is_array ? GetRegister(array_register) : nullptr;
|
||||
// When lod is used always is in gpr20
|
||||
const Node lod = lod_enabled ? GetRegister(instr.gpr20) : Immediate(0);
|
||||
|
||||
std::vector<Node> aoffi;
|
||||
if (aoffi_enabled) {
|
||||
aoffi = GetAoffiCoordinates(GetRegister(instr.gpr20), type_coord_count, false);
|
||||
}
|
||||
|
||||
Node4 values;
|
||||
for (u32 element = 0; element < values.size(); ++element) {
|
||||
auto coords_copy = coords;
|
||||
MetaTexture meta{*sampler, array, {}, {}, {}, {}, {}, lod, {}, element, {}};
|
||||
MetaTexture meta{*sampler, array, {}, aoffi, {}, {}, {}, lod, {}, element, {}};
|
||||
values[element] = Operation(OperationCode::TexelFetch, meta, std::move(coords_copy));
|
||||
}
|
||||
return values;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <mutex>
|
||||
#include "video_core/shader_notify.h"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
@@ -120,9 +120,10 @@ void AddImageAlias(ImageBase& lhs, ImageBase& rhs, ImageId lhs_id, ImageId rhs_i
|
||||
if (lhs.info.type == ImageType::Linear) {
|
||||
base = SubresourceBase{.level = 0, .layer = 0};
|
||||
} else {
|
||||
// We are passing relaxed formats as an option, having broken views or not won't matter
|
||||
// We are passing relaxed formats as an option, having broken views/bgr or not won't matter
|
||||
static constexpr bool broken_views = false;
|
||||
base = FindSubresource(rhs.info, lhs, rhs.gpu_addr, OPTIONS, broken_views);
|
||||
static constexpr bool native_bgr = true;
|
||||
base = FindSubresource(rhs.info, lhs, rhs.gpu_addr, OPTIONS, broken_views, native_bgr);
|
||||
}
|
||||
if (!base) {
|
||||
LOG_ERROR(HW_GPU, "Image alias should have been flipped");
|
||||
|
||||
@@ -24,7 +24,7 @@ ImageViewBase::ImageViewBase(const ImageViewInfo& info, const ImageInfo& image_i
|
||||
.height = std::max(image_info.size.height >> range.base.level, 1u),
|
||||
.depth = std::max(image_info.size.depth >> range.base.level, 1u),
|
||||
} {
|
||||
ASSERT_MSG(VideoCore::Surface::IsViewCompatible(image_info.format, info.format, false),
|
||||
ASSERT_MSG(VideoCore::Surface::IsViewCompatible(image_info.format, info.format, false, true),
|
||||
"Image view format {} is incompatible with image format {}", info.format,
|
||||
image_info.format);
|
||||
const bool is_async = Settings::values.use_asynchronous_gpu_emulation.GetValue();
|
||||
|
||||
@@ -876,6 +876,7 @@ ImageId TextureCache<P>::FindImage(const ImageInfo& info, GPUVAddr gpu_addr,
|
||||
return ImageId{};
|
||||
}
|
||||
const bool broken_views = runtime.HasBrokenTextureViewFormats();
|
||||
const bool native_bgr = runtime.HasNativeBgr();
|
||||
ImageId image_id;
|
||||
const auto lambda = [&](ImageId existing_image_id, ImageBase& existing_image) {
|
||||
if (info.type == ImageType::Linear || existing_image.info.type == ImageType::Linear) {
|
||||
@@ -885,11 +886,12 @@ ImageId TextureCache<P>::FindImage(const ImageInfo& info, GPUVAddr gpu_addr,
|
||||
if (existing_image.gpu_addr == gpu_addr && existing.type == info.type &&
|
||||
existing.pitch == info.pitch &&
|
||||
IsPitchLinearSameSize(existing, info, strict_size) &&
|
||||
IsViewCompatible(existing.format, info.format, broken_views)) {
|
||||
IsViewCompatible(existing.format, info.format, broken_views, native_bgr)) {
|
||||
image_id = existing_image_id;
|
||||
return true;
|
||||
}
|
||||
} else if (IsSubresource(info, existing_image, gpu_addr, options, broken_views)) {
|
||||
} else if (IsSubresource(info, existing_image, gpu_addr, options, broken_views,
|
||||
native_bgr)) {
|
||||
image_id = existing_image_id;
|
||||
return true;
|
||||
}
|
||||
@@ -920,6 +922,7 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
|
||||
ImageInfo new_info = info;
|
||||
const size_t size_bytes = CalculateGuestSizeInBytes(new_info);
|
||||
const bool broken_views = runtime.HasBrokenTextureViewFormats();
|
||||
const bool native_bgr = runtime.HasNativeBgr();
|
||||
std::vector<ImageId> overlap_ids;
|
||||
std::vector<ImageId> left_aliased_ids;
|
||||
std::vector<ImageId> right_aliased_ids;
|
||||
@@ -935,8 +938,8 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
|
||||
return;
|
||||
}
|
||||
static constexpr bool strict_size = true;
|
||||
const std::optional<OverlapResult> solution =
|
||||
ResolveOverlap(new_info, gpu_addr, cpu_addr, overlap, strict_size, broken_views);
|
||||
const std::optional<OverlapResult> solution = ResolveOverlap(
|
||||
new_info, gpu_addr, cpu_addr, overlap, strict_size, broken_views, native_bgr);
|
||||
if (solution) {
|
||||
gpu_addr = solution->gpu_addr;
|
||||
cpu_addr = solution->cpu_addr;
|
||||
@@ -946,10 +949,10 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
|
||||
}
|
||||
static constexpr auto options = RelaxedOptions::Size | RelaxedOptions::Format;
|
||||
const ImageBase new_image_base(new_info, gpu_addr, cpu_addr);
|
||||
if (IsSubresource(new_info, overlap, gpu_addr, options, broken_views)) {
|
||||
if (IsSubresource(new_info, overlap, gpu_addr, options, broken_views, native_bgr)) {
|
||||
left_aliased_ids.push_back(overlap_id);
|
||||
} else if (IsSubresource(overlap.info, new_image_base, overlap.gpu_addr, options,
|
||||
broken_views)) {
|
||||
broken_views, native_bgr)) {
|
||||
right_aliased_ids.push_back(overlap_id);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1035,13 +1035,13 @@ bool IsPitchLinearSameSize(const ImageInfo& lhs, const ImageInfo& rhs, bool stri
|
||||
|
||||
std::optional<OverlapResult> ResolveOverlap(const ImageInfo& new_info, GPUVAddr gpu_addr,
|
||||
VAddr cpu_addr, const ImageBase& overlap,
|
||||
bool strict_size, bool broken_views) {
|
||||
bool strict_size, bool broken_views, bool native_bgr) {
|
||||
ASSERT(new_info.type != ImageType::Linear);
|
||||
ASSERT(overlap.info.type != ImageType::Linear);
|
||||
if (!IsLayerStrideCompatible(new_info, overlap.info)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
if (!IsViewCompatible(overlap.info.format, new_info.format, broken_views)) {
|
||||
if (!IsViewCompatible(overlap.info.format, new_info.format, broken_views, native_bgr)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
if (gpu_addr == overlap.gpu_addr) {
|
||||
@@ -1085,14 +1085,14 @@ bool IsLayerStrideCompatible(const ImageInfo& lhs, const ImageInfo& rhs) {
|
||||
|
||||
std::optional<SubresourceBase> FindSubresource(const ImageInfo& candidate, const ImageBase& image,
|
||||
GPUVAddr candidate_addr, RelaxedOptions options,
|
||||
bool broken_views) {
|
||||
bool broken_views, bool native_bgr) {
|
||||
const std::optional<SubresourceBase> base = image.TryFindBase(candidate_addr);
|
||||
if (!base) {
|
||||
return std::nullopt;
|
||||
}
|
||||
const ImageInfo& existing = image.info;
|
||||
if (False(options & RelaxedOptions::Format)) {
|
||||
if (!IsViewCompatible(existing.format, candidate.format, broken_views)) {
|
||||
if (!IsViewCompatible(existing.format, candidate.format, broken_views, native_bgr)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
@@ -1129,8 +1129,9 @@ std::optional<SubresourceBase> FindSubresource(const ImageInfo& candidate, const
|
||||
}
|
||||
|
||||
bool IsSubresource(const ImageInfo& candidate, const ImageBase& image, GPUVAddr candidate_addr,
|
||||
RelaxedOptions options, bool broken_views) {
|
||||
return FindSubresource(candidate, image, candidate_addr, options, broken_views).has_value();
|
||||
RelaxedOptions options, bool broken_views, bool native_bgr) {
|
||||
return FindSubresource(candidate, image, candidate_addr, options, broken_views, native_bgr)
|
||||
.has_value();
|
||||
}
|
||||
|
||||
void DeduceBlitImages(ImageInfo& dst_info, ImageInfo& src_info, const ImageBase* dst,
|
||||
|
||||
@@ -87,7 +87,8 @@ void SwizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, const Ima
|
||||
[[nodiscard]] std::optional<OverlapResult> ResolveOverlap(const ImageInfo& new_info,
|
||||
GPUVAddr gpu_addr, VAddr cpu_addr,
|
||||
const ImageBase& overlap,
|
||||
bool strict_size, bool broken_views);
|
||||
bool strict_size, bool broken_views,
|
||||
bool native_bgr);
|
||||
|
||||
[[nodiscard]] bool IsLayerStrideCompatible(const ImageInfo& lhs, const ImageInfo& rhs);
|
||||
|
||||
@@ -95,11 +96,11 @@ void SwizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, const Ima
|
||||
const ImageBase& image,
|
||||
GPUVAddr candidate_addr,
|
||||
RelaxedOptions options,
|
||||
bool broken_views);
|
||||
bool broken_views, bool native_bgr);
|
||||
|
||||
[[nodiscard]] bool IsSubresource(const ImageInfo& candidate, const ImageBase& image,
|
||||
GPUVAddr candidate_addr, RelaxedOptions options,
|
||||
bool broken_views);
|
||||
GPUVAddr candidate_addr, RelaxedOptions options, bool broken_views,
|
||||
bool native_bgr);
|
||||
|
||||
void DeduceBlitImages(ImageInfo& dst_info, ImageInfo& src_info, const ImageBase* dst,
|
||||
const ImageBase* src);
|
||||
|
||||
@@ -383,6 +383,25 @@ void GRenderWindow::keyReleaseEvent(QKeyEvent* event) {
|
||||
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) {
|
||||
// Touch input is handled in TouchBeginEvent
|
||||
if (event->source() == Qt::MouseEventSynthesizedBySystem) {
|
||||
@@ -391,7 +410,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 +445,8 @@ void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
|
||||
return;
|
||||
}
|
||||
|
||||
input_subsystem->GetMouse()->ReleaseButton(event->button());
|
||||
const auto button = QtButtonToMouseButton(event->button());
|
||||
input_subsystem->GetMouse()->ReleaseButton(button);
|
||||
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
this->TouchReleased(0);
|
||||
|
||||
@@ -28,6 +28,10 @@ namespace InputCommon {
|
||||
class InputSubsystem;
|
||||
}
|
||||
|
||||
namespace MouseInput {
|
||||
enum class MouseButton;
|
||||
}
|
||||
|
||||
namespace VideoCore {
|
||||
enum class LoadCallbackStage;
|
||||
}
|
||||
@@ -149,6 +153,9 @@ public:
|
||||
void keyPressEvent(QKeyEvent* event) override;
|
||||
void keyReleaseEvent(QKeyEvent* event) override;
|
||||
|
||||
/// Converts a Qt mouse button into MouseInput mouse button
|
||||
static MouseInput::MouseButton QtButtonToMouseButton(Qt::MouseButton button);
|
||||
|
||||
void mousePressEvent(QMouseEvent* event) override;
|
||||
void mouseMoveEvent(QMouseEvent* event) override;
|
||||
void mouseReleaseEvent(QMouseEvent* event) override;
|
||||
|
||||
@@ -235,7 +235,7 @@ const std::array<UISettings::Shortcut, 17> Config::default_hotkeys{{
|
||||
{QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Toggle Mouse Panning"), QStringLiteral("Main Window"), {QStringLiteral("F9"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Toggle Mouse Panning"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F9"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Toggle Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Z"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Toggle Status Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+S"), Qt::WindowShortcut}},
|
||||
}};
|
||||
@@ -508,7 +508,7 @@ void Config::ReadControlValues() {
|
||||
|
||||
Settings::values.emulate_analog_keyboard =
|
||||
ReadSetting(QStringLiteral("emulate_analog_keyboard"), false).toBool();
|
||||
Settings::values.mouse_panning = ReadSetting(QStringLiteral("mouse_panning"), false).toBool();
|
||||
Settings::values.mouse_panning = false;
|
||||
Settings::values.mouse_panning_sensitivity =
|
||||
ReadSetting(QStringLiteral("mouse_panning_sensitivity"), 1).toFloat();
|
||||
|
||||
@@ -648,7 +648,7 @@ void Config::ReadDebuggingValues() {
|
||||
void Config::ReadServiceValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Services"));
|
||||
Settings::values.bcat_backend =
|
||||
ReadSetting(QStringLiteral("bcat_backend"), QStringLiteral("null"))
|
||||
ReadSetting(QStringLiteral("bcat_backend"), QStringLiteral("none"))
|
||||
.toString()
|
||||
.toStdString();
|
||||
Settings::values.bcat_boxcat_local =
|
||||
@@ -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();
|
||||
@@ -1239,7 +1238,7 @@ void Config::SaveDebuggingValues() {
|
||||
void Config::SaveServiceValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Services"));
|
||||
WriteSetting(QStringLiteral("bcat_backend"),
|
||||
QString::fromStdString(Settings::values.bcat_backend), QStringLiteral("null"));
|
||||
QString::fromStdString(Settings::values.bcat_backend), QStringLiteral("none"));
|
||||
WriteSetting(QStringLiteral("bcat_boxcat_local"), Settings::values.bcat_boxcat_local, false);
|
||||
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"
|
||||
@@ -1345,7 +1346,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:
|
||||
|
||||
@@ -854,8 +854,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 +1207,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 +2374,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 +2620,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 +2630,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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -457,7 +457,7 @@ void Config::ReadValues() {
|
||||
Settings::values.yuzu_token = sdl2_config->Get("WebService", "yuzu_token", "");
|
||||
|
||||
// Services
|
||||
Settings::values.bcat_backend = sdl2_config->Get("Services", "bcat_backend", "null");
|
||||
Settings::values.bcat_backend = sdl2_config->Get("Services", "bcat_backend", "none");
|
||||
Settings::values.bcat_boxcat_local =
|
||||
sdl2_config->GetBoolean("Services", "bcat_boxcat_local", false);
|
||||
}
|
||||
|
||||
@@ -35,18 +35,36 @@ void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
|
||||
input_subsystem->GetMouse()->MouseMove(x, y, 0, 0);
|
||||
}
|
||||
|
||||
MouseInput::MouseButton EmuWindow_SDL2::SDLButtonToMouseButton(u32 button) const {
|
||||
switch (button) {
|
||||
case SDL_BUTTON_LEFT:
|
||||
return MouseInput::MouseButton::Left;
|
||||
case SDL_BUTTON_RIGHT:
|
||||
return MouseInput::MouseButton::Right;
|
||||
case SDL_BUTTON_MIDDLE:
|
||||
return MouseInput::MouseButton::Wheel;
|
||||
case SDL_BUTTON_X1:
|
||||
return MouseInput::MouseButton::Backward;
|
||||
case SDL_BUTTON_X2:
|
||||
return MouseInput::MouseButton::Forward;
|
||||
default:
|
||||
return MouseInput::MouseButton::Undefined;
|
||||
}
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
|
||||
const auto mouse_button = SDLButtonToMouseButton(button);
|
||||
if (button == SDL_BUTTON_LEFT) {
|
||||
if (state == SDL_PRESSED) {
|
||||
TouchPressed((unsigned)std::max(x, 0), (unsigned)std::max(y, 0), 0);
|
||||
} else {
|
||||
TouchReleased(0);
|
||||
}
|
||||
} else if (button == SDL_BUTTON_RIGHT) {
|
||||
} else {
|
||||
if (state == SDL_PRESSED) {
|
||||
input_subsystem->GetMouse()->PressButton(x, y, button);
|
||||
input_subsystem->GetMouse()->PressButton(x, y, mouse_button);
|
||||
} else {
|
||||
input_subsystem->GetMouse()->ReleaseButton(button);
|
||||
input_subsystem->GetMouse()->ReleaseButton(mouse_button);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,10 @@ namespace InputCommon {
|
||||
class InputSubsystem;
|
||||
}
|
||||
|
||||
namespace MouseInput {
|
||||
enum class MouseButton;
|
||||
}
|
||||
|
||||
class EmuWindow_SDL2 : public Core::Frontend::EmuWindow {
|
||||
public:
|
||||
explicit EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem);
|
||||
@@ -42,6 +46,9 @@ protected:
|
||||
/// Called by WaitEvent when the mouse moves.
|
||||
void OnMouseMotion(s32 x, s32 y);
|
||||
|
||||
/// Converts a SDL mouse button into MouseInput mouse button
|
||||
MouseInput::MouseButton SDLButtonToMouseButton(u32 button) const;
|
||||
|
||||
/// Called by WaitEvent when a mouse button is pressed or released
|
||||
void OnMouseButton(u32 button, u8 state, s32 x, s32 y);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user