Welcome Guest! To enable all features please Login or Register.

Notification

Icon
Error

Options
Go to last post Go to first unread
Offline codesmith  
#1 Posted : Monday, April 29, 2013 5:11:18 PM(UTC)
codesmith

Joined: 4/26/2013(UTC)
Posts: 46

Thanks: 6 times
Was thanked: 1 time(s) in 1 post(s)
I just had quick performance tests with my Particle Engine using Delta Engine version of it and direct XNA version of it. DE version was the one that is available on Github, it's pretty much the same code except in DE version each particle is derived from Sprite, each sprite is added to Renderer.

DE version used was Windows.OpenGL.

In XNA version each particle is a class owning a Texture2D and drawn using SpriteBatch.

Both tests used a single ParticleEffect with one ParticleEmitter. Also both tests used three runtime ParticleModifiers, so the test was pretty similar.

XNA version runs over 30fps with 10000 particles and 70fps with 5000 active particles that also are drawn.
DE version runs 11fps with 3300 active particles that are drawn by Renderer.

I have screenshots that I can provide too if you want to see it.

No, please don't understand me wrong. I don't mean to mock DE, I am probably doing something wrong in the DE port of the particle engine.

I guess using Sprites for particle engine is not the most optimal way. I would perhaps need to render stuff differently?

Any hints?
-erno
Codesmith - Erno Pakarinen
Check my development blog @ blog.codesmith.fi

Wanna join the discussion?! Login to your forum accountregister a new account. Or Connect via Facebook Twitter Google

Offline codesmith  
#2 Posted : Monday, April 29, 2013 8:36:15 PM(UTC)
codesmith

Joined: 4/26/2013(UTC)
Posts: 46

Thanks: 6 times
Was thanked: 1 time(s) in 1 post(s)
Here are two screenshots. Effect looks a bit different, but both have similar properties:

XNA
Delta Engine

-erno

Edited by user Monday, April 29, 2013 8:37:26 PM(UTC)  | Reason: Not specified

Codesmith - Erno Pakarinen
Check my development blog @ blog.codesmith.fi
Offline Benjamin  
#3 Posted : Monday, April 29, 2013 10:01:22 PM(UTC)
Benjamin

Medals: Admin

Joined: 8/20/2011(UTC)
Posts: 1,421
Location: Hannover

Thanks: 18 times
Was thanked: 97 time(s) in 92 post(s)
Originally Posted by: codesmith Go to Quoted Post
I just had quick performance tests with my Particle Engine using Delta Engine version of it and direct XNA version of it. DE version was the one that is available on Github, it's pretty much the same code except in DE version each particle is derived from Sprite, each sprite is added to Renderer.

DE version used was Windows.OpenGL.

In XNA version each particle is a class owning a Texture2D and drawn using SpriteBatch.

Both tests used a single ParticleEffect with one ParticleEmitter. Also both tests used three runtime ParticleModifiers, so the test was pretty similar.

XNA version runs over 30fps with 10000 particles and 70fps with 5000 active particles that also are drawn.
DE version runs 11fps with 3300 active particles that are drawn by Renderer.

I have screenshots that I can provide too if you want to see it.

No, please don't understand me wrong. I don't mean to mock DE, I am probably doing something wrong in the DE port of the particle engine.

I guess using Sprites for particle engine is not the most optimal way. I would perhaps need to render stuff differently?

Any hints?
-erno


Very easy explanation, we never had the need to render that many sprites in a frame. For a particle system it also makes no sense to use full fletched sprites anyway (neither in XNA or DE or any other engine) because of all the overhead handling these classes. It is better to just have a flat data structure to render things out (e.g. an array of points, colors, etc.)

In your case it is probably very easy to optimize the 3300 active particles by just not setting the texture, vertex buffer, index buffer, etc. every single time for every single one of them ^^ At some point you will get GPU bound, but with nowadays hardware probably not with this few polygons (with a good particle system and some clever optimizations millions of particles can be handled each frame on very modern hardware, but that is not our goal right now).

