DirectX12(D3D12)基础教程九 间接绘制

在学习directx12 microsoft提供了很多示例,有简单的也有复杂,下载网址:https://github.com/microsoft/DirectX-Graphics-Samples

本章对D3D12ExecuteIndirect 示例做了简化,只保留间接绘制部分,删除了计算着色器部分。

间接绘制适用场景:通过命令签名传递大量实例数据(如顶点位置、索引等),减少GPU调用次数.

1.创建命令签名

API函数 ID3D12Device::CreateCommandSignature()

HRESULT CreateCommandSignature([in]            const D3D12_COMMAND_SIGNATURE_DESC *pDesc,[in, optional]  ID3D12RootSignature                *pRootSignature,REFIID                             riid,[out, optional] void                               **ppvCommandSignature
);参数
[in] pDesc类型: const D3D12_COMMAND_SIGNATURE_DESC*描述使用 D3D12_COMMAND_SIGNATURE_DESC 结构创建的命令签名。[in, optional] pRootSignature类型: ID3D12RootSignature*指定命令签名应用到的 ID3D12RootSignature 。如果签名中的任何命令将更新管道上的绑定,则需要根签名。 如果存在的唯一命令是绘图或调度,则可以将根签名参数设置为 NULL。riid类型: REFIID命令签名接口的全局唯一标识符 (GUID) (ID3D12CommandSignature) 。 可以使用 __uuidof () 宏获取命令签名接口的 REFIID 或 GUID。 例如,__uuidof (ID3D12CommandSignature) 将获取命令签名接口的 GUID 。[out, optional] ppvCommandSignature类型: void**指定一个指针,该方法成功完成后将指向创建的命令签名 (ID3D12CommandSignature) 。

描述命令签名的参数 D3D12_COMMAND_SIGNATURE_DESC

typedef struct D3D12_COMMAND_SIGNATURE_DESC {UINT                               ByteStride;UINT                               NumArgumentDescs;const D3D12_INDIRECT_ARGUMENT_DESC *pArgumentDescs;UINT                               NodeMask;
} D3D12_COMMAND_SIGNATURE_DESC;ByteStride指定绘图缓冲区中每个命令的大小(以字节为单位)。NumArgumentDescs指定命令签名中的参数数。pArgumentDescsD3D12_INDIRECT_ARGUMENT_DESC结构的数组,包含参数的详细信息,包括参数是顶点缓冲区、常量、常量缓冲区视图、着色器资源视图还是无序访问视图。NodeMask对于单个 GPU 操作,请将此设置为零。 如果有多个 GPU 节点,请设置位来标识 (要应用命令签名的设备物理适配器) 节点。 掩码中的每个位都对应一个节点。 请参阅 多适配器系统。

指定间接参数的类型  D3D12_INDIRECT_ARGUMENT_TYPE

typedef enum D3D12_INDIRECT_ARGUMENT_TYPE {D3D12_INDIRECT_ARGUMENT_TYPE_DRAW = 0,D3D12_INDIRECT_ARGUMENT_TYPE_DRAW_INDEXED,D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH,D3D12_INDIRECT_ARGUMENT_TYPE_VERTEX_BUFFER_VIEW,D3D12_INDIRECT_ARGUMENT_TYPE_INDEX_BUFFER_VIEW,D3D12_INDIRECT_ARGUMENT_TYPE_CONSTANT,D3D12_INDIRECT_ARGUMENT_TYPE_CONSTANT_BUFFER_VIEW,D3D12_INDIRECT_ARGUMENT_TYPE_SHADER_RESOURCE_VIEW,D3D12_INDIRECT_ARGUMENT_TYPE_UNORDERED_ACCESS_VIEW,D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH_RAYS,D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH_MESH,D3D12_INDIRECT_ARGUMENT_TYPE_INCREMENTING_CONSTANT
} ;D3D12_INDIRECT_ARGUMENT_TYPE_DRAW 
指示类型为 Draw 调用。D3D12_INDIRECT_ARGUMENT_TYPE_DRAW_INDEXED
指示类型是 DrawIndexed 调用。D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH
指示类型是 Dispatch 调用。D3D12_INDIRECT_ARGUMENT_TYPE_VERTEX_BUFFER_VIEW
指示类型是顶点缓冲区视图。D3D12_INDIRECT_ARGUMENT_TYPE_INDEX_BUFFER_VIEW
指示类型是索引缓冲区视图。D3D12_INDIRECT_ARGUMENT_TYPE_CONSTANT
指示类型为常量。D3D12_INDIRECT_ARGUMENT_TYPE_CONSTANT_BUFFER_VIEW
指示类型是 CBV 常量缓冲区视图。D3D12_INDIRECT_ARGUMENT_TYPE_SHADER_RESOURCE_VIEW
指示类型是 SRV 着色器资源视图。D3D12_INDIRECT_ARGUMENT_TYPE_UNORDERED_ACCESS_VIEW
指示类型是 UAV 无序访问视图。

