Page 1 of 3

A Question of Draw performance

Posted: Mon Jan 28, 2013 7:20 pm
by Pash
Hi guys and gals,

Attached is an image to show my point. I am trying to create a draw only the visible blocks system for a game I am working on. Using code like this:-

Code: Select all

function world:update()
	for i=1, #worldBlocks do
		if worldBlocks[i].x * self.blockSize >= PlayerCam.x and worldBlocks[i].x * self.blockSize < love.graphics.getWidth() 
			and worldBlocks[i].y * self.blockSize >= PlayerCam.y and worldBlocks[i].y * self.blockSize < love.graphics.getHeight() then   
			worldBlocks[i].vis = true
		else 
			worldBlocks[i].vis = false
		end 
	end 
end 

function world:draw()
	for i=1, #worldBlocks do
		if worldBlocks[i].vis then 
			love.graphics.draw(worldBlocks[i].id.img, worldBlocks[i].x * self.blockSize, worldBlocks[i].y * self.blockSize )
		end 
	end
end
These callbacks are in love.update and love.draw respectively.

Now, while this appears to definitely improve the fps drop that I was experiencing without including this, it still isn't as good as I was expecting. Is this fps drop of around 50% in this case, or even 60-70% in other cases (more blocks drawn on screen) to be expected simply because of the volume of items being drawn? I have the base level start randomized to start at different points as well, if the blocks are all not visible on the screen the fps still seems to drop. Is this to do with table lookups or something then because of the lightweight logic in draw?

These blocks will be destroyable and will all have collision properties at some point as well.

Does anyone have better methods for engineering this? At the moment the block count is quiet small as well. I have attached my love file as well. Would really appreciate any pointers :)

Thanks,

Pash

Re: A Question of Draw performance

Posted: Mon Jan 28, 2013 7:31 pm
by MarekkPie
This looks like the case for SpriteBatches.

Re: A Question of Draw performance

Posted: Mon Jan 28, 2013 9:12 pm
by Pash
Thanks. This seems to be the general consensus. Ill give that a go.

Re: A Question of Draw performance

Posted: Tue Jan 29, 2013 8:54 pm
by Pash
I really don't think this is going to scale well at all.

Using spritebatch now. I also added a timer in to check out the speed of the world draw code.

Code: Select all

function world:update()
	timeBefore = love.timer.getTime()
	blockSetBatch:clear()
	for i=1, #worldBlocks do
		if worldBlocks[i].x * self.blockSize >= PlayerCam.x and worldBlocks[i].x * self.blockSize < love.graphics.getWidth() 
			and worldBlocks[i].y * self.blockSize >= PlayerCam.y and worldBlocks[i].y * self.blockSize < love.graphics.getHeight() then   
			blockSetBatch:addq( worldBlocks[i].id.img, worldBlocks[i].x * self.blockSize, worldBlocks[i].y * self.blockSize )
		end 
	end
	howLongItTookToExecute = love.timer.getTime() - timeBefore 
end 

function world:draw()
	love.graphics.draw(blockSetBatch)
end
Attached Love file and Screenshot

Re: A Question of Draw performance

Posted: Tue Jan 29, 2013 9:40 pm
by Pash
As usual, the peeps on irc are very awesome, gave me some good suggestions which I will give a go.

Re: A Question of Draw performance

Posted: Wed Jan 30, 2013 12:22 am
by slime
In case anyone else has similar issues and wanders into this topic: a suggestion from IRC was to only clear/re-add all the sprites when a sprite needs to be removed from the spritebatch, instead of doing it every frame regardless of whether anything's changed.

Additionally, if only a few sprites within the spritebatch change position/rotation/color/whatever and the rest stay the same, you can use SpriteBatch:setq to directly set only the few that changed, instead of redoing everything.

It's also a good idea to use SpriteBatch:bind / SpriteBatch:unbind when clearing and re-adding everything at once.

Re: A Question of Draw performance

Posted: Thu Jan 31, 2013 6:15 pm
by Pash
Thanks Slime. I won't get a chance to get to try this until next week but ill post back when I've tried.

Re: A Question of Draw performance

Posted: Tue Feb 05, 2013 7:56 pm
by Pash
Into a much more smooth performance now with the methods slime posted above. Only updating when you need to is key for using spritebatchs for tile/block based games.

Thanks all!!

Re: A Question of Draw performance

Posted: Mon Feb 11, 2013 12:35 pm
by Pash
I am reaching this hard limit of ~16k sprites in a spritebatch for version 0.8.0 of love. Up to this limit, the performance is fine. I would say its fine to work it this way. But with this limit, I cant add all my tiles to the batch.

So naturally, my next idea is to improve my logic of choosing which tiles should be added to batch. I think larger table iterations are killing my performance. We are talking 100,000's of table items to look through. So I was thinking to add a second table, which is updated every frame, and would only contain drawable tiles. Then I would update the spritebatches based on the table contents. Even at higher resolutions there shouldnt be more than 10k tiles to iterate through in that table. Which is considerably less than the 100,000's of blocks in the world map.

I am gonna try this tonight. I can't think of better ways to do this, unless anyone has any suggestions?

Cheers,

Pash

Re: A Question of Draw performance

Posted: Mon Feb 11, 2013 12:58 pm
by micha
Pash wrote:I think larger table iterations are killing my performance. We are talking 100,000's of table items to look through.
I suppose you have a table with to indeces, with all the tiles in it. If this is the case, you can shorten the iteration by using the two-index structure. So lets say you have a table with two indices, containing information on what tiles have to be drawn: tile[x][y]. For a whole loop you would do two nested for loops for x and y each from 1 to the respective maximum. You can shorten these loopes, if you know the camera position. From the camera position, first determine the (tile)-coordinates of the first tile in the upper left corner and of the last tile in the lower right corner. These tile coordinates can now be used as the start and the end of the for loops. That way, the number of table entries to consider will never be larger than the number of tiles visible on screen.