上一篇我们讲了如何建立D3D12的设备,并在其之上建立出11on12的设备。接下去就要开始一步一步转移到纯D3D12下了。
第一个应该转的是相对独立的资源,包括buffer和texture。建立D3D12的资源,之后用前文说的CreateWrappedResource转成D3D11的资源,继续交给D3D11on12渲染就可以了。这样仍然可以往前走一小步,保证引擎还能工作。
Buffer
Buffer包括vertex buffer、index buffer和constant buffer。
D3D12_HEAP_PROPERTIES heap_prop; heap_prop.Type = D3D12_HEAP_TYPE_UPLOAD; heap_prop.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; heap_prop.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; heap_prop.CreationNodeMask = 1; heap_prop.VisibleNodeMask = 1; D3D12_RESOURCE_DESC res_desc; res_desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; res_desc.Alignment = 0; res_desc.Width = size_in_byte_; res_desc.Height = 1; res_desc.DepthOrArraySize = 1; res_desc.MipLevels = 1; res_desc.Format = DXGI_FORMAT_UNKNOWN; res_desc.SampleDesc.Count = 1; res_desc.SampleDesc.Quality = 0; res_desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; res_desc.Flags = D3D12_RESOURCE_FLAG_NONE; ID3D12Resource* buffer_12; TIF(d3d_12_device->CreateCommittedResource(&heap_prop, D3D12_HEAP_FLAG_NONE, &res_desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_ID3D12Resource, reinterpret_cast<void**>(&buffer_12)));
这样就把D3D12的buffer建立出来了。需要注意的是,和D3D11不一样的是,D3D12在建立资源的时候不能直接提供初始的数据,这里需要用map/unmap自己把数据拷进去。
void* p; buffer_12_->Map(0, nullptr, &p); memcpy(p, subres_init, size_in_byte_); buffer_12_->Unmap(0, nullptr);
之后,就能用CreateWrappedResource生成D3D11的buffer。另外,Map和Unmap也可以直接调用D3D12的,在12里不需要那些map的标志了。
Texture
Texture也可以用类似的方法,CreateCommittedResource建立12的texture,CreateWrappedResource转成11的texture,交给11on12渲染。甚至代码上都和前面的buffer几乎一样,所以我就不再贴一遍了。
有一个和以前不同的地方,12里texture不提供map/unmap。这就意味着你不能用以前的方式往里填数据,或者读数据出来。但12提供了CopyTextureRegion,可以把buffer或者texture的数据拷贝到texture里。有了这个,就能给每个texture建立一个对应的buffer,把线性的数据拷贝进去,在调用CopyTextureRegion把数据放入texture。
std::vector<D3D12_PLACED_SUBRESOURCE_FOOTPRINT> layouts(num_subres);
std::vector<uint64_t> row_sizes_in_bytes(num_subres);
std::vector<uint32_t> num_rows(num_subres);
uint64_t required_size = 0;
d3d_12_device->GetCopyableFootprints(&tex_desc, 0, num_subres, 0, &layouts[0], &num_rows[0], &row_sizes_in_bytes[0], &required_size);
uint8_t* p;
d3d_12_texture_upload_heaps_->Map(0, nullptr, reinterpret_cast<void**>(&p));
for (uint32_t i = 0; i < num_subres; ++ i)
{
D3D12_SUBRESOURCE_DATA src_data;
src_data.pData = subres_data[i].pSysMem;
src_data.RowPitch = subres_data[i].SysMemPitch;
src_data.SlicePitch = subres_data[i].SysMemSlicePitch;
D3D12_MEMCPY_DEST dest_data;
dest_data.pData = p + layouts[i].Offset;
dest_data.RowPitch = layouts[i].Footprint.RowPitch;
dest_data.SlicePitch = layouts[i].Footprint.RowPitch * num_rows[i];
for (UINT z = 0; z < layouts[i].Footprint.Depth; ++ z)
{
uint8_t const * src_slice
= reinterpret_cast(src_data.pData) + src_data.SlicePitch * z;
uint8_t* dest_slice = reinterpret_cast<uint8_t*>(dest_data.pData) + dest_data.SlicePitch * z;
for (UINT y = 0; y < num_rows[i]; ++ y)
{
memcpy(dest_slice + dest_data.RowPitch * y, src_slice + src_data.RowPitch * y,
row_sizes_in_bytes[i]);
}
}
}
d3d_12_texture_upload_heaps_->Unmap(0, nullptr);
for (uint32_t i = 0; i < num_subres; ++ i)
{
D3D12_TEXTURE_COPY_LOCATION src;
src.pResource = d3d_12_texture_upload_heaps_.get();
src.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
src.PlacedFootprint = layouts[i];
D3D12_TEXTURE_COPY_LOCATION dst;
dst.pResource = d3d_12_texture_.get();
dst.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
dst.SubresourceIndex = i;
cmd_list->CopyTextureRegion(&dst, 0, 0, 0, &src, nullptr);
}
就这样,buffer里的线性数据就能拷贝到texture。同理也可以把texture拷贝到buffer。Map/Unmap也得依赖这样的方法。实际上在D3D11里,这些事情也存在,只是由驱动代劳了。现在这些被暴露到应用层实现。
总结
buffer和texture这样的资源,经过包装,就能在接口不变的情况下换用D3D12的实现。我们朝纯D3D12更近了一步。



Comments