From f28b4cfae5dbb6036d2369b71c6db5e6d4962624 Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Thu, 25 Dec 2014 16:45:26 +0000 Subject: [PATCH] #756: multi delta support: * 5 buckets by default (single bucket for older clients - as before) * added to xpra info * renamed "last_pixmap_data" to "delta_pixel_data" git-svn-id: https://xpra.org/svn/Xpra/trunk@8285 3bb7dfac-3a0b-4e04-842a-767bc560f471 --- src/xpra/client/gtk_base/gtk_client_base.py | 2 + src/xpra/client/window_backing_base.py | 17 ++++-- src/xpra/server/window_source.py | 66 +++++++++++++++------ 3 files changed, 61 insertions(+), 24 deletions(-) diff --git a/src/xpra/client/gtk_base/gtk_client_base.py b/src/xpra/client/gtk_base/gtk_client_base.py index bc9b7f2ab1..1dc035e79a 100644 --- a/src/xpra/client/gtk_base/gtk_client_base.py +++ b/src/xpra/client/gtk_base/gtk_client_base.py @@ -221,6 +221,8 @@ def make_hello(self): #window icon bits capabilities["encoding.icons.size"] = 64, 64 #size we want capabilities["encoding.icons.max_size"] = 128, 128 #limit + from xpra.client.window_backing_base import DELTA_BUCKETS + capabilities["encoding.delta_buckets"] = DELTA_BUCKETS return capabilities diff --git a/src/xpra/client/window_backing_base.py b/src/xpra/client/window_backing_base.py index f08f49459e..01ec632196 100644 --- a/src/xpra/client/window_backing_base.py +++ b/src/xpra/client/window_backing_base.py @@ -4,6 +4,7 @@ # Xpra is released under the terms of the GNU GPL v2, or, at your option, any # later version. See the file COPYING for details. +import os from xpra.log import Logger log = Logger("paint") @@ -18,6 +19,8 @@ from xpra.codecs.xor.cyxor import xor_str #@UnresolvedImport from xpra.codecs.argb.argb import unpremultiply_argb, unpremultiply_argb_in_place #@UnresolvedImport +DELTA_BUCKETS = int(os.environ.get("XPRA_DELTA_BUCKETS", "5")) + PIL = get_codec("PIL") #ie: @@ -73,7 +76,7 @@ def __init__(self, wid, window_alpha, idle_add): self.idle_add = idle_add self._alpha_enabled = window_alpha self._backing = None - self._last_pixmap_data = None + self._delta_pixel_data = [None for _ in xrange(DELTA_BUCKETS)] self._video_decoder = None self._csc_decoder = None self._decoder_lock = Lock() @@ -170,17 +173,19 @@ def process_delta(self, raw_data, width, height, rowstride, options): raise Exception("expected %s bytes for %sx%s with rowstride=%s but received %s (%s compressed)" % (rowstride * height, width, height, rowstride, len(img_data), len(raw_data))) delta = options.intget("delta", -1) + bucket = options.intget("bucket", 0) rgb_data = img_data if delta>=0: - if not self._last_pixmap_data: - raise Exception("delta region references pixmap data we do not have!") - lwidth, lheight, store, ldata = self._last_pixmap_data - assert width==lwidth and height==lheight and delta==store + assert bucket>=0 and bucket=0: - self._last_pixmap_data = width, height, store, rgb_data + self._delta_pixel_data[bucket] = width, height, store, rgb_data return rgb_data diff --git a/src/xpra/server/window_source.py b/src/xpra/server/window_source.py index dd4bec88a1..06732b56a0 100644 --- a/src/xpra/server/window_source.py +++ b/src/xpra/server/window_source.py @@ -26,7 +26,7 @@ MAX_PIXELS_PREFER_RGB = 4096 DELTA = os.environ.get("XPRA_DELTA", "1")=="1" -MAX_DELTA_SIZE = int(os.environ.get("XPRA_MAX_DELTA_SIZE", "10000")) +MAX_DELTA_SIZE = int(os.environ.get("XPRA_MAX_DELTA_SIZE", "32768")) HAS_ALPHA = os.environ.get("XPRA_ALPHA", "1")=="1" FORCE_BATCH = os.environ.get("XPRA_FORCE_BATCH", "0")=="1" STRICT_MODE = os.environ.get("XPRA_ENCODING_STRICT_MODE", "0")=="1" @@ -110,6 +110,9 @@ def __init__(self, queue_size, call_in_encode_thread, queue_packet, compressed_w self.supports_delta = [] if not window.is_tray(): self.supports_delta = [x for x in encoding_options.strlistget("supports_delta", []) if x in ("png", "rgb24", "rgb32")] + if self.supports_delta: + self.delta_buckets = encoding_options.intget("delta_buckets", 1) + self.delta_pixel_data = [None for _ in range(self.delta_buckets)] self.batch_config = batch_config #auto-refresh: self.auto_refresh_delay = auto_refresh_delay @@ -205,7 +208,8 @@ def init_vars(self): self.supports_transparency = False self.full_frames_only = False self.supports_delta = [] - self.last_pixmap_data = None + self.delta_buckets = 0 + self.delta_pixel_data = [] self.suspended = False self.strict = STRICT_MODE # @@ -220,7 +224,7 @@ def init_vars(self): self.soft_timer = None self.soft_expired = 0 self.max_soft_expired = 5 - self.min_delta_size = 512 + self.min_delta_size = 1024 self.max_delta_size = MAX_DELTA_SIZE self.is_OR = False self.is_tray = False @@ -287,7 +291,14 @@ def up(prefix, d): "last_used" : self.encoding_last_used or "", "full-frames-only" : self.full_frames_only, "supports-transparency" : self.supports_transparency, + "delta" : self.supports_delta, + "delta.buckets" : self.delta_buckets, }) + now = time.time() + for i,x in enumerate(self.delta_pixel_data): + if x: + w, h, coding, store, dpixels, last_used = x + info["encoding.delta.bucket[%s]" % i] = w, h, coding, store, len(dpixels), int((now-last_used)*1000) up("encoding", self.get_quality_speed_info()) try: #ie: get_strict_encoding -> "strict_encoding" @@ -490,7 +501,7 @@ def set_new_encoding(self, encoding, strict): if self.encoding==encoding: return self.statistics.reset() - self.last_pixmap_data = None + self.delta_pixel_data = [None for _ in range(self.delta_buckets)] self.update_encoding_selection(encoding) @@ -623,7 +634,7 @@ def cancel_damage(self): self.refresh_regions = [] self._damage_delayed = None self._damage_delayed_expired = False - self.last_pixmap_data = None + self.delta_pixel_data = [None for _ in range(self.delta_buckets)] #make sure we don't account for those as they will get dropped #(generally before encoding - only one may still get encoded): for sequence in self.statistics.encoding_pending.keys(): @@ -1383,7 +1394,7 @@ def damage_packet_acked(self, damage_packet_sequence, width, height, decode_time def client_decode_error(self, error): self.global_statistics.decode_errors += 1 #something failed client-side, so we can't rely on the delta being available - self.last_pixmap_data = None + self.delta_pixel_data = [None for _ in range(self.delta_buckets)] def make_data_packet(self, damage_time, process_damage_time, wid, image, coding, sequence, options): @@ -1422,22 +1433,25 @@ def make_data_packet(self, damage_time, process_damage_time, wid, image, coding, options["mmap_data"] = data #if client supports delta pre-compression for this encoding, use it if we can: - delta = -1 - store = -1 - isize = image.get_width() * image.get_height() - if DELTA and w>2 and h>2 and not (self._mmap and self._mmap_size>0) and (coding in self.supports_delta) and self.min_delta_size0) and self.delta_buckets>0 and (coding in self.supports_delta) and self.min_delta_size=0: client_options["delta"] = delta + client_options["bucket"] = bucket csize = len(data) if store>0: if delta>0 and csize>=psize/3: #compressed size is more than 33% of the original #maybe delta is not helping us, so clear it: - self.last_pixmap_data = None + self.delta_pixel_data[bucket] = None #TODO: could tell the clients they can clear it too #(add a new client capability and send it a zero store value) else: - self.last_pixmap_data = w, h, coding, store, dpixels + #find the bucket to use: + if bucket<0: + lpd = self.delta_pixel_data + try: + bucket = lpd.index(None) + except: + #find a bucket which has not been used recently + t = 0 + bucket = 0 + for i,dr in enumerate(lpd): + if dr and (t==0 or dr[-1]