mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-09 08:41: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 */
|
/* final free */
|
||||||
void (*free)(void * udata);
|
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 */
|
* optional, if omitted assumes false */
|
||||||
bool (*needs_render)(void * udata, bool interactive);
|
bool (*needs_render)(void * udata, bool interactive);
|
||||||
|
|
||||||
|
|||||||
@@ -630,9 +630,16 @@ static int renderThread(void * unused)
|
|||||||
|
|
||||||
const uint64_t pending =
|
const uint64_t pending =
|
||||||
atomic_load_explicit(&g_state.pendingCount, memory_order_acquire);
|
atomic_load_explicit(&g_state.pendingCount, memory_order_acquire);
|
||||||
const bool overlayRender = app_overlayNeedsRender() &&
|
const bool overlayNeeded = app_overlayNeedsRender();
|
||||||
(!g_state.lastRenderTimeValid ||
|
uint64_t outputPeriod = 0;
|
||||||
nanotime() - g_state.lastRenderTime >= g_state.overlayFrameTime);
|
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)
|
if (!lgResetEvent(g_state.frameEvent)
|
||||||
&& !forceRender
|
&& !forceRender
|
||||||
@@ -690,10 +697,6 @@ static int renderThread(void * unused)
|
|||||||
atomic_compare_exchange_weak(&g_state.lgrResize, &resize, 0);
|
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 LG_RendererFrameToken frameTokenLimit = frameTimingQueuedToken();
|
||||||
const uint64_t prepareStart = nanotime();
|
const uint64_t prepareStart = nanotime();
|
||||||
|
|
||||||
@@ -701,6 +704,10 @@ static int renderThread(void * unused)
|
|||||||
|
|
||||||
renderQueue_process();
|
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;
|
const uint64_t prepareTime = nanotime() - prepareStart;
|
||||||
|
|
||||||
LG_RendererFrameTiming rendererTiming = {};
|
LG_RendererFrameTiming rendererTiming = {};
|
||||||
@@ -728,7 +735,7 @@ static int renderThread(void * unused)
|
|||||||
{
|
{
|
||||||
/* A frame-driven swap also satisfies the minimum render rate. */
|
/* A frame-driven swap also satisfies the minimum render rate. */
|
||||||
clock_gettime(CLOCK_MONOTONIC, &time);
|
clock_gettime(CLOCK_MONOTONIC, &time);
|
||||||
tsAdd(&time, app_isOverlayMode() ?
|
tsAdd(&time, app_overlayNeedsRender() ?
|
||||||
g_state.overlayFrameTime : g_state.frameTime);
|
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)
|
if (atomic_load_explicit(&g_state.pendingCount, memory_order_acquire) < 10)
|
||||||
atomic_fetch_add_explicit(&g_state.pendingCount, 1,
|
atomic_fetch_add_explicit(&g_state.pendingCount, 1,
|
||||||
memory_order_release);
|
memory_order_release);
|
||||||
|
overlaySplash_show(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
overlaySplash_show(false);
|
||||||
lgSignalEvent(g_state.frameEvent);
|
lgSignalEvent(g_state.frameEvent);
|
||||||
|
}
|
||||||
|
|
||||||
LG_TransportFrameTiming timing = {};
|
LG_TransportFrameTiming timing = {};
|
||||||
if (g_state.transportOps->getFrameTiming)
|
if (g_state.transportOps->getFrameTiming)
|
||||||
g_state.transportOps->getFrameTiming(
|
g_state.transportOps->getFrameTiming(
|
||||||
g_state.transport, &frame, &timing);
|
g_state.transport, &frame, &timing);
|
||||||
|
|
||||||
overlaySplash_show(false);
|
|
||||||
if ((frame.flags & LG_TRANSPORT_FRAME_REQUEST_ACTIVATION) &&
|
if ((frame.flags & LG_TRANSPORT_FRAME_REQUEST_ACTIVATION) &&
|
||||||
g_params.requestActivation)
|
g_params.requestActivation)
|
||||||
g_state.ds->requestActivation();
|
g_state.ds->requestActivation();
|
||||||
|
|||||||
@@ -30,21 +30,32 @@
|
|||||||
#include "common/appstrings.h"
|
#include "common/appstrings.h"
|
||||||
#include "common/stringlist.h"
|
#include "common/stringlist.h"
|
||||||
#include "common/stringutils.h"
|
#include "common/stringutils.h"
|
||||||
|
#include "common/time.h"
|
||||||
|
|
||||||
#include "resources/lg-logo.svg.h"
|
#include "resources/lg-logo.svg.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
#include <stdatomic.h>
|
||||||
|
|
||||||
#define SEGMENTS 12
|
#define SEGMENTS 12
|
||||||
|
#define FADE_TIME_NS 1000000000ULL
|
||||||
|
|
||||||
static bool l_show;
|
enum SplashState
|
||||||
static bool l_fadeDone;
|
{
|
||||||
static float l_alpha;
|
SPLASH_VISIBLE,
|
||||||
static OverlayImage l_logo;
|
SPLASH_FADING,
|
||||||
static float l_vectors[SEGMENTS][2];
|
SPLASH_HIDDEN,
|
||||||
static StringList l_tagline;
|
};
|
||||||
static StringList l_footline;
|
|
||||||
|
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)
|
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)
|
static bool splash_init(void ** udata, const void * params)
|
||||||
{
|
{
|
||||||
l_show = true;
|
atomic_store_explicit(&l_requestedShow, true, memory_order_relaxed);
|
||||||
l_fadeDone = false;
|
l_appliedShow = true;
|
||||||
l_alpha = 1.0f;
|
l_state = SPLASH_VISIBLE;
|
||||||
|
l_fadeStart = 0;
|
||||||
|
|
||||||
overlayLoadSVG(b_lg_logo_svg, b_lg_logo_svg_size, &l_logo, 200, 200);
|
overlayLoadSVG(b_lg_logo_svg, b_lg_logo_svg_size, &l_logo, 200, 200);
|
||||||
calcRadialVectors(l_vectors, ARRAY_LENGTH(l_vectors));
|
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,
|
static int splash_render(void * udata, bool interactive, struct Rect * windowRects,
|
||||||
int maxRects)
|
int maxRects)
|
||||||
{
|
{
|
||||||
if (!l_show && l_fadeDone)
|
float alpha = 1.0f;
|
||||||
|
if (l_state == SPLASH_HIDDEN)
|
||||||
return 0;
|
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();
|
ImVec2 * screen = overlayGetScreenSize();
|
||||||
ImDrawList * list = igGetBackgroundDrawList_Nil();
|
ImDrawList * list = igGetBackgroundDrawList_Nil();
|
||||||
|
|
||||||
@@ -226,30 +248,36 @@ static int splash_render(void * udata, bool interactive, struct Rect * windowRec
|
|||||||
return 1;
|
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)
|
return l_state == SPLASH_FADING ||
|
||||||
{
|
atomic_load_explicit(&l_requestedShow, memory_order_acquire) !=
|
||||||
if (g_params.quickSplash)
|
l_appliedShow;
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool splash_needsFullRender(void * udata)
|
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 =
|
struct LG_OverlayOps LGOverlaySplash =
|
||||||
@@ -257,17 +285,16 @@ struct LG_OverlayOps LGOverlaySplash =
|
|||||||
.name = "splash",
|
.name = "splash",
|
||||||
.init = splash_init,
|
.init = splash_init,
|
||||||
.free = splash_free,
|
.free = splash_free,
|
||||||
|
.needs_render = splash_needsRender,
|
||||||
.needs_full_render = splash_needsFullRender,
|
.needs_full_render = splash_needsFullRender,
|
||||||
.render = splash_render,
|
.render = splash_render,
|
||||||
.tick = splash_tick,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void overlaySplash_show(bool show)
|
void overlaySplash_show(bool show)
|
||||||
{
|
{
|
||||||
if (l_show == show)
|
if (atomic_exchange_explicit(
|
||||||
|
&l_requestedShow, show, memory_order_acq_rel) == show)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
l_show = show;
|
|
||||||
app_invalidateOverlay(true);
|
|
||||||
app_invalidateWindow(true);
|
app_invalidateWindow(true);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user