pouët.net

4k GL framework for Linux

category: code [glöplog]
Hi there, I was thinking it might be interesting to see what's the minimal amount of code you need to set up a GL window and render loop in Linux, without SDL. There are many Linux experts here, I though they could help out on this, like the discussion that happened long ago in in4k for the Windows case.

So far I have this:


Code: #include <X11/X.h> #include <X11/Xutil.h> #include <X11/keysym.h> #include <sys/time.h> #include <GL/gl.h> #include <GL/glx.h> #include "demo.h" #define XRES 1280 #define YRES 720 static int doubleBufferVisual[] = { GLX_RGBA,GLX_DEPTH_SIZE, 24,GLX_DOUBLEBUFFER, None }; static long timeGetTime( void ) { double long t; struct timeval now, res; gettimeofday(&now, 0); t = (now.tv_sec*1000) + (now.tv_usec/1000); return( (long)t ); } int main( void ) { Display *hDisplay = XOpenDisplay( NULL ); if( !hDisplay ) return 0; // make sure OpenGL's GLX extension supported int errorBase, eventBase; if( !glXQueryExtension( hDisplay, &errorBase, &eventBase ) ) return 0; XVisualInfo *visualInfo = glXChooseVisual( hDisplay, DefaultScreen(hDisplay), doubleBufferVisual ); if( visualInfo == NULL ) return 0; GLXContext hRC = glXCreateContext( hDisplay, visualInfo, NULL, GL_TRUE ); if( hRC == NULL ) return 0; XSetWindowAttributes winAttr; winAttr.colormap = XCreateColormap( hDisplay, RootWindow(hDisplay, visualInfo->screen), visualInfo->visual, AllocNone ); winAttr.event_mask = KeyPressMask; Window hWnd = XCreateWindow( hDisplay, RootWindow(hDisplay, visualInfo->screen), 0, 0, XRES, YRES, 0, visualInfo->depth, InputOutput, visualInfo->visual, CWColormap | CWEventMask, &winAttr ); // Window hWnd = XCreateSimpleWindow( hDisplay, RootWindow(hDisplay, visualInfo->screen), 0, 0, XRES, YRES, 0, 0, 0 ); if( !hWnd ) return 0; XSetStandardProperties( hDisplay, hWnd, "my demo", NULL, None, NULL, 0, NULL ); glXMakeCurrent( hDisplay, hWnd, hRC ); XMapWindow( hDisplay, hWnd ); demo_init(); int done = 0; long to = timeGetTime(); while( !done ) { while( XPending(hDisplay) ) { XEvent event; XNextEvent( hDisplay, &event ); if( event.type == KeyPress ) { if( XKeycodeToKeysym( hDisplay, event.xkey.keycode, 0 ) == XK_Escape ) { done = 1; } } else if( event.type == DestroyNotify ) { done = 1; } } demo_paint( 0.001f*(timeGetTime()-to) ); glXSwapBuffers( hDisplay, hWnd ); } demo_end(); XDestroyWindow( hDisplay, hWnd ); XCloseDisplay( hDisplay ); return 0; }


Ideally seems we would like to use XCreateSimpleWindow() but then I get no events sent, and under the lack of a GetAsyncKeyState(VK_ESCAPE) for Linux we can't go that way,.. or can we? Also, glXQueryExtension() can be removed...

Please, post your tricks. ELF header tricks and packers are orthogonal to what we look for here, so API optimization are welcome. Little things like reorganizing the if()'s to save three bytes or chaging "my demo" to "md" don't count as optimizations of course, we want to make this thing reasonably small first.

Help!
added on the 2009-12-08 02:37:08 by iq iq
ok, this is shorter already:

