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

  • »Johannes Schneider« ist der Autor dieses Themas

Beiträge: 103

Beruf: Chemiestudent

  • Private Nachricht senden

1

20.07.2014, 22:26

DX11 Dynamische Vertexbuffer richtig ändern

Nabend.

Ich wollte ein Programm basteln welches alle Sekunde zwischen 1 und 50 Vertices und deren Positionen/Farbe zufällig generiert und in den Vertexbuffer schreibt.
Leider "flackert" die Anzeige: Mal sieht man das Standardface, welches ich zu Beginn übergene, mal sehe ich nichts.

Das ist die idealte Gelegenheit, Cryteks neue Open-Source Software Renderdoc zu testen, da Microsoft PIX (DX SDK) in der Version 2010 abstürzt.

Los gehts!
Zunächst erstelle ich mir gemäß Microsofts Vorgehensweise einen dynamischen Vertexbuffer:

C-/C++-Quelltext

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
35
36
37
38
39
40
41
42
43
44
45
46
47
// Vertexbuffer initialisieren
RESULT Init(ID3D11Device* device, ID3D11DeviceContext* context) {

    /*
        // Vertexstruktur
        struct SVertexType {
            D3DXVECTOR3 position;
            D3DXVECTOR4 color;
        };
    */

    SVertexType OurVertices[] =
    {
        {D3DXVECTOR3(0.0f, 0.5f, 0.0f), D3DXCOLOR(1.0f, 0.0f, 0.0f, 1.0f)},
        {D3DXVECTOR3(0.45f, -0.5, 0.0f), D3DXCOLOR(0.0f, 1.0f, 0.0f, 1.0f)},
        {D3DXVECTOR3(-0.45f, -0.5f, 0.0f), D3DXCOLOR(0.0f, 0.0f, 1.0f, 1.0f)}
    };

    // Speicher säubern
    ZeroMemory(&m_BufferDesc, sizeof(D3D11_BUFFER_DESC));

    // Dynamischen Vertexpuffer erstellen
    m_BufferDesc.Usage = D3D11_USAGE_DYNAMIC;           
    m_BufferDesc.ByteWidth = sizeof(SVertexType) * 3; // 3?
    m_BufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
    m_BufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;

    // Puffer erstellen
    // EXIT_DEBUG ist ein Debugmakro meiner Logdateiklasse. Das Erstellen funktioniert
    EXIT_DEBUG( device->CreateBuffer(&m_BufferDesc, NULL, &m_VertexBuffer) );
    
    // Vertexdaten kopieren
    D3D11_MAPPED_SUBRESOURCE ms;
    // Speicher säubern
    ZeroMemory(&ms, sizeof(D3D11_MAPPED_SUBRESOURCE));
    
    // Daten kopieren
    context->Map(m_VertexBuffer, NULL, D3D11_MAP_WRITE_DISCARD, NULL, &ms);
    memcpy(ms.pData, OurVertices, sizeof(OurVertices));
    context->Unmap(m_VertexBuffer, NULL);

    // Anzahl der Vertices im Puffer
    m_vertexCount = 3;

    // Allex klar
    return RESULT_SUCCESS;
}


Das ganze wird noch nicht indiziert gerendert:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
// Buffer rendern
RESULT Render(ID3D11DeviceContext* context) 
{   
    UINT stride = sizeof(SVertexType);
    UINT offset = 0;
    context->IASetVertexBuffers(0, 1, &m_VertexBuffer, &stride, &offset);
    context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

    context->Draw(m_vertexCount, 0); // stimmt das?

    // Alles klar
    return RESULT_SUCCESS;
}


Welches mir folgendes Ergebnis liefert:


(Link)


Sieht gut aus!

Jetzt sollen jede Sekunde bis zu 100 Dreiecke (bis zu 300 Vertices) generiert und an die Grafikkarte geschickt werden:

C-/C++-Quelltext

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
35
36
37
38
39
40
41
42
43
44
45
46
47
// Vertexbuffer ändern (wird jede Sekunde aufgerufen)
RESULT Change(ID3D11DeviceContext* context) 
{   
    // Zufallsgenerator initialisieren
    srand((unsigned)time(NULL));

    // Wieviele Dreiecke sollen generiert werden?
    int random = rand() % 100; // Bis zu 100 Dreiecke

    // 3 Vertices = 1 Face = 1 Dreieck...
    SVertexType* vertices = new SVertexType[random * 3 /*MAL DREI*/ ];

    // Nun die Vertices generieren
    for(int g=0; g< 3*random; g++) 
    {
        // Zufällige FLoat-Werte (?)
        #define RANDVAL float( rand() % 100 / 1000.0f)

        // Zufälliges Vertex generieren
        SVertexType tmp;
        tmp.color = D3DXVECTOR4(1.0f, 0.0f, 0.0f, 1.0f);
        tmp.position = D3DXVECTOR3(RANDVAL, RANDVAL, RANDVAL);

        // Speicher kopieren
        vertices[g] = tmp;
    }
    
    D3D11_MAPPED_SUBRESOURCE ms;
    // Speicher säubern
    ZeroMemory(&ms, sizeof(D3D11_MAPPED_SUBRESOURCE));

    // Daten kopieren
    context->Map(m_VertexBuffer, NULL, D3D11_MAP_WRITE_DISCARD, NULL, &ms);
    memcpy(ms.pData, vertices, sizeof(vertices));
    context->Unmap(m_VertexBuffer, NULL);

    // ... Müssen Vertexbuffer nicht gesperrt werden?

    // Wieviele Vertices waren das nochmal?
    m_vertexCount = 3 * random;

    // Speicher löschen!!
    delete[] vertices;

    // Alles klar
    return RESULT_SUCCESS;
}



