1 /*
2    Copyright (C) 2009-2015 Red Hat, Inc.
3 
4    This library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8 
9    This library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13 
14    You should have received a copy of the GNU Lesser General Public
15    License along with this library; if not, see <http://www.gnu.org/licenses/>.
16 */
17 
18 #ifndef DISPLAY_CHANNEL_PRIVATE_H_
19 #define DISPLAY_CHANNEL_PRIVATE_H_
20 
21 #include "display-channel.h"
22 
23 #define TRACE_ITEMS_SHIFT 3
24 #define NUM_TRACE_ITEMS (1 << TRACE_ITEMS_SHIFT)
25 #define ITEMS_TRACE_MASK (NUM_TRACE_ITEMS - 1)
26 
27 typedef struct DrawContext {
28     SpiceCanvas *canvas;
29     int canvas_draws_on_surface;
30     int top_down;
31     uint32_t width;
32     uint32_t height;
33     int32_t stride;
34     uint32_t format;
35     void *line_0;
36 } DrawContext;
37 
38 typedef struct RedSurface {
39     uint32_t refs;
40     /* A Ring representing a hierarchical tree structure. This tree includes
41      * DrawItems, Containers, and Shadows. It is used to efficiently determine
42      * which drawables overlap, and to exclude regions of drawables that are
43      * obscured by other drawables */
44     Ring current;
45     /* A ring of pending Drawables associated with this surface. This ring is
46      * actually used for drawing. The ring is maintained in order of age, the
47      * tail being the oldest drawable. */
48     Ring current_list;
49     DrawContext context;
50 
51     Ring depend_on_me;
52     QRegion draw_dirty_region;
53 
54     //fix me - better handling here
55     /* 'create_cmd' holds surface data through a pointer to guest memory, it
56      * must be valid as long as the surface is valid */
57     RedSurfaceCmd *create_cmd;
58     /* QEMU expects the guest data for the command to be valid as long as the
59      * surface is valid */
60     RedSurfaceCmd *destroy_cmd;
61 } RedSurface;
62 
63 typedef struct MonitorsConfig {
64     int refs;
65     int count;
66     int max_allowed;
67     QXLHead heads[0];
68 } MonitorsConfig;
69 
70 #define NUM_DRAWABLES 1000
71 typedef struct _Drawable _Drawable;
72 struct _Drawable {
73     union {
74         Drawable drawable;
75         _Drawable *next;
76     } u;
77 };
78 
79 struct DisplayChannelPrivate
80 {
81     SPICE_CXX_GLIB_ALLOCATOR
82 
83     DisplayChannel *pub;
84 
85     QXLInstance *qxl;
86 
87     uint32_t bits_unique;
88 
89     MonitorsConfig *monitors_config;
90 
91     uint32_t renderer;
92     SpiceImageCompression image_compression;
93     int enable_jpeg;
94     int enable_zlib_glz_wrap;
95 
96     /* A ring of pending drawables for this DisplayChannel, regardless of which
97      * surface they're associated with. This list is mainly used to flush older
98      * drawables when we need to make room for new drawables.  The ring is
99      * maintained in order of age, the tail being the oldest drawable */
100     Ring current_list;
101 
102     uint32_t drawable_count;
103     _Drawable drawables[NUM_DRAWABLES];
104     _Drawable *free_drawables;
105 
106     int stream_video;
107     GArray *video_codecs;
108     uint32_t stream_count;
109     VideoStream streams_buf[NUM_STREAMS];
110     VideoStream *free_streams;
111     Ring streams;
112     ItemTrace items_trace[NUM_TRACE_ITEMS];
113     uint32_t next_item_trace;
114     uint64_t streams_size_total;
115 
116     RedSurface surfaces[NUM_SURFACES];
117     uint32_t n_surfaces;
118     SpiceImageSurfaces image_surfaces;
119 
120     ImageCache image_cache;
121 
122     int gl_draw_async_count;
123 
124 /* TODO: some day unify this, make it more runtime.. */
125     stat_info_t add_stat;
126     stat_info_t exclude_stat;
127     stat_info_t __exclude_stat;
128 #ifdef RED_WORKER_STAT
129     uint32_t add_count;
130     uint32_t add_with_shadow_count;
131 #endif
132     RedStatCounter cache_hits_counter;
133     RedStatCounter add_to_cache_counter;
134     RedStatCounter non_cache_counter;
135     ImageEncoderSharedData encoder_shared_data;
136 };
137 
138 #define FOREACH_DCC(_channel, _data) \
139     GLIST_FOREACH((_channel ? _channel->get_clients() : NULL), \
140                   DisplayChannelClient, _data)
141 
142 enum {
143     RED_PIPE_ITEM_TYPE_DRAW = RED_PIPE_ITEM_TYPE_COMMON_LAST,
144     RED_PIPE_ITEM_TYPE_IMAGE,
145     RED_PIPE_ITEM_TYPE_STREAM_CREATE,
146     RED_PIPE_ITEM_TYPE_STREAM_CLIP,
147     RED_PIPE_ITEM_TYPE_STREAM_DESTROY,
148     RED_PIPE_ITEM_TYPE_UPGRADE,
149     RED_PIPE_ITEM_TYPE_MIGRATE_DATA,
150     RED_PIPE_ITEM_TYPE_PIXMAP_SYNC,
151     RED_PIPE_ITEM_TYPE_PIXMAP_RESET,
152     RED_PIPE_ITEM_TYPE_INVAL_PALETTE_CACHE,
153     RED_PIPE_ITEM_TYPE_CREATE_SURFACE,
154     RED_PIPE_ITEM_TYPE_DESTROY_SURFACE,
155     RED_PIPE_ITEM_TYPE_MONITORS_CONFIG,
156     RED_PIPE_ITEM_TYPE_STREAM_ACTIVATE_REPORT,
157     RED_PIPE_ITEM_TYPE_GL_SCANOUT,
158     RED_PIPE_ITEM_TYPE_GL_DRAW,
159 };
160 
161 struct RedMonitorsConfigItem: public RedPipeItemNum<RED_PIPE_ITEM_TYPE_MONITORS_CONFIG> {
162     RedMonitorsConfigItem(MonitorsConfig *monitors_config);
163     ~RedMonitorsConfigItem();
164     MonitorsConfig *monitors_config;
165 };
166 
167 void drawable_unref(Drawable *drawable);
168 
169 MonitorsConfig *monitors_config_ref(MonitorsConfig *config);
170 void monitors_config_unref(MonitorsConfig *config);
171 
172 void display_channel_draw_until(DisplayChannel *display,
173                                 const SpiceRect *area,
174                                 int surface_id,
175                                 Drawable *last);
176 GArray* display_channel_get_video_codecs(DisplayChannel *display);
177 int display_channel_get_stream_video(DisplayChannel *display);
178 void display_channel_current_flush(DisplayChannel *display,
179                                    int surface_id);
180 uint32_t display_channel_generate_uid(DisplayChannel *display);
181 
182 int display_channel_get_video_stream_id(DisplayChannel *display, VideoStream *stream);
183 VideoStream *display_channel_get_nth_video_stream(DisplayChannel *display, gint i);
184 
185 struct RedSurfaceDestroyItem: public RedPipeItemNum<RED_PIPE_ITEM_TYPE_DESTROY_SURFACE> {
186     RedSurfaceDestroyItem(uint32_t surface_id);
187     SpiceMsgSurfaceDestroy surface_destroy;
188 };
189 
190 struct RedSurfaceCreateItem: public RedPipeItemNum<RED_PIPE_ITEM_TYPE_CREATE_SURFACE> {
191     RedSurfaceCreateItem(uint32_t surface_id,
192                          uint32_t width,
193                          uint32_t height,
194                          uint32_t format,
195                          uint32_t flags);
196     SpiceMsgSurfaceCreate surface_create;
197 };
198 
199 struct RedGlScanoutUnixItem: public RedPipeItemNum<RED_PIPE_ITEM_TYPE_GL_SCANOUT> {
200 };
201 
202 struct RedGlDrawItem: public RedPipeItemNum<RED_PIPE_ITEM_TYPE_GL_DRAW> {
203     SpiceMsgDisplayGlDraw draw;
204 };
205 
206 struct RedImageItem final: public RedPipeItemNum<RED_PIPE_ITEM_TYPE_IMAGE> {
207     SpicePoint pos;
208     int width;
209     int height;
210     int stride;
211     int top_down;
212     int surface_id;
213     int image_format;
214     uint32_t image_flags;
215     int can_lossy;
216     uint8_t data[0];
217 };
218 
219 struct RedDrawablePipeItem: public RedPipeItemNum<RED_PIPE_ITEM_TYPE_DRAW> {
220     RedDrawablePipeItem(DisplayChannelClient *dcc, Drawable *drawable);
221     ~RedDrawablePipeItem();
222     Drawable *const drawable;
223     DisplayChannelClient *const dcc;
224 };
225 
226 /* This item is used to send a full quality image (lossless) of the area where the stream was.
227  * This to avoid the artifacts due to the lossy compression. */
228 struct RedUpgradeItem: public RedPipeItemNum<RED_PIPE_ITEM_TYPE_UPGRADE> {
229     RedUpgradeItem(Drawable *drawable);
230     ~RedUpgradeItem();
231     Drawable *const drawable;
232     red::glib_unique_ptr<SpiceClipRects> rects;
233 };
234 
235 struct RedStreamActivateReportItem:
236     public RedPipeItemNum<RED_PIPE_ITEM_TYPE_STREAM_ACTIVATE_REPORT>
237 {
238     uint32_t stream_id;
239     uint32_t report_id;
240 };
241 
is_equal_path(SpicePath * path1,SpicePath * path2)242 static inline int is_equal_path(SpicePath *path1, SpicePath *path2)
243 {
244     SpicePathSeg *seg1, *seg2;
245     int i, j;
246 
247     if (path1->num_segments != path2->num_segments)
248         return FALSE;
249 
250     for (i = 0; i < path1->num_segments; i++) {
251         seg1 = path1->segments[i];
252         seg2 = path2->segments[i];
253 
254         if (seg1->flags != seg2->flags ||
255             seg1->count != seg2->count) {
256             return FALSE;
257         }
258         for (j = 0; j < seg1->count; j++) {
259             if (seg1->points[j].x != seg2->points[j].x ||
260                 seg1->points[j].y != seg2->points[j].y) {
261                 return FALSE;
262             }
263         }
264     }
265 
266     return TRUE;
267 }
268 
269 // partial imp
is_equal_brush(SpiceBrush * b1,SpiceBrush * b2)270 static inline int is_equal_brush(SpiceBrush *b1, SpiceBrush *b2)
271 {
272     return b1->type == b2->type &&
273            b1->type == SPICE_BRUSH_TYPE_SOLID &&
274            b1->u.color == b2->u.color;
275 }
276 
277 // partial imp
is_equal_line_attr(SpiceLineAttr * a1,SpiceLineAttr * a2)278 static inline int is_equal_line_attr(SpiceLineAttr *a1, SpiceLineAttr *a2)
279 {
280     return a1->flags == a2->flags &&
281            a1->style_nseg == a2->style_nseg &&
282            a1->style_nseg == 0;
283 }
284 
285 // partial imp
is_same_geometry(Drawable * d1,Drawable * d2)286 static inline int is_same_geometry(Drawable *d1, Drawable *d2)
287 {
288     if (d1->red_drawable->type != d2->red_drawable->type) {
289         return FALSE;
290     }
291 
292     switch (d1->red_drawable->type) {
293     case QXL_DRAW_STROKE:
294         return is_equal_line_attr(&d1->red_drawable->u.stroke.attr,
295                                   &d2->red_drawable->u.stroke.attr) &&
296                is_equal_path(d1->red_drawable->u.stroke.path,
297                              d2->red_drawable->u.stroke.path);
298     case QXL_DRAW_FILL:
299         return rect_is_equal(&d1->red_drawable->bbox, &d2->red_drawable->bbox);
300     default:
301         return FALSE;
302     }
303 }
304 
is_same_drawable(Drawable * d1,Drawable * d2)305 static inline int is_same_drawable(Drawable *d1, Drawable *d2)
306 {
307     if (!is_same_geometry(d1, d2)) {
308         return FALSE;
309     }
310 
311     switch (d1->red_drawable->type) {
312     case QXL_DRAW_STROKE:
313         return is_equal_brush(&d1->red_drawable->u.stroke.brush,
314                               &d2->red_drawable->u.stroke.brush);
315     case QXL_DRAW_FILL:
316         return is_equal_brush(&d1->red_drawable->u.fill.brush,
317                               &d2->red_drawable->u.fill.brush);
318     default:
319         return FALSE;
320     }
321 }
322 
is_drawable_independent_from_surfaces(Drawable * drawable)323 static inline int is_drawable_independent_from_surfaces(Drawable *drawable)
324 {
325     int x;
326 
327     for (x = 0; x < 3; ++x) {
328         if (drawable->surface_deps[x] != -1) {
329             return FALSE;
330         }
331     }
332     return TRUE;
333 }
334 
has_shadow(RedDrawable * drawable)335 static inline int has_shadow(RedDrawable *drawable)
336 {
337     return drawable->type == QXL_COPY_BITS;
338 }
339 
is_primary_surface(DisplayChannel * display,uint32_t surface_id)340 static inline int is_primary_surface(DisplayChannel *display, uint32_t surface_id)
341 {
342     if (surface_id == 0) {
343         return TRUE;
344     }
345     return FALSE;
346 }
347 
region_add_clip_rects(QRegion * rgn,SpiceClipRects * data)348 static inline void region_add_clip_rects(QRegion *rgn, SpiceClipRects *data)
349 {
350     int i;
351 
352     for (i = 0; i < data->num_rects; i++) {
353         region_add(rgn, data->rects + i);
354     }
355 }
356 
357 #endif /* DISPLAY_CHANNEL_PRIVATE_H_ */
358