Post-0.10.0 feature wishlist

General discussion about LÖVE, Lua, game development, puns, and unicorns.
Post Reply
User avatar
spill
Prole
Posts: 27
Joined: Thu May 07, 2015 1:53 am
Contact:

Re: Post-0.10.0 feature wishlist

Post by spill »

bartbes wrote:Right:

Code: Select all

self.x = self.x + self.velocity.x*dt
self.y = self.y + self.velocity.y*dt
I suppose the "best" solution would be a method

Code: Select all

self.pos:scaledAdd(self.velocity, dt)
but it's not exactly as nice.
The best two solutions to the problem that I can think of are:
1. Have vectors be a primitive datatype in Lua. (Wishful thinking)
2. Implement vectors as immutable userdata objects and intern them. That way, stuff like "dt*GRAVITY" would not create more than one new object per frame, and it would create none if the timestep is the same as a previous frame. In my own C vector module, I experimented with using fixed point representations of the two components, which would make it more likely to re-encounter previously used vectors (I had a few other motivations, but that's one of the nice side effects). Still, you'd need some sort of policy to determine how many vectors to intern and some eviction policy as well. If I were going to implement it, I'd probably use a hash table and evict old entries every time there's a hash collision, to approximate LRU.
User avatar
murks
Party member
Posts: 185
Joined: Tue Jun 03, 2014 4:18 pm

Re: Post-0.10.0 feature wishlist

Post by murks »

On the topic of 0.10.0, I loved the addition of video playback as this was very painful before, the only possibility was to play a sequence of jpegs or other images. Even if ogg theora is not the very best codec out there, it is way better than playing jpegs, especially wrt. file size.

I personally don't care much about the mobile stuff, but I guess it is nice to have. It is a couple of years late if you plan to make money though.

I don't really miss anything. Most stuff can be done through libraries, especially those by kikito and vrld.

What is a bit painful is audio. There are essentially two libraries I know, SLAM and TEsound. Both implement similar features, most importantly playing the same sound multiple times at once, so that might be things worth implementing. SLAM is nice but by overwriting love core functions breaks other functionality like video loading (with sound enabled) or kikito's love-loader. TEsound seems to use a different approach but was rather buggy last time I tried it and does not seem to be well maintained.

I don't know whether the SLAM approach could be made compatible, TEsound could probably be improved, but I would very much prefer to have that rather basic functionality in löve itself.
User avatar
airstruck
Party member
Posts: 650
Joined: Thu Jun 04, 2015 7:11 pm
Location: Not being time thief.

Re: Post-0.10.0 feature wishlist

Post by airstruck »

Probably off-topic at this point, but re. vectors, I wonder if a more functional approach might make a good compromise between readability and performance; for example maybe something like this:

Code: Select all

local function extract (...) return ... end

local function Vector (x, y)
    return function (operate)
        return (operate or extract)(x, y)
    end
end

local function plus (x, y)
    return function (vector)
        local x2, y2 = vector(extract)
        return Vector(x + x2, y + y2)
    end
end

local function times (x, y)
    return function (vector)
        local x2, y2 = vector(extract)
        return Vector(x * x2, y * y2)
    end
end

local gravity = Vector(1, 5)
local position = Vector(10, 15)
local velocity = Vector(2, 1)

velocity = velocity(times)(gravity)
position = position(plus)(velocity)

print(position())
It won't produce a lot of tables, at least, and it's fairly readable, but how does it compare to the tables-with-metamethods approach, and how does it fare against the primitive value inputs and outputs approach?

Not suggesting this as part of Love's API of course, just wondering how it stacks up against the other approaches in general.
Last edited by airstruck on Fri Jan 22, 2016 5:52 am, edited 1 time in total.
User avatar
spill
Prole
Posts: 27
Joined: Thu May 07, 2015 1:53 am
Contact:

Re: Post-0.10.0 feature wishlist

Post by spill »

airstruck wrote:Probably off-topic at this point, but re. vectors, I wonder if a more functional approach might make a good compromise between readability and performance; for example maybe something like this:
[...]
It won't produce a lot of tables, at least, and it's fairly readable, but how does it compare to the tables-with-metamethods approach, and how does it fare against the primitive value inputs and outputs approach?

Not suggesting this as part of Love's API of course, just wondering how it stacks up against the other approaches in general.
You're basically just allocating a bunch of functions and closures to store the data instead of tables. It's less readable and it doesn't solve the memory churn problem because you're still doing at least one allocation per math operation. You'd also be sacrificing a lot of readability compared to the regular table implementation of vectors.
User avatar
spill
Prole
Posts: 27
Joined: Thu May 07, 2015 1:53 am
Contact:

Re: Post-0.10.0 feature wishlist

Post by spill »

spill wrote:You're basically just allocating a bunch of functions and closures to store the data instead of tables. It's less readable and it doesn't solve the memory churn problem because you're still doing at least one allocation per math operation.
To see that the functional style does not save memory allocations, here's some simple code using lua's collectgarbage() function:

Code: Select all

collectgarbage('stop') -- disable automatic garbage collection
collectgarbage('collect')

-- Functional style
do
    local function createReturner(x)
        return function() return x end
    end
    local t
    for i=1,10000 do
        t = createReturner(i)
    end
    t = nil
    local memory = collectgarbage('count')
    collectgarbage('collect')
    print("functional method uses (Kb):", memory - collectgarbage('count'))
end

collectgarbage('collect')

-- Just using regular tables
do
    local t
    for i=1,10000 do
        t = {i}
    end
    t = nil
    local memory = collectgarbage('count')
    collectgarbage('collect')
    print("table method uses (Kb):", memory - collectgarbage('count'))
end

collectgarbage('collect')

-- Just using numbers
do
    local t
    for i=1,10000 do
        t = i
    end
    t = nil
    local memory = collectgarbage('count')
    collectgarbage('collect')
    print("numbers uses (Kb):", memory - collectgarbage('count'))
end
this produces the output:

Code: Select all

functional method uses (Kb):	780.65625
table method uses (Kb):	703.125
numbers uses (Kb):	0
User avatar
airstruck
Party member
Posts: 650
Joined: Thu Jun 04, 2015 7:11 pm
Location: Not being time thief.

Re: Post-0.10.0 feature wishlist

Post by airstruck »

Interesting, thanks for testing it. I figured tables and functions would have different memory overhead, but wasn't sure which would come out on top. I do think it's fairly readable, but that's just a matter of opinion. I'm still curious how two vector implementations would compare instead of just dummy data, but I don't expect any improvement in the fp approach (it might get worse, there are more closed-over values than in the dummy test).

I assume you tested this in 5.3. I think collectgarbage('collect') is actually restarting the GC in 5.1 and LuaJIT (but not 5.2 or 5.3); I got very different results (much more consistent with results from 5.2) when calling collectgarbage('stop') before each section of the test, and calling cg('collect') before cg('stop') instead of after. It looks like under LuaJIT the functional approach performs slightly better than tables (+15% or so) for this particular test.
LuaJIT wrote:functional method uses (Kb): 468.75
table method uses (Kb): 546.875
numbers uses (Kb): 0
In 5.2, the results are nearly identical (~781), and in 5.1 I got 859.375 for functions and 781.25 for tables.
User avatar
spill
Prole
Posts: 27
Joined: Thu May 07, 2015 1:53 am
Contact:

Re: Post-0.10.0 feature wishlist

Post by spill »

airstruck wrote:Interesting, thanks for testing it. I figured tables and functions would have different memory overhead, but wasn't sure which would come out on top. I do think it's fairly readable, but that's just a matter of opinion. I'm still curious how two vector implementations would compare instead of just dummy data, but I don't expect any improvement in the fp approach (it might get worse, there are more closed-over values than in the dummy test).
The problem is not how much memory they take up, it's how many memory allocations they perform. In total, 10,000 vectors are only using a few hundred Kb of RAM, which is pretty negligible. However, the code is performing a lot of memory allocations, all of which need to be cleaned up during garbage collection. There's also some function call overhead, because "dt*vel" calls a function, but "dt*velx" does not. Anyway, the speed is the real issue and here's some test code that shows why:

Code: Select all

local vec = require "hump_vector"
collectgarbage('collect')
collectgarbage('stop')
do
    local start = os.clock()
    local p = vec(0,0)
    local v = vec(1,1)
    local dt = 1/60
    for _=1,1000 do
        for _=1,1000 do
            p = p + dt*v
        end
        collectgarbage('collect')
    end
    print("hump vectors took", os.clock() - start)
end

collectgarbage('collect')
do
    local function extract(...) return ... end
    local function Vector(x,y)
        return function(operator) return (operator or extract)(x,y) end
    end
    local function times (x,y)
        return function (c)
            return Vector(c * x, c * y)
        end
    end
    local function plus (x, y)
        return function (vector)
            local x2, y2 = vector(extract)
            return Vector(x + x2, y + y2)
        end
    end
    local start = os.clock()
    local p = Vector(0,0)
    local v = Vector(1,1)
    local dt = 1/60
    for _=1,1000 do
        for _=1,1000 do
            p = p(plus)(v(times)(dt))
        end
        collectgarbage('collect')
    end
    print("functions took", os.clock() - start)
end

collectgarbage('collect')
do
    local start = os.clock()
    local x,y = 0,0
    local vx,vy = 1,1
    local dt = 1/60
    for _=1,1000 do
        for _=1,1000 do
            x,y = x+dt*vx, y+dt*vy
        end
        collectgarbage('collect')
    end
    print("components took", os.clock() - start)
end
Which produces this output (numbers are in seconds to perform 2 million vector operations and 1,000 garbage collections):

Code: Select all

hump vectors took	2.799155
functions took	3.679628
components took	0.079730000000001
You can see that both types of vector are considerably slower than just using components. However, you'd have to do some real profiling of a game to see how much of the game's actual performance that is, because it's only 1.4 microseconds per vector operation (amortized). I think it's worth the performance tradeoff if you're working with a few dozen or a few hundred objects (~0.01-0.1 ms per frame), but if you were working with code that did 10,000 vector operations per frame, I can see why you'd be unhappy if LÖVE forced you into using vectors.
airstruck wrote:I assume you tested this in 5.3. I think collectgarbage('collect') is actually restarting the GC in 5.1 and LuaJIT (but not 5.2 or 5.3); I got very different results (much more consistent with results from 5.2) when calling collectgarbage('stop') before each section of the test, and calling cg('collect') before cg('stop') instead of after. It looks like under LuaJIT the functional approach performs slightly better than tables (+15% or so) for this particular test.
As far as collectgarbage() goes, in 5.1, 5.2, and 5.3, "stop" is used to stop automatic garbage collection and "restart" is used to resume. "collect" runs a single pass of the garbage collection.
User avatar
airstruck
Party member
Posts: 650
Joined: Thu Jun 04, 2015 7:11 pm
Location: Not being time thief.

Re: Post-0.10.0 feature wishlist

Post by airstruck »

Another interesting test, thanks for sharing it. What version of Lua are you testing with? The results I got look a little different:

Code: Select all

#lua5.1
 
hump vectors took	5.409683
functions took	4.237445
components took	0.199829

#lua5.2 

hump vectors took	6.43021
functions took	4.594765
components took	0.171784

#luajit 

hump vectors took	0.107289
functions took	2.694668
components took	0.062893
I thought it might better reflect a real-world scenario if you took the collectgarbage calls out for the speed test. It didn't seem to affect the 5.1 and 5.2 tests much, but the LuaJIT test looks much different:

Code: Select all

#luajit 

hump vectors took	0.010799
functions took	2.780322
components took	0.0026269999999999
I guess collectgarbage is somehow keeping the JIT from working its magic. Hump's vectors look pretty good here.
spill wrote:As far as collectgarbage() goes, in 5.1, 5.2, and 5.3, "stop" is used to stop automatic garbage collection and "restart" is used to resume. "collect" runs a single pass of the garbage collection.
I know that's what the manual says, but check this out:

Code: Select all

if jit then jit.off() end
collectgarbage('collect')
collectgarbage('stop')
-- collectgarbage('collect')
do
    local t
    for i = 1, 10000 do
        t = {}
    end
    t = nil
    local memory = collectgarbage('count')
    collectgarbage('collect')
    print('Memory used (Kb):', memory - collectgarbage('count'))
end

Code: Select all

#lua5.1
Memory used (Kb):	625

#lua5.2 
Memory used (Kb):	625

#luajit 
Memory used (Kb):	312.5
Now when you uncomment that commented collectgarbage('collect'), look what happens:

Code: Select all

#lua5.1
Memory used (Kb):	9.3125

#lua5.2 
Memory used (Kb):	625

#luajit 
Memory used (Kb):	16.34375
That's a huge drop-off, my guess was that collectgarbage('collect') had restarted the GC pre-5.2, of course it could be something else but I'm not sure what.

Anyway, sorry for going so far off-topic with this.
User avatar
Guard13007
Party member
Posts: 132
Joined: Sat Oct 25, 2014 3:42 am
Location: Internet, USA
Contact:

Re: Post-0.10.0 feature wishlist

Post by Guard13007 »

Sorry to be super late and bring up an old comment on here, but I didn't see anyone replying to this..
Tjakka5 wrote:As stated in the wiki, the function love.touch.getPressure() will usually return 1, as most devices do not support pressure.
However, you could somewhat simulate pressure by getting the surface of the object touching the screen.

That is, when the player is softly touching the screen not his whole fingertip will be on the screen, but as he applies more pressure more skin would be pressed against the screen.

I was hoping this could somehow be supported also, so we can atleast have some degree of "pressure" control.
You can't do that. Capacitive touch screens (of which the majority if not all Android (and certainly all iOS) devices are) can only detect the focus of a touch, not the "shape" of what is creating the touch.

(I'm only 99% certain of that though, so if anyone knows better / knows where I can learn more about capacitive touch, I'd love to hear it.)
User avatar
slime
Solid Snayke
Posts: 3160
Joined: Mon Aug 23, 2010 6:45 am
Location: Nova Scotia, Canada
Contact:

Re: Post-0.10.0 feature wishlist

Post by slime »

iOS does have APIs to get the estimated radius of a touch press (and the accuracy of the estimation) – I'm not sure about Android.

However it's a distinct and separate feature from touch pressure, e.g. you can combine them. I wouldn't want to emulate pressure with a radius.
Post Reply

Who is online

Users browsing this forum: No registered users and 1 guest