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

news/2024/9/29 21:25:36

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

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

帧资源

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

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

struct ObjectConstants
{DirectX::XMFLOAT4X4 World = MathHelper::Identity4x4();
};
针对每次渲染过程(rendering pass)所要用到的数据
比如 观察矩阵 投影矩阵 时间 等等
struct PassConstants
{DirectX::XMFLOAT4X4 View = MathHelper::Identity4x4();DirectX::XMFLOAT4X4 InvView = MathHelper::Identity4x4();DirectX::XMFLOAT4X4 Proj = MathHelper::Identity4x4();DirectX::XMFLOAT4X4 InvProj = MathHelper::Identity4x4();DirectX::XMFLOAT4X4 ViewProj = MathHelper::Identity4x4();DirectX::XMFLOAT4X4 InvViewProj = MathHelper::Identity4x4();DirectX::XMFLOAT3 EyePosW = { 0.0f, 0.0f, 0.0f };float cbPerObjectPad1 = 0.0f;DirectX::XMFLOAT2 RenderTargetSize = { 0.0f, 0.0f };DirectX::XMFLOAT2 InvRenderTargetSize = { 0.0f, 0.0f };float NearZ = 0.0f;float FarZ = 0.0f;float TotalTime = 0.0f;float DeltaTime = 0.0f;
};
顶点定义
struct Vertex
{DirectX::XMFLOAT3 Pos;DirectX::XMFLOAT4 Color;
};存储cpu为一帧构建命令列表所需资源
struct FrameResource
{
public:FrameResource(ID3D12Device* device, UINT passCount, UINT objectCount);FrameResource(const FrameResource& rhs) = delete;FrameResource& operator=(const FrameResource& rhs) = delete;~FrameResource();每一帧都要有自己的命令分配器因为当上一帧的gpu还在处理命令时 我们不能重置命令分配器Microsoft::WRL::ComPtr<ID3D12CommandAllocator> CmdListAlloc;同理 每个帧资源也要有自己的常量缓冲区std::unique_ptr<UploadBuffer<PassConstants>> PassCB = nullptr;std::unique_ptr<UploadBuffer<ObjectConstants>> ObjectCB = nullptr;围栏点可以帮助检测 gpu是否仍然使用着帧资源UINT64 Fence = 0;
};

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

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

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

cpu端更新第n帧:

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

绘制第n帧:

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

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

渲染项

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

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

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

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

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

cbuffer cbPerObject : register(b0)
{float4x4 gWorld; 
};cbuffer cbPass : register(b1)
{float4x4 gView;float4x4 gInvView;float4x4 gProj;float4x4 gInvProj;float4x4 gViewProj;float4x4 gInvViewProj;float3 gEyePosW;float cbPerObjectPad1;float2 gRenderTargetSize;float2 gInvRenderTargetSize;float gNearZ;float gFarZ;float gTotalTime;float gDeltaTime;
};

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

