Laden...

Darstellung einer interaktiven Karte mit WinForms und SharpDX

Erstellt von zeh-scharb vor 9 Jahren Letzter Beitrag vor 9 Jahren 11.399 Views
Hinweis von MrSparkle vor 9 Jahren

Abgeteilt von Direct2D: viele Linien zeichnen

Z
zeh-scharb Themenstarter:in
32 Beiträge seit 2015
vor 9 Jahren

Ich habe prinzipiell das gleiche Problem, muss jedoch nicht 20.000 Linien sondern mindestens 200k ohne sichtbares Delay zeichnen. Wozu so viele Linien? Für einen sehr detailierten Plan, in den man hineinzoomen kann.

Ich habe auch mit WPF angefangen und mit kleinen Zeichnungen schnell Fortschritte gemacht. Aber bei steigender Größe / Deteilgrad der Pläne gibt es große Delays und die Anwendung nutzt mehrere hundert MB Arbeitsspeicher. (auch nachdem ich die Optimierungs-Tipps umgesetzt habe).

Deswegen habe ich mich mit DirectX beschäftigt, wofür es ja zumeist leider nur Anleitungen für C++ gibt. Ich habe etwas mit SharpDX und SlimDX experimentiert und kann nun 1mio Linien in ca einer Sekunde zeichnen, wobei die Anwendung keine 40MB im Arbeitsspeicher belegt.
Nun stehe ich vor der Umsetzung von Zooming, Scrolling aber finde nur Anleitungen und Foreneinträge zu 3D-Anwendungen. Ich benötige aber nur folgendes:

  • Einmaliges Übertragen der Linienkoordinaten auf die Grafikkarte
  • Verschieben des Blickfeldes horizontal / vertikal
  • Zooming
    Klingt jetzt nicht soooo exotisch, aber ich habe bisher nichts brauchbares gefunden.

Was ich gefunden habe ist:

D2D.WindowRenderTarget.Transform

Damit kann ich vor dem Zeichnen tranformieren, aber nicht im nachhinein den Bildausschnitt wählen.

Habt ihr eine Idee wo ich dazu Informationen bekomme oder welches Buch ich mir dazu besorgen sollte?

5.658 Beiträge seit 2006
vor 9 Jahren

Hi zeh-scharb,

hast du dir den weiter oben im Beitrag verlinkten Artikel einmal angeschaut? Und hast du dir dein Programm schonmal mit einem Profiler angesehen? Wenn ja, wo liegen die Probleme? Ganz allgemein gesagt, klingt das für mich nach einem Clipping-Problem. Such mal bei Google nach ViewPort-Clipping, das würde deine Performance sicherlich wesentlich verbessern. Wenn du 200k Linien zeichnest, zeichnest du definitiv mehr, als auf einem Bildschirm Platz hätte. Da hat man viel Raum für Optimierungen.

Christian

Weeks of programming can save you hours of planning

Z
zeh-scharb Themenstarter:in
32 Beiträge seit 2015
vor 9 Jahren

Habe es endlich geschafft die "Wpf Performance Suite" ans Laufen zu bekommen.
Dazu war es nötig:

  • Unter der Systemsteuerung die alte Version von "Microsoft Visula C++ 2010 Redistributable" deinstallieren damit das Windows SDK installiert werden kann und nicht im Setup abbricht
  • Den Patch für die Performance Suite ausführen, um den "Visual Profiler" zu fixen:
    link zum Pach

Leider hat mich das nicht wirklich schlauer gemacht.

Eingebunden ist die Grafikkomponente via Element-Host in ein Windows Form.
Der Teil für die Darstellung in der WPF-Komponente:

<ScrollViewer x:Name="scrollViewer" DockPanel.Dock="Left"  HorizontalScrollBarVisibility="Hidden"  VerticalScrollBarVisibility="Hidden"  HorizontalAlignment="Center" Margin="0,0,0,0" VerticalAlignment="Center" Focusable="False" PreviewMouseWheel="scrollViewer_PreviewMouseWheel">
           
 <Canvas x:Name="canvas" Background="#FFA4A3A3" HorizontalAlignment="Center" VerticalAlignment="Center" ScrollViewer.VerticalScrollBarVisibility="Disabled" MinWidth="1000" MinHeight="1000"/>

</ScrollViewer>

Z
zeh-scharb Themenstarter:in
32 Beiträge seit 2015
vor 9 Jahren

Analyse aus VS2013

742 Beiträge seit 2005
vor 9 Jahren

Auf die schnelle habe ich nur folgenden Link gefunden als Einstieg gefunden:

Tutorial series: learning how to write a 3D soft engine from scratch in C#, TypeScript or JavaScript

Im Prinzip läuft es darauf hinweis, dass du ein oder mehrere VertexBuffer erstellst und diese einmalig befüllst. Mit einer Camera Klasse (View Matrix) kannst du dich dann in deiner Szene bewegen.

Damit sollte es auch möglich sein, mehrere Mio Linien mit 30FPS zu zeichnen (je nach Graka).

5.658 Beiträge seit 2006
vor 9 Jahren

Hi zeh-scharb,

bevor du hier so kommentarlos irgendwelche Screenshots und Detailinfos postest: Um was geht es eigentlich? Am Anfang hast du von DirectX geredet, jetzt sprichst du von einer WinForms-Anwendung, die ein WPF-Control hostet...? Beschreib doch einfach mal den Ist-Zustand und den Soll-Zustand, sonst kann dir hier wirklich niemand weiterhelfen.

