Code Monkey home page Code Monkey logo

offscreencanvasanimation's People

Contributors

fserb avatar guest271314 avatar junov avatar xlai-o avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

offscreencanvasanimation's Issues

await ctx.commit() is an illusion

In 5f969dd#diff-fe83e208ade4a4d8de4dc610010e1271R91, there is an example to illustrate how to set up a synchronously blocking main loop:

"Another possibility is to use the async/await syntax:"

async function animationLoop() {
  var promise;
  do {
    //draw stuff
    (...)
    promise = ctx.commit()
    // do post commit work
    (...)
  } while (await promise);
}

However this does not actually do what readers expect - and especially - it does not solve what @OleksandrChekhovskyi was asking for in https://discourse.wicg.io/t/offscreencanvas-animations-in-workers/1989/15. That is, the comment

"Okay, I think we've got it. Basically, the syntax for getting a commit that throttles by blocking (as Oleksadr suggests) would simply be "await context.commit();". And of course, this is much better than hard-blocking the entire thread."

unfortunately does not solve the use case for a synchronous main loop.

The subtle issue is that an async function is a function that returns a Promise, and calling await immediately returns from the calling function (chain of async functions) and continues execution from the first non-async function. This effect breaks the computation model that is required for WebAssembly/Emscripten-based applications that set up their own main loops.

Here is a more concrete example based on OffscreenCanvas prototypes, which attempts to set up a blocking main loop, but fails due to the subtlety:

webgl_modal_loop.html:

<html><body><canvas id='canvas'>
<script>
var htmlCanvas = document.getElementById("canvas");
var offscreen = htmlCanvas.transferControlToOffscreen();
var worker = new Worker("webgl_worker.js"); 
worker.postMessage({canvas: offscreen}, [offscreen]);
</script>
</body></html>

webgl_worker.js:

/*
// This is a simulation of a blocking GL.commit(): (can try as alternative to real GL.commit() if OffscreenCanvas not yet implemented)
function commit() {
  var displayRefreshRate = 1; // Simulate a 1Hz display for easy observing
  return new Promise(function (resolve, reject) {
    setTimeout(function() {
      resolve();
    }, 1000/displayRefreshRate);
  });
}
*/

var gl = null;

async function renderLoop() {
  var frameNumber = 0;
  // Start our modal rendering loop
  for(;;) {
    gl.clearColor(performance.now(), performance.now(), performance.now(), performance.now());
    gl.clear(gl.COLOR_BUFFER_BIT);
    await gl.commit();
//    await commit(); // Alternatively to try out simulated gl.commit() in the absence of real thing

    console.log('rendered frame ' + frameNumber++);
    if (frameNumber > 10) break; // Stop test after 10 frames to not flood the browser
  }
}

function init(evt) {
  console.log('init');
  // Startup initialization for the application
  var canvas = evt.data.canvas;
  gl = canvas.getContext("webgl");
}

function runGame() {
  console.log('runGame');
  renderLoop();
}

function deinit() {
  console.log('deinit');
  gl = null; // tear down
}

onmessage = function(evt) {
  // Game "main": init, run loop, and deinit
  init(evt);
  runGame();
  deinit();
};

The expectation from a synchronously blocking execution is that the above application should print out

init
runGame
rendered frame 0
rendered frame 1
rendered frame 2
rendered frame 3
rendered frame 4
rendered frame 5
rendered frame 6
rendered frame 7
rendered frame 8
rendered frame 9
rendered frame 10
deinit

but instead, running the page will print out

init
runGame
deinit
rendered frame 0
<throw since gl is null>

This is because the onmessage function will continue executing immediately after the the await gl.commit(); is called, and deinit() will be called, breaking down the synchronous programming model.

I am currently implementing OffscreenCanvas support to Emscripten, and trying to figure out how to implement vsync synchronization when a Worker is rendering via OffscreenCanvas. In the absence of a GL.commit(blockUntilVsyncFinished=true) type of API or similar help from the OffscreenCanvas spec, my current thinking is to set up a requestAnimationFrame loop in the main browser thread, and use that to ping "vsync finished" events to a Worker, via SharedArrayBuffer. This will work if OffscreenCanvas based rendering is guaranteed to still allow observing "proper" requestAnimationFrame synchronization on the main browser thread, though I am not sure if OffscreenCanvas currently says anything about this?

Understanding the multiple canvas solution

I'm hoping OffscreenCanvas allows one WebGL context to be used to efficiently update multiple canvases and it's not clear to me how that is solved in the current proposal.

Unrelated to the proposal two solutions come to mind.

  1. A callback for each canvas

    In this case when you get the callback the WebGLContext is setup to render to current canvas

  2. A way to manually bind a WebGLContext to any canvas.

Note that currently MDN lists code like this as the way to draw to multiple canvases

var one = document.getElementById("one").getContext("bitmaprenderer"); 
var two = document.getElementById("two").getContext("bitmaprenderer");

var offscreen = new OffscreenCanvas(256, 256);
var gl = offscreen.getContext('webgl');

// ... some drawing for the first canvas using the gl context ...

// Commit rendering to the first canvas
var bitmapOne = offscreen.transferToImageBitmap();
one.transferImageBitmap(bitmapOne);

// ... some more drawing for the second canvas using the gl context ...

// Commit rendering to the second canvas 
var bitmapTwo = offscreen.transferToImageBitmap();
two.transferImageBitmap(bitmapTwo);

But that seems likely to be super inefficient unless I'm missing something. In order to be able to draw to multiple canvases following the MDN style API you end up needed to set the size your rendering to. In other world you'd have to do this

offscreen.width = widthOfOne;       // EXPENSIVE
offscreen.height = heightOfOne;    // EXPENSIVE
renderSceneForOne();
var bitmapOne = offscreen.transferToImageBitmap();
one.transferImageBitmap(bitmapOne);

offscreen.width = widthOfTwo;       // EXPENSIVE
offscreen.height = heightOfTwo;    // EXPENSIVE
renderSceneForTwo();
var bitmapTwo = offscreen.transferToImageBitmap();
two.transferImageBitmap(bitmapTwo);

Where as if you can either bind to the canvas OR get callback then WebGL knows what canvas it's being asked to render to and if every canvas is double buffered there are no expensive operations.

Maybe there are other solutions? Or maybe the current proposal does this already?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.