Re: [whatwg] Counterproposal for canvas in workers
Hi Justin, I will do reach out to some developers to see if they think it's useful. As an experiment, I wrote a javascript polyfill that implements a rough version of my proposal. I took a box 2d example and ported it over. Since it draws to canvas under the hood, I didn't have to change any code to make it work. Original example: http://cabanier.github.io/CanvasWorker/test2.html Example that uses a worker: http://cabanier.github.io/CanvasWorker/test.html I added an js-driven animation to show how the original example is hogging the main thread. Unfortunately, on Firefox there's a lot of GC overhead so it's better to use Safari or Chrome. On Fri, Oct 18, 2013 at 11:30 AM, Justin Novosad wrote: > > > > On Fri, Oct 18, 2013 at 12:36 PM, Rik Cabanier 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 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 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
Re: [whatwg] Proposal: Adding methods like getElementById and getElementsByTagName to DocumentFragments
On 19 Oct 2013 at 18:27, Ms2ger wrote: > Quoting part of the original email you trimmed: > >> Luckily, we have SVGSVGElement.prototype.getElementById available to >> compare to Element.prototype.querySelector. > > That is, getElementById is available on |svg| elements in the SVG namespace. Yeah, thanks, I missed the significance of that, drat it. -- Cheers -- Tim
Re: [whatwg] Proposal: Adding methods like getElementById and getElementsByTagName to DocumentFragments
Hi Tim, On 10/19/2013 07:03 PM, Tim Streater wrote: On 18 Oct 2013 at 22:56, Boris Zbarsky posted, inter alia, this code: [1] The testcase: document.write("\n"); var node; var count = 20; function doTests(root, elementId, descQS, descQSNoConcat, descGEBI) { var start = new Date; for (var i = 0; i < count; ++i) node = root.querySelector("#" + elementId); var stop = new Date; document.writeln(descQS + ((stop - start) / count * 1e6)); var start = new Date; for (var i = 0; i < count; ++i) node = root.querySelector("#test"); var stop = new Date; document.writeln(descQSNoConcat + ((stop - start) / count * 1e6)); var start = new Date; for (var i = 0; i < count; ++i) node = root.getElementById(elementId); var stop = new Date; document.writeln(descGEBI + ((stop - start) / count * 1e6)); } var root = document.getElementById("root"); var start = new Date; for (var i = 0; i < count; ++i) node = document.getElementById("test"); var stop = new Date; document.writeln("document.getElementById: " + ((stop - start) / count * 1e6)); doTests(root, "test", "In-tree querySelector: ", "In-tree querySelector, no string concat: ", "In-tree getElementById: "); root.remove(); doTests(root, "test", "Out-of-tree querySelector: ", "Out-of-tree querySelector, no string concat: ", "Out-of-tree getElementById: "); I've tested this here on five browsers and it runs to completion Ok apart from iCab, which didn't like root.remove so I did that bit longhand. But I'm left confused. The other day I ranted about needing to use a document fragment and having to use querySelector on a table body. Now this code appears to imply that I need neither and could have used getElementById all along, since your application of getElementById above is mostly not to a document. I'm sure that when I tested that, a year or so back, it didn't work. Could you elucidate? Quoting part of the original email you trimmed: Luckily, we have SVGSVGElement.prototype.getElementById available to compare to Element.prototype.querySelector. That is, getElementById is available on |svg| elements in the SVG namespace. HTH Ms2ger
Re: [whatwg] Proposal: Adding methods like getElementById and getElementsByTagName to DocumentFragments
On 18 Oct 2013 at 22:56, Boris Zbarsky posted, inter alia, this code: > [1] The testcase: > > > > document.write("\n"); > > > var node; > var count = 20; > function doTests(root, elementId, descQS, descQSNoConcat, descGEBI) { > var start = new Date; > for (var i = 0; i < count; ++i) > node = root.querySelector("#" + elementId); > var stop = new Date; > document.writeln(descQS + ((stop - start) / count * 1e6)); > var start = new Date; > for (var i = 0; i < count; ++i) > node = root.querySelector("#test"); > var stop = new Date; > document.writeln(descQSNoConcat + ((stop - start) / count * 1e6)); > var start = new Date; > for (var i = 0; i < count; ++i) > node = root.getElementById(elementId); > var stop = new Date; > document.writeln(descGEBI + ((stop - start) / count * 1e6)); > } > var root = document.getElementById("root"); > var start = new Date; > for (var i = 0; i < count; ++i) > node = document.getElementById("test"); > var stop = new Date; > document.writeln("document.getElementById: " + ((stop - start) / > count * 1e6)); > doTests(root, "test", > "In-tree querySelector: ", > "In-tree querySelector, no string concat: ", > "In-tree getElementById: "); > root.remove(); > doTests(root, "test", > "Out-of-tree querySelector: ", > "Out-of-tree querySelector, no string concat: ", > "Out-of-tree getElementById: "); > I've tested this here on five browsers and it runs to completion Ok apart from iCab, which didn't like root.remove so I did that bit longhand. But I'm left confused. The other day I ranted about needing to use a document fragment and having to use querySelector on a table body. Now this code appears to imply that I need neither and could have used getElementById all along, since your application of getElementById above is mostly not to a document. I'm sure that when I tested that, a year or so back, it didn't work. Could you elucidate? Thanks, -- Cheers -- Tim
Re: [whatwg] Proposal: Adding methods like getElementById and getElementsByTagName to DocumentFragments
On 10/18/13 5:56 PM, Boris Zbarsky wrote: I used a fairly large subtree that needs walking (1000 elements) Er, I _meant_ to, but the testcase clearly only has 100 elements. The numbers with 1000 elements are: Chrome: document.getElementById: 50 In-tree querySelector: 210 In-tree querySelector, no string concat: 100 In-tree getElementById: 60 Out-of-tree querySelector: 23590 Out-of-tree querySelector, no string concat: 22870 Out-of-tree getElementById: 4450 Stock Firefox: document.getElementById: 60 In-tree querySelector: 140 In-tree querySelector, no string concat: 130 In-tree getElementById: 190 Out-of-tree querySelector: 8590 Out-of-tree querySelector, no string concat: 8560 Out-of-tree getElementById: 8620 Modified Firefox: document.getElementById: 60 In-tree querySelector: 130 In-tree querySelector, no string concat: 120 In-tree getElementById: 4270 Out-of-tree querySelector: 8320 Out-of-tree querySelector, no string concat: 8300 Out-of-tree getElementById: 4210 -Boris
Re: [whatwg] Canvas in workers
On Sat, Oct 19, 2013 at 1:28 AM, Glenn Maynard wrote: > I'd like to hear thoughts on the "context.attachToCanvas" approach. I > think it has important advantages over ImageBitmap: > > - ImageBitmap requires the user to call close(). If the user forgets, or > doesn't know, or misses it in some code paths, the problems caused aren't > obvious. Worse, they may only appear in some implementations and not > others, depending on GC strategies. attachToCanvas doesn't need cleanup in > the first place, which is a nicer solution--there's nothing for the user to > get wrong. > It's not clear to me how attachToCanvas works. An application like Google Maps wants to draw to multiple canvases from a worker and then render the updated canvas contents all at once, in synchrony with changes to the DOM made by the main thread. How would you do that with attachToCanvas? > - If you're rendering in a worker and the eventual target is in the main > thread, the worker needs to be careful to not start rendering again until > the main thread has assigned the ImageBitmap to where it wants it, and > called .close(). You'd need to send a message back to the worker going > "okay, you can continue now". Otherwise, you'd start rendering before a > buffer has been freed up for reuse, and end up creating more backbuffers > than you intended (which matters for large screens). This seems easy to > get wrong, and attachToCanvas doesn't have this problem. > Not if you use transferToImageBitmap. > - With ImageBitmap, you need to create a helper canvas, then each time you > render to a new target, you need to resize the canvas to match where it'll > eventually go, so the resulting ImageBitmap is the size of its > destination. (This may also need to be carefully optimized, so the > implementation doesn't actually resize the backing store every time its > size changes.) With attachToCanvas, you just size both canvases normally > once, and switch between them with a single function call. > I'm not sure how helpful this is. In the case of WebGL, the rendering context has resources that need to be sized the same as the destination color buffer, so they'll need to be resized anyway if you're actually using a single context to render to canvases of different sizes. My guess is that the advice will always be "don't do that". - attachToCanvas matches the way Canvas works today: you create a Canvas, > put it in the document (if it's for display), and render to it. For two > canvases, you'd just add a second Canvas, and toggle as needed. With > ImageBitmap, you have to restructure everything as soon as you want a > second canvas, since you'd want to have a single offscreen Canvas for > rendering, and to have elements in the document instead of canvases. > I believe these are minor changes, especially compared to moving drawing to a worker. Rob -- Jtehsauts tshaei dS,o n" Wohfy Mdaon yhoaus eanuttehrotraiitny eovni le atrhtohu gthot sf oirng iyvoeu rs ihnesa.r"t sS?o Whhei csha iids teoa stiheer :p atroa lsyazye,d 'mYaonu,r "sGients uapr,e tfaokreg iyvoeunr, 'm aotr atnod sgaoy ,h o'mGee.t" uTph eann dt hwea lmka'n? gBoutt uIp waanndt wyeonut thoo mken.o w * *