Weird love.thread behaviour or my struggle with loading screen

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
YellowButShort
Prole
Posts: 2
Joined: Sat Apr 04, 2020 6:53 pm

Weird love.thread behaviour or my struggle with loading screen

Post by YellowButShort »

Im actually making a tile-based game that generating tiles ingame, but the bigger map is, the longer it takes to actually generate a map. While generating, screen leaves empty and nothing happens. So I decided to make a loading screen where player can see how long he has to wait. But generator makes game dead for any updates until the generation is finished. This means i cant do anything until the map is finished

Code: Select all

function tile.genTiles(wight, height)
    tileTable["sizeX"] = wight - 1
    tileTable["sizeY"] = height - 1
    
    for h = 0, height - 1 do
        for w = 0, wight - 1 do
            tileTable[w.."/"..h.."_valid"] = true
            tileTable[w.."/"..h.."_quad"] = love.graphics.newQuad(0, 0, 48, 48, 48, 48)		
        end
    end
end

To solve this problem, I decided to put it into thread, so i could have some control on the screen. But it didnt go well.

Code: Select all

function tile.genTiles(wight, height)
    tileTable["sizeX"] = wight - 1
    tileTable["sizeY"] = height - 1

    local threadCode = [[
        Channel = {}
        Channel.inp = love.thread.getChannel("tileChannelIn")
        Channel.out = love.thread.getChannel("tileChannelOut")

        local draw = require "bin.graph" -- looks like it cant take it as global so it is there too
        local tileTable = Channel.inp:demand()

        local progressFinal = tileTable["sizeX"] * tileTable["sizeY"]
        local progress = 0
        local progressOut

        local wight = tileTable["sizeX"] + 1
        local height = tileTable["sizeY"] + 1

        for h = 0, height - 1 do
            for w = 0, wight - 1 do
                progress = progressFinal + 1

                tileTable[w.."/"..h.."_valid"] = true
                tileTable[w.."/"..h.."_quad"] = love.graphics.newQuad(0, 0, 48, 48, 48, 48)

                progressOut = progress / progressFinal
                Channel.out:push(progressOut)
            end
        end
    ]]

    thread = love.thread.newThread(threadCode)
    thread:start()

    Channel = {}
    Channel.inp = love.thread.getChannel("tileChannelIn")
    Channel.out = love.thread.getChannel("tileChannelOut")

    Channel.inp:push(tileTable)
    print("success")
    print(Channel.out:pop()) -- enough to print it into console for testing
end

After launch it fails with this error
attempt to index field 'graphics' (a nil value)

Also i have code to send out information how many unloaded tiles left and it should send out information each with every tile made. For test purpose i actually receiving it once, but it sends out only nil (or nothing, i have no idea how to check it)

I would really appreciate if you help me with existing code or just give me some ideas how could i avoid using cursed thread code
MrFariator
Party member
Posts: 509
Joined: Wed Oct 05, 2016 11:53 am

Re: Weird love.thread behaviour or my struggle with loading screen

Post by MrFariator »

First, you need to require the relevant love.modules with require, like:

Code: Select all

-- at the start of your thread code
require ( "love.graphics" )
Second, love.graphics, among some other modules, can not be used in threads fully as explained in one of the yellow textboxes here. I am not sure if love.graphics.newQuad is permitted; I'd presume no.

What I instead suggest is to break up your generation to 'chunks', in that you keep track of how many tiles you have processed that frame. If the amount exceeds some predetermined value just halt the generation, store where you left off, and pick up the loop from there the next frame. You could then keep track of how many tiles you have processed on, say, the vertical axis, and get a rough estimate for your loading screen from that. Should not require many changes to your for loops that way, and won't require threading.

Another alternative approach is to only process the tiles that are visible to the player (+ some margin). This way you can get shorter loading times, and you can generate more of the tiles as the player moves around. Of course, this approach could potentially cause some stuttering, particularly when a lot of tile generation happens in a single frame.
KayleMaster
Party member
Posts: 234
Joined: Mon Aug 29, 2016 8:51 am

Re: Weird love.thread behaviour or my struggle with loading screen

Post by KayleMaster »

What kind of size are we talking about here?
If you're map is too big it might make sense to split it into chunks, you'll get a lot of other performance boosts by doing so, like rendering at only what you're looking.
You're also concatenating strings for your entry, but it seems like this would be much more efficient:

Code: Select all

function tile.genTiles(wight, height)
    tileTable["sizeX"] = wight - 1
    tileTable["sizeY"] = height - 1
    local tileQuad = love.graphics.newQuad(0, 0, 48, 48, 48, 48)
    
    for x = 1, height do
        tileTable[x] = {}
        for y = 1, wight do
            tileTable[x][y] = {valid = true, quad = tileQuad}
        end
    end
end
I made the array 2 dimensional, made it start from 1 because if it starts from 0 you won't be able to use the ipairs method. (0 is stored as a key, instead of integer index).
So now you can access whether is valid or not like so:

Code: Select all

if tileTable[x][y].valid then ... end
I also took out your quad outside the loop since it's the same quad everytime, what's the point of creating a new one?

P.S. "wight" is actually spelled "width"
YellowButShort
Prole
Posts: 2
Joined: Sat Apr 04, 2020 6:53 pm

Re: Weird love.thread behaviour or my struggle with loading screen

Post by YellowButShort »

MrFariator wrote: Sat Apr 04, 2020 8:11 pm What I instead suggest is to break up your generation to 'chunks', in that you keep track of how many tiles you have processed that frame. If the amount exceeds some predetermined value just halt the generation, store where you left off, and pick up the loop from there the next frame. You could then keep track of how many tiles you have processed on, say, the vertical axis, and get a rough estimate for your loading screen from that. Should not require many changes to your for loops that way, and won't require threading.
Yep, did this and it worked perfectly. Thank you!
KayleMaster wrote: Sun Apr 05, 2020 7:55 am I made the array 2 dimensional, made it start from 1 because if it starts from 0 you won't be able to use the ipairs method. (0 is stored as a key, instead of integer index).
So now you can access whether is valid or not like so:

Code: Select all

if tileTable[x][y].valid then ... end
Didnt know that i can do it that way but it looks much more efficient and prettier. Thanks!
KayleMaster wrote: Sun Apr 05, 2020 7:55 am I also took out your quad outside the loop since it's the same quad everytime, what's the point of creating a new one?

P.S. "wight" is actually spelled "width"
It has to be inside the loop because I cant create a quad that contains every texture of every tile from whole chunk
KayleMaster wrote: Sun Apr 05, 2020 7:55 am P.S. "wight" is actually spelled "width"
English isnt my first language and it i really struggle with remembering the spelling of this word. Ive been trying to remember it for about 2-3 years and still cant lol
KayleMaster
Party member
Posts: 234
Joined: Mon Aug 29, 2016 8:51 am

Re: Weird love.thread behaviour or my struggle with loading screen

Post by KayleMaster »

Don't worry about that last thing, I'm the same haha
Mostly messing up height and heigth
Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot], Google [Bot] and 30 guests