Direct2D die zweite



  • Hey cooky,

    also genau sowas hatte ich mir ja eigentlich auch gedacht. OpenGL hat sowas, GDI hat sowas, Gdiplus hat sowas, ja sogar Direct3D hat sowas. Jetzt weiß ich nicht, wo diese APIs das Tesselieren übernehmen (ich war zumindest immer der Meinung, dass Consumer-Grakas Linien auch nur per Treiber über Vierecke zeichnen). Aber in Direct2D gibt es keinen untesselierten LineStrip, oder sogar bloß Arrays von Punkten, die man direkt hochladen könnte und Shader natürlich auch nicht.

    Im Moment erzeuge ich also für jedes neue Frame und für jede Datenkurve eine neue Geometrie (weil man die alte noch nicht mal leeren oder sonstwie wiederbenutzen kann), packe die Linienzug-Punkte rein und kann dann beim RenderTarget::EndDraw dabei zuschauen, wie das in schönste Dreiecke zerlegt wird:

    for( unsigned int i=0; i<data_paths_.size(); ++i ) {
    		SafeRelease( &data_paths_[i] );
    		HRESULT hr = m_pDirect2dFactory->CreatePathGeometry( &data_paths_[i] );
    
    		ID2D1GeometrySink* geometry_sink;
    		data_paths_[i]->Open( &geometry_sink );
    
    		geometry_sink->BeginFigure(D2D1::Point2F(0, 0), D2D1_FIGURE_BEGIN_HOLLOW);
    		geometry_sink->AddLines( data_lines_[i].data(), data_lines_[i].size() );
    		geometry_sink->EndFigure( D2D1_FIGURE_END_OPEN );
    
    		geometry_sink->Close();
    		SafeRelease( &geometry_sink );
    	}
    

    Das ist hier in dem Direct2D-Sample von Microsoft aufgegangen, drum gibts da SafeRelease statt RAII 😉

    Ich denke, das läuft wohl darauf hinaus, dass ich Direct3D unter Direct2D baue. Vielleicht sollten die noch Direct1D für effizientes Linienzeichnen einführen! 🙂



  • Nur testhalber habe ich mal darauf verzichtet, die Geomtrie jedes Frame neu zu erzeugen. Trotzdem wird ein Core total blockiert, diesmal gibts ganze 3 Frames mehr. Diese Geometrien werden bei jedem Zeichenvorgang neu tesseliert, das ist kein Preprocessing/Caching vorhanden... Grummel.



  • Also die ersten Samples die man von Microsoft so sieht nutzen einfach DrawLine, haste das mal ausprobiert?

    Edit: Tjoa, PathGeometry sollte schon das schnellste sein, irgendwie. Etwas eigenartig, eine Linie über 1k Punkte zu zeichnen sollte wirklich keine CPU-Last erzeugen. Vielleicht hast du irgendwo anders was ganz komisches drin? Meinst du, du könntest ein kleines Testprogramm/Projekt online stellen?



  • Klar, ich bereite das mal vor uns nehme die unnötigen Sachen raus. Ist eigentlich das Direct2D-Sample von Microsoft, erweitert um ein paar Testzeilen. Unos Momentos!



  • Da zu finden:
    http://www.file-upload.net/download-7370316/direct2dtest.zip.html

    Ist ein VS2012-Projekt, aber ich glaube, die sind soweit kompatibel zu VS2010, außer dass man das Platform-Toolset anpassen muss!
    Hab leider gerade kein git mehr installiert...



  • Oha, entschuldigung, da waren noch Ressourcen referenziert, die ich gelöscht hatte, irgendwie lief es vor einem kompletten Neubau trotzdem.

    http://www.file-upload.net/download-7370497/direct2dtest.zip.html



  • Okay, also ich habe jetzt mal angefangen damit, D3D und eine SwapChain zu benutzen, um D2D mit D3D zu verwenden.
    Aber WTF. Man braucht d3d 10.1 um das Pixelformat DXGI_FORMAT_B8G8R8A8_UNORM verwenden zu können, das wiederum Direct2D braucht, um die Surface benutzen zu können. Wenn ich RGBA benutze, kriege ich zwar einen D3D-Device bzw. eine Swapchain erzeugt, allerdings steigt dann die Erzeugung eines Direct2D-RenderTargets auf der DXGISurface aus. Wenn ich DXGI_FORMAT_B8G8R8A8_UNORM angebe, bekomme ich ein E_INVALIDARG.
    Ich habe eine alte Gtx260, welche überhaupt gar kein D3D 10.1 voll unterstützt. Gibt es auf meiner Karte also auch kein Interop mit Direct2D? Was wäre das denn bitte für eine Grütze?
    Und überhaupt verstehe ich das Versions-Prozedere in D3D irgendwie nicht so ganz. Das D3D-D2D-Interop-Beispiel von Microsoft benutzt zwar den "d3d10_1.h"-Header, allerdings keine D3D-10.1-Spezifischen Interfaces. Ich verstehe überhaupt nicht, wie es dann DXGI_FORMAT_B8G8R8A8_UNORM an's laufen bekommt, schließlich braucht man dafür doch ein 10.1-Device, aber der ganze Quellcode holt sich überall nur D3D10-Kram.
    Hat da jemand von euch etwas Erfahrung?

    Edit: Und überhaupt, gehört der Thread hier eher in Spiele/Grafikprogrammierung?



  • Ich werd' hier gleich zum Berserker... das DXGI-Interop-Sample bekomme ich nicht ans laufen, weil es die WIC verwendet, die ich mit dem Installer aber nicht installiert bekomme. Diesen ganzen MS-Grafik-Kram finde ich MAXIMAL zum Kotzen. Schnelle 2D-Grafik erst ab DirectX 10.1-Level? Na klar! Und unter Windows 8 kann man dann aber erst Direct3D 11 hernehmen? Wie wollen die jemals eine stabile 2D-Plattform bieten, wenn sie ständig solche Einbrüche vornehmen?
    Wer will schon 3 verschiedene Renderpfade für ein und dieselbe Plattform schreiben? GNAAAAAAAAA



  • Hm, in dem Beispiel werden die Ressourcen jeden Frame neu erzeugt. Aber selbst wenn man das und die Linien-Generierung nur einmal macht, ist das Rendern viel zu langsam. Keine Ahnung was da los ist, aber nur mal so: Warum nicht gleich GDI? Dann läuft das Programm auch unter XP. 😉



  • Die Ressourcen erstelle ich ja jedes Mal neu, um zu simulieren, wie die Situation später ist. Die Daten kommen wie gesagt live rein und verändern sich dadurch immer.

    GDI unterstützt leider keine Kantenglättung oder Floating-Point-Koordinaten für "glatte" Animationen. [Edit: Ich komme von GDI über GDI+ zur gerade vorliegenden Situation. Ich möchte eine schöne, animierte und flüssige UI anbieten (nicht überfrachtet oder knallbunt, einfach "ansprechend"), aber das stellt mich vor größere Hürden als zunächst gedacht...]

    Ich glaube, ich werde eher etwas unschönere Fonts in Kauf nehmen und auf OpenGL ausweichen... Vielleicht gibt's ja auch irgendwie einen weg, Direct2D-Fonts in eine Bitmap zu rendern und die dann in OpenGL zu verwenden... Das ist ja kein Zustand, was man hier mitmacht.



  • Ich glaub ich hätte hier schon lange drauf geschissen und alles komplett mit D3D gemacht.
    Ausser vielleicht D2D zum Texte malen - dazu sollte man aber nicht mehr als einfache monochrome Memory-Surfaces brauchen.



  • Echtes Interop bekomme ich ja nur mit D3D Hardwarelevel 10.1 hin, das ist ja das Problem, darauf habe ich heute den Nachmittag verschwendet. Gibts irgendwie noch einen anderen Weg, um D2D-MemoryTargets halbwegs effizient als Texturen in D3D zu verwenden?



  • Aber warum willst du denn noch Direct2D benutzen wenn du Direct3D schon hast? Macht das Sinn?



  • Weil das Font-Rendering von DirectWrite etwas ist, das man sich nicht eben mal schnell selber hinfrickelt. Zumindest ich nicht. Mag vielleicht jemanden geben, der das an nem Nachmittag hinbaut... Oder das Tesselieren von beliebigen Pfaden usw. Jeder einzelne Punkt davon ist eine Wissenschaft für sich. Für D3D müsste ich mir jetzt auch erstmal eine Bibliothek basteln, die mir gefüllte oder ungefüllte Bezier-Pfade, bei unterschiedlichen Strichbreiten, runterbricht. Vielleicht sogar noch Dashed-Kram. Das ist ja alles in D3D nicht vorhanden.
    Klar, gefüllte Vierecke bekommt man schnell hin, und Bilder bekommt man auch Problemlos draufgemapped. Aber D3D bietet nunmal keine fertige Vektorgrafik-API.



  • Wo ist jetzt genau der Unterschied zu LINE_STRIP?^^



  • Wie meinst Du das?
    Es sollte jetzt nicht der Eindruck aufkommen, dynamische Linienzüge zu zeichnen wäre alles was ich so machen muss. Es ist nur der einzige Showstopper in Bezug auf Direct2D.



  • Nur mal so rein prinzipiell, ohne den ganzen Thread gelesen zu haben: Wieso musst du tatsächlich die ganze Kurve ständig neu malen? Kannst du nicht z.B. in einen eigenen Buffer rendern (Bitmap Render Target oder sowas), sodass du immer nur die neuen Messwerte dazumalen musst? Denn der Rest der Kurve ändert sich ja wohl nicht, wenn neue Werte dazukommen!? Mit wievielen Messwerten haben wir es denn zu tun, dass es da Probleme gibt? Spätestens wenn der Abstand zwischen zwei Messwerten kleiner wird als ein Pixel, kannst du z.B. jeden zweiten Wert überspringen, ohne wirklich was zu verlieren...



  • Hallo dot hier 😃
    Darüber habe ich mir auch Gedanken gemacht, aber kam zu dem Schluss, dass das gar nicht so einfach ist, wenn man Antialiasing zugrunde legt.
    Zum einen müsste ich also das Scrollen im Datenbereich auf eine Quantisierung beschränken, die nach Projektion genau auf ganze Pixel-Verschiebung kommt (mit allen Rundungsfehlern, die dabei wohl so auftreten können). Zum anderen hatte ich Bedenken, dass das "aneinanderfügen" von gefilterten Linienzügen nicht so aussieht, wie wenn man den Linienzug in einem Rutsch raushaut. Dem kann man wohl begegnen, indem man eine Clipping-Zone einrichtet und noch ein paar Punkte vom letzten Linienzug verwendet.
    Spätestens wenn der Benutzer aber rumscrollt, wird dann aber wieder der CPU-Lüfter ausgepackt.
    Was also früher mit GDI ein Einzeiler war und mit Direct3D ein vierzeiler wäre, würde hier zu einem komplexen Algorithmus, der Auswirkungen auf das Datenmodell von Controls hat, der für den worstcase immernoch extrem langsam ist.

    Bezüglich der Unterabtastung: Hier müsste ich Punkte adaptiv auslassen, weil die Pixelabstände ja nicht konstant sind. Das wäre eine Option, die ich mal auf ein paar Datensätzen durchspielen könnte.



  • Decimad schrieb:

    Zum einen müsste ich also das Scrollen im Datenbereich auf eine Quantisierung beschränken, die nach Projektion genau auf ganze Pixel-Verschiebung kommt (mit allen Rundungsfehlern, die dabei wohl so auftreten können).

    Ich bezweifle, dass du wirklich mit Subpixelgenauigkeit wirst scrollen müssen. Könnte mir sogar vorstellen, dass das eher weniger gut aussehen würde...

    Decimad schrieb:

    Was also früher mit GDI ein Einzeiler war und mit Direct3D ein vierzeiler wäre, würde hier zu einem komplexen Algorithmus, der Auswirkungen auf das Datenmodell von Controls hat, der für den worstcase immernoch extrem langsam ist.

    Wenn das, was du machen willst, mit Direct3D soviel einfacher wäre, dann mach es doch mit Direct3D und mach alles andere mit Direct2D, gibt ja Interop... 😉

    Decimad schrieb:

    Bezüglich der Unterabtastung: Hier müsste ich Punkte adaptiv auslassen, weil die Pixelabstände ja nicht konstant sind. Das wäre eine Option, die ich mal auf ein paar Datensätzen durchspielen könnte.

    Das wirst du aber sowieso machen wollen, denn sobald deine Geometrie eine höhere Auflösung hat als dein Bildschirm, gibts nämlich wieder Aliasing, nur in die andere Richtung... 😉



  • Interop funktioniert leider nur, wenn die Hardware DirectX Hardware Level 10.1 mitmacht: Ausschlusskriterium.

    Zur Subpixel-scroll-genauigkeit: Ich muss sie nicht unbedingt haben, aber sie kommt im Moment automatisch und ich muss mich aktiv darum bemühen, in festen Pixelintervallen zu scrollen. Das heißt, mein Intervall-Scroll-Algorithmus muss irgendwie die Projektion im Hinterkopf behalten. Das ist alles keine Raketenwissenschaft, ich weiß, aber die Controls müssen auf einmal Optimierungen verwenden für Dinge, die in keiner anderen 2D-API, die ich jemals gesehen habe, vonnöten wären. In der Tat frage ich mich gerade, ob Direct2D überhaupt in irgendeiner Art und Weise schneller ist, wenn es nicht gerade um große texturierte, rotierte und skalierte Vierecke geht. Es ist echt nicht einladend, wenn man gleich innerhalb der ersten Minuten mit einer API gegen eine Wand rennt...

    Und irgendwann werde ich wieder auf die Idee kommen, dass es praktisch wäre, an die darunterliegende Grafik-API zu gelangen. Ich verstehe einfach nicht, warum Direct2D nicht auch nach außen als eine Bibliothek überhalb von Direct3D implementiert ist und man auf komische Interop-Geschichten angewiesen ist, statt Zugriff auf das Device zu bekommen.


Anmelden zum Antworten