The new entity system is actually pretty close to a data driven architecture, while coding for it is still very object and component driven. We will try to archive good enough performance for debugging, editor use and testing and then you can use the build service to flatten out your code (and making it really ugly) to run as fast as possible on each target hardware. But that is a discussion for another day.

Thanks to your contest submission we have now some code we can look at and try to port to our entity system. We have several other particle systems from the past and some employees have their own stuff from the last Game Jam.
Offline codesmith  
#4 Posted : Monday, April 29, 2013 10:33:10 PM(UTC)
codesmith

Joined: 4/26/2013(UTC)
Posts: 46

Thanks: 6 times
Was thanked: 1 time(s) in 1 post(s)
Originally Posted by: Benjamin Nitschke Go to Quoted Post
Very easy explanation, we never had the need to render that many sprites in a frame. For a particle system it also makes no sense to use full fletched sprites anyway (neither in XNA or DE or any other engine) because of all the overhead handling these classes. It is better to just have a flat data structure to render things out (e.g. an array of points, colors, etc.)


In XNA I use classes that contain Texture2Ds but the question is how do I accomplish this in Delta Engine? I used Sprites because it was the most obvious way to render graphics easily with rotation, scale, position and such. My current assumption is that I do not know yet so much of DE and that's why it's slow :) In XNA I'm drawing Texture2Ds and its much faster than with DE currently, same code except the difference between Particle in DE version and XNA version.

What is fastest way to draw a 2D image in DE with rotation, scaling, opacity and such to a specified position?

Btw, particle systems can make a lot of stuff to draw :) 10k is quite much for a game, especially for a mobile game. But this was just a performance test after all. :)

Originally Posted by: Benjamin Nitschke Go to Quoted Post
In your case it is probably very easy to optimize the 3300 active particles by just not setting the texture, vertex buffer, index buffer, etc. every single time for every single one of them ^^

In this version I create a new sprite every time when I need to make a new particle (unless the Cache/pool is enabled in particlesystem). XNA version painlessly runs over 30fps with 10k particles, with garbage collection and when a new particle is allocated whenever needed (whithout caching I mean):

Originally Posted by: Benjamin Nitschke Go to Quoted Post
Thanks to your contest submission we have now some code we can look at and try to port to our entity system. We have several other particle systems from the past and some employees have their own stuff from the last Game Jam.


You're welcome. I am sure my implementation is flawed and I need to fix it somehow to get better framerates. So I'm all ears for all hints :)
-erno

Edited by user Monday, April 29, 2013 10:34:40 PM(UTC)  | Reason: Not specified

Codesmith - Erno Pakarinen
Check my development blog @ blog.codesmith.fi
Offline Benjamin  
#5 Posted : Monday, April 29, 2013 11:35:25 PM(UTC)
Benjamin

Medals: Admin

Joined: 8/20/2011(UTC)
Posts: 1,421
Location: Hannover

Thanks: 18 times
Was thanked: 97 time(s) in 92 post(s)
Well, you are using classes of the DeltaEngine.Rendering assembly, which is absolutely fine and we can provide the best kind of optimizations here even if the code might not run as fast as it could right now.

If you want to be in total control you should however work with classes from DeltaEngine.Graphics, which don't provide you with any fancy features, they just provide raw drawing operations. For example you can load an image and then create some vertices and give them to the Drawing class to render, this will always be the fastest route. However it also the most amount of work. For a particle system or any other complex rendering system this makes sense and is worth the effort, for a simple 2D game you will much rather want to work with higher level classes that handle everything for you. Over time the higher level classes will also get faster, but we just have not spend any time yet optimizing them because there was just no need for it (goes against our instincts, but that is what TDD and agile tells us to do ^^).
Offline codesmith  
#6 Posted : Monday, April 29, 2013 11:45:12 PM(UTC)
codesmith

Joined: 4/26/2013(UTC)
Posts: 46

