Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

DaiFei

Frischling

  • »DaiFei« ist der Autor dieses Themas

Beiträge: 28

Wohnort: München

  • Private Nachricht senden

1

09.08.2013, 13:03

Surface mit Alphakanal auf den Backbuffer legen (schon dagewesen) ODER Warum hängt die Performance von der auf den Backbuffer zu schreibenden Farbe ab?

Hallo zusammen!

Mein Problem fängt sehr ähnlich zu anderen, hier bereits
besprochenen Surface-mit-Alphakanal-darstellen Problemen an. Und zwar habe ich
ein (einmalig in der Initialisierungsphase) selbst beschriebenes Surface
welches einfach einen fließenden Überagng von Rot nach Grün zeigt (ich habe alles einmal auf das Wichtigste beschränkt):

Quellcode

1
2
3
4
5
6
7
CreateOffscreenPlainSurface(128, 16, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, & lpSurface, NULL);

float delta = 255.0f / 127;

for (int i = 0; i < 128; i++)
   for (int j = 0; j < 16; j++)
    Pixels[j * nPitch + i] = D3DCOLOR_ARGB(0xC0 ,0xFF - FS_Round(i * delta), FS_Round(i * delta), 0x00);


Das es nun mit den vorhandenen Funktionen, welche ein
Surface auf den Backbuffer legen, nicht möglich ist, den Alphakanal zu
berücksichtigen, habe ich mir einfach selber eine geschrieben. Dies passiert
ähnlich zu anderen (gut funktionierenden) Funktionen von mir, welche direkt auf
den Backbuffer einen Punkt eine Linie oder einen Kreis zeichnen.

Innerhalb von BeginScene und EndScene und wiederum innerhalb
der (eigenen) Funktionen BeginDrawOnBackbuffer (Lock Backbuffer) und EndDrawOnBackbuffer
(Unlock Backbuffer) finden diese Methoden jeweils ihren Aufruf.

Meine ebenfalls zwischen BeginDrawOnBackbuffer() und BeginDrawOnBackbuffer()
aufgerufene Funktion zum „Auflegen“ des Surface sieht nun folgendermaßen aus:

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
void CDirect3D::CopyRectOnBackBufferAlpha(const LPDIRECT3DSURFACE9 a_lpSourceSurface, const RECT * a_lpSourceRect,
                                                            const UINT & a_uiPosX, const UINT & a_uiPosY)
{

    // Source Surface
    D3DLOCKED_RECT LockedRect;

a_lpSourceSurface->LockRect(&LockedRect, 0, D3DLOCK_READONLY);

    const int nPitch = LockedRect.Pitch / 4;

    const D3DCOLOR * lpPixels = reinterpret_cast <D3DCOLOR*> (LockedRect.pBits);

 int i = 0;
int j = 0;

 // Neuen Farbwert auf BackBuffer schreiben

    for (int x = a_lpSourceRect->left; x < a_lpSourceRect->right; x++)
    {
    for (int y = a_lpSourceRect->top; y < a_lpSourceRect->bottom; y++)
            {
        const int index = (a_uiPosY + j) * m_iPitch + (a_uiPosX + i);

        m_lpPixelsBackBuffer[index] = CalcNewColorAlpha(lpPixels[y * nPitch + x], m_lpPixelsBackBuffer[index]);

                    j++;
            }
    i++;
    j = 0;
        }

    a_lpSourceSurface->UnlockRect();
}


Die Farbwert-Berechnung:

Quellcode

1
2
3
4
5
6
7
8
9
inline const D3DCOLOR CDirect3D::CalcNewColorAlpha(const D3DCOLOR & a_colA, const D3DCOLOR & a_colB)
{
    const DWORD A_a = a_colA >> 24 & 0xFF;

    // Farbe A mit Alphakanal wird auf B gelegt
    return D3DCOLOR(D3DCOLOR_XRGB(static_cast <DWORD> ((A_a / 255.0f) * (a_colA >> 16 & 0xFF) + (1 - (A_a / 255.0f)) * (a_colB >> 16 & 0xFF)),
                                static_cast <DWORD> ((A_a / 255.0f) * (a_colA >> 8  & 0xFF) + (1 - (A_a / 255.0f)) * (a_colB >> 8  & 0xFF)),
                                static_cast <DWORD> ((A_a / 255.0f) * (a_colA       & 0xFF) + (1 - (A_a / 255.0f)) * (a_colB    & 0xFF))));
}


