mirror of
https://github.com/qemu/qemu.git
synced 2025-08-03 06:33:33 +00:00
UI-related for 10.1
- [PATCH v3 0/2] ui/vnc: Do not copy z_stream - [PATCH v6 0/7] ui/spice: Enable gl=on option for non-local or remote clients - [PATCH v6 0/1] Allow injection of virtio-gpu EDID name - [PATCH 0/2] ui/gtk: Add keep-aspect-ratio and scale option -----BEGIN PGP SIGNATURE----- iQJQBAABCgA6FiEEh6m9kz+HxgbSdvYt2ujhCXWWnOUFAmh19eYcHG1hcmNhbmRy ZS5sdXJlYXVAcmVkaGF0LmNvbQAKCRDa6OEJdZac5cLsEAC1NV4DFQmb0TjuK/Bb 81dDED9DGHsYybVy5x3xSqVkJtAoHTC4FmCm8x9T8wwg+utDvCGFfRM1GeMFR/yI IzM+2xs9PcG/+7j/HhVLWr9QhoWV/yoKHcjJScfkTrTtZxAQRA3suUdQT1RjvwUY NEuKaOx42dEpV7E+OHp8172eG8CWBzFMjH+cx2b6yKoxF1kVsB7kgVb+kCMYBEQi 1YHf34G+HGTev+IzzpxnO+P7p2lJ1ud93kCp1Yz8ua5zOUEPiaHkbClFj4M9mdsn xvaxby+zJqe33rh8pVr3qD/4R2j35OW7F5uiAQ8C96KF5Eviia8Cno1s4QInpcw/ sqtorkaP+OLO6sCnvBQqo99iMH2KloCV7b5sUzfxlUkS+3txD1AKRbodz+vhBqMN dbESdd1veUFEvi00DGbxfJbbkzVIhxAwad8CNnSjCdsvJdfYLA7TuSEuBtf1lQPF lqpVZFB6C3LQMbmTwT9YrOzMtMXQcT+GFpJLOBk0Cxv4rCSil+TeDpEUNXHurYjI qWZT+vyGDqyhoZHyQMPsBwAywKgtMC3IwnkKgJdTHroJ57Am86BvZqELRzh8Tffl nkdu1uHdNQXT/u8ybU3mStaQ7xMJALL4tlMuIZ5TIkvMeQm4CiViGb/i5LSn/GMk lx2JmBwXXf/imsXeBUfxktJFrw== =QQ/7 -----END PGP SIGNATURE----- Merge tag 'ui-pull-request' of https://gitlab.com/marcandre.lureau/qemu into staging UI-related for 10.1 - [PATCH v3 0/2] ui/vnc: Do not copy z_stream - [PATCH v6 0/7] ui/spice: Enable gl=on option for non-local or remote clients - [PATCH v6 0/1] Allow injection of virtio-gpu EDID name - [PATCH 0/2] ui/gtk: Add keep-aspect-ratio and scale option # -----BEGIN PGP SIGNATURE----- # # iQJQBAABCgA6FiEEh6m9kz+HxgbSdvYt2ujhCXWWnOUFAmh19eYcHG1hcmNhbmRy # ZS5sdXJlYXVAcmVkaGF0LmNvbQAKCRDa6OEJdZac5cLsEAC1NV4DFQmb0TjuK/Bb # 81dDED9DGHsYybVy5x3xSqVkJtAoHTC4FmCm8x9T8wwg+utDvCGFfRM1GeMFR/yI # IzM+2xs9PcG/+7j/HhVLWr9QhoWV/yoKHcjJScfkTrTtZxAQRA3suUdQT1RjvwUY # NEuKaOx42dEpV7E+OHp8172eG8CWBzFMjH+cx2b6yKoxF1kVsB7kgVb+kCMYBEQi # 1YHf34G+HGTev+IzzpxnO+P7p2lJ1ud93kCp1Yz8ua5zOUEPiaHkbClFj4M9mdsn # xvaxby+zJqe33rh8pVr3qD/4R2j35OW7F5uiAQ8C96KF5Eviia8Cno1s4QInpcw/ # sqtorkaP+OLO6sCnvBQqo99iMH2KloCV7b5sUzfxlUkS+3txD1AKRbodz+vhBqMN # dbESdd1veUFEvi00DGbxfJbbkzVIhxAwad8CNnSjCdsvJdfYLA7TuSEuBtf1lQPF # lqpVZFB6C3LQMbmTwT9YrOzMtMXQcT+GFpJLOBk0Cxv4rCSil+TeDpEUNXHurYjI # qWZT+vyGDqyhoZHyQMPsBwAywKgtMC3IwnkKgJdTHroJ57Am86BvZqELRzh8Tffl # nkdu1uHdNQXT/u8ybU3mStaQ7xMJALL4tlMuIZ5TIkvMeQm4CiViGb/i5LSn/GMk # lx2JmBwXXf/imsXeBUfxktJFrw== # =QQ/7 # -----END PGP SIGNATURE----- # gpg: Signature made Tue 15 Jul 2025 02:32:06 EDT # gpg: using RSA key 87A9BD933F87C606D276F62DDAE8E10975969CE5 # gpg: issuer "marcandre.lureau@redhat.com" # gpg: Good signature from "Marc-André Lureau <marcandre.lureau@redhat.com>" [full] # gpg: aka "Marc-André Lureau <marcandre.lureau@gmail.com>" [full] # Primary key fingerprint: 87A9 BD93 3F87 C606 D276 F62D DAE8 E109 7596 9CE5 * tag 'ui-pull-request' of https://gitlab.com/marcandre.lureau/qemu: tpm: "qemu -tpmdev help" should return success ui/gtk: Add scale option ui/gtk: Add keep-aspect-ratio option hw/display: Allow injection of virtio-gpu EDID name ui/spice: Blit the scanout texture if its memory layout is not linear ui/spice: Create a new texture with linear layout when gl=on is specified ui/console-gl: Add a helper to create a texture with linear memory layout ui/spice: Add an option to submit gl_draw requests at fixed rate ui/spice: Add an option for users to provide a preferred video codec ui/spice: Enable gl=on option for non-local or remote clients ui/egl-helpers: Error check the fds in egl_dmabuf_export_texture() ui/vnc: Introduce the VncWorker type ui/vnc: Do not copy z_stream Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
commit
1c37425423
@ -1299,3 +1299,47 @@ const PropertyInfo qdev_prop_vmapple_virtio_blk_variant = {
|
||||
.set = qdev_propinfo_set_enum,
|
||||
.set_default_value = qdev_propinfo_set_default_value_enum,
|
||||
};
|
||||
|
||||
/* --- VirtIOGPUOutputList --- */
|
||||
|
||||
static void get_virtio_gpu_output_list(Object *obj, Visitor *v,
|
||||
const char *name, void *opaque, Error **errp)
|
||||
{
|
||||
VirtIOGPUOutputList **prop_ptr =
|
||||
object_field_prop_ptr(obj, opaque);
|
||||
|
||||
visit_type_VirtIOGPUOutputList(v, name, prop_ptr, errp);
|
||||
}
|
||||
|
||||
static void set_virtio_gpu_output_list(Object *obj, Visitor *v,
|
||||
const char *name, void *opaque, Error **errp)
|
||||
{
|
||||
VirtIOGPUOutputList **prop_ptr =
|
||||
object_field_prop_ptr(obj, opaque);
|
||||
VirtIOGPUOutputList *list;
|
||||
|
||||
if (!visit_type_VirtIOGPUOutputList(v, name, &list, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
qapi_free_VirtIOGPUOutputList(*prop_ptr);
|
||||
*prop_ptr = list;
|
||||
}
|
||||
|
||||
static void release_virtio_gpu_output_list(Object *obj,
|
||||
const char *name, void *opaque)
|
||||
{
|
||||
VirtIOGPUOutputList **prop_ptr =
|
||||
object_field_prop_ptr(obj, opaque);
|
||||
|
||||
qapi_free_VirtIOGPUOutputList(*prop_ptr);
|
||||
*prop_ptr = NULL;
|
||||
}
|
||||
|
||||
const PropertyInfo qdev_prop_virtio_gpu_output_list = {
|
||||
.type = "VirtIOGPUOutputList",
|
||||
.description = "VirtIO GPU output list [{\"name\":\"<name>\"},...]",
|
||||
.get = get_virtio_gpu_output_list,
|
||||
.set = set_virtio_gpu_output_list,
|
||||
.release = release_virtio_gpu_output_list,
|
||||
};
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "qemu/error-report.h"
|
||||
#include "hw/display/edid.h"
|
||||
#include "trace.h"
|
||||
#include "qapi/qapi-types-virtio.h"
|
||||
|
||||
void
|
||||
virtio_gpu_base_reset(VirtIOGPUBase *g)
|
||||
@ -56,6 +57,8 @@ void
|
||||
virtio_gpu_base_generate_edid(VirtIOGPUBase *g, int scanout,
|
||||
struct virtio_gpu_resp_edid *edid)
|
||||
{
|
||||
size_t output_idx;
|
||||
VirtIOGPUOutputList *node;
|
||||
qemu_edid_info info = {
|
||||
.width_mm = g->req_state[scanout].width_mm,
|
||||
.height_mm = g->req_state[scanout].height_mm,
|
||||
@ -64,6 +67,14 @@ virtio_gpu_base_generate_edid(VirtIOGPUBase *g, int scanout,
|
||||
.refresh_rate = g->req_state[scanout].refresh_rate,
|
||||
};
|
||||
|
||||
for (output_idx = 0, node = g->conf.outputs;
|
||||
output_idx <= scanout && node; output_idx++, node = node->next) {
|
||||
if (output_idx == scanout && node->value && node->value->name) {
|
||||
info.name = node->value->name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
edid->size = cpu_to_le32(sizeof(edid->edid));
|
||||
qemu_edid_generate(edid->edid, sizeof(edid->edid), &info);
|
||||
}
|
||||
@ -172,6 +183,8 @@ virtio_gpu_base_device_realize(DeviceState *qdev,
|
||||
VirtIOHandleOutput cursor_cb,
|
||||
Error **errp)
|
||||
{
|
||||
size_t output_idx;
|
||||
VirtIOGPUOutputList *node;
|
||||
VirtIODevice *vdev = VIRTIO_DEVICE(qdev);
|
||||
VirtIOGPUBase *g = VIRTIO_GPU_BASE(qdev);
|
||||
int i;
|
||||
@ -181,6 +194,20 @@ virtio_gpu_base_device_realize(DeviceState *qdev,
|
||||
return false;
|
||||
}
|
||||
|
||||
for (output_idx = 0, node = g->conf.outputs;
|
||||
node; output_idx++, node = node->next) {
|
||||
if (output_idx == g->conf.max_outputs) {
|
||||
error_setg(errp, "invalid outputs > %d", g->conf.max_outputs);
|
||||
return false;
|
||||
}
|
||||
if (node->value && node->value->name &&
|
||||
strlen(node->value->name) > EDID_NAME_MAX_LENGTH) {
|
||||
error_setg(errp, "invalid output name '%s' > %d",
|
||||
node->value->name, EDID_NAME_MAX_LENGTH);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (virtio_gpu_virgl_enabled(g->conf)) {
|
||||
error_setg(&g->migration_blocker, "virgl is not yet migratable");
|
||||
if (migrate_add_blocker(&g->migration_blocker, errp) < 0) {
|
||||
|
@ -1,6 +1,8 @@
|
||||
#ifndef EDID_H
|
||||
#define EDID_H
|
||||
|
||||
#define EDID_NAME_MAX_LENGTH 12
|
||||
|
||||
typedef struct qemu_edid_info {
|
||||
const char *vendor; /* http://www.uefi.org/pnp_id_list */
|
||||
const char *name;
|
||||
|
@ -32,6 +32,7 @@ extern const PropertyInfo qdev_prop_cpus390entitlement;
|
||||
extern const PropertyInfo qdev_prop_iothread_vq_mapping_list;
|
||||
extern const PropertyInfo qdev_prop_endian_mode;
|
||||
extern const PropertyInfo qdev_prop_vmapple_virtio_blk_variant;
|
||||
extern const PropertyInfo qdev_prop_virtio_gpu_output_list;
|
||||
|
||||
#define DEFINE_PROP_PCI_DEVFN(_n, _s, _f, _d) \
|
||||
DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_pci_devfn, int32_t)
|
||||
@ -110,4 +111,8 @@ extern const PropertyInfo qdev_prop_vmapple_virtio_blk_variant;
|
||||
qdev_prop_vmapple_virtio_blk_variant, \
|
||||
VMAppleVirtioBlkVariant)
|
||||
|
||||
#define DEFINE_PROP_VIRTIO_GPU_OUTPUT_LIST(_name, _state, _field) \
|
||||
DEFINE_PROP(_name, _state, _field, qdev_prop_virtio_gpu_output_list, \
|
||||
VirtIOGPUOutputList *)
|
||||
|
||||
#endif
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "hw/virtio/virtio.h"
|
||||
#include "qemu/log.h"
|
||||
#include "system/vhost-user-backend.h"
|
||||
#include "qapi/qapi-types-virtio.h"
|
||||
|
||||
#include "standard-headers/linux/virtio_gpu.h"
|
||||
#include "standard-headers/linux/virtio_ids.h"
|
||||
@ -128,6 +129,7 @@ struct virtio_gpu_base_conf {
|
||||
uint32_t xres;
|
||||
uint32_t yres;
|
||||
uint64_t hostmem;
|
||||
VirtIOGPUOutputList *outputs;
|
||||
};
|
||||
|
||||
struct virtio_gpu_ctrl_command {
|
||||
@ -167,6 +169,7 @@ struct VirtIOGPUBaseClass {
|
||||
|
||||
#define VIRTIO_GPU_BASE_PROPERTIES(_state, _conf) \
|
||||
DEFINE_PROP_UINT32("max_outputs", _state, _conf.max_outputs, 1), \
|
||||
DEFINE_PROP_VIRTIO_GPU_OUTPUT_LIST("outputs", _state, _conf.outputs), \
|
||||
DEFINE_PROP_BIT("edid", _state, _conf.flags, \
|
||||
VIRTIO_GPU_FLAG_EDID_ENABLED, true), \
|
||||
DEFINE_PROP_UINT32("xres", _state, _conf.xres, 1280), \
|
||||
|
@ -422,6 +422,9 @@ bool console_gl_check_format(DisplayChangeListener *dcl,
|
||||
pixman_format_code_t format);
|
||||
void surface_gl_create_texture(QemuGLShader *gls,
|
||||
DisplaySurface *surface);
|
||||
bool surface_gl_create_texture_from_fd(DisplaySurface *surface,
|
||||
int fd, GLuint *texture,
|
||||
GLuint *mem_obj);
|
||||
void surface_gl_update_texture(QemuGLShader *gls,
|
||||
DisplaySurface *surface,
|
||||
int x, int y, int w, int h);
|
||||
|
@ -41,6 +41,7 @@ typedef struct VirtualGfxConsole {
|
||||
DisplaySurface *ds;
|
||||
pixman_image_t *convert;
|
||||
cairo_surface_t *surface;
|
||||
double preferred_scale;
|
||||
double scale_x;
|
||||
double scale_y;
|
||||
#if defined(CONFIG_OPENGL)
|
||||
@ -140,6 +141,7 @@ struct GtkDisplayState {
|
||||
GdkCursor *null_cursor;
|
||||
Notifier mouse_mode_notifier;
|
||||
gboolean free_scale;
|
||||
gboolean keep_aspect_ratio;
|
||||
|
||||
bool external_pause_update;
|
||||
|
||||
|
@ -132,6 +132,9 @@ struct SimpleSpiceDisplay {
|
||||
egl_fb guest_fb;
|
||||
egl_fb blit_fb;
|
||||
egl_fb cursor_fb;
|
||||
bool backing_y_0_top;
|
||||
bool blit_scanout_texture;
|
||||
bool new_scanout_texture;
|
||||
bool have_hot;
|
||||
#endif
|
||||
};
|
||||
@ -151,6 +154,8 @@ 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);
|
||||
|
@ -22,6 +22,7 @@ typedef struct DisplaySurface {
|
||||
GLenum glformat;
|
||||
GLenum gltype;
|
||||
GLuint texture;
|
||||
GLuint mem_obj;
|
||||
#endif
|
||||
qemu_pixman_shareable share_handle;
|
||||
uint32_t share_handle_offset;
|
||||
|
15
qapi/ui.json
15
qapi/ui.json
@ -1335,13 +1335,20 @@
|
||||
# @show-menubar: Display the main window menubar. Defaults to "on".
|
||||
# (Since 8.0)
|
||||
#
|
||||
# @keep-aspect-ratio: Keep width/height aspect ratio of guest content when
|
||||
# resizing host window. Defaults to "on". (Since 10.1)
|
||||
#
|
||||
# @scale: Set preferred scale of the display. Defaults to 1.0. (Since 10.1)
|
||||
#
|
||||
# Since: 2.12
|
||||
##
|
||||
{ 'struct' : 'DisplayGTK',
|
||||
'data' : { '*grab-on-hover' : 'bool',
|
||||
'*zoom-to-fit' : 'bool',
|
||||
'*show-tabs' : 'bool',
|
||||
'*show-menubar' : 'bool' } }
|
||||
'data' : { '*grab-on-hover' : 'bool',
|
||||
'*zoom-to-fit' : 'bool',
|
||||
'*show-tabs' : 'bool',
|
||||
'*show-menubar' : 'bool',
|
||||
'*keep-aspect-ratio' : 'bool',
|
||||
'*scale' : 'number' } }
|
||||
|
||||
##
|
||||
# @DisplayEGLHeadless:
|
||||
|
@ -963,17 +963,31 @@
|
||||
{ 'struct': 'IOThreadVirtQueueMapping',
|
||||
'data': { 'iothread': 'str', '*vqs': ['uint16'] } }
|
||||
|
||||
##
|
||||
# @VirtIOGPUOutput:
|
||||
#
|
||||
# Describes configuration of a VirtIO GPU output.
|
||||
#
|
||||
# @name: the name of the output
|
||||
#
|
||||
# Since: 10.1
|
||||
##
|
||||
|
||||
{ 'struct': 'VirtIOGPUOutput',
|
||||
'data': { 'name': 'str' } }
|
||||
|
||||
##
|
||||
# @DummyVirtioForceArrays:
|
||||
#
|
||||
# Not used by QMP; hack to let us use IOThreadVirtQueueMappingList
|
||||
# internally
|
||||
# and VirtIOGPUOutputList internally
|
||||
#
|
||||
# Since: 9.0
|
||||
##
|
||||
|
||||
{ 'struct': 'DummyVirtioForceArrays',
|
||||
'data': { 'unused-iothread-vq-mapping': ['IOThreadVirtQueueMapping'] } }
|
||||
'data': { 'unused-iothread-vq-mapping': ['IOThreadVirtQueueMapping'],
|
||||
'unused-virtio-gpu-output': ['VirtIOGPUOutput'] } }
|
||||
|
||||
##
|
||||
# @GranuleMode:
|
||||
|
@ -2286,6 +2286,8 @@ DEF("spice", HAS_ARG, QEMU_OPTION_spice,
|
||||
" [,streaming-video=[off|all|filter]][,disable-copy-paste=on|off]\n"
|
||||
" [,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",
|
||||
@ -2374,6 +2376,17 @@ SRST
|
||||
``seamless-migration=[on|off]``
|
||||
Enable/disable spice seamless migration. Default is off.
|
||||
|
||||
``video-codec=<codec>``
|
||||
Provide the preferred codec the Spice server should use with the
|
||||
Gstreamer encoder. This option is only relevant when gl=on is
|
||||
specified. If no codec is provided, then the codec gstreamer:h264
|
||||
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.
|
||||
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "system/tpm.h"
|
||||
#include "qemu/config-file.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/help_option.h"
|
||||
|
||||
static QLIST_HEAD(, TPMBackend) tpm_backends =
|
||||
QLIST_HEAD_INITIALIZER(tpm_backends);
|
||||
@ -179,9 +180,9 @@ int tpm_config_parse(QemuOptsList *opts_list, const char *optstr)
|
||||
{
|
||||
QemuOpts *opts;
|
||||
|
||||
if (!strcmp(optstr, "help")) {
|
||||
if (is_help_option(optstr)) {
|
||||
tpm_display_backend_drivers();
|
||||
return -1;
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
opts = qemu_opts_parse_noisily(opts_list, optstr, true);
|
||||
if (!opts) {
|
||||
|
@ -25,6 +25,7 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "ui/console.h"
|
||||
#include "ui/shader.h"
|
||||
|
||||
@ -96,6 +97,53 @@ void surface_gl_create_texture(QemuGLShader *gls,
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
}
|
||||
|
||||
bool surface_gl_create_texture_from_fd(DisplaySurface *surface,
|
||||
int fd, GLuint *texture,
|
||||
GLuint *mem_obj)
|
||||
{
|
||||
unsigned long size = surface_stride(surface) * surface_height(surface);
|
||||
GLenum err = glGetError();
|
||||
*texture = 0;
|
||||
*mem_obj = 0;
|
||||
|
||||
if (!epoxy_has_gl_extension("GL_EXT_memory_object") ||
|
||||
!epoxy_has_gl_extension("GL_EXT_memory_object_fd")) {
|
||||
error_report("spice: required OpenGL extensions not supported: "
|
||||
"GL_EXT_memory_object and GL_EXT_memory_object_fd");
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef GL_EXT_memory_object_fd
|
||||
glCreateMemoryObjectsEXT(1, mem_obj);
|
||||
glImportMemoryFdEXT(*mem_obj, size, GL_HANDLE_TYPE_OPAQUE_FD_EXT, fd);
|
||||
|
||||
err = glGetError();
|
||||
if (err != GL_NO_ERROR) {
|
||||
error_report("spice: cannot import memory object from fd");
|
||||
goto cleanup_mem;
|
||||
}
|
||||
|
||||
glGenTextures(1, texture);
|
||||
glBindTexture(GL_TEXTURE_2D, *texture);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_TILING_EXT, GL_LINEAR_TILING_EXT);
|
||||
glTexStorageMem2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8, surface_width(surface),
|
||||
surface_height(surface), *mem_obj, 0);
|
||||
err = glGetError();
|
||||
if (err != GL_NO_ERROR) {
|
||||
error_report("spice: cannot create texture from memory object");
|
||||
goto cleanup_tex_and_mem;
|
||||
}
|
||||
return true;
|
||||
|
||||
cleanup_tex_and_mem:
|
||||
glDeleteTextures(1, texture);
|
||||
cleanup_mem:
|
||||
glDeleteMemoryObjectsEXT(1, mem_obj);
|
||||
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
void surface_gl_update_texture(QemuGLShader *gls,
|
||||
DisplaySurface *surface,
|
||||
int x, int y, int w, int h)
|
||||
@ -136,6 +184,12 @@ void surface_gl_destroy_texture(QemuGLShader *gls,
|
||||
}
|
||||
glDeleteTextures(1, &surface->texture);
|
||||
surface->texture = 0;
|
||||
#ifdef GL_EXT_memory_object_fd
|
||||
if (surface->mem_obj) {
|
||||
glDeleteMemoryObjectsEXT(1, &surface->mem_obj);
|
||||
surface->mem_obj = 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void surface_gl_setup_viewport(QemuGLShader *gls,
|
||||
|
@ -295,6 +295,7 @@ bool egl_dmabuf_export_texture(uint32_t tex_id, int *fd, EGLint *offset,
|
||||
{
|
||||
EGLImageKHR image;
|
||||
EGLuint64KHR modifiers[DMABUF_MAX_PLANES];
|
||||
int i;
|
||||
|
||||
image = eglCreateImageKHR(qemu_egl_display, eglGetCurrentContext(),
|
||||
EGL_GL_TEXTURE_2D_KHR,
|
||||
@ -314,6 +315,11 @@ bool egl_dmabuf_export_texture(uint32_t tex_id, int *fd, EGLint *offset,
|
||||
*modifier = modifiers[0];
|
||||
}
|
||||
|
||||
for (i = 0; i < *num_planes; i++) {
|
||||
if (fd[i] < 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
58
ui/gtk.c
58
ui/gtk.c
@ -67,6 +67,7 @@
|
||||
#define VC_TERM_X_MIN 80
|
||||
#define VC_TERM_Y_MIN 25
|
||||
#define VC_SCALE_MIN 0.25
|
||||
#define VC_SCALE_MAX 4
|
||||
#define VC_SCALE_STEP 0.25
|
||||
|
||||
#ifdef GDK_WINDOWING_X11
|
||||
@ -272,15 +273,11 @@ static void gd_update_geometry_hints(VirtualConsole *vc)
|
||||
if (!vc->gfx.ds) {
|
||||
return;
|
||||
}
|
||||
if (s->free_scale) {
|
||||
geo.min_width = surface_width(vc->gfx.ds) * VC_SCALE_MIN;
|
||||
geo.min_height = surface_height(vc->gfx.ds) * VC_SCALE_MIN;
|
||||
mask |= GDK_HINT_MIN_SIZE;
|
||||
} else {
|
||||
geo.min_width = surface_width(vc->gfx.ds) * vc->gfx.scale_x;
|
||||
geo.min_height = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
|
||||
mask |= GDK_HINT_MIN_SIZE;
|
||||
}
|
||||
double scale_x = s->free_scale ? VC_SCALE_MIN : vc->gfx.scale_x;
|
||||
double scale_y = s->free_scale ? VC_SCALE_MIN : vc->gfx.scale_y;
|
||||
geo.min_width = surface_width(vc->gfx.ds) * scale_x;
|
||||
geo.min_height = surface_height(vc->gfx.ds) * scale_y;
|
||||
mask |= GDK_HINT_MIN_SIZE;
|
||||
geo_widget = vc->gfx.drawing_area;
|
||||
gtk_widget_set_size_request(geo_widget, geo.min_width, geo.min_height);
|
||||
|
||||
@ -828,8 +825,12 @@ void gd_update_scale(VirtualConsole *vc, int ww, int wh, int fbw, int fbh)
|
||||
|
||||
sx = (double)ww / fbw;
|
||||
sy = (double)wh / fbh;
|
||||
|
||||
vc->gfx.scale_x = vc->gfx.scale_y = MIN(sx, sy);
|
||||
if (vc->s->keep_aspect_ratio) {
|
||||
vc->gfx.scale_x = vc->gfx.scale_y = MIN(sx, sy);
|
||||
} else {
|
||||
vc->gfx.scale_x = sx;
|
||||
vc->gfx.scale_y = sy;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
@ -1575,8 +1576,8 @@ static void gd_menu_full_screen(GtkMenuItem *item, void *opaque)
|
||||
}
|
||||
s->full_screen = FALSE;
|
||||
if (vc->type == GD_VC_GFX) {
|
||||
vc->gfx.scale_x = 1.0;
|
||||
vc->gfx.scale_y = 1.0;
|
||||
vc->gfx.scale_x = vc->gfx.preferred_scale;
|
||||
vc->gfx.scale_y = vc->gfx.preferred_scale;
|
||||
gd_update_windowsize(vc);
|
||||
}
|
||||
}
|
||||
@ -1632,8 +1633,8 @@ static void gd_menu_zoom_fixed(GtkMenuItem *item, void *opaque)
|
||||
GtkDisplayState *s = opaque;
|
||||
VirtualConsole *vc = gd_vc_find_current(s);
|
||||
|
||||
vc->gfx.scale_x = 1.0;
|
||||
vc->gfx.scale_y = 1.0;
|
||||
vc->gfx.scale_x = vc->gfx.preferred_scale;
|
||||
vc->gfx.scale_y = vc->gfx.preferred_scale;
|
||||
|
||||
gd_update_windowsize(vc);
|
||||
}
|
||||
@ -1647,8 +1648,8 @@ static void gd_menu_zoom_fit(GtkMenuItem *item, void *opaque)
|
||||
s->free_scale = TRUE;
|
||||
} else {
|
||||
s->free_scale = FALSE;
|
||||
vc->gfx.scale_x = 1.0;
|
||||
vc->gfx.scale_y = 1.0;
|
||||
vc->gfx.scale_x = vc->gfx.preferred_scale;
|
||||
vc->gfx.scale_y = vc->gfx.preferred_scale;
|
||||
}
|
||||
|
||||
gd_update_windowsize(vc);
|
||||
@ -2239,6 +2240,11 @@ static void gl_area_realize(GtkGLArea *area, VirtualConsole *vc)
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool gd_scale_valid(double scale)
|
||||
{
|
||||
return scale >= VC_SCALE_MIN && scale <= VC_SCALE_MAX;
|
||||
}
|
||||
|
||||
static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
|
||||
QemuConsole *con, int idx,
|
||||
GSList *group, GtkWidget *view_menu)
|
||||
@ -2248,8 +2254,18 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
|
||||
|
||||
vc->label = qemu_console_get_label(con);
|
||||
vc->s = s;
|
||||
vc->gfx.scale_x = 1.0;
|
||||
vc->gfx.scale_y = 1.0;
|
||||
vc->gfx.preferred_scale = 1.0;
|
||||
if (s->opts->u.gtk.has_scale) {
|
||||
if (gd_scale_valid(s->opts->u.gtk.scale)) {
|
||||
vc->gfx.preferred_scale = s->opts->u.gtk.scale;
|
||||
} else {
|
||||
error_report("Invalid scale value %lf given, being ignored",
|
||||
s->opts->u.gtk.scale);
|
||||
s->opts->u.gtk.has_scale = false;
|
||||
}
|
||||
}
|
||||
vc->gfx.scale_x = vc->gfx.preferred_scale;
|
||||
vc->gfx.scale_y = vc->gfx.preferred_scale;
|
||||
|
||||
#if defined(CONFIG_OPENGL)
|
||||
if (display_opengl) {
|
||||
@ -2328,6 +2344,10 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
|
||||
s->free_scale = true;
|
||||
}
|
||||
|
||||
s->keep_aspect_ratio = true;
|
||||
if (s->opts->u.gtk.has_keep_aspect_ratio)
|
||||
s->keep_aspect_ratio = s->opts->u.gtk.keep_aspect_ratio;
|
||||
|
||||
for (i = 0; i < INPUT_EVENT_SLOTS_MAX; i++) {
|
||||
struct touch_slot *slot = &touch_slots[i];
|
||||
slot->tracking_id = -1;
|
||||
|
@ -56,6 +56,8 @@ struct SpiceTimer {
|
||||
QEMUTimer *timer;
|
||||
};
|
||||
|
||||
#define DEFAULT_MAX_REFRESH_RATE 30
|
||||
|
||||
static SpiceTimer *timer_add(SpiceTimerFunc func, void *opaque)
|
||||
{
|
||||
SpiceTimer *timer;
|
||||
@ -488,6 +490,12 @@ static QemuOptsList qemu_spice_opts = {
|
||||
},{
|
||||
.name = "streaming-video",
|
||||
.type = QEMU_OPT_STRING,
|
||||
},{
|
||||
.name = "video-codec",
|
||||
.type = QEMU_OPT_STRING,
|
||||
},{
|
||||
.name = "max-refresh-rate",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
},{
|
||||
.name = "agent-mouse",
|
||||
.type = QEMU_OPT_BOOL,
|
||||
@ -801,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
|
||||
@ -836,9 +851,26 @@ static void qemu_spice_init(void)
|
||||
#ifdef HAVE_SPICE_GL
|
||||
if (qemu_opt_get_bool(opts, "gl", 0)) {
|
||||
if ((port != 0) || (tls_port != 0)) {
|
||||
#if SPICE_SERVER_VERSION >= 0x000f03 /* release 0.15.3 */
|
||||
const char *video_codec = NULL;
|
||||
g_autofree char *enc_codec = NULL;
|
||||
|
||||
spice_remote_client = 1;
|
||||
|
||||
video_codec = qemu_opt_get(opts, "video-codec");
|
||||
if (video_codec) {
|
||||
enc_codec = g_strconcat("gstreamer:", video_codec, NULL);
|
||||
}
|
||||
if (spice_server_set_video_codecs(spice_server,
|
||||
enc_codec ?: "gstreamer:h264")) {
|
||||
error_report("invalid video codec");
|
||||
exit(1);
|
||||
}
|
||||
#else
|
||||
error_report("SPICE GL support is local-only for now and "
|
||||
"incompatible with -spice port/tls-port");
|
||||
exit(1);
|
||||
#endif
|
||||
}
|
||||
egl_init(qemu_opt_get(opts, "rendernode"), DISPLAY_GL_MODE_ON, &error_fatal);
|
||||
spice_opengl = 1;
|
||||
|
@ -31,6 +31,8 @@
|
||||
#include "standard-headers/drm/drm_fourcc.h"
|
||||
|
||||
bool spice_opengl;
|
||||
bool spice_remote_client;
|
||||
int spice_max_refresh_rate;
|
||||
|
||||
int qemu_spice_rect_is_empty(const QXLRect* r)
|
||||
{
|
||||
@ -843,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;
|
||||
}
|
||||
|
||||
@ -856,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;
|
||||
}
|
||||
}
|
||||
@ -874,6 +893,81 @@ static void spice_gl_update(DisplayChangeListener *dcl,
|
||||
ssd->gl_updates++;
|
||||
}
|
||||
|
||||
static bool spice_gl_replace_fd_texture(SimpleSpiceDisplay *ssd,
|
||||
int *fds, uint64_t *modifier,
|
||||
int *num_planes)
|
||||
{
|
||||
uint32_t offsets[DMABUF_MAX_PLANES], strides[DMABUF_MAX_PLANES];
|
||||
GLuint texture;
|
||||
GLuint mem_obj;
|
||||
int fourcc;
|
||||
bool ret;
|
||||
|
||||
if (!spice_remote_client) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (*modifier == DRM_FORMAT_MOD_LINEAR) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (*num_planes > 1) {
|
||||
error_report("spice: cannot replace texture with multiple planes");
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* We really want to ensure that the memory layout of the texture
|
||||
* is linear; otherwise, the encoder's output may show corruption.
|
||||
*/
|
||||
if (!surface_gl_create_texture_from_fd(ssd->ds, fds[0], &texture,
|
||||
&mem_obj)) {
|
||||
error_report("spice: cannot create new texture from fd");
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* A successful return after glImportMemoryFdEXT() means that
|
||||
* the ownership of fd has been passed to GL. In other words,
|
||||
* the fd we got above should not be used anymore.
|
||||
*/
|
||||
ret = egl_dmabuf_export_texture(texture,
|
||||
fds,
|
||||
(EGLint *)offsets,
|
||||
(EGLint *)strides,
|
||||
&fourcc,
|
||||
num_planes,
|
||||
modifier);
|
||||
if (!ret) {
|
||||
glDeleteTextures(1, &texture);
|
||||
#ifdef GL_EXT_memory_object_fd
|
||||
glDeleteMemoryObjectsEXT(1, &mem_obj);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Since we couldn't export our newly create texture (or create,
|
||||
* an fd associated with it) we need to backtrack and try to
|
||||
* recreate the fd associated with the original texture.
|
||||
*/
|
||||
ret = egl_dmabuf_export_texture(ssd->ds->texture,
|
||||
fds,
|
||||
(EGLint *)offsets,
|
||||
(EGLint *)strides,
|
||||
&fourcc,
|
||||
num_planes,
|
||||
modifier);
|
||||
if (!ret) {
|
||||
surface_gl_destroy_texture(ssd->gls, ssd->ds);
|
||||
warn_report("spice: no texture available to display");
|
||||
}
|
||||
} else {
|
||||
surface_gl_destroy_texture(ssd->gls, ssd->ds);
|
||||
ssd->ds->texture = texture;
|
||||
ssd->ds->mem_obj = mem_obj;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void spice_server_gl_scanout(QXLInstance *qxl,
|
||||
const int *fd,
|
||||
uint32_t width, uint32_t height,
|
||||
@ -898,6 +992,7 @@ static void spice_gl_switch(DisplayChangeListener *dcl,
|
||||
struct DisplaySurface *new_surface)
|
||||
{
|
||||
SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
|
||||
bool ret;
|
||||
|
||||
if (ssd->ds) {
|
||||
surface_gl_destroy_texture(ssd->gls, ssd->ds);
|
||||
@ -920,6 +1015,12 @@ static void spice_gl_switch(DisplayChangeListener *dcl,
|
||||
return;
|
||||
}
|
||||
|
||||
ret = spice_gl_replace_fd_texture(ssd, fd, &modifier, &num_planes);
|
||||
if (!ret) {
|
||||
surface_gl_destroy_texture(ssd->gls, ssd->ds);
|
||||
return;
|
||||
}
|
||||
|
||||
trace_qemu_spice_gl_surface(ssd->qxl.id,
|
||||
surface_width(ssd->ds),
|
||||
surface_height(ssd->ds),
|
||||
@ -953,6 +1054,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);
|
||||
@ -971,7 +1086,7 @@ static void qemu_spice_gl_scanout_texture(DisplayChangeListener *dcl,
|
||||
{
|
||||
SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
|
||||
EGLint offset[DMABUF_MAX_PLANES], stride[DMABUF_MAX_PLANES], fourcc = 0;
|
||||
int fd[DMABUF_MAX_PLANES], num_planes;
|
||||
int fd[DMABUF_MAX_PLANES], num_planes, i;
|
||||
uint64_t modifier;
|
||||
|
||||
assert(tex_id);
|
||||
@ -983,11 +1098,26 @@ static void qemu_spice_gl_scanout_texture(DisplayChangeListener *dcl,
|
||||
|
||||
trace_qemu_spice_gl_scanout_texture(ssd->qxl.id, w, h, fourcc);
|
||||
|
||||
/* note: spice server will close the fd */
|
||||
spice_server_gl_scanout(&ssd->qxl, fd, backing_width, backing_height,
|
||||
(uint32_t *)offset, (uint32_t *)stride, num_planes,
|
||||
fourcc, modifier, y_0_top);
|
||||
qemu_spice_gl_monitor_config(ssd, x, y, w, h);
|
||||
if (spice_remote_client && modifier != DRM_FORMAT_MOD_LINEAR) {
|
||||
egl_fb_destroy(&ssd->guest_fb);
|
||||
egl_fb_setup_for_tex(&ssd->guest_fb,
|
||||
backing_width, backing_height,
|
||||
tex_id, false);
|
||||
ssd->backing_y_0_top = y_0_top;
|
||||
ssd->blit_scanout_texture = true;
|
||||
ssd->new_scanout_texture = true;
|
||||
|
||||
for (i = 0; i < num_planes; i++) {
|
||||
close(fd[i]);
|
||||
}
|
||||
} else {
|
||||
/* note: spice server will close the fd */
|
||||
spice_server_gl_scanout(&ssd->qxl, fd, backing_width, backing_height,
|
||||
(uint32_t *)offset, (uint32_t *)stride,
|
||||
num_planes, fourcc, modifier, y_0_top);
|
||||
qemu_spice_gl_monitor_config(ssd, x, y, w, h);
|
||||
}
|
||||
|
||||
ssd->have_surface = false;
|
||||
ssd->have_scanout = true;
|
||||
}
|
||||
@ -1053,6 +1183,50 @@ static void qemu_spice_gl_release_dmabuf(DisplayChangeListener *dcl,
|
||||
egl_dmabuf_release_texture(dmabuf);
|
||||
}
|
||||
|
||||
static bool spice_gl_blit_scanout_texture(SimpleSpiceDisplay *ssd,
|
||||
egl_fb *scanout_tex_fb)
|
||||
{
|
||||
uint32_t offsets[DMABUF_MAX_PLANES], strides[DMABUF_MAX_PLANES];
|
||||
int fds[DMABUF_MAX_PLANES], num_planes, fourcc;
|
||||
uint64_t modifier;
|
||||
bool ret;
|
||||
|
||||
egl_fb_destroy(scanout_tex_fb);
|
||||
egl_fb_setup_for_tex(scanout_tex_fb,
|
||||
surface_width(ssd->ds), surface_height(ssd->ds),
|
||||
ssd->ds->texture, false);
|
||||
egl_fb_blit(scanout_tex_fb, &ssd->guest_fb, false);
|
||||
glFlush();
|
||||
|
||||
if (!ssd->new_scanout_texture) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ret = egl_dmabuf_export_texture(ssd->ds->texture,
|
||||
fds,
|
||||
(EGLint *)offsets,
|
||||
(EGLint *)strides,
|
||||
&fourcc,
|
||||
&num_planes,
|
||||
&modifier);
|
||||
if (!ret) {
|
||||
error_report("spice: failed to get fd for texture");
|
||||
return false;
|
||||
}
|
||||
|
||||
spice_server_gl_scanout(&ssd->qxl, fds,
|
||||
surface_width(ssd->ds),
|
||||
surface_height(ssd->ds),
|
||||
(uint32_t *)offsets, (uint32_t *)strides,
|
||||
num_planes, fourcc, modifier,
|
||||
ssd->backing_y_0_top);
|
||||
qemu_spice_gl_monitor_config(ssd, 0, 0,
|
||||
surface_width(ssd->ds),
|
||||
surface_height(ssd->ds));
|
||||
ssd->new_scanout_texture = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void qemu_spice_gl_update(DisplayChangeListener *dcl,
|
||||
uint32_t x, uint32_t y, uint32_t w, uint32_t h)
|
||||
{
|
||||
@ -1060,7 +1234,7 @@ static void qemu_spice_gl_update(DisplayChangeListener *dcl,
|
||||
EGLint fourcc = 0;
|
||||
bool render_cursor = false;
|
||||
bool y_0_top = false; /* FIXME */
|
||||
uint64_t cookie;
|
||||
bool ret;
|
||||
uint32_t width, height, texture;
|
||||
|
||||
if (!ssd->have_scanout) {
|
||||
@ -1155,11 +1329,31 @@ static void qemu_spice_gl_update(DisplayChangeListener *dcl,
|
||||
glFlush();
|
||||
}
|
||||
|
||||
if (spice_remote_client && ssd->blit_scanout_texture) {
|
||||
egl_fb scanout_tex_fb;
|
||||
|
||||
ret = spice_gl_blit_scanout_texture(ssd, &scanout_tex_fb);
|
||||
if (!ret) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
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 = {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -46,23 +46,23 @@ void vnc_zlib_zfree(void *x, void *addr)
|
||||
g_free(addr);
|
||||
}
|
||||
|
||||
static void vnc_zlib_start(VncState *vs)
|
||||
static void vnc_zlib_start(VncState *vs, VncWorker *worker)
|
||||
{
|
||||
buffer_reset(&vs->zlib.zlib);
|
||||
buffer_reset(&worker->zlib.zlib);
|
||||
|
||||
// make the output buffer be the zlib buffer, so we can compress it later
|
||||
vs->zlib.tmp = vs->output;
|
||||
vs->output = vs->zlib.zlib;
|
||||
worker->zlib.tmp = vs->output;
|
||||
vs->output = worker->zlib.zlib;
|
||||
}
|
||||
|
||||
static int vnc_zlib_stop(VncState *vs)
|
||||
static int vnc_zlib_stop(VncState *vs, VncWorker *worker)
|
||||
{
|
||||
z_streamp zstream = &vs->zlib.stream;
|
||||
z_streamp zstream = &worker->zlib.stream;
|
||||
int previous_out;
|
||||
|
||||
// switch back to normal output/zlib buffers
|
||||
vs->zlib.zlib = vs->output;
|
||||
vs->output = vs->zlib.tmp;
|
||||
worker->zlib.zlib = vs->output;
|
||||
vs->output = worker->zlib.tmp;
|
||||
|
||||
// compress the zlib buffer
|
||||
|
||||
@ -76,7 +76,7 @@ static int vnc_zlib_stop(VncState *vs)
|
||||
zstream->zalloc = vnc_zlib_zalloc;
|
||||
zstream->zfree = vnc_zlib_zfree;
|
||||
|
||||
err = deflateInit2(zstream, vs->tight->compression, Z_DEFLATED,
|
||||
err = deflateInit2(zstream, worker->tight.compression, Z_DEFLATED,
|
||||
MAX_WBITS,
|
||||
MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY);
|
||||
|
||||
@ -85,24 +85,24 @@ static int vnc_zlib_stop(VncState *vs)
|
||||
return -1;
|
||||
}
|
||||
|
||||
vs->zlib.level = vs->tight->compression;
|
||||
worker->zlib.level = worker->tight.compression;
|
||||
zstream->opaque = vs;
|
||||
}
|
||||
|
||||
if (vs->tight->compression != vs->zlib.level) {
|
||||
if (deflateParams(zstream, vs->tight->compression,
|
||||
if (worker->tight.compression != worker->zlib.level) {
|
||||
if (deflateParams(zstream, worker->tight.compression,
|
||||
Z_DEFAULT_STRATEGY) != Z_OK) {
|
||||
return -1;
|
||||
}
|
||||
vs->zlib.level = vs->tight->compression;
|
||||
worker->zlib.level = worker->tight.compression;
|
||||
}
|
||||
|
||||
// reserve memory in output buffer
|
||||
buffer_reserve(&vs->output, vs->zlib.zlib.offset + 64);
|
||||
buffer_reserve(&vs->output, worker->zlib.zlib.offset + 64);
|
||||
|
||||
// set pointers
|
||||
zstream->next_in = vs->zlib.zlib.buffer;
|
||||
zstream->avail_in = vs->zlib.zlib.offset;
|
||||
zstream->next_in = worker->zlib.zlib.buffer;
|
||||
zstream->avail_in = worker->zlib.zlib.offset;
|
||||
zstream->next_out = vs->output.buffer + vs->output.offset;
|
||||
zstream->avail_out = vs->output.capacity - vs->output.offset;
|
||||
previous_out = zstream->avail_out;
|
||||
@ -118,7 +118,8 @@ static int vnc_zlib_stop(VncState *vs)
|
||||
return previous_out - zstream->avail_out;
|
||||
}
|
||||
|
||||
int vnc_zlib_send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
|
||||
int vnc_zlib_send_framebuffer_update(VncState *vs, VncWorker *worker,
|
||||
int x, int y, int w, int h)
|
||||
{
|
||||
int old_offset, new_offset, bytes_written;
|
||||
|
||||
@ -129,9 +130,9 @@ int vnc_zlib_send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
|
||||
vnc_write_s32(vs, 0);
|
||||
|
||||
// compress the stream
|
||||
vnc_zlib_start(vs);
|
||||
vnc_zlib_start(vs, worker);
|
||||
vnc_raw_send_framebuffer_update(vs, x, y, w, h);
|
||||
bytes_written = vnc_zlib_stop(vs);
|
||||
bytes_written = vnc_zlib_stop(vs, worker);
|
||||
|
||||
if (bytes_written == -1)
|
||||
return 0;
|
||||
@ -145,10 +146,10 @@ int vnc_zlib_send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
|
||||
return 1;
|
||||
}
|
||||
|
||||
void vnc_zlib_clear(VncState *vs)
|
||||
void vnc_zlib_clear(VncWorker *worker)
|
||||
{
|
||||
if (vs->zlib.stream.opaque) {
|
||||
deflateEnd(&vs->zlib.stream);
|
||||
if (worker->zlib.stream.opaque) {
|
||||
deflateEnd(&worker->zlib.stream);
|
||||
}
|
||||
buffer_free(&vs->zlib.zlib);
|
||||
buffer_free(&worker->zlib.zlib);
|
||||
}
|
||||
|
@ -35,45 +35,45 @@ static const int bits_per_packed_pixel[] = {
|
||||
};
|
||||
|
||||
|
||||
static void vnc_zrle_start(VncState *vs)
|
||||
static void vnc_zrle_start(VncState *vs, VncZrle *zrle)
|
||||
{
|
||||
buffer_reset(&vs->zrle->zrle);
|
||||
buffer_reset(&zrle->zrle);
|
||||
|
||||
/* make the output buffer be the zlib buffer, so we can compress it later */
|
||||
vs->zrle->tmp = vs->output;
|
||||
vs->output = vs->zrle->zrle;
|
||||
zrle->tmp = vs->output;
|
||||
vs->output = zrle->zrle;
|
||||
}
|
||||
|
||||
static void vnc_zrle_stop(VncState *vs)
|
||||
static void vnc_zrle_stop(VncState *vs, VncZrle *zrle)
|
||||
{
|
||||
/* switch back to normal output/zlib buffers */
|
||||
vs->zrle->zrle = vs->output;
|
||||
vs->output = vs->zrle->tmp;
|
||||
zrle->zrle = vs->output;
|
||||
vs->output = zrle->tmp;
|
||||
}
|
||||
|
||||
static void *zrle_convert_fb(VncState *vs, int x, int y, int w, int h,
|
||||
int bpp)
|
||||
static void *zrle_convert_fb(VncState *vs, VncZrle *zrle,
|
||||
int x, int y, int w, int h, int bpp)
|
||||
{
|
||||
Buffer tmp;
|
||||
|
||||
buffer_reset(&vs->zrle->fb);
|
||||
buffer_reserve(&vs->zrle->fb, w * h * bpp + bpp);
|
||||
buffer_reset(&zrle->fb);
|
||||
buffer_reserve(&zrle->fb, w * h * bpp + bpp);
|
||||
|
||||
tmp = vs->output;
|
||||
vs->output = vs->zrle->fb;
|
||||
vs->output = zrle->fb;
|
||||
|
||||
vnc_raw_send_framebuffer_update(vs, x, y, w, h);
|
||||
|
||||
vs->zrle->fb = vs->output;
|
||||
zrle->fb = vs->output;
|
||||
vs->output = tmp;
|
||||
return vs->zrle->fb.buffer;
|
||||
return zrle->fb.buffer;
|
||||
}
|
||||
|
||||
static int zrle_compress_data(VncState *vs, int level)
|
||||
static int zrle_compress_data(VncState *vs, VncZrle *zrle, int level)
|
||||
{
|
||||
z_streamp zstream = &vs->zrle->stream;
|
||||
z_streamp zstream = &zrle->stream;
|
||||
|
||||
buffer_reset(&vs->zrle->zlib);
|
||||
buffer_reset(&zrle->zlib);
|
||||
|
||||
if (zstream->opaque != vs) {
|
||||
int err;
|
||||
@ -93,13 +93,13 @@ static int zrle_compress_data(VncState *vs, int level)
|
||||
}
|
||||
|
||||
/* reserve memory in output buffer */
|
||||
buffer_reserve(&vs->zrle->zlib, vs->zrle->zrle.offset + 64);
|
||||
buffer_reserve(&zrle->zlib, zrle->zrle.offset + 64);
|
||||
|
||||
/* set pointers */
|
||||
zstream->next_in = vs->zrle->zrle.buffer;
|
||||
zstream->avail_in = vs->zrle->zrle.offset;
|
||||
zstream->next_out = vs->zrle->zlib.buffer;
|
||||
zstream->avail_out = vs->zrle->zlib.capacity;
|
||||
zstream->next_in = zrle->zrle.buffer;
|
||||
zstream->avail_in = zrle->zrle.offset;
|
||||
zstream->next_out = zrle->zlib.buffer;
|
||||
zstream->avail_out = zrle->zlib.capacity;
|
||||
zstream->data_type = Z_BINARY;
|
||||
|
||||
/* start encoding */
|
||||
@ -108,8 +108,8 @@ static int zrle_compress_data(VncState *vs, int level)
|
||||
return -1;
|
||||
}
|
||||
|
||||
vs->zrle->zlib.offset = vs->zrle->zlib.capacity - zstream->avail_out;
|
||||
return vs->zrle->zlib.offset;
|
||||
zrle->zlib.offset = zrle->zlib.capacity - zstream->avail_out;
|
||||
return zrle->zlib.offset;
|
||||
}
|
||||
|
||||
/* Try to work out whether to use RLE and/or a palette. We do this by
|
||||
@ -252,21 +252,21 @@ static void zrle_write_u8(VncState *vs, uint8_t value)
|
||||
#undef ZRLE_COMPACT_PIXEL
|
||||
#undef ZRLE_BPP
|
||||
|
||||
static int zrle_send_framebuffer_update(VncState *vs, int x, int y,
|
||||
int w, int h)
|
||||
static int zrle_send_framebuffer_update(VncState *vs, VncWorker *worker,
|
||||
int x, int y, int w, int h)
|
||||
{
|
||||
bool be = vs->client_endian == G_BIG_ENDIAN;
|
||||
size_t bytes;
|
||||
int zywrle_level;
|
||||
|
||||
if (vs->zrle->type == VNC_ENCODING_ZYWRLE) {
|
||||
if (!vs->vd->lossy || vs->tight->quality == (uint8_t)-1
|
||||
|| vs->tight->quality == 9) {
|
||||
if (worker->zrle.type == VNC_ENCODING_ZYWRLE) {
|
||||
if (!vs->vd->lossy || worker->tight.quality == (uint8_t)-1
|
||||
|| worker->tight.quality == 9) {
|
||||
zywrle_level = 0;
|
||||
vs->zrle->type = VNC_ENCODING_ZRLE;
|
||||
} else if (vs->tight->quality < 3) {
|
||||
worker->zrle.type = VNC_ENCODING_ZRLE;
|
||||
} else if (worker->tight.quality < 3) {
|
||||
zywrle_level = 3;
|
||||
} else if (vs->tight->quality < 6) {
|
||||
} else if (worker->tight.quality < 6) {
|
||||
zywrle_level = 2;
|
||||
} else {
|
||||
zywrle_level = 1;
|
||||
@ -275,25 +275,25 @@ static int zrle_send_framebuffer_update(VncState *vs, int x, int y,
|
||||
zywrle_level = 0;
|
||||
}
|
||||
|
||||
vnc_zrle_start(vs);
|
||||
vnc_zrle_start(vs, &worker->zrle);
|
||||
|
||||
switch (vs->client_pf.bytes_per_pixel) {
|
||||
case 1:
|
||||
zrle_encode_8ne(vs, x, y, w, h, zywrle_level);
|
||||
zrle_encode_8ne(vs, &worker->zrle, x, y, w, h, zywrle_level);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
if (vs->client_pf.gmax > 0x1F) {
|
||||
if (be) {
|
||||
zrle_encode_16be(vs, x, y, w, h, zywrle_level);
|
||||
zrle_encode_16be(vs, &worker->zrle, x, y, w, h, zywrle_level);
|
||||
} else {
|
||||
zrle_encode_16le(vs, x, y, w, h, zywrle_level);
|
||||
zrle_encode_16le(vs, &worker->zrle, x, y, w, h, zywrle_level);
|
||||
}
|
||||
} else {
|
||||
if (be) {
|
||||
zrle_encode_15be(vs, x, y, w, h, zywrle_level);
|
||||
zrle_encode_15be(vs, &worker->zrle, x, y, w, h, zywrle_level);
|
||||
} else {
|
||||
zrle_encode_15le(vs, x, y, w, h, zywrle_level);
|
||||
zrle_encode_15le(vs, &worker->zrle, x, y, w, h, zywrle_level);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -314,53 +314,55 @@ static int zrle_send_framebuffer_update(VncState *vs, int x, int y,
|
||||
|
||||
if ((fits_in_ls3bytes && !be) || (fits_in_ms3bytes && be)) {
|
||||
if (be) {
|
||||
zrle_encode_24abe(vs, x, y, w, h, zywrle_level);
|
||||
zrle_encode_24abe(vs, &worker->zrle, x, y, w, h, zywrle_level);
|
||||
} else {
|
||||
zrle_encode_24ale(vs, x, y, w, h, zywrle_level);
|
||||
zrle_encode_24ale(vs, &worker->zrle, x, y, w, h, zywrle_level);
|
||||
}
|
||||
} else if ((fits_in_ls3bytes && be) || (fits_in_ms3bytes && !be)) {
|
||||
if (be) {
|
||||
zrle_encode_24bbe(vs, x, y, w, h, zywrle_level);
|
||||
zrle_encode_24bbe(vs, &worker->zrle, x, y, w, h, zywrle_level);
|
||||
} else {
|
||||
zrle_encode_24ble(vs, x, y, w, h, zywrle_level);
|
||||
zrle_encode_24ble(vs, &worker->zrle, x, y, w, h, zywrle_level);
|
||||
}
|
||||
} else {
|
||||
if (be) {
|
||||
zrle_encode_32be(vs, x, y, w, h, zywrle_level);
|
||||
zrle_encode_32be(vs, &worker->zrle, x, y, w, h, zywrle_level);
|
||||
} else {
|
||||
zrle_encode_32le(vs, x, y, w, h, zywrle_level);
|
||||
zrle_encode_32le(vs, &worker->zrle, x, y, w, h, zywrle_level);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
vnc_zrle_stop(vs);
|
||||
bytes = zrle_compress_data(vs, Z_DEFAULT_COMPRESSION);
|
||||
vnc_framebuffer_update(vs, x, y, w, h, vs->zrle->type);
|
||||
vnc_zrle_stop(vs, &worker->zrle);
|
||||
bytes = zrle_compress_data(vs, &worker->zrle, Z_DEFAULT_COMPRESSION);
|
||||
vnc_framebuffer_update(vs, x, y, w, h, worker->zrle.type);
|
||||
vnc_write_u32(vs, bytes);
|
||||
vnc_write(vs, vs->zrle->zlib.buffer, vs->zrle->zlib.offset);
|
||||
vnc_write(vs, worker->zrle.zlib.buffer, worker->zrle.zlib.offset);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int vnc_zrle_send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
|
||||
int vnc_zrle_send_framebuffer_update(VncState *vs, VncWorker *worker,
|
||||
int x, int y, int w, int h)
|
||||
{
|
||||
vs->zrle->type = VNC_ENCODING_ZRLE;
|
||||
return zrle_send_framebuffer_update(vs, x, y, w, h);
|
||||
worker->zrle.type = VNC_ENCODING_ZRLE;
|
||||
return zrle_send_framebuffer_update(vs, worker, x, y, w, h);
|
||||
}
|
||||
|
||||
int vnc_zywrle_send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
|
||||
int vnc_zywrle_send_framebuffer_update(VncState *vs, VncWorker *worker,
|
||||
int x, int y, int w, int h)
|
||||
{
|
||||
vs->zrle->type = VNC_ENCODING_ZYWRLE;
|
||||
return zrle_send_framebuffer_update(vs, x, y, w, h);
|
||||
worker->zrle.type = VNC_ENCODING_ZYWRLE;
|
||||
return zrle_send_framebuffer_update(vs, worker, x, y, w, h);
|
||||
}
|
||||
|
||||
void vnc_zrle_clear(VncState *vs)
|
||||
void vnc_zrle_clear(VncWorker *worker)
|
||||
{
|
||||
if (vs->zrle->stream.opaque) {
|
||||
deflateEnd(&vs->zrle->stream);
|
||||
if (worker->zrle.stream.opaque) {
|
||||
deflateEnd(&worker->zrle.stream);
|
||||
}
|
||||
buffer_free(&vs->zrle->zrle);
|
||||
buffer_free(&vs->zrle->fb);
|
||||
buffer_free(&vs->zrle->zlib);
|
||||
buffer_free(&worker->zrle.zrle);
|
||||
buffer_free(&worker->zrle.fb);
|
||||
buffer_free(&worker->zrle.zlib);
|
||||
}
|
||||
|
@ -62,16 +62,16 @@
|
||||
#define ZRLE_ENCODE_TILE ZRLE_CONCAT2(zrle_encode_tile, ZRLE_ENCODE_SUFFIX)
|
||||
#define ZRLE_WRITE_PALETTE ZRLE_CONCAT2(zrle_write_palette,ZRLE_ENCODE_SUFFIX)
|
||||
|
||||
static void ZRLE_ENCODE_TILE(VncState *vs, ZRLE_PIXEL *data, int w, int h,
|
||||
int zywrle_level);
|
||||
static void ZRLE_ENCODE_TILE(VncState *vs, VncZrle *zrle, ZRLE_PIXEL *data,
|
||||
int w, int h, int zywrle_level);
|
||||
|
||||
#if ZRLE_BPP != 8
|
||||
#include "vnc-enc-zywrle-template.c"
|
||||
#endif
|
||||
|
||||
|
||||
static void ZRLE_ENCODE(VncState *vs, int x, int y, int w, int h,
|
||||
int zywrle_level)
|
||||
static void ZRLE_ENCODE(VncState *vs, VncZrle *zrle,
|
||||
int x, int y, int w, int h, int zywrle_level)
|
||||
{
|
||||
int ty;
|
||||
|
||||
@ -87,16 +87,16 @@ static void ZRLE_ENCODE(VncState *vs, int x, int y, int w, int h,
|
||||
|
||||
tw = MIN(VNC_ZRLE_TILE_WIDTH, x + w - tx);
|
||||
|
||||
buf = zrle_convert_fb(vs, tx, ty, tw, th, ZRLE_BPP);
|
||||
ZRLE_ENCODE_TILE(vs, buf, tw, th, zywrle_level);
|
||||
buf = zrle_convert_fb(vs, zrle, tx, ty, tw, th, ZRLE_BPP);
|
||||
ZRLE_ENCODE_TILE(vs, zrle, buf, tw, th, zywrle_level);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ZRLE_ENCODE_TILE(VncState *vs, ZRLE_PIXEL *data, int w, int h,
|
||||
int zywrle_level)
|
||||
static void ZRLE_ENCODE_TILE(VncState *vs, VncZrle *zrle, ZRLE_PIXEL *data,
|
||||
int w, int h, int zywrle_level)
|
||||
{
|
||||
VncPalette *palette = &vs->zrle->palette;
|
||||
VncPalette *palette = &zrle->palette;
|
||||
|
||||
int runs = 0;
|
||||
int single_pixels = 0;
|
||||
@ -236,7 +236,7 @@ static void ZRLE_ENCODE_TILE(VncState *vs, ZRLE_PIXEL *data, int w, int h,
|
||||
#if ZRLE_BPP != 8
|
||||
if (zywrle_level > 0 && !(zywrle_level & 0x80)) {
|
||||
ZYWRLE_ANALYZE(data, data, w, h, w, zywrle_level, vs->zywrle.buf);
|
||||
ZRLE_ENCODE_TILE(vs, data, w, h, zywrle_level | 0x80);
|
||||
ZRLE_ENCODE_TILE(vs, zrle, data, w, h, zywrle_level | 0x80);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
|
@ -185,14 +185,10 @@ static void vnc_async_encoding_start(VncState *orig, VncState *local)
|
||||
local->vnc_encoding = orig->vnc_encoding;
|
||||
local->features = orig->features;
|
||||
local->vd = orig->vd;
|
||||
local->lossy_rect = orig->lossy_rect;
|
||||
local->write_pixels = orig->write_pixels;
|
||||
local->client_pf = orig->client_pf;
|
||||
local->client_endian = orig->client_endian;
|
||||
local->tight = orig->tight;
|
||||
local->zlib = orig->zlib;
|
||||
local->hextile = orig->hextile;
|
||||
local->zrle = orig->zrle;
|
||||
local->client_width = orig->client_width;
|
||||
local->client_height = orig->client_height;
|
||||
}
|
||||
@ -200,11 +196,7 @@ static void vnc_async_encoding_start(VncState *orig, VncState *local)
|
||||
static void vnc_async_encoding_end(VncState *orig, VncState *local)
|
||||
{
|
||||
buffer_free(&local->output);
|
||||
orig->tight = local->tight;
|
||||
orig->zlib = local->zlib;
|
||||
orig->hextile = local->hextile;
|
||||
orig->zrle = local->zrle;
|
||||
orig->lossy_rect = local->lossy_rect;
|
||||
}
|
||||
|
||||
static bool vnc_worker_clamp_rect(VncState *vs, VncJob *job, VncRect *rect)
|
||||
@ -237,6 +229,7 @@ static bool vnc_worker_clamp_rect(VncState *vs, VncJob *job, VncRect *rect)
|
||||
|
||||
static int vnc_worker_thread_loop(VncJobQueue *queue)
|
||||
{
|
||||
VncConnection *vc;
|
||||
VncJob *job;
|
||||
VncRectEntry *entry, *tmp;
|
||||
VncState vs = {};
|
||||
@ -256,6 +249,7 @@ static int vnc_worker_thread_loop(VncJobQueue *queue)
|
||||
}
|
||||
|
||||
assert(job->vs->magic == VNC_MAGIC);
|
||||
vc = container_of(job->vs, VncConnection, vs);
|
||||
|
||||
vnc_lock_output(job->vs);
|
||||
if (job->vs->ioc == NULL || job->vs->abort == true) {
|
||||
@ -295,7 +289,8 @@ static int vnc_worker_thread_loop(VncJobQueue *queue)
|
||||
}
|
||||
|
||||
if (vnc_worker_clamp_rect(&vs, job, &entry->rect)) {
|
||||
n = vnc_send_framebuffer_update(&vs, entry->rect.x, entry->rect.y,
|
||||
n = vnc_send_framebuffer_update(&vs, &vc->worker,
|
||||
entry->rect.x, entry->rect.y,
|
||||
entry->rect.w, entry->rect.h);
|
||||
|
||||
if (n >= 0) {
|
||||
|
83
ui/vnc.c
83
ui/vnc.c
@ -946,29 +946,30 @@ int vnc_raw_send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int vnc_send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
|
||||
int vnc_send_framebuffer_update(VncState *vs, VncWorker *worker,
|
||||
int x, int y, int w, int h)
|
||||
{
|
||||
int n = 0;
|
||||
|
||||
switch(vs->vnc_encoding) {
|
||||
case VNC_ENCODING_ZLIB:
|
||||
n = vnc_zlib_send_framebuffer_update(vs, x, y, w, h);
|
||||
n = vnc_zlib_send_framebuffer_update(vs, worker, x, y, w, h);
|
||||
break;
|
||||
case VNC_ENCODING_HEXTILE:
|
||||
vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_HEXTILE);
|
||||
n = vnc_hextile_send_framebuffer_update(vs, x, y, w, h);
|
||||
break;
|
||||
case VNC_ENCODING_TIGHT:
|
||||
n = vnc_tight_send_framebuffer_update(vs, x, y, w, h);
|
||||
n = vnc_tight_send_framebuffer_update(vs, worker, x, y, w, h);
|
||||
break;
|
||||
case VNC_ENCODING_TIGHT_PNG:
|
||||
n = vnc_tight_png_send_framebuffer_update(vs, x, y, w, h);
|
||||
n = vnc_tight_png_send_framebuffer_update(vs, worker, x, y, w, h);
|
||||
break;
|
||||
case VNC_ENCODING_ZRLE:
|
||||
n = vnc_zrle_send_framebuffer_update(vs, x, y, w, h);
|
||||
n = vnc_zrle_send_framebuffer_update(vs, worker, x, y, w, h);
|
||||
break;
|
||||
case VNC_ENCODING_ZYWRLE:
|
||||
n = vnc_zywrle_send_framebuffer_update(vs, x, y, w, h);
|
||||
n = vnc_zywrle_send_framebuffer_update(vs, worker, x, y, w, h);
|
||||
break;
|
||||
default:
|
||||
vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_RAW);
|
||||
@ -1306,7 +1307,7 @@ static void vnc_disconnect_start(VncState *vs)
|
||||
|
||||
void vnc_disconnect_finish(VncState *vs)
|
||||
{
|
||||
int i;
|
||||
VncConnection *vc = container_of(vs, VncConnection, vs);
|
||||
|
||||
trace_vnc_client_disconnect_finish(vs, vs->ioc);
|
||||
|
||||
@ -1320,9 +1321,9 @@ void vnc_disconnect_finish(VncState *vs)
|
||||
|
||||
qapi_free_VncClientInfo(vs->info);
|
||||
|
||||
vnc_zlib_clear(vs);
|
||||
vnc_tight_clear(vs);
|
||||
vnc_zrle_clear(vs);
|
||||
vnc_zlib_clear(&vc->worker);
|
||||
vnc_tight_clear(&vc->worker);
|
||||
vnc_zrle_clear(&vc->worker);
|
||||
|
||||
#ifdef CONFIG_VNC_SASL
|
||||
vnc_sasl_client_cleanup(vs);
|
||||
@ -1350,19 +1351,12 @@ void vnc_disconnect_finish(VncState *vs)
|
||||
}
|
||||
buffer_free(&vs->jobs_buffer);
|
||||
|
||||
for (i = 0; i < VNC_STAT_ROWS; ++i) {
|
||||
g_free(vs->lossy_rect[i]);
|
||||
}
|
||||
g_free(vs->lossy_rect);
|
||||
|
||||
object_unref(OBJECT(vs->ioc));
|
||||
vs->ioc = NULL;
|
||||
object_unref(OBJECT(vs->sioc));
|
||||
vs->sioc = NULL;
|
||||
vs->magic = 0;
|
||||
g_free(vs->zrle);
|
||||
g_free(vs->tight);
|
||||
g_free(vs);
|
||||
g_free(vc);
|
||||
}
|
||||
|
||||
size_t vnc_client_io_error(VncState *vs, ssize_t ret, Error *err)
|
||||
@ -2126,13 +2120,14 @@ static void send_xvp_message(VncState *vs, int code)
|
||||
|
||||
static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings)
|
||||
{
|
||||
VncConnection *vc = container_of(vs, VncConnection, vs);
|
||||
int i;
|
||||
unsigned int enc = 0;
|
||||
|
||||
vs->features = 0;
|
||||
vs->vnc_encoding = 0;
|
||||
vs->tight->compression = 9;
|
||||
vs->tight->quality = -1; /* Lossless by default */
|
||||
vc->worker.tight.compression = 9;
|
||||
vc->worker.tight.quality = -1; /* Lossless by default */
|
||||
vs->absolute = -1;
|
||||
|
||||
/*
|
||||
@ -2220,11 +2215,11 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings)
|
||||
vnc_server_cut_text_caps(vs);
|
||||
break;
|
||||
case VNC_ENCODING_COMPRESSLEVEL0 ... VNC_ENCODING_COMPRESSLEVEL0 + 9:
|
||||
vs->tight->compression = (enc & 0x0F);
|
||||
vc->worker.tight.compression = (enc & 0x0F);
|
||||
break;
|
||||
case VNC_ENCODING_QUALITYLEVEL0 ... VNC_ENCODING_QUALITYLEVEL0 + 9:
|
||||
if (vs->vd->lossy) {
|
||||
vs->tight->quality = (enc & 0x0F);
|
||||
vc->worker.tight.quality = (enc & 0x0F);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@ -2953,7 +2948,7 @@ static VncRectStat *vnc_stat_rect(VncDisplay *vd, int x, int y)
|
||||
return &vs->stats[y / VNC_STAT_RECT][x / VNC_STAT_RECT];
|
||||
}
|
||||
|
||||
void vnc_sent_lossy_rect(VncState *vs, int x, int y, int w, int h)
|
||||
void vnc_sent_lossy_rect(VncWorker *worker, int x, int y, int w, int h)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
@ -2964,7 +2959,7 @@ void vnc_sent_lossy_rect(VncState *vs, int x, int y, int w, int h)
|
||||
|
||||
for (j = y; j <= h; j++) {
|
||||
for (i = x; i <= w; i++) {
|
||||
vs->lossy_rect[j][i] = 1;
|
||||
worker->lossy_rect[j][i] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2980,6 +2975,7 @@ static int vnc_refresh_lossy_rect(VncDisplay *vd, int x, int y)
|
||||
x = QEMU_ALIGN_DOWN(x, VNC_STAT_RECT);
|
||||
|
||||
QTAILQ_FOREACH(vs, &vd->clients, next) {
|
||||
VncConnection *vc = container_of(vs, VncConnection, vs);
|
||||
int j;
|
||||
|
||||
/* kernel send buffers are full -> refresh later */
|
||||
@ -2987,11 +2983,11 @@ static int vnc_refresh_lossy_rect(VncDisplay *vd, int x, int y)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!vs->lossy_rect[sty][stx]) {
|
||||
if (!vc->worker.lossy_rect[sty][stx]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
vs->lossy_rect[sty][stx] = 0;
|
||||
vc->worker.lossy_rect[sty][stx] = 0;
|
||||
for (j = 0; j < VNC_STAT_RECT; ++j) {
|
||||
bitmap_set(vs->dirty[y + j],
|
||||
x / VNC_DIRTY_PIXELS_PER_BIT,
|
||||
@ -3241,13 +3237,11 @@ static void vnc_refresh(DisplayChangeListener *dcl)
|
||||
static void vnc_connect(VncDisplay *vd, QIOChannelSocket *sioc,
|
||||
bool skipauth, bool websocket)
|
||||
{
|
||||
VncState *vs = g_new0(VncState, 1);
|
||||
VncConnection *vc = g_new0(VncConnection, 1);
|
||||
VncState *vs = &vc->vs;
|
||||
bool first_client = QTAILQ_EMPTY(&vd->clients);
|
||||
int i;
|
||||
|
||||
trace_vnc_client_connect(vs, sioc);
|
||||
vs->zrle = g_new0(VncZrle, 1);
|
||||
vs->tight = g_new0(VncTight, 1);
|
||||
vs->magic = VNC_MAGIC;
|
||||
vs->sioc = sioc;
|
||||
object_ref(OBJECT(vs->sioc));
|
||||
@ -3255,23 +3249,23 @@ static void vnc_connect(VncDisplay *vd, QIOChannelSocket *sioc,
|
||||
object_ref(OBJECT(vs->ioc));
|
||||
vs->vd = vd;
|
||||
|
||||
buffer_init(&vs->input, "vnc-input/%p", sioc);
|
||||
buffer_init(&vs->output, "vnc-output/%p", sioc);
|
||||
buffer_init(&vs->jobs_buffer, "vnc-jobs_buffer/%p", sioc);
|
||||
buffer_init(&vs->input, "vnc-input/%p", sioc);
|
||||
buffer_init(&vs->output, "vnc-output/%p", sioc);
|
||||
buffer_init(&vs->jobs_buffer, "vnc-jobs_buffer/%p", sioc);
|
||||
|
||||
buffer_init(&vs->tight->tight, "vnc-tight/%p", sioc);
|
||||
buffer_init(&vs->tight->zlib, "vnc-tight-zlib/%p", sioc);
|
||||
buffer_init(&vs->tight->gradient, "vnc-tight-gradient/%p", sioc);
|
||||
buffer_init(&vc->worker.tight.tight, "vnc-tight/%p", sioc);
|
||||
buffer_init(&vc->worker.tight.zlib, "vnc-tight-zlib/%p", sioc);
|
||||
buffer_init(&vc->worker.tight.gradient, "vnc-tight-gradient/%p", sioc);
|
||||
#ifdef CONFIG_VNC_JPEG
|
||||
buffer_init(&vs->tight->jpeg, "vnc-tight-jpeg/%p", sioc);
|
||||
buffer_init(&vc->worker.tight.jpeg, "vnc-tight-jpeg/%p", sioc);
|
||||
#endif
|
||||
#ifdef CONFIG_PNG
|
||||
buffer_init(&vs->tight->png, "vnc-tight-png/%p", sioc);
|
||||
buffer_init(&vc->worker.tight.png, "vnc-tight-png/%p", sioc);
|
||||
#endif
|
||||
buffer_init(&vs->zlib.zlib, "vnc-zlib/%p", sioc);
|
||||
buffer_init(&vs->zrle->zrle, "vnc-zrle/%p", sioc);
|
||||
buffer_init(&vs->zrle->fb, "vnc-zrle-fb/%p", sioc);
|
||||
buffer_init(&vs->zrle->zlib, "vnc-zrle-zlib/%p", sioc);
|
||||
buffer_init(&vc->worker.zlib.zlib, "vnc-zlib/%p", sioc);
|
||||
buffer_init(&vc->worker.zrle.zrle, "vnc-zrle/%p", sioc);
|
||||
buffer_init(&vc->worker.zrle.fb, "vnc-zrle-fb/%p", sioc);
|
||||
buffer_init(&vc->worker.zrle.zlib, "vnc-zrle-zlib/%p", sioc);
|
||||
|
||||
if (skipauth) {
|
||||
vs->auth = VNC_AUTH_NONE;
|
||||
@ -3288,11 +3282,6 @@ static void vnc_connect(VncDisplay *vd, QIOChannelSocket *sioc,
|
||||
VNC_DEBUG("Client sioc=%p ws=%d auth=%d subauth=%d\n",
|
||||
sioc, websocket, vs->auth, vs->subauth);
|
||||
|
||||
vs->lossy_rect = g_malloc0(VNC_STAT_ROWS * sizeof (*vs->lossy_rect));
|
||||
for (i = 0; i < VNC_STAT_ROWS; ++i) {
|
||||
vs->lossy_rect[i] = g_new0(uint8_t, VNC_STAT_COLS);
|
||||
}
|
||||
|
||||
VNC_DEBUG("New client on socket %p\n", vs->sioc);
|
||||
update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_BASE);
|
||||
qio_channel_set_blocking(vs->ioc, false, NULL);
|
||||
|
45
ui/vnc.h
45
ui/vnc.h
@ -272,8 +272,6 @@ struct VncState
|
||||
gboolean disconnecting;
|
||||
|
||||
DECLARE_BITMAP(dirty[VNC_MAX_HEIGHT], VNC_DIRTY_BITS);
|
||||
uint8_t **lossy_rect; /* Not an Array to avoid costly memcpy in
|
||||
* vnc-jobs-async.c */
|
||||
|
||||
VncDisplay *vd;
|
||||
VncStateUpdate update; /* Most recent pending request from client */
|
||||
@ -341,10 +339,7 @@ struct VncState
|
||||
/* Encoding specific, if you add something here, don't forget to
|
||||
* update vnc_async_encoding_start()
|
||||
*/
|
||||
VncTight *tight;
|
||||
VncZlib zlib;
|
||||
VncHextile hextile;
|
||||
VncZrle *zrle;
|
||||
VncZywrle zywrle;
|
||||
|
||||
Notifier mouse_mode_notifier;
|
||||
@ -356,6 +351,19 @@ struct VncState
|
||||
QTAILQ_ENTRY(VncState) next;
|
||||
};
|
||||
|
||||
typedef struct VncWorker {
|
||||
uint8_t lossy_rect[VNC_STAT_ROWS][VNC_STAT_COLS];
|
||||
|
||||
VncTight tight;
|
||||
VncZlib zlib;
|
||||
VncZrle zrle;
|
||||
} VncWorker;
|
||||
|
||||
typedef struct VncConnection {
|
||||
VncState vs;
|
||||
VncWorker worker;
|
||||
} VncConnection;
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
@ -602,10 +610,11 @@ int vnc_server_fb_stride(VncDisplay *vd);
|
||||
|
||||
void vnc_convert_pixel(VncState *vs, uint8_t *buf, uint32_t v);
|
||||
double vnc_update_freq(VncState *vs, int x, int y, int w, int h);
|
||||
void vnc_sent_lossy_rect(VncState *vs, int x, int y, int w, int h);
|
||||
void vnc_sent_lossy_rect(VncWorker *worker, int x, int y, int w, int h);
|
||||
|
||||
/* Encodings */
|
||||
int vnc_send_framebuffer_update(VncState *vs, int x, int y, int w, int h);
|
||||
int vnc_send_framebuffer_update(VncState *vs, VncWorker *worker,
|
||||
int x, int y, int w, int h);
|
||||
|
||||
int vnc_raw_send_framebuffer_update(VncState *vs, int x, int y, int w, int h);
|
||||
|
||||
@ -615,17 +624,21 @@ void vnc_hextile_set_pixel_conversion(VncState *vs, int generic);
|
||||
|
||||
void *vnc_zlib_zalloc(void *x, unsigned items, unsigned size);
|
||||
void vnc_zlib_zfree(void *x, void *addr);
|
||||
int vnc_zlib_send_framebuffer_update(VncState *vs, int x, int y, int w, int h);
|
||||
void vnc_zlib_clear(VncState *vs);
|
||||
int vnc_zlib_send_framebuffer_update(VncState *vs, VncWorker *worker,
|
||||
int x, int y, int w, int h);
|
||||
void vnc_zlib_clear(VncWorker *worker);
|
||||
|
||||
int vnc_tight_send_framebuffer_update(VncState *vs, int x, int y, int w, int h);
|
||||
int vnc_tight_png_send_framebuffer_update(VncState *vs, int x, int y,
|
||||
int w, int h);
|
||||
void vnc_tight_clear(VncState *vs);
|
||||
int vnc_tight_send_framebuffer_update(VncState *vs, VncWorker *worker,
|
||||
int x, int y, int w, int h);
|
||||
int vnc_tight_png_send_framebuffer_update(VncState *vs, VncWorker *worker,
|
||||
int x, int y, int w, int h);
|
||||
void vnc_tight_clear(VncWorker *worker);
|
||||
|
||||
int vnc_zrle_send_framebuffer_update(VncState *vs, int x, int y, int w, int h);
|
||||
int vnc_zywrle_send_framebuffer_update(VncState *vs, int x, int y, int w, int h);
|
||||
void vnc_zrle_clear(VncState *vs);
|
||||
int vnc_zrle_send_framebuffer_update(VncState *vs, VncWorker *worker,
|
||||
int x, int y, int w, int h);
|
||||
int vnc_zywrle_send_framebuffer_update(VncState *vs, VncWorker *worker,
|
||||
int x, int y, int w, int h);
|
||||
void vnc_zrle_clear(VncWorker *worker);
|
||||
|
||||
/* vnc-clipboard.c */
|
||||
void vnc_server_cut_text_caps(VncState *vs);
|
||||
|
Loading…
Reference in New Issue
Block a user