Abgeteilt von Direct2D: viele Linien zeichnen
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:
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?
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
Habe es endlich geschafft die "Wpf Performance Suite" ans Laufen zu bekommen.
Dazu war es nötig:
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>
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).
ImageTools for Silverlight: http://imagetools.codeplex.com | http://www.silverdiagram.net | http://www.cleancodedeveloper.de b:::
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
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.
ImageTools for Silverlight: http://imagetools.codeplex.com | http://www.silverdiagram.net | http://www.cleancodedeveloper.de b:::
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.
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.
ImageTools for Silverlight: http://imagetools.codeplex.com | http://www.silverdiagram.net | http://www.cleancodedeveloper.de b:::
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 ... .
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
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:
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
ImageTools for Silverlight: http://imagetools.codeplex.com | http://www.silverdiagram.net | http://www.cleancodedeveloper.de b:::
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;
}
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?
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.
ImageTools for Silverlight: http://imagetools.codeplex.com | http://www.silverdiagram.net | http://www.cleancodedeveloper.de b:::
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.
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;
}
}
}
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
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.
ImageTools for Silverlight: http://imagetools.codeplex.com | http://www.silverdiagram.net | http://www.cleancodedeveloper.de b:::
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
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.
ImageTools for Silverlight: http://imagetools.codeplex.com | http://www.silverdiagram.net | http://www.cleancodedeveloper.de b:::
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();
}
}
}
Nur so, bei ner HD4000 gibst nur ne Exception und der Graka Treiber unter W8.1 reinitialisiert sich mit deiner Beispielanwendung.
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?
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)"