Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does OpenGL use Xlib to draw windows and render things, or is it the other way around?

I want to render fonts and lines on a window using either OpenGL or Xlib, but I'd like to know which one is "more fundamental".

Using the Xlib interface I can render such things with something like this (which I found here):

// gcc x_1.c -o x_1 -lX11 && ./x_1

#include <stdio.h>
#include <X11/Xlib.h>

// This program draws a red line and some text in a chosen font.
Display *display;
Window  window;
XSetWindowAttributes attributes;
XGCValues gr_values;
XFontStruct *fontinfo;
GC gr_context;
Visual *visual;
int depth;
int screen;
XEvent event;
XColor    color, dummy;

int main() {
     display = XOpenDisplay(NULL);
     screen = DefaultScreen(display);
     visual = DefaultVisual(display,screen);
     depth  = DefaultDepth(display,screen);
     attributes.background_pixel = XWhitePixel(display,screen);

     window = XCreateWindow( display,XRootWindow(display,screen),
                            200, 200, 350, 200, 5, depth,  InputOutput,
                            visual ,CWBackPixel, &attributes);
     XSelectInput(display,window,ExposureMask | KeyPressMask) ;
     fontinfo = XLoadQueryFont(display,"6x10");

     XAllocNamedColor(display, DefaultColormap(display, screen),"red",
                      &color,&dummy);

     gr_values.font = fontinfo->fid;
     gr_values.foreground = color.pixel;
     gr_context=XCreateGC(display,window,GCFont+GCForeground, &gr_values);
     XFlush(display);
     XMapWindow(display,window);
     XFlush(display);

     while(1){
        XNextEvent(display,&event);

        switch(event.type){
        case Expose:
             XDrawLine(display,window,gr_context,0,0, 100, 100);
             XDrawString(display,window,gr_context,100,100,"hello",5);
             break;
        case KeyPress:
             XCloseDisplay(display);
             return 1;

        }
     }
     return 0;
}

Now, I can accomplish the exact same thing using pure OpenGL code, which raises a few questions.

Does OpenGL use Xlib to display windows and render fonts/geometric primitives? Or is it the other way around?

Which one should I use if I want more "low-level" control over the rendering and display?


Edit:

My target application is a barebones text editor that will only render bitmap fonts, only have 255 colors, and only do software rendering (ie. assume no GPU is present).

I tried Cairo/Pango, SDL2, GTK and Qt, but they're slow and unresponsive compared to pure Xlib.

like image 993
étale-cohomology Avatar asked Nov 10 '16 20:11

étale-cohomology


People also ask

Does OpenGL use Xlib?

First and foremost OpenGL and Xlib are orthogonal with each other. Neither depends on the other one and you can use them independently.

What is OpenGL used for?

OpenGL (Open Graphics Library) is a cross-language, cross-platform application programming interface (API) for rendering 2D and 3D vector graphics. The API is typically used to interact with a graphics processing unit (GPU), to achieve hardware-accelerated rendering.

Is X11 OpenGL?

No, Xorg itself is not OpenGL. However, it connects to the Direct Rendering Infrastructure (DRI for short) that can be used to provide support for OpenGL.


2 Answers

Short answer (inferred from using Xlib/XCB/GLX/OpenGL) and Youtube tutorial:

  • Xlib is a high-level C wrapper around the X protocol (client-side).
  • XCB is a low-level C warpper around the X protocol (client-side).
  • The X window System works as a "client-server" system where the "X server" is the GPU and the "X client" is your application.
  • The X Protocol is used by the X client and X server to send requests/replies/errors/events to each other (each 32 bytes long?) over a socket.
  • GLX is both a protocol extension and an API that connects the X Window System to OpenGL and allows X resources (eg. X window or X pixmaps) to be used for OpenGL calls.
  • OpenGL is a graphics API that sends draw/memory commands to the GPU. It's implemented in a shared library often called libGL.so, which, in the case of NVIDIA, is implemented by NVIDIA.
  • An OpenGL application needs an X display/connection, an X screen, an X window (or a GLX window), an X colormap, a GLX fbconfig, and a GLX context before you can make your first OpenGL call. The results of OpenGL calls are then rendered on the X window (or GLX window).
  • The Linux kernel has the DRI, the DRM, and the KMS, which seem to be lower-level than Xlib/XCB/GLX/OpenGL and completely invisible to them (close to the level of the GPU driver; in fact I think the NVIDIA driver may bypass a big chunk of kernel infrastructure?).

