monotonic_buffer_resource sorgt für DMA Interrupt Error



  • @D_Key Dagegen ist Debugging bei meinem DOS-Projekt ja richtig luxuriös: Ich kann u.a. in QEMU mit FreeDOS testen. QEMU hat einen integrierten GDB Server (aktivierbar mit `-gdb'). Ich baue dann die DOS .EXE in zwei Schritten: Einmal eine ELF-Datei, mit einem über Linker Script integrierten MZ-header (DOS Executables) und vollen DWARF Debug-Informationen. Aus der erzeuge ich dann eine "rohe Binärkopie", das ist dann die DOS-Executable (MZ-Format). Die ELF-Datei verwende ich dann im GDB Debugger als "executable" und lasse den Debugger mit QEMU verbinden, während die .EXE ausgeführt wird. Das erlaubt dann volles Debugging in der IDE (Durchsteppen, Werte/Speicher inspizieren, Breakpoints setzen, etc.). Purer Luxus 😉

    Ich frage mich da gerade, ob es auch für die PS2 einen Emulator mit integriertem GDB-Server gibt. Dann wäre evtl. etwas ähnliches möglich. Eine schnelle Suche hat sowas hier zutage gefördert: Fork of PCSX2 that includes a GDB server. Keine Ahnung wie gut das funktioniert, aber vielleicht lohnt es sich ja, damit mal etwas zu experimentieren.

    Wenn das PS2-Board irgendwo ein serielles Interface hat, könnte man so eventuell sogar auf echter Hardware remote-debuggen. Evtl. auch über einen USB-port. Aber das könnte sehr aufwändig sein, sowas ans laufen zu bekommen. Es gibt da diese gdbstub.c die man im Prinzip mit einkompiliert und darin dann die Kommunikation mit der Außenwelt selbst implementiert (UART oder sowas). Das GDB-Protokoll ist in dem Stub schon fertig, es braucht nur den Code um die serielle Kommunikation durchzuführen. Aber wie gesagt, das könnte ein Krampf sein, bis sowas dann halbwegs läuft, wäre aber fürs Debuggen ein Traum.

    Auch das Problem mit der unpräzisen Emulation kenne ich (was du mit DMA Timing und PSCRT Register angerissen hast). QEMU und DOSBox sind für einige Dinge auch ungeeignet (speziell Hardware direkt ansprechen). Da nutze ich dann 86Box, was da deutlich präziser emuliert (und z.B. auch mit der Original Firmware/BIOS arbeitet). Es gab einige Bugs, die ich nur damit finden konnte. Auch 86Box scheint so einen GDB Server zu haben, der aber oft nicht mit einkompiliert ist. Den zu integrieren steht als nächstes an.

    Und was DOS-Grafik angeht: Die VGA-Adapter kennen zwar fixe Modi mit wohldefinierten Auflösungen, sind aber im Prinzip letztendlich auch nur "ein PCB, wo etwa 300 virtuelle Drähte rausgucken". Das ist dann die Grafik-API 😉 ... da kann man dann auch nicht-Standard Modi mit konfigurieren, wenn man an Registern für Timings und Frequenzen herumbastelt (und alles richtig macht, ohne dass der CRT Monitor explodiert). Das ermöglicht dann solche Modi wie "Mode X", die ursprünglich nicht vorgesehen waren aber sich dann doch irgendwie als "Standard" etabliert haben (gerade Mode X für Spiele wie Doom damals). Solche lowlevel-Hacks sind mir also durchaus ein Begriff 😉



  • @D_Key sagte in monotonic_buffer_resource sorgt für DMA Interrupt Error:

    Einige Sachen habe ich schon gefundene,d ie HEAP Fresser sind, unordered_map und vor allem
    unordered_map<int, std::vector> sind absolute heap Killer.

    Wegen den Vorgaben des C++ Standard bezüglich Iterator Invalidation muss unordered_map auch Kollisionen mittels Chaining auflösen und kann keine offene Adressierung verwenden. Das bedeutet, dass jeder "Bucket" eine Linked List ist, die alle Einträge mit dem selben Hash enthält. Konkret ist das eine zusätzliche Indirektion und eben auch eine zusätzliche Heap-Allokation je Eintrag. Eventuell sind hier alternative Implementierungen eine gute Wahl, wie z.B. Container der Google Abseil Bibliothek oder andere.

    Und: Wie wird Heap reserviert? Alles "manuell" oder über eine eine "malloc"-Implementierung? Wenn ja, welche? Eventuell ist das ja ein Punkt wo man ansetzen könnte, um Fragmentierung zu reduzieren.



  • @Finnegan sagte in monotonic_buffer_resource sorgt für DMA Interrupt Error:

    Dagegen ist Debugging bei meinem DOS-Projekt ja richtig luxuriös:

    Dadurch das ich ohnehin nur Mikrocontroller Programmiere, bin ich debug Probleme gewohnt.

    @Finnegan sagte in monotonic_buffer_resource sorgt für DMA Interrupt Error:

    Ich frage mich da gerade, ob es auch für die PS2 einen Emulator mit integriertem GDB-Server gibt.

    Bei PCSX2 ist ein Debugger mit integriert, der funktioniert auch ganz gut. Aber wie gesagt bei Timing Problemen ist der Emulator unnütz.

    @Finnegan sagte in monotonic_buffer_resource sorgt für DMA Interrupt Error:

    Wenn das PS2-Board irgendwo ein serielles Interface hat, könnte man so eventuell sogar auf echter Hardware remote-debuggen.

    Dazu gibt es https://github.com/ps2dev/ps2gdb
    Das funktioniert über die Netzwerkschnittstelle, aber das ist schnarch langsam
    wenn du 20Mb an Texturen drüber schaufelst vergehen 40 Min.
    Für die ersten Gehversuche ist das aber ok.

    Wenn die Projekte größer werden ist FreeMC Boot und ein USB-Stick die beste Wahl.
    Der USB Port ist zwar auch langsam aber immer noch besser als die Netzwerkschnittstelle.

    Der richtige Kracher ist dann der MemoryCard BUS, das ist ein "Regulärer SPI Bus".
    Aber es gibt eine Fallstricke es gibt kein Chip Select.
    das ist anders gelöst siehe:
    https://hackaday.io/project/170365-blueretro/log/186471-playstation-playstation-2-spi-interface.
    Da kannst du dann die Projekte via SD-Karte laden:
    https://www.trisaster.de/page/index.php?topic=575

    Das beste ist aber man hat ein DEV Tool, da kann man auch den IOP+PS1 Spiele debuggen, was in PCSX2 überhaupt nicht geht.

    Bei den DOS Kram bin ich raus, ich habe zwar noch einen Pentium 133 auf dem Dachboden aber ich nicht so in die DOS Welt hineingewachsen.
    Obwohl ich auch gerne crystal caves und simcity damals gespielt habe.
    Oder Superfrog...

    @Finnegan sagte in monotonic_buffer_resource sorgt für DMA Interrupt Error:

    Und: Wie wird Heap reserviert? Alles "manuell" oder über eine eine "malloc"-Implementierung?

    Wie genau meinen heap initialisieren? Im Likerfile wird nur der Stack initialisiert und beim Linken die Statischen Arrays bzw in crt0.s. Der Rest der über bleibt ist der heap. std:.vector oder std::unordered_map wird beim Programmstart in den heap geladen.

    @Finnegan sagte in monotonic_buffer_resource sorgt für DMA Interrupt Error:

    wie z.B. Container der Google Abseil Bibliothek oder andere.

    Das schaue ich mir an.



  • @D_Key sagte in monotonic_buffer_resource sorgt für DMA Interrupt Error:

    Das funktioniert über die Netzwerkschnittstelle, aber das ist schnarch langsam
    wenn du 20Mb an Texturen drüber schaufelst vergehen 40 Min.
    Für die ersten Gehversuche ist das aber ok.

    Texturen über die Debug-Schnittstelle finde ich auch etwas schwergewichtig. Auf einer DOS-Maschine über den seriellen Port wäre das auch nicht gerade schneller. Eher noch langsamer. Aber braucht man das wirklich? Ich würde darüber eher nur Breakpoints setzen und einzelne Variablen im Speicher inspizieren. Wenn ich die ganze Textur begutachten will, würd ich mir die dann eher auf der Maschine anzeigen lassen. Also in dem Fall dann sowas wie "printf-Debugging" nur eben mit Grafik und Texturen 😉

    @Finnegan sagte in monotonic_buffer_resource sorgt für DMA Interrupt Error:

    Und: Wie wird Heap reserviert? Alles "manuell" oder über eine eine "malloc"-Implementierung?

    Wie genau meinen heap initialisieren? Im Likerfile wird nur der Stack initialisiert und beim Linken die Statischen Arrays bzw in crt0.s. Der Rest der über bleibt ist der heap. std:.vector oder std::unordered_map wird beim Programmstart in den heap geladen.

    Ich hab das glaube ich nicht ganz sauber formuliert. Wie wird der Heap verwaltet wenn du dynamisch zugewiesenen Speicher benötigst? Das was "übrig bleibt" ist der Heap, geschenkt. Aber auf dem arbeitet ja meist noch ein Speichermanager wie eine malloc-Implementierung bzw. im weiteren dann ein operator new der meist eben auch auf malloc aufbaut.

    Statisch initialisierte std::vector und evtl. auch std::unordered_map können theoretisch auch direkt vom Compiler in der .bss oder .data Section angelegt werden. Evtl. mit einem statischen Initialisierungscode um die Datenstrukturen zu füllen (sollte von der crt0 aufgerufen werden). Aber neue Werte einfügen und eventuell auch alte löschen sollte eigentlich auch malloc/free benötigen. Zumindest für den Standard-C++-Allokator.

    Kurz: bei dem SDK ist eine C Runtime dabei und die kann auch malloc/free? Ich vermute mal ja. Dieses malloc/free dürfte halt auch seinen Anteil an der "Heap-Fragmentierung" haben. Darauf will ich hinaus.

    Verwendest und verwaltest du denn auch Heap-Speicher direkt oder läuft das alles über malloc/free und new/delete? Oder machst du es eventuell nur "direkt"?

    In meinem DOS-Projekt habe ich auch einen solchen Heap (der Aufgrund von memory-gemappter Hardware und geladenem DOS bereits von Anfang an eine gewisse "Fragmentierung" aufweist). Den Speicher stelle ich dann dlmalloc zur Vefügung (eine simple und leichtgewichtige malloc-Implementierung). Das verwaltet dann den verfügbaren Heap dynamisch via malloc/free, auf denen dann alles weitere aufbaut. Letzten Endes eben auch die Default-Allokatoren für std::vector und andere.



  • @Finnegan sagte in monotonic_buffer_resource sorgt für DMA Interrupt Error:

    Texturen über die Debug-Schnittstelle finde ich auch etwas schwergewichtig.

    Ja da gebe ich dir recht, im Grunde richtet man seinen TexturPuffer-Konfiguration einmalig ein und dann ist gut.

    @Finnegan sagte in monotonic_buffer_resource sorgt für DMA Interrupt Error:

    Statisch initialisierte std::vector und evtl. auch std::unordered_map können theoretisch auch direkt vom Compiler in der .bss oder .data Section angelegt werden. Evtl. mit einem statischen Initialisierungscode um die Datenstrukturen zu füllen (sollte von der crt0 aufgerufen werden).

    Also was crt0 genau macht, habe ich noch nicht untersucht.
    Die Datei ist aber kein Geheimnis:
    https://github.com/ps2dev/ps2sdk/blob/master/ee/startup/src/crt0.c

    @Finnegan sagte in monotonic_buffer_resource sorgt für DMA Interrupt Error:

    Verwendest und verwaltest du denn auch Heap-Speicher direkt oder läuft das alles über malloc/free und new/delete?

    Das kommt darauf an für einigen Sachen nutze ich new.
    Bei den Texturen habe ich was mit align_val_t gebastelt.

    Bei MIPS Prozessoren muss alles ausgerichtet sein, sonst knallt es bei SQ oder LQ ,
    Sprich Store Quadword / Load Quadword.



  • @D_Key Ja, ich gehe davon aus, dass da irgendwo eine malloc-Implementierung drin ist. Beim groben Überfliegen hab ich auch was von "newlib" gesehen. Das ist eine flexible libc-Implementierung, die sich gut an verschiedenste Systeme anpassen lässt. Ich verwende auch für mein Projekt ein Derivat von dieser (picolibc). Das ist dann unter der Haube sehr wahrscheinlich ebenfalls dlmalloc oder eine Modifikation davon, was da den Speicher für new/delete verwaltet.

    Ich versuche halt, dein Heap-Fragmentierungs-Problem zu verstehen. Das sollte sich eigentlich dadurch äußern, dass Allokationen ab einer gewissen Größe fehlschlagen, obwohl in der Summe noch genug Speicher frei wäre. Ist das korrekt?

    Das mit dem VIF FIFO verstehe ich a nicht so ganz. Ich hab wie gesagt keine Ahnung von der PS2, aber wenn das stimmt was mir die KI hier erzählt, dann sollte der eigentlich nicht im Hauptspeicher liegen, sondern ein relativ kleiner (nur 128 Byte großer) Hardware-Puffer der Vector Instructional Unit (VIF) sein, der ausschließlich über spezielle Register angesprochen wird. Also nicht über MMIO in den Hauptspeicher-Adressraum gemappt. Daher ist mir etwas unklar, wie sich das mit dem normalen RAM und dem Heap in die Quere kommen kann.

    Der FIFO sollte eigentlich auch nur sehr kleine und kompakte Grafik-Kommandos beinhalten. Kann es vielleicht sein, dass sich da irgendwas staut und die Kommandos nicht abgearbeitet werden, so dass sich letztendlich dann auch sowas wie geladene Texturen im RAM unterwegs in den VRAM (via DMA?) ansammeln und den voll machen? Das wäre zumindest für mich einleuchtender. Aber wie gesagt, keine wirkliche Ahnung von der Kiste 😉

    @D_Key sagte in monotonic_buffer_resource sorgt für DMA Interrupt Error:

    align_val_t

    Ich kenne da deine konkrete Problematik nicht, aber Klassen und Daten-Member sollten sich eigentlich mit alignas(N) passend ausrichten lassen. Automatische Variablen, die Default-Allokatoren und new/delete sollten das auch entsprechend respektieren. Eine selbst-implementierte std::pmr::monotonic_buffer_resource sollte das in do_allocate() über den Alignment-Parameter natürlich auch berücksichtigen. dlmalloc sollte auch auf malloc/free-Ebene posix_memalign() unterstützen. Da müsste man mal schauen, ob das nach einem #include <malloc.h> zur Verfügung steht. C++17 kennt auch std::aligned_alloc(). Nur so als Anregung, aber offenbar hast du das Problem ja bereits gelöst.



  • @Finnegan sagte in monotonic_buffer_resource sorgt für DMA Interrupt Error:

    Ich versuche halt, dein Heap-Fragmentierungs-Problem zu verstehen. Das sollte sich eigentlich dadurch äußern, dass Allokationen ab einer gewissen Größe fehlschlagen, obwohl in der Summe noch genug Speicher frei wäre. Ist das korrekt?

    Ja und nein, als ich noch nicht mit monotonic buffer resource gearbeitet habe, ist es zwischendurch einfach mit sdt::bad_alloc krachen gegangen. Obwohl noch 10 MB frei sein sollten. Ein Charakter hat zwei Texturatlaten beide zusammen ca 1MB.
    Das ist so das größte was ich zur Laufzeit des Spieles lade.

    Das laden von Texturen zu Laufzeit hat sich aber als schlecht erwiesen, weil das immer leichte Laderuckler verursacht hat.
    Nun lade nun via Ladebildschirm alle Texturen in den RAM.

    Weil aber die Luft hinten raus ganz schön dünn wird habe ich allen anderen kleinen Objekten die via new hinzugekommen, eine monotonic buffer resource verpasst.

    Die buffer resource läuft bei CPU Core zugriffen perfekt. Aber sobald man da Sachen ablegt die man via DMA verschickt knallt es irgendwann.

    Aus Komfort gründen habe ich aber für die map's eine unsynchronized_pool_resource beaufschlagt.

    @Finnegan sagte in monotonic_buffer_resource sorgt für DMA Interrupt Error:

    Das mit dem VIF FIFO verstehe ich a nicht so ganz.

    Also... Es gibt drei wege Grafiken aus zu geben (drei DMA Channel) bei der PS2, Path 1, Path 2, Path 3.
    Diese sind Priorisiert. Path 1 hat die höchste Priorität, dann Path 2 und dann Path 3
    Path 1 wird von der Vector Unit 1 losgelassen und schreibt aus dem eignen VU Mem.
    Path 2 befeuert entweder den Graik chip oder die VU1 mit Daten.
    Bei mir befeuert Path 2 die VU1.
    Path 3 läuft bei mir im Chain Mode und lädt die Texturen in den VRAM.
    Das Passiert Permanent Textur->VU1->xgkick(Path 1)

    Damit das Sprite aber gezeichnet werden kann muss ich zuvor auf meine Textur warten. Ich bremse also die VU1 noch aus. Dadurch läuft der VIF1 FIFO voll.
    VIF = vector interface 1

    Man kann einen Textur Transfer starten und parallel dazu VU1 Daten an das GIF Schicken.
    Aber wenn die Textur vom GIF noch nicht verarbeitet wurde, kommt es zu Textur glitchen
    Das ist jetzt in meinem Fall so, weil die Animation bei mir in der VU 1 berechnet werden aber die Textur muss vorher im Gif angekommen sein

    Wenn man ein 2D Spiel alla Street FIghter oder Tekken programmiert, könnte man die Texturatlanten beider Spielfiguren einmalig in den VRAM kopieren und dann die Animation via VU 1 hintereinander berechnen.

    Aber bei meinem Spiel habe ich 20 verschneide Spielfiguren. Es muss also dafür gesorgt werden das innerhalb der 30 fps alle Figuren gegenzeichnet wurden.
    Und das funktioniert.

    Laut Datenblatt ist der VIF FIFO 16 Qwörter groß also 256 Byte.
    Wo die liegen K.A.

    Man kann auch eine Textur im ganzen, also die gesamte Bitmap an das GIF schicken, aber das ist ineffizient. Das Geschieht via VIF 1 Direkt Transfer. Du hast dann eben ein 1Mb großes DMA Paket. Vorteil ist die Textur muss erst im GIF ankommen, ehe man Daten zur VU1 senden kann.

    @Finnegan sagte in monotonic_buffer_resource sorgt für DMA Interrupt Error:

    Beim groben Überfliegen hab ich auch was von "newlib" gesehen.

    Ja das ist korrekt, das SDk wurde irgendwann mal auf newlib Portiert.
    Einige alte Sachen lassen sich dadurch auch nicht mehr Compilern.
    z.b der Mincraft Port oder Quake2



  • @D_Key So wie ich das verstehe werden die Daten wie Texturen zuerst in den Hauptspeicher geladen und dann via DMA in den VRAM übertragen. Ist das korrekt?

    Ich frage mich, ob es eventuell Sinn macht einen DMA Buffer mit fester Größe für die Übertragung zu verwenden. Ich kenne von meinem Projekt einen ähnlichen Mechanismus nur von digitalen Audiodaten für die Soundblaster-Karte. Dort verwendet man eine Art "Double Buffering"-Ansatz (oder auch Ring Buffer): Man nimmt einen fixen, sagen wir mal 256 KiB großer Buffer, und füllt ihn mit (Audio-) Daten. Dann programmiert man die Soundkarte dann so, dass sie den Buffer in Blöcken zu je 128KiB in einer kontinuierlichen Schleife übertragen soll. Nach jedem dieser 128KiB-Blöcke generiert der DMA Controller einen Hardware-Interrupt, der dann das Signal dafür ist, dass man die Hälfte des Buffers, die gerade übertragen wurde, mit neuen Daten füllen kann. Also in etwa so:

    1. Buffer Komplett füllen
    2. DMA konfigurieren und starten
    3. Interrupt: erste Hälfte des Buffers wurde übertragen -> Programm beginnt, weitere Daten in erste Hälfte zu schreiben.
    4. Interrupt: zweite Hälfte wurde übertragen -> Programm schreibt weitere Daten in zweite Hälfte des Buffers.
    5. GOTO 3

    Ich frage mich, ob man so ein Streaming-Schema eventuell auch für die Übertragung von Textur- und anderen Daten auf der PS2 verwenden kann. Das würde wegen des fixen Buffers die ganze dynamische Speicherverwaltung für den Vorgang eliminieren. Du müsstest beim Laden der Daten dann auf ein Signal (Interrupt?) warten, bevor du die nächsten Daten lädst und in den Buffer schreiben kannst.

    Nur so eine Idee, aber keine Ahnung ob sich das so auf der PS2 umsetzen lässt. Da gab es aber schon ganz schön grafisch anspruchsvolle Spiele auf dem Ding, ich frage mich, wie die das gemacht haben. Da waren sicher oft mehr als nur 4 MiB Textur- und Geometriedaten involviert (die PS2 hat ja soweit ich sehe nur 4MiB VRAM (?)).



  • @Finnegan sagte in monotonic_buffer_resource sorgt für DMA Interrupt Error:

    Ich frage mich, ob man so ein Streaming-Schema eventuell auch für die Übertragung von Textur- und anderen Daten auf der PS2 verwenden kann. Das würde wegen des fixen Buffers die ganze dynamische Speicherverwaltung für den Vorgang eliminieren. Du müsstest beim Laden der Daten dann auf ein Signal (Interrupt?) warten, bevor du die nächsten Daten lädst und in den Buffer schreiben kannst.

    Ja das geht, die idee ist noch nicht mal so schlecht.
    Ich habe noch nie in den Interrupt Controller geschaut was der so kann. Nur einmal für die Timer.
    Wird aber auch tricky bei meinem Spiel.

    Ich muss das mit den Texturatlanten mal Sensibilisieren.
    Ich habe 20 unterschiedliche Charaktere, 10 Waffen, 30 Spezial Angriffe.
    4 Spezial Effekte (Blut, blast usw).
    Allein nur die Charaktere haben 400 Animationen in jeweils 2 bmp Dateien.

    Ein Texturatlas ist 800x560px und die Sprites jeweils 80x80 px ca.
    Ich schiebe nun jeweils vor jedem draw Befel den Kompletten Texturatlas in den vram.
    Der Grafik Chip pickt nun src xy und Pakt es im Framebuffer an dest xy.
    Und geht zum nächsten Charakter bzw zu zeichnenden Objekt.
    Ich muss also auf der CPU eine Draw List Sprich Zeichen Reihenfolge erstellen

    Mir ist zwischendurch eingefallen man kann dank Chain DMA, sich aus dem Texutratlas ein 80x80 Sprite herauspicken und schreibt das an eine Koordinate im VRAM.
    Da mein TexturPuffer im VRAM 800x560 PX ist, passen da auch meine 80x80 Sprites der 20 Charaktere rein.

    Nun das aber, denn ich verwende 8bit Texturen mit Clut.
    Wenn ich nun nur die Indexe hochlade, würde er für alle Charaktere die CLUT vom letzten Sprite nehmen. Alle andren hätten die Falsche Farbe.

    Ich müsste alles auf 16 bit Texturen umbauen, was auch nicht geht da kein VRAM frei.

    Es ist halt alles mager ausgelegt bei der PS2.



  • @D_Key sagte in monotonic_buffer_resource sorgt für DMA Interrupt Error:

    Es ist halt alles mager ausgelegt bei der PS2.

    Vielleicht ist das ja auch die besondere Herausforderung der effizienten PS2-Programmierung die ganzen Chips den Perfekten Tanz aufführen zu lassen? Permanent neue Daten rein-streamen und alte verwerfen, so dass alles genau zum richtigen Zeitpunkt zur Verfügung steht?

    Was ist mit Texturkompression? Das sollte die doch auch in Hardware machen können, evtl. während des Renderns dekomprimieren? ChatGPT erzählt mir sogar gerade, dass die PS2 angeblich auch JPEG während des Renderns dekomprimieren konnte. Wenn das stimmt, dann nimmt das eine Menge Druck von dem mageren VRAM.

    Edit: Jo, JPEG war wohl ne Halluzination. Kam mir auch etwas seltsam vor für die Zeit. CLUT (was du ja auch verwendest) und VQ-Kompression waren offenbar eher die Methoden um den Speicherbedarf niedrig zu halten.


Anmelden zum Antworten