Code: #if 0 XSetWindowAttributes winAttr; winAttr.colormap = XCreateColormap( hDisplay, RootWindow(hDisplay, visualInfo->screen), visualInfo->visual, AllocNone ); winAttr.event_mask = KeyPressMask; Window hWnd = XCreateWindow( hDisplay, RootWindow(hDisplay, visualInfo->screen), 0, 0, XRES, YRES, 0, visualInfo->depth, InputOutput, visualInfo->visual, CWColormap | CWEventMask, &winAttr ); if( !hWnd ) return 0; #else Window hWnd = XCreateSimpleWindow( hDisplay, RootWindow(hDisplay, visualInfo->screen), 0, 0, XRES, YRES, 0, 0, 0 ); if( !hWnd ) return 0; XSelectInput( hDisplay, hWnd, KeyPressMask ); #endif
added on the 2009-12-08 02:47:57 by iq iq
Drop XCloseDisplay()
i know it's just a few bytes but why not drop all the if(something == NULL) return 0; checks?
added on the 2009-12-08 03:51:53 by src src
We of course need sound to, right. So let's start by adding this:

Code: #include <fcntl.h> #include <sys/ioctl.h> #include <sys/soundcard.h> const int format = AFMT_S16_LE, rate=44100, channels =2; int audp; ... in main somplace... if((audp = open("/dev/audio", O_WRONLY, 0))<0) return 0; if(ioctl(audp, SNDCTL_DSP_SETFMT, &format) <0 || ioctl(audp, SNDCTL_DSP_SPEED, &rate) <0 || ioctl(audp,SNDCTL_DSP_STEREO, &channels)<0 ) return 0; ... end in main somplace ...
added on the 2009-12-08 05:34:34 by sigflup sigflup
Code: #include <X11/X.h> #include <sys/time.h> #define GL_GLEXT_PROTOTYPES 1 #include <GL/gl.h> #include <GL/glx.h> #define XRES 1280 #define YRES 720 //---------------------------------------------------------------- static const char *vsh = \ "varying vec4 q;" "void main(void)" "{" "gl_Position = gl_Vertex;" "q = vec4(gl_Vertex.xy,gl_MultiTexCoord0.xy);" "}"; static const char *fsh = \ "varying vec4 q;" "void main(void)" "{" "vec2 z = q.xy*vec2(1.7,1.0);" // animate "vec2 c = vec2(0.5*cos(0.05*q.z) - 0.25*cos(0.1*q.z)," "0.5*sin(0.05*q.z) - 0.25*sin(0.1*q.z))*1.01;" // julia set! "float n;" "for( n=0; n<256.0 && dot(z,z)<10.0; n++ )" "z = vec2(z.x*z.x-z.y*z.y, 2.0*z.x*z.y) + c;" // smooth iterations "n -= log(.5*log(dot(z,z)))/log(2.0);" "gl_FragColor=vec4(1.0-n/256.0);" "}"; //---------------------------------------------------------------- static int doubleBufferVisual[] = { GLX_RGBA,GLX_DEPTH_SIZE, 24, GLX_DOUBLEBUFFER, None }; static long timeGetTime( void ) { struct timeval now, res; gettimeofday(&now, 0); return (long)((now.tv_sec*1000) + (now.tv_usec/1000)); } int main( void ) { Display *hDisplay = XOpenDisplay( NULL ); if( !hDisplay )return 0; //if( !glXQueryExtension( hDisplay, 0, 0 ) ) return 0; XVisualInfo *visualInfo = glXChooseVisual( hDisplay, DefaultScreen(hDisplay), doubleBufferVisual ); if( visualInfo == NULL ) return 0; GLXContext hRC = glXCreateContext( hDisplay, visualInfo, NULL, GL_TRUE ); if( hRC == NULL ) return 0; XSetWindowAttributes winAttr; winAttr.override_redirect = 1; #if 0 winAttr.colormap = XCreateColormap( hDisplay, RootWindow(hDisplay, visualInfo->screen), visualInfo->visual, AllocNone ); Window hWnd = XCreateWindow( hDisplay, RootWindow(hDisplay, visualInfo->screen), 0, 0, XRES, YRES, 0, visualInfo->depth, InputOutput, visualInfo->visual, CWColormap|CWOverrideRedirect, &winAttr ); if( !hWnd ) return 0; #else Window hWnd = XCreateSimpleWindow( hDisplay, RootWindow(hDisplay, visualInfo->screen), 0, 0, XRES, YRES, 0, 0, 0 ); if( !hWnd ) return 0; XChangeWindowAttributes(hDisplay, hWnd, CWOverrideRedirect, &winAttr); #endif // move cursor out of the windows please XWarpPointer(hDisplay, None, hWnd, 0, 0, 0, 0, XRES, 0); XMapRaised(hDisplay, hWnd); XGrabKeyboard(hDisplay, hWnd, True, GrabModeAsync,GrabModeAsync, CurrentTime); glXMakeCurrent( hDisplay, hWnd, hRC ); const int prId = glCreateProgram(); if( !prId )return 0; const int vsId = glCreateShader( GL_VERTEX_SHADER ); const int fsId = glCreateShader( GL_FRAGMENT_SHADER ); glShaderSource( vsId, 1, &vsh, 0 ); glShaderSource( fsId, 1, &fsh, 0 ); glCompileShader( vsId ); glCompileShader( fsId ); glAttachShader( prId, fsId ); glAttachShader( prId, vsId ); glLinkProgram( prId ); long to = timeGetTime(); XEvent event; while( !XCheckTypedEvent(hDisplay,KeyPress,&event ) ) { glTexCoord1f( 0.001f*(timeGetTime()-to) ); glUseProgram( prId ); glRects( -1, -1, 1, 1 ); glXSwapBuffers( hDisplay, hWnd ); } return 0; }