Thanks: 6 times
Was thanked: 1 time(s) in 1 post(s)
Originally Posted by: Benjamin Nitschke Go to Quoted Post
Well, you are using classes of the DeltaEngine.Rendering assembly, which is absolutely fine and we can provide the best kind of optimizations here even if the code might not run as fast as it could right now.


How can I reach similar performance as with rendering Texture2D's with SpriteBatch in XNA? If you'll say wait for the next release, then it's totally ok :) But please give me a hint on how to *actually do it* and when. No marketing stuff please anymore.

I do not fear the amount of work needed, just need a hint on how to do it. With XNA it's simple, just use SpriteBatch and Draw a Texture2D with it.

EDIT:
With XNA I achieve good performance just by drawing particle with:

Code:
public virtual void Draw(SpriteBatch spriteBatch)
        {
            spriteBatch.Draw(Texture, Position, null, 
                Color * Opacity, Rotation, Origin, Scale, SpriteEffects.None, Depth);
        }

My question is, how can I do this with Delta ?

Also, I am sorry if I challenge your work, but I think it's needed. I am not a pro in gamedev, but I think I am a pro in sw engineering. And as a customer I think I deserve a right to question the comments you make. And after all, questions I make are simple ones. :)

-erno

Edited by user Tuesday, April 30, 2013 12:05:52 AM(UTC)  | Reason: Not specified

Codesmith - Erno Pakarinen
Check my development blog @ blog.codesmith.fi
Offline Benjamin  
#7 Posted : Tuesday, April 30, 2013 12:23:41 AM(UTC)
Benjamin

Medals: Admin

Joined: 8/20/2011(UTC)
Posts: 1,421
Location: Hannover

Thanks: 18 times
Was thanked: 97 time(s) in 92 post(s)
I just told you. All you have to do is to go to the DeltaEngine.Graphics.Tests and look around.

For example you could modify the DrawImage test to draw many vertices, I did not even change any code, I just added this test. There is plently more optimizations that can be done (e.g. it is not a good idea to give each vertex a random color, normally the whole quad is colored in the same color, if needed at all). This runs easily with 71 fps on my PC with 1 million polygons per frame as you can see from the screenshot below.

Code:

                [VisualTest]
		public void DrawImagesWithOneMillionPolygonsPerFrame(Type resolver)
		{
			Image image = null;
[VisualTest]
		public void DrawImagesWithOneMillionPolygonsPerFrame(Type resolver)
		{
			Image image = null;
			var vertices = new VertexPositionColorTextured[40000];
			var indices = new short[60000];
			var indicesIndex = 0;
			for (int y = 0; y < 100; y++)
				for (int x = 0; x < 100; x++)
				{
					int quadIndex = (y * 100 + x) * 4;
					vertices[quadIndex + 0] = new VertexPositionColorTextured(new Point(x, y) * 10,
						Color.GetRandomColor(), Point.Zero);
					vertices[quadIndex + 1] = new VertexPositionColorTextured(new Point(x + 1, y) * 10,
						Color.GetRandomColor(), Point.UnitX);
					vertices[quadIndex + 2] = new VertexPositionColorTextured(new Point(x + 1, y + 1) * 10,
						Color.GetRandomColor(), Point.One);
					vertices[quadIndex + 3] = new VertexPositionColorTextured(new Point(x, y + 1) * 10,
						Color.GetRandomColor(), Point.UnitY);
					// 2 Polygons per quad
					indices[indicesIndex++] = (short)quadIndex;
					indices[indicesIndex++] = (short)(quadIndex + 1);
					indices[indicesIndex++] = (short)(quadIndex + 2);
					indices[indicesIndex++] = (short)quadIndex;
					indices[indicesIndex++] = (short)(quadIndex + 2);
					indices[indicesIndex++] = (short)(quadIndex + 3);
				}

			Start(resolver, (ContentLoader content) =>
			{
				image = content.Load<Image>("DeltaEngineLogo");
			}, (Drawing drawing, Window window) =>
			{
				// Currently required to make sure the texture is set
				image.Draw(new VertexPositionColorTextured[4]);
				// Draw 50 times to reach 1 million polygons per frame
				drawing.SetIndices(indices, indices.Length);
				for (int num = 0; num < 50; num++)
					drawing.DrawVertices(VerticesMode.Triangles, vertices);

				if (Time.Current.CheckEvery(1))
					window.Title = "Fps: " + Time.Current.Fps;
			});
		}


Screenshot of DrawImagesWithOneMillionPolygonsPerFrame

Not sure what you mean by marketing? We are not doing any marketing right now, I just was trying to explain.

Edited by user Tuesday, April 30, 2013 12:28:38 AM(UTC)  | Reason: Adding screenshot

File Attachment(s):
OneMillionPolygonsPerFrameDeltaEngineV0975.png (660kb) downloaded 184 time(s).

You cannot view/download attachments. Try to login or register.
Offline codesmith  
#8 Posted : Tuesday, April 30, 2013 8:03:58 AM(UTC)
codesmith

Joined: 4/26/2013(UTC)
Posts: 46

Thanks: 6 times
Was thanked: 1 time(s) in 1 post(s)
Thanks, I'll try to adapt that into the particle system later today or tomorrow.

In particle system you need to have the possibility to change color (including alpha), rotation, scale and such. But I am sure this is just a matter of work, e.g. to rotate the vertex points around certain point. And to scale just move the vertices..

Edit: also the whole draw loop / update needs to be modified quite a bit. In the current implementation each particle is a sprite having its own vertices. Renderer draws them one at a time, that means instead of having a ready made vertex array/buffer the vertices of each sprite are managed one by one (vertices are rotated if needed etc.). I'll check what can be done to the update/draw loop to reach bit like similar situation like in your example code.

This was just the answer I was looking :)
-erno

