From ae74bd323ed848e2d1409b668be98680ac77cf25 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 15 Jul 2026 23:53:23 +0200 Subject: [PATCH] Prototype: establish buffer data structure and initialise URing - setup a IORING_OP_TIMEOUT to drive the frame loop - use the status mark of buffers ahead to supply new IORING_OP_READ - rely on two running framecount cursors with modulus to map onto buffer slots --- FrOSCon26/FrOSCon.26.mm | 147 ++++++++++++++++++++++++++++-- FrOSCon26/examples/commons.hpp | 19 ++++ FrOSCon26/examples/demo-uring.cpp | 65 ++++++++++++- 3 files changed, 219 insertions(+), 12 deletions(-) diff --git a/FrOSCon26/FrOSCon.26.mm b/FrOSCon26/FrOSCon.26.mm index 8859470..fd48f7c 100644 --- a/FrOSCon26/FrOSCon.26.mm +++ b/FrOSCon26/FrOSCon.26.mm @@ -897,7 +897,9 @@ - + + + @@ -1012,8 +1014,8 @@ - - + + @@ -1406,7 +1408,7 @@ - + @@ -1464,6 +1466,7 @@ + @@ -1714,8 +1717,8 @@ - - + + @@ -2096,7 +2099,8 @@ - + + @@ -2126,7 +2130,8 @@ - + + @@ -2145,11 +2150,90 @@ - + + + + + + + + + + + + + + + +

+ Mehrere Argumente dafür: man könnte die low-level-Flags setzen (wie O_CLOEXEX, O_NOFOLLOW, oder gar O_DIRECT). Das Öffnen ist atomic,  d.h. das File kann nicht mehr gelöscht/verschoben werden beform man das Filehandle hat (was bei einem getrennten STAT und Path-Check durchaus möglich ist) +

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

+ Begründung +

+
    +
  • + da wir stets in bulk die Submissions hinzufügen und erst danach (synchronisiert) den Output antriggern, können das maximal so viele Einträge sein, wie wir Buffer haben +
  • +
  • + zusätzlich wird ein Eintrag benötigt, um am Anfang auch den periodischen Timer in Gang zu setzen +
  • +
  • + eben dieser eine zusätzliche Eintrag genügt auch, um jeweils noch ein Timer-Event zu empfangen +
  • +
  • + weitere Completions können nicht kommen, da wir nicht mehr Submissions abgeschickt haben +
  • +
  • + weitere Timer-Events könnten kommen, falls wir zum Entnehmen und Übergeben mehr als einen Timer-tick brauchen (unwahrscheinlich, aber nicht auszuschließen). In einem solchen Fall würden schlimmstenfalls hinten weitere Timer-Events „runterfallen“ (wobei aktuelle Kernel ein internes Backlog haben). Sollten wir dergestalt Timer-Events verpassen, sind sie aber auch schon überholt, und das nächste reguläre Event wird uns wieder weiterschieben +
  • +
+ + +
+ +
+ + + +
+
+ + + + + + + + + + + + + + @@ -2171,6 +2255,51 @@ + + + + + + + + +

+ also keine explizite Steuerung +

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

+ ist jeweils nur möglich wenn der dafür vorgesehene Buffer bereits frei geworden ist +

+ + +
+
+ + + + + + +

+ Ist nur möglich wenn diese Daten für den erwartungsgemäß nächsten Frame bereits da sind, und der unmittelbar vorausgegangene Frame schon komplett vom Video-Displayer übernommen wurde +