void ShapesApp::UpdateObjectCBs(const GameTimer& gt)
{auto currObjectCB = mCurrFrameResource->ObjectCB.get();for(auto& e : mAllRitems){每个帧资源都需要更新物体缓冲区if(e->NumFramesDirty > 0){XMMATRIX world = XMLoadFloat4x4(&e->World);ObjectConstants objConstants;XMStoreFloat4x4(&objConstants.World, XMMatrixTranspose(world));currObjectCB->CopyData(e->ObjCBIndex, objConstants);// Next FrameResource need to be updated too.e->NumFramesDirty--;}}
}void ShapesApp::UpdateMainPassCB(const GameTimer& gt)
{XMMATRIX view = XMLoadFloat4x4(&mView);XMMATRIX proj = XMLoadFloat4x4(&mProj);XMMATRIX viewProj = XMMatrixMultiply(view, proj);XMMATRIX invView = XMMatrixInverse(&XMMatrixDeterminant(view), view);XMMATRIX invProj = XMMatrixInverse(&XMMatrixDeterminant(proj), proj);XMMATRIX invViewProj = XMMatrixInverse(&XMMatrixDeterminant(viewProj), viewProj);XMStoreFloat4x4(&mMainPassCB.View, XMMatrixTranspose(view));XMStoreFloat4x4(&mMainPassCB.InvView, XMMatrixTranspose(invView));XMStoreFloat4x4(&mMainPassCB.Proj, XMMatrixTranspose(proj));XMStoreFloat4x4(&mMainPassCB.InvProj, XMMatrixTranspose(invProj));XMStoreFloat4x4(&mMainPassCB.ViewProj, XMMatrixTranspose(viewProj));XMStoreFloat4x4(&mMainPassCB.InvViewProj, XMMatrixTranspose(invViewProj));mMainPassCB.EyePosW = mEyePos;mMainPassCB.RenderTargetSize = XMFLOAT2((float)mClientWidth, (float)mClientHeight);mMainPassCB.InvRenderTargetSize = XMFLOAT2(1.0f / mClientWidth, 1.0f / mClientHeight);mMainPassCB.NearZ = 1.0f;mMainPassCB.FarZ = 1000.0f;mMainPassCB.TotalTime = gt.TotalTime();mMainPassCB.DeltaTime = gt.DeltaTime();auto currPassCB = mCurrFrameResource->PassCB.get();currPassCB->CopyData(0, mMainPassCB);
}

绘制多种几何体

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

创建顶点与索引缓冲区

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

void ShapesApp::BuildShapeGeometry()
{GeometryGenerator geoGen;GeometryGenerator::MeshData box = geoGen.CreateBox(1.5f, 0.5f, 1.5f, 3);GeometryGenerator::MeshData grid = geoGen.CreateGrid(20.0f, 30.0f, 60, 40);GeometryGenerator::MeshData sphere = geoGen.CreateSphere(0.5f, 20, 20);GeometryGenerator::MeshData cylinder = geoGen.CreateCylinder(0.5f, 0.3f, 3.0f, 20, 20);// 计算各几何体的起始顶点UINT boxVertexOffset = 0;UINT gridVertexOffset = (UINT)box.Vertices.size();UINT sphereVertexOffset = gridVertexOffset + (UINT)grid.Vertices.size();UINT cylinderVertexOffset = sphereVertexOffset + (UINT)sphere.Vertices.size();// 存储起始索引UINT boxIndexOffset = 0;UINT gridIndexOffset = (UINT)box.Indices32.size();UINT sphereIndexOffset = gridIndexOffset + (UINT)grid.Indices32.size();UINT cylinderIndexOffset = sphereIndexOffset + (UINT)sphere.Indices32.size();定义各子网格结构体SubmeshGeometry boxSubmesh;boxSubmesh.IndexCount = (UINT)box.Indices32.size();boxSubmesh.StartIndexLocation = boxIndexOffset;boxSubmesh.BaseVertexLocation = boxVertexOffset;SubmeshGeometry gridSubmesh;gridSubmesh.IndexCount = (UINT)grid.Indices32.size();gridSubmesh.StartIndexLocation = gridIndexOffset;gridSubmesh.BaseVertexLocation = gridVertexOffset;SubmeshGeometry sphereSubmesh;sphereSubmesh.IndexCount = (UINT)sphere.Indices32.size();sphereSubmesh.StartIndexLocation = sphereIndexOffset;sphereSubmesh.BaseVertexLocation = sphereVertexOffset;SubmeshGeometry cylinderSubmesh;cylinderSubmesh.IndexCount = (UINT)cylinder.Indices32.size();cylinderSubmesh.StartIndexLocation = cylinderIndexOffset;cylinderSubmesh.BaseVertexLocation = cylinderVertexOffset;将各顶点 各索引合并子网格合并为一个大的meshgeometryauto totalVertexCount =box.Vertices.size() +grid.Vertices.size() +sphere.Vertices.size() +cylinder.Vertices.size();std::vector<Vertex> vertices(totalVertexCount);UINT k = 0;for(size_t i = 0; i < box.Vertices.size(); ++i, ++k){vertices[k].Pos = box.Vertices[i].Position;vertices[k].Color = XMFLOAT4(DirectX::Colors::DarkGreen);}for(size_t i = 0; i < grid.Vertices.size(); ++i, ++k){vertices[k].Pos = grid.Vertices[i].Position;vertices[k].Color = XMFLOAT4(DirectX::Colors::ForestGreen);}for(size_t i = 0; i < sphere.Vertices.size(); ++i, ++k){vertices[k].Pos = sphere.Vertices[i].Position;vertices[k].Color = XMFLOAT4(DirectX::Colors::Crimson);}for(size_t i = 0; i < cylinder.Vertices.size(); ++i, ++k){vertices[k].Pos = cylinder.Vertices[i].Position;vertices[k].Color = XMFLOAT4(DirectX::Colors::SteelBlue);}std::vector<std::uint16_t> indices;indices.insert(indices.end(), std::begin(box.GetIndices16()), std::end(box.GetIndices16()));indices.insert(indices.end(), std::begin(grid.GetIndices16()), std::end(grid.GetIndices16()));indices.insert(indices.end(), std::begin(sphere.GetIndices16()), std::end(sphere.GetIndices16()));indices.insert(indices.end(), std::begin(cylinder.GetIndices16()), std::end(cylinder.GetIndices16()));const UINT vbByteSize = (UINT)vertices.size() * sizeof(Vertex);const UINT ibByteSize = (UINT)indices.size()  * sizeof(std::uint16_t);auto geo = std::make_unique<MeshGeometry>();geo->Name = "shapeGeo";ThrowIfFailed(D3DCreateBlob(vbByteSize, &geo->VertexBufferCPU));CopyMemory(geo->VertexBufferCPU->GetBufferPointer(), vertices.data(), vbByteSize);ThrowIfFailed(D3DCreateBlob(ibByteSize, &geo->IndexBufferCPU));CopyMemory(geo->IndexBufferCPU->GetBufferPointer(), indices.data(), ibByteSize);geo->VertexBufferGPU = d3dUtil::CreateDefaultBuffer(md3dDevice.Get(),mCommandList.Get(), vertices.data(), vbByteSize, geo->VertexBufferUploader);geo->IndexBufferGPU = d3dUtil::CreateDefaultBuffer(md3dDevice.Get(),mCommandList.Get(), indices.data(), ibByteSize, geo->IndexBufferUploader);geo->VertexByteStride = sizeof(Vertex);geo->VertexBufferByteSize = vbByteSize;geo->IndexFormat = DXGI_FORMAT_R16_UINT;geo->IndexBufferByteSize = ibByteSize;geo->DrawArgs["box"] = boxSubmesh;geo->DrawArgs["grid"] = gridSubmesh;geo->DrawArgs["sphere"] = sphereSubmesh;geo->DrawArgs["cylinder"] = cylinderSubmesh;mGeometries[geo->Name] = std::move(geo);
}

定义具体渲染项

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

void ShapesApp::BuildRenderItems()
{auto boxRitem = std::make_unique<RenderItem>();XMStoreFloat4x4(&boxRitem->World, XMMatrixScaling(2.0f, 2.0f, 2.0f)*XMMatrixTranslation(0.0f, 0.5f, 0.0f));boxRitem->ObjCBIndex = 0;boxRitem->Geo = mGeometries["shapeGeo"].get();boxRitem->PrimitiveType = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;boxRitem->IndexCount = boxRitem->Geo->DrawArgs["box"].IndexCount;boxRitem->StartIndexLocation = boxRitem->Geo->DrawArgs["box"].StartIndexLocation;boxRitem->BaseVertexLocation = boxRitem->Geo->DrawArgs["box"].BaseVertexLocation;mAllRitems.push_back(std::move(boxRitem));auto gridRitem = std::make_unique<RenderItem>();gridRitem->World = MathHelper::Identity4x4();gridRitem->ObjCBIndex = 1;gridRitem->Geo = mGeometries["shapeGeo"].get();gridRitem->PrimitiveType = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;gridRitem->IndexCount = gridRitem->Geo->DrawArgs["grid"].IndexCount;gridRitem->StartIndexLocation = gridRitem->Geo->DrawArgs["grid"].StartIndexLocation;gridRitem->BaseVertexLocation = gridRitem->Geo->DrawArgs["grid"].BaseVertexLocation;mAllRitems.push_back(std::move(gridRitem));UINT objCBIndex = 2;for(int i = 0; i < 5; ++i){auto leftCylRitem = std::make_unique<RenderItem>();auto rightCylRitem = std::make_unique<RenderItem>();auto leftSphereRitem = std::make_unique<RenderItem>();auto rightSphereRitem = std::make_unique<RenderItem>();XMMATRIX leftCylWorld = XMMatrixTranslation(-5.0f, 1.5f, -10.0f + i*5.0f);XMMATRIX rightCylWorld = XMMatrixTranslation(+5.0f, 1.5f, -10.0f + i*5.0f);XMMATRIX leftSphereWorld = XMMatrixTranslation(-5.0f, 3.5f, -10.0f + i*5.0f);XMMATRIX rightSphereWorld = XMMatrixTranslation(+5.0f, 3.5f, -10.0f + i*5.0f);XMStoreFloat4x4(&leftCylRitem->World, rightCylWorld);leftCylRitem->ObjCBIndex = objCBIndex++;leftCylRitem->Geo = mGeometries["shapeGeo"].get();leftCylRitem->PrimitiveType = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;leftCylRitem->IndexCount = leftCylRitem->Geo->DrawArgs["cylinder"].IndexCount;leftCylRitem->StartIndexLocation = leftCylRitem->Geo->DrawArgs["cylinder"].StartIndexLocation;leftCylRitem->BaseVertexLocation = leftCylRitem->Geo->DrawArgs["cylinder"].BaseVertexLocation;此处省略}for(auto& e : mAllRitems)mOpaqueRitems.push_back(e.get());
}

定义常量缓冲区视图

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

void ShapesApp::BuildDescriptorHeaps()
{UINT objCount = (UINT)mOpaqueRitems.size();UINT numDescriptors = (objCount+1) * gNumFrameResources;mPassCbvOffset = objCount * gNumFrameResources;D3D12_DESCRIPTOR_HEAP_DESC cbvHeapDesc;cbvHeapDesc.NumDescriptors = numDescriptors;cbvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;cbvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;cbvHeapDesc.NodeMask = 0;ThrowIfFailed(md3dDevice->CreateDescriptorHeap(&cbvHeapDesc,IID_PPV_ARGS(&mCbvHeap)));
}
void ShapesApp::BuildConstantBufferViews()
{UINT objCBByteSize = d3dUtil::CalcConstantBufferByteSize(sizeof(ObjectConstants));UINT objCount = (UINT)mOpaqueRitems.size();每个帧资源中的每个object都需要一个cbvfor(int frameIndex = 0; frameIndex < gNumFrameResources; ++frameIndex){auto objectCB = mFrameResources[frameIndex]->ObjectCB->Resource();for(UINT i = 0; i < objCount; ++i){D3D12_GPU_VIRTUAL_ADDRESS cbAddress = objectCB->GetGPUVirtualAddress();// 每个物体的偏移cbAddress += i*objCBByteSize;// 计算在描述符堆中的偏移int heapIndex = frameIndex*objCount + i;auto handle = CD3DX12_CPU_DESCRIPTOR_HANDLE(mCbvHeap->GetCPUDescriptorHandleForHeapStart());handle.Offset(heapIndex, mCbvSrvUavDescriptorSize);D3D12_CONSTANT_BUFFER_VIEW_DESC cbvDesc;cbvDesc.BufferLocation = cbAddress;cbvDesc.SizeInBytes = objCBByteSize;md3dDevice->CreateConstantBufferView(&cbvDesc, handle);}}UINT passCBByteSize = d3dUtil::CalcConstantBufferByteSize(sizeof(PassConstants));每个帧资源都要一个pass 描述符for(int frameIndex = 0; frameIndex < gNumFrameResources; ++frameIndex){auto passCB = mFrameResources[frameIndex]->PassCB->Resource();D3D12_GPU_VIRTUAL_ADDRESS cbAddress = passCB->GetGPUVirtualAddress();计算偏移int heapIndex = mPassCbvOffset + frameIndex;auto handle = CD3DX12_CPU_DESCRIPTOR_HANDLE(mCbvHeap->GetCPUDescriptorHandleForHeapStart());handle.Offset(heapIndex, mCbvSrvUavDescriptorSize);D3D12_CONSTANT_BUFFER_VIEW_DESC cbvDesc;cbvDesc.BufferLocation = cbAddress;cbvDesc.SizeInBytes = passCBByteSize;md3dDevice->CreateConstantBufferView(&cbvDesc, handle);}
}

绘制

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

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

细探根签名

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

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

根签名结构体定义:

typedef struct D3D12_ROOT_PARAMETER{D3D12_ROOT_PARAMETER_TYPE ParameterType;union {D3D12_ROOT_DESCRIPTOR_TABLE DescriptorTable;D3D12_ROOT_CONSTANTS Constants;D3D12_ROOT_DESCRIPTOR Descriptor;} 	;D3D12_SHADER_VISIBILITY ShaderVisibility;} 	D3D12_ROOT_PARAMETER;

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

创建 DescriptorTable Constants Descriptor

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

struct CD3DX12_DESCRIPTOR_RANGE : public D3D12_DESCRIPTOR_RANGE
{CD3DX12_DESCRIPTOR_RANGE() { }explicit CD3DX12_DESCRIPTOR_RANGE(const D3D12_DESCRIPTOR_RANGE &o) :D3D12_DESCRIPTOR_RANGE(o){}CD3DX12_DESCRIPTOR_RANGE(D3D12_DESCRIPTOR_RANGE_TYPE rangeType,UINT numDescriptors,UINT baseShaderRegister,UINT registerSpace = 0,UINT offsetInDescriptorsFromTableStart =D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND){Init(rangeType, numDescriptors, baseShaderRegister, registerSpace, offsetInDescriptorsFromTableStart);}inline void Init(D3D12_DESCRIPTOR_RANGE_TYPE rangeType,UINT numDescriptors,UINT baseShaderRegister,UINT registerSpace = 0,UINT offsetInDescriptorsFromTableStart =D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND){Init(*this, rangeType, numDescriptors, baseShaderRegister, registerSpace, offsetInDescriptorsFromTableStart);}
}

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

img
然后使用InitAsDescriptorTable创建 :

 CD3DX12_DESCRIPTOR_RANGE cbvTable0;cbvTable0.Init(D3D12_DESCRIPTOR_RANGE_TYPE_CBV, 1, 0);CD3DX12_DESCRIPTOR_RANGE cbvTable1;cbvTable1.Init(D3D12_DESCRIPTOR_RANGE_TYPE_CBV, 1, 1);CD3DX12_ROOT_PARAMETER slotRootParameter[2];slotRootParameter[0].InitAsDescriptorTable(1, &cbvTable0);slotRootParameter[1].InitAsDescriptorTable(1, &cbvTable1);

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

static inline void InitAsConstants(_Out_ D3D12_ROOT_PARAMETER &rootParam,UINT num32BitValues,UINT shaderRegister,UINT registerSpace = 0,D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL)
{rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS;rootParam.ShaderVisibility = visibility;CD3DX12_ROOT_CONSTANTS::Init(rootParam.Constants, num32BitValues, shaderRegister, registerSpace);
}static inline void InitAsConstantBufferView(_Out_ D3D12_ROOT_PARAMETER &rootParam,UINT shaderRegister,UINT registerSpace = 0,D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL)
{rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;rootParam.ShaderVisibility = visibility;CD3DX12_ROOT_DESCRIPTOR::Init(rootParam.Descriptor, shaderRegister, registerSpace);
}

例子:
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
其中根常量与根描述符都不需要涉及描述符堆

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

如何使用postman测试带Token的登录以及其他的测试接口

参考文章:https://blog.csdn.net/xzytl60937234/article/details/845009371.Postman设置变量并访问 点击右上角眼睛,在Globals选项中,选择edit 然后add 在弹出的页面中:,填写token 填写成功之后,右下角,点击save 在登录接口中,Test 选项中加入以下代码 var data = JSO…

PTA-1002

原先主要错误: 没有考虑到有关0的相关情况观看的大佬代码整理思路无非就是在相同的指数的情况下,系数相加 因为最后是要从大到小输出来。注意要对最后的结果进行四舍五入; PTA的英语题对英语不好的我真心不友好。#include<map> #include<cmath> #include<iost…

【笔记】通过目标编码和分类损失的神经场分类器【ICLR2024】

这是一篇发表在 ICLR 2024 会议上的学术论文的阅读笔记,题为“通过目标编码和分类损失的神经场分类器”,作者来自北京交通大学和百度研究院。 为什么会找到这篇文章,因为文章的一个作者是HKUST一个基础实验室的,搞的是偏底层的算法理论,所以自然就顺下来了。但是看完之后发…

winform开发杂碎知识点

1、Form属性 起始位置:StartPosition 默认最大化:WindowState 最小尺寸:MinimumSize 是否父窗体:IsMdiContainer ImeMode 图标:icon2、Form事件3、Location 元素相对父控件或父元素的位置1 this.panelLogin.BackColor = System.Drawing.Color.White;2 this.p…

【软件构造课程相关】幻方及其构造(下)

前言 ​ 在上一篇博客(click here)中,我们完成了实验要求的部分,即实现奇数阶幻方的构造。接下来我们将要着手实现剩下的4M阶幻方和4M+2阶幻方的构造。 构造方法 4M阶幻方 ​ 4M阶幻方的构造方法较奇数阶幻方的构造更为复杂,在此我们采用对角线法,其策略如下:…

vision mamba的跑通(wsl2, 单卡)

动机 随着mamba模型的出现,出现了mamba模型可以超越transformer的风头,视觉领域采用了vision mamba encoder(虽然说是mamba encoder, 更像是用了mamba的思想做的一个双向ssm的特征提取器), 个人认为这种新的架构应该可以用在自己的任务上, 因此进行了代码跑通尝试。 本文阐…

springboot支持国际化

Spring Boot支持本地化的消息,这样你的应用程序就可以满足不同语言偏好的用户。 默认情况下,Spring Boot会在classpath的根部寻找 messages 资源包(resource bundle)的存在。 自动配置只有在资源包中存在默认的properties文件(默认为 messages.properties )时才生效。如果…

【Azure App Service】列举为App Service集成虚拟网络(VNET)操作时所需要的最小权限

问题描述 作为Azure资源管理人员,对每一种资源操作时,都需要考虑权限设置。否则,会遇见类似如下错误:The client *************** with object id ********-****-****-****-************ does not have authorization to perform action Microsoft.Network/virtualNetworks…