程序中使用 D3D12_INDIRECT_ARGUMENT_TYPE_CONSTANT_BUFFER_VIEW (CBV)常量缓冲区视图 )和 D3D12_INDIRECT_ARGUMENT_TYPE_DRAW ( Draw 调用)。创建命令签名的代码

void CExecuteIndirect::CreateCommandSignature2(void)
{D3D12_INDIRECT_ARGUMENT_DESC argumentDescs[2] = {};argumentDescs[0].Type = D3D12_INDIRECT_ARGUMENT_TYPE_CONSTANT_BUFFER_VIEW;argumentDescs[0].ConstantBufferView.RootParameterIndex = 0;argumentDescs[1].Type = D3D12_INDIRECT_ARGUMENT_TYPE_DRAW;D3D12_COMMAND_SIGNATURE_DESC commandSignatureDesc = {};commandSignatureDesc.pArgumentDescs = argumentDescs;commandSignatureDesc.NumArgumentDescs = _countof(argumentDescs);commandSignatureDesc.ByteStride = sizeof(IndirectCommand);CreateCommandSignature(&commandSignatureDesc,m_pipelineStatePSO.GetRootSignature(), m_commandSignature);
}

这里声明了两个参数,第一个常量缓冲区视图,第二个 绘画调用,结构体

struct IndirectCommand
{D3D12_GPU_VIRTUAL_ADDRESS cbv;D3D12_DRAW_ARGUMENTS drawArguments;
};

 cbv可以理解为指向数据的指针。

2.创建命令资源数据

要创建一个默认堆资源 ID3D12Resource,首先要确定数据的大小,比如要显示 512个小三角形,交换链有两个,每帧数据大小 512 * sizeof(IndirectCommand),用一个资源来存放=2*512 * sizeof(IndirectCommand)。由于是默认堆资源,所以要借助一个上传堆来初化数据 代码如下

void CExecuteIndirect::CreateCommandBuffers()
{std::vector<IndirectCommand> commands;commands.resize(TriangleResourceCount);const UINT commandBufferSize = CommandSizePerFrame * m_nFrameCount;CreateCommittedResource(m_device, CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),D3D12_HEAP_FLAG_NONE, CD3DX12_RESOURCE_DESC::Buffer(commandBufferSize), D3D12_RESOURCE_STATE_COPY_DEST, m_commandResourceBuffer);auto pItem = m_vectorStatisticResource.GetStatisticResourceItemByType(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);D3D12_GPU_VIRTUAL_ADDRESS gpuAddress = pItem->GetGPUVirtualAddress(0);UINT commandIndex = 0;for (UINT frame = 0; frame < m_nFrameCount; frame++){for (UINT n = 0; n < TriangleCount; n++){commands[commandIndex].cbv = gpuAddress;commands[commandIndex].drawArguments.VertexCountPerInstance = 3;commands[commandIndex].drawArguments.InstanceCount = 1;commands[commandIndex].drawArguments.StartVertexLocation = 0;commands[commandIndex].drawArguments.StartInstanceLocation = 0;commandIndex++;gpuAddress += sizeof(SceneConstantBuffer);}}D3D12_SUBRESOURCE_DATA commandData = {};commandData.pData = reinterpret_cast<UINT8*>(&commands[0]);commandData.RowPitch = commandBufferSize;commandData.SlicePitch = commandData.RowPitch;m_cmdPrimaryInfo.UploadDataToDefaultHeap(m_commandResourceBuffer.Get(), commandData, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);
}

