]> git.proxmox.com Git - mirror_novnc.git/commitdiff
Scroll render test and perf speedup.
authorJoel Martin <github@martintribe.org>
Fri, 6 Aug 2010 20:53:13 +0000 (15:53 -0500)
committerJoel Martin <github@martintribe.org>
Fri, 6 Aug 2010 21:43:24 +0000 (16:43 -0500)
Turns out when Windows is running in QEMU and a window scroll happens,
there are lots of little hextile rects sent. This is slow in noVNC.

- Some recording/playback improvement.
- Add test harness to drive playback of recordings.
- By pulling off the rect header in one chunk we get a 3X speedup in
  Chrome and a 20% speedup in firefox (specifically for the scroll
  test).
- Also, get rid of some noise from creating timers for handle_message.
  Check to make sure there isn't already a pending timer first.

include/rfb.js
tests/vnc_playback.html [new file with mode: 0644]
utils/wsproxy.py

index c20b587698fffb8e4c33a8f22d20c5da36647652..4f6fbc966c614e12aa2e5e2f3a4e8fb4a4bd33d0 100644 (file)
@@ -64,7 +64,8 @@ var that           = {},         // Public API interface
 
     ws             = null,  // Web Socket object
     canvas         = null,  // Canvas object
-    sendID         = null,  // Send Queue check timer
+    sendTimer      = null,  // Send Queue check timer
+    msgTimer       = null,  // queued handle_message timer
 
     // Receive and send queues
     RQ             = [],  // Receive Queue
