From 4699ae8f03ecdf96790bef72cd9c026c3bd11ae1 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 15 Jul 2026 18:56:37 +0200 Subject: [PATCH] Prototype: settle wiring and configuration of UringVideoPlayer and EglDisplay This implies a basic decision regarding the buffer and state data to use, and how this can be defined, parameterised and passed between threads. I decided to place separation of domains first and to prefer using a functional callback over using a shared data structure. --- FrOSCon26/FrOSCon.26.mm | 241 ++++++++++++++++++++++++++- FrOSCon26/examples/demo-uring.cpp | 25 ++- FrOSCon26/examples/egl-display.hpp | 68 ++++---- FrOSCon26/examples/gtk-uring-app.hpp | 2 +- 4 files changed, 296 insertions(+), 40 deletions(-) diff --git a/FrOSCon26/FrOSCon.26.mm b/FrOSCon26/FrOSCon.26.mm index 3005a4c..9ceb7a7 100644 --- a/FrOSCon26/FrOSCon.26.mm +++ b/FrOSCon26/FrOSCon.26.mm @@ -1171,7 +1171,7 @@

- + @@ -1183,6 +1183,7 @@ + @@ -1787,7 +1788,7 @@ - + @@ -1807,7 +1808,9 @@ - + + + @@ -1816,7 +1819,7 @@ - + @@ -1824,7 +1827,8 @@ - + + @@ -1861,7 +1865,6 @@ - @@ -1870,11 +1873,237 @@ + + + + + + + + + + + + + + + + + + + + + + + +

+ ...es war nicht so, daß Claude diese „Idee gehabt“ hätte — denn ich hatte die Konversation ja so geführt, daß Claude mir meine Konzept-Ansätze validiert und verfeinert. Dann habe ich das Prinzip formuliert, daß für Async-IO irgendwo eine State-Machine sein muß. Claude hat das aufgegriffen, validiert und verschärft; und damit war offensichtlich geworden, daß man diese State-Flag als Atomic auslegen kann. Das habe ich in dem Moment genauso »offensichtlich« gesehen, und Claude hat es ebenso formuliert. +

+

+ Warum ich hier nochmal nachzeichne, wie das zustandegekommen ist: +

+
    +
  • + weil ich allein da nicht draufgekommen wäre +
  • +
  • + aber weil es offensichtlich nicht von Claude stammt +
  • +
+

+ ⟹ das wirft ein Licht darauf, wie dieses scheinbar intelligente »Generieren« funktioniert, auf Basis einer industriellen Daten-Technik, die Plausibilitäten integriert und interpoliert. +

+ + +
+ + +
+ + + + + + + + + + + + + +

+ hier muß auch die Backpressure beachtet und realisiert werden +

+ + +
+ + + + + + + + + + + + +

+ Sofern er aus dem GUI-Thread alloziert wird, also bevor der Proactor-Thread startet, ist die Position für beide Threads sichtbar (und unveränderlich) +

+ + +
+ + + + + + +

+ Diese wäre dann ebenfalls teil der cross-Trade-Synchronisation (ebenfalls bewirkt durch release-Semantik auf dem State-Atomic) +

+ + +
+
+ + + + + + +

+ ...nur als lokale Variable, die er jeweils hochzählt, wenn er einen Trigger bekommt (Nachteil: duplizierter State, dessen Konsistenz sich nur implizit ergibt und nicht offensichtlich ist) +

+ + +
+
+
+ + + + + + +

+ Das wäre dann ein Pointer (const void *); seine Sichtbarkeit ergibt sich auch hier aus dem atomic-State +

+ + +
+ + + + + + + + + + +

+ ...da ich immer in Zuständigkeiten denke, ist es mir unsympatisch, einen Satz gemeinsamer Zustands-Variablen zu verwenden +

+ + +
+
+
+ + + + + + + + + +

+ ...das gilt für ein Prod-System, in dem der Speicher framentiert ist, und man vermutlich Buffer-Pools hat +

+ + +
+ +
+ + + + + + +

+ Für die Demo ist das die offensichtliche Lösung: man bekommt unique-ownership und Subscript-Operator einfach vom std::unique_ptr. Wenn man den notwendigen Speicher nicht bekommt, dann läuft die Demo eh nicht, also wozu die Umstände? +

+ + +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ heißt: hier gibt es keine Möglichkeit, ein Auge zuzudrücken +

