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
This commit is contained in:
Fischlurch 2026-07-15 23:53:23 +02:00
parent 2a61def8a4
commit ae74bd323e
3 changed files with 219 additions and 12 deletions

View file

@ -897,7 +897,9 @@
<node COLOR="#338800" CREATED="1783644343706" ID="ID_481307677" MODIFIED="1784052427730" TEXT="ein GTK-Rahmen (wie letztes Jahr)">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1783644357376" ID="ID_1687423764" MODIFIED="1784052436428" TEXT="ein Proactor-Thread"/>
<node COLOR="#338800" CREATED="1783644357376" ID="ID_1687423764" MODIFIED="1784142889926" TEXT="ein Proactor-Thread">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1783644382004" ID="ID_222096554" MODIFIED="1783644392847" TEXT="Handler">
<node CREATED="1783644394013" ID="ID_599467746" MODIFIED="1783644398783" TEXT="Completion-Handler"/>
<node CREATED="1783644399671" ID="ID_1684157516" MODIFIED="1783644406037" TEXT="Submission-Handler"/>
@ -1012,8 +1014,8 @@
</node>
<node CREATED="1783902821732" ID="ID_1465201307" MODIFIED="1783902838122" TEXT="getriggert w&#xfc;rde dann der Code, der die OpenGL-Pipeline mit neuen Daten versorgt"/>
<node CREATED="1783902838536" ID="ID_1259653595" MODIFIED="1783902845946" TEXT="dieser w&#xfc;rde mithin im GTK-Thread laufen"/>
<node CREATED="1783902847135" ID="ID_1990712475" MODIFIED="1783902883778" TEXT="Hinweis auf die VSync-Problematik">
<arrowlink COLOR="#347acc" DESTINATION="ID_1393441883" ENDARROW="Default" ENDINCLINATION="154;0;" ID="Arrow_ID_707364611" STARTARROW="None" STARTINCLINATION="140;12;"/>
<node CREATED="1783902847135" ID="ID_1990712475" MODIFIED="1784140538042" TEXT="Hinweis auf die VSync-Problematik">
<arrowlink COLOR="#347acc" DESTINATION="ID_1393441883" ENDARROW="Default" ENDINCLINATION="288;0;" ID="Arrow_ID_707364611" STARTARROW="None" STARTINCLINATION="140;12;"/>
</node>
<node BACKGROUND_COLOR="#efdeb8" CREATED="1783956967738" ID="ID_1170137190" MODIFIED="1783957012593" TEXT="Wichtige Einsicht: warum nicht jeder Desktop-Frame per Buffer-Flip bedient werden mu&#xdf;">
<icon BUILTIN="idea"/>
@ -1406,7 +1408,7 @@
</node>
</node>
</node>
<node BACKGROUND_COLOR="#accdc3" COLOR="#3f4015" CREATED="1783957483612" ID="ID_918051178" MODIFIED="1784136111534" STYLE="fork" TEXT="a Zuckerl: Chrono-to-Kernel-Timespec">
<node BACKGROUND_COLOR="#accdc3" COLOR="#3f4015" CREATED="1783957483612" ID="ID_918051178" MODIFIED="1784151801322" STYLE="fork" TEXT="a Zuckerl: Chrono-to-Kernel-Timespec">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
@ -1464,6 +1466,7 @@
</body>
</html></richcontent>
<edge COLOR="#808080" STYLE="bezier" WIDTH="thin"/>
<linktarget COLOR="#5b8796" DESTINATION="ID_918051178" ENDARROW="Default" ENDINCLINATION="-181;1174;" ID="Arrow_ID_1798992367" SOURCE="ID_546021072" STARTARROW="None" STARTINCLINATION="-274;12;"/>
<icon BUILTIN="wizard"/>
<icon BUILTIN="edit"/>
</node>
@ -1714,8 +1717,8 @@
</node>
</node>
<node CREATED="1783902798597" ID="ID_599725786" MODIFIED="1783902800007" TEXT="eglSwapInterval">
<node CREATED="1783902800007" ID="ID_1393441883" MODIFIED="1783902877712" TEXT="damit kann man wohl das Verhl&#xe4;tnis zu VSync steuern">
<linktarget COLOR="#347acc" DESTINATION="ID_1393441883" ENDARROW="Default" ENDINCLINATION="154;0;" ID="Arrow_ID_707364611" SOURCE="ID_1990712475" STARTARROW="None" STARTINCLINATION="140;12;"/>
<node CREATED="1783902800007" ID="ID_1393441883" MODIFIED="1784140538042" TEXT="damit kann man wohl das Verhl&#xe4;tnis zu VSync steuern">
<linktarget COLOR="#347acc" DESTINATION="ID_1393441883" ENDARROW="Default" ENDINCLINATION="288;0;" ID="Arrow_ID_707364611" SOURCE="ID_1990712475" STARTARROW="None" STARTINCLINATION="140;12;"/>
</node>
</node>
<node CREATED="1783904509744" ID="ID_1126395932" MODIFIED="1783904513706" TEXT="X11 / Wayland">
@ -2096,7 +2099,8 @@
<icon BUILTIN="yes"/>
</node>
</node>
<node CREATED="1784083148598" ID="ID_504827142" MODIFIED="1784083164848" TEXT="Problem: Anordnung der Daten-Definition im Code">
<node BACKGROUND_COLOR="#c8c0b6" COLOR="#435e98" CREATED="1784083148598" ID="ID_504827142" MODIFIED="1784143583720" TEXT="Problem: Anordnung der Daten-Definition im Code">
<icon BUILTIN="messagebox_warning"/>
<node CREATED="1784083166480" ID="ID_1404935910" MODIFIED="1784132116787" TEXT="mu&#xdf; hierzu auch das Enum definieren">
<linktarget COLOR="#b92b54" DESTINATION="ID_1404935910" ENDARROW="Default" ENDINCLINATION="101;8;" ID="Arrow_ID_1619759457" SOURCE="ID_845039951" STARTARROW="None" STARTINCLINATION="53;82;"/>
</node>
@ -2126,7 +2130,8 @@
</node>
</node>
</node>
<node CREATED="1784131432702" ID="ID_1143489272" MODIFIED="1784131440428" TEXT="L&#xf6;sung: Zugriff vom Datentyp trennen">
<node COLOR="#338800" CREATED="1784131432702" ID="ID_1143489272" MODIFIED="1784143579660" TEXT="L&#xf6;sung: Zugriff vom Datentyp trennen">
<icon BUILTIN="button_ok"/>
<node CREATED="1784131442690" ID="ID_656100547" MODIFIED="1784131483235" TEXT="dadurch bleibt das Scoping unbedingt erhalten"/>
<node CREATED="1784131484193" ID="ID_169121010" MODIFIED="1784131533952" TEXT="Zugriff als Funktion darstellen &#x27f9; &#x3bb;">
<linktarget COLOR="#5a94d9" DESTINATION="ID_169121010" ENDARROW="Default" ENDINCLINATION="135;0;" ID="Arrow_ID_84801803" SOURCE="ID_1267317279" STARTARROW="None" STARTINCLINATION="76;6;"/>
@ -2145,11 +2150,90 @@
</richcontent>
<icon BUILTIN="messagebox_warning"/>
</node>
<node CREATED="1784137038242" ID="ID_1199476075" MODIFIED="1784137051164" TEXT="dann doch ein klassisches OO-Callback-Interface"/>
<node BACKGROUND_COLOR="#c8c0b6" COLOR="#435e98" CREATED="1784137038242" ID="ID_1199476075" MODIFIED="1784143571509" TEXT="dann doch ein klassisches OO-Callback-Interface">
<icon BUILTIN="yes"/>
</node>
</node>
</node>
</node>
</node>
<node CREATED="1784143662545" ID="ID_1198363494" MODIFIED="1784143666308" TEXT="Event-Handler">
<node COLOR="#338800" CREATED="1784143667543" ID="ID_1705410666" MODIFIED="1784152302590" TEXT="Setup der Event-Verarbeitung">
<icon BUILTIN="button_ok"/>
<node COLOR="#338800" CREATED="1784143709139" ID="ID_1508199132" MODIFIED="1784151704956" TEXT="File &#xf6;ffnen">
<icon BUILTIN="button_ok"/>
<node CREATED="1784145655032" ID="ID_815825627" MODIFIED="1784145664817" TEXT="macht man das mit C++?"/>
<node CREATED="1784145665437" ID="ID_1276445757" MODIFIED="1784145828191" TEXT="besser direkt mit den POSIX-Funktionen">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
Mehrere Argumente daf&#252;r: man k&#246;nnte die low-level-Flags setzen (wie <code>O_CLOEXEX, O_NOFOLLOW, </code>oder gar <code>O_DIRECT</code>). Das &#214;ffnen ist <i>atomic,</i>&#160; d.h. das File kann nicht mehr gel&#246;scht/verschoben werden beform man das Filehandle hat (was bei einem getrennten STAT und Path-Check durchaus m&#246;glich ist)
</p>
</body>
</html>
</richcontent>
</node>
<node CREATED="1784147238004" ID="ID_973311307" LINK="https://man7.org/linux/man-pages/man2/open.2.html" MODIFIED="1784147251992" TEXT="man: open()"/>
</node>
<node COLOR="#338800" CREATED="1784143714978" ID="ID_127436641" MODIFIED="1784151706946" TEXT="URings anlegen">
<icon BUILTIN="button_ok"/>
</node>
<node COLOR="#338800" CREATED="1784143731216" ID="ID_101354381" MODIFIED="1784151710995" TEXT="Timer aktivieren">
<icon BUILTIN="button_ok"/>
<node CREATED="1784151717872" ID="ID_1035839641" MODIFIED="1784151726466" TEXT="das ist eine spezielle Submission in den URing"/>
<node BACKGROUND_COLOR="#e6dfa8" COLOR="#690f14" CREATED="1784151807158" ID="ID_161068537" MODIFIED="1784152246318" TEXT="&#x27f9; mu&#xdf; URing um +1 Slot gr&#xf6;&#xdf;er dimensionieren">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
Begr&#252;ndung
</p>
<ul>
<li>
da wir stets <i>in bulk</i>&#160;die Submissions hinzuf&#252;gen <i>und erst danach (synchronisiert)</i>&#160;den Output antriggern, k&#246;nnen das maximal so viele Eintr&#228;ge sein, wie wir Buffer haben
</li>
<li>
<i>zus&#228;tzlich</i>&#160;wird ein Eintrag ben&#246;tigt, um am Anfang auch den periodischen Timer in Gang zu setzen
</li>
<li>
eben dieser <i>eine zus&#228;tzliche Eintrag</i>&#160;gen&#252;gt auch, um jeweils noch <i>ein</i>&#160;Timer-Event zu empfangen
</li>
<li>
weitere Completions k&#246;nnen nicht kommen, da wir nicht mehr Submissions abgeschickt haben
</li>
<li>
weitere Timer-Events k&#246;nnten kommen, falls wir zum Entnehmen und &#220;bergeben mehr als einen Timer-tick brauchen (unwahrscheinlich, aber nicht auszuschlie&#223;en). In einem solchen Fall w&#252;rden schlimmstenfalls hinten weitere Timer-Events &#8222;runterfallen&#8220; (wobei aktuelle Kernel ein internes Backlog haben). Sollten wir dergestalt Timer-Events verpassen, sind sie aber auch schon &#252;berholt, und das n&#228;chste regul&#228;re Event wird uns wieder weiterschieben
</li>
</ul>
</body>
</html>
</richcontent>
<icon BUILTIN="messagebox_warning"/>
</node>
<node CREATED="1784151734786" ID="ID_546021072" MODIFIED="1784151801321" TEXT="verwende hier meine Hilfsfunktion f&#xfc;r Chrono &#x27fc; Kernel-Timespec">
<arrowlink COLOR="#5b8796" DESTINATION="ID_918051178" ENDARROW="Default" ENDINCLINATION="-181;1174;" ID="Arrow_ID_1798992367" STARTARROW="None" STARTINCLINATION="-274;12;"/>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1784143738263" ID="ID_1385880788" MODIFIED="1784152297376" TEXT="Handler-Loop">
<icon BUILTIN="pencil"/>
<node CREATED="1784143781367" ID="ID_1463866164" MODIFIED="1784143789996" TEXT="gelesene Daten entgegennehmen"/>
<node COLOR="#338800" CREATED="1784143790590" ID="ID_1916682510" MODIFIED="1784152292235" TEXT="neue Leseauftr&#xe4;ge absetzen">
<icon BUILTIN="button_ok"/>
<node CREATED="1784152255150" ID="ID_1732543149" MODIFIED="1784152264977" TEXT="lagere das in eine Hilfsfunktion aus"/>
<node CREATED="1784152265502" ID="ID_861900858" MODIFIED="1784152274216" TEXT="diese orientiert sich nur an den freien Slots"/>
<node CREATED="1784152276235" ID="ID_812423922" MODIFIED="1784152289135" TEXT="&#x27f9; Backpressure und anfangs alle Slots">
<icon BUILTIN="idea"/>
</node>
</node>
<node CREATED="1784143798563" ID="ID_532286724" MODIFIED="1784143809876" TEXT="Time-Tick : Frame weiterschalten"/>
</node>
</node>
<node CREATED="1783644430659" ID="ID_269686877" MODIFIED="1783644452555" TEXT="Aufrabe: Vorlauf steuern">
<node CREATED="1783644529049" ID="ID_486151338" MODIFIED="1783644875737" TEXT="zwei Phasen (Chase/Lock)">
<richcontent TYPE="NOTE"><html>
@ -2171,6 +2255,51 @@
</body>
</html></richcontent>
</node>
<node CREATED="1784142959996" ID="ID_1894186700" MODIFIED="1784142979142" TEXT="Ansatz: realisiere das durch &#xbb;Backpressure&#xab;">
<icon BUILTIN="idea"/>
<node CREATED="1784142980731" ID="ID_1146524396" MODIFIED="1784142995774">
<richcontent TYPE="NODE"><html>
<head>
</head>
<body>
<p>
also <i>keine explizite</i>&#160;Steuerung
</p>
</body>
</html>
</richcontent>
</node>
<node CREATED="1784143310222" ID="ID_1598149960" MODIFIED="1784143340787" TEXT="vielmehr sorgt die feste zyklische Buffer-Struktur f&#xfc;r die Limitierung"/>
<node CREATED="1784143015199" ID="ID_218469149" MODIFIED="1784143039421" TEXT="zwei Phasen schreiten jeweils so schnell voran wie derzeig m&#xf6;glich">
<node CREATED="1784143040300" ID="ID_227838217" MODIFIED="1784143254426" TEXT="Phase-1 : read-Submissions beauftragen">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
ist jeweils nur m&#246;glich wenn der daf&#252;r vorgesehene Buffer bereits frei geworden ist
</p>
</body>
</html>
</richcontent>
</node>
<node CREATED="1784143055979" ID="ID_1727640820" MODIFIED="1784143309405" TEXT="Phase-2 : eingelesene Daten &#xfc;bergeben">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
Ist nur m&#246;glich wenn diese Daten f&#252;r den erwartungsgem&#228;&#223; n&#228;chsten Frame bereits da sind, und der unmittelbar vorausgegangene Frame schon komplett vom Video-Displayer &#252;bernommen wurde
</p>
</body>
</html>
</richcontent>
</node>
</node>
</node>
</node>
</node>
</node>

View file

@ -15,8 +15,11 @@
#include <cstdint>
#include <iostream>
#include <chrono>
#include <string>
#include <linux/time_types.h> // 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<class REP, class SCALE>
constexpr struct __kernel_timespec
asKernelTimespec (std::chrono::duration<REP, SCALE> dur)
{
using namespace std::chrono;
auto secs = duration_cast<seconds>(dur);
auto nsecs = duration_cast<nanoseconds>(dur - secs);
return __kernel_timespec{ static_cast<long long> (secs.count())
, static_cast<long long> (nsecs.count())
};
}
#endif /*COMMONS_H*/

View file

@ -19,14 +19,24 @@
#include <thread>
#include <atomic>
#include <fcntl.h>
#include <liburing.h>
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<Buffer[]> (RING_SIZ);
buffState_ = make_unique<AtomicBuffState[]> (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, &timespec, /*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_;
}
}