Some questions (particles, bugs in editor, etc)
krumza

Hi Gio! Sorry for my bad english!

at first i want to point on some errors in editor - 

a)  in object propertyes disappear option for rename Object - user can rename object only in the json editor

b) in some cases divs with layer properties not initializing jquery ui ( id = layer_property_...), jquery ui work only on most bottom div.

c) console in editor less representative than browser console at example in most case i see Script error. (,0:0) , but in browser console i can see at example that reason is not defined variable. But - it in fact impossible find where it variable - if we have a lot number of objects. I think  - this is be cool feature if we can see in object list object with error (if function in this is error)

d) please add some  self-dumb-defence in editor. sometimes user can write infinity function loop or some like monster-code-construction. At now - there is no defence for this situations and browser easy can use all pc resourses. Can you add some prelaunch wrapper for find this loop, exceptions and other

Second  - is question.

I plane create a shooter-platformer with destroyable world and big-boom effects with thousands particles, and i look how it make in my favorite wade.

But huge number of particles is slow PC  - that negative fluence on players.

To understand this  i create copy of pixi bunnytest (there is many versions - i use from goodboydigital.com) in wade, then compare wade and pixi versions/

i use  simple objects - 

var b = a.clone();
wade.addSceneObject(b);

with function:

b.onUpdate = function{
//same as pixi logic for pseudo phisics
//then i use this.setPosition() for calculated coordinates
}

in pixi my pc can show 120 000 bunyes without discrease fpc

in wade - 3 200 sprites  - is maximum with 60 fps, then fps is descrease

i compare performance in google - wade eat 99% of cpu when 3000  bunnyes on screen

pixy  - only 4% when 100000 bunnies

its because pixi have an particles container class that use some webgl methods 

Gio, what you think about this?

All 12 Comments
Gio

Hi krumza

A lot of questions! Let's try to go through them one by one.

a) There is a pencil icon next to the object name in the editor. You can click that to change the name. You can also just click the name itself (on the top-right corner of your screen).

b) Yes I've seen that, but I'm not exactly sure when it happens. I'll have a closer look and see if I can fix it.

c) I think that when the broser console is closed, a lot of the debugging information just isn't available. The editor tries to get a stack trace for the error that just happened, but sometimes it just isn't available. When the browser console is open, the editor console works better. This is not ideal of course. I like your suggestion, it would be a nice feature.

d) That's not easy to do. In fact, it's mathematically impossible to detect all infinite loops in a general case. Maybe there's something we can do for some of the more common cases though.

Regarding your particles, there are 2 aspects to consider: GPU and CPU.

On the GPU side, Wade is quite clever, so if you draw N sprites with the same default shader, it doesn't do N draw calls. Instead it batches those sprites into a 3D mesh with N quads, and does 1 draw call. Up to N=1024 I think, but anyway, it's pretty fast.

I think your main issue there is what you're doing on the CPU side. Having thousands of update functions is going to slow things down a lot.

It is goign to be much, much more efficient to have one update function that updates all your particles, for example via wade.setMainLoop.

It would be interesting to see a CPU profile capture once you've done that.

krumza

you mean that there is a fundamental difference between using update in an object or using it one function in global loop

I think that no difference (may be i wrong)  - what we use 

//a - is wade scene object declared before
a.onUpdate = function(){
//calculate
}

or this^

wade.setMainLoop(CalcAndUpdate);

var CalcAndUpdate = function(){
  
for(var i=0;i<arrays_of_a.lenght;i++){
//calculate a[i]
}
}

i dont see any difference - it both expensive in js loops in arrays 

krumza

ie I mean have seen many engines and all this time almost the same - here we have went update dt

and then we update all scene objects (use quadtree or not ) which stored in some array

maybe i do mistake say it but  - there is bottleneck for all html5 game engines - a lot number of objects kill every game

i do not deep dive into optimization but maybe exist an another way?

that's purely my bozo version.create a container for particles and exclude it from the main list of objects when the particle is included in the container.

update, create three typed arrays at the same time-for the x,y coordinate and the rotation angle of the object and the container update these typed arrays

ie separate coordinates from objects and pust it to typed arrays, then work with this arrays

Gio

Hey krumza

Firstly, calling a function is always going to be slower than not calling a function. So for example

var f = function(obj)
{
    obj._position.x += 10;
}

for (var i=0; i<1000000; i++)
{
   f(myObjects[i]);
}

is always going to be slower than

for (var i=0; i<1000000; i++)
{
    myObjects[i]._position.x += 10;
}

Don't take this too literally, it is possible that sometimes the compiler can optimize some function calls away in very simple cases. But in a general case, the second version is going to be much faster.

But there is more to it than that, as Wade doesn't just blindly loop through all the objects to update them. It first has to decide which objects need an update, and this process can take time if you have a crazy number of objects. It is also pointless if you already know that you need to update all of your objects, and the update itself is a simple operation - then preprocessing the list of objects is just a waste of CPU time.

