red-qxl: use RedGLScanout for multi plane support

Create a new scanout struct supporting multi plane.
We'll use this struct to create different protocol
message for clients with different capability.

Signed-off-by: Qiang Yu <yuq825@gmail.com>
Acked-by: Frediano Ziglio <freddy77@gmail.com>
This commit is contained in:
Qiang Yu 2025-03-22 09:49:48 +08:00 committed by Frediano Ziglio
parent bedd2e1bcb
commit cf678836a9
4 changed files with 71 additions and 25 deletions

View File

@ -1788,15 +1788,23 @@ static void red_marshall_gl_draw_stream(DisplayChannelClient *dcc,
}
QXLInstance* qxl = display->priv->qxl;
SpiceMsgDisplayGlScanoutUnix *scanout = red_qxl_get_gl_scanout(qxl);
RedGLScanout *scanout = red_qxl_get_gl_scanout(qxl);
if (!scanout) {
spice_warning("Cannot access scanout");
delete dmabuf_data;
return;
}
dmabuf_data->drm_dma_buf_fd = scanout->drm_dma_buf_fd;
dmabuf_data->drm_fourcc_format = scanout->drm_fourcc_format;
/* TODO: add support for it */
if (scanout->num_planes > 1) {
spice_warning("Video encoder Does not support multi plane");
red_qxl_put_gl_scanout(qxl, scanout);
delete dmabuf_data;
return;
}
dmabuf_data->drm_dma_buf_fd = scanout->fd[0];
dmabuf_data->drm_fourcc_format = scanout->fourcc;
dmabuf_data->width = stream->width;
dmabuf_data->height = stream->height;
dmabuf_data->stride = stream->stride;
@ -2370,10 +2378,22 @@ static void marshall_gl_scanout(DisplayChannelClient *dcc,
DisplayChannel *display_channel = DCC_TO_DC(dcc);
QXLInstance* qxl = display_channel->priv->qxl;
SpiceMsgDisplayGlScanoutUnix *scanout = red_qxl_get_gl_scanout(qxl);
RedGLScanout *scanout = red_qxl_get_gl_scanout(qxl);
if (scanout != nullptr) {
dcc->init_send_data(SPICE_MSG_DISPLAY_GL_SCANOUT_UNIX);
spice_marshall_msg_display_gl_scanout_unix(m, scanout);
if (scanout->num_planes <= 1) {
SpiceMsgDisplayGlScanoutUnix msg;
msg.drm_dma_buf_fd = scanout->fd[0];
msg.width = scanout->width;
msg.height = scanout->height;
msg.stride = scanout->stride[0];
msg.drm_fourcc_format = scanout->fourcc;
msg.flags = scanout->flags;
dcc->init_send_data(SPICE_MSG_DISPLAY_GL_SCANOUT_UNIX);
spice_marshall_msg_display_gl_scanout_unix(m, &msg);
} else {
spice_error("gl scanout does not support multi plane");
}
}
red_qxl_put_gl_scanout(qxl, scanout);
}

View File

