speed

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
1Minus2P1Stringer2
Prole
Posts: 10
Joined: Mon May 13, 2024 4:05 pm

speed

Post by 1Minus2P1Stringer2 »

im making a z buffer, so im testing speed. why does this run so slow?
Attachments
Render.lua
(576 Bytes) Downloaded 14 times
main.lua
(715 Bytes) Downloaded 11 times
conf.lua
(640 Bytes) Downloaded 11 times
RNavega
Party member
Posts: 287
Joined: Sun Aug 16, 2020 1:28 pm

Re: speed

Post by RNavega »

You're doing it in a brute force way with lots of redundant work. "love.draw()" is supposed to only have very fast code, as it executes at real-time speeds like 60FPS or more.
But in your case your love.draw() will end up calling love.filesystem.load() to read a hard disk file, interpreting it as Lua, and executing draw commands (also in an unoptimized way).

This is why throughout the wiki you have these warnings on the pages of API functions that load stuff from the hard disk:
temp.png
temp.png (13.83 KiB) Viewed 218 times
For software rasterization you can make it much faster by plotting pixels directly into the byte data of an image, then drawing the image to screen.
I'm using this code to plot a grid image, you can repurpose it for whatever you want:

Code: Select all

local ffi = require('ffi')

local UINT8_PTR_TYPEOF = ffi.typeof('uint8_t*')
local FLOAT_PTR_TYPEOF = ffi.typeof('float*')
local SIZEOF_FLOAT     = ffi.sizeof('float')

-- Returns a LÖVE ByteData object, as well as its uint8_t FFI pointer.
-- The pointer is for modifying the contents.
-- Use makeFloatData() when it's for a GLSL uniform.
local function makeByteData(totalBytes)
    local data = love.data.newByteData(totalBytes)
    return data, UINT8_PTR_TYPEOF(data:getFFIPointer())
end

-- For use with GLSL uniforms.
local function makeFloatData(totalFloats)
    local data = love.data.newByteData(totalFloats * SIZEOF_FLOAT)
    return data, FLOAT_PTR_TYPEOF(data:getFFIPointer())
end

local function plotImage(width, height)
    local BYTES_PER_PIXEL = 1
    local data, ptr = makeByteData(width * height * BYTES_PER_PIXEL)

    for y = 0, height - 1 do
        local rowIndex = y * width
        for x = 0, width - 1 do
            local alpha = ... -- Somehow calculate a floating point in the range [0, 1].
            ptr[x + rowIndex] = math.floor(alpha * 255.0)
        end
    end
    local imageData = love.image.newImageData(width, height, 'r8', data)
    local image = love.graphics.newImage(imageData)
    image:setWrap('clamp', 'clamp')
    return image
end
You can also use the hardware-accelerated Z buffer, as that would be even faster. Read more in here: https://love2d.org/wiki/love.graphics.setDepthMode

Edit: that plotImage() function creates a new image each time it's called, because that was my usecase (create an image once so I can draw it over and over). If you want to render different things onto it on each frame then you need to modify that function to also return the 'imageData' object, so you can keep modifying it and using it with image:replacePixels(), so each frame the only thing that changes is the contents imageData object, pointed to by imageData:getFFIPointer().
1Minus2P1Stringer2
Prole
Posts: 10
Joined: Mon May 13, 2024 4:05 pm

Re: speed

Post by 1Minus2P1Stringer2 »

"You're doing it in a brute force way with lots of redundant work. "love.draw()" is supposed to only have very fast code, as it executes at real-time speeds like 60FPS or more.
But in your case your love.draw() will end up calling love.filesystem.load() to read a hard disk file, interpreting it as Lua, and executing draw commands (also in an unoptimized way)"

Oh I see. So are you saying that since love.draw is a real time updater, based on frames rather than runtime, it can only do something so fast, instead, you should load the image using a faster method, then draw it. Also, when you say (also in an unoptimized way) Do you mean the code? Would there be a way to write it to execute faster? I split it up as multiple files to load functions and operations in a cleaner way. In my case im using a z-buffer to make a 3d engine. So, dependent on what it is that needs to be loaded, i need to load that image, then draw it, but, if I cant use love.draw, how will I update it? I fear that love.update will act the same way.
1Minus2P1Stringer2
Prole
Posts: 10
Joined: Mon May 13, 2024 4:05 pm

