mirror of
https://gitlab.uni-freiburg.de/opensourcevdi/spice
synced 2026-08-08 03:47:18 +00:00
worker: painfully move display_channel_add_drawable
Acked-by: Jonathon Jongsma <jjongsma@redhat.com> Acked-by: Frediano Ziglio <fziglio@redhat.com>
This commit is contained in:
parent
960e0321b6
commit
d9a488a62f
@ -20,6 +20,21 @@
|
||||
|
||||
#include "display-channel.h"
|
||||
|
||||
static stat_time_t display_channel_stat_now(DisplayChannel *display)
|
||||
{
|
||||
#ifdef RED_WORKER_STAT
|
||||
RedWorker *worker = COMMON_CHANNEL(display)->worker;
|
||||
|
||||
return stat_now(red_worker_get_clockid(worker));
|
||||
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#define stat_start(display, var) \
|
||||
G_GNUC_UNUSED stat_time_t var = display_channel_stat_now((display));
|
||||
|
||||
void display_channel_compress_stats_reset(DisplayChannel *display)
|
||||
{
|
||||
spice_return_if_fail(display);
|
||||
@ -388,3 +403,526 @@ bool display_channel_surface_has_canvas(DisplayChannel *display,
|
||||
{
|
||||
return display->surfaces[surface_id].context.canvas != NULL;
|
||||
}
|
||||
|
||||
static void streams_update_visible_region(DisplayChannel *display, Drawable *drawable)
|
||||
{
|
||||
Ring *ring;
|
||||
RingItem *item;
|
||||
RingItem *dcc_ring_item, *next;
|
||||
DisplayChannelClient *dcc;
|
||||
|
||||
if (!red_channel_is_connected(RED_CHANNEL(display))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_primary_surface(display, drawable->surface_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ring = &display->streams;
|
||||
item = ring_get_head(ring);
|
||||
|
||||
while (item) {
|
||||
Stream *stream = SPICE_CONTAINEROF(item, Stream, link);
|
||||
StreamAgent *agent;
|
||||
|
||||
item = ring_next(ring, item);
|
||||
|
||||
if (stream->current == drawable) {
|
||||
continue;
|
||||
}
|
||||
|
||||
FOREACH_DCC(display, dcc_ring_item, next, dcc) {
|
||||
agent = &dcc->stream_agents[get_stream_id(display, stream)];
|
||||
|
||||
if (region_intersects(&agent->vis_region, &drawable->tree_item.base.rgn)) {
|
||||
region_exclude(&agent->vis_region, &drawable->tree_item.base.rgn);
|
||||
region_exclude(&agent->clip, &drawable->tree_item.base.rgn);
|
||||
dcc_add_stream_agent_clip(dcc, agent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void current_add_drawable(DisplayChannel *display,
|
||||
Drawable *drawable, RingItem *pos)
|
||||
{
|
||||
RedSurface *surface;
|
||||
uint32_t surface_id = drawable->surface_id;
|
||||
|
||||
surface = &display->surfaces[surface_id];
|
||||
ring_add_after(&drawable->tree_item.base.siblings_link, pos);
|
||||
ring_add(&display->current_list, &drawable->list_link);
|
||||
ring_add(&surface->current_list, &drawable->surface_list_link);
|
||||
display->current_size++;
|
||||
drawable->refs++;
|
||||
}
|
||||
|
||||
static int current_add_equal(DisplayChannel *display, DrawItem *item, TreeItem *other)
|
||||
{
|
||||
DrawItem *other_draw_item;
|
||||
Drawable *drawable;
|
||||
Drawable *other_drawable;
|
||||
|
||||
if (other->type != TREE_ITEM_TYPE_DRAWABLE) {
|
||||
return FALSE;
|
||||
}
|
||||
other_draw_item = (DrawItem *)other;
|
||||
|
||||
if (item->shadow || other_draw_item->shadow || item->effect != other_draw_item->effect) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
drawable = SPICE_CONTAINEROF(item, Drawable, tree_item);
|
||||
other_drawable = SPICE_CONTAINEROF(other_draw_item, Drawable, tree_item);
|
||||
|
||||
if (item->effect == QXL_EFFECT_OPAQUE) {
|
||||
int add_after = !!other_drawable->stream &&
|
||||
is_drawable_independent_from_surfaces(drawable);
|
||||
stream_maintenance(display, drawable, other_drawable);
|
||||
current_add_drawable(display, drawable, &other->siblings_link);
|
||||
other_drawable->refs++;
|
||||
current_remove_drawable(display, other_drawable);
|
||||
if (add_after) {
|
||||
red_pipes_add_drawable_after(display, drawable, other_drawable);
|
||||
} else {
|
||||
red_pipes_add_drawable(display, drawable);
|
||||
}
|
||||
red_pipes_remove_drawable(other_drawable);
|
||||
display_channel_drawable_unref(display, other_drawable);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
switch (item->effect) {
|
||||
case QXL_EFFECT_REVERT_ON_DUP:
|
||||
if (is_same_drawable(drawable, other_drawable)) {
|
||||
|
||||
DisplayChannelClient *dcc;
|
||||
DrawablePipeItem *dpi;
|
||||
RingItem *worker_ring_item, *dpi_ring_item;
|
||||
|
||||
other_drawable->refs++;
|
||||
current_remove_drawable(display, other_drawable);
|
||||
|
||||
/* sending the drawable to clients that already received
|
||||
* (or will receive) other_drawable */
|
||||
worker_ring_item = ring_get_head(&RED_CHANNEL(display)->clients);
|
||||
dpi_ring_item = ring_get_head(&other_drawable->pipes);
|
||||
/* dpi contains a sublist of dcc's, ordered the same */
|
||||
while (worker_ring_item) {
|
||||
dcc = SPICE_CONTAINEROF(worker_ring_item, DisplayChannelClient,
|
||||
common.base.channel_link);
|
||||
dpi = SPICE_CONTAINEROF(dpi_ring_item, DrawablePipeItem, base);
|
||||
while (worker_ring_item && (!dpi || dcc != dpi->dcc)) {
|
||||
dcc_add_drawable(dcc, drawable);
|
||||
worker_ring_item = ring_next(&RED_CHANNEL(display)->clients,
|
||||
worker_ring_item);
|
||||
dcc = SPICE_CONTAINEROF(worker_ring_item, DisplayChannelClient,
|
||||
common.base.channel_link);
|
||||
}
|
||||
|
||||
if (dpi_ring_item) {
|
||||
dpi_ring_item = ring_next(&other_drawable->pipes, dpi_ring_item);
|
||||
}
|
||||
if (worker_ring_item) {
|
||||
worker_ring_item = ring_next(&RED_CHANNEL(display)->clients,
|
||||
worker_ring_item);
|
||||
}
|
||||
}
|
||||
/* not sending other_drawable where possible */
|
||||
red_pipes_remove_drawable(other_drawable);
|
||||
|
||||
display_channel_drawable_unref(display, other_drawable);
|
||||
return TRUE;
|
||||
}
|
||||
break;
|
||||
case QXL_EFFECT_OPAQUE_BRUSH:
|
||||
if (is_same_geometry(drawable, other_drawable)) {
|
||||
current_add_drawable(display, drawable, &other->siblings_link);
|
||||
red_pipes_remove_drawable(other_drawable);
|
||||
current_remove_drawable(display, other_drawable);
|
||||
red_pipes_add_drawable(display, drawable);
|
||||
return TRUE;
|
||||
}
|
||||
break;
|
||||
case QXL_EFFECT_NOP_ON_DUP:
|
||||
if (is_same_drawable(drawable, other_drawable)) {
|
||||
return TRUE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void __exclude_region(DisplayChannel *display, Ring *ring, TreeItem *item, QRegion *rgn,
|
||||
Ring **top_ring, Drawable *frame_candidate)
|
||||
{
|
||||
QRegion and_rgn;
|
||||
stat_start(display, start_time);
|
||||
|
||||
region_clone(&and_rgn, rgn);
|
||||
region_and(&and_rgn, &item->rgn);
|
||||
if (!region_is_empty(&and_rgn)) {
|
||||
if (IS_DRAW_ITEM(item)) {
|
||||
DrawItem *draw = (DrawItem *)item;
|
||||
|
||||
if (draw->effect == QXL_EFFECT_OPAQUE) {
|
||||
region_exclude(rgn, &and_rgn);
|
||||
}
|
||||
|
||||
if (draw->shadow) {
|
||||
Shadow *shadow;
|
||||
int32_t x = item->rgn.extents.x1;
|
||||
int32_t y = item->rgn.extents.y1;
|
||||
|
||||
region_exclude(&draw->base.rgn, &and_rgn);
|
||||
shadow = draw->shadow;
|
||||
region_offset(&and_rgn, shadow->base.rgn.extents.x1 - x,
|
||||
shadow->base.rgn.extents.y1 - y);
|
||||
region_exclude(&shadow->base.rgn, &and_rgn);
|
||||
region_and(&and_rgn, &shadow->on_hold);
|
||||
if (!region_is_empty(&and_rgn)) {
|
||||
region_exclude(&shadow->on_hold, &and_rgn);
|
||||
region_or(rgn, &and_rgn);
|
||||
// in flat representation of current, shadow is always his owner next
|
||||
if (!tree_item_contained_by((TreeItem*)shadow, *top_ring)) {
|
||||
*top_ring = tree_item_container_items((TreeItem*)shadow, ring);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (frame_candidate) {
|
||||
Drawable *drawable = SPICE_CONTAINEROF(draw, Drawable, tree_item);
|
||||
stream_maintenance(display, frame_candidate, drawable);
|
||||
}
|
||||
region_exclude(&draw->base.rgn, &and_rgn);
|
||||
}
|
||||
} else if (item->type == TREE_ITEM_TYPE_CONTAINER) {
|
||||
region_exclude(&item->rgn, &and_rgn);
|
||||
|
||||
if (region_is_empty(&item->rgn)) { //assume container removal will follow
|
||||
Shadow *shadow;
|
||||
|
||||
region_exclude(rgn, &and_rgn);
|
||||
if ((shadow = tree_item_find_shadow(item))) {
|
||||
region_or(rgn, &shadow->on_hold);
|
||||
if (!tree_item_contained_by((TreeItem*)shadow, *top_ring)) {
|
||||
*top_ring = tree_item_container_items((TreeItem*)shadow, ring);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Shadow *shadow;
|
||||
|
||||
spice_assert(item->type == TREE_ITEM_TYPE_SHADOW);
|
||||
shadow = (Shadow *)item;
|
||||
region_exclude(rgn, &and_rgn);
|
||||
region_or(&shadow->on_hold, &and_rgn);
|
||||
}
|
||||
}
|
||||
region_destroy(&and_rgn);
|
||||
stat_add(&display->__exclude_stat, start_time);
|
||||
}
|
||||
|
||||
static void exclude_region(DisplayChannel *display, Ring *ring, RingItem *ring_item,
|
||||
QRegion *rgn, TreeItem **last, Drawable *frame_candidate)
|
||||
{
|
||||
Ring *top_ring;
|
||||
stat_start(display, start_time);
|
||||
|
||||
if (!ring_item) {
|
||||
return;
|
||||
}
|
||||
|
||||
top_ring = ring;
|
||||
|
||||
for (;;) {
|
||||
TreeItem *now = SPICE_CONTAINEROF(ring_item, TreeItem, siblings_link);
|
||||
Container *container = now->container;
|
||||
|
||||
spice_assert(!region_is_empty(&now->rgn));
|
||||
|
||||
if (region_intersects(rgn, &now->rgn)) {
|
||||
__exclude_region(display, ring, now, rgn, &top_ring, frame_candidate);
|
||||
|
||||
if (region_is_empty(&now->rgn)) {
|
||||
spice_assert(now->type != TREE_ITEM_TYPE_SHADOW);
|
||||
ring_item = now->siblings_link.prev;
|
||||
current_remove(display, now);
|
||||
if (last && *last == now) {
|
||||
*last = (TreeItem *)ring_next(ring, ring_item);
|
||||
}
|
||||
} else if (now->type == TREE_ITEM_TYPE_CONTAINER) {
|
||||
Container *container = (Container *)now;
|
||||
if ((ring_item = ring_get_head(&container->items))) {
|
||||
ring = &container->items;
|
||||
spice_assert(((TreeItem *)ring_item)->container);
|
||||
continue;
|
||||
}
|
||||
ring_item = &now->siblings_link;
|
||||
}
|
||||
|
||||
if (region_is_empty(rgn)) {
|
||||
stat_add(&display->exclude_stat, start_time);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
while ((last && *last == (TreeItem *)ring_item) ||
|
||||
!(ring_item = ring_next(ring, ring_item))) {
|
||||
if (ring == top_ring) {
|
||||
stat_add(&display->exclude_stat, start_time);
|
||||
return;
|
||||
}
|
||||
ring_item = &container->base.siblings_link;
|
||||
container = container->base.container;
|
||||
ring = (container) ? &container->items : top_ring;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int current_add_with_shadow(DisplayChannel *display, Ring *ring, Drawable *item)
|
||||
{
|
||||
stat_start(display, start_time);
|
||||
#ifdef RED_WORKER_STAT
|
||||
++display->add_with_shadow_count;
|
||||
#endif
|
||||
|
||||
RedDrawable *red_drawable = item->red_drawable;
|
||||
SpicePoint delta = {
|
||||
.x = red_drawable->u.copy_bits.src_pos.x - red_drawable->bbox.left,
|
||||
.y = red_drawable->u.copy_bits.src_pos.y - red_drawable->bbox.top
|
||||
};
|
||||
|
||||
Shadow *shadow = shadow_new(&item->tree_item, &delta);
|
||||
if (!shadow) {
|
||||
stat_add(&display->add_stat, start_time);
|
||||
return FALSE;
|
||||
}
|
||||
// item and his shadow must initially be placed in the same container.
|
||||
// for now putting them on root.
|
||||
|
||||
// only primary surface streams are supported
|
||||
if (is_primary_surface(display, item->surface_id)) {
|
||||
detach_streams_behind(display, &shadow->base.rgn, NULL);
|
||||
}
|
||||
|
||||
ring_add(ring, &shadow->base.siblings_link);
|
||||
current_add_drawable(display, item, ring);
|
||||
if (item->tree_item.effect == QXL_EFFECT_OPAQUE) {
|
||||
QRegion exclude_rgn;
|
||||
region_clone(&exclude_rgn, &item->tree_item.base.rgn);
|
||||
exclude_region(display, ring, &shadow->base.siblings_link, &exclude_rgn, NULL, NULL);
|
||||
region_destroy(&exclude_rgn);
|
||||
streams_update_visible_region(display, item);
|
||||
} else {
|
||||
if (is_primary_surface(display, item->surface_id)) {
|
||||
detach_streams_behind(display, &item->tree_item.base.rgn, item);
|
||||
}
|
||||
}
|
||||
stat_add(&display->add_stat, start_time);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int current_add(DisplayChannel *display, Ring *ring, Drawable *drawable)
|
||||
{
|
||||
DrawItem *item = &drawable->tree_item;
|
||||
RingItem *now;
|
||||
QRegion exclude_rgn;
|
||||
RingItem *exclude_base = NULL;
|
||||
stat_start(display, start_time);
|
||||
|
||||
spice_assert(!region_is_empty(&item->base.rgn));
|
||||
region_init(&exclude_rgn);
|
||||
now = ring_next(ring, ring);
|
||||
|
||||
while (now) {
|
||||
TreeItem *sibling = SPICE_CONTAINEROF(now, TreeItem, siblings_link);
|
||||
int test_res;
|
||||
|
||||
if (!region_bounds_intersects(&item->base.rgn, &sibling->rgn)) {
|
||||
now = ring_next(ring, now);
|
||||
continue;
|
||||
}
|
||||
test_res = region_test(&item->base.rgn, &sibling->rgn, REGION_TEST_ALL);
|
||||
if (!(test_res & REGION_TEST_SHARED)) {
|
||||
now = ring_next(ring, now);
|
||||
continue;
|
||||
} else if (sibling->type != TREE_ITEM_TYPE_SHADOW) {
|
||||
if (!(test_res & REGION_TEST_RIGHT_EXCLUSIVE) &&
|
||||
!(test_res & REGION_TEST_LEFT_EXCLUSIVE) &&
|
||||
current_add_equal(display, item, sibling)) {
|
||||
stat_add(&display->add_stat, start_time);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!(test_res & REGION_TEST_RIGHT_EXCLUSIVE) && item->effect == QXL_EFFECT_OPAQUE) {
|
||||
Shadow *shadow;
|
||||
int skip = now == exclude_base;
|
||||
|
||||
if ((shadow = tree_item_find_shadow(sibling))) {
|
||||
if (exclude_base) {
|
||||
TreeItem *next = sibling;
|
||||
exclude_region(display, ring, exclude_base, &exclude_rgn, &next, NULL);
|
||||
if (next != sibling) {
|
||||
now = next ? &next->siblings_link : NULL;
|
||||
exclude_base = NULL;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
region_or(&exclude_rgn, &shadow->on_hold);
|
||||
}
|
||||
now = now->prev;
|
||||
current_remove(display, sibling);
|
||||
now = ring_next(ring, now);
|
||||
if (shadow || skip) {
|
||||
exclude_base = now;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(test_res & REGION_TEST_LEFT_EXCLUSIVE) && is_opaque_item(sibling)) {
|
||||
Container *container;
|
||||
|
||||
if (exclude_base) {
|
||||
exclude_region(display, ring, exclude_base, &exclude_rgn, NULL, NULL);
|
||||
region_clear(&exclude_rgn);
|
||||
exclude_base = NULL;
|
||||
}
|
||||
if (sibling->type == TREE_ITEM_TYPE_CONTAINER) {
|
||||
container = (Container *)sibling;
|
||||
ring = &container->items;
|
||||
item->base.container = container;
|
||||
now = ring_next(ring, ring);
|
||||
continue;
|
||||
}
|
||||
spice_assert(IS_DRAW_ITEM(sibling));
|
||||
if (!DRAW_ITEM(sibling)->container_root) {
|
||||
container = container_new(DRAW_ITEM(sibling));
|
||||
if (!container) {
|
||||
spice_warning("create new container failed");
|
||||
region_destroy(&exclude_rgn);
|
||||
return FALSE;
|
||||
}
|
||||
item->base.container = container;
|
||||
ring = &container->items;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!exclude_base) {
|
||||
exclude_base = now;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (item->effect == QXL_EFFECT_OPAQUE) {
|
||||
region_or(&exclude_rgn, &item->base.rgn);
|
||||
exclude_region(display, ring, exclude_base, &exclude_rgn, NULL, drawable);
|
||||
stream_trace_update(display, drawable);
|
||||
streams_update_visible_region(display, drawable);
|
||||
/*
|
||||
* Performing the insertion after exclude_region for
|
||||
* safety (todo: Not sure if exclude_region can affect the drawable
|
||||
* if it is added to the tree before calling exclude_region).
|
||||
*/
|
||||
current_add_drawable(display, drawable, ring);
|
||||
} else {
|
||||
/*
|
||||
* red_detach_streams_behind can affect the current tree since it may
|
||||
* trigger calls to update_area. Thus, the drawable should be added to the tree
|
||||
* before calling red_detach_streams_behind
|
||||
*/
|
||||
current_add_drawable(display, drawable, ring);
|
||||
if (is_primary_surface(display, drawable->surface_id)) {
|
||||
detach_streams_behind(display, &drawable->tree_item.base.rgn, drawable);
|
||||
}
|
||||
}
|
||||
region_destroy(&exclude_rgn);
|
||||
stat_add(&display->add_stat, start_time);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static bool drawable_can_stream(DisplayChannel *display, Drawable *drawable)
|
||||
{
|
||||
RedDrawable *red_drawable = drawable->red_drawable;
|
||||
SpiceImage *image;
|
||||
|
||||
if (display->stream_video == SPICE_STREAM_VIDEO_OFF) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!is_primary_surface(display, drawable->surface_id)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (drawable->tree_item.effect != QXL_EFFECT_OPAQUE ||
|
||||
red_drawable->type != QXL_DRAW_COPY ||
|
||||
red_drawable->u.copy.rop_descriptor != SPICE_ROPD_OP_PUT) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
image = red_drawable->u.copy.src_bitmap;
|
||||
if (image == NULL ||
|
||||
image->descriptor.type != SPICE_IMAGE_TYPE_BITMAP) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (display->stream_video == SPICE_STREAM_VIDEO_FILTER) {
|
||||
SpiceRect* rect;
|
||||
int size;
|
||||
|
||||
rect = &drawable->red_drawable->u.copy.src_area;
|
||||
size = (rect->right - rect->left) * (rect->bottom - rect->top);
|
||||
if (size < RED_STREAM_MIN_SIZE) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void display_channel_print_stats(DisplayChannel *display)
|
||||
{
|
||||
#ifdef RED_WORKER_STAT
|
||||
stat_time_t total = display->add_stat.total;
|
||||
spice_info("add with shadow count %u",
|
||||
display->add_with_shadow_count);
|
||||
display->add_with_shadow_count = 0;
|
||||
spice_info("add[%u] %f exclude[%u] %f __exclude[%u] %f",
|
||||
display->add_stat.count,
|
||||
stat_cpu_time_to_sec(total),
|
||||
display->exclude_stat.count,
|
||||
stat_cpu_time_to_sec(display->exclude_stat.total),
|
||||
display->__exclude_stat.count,
|
||||
stat_cpu_time_to_sec(display->__exclude_stat.total));
|
||||
spice_info("add %f%% exclude %f%% exclude2 %f%% __exclude %f%%",
|
||||
(double)(total - display->exclude_stat.total) / total * 100,
|
||||
(double)(display->exclude_stat.total) / total * 100,
|
||||
(double)(display->exclude_stat.total -
|
||||
display->__exclude_stat.total) / display->exclude_stat.total * 100,
|
||||
(double)(display->__exclude_stat.total) / display->exclude_stat.total * 100);
|
||||
stat_reset(&display->add_stat);
|
||||
stat_reset(&display->exclude_stat);
|
||||
stat_reset(&display->__exclude_stat);
|
||||
#endif
|
||||
}
|
||||
|
||||
int display_channel_add_drawable(DisplayChannel *display, Drawable *drawable)
|
||||
{
|
||||
int ret = FALSE, surface_id = drawable->surface_id;
|
||||
RedDrawable *red_drawable = drawable->red_drawable;
|
||||
Ring *ring = &display->surfaces[surface_id].current;
|
||||
|
||||
if (has_shadow(red_drawable)) {
|
||||
ret = current_add_with_shadow(display, ring, drawable);
|
||||
} else {
|
||||
drawable->streamable = drawable_can_stream(display, drawable);
|
||||
ret = current_add(display, ring, drawable);
|
||||
}
|
||||
|
||||
#ifdef RED_WORKER_STAT
|
||||
if ((++display->add_count % 100) == 0)
|
||||
display_channel_print_stats(display);
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -419,6 +419,8 @@ void display_channel_surface_unref (DisplayCha
|
||||
uint32_t surface_id);
|
||||
bool display_channel_surface_has_canvas (DisplayChannel *display,
|
||||
uint32_t surface_id);
|
||||
int display_channel_add_drawable (DisplayChannel *display,
|
||||
Drawable *drawable);
|
||||
|
||||
static inline int is_equal_path(SpicePath *path1, SpicePath *path2)
|
||||
{
|
||||
@ -535,4 +537,13 @@ static inline void region_add_clip_rects(QRegion *rgn, SpiceClipRects *data)
|
||||
}
|
||||
}
|
||||
|
||||
void red_pipes_add_drawable(DisplayChannel *display, Drawable *drawable);
|
||||
void current_remove_drawable(DisplayChannel *display, Drawable *item);
|
||||
void red_pipes_add_drawable_after(DisplayChannel *display,
|
||||
Drawable *drawable, Drawable *pos_after);
|
||||
void red_pipes_remove_drawable(Drawable *drawable);
|
||||
void dcc_add_drawable(DisplayChannelClient *dcc, Drawable *drawable);
|
||||
void current_remove(DisplayChannel *display, TreeItem *item);
|
||||
void detach_streams_behind(DisplayChannel *display, QRegion *region, Drawable *drawable);
|
||||
|
||||
#endif /* DISPLAY_CHANNEL_H_ */
|
||||
|
||||
@ -492,7 +492,7 @@ static int cursor_is_connected(RedWorker *worker)
|
||||
red_channel_is_connected(RED_CHANNEL(worker->cursor_channel));
|
||||
}
|
||||
|
||||
static void dcc_add_drawable(DisplayChannelClient *dcc, Drawable *drawable)
|
||||
void dcc_add_drawable(DisplayChannelClient *dcc, Drawable *drawable)
|
||||
{
|
||||
DrawablePipeItem *dpi;
|
||||
|
||||
@ -501,7 +501,7 @@ static void dcc_add_drawable(DisplayChannelClient *dcc, Drawable *drawable)
|
||||
red_channel_client_pipe_add(RED_CHANNEL_CLIENT(dcc), &dpi->dpi_pipe_item);
|
||||
}
|
||||
|
||||
static void red_pipes_add_drawable(DisplayChannel *display, Drawable *drawable)
|
||||
void red_pipes_add_drawable(DisplayChannel *display, Drawable *drawable)
|
||||
{
|
||||
DisplayChannelClient *dcc;
|
||||
RingItem *dcc_ring_item, *next;
|
||||
@ -524,8 +524,8 @@ static void dcc_add_drawable_to_tail(DisplayChannelClient *dcc, Drawable *drawab
|
||||
red_channel_client_pipe_add_tail(RED_CHANNEL_CLIENT(dcc), &dpi->dpi_pipe_item);
|
||||
}
|
||||
|
||||
static inline void red_pipes_add_drawable_after(DisplayChannel *display,
|
||||
Drawable *drawable, Drawable *pos_after)
|
||||
void red_pipes_add_drawable_after(DisplayChannel *display,
|
||||
Drawable *drawable, Drawable *pos_after)
|
||||
{
|
||||
DrawablePipeItem *dpi, *dpi_pos_after;
|
||||
RingItem *dpi_link, *dpi_next;
|
||||
@ -571,7 +571,7 @@ static inline PipeItem *red_pipe_get_tail(DisplayChannelClient *dcc)
|
||||
return (PipeItem*)ring_get_tail(&RED_CHANNEL_CLIENT(dcc)->pipe);
|
||||
}
|
||||
|
||||
static inline void red_pipes_remove_drawable(Drawable *drawable)
|
||||
void red_pipes_remove_drawable(Drawable *drawable)
|
||||
{
|
||||
DrawablePipeItem *dpi;
|
||||
RingItem *item, *next;
|
||||
@ -816,8 +816,9 @@ static void red_flush_source_surfaces(DisplayChannel *display, Drawable *drawabl
|
||||
}
|
||||
}
|
||||
|
||||
static inline void current_remove_drawable(DisplayChannel *display, Drawable *item)
|
||||
void current_remove_drawable(DisplayChannel *display, Drawable *item)
|
||||
{
|
||||
/* todo: move all to unref? */
|
||||
display_stream_trace_add_drawable(display, item);
|
||||
draw_item_remove_shadow(&item->tree_item);
|
||||
ring_remove(&item->tree_item.base.siblings_link);
|
||||
@ -827,13 +828,7 @@ static inline void current_remove_drawable(DisplayChannel *display, Drawable *it
|
||||
display->current_size--;
|
||||
}
|
||||
|
||||
static void remove_drawable(DisplayChannel *display, Drawable *drawable)
|
||||
{
|
||||
red_pipes_remove_drawable(drawable);
|
||||
current_remove_drawable(display, drawable);
|
||||
}
|
||||
|
||||
static inline void current_remove(DisplayChannel *display, TreeItem *item)
|
||||
void current_remove(DisplayChannel *display, TreeItem *item)
|
||||
{
|
||||
TreeItem *now = item;
|
||||
|
||||
@ -843,8 +838,10 @@ static inline void current_remove(DisplayChannel *display, TreeItem *item)
|
||||
RingItem *ring_item;
|
||||
|
||||
if (now->type == TREE_ITEM_TYPE_DRAWABLE) {
|
||||
Drawable *drawable = SPICE_CONTAINEROF(now, Drawable, tree_item);
|
||||
ring_item = now->siblings_link.prev;
|
||||
remove_drawable(display, SPICE_CONTAINEROF(now, Drawable, tree_item));
|
||||
red_pipes_remove_drawable(drawable);
|
||||
current_remove_drawable(display, drawable);
|
||||
} else {
|
||||
Container *container = (Container *)now;
|
||||
|
||||
@ -975,152 +972,6 @@ static void red_clear_surface_drawables_from_pipes(DisplayChannel *display,
|
||||
}
|
||||
}
|
||||
|
||||
static void __exclude_region(DisplayChannel *display, Ring *ring, TreeItem *item, QRegion *rgn,
|
||||
Ring **top_ring, Drawable *frame_candidate)
|
||||
{
|
||||
QRegion and_rgn;
|
||||
#ifdef RED_WORKER_STAT
|
||||
RedWorker *worker = COMMON_CHANNEL(display)->worker;
|
||||
stat_time_t start_time = stat_now(worker->clockid);
|
||||
#endif
|
||||
|
||||
region_clone(&and_rgn, rgn);
|
||||
region_and(&and_rgn, &item->rgn);
|
||||
if (!region_is_empty(&and_rgn)) {
|
||||
if (IS_DRAW_ITEM(item)) {
|
||||
DrawItem *draw = (DrawItem *)item;
|
||||
|
||||
if (draw->effect == QXL_EFFECT_OPAQUE) {
|
||||
region_exclude(rgn, &and_rgn);
|
||||
}
|
||||
|
||||
if (draw->shadow) {
|
||||
Shadow *shadow;
|
||||
int32_t x = item->rgn.extents.x1;
|
||||
int32_t y = item->rgn.extents.y1;
|
||||
|
||||
region_exclude(&draw->base.rgn, &and_rgn);
|
||||
shadow = draw->shadow;
|
||||
region_offset(&and_rgn, shadow->base.rgn.extents.x1 - x,
|
||||
shadow->base.rgn.extents.y1 - y);
|
||||
region_exclude(&shadow->base.rgn, &and_rgn);
|
||||
region_and(&and_rgn, &shadow->on_hold);
|
||||
if (!region_is_empty(&and_rgn)) {
|
||||
region_exclude(&shadow->on_hold, &and_rgn);
|
||||
region_or(rgn, &and_rgn);
|
||||
// in flat representation of current, shadow is always his owner next
|
||||
if (!tree_item_contained_by((TreeItem*)shadow, *top_ring)) {
|
||||
*top_ring = tree_item_container_items((TreeItem*)shadow, ring);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (frame_candidate) {
|
||||
Drawable *drawable = SPICE_CONTAINEROF(draw, Drawable, tree_item);
|
||||
stream_maintenance(display, frame_candidate, drawable);
|
||||
}
|
||||
region_exclude(&draw->base.rgn, &and_rgn);
|
||||
}
|
||||
} else if (item->type == TREE_ITEM_TYPE_CONTAINER) {
|
||||
region_exclude(&item->rgn, &and_rgn);
|
||||
|
||||
if (region_is_empty(&item->rgn)) { //assume container removal will follow
|
||||
Shadow *shadow;
|
||||
|
||||
region_exclude(rgn, &and_rgn);
|
||||
if ((shadow = tree_item_find_shadow(item))) {
|
||||
region_or(rgn, &shadow->on_hold);
|
||||
if (!tree_item_contained_by((TreeItem*)shadow, *top_ring)) {
|
||||
*top_ring = tree_item_container_items((TreeItem*)shadow, ring);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Shadow *shadow;
|
||||
|
||||
spice_assert(item->type == TREE_ITEM_TYPE_SHADOW);
|
||||
shadow = (Shadow *)item;
|
||||
region_exclude(rgn, &and_rgn);
|
||||
region_or(&shadow->on_hold, &and_rgn);
|
||||
}
|
||||
}
|
||||
region_destroy(&and_rgn);
|
||||
stat_add(&display->__exclude_stat, start_time);
|
||||
}
|
||||
|
||||
static void exclude_region(DisplayChannel *display, Ring *ring, RingItem *ring_item,
|
||||
QRegion *rgn, TreeItem **last, Drawable *frame_candidate)
|
||||
{
|
||||
#ifdef RED_WORKER_STAT
|
||||
RedWorker *worker = COMMON_CHANNEL(display)->worker;
|
||||
stat_time_t start_time = stat_now(worker->clockid);
|
||||
#endif
|
||||
Ring *top_ring;
|
||||
|
||||
if (!ring_item) {
|
||||
return;
|
||||
}
|
||||
|
||||
top_ring = ring;
|
||||
|
||||
for (;;) {
|
||||
TreeItem *now = SPICE_CONTAINEROF(ring_item, TreeItem, siblings_link);
|
||||
Container *container = now->container;
|
||||
|
||||
spice_assert(!region_is_empty(&now->rgn));
|
||||
|
||||
if (region_intersects(rgn, &now->rgn)) {
|
||||
__exclude_region(display, ring, now, rgn, &top_ring, frame_candidate);
|
||||
|
||||
if (region_is_empty(&now->rgn)) {
|
||||
spice_assert(now->type != TREE_ITEM_TYPE_SHADOW);
|
||||
ring_item = now->siblings_link.prev;
|
||||
current_remove(display, now);
|
||||
if (last && *last == now) {
|
||||
*last = (TreeItem *)ring_next(ring, ring_item);
|
||||
}
|
||||
} else if (now->type == TREE_ITEM_TYPE_CONTAINER) {
|
||||
Container *container = (Container *)now;
|
||||
if ((ring_item = ring_get_head(&container->items))) {
|
||||
ring = &container->items;
|
||||
spice_assert(((TreeItem *)ring_item)->container);
|
||||
continue;
|
||||
}
|
||||
ring_item = &now->siblings_link;
|
||||
}
|
||||
|
||||
if (region_is_empty(rgn)) {
|
||||
stat_add(&display->exclude_stat, start_time);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
while ((last && *last == (TreeItem *)ring_item) ||
|
||||
!(ring_item = ring_next(ring, ring_item))) {
|
||||
if (ring == top_ring) {
|
||||
stat_add(&display->exclude_stat, start_time);
|
||||
return;
|
||||
}
|
||||
ring_item = &container->base.siblings_link;
|
||||
container = container->base.container;
|
||||
ring = (container) ? &container->items : top_ring;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void current_add_drawable(DisplayChannel *display,
|
||||
Drawable *drawable, RingItem *pos)
|
||||
{
|
||||
RedSurface *surface;
|
||||
uint32_t surface_id = drawable->surface_id;
|
||||
|
||||
surface = &display->surfaces[surface_id];
|
||||
ring_add_after(&drawable->tree_item.base.siblings_link, pos);
|
||||
ring_add(&display->current_list, &drawable->list_link);
|
||||
ring_add(&surface->current_list, &drawable->surface_list_link);
|
||||
display->current_size++;
|
||||
drawable->refs++;
|
||||
}
|
||||
|
||||
void detach_stream(DisplayChannel *display, Stream *stream,
|
||||
int detach_sized)
|
||||
{
|
||||
@ -1243,7 +1094,7 @@ static void detach_stream_gracefully(DisplayChannel *display, Stream *stream,
|
||||
* involves sending an upgrade image to the client, this drawable won't be rendered
|
||||
* (see dcc_detach_stream_gracefully).
|
||||
*/
|
||||
static void detach_streams_behind(DisplayChannel *display, QRegion *region, Drawable *drawable)
|
||||
void detach_streams_behind(DisplayChannel *display, QRegion *region, Drawable *drawable)
|
||||
{
|
||||
Ring *ring = &display->streams;
|
||||
RingItem *item = ring_get_head(ring);
|
||||
@ -1276,46 +1127,6 @@ static void detach_streams_behind(DisplayChannel *display, QRegion *region, Draw
|
||||
}
|
||||
}
|
||||
|
||||
static void streams_update_visible_region(DisplayChannel *display, Drawable *drawable)
|
||||
{
|
||||
Ring *ring;
|
||||
RingItem *item;
|
||||
RingItem *dcc_ring_item, *next;
|
||||
DisplayChannelClient *dcc;
|
||||
|
||||
if (!red_channel_is_connected(RED_CHANNEL(display))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_primary_surface(display, drawable->surface_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ring = &display->streams;
|
||||
item = ring_get_head(ring);
|
||||
|
||||
while (item) {
|
||||
Stream *stream = SPICE_CONTAINEROF(item, Stream, link);
|
||||
StreamAgent *agent;
|
||||
|
||||
item = ring_next(ring, item);
|
||||
|
||||
if (stream->current == drawable) {
|
||||
continue;
|
||||
}
|
||||
|
||||
FOREACH_DCC(display, dcc_ring_item, next, dcc) {
|
||||
agent = &dcc->stream_agents[get_stream_id(display, stream)];
|
||||
|
||||
if (region_intersects(&agent->vis_region, &drawable->tree_item.base.rgn)) {
|
||||
region_exclude(&agent->vis_region, &drawable->tree_item.base.rgn);
|
||||
region_exclude(&agent->clip, &drawable->tree_item.base.rgn);
|
||||
dcc_add_stream_agent_clip(dcc, agent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void display_channel_streams_timeout(DisplayChannel *display)
|
||||
{
|
||||
Ring *ring = &display->streams;
|
||||
@ -1377,350 +1188,6 @@ static void dcc_destroy_stream_agents(DisplayChannelClient *dcc)
|
||||
}
|
||||
}
|
||||
|
||||
static inline int red_current_add_equal(DisplayChannel *display, DrawItem *item, TreeItem *other)
|
||||
{
|
||||
DrawItem *other_draw_item;
|
||||
Drawable *drawable;
|
||||
Drawable *other_drawable;
|
||||
|
||||
if (other->type != TREE_ITEM_TYPE_DRAWABLE) {
|
||||
return FALSE;
|
||||
}
|
||||
other_draw_item = (DrawItem *)other;
|
||||
|
||||
if (item->shadow || other_draw_item->shadow || item->effect != other_draw_item->effect) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
drawable = SPICE_CONTAINEROF(item, Drawable, tree_item);
|
||||
other_drawable = SPICE_CONTAINEROF(other_draw_item, Drawable, tree_item);
|
||||
|
||||
if (item->effect == QXL_EFFECT_OPAQUE) {
|
||||
int add_after = !!other_drawable->stream &&
|
||||
is_drawable_independent_from_surfaces(drawable);
|
||||
stream_maintenance(display, drawable, other_drawable);
|
||||
current_add_drawable(display, drawable, &other->siblings_link);
|
||||
other_drawable->refs++;
|
||||
current_remove_drawable(display, other_drawable);
|
||||
if (add_after) {
|
||||
red_pipes_add_drawable_after(display, drawable, other_drawable);
|
||||
} else {
|
||||
red_pipes_add_drawable(display, drawable);
|
||||
}
|
||||
red_pipes_remove_drawable(other_drawable);
|
||||
display_channel_drawable_unref(display, other_drawable);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
switch (item->effect) {
|
||||
case QXL_EFFECT_REVERT_ON_DUP:
|
||||
if (is_same_drawable(drawable, other_drawable)) {
|
||||
|
||||
DisplayChannelClient *dcc;
|
||||
DrawablePipeItem *dpi;
|
||||
RingItem *worker_ring_item, *dpi_ring_item;
|
||||
|
||||
other_drawable->refs++;
|
||||
current_remove_drawable(display, other_drawable);
|
||||
|
||||
/* sending the drawable to clients that already received
|
||||
* (or will receive) other_drawable */
|
||||
worker_ring_item = ring_get_head(&RED_CHANNEL(display)->clients);
|
||||
dpi_ring_item = ring_get_head(&other_drawable->pipes);
|
||||
/* dpi contains a sublist of dcc's, ordered the same */
|
||||
while (worker_ring_item) {
|
||||
dcc = SPICE_CONTAINEROF(worker_ring_item, DisplayChannelClient,
|
||||
common.base.channel_link);
|
||||
dpi = SPICE_CONTAINEROF(dpi_ring_item, DrawablePipeItem, base);
|
||||
while (worker_ring_item && (!dpi || dcc != dpi->dcc)) {
|
||||
dcc_add_drawable(dcc, drawable);
|
||||
worker_ring_item = ring_next(&RED_CHANNEL(display)->clients,
|
||||
worker_ring_item);
|
||||
dcc = SPICE_CONTAINEROF(worker_ring_item, DisplayChannelClient,
|
||||
common.base.channel_link);
|
||||
}
|
||||
|
||||
if (dpi_ring_item) {
|
||||
dpi_ring_item = ring_next(&other_drawable->pipes, dpi_ring_item);
|
||||
}
|
||||
if (worker_ring_item) {
|
||||
worker_ring_item = ring_next(&RED_CHANNEL(display)->clients,
|
||||
worker_ring_item);
|
||||
}
|
||||
}
|
||||
/* not sending other_drawable where possible */
|
||||
red_pipes_remove_drawable(other_drawable);
|
||||
|
||||
display_channel_drawable_unref(display, other_drawable);
|
||||
return TRUE;
|
||||
}
|
||||
break;
|
||||
case QXL_EFFECT_OPAQUE_BRUSH:
|
||||
if (is_same_geometry(drawable, other_drawable)) {
|
||||
current_add_drawable(display, drawable, &other->siblings_link);
|
||||
remove_drawable(display, other_drawable);
|
||||
red_pipes_add_drawable(display, drawable);
|
||||
return TRUE;
|
||||
}
|
||||
break;
|
||||
case QXL_EFFECT_NOP_ON_DUP:
|
||||
if (is_same_drawable(drawable, other_drawable)) {
|
||||
return TRUE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static int current_add(DisplayChannel *display, Ring *ring, Drawable *drawable)
|
||||
{
|
||||
DrawItem *item = &drawable->tree_item;
|
||||
#ifdef RED_WORKER_STAT
|
||||
RedWorker *worker = COMMON_CHANNEL(display)->worker;
|
||||
stat_time_t start_time = stat_now(worker->clockid);
|
||||
#endif
|
||||
RingItem *now;
|
||||
QRegion exclude_rgn;
|
||||
RingItem *exclude_base = NULL;
|
||||
|
||||
spice_assert(!region_is_empty(&item->base.rgn));
|
||||
region_init(&exclude_rgn);
|
||||
now = ring_next(ring, ring);
|
||||
|
||||
while (now) {
|
||||
TreeItem *sibling = SPICE_CONTAINEROF(now, TreeItem, siblings_link);
|
||||
int test_res;
|
||||
|
||||
if (!region_bounds_intersects(&item->base.rgn, &sibling->rgn)) {
|
||||
now = ring_next(ring, now);
|
||||
continue;
|
||||
}
|
||||
test_res = region_test(&item->base.rgn, &sibling->rgn, REGION_TEST_ALL);
|
||||
if (!(test_res & REGION_TEST_SHARED)) {
|
||||
now = ring_next(ring, now);
|
||||
continue;
|
||||
} else if (sibling->type != TREE_ITEM_TYPE_SHADOW) {
|
||||
if (!(test_res & REGION_TEST_RIGHT_EXCLUSIVE) &&
|
||||
!(test_res & REGION_TEST_LEFT_EXCLUSIVE) &&
|
||||
red_current_add_equal(display, item, sibling)) {
|
||||
stat_add(&display->add_stat, start_time);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!(test_res & REGION_TEST_RIGHT_EXCLUSIVE) && item->effect == QXL_EFFECT_OPAQUE) {
|
||||
Shadow *shadow;
|
||||
int skip = now == exclude_base;
|
||||
|
||||
if ((shadow = tree_item_find_shadow(sibling))) {
|
||||
if (exclude_base) {
|
||||
TreeItem *next = sibling;
|
||||
exclude_region(display, ring, exclude_base, &exclude_rgn, &next, NULL);
|
||||
if (next != sibling) {
|
||||
now = next ? &next->siblings_link : NULL;
|
||||
exclude_base = NULL;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
region_or(&exclude_rgn, &shadow->on_hold);
|
||||
}
|
||||
now = now->prev;
|
||||
current_remove(display, sibling);
|
||||
now = ring_next(ring, now);
|
||||
if (shadow || skip) {
|
||||
exclude_base = now;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(test_res & REGION_TEST_LEFT_EXCLUSIVE) && is_opaque_item(sibling)) {
|
||||
Container *container;
|
||||
|
||||
if (exclude_base) {
|
||||
exclude_region(display, ring, exclude_base, &exclude_rgn, NULL, NULL);
|
||||
region_clear(&exclude_rgn);
|
||||
exclude_base = NULL;
|
||||
}
|
||||
if (sibling->type == TREE_ITEM_TYPE_CONTAINER) {
|
||||
container = (Container *)sibling;
|
||||
ring = &container->items;
|
||||
item->base.container = container;
|
||||
now = ring_next(ring, ring);
|
||||
continue;
|
||||
}
|
||||
spice_assert(IS_DRAW_ITEM(sibling));
|
||||
if (!DRAW_ITEM(sibling)->container_root) {
|
||||
container = container_new(DRAW_ITEM(sibling));
|
||||
if (!container) {
|
||||
spice_warning("create new container failed");
|
||||
region_destroy(&exclude_rgn);
|
||||
return FALSE;
|
||||
}
|
||||
item->base.container = container;
|
||||
ring = &container->items;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!exclude_base) {
|
||||
exclude_base = now;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (item->effect == QXL_EFFECT_OPAQUE) {
|
||||
region_or(&exclude_rgn, &item->base.rgn);
|
||||
exclude_region(display, ring, exclude_base, &exclude_rgn, NULL, drawable);
|
||||
stream_trace_update(display, drawable);
|
||||
streams_update_visible_region(display, drawable);
|
||||
/*
|
||||
* Performing the insertion after exclude_region for
|
||||
* safety (todo: Not sure if exclude_region can affect the drawable
|
||||
* if it is added to the tree before calling exclude_region).
|
||||
*/
|
||||
current_add_drawable(display, drawable, ring);
|
||||
} else {
|
||||
/*
|
||||
* red_detach_streams_behind can affect the current tree since it may
|
||||
* trigger calls to update_area. Thus, the drawable should be added to the tree
|
||||
* before calling red_detach_streams_behind
|
||||
*/
|
||||
current_add_drawable(display, drawable, ring);
|
||||
if (is_primary_surface(display, drawable->surface_id)) {
|
||||
detach_streams_behind(display, &drawable->tree_item.base.rgn, drawable);
|
||||
}
|
||||
}
|
||||
region_destroy(&exclude_rgn);
|
||||
stat_add(&display->add_stat, start_time);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int current_add_with_shadow(DisplayChannel *display, Ring *ring, Drawable *item)
|
||||
{
|
||||
#ifdef RED_WORKER_STAT
|
||||
RedWorker *worker = COMMON_CHANNEL(display)->worker;
|
||||
stat_time_t start_time = stat_now(worker->clockid);
|
||||
++display->add_with_shadow_count;
|
||||
#endif
|
||||
|
||||
RedDrawable *red_drawable = item->red_drawable;
|
||||
SpicePoint delta = {
|
||||
.x = red_drawable->u.copy_bits.src_pos.x - red_drawable->bbox.left,
|
||||
.y = red_drawable->u.copy_bits.src_pos.y - red_drawable->bbox.top
|
||||
};
|
||||
|
||||
Shadow *shadow = shadow_new(&item->tree_item, &delta);
|
||||
if (!shadow) {
|
||||
stat_add(&display->add_stat, start_time);
|
||||
return FALSE;
|
||||
}
|
||||
// item and his shadow must initially be placed in the same container.
|
||||
// for now putting them on root.
|
||||
|
||||
// only primary surface streams are supported
|
||||
if (is_primary_surface(display, item->surface_id)) {
|
||||
detach_streams_behind(display, &shadow->base.rgn, NULL);
|
||||
}
|
||||
|
||||
ring_add(ring, &shadow->base.siblings_link);
|
||||
current_add_drawable(display, item, ring);
|
||||
if (item->tree_item.effect == QXL_EFFECT_OPAQUE) {
|
||||
QRegion exclude_rgn;
|
||||
region_clone(&exclude_rgn, &item->tree_item.base.rgn);
|
||||
exclude_region(display, ring, &shadow->base.siblings_link, &exclude_rgn, NULL, NULL);
|
||||
region_destroy(&exclude_rgn);
|
||||
streams_update_visible_region(display, item);
|
||||
} else {
|
||||
if (is_primary_surface(display, item->surface_id)) {
|
||||
detach_streams_behind(display, &item->tree_item.base.rgn, item);
|
||||
}
|
||||
}
|
||||
stat_add(&display->add_stat, start_time);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void drawable_update_streamable(DisplayChannel *display, Drawable *drawable)
|
||||
{
|
||||
RedDrawable *red_drawable = drawable->red_drawable;
|
||||
SpiceImage *image;
|
||||
|
||||
if (display->stream_video == SPICE_STREAM_VIDEO_OFF) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_primary_surface(display, drawable->surface_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (drawable->tree_item.effect != QXL_EFFECT_OPAQUE ||
|
||||
red_drawable->type != QXL_DRAW_COPY ||
|
||||
red_drawable->u.copy.rop_descriptor != SPICE_ROPD_OP_PUT) {
|
||||
return;
|
||||
}
|
||||
|
||||
image = red_drawable->u.copy.src_bitmap;
|
||||
if (image == NULL ||
|
||||
image->descriptor.type != SPICE_IMAGE_TYPE_BITMAP) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (display->stream_video == SPICE_STREAM_VIDEO_FILTER) {
|
||||
SpiceRect* rect;
|
||||
int size;
|
||||
|
||||
rect = &drawable->red_drawable->u.copy.src_area;
|
||||
size = (rect->right - rect->left) * (rect->bottom - rect->top);
|
||||
if (size < RED_STREAM_MIN_SIZE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
drawable->streamable = TRUE;
|
||||
}
|
||||
|
||||
void print_stats(DisplayChannel *display)
|
||||
{
|
||||
#ifdef RED_WORKER_STAT
|
||||
stat_time_t total = display->add_stat.total;
|
||||
spice_info("add with shadow count %u",
|
||||
display->add_with_shadow_count);
|
||||
display->add_with_shadow_count = 0;
|
||||
spice_info("add[%u] %f exclude[%u] %f __exclude[%u] %f",
|
||||
display->add_stat.count,
|
||||
stat_cpu_time_to_sec(total),
|
||||
display->exclude_stat.count,
|
||||
stat_cpu_time_to_sec(display->exclude_stat.total),
|
||||
display->__exclude_stat.count,
|
||||
stat_cpu_time_to_sec(display->__exclude_stat.total));
|
||||
spice_info("add %f%% exclude %f%% exclude2 %f%% __exclude %f%%",
|
||||
(double)(total - display->exclude_stat.total) / total * 100,
|
||||
(double)(display->exclude_stat.total) / total * 100,
|
||||
(double)(display->exclude_stat.total -
|
||||
display->__exclude_stat.total) / display->exclude_stat.total * 100,
|
||||
(double)(display->__exclude_stat.total) / display->exclude_stat.total * 100);
|
||||
stat_reset(&display->add_stat);
|
||||
stat_reset(&display->exclude_stat);
|
||||
stat_reset(&display->__exclude_stat);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int red_add_drawable(DisplayChannel *display, Drawable *drawable)
|
||||
{
|
||||
int ret = FALSE, surface_id = drawable->surface_id;
|
||||
RedDrawable *red_drawable = drawable->red_drawable;
|
||||
Ring *ring = &display->surfaces[surface_id].current;
|
||||
|
||||
if (has_shadow(red_drawable)) {
|
||||
ret = current_add_with_shadow(display, ring, drawable);
|
||||
} else {
|
||||
drawable_update_streamable(display, drawable);
|
||||
ret = current_add(display, ring, drawable);
|
||||
}
|
||||
|
||||
#ifdef RED_WORKER_STAT
|
||||
if ((++display->add_count % 100) == 0)
|
||||
print_stats(display);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void red_get_area(DisplayChannel *display, int surface_id, const SpiceRect *area,
|
||||
uint8_t *dest, int dest_stride, int update)
|
||||
{
|
||||
@ -2018,7 +1485,7 @@ static inline void red_process_draw(RedWorker *worker, RedDrawable *red_drawable
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (red_add_drawable(worker->display_channel, drawable)) {
|
||||
if (display_channel_add_drawable(worker->display_channel, drawable)) {
|
||||
red_pipes_add_drawable(worker->display_channel, drawable);
|
||||
}
|
||||
cleanup:
|
||||
@ -8952,3 +8419,10 @@ RedChannel* red_worker_get_display_channel(RedWorker *worker)
|
||||
|
||||
return RED_CHANNEL(worker->display_channel);
|
||||
}
|
||||
|
||||
clockid_t red_worker_get_clockid(RedWorker *worker)
|
||||
{
|
||||
spice_return_val_if_fail(worker, 0);
|
||||
|
||||
return worker->clockid;
|
||||
}
|
||||
|
||||
@ -108,6 +108,7 @@ bool red_worker_run(RedWorker *worker);
|
||||
QXLInstance* red_worker_get_qxl(RedWorker *worker);
|
||||
RedChannel* red_worker_get_cursor_channel(RedWorker *worker);
|
||||
RedChannel* red_worker_get_display_channel(RedWorker *worker);
|
||||
clockid_t red_worker_get_clockid(RedWorker *worker);
|
||||
|
||||
RedChannel *red_worker_new_channel(RedWorker *worker, int size,
|
||||
const char *name,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user