Prototype: integrate EglDisplay as self-contained component

This commit is contained in:
Fischlurch 2026-07-14 20:16:18 +02:00
parent b92de63d25
commit 6bf8a0da7a
4 changed files with 151 additions and 200 deletions

View file

@ -776,16 +776,13 @@
<node CREATED="1783820461975" ID="ID_715616295" MODIFIED="1783820480357" TEXT="mit optimiser also praktisch direkter Kernel-Aufruf"/>
<node CREATED="1783820481050" ID="ID_1869508632" MODIFIED="1783820542141" TEXT="liburing-ffi: definiert alle calls als binary-Symbols (&#x27f9; langsamer)">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
Ist notwendig f&#252;r andere Sprach-Bindings oder &#228;ltere Compiler; dadurch hat dann nat&#252;rlich jeder Call einen Overhead
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
</node>
</node>
@ -806,7 +803,8 @@
<node CREATED="1783642938966" ID="ID_1117409154" MODIFIED="1783642955536" TEXT="m&#xe4;&#xdf;ig sinnvoll"/>
<node CREATED="1783642956739" ID="ID_141690825" MODIFIED="1783642968326" TEXT="hier nur ein Consumer"/>
</node>
<node CREATED="1783642985618" ID="ID_169416260" MODIFIED="1783642994611" TEXT="oder einen pull-Worker">
<node COLOR="#5b280f" CREATED="1783642985618" ID="ID_169416260" MODIFIED="1784052474215" TEXT="oder einen pull-Worker">
<icon BUILTIN="button_cancel"/>
<node CREATED="1783642995707" ID="ID_216666777" MODIFIED="1783642999794" TEXT="das w&#xe4;re n&#xe4;her an Lumiera"/>
<node CREATED="1783643000727" ID="ID_737342556" MODIFIED="1783643655114" TEXT="der w&#xfc;rde jeweils nur die Queue ausr&#xe4;umen"/>
<node CREATED="1783643039177" ID="ID_1046849410" MODIFIED="1783643729328" TEXT="und eine Flag am Buffer setzen">
@ -840,6 +838,14 @@
</html></richcontent>
</node>
</node>
<node CREATED="1784052477036" ID="ID_1273958721" MODIFIED="1784052501398" TEXT="nach Diskussion mit Claude: Proactor ist didaktisch vorzuziehen">
<icon BUILTIN="yes"/>
<node BACKGROUND_COLOR="#fbfdca" CREATED="1784052519992" ID="ID_1545269972" MODIFIED="1784052885403" TEXT="man kann auch die Timings dar&#xfc;ber steuern">
<arrowlink COLOR="#f4f3a4" DESTINATION="ID_1489274093" ENDARROW="Default" ENDINCLINATION="638;-170;" ID="Arrow_ID_396926940" STARTARROW="None" STARTINCLINATION="1155;67;"/>
<icon BUILTIN="idea"/>
</node>
<node CREATED="1784052896300" ID="ID_746832214" MODIFIED="1784052909501" TEXT="kann auf die Weise alle zentralen Konzepte in einem St&#xfc;ck Code zeigen"/>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1783644313430" ID="ID_170028474" MODIFIED="1783788829437" TEXT="Prototyp entwickeln">
@ -878,21 +884,20 @@
<node CREATED="1783795475774" ID="ID_1250076625" MODIFIED="1783795489958" TEXT="als &quot;external builder Make&quot;-Projekt importieren"/>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1783795490756" ID="ID_972729826" MODIFIED="1783795620602" TEXT="Vorsicht: brauche keinen C-Build">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
Hatte immer die Fehlermeldung &quot;build misconfigured&quot; (o.&#228;). Hab lange rumgesucht, denn alle Settings waren effektiv gleich wie beim &#187;VideoOutput&#171;-Projekt. Bis ich dann im .project-File gesehen hab, da&#223; auch noch ein C-Builder konfiguriert worden war (m&#246;glichweise ein Toolchain-Default).
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<icon BUILTIN="messagebox_warning"/>
</node>
</node>
<node CREATED="1783644343706" ID="ID_481307677" MODIFIED="1783644354828" TEXT="ein GTK-Rahmen (wie letztes Jahr)"/>
<node CREATED="1783644357376" ID="ID_1687423764" MODIFIED="1783644363091" TEXT="ein Worker-Thread"/>
<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 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"/>
@ -933,25 +938,20 @@
<node CREATED="1783901711226" ID="ID_1930870094" MODIFIED="1783901723228" TEXT="Claude schl&#xe4;gt vor, &#xfc;berhaupt nicht zu &#xbb;synchronisieren&#xab;"/>
<node CREATED="1783901727663" ID="ID_1596599158" MODIFIED="1783901972168" TEXT="Argument: da gibts nichts zu synchronisieren, denn die Sync geht nicht auf">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
Der Desktop l&#228;uft mit z.B. 60fps, das video mit 25fps. Man sollte akzeptieren (und im Talk klar machen) da&#223; das niemals aufgeht. Vielmehr sollte man regelm&#228;&#223;ig die fertig geladenen Frames &#252;bergeben, wenn sie eben da sind. F&#252;r den Talk schl&#228;gt Claude vor, das Thema &quot;tearing&quot; mehr oder weniger zu ignorieren (hatte ich nicht gesagt, sehe ich aber genauso). Sp&#228;ter, f&#252;r die Prod-Version sei das ein nicht-trivialer Aspekt
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1783901847104" ID="ID_1936627376" MODIFIED="1783901856567" TEXT="Guter Punkt, da h&#xe4;tte ich mich m&#xf6;glicherweise verrant!">
<icon BUILTIN="idea"/>
</node>
</node>
<node CREATED="1783901974887" ID="ID_1974536015" MODIFIED="1783902062028" TEXT="Claude geht davon aus, da&#xdf; die OpenGL-Pipeline im GUI-Thread laufen sollte">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
...das sei zwar nicht notwendig (korrekt!), aber vermeide Komplexit&#228;ten. Ein OpenGL-Kontext ist an einen Thread gebunden, also m&#252;&#223;te man nach jedem Thread-Wechsel umbinden.
@ -960,8 +960,7 @@
&#10233; sehr gut, damit hat sich eine Frage von selber gekl&#228;rt, die ich noch stellen wollte
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
<node CREATED="1783902186522" ID="ID_80755144" MODIFIED="1783902207310" TEXT="wegen dem Timing weist Claude auf die M&#xf6;glichkeit hin, das &#xfc;ber io_uring selber zu machen">
<icon BUILTIN="back"/>
@ -1021,9 +1020,7 @@
<node CREATED="1783957017373" ID="ID_1419210108" MODIFIED="1783957033385" TEXT="wenn wir neue daten haben dann triggern wir die Anzige-Pipeline"/>
<node BACKGROUND_COLOR="#accdc3" COLOR="#3f4015" CREATED="1783957040816" ID="ID_468719143" MODIFIED="1783957188965" STYLE="fork" TEXT="das funktioniert weil darunter ein Compositor sitzt">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<!--
body { font-family: SansSerif; font-size: 12pt }
@ -1043,8 +1040,7 @@
<strong http-equiv="content-type" content="text/html; charset=utf-8">Your aside &#8212; yes, and here's the actual mechanism:</strong>&#160;with swap interval 0 and swaps only triggered by <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">frameReady.emit()</code>, you are <em>not</em>&#160;relying on OpenGL to &quot;resend&quot; anything between your swaps &#8212; OpenGL/EGL has no ongoing activity at all in between. What actually keeps the picture on screen is the desktop compositor: once <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">eglSwapBuffers()</code>&#160;makes a buffer the current front buffer, that's simply what's there until you swap again, and the compositor samples it at its own repaint cadence (its vsync, typically 60Hz+) regardless of whether your app does anything. So your assumption holds, but the credit belongs to &quot;a swapped buffer is persistent state the compositor keeps re-showing,&quot; not to any active redisplay behavior on your side &#8212; a good one-sentence clarification to have ready if someone in the audience asks &quot;wait, doesn't something have to keep pushing frames?&quot;
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<edge COLOR="#808080" STYLE="bezier" WIDTH="thin"/>
<icon BUILTIN="wizard"/>
</node>
@ -1093,32 +1089,42 @@
<node CREATED="1783952838505" ID="ID_1257919936" MODIFIED="1783952858029" TEXT="Worker Thread and State organisation">
<node BACKGROUND_COLOR="#accdc3" COLOR="#3f4015" CREATED="1783952862448" ID="ID_842498915" MODIFIED="1783954578059" STYLE="fork">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
ich habe das Stichwort &quot;<b>Proactor</b>&quot; gebraucht. Claude best&#228;tigt das
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
das hei&#223;t: die L&#246;sung, auf die es nun f&#252;r den Talk hinausl&#228;uft, ist da&#223; ich nicht wirklich einen &quot;Worker&quot; hab (kein Vorgriff auf die Lumiera Engine), sondern einen Proactor.
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<edge COLOR="#808080" STYLE="bezier" WIDTH="thin"/>
<icon BUILTIN="wizard"/>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1783954584980" ID="ID_532077726" MODIFIED="1783954609626" TEXT="&#x27f9; hei&#xdf;t: blocken auf wait_cqe()">
<icon BUILTIN="messagebox_warning"/>
</node>
<node BACKGROUND_COLOR="#fefc4e" COLOR="#351d75" CREATED="1784052687896" ID="ID_1489274093" MODIFIED="1784052865429" TEXT="interessanter Ansatz: man kann einen Kernel-Timer in IO_Uring einbinden">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
Darauf w&#228;re ich nicht gekommen; ich h&#228;tte an meinem von Lumiera abgeleiteten Schema festgehalten....
</p>
<p>
Claude betont, das w&#228;re idiomatisch und w&#252;rde die Proactor-Loop ins Zentrum stellen. Das &#252;berzeugt mich &#10233; werde dann also doch einen Proactor in der Demo zeigen
</p>
</body>
</html></richcontent>
<linktarget COLOR="#f4f3a4" DESTINATION="ID_1489274093" ENDARROW="Default" ENDINCLINATION="638;-170;" ID="Arrow_ID_396926940" SOURCE="ID_1545269972" STARTARROW="None" STARTINCLINATION="1155;67;"/>
<font BOLD="true" NAME="SansSerif" SIZE="12"/>
<icon BUILTIN="idea"/>
</node>
</node>
<node CREATED="1783953153918" ID="ID_1754327495" MODIFIED="1783953520369" TEXT="ich sage: f&#xfc;r async IO braucht man immer eine Art State-Engine">
<node CREATED="1783953183906" ID="ID_339096490" MODIFIED="1783953350863" TEXT="Claude widerspricht nicht">
@ -1185,9 +1191,7 @@
<node CREATED="1783953396149" ID="ID_12931299" MODIFIED="1783953416430" TEXT="ich sage: das bekommt man doch von selbst, wenn man die Buffer in einen Ring organisiert"/>
<node BACKGROUND_COLOR="#accdc3" COLOR="#3f4015" CREATED="1783953941213" ID="ID_713186135" MODIFIED="1783954028109" STYLE="fork" TEXT="Claude greift das auf, betont aber (nochmal) da&#xdf; die CQ-Reihenfolge nicht garantiert ist">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p http-equiv="content-type" content="text/html; charset=utf-8" class="font-claude-response-body break-words whitespace-normal">
The ring idea is correct, and it's the right way to lay out storage &#8212; but it solves a different problem than the one we flagged last time, and I think that's the thing worth pulling apart carefully, because the two compose nicely rather than one replacing the other.
@ -1199,8 +1203,7 @@
<strong>What it does <em>not</em>&#160;give you for free:</strong>&#160;the display-order guarantee. The ring tells you <em>where</em>&#160;frame K's bytes live; it says nothing about <em>when</em>&#160;the read for frame K finishes relative to the read for frame K+1. Two SQEs submitted in order are not guaranteed to complete in order &#8212; for sequential reads against one local file on one machine it's very likely in practice, but &quot;likely&quot; is exactly the kind of unverified pragmatism you just said you don't accept. So you still need the mechanism from last time: track which frame is <em>next expected for display</em>, and only <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">emit()</code>&#160;once that specific slot says <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">Ready</code>&#160; &#8212; regardless of what else has completed in the meantime.
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<edge COLOR="#808080" STYLE="bezier" WIDTH="thin"/>
<icon BUILTIN="wizard"/>
</node>
@ -1212,9 +1215,7 @@
</node>
<node BACKGROUND_COLOR="#accdc3" COLOR="#3f4015" CREATED="1783954171957" ID="ID_1248856525" MODIFIED="1783954243633" STYLE="fork" TEXT="sollte einen &quot;nextExpected&quot;-Cursor im Thread-State mitf&#xfc;hren">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<ul>
<li http-equiv="content-type" content="text/html; charset=utf-8" class="font-claude-response-body whitespace-normal break-words pl-2">
@ -1225,8 +1226,7 @@
</li>
</ul>
</body>
</html>
</richcontent>
</html></richcontent>
<edge COLOR="#808080" STYLE="bezier" WIDTH="thin"/>
<icon BUILTIN="wizard"/>
</node>
@ -1377,9 +1377,7 @@
</node>
<node BACKGROUND_COLOR="#accdc3" COLOR="#3f4015" CREATED="1783957483612" ID="ID_918051178" MODIFIED="1783957589661" STYLE="fork" TEXT="a Zuckerl: Chrono-to-Kernel-Timespec">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<!--
body { font-family: SansSerif; font-size: 12pt }
@ -1433,8 +1431,7 @@
</p>
<font color="#800c0c">auto ts = toKernelTimespec(40ms);</font>
</body>
</html>
</richcontent>
</html></richcontent>
<edge COLOR="#808080" STYLE="bezier" WIDTH="thin"/>
<icon BUILTIN="wizard"/>
</node>
@ -1442,16 +1439,13 @@
<icon BUILTIN="back"/>
<node CREATED="1783964913819" ID="ID_1290458999" MODIFIED="1783964940263" TEXT="gebe Claude eine &#xbb;executive summary&#xab; bez&#xfc;glich Lumiera Engine">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
er hat dazu keine weiteren Fragen
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
<node COLOR="#338800" CREATED="1783964942767" ID="ID_993102807" MODIFIED="1783966105865" TEXT="lasse Claude die Diskrepanz herausarbeiten gegen&#xfc;ber dem Proactor-Pattern">
<richcontent TYPE="NOTE"><html>
@ -1520,9 +1514,7 @@
</node>
<node BACKGROUND_COLOR="#f8f1cb" COLOR="#a50125" CREATED="1783966752311" ID="ID_1363514128" MODIFIED="1783966837345" TEXT="whatever you do inside the token-held section must never block! ">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
That's a much bigger problem than in the FrOSCon demo, where blocking on
@ -1530,8 +1522,7 @@
<code http-equiv="content-type" content="text/html; charset=utf-8" class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">wait_cqe</code>&#160;was the worker's <em>entire job</em>. Here it would reintroduce exactly the kind of global stall your pull architecture is designed to avoid. The fix is straightforward but needs to be deliberate: only ever use the non-blocking <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">io_uring_peek_cqe()</code>&#160; inside grooming &#8212; check if anything's ready, harvest what's there, submit what's ready to submit, and move on regardless of whether anything was found. That keeps the ring interaction cheap and bounded, consistent with a critical section that's already supposed to be short.
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<icon BUILTIN="clanbomber"/>
<node CREATED="1783966769728" ID="ID_882108638" MODIFIED="1783966784791" TEXT="Gefahr: wait_cqe() kann die ganze Engine stallen"/>
<node CREATED="1783966793306" ID="ID_1685734854" MODIFIED="1783966801007" TEXT="stets nur io_uring_peek_cqe() verwenden">
@ -1542,9 +1533,7 @@
<icon BUILTIN="messagebox_warning"/>
<node BACKGROUND_COLOR="#accdc3" COLOR="#3f4015" CREATED="1783966894277" ID="ID_231061601" MODIFIED="1783966904895" STYLE="fork" TEXT="">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
CQ overflow: if grooming genuinely doesn't run for a while under heavy CPU load, does a full completion queue drop completions, or buffer them? Recent kernels buffer overflow internally rather than dropping (subject to
@ -1552,30 +1541,24 @@
<code http-equiv="content-type" content="text/html; charset=utf-8" class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">IORING_FEATURE_NODROP</code>-era behavior), but it's worth confirming for whatever kernel baseline you commit to, rather than assuming.
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<edge COLOR="#808080" STYLE="bezier" WIDTH="thin"/>
<icon BUILTIN="wizard"/>
</node>
</node>
<node CREATED="1783966925433" ID="ID_852283957" MODIFIED="1783967012774">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
Deliberately avoid <code><font color="#7c5252">IORING_SETUP_SQPOLL</font></code>
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<icon BUILTIN="clanbomber"/>
<node BACKGROUND_COLOR="#accdc3" COLOR="#3f4015" CREATED="1783967014652" ID="ID_758622095" MODIFIED="1783967890660" STYLE="fork" TEXT="">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
Deliberately avoid
@ -1583,8 +1566,7 @@
<code http-equiv="content-type" content="text/html; charset=utf-8" class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">IORING_SETUP_SQPOLL</code>. It's tempting since it removes the need to call into the kernel for submission most of the time, but it works by adding a kernel-side polling thread that reads the SQ ring on its own schedule &#8212; an actor outside your GroomingToken discipline and outside your capacity self-calibration entirely. That's exactly the kind of uncontrolled independent actor your architecture is built to avoid.
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<edge COLOR="#808080" STYLE="bezier" WIDTH="thin"/>
<linktarget COLOR="#4389dd" DESTINATION="ID_758622095" ENDARROW="Default" ENDINCLINATION="196;89;" ID="Arrow_ID_200171107" SOURCE="ID_156133270" STARTARROW="None" STARTINCLINATION="334;27;"/>
<icon BUILTIN="wizard"/>
@ -1593,9 +1575,7 @@
</node>
<node BACKGROUND_COLOR="#accdc3" COLOR="#3f4015" CREATED="1783967051428" ID="ID_1173652481" MODIFIED="1783967077582" STYLE="fork" TEXT="Claude gibt eine &#xbb;freundliche conclusion&#xab;">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
Net: I think folding I/O into the same single-threaded grooming discipline you already have is the
@ -1603,8 +1583,7 @@
<em http-equiv="content-type" content="text/html; charset=utf-8">consistent</em>&#160;choice, not a compromise &#8212; you've already accepted one global serialization point for scheduling decisions, and a small, non-blocking peek/submit pair is cheap enough to ride along in that same section without becoming a new bottleneck category. The real design work is less &quot;is this safe&quot; (I think it is, modulo the ordering-semantics check and the non-blocking discipline) and more &quot;how do we know, empirically, that grooming-section latency stays small enough once I/O bookkeeping is added&quot; &#8212; which sounds like exactly the kind of thing you'd want numbers for before committing, rather than reasoning about further in the abstract.
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<edge COLOR="#808080" STYLE="bezier" WIDTH="thin"/>
<icon BUILTIN="wizard"/>
<node CREATED="1783967080199" ID="ID_820048763" MODIFIED="1783967099409" TEXT="geschickt schmeicheln wurde gut trainiert">
@ -1640,9 +1619,7 @@
</node>
<node BACKGROUND_COLOR="#accdc3" COLOR="#3f4015" CREATED="1783967604917" ID="ID_1465824912" MODIFIED="1783967742187" STYLE="fork" TEXT="abschlie&#xdf;ene Einordnung">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
Even more directly relevant to your capacity-calibration idea: Glommio ships built-in &quot;controllers&quot; that automatically adjust per-task-queue scheduler shares to control throughput against a user-specified criterion &#8212; that's essentially your self-calibrating capacity-weights idea, independently arrived at by a different team solving an adjacent problem. Worth knowing that's not an idiosyncratic idea of yours &#8212; it's something the field has landed on more than once, which is generally a good sign for an architectural instinct.
@ -1659,22 +1636,18 @@
<strong>One trend worth just keeping an eye on, not acting on:</strong>&#160;core counts keep climbing &#8212; high core-count workstation and consumer chips are increasingly normal &#8212; and any single serialization point gets proportionally more expensive as contention scales with thread count, even if each individual crossing is cheap. Not a problem today, and your task granularity buys you a lot of headroom, but it's the kind of thing worth having empirical data on (exactly the &quot;more empirics needed&quot; conclusion you already reached) if Lumiera is ever deployed on genuinely large core counts rather than typical desktop/workstation counts.
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<edge COLOR="#808080" STYLE="bezier" WIDTH="thin"/>
<icon BUILTIN="wizard"/>
<node CREATED="1783967750406" ID="ID_1287889954" MODIFIED="1783967801786" TEXT="Hinweis auf steigende Anzahl an Cores">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
Lumiera k&#246;nnte ja schon auch auf eine high-end-Workstation laufen
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1783967758928" ID="ID_284611592" MODIFIED="1783967785981" TEXT="sollte Contention um das single-threaded bottleneck im Blick behalten">
<icon BUILTIN="messagebox_warning"/>
@ -1686,31 +1659,25 @@
<icon BUILTIN="bell"/>
<node COLOR="#435e98" CREATED="1783901558710" ID="ID_156133270" MODIFIED="1783967976008" STYLE="fork" TEXT="was ist IORING_SETUP_SQPOLL?">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
von Claude erw&#228;hnt im Zusammenhang mit Locking visibility. Er weist auch darauf hin da&#223; io_uring keine multiple thread submissions unterst&#252;tzt. Zu dem Schlu&#223; war ich ja auch gekommen
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<arrowlink COLOR="#4389dd" DESTINATION="ID_758622095" ENDARROW="Default" ENDINCLINATION="196;89;" ID="Arrow_ID_200171107" STARTARROW="None" STARTINCLINATION="334;27;"/>
<font NAME="SansSerif" SIZE="12"/>
<icon BUILTIN="help"/>
<node BACKGROUND_COLOR="#c8c0b6" COLOR="#435e98" CREATED="1783967899141" HGAP="34" ID="ID_1537697930" MODIFIED="1783967981234" TEXT="verstanden &#x2014; klarer Fall" VSHIFT="23">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
der Kernel kann nicht zaubern; stattdessen hat er dann einfach einen poll-Worker. Keine gute Idee in einer Architektur die selber alle Resourcen steuern m&#246;chte
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<icon BUILTIN="ksmiletris"/>
</node>
</node>
@ -1723,9 +1690,7 @@
<node CREATED="1783904515877" ID="ID_718620596" MODIFIED="1783904524313" TEXT="habe das nebenbei erw&#xe4;hnt"/>
<node CREATED="1783904524844" ID="ID_473487490" MODIFIED="1783904606751" TEXT="Claude best&#xe4;tigt: unter EGL ist die Anpassung nur minimal">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p http-equiv="content-type" content="text/html; charset=utf-8" class="font-claude-response-body break-words whitespace-normal">
<strong>Wayland/X11</strong>
@ -1745,8 +1710,7 @@
So it's genuinely a small, well-isolated patch of platform glue &#8212; a handful of lines, gated behind a runtime check of which GDK backend is active (<code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">GDK_IS_X11_DISPLAY</code>&#160;/ <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">GDK_IS_WAYLAND_DISPLAY</code>) &#8212; everything downstream of surface creation is untouched.
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<icon BUILTIN="idea"/>
</node>
</node>
@ -1908,6 +1872,9 @@
</node>
</node>
<node CREATED="1783644425567" ID="ID_238189215" MODIFIED="1783644429274" TEXT="Flow-Steuerung">
<node CREATED="1784052955974" ID="ID_1491751742" MODIFIED="1784052978769" TEXT="da nun Proactor &#x27f9; getrieben vom Timer-Tick">
<icon BUILTIN="idea"/>
</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>
@ -1969,54 +1936,42 @@
</node>
<node COLOR="#435e98" CREATED="1783786668847" ID="ID_517750346" MODIFIED="1783787279327">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
mein Bauchgef&#252;hl: <b>GPL</b>
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<icon BUILTIN="back"/>
<node CREATED="1783787032442" ID="ID_1472543330" MODIFIED="1783787496449">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
Das ist ein politisches Statement. <i>Das ist wichtiger denn je</i>
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
Der Vollst&#228;ndigkeit halber: es gibt den Unterschied zwischen negativer Freiheit und positiver Feiheit; letztere ist der Entschlu&#223; zu einer klar formulierten Vision. Die GPL formuliert hierzu die &#187;vier Freiheiten&#171;. Dahinter steht das Ideal des Gemeinwohls. Das mu&#223; leider verteidigt werden gegen Ausbeutung und Veruntreuung.
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
<node CREATED="1783787061169" ID="ID_1906878927" MODIFIED="1783787101544" TEXT="Der Code soll auf die Lumiera-Website. Eine Abweichende Lizenz schafft dort nur Komplikationen"/>
<node CREATED="1783787106667" ID="ID_1055216820" MODIFIED="1783787173608" TEXT="und dann ist da noch der EGL-Code aus dem letzen Jahr (den ich tats&#xe4;chlich kopiere)">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
...und der beruht, transitiv argumentiert, nicht nur pro-forma, sondern auch dem Geist nach auf Publikationen unter Copyleft
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
</node>
</node>

View file

@ -29,12 +29,14 @@ class UringVideoPlayer
Glib::Dispatcher dispatchFrame_;
EglDisplay videoDisplayer_;
std::thread proactorThread_;
public:
UringVideoPlayer (Gtk::Window& appWindow)
: videoDisplayer_{appWindow, dispatchFrame_}
, proactorThread_{[this]{ playVideo(); }}
{ }
void terminate();
private:
void
@ -48,17 +50,20 @@ class UringVideoPlayer
unique_ptr<UringVideoPlayer>
launchPlayer (Gtk::Window& appWindow)
{
appWindow.resize (EglDisplay::VIDEO_WIDTH, EglDisplay::VIDEO_HEIGHT);
auto player = std::make_unique<UringVideoPlayer> (appWindow);
return player;
}
void
cleanUp (UringVideoPlayer& player)
UringVideoPlayer::terminate()
{
///// signal stop to proactor
videoDisplayer_.cleanUp();
}
@ -68,6 +73,6 @@ main (int, const char*[])
{
return GtkUringApp<UringVideoPlayer>{"io-uring-demo"}
.onClick (launchPlayer)
.onClose (cleanUp)
.onClose ([](auto& player){ player.terminate();})
.run();
}

View file

@ -12,8 +12,7 @@
#include "commons.hpp"
#include "gtk-egl-app.hpp"
#include "image-generator.hpp"
#include "gtk-uring-app.hpp"
#include <iostream>
#include <algorithm>
@ -39,35 +38,43 @@ using std::string;
class EglDisplay
{
/** EGL / X11 connection. */
EGLDisplay display{nullptr};
EGLSurface surface{nullptr};
EGLDisplay display_{nullptr};
EGLSurface surface_{nullptr};
EGLContext egl{nullptr};
EGLContext egl_{nullptr};
uint texID{0};
uint vboID{0};
uint vaoID{0};
uint texID_{0};
uint vboID_{0};
uint vaoID_{0};
float scaleX{1};
float scaleY{1};
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};
EglDisplay(FrameRate fps)
{ }
EglDisplay(Gtk::Window& appWindow, Glib::Dispatcher& dispatchFrame)
{
openDisplay (appWindow);
dispatchFrame.connect([this]{ displayFrame(); });
}
void cleanUp();
private:
void openDisplay (Gtk::Window&);
void displayFrame();
};
EglDisplay
openDisplay (Gtk::Window& appWindow, FrameRate fps)
void
EglDisplay::openDisplay (Gtk::Window& appWindow)
{
std::cout << "Open display-connection through EGL..." << std::endl;
EglDisplay ctx{fps};
// use the X-Window as anchor to build an OpenGL context via EGL
Glib::RefPtr<Gdk::Window> gdkWindow = appWindow.get_window();
@ -85,9 +92,9 @@ openDisplay (Gtk::Window& appWindow, FrameRate fps)
{EGL_PLATFORM_X11_SCREEN_EXT, xScreen
,EGL_NONE};
ctx.display = eglGetPlatformDisplay (EGL_PLATFORM_X11_EXT, xDisplay, SCREEN_SPEC);
if (EGL_NO_DISPLAY == ctx.display
or not eglInitialize(ctx.display, NULL,NULL))
display_ = eglGetPlatformDisplay (EGL_PLATFORM_X11_EXT, xDisplay, SCREEN_SPEC);
if (EGL_NO_DISPLAY == display_
or not eglInitialize(display_, NULL,NULL))
__FAIL ("could not establish EGL Display connection.");
EGLint DESIRED_ATTRIBS[]
@ -103,7 +110,7 @@ openDisplay (Gtk::Window& appWindow, FrameRate fps)
EGLint _cnt;
EGLConfig config;
if (not eglChooseConfig (ctx.display
if (not eglChooseConfig (display_
,DESIRED_ATTRIBS
,& config
,1,&_cnt)
@ -111,8 +118,8 @@ openDisplay (Gtk::Window& appWindow, FrameRate fps)
__FAIL ("unable to select a EGL display config with the required attributes");
ctx.surface = eglCreateWindowSurface (ctx.display,config,xWindow,nullptr);
if (not ctx.surface)
surface_ = eglCreateWindowSurface (display_,config,xWindow,nullptr);
if (not surface_)
switch (eglGetError())
{
case EGL_BAD_CONFIG:
@ -138,14 +145,14 @@ openDisplay (Gtk::Window& appWindow, FrameRate fps)
// ,EGL_CONTEXT_MINOR_VERSION, 3
,EGL_NONE};
ctx.egl = eglCreateContext (ctx.display, config
,EGL_NO_CONTEXT // do not share definitions and data with another context
,CTX_ATTRIBS);
if (not ctx.egl)
egl_ = eglCreateContext (display_, config
,EGL_NO_CONTEXT // do not share definitions and data with another context
,CTX_ATTRIBS);
if (not egl_)
__FAIL ("failed to create OpenGL context for this display with desired visuals");
// create a binding for the current thread to use this context on this window
if (not eglMakeCurrent (ctx.display, ctx.surface,ctx.surface, ctx.egl))
if (not eglMakeCurrent (display_, surface_,surface_, egl_))
__FAIL ("failed to attach an OpenGL context to the application X-Window");
@ -241,8 +248,8 @@ openDisplay (Gtk::Window& appWindow, FrameRate fps)
/* === setup a 2D texture, to be mapped into the viewport === */
glActiveTexture(GL_TEXTURE0); // activate texture-unit #0 on the GPU
glGenTextures (1, &ctx.texID); // allocate 1 new texture ID
glBindTexture (GL_TEXTURE_2D, ctx.texID); // use this textureID as "the" TEXTURE_2D
glGenTextures (1, &texID_); // allocate 1 new texture ID
glBindTexture (GL_TEXTURE_2D, texID_); // use this textureID as "the" TEXTURE_2D
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // configure image scaling filter
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); // clamp, don't wrap at the texture edges
@ -255,13 +262,13 @@ openDisplay (Gtk::Window& appWindow, FrameRate fps)
/* === setup a fixed geometry to hold that texture === */
glDisable (GL_DEPTH_TEST); // we do not need 3D layering / positioning
glViewport (0, 0, ctx.VIDEO_WIDTH, ctx.VIDEO_HEIGHT); // Origin in the middle of the window (note Y points upwards)
glViewport (0, 0, VIDEO_WIDTH, VIDEO_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
// dynamically into the available screen space, possibly scaling up/down
GLfloat sX{ctx.scaleX};
GLfloat sY{ctx.scaleY}; // Note: positions use »normalised device coordinates« (NDC)
GLfloat sX{scaleX_};
GLfloat sY{scaleY_}; // Note: positions use »normalised device coordinates« (NDC)
GLfloat w{1}; // TEXTURE_2D points cover the range (0,0) ... (1,1)
GLfloat h{1};
@ -273,11 +280,11 @@ openDisplay (Gtk::Window& appWindow, FrameRate fps)
,-sX, sY , 0,0 // have to map the texture flipped, since OpenGL orients Y upwards.
};
glGenBuffers (1, &ctx.vboID); // allocate 1 new vertex-buffer-object
glGenVertexArrays(1, &ctx.vaoID); // allocate 1 new vertex-array-object (to attach all definitions)
glGenBuffers (1, &vboID_); // allocate 1 new vertex-buffer-object
glGenVertexArrays(1, &vaoID_); // allocate 1 new vertex-array-object (to attach all definitions)
glBindVertexArray(ctx.vaoID); // activate the VAO to pick up the following definitions...
glBindBuffer (GL_ARRAY_BUFFER, ctx.vboID);
glBindVertexArray(vaoID_); // activate the VAO to pick up the following definitions...
glBindBuffer (GL_ARRAY_BUFFER, vboID_);
// copy the vertexData into the vertex-buffer-object currently bound to GL_ARRAY_BUFFER
glBufferData (GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_STATIC_DRAW);
@ -294,29 +301,25 @@ openDisplay (Gtk::Window& appWindow, FrameRate fps)
glEnableVertexAttribArray (0); // activate attribute #1 ≙ input argument `position` in vertexShader
glEnableVertexAttribArray (1); // activate attribute #2 ≙ input argument `texCoord` in vertexShader
// hand-over the activated connection context
// to be managed by the GTK application...
std::cout << "Started playback at "<<fps<<" frames/sec." << std::endl;
return ctx;
std::cout << "Successfully established OpenGL display ("<<VIDEO_WIDTH<<"x"<<VIDEO_HEIGHT<<"px) within GTK-Window." << std::endl;
}
void
displayFrame (EglDisplay& ctx)
EglDisplay::displayFrame()
{
uint frameNr = ctx.imgGen_.getFrameNr();
uint fps = ctx.imgGen_.getFps();
uint frameNr = imgGen_.getFrameNr();
uint fps = imgGen_.getFps();
if (0 == frameNr % fps)
std::cout << "tick ... " << ctx.imgGen_.getFrameNr() << std::endl;
std::cout << "tick ... " << imgGen_.getFrameNr() << std::endl;
// compute a buffer with RGB data and bind it into the prepared texture...
const void* buffer = ctx.imgGen_.buildNext().data();
const void* buffer = imgGen_.buildNext().data();
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
,ctx.VIDEO_WIDTH,ctx.VIDEO_HEIGHT, /*border*/ 0
,VIDEO_WIDTH,VIDEO_HEIGHT, /*border*/ 0
,GL_RGB // data layout of the provided pixels
,GL_UNSIGNED_BYTE // data format / size of the pixels
,buffer
@ -326,30 +329,18 @@ displayFrame (EglDisplay& ctx)
glDrawArrays(GL_QUADS, 0, 4);
// double-buffer flip, automatically invokes glFlush()
eglSwapBuffers (ctx.display, ctx.surface);
eglSwapBuffers (display_, surface_);
}
void
cleanUp (EglDisplay& ctx)
EglDisplay::cleanUp()
{
std::cout << "STOP " << ctx.imgGen_.getFrameNr() << " frames displayed." << std::endl;
std::cout << "STOP " << imgGen_.getFrameNr() << " frames displayed." << std::endl;
// detach binding with OpenGL context
eglMakeCurrent (ctx.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglDestroySurface (ctx.display, ctx.surface);
eglDestroyContext (ctx.display, ctx.egl);
eglTerminate(ctx.display); // detach EGL from display
}
int
main (int, const char*[])
{
return GtkEglApp<EglDisplay>{"demo.egl"}
.onStart (openDisplay)
.onFrame (displayFrame)
.onClose (cleanUp)
.run(30);
eglMakeCurrent (display_, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglDestroySurface (display_, surface_);
eglDestroyContext (display_, egl_);
eglTerminate(display_); // detach EGL from display
}

View file

@ -108,7 +108,7 @@ class GtkUringApp
demoWindow_.button_.set_sensitive(false); // disable the button
demoWindow_.button_.set_label("active");
auto closeHook = [this]{
if (closeTask_)
closeTask_(*processor_);