每天学习一点点9.12: d3d12中的 drawing Vertex, texture, frame buffering, const buffers, bundles, 一次性全部源码解释
这一期我们来梳理初始化之后的一些基本入门操作
此篇blog都是和第一篇D3d的初始化文章比对进行讲解的.
1.D3D12HelloTriangle
1.Viewport 和scissorRect的区别就是如图所示
2.代码新增了啥?
新增了Vertex格式声明:
这个就是老生常谈了,d3d9里面也有, 就是顶点格式的描述,后面计算的时候能知道input和output
ComPtr<ID3D12RootSignature> m_rootSignature:
首先signature确定哪些资源会被绑定到渲染管线, 以及这些资源将如何映射到shader的输入寄存器. signature一定要和shader兼容,不然你的映射方法得到的输入不是shader想要的那就搞笑了. 并且signature还要提供所有shader需要的资源, 这个检查会在渲染管线被创建时进行.
而且不同的draw call 可能还会需要不同的shader 程序,这个时候就又需要不同的signature.
它需要填写一系列的参数包括 root 常量, root 描述符, 描述 table(有点像d3dshader的mapping table). 描述表就是一系列连续的描述堆里的描述符
大概可以理解成root signature是资源和shader的桥梁, 以及哪些shader用哪些资源,
m_scissorRect, m_viewport.:
这个上面已经谈了
ComPtr<ID3D12Resource> m_vertexBuffer:
这个其实和
d3d9的vertex buffer差不多没啥好说的
D3D12_VERTEX_BUFFER_VIEW m_vertexBufferView
这个看都不用看都知道是有关drawing index和drawing stride, primitive type这些内容的.
3. 下面看代码里的一些新增玩意:
主要都是在D3D12HelloTriangle.cpp里面
-
新增对viewportRect和scissorRect的填充
-
LoadAsset这里增加了非常多的内容
老办法源码逐行分析:只看新增部分哈, 之前的一些部分基本没变的就不说了
void D3D12HelloTriangle::LoadAssets()
{
// 创建一个空的rootsignature
{
// 注意这个Init函数
// 参数1: 参数个数
//参数2: 参数的数组
// 参数3: 静态的采样的个数,打个比方ue4里经常一个matl里n个texture这个就是
//参数4 :静态采样的样本数组
// 参数5: 告诉我们这个signature的类型://D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT:
//The app is opting in to using the Input Assembler (requiring an input layout that defines a set of //vertex buffer bindings)
CD3DX12_ROOT_SIGNATURE_DESC rootSignatureDesc;
rootSignatureDesc.Init(0, nullptr, 0, nullptr, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
//Blob其实是d3d12里的一团数据的意思用于序列化,这样省空间
ComPtr<ID3DBlob> signature;
ComPtr<ID3DBlob> error;
//先创建序列化的signature然后创建解压版的
ThrowIfFailed(D3D12SerializeRootSignature(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error));
//从序列化的signature里面摆好放回m_rootSignature;
ThrowIfFailed(m_device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&m_rootSignature)));
}
//创建一个包括pixelshader和vertex shader的pipeline object
上一节的pipeline啥也没有
{
ComPtr<ID3DBlob> vertexShader;
ComPtr<ID3DBlob> pixelShader;
//debug 的分支编译选项
#if defined(_DEBUG)
//debug 的话就没有优化了
// Enable better shader debugging with the graphics debugging tools.
UINT compileFlags = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
#else
UINT compileFlags = 0;
#endif
//从文件编译shader文件
// parameter
1:文件名
2:shader用到的宏
3:shader用到的include文件
4:入口函数
5:编译目标平台
6: flag1
7. flag2
8. 填入的blob code的shader 文件地址
9.Error messages存的地方
ThrowIfFailed(D3DCompileFromFile(GetAssetFullPath(L"shaders.hlsl").c_str(), nullptr, nullptr, "VSMain", "vs_5_0", compileFlags, 0, &vertexShader, nullptr));
ThrowIfFailed(D3DCompileFromFile(GetAssetFullPath(L"shaders.hlsl").c_str(), nullptr, nullptr, "PSMain", "ps_5_0", compileFlags, 0, &pixelShader, nullptr));
// 定义顶点输入映射
// 输入一个position一个color
//结构体
D3D12_INPUT_ELEMENT_DESC
{
LPCSTR SemanticName; // 在shader里的名字
UINT SemanticIndex; // 语义索引
DXGI_FORMAT Format; //格式
UINT InputSlot; //输入槽
UINT AlignedByteOffset; //对齐offset
D3D12_INPUT_CLASSIFICATION InputSlotClass; // 输入槽的类型
UINT InstanceDataStepRate; // 不清楚
}
D3D12_INPUT_ELEMENT_DESC inputElementDescs[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
{ "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }
};
// Describe and create the graphics pipeline state object (PSO).
//创建pipeline 管线
D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {}; // 渲染管线状态描述符
psoDesc.InputLayout = { inputElementDescs, _countof(inputElementDescs) }; //输入elment 的描述
psoDesc.pRootSignature = m_rootSignature.Get(); //指定root signature一个signature就指定了资源和layout
psoDesc.VS = CD3DX12_SHADER_BYTECODE(vertexShader.Get()); // 设定vertex shader
psoDesc.PS = CD3DX12_SHADER_BYTECODE(pixelShader.Get()); // 设定pixel shader
psoDesc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT); //光栅化状态
psoDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT); // blend 状态主要是α混合时候的计算公式在这里指定
psoDesc.DepthStencilState.DepthEnable = FALSE; //直接取消depth
psoDesc.DepthStencilState.StencilEnable = FALSE; // 直接取消stencil
psoDesc.SampleMask = UINT_MAX; // 采样mask
psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; // primitive 类型
psoDesc.NumRenderTargets = 1; //渲染喂过去的target有几个
psoDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM; //render target view 格式
psoDesc.SampleDesc.Count = 1; //采样DESC的个数
ThrowIfFailed(m_device->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&m_pipelineState)));
}
// Create the vertex buffer.
{
// Define the geometry for a triangle.
Vertex triangleVertices[] =
{
{ { 0.0f, 0.25f * m_aspectRatio, 0.0f }, { 1.0f, 0.0f, 0.0f, 1.0f } },
{ { 0.25f, -0.25f * m_aspectRatio, 0.0f }, { 0.0f, 1.0f, 0.0f, 1.0f } },
{ { -0.25f, -0.25f * m_aspectRatio, 0.0f }, { 0.0f, 0.0f, 1.0f, 1.0f } }
};
const UINT vertexBufferSize = sizeof(triangleVertices);
//这里的创建committed resource就是在创建顶点缓存, D3D12_HEAP_TYPE_UPLOAD其实不太适合这种静态数据, 这里只是为了代码简洁
ThrowIfFailed(m_device->CreateCommittedResource(
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD),
D3D12_HEAP_FLAG_NONE,
&CD3DX12_RESOURCE_DESC::Buffer(vertexBufferSize),
D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr,
IID_PPV_ARGS(&m_vertexBuffer)));
// 把geometry的数据复制到vertex buffer,跟d3d9差不多
UINT8* pVertexDataBegin;
CD3DX12_RANGE readRange(0, 0); // We do not intend to read from this resource on the CPU.
//其实就是d3d9里面的锁定
ThrowIfFailed(m_vertexBuffer->Map(0, &readRange, reinterpret_cast<void**>(&pVertexDataBegin)));
//其实就是把pVertexDataBegin的地址换成了vertexbuffer里面的raw指针的内存首地址.
//然后直接用pVertexDataBegin的地址来进行拷贝
memcpy(pVertexDataBegin, triangleVertices, sizeof(triangleVertices));
//搞完之后unlock
m_vertexBuffer->Unmap(0, nullptr);
// Initialize the vertex buffer view.
//果然view就是关于vertex的地址,stride等的信息
m_vertexBufferView.BufferLocation = m_vertexBuffer->GetGPUVirtualAddress();
m_vertexBufferView.StrideInBytes = sizeof(Vertex);
m_vertexBufferView.SizeInBytes = vertexBufferSize;
}
}
-
PopulateCommandList()
我们这里删除一些之前都有的代码只看新增的
void D3D12HelloTriangle::PopulateCommandList()
{
//设置signature其实就是设置shader
m_commandList->SetGraphicsRootSignature(m_rootSignature.Get());
//这里如果在geometry shader里面不指定SV_ViewportArrayIndex的话就会使用setviewports里面的viewport数组的第一个
m_commandList->RSSetViewports(1, &m_viewport);
m_commandList->RSSetScissorRects(1, &m_scissorRect);
CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(m_rtvHeap->GetCPUDescriptorHandleForHeapStart(), m_frameIndex, m_rtvDescriptorSize);
m_commandList->OMSetRenderTargets(1, &rtvHandle, FALSE, nullptr);
m_commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
m_commandList->IASetVertexBuffers(0, 1, &m_vertexBufferView);
m_commandList->DrawInstanced(3, 1, 0, 0);
}
2.D3D12HelloTexture
1.新增了啥?
LoadPipeline():
新增了另外一种resource view的heap创建
// Describe and create a shader resource view (SRV) heap for the texture.
D3D12_DESCRIPTOR_HEAP_DESC srvHeapDesc = {};
srvHeapDesc.NumDescriptors = 1;
srvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
srvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
ThrowIfFailed(m_device->CreateDescriptorHeap(&srvHeapDesc, IID_PPV_ARGS(&m_srvHeap)));
LoadAssets():
// 创建rootsignature有很大变化
{
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];
ranges[0].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0, 0, D3D12_DESCRIPTOR_RANGE_FLAG_DATA_STATIC);
CD3DX12_ROOT_PARAMETER1 rootParameters[1];
rootParameters[0].InitAsDescriptorTable(1, &ranges[0], D3D12_SHADER_VISIBILITY_PIXEL);
//创建采样器sampler,
它定义了采样的时候的一些标准,比如超过寻址范围之后如何寻址
D3D12_STATIC_SAMPLER_DESC sampler = {};
sampler.Filter = D3D12_FILTER_MIN_MAG_MIP_POINT;
sampler.AddressU = D3D12_TEXTURE_ADDRESS_MODE_BORDER;
sampler.AddressV = D3D12_TEXTURE_ADDRESS_MODE_BORDER;
sampler.AddressW = D3D12_TEXTURE_ADDRESS_MODE_BORDER;
sampler.MipLODBias = 0;
sampler.MaxAnisotropy = 0;
sampler.ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER;
sampler.BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK;
sampler.MinLOD = 0.0f;
sampler.MaxLOD = D3D12_FLOAT32_MAX;
sampler.ShaderRegister = 0;
sampler.RegisterSpace = 0;
sampler.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
// 新版本的root signature desc的创建
//这里在初始化signature的时候就要传入sampler说明,signature其实和sampler绑定了渲染的一些行为.
CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC rootSignatureDesc;
rootSignatureDesc.Init_1_1(_countof(rootParameters), rootParameters, 1, &sampler, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
ComPtr<ID3DBlob> signature;
ComPtr<ID3DBlob> error;
ThrowIfFailed(D3DX12SerializeVersionedRootSignature(&rootSignatureDesc, featureData.HighestVersion, &signature, &error));
ThrowIfFailed(m_device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&m_rootSignature)));
}
// 创建pipeline state中也就input element声明那有点变化
// Create the pipeline state, which includes compiling and loading shaders.
{
// Define the vertex input layout.
D3D12_INPUT_ELEMENT_DESC inputElementDescs[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }
};
}
//创建texture
ComPtr<ID3D12Resource> textureUploadHeap
// Create the texture.
{
// 一样描述你这个texture是怎样的
D3D12_RESOURCE_DESC textureDesc = {};
textureDesc.MipLevels = 1;
textureDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
textureDesc.Width = TextureWidth;
textureDesc.Height = TextureHeight;
textureDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
textureDesc.DepthOrArraySize = 1;
textureDesc.SampleDesc.Count = 1;
textureDesc.SampleDesc.Quality = 0;
textureDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
//然后直接创建resource的内存空间
ThrowIfFailed(m_device->CreateCommittedResource(
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
D3D12_HEAP_FLAG_NONE,
&textureDesc,
D3D12_RESOURCE_STATE_COPY_DEST,
nullptr,
IID_PPV_ARGS(&m_texture)));
//获得upload的buffer size
const UINT64 uploadBufferSize = GetRequiredIntermediateSize(m_texture.Get(), 0, 1);
// 创建GPU upload的buffer
//D3D12_HEAP_TYPE_UPLOAD这个意思其实就是堆里面一块是专门针对CPU 读一次,GPU 读一次优化的,即专门用来上传内存到显存的快速通道
ThrowIfFailed(m_device->CreateCommittedResource(
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD),
D3D12_HEAP_FLAG_NONE,
&CD3DX12_RESOURCE_DESC::Buffer(uploadBufferSize),
D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr,
IID_PPV_ARGS(&textureUploadHeap)));
// Copy data to the intermediate upload heap and then schedule a copy
// from the upload heap to the Texture2D.
//把texture的数据从textureUploadHeap和texture移到m_texture
std::vector<UINT8> texture = GenerateTextureData();
//获取texture的data的数据头指针,之后就不用texture了
D3D12_SUBRESOURCE_DATA textureData = {};
textureData.pData = &texture[0];
textureData.RowPitch = TextureWidth * TexturePixelSize;
textureData.SlicePitch = textureData.RowPitch * TextureHeight;
UpdateSubresources(m_commandList.Get(), m_texture.Get(), textureUploadHeap.Get(), 0, 0, 1, &textureData);
//把m_texture从copy_dest状态调整到pixel_shader_resource供着色器使用
m_commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_texture.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE));
// Describe and create a SRV for the texture.
D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
srvDesc.Format = textureDesc.Format;
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
srvDesc.Texture2D.MipLevels = 1;
//实际就是veiw和resource联系到一起才能起作用
m_device->CreateShaderResourceView(m_texture.Get(), &srvDesc, m_srvHeap->GetCPUDescriptorHandleForHeapStart());
}
GenerateTextureData():
下面看一看textureData怎么generate的
std::vector<UINT8> D3D12HelloTexture::GenerateTextureData()
{
const UINT rowPitch = TextureWidth * TexturePixelSize;//1024 bytes
const UINT cellPitch = rowPitch >> 3; //128 bytes //32 pixel // The width of a cell in the checkboard texture.
const UINT cellHeight = TextureWidth >> 3; //32 byte //8 pixel The height of a cell in the checkerboard texture.
const UINT textureSize = rowPitch * TextureHeight;
std::vector<UINT8> data(textureSize);
UINT8* pData = &data[0];
for (UINT n = 0; n < textureSize; n += TexturePixelSize)
{
UINT x = n % rowPitch; //字节 第几列
UINT y = n / rowPitch; //字节第几行
UINT i = x / cellPitch; //第几列的格子
UINT j = y / cellHeight; //第几行的
if (i % 2 == j % 2) // 格子横纵坐标奇偶一致的都是白色
{
pData[n] = 0x00; // R
pData[n + 1] = 0x00; // G
pData[n + 2] = 0x00; // B
pData[n + 3] = 0xff; // A
}
else
{
pData[n] = 0xff; // R
pData[n + 1] = 0xff; // G
pData[n + 2] = 0xff; // B
pData[n + 3] = 0xff; // A
}
}
return data;
}
PopulateCommandList():我们只写新增的, 其余部分跟上一期的一样
void D3D12HelloTexture::PopulateCommandList()
{
//设置了一个Texture资源heap
//之前的都没有,因为不存在资源啊
ID3D12DescriptorHeap* ppHeaps[] = { m_srvHeap.Get() };
m_commandList->SetDescriptorHeaps(_countof(ppHeaps), ppHeaps);
// 还要设置Root 描述符的表其实就是设置要用的资源
m_commandList->SetGraphicsRootDescriptorTable(0, m_srvHeap->GetGPUDescriptorHandleForHeapStart());
//这里其实就是先拿到handle的start位置然后offset m_frameIndex*m_rtvDescriptorSize 就到了我们当前的backbuffer的handle
CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(m_rtvHeap->GetCPUDescriptorHandleForHeapStart(), m_frameIndex, m_rtvDescriptorSize);
m_commandList->OMSetRenderTargets(1, &rtvHandle, FALSE, nullptr);
}
3.D3D12HelloFrameBuffering
1.主要变化
成员数据
//每个frame都有一个allocator了
ComPtr<ID3D12CommandAllocator> m_commandAllocators[FrameCount];
//每个frame都有一个m_fenceValue了
UINT64 m_fenceValues[FrameCount];
函数
LoadPipeline()
for (UINT n = 0; n < FrameCount; n++)
{
ThrowIfFailed(m_swapChain->GetBuffer(n, IID_PPV_ARGS(&m_renderTargets[n])));
m_device->CreateRenderTargetView(m_renderTargets[n].Get(), nullptr, rtvHandle);
rtvHandle.Offset(1, m_rtvDescriptorSize);
//其实就是新增了一个allocator而已
ThrowIfFailed(m_device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&m_commandAllocators[n])));
}
LoadAssets()
//创建同步objects的并等待所有asset已经被加载至GPU
{
ThrowIfFailed(m_device->CreateFence(m_fenceValues[m_frameIndex], D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&m_fence)));
m_fenceValues[m_frameIndex]++;
// Create an event handle to use for frame synchronization.
m_fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
if (m_fenceEvent == nullptr)
{
ThrowIfFailed(HRESULT_FROM_WIN32(GetLastError()));
}
//等GPU加载完成
WaitForGpu();
}
MoveToNextFrame()
{
// Schedule a Signal command in the queue.
const UINT64 currentFenceValue = m_fenceValues[m_frameIndex];
ThrowIfFailed(m_commandQueue->Signal(m_fence.Get(), currentFenceValue));
// Update the frame index.
m_frameIndex = m_swapChain->GetCurrentBackBufferIndex();
// If the next frame is not ready to be rendered yet, wait until it is ready.
if (m_fence->GetCompletedValue() < m_fenceValues[m_frameIndex])
{
ThrowIfFailed(m_fence->SetEventOnCompletion(m_fenceValues[m_frameIndex], m_fenceEvent));
WaitForSingleObjectEx(m_fenceEvent, INFINITE, FALSE);
}
// Set the fence value for the next frame.
m_fenceValues[m_frameIndex] = currentFenceValue + 1;
}
我们可以看到这一次除了第一次等待GPU和第一的MoveToNextFrame需要等待,其余时间就没有WatiforSingleObject了,就非常完美,减少了堵塞的可能性. 之前的版本就很离谱需要你每次渲染的时候都要堵塞一下.
WaitForGpu();
倒也没啥太大变化,等GPU上面的command 全部处理完
void D3D12HelloFrameBuffering::WaitForGpu()
{
// Schedule a Signal command in the queue.
ThrowIfFailed(m_commandQueue->Signal(m_fence.Get(), m_fenceValues[m_frameIndex]));
// Wait until the fence has been processed.
ThrowIfFailed(m_fence->SetEventOnCompletion(m_fenceValues[m_frameIndex], m_fenceEvent));
WaitForSingleObjectEx(m_fenceEvent, INFINITE, FALSE);
// Increment the fence value for the current frame.
m_fenceValues[m_frameIndex]++;
}
4.D3D12HelloConstBuffers
1.新增了啥
ComPtr<ID3D12Resource> m_constantBuffer:
const buffer的资源类接口
SceneConstantBuffer m_constantBufferData
其实就是类似于ue4材质节点的world position offset一样的东西
struct SceneConstantBuffer
{
XMFLOAT4 offset;
float padding[60]; // 256字节对齐
};
constant_buffer的实际数据位置
UINT8* m_pCbvDataBegin:
const buffer数据的开始指针
ComPtr<ID3D12DescriptorHeap> m_cbvHeap:
新增了存储const buffer view的heap
LoadPipeLine():
D3D12_DESCRIPTOR_HEAP_DESC cbvHeapDesc = {};
cbvHeapDesc.NumDescriptors = 1;
cbvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
cbvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
ThrowIfFailed(m_device->CreateDescriptorHeap(&cbvHeapDesc, IID_PPV_ARGS(&m_cbvHeap)));
其实就是在这里新增了存放Constant Buffer的位置资源的位置
Load Assets():
创建一个带有cbv的descriptor table
{
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);
// Allow input layout and deny uneccessary access to certain pipeline stages.
D3D12_ROOT_SIGNATURE_FLAGS rootSignatureFlags =
D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |
D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS |
D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS |
D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS |
D3D12_ROOT_SIGNATURE_FLAG_DENY_PIXEL_SHADER_ROOT_ACCESS;
CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC rootSignatureDesc;
rootSignatureDesc.Init_1_1(_countof(rootParameters), rootParameters, 0, nullptr, rootSignatureFlags);
ComPtr<ID3DBlob> signature;
ComPtr<ID3DBlob> error;
ThrowIfFailed(D3DX12SerializeVersionedRootSignature(&rootSignatureDesc, featureData.HighestVersion, &signature, &error));
ThrowIfFailed(m_device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&m_rootSignature)));
}
创建Constant Buffer
// Create the constant buffer.
{
const UINT constantBufferSize = sizeof(SceneConstantBuffer); // CB size is required to be 256-byte aligned.
ThrowIfFailed(m_device->CreateCommittedResource(
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD),
D3D12_HEAP_FLAG_NONE,
&CD3DX12_RESOURCE_DESC::Buffer(constantBufferSize),
D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr,
IID_PPV_ARGS(&m_constantBuffer)));
// Describe and create a constant buffer view.
D3D12_CONSTANT_BUFFER_VIEW_DESC cbvDesc = {};
cbvDesc.BufferLocation = m_constantBuffer->GetGPUVirtualAddress();
cbvDesc.SizeInBytes = constantBufferSize;
m_device->CreateConstantBufferView(&cbvDesc, m_cbvHeap->GetCPUDescriptorHandleForHeapStart());
// Map and initialize the constant buffer. We don't unmap this until the
// app closes. Keeping things mapped for the lifetime of the resource is okay.
CD3DX12_RANGE readRange(0, 0); // We do not intend to read from this resource on the CPU.
//把resource的buffer的指针给m_pCbvDataBegin赋值,然后把m_constantBufferData的值赋给资源
ThrowIfFailed(m_constantBuffer->Map(0, &readRange, reinterpret_cast<void**>(&m_pCbvDataBegin)));
memcpy(m_pCbvDataBegin, &m_constantBufferData, sizeof(m_constantBufferData));
}
OnUpdate():
其实就是在不断改变这个offset的值并拷贝给资源所在的内存
const float translationSpeed = 0.005f;
const float offsetBounds = 1.25f;
m_constantBufferData.offset.x += translationSpeed;
if (m_constantBufferData.offset.x > offsetBounds)
{
m_constantBufferData.offset.x = -offsetBounds;
}
memcpy(m_pCbvDataBegin, &m_constantBufferData, sizeof(m_constantBufferData));
PopulateCommandList():
{
//把cbv_heap设置为当前使用的descriptor heap
ID3D12DescriptorHeap* ppHeaps[] = { m_cbvHeap.Get() };
m_commandList->SetDescriptorHeaps(_countof(ppHeaps), ppHeaps);
m_commandList->SetGraphicsRootDescriptorTable(0, m_cbvHeap->GetGPUDescriptorHandleForHeapStart());
}
5.D3D12HelloBundles
1.新增了啥
成员数据
m_bundleAllocator:
ComPtr<ID3D12CommandAllocator>类型的allocator专门个bundle分配内存.
m_bundle:
ComPtr<ID3D12GraphicsCommandList>类型的command list用来存command
函数变化:
LoadPipeline()
就创建了一个bundle_allocator
ThrowIfFailed(m_device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_BUNDLE, IID_PPV_ARGS(&m_bundleAllocator)));
LoadAssets():
我们可以把bundle理解为一个包就行了
其实这个很适合像UE4里面每一个物体都有很复杂的shader的时候, 每个object的command 就做成一个bundle就挺好. 保证了更好的封装性,性能也更好.
其实就是创建了一个command list bundle
// Create and record the bundle.
{
ThrowIfFailed(m_device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_BUNDLE, m_bundleAllocator.Get(), m_pipelineState.Get(), IID_PPV_ARGS(&m_bundle)));
m_bundle->SetGraphicsRootSignature(m_rootSignature.Get());
m_bundle->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
m_bundle->IASetVertexBuffers(0, 1, &m_vertexBufferView);
m_bundle->DrawInstanced(3, 1, 0, 0);
ThrowIfFailed(m_bundle->Close());
}
PopulateCommandList()
// 其实这里应该不是真的执行而是把bundle里面的命令添加到commandlist里面
m_commandList->ExecuteBundle(m_bundle.Get());