[C++] generische Inputlayout-Erstellung für verschiedene Vertex-Typen in DX11

Programmiersprachen, APIs, Bibliotheken, Open Source Engines, Debugging, Quellcode Fehler und alles was mit praktischer Programmierung zu tun hat.
Antworten
Benutzeravatar
gombolo
Establishment
Beiträge: 161
Registriert: 26.01.2011, 20:33

[C++] generische Inputlayout-Erstellung für verschiedene Vertex-Typen in DX11

Beitrag von gombolo »

Ich möchte die Definition eines InputLayouts in DX11 etwas anpassen. Meine Idee besteht darin, eine Klasse zu erstellen, die wie folgt definiert ist:

Code: Alles auswählen

template<typename VertexType>
class InputLayoutCreator
Wie man erkennen kann, beabsichtige ich, die Möglichkeit zu bieten, verschiedene Vertex-Typen zu erstellen. Hier sind zwei Beispiele:

Code: Alles auswählen

struct Vertex {
    DirectX::XMFLOAT3 position;
    DirectX::XMFLOAT4 color;
    DirectX::XMFLOAT3 normal;
    DirectX::XMFLOAT2 texCoord;
};

struct VertexPosColor {
    DirectX::XMFLOAT3 position;
    DirectX::XMFLOAT4 color;
};
Ich kann das dann wie folgt anwenden:

Code: Alles auswählen

InputLayoutCreator<Vertex> inputLayoutCreator(Engine::engine->Device.GetDevice());
InputLayoutCreator<VertexPosCol> inputLayoutCreatorPosCol(Engine::engine->Device.GetDevice());
Bis hierhin läuft alles gut. Jetzt stößt jedoch ein Problem auf. In dieser Klasse gibt es eine Funktion namens CreateInputLayout, die wie folgt aussieht:

Code: Alles auswählen