Edited by user Tuesday, April 30, 2013 8:33:07 AM(UTC)  | Reason: Not specified

Codesmith - Erno Pakarinen
Check my development blog @ blog.codesmith.fi
Offline codesmith  
#9 Posted : Tuesday, April 30, 2013 9:07:18 AM(UTC)
codesmith

Joined: 4/26/2013(UTC)
Posts: 46

Thanks: 6 times
Was thanked: 1 time(s) in 1 post(s)
I did the first step to optimize the drawing by just modifying the Drawing on Particle Emitters. Instead of renderer I do it manually now like this:

ParticleEmitter code:
Code:


        public virtual void Draw(Time gameTime)
        {
            // Draw the particles
            int pc = particles.Count;
            var vertices = new VertexPositionColorTextured[4];
        public virtual void Draw(Time gameTime)
        {
            // Draw the particles
            int pc = particles.Count;
            var vertices = new VertexPositionColorTextured[4];
            ScreenSpace screen = hostEffect.ParticleSystem.Screen;
            Particle p;
            for (int i = 0; i < particles.Count; ++i)
            {
                p=particles[i];
                vertices[0] = new VertexPositionColorTextured(
                    screen.ToPixelSpaceRounded(Rotate(p.DrawArea.TopLeft, p)), p.Color, Point.Zero);
                vertices[1] = new VertexPositionColorTextured(
                    screen.ToPixelSpaceRounded(Rotate(p.DrawArea.TopRight, p)), p.Color, Point.UnitX);
                vertices[2] = new VertexPositionColorTextured(
                    screen.ToPixelSpaceRounded(Rotate(p.DrawArea.BottomRight, p)), p.Color, Point.One);
                vertices[3] = new VertexPositionColorTextured(
                    screen.ToPixelSpaceRounded(Rotate(p.DrawArea.BottomLeft, p)), p.Color, Point.UnitY);
                p.Image.Draw(vertices);
            }
        }

		protected Point Rotate(Point point, Particle particle)
		{
			var rotationCenter = particle.DrawArea.Center;
			point -= rotationCenter;
			float sin = MathFunctions.Sin(particle.Rotation);
			float cos = MathFunctions.Cos(particle.Rotation);
			point = new Point(point.X * cos - point.Y * sin, point.X * sin + point.Y * cos);
			return rotationCenter + point;
		}



This alone doubled the Fps. So it's getting there, and of course can be improved a lot still. Particles are still handled one by one and vertices are taken from the particles one by one. But anyway, need to leave now so I'll get back to this later :)