Hi malignate,

Damit sollte es auch möglich sein, mehrere Mio Linien mit 30FPS zu zeichnen (je nach Graka).

Eine Linie wird mit 4 Vertices bzw. 2 Dreiecken gerendert, Kurven mit entsprechend mehr Vertices und Dreiecken. Selbst wenn es _möglich _ist, diese Anzahl von Objekten zu zeichnen, ist es noch lange nicht notwendig. Ganz unabhängig ob mit WPF, DirectX oder sonstwas. (Btw. warum postest du dazu ausgerechnet ein WebGL-Beispiel? Das paßt hier gerade nicht so richtig.)

Christian

Weeks of programming can save you hours of planning

742 Beiträge seit 2005
vor 9 Jahren

Hi MrSparkle,

also ich sehe in dem Link Beispiele zu SharpDX und ein kurze Erklärung zu Matrizen mit weiterführenden Links. Das sollte schon helfen.

Ob jetzt 1 Mio Linien notwendig sind, weiß ich nicht, ich kenne sein Szenario nicht. Ich kann mir schon vorstellen, dass bei irgendwelchen Pläne mit viel Kurven schnell eine große Menge zusammenkommt und wenn die Grafikkarte dann immer noch an Unterkühlung leidet, braucht man sich ja keine Gedanken machen, ob man Culling, Quadtrees oder was auch immer einsetzen will. Natürlich wird man bei 1 Mio weißen Linien nur einen fetten weißen Fleck am Monitor sehen, wenn diese alle gleichmäßig auf die sichtbare Fläche verteilt sind, aber das ist ja nicht der Fall, weil er ja z.B. auch in einem Plan navigieren möchte.

Z
zeh-scharb Themenstarter:in
32 Beiträge seit 2015
vor 9 Jahren

Um was geht es eigentlich? Am Anfang hast du von DirectX geredet, jetzt sprichst du von einer WinForms-Anwendung, die ein WPF-Control hostet...?

Gegeben ist eine Windows Forms Anwendung, die ein neues Feature benötigt und zwar die Darstellung einer Karte. Die Karte benötigt die Funktionen Scrollen und Zoomen, weil sie sehr groß und detailiert ist.
Die Definition der Abbildung liegt vor als Double[]={startLinieX1, startLinieY1, endLinieX1, endLinieY1, startLinieX2, startLinieY2, endLinieX2, endLinieY2,.......................}

Deswegen habe ich ein WPF-Control eingefügt. Und WPF nutzt ja die Hardwarebeschleunigung mit DirectX. Die Anzahl der Striche lässt sich nicht reduzieren, weil sie gegeben ist. Es wird jedes Detail benötigt.
Weil das mit dem WPF-Control Element zu langsam war habe ich mit SlimDX und SharpFX angefangen.

Natürlich wird man bei 1 Mio weißen Linien nur einen fetten weißen Fleck am Monitor sehen, wenn diese alle gleichmäßig auf die sichtbare Fläche verteilt sind

Das ist korrekt. Wenn man ganz weit heraus-zommt, sodass man die komplette Karte sehen kann, dann sieht man teilweise gefüllte Flächen. Das ist so beabsichtigt. Ich erhöhe dazu bei jedem Schritt des Herauszoomens extra die Strichstärke, weil die feinen Linien sonst ab einem gewissen Faktor garnicht mehr angezeigt werden und vom Bild verschwinden.
Wird das Bild vergrößtert, so werden die Striche wieder dünner gezeichnet, sodass man die Details erkennen kann.

742 Beiträge seit 2005
vor 9 Jahren

Ich glaube mit SharpDX solltest du dann weit kommen. Linien haben in DirectX glaube ich immer eine Breite von einem Pixel, unabhängig von ihrem Abstand zur Kamera etc.

Wenn du die Linien-Dicke dynamisch machen willst, musst du Recktecke erzeugen.

189 Beiträge seit 2014
vor 9 Jahren

Aber wenn du eh die Linienstärke ändern musst, damit die Linien ab einem bestimmten Zoom-Level überhaupt noch sichtbar sind, ist es IMO logischer, wenn du anfängst mehrere Linien zu einer Fläche zusammenzufügen. Immer so, dass es dem Zoom-Level genügt.
Dann hast du eben nicht mehr 200000 Linien, sondern 50000 Rechtecke -> das sollte doch immernoch genügen?
Es scheint mir nicht sinnvoll zu sein den Anspruch zu erheben, dass ein User in einem Bildausschnitt mehrere tausend Einzelobjekte erkennen muss.

EDIT: Ich weiß nicht ob du den Punkt erreichst, aber irgendwann ist es doch eh so, dass auf einem physischen Pixel mehrere Linien liegen ... .

5.658 Beiträge seit 2006
vor 9 Jahren

Hi zeh-scharb,

Gegeben ist eine Windows Forms Anwendung, die ein neues Feature benötigt und zwar die Darstellung einer Karte. Die Karte benötigt die Funktionen Scrollen und Zoomen, weil sie sehr groß und detailiert ist.

Da das mit dem ursprünglichen Thema nichts zu tun hat, habe ich den Beitrag mal abgeteilt.

Ansonsten sind die zwei Sätze aber auch nicht besonders aufschlußreich. Was du als Karte bezeichnest, kann für jemanden anderen etwas völlig verschiedenes sein. Anwendungen, die einen Lageplan oder einen Bauplan anzeigen sollen, haben völlig andere Anforderungen als Anwendungen, die eine Landkarte anzeigen sollen.

