## [SOLVED][STI]crash when map:resize

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
gotokiichi
Prole
Posts: 6
Joined: Wed Jan 29, 2020 7:41 pm

### [SOLVED][STI]crash when map:resize

I'm using karai17's STI library for my little project. Thanks karai17, it realy helps!
But when I try to implement zoom in or out the tiled map, the program just crashes every runtime without any error infomation to me. I have located the error to map:resize. If I delete this line, everything's ok and stable. I really need this function for zoom in and out the stage map, because the function map:draw with scale will also scale the drawsize of it, so that I need map:resize to change the drawarea back to original. Pls help and suggestion, thanks in advance!

I attach the love file and copy my urgly code as below:
mouse drag to pan the map
mouse wheel up/down to scale the map

Code: Select all

sti = require('sti')
map = sti("assets/map01.lua")
main_canvas = love.graphics.newCanvas()

Game = {}
Game.tileWidth = 48
Game.tileHeight = 48
Game.tileRowNum = 30
Game.tileColNum = 30
Game.xScale = 1
Game.yScale = 1
Game.windowWidth = 48*15
Game.windowHeight = 48*10

local cam, map
local flag_XYRecorded
local dx, dy
local x_start, y_start
local scale --scale for sti
local main_canvas
local floor = math.floor

function clamp(value, min, max)
if max then
if value > max then return max end
end
if min then
if value < min then return min end
end
return value
end

function initCamera()
flag_XYRecorded = false
dx, dy = 0, 0
x_start, y_start = 0, 0
scale = 1
end

function drawWorld(dx, dy)
love.graphics.setCanvas(main_canvas)
love.graphics.clear()
width_change = floor(Game.tileRowNum*Game.tileWidth/scale)
height_change = floor(Game.tileColNum*Game.tileHeight/scale)
map:resize(width_change,height_change)
map:draw(dx,dy,scale, scale)
love.graphics.setCanvas()
love.graphics.draw(main_canvas, 0, 0, 0, 1)
end

function love.wheelmoved(x, y)
if y > 0 then
--Mouse wheel moved up
scale = scale + 0.1
elseif y < 0 then
--Mouse wheel moved down
scale = scale - 0.1
end
scale = clamp(scale, 0.5, 1.0)
end

----------------

map = sti("assets/map01.lua")

initCamera()

main_canvas =  love.graphics.newCanvas(Game.tileRowNum*Game.tileWidth, Game.tileColNum*Game.tileHeight)
end

function love:update(dt)
-- print('update')
if love.mouse.isDown(1) then -- mouse key1 pressed
if not flag_XYRecorded then
local x, y = love.mouse.getPosition()
x_start = x - dx
y_start = y - dy
flag_XYRecorded = true
else
local x, y = love.mouse.getPosition()
dx = x - x_start
dy = y - y_start
end
else -- mouse key1 release
if flag_XYRecorded then
local x, y = love.mouse.getPosition()
dx = x - x_start
dy = y - y_start

dx = clamp(dx, floor(Game.windowWidth/Game.xScale-Game.tileWidth*Game.tileRowNum), 0)
dy = clamp(dy, floor(Game.windowHeight/Game.yScale-Game.tileHeight*Game.tileColNum), 0)

flag_XYRecorded = false
end
end

map:update(dt)
end

function love:draw()
drawWorld(dx,dy)

--debug info
local x, y = love.mouse.getPosition()
love.graphics.setColor(1, 0, 0)
love.graphics.print('scr: '..x..' '..y, 10, 10)
love.graphics.print('map: '..(x-dx*Game.xScale)..' '..(y-dy*Game.yScale), 10, 40)
love.graphics.print('init: '..dx..' '..dy, 10, 70)
love.graphics.print('start: '..x_start..' '..y_start, 10, 100)
love.graphics.print('scale: '..scale, 10, 130)
love.graphics.setColor(1, 1, 1)

