mirror of
https://github.com/Vita3K/Vita3K-Android.git
synced 2024-12-11 15:14:44 +00:00
touch: Improve accuracy of SceTouch functions.
This commit is contained in:
parent
8fb76acd45
commit
647e254265
@ -8,5 +8,5 @@ add_library(
|
||||
)
|
||||
|
||||
target_include_directories(display PUBLIC include)
|
||||
target_link_libraries(display PUBLIC mem threads kernel util)
|
||||
target_link_libraries(display PUBLIC host)
|
||||
target_link_libraries(display PRIVATE)
|
||||
|
@ -18,10 +18,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <kernel/callback.h>
|
||||
#include <util/types.h>
|
||||
|
||||
struct DisplayState;
|
||||
struct KernelState;
|
||||
struct HostState;
|
||||
|
||||
void start_sync_thread(DisplayState &display, KernelState &kernel);
|
||||
void wait_vblank(DisplayState &display, KernelState &kernel, const ThreadStatePtr &wait_thread, const int count, const bool is_cb);
|
||||
void start_sync_thread(HostState &host);
|
||||
void wait_vblank(DisplayState &display, KernelState &kernel, const ThreadStatePtr &wait_thread, const uint64_t target_vcount, const bool is_cb);
|
||||
|
@ -36,7 +36,7 @@ typedef std::shared_ptr<ThreadState> ThreadStatePtr;
|
||||
|
||||
struct DisplayStateVBlankWaitInfo {
|
||||
ThreadStatePtr target_thread;
|
||||
std::int32_t vsync_left;
|
||||
uint64_t target_vcount;
|
||||
bool is_cb;
|
||||
};
|
||||
|
||||
|
@ -15,10 +15,10 @@
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
#include <display/state.h>
|
||||
#include <kernel/state.h>
|
||||
#include <host/state.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <touch/functions.h>
|
||||
#include <util/find.h>
|
||||
|
||||
// Code heavily influenced by PPSSSPP's SceDisplay.cpp
|
||||
@ -26,7 +26,9 @@
|
||||
static constexpr int TARGET_FPS = 60;
|
||||
static constexpr int64_t TARGET_MICRO_PER_FRAME = 1000000LL / TARGET_FPS;
|
||||
|
||||
static void vblank_sync_thread(DisplayState &display, KernelState &kernel) {
|
||||
static void vblank_sync_thread(HostState &host) {
|
||||
DisplayState &display = host.display;
|
||||
|
||||
while (!display.abort.load()) {
|
||||
{
|
||||
const std::lock_guard<std::mutex> guard(display.mutex);
|
||||
@ -41,13 +43,16 @@ static void vblank_sync_thread(DisplayState &display, KernelState &kernel) {
|
||||
}
|
||||
}
|
||||
|
||||
// maybe we should also use a mutex for this part, but it shouldn't be an issue
|
||||
touch_vsync_update(host);
|
||||
|
||||
// Notify Vblank callback in each VBLANK start
|
||||
for (auto &cb : display.vblank_callbacks)
|
||||
cb.second->event_notify(cb.second->get_notifier_id());
|
||||
|
||||
for (std::size_t i = 0; i < display.vblank_wait_infos.size();) {
|
||||
auto &vblank_wait_info = display.vblank_wait_infos[i];
|
||||
if (--vblank_wait_info.vsync_left == 0) {
|
||||
if (vblank_wait_info.target_vcount <= display.vblank_count) {
|
||||
ThreadStatePtr target_wait = vblank_wait_info.target_thread;
|
||||
|
||||
target_wait->update_status(ThreadStatus::run);
|
||||
@ -57,7 +62,7 @@ static void vblank_sync_thread(DisplayState &display, KernelState &kernel) {
|
||||
CallbackPtr &cb = callback.second;
|
||||
if (cb->get_owner_thread_id() == target_wait->id) {
|
||||
std::string name = cb->get_name();
|
||||
cb->execute(kernel, [name]() {
|
||||
cb->execute(host.kernel, [name]() {
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -75,11 +80,11 @@ static void vblank_sync_thread(DisplayState &display, KernelState &kernel) {
|
||||
}
|
||||
}
|
||||
|
||||
void start_sync_thread(DisplayState &display, KernelState &kernel) {
|
||||
display.vblank_thread = std::make_unique<std::thread>(vblank_sync_thread, std::ref(display), std::ref(kernel));
|
||||
void start_sync_thread(HostState &host) {
|
||||
host.display.vblank_thread = std::make_unique<std::thread>(vblank_sync_thread, std::ref(host));
|
||||
}
|
||||
|
||||
void wait_vblank(DisplayState &display, KernelState &kernel, const ThreadStatePtr &wait_thread, int count, const bool is_cb) {
|
||||
void wait_vblank(DisplayState &display, KernelState &kernel, const ThreadStatePtr &wait_thread, const uint64_t target_vcount, const bool is_cb) {
|
||||
if (!wait_thread) {
|
||||
return;
|
||||
}
|
||||
@ -89,8 +94,11 @@ void wait_vblank(DisplayState &display, KernelState &kernel, const ThreadStatePt
|
||||
{
|
||||
const std::lock_guard<std::mutex> guard(display.mutex);
|
||||
|
||||
if (target_vcount <= display.vblank_count)
|
||||
return;
|
||||
|
||||
wait_thread->update_status(ThreadStatus::wait);
|
||||
display.vblank_wait_infos.push_back({ wait_thread, count, is_cb });
|
||||
display.vblank_wait_infos.push_back({ wait_thread, target_vcount, is_cb });
|
||||
}
|
||||
|
||||
wait_thread->status_cond.wait(thread_lock, [=]() { return wait_thread->status == ThreadStatus::run; });
|
||||
|
@ -774,7 +774,7 @@ ExitCode run_app(HostState &host, Ptr<const void> &entry_point) {
|
||||
return RunThreadFailed;
|
||||
}
|
||||
|
||||
start_sync_thread(host.display, host.kernel);
|
||||
start_sync_thread(host);
|
||||
|
||||
if (host.cfg.boot_apps_full_screen && !host.display.fullscreen.load())
|
||||
switch_full_screen(host);
|
||||
|
@ -29,22 +29,20 @@
|
||||
static int display_wait(HostState &host, SceUID thread_id, int vcount, const bool is_since_setbuf, const bool is_cb) {
|
||||
const auto &thread = host.kernel.get_thread(thread_id);
|
||||
|
||||
// this part should not need a mutex
|
||||
const uint64_t vblank_count = host.display.vblank_count.load();
|
||||
uint64_t target_vcount;
|
||||
if (is_since_setbuf) {
|
||||
vcount = host.display.last_setframe_vblank_count + vcount - vblank_count;
|
||||
target_vcount = host.display.last_setframe_vblank_count + vcount;
|
||||
} else {
|
||||
// the wait is considered starting from the last time the thread resumed
|
||||
// from a vblank wait (sceDisplayWait...) and not from the time this function was called
|
||||
// but we still need to wait at least for one vblank
|
||||
const uint64_t next_vsync = vblank_count + 1;
|
||||
const uint64_t next_vsync = host.display.vblank_count + 1;
|
||||
const uint64_t min_vsync = thread->last_vblank_waited + vcount;
|
||||
thread->last_vblank_waited = std::max(next_vsync, min_vsync);
|
||||
vcount = static_cast<int>(thread->last_vblank_waited - vblank_count);
|
||||
target_vcount = thread->last_vblank_waited;
|
||||
}
|
||||
|
||||
if (vcount > 0)
|
||||
wait_vblank(host.display, host.kernel, thread, vcount, is_cb);
|
||||
wait_vblank(host.display, host.kernel, thread, target_vcount, is_cb);
|
||||
|
||||
if (host.display.abort.load())
|
||||
return SCE_DISPLAY_ERROR_NO_PIXEL_DATA;
|
||||
|
@ -130,7 +130,7 @@ EXPORT(int, sceTouchPeek, SceUInt32 port, SceTouchData *pData, SceUInt32 nBufs)
|
||||
return RET_ERROR(SCE_TOUCH_ERROR_INVALID_ARG);
|
||||
}
|
||||
|
||||
return peek_touch(host, port, pData, nBufs);
|
||||
return touch_get(thread_id, host, port, pData, nBufs, true);
|
||||
}
|
||||
|
||||
EXPORT(int, sceTouchPeek2, SceUInt32 port, SceTouchData *pData, SceUInt32 nBufs) {
|
||||
@ -141,7 +141,7 @@ EXPORT(int, sceTouchPeek2, SceUInt32 port, SceTouchData *pData, SceUInt32 nBufs)
|
||||
return RET_ERROR(SCE_TOUCH_ERROR_INVALID_ARG);
|
||||
}
|
||||
|
||||
return peek_touch(host, port, pData, nBufs);
|
||||
return touch_get(thread_id, host, port, pData, nBufs, true);
|
||||
}
|
||||
|
||||
EXPORT(int, sceTouchPeekRegion) {
|
||||
@ -159,8 +159,7 @@ EXPORT(int, sceTouchRead, SceUInt32 port, SceTouchData *pData, SceUInt32 nBufs)
|
||||
if (pData == nullptr) {
|
||||
return RET_ERROR(SCE_TOUCH_ERROR_INVALID_ARG);
|
||||
}
|
||||
|
||||
return peek_touch(host, port, pData, nBufs);
|
||||
return touch_get(thread_id, host, port, pData, nBufs, false);
|
||||
}
|
||||
|
||||
EXPORT(int, sceTouchRead2, SceUInt32 port, SceTouchData *pData, SceUInt32 nBufs) {
|
||||
@ -171,7 +170,7 @@ EXPORT(int, sceTouchRead2, SceUInt32 port, SceTouchData *pData, SceUInt32 nBufs)
|
||||
return RET_ERROR(SCE_TOUCH_ERROR_INVALID_ARG);
|
||||
}
|
||||
|
||||
return peek_touch(host, port, pData, nBufs);
|
||||
return touch_get(thread_id, host, port, pData, nBufs, false);
|
||||
}
|
||||
|
||||
EXPORT(int, sceTouchReadRegion) {
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <host/state.h>
|
||||
#include <touch/touch.h>
|
||||
|
||||
void touch_vsync_update(const HostState &host);
|
||||
int handle_touch_event(SDL_TouchFingerEvent &finger);
|
||||
int toggle_touchscreen();
|
||||
int peek_touch(const HostState &host, const SceUInt32 &port, SceTouchData *pData, SceUInt32 count);
|
||||
int touch_get(const SceUID thread_id, HostState &host, const SceUInt32 &port, SceTouchData *pData, SceUInt32 count, bool is_peek);
|
||||
|
@ -15,6 +15,8 @@
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
#include <display/functions.h>
|
||||
#include <host/state.h>
|
||||
#include <touch/functions.h>
|
||||
#include <touch/touch.h>
|
||||
|
||||
@ -22,11 +24,19 @@
|
||||
|
||||
#include <cstring>
|
||||
|
||||
constexpr int MAX_TOUCH_BUFFER_SAVED = 64;
|
||||
|
||||
static SceTouchData touch_buffers[MAX_TOUCH_BUFFER_SAVED][2];
|
||||
int touch_buffer_idx = 0;
|
||||
|
||||
static uint64_t timestamp;
|
||||
static SDL_TouchFingerEvent finger_buffer[8];
|
||||
static uint8_t finger_count = 0;
|
||||
static long int finger_id_ref;
|
||||
static auto touchscreen_port = SCE_TOUCH_PORT_FRONT;
|
||||
static bool is_touched[2] = { false, false };
|
||||
static int curr_touch_id[2] = { 0, 0 };
|
||||
static uint64_t last_vcount[2] = { 0, 0 };
|
||||
|
||||
static SceTouchData recover_touch_events() {
|
||||
SceTouchData touch_data;
|
||||
@ -49,6 +59,71 @@ static SceTouchData recover_touch_events() {
|
||||
return touch_data;
|
||||
}
|
||||
|
||||
void touch_vsync_update(const HostState &host) {
|
||||
SceIVector2 touch_pos_window = { 0, 0 };
|
||||
const uint32_t buttons = SDL_GetMouseState(&touch_pos_window.x, &touch_pos_window.y);
|
||||
|
||||
std::chrono::time_point<std::chrono::steady_clock> ts = std::chrono::steady_clock::now();
|
||||
uint64_t timestamp = std::chrono::duration_cast<std::chrono::microseconds>(ts.time_since_epoch()).count();
|
||||
|
||||
for (int port = 0; port < 2; port++) {
|
||||
// do it for both the front and the back touchscreen
|
||||
SceTouchData *data = &touch_buffers[(touch_buffer_idx + 1) % MAX_TOUCH_BUFFER_SAVED][port];
|
||||
memset(data, 0, sizeof(SceTouchData));
|
||||
data->timeStamp = timestamp;
|
||||
|
||||
const uint32_t mask = (port == SCE_TOUCH_PORT_BACK) ? SDL_BUTTON_RMASK : SDL_BUTTON_LMASK;
|
||||
if ((buttons & mask) && host.renderer_focused) {
|
||||
if (!is_touched[port]) {
|
||||
curr_touch_id[port]++;
|
||||
// the touch id must be between 0 and 127
|
||||
curr_touch_id[port] %= 128;
|
||||
is_touched[port] = true;
|
||||
}
|
||||
|
||||
SceIVector2 SDL_window_size = { 0, 0 };
|
||||
SDL_Window *const window = SDL_GetMouseFocus();
|
||||
SDL_GetWindowSize(window, &SDL_window_size.x, &SDL_window_size.y);
|
||||
|
||||
SceFVector2 scale = { 1, 1 };
|
||||
if ((SDL_window_size.x > 0) && (SDL_window_size.y > 0)) {
|
||||
scale.x = static_cast<float>(host.drawable_size.x) / SDL_window_size.x;
|
||||
scale.y = static_cast<float>(host.drawable_size.y) / SDL_window_size.y;
|
||||
}
|
||||
|
||||
const SceFVector2 touch_pos_drawable = {
|
||||
touch_pos_window.x * scale.x,
|
||||
touch_pos_window.y * scale.y
|
||||
};
|
||||
|
||||
const SceFVector2 touch_pos_viewport = {
|
||||
(touch_pos_drawable.x - host.viewport_pos.x) / host.viewport_size.x,
|
||||
(touch_pos_drawable.y - host.viewport_pos.y) / host.viewport_size.y
|
||||
};
|
||||
|
||||
if ((touch_pos_viewport.x >= 0) && (touch_pos_viewport.y >= 0) && (touch_pos_viewport.x < 1) && (touch_pos_viewport.y < 1)) {
|
||||
data->report[data->reportNum].x = static_cast<uint16_t>(touch_pos_viewport.x * 1920);
|
||||
if (port == SCE_TOUCH_PORT_FRONT) {
|
||||
data->report[data->reportNum].y = static_cast<uint16_t>(touch_pos_viewport.y * 1088);
|
||||
} else {
|
||||
data->report[data->reportNum].y = static_cast<uint16_t>(108 + touch_pos_viewport.y * 781);
|
||||
}
|
||||
data->report[data->reportNum].id = curr_touch_id[port];
|
||||
++data->reportNum;
|
||||
}
|
||||
|
||||
if (!host.touch.touch_mode[port]) {
|
||||
data->reportNum = 0;
|
||||
}
|
||||
} else {
|
||||
is_touched[port] = false;
|
||||
}
|
||||
}
|
||||
|
||||
touch_buffer_idx++;
|
||||
touch_buffer_idx %= MAX_TOUCH_BUFFER_SAVED;
|
||||
}
|
||||
|
||||
int handle_touch_event(SDL_TouchFingerEvent &finger) {
|
||||
switch (finger.type) {
|
||||
case SDL_FINGERDOWN: {
|
||||
@ -105,63 +180,52 @@ int toggle_touchscreen() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int peek_touch(const HostState &host, const SceUInt32 &port, SceTouchData *pData, SceUInt32 count) {
|
||||
memset(pData, 0, sizeof(SceTouchData));
|
||||
int touch_get(const SceUID thread_id, HostState &host, const SceUInt32 &port, SceTouchData *pData, SceUInt32 count, bool is_peek) {
|
||||
memset(pData, 0, sizeof(SceTouchData) * count);
|
||||
const int port_idx = static_cast<int>(port);
|
||||
|
||||
std::chrono::time_point<std::chrono::steady_clock> ts = std::chrono::steady_clock::now();
|
||||
pData->timeStamp = std::chrono::duration_cast<std::chrono::microseconds>(ts.time_since_epoch()).count();
|
||||
int nb_returned_data = 1;
|
||||
if (is_peek) {
|
||||
if (host.touch.touch_mode[port])
|
||||
nb_returned_data = count;
|
||||
else
|
||||
nb_returned_data = 0;
|
||||
} else {
|
||||
uint64_t vblank_count = host.display.vblank_count;
|
||||
if (vblank_count <= last_vcount[port_idx]) {
|
||||
// sceTouchRead is blocking, wait for the next vsync for the buffer to be updated
|
||||
auto thread = host.kernel.get_thread(thread_id);
|
||||
|
||||
if (host.common_dialog.status == SCE_COMMON_DIALOG_STATUS_RUNNING) {
|
||||
// return one empty touch data
|
||||
return 1;
|
||||
wait_vblank(host.display, host.kernel, thread, last_vcount[port_idx] + 1, false);
|
||||
vblank_count = host.display.vblank_count;
|
||||
}
|
||||
nb_returned_data = std::min<int>(count, host.display.vblank_count - last_vcount[port_idx]);
|
||||
last_vcount[port_idx] = host.display.vblank_count;
|
||||
}
|
||||
|
||||
SceIVector2 touch_pos_window = { 0, 0 };
|
||||
const uint32_t buttons = SDL_GetMouseState(&touch_pos_window.x, &touch_pos_window.y);
|
||||
const uint32_t mask = (port == SCE_TOUCH_PORT_BACK) ? SDL_BUTTON_RMASK : SDL_BUTTON_LMASK;
|
||||
if ((buttons & mask) && host.renderer_focused) {
|
||||
SceIVector2 SDL_window_size = { 0, 0 };
|
||||
SDL_Window *const window = SDL_GetMouseFocus();
|
||||
SDL_GetWindowSize(window, &SDL_window_size.x, &SDL_window_size.y);
|
||||
|
||||
SceFVector2 scale = { 1, 1 };
|
||||
if ((SDL_window_size.x > 0) && (SDL_window_size.y > 0)) {
|
||||
scale.x = static_cast<float>(host.drawable_size.x) / SDL_window_size.x;
|
||||
scale.y = static_cast<float>(host.drawable_size.y) / SDL_window_size.y;
|
||||
}
|
||||
|
||||
const SceFVector2 touch_pos_drawable = {
|
||||
touch_pos_window.x * scale.x,
|
||||
touch_pos_window.y * scale.y
|
||||
};
|
||||
|
||||
const SceFVector2 touch_pos_viewport = {
|
||||
(touch_pos_drawable.x - host.viewport_pos.x) / host.viewport_size.x,
|
||||
(touch_pos_drawable.y - host.viewport_pos.y) / host.viewport_size.y
|
||||
};
|
||||
|
||||
if ((touch_pos_viewport.x >= 0) && (touch_pos_viewport.y >= 0) && (touch_pos_viewport.x < 1) && (touch_pos_viewport.y < 1)) {
|
||||
pData->report[pData->reportNum].x = static_cast<uint16_t>(touch_pos_viewport.x * 1920);
|
||||
if (port == SCE_TOUCH_PORT_FRONT) {
|
||||
pData->report[pData->reportNum].y = static_cast<uint16_t>(touch_pos_viewport.y * 1088);
|
||||
} else {
|
||||
pData->report[pData->reportNum].y = static_cast<uint16_t>(108 + touch_pos_viewport.y * 781);
|
||||
}
|
||||
pData->report[pData->reportNum].id = 1;
|
||||
++pData->reportNum;
|
||||
}
|
||||
|
||||
if (!host.touch.touch_mode[port]) {
|
||||
pData->reportNum = 0;
|
||||
}
|
||||
} else if (registered_touch() == true && port == touchscreen_port) {
|
||||
if (registered_touch() && port == touchscreen_port) {
|
||||
// less accurate implementation, but which is able to take a real touchscreen as the input
|
||||
pData[0] = recover_touch_events();
|
||||
for (uint32_t i = 0; i < nb_returned_data; i++) {
|
||||
memcpy(&pData[i], &pData[0], sizeof(SceTouchData));
|
||||
}
|
||||
} else {
|
||||
int corr_buffer_idx;
|
||||
if (is_peek) {
|
||||
corr_buffer_idx = touch_buffer_idx;
|
||||
} else {
|
||||
// give the oldest buffer first
|
||||
corr_buffer_idx = (touch_buffer_idx - nb_returned_data + 1 + MAX_TOUCH_BUFFER_SAVED) % MAX_TOUCH_BUFFER_SAVED;
|
||||
}
|
||||
for (uint32_t i = 0; i < nb_returned_data; i++) {
|
||||
memcpy(&pData[i], &touch_buffers[corr_buffer_idx][port_idx], sizeof(SceTouchData));
|
||||
// if peek, repeat the last buffer
|
||||
if (!is_peek) {
|
||||
corr_buffer_idx++;
|
||||
corr_buffer_idx %= MAX_TOUCH_BUFFER_SAVED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// the other buffers are used when a call to ReadTouch is late
|
||||
for (uint32_t i = 1; i < count; i++) {
|
||||
memset(&pData[i], 0, sizeof(SceTouchData));
|
||||
}
|
||||
|
||||
return 1;
|
||||
return nb_returned_data;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user