Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# -*- coding: utf-8 -*- 

2# This file is part of Xpra. 

3# Copyright (C) 2011 Serviware (Arthur Huillet, <ahuillet@serviware.com>) 

4# Copyright (C) 2010-2019 Antoine Martin <antoine@xpra.org> 

5# Copyright (C) 2008 Nathaniel Smith <njs@pobox.com> 

6# Xpra is released under the terms of the GNU GPL v2, or, at your option, any 

7# later version. See the file COPYING for details. 

8 

9import os 

10 

11from collections import deque 

12from xpra.simple_stats import get_list_stats 

13from xpra.os_util import monotonic_time 

14from xpra.log import Logger 

15 

16#how many historical records to keep 

17#for the various statistics we collect: 

18#(cannot be lower than DamageBatchConfig.MAX_EVENTS) 

19NRECS = 100 

20 

21 

22log = Logger("damage") 

23 

24def ival(key, default, minv=0, maxv=None) -> int: 

25 try: 

26 v = os.environ.get("XPRA_BATCH_%s" % key) 

27 if v is None: 

28 return default 

29 iv = int(v) 

30 if minv is not None and iv<minv: 

31 log.warn("value for %s is too small: %s (minimum is %s)", key, iv, minv) 

32 return minv 

33 if maxv is not None and iv>maxv: 

34 log.warn("value for %s is too high: %s (maximum is %s)", key, iv, maxv) 

35 return maxv 

36 return iv 

37 except Exception as e: 

38 log.warn("failed to parse value '%s' for %s: %s", v, key, e) 

39 return default 

40 

41 

42class DamageBatchConfig: 

43 """ 

44 Encapsulate all the damage batching configuration into one object. 

45 """ 

46 ALWAYS = ival("ALWAYS", 0, 0, 1)==1 

47 MAX_EVENTS = ival("MAX_EVENTS", min(50, NRECS), 10) #maximum number of damage events 

48 MAX_PIXELS = ival("MAX_PIXELS", 1024*1024*MAX_EVENTS) #small screen at MAX_EVENTS frames 

49 TIME_UNIT = ival("TIME_UNIT", 1, 1, 1000) #per second 

50 MIN_DELAY = ival("MIN_DELAY", 5, 0, 1000) #if lower than 5 milliseconds: just don't batch 

51 START_DELAY = ival("START_DELAY", 50, 1, 1000) 

52 MAX_DELAY = ival("MAX_DELAY", 500, 1, 15000) 

53 EXPIRE_DELAY = ival("EXPIRE_DELAY", 50, 10, 1000) 

54 TIMEOUT_DELAY = ival("TIMEOUT_DELAY", 15000, 100, 100000) 

55 

56 def __init__(self): 

57 self.wid = 0 

58 self.always = self.ALWAYS 

59 self.max_events = self.MAX_EVENTS 

60 self.max_pixels = self.MAX_PIXELS 

61 self.time_unit = self.TIME_UNIT 

62 self.min_delay = self.MIN_DELAY 

63 self.max_delay = self.MAX_DELAY 

64 self.timeout_delay = self.TIMEOUT_DELAY 

65 self.expire_delay = self.EXPIRE_DELAY 

66 self.delay = self.START_DELAY 

67 self.delay_per_megapixel = -1 

68 self.saved = self.START_DELAY 

69 self.locked = False #to force a specific delay 

70 self.last_event = 0 

71 self.last_delays = deque(maxlen=64) #the delays we have tried to use (milliseconds) 

72 self.last_delay = None 

73 self.last_actual_delays = deque(maxlen=64) #the delays we actually used (milliseconds) 

74 self.last_actual_delay = None 

75 self.last_updated = 0 

76 #the metrics derived from statistics which we use for calculating the new batch delay: 

77 #(see batch delay calculator) 

78 self.factors = () 

79 

80 def cleanup(self): 

81 self.factors = () 

82 

83 def get_info(self) -> dict: 

84 info = { 

85 "min-delay" : self.min_delay, 

86 "max-delay" : self.max_delay, 

87 "expire" : self.expire_delay, 

88 "timeout-delay" : self.timeout_delay, 

89 "locked" : self.locked, 

90 } 

91 if self.delay_per_megapixel>=0: 

92 info["normalized"] = self.delay_per_megapixel 

93 if self.last_event>0: 

94 info["last-event"] = int(monotonic_time()-self.last_event) 

95 if self.locked: 

96 info["delay"] = self.delay 

97 else: 

98 ld = tuple(x[1] for x in self.last_delays) 

99 if ld: 

100 ls = get_list_stats(ld) 

101 ldv = self.last_delay 

102 if ldv: 

103 ls["last"] = ldv[1] 

104 info["delay"] = ls 

105 lad = tuple(x[1] for x in self.last_actual_delays) 

106 if lad: 

107 ls = get_list_stats(lad, show_percentile=(9,)) 

108 ladv = self.last_actual_delay 

109 if ladv: 

110 ls["last"] = ladv[1] 

111 info["actual_delays"] = ls 

112 for name, details, factor, weight in self.factors: 

113 fdetails = details.copy() 

114 fdetails[""] = int(100.0*factor), int(100.0*weight) 

115 info[name] = fdetails 

116 return info 

117 

118 

119 def clone(self): 

120 c = DamageBatchConfig() 

121 for x in ( 

122 "always", "max_events", "max_pixels", "time_unit", 

123 "min_delay", "max_delay", "timeout_delay", "delay", "expire_delay", 

124 ): 

125 setattr(c, x, getattr(self, x)) 

126 return c 

127 

128 def __repr__(self): 

129 return "DamageBatchConfig(%i)" % (self.wid)