I will add the sound code tomorrow sigflup. I need Auld now, or some of the other hardcore 4 kilobyters. I don't like the timeGetTime(), and the window creation code is too big too...
added on the 2009-12-08 09:33:05 by iq iq
iq, just wondering why you don't want to use SDL? I'm not a linux expert, but i thought it was quite common in a linux distro and afaik, most of the linux-intros (if not all), are usually using SDL, no?
added on the 2009-12-08 09:42:20 by xoofx xoofx
just a slightly off-topic thought: worst thing that linux demos/intros do is mess with the resolution and not set it back.. then one has to restart the X window system. very annoying. I think SDL does this. IMHO better to just use the current screen resolution..
added on the 2009-12-08 09:46:14 by jaw jaw
Quote:
then one has to restart the X window system. very annoying
man xrandr :)
Also I second @lx. Why not use SDL?
added on the 2009-12-08 10:02:27 by masterm masterm
what @lx said.
added on the 2009-12-08 10:06:00 by ponce ponce

@lx, I'm not able to run any of the linux intros of pouet in any none of the linux machines I have access to.

Plus, in anycase the exercise is to see how small the thing can get without SDL, so using SDL is not an option by definition...
added on the 2009-12-08 10:17:00 by iq iq
iq: well when I take a quick look, all you can do for now is to replace:
Code: glXMakeCurrent( hDisplay, hWnd, hRC );

with
Code: glXMakeCurrent(hDisplay, hWnd, glXCreateContext(hDisplay, visualInfo, NULL, True));

and get rid of hRC completely. Not that this should change too much, but in case the compiler is a moron then it might save a few bytes. Also as aforementioned please get rid of all the error checks.

Finally what cruncher are you using for packing the executable? Also I just realized, you really don't need the override_redirect flag for a windowed prod, because the window manager successfully terminates the execution by default once you press the close button on the window, which means you wouldn't need the keyboard and mouse warping stuff at all, which means coolness.

I will work a bit more on it, I am sure we can make good use of that doubleBufferVisual array to store the pointers returned which means the same set of opcodes will be generated for many memory accesses and a cruncher can easily crunch the heck out of it (that is the method I used in Muon Baryon with that pointer trick).
added on the 2009-12-08 10:29:09 by decipher decipher
Quote:
@lx, I'm not able to run any of the linux intros of pouet in any none of the linux machines I have access to.

woo... well, I have never tested this myself, so I can't confirm! ;)

