经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » C++ » 查看文章
d3d12龙书阅读----绘制几何体(下)
来源:cnblogs  作者:dyccyber  时间:2024/5/11 8:53:25  对本文有异议

d3d12龙书阅读----绘制几何体(下)

本节在上一节的基础上,对整个绘制过程进行优化,将绘制单个几何体的内容拓展到了多个几何体,同时对根签名进行了进一步地探索。

帧资源

在之前绘制每帧的结尾,我们都要使用flushingcommandqueue方法,要一直等待gpu执行完所有命令,才会继续绘制下一帧,此时cpu处于空闲时间,同时,在绘制每一帧的初始阶段,gpu要等待cpu提交命令,此时gpu处于空闲时间
解决上述问题的一种方法是:
构建以cpu每帧都要更新的资源为数组元素的环形数组,这些资源被称为帧资源,一般循环数组由3个帧资源元素构成
当gpu在处理上一帧的命令时,cpu可以为下一帧更新资源,并构建并提交相应的命令列表,如果环形数组有三个元素,则令cpu比gpu提前处理两帧,这样可以确保gpu持续工作
帧资源定义:

针对每个物体/几何体的常量缓冲区定义
目前存储的是每个物体的世界矩阵 即 模型矩阵 将物体从局部坐标系转换到世界坐标系 代表着物体的位置

  1. struct ObjectConstants
  2. {
  3. DirectX::XMFLOAT4X4 World = MathHelper::Identity4x4();
  4. };
  5. 针对每次渲染过程(rendering pass)所要用到的数据
  6. 比如 观察矩阵 投影矩阵 时间 等等
  7. struct PassConstants
  8. {
  9. DirectX::XMFLOAT4X4 View = MathHelper::Identity4x4();
  10. DirectX::XMFLOAT4X4 InvView = MathHelper::Identity4x4();
  11. DirectX::XMFLOAT4X4 Proj = MathHelper::Identity4x4();
  12. DirectX::XMFLOAT4X4 InvProj = MathHelper::Identity4x4();
  13. DirectX::XMFLOAT4X4 ViewProj = MathHelper::Identity4x4();
  14. DirectX::XMFLOAT4X4 InvViewProj = MathHelper::Identity4x4();
  15. DirectX::XMFLOAT3 EyePosW = { 0.0f, 0.0f, 0.0f };
  16. float cbPerObjectPad1 = 0.0f;
  17. DirectX::XMFLOAT2 RenderTargetSize = { 0.0f, 0.0f };
  18. DirectX::XMFLOAT2 InvRenderTargetSize = { 0.0f, 0.0f };
  19. float NearZ = 0.0f;
  20. float FarZ = 0.0f;
  21. float TotalTime = 0.0f;
  22. float DeltaTime = 0.0f;
  23. };
  24. 顶点定义
  25. struct Vertex
  26. {
  27. DirectX::XMFLOAT3 Pos;
  28. DirectX::XMFLOAT4 Color;
  29. };
  30. 存储cpu为一帧构建命令列表所需资源
  31. struct FrameResource
  32. {
  33. public:
  34. FrameResource(ID3D12Device* device, UINT passCount, UINT objectCount);
  35. FrameResource(const FrameResource& rhs) = delete;
  36. FrameResource& operator=(const FrameResource& rhs) = delete;
  37. ~FrameResource();
  38. 每一帧都要有自己的命令分配器
  39. 因为当上一帧的gpu还在处理命令时 我们不能重置命令分配器
  40. Microsoft::WRL::ComPtr<ID3D12CommandAllocator> CmdListAlloc;
  41. 同理 每个帧资源也要有自己的常量缓冲区
  42. std::unique_ptr<UploadBuffer<PassConstants>> PassCB = nullptr;
  43. std::unique_ptr<UploadBuffer<ObjectConstants>> ObjectCB = nullptr;
  44. 围栏点可以帮助检测 gpu是否仍然使用着帧资源
  45. UINT64 Fence = 0;
  46. };

可以看到我们在帧资源中将常量缓冲区分为pass 与 object, 这是基于资源的更新频率对常量资源进行分组,每次渲染过程我们都要更新pass缓冲区,而对于object来说,只有当发生变化的时候才需要更新,具体代码我们待会再看。

