[client] network: bound video decoder backlog

Cap decoded snapshots and queued GPU work so a slow presentation path
cannot accumulate frames indefinitely.

Coalesce deferred damage and retry the latest image when a texture
returns, including while JIT rendering is idle.
This commit is contained in:
Geoffrey McRae
2026-10-08 23:56:14 +11:00
parent 0b0f227c08
commit ea24c288ad
3 changed files with 632 additions and 139 deletions

View File

@@ -143,6 +143,21 @@ struct NetworkVideoEncoded
LGNetVideoSchedule schedule; LGNetVideoSchedule schedule;
}; };
struct NetworkVideoDeferred
{
uint64_t decodeGeneration;
uint64_t configEpoch;
uint64_t frameID;
uint64_t epoch;
uint64_t timestamp;
uint64_t captureTimestampNs;
uint64_t presentationTimestampNs;
uint64_t receiveTimeNs;
uint32_t formatVersion;
bool complete;
bool valid;
};
struct NetworkVideo struct NetworkVideo
{ {
NetworkConnection * connection; NetworkConnection * connection;
@@ -165,6 +180,7 @@ struct NetworkVideo
bool waitCancelled; bool waitCancelled;
bool decodeStopping; bool decodeStopping;
bool decodePending; bool decodePending;
bool framePollPending;
uint16_t serviceVersion; uint16_t serviceVersion;
uint64_t subscriberID; uint64_t subscriberID;
@@ -192,6 +208,7 @@ struct NetworkVideo
struct NetworkVideoAssembly assembly; struct NetworkVideoAssembly assembly;
struct NetworkVideoEncoded pendingDecode; struct NetworkVideoEncoded pendingDecode;
struct NetworkVideoDeferred deferred;
struct NetworkVideoScheduleEntry schedules[NETWORK_VIDEO_SCHEDULE_CACHE]; struct NetworkVideoScheduleEntry schedules[NETWORK_VIDEO_SCHEDULE_CACHE];
struct NetworkVideoOutput * output; struct NetworkVideoOutput * output;
struct NetworkVideoOutput * leases; struct NetworkVideoOutput * leases;
@@ -354,6 +371,39 @@ static void networkVideoClearEncoded(struct NetworkVideoEncoded * encoded)
memset(encoded, 0, sizeof(*encoded)); memset(encoded, 0, sizeof(*encoded));
} }
static void networkVideoDeferNL(NetworkVideo * video,
const struct NetworkVideoEncoded * encoded)
{
video->deferred = (struct NetworkVideoDeferred)
{
.decodeGeneration = encoded->generation,
.configEpoch = encoded->config.configEpoch,
.frameID = encoded->frameID,
.epoch = video->status.epoch,
.timestamp = encoded->presentationTimestampNs,
.captureTimestampNs = encoded->captureTimestampNs,
.presentationTimestampNs = encoded->presentationTimestampNs,
.receiveTimeNs = encoded->receiveTimeNs,
.formatVersion = video->localConfigGeneration,
.complete = encoded->complete,
.valid = true,
};
video->framePollPending = true;
}
static bool networkVideoDeferredCurrentNL(
const NetworkVideo * video, const struct NetworkVideoDeferred * deferred)
{
return deferred->valid && video->deferred.valid &&
deferred->decodeGeneration == video->deferred.decodeGeneration &&
deferred->configEpoch == video->deferred.configEpoch &&
deferred->frameID == video->deferred.frameID &&
deferred->epoch == video->deferred.epoch &&
video->decodeGeneration == deferred->decodeGeneration &&
video->config.configEpoch == deferred->configEpoch &&
video->status.epoch == deferred->epoch;
}
static void networkVideoInvalidateDecodeNL(NetworkVideo * video, static void networkVideoInvalidateDecodeNL(NetworkVideo * video,
struct NetworkVideoEncoded * pending) struct NetworkVideoEncoded * pending)
{ {
@@ -361,6 +411,8 @@ static void networkVideoInvalidateDecodeNL(NetworkVideo * video,
*pending = video->pendingDecode; *pending = video->pendingDecode;
video->pendingDecode = (struct NetworkVideoEncoded){0}; video->pendingDecode = (struct NetworkVideoEncoded){0};
video->decodePending = false; video->decodePending = false;
video->framePollPending = false;
memset(&video->deferred, 0, sizeof(video->deferred));
} }
static struct NetworkVideoOutput * networkVideoFindLeaseNL( static struct NetworkVideoOutput * networkVideoFindLeaseNL(
@@ -1102,7 +1154,10 @@ static void networkVideoDecodeFailure(NetworkVideo * video,
video->wireConfigured && video->wireConfigured &&
video->config.configEpoch == encoded->config.configEpoch) video->config.configEpoch == encoded->config.configEpoch)
{ {
video->lostFrames = networkVideoSaturatingIncrement(video->lostFrames); video->lostFrames = networkVideoSaturatingIncrement(
video->lostFrames);
video->framePollPending = false;
memset(&video->deferred, 0, sizeof(video->deferred));
const LGNetVideoFeedbackFlags flags = result == const LGNetVideoFeedbackFlags flags = result ==
NETWORK_VIDEO_DECODE_ERROR ? NETWORK_VIDEO_DECODE_ERROR ?
@@ -1141,24 +1196,18 @@ static void networkVideoDecode(NetworkVideo * video,
return; return;
} }
struct NetworkVideoOutput * output = calloc(1, sizeof(*output));
if (!output)
{
free(encoded->ownedData);
networkVideoDecodeFailure(
video, encoded, NETWORK_VIDEO_DECODE_ERROR);
return;
}
NetworkVideoDecodedFrame decoded = {0}; NetworkVideoDecodedFrame decoded = {0};
const NetworkVideoDecodeResult result = networkVideoDecoderDecode( const NetworkVideoDecodeResult result = networkVideoDecoderDecode(
video->decoder, &encoded->config, encoded->data, video->decoder, &encoded->config, encoded->data,
encoded->length, encoded->blockCount, encoded->update, encoded->length, encoded->blockCount, encoded->update,
encoded->complete, encoded->baseFrameID, encoded->frameID, &decoded); encoded->complete, encoded->baseFrameID, encoded->frameID, &decoded);
free(encoded->ownedData); free(encoded->ownedData);
if (result == NETWORK_VIDEO_DECODE_NO_FRAME) encoded->ownedData = NULL;
encoded->data = NULL;
if (result == NETWORK_VIDEO_DECODE_NO_FRAME ||
result == NETWORK_VIDEO_DECODE_DEFERRED)
{ {
free(output); bool pollFrame = false;
LG_LOCK(video->stateLock); LG_LOCK(video->stateLock);
if (video->decodeGeneration == encoded->generation && if (video->decodeGeneration == encoded->generation &&
video->wireConfigured && video->wireConfigured &&
@@ -1169,18 +1218,33 @@ static void networkVideoDecode(NetworkVideo * video,
if (encoded->complete) if (encoded->complete)
video->lastCompleteFrameID = encoded->frameID; video->lastCompleteFrameID = encoded->frameID;
video->lastDecodedFrameID = encoded->frameID; video->lastDecodedFrameID = encoded->frameID;
if (result == NETWORK_VIDEO_DECODE_DEFERRED)
{
networkVideoDeferNL(video, encoded);
pollFrame = true;
}
networkVideoQueueFeedbackNL(video, 0); networkVideoQueueFeedbackNL(video, 0);
} }
LG_UNLOCK(video->stateLock); LG_UNLOCK(video->stateLock);
if (pollFrame)
lgSignalEvent(video->frameEvent);
return; return;
} }
if (result != NETWORK_VIDEO_DECODE_OK) if (result != NETWORK_VIDEO_DECODE_OK)
{ {
free(output);
networkVideoDecodeFailure(video, encoded, result); networkVideoDecodeFailure(video, encoded, result);
return; return;
} }
struct NetworkVideoOutput * output = calloc(1, sizeof(*output));
if (!output)
{
networkVideoDecoderRelease(&decoded);
networkVideoDecodeFailure(
video, encoded, NETWORK_VIDEO_DECODE_ERROR);
return;
}
struct NetworkVideoOutput * replaced = NULL; struct NetworkVideoOutput * replaced = NULL;
output->decoded = decoded; output->decoded = decoded;
output->frameID = encoded->frameID; output->frameID = encoded->frameID;
@@ -1209,6 +1273,8 @@ static void networkVideoDecode(NetworkVideo * video,
if (encoded->complete) if (encoded->complete)
video->lastCompleteFrameID = output->frameID; video->lastCompleteFrameID = output->frameID;
video->lastDecodedFrameID = output->frameID; video->lastDecodedFrameID = output->frameID;
video->framePollPending = false;
memset(&video->deferred, 0, sizeof(video->deferred));
output->decoded.format.version = video->localConfigGeneration; output->decoded.format.version = video->localConfigGeneration;
output->epoch = video->status.epoch; output->epoch = video->status.epoch;
video->output = output; video->output = output;
@@ -1218,6 +1284,101 @@ static void networkVideoDecode(NetworkVideo * video,
lgSignalEvent(video->frameEvent); lgSignalEvent(video->frameEvent);
} }
static bool networkVideoPresentDeferred(NetworkVideo * video,
const struct NetworkVideoDeferred * deferred)
{
LG_LOCK(video->stateLock);
const bool current = !video->destroying && !video->decodeStopping &&
video->connected && video->wireConfigured && video->rendererAttached &&
video->decoderReady && networkVideoDeferredCurrentNL(video, deferred);
LG_UNLOCK(video->stateLock);
if (!current)
return false;
NetworkVideoDecodedFrame decoded = {0};
const NetworkVideoDecodeResult result =
networkVideoDecoderPresentPending(video->decoder, &decoded);
if (result == NETWORK_VIDEO_DECODE_DEFERRED)
return false;
if (result == NETWORK_VIDEO_DECODE_NO_FRAME)
{
LG_LOCK(video->stateLock);
if (networkVideoDeferredCurrentNL(video, deferred))
{
video->framePollPending = false;
memset(&video->deferred, 0, sizeof(video->deferred));
}
LG_UNLOCK(video->stateLock);
return true;
}
if (result != NETWORK_VIDEO_DECODE_OK)
{
struct NetworkVideoEncoded encoded = {0};
encoded.config.configEpoch = deferred->configEpoch;
encoded.generation = deferred->decodeGeneration;
encoded.frameID = deferred->frameID;
encoded.complete = deferred->complete;
LG_LOCK(video->stateLock);
if (networkVideoDeferredCurrentNL(video, deferred))
{
video->framePollPending = false;
memset(&video->deferred, 0, sizeof(video->deferred));
}
LG_UNLOCK(video->stateLock);
networkVideoDecodeFailure(video, &encoded, result);
return true;
}
struct NetworkVideoOutput * output = calloc(1, sizeof(*output));
if (!output)
{
struct NetworkVideoEncoded encoded = {0};
encoded.config.configEpoch = deferred->configEpoch;
encoded.generation = deferred->decodeGeneration;
encoded.frameID = deferred->frameID;
encoded.complete = deferred->complete;
networkVideoDecoderRelease(&decoded);
networkVideoDecodeFailure(
video, &encoded, NETWORK_VIDEO_DECODE_ERROR);
return true;
}
struct NetworkVideoOutput * replaced = NULL;
output->decoded = decoded;
output->frameID = deferred->frameID;
output->timestamp = deferred->timestamp;
output->captureTimestampNs = deferred->captureTimestampNs;
output->presentationTimestampNs = deferred->presentationTimestampNs;
output->receiveTimeNs = deferred->receiveTimeNs;
LG_LOCK(video->stateLock);
if (!networkVideoDeferredCurrentNL(video, deferred) ||
output->frameID != video->lastDecodedFrameID ||
!video->rendererAttached || !video->decoderReady)
{
LG_UNLOCK(video->stateLock);
networkVideoFreeOutput(&output);
return true;
}
if (video->output)
{
video->lostFrames = networkVideoSaturatingIncrement(video->lostFrames);
replaced = video->output;
networkVideoMergeDamage(&output->decoded, &replaced->decoded);
}
output->decoded.format.version = deferred->formatVersion;
output->epoch = deferred->epoch;
video->output = output;
video->framePollPending = false;
memset(&video->deferred, 0, sizeof(video->deferred));
networkVideoQueueFeedbackNL(video, 0);
LG_UNLOCK(video->stateLock);
networkVideoFreeOutput(&replaced);
lgSignalEvent(video->frameEvent);
return true;
}
static void networkVideoQueueDecode(NetworkVideo * video, static void networkVideoQueueDecode(NetworkVideo * video,
struct NetworkVideoEncoded * encoded) struct NetworkVideoEncoded * encoded)
{ {
@@ -1283,7 +1444,8 @@ static int networkVideoDecodeThread(void * opaque)
lgWaitEvent(video->decodeEvent, TIMEOUT_INFINITE); lgWaitEvent(video->decodeEvent, TIMEOUT_INFINITE);
for (;;) for (;;)
{ {
struct NetworkVideoEncoded encoded = {0}; struct NetworkVideoEncoded encoded = {0};
struct NetworkVideoDeferred deferred = {0};
LG_LOCK(video->stateLock); LG_LOCK(video->stateLock);
if (video->decodeStopping) if (video->decodeStopping)
{ {
@@ -1300,11 +1462,18 @@ static int networkVideoDecodeThread(void * opaque)
video->pendingDecode = (struct NetworkVideoEncoded){0}; video->pendingDecode = (struct NetworkVideoEncoded){0};
video->decodePending = false; video->decodePending = false;
} }
else if (video->deferred.valid)
deferred = video->deferred;
LG_UNLOCK(video->stateLock); LG_UNLOCK(video->stateLock);
if (!encoded.ownedData) if (encoded.ownedData)
{
networkVideoDecode(video, &encoded);
continue;
}
if (!deferred.valid ||
!networkVideoPresentDeferred(video, &deferred))
break; break;
networkVideoDecode(video, &encoded);
} }
} }
} }
@@ -1960,6 +2129,7 @@ void networkVideoProcess(NetworkVideo * video)
const uint64_t now = nanotime(); const uint64_t now = nanotime();
struct NetworkVideoAssembly retiredAssembly = {0}; struct NetworkVideoAssembly retiredAssembly = {0};
struct NetworkVideoEncoded recovered = {0}; struct NetworkVideoEncoded recovered = {0};
bool pollFrame = false;
LG_LOCK(video->stateLock); LG_LOCK(video->stateLock);
if (video->destroying) if (video->destroying)
{ {
@@ -1978,10 +2148,17 @@ void networkVideoProcess(NetworkVideo * video)
networkVideoSendSubscribeNL(video, now); networkVideoSendSubscribeNL(video, now);
networkVideoSendKeyframeNL(video, now); networkVideoSendKeyframeNL(video, now);
networkVideoSendFeedbackNL(video, now); networkVideoSendFeedbackNL(video, now);
if (video->deferred.valid && !video->framePollPending)
{
video->framePollPending = true;
pollFrame = true;
}
LG_UNLOCK(video->stateLock); LG_UNLOCK(video->stateLock);
networkVideoClearAssembly(&retiredAssembly); networkVideoClearAssembly(&retiredAssembly);
if (recovered.ownedData) if (recovered.ownedData)
networkVideoQueueDecode(video, &recovered); networkVideoQueueDecode(video, &recovered);
if (pollFrame)
lgSignalEvent(video->frameEvent);
} }
static bool networkVideoTimer(void * opaque) static bool networkVideoTimer(void * opaque)
@@ -2032,7 +2209,7 @@ bool networkVideoCreate(NetworkVideo ** result,
video->frameEvent = lgCreateEvent(true, 0); video->frameEvent = lgCreateEvent(true, 0);
video->decodeEvent = lgCreateEvent(true, 0); video->decodeEvent = lgCreateEvent(true, 0);
if (!video->frameEvent || !video->decodeEvent || if (!video->frameEvent || !video->decodeEvent ||
!networkVideoDecoderCreate(&video->decoder) || !networkVideoDecoderCreate(&video->decoder, video->decodeEvent) ||
!lgCreateThread("networkDecode", networkVideoDecodeThread, !lgCreateThread("networkDecode", networkVideoDecodeThread,
video, &video->decodeThread)) video, &video->decodeThread))
{ {
@@ -2324,6 +2501,12 @@ LG_TransportStatus networkVideoNextFrame(NetworkVideo * video, bool useDMA,
frame->releaseHandle = output->handle; frame->releaseHandle = output->handle;
ready = true; ready = true;
} }
else if (video->framePollPending)
{
video->framePollPending = false;
status = LG_TRANSPORT_TIMEOUT;
ready = true;
}
} }
LG_UNLOCK(video->stateLock); LG_UNLOCK(video->stateLock);
networkVideoFreeOutputList(released); networkVideoFreeOutputList(released);

View File

@@ -24,6 +24,8 @@
#include "interface/renderer.h" #include "interface/renderer.h"
#include "interface/transport.h" #include "interface/transport.h"
#include "common/event.h"
#include <LGProtocol/NetworkServices.h> #include <LGProtocol/NetworkServices.h>
#include <stdbool.h> #include <stdbool.h>
@@ -36,6 +38,7 @@ typedef enum NetworkVideoDecodeResult
{ {
NETWORK_VIDEO_DECODE_OK, NETWORK_VIDEO_DECODE_OK,
NETWORK_VIDEO_DECODE_NO_FRAME, NETWORK_VIDEO_DECODE_NO_FRAME,
NETWORK_VIDEO_DECODE_DEFERRED,
NETWORK_VIDEO_DECODE_UNSUPPORTED, NETWORK_VIDEO_DECODE_UNSUPPORTED,
NETWORK_VIDEO_DECODE_CORRUPT, NETWORK_VIDEO_DECODE_CORRUPT,
NETWORK_VIDEO_DECODE_ERROR, NETWORK_VIDEO_DECODE_ERROR,
@@ -53,7 +56,8 @@ typedef struct NetworkVideoDecodedFrame
} }
NetworkVideoDecodedFrame; NetworkVideoDecodedFrame;
bool networkVideoDecoderCreate(NetworkVideoDecoder ** decoder); bool networkVideoDecoderCreate(NetworkVideoDecoder ** decoder,
LGEvent * presentEvent);
void networkVideoDecoderDestroy(NetworkVideoDecoder ** decoder); void networkVideoDecoderDestroy(NetworkVideoDecoder ** decoder);
bool networkVideoDecoderAttach(NetworkVideoDecoder * decoder, bool networkVideoDecoderAttach(NetworkVideoDecoder * decoder,
@@ -68,5 +72,7 @@ NetworkVideoDecodeResult networkVideoDecoderDecode(
const void * data, size_t size, uint32_t blockCount, const void * data, size_t size, uint32_t blockCount,
bool update, bool complete, uint64_t baseFrameID, uint64_t frameID, bool update, bool complete, uint64_t baseFrameID, uint64_t frameID,
NetworkVideoDecodedFrame * frame); NetworkVideoDecodedFrame * frame);
NetworkVideoDecodeResult networkVideoDecoderPresentPending(
NetworkVideoDecoder * decoder, NetworkVideoDecodedFrame * frame);
void networkVideoDecoderRelease(NetworkVideoDecodedFrame * frame); void networkVideoDecoderRelease(NetworkVideoDecodedFrame * frame);

View File

@@ -50,6 +50,7 @@
#include "shaders.h" #include "shaders.h"
#include "common/debug.h" #include "common/debug.h"
#include "common/event.h"
#include "common/locking.h" #include "common/locking.h"
#include "common/time.h" #include "common/time.h"
@@ -67,6 +68,7 @@
#define NETWORK_VIDEO_ALIGNMENT (1U << NETWORK_VIDEO_LEVELS) #define NETWORK_VIDEO_ALIGNMENT (1U << NETWORK_VIDEO_LEVELS)
#define NETWORK_VIDEO_MIN_SIZE (4U << NETWORK_VIDEO_LEVELS) #define NETWORK_VIDEO_MIN_SIZE (4U << NETWORK_VIDEO_LEVELS)
#define NETWORK_VIDEO_UPLOADS 3U #define NETWORK_VIDEO_UPLOADS 3U
#define NETWORK_VIDEO_POOL_MIN_SIZE 2U
#define NETWORK_VIDEO_POOL_SIZE 3U #define NETWORK_VIDEO_POOL_SIZE 3U
#define NETWORK_VIDEO_DAMAGE_HISTORY 64U #define NETWORK_VIDEO_DAMAGE_HISTORY 64U
#define NETWORK_VIDEO_POOL_BYTE_LIMIT \ #define NETWORK_VIDEO_POOL_BYTE_LIMIT \
@@ -131,11 +133,13 @@ struct NetworkVideoTexturePool
LG_Lock lock; LG_Lock lock;
atomic_uint references; atomic_uint references;
LGEvent * presentEvent;
EGLDisplay display; EGLDisplay display;
EGLConfig eglConfig; EGLConfig eglConfig;
EGLContext shareContext; EGLContext shareContext;
struct NetworkVideoTexture textures[NETWORK_VIDEO_POOL_SIZE]; struct NetworkVideoTexture textures[NETWORK_VIDEO_POOL_SIZE];
unsigned textureCount; unsigned textureCount;
unsigned textureAllocated;
unsigned textureLimit; unsigned textureLimit;
uint32_t width; uint32_t width;
uint32_t height; uint32_t height;
@@ -219,6 +223,8 @@ struct NetworkVideoDecoder
uint8_t * outputDirty; uint8_t * outputDirty;
struct NetworkVideoDamageHistory struct NetworkVideoDamageHistory
damageHistory[NETWORK_VIDEO_DAMAGE_HISTORY]; damageHistory[NETWORK_VIDEO_DAMAGE_HISTORY];
LGNetVideoDamage pendingDamage;
LGEvent * presentEvent;
LGNetVideoStreamConfig config; LGNetVideoStreamConfig config;
struct NetworkVideoBlockMeta struct NetworkVideoBlockMeta
@@ -232,6 +238,7 @@ struct NetworkVideoDecoder
uint32_t outputTileHeight; uint32_t outputTileHeight;
uint64_t referenceFrameID; uint64_t referenceFrameID;
uint64_t outputGeneration; uint64_t outputGeneration;
uint64_t pendingDecodeTimeNs;
unsigned uploadIndex; unsigned uploadIndex;
GLint maxTextureSize; GLint maxTextureSize;
GLint maxArrayLayers; GLint maxArrayLayers;
@@ -240,6 +247,7 @@ struct NetworkVideoDecoder
bool attached; bool attached;
bool configured; bool configured;
bool referenceValid; bool referenceValid;
bool pendingChromaLeft;
}; };
struct NetworkVideoExternalFrame struct NetworkVideoExternalFrame
@@ -257,6 +265,12 @@ static uint32_t networkVideoAlign(uint32_t value, uint32_t alignment)
return (value + alignment - 1U) & ~(alignment - 1U); return (value + alignment - 1U) & ~(alignment - 1U);
} }
static uint64_t networkVideoSaturatingAdd(
uint64_t left, uint64_t right)
{
return left > UINT64_MAX - right ? UINT64_MAX : left + right;
}
static uint32_t networkVideoReadU32(const uint8_t * data) static uint32_t networkVideoReadU32(const uint8_t * data)
{ {
return (uint32_t)data[0] | return (uint32_t)data[0] |
@@ -384,21 +398,24 @@ static struct NetworkVideoTexturePool * networkVideoTexturePoolCreate(
if (!pool) if (!pool)
return NULL; return NULL;
const uint64_t width = decoder->config.width; const uint64_t width = decoder->config.width;
const uint64_t height = decoder->config.height; const uint64_t height = decoder->config.height;
const uint64_t textureBytes = width * height * NETWORK_VIDEO_PIXEL_BYTES; const uint64_t textureBytes = width * height * NETWORK_VIDEO_PIXEL_BYTES;
const uint64_t textureLimit = textureBytes ? const uint64_t textureLimit = textureBytes ?
NETWORK_VIDEO_POOL_BYTE_LIMIT / textureBytes : 0U; NETWORK_VIDEO_POOL_BYTE_LIMIT / textureBytes : 0U;
const uint64_t boundedLimit = textureLimit < NETWORK_VIDEO_POOL_MIN_SIZE ?
NETWORK_VIDEO_POOL_MIN_SIZE : textureLimit;
LG_LOCK_INIT(pool->lock); LG_LOCK_INIT(pool->lock);
atomic_init(&pool->references, 1U); atomic_init(&pool->references, 1U);
pool->presentEvent = decoder->presentEvent;
pool->display = decoder->display; pool->display = decoder->display;
pool->eglConfig = decoder->eglConfig; pool->eglConfig = decoder->eglConfig;
pool->shareContext = decoder->shareContext; pool->shareContext = decoder->shareContext;
pool->width = decoder->config.width; pool->width = decoder->config.width;
pool->height = decoder->config.height; pool->height = decoder->config.height;
pool->textureLimit = textureLimit < NETWORK_VIDEO_POOL_SIZE ? pool->textureLimit = boundedLimit < NETWORK_VIDEO_POOL_SIZE ?
(unsigned)textureLimit : NETWORK_VIDEO_POOL_SIZE; (unsigned)boundedLimit : NETWORK_VIDEO_POOL_SIZE;
return pool; return pool;
} }
@@ -408,6 +425,17 @@ static void networkVideoTexturePoolRetain(
atomic_fetch_add_explicit(&pool->references, 1U, memory_order_relaxed); atomic_fetch_add_explicit(&pool->references, 1U, memory_order_relaxed);
} }
static void networkVideoTexturePoolDetachEvent(
struct NetworkVideoTexturePool * pool)
{
if (!pool)
return;
LG_LOCK(pool->lock);
pool->presentEvent = NULL;
LG_UNLOCK(pool->lock);
}
static void networkVideoTexturePoolRelease( static void networkVideoTexturePoolRelease(
struct NetworkVideoTexturePool * pool, bool current) struct NetworkVideoTexturePool * pool, bool current)
{ {
@@ -452,7 +480,7 @@ static bool networkVideoTexturePoolReturn(
bool retained = false; bool retained = false;
LG_LOCK(pool->lock); LG_LOCK(pool->lock);
if (pool->textureCount < pool->textureLimit) if (pool->textureCount < pool->textureAllocated)
{ {
struct NetworkVideoTexture * pooled = struct NetworkVideoTexture * pooled =
&pool->textures[pool->textureCount++]; &pool->textures[pool->textureCount++];
@@ -460,6 +488,8 @@ static bool networkVideoTexturePoolReturn(
pooled->readySync = readySync; pooled->readySync = readySync;
pooled->generation = generation; pooled->generation = generation;
retained = true; retained = true;
if (pool->presentEvent)
lgSignalEvent(pool->presentEvent);
} }
LG_UNLOCK(pool->lock); LG_UNLOCK(pool->lock);
return retained; return retained;
@@ -549,6 +579,7 @@ static void networkVideoDeleteConfigurationNL(NetworkVideoDecoder * decoder)
if (upload->payloadBuffer) if (upload->payloadBuffer)
glDeleteBuffers(1, &upload->payloadBuffer); glDeleteBuffers(1, &upload->payloadBuffer);
} }
networkVideoTexturePoolDetachEvent(decoder->texturePool);
networkVideoTexturePoolRelease(decoder->texturePool, true); networkVideoTexturePoolRelease(decoder->texturePool, true);
networkVideoFreePacketStates( networkVideoFreePacketStates(
decoder->packetStates, decoder->blockCapacity); decoder->packetStates, decoder->blockCapacity);
@@ -563,23 +594,26 @@ static void networkVideoDeleteConfigurationNL(NetworkVideoDecoder * decoder)
memset(decoder->uploads, 0, sizeof(decoder->uploads)); memset(decoder->uploads, 0, sizeof(decoder->uploads));
memset(decoder->damageHistory, 0, sizeof(decoder->damageHistory)); memset(decoder->damageHistory, 0, sizeof(decoder->damageHistory));
decoder->texturePool = NULL; memset(&decoder->pendingDamage, 0, sizeof(decoder->pendingDamage));
decoder->offsets = NULL; decoder->texturePool = NULL;
decoder->packetStates = NULL; decoder->offsets = NULL;
decoder->pendingPackets = NULL; decoder->packetStates = NULL;
decoder->dirtyBlocks = NULL; decoder->pendingPackets = NULL;
decoder->outputDirty = NULL; decoder->dirtyBlocks = NULL;
decoder->canonicalTexture = 0; decoder->outputDirty = NULL;
decoder->alignedWidth = 0; decoder->canonicalTexture = 0;
decoder->alignedHeight = 0; decoder->alignedWidth = 0;
decoder->blockCapacity = 0; decoder->alignedHeight = 0;
decoder->outputTileWidth = 0; decoder->blockCapacity = 0;
decoder->outputTileHeight = 0; decoder->outputTileWidth = 0;
decoder->referenceFrameID = 0; decoder->outputTileHeight = 0;
decoder->outputGeneration = 0; decoder->referenceFrameID = 0;
decoder->uploadIndex = 0; decoder->outputGeneration = 0;
decoder->configured = false; decoder->pendingDecodeTimeNs = 0;
decoder->referenceValid = false; decoder->uploadIndex = 0;
decoder->configured = false;
decoder->referenceValid = false;
decoder->pendingChromaLeft = false;
memset(decoder->coefficientTextures, 0, memset(decoder->coefficientTextures, 0,
sizeof(decoder->coefficientTextures)); sizeof(decoder->coefficientTextures));
memset(decoder->planeTextures, 0, sizeof(decoder->planeTextures)); memset(decoder->planeTextures, 0, sizeof(decoder->planeTextures));
@@ -800,10 +834,32 @@ static bool networkVideoCreateTexture2D(GLuint * texture, GLenum format,
return false; return false;
} }
static bool networkVideoTexturePoolAllocateNL(
struct NetworkVideoTexturePool * pool)
{
while (pool->textureAllocated < NETWORK_VIDEO_POOL_MIN_SIZE)
{
struct NetworkVideoTexture * pooled =
&pool->textures[pool->textureCount];
if (!networkVideoCreateTexture2D(&pooled->texture, GL_RGBA16F,
pool->width, pool->height, GL_LINEAR))
{
while (glGetError() != GL_NO_ERROR)
{
}
return false;
}
++pool->textureCount;
++pool->textureAllocated;
}
return true;
}
static struct NetworkVideoTexture networkVideoTexturePoolAcquire( static struct NetworkVideoTexture networkVideoTexturePoolAcquire(
struct NetworkVideoTexturePool * pool) struct NetworkVideoTexturePool * pool)
{ {
struct NetworkVideoTexture pooled = { 0 }; struct NetworkVideoTexture pooled = { 0 };
bool allocate = false;
LG_LOCK(pool->lock); LG_LOCK(pool->lock);
if (pool->textureCount) if (pool->textureCount)
{ {
@@ -811,15 +867,26 @@ static struct NetworkVideoTexture networkVideoTexturePoolAcquire(
memset(&pool->textures[pool->textureCount], 0, memset(&pool->textures[pool->textureCount], 0,
sizeof(pool->textures[pool->textureCount])); sizeof(pool->textures[pool->textureCount]));
} }
else if (pool->textureAllocated < pool->textureLimit)
{
++pool->textureAllocated;
allocate = true;
}
LG_UNLOCK(pool->lock); LG_UNLOCK(pool->lock);
if (allocate && !networkVideoCreateTexture2D(&pooled.texture, GL_RGBA16F,
pool->width, pool->height, GL_LINEAR))
{
while (glGetError() != GL_NO_ERROR)
{
}
LG_LOCK(pool->lock);
--pool->textureAllocated;
pool->textureLimit = pool->textureAllocated;
LG_UNLOCK(pool->lock);
}
if (pooled.readySync) if (pooled.readySync)
glDeleteSync(pooled.readySync); glDeleteSync(pooled.readySync);
if (pooled.texture)
return pooled;
if (!networkVideoCreateTexture2D(&pooled.texture, GL_RGBA16F,
pool->width, pool->height, GL_LINEAR))
pooled.texture = 0;
return pooled; return pooled;
} }
@@ -888,7 +955,8 @@ static bool networkVideoAllocateNL(NetworkVideoDecoder * decoder)
outputTileCount, sizeof(*decoder->outputDirty)); outputTileCount, sizeof(*decoder->outputDirty));
if (!decoder->offsets || !decoder->packetStates || if (!decoder->offsets || !decoder->packetStates ||
!decoder->pendingPackets || !decoder->dirtyBlocks || !decoder->pendingPackets || !decoder->dirtyBlocks ||
!decoder->texturePool || !decoder->outputDirty) !decoder->texturePool || !decoder->outputDirty ||
!networkVideoTexturePoolAllocateNL(decoder->texturePool))
return false; return false;
for (unsigned component = 0; for (unsigned component = 0;
@@ -997,16 +1065,18 @@ static const LG_ExternalFrameOps NETWORK_VIDEO_EXTERNAL_OPS =
.query = networkVideoExternalQuery, .query = networkVideoExternalQuery,
}; };
bool networkVideoDecoderCreate(NetworkVideoDecoder ** result) bool networkVideoDecoderCreate(NetworkVideoDecoder ** result,
LGEvent * presentEvent)
{ {
if (!result) if (!result || !presentEvent)
return false; return false;
NetworkVideoDecoder * decoder = calloc(1, sizeof(*decoder)); NetworkVideoDecoder * decoder = calloc(1, sizeof(*decoder));
if (!decoder) if (!decoder)
return false; return false;
LG_LOCK_INIT(decoder->lock); LG_LOCK_INIT(decoder->lock);
*result = decoder; decoder->presentEvent = presentEvent;
*result = decoder;
return true; return true;
} }
@@ -1027,6 +1097,12 @@ void networkVideoDecoderDetach(NetworkVideoDecoder * decoder)
} }
eglDestroyContext(decoder->display, decoder->context); eglDestroyContext(decoder->display, decoder->context);
} }
if (decoder->texturePool)
{
networkVideoTexturePoolDetachEvent(decoder->texturePool);
networkVideoTexturePoolRelease(decoder->texturePool, false);
decoder->texturePool = NULL;
}
decoder->display = EGL_NO_DISPLAY; decoder->display = EGL_NO_DISPLAY;
decoder->eglConfig = NULL; decoder->eglConfig = NULL;
decoder->shareContext = EGL_NO_CONTEXT; decoder->shareContext = EGL_NO_CONTEXT;
@@ -1037,8 +1113,11 @@ void networkVideoDecoderDetach(NetworkVideoDecoder * decoder)
decoder->maxWorkGroupCount[1] = 0; decoder->maxWorkGroupCount[1] = 0;
decoder->maxStorageBlockSize = 0; decoder->maxStorageBlockSize = 0;
decoder->referenceFrameID = 0; decoder->referenceFrameID = 0;
decoder->pendingDecodeTimeNs = 0;
decoder->attached = false; decoder->attached = false;
decoder->referenceValid = false; decoder->referenceValid = false;
decoder->pendingChromaLeft = false;
memset(&decoder->pendingDamage, 0, sizeof(decoder->pendingDamage));
LG_UNLOCK(decoder->lock); LG_UNLOCK(decoder->lock);
} }
@@ -1068,8 +1147,11 @@ void networkVideoDecoderReset(NetworkVideoDecoder * decoder)
networkVideoRestoreCurrent(decoder, &previous); networkVideoRestoreCurrent(decoder, &previous);
} }
} }
decoder->referenceFrameID = 0; decoder->referenceFrameID = 0;
decoder->referenceValid = false; decoder->pendingDecodeTimeNs = 0;
decoder->referenceValid = false;
decoder->pendingChromaLeft = false;
memset(&decoder->pendingDamage, 0, sizeof(decoder->pendingDamage));
LG_UNLOCK(decoder->lock); LG_UNLOCK(decoder->lock);
} }
@@ -1765,6 +1847,11 @@ static bool networkVideoDamageFull(const LGNetVideoDamage * damage)
return (damage->flags & LG_NET_VIDEO_DAMAGE_FULL) != 0; return (damage->flags & LG_NET_VIDEO_DAMAGE_FULL) != 0;
} }
static bool networkVideoDamagePresent(const LGNetVideoDamage * damage)
{
return networkVideoDamageFull(damage) || damage->count;
}
static void networkVideoDispatchRect(GLint offsetUniform, static void networkVideoDispatchRect(GLint offsetUniform,
GLint extentUniform, uint32_t x, uint32_t y, GLint extentUniform, uint32_t x, uint32_t y,
uint32_t width, uint32_t height) uint32_t width, uint32_t height)
@@ -1994,6 +2081,95 @@ static bool networkVideoAppendDamageRect(LGNetVideoDamage * damage,
return true; return true;
} }
static bool networkVideoUnionDamageRect(LGNetVideoDamage * damage,
const LGNetVideoDamageRect * source)
{
uint32_t x = source->x;
uint32_t y = source->y;
uint32_t endX = source->x + source->width;
uint32_t endY = source->y + source->height;
for (uint16_t index = 0; index < damage->count;)
{
const LGNetVideoDamageRect * current = &damage->rects[index];
const uint32_t currentEndX =
current->x + current->width;
const uint32_t currentEndY =
current->y + current->height;
if (endX < current->x || currentEndX < x ||
endY < current->y || currentEndY < y)
{
++index;
continue;
}
x = current->x < x ? current->x : x;
y = current->y < y ? current->y : y;
endX = currentEndX > endX ? currentEndX : endX;
endY = currentEndY > endY ? currentEndY : endY;
damage->rects[index] = damage->rects[--damage->count];
}
return networkVideoAppendDamageRect(
damage, x, y, endX - x, endY - y);
}
static void networkVideoMergeDamage(LGNetVideoDamage * destination,
const LGNetVideoDamage * source)
{
if (!networkVideoDamagePresent(source) ||
networkVideoDamageFull(destination))
return;
if (networkVideoDamageFull(source))
{
memset(destination, 0, sizeof(*destination));
destination->flags =
(LGNetVideoDamageFlags)LG_NET_VIDEO_DAMAGE_FULL;
return;
}
for (uint16_t index = 0; index < source->count; ++index)
{
const LGNetVideoDamageRect * rect = &source->rects[index];
if (networkVideoUnionDamageRect(destination, rect))
continue;
uint32_t left = UINT32_MAX;
uint32_t top = UINT32_MAX;
uint32_t right = 0;
uint32_t bottom = 0;
for (unsigned set = 0; set < 2; ++set)
{
const LGNetVideoDamageRect * rects = set ?
source->rects : destination->rects;
const uint16_t count = set ?
source->count : destination->count;
for (uint16_t rectIndex = 0; rectIndex < count; ++rectIndex)
{
const LGNetVideoDamageRect * current = &rects[rectIndex];
const uint32_t currentRight =
current->x + current->width;
const uint32_t currentBottom =
current->y + current->height;
left = current->x < left ? current->x : left;
top = current->y < top ? current->y : top;
right = currentRight > right ? currentRight : right;
bottom = currentBottom > bottom ? currentBottom : bottom;
}
}
memset(destination, 0, sizeof(*destination));
destination->count = 1;
destination->rects[0] = (LGNetVideoDamageRect)
{
.x = (uint16_t)left,
.y = (uint16_t)top,
.width = (uint16_t)(right - left),
.height = (uint16_t)(bottom - top),
};
return;
}
}
static void networkVideoIntersectDamage( static void networkVideoIntersectDamage(
const LGNetVideoDamage * source, const LGNetVideoDamage * source,
const LGNetVideoDamage * reconstruction, const LGNetVideoDamage * reconstruction,
@@ -2074,10 +2250,20 @@ static void networkVideoBuildDamage(const LGNetVideoDamage * damage,
result->damageRectsCount = damage->count; result->damageRectsCount = damage->count;
} }
static struct NetworkVideoExternalFrame * networkVideoConvertNL( static bool networkVideoPresentNL(NetworkVideoDecoder * decoder,
NetworkVideoDecoder * decoder, bool chromaLeft, uint64_t generation, struct NetworkVideoTexture pooled, bool chromaLeft, uint64_t generation,
const LGNetVideoDamage * damage) const LGNetVideoDamage * damage,
struct NetworkVideoExternalFrame ** result)
{ {
*result = NULL;
struct NetworkVideoExternalFrame * frame = calloc(1, sizeof(*frame));
if (!frame)
{
networkVideoTexturePoolRecycleCurrent(decoder->texturePool,
pooled.texture, pooled.readySync, pooled.generation);
return false;
}
glUseProgram(decoder->yuvProgram); glUseProgram(decoder->yuvProgram);
for (unsigned component = 0; for (unsigned component = 0;
component < NETWORK_VIDEO_COMPONENTS; ++component) component < NETWORK_VIDEO_COMPONENTS; ++component)
@@ -2108,28 +2294,16 @@ static struct NetworkVideoExternalFrame * networkVideoConvertNL(
glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT | glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT |
GL_TEXTURE_FETCH_BARRIER_BIT); GL_TEXTURE_FETCH_BARRIER_BIT);
struct NetworkVideoTexture pooled =
networkVideoTexturePoolAcquire(decoder->texturePool);
if (!pooled.texture)
return NULL;
networkVideoCopyCanonicalNL(decoder, pooled.texture, networkVideoCopyCanonicalNL(decoder, pooled.texture,
pooled.generation, generation, damage); pooled.generation, generation, damage);
GLsync readySync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); GLsync readySync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
glFlush();
if (!readySync || glGetError() != GL_NO_ERROR) if (!readySync || glGetError() != GL_NO_ERROR)
{ {
free(frame);
networkVideoTexturePoolRecycleCurrent( networkVideoTexturePoolRecycleCurrent(
decoder->texturePool, pooled.texture, readySync, 0); decoder->texturePool, pooled.texture, readySync, 0);
return NULL; return false;
}
struct NetworkVideoExternalFrame * frame = calloc(1, sizeof(*frame));
if (!frame)
{
networkVideoTexturePoolRecycleCurrent(
decoder->texturePool, pooled.texture, readySync, 0);
return NULL;
} }
frame->base.ops = &NETWORK_VIDEO_EXTERNAL_OPS; frame->base.ops = &NETWORK_VIDEO_EXTERNAL_OPS;
frame->pool = decoder->texturePool; frame->pool = decoder->texturePool;
@@ -2138,7 +2312,43 @@ static struct NetworkVideoExternalFrame * networkVideoConvertNL(
frame->generation = generation; frame->generation = generation;
networkVideoTexturePoolRetain(frame->pool); networkVideoTexturePoolRetain(frame->pool);
atomic_init(&frame->references, 1U); atomic_init(&frame->references, 1U);
return frame; *result = frame;
return true;
}
static void networkVideoBuildFrameNL(NetworkVideoDecoder * decoder,
struct NetworkVideoExternalFrame * external,
const LGNetVideoDamage * damage, uint64_t decodeTime,
NetworkVideoDecodedFrame * result)
{
uint32_t formatVersion = (uint32_t)decoder->config.configEpoch;
if (!formatVersion)
formatVersion = 1;
result->externalFrame = &external->base;
result->decodeTimeNs = decodeTime;
networkVideoBuildDamage(damage, result);
result->format = (LG_TransportFrameFormat)
{
.version = formatVersion,
.type = FRAME_TYPE_RGBA16F,
.screenWidth = decoder->config.width,
.screenHeight = decoder->config.height,
.dataWidth = decoder->config.width,
.dataHeight = decoder->config.height,
.frameWidth = decoder->config.width,
.frameHeight = decoder->config.height,
.rotation = FRAME_ROT_0,
.stride = decoder->config.width,
.pitch = decoder->config.width * 8U,
.hdr = (decoder->config.flags & LG_NET_VIDEO_STREAM_HDR) != 0,
.hdrPQ = decoder->config.colorTransfer ==
LG_NET_COLOR_TRANSFER_PQ,
.hdrMetadata = false,
.sdrWhiteLevel = LG_SDR_WHITE_LEVEL_DEFAULT,
};
result->timing.providerValid = true;
result->timing.prepareTime = decodeTime;
} }
NetworkVideoDecodeResult networkVideoDecoderDecode( NetworkVideoDecodeResult networkVideoDecoderDecode(
@@ -2186,7 +2396,9 @@ NetworkVideoDecodeResult networkVideoDecoderDecode(
LG_UNLOCK(decoder->lock); LG_UNLOCK(decoder->lock);
return NETWORK_VIDEO_DECODE_ERROR; return NETWORK_VIDEO_DECODE_ERROR;
} }
if (update && !changedCount) const bool codecChanges = !update || changedCount;
if (!codecChanges &&
!networkVideoDamagePresent(&decoder->pendingDamage))
{ {
networkVideoCommitPendingPacketsNL(decoder); networkVideoCommitPendingPacketsNL(decoder);
decoder->referenceFrameID = frameID; decoder->referenceFrameID = frameID;
@@ -2194,10 +2406,13 @@ NetworkVideoDecodeResult networkVideoDecoderDecode(
return NETWORK_VIDEO_DECODE_NO_FRAME; return NETWORK_VIDEO_DECODE_NO_FRAME;
} }
if (update) if (codecChanges)
networkVideoPrepareDirtyNL(decoder); {
else if (update)
networkVideoClearDirtyNL(decoder); networkVideoPrepareDirtyNL(decoder);
else
networkVideoClearDirtyNL(decoder);
}
struct NetworkVideoCurrent previous; struct NetworkVideoCurrent previous;
if (!networkVideoMakeCurrent(decoder, &previous)) if (!networkVideoMakeCurrent(decoder, &previous))
@@ -2210,52 +2425,71 @@ NetworkVideoDecodeResult networkVideoDecoderDecode(
while (glGetError() != GL_NO_ERROR) while (glGetError() != GL_NO_ERROR)
{ {
} }
struct NetworkVideoUpload * upload = networkVideoUploadNL( struct NetworkVideoUpload * upload = NULL;
decoder, data, size); if (codecChanges)
if (!upload)
{ {
networkVideoDiscardPendingPacketsNL(decoder); upload = networkVideoUploadNL(decoder, data, size);
networkVideoRestoreCurrent(decoder, &previous); if (!upload)
LG_UNLOCK(decoder->lock); {
return NETWORK_VIDEO_DECODE_ERROR; networkVideoDiscardPendingPacketsNL(decoder);
networkVideoRestoreCurrent(decoder, &previous);
LG_UNLOCK(decoder->lock);
return NETWORK_VIDEO_DECODE_ERROR;
}
} }
const uint64_t decodeStart = nanotime(); const uint64_t decodeStart = nanotime();
uint64_t generation = decoder->outputGeneration + 1U; uint64_t generation = decoder->outputGeneration + 1U;
LGNetVideoDamage damage = { 0 };
if (!generation) if (!generation)
generation = 1U; generation = 1U;
networkVideoDequantNL(decoder, upload, update); if (codecChanges)
upload->readySync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); {
networkVideoIDWTNL(decoder, update); networkVideoDequantNL(decoder, upload, update);
LGNetVideoDamage reconstructionDamage; networkVideoIDWTNL(decoder, update);
LGNetVideoDamage damage; LGNetVideoDamage reconstructionDamage;
networkVideoBuildReconstructionDamageNL( networkVideoBuildReconstructionDamageNL(
decoder, update, &reconstructionDamage); decoder, update, &reconstructionDamage);
/* Wire damage accumulates against the sender's acknowledged reference. /* Wire damage accumulates against the sender's acknowledged reference.
* Limit it to output reconstructed against the decoder's current state. */ * Limit it to output reconstructed against the decoder's current state. */
if (update) if (update)
networkVideoIntersectDamage( networkVideoIntersectDamage(
&layout.damage, &reconstructionDamage, &damage); &layout.damage, &reconstructionDamage, &damage);
else else
damage = reconstructionDamage; damage = reconstructionDamage;
const bool hasPresentationDamage = }
networkVideoDamageFull(&damage) || damage.count;
struct NetworkVideoExternalFrame * external = NULL; LGNetVideoDamage presentationDamage = decoder->pendingDamage;
networkVideoMergeDamage(&presentationDamage, &damage);
const bool hasPresentationDamage =
networkVideoDamagePresent(&presentationDamage);
struct NetworkVideoTexture pooled = { 0 };
struct NetworkVideoExternalFrame * external = NULL;
bool presented = true;
if (hasPresentationDamage) if (hasPresentationDamage)
external = networkVideoConvertNL(decoder, {
layout.chromaLeft, generation, &damage); pooled = networkVideoTexturePoolAcquire(decoder->texturePool);
else if (pooled.texture)
glFlush(); presented = networkVideoPresentNL(decoder, pooled,
layout.chromaLeft, generation, &presentationDamage, &external);
}
if (upload)
upload->readySync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
glFlush();
const uint64_t decodeTime = nanotime() - decodeStart; const uint64_t decodeTime = nanotime() - decodeStart;
const bool success = const bool success = presented &&
(!hasPresentationDamage || external) && glGetError() == GL_NO_ERROR; (!upload || upload->readySync) && glGetError() == GL_NO_ERROR;
networkVideoRestoreCurrent(decoder, &previous); networkVideoRestoreCurrent(decoder, &previous);
if (!success) if (!success)
{ {
networkVideoDiscardPendingPacketsNL(decoder); networkVideoDiscardPendingPacketsNL(decoder);
decoder->referenceFrameID = 0; decoder->referenceFrameID = 0;
decoder->referenceValid = false; decoder->referenceValid = false;
decoder->pendingDecodeTimeNs = 0;
decoder->pendingChromaLeft = false;
memset(&decoder->pendingDamage, 0,
sizeof(decoder->pendingDamage));
if (external) if (external)
networkVideoExternalRelease(&external->base); networkVideoExternalRelease(&external->base);
LG_UNLOCK(decoder->lock); LG_UNLOCK(decoder->lock);
@@ -2270,41 +2504,111 @@ NetworkVideoDecodeResult networkVideoDecoderDecode(
LG_UNLOCK(decoder->lock); LG_UNLOCK(decoder->lock);
return NETWORK_VIDEO_DECODE_NO_FRAME; return NETWORK_VIDEO_DECODE_NO_FRAME;
} }
if (!pooled.texture)
{
decoder->pendingDamage = presentationDamage;
decoder->pendingDecodeTimeNs = networkVideoSaturatingAdd(
decoder->pendingDecodeTimeNs, decodeTime);
decoder->pendingChromaLeft = layout.chromaLeft;
LG_UNLOCK(decoder->lock);
return NETWORK_VIDEO_DECODE_DEFERRED;
}
const uint64_t totalDecodeTime = networkVideoSaturatingAdd(
decoder->pendingDecodeTimeNs, decodeTime);
memset(&decoder->pendingDamage, 0, sizeof(decoder->pendingDamage));
decoder->pendingDecodeTimeNs = 0;
decoder->pendingChromaLeft = false;
struct NetworkVideoDamageHistory * history = struct NetworkVideoDamageHistory * history =
&decoder->damageHistory[generation % NETWORK_VIDEO_DAMAGE_HISTORY]; &decoder->damageHistory[generation % NETWORK_VIDEO_DAMAGE_HISTORY];
history->generation = generation; history->generation = generation;
history->damage = damage; history->damage = presentationDamage;
decoder->outputGeneration = generation; decoder->outputGeneration = generation;
uint32_t formatVersion = (uint32_t)config->configEpoch; networkVideoBuildFrameNL(
if (!formatVersion) decoder, external, &presentationDamage, totalDecodeTime, result);
formatVersion = 1; LG_UNLOCK(decoder->lock);
return NETWORK_VIDEO_DECODE_OK;
}
result->externalFrame = &external->base; NetworkVideoDecodeResult networkVideoDecoderPresentPending(
result->decodeTimeNs = decodeTime; NetworkVideoDecoder * decoder, NetworkVideoDecodedFrame * result)
if (update) {
networkVideoBuildDamage(&damage, result); if (!decoder || !result)
result->format = (LG_TransportFrameFormat) return NETWORK_VIDEO_DECODE_ERROR;
memset(result, 0, sizeof(*result));
LG_LOCK(decoder->lock);
if (!decoder->configured)
{ {
.version = formatVersion, LG_UNLOCK(decoder->lock);
.type = FRAME_TYPE_RGBA16F, return NETWORK_VIDEO_DECODE_UNSUPPORTED;
.screenWidth = config->width, }
.screenHeight = config->height, if (!networkVideoDamagePresent(&decoder->pendingDamage))
.dataWidth = config->width, {
.dataHeight = config->height, LG_UNLOCK(decoder->lock);
.frameWidth = config->width, return NETWORK_VIDEO_DECODE_NO_FRAME;
.frameHeight = config->height, }
.rotation = FRAME_ROT_0,
.stride = config->width, struct NetworkVideoCurrent previous;
.pitch = config->width * 8U, if (!networkVideoMakeCurrent(decoder, &previous))
.hdr = (config->flags & LG_NET_VIDEO_STREAM_HDR) != 0, {
.hdrPQ = config->colorTransfer == LG_NET_COLOR_TRANSFER_PQ, LG_UNLOCK(decoder->lock);
.hdrMetadata = false, return NETWORK_VIDEO_DECODE_ERROR;
.sdrWhiteLevel = LG_SDR_WHITE_LEVEL_DEFAULT, }
};
result->timing.providerValid = true; while (glGetError() != GL_NO_ERROR)
result->timing.prepareTime = decodeTime; {
}
struct NetworkVideoTexture pooled =
networkVideoTexturePoolAcquire(decoder->texturePool);
if (!pooled.texture)
{
networkVideoRestoreCurrent(decoder, &previous);
LG_UNLOCK(decoder->lock);
return NETWORK_VIDEO_DECODE_DEFERRED;
}
uint64_t generation = decoder->outputGeneration + 1U;
if (!generation)
generation = 1U;
const uint64_t presentStart = nanotime();
struct NetworkVideoExternalFrame * external = NULL;
const bool presented = networkVideoPresentNL(
decoder, pooled,
decoder->pendingChromaLeft, generation, &decoder->pendingDamage,
&external);
glFlush();
const uint64_t presentTime = nanotime() - presentStart;
const bool success = presented && glGetError() == GL_NO_ERROR;
networkVideoRestoreCurrent(decoder, &previous);
if (!success)
{
decoder->referenceFrameID = 0;
decoder->referenceValid = false;
decoder->pendingDecodeTimeNs = 0;
decoder->pendingChromaLeft = false;
memset(&decoder->pendingDamage, 0,
sizeof(decoder->pendingDamage));
if (external)
networkVideoExternalRelease(&external->base);
LG_UNLOCK(decoder->lock);
return NETWORK_VIDEO_DECODE_ERROR;
}
const LGNetVideoDamage damage = decoder->pendingDamage;
const uint64_t decodeTime = networkVideoSaturatingAdd(
decoder->pendingDecodeTimeNs, presentTime);
struct NetworkVideoDamageHistory * history =
&decoder->damageHistory[generation % NETWORK_VIDEO_DAMAGE_HISTORY];
history->generation = generation;
history->damage = damage;
decoder->outputGeneration = generation;
memset(&decoder->pendingDamage, 0, sizeof(decoder->pendingDamage));
decoder->pendingDecodeTimeNs = 0;
decoder->pendingChromaLeft = false;
networkVideoBuildFrameNL(
decoder, external, &damage, decodeTime, result);
LG_UNLOCK(decoder->lock); LG_UNLOCK(decoder->lock);
return NETWORK_VIDEO_DECODE_OK; return NETWORK_VIDEO_DECODE_OK;
} }