-erno

Edited by moderator Tuesday, April 30, 2013 10:54:36 AM(UTC)  | Reason: Not specified

Codesmith - Erno Pakarinen
Check my development blog @ blog.codesmith.fi
Offline Benjamin  
#10 Posted : Tuesday, April 30, 2013 10:54:08 AM(UTC)
Benjamin

Medals: Admin

Joined: 8/20/2011(UTC)
Posts: 1,421
Location: Hannover

Thanks: 18 times
Was thanked: 97 time(s) in 92 post(s)
Looks like a good first step. All that is left is to remove the p.Image.Draw call from the for loop and only do one draw call below the for loop. For that you need a filled index buffer to reach all vertices and a large enough vertex buffer to hold all particle quads like in my example.

The rotation and screen conversion code is also quite slow, but I don't think you will notice it until very high particle or fps rates. Rotations could be handled in a shader and screen space calculations as well, but it is a better idea to already be in the correct space from the start for particles (probably 1 optimized system for 2D and one for 3D).

Edited by user Tuesday, April 30, 2013 11:03:36 AM(UTC)  | Reason: Not specified

thanks 1 user thanked Benjamin for this useful post.
codesmith on 4/30/2013(UTC)
Offline codesmith  
#11 Posted : Tuesday, April 30, 2013 1:56:48 PM(UTC)
codesmith

Joined: 4/26/2013(UTC)
Posts: 46

Thanks: 6 times
Was thanked: 1 time(s) in 1 post(s)
Benjamin, look at this:

After few optimizations:

I did the same thing as in your example. What I did:

1) ParticleSystem keeps particles in PixelSpace coordinates...
2) I made a vertex buffer and indices like you suggested

Of course this approach takes more memory during runtime, but the performance is *so much* higher. With that 3000 particles, I got 120fps.

The particle system update itself (meaning, generating particles, modifying existing particles etc.) takes also some time. But I think I'm satisfied with this.

This is the draw loop currently, check this gist @ github

The Code formatter bugs and doesn't display things correctly always, thats why the above gist.
Code:
       
public virtual void Draw(Time gameTime, Drawing drawing)
        {
            int pc = particles.Count;
            if (_vertices.Length < (4 * pc) || _indices.Length < pc)
            {
                _vertices = new VertexPositionColorTextured[4 * pc];
                _indices = new short[6 * pc];
            }

            Particle p; 
            var indicesIndex = 0;
            var quadIndex = 0;
            for (int i = 0; i < pc; i++, quadIndex+=4)
            {
                p = particles[i];
                _vertices[quadIndex] = new VertexPositionColorTextured(
                    Rotate(p.DrawArea.TopLeft, p), p.Color, Point.Zero);
                _vertices[quadIndex + 1] = new VertexPositionColorTextured(
                    Rotate(p.DrawArea.TopRight, p), p.Color, Point.UnitX);
                _vertices[quadIndex + 2] = new VertexPositionColorTextured(
                    Rotate(p.DrawArea.BottomRight, p), p.Color, Point.One);
                _vertices[quadIndex + 3] = new VertexPositionColorTextured(
                    Rotate(p.DrawArea.BottomLeft, p), p.Color, Point.UnitY);

                _indices[indicesIndex++] = (short)quadIndex;
                _indices[indicesIndex++] = (short)(quadIndex + 1);
                _indices[indicesIndex++] = (short)(quadIndex + 2);
                _indices[indicesIndex++] = (short)quadIndex;
                _indices[indicesIndex++] = (short)(quadIndex + 2);
                _indices[indicesIndex++] = (short)(quadIndex + 3);
            }

            foreach (Image img in textures)
            {
                img.Draw(new VertexPositionColorTextured[4]);
            }

            drawing.SetIndices(_indices, indicesIndex);
            drawing.DrawVertices(VerticesMode.Triangles, _vertices);
        }