In den wenigsten Fällen, wirst du jedenfalls der erste sein, der auf ein solches Problem stößt. Hast du denn schonmal geschaut, wie andere dein Problem lösen? Es existieren doch mehrere unterschiedliche Ansätze für die jeweilgen Anforderungen, das kann man sich doch wenigstens vorher einmal anschauen. Dann muß man auch nicht mehr so ins Blaue hinein raten wie bei deinen bisherigen Beschreibungen.

Wenn es dir nur darum geht, soviele Linien wie möglich zu zeichnen, mußt du uns schonmal genauere Infos geben als "200k Linien". Was dauert wielange? Und wie zeichnest du die Linien? Und was hast du schon probiert, um deine Herangehensweise zu optimieren?

Christian

Weeks of programming can save you hours of planning

Z
zeh-scharb Themenstarter:in
32 Beiträge seit 2015
vor 9 Jahren

Ich glaube mit SharpDX solltest du dann weit kommen. Linien haben in DirectX glaube ich immer eine Breite von einem Pixel, unabhängig von ihrem Abstand zur Kamera etc.

Nehme jetzt Linien mit 1px Breite, um es einfacher zu halten.

Ich habe mit dem Beispiel "SwitchContext.WPF" von den SharpDX Samples etwas rumprobiert.
Geschwindigkeit ist top! 40mio Linien in unter 2 Sekunden.
Von der Sache her genau das was ich brauche. Ich kippe die ganzen Koordinaten einmal in den VertexBuffer und den Rest macht die Karte. Arbeitsspeicherverbrauch ist nun auch völlig im Rahmen.

MiniCube.cs


    public class MiniCubeGame : Game
    {
        private GraphicsDeviceManager graphicsDeviceManager;
        private BasicEffect basicEffect;
        private Buffer<VertexPositionColor> vertices;
        private VertexInputLayout inputLayout;

        /// <summary>
        /// Initializes a new instance of the <see cref="MiniCubeGame" /> class.
        /// </summary>
        public MiniCubeGame()
        {
            // Creates a graphics manager. This is mandatory.
            graphicsDeviceManager = new GraphicsDeviceManager(this);
            // Setup the relative directory to the executable directory
            // for loading contents with the ContentManager
            Content.RootDirectory = "Content";
        }

        protected override void LoadContent()
        {
            // Creates a basic effect
            basicEffect = ToDisposeContent(new BasicEffect(GraphicsDevice)
                {
                    VertexColorEnabled = true,
                    View = Matrix.LookAtRH(new Vector3(0, 0, 100), new Vector3(000, 0, 0), Vector3.UnitY),
                    Projection = Matrix.OrthoRH(1000, 1000, 0, 1001.0f),
                    World = Matrix.Identity
                });

            int countVert = 80000000;

            Stopwatch sw = new Stopwatch();
            sw.Restart();
            VertexPositionColor[] liste = new VertexPositionColor[countVert];
            for (int i = 0; i < (countVert - 1); i++)
            {
                liste[i++] = new VertexPositionColor(new Vector3(0, 0, 1.0f), Color.Orange);
                liste[i++] = new VertexPositionColor(new Vector3(1000, (i % 10000) * 0.1f, 1.0f), Color.Orange);
            }
            sw.Stop();
            Console.WriteLine(countVert / 2 + " lines in " + sw.ElapsedMilliseconds);

            // Creates vertices for the cube
            vertices = ToDisposeContent(Buffer.Vertex.New(GraphicsDevice, liste));
            ToDisposeContent(vertices);

            // Create an input layout from the vertices
            inputLayout = VertexInputLayout.FromBuffer(0, vertices);
            base.LoadContent();
        }

        protected override void Initialize()
        {
            Window.Title = "MiniCube demo";
            base.Initialize();
        }

        protected override void Update(GameTime gameTime)
        {
            // Rotate the cube.
            var time = (float)gameTime.TotalGameTime.TotalSeconds;
            //basicEffect.World = Matrix.RotationY(time);
            //  basicEffect.Projection = Matrix.PerspectiveFovRH((float)Math.PI / 0.40f, (float)GraphicsDevice.BackBuffer.Width / GraphicsDevice.BackBuffer.Height, 0.1f, 100.0f);

            //    basicEffect.View = Matrix.LookAtRH(new Vector3(0, 0, zoom), new Vector3(lookX, 0, 0), Vector3.UnitY);        
            //    basicEffect.Projection = Matrix.OrthoRH(zoom, zoom, 0, 1001.0f);          

            Console.WriteLine("UPDATE");
         
            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            // Clears the screen with the Color.CornflowerBlue
            GraphicsDevice.Clear(Color.CornflowerBlue);

            // Setup the vertices
            GraphicsDevice.SetVertexBuffer(vertices);
            GraphicsDevice.SetVertexInputLayout(inputLayout);

            // Apply the basic effect technique and draw the rotating cube
            basicEffect.CurrentTechnique.Passes[0].Apply();
            //     GraphicsDevice.Draw(PrimitiveType.TriangleList, vertices.ElementCount);
            GraphicsDevice.Draw(PrimitiveType.LineList, vertices.ElementCount);

            // Handle base.Draw
            base.Draw(gameTime);
        }
        public void zoomIn()
        {
        }
        public void zoomOut()
        {
        }
    }

MainWindow.xml.cs


namespace MiniCube.SwitchContext.WPF
{
    using System;
    using System.Windows;
    using System.Windows.Controls;
    using SharpDX.Toolkit;

