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

Re: [Public WebGL] Issues with sharing resources across contexts



In previous systems I've worked with I've used implicit flushes when swapping contexts and it made life much easier. Every call in the graphics layer basically had this:
void MyGraphicsAPI::SomeCall() {
  MakeCurrent();
  ...
};
void MyGraphicsAPI::MakeCurrent() {
  if (threadCurrentContext != this) {
    Flush();
  }
  // ... set global threadCurrentContext, etc
};

Using some macro-fu it was made to be a single variable lookup and branch that predicted well per call, and in the common case of single-context apps had little impact on performance. In apps with multiple contexts it made life much easier when dealing with the sometimes long and hairy code sequences that touched both (rare, but often unavoidable). Had the flush not been implicit it would have required a significant amount of bookkeeping logic that every piece of code that touched a context would have to interact with - yuck. It also meant that code could be moved between apps that used single contexts and multiple contexts without having to change, or the app could even decide at runtime with no ill effect. Explicit flushes would have made that a nightmare.

The one downside of the implicit flushes is that it's easy to start flushing all over the place without knowing about it. A simple counter of flushes/frame was enough to help warn that they were happening though, as the target was usually <5 and if you mess things up you would see dozens. It's also often much trickier to find missing explicit flushes that cause subtle and sometimes very hard to identify behavioral differences. Just look at the number of WebGL pages out there today that emit warnings and imagine what it'd be like with this x_x


On Mon, Jul 16, 2012 at 5:18 PM, Gregg Tavares (社用) <gman@google.com> wrote:
So ........... I've been playing around with sharing resources across WebGL contexts and I'm running into issues and looking for solutions.

The biggest issue is that GL is command buffer driven and so calling some GL function doesn't mean it's actually been executed. You have to call glFlush. This raises lots of issues of where a program might work on some browser / driver / platform combo but not others if the users forgets to call flush.  

For example, assume I have 2 contexts sharing resources. gl1, and gl2


  var vs = gl1.createShader(gl1.VERTEX_SHADER);
  var fs = gl1.createShader(gl1.FRAGMENT_SHADER);
  //...
  // assume shaders are validly compiled
  // ...
  var p = gl1.createProgram();
  gl1.attachShader(p, vs);
  gl1.attachShader(p, fs);
  gl2.linkProgram(p);

I attached on gl1 but linked on gl2. There's is no guarantee in GL that that link will succeed because the 2 attach commands may have only been queued and not excuted in which case the linkProgram call will fail with "missing shaders". The correct code is

  var p = gl1.createProgram();
  gl1.attachShader(p, vs);
  gl1.attachShader(p, fs);
  gl1.flush(); // <--- important 
  gl2.linkProgram(p);

That seems unacceptable. 

2 Approaches off the of my head

1) Try to make it just work without the flush

One solution might be for the implementation to track which context last had a call. Any call to a different context causes an automatic implicit flush

2) Try to make it fail when not done correctly

This solution would be to try to track of an object is "dirty" (state has been changed) and no flush has been issued since then for that object. When an object is used, if it's dirty then either a flush is issued (which is solution #1) or a error is generated. ("called function on dirty object, did you forget to call flush?")

Neither solution seems ideal. Worse, whatever solution is chosen also has issues if we ever get WebGL in Workers.

A 3rd solution is just to leave it with the flush required and forgetting means you get random success / failure. No such a great prospect. :-(

Thoughts?