in Src/DDSTextureLoader.cpp [275:550]
HRESULT CreateTextureFromDDS(_In_ ID3D12Device* d3dDevice,
_In_ const DDS_HEADER* header,
_In_reads_bytes_(bitSize) const uint8_t* bitData,
size_t bitSize,
size_t maxsize,
D3D12_RESOURCE_FLAGS resFlags,
DDS_LOADER_FLAGS loadFlags,
_Outptr_ ID3D12Resource** texture,
std::vector<D3D12_SUBRESOURCE_DATA>& subresources,
_Out_opt_ bool* outIsCubeMap) noexcept(false)
{
HRESULT hr = S_OK;
UINT width = header->width;
UINT height = header->height;
UINT depth = header->depth;
D3D12_RESOURCE_DIMENSION resDim = D3D12_RESOURCE_DIMENSION_UNKNOWN;
UINT arraySize = 1;
DXGI_FORMAT format = DXGI_FORMAT_UNKNOWN;
bool isCubeMap = false;
size_t mipCount = header->mipMapCount;
if (0 == mipCount)
{
mipCount = 1;
}
if ((header->ddspf.flags & DDS_FOURCC) &&
(MAKEFOURCC('D', 'X', '1', '0') == header->ddspf.fourCC))
{
auto d3d10ext = reinterpret_cast<const DDS_HEADER_DXT10*>(reinterpret_cast<const char*>(header) + sizeof(DDS_HEADER));
arraySize = d3d10ext->arraySize;
if (arraySize == 0)
{
return HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
}
switch (d3d10ext->dxgiFormat)
{
case DXGI_FORMAT_AI44:
case DXGI_FORMAT_IA44:
case DXGI_FORMAT_P8:
case DXGI_FORMAT_A8P8:
DebugTrace("ERROR: DDSTextureLoader does not support video textures. Consider using DirectXTex instead.\n");
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
default:
if (BitsPerPixel(d3d10ext->dxgiFormat) == 0)
{
DebugTrace("ERROR: Unknown DXGI format (%u)\n", static_cast<uint32_t>(d3d10ext->dxgiFormat));
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
}
break;
}
format = d3d10ext->dxgiFormat;
switch (d3d10ext->resourceDimension)
{
case D3D12_RESOURCE_DIMENSION_TEXTURE1D:
// D3DX writes 1D textures with a fixed Height of 1
if ((header->flags & DDS_HEIGHT) && height != 1)
{
return HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
}
height = depth = 1;
break;
case D3D12_RESOURCE_DIMENSION_TEXTURE2D:
if (d3d10ext->miscFlag & 0x4 /* RESOURCE_MISC_TEXTURECUBE */)
{
arraySize *= 6;
isCubeMap = true;
}
depth = 1;
break;
case D3D12_RESOURCE_DIMENSION_TEXTURE3D:
if (!(header->flags & DDS_HEADER_FLAGS_VOLUME))
{
return HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
}
if (arraySize > 1)
{
DebugTrace("ERROR: Volume textures are not texture arrays\n");
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
}
break;
case D3D12_RESOURCE_DIMENSION_BUFFER:
DebugTrace("ERROR: Resource dimension buffer type not supported for textures\n");
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
case D3D12_RESOURCE_DIMENSION_UNKNOWN:
default:
DebugTrace("ERROR: Unknown resource dimension (%u)\n", static_cast<uint32_t>(d3d10ext->resourceDimension));
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
}
resDim = static_cast<D3D12_RESOURCE_DIMENSION>(d3d10ext->resourceDimension);
}
else
{
format = GetDXGIFormat(header->ddspf);
if (format == DXGI_FORMAT_UNKNOWN)
{
DebugTrace("ERROR: DDSTextureLoader does not support all legacy DDS formats. Consider using DirectXTex.\n");
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
}
if (header->flags & DDS_HEADER_FLAGS_VOLUME)
{
resDim = D3D12_RESOURCE_DIMENSION_TEXTURE3D;
}
else
{
if (header->caps2 & DDS_CUBEMAP)
{
// We require all six faces to be defined
if ((header->caps2 & DDS_CUBEMAP_ALLFACES) != DDS_CUBEMAP_ALLFACES)
{
DebugTrace("ERROR: DirectX 12 does not support partial cubemaps\n");
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
}
arraySize = 6;
isCubeMap = true;
}
depth = 1;
resDim = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
// Note there's no way for a legacy Direct3D 9 DDS to express a '1D' texture
}
assert(BitsPerPixel(format) != 0);
}
// Bound sizes (for security purposes we don't trust DDS file metadata larger than the Direct3D hardware requirements)
if (mipCount > D3D12_REQ_MIP_LEVELS)
{
DebugTrace("ERROR: Too many mipmap levels defined for DirectX 12 (%zu).\n", mipCount);
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
}
switch (resDim)
{
case D3D12_RESOURCE_DIMENSION_TEXTURE1D:
if ((arraySize > D3D12_REQ_TEXTURE1D_ARRAY_AXIS_DIMENSION) ||
(width > D3D12_REQ_TEXTURE1D_U_DIMENSION))
{
DebugTrace("ERROR: Resource dimensions too large for DirectX 12 (1D: array %u, size %u)\n", arraySize, width);
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
}
break;
case D3D12_RESOURCE_DIMENSION_TEXTURE2D:
if (isCubeMap)
{
// This is the right bound because we set arraySize to (NumCubes*6) above
if ((arraySize > D3D12_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION) ||
(width > D3D12_REQ_TEXTURECUBE_DIMENSION) ||
(height > D3D12_REQ_TEXTURECUBE_DIMENSION))
{
DebugTrace("ERROR: Resource dimensions too large for DirectX 12 (2D cubemap: array %u, size %u by %u)\n", arraySize, width, height);
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
}
}
else if ((arraySize > D3D12_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION) ||
(width > D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION) ||
(height > D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION))
{
DebugTrace("ERROR: Resource dimensions too large for DirectX 12 (2D: array %u, size %u by %u)\n", arraySize, width, height);
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
}
break;
case D3D12_RESOURCE_DIMENSION_TEXTURE3D:
if ((arraySize > 1) ||
(width > D3D12_REQ_TEXTURE3D_U_V_OR_W_DIMENSION) ||
(height > D3D12_REQ_TEXTURE3D_U_V_OR_W_DIMENSION) ||
(depth > D3D12_REQ_TEXTURE3D_U_V_OR_W_DIMENSION))
{
DebugTrace("ERROR: Resource dimensions too large for DirectX 12 (3D: array %u, size %u by %u by %u)\n", arraySize, width, height, depth);
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
}
break;
case D3D12_RESOURCE_DIMENSION_BUFFER:
DebugTrace("ERROR: Resource dimension buffer type not supported for textures\n");
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
default:
DebugTrace("ERROR: Unknown resource dimension (%u)\n", static_cast<uint32_t>(resDim));
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
}
UINT numberOfPlanes = D3D12GetFormatPlaneCount(d3dDevice, format);
if (!numberOfPlanes)
return E_INVALIDARG;
if ((numberOfPlanes > 1) && IsDepthStencil(format))
{
// DirectX 12 uses planes for stencil, DirectX 11 does not
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
}
if (outIsCubeMap != nullptr)
{
*outIsCubeMap = isCubeMap;
}
// Create the texture
size_t numberOfResources = (resDim == D3D12_RESOURCE_DIMENSION_TEXTURE3D)
? 1 : arraySize;
numberOfResources *= mipCount;
numberOfResources *= numberOfPlanes;
if (numberOfResources > D3D12_REQ_SUBRESOURCES)
return E_INVALIDARG;
subresources.reserve(numberOfResources);
size_t skipMip = 0;
size_t twidth = 0;
size_t theight = 0;
size_t tdepth = 0;
hr = FillInitData(width, height, depth, mipCount, arraySize,
numberOfPlanes, format,
maxsize, bitSize, bitData,
twidth, theight, tdepth, skipMip, subresources);
if (SUCCEEDED(hr))
{
size_t reservedMips = mipCount;
if (loadFlags & (DDS_LOADER_MIP_AUTOGEN | DDS_LOADER_MIP_RESERVE))
{
reservedMips = std::min<size_t>(D3D12_REQ_MIP_LEVELS,
LoaderHelpers::CountMips(width, height));
}
hr = CreateTextureResource(d3dDevice, resDim, twidth, theight, tdepth, reservedMips - skipMip, arraySize,
format, resFlags, loadFlags, texture);
if (FAILED(hr) && !maxsize && (mipCount > 1))
{
subresources.clear();
maxsize = static_cast<size_t>(
(resDim == D3D12_RESOURCE_DIMENSION_TEXTURE3D)
? D3D12_REQ_TEXTURE3D_U_V_OR_W_DIMENSION
: D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION);
hr = FillInitData(width, height, depth, mipCount, arraySize,
numberOfPlanes, format,
maxsize, bitSize, bitData,
twidth, theight, tdepth, skipMip, subresources);
if (SUCCEEDED(hr))
{
hr = CreateTextureResource(d3dDevice, resDim, twidth, theight, tdepth, mipCount - skipMip, arraySize,
format, resFlags, loadFlags, texture);
}
}
}
if (FAILED(hr))
{
subresources.clear();
}
return hr;
}