Вопрос

I have a Node-Webkit based application, which does some heavy WebGL work (enough to stress out a high-end GPU), a fairly significant amount of JS processing, and I'm sending OSC data on the order of ~4kb/s with the node dgram module to an scsynth child process to control audio.

The OSC data is contained in one bundle every 10 frames of animation and the audio is fairly tolerant of some latency or irregularity in the messages it receives, but not on the scale I'm experiencing.

Unfortunately I find that significantly often, there is a large delay between calling socket.send(...) and the data actually being sent. It seems that under certain circumstances the scheduler places such a low priority on actually sending the data that each new packet becomes stalled almost indefinitely, before being suddenly released in large uncontrolled bursts that overflow the scsynth command queue.

I cannot put the udp code into a WebWorker as node.js objects do not work in that context. I'm considering attempting setting up a separate window (and as such, process) solely responsible for forwarding data received via window.postMessage to UDP (and vice-versa), but since postMessage itself is also asynchronous, and the other window itself will probably have low priority if it's not visible, I wonder if this is likely to provide much benefit.

I'm pretty sure that the main problem lies in the scheduling of work in Javascript rather than anywhere else in the process; I see no particular implication of trouble at the receiving end, although perhaps this could be assessed more closely.

Here's a brief snippet showing how the socket is setup and used (including gathering some basic stats on the send callback).

udp = require('dgram').createSocket('udp4');
//...
var udpStats = {lastSendDelay: 0, minSendDelay:Number.MAX_VALUE, maxSendDelay:-1, meanSendDelay:undefined};
var udpSend = function(buf) {
    var t = new Date();
    var wasSent = function(timeOfRequest) {
        return function(err) {
            if ((err)) sclog("UDP send Error: " + err);
            var t2 = new Date();
            var dt = t2 - timeOfRequest;
            udpStats.lastSendDelay = dt;
            udpStats.minSendDelay = Math.min(dt, udpStats.minSendDelay);
            udpStats.maxSendDelay = Math.max(dt, udpStats.maxSendDelay);
            udpStats.meanSendDelay = udpStats.meanSendDelay === undefined ? dt : (udpStats.meanSendDelay+dt)/2;
        };
    };

    udp.send(buf, 0, buf.length, UDP_PORT, 'localhost', wasSent(t));
};
Это было полезно?

Решение

In case it helps anybody else, we resolved this by replacing calls to requestAnimationFrame to calls with a timeout of 1ms and requestAnimationFrame. That permitted other things (such as udp send/receive processing) to squeeze in on the thread in the 1ms dealy, without significantly impacting overall fps.

~~~

p.s. Using nw 8.5 I found that deferring requestAnimationFrame with setTimeout worked best, giving typical OSC roundtrip times of 2ms. Deferring with process.setTick gave OSC roundtrip times of around 20ms. Calling requestAnimationFrame directly caused the almost complete block of OSC messages.

Using nw 12.1 there was no issue, and all three variants behaved almost exactly the same with typical roundtrip times of 2ms.

We have been constrained to use nw 8.5 for some time as our webGL code would not work with ANGLE and later versions of nw would not work with -use-gl=desktop. With the latest versions of nw, ANGLE is working better and -use-gl=desktop is also working again.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top