Faster way to draw rectangles?

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
Zilarrezko
Party member
Posts: 345
Joined: Mon Dec 10, 2012 5:50 am
Location: Oregon

Faster way to draw rectangles?

Post by Zilarrezko »

Apparently if you are drawing more than about 2500 rectangles, then the situation gets kinda laggy. Anyone know of a way to get around this? Cause I plan on having a lot more than 2500 rectangles...

I tried stencils, couldn't get that working right.

Though I have noticed that fill rectangles are less laggy than line rectangles. Don't know what the deal is with that.

Not interested in spritebatches and canvas' because some people don't support canvas' and I'm not even working with textures yet, so don't know why I would need a spritebatch.

Any help is appreciated, especially the kind where I learn.
User avatar
Kingdaro
Party member
Posts: 395
Joined: Sun Jul 18, 2010 3:08 am

Re: Faster way to draw rectangles?

Post by Kingdaro »

Why in hell would you need 2500 rectangles onscreen? (I emphasize onscreen, because if you're drawing anything offscreen, you're doing it wrong.)

And, I think canvases have a reasonable amount of support for you to use them. It's most likely only a small minority that would be affected, if in the name of performance.
User avatar
Zilarrezko
Party member
Posts: 345
Joined: Mon Dec 10, 2012 5:50 am
Location: Oregon

Re: Faster way to draw rectangles?

Post by Zilarrezko »

Kingdaro wrote:Why in hell would you need 2500 rectangles onscreen? (I emphasize onscreen, because if you're drawing anything offscreen, you're doing it wrong.)

And, I think canvases have a reasonable amount of support for you to use them. It's most likely only a small minority that would be affected, if in the name of performance.
Testing, I gotta zoom out to right click to test out my laggy arse pathfinding.

But the thing is, is that I get the performance drop whether they're on screen, or they aren't. So if you know some magic, pass some ovah here.

Besides, I'm having trouble with canvases. I put the same draw code into the canvas after setCanvas. Then I'm having trouble that the conditions in the canvas aren't working. So I have to recreate the canvas object pretty much every frame. Now I'm back where I started performance wise.
User avatar
Jasoco
Inner party member
Posts: 3725
Joined: Mon Jun 22, 2009 9:35 am
Location: Pennsylvania, USA
Contact:

Re: Faster way to draw rectangles?

Post by Jasoco »

You're gonna have to provide a .love or an example of what kind of game you're making.
User avatar
slime
Solid Snayke
Posts: 3132
Joined: Mon Aug 23, 2010 6:45 am
Location: Nova Scotia, Canada
Contact:

Re: Faster way to draw rectangles?

Post by slime »

Zilarrezko wrote:Not interested in spritebatches and canvas' because some people don't support canvas' and I'm not even working with textures yet, so don't know why I would need a spritebatch.
When talking about filled rectangles, a white image of any size is essentially equivalent to a rectangle.

Something like this will be the most efficient (for filled rectangles):

Code: Select all

-- Create a 1x1 white image for use with a SpriteBatch.
local imagedata = love.image.newImageData(1, 1)
imagedata:setPixel(0, 0,  255, 255, 255, 255)

local rectimage = love.graphics.newImage(imagedata)

local spritebatch = love.graphics.newSpriteBatch(rectimage, 3000)

function UpdateRects(rectlist)
    spritebatch:clear()
    spritebatch:bind()

    for i, rect in ipairs(rectlist) do
        spritebatch:setColor(rect.color)

        -- We can use the scale parameters for width and height, since we're scaling a 1x1 image.
        spritebatch:add(rect.x, rect.y, 0, rect.width, rect.height)
    end

    spritebatch:unbind()
end

function DrawRects()
    love.graphics.draw(spritebatch, 0, 0)
end
If the rectangles overlap a lot and they don't change positions relative to each other every frame, it will improve performance to draw them to a Canvas when they do change positions, and draw the Canvas to the main screen (and be sure to use [wiki]Canvas:clear[/wiki] before drawing to the Canvas.)


EDIT: kikito's advice below applies to this as well.
Last edited by slime on Wed Oct 15, 2014 7:59 am, edited 1 time in total.
User avatar
kikito
Inner party member
Posts: 3153
Joined: Sat Oct 03, 2009 5:22 pm
Location: Madrid, Spain
Contact:

