上一篇我们讲了如何建立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