3.创建常量缓冲区视图 (CBV)数据

注意 程序中并没有调用 CreateConstantBufferView(), 根签名中 声明了InitAsConstantBufferView()使用,程序没有明指其联系,因程序中描述符堆只一个常量缓冲区视图资源,命令资源中的数据也是直接引用它,创建代码

void CExecuteIndirect::OnInitD3D12CbvSrvUavResource(D3D12ResourceItem* pResourceItem)
{int cbvSize = TriangleResourceCount * sizeof(SceneConstantBuffer);cbvSize = UPPER_ALING_DIV(cbvSize, 256);CreateCommittedResource(m_device, CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD), D3D12_HEAP_FLAG_NONE, CD3DX12_RESOURCE_DESC::Buffer(cbvSize), D3D12_RESOURCE_STATE_GENERIC_READ, pResourceItem->GetResourceListItem(0));/* D3D12_CONSTANT_BUFFER_VIEW_DESC cbvDesc = {};cbvDesc.BufferLocation = pResourceItem->GetGPUVirtualAddress(0);cbvDesc.SizeInBytes = sizeof(SceneConstantBuffer);//CreateConstantBufferView(m_device, cbvDesc, pResourceItem->GetCPUDescriptorHandle(0));*/m_pCbvDataBegin = (UINT8*)pResourceItem->MapResource(0);}

初始化时填充数据代码 

void CExecuteIndirect::OnInitUserData(void)
{m_constantBufferData.resize(TriangleResourceCount);for (UINT n = 0; n < TriangleCount; n++){m_constantBufferData[n].velocity = XMFLOAT4(GetRandomFloat(0.01f, 0.03f), 0.0f, 0.0f, 0.0f);m_constantBufferData[n].offset = XMFLOAT4(GetRandomFloat(-5.0f, -1.5f), GetRandomFloat(1.0f, 5.0f), GetRandomFloat(0.0f, 2.0f), 0.0f);m_constantBufferData[n].color = XMFLOAT4(GetRandomFloat(0.0f, 1.0f), GetRandomFloat(0.0f, 1.0f), GetRandomFloat(0.0f, 1.0f), 1.0f);XMStoreFloat4x4(&m_constantBufferData[n].projection, XMMatrixTranspose(XMMatrixPerspectiveFovLH(XM_PIDIV4, GetWindowAspectRatio(), 0.01f, 20.0f)));}CreateCommandSignature2();CreateCommandBuffers();}

动态时填充数据代码 

