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

Re: [Public WebGL] For review: ANGLE_timer_query extension



The difference between APIs like this and rAF is the frequency of use. There may be 100-1000 timers in a single frame, leading to 100-1000 callbacks. rAF is 1/frame. Apples and oranges different.

I can understand how most _javascript_ developers (and even browser implementers  are oblivious to the overhead imposed by the '_javascript_y' ways of doing things - most pages don't require (and shouldn't require) anyone to know. But when you're pushing the bounds of the platform you notice very fast and it's something that's often impossible to work around.

In the case of Chrome, a single callback from the browser into user _javascript_ can have anywhere from .05ms to .5ms of overhead -- not much if you've got a few a frame for things like network responses/etc - but if you've got 1000 coming back you've just blown your entire frame time. Not to mention the risk of additional allocations required to make the callbacks and the GC time this could impose.

We requested this extension to target specific use cases around high-performance applications. If the API is designed in such a way to prevent the use in high-performance applications, it's not going to be worth it. 

So I'm suggesting that you all step back from the callback cliff and instead look at what the problem is and how it can be solved without making the whole endeavor useless.


The way I see it, the query API would work like this in a browser such as Chrome where rendering occurs in another thread (though it can be done similarly for other implementations):

- user js runs init:
  - createQuery()
    - added to command buffer to send over to the gpu process
    - stashed in a 'query map' on the renderer side
- user js runs frame:
  - beginQuery()
  - drawElements()
  - endQuery()
    - commands added to buffer, sent to gpu process
  - queryCounter()
    - returns the value of the renderer-side query object immediately - no blocking
- gpu process:
   - run command buffer, see active timers, schedule them for processing
   - for each scheduled counter: query, if available then queue for sending back to renderer in a batch
- renderer message from gpu:
   - for each updated query:
     - find in query map, set value
- user js runs frame:
   - queryCounter()
     - returns the new value that was just set


There's no way to spin lock here, as the query results are never made available in the same tick that requested them nor will they ever change value inside of a tick. As I said before, any loop trying to test them will be reduced to a while(true); and not work anywhere. The spec can be revised to say that counter values cannot change within a js tick.

The difference here vs. user callbacks is that the application can schedule when the counters are queried and - more importantly - if they are queried at all. There's no more overhead spent doing more native-to-js transitions than the application wants, and it happens when the application allows it. This fact *must not be overlooked* - it's already hard enough to build a jank-free application in the browser.

For the use cases this API is designed for - which are those that we intend to use it for - this design is acceptable. It allows us to do things that would be impossible with callbacks, such as aggressively timing things but only reading the timers back if we decide post-hoc that the frame was slow.

So again, I propose keeping the API as is, with a spec note talking about when query values are allowed to change.
For those providing feedback who have never needed an API like this please understand that there are scenarios that exist that are beyond the trivial examples in the spec, and 'webifying' it for the sake of trying to match the convention used by other APIs with completely different usage and semantics is not doing anyone any favors.



On Wed, Apr 3, 2013 at 11:51 AM, Florian Bösch <pyalot@gmail.com> wrote:
Btw. can somebody present a good usecase for a deadspinner?


On Wed, Apr 3, 2013 at 8:40 PM, Florian Bösch <pyalot@gmail.com> wrote:
On Wed, Apr 3, 2013 at 8:18 PM, Brian Cornell <bcornell@google.com> wrote:
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.
Since the goal of the query API is to make it possible to get timing results without blocking (i.e. calling gl.finish()), I don't really see the utility in requiring an API semantic enabling blocking. If you want to block until something has finished, just call gl.finish(). If you want to time it, call performance.now(). The whole reason timer queries exist is because something is executed off in some process/driver in a rendering queue which you didn't want to block.
 
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.
A realistic usecase can't work with single timing values. You'd just flipflop between states every frame, and that wouldn't work. You will need to apply some averaging and statistics. To do that you will need to capture things in a buffer. Capturing things in a buffer, and then working with that buffer when you get a callback to requestAnimation frame, is exactly the same that you would do anyways. I don't see a difference of one preventing the other. I think either polling or callback properly handled amount to the exact identical same. One just seems a little less to write and less prone to abuse by beginners.

It can be said however that there is an assumption that you can do real work in event callbacks (also regarding clicks and so on). This is not really true, you should not do that. That's unfortunately another mistake, shared not just by beginners.
 
I agree that callbacks are very _javascript_-y, but not everything in _javascript_ is well designed for high performance apps.
requestAnimationFrame is a callback API and seems to work pretty fine.