    public partial class MainWindow
    {
        private readonly Game _game;

        public MainWindow()
        {
            InitializeComponent();

            _game = new MiniCubeGame();
            RunGameOn(button1, button2, contentContainer1);
        }

        private void Button1Click(object sender, RoutedEventArgs e)
        {
            RunGameOn(button1, button2, contentContainer1);
        }

        private void Button2Click(object sender, RoutedEventArgs e)
        {
            RunGameOn(button2, button1, contentContainer2);
        }

        private void RunGameOn(UIElement disableButton, UIElement enableButton, ContentControl contentContainer)
        {
            DisposeContent(contentContainer1);
            DisposeContent(contentContainer2);

            disableButton.IsEnabled = false;
            enableButton.IsEnabled = true;

            var element = new SharpDXElement
                          {
                              SendResizeToGame = true,
                              SendResizeDelay = TimeSpan.FromSeconds(2),
                              LowPriorityRendering = false,
                          };

            contentContainer.Content = element;

            if (_game.IsRunning)
                _game.Switch(element);
            else
                _game.Run(element);
        }

        private void DisposeContent(ContentControl contentContainer)
        {
            var element = contentContainer.Content as SharpDXElement;
            contentContainer.Content = null;
            if (element != null)
                element.Dispose();
        }

        private void contentContainer1_PreviewMouseWheel(object sender, System.Windows.Input.MouseWheelEventArgs e)
        {
            if (e.Delta > 0)
                ((MiniCubeGame)_game).zoomIn();
            else
                ((MiniCubeGame)_game).zoomOut();
        }
    }
}


Was mir noch fehlt um es für mich nutzbar zu machen ist:

  • basicEffect.View/basicEffect.Projection verstehen und anpassen, um das Zoomen und Scrollen zu implementieren
  • Kontrolle über die "Update(GameTime gameTime)", damit diese nur ausgeführt wird, wenn etwas passiert. Momentan läuft die Grafikkarte auf Volllast und der Lüfter nervt 👅
742 Beiträge seit 2005
vor 9 Jahren

Sieht gut aus. Eigentlich sollte SharpDX aber nur mit 60FPS laufen (bzw. die Game Klasse die FPS auf 60 reduzieren).

Ich wüsste nicht, wie du das per Einstellung reduzieren kannst, aber im Prinzip kannst du ein Counter hochzählen und nur jeden 2. Frame zeichnen. Evtl. gibst du auch mal die FPS aus.

Wenn du noch weiter optimieren willst, solltest du mal schauen, wie der VertexBuffer initialisiert wird. Je nach Flags kann man da noch viel rausholen (z.B. kann man definieren, dass die CPU nicht mehr drauf zugreifen kann).

Wenn du die Möglichkeit hast, kannst du auch LineStrips verwenden: https://msdn.microsoft.com/de-de/library/windows/desktop/bb174701%28v=vs.85%29.aspx

Z
zeh-scharb Themenstarter:in
32 Beiträge seit 2015
vor 9 Jahren

Aleine durch das Blockieren der Update-Funktion wird das Performance-Problem nicht gelöst. Die GPU brennt trotzdem


    protected override void Update(GameTime gameTime)
        {
            // Rotate the cube.
            var time = (float)gameTime.TotalGameTime.TotalSeconds;
            //basicEffect.World = Matrix.RotationY(time);
            //  basicEffect.Projection = Matrix.PerspectiveFovRH((float)Math.PI / 0.40f, (float)GraphicsDevice.BackBuffer.Width /    GraphicsDevice.BackBuffer.Height, 0.1f, 100.0f);

            //    basicEffect.View = Matrix.LookAtRH(new Vector3(0, 0, zoom), new Vector3(lookX, 0, 0), Vector3.UnitY);        
            //    basicEffect.Projection = Matrix.OrthoRH(zoom, zoom, 0, 1001.0f);          

            if (updateGrafics)
            {
                Console.WriteLine("UPDATE");
                base.Update(gameTime);
                updateGrafics = false;
            }


        }

        protected override void Draw(GameTime gameTime)
        {
            // Clears the screen with the Color.CornflowerBlue
            GraphicsDevice.Clear(Color.CornflowerBlue);

            // Setup the vertices
            GraphicsDevice.SetVertexBuffer(vertices);
            GraphicsDevice.SetVertexInputLayout(inputLayout);

            // Apply the basic effect technique and draw the rotating cube
            basicEffect.CurrentTechnique.Passes[0].Apply();
            //     GraphicsDevice.Draw(PrimitiveType.TriangleList, vertices.ElementCount);
            GraphicsDevice.Draw(PrimitiveType.LineList, vertices.ElementCount);

            // Handle base.Draw
            base.Draw(gameTime);
        }
        public void zoomIn()
        {
            updateGrafics = true;
        }
        public void zoomOut()
        {
            updateGrafics = true;
        }

Z
zeh-scharb Themenstarter:in
32 Beiträge seit 2015
vor 9 Jahren

Hier mein anderes Sample: Windows-Forms-Element mit SlimDx/Direct2D.
Vorteil: Scrollen und Zoomen einfach über die Transform-Matrix. Kein GPU-Load nachdem die Linien gezeichnet wurden.
Nachteil: Langsamer als das 3D-Beispiel (1mio Linien dauern über 2 Sekunden)


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;

