in TechniqueDemos/D3D12MemoryManagement/src/Framework.cpp [1561:1940]
HRESULT DX12Framework::LoadMip(Resource* pResource, UINT32 Mip)
{
HRESULT hr;
LOG_MESSAGE("Loading mip %d", Mip);
UINT32 MipHeap = Mip;
UINT NumMips = GetResourceMipCount(pResource);
if (Mip >= NumMips)
{
LOG_WARNING("Trying to load mip %d for pResource 0x%p, but the image file only contains %d mip levels", Mip, pResource, NumMips);
return S_FALSE;
}
BitmapFrameInfo MipFrameInfo = {};
ComPtr<IWICDdsDecoder> pDdsDecoder;
ComPtr<IWICBitmapFrameDecode> pBitmapFrame;
ComPtr<IWICDdsFrameDecode> pDdsFrame;
ComPtr<IWICBitmapSource> pSourceBitmap;
//
// Different behavior is performed depending on whether this is a generated image,
// DDS file, or other image format, since it may contain block compressed pixel data.
//
if (pResource->pDecoder)
{
hr = pResource->pDecoder->QueryInterface(IID_PPV_ARGS(&pDdsDecoder));
if (SUCCEEDED(hr))
{
hr = pDdsDecoder->GetFrame(0, Mip, 0, &pBitmapFrame);
if (FAILED(hr))
{
LOG_ERROR("Failed to load decode frame for Resource 0x%p, mip %d, hr=0x%.8x", pResource, Mip, hr);
return hr;
}
hr = pBitmapFrame.As(&pDdsFrame);
if (FAILED(hr))
{
LOG_ERROR("Failed to query DDS frame for mip %d, hr=0x%.8x", Mip, hr);
return hr;
}
hr = GetDdsFrameInfo(pDdsFrame.Get(), &MipFrameInfo);
if (FAILED(hr))
{
LOG_ERROR("Failed to load frame information for mip %d, hr=0x%.8x", Mip, hr);
return hr;
}
}
else
{
hr = pResource->pDecoder->GetFrame(0, &pBitmapFrame);
if (FAILED(hr))
{
LOG_ERROR("Failed to decode bitmap for Resource 0x%p, mip %d, hr=0x%.8x", pResource, Mip, hr);
return hr;
}
hr = GetBitmapFrameInfo(pBitmapFrame.Get(), &MipFrameInfo);
if (FAILED(hr))
{
LOG_ERROR("Failed to load bitmap information for mip %d, hr=0x%.8x", Mip, hr);
return hr;
}
//
// Non-DDS images may need a pixel converter to convert between the source and
// target pixel formats.
//
ComPtr<IWICFormatConverter> pConverter;
hr = m_pWICFactory->CreateFormatConverter(&pConverter);
if (FAILED(hr))
{
LOG_ERROR("Failed to create pixel format converter, hr=0x%.8x", hr);
return hr;
}
hr = pConverter->Initialize(
pBitmapFrame.Get(),
MipFrameInfo.TargetPixelFormat,
WICBitmapDitherTypeNone,
nullptr,
0.0f,
WICBitmapPaletteTypeCustom);
if (FAILED(hr))
{
LOG_ERROR("Failed to initialize pixel format converter, hr=0x%.8x", hr);
return hr;
}
pSourceBitmap = pConverter;
}
}
else
{
D3D12_RESOURCE_DESC resourceDesc = pResource->pDeviceState->pD3DResource->GetDesc();
MipFrameInfo.BlockWidth = 1;
MipFrameInfo.BlockHeight = 1;
MipFrameInfo.DxgiFormat = resourceDesc.Format;
MipFrameInfo.WidthInBlocks = (UINT)(resourceDesc.Width) >> Mip;
MipFrameInfo.HeightInBlocks = resourceDesc.Height >> Mip;
}
UINT NumTiles;
UINT WidthInTiles;
//
// Mip maps beyond NumStandardMips are packed, and so the entire set of mipmaps
// under it is packed into a single heap.
//
if (MipHeap >= pResource->PackedMipHeapIndex)
{
MipHeap = pResource->PackedMipHeapIndex;
WidthInTiles = pResource->PackedMipTileCount;
NumTiles = pResource->PackedMipTileCount;
}
else
{
WidthInTiles = pResource->pDeviceState->Mips[MipHeap].Desc.WidthInTiles;
NumTiles = WidthInTiles * pResource->pDeviceState->Mips[MipHeap].Desc.HeightInTiles;
}
//
// Round up the number of heaps needed for this mip.
//
UINT NumHeapsInMip = ((NumTiles * TILE_SIZE) + MAX_HEAP_SIZE - 1) / MAX_HEAP_SIZE;
//
// Although we have a fixed heap count that is already calculated, we still calculate
// the required size so we don't allocate more space than necessary. We do not want
// to create 16MB heaps for mipmaps that are much smaller, or mipmaps which only
// excced multiples of 16MB by a small amount (e.g. 33MB == 3 heaps == 16+16+1)
//
UINT64 RemainingSize = NumTiles * TILE_SIZE;
ID3D12Heap** ppHeaps = pResource->pDeviceState->Mips[MipHeap].ppHeaps;
for (UINT i = 0; i < NumHeapsInMip; ++i)
{
ID3D12Heap* pHeap = ppHeaps[i];
if (pHeap == nullptr)
{
D3D12_HEAP_DESC heapDesc = {};
heapDesc.SizeInBytes = min(RemainingSize, MAX_HEAP_SIZE);
heapDesc.Alignment = 0;
heapDesc.Properties.Type = D3D12_HEAP_TYPE_DEFAULT;
heapDesc.Properties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
heapDesc.Properties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
//
// Tier 1 heaps have restrictions on the type of information that can be stored in
// a heap. To accommodate this, we will retsrict the content to only shader resources.
// The heap cannot store textures that are used as render targets, depth-stencil
// output, or buffers. But this is okay, since we do not use these heaps for those
// purposes.
//
heapDesc.Flags = D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES | D3D12_HEAP_FLAG_DENY_BUFFERS;
hr = m_pDevice->CreateHeap(&heapDesc, IID_PPV_ARGS(&pHeap));
if (FAILED(hr))
{
LOG_ERROR("Failed to create D3D12 heap, hr=0x%.8x", hr);
return hr;
}
ppHeaps[i] = pHeap;
RemainingSize -= heapDesc.SizeInBytes;
}
}
//
// Calculate the required size of the upload buffer for transferring the contents
// to the new heaps.
//
UINT64 UploadBufferSize = GetRequiredIntermediateSize(pResource->pDeviceState->pD3DResource, Mip, 1);
ID3D12Resource* pUploadSurface;
void* pUploadData;
//
// If we are using a shared staging surface, we don't have to create a new upload
// resource for each transfer. This feature is currently experimental, and disabled
// by default.
//
ComPtr<ID3D12Resource> pUploadBuffer;
if (m_bUseSharedStagingSurface)
{
pUploadSurface = m_pStagingSurface;
pUploadData = m_pStagingSurfaceData;
}
else
{
hr = m_pDevice->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(&pUploadBuffer));
if (FAILED(hr))
{
LOG_ERROR("Failed to create upload buffer, hr=0x%.8x", hr);
return hr;
}
CD3DX12_RANGE readRange(0, 0);
hr = pUploadBuffer->Map(0, &readRange, &pUploadData);
if (FAILED(hr))
{
LOG_ERROR("Failed to map upload buffer, hr=0x%.8x", hr);
return hr;
}
pUploadSurface = pUploadBuffer.Get();
}
//
// Map the reserved resource (e.g. the virtual address we created with the resource)
// to the heaps that back them. Page table updates for tiled resources can be costly,
// so we will split up the mapping into one heap at a time (16MB of updates per call).
//
{
static const UINT MaxTilesPerUpdate = MAX_HEAP_SIZE / TILE_SIZE;
UINT32 NumTilesRemaining = NumTiles;
while (NumTilesRemaining)
{
UINT TilesInUpdate = min(MaxTilesPerUpdate, NumTilesRemaining);
UINT32 TileOffset = NumTiles - NumTilesRemaining;
D3D12_TILED_RESOURCE_COORDINATE Coordinates = {};
Coordinates.Subresource = Mip;
Coordinates.X = TileOffset % WidthInTiles;
Coordinates.Y = TileOffset / WidthInTiles;
D3D12_TILE_REGION_SIZE RegionSize = {};
RegionSize.NumTiles = TilesInUpdate;
D3D12_TILE_RANGE_FLAGS RangeFlags = D3D12_TILE_RANGE_FLAG_NONE;
UINT RangeOffset = 0;
UINT RangeCount = TilesInUpdate;
m_PagingContext.GetCommandQueue()->UpdateTileMappings(
pResource->pDeviceState->pD3DResource,
1,
&Coordinates,
&RegionSize,
*ppHeaps,
1,
&RangeFlags,
&RangeOffset,
&RangeCount,
D3D12_TILE_MAPPING_FLAG_NO_HAZARD);
NumTilesRemaining -= TilesInUpdate;
++ppHeaps;
}
}
//
// Copy the pixel data into the staging resource, and then transfer it to the
// reserved resource via CopyTextureRegion.
//
D3D12_PLACED_SUBRESOURCE_FOOTPRINT Layout;
UINT NumRows;
UINT64 RowSizeInBytes;
UINT64 TotalBytes;
D3D12_RESOURCE_DESC Desc = pResource->pDeviceState->pD3DResource->GetDesc();
m_pDevice->GetCopyableFootprints(&Desc, Mip, 1, 0, &Layout, &NumRows, &RowSizeInBytes, &TotalBytes);
UINT64 RemainingBytes = TotalBytes;
UINT32 CurrentRow = 0;
while (RemainingBytes > 0)
{
UINT32 MaxTransferHeightInBlocks;
//
// Begin a paging frame. Each paging operation (i.e. a copy/transfer) must be contained
// within a paging frame so we can track and synchronize the operation on the context.
//
m_PagingContext.Begin();
UINT64 BytesInTransfer;
if (m_bUseSharedStagingSurface)
{
MaxTransferHeightInBlocks = static_cast<UINT32>(MAX_TRANSFER_SIZE / Layout.Footprint.RowPitch);
BytesInTransfer = min(MAX_TRANSFER_SIZE, RemainingBytes);
}
else
{
MaxTransferHeightInBlocks = NumRows;
BytesInTransfer = RemainingBytes;
}
UINT32 TransferHeightInBlocks = static_cast<UINT32>((BytesInTransfer + Layout.Footprint.RowPitch - 1) / Layout.Footprint.RowPitch);
TransferHeightInBlocks = min(TransferHeightInBlocks, MaxTransferHeightInBlocks);
UINT32 TransferHeightInRows = TransferHeightInBlocks * MipFrameInfo.BlockHeight;
WICRect SourceRect;
SourceRect.X = 0;
SourceRect.Y = CurrentRow / MipFrameInfo.BlockHeight;
SourceRect.Width = MipFrameInfo.WidthInBlocks;
SourceRect.Height = TransferHeightInBlocks;
//
// The copy differs slightly based on whether or not this is a DDS file with block compressed data.
//
if (pDdsFrame)
{
hr = pDdsFrame->CopyBlocks(&SourceRect, Layout.Footprint.RowPitch, Layout.Footprint.RowPitch * TransferHeightInBlocks, ((BYTE*)pUploadData));
}
else if (pSourceBitmap.Get())
{
hr = pSourceBitmap->CopyPixels(&SourceRect, Layout.Footprint.RowPitch, Layout.Footprint.RowPitch * TransferHeightInBlocks, ((BYTE*)pUploadData));
}
else
{
hr = GenerateMip(pResource->GeneratedImageIndex, &SourceRect, Layout.Footprint.RowPitch, Layout.Footprint.RowPitch * TransferHeightInBlocks, (UINT*)pUploadData);
}
if (FAILED(hr))
{
LOG_ERROR("Failed to copy frame data to upload staging buffer, hr=0x%.8x", hr);
return hr;
}
//
// Copy the texture region on the copy command queue.
//
Frame* pPagingFrame = m_PagingContext.GetCurrentFrame();
D3D12_BOX SrcBox =
{
0, // UINT left;
0, // UINT top;
0, // UINT front;
MipFrameInfo.WidthInBlocks * MipFrameInfo.BlockWidth, // UINT right;
TransferHeightInRows, // UINT bottom;
1, // UINT back;
};
CD3DX12_TEXTURE_COPY_LOCATION Dst(pResource->pDeviceState->pD3DResource, Mip);
CD3DX12_TEXTURE_COPY_LOCATION Src(pUploadSurface, Layout);
Src.PlacedFootprint.Footprint.Height = TransferHeightInRows;
Src.PlacedFootprint.Offset = 0;
pPagingFrame->pCommandList->CopyTextureRegion(&Dst, 0, CurrentRow, 0, &Src, &SrcBox);
//
// Synchronize on this transfer. An application may extend this implementation
// to support multiple operations at a time (by allowing multiple paging frames
// to run concurrently). However, in the interest of simplifying the concepts
// in the sample, it was opted to serialize paging operations to make things
// easier to understand.
//
hr = m_PagingContext.Execute();
if (FAILED(hr))
{
LOG_WARNING("Failed to transfer content for resource 0x%p, mip %d. hr=0x%.8x", pResource, Mip, hr);
return hr;
}
m_PagingContext.End();
m_PagingContext.Flush();
CurrentRow += TransferHeightInRows;
RemainingBytes -= BytesInTransfer;
}
pResource->MostDetailedMipResident = Mip;
AddResourceCommitment(pResource);
return S_OK;
}