[client/idd] network: preserve exact frame damage

This commit is contained in:
Geoffrey McRae
2026-10-08 14:18:46 +11:00
parent ae031f6064
commit ccff282cd0
14 changed files with 519 additions and 293 deletions

View File

@@ -33,7 +33,7 @@
#include "interface/external_frame.h" #include "interface/external_frame.h"
#include "interface/input.h" #include "interface/input.h"
#define LG_TRANSPORT_MAX_DAMAGE_RECTS LG_MAX_FRAME_DAMAGE_RECTS #define LG_TRANSPORT_MAX_DAMAGE_RECTS LG_MAX_RUNTIME_DAMAGE_RECTS
typedef struct LG_Transport LG_Transport; typedef struct LG_Transport LG_Transport;
typedef struct LG_RendererInterop LG_RendererInterop; typedef struct LG_RendererInterop LG_RendererInterop;

View File

@@ -88,7 +88,8 @@ bool egl_damageInit(EGL_Damage ** damage)
return false; return false;
} }
if (!egl_desktopRectsInit(&(*damage)->mesh, LG_MAX_FRAME_DAMAGE_RECTS)) if (!egl_desktopRectsInit(&(*damage)->mesh,
LG_MAX_RUNTIME_DAMAGE_RECTS))
{ {
DEBUG_ERROR("Failed to initialize the mesh"); DEBUG_ERROR("Failed to initialize the mesh");
return false; return false;

View File

@@ -29,7 +29,7 @@ struct DesktopDamage
{ {
LG_RendererFrameToken frameToken; LG_RendererFrameToken frameToken;
int count; int count;
KVMFRFrameDamageRect rects[LG_MAX_FRAME_DAMAGE_RECTS]; KVMFRFrameDamageRect rects[LG_MAX_RUNTIME_DAMAGE_RECTS];
}; };
typedef struct EGL_Damage EGL_Damage; typedef struct EGL_Damage EGL_Damage;

View File

@@ -58,10 +58,11 @@
#include "postprocess.h" #include "postprocess.h"
#include "util.h" #include "util.h"
#define MAX_BUFFER_AGE 3 #define MAX_BUFFER_AGE 3
#define DESKTOP_DAMAGE_COUNT 4 #define DESKTOP_DAMAGE_COUNT 4
#define DESKTOP_DAMAGE_MARGIN 1 #define DESKTOP_DAMAGE_MARGIN 1
#define MAX_ACCUMULATED_DAMAGE ((LG_MAX_FRAME_DAMAGE_RECTS + MAX_OVERLAY_RECTS + 2) * MAX_BUFFER_AGE) #define MAX_ACCUMULATED_DAMAGE \
((LG_MAX_RUNTIME_DAMAGE_RECTS + MAX_OVERLAY_RECTS + 2) * MAX_BUFFER_AGE)
#define IDX_AGO(counter, i, total) (((counter) + (total) - (i)) % (total)) #define IDX_AGO(counter, i, total) (((counter) + (total) - (i)) % (total))
struct Options struct Options
@@ -474,7 +475,7 @@ static void egl_addSwSurfaceDamage(
damage->frameToken = LG_RENDERER_FRAME_TOKEN_NONE; damage->frameToken = LG_RENDERER_FRAME_TOKEN_NONE;
if (damage->count >= 0) if (damage->count >= 0)
{ {
if (damage->count + 1 >= LG_MAX_FRAME_DAMAGE_RECTS) if (damage->count + 1 > LG_MAX_RUNTIME_DAMAGE_RECTS)
damage->count = -1; damage->count = -1;
else else
damage->rects[damage->count++] = rect; damage->rects[damage->count++] = rect;
@@ -880,7 +881,7 @@ static bool egl_onFrame(LG_Renderer * renderer,
if (unlikely( if (unlikely(
damage->count == -1 || damage->count == -1 ||
damageRectsCount == 0 || damageRectsCount == 0 ||
damage->count + damageRectsCount >= LG_MAX_FRAME_DAMAGE_RECTS)) damage->count + damageRectsCount > LG_MAX_RUNTIME_DAMAGE_RECTS))
{ {
damage->count = -1; damage->count = -1;
} }
@@ -1443,7 +1444,7 @@ static void egl_requeueDesktopDamage(
this->desktopDamage + this->desktopDamageIdx; this->desktopDamage + this->desktopDamageIdx;
if (damage->count < 0 || pending->count < 0 || if (damage->count < 0 || pending->count < 0 ||
damage->count + pending->count >= LG_MAX_FRAME_DAMAGE_RECTS) damage->count + pending->count > LG_MAX_RUNTIME_DAMAGE_RECTS)
pending->count = -1; pending->count = -1;
else else
{ {
@@ -1698,8 +1699,8 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
/* The diagnostics being displayed must not contribute to Compose. */ /* The diagnostics being displayed must not contribute to Compose. */
timing->composeTime = nanotime() - composeStart; timing->composeTime = nanotime() - composeStart;
struct Rect damage[LG_MAX_FRAME_DAMAGE_RECTS + MAX_OVERLAY_RECTS + 2]; struct Rect damage[LG_MAX_RUNTIME_DAMAGE_RECTS + MAX_OVERLAY_RECTS + 2];
int damageIdx = app_renderOverlay(damage, MAX_OVERLAY_RECTS); int damageIdx = app_renderOverlay(damage, MAX_OVERLAY_RECTS);
if (unlikely(damageIdx != 0)) if (unlikely(damageIdx != 0))
{ {
if (damageIdx == -1) if (damageIdx == -1)

View File

@@ -27,7 +27,7 @@
struct TexDamage struct TexDamage
{ {
int count; int count;
KVMFRFrameDamageRect rects[LG_MAX_FRAME_DAMAGE_RECTS]; KVMFRFrameDamageRect rects[LG_MAX_RUNTIME_DAMAGE_RECTS];
}; };
typedef struct TexFB typedef struct TexFB
@@ -87,10 +87,11 @@ static bool egl_texFBUpdate(EGL_Texture * texture, const EGL_TexUpdate * update)
if (!egl_texBufferStreamLock(parent)) if (!egl_texBufferStreamLock(parent))
return false; return false;
struct TexDamage * damage = this->damage + parent->bufIndex; struct TexDamage * damage = this->damage + parent->bufIndex;
bool damageAll = !update->rects || update->rectCount == 0 || damage->count < 0 || bool damageAll =
damage->count + update->rectCount > LG_MAX_FRAME_DAMAGE_RECTS; !update->rects || update->rectCount == 0 || damage->count < 0 ||
bool complete; damage->count + update->rectCount > LG_MAX_RUNTIME_DAMAGE_RECTS;
bool complete;
if (damageAll) if (damageAll)
{ {
@@ -172,7 +173,7 @@ static bool egl_texFBUpdate(EGL_Texture * texture, const EGL_TexUpdate * update)
if (i == parent->bufIndex) if (i == parent->bufIndex)
damage->count = 0; damage->count = 0;
else if (update->rects && update->rectCount > 0 && damage->count >= 0 && else if (update->rects && update->rectCount > 0 && damage->count >= 0 &&
damage->count + update->rectCount <= LG_MAX_FRAME_DAMAGE_RECTS) damage->count + update->rectCount <= LG_MAX_RUNTIME_DAMAGE_RECTS)
{ {
memcpy(damage->rects + damage->count, update->rects, memcpy(damage->rects + damage->count, update->rects,
update->rectCount * sizeof(KVMFRFrameDamageRect)); update->rectCount * sizeof(KVMFRFrameDamageRect));

View File

@@ -147,7 +147,7 @@ typedef struct RenderQueueSwSurface
bool damageFull; bool damageFull;
int damageCount; int damageCount;
KVMFRFrameDamageRect damage[LG_MAX_FRAME_DAMAGE_RECTS]; KVMFRFrameDamageRect damage[LG_MAX_RUNTIME_DAMAGE_RECTS];
bool updateQueued; bool updateQueued;
RenderCommand updateCommand; RenderCommand updateCommand;
@@ -361,7 +361,7 @@ static void swSurfaceDamageAdd(RenderQueueSwSurface * surface,
i = 0; i = 0;
} }
if (surface->damageCount < LG_MAX_FRAME_DAMAGE_RECTS) if (surface->damageCount < LG_MAX_RUNTIME_DAMAGE_RECTS)
break; break;
int best = 0; int best = 0;

View File

@@ -300,7 +300,7 @@ static void networkVideoMergeDamage(NetworkVideoDecodedFrame * destination,
return; return;
} }
if (destination->damageRectsCount >= if (destination->damageRectsCount >
LG_TRANSPORT_MAX_DAMAGE_RECTS - source->damageRectsCount) LG_TRANSPORT_MAX_DAMAGE_RECTS - source->damageRectsCount)
{ {
uint32_t left = UINT32_MAX; uint32_t left = UINT32_MAX;

View File

@@ -68,10 +68,10 @@
#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_SIZE 3U #define NETWORK_VIDEO_POOL_SIZE 3U
#define NETWORK_VIDEO_DAMAGE_HISTORY 64U
#define NETWORK_VIDEO_POOL_BYTE_LIMIT \ #define NETWORK_VIDEO_POOL_BYTE_LIMIT \
(UINT64_C(192) * 1024U * 1024U) (UINT64_C(192) * 1024U * 1024U)
#define NETWORK_VIDEO_PIXEL_BYTES 8U #define NETWORK_VIDEO_PIXEL_BYTES 8U
#define NETWORK_VIDEO_TILE_SIZE 32U
#define NETWORK_VIDEO_SEQUENCE_BITS (UINT32_C(7) << 28U) #define NETWORK_VIDEO_SEQUENCE_BITS (UINT32_C(7) << 28U)
struct NetworkVideoBlockMeta struct NetworkVideoBlockMeta
@@ -119,6 +119,12 @@ struct NetworkVideoTexture
uint64_t generation; uint64_t generation;
}; };
struct NetworkVideoDamageHistory
{
uint64_t generation;
LGNetVideoDamage damage;
};
struct NetworkVideoTexturePool struct NetworkVideoTexturePool
{ {
LG_Lock lock; LG_Lock lock;
@@ -165,6 +171,7 @@ struct NetworkVideoUniforms
GLint crPlane; GLint crPlane;
GLint outputSize; GLint outputSize;
GLint outputOffset; GLint outputOffset;
GLint outputExtent;
GLint bt2020; GLint bt2020;
GLint chromaLeft; GLint chromaLeft;
GLint lumaOffset; GLint lumaOffset;
@@ -173,6 +180,14 @@ struct NetworkVideoUniforms
GLint chromaScale; GLint chromaScale;
} }
yuv; yuv;
struct
{
GLint inputImage;
GLint outputOffset;
GLint outputExtent;
}
copy;
}; };
struct NetworkVideoDecoder struct NetworkVideoDecoder
@@ -187,8 +202,10 @@ struct NetworkVideoDecoder
GLuint dequantProgram; GLuint dequantProgram;
GLuint idwtProgram; GLuint idwtProgram;
GLuint yuvProgram; GLuint yuvProgram;
GLuint copyProgram;
GLuint coefficientTextures[NETWORK_VIDEO_LEVELS]; GLuint coefficientTextures[NETWORK_VIDEO_LEVELS];
GLuint planeTextures [NETWORK_VIDEO_COMPONENTS]; GLuint planeTextures [NETWORK_VIDEO_COMPONENTS];
GLuint canonicalTexture;
struct NetworkVideoUpload uploads [NETWORK_VIDEO_UPLOADS]; struct NetworkVideoUpload uploads [NETWORK_VIDEO_UPLOADS];
struct NetworkVideoTexturePool * texturePool; struct NetworkVideoTexturePool * texturePool;
struct NetworkVideoUniforms uniforms; struct NetworkVideoUniforms uniforms;
@@ -198,8 +215,8 @@ struct NetworkVideoDecoder
uint8_t * dirtyBlocks; uint8_t * dirtyBlocks;
struct NetworkVideoDirtyGrid struct NetworkVideoDirtyGrid
dirty[NETWORK_VIDEO_COMPONENTS][NETWORK_VIDEO_LEVELS]; dirty[NETWORK_VIDEO_COMPONENTS][NETWORK_VIDEO_LEVELS];
uint8_t * outputDirty; struct NetworkVideoDamageHistory
uint64_t * outputTileGenerations; damageHistory[NETWORK_VIDEO_DAMAGE_HISTORY];
LGNetVideoStreamConfig config; LGNetVideoStreamConfig config;
struct NetworkVideoBlockMeta struct NetworkVideoBlockMeta
@@ -209,8 +226,6 @@ struct NetworkVideoDecoder
uint32_t alignedWidth; uint32_t alignedWidth;
uint32_t alignedHeight; uint32_t alignedHeight;
uint32_t blockCapacity; uint32_t blockCapacity;
uint32_t outputTileWidth;
uint32_t outputTileHeight;
uint64_t referenceFrameID; uint64_t referenceFrameID;
uint64_t outputGeneration; uint64_t outputGeneration;
unsigned uploadIndex; unsigned uploadIndex;
@@ -518,6 +533,8 @@ static void networkVideoDeleteConfigurationNL(NetworkVideoDecoder * decoder)
{ {
glDeleteTextures(NETWORK_VIDEO_LEVELS, decoder->coefficientTextures); glDeleteTextures(NETWORK_VIDEO_LEVELS, decoder->coefficientTextures);
glDeleteTextures(NETWORK_VIDEO_COMPONENTS, decoder->planeTextures); glDeleteTextures(NETWORK_VIDEO_COMPONENTS, decoder->planeTextures);
if (decoder->canonicalTexture)
glDeleteTextures(1, &decoder->canonicalTexture);
for (unsigned index = 0; index < NETWORK_VIDEO_UPLOADS; ++index) for (unsigned index = 0; index < NETWORK_VIDEO_UPLOADS; ++index)
{ {
struct NetworkVideoUpload * upload = &decoder->uploads[index]; struct NetworkVideoUpload * upload = &decoder->uploads[index];
@@ -537,28 +554,24 @@ static void networkVideoDeleteConfigurationNL(NetworkVideoDecoder * decoder)
component < NETWORK_VIDEO_COMPONENTS; ++component) component < NETWORK_VIDEO_COMPONENTS; ++component)
for (unsigned level = 0; level < NETWORK_VIDEO_LEVELS; ++level) for (unsigned level = 0; level < NETWORK_VIDEO_LEVELS; ++level)
free(decoder->dirty[component][level].tiles); free(decoder->dirty[component][level].tiles);
free(decoder->outputDirty);
free(decoder->outputTileGenerations);
free(decoder->offsets); free(decoder->offsets);
memset(decoder->uploads, 0, sizeof(decoder->uploads)); memset(decoder->uploads, 0, sizeof(decoder->uploads));
decoder->texturePool = NULL; memset(decoder->damageHistory, 0, sizeof(decoder->damageHistory));
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->outputTileGenerations = NULL; decoder->canonicalTexture = 0;
decoder->alignedWidth = 0; decoder->alignedWidth = 0;
decoder->alignedHeight = 0; decoder->alignedHeight = 0;
decoder->blockCapacity = 0; decoder->blockCapacity = 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->uploadIndex = 0; decoder->referenceValid = false;
decoder->configured = false;
decoder->referenceValid = 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));
@@ -575,9 +588,12 @@ static void networkVideoDeleteProgramsNL(NetworkVideoDecoder * decoder)
glDeleteProgram(decoder->idwtProgram); glDeleteProgram(decoder->idwtProgram);
if (decoder->yuvProgram) if (decoder->yuvProgram)
glDeleteProgram(decoder->yuvProgram); glDeleteProgram(decoder->yuvProgram);
if (decoder->copyProgram)
glDeleteProgram(decoder->copyProgram);
decoder->dequantProgram = 0; decoder->dequantProgram = 0;
decoder->idwtProgram = 0; decoder->idwtProgram = 0;
decoder->yuvProgram = 0; decoder->yuvProgram = 0;
decoder->copyProgram = 0;
memset(&decoder->uniforms, 0, sizeof(decoder->uniforms)); memset(&decoder->uniforms, 0, sizeof(decoder->uniforms));
} }
@@ -619,6 +635,8 @@ static bool networkVideoCacheUniformsNL(NetworkVideoDecoder * decoder)
decoder->yuvProgram, "outputSize"); decoder->yuvProgram, "outputSize");
decoder->uniforms.yuv.outputOffset = glGetUniformLocation( decoder->uniforms.yuv.outputOffset = glGetUniformLocation(
decoder->yuvProgram, "outputOffset"); decoder->yuvProgram, "outputOffset");
decoder->uniforms.yuv.outputExtent = glGetUniformLocation(
decoder->yuvProgram, "outputExtent");
decoder->uniforms.yuv.bt2020 = glGetUniformLocation( decoder->uniforms.yuv.bt2020 = glGetUniformLocation(
decoder->yuvProgram, "bt2020"); decoder->yuvProgram, "bt2020");
decoder->uniforms.yuv.chromaLeft = glGetUniformLocation( decoder->uniforms.yuv.chromaLeft = glGetUniformLocation(
@@ -632,6 +650,13 @@ static bool networkVideoCacheUniformsNL(NetworkVideoDecoder * decoder)
decoder->uniforms.yuv.chromaScale = glGetUniformLocation( decoder->uniforms.yuv.chromaScale = glGetUniformLocation(
decoder->yuvProgram, "chromaScale"); decoder->yuvProgram, "chromaScale");
decoder->uniforms.copy.inputImage = glGetUniformLocation(
decoder->copyProgram, "inputImage");
decoder->uniforms.copy.outputOffset = glGetUniformLocation(
decoder->copyProgram, "outputOffset");
decoder->uniforms.copy.outputExtent = glGetUniformLocation(
decoder->copyProgram, "outputExtent");
const struct NetworkVideoUniforms * uniforms = &decoder->uniforms; const struct NetworkVideoUniforms * uniforms = &decoder->uniforms;
if (uniforms->dequant.resolution < 0 || if (uniforms->dequant.resolution < 0 ||
uniforms->dequant.outputLayer < 0 || uniforms->dequant.outputLayer < 0 ||
@@ -648,11 +673,15 @@ static bool networkVideoCacheUniformsNL(NetworkVideoDecoder * decoder)
uniforms->yuv.lumaPlane < 0 || uniforms->yuv.lumaPlane < 0 ||
uniforms->yuv.cbPlane < 0 || uniforms->yuv.crPlane < 0 || uniforms->yuv.cbPlane < 0 || uniforms->yuv.crPlane < 0 ||
uniforms->yuv.outputSize < 0 || uniforms->yuv.outputOffset < 0 || uniforms->yuv.outputSize < 0 || uniforms->yuv.outputOffset < 0 ||
uniforms->yuv.outputExtent < 0 ||
uniforms->yuv.bt2020 < 0 || uniforms->yuv.bt2020 < 0 ||
uniforms->yuv.chromaLeft < 0 || uniforms->yuv.lumaOffset < 0 || uniforms->yuv.chromaLeft < 0 || uniforms->yuv.lumaOffset < 0 ||
uniforms->yuv.lumaScale < 0 || uniforms->yuv.lumaScale < 0 ||
uniforms->yuv.chromaMidpoint < 0 || uniforms->yuv.chromaMidpoint < 0 ||
uniforms->yuv.chromaScale < 0) uniforms->yuv.chromaScale < 0 ||
uniforms->copy.inputImage < 0 ||
uniforms->copy.outputOffset < 0 ||
uniforms->copy.outputExtent < 0)
return false; return false;
glUseProgram(decoder->idwtProgram); glUseProgram(decoder->idwtProgram);
@@ -661,6 +690,8 @@ static bool networkVideoCacheUniformsNL(NetworkVideoDecoder * decoder)
glUniform1i(uniforms->yuv.lumaPlane, 0); glUniform1i(uniforms->yuv.lumaPlane, 0);
glUniform1i(uniforms->yuv.cbPlane, 1); glUniform1i(uniforms->yuv.cbPlane, 1);
glUniform1i(uniforms->yuv.crPlane, 2); glUniform1i(uniforms->yuv.crPlane, 2);
glUseProgram(decoder->copyProgram);
glUniform1i(uniforms->copy.inputImage, 0);
return glGetError() == GL_NO_ERROR; return glGetError() == GL_NO_ERROR;
} }
@@ -824,31 +855,22 @@ static bool networkVideoAllocateNL(NetworkVideoDecoder * decoder)
return false; return false;
} }
decoder->offsets = malloc( if (!networkVideoCreateTexture2D(&decoder->canonicalTexture, GL_RGBA16F,
decoder->config.width, decoder->config.height, GL_LINEAR))
return false;
decoder->offsets = malloc(
(size_t)decoder->blockCapacity * sizeof(*decoder->offsets)); (size_t)decoder->blockCapacity * sizeof(*decoder->offsets));
decoder->packetStates = calloc( decoder->packetStates = calloc(
decoder->blockCapacity, sizeof(*decoder->packetStates)); decoder->blockCapacity, sizeof(*decoder->packetStates));
decoder->pendingPackets = calloc( decoder->pendingPackets = calloc(
decoder->blockCapacity, sizeof(*decoder->pendingPackets)); decoder->blockCapacity, sizeof(*decoder->pendingPackets));
decoder->dirtyBlocks = calloc( decoder->dirtyBlocks = calloc(
decoder->blockCapacity, sizeof(*decoder->dirtyBlocks)); decoder->blockCapacity, sizeof(*decoder->dirtyBlocks));
decoder->texturePool = networkVideoTexturePoolCreate(decoder); decoder->texturePool = networkVideoTexturePoolCreate(decoder);
decoder->outputTileWidth =
(decoder->config.width + NETWORK_VIDEO_TILE_SIZE - 1U) /
NETWORK_VIDEO_TILE_SIZE;
decoder->outputTileHeight =
(decoder->config.height + NETWORK_VIDEO_TILE_SIZE - 1U) /
NETWORK_VIDEO_TILE_SIZE;
const size_t outputTileCount =
(size_t)decoder->outputTileWidth * decoder->outputTileHeight;
decoder->outputDirty = calloc(
outputTileCount, sizeof(*decoder->outputDirty));
decoder->outputTileGenerations = calloc(
outputTileCount, sizeof(*decoder->outputTileGenerations));
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->outputTileGenerations)
return false; return false;
for (unsigned component = 0; for (unsigned component = 0;
@@ -1105,8 +1127,12 @@ bool networkVideoDecoderAttach(NetworkVideoDecoder * decoder,
NETWORK_VIDEO_YUV_SOURCES, NETWORK_VIDEO_YUV_SOURCES,
sizeof(NETWORK_VIDEO_YUV_SOURCES) / sizeof(NETWORK_VIDEO_YUV_SOURCES) /
sizeof(NETWORK_VIDEO_YUV_SOURCES[0])); sizeof(NETWORK_VIDEO_YUV_SOURCES[0]));
decoder->copyProgram = networkVideoCreateProgram(
NETWORK_VIDEO_COPY_SOURCES,
sizeof(NETWORK_VIDEO_COPY_SOURCES) /
sizeof(NETWORK_VIDEO_COPY_SOURCES[0]));
const bool programsReady = decoder->dequantProgram && const bool programsReady = decoder->dequantProgram &&
decoder->idwtProgram && decoder->yuvProgram && decoder->idwtProgram && decoder->yuvProgram && decoder->copyProgram &&
networkVideoCacheUniformsNL(decoder); networkVideoCacheUniformsNL(decoder);
networkVideoRestoreCurrent(decoder, &previous); networkVideoRestoreCurrent(decoder, &previous);
if (!programsReady) if (!programsReady)
@@ -1304,8 +1330,6 @@ static void networkVideoClearDirtyNL(NetworkVideoDecoder * decoder)
grid->count = 0; grid->count = 0;
} }
} }
memset(decoder->outputDirty, 0,
(size_t)decoder->outputTileWidth * decoder->outputTileHeight);
} }
static uint32_t networkVideoMirrorTexel( static uint32_t networkVideoMirrorTexel(
@@ -1383,44 +1407,6 @@ static void networkVideoMarkGridRect(struct NetworkVideoDirtyGrid * grid,
} }
} }
static void networkVideoMarkOutputRect(NetworkVideoDecoder * decoder,
unsigned component, uint32_t x, uint32_t y,
uint32_t width, uint32_t height)
{
uint32_t expansion = component ? 1U : 0U;
if (component &&
decoder->config.chromaSubsampling == LG_NET_VIDEO_CHROMA_420)
{
x *= 2U;
y *= 2U;
width *= 2U;
height *= 2U;
expansion = 2U;
}
const uint32_t startX = x > expansion ? x - expansion : 0U;
const uint32_t startY = y > expansion ? y - expansion : 0U;
const uint64_t rawEndX = (uint64_t)x + width + expansion;
const uint64_t rawEndY = (uint64_t)y + height + expansion;
const uint32_t endX = rawEndX < decoder->config.width ?
(uint32_t)rawEndX : decoder->config.width;
const uint32_t endY = rawEndY < decoder->config.height ?
(uint32_t)rawEndY : decoder->config.height;
if (startX >= endX || startY >= endY)
return;
const uint32_t tileStartX = startX / NETWORK_VIDEO_TILE_SIZE;
const uint32_t tileStartY = startY / NETWORK_VIDEO_TILE_SIZE;
const uint32_t tileEndX =
(endX + NETWORK_VIDEO_TILE_SIZE - 1U) / NETWORK_VIDEO_TILE_SIZE;
const uint32_t tileEndY =
(endY + NETWORK_VIDEO_TILE_SIZE - 1U) / NETWORK_VIDEO_TILE_SIZE;
for (uint32_t tileY = tileStartY; tileY < tileEndY; ++tileY)
for (uint32_t tileX = tileStartX; tileX < tileEndX; ++tileX)
decoder->outputDirty[
(size_t)tileY * decoder->outputTileWidth + tileX] = 1U;
}
static void networkVideoPrepareDirtyNL(NetworkVideoDecoder * decoder) static void networkVideoPrepareDirtyNL(NetworkVideoDecoder * decoder)
{ {
networkVideoClearDirtyNL(decoder); networkVideoClearDirtyNL(decoder);
@@ -1573,6 +1559,9 @@ static void networkVideoDequantNL(NetworkVideoDecoder * decoder,
static void networkVideoPropagateDirtyNL(NetworkVideoDecoder * decoder, static void networkVideoPropagateDirtyNL(NetworkVideoDecoder * decoder,
unsigned component, unsigned level, bool final) unsigned component, unsigned level, bool final)
{ {
if (final)
return;
const struct NetworkVideoDirtyGrid * grid = const struct NetworkVideoDirtyGrid * grid =
&decoder->dirty[component][level]; &decoder->dirty[component][level];
const uint32_t outputWidth = decoder->alignedWidth >> level; const uint32_t outputWidth = decoder->alignedWidth >> level;
@@ -1589,13 +1578,9 @@ static void networkVideoPropagateDirtyNL(NetworkVideoDecoder * decoder,
(outputWidth - outputX < 32U ? outputWidth - outputX : 32U) : 0U; (outputWidth - outputX < 32U ? outputWidth - outputX : 32U) : 0U;
const uint32_t height = outputY < outputHeight ? const uint32_t height = outputY < outputHeight ?
(outputHeight - outputY < 32U ? outputHeight - outputY : 32U) : 0U; (outputHeight - outputY < 32U ? outputHeight - outputY : 32U) : 0U;
if (final) networkVideoMarkGridRect(&decoder->dirty[component][level - 1U],
networkVideoMarkOutputRect( outputWidth, outputHeight,
decoder, component, outputX, outputY, width, height); outputX, outputY, width, height);
else
networkVideoMarkGridRect(&decoder->dirty[component][level - 1U],
outputWidth, outputHeight,
outputX, outputY, width, height);
} }
} }
} }
@@ -1676,10 +1661,6 @@ static void networkVideoIDWTNL(NetworkVideoDecoder * decoder, bool update)
GL_TEXTURE_FETCH_BARRIER_BIT); GL_TEXTURE_FETCH_BARRIER_BIT);
} }
} }
if (!update)
memset(decoder->outputDirty, 1,
(size_t)decoder->outputTileWidth * decoder->outputTileHeight);
} }
static void networkVideoColorRange( static void networkVideoColorRange(
@@ -1716,120 +1697,125 @@ static void networkVideoColorRange(
} }
} }
static bool networkVideoTextureTileDirty( static bool networkVideoDamageFull(const LGNetVideoDamage * damage)
const NetworkVideoDecoder * decoder, size_t index,
uint64_t textureGeneration)
{ {
return decoder->outputDirty[index] || return (damage->flags & LG_NET_VIDEO_DAMAGE_FULL) != 0;
decoder->outputTileGenerations[index] > textureGeneration;
} }
static void networkVideoBuildDamageNL(NetworkVideoDecoder * decoder, static void networkVideoDispatchRect(GLint offsetUniform,
NetworkVideoDecodedFrame * result) GLint extentUniform, uint32_t x, uint32_t y,
uint32_t width, uint32_t height)
{ {
uint32_t count = 0; glUniform2i(offsetUniform, (GLint)x, (GLint)y);
bool overflow = false; glUniform2i(extentUniform, (GLint)width, (GLint)height);
for (uint32_t tileY = 0; glDispatchCompute((width + 7U) / 8U, (height + 7U) / 8U, 1);
tileY < decoder->outputTileHeight && !overflow; ++tileY) }
{
uint32_t tileX = 0;
while (tileX < decoder->outputTileWidth)
{
const size_t row = (size_t)tileY * decoder->outputTileWidth;
while (tileX < decoder->outputTileWidth &&
!decoder->outputDirty[row + tileX])
++tileX;
const uint32_t start = tileX;
while (tileX < decoder->outputTileWidth &&
decoder->outputDirty[row + tileX])
++tileX;
if (start == tileX)
continue;
const uint32_t x = start * NETWORK_VIDEO_TILE_SIZE; static void networkVideoDispatchDamageNL(NetworkVideoDecoder * decoder,
const uint32_t y = tileY * NETWORK_VIDEO_TILE_SIZE; GLint offsetUniform, GLint extentUniform,
const uint32_t endX = tileX * NETWORK_VIDEO_TILE_SIZE < const LGNetVideoDamage * damage)
decoder->config.width ? {
tileX * NETWORK_VIDEO_TILE_SIZE : decoder->config.width; if (networkVideoDamageFull(damage))
const uint32_t endY = y + NETWORK_VIDEO_TILE_SIZE <
decoder->config.height ?
y + NETWORK_VIDEO_TILE_SIZE : decoder->config.height;
bool merged = false;
for (uint32_t index = 0; index < count; ++index)
{
KVMFRFrameDamageRect * rect = &result->damageRects[index];
if (rect->x == x && rect->width == endX - x &&
rect->y + rect->height == y)
{
rect->height += endY - y;
merged = true;
break;
}
}
if (merged)
continue;
if (count + 1U >= LG_TRANSPORT_MAX_DAMAGE_RECTS)
{
overflow = true;
break;
}
result->damageRects[count++] = (KVMFRFrameDamageRect)
{
.x = x,
.y = y,
.width = endX - x,
.height = endY - y,
};
}
}
if (!overflow)
{ {
result->damageRectsCount = count; networkVideoDispatchRect(offsetUniform, extentUniform, 0, 0,
decoder->config.width, decoder->config.height);
return; return;
} }
uint32_t minX = decoder->config.width; for (uint16_t index = 0; index < damage->count; ++index)
uint32_t minY = decoder->config.height;
uint32_t maxX = 0;
uint32_t maxY = 0;
for (uint32_t tileY = 0; tileY < decoder->outputTileHeight; ++tileY)
{ {
for (uint32_t tileX = 0; tileX < decoder->outputTileWidth; ++tileX) const LGNetVideoDamageRect * rect = &damage->rects[index];
{ networkVideoDispatchRect(offsetUniform, extentUniform,
if (!decoder->outputDirty[ rect->x, rect->y, rect->width, rect->height);
(size_t)tileY * decoder->outputTileWidth + tileX])
continue;
const uint32_t x = tileX * NETWORK_VIDEO_TILE_SIZE;
const uint32_t y = tileY * NETWORK_VIDEO_TILE_SIZE;
minX = x < minX ? x : minX;
minY = y < minY ? y : minY;
maxX = x + NETWORK_VIDEO_TILE_SIZE > maxX ?
x + NETWORK_VIDEO_TILE_SIZE : maxX;
maxY = y + NETWORK_VIDEO_TILE_SIZE > maxY ?
y + NETWORK_VIDEO_TILE_SIZE : maxY;
}
} }
maxX = maxX < decoder->config.width ? maxX : decoder->config.width; }
maxY = maxY < decoder->config.height ? maxY : decoder->config.height;
result->damageRects[0] = (KVMFRFrameDamageRect) static const LGNetVideoDamage * networkVideoFindDamageNL(
const NetworkVideoDecoder * decoder, uint64_t generation)
{
const struct NetworkVideoDamageHistory * history =
&decoder->damageHistory[generation % NETWORK_VIDEO_DAMAGE_HISTORY];
return history->generation == generation ? &history->damage : NULL;
}
static bool networkVideoCopyNeedsFullNL(
const NetworkVideoDecoder * decoder, uint64_t textureGeneration,
uint64_t generation, const LGNetVideoDamage * damage)
{
if (!textureGeneration || textureGeneration >= generation ||
generation - textureGeneration > NETWORK_VIDEO_DAMAGE_HISTORY ||
networkVideoDamageFull(damage))
return true;
for (uint64_t previous = textureGeneration + 1U;
previous < generation; ++previous)
{ {
.x = minX, const LGNetVideoDamage * prior =
.y = minY, networkVideoFindDamageNL(decoder, previous);
.width = maxX - minX, if (!prior || networkVideoDamageFull(prior))
.height = maxY - minY, return true;
}; }
result->damageRectsCount = 1; return false;
}
static void networkVideoCopyCanonicalNL(NetworkVideoDecoder * decoder,
GLuint texture, uint64_t textureGeneration, uint64_t generation,
const LGNetVideoDamage * damage)
{
glUseProgram(decoder->copyProgram);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, decoder->canonicalTexture);
glBindImageTexture(0, texture, 0, GL_FALSE, 0,
GL_WRITE_ONLY, GL_RGBA16F);
if (networkVideoCopyNeedsFullNL(
decoder, textureGeneration, generation, damage))
{
networkVideoDispatchRect(decoder->uniforms.copy.outputOffset,
decoder->uniforms.copy.outputExtent, 0, 0,
decoder->config.width, decoder->config.height);
}
else
{
for (uint64_t previous = textureGeneration + 1U;
previous < generation; ++previous)
networkVideoDispatchDamageNL(decoder,
decoder->uniforms.copy.outputOffset,
decoder->uniforms.copy.outputExtent,
networkVideoFindDamageNL(decoder, previous));
networkVideoDispatchDamageNL(decoder,
decoder->uniforms.copy.outputOffset,
decoder->uniforms.copy.outputExtent, damage);
}
glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT |
GL_TEXTURE_FETCH_BARRIER_BIT);
}
static void networkVideoBuildDamage(const LGNetVideoDamage * damage,
NetworkVideoDecodedFrame * result)
{
if (networkVideoDamageFull(damage))
return;
for (uint16_t index = 0; index < damage->count; ++index)
{
const LGNetVideoDamageRect * source = &damage->rects[index];
result->damageRects[index] = (KVMFRFrameDamageRect)
{
.x = source->x,
.y = source->y,
.width = source->width,
.height = source->height,
};
}
result->damageRectsCount = damage->count;
} }
static struct NetworkVideoExternalFrame * networkVideoConvertNL( static struct NetworkVideoExternalFrame * networkVideoConvertNL(
NetworkVideoDecoder * decoder, bool chromaLeft, uint64_t generation) NetworkVideoDecoder * decoder, bool chromaLeft, uint64_t generation,
const LGNetVideoDamage * damage)
{ {
struct NetworkVideoTexture pooled =
networkVideoTexturePoolAcquire(decoder->texturePool);
if (!pooled.texture)
return NULL;
glUseProgram(decoder->yuvProgram); glUseProgram(decoder->yuvProgram);
for (unsigned component = 0; for (unsigned component = 0;
component < NETWORK_VIDEO_COMPONENTS; ++component) component < NETWORK_VIDEO_COMPONENTS; ++component)
@@ -1852,46 +1838,21 @@ static struct NetworkVideoExternalFrame * networkVideoConvertNL(
glUniform1f(decoder->uniforms.yuv.lumaScale, lumaScale); glUniform1f(decoder->uniforms.yuv.lumaScale, lumaScale);
glUniform1f(decoder->uniforms.yuv.chromaMidpoint, chromaMidpoint); glUniform1f(decoder->uniforms.yuv.chromaMidpoint, chromaMidpoint);
glUniform1f(decoder->uniforms.yuv.chromaScale, chromaScale); glUniform1f(decoder->uniforms.yuv.chromaScale, chromaScale);
glBindImageTexture(0, pooled.texture, 0, GL_FALSE, 0, glBindImageTexture(0, decoder->canonicalTexture, 0, GL_FALSE, 0,
GL_WRITE_ONLY, GL_RGBA16F); GL_WRITE_ONLY, GL_RGBA16F);
if (!pooled.generation) networkVideoDispatchDamageNL(decoder,
{ decoder->uniforms.yuv.outputOffset,
glUniform2i(decoder->uniforms.yuv.outputOffset, 0, 0); decoder->uniforms.yuv.outputExtent, damage);
glDispatchCompute((decoder->config.width + 7U) / 8U,
(decoder->config.height + 7U) / 8U, 1);
}
else
{
for (uint32_t tileY = 0; tileY < decoder->outputTileHeight; ++tileY)
{
uint32_t tileX = 0;
while (tileX < decoder->outputTileWidth)
{
const size_t row = (size_t)tileY * decoder->outputTileWidth;
while (tileX < decoder->outputTileWidth &&
!networkVideoTextureTileDirty(
decoder, row + tileX, pooled.generation))
++tileX;
const uint32_t start = tileX;
while (tileX < decoder->outputTileWidth &&
networkVideoTextureTileDirty(
decoder, row + tileX, pooled.generation))
++tileX;
if (start == tileX)
continue;
const uint32_t pixelWidth =
(tileX - start) * NETWORK_VIDEO_TILE_SIZE;
glUniform2i(decoder->uniforms.yuv.outputOffset,
(GLint)(start * NETWORK_VIDEO_TILE_SIZE),
(GLint)(tileY * NETWORK_VIDEO_TILE_SIZE));
glDispatchCompute((pixelWidth + 7U) / 8U,
NETWORK_VIDEO_TILE_SIZE / 8U, 1);
}
}
}
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,
pooled.generation, generation, damage);
GLsync readySync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); GLsync readySync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
glFlush(); glFlush();
if (!readySync || glGetError() != GL_NO_ERROR) if (!readySync || glGetError() != GL_NO_ERROR)
@@ -1956,6 +1917,17 @@ NetworkVideoDecodeResult networkVideoDecoderDecode(
NETWORK_VIDEO_DECODE_UNSUPPORTED : NETWORK_VIDEO_DECODE_CORRUPT; NETWORK_VIDEO_DECODE_UNSUPPORTED : NETWORK_VIDEO_DECODE_CORRUPT;
} }
LGNetVideoDamage damage = layout.damage;
if (!update)
{
damage = (LGNetVideoDamage)
{
.flags = (LGNetVideoDamageFlags)LG_NET_VIDEO_DAMAGE_FULL,
};
}
const bool hasPresentationDamage =
networkVideoDamageFull(&damage) || damage.count;
uint32_t changedCount = 0; uint32_t changedCount = 0;
if (!networkVideoStagePacketsNL( if (!networkVideoStagePacketsNL(
decoder, data, size, update, &changedCount)) decoder, data, size, update, &changedCount))
@@ -2004,10 +1976,15 @@ NetworkVideoDecodeResult networkVideoDecoderDecode(
networkVideoDequantNL(decoder, upload, update); networkVideoDequantNL(decoder, upload, update);
upload->readySync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); upload->readySync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
networkVideoIDWTNL(decoder, update); networkVideoIDWTNL(decoder, update);
struct NetworkVideoExternalFrame * external = struct NetworkVideoExternalFrame * external = NULL;
networkVideoConvertNL(decoder, layout.chromaLeft, generation); if (hasPresentationDamage)
external = networkVideoConvertNL(decoder,
layout.chromaLeft, generation, &damage);
else
glFlush();
const uint64_t decodeTime = nanotime() - decodeStart; const uint64_t decodeTime = nanotime() - decodeStart;
const bool success = external && glGetError() == GL_NO_ERROR; const bool success =
(!hasPresentationDamage || external) && glGetError() == GL_NO_ERROR;
networkVideoRestoreCurrent(decoder, &previous); networkVideoRestoreCurrent(decoder, &previous);
if (!success) if (!success)
{ {
@@ -2021,14 +1998,19 @@ NetworkVideoDecodeResult networkVideoDecoderDecode(
} }
networkVideoCommitPendingPacketsNL(decoder); networkVideoCommitPendingPacketsNL(decoder);
const size_t outputTileCount =
(size_t)decoder->outputTileWidth * decoder->outputTileHeight;
for (size_t index = 0; index < outputTileCount; ++index)
if (decoder->outputDirty[index])
decoder->outputTileGenerations[index] = generation;
decoder->outputGeneration = generation;
decoder->referenceFrameID = complete ? frameID : 0; decoder->referenceFrameID = complete ? frameID : 0;
decoder->referenceValid = complete; decoder->referenceValid = complete;
if (!hasPresentationDamage)
{
LG_UNLOCK(decoder->lock);
return NETWORK_VIDEO_DECODE_NO_FRAME;
}
struct NetworkVideoDamageHistory * history =
&decoder->damageHistory[generation % NETWORK_VIDEO_DAMAGE_HISTORY];
history->generation = generation;
history->damage = damage;
decoder->outputGeneration = generation;
uint32_t formatVersion = (uint32_t)config->configEpoch; uint32_t formatVersion = (uint32_t)config->configEpoch;
if (!formatVersion) if (!formatVersion)
@@ -2037,7 +2019,7 @@ NetworkVideoDecodeResult networkVideoDecoderDecode(
result->externalFrame = &external->base; result->externalFrame = &external->base;
result->decodeTimeNs = decodeTime; result->decodeTimeNs = decodeTime;
if (update) if (update)
networkVideoBuildDamageNL(decoder, result); networkVideoBuildDamage(&damage, result);
result->format = (LG_TransportFrameFormat) result->format = (LG_TransportFrameFormat)
{ {
.version = formatVersion, .version = formatVersion,

View File

@@ -247,11 +247,11 @@ NetworkPyroWaveResult networkPyroWaveParse(
size_t cursor = 0; size_t cursor = 0;
uint32_t uniqueBlocks = 0; uint32_t uniqueBlocks = 0;
uint32_t declaredBlocks = 0; uint32_t declaredBlocks = 0;
uint32_t headerFirst = 0;
uint32_t headerSecond = 0; uint32_t headerSecond = 0;
uint8_t frameSequence = 0; uint8_t frameSequence = 0;
bool haveSequence = false; bool haveSequence = false;
bool haveHeader = false; bool haveHeader = false;
bool haveDamage = false;
bool haveClear = false; bool haveClear = false;
bool valid = true; bool valid = true;
@@ -285,21 +285,39 @@ NetworkPyroWaveResult networkPyroWaveParse(
width != config->width || height != config->height || width != config->width || height != config->height ||
!blocks || blocks > capacity || !blocks || blocks > capacity ||
!networkPyroWaveSequenceProfile(config, second) || !networkPyroWaveSequenceProfile(config, second) ||
(haveHeader && haveHeader)
(first != headerFirst || second != headerSecond)))
{ {
valid = false; valid = false;
break; break;
} }
declaredBlocks = blocks; declaredBlocks = blocks;
headerFirst = first;
headerSecond = second; headerSecond = second;
haveHeader = true; haveHeader = true;
cursor += NETWORK_PYROWAVE_HEADER_SIZE; cursor += NETWORK_PYROWAVE_HEADER_SIZE;
if (code == LG_NET_VIDEO_PYROWAVE_MODE_DELTA)
{
size_t damageSize = 0;
if (lgNetVideoDamageDecode(&layout->damage,
bytes + cursor, size - cursor,
config->width, config->height, &damageSize) != LG_NET_PARSE_OK)
{
valid = false;
break;
}
cursor += damageSize;
haveDamage = true;
}
continue; continue;
} }
if (!haveHeader)
{
valid = false;
break;
}
const uint32_t ballot = first & UINT32_C(0x0000FFFF); const uint32_t ballot = first & UINT32_C(0x0000FFFF);
const uint32_t payloadWords = (first >> 16) & UINT32_C(0x00000FFF); const uint32_t payloadWords = (first >> 16) & UINT32_C(0x00000FFF);
const uint32_t blockIndex = second >> 8; const uint32_t blockIndex = second >> 8;
@@ -322,20 +340,19 @@ NetworkPyroWaveResult networkPyroWaveParse(
cursor += packetSize; cursor += packetSize;
} }
const bool delta =
((headerSecond >> LG_NET_VIDEO_PYROWAVE_MODE_SHIFT) &
LG_NET_VIDEO_PYROWAVE_MODE_MASK) ==
LG_NET_VIDEO_PYROWAVE_MODE_DELTA;
valid = valid && cursor == size && haveHeader && haveSequence && valid = valid && cursor == size && haveHeader && haveSequence &&
haveDamage == delta &&
declaredBlocks == uniqueBlocks && expectedBlocks == uniqueBlocks && declaredBlocks == uniqueBlocks && expectedBlocks == uniqueBlocks &&
(!haveClear || (!haveClear || delta);
((headerSecond >> LG_NET_VIDEO_PYROWAVE_MODE_SHIFT) &
LG_NET_VIDEO_PYROWAVE_MODE_MASK) ==
LG_NET_VIDEO_PYROWAVE_MODE_DELTA);
if (!valid) if (!valid)
return NETWORK_PYROWAVE_INVALID; return NETWORK_PYROWAVE_INVALID;
layout->sequence = frameSequence; layout->sequence = frameSequence;
layout->chromaLeft = (headerSecond >> 31) != 0; layout->chromaLeft = (headerSecond >> 31) != 0;
layout->delta = layout->delta = delta;
((headerSecond >> LG_NET_VIDEO_PYROWAVE_MODE_SHIFT) &
LG_NET_VIDEO_PYROWAVE_MODE_MASK) ==
LG_NET_VIDEO_PYROWAVE_MODE_DELTA;
return NETWORK_PYROWAVE_OK; return NETWORK_PYROWAVE_OK;
} }

