https://bell0bytes.eu/direct2d-a-revision/
Drawing Text in Direct2D
Direct2D文本渲染可以分为两部分。第一部分暴露为DrawText和DrawTextLayout方法,允许我们传递一个字符串或者文本进去。
第二个部分是渲染文字,使用到了ID2D1RenderTarget::DrawGlyphRun方法。
DrawText非常容易使用。它使用了Unicode字符串,一个前景画刷,一个Format物体和一个目的矩形。将文字放进矩形里面,然后裁剪它。
DrawTextLayout
DrawTextLayout is a bit more advanced than the simple DrawText method. By creating an IDWriteTextLayout object, it is possible to measure and arrange text as desired. With text layouts, multiple fonts, styles, underlines and strikethroughs are supported as well.
DrawTextLayOut比DrawText稍微先进一些。当创建了IDWriteTextLayout物体后,就可以测量和管理文本了。当然可以支持多种字体,风格和下划线。
当使用Text Layouts的时候,此Glyph Position将在Layout上创建一个缓存,意味着当多次使用DrawCalls,如果能够多次使用同样的Layout物体后,将节省非常多的性能。
DrawGlyphRun
Finally, it is possible to implement the IDWriteTextRenderer interface and to call DrawGlyphRun and FillRectangle manually.
效率
DrawTextLayout把已经存在的DWriteTextLayout物体绘制到RenderTarget上,此时DrawText首先已经通过传入的参数创建了一个DirectWrite的Layout。如果要将相同的文本内容绘制多次,那么用DrawTextLayout将更加高效,因为DrawText每次都会创建一个Layout。
抗锯齿
使用抗锯齿模式 D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE 非常有效。效果与 ClearType 有一比,但是更高效。我们可以用DeviceContext来在全局设置这个。
1 | devCon->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE); |
DXGI
As we have seen, Direct2D interoperates seamlessly with Direct3D surfaces. When rendering to a DXGI surface, Direct2D saves the state of the Direct3D devices while rendering and restores it when rendering is completed. Every time that a batch of Direct2D rendering is completed, the cost of this save and restore and the cost of flushing all the 2D operations are paid, and yet, the Direct3D device is not flushed. Therefore, to increase performance, the number of rendering switches between Direct2D and Direct3D must be limited.
正如我们看到的,D2D能无缝衔接D3D界面。当渲染DXGI表面时,D2D会在D3D渲染时保存状态,在D3D渲染完成后重新读取。为了提高性能,我们必须限制D2D和D3D之间的切换。因此我们需要更改PrintFPS函数。
1 2 3 4 5 6 7 8 | void Direct2D::printFPS(const Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> brush) { if (dxApp->showFPS && textLayoutFPS) { // draw the text devCon->DrawTextLayout(D2D1::Point2F(2.5f, 5.0f), textLayoutFPS.Get(), brush.Get()); } } |
我们需要在D2D绘制块调用printFPS方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | util::Expected<int> DirectXGame::render(double /*farSeer*/) { // clear the back buffer and the depth/stencil buffer d3d->clearBuffers(); //////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////// Direct2D ///////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// d2d->devCon->BeginDraw(); // print FPS information d2d->printFPS(d2d->blackBrush.Get()); if(FAILED(d2d->devCon->EndDraw())) return std::runtime_error("Failed to draw 2D graphics!"); //////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////// Direct3D ///////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// // present the scene if (!d3d->present().wasSuccessful()) return std::runtime_error("Failed to present the scene!"); // return success return 0; } |
从Win10开始,D2D开始支持创建并渲染Sprite Batches。与通常的绘制图像的方法相比,SpriteBatchesSprite批处理产生的每个映像CPU开销大大减少。这让其成为渲染成百上千图像的旋转,比如粒子系统。
D2D现在为Gradient Meshes提供新的Primitive。
在WIN10下,D2D为加载图像提供了一套新的API,即ID2D1ImageSource。图像源改进了现有图像加载API,包括CreateBitmapFromWicBitmap,位图源效果和YCbCr效果。
Direct2D图像源将这些API的功能与对任意大图像的支持,与打印和效果的轻松集成以及众多优化(包括YCbCr JPEG和索引JPEG)相结合。
Ink Rendering
在Win10上,D2D提供一种新的Primitve来表现ink Strokes。这个Ink Strokes由贝塞尔曲线定义。
资源包括笔刷,位图,在D2D里,可以在硬件或软件上创建。但在硬件上删除,则需要大量的开销。
在D2D中,所有的渲染指令,都在BeginDraw和EndDraw两个调用中。当BeginDraw被调用后,将建立一系列渲染指令,但将在一些状态为真后才执行这些指令。
- EndDraw被调用后,会让所有Batched的绘制操作取完成并且返回这些操作的状态。
- Flush将会被明确地调用,让被执行的batch和所有等待状态的指令都发布。
- 保存渲染指令的缓存满了。如果保存渲染指令的缓存在上面上面两个指令满足之前就满了,那么这些渲染指令将被Flushed Out。
在Primitive被flush之前,D2D将一直保持对资源的引用,比如bitmaps和brushed。
Reuse Resources
正如之前提到的,创建和删除资源在硬件上非常昂贵,所以应当尽量复用资源。
比如位图,在游戏一开始就创建了所有不同种类的位图,以便之后使用,而不是重新创建。丹青注意当窗口缩放时,有些与窗口尺寸有关的资源比如Compatible render target则需要重新创建,因为保证渲染场景的整体质量是非常重要的。
Don't Flush (too often)
因为Flush方法会让所有batched的渲染指令被执行,我们推荐你别去用它,而是把资源管理交给D2D。
Large Bitmaps
显卡通常有一个最小的内存分配大小限制。如果一个分配请求要比这个还小,那么其余的内存空间将会被浪费无法做任何事。
那么如果你有一系列很小的位图,那么最好是把它们一起放在一张大位图上。【原来是这样】。这个也被叫做一个Atlas,可以减少创建位图的开销和内存的浪费。推荐位图尽量大于64kb而不去使用小于4kb的位图。
同样,显卡内存也有最大内存分配大小限制,取决于适配器。D2D从WIN8开始就开始支持Atlas了。
Shared Bitmaps
创建Shared Bitmaps允许更高级调用,即创建D2D位图物体,可以被已存在的物体烘焙,也可以与Render Target兼容。这可以避免创建多个表面并且帮助减少开销。
Shared bitmaps are usually limited to software targets or to targets interoperable with DXGI. The CreateBitmapFromDxgiSurface, CreateBitmapFromWicBitmap, and CreateSharedBitmap methods can be used to create shared bitmaps.
Copying Bitmaps
创建DXGI表面是非常昂贵的操作,所以重新使用已存在的表面吧。渲染通常要比复制更昂贵,因为为了提高缓存性能,bitmap在硬件上的位置通常会被打乱。因此CopyFrom*方法可以用于从一个Source复制矩形到D2D位图。
Caching
当要把同一个东西渲染很多遍的时候,最好能够缓存它。
Full scene caching using a color bitmap
When rendering static content, in scenarios like animation, creating another full colour bitmap instead of writing directly to the screen bitmap is a lot more efficient.
Per primitive caching using an A8 bitmap and the FillOpacityMask method
当一个场景不是静态的时候,但却包含很多静态元素比如几何体和Text,Per primitive缓存技术可以被用上。这个技术保持抗锯齿的Primitive和Brush types。位图是A8的,即Alpha通道使用了8bits。
当不透明的物体必须改变时,尽量去改变它的Mask而不是物体本身。
Per-primitive caching using geometry realizations
https://docs.microsoft.com/en-us/windows/win32/direct2d/geometry-realizations-overview
简单来说,就是如果你的场景里有一些不怎么改变的几何体,那么每一帧都去Tesselation,渲染效率太低了。
于是大家想到一种方法,把这些几何体缓存成位图,这样就可以节省大量资源。
但是一旦这个几何体有所改变,又要重新缓存成位图,这不好。
于是就有了Geometry Realization,就是把几何体缓存成顶点数据。
Geometries
Primitives over Geometries
When drawing geometries, it is more efficient to call DrawRectangle than DrawGeometry, as with DrawRectangle, the geometry is already known so rendering is faster.
当要绘制几何形状时,使用DrawRectangle要比DrawGeometry更快,因为前者已经知道了要绘制什么形状。
Multithreading
With the
D2D1_DEVICE_CONTEXT_OPTIONS_ENABLE_MULTI_THREADED_OPTIMIZATIONS
flag (which we set when we created our device), Direct2D will distribute rendering across all of the logical cores present on the system, which can significantly decrease overall rendering time.
Note though that as of Windows 8.1 this only affects path geometries.
Pixel Formats
如果Render Target不使用Alpha通道,那么应该用D2D1_ALPHA_MODE_IGNORE模式。