+ + +
+
+ + +
+
+ + + + + + + +
+
diff --git a/FrOSCon26/examples/demo-uring.cpp b/FrOSCon26/examples/demo-uring.cpp index 217e371..a7bac4f 100644 --- a/FrOSCon26/examples/demo-uring.cpp +++ b/FrOSCon26/examples/demo-uring.cpp @@ -21,28 +21,43 @@ using std::unique_ptr; //-----------------hard-coded-config-- +const size_t RING_SIZ = 5; + +const uint VIDEO_WIDTH = 320; +const uint VIDEO_HEIGHT = 240; //-----------------hard-coded-config-- class UringVideoPlayer { + using VideoDisplay = EglDisplay; + using Buffer = VideoDisplay::Buffer; + Glib::Dispatcher dispatchFrame_; - EglDisplay videoDisplayer_; + VideoDisplay videoDisplayer_; std::thread proactorThread_; public: UringVideoPlayer (Gtk::Window& appWindow) - : videoDisplayer_{appWindow, dispatchFrame_} + : videoDisplayer_{appWindow, dispatchFrame_, [this]{ return retrieveCurrFrame(); }} , proactorThread_{[this]{ playVideo(); }} { } void terminate(); - + private: void playVideo() { - + // allocate buffer data structure + // initialise URing + // ++ proactor-loop ++ + } + + Buffer* + retrieveCurrFrame() + { + ///////OOO threadsafe hand-over of current frame data } }; @@ -50,7 +65,7 @@ class UringVideoPlayer unique_ptr launchPlayer (Gtk::Window& appWindow) { - appWindow.resize (EglDisplay::VIDEO_WIDTH, EglDisplay::VIDEO_HEIGHT); + appWindow.resize (VIDEO_WIDTH, VIDEO_HEIGHT); auto player = std::make_unique (appWindow); diff --git a/FrOSCon26/examples/egl-display.hpp b/FrOSCon26/examples/egl-display.hpp index 0c525bd..d853bff 100644 --- a/FrOSCon26/examples/egl-display.hpp +++ b/FrOSCon26/examples/egl-display.hpp @@ -14,10 +14,11 @@ #include "commons.hpp" #include "gtk-uring-app.hpp" -#include +#include #include #include #include +#include #include // for low-level access -> X-Window @@ -35,8 +36,27 @@ using std::string; /** * Connection and drawing context used for EGL based video display. */ +template class EglDisplay { + + public: + using Buffer = std::array; + using PixFeed = std::function; + + EglDisplay(Gtk::Window& appWindow + ,Glib::Dispatcher& dispatchFrame + ,PixFeed frameCallback + ) + : retrieveFrame_{frameCallback} + { + openDisplay (appWindow); + dispatchFrame.connect([this]{ displayFrame(); }); + } + + void cleanUp(); + + private: /** EGL / X11 connection. */ EGLDisplay display_{nullptr}; EGLSurface surface_{nullptr}; @@ -50,20 +70,9 @@ class EglDisplay float scaleX_{1}; float scaleY_{1}; - public: - // hard wired here (should be dynamic in real-world usage) - constexpr static uint VIDEO_WIDTH {320}; - constexpr static uint VIDEO_HEIGHT{240}; + /** Callback functor to access current pixel data */ + PixFeed retrieveFrame_{}; - EglDisplay(Gtk::Window& appWindow, Glib::Dispatcher& dispatchFrame) - { - openDisplay (appWindow); - dispatchFrame.connect([this]{ displayFrame(); }); - } - - void cleanUp(); - - private: void openDisplay (Gtk::Window&); void displayFrame(); }; @@ -71,8 +80,9 @@ class EglDisplay +template void -EglDisplay::openDisplay (Gtk::Window& appWindow) +EglDisplay::openDisplay (Gtk::Window& appWindow) { std::cout << "Open display-connection through EGL..." << std::endl; @@ -262,7 +272,7 @@ EglDisplay::openDisplay (Gtk::Window& appWindow) /* === setup a fixed geometry to hold that texture === */ glDisable (GL_DEPTH_TEST); // we do not need 3D layering / positioning - glViewport (0, 0, VIDEO_WIDTH, VIDEO_HEIGHT); // Origin in the middle of the window (note Y points upwards) + glViewport (0, 0, WIDTH, HEIGHT); // Origin in the middle of the window (note Y points upwards) // Note: this demo uses a fixed-size window and hard-coded video size; // a real-world implementation would have to place the video frame @@ -301,28 +311,28 @@ EglDisplay::openDisplay (Gtk::Window& appWindow) glEnableVertexAttribArray (0); // activate attribute #1 ≙ input argument `position` in vertexShader glEnableVertexAttribArray (1); // activate attribute #2 ≙ input argument `texCoord` in vertexShader - std::cout << "Successfully established OpenGL display ("< void -EglDisplay::displayFrame() +EglDisplay::displayFrame() { - uint frameNr = imgGen_.getFrameNr(); - uint fps = imgGen_.getFps(); - if (0 == frameNr % fps) - std::cout << "tick ... " << imgGen_.getFrameNr() << std::endl; - // compute a buffer with RGB data and bind it into the prepared texture... - const void* buffer = imgGen_.buildNext().data(); + auto currFrame = retrieveFrame_(); + if (not currFrame) return; + + // pick up current buffer with RGB data + // and send it to the GPU (into the prepared texture)... 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 - ,VIDEO_WIDTH,VIDEO_HEIGHT, /*border*/ 0 + ,WIDTH,HEIGHT, /*border*/ 0 ,GL_RGB // data layout of the provided pixels ,GL_UNSIGNED_BYTE // data format / size of the pixels - ,buffer + ,currFrame ); // Draw the quadrilateral @@ -333,10 +343,12 @@ EglDisplay::displayFrame() } + +template void -EglDisplay::cleanUp() +EglDisplay::cleanUp() { - std::cout << "STOP " << imgGen_.getFrameNr() << " frames displayed." << std::endl; +// std::cout << "STOP " << imgGen_.getFrameNr() << " frames displayed." << std::endl; // detach binding with OpenGL context eglMakeCurrent (display_, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); diff --git a/FrOSCon26/examples/gtk-uring-app.hpp b/FrOSCon26/examples/gtk-uring-app.hpp index 9ba69a4..b98697d 100644 --- a/FrOSCon26/examples/gtk-uring-app.hpp +++ b/FrOSCon26/examples/gtk-uring-app.hpp @@ -86,7 +86,7 @@ class GtkUringApp run() { demoWindow_.button_.signal_clicked().connect( - sigc::mem_fun (*this, GtkUringApp::triggerProcessing)); + sigc::mem_fun (*this, &GtkUringApp::triggerProcessing)); return Gtk::Application::run (demoWindow_); } // blocks while application is active