回到cpu与gpu的同步上来,首先创建初始化帧资源数组:

  1. void ShapesApp::BuildFrameResources()
  2. {
  3. for(int i = 0; i < gNumFrameResources; ++i)
  4. {
  5. mFrameResources.push_back(std::make_unique<FrameResource>(md3dDevice.Get(),
  6. 1, (UINT)mAllRitems.size()));
  7. 其中1代表着一个帧资源1pass缓冲区 第二个是所有渲染物体的数目
  8. }
  9. }

cpu端更新第n帧:

  1. void ShapesApp::Update(const GameTimer& gt)
  2. {
  3. OnKeyboardInput(gt);
  4. UpdateCamera(gt);
  5. 循环帧资源数组
  6. mCurrFrameResourceIndex = (mCurrFrameResourceIndex + 1) % gNumFrameResources;
  7. mCurrFrameResource = mFrameResources[mCurrFrameResourceIndex].get();
  8. 等待gpu完成围栏点之前的所有命令
  9. if(mCurrFrameResource->Fence != 0 && mFence->GetCompletedValue() < mCurrFrameResource->Fence)
  10. {
  11. HANDLE eventHandle = CreateEventEx(nullptr, false, false, EVENT_ALL_ACCESS);
  12. ThrowIfFailed(mFence->SetEventOnCompletion(mCurrFrameResource->Fence, eventHandle));
  13. WaitForSingleObject(eventHandle, INFINITE);
  14. CloseHandle(eventHandle);
  15. }
  16. 更新常量缓冲区
  17. UpdateObjectCBs(gt);
  18. UpdateMainPassCB(gt);
  19. }

绘制第n帧:

  1. void ShapesApp::draw(const GameTimer& gt){
  2. 添加围栏值 将命令标记到此围栏点
  3. mCurrFrameResource->Fence = ++mCurrentFence;
  4. 向命令队列中添加一条设置新围栏点的命令
  5. 由于这条命令要交给gpu处理,所以gpu处理完signal之前的所有命令之前,它不会设置新的围栏点
  6. mCommandQueue->Signal(mFence.Get(), mCurrentFence);
  7. }

其实这种方法也有着缺陷,如果gpu处理命令的速度大于cpu提交命令列表的速度,则还是要等待cpu,理想的情况是cpu处理帧的速度大于gpu,这样cpu可以有空闲时间来处理游戏逻辑的其它部分,此方法的最大好处是cpu可以持续向gpu提供数据

渲染项

渲染项是一个轻量型结构 用于存储绘制物体所需要数据:

  1. struct RenderItem
  2. {
  3. RenderItem() = default;
  4. 世界矩阵
  5. XMFLOAT4X4 World = MathHelper::Identity4x4();
  6. // 一个脏标记用于记录是否需要更新物体缓冲区 因为每个帧资源都有各自独立的物体缓冲区 所以脏标记的数目要设置和帧资源数目一致
  7. int NumFramesDirty = gNumFrameResources;
  8. // 当前渲染项对应object缓冲区索引
  9. UINT ObjCBIndex = -1;
  10. 该渲染项参与绘制的几何体
  11. MeshGeometry* Geo = nullptr;
  12. //图元拓扑类型
  13. D3D12_PRIMITIVE_TOPOLOGY PrimitiveType = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
  14. // DrawIndexedInstanced 方法的参数
  15. UINT IndexCount = 0;
  16. UINT StartIndexLocation = 0;
  17. int BaseVertexLocation = 0;
  18. };

渲染项的具体使用之后介绍

渲染过程中用到的常量数据

我们需要更新hlsl中用到的cbuffer:

  1. cbuffer cbPerObject : register(b0)
  2. {
  3. float4x4 gWorld;
  4. };
  5. cbuffer cbPass : register(b1)
  6. {
  7. float4x4 gView;
  8. float4x4 gInvView;
  9. float4x4 gProj;
  10. float4x4 gInvProj;
  11. float4x4 gViewProj;
  12. float4x4 gInvViewProj;
  13. float3 gEyePosW;
  14. float cbPerObjectPad1;
  15. float2 gRenderTargetSize;
  16. float2 gInvRenderTargetSize;
  17. float gNearZ;
  18. float gFarZ;
  19. float gTotalTime;
  20. float gDeltaTime;
  21. };

