- From: Justin Novosad <junov@google.com>
- Date: Fri, 18 Oct 2013 14:30:17 -0400
- To: Rik Cabanier <cabanier@gmail.com>
- Cc: "whatwg@whatwg.org" <whatwg@whatwg.org>, Robert O'Callahan <robert@ocallahan.org>
On Fri, Oct 18, 2013 at 12:36 PM, Rik Cabanier <cabanier@gmail.com> wrote: > Hi Justin, > > no, everything is running synchronized and there is no added latency. > > API calls on canvas will be recorded if there are outstanding tasks. > So, for this API call: > > ctx.drawImage(MinionCanvas, ...) > > Since it is happening in a task, drawImage will be recorded. It will only > execute once the task and its subtasks (ie MinionCanvas.executeTask("drawMinion", > {}) ) are done. > > It sounds complicated but I think it's much easier for an author than > having to send bitmaps and message back and forth. > Ok, got it. I think this is compelling for many use cases, but I am not sure whether it is as generally useful/usable as WorkerCanvas. It would be great to get more web developer feedback. In particular from graphics intensive game and interactive app people. > On Fri, Oct 18, 2013 at 6:48 AM, Justin Novosad <junov@google.com> wrote: > >> Rik, I don't think the nested tasks in your example are a good use case. >> That workflow adds a frame of latency to the sub tasks. This is a problem >> because the drawImage call would be drawing from a source canvas that it >> out of phase with the mainscene context. To synchronize the content, I >> think the drawImage calls would have to be placed in a Promise resolution >> handler that gets invoked once all the executeTask Promises for the >> subtasks are resolved. That means that the drawImage calls necessary for >> drawing "mainscene" would end-up executing asynchronously, therefore >> outside the scope of the "mainscene" task function, which is a problem. So >> I don't think the executeTask proposal is well suited for sharding >> rendering jobs, at least not the way you illustrated it with that example. >> >> >> >> On Fri, Oct 18, 2013 at 12:50 AM, Rik Cabanier <cabanier@gmail.com>wrote: >> >>> Extra methods on the canvas API: >>> >>> Promise setTaskScript(DOMString script); // can error be in promise? >>> Promise executeTask(DOMString id, dictionary json, boolean synchronized >>> = true); // Transferable elements allowed in dictionary >>> >>> Object that is active in the task: >>> >>> interface CanvasTask { >>> >>> HTMLCanvasElement createCanvas(unsigned long width, unsigned long >>> height); >>> attribute Function onTask; >>> >>> } >>> >>> CanvasTask implements HTMLCanvasElement; >>> >>> Example code: >>> >>> var c = document.getElementById("gameCanvas"); >>> >>> var gameState = {}; >>> >>> window.addEventListener("load", function(){ >>> >>> c.setTaskScript("gameLogic.js").then(function(){ >>> >>> c.executeTask("mainscene", gameState); >>> >>> }); >>> >>> }); >>> >>> >>> window.requestAnimationFrame(function(){ >>> >>> c.executeTask("mainscene", gameState); >>> >>> } >>> >>> >>> Example code for gameLogic.js: >>> >>> var ctx = getContext("2d"); >>> >>> onTask = function(DOMString id, dictionary json) { >>> >>> if(id == "mainscene") { >>> >>> if(typeof(MinionCanvas)=="Undefined") { >>> >>> MinionCanvas = createCanvas(200, 300); >>> >>> MinionCanvas.executeTask("drawMinion", {}) // creates promise under >>> the hood >>> >>> } >>> >>> if(typeof(SpaceShipCanvas)=="Undefined") >>> >>> SpaceShipCanvas = createCanvas(300, 300); >>> >>> >>> SpaceShipCanvas.executeTask("drawSpaceShip", gameState); // redraw >>> spaceship >>> >>> >>> executeTask("drawBackDrop", gameState); // in other task >>> >>> executeTask("drawBoss", gameState); // lots of js to draw the boss so >>> better done in task >>> >>> >>> for(...) //for each minion { >>> >>> ... // set the matrix >>> ctx.drawImage(MinionCanvas, ...); // draw the minion <- note that the >>> minion might still be drawing in the other thread >>> >>> } >>> for(...) //for each spaceship { >>> >>> ..// set the matrix >>> >>> ctx.drawImage(SpaceShipCanvas); // draw the spaceship <- it might still >>> be drawing in the other task >>> >>> } >>> >>> .. // other drawing commands for score, controls, etc >>> >>> } else if(id == "drawMinion") { >>> >>> ... >>> >>> } else if(id == "drawSpaceShip") { >>> >>> ... // set up tasks to draw parts of the ship? >>> >>> } ... >>> >>> } >>> >>> >>> >>> On Thu, Oct 17, 2013 at 8:10 PM, Rik Cabanier <cabanier@gmail.com>wrote: >>> >>>> >>>> >>>> >>>> On Thu, Oct 17, 2013 at 4:01 PM, Robert O'Callahan < >>>> robert@ocallahan.org> wrote: >>>> >>>>> On Fri, Oct 18, 2013 at 10:56 AM, Justin Novosad <junov@google.com>wrote: >>>>> >>>>>> On Thu, Oct 17, 2013 at 5:50 PM, Rik Cabanier <cabanier@gmail.com>wrote: >>>>>> >>>>>>> Creating temporary canvases is still possible. I'm unsure how it >>>>>>> would be different from a worker. >>>>>>> An advantage would be that you can draw to the temporary canvases in >>>>>>> parallel to using them. Only PIXEL access is disallowed, you can still call >>>>>>> drawImage using a canvas that has outstanding tasks. >>>>>>> >>>>>> >>>>>> Right. The write-only restriction would only apply to canvas contexts >>>>>> that commit (push their tasks) directly down to the compositor. You could >>>>>> still create a canvas that is local to the worker, rasterize it in the >>>>>> worker and do readbacks in the worker, create ImageBitmaps from it, etc. >>>>>> >>>>> >>>>> I'm not sure that you and Rik are talking about the same thing, since >>>>> he's still talking about "outstanding tasks". If you are talking about the >>>>> same thing, I don't know what it is. I'd like to see some concrete details >>>>> for what you'd change in the current WorkerCanvas proposal. For the sake of >>>>> clarity I've put (my understand of) it here: >>>>> https://wiki.mozilla.org/User:Roc/WorkerCanvasProposal >>>>> >>>> >>>> I'll work on drawing up an example of my proposal. >>>> >>>> With WorkerCanvas and transferToImageBitmap, you can draw multiple >>>>> layers in parallel (and actually draw, not just queue drawing commands) by >>>>> creating multiple workers, having them each produce an ImageBitmap, and >>>>> compositing those ImageBitmaps together by stacking <img> elements or >>>>> drawing them all to a single canvas. It uses more memory but you get more >>>>> parallelism. >>>>> >>>> >>>> They would still have to wait for each other so the images are >>>> composited in-order. If you don't care about that, the 'synchronized' >>>> option would let you draw as soon as you exit the task (which is how Chrome >>>> always draws since it's faster) >>>> >>>> In fact, an implementation could choose to take the deferred-drawing >>>>> approach instead. You would queue up drawing commands in the WorkerCanvas >>>>> (or the drawing context), and then transferToImageBitmap would not >>>>> immediately render but produce an ImageBitmap implementation encapsulating >>>>> the list of drawing commands to be drawn later, wherever/whenever that >>>>> ImageBitmap ended up being used. I think for commit() the implementation >>>>> would always want to force rasterization on the worker (or possibly some >>>>> dedicated canvas-rendering thread); you could forward a list of drawing >>>>> commands to the compositor thread for rasterization but I don't think >>>>> there's any reason to do that (and some good reasons not to). >>>>> >>>> >>>> Can you tell me how you can ensure that you don't do too much work? >>>> Drawing in a continuous loop using 'Commit' would waste a lot of resources. >>>> >>> >>> >> >
Received on Friday, 18 October 2013 18:30:44 UTC