Send initial draw on client connect for DMA-BUF encoder

Ensure an initial draw is sent immediately upon client connection
if the DMA-BUF video encoder is in use. Without this, a frame would
only be pushed when the screen content changes and the GPU renders
a new frame—which could take a while, causing delays for the client.
GStreamer now receives a duplicated file descriptor (fd) and takes
responsibility for closing it. This guarantees the buffer remains
available long enough. The previous fd mechanism is still retained
to ensure a valid scanout buffer is always available for a new
client.
This commit is contained in:
Michael Scherle 2025-05-13 14:09:09 +02:00
parent b0374ff7da
commit 6c1a518539
5 changed files with 39 additions and 22 deletions

View File

@ -66,6 +66,7 @@ struct DisplayChannelClientPrivate
std::array<VideoStreamAgent, NUM_STREAMS> stream_agents;
uint64_t streams_max_bit_rate;
bool gl_draw_ongoing;
bool gl_initial_draw_ongoing = FALSE;
};
#include "pop-visibility.h"

View File

@ -20,6 +20,7 @@
#include <common/marshaller.h>
#include <common/generated_server_marshallers.h>
#include "dcc.h"
#include "dcc-private.h"
#include "display-channel-private.h"
#include "red-qxl.h"
@ -1761,10 +1762,7 @@ static void red_free_cb(VideoEncoderDmabufData *video_dmabuf_data)
{
auto dmabuf_data = static_cast<DmabufData*>(video_dmabuf_data);
auto dcc = dmabuf_data->dcc;
DisplayChannel *display = DCC_TO_DC(dcc);
dcc->priv->gl_draw_ongoing = false;
display_channel_gl_draw_done(display);
dcc_handle_gl_draw_done(dcc);
delete dmabuf_data;
}
@ -1803,7 +1801,7 @@ static void red_marshall_gl_draw_stream(DisplayChannelClient *dcc,
return;
}
dmabuf_data->drm_dma_buf_fd = scanout->fd[0];
dmabuf_data->drm_dma_buf_fd = dup(scanout->fd[0]);
dmabuf_data->drm_fourcc_format = scanout->fourcc;
dmabuf_data->width = stream->width;
dmabuf_data->height = stream->height;
@ -1828,9 +1826,7 @@ static void red_marshall_gl_draw_stream(DisplayChannelClient *dcc,
spice_warning("bad ret value (%d) from VideoEncoder::encode_dmabuf",
ret);
}
dcc->priv->gl_draw_ongoing = false;
display_channel_gl_draw_done(display);
dcc_handle_gl_draw_done(dcc);
delete dmabuf_data;
return;
}
@ -2432,9 +2428,7 @@ static void marshall_gl_draw(DisplayChannelClient *dcc,
} else if (DCC_TO_DC(dcc)->priv->gl_draw_stream) {
red_marshall_gl_draw_stream(dcc, m);
} else if (dcc->priv->gl_draw_ongoing) {
auto display = DCC_TO_DC(dcc);
dcc->priv->gl_draw_ongoing = false;
display_channel_gl_draw_done(display);
dcc_handle_gl_draw_done(dcc);
} else {
spice_warning("nothing to send to the client");
}

View File

@ -24,6 +24,7 @@
#include "red-client.h"
#include "main-channel-client.h"
#include <spice-server-enums.h>
#include "red-qxl.h"
#define DISPLAY_CLIENT_SHORT_TIMEOUT 15000000000ULL //nano
#define DISPLAY_FREE_LIST_DEFAULT_SIZE 128
@ -421,6 +422,22 @@ void dcc_start(DisplayChannelClient *dcc)
dcc->pipe_add(dcc_gl_scanout_item_new(dcc, nullptr, 0));
dcc_push_monitors_config(dcc);
}
if (!dcc->is_gl_client()) {
QXLInstance* qxl = display->priv->qxl;
RedGLScanout *scanout = red_qxl_get_gl_scanout(qxl);
if (scanout) {
SpiceMsgDisplayGlDraw draw = {
.x = 0,
.y = 0,
.w = scanout->width,
.h = scanout->height
};
red_qxl_put_gl_scanout(qxl, scanout);
dcc->pipe_add(dcc_gl_draw_item_new(dcc, &draw, -1));
dcc_push_monitors_config(dcc);
}
}
}
static void dcc_destroy_stream_agents(DisplayChannelClient *dcc)
@ -531,7 +548,11 @@ RedPipeItemPtr dcc_gl_draw_item_new(RedChannelClient *rcc, void *data, int num)
}
}
dcc->priv->gl_draw_ongoing = TRUE;
if (num >= 0) {
dcc->priv->gl_draw_ongoing = TRUE;
} else {
dcc->priv->gl_initial_draw_ongoing = TRUE;
}
auto item = red::make_shared<RedGlDrawItem>();
item->draw = *draw;
@ -983,19 +1004,20 @@ GArray *dcc_get_preferred_video_codecs_for_encoding(DisplayChannelClient *dcc)
return display_channel_get_video_codecs(DCC_TO_DC(dcc));
}
static bool dcc_handle_gl_draw_done(DisplayChannelClient *dcc)
bool dcc_handle_gl_draw_done(DisplayChannelClient *dcc)
{
DisplayChannel *display = DCC_TO_DC(dcc);
if (G_UNLIKELY(!dcc->priv->gl_draw_ongoing)) {
if (G_UNLIKELY(!dcc->priv->gl_initial_draw_ongoing && !dcc->priv->gl_draw_ongoing)) {
g_warning("unexpected DRAW_DONE received\n");
/* close client connection */
return FALSE;
}
dcc->priv->gl_draw_ongoing = FALSE;
display_channel_gl_draw_done(display);
if (!dcc->priv->gl_initial_draw_ongoing) {
dcc->priv->gl_draw_ongoing = FALSE;
display_channel_gl_draw_done(display);
}
dcc->priv->gl_initial_draw_ongoing = FALSE;
return TRUE;
}

View File

@ -144,6 +144,7 @@ int dcc_compress_image (DisplayCha
SpiceImage *dest, SpiceBitmap *src, Drawable *drawable,
int can_lossy,
compress_send_data_t* o_comp_data);
bool dcc_handle_gl_draw_done (DisplayChannelClient *dcc);
void dcc_create_surface(DisplayChannelClient *dcc, struct RedSurface *surface);
void dcc_push_surface_image(DisplayChannelClient *dcc, struct RedSurface *surface);

View File

@ -1793,10 +1793,9 @@ spice_gst_encoder_encode_dmabuf(VideoEncoder *video_encoder,
*outbuf = NULL;
rc = VIDEO_ENCODER_FRAME_UNSUPPORTED;
mem = gst_dmabuf_allocator_alloc_with_flags(encoder->allocator,
dmabuf_data->drm_dma_buf_fd,
size,
GST_FD_MEMORY_FLAG_DONT_CLOSE);
mem = gst_dmabuf_allocator_alloc(encoder->allocator,
dmabuf_data->drm_dma_buf_fd,
size);
if (!mem) {
return rc;
}