X11: Off screen: glBlitFramebuffer() operation: Random flickers

Greetings and Hello!

This is my first post, and it is my first attempt to use hardware acceleration. Because I am an absolute beginner with OpenGL, and am totally self taught, could I ask for a quick review of my code? This is not homework. I just need a bit of mentoring at the start to see if there is anything obviously ridiculous, or you can advise to do things better.

I wanted a source image to be recast as a backround image for my X11 window. It is event driven when the user re-sizes the window. If I mouse-grab the window and continually resize it, the transitions are smooth - but there are occasional random flickers (this might be normal).

For the off screen context, the EGL section was more of a fluke by trial and error - so I need to learn more about that - but it seems to work.

Thank you kindly.

This code example is runnable and is stand alone.

/* lgpu010.cpp:  Window redraws when resized,
     using a shared memory buffer and XShmCreatePixmap(),
     and using glBlitFramebuffer() to recast source image to background size.
   g++ lgpu010.cpp -o lgpu -lX11 -lXext -lGL -lEGL         [260712]  */


#define GL_GLEXT_PROTOTYPES 1

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/XShm.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <GL/gl.h>
#include <EGL/egl.h>
#include <math.h>
#include <stdio.h>
using namespace std;

Display *dis;
GC gc;
Window win;
Atom WM_DELETE_WINDOW;
XShmSegmentInfo shminfo;
Pixmap pxmp;
EGLDisplay egldis = EGL_NO_DISPLAY;
EGLContext eglctx = EGL_NO_CONTEXT;
int waW, waH, bufW, bufH; // working area width/height   and  buffer W/H
int cfgW, cfgH;           // notified configuration W/H
int srcW, srcH;           // source image W/H
unsigned *p;              // pixels
unsigned glFB[2];         // two gl_framebuffers
unsigned glTxtr[2];       // two gl_textures
unsigned src[16000];      // source image
bool loop = true, redraw;

void draw_source() {
  srcW = 160; srcH = 100;
  int orb[] = {110, 20, 48, 48, -51, 80,
               30, 45, 60, 0, -51, 50,
               90, 80, 0, 45, -51, 50};
  int i, j, m, n = 0;
  unsigned char r, g, b;
  double q;
  for (j = 0; j < srcH; j++) {
    for (i = 0; i < srcW; i++) {
      r = 0; g = 0; b = 51;
      for (m = 0; m < 18; m += 6) {
        q = sqrt(pow((i - orb[m]), 2) + pow((j - orb[m + 1]), 2));
        if (q < orb[m + 5]) {
          q = cos(q / orb[m + 5] * M_PI_2);
          r += q * orb[m + 2];
          g += q * orb[m + 3];
          if (b > -q * orb[m + 4]) b += q * orb[m + 4]; else b = 0;
        }
      }
      src[n] = 0xff000000 + r * 0x10000 + g * 0x100 + b;
      n++;
    }
  }
}

