//-------------------------------------------------------------------------------------- // File: DDSTextureLoader.cpp // // Functions for loading a DDS texture and creating a Direct3D 11 runtime resource for it // // Note these functions are useful as a light-weight runtime loader for DDS files. For // a full-featured DDS file reader, writer, and texture processing pipeline see // the 'Texconv' sample and the 'DirectXTex' library. // // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A // PARTICULAR PURPOSE. // // Copyright (c) Microsoft Corporation. All rights reserved. // // http://go.microsoft.com/fwlink/?LinkId=248926 // http://go.microsoft.com/fwlink/?LinkId=248929 //-------------------------------------------------------------------------------------- #include "pch.h" #include "DDSTextureLoader.h" #include "dds.h" #include "DirectXHelpers.h" #include "PlatformHelpers.h" using namespace DirectX; //-------------------------------------------------------------------------------------- static HRESULT LoadTextureDataFromFile( _In_z_ const wchar_t* fileName, std::unique_ptr& ddsData, DDS_HEADER** header, uint8_t** bitData, size_t* bitSize ) { if (!header || !bitData || !bitSize) { return E_POINTER; } // open the file #if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) ScopedHandle hFile( safe_handle( CreateFile2( fileName, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, nullptr ) ) ); #else ScopedHandle hFile( safe_handle( CreateFileW( fileName, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr ) ) ); #endif if ( !hFile ) { return HRESULT_FROM_WIN32( GetLastError() ); } // Get the file size LARGE_INTEGER FileSize = { 0 }; #if (_WIN32_WINNT >= _WIN32_WINNT_VISTA) FILE_STANDARD_INFO fileInfo; if ( !GetFileInformationByHandleEx( hFile.get(), FileStandardInfo, &fileInfo, sizeof(fileInfo) ) ) { return HRESULT_FROM_WIN32( GetLastError() ); } FileSize = fileInfo.EndOfFile; #else GetFileSizeEx( hFile.get(), &FileSize ); #endif // File is too big for 32-bit allocation, so reject read if (FileSize.HighPart > 0) { return E_FAIL; } // Need at least enough data to fill the header and magic number to be a valid DDS if (FileSize.LowPart < ( sizeof(DDS_HEADER) + sizeof(uint32_t) ) ) { return E_FAIL; } // create enough space for the file data ddsData.reset( new (std::nothrow) uint8_t[ FileSize.LowPart ] ); if (!ddsData) { return E_OUTOFMEMORY; } // read the data in DWORD BytesRead = 0; if (!ReadFile( hFile.get(), ddsData.get(), FileSize.LowPart, &BytesRead, nullptr )) { return HRESULT_FROM_WIN32( GetLastError() ); } if (BytesRead < FileSize.LowPart) { return E_FAIL; } // DDS files always start with the same magic number ("DDS ") uint32_t dwMagicNumber = *( const uint32_t* )( ddsData.get() ); if (dwMagicNumber != DDS_MAGIC) { return E_FAIL; } auto hdr = reinterpret_cast( ddsData.get() + sizeof( uint32_t ) ); // Verify header to validate DDS file if (hdr->size != sizeof(DDS_HEADER) || hdr->ddspf.size != sizeof(DDS_PIXELFORMAT)) { return E_FAIL; } // Check for DX10 extension bool bDXT10Header = false; if ((hdr->ddspf.flags & DDS_FOURCC) && (MAKEFOURCC( 'D', 'X', '1', '0' ) == hdr->ddspf.fourCC)) { // Must be long enough for both headers and magic value if (FileSize.LowPart < ( sizeof(DDS_HEADER) + sizeof(uint32_t) + sizeof(DDS_HEADER_DXT10) ) ) { return E_FAIL; } bDXT10Header = true; } // setup the pointers in the process request *header = hdr; ptrdiff_t offset = sizeof( uint32_t ) + sizeof( DDS_HEADER ) + (bDXT10Header ? sizeof( DDS_HEADER_DXT10 ) : 0); *bitData = ddsData.get() + offset; *bitSize = FileSize.LowPart - offset; return S_OK; } //-------------------------------------------------------------------------------------- // Return the BPP for a particular format //-------------------------------------------------------------------------------------- static size_t BitsPerPixel( _In_ DXGI_FORMAT fmt ) { switch( fmt ) { case DXGI_FORMAT_R32G32B32A32_TYPELESS: case DXGI_FORMAT_R32G32B32A32_FLOAT: case DXGI_FORMAT_R32G32B32A32_UINT: case DXGI_FORMAT_R32G32B32A32_SINT: return 128; case DXGI_FORMAT_R32G32B32_TYPELESS: case DXGI_FORMAT_R32G32B32_FLOAT: case DXGI_FORMAT_R32G32B32_UINT: case DXGI_FORMAT_R32G32B32_SINT: return 96; case DXGI_FORMAT_R16G16B16A16_TYPELESS: case DXGI_FORMAT_R16G16B16A16_FLOAT: case DXGI_FORMAT_R16G16B16A16_UNORM: case DXGI_FORMAT_R16G16B16A16_UINT: case DXGI_FORMAT_R16G16B16A16_SNORM: case DXGI_FORMAT_R16G16B16A16_SINT: case DXGI_FORMAT_R32G32_TYPELESS: case DXGI_FORMAT_R32G32_FLOAT: case DXGI_FORMAT_R32G32_UINT: case DXGI_FORMAT_R32G32_SINT: case DXGI_FORMAT_R32G8X24_TYPELESS: case DXGI_FORMAT_D32_FLOAT_S8X24_UINT: case DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS: case DXGI_FORMAT_X32_TYPELESS_G8X24_UINT: case DXGI_FORMAT_Y416: case DXGI_FORMAT_Y210: case DXGI_FORMAT_Y216: return 64; case DXGI_FORMAT_R10G10B10A2_TYPELESS: case DXGI_FORMAT_R10G10B10A2_UNORM: case DXGI_FORMAT_R10G10B10A2_UINT: case DXGI_FORMAT_R11G11B10_FLOAT: case DXGI_FORMAT_R8G8B8A8_TYPELESS: case DXGI_FORMAT_R8G8B8A8_UNORM: case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: case DXGI_FORMAT_R8G8B8A8_UINT: case DXGI_FORMAT_R8G8B8A8_SNORM: case DXGI_FORMAT_R8G8B8A8_SINT: case DXGI_FORMAT_R16G16_TYPELESS: case DXGI_FORMAT_R16G16_FLOAT: case DXGI_FORMAT_R16G16_UNORM: case DXGI_FORMAT_R16G16_UINT: case DXGI_FORMAT_R16G16_SNORM: case DXGI_FORMAT_R16G16_SINT: case DXGI_FORMAT_R32_TYPELESS: case DXGI_FORMAT_D32_FLOAT: case DXGI_FORMAT_R32_FLOAT: case DXGI_FORMAT_R32_UINT: case DXGI_FORMAT_R32_SINT: case DXGI_FORMAT_R24G8_TYPELESS: case DXGI_FORMAT_D24_UNORM_S8_UINT: case DXGI_FORMAT_R24_UNORM_X8_TYPELESS: case DXGI_FORMAT_X24_TYPELESS_G8_UINT: case DXGI_FORMAT_R9G9B9E5_SHAREDEXP: case DXGI_FORMAT_R8G8_B8G8_UNORM: case DXGI_FORMAT_G8R8_G8B8_UNORM: case DXGI_FORMAT_B8G8R8A8_UNORM: case DXGI_FORMAT_B8G8R8X8_UNORM: case DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM: case DXGI_FORMAT_B8G8R8A8_TYPELESS: case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: case DXGI_FORMAT_B8G8R8X8_TYPELESS: case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: case DXGI_FORMAT_AYUV: case DXGI_FORMAT_Y410: case DXGI_FORMAT_YUY2: return 32; case DXGI_FORMAT_P010: case DXGI_FORMAT_P016: return 24; case DXGI_FORMAT_R8G8_TYPELESS: case DXGI_FORMAT_R8G8_UNORM: case DXGI_FORMAT_R8G8_UINT: case DXGI_FORMAT_R8G8_SNORM: case DXGI_FORMAT_R8G8_SINT: case DXGI_FORMAT_R16_TYPELESS: case DXGI_FORMAT_R16_FLOAT: case DXGI_FORMAT_D16_UNORM: case DXGI_FORMAT_R16_UNORM: case DXGI_FORMAT_R16_UINT: case DXGI_FORMAT_R16_SNORM: case DXGI_FORMAT_R16_SINT: case DXGI_FORMAT_B5G6R5_UNORM: case DXGI_FORMAT_B5G5R5A1_UNORM: case DXGI_FORMAT_A8P8: case DXGI_FORMAT_B4G4R4A4_UNORM: return 16; case DXGI_FORMAT_NV12: case DXGI_FORMAT_420_OPAQUE: case DXGI_FORMAT_NV11: return 12; case DXGI_FORMAT_R8_TYPELESS: case DXGI_FORMAT_R8_UNORM: case DXGI_FORMAT_R8_UINT: case DXGI_FORMAT_R8_SNORM: case DXGI_FORMAT_R8_SINT: case DXGI_FORMAT_A8_UNORM: case DXGI_FORMAT_AI44: case DXGI_FORMAT_IA44: case DXGI_FORMAT_P8: return 8; case DXGI_FORMAT_R1_UNORM: return 1; case DXGI_FORMAT_BC1_TYPELESS: case DXGI_FORMAT_BC1_UNORM: case DXGI_FORMAT_BC1_UNORM_SRGB: case DXGI_FORMAT_BC4_TYPELESS: case DXGI_FORMAT_BC4_UNORM: case DXGI_FORMAT_BC4_SNORM: return 4; case DXGI_FORMAT_BC2_TYPELESS: case DXGI_FORMAT_BC2_UNORM: case DXGI_FORMAT_BC2_UNORM_SRGB: case DXGI_FORMAT_BC3_TYPELESS: case DXGI_FORMAT_BC3_UNORM: case DXGI_FORMAT_BC3_UNORM_SRGB: case DXGI_FORMAT_BC5_TYPELESS: case DXGI_FORMAT_BC5_UNORM: case DXGI_FORMAT_BC5_SNORM: case DXGI_FORMAT_BC6H_TYPELESS: case DXGI_FORMAT_BC6H_UF16: case DXGI_FORMAT_BC6H_SF16: case DXGI_FORMAT_BC7_TYPELESS: case DXGI_FORMAT_BC7_UNORM: case DXGI_FORMAT_BC7_UNORM_SRGB: return 8; #if defined(_XBOX_ONE) && defined(_TITLE) case DXGI_FORMAT_R10G10B10_7E3_A2_FLOAT: case DXGI_FORMAT_R10G10B10_6E4_A2_FLOAT: case DXGI_FORMAT_R10G10B10_SNORM_A2_UNORM: return 32; case DXGI_FORMAT_D16_UNORM_S8_UINT: case DXGI_FORMAT_R16_UNORM_X8_TYPELESS: case DXGI_FORMAT_X16_TYPELESS_G8_UINT: return 24; case DXGI_FORMAT_R4G4_UNORM: return 8; #endif // _XBOX_ONE && _TITLE default: return 0; } } //-------------------------------------------------------------------------------------- // Get surface information for a particular format //-------------------------------------------------------------------------------------- static void GetSurfaceInfo( _In_ size_t width, _In_ size_t height, _In_ DXGI_FORMAT fmt, _Out_opt_ size_t* outNumBytes, _Out_opt_ size_t* outRowBytes, _Out_opt_ size_t* outNumRows ) { size_t numBytes = 0; size_t rowBytes = 0; size_t numRows = 0; bool bc = false; bool packed = false; bool planar = false; size_t bpe = 0; switch (fmt) { case DXGI_FORMAT_BC1_TYPELESS: case DXGI_FORMAT_BC1_UNORM: case DXGI_FORMAT_BC1_UNORM_SRGB: case DXGI_FORMAT_BC4_TYPELESS: case DXGI_FORMAT_BC4_UNORM: case DXGI_FORMAT_BC4_SNORM: bc=true; bpe = 8; break; case DXGI_FORMAT_BC2_TYPELESS: case DXGI_FORMAT_BC2_UNORM: case DXGI_FORMAT_BC2_UNORM_SRGB: case DXGI_FORMAT_BC3_TYPELESS: case DXGI_FORMAT_BC3_UNORM: case DXGI_FORMAT_BC3_UNORM_SRGB: case DXGI_FORMAT_BC5_TYPELESS: case DXGI_FORMAT_BC5_UNORM: case DXGI_FORMAT_BC5_SNORM: case DXGI_FORMAT_BC6H_TYPELESS: case DXGI_FORMAT_BC6H_UF16: case DXGI_FORMAT_BC6H_SF16: case DXGI_FORMAT_BC7_TYPELESS: case DXGI_FORMAT_BC7_UNORM: case DXGI_FORMAT_BC7_UNORM_SRGB: bc = true; bpe = 16; break; case DXGI_FORMAT_R8G8_B8G8_UNORM: case DXGI_FORMAT_G8R8_G8B8_UNORM: case DXGI_FORMAT_YUY2: packed = true; bpe = 4; break; case DXGI_FORMAT_Y210: case DXGI_FORMAT_Y216: packed = true; bpe = 8; break; case DXGI_FORMAT_NV12: case DXGI_FORMAT_420_OPAQUE: planar = true; bpe = 2; break; case DXGI_FORMAT_P010: case DXGI_FORMAT_P016: planar = true; bpe = 4; break; #if defined(_XBOX_ONE) && defined(_TITLE) case DXGI_FORMAT_D16_UNORM_S8_UINT: case DXGI_FORMAT_R16_UNORM_X8_TYPELESS: case DXGI_FORMAT_X16_TYPELESS_G8_UINT: planar = true; bpe = 4; break; #endif } if (bc) { size_t numBlocksWide = 0; if (width > 0) { numBlocksWide = std::max( 1, (width + 3) / 4 ); } size_t numBlocksHigh = 0; if (height > 0) { numBlocksHigh = std::max( 1, (height + 3) / 4 ); } rowBytes = numBlocksWide * bpe; numRows = numBlocksHigh; numBytes = rowBytes * numBlocksHigh; } else if (packed) { rowBytes = ( ( width + 1 ) >> 1 ) * bpe; numRows = height; numBytes = rowBytes * height; } else if ( fmt == DXGI_FORMAT_NV11 ) { rowBytes = ( ( width + 3 ) >> 2 ) * 4; numRows = height * 2; // Direct3D makes this simplifying assumption, although it is larger than the 4:1:1 data numBytes = rowBytes * numRows; } else if (planar) { rowBytes = ( ( width + 1 ) >> 1 ) * bpe; numBytes = ( rowBytes * height ) + ( ( rowBytes * height + 1 ) >> 1 ); numRows = height + ( ( height + 1 ) >> 1 ); } else { size_t bpp = BitsPerPixel( fmt ); rowBytes = ( width * bpp + 7 ) / 8; // round up to nearest byte numRows = height; numBytes = rowBytes * height; } if (outNumBytes) { *outNumBytes = numBytes; } if (outRowBytes) { *outRowBytes = rowBytes; } if (outNumRows) { *outNumRows = numRows; } } //-------------------------------------------------------------------------------------- #define ISBITMASK( r,g,b,a ) ( ddpf.RBitMask == r && ddpf.GBitMask == g && ddpf.BBitMask == b && ddpf.ABitMask == a ) static DXGI_FORMAT GetDXGIFormat( const DDS_PIXELFORMAT& ddpf ) { if (ddpf.flags & DDS_RGB) { // Note that sRGB formats are written using the "DX10" extended header switch (ddpf.RGBBitCount) { case 32: if (ISBITMASK(0x000000ff,0x0000ff00,0x00ff0000,0xff000000)) { return DXGI_FORMAT_R8G8B8A8_UNORM; } if (ISBITMASK(0x00ff0000,0x0000ff00,0x000000ff,0xff000000)) { return DXGI_FORMAT_B8G8R8A8_UNORM; } if (ISBITMASK(0x00ff0000,0x0000ff00,0x000000ff,0x00000000)) { return DXGI_FORMAT_B8G8R8X8_UNORM; } // No DXGI format maps to ISBITMASK(0x000000ff,0x0000ff00,0x00ff0000,0x00000000) aka D3DFMT_X8B8G8R8 // Note that many common DDS reader/writers (including D3DX) swap the // the RED/BLUE masks for 10:10:10:2 formats. We assume // below that the 'backwards' header mask is being used since it is most // likely written by D3DX. The more robust solution is to use the 'DX10' // header extension and specify the DXGI_FORMAT_R10G10B10A2_UNORM format directly // For 'correct' writers, this should be 0x000003ff,0x000ffc00,0x3ff00000 for RGB data if (ISBITMASK(0x3ff00000,0x000ffc00,0x000003ff,0xc0000000)) { return DXGI_FORMAT_R10G10B10A2_UNORM; } // No DXGI format maps to ISBITMASK(0x000003ff,0x000ffc00,0x3ff00000,0xc0000000) aka D3DFMT_A2R10G10B10 if (ISBITMASK(0x0000ffff,0xffff0000,0x00000000,0x00000000)) { return DXGI_FORMAT_R16G16_UNORM; } if (ISBITMASK(0xffffffff,0x00000000,0x00000000,0x00000000)) { // Only 32-bit color channel format in D3D9 was R32F return DXGI_FORMAT_R32_FLOAT; // D3DX writes this out as a FourCC of 114 } break; case 24: // No 24bpp DXGI formats aka D3DFMT_R8G8B8 break; case 16: if (ISBITMASK(0x7c00,0x03e0,0x001f,0x8000)) { return DXGI_FORMAT_B5G5R5A1_UNORM; } if (ISBITMASK(0xf800,0x07e0,0x001f,0x0000)) { return DXGI_FORMAT_B5G6R5_UNORM; } // No DXGI format maps to ISBITMASK(0x7c00,0x03e0,0x001f,0x0000) aka D3DFMT_X1R5G5B5 if (ISBITMASK(0x0f00,0x00f0,0x000f,0xf000)) { return DXGI_FORMAT_B4G4R4A4_UNORM; } // No DXGI format maps to ISBITMASK(0x0f00,0x00f0,0x000f,0x0000) aka D3DFMT_X4R4G4B4 // No 3:3:2, 3:3:2:8, or paletted DXGI formats aka D3DFMT_A8R3G3B2, D3DFMT_R3G3B2, D3DFMT_P8, D3DFMT_A8P8, etc. break; } } else if (ddpf.flags & DDS_LUMINANCE) { if (8 == ddpf.RGBBitCount) { if (ISBITMASK(0x000000ff,0x00000000,0x00000000,0x00000000)) { return DXGI_FORMAT_R8_UNORM; // D3DX10/11 writes this out as DX10 extension } // No DXGI format maps to ISBITMASK(0x0f,0x00,0x00,0xf0) aka D3DFMT_A4L4 } if (16 == ddpf.RGBBitCount) { if (ISBITMASK(0x0000ffff,0x00000000,0x00000000,0x00000000)) { return DXGI_FORMAT_R16_UNORM; // D3DX10/11 writes this out as DX10 extension } if (ISBITMASK(0x000000ff,0x00000000,0x00000000,0x0000ff00)) { return DXGI_FORMAT_R8G8_UNORM; // D3DX10/11 writes this out as DX10 extension } } } else if (ddpf.flags & DDS_ALPHA) { if (8 == ddpf.RGBBitCount) { return DXGI_FORMAT_A8_UNORM; } } else if (ddpf.flags & DDS_BUMPDUDV) { if (16 == ddpf.RGBBitCount) { if (ISBITMASK(0x00ff, 0xff00, 0x0000, 0x0000)) { return DXGI_FORMAT_R8G8_SNORM; // D3DX10/11 writes this out as DX10 extension } } if (32 == ddpf.RGBBitCount) { if (ISBITMASK(0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000)) { return DXGI_FORMAT_R8G8B8A8_SNORM; // D3DX10/11 writes this out as DX10 extension } if (ISBITMASK(0x0000ffff, 0xffff0000, 0x00000000, 0x00000000)) { return DXGI_FORMAT_R16G16_SNORM; // D3DX10/11 writes this out as DX10 extension } // No DXGI format maps to ISBITMASK(0x3ff00000, 0x000ffc00, 0x000003ff, 0xc0000000) aka D3DFMT_A2W10V10U10 } } else if (ddpf.flags & DDS_FOURCC) { if (MAKEFOURCC( 'D', 'X', 'T', '1' ) == ddpf.fourCC) { return DXGI_FORMAT_BC1_UNORM; } if (MAKEFOURCC( 'D', 'X', 'T', '3' ) == ddpf.fourCC) { return DXGI_FORMAT_BC2_UNORM; } if (MAKEFOURCC( 'D', 'X', 'T', '5' ) == ddpf.fourCC) { return DXGI_FORMAT_BC3_UNORM; } // While pre-multiplied alpha isn't directly supported by the DXGI formats, // they are basically the same as these BC formats so they can be mapped if (MAKEFOURCC( 'D', 'X', 'T', '2' ) == ddpf.fourCC) { return DXGI_FORMAT_BC2_UNORM; } if (MAKEFOURCC( 'D', 'X', 'T', '4' ) == ddpf.fourCC) { return DXGI_FORMAT_BC3_UNORM; } if (MAKEFOURCC( 'A', 'T', 'I', '1' ) == ddpf.fourCC) { return DXGI_FORMAT_BC4_UNORM; } if (MAKEFOURCC( 'B', 'C', '4', 'U' ) == ddpf.fourCC) { return DXGI_FORMAT_BC4_UNORM; } if (MAKEFOURCC( 'B', 'C', '4', 'S' ) == ddpf.fourCC) { return DXGI_FORMAT_BC4_SNORM; } if (MAKEFOURCC( 'A', 'T', 'I', '2' ) == ddpf.fourCC) { return DXGI_FORMAT_BC5_UNORM; } if (MAKEFOURCC( 'B', 'C', '5', 'U' ) == ddpf.fourCC) { return DXGI_FORMAT_BC5_UNORM; } if (MAKEFOURCC( 'B', 'C', '5', 'S' ) == ddpf.fourCC) { return DXGI_FORMAT_BC5_SNORM; } // BC6H and BC7 are written using the "DX10" extended header if (MAKEFOURCC( 'R', 'G', 'B', 'G' ) == ddpf.fourCC) { return DXGI_FORMAT_R8G8_B8G8_UNORM; } if (MAKEFOURCC( 'G', 'R', 'G', 'B' ) == ddpf.fourCC) { return DXGI_FORMAT_G8R8_G8B8_UNORM; } if (MAKEFOURCC('Y','U','Y','2') == ddpf.fourCC) { return DXGI_FORMAT_YUY2; } // Check for D3DFORMAT enums being set here switch( ddpf.fourCC ) { case 36: // D3DFMT_A16B16G16R16 return DXGI_FORMAT_R16G16B16A16_UNORM; case 110: // D3DFMT_Q16W16V16U16 return DXGI_FORMAT_R16G16B16A16_SNORM; case 111: // D3DFMT_R16F return DXGI_FORMAT_R16_FLOAT; case 112: // D3DFMT_G16R16F return DXGI_FORMAT_R16G16_FLOAT; case 113: // D3DFMT_A16B16G16R16F return DXGI_FORMAT_R16G16B16A16_FLOAT; case 114: // D3DFMT_R32F return DXGI_FORMAT_R32_FLOAT; case 115: // D3DFMT_G32R32F return DXGI_FORMAT_R32G32_FLOAT; case 116: // D3DFMT_A32B32G32R32F return DXGI_FORMAT_R32G32B32A32_FLOAT; } } return DXGI_FORMAT_UNKNOWN; } //-------------------------------------------------------------------------------------- static DXGI_FORMAT MakeSRGB( _In_ DXGI_FORMAT format ) { switch( format ) { case DXGI_FORMAT_R8G8B8A8_UNORM: return DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; case DXGI_FORMAT_BC1_UNORM: return DXGI_FORMAT_BC1_UNORM_SRGB; case DXGI_FORMAT_BC2_UNORM: return DXGI_FORMAT_BC2_UNORM_SRGB; case DXGI_FORMAT_BC3_UNORM: return DXGI_FORMAT_BC3_UNORM_SRGB; case DXGI_FORMAT_B8G8R8A8_UNORM: return DXGI_FORMAT_B8G8R8A8_UNORM_SRGB; case DXGI_FORMAT_B8G8R8X8_UNORM: return DXGI_FORMAT_B8G8R8X8_UNORM_SRGB; case DXGI_FORMAT_BC7_UNORM: return DXGI_FORMAT_BC7_UNORM_SRGB; default: return format; } } //-------------------------------------------------------------------------------------- static HRESULT FillInitData( _In_ size_t width, _In_ size_t height, _In_ size_t depth, _In_ size_t mipCount, _In_ size_t arraySize, _In_ DXGI_FORMAT format, _In_ size_t maxsize, _In_ size_t bitSize, _In_reads_bytes_(bitSize) const uint8_t* bitData, _Out_ size_t& twidth, _Out_ size_t& theight, _Out_ size_t& tdepth, _Out_ size_t& skipMip, _Out_writes_(mipCount*arraySize) D3D11_SUBRESOURCE_DATA* initData ) { if ( !bitData || !initData ) { return E_POINTER; } skipMip = 0; twidth = 0; theight = 0; tdepth = 0; size_t NumBytes = 0; size_t RowBytes = 0; const uint8_t* pSrcBits = bitData; const uint8_t* pEndBits = bitData + bitSize; size_t index = 0; for( size_t j = 0; j < arraySize; j++ ) { size_t w = width; size_t h = height; size_t d = depth; for( size_t i = 0; i < mipCount; i++ ) { GetSurfaceInfo( w, h, format, &NumBytes, &RowBytes, nullptr ); if ( (mipCount <= 1) || !maxsize || (w <= maxsize && h <= maxsize && d <= maxsize) ) { if ( !twidth ) { twidth = w; theight = h; tdepth = d; } assert(index < mipCount * arraySize); _Analysis_assume_(index < mipCount * arraySize); initData[index].pSysMem = ( const void* )pSrcBits; initData[index].SysMemPitch = static_cast( RowBytes ); initData[index].SysMemSlicePitch = static_cast( NumBytes ); ++index; } else if ( !j ) { // Count number of skipped mipmaps (first item only) ++skipMip; } if (pSrcBits + (NumBytes*d) > pEndBits) { return HRESULT_FROM_WIN32( ERROR_HANDLE_EOF ); } pSrcBits += NumBytes * d; w = w >> 1; h = h >> 1; d = d >> 1; if (w == 0) { w = 1; } if (h == 0) { h = 1; } if (d == 0) { d = 1; } } } return (index > 0) ? S_OK : E_FAIL; } //-------------------------------------------------------------------------------------- static HRESULT CreateD3DResources( _In_ ID3D11Device* d3dDevice, _In_ uint32_t resDim, _In_ size_t width, _In_ size_t height, _In_ size_t depth, _In_ size_t mipCount, _In_ size_t arraySize, _In_ DXGI_FORMAT format, _In_ D3D11_USAGE usage, _In_ unsigned int bindFlags, _In_ unsigned int cpuAccessFlags, _In_ unsigned int miscFlags, _In_ bool forceSRGB, _In_ bool isCubeMap, _In_reads_opt_(mipCount*arraySize) D3D11_SUBRESOURCE_DATA* initData, _Outptr_opt_ ID3D11Resource** texture, _Outptr_opt_ ID3D11ShaderResourceView** textureView ) { if ( !d3dDevice ) return E_POINTER; HRESULT hr = E_FAIL; if ( forceSRGB ) { format = MakeSRGB( format ); } switch ( resDim ) { case D3D11_RESOURCE_DIMENSION_TEXTURE1D: { D3D11_TEXTURE1D_DESC desc; desc.Width = static_cast( width ); desc.MipLevels = static_cast( mipCount ); desc.ArraySize = static_cast( arraySize ); desc.Format = format; desc.Usage = usage; desc.BindFlags = bindFlags; desc.CPUAccessFlags = cpuAccessFlags; desc.MiscFlags = miscFlags & ~D3D11_RESOURCE_MISC_TEXTURECUBE; ID3D11Texture1D* tex = nullptr; hr = d3dDevice->CreateTexture1D( &desc, initData, &tex ); if (SUCCEEDED( hr ) && tex != 0) { if (textureView != 0) { D3D11_SHADER_RESOURCE_VIEW_DESC SRVDesc; memset( &SRVDesc, 0, sizeof( SRVDesc ) ); SRVDesc.Format = format; if (arraySize > 1) { SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE1DARRAY; SRVDesc.Texture1DArray.MipLevels = (!mipCount) ? -1 : desc.MipLevels; SRVDesc.Texture1DArray.ArraySize = static_cast( arraySize ); } else { SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE1D; SRVDesc.Texture1D.MipLevels = (!mipCount) ? -1 : desc.MipLevels; } hr = d3dDevice->CreateShaderResourceView( tex, &SRVDesc, textureView ); if ( FAILED(hr) ) { tex->Release(); return hr; } } if (texture != 0) { *texture = tex; } else { SetDebugObjectName(tex, "DDSTextureLoader"); tex->Release(); } } } break; case D3D11_RESOURCE_DIMENSION_TEXTURE2D: { D3D11_TEXTURE2D_DESC desc; desc.Width = static_cast( width ); desc.Height = static_cast( height ); desc.MipLevels = static_cast( mipCount ); desc.ArraySize = static_cast( arraySize ); desc.Format = format; desc.SampleDesc.Count = 1; desc.SampleDesc.Quality = 0; desc.Usage = usage; desc.BindFlags = bindFlags; desc.CPUAccessFlags = cpuAccessFlags; if ( isCubeMap ) { desc.MiscFlags = miscFlags | D3D11_RESOURCE_MISC_TEXTURECUBE; } else { desc.MiscFlags = miscFlags & ~D3D11_RESOURCE_MISC_TEXTURECUBE; } ID3D11Texture2D* tex = nullptr; hr = d3dDevice->CreateTexture2D( &desc, initData, &tex ); if (SUCCEEDED( hr ) && tex != 0) { if (textureView != 0) { D3D11_SHADER_RESOURCE_VIEW_DESC SRVDesc; memset( &SRVDesc, 0, sizeof( SRVDesc ) ); SRVDesc.Format = format; if ( isCubeMap ) { if (arraySize > 6) { SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBEARRAY; SRVDesc.TextureCubeArray.MipLevels = (!mipCount) ? -1 : desc.MipLevels; // Earlier we set arraySize to (NumCubes * 6) SRVDesc.TextureCubeArray.NumCubes = static_cast( arraySize / 6 ); } else { SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE; SRVDesc.TextureCube.MipLevels = (!mipCount) ? -1 : desc.MipLevels; } } else if (arraySize > 1) { SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY; SRVDesc.Texture2DArray.MipLevels = (!mipCount) ? -1 : desc.MipLevels; SRVDesc.Texture2DArray.ArraySize = static_cast( arraySize ); } else { SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; SRVDesc.Texture2D.MipLevels = (!mipCount) ? -1 : desc.MipLevels; } hr = d3dDevice->CreateShaderResourceView( tex, &SRVDesc, textureView ); if ( FAILED(hr) ) { tex->Release(); return hr; } } if (texture != 0) { *texture = tex; } else { SetDebugObjectName(tex, "DDSTextureLoader"); tex->Release(); } } } break; case D3D11_RESOURCE_DIMENSION_TEXTURE3D: { D3D11_TEXTURE3D_DESC desc; desc.Width = static_cast( width ); desc.Height = static_cast( height ); desc.Depth = static_cast( depth ); desc.MipLevels = static_cast( mipCount ); desc.Format = format; desc.Usage = usage; desc.BindFlags = bindFlags; desc.CPUAccessFlags = cpuAccessFlags; desc.MiscFlags = miscFlags & ~D3D11_RESOURCE_MISC_TEXTURECUBE; ID3D11Texture3D* tex = nullptr; hr = d3dDevice->CreateTexture3D( &desc, initData, &tex ); if (SUCCEEDED( hr ) && tex != 0) { if (textureView != 0) { D3D11_SHADER_RESOURCE_VIEW_DESC SRVDesc; memset( &SRVDesc, 0, sizeof( SRVDesc ) ); SRVDesc.Format = format; SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D; SRVDesc.Texture3D.MipLevels = (!mipCount) ? -1 : desc.MipLevels; hr = d3dDevice->CreateShaderResourceView( tex, &SRVDesc, textureView ); if ( FAILED(hr) ) { tex->Release(); return hr; } } if (texture != 0) { *texture = tex; } else { SetDebugObjectName(tex, "DDSTextureLoader"); tex->Release(); } } } break; } return hr; } //-------------------------------------------------------------------------------------- static HRESULT CreateTextureFromDDS( _In_ ID3D11Device* d3dDevice, _In_opt_ ID3D11DeviceContext* d3dContext, #if defined(_XBOX_ONE) && defined(_TITLE) _In_opt_ ID3D11DeviceX* d3dDeviceX, _In_opt_ ID3D11DeviceContextX* d3dContextX, #endif _In_ const DDS_HEADER* header, _In_reads_bytes_(bitSize) const uint8_t* bitData, _In_ size_t bitSize, _In_ size_t maxsize, _In_ D3D11_USAGE usage, _In_ unsigned int bindFlags, _In_ unsigned int cpuAccessFlags, _In_ unsigned int miscFlags, _In_ bool forceSRGB, _Outptr_opt_ ID3D11Resource** texture, _Outptr_opt_ ID3D11ShaderResourceView** textureView ) { HRESULT hr = S_OK; UINT width = header->width; UINT height = header->height; UINT depth = header->depth; uint32_t resDim = D3D11_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 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: return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); default: if ( BitsPerPixel( d3d10ext->dxgiFormat ) == 0 ) { return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); } } format = d3d10ext->dxgiFormat; switch ( d3d10ext->resourceDimension ) { case D3D11_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 D3D11_RESOURCE_DIMENSION_TEXTURE2D: if (d3d10ext->miscFlag & D3D11_RESOURCE_MISC_TEXTURECUBE) { arraySize *= 6; isCubeMap = true; } depth = 1; break; case D3D11_RESOURCE_DIMENSION_TEXTURE3D: if (!(header->flags & DDS_HEADER_FLAGS_VOLUME)) { return HRESULT_FROM_WIN32( ERROR_INVALID_DATA ); } if (arraySize > 1) { return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); } break; default: return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); } resDim = d3d10ext->resourceDimension; } else { format = GetDXGIFormat( header->ddspf ); if (format == DXGI_FORMAT_UNKNOWN) { return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); } if (header->flags & DDS_HEADER_FLAGS_VOLUME) { resDim = D3D11_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) { return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); } arraySize = 6; isCubeMap = true; } depth = 1; resDim = D3D11_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 D3D 11.x hardware requirements) if (mipCount > D3D11_REQ_MIP_LEVELS) { return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); } switch ( resDim ) { case D3D11_RESOURCE_DIMENSION_TEXTURE1D: if ((arraySize > D3D11_REQ_TEXTURE1D_ARRAY_AXIS_DIMENSION) || (width > D3D11_REQ_TEXTURE1D_U_DIMENSION) ) { return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); } break; case D3D11_RESOURCE_DIMENSION_TEXTURE2D: if ( isCubeMap ) { // This is the right bound because we set arraySize to (NumCubes*6) above if ((arraySize > D3D11_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION) || (width > D3D11_REQ_TEXTURECUBE_DIMENSION) || (height > D3D11_REQ_TEXTURECUBE_DIMENSION)) { return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); } } else if ((arraySize > D3D11_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION) || (width > D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION) || (height > D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION)) { return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); } break; case D3D11_RESOURCE_DIMENSION_TEXTURE3D: if ((arraySize > 1) || (width > D3D11_REQ_TEXTURE3D_U_V_OR_W_DIMENSION) || (height > D3D11_REQ_TEXTURE3D_U_V_OR_W_DIMENSION) || (depth > D3D11_REQ_TEXTURE3D_U_V_OR_W_DIMENSION) ) { return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); } break; default: return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); } bool autogen = false; if ( mipCount == 1 && d3dContext != 0 && textureView != 0 ) // Must have context and shader-view to auto generate mipmaps { // See if format is supported for auto-gen mipmaps (varies by feature level) UINT fmtSupport = 0; hr = d3dDevice->CheckFormatSupport( format, &fmtSupport ); if ( SUCCEEDED(hr) && ( fmtSupport & D3D11_FORMAT_SUPPORT_MIP_AUTOGEN ) ) { // 10level9 feature levels do not support auto-gen mipgen for volume textures if ( ( resDim != D3D11_RESOURCE_DIMENSION_TEXTURE3D ) || ( d3dDevice->GetFeatureLevel() >= D3D_FEATURE_LEVEL_10_0 ) ) { autogen = true; #if defined(_XBOX_ONE) && defined(_TITLE) if ( !d3dDeviceX || !d3dContextX ) return E_INVALIDARG; #endif } } } if ( autogen ) { // Create texture with auto-generated mipmaps ID3D11Resource* tex = nullptr; hr = CreateD3DResources( d3dDevice, resDim, width, height, depth, 0, arraySize, format, usage, bindFlags | D3D11_BIND_RENDER_TARGET, cpuAccessFlags, miscFlags | D3D11_RESOURCE_MISC_GENERATE_MIPS, forceSRGB, isCubeMap, nullptr, &tex, textureView ); if ( SUCCEEDED(hr) ) { size_t numBytes = 0; size_t rowBytes = 0; GetSurfaceInfo( width, height, format, &numBytes, &rowBytes, nullptr ); if ( numBytes > bitSize ) { (*textureView)->Release(); *textureView = nullptr; tex->Release(); return HRESULT_FROM_WIN32( ERROR_HANDLE_EOF ); } D3D11_SHADER_RESOURCE_VIEW_DESC desc; (*textureView)->GetDesc( &desc ); UINT mipLevels = 1; switch( desc.ViewDimension ) { case D3D_SRV_DIMENSION_TEXTURE1D: mipLevels = desc.Texture1D.MipLevels; break; case D3D_SRV_DIMENSION_TEXTURE1DARRAY: mipLevels = desc.Texture1DArray.MipLevels; break; case D3D_SRV_DIMENSION_TEXTURE2D: mipLevels = desc.Texture2D.MipLevels; break; case D3D_SRV_DIMENSION_TEXTURE2DARRAY: mipLevels = desc.Texture2DArray.MipLevels; break; case D3D_SRV_DIMENSION_TEXTURECUBE: mipLevels = desc.TextureCube.MipLevels; break; case D3D_SRV_DIMENSION_TEXTURECUBEARRAY:mipLevels = desc.TextureCubeArray.MipLevels; break; case D3D_SRV_DIMENSION_TEXTURE3D: mipLevels = desc.Texture3D.MipLevels; break; default: (*textureView)->Release(); *textureView = nullptr; tex->Release(); return E_UNEXPECTED; } #if defined(_XBOX_ONE) && defined(_TITLE) std::unique_ptr initData( new (std::nothrow) D3D11_SUBRESOURCE_DATA[ arraySize ] ); if ( !initData ) { return E_OUTOFMEMORY; } const uint8_t* pSrcBits = bitData; const uint8_t* pEndBits = bitData + bitSize; for( UINT item = 0; item < arraySize; ++item ) { if ( (pSrcBits + numBytes) > pEndBits ) { (*textureView)->Release(); *textureView = nullptr; tex->Release(); return HRESULT_FROM_WIN32( ERROR_HANDLE_EOF ); } initData[item].pSysMem = pSrcBits; initData[item].SysMemPitch = static_cast(rowBytes); initData[item].SysMemSlicePitch = static_cast(numBytes); pSrcBits += numBytes; } ID3D11Resource* pStaging = nullptr; switch( resDim ) { case D3D11_RESOURCE_DIMENSION_TEXTURE1D: { ID3D11Texture1D *temp = nullptr; CD3D11_TEXTURE1D_DESC stagingDesc( format, width, arraySize, 1, 0, D3D11_USAGE_STAGING, D3D11_CPU_ACCESS_READ ); hr = d3dDevice->CreateTexture1D( &stagingDesc, initData.get(), &temp ); if ( SUCCEEDED(hr) ) pStaging = temp; } break; case D3D11_RESOURCE_DIMENSION_TEXTURE2D: { ID3D11Texture2D *temp = nullptr; CD3D11_TEXTURE2D_DESC stagingDesc( format, width, height, arraySize, 1, 0, D3D11_USAGE_STAGING, D3D11_CPU_ACCESS_READ, 1, 0, isCubeMap ? D3D11_RESOURCE_MISC_TEXTURECUBE : 0 ); hr = d3dDevice->CreateTexture2D( &stagingDesc, initData.get(), &temp ); if ( SUCCEEDED(hr) ) pStaging = temp; } break; case D3D11_RESOURCE_DIMENSION_TEXTURE3D: { ID3D11Texture3D *temp = nullptr; CD3D11_TEXTURE3D_DESC stagingDesc( format, width, height, depth, 1, 0, D3D11_USAGE_STAGING, D3D11_CPU_ACCESS_READ ); hr = d3dDevice->CreateTexture3D( &stagingDesc, initData.get(), &temp ); if ( SUCCEEDED(hr) ) pStaging = temp; } break; }; if ( SUCCEEDED(hr) ) { for( UINT item = 0; item < arraySize; ++item ) { UINT res = D3D11CalcSubresource( 0, item, mipLevels ); d3dContext->CopySubresourceRegion( tex, res, 0, 0, 0, pStaging, item, nullptr ); } UINT64 copyFence = d3dContextX->InsertFence(0); while( d3dDeviceX->IsFencePending( copyFence ) ) { SwitchToThread(); } pStaging->Release(); } #else if ( arraySize > 1 ) { const uint8_t* pSrcBits = bitData; const uint8_t* pEndBits = bitData + bitSize; for( UINT item = 0; item < arraySize; ++item ) { if ( (pSrcBits + numBytes) > pEndBits ) { (*textureView)->Release(); *textureView = nullptr; tex->Release(); return HRESULT_FROM_WIN32( ERROR_HANDLE_EOF ); } UINT res = D3D11CalcSubresource( 0, item, mipLevels ); d3dContext->UpdateSubresource( tex, res, nullptr, pSrcBits, static_cast(rowBytes), static_cast(numBytes) ); pSrcBits += numBytes; } } else { d3dContext->UpdateSubresource( tex, 0, nullptr, bitData, static_cast(rowBytes), static_cast(numBytes) ); } #endif d3dContext->GenerateMips( *textureView ); if ( texture ) { *texture = tex; } else { tex->Release(); } } } else { // Create the texture std::unique_ptr initData( new (std::nothrow) D3D11_SUBRESOURCE_DATA[ mipCount * arraySize ] ); if ( !initData ) { return E_OUTOFMEMORY; } size_t skipMip = 0; size_t twidth = 0; size_t theight = 0; size_t tdepth = 0; hr = FillInitData( width, height, depth, mipCount, arraySize, format, maxsize, bitSize, bitData, twidth, theight, tdepth, skipMip, initData.get() ); if ( SUCCEEDED(hr) ) { hr = CreateD3DResources( d3dDevice, resDim, twidth, theight, tdepth, mipCount - skipMip, arraySize, format, usage, bindFlags, cpuAccessFlags, miscFlags, forceSRGB, isCubeMap, initData.get(), texture, textureView ); if ( FAILED(hr) && !maxsize && (mipCount > 1) ) { // Retry with a maxsize determined by feature level switch( d3dDevice->GetFeatureLevel() ) { case D3D_FEATURE_LEVEL_9_1: case D3D_FEATURE_LEVEL_9_2: if ( isCubeMap ) { maxsize = 512 /*D3D_FL9_1_REQ_TEXTURECUBE_DIMENSION*/; } else { maxsize = (resDim == D3D11_RESOURCE_DIMENSION_TEXTURE3D) ? 256 /*D3D_FL9_1_REQ_TEXTURE3D_U_V_OR_W_DIMENSION*/ : 2048 /*D3D_FL9_1_REQ_TEXTURE2D_U_OR_V_DIMENSION*/; } break; case D3D_FEATURE_LEVEL_9_3: maxsize = (resDim == D3D11_RESOURCE_DIMENSION_TEXTURE3D) ? 256 /*D3D_FL9_1_REQ_TEXTURE3D_U_V_OR_W_DIMENSION*/ : 4096 /*D3D_FL9_3_REQ_TEXTURE2D_U_OR_V_DIMENSION*/; break; default: // D3D_FEATURE_LEVEL_10_0 & D3D_FEATURE_LEVEL_10_1 maxsize = (resDim == D3D11_RESOURCE_DIMENSION_TEXTURE3D) ? 2048 /*D3D10_REQ_TEXTURE3D_U_V_OR_W_DIMENSION*/ : 8192 /*D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION*/; break; } hr = FillInitData( width, height, depth, mipCount, arraySize, format, maxsize, bitSize, bitData, twidth, theight, tdepth, skipMip, initData.get() ); if ( SUCCEEDED(hr) ) { hr = CreateD3DResources( d3dDevice, resDim, twidth, theight, tdepth, mipCount - skipMip, arraySize, format, usage, bindFlags, cpuAccessFlags, miscFlags, forceSRGB, isCubeMap, initData.get(), texture, textureView ); } } } } return hr; } //-------------------------------------------------------------------------------------- static DDS_ALPHA_MODE GetAlphaMode( _In_ const DDS_HEADER* header ) { if ( header->ddspf.flags & DDS_FOURCC ) { if ( MAKEFOURCC( 'D', 'X', '1', '0' ) == header->ddspf.fourCC ) { auto d3d10ext = reinterpret_cast( (const char*)header + sizeof(DDS_HEADER) ); auto mode = static_cast( d3d10ext->miscFlags2 & DDS_MISC_FLAGS2_ALPHA_MODE_MASK ); switch( mode ) { case DDS_ALPHA_MODE_STRAIGHT: case DDS_ALPHA_MODE_PREMULTIPLIED: case DDS_ALPHA_MODE_OPAQUE: case DDS_ALPHA_MODE_CUSTOM: return mode; } } else if ( ( MAKEFOURCC( 'D', 'X', 'T', '2' ) == header->ddspf.fourCC ) || ( MAKEFOURCC( 'D', 'X', 'T', '4' ) == header->ddspf.fourCC ) ) { return DDS_ALPHA_MODE_PREMULTIPLIED; } } return DDS_ALPHA_MODE_UNKNOWN; } //-------------------------------------------------------------------------------------- _Use_decl_annotations_ HRESULT DirectX::CreateDDSTextureFromMemory( ID3D11Device* d3dDevice, const uint8_t* ddsData, size_t ddsDataSize, ID3D11Resource** texture, ID3D11ShaderResourceView** textureView, size_t maxsize, DDS_ALPHA_MODE* alphaMode ) { return CreateDDSTextureFromMemoryEx( d3dDevice, ddsData, ddsDataSize, maxsize, D3D11_USAGE_DEFAULT, D3D11_BIND_SHADER_RESOURCE, 0, 0, false, texture, textureView, alphaMode ); } _Use_decl_annotations_ #if defined(_XBOX_ONE) && defined(_TITLE) HRESULT DirectX::CreateDDSTextureFromMemory( ID3D11DeviceX* d3dDevice, ID3D11DeviceContextX* d3dContext, #else HRESULT DirectX::CreateDDSTextureFromMemory( ID3D11Device* d3dDevice, ID3D11DeviceContext* d3dContext, #endif const uint8_t* ddsData, size_t ddsDataSize, ID3D11Resource** texture, ID3D11ShaderResourceView** textureView, size_t maxsize, DDS_ALPHA_MODE* alphaMode ) { return CreateDDSTextureFromMemoryEx( d3dDevice, d3dContext, ddsData, ddsDataSize, maxsize, D3D11_USAGE_DEFAULT, D3D11_BIND_SHADER_RESOURCE, 0, 0, false, texture, textureView, alphaMode ); } _Use_decl_annotations_ HRESULT DirectX::CreateDDSTextureFromMemoryEx( ID3D11Device* d3dDevice, const uint8_t* ddsData, size_t ddsDataSize, size_t maxsize, D3D11_USAGE usage, unsigned int bindFlags, unsigned int cpuAccessFlags, unsigned int miscFlags, bool forceSRGB, ID3D11Resource** texture, ID3D11ShaderResourceView** textureView, DDS_ALPHA_MODE* alphaMode ) { if ( texture ) { *texture = nullptr; } if ( textureView ) { *textureView = nullptr; } if ( alphaMode ) { *alphaMode = DDS_ALPHA_MODE_UNKNOWN; } if (!d3dDevice || !ddsData || (!texture && !textureView)) { return E_INVALIDARG; } // Validate DDS file in memory if (ddsDataSize < (sizeof(uint32_t) + sizeof(DDS_HEADER))) { return E_FAIL; } uint32_t dwMagicNumber = *( const uint32_t* )( ddsData ); if (dwMagicNumber != DDS_MAGIC) { return E_FAIL; } auto header = reinterpret_cast( ddsData + sizeof( uint32_t ) ); // Verify header to validate DDS file if (header->size != sizeof(DDS_HEADER) || header->ddspf.size != sizeof(DDS_PIXELFORMAT)) { return E_FAIL; } // Check for DX10 extension bool bDXT10Header = false; if ((header->ddspf.flags & DDS_FOURCC) && (MAKEFOURCC( 'D', 'X', '1', '0' ) == header->ddspf.fourCC) ) { // Must be long enough for both headers and magic value if (ddsDataSize < (sizeof(DDS_HEADER) + sizeof(uint32_t) + sizeof(DDS_HEADER_DXT10))) { return E_FAIL; } bDXT10Header = true; } ptrdiff_t offset = sizeof( uint32_t ) + sizeof( DDS_HEADER ) + (bDXT10Header ? sizeof( DDS_HEADER_DXT10 ) : 0); HRESULT hr = CreateTextureFromDDS( d3dDevice, nullptr, #if defined(_XBOX_ONE) && defined(_TITLE) nullptr, nullptr, #endif header, ddsData + offset, ddsDataSize - offset, maxsize, usage, bindFlags, cpuAccessFlags, miscFlags, forceSRGB, texture, textureView ); if ( SUCCEEDED(hr) ) { if (texture != 0 && *texture != 0) { SetDebugObjectName(*texture, "DDSTextureLoader"); } if (textureView != 0 && *textureView != 0) { SetDebugObjectName(*textureView, "DDSTextureLoader"); } if ( alphaMode ) *alphaMode = GetAlphaMode( header ); } return hr; } _Use_decl_annotations_ #if defined(_XBOX_ONE) && defined(_TITLE) HRESULT DirectX::CreateDDSTextureFromMemoryEx( ID3D11DeviceX* d3dDevice, ID3D11DeviceContextX* d3dContext, #else HRESULT DirectX::CreateDDSTextureFromMemoryEx( ID3D11Device* d3dDevice, ID3D11DeviceContext* d3dContext, #endif const uint8_t* ddsData, size_t ddsDataSize, size_t maxsize, D3D11_USAGE usage, unsigned int bindFlags, unsigned int cpuAccessFlags, unsigned int miscFlags, bool forceSRGB, ID3D11Resource** texture, ID3D11ShaderResourceView** textureView, DDS_ALPHA_MODE* alphaMode ) { if ( texture ) { *texture = nullptr; } if ( textureView ) { *textureView = nullptr; } if ( alphaMode ) { *alphaMode = DDS_ALPHA_MODE_UNKNOWN; } if (!d3dDevice || !ddsData || (!texture && !textureView)) { return E_INVALIDARG; } // Validate DDS file in memory if (ddsDataSize < (sizeof(uint32_t) + sizeof(DDS_HEADER))) { return E_FAIL; } uint32_t dwMagicNumber = *( const uint32_t* )( ddsData ); if (dwMagicNumber != DDS_MAGIC) { return E_FAIL; } auto header = reinterpret_cast( ddsData + sizeof( uint32_t ) ); // Verify header to validate DDS file if (header->size != sizeof(DDS_HEADER) || header->ddspf.size != sizeof(DDS_PIXELFORMAT)) { return E_FAIL; } // Check for DX10 extension bool bDXT10Header = false; if ((header->ddspf.flags & DDS_FOURCC) && (MAKEFOURCC( 'D', 'X', '1', '0' ) == header->ddspf.fourCC) ) { // Must be long enough for both headers and magic value if (ddsDataSize < (sizeof(DDS_HEADER) + sizeof(uint32_t) + sizeof(DDS_HEADER_DXT10))) { return E_FAIL; } bDXT10Header = true; } ptrdiff_t offset = sizeof( uint32_t ) + sizeof( DDS_HEADER ) + (bDXT10Header ? sizeof( DDS_HEADER_DXT10 ) : 0); HRESULT hr = CreateTextureFromDDS( d3dDevice, d3dContext, #if defined(_XBOX_ONE) && defined(_TITLE) d3dDevice, d3dContext, #endif header, ddsData + offset, ddsDataSize - offset, maxsize, usage, bindFlags, cpuAccessFlags, miscFlags, forceSRGB, texture, textureView ); if ( SUCCEEDED(hr) ) { if (texture != 0 && *texture != 0) { SetDebugObjectName(*texture, "DDSTextureLoader"); } if (textureView != 0 && *textureView != 0) { SetDebugObjectName(*textureView, "DDSTextureLoader"); } if ( alphaMode ) *alphaMode = GetAlphaMode( header ); } return hr; } //-------------------------------------------------------------------------------------- _Use_decl_annotations_ HRESULT DirectX::CreateDDSTextureFromFile( ID3D11Device* d3dDevice, const wchar_t* fileName, ID3D11Resource** texture, ID3D11ShaderResourceView** textureView, size_t maxsize, DDS_ALPHA_MODE* alphaMode ) { return CreateDDSTextureFromFileEx( d3dDevice, fileName, maxsize, D3D11_USAGE_DEFAULT, D3D11_BIND_SHADER_RESOURCE, 0, 0, false, texture, textureView, alphaMode ); } _Use_decl_annotations_ #if defined(_XBOX_ONE) && defined(_TITLE) HRESULT DirectX::CreateDDSTextureFromFile( ID3D11DeviceX* d3dDevice, ID3D11DeviceContextX* d3dContext, #else HRESULT DirectX::CreateDDSTextureFromFile( ID3D11Device* d3dDevice, ID3D11DeviceContext* d3dContext, #endif const wchar_t* fileName, ID3D11Resource** texture, ID3D11ShaderResourceView** textureView, size_t maxsize, DDS_ALPHA_MODE* alphaMode ) { return CreateDDSTextureFromFileEx( d3dDevice, d3dContext, fileName, maxsize, D3D11_USAGE_DEFAULT, D3D11_BIND_SHADER_RESOURCE, 0, 0, false, texture, textureView, alphaMode ); } _Use_decl_annotations_ HRESULT DirectX::CreateDDSTextureFromFileEx( ID3D11Device* d3dDevice, const wchar_t* fileName, size_t maxsize, D3D11_USAGE usage, unsigned int bindFlags, unsigned int cpuAccessFlags, unsigned int miscFlags, bool forceSRGB, ID3D11Resource** texture, ID3D11ShaderResourceView** textureView, DDS_ALPHA_MODE* alphaMode ) { if ( texture ) { *texture = nullptr; } if ( textureView ) { *textureView = nullptr; } if ( alphaMode ) { *alphaMode = DDS_ALPHA_MODE_UNKNOWN; } if (!d3dDevice || !fileName || (!texture && !textureView)) { return E_INVALIDARG; } DDS_HEADER* header = nullptr; uint8_t* bitData = nullptr; size_t bitSize = 0; std::unique_ptr ddsData; HRESULT hr = LoadTextureDataFromFile( fileName, ddsData, &header, &bitData, &bitSize ); if (FAILED(hr)) { return hr; } hr = CreateTextureFromDDS( d3dDevice, nullptr, #if defined(_XBOX_ONE) && defined(_TITLE) nullptr, nullptr, #endif header, bitData, bitSize, maxsize, usage, bindFlags, cpuAccessFlags, miscFlags, forceSRGB, texture, textureView ); if ( SUCCEEDED(hr) ) { #if !defined(NO_D3D11_DEBUG_NAME) && ( defined(_DEBUG) || defined(PROFILE) ) #if defined(_XBOX_ONE) && defined(_TITLE) if (texture != 0 && *texture != 0) { (*texture)->SetName( fileName ); } if (textureView != 0 && *textureView != 0 ) { (*textureView)->SetName( fileName ); } #else if (texture != 0 || textureView != 0) { CHAR strFileA[MAX_PATH]; int result = WideCharToMultiByte( CP_ACP, WC_NO_BEST_FIT_CHARS, fileName, -1, strFileA, MAX_PATH, nullptr, FALSE ); if ( result > 0 ) { const CHAR* pstrName = strrchr( strFileA, '\\' ); if (!pstrName) { pstrName = strFileA; } else { pstrName++; } if (texture != 0 && *texture != 0) { (*texture)->SetPrivateData( WKPDID_D3DDebugObjectName, static_cast( strnlen_s(pstrName, MAX_PATH) ), pstrName ); } if (textureView != 0 && *textureView != 0 ) { (*textureView)->SetPrivateData( WKPDID_D3DDebugObjectName, static_cast( strnlen_s(pstrName, MAX_PATH) ), pstrName ); } } } #endif #endif if ( alphaMode ) *alphaMode = GetAlphaMode( header ); } return hr; } _Use_decl_annotations_ #if defined(_XBOX_ONE) && defined(_TITLE) HRESULT DirectX::CreateDDSTextureFromFileEx( ID3D11DeviceX* d3dDevice, ID3D11DeviceContextX* d3dContext, #else HRESULT DirectX::CreateDDSTextureFromFileEx( ID3D11Device* d3dDevice, ID3D11DeviceContext* d3dContext, #endif const wchar_t* fileName, size_t maxsize, D3D11_USAGE usage, unsigned int bindFlags, unsigned int cpuAccessFlags, unsigned int miscFlags, bool forceSRGB, ID3D11Resource** texture, ID3D11ShaderResourceView** textureView, DDS_ALPHA_MODE* alphaMode ) { if ( texture ) { *texture = nullptr; } if ( textureView ) { *textureView = nullptr; } if ( alphaMode ) { *alphaMode = DDS_ALPHA_MODE_UNKNOWN; } if (!d3dDevice || !fileName || (!texture && !textureView)) { return E_INVALIDARG; } DDS_HEADER* header = nullptr; uint8_t* bitData = nullptr; size_t bitSize = 0; std::unique_ptr ddsData; HRESULT hr = LoadTextureDataFromFile( fileName, ddsData, &header, &bitData, &bitSize ); if (FAILED(hr)) { return hr; } hr = CreateTextureFromDDS( d3dDevice, d3dContext, #if defined(_XBOX_ONE) && defined(_TITLE) d3dDevice, d3dContext, #endif header, bitData, bitSize, maxsize, usage, bindFlags, cpuAccessFlags, miscFlags, forceSRGB, texture, textureView ); if ( SUCCEEDED(hr) ) { #if !defined(NO_D3D11_DEBUG_NAME) && ( defined(_DEBUG) || defined(PROFILE) ) #if defined(_XBOX_ONE) && defined(_TITLE) if (texture != 0 && *texture != 0) { (*texture)->SetName( fileName ); } if (textureView != 0 && *textureView != 0 ) { (*textureView)->SetName( fileName ); } #else if (texture != 0 || textureView != 0) { CHAR strFileA[MAX_PATH]; int result = WideCharToMultiByte( CP_ACP, WC_NO_BEST_FIT_CHARS, fileName, -1, strFileA, MAX_PATH, nullptr, FALSE ); if ( result > 0 ) { const CHAR* pstrName = strrchr( strFileA, '\\' ); if (!pstrName) { pstrName = strFileA; } else { pstrName++; } if (texture != 0 && *texture != 0) { (*texture)->SetPrivateData( WKPDID_D3DDebugObjectName, static_cast( strnlen_s(pstrName, MAX_PATH) ), pstrName ); } if (textureView != 0 && *textureView != 0 ) { (*textureView)->SetPrivateData( WKPDID_D3DDebugObjectName, static_cast( strnlen_s(pstrName, MAX_PATH) ), pstrName ); } } } #endif #endif if ( alphaMode ) *alphaMode = GetAlphaMode( header ); } return hr; }