D3D12 二进制遮挡查询与 Predication 实战:解读 DirectX-Graphics-Samples 的 PredicationQueries 示例
示例工程【免费下载链接】DirectX-Graphics-SamplesThis repo contains the DirectX Graphics samples that demonstrate how to build graphics intensive applications on Windows.项目地址https://gitcode.com/gh_mirrors/di/DirectX-Graphics-Samples点击查看免费下载本篇指南以 DirectX-Graphics-Samples 仓库中Samples/Desktop/D3D12PredicationQueries示例为蓝本系统讲解 Direct3D 12 中**二进制遮挡查询Binary Occlusion Query与预测Predication**的完整用法从查询堆、查询结果缓冲等底层资源搭建到BeginQuery/EndQuery/ResolveQueryData/SetPredication的逐帧调用链再到 Root Signature 1.1 静态描述符优化与构建运行方式。读完本文你将能够在自己项目中复现上一帧查询、本帧条件绘制的经典遮挡剔除流程。示例总览两个四边形如何演示查询与预测该示例说明见 readme.md演示了 Direct3D 12 中二进制遮挡查询与预测的结合使用。场景非常简单但直击要点渲染两个四边形其中一个会来回动画并周期性遮挡另一个。每一帧执行一次二进制遮挡查询并通过SetPredicationAPI 条件性地绘制可能被遮挡的四边形当查询判定该四边形被完全遮挡时利用透明度混合直观地展示这一结果。从 Main.cpp 可以看到示例以 1280×720 窗口启动D3D12PredicationQueries sample(1280, 720, LD3D12 Predication and Queries sample); return Win32Application::Run(sample, hInstance, nCmdShow);场景中共有三份几何数据见 D3D12PredicationQueries.cpp 的LoadAssets几何体位置/尺寸颜色用途远四边形Far quad±0.25 × ±0.25z 0.5白色不透明实际场景中代表复杂几何体是可能被遮挡的绘制对象近四边形Near quad±0.5 × ±0.35z 0.0红色/黄色alpha 0.65半透明动画物体周期性遮挡远四边形远四边形包围盒±0.25 × ±0.25z 0.4999黑色只用于遮挡查询的几何z 值略微偏移以避免与远四边形 z-fighting注释明确说明Geometry will be rendered back-to-front to support transparency in the scene即为了支持透明度几何按从后到前的顺序绘制。前置概念二进制遮挡查询与 Predication遮挡查询Occlusion Query向 GPU 提交一段几何体绘制由硬件统计该几何体在深度测试中有多少像素通过。二进制查询只关心有没有任何像素通过结果0 表示被完全遮挡没有任何样本通过深度测试1 表示可见至少一个样本通过。预测Predication与查询问问题不同Predication 是用答案做决定——SetPredication把一个查询结果缓冲区绑定到命令列表中其后的绘制/复制命令会根据谓词值决定是否执行。D3D12_PREDICATION_OP_EQUAL_ZERO表示当谓词值等于 0 时执行后续命令。一帧延迟查询结果需要 GPU 执行完后才能回读因此示例在上一帧执行查询并解析到缓冲区当前帧用其结果驱动远四边形的条件绘制代码注释Draw the far quad conditionally based on the result of the occlusion query from the previous frame。从零搭建查询基础设施1. 创建查询堆Query Heap在LoadPipeline中示例创建了一个容量为 1 的遮挡类型查询堆D3D12_QUERY_HEAP_DESC queryHeapDesc {}; queryHeapDesc.Count 1; queryHeapDesc.Type D3D12_QUERY_HEAP_TYPE_OCCLUSION; ThrowIfFailed(m_device-CreateQueryHeap(queryHeapDesc, IID_PPV_ARGS(m_queryHeap)));Count为查询槽位数量Type决定了可用的查询类型——D3D12_QUERY_HEAP_TYPE_OCCLUSION支持二进制遮挡查询D3D12_QUERY_TYPE_BINARY_OCCLUSION和整型遮挡查询。对应成员声明见 D3D12PredicationQueries.h 中的ComPtrID3D12QueryHeap m_queryHeap;。2. 创建查询结果缓冲查询结果必须落在一个8 字节的缓冲中二进制遮挡查询结果占 1 个BOOL但缓冲需按 8 字节对齐创建并且创建时直接置于D3D12_RESOURCE_STATE_PREDICATION状态——这正是SetPredication读取该缓冲所需的资源状态D3D12_RESOURCE_DESC queryResultDesc CD3DX12_RESOURCE_DESC::Buffer(8); ThrowIfFailed(m_device-CreateCommittedResource( CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT), D3D12_HEAP_FLAG_NONE, queryResultDesc, D3D12_RESOURCE_STATE_PREDICATION, nullptr, IID_PPV_ARGS(m_queryResult)));3. 两个 PSO普通管线与查询专用管线同一个 PSO 描述在创建后会被复用两次先创建m_pipelineState正常绘制开启 alpha 混合以便可视化查询结果随后关闭颜色写入和深度写入再创建m_queryState——查询绘制只需要让深度测试发生不需要产生任何可见像素psoDesc.BlendState.RenderTarget[0].RenderTargetWriteMask 0; psoDesc.DepthStencilState.DepthWriteMask D3D12_DEPTH_WRITE_MASK_ZERO; ThrowIfFailed(m_device-CreateGraphicsPipelineState(psoDesc, IID_PPV_ARGS(m_queryState)));这里还体现了两个值得注意的实践顶点输入布局包含POSITIONR32G32B32_FLOAT与COLORR32G32B32A32_FLOAT偏移 12 字节混合状态采用标准源 alpha 混合D3D12_BLEND_SRC_ALPHA / D3D12_BLEND_INV_SRC_ALPHA这正是半透明近四边形能透过看到远处几何的原因。核心渲染流程逐行拆解整个查询与预测逻辑集中在PopulateCommandListD3D12PredicationQueries.cpp可以分为三个紧密衔接的阶段。阶段一用上一帧查询结果条件绘制远四边形// Draw the far quad conditionally based on the result of the occlusion query // from the previous frame. m_commandList-SetGraphicsRootDescriptorTable(0, cbvFarQuad); m_commandList-SetPredication(m_queryResult.Get(), 0, D3D12_PREDICATION_OP_EQUAL_ZERO); m_commandList-DrawInstanced(4, 1, 0, 0);SetPredication的三个参数分别为谓词缓冲资源、缓冲内字节偏移此处为 0、谓词操作。EQUAL_ZERO意味着当缓冲中值等于 0远四边形完全被遮挡时执行随后的DrawInstanced——配合半透明近四边形被遮挡时白色远四边形会在其后透出形成混色视觉效果这正是 readme 所说transparency effect is used to illustrate when the query determines that the quad is fully occluded。阶段二解除预测无条件绘制近四边形// Disable predication and always draw the near quad. m_commandList-SetPredication(nullptr, 0, D3D12_PREDICATION_OP_EQUAL_ZERO); m_commandList-SetGraphicsRootDescriptorTable(0, cbvNearQuad); m_commandList-DrawInstanced(4, 1, 4, 0);把谓词缓冲设为nullptr即关闭预测此后命令无条件执行。注意两个DrawInstanced的起始顶点索引不同0 与 4对应顶点缓冲中不同段的四边形。阶段三执行查询并解析结果// Run the occlusion query with the bounding box quad. m_commandList-SetPipelineState(m_queryState.Get()); m_commandList-BeginQuery(m_queryHeap.Get(), D3D12_QUERY_TYPE_BINARY_OCCLUSION, 0); m_commandList-DrawInstanced(4, 1, 8, 0); m_commandList-EndQuery(m_queryHeap.Get(), D3D12_QUERY_TYPE_BINARY_OCCLUSION, 0);先切换为不写颜色/深度的查询管线在BeginQuery与EndQuery之间绘制包围盒四边形起始顶点索引 8。深度测试在此刻真实发生其结果即为远四边形本帧的可见性。随后通过资源屏障把查询结果缓冲从D3D12_RESOURCE_STATE_PREDICATION切换到D3D12_RESOURCE_STATE_COPY_DEST调用ResolveQueryData将 GPU 查询结果写入缓冲再切回PREDICATION状态供下一帧SetPredication读取m_commandList-ResourceBarrier(1, CD3DX12_RESOURCE_BARRIER::Transition( m_queryResult.Get(), D3D12_RESOURCE_STATE_PREDICATION, D3D12_RESOURCE_STATE_COPY_DEST)); m_commandList-ResolveQueryData(m_queryHeap.Get(), D3D12_QUERY_TYPE_BINARY_OCCLUSION, 0, 1, m_queryResult.Get(), 0); m_commandList-ResourceBarrier(1, CD3DX12_RESOURCE_BARRIER::Transition( m_queryResult.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PREDICATION));至此形成完整闭环本帧查询 → 解析 → 下一帧条件绘制。此外三个阶段分别用PIXBeginEventDraw potentially occluded geometry、Draw animating geometry、Execute occlusion query做了标注配合 PIX 可逐段分析 GPU 工作负载。动画逻辑近四边形周期性遮挡OnUpdate中近四边形的 x 偏移每帧增加0.01f到达1.5f边界后回绕到-1.5f实现往复移动随后把对应常量缓冲内容拷入上传堆中的帧专属区域每个几何体一份独立常量缓冲CbvCountPerFrame 2const float translationSpeed 0.01f; const float offsetBounds 1.5f; // Animate the near quad. m_constantBufferData[1].offset.x translationSpeed; if (m_constantBufferData[1].offset.x offsetBounds) { m_constantBufferData[1].offset.x -offsetBounds; } UINT cbvIndex m_frameIndex * CbvCountPerFrame 1; UINT8* destination m_pCbvDataBegin (cbvIndex * sizeof(SceneConstantBuffer)); memcpy(destination, m_constantBufferData[1], sizeof(SceneConstantBuffer));近四边形在-1.5 ~ 1.5区间内掠过远四边形±0.25范围从而周期性触发完全遮挡 → 查询结果为 0 → 谓词命中的演示状态。常量缓冲结构在 D3D12PredicationQueries.h 中定义注意其FLOAT padding[60]注释常量缓冲需 256 字节对齐填充是为了让多个缓冲可按数组索引寻址。对应的 shaders.hlsl 非常精简——顶点着色器把常量缓冲中的offset叠加到顶点位置上像素着色器直接输出顶点色cbuffer SceneConstantBuffer : register(b0) { float4 offset; }; PSInput VSMain(float4 position : POSITION, float4 color : COLOR) { PSInput result; result.position position offset; result.color color; return result; } float4 PSMain(PSInput input) : SV_TARGET { return input.color; }配合 Root Signature 1.1 的静态描述符优化readme 的Optional features一节说明了本示例的另一个亮点示例已更新为基于Windows 10 Anniversary Update SDK构建该 SDK 为 Direct3D 12 提供了新版 Root Signature。Root Signature 1.1允许应用声明描述符堆中的描述符不会改变或描述符指向的数据不会改变从而给驱动提供在描述符或其所指内存在一段时间内保持静态这一前提下进行优化的机会。源码中这一机制落地为一段完整的版本协商 静态标志代码D3D12_FEATURE_DATA_ROOT_SIGNATURE featureData {}; featureData.HighestVersion D3D_ROOT_SIGNATURE_VERSION_1_1; if (FAILED(m_device-CheckFeatureSupport(D3D12_FEATURE_ROOT_SIGNATURE, featureData, sizeof(featureData)))) { featureData.HighestVersion D3D_ROOT_SIGNATURE_VERSION_1_0; } CD3DX12_DESCRIPTOR_RANGE1 ranges[1]; CD3DX12_ROOT_PARAMETER1 rootParameters[1]; ranges[0].Init(D3D12_DESCRIPTOR_RANGE_TYPE_CBV, 1, 0, 0, D3D12_DESCRIPTOR_RANGE_FLAG_DATA_STATIC); rootParameters[0].InitAsDescriptorTable(1, ranges[0], D3D12_SHADER_VISIBILITY_VERTEX);要点拆解能力协商先请求D3D_ROOT_SIGNATURE_VERSION_1_1通过CheckFeatureSupport检测失败则回退到D3D_ROOT_SIGNATURE_VERSION_1_0保证在旧驱动上也能运行静态声明D3D12_DESCRIPTOR_RANGE_FLAG_DATA_STATIC告诉驱动该描述符指向的数据在整个根签名有效期内不会改变这是 1.1 新增的优化信号示例中常量缓冲每帧更新但根签名本身持续复用因此数据静态声明是安全的版本化描述用CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC::Init_1_1构造描述再经D3DX12SerializeVersionedRootSignature序列化为字节码创建根签名管线阶段收敛根签名标志同时声明DENY_HULL/DOMAIN/GEOMETRY/PIXEL_SHADER_ROOT_ACCESS仅允许顶点着色器访问配合D3D12_SHADER_VISIBILITY_VERTEX限制描述符表可见性减少驱动需要维护的状态。这是把新版根签名 静态数据声明用于实际渲染管线的完整范例也是 readme 中 optional feature 的源码级印证。构建与运行示例工程文件为 D3D12PredicationQueries.vcxproj支持Debug/Release × x64/ARM64四种配置链接dxgi.lib; d3d12.lib; d3dcompiler.lib; dxguid.lib并延迟加载d3d12.dll。构建层面的几个关键点着色器编译工程通过自定义构建步骤调用dxc.exe来自 NuGet 包Microsoft.Direct3D.DXC.1.8.2505.32分别以vs_6_0和ps_6_0目标编译VSMain/PSMain输出shaders_VSMain.cso与shaders_PSMain.cso随后由应用在运行时通过ReadDataFromFile读取dxc.exe -nologo -Tvs_6_0 -EVSMain -Zi -Qembed_debug -Fo$(OutDir)shaders_VSMain.cso shaders.hlsl dxc.exe -nologo -Tps_6_0 -EPSMain -Zi -Qembed_debug -Fo$(OutDir)shaders_PSMain.cso shaders.hlslD3D12 SDK 版本源文件顶部导出了D3D12SDKVersion 618与D3D12SDKPath u8.\\D3D12\\指示 D3D12 SDK 加载器使用本地.\D3D12\目录下对应版本的 SDK 运行库NuGet 包Microsoft.Direct3D.D3D12.1.618.3提供配套的构建属性与目标。PIX 集成工程引用WinPixEventRuntime.1.0.240308001配合代码中的PIXBeginEvent/PIXEndEvent可在 PIX 中分析渲染事件。运行参数应用支持命令行加/warp或-warp强制使用 WARP 软件设备解析逻辑见 DXSample.cpp 的ParseCommandLineArgs无 D3D12 硬件时也可观察行为设备创建请求的功能级别为D3D_FEATURE_LEVEL_11_0。环境前提方面readme 说明示例面向 Windows 10 Anniversary Update SDKRoot Signature 1.1 在该 SDK 中可用仓库主分支 README.md 要求 Windows 10 2004 及以上版本搭配 Visual Studio 2019 与 Windows 10 SDK 19041 构建。Debug 构建下代码会启用 D3D12 调试层与DXGI_CREATE_FACTORY_DEBUG需要系统安装 Graphics Tools 可选功能并调用DXGIDeclareAdapterRemovalSupport运行期若遇到DXGI_ERROR_DEVICE_REMOVED/RESET示例会通过RestoreD3DResources重建全部 D3D 资源。关键文件索引示例说明文档二进制遮挡查询 Predication 概述与 Root Signature 1.1 optional feature 说明主实现查询堆/结果缓冲创建、双 PSO、谓词绘制与查询解析的完整调用链类声明资源成员、SceneConstantBuffer对齐结构、帧计数常量着色器常量缓冲偏移驱动的顶点变换与纯色输出工程文件dxc 编译命令、NuGet 依赖与链接配置应用入口 与 通用框架窗口创建、适配器枚举、/warp参数解析仓库顶层说明各 API 示例的组织方式与构建环境要求赞分享示例工程【免费下载链接】DirectX-Graphics-SamplesThis repo contains the DirectX Graphics samples that demonstrate how to build graphics intensive applications on Windows.项目地址https://gitcode.com/gh_mirrors/di/DirectX-Graphics-Samples点击查看免费下载相关推荐Yii 2 RESTful API 快速开始用最小代码构建完整的用户资源接口Yii 2 RESTful API 快速开始用最小代码构建完整的用户资源接口 本篇技术指南围绕 Yii 2 官方指南的 REST 快速开始章节展开演示如何以示例工程微信聊天记录导出完整指南用 WeChatMsg 免费把历史对话转成 Word、CSV 和年度聊天报告微信聊天记录导出完整指南用 WeChatMsg 免费把历史对话转成 Word、CSV 和年度聊天报告 手机一换、系统一重装很多微信群里的老消息就再也找不回来示例工程深入 Pydantic AI 的 pydantic_ai.ext如何把 LangChain 工具无缝桥接为 Pydantic AI 工具深入 Pydantic AI 的 pydantic_ai.ext 如何把 LangChain 工具无缝桥接为 Pydantic AI 工具 Pydantic示例工程上一篇swagger-codegen 生成的 Dart 浏览器客户端 Order 模型全解析从 Swagger 定义到订单模型实战下一篇Play 2 设计哲学解读面向异步编程与类型安全的 JVM Web 框架架构创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考