Updating spritebatches

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Post Reply
User avatar
OmarShehata
Party member
Posts: 259
Joined: Tue May 29, 2012 6:46 pm
Location: Egypt
Contact:

Updating spritebatches

Post by OmarShehata »

So I've been implementing sprite batches in my engine in various places, and they're all beautiful and super optimized.

Except when I need to move something in the batch, and strangely enough, binding and unbinding the batch to memory seems to make the update slower. Although I haven't done any extensive tests.

Sprite batches seem to work wonderfully for a lot of cases, but it seems like a really bad choice if most or all your batch items are moving every frame.

So my question is, is there a way to sort of, bind a texture to memory, and draw it over and over wherever I want? Something like:

Code: Select all

Image:bind()
draw(x,y,angle)
draw(x2,y2,angle)
...

Image:unbind()
Because I remember when I was first starting with C++, and drawing a hundred instances of the same image would lag horribly, then I switched to binding the texture once, then drawing thousands of times and it was completely lag free.

It was a pretty simple change. Is something like this already happening under the hood? Thoughts?

Thanks!
spectralcanine
Citizen
Posts: 65
Joined: Sat Dec 22, 2012 8:17 am

Re: Updating spritebatches

Post by spectralcanine »

Your old C++ code most likely used the old (and deprecated for about 9 years) rendering code with glBegin/End, and glVertex3f etc.