//Some namespace mappings
using D2D = SlimDX.Direct2D;
using System.Drawing.Drawing2D;
using SlimDX;
using SlimDX.Direct2D;
using System.Diagnostics;
using SlimDX.Direct3D11;

namespace WindowsFormsMitD2D
{
    public partial class UserControl1 : UserControl
    {
        //Resources for Direct2D rendering
        private D2D.Factory m_factory;
        private D2D.WindowRenderTarget m_renderTarget;
        private D2D.LinearGradientBrush m_backBrushEx;
        private D2D.GradientStopCollection m_backBrushGradient;


        //Some generic members
        private System.Drawing.Brush m_backBrushGdi;
        private bool m_initialized;

        public UserControl1()
        {
            InitializeComponent();

            //Update control styles
            this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            this.SetStyle(ControlStyles.Opaque, true);
            this.SetStyle(ControlStyles.ResizeRedraw, true);

            //Update back brush
            m_backBrushGdi = new SolidBrush(this.BackColor);
        }

        private void InitializeGraphics()
        {
            Console.WriteLine("3");
            //Create factory object
            m_factory = new D2D.Factory(D2D.FactoryType.SingleThreaded, D2D.DebugLevel.None);

            //Create the render target
            m_renderTarget = new D2D.WindowRenderTarget(m_factory, new D2D.WindowRenderTargetProperties()
            {
                Handle = this.Handle,
                PixelSize = this.Size,
                PresentOptions = D2D.PresentOptions.Immediately
            });
            //Create linear gradient brush
            D2D.GradientStop[] gradientStops = new D2D.GradientStop[]
            {
                new D2D.GradientStop(){ Position = 0f, Color = new Color4(Color.LightGray) },
                new D2D.GradientStop(){ Position = 1f, Color = new Color4(Color.LightSteelBlue) }
            };
            m_backBrushGradient = new D2D.GradientStopCollection(m_renderTarget, gradientStops);
            m_backBrushEx = new D2D.LinearGradientBrush(
                m_renderTarget,
                m_backBrushGradient,
                new D2D.LinearGradientBrushProperties()
                {
                    StartPoint = new PointF(0, this.Height),
                    EndPoint = new PointF(0, 0)
                });

            //Update initialization flag
            m_initialized = true;
        }

        /// <summary>
        /// Unloads all graphics resources.
        /// </summary>
        private void UnloadGraphics()
        {
            if (m_backBrushEx != null) { m_backBrushEx.Dispose(); }
            if (m_backBrushGradient != null) { m_backBrushGradient.Dispose(); }
            if (m_renderTarget != null) { m_renderTarget.Dispose(); }
            if (m_factory != null) { m_factory.Dispose(); }

            m_backBrushEx = null;
            m_backBrushGradient = null;
            m_renderTarget = null;
            m_factory = null;
        }

        /// <summary>
        /// Called when window opens.
        /// </summary>
        /// <param name="e">An <see cref="T:System.EventArgs"/> that contains the event data.</param>
        protected override void OnHandleCreated(EventArgs e)
        {
            base.OnHandleCreated(e);

            if ((!m_initialized) || (!this.DesignMode))
            {
                InitializeGraphics();
            }
        }
        /// <summary>
        /// Called when window closes.
        /// </summary>
        /// <param name="e">An <see cref="T:System.EventArgs"/> that contains the event data.</param>
        protected override void OnHandleDestroyed(EventArgs e)
        {
            base.OnHandleDestroyed(e);

            if (m_initialized)
            {
                UnloadGraphics();
            }
        }
        protected override void OnResize(EventArgs e)
        {
            Console.WriteLine("RESIZE! ");
            base.OnResize(e);

            if ((this.Width > 0) && (this.Height > 0))
            {
                if (m_backBrushGdi != null) { m_backBrushGdi.Dispose(); }
                m_backBrushGdi = new System.Drawing.Drawing2D.LinearGradientBrush(
                    new Point(0, this.Height),
                    new Point(0, 0),
                    Color.LightGray,
                    Color.LightSteelBlue);

                if (m_initialized)
                {
                    //Resize render target
                    m_renderTarget.Resize(this.Size);
                    m_backBrushEx.StartPoint = new PointF(0, this.Height);
                }
            }
        }
        /// <summary>
        /// Called when control has to paint itself.
        /// </summary>
        /// <param name="e">A <see cref="T:System.Windows.Forms.PaintEventArgs"/> that contains the event data.</param>
        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);

