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

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

So I've been taking a look at getting something working. My first thought was doing a kind of auto sync. 

If you bind a resource and it has been modified by a different context since you last modified it I'd do a glFinish or glFlush/glWaitSync
If you try to use a resource and it's been modified by another context and you haven't bound it since then you'd get INVALID_OPERATION

I was hoping that would be the simplest way to make it work but unfortunately, to get that to work across workers requires too much locking. Every function that touches a shared resource has to lock the resource the entire time it's dealing with it.

So, that suggests the acquire/release method is probably a better way to go. Then locks are only needed during acquire and release. I'd still track modifications and generate INVALID_OPERATION if you haven't bound the resources in your context if has been modified by another context. Similarly I can auto insert the appropriate glFinish or glFlush/glWaitSync or similar synchronization.

Some questions come up about acquireResource though. 

How should it fail (gl ERROR? Exception?)
Should it fail at all, maybe it should be async as in gl.acquireResource(resource, flags, callback);

You then have the issue though of multiple callbacks, on multiple threads (workers and the main thread).

It's one of those things that seems like I need to implement it before I know if it sucks or not but I suppose we can write some pseudo code.

gl = canvas.getContext("webgl");

// make a texture to share.
texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, ....., width, height);

worker = new Worker("renderscene.js");

// Write a dispatch function for messages received from the worker.
worker. {
  switch (oEvent.data.cmd) {
    case 'initialized':
    case 'rendered':

// Tell the worker to startup. Give it our shared
// texture and the shareGroup.
  cmd: 'start',
  data: {
    shareGroup: gl.getContextAttributes().shareGrouo

// Ask the worker to render a frame.
function requestFrame() {
  worker.postMessage({cmd: 'render'});

// Called by the worker when a frame is ready.
function render()
   // Draw to canvas with texture that worker rendered to
   gl.acquireResource(texture, gl.READ_ONLY);

   // must bind to see the changes.
   gl.bindTexture(gl.TEXTURE_2D, texture); 

   gl.drawArrays(gl.TRIANGLES, 0, 4);


   // Request a new frame using RAF.


self. (oEvent) {
  switch (oEvent.data.cmd) {
    case 'start':
    case 'render':

function init(data) {
  gl = createWebGLContextInWorkerMagic({
     shareGroup: data.shareGroup;
  texture = data.texture;

  // Even though framebufferTexture2D does not modify or read
  // the texture to make sure it's not deleted when we try 
  // to attach it to or our FBO we need to acquire it.
  gl.acquireResource(texture, gl.READ_ONLY);

  // Make an FBO to use the texture.
  fbo = gl.createFramebuffer();
  gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
  gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.TEXTURE_2D, texture, 0);


  // Load all our scene data.

  self.postMessage({cmd: "initialized"});  

function render(data) {
  gl.acquireResource(texture, gl.WRITE);

  // This is not needed if we never write to this texture from
  // the main thread.
  gl.bindTexture(gl.TEXTURE_2D, texture);


  self.postMessage({cmd: "rendered"});  

Note: I'm not saying the code above is how you'd actually write this. At a minimum you'd probably want to double buffer 'texture' and figure out a more performant way of getting parallelization.

But, after writing this it seems like a non-async acquireResource that throws an exception if it can't acquire would be best? It would be easiest to use and you'd get an error immediately if you make a mistake.

One interesting question this brings up is when do you need to start using acquireResource/releaseResource. Meaning, right now, with 1 context you don't need to call it at all. It almost seems like we'd need a 'shareResource(resource)' function to mark those resources that are shared and therefore require calling acquireResource before being used vs those resources that don't. And if you don't call shareResource on an object you'd get an error if you tried to use it in another context.

Thoughts? I'm thinking out loud here.