@@ -341,9 +342,9 @@ updateState = function(state, statusMsg) {
     case 'loaded':
     case 'disconnected':
 
-        if (sendID) {
-            clearInterval(sendID);
-            sendID = null;
+        if (sendTimer) {
+            clearInterval(sendTimer);
+            sendTimer = null;
         }
 
         if (ws) {
@@ -471,9 +472,16 @@ function handle_message() {
     case 'normal':
         if (normal_msg() && RQ.length > 0) {
             // true means we can continue processing
-            Util.Debug("More data to process");
             // Give other events a chance to run
-            setTimeout(handle_message, 10);
+            if (msgTimer === null) {
+                Util.Debug("More data to process, creating timer");
+                msgTimer = setTimeout(function () {
+                            msgTimer = null;
+                            handle_message();
+                        }, 10);
+            } else {
+                Util.Debug("More data to process, existing timer");
+            }
         }
         break;
     default:
@@ -686,7 +694,7 @@ init_msg = function() {
         }
 
         if (! test_mode) {
-            sendID = setInterval(function() {
+            sendTimer = setInterval(function() {
                     // Send updates either at a rate of one update
                     // every 50ms, or whatever slower rate the network
                     // can handle.
@@ -948,7 +956,7 @@ normal_msg = function() {
 };
 
 framebufferUpdate = function() {
-    var now, fbu_rt_diff, last_bytes, last_rects, ret = true;
+    var now, hdr, fbu_rt_diff, last_bytes, last_rects, ret = true;
 
     if (FBU.rects === 0) {
         //Util.Debug("New FBU: RQ.slice(0,20): " + RQ.slice(0,20));
@@ -982,11 +990,14 @@ framebufferUpdate = function() {
                 return false;
             }
             /* New FramebufferUpdate */
-            FBU.x      = RQ.shift16();
-            FBU.y      = RQ.shift16();
-            FBU.width  = RQ.shift16();
-            FBU.height = RQ.shift16();
-            FBU.encoding = parseInt(RQ.shift32(), 10);
+
+            hdr = RQ.shiftBytes(12);
+            FBU.x      = (hdr[0] << 8) + hdr[1];
+            FBU.y      = (hdr[2] << 8) + hdr[3];
+            FBU.width  = (hdr[4] << 8) + hdr[5];
+            FBU.height = (hdr[6] << 8) + hdr[7];
+            FBU.encoding = parseInt((hdr[8] << 24) + (hdr[9] << 16) +
+                                    (hdr[10] << 8) +  hdr[11], 10);
             timing.h_bytes += 12;
 
             if (encNames[FBU.encoding]) {
diff --git a/tests/vnc_playback.html b/tests/vnc_playback.html
new file mode 100644 (file)
index 0000000..4c57941
--- /dev/null
@@ -0,0 +1,164 @@
+<html>
+    <head>
+        <title>VNC Test</title>
+        <link rel="stylesheet" href="include/plain.css">
+    </head>
+    <body>
+
+        Iterations: <input id='iterations' style='width:50' value="3">&nbsp;
+
+        <input id='startButton' type='button' value='Start' style='width:100px'
+            onclick="start();" disabled>&nbsp;
+
+        <br><br>
+
+        <div id="VNC_screen">
+            <div id="VNC_status_bar" class="VNC_status_bar" style="margin-top: 0px;">
+                <table border=0 width=100%><tr>
+                    <td><div id="VNC_status">Loading</div></td>
+                </tr></table>
+            </div>
+            <canvas id="VNC_canvas" width="640px" height="20px">
+                Canvas not supported.
+            </canvas>
+        </div>
+
+        <br>
+        Results:<br>
+        <textarea id="messages" style="font-size: 9;" cols=80 rows=25></textarea>
+    </body>
+
+    <!--
+    <script type='text/javascript' 
+        src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
+    -->
+
+    <script src="include/vnc.js"></script>
+
+    <script>
+        var rfb, fname, test_state, frame_idx, frame_length, iteration,
+            iterations, start_time, packetID, waitTimer;
+
+        function message(str) {
+            console.log(str);
+            cell = $('messages');
+            cell.innerHTML += str + "\n";
+            cell.scrollTop = cell.scrollHeight;
+        }
+
+        fname = (document.location.href.match(
+                 /data=([A-Za-z0-9\._\-]*)/) ||
+                 ['', ''])[1];
+
+        if (fname) {
+            message("Loading " + fname);
+            document.write('<script src="' + fname + '"><\/script>');
+        } else {
+            message("Must specify data=FOO in query string.");
+        }
+
+        // Override send_array
+        send_array = function (arr) {
+            // Stub out send_array
+        }
+
+        updateState = function (rfb, state, oldstate, msg) {
+            switch (state) {
+                case 'failed':
+                case 'fatal':
+                    message("noVNC sent '" + state + "' state during iteration " + iteration);
+                    test_state = 'failed';
+                    break;
+                case 'loaded': 
+                    $('startButton').disabled = false;
+                    break;
+            }
+            if (typeof msg !== 'undefined') {
+                $('VNC_status').innerHTML = msg;
+            }
+        }
+
+        function start () {
+            $('startButton').value = "Running";
+            $('startButton').disabled = true;
+            test_state = 'running';
+
+            iterations = $('iterations').value;
+            iteration = 0;
+            frame_length = VNC_frame_data.length;
+            total_time = 0;
+            start_time = (new Date()).getTime();
+
+            setTimeout(next_iteration, 1);
+        }
+
+        function next_iteration () {
+            var time, iter_time, end_time;
+
+            if (test_state !== 'running') { return; }
+
+            if (iteration !== 0) {
+                rfb.disconnect();
+            }
+            
+            iteration++;
+            if (iteration > iterations) {
+                // Finished with all iterations
+                var end_time = (new Date()).getTime();
+                total_time = end_time - start_time;
+
+                iter_time = parseInt(total_time / iterations, 10);
+                message(iterations + " iterations took " + total_time + "ms, " +
+                        iter_time + "ms per iteration");
+                rfb.get_canvas().stop();   // Shut-off event interception
+                $('startButton').disabled = false;
+                $('startButton').value = "Start";
+                return;
+            }
+
+            frame_idx = 0;
+            rfb.connect('test', 0, "bogus");
+
+            setTimeout(do_packet, 1);
+
+        }
+
+        function do_packet () {
+            var frame;
+            if (test_state !== 'running') { return; }
+
+            frame = VNC_frame_data[frame_idx];
+            while (frame.charAt(0) === "}") {
+                //message("Send frame " + frame_idx);
+                frame_idx ++;
+                frame = VNC_frame_data[frame_idx];
+                if (frame_idx >= frame_length) {
+                    break;
+                }
+            }
+
+
+            //message("Processing frame: " + frame_idx);
+            if (frame) {
+                rfb.recv_message({'data' : frame.slice(frame.indexOf('{', 1)+1)});
+                frame_idx++;
+            }
+
+            if (frame_idx >= frame_length) {
+                next_iteration();
+            } else {
+                setTimeout(do_packet, 1);
+            }
+        }
+
+        window.onload = function() {
+            if (fname) {
+                message("VNC_frame_data.length: " + VNC_frame_data.length);
+                rfb = RFB({'target': 'VNC_canvas',
+                        'updateState': updateState});
+                rfb.testMode(send_array);
+                rfb.init();
+            }
+        }
+    </script>
+</html>
index 845df2a00be5d88eec676410ab595e1a39c5a084..cf4c94a5d677963d0fde2f9cb0820b91441930fe 100755 (executable)
@@ -11,7 +11,7 @@ as taken from http://docs.python.org/dev/library/ssl.html#certificates
 
 '''
 
-import socket, optparse
+import socket, optparse, time
 from select import select
 from websocket import *
 
@@ -37,9 +37,11 @@ def do_proxy(client, target):
     cpartial = ""
     tqueue = []
     rlist = [client, target]
+    tstart = int(time.time()*1000)
 
     while True:
         wlist = []
+        tdelta = int(time.time()*1000) - tstart
         if tqueue: wlist.append(target)
         if cqueue: wlist.append(client)
         ins, outs, excepts = select(rlist, wlist, [], 1)
@@ -61,7 +63,7 @@ def do_proxy(client, target):
             if sent == len(dat):
                 traffic("<")
                 ##if rec: rec.write("Client send: %s ...\n" % repr(dat[0:80]))
-                if rec: rec.write("%s,\n" % repr(">" + dat[1:-1]))
+                if rec: rec.write("%s,\n" % repr("{%s{" % tdelta + dat[1:-1]))
             else:
                 cqueue.insert(0, dat[sent:])
                 traffic("<.")
@@ -87,7 +89,7 @@ def do_proxy(client, target):
                     traffic(str(buf.count('\xff')))
                 traffic("}")
                 ##if rec: rec.write("Client recv (%d): %s\n" % (len(buf), repr(buf)))
-                if rec: rec.write("%s,\n" % repr(buf[1:-1]))
+                if rec: rec.write("%s,\n" % (repr("}%s}" % tdelta + buf[1:-1])))
                 if cpartial:
                     tqueue.extend(decode(cpartial + buf))
                     cpartial = ""