end

Attachments
test_sti.love
Last edited by gotokiichi on Thu Jan 30, 2020 2:26 pm, edited 2 times in total.

JJSax
Prole
Posts: 38
Joined: Fri Apr 04, 2014 3:59 am

### Re: [STI]crash when map:resize

I'm not seeing anywhere in there where it calls map:resize(). Perhaps you copied over the code that isn't crashing by mistake. It would surely help to see how you're calling it as well.

Edit: also let us know what the error you're getting is.

gotokiichi
Prole
Posts: 6
Joined: Wed Jan 29, 2020 7:41 pm

### Re: [STI]crash when map:resize

JJSax wrote:
Thu Jan 30, 2020 1:20 am
I'm not seeing anywhere in there where it calls map:resize(). Perhaps you copied over the code that isn't crashing by mistake. It would surely help to see how you're calling it as well.

Edit: also let us know what the error you're getting is.
Pls forgive a sleepy dog:P
I have changed the code and attached the runable love file.

JJSax
Prole
Posts: 38
Joined: Fri Apr 04, 2014 3:59 am

### Re: [STI]crash when map:resize

gotokiichi wrote:
Thu Jan 30, 2020 2:36 am
Pls forgive a sleepy dog:P
I have changed the code and attached the runable love file.
You are definitely forgiven.

I'll preface this by saying I'm not an expert. But I think I fixed it and I have a theory of why it works. It didn't crash right away and just eventually had the error. I dug into the init for sti and found that Map:resize(w,h) sets a new canvas every time it's called. The wiki says that love.graphics.newCanvas can be slow when ran repeatedly, like in a draw loop like you had it. This not only would cause your FPS to drop, but I think it was causing some issues with running too many canvases. Here is what I did. (just the draw loop and the wheel moved functions)

Code: Select all

function drawWorld(dx, dy)
love.graphics.setCanvas(main_canvas)
love.graphics.clear()
-- removed the resize function from here
map:draw(dx,dy,scale, scale)
love.graphics.setCanvas()
love.graphics.draw(main_canvas, 0, 0, 0, 1)
end

function love.wheelmoved(x, y)
if y > 0 then
--Mouse wheel moved up
scale = scale + 0.1
elseif y < 0 then
--Mouse wheel moved down
scale = scale - 0.1
end
-- put it here so it's not called all the time, only when it's needed.
width_change = floor(Game.tileRowNum*Game.tileWidth/scale)
height_change = floor(Game.tileColNum*Game.tileHeight/scale)
map:resize(width_change,height_change)
scale = clamp(scale, 0.5, 1.0)
end

Side note, is there a reason you set main_canvas at the top, then on a separate line make it local? It doesn't pose an issue as far as I know and you're not the only one who I've seen do it, but just curious why you don't set it local to begin with.

gotokiichi
Prole
Posts: 6
Joined: Wed Jan 29, 2020 7:41 pm

### Re: [STI]crash when map:resize

JJSax wrote:
Thu Jan 30, 2020 3:06 am
gotokiichi wrote:
Thu Jan 30, 2020 2:36 am
Pls forgive a sleepy dog:P
I have changed the code and attached the runable love file.
You are definitely forgiven.

I'll preface this by saying I'm not an expert. But I think I fixed it and I have a theory of why it works. It didn't crash right away and just eventually had the error. I dug into the init for sti and found that Map:resize(w,h) sets a new canvas every time it's called. The wiki says that love.graphics.newCanvas can be slow when ran repeatedly, like in a draw loop like you had it. This not only would cause your FPS to drop, but I think it was causing some issues with running too many canvases. Here is what I did. (just the draw loop and the wheel moved functions)

Code: Select all

function drawWorld(dx, dy)
love.graphics.setCanvas(main_canvas)
love.graphics.clear()
-- removed the resize function from here
map:draw(dx,dy,scale, scale)
love.graphics.setCanvas()
love.graphics.draw(main_canvas, 0, 0, 0, 1)
end