Quote:
Plus, in anycase the exercise is to see how small the thing can get without SDL, so using SDL is not an option by definition...

Yep, i understand from your first post that the challenge is to do it without SDL. Sure that with SDL it's easier, but yeah, your code looks really close to what we have under windows (although i would put some macros to test results for != 0 after any system functions, disabling those checks in release mode) , so i agree that it would quite interesting to see how short the code would be without SDL.

And well, if the code is as short as you are showing us, It would be probably quite "fair" for linux intros to stop using SDL for small intros and use the "raw" way... as we use to do on windows...
added on the 2009-12-08 10:45:10 by xoofx xoofx
iq, if you can't run any intros you're likely missing a symlink for libSDL.so pointing to the version you have installed (you probably already know this though).
pete: I think the Linux machines in question are those with Pixar's custom linux distro on them, so I am sure SDL is not there for a good reason :). I might be wrong though.
added on the 2009-12-08 12:51:27 by decipher decipher
I have read the discussion, and not sure what is the point of using XLib. I would focus on making it smaller, in more subtle way, and *using* SDL, then trying to make it work under XLib.
SDL is virtually everywhere, and make life easier. I've never had problems in running/developing with SDL under Linux, moreover SDL is available on all distros from a binary package.
If the point is to not use SDL, then yes, it is a good exercise otherwise it is not that interesting. One can use Glut or even GTK, for init stuff rather then GLX.
added on the 2009-12-08 13:14:57 by sp3c sp3c
The "raw" way would abandon the X11 libraries as well and do everything via plain kernel calls.

I experimented with the raw X11 protocol some years ago and got the code for opening a window and refreshing a pixel framebuffer into something like 300 bytes. Since then I've been thinking about doing a similar experiment with X11+GLX but this has never been a top-priority task for me, so it's still pending.
And by the way, gettimeofday() is a kernel call and AFAIK the simplest timing interface available on this level of abstraction, even though the decimal thing may feel tremendously ugly.
oh, great, this is the help I needed! How can I do lower level system function calls? Where do I learn about it? Will that be compatible in all systems?

Also I need to set fullscreen mode, any help is welcome (that's why I need the override_redirect=1, I believe...?)
added on the 2009-12-08 17:52:41 by iq iq
Quote:
I have read the discussion, and not sure what is the point of using XLib

second that, there is no point on using Xlib/GLX, well.. only if you wanna learn to doing things in the hard way :)

If you don't need sound (1k intros) use GLUT, you save some bytes (than using SDL).
added on the 2009-12-08 17:59:54 by pera pera
i love how Linux people can't answer the question "HOW?"

"how can I achieve A using B?" "- oh B is crap, you should use C instead" while it's legitimate to use B to achieve A...

"how can I do that with Gnome/GTK+?" "oh Gnome/GTK+ is crap, you should use KDE/Qt instead!"
iq: you are right with the override_redirect bit for fullscreen :). not necessarily though but it saves shitloads of hassle.
added on the 2009-12-08 18:13:01 by decipher decipher
Quote:
i love how Linux people can't answer the question "HOW?"

This. It's still better than in the 90s, though:

- XY is not working, what should i do?
- duh, recompile the kernel!
- ...
added on the 2009-12-08 18:14:50 by blala blala
I can understand a point, if we plan to use raw X protocol and just send the commands straight to the X server or GLX, but i wouldn't fiddle with it as it is bigger disadvantage than advantage.

The real power is with custom ELF headers, that do support importing stuff :) (means dlopen, dlsym pair)


added on the 2009-12-08 19:59:26 by sp3c sp3c
Quote:

Also I need to set fullscreen mode, any help is welcome (that's why I need the override_redirect=1, I believe...?)


well, iq, first you need to answer the question, why would like to do it in that way? If you want a basic framework for spawning SDL window, let me know.

I am sure there will be more stuff coming from the Linux side soon:)
added on the 2009-12-08 20:07:41 by sp3c sp3c

login