            if (m_initialized)
            {
                m_renderTarget.AntialiasMode = AntialiasMode.PerPrimitive;
                m_renderTarget.BeginDraw();
                try
                {
                    m_renderTarget.Clear(new Color4(this.BackColor));

                    //Perform all Direct2D rendering here
                    m_renderTarget.FillRectangle(
                       m_backBrushEx,
                       new Rectangle(new Point(), this.Size));

                    //Apply quality
                    //    D2D.InterpolationMode interpolationMode = D2D.InterpolationMode.NearestNeighbor;
                    //    interpolationMode = D2D.InterpolationMode.Linear;


                    Pen p = new Pen(Brushes.Black);
                    //     for (int loopX = 0; loopX < 10000; loopX++)
                    //         e.Graphics.DrawLine(p, new Point(0, 0), new Point(300, loopX));

                    List<Point> liste = new List<Point>();


                    SolidColorBrush bru = new SolidColorBrush(m_renderTarget, new Color4(Color.Black));

                    Stopwatch sw = new Stopwatch();
                    sw.Reset();
                    sw.Start();
                    int obje = 1000000;
                    for (int loopX = 0; loopX < obje; loopX++)
                    {
                        m_renderTarget.DrawLine(bru, new Point(0, 0), new Point(1000, loopX % 10000));

                    }
                    sw.Stop();
                    Console.WriteLine(obje + " objects in " + sw.ElapsedMilliseconds);
                    Console.WriteLine("Matrix " + m_renderTarget.Transform.M11 + "  " + m_renderTarget.Transform.M12 + "  " + m_renderTarget.Transform.M21 + "  " + m_renderTarget.Transform.M22 + "  " + m_renderTarget.Transform.M31 + "  " + m_renderTarget.Transform.M32);
                }

                finally
                {
                    m_renderTarget.EndDraw();
                }
            }
        }
        public void scaleUp()
        {
            Matrix3x2 transMatrix = m_renderTarget.Transform;
            transMatrix.M11++;
            transMatrix.M22++;

            transMatrix.M31 -= (m_renderTarget.Size.Width / 2);
            transMatrix.M32 -= (m_renderTarget.Size.Height / 2);



            m_renderTarget.Transform = transMatrix;
            //    Invalidate();

        }

        public void scaleDown()
        {
            Matrix3x2 transMatrix = m_renderTarget.Transform;

            transMatrix.M31 -= 100;

            m_renderTarget.Transform = transMatrix;
            //   Invalidate();

        }
    }
}


Kann man das vielfache Aufrufen der Funktion

  m_renderTarget.DrawLine(bru, new Point(0, 0), new Point(1000, loopX % 10000));

vermeiden, indem man einen kompletten Array an Linien / Punkten übergibt wie im Beispiel zuvor?

742 Beiträge seit 2005
vor 9 Jahren

Du musst die Draw Methode blockieren, sonst bringts natürlich nichts.

Der Vergleich der 2 Code-Teile hinkt aber auch. In Winforms wird nicht 60x pro Sekunde neugezeichnet, was auch gar nicht notwendig ist. Würdest du jetzt aber in dein Winforms Beispiel auch noch Zooming usw. einfügen, brauchst du wohl mindestens 20 FPS, damit es einigermaßen flüssig aussieht.

Z
zeh-scharb Themenstarter:in
32 Beiträge seit 2015
vor 9 Jahren

Du musst die Draw Methode blockieren, sonst bringts natürlich nichts.

Danke! Nun heizt die GPU nicht mehr.

Parallel versuche ich noch das SlimDX Beispiel schneller zu bekommen, weil es einfacher ist und von der Logik her besser zu dem passt, was ich mir vorstelle. Ich benötige kein 3D.

Der Verzicht auf die Erzeugung der zwei Point-Objekte beim Aufruf der DrawLine-Methode brachte leider kaum Gewinn (~1800ms auf ~1750ms).

for (int loopX = 0; loopX < obje; loopX++)
                        m_renderTarget.DrawLine(bru, 0, 0, 1000, loopX % 10000);

Ich habe bisher leider nicht herausgefunden, wie ich einen Array übergeben kann, um alles mit einem einzigen Methodenaufruf zu verarbeiten.

Z
zeh-scharb Themenstarter:in
32 Beiträge seit 2015
vor 9 Jahren

Ich bin Stück für Stück Anleitungen und Bücher zum Thema durchgegangen, aber das Ergebnis ist leider immernoch zu langsam.
Die Darstellung von 1 Mio Linien sollten eine Kleinigkeit für eine Grafikkarte sein.
Der Aufruf des Konstruktors dauert ca 300ms. Der Erste Click auf "fill" dauert laut Stop-Uhr 120ms, jeder weitere 0ms. Bis die Linien jedoch auf dem Monitor erscheinen vergeht geht eine Sekunde.

Wo liegt mein Fehler, was mache ich falsch?

Windows Form-Anwendung

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace test
{
    public partial class Form2 : Form
    {
        public Form2()
        {
            InitializeComponent();
        }

        private void fill_Click(object sender, EventArgs e)
        {
            linePanel_21.Render(true);
        }

        private void clear_Click(object sender, EventArgs e)
        {
            linePanel_21.Render(false);
        }
    }
}

Panel

