Compare commits
28 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2910aa77b2 | ||
|
|
9e9341f4b4 | ||
|
|
ee9ebeeb80 | ||
|
|
e895ab7d6f | ||
|
|
55f556c53e | ||
|
|
ab65cb499d | ||
|
|
09f7c355c6 | ||
|
|
bfa1644464 | ||
|
|
272bc4c3d6 | ||
|
|
1ba578c4aa | ||
|
|
d31dbb1bc1 | ||
|
|
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)
|
||||
|
||||
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;
|
||||
|
||||
@@ -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>; \
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -308,6 +308,9 @@ struct System::Impl {
|
||||
// Close all CPU/threading state
|
||||
cpu_manager.Shutdown();
|
||||
|
||||
// Release the Time Manager's resources
|
||||
time_manager.Shutdown();
|
||||
|
||||
// Shutdown kernel and core timing
|
||||
core_timing.Shutdown();
|
||||
kernel.Shutdown();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user