Im Debugmodus "flackert" die Anzeige: Mal sehe ich EIN Dreieck, mal sehe ich einen schwarzen Strich.
Das liegt vielleicht an der Ausführungsgeschwindigkeit... mal sehen

In Renderdoc flackert nichts, jedoch sehe ich nach wie vor nur EIN Dreieck...
Wobei es doch bis zu 100 sein sollen...

So siehts aus:
Verdächtig ist, dass angeblich 120 Vertices (40 Dreiecke) gerendert werden, wobei doch nur 3 Vertices im Input Assembler landen...


(Link)


Screenshots bleiben einige Jahre gehostet.

Einige Vorschläge?
Danke für jegliche Hilfe!
"Das Glück des Forschers besteht nicht darin, die Wahrheit zu besitzen, sondern eine Wahrheit zu erringen. Und in diesem fortschreitendem, erfolgreichen Suchen nach der Wahrheit - darin liegt die
eigentliche Befriedigung." Max Planck

David_pb

Community-Fossil

Beiträge: 3 886

Beruf: 3D Graphics Programmer

  • Private Nachricht senden

2

20.07.2014, 23:01

Dein Vertexbuffer ist viel zu klein (m_BufferDesc.ByteWidth = sizeof(SVertexType) * 3; // 3?), außerdem kopierst du nicht den kompletten Pufferinhalt:

Quellcode

1
memcpy(ms.pData, vertices, sizeof(vertices))


Beachte, dass sizeof(vertices) hier die größe eines Zeigers liefert.

Quellcode

1
2
3
4
5
6
7
8
9
const size_t bufferSizeInBytes = random * 3 * sizeof(SVertexType);

// fill buffer with random data

if (SUCCEEDED(context->Map(m_VertexBuffer, NULL, D3D11_MAP_WRITE_DISCARD, NULL, &ms)))
{
  memcpy(ms.pData, vertices, bufferSizeInBytes);
  context->Unmap(m_VertexBuffer, NULL);
}


Und achte darauf den Vertexpuffer groß genug zu allozieren.
@D13_Dreinig

  • »Johannes Schneider« ist der Autor dieses Themas

Beiträge: 103

Beruf: Chemiestudent

  • Private Nachricht senden

3

20.07.2014, 23:14

Also muss man die Maximalgröße des Vertexbuffers beim Erstellen überdenken!
Das stand leider nicht im Tutorial.

Auf die Speichergröße wär ich nie gekommen...

So siehts aus:


(Link)

Danke.
"Das Glück des Forschers besteht nicht darin, die Wahrheit zu besitzen, sondern eine Wahrheit zu erringen. Und in diesem fortschreitendem, erfolgreichen Suchen nach der Wahrheit - darin liegt die
eigentliche Befriedigung." Max Planck

David_pb

Community-Fossil

Beiträge: 3 886

Beruf: 3D Graphics Programmer

  • Private Nachricht senden

4

21.07.2014, 08:45

Übrigens:

den dynamischen Puffer pro Frame mit "discard" zu mappen kann zu ziemlich drastischen Performanzproblemen führen, da der Puffer nicht überschrieben werden darf, wenn die Resource noch von der GPU verwendet wird. In diesem Fall findet ein 'buffer-renaming' statt, d.h. DX stellt dir einen anderen Puffer zur Verfügung. Wenn dein Puffer hinreichend klein ist (z.B. < 4 MB) dann kann es sein, dass der Puffer intern von DX etwas größer angelegt wird und bei jedem 'rename' einfach ein Offset auf den ursprünglichen Zeiger addiert wird. Schlimmer ist es, wenn der interne Speicher komplett aufgebraucht wurde, dann muss ein neuer Puffer alloziert werden und dein Programm wird kurzfristig blockiert.

Ich weiß nicht genau was du tun willst und ob Performanz tatsächlich ein wichtiges Kriterium für dich ist. Aber falls doch, oder falls du dich einfach dafür interessierst:

Es kann von Vorteil sein den Puffer selbst etwas größer zu allozieren (z.B. 3x so groß) und das Offset-Management selbst durchzufüren. D.h. jeden 3x Frame wird der Puffer 'discarded' und dazwischen mit 'MAP_WRITE_NO_OVERWRITE' gemapped. Dadurch verhinderst du (oder verminderst zumindest) das der Puffer voll läuft und komplett neu alloziert werden muss.
@D13_Dreinig

Werbeanzeige