using SlimDX;
using SlimDX.Direct3D9;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace test
{
    class LinePanel_2 : Panel
    {
        VertexBuffer vertexBuffer;
        private Device device = null;
        private PresentParameters presentParams = null;
        VertexDeclaration vertexDecl;
        int count = 2000000;

        public LinePanel_2()
        {
            //Device-Beschreibung
            presentParams = new PresentParameters();
            presentParams.SwapEffect = SwapEffect.Discard;
            presentParams.Windowed = true;

            Direct3D d3d = new Direct3D();
            device = new Device(d3d, 0, DeviceType.Hardware, this.Handle, CreateFlags.HardwareVertexProcessing, presentParams);

            ColoredVertex[] vecListe = new ColoredVertex[count];
            for (int i = 0; i < (count - 1); i += 2)
            {
                vecListe[i] = new ColoredVertex(new Vector3(0, 0, 1), Color.Red.ToArgb());
                vecListe[i + 1] = new ColoredVertex(new Vector3(i % 1000, (i + 30) % 1000, 1), Color.Red.ToArgb());
            }

            vertexBuffer = new VertexBuffer(device, count * Marshal.SizeOf(typeof(ColoredVertex)), Usage.WriteOnly, VertexFormat.None, Pool.Managed);

            var stream = vertexBuffer.Lock(0, 0, LockFlags.None);
            stream.WriteRange(vecListe);
            vertexBuffer.Unlock();

            device.SetRenderState(RenderState.Lighting, false);

            vertexDecl = new VertexDeclaration(device, new[] {
                                new VertexElement(0, 0, DeclarationType.Float3, DeclarationMethod.Default, DeclarationUsage.Position, 0),
                                new VertexElement(0, 12, DeclarationType.Color, DeclarationMethod.Default, DeclarationUsage.Color, 0),
                                VertexElement.VertexDeclarationEnd  });
        }

        public void Render(Boolean fillMode)
        {
            Stopwatch sw = new Stopwatch();
            sw.Restart();
            device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, new Color4(0.3f, 0.3f, 0.3f), 1.0f, 0);

            if (fillMode)
            {
                device.BeginScene();
                device.SetStreamSource(0, vertexBuffer, 0, Marshal.SizeOf(typeof(ColoredVertex)));
                device.VertexDeclaration = vertexDecl;
                device.DrawPrimitives(PrimitiveType.LineList, 0, count / 2);
                device.EndScene();             
            }

            // +++++++++++++++++++++++++
            //  Funktion dauert lange
            device.Present();
            //++++++++++++++++++++++++++

            sw.Stop();
            Console.WriteLine(count / 2 + " lines in " + sw.ElapsedMilliseconds);
        }

        [StructLayout(LayoutKind.Sequential)]
        struct ColoredVertex
        {
            public Vector3 Position;
            public int Color;

            public ColoredVertex(Vector3 v, int c)
            {
                Position = v;
                Color = c;
            }

            public static readonly VertexFormat Format = VertexFormat.Position | VertexFormat.Diffuse;
        }
    }
}

5.658 Beiträge seit 2006
vor 9 Jahren

Hi zeh-scharb,

Die Darstellung von 1 Mio Linien sollten eine Kleinigkeit für eine Grafikkarte sein.

Woher stammt diese Aussage? Auf welchen Grafikkarten hast du dein Programm getestet?

Der Erste Click auf "fill" dauert laut Stop-Uhr 120ms, jeder weitere 0ms. Bis die Linien jedoch auf dem Monitor erscheinen vergeht geht eine Sekunde.

Du mißt die Zeit, die es braucht, deine Daten auf die Grafikkarte zu übertragen, aber nicht die Zeit, die die Grafikkarte mit der Darstellung verbringt.

Wo liegt mein Fehler, was mache ich falsch?

Ich denke einfach, daß deine Annahme bezüglich der Performance falsch ist. Wie ich schon ganz am Anfang angedeutet hatte, fällt mir kein Anwendungs-Beispiel ein, wo man die Anforderung hätte, 1 Mio Linien gleichzeitig auf dem Monitor darzustellen.

Christian

Weeks of programming can save you hours of planning

742 Beiträge seit 2005
vor 9 Jahren

Lade doch mal irgendwo eine kompilierbare Solution hoch (auch mit SharpDX, wenn kein NuGet), dann kann ich ja mal schauen, wie schnell es bei mir läuft und vll. finde ich ja auch ein Fehler.

Z
zeh-scharb Themenstarter:in
32 Beiträge seit 2015
vor 9 Jahren

Auf welchen Grafikkarten hast du dein Programm getestet?

-integrierte Intel HD
-NVidia Quattro K1100M

fällt mir kein Anwendungs-Beispiel ein, wo man die Anforderung hätte, 1 Mio Linien gleichzeitig auf dem Monitor darzustellen.

Zum Beispiel CAD-Programme, wenn Du Drahtgittermodelle darstellen willst.

Lade doch mal irgendwo eine kompilierbare Solution hoch (auch mit SharpDX, wenn kein NuGet), dann kann ich ja mal schauen, wie schnell es bei mir läuft und vll. finde ich ja auch ein Fehler.

Vielen Dank 👍WindowsFormsSlim.zip

742 Beiträge seit 2005
vor 9 Jahren

Also bei mir dauert der erste Call 87ms und dann nur noch 0.7 / 0.8 .... d.h 1200 FPS.

NVIDIA Gforce GTX 970. Ich würde dir mal raten, einfach mal eine Anwendung zu bauen mit Kamera Bewegung und dann die FPS nochmal messen. Solche Kleinen Tests haben keine Aussagekraft. Nimm dir ein Game Gerüst, fixiere die FPS auf max. 30 und dann zeichne die ein Gitter mit 1 Mio Linien und bewege dich drin herum. Ich glaube nicht, dass du ein Performance Problem bekommst. Das würde auch ein iPad wahrscheinlich hinkriegen.

Natürlich hast du einigermaßen viel Geometrie, aber kaum Operationen pro Pixel. Der muss einfach nur auf eine Farbe gesetzt werden. In Games wierden hier Normalen interpoliert, die Matrix Multiplikationen pro Pixel durchgeführt und das ganze vll. sogar mehrfach. Bei einem Drahtgittermodell ohne Beleuchtung dürfte sich jede normale Grafikkarte eher langweilen.

Z
zeh-scharb Themenstarter:in
32 Beiträge seit 2015
vor 9 Jahren

Bei einem Drahtgittermodell ohne Beleuchtung dürfte sich jede normale Grafikkarte eher langweilen.

Vielen Dank 👍
Ich habe jetzt meine Lösung mit der ich erstmal zufrieden bin. Durch das Anpassen der App.config läuft nun auch Microsoft.DirectX mit .Net 4.5.1

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <startup useLegacyV2RuntimeActivationPolicy="true">
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.1"/>
  </startup>
