ui/spice: Add an option to submit gl_draw requests at fixed rate
In the specific case where the display layer (virtio-gpu) is using dmabuf, and if remote clients are enabled (-spice gl=on,port=xxxx), it makes sense to limit the maximum (streaming) rate (refresh rate) to a fixed value using the GUI refresh timer. Otherwise, the updates or gl_draw requests would be sent as soon as the Guest submits a new frame which is not optimal as it would lead to increased network traffic and wastage of GPU cycles if the frames get dropped. Cc: Gerd Hoffmann <kraxel@redhat.com> Cc: Marc-André Lureau <marcandre.lureau@redhat.com> Cc: Dmitry Osipenko <dmitry.osipenko@collabora.com> Cc: Frediano Ziglio <freddy77@gmail.com> Cc: Dongwon Kim <dongwon.kim@intel.com> Cc: Michael Scherle <michael.scherle@rz.uni-freiburg.de> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Signed-off-by: Vivek Kasireddy <vivek.kasireddy@intel.com> Message-Id: <20250617043546.1022779-5-vivek.kasireddy@intel.com>
This commit is contained in:
parent
bd46161dd1
commit
50d135e377
4 changed files with 70 additions and 10 deletions
|
|
@ -152,6 +152,7 @@ struct SimpleSpiceCursor {
|
|||
|
||||
extern bool spice_opengl;
|
||||
extern bool spice_remote_client;
|
||||
extern int spice_max_refresh_rate;
|
||||
|
||||
int qemu_spice_rect_is_empty(const QXLRect* r);
|
||||
void qemu_spice_rect_union(QXLRect *dest, const QXLRect *r);
|
||||
|
|
|
|||
|
|
@ -2282,6 +2282,7 @@ DEF("spice", HAS_ARG, QEMU_OPTION_spice,
|
|||
" [,disable-agent-file-xfer=on|off][,agent-mouse=[on|off]]\n"
|
||||
" [,playback-compression=[on|off]][,seamless-migration=[on|off]]\n"
|
||||
" [,video-codec=<codec>\n"
|
||||
" [,max-refresh-rate=rate\n"
|
||||
" [,gl=[on|off]][,rendernode=<file>]\n"
|
||||
" enable spice\n"
|
||||
" at least one of {port, tls-port} is mandatory\n",
|
||||
|
|
@ -2377,6 +2378,10 @@ SRST
|
|||
would be used as default. And, for the case where gl=off, the
|
||||
default codec to be used is determined by the Spice server.
|
||||
|
||||
``max-refresh-rate=rate``
|
||||
Provide the maximum refresh rate (or FPS) at which the encoding
|
||||
requests should be sent to the Spice server. Default would be 30.
|
||||
|
||||
``gl=[on|off]``
|
||||
Enable/disable OpenGL context. Default is off.
|
||||
|
||||
|
|
|
|||
|
|
@ -56,6 +56,8 @@ struct SpiceTimer {
|
|||
QEMUTimer *timer;
|
||||
};
|
||||
|
||||
#define DEFAULT_MAX_REFRESH_RATE 30
|
||||
|
||||
static SpiceTimer *timer_add(SpiceTimerFunc func, void *opaque)
|
||||
{
|
||||
SpiceTimer *timer;
|
||||
|
|
@ -491,6 +493,9 @@ static QemuOptsList qemu_spice_opts = {
|
|||
},{
|
||||
.name = "video-codec",
|
||||
.type = QEMU_OPT_STRING,
|
||||
},{
|
||||
.name = "max-refresh-rate",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
},{
|
||||
.name = "agent-mouse",
|
||||
.type = QEMU_OPT_BOOL,
|
||||
|
|
@ -804,6 +809,13 @@ static void qemu_spice_init(void)
|
|||
spice_server_set_streaming_video(spice_server, SPICE_STREAM_VIDEO_OFF);
|
||||
}
|
||||
|
||||
spice_max_refresh_rate = qemu_opt_get_number(opts, "max-refresh-rate",
|
||||
DEFAULT_MAX_REFRESH_RATE);
|
||||
if (spice_max_refresh_rate <= 0) {
|
||||
error_report("max refresh rate/fps is invalid");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
spice_server_set_agent_mouse
|
||||
(spice_server, qemu_opt_get_bool(opts, "agent-mouse", 1));
|
||||
spice_server_set_playback_compression
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
|
||||
bool spice_opengl;
|
||||
bool spice_remote_client;
|
||||
int spice_max_refresh_rate;
|
||||
|
||||
int qemu_spice_rect_is_empty(const QXLRect* r)
|
||||
{
|
||||
|
|
@ -844,12 +845,32 @@ static void qemu_spice_gl_block_timer(void *opaque)
|
|||
warn_report("spice: no gl-draw-done within one second");
|
||||
}
|
||||
|
||||
static void spice_gl_draw(SimpleSpiceDisplay *ssd,
|
||||
uint32_t x, uint32_t y, uint32_t w, uint32_t h)
|
||||
{
|
||||
uint64_t cookie;
|
||||
|
||||
cookie = (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_GL_DRAW_DONE, 0);
|
||||
spice_qxl_gl_draw_async(&ssd->qxl, x, y, w, h, cookie);
|
||||
}
|
||||
|
||||
static void spice_gl_refresh(DisplayChangeListener *dcl)
|
||||
{
|
||||
SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
|
||||
uint64_t cookie;
|
||||
|
||||
if (!ssd->ds || qemu_console_is_gl_blocked(ssd->dcl.con)) {
|
||||
if (!ssd->ds) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (qemu_console_is_gl_blocked(ssd->dcl.con)) {
|
||||
if (spice_remote_client && ssd->gl_updates && ssd->have_scanout) {
|
||||
glFlush();
|
||||
spice_gl_draw(ssd, 0, 0,
|
||||
surface_width(ssd->ds), surface_height(ssd->ds));
|
||||
ssd->gl_updates = 0;
|
||||
/* E.g, to achieve 60 FPS, update_interval needs to be ~16.66 ms */
|
||||
dcl->update_interval = 1000 / spice_max_refresh_rate;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -857,11 +878,8 @@ static void spice_gl_refresh(DisplayChangeListener *dcl)
|
|||
if (ssd->gl_updates && ssd->have_surface) {
|
||||
qemu_spice_gl_block(ssd, true);
|
||||
glFlush();
|
||||
cookie = (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_GL_DRAW_DONE, 0);
|
||||
spice_qxl_gl_draw_async(&ssd->qxl, 0, 0,
|
||||
surface_width(ssd->ds),
|
||||
surface_height(ssd->ds),
|
||||
cookie);
|
||||
spice_gl_draw(ssd, 0, 0,
|
||||
surface_width(ssd->ds), surface_height(ssd->ds));
|
||||
ssd->gl_updates = 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -954,6 +972,20 @@ static void qemu_spice_gl_scanout_disable(DisplayChangeListener *dcl)
|
|||
SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
|
||||
|
||||
trace_qemu_spice_gl_scanout_disable(ssd->qxl.id);
|
||||
|
||||
/*
|
||||
* We need to check for the case of "lost" updates, where a gl_draw
|
||||
* was not submitted because the timer did not get a chance to run.
|
||||
* One case where this happens is when the Guest VM is getting
|
||||
* rebooted. If the console is blocked in this situation, we need
|
||||
* to unblock it. Otherwise, newer updates would not take effect.
|
||||
*/
|
||||
if (qemu_console_is_gl_blocked(ssd->dcl.con)) {
|
||||
if (spice_remote_client && ssd->gl_updates && ssd->have_scanout) {
|
||||
ssd->gl_updates = 0;
|
||||
qemu_spice_gl_block(ssd, false);
|
||||
}
|
||||
}
|
||||
spice_server_gl_scanout(&ssd->qxl, NULL, 0, 0, NULL, NULL, 0, DRM_FORMAT_INVALID,
|
||||
DRM_FORMAT_MOD_INVALID, false);
|
||||
qemu_spice_gl_monitor_config(ssd, 0, 0, 0, 0);
|
||||
|
|
@ -1061,7 +1093,6 @@ static void qemu_spice_gl_update(DisplayChangeListener *dcl,
|
|||
EGLint fourcc = 0;
|
||||
bool render_cursor = false;
|
||||
bool y_0_top = false; /* FIXME */
|
||||
uint64_t cookie;
|
||||
uint32_t width, height, texture;
|
||||
|
||||
if (!ssd->have_scanout) {
|
||||
|
|
@ -1159,8 +1190,19 @@ static void qemu_spice_gl_update(DisplayChangeListener *dcl,
|
|||
trace_qemu_spice_gl_update(ssd->qxl.id, w, h, x, y);
|
||||
qemu_spice_gl_block(ssd, true);
|
||||
glFlush();
|
||||
cookie = (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_GL_DRAW_DONE, 0);
|
||||
spice_qxl_gl_draw_async(&ssd->qxl, x, y, w, h, cookie);
|
||||
|
||||
/*
|
||||
* In the case of remote clients, the submission of gl_draw request is
|
||||
* deferred here, so that it can be submitted later (to spice server)
|
||||
* from spice_gl_refresh() timer callback. This is done to ensure that
|
||||
* Guest updates are submitted at a steady rate (e.g. 60 FPS) instead
|
||||
* of submitting them arbitrarily.
|
||||
*/
|
||||
if (spice_remote_client) {
|
||||
ssd->gl_updates++;
|
||||
} else {
|
||||
spice_gl_draw(ssd, x, y, w, h);
|
||||
}
|
||||
}
|
||||
|
||||
static const DisplayChangeListenerOps display_listener_gl_ops = {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue