Grafikstatus und Komponenten verwalten?

11

Ich neige oft dazu, viel vorzeitige Optimierung zu betreiben, wenn ich mich mit Grafiken beschäftige. Es gibt einige Prinzipien, denen ich immer zu folgen versuche:

  • Halten Sie die Anzahl der D3D-Komponenten auf ein Minimum.(Renderzustände, Puffer, Shader usw.)
  • Binden Sie Komponenten nur, wenn dies unbedingt erforderlich ist.(Nicht schon gebunden usw.)
  • Spezialisieren Sie die Komponenten so weit wie möglich.(Setzen Sie nur die erforderlichen BindFlags usw.)

Dies führte mich dazu, sehr aufwendige Wrapper für die Verwaltung der erstellten Komponenten und des aktuellen Rohrleitungsstatus zu erstellen. Dies verbraucht nicht nur viel meiner wertvollen Entwicklungszeit, sondern erhöht auch die Komplexität.

Und das Schlimmste von allem: Ich weiß nicht einmal, ob sich die Mühe lohnt.

Einige meiner Optimierungsüberlegungen sind möglicherweise bereits auf einer niedrigeren Ebene implementiert, und ich repliziere sie nur, wodurch zusätzlich Zeit für die CPU verschwendet wird. Andere Überlegungen können völlig unnötig sein, da die Auswirkung auf die Leistung vernachlässigbar ist.

Meine Fragen sind also:

  1. Welche der oben genannten Richtlinien sind gültig und in welchem ​​Umfang sollte ich sie befolgen?
  2. Wie geht die GPU mit Statusänderungen um?
  3. Was passiert, wenn ich einen Status ändere, der nie verwendet wird? (Es wird kein Draw Call getätigt, solange es aktiv ist.)
  4. Was sind die tatsächlichen Leistungseinbußen beim Binden der verschiedenen Komponenten?
  5. Welche anderen Leistungsüberlegungen sollten gemacht werden?

Bitte sagen Sie mir nicht nur, dass ich mich nicht um die Leistung kümmern sollte, bis ich die tatsächlichen Grenzen erreicht habe. Während dies aus praktischer Sicht offensichtlich zutrifft, interessiert mich hauptsächlich die Theorie. Ich muss irgendwie dem Drang entgegenwirken, das optimale Grafik-Framework zu erstellen erstellen, und ich glaube nicht, dass ich das mit der üblichen "Vorlesung zur vorzeitigen Optimierung" tun kann.

Komponenten verwalten

Ich schreibe derzeit DirectX 11-Anwendungen in C # mit SlimDX als verwaltetem Wrapper. Es ist ein Wrapper auf sehr niedriger Ebene und meine aktuelle Abstraktion baut darauf auf.

Die Verwendung einer Direct3D-Abstraktion bietet einige offensichtliche Vorteile. Das Einrichten der Umgebung, das Laden von Shadern, das Festlegen von Konstanten und das Zeichnen eines Netzes ist viel einfacher und erfordert viel weniger Code. Da es die Erstellung und Entsorgung der meisten Komponenten verwaltet, können sie überall automatisch wiederverwendet werden, und ich vermeide fast vollständig Speicherlecks.

  1. Wie verwalten Sie normalerweise alle Grafikkomponenten und Ressourcen?
  2. Können Sie verwaltete Wrapper empfehlen, die etwas Ähnliches wie mein Beispiel unten tun?

Hier ist ein Beispiel meiner aktuellen Implementierung. Ich bin sehr zufrieden mit der Schnittstelle. Es hat genug Flexibilität für meine Bedürfnisse und ist sehr einfach zu bedienen und zu verstehen:

// Init D3D environment
var window = new RenderForm();
var d3d = new Direct3D(window, GraphicsSettings.Default);
var graphics = new GraphicsManager(d3d.Device);

// Load assets
var mesh = GeometryPackage.FromFile(d3d, "teapot.gp");
var texture = Texture.FromFile(d3d, "bricks.dds");

