/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
Copyright (C) 2009-2015 Red Hat, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see .
*/
#ifndef DISPLAY_CHANNEL_H_
# define DISPLAY_CHANNEL_H_
#include
#include "common/rect.h"
#include "red-worker.h"
#include "reds-stream.h"
#include "cache-item.h"
#include "pixmap-cache.h"
#include "sw-canvas.h"
#include "stat.h"
#include "reds.h"
#include "memslot.h"
#include "red-parse-qxl.h"
#include "red-record-qxl.h"
#include "demarshallers.h"
#include "red-channel.h"
#include "red-qxl.h"
#include "dispatcher.h"
#include "main-channel.h"
#include "migration-protocol.h"
#include "main-dispatcher.h"
#include "spice-bitmap-utils.h"
#include "image-cache.h"
#include "utils.h"
#include "tree.h"
#include "stream.h"
#include "dcc.h"
#include "display-limits.h"
typedef struct DependItem {
Drawable *drawable;
RingItem ring_item;
} DependItem;
struct Drawable {
uint8_t refs;
RingItem surface_list_link;
RingItem list_link;
DrawItem tree_item;
Ring pipes;
PipeItem *pipe_item_rest;
uint32_t size_pipe_item_rest;
RedDrawable *red_drawable;
Ring glz_ring;
red_time_t creation_time;
red_time_t first_frame_time;
int frames_count;
int gradual_frames_count;
int last_gradual_frame;
Stream *stream;
Stream *sized_stream;
int streamable;
BitmapGradualType copy_bitmap_graduality;
DependItem depend_items[3];
int surface_id;
int surface_deps[3];
uint32_t process_commands_generation;
};
#define LINK_TO_DPI(ptr) SPICE_CONTAINEROF((ptr), DrawablePipeItem, base)
#define DRAWABLE_FOREACH_DPI_SAFE(drawable, link, next, dpi) \
SAFE_FOREACH(link, next, drawable, &(drawable)->pipes, dpi, LINK_TO_DPI(link))
#define LINK_TO_GLZ(ptr) SPICE_CONTAINEROF((ptr), RedGlzDrawable, \
drawable_link)
#define DRAWABLE_FOREACH_GLZ_SAFE(drawable, link, next, glz) \
SAFE_FOREACH(link, next, drawable, &(drawable)->glz_ring, glz, LINK_TO_GLZ(link))
enum {
PIPE_ITEM_TYPE_DRAW = PIPE_ITEM_TYPE_COMMON_LAST,
PIPE_ITEM_TYPE_IMAGE,
PIPE_ITEM_TYPE_STREAM_CREATE,
PIPE_ITEM_TYPE_STREAM_CLIP,
PIPE_ITEM_TYPE_STREAM_DESTROY,
PIPE_ITEM_TYPE_UPGRADE,
PIPE_ITEM_TYPE_MIGRATE_DATA,
PIPE_ITEM_TYPE_PIXMAP_SYNC,
PIPE_ITEM_TYPE_PIXMAP_RESET,
PIPE_ITEM_TYPE_INVAL_PALETTE_CACHE,
PIPE_ITEM_TYPE_CREATE_SURFACE,
PIPE_ITEM_TYPE_DESTROY_SURFACE,
PIPE_ITEM_TYPE_MONITORS_CONFIG,
PIPE_ITEM_TYPE_STREAM_ACTIVATE_REPORT,
PIPE_ITEM_TYPE_GL_SCANOUT,
PIPE_ITEM_TYPE_GL_DRAW,
};
typedef struct MonitorsConfig {
int refs;
int count;
int max_allowed;
QXLHead heads[0];
} MonitorsConfig;
typedef struct MonitorsConfigItem {
PipeItem pipe_item;
MonitorsConfig *monitors_config;
} MonitorsConfigItem;
MonitorsConfig* monitors_config_new (QXLHead *heads, ssize_t nheads,
ssize_t max);
MonitorsConfig * monitors_config_ref (MonitorsConfig *config);
void monitors_config_unref (MonitorsConfig *config);
#define TRACE_ITEMS_SHIFT 3
#define NUM_TRACE_ITEMS (1 << TRACE_ITEMS_SHIFT)
#define ITEMS_TRACE_MASK (NUM_TRACE_ITEMS - 1)
typedef struct DrawContext {
SpiceCanvas *canvas;
int canvas_draws_on_surface;
int top_down;
uint32_t width;
uint32_t height;
int32_t stride;
uint32_t format;
void *line_0;
} DrawContext;
typedef struct RedSurface {
uint32_t refs;
Ring current;
Ring current_list;
DrawContext context;
Ring depend_on_me;
QRegion draw_dirty_region;
//fix me - better handling here
QXLReleaseInfoExt create, destroy;
} RedSurface;
#define NUM_DRAWABLES 1000
typedef struct _Drawable _Drawable;
struct _Drawable {
union {
Drawable drawable;
_Drawable *next;
} u;
};
struct DisplayChannel {
CommonGraphicsChannel common; // Must be the first thing
uint32_t bits_unique;
MonitorsConfig *monitors_config;
uint32_t renderer;
int enable_jpeg;
int enable_zlib_glz_wrap;
Ring current_list; // of TreeItem
uint32_t current_size;
uint32_t drawable_count;
_Drawable drawables[NUM_DRAWABLES];
_Drawable *free_drawables;
uint32_t glz_drawable_count;
int stream_video;
uint32_t stream_count;
Stream streams_buf[NUM_STREAMS];
Stream *free_streams;
Ring streams;
ItemTrace items_trace[NUM_TRACE_ITEMS];
uint32_t next_item_trace;
uint64_t streams_size_total;
RedSurface surfaces[NUM_SURFACES];
uint32_t n_surfaces;
SpiceImageSurfaces image_surfaces;
ImageCache image_cache;
RedCompressBuf *free_compress_bufs;
int gl_draw_async_count;
/* TODO: some day unify this, make it more runtime.. */
stat_info_t add_stat;
stat_info_t exclude_stat;
stat_info_t __exclude_stat;
#ifdef RED_WORKER_STAT
uint32_t add_count;
uint32_t add_with_shadow_count;
#endif
#ifdef RED_STATISTICS
uint64_t *cache_hits_counter;
uint64_t *add_to_cache_counter;
uint64_t *non_cache_counter;
#endif
stat_info_t off_stat;
stat_info_t lz_stat;
stat_info_t glz_stat;
stat_info_t quic_stat;
stat_info_t jpeg_stat;
stat_info_t zlib_glz_stat;
stat_info_t jpeg_alpha_stat;
stat_info_t lz4_stat;
};
#define LINK_TO_DCC(ptr) SPICE_CONTAINEROF(ptr, DisplayChannelClient, \
common.base.channel_link)
#define DCC_FOREACH_SAFE(link, next, dcc, channel) \
SAFE_FOREACH(link, next, channel, &(channel)->clients, dcc, LINK_TO_DCC(link))
#define FOREACH_DCC(display_channel, link, next, dcc) \
DCC_FOREACH_SAFE(link, next, dcc, RED_CHANNEL(display_channel))
static inline int get_stream_id(DisplayChannel *display, Stream *stream)
{
return (int)(stream - display->streams_buf);
}
typedef struct SurfaceDestroyItem {
SpiceMsgSurfaceDestroy surface_destroy;
PipeItem pipe_item;
} SurfaceDestroyItem;
typedef struct UpgradeItem {
PipeItem base;
int refs;
Drawable *drawable;
SpiceClipRects *rects;
} UpgradeItem;
DisplayChannel* display_channel_new (SpiceServer *reds,
RedWorker *worker,
int migrate,
int stream_video,
uint32_t n_surfaces);
void display_channel_create_surface (DisplayChannel *display, uint32_t surface_id,
uint32_t width, uint32_t height,
int32_t stride, uint32_t format, void *line_0,
int data_is_valid, int send_client);
void display_channel_draw (DisplayChannel *display,
const SpiceRect *area,
int surface_id);
void display_channel_draw_until (DisplayChannel *display,
const SpiceRect *area,
int surface_id,
Drawable *last);
void display_channel_update (DisplayChannel *display,
uint32_t surface_id,
const QXLRect *area,
uint32_t clear_dirty,
QXLRect **qxl_dirty_rects,
uint32_t *num_dirty_rects);
void display_channel_free_some (DisplayChannel *display);
void display_channel_set_stream_video (DisplayChannel *display,
int stream_video);
int display_channel_get_streams_timeout (DisplayChannel *display);
void display_channel_compress_stats_print (const DisplayChannel *display);
void display_channel_compress_stats_reset (DisplayChannel *display);
Drawable * display_channel_drawable_try_new (DisplayChannel *display,
int process_commands_generation);
void display_channel_drawable_unref (DisplayChannel *display, Drawable *drawable);
void display_channel_surface_unref (DisplayChannel *display,
uint32_t surface_id);
bool display_channel_surface_has_canvas (DisplayChannel *display,
uint32_t surface_id);
void display_channel_current_flush (DisplayChannel *display,
int surface_id);
int display_channel_wait_for_migrate_data (DisplayChannel *display);
void display_channel_flush_all_surfaces (DisplayChannel *display);
void display_channel_free_glz_drawables_to_free(DisplayChannel *display);
void display_channel_free_glz_drawables (DisplayChannel *display);
void display_channel_destroy_surface_wait (DisplayChannel *display,
uint32_t surface_id);
void display_channel_destroy_surfaces (DisplayChannel *display);
void display_channel_destroy_surface (DisplayChannel *display,
uint32_t surface_id);
uint32_t display_channel_generate_uid (DisplayChannel *display);
void display_channel_process_draw (DisplayChannel *display,
RedDrawable *red_drawable,
int process_commands_generation);
void display_channel_process_surface_cmd (DisplayChannel *display,
RedSurfaceCmd *surface,
int loadvm);
void display_channel_update_compression (DisplayChannel *display,
DisplayChannelClient *dcc);
void display_channel_gl_scanout (DisplayChannel *display);
void display_channel_gl_draw (DisplayChannel *display,
SpiceMsgDisplayGlDraw *draw);
void display_channel_gl_draw_done (DisplayChannel *display);
static inline int validate_surface(DisplayChannel *display, uint32_t surface_id)
{
if SPICE_UNLIKELY(surface_id >= display->n_surfaces) {
spice_warning("invalid surface_id %u", surface_id);
return 0;
}
if (!display->surfaces[surface_id].context.canvas) {
spice_warning("canvas address is %p for %d (and is NULL)\n",
&(display->surfaces[surface_id].context.canvas), surface_id);
spice_warning("failed on %d", surface_id);
return 0;
}
return 1;
}
static inline int is_equal_path(SpicePath *path1, SpicePath *path2)
{
SpicePathSeg *seg1, *seg2;
int i, j;
if (path1->num_segments != path2->num_segments)
return FALSE;
for (i = 0; i < path1->num_segments; i++) {
seg1 = path1->segments[i];
seg2 = path2->segments[i];
if (seg1->flags != seg2->flags ||
seg1->count != seg2->count) {
return FALSE;
}
for (j = 0; j < seg1->count; j++) {
if (seg1->points[j].x != seg2->points[j].x ||
seg1->points[j].y != seg2->points[j].y) {
return FALSE;
}
}
}
return TRUE;
}
// partial imp
static inline int is_equal_brush(SpiceBrush *b1, SpiceBrush *b2)
{
return b1->type == b2->type &&
b1->type == SPICE_BRUSH_TYPE_SOLID &&
b1->u.color == b2->u.color;
}
// partial imp
static inline int is_equal_line_attr(SpiceLineAttr *a1, SpiceLineAttr *a2)
{
return a1->flags == a2->flags &&
a1->style_nseg == a2->style_nseg &&
a1->style_nseg == 0;
}
// partial imp
static inline int is_same_geometry(Drawable *d1, Drawable *d2)
{
if (d1->red_drawable->type != d2->red_drawable->type) {
return FALSE;
}
switch (d1->red_drawable->type) {
case QXL_DRAW_STROKE:
return is_equal_line_attr(&d1->red_drawable->u.stroke.attr,
&d2->red_drawable->u.stroke.attr) &&
is_equal_path(d1->red_drawable->u.stroke.path,
d2->red_drawable->u.stroke.path);
case QXL_DRAW_FILL:
return rect_is_equal(&d1->red_drawable->bbox, &d2->red_drawable->bbox);
default:
return FALSE;
}
}
static inline int is_same_drawable(Drawable *d1, Drawable *d2)
{
if (!is_same_geometry(d1, d2)) {
return FALSE;
}
switch (d1->red_drawable->type) {
case QXL_DRAW_STROKE:
return is_equal_brush(&d1->red_drawable->u.stroke.brush,
&d2->red_drawable->u.stroke.brush);
case QXL_DRAW_FILL:
return is_equal_brush(&d1->red_drawable->u.fill.brush,
&d2->red_drawable->u.fill.brush);
default:
return FALSE;
}
}
static inline int is_drawable_independent_from_surfaces(Drawable *drawable)
{
int x;
for (x = 0; x < 3; ++x) {
if (drawable->surface_deps[x] != -1) {
return FALSE;
}
}
return TRUE;
}
static inline int has_shadow(RedDrawable *drawable)
{
return drawable->type == QXL_COPY_BITS;
}
static inline int is_primary_surface(DisplayChannel *display, uint32_t surface_id)
{
if (surface_id == 0) {
return TRUE;
}
return FALSE;
}
static inline void region_add_clip_rects(QRegion *rgn, SpiceClipRects *data)
{
int i;
for (i = 0; i < data->num_rects; i++) {
region_add(rgn, data->rects + i);
}
}
#endif /* DISPLAY_CHANNEL_H_ */