void resize_draw() {
  redraw = false;
  bufW = cfgW; bufH = cfgH;
  if (pxmp) XFreePixmap(dis, pxmp);
  pxmp = XShmCreatePixmap(dis, win, shminfo.shmaddr, &shminfo,
          bufW, bufH, 24);
  unsigned d, c;
  int i, j, k, m, n, o;
  double r, q;
            //Section: prepare the 'draw' buffer (background image)
  glGenFramebuffers(1, &glFB[1]);
  glBindFramebuffer(GL_DRAW_FRAMEBUFFER, glFB[1]);
  glFramebufferParameteri(GL_DRAW_FRAMEBUFFER,
          GL_FRAMEBUFFER_DEFAULT_WIDTH, bufW);
  glFramebufferParameteri(GL_DRAW_FRAMEBUFFER,
          GL_FRAMEBUFFER_DEFAULT_HEIGHT, bufH);
  glGenTextures(1, &glTxtr[1]);
  glBindTexture(GL_TEXTURE_2D, glTxtr[1]);
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, bufW, bufH, 0, GL_RGBA,
          GL_UNSIGNED_BYTE, nullptr);
  glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
          GL_TEXTURE_2D, glTxtr[1], 0);
  if (glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER)
          != GL_FRAMEBUFFER_COMPLETE) printf("glFG[1] not complete\n");
           //Section: OpenGL performs the actual resizing of image
  q = (double)bufW / bufH; r = (double)srcW / srcH;
  if (q > r) {
    m = srcW / q + 0.5; i = 0; j = (srcH - m) / 2.0 + 0.5; k = srcW; m += j;
  }
  else {
    k = srcH * q + 0.5; i = (srcW - k) / 2.0 + 0.5; j = 0; m = srcH; k += i; 
  }
  glBlitFramebuffer(i, j, k, m, 0, 0, bufW, bufH,
          GL_COLOR_BUFFER_BIT, GL_LINEAR);
  //  now the 'draw' buffer (glFB[1]) must temporarily be set as 'read',
  glBindFramebuffer(GL_READ_FRAMEBUFFER, glFB[1]);
  //  so that it can then be copied into client memory,
  glReadPixels(0, 0, bufW, bufH, GL_RGBA, GL_UNSIGNED_BYTE, shminfo.shmaddr);
  //  then bind back the original read buffer (glFB[0]) as 'read'.
  glBindFramebuffer(GL_READ_FRAMEBUFFER, glFB[0]);
  glDeleteTextures(1, &glTxtr[1]);
  glDeleteFramebuffers(1, &glFB[1]);
  GLenum err = glGetError();
  if (err != GL_NO_ERROR) printf("OpenGL glFB[1] error: %u\n", err);
            //Section: Circle
  c = 0xffaaaa66;
  k = (bufH / 2) * bufW + (bufW / 2);
  if (bufW <= bufH) r = bufW * 0.45;
  else r = bufH * 0.45;
  o = 0.7071 * r + 1.5;
  for (i = 0; i < o; i++) {
    j = (cos(asin(i / r)) * r + 0.5);
    n = i * bufW; m = j * bufW;
    p[k + i + m] = c; p[k + i - m] = c;
    p[k - i + m] = c; p[k - i - m] = c;
    p[k + j + n] = c; p[k + j - n] = c;
    p[k - j + n] = c; p[k - j - n] = c;
  }
            //Section: Show source image
  m = 0;
  n = (bufH - srcH) / 2  * bufW + (bufW - srcW) / 2;
  for (j = 0; j < srcH; j++) {
    for (i = 0; i < srcW; i++) {
      p[n] = src[m];
      m++; n++;
    }
    n += bufW - srcW;
  } 
            //Section: Rectangles
  XSetForeground(dis, gc, 0xffff7733);
  XDrawRectangle(dis, pxmp, gc, 2, 2, 100, 50);
  XDrawRectangle(dis, pxmp, gc, bufW - 102, bufH - 52, 100, 50);
}

void paint() {
  XCopyArea(dis, pxmp, win, gc, 0, 0, bufW, bufH, 0, 0);
  XFlush(dis);
  redraw = true;
}