Nun habe ich allerdings das Problem, das ein Aufruf dieser Methode zu einem extremen Performance-Einbruch (unter 30fps, von ursprünglich
konstanten 59fps bei D3DPRESENT_INTERVAL_DEFAULT) führt, was ich mir nicht weiter erklären kann, da meine anderen Zeichenfunktionen meinen Rechner Performance-technisch absolut kalt lassen (mögen es noch so viele Kreise, Linien und Punkte sein, welche da direkt auf den Backbuffer geschrieben werden). Das eigentlich verwunderliche ist jedoch die Tatsache, dass folgende (natürlich nicht zum Ziel führende) Quellcode-Änderungen keine Performance-Einbußen mit sich führen:

Anstatt

Quellcode

1
m_lpPixelsBackBuffer[index] = CalcNewColorAlpha(lpPixels[y * nPitch + x], m_lpPixelsBackBuffer[index]);


Möglichkeit 1:

Quellcode

1
m_lpPixelsBackBuffer[index] = 0;


Möglichkeit 2:

Quellcode

1
2
D3DCOLOR color = D3DCOLOR_XRGB(0x80, 0x80, 0x80);
m_lpPixelsBackBuffer[index] = color;


Möglichkeit 3:

Quellcode

1
D3DCOLOR color = CalcNewColorAlpha(lpPixels[y * nPitch + x], m_lpPixelsBackBuffer[index]);


Diese 3 Möglichkeiten machen sich in der Performance nicht bemerkbar, lediglich das Schreiben der berechneten Farbe direkt oder indirekt (mit dem Umweg über einer temporären D3DCOLOR Variable) auf den Backbuffer geht so gar nicht.

Es scheint also nichts mit meiner Funktion zur Berechnung des neuen Farbwerts unter Berücksichtigung des Alphakanals zu tun haben, geht es doch, wenn der von dieser Funktion zurückgegebene Farbwert in einer lokalen D3DCOLOR Variable gespeichert wird. Kann es sein, dass der Backbuffer hier auf nicht konstante Farben über die gesamte Schleife hinweg allergisch reagiert? Warum funktioniert die Berechnung und Zuweisung für eine lokale D3DCOLOR Variable Performance-technisch so einwandfrei?

Letztlich geht es also lediglich darum, den Backbuffer direkt mit einem Farbwert zu beschreiben, welcher aber scheinbar in Abhängigkeit dessen mal in die Knie geht und mal nicht.

Das Surfaces vielleicht nicht unbedingt die besten Lösungen mehr sind für derartige Probleme ist mir zwar klar, dennoch wäre ich in diesem Fall sehr am Hintergrund dieses Problems interessiert.

Gibt es evtl. doch noch eine (unter Umständen von DirectX selbst bereitgestellte) bessere Lösung/Funktion (mit Surfaces)?

Vielen Dank für die Hilfe!

PS: Vielleicht sind meine folgenden Einstellungen noch hilfreich:

ADAPTER = D3DADAPTER_DEFAULT;
WINDOWED = TRUE;

DEVICE_TYPE = D3DDEVTYPE_HAL;
WIDTH = 800;
HEIGHT = 600;
BACKBUFFER_FORMAT = D3DFMT_X8R8G8B8;

DEPTHSTENCILBUFFER_FORMAT = D3DFMT_D16;
VERTEXPROCESSING = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
MULTISAMPLETYPE = D3DMULTISAMPLE_NONE;
PRESENTINTERVAL = D3DPRESENT_INTERVAL_DEFAULT;

BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

2

09.08.2013, 13:07

Lesen aus dem Backbuffer ist eine ganz schlechte Idee und mit irgendwas anderem außer einem Textur-Rendering reinschreiben zu wollen ebenfalls.
Mir ist nicht so ganz klar, warum Du nicht eine ganz normale Textur erzeugst und die mit einem Quad renderst?
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

DaiFei

Frischling

  • »DaiFei« ist der Autor dieses Themas

Beiträge: 28

Wohnort: München

  • Private Nachricht senden

3

09.08.2013, 13:17

Ich bin tatsächlich schon dabei, meine etwas angestaubten Surface- und Direkt-auf-Backbuffer-Schreib-Funktionen aufzulösen, jedoch fand ich das oben beschriebene Phänomen schon recht faszinierend respektive unerklärlich.

Schrompf

Alter Hase

Beiträge: 1 470

Wohnort: Dresden

Beruf: Softwareentwickler

  • Private Nachricht senden

4

09.08.2013, 13:19

Ich vermute ebenso, dass es am Backbuffer-Auslesen liegt. Ich habe Deine Aufzählung ehrlich gesagt nur überflogen, aber soweit ich das verstanden habe, ist die Option 3 mit Alphablending die einzige, die den vorherigen Wert des Backbuffers ausliest. Dass die Alphablending-Funktion selbst dann mit Konvertierung zu double und zurück nicht sonderlich schlau implementiert ist, dürfte da gar nicht weiter ins Gewicht fallen.

