mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-22 07:01:30 +00:00
[client] video: switch transport sources
Track primary and fallback video with source-specific generations, and publish a source only after its first frame or software surface is ready. Route SPICE surfaces and cursors through the generic render queue. Keep the current image across handoffs, and switch input only after the new source has been presented. Move fallback activation to its worker so display callbacks cannot block or deadlock the render path.
This commit is contained in:
220
client/src/app.c
220
client/src/app.c
@@ -306,6 +306,7 @@ void app_handleEnterEvent(bool entered)
|
||||
if (!g_params.alwaysShowCursor)
|
||||
g_cursor.draw = false;
|
||||
g_cursor.redraw = true;
|
||||
app_invalidateWindow(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1139,81 +1140,166 @@ void app_stopVideo(bool stop)
|
||||
|
||||
if (stop)
|
||||
{
|
||||
core_stopCursorThread();
|
||||
core_stopFrameThread();
|
||||
if (g_state.videoOps->type == LG_VIDEO_TYPE_FRAME)
|
||||
{
|
||||
core_stopCursorThread();
|
||||
core_stopFrameThread();
|
||||
}
|
||||
else
|
||||
g_state.videoOps->swSurface->setActive(
|
||||
g_state.transport.handle, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
core_startCursorThread();
|
||||
core_startFrameThread();
|
||||
if (g_state.videoOps->type == LG_VIDEO_TYPE_FRAME)
|
||||
{
|
||||
core_startCursorThread();
|
||||
core_startFrameThread();
|
||||
}
|
||||
else
|
||||
{
|
||||
g_state.videoOps->swSurface->setActive(
|
||||
g_state.transport.handle, true);
|
||||
app_useVideoSource(LG_VIDEO_SOURCE_PRIMARY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool app_useSpiceDisplay(bool enable)
|
||||
static RenderQueueSource renderSource(LG_VideoSource source)
|
||||
{
|
||||
if (!g_params.useSpice || !g_state.fallback)
|
||||
return false;
|
||||
|
||||
atomic_store_explicit(&g_state.fallbackDisplayRequested, enable,
|
||||
memory_order_release);
|
||||
|
||||
// if the fallback is not yet ready, retain the requested state
|
||||
if (!lgTransportFallback_ready(g_state.fallback))
|
||||
return false;
|
||||
|
||||
bool active = atomic_load_explicit(&g_state.fallbackDisplayActive,
|
||||
memory_order_acquire);
|
||||
if (active == enable)
|
||||
return active;
|
||||
|
||||
// do not allow stopping of the host app if not connected
|
||||
if (!enable && !atomic_load_explicit(
|
||||
&g_state.lgHostConnected, memory_order_acquire))
|
||||
switch(source)
|
||||
{
|
||||
atomic_store_explicit(&g_state.fallbackDisplayRequested, active,
|
||||
memory_order_release);
|
||||
return false;
|
||||
case LG_VIDEO_SOURCE_NONE:
|
||||
return RENDER_QUEUE_SOURCE_NONE;
|
||||
|
||||
case LG_VIDEO_SOURCE_PRIMARY:
|
||||
return RENDER_QUEUE_SOURCE_PRIMARY;
|
||||
|
||||
case LG_VIDEO_SOURCE_FALLBACK:
|
||||
return RENDER_QUEUE_SOURCE_FALLBACK;
|
||||
|
||||
case LG_VIDEO_SOURCE_COUNT:
|
||||
break;
|
||||
}
|
||||
|
||||
bool expected = false;
|
||||
if (!atomic_compare_exchange_strong_explicit(
|
||||
&g_state.fallbackDisplayTransition, &expected, true,
|
||||
memory_order_acquire, memory_order_relaxed))
|
||||
return atomic_load_explicit(&g_state.fallbackDisplayActive,
|
||||
memory_order_acquire);
|
||||
|
||||
active = atomic_load_explicit(&g_state.fallbackDisplayActive,
|
||||
memory_order_relaxed);
|
||||
if (active == enable)
|
||||
goto done;
|
||||
|
||||
if (!lgTransportFallback_setVideoActive(g_state.fallback, enable))
|
||||
goto fail;
|
||||
|
||||
renderQueue_swSurfaceShow(enable);
|
||||
|
||||
active = enable;
|
||||
atomic_store_explicit(&g_state.fallbackDisplayActive, active,
|
||||
memory_order_release);
|
||||
lgInput_useTransport(!active);
|
||||
overlayStatus_set(LG_USER_STATUS_SPICE, enable);
|
||||
|
||||
done:
|
||||
atomic_store_explicit(&g_state.fallbackDisplayTransition, false,
|
||||
memory_order_release);
|
||||
|
||||
enable = atomic_load_explicit(&g_state.fallbackDisplayRequested,
|
||||
memory_order_acquire);
|
||||
if (enable != active)
|
||||
return app_useSpiceDisplay(enable);
|
||||
|
||||
return active;
|
||||
|
||||
fail:
|
||||
DEBUG_ERROR("Failed to %s the SPICE display",
|
||||
enable ? "enable" : "disable");
|
||||
lgTransportFallback_setVideoActive(g_state.fallback, active);
|
||||
atomic_store_explicit(&g_state.fallbackDisplayRequested, active,
|
||||
memory_order_release);
|
||||
goto done;
|
||||
return RENDER_QUEUE_SOURCE_NONE;
|
||||
}
|
||||
|
||||
static bool useVideoSourceLocked(LG_VideoSource source,
|
||||
LG_VideoSource previousRequested, bool inputAvailable)
|
||||
{
|
||||
const RenderQueueSource queueSource = renderSource(source);
|
||||
if (source == LG_VIDEO_SOURCE_NONE)
|
||||
return renderQueue_sourceTransition(
|
||||
queueSource, 0, false, NULL) != 0;
|
||||
|
||||
struct VideoSourceState * state = &g_state.videoSource[source];
|
||||
if (previousRequested != source)
|
||||
{
|
||||
atomic_store_explicit(
|
||||
&state->transitionSerial, 0, memory_order_release);
|
||||
atomic_store_explicit(
|
||||
&state->transitionPending, false, memory_order_release);
|
||||
}
|
||||
|
||||
const uint64_t generation = atomic_load_explicit(
|
||||
&state->generation, memory_order_acquire);
|
||||
if (!generation || !atomic_load_explicit(
|
||||
&state->ready, memory_order_acquire))
|
||||
return false;
|
||||
|
||||
const LG_VideoSource applied = atomic_load_explicit(
|
||||
&g_state.videoSourceApplied, memory_order_acquire);
|
||||
const uint64_t appliedGeneration = atomic_load_explicit(
|
||||
&g_state.videoSourceAppliedGeneration, memory_order_acquire);
|
||||
const int width = atomic_load_explicit(
|
||||
&state->width, memory_order_relaxed);
|
||||
const int height = atomic_load_explicit(
|
||||
&state->height, memory_order_relaxed);
|
||||
const LG_RendererRotate rotate = atomic_load_explicit(
|
||||
&state->rotate, memory_order_relaxed);
|
||||
if (width <= 0 || height <= 0)
|
||||
return false;
|
||||
|
||||
const LG_RendererRotate appliedRotate = atomic_load_explicit(
|
||||
&state->appliedRotate, memory_order_acquire);
|
||||
const bool geometryApplied =
|
||||
atomic_load_explicit(&state->appliedWidth, memory_order_relaxed) ==
|
||||
(unsigned int)width &&
|
||||
atomic_load_explicit(&state->appliedHeight, memory_order_relaxed) ==
|
||||
(unsigned int)height &&
|
||||
appliedRotate == rotate;
|
||||
if (applied == source && appliedGeneration == generation &&
|
||||
previousRequested == source && geometryApplied)
|
||||
return true;
|
||||
|
||||
if (previousRequested == source && atomic_load_explicit(
|
||||
&state->transitionPending, memory_order_acquire))
|
||||
return true;
|
||||
|
||||
if (state->cursorStateValid)
|
||||
renderQueue_sourceCursorState(queueSource, generation,
|
||||
state->cursorVisible &&
|
||||
(source == LG_VIDEO_SOURCE_FALLBACK ||
|
||||
g_cursor.draw || !inputAvailable),
|
||||
state->cursorX, state->cursorY,
|
||||
state->cursorHX, state->cursorHY);
|
||||
|
||||
const bool configure = state->swSurface && state->configurePending;
|
||||
atomic_store_explicit(
|
||||
&state->transitionPending, true, memory_order_release);
|
||||
const uint64_t serial = configure ?
|
||||
renderQueue_sourceSwSurfaceConfigureTransition(
|
||||
queueSource, generation, width, height,
|
||||
&state->transitionSerial) :
|
||||
renderQueue_sourceTransition(
|
||||
queueSource, generation, state->swSurface,
|
||||
&state->transitionSerial);
|
||||
if (!serial)
|
||||
{
|
||||
atomic_store_explicit(
|
||||
&state->transitionPending, false, memory_order_release);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool app_useVideoSource(LG_VideoSource source)
|
||||
{
|
||||
if (source < LG_VIDEO_SOURCE_NONE || source >= LG_VIDEO_SOURCE_COUNT)
|
||||
return false;
|
||||
|
||||
if (source == LG_VIDEO_SOURCE_PRIMARY && !atomic_load_explicit(
|
||||
&g_state.lgHostConnected, memory_order_acquire))
|
||||
return false;
|
||||
|
||||
if (source == LG_VIDEO_SOURCE_FALLBACK && !g_state.fallback)
|
||||
return false;
|
||||
|
||||
const bool inputAvailable = lgInput_available();
|
||||
LG_LOCK(g_state.videoSourceLock);
|
||||
const LG_VideoSource previousRequested = atomic_load_explicit(
|
||||
&g_state.videoSourceRequested, memory_order_acquire);
|
||||
atomic_store_explicit(
|
||||
&g_state.videoSourceRequested, source, memory_order_release);
|
||||
if (source == LG_VIDEO_SOURCE_FALLBACK)
|
||||
lgTransportFallback_requestVideoActive(g_state.fallback, true);
|
||||
const bool result =
|
||||
useVideoSourceLocked(source, previousRequested, inputAvailable);
|
||||
LG_UNLOCK(g_state.videoSourceLock);
|
||||
return result;
|
||||
}
|
||||
|
||||
void app_refreshVideoSource(void)
|
||||
{
|
||||
const bool inputAvailable = lgInput_available();
|
||||
LG_LOCK(g_state.videoSourceLock);
|
||||
const LG_VideoSource source = atomic_load_explicit(
|
||||
&g_state.videoSourceRequested, memory_order_acquire);
|
||||
if (source != LG_VIDEO_SOURCE_NONE &&
|
||||
(source != LG_VIDEO_SOURCE_PRIMARY || atomic_load_explicit(
|
||||
&g_state.lgHostConnected, memory_order_acquire)) &&
|
||||
(source != LG_VIDEO_SOURCE_FALLBACK || g_state.fallback))
|
||||
useVideoSourceLocked(source, source, inputAvailable);
|
||||
LG_UNLOCK(g_state.videoSourceLock);
|
||||
}
|
||||
|
||||
@@ -187,6 +187,7 @@ static void applyView(bool active, bool force)
|
||||
(g_params.alwaysShowCursor || g_params.captureInputOnly)
|
||||
? true : g_cursor.inView;
|
||||
g_cursor.redraw = true;
|
||||
app_invalidateWindow(false);
|
||||
g_cursor.motionValid = false;
|
||||
g_cursor.autoCaptureActive = active && g_params.autoCapture &&
|
||||
!g_cursor.grab && !g_params.captureInputOnly;
|
||||
@@ -984,6 +985,7 @@ void core_handleMouseNormal(double ex, double ey)
|
||||
g_cursor.realign = false;
|
||||
g_cursor.realigning = false;
|
||||
g_cursor.redraw = true;
|
||||
app_invalidateWindow(false);
|
||||
|
||||
if (!g_cursor.inWindow)
|
||||
return;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -57,6 +57,30 @@ enum AudioResampler {
|
||||
AUDIO_RESAMPLER_BACKEND
|
||||
};
|
||||
|
||||
struct VideoSourceState
|
||||
{
|
||||
atomic_uint_least64_t generation;
|
||||
atomic_uint_least64_t transitionSerial;
|
||||
atomic_bool transitionPending;
|
||||
atomic_uint_least32_t width;
|
||||
atomic_uint_least32_t height;
|
||||
atomic_int rotate;
|
||||
atomic_uint_least32_t appliedWidth;
|
||||
atomic_uint_least32_t appliedHeight;
|
||||
atomic_int appliedRotate;
|
||||
atomic_bool ready;
|
||||
bool swSurface;
|
||||
bool configurePending;
|
||||
|
||||
bool cursorStateValid;
|
||||
bool cursorVisible;
|
||||
int cursorX;
|
||||
int cursorY;
|
||||
int cursorHX;
|
||||
int cursorHY;
|
||||
uint32_t cursorWhiteLevel;
|
||||
};
|
||||
|
||||
struct AppState
|
||||
{
|
||||
ImGuiIO * io;
|
||||
@@ -79,11 +103,15 @@ struct AppState
|
||||
bool dsInitialized;
|
||||
bool jitRender;
|
||||
|
||||
atomic_bool fallbackDisplayRequested;
|
||||
atomic_bool fallbackDisplayActive;
|
||||
atomic_bool fallbackDisplayTransition;
|
||||
struct VideoSourceState videoSource[LG_VIDEO_SOURCE_COUNT];
|
||||
atomic_int videoSourceRequested;
|
||||
atomic_int videoSourceApplied;
|
||||
atomic_uint_least64_t videoSourceAppliedGeneration;
|
||||
LG_Lock videoSourceLock;
|
||||
LG_Lock videoSplashLock;
|
||||
bool videoGeometryDirty;
|
||||
|
||||
atomic_bool fallbackUUIDMismatch;
|
||||
bool fallbackSurfaceValid;
|
||||
|
||||
uint8_t guestUUID[16];
|
||||
bool guestUUIDValid;
|
||||
@@ -296,7 +324,7 @@ struct CursorState
|
||||
bool realigning;
|
||||
|
||||
/* true if the cursor needs re-drawing/updating */
|
||||
bool redraw;
|
||||
atomic_bool redraw;
|
||||
|
||||
/* true if the cursor movements should be scaled */
|
||||
bool useScale;
|
||||
|
||||
@@ -20,20 +20,167 @@
|
||||
|
||||
#include "render_queue.h"
|
||||
|
||||
#include <stdatomic.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/ll.h"
|
||||
#include "main.h"
|
||||
#include "overlays.h"
|
||||
|
||||
struct ll * l_renderQueue = NULL;
|
||||
static bool l_showSwSurface;
|
||||
static bool l_surfaceFormatValid;
|
||||
static bool l_rendererSupportsNativeHDR;
|
||||
static bool l_showSwSurface;
|
||||
static bool l_surfaceFormatValid;
|
||||
static bool l_rendererSupportsNativeHDR;
|
||||
static LG_RendererFormat l_surfaceFormat;
|
||||
|
||||
static _Atomic(uint64_t) l_sourceGeneration[RENDER_QUEUE_SOURCE_COUNT];
|
||||
static _Atomic(uint64_t) l_transitionSerial;
|
||||
static LG_Lock l_sourceLock;
|
||||
static LG_Lock l_transitionLock;
|
||||
|
||||
static RenderQueueSource l_appliedSource;
|
||||
static uint64_t l_appliedGeneration;
|
||||
static bool l_appliedSwSurface;
|
||||
|
||||
static RenderQueueSourcePrepareFn l_sourcePrepareFn;
|
||||
static RenderQueueSourceAppliedFn l_sourceAppliedFn;
|
||||
static void * l_sourceCallbackOpaque;
|
||||
|
||||
typedef struct RenderQueueTransition
|
||||
{
|
||||
RenderQueueSource source;
|
||||
uint64_t generation;
|
||||
uint64_t serial;
|
||||
bool swSurface;
|
||||
bool valid;
|
||||
}
|
||||
RenderQueueTransition;
|
||||
|
||||
static RenderQueueTransition l_pendingTransition;
|
||||
|
||||
typedef struct RenderQueueCursor
|
||||
{
|
||||
uint64_t stateGeneration;
|
||||
bool stateValid;
|
||||
bool visible;
|
||||
int x;
|
||||
int y;
|
||||
int hx;
|
||||
int hy;
|
||||
|
||||
uint64_t imageGeneration;
|
||||
bool imageValid;
|
||||
LG_RendererCursor type;
|
||||
int width;
|
||||
int height;
|
||||
int pitch;
|
||||
uint8_t * data;
|
||||
|
||||
uint64_t colorGeneration;
|
||||
bool colorValid;
|
||||
LGColorTransform colorTransform;
|
||||
|
||||
uint64_t whiteGeneration;
|
||||
bool whiteValid;
|
||||
uint32_t sdrWhiteLevel;
|
||||
}
|
||||
RenderQueueCursor;
|
||||
|
||||
typedef struct RenderQueueFormat
|
||||
{
|
||||
uint64_t generation;
|
||||
bool valid;
|
||||
LG_RendererFormat format;
|
||||
bool rendererSupportsNativeHDR;
|
||||
}
|
||||
RenderQueueFormat;
|
||||
|
||||
static RenderQueueCursor l_cursor[RENDER_QUEUE_SOURCE_COUNT];
|
||||
static RenderQueueFormat l_format[RENDER_QUEUE_SOURCE_COUNT];
|
||||
static const LGColorTransform l_identityColorTransform;
|
||||
|
||||
static bool sourceValid(RenderQueueSource source)
|
||||
{
|
||||
return source > RENDER_QUEUE_SOURCE_NONE &&
|
||||
source < RENDER_QUEUE_SOURCE_COUNT;
|
||||
}
|
||||
|
||||
static bool generationValid(RenderQueueSource source, uint64_t generation)
|
||||
{
|
||||
return sourceValid(source) && generation &&
|
||||
atomic_load_explicit(&l_sourceGeneration[source],
|
||||
memory_order_acquire) == generation;
|
||||
}
|
||||
|
||||
static bool transitionCommand(const RenderCommand * cmd)
|
||||
{
|
||||
return cmd->op == SOURCE_OP_TRANSITION ||
|
||||
cmd->op == SW_SURFACE_OP_CONFIGURE_TRANSITION;
|
||||
}
|
||||
|
||||
static bool commandValid(const RenderCommand * cmd)
|
||||
{
|
||||
if (transitionCommand(cmd) &&
|
||||
atomic_load_explicit(&l_transitionSerial, memory_order_acquire) !=
|
||||
cmd->transitionSerial)
|
||||
return false;
|
||||
|
||||
if (cmd->source == RENDER_QUEUE_SOURCE_NONE)
|
||||
return cmd->op == SOURCE_OP_TRANSITION;
|
||||
|
||||
return generationValid(cmd->source, cmd->generation);
|
||||
}
|
||||
|
||||
static void freeCommand(RenderCommand * cmd)
|
||||
{
|
||||
switch (cmd->op)
|
||||
{
|
||||
case SW_SURFACE_OP_DRAW_BITMAP:
|
||||
free(cmd->swSurfaceDrawBitmap.data);
|
||||
break;
|
||||
|
||||
case CURSOR_OP_IMAGE:
|
||||
free(cmd->cursorImage.data);
|
||||
break;
|
||||
|
||||
case CURSOR_OP_COLOR_TRANSFORM:
|
||||
free(cmd->cursorColorTransform.data);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
free(cmd);
|
||||
}
|
||||
|
||||
static void setCommandSource(RenderCommand * cmd, RenderQueueSource source,
|
||||
uint64_t generation)
|
||||
{
|
||||
cmd->source = source;
|
||||
cmd->generation = generation;
|
||||
}
|
||||
|
||||
static bool copyCursorImage(RenderCommand * cmd, const void * data)
|
||||
{
|
||||
if (!data || cmd->cursorImage.height <= 0 || cmd->cursorImage.pitch <= 0)
|
||||
return false;
|
||||
|
||||
if ((size_t)cmd->cursorImage.height >
|
||||
SIZE_MAX / (size_t)cmd->cursorImage.pitch)
|
||||
return false;
|
||||
|
||||
const size_t size =
|
||||
(size_t)cmd->cursorImage.height * (size_t)cmd->cursorImage.pitch;
|
||||
cmd->cursorImage.data = malloc(size);
|
||||
if (!cmd->cursorImage.data)
|
||||
return false;
|
||||
|
||||
memcpy(cmd->cursorImage.data, data, size);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void updateSurfaceFormat(void)
|
||||
{
|
||||
if (!g_state.ds->setHDRImageDesc)
|
||||
@@ -56,7 +203,11 @@ static void updateSurfaceFormat(void)
|
||||
}
|
||||
|
||||
if (!l_surfaceFormatValid)
|
||||
{
|
||||
g_state.ds->setHDRImageDesc(&format);
|
||||
atomic_store(&g_state.hdrDescFailed, false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (format.hdr && !l_rendererSupportsNativeHDR)
|
||||
{
|
||||
@@ -83,7 +234,22 @@ void renderQueue_init(void)
|
||||
l_showSwSurface = false;
|
||||
l_surfaceFormatValid = false;
|
||||
l_rendererSupportsNativeHDR = false;
|
||||
l_appliedSource = RENDER_QUEUE_SOURCE_NONE;
|
||||
l_appliedGeneration = 0;
|
||||
l_appliedSwSurface = false;
|
||||
l_sourcePrepareFn = NULL;
|
||||
l_sourceAppliedFn = NULL;
|
||||
l_sourceCallbackOpaque = NULL;
|
||||
l_pendingTransition = (RenderQueueTransition) {};
|
||||
memset(&l_surfaceFormat, 0, sizeof(l_surfaceFormat));
|
||||
memset(l_cursor, 0, sizeof(l_cursor));
|
||||
memset(l_format, 0, sizeof(l_format));
|
||||
|
||||
for (int i = 0; i < RENDER_QUEUE_SOURCE_COUNT; ++i)
|
||||
atomic_store_explicit(&l_sourceGeneration[i], 0, memory_order_relaxed);
|
||||
atomic_store_explicit(&l_transitionSerial, 0, memory_order_relaxed);
|
||||
LG_LOCK_INIT(l_sourceLock);
|
||||
LG_LOCK_INIT(l_transitionLock);
|
||||
}
|
||||
|
||||
void renderQueue_free(void)
|
||||
@@ -92,47 +258,194 @@ void renderQueue_free(void)
|
||||
return;
|
||||
|
||||
renderQueue_clear();
|
||||
|
||||
for (int i = 0; i < RENDER_QUEUE_SOURCE_COUNT; ++i)
|
||||
{
|
||||
free(l_cursor[i].data);
|
||||
l_cursor[i].data = NULL;
|
||||
}
|
||||
|
||||
ll_free(l_renderQueue);
|
||||
l_renderQueue = NULL;
|
||||
LG_LOCK_FREE(l_sourceLock);
|
||||
LG_LOCK_FREE(l_transitionLock);
|
||||
}
|
||||
|
||||
void renderQueue_clear(void)
|
||||
{
|
||||
RenderCommand * cmd;
|
||||
while(ll_shift(l_renderQueue, (void **)&cmd))
|
||||
{
|
||||
if (cmd->op == SW_SURFACE_OP_DRAW_BITMAP)
|
||||
free(cmd->swSurfaceDrawBitmap.data);
|
||||
free(cmd);
|
||||
}
|
||||
freeCommand(cmd);
|
||||
}
|
||||
|
||||
void renderQueue_swSurfaceConfigure(int width, int height)
|
||||
void renderQueue_setSourceFns(RenderQueueSourcePrepareFn prepare,
|
||||
RenderQueueSourceAppliedFn applied, void * opaque)
|
||||
{
|
||||
RenderCommand * cmd = malloc(sizeof(*cmd));
|
||||
cmd->op = SW_SURFACE_OP_CONFIGURE;
|
||||
cmd->swSurfaceConfigure.width = width;
|
||||
cmd->swSurfaceConfigure.height = height;
|
||||
ll_push(l_renderQueue, cmd);
|
||||
app_invalidateWindow(true);
|
||||
l_sourcePrepareFn = prepare;
|
||||
l_sourceAppliedFn = applied;
|
||||
l_sourceCallbackOpaque = opaque;
|
||||
}
|
||||
|
||||
void renderQueue_swSurfaceDrawFill(int x, int y, int width, int height,
|
||||
uint64_t renderQueue_sourceBegin(RenderQueueSource source)
|
||||
{
|
||||
if (!sourceValid(source))
|
||||
return 0;
|
||||
|
||||
LG_LOCK(l_sourceLock);
|
||||
const uint64_t previous = atomic_load_explicit(
|
||||
&l_sourceGeneration[source], memory_order_relaxed);
|
||||
const uint64_t generation = previous + 1;
|
||||
atomic_store_explicit(
|
||||
&l_sourceGeneration[source], generation, memory_order_release);
|
||||
|
||||
RenderQueueCursor * cursor = &l_cursor[source];
|
||||
if (cursor->stateValid && cursor->stateGeneration == previous)
|
||||
cursor->stateGeneration = generation;
|
||||
if (cursor->imageValid && cursor->imageGeneration == previous)
|
||||
cursor->imageGeneration = generation;
|
||||
if (cursor->colorValid && cursor->colorGeneration == previous)
|
||||
cursor->colorGeneration = generation;
|
||||
if (cursor->whiteValid && cursor->whiteGeneration == previous)
|
||||
cursor->whiteGeneration = generation;
|
||||
LG_UNLOCK(l_sourceLock);
|
||||
return generation;
|
||||
}
|
||||
|
||||
void renderQueue_sourceInvalidate(RenderQueueSource source,
|
||||
uint64_t generation)
|
||||
{
|
||||
if (!sourceValid(source) || !generation)
|
||||
return;
|
||||
LG_LOCK(l_sourceLock);
|
||||
const uint64_t invalidGeneration = generation + 1;
|
||||
if (atomic_compare_exchange_strong_explicit(&l_sourceGeneration[source],
|
||||
&generation, invalidGeneration, memory_order_acq_rel,
|
||||
memory_order_relaxed))
|
||||
{
|
||||
RenderQueueCursor * cursor = &l_cursor[source];
|
||||
if (cursor->stateValid && cursor->stateGeneration == generation)
|
||||
cursor->stateGeneration = invalidGeneration;
|
||||
if (cursor->imageValid && cursor->imageGeneration == generation)
|
||||
cursor->imageGeneration = invalidGeneration;
|
||||
if (cursor->colorValid && cursor->colorGeneration == generation)
|
||||
cursor->colorGeneration = invalidGeneration;
|
||||
if (cursor->whiteValid && cursor->whiteGeneration == generation)
|
||||
cursor->whiteGeneration = invalidGeneration;
|
||||
}
|
||||
LG_UNLOCK(l_sourceLock);
|
||||
}
|
||||
|
||||
void renderQueue_sourceClearCursor(RenderQueueSource source)
|
||||
{
|
||||
if (!sourceValid(source))
|
||||
return;
|
||||
|
||||
LG_LOCK(l_sourceLock);
|
||||
free(l_cursor[source].data);
|
||||
memset(&l_cursor[source], 0, sizeof(l_cursor[source]));
|
||||
LG_UNLOCK(l_sourceLock);
|
||||
}
|
||||
|
||||
static uint64_t enqueueTransition(RenderCommand * cmd,
|
||||
atomic_uint_least64_t * publishedSerial)
|
||||
{
|
||||
LG_LOCK(l_transitionLock);
|
||||
const uint64_t serial = atomic_load_explicit(
|
||||
&l_transitionSerial, memory_order_relaxed) + 1;
|
||||
cmd->transitionSerial = serial;
|
||||
if (!ll_push(l_renderQueue, cmd))
|
||||
{
|
||||
LG_UNLOCK(l_transitionLock);
|
||||
free(cmd);
|
||||
return 0;
|
||||
}
|
||||
atomic_store_explicit(
|
||||
&l_transitionSerial, serial, memory_order_release);
|
||||
if (publishedSerial)
|
||||
atomic_store_explicit(publishedSerial, serial, memory_order_release);
|
||||
LG_UNLOCK(l_transitionLock);
|
||||
|
||||
app_invalidateWindow(true);
|
||||
return serial;
|
||||
}
|
||||
|
||||
uint64_t renderQueue_sourceTransition(RenderQueueSource source,
|
||||
uint64_t generation, bool swSurface,
|
||||
atomic_uint_least64_t * publishedSerial)
|
||||
{
|
||||
if (source != RENDER_QUEUE_SOURCE_NONE &&
|
||||
!generationValid(source, generation))
|
||||
return 0;
|
||||
|
||||
if (source == RENDER_QUEUE_SOURCE_NONE)
|
||||
{
|
||||
generation = 0;
|
||||
swSurface = false;
|
||||
}
|
||||
|
||||
RenderCommand * cmd = malloc(sizeof(*cmd));
|
||||
if (!cmd)
|
||||
return 0;
|
||||
|
||||
setCommandSource(cmd, source, generation);
|
||||
cmd->op = SOURCE_OP_TRANSITION;
|
||||
cmd->sourceTransition.swSurface = swSurface;
|
||||
return enqueueTransition(cmd, publishedSerial);
|
||||
}
|
||||
|
||||
uint64_t renderQueue_sourceSwSurfaceConfigureTransition(
|
||||
RenderQueueSource source, uint64_t generation, int width, int height,
|
||||
atomic_uint_least64_t * publishedSerial)
|
||||
{
|
||||
if (!generationValid(source, generation))
|
||||
return 0;
|
||||
|
||||
RenderCommand * cmd = malloc(sizeof(*cmd));
|
||||
if (!cmd)
|
||||
return 0;
|
||||
|
||||
setCommandSource(cmd, source, generation);
|
||||
cmd->op =
|
||||
SW_SURFACE_OP_CONFIGURE_TRANSITION;
|
||||
cmd->swSurfaceConfigureTransition.width = width;
|
||||
cmd->swSurfaceConfigureTransition.height = height;
|
||||
return enqueueTransition(cmd, publishedSerial);
|
||||
}
|
||||
|
||||
void renderQueue_sourceSwSurfaceDrawFill(RenderQueueSource source,
|
||||
uint64_t generation, int x, int y, int width, int height,
|
||||
uint32_t color)
|
||||
{
|
||||
if (!generationValid(source, generation))
|
||||
return;
|
||||
|
||||
RenderCommand * cmd = malloc(sizeof(*cmd));
|
||||
if (!cmd)
|
||||
return;
|
||||
|
||||
setCommandSource(cmd, source, generation);
|
||||
cmd->op = SW_SURFACE_OP_DRAW_FILL;
|
||||
cmd->swSurfaceDrawFill.x = x;
|
||||
cmd->swSurfaceDrawFill.y = y;
|
||||
cmd->swSurfaceDrawFill.width = width;
|
||||
cmd->swSurfaceDrawFill.height = height;
|
||||
cmd->swSurfaceDrawFill.color = color;
|
||||
ll_push(l_renderQueue, cmd);
|
||||
if (!ll_push(l_renderQueue, cmd))
|
||||
{
|
||||
free(cmd);
|
||||
return;
|
||||
}
|
||||
|
||||
app_invalidateWindow(true);
|
||||
}
|
||||
|
||||
void renderQueue_swSurfaceDrawBitmap(int x, int y, int width, int height,
|
||||
int stride, const void * data, bool topDown)
|
||||
void renderQueue_sourceSwSurfaceDrawBitmap(RenderQueueSource source,
|
||||
uint64_t generation, int x, int y, int width, int height, int stride,
|
||||
const void * data, bool topDown)
|
||||
{
|
||||
if (!generationValid(source, generation))
|
||||
return;
|
||||
|
||||
if (width <= 0 || height <= 0 || stride <= 0)
|
||||
{
|
||||
if (width < 0 || height < 0 || stride < 0)
|
||||
@@ -175,6 +488,7 @@ void renderQueue_swSurfaceDrawBitmap(int x, int y, int width, int height,
|
||||
|
||||
memcpy(copy, data, size);
|
||||
|
||||
setCommandSource(cmd, source, generation);
|
||||
cmd->op = SW_SURFACE_OP_DRAW_BITMAP;
|
||||
cmd->swSurfaceDrawBitmap.x = x;
|
||||
cmd->swSurfaceDrawBitmap.y = y;
|
||||
@@ -186,7 +500,31 @@ void renderQueue_swSurfaceDrawBitmap(int x, int y, int width, int height,
|
||||
|
||||
if (!ll_push(l_renderQueue, cmd))
|
||||
{
|
||||
free(copy);
|
||||
freeCommand(cmd);
|
||||
return;
|
||||
}
|
||||
|
||||
app_invalidateWindow(true);
|
||||
}
|
||||
|
||||
void renderQueue_sourceSurfaceFormat(RenderQueueSource source,
|
||||
uint64_t generation, const LG_RendererFormat format,
|
||||
bool rendererSupportsNativeHDR)
|
||||
{
|
||||
if (!generationValid(source, generation))
|
||||
return;
|
||||
|
||||
RenderCommand * cmd = malloc(sizeof(*cmd));
|
||||
if (!cmd)
|
||||
return;
|
||||
|
||||
setCommandSource(cmd, source, generation);
|
||||
cmd->op = SURFACE_OP_FORMAT;
|
||||
cmd->surfaceFormat.format = format;
|
||||
cmd->surfaceFormat.rendererSupportsNativeHDR =
|
||||
rendererSupportsNativeHDR;
|
||||
if (!ll_push(l_renderQueue, cmd))
|
||||
{
|
||||
free(cmd);
|
||||
return;
|
||||
}
|
||||
@@ -194,50 +532,161 @@ void renderQueue_swSurfaceDrawBitmap(int x, int y, int width, int height,
|
||||
app_invalidateWindow(true);
|
||||
}
|
||||
|
||||
void renderQueue_swSurfaceShow(bool show)
|
||||
void renderQueue_sourceCursorState(RenderQueueSource source,
|
||||
uint64_t generation, bool visible, int x, int y, int hx, int hy)
|
||||
{
|
||||
RenderCommand * cmd = malloc(sizeof(*cmd));
|
||||
cmd->op = SW_SURFACE_OP_SHOW;
|
||||
cmd->swSurfaceShow.show = show;
|
||||
ll_push(l_renderQueue, cmd);
|
||||
app_invalidateWindow(true);
|
||||
}
|
||||
if (!generationValid(source, generation))
|
||||
return;
|
||||
|
||||
void renderQueue_surfaceFormat(const LG_RendererFormat format,
|
||||
bool rendererSupportsNativeHDR)
|
||||
{
|
||||
RenderCommand * cmd = malloc(sizeof(*cmd));
|
||||
cmd->op = SURFACE_OP_FORMAT;
|
||||
cmd->surfaceFormat.format = format;
|
||||
cmd->surfaceFormat.rendererSupportsNativeHDR =
|
||||
rendererSupportsNativeHDR;
|
||||
ll_push(l_renderQueue, cmd);
|
||||
app_invalidateWindow(true);
|
||||
}
|
||||
if (!cmd)
|
||||
return;
|
||||
|
||||
void renderQueue_cursorState(bool visible, int x, int y, int hx, int hy)
|
||||
{
|
||||
RenderCommand * cmd = malloc(sizeof(*cmd));
|
||||
setCommandSource(cmd, source, generation);
|
||||
cmd->op = CURSOR_OP_STATE;
|
||||
cmd->cursorState.visible = visible;
|
||||
cmd->cursorState.x = x;
|
||||
cmd->cursorState.y = y;
|
||||
cmd->cursorState.hx = hx;
|
||||
cmd->cursorState.hy = hy;
|
||||
ll_push(l_renderQueue, cmd);
|
||||
if (!ll_push(l_renderQueue, cmd))
|
||||
free(cmd);
|
||||
}
|
||||
|
||||
void renderQueue_cursorImage(LG_RendererCursor type,
|
||||
int width, int height, int pitch, uint8_t * data)
|
||||
void renderQueue_sourceCursorImage(RenderQueueSource source,
|
||||
uint64_t generation, LG_RendererCursor type,
|
||||
int width, int height, int pitch, const void * data)
|
||||
{
|
||||
RenderCommand * cmd = malloc(sizeof(*cmd));
|
||||
if (!generationValid(source, generation))
|
||||
return;
|
||||
|
||||
RenderCommand * cmd = malloc(sizeof(*cmd));
|
||||
if (!cmd)
|
||||
return;
|
||||
|
||||
setCommandSource(cmd, source, generation);
|
||||
cmd->op = CURSOR_OP_IMAGE;
|
||||
cmd->cursorImage.type = type;
|
||||
cmd->cursorImage.width = width;
|
||||
cmd->cursorImage.height = height;
|
||||
cmd->cursorImage.pitch = pitch;
|
||||
cmd->cursorImage.data = data;
|
||||
ll_push(l_renderQueue, cmd);
|
||||
cmd->cursorImage.data = NULL;
|
||||
if (!copyCursorImage(cmd, data) || !ll_push(l_renderQueue, cmd))
|
||||
freeCommand(cmd);
|
||||
}
|
||||
|
||||
void renderQueue_sourceCursorColorTransform(RenderQueueSource source,
|
||||
uint64_t generation, const LGColorTransform * transform)
|
||||
{
|
||||
if (!generationValid(source, generation) || !transform)
|
||||
return;
|
||||
|
||||
RenderCommand * cmd = malloc(sizeof(*cmd));
|
||||
if (!cmd)
|
||||
return;
|
||||
|
||||
LGColorTransform * copy = malloc(sizeof(*copy));
|
||||
if (!copy)
|
||||
{
|
||||
free(cmd);
|
||||
return;
|
||||
}
|
||||
*copy = *transform;
|
||||
|
||||
setCommandSource(cmd, source, generation);
|
||||
cmd->op = CURSOR_OP_COLOR_TRANSFORM;
|
||||
cmd->cursorColorTransform.data = copy;
|
||||
if (!ll_push(l_renderQueue, cmd))
|
||||
freeCommand(cmd);
|
||||
}
|
||||
|
||||
void renderQueue_sourceCursorWhiteLevel(RenderQueueSource source,
|
||||
uint64_t generation, uint32_t sdrWhiteLevel)
|
||||
{
|
||||
if (!generationValid(source, generation))
|
||||
return;
|
||||
|
||||
RenderCommand * cmd = malloc(sizeof(*cmd));
|
||||
if (!cmd)
|
||||
return;
|
||||
|
||||
setCommandSource(cmd, source, generation);
|
||||
cmd->op = CURSOR_OP_WHITE_LEVEL;
|
||||
cmd->cursorWhiteLevel.value = sdrWhiteLevel;
|
||||
if (!ll_push(l_renderQueue, cmd))
|
||||
free(cmd);
|
||||
}
|
||||
|
||||
static void applyCursor(RenderQueueSource source, uint64_t generation)
|
||||
{
|
||||
RenderQueueCursor * cursor = &l_cursor[source];
|
||||
|
||||
if (cursor->imageValid && cursor->imageGeneration == generation)
|
||||
RENDERER(onMouseShape, cursor->type, cursor->width, cursor->height,
|
||||
cursor->pitch, cursor->data);
|
||||
|
||||
if (cursor->imageValid && cursor->imageGeneration == generation &&
|
||||
cursor->stateValid && cursor->stateGeneration == generation)
|
||||
RENDERER(onMouseEvent, cursor->visible, cursor->x, cursor->y,
|
||||
cursor->hx, cursor->hy);
|
||||
else
|
||||
RENDERER(onMouseEvent, false, 0, 0, 0, 0);
|
||||
|
||||
if (g_state.lgr->ops.onMouseColorTransform)
|
||||
g_state.lgr->ops.onMouseColorTransform(g_state.lgr,
|
||||
cursor->colorValid && cursor->colorGeneration == generation ?
|
||||
&cursor->colorTransform : &l_identityColorTransform);
|
||||
|
||||
if (g_state.lgr->ops.onMouseWhiteLevel)
|
||||
g_state.lgr->ops.onMouseWhiteLevel(g_state.lgr,
|
||||
cursor->whiteValid && cursor->whiteGeneration == generation ?
|
||||
cursor->sdrWhiteLevel : LG_SDR_WHITE_LEVEL_DEFAULT);
|
||||
}
|
||||
|
||||
static void applySourceTransition(const RenderCommand * cmd,
|
||||
bool showSwSurface)
|
||||
{
|
||||
if (l_showSwSurface != showSwSurface)
|
||||
{
|
||||
RENDERER(swSurfaceShow, showSwSurface);
|
||||
l_showSwSurface = showSwSurface;
|
||||
}
|
||||
|
||||
l_surfaceFormatValid = false;
|
||||
l_rendererSupportsNativeHDR = false;
|
||||
if (sourceValid(cmd->source))
|
||||
{
|
||||
const RenderQueueFormat * format = &l_format[cmd->source];
|
||||
if (format->valid && format->generation == cmd->generation)
|
||||
{
|
||||
l_surfaceFormat = format->format;
|
||||
l_surfaceFormatValid = true;
|
||||
l_rendererSupportsNativeHDR =
|
||||
format->rendererSupportsNativeHDR;
|
||||
}
|
||||
}
|
||||
updateSurfaceFormat();
|
||||
|
||||
l_appliedSource = cmd->source;
|
||||
l_appliedGeneration = cmd->generation;
|
||||
l_appliedSwSurface = showSwSurface;
|
||||
|
||||
if (sourceValid(cmd->source))
|
||||
applyCursor(cmd->source, cmd->generation);
|
||||
else
|
||||
RENDERER(onMouseEvent, false, 0, 0, 0, 0);
|
||||
|
||||
l_pendingTransition.source = cmd->source;
|
||||
l_pendingTransition.generation = cmd->generation;
|
||||
l_pendingTransition.serial = cmd->transitionSerial;
|
||||
l_pendingTransition.swSurface = l_appliedSwSurface;
|
||||
l_pendingTransition.valid = true;
|
||||
}
|
||||
|
||||
static bool prepareSourceTransition(const RenderCommand * cmd)
|
||||
{
|
||||
return !l_sourcePrepareFn || l_sourcePrepareFn(l_sourceCallbackOpaque,
|
||||
cmd->source, cmd->generation, cmd->transitionSerial);
|
||||
}
|
||||
|
||||
void renderQueue_process(void)
|
||||
@@ -245,12 +694,32 @@ void renderQueue_process(void)
|
||||
RenderCommand * cmd;
|
||||
while(ll_shift(l_renderQueue, (void **)&cmd))
|
||||
{
|
||||
const bool transition = transitionCommand(cmd);
|
||||
if (transition)
|
||||
LG_LOCK(l_transitionLock);
|
||||
LG_LOCK(l_sourceLock);
|
||||
|
||||
if (!commandValid(cmd))
|
||||
{
|
||||
LG_UNLOCK(l_sourceLock);
|
||||
if (transition)
|
||||
LG_UNLOCK(l_transitionLock);
|
||||
freeCommand(cmd);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch(cmd->op)
|
||||
{
|
||||
case SW_SURFACE_OP_CONFIGURE:
|
||||
case SW_SURFACE_OP_CONFIGURE_TRANSITION:
|
||||
if (!prepareSourceTransition(cmd))
|
||||
break;
|
||||
RENDERER(swSurfaceConfigure,
|
||||
cmd->swSurfaceConfigure.width,
|
||||
cmd->swSurfaceConfigure.height);
|
||||
cmd->swSurfaceConfigureTransition.width,
|
||||
cmd->swSurfaceConfigureTransition.height);
|
||||
RENDERER(swSurfaceDrawFill, 0, 0,
|
||||
cmd->swSurfaceConfigureTransition.width,
|
||||
cmd->swSurfaceConfigureTransition.height, 0);
|
||||
applySourceTransition(cmd, true);
|
||||
break;
|
||||
|
||||
case SW_SURFACE_OP_DRAW_FILL:
|
||||
@@ -267,36 +736,132 @@ void renderQueue_process(void)
|
||||
cmd->swSurfaceDrawBitmap.stride, cmd->swSurfaceDrawBitmap.data,
|
||||
cmd->swSurfaceDrawBitmap.topDown);
|
||||
free(cmd->swSurfaceDrawBitmap.data);
|
||||
break;
|
||||
|
||||
case SW_SURFACE_OP_SHOW:
|
||||
l_showSwSurface = cmd->swSurfaceShow.show;
|
||||
RENDERER(swSurfaceShow, cmd->swSurfaceShow.show);
|
||||
updateSurfaceFormat();
|
||||
if (cmd->swSurfaceShow.show)
|
||||
overlaySplash_show(false);
|
||||
cmd->swSurfaceDrawBitmap.data = NULL;
|
||||
break;
|
||||
|
||||
case SURFACE_OP_FORMAT:
|
||||
l_surfaceFormat = cmd->surfaceFormat.format;
|
||||
l_surfaceFormatValid = true;
|
||||
l_rendererSupportsNativeHDR =
|
||||
{
|
||||
RenderQueueFormat * format = &l_format[cmd->source];
|
||||
format->generation = cmd->generation;
|
||||
format->valid = true;
|
||||
format->format = cmd->surfaceFormat.format;
|
||||
format->rendererSupportsNativeHDR =
|
||||
cmd->surfaceFormat.rendererSupportsNativeHDR;
|
||||
updateSurfaceFormat();
|
||||
|
||||
if (l_appliedSource == cmd->source &&
|
||||
l_appliedGeneration == cmd->generation &&
|
||||
!l_appliedSwSurface)
|
||||
{
|
||||
l_surfaceFormat = format->format;
|
||||
l_surfaceFormatValid = true;
|
||||
l_rendererSupportsNativeHDR =
|
||||
format->rendererSupportsNativeHDR;
|
||||
updateSurfaceFormat();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case CURSOR_OP_STATE:
|
||||
RENDERER(onMouseEvent, cmd->cursorState.visible, cmd->cursorState.x,
|
||||
cmd->cursorState.y, cmd->cursorState.hx, cmd->cursorState.hy);
|
||||
{
|
||||
RenderQueueCursor * cursor = &l_cursor[cmd->source];
|
||||
cursor->stateGeneration = cmd->generation;
|
||||
cursor->stateValid = true;
|
||||
cursor->visible = cmd->cursorState.visible;
|
||||
cursor->x = cmd->cursorState.x;
|
||||
cursor->y = cmd->cursorState.y;
|
||||
cursor->hx = cmd->cursorState.hx;
|
||||
cursor->hy = cmd->cursorState.hy;
|
||||
|
||||
if (l_appliedSource == cmd->source &&
|
||||
l_appliedGeneration == cmd->generation)
|
||||
RENDERER(onMouseEvent,
|
||||
cursor->imageValid &&
|
||||
cursor->imageGeneration == cmd->generation &&
|
||||
cursor->visible,
|
||||
cursor->x, cursor->y, cursor->hx, cursor->hy);
|
||||
break;
|
||||
}
|
||||
|
||||
case CURSOR_OP_IMAGE:
|
||||
RENDERER(onMouseShape,
|
||||
cmd->cursorImage.type,
|
||||
cmd->cursorImage.width, cmd->cursorImage.height,
|
||||
cmd->cursorImage.pitch, cmd->cursorImage.data);
|
||||
free(cmd->cursorImage.data);
|
||||
{
|
||||
RenderQueueCursor * cursor = &l_cursor[cmd->source];
|
||||
free(cursor->data);
|
||||
cursor->imageGeneration = cmd->generation;
|
||||
cursor->imageValid = true;
|
||||
cursor->type = cmd->cursorImage.type;
|
||||
cursor->width = cmd->cursorImage.width;
|
||||
cursor->height = cmd->cursorImage.height;
|
||||
cursor->pitch = cmd->cursorImage.pitch;
|
||||
cursor->data = cmd->cursorImage.data;
|
||||
cmd->cursorImage.data = NULL;
|
||||
|
||||
if (l_appliedSource == cmd->source &&
|
||||
l_appliedGeneration == cmd->generation)
|
||||
{
|
||||
RENDERER(onMouseShape, cursor->type, cursor->width,
|
||||
cursor->height, cursor->pitch, cursor->data);
|
||||
if (cursor->stateValid &&
|
||||
cursor->stateGeneration == cmd->generation)
|
||||
RENDERER(onMouseEvent, cursor->visible, cursor->x, cursor->y,
|
||||
cursor->hx, cursor->hy);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case CURSOR_OP_COLOR_TRANSFORM:
|
||||
{
|
||||
RenderQueueCursor * cursor = &l_cursor[cmd->source];
|
||||
cursor->colorGeneration = cmd->generation;
|
||||
cursor->colorValid = true;
|
||||
cursor->colorTransform = *cmd->cursorColorTransform.data;
|
||||
|
||||
if (l_appliedSource == cmd->source &&
|
||||
l_appliedGeneration == cmd->generation &&
|
||||
g_state.lgr->ops.onMouseColorTransform)
|
||||
g_state.lgr->ops.onMouseColorTransform(g_state.lgr,
|
||||
&cursor->colorTransform);
|
||||
break;
|
||||
}
|
||||
|
||||
case CURSOR_OP_WHITE_LEVEL:
|
||||
{
|
||||
RenderQueueCursor * cursor = &l_cursor[cmd->source];
|
||||
cursor->whiteGeneration = cmd->generation;
|
||||
cursor->whiteValid = true;
|
||||
cursor->sdrWhiteLevel = cmd->cursorWhiteLevel.value;
|
||||
|
||||
if (l_appliedSource == cmd->source &&
|
||||
l_appliedGeneration == cmd->generation &&
|
||||
g_state.lgr->ops.onMouseWhiteLevel)
|
||||
g_state.lgr->ops.onMouseWhiteLevel(g_state.lgr,
|
||||
cursor->sdrWhiteLevel);
|
||||
break;
|
||||
}
|
||||
|
||||
case SOURCE_OP_TRANSITION:
|
||||
if (!prepareSourceTransition(cmd))
|
||||
break;
|
||||
applySourceTransition(cmd,
|
||||
cmd->source != RENDER_QUEUE_SOURCE_NONE &&
|
||||
cmd->sourceTransition.swSurface);
|
||||
break;
|
||||
}
|
||||
free(cmd);
|
||||
LG_UNLOCK(l_sourceLock);
|
||||
if (transition)
|
||||
LG_UNLOCK(l_transitionLock);
|
||||
freeCommand(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
void renderQueue_presented(void)
|
||||
{
|
||||
if (!l_pendingTransition.valid)
|
||||
return;
|
||||
|
||||
const RenderQueueTransition pending = l_pendingTransition;
|
||||
l_pendingTransition.valid = false;
|
||||
|
||||
if (l_sourceAppliedFn)
|
||||
l_sourceAppliedFn(l_sourceCallbackOpaque, pending.source,
|
||||
pending.generation, pending.serial, pending.swSurface);
|
||||
}
|
||||
|
||||
@@ -21,17 +21,40 @@
|
||||
#include "common/ll.h"
|
||||
#include "interface/renderer.h"
|
||||
|
||||
#include <stdatomic.h>
|
||||
|
||||
typedef enum RenderQueueSource
|
||||
{
|
||||
RENDER_QUEUE_SOURCE_NONE,
|
||||
RENDER_QUEUE_SOURCE_PRIMARY,
|
||||
RENDER_QUEUE_SOURCE_FALLBACK,
|
||||
RENDER_QUEUE_SOURCE_COUNT,
|
||||
}
|
||||
RenderQueueSource;
|
||||
|
||||
typedef void (*RenderQueueSourceAppliedFn)(void * opaque,
|
||||
RenderQueueSource source, uint64_t generation, uint64_t serial,
|
||||
bool swSurface);
|
||||
typedef bool (*RenderQueueSourcePrepareFn)(void * opaque,
|
||||
RenderQueueSource source, uint64_t generation, uint64_t serial);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
RenderQueueSource source;
|
||||
uint64_t generation;
|
||||
uint64_t transitionSerial;
|
||||
|
||||
enum
|
||||
{
|
||||
SW_SURFACE_OP_CONFIGURE,
|
||||
SW_SURFACE_OP_CONFIGURE_TRANSITION,
|
||||
SW_SURFACE_OP_DRAW_FILL,
|
||||
SW_SURFACE_OP_DRAW_BITMAP,
|
||||
SW_SURFACE_OP_SHOW,
|
||||
SURFACE_OP_FORMAT,
|
||||
CURSOR_OP_STATE,
|
||||
CURSOR_OP_IMAGE,
|
||||
CURSOR_OP_COLOR_TRANSFORM,
|
||||
CURSOR_OP_WHITE_LEVEL,
|
||||
SOURCE_OP_TRANSITION,
|
||||
}
|
||||
op;
|
||||
|
||||
@@ -41,7 +64,7 @@ typedef struct
|
||||
{
|
||||
int width, height;
|
||||
}
|
||||
swSurfaceConfigure;
|
||||
swSurfaceConfigureTransition;
|
||||
|
||||
struct
|
||||
{
|
||||
@@ -61,12 +84,6 @@ typedef struct
|
||||
}
|
||||
swSurfaceDrawBitmap;
|
||||
|
||||
struct
|
||||
{
|
||||
bool show;
|
||||
}
|
||||
swSurfaceShow;
|
||||
|
||||
struct
|
||||
{
|
||||
LG_RendererFormat format;
|
||||
@@ -93,6 +110,24 @@ typedef struct
|
||||
uint8_t * data;
|
||||
}
|
||||
cursorImage;
|
||||
|
||||
struct
|
||||
{
|
||||
LGColorTransform * data;
|
||||
}
|
||||
cursorColorTransform;
|
||||
|
||||
struct
|
||||
{
|
||||
uint32_t value;
|
||||
}
|
||||
cursorWhiteLevel;
|
||||
|
||||
struct
|
||||
{
|
||||
bool swSurface;
|
||||
}
|
||||
sourceTransition;
|
||||
};
|
||||
}
|
||||
RenderCommand;
|
||||
@@ -101,22 +136,48 @@ void renderQueue_init(void);
|
||||
void renderQueue_free(void);
|
||||
void renderQueue_clear(void);
|
||||
void renderQueue_process(void);
|
||||
void renderQueue_presented(void);
|
||||
|
||||
void renderQueue_swSurfaceConfigure(int width, int height);
|
||||
void renderQueue_setSourceFns(RenderQueueSourcePrepareFn prepare,
|
||||
RenderQueueSourceAppliedFn applied, void * opaque);
|
||||
|
||||
void renderQueue_swSurfaceDrawFill(int x, int y, int width, int height,
|
||||
/* Starting a source lifecycle makes commands from its prior generation stale.
|
||||
* After a successful render, call renderQueue_presented on the render thread
|
||||
* to acknowledge the newest applied transition. */
|
||||
uint64_t renderQueue_sourceBegin(RenderQueueSource source);
|
||||
void renderQueue_sourceInvalidate(RenderQueueSource source,
|
||||
uint64_t generation);
|
||||
void renderQueue_sourceClearCursor(RenderQueueSource source);
|
||||
uint64_t renderQueue_sourceTransition(RenderQueueSource source,
|
||||
uint64_t generation, bool swSurface,
|
||||
atomic_uint_least64_t * publishedSerial);
|
||||
|
||||
uint64_t renderQueue_sourceSwSurfaceConfigureTransition(
|
||||
RenderQueueSource source, uint64_t generation, int width, int height,
|
||||
atomic_uint_least64_t * publishedSerial);
|
||||
|
||||
void renderQueue_sourceSwSurfaceDrawFill(RenderQueueSource source,
|
||||
uint64_t generation, int x, int y, int width, int height,
|
||||
uint32_t color);
|
||||
|
||||
void renderQueue_swSurfaceDrawBitmap(int x, int y, int width, int height,
|
||||
int stride, const void * data, bool topDown);
|
||||
void renderQueue_sourceSwSurfaceDrawBitmap(RenderQueueSource source,
|
||||
uint64_t generation, int x, int y, int width, int height, int stride,
|
||||
const void * data, bool topDown);
|
||||
|
||||
void renderQueue_swSurfaceShow(bool show);
|
||||
|
||||
void renderQueue_surfaceFormat(const LG_RendererFormat format,
|
||||
void renderQueue_sourceSurfaceFormat(RenderQueueSource source,
|
||||
uint64_t generation, const LG_RendererFormat format,
|
||||
bool rendererSupportsNativeHDR);
|
||||
|
||||
void renderQueue_cursorState(bool visible, int x, int y, int hx, int hy);
|
||||
void renderQueue_sourceCursorState(RenderQueueSource source,
|
||||
uint64_t generation, bool visible, int x, int y, int hx, int hy);
|
||||
|
||||
void renderQueue_cursorImage(LG_RendererCursor type,
|
||||
int width, int height, int pitch,
|
||||
uint8_t * data);
|
||||
/* Cursor payloads are copied before these calls return. */
|
||||
void renderQueue_sourceCursorImage(RenderQueueSource source,
|
||||
uint64_t generation, LG_RendererCursor type,
|
||||
int width, int height, int pitch, const void * data);
|
||||
|
||||
void renderQueue_sourceCursorColorTransform(RenderQueueSource source,
|
||||
uint64_t generation, const LGColorTransform * transform);
|
||||
|
||||
void renderQueue_sourceCursorWhiteLevel(RenderQueueSource source,
|
||||
uint64_t generation, uint32_t sdrWhiteLevel);
|
||||
|
||||
@@ -49,7 +49,6 @@ struct LG_TransportFallback
|
||||
|
||||
LGThread * thread;
|
||||
LGEvent * wakeEvent;
|
||||
LGEvent * videoIdleEvent;
|
||||
LG_RWLock lock;
|
||||
|
||||
atomic_bool stop;
|
||||
@@ -66,7 +65,6 @@ struct LG_TransportFallback
|
||||
bool closing;
|
||||
bool videoRequested;
|
||||
bool videoActive;
|
||||
bool videoBusy;
|
||||
|
||||
bool primaryUUIDValid;
|
||||
uint8_t primaryUUID[16];
|
||||
@@ -76,6 +74,8 @@ struct LG_TransportFallback
|
||||
uint8_t mismatchFallback[16];
|
||||
};
|
||||
|
||||
static bool applyVideoRequest(LG_TransportFallback * fallback);
|
||||
|
||||
static bool connectCancelled(void * opaque)
|
||||
{
|
||||
const LG_TransportFallback * fallback = opaque;
|
||||
@@ -139,26 +139,18 @@ static void unpublishProviders(LG_TransportFallback * fallback, bool live)
|
||||
fallback->providersPublished = false;
|
||||
}
|
||||
|
||||
static void waitForVideoIdle(LG_TransportFallback * fallback)
|
||||
static void closeVideoAdmission(LG_TransportFallback * fallback)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
LG_LOCK_EXCLUSIVE(fallback->lock);
|
||||
atomic_store_explicit(&fallback->ready, false, memory_order_release);
|
||||
fallback->closing = true;
|
||||
const bool busy = fallback->videoBusy;
|
||||
LG_UNLOCK_EXCLUSIVE(fallback->lock);
|
||||
|
||||
if (!busy)
|
||||
return;
|
||||
lgWaitEvent(fallback->videoIdleEvent, TIMEOUT_INFINITE);
|
||||
}
|
||||
LG_LOCK_EXCLUSIVE(fallback->lock);
|
||||
atomic_store_explicit(&fallback->ready, false, memory_order_release);
|
||||
fallback->closing = true;
|
||||
LG_UNLOCK_EXCLUSIVE(fallback->lock);
|
||||
}
|
||||
|
||||
static bool cleanupConnection(LG_TransportFallback * fallback,
|
||||
bool knownDead)
|
||||
{
|
||||
waitForVideoIdle(fallback);
|
||||
closeVideoAdmission(fallback);
|
||||
|
||||
LG_LOCK_EXCLUSIVE(fallback->lock);
|
||||
const bool reportDisconnected = fallback->connectedReported;
|
||||
@@ -352,6 +344,7 @@ static bool connectFallback(LG_TransportFallback * fallback)
|
||||
while (!atomic_load_explicit(&fallback->stop, memory_order_acquire))
|
||||
{
|
||||
lgWaitEvent(fallback->wakeEvent, SESSION_POLL_MS);
|
||||
applyVideoRequest(fallback);
|
||||
|
||||
LG_LOCK_EXCLUSIVE(fallback->lock);
|
||||
const bool reject = uuidMismatchLocked(fallback);
|
||||
@@ -435,10 +428,6 @@ bool lgTransportFallback_start(const char * transportName,
|
||||
if (!fallback->wakeEvent)
|
||||
goto fail;
|
||||
|
||||
fallback->videoIdleEvent = lgCreateEvent(true, 0);
|
||||
if (!fallback->videoIdleEvent)
|
||||
goto fail;
|
||||
|
||||
*result = fallback;
|
||||
if (!lgCreateThread("transportFallback", fallbackThread,
|
||||
fallback, &fallback->thread))
|
||||
@@ -448,8 +437,6 @@ bool lgTransportFallback_start(const char * transportName,
|
||||
|
||||
fail:
|
||||
*result = NULL;
|
||||
if (fallback->videoIdleEvent)
|
||||
lgFreeEvent(fallback->videoIdleEvent);
|
||||
if (fallback->wakeEvent)
|
||||
lgFreeEvent(fallback->wakeEvent);
|
||||
LG_RWLOCK_FREE(fallback->lock);
|
||||
@@ -477,7 +464,6 @@ void lgTransportFallback_stop(LG_TransportFallback ** fallbackPtr)
|
||||
return;
|
||||
}
|
||||
|
||||
lgFreeEvent(fallback->videoIdleEvent);
|
||||
lgFreeEvent(fallback->wakeEvent);
|
||||
LG_RWLOCK_FREE(fallback->lock);
|
||||
free(fallback->transportName);
|
||||
@@ -490,20 +476,16 @@ bool lgTransportFallback_ready(const LG_TransportFallback * fallback)
|
||||
&fallback->ready, memory_order_acquire);
|
||||
}
|
||||
|
||||
bool lgTransportFallback_setVideoActive(
|
||||
LG_TransportFallback * fallback, bool active)
|
||||
static bool applyVideoRequest(LG_TransportFallback * fallback)
|
||||
{
|
||||
if (!fallback)
|
||||
return false;
|
||||
|
||||
uint64_t connectionSerial;
|
||||
LG_Transport * transport;
|
||||
const LG_SwSurfaceOps * surfaceOps;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
uint64_t connectionSerial;
|
||||
LG_Transport * transport;
|
||||
const LG_SwSurfaceOps * surfaceOps;
|
||||
bool requested;
|
||||
|
||||
LG_LOCK_EXCLUSIVE(fallback->lock);
|
||||
fallback->videoRequested = active;
|
||||
if (!atomic_load_explicit(&fallback->ready, memory_order_acquire) ||
|
||||
fallback->closing)
|
||||
{
|
||||
@@ -511,38 +493,44 @@ bool lgTransportFallback_setVideoActive(
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fallback->videoActive == active)
|
||||
requested = fallback->videoRequested;
|
||||
if (fallback->videoActive == requested)
|
||||
{
|
||||
LG_UNLOCK_EXCLUSIVE(fallback->lock);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!fallback->videoBusy)
|
||||
{
|
||||
fallback->videoBusy = true;
|
||||
connectionSerial = fallback->connectionSerial;
|
||||
transport = fallback->transport.handle;
|
||||
surfaceOps = fallback->videoOps->swSurface;
|
||||
LG_UNLOCK_EXCLUSIVE(fallback->lock);
|
||||
break;
|
||||
}
|
||||
|
||||
connectionSerial = fallback->connectionSerial;
|
||||
transport = fallback->transport.handle;
|
||||
surfaceOps = fallback->videoOps->swSurface;
|
||||
LG_UNLOCK_EXCLUSIVE(fallback->lock);
|
||||
lgWaitEvent(fallback->videoIdleEvent, TIMEOUT_INFINITE);
|
||||
}
|
||||
|
||||
lgResetEvent(fallback->videoIdleEvent);
|
||||
const bool result = surfaceOps->setActive(transport, active);
|
||||
const bool result = surfaceOps->setActive(transport, requested);
|
||||
|
||||
LG_LOCK_EXCLUSIVE(fallback->lock);
|
||||
const bool current = fallback->connectionSerial == connectionSerial;
|
||||
if (current && result)
|
||||
fallback->videoActive = requested;
|
||||
const bool accepted = current && !fallback->closing && result;
|
||||
const bool retry = current && !fallback->closing &&
|
||||
fallback->videoRequested != requested;
|
||||
LG_UNLOCK_EXCLUSIVE(fallback->lock);
|
||||
|
||||
if (!retry)
|
||||
return accepted;
|
||||
}
|
||||
}
|
||||
|
||||
void lgTransportFallback_requestVideoActive(
|
||||
LG_TransportFallback * fallback, bool active)
|
||||
{
|
||||
if (!fallback)
|
||||
return;
|
||||
|
||||
LG_LOCK_EXCLUSIVE(fallback->lock);
|
||||
const bool current = fallback->connectionSerial == connectionSerial;
|
||||
if (current && result)
|
||||
fallback->videoActive = active;
|
||||
const bool accepted = current && !fallback->closing && result;
|
||||
fallback->videoBusy = false;
|
||||
fallback->videoRequested = active;
|
||||
LG_UNLOCK_EXCLUSIVE(fallback->lock);
|
||||
lgSignalEvent(fallback->videoIdleEvent);
|
||||
return accepted;
|
||||
lgSignalEvent(fallback->wakeEvent);
|
||||
}
|
||||
|
||||
void lgTransportFallback_setPrimaryUUID(
|
||||
|
||||
@@ -46,7 +46,7 @@ void lgTransportFallback_stop(LG_TransportFallback ** fallback);
|
||||
|
||||
bool lgTransportFallback_ready(const LG_TransportFallback * fallback);
|
||||
/* The requested state is retained across reconnects. */
|
||||
bool lgTransportFallback_setVideoActive(
|
||||
void lgTransportFallback_requestVideoActive(
|
||||
LG_TransportFallback * fallback, bool active);
|
||||
|
||||
void lgTransportFallback_setPrimaryUUID(
|
||||
|
||||
Reference in New Issue
Block a user