void init() {
  dis = XOpenDisplay(0);
  waW = XDisplayWidth(dis, 0); waH = XDisplayHeight(dis, 0);
  cfgW = 960; cfgH = 540;
                  //Section: Set up shared memory
  shminfo.readOnly = False;
  shminfo.shmid = shmget(IPC_PRIVATE, waW * waH * 4, IPC_CREAT | 0777);
  shminfo.shmaddr = reinterpret_cast<char*>(shmat(shminfo.shmid, 0, 0));
  p = reinterpret_cast<unsigned*>(shminfo.shmaddr);
  XShmAttach(dis, &shminfo);
                  //Section: Initiate EGL
  egldis = eglGetDisplay(EGL_DEFAULT_DISPLAY);
  int major, minor;
  if (!eglInitialize(egldis, &major, &minor)) {
          printf("EGL initialization failed.\n"); loop = false; return;}
  //printf("major = %d\n", major); printf("minor = %d\n", minor);
  //printf("%s\n", eglQueryString(egldis, EGL_EXTENSIONS));
  if (!eglBindAPI(EGL_OPENGL_API)) {
          printf("EGL Failed to bind to OpenGL API.\n");
          loop = false; return; }
  int eglattribs[] = { EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
          EGL_ALPHA_SIZE, 8, EGL_RED_SIZE, 8,
          EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_NONE };
  EGLConfig eglcfg;
  int numcfgs;
  if (!eglChooseConfig(egldis, eglattribs, &eglcfg, 1, &numcfgs)) {
          printf("Failed to choose EGL config.\n"); loop = false; return; } 
  //printf("number of configs = %d\n", numcfgs);
  int ctx_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
  eglctx = eglCreateContext(egldis, eglcfg, EGL_NO_CONTEXT, ctx_attribs);
  if (eglctx == EGL_NO_CONTEXT) { printf("Failed to create EGL context.\n");
          loop = false; return; }
  if (!eglMakeCurrent(egldis, EGL_NO_SURFACE, EGL_NO_SURFACE, eglctx)) {
          printf("Failed to make context current.\n");
          loop = false; return; }
                  //Section: Prepare the 'read' buffer (source image)
  glGenFramebuffers(1, &glFB[0]);
  glBindFramebuffer(GL_READ_FRAMEBUFFER, glFB[0]);
  glFramebufferParameteri(GL_READ_FRAMEBUFFER,
          GL_FRAMEBUFFER_DEFAULT_WIDTH, srcW);
  glFramebufferParameteri(GL_READ_FRAMEBUFFER,
          GL_FRAMEBUFFER_DEFAULT_HEIGHT, srcH);
  glGenTextures(1, &glTxtr[0]);
  glBindTexture(GL_TEXTURE_RECTANGLE, glTxtr[0]);
  glTexImage2D(GL_TEXTURE_RECTANGLE, 0, GL_RGBA8, srcW, srcH, 0, GL_RGBA,
          GL_UNSIGNED_BYTE, nullptr);
  glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
          GL_TEXTURE_RECTANGLE, glTxtr[0], 0);
  glTexSubImage2D(GL_TEXTURE_RECTANGLE, 0, 0, 0, srcW, srcH, GL_RGBA,
          GL_UNSIGNED_BYTE, reinterpret_cast<char*>(src));
  GLenum err = glGetError();
  if (err != GL_NO_ERROR) printf("OpenGL error: %u\n", err);
  if (glCheckFramebufferStatus(GL_READ_FRAMEBUFFER)
          != GL_FRAMEBUFFER_COMPLETE) printf("glFG[0] not complete\n");
                  //Section: Prep Window
  gc = XDefaultGC(dis, DefaultScreen(dis));
  int attriMask = CWBackPixel | CWWinGravity | CWEventMask;
  XSetWindowAttributes winAttr;
  winAttr.background_pixel = 0xff000033;
  winAttr.win_gravity = CenterGravity;
  winAttr.event_mask = KeyReleaseMask | ButtonPressMask |
          PointerMotionMask | ExposureMask | StructureNotifyMask;
  int left = (waW - cfgW) / 2;
  int top = (waH - cfgH) / 2;
  win = XCreateWindow(dis, XDefaultRootWindow(dis), left, top,
          cfgW, cfgH, 1, DefaultDepth(dis,0), InputOutput,
          DefaultVisual(dis, 0), attriMask, &winAttr);
  WM_DELETE_WINDOW = XInternAtom(dis, "WM_DELETE_WINDOW", False);
  XSetWMProtocols(dis, win, &WM_DELETE_WINDOW, 1);
  resize_draw();
  XSizeHints sh; XWMHints hnts; XTextProperty nm, icnm;
  sh.flags = PPosition | PSize | PMinSize | PMaxSize | PWinGravity;
  sh.x = left; sh.y = top; sh.width = cfgW; sh.height = cfgH;
  sh.max_width = waW; sh.max_height = waH; 
  sh.min_width = 320; sh.min_height = 180;
  sh.win_gravity = CenterGravity;
  hnts.flags = 1; hnts.input = true;
  char snm[] = "Test GUI(Title Bar Text)";
  char *psnm = snm;
  XStringListToTextProperty(&psnm, 1, &nm);
  char sicnm[] = "Test GUI(Icon Text)";
  char *psicnm = sicnm;
  XStringListToTextProperty(&psicnm, 1, &icnm);
  XSetWMProperties(dis, win, &nm, &icnm, NULL, 0, &sh, &hnts, NULL);
  XMapWindow(dis, win);
}

int main() {
  draw_source();
  init();
  if (!loop) return 1;
  KeySym key;
  char text;
  bool pflag;
  XEvent evnt;
  XConfigureEvent *xcfg;
  while(loop) {
    pflag = false;
    XNextEvent(dis, &evnt);
    switch(evnt.type) {
      case KeyRelease:
        XLookupString(&evnt.xkey, &text, 1, &key, 0);
        if ((key == XK_Escape) || (key == XK_q)) loop = false;
        //else printf("You pressed the %c key!\n",  text);
        break;
      case Expose:
        pflag = true; break;
      case ConfigureNotify:
        xcfg = reinterpret_cast<XConfigureEvent*>(&evnt);
        cfgW = xcfg->width; cfgH = xcfg->height;
        if (redraw && (cfgW != bufW || cfgH != bufH)) resize_draw();
        break;
      case ClientMessage:
        if ((Atom) evnt.xclient.data.l[0] == WM_DELETE_WINDOW)
                loop = false;
        break;
    }
    if (pflag) paint();
  }
  XShmDetach(dis, &shminfo);
  shmdt(shminfo.shmaddr);
  shmctl(shminfo.shmid, IPC_RMID, 0);
  XDestroyWindow(dis, win);
  return 0;
}

It might be easier to read my code here https://github.com/LeonCSt/beginner_gpu/blob/main/X11_examples/lgpu010.cpp

I don’t know how to help you :thinking:. But I like your code! :+1: