From 2a61def8a452b68fdbf8bbdb688f685f1a3d73af Mon Sep 17 00:00:00 2001
From: Ichthyostega
Date: Wed, 15 Jul 2026 20:40:59 +0200
Subject: [PATCH] Prototype: need to introduce an interface for the frame data
feed
Since I want to segregate the code according to the domain,
several methods must be provided to the video display to conduct
a properly synchronised handover of video pixel data; by using
`Glib::Dispatcher` as base class, all aspects of feed synchronisation
can be packaged into a single connection interface, without having
to share implementation details between the video display and player.
---
FrOSCon26/FrOSCon.26.mm | 66 +++++++++++++++---
FrOSCon26/examples/commons.hpp | 12 ++++
FrOSCon26/examples/demo-uring.cpp | 107 +++++++++++++++++++++--------
FrOSCon26/examples/egl-display.hpp | 36 +++++++---
4 files changed, 174 insertions(+), 47 deletions(-)
diff --git a/FrOSCon26/FrOSCon.26.mm b/FrOSCon26/FrOSCon.26.mm
index 9ceb7a7..8859470 100644
--- a/FrOSCon26/FrOSCon.26.mm
+++ b/FrOSCon26/FrOSCon.26.mm
@@ -1057,7 +1057,7 @@
-
+
@@ -1083,6 +1083,7 @@
+
@@ -1249,7 +1250,7 @@
-
+
@@ -1312,8 +1313,11 @@
+
+
+
-
+
@@ -1355,15 +1359,41 @@
+
+
+
-
+
-
+
+
+
- void onTick() { if (slots[nextExpected % N].state == Ready) { frameReady.emit(); // GUI thread will draw slots[nextExpected % N] ++nextExpected; // GUI marks the slot Empty once it's done drawing } // else: do nothing — this frame slips one tick later. Not dropped, just delayed. while (slots[nextToSubmit % N].state == Empty) { submitRead(nextToSubmit); // io_uring_prep_read + set_data64(nextToSubmit) ++nextToSubmit; } io_uring_submit(&ring); }
+ void onTick()
+ {
+ if (slots[nextExpected % N].state == Ready)
+ {
+ frameReady.emit(); // GUI thread will draw slots[nextExpected % N]
+ ++nextExpected; // GUI marks the slot Empty once it's done drawing
+ }
+ // else: do nothing — this frame slips one tick later. Not dropped, just delayed.
+
+ while (slots[nextToSubmit % N].state == Empty)
+ {
+ submitRead(nextToSubmit); // io_uring_prep_read + set_data64(nextToSubmit)
+ ++nextToSubmit;
+ }
+ io_uring_submit(&ring);
+}
+
+
-
-
+
+
+
+
+
+
@@ -1376,7 +1406,7 @@
-
+
@@ -1435,6 +1465,7 @@
+
@@ -2056,7 +2087,7 @@
-
+
@@ -2100,6 +2131,21 @@
+
+
+
+
+
+
+
+ Eine Operation zum Zugriff und eine Operation um den abgeschlossenen Zugriff zu markieren; zudem gibt es auch noch den Dispatcher.
+
+
+
+
+
+
+
diff --git a/FrOSCon26/examples/commons.hpp b/FrOSCon26/examples/commons.hpp
index be15fc1..6ee5c06 100644
--- a/FrOSCon26/examples/commons.hpp
+++ b/FrOSCon26/examples/commons.hpp
@@ -29,5 +29,17 @@ __FAIL (string msg)
std::abort();
}
+/**
+ * Marker class: any copy and copy construction prohibited
+ */
+class NonCopyable
+ {
+ protected:
+ ~NonCopyable() = default;
+ NonCopyable() = default;
+ NonCopyable (NonCopyable const&) = delete;
+ NonCopyable& operator= (NonCopyable const&) = delete;
+ };
+
#endif /*COMMONS_H*/
diff --git a/FrOSCon26/examples/demo-uring.cpp b/FrOSCon26/examples/demo-uring.cpp
index a7bac4f..075b2fa 100644
--- a/FrOSCon26/examples/demo-uring.cpp
+++ b/FrOSCon26/examples/demo-uring.cpp
@@ -17,8 +17,10 @@
#include
#include
#include
+#include
using std::unique_ptr;
+using std::make_unique;
//-----------------hard-coded-config--
const size_t RING_SIZ = 5;
@@ -29,56 +31,105 @@ const uint VIDEO_HEIGHT = 240;
class UringVideoPlayer
+ : NonCopyable
{
using VideoDisplay = EglDisplay;
using Buffer = VideoDisplay::Buffer;
- Glib::Dispatcher dispatchFrame_;
+ enum BuffState { FREE, READ, SHOW };
+ using AtomicBuffState = std::atomic;
+
+ using BufferAlloc = unique_ptr;
+ using BuffStateAlloc = unique_ptr;
+
+ /**
+ * Connector to the VideoDisplay for dispatching video data.
+ * @note inherits from Glib::Dispatcher -> signal frame readiness
+ * @remark the callback functions allow the VideoDisplay to interact
+ * with the buffer data and state in a threadsafe manner
+ */
+ class BufferDispatcher
+ : public VideoDisplay::PixelFeed
+ {
+ Buffer* retrieveFrame() override;
+ void completedFrame() override;
+
+ UringVideoPlayer& player_;
+ public:
+ BufferDispatcher (UringVideoPlayer& player)
+ : player_{player}
+ { }
+ };
+
+ size_t nextRead_{0};
+ size_t nextShow_{0};
+ BufferAlloc buffer_{};
+ BuffStateAlloc buffState_{};
+ std::atomic_bool playing_{false};
+ BufferDispatcher dispatchFrame_;
VideoDisplay videoDisplayer_;
std::thread proactorThread_;
public:
UringVideoPlayer (Gtk::Window& appWindow)
- : videoDisplayer_{appWindow, dispatchFrame_, [this]{ return retrieveCurrFrame(); }}
+ : dispatchFrame_{*this}
+ , videoDisplayer_{appWindow, dispatchFrame_}
, 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
- }
+ void playVideo();
};
+/**
+ * Logic of the »Proactor Thread«:
+ * - maintain the working buffers
+ * - open the raw video file to retrieve data
+ * - establish an IO_URING connection for reading
+ * - periodic processing to fill and hand-off buffers
+ */
+void
+UringVideoPlayer::playVideo()
+{
+ buffer_ = make_unique (RING_SIZ);
+ buffState_ = make_unique (RING_SIZ);
+ // initialise URing
+ // ++ proactor-loop ++
+}
+
+
+UringVideoPlayer::Buffer*
+UringVideoPlayer::BufferDispatcher::retrieveFrame()
+{
+ ///////OOO threadsafe hand-over of current frame data
+}
+
+
+void
+UringVideoPlayer::BufferDispatcher::completedFrame()
+{
+ ///////OOO mark end of buffer access
+}
+
+
+void
+UringVideoPlayer::terminate()
+{
+ playing_.store (false, std::memory_order_release);
+ proactorThread_.join();
+ videoDisplayer_.cleanUp();
+}
+
+
+
unique_ptr
launchPlayer (Gtk::Window& appWindow)
{
appWindow.resize (VIDEO_WIDTH, VIDEO_HEIGHT);
-
- auto player = std::make_unique (appWindow);
-
- return player;
-}
-
-
-
-void
-UringVideoPlayer::terminate()
-{
- ///// signal stop to proactor
- videoDisplayer_.cleanUp();
+ return std::make_unique (appWindow);
}
diff --git a/FrOSCon26/examples/egl-display.hpp b/FrOSCon26/examples/egl-display.hpp
index d853bff..a64a151 100644
--- a/FrOSCon26/examples/egl-display.hpp
+++ b/FrOSCon26/examples/egl-display.hpp
@@ -38,20 +38,35 @@ using std::string;
*/
template
class EglDisplay
+ : NonCopyable
{
public:
using Buffer = std::array;
- using PixFeed = std::function;
- EglDisplay(Gtk::Window& appWindow
- ,Glib::Dispatcher& dispatchFrame
- ,PixFeed frameCallback
- )
- : retrieveFrame_{frameCallback}
+ /**
+ * 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}
{
openDisplay (appWindow);
- dispatchFrame.connect([this]{ displayFrame(); });
+ pixelFeed_.connect([this]{ displayFrame(); });
}
void cleanUp();
@@ -71,7 +86,7 @@ class EglDisplay
float scaleY_{1};
/** Callback functor to access current pixel data */
- PixFeed retrieveFrame_{};
+ PixelFeed& pixelFeed_{};
void openDisplay (Gtk::Window&);
void displayFrame();
@@ -321,7 +336,7 @@ void
EglDisplay::displayFrame()
{
- auto currFrame = retrieveFrame_();
+ auto currFrame = pixelFeed_.retrieveFrame();
if (not currFrame) return;
// pick up current buffer with RGB data
@@ -340,6 +355,9 @@ EglDisplay::displayFrame()
// double-buffer flip, automatically invokes glFlush()
eglSwapBuffers (display_, surface_);
+
+ // signal that processing of this frame is complete
+ pixelFeed_.completedFrame();
}