This has one problem, image/texture is not used correctly. Some explanation:

When emitter generates a particle, it randomly selects a texture from one of the textures that were added to the ParticleEmitter:

Quote:
emitter.AddTexture(content.Load ... );
emitter.AddTexture(content.Load ... );
etc..

// ParticleEmitter.Generate does this at one point:
p= new Particle( textures[random.Next(textures.Count)],
Rectangle.FromCenter(screen.ToPixelSpaceRounded(Position), screen.ToPixelSpace(hostEffect.ParticleSize)));

I think my draw loop (as shown above) does not set the image correctly... Any hints?

Thanks a lot for a good hint :)

One obvious optimization would be making the rotation table based, I mean to store Sin and Cos values in tables for e.g. angles in Degrees (table of 360 items for example). Of course this would make the rotation more unaccurate but would be an optimization to this if it's needed.

But I think this above is ok, as you said, with "few" particles like this :)

-erno

Edited by user Tuesday, April 30, 2013 2:18:17 PM(UTC)  | Reason: Not specified

Codesmith - Erno Pakarinen
Check my development blog @ blog.codesmith.fi
thanks 1 user thanked codesmith for this useful post.
panky on 5/1/2013(UTC)
Offline codesmith  
#12 Posted : Tuesday, April 30, 2013 2:35:39 PM(UTC)
codesmith

Joined: 4/26/2013(UTC)
Posts: 46

Thanks: 6 times
Was thanked: 1 time(s) in 1 post(s)
Btw, it seems it draws the Last image that was "Drawn" from multiple images before the Drawing was drawn. So if I want the Emitter generate multiple "textures/images" for a particle I need to split the draw loop into as many iterations as there are textures.

E.g. if there are 2 texture variations of particles I need to do (in semispeudocode:)

// Draw particles with Image1
SetupVerticesForParticleWithImage1()
Image1.draw(new VertexPositionColorTextured[4]);
drawing.SetIndices(_indices1, indicesIndex1);
drawing.DrawVertices(VerticesMode.Triangles, _vertices1);

// Draw particles with Image2
SetupVerticesForParticleWithImage2()
Image2.draw(new VertexPositionColorTextured[4]);
drawing.SetIndices(_indices2, indicesIndex2);
drawing.DrawVertices(VerticesMode.Triangles, _vertices2);

Like that...

-erno

Edited by user Tuesday, April 30, 2013 2:36:27 PM(UTC)  | Reason: Not specified

Codesmith - Erno Pakarinen
Check my development blog @ blog.codesmith.fi
Offline Benjamin  
#13 Posted : Tuesday, April 30, 2013 6:39:17 PM(UTC)
Benjamin

Medals: Admin

Joined: 8/20/2011(UTC)
Posts: 1,421
Location: Hannover

Thanks: 18 times
Was thanked: 97 time(s) in 92 post(s)
Yes, exactly, each vertex/index buffer combination needs its own material (or image in this case). Otherwise your code looks good and it is nice to hear you could optimize the particle rendering so much ThumpUp
thanks 1 user thanked Benjamin for this useful post.
codesmith on 5/1/2013(UTC)
Offline codesmith  
#14 Posted : Tuesday, April 30, 2013 6:47:18 PM(UTC)
codesmith

Joined: 4/26/2013(UTC)
Posts: 46

Thanks: 6 times
Was thanked: 1 time(s) in 1 post(s)
Thanks, it's been fun to learn the basics :)

Another thing I need to solve is of course the Depth buffer thingy. In this optimized version everything is drawn in the order particles are added which does not really work for every situation :) So I need to make my own depth sorter or use something that already exists. Drawing particles in the system gets of course gets more complex quite fast eventually because I have my particle system structure like this:

ParticleSystem
1
|
n
ParticleEffect
1
|
n
ParticleEmitter
1
|
n
Particle


