Direct2D die zweite



  • Hallodrio,

    ich habe nun also angefangen, meinen Kram auf Direct2D umzusetzen und es war auch alles in Ordnung, bis ich nun gesehen habe, dass man zum Zeichnen von Linienzügen eben eine extra Geometrie anlegen muss. Nun möchte ich Messdaten als Linienzüge darstellen. Diese Messdaten kommen aber als live Stream rein. Dargestellt werden so ca. 1000 Punkte einer Messkurve auf einmal.
    Nun ist das Tesselieren von Linienzügen kein Pappenstiel, das ist mir klar. Allerdings bietet D2D überhaupt keine Möglichkeit, Linienzüge später zu erweitern, zudem habe ich das Gefühl, dass sie sowieso pro Frame alles immer wieder tesseliert wird.
    Ich habe in der Folge ein kleines Testprogramm geschrieben, das mir 12 solcher Linienzüge anzeigen soll und komme auf 40fps bei totaler CPU-Limitierung (Eben verwendet auf das fram-weise komplette Tesselieren der Linienzüge, ansonsten wird Hardware zum Rasterisieren verwendet, bin ich der Meinung).
    Ich mein, die Darstellung solcher Messkurven kommt doch bestimmt öfter vor, habe ich nur den eleganten Weg, sowas mit D2D umzusetzen, nicht gefunden?
    Im Internet habe ich nur eine etwas ältere Idee gefunden, die von der Sortierung der Datenpunkte ausgeht und in der Folge Pixel-Shader benutzt, um den Abstand vom aktuellen Linienzugsegment auszurechnen und so das Zeichnen umsetzt. Dann müsste ich aber jetzt auch noch Direct3D lernen, auspacken, unter das Direct2D pflanzen, zudem noch in die Shaderprogrammierung einsteigen.
    Ich mein, das kann man natürlich alles machen, aber irgendwie hatte ich solche Sachen eher schon in der 2D-Blibliothek erwartet.

    Hat jemand noch eine Idee?



  • Also erst mal ist Direct2D eher eine API als eine Bibliothek, da gibt's das Nötige und keinen Komfort. 😉
    Wie zeichnest du denn momentan? Direct2D hab ich noch nie benutzt, aber das erste was mir für Direct3D einfallen würde, wäre einfach alle Punkte hintereinander im Puffer (auf der CPU) zu halten, dann jeden Frame als VBO hochzuladen und dann als LINE_STRIP zu rendern. Das sollte eigentlich auch auf dem letzten Gammel-Rechner mit 1500 FPS laufen. (Besser wäre natürlich VBO-Puffer-Gefrickel indem man mehr reserviert und dann nach und nach den Kram rein schreibt, aber wie gesagt, ich denke nicht, dass das nötig ist.)



  • 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.


Log in to reply