Laden...

Schnelle GDI(+) Grafik - wie? [Parallax Scrolling]

Erstellt von egrath vor 17 Jahren Letzter Beitrag vor 14 Jahren 26.135 Views
Information von herbivore vor 15 Jahren

Dies ist ein Thread, auf den aus der FAQ verwiesen wird. Bitte keine weitere Diskussion, sondern nur wichtige Ergänzungen und diese bitte knapp und präzise. Vielen Dank!

egrath Themenstarter:in
871 Beiträge seit 2005
vor 17 Jahren
Schnelle GDI(+) Grafik - wie? [Parallax Scrolling]

Hallo,

ich beschäftige mich derzeit mit verschiedenen (veralteten^^) Grafikdarstellungen wie beispielsweise Parallax Scrolling.

Dabei habe ich allerdings dass Problem dass GDI+ wesentlich zu langsam ist um mehrere sich gegeneinander verschiebene Grafikebenen darzustellen. GDI+ ist ja nicht direkt HW beschleunigt (was ich aber gesehen habe sind verschiedene GDI+ Methoden nur Wrapper für die nativen GDI Funktionen).

So nun zu meiner eigentlichen Frage:

Wie kann ich unter Windows mit .NET extrem performante 2D Grafik darstellen? Mit methodiken wie BackBuffering (Daten in Bitmap schreiben und erst dann auf den Screen schreiben), etc. habe ich es schon versucht, ist aber trotzdem zu langsam um mind. 25 Frames/sec darzustellen. Dass das ganze mit DirectX möglich ist, ist mir bewusst, allerdings möchte ich davon gerne absehen da ich eine persönliche abneigung gegen diese Schnittstelle habe.

Wie haben das Windows Spiele in Pre-DirectX Zeiten geschafft rasante Grafik darzustellen? Unter Windows gibts ja meines Wissens nur GDI und DX, also muss (sollte) es GDI Methodiken für schnelle Darstellung geben.

Danke und Grüsse, Egon

Gelöschter Account
vor 17 Jahren

eine möglichkeit wäre noch opengl
ansonsten bietet sich noch dieser thread an

49.485 Beiträge seit 2005
vor 17 Jahren

Hallo egrath,

hm, also erstml denke ich, dass man es auf aktuellen Rechnern mit aktuellen Grafikkarten auch mit GDI schaffen müsste, einigermaßen ruckelfreie 2D-Jump-'n'-Run-Grafik (darauf läuft es ja wohl hinaus) hinzubekommen.

Wie kann ich unter Windows mit .NET extrem performante 2D Grafik darstellen? Zeichnen Optimieren / Schnelles zeichnen

Wie haben das Windows Spiele in Pre-DirectX Zeiten geschafft rasante Grafik darzustellen?

Mit handoptimiertem Assemblercode und direktem Zugriff auf den Bildschirmspeicher.

herbivore

egrath Themenstarter:in
871 Beiträge seit 2005
vor 17 Jahren

Hallo,

erstmal danke für die Links - die werd ich mir mal genauer ansehen ob ich damit die gewünschte Leistung erziele. Wenn nicht werd ich mich wohl oder übel mit 2D unter DirectX anfreunden müssen.

Es geht bei meinem Projekt um ein Jump'n'Run im Stil von Turrican oder Shadow of the Beast.

Grüsse, Egon

egrath Themenstarter:in
871 Beiträge seit 2005
vor 17 Jahren

Hallo nochmal,

so ich hab mal etwas experimentiert und bin zum schluss gekommen, dass sich GDI nicht für solche Dinge eignet. Grund hierfür sind schlicht und simpel der Fakt dass ich es nicht in ansprechender Performance hinbekomme dass Parallaxscrolling mit 4 Layer, die Spielfigur und deren Aktionen sauber funktionieren. Werd also wohl oder übel DX lernen müssen ....