void CExecuteIndirect::UpdateData(void)
{if (m_swapChainEvent != NULL){WaitForSingleObjectEx(m_swapChainEvent, 100, FALSE);}for (UINT n = 0; n < TriangleCount; n++){const float offsetBounds = 2.5f;// Animate the triangles.m_constantBufferData[n].offset.x += m_constantBufferData[n].velocity.x;m_constantBufferData[n].offset.y -= m_constantBufferData[n].velocity.x;if (m_constantBufferData[n].offset.x > offsetBounds){m_constantBufferData[n].velocity.x = GetRandomFloat(0.001f, 0.03f);m_constantBufferData[n].offset.x = -offsetBounds;}if (m_constantBufferData[n].offset.y < -offsetBounds){m_constantBufferData[n].offset.y += offsetBounds*2;}}UINT8* destination = m_pCbvDataBegin + (TriangleCount * m_frameIndex * sizeof(SceneConstantBuffer));memcpy(destination, &m_constantBufferData[0], TriangleCount * sizeof(SceneConstantBuffer));}

 UINT8* destination = m_pCbvDataBegin + (TriangleCount * m_frameIndex * sizeof(SceneConstantBuffer));  

m_constantBufferData记录了所有小三角形的位置,m_frameIndex 可能是1或0,这段代码功能是m_constantBufferData更新到当前帧。

SceneConstantBuffer结构体 

struct SceneConstantBuffer
{XMFLOAT4 velocity; 移动速度XMFLOAT4 offset;  位置XMFLOAT4 color;  XMFLOAT4X4 projection;float padding[36];
};

velocity 移动速度 给定一个随机值

offset  位置 集中在左上边

color 给定一个随机值

4. 其他 

顶点数据只一组数据,它定义了每个三角形的大小。

void CExecuteIndirect::InitializeVertexBuffer3(PSOPipelineState& pipeline)
{Vertex dataVertex[] ={{ { 0.0f, m_fWidth, m_fDepth,1 } },{ { m_fWidth, -m_fWidth, m_fDepth,1 } },{ { -m_fWidth, -m_fWidth, m_fDepth,1 } },};pipeline.InitializeVertexBuffer(&dataVertex,sizeof(dataVertex),sizeof(Vertex));
}

ExecuteIndirect_VShader.hlsl

PSInput main(float4 pos : POSITION)
{PSInput result;result.position = mul(pos + offset, projection);///float intensity = saturate((4.0f - result.position.z) / 2.0f);result.color = float4(color.xyz, 1);return result;
}

一个三角形是由三个顶点数据构成的,所每个顶点坐标加一个位置 然后 mvp运算得到屏幕坐标。

工程代码最近做重构由于时间的关系还有很多地方要做优化,目前将directx12分为以下几个部分

1. 默认创建

比如会默认创建一个ID3D12Device对象,一个命令队列,交换链,RTV,DSV等,在基类里默认创建,这样子类可以专注新增的东西。

2. PSO渲染管线状态

由根签名、shader程序构成是个繁杂的设置,这里先填充默认值,由子类根据需要改写其值 比如我们程序的处理渲染管线状态的代码

void CExecuteIndirect::InitPSOPipelineState0(PSOPipelineState& pipeline)
{pipeline.SetRootParamCount(1);pipeline.GetD3D12RootParameter(0).InitAsConstantBufferView(0, 0, D3D12_ROOT_DESCRIPTOR_FLAG_DATA_STATIC, D3D12_SHADER_VISIBILITY_VERTEX);pipeline.TableBuildInitRootSignatureDesc(D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT,false);pipeline.AddShaderByteCode(GetFilePath(L"ExecuteIndirect_VShader.cso"), PSOPipelineState::VS);pipeline.AddShaderByteCode(GetFilePath(L"ExecuteIndirect_PShader.cso"), PSOPipelineState::PS);}

3. 资源声明

用一个vector管理描述符堆相关连的资源,自动创建描述符堆,资源的创建由子类去实现,用法下如

void CExecuteIndirect::CreateD3D12StatementResourceItemLayout5(D3D12StatisticResource& statisticResource)
{auto item1 = D3D12ResourceItem(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, 1);statisticResource.CreateD3D12StatisticResourceItem(item1);
}

本章内容相对比较易容,程序运行效果如下 

工程代码全部都是免费下载的,不要积分也不要钱,禁止商用,个人免费,违者必究。


感谢大家的支持,如要问题欢迎提问指正。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/89152.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/89152.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

fastApi连接数据库

1&#xff1a;pip install tortoise-orm2&#xff1a;pip install aiomysql3&#xff1a;pip install asyncmy或者使用国内清华园pip install -i https://pypi.tuna.tsinghua.edu.cn/simple asyncmy4&#xff1a;pip install aerich通过 python -m 直接运行&#xff08;推荐&a…

Apache-web服务器环境搭建

目录 实验要求 思路总结 1.常规配置web服务 2.通过用户主页配置web服务 3.通过虚拟目录配置web服务 4.添加DNS解析服务&#xff0c;访问虚拟机域名&#xff1a; www.TestWeb.com 实验要求 (ip 192.168.48.130) 1、常规配置web服务 2、通过用户主页配置web服务 3、通过虚…

Altium Designer 25 安装与配置完整教程

本教程将带您一步步完成 Altium Designer 25 的下载、安装与激活配置 第一步&#xff1a;下载安装包 首先&#xff0c;需要获取 Altium Designer 25 的完整安装程序。 &#x1f449; 下载链接&#xff1a; 百度网盘&#xff1a;百度网盘 请输入提取码 提取码: dxei 夸克网盘…

【工具】AndroidStudio修改中文语言汉化

AndroidStudio修改中文语言汉化 https://github.com/sollyu/AndroidStudioChineseLanguagePackhttps://github.com/sollyu/AndroidStudioChineseLanguagePack

代码随想录|图论|15并查集理论基础

并查集理论基础 | 代码随想录 并查集还是比较简单的&#xff0c;只要搞清楚两个事情&#xff1a; 并查集是干啥的&#xff1f;解决什么类型问题&#xff1f;并查集模板&#xff08;背下来&#xff09; 1、并查集是干啥的 并查集主要是两个功能&#xff1a; 两个元素添加到…

用MYSQL学习sql第一次总结和作业

总结 数据库&#xff08;Database&#xff09; 理解为“文件夹”&#xff0c;里面可以装很多张表。作业中要求先建一个名字叫 mydb6_product 的数据库。 表&#xff08;Table&#xff09; 理解为“Excel 工作表”&#xff0c;由“列&#xff08;字段&#xff09;”和“行&…

SQLite技术架构解析,适用场景有哪些?

一、SQLite技术架构解析 SQLite是一款轻量级、无服务器、嵌入式关系型数据库&#xff0c;其架构设计围绕“简化复杂性、提升效率”展开&#xff0c;核心由前端&#xff08;SQL处理&#xff09;、执行引擎&#xff08;VDBE&#xff09;、存储引擎&#xff08;B-Tree&#xff09;…

【Luogu】每日一题——Day3. P6392 中意 (数学 取模)

链接&#xff1a;P6392 中意 - 洛谷 题目&#xff1a; 思路&#xff1a; 数论这一块 题目让我们求这个结果对 MOD 取模&#xff0c;那么我们肯定是不像看到这个除法&#xff0c;所以考虑如何消除这个除法 我们可以想到&#xff0c;向上取整就是加上一个数&#xff0c;假设其为…

React强大且灵活hooks库——ahooks入门实践之DOM类hook(dom)详解

什么是 ahooks&#xff1f; ahooks 是一个 React Hooks 库&#xff0c;提供了大量实用的自定义 hooks&#xff0c;帮助开发者更高效地构建 React 应用。其中 DOM 类 hooks 是 ahooks 的一个重要分类&#xff0c;专门用于处理 DOM 相关操作&#xff0c;如事件监听、元素状态、拖…

GeoTools 工厂设计模式

前言使用GeoTools开发时有必要了解其工厂设计模式&#xff0c;作为软件开发核心设计模式&#xff0c;其设计思想具有普遍性和研究性。明白方法原理有助于提高开发效率&#xff0c;达到事半功倍的效果。1. 工厂模式 工厂模式&#xff08;Factory Pattern&#xff09;是面向对象中…

npu-smi info命令参数解释

华为昇腾npu-smi显示npu-smi工具的帮助信息npu-smi -h字段说明-h命令的帮助信息–help命令的帮助信息-vnpu-smi版本信息info显示硬件详细信息set修改设备配置属性clear清除设备信息upgrade升级MCU固件 npu-smi info 用于监控和管理华为NPU的状态和性能字段值说明npu-smi24.1.rc…

OneCode3.0 通信架构简介——MCPServer微内核设计哲学与实现

在数字化转型加速的今天&#xff0c;低代码平台已成为企业快速交付应用的核心基础设施。然而&#xff0c;通用消息中间件与低代码开发范式之间存在难以调和的矛盾&#xff1a;标准化协议无法匹配可视化编排的动态性&#xff0c;通用架构难以满足低代码场景下高频短消息的性能需…

Android14 Launcher3 修改All App上下滑动头部显示阴影

正常情况下的样子&#xff1a; 下拉App抽屉后的样子&#xff1a;修改方案&#xff1a;qssi14/packages/apps/Launcher3/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.javaprotected void updateHeaderScroll(int scrolledOffset) {float prog1 Utilities…

Zookeeper入门安装与使用详解

文章目录一、简介二、下载安装1、安装jdk2、windows&#xff08;1&#xff09;下载&#xff08;2&#xff09;配置与启动一、简介 略。 二、下载安装 1、安装jdk 安装jdk8&#xff0c;高版本可能会有问题。 2、windows &#xff08;1&#xff09;下载 官网地址&#xff…

设计模式之适配器模式:让不兼容的接口协同工作的艺术

适配器模式&#xff1a;让不兼容的接口协同工作的艺术在软件开发中&#xff0c;我们经常会遇到系统整合的挑战——如何让新旧组件协同工作&#xff1f;适配器模式正是解决这类接口不兼容问题的利器&#xff0c;本文将深入探讨这一经典设计模式。1. 引言&#xff1a;接口不兼容的…

AI驱动的软件工程(中):文档驱动的编码与执行

&#x1f4da; 系列文章导航 AI驱动的软件工程&#xff08;上&#xff09;&#xff1a;人机协同的设计与建模 AI驱动的软件工程&#xff08;中&#xff09;&#xff1a;文档驱动的编码与执行 AI驱动的软件工程&#xff08;下&#xff09;&#xff1a;AI辅助的质检与交付 大家好…

HTML应用指南:利用GET请求获取河南省胖东来超市门店位置信息

胖东来作为中国知名的零售企业&#xff0c;自1995年成立以来&#xff0c;始终致力于为消费者提供丰富、新鲜的商品选择与优质的购物体验。经过近30年的稳步发展&#xff0c;目前已在河南省内的许昌、新乡等地共开设13家门店&#xff0c;涵盖大型综合百货商场、中型社区超市及服…

8.服务通信:Feign深度优化 - 解密声明式调用与现代负载均衡内核

让服务调用更优雅 在微服务架构中,服务间通信如同血液流动般重要。传统方式中,开发者需要手动拼接URL、处理负载均衡、管理连接池——这些重复性工作不仅效率低下,还容易出错。Spring Cloud OpenFeign 的诞生,正是为了解决这一核心痛点。它通过声明式接口将HTTP请求模板化…

Docker入门指南(超详细)

一、什么是docker 在云计算和微服务架构盛行的今天&#xff0c;Docker 作为容器技术的标杆&#xff0c;彻底改变了应用部署和运行的方式。简单来说&#xff0c;Docker 是一个开源的容器化平台&#xff0c;它通过将应用程序及其依赖环境打包成一个轻量级、可移植的容器&#xff…

学习秒杀系统-实现秒杀功能(商品列表,商品详情,基本秒杀功能实现,订单详情)

文章目录前言数据库设计秒杀商品列表页秒杀商品详情实现简单秒杀订单详情前言 由于慕课课程中是先实现最基本的功能然后对其压测&#xff0c;压测那个地方出问题&#xff0c;然后在对其优化。所以本文记录的也是实现的是简单的秒杀功能没有涉及到高并发的优化。 数据库设计 …