Start von 619.000 Tetris auf GLSL, deren Rendering und einem einfachen Bot

Ich hatte die „Idee“, die maximale Anzahl von gleichzeitig ausgeführten Tetris für einen Shader (eine Framebuffer-Textur) festzulegen.


Das Folgende ist eine kurze Beschreibung der Funktionsweise des resultierenden Codes.


Was ist das:


Jedes Tetris arbeitet in drei Pixeln. Bei einer Auflösung von 619200 können Sie jeweils 619200 Kopien 619200 . Hat auch einen einfachen Bot für das automatische Spielen gemacht.
Am Ende des Beitrags Links zum Ausführen und Quellen.
Video aktualisiert, zeigt die Anzahl der verbleibenden Felder bis Null.



Datenspeicherung


Tetris-Tisch der Größe [10, 22] (10 Breite, 22 Höhe).
Jede Zelle kann entweder leer oder nicht leer sein.
Zum Speichern der gesamten Tabelle sind insgesamt 22 * 10 = 220 Bit erforderlich.
Ein „Pixel“ sind vier 24-Bit-Floats mit 96 Bit pro Pixel.


Visuell (ein Teil des Debug-Frames) werden drei Pixel rot hervorgehoben. Dies ist ein gespeichertes Feld:


Bild


2 * 96 + 24 + 4
Zwei Pixel, ein Float des dritten Pixels, 4 Bits des zweiten Floats des dritten Pixels
Es gibt zwei unbenutzte Floats im dritten Pixel pixel3.zw , sie speichern den Zustand der Logik genauer


  • z speichert drei Acht-Bit-Zahlen [a,b,c]
    - eine Position des aktuellen Blocks als ID der Position im Array (ein Array mit einer Größe von 220 Bit, die maximale Position ist 220, was weniger als 0xff ist)
    - b Zeit bis zum automatischen Herunterfallen (Timer) jedes Frames -1 auf diese Zahl, da es 0 wurde, dann fällt es auf den Block herunter
    - c ID des aktuellen Blocks
  • w auch [a,b,c] , aber auch das Vorzeichen (positiv oder negativ) des gesamten Schwimmers ist die Flagge des Spielendes in der aktuellen Tabelle (um keine Ressourcen zu verschwenden, wenn das Feld überfordert ist )
    - eine Aktion, keine Aktion (0), links (1), rechts (2) usw. Der vollständige Code in Common , Aktionen haben zwei Zustände. Überprüfen Sie links und prüfen Sie, ob es möglich ist, sich nach links zu bewegen. Dann wird die Aktion auf left_ move gesetzt .
    - [b,c] 0xffff (16 Bit) Punkte der aktuellen Tabelle, die Anzahl der Zeilen, die gebrannt haben

Im zweiten Float des dritten Pixels bleiben 20 ungenutzt.


Debug- Frame, der zeigt, dass die Speicherlogik ordnungsgemäß funktioniert
Auf der linken Seite befindet sich ein weißes Feld mit einer Größe von drei Pixeln, das speziell festgelegt wurde, um zu zeigen, dass die Lücken korrekt verarbeitet werden (bei einer Auflösung von nicht einem Vielfachen von drei wird der Streifen in einem Winkel verlaufen).
Bedingung in Zeile 75 Puffer A.


Bild


Warum brauche ich Aktions-IDs:


  • Die Daten werden in drei Pixeln gespeichert. Es ist unmöglich, die Logik gleichzeitig zu überprüfen und die Daten in einem Frame zu ändern (ohne die gesamte Logik auszuführen und die gesamte Karte in jedes Pixel zu laden, erhöht sich die Last um das Zehnfache).
  • Daher arbeitet die Datenspeicherlogik in jedem Pixel und führt die empfangenen left_ move- Befehle aus. Die left_ check check- Befehle werden nur in einem Pixel (drittes) ausgeführt.

Langsamer Ort


  • Jedes dritte Pixel (Logikpixel) dekomprimiert die gesamte Karte (Lesen aller drei Pixel).
  • Die verbleibenden zwei Pixel dekomprimieren nur "sich selbst" (ein Pixel), um die gespeicherte Aktion auszuführen.
  • Während der Aktion wird die Linie gebrannt, ein weiteres Pixel wird geladen, da der Tisch herunterfällt und die unteren Teile des Tisches wissen sollten, was oben ist.

Leistung des Speicheralgorithmus


Setzen Sie für den Test #define debug auch dort auf Common und AI 0 .
Ich habe ein solches Ergebnis erzielt - 10 FPS beim Rendern und Verarbeiten aller 619200 Felder.
auf 120 Tausend Feldern (25fps)


Bild


Bot-Logik


Logik Sehr schlecht , der Bot brennt in einer Minute aus und erreicht bis zu 60 Punkte.


Ich konnte keine gute Logik mit vielen Zyklen starten, in denen Löcher, Leisten und brennbare Felder überprüft wurden, wobei die beste Position auf der Grundlage aller möglichen Stürze berücksichtigt wurde ...
Gute Logik funktionierte bei mir bis zu 100 Kopien und gab eine starke Verzögerung bei der Umgehung aller Zyklen.


Meine Bot-Logik funktioniert so
Die gesamte Logik befindet sich in der Funktion AI_pos_gen in Puffer A, es gibt zehn Zeilen davon.


Pseudocode:
Die Höhe für die Blockinstallation prüfen entspricht dem Maximum für das Feld in der aktuellen Spalte (eine Zeile auf Höhe prüfen).


 (4   ){ (  (10)){ (     ){  (    ,  )   best ID()  best POS } } }  (     )   (  )      0     1 

