feat: add spice_server_set_rendernode which sets the gpu to encode for va

This commit is contained in:
Michael Scherle 2026-04-21 09:23:52 +02:00
parent e330e7cafc
commit d298bb2f14
16 changed files with 73 additions and 42 deletions

View File

@ -1004,6 +1004,11 @@ GArray *dcc_get_preferred_video_codecs_for_encoding(DisplayChannelClient *dcc)
return display_channel_get_video_codecs(DCC_TO_DC(dcc));
}
const char * dcc_get_renderNode(DisplayChannelClient *dcc)
{
return DCC_TO_DC(dcc)->priv->rendernode;
}
bool dcc_handle_gl_draw_done(DisplayChannelClient *dcc)
{
DisplayChannel *display = DCC_TO_DC(dcc);

View File

@ -163,6 +163,7 @@ uint64_t dcc_get_max_stream_bit_rate(DisplayChannelClient *dcc);
void dcc_set_max_stream_bit_rate(DisplayChannelClient *dcc, uint64_t rate);
gboolean dcc_is_low_bandwidth(DisplayChannelClient *dcc);
GArray *dcc_get_preferred_video_codecs_for_encoding(DisplayChannelClient *dcc);
const char * dcc_get_renderNode(DisplayChannelClient *dcc);
void dcc_video_codecs_update(DisplayChannelClient *dcc);
#include "pop-visibility.h"

View File

@ -106,6 +106,7 @@ struct DisplayChannelPrivate
_Drawable *free_drawables;
int stream_video;
const char *rendernode;
GArray *video_codecs;
uint32_t stream_count;
std::array<VideoStream, NUM_STREAMS> streams_buf;

View File

@ -2163,13 +2163,14 @@ display_channel_new(RedsState *reds, QXLInstance *qxl,
SpiceCoreInterfaceInternal *core, Dispatcher *dispatcher,
int migrate, int stream_video,
GArray *video_codecs,
const char* rendernode,
uint32_t n_surfaces)
{
/* FIXME: migrate is not used...? */
spice_debug("create display channel");
auto display =
red::make_shared<DisplayChannel>(reds, qxl, core, dispatcher, migrate, stream_video,
video_codecs, n_surfaces);
video_codecs, rendernode, n_surfaces);
if (display) {
display_channel_set_stream_video(display.get(), stream_video);
display->priv->gl_draw_stream = nullptr;
@ -2183,6 +2184,7 @@ DisplayChannel::DisplayChannel(RedsState *reds,
Dispatcher *dispatcher,
int migrate, int stream_video,
GArray *video_codecs,
const char *rendernode,
uint32_t n_surfaces):
CommonGraphicsChannel(reds, SPICE_CHANNEL_DISPLAY, qxl->id,
RedChannel::MigrateAll|RedChannel::HandleAcks, core, dispatcher)
@ -2238,6 +2240,7 @@ DisplayChannel::DisplayChannel(RedsState *reds,
set_cap(SPICE_DISPLAY_CAP_CODEC_VP9_444);
set_cap(SPICE_DISPLAY_CAP_CODEC_H265_444);
// set_cap(SPICE_DISPLAY_CAP_CODEC_VP9_U);
priv->rendernode = rendernode;
reds_register_channel(reds, this);
}

View File

@ -47,6 +47,7 @@ struct DisplayChannel final: public CommonGraphicsChannel
Dispatcher *dispatcher,
int migrate, int stream_video,
GArray *video_codecs,
const char *rendernode,
uint32_t n_surfaces);
~DisplayChannel();
void on_connect(RedClient *client, RedStream *stream, int migration,
@ -93,6 +94,7 @@ display_channel_new(RedsState *reds, QXLInstance *qxl,
SpiceCoreInterfaceInternal *core, Dispatcher *dispatcher,
int migrate, int stream_video,
GArray *video_codecs,
const char* rendernode,
uint32_t n_surfaces);
void display_channel_surface_id_unref(DisplayChannel *display, uint32_t surface_id);
RedSurface *display_channel_create_surface(DisplayChannel *display, uint32_t surface_id,

View File

@ -304,6 +304,7 @@ typedef struct SpiceGstEncoder {
/* How many frames were dropped by the server since the last encoded frame. */
uint32_t server_drops;
GstAllocator *allocator;
const char * rendernode;
} SpiceGstEncoder;
@ -975,43 +976,38 @@ static bool gst_features_lookup(const gchar *feature_name)
return true;
}
static const gchar *get_encode_gpu(){
const gchar* env_encode_gpu_str = g_getenv("SPICE_ENCODE_GPU");
if ((!env_encode_gpu_str) || (strcmp(env_encode_gpu_str, "renderD128") == 0)) {
return NULL;
}
g_autoptr(GRegex) regex = g_regex_new("renderD1[[:digit:]]{2}", 0, 0, NULL);
if (g_regex_match(regex, env_encode_gpu_str, 0, NULL)) {
return env_encode_gpu_str;
}
return NULL;
}
static gchar *find_best_hw_plugin(const gchar *codec_name)
static gchar *find_best_hw_plugin(const gchar *codec_name, const char *rendernode)
{
char plugins[][20] = {"va","msdk", "vaapi"};
char plugins[][8] = {"va","msdk", "vaapi"};
char postproc[][10] ={"postproc","vpp","postproc"};
gchar *feature_name;
int i;
const gchar *encode_gpu = get_encode_gpu();
if (encode_gpu) {
g_strlcat(plugins[1], encode_gpu, sizeof(plugins[1]));
}
for (i = 0; i < G_N_ELEMENTS(plugins); i++) {
feature_name = !codec_name ? g_strconcat(plugins[i], "postproc", NULL) :
g_strconcat(plugins[i], codec_name, "enc", NULL);
if (gst_features_lookup(feature_name)) {
return feature_name;
}
g_free(feature_name);
if(codec_name){
feature_name = g_strconcat(plugins[i], codec_name, "lpenc", NULL);
if (gst_features_lookup(feature_name)) {
return feature_name;
if (codec_name == NULL){
feature_name = g_strdup_printf("%s%s", plugins[i], postproc[i]);
if (!gst_features_lookup(feature_name)) {
g_free(feature_name);
continue;
}
} else {
feature_name = g_strconcat(plugins[i], codec_name, "enc", NULL);
if (!gst_features_lookup(feature_name)) {
g_free(feature_name);
feature_name = g_strconcat(plugins[i], codec_name, "lpenc", NULL);
if (!gst_features_lookup(feature_name)) {
g_free(feature_name);
continue;
}
}
}
if(i == 0 && rendernode){
gchar * feature_name_with_render = g_strdup_printf("%s device-path=%s",feature_name, rendernode);
g_free(feature_name);
return feature_name_with_render;
}
}
return NULL;
@ -1058,11 +1054,11 @@ static void try_intel_hw_plugins(SpiceGstEncoder* spice_encoder, gchar **convert
return;
}
encoder = find_best_hw_plugin(codec_name);
encoder = find_best_hw_plugin(codec_name, spice_encoder->rendernode);
if (!encoder) {
return;
}
vpp = find_best_hw_plugin(NULL);
vpp = find_best_hw_plugin(NULL,spice_encoder->rendernode);
if (!vpp) {
g_free(encoder);
return;
@ -2077,7 +2073,8 @@ VideoEncoder *gstreamer_encoder_new(SpiceVideoCodecType codec_type,
uint64_t starting_bit_rate,
VideoEncoderRateControlCbs *cbs,
bitmap_ref_t bitmap_ref,
bitmap_unref_t bitmap_unref)
bitmap_unref_t bitmap_unref,
const char * rendernode)
{
SPICE_VERIFY(SPICE_GST_FRAME_STATISTICS_COUNT <= SPICE_GST_HISTORY_SIZE);
spice_return_val_if_fail(codec_type == SPICE_VIDEO_CODEC_TYPE_MJPEG ||
@ -2122,6 +2119,7 @@ VideoEncoder *gstreamer_encoder_new(SpiceVideoCodecType codec_type,
encoder->bitmap_ref = bitmap_ref;
encoder->bitmap_unref = bitmap_unref;
encoder->format = GSTREAMER_FORMAT_INVALID;
encoder->rendernode = rendernode;
pthread_mutex_init(&encoder->outbuf_mutex, NULL);
pthread_cond_init(&encoder->outbuf_cond, NULL);

View File

@ -1364,7 +1364,8 @@ VideoEncoder *mjpeg_encoder_new(SpiceVideoCodecType codec_type,
uint64_t starting_bit_rate,
VideoEncoderRateControlCbs *cbs,
bitmap_ref_t bitmap_ref,
bitmap_unref_t bitmap_unref)
bitmap_unref_t bitmap_unref,
const char *rendernode)
{
MJpegEncoder *encoder;

View File

@ -990,7 +990,9 @@ RedWorker* red_worker_new(QXLInstance *qxl)
FALSE,
reds_get_streaming_video(reds),
reds_get_video_codecs(reds),
init_info.n_surfaces).get(); // XXX
reds_get_rendernode(reds),
init_info.n_surfaces
).get(); // XXX
channel = worker->display_channel;
channel->init_stat_node(&worker->stat, "display_channel");
display_channel_set_image_compression(worker->display_channel,

View File

@ -121,6 +121,7 @@ struct RedServerConfig {
char *sasl_appname;
#endif
char *spice_name;
char *rendernode;
bool spice_uuid_is_set;
uint8_t spice_uuid[16];
@ -4093,6 +4094,16 @@ SPICE_GNUC_VISIBLE int spice_server_set_video_codecs(SpiceServer *reds, const ch
return 0;
}
SPICE_GNUC_VISIBLE int spice_server_set_rendernode(SpiceServer *s, const char *rendernode) {
g_free(s->config->rendernode);
s->config->rendernode = g_strdup(rendernode);
return 0;
}
const char* reds_get_rendernode(const RedsState *reds){
return reds->config->rendernode;
}
SPICE_GNUC_VISIBLE const char *spice_server_get_video_codecs(SpiceServer *reds)
{
return video_codecs_to_string(reds_get_video_codecs(reds), ";");

View File

@ -97,6 +97,7 @@ void reds_on_main_channel_migrate(RedsState *reds, MainChannelClient *mcc);
uint32_t reds_get_streaming_video(const RedsState *reds);
GArray* reds_get_video_codecs(const RedsState *reds);
const char* reds_get_rendernode(const RedsState *reds);
spice_wan_compression_t reds_get_jpeg_state(const RedsState *reds);
spice_wan_compression_t reds_get_zlib_glz_state(const RedsState *reds);
SpiceCoreInterfaceInternal* reds_get_core_interface(RedsState *reds);

View File

@ -139,6 +139,8 @@ enum {
int spice_server_set_video_codecs(SpiceServer *s, const char* video_codecs);
int spice_server_set_rendernode(SpiceServer *s, const char *rendernode);
/**
* Returns a newly allocated string describing video encoders/codecs
* currently allowed in @s Spice server. The string returned by

View File

@ -187,4 +187,5 @@ global:
SPICE_SERVER_0.16 {
global:
spice_qxl_gl_scanout2;
spice_server_set_rendernode;
} SPICE_SERVER_0.14.3;

View File

@ -806,7 +806,7 @@ static QXLInterface display_sif = {
.major_version = SPICE_INTERFACE_QXL_MAJOR,
.minor_version = SPICE_INTERFACE_QXL_MINOR
},
{ .attached_worker = attached_worker },
.attached_worker = attached_worker ,
.set_compression_level = set_compression_level,
.set_mm_time = nullptr,
.get_init_info = get_init_info,

View File

@ -692,7 +692,7 @@ create_video_encoder(const EncoderInfo *encoder)
spice_assert(encoder);
video_encoder = encoder->new_encoder(encoder->coded_type, starting_bit_rate, &rate_control_cbs,
(bitmap_ref_t) frame_ref, (bitmap_unref_t) frame_unref);
(bitmap_ref_t) frame_ref, (bitmap_unref_t) frame_unref, NULL);
if (video_encoder == NULL) {
exit(123);
}

View File

@ -208,19 +208,22 @@ typedef VideoEncoder* (*new_video_encoder_t)(SpiceVideoCodecType codec_type,
uint64_t starting_bit_rate,
VideoEncoderRateControlCbs *cbs,
bitmap_ref_t bitmap_ref,
bitmap_unref_t bitmap_unref);
bitmap_unref_t bitmap_unref,
const char * rendernode);
VideoEncoder* mjpeg_encoder_new(SpiceVideoCodecType codec_type,
uint64_t starting_bit_rate,
VideoEncoderRateControlCbs *cbs,
bitmap_ref_t bitmap_ref,
bitmap_unref_t bitmap_unref);
bitmap_unref_t bitmap_unref,
const char * rendernode);
#if defined(HAVE_GSTREAMER_1_0)
VideoEncoder* gstreamer_encoder_new(SpiceVideoCodecType codec_type,
uint64_t starting_bit_rate,
VideoEncoderRateControlCbs *cbs,
bitmap_ref_t bitmap_ref,
bitmap_unref_t bitmap_unref);
bitmap_unref_t bitmap_unref,
const char * rendernode);
#endif

View File

@ -820,7 +820,7 @@ static VideoEncoder* dcc_create_video_encoder(DisplayChannelClient *dcc,
continue;
}
VideoEncoder* video_encoder = video_codec->create(video_codec->type, starting_bit_rate, cbs, bitmap_ref, bitmap_unref);
VideoEncoder* video_encoder = video_codec->create(video_codec->type, starting_bit_rate, cbs, bitmap_ref, bitmap_unref, dcc_get_renderNode(dcc));
if (video_encoder) {
return video_encoder;
}
@ -828,7 +828,7 @@ static VideoEncoder* dcc_create_video_encoder(DisplayChannelClient *dcc,
/* Try to use the builtin MJPEG video encoder as a fallback */
if (!client_has_multi_codec || dcc->test_remote_cap(SPICE_DISPLAY_CAP_CODEC_MJPEG)) {
return mjpeg_encoder_new(SPICE_VIDEO_CODEC_TYPE_MJPEG, starting_bit_rate, cbs, bitmap_ref, bitmap_unref);
return mjpeg_encoder_new(SPICE_VIDEO_CODEC_TYPE_MJPEG, starting_bit_rate, cbs, bitmap_ref, bitmap_unref, dcc_get_renderNode(dcc));
}
return nullptr;