Rendering this way, by sending each coordinate alone (or from Love's standpoint, each sprite alone) is highly unoptimal and will result in really bad frames per second.

Sprite batches use buffers, which are as you say - super optimized for static data, but a bit on the slow side on editing data in real time.
Modern code literally has no other way to render, though. Only buffers are supported (and honestly that's the correct way to do it).
You can give your drivers hints as to what the buffers are used for, which gives them more freedom of choice to where in memory to place them. Look at the third argument of newSpriteBatch (the relevant hint for you is "stream").

As to why using SpriteBatch's bind and unbind methods seem to make it slower - they use a different method to send your data to the buffer. This method (mapping a buffer to client memory, if it means anything to you) has a little more overhead than the regular method of binding buffers, but makes editing faster.
If you are only editing tiny pieces, like a few sprites, you should consider not binding.
You did write that most if not all of your sprites move - how many are we talking about?

To make more optimal code we'd first need access to vertex shaders, which we should have by version 0.9.X as far as I know.

As an optimization until then (and this should be built-in to begin with, if any developer reads this), you should change your love.run callback so that it runs love.update in a constant rate of (probably) 30/60 FPS.
Check this article for the why/how (beside the fact that it will simply hog far less computations from your CPU).
Granted, this method does add a little complexity to rendering, because you need to interpolate between frames for smooth movement.

Good luck. :)
User avatar
markgo
Party member
Posts: 190
Joined: Sat Jan 05, 2013 12:21 am
Location: USA

Re: Updating spritebatches

Post by markgo »

While we're on the same subject, when should one bind/unbind spritebatches? I have the impression that it's only necessary if you're adding/setting large amounts of sprites.
User avatar
bartbes
Sex machine
Posts: 4946
Joined: Fri Aug 29, 2008 10:35 am
Location: The Netherlands
Contact:

Re: Updating spritebatches

Post by bartbes »

Yes, you should only bind/unbind around adding/editing, the sweet spot for when to use it is.. a mystery, though. (In any case, it's better for large amounts of edits.)
User avatar
OmarShehata
Party member
Posts: 259
Joined: Tue May 29, 2012 6:46 pm
Location: Egypt
Contact:

Re: Updating spritebatches

Post by OmarShehata »

Thanks for the super detailed post and explanation spectralcanine!
Modern code literally has no other way to render, though. Only buffers are supported (and honestly that's the correct way to do it).
So, if I understand correctly, the whole problem is in sending data to the GPU or however it gets rendered? So instead of sending an Image, and its geometry to draw it, sprite batches sends the Image once and stores the geometry somewhere, so you can redraw it many times, whenever you like.

And there's no super fast way to update this geometry data.

For this specific case, I was trying to tile my background, and I realized I could make the initial size bigger, use less tiles, and then render them normally, which would be more optimized than using a sprite batch in this case.

But I'd still need to use some combination of optimizations, and maybe some creative use of canvases or something for other cases that have lots of repeated moving or animated objects.
User avatar
Pash
Prole
Posts: 43
Joined: Sun Dec 30, 2012 8:04 am

Re: Updating spritebatches

Post by Pash »

Interestingly enough, I hit the 16k SpriteBatch limit in 0.8.0 while testing a large tile/block idea for a game. I noticed that once they were added to the batch though, and just being called in a single draw, things are merry and fine. I saw this limit exists through testing and the incredible memory of the regular IRC crew (ie. slime) instructed me that this was a limitation.

My conclusion is, even if the guys fix (I was told it was a fix due to 16bit integers being used for SB's) for 0.8.1 the real engineering choice is when to update the SB's anyway. My experience is way too limited to say what is best and I guess I would need a few more years behind me with good profiling examples to give a good piece of advice.

However, I did notice that spectralcanine in this thread https://www.love2d.org/forums/viewtopic.php?f=4&t=18145 pointed out a good use of the argument returned when using add/addq.

Ohh and I thought about using multiple SB's and regions, to actually break up the items being added or updated based on requirements. But I guess this is standard stuff.
Sagan Interactive - Looking forward to a creative 2013.
spectralcanine
Citizen
Posts: 65
Joined: Sat Dec 22, 2012 8:17 am

Re: Updating spritebatches

Post by spectralcanine »

Ideally buffers are stored directly in GPU memory (also known as dedicated memory).
This makes the GPU fetch them incredibly fast, so rendering is fast, but it makes editing them a little awkward, since we don't normally have access to the GPU's memory.

Using different hints for the graphics driver might make it decide to put your buffer in other places (most commonly in system RAM), if it thinks it will be more efficient. Drivers might ignore it completely too, and do whatever they feel like. E.g. profile your buffers and switch them to anywhere they want when they see how you use them.
Generally this isn't really an issue.

Buffers aren't directly related to textures, they are just chunks of arbitrary data. You (or Love in this case) tell the GPU how to interpret that data and what to do with it.
GPUs generally like low amounts of big chunks, rather than big amounts of tiny chunks - this is why drawing 10000 sprites one by one is a lot less efficient than drawing one sprite batch with 10000 sprites.

You use texture atlases, or, images that encapsulate a lot of images, because when you draw with a buffer, you draw all of it for performance, and so you can't switch textures in the middle. At the same time, you want to use many different images for your sprites inside the sprite batch, so you must be able to refer to these images using one texture (there are better ways to do this by now, but they are far less backwards compatible).

Generally, if your sprites are static, don't hesitate a moment and go with sprite batches.
If you have single sprites that tend to change a lot (e.g. players, enemies, moving platforms), you should not include them in big chunks of static sprites, like a level's terrain.

As far as further optimizations, I don't think you'll need any. ^^

Do tell me if I should stop explaining things you probably don't care about worse than any Google search can :ultraglee:
User avatar
slime
Solid Snayke
Posts: 3161
Joined: Mon Aug 23, 2010 6:45 am
Location: Nova Scotia, Canada
Contact:

Re: Updating spritebatches

Post by slime »

If you just need to change the position/color/orientation/whatever of a small number of sprites within the SpriteBatch, then SpriteBatch:set and SpriteBatch:setq let you do it without clearing and re-adding everything. You can even do a sort of pseudo-SpriteBatch:remove with

Code: Select all

SpriteBatch:set(id, 0, 0, 0, 0, 0)
This makes all the sprite's vertices equal (because the x and y scales are 0), which prevents OpenGL from processing the sprite when drawing the SpriteBatch.
spectralcanine
Citizen
Posts: 65
Joined: Sat Dec 22, 2012 8:17 am

Re: Updating spritebatches

Post by spectralcanine »

slime wrote:If you just need to change the position/color/orientation/whatever of a small number of sprites within the SpriteBatch, then SpriteBatch:set and SpriteBatch:setq let you do it without clearing and re-adding everything. You can even do a sort of pseudo-SpriteBatch:remove with

Code: Select all

SpriteBatch:set(id, 0, 0, 0, 0, 0)
This makes all the sprite's vertices equal (because the x and y scales are 0), which prevents OpenGL from processing the sprite when drawing the SpriteBatch.
Truth be told, I never checked if there is any performance difference when editing the same data on different sized buffers, though logically there shouldn't be any. In that case sticking the whole game in batches is preferable.
Post Reply

Who is online

Users browsing this forum: Google [Bot], Majestic-12 [Bot] and 0 guests