更新object缓冲区 与 pass缓冲区 这里利用了前一节介绍的uploadbuffer的方法 从cpu端更新数据:

  1. void ShapesApp::UpdateObjectCBs(const GameTimer& gt)
  2. {
  3. auto currObjectCB = mCurrFrameResource->ObjectCB.get();
  4. for(auto& e : mAllRitems)
  5. {
  6. 每个帧资源都需要更新物体缓冲区
  7. if(e->NumFramesDirty > 0)
  8. {
  9. XMMATRIX world = XMLoadFloat4x4(&e->World);
  10. ObjectConstants objConstants;
  11. XMStoreFloat4x4(&objConstants.World, XMMatrixTranspose(world));
  12. currObjectCB->CopyData(e->ObjCBIndex, objConstants);
  13. // Next FrameResource need to be updated too.
  14. e->NumFramesDirty--;
  15. }
  16. }
  17. }
  18. void ShapesApp::UpdateMainPassCB(const GameTimer& gt)
  19. {
  20. XMMATRIX view = XMLoadFloat4x4(&mView);
  21. XMMATRIX proj = XMLoadFloat4x4(&mProj);
  22. XMMATRIX viewProj = XMMatrixMultiply(view, proj);
  23. XMMATRIX invView = XMMatrixInverse(&XMMatrixDeterminant(view), view);
  24. XMMATRIX invProj = XMMatrixInverse(&XMMatrixDeterminant(proj), proj);
  25. XMMATRIX invViewProj = XMMatrixInverse(&XMMatrixDeterminant(viewProj), viewProj);
  26. XMStoreFloat4x4(&mMainPassCB.View, XMMatrixTranspose(view));
  27. XMStoreFloat4x4(&mMainPassCB.InvView, XMMatrixTranspose(invView));
  28. XMStoreFloat4x4(&mMainPassCB.Proj, XMMatrixTranspose(proj));
  29. XMStoreFloat4x4(&mMainPassCB.InvProj, XMMatrixTranspose(invProj));
  30. XMStoreFloat4x4(&mMainPassCB.ViewProj, XMMatrixTranspose(viewProj));
  31. XMStoreFloat4x4(&mMainPassCB.InvViewProj, XMMatrixTranspose(invViewProj));
  32. mMainPassCB.EyePosW = mEyePos;
  33. mMainPassCB.RenderTargetSize = XMFLOAT2((float)mClientWidth, (float)mClientHeight);
  34. mMainPassCB.InvRenderTargetSize = XMFLOAT2(1.0f / mClientWidth, 1.0f / mClientHeight);
  35. mMainPassCB.NearZ = 1.0f;
  36. mMainPassCB.FarZ = 1000.0f;
  37. mMainPassCB.TotalTime = gt.TotalTime();
  38. mMainPassCB.DeltaTime = gt.DeltaTime();
  39. auto currPassCB = mCurrFrameResource->PassCB.get();
  40. currPassCB->CopyData(0, mMainPassCB);
  41. }

绘制多种几何体

在这里就不再介绍柱体 球体 正方体的过程 设计到一些几何知识
直接进入几何体的绘制阶段

创建顶点与索引缓冲区

