[obs] dmabuf: fix failure to display frames in both buffers

This commit is contained in:
Geoffrey McRae 2025-02-14 19:09:16 +11:00
parent e25492a3a3
commit c169d4ab23

182
obs/lg.c
View File

@ -61,13 +61,16 @@ typedef enum
} }
LGState; LGState;
#if LIBOBS_API_MAJOR_VER >= 27
typedef struct typedef struct
{ {
KVMFRFrame * frame; KVMFRFrame * frame;
size_t dataSize; size_t dataSize;
int fd; int fd;
gs_texture_t * texture;
} }
DMAFrameInfo; DMAFrameInfo;
#endif
typedef struct typedef struct
{ {
@ -78,6 +81,9 @@ typedef struct
uint32_t screenWidth, screenHeight; uint32_t screenWidth, screenHeight;
uint32_t dataWidth, dataHeight; uint32_t dataWidth, dataHeight;
uint32_t frameWidth, frameHeight; uint32_t frameWidth, frameHeight;
enum gs_color_format format;
bool unpack;
uint32_t drmFormat;
struct vec2 screenScale; struct vec2 screenScale;
FrameType type; FrameType type;
int bpp; int bpp;
@ -93,6 +99,7 @@ typedef struct
#if LIBOBS_API_MAJOR_VER >= 27 #if LIBOBS_API_MAJOR_VER >= 27
bool dmabuf; bool dmabuf;
DMAFrameInfo dmaInfo[LGMP_Q_FRAME_LEN]; DMAFrameInfo dmaInfo[LGMP_Q_FRAME_LEN];
gs_texture_t * dmaTexture;
#endif #endif
#if LIBOBS_API_MAJOR_VER >= 28 #if LIBOBS_API_MAJOR_VER >= 28
@ -217,15 +224,11 @@ static void deinit(LGPlugin * this)
} }
obs_enter_graphics(); obs_enter_graphics();
if (this->dstTexture) if (this->unpack && this->dstTexture)
{
if (this->dstTexture == this->texture)
this->dstTexture = NULL;
else
{ {
gs_texture_destroy(this->dstTexture); gs_texture_destroy(this->dstTexture);
this->dstTexture = NULL; this->dstTexture = NULL;
} this->unpack = false;
} }
if (this->texture) if (this->texture)
@ -241,6 +244,17 @@ static void deinit(LGPlugin * this)
gs_texture_destroy(this->cursorTex); gs_texture_destroy(this->cursorTex);
this->cursorTex = NULL; this->cursorTex = NULL;
} }
#if LIBOBS_API_MAJOR_VER >= 27
for (int i = 0 ; i < ARRAY_LENGTH(this->dmaInfo); ++i)
if (this->dmaInfo[i].texture)
{
gs_texture_destroy(this->dmaInfo[i].texture);
this->dmaInfo[i].texture = NULL;
}
this->dmaTexture = NULL;
#endif
obs_leave_graphics(); obs_leave_graphics();
this->state = STATE_STOPPED; this->state = STATE_STOPPED;
@ -486,53 +500,58 @@ static void lgUpdate(void * data, obs_data_t * settings)
} }
#if LIBOBS_API_MAJOR_VER >= 27 #if LIBOBS_API_MAJOR_VER >= 27
static int dmabufGetFd(LGPlugin * this, LGMPMessage * msg, KVMFRFrame * frame, size_t dataSize) static DMAFrameInfo * dmabufOpenDMAFrameInfo(LGPlugin * this, LGMPMessage * msg,
KVMFRFrame * frame, size_t dataSize)
{ {
DMAFrameInfo * dma = NULL; DMAFrameInfo * fi = NULL;
/* find the existing dma buffer if it exists */ /* find the existing dma buffer if it exists */
for (int i = 0; i < ARRAY_LENGTH(this->dmaInfo); ++i) for (int i = 0; i < ARRAY_LENGTH(this->dmaInfo); ++i)
if (this->dmaInfo[i].frame == frame) if (this->dmaInfo[i].frame == frame)
{ fi = &this->dmaInfo[i];
dma = this->dmaInfo + i;
/* if it's too small close it */ /* if it's too small close it */
if (dma->dataSize < dataSize) if (fi && fi->dataSize < dataSize)
{ {
close(dma->fd); if (fi->texture)
dma->fd = -1; {
gs_texture_destroy(fi->texture);
fi->texture = NULL;
} }
break; close(fi->fd);
fi->fd = -1;
} }
/* otherwise find a free buffer for use */ /* otherwise find a free buffer for use */
if (!dma) if (!fi)
for (int i = 0; i < ARRAY_LENGTH(this->dmaInfo); ++i) for (int i = 0; i < ARRAY_LENGTH(this->dmaInfo); ++i)
if (!this->dmaInfo[i].frame) if (!this->dmaInfo[i].frame)
{ {
dma = this->dmaInfo + i; fi = &this->dmaInfo[i];
dma->frame = frame; fi->frame = frame;
dma->fd = -1; fi->fd = -1;
break; break;
} }
assert(dma); assert(fi);
/* open the buffer */ /* open the buffer */
if (dma->fd == -1) if (fi->fd == -1)
{ {
const uintptr_t pos = (uintptr_t) msg->mem - (uintptr_t) this->shmDev.mem; const uintptr_t pos = (uintptr_t) msg->mem - (uintptr_t) this->shmDev.mem;
const uintptr_t offset = (uintptr_t) frame->offset + sizeof(FrameBuffer); const uintptr_t offset = (uintptr_t) frame->offset + sizeof(FrameBuffer);
dma->dataSize = dataSize; fi->dataSize = dataSize;
dma->fd = ivshmemGetDMABuf(&this->shmDev, pos + offset, dataSize); fi->fd = ivshmemGetDMABuf(&this->shmDev, pos + offset, dataSize);
if (dma->fd < 0) fi->texture = NULL;
if (fi->fd < 0)
{ {
puts("Failed to get the DMA buffer for the frame"); puts("Failed to get the DMA buffer for the frame");
return -1; return NULL;
} }
} }
return dma->fd; return fi;
} }
#endif #endif
@ -659,7 +678,7 @@ static void lgVideoTick(void * data, float seconds)
obs_enter_graphics(); obs_enter_graphics();
if (this->texture) if (this->texture)
{ {
if (this->dstTexture && this->dstTexture != this->texture) if (this->unpack && this->dstTexture)
{ {
gs_texture_destroy(this->dstTexture); gs_texture_destroy(this->dstTexture);
this->dstTexture = NULL; this->dstTexture = NULL;
@ -672,33 +691,31 @@ static void lgVideoTick(void * data, float seconds)
this->texture = NULL; this->texture = NULL;
} }
enum gs_color_format format; this->dataWidth = frame->dataWidth;
uint32_t drm_format; this->unpack = false;
unsigned width = frame->dataWidth;
bool unpack = false;
this->bpp = 4; this->bpp = 4;
switch(this->type) switch(this->type)
{ {
case FRAME_TYPE_BGRA: case FRAME_TYPE_BGRA:
format = GS_BGRA_UNORM; this->format = GS_BGRA_UNORM;
drm_format = DRM_FORMAT_ARGB8888; this->drmFormat = DRM_FORMAT_ARGB8888;
#if LIBOBS_API_MAJOR_VER >= 28 #if LIBOBS_API_MAJOR_VER >= 28
this->colorSpace = GS_CS_SRGB; this->colorSpace = GS_CS_SRGB;
#endif #endif
break; break;
case FRAME_TYPE_RGBA: case FRAME_TYPE_RGBA:
format = GS_RGBA_UNORM; this->format = GS_RGBA_UNORM;
drm_format = DRM_FORMAT_ARGB8888; this->drmFormat = DRM_FORMAT_ARGB8888;
#if LIBOBS_API_MAJOR_VER >= 28 #if LIBOBS_API_MAJOR_VER >= 28
this->colorSpace = GS_CS_SRGB; this->colorSpace = GS_CS_SRGB;
#endif #endif
break; break;
case FRAME_TYPE_RGBA10: case FRAME_TYPE_RGBA10:
format = GS_R10G10B10A2; this->format = GS_R10G10B10A2;
drm_format = DRM_FORMAT_BGRA1010102; this->drmFormat = DRM_FORMAT_BGRA1010102;
#if LIBOBS_API_MAJOR_VER >= 28 #if LIBOBS_API_MAJOR_VER >= 28
this->colorSpace = GS_CS_709_SCRGB; this->colorSpace = GS_CS_709_SCRGB;
#endif #endif
@ -706,22 +723,22 @@ static void lgVideoTick(void * data, float seconds)
case FRAME_TYPE_RGB_24: case FRAME_TYPE_RGB_24:
this->bpp = 3; this->bpp = 3;
width = frame->pitch / 4; this->dataWidth = frame->pitch / 4;
/* fallthrough */ /* fallthrough */
case FRAME_TYPE_BGR_32: case FRAME_TYPE_BGR_32:
format = GS_BGRA_UNORM; this->format = GS_BGRA_UNORM;
drm_format = DRM_FORMAT_ARGB8888; this->drmFormat = DRM_FORMAT_ARGB8888;
#if LIBOBS_API_MAJOR_VER >= 28 #if LIBOBS_API_MAJOR_VER >= 28
this->colorSpace = GS_CS_SRGB; this->colorSpace = GS_CS_SRGB;
#endif #endif
unpack = true; this->unpack = true;
break; break;
case FRAME_TYPE_RGBA16F: case FRAME_TYPE_RGBA16F:
this->bpp = 8; this->bpp = 8;
format = GS_RGBA16F; this->format = GS_RGBA16F;
drm_format = DRM_FORMAT_ABGR16161616F; this->drmFormat = DRM_FORMAT_ABGR16161616F;
#if LIBOBS_API_MAJOR_VER >= 28 #if LIBOBS_API_MAJOR_VER >= 28
this->colorSpace = GS_CS_709_SCRGB; this->colorSpace = GS_CS_709_SCRGB;
#endif #endif
@ -738,21 +755,23 @@ static void lgVideoTick(void * data, float seconds)
#if LIBOBS_API_MAJOR_VER >= 27 #if LIBOBS_API_MAJOR_VER >= 27
if (this->dmabuf) if (this->dmabuf)
{ {
int fd = dmabufGetFd(this, &msg, frame, frame->frameHeight * frame->pitch); DMAFrameInfo * fi = dmabufOpenDMAFrameInfo(this, &msg, frame,
if (fd >= 0) frame->frameHeight * frame->pitch);
if (fi && !fi->texture)
{ {
this->texture = gs_texture_create_from_dmabuf( // create the first texture now so we can test if dmabuf is usable
width, fi->texture = gs_texture_create_from_dmabuf(
this->dataWidth,
this->dataHeight, this->dataHeight,
drm_format, this->drmFormat,
format, this->format,
1, 1,
&fd, &fi->fd,
&(uint32_t) { frame->pitch }, &(uint32_t) { frame->pitch },
&(uint32_t) { 0 }, &(uint32_t) { 0 },
&(uint64_t) { 0 }); &(uint64_t) { 0 });
if (!this->texture) if (!fi->texture)
{ {
puts("Failed to create dmabuf texture"); puts("Failed to create dmabuf texture");
this->dmabuf = false; this->dmabuf = false;
@ -760,15 +779,15 @@ static void lgVideoTick(void * data, float seconds)
} }
} }
#else #else
(void)drm_format; (void)drmFormat;
#endif #endif
if (!this->dmabuf) if (!this->dmabuf)
{ {
this->texture = gs_texture_create( this->texture = gs_texture_create(
width, this->dataWidth,
this->dataHeight, this->dataHeight,
format, this->format,
1, 1,
NULL, NULL,
GS_DYNAMIC); GS_DYNAMIC);
@ -785,7 +804,7 @@ static void lgVideoTick(void * data, float seconds)
gs_texture_map(this->texture, &this->texData, &this->linesize); gs_texture_map(this->texture, &this->texData, &this->linesize);
} }
if (unpack) if (this->unpack)
{ {
// create the render target for format unpacking // create the render target for format unpacking
this->dstTexture = gs_texture_create( this->dstTexture = gs_texture_create(
@ -796,14 +815,43 @@ static void lgVideoTick(void * data, float seconds)
NULL, NULL,
GS_RENDER_TARGET); GS_RENDER_TARGET);
} }
else
this->dstTexture = this->texture;
obs_leave_graphics(); obs_leave_graphics();
} }
// if using dmabuf there is nothing more here to do #if LIBOBS_API_MAJOR_VER >= 27
if (!this->texture || this->dmabuf) if (this->dmabuf)
{
DMAFrameInfo * fi = dmabufOpenDMAFrameInfo(this, &msg, frame,
frame->frameHeight * frame->pitch);
if (!fi->texture)
{
fi->texture = gs_texture_create_from_dmabuf(
this->dataWidth,
this->dataHeight,
this->drmFormat,
this->format,
1,
&fi->fd,
&(uint32_t) { frame->pitch },
&(uint32_t) { 0 },
&(uint64_t) { 0 });
}
lgmpClientMessageDone(this->frameQueue);
// wait for the frame to be complete before we try to use it
FrameBuffer * fb = (FrameBuffer *)(((uint8_t*)frame) + frame->offset);
framebuffer_wait(fb, frame->frameHeight * frame->pitch);
this->dmaTexture = fi->texture;
os_sem_post(this->frameSem);
return;
}
#endif
if (!this->texture)
{ {
lgmpClientMessageDone(this->frameQueue); lgmpClientMessageDone(this->frameQueue);
os_sem_post(this->frameSem); os_sem_post(this->frameSem);
@ -833,14 +881,21 @@ static void lgVideoTick(void * data, float seconds)
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;
if (!this->texture) #if LIBOBS_API_MAJOR_VER >= 27
texture = this->dmaTexture;
if (!texture)
texture = this->texture;
#endif
if (!texture)
return; return;
if (this->type == FRAME_TYPE_RGB_24 || this->type == FRAME_TYPE_BGR_32) if (this->type == FRAME_TYPE_RGB_24 || this->type == FRAME_TYPE_BGR_32)
{ {
effect = this->unpackEffect; effect = this->unpackEffect;
gs_effect_set_texture(this->image, this->texture); gs_effect_set_texture(this->image, texture);
struct vec2 outputSize; struct vec2 outputSize;
vec2_set(&outputSize, this->frameWidth, this->frameHeight); vec2_set(&outputSize, this->frameWidth, this->frameHeight);
gs_effect_set_vec2(this->outputSize, &outputSize); gs_effect_set_vec2(this->outputSize, &outputSize);
@ -850,11 +905,14 @@ static void lgVideoRender(void * data, gs_effect_t * effect)
{ {
effect = obs_get_base_effect(OBS_EFFECT_OPAQUE); effect = obs_get_base_effect(OBS_EFFECT_OPAQUE);
gs_eparam_t * image = gs_effect_get_param_by_name(effect, "image"); gs_eparam_t * image = gs_effect_get_param_by_name(effect, "image");
gs_effect_set_texture(image, this->texture); gs_effect_set_texture(image, texture);
} }
if (this->unpack)
texture = this->dstTexture;
while (gs_effect_loop(effect, "Draw")) while (gs_effect_loop(effect, "Draw"))
gs_draw_sprite(this->dstTexture, 0, 0, 0); gs_draw_sprite(texture, 0, 0, 0);
if (this->cursorVisible && this->cursorTex) if (this->cursorVisible && this->cursorTex)
{ {