Mein Tipp dazu lautet genauso wie der von BlueCobold: schreib Deinen Kram in Texturen und render die mit Quads direkt in der Grafikkarte. Linien, Kreise usw. brauchst Du eigentlich nicht. Was Du brauchst, sind texturierte Rechtecke. Und wenn die Texturen größtenteils über mehrere Frames feststehen, umso besser.

Und nebenbei: Du kannst die Performance *nicht* an der Framerate festmachen. Stabile 59/60 fps sind nur ein Zeichen, dass Du am VSync hängst. Ohne VSync wärst Du wahrscheinlich bei tausenden von Frames pro Sekunde. Der Unterschied käme Dir dann umso drastischer vor, ist aber immernoch irrelevant :-) Die relevante Frage ist, wie lange diese eine Operation dauert. Und das kannst Du hier direkt nachmessen, weil ein Beschreiben des VideoRAMs und noch viel schlimmer ein Auslesen des VideoRAMs einen GPU Sync erfordern, der je nach GPU-Last für sich allein bereits ~100ms fressen kann.
Häuptling von Dreamworlds. Baut aktuell an nichts konkretem, weil das Vollzeitangestelltenverhältnis ihn fest im Griff hat. Baut daneben nur noch sehr selten an der Open Asset Import Library mit.

DaiFei

Frischling

  • »DaiFei« ist der Autor dieses Themas

Beiträge: 28

Wohnort: München

  • Private Nachricht senden

5

11.08.2013, 11:12

Erst mal vielen Dank für die Antworten! Ich konnte das eigentlich Problem zwar nicht lösen, jedoch erachte ich es inzwischen auch als sinnvoll, auf eine andere Methode zurückzugreifen.

Bleiben abschließend noch zwei Fragen:

Wenn ich rein in 2D arbeite, dass heißt ausschließlich Sprites und (bis jetzt noch) Surfaces verwende, spricht dann etwas dagegen mein Vorhaben mit einem Sprite anstelle einer Textur auf Vertexen zu realisieren?

Kann man allgemeingültig sagen, dass Surfaces vollständig überholt sind und nicht mehr angewendet werden sollten oder haben diese zusammen mit ihren Funktionen wie StretchRect() und co. in bestimmten Fällen nach wie vor ihre Daseinsberechtigung?

Danke!

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

6

11.08.2013, 11:42

Wenn ich rein in 2D arbeite, dass heißt ausschließlich Sprites und (bis jetzt noch) Surfaces verwende, spricht dann etwas dagegen mein Vorhaben mit einem Sprite anstelle einer Textur auf Vertexen zu realisieren?

Kann man allgemeingültig sagen, dass Surfaces vollständig überholt sind und nicht mehr angewendet werden sollten oder haben diese zusammen mit ihren Funktionen wie StretchRect() und co. in bestimmten Fällen nach wie vor ihre Daseinsberechtigung?

Mir scheint du bringst da ein paar Dinge durcheinander. Zunächst mal: Was genau verstehst du unter einem Sprite bzw. wo genau liegt der Unterschied zwischen dem was du unter einem Sprite verstehst und einer "Textur auf Vertexen"? Eine Surface ist nichts anderes als ein Speicherbereich, der Bilddaten enthält. Eine Textur beispielsweise besteht aus einer oder mehreren Surfaces...

DaiFei

Frischling

  • »DaiFei« ist der Autor dieses Themas

Beiträge: 28

Wohnort: München

  • Private Nachricht senden

7

11.08.2013, 12:01

Unter einem Sprite verstehe die Darstellung einer Textur mit Hilfe eines ID3DXSprite interface, welches für ein rein 2-dimensionales Bild hinsichtlich Transformation, also Bewegung, Rotation und Skalierung recht einfach zu handhaben ist.

Das legen einer Textur auf ein Quadrat bestehend aus zuvor erstellten 2 Dreiecken wie oben empfohlen, kommt mir zumindest (vielleicht auch aus Mangel an Erfahrung in dem Bereich) im 2-Dimensionalen etwas befremdlich vor.