将所有几何体的顶点缓冲区 与 索引缓冲区,合成一个大的顶点缓冲区与 索引缓冲区,之后使用drawindexinstanced方法绘制 需要记录每个几何体起始索引 索引数 以及起始顶点

  1. void ShapesApp::BuildShapeGeometry()
  2. {
  3. GeometryGenerator geoGen;
  4. GeometryGenerator::MeshData box = geoGen.CreateBox(1.5f, 0.5f, 1.5f, 3);
  5. GeometryGenerator::MeshData grid = geoGen.CreateGrid(20.0f, 30.0f, 60, 40);
  6. GeometryGenerator::MeshData sphere = geoGen.CreateSphere(0.5f, 20, 20);
  7. GeometryGenerator::MeshData cylinder = geoGen.CreateCylinder(0.5f, 0.3f, 3.0f, 20, 20);
  8. // 计算各几何体的起始顶点
  9. UINT boxVertexOffset = 0;
  10. UINT gridVertexOffset = (UINT)box.Vertices.size();
  11. UINT sphereVertexOffset = gridVertexOffset + (UINT)grid.Vertices.size();
  12. UINT cylinderVertexOffset = sphereVertexOffset + (UINT)sphere.Vertices.size();
  13. // 存储起始索引
  14. UINT boxIndexOffset = 0;
  15. UINT gridIndexOffset = (UINT)box.Indices32.size();
  16. UINT sphereIndexOffset = gridIndexOffset + (UINT)grid.Indices32.size();
  17. UINT cylinderIndexOffset = sphereIndexOffset + (UINT)sphere.Indices32.size();
  18. 定义各子网格结构体
  19. SubmeshGeometry boxSubmesh;
  20. boxSubmesh.IndexCount = (UINT)box.Indices32.size();
  21. boxSubmesh.StartIndexLocation = boxIndexOffset;
  22. boxSubmesh.BaseVertexLocation = boxVertexOffset;
  23. SubmeshGeometry gridSubmesh;
  24. gridSubmesh.IndexCount = (UINT)grid.Indices32.size();
  25. gridSubmesh.StartIndexLocation = gridIndexOffset;
  26. gridSubmesh.BaseVertexLocation = gridVertexOffset;
  27. SubmeshGeometry sphereSubmesh;
  28. sphereSubmesh.IndexCount = (UINT)sphere.Indices32.size();
  29. sphereSubmesh.StartIndexLocation = sphereIndexOffset;
  30. sphereSubmesh.BaseVertexLocation = sphereVertexOffset;
  31. SubmeshGeometry cylinderSubmesh;
  32. cylinderSubmesh.IndexCount = (UINT)cylinder.Indices32.size();
  33. cylinderSubmesh.StartIndexLocation = cylinderIndexOffset;
  34. cylinderSubmesh.BaseVertexLocation = cylinderVertexOffset;
  35. 将各顶点 各索引合并
  36. 子网格合并为一个大的meshgeometry
  37. auto totalVertexCount =
  38. box.Vertices.size() +
  39. grid.Vertices.size() +
  40. sphere.Vertices.size() +
  41. cylinder.Vertices.size();
  42. std::vector<Vertex> vertices(totalVertexCount);
  43. UINT k = 0;
  44. for(size_t i = 0; i < box.Vertices.size(); ++i, ++k)
  45. {
  46. vertices[k].Pos = box.Vertices[i].Position;
  47. vertices[k].Color = XMFLOAT4(DirectX::Colors::DarkGreen);
  48. }
  49. for(size_t i = 0; i < grid.Vertices.size(); ++i, ++k)
  50. {
  51. vertices[k].Pos = grid.Vertices[i].Position;
  52. vertices[k].Color = XMFLOAT4(DirectX::Colors::ForestGreen);
  53. }
  54. for(size_t i = 0; i < sphere.Vertices.size(); ++i, ++k)
  55. {
  56. vertices[k].Pos = sphere.Vertices[i].Position;
  57. vertices[k].Color = XMFLOAT4(DirectX::Colors::Crimson);
  58. }
  59. for(size_t i = 0; i < cylinder.Vertices.size(); ++i, ++k)
  60. {
  61. vertices[k].Pos = cylinder.Vertices[i].Position;
  62. vertices[k].Color = XMFLOAT4(DirectX::Colors::SteelBlue);
  63. }
  64. std::vector<std::uint16_t> indices;
  65. indices.insert(indices.end(), std::begin(box.GetIndices16()), std::end(box.GetIndices16()));
  66. indices.insert(indices.end(), std::begin(grid.GetIndices16()), std::end(grid.GetIndices16()));
  67. indices.insert(indices.end(), std::begin(sphere.GetIndices16()), std::end(sphere.GetIndices16()));
  68. indices.insert(indices.end(), std::begin(cylinder.GetIndices16()), std::end(cylinder.GetIndices16()));
  69. const UINT vbByteSize = (UINT)vertices.size() * sizeof(Vertex);
  70. const UINT ibByteSize = (UINT)indices.size() * sizeof(std::uint16_t);
  71. auto geo = std::make_unique<MeshGeometry>();
  72. geo->Name = "shapeGeo";
  73. ThrowIfFailed(D3DCreateBlob(vbByteSize, &geo->VertexBufferCPU));
  74. CopyMemory(geo->VertexBufferCPU->GetBufferPointer(), vertices.data(), vbByteSize);
  75. ThrowIfFailed(D3DCreateBlob(ibByteSize, &geo->IndexBufferCPU));
  76. CopyMemory(geo->IndexBufferCPU->GetBufferPointer(), indices.data(), ibByteSize);
  77. geo->VertexBufferGPU = d3dUtil::CreateDefaultBuffer(md3dDevice.Get(),
  78. mCommandList.Get(), vertices.data(), vbByteSize, geo->VertexBufferUploader);
  79. geo->IndexBufferGPU = d3dUtil::CreateDefaultBuffer(md3dDevice.Get(),
  80. mCommandList.Get(), indices.data(), ibByteSize, geo->IndexBufferUploader);
  81. geo->VertexByteStride = sizeof(Vertex);
  82. geo->VertexBufferByteSize = vbByteSize;
  83. geo->IndexFormat = DXGI_FORMAT_R16_UINT;
  84. geo->IndexBufferByteSize = ibByteSize;
  85. geo->DrawArgs["box"] = boxSubmesh;
  86. geo->DrawArgs["grid"] = gridSubmesh;
  87. geo->DrawArgs["sphere"] = sphereSubmesh;
  88. geo->DrawArgs["cylinder"] = cylinderSubmesh;
  89. mGeometries[geo->Name] = std::move(geo);
  90. }