For what you want to achieve, it should be enough to create one single SceneObject that contains lots of Sprites (one per particle). Ideally you should put these sprites on a layer where you disable the quad tree.

Finally, you are right that you could take this further by writing a specialized container for particles, and this is generally true not just for particles, but for any game where you have a very high number of objects that need updating. In these cases, you may want to sacrifice the convenience of Object Oriented Programming for performance. In other words, OOP is good as it makes it easy to understand what's going on and compartmentalize the game logic, but in terms of performance it's never the best option. Like you said above, a structure of arrays can be much faster than an array of structures. It makes it harder to deal with the update logic, but sometimes you have to do that if you want to have a very large number of entities.

However, I wouldn't worry about that just yet. I would first try creating a single scene object with lots of sprites and one update function to update all positions at once (also remembering to disable the quad tree on the particle layer). That may be fast enough already.

krumza

even if you remove functions from each object and make one General update function, the result will be insignificant

I redid the project and made 1 object adding sprites to it. disabled the quadrant tree, but still the performance gain was no more than 15%

the next step is to make several arrays of no more than 2000 sprites and, accordingly, several web workers for parallel calculation of these arrays.

But I think it would be 2000 * 16 sprites without losing frames per second.

After that I will move in the direction of Shared Memory and Atomics, but I can still see that the possibility of the processor in this is very limited

 

krumza

still, it is possible or not to see it in the implementation of wade. some interface based on SceneObject allows you to turn it into a container of particles?

and how to convey the logic of particles (mechanics of motion) in the webgl?

Gio

I can add it to the list of things to do, it would be nice to have a built-in solution for particles in Wade. But it won't be very high priority as it isn't very common that you need 100,000 objects of the same type. Most games need just a few dozen partciles to be on screen at the same time, and the current implementation is good enough for that. But like I said, I'll make a note to add it.

Having said that, if you have any performance problems the first thing to do should be to capture a profile and determine what part is slow. Is there something we can do when drawing the objects to speed things up? Or is the actual logic of updating the particle positions that is slow - in which case maybe there's a simple optimization that you can add to your code... I'd look at the profile data first, then try some targeted optimization.

If you want to share your profile captures, I'd be happy to have a look too.

krumza

hi again Gio!

amazing things!

if you create an editor - drawdown fps begins at the level of 4400 sprites, if you write code without editor then 22 thousand objects

the result is simply awesome

you can see test here

i say before that i try use webworkers to calculate particles - but how pass array of wade objects into webworker?

my aim  - turn all for(){} loops into webworker, I just want to get the most out of the CPU.

 

Gio

Oh yes that makes sense. The editor does slow things down, as it runs your code in debug mode to help you track down bugs using dev tools. It also has to do a lot of background work while your game is running in the preview window (it keeps copies of all your project assets in memory and transfers data to the preview window as needed). So it makes it easier to write code and debug, but it slows things down. When you export the project and run it without the editor, of course it's going to be much faster.

You can probably get further performance benefits by minifying your code. If CPU performance is an issue, then having minified code usually helps.

I am not sure that webworkers are the way to go with this - it takes time to transfer data to and from a web worker, because the data is essentially copied. If you want to try this approach, you should not try to pass entire SceneObjects to your webworkers. Instead, copy the bits that you need (position and velocity?) into a separate array, and pass that to the web worker. You can copy data from the SceneObjects into this array just once. Your webworker will be responsible for updating this array. After each update step, copy the data back into your SceneObjects.

I don't think this is going to be much faster (because of all the data copying that happens after every step). Ideally you would want to use a SharedArrayBuffer, but sadly this super-useful feature was removed some time ago from all major browsers over security concerns.

krumza

whether there is a reverse SceneObject.serialize function?

since we are talking about 1 object with many sprites, then I could make a SceneObject.serialize and pass this string to the worker, process there and then return to wade.app where to apply the results already.

and even better if you break this string / object into parts and pass it to different workers

Gio

You could use the SceneObject constructor as a reverse serialize function:

var serialized = mySceneObject.serialize();
var newSceneObject = new SceneObject(serialized);

But don't do this :)

If you call serialize(), then all the Sprite data will be serialized, including a lot of stuff that you don't need. For example, each sprite's image names, uv coordinates, draw modifiers, offsets, etc. In addition to doing unnecessary work, this will make your data structures unnecessarily big, increasing the data copying time.

I would just get the bits that you need in your webworker (position, velocity, maybe rotation and angular velocity), and keep the data as small as possible - data size is almost certainly going to be the bottleneck if you want to go with the webworker approach.

krumza

there is a new question - how efficient and fast emit huge array of particles?

i see as iteration -1 object with 10 sprites and settimeout 1 ms to clone that (multiply by 2 )

but maybe exist better way?

Post a reply
Add Attachment
Submit Reply
Login to Reply