</configuration>


using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using System;
using System.Drawing;
using System.Windows.Forms;

namespace WindowsFormsDirect
{
    public partial class DirectPanel : Panel
    {
        private Device m_oDevice = null;
        private PresentParameters m_oPresentParams = null;

        VertexBuffer oVertexBuffer;
        Boolean scrolling = false;
        int mouseX = 0;
        int mouseY = 0;

        int diffX = 0;
        int difY = 0;
        int zoomLevel = -100;

        int count = 2000000;

        public directPanel()
        {
            InitializeComponent();

            //Device-Beschreibung
            m_oPresentParams = new PresentParameters();
            m_oPresentParams.SwapEffect = SwapEffect.Discard;
            m_oPresentParams.Windowed = true;

            // Hier wird auf den HANDLE gezeigt "this"
            m_oDevice = new Device(0, DeviceType.Hardware, this, CreateFlags.HardwareVertexProcessing, m_oPresentParams);

            m_oDevice.RenderState.ShadeMode = ShadeMode.Gouraud;

            //Vertex-Buffer erstellen
            oVertexBuffer = new VertexBuffer(typeof(CustomVertex.PositionColored), count, m_oDevice, Usage.None,
            CustomVertex.PositionColored.Format, Pool.Managed);

            CustomVertex.PositionColored[] vecListe = new CustomVertex.PositionColored[count];
            for (int i = 0; i < (count - 1); i += 2)
            {
                vecListe[i].X = 0; vecListe[i].Y = 0; vecListe[i].Z = 1; vecListe[i].Color = Color.Orange.ToArgb();
                vecListe[i + 1].X = i % 1000; vecListe[i + 1].Y = (i + 30) % 940; vecListe[i + 1].Z = 1; vecListe[i + 1].Color = Color.Orange.ToArgb();
            }
            oVertexBuffer.SetData(vecListe, 0, LockFlags.None);

            this.MouseWheel += new System.Windows.Forms.MouseEventHandler(this.direct_MouseWheel);
        }


        public void Render()
        {
            if (m_oDevice == null)
                return;

            m_oDevice.Clear(ClearFlags.Target, Color.Blue, 1.0f, 0);
            m_oDevice.BeginScene();

            m_oDevice.RenderState.CullMode = Cull.None;
            m_oDevice.RenderState.Lighting = false;

            m_oDevice.Transform.Projection = Matrix.PerspectiveFovLH((float)Math.PI / 4, (float)(m_oPresentParams.BackBufferWidth / m_oPresentParams.BackBufferHeight), 1.0f, 500.0f);
            m_oDevice.Transform.View = Matrix.LookAtLH(new Vector3(diffX, difY, zoomLevel), new Vector3(diffX, difY, 0), new Vector3(0, 1, 0));

            m_oDevice.VertexFormat = CustomVertex.PositionColored.Format;
            m_oDevice.SetStreamSource(0, oVertexBuffer, 0);
            m_oDevice.DrawPrimitives(PrimitiveType.LineList, 0, count / 2);

            m_oDevice.EndScene();
            m_oDevice.Present();
        }

        private void InitializeComponent()
        {
            this.SuspendLayout();
            this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.directPanel_MouseDown);
            this.MouseEnter += new System.EventHandler(this.directPanel_MouseEnter);
            this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.directPanel_MouseMove);
            this.MouseUp += new System.Windows.Forms.MouseEventHandler(this.directPanel_MouseUp);
            this.ResumeLayout(false);
        }

        private void directPanel_MouseDown(object sender, MouseEventArgs e)
        {
            mouseX = e.X;
            mouseY = e.Y;
            scrolling = true;
        }

        private void directPanel_MouseUp(object sender, MouseEventArgs e)
        {
            scrolling = false;
        }

        private void directPanel_MouseMove(object sender, MouseEventArgs e)
        {
            if (scrolling)
            {
                diffX -= (e.X - mouseX);
                difY += (e.Y - mouseY);
                Console.WriteLine("Moved : " + diffX + "  " + difY);
                mouseX = e.X;
                mouseY = e.Y;
                Render();
            }
        }

        private void directPanel_MouseEnter(object sender, EventArgs e)
        {
            this.Focus();
        }
        private void direct_MouseWheel(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            if (e.Delta < 0)
                zoomLevel += 10;
            else
                zoomLevel -= 10;
            Render();
        }
    }
}

F
10.010 Beiträge seit 2004
vor 9 Jahren

Nur so, bei ner HD4000 gibst nur ne Exception und der Graka Treiber unter W8.1 reinitialisiert sich mit deiner Beispielanwendung.

Z
zeh-scharb Themenstarter:in
32 Beiträge seit 2015
vor 9 Jahren

Nur so, bei ner HD4000 gibst nur ne Exception und der Graka Treiber unter W8.1 reinitialisiert sich mit deiner Beispielanwendung.

Vielen Dank für Dein Feedback.
Ich entwickel mit **Windows 7 **und dem "DXSDK_Jun10.exe". Auf einem anderen Win7-Rechner mit einer Radeon HD6450 Karte läuft es auch fehlerfrei.
Was für eine Exception fliegt denn genau?

F
10.010 Beiträge seit 2004
vor 9 Jahren

Wie gesagt, nach drücken von Fill, gibt es eine Meldung das der GraKa Treiber restarted wird und danach gibt es "D3DERR_DEVICELOST: Device lost (-2005530520)"