定义具体渲染项

在完成构建几何体之后 我们根据上一步创建的meshgeometry 来提取submeshgeometry 然后 里面的信息 根据需要创建相应的渲染项 并填写相应的内容

  1. void ShapesApp::BuildRenderItems()
  2. {
  3. auto boxRitem = std::make_unique<RenderItem>();
  4. XMStoreFloat4x4(&boxRitem->World, XMMatrixScaling(2.0f, 2.0f, 2.0f)*XMMatrixTranslation(0.0f, 0.5f, 0.0f));
  5. boxRitem->ObjCBIndex = 0;
  6. boxRitem->Geo = mGeometries["shapeGeo"].get();
  7. boxRitem->PrimitiveType = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
  8. boxRitem->IndexCount = boxRitem->Geo->DrawArgs["box"].IndexCount;
  9. boxRitem->StartIndexLocation = boxRitem->Geo->DrawArgs["box"].StartIndexLocation;
  10. boxRitem->BaseVertexLocation = boxRitem->Geo->DrawArgs["box"].BaseVertexLocation;
  11. mAllRitems.push_back(std::move(boxRitem));
  12. auto gridRitem = std::make_unique<RenderItem>();
  13. gridRitem->World = MathHelper::Identity4x4();
  14. gridRitem->ObjCBIndex = 1;
  15. gridRitem->Geo = mGeometries["shapeGeo"].get();
  16. gridRitem->PrimitiveType = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
  17. gridRitem->IndexCount = gridRitem->Geo->DrawArgs["grid"].IndexCount;
  18. gridRitem->StartIndexLocation = gridRitem->Geo->DrawArgs["grid"].StartIndexLocation;
  19. gridRitem->BaseVertexLocation = gridRitem->Geo->DrawArgs["grid"].BaseVertexLocation;
  20. mAllRitems.push_back(std::move(gridRitem));
  21. UINT objCBIndex = 2;
  22. for(int i = 0; i < 5; ++i)
  23. {
  24. auto leftCylRitem = std::make_unique<RenderItem>();
  25. auto rightCylRitem = std::make_unique<RenderItem>();
  26. auto leftSphereRitem = std::make_unique<RenderItem>();
  27. auto rightSphereRitem = std::make_unique<RenderItem>();
  28. XMMATRIX leftCylWorld = XMMatrixTranslation(-5.0f, 1.5f, -10.0f + i*5.0f);
  29. XMMATRIX rightCylWorld = XMMatrixTranslation(+5.0f, 1.5f, -10.0f + i*5.0f);
  30. XMMATRIX leftSphereWorld = XMMatrixTranslation(-5.0f, 3.5f, -10.0f + i*5.0f);
  31. XMMATRIX rightSphereWorld = XMMatrixTranslation(+5.0f, 3.5f, -10.0f + i*5.0f);
  32. XMStoreFloat4x4(&leftCylRitem->World, rightCylWorld);
  33. leftCylRitem->ObjCBIndex = objCBIndex++;
  34. leftCylRitem->Geo = mGeometries["shapeGeo"].get();
  35. leftCylRitem->PrimitiveType = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
  36. leftCylRitem->IndexCount = leftCylRitem->Geo->DrawArgs["cylinder"].IndexCount;
  37. leftCylRitem->StartIndexLocation = leftCylRitem->Geo->DrawArgs["cylinder"].StartIndexLocation;
  38. leftCylRitem->BaseVertexLocation = leftCylRitem->Geo->DrawArgs["cylinder"].BaseVertexLocation;
  39. 此处省略
  40. }
  41. for(auto& e : mAllRitems)
  42. mOpaqueRitems.push_back(e.get());
  43. }