Bezüglich Spiele unter Windows habe ich auch etwas recherchiert: In Pre-DX Zeiten gab es unter den NT-basierten Systemen keine Spiele welche mit GDI schnelle Grafik implementierten sondern nur mittels OpenGL. Unter den DOS-basierenden Systemen welche noch den direkten Zugriff auf die Hardware erlauben, wurde es wie Herbivore oben beschrieb mittels direkten Zugriff auf den Videospeicher realisiert.

Einige Referenzen:
Direct X History
GDI Overview

Grüsse, Egon

5.941 Beiträge seit 2005
vor 17 Jahren

Hallo Egon

Original von egrath
Werd also wohl oder übel DX lernen müssen ....

Wieso verwendest du nicht OpenGL?
Habe per Zufall noch entdeckt dass du das TAO Framework ja bereits kennst (OpenGL CSGL - Transparenz/Alpha/durchsichtig).
Scheint eine gute Sache zu sein.

Ich versuche mich im Moment auch an einem Spiel, dabei habe ich mich für Managed DirectX 9 entschieden.
Wieso? Kann ich nicht genau sagen, irgendwie geht es leicht von der Hand und soll scheinbar die performanteste Möglichkeit, wenn man nur für Windows entwickeln möchte.

Gruss Peter

--
Microsoft MVP - Visual Developer ASP / ASP.NET, Switzerland 2007 - 2011

egrath Themenstarter:in
871 Beiträge seit 2005
vor 17 Jahren

Original von Peter Bucher
Wieso verwendest du nicht OpenGL?

Hallo Peter,

weil ich mich zwangsläufig ohnehin mit DX beschäftigen muss wegen der Ansteuerung von Joysticks, Soundausgabe, etc. Auch wenn es andere Librarys dafür gibt (tao beherrscht ja z.B. openAL) - ich hätt lieber gerne alles aus einer (M$)-Hand 😉

Grüsse, Egon

1.815 Beiträge seit 2005
vor 17 Jahren

Hallo!

Bzgl. der Performance ist evtl. noch zu beachten. dass VS zumindest im Debug-Modus die Anwendung teilweise ziemlich ausbremst, wie's im Release-Modus ist, weis' ich nicht. Startet man dieselbe Anwendung ohne VS, läuft sie teilweise erheblich schneller ab (gerade bei Schleifen).

Nobody is perfect. I'm sad, i'm not nobody 🙁

Gelöschter Account
vor 17 Jahren

--> weil im releasemodus keine debuginfos gebraucht werden und daher nicht mehr bremst...

was aber bezüglich spieleprogrammierung noch interessant wäre:
XNA game studio
(auf directx basierend)
downloadseite auf msdn

5.941 Beiträge seit 2005
vor 17 Jahren

Hallo zusammen

Original von egrath
weil ich mich zwangsläufig ohnehin mit DX beschäftigen muss wegen der Ansteuerung von Joysticks, Soundausgabe, etc. Auch wenn es andere Librarys dafür gibt (tao beherrscht ja z.B. openAL) - ich hätt lieber gerne alles aus einer (M$)-Hand 😉

Okay, das sind wirklich gut Argumente.
Und MDX ist sicher auch wesentlich angenehmer zum arbeiten, kein Kampf mit 100 Konstanten~

Original von tom-essen
Bzgl. der Performance ist evtl. noch zu beachten. dass VS zumindest im Debug-Modus die Anwendung teilweise ziemlich ausbremst, wie's im Release-Modus ist, weis' ich nicht.

Im Release Modus ist die Performance ziemlich schwankend, richtig läufts erst, wenn man die Antwendung bzw. das Spiel ohne die IDE startet.

Gruss Peter

--
Microsoft MVP - Visual Developer ASP / ASP.NET, Switzerland 2007 - 2011

Information von herbivore vor 15 Jahren

Dies ist ein Thread, auf den aus der FAQ verwiesen wird. Bitte keine weitere Diskussion, sondern nur wichtige Ergänzungen und diese bitte knapp und präzise. Vielen Dank!

2.921 Beiträge seit 2005
vor 17 Jahren

@Egrath:

Hi Du. 🙂 ICh habe mal (innerhalb von 10 min.) mir dieses Beispiel zurechtgemacht:

Es zeigt zumindest ansatzweise, wie es in C# möglich wäre, mit reinem GDI ein Parallax Scrolling zu realisieren, wie man sieht sind auch schon "Tiles" angedacht, aber noch nicht wirklich realisiert.

Ausserdem sieht man noch viele Fehler, die sich aber noch schnell ausmerzen liessen, wenn Du doch noch in diese Richtung interessiert bist, sag Bescheid.

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

2.921 Beiträge seit 2005
vor 17 Jahren

ein etwas komplexeres immer noch sehr unvollständiges fehlerhaftes Parallax Scrolling:

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

2.921 Beiträge seit 2005
vor 17 Jahren

Undhier noch ein Screenshot dazu:

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

C
1.214 Beiträge seit 2006
vor 17 Jahren

Hallo zusammen,

ich hab noch nie versucht ein Spiel oder eine Animation mit Gdi zu realisieren, aber ich wollte zumindest erwähnen, dass ich das nicht für ausgeschlossen halte. Ich habe vor etlichen Jahren auf einem 400 MHZ Rechner (!!!) mit BitBlt rumgespielt, und die Zeit, die benötigt wurde, um eine bildschrimfühlende Bitmap auf den Bildschirm zu zeichnen lag bei etwa 30 ms. D.h., da wären unter Umständen 30 Frames/Sekunde auf einem uralten 400 MHZ Rechner möglich. Natürlich bräuchte man noch Zeit, um die Bilder aufzubauen. So, das wollt ich nur mal erwähnt haben.

egrath Themenstarter:in
871 Beiträge seit 2005
vor 17 Jahren

Hallo dr4g0n76,

Danke für dein Beispiel. Bei ungefähr der gleichen Performance war ich bei meinen letzten GDI+ Tests zu dem Thema auch angelangt. Ich bin dann allerdings für das ganze auf SDL umgestiegen, weil es doch wesentlich performanter war (ist).

Aber nur aus reinem Interesse werd ich das Thema hier mit GDI noch etwas weiter verfolgen da Du mir jetzt wieder einen gewissen Ansporn gegeben hast 😉

Danke und Grüsse, Egon

egrath Themenstarter:in
871 Beiträge seit 2005
vor 17 Jahren

Hi,

so ich hab mich mit dem ganzen noch mal etwas gespielt und hab eine erkenntnis erlangt die ich nicht für möglich hielt: GDI+ ist eigentlich gar nicht so langsam wie das vielleicht vielerorts dargestellt wird - natürlich nur wenn man etwas Programmieraufwand nicht scheut.

Das gesamte Problem der schnellen Grafikausgabe reduziert sich im Endeffekt auf genau einen Punkt:

*) Wie schaffe ich es, meine Daten vom Back-Buffer schnell in die Grafikkarte zu bekommen?

Dazu habe ich einige kleine Tests angestellt und habe festgestellt dass es teilweise gravierende Unterschiede in der Kopierdauer gibt wenn man einfach die erstbeste Methode benutzt. In der Grafik im Anhang findet sich eine Auflistung der getesteten Methoden - als Datenblock wurde die Grösse einer Grafik mit der Auflösung 1600x1200x32 benutzt.

Der Quellcode des Testprogramms:


using System;
using System.Runtime.InteropServices;
using System.Diagnostics;

