From e0fcbf0910b52a6e66eb733a2850ec58a53cf0e3 Mon Sep 17 00:00:00 2001 From: Andrew Opalach Date: Mon, 23 Jun 2025 17:49:11 -0400 Subject: Improve FFmpeg hwaccel fallback, VR emulation - Optimize sink seeking with single video frame. - Small cleanups. Signed-off-by: Andrew Opalach --- src/screen/screen.c | 148 +++++++++++++++++++++++++++++++--------------------- src/screen/screen.h | 15 ++++-- src/screen/view.c | 12 +++++ src/screen/view.h | 2 + 4 files changed, 115 insertions(+), 62 deletions(-) (limited to 'src/screen') diff --git a/src/screen/screen.c b/src/screen/screen.c index 09fbe64..965088f 100644 --- a/src/screen/screen.c +++ b/src/screen/screen.c @@ -6,8 +6,8 @@ #include "view.h" #include "screen.h" -#define SCREEN_MOD1(scr) ((scr)->flags & CAMU_SCREEN_MOD_CONTROL) -#define SCREEN_MOD2(scr) ((scr)->flags & CAMU_SCREEN_MOD_SHIFT) +#define SCREEN_MOD1(scr) ((scr)->window->held_modifiers & (STELA_MOD_LCONTROL | STELA_MOD_RCONTROL)) +#define SCREEN_MOD2(scr) ((scr)->window->held_modifiers & (STELA_MOD_LSHIFT | STELA_MOD_RSHIFT)) #define SCREEN_IS_DRAGGING(scr) ((scr)->flags & CAMU_SCREEN_DRAGGING) #define SCREEN_IS_ZOOMING(scr) ((scr)->flags & CAMU_SCREEN_ZOOMING) #define SCREEN_ZOOM_MODE(scr, mode) ((scr)->flags & mode) @@ -18,6 +18,7 @@ static void should_close_callback(void *userdata) { struct camu_screen *scr = (struct camu_screen *)userdata; + scr->window->close(scr->window); scr->callback(scr->userdata, CAMU_SCREEN_CLOSE, NULL); } @@ -38,6 +39,8 @@ static void do_resize(struct camu_screen *scr, u32 width, u32 height) al_assert(width > 0 && height > 0); scr->width = width; scr->height = height; + scr->calibrate_x = scr->width / 2.0; + scr->calibrate_y = scr->height / 2.0; struct camu_screen_video *video; al_array_foreach_ptr(scr->videos, i, video) { camu_view_calculate(&video->view, scr->width, scr->height); @@ -68,23 +71,40 @@ static struct camu_view *get_view_from_mouse_pos(struct camu_screen *scr) return view; } +static void seek_to_percent_at_pointer(struct camu_screen *scr, f64 x) +{ + f64 percent = x / scr->width; + percent = CLAMP(percent, 0.0, 100.0); + scr->callback(scr->userdata, CAMU_SCREEN_PERCENT_SEEK, &percent); +} + static bool pointer_pos_callback(void *userdata, f64 x, f64 y) { struct camu_screen *scr = (struct camu_screen *)userdata; struct camu_view *view = get_view_from_mouse_pos(scr); bool queue_refresh = false; if (SCREEN_IS_DRAGGING(scr)) { - if (view && SCREEN_ZOOM_MODE(scr, CAMU_SCREEN_ZOOM_PAN_SIMPLE)) { - f64 dx = x - scr->last_pointer_x; - f64 dy = y - scr->last_pointer_y; - if (camu_view_pan_simple(view, scr->width, scr->height, dx, dy)) { - view->mode = CAMU_VIEW_DETACHED; - queue_refresh = true; - } - if (fabs(dx) + fabs(dy) > 3.0) { - scr->last_click_ts = SCREEN_INVALID_TS; +#ifdef CAMU_SCREEN_DRAG_SEEK + u64 now = nn_get_timestamp(); + if (view && now - scr->last_seek_ts > 110000) { + seek_to_percent_at_pointer(scr, x); + scr->last_seek_ts = now; + } +#else + if (view) { + if (SCREEN_ZOOM_MODE(scr, CAMU_SCREEN_ZOOM_PAN_SIMPLE)) { + f64 dx = x - scr->last_pointer_x; + f64 dy = y - scr->last_pointer_y; + if (camu_view_pan_simple(view, scr->width, scr->height, dx, dy)) { + view->mode = CAMU_VIEW_DETACHED; + queue_refresh = true; + } + if (fabs(dx) + fabs(dy) > 3.0) { + scr->last_click_ts = SCREEN_INVALID_TS; + } } } +#endif } scr->last_pointer_x = x; scr->last_pointer_y = y; @@ -144,13 +164,6 @@ static bool touch_callback(void *userdata, s32 index, u8 phase, f64 x, f64 y) return false; } -static void seek_to_percent_at_pointer(struct camu_screen *scr) -{ - f64 percent = scr->last_pointer_x / scr->width; - percent = CLAMP(percent, 0.0, 100.0); - scr->callback(scr->userdata, CAMU_SCREEN_PERCENT_SEEK, &percent); -} - static bool mouse_button_callback(void *userdata, u8 state, u8 button) { struct camu_screen *scr = (struct camu_screen *)userdata; @@ -159,13 +172,22 @@ static bool mouse_button_callback(void *userdata, u8 state, u8 button) switch (state) { case STELA_BUTTON_PRESSED: scr->flags |= CAMU_SCREEN_DRAGGING; + scr->window->set_cursor(scr->window, STELA_CURSOR_CROSSHAIR); +#ifdef CAMU_SCREEN_DRAG_SEEK + //scr->callback(scr->userdata, CAMU_SCREEN_TOGGLE_PAUSE, NULL); +#endif scr->last_click_ts = nn_get_timestamp(); break; case STELA_BUTTON_RELEASED: scr->flags &= ~CAMU_SCREEN_DRAGGING; + scr->window->set_cursor(scr->window, (scr->fullscreen || scr->vr_emulation) ? + STELA_CURSOR_HIDDEN : STELA_CURSOR_NORMAL); +#ifdef CAMU_SCREEN_DRAG_SEEK + //scr->callback(scr->userdata, CAMU_SCREEN_TOGGLE_PAUSE, NULL); +#endif if (SCREEN_LAST_CLICK_WITHIN(200000)) { if (SCREEN_MOD1(scr)) { - seek_to_percent_at_pointer(scr); + seek_to_percent_at_pointer(scr, scr->last_pointer_x); } else { s32 n = (scr->last_pointer_x >= scr->width / 2.0) ? 1 : -1; scr->callback(scr->userdata, CAMU_SCREEN_SKIP, &n); @@ -177,7 +199,7 @@ static bool mouse_button_callback(void *userdata, u8 state, u8 button) case STELA_MOUSE3: { switch (state) { case STELA_BUTTON_RELEASED: { - seek_to_percent_at_pointer(scr); + seek_to_percent_at_pointer(scr, scr->last_pointer_x); break; } } @@ -205,10 +227,14 @@ bool scroll_callback(void *userdata, f64 y) y = -y / 20.0; #endif struct camu_view *view = get_view_from_mouse_pos(scr); - if (view && SCREEN_ZOOM_MODE(scr, CAMU_SCREEN_ZOOM_PAN_SIMPLE)) { - if (camu_view_zoom_simple(view, scr->width, scr->height, scr->last_pointer_x, scr->last_pointer_y, y)) { - view->mode = CAMU_VIEW_DETACHED; - return true; + if (view) { + if (SCREEN_ZOOM_MODE(scr, CAMU_SCREEN_ZOOM_PAN_SIMPLE)) { + if (camu_view_zoom_simple(view, scr->width, scr->height, scr->last_pointer_x, scr->last_pointer_y, y)) { + view->mode = CAMU_VIEW_DETACHED; + return true; + } + } else if (SCREEN_ZOOM_MODE(scr, CAMU_SCREEN_ZOOM_FOV)) { + view->fov -= y; } } } @@ -221,15 +247,8 @@ static bool key_callback(void *userdata, u8 state, u16 button) switch (state) { case STELA_BUTTON_PRESSED: switch (button) { - case STELA_KEY_LEFT_SHIFT: - case STELA_KEY_RIGHT_SHIFT: - scr->flags |= CAMU_SCREEN_MOD_SHIFT; - break; - case STELA_KEY_LEFT_CONTROL: - case STELA_KEY_RIGHT_CONTROL: - scr->flags |= CAMU_SCREEN_MOD_CONTROL; - break; case STELA_KEY_Q: + scr->window->close(scr->window); scr->callback(scr->userdata, CAMU_SCREEN_CLOSE, NULL); break; case STELA_KEY_RIGHT: @@ -285,6 +304,8 @@ static bool key_callback(void *userdata, u8 state, u16 button) break; #endif case STELA_KEY_R: { + scr->calibrate_x = scr->last_pointer_x; + scr->calibrate_y = scr->last_pointer_y; struct camu_view *view = get_view_from_mouse_pos(scr); if (view) { if (SCREEN_MOD2(scr)) { @@ -367,21 +388,29 @@ static bool key_callback(void *userdata, u8 state, u16 button) // Force refresh for if there's no video buffer. al_atomic_add(u32)(&scr->force_refresh, 1, AL_ATOMIC_RELAXED); return true; + case STELA_KEY_V: { + struct camu_view *view = get_view_from_mouse_pos(scr); + if (view) { + view->mode = CAMU_DEFAULT_VIEW; + camu_view_calculate(view, scr->width, scr->height); + } + if (!scr->vr_emulation) { + scr->flags = (scr->flags & ~CAMU_SCREEN_ZOOM_PAN_SIMPLE) | CAMU_SCREEN_ZOOM_FOV; + scr->window->set_cursor(scr->window, STELA_CURSOR_HIDDEN); + scr->vr_emulation = true; + } else { + scr->flags = (scr->flags & ~CAMU_SCREEN_ZOOM_FOV) | CAMU_SCREEN_ZOOM_PAN_SIMPLE; + scr->window->set_cursor(scr->window, STELA_CURSOR_NORMAL); + scr->vr_emulation = false; + } + break; + } default: break; } break; case STELA_BUTTON_RELEASED: - switch (button) { - case STELA_KEY_LEFT_SHIFT: - case STELA_KEY_RIGHT_SHIFT: - scr->flags &= ~CAMU_SCREEN_MOD_SHIFT; - break; - case STELA_KEY_LEFT_CONTROL: - case STELA_KEY_RIGHT_CONTROL: - scr->flags &= ~CAMU_SCREEN_MOD_CONTROL; - break; - } + break; default: break; } @@ -396,6 +425,7 @@ static void key_immediate_callback(void *userdata, u8 state, u16 button) switch (button) { case STELA_KEY_F: scr->fullscreen = !scr->fullscreen; + scr->window->set_cursor(scr->window, scr->fullscreen ? STELA_CURSOR_HIDDEN : STELA_CURSOR_NORMAL); #ifdef STELA_API_DX11 scr->renderer->set(scr->renderer, CAMU_RENDERER_FULLSCREEN, scr->fullscreen); #else @@ -429,6 +459,10 @@ bool camu_screen_init(struct camu_screen *scr) scr->last_click_ts = SCREEN_INVALID_TS; scr->last_pointer_x = 0.0; scr->last_pointer_y = 0.0; +#ifdef CAMU_SCREEN_DRAG_SEEK + scr->last_seek_ts = 0; +#endif + scr->vr_emulation = false; al_array_init(scr->videos); #ifdef CAMU_SCREEN_THREADED al_array_init(scr->add_queue); @@ -444,7 +478,9 @@ static nn_thread_result NNWT_THREADCALL event_thread(void *userdata) { struct camu_screen *scr = (struct camu_screen *)userdata; nn_thread_set_name("window_events"); - while (scr->window->poll(scr->window, true) && al_atomic_load(s32)(&scr->state, AL_ATOMIC_RELAXED) != CAMU_SCREEN_STOPPED) { + // If we decide to close within a poll, we must not expect any + // window events after that point. + while (scr->window->poll(scr->window, true)) { scr->window->process_events(scr->window); } return 0; @@ -459,6 +495,8 @@ bool camu_screen_create_window(struct camu_screen *scr, const char *name) } scr->width = scr->window->width; scr->height = scr->window->height; + scr->calibrate_x = scr->width / 2.0; + scr->calibrate_y = scr->height / 2.0; scr->fullscreen = false; scr->scaling_disabled = false; scr->transparent_background = false; @@ -510,12 +548,7 @@ static void add_buffer_internal(struct camu_screen *scr, struct camu_video_buffe video.view.height = fmt->height; #endif } else { - video.view.mode = CAMU_DEFAULT_VIEW; - video.view.rotation = CAMU_VIEW_ROTATION_0; - video.view.width = fmt->width; - video.view.height = fmt->height; - video.view.zindex = 0; - video.view.stretch = 1.0; + camu_view_init(&video.view, fmt->width, fmt->height); } camu_view_calculate(&video.view, scr->width, scr->height); #ifdef CAMU_SCREEN_THREADED @@ -644,11 +677,9 @@ void camu_screen_clear(struct camu_screen *scr) void camu_screen_set_state(struct camu_screen *scr, s32 state) { // @TODO: What to do if a buffer is queued for removal and we pause here. - if (al_atomic_load(s32)(&scr->state, AL_ATOMIC_ACQUIRE) != CAMU_SCREEN_STOPPED) { - al_atomic_store(s32)(&scr->state, state, AL_ATOMIC_RELEASE); - if (state == CAMU_SCREEN_PAUSED) { - al_atomic_add(u32)(&scr->force_refresh, 1, AL_ATOMIC_RELAXED); - } + al_atomic_store(s32)(&scr->state, state, AL_ATOMIC_RELEASE); + if (state == CAMU_SCREEN_PAUSED) { + al_atomic_add(u32)(&scr->force_refresh, 1, AL_ATOMIC_RELAXED); } } @@ -668,8 +699,9 @@ bool camu_screen_poll(struct camu_screen *scr, bool block) } stl_window_read_events(scr->window); #else - scr->window->poll(scr->window, block); - scr->window->process_events(scr->window); + if (scr->window->poll(scr->window, block)) { + scr->window->process_events(scr->window); + } #endif #ifdef STELA_PAUSE return scr->window->should_refresh(scr->window); @@ -681,9 +713,8 @@ bool camu_screen_poll(struct camu_screen *scr, bool block) bool camu_screen_tick(struct camu_screen *scr, bool *force) { s32 state = al_atomic_load(s32)(&scr->state, AL_ATOMIC_RELAXED); - al_assert(state != CAMU_SCREEN_STOPPED); u32 force_refresh = al_atomic_load(u32)(&scr->force_refresh, AL_ATOMIC_ACQUIRE); - bool paused = state == CAMU_SCREEN_PAUSED && force_refresh == 0; + bool paused = !scr->vr_emulation && state == CAMU_SCREEN_PAUSED && force_refresh == 0; bool do_render = camu_screen_poll(scr, paused) || !paused; // Should be AL_ATOMIC_ACQ_REL. force_refresh = al_atomic_load(u32)(&scr->force_refresh, AL_ATOMIC_ACQUIRE); @@ -712,7 +743,6 @@ void camu_screen_close(struct camu_screen *scr) nn_mutex_destroy(&scr->mutex); #endif al_array_free(scr->videos); - al_atomic_store(s32)(&scr->state, CAMU_SCREEN_STOPPED, AL_ATOMIC_RELAXED); #ifdef STELA_EVENT_BUFFER nn_thread_join(&scr->thread); #endif diff --git a/src/screen/screen.h b/src/screen/screen.h index dacf756..4c6e205 100644 --- a/src/screen/screen.h +++ b/src/screen/screen.h @@ -14,6 +14,9 @@ //#define CAMU_SCREEN_DEBUG_KEY +// Interpret click and drag as seeking. This is mainly a seek performance test. +//#define CAMU_SCREEN_DRAG_SEEK + #define CAMU_SCREEN_WIDTH 700 #define CAMU_SCREEN_HEIGHT 700 @@ -22,13 +25,13 @@ enum { CAMU_SCREEN_MOD_CONTROL = 1 << 1, CAMU_SCREEN_DRAGGING = 1 << 2, CAMU_SCREEN_ZOOMING = 1 << 3, - CAMU_SCREEN_ZOOM_PAN_SIMPLE = 1 << 4 + CAMU_SCREEN_ZOOM_PAN_SIMPLE = 1 << 4, + CAMU_SCREEN_ZOOM_FOV = 1 << 5 }; enum { CAMU_SCREEN_PAUSED = 0, - CAMU_SCREEN_PLAYING, - CAMU_SCREEN_STOPPED + CAMU_SCREEN_PLAYING }; enum { @@ -69,6 +72,12 @@ struct camu_screen { u64 last_click_ts; f64 last_pointer_y; f64 last_pointer_x; +#ifdef CAMU_SCREEN_DRAG_SEEK + u64 last_seek_ts; +#endif + f64 calibrate_x; + f64 calibrate_y; + bool vr_emulation; array(struct camu_screen_video) videos; #ifdef CAMU_SCREEN_THREADED array(struct camu_video_buffer *) add_queue; diff --git a/src/screen/view.c b/src/screen/view.c index b5fade2..1676084 100644 --- a/src/screen/view.c +++ b/src/screen/view.c @@ -1,4 +1,5 @@ #include +#include #include "view.h" @@ -15,6 +16,17 @@ #define VIEW_ON_ITS_SIDE(view) \ (view->rotation % CAMU_VIEW_ROTATION_180 == CAMU_VIEW_ROTATION_90) +void camu_view_init(struct camu_view *view, u32 width, u32 height) +{ + view->mode = CAMU_DEFAULT_VIEW; + view->rotation = CAMU_VIEW_ROTATION_0; + view->width = width; + view->height = height; + view->zindex = 0; + view->stretch = 1.0; + view->fov = M_PI / 2.25; // 80 degrees +} + void camu_view_calculate(struct camu_view *view, u32 window_width, u32 window_height) { u32 width, height; diff --git a/src/screen/view.h b/src/screen/view.h index 30b6f64..b5f948e 100644 --- a/src/screen/view.h +++ b/src/screen/view.h @@ -29,8 +29,10 @@ struct camu_view { f64 x_offset; f64 y_offset; f64 stretch; + f64 fov; }; +void camu_view_init(struct camu_view *view, u32 width, u32 height); void camu_view_calculate(struct camu_view *view, u32 window_width, u32 window_height); bool camu_view_pan_simple(struct camu_view *view, u32 window_width, u32 window_height, f64 dx, f64 dy); bool camu_view_zoom_simple(struct camu_view *view, u32 window_width, u32 window_height, f64 x, f64 y, f64 v); -- cgit v1.2.3-101-g0448