定义常量缓冲区视图

之后由于我们现在有3个pass常量缓冲区 3n个object常量缓冲区 总共3n+3个常量缓冲区 所以就需要 3n+3个cbv 同时也要拓展描述符堆的大小:

  1. void ShapesApp::BuildDescriptorHeaps()
  2. {
  3. UINT objCount = (UINT)mOpaqueRitems.size();
  4. UINT numDescriptors = (objCount+1) * gNumFrameResources;
  5. mPassCbvOffset = objCount * gNumFrameResources;
  6. D3D12_DESCRIPTOR_HEAP_DESC cbvHeapDesc;
  7. cbvHeapDesc.NumDescriptors = numDescriptors;
  8. cbvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
  9. cbvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
  10. cbvHeapDesc.NodeMask = 0;
  11. ThrowIfFailed(md3dDevice->CreateDescriptorHeap(&cbvHeapDesc,
  12. IID_PPV_ARGS(&mCbvHeap)));
  13. }
  1. void ShapesApp::BuildConstantBufferViews()
  2. {
  3. UINT objCBByteSize = d3dUtil::CalcConstantBufferByteSize(sizeof(ObjectConstants));
  4. UINT objCount = (UINT)mOpaqueRitems.size();
  5. 每个帧资源中的每个object都需要一个cbv
  6. for(int frameIndex = 0; frameIndex < gNumFrameResources; ++frameIndex)
  7. {
  8. auto objectCB = mFrameResources[frameIndex]->ObjectCB->Resource();
  9. for(UINT i = 0; i < objCount; ++i)
  10. {
  11. D3D12_GPU_VIRTUAL_ADDRESS cbAddress = objectCB->GetGPUVirtualAddress();
  12. // 每个物体的偏移
  13. cbAddress += i*objCBByteSize;
  14. // 计算在描述符堆中的偏移
  15. int heapIndex = frameIndex*objCount + i;
  16. auto handle = CD3DX12_CPU_DESCRIPTOR_HANDLE(mCbvHeap->GetCPUDescriptorHandleForHeapStart());
  17. handle.Offset(heapIndex, mCbvSrvUavDescriptorSize);
  18. D3D12_CONSTANT_BUFFER_VIEW_DESC cbvDesc;
  19. cbvDesc.BufferLocation = cbAddress;
  20. cbvDesc.SizeInBytes = objCBByteSize;
  21. md3dDevice->CreateConstantBufferView(&cbvDesc, handle);
  22. }
  23. }
  24. UINT passCBByteSize = d3dUtil::CalcConstantBufferByteSize(sizeof(PassConstants));
  25. 每个帧资源都要一个pass 描述符
  26. for(int frameIndex = 0; frameIndex < gNumFrameResources; ++frameIndex)
  27. {
  28. auto passCB = mFrameResources[frameIndex]->PassCB->Resource();
  29. D3D12_GPU_VIRTUAL_ADDRESS cbAddress = passCB->GetGPUVirtualAddress();
  30. 计算偏移
  31. int heapIndex = mPassCbvOffset + frameIndex;
  32. auto handle = CD3DX12_CPU_DESCRIPTOR_HANDLE(mCbvHeap->GetCPUDescriptorHandleForHeapStart());
  33. handle.Offset(heapIndex, mCbvSrvUavDescriptorSize);
  34. D3D12_CONSTANT_BUFFER_VIEW_DESC cbvDesc;
  35. cbvDesc.BufferLocation = cbAddress;
  36. cbvDesc.SizeInBytes = passCBByteSize;
  37. md3dDevice->CreateConstantBufferView(&cbvDesc, handle);
  38. }
  39. }

