/* * Copyright © 2017 Intel Corporation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ /* * Tests GLX config/visual selection and sRGB. See init_glx() function for * different methods for selection. * * g++ -g -ggdb -o glxapp test.cpp -lX11 -lXrender -lGL * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include static Visual* find_depth_visual(Display *dpy, int scr, int depth) { XVisualInfo *xvi; XVisualInfo vtemplate; int nvi, i; XRenderPictFormat *format; Visual *visual; vtemplate.screen = scr; vtemplate.depth = depth; vtemplate.c_class = TrueColor; xvi = XGetVisualInfo(dpy, VisualScreenMask | VisualDepthMask | VisualClassMask, &vtemplate, &nvi); if (xvi == NULL) return NULL; visual = NULL; for (i = 0; i < nvi; i++) { format = XRenderFindVisualFormat (dpy, xvi[i].visual); if (format->type == PictTypeDirect && format->direct.alphaMask) { visual = xvi[i].visual; break; } } XFree (xvi); return visual; } static Bool WaitForNotify(Display *d, XEvent *e, char *arg) { return (e->type == MapNotify) && (e->xmap.window == (Window) arg); } class glxapp { public: explicit glxapp(unsigned w, unsigned h) { init_failed = true; xdpy = XOpenDisplay(NULL); scr = DefaultScreen(xdpy); root = RootWindow(xdpy, scr); depth = DefaultDepth(xdpy, scr); xres = ScreenOfDisplay(xdpy, scr)->width; yres = ScreenOfDisplay(xdpy, scr)->height; glxctx = NULL; #if 0 // 'regular' visual vis = DefaultVisual(xdpy, scr); #else // visual with per pixel alpha vis = find_depth_visual(xdpy, scr, 32); depth = 32; #endif if (!vis) { fprintf(stderr, "could not find such X visual\n"); return; } visual_id = XVisualIDFromVisual(vis); cmap = XCreateColormap(xdpy, root, vis, AllocNone); wa.background_pixel = 0x00000000; wa.colormap = cmap; wa.border_pixel = 0; XEvent event; // create and map X window xwin = XCreateWindow(xdpy, root, 0, 0, w, h, 0, depth, InputOutput, vis, CWBackPixel | CWBorderPixel | CWColormap, &wa); delete_atom = XInternAtom(xdpy, "WM_DELETE_WINDOW", False); XSetWMProtocols(xdpy, xwin, &delete_atom, 1); XSelectInput(xdpy, xwin, StructureNotifyMask | VisibilityNotify | ExposureMask | KeyPressMask); XClearWindow(xdpy, xwin); XMapWindow(xdpy, xwin); XFlush(xdpy); XIfEvent(xdpy, &event, WaitForNotify, (char*) xwin); fprintf(stderr, "created window with visual id %d\n", visual_id); init_failed = false; } int glx_attrib(GLXFBConfig config, int attr) { int value; glXGetFBConfigAttrib(xdpy, config, attr, &value); return value; } int visual_depth(GLXFBConfig config) { XVisualInfo templ, *xvi; int nvi = 0; int depth = 0; templ.visualid = glx_attrib(config, GLX_VISUAL_ID); xvi = XGetVisualInfo(xdpy, VisualIDMask, &templ, &nvi); if (xvi && nvi > 0) depth = xvi->depth; XFree(xvi); return depth; } void print_conf(GLXFBConfig conf) { printf("config %#x visual %#x (vis depth %d) (srgb %d) (rgba %d%d%d%d) (buf size %d) (zs %d:%d) (bind-to-texture %d %d)\n", glx_attrib(conf, GLX_FBCONFIG_ID), glx_attrib(conf, GLX_VISUAL_ID), visual_depth(conf), glx_attrib(conf, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB), glx_attrib(conf, GLX_RED_SIZE), glx_attrib(conf, GLX_GREEN_SIZE), glx_attrib(conf, GLX_BLUE_SIZE), glx_attrib(conf, GLX_ALPHA_SIZE), glx_attrib(conf, GLX_BUFFER_SIZE), glx_attrib(conf, GLX_DEPTH_SIZE), glx_attrib(conf, GLX_STENCIL_SIZE), glx_attrib(conf, GLX_BIND_TO_TEXTURE_RGB_EXT), glx_attrib(conf, GLX_BIND_TO_TEXTURE_RGBA_EXT)); } /* 1. glXGetFBConfigs * 2. investigate attributes, match with x window visual id * 3. ask for sRGB via glXChooseVisual */ bool init_glx_by_matching_visual() { int num_elements; GLXFBConfig *conf = glXGetFBConfigs(xdpy, scr, &num_elements); if (num_elements <= 0 || !conf) return false; // query window attributes, including visual id it got XWindowAttributes attributes; if (!XGetWindowAttributes(xdpy, xwin, &attributes)) { fprintf(stderr, "error with XGetWindowAttributes\n"); return false; } // paranoid check that visual id matches what we've got if (XVisualIDFromVisual(attributes.visual) != visual_id) { fprintf(stderr, "window has different visual than on creation\n"); return false; } // find GLXFBConfig that matches our visual id and has srgb for (int i = 0; i < num_elements; i++) { if (glx_attrib(conf[i], GLX_VISUAL_ID) != visual_id) continue; fprintf(stderr, "found a matching visual id (%d)\n", visual_id); // ask for srgb int srgb_attr[] = { GLX_RENDER_TYPE, GLX_RGBA_BIT, GLX_RGBA, GLX_RED_SIZE, 8, GLX_GREEN_SIZE, 8, GLX_BLUE_SIZE, 8, GLX_ALPHA_SIZE, 0, GLX_DEPTH_SIZE, 8, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, None }; XVisualInfo *vi = glXChooseVisual(xdpy, scr, srgb_attr); if (!vi) { printf("error with glXChooseVisual\n"); return false; } print_conf(conf[i]); glxctx = glXCreateContext(xdpy, vi, NULL, True); glxwin = glXCreateWindow(xdpy, conf[i], xwin, NULL); XFree(vi); } return true; } bool list_kwin_configs() { const int attr_win[] = { GLX_RENDER_TYPE, GLX_RGBA_BIT, GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, GLX_ALPHA_SIZE, 0, GLX_DEPTH_SIZE, 0, GLX_STENCIL_SIZE, 0, GLX_CONFIG_CAVEAT, GLX_NONE, GLX_DOUBLEBUFFER, True, //GLX_FRAMEBUFFER_SRGB_CAPABLE_EXT, True, None }; const int attr_pix[] = { GLX_RENDER_TYPE, GLX_RGBA_BIT, GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT | GLX_PIXMAP_BIT, GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR, GLX_X_RENDERABLE, True, GLX_CONFIG_CAVEAT, int(GLX_DONT_CARE), GLX_BUFFER_SIZE, 32, GLX_RED_SIZE, 8, GLX_GREEN_SIZE, 8, GLX_BLUE_SIZE, 8, GLX_ALPHA_SIZE, 8, GLX_DEPTH_SIZE, 0, GLX_STENCIL_SIZE, 0, None }; printf("listing configs kwin_x11 would use for the overlay window\n"); int num_elements; GLXFBConfig *wconf = glXChooseFBConfig(xdpy, scr, attr_win, &num_elements); printf("got %d compatible configs\n", num_elements); if (num_elements <= 0 || !wconf) return false; for (int i = 0; i < num_elements; i++) { if (glx_attrib(wconf[i], GLX_FRAMEBUFFER_SRGB_CAPABLE_EXT) == 1) { printf("oh no, srgb config found\n"); } print_conf(wconf[i]); } printf("listing configs kwin_x11 would use for the window pixmap\n"); GLXFBConfig *pconf = glXChooseFBConfig(xdpy, scr, attr_pix, &num_elements); printf("got %d compatible configs\n", num_elements); if (num_elements <= 0 || !pconf) return false; for (int i = 0; i < num_elements; i++) { if (glx_attrib(pconf[i], GLX_FRAMEBUFFER_SRGB_CAPABLE_EXT) == 1) { printf("oh no, srgb config found\n"); } print_conf(pconf[i]); } struct FBConfig { GLXFBConfig config; int depth; int stencil; }; std::deque candidates; for (int i = 0; i < num_elements; i++) { int red = glx_attrib(pconf[i], GLX_RED_SIZE); int green = glx_attrib(pconf[i], GLX_RED_SIZE); int blue = glx_attrib(pconf[i], GLX_RED_SIZE); if (std::tie(red, green, blue) != std::make_tuple(8, 8, 8)) { printf("rejecting config %#x because of RGB size mismatch " "(config rgb = %d%d%d, window rgb = 888\n", glx_attrib(pconf[i], GLX_FBCONFIG_ID), red, green, blue); continue; } if (visual_depth(pconf[i]) != 32) { printf("rejecting config %#x because of visual depth mismatch " "(visual depth = %d, window depth = 32)\n", glx_attrib(pconf[i], GLX_FBCONFIG_ID), visual_depth(pconf[i])); continue; } if (!glx_attrib(pconf[i], GLX_BIND_TO_TEXTURE_RGBA_EXT) && !glx_attrib(pconf[i], GLX_BIND_TO_TEXTURE_RGB_EXT)) { printf("rejecting config %d because it does not support bind-to-texture\n", glx_attrib(pconf[i], GLX_FBCONFIG_ID)); continue; } candidates.emplace_back(FBConfig { pconf[i], glx_attrib(pconf[i], GLX_DEPTH_SIZE), glx_attrib(pconf[i], GLX_STENCIL_SIZE), }); } std::stable_sort(candidates.begin(), candidates.end(), [](const FBConfig &left, const FBConfig &right) { if (left.depth < right.depth) return true; if (left.stencil < right.stencil) return true; return false; }); printf("%lu configs remaining after filtering and sorting\n", candidates.size()); for (const FBConfig &entry : candidates) print_conf(entry.config); printf("READY.\n"); return true; } /* 1. glXChooseFBConfig * 2. investigate attributes, match with GLX_FRAMEBUFFER_SRGB_CAPABLE_EXT * 3. get visual with glXGetVisualFromFBConfig */ bool init_glx_by_choosing_config() { const int attr[] = { GLX_RENDER_TYPE, GLX_RGBA_BIT, GLX_RED_SIZE, 4, GLX_GREEN_SIZE, 4, GLX_BLUE_SIZE, 4, GLX_ALPHA_SIZE, 4, GLX_DEPTH_SIZE, 8, GLX_DOUBLEBUFFER, True, GLX_FRAMEBUFFER_SRGB_CAPABLE_EXT, True, None }; printf("attempt to use sRGB visual\n"); int num_elements; GLXFBConfig *conf = glXChooseFBConfig(xdpy, scr, attr, &num_elements); if (num_elements <= 0 || !conf) return false; // search one that has GLX_FRAMEBUFFER_SRGB_CAPABLE_EXT set for (int i = 0; i < num_elements; i++) { if (glx_attrib(conf[i], GLX_FRAMEBUFFER_SRGB_CAPABLE_EXT) != 1) continue; XVisualInfo *vi = glXGetVisualFromFBConfig(xdpy, conf[i]); if (!vi) { printf("no srgb visual found!\n"); return false; } print_conf(conf[i]); glxctx = glXCreateContext(xdpy, vi, NULL, True); glxwin = glXCreateWindow(xdpy, conf[i], xwin, NULL); return true; } return false; } /* 1. glXGetFBConfigs * 2. investigate attributes, match with GLX_FRAMEBUFFER_SRGB_CAPABLE_EXT * 3. get visual with glXGetVisualFromFBConfig */ bool init_glx_by_searching_for_srgb() { int num_elements; GLXFBConfig *conf = glXGetFBConfigs(xdpy, scr, &num_elements); if (num_elements <= 0 || !conf) return false; // search first config that has srgb support and has doublebuffer // and no alpha for (unsigned i = 0; i < num_elements; i++) { if (glx_attrib(conf[i], GLX_FRAMEBUFFER_SRGB_CAPABLE_EXT) != 1) continue; if (glx_attrib(conf[i], GLX_DOUBLEBUFFER) != 1) continue; if (glx_attrib(conf[i], GLX_ALPHA_SIZE) != 0) continue; XVisualInfo *vi = glXGetVisualFromFBConfig(xdpy, conf[i]); if (!vi) { printf("no srgb visual found for config!\n"); return false; } else { print_conf(conf[i]); glxctx = glXCreateContext(xdpy, vi, NULL, True); glxwin = glXCreateWindow(xdpy, conf[i], xwin, NULL); XFree(vi); return true; } } return false; } bool init_glx() { if (init_failed) return false; // OPTION 1 // this kind of works even though GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB is // set as 0 since glGetFramebufferAttachmentParameterivEXT returns 0x8c40 //return init_glx_by_matching_visual(); // OPTION 2 // works nicely, has srgb set //return init_glx_by_searching_for_srgb(); list_kwin_configs(); // OPTION 3 // works nicely, has srgb set return init_glx_by_choosing_config(); return false; } ~glxapp() { if (init_failed) return; if (glxctx) { glXMakeContextCurrent(xdpy, None, None, glxctx); glXDestroyContext(xdpy, glxctx); } XDestroyWindow(xdpy, xwin); XCloseDisplay(xdpy); } GLint ask_fb_param(GLenum p) { static PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC func = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC) glXGetProcAddressARB((const GLubyte*) "glGetFramebufferAttachmentParameterivEXT"); if (func) { GLint param = 0xdeadbeef; func(GL_DRAW_FRAMEBUFFER, GL_BACK_LEFT, p, ¶m); return param; } return 0xdeadbeef; } int exec() { if (init_failed) return -1; if (!glxctx) return -1; if (!glXMakeCurrent(xdpy, glxwin, glxctx)) { printf("glXMakeCurrent failed\n"); return -1; } GLint fb_type = ask_fb_param(GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE); if (fb_type == GL_NONE) { printf("huh! maybe config was not doublebuffered?\n"); return 0; } glClearColor(1, 1, 1, 1); XEvent event; long key_pressed; Bool done = False; #define PRINT_PARAM(p) printf("%s 0x%x\n", #p, ask_fb_param(p)); PRINT_PARAM(GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE); PRINT_PARAM(GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE); PRINT_PARAM(GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE); PRINT_PARAM(GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE); PRINT_PARAM(GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING); GLenum err = glGetError(); if (err != GL_NO_ERROR) { printf("gl error 0x%x when asking fb parameters\n", err); return 0; } while (!done) { if (XEventsQueued(xdpy, QueuedAfterReading) <= 0) { glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); } else { XNextEvent(xdpy, &event); switch (event.type) { case ButtonRelease: done = True; break; case KeyPress : key_pressed = XLookupKeysym((XKeyEvent *)&event, 0); if (key_pressed == XK_Escape) done = True; break; case ClientMessage : if ((signed)event.xclient.data.l[0] == delete_atom) done = True; break; } } glXSwapBuffers(xdpy, glxwin); } return 0; } private: Display *xdpy; Window xwin; Window root; Atom delete_atom; XSetWindowAttributes wa; Colormap cmap; Visual *vis; GLXContext glxctx; GLXWindow glxwin; bool init_failed; // X window visual ID int visual_id; int scr; int depth; int xres; int yres; }; int main() { glxapp app(256, 256); if (!app.init_glx()) { printf("error initializing glx\n"); return -1; } return app.exec(); }