So, Xlib/XCB creates the windows, then OpenGL renders to them.

(OpenGL can't create windows. Xlib/XCB can render, but I don't think it's GPU-accerelated.)

like image 139
étale-cohomology Avatar answered Oct 12 '22 14:10

étale-cohomology


WARNING: Wall of text coming in!

@sourcejedi already gave an answer but there are some inaccuracies in it – that are not hurtful, but may be confusing.

First and foremost OpenGL and Xlib are orthogonal with each other. Neither depends on the other one and you can use them independently. There is a dependency on Xlib by GLX (which is, why you can't fully ditch Xlib in favor of Xcb if you want to GLX), but the surface area of that dependency is small enough, so that you can use Xcb for the most part and use the Xlib-on-Xcb wrapper for the few parts where you have to use GLX. Now I've dropped a few acronyms here, so let's categorize them first:

Xlib & Xcb are both libraries that implement the client side of the X11 protocol. Technically you could talk to a X11 server without them, by implementing the protocol "yourself" (in fact there is an ongoing effort to autogenerate Haskell code from the same specification files that are also used to generate Xcb code). X11 is transferred over sockets, so if you can somehow transfer that socket and some internal state between bindings you can even mix and match bindings, if you're careful where you tread.

OpenGL itself is a pure low level (low level in the sense of which primitives it offers: points, lines and triangles) drawing tool, and there is no concept of "windows" in it. With the introduction of framebuffer objects, in principle you can create a headless OpenGL context and operate entirely within the bounds of self-managed image surfaces. But often (but certainly not always) you want your drawing(s) to be visible immediately on a display. That's where a window system interface (WSI) comes into play. OpenGL and thereby the whole support structure around it is quite old though, so not a lot of thought has been put into the whole WSI business, which led to a rather, well… actually there's no cleanly defined interface for this at all, so far, it all happens circumstancial. The usual way is "here I've got some preexisting image surface curtsey of the display system, I can haz OpenGL on that please?" Which brings us to the rather ad-hoc window system APIs for OpenGL. GLX for X11 and WGL for Win32; Apple is a whole chapter of its own…

GLX is two things: First it's a X11 extension, i.e. it uses X11 transport to create a channel for a new set of commands in the X11 server. These commands are the OpenGL functions as specified for versions 1.1 through 2.1 (GLX has not yet been specified for 3.x onward). In that regard GLX places a OpenGL implementation on the X11 server side and GLX opcodes are used for remote procedure call toward the OpenGL context running server side. Second a GLX client side implementation that's housed in the libGL.so OpenGL API access library.

In addition to that GLX implements a few "convenience" functions, like creating OpenGL display-listed rasterop "fonts" from X11 server side fonts; works only for the old-and-busted bitmap fonts that have been around with X11 core since ever. Doesn't antialias and don't look particularly good on low pixel density displays (if you have a high pixel density output device you can get away without antialiasing and high resolution bitmap fonts actually look excellent, case in point: laser printers). WGL followed GLX's lead and did the same for Windows bitmap fonts. That's the only "kind-of" font rendering support that's built directly into the OpenGL ecosystem. But it's pretty much useless for anything outside the 7-bit printable ASCII set. And it totally lacks a layout engine that would properly format the text (left, right, center aligned, justified or whatever). And it completely relies on display lists and OpenGL raster ops which have caveats like "the whole thing won't even go to work if the starting position of the first character happens to lie outside the viewport". So don't bother even trying to use it.

With GLX you can take an existing X11 window and send the X11 server commands that set up this window for use with OpenGL. Also with GLX you can send the X11 server commands that ask it to create a OpenGL context. A OpenGL context is not tied to a particular X11 drawable. You can attach and detach it freely, as long as the drawable you're intending to attach it is compatible with that context (i.e. has been configured for the particular framebuffer format, that context expects for its main framebuffer). Then when you make OpenGL calls, GLX wraps them up into opcodes and sends them over the socket connection to the X11 server where they're executed.

Of course all that de-/serialization incurs some non insignificant execution cost, hence very quickly a special mode called "direct context" was introduced, where the GLX/X11-server combo are used only to setup the OpenGL context and making it current on X11 drawables and for the rest the program is working directly against the OpenGL implementation, thereby avoiding all that serialization and even getting direct access to address space where bulk data objects (textures, buffer objects, etc.) reside. This is why in a X11 based environment you can find instances of a OpenGL implementation for the same direct connection context to actually be present in two processes: The X11 server and the client using it.

As I already mentioned above Xlib, Xcb & co are just client side implementations of the X11 protocol. The server side of X11 provides a basic set of graphics functions. X11 core has some limited bitmap font drawing capabilities, along with functions for drawing points, lines, arcs, (rounded) rectangles, circles and polygons. Drawing and filling happens with a solid pen or hatching pattern; there's no interpolation or gradients going on. Depending on the kind of image you want to draw this is rather limiting. Between the years 2000 to 2010 everybody wanted to draw colourful UIs with lots of gradients and fancy borders and whatnot (personally I never liked those), but in past years semi-/flat UI designs have become a trend and those you could actually draw rather well with X11 core (sans the antialiased text). Eventually people wanted to be able to draw gradients X11 side, so the XRender extension was introduced, which gives you what is essentially the 2D equivalent of OpenGL's GL_TRIANGLES primitive, without shaders, textures and illumination; you get color interpolation, gradients and antialiasing though. As a sidetrack the Xft library (important, that's a library, not a X11 server thing), was created that piggybacks on XRender to load a bunch of client side rasterized font glyphs to the server so that you could with that onto a drawable "antialiased" text. That's about the drawing capabilities of a X11 server. How these drawing capabilities are actually implemented in the X11 server is unspecified and completely depend on the implementation and more importantly on the device dependent X code (DDX) aka the X graphics driver! It could be done purely in software (often the case, currently through a server side component based on the pixman render library), but as well GPU accelerated or even based on the server side part of the used OpenGL implementation (glamor).

Which one should I use if I want more "low-level" control over the rendering and display?

That depends entirely on your target application.

If you're aiming for doing something that's not doing a lot of graphics-graphics (text editor, chat program or so) and you're okay with being tied to X11 then probably X11 through Xlib or Xcb. Most importantly X11 gives you something that pretends to be half-way reasonable text drawing functions that at least work in predictable ways.

OpenGL is the proper choice if you're aiming for doing graphics-graphics work (image manipulation, CAD, desktop publishing, also web browsers), but it means that you're "on your own" for anything that goes beyond points, lines and triangles.

These days it's practically a given that a GPU is present. 10 years ago, not so much, so the UI toolkits and graphics backends that were developed back then (and we're still using those) will do most of their work purely in software, on the CPU and transfer the final image to the display device. This was reasonable then. And if the ultimate goal is reproducible, high quality graphics it still is, because GPUs cut a lot of corners for performance and don't behave identically (OpenGL demands that rendering results of the same chain of operations within the same context are 1:1 matches, but change only one bit and they all bets are off).

The three rendering libraries you can find these days on a modern Linux(!) system (not X11 based in general!) and can be used "stand-alone" are Cairo, Anti-Grain-Geometry (AGG) and pixman. Cairo is used mostly by GDK/GTK+ based programs, pixman in the X11 server (but you can use it in your program, too). AGG is none toolkit's primary graphics kit in particular, but used by a number of programs and libraries (most notably plotting, like matplotlib). One must also not forget the "raster" graphics engine in Qt, but that can be used only within Qt within QPainter (so stand-alone if your program uses Qt, but can't be used without Qt).

When it comes to AGG vs. Cairo the biggest selling point for Cairo is, that it got a pure C binding. Unfortunately Cairo is not a very fast renderer (it made huge progress in the past 10 years). But it's eating the dust of AGG, which is much faster and (ironically) also produces better quality; unfortunately AGG is a C++ library, so to effectively use it you're bound to use C++, which is a downside. Also the principle developer of AGG sadly passed away a few years ago, which stalled development for some time, but it's been picked up by the community again.

like image 22
datenwolf Avatar answered Oct 12 '22 14:10

datenwolf