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