2013-03-30 11 views
5

Wie würde man Texturen asynchron laden, während eine 3D-Anwendung läuft? Was ich verstehe, ist, dass OpenGL-Kontexte nicht Thread-sicher sind und dass ich sie auf verschiedenen Threads trennen sollte.Einrichtungen zum asynchronen Laden von Texturen

Aber mein Hauptproblem ist die Auswahl einer richtigen Multithreading-Einrichtung/Framework, um dies tatsächlich mit Windows und C++ zu implementieren, ich habe eine Menge über C++ 11 einschließlich Threading-Unterstützung in seiner Standard-Bibliothek gehört, aber könnte jemand nur Layout die grundlegenden Schritte?

Was wäre der sicherste Weg, dies zu tun? Und wie aktualisiert man den Zustand des anderen Kontexts, in dem die Änderungen des anderen Threads registriert werden? Ich vermute glFlush und glBind*?

+0

Eigentlich OpenGL und OpenGL-Kontexte ** sind ** thread sicher. Sie können nur einen einzigen Kontext in mehreren Threads gleichzeitig aktiv haben. – datenwolf

Antwort

5

Der zeitaufwendigste Teil des Texturladens ist normalerweise der Plattenzugriff und jede Formatkonvertierung, die beide unabhängig von OpenGL sind und daher sicher auf einem anderen Thread stattfinden können. Sobald die Textur in den Speicher und in das gewünschte Format eingelesen wurde, ist die eigentliche Kopie in einen OpenGL-Puffer ziemlich schnell.

Der Einstieg in die Details der Thread-Programmierung ist für diese Antwort völlig zu komplex, aber es gibt ein gutes Stück Dokumentation, und sobald es in Ihrem Kopf klickt, ist es ziemlich einfach (wie Zeiger und Speicher).

Das allgemeine Konzept besteht darin, eine Liste von Texture Holder-Objekten zu erstellen (z. B. die Datei/den Namen, einen initially-null-Puffer und ein loading-complete-Flag) und diese an Ihren Lade-Thread zu übergeben . Der Lade-Thread durchläuft dann die Liste, öffnet jede Datei, lädt sie in den Speicher und hängt den Puffer an den Listeneintrag an, setzt dann das geladene Flag und inkrementiert möglicherweise einen Zähler. Der Haupt-Thread übernimmt die neu geladene Textur, kopiert sie in eine OpenGL-Textur und erhöht den Fortschrittsbalken oder was auch immer Ihr Lade-Indikator ist. Sobald alle Texturen in der Liste Puffer haben und als geladen markiert sind, ist die Arbeit des anderen Threads beendet und kann gestoppt werden (oder belassen, um zukünftige Texturen zu laden).

Der Hauptvorteil dieses Modells liegt darin, dass Sie nicht den tatsächlichen Grafikkontext teilen müssen. In APIs, die threadsicher (DirectX) sein können, ist dies mit einer Leistungseinbuße verbunden, und OpenGL erfordert von Ihnen eine ordentliche Arbeit, um mehrere Kontexte zu haben oder sicherzustellen, dass Sie sie korrekt teilen. Das schwere Heben beim Laden von Texturen ist normalerweise das Lesen von Dateien und das Überprüfen von Parametern, es sei denn, Sie konvertieren oder rotieren im Format, was sogar den Festplattenzugriff beeinträchtigen kann. Die tatsächliche Kopie in den Videospeicher ist stark optimiert und stellt wahrscheinlich keinen Engpass dar (wenn Sie sich darüber Sorgen machen, versuchen Sie, ein Tool zu erstellen, das GPU-Anrufkosten erkennt und sehen kann). Nichts davon hängt direkt von OpenGL ab, also können Sie es ohne großen Aufwand in einen anderen Thread verschieben.

Wenn Sie speziell Windows verwenden, gibt es eingebaute Threading-Funktionen, die verwendet werden können. Sie arbeiten mit einem einfachen Callback-Modell (stellen Sie eine Funktion und erste Parameter, in diesem Fall Ihre Texturliste, zur Verfügung Anruf). Ich bin nicht persönlich vertraut mit C++ 11 Threading-Unterstützung oder wie es in Visual Studio funktioniert, wenn überhaupt, so dass Sie das überprüfen müssen.

+0

Schöne Antwort. Ja, C++ 11 'std :: thread' funktioniert auf MSVC++. Es ist sehr ähnlich zu boost :: thread. –

0

Die Antwort von @ssube ist korrekt bezüglich der Komplexität der Aufgabe, geht jedoch davon aus, dass "OpenGL eine ordentliche Arbeit von Ihnen erfordert, um mehrere Kontexte zu haben oder sicherzustellen, dass Sie sie richtig teilen", und ich nicht zustimmen.

Eine einfache Lösung ist es, einen Haupt Kontext zu schaffen (zum Zeichnen) und einen sekundären Rahmen (für Textur Laden), zu Beginn des Programms, zum Beispiel mit:

m_hRCDrawing = wglCreateContext(m_hDC); 
m_hRCSecondary = wglCreateContext(m_hDC); 

Und dann die gemeinsame Nutzung von Daten zwischen dem m_hRCSecondary und den m_hRCDrawing Kontexten können gemacht werden:

wglShareLists(m_hRCSecondary, m_hRCDrawing); 

Schließlich, wenn Sie dabei sind, eine Textur aus einer „Textur loading“ Faden, der keine GL Kontext hat, können Sie einfach anrufen zu lesen:

wglMakeCurrent(m_hDC, m_hRCSecondary); 

nach dem alle in diesem Thread geladenen Assets vom Kontext des Zeichnungs-Threads gemeinsam genutzt werden.

Eine etwas gründlichere Erklärung ist verfügbar here.

Verwandte Themen