Re: Faster way to draw rectangles?

Post by kikito »

When talking about reducing the amount of tiles being drawn, a simple "if" is not enough. The trick is setting up things so that most of the rectangles are not even considered in the first place (so you don't have to do an "if" around them at all).

If the rectangles you are trying to draw are tile-based, you can do this to minimize (correctly) the amount of rectangles being drawn:

viewtopic.php?f=4&t=77081#p161482

If you are not, then your best bet is using some Spatial Database to get the rectangles efficiently. In my experience, a simple grid (where each cell has a list of rectangles touching it, and each rectangle knows what cells it touches, and these lists get updated as the rectangle moves) is enough - quadtress etc are more complex but they get similar or even worse performance when the rectangles are not sparse.
When I write def I mean function.
User avatar
Zilarrezko
Party member
Posts: 345
Joined: Mon Dec 10, 2012 5:50 am
Location: Oregon

Re: Faster way to draw rectangles?

Post by Zilarrezko »

Alright, so I have no experience what so ever in spritebatches... This is the first time I've looked at them...

I can't do a lot right now, because I'm at school (got chewed out once for being too into my laptop). But in a few hours I'll either edit this post, trying different things and letting you guys know the outcome. Or I'll be stuck doing homework for the rest of the day. Who knows which.

kikito, your idea has struck my mind (Since I implemented something similar to my very bad 3D thing). But I have no idea how to forsee a rectangle being out of the window. Other than if one rectangle is out of bounds of the (let's say first right part of the screen), then if another rectangles' x is greater than the one that's outside of the screen than don't draw it. But for me I think that's going to require the same amount of if then's to every rectangle. Not to mention my newbness of making things over-engineered, and in that making it even slower than it was before.

They're all just tile squares to be honest, and at this point all white/gray squares (I tried line rectangles and apparently those are slower than filled ones). So I don't think I need or will understand spatial databases for a long while.

I may end up using spritebaches though... we'll see.

And Jasoco, The game is no where near what it is going to be like haha. But It's going to be like the thing I mentioned in the what's everyone working on? Thread. Thing of a dwarf fortress adventure mode. (Chances mostly in favor of I'll never make it playable. But I'll learn a lot in the meantime). Except entities and the like won't be restricted to tiles.

But if you'd like to look at the source code so far. It's just a mashup of pretty much everything I've done... Except shaders and stuff.
Attachments
unnamed.love
(9.25 KiB) Downloaded 93 times
User avatar
Zilarrezko
Party member
Posts: 345
Joined: Mon Dec 10, 2012 5:50 am
Location: Oregon

Re: Faster way to draw rectangles?

Post by Zilarrezko »

Alright, you guys thought we were done huh? HAHA nope.

So as far as I see things, in the future I need to use spritebatches.

So I'm confused as how this works...

So... I take a texture... and I assign a whole spritebatch to that texture.

I then assign rectangles that use that texture after using the :bind and before the :unbind.

Then draw it with a draw call.

I kinda get it, and I kinda don't. I use a single spritebatch for every texture, and add the position to it of every rectangle of that texture type, then draw every spritebatch? That not only sounds like it would be iffy on rendering depth for things in front of other things if I go a semi isometric view. But sounds like it still wouldn't do as much performance wise. But then again, I don't know much how this stuff works or even would begin to understand if I looked at the source code :? .

yet...
slime wrote:If the rectangles overlap a lot and they don't change positions relative to each other every frame, it will improve performance to draw them to a Canvas when they do change positions, and draw the Canvas to the main screen (and be sure to use Canvas:clear before drawing to the Canvas.)
It sounds like the more optimal solution is canvas... My game is going to be like the functionality of map and entities in... prison architect. Unless I can't get path smoothing in, then it's more like dwarf fortress.

However I had used canvas... and it didn't really improve performance at all. I mentioned that in my reply to Kindaro.
User avatar
kikito
Inner party member
Posts: 3153
Joined: Sat Oct 03, 2009 5:22 pm
Location: Madrid, Spain
Contact:

Re: Faster way to draw rectangles?

Post by kikito »

Zilarrezko wrote:But I have no idea how to forsee a rectangle being out of the window. Other than if one rectangle is out of bounds of the (let's say first right part of the screen), then if another rectangles' x is greater than the one that's outside of the screen than don't draw it. But for me I think that's going to require the same amount of if then's to every rectangle.
Ok, let me try to explain better.

Let's imagine that your "world" is a 10x10 grid, and each cell is a rectangle (a tile) that you have to draw:

Code: Select all

##########
##########
##########
##########
##########
##########
##########
##########
Drawing "everything" would be done with this loop:

Code: Select all

local function drawMap(map)
  for x=1,map.columns do -- 10 columns
    for y=1,map.rows do -- 10 rows
      drawTile(map,x,y)
    end
  end
end
When you only have 10x10, that's 100 drawing operations - that's ok in all machines. But it goes up very quickly. When you have 100x100, it's 10000, which is already pushing it. 500x500 is 250000, which is too much.

Now imagine that your screen only "sees" a 4x3 subsection:

Code: Select all

##########
##########
##....####
##....####
##....####
##########
##########
##########
A naïve way to draw those tiles only would be this:

Code: Select all

local function drawMap(map,screenLeft, screenTop, screenRight, screenBottom)
  for x=1,map.columns
    for y=1,map.rows
      if x >= screenLeft and x <= screenRight
      and y >= screenTop and y <= screenBottom
      then
        drawTile(x,y)
      end
    end
  end
Notice that now drawMap takes parameters. The function still goes through all tiles, and makes an if; and only when it's true, it draws stuff. This is better than before (usually). But you are still going through all the tiles. When you have 10x10, it does 100 "ifs" and 12 (4x3) drawing operations. But when you have 500x500, you are doing 250000 "ifs" every frame (and 12 drawing ops). The problem are not the drawing operations any more. The problem is the "ifs": you are doing 250000 of them. Even if each individual comparison is fast, there's lots of them, and that ends up taking time.

Now compare that implementation to this:

Code: Select all

local function drawMap(map,screenLeft, screenTop, screenRight, screenBottom)
  for x=screenLeft,screenTop
    for y=screenRight,screenBottom
      drawTile(x,y)
    end
  end
end
This draws the same thing as before, but it is much better. If you have a 10x10 grid, it only does 12 drawing operations. If you have a 500x500 map, it only does 12 drawing operations, too! The amount of calculations that you have to do does not depend any more on the size of your map, but on what your screen sees.

I hope this clarifies things a bit.
When I write def I mean function.
User avatar
Zilarrezko
Party member
Posts: 345
Joined: Mon Dec 10, 2012 5:50 am
Location: Oregon

Re: Faster way to draw rectangles?

Post by Zilarrezko »

Alright, It does a bit. The problem was with camera, but actually now that I think about it, that's pretty easy to implement, do some pluses of some translations, some division magic here and there and then widen the tolerance to allow drawing of rectangles if they would be in the screen even if their x and/or y isn't on screen, but a part of them is.

However, I'd like, at least right now, while I'm testing somethings to zoom out pretty far, to hardcore test pathfinding, path smoothing... just path stuff ya know? So I'd like to zoom almost all the way out, and be able right click (path find to a tile) and not wait 7 millennium and 15 tiberium wars and 2 Kane lifetimes to then see the path.

something like a huge rooms map, or a map from an actual game.

Although, eventually I'd like to limit zooming out. But I have in mind some cases in the future where I'd like users to zoom out enough and not lag out, see what I'm sayin'?

Thanks for helpin' kikito, still helping me since my OOP problem post.

And in that last post I forgot to put in my project if people wanted to see what I have so far, except this is waaaayy too abstract to get an idea of what it's going to be. The "agent" (Is this what we call entities with AI capabilities?) will eventually move independently from the grid (I've said this about 12 times, and I'm sorry.). Although maybe not, depending if I can implement it or not.

So control are like f1 to make a pseudo rooms test thing (So hard to make something good with those 1 and 0 maps, I hate those.) f2 to make like a spiral thing. f3 to designate everything to be demolished. f5 to change heuristic. w a s d to move camera, scroll wheel to zoom, and uh... left click to construct/destruct a wall, and right click to move to that point.
Attachments
nameless.love
(204.43 KiB) Downloaded 108 times
Post Reply

Who is online

Users browsing this forum: No registered users and 48 guests