[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Public WebGL] For review: ANGLE_timer_query extension



I agree with Ben that this is better without callbacks for a couple of reasons:

1) Callbacks are less flexible because they are necessarily asynchronous. This means that it is impossible to get the results back before your content might be composited. In this case that might not be that useful or even possible on some browsers, but generally callback APIs make it hard to do something that might affect rendering.

2) Callbacks can happen whenever the browser feels like it, even if you don't want them to. This makes it really hard to tightly manage framerate in a performance critical app. You could schedule all of your work very carefully to take up just about all of the free time in a frame, but the browser can then call 1000 callbacks and skip the next frame. There's no way to know how many callbacks are waiting so that you can leave time for them. And if you're really performance critical, your callbacks can't actually do anything, they have to just add something to a queue so that you can do the work somewhere where you are properly tracking the time.

As an example, XHRs are actually pretty annoying. They allow you to either block the entire app or process the results whenever the browser feels like it in a callback. A polling API to say "hey, process one XHR right now if you have any waiting" would be awesome, as it would allow the developer to manage timing better.

I agree that callbacks are very _javascript_-y, but not everything in _javascript_ is well designed for high performance apps.

-Brian


On Wed, Apr 3, 2013 at 10:45 AM, Florian Bösch <pyalot@gmail.com> wrote:
I think it might make sense that if you do callbacks, that the callbacks are tied to requestAnimationFrame. You usually don't care to receive callbacks unless just before you actually would want to work with the captured data right? And usually the next chance you'll get to work with the data is in a requestAnimationFrame callback.


On Wed, Apr 3, 2013 at 7:18 PM, Gregg Tavares <gman@google.com> wrote:
There's another issue with the polling API. It exposes inconsistent behavior. 

Devs can decide not to check whether a query result is available and just read the result. For example they could assume if they ended a query last frame that reading the result next frame will be valid without checking that it actually is valid. This may work on some % of systems but fail on another % of systems. It might also fail on the same system depending on various random factors. 

Adding such inconsistent behavior into WebGL doesn't seem like a good idea.



        



On Wed, Apr 3, 2013 at 9:45 AM, Florian Bösch <pyalot@gmail.com> wrote:
Well, does the callback proposal actually improve things? Let's see.

var samples = new Float32Array(1024);
var sampleIdx = 0;

var sampleDone = function(timeElapsed){
  samples[sampleIdx] = timeElapsed;
  sampleIdx = (sampleIdx+1)%samples.length;
  ext.deleteQueryAngle(this);
}

var draw = function(){
  var query = var query = ext.createQueryANGLE();
  query.>

  ext.beginQueryANGLE(ext.TIME_ELAPSED_ANGLE, query);
  gl.drawElements(...);
  ext.endQueryANGLE(ext.TIME_ELAPSED_ANGLE);
}

I think that's nicer. Less mucking about with with keeping query objects in a container. Assuming of course that queries will become available in order of things actually completing, and not in some random order. So the browser will have to remember the sequency and not call back to it out of order. I'm assuming that wouldn't be a problem right?


On Wed, Apr 3, 2013 at 6:34 PM, Gregg Tavares <gman@google.com> wrote:
Florian,

This isn't about whether a good programmer like you can write correct code. It's about whether bad programmers will write code that browser vendors are stuck supporting.

The API proposed by ANGLE_timer_query is the same API used for occlusion queries. The only difference is a 3 emums. Bad devs will do this

   for each object () {
      drawOccluder();
      spinWaitForAnswer();
      if (!occluded()) {
           doHeavyDraw();
      }
   }

If that happens to run fast enough on some browser/driver combo they'll ship it. Then users will complain they can't interact with the browser because it's stuck waiting for _javascript_. This is the same reason why the browser vendors are sad there's a synchronous XHR function. It kills UX. They'd get rid of it if they could but MS made that mistake 15 years ago so were stuck with it. Let's not make the same mistake.







On Wed, Apr 3, 2013 at 2:55 AM, Florian Bösch <pyalot@gmail.com> wrote:
I don't agree that not having callbacks would lead to while(true){} use. But I might not fully understand the API. Here is what you would normally do with measured values:

- Compute an average sliding window
- Compute statistic measures such as variance and deviation

To do that a ring-buffer is usually used to store N previous sample points. It works about like this everytime you have a sample point.

buffer[i] = samplePoint;
i = (i+1)%buffer.length;

And then you compute/draw whatever from that buffer. A sliding average can be efficiently implemented this way by subtracting the old value from the average and adding the new value, like so:

sumAvg += samplePoint - buffer[i];
average = sumAvg/buffer.length;
buffer[i] = samplePoint;
i = (i+1)%buffer.length;

To do that with the query object API I'd imagine you'd do this:

var queries = [];
var samples = new Float32Array(1024);
var sampleIdx = 0;

var draw = function(){
  var query = var query = ext.createQueryANGLE();
  queries.push(query);
  ext.beginQueryANGLE(ext.TIME_ELAPSED_ANGLE, query);
  gl.drawElements(...);
  ext.endQueryANGLE(ext.TIME_ELAPSED_ANGLE);

  while(queries.length > 0){
    query = queries[0];
    if(ext.getQueryParameterANGLE(query, ext.QUERY_RESULT_AVAILABLE_ANGLE)){
      queries.shift();
      var timeElapsed = ext.getQueryParameterANGLE(query, ext.QUERY_RESULT_ANGLE);
      var samples[sampleIdx] = timeElapsed;
      sampleIdx = (sampleIdx+1)%samples.length;
    }
    else{
      break;
    }
  }
}

I think that would be fairly acceptable code. Do I miss something?