Page 1 of 3

How to avoid drawing map every single tick

Posted: Tue Aug 21, 2012 2:33 pm
by munchor

Code: Select all

function love.draw()
  currentMap:setDrawRange(player.translated, 0, 800, 640)
  love.graphics.translate(-player.translated, 0)

  --love.graphics.push()
  currentMap:draw()
  --love.graphics.pop()

  --love.graphics.push()
  love.graphics.draw(player.image, player.x, player.y)
  --love.graphics.pop()
end
That is my love.draw() function. It works just fine, but when there are lots of tiles in a 800*600 screen, the game renders really really slowly. I think it's because I'm drawing the map every single tick. How can I avoid drawing it every single tick?

Thank you.

Re: How to avoid drawing map every single tick

Posted: Tue Aug 21, 2012 2:50 pm
by OmarShehata
You need to draw everything every single frame. It sounds counter intuitive but I've been told that trying to only redraw parts of the screen is actually slower than clearing the screen and drawing everything again, (at least that was for the set up I had in a project with SDL/OpenGL)

With that said, you should be able to render a lot of tiles without a slow down.

It's likely in your currentMap:draw() function. Can you post its contents?

Re: How to avoid drawing map every single tick

Posted: Tue Aug 21, 2012 2:54 pm
by T-Bone
You should check out Canvas (https://love2d.org/wiki/Canvas) and Spritebatch (https://love2d.org/wiki/SpriteBatch). They work differently but both can be useful in this case.

Basically, the idea is that if some parts don't change, you can draw them once to some sort of buffer and then just draw that every frame. This can, in many cases, dramatically improve performance.

Spritebatches can only work with a single image source file, and you can add parts of it to the batch using Quads. The Spritebatch also doesn't need to know how large it's going to be, which is useful in some situations. It is also compatible with love-native-android which can be good to know.

Canvases (previously called Framebuffers) are fixed-size images basically, that you can draw to by passing a function similar to love.draw to using the Canvas:renderTo method. They allow you to draw basically anything.

Once you have created a Canvas or a Spritebatch, you can draw them by using love.graphics.draw inside love.draw like you'd normally do with an image or something.

Re: How to avoid drawing map every single tick

Posted: Tue Aug 21, 2012 3:27 pm
by Boolsheet
munchor wrote: That is my love.draw() function. It works just fine, but when there are lots of tiles in a 800*600 screen, the game renders really really slowly. I think it's because I'm drawing the map every single tick. How can I avoid drawing it every single tick?
Like OmarShehata and T-Bone said, you need to draw it everytime or store the intermediate result in a Canvas and draw that. If this is new to you, then you've only been exposed to applications that do the redrawing for you. LÖVE is a bit closer to the OpenGL API.

There can be many reasons for the slowdown. Hardware has its limits and maybe you already hit it. LÖVE has a few gotchas and maybe you did something in your code to get a perfomance hit. For that we need to see the full code. It doesn't have to be your game, it can also be a small example (like a scene out of the game) where you see the same effect.
T-Bone wrote:The Spritebatch also doesn't need to know how large it's going to be, which is useful in some situations.
SpriteBatches do not dynamically allocate the memory, if you mean that. The size argument of love.graphics.newSpriteBatch defaults to 1000 if you leave it out.

Re: How to avoid drawing map every single tick

Posted: Tue Aug 21, 2012 4:28 pm
by T-Bone
Boolsheet wrote:
munchor wrote: That is my love.draw() function. It works just fine, but when there are lots of tiles in a 800*600 screen, the game renders really really slowly. I think it's because I'm drawing the map every single tick. How can I avoid drawing it every single tick?
Like OmarShehata and T-Bone said, you need to draw it everytime or store the intermediate result in a Canvas and draw that. If this is new to you, then you've only been exposed to applications that do the redrawing for you. LÖVE is a bit closer to the OpenGL API.

There can be many reasons for the slowdown. Hardware has its limits and maybe you already hit it. LÖVE has a few gotchas and maybe you did something in your code to get a perfomance hit. For that we need to see the full code. It doesn't have to be your game, it can also be a small example (like a scene out of the game) where you see the same effect.
T-Bone wrote:The Spritebatch also doesn't need to know how large it's going to be, which is useful in some situations.

SpriteBatches do not dynamically allocate the memory, if you mean that. The size argument of love.graphics.newSpriteBatch defaults to 1000 if you leave it out.
Ah, but that's the number of stuff that can be drawn to the SpriteBatch, not the size of the SpriteBatch (as in numbers of pixels in width and height). The only reason I mention it is that it is a clear difference between SpriteBatches and Canvases.

Re: How to avoid drawing map every single tick

Posted: Tue Aug 21, 2012 4:47 pm
by Boolsheet
T-Bone wrote:Ah, but that's the number of stuff that can be drawn to the SpriteBatch, not the size of the SpriteBatch (as in numbers of pixels in width and height). The only reason I mention it is that it is a clear difference between SpriteBatches and Canvases.
Oh, ok, but you do not "draw to the SpriteBatch". You give it the information where you want the images drawn. When you then draw the SpriteBatch, it will go through the list and draw everything in one call.

Re: How to avoid drawing map every single tick

Posted: Tue Aug 21, 2012 5:38 pm
by Lafolie
The term "the size of the spriteBatch is 10" says to me that the spriteBatch in question can hold 10 quads. In fact, spriteBatches don't have any attributes such as width or height, as a spriteBatch is a collective object and has nothing by itself that is drawn. Indeed, it is possible to retrieve the width/height of the source image, but that has little to do with the spriteBatch itself.

That's a pretty poor example of how a spriteBatch differs from a Canvas. A batch is used for speedily drawing images that use a texture atlas (a quad), and is particularly useful for repetitions of said images. It is essentially a collection of quads and a reference to the image that the quads should refer to.

A canvas is used for dynamic drawing operations; drawing things to something that isn't the screen. This can be used to make composite images and all manner of effects. I once used them to "flatten" blood particles onto a background, making them permanent but cost-effective.

Re: How to avoid drawing map every single tick

Posted: Wed Aug 22, 2012 4:08 pm
by munchor
I'm using AdvTiledLoader, so the map:draw() function can be found here.

I'll give the Canvas a try, it looks good.

Edit
I tried the canvas, it didn't work very well. On love.load() I have "myCanvas = love.graphics.newCanvas(800, 640)"

Code: Select all

  myCanvas:renderTo(function()
                      currentMap:draw()
                   end)
  love.graphics.draw(myCanvas)
But the map isn't drawn correctly, it's all messed up :S If I replace all those lines with "currentMap:draw()" it works just fine.

Re: How to avoid drawing map every single tick

Posted: Wed Aug 22, 2012 10:23 pm
by Jasoco
Not sure what renderTo is. What you probably want is setTarget.

Code: Select all

love.graphics.setTarget(myCanvas)
--Draw your map and everything you want on your canvas
love.graphics.setTarget() --To return the drawing to the main screen
You'd call that whenever you need to redraw the map. And then simply love.graphics.draw(myCanvas, x, y) every frame. Only call the map drawing stuff when it's needed like when something changes and you'll only need to draw a single image every frame. Well, plus whatever you want to draw that's not the map.

Just remember that not everyone can support canvases.

Re: How to avoid drawing map every single tick

Posted: Wed Aug 22, 2012 10:57 pm
by Nixola
Jasoco wrote:Not sure what renderTo is. What you probably want is setTarget.
1) Isn't it setCanvas?
2) canvas:renderTo(functionThatDrawsSomething() --[

Code: Select all

] end) and
love.graphics.setCanvas(canvas)
functionThatDrawsSomething()
love.graphics.setCanvas()
are the same thing