[obs] dmabuf: snapshot frames before release

This commit is contained in:
Geoffrey McRae
2026-08-05 17:11:20 +10:00
parent f95c8f0660
commit 2147567586

102
obs/lg.c
View File

@@ -104,6 +104,7 @@ typedef struct
PLGMPClientQueue pointerQueue; PLGMPClientQueue pointerQueue;
gs_texture_t * texture; gs_texture_t * texture;
gs_texture_t * dstTexture; gs_texture_t * dstTexture;
bool textureMapped;
uint8_t * texData; uint8_t * texData;
uint32_t linesize; uint32_t linesize;
@@ -117,7 +118,7 @@ typedef struct
bool dmabuf; bool dmabuf;
bool dmabufTested; bool dmabufTested;
DMAFrameInfo dmaInfo[LGMP_Q_FRAME_BUFFER_LEN]; DMAFrameInfo dmaInfo[LGMP_Q_FRAME_BUFFER_LEN];
gs_texture_t * dmaTexture; gs_timer_t * dmaCopyTimer;
#endif #endif
#if LIBOBS_API_MAJOR_VER >= 28 #if LIBOBS_API_MAJOR_VER >= 28
@@ -418,7 +419,6 @@ static void dmabufInvalidateTextures(LGPlugin * this)
this->dmaInfo[i].texture = NULL; this->dmaInfo[i].texture = NULL;
} }
this->dmaTexture = NULL;
this->dmabufTested = false; this->dmabufTested = false;
} }
@@ -427,6 +427,12 @@ static void dmabufReset(LGPlugin * this)
{ {
dmabufInvalidateTextures(this); dmabufInvalidateTextures(this);
if (this->dmaCopyTimer)
{
gs_timer_destroy(this->dmaCopyTimer);
this->dmaCopyTimer = NULL;
}
/* Ensure the driver releases every DMA-BUF attachment before the exported /* Ensure the driver releases every DMA-BUF attachment before the exported
* FDs and their backing IVSHMEM device are closed. */ * FDs and their backing IVSHMEM device are closed. */
gs_flush(); gs_flush();
@@ -486,11 +492,14 @@ static void deinit(LGPlugin * this)
if (this->texture) if (this->texture)
{ {
if (!this->dmabuf) if (this->textureMapped)
gs_texture_unmap(this->texture); gs_texture_unmap(this->texture);
gs_texture_destroy(this->texture); gs_texture_destroy(this->texture);
this->texture = NULL; this->texture = NULL;
this->textureMapped = false;
} }
this->texData = NULL;
this->linesize = 0;
if (this->cursorTex) if (this->cursorTex)
{ {
@@ -1120,12 +1129,15 @@ static void lgFormatInit(LGPlugin * this, const KVMFRFrame * frame,
if (this->texture) if (this->texture)
{ {
if (!this->dmabuf) if (this->textureMapped)
gs_texture_unmap(this->texture); gs_texture_unmap(this->texture);
gs_texture_destroy(this->texture); gs_texture_destroy(this->texture);
this->texture = NULL; this->texture = NULL;
this->textureMapped = false;
} }
this->texData = NULL;
this->linesize = 0;
this->dataWidth = frame->dataWidth; this->dataWidth = frame->dataWidth;
this->unpack = false; this->unpack = false;
@@ -1211,6 +1223,31 @@ static void lgFormatInit(LGPlugin * this, const KVMFRFrame * frame,
this->dmabufTested = true; this->dmabufTested = true;
} }
if (!this->dmaCopyTimer)
this->dmaCopyTimer = gs_timer_create();
if (!this->dmaCopyTimer)
{
puts("Failed to create DMA copy timer");
this->dmabuf = false;
}
}
if (this->dmabuf)
{
this->texture = gs_texture_create(
this->dataWidth,
this->dataHeight,
this->format,
1,
NULL,
0);
if (!this->texture)
{
puts("Failed to create DMA copy texture");
this->dmabuf = false;
}
} }
#else #else
(void)drmFormat; (void)drmFormat;
@@ -1235,7 +1272,16 @@ static void lgFormatInit(LGPlugin * this, const KVMFRFrame * frame,
return; return;
} }
this->textureMapped =
gs_texture_map(this->texture, &this->texData, &this->linesize); gs_texture_map(this->texture, &this->texData, &this->linesize);
if (!this->textureMapped)
{
puts("Failed to map upload texture");
gs_texture_destroy(this->texture);
this->texture = NULL;
this->texData = NULL;
this->linesize = 0;
}
} }
if (this->unpack) if (this->unpack)
@@ -1454,7 +1500,10 @@ static void lgVideoTick(void * data, float seconds)
this->frameSerial = frame->frameSerial; this->frameSerial = frame->frameSerial;
this->frameSerialValid = true; this->frameSerialValid = true;
bool textureValid = (this->dmabufTested && this->dmabuf) || this->texture; const bool textureValid = this->texture &&
(this->dmabuf ?
this->dmabufTested && this->dmaCopyTimer :
this->textureMapped);
if (!textureValid || this->formatVer != frame->formatVer) if (!textureValid || this->formatVer != frame->formatVer)
lgFormatInit(this, frame, &frameMessage.msg, frameMessage.queue); lgFormatInit(this, frame, &frameMessage.msg, frameMessage.queue);
@@ -1471,19 +1520,35 @@ static void lgVideoTick(void * data, float seconds)
if (!importFailed) if (!importFailed)
{ {
// wait for the frame to be complete before we try to use it // Wait for the frame to be complete before copying it into OBS-owned
// storage. The imported texture remains backed by reusable IVSHMEM.
if (!frameComplete) if (!frameComplete)
frameComplete = framebuffer_wait( frameComplete = framebuffer_wait(
fb, (size_t)frame->dataHeight * frame->pitch); fb, (size_t)frame->dataHeight * frame->pitch);
lgmpClientMessageDone(frameMessage.queue);
if (!frameComplete) if (!frameComplete)
{ {
lgmpClientMessageDone(frameMessage.queue);
os_sem_post(this->frameSem); os_sem_post(this->frameSem);
return; return;
} }
this->dmaTexture = fi->texture; obs_enter_graphics();
uint64_t copyTime;
gs_timer_begin(this->dmaCopyTimer);
gs_copy_texture(this->texture, fi->texture);
gs_timer_end(this->dmaCopyTimer);
if (!gs_timer_get_data(this->dmaCopyTimer, &copyTime))
{
blog(LOG_ERROR, "Failed to wait for DMA frame copy");
obs_leave_graphics();
this->state = STATE_STOPPING;
os_sem_post(this->frameSem);
return;
}
obs_leave_graphics();
lgmpClientMessageDone(frameMessage.queue);
os_sem_post(this->frameSem); os_sem_post(this->frameSem);
return; return;
} }
@@ -1494,7 +1559,7 @@ static void lgVideoTick(void * data, float seconds)
} }
#endif #endif
if (!this->texture) if (!this->texture || !this->textureMapped || !this->texData)
{ {
lgmpClientMessageDone(frameMessage.queue); lgmpClientMessageDone(frameMessage.queue);
os_sem_post(this->frameSem); os_sem_post(this->frameSem);
@@ -1515,8 +1580,19 @@ static void lgVideoTick(void * data, float seconds)
os_sem_post(this->frameSem); os_sem_post(this->frameSem);
obs_enter_graphics(); obs_enter_graphics();
if (this->textureMapped)
gs_texture_unmap(this->texture); gs_texture_unmap(this->texture);
this->textureMapped = false;
this->texData = NULL;
this->linesize = 0;
this->textureMapped =
gs_texture_map(this->texture, &this->texData, &this->linesize); gs_texture_map(this->texture, &this->texData, &this->linesize);
if (!this->textureMapped)
{
blog(LOG_ERROR, "Failed to remap upload texture");
gs_texture_destroy(this->texture);
this->texture = NULL;
}
obs_leave_graphics(); obs_leave_graphics();
} }
@@ -1546,13 +1622,7 @@ static void lgSetupCursorEffect(LGPlugin * this, int outputTransfer)
static void lgVideoRender(void * data, gs_effect_t * effect) static void lgVideoRender(void * data, gs_effect_t * effect)
{ {
LGPlugin * this = (LGPlugin *)data; LGPlugin * this = (LGPlugin *)data;
gs_texture_t * texture; gs_texture_t * texture = this->texture;
#if LIBOBS_API_MAJOR_VER >= 27
texture = this->dmaTexture;
if (!texture)
texture = this->texture;
#endif
if (!texture) if (!texture)
return; return;