So if I want to mix the depth/renderlayer of particles between all ParticleEffects, I need to depth buffer everything in the whole ParticleSystem. The above optimization I made is per ParticleEmitter which is just one part of a bigger particle system.

However, most of this is just educational, I am sure the current implementation works for many purposes :P

Stupid Question number 42:) : Is there some ready made, fast data structure, in DE I can use for sorting my sprites?
-erno

Edited by user Tuesday, April 30, 2013 6:55:57 PM(UTC)  | Reason: Not specified

Codesmith - Erno Pakarinen
Check my development blog @ blog.codesmith.fi
Offline codesmith  
#15 Posted : Tuesday, April 30, 2013 7:37:28 PM(UTC)
codesmith

Joined: 4/26/2013(UTC)
Posts: 46

Thanks: 6 times
Was thanked: 1 time(s) in 1 post(s)
By the way: Delta Engine and 2D optimization

A honest blog entry :)
Codesmith - Erno Pakarinen
Check my development blog @ blog.codesmith.fi
Offline panky  
#16 Posted : Wednesday, May 1, 2013 11:29:04 AM(UTC)
panky

Joined: 9/6/2012(UTC)
Posts: 32

Thanks: 3 times
Was thanked: 1 time(s) in 1 post(s)
I have the same issue :) Nice thread.
thanks 1 user thanked panky for this useful post.
codesmith on 5/1/2013(UTC)
Offline codesmith  
#17 Posted : Wednesday, May 1, 2013 11:41:08 AM(UTC)
codesmith

Joined: 4/26/2013(UTC)
Posts: 46

Thanks: 6 times
Was thanked: 1 time(s) in 1 post(s)
Thanks Panky :) We found a great solution which works very well. I really appreciate the prompt help from Benjamin!
-erno
Codesmith - Erno Pakarinen
Check my development blog @ blog.codesmith.fi
Offline panky  
#18 Posted : Wednesday, May 1, 2013 4:40:17 PM(UTC)
panky

Joined: 9/6/2012(UTC)
Posts: 32

Thanks: 3 times
Was thanked: 1 time(s) in 1 post(s)
Wow, 100000 particle rendered seamlessly.
Offline Benjamin  
#19 Posted : Wednesday, May 1, 2013 5:01:16 PM(UTC)
Benjamin

Medals: Admin

Joined: 8/20/2011(UTC)
Posts: 1,421
Location: Hannover

Thanks: 18 times
Was thanked: 97 time(s) in 92 post(s)
Originally Posted by: codesmith Go to Quoted Post

One obvious optimization would be making the rotation table based, I mean to store Sin and Cos values in tables for e.g. angles in Degrees (table of 360 items for example). Of course this would make the rotation more unaccurate but would be an optimization to this if it's needed.


A few months ago one programmer did implement rotation lookup tables and after lots of work I told him that it is most likely not making a difference and he wrote some performance tests to prove his optimizations would be good. As it turns out it made no difference or was even slower than to call the math functions.

You are right that calculating sin/cos is a overhead and there are some tricks that can be employed, especially for particle sprites, but overzealous optimizations can lead to ugly and hard to understand code. For this reason all of the optimizations have been removed. We even went the other way and made code slower, but more clean, e.g. if you look at the Matrix class.

We tried instead to optimize the code in our build service, the Matrix class is easy, it can just be replaced with an uglier, but faster version. Something like sin/cos is harder, but there might be places where code like this is basically recalculating the same values thousands of times:

Code:

foreach (var entity in entities) // lets say this is a huge number like 1000
  entity.DrawRotated(90);