// Render states
graphics.SetViewports(new Viewport(0, 0, 800, 600);
graphics.SetRasterizer(wireFrame: false, culling: CullMode.Back);
graphics.SetDepthState(depthEnabled: true, depthWriteEnabled: true);
graphics.SetBlendState(BlendMethod.Transparency);

// Input layout
graphics.SetLayout("effect.fx", "VS", "vs_4_0",
    new InputElement("POSITION", 0, Format.R32G32B32_Float, 0),
    new InputElement("TEXCOORD", 0, Format.R32G32_Float, 0)
);

// Vertex shader
graphics.SetShader(Shader.Vertex, "effect.fx", "VS", "vs_4_0");
graphics.SetConstants(Shader.Vertex, 0, 4, stream => stream.Write(wvpMatrix));

// Pixel shader
graphics.SetShader(Shader.Pixel, "effect.fx", "PS", "ps_4_0");
graphics.SetTexture(Shader.Pixel, 0, texture);
graphics.SetSampler(Shader.Pixel, 0, Sampler.AnisotropicWrap);
graphics.SetConstants(Shader.Pixel, 0, 1, stream => stream.Write(new Color4(1, 0, 1, 0);

d3d.Run(() =>
{
    // Draw and present
    d3d.BackBuffer.Clear(new Color4(1, 0, 0.5f, 1));
    graphics.SetOutput(d3d.BackBuffer);
    graphics.Draw(mesh);
    d3d.Present();
}
Lucius
quelle
8
Für diese Art von Frage würde ich nicht die Vorlesung "Vorzeitige Optimierung" halten, sondern die Vorlesung "Profiländerungen, damit Sie sich selbst davon überzeugen können".
Tetrad
@ Tetrad Ich schäme mich fast zuzugeben, dass dies ein ziemlich anständiger Rat ist. Ich sollte definitiv mehr Profiling machen.
Lucius
1
Profilnummern sind mit Sicherheit die Gamedev-Version von "Bilder oder es ist nicht passiert" =)
Patrick Hughes

Antworten:

3

Ich mag den Abstraktionsansatz, den Hodgman in diesen Threads auf gamedev.net skizziert hat:

Er beschreibt ein dreistufiges Rendering-System:

  1. Low-Level-Rendering-API, die "Befehle" akzeptiert und nur die Unterschiede zwischen verschiedenen Grafik-APIs wie Direct3D 9, Direct3D 11 und OpenGL abstrahiert. Jeder "Befehl" wird einem anderen Status oder Zeichnungsaufruf zugeordnet, z. B. dem Binden eines Scheitelpunktstroms oder einer Textur oder dem Zeichnen der Grundelemente.
  2. API, die "Renderelemente" akzeptiert, die alle Status und einen einzelnen Zeichenaufruf gruppieren, der zum Rendern eines bestimmten Objekts erforderlich ist, und diese sortiert und in Befehle übersetzt, die an die erste Ebene gesendet werden. Ein Renderstatus enthält einen Draw-Call und einen Stapel von "Statusgruppen", die Statusänderungen logisch gruppieren. Beispielsweise hätten Sie eine Statusgruppe für den Renderdurchlauf, eine Statusgruppe für das Material, eine Statusgruppe für die Geometrie, eine Statusgruppe für die Instanz usw. Diese Ebene ist für das Sortieren dieser Renderelemente verantwortlich, um redundante Statusänderungen zu reduzieren und alle redundanten Statusänderungen auszusortieren.
  3. Übergeordnete Systeme wie ein Szenendiagramm oder ein GUI-Renderer senden Renderelemente an die zweite Ebene. Es ist wichtig zu beachten, dass diese Systeme weder die Statussortierungsalgorithmen noch die spezifische Rendering-API kennen, was sie vollständig plattformunabhängig macht. Sie sind auch einfach zu verwenden, sobald die untergeordneten APIs implementiert wurden.

Zusammenfassend lässt sich sagen, dass dieses Modell beide Probleme gleichzeitig löst.

jmegaffin
quelle