在寻找替代GDI的替代方案时,我试图在Windows 7中测试Delphi的2010 TDirect2DCanvas性能.
我通过使用Direct2D绘制一条巨大的折线来测试它,结果非常慢,即使数据量少于我使用GDI进行相同测试的数量少500倍(我甚至没有在GDI中使用位图作为后备缓冲,我只是直接绘制到表单画布).
所以我想:
a) Direct2D比GDI慢;
b) TDirect2DCanvas很慢;
c)我做错了什么
,希望它是c).
我写的测试代码是:
unit Unit2;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, Direct2D, D2D1;
type
TForm2 = class(TForm)
private
{ Private declarations }
FD2DCanvas: TDirect2DCanvas;
FData: array[0..50000] of TPoint;
public
procedure CreateWnd; override;
procedure WMSize(var Message: TWMSize); message WM_SIZE;
procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
{ Public declarations }
end;
var
Form2: TForm2;
implementation
uses utils;
{$R *.dfm}
procedure TForm2.CreateWnd;
var
i: Integer; …
Run Code Online (Sandbox Code Playgroud) 我正在编程模拟,我想将我的应用程序从使用GDI移植到使用Direct2D.但我的Direct2D代码比我的GDI代码要慢得多.
我在屏幕上渲染了很多椭圆.在我的GDI应用程序中,我绘制到内存设备上下文,然后使用BitBlt在Windows设备上下文中绘制.使用Direct2D,我绘制到ID2D1HwndRenderTarget.
我的问题是,当使用GDI时,我可以轻松绘制400多个椭圆,并且仍然有400 FPS.当我使用Direct2D执行相同数量的省略号时,我的FPS下降到30FPS.
我已经关闭了antialiasing,但它并没有真正帮助.有趣的是,与GDI相比,在Direct2D中绘制几个省略号更快.我可以做些什么来提高Direct2D的性能,还是应该使用GDI保留我的应用程序?
这是我使用GDI的绘图代码:
VOID Begin() {
SelectObject(this->MemDeviceContext, this->MemoryBitmap);
this->BackgroundBrush = CreateSolidBrush(this->BackgroundColor);
HBRUSH OldBrush = (HBRUSH)SelectObject(this->MemDeviceContext, this->BackgroundBrush);
Rectangle(this->MemDeviceContext, -1, -1, 801, 601);
SelectObject(this->MemDeviceContext, OldBrush);
DeleteObject(this->BackgroundBrush);
SetViewportOrgEx(this->MemDeviceContext, 400, 300, &this->OldOrigin);
}
VOID End() {
SetViewportOrgEx(this->MemDeviceContext, this->OldOrigin.x, this->OldOrigin.y, 0);
BitBlt(this->DeviceContext, 0, 0, 800, 600, this->MemDeviceContext, 0, 0, SRCCOPY);
}
Run Code Online (Sandbox Code Playgroud)
在我的Begin和End函数之间,我使用标准的GDI方式绘制省略号.
以下是使用Direct2D的开始和结束函数:
VOID BeginDrawing() {
this->RenderTarget->BeginDraw();
RenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::CornflowerBlue));
RenderTarget->SetTransform(this->ScalingMatrix * this->TranslateMatrix);
}
VOID EndDrawing() {
this->RenderTarget->EndDraw();
}
Run Code Online (Sandbox Code Playgroud)
以下是我如何设置Direct2D接口.这一切都包括在课堂上; 这就是我无法发布完整代码的原因:
if(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &Direct2DFactory) != S_OK)
throw std::runtime_error("RENDERWINDOW::InitializeDirect2D: Failed to create a factory interface.");
RECT …
Run Code Online (Sandbox Code Playgroud) 我正在开发的绘图应用程序Visual C++
通过以下方式Direct2D
.我有一个演示应用程序,其中:
// create the ID2D1Factory
D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &m_pDirect2dFactory);
// create the main window
HWND m_hwnd = CreateWindow(...);
// set the render target of type ID2D1HwndRenderTarget
m_pDirect2dFactory->CreateHwndRenderTarget(
D2D1::RenderTargetProperties(),
D2D1::HwndRenderTargetProperties(m_hwnd, size),
&m_pRenderTarget
);
Run Code Online (Sandbox Code Playgroud)
当我收到WM_PAINT
消息时,我会画出我的形状.
现在我需要开发一个代表我的新渲染目标的WPF控件(一种Panel)(因此它将替换主窗口m_hwnd
),这样我就可以创建一个新的(C#)WPF项目,其主窗口具有子窗口我的自定义面板,而渲染部分保留在本机C++/CLI DLL项目中.
我怎样才能做到这一点?我必须设置为新的渲染目标?
我想用我的WPF窗口的句柄:
IntPtr windowHandle = new WindowInteropHelper(MyMainWindow).Handle;
Run Code Online (Sandbox Code Playgroud)
但我需要在我的面板上画画,而不是在我的窗户上.
请注意,我不想将任何WPF类用于渲染部分(Shapes
,DrawingVisuals
...)
我正在尝试进行垂直同步渲染,以便每个垂直同步完成一次渲染,而不跳过或重复任何帧.我需要这个在Windows 7和(将来)Windows 8下工作.
它基本上包括绘制QUADS
适合屏幕的序列,以便原始图像中的像素与屏幕上的1:1像素匹配.使用OpenGL或DirectX,渲染部分不是问题.问题是正确的同步.
我以前尝试使用OpenGL,WGL_EXT_swap_control
扩展,绘图然后调用
SwapBuffers(g_hDC);
glFinish();
Run Code Online (Sandbox Code Playgroud)
我尝试了这两个指令的所有组合和置换以及glFlush(),并且它不可靠.
然后我尝试使用Direct3D 10,通过绘图然后调用
g_pSwapChain->Present(1, 0);
pOutput->WaitForVBlank();
Run Code Online (Sandbox Code Playgroud)
其中g_pSwapChain
是IDXGISwapChain*
与pOutput
是IDXGIOutput*
关联到该SwapChain.
两个版本,OpenGL和Direct3D,都是相同的:第一个序列,比方说,60帧,并没有达到应有的水平(而不是大约1000毫秒,60hz,持续时间等于1030或1050ms),以下似乎工作正常(约1000.40ms),但不时似乎跳过一帧.我做测量QueryPerformanceCounter
.
在Direct3D上,尝试仅使用WaitForVBlank的循环,1000次迭代的持续时间始终为1000.40,几乎没有变化.
因此,这里的问题不是确切地知道每个被调用的函数何时返回,以及交换是否在垂直同步期间完成(不是更早,以避免撕裂).
理想情况下(如果我没有记错),要实现我想要的,它将执行一次渲染,等待同步开始,在同步期间交换,然后等待直到同步完成.如何使用OpenGL或DirectX做到这一点?
编辑:
仅WaitForVSync
60x的测试循环始终从1000.30ms到1000.50ms.与Present(1,0)
之前相同的循环WaitForVSync
,没有别的,没有渲染,需要相同的时间,但有时它会失败并需要1017毫秒,就像重复一帧一样.没有渲染,所以这里有问题.
令我很生气的是,在Windows中调整窗口的大小并不像我希望的那样"平滑"(Windows程序通常就是这种情况,而不仅仅是我自己的.Visual Studio就是一个很好的例子).它使操作系统及其程序感觉"脆弱"和"便宜"(是的,我关心程序和用户界面的感觉,就像我关心关闭车门的声音和感觉一样.这是构建的反映质量),在我看来影响整体用户体验,最终影响品牌的感知.
重新调整大小时,重新绘制窗口内容无法跟上鼠标移动的步伐.每当我调整窗口大小时,都会出现"口吃"/"闪烁"效果,这似乎是由于在绘制新的已调整大小的内容之前,在新的已调整大小的窗口框架中重绘了窗口的先前大小内容.
我正在构建一个使用Direct2D 1.1绘制UI的Win32应用程序(x64),并且考虑到Direct2D的速度,我认为在2014年操作系统中不应该有这样的工件.我自己在Windows 8.1上,但是目标是Windows 7及以上版本的应用程序.
当最大化小窗口时,"先前大小"效果尤其明显(因为窗口大小的差异足以容易地对比旧内容的图像,因为它在较大窗口的左上角短暂闪烁与新内容随后被涂在上面).
这似乎是正在发生的事情:
我想知道是否有任何方法可以缓解这种情况(即摆脱第4步) - 例如通过拦截Windows消息 - 并避免在最终重新呈现之前使用旧内容重新绘制新大小的窗口新内容发生了.这就像Windows窗口重绘自己,使用已有的任何图形,之前很难要求我提供WM_PAINT消息或类似的更新内容.
可以吗?
编辑:似乎 WM_WINDOWPOSCHANGING
/WM_SIZING
提供了对新大小数据的"早期访问",但我仍然没有设法抑制旧内容的绘制.
我WndProc
看起来像这样:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_ERASEBKGND:
return 1;
case WM_PAINT:
PAINTSTRUCT ps;
BeginPaint(hWnd, &ps);
D2DRender();
EndPaint(hWnd, &ps);
return 0;
case WM_SIZE:
if (DeviceContext && wParam != SIZE_MINIMIZED)
{
D2DResizeTargetBitmap();
D2DRender();
}
return 0;
case WM_DISPLAYCHANGE:
D2DRender(); …
Run Code Online (Sandbox Code Playgroud) 我需要绘制一个形状列表,我正在使用Direct2D.我从文件中获取形状列表.列表已排序,文件中元素的顺序表示将绘制这些形状的顺序.所以,如果例如文件指定在相同的位置和相同大小的两个矩形,只有第二个将是可见的(因为第一将被覆盖).
鉴于我的形状列表,我按以下方式进行绘图:
list<Shape> shapes;
for (const auto& shape : shapes)
shape.draw();
Run Code Online (Sandbox Code Playgroud)
很容易看出,如果我有两个形状,我无法反转绘图操作的顺序,这意味着我必须确保shape2
将始终绘制shape1
,依此类推.接下来我无法使用多个线程来绘制我的形状,这在性能方面是一个巨大的劣势.
我读到Direct3D支持深度缓冲区(或z缓冲区),它为每个像素指定了它的z坐标,这样只会绘制"可见"像素(更接近观察者的on),无论顺序如何形状是绘制的.当我读取文件时,我有每个形状的深度信息.
有没有办法在Direct2D中使用深度缓冲区,或类似的技术允许我使用多个线程来绘制我的形状?
深灰色线应该是黑色和1像素宽:
pRT->DrawLine(Point2F(100, 120), Point2F(300, 120), blackbrush, 1);
Run Code Online (Sandbox Code Playgroud)
浅灰色线应该是黑色和0.5像素宽:
pRT->DrawLine(Point2F(120, 130), Point2F(280, 130), blackbrush, 0.5);
Run Code Online (Sandbox Code Playgroud)
相反,它们都是2像素宽.如果我要求宽2像素,则线条为黑色,但自然宽2像素.
渲染目标具有与窗口的客户区域相同的大小.我希望像GDI中的像素精度,一个坐标=一个像素和纯色......
谢谢.
我正在Win7上开发一个需要执行映射的Dotnet 4.0应用程序.作为地图绘制应用程序,它输出高分辨率抗锯齿多边形的垃圾负载.它目前支持两种类型的渲染输出,GDI +和Direct2D.
我担心因为GDI +输出比Direct2D快3倍左右.
两个渲染器都使用AA.我知道我可以在Direct2D中关闭它,这有点提高了吞吐量(比GDI +差了大约2倍).但这不是一个解决方案,因为我可以在GDI +中关闭AA并在那里获得更好的性能.出于此基准测试的目的,我的渲染代码是微不足道的.我希望我犯了一些可怕的错误,有人可以指出我会纠正这种情况.
_renderTarget.BeginDraw();
// Paint background.
RectF rf = new RectF(0.0f, 0.0f, renderTargetSize.Width, renderTargetSize.Height);
_renderTarget.FillRectangle(rf, _backgroundBrush);
// Draw polygons
foreach (GisTypes.Polygon polygon in _polygons)
{
using (PathGeometry path = _factory.CreatePathGeometry())
{
using (GeometrySink sink = path.Open())
{
sink.SetFillMode(Microsoft.WindowsAPICodePack.DirectX.Direct2D1
.FillMode.Alternate);
Point2F[] points = Array.ConvertAll(polygon.Points,
x => new Point2F((float)x.X, (float)x.Y));
sink.BeginFigure(points[0], FigureBegin.Filled);
for (int i = 1; i < points.Length; ++i)
{
sink.AddLine(points[i]);
}
sink.EndFigure(FigureEnd.Closed);
sink.Close();
}
using (TransformedGeometry transformedPath = _factory.CreateTransformedGeometry(
path, WorldToPage))
{
_renderTarget.FillGeometry(transformedPath, …
Run Code Online (Sandbox Code Playgroud) 我正在尝试使用C++ Builder的TDirect2DCanvas的Direct2D RenderTarget属性来使用GPU对位图操作进行基准测试.与GDI相比,这是最令人印象深刻的.但是,我无法弄清楚如何将结果返回到CPU内存中,因此我可以将其用于进一步的CPU处理.读过关于使用WIC的内容,但显然你放弃了大部分的硬件加速.Windows 8似乎提供了一个带有地图功能的新位图,但我想使用(标准)Windows 7.此外,C++ Builder不提供这些新功能的头文件.
给定一个TDirect2DCanvas,或者您希望使用Direct2D RenderTarget或ID2D1Bitmap,如何将位图复制回CPU内存?
我有样条曲线的数据
我需要使用绘制这条曲线Direct2D
.目前我正在使用ID2D1GeometrySink接口绘制几何图形,但似乎它没有实现可能的AddSpline
方法.
有没有办法通过Direct2D
?绘制样条?即使是DirectX
可以在Direct2D
应用程序中使用的实现也没问题.