绘制

最后一步是绘制每个渲染项 :

  1. void ShapesApp::DrawRenderItems(ID3D12GraphicsCommandList* cmdList, const std::vector<RenderItem*>& ritems)
  2. {
  3. UINT objCBByteSize = d3dUtil::CalcConstantBufferByteSize(sizeof(ObjectConstants));
  4. auto objectCB = mCurrFrameResource->ObjectCB->Resource();
  5. for(size_t i = 0; i < ritems.size(); ++i)
  6. {
  7. auto ri = ritems[i];
  8. cmdList->IASetVertexBuffers(0, 1, &ri->Geo->VertexBufferView());
  9. cmdList->IASetIndexBuffer(&ri->Geo->IndexBufferView());
  10. cmdList->IASetPrimitiveTopology(ri->PrimitiveType);
  11. UINT cbvIndex = mCurrFrameResourceIndex*(UINT)mOpaqueRitems.size() + ri->ObjCBIndex;
  12. auto cbvHandle = CD3DX12_GPU_DESCRIPTOR_HANDLE(mCbvHeap->GetGPUDescriptorHandleForHeapStart());
  13. cbvHandle.Offset(cbvIndex, mCbvSrvUavDescriptorSize);
  14. cmdList->SetGraphicsRootDescriptorTable(0, cbvHandle);
  15. cmdList->DrawIndexedInstanced(ri->IndexCount, 1, ri->StartIndexLocation, ri->BaseVertexLocation, 0);
  16. }
  17. }

细探根签名

根签名由一系列根参数构成 根参数主要有以下三种类型
img

我们可以创建出任意组合的根签名 只要不超过64 DWORD大小 根常量使用方便 无需使用相应的常量缓冲区 与 cbv堆,但是假如我们使用根常量存储mvp矩阵,16个float元素需要16个DWORD 即需要16个根常量 大幅消耗了根签名的空间 所以在使用时我们要灵活组合

根签名结构体定义:

  1. typedef struct D3D12_ROOT_PARAMETER
  2. {
  3. D3D12_ROOT_PARAMETER_TYPE ParameterType;
  4. union
  5. {
  6. D3D12_ROOT_DESCRIPTOR_TABLE DescriptorTable;
  7. D3D12_ROOT_CONSTANTS Constants;
  8. D3D12_ROOT_DESCRIPTOR Descriptor;
  9. } ;
  10. D3D12_SHADER_VISIBILITY ShaderVisibility;
  11. } D3D12_ROOT_PARAMETER;

其中ParameterType的定义是根参数的类型,包括描述符表,根常量,cbv根描述符,srv根描述符,uav根描述符:
img
ShaderVisibility代表着着色器可见性:
img

创建 DescriptorTable Constants Descriptor