Re: speed

Post by 1Minus2P1Stringer2 »

Also thanks very much for the code. Dont know anything about how bytedata, because I have not found out the format for images or not. I should probaly learn that.
User avatar
dusoft
Party member
Posts: 535
Joined: Fri Nov 08, 2013 12:07 am
Location: Europe usually
Contact:

Re: speed

Post by dusoft »

1Minus2P1Stringer2 wrote: Mon Jun 10, 2024 9:41 pm Oh I see. So are you saying that since love.draw is a real time updater, based on frames rather than runtime, it can only do something so fast, instead, you should load the image using a faster method, then draw it. Also, when you say (also in an unoptimized way) Do you mean the code? Would there be a way to write it to execute faster? I split it up as multiple files to load functions and operations in a cleaner way. In my case im using a z-buffer to make a 3d engine. So, dependent on what it is that needs to be loaded, i need to load that image, then draw it, but, if I cant use love.draw, how will I update it? I fear that love.update will act the same way.
You should offload your asset loading to love.load or scene/state load functions. love.update is meant exactly just for updates (math, recalculations, computing, speed regulation/dt, partially also input handling) and love.draw for drawing on screen.
1Minus2P1Stringer2
Prole
Posts: 10
Joined: Mon May 13, 2024 4:05 pm

Re: speed

Post by 1Minus2P1Stringer2 »

Not for assets, Like for other stuff like pixel color, position, stuff like that for. I see. So love.update DOESNT go by frames it goes by runtime. right?
RNavega
Party member
Posts: 287
Joined: Sun Aug 16, 2020 1:28 pm

Re: speed

Post by RNavega »

1Minus2P1Stringer2 wrote: Mon Jun 10, 2024 9:41 pm Oh I see. So are you saying that since love.draw is a real time updater, based on frames rather than runtime, it can only do something so fast, instead, you should load the image using a faster method, then draw it.
The difference between (or rather, the meanings of) love.update(dt) and love.draw() is in how they're called by LÖVE, more specifically inside the love.run() function. By default it has the mainloop of your LÖVE program, but you can (expertly and carefully, of course) override it to customize how your program runs, but usually this isn't needed and it's just something useful to know about.
Anyway, the default code to love.run() can be found in here:
Also, when you say (also in an unoptimized way) Do you mean the code? Would there be a way to write it to execute faster? I split it up as multiple files to load functions and operations in a cleaner way. In my case im using a z-buffer to make a 3d engine. So, dependent on what it is that needs to be loaded, i need to load that image, then draw it, but, if I cant use love.draw, how will I update it? I fear that love.update will act the same way.
Sorry, I should've been clearer.
For real-time, or even near-real-time, it's not efficient to have a Lua function call for each pixel on screen, as well as using one or more love.graphics.* call for each pixel. It's a lot of overhead.

Using a couple of X and Y FOR loops to iterate on each pixel and setting their R,G,B,A etc bytes on an FFI buffer like shown is more efficient, especially after the LuaJIT interpreter compiles this frequently called Lua code into a very fast form (still not as fast as using the Z buffer from the GPU, but at least faster than what's giving you the slow result right now).
So to reiterate, creating an ImageData object and an Image object to plug it into. Then on each frame you update the byte data pointed to by the pointer of the ImageData, and call the :replacePixels() from that Image object so it's refreshed. Including rearranging the moment when you call love.filesystem.load() to be outside frequently called functions like @dusoft mentioned, this all should be very fast.
RNavega
Party member
Posts: 287
Joined: Sun Aug 16, 2020 1:28 pm

Re: speed

Post by RNavega »

...also, you only need to bother with ImageData + Image objects if you actually need to visualize / debug the depth buffer at all by drawing it on screen.
Usually it's an internal buffer used to keep track of the depth of pixels of triangles that were already rendered on screen, so you technically don't have to bother visualizing it unless it's for some debugging purpose.
Post Reply

Who is online

Users browsing this forum: Google [Bot] and 3 guests