VRAM als Zwischenspeicher nutzen.

    • Skript

    Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

    • VRAM als Zwischenspeicher nutzen.


      Vorwort
      Hinweis für Allergiker: Kann Spuren von Esotherik und fehlenden Fakten enthalten. Mathematisch habe ich mich leider verskillt, daher kann ich die Formel nicht auf Plausibilität prüfen. Ich bedanke mich aber an math.stackexchange.com für das Bereitstellen der Formel. Ich kann zumindest bestätigen, dass die Rückrechnung jedes Mal erfolgreich war. Es ist komplett fraglich, ob das ganze effizienter als der gewöhnliche RAM ist. Ich habe aber ein paar Tage lang recherchiert und zumindest ein Proof of Concept.

      Hintergrund
      Für gewöhnlich landen die meisten Daten des Game Makers im RAM. Der Game Maker speichert jedoch scheinbar Surfaces aus Geschwindigkeitsgründen im VRAM der Grafikkarte. Leider sind jegliche Interaktionen im VRAM dem User entweder verwehrt, oder gar nicht erst dokumentiert. Aufgrund der Umrechnung, bin ich mir nicht sicher, ob die schnelleren Zugriffsgeschwindigkeiten sich überhaupt lohnen.

      Funktionsweise
      Um eigene Informationen (bevorzugt Integer) im VRAM ablegen zu können, greife ich auf folgenden Trick zurück:
      Der Integer wird als Pixel dargestellt. Der Game Maker kann Pixel in einem Surface speichern. Ein Surface liegt im VRAM -> Jackpot!

      An dieser stelle ist mal dahingestellt, ob die Umrechnung nun die tatsächliche Zeitersparnis des Zugriffs wieder gutmacht.


      Theoretische Anwendungsmöglichkeiten sind: Poligone/Vertexe (3D Modelle) o.Ä.
      Da RGB nur 256^3 Farben darstellen kann, darf der Integer auch nicht 256^3 überschreiten! Floats werden nach meinen Kenntnissen im GM gerundet.

      Zuerst wird der Integer in einen rgb-Wert umgewandelt:

      GML-Quellcode

      1. /*int2rgb
      2. Script converts any integer below 256^3 into an three values: r, g, b
      3. by domis4, thanks to math.stackexchange.com for the formular.
      4. */
      5. var integer, color;
      6. integer = argument0;
      7. r = integer div (256*256);
      8. g = integer div 256 mod (256*256);
      9. b = integer mod 256;
      10. color = ds_list_create();
      11. ds_list_add(color, r);
      12. ds_list_add(color, g);
      13. ds_list_add(color, b);
      14. return color;
      Alles anzeigen


      die Rückrechnung von rgb zu integer:

      GML-Quellcode

      1. /*rgb2int
      2. Script converts values r, g, b into integer
      3. by domis4, thanks to math.stackexchange.com for the formular.
      4. */
      5. var integer, r, g, b;
      6. r = argument0;
      7. g = argument1;
      8. b = argument2;
      9. integer = 256*256 * r + 256 * g + b;
      10. return integer;
      Alles anzeigen


      Beispiel eines Speicher und Ladevorganges:

      GML-Quellcode

      1. integer = 1958; //integer to store in surface
      2. show_message("integer: "+string(integer)) //output integer
      3. color = int2rgb(integer); //create rgb color from integer
      4. w = 1; //width of surface
      5. h = 1; //height of surface
      6. r = ds_list_find_value(color, 0); //save red from integer
      7. g = ds_list_find_value(color, 1); //save green from integer
      8. b = ds_list_find_value(color, 2); //save blue from integer
      9. show_message("red: "+string(r)+string(" green: ")+string(g)+string(" blue: ")+string(b));
      10. pixel = make_color_rgb(r, g, b); //create pixel from integer colors
      11. surface = surface_create(w, h); //create surface
      12. surface_set_target(surface); //target surface
      13. draw_point_color(0, 0, pixel); //draw pixel to surface
      14. surface_reset_target(); //reset target
      15. getpixel = surface_getpixel(surface,0,0); //get pixel from surface
      16. r_new = color_get_red(getpixel); //get red from pixel
      17. g_new = color_get_green(getpixel); //get green from pixel
      18. b_new = color_get_blue(getpixel); //get blue from pixel
      19. show_message("surface red: "+string(r_new)+string(" green: ")+string(g)+string(" blue: ")+string(b));
      20. newinteger = rgb2int(r_new, g_new, b_new);
      21. show_message("integer from surface: "+string(newinteger));
      Alles anzeigen


      Ein Beispielprojekt (GM8):
      vramtest.zip
      speichert einen Integer in den VRAM und lädt ihn wieder heraus.

      Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von domis4 ()

    • Ich finde das eine sehr interessante Idee und auch die Umsetzung gefällt mir :)
      Für welche Anwendungszwecke hast du das angedacht? Aus deiner Beschreibung liest es sich als wenn du Daten zur Laufzeit abspeichern und laden möchtest? Sofern sich an der Funktion nichts geändert hat ist get_pixel nicht wirklich echtzeitfähig. Hast du da was gemerkt?
    • Vorsicht, Laufzeit != Echtzeit. Zeug zu einzelnen Zeitpunkten (nicht in jedem Frame aufs neue) im Videospeicher zwischenzuspeichern macht in der Praxis echt Sinn; da gibt's z.B. im aktuellen OpenGL-Standard die Vertex Buffer Objects und -Arrays, die es dir erlauben einen ganzen Batzen Polygone samt Texturkoordinaten und Normalvektoren auf Hardwareseite zwischenzuspeichern und dann einfach nur noch an verschiedenen Positionen zu rendern - quasi vorgebacken.
      Aber das ist, wie auch in diesem Beispiel, nicht nur auf Grafik beschränkt. Die Hardware kann zwar nur Vektoren und Matrizen behandeln, aber die müssen nicht unbedingt Objekte repräsentieren, die dir auf den Schirm gerendert werde sollen - du kannst den Output eines Shaders auch wieder auf Softwareseite zurückholen und so interpretieren, wie du's grade brauchst; das machen sich z.B. Bitcoin-Miner zunutze.
      Davon, wie sinnvoll diese Nutzung der Grafikkarte im GM ist, hab ich zu wenig Ahnung, dafür hab ich zu lang nix mehr im GM gemacht. Wenn es aber keine Beschränkungen darin gibt, was du in Shadern machen kannst, könnte man vermutlich ein paar spaßige Sachen treiben.
    • @HIM666 Danke für dein Feedback,
      leider hab ich keine großen Geschwindikeitstests durchgeführt, sondern lediglich den Input/Output validiert. Wäre natürlich ein Super-GAU, wenn die getpixel Funktionen langsam sind.

      Letzten Samstag hab ich mich damit befasst, kurz darauf ist mir die Idee beim durchforsten einiger 3D-Modellskripte gekommen. Ich gehe mal davon aus, dass größere AAA Engines auch direkt auf den VRAM zugreifen (Zumindest Texturen). Wäre doch toll, hochauflösende 3D Modelle schnell zum Drawen bereit zu haben. Das allerdings nur, wenn die Draw-Funktionen direkt den VRAM begünstigen würden. Meine Funktionen kopieren letzten Endes leider nur vom RAM zum VRAM zurück zum RAM. Wenn jedoch mal die Brücke zwischen Draw-Funktionen und VRAM da wäre, dürften die Zugriffsgeschwindigkeiten wirklich enorm verbessern. Aber da wäre wohl OpenGL oder Direct X wieder gefragt.
    • getpixel ist eine extrem langsame Funktion, wodurch der gesamte Geschwindigkeitsgewinn nutzlos ist. In der Regel sind das Buffer die das erledigen, wobei viele davon einfach den RAM und den VRAM belegen und diese dann Synchroniesieren. Hier mal unnützes Wissen, für Buffertypen:
      Ressourcen Nutzung
      Default
      Dynamic
      Imutable
      Staging
      GPU-Lesen
      ja
      ja
      ja
      ja
      GPU-Schreiben
      ja


      ja
      CPU-Lesen



      ja
      CPU-Schreiben

      ja

      ja

      Im Game Maker kann man die Buffertypen aber nicht direkt angeben, deswegen unnützes Wissen.

    • Was genau ist der Sinn davon?
      Zuerst Daten vom RAM in den VRAM zu kopieren, um sie später aus dem VRAM wieder zurück in den RAM zu kopieren? Irgendwie sinnlos oder?
      Der VRAM ist für die GPU gedacht. Den VRAM als RAM Ersatz zu verwenden ist absoluter Quatsch, da die Daten zwischen den beiden Speichern kopiert werden müssen, was u.U. extrem lange dauert.

      Sinn macht das nur bei Texturen und Vertex Daten (Mesh, Normalen, usw), die ja normalerweise statisch sind (Vertextransformationen ("Bones") laufen soweit ich weiß über Shader).
      Texturen werden im GM mit Sicherheit schon bei Programmstart in den VRAM kopiert. Wie es mit Vertex Buffern aussieht bin ich mir nicht sicher. Es könnte sein, dass die draw_primitive Funktionen intern VBOs verwenden.
      Wenn man mehr Kontrolle über sowas braucht, sollte man zu einer anderen Sprache wechseln, weil der GM einfach nicht für komplexere Sachen geeignet ist.

      Zu deinem Code:

      Kritisch wird es bei dir bereits an dem Punkt, wo du Daten in RGB Werte konvertieren willst.
      Ich gehe jetzt mal davon aus, dass der GM intern für Ganzzahlen 32 bit signed integers verwendet. Das heißt es können positive Werte kleiner 2^16 gespeichert werden. Alle Werte über 2^16 werden als negativ interpretiert. Und hier ist auch schon das Problem: was passiert wenn du negative Werte verwendest? Da das 4. Byte rausfliegt, geht das Vorzeichen, sowie alle restlichen Daten des 4. Bytes flöten und du bekommst am Ende etwas ganz anderes heraus.

      Eine Liste mit den RGB Werten zurück zu geben ist auch nicht gerade optimal. Warum gibst du nicht gleich in int2rgb den Pixel zurück? Damit ersparst du dir die Liste und damit auch das Problem, zu vergessen sie mit ds_list_destroy wieder freizugeben (was du hast).

      Außerdem, wie andere schon gesagt haben, ist getpixel stink langsam, was vermutlich daran liegt, dass die Pixeldaten erst aus dem VRAM gelesen werden müssen. Das zeigt auch gleich wieder, dass dein Vorhaben hier eher unnütz ist.
    • das war aber nicht zufällig von der letzten Diskussion wo es darum ging Dinge im Vram anstatt des Rams abzulegen wo ich sagte das wäre vermutlich nicht Performanz genug trotz höher getaktetem GDDR Speicher? aber danke das du dir die Mühe machst was ist so dein Feedback im bezug auf geschwindigkeit?
    • Dass das ganze ineffizient ist, war mir von Anfang an klar. Ich habe im ersten Beitrag hoffentlich genügend darauf hingewiesen.

      Die Machbarkeit ist jedoch möglich und das Ergebnis wollte ich euch nicht vorenthalten. Danke auf jeden Fall für die ganzen Rückmeldungen,
      immerhin kann ich fuexline beruhigen, da das ganze nicht viel Zeit in Anspruch genommen hat. Wie bereits oben erwähnt, habe ich keinerlei Tests bzgl. der Geschwindigkeit durchgeführt, diese werden aber auf definitiv langsamer ausfallen, als würde man den Schritt weglassen (Weil RAM -> VRAM -> RAM).

      Viel interessanter ist da Ih³s Shader Ansatz, da ich aber Studio seit der Installation nicht angefasst habe, werd ich mich damit eher weniger in absehbarer Zeit mit befassen.
    • -$Marvin$- schrieb:

      Was genau ist der Sinn davon?
      Zuerst Daten vom RAM in den VRAM zu kopieren, um sie später aus dem VRAM wieder zurück in den RAM zu kopieren? Irgendwie sinnlos oder?
      Der VRAM ist für die GPU gedacht. Den VRAM als RAM Ersatz zu verwenden ist absoluter Quatsch, da die Daten zwischen den beiden Speichern kopiert werden müssen, was u.U. extrem lange dauert.

      This. Die Daten rüberzuschaufeln dauert viel zu lange. Wenn man z.B. CUDA benutzt, um Berechnungen auf der Grafikkarte auszuführen, lernt man sehr schnell, dass sich das erst lohnt, wenn man sehr viel massiv parallel berechnet; und selbst dann wird man häufig doch davon überrascht, wie wenig Zeit man spart.
      Außerdem rechnet der GM nicht auf der Grafikkarte, solange du keine Shader benutzt. Zu jeder Operation werden die Daten also wieder zurück in den RAM geholt (außer, der GM hat noch eine Kopie im RAM - was nicht unwahrscheinlich ist). Die einzige Ausnahme sind, wie du korrekt herausgefunden hast, Surfaces. Diese existieren tatsächlich ausschließlich im VRAM.

      -$Marvin$- schrieb:

      Sinn macht das nur bei Texturen und Vertex Daten (Mesh, Normalen, usw), die ja normalerweise statisch sind (Vertextransformationen ("Bones") laufen soweit ich weiß über Shader).
      Texturen werden im GM mit Sicherheit schon bei Programmstart in den VRAM kopiert. Wie es mit Vertex Buffern aussieht bin ich mir nicht sicher. Es könnte sein, dass die draw_primitive Funktionen intern VBOs verwenden.
      Wenn man mehr Kontrolle über sowas braucht, sollte man zu einer anderen Sprache wechseln, weil der GM einfach nicht für komplexere Sachen geeignet ist.

      Texturen (also die Dinger, die der GM aus den ganzen Sprites und Backgrounds erzeugt) landen erstmal nicht zwingenderweise im VRAM. Dies kann man schön beobachten, wenn man sehr große Hintergründe verwendet und auf einem mobilen Gerät arbeitet. Die Framerate bricht dann total ein, da bei jedem Draw-Zyklus Texturen vom RAM in den VRAM hin- und hergeschoben werden.

      VBOs sind (und Texturen sind da nicht viel anders) Teil von OpenGLs schwarzer Magie. OpenGL kümmert sich selbst darum, diese Dinger in den VRAM zu kopieren oder halt auch nicht. Man kann Buffer unterschiedlich klassifizieren und dieses Verhalten damit dezent beeinflussen, aber echte Kontrolle hat man darüber nie. Das hat man erst mit Mantle/Vulcan.

      Wenn du innerhalb des GMs wirklich im Hinblick auf die Grafikkarte optimieren willst, solltest du deine Sprites/Backgrounds sehr umsichtig in Texturen gruppieren, damit es beim normalen Spielen so selten wie möglich zu einem Swap der Texturen kommt.
      Ansonsten könntest du natürlich ein paar Berechnungen über Shader auslagern, aber ich halte die Anwendungsfälle dafür für sehr begrenzt, wenn sie denn überhaupt existieren (wenn man die zusätzlichen Einschränkungen des GMs bedenkt).

      Wenn du deine Neugier aber mal befriedigen willst, kann ich dir nur empfehlen, einmal CUDA ausprobieren oder dich zumindest ein wenig einzulesen.