mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-22 15:11:31 +00:00
[client] render: bound software surface update backlog
This commit is contained in:
@@ -1732,6 +1732,27 @@ static void videoSourceClearCursor(LG_VideoSource source)
|
|||||||
renderQueue_sourceClearCursor(renderQueueSource(source));
|
renderQueue_sourceClearCursor(renderQueueSource(source));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void videoSourceReject(void * opaque, RenderQueueSource queueSource,
|
||||||
|
uint64_t serial)
|
||||||
|
{
|
||||||
|
(void)opaque;
|
||||||
|
|
||||||
|
LG_VideoSource source = LG_VIDEO_SOURCE_NONE;
|
||||||
|
if (queueSource == RENDER_QUEUE_SOURCE_PRIMARY)
|
||||||
|
source = LG_VIDEO_SOURCE_PRIMARY;
|
||||||
|
else if (queueSource == RENDER_QUEUE_SOURCE_FALLBACK)
|
||||||
|
source = LG_VIDEO_SOURCE_FALLBACK;
|
||||||
|
|
||||||
|
if (source == LG_VIDEO_SOURCE_NONE)
|
||||||
|
return;
|
||||||
|
|
||||||
|
struct VideoSourceState * state = &g_state.videoSource[source];
|
||||||
|
if (atomic_compare_exchange_strong_explicit(&state->transitionSerial,
|
||||||
|
&serial, 0, memory_order_acq_rel, memory_order_relaxed))
|
||||||
|
atomic_store_explicit(
|
||||||
|
&state->transitionPending, false, memory_order_release);
|
||||||
|
}
|
||||||
|
|
||||||
static bool videoSourcePrepare(void * opaque, RenderQueueSource queueSource,
|
static bool videoSourcePrepare(void * opaque, RenderQueueSource queueSource,
|
||||||
uint64_t generation, uint64_t serial)
|
uint64_t generation, uint64_t serial)
|
||||||
{
|
{
|
||||||
@@ -1799,10 +1820,7 @@ static bool videoSourcePrepare(void * opaque, RenderQueueSource queueSource,
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
reject:
|
reject:
|
||||||
if (atomic_compare_exchange_strong_explicit(&state->transitionSerial,
|
videoSourceReject(opaque, queueSource, serial);
|
||||||
&serial, 0, memory_order_acq_rel, memory_order_relaxed))
|
|
||||||
atomic_store_explicit(
|
|
||||||
&state->transitionPending, false, memory_order_release);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1892,6 +1910,7 @@ static void swSurfaceConfigure(LG_VideoSource source,
|
|||||||
unsigned int width, unsigned int height)
|
unsigned int width, unsigned int height)
|
||||||
{
|
{
|
||||||
struct VideoSourceState * state = &g_state.videoSource[source];
|
struct VideoSourceState * state = &g_state.videoSource[source];
|
||||||
|
uint64_t generation;
|
||||||
LG_LOCK(g_state.videoSourceLock);
|
LG_LOCK(g_state.videoSourceLock);
|
||||||
if (!swSurfaceEventAdmitted(source))
|
if (!swSurfaceEventAdmitted(source))
|
||||||
{
|
{
|
||||||
@@ -1900,10 +1919,25 @@ static void swSurfaceConfigure(LG_VideoSource source,
|
|||||||
}
|
}
|
||||||
|
|
||||||
videoSourceBeginLocked(source);
|
videoSourceBeginLocked(source);
|
||||||
|
generation = atomic_load_explicit(
|
||||||
|
&state->generation, memory_order_acquire);
|
||||||
atomic_store_explicit(&state->width, width, memory_order_relaxed);
|
atomic_store_explicit(&state->width, width, memory_order_relaxed);
|
||||||
atomic_store_explicit(&state->height, height, memory_order_relaxed);
|
atomic_store_explicit(&state->height, height, memory_order_relaxed);
|
||||||
atomic_store_explicit(&state->rotate, LG_ROTATE_0, memory_order_release);
|
atomic_store_explicit(&state->rotate, LG_ROTATE_0, memory_order_release);
|
||||||
state->configurePending = true;
|
state->configurePending = true;
|
||||||
|
LG_UNLOCK(g_state.videoSourceLock);
|
||||||
|
|
||||||
|
if (!renderQueue_sourceSwSurfaceConfigure(renderQueueSource(source),
|
||||||
|
generation, width, height))
|
||||||
|
return;
|
||||||
|
|
||||||
|
LG_LOCK(g_state.videoSourceLock);
|
||||||
|
if (atomic_load_explicit(
|
||||||
|
&state->generation, memory_order_acquire) != generation)
|
||||||
|
{
|
||||||
|
LG_UNLOCK(g_state.videoSourceLock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
atomic_store_explicit(&state->ready, true, memory_order_release);
|
atomic_store_explicit(&state->ready, true, memory_order_release);
|
||||||
LG_UNLOCK(g_state.videoSourceLock);
|
LG_UNLOCK(g_state.videoSourceLock);
|
||||||
|
|
||||||
@@ -3002,7 +3036,8 @@ static int lg_run(void)
|
|||||||
LG_LOCK_INIT(g_state.videoSourceLock);
|
LG_LOCK_INIT(g_state.videoSourceLock);
|
||||||
LG_LOCK_INIT(g_state.videoSplashLock);
|
LG_LOCK_INIT(g_state.videoSplashLock);
|
||||||
renderQueue_init();
|
renderQueue_init();
|
||||||
renderQueue_setSourceFns(videoSourcePrepare, videoSourceApplied, NULL);
|
renderQueue_setSourceFns(
|
||||||
|
videoSourcePrepare, videoSourceReject, videoSourceApplied, NULL);
|
||||||
atomic_store_explicit(&g_state.videoSourceRequested,
|
atomic_store_explicit(&g_state.videoSourceRequested,
|
||||||
LG_VIDEO_SOURCE_PRIMARY, memory_order_relaxed);
|
LG_VIDEO_SOURCE_PRIMARY, memory_order_relaxed);
|
||||||
atomic_store_explicit(&g_state.videoSourceApplied,
|
atomic_store_explicit(&g_state.videoSourceApplied,
|
||||||
@@ -3552,7 +3587,7 @@ static void lg_shutdown(void)
|
|||||||
g_state.overlays = NULL;
|
g_state.overlays = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderQueue_setSourceFns(NULL, NULL, NULL);
|
renderQueue_setSourceFns(NULL, NULL, NULL, NULL);
|
||||||
renderQueue_free();
|
renderQueue_free();
|
||||||
LG_LOCK_FREE(g_state.videoSourceLock);
|
LG_LOCK_FREE(g_state.videoSourceLock);
|
||||||
LG_LOCK_FREE(g_state.videoSplashLock);
|
LG_LOCK_FREE(g_state.videoSplashLock);
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
#include "render_queue.h"
|
#include "render_queue.h"
|
||||||
|
|
||||||
|
#include <limits.h>
|
||||||
#include <stdatomic.h>
|
#include <stdatomic.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@@ -40,8 +41,7 @@ typedef struct RenderCommand
|
|||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
SW_SURFACE_OP_CONFIGURE_TRANSITION,
|
SW_SURFACE_OP_CONFIGURE_TRANSITION,
|
||||||
SW_SURFACE_OP_DRAW_FILL,
|
SW_SURFACE_OP_UPDATE,
|
||||||
SW_SURFACE_OP_DRAW_BITMAP,
|
|
||||||
SURFACE_OP_FORMAT,
|
SURFACE_OP_FORMAT,
|
||||||
CURSOR_OP_STATE,
|
CURSOR_OP_STATE,
|
||||||
CURSOR_OP_IMAGE,
|
CURSOR_OP_IMAGE,
|
||||||
@@ -56,26 +56,15 @@ typedef struct RenderCommand
|
|||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
int width, height;
|
int width, height;
|
||||||
|
uint64_t epoch;
|
||||||
}
|
}
|
||||||
swSurfaceConfigureTransition;
|
swSurfaceConfigureTransition;
|
||||||
|
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
int x, y;
|
uint64_t epoch;
|
||||||
int width, height;
|
|
||||||
uint32_t color;
|
|
||||||
}
|
}
|
||||||
swSurfaceDrawFill;
|
swSurfaceUpdate;
|
||||||
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
int x , y;
|
|
||||||
int width, height;
|
|
||||||
int stride;
|
|
||||||
uint8_t * data;
|
|
||||||
bool topDown;
|
|
||||||
}
|
|
||||||
swSurfaceDrawBitmap;
|
|
||||||
|
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
@@ -139,6 +128,44 @@ static LG_Lock l_renderQueueLock;
|
|||||||
static bool l_renderQueueInitialized;
|
static bool l_renderQueueInitialized;
|
||||||
static RenderQueueInvalidate l_renderQueueInvalidate;
|
static RenderQueueInvalidate l_renderQueueInvalidate;
|
||||||
|
|
||||||
|
typedef struct RenderQueueSwSurface
|
||||||
|
{
|
||||||
|
LG_Lock lock;
|
||||||
|
|
||||||
|
/* Transport bitmap storage is borrowed, so callbacks compose it into this
|
||||||
|
* persistent image before returning. The renderer consumes it synchronously
|
||||||
|
* while lock is held. */
|
||||||
|
uint64_t generation;
|
||||||
|
uint64_t epoch;
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
int pitch;
|
||||||
|
uint8_t * data;
|
||||||
|
|
||||||
|
bool damageFull;
|
||||||
|
int damageCount;
|
||||||
|
FrameDamageRect damage[LG_MAX_FRAME_DAMAGE_RECTS];
|
||||||
|
|
||||||
|
bool updateQueued;
|
||||||
|
RenderCommand updateCommand;
|
||||||
|
}
|
||||||
|
RenderQueueSwSurface;
|
||||||
|
|
||||||
|
static RenderQueueSwSurface l_swSurface[RENDER_QUEUE_SOURCE_COUNT];
|
||||||
|
static RenderQueueSource l_swSurfaceResidentSource;
|
||||||
|
static uint64_t l_swSurfaceResidentGeneration;
|
||||||
|
static uint64_t l_swSurfaceResidentEpoch;
|
||||||
|
|
||||||
|
static RenderQueueSwSurface * swSurfaceFromUpdateCommand(
|
||||||
|
const RenderCommand * cmd)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < RENDER_QUEUE_SOURCE_COUNT; ++i)
|
||||||
|
if (cmd == &l_swSurface[i].updateCommand)
|
||||||
|
return &l_swSurface[i];
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static bool l_showSwSurface;
|
static bool l_showSwSurface;
|
||||||
static bool l_surfaceFormatValid;
|
static bool l_surfaceFormatValid;
|
||||||
static bool l_rendererSupportsNativeHDR;
|
static bool l_rendererSupportsNativeHDR;
|
||||||
@@ -154,6 +181,7 @@ static uint64_t l_appliedGeneration;
|
|||||||
static bool l_appliedSwSurface;
|
static bool l_appliedSwSurface;
|
||||||
|
|
||||||
static RenderQueueSourcePrepareFn l_sourcePrepareFn;
|
static RenderQueueSourcePrepareFn l_sourcePrepareFn;
|
||||||
|
static RenderQueueSourceRejectFn l_sourceRejectFn;
|
||||||
static RenderQueueSourceAppliedFn l_sourceAppliedFn;
|
static RenderQueueSourceAppliedFn l_sourceAppliedFn;
|
||||||
static void * l_sourceCallbackOpaque;
|
static void * l_sourceCallbackOpaque;
|
||||||
|
|
||||||
@@ -242,8 +270,119 @@ static bool commandValid(const RenderCommand * cmd)
|
|||||||
return generationValid(cmd->source, cmd->generation);
|
return generationValid(cmd->source, cmd->generation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint64_t swSurfaceDamageArea(const FrameDamageRect * rect)
|
||||||
|
{
|
||||||
|
return (uint64_t)rect->width * rect->height;
|
||||||
|
}
|
||||||
|
|
||||||
|
static FrameDamageRect swSurfaceDamageUnion(
|
||||||
|
const FrameDamageRect * a, const FrameDamageRect * b)
|
||||||
|
{
|
||||||
|
const uint32_t left = min(a->x, b->x);
|
||||||
|
const uint32_t top = min(a->y, b->y);
|
||||||
|
const uint32_t right = max(a->x + a->width, b->x + b->width);
|
||||||
|
const uint32_t bottom = max(a->y + a->height, b->y + b->height);
|
||||||
|
|
||||||
|
return (FrameDamageRect)
|
||||||
|
{
|
||||||
|
.x = left,
|
||||||
|
.y = top,
|
||||||
|
.width = right - left,
|
||||||
|
.height = bottom - top,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool swSurfaceDamageTouches(
|
||||||
|
const FrameDamageRect * a, const FrameDamageRect * b)
|
||||||
|
{
|
||||||
|
return a->x <= b->x + b->width && b->x <= a->x + a->width &&
|
||||||
|
a->y <= b->y + b->height && b->y <= a->y + a->height;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void swSurfaceDamageReset(RenderQueueSwSurface * surface)
|
||||||
|
{
|
||||||
|
surface->damageFull = false;
|
||||||
|
surface->damageCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool swSurfaceDamagePending(const RenderQueueSwSurface * surface)
|
||||||
|
{
|
||||||
|
return surface->damageFull || surface->damageCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void swSurfaceDamageAdd(RenderQueueSwSurface * surface,
|
||||||
|
const FrameDamageRect * damage)
|
||||||
|
{
|
||||||
|
if (surface->damageFull)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (damage->x == 0 && damage->y == 0 &&
|
||||||
|
damage->width == (uint32_t)surface->width &&
|
||||||
|
damage->height == (uint32_t)surface->height)
|
||||||
|
{
|
||||||
|
surface->damageFull = true;
|
||||||
|
surface->damageCount = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FrameDamageRect rect = *damage;
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < surface->damageCount;)
|
||||||
|
{
|
||||||
|
if (!swSurfaceDamageTouches(&rect, &surface->damage[i]))
|
||||||
|
{
|
||||||
|
++i;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
rect = swSurfaceDamageUnion(&rect, &surface->damage[i]);
|
||||||
|
surface->damage[i] = surface->damage[--surface->damageCount];
|
||||||
|
i = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (surface->damageCount < LG_MAX_FRAME_DAMAGE_RECTS)
|
||||||
|
break;
|
||||||
|
|
||||||
|
int best = 0;
|
||||||
|
uint64_t bestCost = UINT64_MAX;
|
||||||
|
for (int i = 0; i < surface->damageCount; ++i)
|
||||||
|
{
|
||||||
|
const FrameDamageRect merged =
|
||||||
|
swSurfaceDamageUnion(&rect, &surface->damage[i]);
|
||||||
|
const uint64_t cost = swSurfaceDamageArea(&merged) -
|
||||||
|
swSurfaceDamageArea(&surface->damage[i]);
|
||||||
|
if (cost < bestCost)
|
||||||
|
{
|
||||||
|
best = i;
|
||||||
|
bestCost = cost;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rect = swSurfaceDamageUnion(&rect, &surface->damage[best]);
|
||||||
|
surface->damage[best] = surface->damage[--surface->damageCount];
|
||||||
|
}
|
||||||
|
|
||||||
|
surface->damage[surface->damageCount++] = rect;
|
||||||
|
|
||||||
|
uint64_t area = 0;
|
||||||
|
for (int i = 0; i < surface->damageCount; ++i)
|
||||||
|
area += swSurfaceDamageArea(&surface->damage[i]);
|
||||||
|
|
||||||
|
const uint64_t fullArea =
|
||||||
|
(uint64_t)surface->width * surface->height;
|
||||||
|
if (area >= fullArea - fullArea / 4)
|
||||||
|
{
|
||||||
|
surface->damageFull = true;
|
||||||
|
surface->damageCount = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void freeCommand(RenderCommand * cmd)
|
static void freeCommand(RenderCommand * cmd)
|
||||||
{
|
{
|
||||||
|
if (cmd->op == SW_SURFACE_OP_UPDATE)
|
||||||
|
return;
|
||||||
|
|
||||||
switch (cmd->op)
|
switch (cmd->op)
|
||||||
{
|
{
|
||||||
case CURSOR_OP_IMAGE:
|
case CURSOR_OP_IMAGE:
|
||||||
@@ -283,6 +422,23 @@ static bool queueCommand(RenderCommand * cmd,
|
|||||||
return wake;
|
return wake;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* The surface lock must be held while its embedded command is queued. */
|
||||||
|
static bool queueSwSurfaceUpdate(RenderQueueSource source,
|
||||||
|
RenderQueueSwSurface * surface)
|
||||||
|
{
|
||||||
|
if (surface->updateQueued || !surface->data ||
|
||||||
|
!swSurfaceDamagePending(surface))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
RenderCommand * cmd = &surface->updateCommand;
|
||||||
|
cmd->source = source;
|
||||||
|
cmd->generation = surface->generation;
|
||||||
|
cmd->op = SW_SURFACE_OP_UPDATE;
|
||||||
|
cmd->swSurfaceUpdate.epoch = surface->epoch;
|
||||||
|
surface->updateQueued = true;
|
||||||
|
return queueCommand(cmd, RENDER_QUEUE_INVALIDATE_PARTIAL);
|
||||||
|
}
|
||||||
|
|
||||||
static void wakeQueue(RenderQueueInvalidate invalidate, bool wake)
|
static void wakeQueue(RenderQueueInvalidate invalidate, bool wake)
|
||||||
{
|
{
|
||||||
if (wake)
|
if (wake)
|
||||||
@@ -391,16 +547,26 @@ void renderQueue_init(void)
|
|||||||
l_appliedSource = RENDER_QUEUE_SOURCE_NONE;
|
l_appliedSource = RENDER_QUEUE_SOURCE_NONE;
|
||||||
l_appliedGeneration = 0;
|
l_appliedGeneration = 0;
|
||||||
l_appliedSwSurface = false;
|
l_appliedSwSurface = false;
|
||||||
|
|
||||||
|
l_swSurfaceResidentSource = RENDER_QUEUE_SOURCE_NONE;
|
||||||
|
l_swSurfaceResidentGeneration = 0;
|
||||||
|
l_swSurfaceResidentEpoch = 0;
|
||||||
|
|
||||||
l_sourcePrepareFn = NULL;
|
l_sourcePrepareFn = NULL;
|
||||||
|
l_sourceRejectFn = NULL;
|
||||||
l_sourceAppliedFn = NULL;
|
l_sourceAppliedFn = NULL;
|
||||||
l_sourceCallbackOpaque = NULL;
|
l_sourceCallbackOpaque = NULL;
|
||||||
l_pendingTransition = (RenderQueueTransition) {};
|
l_pendingTransition = (RenderQueueTransition) {};
|
||||||
memset(&l_surfaceFormat, 0, sizeof(l_surfaceFormat));
|
memset(&l_surfaceFormat, 0, sizeof(l_surfaceFormat));
|
||||||
memset(l_cursor, 0, sizeof(l_cursor));
|
memset(l_cursor, 0, sizeof(l_cursor));
|
||||||
memset(l_format, 0, sizeof(l_format));
|
memset(l_format, 0, sizeof(l_format));
|
||||||
|
memset(l_swSurface, 0, sizeof(l_swSurface));
|
||||||
|
|
||||||
for (int i = 0; i < RENDER_QUEUE_SOURCE_COUNT; ++i)
|
for (int i = 0; i < RENDER_QUEUE_SOURCE_COUNT; ++i)
|
||||||
|
{
|
||||||
atomic_store_explicit(&l_sourceGeneration[i], 0, memory_order_relaxed);
|
atomic_store_explicit(&l_sourceGeneration[i], 0, memory_order_relaxed);
|
||||||
|
LG_LOCK_INIT(l_swSurface[i].lock);
|
||||||
|
}
|
||||||
atomic_store_explicit(&l_transitionSerial, 0, memory_order_relaxed);
|
atomic_store_explicit(&l_transitionSerial, 0, memory_order_relaxed);
|
||||||
LG_LOCK_INIT(l_renderQueueLock);
|
LG_LOCK_INIT(l_renderQueueLock);
|
||||||
LG_LOCK_INIT(l_sourceLock);
|
LG_LOCK_INIT(l_sourceLock);
|
||||||
@@ -419,6 +585,9 @@ void renderQueue_free(void)
|
|||||||
{
|
{
|
||||||
free(l_cursor[i].data);
|
free(l_cursor[i].data);
|
||||||
l_cursor[i].data = NULL;
|
l_cursor[i].data = NULL;
|
||||||
|
free(l_swSurface[i].data);
|
||||||
|
l_swSurface[i].data = NULL;
|
||||||
|
LG_LOCK_FREE(l_swSurface[i].lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
l_renderQueueInitialized = false;
|
l_renderQueueInitialized = false;
|
||||||
@@ -433,15 +602,25 @@ void renderQueue_clear(void)
|
|||||||
while(cmd)
|
while(cmd)
|
||||||
{
|
{
|
||||||
RenderCommand * next = cmd->next;
|
RenderCommand * next = cmd->next;
|
||||||
|
RenderQueueSwSurface * surface = swSurfaceFromUpdateCommand(cmd);
|
||||||
|
if (surface)
|
||||||
|
{
|
||||||
|
LG_LOCK(surface->lock);
|
||||||
|
surface->updateQueued = false;
|
||||||
|
LG_UNLOCK(surface->lock);
|
||||||
|
}
|
||||||
|
else
|
||||||
freeCommand(cmd);
|
freeCommand(cmd);
|
||||||
cmd = next;
|
cmd = next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void renderQueue_setSourceFns(RenderQueueSourcePrepareFn prepare,
|
void renderQueue_setSourceFns(RenderQueueSourcePrepareFn prepare,
|
||||||
|
RenderQueueSourceRejectFn reject,
|
||||||
RenderQueueSourceAppliedFn applied, void * opaque)
|
RenderQueueSourceAppliedFn applied, void * opaque)
|
||||||
{
|
{
|
||||||
l_sourcePrepareFn = prepare;
|
l_sourcePrepareFn = prepare;
|
||||||
|
l_sourceRejectFn = reject;
|
||||||
l_sourceAppliedFn = applied;
|
l_sourceAppliedFn = applied;
|
||||||
l_sourceCallbackOpaque = opaque;
|
l_sourceCallbackOpaque = opaque;
|
||||||
}
|
}
|
||||||
@@ -452,6 +631,7 @@ uint64_t renderQueue_sourceBegin(RenderQueueSource source)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
LG_LOCK(l_sourceLock);
|
LG_LOCK(l_sourceLock);
|
||||||
|
LG_LOCK(l_swSurface[source].lock);
|
||||||
const uint64_t previous = atomic_load_explicit(
|
const uint64_t previous = atomic_load_explicit(
|
||||||
&l_sourceGeneration[source], memory_order_relaxed);
|
&l_sourceGeneration[source], memory_order_relaxed);
|
||||||
const uint64_t generation = previous + 1;
|
const uint64_t generation = previous + 1;
|
||||||
@@ -467,6 +647,7 @@ uint64_t renderQueue_sourceBegin(RenderQueueSource source)
|
|||||||
cursor->colorGeneration = generation;
|
cursor->colorGeneration = generation;
|
||||||
if (cursor->whiteValid && cursor->whiteGeneration == previous)
|
if (cursor->whiteValid && cursor->whiteGeneration == previous)
|
||||||
cursor->whiteGeneration = generation;
|
cursor->whiteGeneration = generation;
|
||||||
|
LG_UNLOCK(l_swSurface[source].lock);
|
||||||
LG_UNLOCK(l_sourceLock);
|
LG_UNLOCK(l_sourceLock);
|
||||||
return generation;
|
return generation;
|
||||||
}
|
}
|
||||||
@@ -477,6 +658,7 @@ void renderQueue_sourceInvalidate(RenderQueueSource source,
|
|||||||
if (!sourceValid(source) || !generation)
|
if (!sourceValid(source) || !generation)
|
||||||
return;
|
return;
|
||||||
LG_LOCK(l_sourceLock);
|
LG_LOCK(l_sourceLock);
|
||||||
|
LG_LOCK(l_swSurface[source].lock);
|
||||||
const uint64_t invalidGeneration = generation + 1;
|
const uint64_t invalidGeneration = generation + 1;
|
||||||
if (atomic_compare_exchange_strong_explicit(&l_sourceGeneration[source],
|
if (atomic_compare_exchange_strong_explicit(&l_sourceGeneration[source],
|
||||||
&generation, invalidGeneration, memory_order_acq_rel,
|
&generation, invalidGeneration, memory_order_acq_rel,
|
||||||
@@ -492,6 +674,7 @@ void renderQueue_sourceInvalidate(RenderQueueSource source,
|
|||||||
if (cursor->whiteValid && cursor->whiteGeneration == generation)
|
if (cursor->whiteValid && cursor->whiteGeneration == generation)
|
||||||
cursor->whiteGeneration = invalidGeneration;
|
cursor->whiteGeneration = invalidGeneration;
|
||||||
}
|
}
|
||||||
|
LG_UNLOCK(l_swSurface[source].lock);
|
||||||
LG_UNLOCK(l_sourceLock);
|
LG_UNLOCK(l_sourceLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -506,6 +689,99 @@ void renderQueue_sourceClearCursor(RenderQueueSource source)
|
|||||||
LG_UNLOCK(l_sourceLock);
|
LG_UNLOCK(l_sourceLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool configureSwSurface(RenderQueueSource source,
|
||||||
|
uint64_t generation, int width, int height, uint64_t * epoch)
|
||||||
|
{
|
||||||
|
if (width <= 0 || height <= 0 || width > INT_MAX / 4)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Invalid software surface dimensions: %dx%d", width, height);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int pitch = width * 4;
|
||||||
|
if ((size_t)height > SIZE_MAX / (size_t)pitch)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Software surface size overflows: %dx%d", width, height);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
RenderQueueSwSurface * surface = &l_swSurface[source];
|
||||||
|
LG_LOCK(surface->lock);
|
||||||
|
if (generationValid(source, generation) && surface->data &&
|
||||||
|
surface->generation == generation &&
|
||||||
|
surface->width == width && surface->height == height)
|
||||||
|
{
|
||||||
|
*epoch = surface->epoch;
|
||||||
|
LG_UNLOCK(surface->lock);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
LG_UNLOCK(surface->lock);
|
||||||
|
|
||||||
|
uint8_t * data = calloc((size_t)height, (size_t)pitch);
|
||||||
|
if (!data)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Failed to allocate software surface: %dx%d", width, height);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LG_LOCK(surface->lock);
|
||||||
|
if (!generationValid(source, generation))
|
||||||
|
{
|
||||||
|
LG_UNLOCK(surface->lock);
|
||||||
|
free(data);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (surface->data && surface->generation == generation &&
|
||||||
|
surface->width == width && surface->height == height)
|
||||||
|
{
|
||||||
|
*epoch = surface->epoch;
|
||||||
|
LG_UNLOCK(surface->lock);
|
||||||
|
free(data);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t nextEpoch = surface->epoch + 1;
|
||||||
|
if (!nextEpoch)
|
||||||
|
++nextEpoch;
|
||||||
|
|
||||||
|
free(surface->data);
|
||||||
|
surface->generation = generation;
|
||||||
|
surface->epoch = nextEpoch;
|
||||||
|
surface->width = width;
|
||||||
|
surface->height = height;
|
||||||
|
surface->pitch = pitch;
|
||||||
|
surface->data = data;
|
||||||
|
swSurfaceDamageReset(surface);
|
||||||
|
*epoch = nextEpoch;
|
||||||
|
LG_UNLOCK(surface->lock);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool renderQueue_sourceSwSurfaceConfigure(RenderQueueSource source,
|
||||||
|
uint64_t generation, int width, int height)
|
||||||
|
{
|
||||||
|
if (!generationValid(source, generation))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
uint64_t epoch;
|
||||||
|
return configureSwSurface(source, generation, width, height, &epoch);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool getSwSurfaceConfiguration(RenderQueueSource source,
|
||||||
|
uint64_t generation, int width, int height, uint64_t * epoch)
|
||||||
|
{
|
||||||
|
RenderQueueSwSurface * surface = &l_swSurface[source];
|
||||||
|
LG_LOCK(surface->lock);
|
||||||
|
const bool valid = generationValid(source, generation) && surface->data &&
|
||||||
|
surface->generation == generation &&
|
||||||
|
surface->width == width && surface->height == height;
|
||||||
|
if (valid)
|
||||||
|
*epoch = surface->epoch;
|
||||||
|
LG_UNLOCK(surface->lock);
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
|
|
||||||
static uint64_t enqueueTransition(RenderCommand * cmd,
|
static uint64_t enqueueTransition(RenderCommand * cmd,
|
||||||
atomic_uint_least64_t * publishedSerial)
|
atomic_uint_least64_t * publishedSerial)
|
||||||
{
|
{
|
||||||
@@ -558,33 +834,86 @@ uint64_t renderQueue_sourceSwSurfaceConfigureTransition(
|
|||||||
if (!cmd)
|
if (!cmd)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
uint64_t epoch;
|
||||||
|
if (!getSwSurfaceConfiguration(
|
||||||
|
source, generation, width, height, &epoch))
|
||||||
|
{
|
||||||
|
free(cmd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
setCommandSource(cmd, source, generation);
|
setCommandSource(cmd, source, generation);
|
||||||
cmd->op =
|
cmd->op =
|
||||||
SW_SURFACE_OP_CONFIGURE_TRANSITION;
|
SW_SURFACE_OP_CONFIGURE_TRANSITION;
|
||||||
cmd->swSurfaceConfigureTransition.width = width;
|
cmd->swSurfaceConfigureTransition.width = width;
|
||||||
cmd->swSurfaceConfigureTransition.height = height;
|
cmd->swSurfaceConfigureTransition.height = height;
|
||||||
|
cmd->swSurfaceConfigureTransition.epoch = epoch;
|
||||||
return enqueueTransition(cmd, publishedSerial);
|
return enqueueTransition(cmd, publishedSerial);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool clipSwSurfaceRect(const RenderQueueSwSurface * surface,
|
||||||
|
int * x, int * y, int * width, int * height,
|
||||||
|
int * sourceX, int * sourceY)
|
||||||
|
{
|
||||||
|
const int64_t left = max((int64_t)*x, 0);
|
||||||
|
const int64_t top = max((int64_t)*y, 0);
|
||||||
|
const int64_t right = min((int64_t)*x + *width,
|
||||||
|
(int64_t)surface->width);
|
||||||
|
const int64_t bottom = min((int64_t)*y + *height,
|
||||||
|
(int64_t)surface->height);
|
||||||
|
if (right <= left || bottom <= top)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (sourceX)
|
||||||
|
*sourceX = (int)(left - *x);
|
||||||
|
if (sourceY)
|
||||||
|
*sourceY = (int)(top - *y);
|
||||||
|
*x = (int)left;
|
||||||
|
*y = (int)top;
|
||||||
|
*width = (int)(right - left);
|
||||||
|
*height = (int)(bottom - top);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void renderQueue_sourceSwSurfaceDrawFill(RenderQueueSource source,
|
void renderQueue_sourceSwSurfaceDrawFill(RenderQueueSource source,
|
||||||
uint64_t generation, int x, int y, int width, int height,
|
uint64_t generation, int x, int y, int width, int height,
|
||||||
uint32_t color)
|
uint32_t color)
|
||||||
{
|
{
|
||||||
if (!generationValid(source, generation))
|
if (!generationValid(source, generation) || width <= 0 || height <= 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
RenderCommand * cmd = malloc(sizeof(*cmd));
|
RenderQueueSwSurface * surface = &l_swSurface[source];
|
||||||
if (!cmd)
|
LG_LOCK(surface->lock);
|
||||||
|
if (surface->generation != generation ||
|
||||||
|
!generationValid(source, generation) ||
|
||||||
|
!clipSwSurfaceRect(surface,
|
||||||
|
&x, &y, &width, &height, NULL, NULL))
|
||||||
|
{
|
||||||
|
LG_UNLOCK(surface->lock);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
setCommandSource(cmd, source, generation);
|
uint8_t * row = surface->data +
|
||||||
cmd->op = SW_SURFACE_OP_DRAW_FILL;
|
(size_t)y * surface->pitch + (size_t)x * 4;
|
||||||
cmd->swSurfaceDrawFill.x = x;
|
for (int dy = 0; dy < height; ++dy)
|
||||||
cmd->swSurfaceDrawFill.y = y;
|
{
|
||||||
cmd->swSurfaceDrawFill.width = width;
|
uint32_t * dst = (uint32_t *)row;
|
||||||
cmd->swSurfaceDrawFill.height = height;
|
for (int dx = 0; dx < width; ++dx)
|
||||||
cmd->swSurfaceDrawFill.color = color;
|
dst[dx] = color;
|
||||||
enqueueCommand(cmd, RENDER_QUEUE_INVALIDATE_PARTIAL);
|
row += surface->pitch;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FrameDamageRect damage =
|
||||||
|
{
|
||||||
|
.x = (uint32_t)x,
|
||||||
|
.y = (uint32_t)y,
|
||||||
|
.width = (uint32_t)width,
|
||||||
|
.height = (uint32_t)height,
|
||||||
|
};
|
||||||
|
swSurfaceDamageAdd(surface, &damage);
|
||||||
|
const bool wake = queueSwSurfaceUpdate(source, surface);
|
||||||
|
LG_UNLOCK(surface->lock);
|
||||||
|
wakeQueue(RENDER_QUEUE_INVALIDATE_PARTIAL, wake);
|
||||||
}
|
}
|
||||||
|
|
||||||
void renderQueue_sourceSwSurfaceDrawBitmap(RenderQueueSource source,
|
void renderQueue_sourceSwSurfaceDrawBitmap(RenderQueueSource source,
|
||||||
@@ -616,41 +945,51 @@ void renderQueue_sourceSwSurfaceDrawBitmap(RenderQueueSource source,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const size_t rowSize = (size_t)width * 4;
|
if ((size_t)height > SIZE_MAX / (size_t)stride)
|
||||||
if ((size_t)height > (SIZE_MAX - sizeof(RenderCommand)) / rowSize)
|
|
||||||
{
|
{
|
||||||
DEBUG_ERROR("Software surface bitmap size overflows: "
|
DEBUG_ERROR("Software surface bitmap size overflows: "
|
||||||
"width: %d, height: %d", width, height);
|
"height: %d, stride: %d", height, stride);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const size_t size = (size_t)height * rowSize;
|
RenderQueueSwSurface * surface = &l_swSurface[source];
|
||||||
RenderCommand * cmd = malloc(sizeof(*cmd) + size);
|
LG_LOCK(surface->lock);
|
||||||
if (!cmd)
|
int sourceX;
|
||||||
|
int sourceY;
|
||||||
|
const int sourceHeight = height;
|
||||||
|
if (surface->generation != generation ||
|
||||||
|
!generationValid(source, generation) ||
|
||||||
|
!clipSwSurfaceRect(surface,
|
||||||
|
&x, &y, &width, &height, &sourceX, &sourceY))
|
||||||
{
|
{
|
||||||
DEBUG_ERROR("Failed to allocate %zu bytes for software surface bitmap",
|
LG_UNLOCK(surface->lock);
|
||||||
size);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t * copy = (uint8_t *)(cmd + 1);
|
const size_t copySize = (size_t)width * 4;
|
||||||
if ((size_t)stride == rowSize)
|
uint8_t * dst = surface->data +
|
||||||
memcpy(copy, data, size);
|
(size_t)y * surface->pitch + (size_t)x * 4;
|
||||||
else
|
for (int dy = 0; dy < height; ++dy)
|
||||||
for(int row = 0; row < height; ++row)
|
{
|
||||||
memcpy(copy + (size_t)row * rowSize,
|
const int sourceRow = topDown ?
|
||||||
(const uint8_t *)data + (size_t)row * stride, rowSize);
|
sourceY + dy : sourceHeight - sourceY - dy - 1;
|
||||||
|
const uint8_t * src = (const uint8_t *)data +
|
||||||
|
(size_t)sourceRow * stride + (size_t)sourceX * 4;
|
||||||
|
memcpy(dst, src, copySize);
|
||||||
|
dst += surface->pitch;
|
||||||
|
}
|
||||||
|
|
||||||
setCommandSource(cmd, source, generation);
|
const FrameDamageRect damage =
|
||||||
cmd->op = SW_SURFACE_OP_DRAW_BITMAP;
|
{
|
||||||
cmd->swSurfaceDrawBitmap.x = x;
|
.x = (uint32_t)x,
|
||||||
cmd->swSurfaceDrawBitmap.y = y;
|
.y = (uint32_t)y,
|
||||||
cmd->swSurfaceDrawBitmap.width = width;
|
.width = (uint32_t)width,
|
||||||
cmd->swSurfaceDrawBitmap.height = height;
|
.height = (uint32_t)height,
|
||||||
cmd->swSurfaceDrawBitmap.stride = (int)rowSize;
|
};
|
||||||
cmd->swSurfaceDrawBitmap.data = copy;
|
swSurfaceDamageAdd(surface, &damage);
|
||||||
cmd->swSurfaceDrawBitmap.topDown = topDown;
|
const bool wake = queueSwSurfaceUpdate(source, surface);
|
||||||
enqueueCommand(cmd, RENDER_QUEUE_INVALIDATE_PARTIAL);
|
LG_UNLOCK(surface->lock);
|
||||||
|
wakeQueue(RENDER_QUEUE_INVALIDATE_PARTIAL, wake);
|
||||||
}
|
}
|
||||||
|
|
||||||
void renderQueue_sourceSurfaceFormat(RenderQueueSource source,
|
void renderQueue_sourceSurfaceFormat(RenderQueueSource source,
|
||||||
@@ -756,6 +1095,135 @@ void renderQueue_sourceCursorWhiteLevel(RenderQueueSource source,
|
|||||||
enqueueCommand(cmd, RENDER_QUEUE_INVALIDATE_NONE);
|
enqueueCommand(cmd, RENDER_QUEUE_INVALIDATE_NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool swSurfaceCommandMatches(const RenderQueueSwSurface * surface,
|
||||||
|
const RenderCommand * cmd, uint64_t epoch)
|
||||||
|
{
|
||||||
|
return surface->data && surface->generation == cmd->generation &&
|
||||||
|
surface->epoch == epoch;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void uploadSwSurfaceFull(RenderQueueSource source,
|
||||||
|
RenderQueueSwSurface * surface)
|
||||||
|
{
|
||||||
|
RENDERER(swSurfaceConfigure, surface->width, surface->height);
|
||||||
|
RENDERER(swSurfaceDrawBitmap,
|
||||||
|
0, 0, surface->width, surface->height,
|
||||||
|
surface->pitch, surface->data, true);
|
||||||
|
swSurfaceDamageReset(surface);
|
||||||
|
l_swSurfaceResidentSource = source;
|
||||||
|
l_swSurfaceResidentGeneration = surface->generation;
|
||||||
|
l_swSurfaceResidentEpoch = surface->epoch;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool swSurfaceResident(RenderQueueSource source,
|
||||||
|
const RenderQueueSwSurface * surface)
|
||||||
|
{
|
||||||
|
return l_swSurfaceResidentSource == source &&
|
||||||
|
l_swSurfaceResidentGeneration == surface->generation &&
|
||||||
|
l_swSurfaceResidentEpoch == surface->epoch;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void uploadSwSurfaceDamage(RenderQueueSource source,
|
||||||
|
RenderQueueSwSurface * surface)
|
||||||
|
{
|
||||||
|
if (!swSurfaceResident(source, surface))
|
||||||
|
{
|
||||||
|
uploadSwSurfaceFull(source, surface);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (surface->damageFull)
|
||||||
|
RENDERER(swSurfaceDrawBitmap,
|
||||||
|
0, 0, surface->width, surface->height,
|
||||||
|
surface->pitch, surface->data, true);
|
||||||
|
else
|
||||||
|
for (int i = 0; i < surface->damageCount; ++i)
|
||||||
|
{
|
||||||
|
const FrameDamageRect * damage = &surface->damage[i];
|
||||||
|
uint8_t * data = surface->data +
|
||||||
|
(size_t)damage->y * surface->pitch + (size_t)damage->x * 4;
|
||||||
|
RENDERER(swSurfaceDrawBitmap,
|
||||||
|
damage->x, damage->y, damage->width, damage->height,
|
||||||
|
surface->pitch, data, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
swSurfaceDamageReset(surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool swSurfaceTransitionMatches(
|
||||||
|
const RenderQueueSwSurface * surface, const RenderCommand * cmd,
|
||||||
|
bool configure)
|
||||||
|
{
|
||||||
|
const uint64_t epoch = configure ?
|
||||||
|
cmd->swSurfaceConfigureTransition.epoch : surface->epoch;
|
||||||
|
return swSurfaceCommandMatches(surface, cmd, epoch) &&
|
||||||
|
(!configure ||
|
||||||
|
(surface->width == cmd->swSurfaceConfigureTransition.width &&
|
||||||
|
surface->height == cmd->swSurfaceConfigureTransition.height));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool prepareSwSurfaceTransition(const RenderCommand * cmd,
|
||||||
|
bool configure)
|
||||||
|
{
|
||||||
|
RenderQueueSwSurface * surface = &l_swSurface[cmd->source];
|
||||||
|
LG_LOCK(surface->lock);
|
||||||
|
|
||||||
|
const bool valid = swSurfaceTransitionMatches(surface, cmd, configure);
|
||||||
|
if (valid)
|
||||||
|
{
|
||||||
|
if (configure || !swSurfaceResident(cmd->source, surface))
|
||||||
|
uploadSwSurfaceFull(cmd->source, surface);
|
||||||
|
else
|
||||||
|
uploadSwSurfaceDamage(cmd->source, surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
LG_UNLOCK(surface->lock);
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool swSurfaceTransitionReady(const RenderCommand * cmd,
|
||||||
|
bool configure)
|
||||||
|
{
|
||||||
|
RenderQueueSwSurface * surface = &l_swSurface[cmd->source];
|
||||||
|
LG_LOCK(surface->lock);
|
||||||
|
const bool ready = swSurfaceTransitionMatches(surface, cmd, configure);
|
||||||
|
LG_UNLOCK(surface->lock);
|
||||||
|
return ready;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Called with l_sourceLock held. The marker may be recycled into the live
|
||||||
|
* queue after an older generation was detached from it. */
|
||||||
|
static bool processSwSurfaceUpdate(RenderCommand * cmd, bool commandValid)
|
||||||
|
{
|
||||||
|
RenderQueueSwSurface * surface = &l_swSurface[cmd->source];
|
||||||
|
LG_LOCK(surface->lock);
|
||||||
|
|
||||||
|
if (!surface->updateQueued)
|
||||||
|
{
|
||||||
|
LG_UNLOCK(surface->lock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
surface->updateQueued = false;
|
||||||
|
const bool matches = commandValid &&
|
||||||
|
swSurfaceCommandMatches(surface, cmd, cmd->swSurfaceUpdate.epoch);
|
||||||
|
const bool active = l_appliedSwSurface &&
|
||||||
|
l_appliedSource == cmd->source &&
|
||||||
|
l_appliedGeneration == cmd->generation;
|
||||||
|
if (matches && active)
|
||||||
|
uploadSwSurfaceDamage(cmd->source, surface);
|
||||||
|
|
||||||
|
bool wake = false;
|
||||||
|
if (swSurfaceDamagePending(surface) &&
|
||||||
|
l_appliedSwSurface && l_appliedSource == cmd->source &&
|
||||||
|
l_appliedGeneration == surface->generation &&
|
||||||
|
generationValid(cmd->source, surface->generation))
|
||||||
|
wake = queueSwSurfaceUpdate(cmd->source, surface);
|
||||||
|
|
||||||
|
LG_UNLOCK(surface->lock);
|
||||||
|
return wake;
|
||||||
|
}
|
||||||
|
|
||||||
static void applyCursor(RenderQueueSource source, uint64_t generation)
|
static void applyCursor(RenderQueueSource source, uint64_t generation)
|
||||||
{
|
{
|
||||||
RenderQueueCursor * cursor = &l_cursor[source];
|
RenderQueueCursor * cursor = &l_cursor[source];
|
||||||
@@ -828,6 +1296,13 @@ static bool prepareSourceTransition(const RenderCommand * cmd)
|
|||||||
cmd->source, cmd->generation, cmd->transitionSerial);
|
cmd->source, cmd->generation, cmd->transitionSerial);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void rejectSourceTransition(const RenderCommand * cmd)
|
||||||
|
{
|
||||||
|
if (l_sourceRejectFn)
|
||||||
|
l_sourceRejectFn(l_sourceCallbackOpaque,
|
||||||
|
cmd->source, cmd->transitionSerial);
|
||||||
|
}
|
||||||
|
|
||||||
void renderQueue_process(void)
|
void renderQueue_process(void)
|
||||||
{
|
{
|
||||||
RenderCommand * cmd = detachCommands();
|
RenderCommand * cmd = detachCommands();
|
||||||
@@ -835,16 +1310,21 @@ void renderQueue_process(void)
|
|||||||
{
|
{
|
||||||
RenderCommand * next = cmd->next;
|
RenderCommand * next = cmd->next;
|
||||||
const bool transition = transitionCommand(cmd);
|
const bool transition = transitionCommand(cmd);
|
||||||
|
bool wake = false;
|
||||||
if (transition)
|
if (transition)
|
||||||
LG_LOCK(l_transitionLock);
|
LG_LOCK(l_transitionLock);
|
||||||
LG_LOCK(l_sourceLock);
|
LG_LOCK(l_sourceLock);
|
||||||
|
|
||||||
if (!commandValid(cmd))
|
const bool validCommand = commandValid(cmd);
|
||||||
|
if (!validCommand)
|
||||||
{
|
{
|
||||||
|
if (cmd->op == SW_SURFACE_OP_UPDATE)
|
||||||
|
wake = processSwSurfaceUpdate(cmd, false);
|
||||||
LG_UNLOCK(l_sourceLock);
|
LG_UNLOCK(l_sourceLock);
|
||||||
if (transition)
|
if (transition)
|
||||||
LG_UNLOCK(l_transitionLock);
|
LG_UNLOCK(l_transitionLock);
|
||||||
freeCommand(cmd);
|
freeCommand(cmd);
|
||||||
|
wakeQueue(RENDER_QUEUE_INVALIDATE_PARTIAL, wake);
|
||||||
cmd = next;
|
cmd = next;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -852,30 +1332,24 @@ void renderQueue_process(void)
|
|||||||
switch(cmd->op)
|
switch(cmd->op)
|
||||||
{
|
{
|
||||||
case SW_SURFACE_OP_CONFIGURE_TRANSITION:
|
case SW_SURFACE_OP_CONFIGURE_TRANSITION:
|
||||||
|
if (!swSurfaceTransitionReady(cmd, true))
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Software surface configuration is unavailable");
|
||||||
|
rejectSourceTransition(cmd);
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (!prepareSourceTransition(cmd))
|
if (!prepareSourceTransition(cmd))
|
||||||
break;
|
break;
|
||||||
RENDERER(swSurfaceConfigure,
|
if (!prepareSwSurfaceTransition(cmd, true))
|
||||||
cmd->swSurfaceConfigureTransition.width,
|
{
|
||||||
cmd->swSurfaceConfigureTransition.height);
|
rejectSourceTransition(cmd);
|
||||||
RENDERER(swSurfaceDrawFill, 0, 0,
|
break;
|
||||||
cmd->swSurfaceConfigureTransition.width,
|
}
|
||||||
cmd->swSurfaceConfigureTransition.height, 0);
|
|
||||||
applySourceTransition(cmd, true);
|
applySourceTransition(cmd, true);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SW_SURFACE_OP_DRAW_FILL:
|
case SW_SURFACE_OP_UPDATE:
|
||||||
RENDERER(swSurfaceDrawFill,
|
wake = processSwSurfaceUpdate(cmd, true);
|
||||||
cmd->swSurfaceDrawFill.x , cmd->swSurfaceDrawFill.y,
|
|
||||||
cmd->swSurfaceDrawFill.width, cmd->swSurfaceDrawFill.height,
|
|
||||||
cmd->swSurfaceDrawFill.color);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SW_SURFACE_OP_DRAW_BITMAP:
|
|
||||||
RENDERER(swSurfaceDrawBitmap,
|
|
||||||
cmd->swSurfaceDrawBitmap.x , cmd->swSurfaceDrawBitmap.y,
|
|
||||||
cmd->swSurfaceDrawBitmap.width , cmd->swSurfaceDrawBitmap.height,
|
|
||||||
cmd->swSurfaceDrawBitmap.stride, cmd->swSurfaceDrawBitmap.data,
|
|
||||||
cmd->swSurfaceDrawBitmap.topDown);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SURFACE_OP_FORMAT:
|
case SURFACE_OP_FORMAT:
|
||||||
@@ -978,17 +1452,32 @@ void renderQueue_process(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
case SOURCE_OP_TRANSITION:
|
case SOURCE_OP_TRANSITION:
|
||||||
|
{
|
||||||
|
const bool swSurface =
|
||||||
|
cmd->source != RENDER_QUEUE_SOURCE_NONE &&
|
||||||
|
cmd->sourceTransition.swSurface;
|
||||||
|
if (swSurface && !swSurfaceTransitionReady(cmd, false))
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Software surface source is unavailable");
|
||||||
|
rejectSourceTransition(cmd);
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (!prepareSourceTransition(cmd))
|
if (!prepareSourceTransition(cmd))
|
||||||
break;
|
break;
|
||||||
applySourceTransition(cmd,
|
if (swSurface && !prepareSwSurfaceTransition(cmd, false))
|
||||||
cmd->source != RENDER_QUEUE_SOURCE_NONE &&
|
{
|
||||||
cmd->sourceTransition.swSurface);
|
rejectSourceTransition(cmd);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
applySourceTransition(cmd, swSurface);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
LG_UNLOCK(l_sourceLock);
|
LG_UNLOCK(l_sourceLock);
|
||||||
if (transition)
|
if (transition)
|
||||||
LG_UNLOCK(l_transitionLock);
|
LG_UNLOCK(l_transitionLock);
|
||||||
freeCommand(cmd);
|
freeCommand(cmd);
|
||||||
|
wakeQueue(RENDER_QUEUE_INVALIDATE_PARTIAL, wake);
|
||||||
cmd = next;
|
cmd = next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ typedef void (*RenderQueueSourceAppliedFn)(void * opaque,
|
|||||||
bool swSurface);
|
bool swSurface);
|
||||||
typedef bool (*RenderQueueSourcePrepareFn)(void * opaque,
|
typedef bool (*RenderQueueSourcePrepareFn)(void * opaque,
|
||||||
RenderQueueSource source, uint64_t generation, uint64_t serial);
|
RenderQueueSource source, uint64_t generation, uint64_t serial);
|
||||||
|
typedef void (*RenderQueueSourceRejectFn)(void * opaque,
|
||||||
|
RenderQueueSource source, uint64_t serial);
|
||||||
|
|
||||||
void renderQueue_init(void);
|
void renderQueue_init(void);
|
||||||
void renderQueue_free(void);
|
void renderQueue_free(void);
|
||||||
@@ -44,6 +46,7 @@ void renderQueue_process(void);
|
|||||||
void renderQueue_presented(void);
|
void renderQueue_presented(void);
|
||||||
|
|
||||||
void renderQueue_setSourceFns(RenderQueueSourcePrepareFn prepare,
|
void renderQueue_setSourceFns(RenderQueueSourcePrepareFn prepare,
|
||||||
|
RenderQueueSourceRejectFn reject,
|
||||||
RenderQueueSourceAppliedFn applied, void * opaque);
|
RenderQueueSourceAppliedFn applied, void * opaque);
|
||||||
|
|
||||||
/* Starting a source lifecycle makes commands from its prior generation stale.
|
/* Starting a source lifecycle makes commands from its prior generation stale.
|
||||||
@@ -57,6 +60,9 @@ uint64_t renderQueue_sourceTransition(RenderQueueSource source,
|
|||||||
uint64_t generation, bool swSurface,
|
uint64_t generation, bool swSurface,
|
||||||
atomic_uint_least64_t * publishedSerial);
|
atomic_uint_least64_t * publishedSerial);
|
||||||
|
|
||||||
|
bool renderQueue_sourceSwSurfaceConfigure(RenderQueueSource source,
|
||||||
|
uint64_t generation, int width, int height);
|
||||||
|
|
||||||
uint64_t renderQueue_sourceSwSurfaceConfigureTransition(
|
uint64_t renderQueue_sourceSwSurfaceConfigureTransition(
|
||||||
RenderQueueSource source, uint64_t generation, int width, int height,
|
RenderQueueSource source, uint64_t generation, int width, int height,
|
||||||
atomic_uint_least64_t * publishedSerial);
|
atomic_uint_least64_t * publishedSerial);
|
||||||
|
|||||||
@@ -243,12 +243,12 @@ static void start(void)
|
|||||||
g_state.ds = &f.ds;
|
g_state.ds = &f.ds;
|
||||||
|
|
||||||
renderQueue_init();
|
renderQueue_init();
|
||||||
renderQueue_setSourceFns(prep, applied, &f);
|
renderQueue_setSourceFns(prep, NULL, applied, &f);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void stop(void)
|
static void stop(void)
|
||||||
{
|
{
|
||||||
renderQueue_setSourceFns(NULL, NULL, NULL);
|
renderQueue_setSourceFns(NULL, NULL, NULL, NULL);
|
||||||
renderQueue_free();
|
renderQueue_free();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -296,6 +296,8 @@ static void testLatest(void)
|
|||||||
renderQueue_sourceBegin(RENDER_QUEUE_SOURCE_PRIMARY);
|
renderQueue_sourceBegin(RENDER_QUEUE_SOURCE_PRIMARY);
|
||||||
const uint64_t fallback =
|
const uint64_t fallback =
|
||||||
renderQueue_sourceBegin(RENDER_QUEUE_SOURCE_FALLBACK);
|
renderQueue_sourceBegin(RENDER_QUEUE_SOURCE_FALLBACK);
|
||||||
|
CHECK(renderQueue_sourceSwSurfaceConfigure(
|
||||||
|
RENDER_QUEUE_SOURCE_FALLBACK, fallback, 2, 1));
|
||||||
atomic_uint_least64_t published;
|
atomic_uint_least64_t published;
|
||||||
atomic_init(&published, 0);
|
atomic_init(&published, 0);
|
||||||
|
|
||||||
@@ -332,9 +334,11 @@ static void testPrepare(void)
|
|||||||
|
|
||||||
const uint64_t generation =
|
const uint64_t generation =
|
||||||
renderQueue_sourceBegin(RENDER_QUEUE_SOURCE_PRIMARY);
|
renderQueue_sourceBegin(RENDER_QUEUE_SOURCE_PRIMARY);
|
||||||
|
CHECK(renderQueue_sourceSwSurfaceConfigure(
|
||||||
|
RENDER_QUEUE_SOURCE_PRIMARY, generation, 4, 4));
|
||||||
f.prepResult = false;
|
f.prepResult = false;
|
||||||
CHECK(renderQueue_sourceSwSurfaceConfigureTransition(
|
CHECK(renderQueue_sourceSwSurfaceConfigureTransition(
|
||||||
RENDER_QUEUE_SOURCE_PRIMARY, generation, 640, 480, NULL) != 0);
|
RENDER_QUEUE_SOURCE_PRIMARY, generation, 4, 4, NULL) != 0);
|
||||||
renderQueue_process();
|
renderQueue_process();
|
||||||
renderQueue_presented();
|
renderQueue_presented();
|
||||||
|
|
||||||
@@ -345,19 +349,23 @@ static void testPrepare(void)
|
|||||||
CHECK(f.appliedCount == 0);
|
CHECK(f.appliedCount == 0);
|
||||||
|
|
||||||
f.prepResult = true;
|
f.prepResult = true;
|
||||||
|
CHECK(renderQueue_sourceSwSurfaceConfigure(
|
||||||
|
RENDER_QUEUE_SOURCE_PRIMARY, generation, 3, 2));
|
||||||
const uint64_t serial = renderQueue_sourceSwSurfaceConfigureTransition(
|
const uint64_t serial = renderQueue_sourceSwSurfaceConfigureTransition(
|
||||||
RENDER_QUEUE_SOURCE_PRIMARY, generation, 320, 200, NULL);
|
RENDER_QUEUE_SOURCE_PRIMARY, generation, 3, 2, NULL);
|
||||||
CHECK(serial != 0);
|
CHECK(serial != 0);
|
||||||
renderQueue_process();
|
renderQueue_process();
|
||||||
CHECK(f.configCount == 1);
|
CHECK(f.configCount == 1);
|
||||||
CHECK(f.configWidth == 320);
|
CHECK(f.configWidth == 3);
|
||||||
CHECK(f.configHeight == 200);
|
CHECK(f.configHeight == 2);
|
||||||
CHECK(f.fillCount == 1);
|
CHECK(f.fillCount == 0);
|
||||||
CHECK(f.fillX == 0);
|
CHECK(f.bitmapCount == 1);
|
||||||
CHECK(f.fillY == 0);
|
CHECK(f.bitmapX == 0);
|
||||||
CHECK(f.fillWidth == 320);
|
CHECK(f.bitmapY == 0);
|
||||||
CHECK(f.fillHeight == 200);
|
CHECK(f.bitmapWidth == 3);
|
||||||
CHECK(f.fillColor == 0);
|
CHECK(f.bitmapHeight == 2);
|
||||||
|
CHECK(f.bitmapStride == 12);
|
||||||
|
CHECK(f.bitmapTopDown);
|
||||||
CHECK(f.showCount == 1);
|
CHECK(f.showCount == 1);
|
||||||
CHECK(f.show);
|
CHECK(f.show);
|
||||||
|
|
||||||
@@ -406,6 +414,8 @@ static void testPayload(void)
|
|||||||
|
|
||||||
const uint64_t generation =
|
const uint64_t generation =
|
||||||
renderQueue_sourceBegin(RENDER_QUEUE_SOURCE_PRIMARY);
|
renderQueue_sourceBegin(RENDER_QUEUE_SOURCE_PRIMARY);
|
||||||
|
CHECK(renderQueue_sourceSwSurfaceConfigure(
|
||||||
|
RENDER_QUEUE_SOURCE_PRIMARY, generation, 2, 2));
|
||||||
uint8_t bitmap[] =
|
uint8_t bitmap[] =
|
||||||
{
|
{
|
||||||
1, 2, 3, 4, 5, 6, 7, 8,
|
1, 2, 3, 4, 5, 6, 7, 8,
|
||||||
@@ -427,7 +437,7 @@ static void testPayload(void)
|
|||||||
};
|
};
|
||||||
|
|
||||||
renderQueue_sourceSwSurfaceDrawBitmap(RENDER_QUEUE_SOURCE_PRIMARY,
|
renderQueue_sourceSwSurfaceDrawBitmap(RENDER_QUEUE_SOURCE_PRIMARY,
|
||||||
generation, 5, 6, 2, 2, 8, bitmap, true);
|
generation, 0, 0, 2, 2, 8, bitmap, true);
|
||||||
renderQueue_sourceCursorImage(RENDER_QUEUE_SOURCE_PRIMARY, generation,
|
renderQueue_sourceCursorImage(RENDER_QUEUE_SOURCE_PRIMARY, generation,
|
||||||
LG_CURSOR_COLOR, 2, 2, 4, shape);
|
LG_CURSOR_COLOR, 2, 2, 4, shape);
|
||||||
renderQueue_sourceCursorColorTransform(RENDER_QUEUE_SOURCE_PRIMARY,
|
renderQueue_sourceCursorColorTransform(RENDER_QUEUE_SOURCE_PRIMARY,
|
||||||
@@ -435,7 +445,7 @@ static void testPayload(void)
|
|||||||
renderQueue_sourceCursorState(RENDER_QUEUE_SOURCE_PRIMARY, generation,
|
renderQueue_sourceCursorState(RENDER_QUEUE_SOURCE_PRIMARY, generation,
|
||||||
true, 20, 21, 1, 2);
|
true, 20, 21, 1, 2);
|
||||||
CHECK(renderQueue_sourceTransition(RENDER_QUEUE_SOURCE_PRIMARY,
|
CHECK(renderQueue_sourceTransition(RENDER_QUEUE_SOURCE_PRIMARY,
|
||||||
generation, false, NULL) != 0);
|
generation, true, NULL) != 0);
|
||||||
|
|
||||||
memset(bitmap, 0, sizeof(bitmap));
|
memset(bitmap, 0, sizeof(bitmap));
|
||||||
memset(shape, 0, sizeof(shape));
|
memset(shape, 0, sizeof(shape));
|
||||||
@@ -446,8 +456,8 @@ static void testPayload(void)
|
|||||||
renderQueue_process();
|
renderQueue_process();
|
||||||
|
|
||||||
CHECK(f.bitmapCount == 1);
|
CHECK(f.bitmapCount == 1);
|
||||||
CHECK(f.bitmapX == 5);
|
CHECK(f.bitmapX == 0);
|
||||||
CHECK(f.bitmapY == 6);
|
CHECK(f.bitmapY == 0);
|
||||||
CHECK(f.bitmapWidth == 2);
|
CHECK(f.bitmapWidth == 2);
|
||||||
CHECK(f.bitmapHeight == 2);
|
CHECK(f.bitmapHeight == 2);
|
||||||
CHECK(f.bitmapStride == 8);
|
CHECK(f.bitmapStride == 8);
|
||||||
|
|||||||
Reference in New Issue
Block a user