C#如何提高Direct2D绘图的效率

时间:2019-03-21 17:07:19

标签: c# direct2d

早上好

我已经在C#中教自己一些Direct2D编程,利用可用的本机包装器(当前使用d2dSharp,但也尝试过SharpDX)。但是,我遇到了效率问题,在这种情况下,基本绘图Direct2D绘图方法大约需要250毫秒才能绘制45,000个基本多边形。我所看到的性能与Windows GDI +相当,甚至更低。我希望有人可以看看我所做的事情,并提出一种可以极大地缩短绘制时间的方法。

背景是我有一个个人项目,在这个项目中,我正在开发一个基本但功能强大的CAD界面,该界面能够执行各种任务,包括2D有限元分析。为了使其完全有用,该界面需要能够显示数以万计的基本元素(多边形,圆形,矩形,点,弧形等)。

我最初是使用Windows GDI +(System.Drawing)编写绘图方法的,在直到在任何给定时间屏幕上达到约3,000个元素之前,性能都相当不错。用户每次平移,缩放,绘制新元素,删除元素,移动,旋转等时,都必须更新屏幕。现在,为了提高效率,我使用四叉树数据结构来存储我的元素,而我只绘制实际在控制窗口范围内的元素。这在放大时有很大帮助,但是很明显,在完全缩小并显示所有元素时,没有什么区别。我还使用计时器和滴答事件以刷新频率(60 Hz)更新屏幕,因此,我没有试图每秒或每次鼠标事件更新数千次。

这是我第一次使用DirectX和Direct2D进行编程,因此我肯定在这里学习。话虽如此,我已经花了几天的时间回顾教程,示例和论坛,但发现没有什么帮助。我尝试了十二种不同的绘制,预处理,多线程等方法。下面是我的代码

用于遍历和绘制元素的代码

List<IDrawingElement> elementsInBounds = GetElementsInDraftingWindow();

_d2dContainer.Target.BeginDraw();
_d2dContainer.Target.Clear(ColorD2D.FromKnown(Colors.White, 1));

if (elementsInBounds.Count > 0)
{
    Stopwatch watch = new Stopwatch();
    watch.Start();

    #region Using Drawing Element DrawDX Method

    foreach (IDrawingElement elem in elementsInBounds)
    {
        elem.DrawDX(ref _d2dContainer.Target, ref _d2dContainer.Factory, ZeroPoint, DrawingScale, _selectedElementBrush, _selectedElementPointBrush);
    }

    #endregion

    watch.Stop();
    double drawingTime = watch.ElapsedMilliseconds;
    Console.WriteLine("DirectX drawing time = " + drawingTime);
    watch.Reset();
    watch.Start();

    Matrix3x2 scale = Matrix3x2.Scale(new SizeFD2D((float)DrawingScale, (float)DrawingScale), new PointFD2D(0, 0));
    Matrix3x2 translate = Matrix3x2.Translation((float)ZeroPoint.X, (float)ZeroPoint.Y);

    _d2dContainer.Target.Transform = scale * translate;

    watch.Stop();
    double transformTime = watch.ElapsedMilliseconds;
    Console.WriteLine("DirectX transform time = " + transformTime);
}

用于多边形的DrawDX函数

public override void DrawDX(ref WindowRenderTarget rt, ref Direct2DFactory fac, Point zeroPoint, double drawingScale, SolidColorBrush selectedLineBrush, SolidColorBrush selectedPointBrush)
    {
        if (_pathGeometry == null)
        {
            CreatePathGeometry(ref fac);
        }

        float brushWidth = (float)(Layer.Width / (drawingScale));
        brushWidth = (float)(brushWidth * 2);

        if (Selected == false)
        {
            rt.DrawGeometry(Layer.Direct2DBrush, brushWidth, _pathGeometry);
            //Note that _pathGeometry is a PathGeometry
        }
        else
        {
            rt.DrawGeometry(selectedLineBrush, brushWidth, _pathGeometry);
        }
    }

用于创建Direct2D Factory和渲染目标的代码

private void CreateD2DResources(float dpiX, float dpiY)
    {
        Factory = Direct2DFactory.CreateFactory(FactoryType.SingleThreaded, DebugLevel.None, FactoryVersion.Auto);

        RenderTargetProperties props = new RenderTargetProperties(
            RenderTargetType.Default, new PixelFormat(DxgiFormat.B8G8R8A8_UNORM,
            AlphaMode.Premultiplied), dpiX, dpiY, RenderTargetUsage.None, FeatureLevel.Default);

        Target = Factory.CreateWindowRenderTarget(_targetPanel, PresentOptions.None, props);
        Target.AntialiasMode = AntialiasMode.Aliased;

        if (_selectionBoxLeftStrokeStyle != null)
        {
            _selectionBoxLeftStrokeStyle.Dispose();
        }

        _selectionBoxLeftStrokeStyle = Factory.CreateStrokeStyle(new StrokeStyleProperties1(LineCapStyle.Flat,
                LineCapStyle.Flat, LineCapStyle.Flat, LineJoin.Bevel, 10, DashStyle.Dash, 0, StrokeTransformType.Normal), null);
    }

我创建一个Direct2D工厂并渲染一次目标,并始终保留对其的引用(这样,我就不会每次都在重新创建)。创建绘图层(描述颜色,宽度等)时,我还将创建所有画笔。因此,我并不是在每次绘制时都创建一个新的画笔,只是引用一个已经存在的画笔。与几何形状相同,如第二个代码段所示。我只创建一次几何图形,并且仅在元素本身移动或旋转时才更新几何图形。否则,我只需在绘制后将转换应用于渲染目标即可。

根据我的秒表,遍历并调用elem.DrawDX方法所需的时间约为225-250毫秒(对于45,000个多边形)。应用转换所花费的时间是0-1毫秒,因此似乎瓶颈在RenderTarget.DrawGeometry()函数中。

我已经使用RenderTarget.DrawEllipse()或RenderTarget.DrawRectangle()进行了相同的测试,因为我已经知道使用DrawGeometry比DrawRectangle或DrawEllipse慢,因为事先知道矩形/椭圆几何。但是,在我所有的测试中,我使用哪个绘制函数都没有关系,相同数量的元素的时间总是相等的。

我尝试过建立多线程Direct2D工厂并通过任务运行绘图功能,但这要慢得多(大约慢两倍)。 Direct2D方法似乎正在使用我的图形卡(启用了硬件加速),因为当我监视图形卡使用情况时,它会在屏幕更新时达到峰值(我的笔记本电脑装有NVIDIA Quadro移动图形卡)。

道歉的帖子的道歉。我希望这是我尝试过的足够的背景知识和描述。预先感谢您的帮助!

编辑#1 因此将代码从使用foreach遍历列表更改为使用for遍历数组将代码绘制时间缩短了一半!我没有意识到列表比数组慢多少(我知道有一些性能上的优势,但是没有意识到!)。但是,绘制仍需要125毫秒。这好多了,但仍然不顺利。还有其他建议吗?

1 个答案:

答案 0 :(得分:0)

Direct2D可以与P / Invoke一起使用 请参见示例“ VB Direct2D像素完美碰撞” 来自https://social.msdn.microsoft.com/Forums/en-US/cea42526-4b82-454d-9d79-2e1d94083552/collisions?forum=vbgeneral 动画是完美的,甚至可以在VB中完成