Re: [whatwg] Counterproposal for canvas in workers

2013-10-19 Thread Rik Cabanier
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

2013-10-19 Thread Tim Streater
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

2013-10-19 Thread Ms2ger

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("");
   for (var i = 0; i < 100; ++i) {
 document.write("");
   }
   document.write("");
   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

2013-10-19 Thread Tim Streater
On 18 Oct 2013 at 22:56, Boris Zbarsky  posted, inter alia, 
this code: 

> [1] The testcase:
>
> 
> 
>   document.write("");
>   for (var i = 0; i < 100; ++i) {
> document.write("");
>   }
>   document.write("");
>   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

2013-10-19 Thread Boris Zbarsky

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

2013-10-19 Thread Robert O'Callahan
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  *
*