namespace ConsoleApplication1
{
    public class Program
    {
        static void Main( string[] args )
        {
            byte[] srcArray = new byte[7680000];      // Size of a Image with 1600x1200x32
            byte[] dstArray = new byte[7680000];      // same

            Stopwatch sw = new Stopwatch();
            sw.Start();

            // Copy with .NET Methods
            sw.Start();
            Array.Copy( srcArray, dstArray, dstArray.Length );
            sw.Stop();
            Console.Out.WriteLine( "COPY-dotnet-1         : {0}", sw.Elapsed.ToString() );

            // Copy with another .NET Method
            sw.Reset();
            sw.Start();
            Buffer.BlockCopy( srcArray, 0, dstArray, 0, dstArray.Length );
            sw.Stop();
            Console.Out.WriteLine( "COPY-dotnet-2         : {0}", sw.Elapsed.ToString() );

            // Copy with Native Method
            sw.Reset();
            sw.Start();
            unsafe
            {
                fixed( byte* ptrSrc = srcArray, ptrDst = dstArray )
                {
                    Win32Native.CopyMemory( ptrSrc, ptrDst, dstArray.Length );
                }
            }
            sw.Stop();
            Console.Out.WriteLine( "COPY-native           : {0}", sw.Elapsed.ToString() );

            // Copy with unsafe code only
            sw.Reset();
            sw.Start();
            unsafe
            {
                fixed( byte* ptrSrc = srcArray, ptrDst = dstArray )
                {
                    for( int index = 0; index < dstArray.Length; index ++ )
                    {
                        *( ptrDst + index ) = *( ptrSrc + index );
                    }
                }
            }
            sw.Stop();
            Console.Out.WriteLine( "COPY-unsafe           : {0}", sw.Elapsed.ToString() );

            // Copy with Serial
            sw.Reset();
            sw.Start();
            unsafe
            {
                fixed( byte* ptrSrc = srcArray, ptrDst = dstArray )
                {
                    byte* to = ptrDst;
                    byte* from = ptrSrc;
                    int count = srcArray.Length;

                    do
                    {
                        *to++ = *from++;
                    } while( --count > 0 );
                }
            }
            sw.Stop();
            Console.Out.WriteLine( "COPY-serial             : {0}", sw.Elapsed.ToString());
        }
    }

    internal class Win32Native
    {
        [DllImport( "kernel32.dll" )]
        unsafe public static extern void CopyMemory( byte* src, byte* dst, int length );
    }
}

Ich kann gleich vorweg sagen, dass sich die Native Kernel Funktion "CopyMemory" und Buffer.BlockCopy ein heisses Rennen liefern, das auf unterschiedlichen Rechnern unterschiedlich schnell ist (aber nur marginal, mal der eine mal der andere schneller).

Zur Zeit ist es mir mit einer Testapplikation schon möglich, schnelle GDI Grafik mit 1600x1200 ohne Ruckler und Flackern darzustellen - das entsprechende Programm liefere ich bald nach, ich möchte es noch fertig machen damit einem nicht alles Hochkommt wenn man sich den Source ansieht.

Grüsse, Egon

2.921 Beiträge seit 2005
vor 17 Jahren

Interessante Auflistung, ich habe natürlich wie Du Dir sicher vorstellen kannst, auch schon einige interessante versuche in diese Richtung gemacht.

Auch zusätzlich mit Erzeugung von Binary Images (NGen) usw.

Vielleicht kann ich ja hiermit noch anmerken, dass ich gerade den Artikel hier neu schreibe. Er soll ja gut werden. Das war damals ein Schnellschuss.

Ausserdem wenn man jetzt noch guckt, welcher Byte-Code erzeugt wird, um das meiste aus Schleifen auszulagern, kann man das ja, dann hat man wohl wirklich alles ausgeschöpft.

Es sei denn, man kann duch geschickte Umrechnungen/Umstellungen an manchen Stellen auf Schleifen verzichten und Formeln benutzen.

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

egrath Themenstarter:in
871 Beiträge seit 2005
vor 17 Jahren

Hallo,

so ich hab mein Demo jetzt fertig. Folgende Features:

*) Unlimitierte Anzahl von Ebenen möglich
*) Unterschiedliche Geschwindigkeit der einzelnen Ebenen
*) Unterschiedliche Richtung der einzelnen Ebenen

Im Demo sind vier Ebenen drinnen die alle nach links scrollen - so als ob man von einem Zug aus auf die Landschaft sieht.

@dr4g0n76: Es gibt sicherlich nocht viele Optimierungsmöglichkeiten die man ausschöpfen könnte, auch wenn diese mit etwas Arbeit verbunden sind. Eventuell entwickelt sich der Thread ja zu einer Orgie für GDI+ Optimierungen und was man daraus wirklich rausholen kann (würde mich persönlich freuen, da man den Mythos des langsamen GDI+ damit ausräumen kann)