Es stellt sich heraus, dass drei Zyklen üblich sind - sie setzen den Block so, dass die Höhe minimal ist.


Die Funktion AI_pos_gen wird aufgerufen, wenn ein neuer Block angezeigt wird, und gibt die Position für das Herunterfallen von oben zurück. Dabei wird die Block-ID verwendet und gedreht. Die Funktion arbeitet im dritten Pixel (Logik), dh sie verfügt über eine vollständig geladene Karte (Kartenarray).
Sie können leicht versuchen, Ihren Bot zu schreiben, wenn Sie möchten.


Langsamster Ort
Als ich nur eine Schleife zum Testen von Löchern hinzufügte, stürzte mein Grafikkartentreiber ab, als die Anzahl der Bots mehr als 10.000 betrug. Der Bot, den ich geschrieben habe, ist die „minimalistischste“ Version des Bots, die ich machen konnte, und es ist leider sehr schlecht.


Benutzeroberfläche / Rendering


Alle Renderings in Image , UI-Logik in Buffer B.


Rendern:
Teilen Sie den Bildschirm in Kacheln und zeichnen Sie eine Tabelle in jede Kachel, minimale Belastung.


Logik des Ladens einer Karte - jedes Pixel wird nicht entpackt, jedes Pixel wird entpackt, nur das „erforderliche Bit“ wird (buchstäblich) entpackt, der Funktionscode lautet:


 int maptmp(int id, int midg) { int nBits = 8; ivec4 pixeldata = loadat(id, midg); int itt = (id / 24) / 4; //data pixel id 0-2 int jtt = (id - itt * 24 * 4) / 24; //component in data pizel id 0-3 int ott = (id - itt * 24 * 4 - jtt * 24) / 8; //component in unpacked value 0-2 int ttt = (id - itt * 24 * 4 - jtt * 24 - ott * 8); //bit after int2bit 0-7 ivec3 val = decodeval16(pixeldata[jtt]); int n = val[ott]; for (int i = 0; i < nBits; ++i, n /= 2) { if (i == ttt) { if ((n % 2) == 0)return 0; else return 1; //switch + return does not work on windows(Angle) /*switch (n % 2) { case 0:return 0;break; case 1:return 1;break; }*/ } } return 0; } 

Um Pixelbildung während des Bildlaufs zu vermeiden , geht ab 43000 der Bruchteil des Floats verloren, und es funktioniert nicht, 619.000 zum UV-Bildlauf zum Bildlauf hinzuzufügen (anstelle von Tabellen werden Pixel angezeigt).
Alle Bildläufe sind in eine große Kachel unterteilt und drehen sich in einem Kreis, wodurch maximal 32 UV-Werte hinzugefügt werden. (Zeile 207 im Bild ).


Das gleiche wird gemacht, um die Feld-ID zu bestimmen. (Zeile 215 im Bild )


Benutzeroberfläche


Zahlen:
Gelb ist die Anzahl der Tetrisfelder.
Links groß - die Nummer des aktuellen Feldes.
Unten rechts - die Punkte des aktuellen Feldes.


Quelle und Start


Bufer A- Logik, Bufer B ist UI-Steuerung, Bildwiedergabe
Quelle unter https://www.shadertoy.com/view/3dlSzs (Kompilierungszeit durch Winkel 16 Sekunden)
Der Bot ist dort deaktiviert (Sie können ihn aktivieren) und alle Felder können über die Tastatur abgespielt werden.


Steuern Sie die Pfeile nach links / rechts / oben / unten.


Zurücksetzen des roten Rechtecks ​​der Benutzeroberfläche, Verschieben (Ziehen Sie die Maus, indem Sie auf LMB klicken) und Klicken Sie auf die Felder, um einen Bildlauf durchzuführen, oder wählen Sie das anzuzeigende Feld aus.


Starten Sie über einen Webbrowser:


  • Führen Sie chrome mit chrome.exe aus --use-angle = gl
  • Folgen Sie dem Link zu Shadertoy
  • Wählen Sie im Editor auf der Site Common aus und löschen Sie #define no_AI
  • (auch allgemein) setze #define AI 199 auf 0, d. h. #define AI 0
  • Klicken Sie auf die Kompilierungsschaltfläche (unter dem Editorfenster im Shader) und klicken Sie auf Vollbild

Die zweite Möglichkeit besteht darin, den Shader in einem beliebigen "Shader Launcher" auszuführen. Hier ist der Link zu dem Archiv ( Download ), in dem sich die * .exe-Datei mit diesem Shader befindet.


OpenGL-Kompilierungszeit ca. 10 Sek.


Update : Ein Shader mit Lochprüfung https://www.shadertoy.com/view/wsXXzH wurde hinzugefügt
anstelle der Bedingung für eine bessere Position auf gleicher Höhe. Die Funktion check_block_at_wh (Zeile 380 BufA) zählt Löcher zusammen mit der Überprüfung der Gültigkeit der Position, es wurden keine neuen Zyklen hinzugefügt und die Bedingungszeile 442 bis 459 BufA.
Es brennt auch schnell in einer Minute innerhalb von 30-60 Punkten. (Natürlich müssen Sie einen großen Bereich auf Löcher überprüfen, aber dies führt zu starken Bremsen.)


Und zwei Bilder, die die Arbeit ein wenig erklären:
Positionsauswahl https://i.imgur.com/e0uENgV.png
Die Blockposition für die Bedingung lautet https://i.imgur.com/ORECXUW.png

Source: https://habr.com/ru/post/de443042/


All Articles