DescriptorTable :
描述符表的定义可以借助CD3DX12_DESCRIPTOR_RANGE的init方法

  1. struct CD3DX12_DESCRIPTOR_RANGE : public D3D12_DESCRIPTOR_RANGE
  2. {
  3. CD3DX12_DESCRIPTOR_RANGE() { }
  4. explicit CD3DX12_DESCRIPTOR_RANGE(const D3D12_DESCRIPTOR_RANGE &o) :
  5. D3D12_DESCRIPTOR_RANGE(o)
  6. {}
  7. CD3DX12_DESCRIPTOR_RANGE(
  8. D3D12_DESCRIPTOR_RANGE_TYPE rangeType,
  9. UINT numDescriptors,
  10. UINT baseShaderRegister,
  11. UINT registerSpace = 0,
  12. UINT offsetInDescriptorsFromTableStart =
  13. D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND)
  14. {
  15. Init(rangeType, numDescriptors, baseShaderRegister, registerSpace, offsetInDescriptorsFromTableStart);
  16. }
  17. inline void Init(
  18. D3D12_DESCRIPTOR_RANGE_TYPE rangeType,
  19. UINT numDescriptors,
  20. UINT baseShaderRegister,
  21. UINT registerSpace = 0,
  22. UINT offsetInDescriptorsFromTableStart =
  23. D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND)
  24. {
  25. Init(*this, rangeType, numDescriptors, baseShaderRegister, registerSpace, offsetInDescriptorsFromTableStart);
  26. }
  27. }

其中D3D12_DESCRIPTOR_RANGE_TYPE rangeType定义为:
img
numDescriptors代表着范围内描述符的数量
baseShaderRegister:
img

img
然后使用InitAsDescriptorTable创建 :

  1. CD3DX12_DESCRIPTOR_RANGE cbvTable0;
  2. cbvTable0.Init(D3D12_DESCRIPTOR_RANGE_TYPE_CBV, 1, 0);
  3. CD3DX12_DESCRIPTOR_RANGE cbvTable1;
  4. cbvTable1.Init(D3D12_DESCRIPTOR_RANGE_TYPE_CBV, 1, 1);
  5. CD3DX12_ROOT_PARAMETER slotRootParameter[2];
  6. slotRootParameter[0].InitAsDescriptorTable(1, &cbvTable0);
  7. slotRootParameter[1].InitAsDescriptorTable(1, &cbvTable1);

根描述符与根常量的定义可以直接使用如下方法创建:

  1. static inline void InitAsConstants(
  2. _Out_ D3D12_ROOT_PARAMETER &rootParam,
  3. UINT num32BitValues,
  4. UINT shaderRegister,
  5. UINT registerSpace = 0,
  6. D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL)
  7. {
  8. rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS;
  9. rootParam.ShaderVisibility = visibility;
  10. CD3DX12_ROOT_CONSTANTS::Init(rootParam.Constants, num32BitValues, shaderRegister, registerSpace);
  11. }
  12. static inline void InitAsConstantBufferView(
  13. _Out_ D3D12_ROOT_PARAMETER &rootParam,
  14. UINT shaderRegister,
  15. UINT registerSpace = 0,
  16. D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL)
  17. {
  18. rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
  19. rootParam.ShaderVisibility = visibility;
  20. CD3DX12_ROOT_DESCRIPTOR::Init(rootParam.Descriptor, shaderRegister, registerSpace);
  21. }

例子:
img
img

不同类型的根签名绑定着色器寄存器

将不同类型的根签名绑定着色器寄存器需要使用不同的命令:

根常量:ID3D12GraphicsCommandList::SetComputeRoot32BitConstants
https://learn.microsoft.com/zh-cn/windows/win32/api/d3d12/nf-d3d12-id3d12graphicscommandlist-setcomputeroot32bitconstants
根描述符:ID3D12GraphicsCommandList::SetComputeRootConstantBufferView
https://learn.microsoft.com/zh-cn/windows/win32/api/d3d12/nf-d3d12-id3d12graphicscommandlist-setcomputerootconstantbufferview
描述符表:ID3D12GraphicsCommandList::SetComputeRootDescriptorTable
https://learn.microsoft.com/zh-cn/windows/win32/api/d3d12/nf-d3d12-id3d12graphicscommandlist-setcomputerootdescriptortable
其中根常量与根描述符都不需要涉及描述符堆

原文链接:https://www.cnblogs.com/dyccyber/p/18185325

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号