Vielleicht habe ich die Frage etwas missverständlich formuliert. Ursprünglich ging es mir ja lediglich darum, eine einzige „Pixelschicht“, also ein Surface mit Alphakanal auch unter Berücksichtigung dieses Kanals auf den Backbuffer zu bringen, sprich dieses korrekt anzuzeigen.
Während ich nun einfach eine Textur mit verschiedenen Surfaces belegen kann und das vielleicht auch gar keine so schlechte Methode ist (weiß ich ehrlich gesagt nicht), beläuft sich meine Frage vielleicht viel eher dahingehend, ob Surfaces, also IDirect3DSurface9 (arbeite mit DirectX 9) noch direkt verwendet werden sollten respektive mit ihren entsprechenden Funktionen auf den Backbuffer gebracht oder sämtliche Grafiken nur noch über Texturen gehandhabt werden sollten.

An dieser Stelle vielleicht auch einfach mal die Frage, was ist denn nun der signifikante (technisch und Performance-technische) Unterschied zwischen einem Sprite, welches eine Textur anzeigt und einer Textur auf Vertexen?

Schrompf

Alter Hase

Beiträge: 1 470

Wohnort: Dresden

Beruf: Softwareentwickler

  • Private Nachricht senden

8

11.08.2013, 20:25

D3DXSprite *ist* eine Textur auf Vertizes. Die machen das intern ja genauso. Es gibt aus Hardwaresicht kein "Sprite" mehr. Es gibt nur noch Bildbereiche (Texturen sind Surfaces) und Dreiecke. Demzufolge ist alles, und zwar wirklich alles, was Du heutzutage auf dem Bildschirm siehst, mit texturierten Dreiecken entstanden. Auch Windows selbst malt alles, was Du auf dem Desktop siehst, mit texturierten Dreiecken. Evtl. mit einer Ausnahme: der Fontrenderer zeichnet seine Kurven evtl. noch mit Software und lädt das Ergebnis dann in eine der vielen Texturen, die Du auf dem Bildschirm siehst.
Häuptling von Dreamworlds. Baut aktuell an nichts konkretem, weil das Vollzeitangestelltenverhältnis ihn fest im Griff hat. Baut daneben nur noch sehr selten an der Open Asset Import Library mit.

DaiFei

Frischling

  • »DaiFei« ist der Autor dieses Themas

Beiträge: 28

Wohnort: München

  • Private Nachricht senden

9

14.08.2013, 23:48

Verstehe. Hinter beiden steckt also die gleiche Technologie.

Bleibt zuletzt noch die Frage, ob es nun stets am sinnvollsten ist mit Texturen zu arbeiten und sie mit ihren eigenen Funktionen zu rendern oder ob, wie oben schon gefragt, Surfaces mit ihren StretchRect(), UpdateSurface() etc. Funktionen noch ihre Berechtigung haben? Oder sollte man grundsätzlich die Finger von einem Zeiger auf den Backbuffer lassen?

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

10

15.08.2013, 01:13

Du scheinst der Auffassung zu sein, dass Funktionen wie StretchRect(), UpdateSurface() etc. ursprünglich mal dazu gedacht waren, Sachen auf den Bildschirm zu malen und heute nichtmehr verwendet werden oder so!? Dem ist nicht so, diese Funktionen sind im Prinzip sowas wie memcpy() für den Grafikspeicher und haben natürlich ihre Berechtigung, sie dienen einfach nur einem völlig anderem Zweck, als du vielleicht denkst. Natürlich könnte man damit effektiv auch was zeichnen, indem man einfach Bildbereiche in den BackBuffer kopiert. Und in der Tat, früher hat man Grafik auch auch so betrieben; "Blitting" lautete das Zauberwort. Aber früher heißt hier: Bevor es 3D Grafikkarten gab, lange bevor es Direct3D gab, also so in etwa irgendwann in der Frühsteinzeit. Auf moderner Hardware würde das weder besonders schnell sein, noch kann man damit besonders tolle Sachen machen. Heutzutage will man in der Regel Dinge wie Alphablending oder Rotationen haben. Kein Problem: Einfach Dreiecke mit Texturen drauf malen. Ich weiß, das klingt auf den ersten Blick wie völliger Overkill, ist es aber nicht unbedingt, im Gegenteil, denn texturierte Dreiecke sind extrem vielseitig. Es wäre wohl sehr viel mehr Overkill, zu versuchen, Hardware zu bauen, die zur Hälfte dies, zur Häfte jenes und irgendwie vielleicht auch noch ein bisschen von was anderem macht. Stattdessen baut man einfach Hardware, die eine Sache so richtig gut kann: Dreiecke rendern, sobald ich das kann, bekomm ich 2D Grafik sowieso praktisch im Vorbeigehen geschenkt dazu... ;)

Dieser Beitrag wurde bereits 11 mal editiert, zuletzt von »dot« (15.08.2013, 01:56)


Werbeanzeige