template<typename VertexType>
ID3D11InputLayout* CreateInputLayout(DWORD flags, const void* shaderBytecode, size_t bytecodeSize) {
    std::vector<D3D11_INPUT_ELEMENT_DESC> layoutElements;

    if (flags & D3DFVF_POSITION) {
        layoutElements.push_back({ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 });
    }
    if (flags & D3DFVF_COLOR) {
        layoutElements.push_back({ "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, offsetof(VertexType, color), D3D11_INPUT_PER_VERTEX_DATA, 0 });
    }
    if (flags & D3DFVF_NORMAL) {
        layoutElements.push_back({ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, offsetof(VertexType, normal), D3D11_INPUT_PER_VERTEX_DATA, 0 });
    }
    if (flags & D3DFVF_TEX1) {
        layoutElements.push_back({ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, offsetof(VertexType, texCoord), D3D11_INPUT_PER_VERTEX_DATA, 0 });
    }

    ID3D11InputLayout* inputLayout = nullptr;
    HRESULT hr = m_Device->CreateInputLayout(layoutElements.data(), layoutElements.size(), shaderBytecode, bytecodeSize, &inputLayout);
    if (FAILED(hr)) {
        Debug::Log("Fehler in Datei InputLayout.h");
    }

    return inputLayout;
}
Einige von Euch sehen vielleicht sofort mein Problem. Beim ersten Vertex funktioniert das gut, aber beim zweiten wirft der Compiler einen Fehler, weil er die Elemente 'normal' und 'texCoord' im zweiten struct VertexPosCol nicht finden kann.

Von der Idee das so wie oben beschrieben umzusetzen bin ich jetzt weg weil ich keine Lösung finden konnte, aber vielleicht hat jemand von Euch eine Idee um das flexibel zu gestallten?

Danke und VG Gombolo
Benutzeravatar
gombolo
Establishment
Beiträge: 161
Registriert: 26.01.2011, 20:33

Re: [C++] generische Inputlayout-Erstellung für verschiedene Vertex-Typen in DX11

Beitrag von gombolo »

Falls sich jemand meine Frage angetan hat, weil das Thema auch für mich nicht mehr ganz zu durchschauen ist :D

Ich mache das jetzt so:

Ich berechne den Offset individuell...naja hätte ich auch vorher machen sollen, aber wenn man zu tief in einem Thema drin ist, dann sieht man viele Sachen nicht...

Code: Alles auswählen

            size_t currentOffset = 0;

            if (flags & D3DFVF_POSITION) {
                layoutElements.push_back({ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, currentOffset, D3D11_INPUT_PER_VERTEX_DATA, 0 });
                currentOffset += sizeof(DirectX::XMFLOAT3); // Increase offset for next element

                Debug::Log("POSITION: ", currentOffset);
            }

            if (flags & D3DFVF_COLOR) {
                layoutElements.push_back({ "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, currentOffset, D3D11_INPUT_PER_VERTEX_DATA, 0 });
                currentOffset += sizeof(DirectX::XMFLOAT4); // Increase offset for next element
                Debug::Log("COLOR: ", currentOffset);
            }

            if (flags & D3DFVF_NORMAL) {
                layoutElements.push_back({ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, currentOffset, D3D11_INPUT_PER_VERTEX_DATA, 0 });
                currentOffset += sizeof(DirectX::XMFLOAT3); // Increase offset for next element
                Debug::Log("NORMAL: ", currentOffset);
            }

            if (flags & D3DFVF_TEX1) {
                layoutElements.push_back({ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, currentOffset, D3D11_INPUT_PER_VERTEX_DATA, 0 });
                currentOffset += sizeof(DirectX::XMFLOAT2); // Increase offset for next element
                Debug::Log("TEX1: ", currentOffset);
            }
Benutzeravatar
Hannes
Beiträge: 43
Registriert: 11.06.2008, 06:04

Re: [C++] generische Inputlayout-Erstellung für verschiedene Vertex-Typen in DX11

Beitrag von Hannes »

statt currentOffset kannst du glaub ich auch D3D11_APPEND_ALIGNED_ELEMENT angeben.
Siehe: https://learn.microsoft.com/en-us/windo ... ement_desc
Benutzeravatar
gombolo
Establishment
Beiträge: 161
Registriert: 26.01.2011, 20:33

Re: [C++] generische Inputlayout-Erstellung für verschiedene Vertex-Typen in DX11

Beitrag von gombolo »

stimmt..habe ich auch in der DOKU entdeckt, aber wollte es nicht in Richtung DX treiben, weil das ursprüngliche Problem ein C++-Problem war...was eigentlich auch keins war weil ich einfach zu lange am PC gesessen bin und nach einer Weile das Gehirn einfach nicht mehr mitmacht :D
Benutzeravatar
Schrompf
Moderator
Beiträge: 5042
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas
Wohnort: Dresden
Kontaktdaten:

Re: [C++] generische Inputlayout-Erstellung für verschiedene Vertex-Typen in DX11

Beitrag von Schrompf »

Ich bin allgemein nicht so glücklich mit Deiner Lösung. Was wenn Du mal die Reihenfolge von normal und color vertauschst? Was wenn Du mal zwei Texkoords brauchst? Was Du anscheinend eigentlich haben willst, ist ne echte Reflection, wie es sie in C++ noch gar nicht gibt. Sprich: Du möchtest für jedes Element in der Struktur ein bisschen Code ausführen. Das könntest Du mit C++17-Structured Bindings lösen:

Code: Alles auswählen

// Lösung für drei Member
template <typename T>
void MachLayoutFuer3Member() {
  const auto& [a, b, c] = T{};
  if constexpr( std::is_same_v<std::decay_t<decltype(a)>, DirectX::XMFLOAT2> ) {
    layoutElements.push_back({ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, currentOffset, D3D11_INPUT_PER_VERTEX_DATA, 0 });
    currentOffset += sizeof(DirectX::XMFLOAT2); // Increase offset for next element  
  }
  ...
}
Der Code prüft offensichtlich nur den ersten Member, während Du alle Member durchgehen willst. Das kann man mit nem Variadic Template Arg erreichen. Der Code ist offensichtlich auf Strukturen mit 3 Membern spezialisiert, wie man die Anzahl Member einer Struktur durchzählt, hab ich hier vor nem halben Jahr schon gebraucht und dann vorgestellt. Der Code behandelt offensichtlich nur VECTOR2, der Rest sind weitere if constexpr. Der Code schließt offensichtlich die Usage aus dem Type, aber der Type ist eh nur Gimmick für die alte Hardware Tesselation - ich hab da schon 2005 oder so nur noch "POSITION" und halt "TEXCOORD0" bis "X" genutzt - reicht auch.

Wenn Du das in C++ ausarbeitest, wirst Du aber feststellen, dass es ganz schön viel Gebastel ist. Überlege Dir also auch mal, ob es wirklich ne generische Lösung sein muss. Oder ob nicht einfach eine Funktion "MachDasEineLayout()" und eine "MachDasAndereLayout()" ausreichen würden. Der Code wäre kurz, präzise, überschaubar und viel wartbarer als ne generische Lösung.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
gombolo
Establishment
Beiträge: 161
Registriert: 26.01.2011, 20:33

Re: [C++] generische Inputlayout-Erstellung für verschiedene Vertex-Typen in DX11

Beitrag von gombolo »

Es Stimmt alles was du schreibst. Und das mit der Reflection war meine erste Überlegung, die ich dann fallen lies und ja es ist ein gefummel...

Meine Lösung ist möglicherweise zu komplex. Vielleicht sind einfachere Ansätze wie separate Funktionen für verschiedene Layouts besser geeignet. Das würde den Code übersichtlicher und wartbarer machen. Gebe ich dir 100% Recht.

Aber am Ende ist es ein Hobby und ich lerne dabei viel dazu...Deine Gedanken sind sehr hilfreich und ich werde mir Gedanken über alternative Ansätze machen. Danke dir...
Benutzeravatar
Jonathan
Establishment
Beiträge: 2545
Registriert: 04.08.2004, 20:06
Kontaktdaten:

Re: [C++] generische Inputlayout-Erstellung für verschiedene Vertex-Typen in DX11

Beitrag von Jonathan »

Ich bin generell kein Freund von zu komplexen templates in C++. Erstens, weil ich kein großer Held darin bin sie zu schreiben, und für etwas, was eigentlich sehr simpel sein sollte einen halben Tag (oder mehr) brauche (wie du gerade auch ;) ), und zweitens, weil selbst wenn es dann funktioniert man irgendwo halt 5 Zeilen Code gespart hat und das wars.
In Bibliotheken mag ich templates eigentlich auch meistens nicht, weil der Code dann schwerer zu lesen ist, ich nicht direkt verstehe, was passiert, und Fehlermeldungen nutzlos sind, weil sie über mehrere Seiten gehen. Ich habe auch schon halbe Tage darauf verschwendet "gute" templates die jemand anderes sehr schön implementiert hat korrekt kompilieren zu können. Von "alles muss im Header stehen und überall mitkompiliert werden" mal ganz abgesehen...

Für Vertex-Buffer hab ich dann irgendwann z.B. sowas benutzt um einzelne Felder zu registrieren:

Code: Alles auswählen

//computes the offset, the size (number of floats) and the stride of an entry
#define VERTEX_BUFFER_MEMBER(tStruct, member) offsetof(tStruct, member), sizeof(tStruct().member)/4, sizeof(tStruct)
Was ich dann entsprechend irgendwo 5 mal aufrufen damit klar ist wie meine Struktur aussieht. Das kann man dann mit ein paar Laufzeitchecks kombinieren und dann ist es auch halbwegs robust und es fällt schnell auf, wenn man mal vergessen hat etwas zu registrieren.

An einer anderen Stelle hab ich mal lange versucht die Overload-Resolution korrekt hinzubekommen. Hab sogar hier im Forum nachgefragt und nichts ging, am Ende hab ich dann an meine "DoSomthing()" Funktion einfach den Typ angehängt ("DoSomething_vec3()") und alles hat direkt funktioniert und war immer noch einfach zu benutzen.
Lieber dumm fragen, als dumm bleiben!
https://jonathank.de/games/
Antworten