View File

@@ -36,9 +36,10 @@ NetworkPyroWaveResult;
typedef struct NetworkPyroWaveLayout typedef struct NetworkPyroWaveLayout
{ {
uint8_t sequence; uint8_t sequence;
bool chromaLeft; bool chromaLeft;
bool delta; bool delta;
LGNetVideoDamage damage;
} }
NetworkPyroWaveLayout; NetworkPyroWaveLayout;

View File

@@ -308,6 +308,7 @@ static const char NETWORK_VIDEO_YUV_SHADER[] =
"layout(rgba16f,binding=0) writeonly uniform highp image2D outputImage;\n" "layout(rgba16f,binding=0) writeonly uniform highp image2D outputImage;\n"
"uniform ivec2 outputSize;\n" "uniform ivec2 outputSize;\n"
"uniform ivec2 outputOffset;\n" "uniform ivec2 outputOffset;\n"
"uniform ivec2 outputExtent;\n"
"uniform bool bt2020;\n" "uniform bool bt2020;\n"
"uniform bool chromaLeft;\n" "uniform bool chromaLeft;\n"
"uniform float lumaOffset;\n" "uniform float lumaOffset;\n"
@@ -315,7 +316,9 @@ static const char NETWORK_VIDEO_YUV_SHADER[] =
"uniform float chromaMidpoint;\n" "uniform float chromaMidpoint;\n"
"uniform float chromaScale;\n" "uniform float chromaScale;\n"
"void main(){\n" "void main(){\n"
" ivec2 pixel = ivec2(gl_GlobalInvocationID.xy)+outputOffset;\n" " ivec2 local = ivec2(gl_GlobalInvocationID.xy);\n"
" if(any(greaterThanEqual(local,outputExtent)))return;\n"
" ivec2 pixel = local+outputOffset;\n"
" if(any(greaterThanEqual(pixel,outputSize)))return;\n" " if(any(greaterThanEqual(pixel,outputSize)))return;\n"
" ivec2 planeSize = textureSize(lumaPlane,0);\n" " ivec2 planeSize = textureSize(lumaPlane,0);\n"
" vec2 uv = (vec2(pixel)+0.5)/vec2(planeSize);\n" " vec2 uv = (vec2(pixel)+0.5)/vec2(planeSize);\n"
@@ -335,6 +338,21 @@ static const char NETWORK_VIDEO_YUV_SHADER[] =
" vec3 rgb = clamp(m*vec3(y,cb,cr),vec3(0.0),vec3(1.0));\n" " vec3 rgb = clamp(m*vec3(y,cb,cr),vec3(0.0),vec3(1.0));\n"
" imageStore(outputImage,pixel,vec4(rgb,1.0));}\n"; " imageStore(outputImage,pixel,vec4(rgb,1.0));}\n";
static const char NETWORK_VIDEO_COPY_SHADER[] =
"#version 310 es\n"
"precision highp float;\n"
"precision highp int;\n"
"layout(local_size_x=8,local_size_y=8) in;\n"
"layout(binding=0) uniform highp sampler2D inputImage;\n"
"layout(rgba16f,binding=0) writeonly uniform highp image2D outputImage;\n"
"uniform ivec2 outputOffset;\n"
"uniform ivec2 outputExtent;\n"
"void main(){\n"
" ivec2 local = ivec2(gl_GlobalInvocationID.xy);\n"
" if(any(greaterThanEqual(local,outputExtent)))return;\n"
" ivec2 pixel = local+outputOffset;\n"
" imageStore(outputImage,pixel,texelFetch(inputImage,pixel,0));}\n";
static const char * const NETWORK_VIDEO_DEQUANT_SOURCES[] = static const char * const NETWORK_VIDEO_DEQUANT_SOURCES[] =
{ {
NETWORK_VIDEO_DEQUANT_SHADER_0, NETWORK_VIDEO_DEQUANT_SHADER_0,
@@ -351,3 +369,8 @@ static const char * const NETWORK_VIDEO_YUV_SOURCES[] =
{ {
NETWORK_VIDEO_YUV_SHADER, NETWORK_VIDEO_YUV_SHADER,
}; };
static const char * const NETWORK_VIDEO_COPY_SOURCES[] =
{
NETWORK_VIDEO_COPY_SHADER,
};

View File

@@ -25,6 +25,8 @@
#include <LGProtocol/KVMFRTypes.h> #include <LGProtocol/KVMFRTypes.h>
#define LG_MAX_RUNTIME_DAMAGE_RECTS 256
typedef void (*LG_FrameReleaseFn)(void * opaque, uint64_t handle); typedef void (*LG_FrameReleaseFn)(void * opaque, uint64_t handle);
struct Point struct Point

View File

@@ -83,8 +83,11 @@ namespace
constexpr uint32_t PYROWAVE_ALIGNMENT = 1U << PYROWAVE_LEVELS; constexpr uint32_t PYROWAVE_ALIGNMENT = 1U << PYROWAVE_LEVELS;
constexpr uint32_t PYROWAVE_MIN_DIMENSION = 4U << PYROWAVE_LEVELS; constexpr uint32_t PYROWAVE_MIN_DIMENSION = 4U << PYROWAVE_LEVELS;
constexpr uint32_t PYROWAVE_PACKET_BYTES = 2488; constexpr uint32_t PYROWAVE_PACKET_BYTES = 2488;
constexpr uint32_t PYROWAVE_DAMAGE_BYTES_MAX =
LG_NET_VIDEO_DAMAGE_HEADER_WIRE_SIZE +
LG_NET_VIDEO_MAX_DAMAGE_RECTS * LG_NET_VIDEO_DAMAGE_RECT_WIRE_SIZE;
constexpr uint32_t PYROWAVE_BLOCK_BYTES_MAX = constexpr uint32_t PYROWAVE_BLOCK_BYTES_MAX =
PYROWAVE_PACKET_BYTES + 8U; PYROWAVE_PACKET_BYTES + 8U + PYROWAVE_DAMAGE_BYTES_MAX;
// Reserve an equal fragment budget for every possible codec block so a // Reserve an equal fragment budget for every possible codec block so a
// negotiated delivery path can always represent the encoded frame. // negotiated delivery path can always represent the encoded frame.
constexpr uint32_t PYROWAVE_FRAGS_PER_BLOCK = constexpr uint32_t PYROWAVE_FRAGS_PER_BLOCK =
@@ -93,7 +96,8 @@ namespace
(PYROWAVE_BLOCK_BYTES_MAX + PYROWAVE_FRAGS_PER_BLOCK - 1U) / (PYROWAVE_BLOCK_BYTES_MAX + PYROWAVE_FRAGS_PER_BLOCK - 1U) /
PYROWAVE_FRAGS_PER_BLOCK; PYROWAVE_FRAGS_PER_BLOCK;
constexpr uint32_t PYROWAVE_FRAGMENT_MIN = constexpr uint32_t PYROWAVE_FRAGMENT_MIN =
LG_NET_VIDEO_FRAGMENT_HEADER_WIRE_SIZE + PYROWAVE_FRAGMENT_DATA_MIN; LG_NET_VIDEO_FRAME_UPDATE_FRAGMENT_HEADER_WIRE_SIZE +
PYROWAVE_FRAGMENT_DATA_MIN;
constexpr uint32_t PYROWAVE_PACKET_META_WORDS = 2; constexpr uint32_t PYROWAVE_PACKET_META_WORDS = 2;
constexpr uint32_t PYROWAVE_SEQUENCE_MASK = 7; constexpr uint32_t PYROWAVE_SEQUENCE_MASK = 7;
constexpr uint32_t PYROWAVE_HEADER_CODE_MASK = constexpr uint32_t PYROWAVE_HEADER_CODE_MASK =
@@ -135,6 +139,8 @@ namespace
static_assert(PYROWAVE_BLOCK_BYTES_MAX <= static_assert(PYROWAVE_BLOCK_BYTES_MAX <=
PYROWAVE_FRAGS_PER_BLOCK * LG_NET_VIDEO_MAX_FRAGMENT_LENGTH, PYROWAVE_FRAGS_PER_BLOCK * LG_NET_VIDEO_MAX_FRAGMENT_LENGTH,
"The fragment payload limit must represent every codec block"); "The fragment payload limit must represent every codec block");
static_assert(LG_NET_VIDEO_MAX_DAMAGE_RECTS >= FRAME_DAMAGE_MAX,
"The network protocol must represent every capture damage rectangle");
constexpr LGNetEnvelopeFlags DELIVERY_MASK = constexpr LGNetEnvelopeFlags DELIVERY_MASK =
LG_NET_ENVELOPE_RESPONSE | LG_NET_ENVELOPE_FINAL | LG_NET_ENVELOPE_RESPONSE | LG_NET_ENVELOPE_FINAL |
@@ -1502,6 +1508,155 @@ void FinalizeMain()
uint32_t index = 0; uint32_t index = 0;
}; };
bool VideoDamageContains(const LGNetVideoDamageRect& outer,
const LGNetVideoDamageRect& inner)
{
return outer.x <= inner.x && outer.y <= inner.y &&
static_cast<uint32_t>(outer.x) + outer.width >=
static_cast<uint32_t>(inner.x) + inner.width &&
static_cast<uint32_t>(outer.y) + outer.height >=
static_cast<uint32_t>(inner.y) + inner.height;
}
bool MergeVideoDamageRect(const LGNetVideoDamageRect& first,
const LGNetVideoDamageRect& second, LGNetVideoDamageRect& merged)
{
const uint32_t firstRight =
static_cast<uint32_t>(first.x) + first.width;
const uint32_t secondRight =
static_cast<uint32_t>(second.x) + second.width;
const uint32_t firstBottom =
static_cast<uint32_t>(first.y) + first.height;
const uint32_t secondBottom =
static_cast<uint32_t>(second.y) + second.height;
if (first.y == second.y && first.height == second.height &&
first.x <= secondRight && second.x <= firstRight)
{
const uint32_t left = (std::min)(first.x, second.x);
const uint32_t right = (std::max)(firstRight, secondRight);
merged.x = static_cast<uint16_t>(left);
merged.y = first.y;
merged.width = static_cast<uint16_t>(right - left);
merged.height = first.height;
return true;
}
if (first.x == second.x && first.width == second.width &&
first.y <= secondBottom && second.y <= firstBottom)
{
const uint32_t top = (std::min)(first.y, second.y);
const uint32_t bottom = (std::max)(firstBottom, secondBottom);
merged.x = first.x;
merged.y = static_cast<uint16_t>(top);
merged.width = first.width;
merged.height = static_cast<uint16_t>(bottom - top);
return true;
}
return false;
}
bool AddVideoDamageRect(LGNetVideoDamage& damage,
const LGNetVideoDamageRect& rect)
{
LGNetVideoDamageRect current = rect;
for (uint16_t index = 0; index < damage.count;)
{
const LGNetVideoDamageRect existing = damage.rects[index];
if (VideoDamageContains(existing, current))
return true;
LGNetVideoDamageRect merged = {};
if (!VideoDamageContains(current, existing) &&
!MergeVideoDamageRect(existing, current, merged))
{
++index;
continue;
}
if (VideoDamageContains(current, existing))
merged = current;
current = merged;
--damage.count;
damage.rects[index] = damage.rects[damage.count];
index = 0;
}
if (damage.count == LG_NET_VIDEO_MAX_DAMAGE_RECTS)
return false;
damage.rects[damage.count++] = current;
return true;
}
void SetFullVideoDamage(LGNetVideoDamage& damage)
{
damage = {};
damage.flags = static_cast<LGNetVideoDamageFlags>(
LG_NET_VIDEO_DAMAGE_FULL);
}
bool SetVideoDamage(const FrameDesc& frame, uint32_t width,
uint32_t height, LGNetVideoDamage& damage)
{
damage = {};
if (frame.damage == FrameDamage::NONE)
return true;
if (frame.damage == FrameDamage::FULL)
{
SetFullVideoDamage(damage);
return true;
}
if (frame.damage != FrameDamage::RECTS || !frame.count ||
frame.count > FRAME_DAMAGE_MAX)
return false;
for (unsigned index = 0; index < frame.count; ++index)
{
const RECT& rect = frame.rects[index];
if (rect.left < 0 || rect.top < 0 || rect.right <= rect.left ||
rect.bottom <= rect.top ||
static_cast<uint32_t>(rect.right) > width ||
static_cast<uint32_t>(rect.bottom) > height)
{
SetFullVideoDamage(damage);
return true;
}
const LGNetVideoDamageRect output =
{
static_cast<uint16_t>(rect.left),
static_cast<uint16_t>(rect.top),
static_cast<uint16_t>(rect.right - rect.left),
static_cast<uint16_t>(rect.bottom - rect.top),
};
if (!AddVideoDamageRect(damage, output))
{
SetFullVideoDamage(damage);
return true;
}
}
return lgNetVideoDamageValid(&damage, width, height);
}
void MergeVideoDamage(LGNetVideoDamage& destination,
const LGNetVideoDamage& source)
{
if ((destination.flags & LG_NET_VIDEO_DAMAGE_FULL) ||
(!source.flags && !source.count))
return;
if (source.flags & LG_NET_VIDEO_DAMAGE_FULL)
{
SetFullVideoDamage(destination);
return;
}
bool complete = true;
for (uint16_t index = 0; index < source.count; ++index)
if (!AddVideoDamageRect(destination, source.rects[index]))
{
complete = false;
break;
}
if (complete)
return;
SetFullVideoDamage(destination);
}
struct EncodedFrame struct EncodedFrame
{ {
uint64_t configEpoch = 0; uint64_t configEpoch = 0;
@@ -1511,6 +1666,7 @@ void FinalizeMain()
uint32_t deadlineMs = 0; uint32_t deadlineMs = 0;
uint32_t blockCapacity = 0; uint32_t blockCapacity = 0;
uint32_t hardLimit = 0; uint32_t hardLimit = 0;
LGNetVideoDamage damage = {};
std::vector<uint8_t> damagedBlocks; std::vector<uint8_t> damagedBlocks;
std::vector<uint8_t> data; std::vector<uint8_t> data;
std::vector<EncodedBlock> blocks; std::vector<EncodedBlock> blocks;
@@ -3317,7 +3473,11 @@ void FinalizeMain()
output = {}; output = {};
const bool result = Compact(*slot, slot->mapped, output); bool result = Compact(*slot, slot->mapped, output);
if (result)
result = SetVideoDamage(slot->frame.desc.frame,
m_cfg.width, m_cfg.height, output.damage);
if (result) if (result)
{ {
@@ -3389,8 +3549,9 @@ struct CNetworkVideo::Impl
{ {
struct ReferenceFrame struct ReferenceFrame
{ {
uint64_t frameID = 0; uint64_t frameID = 0;
std::shared_ptr<const EncodedFrame> frame; std::shared_ptr<const EncodedFrame> frame;
LGNetVideoDamage subsequentDamage = {};
std::vector<uint8_t> subsequentBlocks; std::vector<uint8_t> subsequentBlocks;
}; };
@@ -3416,6 +3577,7 @@ struct CNetworkVideo::Impl
uint8_t blockedDeliveries = 0; uint8_t blockedDeliveries = 0;
std::shared_ptr<const EncodedFrame> referenceFrame; std::shared_ptr<const EncodedFrame> referenceFrame;
std::deque<ReferenceFrame> referenceHistory; std::deque<ReferenceFrame> referenceHistory;
LGNetVideoDamage updateDamage = {};
std::vector<uint8_t> updateBlocks; std::vector<uint8_t> updateBlocks;
bool statusSent = false; bool statusSent = false;
bool subscribed = false; bool subscribed = false;
@@ -3604,10 +3766,21 @@ struct CNetworkVideo::Impl
peer.referenceFrame.reset(); peer.referenceFrame.reset();
peer.referenceHistory.clear(); peer.referenceHistory.clear();
peer.updateBlocks.clear(); peer.updateBlocks.clear();
peer.updateDamage = {};
peer.referenceFrameID = 0; peer.referenceFrameID = 0;
peer.referencePending = false; peer.referencePending = false;
} }
static void AccumulatePeerDamageNL(PeerState& peer,
const LGNetVideoDamage& damage)
{
if (!peer.referenceFrame)
return;
MergeVideoDamage(peer.updateDamage, damage);
for (ReferenceFrame& reference : peer.referenceHistory)
MergeVideoDamage(reference.subsequentDamage, damage);
}
static bool PeerNeedsReferenceNL( static bool PeerNeedsReferenceNL(
const PeerState& peer, const EncodedFrame& frame) const PeerState& peer, const EncodedFrame& frame)
{ {
@@ -3780,6 +3953,7 @@ struct CNetworkVideo::Impl
result->deadlineMs = current.deadlineMs; result->deadlineMs = current.deadlineMs;
result->blockCapacity = current.blockCapacity; result->blockCapacity = current.blockCapacity;
result->hardLimit = current.hardLimit; result->hardLimit = current.hardLimit;
result->damage = current.damage;
result->damagedBlocks = std::move(current.damagedBlocks); result->damagedBlocks = std::move(current.damagedBlocks);
result->blocks.reserve((std::max)( result->blocks.reserve((std::max)(
previous->blocks.size(), current.blocks.size())); previous->blocks.size(), current.blocks.size()));
@@ -3917,6 +4091,7 @@ struct CNetworkVideo::Impl
static std::shared_ptr<const EncodedFrame> BuildUpdateFrame( static std::shared_ptr<const EncodedFrame> BuildUpdateFrame(
const EncodedFrame& reference, const EncodedFrame& current, const EncodedFrame& reference, const EncodedFrame& current,
const std::vector<uint8_t>& previousBlocks, const std::vector<uint8_t>& previousBlocks,
const LGNetVideoDamage& damage, uint32_t width, uint32_t height,
std::vector<uint8_t>& updateBlocks) std::vector<uint8_t>& updateBlocks)
{ {
try try
@@ -3947,9 +4122,14 @@ struct CNetworkVideo::Impl
update->captureTimeNs = current.captureTimeNs; update->captureTimeNs = current.captureTimeNs;
update->deadlineMs = current.deadlineMs; update->deadlineMs = current.deadlineMs;
update->blockCapacity = current.blockCapacity; update->blockCapacity = current.blockCapacity;
update->damage = damage;
update->blocks.reserve(operationCount); update->blocks.reserve(operationCount);
update->data.reserve(current.data.size()); const size_t damageSize = lgNetVideoDamageSize(&damage);
update->data.resize(8U); if (!damageSize ||
damageSize > LG_NET_VIDEO_MAX_FRAME_UPDATE_LENGTH - 8U)
return {};
update->data.reserve(current.data.size() + damageSize);
update->data.resize(8U + damageSize);
const uint32_t firstWord = LoadLE32(current.data.data()); const uint32_t firstWord = LoadLE32(current.data.data());
const uint32_t secondWord = const uint32_t secondWord =
@@ -3958,6 +4138,9 @@ struct CNetworkVideo::Impl
PYROWAVE_HEADER_CODE_DELTA | operationCount; PYROWAVE_HEADER_CODE_DELTA | operationCount;
StoreLE32(update->data.data(), firstWord); StoreLE32(update->data.data(), firstWord);
StoreLE32(update->data.data() + sizeof(uint32_t), secondWord); StoreLE32(update->data.data() + sizeof(uint32_t), secondWord);
if (!lgNetVideoDamageEncode(update->data.data() + 8U,
damageSize, &damage, width, height))
return {};
const uint32_t sequence = const uint32_t sequence =
(firstWord >> 28U) & PYROWAVE_SEQUENCE_MASK; (firstWord >> 28U) & PYROWAVE_SEQUENCE_MASK;
@@ -4007,7 +4190,7 @@ struct CNetworkVideo::Impl
return {}; return {};
update->blocks[0].offset = 0; update->blocks[0].offset = 0;
update->blocks[0].length += 8U; update->blocks[0].length += static_cast<uint32_t>(8U + damageSize);
return update; return update;
} }
catch (...) catch (...)
@@ -4096,14 +4279,20 @@ struct CNetworkVideo::Impl
if (candidate->subsequentBlocks.size() != promotedFrame->blockCapacity) if (candidate->subsequentBlocks.size() != promotedFrame->blockCapacity)
return false; return false;
std::vector<uint8_t> subsequentBlocks = std::vector<uint8_t> subsequentBlocks =
std::move(candidate->subsequentBlocks); std::move(candidate->subsequentBlocks);
const LGNetVideoDamage subsequentDamage = candidate->subsequentDamage;
peer.referenceFrame = promotedFrame; peer.referenceFrame = promotedFrame;
peer.referenceFrameID = promotedID; peer.referenceFrameID = promotedID;
peer.referencePending = false; peer.referencePending = false;
peer.keyframePending = false; peer.keyframePending = false;
peer.updateDamage = subsequentDamage;
peer.updateBlocks = std::move(subsequentBlocks); peer.updateBlocks = std::move(subsequentBlocks);
// Damage also accumulates across frames intentionally skipped while the
// reference is pending, even though no block comparison is done for them.
// BuildUpdateFrame will authoritatively compare against the promoted
// reference and clear the damage if the encoded state did not change.
while (!peer.referenceHistory.empty() && while (!peer.referenceHistory.empty() &&
peer.referenceHistory.front().frameID <= promotedID) peer.referenceHistory.front().frameID <= promotedID)
peer.referenceHistory.pop_front(); peer.referenceHistory.pop_front();
@@ -4808,6 +4997,7 @@ struct CNetworkVideo::Impl
peer.referenceFrame = frame; peer.referenceFrame = frame;
peer.referenceFrameID = frameID; peer.referenceFrameID = frameID;
peer.referencePending = true; peer.referencePending = true;
peer.updateDamage = {};
peer.updateBlocks = std::move(cleanBlocks); peer.updateBlocks = std::move(cleanBlocks);
RecordReferenceFrameNL(peer, frameID, frame); RecordReferenceFrameNL(peer, frameID, frame);
} }
@@ -4869,10 +5059,14 @@ struct CNetworkVideo::Impl
if (PeerNeedsReferenceNL(peer, *current)) if (PeerNeedsReferenceNL(peer, *current))
return QueueFrameNL( return QueueFrameNL(
peer, current, frameID, presentationBaseNs, true); peer, current, frameID, presentationBaseNs, true);
if (peer.updateDamage.flags & LG_NET_VIDEO_DAMAGE_FULL)
return QueueFrameNL(
peer, current, frameID, presentationBaseNs, true);
DropPeerFramesNL(peer); DropPeerFramesNL(peer);
std::vector<uint8_t> updateBlocks; std::vector<uint8_t> updateBlocks;
const std::shared_ptr<const EncodedFrame> update = BuildUpdateFrame( const std::shared_ptr<const EncodedFrame> update = BuildUpdateFrame(
*peer.referenceFrame, *current, peer.updateBlocks, *peer.referenceFrame, *current, peer.updateBlocks,
peer.updateDamage, m_activeCfg.width, m_activeCfg.height,
updateBlocks); updateBlocks);
const bool compared = updateBlocks.size() == current->blockCapacity; const bool compared = updateBlocks.size() == current->blockCapacity;
const bool changed = std::any_of(updateBlocks.begin(), const bool changed = std::any_of(updateBlocks.begin(),
@@ -4882,6 +5076,7 @@ struct CNetworkVideo::Impl
if (compared && !changed) if (compared && !changed)
{ {
peer.lastCaptureTimeNs = current->captureTimeNs; peer.lastCaptureTimeNs = current->captureTimeNs;
peer.updateDamage = {};
return true; return true;
} }
return QueueFrameNL( return QueueFrameNL(
@@ -5014,14 +5209,17 @@ struct CNetworkVideo::Impl
encoded->sourceSerial == m_latestSourceSerial) encoded->sourceSerial == m_latestSourceSerial)
m_latestFrame = encoded; m_latestFrame = encoded;
for (PeerState& peer : m_peers) for (PeerState& peer : m_peers)
if (peer.subscribed && peer.configured && if (peer.subscribed && peer.configured)
PeerFrameDueNL(peer, *encoded))
{ {
if (!QueueCurrentFrameNL(peer, encoded, AccumulatePeerDamageNL(peer, encoded->damage);
frameID, encoded->captureTimeNs)) if (PeerFrameDueNL(peer, *encoded))
QueueStatusNL(peer, LG_NET_VIDEO_STATE_STALLED, 2); {
else if (!QueueCurrentFrameNL(peer, encoded,
QueueStatusNL(peer, LG_NET_VIDEO_STATE_STREAMING, 0); frameID, encoded->captureTimeNs))
QueueStatusNL(peer, LG_NET_VIDEO_STATE_STALLED, 2);
else
QueueStatusNL(peer, LG_NET_VIDEO_STATE_STREAMING, 0);
}
} }
} }