stetre / moonlibs

Lua libraries for graphics and audio programming
215 stars 11 forks source link

Alternative renderers #4

Closed awsdert closed 4 years ago

awsdert commented 4 years ago

First I'll link something I found today which could be used as a renderer: https://github.com/zauonlok/renderer

Now onto the main one/s that I wanted to know if you plan to make a library for, X11 & Wayland, instead of taking GPU resources from a simultaneously running game (look here for an example of how that would happen, https://github.com/awsdert/gasp) a straight up hook to the common base libraries that linux distros use would be helpful in reducing slowdown. In meantime I'm gonna ask the nuklear developer if there is a non-framerate mode (ie only redraw after a call to nkupdate or something)

stetre commented 4 years ago

It's not in my plan to write direct bindings to lower level OS API, if that's the question. I honestly don't see what performance gain they would give over using GLFW that already does a good job in abstracting them away. The resources available on your system are the same either if you access them via GLFW or directly via lower level APIs.

As for the non-framerate mode, the rate at which Nuklear's GUI are updated and redrawn is already under the control of the application. Is there anything I'm missing?

awsdert commented 4 years ago

Ah, I thought that OpenGL relied on the graphics card, thanks for clearing that up, do you think using moonvulkan would improve the situation at all?

awsdert commented 4 years ago

Only just noticed the question, responded to first half while I had the response in mind, as for the framerate thing, I've only been relying on the example from the moonlibs combo, I didn't really look at the details of the boot up process (or drawing), only edited the variables a bit to be used the way I wanted to, mind telling me what I should look for to both of the original example and of available functions to achieve the non-redraw mode with?

stetre commented 4 years ago

Just look at any example in moonnuklear, e.g. examples/template.lua. Its main loop is typical for a GLFW based application (and even if you used something else than GLFW, you'd likely have something similar, just with different function calls).

It looks something like this:

while not glfw.window_should_close(window) do
   glfw.wait_events_timeout(timeut)
   ...
   glfw.swap_buffers(window)
end

The wait_events_timeout( ) call puts the application to sleep until either some input is available or the timeout expires. This gives you control on the rate of execution of whatever you put in place of the three dots. If, for example, you put there the code that draws a frame, your application would ideally draw 1/timeout frames per second.

This is only a first level of control, though. An application may have several tasks to be executed at different rates, so it would typically choose a timout such that the loop runs at the rate of the task that needs to be done more often, and at each iteration it will execute only those tasks whose time has come.

stetre commented 4 years ago

Now that I have a few minutes I'd like to try to answer to this:

Ah, I thought that OpenGL relied on the graphics card, thanks for clearing that up, do you think using moonvulkan would improve the situation at all?

OpenGL does indeed rely on the graphics card, as Vulkan does. More precisely, they are just APIs that rely on an 'implementation' that is usually part of by the GPU's driver and that uses the GPU to perform the operations specified by API.

Both, however, are designed in such a way to be platform agnostic, meaning that they expect the application to create and provide the surface where to draw. This 'surface' is typically (but not necessarily) a OS window, which the application must create using APIs provided by the specific OS, such as X11, Wayland, etc.

The GLFW library does just this, as says the pitch on their site: "It provides a simple API for creating windows, contexts and surfaces, receiving input and events."

This is to say that GLFW doesfor you the same as what you would do via X11, Wayland, etc, but it is not an alternative to them: it uses one of them, depending on which system you are running.

awsdert commented 4 years ago

Hmm, looking at the example you gave there it seems I'm already doing that: https://github.com/awsdert/gasp/blob/ff21cfd2628d4dd6e5c67a6c9163b91831400e71/OTG/lua/gui_mode.lua#L262

    while not glfw.window_should_close(GUI.window) do
        GUI.cfg.window.width, GUI.cfg.window.height =
            glfw.get_window_size(GUI.window)
        glfw.wait_events_timeout(1/(GUI.cfg.fps or 30))
        rear.new_frame()
        GUI.idc = 0
        draw_all(GUI.ctx)
        if GUI.keep_cheat then
            GUI.cheat = rebuild_cheat( GUI.keep_cheat )
        end
    end

Perhaps I'm misunderstanding something here? What do you see as happening from the above?

stetre commented 4 years ago

This should ideally execute one iteration each 30 seconds (or whatever the value in 'GUI.cfg.fps' is) and at every iteration draw a GUI frame and execute the if statement at the bottom. So both these tasks execute at the same rate.

Note that I often use the world 'ideally' here, because firstly you are not running a real time OS, and secondly it's a single threaded application: if any of the functions called in the loop takes a lot of time to execute, it slows down everything (and worse, if it blocks somewhere it blocks everything).

BTW, you can easilly profile your main loop by just inserting some prints. For example:

local now, since = glfw.now, glfw.since

while not glfw.window_should_close(GUI.window) do
    GUI.cfg.window.width, GUI.cfg.window.height = glfw.get_window_size(GUI.window)
    glfw.wait_events_timeout(1/(GUI.cfg.fps or 30))
    local t = now()
    rear.new_frame()
    print("1", since(t)); t=now()
    GUI.idc = 0
    draw_all(GUI.ctx)
    print("2", since(t)); t=now()
    if GUI.keep_cheat then
        GUI.cheat = rebuild_cheat( GUI.keep_cheat )
    end
    print("3", since(t))
end
awsdert commented 4 years ago

Well it is part multi-threaded (the scanner) but is there a way to check if a redraw should be done without loosing interactivity? Also with just a glance at the functions in the file I linked do you have any suggestions for removing the need for wait_events_timeout() so that the aforementioned check can be done? The main problem I experience with the UI is a delay from click to the UI noticing a click, I sometimes have to hold down the mouse button just for it to notice which is what caused me to make this thread.

stetre commented 4 years ago

Are you sure that the delay is caused by too many redraws, or by the redraws being too expensive? I suggested you to profile precisely to investigate this.

However. There are two alternatives to glfw.wait_events_timout(): glfw.poll_events() and glfw.wait_events(). One of them is needed otherwise you would never detect input events. (For details, search for the corresponding GLFW functions in the manual at this page: https://www.glfw.org/docs/3.3/group__window.html )

Maybe for your application the glfw.poll_events() is more suitable. With it, the loop iteration is computed as faster as possible which should make your application more responsive but also make it use more CPU resources. As for drawing only when needed, you can surely do it, if you know how to recognize the 'when needed' condition. Nuklear by itself does not draw anything: it's the application that tells it when and what to draw (perhaps in backend code, which you can copy and adapt, if you need so).

But again, I'd highly suggest you to first profile to find out where the delay arises, before trying any solution.

awsdert commented 4 years ago

Ty :) I'll try out poll_events because there are parts of the GUI that would have to be redrawn always, I didn't know the functions to look up hence I could only go with what the example code had, now I have something I can try when I get home

*Edit: Initial tests definitely feel more responsive, will try in the scanner interface where I normally experienced a noticeable delay another day, tonight however I must sleep

stetre commented 4 years ago

Yeah, I guess that's the function that is typically used for games, to achieve both the maximum fps and maximum responsiveness. The downside of it is that it makes the application consume a lot of CPU time, that's why I usually resort to wait_events_timeout() in the examples.

awsdert commented 4 years ago

as long as making system calls the CPU time is a non-issue because the system will use those calls as a chance to detect if it should delay the action and pass over to another app's action instead (in other words multi-task), apparantly there are other ways also to look for such chances but I only remember the outline of that one.

On another note I hit a possible bug with either lua or nuklear (as unlikely as that seems I cannot glean enough information to say otherwise)

Currently the error reads like this from the output:

~/gasp/OTG/lua/scan.lua:74: bad argument #3 to 'combo' (invalid value)

And the section of code reads like this:

        tmp = scan.region or 1
        if type(tmp) ~= "number" or tmp < 1 then
            tmp = 1
        end

        v = list.txt[1]
        print("tmp = " .. tostring(tmp) )
        print("list.txt = " .. tostring(list.txt) )
        scan.region = nk.combo(
            ctx, list.txt, tmp,

I was trying the scanner (which does feel more responsive in the UI also, just not got the thread running to completion yet) to see where the next error occurs only to be stopped by this unexpected error, any ideas how I could glean more information or do you have an idea of what might be causing the error?

stetre commented 4 years ago

That error occurs when you are not passing a Lua integer as the 3rd arg to nk.combo(). A Lua "number" would also cause an error, even if it had an integer representation like, e.g. the number 1.0.

awsdert commented 4 years ago

still got the error after changing the code to this:

tmp = scan.region or 1
        if (type(tmp) ~= "number" and type(tmp) ~= "integer")
        or tmp < 1 then
            tmp = 1
        end
        tmp = tointeger(tmp)

        v = list.txt[1]
        print("tmp = " .. tostring(tmp) )
        print("list.txt = " .. tostring(list.txt) )
        scan.region = nk.combo(

tointeger() being defined like so:

function tointeger(val)
    return math.floor(tonumber(val) or 0)
end

Short of creating a C function which explicitly returns an integer do you have any other ideas?

stetre commented 4 years ago

The Lua type() function never returns the string "integer": it retuns "number" also for integers because they are just a subtype added in Lua 5.3. You should use math.tointeger or math.type, instead. For example:

tmp = math.tointeger(scan.region) or 1
...

EDIT: corrected the example.

stetre commented 4 years ago

As for this:

as long as making system calls the CPU time is a non-issue because the system will use those calls as a chance to detect if it should delay the action and pass over to another app's action instead

The difference between using wait_events_timeout() and poll_events() is that with the latter you are continuously saying the OS "schedule me for execution as soon as you can", while with the former you are saying "unless there is some input, there is no need for you to shedule me before timeout seconds from now (you can schedule other processes instead, in the meanwhile)". This results in the OS calling you fewer times, and so in you (well, your process, or app) consuming less CPU time.

If you are familiar with socket programming, this is very similar to how the timeout argument works in the POSIX select(). It may be more than similar, actually (I never looked into it, but those GLFW function may as well be implemented using select(), at least on POSIX systems).

awsdert commented 4 years ago

ty, I'll give it a try next chance I get (which us l8 2mw) since the C function method didn't work out

awsdert commented 4 years ago

Got round to trying today but found a strange error occurring, might be due to recent updates causing moongl to be out of date, here's the error with paths edited to less sensitive versions:

make --no-print-directory
Already up to date.
Already up to date.
Already up to date.
Already up to date.
cc -fpic -I /usr/include/lua5.3 -Wall -Wextra -Wpedantic -std=c99 -D _GNU_SOURCE -D LINUX -D LUA_USE_LINUX -D LUA_USE_READLINE -D LUAVER=5.3 -D _FILE_OFFSET_BITS=64 -D _DEFAULT_SOURCE -o gasp.c.o -c gasp.c -llua -ldl -lm -lpthread
cc -fpic -o gasp.elf gasp.c.o space.c.o nodes.c.o proc.c.o arguments.c.o lua_common.c.o lua_process.c.o -llua -ldl -lm -lpthread
./gasp.elf
pkexec ~/gasp/gasp.elf --inrootmode -D PATH="..." -D PWD="~/gasp" -D CWD="~/gasp" -D HOME="/home/zxuiji" -D DISPLAY=":0" -D XDG_CURRENT_DESKTOP="X-Cinnamon" -D GDMSESSION="cinnamon" -D GASP_PATH="~/gasp/OTG" -D LUA_CPATH="~/gasp/?.so" -D LUA_PATH="~/gasp/?.lua;
error loading module 'moongl' from file '~/gasp/moongl.so':
    libGLEW.so.2.1: cannot open shared object file: No such file or directory
...
Compilation finished successfully.

Any ideas if this is down to moongl relying on a probably now out of date library or it just a path finding issue?

stetre commented 4 years ago

I presume the latter, or libglew is not installed (or not installed properly).

MoonGL does indeed rely on GLEW which despite being old, still does the job. I plan to either replace it with GLAD in the future, or to get rid of it altogether and write my own library loading code as I did in other libs, but this is waaaay down on my TODO list (OpenGL headers are a mess, and I'm sure that the very moment I start doing it it'll take me a month...)

awsdert commented 4 years ago

Just did a search on libGLEW.so from / and got this list:

/lib/libGLEW.so
/usr/lib32/libGLEW.so
/lib/libGLEW.so.1.10
/usr/lib32/libGLEW.so.1.10
/lib/libGLEW.so.1.10.0
/usr/lib32/libGLEW.so.1.10.0
/lib/libGLEW.so.2.2
/usr/lib32/libGLEW.so.2.2
/lib/libGLEW.so.2.2.0
/usr/lib32/libGLEW.so.2.2.0

by which I judge the moongl to be relying on an out of date library, anyway you can fix that your end?

stetre commented 4 years ago

My end I just link with the `-lGLEW' flag, no particular version implied. Seems like you updated libglew from 2.1 to 2.2. Have you tried recompiling moongl?

awsdert commented 4 years ago

Just done now, thought I'd mention the error that popped up before I try running gasp again

cc -O2 -Wall -Wextra -Wpedantic -DCOMPAT53_PREFIX=moongl_compat_ -std=gnu99 -DLUAVER=5.3 -fpic -DLINUX -I/usr/include -I/usr/include/lua5.3 -I/usr/local/include/lua5.3   -c -o draw.o draw.c
draw.c: In function ‘MultiDrawElementsBaseVertex’:
draw.c:282:54: warning: passing argument 4 of ‘__glewMultiDrawElementsBaseVertex’ from incompatible pointer type [-Wincompatible-pointer-types]
  282 |     glMultiDrawElementsBaseVertex(mode, count, type, (void**)indices, drawcount,basevertex);
      |                                                      ^~~~~~~~~~~~~~~
      |                                                      |
      |                                                      void **
draw.c:282:54: note: expected ‘const void * const*’ but argument is of type ‘void **’
awsdert commented 4 years ago

Okay that resolved the libGLEW issue however still getting the invalid argument issue, here's what I have now:

        tmp = math.tointeger(scan.region)
        if not tmp or tmp < 1 then
            tmp = math.tointeger(1)
        end

        v = list.txt[1]
        scan.region = nk.combo(
            ctx, list.txt, math.tointeger(tmp),
            pad_height( font, v ),
            {
                pad_width( font, v ) * 2,
                pad_height( font, v ) * 2
            }
        )

I'm starting to think you should add a condition for the number type with something like this:

...
else if ( lua_isnumber( L, 3 ) )
{
    num = lua_tonumber( L, 3 );
    if ( (lua_Integer)(ciel(num)) != (lua_Integer)(floor(num)) )
    {
        lua_checkinteger( L, 3 );
        return 0;
    }
    pos = (int)num;
}
...
stetre commented 4 years ago

Just done now, thought I'd mention the error that popped up before I try running gasp again

cc -O2 -Wall -Wextra -Wpedantic -DCOMPAT53_PREFIX=moongl_compat_ -std=gnu99 -DLUAVER=5.3 -fpic -DLINUX -I/usr/include -I/usr/include/lua5.3 -I/usr/local/include/lua5.3   -c -o draw.o draw.c
draw.c: In function ‘MultiDrawElementsBaseVertex’:
draw.c:282:54: warning: passing argument 4 of ‘__glewMultiDrawElementsBaseVertex’ from incompatible pointer type [-Wincompatible-pointer-types]
  282 |     glMultiDrawElementsBaseVertex(mode, count, type, (void**)indices, drawcount,basevertex);
      |                                                      ^~~~~~~~~~~~~~~
      |                                                      |
      |                                                      void **
draw.c:282:54: note: expected ‘const void * const*’ but argument is of type ‘void **’

I fixed this issue in commit 5539c9, almost a year ago.

awsdert commented 4 years ago

Strange, my makefile auto-updates the clones so it should've received that change, furthermore I only started using moonlibs a few months ago so it shouldn't be possible for me to get an old bug unless you merely thought you fixed the bug but instead hid it (I've done that a number of times with just gasp so nothing unusual if it's the case)

Edit: Deleted my clones and ran the rebuild-all target so if that error still exists it definitely isn't my end that's causing it to show up, maybe it only shows up on arch based distributions?

Edit 2: Disappeared after the reclone, still getting the non-integer integer issue

stetre commented 4 years ago

Strange, my makefile auto-updates the clones so it should've received that change, furthermore I only started using moonlibs a few months ago so it shouldn't be possible for me to get an old bug unless you merely thought you fixed the bug but instead hid it (I've done that a number of times with just gasp so nothing unusual if it's the case)

That is easily checked:lLine 282 of the draw.c file in the master branch is consistent with that commit. Are you sure you're not cloning from a fork?

awsdert commented 4 years ago

Strange, my makefile auto-updates the clones so it should've received that change, furthermore I only started using moonlibs a few months ago so it shouldn't be possible for me to get an old bug unless you merely thought you fixed the bug but instead hid it (I've done that a number of times with just gasp so nothing unusual if it's the case)

That is easily checked:lLine 282 of the draw.c file in the master branch is consistent with that commit. Are you sure you're not cloning from a fork?

Quite sure:

mkdir=$(info $(if $(wildcard $1),,$(shell mkdir $1)))
clone=$(info $(if $(wildcard $1),,$(shell cd $2 && git clone https://github.com/$3)))
pull=$(info $(shell git -C '$1' pull))
...
lua_ver=5.3
lua_dir=cloned/lua
moongl_dir=cloned/moongl
moonglfw_dir=cloned/moonglfw
moonnuklear_dir=cloned/moonnuklear
lua_src_dir:=$(lua_dir)
moongl_src_dir:=$(moongl_dir)/src
moonglfw_src_dir:=$(moonglfw_dir)/src
moonnuklear_src_dir:=$(moonnuklear_dir)/src

$(call mkdir,cloned)
$(call clone,$(lua_dir),cloned,lua/lua)
$(call clone,$(moongl_dir),cloned,stetre/moongl)
$(call clone,$(moonglfw_dir),cloned,stetre/moonglfw)
$(call clone,$(moonnuklear_dir),cloned,stetre/moonnuklear)
$(call pull,$(lua_dir))
$(call pull,$(moongl_dir))
$(call pull,$(moonglfw_dir))
$(call pull,$(moonnuklear_dir))
stetre commented 4 years ago

I frankly don't know what to say about the clone problem. The code for that commit is there on github in the master branch since last year. I don't have any control on how users clone it, and what their git clients and self-brewed scripts do.

As for the non integer issue: you should review your code and ask yourself if it is really doing what you supposedly wrote it to do. Seriously. If that check fails, it simply means that you are not passing an integer in a place where you are supposed to pass an integer.

Adding a cast in the library, weakening the check just to fix this issue would be the wrong think to do. And it may not even fix it: if, for example, scan.region is a float without integer representation, your code would end up passing nil and the error would still occur.

awsdert commented 4 years ago

Thing is, there is only 2 ways that value is directly set, a hard coded integer (1) or via that one call to nk.combo(), as previously noted in my other comments I had been printing the value to check it didn't get corrupted

stetre commented 4 years ago

Thing is, there is only 2 ways that value is directly set, a hard coded integer (1) or via that one call to nk.combo(), as previously noted in my other comments I had been printing the value to check it didn't get corrupted

Looking at the code I just realized that an error may be raised also if the value is out of range, i.e. if it is less than 1 or greater than the number of items passed at argument no. 2.

awsdert commented 4 years ago

In that case could you add some errot output for that scenario, on my end when I get home from work I'll check the list isn't getting corrupted

stetre commented 4 years ago

I changed the message for that error case to "out of range" instead of "invalid value".

awsdert commented 4 years ago

Ah good, at least that's more informative, anyways I tried checking the list by clamping tmp to #(list.txt) and that stopped the crashing and with the help of print() I found that somehow the source array that gets appended to the default "array" of option"s" was somehow wiped out, now I know what to hunt for I can come back to it another day, since the original topic of this "issue" has already had a closing response I'll go ahead and close this "issue" now that I know my side topic problem is unrelated to moon libs