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(); }