function love.wheelmoved(x, y)
if y > 0 then
--Mouse wheel moved up
scale = scale + 0.1
elseif y < 0 then
--Mouse wheel moved down
scale = scale - 0.1
end
-- put it here so it's not called all the time, only when it's needed.
width_change = floor(Game.tileRowNum*Game.tileWidth/scale)
height_change = floor(Game.tileColNum*Game.tileHeight/scale)
map:resize(width_change,height_change)
scale = clamp(scale, 0.5, 1.0)
end

Side note, is there a reason you set main_canvas at the top, then on a separate line make it local? It doesn't pose an issue as far as I know and you're not the only one who I've seen do it, but just curious why you don't set it local to begin with.
Thanks for you such prompt help! You are right, I made a test with counting map:resize time and every time the program just crashed on the 653th resizing. Moving map:resize to wheelmoved callback funtion as your suggestion would delay that crash but it still came on the 653th resizing. No body could escape the end:(

I think only I can do now is freezing the map scale functino to future.

And for that local canvas, there's no reason for that, I just follow BYTEPATH tutorial:D
You can find it here: https://github.com/adnzzzzZ/blog/issues/15

pgimeno
Party member
Posts: 2164
Joined: Sun Oct 18, 2015 2:58 pm

### Re: [STI]crash when map:resize

Yes, JJSax has correctly identified the source of the problem. To prevent it from happening, you can help the garbage collector a bit, e.g. by adding a collectgarbage() call after the resize.

For me it didn't crash, it just became more and more sluggish over time, but the problem disappeared after I added the collectgarbage() call.

The cause of the problem seems to be that LuaJIT does not have information about how many resources a certain object takes, and therefore it does not prioritize: it doesn't collect garbage until many objects (perhaps thousands of them) have accumulated. If those objects happen to be big canvases, the graphics card can't cope with so many of them.

gotokiichi
Prole
Posts: 6
Joined: Wed Jan 29, 2020 7:41 pm

### Re: [STI]crash when map:resize

pgimeno wrote:
Thu Jan 30, 2020 12:13 pm
Yes, JJSax has correctly identified the source of the problem. To prevent it from happening, you can help the garbage collector a bit, e.g. by adding a collectgarbage() call after the resize.

For me it didn't crash, it just became more and more sluggish over time, but the problem disappeared after I added the collectgarbage() call.

The cause of the problem seems to be that LuaJIT does not have information about how many resources a certain object takes, and therefore it does not prioritize: it doesn't collect garbage until many objects (perhaps thousands of them) have accumulated. If those objects happen to be big canvases, the graphics card can't cope with so many of them.
Thanks pgimeno, I will take a try of collectgarbage()!
Yes, it works!
Last edited by gotokiichi on Thu Jan 30, 2020 2:25 pm, edited 1 time in total.

grump
Party member
Posts: 612
Joined: Sat Jul 22, 2017 7:43 pm

### Re: [STI]crash when map:resize

Calling :release() on the old canvas should help as well and is a more localized solution for the problem at hand.

pgimeno
Party member
Posts: 2164
Joined: Sun Oct 18, 2015 2:58 pm

### Re: [SOLVED][STI]crash when map:resize

Eh, good point, thanks grump!

Only problem is that the canvas "belongs" to the lib, so you need to dig into the lib's internals. Maybe the STI maintainers can add code to the library to release the old canvas when creating a new one.

@ gotokiichi That's a guarantee against crashing, but as JJSax said, it's best if you minimize the map:resize() calls, limiting them to the instants where the zoom level changes. An alternative, or addendum, to what he did is to keep the last zoom value in a variable, and only call map:resize() when the zoom actually changes, i.e. when the last zoom differs from the newly calculated zoom.

### Who is online

Users browsing this forum: No registered users and 31 guests