@ -39,6 +39,13 @@
#include "red-qxl.h"
#ifdef HAVE_DRM_DRM_FOURCC_H
#include <drm/drm_fourcc.h>
#endif
#ifndef DRM_FORMAT_MOD_INVALID
#define DRM_FORMAT_MOD_INVALID ((1ULL << 56) - 1)
#endif
#define MAX_MONITORS_COUNT 16
@ -61,7 +68,7 @@ struct QXLState {
bool running;
pthread_mutex_t scanout_mutex;
SpiceMsgDisplayGlScanoutUnix scanout;
RedGLScanout scanout;
uint64_t gl_draw_cookie;
template <typename T>
@ -409,17 +416,17 @@ void spice_qxl_set_max_monitors(QXLInstance *instance, unsigned int max_monitors
instance->st->max_monitors = MAX(1U, max_monitors);
}
SpiceMsgDisplayGlScanoutUnix *red_qxl_get_gl_scanout(QXLInstance *qxl)
RedGLScanout *red_qxl_get_gl_scanout(QXLInstance *qxl)
{
pthread_mutex_lock(&qxl->st->scanout_mutex);
if (qxl->st->scanout.drm_dma_buf_fd >= 0) {
if (qxl->st->scanout.fd[0] >= 0) {
return &qxl->st->scanout;
}
pthread_mutex_unlock(&qxl->st->scanout_mutex);
return nullptr;
}
void red_qxl_put_gl_scanout(QXLInstance *qxl, SpiceMsgDisplayGlScanoutUnix *scanout)
void red_qxl_put_gl_scanout(QXLInstance *qxl, RedGLScanout *scanout)
{
if (scanout) {
pthread_mutex_unlock(&qxl->st->scanout_mutex);
@ -440,17 +447,22 @@ void spice_qxl_gl_scanout(QXLInstance *qxl,
pthread_mutex_lock(&qxl_state->scanout_mutex);
if (qxl_state->scanout.drm_dma_buf_fd >= 0) {
close(qxl_state->scanout.drm_dma_buf_fd);
RedGLScanout *scanout = &qxl_state->scanout;
for (int i = 0; i < scanout->num_planes; i++) {
if (scanout->fd[i] >= 0) {
close(scanout->fd[i]);
}
}
qxl_state->scanout = (SpiceMsgDisplayGlScanoutUnix) {
.drm_dma_buf_fd = fd,
qxl_state->scanout = (RedGLScanout) {
.fd = {fd, -1, -1, -1},
.width = width,
.height = height,
.stride = stride,
.drm_fourcc_format = format,
.stride = {stride, 0, 0, 0},
.fourcc = format,
.num_planes = 1,
.flags = y_0_top ? SPICE_GL_SCANOUT_FLAGS_Y0TOP : 0,
.modifier = DRM_FORMAT_MOD_INVALID,
};
pthread_mutex_unlock(&qxl_state->scanout_mutex);
@ -478,7 +490,7 @@ void spice_qxl_gl_draw_async(QXLInstance *qxl,
spice_return_if_fail(qxl != nullptr);
qxl_state = qxl->st;
if (qxl_state->scanout.drm_dma_buf_fd < 0) {
if (qxl_state->scanout.fd[0] < 0) {
spice_warning("called spice_qxl_gl_draw_async without a buffer");
red_qxl_async_complete(qxl, cookie);
return;
@ -576,9 +588,11 @@ void red_qxl_init(RedsState *reds, QXLInstance *qxl)
qxl_state->reds = reds;
qxl_state->qxl = qxl;
pthread_mutex_init(&qxl_state->scanout_mutex, nullptr);
qxl_state->scanout.drm_dma_buf_fd = -1;
qxl_state->gl_draw_cookie = GL_DRAW_COOKIE_INVALID;
qxl_state->dispatcher = red::make_shared<Dispatcher>(RED_WORKER_MESSAGE_COUNT);
for (auto& fd : qxl_state->scanout.fd) {
fd = -1;
}
qxl_state->max_monitors = UINT_MAX;
qxl->st = qxl_state;
@ -619,7 +633,7 @@ void red_qxl_clear_pending(QXLState *qxl_state, int pending)
bool red_qxl_get_allow_client_mouse(QXLInstance *qxl, int *x_res, int *y_res, int *allow_now)
{
// try to get resolution when 3D enabled, since qemu did not create QXL primary surface
SpiceMsgDisplayGlScanoutUnix *gl;
RedGLScanout *gl;
if ((gl = red_qxl_get_gl_scanout(qxl))) {
*x_res = gl->width;
*y_res = gl->height;

View File

@ -23,6 +23,18 @@
SPICE_BEGIN_DECLS
struct RedGLScanout {
int fd[4];
uint32_t width;
uint32_t height;
uint32_t offset[4];
uint32_t stride[4];
uint32_t fourcc;
uint32_t num_planes;
uint32_t flags;
uint64_t modifier;
};
void red_qxl_init(SpiceServer *reds, QXLInstance *qxl);
void red_qxl_destroy(QXLInstance *qxl);
@ -37,8 +49,8 @@ void red_qxl_start(QXLInstance *qxl);
uint32_t red_qxl_get_ram_size(QXLInstance *qxl);
gboolean red_qxl_client_monitors_config(QXLInstance *qxl, VDAgentMonitorsConfig *monitors_config);
bool red_qxl_get_allow_client_mouse(QXLInstance *qxl, int *x_res, int *y_res, int *allow_now);
SpiceMsgDisplayGlScanoutUnix *red_qxl_get_gl_scanout(QXLInstance *qxl);
void red_qxl_put_gl_scanout(QXLInstance *qxl, SpiceMsgDisplayGlScanoutUnix *scanout);
RedGLScanout *red_qxl_get_gl_scanout(QXLInstance *qxl);
void red_qxl_put_gl_scanout(QXLInstance *qxl, RedGLScanout *scanout);
void red_qxl_gl_draw_async_complete(QXLInstance *qxl);
int red_qxl_check_qxl_version(QXLInstance *qxl, int major, int minor);
SpiceServer* red_qxl_get_server(QXLState *qxl);

View File

@ -441,7 +441,7 @@ static void display_channel_create_stream(DisplayChannel *display,
bool display_channel_create_gl_draw_stream(DisplayChannel *display)
{
QXLInstance* qxl = display->priv->qxl;
SpiceMsgDisplayGlScanoutUnix *scanout = red_qxl_get_gl_scanout(qxl);
RedGLScanout *scanout = red_qxl_get_gl_scanout(qxl);
DisplayChannelClient *dcc;
VideoStream *stream;
bool ret = false;
@ -460,7 +460,7 @@ bool display_channel_create_gl_draw_stream(DisplayChannel *display)
display_channel_surface_id_unref(display, 0);
}
if (!display_channel_create_surface(display, 0, scanout->width,
scanout->height, scanout->stride,
scanout->height, scanout->stride[0],
SPICE_SURFACE_FMT_32_xRGB,
nullptr, true, true)) {
goto err;
@ -478,7 +478,7 @@ bool display_channel_create_gl_draw_stream(DisplayChannel *display)
ret = true;
display_channel_init_stream(display, stream, nullptr, &dest_area);
stream->top_down = (scanout->flags & SPICE_GL_SCANOUT_FLAGS_Y0TOP) ? 0 : 1;
stream->stride = scanout->stride;
stream->stride = scanout->stride[0];
/* This is the upper bound; it should be possible to stream at 60 FPS
* with a hardware based encoder.
*/
@ -501,7 +501,7 @@ bool is_new_stream_needed(DisplayChannel *display)
}
QXLInstance* qxl = display->priv->qxl;
SpiceMsgDisplayGlScanoutUnix *scanout = red_qxl_get_gl_scanout(qxl);
RedGLScanout *scanout = red_qxl_get_gl_scanout(qxl);
uint32_t width, height, top_down;
if (!scanout) {