--- /dev/null
+FLAGS := -Wall
+
+COMMON_SRC = gltest.cpp glwindow.cpp
+COMMON_HDR = glwindow.h
+
+all: glxtest egltest
+
+glxtest: $(COMMON_SRC) $(COMMON_HDR) glxbackend.cpp glxbackend.h
+ g++ $(FLAGS) -DUSE_GLX $(COMMON_SRC) glxbackend.cpp -lGL -lX11 -o glxtest
+
+egltest: $(COMMON_SRC) $(COMMON_HDR) eglbackend.cpp eglbackend.h
+ g++ $(FLAGS) -DUSE_EGL $(COMMON_SRC) eglbackend.cpp -lEGL -lGL -lX11 -o egltest
+
+clean:
+ rm -f glxtest egltest
--- /dev/null
+#include "eglbackend.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include <EGL/eglext.h>
+
+static const char *eglErrorToString(EGLint e)
+{
+#define CASE(name) case name: return #name
+ switch (e) {
+ CASE(EGL_SUCCESS);
+ CASE(EGL_NOT_INITIALIZED);
+ CASE(EGL_BAD_ACCESS);
+ CASE(EGL_BAD_ALLOC);
+ CASE(EGL_BAD_ATTRIBUTE);
+ CASE(EGL_BAD_CONTEXT);
+ CASE(EGL_BAD_CONFIG);
+ CASE(EGL_BAD_CURRENT_SURFACE);
+ CASE(EGL_BAD_DISPLAY);
+ default: return "<unknown>";
+ }
+#undef CASE
+}
+
+static void exitEglError(const char *what)
+{
+ EGLint e = eglGetError();
+ fprintf(stderr, "EGL error %d (%s): %s\n", e, eglErrorToString(e), what);
+ exit(1);
+}
+
+/* attributes for a double buffered visual in RGBA format with at least
+* 4 bits per color */
+static const EGLint config_attribs[] = {
+ EGL_RED_SIZE, 4,
+ EGL_GREEN_SIZE, 4,
+ EGL_BLUE_SIZE, 4,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
+ EGL_NONE,
+};
+
+VisualID EGLBackend::initialize(Display *xDisplay)
+{
+ if (display == EGL_NO_DISPLAY) { // this implies that the context is also unitialized
+ // get connection and bind API
+ EGLint eglMajor, eglMinor;
+ display = eglGetDisplay(xDisplay);
+ if (display == EGL_NO_DISPLAY)
+ exitEglError("Failed to get EGL display");
+ if (eglInitialize(display, &eglMajor, &eglMinor) == EGL_FALSE)
+ exitEglError("Failed to initialize EGL");
+ printf("Using EGL version %d.%d\n", eglMajor, eglMinor);
+ eglBindAPI(EGL_OPENGL_API);
+ // get an appropriate config
+ EGLConfig configs[1];
+ EGLint count;
+ if (eglChooseConfig(display, config_attribs, configs, 1, &count) == EGL_FALSE || count == 0)
+ exitEglError("Failed to choose config");
+ config = configs[0];
+ }
+ // return visual ID
+ EGLint val;
+ eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &val);
+ return (VisualID)val;
+}
+
+void EGLBackend::createContext(Window window)
+{
+ assert(display != EGL_NO_DISPLAY && context == EGL_NO_CONTEXT);
+ surface = eglCreateWindowSurface(display, config, window, NULL);
+ /* create an EGL context and use it with the surface */
+ context = eglCreateContext(display, config, EGL_NO_CONTEXT, NULL);
+ if (context == EGL_NO_CONTEXT)
+ exitEglError("Failed to create context");
+ if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE)
+ exitEglError("Failed to make context current");
+}
+
+EGLBackend::~EGLBackend()
+{
+ if (display != EGL_NO_DISPLAY) {
+ if (context != EGL_NO_CONTEXT) {
+ eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ eglDestroyContext(display, context);
+ eglDestroySurface(display, surface);
+ }
+ eglTerminate(display);
+ eglReleaseThread();
+ }
+}
+
+void EGLBackend::swapBuffers() const
+{
+ assert(context != EGL_NO_CONTEXT); // this implies the display is also initialized
+ eglSwapBuffers(display, surface);
+}
+
+void EGLBackend::setSwapInterval(int i) const
+{
+ assert(context != EGL_NO_CONTEXT);
+ eglSwapInterval(display, i);
+}
--- /dev/null
+#include "glwindow.h"
+
+#include <EGL/egl.h>
+
+
+/** Make an X window fit for GL. You have to manage the X window yourself! */
+class EGLBackend : public GLBackend {
+public:
+ /** Create a GL window class and find an appropriate visual */
+ EGLBackend() : display(EGL_NO_DISPLAY), context(EGL_NO_CONTEXT) {}
+ /** Fre all resources */
+ virtual ~EGLBackend();
+
+ /** Initialize GL backend, choose visual configuration and return the ID */
+ virtual VisualID initialize(Display *display);
+
+ /** create a GL context for the given window */
+ virtual void createContext(Window window);
+
+ /** Swap back and front buffers */
+ virtual void swapBuffers() const;
+
+ /** Set the swap interval */
+ virtual void setSwapInterval(int i) const;
+private:
+ EGLDisplay display;
+ EGLConfig config;
+ EGLSurface surface;
+ EGLContext context;
+};
--- /dev/null
+// stdlib includes
+#include <time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <unistd.h>
+#include <assert.h>
+#include <GL/gl.h>
+
+// include proper GL connector
+#include "glwindow.h"
+#if defined(USE_GLX)
+#include "glxbackend.h"
+GLBackend *createGLBackend()
+{
+ return new GLXBackend();
+}
+#elif defined(USE_EGL)
+#include "eglbackend.h"
+GLBackend *createGLBackend()
+{
+ return new EGLBackend();
+}
+#else
+#error "No GL window type selected"
+#endif
+
+// configuration
+const GLfloat boxWidth = 0.045f;
+const GLfloat boxSpeed = 1.25f; // per second
+
+// profiler
+const int numProfilerStates = 5;
+const char *profilerStateNames[numProfilerStates] = { "Pre-Render", "Drawing", "Swapping", "Post-Render", "Outside renderer"};
+
+// utility functions
+static double getTime()
+{
+ struct timespec tp;
+ clock_gettime(CLOCK_MONOTONIC, &tp);
+ return tp.tv_sec + 1e-9 * tp.tv_nsec;
+}
+
+static void drawRect(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2)
+{
+ glVertex2f(x1, y1); glVertex2f(x2, y1); glVertex2f(x2, y2); glVertex2f(x1, y2);
+}
+
+// the window
+class TearTestWindow : public GLWindow {
+public:
+ TearTestWindow() : GLWindow(XOpenDisplay(0), createGLBackend()), boxPos(0), boxDirection(1)
+ {}
+
+protected:
+ virtual void initGL()
+ {
+ getBackend()->setSwapInterval(1);
+ // initialize GL proper
+ glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+ glDisable(GL_DEPTH_TEST);
+ // initialize clocks
+ lastFrame = getTime();
+ // initailize profiler
+ framect = 0;
+ memset(stateTime, 0, sizeof(stateTime));
+ curState = -1;
+ lastDisplay = lastProfile = getTime();
+ }
+
+ virtual void resizeGL(unsigned int width, unsigned int height)
+ {
+ /* prevent divide-by-zero */
+ if (height == 0)
+ height = 1;
+ glViewport(0, 0, width, height);
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrtho (0, 1, 1, 0, 0, 1);
+ glMatrixMode(GL_MODELVIEW);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glFlush();
+ }
+
+ void profilerTick(int nextState)
+ {
+ assert (nextState >= 0 && nextState < numProfilerStates);
+ double time = getTime();
+ if (curState >= 0)
+ stateTime[curState] += time-lastProfile;
+ curState = nextState;
+ lastProfile = time;
+ // display?
+ const double elapsed = time-lastDisplay;
+ if (elapsed >= 3) {
+ printf("%.1f fps, time spent: ", framect/elapsed);
+ for (int i = 0; i < numProfilerStates; ++i) {
+ if (i != 0) printf(", ");
+ printf("%s %.1f%%", profilerStateNames[i], stateTime[i]/elapsed*100);
+ }
+ printf("\n");
+ lastDisplay = time;
+ framect = 0;
+ memset(stateTime, 0, sizeof(stateTime));
+ }
+ }
+
+ void renderGL()
+ {
+ profilerTick(0);
+ double time = getTime();
+ // anim
+ double passedTime = time-lastFrame;
+ boxPos += boxSpeed*passedTime*boxDirection;
+ while (boxPos < 0 || boxPos+boxWidth > 1) { // wrapover
+ if (boxPos < 0) {
+ boxPos = -boxPos;
+ boxDirection = -boxDirection;
+ }
+ else {
+ boxPos = 1.0-boxWidth-(boxPos+boxWidth-1.0);
+ boxDirection = -boxDirection;
+ }
+ }
+ lastFrame = time;
+ // draw
+ //glFlush();
+ profilerTick(1);
+ //glClear(GL_COLOR_BUFFER_BIT);
+ glBegin(GL_QUADS);
+ // clear manually
+ glColor3f(0.0f, 0.0f, 0.0f);
+ drawRect(0, 0, 1, 1);
+ glColor3f(0.8f, 1.0f, 0.75f);
+ drawRect(boxPos, 0, boxPos+boxWidth, 1);
+ glEnd();
+ usleep(20*1000);
+ profilerTick(2);
+ getBackend()->swapBuffers();
+// glDrawBuffer(GL_FRONT);
+// int xpos = 0;
+// int ypos = 0;
+ //foreach (const QRect &r, region.rects()) {
+ // convert to OpenGL coordinates
+ //int y = displayHeight() - 0 - r.height();
+// glBitmap(0, 0, 0, 0, 0 - xpos, 0 - ypos, NULL); // not glRasterPos2f, see glxbackend.cpp
+// xpos = 0;
+// ypos = 0;
+// glCopyPixels(0, 0, getWidth(), getHeight(), GL_COLOR);
+ //}
+// glBitmap(0, 0, 0, 0, -xpos, -ypos, NULL); // move position back to 0,0
+// glDrawBuffer(GL_BACK);
+ profilerTick(3);
+ glFlush();
+ ++framect;
+ profilerTick(4);
+ }
+
+ virtual void handleKeyPress(KeySym key)
+ {
+ switch (key) {
+ case XK_Escape: close(); break;
+ case XK_F1: setFullscreen(!getFullscreen()); break;
+ default: break;
+ }
+ }
+
+private:
+ double lastFrame;
+ GLfloat boxPos, boxDirection;
+ // FPS
+ double lastDisplay, lastProfile;
+ int framect, curState;
+ double stateTime[numProfilerStates];
+};
+
+
+
+int main(int argc, char ** argv)
+{
+ TearTestWindow w;
+ w.open(800, 600);
+ w.exec();
+}
--- /dev/null
+#include "glwindow.h"
+
+#include <assert.h>
+#include <X11/Xutil.h>
+#include <stdio.h>
+#include <string.h>
+
+GLWindow::~GLWindow()
+{
+ delete backend;
+ if (window)
+ XDestroyWindow(display, window);
+ XCloseDisplay(display);
+}
+
+void GLWindow::create(unsigned int width, unsigned int height)
+{
+ assert(!window);
+ // get visual from backend
+ XVisualInfo vinfo_template;
+ vinfo_template.visualid = backend->initialize(display);
+ int num_vinfo;
+ XVisualInfo *vi = XGetVisualInfo(display, VisualIDMask, &vinfo_template, &num_vinfo);
+ assert(num_vinfo == 1 && vi != NULL);
+ // set winattrs
+ XSetWindowAttributes winAttr;
+ winAttr.colormap = XCreateColormap(display, RootWindow(display, vi->screen), vi->visual, AllocNone);
+ winAttr.event_mask = ExposureMask | KeyPressMask | ButtonPressMask | StructureNotifyMask;
+ winAttr.border_pixel = 0;
+ // create window
+ /*if (fullscreen) {
+ // get root window size
+ width = DisplayWidth(display, vi->screen);
+ height = DisplayHeight(display, vi->screen);
+ printf("Display size: %dx%d\n", width, height);
+ // set window attributes
+ winAttr.override_redirect = True;
+ window = XCreateWindow(display, RootWindow(display, vi->screen),
+ 0, 0, width, height, 0, vi->depth, InputOutput, vi->visual,
+ CWBorderPixel | CWColormap | CWEventMask | CWOverrideRedirect, &winAttr);
+ XWarpPointer(display, None, window, 0, 0, 0, 0, 0, 0);
+ XMapRaised(display, window);
+ XGrabKeyboard(display, window, True, GrabModeAsync, GrabModeAsync, CurrentTime);
+ XGrabPointer(display, window, True, ButtonPressMask, GrabModeAsync, GrabModeAsync, window, None, CurrentTime);
+ }
+ else {*/
+ printf("Initial window size: %dx%d\n", width, height);
+ // create a window in window mode
+ window = XCreateWindow(display, RootWindow(display, vi->screen),
+ 0, 0, width, height, 0, vi->depth, InputOutput, vi->visual,
+ CWBorderPixel | CWColormap | CWEventMask, &winAttr);
+ /* handle wm_delete_events */
+ Atom wmDelete = XInternAtom(display, "WM_DELETE_WINDOW", True);
+ XMapRaised(display, window);
+ XSetWMProtocols(display, window, &wmDelete, 1);
+ // done
+ this->fullscreen = false;
+ this->width = width;
+ this->height = height;
+ backend->createContext(window);
+ initGL();
+ resizeGL(width, height);
+}
+
+void GLWindow::close()
+{
+ if (!window) return;
+ XEvent ev;
+ memset(&ev, 0, sizeof (ev));
+ ev.xclient.type = ClientMessage;
+ ev.xclient.window = window;
+ ev.xclient.message_type = XInternAtom(display, "WM_PROTOCOLS", true);
+ ev.xclient.format = 32;
+ ev.xclient.data.l[0] = XInternAtom(display, "WM_DELETE_WINDOW", false);
+ ev.xclient.data.l[1] = CurrentTime;
+ XSendEvent(display, window, False, NoEventMask, &ev);
+}
+
+void GLWindow::setFullscreen(bool fullscreen)
+{
+ assert(window);
+ // set fullscreen property
+ XEvent e;
+ e.xclient.type = ClientMessage;
+ e.xclient.window = window;
+ e.xclient.message_type = XInternAtom(display, "_NET_WM_STATE", true);
+ e.xclient.format = 32;
+ e.xclient.data.l[0] = 2; // _NET_WM_STATE_TOGGLE
+ e.xclient.data.l[1] = XInternAtom(display, "_NET_WM_STATE_FULLSCREEN", fullscreen);
+ e.xclient.data.l[2] = 0; // no second property to toggle
+ e.xclient.data.l[3] = 1;
+ e.xclient.data.l[4] = 0;
+ XSendEvent(display, DefaultRootWindow(display), False, SubstructureRedirectMask | SubstructureNotifyMask, &e);
+}
+
+void GLWindow::exec()
+{
+ while (true)
+ {
+ /* handle the events in the queue */
+ while (XPending(display) > 0)
+ {
+ XEvent event;
+ XNextEvent(display, &event);
+ switch (event.type)
+ {
+ case Expose:
+ renderGL();
+ break;
+ case ConfigureNotify:
+ if (event.xconfigure.width != this->width || event.xconfigure.height != this->height) {
+ printf("Window resized to: %dx%d\n", event.xconfigure.width, event.xconfigure.height);
+ this->width = event.xconfigure.width;
+ this->height = event.xconfigure.height;
+ resizeGL(this->width, this->height);
+ }
+ break;
+ case KeyPress:
+ handleKeyPress(XLookupKeysym(&event.xkey, 0));
+ break;
+ case ClientMessage:
+ if (strcmp(XGetAtomName(display, event.xclient.message_type), "WM_PROTOCOLS") == 0 &&
+ strcmp(XGetAtomName(display, event.xclient.data.l[0]), "WM_DELETE_WINDOW") == 0)
+ {
+ XDestroyWindow(display, window);
+ window = 0;
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ renderGL();
+ }
+}
--- /dev/null
+#ifndef GL_WINDOW_H
+#define GL_WINDOW_H
+
+#include <X11/Xlib.h>
+
+/** Abstracts the GL binding API away */
+class GLBackend {
+public:
+ /** Create a GL window class, but does not do anything */
+ GLBackend() {}
+ /** Fre all resources */
+ virtual ~GLBackend() {}
+
+ /** Initialize GL backend, choose visual configuration and return the ID */
+ virtual VisualID initialize(Display *display) = 0;
+
+ /** create a GL context for the given window */
+ virtual void createContext(Window window) = 0;
+
+ /** Swap back and front buffers */
+ virtual void swapBuffers() const = 0;
+
+ /** Set the swap interval */
+ virtual void setSwapInterval(int i) const = 0;
+};
+
+/** A window to render GL stuff in */
+class GLWindow {
+public:
+ GLWindow(Display *display, GLBackend *backend) //!< Create the window class, but do not open it. Taks ownership of the backend and the X connection.
+ : display(display), backend(backend), window(0) {}
+ virtual ~GLWindow();
+
+ void open(unsigned int width, unsigned int height) { if (!window) create(width, height); }
+ void close();
+ void setFullscreen(bool fullscreen);
+ bool getFullscreen(void) { return fullscreen; }
+
+ int getWidth() { return width; }
+ int getHeight() { return height; }
+
+ void exec();
+
+protected:
+ const GLBackend * getBackend() { return backend; }
+
+ virtual void initGL() = 0;
+ virtual void resizeGL(unsigned int width, unsigned int height) = 0;
+ virtual void renderGL() = 0;
+ virtual void handleKeyPress(KeySym key) = 0;
+
+private:
+ void create(unsigned int width, unsigned int height);
+
+ Display *display;
+ GLBackend *backend;
+ Window window;
+ int width, height;
+ bool fullscreen;
+};
+
+#endif
--- /dev/null
+#include "glxbackend.h"
+
+#include <stdio.h>
+#include <assert.h>
+#include <GL/glxext.h>
+#include <string>
+
+/* attributes for a double buffered visual in RGBA format with at least
+* 4 bits per color */
+static int attrList[] =
+{
+ GLX_RGBA, GLX_DOUBLEBUFFER,
+ GLX_RED_SIZE, 4,
+ GLX_GREEN_SIZE, 4,
+ GLX_BLUE_SIZE, 4,
+ None
+};
+
+VisualID GLXBackend::initialize(Display *display)
+{
+ if (this->display == NULL) { // this implies that the context is also unitialized
+ this->display = display;
+ // debugging: show version information
+ int glxMajor, glxMinor;
+ glXQueryVersion(display, &glxMajor, &glxMinor);
+ printf("Using GLX version %d.%d\n", glxMajor, glxMinor);
+ // check for swap control functions
+ const char *extensions = glXQueryExtensionsString(display, DefaultScreen(display));
+ if (std::string(extensions).find("GLX_MESA_swap_control") != std::string::npos) {
+ funSwapIntervalMesa = (PFNGLXSWAPINTERVALMESAPROC)glXGetProcAddress((const GLubyte*)"glXSwapIntervalMESA");
+ if (funSwapIntervalMesa) printf("glXSwapIntervalMESA is present\n");
+ }
+ else {
+ funSwapIntervalMesa = 0;
+ }
+ if (std::string(extensions).find("GLX_EXT_swap_control") != std::string::npos) {
+ funSwapIntervalExt = (PFNGLXSWAPINTERVALEXTPROC)glXGetProcAddress((const GLubyte*)"glXSwapIntervalEXT");
+ if (funSwapIntervalExt) printf("glXSwapIntervalEXT is present\n");
+ }
+ else {
+ funSwapIntervalExt = 0;
+ }
+ // get the visual
+ vi = glXChooseVisual(display, DefaultScreen(display), attrList);
+ }
+ return vi->visualid;
+}
+
+void GLXBackend::createContext(Window window)
+{
+ assert(display != NULL && context == None);
+ this->window = window;
+ // create context with that window
+ context = glXCreateContext(display, vi, 0, GL_TRUE);
+ glXMakeCurrent(display, window, context);
+ assert(glXIsDirect(display, context));
+}
+
+GLXBackend::~GLXBackend()
+{
+ if (display != NULL) {
+ if (context != None) {
+ glXMakeCurrent(display, None, NULL);
+ glXDestroyContext(display, context);
+ }
+ XFree(vi);
+ }
+}
+
+void GLXBackend::swapBuffers() const
+{
+ assert(context != None); // this implies the display is also initialized
+ glXSwapBuffers(display, window);
+}
+
+void GLXBackend::setSwapInterval(int i) const
+{
+ assert(context != None);
+ if (funSwapIntervalExt)
+ funSwapIntervalExt(display, window, i);
+ else if (funSwapIntervalMesa)
+ funSwapIntervalMesa(i);
+ else
+ assert(false && "At least one of glXSwapIntervalMESA, glXSwapIntervalEXT must be provided by the system");
+}
--- /dev/null
+#include "glwindow.h"
+
+#include <GL/glx.h>
+
+
+/** Make an X window fit for GL. You have to manage the X window yourself! */
+class GLXBackend : public GLBackend {
+public:
+ /** Create a GL window class and find an appropriate visual */
+ GLXBackend() : display(NULL), context(None) {}
+ /** Fre all resources */
+ virtual ~GLXBackend();
+
+ /** Initialize GL backend, choose visual configuration and return the ID */
+ virtual VisualID initialize(Display *display);
+
+ /** create a GL context for the given window */
+ virtual void createContext(Window window);
+
+ /** Swap back and front buffers */
+ virtual void swapBuffers() const;
+
+ /** Set the swap interval */
+ virtual void setSwapInterval(int i) const;
+private:
+ Display *display;
+ XVisualInfo *vi;
+ GLXContext context;
+ Window window;
+
+ PFNGLXSWAPINTERVALMESAPROC funSwapIntervalMesa;
+ PFNGLXSWAPINTERVALEXTPROC funSwapIntervalExt;
+};