示例工程【免费下载链接】Windows-classic-samplesThis repo contains samples that demonstrate the API used in Windows classic desktop applications.项目地址https://gitcode.com/gh_mirrors/wi/Windows-classic-samples点击查看免费下载导读Direct2D 的几何Geometry是构建复杂矢量图形的核心但逐帧对同一复杂几何执行光栅化会让抗锯齿开销成倍放大。Windows-classic-samples 仓库中的Direct2D Geometry Realization SampleREADME.md演示了一种经典优化思路把复杂的、需要反复绘制的几何预烘焙成不透明度蒙版opacity mask和网格mesh绘制时只需贴图或填充三角形从而显著提升性能。本文将以该示例的文档为主线结合仓库内的完整 C 源码讲解其接口设计、底层实现原理、可复现的构建运行方式以及这套方案在真实项目中的适用场景与已知取舍。一、为什么需要几何实现Geometry Realization1.1 反锯齿几何的渲染代价Direct2D 中复杂几何如贝塞尔路径、渐变填充区域每次绘制都要由 GPU 重新光栅化。若场景中同时存在成百上千个这样的图元例如动画棋盘格、仪表盘刻度、地图标注每帧的填充率fill rate压力会非常大尤其是Per-Primitive 抗锯齿每个图元单独做边缘采样开销与图元数量成正比几何本身复杂大量细分产生的三角形顶点本身就要占用几何处理单元GPU 前端的吞吐。示例 README 开门见山地给出了核心思路This sample shows how to use opacity masks and A8 targets to enhance performance for anti-aliased geometries. It also shows how to use meshes to enhance performance for aliased geometries.也就是说示例用两套实现分别加速两类渲染路径渲染路径实现手段目标抗锯齿Per-Primitive AAOpacity Mask A8 位图渲染目标把几何形状预渲染成灰度蒙版绘制时一次贴图锯齿AliasedID2D1Mesh 网格 镶嵌Tessellate把几何预分割为三角形网格绘制时批量填充1.2 预烘焙的性能收益从哪来把几何预渲染为蒙版/网格后每帧的绘制路径从光栅化复杂路径简化为填充位图或三角形顶点数与填充复杂度大幅下降。源码头文件对接口的定位写得很明确GeometryRealization.hEncapsulates various mesh and/or opacity mask instances to provide efficient rendering of complex primitives.即一个IGeometryRealization对象封装若干 mesh 与 opacity mask 实例为复杂图元的高效绘制服务。二、接口与核心参数两个 COM 接口、两个枚举示例的核心是一个自定义 COM 接口IGeometryRealization及其工厂IGeometryRealizationFactory完整定义在 GeometryRealization.h。2.1 REALIZATION_CREATION_OPTIONS指定生成哪些子实现typedef enum REALIZATION_CREATION_OPTIONS { REALIZATION_CREATION_OPTIONS_ALIASED 1, // Generate mesh REALIZATION_CREATION_OPTIONS_ANTI_ALIASED 2, // Generate opacity mask REALIZATION_CREATION_OPTIONS_UNREALIZED 4, // Retain pointer to original geometry REALIZATION_CREATION_OPTIONS_FILLED 8, // Generate fill realization REALIZATION_CREATION_OPTIONS_STROKED 16 // Generate stroke realization } REALIZATION_CREATION_OPTIONS;这些标志是位掩码可任意组合。一个IGeometryRealization最多可持有 4 个子实现对应矩阵{Aliased, PPAA} × {Filled, Stroked}见 GeometryRealization.cpp 的注释。例如示例主程序一次组合了全部 5 个标志GeometryRealizationSample.cppstatic_castREALIZATION_CREATION_OPTIONS( REALIZATION_CREATION_OPTIONS_ANTI_ALIASED | REALIZATION_CREATION_OPTIONS_ALIASED | REALIZATION_CREATION_OPTIONS_FILLED | REALIZATION_CREATION_OPTIONS_STROKED | REALIZATION_CREATION_OPTIONS_UNREALIZED )2.2 REALIZATION_RENDER_MODE决定用实现还是用原始几何typedef enum REALIZATION_RENDER_MODE { REALIZATION_RENDER_MODE_FORCE_REALIZED 0, // Force the use of the realization REALIZATION_RENDER_MODE_FORCE_UNREALIZED 1, // Force the use of the original geometry REALIZATION_RENDER_MODE_DEFAULT 2 // SW: Unrealized / HW: Realized } REALIZATION_RENDER_MODE;DEFAULT模式是示例最有意思的设计软件渲染目标自动退回未实现路径硬件渲染目标则走已实现路径。这是因为软件渲染在几何本身简单时可能更快而硬件渲染受益于预烘焙。该决策由m_swRT标记驱动——GeometryRealization::Initialize中通过pRT-IsSupported(D2D1_RENDER_TARGET_TYPE_SOFTWARE)探测渲染目标是否支持软件类型GeometryRealization.cpp。2.3 IGeometryRealizationFill / Draw / Update 三件套STDMETHOD(Fill)(ID2D1RenderTarget *pRT, ID2D1Brush *pBrush, REALIZATION_RENDER_MODE mode); STDMETHOD(Draw)(ID2D1RenderTarget *pRT, ID2D1Brush *pBrush, REALIZATION_RENDER_MODE mode); STDMETHOD(Update)(ID2D1Geometry *pGeometry, REALIZATION_CREATION_OPTIONS options, CONST D2D1_MATRIX_3X2_F *pWorldTransform, float strokeWidth, ID2D1StrokeStyle *pIStrokeStyle);Fill绘制填充实现内部走RenderToTarget(true, ...)见 GeometryRealization.cppDraw绘制描边实现内部走RenderToTarget(false, ...)见 GeometryRealization.cppUpdate丢弃旧实现、用新几何重新生成并且会尽量复用已有位图资源。头文件对Update的两条注释值得注意GeometryRealization.h位图复用销毁纹理的代价出奇地昂贵因此Update优先复用现有位图只有尺寸不足时才重建这比每次新建实现要快得多世界变换绑定pWorldTransform是本次实现所针对优化的变换。如果渲染时渲染目标的变换与该变换一致实现版与未实现版画面完全一致否则画质会有所退化。2.4 工厂两个 CreateGeometryRealization 重载STDMETHOD(CreateGeometryRealization)(IGeometryRealization **ppRealization); STDMETHOD(CreateGeometryRealization)(ID2D1Geometry *pGeometry, REALIZATION_CREATION_OPTIONS options, CONST D2D1_MATRIX_3X2_F *pWorldTransform, float strokeWidth, ID2D1StrokeStyle *pIStrokeStyle, IGeometryRealization **ppRealization);第一个重载创建空实现后续用Update填充内容第二个直接传入几何与选项一步创建。工厂通过全局函数CreateGeometryRealizationFactory获取提供两个版本GeometryRealization.cppHRESULT CreateGeometryRealizationFactory(ID2D1RenderTarget *pRT, UINT maxRealizationDimension, IGeometryRealizationFactory **ppFactory); HRESULT CreateGeometryRealizationFactory(ID2D1RenderTarget *pRT, IGeometryRealizationFactory **ppFactory); // 默认 maxRealizationDimension 0xffffffff工厂Initialize会把maxRealizationDimension与渲染目标的GetMaximumBitmapSize()取最小值保证蒙版位图不超过硬件能力若传入 0 则返回E_INVALIDARG0 尺寸位图没有意义且 DXGI surface 渲染目标不支持GeometryRealization.cpp。三、源码级剖析蒙版与网格是如何生成的3.1 抗锯齿实现GenerateOpacityMask 的完整管线GenerateOpacityMask是整个实现里最棘手的部分GeometryRealization.cpp它的目标是把几何渲染成一张灰度位图。关键步骤依次为计算边界填充走GetBounds描边走GetWidenedBounds把 strokeWidth 与 stroke style 考虑进来空边界保护left right定义为空矩形源码用一个能正确处理 NaN 的表达式判断空边界并回退为 1×1 的假边界GeometryRealization.cpp四周膨胀 1 像素在几何四周保留一圈完全透明的像素确保蒙版被拉伸时 alpha 渐变能平滑衰减到 0而不是被矩形边缘硬裁剪GeometryRealization.cpp按 64 像素块向上取整sc_bitmapChunkSize 64定义于 GeometryRealization.cpp。取整的目的是避免几何尺寸缓慢变化时频繁重新分配位图限制最大尺寸若超过maxRealizationDimension按比例scaleX/scaleY缩小位图尺寸复用或重建位图若现有位图任一方向超过新需求 2 倍以上强制重新分配以节省内存若新需求超过现有尺寸也重新分配注释特别说明用 而非 是刻意的——它保证反复小幅缩放几何不会触发反复重分配见 GeometryRealization.cpp创建 A8 兼容渲染目标像素格式为DXGI_FORMAT_A8_UNORMD2D1_ALPHA_MODE_PREMULTIPLIED——这正是 README 强调的A8 targetsGeometryRealization.cpp。A8 是单通道 8 位 alpha 格式把几何形状编码成灰度蒙版内存与带宽占用都远小于全彩格式绘制并记录 bounds用白色实心画刷把几何 Fill/Draw 到兼容渲染目标上同时输出pMaskDestBounds目标矩形与pMaskSourceBounds蒙版有效区域供后续FillOpacityMask使用——绘制时只画实际使用的蒙版区域避免绘制大量多余的透明像素。3.2 锯齿实现Mesh Tessellate / Widen当选项包含REALIZATION_CREATION_OPTIONS_ALIASED时Update走 mesh 路径GeometryRealization.cpp填充网格m_pRT-CreateMesh→pMesh-Open(pSink)→pGeometry-Tessellate(pWorldTransform, pSink)→pSink-Close()把几何按世界变换镶嵌成三角形网格描边网格由于 mesh 只存三角形描边需要先把几何加宽——pGeometry-Widen(strokeWidth, pIStrokeStyle, pWorldTransform, pGeometrySink)生成加宽后的路径几何再对该路径做Tessellate此时 world transform 已由 Widen 处理传 NULL 即可。3.3 RenderToTarget绘制时的撤销变换与抗锯齿切换绘制实现版几何时RenderToTarget做了三件关键事情GeometryRealization.cpp默认/强制未实现分支若mode DEFAULT m_swRT或mode FORCE_UNREALIZED直接用FillGeometry/DrawGeometry渲染原始几何若对象当初未带UNREALIZED选项创建此时会返回E_FAIL反锯齿模式切换FillOpacityMask要求先切换到D2D1_ANTIALIAS_MODE_ALIASED源码注释明确指出这一点因此函数先保存原始 AA 模式调用前切换、结束后恢复变换撤销蒙版/网格是按Update传入的世界变换预生成的绘制时要撤销该变换、再叠加渲染目标当前的变换SetTransform(m_realizationTransformInv * originalTransform)并在结束后恢复原变换。若实现变换恰为单位矩阵则跳过矩阵乘法以省一次计算m_realizationTransformIsIdentity标记见 GeometryRealization.cpp。从代码结构可以推断填充走FillOpacityMaskPPAA 模式或FillMeshAliased 模式描边同理分别对应m_pStrokeRT与m_pStrokeMesh两个成员对象内部保存了m_pFillMesh/m_pStrokeMesh/m_pFillRT/m_pStrokeRT四份资源见 GeometryRealization.cpp。四、DemoApp一个可交互的性能对比实验台示例程序在 GeometryRealizationSample.cpp 中实现了一个完整窗口应用用于直观对比已实现/未实现两种渲染路径的性能差异。4.1 场景构成旋转的沙漏阵列核心图元是一个沙漏hour glass形状的 PathGeometry由直线与贝塞尔曲线构成CreateGeometries见 GeometryRealizationSample.cpp再经CreateTransformedGeometry缩放到单元格大小渲染循环按m_numSquares × m_numSquares网格铺开每个沙漏按离中心的距离获得旋转速度与颜色蓝紫渐变构成旋转棋盘动画RenderMainContent见 GeometryRealizationSample.cpp每个图元Update生成一次实现含 FillStroke 两条路径之后每帧只调用Fill/Draw。默认图元数为16×16sc_defaultNumSquares 16上限 1024×1024屏幕左上角实时显示FPS 与每秒图元数primitives/sec用QueryPerformanceCounter计时、RingBufferRingBuffer.h缓存最近 4 个时间点计算帧率RenderTextInfo见 GeometryRealizationSample.cpp。4.2 键盘与鼠标控制README 操作说明的完整继承README 中列出的控制键在源码中均有对应实现OnKeyDown/OnWheel见 GeometryRealizationSample.cpp整理如下操作功能源码实现↑Up Arrow图元数量翻倍上限 1024并重建几何VK_UP分支↓Down Arrow图元数量减半下限 1并重建几何VK_DOWN分支空格Spacebar暂停/恢复动画VK_SPACE分支暂停时触发实现重建鼠标滚轮以鼠标位置为中心缩放1x15xOnWheelsc_maxZoom15.0fT切换硬件/软件渲染README 文档说明源码OnKeyDown未含 T 分支硬件/软件类型在创建渲染目标时确定R切换使用实现/使用原始几何渲染case R翻转m_useRealizationsA切换抗锯齿模式Aliased ↔ Per-Primitivecase A翻转m_antialiasModeG切换自动重建实现case G翻转m_autoGeometryRegenS切换是否绘制描边case S翻转m_drawStroke其中R 键是最核心的对比开关Fill/Draw调用时m_useRealizations为真则传REALIZATION_RENDER_MODE_DEFAULT硬件下走实现路径为假则传REALIZATION_RENDER_MODE_FORCE_UNREALIZED强制走原始几何路径配合左上角 FPS 数字即可直接量化两者的性能差距GeometryRealizationSample.cpp。4.3 资源生命周期管理DemoApp 遵循 Direct2D 标准模式设备无关资源D2D/DWrite/WIC 工厂与文本格式在CreateDeviceIndependentResources一次性创建GeometryRealizationSample.cpp设备相关资源渲染目标、画刷、IGeometryRealization集中在CreateDeviceResources注意这里把sc_maxRealizationDimension 2000传入工厂见 GeometryRealizationSample.cpp 与 GeometryRealizationSample.cpp设备丢失处理EndDraw返回D2DERR_RECREATE_TARGET时调用DiscardDeviceResources丢弃设备资源下次OnRender自动重建GeometryRealizationSample.cppDPI 感知工程通过 DeclareDPIAware.manifest 声明dpiAwaretrue/dpiAware窗口尺寸按系统 DPI 缩放GetDesktopDpi换算。五、构建与运行继承 README 的完整步骤5.1 前置条件按 README 要求构建本示例需要Microsoft Windows 7及更高版本系统Windows 7 SDK 与 .NET Framework 3.5 Service Pack 1Visual Studio 2008示例工程文件为 VS2008 格式的.vcproj或支持 msbuild 的命令行环境。工程链接的库为d2d1.lib dwrite.lib windowscodecs.lib dxgi.lib d3d10_1.lib见 GeometryRealizationSample.vcproj即 Direct2D、DirectWrite、WIC 与 DXGI/D3D10.1 相关依赖。5.2 命令行构建打开命令提示符并进入示例目录执行msbuild GeometryRealizationSample.sln5.3 Visual Studio 构建推荐用 Windows 资源管理器进入示例目录双击GeometryRealizationSample.sln在 Visual Studio 中打开在生成菜单中选择生成解决方案生成产物默认输出到\Debug或\Release目录。六、设计权衡与已知限制源码注释中的第一手经验实现文件的头部注释GeometryRealization.cpp坦率地记录了这套朴素实现的三个局限对生产实践极具参考价值未做位图图集化atlasingPPAA 实现不会把多个几何的实现合并进同一张位图。若采用图集方案Direct2D 在交错绘制不同几何的实现时所需的状态切换会大幅减少性能还能进一步提升可能消耗大量显存即便是极简单的图元如一条从渲染目标左上到右下的细对角线作为单一蒙版存储也需要一张覆盖整个渲染目标的位图尽管描边实际覆盖面积很小且 GPU 还要浪费大量周期渲染完全透明的像素更优的改进方向把几何划分为网格空单元格或完全被几何覆盖的单元格可被剔除部分覆盖的单元格再做图集化——这既能省显存又能提速。此外Update的世界变换绑定特性意味着实现版画面只有在渲染变换与生成变换一致时才与未实现版完全一致。旋转或平移抗锯齿实现会引入模糊示例注释在 GeometryRealizationSample.cpp 中说明只有左上角图元因使用完全相同变换而看起来完全一致并建议按空格暂停动画触发重建来观察这一现象。因此在实际项目中Geometry Realization 更适合几何相对静态、变换稳定的场景如地图瓦片、静态仪表盘、批量的相似图标而不适合每帧都大幅旋转缩放的动态几何。七、小结Direct2D Geometry Realization Sample 用约 1300 行 C 源码完整演示了一条可复用的性能优化管线接口层面IGeometryRealizationIGeometryRealizationFactory用位掩码选项精确控制生成锯齿/抗锯齿 × 填充/描边四种子实现实现层面A8 兼容渲染目标 FillOpacityMask服务抗锯齿路径WidenTessellateFillMesh服务锯齿路径并通过位图复用、尺寸取整、变换撤销等细节控制质量与开销验证层面内置交互式对比实验台R/A/S/G 键 FPS 统计让开发者在真实渲染目标上直接评估收益。作为参考README 中的文件清单与本文剖析一一对应接口定义见 GeometryRealization.h实现见 GeometryRealization.cppDemoApp 见 GeometryRealizationSample.cpp 与 GeometryRealizationSample.h帧率统计依赖 RingBuffer.hDPI 声明见 DeclareDPIAware.manifest。需要为复杂 Direct2D 几何提速的开发者可以直接从这套模式起步再按图集化 网格剔除的改进方向做生产级落地。赞分享示例工程【免费下载链接】Windows-classic-samplesThis repo contains samples that demonstrate the API used in Windows classic desktop applications.项目地址https://gitcode.com/gh_mirrors/wi/Windows-classic-samples点击查看免费下载相关推荐iced 自定义 Widget 绘制任意几何深入解析 geometry 示例与 Mesh 三角形渲染管线iced 自定义 Widget 绘制任意几何深入解析 geometry 示例与 Mesh 三角形渲染管线 本指南以 geometry 示例 https://l桌面应用跨平台UI组件LiveKit 快速上手3 步跑通自研 WebRTC 实时音视频服务器AI 还能像真人一样进会议室LiveKit 快速上手3 步跑通自研 WebRTC 实时音视频服务器AI 还能像真人一样进会议室 做视频通话或语音 AI 应用时最头疼的往往不是界面而示例工程Direct2D/DXGI 互操作示例DXGISample深度解析在同一窗口中融合 Direct3D 与 Direct2D 渲染Direct2D/DXGI 互操作示例DXGISample深度解析在同一窗口中融合 Direct3D 与 Direct2D 渲染 导读 Samples/W示例工程上一篇2024最新.NET反混淆工具清单.NET-Deobfuscator项目热门工具推荐下一篇NKThesis南开大学LaTeX论文模板轻松搞定高校毕业论文格式排版创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
阅读完成 · 觉得有帮助?