mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-09 00:31:31 +00:00
[client] overlay: render splash fade from monotonic time
Move splash transitions onto the render thread and derive opacity from elapsed monotonic time. Pace active animation at a display-compatible minimum of 60 Hz while allowing incoming frames to satisfy that demand. Reset the splash cleanly across reconnects and latch state changes with the full-frame restoration decision.
This commit is contained in:
@@ -41,7 +41,7 @@ struct LG_OverlayOps
|
||||
/* final free */
|
||||
void (*free)(void * udata);
|
||||
|
||||
/* return true if realtime rendering is required when in jitRender mode
|
||||
/* return true if realtime rendering is required
|
||||
* optional, if omitted assumes false */
|
||||
bool (*needs_render)(void * udata, bool interactive);
|
||||
|
||||
|
||||
@@ -630,9 +630,16 @@ static int renderThread(void * unused)
|
||||
|
||||
const uint64_t pending =
|
||||
atomic_load_explicit(&g_state.pendingCount, memory_order_acquire);
|
||||
const bool overlayRender = app_overlayNeedsRender() &&
|
||||
(!g_state.lastRenderTimeValid ||
|
||||
nanotime() - g_state.lastRenderTime >= g_state.overlayFrameTime);
|
||||
const bool overlayNeeded = app_overlayNeedsRender();
|
||||
uint64_t outputPeriod = 0;
|
||||
const bool periodKnown = overlayNeeded &&
|
||||
g_state.ds->getFramePeriod &&
|
||||
g_state.ds->getFramePeriod(&outputPeriod);
|
||||
const uint64_t elapsed = g_state.lastRenderTimeValid ?
|
||||
nanotime() - g_state.lastRenderTime : 0;
|
||||
const bool overlayRender = overlayNeeded &&
|
||||
(!g_state.lastRenderTimeValid || !periodKnown ||
|
||||
elapsed + outputPeriod >= g_state.overlayFrameTime);
|
||||
|
||||
if (!lgResetEvent(g_state.frameEvent)
|
||||
&& !forceRender
|
||||
@@ -690,10 +697,6 @@ static int renderThread(void * unused)
|
||||
atomic_compare_exchange_weak(&g_state.lgrResize, &resize, 0);
|
||||
}
|
||||
|
||||
const bool invalidate =
|
||||
atomic_exchange(&g_state.invalidateWindow, false) ||
|
||||
app_overlayNeedsFullRender();
|
||||
|
||||
const LG_RendererFrameToken frameTokenLimit = frameTimingQueuedToken();
|
||||
const uint64_t prepareStart = nanotime();
|
||||
|
||||
@@ -701,6 +704,10 @@ static int renderThread(void * unused)
|
||||
|
||||
renderQueue_process();
|
||||
|
||||
const bool windowInvalid =
|
||||
atomic_exchange(&g_state.invalidateWindow, false);
|
||||
const bool overlayFull = app_overlayNeedsFullRender();
|
||||
const bool invalidate = windowInvalid || overlayFull;
|
||||
const uint64_t prepareTime = nanotime() - prepareStart;
|
||||
|
||||
LG_RendererFrameTiming rendererTiming = {};
|
||||
@@ -728,7 +735,7 @@ static int renderThread(void * unused)
|
||||
{
|
||||
/* A frame-driven swap also satisfies the minimum render rate. */
|
||||
clock_gettime(CLOCK_MONOTONIC, &time);
|
||||
tsAdd(&time, app_isOverlayMode() ?
|
||||
tsAdd(&time, app_overlayNeedsRender() ?
|
||||
g_state.overlayFrameTime : g_state.frameTime);
|
||||
}
|
||||
|
||||
@@ -1114,16 +1121,19 @@ int main_frameThread(void * unused)
|
||||
if (atomic_load_explicit(&g_state.pendingCount, memory_order_acquire) < 10)
|
||||
atomic_fetch_add_explicit(&g_state.pendingCount, 1,
|
||||
memory_order_release);
|
||||
overlaySplash_show(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
overlaySplash_show(false);
|
||||
lgSignalEvent(g_state.frameEvent);
|
||||
}
|
||||
|
||||
LG_TransportFrameTiming timing = {};
|
||||
if (g_state.transportOps->getFrameTiming)
|
||||
g_state.transportOps->getFrameTiming(
|
||||
g_state.transport, &frame, &timing);
|
||||
|
||||
overlaySplash_show(false);
|
||||
if ((frame.flags & LG_TRANSPORT_FRAME_REQUEST_ACTIVATION) &&
|
||||
g_params.requestActivation)
|
||||
g_state.ds->requestActivation();
|
||||
|
||||
@@ -30,21 +30,32 @@
|
||||
#include "common/appstrings.h"
|
||||
#include "common/stringlist.h"
|
||||
#include "common/stringutils.h"
|
||||
#include "common/time.h"
|
||||
|
||||
#include "resources/lg-logo.svg.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <stdatomic.h>
|
||||
|
||||
#define SEGMENTS 12
|
||||
#define SEGMENTS 12
|
||||
#define FADE_TIME_NS 1000000000ULL
|
||||
|
||||
static bool l_show;
|
||||
static bool l_fadeDone;
|
||||
static float l_alpha;
|
||||
static OverlayImage l_logo;
|
||||
static float l_vectors[SEGMENTS][2];
|
||||
static StringList l_tagline;
|
||||
static StringList l_footline;
|
||||
enum SplashState
|
||||
{
|
||||
SPLASH_VISIBLE,
|
||||
SPLASH_FADING,
|
||||
SPLASH_HIDDEN,
|
||||
};
|
||||
|
||||
static _Atomic(bool) l_requestedShow;
|
||||
static bool l_appliedShow;
|
||||
static enum SplashState l_state;
|
||||
static uint64_t l_fadeStart;
|
||||
static OverlayImage l_logo;
|
||||
static float l_vectors[SEGMENTS][2];
|
||||
static StringList l_tagline;
|
||||
static StringList l_footline;
|
||||
|
||||
static void calcRadialVectors(float vectors[][2], int segments)
|
||||
{
|
||||
@@ -87,9 +98,10 @@ static void drawRadialGradient(ImDrawList * list, int x, int y, int w, int h,
|
||||
|
||||
static bool splash_init(void ** udata, const void * params)
|
||||
{
|
||||
l_show = true;
|
||||
l_fadeDone = false;
|
||||
l_alpha = 1.0f;
|
||||
atomic_store_explicit(&l_requestedShow, true, memory_order_relaxed);
|
||||
l_appliedShow = true;
|
||||
l_state = SPLASH_VISIBLE;
|
||||
l_fadeStart = 0;
|
||||
|
||||
overlayLoadSVG(b_lg_logo_svg, b_lg_logo_svg_size, &l_logo, 200, 200);
|
||||
calcRadialVectors(l_vectors, ARRAY_LENGTH(l_vectors));
|
||||
@@ -155,10 +167,20 @@ static void renderText(ImDrawList * list, int x, int y, ImU32 color,
|
||||
static int splash_render(void * udata, bool interactive, struct Rect * windowRects,
|
||||
int maxRects)
|
||||
{
|
||||
if (!l_show && l_fadeDone)
|
||||
float alpha = 1.0f;
|
||||
if (l_state == SPLASH_HIDDEN)
|
||||
return 0;
|
||||
if (l_state == SPLASH_FADING)
|
||||
{
|
||||
const uint64_t elapsed = nanotime() - l_fadeStart;
|
||||
if (elapsed >= FADE_TIME_NS)
|
||||
{
|
||||
l_state = SPLASH_HIDDEN;
|
||||
return 0;
|
||||
}
|
||||
alpha -= (float)elapsed / (float)FADE_TIME_NS;
|
||||
}
|
||||
|
||||
const float alpha = l_fadeDone ? 1.0f : l_alpha;
|
||||
ImVec2 * screen = overlayGetScreenSize();
|
||||
ImDrawList * list = igGetBackgroundDrawList_Nil();
|
||||
|
||||
@@ -226,30 +248,36 @@ static int splash_render(void * udata, bool interactive, struct Rect * windowRec
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool splash_tick(void * udata, unsigned long long tickCount)
|
||||
static bool splash_needsRender(void * udata, bool interactive)
|
||||
{
|
||||
if (!l_show && !l_fadeDone)
|
||||
{
|
||||
if (g_params.quickSplash)
|
||||
{
|
||||
l_fadeDone = true;
|
||||
app_invalidateWindow(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
l_alpha -= 1.0f / TICK_RATE;
|
||||
if (l_alpha <= 0.0f)
|
||||
l_fadeDone = true;
|
||||
app_invalidateWindow(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
return l_state == SPLASH_FADING ||
|
||||
atomic_load_explicit(&l_requestedShow, memory_order_acquire) !=
|
||||
l_appliedShow;
|
||||
}
|
||||
|
||||
static bool splash_needsFullRender(void * udata)
|
||||
{
|
||||
return !l_show && !l_fadeDone;
|
||||
const bool show =
|
||||
atomic_load_explicit(&l_requestedShow, memory_order_acquire);
|
||||
const bool changed = show != l_appliedShow;
|
||||
if (changed)
|
||||
{
|
||||
l_appliedShow = show;
|
||||
if (show)
|
||||
{
|
||||
l_state = SPLASH_VISIBLE;
|
||||
l_fadeStart = 0;
|
||||
}
|
||||
else if (g_params.quickSplash)
|
||||
l_state = SPLASH_HIDDEN;
|
||||
else
|
||||
{
|
||||
l_state = SPLASH_FADING;
|
||||
l_fadeStart = nanotime();
|
||||
}
|
||||
}
|
||||
|
||||
return changed || l_state == SPLASH_FADING;
|
||||
}
|
||||
|
||||
struct LG_OverlayOps LGOverlaySplash =
|
||||
@@ -257,17 +285,16 @@ struct LG_OverlayOps LGOverlaySplash =
|
||||
.name = "splash",
|
||||
.init = splash_init,
|
||||
.free = splash_free,
|
||||
.needs_render = splash_needsRender,
|
||||
.needs_full_render = splash_needsFullRender,
|
||||
.render = splash_render,
|
||||
.tick = splash_tick,
|
||||
};
|
||||
|
||||
void overlaySplash_show(bool show)
|
||||
{
|
||||
if (l_show == show)
|
||||
if (atomic_exchange_explicit(
|
||||
&l_requestedShow, show, memory_order_acq_rel) == show)
|
||||
return;
|
||||
|
||||
l_show = show;
|
||||
app_invalidateOverlay(true);
|
||||
app_invalidateWindow(true);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user