2026-07-14 19:27:27 +02:00
|
|
|
|
/*
|
|
|
|
|
|
egl-display.hpp - output video from a GTK application, using EGL - OpenGL binding
|
|
|
|
|
|
|
|
|
|
|
|
Copyright (C)
|
|
|
|
|
|
2025, Benny Lyons <benny.lyons@gmx.net>
|
|
|
|
|
|
2025, Hermann Vosseler <Ichthyostega@web.de>
|
|
|
|
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify it
|
|
|
|
|
|
under the terms of the GNU GPL version 2+ See the LICENSE file for details.
|
|
|
|
|
|
|
|
|
|
|
|
* ************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "commons.hpp"
|
2026-07-14 20:16:18 +02:00
|
|
|
|
#include "gtk-uring-app.hpp"
|
2026-07-14 19:27:27 +02:00
|
|
|
|
|
2026-07-15 18:56:37 +02:00
|
|
|
|
#include <functional>
|
2026-07-14 19:27:27 +02:00
|
|
|
|
#include <algorithm>
|
|
|
|
|
|
#include <cassert>
|
|
|
|
|
|
#include <string>
|
2026-07-15 18:56:37 +02:00
|
|
|
|
#include <array>
|
2026-07-14 19:27:27 +02:00
|
|
|
|
#include <set>
|
|
|
|
|
|
|
|
|
|
|
|
// for low-level access -> X-Window
|
|
|
|
|
|
#include <gdk/gdkx.h>
|
|
|
|
|
|
|
|
|
|
|
|
// X11, EGL and GLEW for OpenGL
|
|
|
|
|
|
#include <X11/Xlib.h>
|
|
|
|
|
|
#include <EGL/egl.h>
|
|
|
|
|
|
#include <EGL/eglext.h> // defines plattform-specific config and extensions
|
|
|
|
|
|
#include <GL/glew.h> // provides system-specific bindings for OpenGL extensions
|
|
|
|
|
|
|
|
|
|
|
|
using std::string;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Connection and drawing context used for EGL based video display.
|
|
|
|
|
|
*/
|
2026-07-15 18:56:37 +02:00
|
|
|
|
template<typename PIX, uint WIDTH, uint HEIGHT>
|
2026-07-14 19:27:27 +02:00
|
|
|
|
class EglDisplay
|
2026-07-15 20:40:59 +02:00
|
|
|
|
: NonCopyable
|
2026-07-14 19:27:27 +02:00
|
|
|
|
{
|
2026-07-15 18:56:37 +02:00
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
using Buffer = std::array<PIX, WIDTH*HEIGHT * 3 >;
|
|
|
|
|
|
|
2026-07-15 20:40:59 +02:00
|
|
|
|
/**
|
|
|
|
|
|
* Interface retrieve pixel data for video frames to display.
|
|
|
|
|
|
* - this is a Glib::Dispatcher, and the attached slot shall be
|
|
|
|
|
|
* triggered whenever data for a new frame is available
|
|
|
|
|
|
* - pixel data is accessed (threadsafe) by invoking #retrieveFrame()
|
|
|
|
|
|
* - when processing is complete and the data will no longer be needed,
|
|
|
|
|
|
* access to the pixel buffer is yielded by invoking #completedFrame()
|
|
|
|
|
|
*/
|
|
|
|
|
|
class PixelFeed
|
|
|
|
|
|
: public Glib::Dispatcher
|
|
|
|
|
|
{
|
|
|
|
|
|
public:
|
|
|
|
|
|
virtual ~PixelFeed() { }; ///< this is an interface
|
|
|
|
|
|
virtual Buffer* retrieveFrame() =0;
|
|
|
|
|
|
virtual void completedFrame() =0;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
EglDisplay (Gtk::Window& appWindow, PixelFeed& pixelFeed)
|
|
|
|
|
|
: pixelFeed_{pixelFeed}
|
2026-07-15 18:56:37 +02:00
|
|
|
|
{
|
|
|
|
|
|
openDisplay (appWindow);
|
2026-07-15 20:40:59 +02:00
|
|
|
|
pixelFeed_.connect([this]{ displayFrame(); });
|
2026-07-15 18:56:37 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cleanUp();
|
|
|
|
|
|
|
|
|
|
|
|
private:
|
2026-07-14 19:27:27 +02:00
|
|
|
|
/** EGL / X11 connection. */
|
2026-07-14 20:16:18 +02:00
|
|
|
|
EGLDisplay display_{nullptr};
|
|
|
|
|
|
EGLSurface surface_{nullptr};
|
2026-07-14 19:27:27 +02:00
|
|
|
|
|
2026-07-14 20:16:18 +02:00
|
|
|
|
EGLContext egl_{nullptr};
|
2026-07-14 19:27:27 +02:00
|
|
|
|
|
2026-07-14 20:16:18 +02:00
|
|
|
|
uint texID_{0};
|
|
|
|
|
|
uint vboID_{0};
|
|
|
|
|
|
uint vaoID_{0};
|
2026-07-14 19:27:27 +02:00
|
|
|
|
|
2026-07-14 20:16:18 +02:00
|
|
|
|
float scaleX_{1};
|
|
|
|
|
|
float scaleY_{1};
|
2026-07-14 19:27:27 +02:00
|
|
|
|
|
2026-07-15 18:56:37 +02:00
|
|
|
|
/** Callback functor to access current pixel data */
|
2026-07-15 20:40:59 +02:00
|
|
|
|
PixelFeed& pixelFeed_{};
|
2026-07-14 20:16:18 +02:00
|
|
|
|
|
|
|
|
|
|
void openDisplay (Gtk::Window&);
|
|
|
|
|
|
void displayFrame();
|
2026-07-14 19:27:27 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-07-15 18:56:37 +02:00
|
|
|
|
template<typename PIX, uint WIDTH, uint HEIGHT>
|
2026-07-14 20:16:18 +02:00
|
|
|
|
void
|
2026-07-15 18:56:37 +02:00
|
|
|
|
EglDisplay<PIX,WIDTH,HEIGHT>::openDisplay (Gtk::Window& appWindow)
|
2026-07-14 19:27:27 +02:00
|
|
|
|
{
|
|
|
|
|
|
std::cout << "Open display-connection through EGL..." << std::endl;
|
|
|
|
|
|
|
|
|
|
|
|
// use the X-Window as anchor to build an OpenGL context via EGL
|
|
|
|
|
|
Glib::RefPtr<Gdk::Window> gdkWindow = appWindow.get_window();
|
|
|
|
|
|
Window xWindow = GDK_WINDOW_XID (gdkWindow->gobj());
|
|
|
|
|
|
Display* xDisplay = GDK_WINDOW_XDISPLAY (gdkWindow->gobj());
|
|
|
|
|
|
|
|
|
|
|
|
long xScreen;
|
|
|
|
|
|
XWindowAttributes xWinAttrs;
|
|
|
|
|
|
if (XGetWindowAttributes (xDisplay, xWindow, &xWinAttrs))
|
|
|
|
|
|
xScreen = XScreenNumberOfScreen (xWinAttrs.screen);
|
|
|
|
|
|
else
|
|
|
|
|
|
__FAIL ("unable to retrieve screen number from the X11 window attributes.");
|
|
|
|
|
|
|
|
|
|
|
|
long SCREEN_SPEC[]
|
|
|
|
|
|
{EGL_PLATFORM_X11_SCREEN_EXT, xScreen
|
|
|
|
|
|
,EGL_NONE};
|
|
|
|
|
|
|
2026-07-14 20:16:18 +02:00
|
|
|
|
display_ = eglGetPlatformDisplay (EGL_PLATFORM_X11_EXT, xDisplay, SCREEN_SPEC);
|
|
|
|
|
|
if (EGL_NO_DISPLAY == display_
|
|
|
|
|
|
or not eglInitialize(display_, NULL,NULL))
|
2026-07-14 19:27:27 +02:00
|
|
|
|
__FAIL ("could not establish EGL Display connection.");
|
|
|
|
|
|
|
|
|
|
|
|
EGLint DESIRED_ATTRIBS[]
|
|
|
|
|
|
{EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER
|
|
|
|
|
|
,EGL_RED_SIZE, 4
|
|
|
|
|
|
,EGL_GREEN_SIZE, 4
|
|
|
|
|
|
,EGL_BLUE_SIZE, 4
|
|
|
|
|
|
,EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT // config must support creating an OpenGL context
|
|
|
|
|
|
,EGL_SURFACE_TYPE, EGL_WINDOW_BIT // want to create a window surface
|
|
|
|
|
|
,EGL_DEPTH_SIZE, 0 // prefer config without depth buffer (occlusion testing not needed)
|
|
|
|
|
|
,EGL_STENCIL_SIZE, 0 // prefer config without stencil buffer (no advanced visual effects)
|
|
|
|
|
|
,EGL_NONE};
|
|
|
|
|
|
|
|
|
|
|
|
EGLint _cnt;
|
|
|
|
|
|
EGLConfig config;
|
2026-07-14 20:16:18 +02:00
|
|
|
|
if (not eglChooseConfig (display_
|
2026-07-14 19:27:27 +02:00
|
|
|
|
,DESIRED_ATTRIBS
|
|
|
|
|
|
,& config
|
|
|
|
|
|
,1,&_cnt)
|
|
|
|
|
|
or not config)
|
|
|
|
|
|
__FAIL ("unable to select a EGL display config with the required attributes");
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-07-14 20:16:18 +02:00
|
|
|
|
surface_ = eglCreateWindowSurface (display_,config,xWindow,nullptr);
|
|
|
|
|
|
if (not surface_)
|
2026-07-14 19:27:27 +02:00
|
|
|
|
switch (eglGetError())
|
|
|
|
|
|
{
|
|
|
|
|
|
case EGL_BAD_CONFIG:
|
|
|
|
|
|
__FAIL ("selected display configuration is not valid");
|
|
|
|
|
|
break;
|
|
|
|
|
|
case EGL_BAD_ALLOC:
|
|
|
|
|
|
__FAIL ("unable to allocate resources or collision with existing resources");
|
|
|
|
|
|
break;
|
|
|
|
|
|
case EGL_BAD_MATCH:
|
|
|
|
|
|
__FAIL ("mismatch between X11 window visuals and the capabilities of the selected config");
|
|
|
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
|
|
|
__FAIL ("unable to create EGL window surface, for unknown reasons");
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (not eglBindAPI( EGL_OPENGL_API))
|
|
|
|
|
|
__FAIL ("unable to configure EGL for usage with the OpenGL API.");
|
|
|
|
|
|
|
|
|
|
|
|
EGLint CTX_ATTRIBS[] // Note: require modern OpenGL
|
|
|
|
|
|
{EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT
|
|
|
|
|
|
// ,EGL_CONTEXT_MAJOR_VERSION, 3
|
|
|
|
|
|
// ,EGL_CONTEXT_MINOR_VERSION, 3
|
|
|
|
|
|
,EGL_NONE};
|
|
|
|
|
|
|
2026-07-14 20:16:18 +02:00
|
|
|
|
egl_ = eglCreateContext (display_, config
|
|
|
|
|
|
,EGL_NO_CONTEXT // do not share definitions and data with another context
|
|
|
|
|
|
,CTX_ATTRIBS);
|
|
|
|
|
|
if (not egl_)
|
2026-07-14 19:27:27 +02:00
|
|
|
|
__FAIL ("failed to create OpenGL context for this display with desired visuals");
|
|
|
|
|
|
|
|
|
|
|
|
// create a binding for the current thread to use this context on this window
|
2026-07-14 20:16:18 +02:00
|
|
|
|
if (not eglMakeCurrent (display_, surface_,surface_, egl_))
|
2026-07-14 19:27:27 +02:00
|
|
|
|
__FAIL ("failed to attach an OpenGL context to the application X-Window");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// use lib-GLEW to manage OpenGL extensions (notably GL Shader Language)
|
|
|
|
|
|
GLenum glewErr = glewInit();
|
|
|
|
|
|
if (GLEW_OK != glewErr)
|
|
|
|
|
|
__FAIL ("could not bind OpenGL extensions through lib-GLEW:\n"
|
|
|
|
|
|
+string{reinterpret_cast<const char*> (glewGetErrorString(glewErr))});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
auto compileShader = [](GLenum kind, string code)
|
|
|
|
|
|
{
|
|
|
|
|
|
uint shaderID = glCreateShader (kind);
|
|
|
|
|
|
const GLchar* codeSegment = code.c_str();
|
|
|
|
|
|
glShaderSource (shaderID, 1, &codeSegment, NULL);
|
|
|
|
|
|
glCompileShader(shaderID);
|
|
|
|
|
|
// diagnostics...
|
|
|
|
|
|
int success;
|
|
|
|
|
|
glGetShaderiv (shaderID, GL_COMPILE_STATUS, &success);
|
|
|
|
|
|
if (not success)
|
|
|
|
|
|
{
|
|
|
|
|
|
int logSiz{0};
|
|
|
|
|
|
glGetShaderiv (shaderID, GL_INFO_LOG_LENGTH, &logSiz);
|
|
|
|
|
|
string report(logSiz,'.');
|
|
|
|
|
|
glGetShaderInfoLog (shaderID, logSiz, NULL, & report[0]);
|
|
|
|
|
|
std::cout << "\n■□■□■□■□■----Shader-Code-------------------------\n"
|
|
|
|
|
|
<< code
|
|
|
|
|
|
<< "\n■□■□■□■□■----Failure-Report----------------------\n"
|
|
|
|
|
|
<< report.c_str()
|
|
|
|
|
|
<< std::endl;
|
|
|
|
|
|
__FAIL ("shader compilation failure");
|
|
|
|
|
|
}
|
|
|
|
|
|
return shaderID;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
auto checkLinkError = [](uint programID)
|
|
|
|
|
|
{
|
|
|
|
|
|
int success;
|
|
|
|
|
|
glGetProgramiv (programID, GL_LINK_STATUS, &success);
|
|
|
|
|
|
if (not success)
|
|
|
|
|
|
{
|
|
|
|
|
|
int logSiz{0};
|
|
|
|
|
|
glGetProgramiv (programID, GL_INFO_LOG_LENGTH, &logSiz);
|
|
|
|
|
|
string report(logSiz,'.');
|
|
|
|
|
|
glGetProgramInfoLog (programID, logSiz, NULL, & report[0]);
|
|
|
|
|
|
std::cout << "\n■□■□■□■□■----Failure-Report----------------------\n"
|
|
|
|
|
|
<< report.c_str()
|
|
|
|
|
|
<< std::endl;
|
|
|
|
|
|
__FAIL ("unable to link shader code");
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uint vertexShader
|
|
|
|
|
|
= compileShader (GL_VERTEX_SHADER,
|
|
|
|
|
|
R"-(#version 330 core
|
|
|
|
|
|
|
|
|
|
|
|
layout(location = 0) in vec2 position;
|
|
|
|
|
|
layout(location = 1) in vec2 texCoord;
|
|
|
|
|
|
|
|
|
|
|
|
out vec2 fragTexCoord;
|
|
|
|
|
|
|
|
|
|
|
|
void main() {
|
|
|
|
|
|
gl_Position = vec4 (position, 0.0, 1.0);
|
|
|
|
|
|
fragTexCoord = texCoord;
|
|
|
|
|
|
}
|
|
|
|
|
|
)-");
|
|
|
|
|
|
|
|
|
|
|
|
uint fragmentShader
|
|
|
|
|
|
= compileShader (GL_FRAGMENT_SHADER,
|
|
|
|
|
|
R"-(#version 330 core
|
|
|
|
|
|
|
|
|
|
|
|
in vec2 fragTexCoord;
|
|
|
|
|
|
out vec4 colour;
|
|
|
|
|
|
|
|
|
|
|
|
uniform sampler2D textureSampler;
|
|
|
|
|
|
|
|
|
|
|
|
void main() {
|
|
|
|
|
|
colour = texture (textureSampler, fragTexCoord);
|
|
|
|
|
|
}
|
|
|
|
|
|
)-");
|
|
|
|
|
|
|
|
|
|
|
|
uint gpuProgram = glCreateProgram();
|
|
|
|
|
|
glAttachShader (gpuProgram, vertexShader);
|
|
|
|
|
|
glAttachShader (gpuProgram, fragmentShader);
|
|
|
|
|
|
glLinkProgram (gpuProgram);
|
|
|
|
|
|
checkLinkError (gpuProgram);
|
|
|
|
|
|
|
|
|
|
|
|
glUseProgram (gpuProgram);
|
|
|
|
|
|
glDeleteShader(vertexShader);
|
|
|
|
|
|
glDeleteShader(fragmentShader);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* === setup a 2D texture, to be mapped into the viewport === */
|
|
|
|
|
|
glActiveTexture(GL_TEXTURE0); // activate texture-unit #0 on the GPU
|
2026-07-14 20:16:18 +02:00
|
|
|
|
glGenTextures (1, &texID_); // allocate 1 new texture ID
|
|
|
|
|
|
glBindTexture (GL_TEXTURE_2D, texID_); // use this textureID as "the" TEXTURE_2D
|
2026-07-14 19:27:27 +02:00
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // configure image scaling filter
|
|
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); // clamp, don't wrap at the texture edges
|
|
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
|
|
|
|
|
|
|
|
|
|
// configure the `textureSampler` parameter (»uniform« is a global parameter visible in all shaders)
|
|
|
|
|
|
GLint samplerParam = glGetUniformLocation(gpuProgram, "textureSampler");
|
|
|
|
|
|
glUniform1i(samplerParam, 0); // configure the shader to use texture-unit #0 of the GPU
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* === setup a fixed geometry to hold that texture === */
|
|
|
|
|
|
glDisable (GL_DEPTH_TEST); // we do not need 3D layering / positioning
|
2026-07-15 18:56:37 +02:00
|
|
|
|
glViewport (0, 0, WIDTH, HEIGHT); // Origin in the middle of the window (note Y points upwards)
|
2026-07-14 19:27:27 +02:00
|
|
|
|
|
|
|
|
|
|
// Note: this demo uses a fixed-size window and hard-coded video size;
|
|
|
|
|
|
// a real-world implementation would have to place the video frame
|
|
|
|
|
|
// dynamically into the available screen space, possibly scaling up/down
|
2026-07-14 20:16:18 +02:00
|
|
|
|
GLfloat sX{scaleX_};
|
|
|
|
|
|
GLfloat sY{scaleY_}; // Note: positions use »normalised device coordinates« (NDC)
|
2026-07-14 19:27:27 +02:00
|
|
|
|
GLfloat w{1}; // TEXTURE_2D points cover the range (0,0) ... (1,1)
|
|
|
|
|
|
GLfloat h{1};
|
|
|
|
|
|
|
|
|
|
|
|
GLfloat vertexData[] // Geometry and mapping data to be sent to the GPU...
|
|
|
|
|
|
//-point-+--texture // We define the 4 edges of a rectangular »projection screen«
|
|
|
|
|
|
{-sX,-sY , 0,h // For each edge, we define the position in NDC [-1 ... +1]
|
|
|
|
|
|
, sX,-sY , w,h // and we specify which point in the texture to map there
|
|
|
|
|
|
, sX, sY , w,0 // Note: the rows of the video image run top-down, and thus we
|
|
|
|
|
|
,-sX, sY , 0,0 // have to map the texture flipped, since OpenGL orients Y upwards.
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-07-14 20:16:18 +02:00
|
|
|
|
glGenBuffers (1, &vboID_); // allocate 1 new vertex-buffer-object
|
|
|
|
|
|
glGenVertexArrays(1, &vaoID_); // allocate 1 new vertex-array-object (to attach all definitions)
|
2026-07-14 19:27:27 +02:00
|
|
|
|
|
2026-07-14 20:16:18 +02:00
|
|
|
|
glBindVertexArray(vaoID_); // activate the VAO to pick up the following definitions...
|
|
|
|
|
|
glBindBuffer (GL_ARRAY_BUFFER, vboID_);
|
2026-07-14 19:27:27 +02:00
|
|
|
|
// copy the vertexData into the vertex-buffer-object currently bound to GL_ARRAY_BUFFER
|
|
|
|
|
|
glBufferData (GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_STATIC_DRAW);
|
|
|
|
|
|
|
|
|
|
|
|
auto SIZ = sizeof(float); // parameters are transferred to the GPU as float data
|
|
|
|
|
|
auto attrib = 2; // we use 2 input attributes for the shader pipeline
|
|
|
|
|
|
auto coords = 2; // our data vectors have 2 coordinates (2D)
|
|
|
|
|
|
auto stride = attrib * coords * SIZ; // stride is the offset in bytes between full data points
|
|
|
|
|
|
auto offset = [&](uint elm){ return (void*) (elm * SIZ); }; // offset of one attribute relative to the start of a data point (packaged into a void*)
|
|
|
|
|
|
auto normalise = GL_FALSE; // do not auto-normalise, positions are already [-1 ... +1]
|
|
|
|
|
|
|
|
|
|
|
|
// define how two two attributes are packed into the data sequence we send to the GPU
|
|
|
|
|
|
glVertexAttribPointer (0, coords, GL_FLOAT, normalise, stride, offset(0) );
|
|
|
|
|
|
glVertexAttribPointer (1, coords, GL_FLOAT, normalise, stride, offset(2) );
|
|
|
|
|
|
glEnableVertexAttribArray (0); // activate attribute #1 ≙ input argument `position` in vertexShader
|
|
|
|
|
|
glEnableVertexAttribArray (1); // activate attribute #2 ≙ input argument `texCoord` in vertexShader
|
|
|
|
|
|
|
2026-07-15 18:56:37 +02:00
|
|
|
|
std::cout << "Successfully established OpenGL display ("<<WIDTH<<"x"<<HEIGHT<<"px) within GTK-Window." << std::endl;
|
2026-07-14 19:27:27 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-07-15 18:56:37 +02:00
|
|
|
|
template<typename PIX, uint WIDTH, uint HEIGHT>
|
2026-07-14 19:27:27 +02:00
|
|
|
|
void
|
2026-07-15 18:56:37 +02:00
|
|
|
|
EglDisplay<PIX,WIDTH,HEIGHT>::displayFrame()
|
2026-07-14 19:27:27 +02:00
|
|
|
|
{
|
|
|
|
|
|
|
2026-07-15 20:40:59 +02:00
|
|
|
|
auto currFrame = pixelFeed_.retrieveFrame();
|
2026-07-15 18:56:37 +02:00
|
|
|
|
if (not currFrame) return;
|
|
|
|
|
|
|
|
|
|
|
|
// pick up current buffer with RGB data
|
|
|
|
|
|
// and send it to the GPU (into the prepared texture)...
|
2026-07-14 19:27:27 +02:00
|
|
|
|
glTexImage2D (GL_TEXTURE_2D // the target texture store to work on, here "the" TEXTURE_2D
|
|
|
|
|
|
,0 // detail level (when using mipmap reduction, which we don't)
|
|
|
|
|
|
,GL_RGB // internal format or features to use for this texture
|
2026-07-15 18:56:37 +02:00
|
|
|
|
,WIDTH,HEIGHT, /*border*/ 0
|
2026-07-14 19:27:27 +02:00
|
|
|
|
,GL_RGB // data layout of the provided pixels
|
|
|
|
|
|
,GL_UNSIGNED_BYTE // data format / size of the pixels
|
2026-07-15 18:56:37 +02:00
|
|
|
|
,currFrame
|
2026-07-14 19:27:27 +02:00
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
// Draw the quadrilateral
|
|
|
|
|
|
glDrawArrays(GL_QUADS, 0, 4);
|
|
|
|
|
|
|
|
|
|
|
|
// double-buffer flip, automatically invokes glFlush()
|
2026-07-14 20:16:18 +02:00
|
|
|
|
eglSwapBuffers (display_, surface_);
|
2026-07-15 20:40:59 +02:00
|
|
|
|
|
|
|
|
|
|
// signal that processing of this frame is complete
|
|
|
|
|
|
pixelFeed_.completedFrame();
|
2026-07-14 19:27:27 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-07-15 18:56:37 +02:00
|
|
|
|
|
|
|
|
|
|
template<typename PIX, uint WIDTH, uint HEIGHT>
|
2026-07-14 19:27:27 +02:00
|
|
|
|
void
|
2026-07-15 18:56:37 +02:00
|
|
|
|
EglDisplay<PIX,WIDTH,HEIGHT>::cleanUp()
|
2026-07-14 19:27:27 +02:00
|
|
|
|
{
|
2026-07-15 18:56:37 +02:00
|
|
|
|
// std::cout << "STOP " << imgGen_.getFrameNr() << " frames displayed." << std::endl;
|
2026-07-14 19:27:27 +02:00
|
|
|
|
|
|
|
|
|
|
// detach binding with OpenGL context
|
2026-07-14 20:16:18 +02:00
|
|
|
|
eglMakeCurrent (display_, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
|
|
|
|
|
eglDestroySurface (display_, surface_);
|
|
|
|
|
|
eglDestroyContext (display_, egl_);
|
|
|
|
|
|
eglTerminate(display_); // detach EGL from display
|
2026-07-14 19:27:27 +02:00
|
|
|
|
}
|