public void DrawRotated(float rotation)
{
  //..
  var vertices = new[]
foreach (var entity in entities) // lets say this is a huge number like 1000
  entity.DrawRotated(90);

public void DrawRotated(float rotation)
{
  //..
  var vertices = new[]
  {
    GetVertex(drawArea.TopLeft.RotateAround(rotationCenter, rotation), Point.Zero, entity),
    GetVertex(drawArea.TopRight.RotateAround(rotationCenter, rotation), Point.UnitX, entity),
    GetVertex(drawArea.BottomRight.RotateAround(rotationCenter, rotation), Point.One, entity),
    GetVertex(drawArea.BottomLeft.RotateAround(rotationCenter, rotation), Point.UnitY, entity)
  };
  entity.Get<Image>().Draw(vertices);
}

public Point RotateAround(Point center, float angleInDegrees)
{
  RotateAround(center, MathExtensions.Sin(angleInDegrees), MathExtensions.Cos(angleInDegrees));
  return this;
}

public void RotateAround(Point center, float rotationSin, float rotationCos)
{
  var translatedPoint = this - center;
  X = center.X + translatedPoint.X * rotationCos - translatedPoint.Y * rotationSin;
  Y = center.Y + translatedPoint.X * rotationSin + translatedPoint.Y * rotationCos;
}


It could be replaced with:

Code:

foreach (var entity in entities) // lets say this is a huge number like 1000
  entity.DrawRotated(MathExtensions.Sin(90), MathExtensions.Cos(90));

public void DrawRotated(float rotationSin, float rotationCos)
{
  //..
  var vertices = new[]
foreach (var entity in entities) // lets say this is a huge number like 1000
  entity.DrawRotated(MathExtensions.Sin(90), MathExtensions.Cos(90));

public void DrawRotated(float rotationSin, float rotationCos)
{
  //..
  var vertices = new[]
  {
    GetVertex(drawArea.TopLeft.RotateAround(rotationCenter, rotationSin, rotationCos), Point.Zero, entity),
    GetVertex(drawArea.TopRight.RotateAround(rotationCenter, rotationSin, rotationCos), Point.UnitX, entity),
    GetVertex(drawArea.BottomRight.RotateAround(rotationCenter, rotationSin, rotationCos), Point.One, entity),
    GetVertex(drawArea.BottomLeft.RotateAround(rotationCenter, rotationSin, rotationCos), Point.UnitY, entity)
  };
  entity.Get<Image>().Draw(vertices);
}

public void RotateAround(Point center, float rotationSin, float rotationCos)
{
  var translatedPoint = this - center;
  X = center.X + translatedPoint.X * rotationCos - translatedPoint.Y * rotationSin;
  Y = center.Y + translatedPoint.X * rotationSin + translatedPoint.Y * rotationCos;
}


Now just one call to sin and cos was done. It is obviously hard to detect such optimizations, but luckily computers are good at detecting patterns :) This is ongoing work and will take a long time, but certain patterns seem to repeat themselves in game programming (e.g. use of vertex buffers instead of using higher level classes like in this thread).

Edited by user Wednesday, May 1, 2013 5:02:03 PM(UTC)  | Reason: fixed code, stupid syntax highlighter

Offline codesmith  
#20 Posted : Wednesday, May 1, 2013 5:31:27 PM(UTC)
codesmith

Joined: 4/26/2013(UTC)
Posts: 46

Thanks: 6 times
Was thanked: 1 time(s) in 1 post(s)
Originally Posted by: Benjamin Nitschke Go to Quoted Post
Now just one call to sin and cos was done. It is obviously hard to detect such optimizations, but luckily computers are good at detecting patterns :) This is ongoing work and will take a long time, but certain patterns seem to repeat themselves in game programming (e.g. use of vertex buffers instead of using higher level classes like in this thread).



Oh yeah, like you said: too eager optimization leads to confusion in readability. Also it's perfectly fine to use higher level classes but you need to understand what happens under the hood. This is why I started this thread :)

-erno

Codesmith - Erno Pakarinen
Check my development blog @ blog.codesmith.fi
Rss Feed  Atom Feed
Users browsing this topic
OceanSpiders 2.0
Forum Jump  
You cannot post new topics in this forum.
You cannot reply to topics in this forum.
You cannot delete your posts in this forum.
You cannot edit your posts in this forum.
You cannot create polls in this forum.
You cannot vote in polls in this forum.

Powered by YAF.NET | YAF.NET © 2003-2023, Yet Another Forum.NET
This page was generated in 0.266 seconds.