Grüsse, Egon

egrath Themenstarter:in
871 Beiträge seit 2005
vor 17 Jahren
... und der Source

QUellcode für das ganze.

/edit: Die Executables sind jetzt auch im Archiv
/edit: Neues Update mit einigen Performance verbesserungen
/edit: FPS Anzeige in Titlebar eingebaut

T
73 Beiträge seit 2004
vor 17 Jahren

Hallo,

tatsächlich würd ich aus performancegründen nur mit Managed DX(9) arbeiten. hier gibts die möglichkeit mit managed direct draw reine 2D blits zu machen die recht hardwarenah und ziemlich schnell sind. einen haken hat die sache allerdings. die schnittstelle ist bei MS als veraltet geflaggt und wird nicht mehr unterstützt, d.h. nur noch in DX9 vorhanden. unter vista mit DX10 gehts wohl nicht mehr. (mal testen...??)

ich arbeite gerade an einer 2d lib dafür die ich bereitstellen könnte...

-t

5.941 Beiträge seit 2005
vor 17 Jahren

Hallo Tomschrot

Wie du schon sagtest, DirectDraw ist "veraltet" und wird nicht mehr weiterentwickelt.
Es ist aber möglich, mit Direct3D für 2D Applikationen zu arbeiten, 2.5D ist das Buzzword dazu 🙂
Man hat so alle Vorteile der 3D beschleunigten Grafik, auf jeden Fall zu empfehlen.

Original von tomschrot
ich arbeite gerade an einer 2d lib dafür die ich bereitstellen könnte...

Ich wäre interessiert, mal da reinzuschauen.

Gruss Peter

--
Microsoft MVP - Visual Developer ASP / ASP.NET, Switzerland 2007 - 2011

egrath Themenstarter:in
871 Beiträge seit 2005
vor 17 Jahren

Hallo,

natürlich wäre es eine möglichkeit DirectX zu verwenden (oder eine auf DX aufsetzende 2D Lib wie z.B. SDL). Für reale Applikationen wäre das sicherlich die Variante die man benutzen sollte.

Das ganze hat sich für mich persönlich aber eher zu einem Proof of Concept entwickelt, da ich einfach sehen will was sich aus GDI so alles rausholen lässt. Glaube allerdings dass sich mit dem letzten gezeigten so ziemlich das Ende der Fahnenstange erreicht ist, da das Blitting einfach eine gewisse Zeit beansprucht und es unter GDI leider nicht die möglichkeit des Page Flippings gibt. (Oder irre ich hier?)

Grüsse, Egon

5.299 Beiträge seit 2008
vor 14 Jahren

Ich hätte noch ne Optimierung für Lockbits auf Lager.
Lockbits kopiert glaub die bitmap-Daten aus der Bitmap raus, und UnlockBits kopiertse wieder zurück, oder sowas.

Die Optimierung besteht darin, einen Bitmap-Konstruktor zu verwenden, bei dem man den Scan0-Pointer angeben kann.
Man pinnt ein Array, und verwendet den gepinnten Pointer als Scan0.
Das Array sind dann direkt die Pixel der Bitmap - glaub DirectX kann da auch nicht schneller Pixel verändern.
Sone gepinnte Bitmap würde dann nicht disposed, sondern immer an ihren Pixeln manipuliert. Dann wird der Flaschenhals das Graphics.DrawImage() sein, welches diese Bitmap auf den bildschirm bringt.
Ist ein bischen gemein zum GC, weil der muß um so gepinnte Arrays quasi drumherum aufräumen.

Was direkt dazu ausgearbeitetes habich aber nicht, weil ich auch find, für bewegte Szenen soll man die entsprechende Technologie nehmen.

Aber in Convolution-Filter ,inne Pixelhelper-Klasse ist dieser bitmap-Konstruktor in Aktion.
Ich reize das da aber nicht aus, weil ich kein dauerhaft gepinntes Array im Speicher haben wollte.

Der frühe Apfel fängt den Wurm.