+ + +
+
+
+
diff --git a/FrOSCon26/examples/commons.hpp b/FrOSCon26/examples/commons.hpp index 6ee5c06..d37c81a 100644 --- a/FrOSCon26/examples/commons.hpp +++ b/FrOSCon26/examples/commons.hpp @@ -15,8 +15,11 @@ #include #include +#include #include +#include // for struct __kernel_timespec + using std::string; using uint = unsigned int; using FrameRate = uint; @@ -42,4 +45,20 @@ class NonCopyable }; + +/** + * Generate a kernel timespec from a given C++ chrono duration + */ +template +constexpr struct __kernel_timespec +asKernelTimespec (std::chrono::duration dur) +{ + using namespace std::chrono; + auto secs = duration_cast(dur); + auto nsecs = duration_cast(dur - secs); + return __kernel_timespec{ static_cast (secs.count()) + , static_cast (nsecs.count()) + }; +} + #endif /*COMMONS_H*/ diff --git a/FrOSCon26/examples/demo-uring.cpp b/FrOSCon26/examples/demo-uring.cpp index 075b2fa..e9daa77 100644 --- a/FrOSCon26/examples/demo-uring.cpp +++ b/FrOSCon26/examples/demo-uring.cpp @@ -19,14 +19,24 @@ #include #include +#include +#include + using std::unique_ptr; using std::make_unique; +using std::chrono::operator ""ms; + //-----------------hard-coded-config-- const size_t RING_SIZ = 5; - +const string RAW_VIDEO_INPUT_FILE = "demovideo.raw"; const uint VIDEO_WIDTH = 320; const uint VIDEO_HEIGHT = 240; +const size_t START_FRAME = 0; +const size_t MAX_LOOP_FRAME = 30 * 25; + +auto calcSrcFrame = [](size_t runningNr){ return START_FRAME + runningNr % MAX_LOOP_FRAME; }; + //-----------------hard-coded-config-- @@ -81,6 +91,7 @@ class UringVideoPlayer private: void playVideo(); + void addNextReadSubmissions (io_uring&, int inputFD); }; @@ -94,10 +105,58 @@ class UringVideoPlayer void UringVideoPlayer::playVideo() { + /* ===== initial setup ===== */ + int inputFD = open (RAW_VIDEO_INPUT_FILE.c_str(), O_RDONLY); + if (inputFD < 0) + __FAIL ("open file \""+RAW_VIDEO_INPUT_FILE+"\""); + + // Allocate several data buffers (used as ring) buffer_ = make_unique (RING_SIZ); buffState_ = make_unique (RING_SIZ); - // initialise URing - // ++ proactor-loop ++ + + // initialise uRing Kernel interface + io_uring uRing; + io_uring_queue_init (RING_SIZ+1, &uRing, /*flags*/ 0 ); // Note: one additional »slot« for the timer event(s) + + // Setup periodic frame beat, firing infinitely + const auto TICK_TAG = UINT64_MAX; // special marker for timer events (different from possible frame numbers) + auto timespec = asKernelTimespec (40ms); // 40ms ≈ 25fps + io_uring_sqe* timerSubmission = io_uring_get_sqe (&uRing); + io_uring_prep_timeout (timerSubmission, ×pec, /*no count limit*/ 0, IORING_TIMEOUT_MULTISHOT); + io_uring_sqe_set_data64(timerSubmission, TICK_TAG); + + addNextReadSubmissions (uRing, inputFD); + io_uring_submit (&uRing); + + /* ===== proactor-loop ===== */ + + close (inputFD); +} + + +/** + * Instruct the next read operations, + * using all free buffer slots ahead. + */ +void +UringVideoPlayer::addNextReadSubmissions (io_uring& uRing, int inputFD) +{ + assert (0 < inputFD); + const size_t BUFFSIZ = sizeof(Buffer); + + while (buffState_[nextRead_ % RING_SIZ] == FREE) + { + size_t srcOffset = calcSrcFrame (nextRead_); + io_uring_sqe* readSubmission = io_uring_get_sqe (&uRing); + io_uring_prep_read (readSubmission + ,inputFD + ,& buffer_[nextRead_ % RING_SIZ] + ,BUFFSIZ + ,BUFFSIZ * srcOffset + ); + io_uring_sqe_set_data64 (readSubmission, nextRead_); + ++nextRead_; + } }