//-------------------------------------------------------------------------------------- // File: DDSTextureLoader.cpp // // Functions for loading a DDS texture and creating a Direct3D 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. // // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. // // http://go.microsoft.com/fwlink/?LinkID=615561 //-------------------------------------------------------------------------------------- #include "pch.h" #include "DDSTextureLoader.h" #include "PlatformHelpers.h" #include "DDS.h" #include "DirectXHelpers.h" #include "LoaderHelpers.h" #include "ResourceUploadBatch.h" using namespace DirectX; using namespace DirectX::LoaderHelpers; static_assert(static_cast(DDS_DIMENSION_TEXTURE1D) == static_cast(D3D12_RESOURCE_DIMENSION_TEXTURE1D), "dds mismatch"); static_assert(static_cast(DDS_DIMENSION_TEXTURE2D) == static_cast(D3D12_RESOURCE_DIMENSION_TEXTURE2D), "dds mismatch"); static_assert(static_cast(DDS_DIMENSION_TEXTURE3D) == static_cast(D3D12_RESOURCE_DIMENSION_TEXTURE3D), "dds mismatch"); namespace { inline bool IsDepthStencil(DXGI_FORMAT fmt) noexcept { switch (fmt) { 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_D32_FLOAT: 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_D16_UNORM: #if (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) case DXGI_FORMAT_D16_UNORM_S8_UINT: case DXGI_FORMAT_R16_UNORM_X8_TYPELESS: case DXGI_FORMAT_X16_TYPELESS_G8_UINT: #endif return true; default: return false; } } //-------------------------------------------------------------------------------------- inline void AdjustPlaneResource( _In_ DXGI_FORMAT fmt, _In_ size_t height, _In_ size_t slicePlane, _Inout_ D3D12_SUBRESOURCE_DATA& res) noexcept { switch (fmt) { case DXGI_FORMAT_NV12: case DXGI_FORMAT_P010: case DXGI_FORMAT_P016: #if (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) case DXGI_FORMAT_D16_UNORM_S8_UINT: case DXGI_FORMAT_R16_UNORM_X8_TYPELESS: case DXGI_FORMAT_X16_TYPELESS_G8_UINT: #endif if (!slicePlane) { // Plane 0 res.SlicePitch = res.RowPitch * static_cast(height); } else { // Plane 1 res.pData = static_cast(res.pData) + uintptr_t(res.RowPitch) * height; res.SlicePitch = res.RowPitch * ((static_cast(height) + 1) >> 1); } break; case DXGI_FORMAT_NV11: if (!slicePlane) { // Plane 0 res.SlicePitch = res.RowPitch * static_cast(height); } else { // Plane 1 res.pData = static_cast(res.pData) + uintptr_t(res.RowPitch) * height; res.RowPitch = (res.RowPitch >> 1); res.SlicePitch = res.RowPitch * static_cast(height); } break; default: break; } } //-------------------------------------------------------------------------------------- HRESULT FillInitData(_In_ size_t width, _In_ size_t height, _In_ size_t depth, _In_ size_t mipCount, _In_ size_t arraySize, _In_ size_t numberOfPlanes, _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, std::vector& initData) { if (!bitData) { return E_POINTER; } skipMip = 0; twidth = 0; theight = 0; tdepth = 0; size_t NumBytes = 0; size_t RowBytes = 0; const uint8_t* pEndBits = bitData + bitSize; initData.clear(); for (size_t p = 0; p < numberOfPlanes; ++p) { const uint8_t* pSrcBits = bitData; 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++) { HRESULT hr = GetSurfaceInfo(w, h, format, &NumBytes, &RowBytes, nullptr); if (FAILED(hr)) return hr; if (NumBytes > UINT32_MAX || RowBytes > UINT32_MAX) return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); if ((mipCount <= 1) || !maxsize || (w <= maxsize && h <= maxsize && d <= maxsize)) { if (!twidth) { twidth = w; theight = h; tdepth = d; } D3D12_SUBRESOURCE_DATA res = { pSrcBits, static_cast(RowBytes), static_cast(NumBytes) }; AdjustPlaneResource(format, h, p, res); initData.emplace_back(res); } 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 initData.empty() ? E_FAIL : S_OK; } //-------------------------------------------------------------------------------------- HRESULT CreateTextureResource( _In_ ID3D12Device* d3dDevice, D3D12_RESOURCE_DIMENSION resDim, size_t width, size_t height, size_t depth, size_t mipCount, size_t arraySize, DXGI_FORMAT format, D3D12_RESOURCE_FLAGS resFlags, DDS_LOADER_FLAGS loadFlags, _Outptr_ ID3D12Resource** texture) noexcept { if (!d3dDevice) return E_POINTER; HRESULT hr = E_FAIL; if (loadFlags & DDS_LOADER_FORCE_SRGB) { format = MakeSRGB(format); } D3D12_RESOURCE_DESC desc = {}; desc.Width = static_cast(width); desc.Height = static_cast(height); desc.MipLevels = static_cast(mipCount); desc.DepthOrArraySize = (resDim == D3D12_RESOURCE_DIMENSION_TEXTURE3D) ? static_cast(depth) : static_cast(arraySize); desc.Format = format; desc.Flags = resFlags; desc.SampleDesc.Count = 1; desc.SampleDesc.Quality = 0; desc.Dimension = resDim; CD3DX12_HEAP_PROPERTIES defaultHeapProperties(D3D12_HEAP_TYPE_DEFAULT); hr = d3dDevice->CreateCommittedResource( &defaultHeapProperties, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_GRAPHICS_PPV_ARGS(texture)); if (SUCCEEDED(hr)) { assert(texture != nullptr && *texture != nullptr); _Analysis_assume_(texture != nullptr && *texture != nullptr); SetDebugObjectName(*texture, L"DDSTextureLoader"); } return hr; } //-------------------------------------------------------------------------------------- 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& 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(reinterpret_cast(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(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(d3d10ext->resourceDimension)); return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); } resDim = static_cast(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(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(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( (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; } //-------------------------------------------------------------------------------------- void SetDebugTextureInfo( _In_z_ const wchar_t* fileName, _In_ ID3D12Resource** texture) noexcept { #if !defined(NO_D3D12_DEBUG_NAME) && ( defined(_DEBUG) || defined(PROFILE) ) if (texture && *texture) { const wchar_t* pstrName = wcsrchr(fileName, '\\'); if (!pstrName) { pstrName = fileName; } else { pstrName++; } (*texture)->SetName(pstrName); } #else UNREFERENCED_PARAMETER(fileName); UNREFERENCED_PARAMETER(texture); #endif } //-------------------------------------------------------------------------------------- DXGI_FORMAT GetPixelFormat(const DDS_HEADER* header) noexcept { if ((header->ddspf.flags & DDS_FOURCC) && (MAKEFOURCC('D', 'X', '1', '0') == header->ddspf.fourCC)) { auto d3d10ext = reinterpret_cast(reinterpret_cast(header) + sizeof(DDS_HEADER)); return d3d10ext->dxgiFormat; } else return GetDXGIFormat(header->ddspf); } } // anonymous namespace //-------------------------------------------------------------------------------------- _Use_decl_annotations_ HRESULT DirectX::LoadDDSTextureFromMemory( ID3D12Device* d3dDevice, const uint8_t* ddsData, size_t ddsDataSize, ID3D12Resource** texture, std::vector& subresources, size_t maxsize, DDS_ALPHA_MODE* alphaMode, bool* isCubeMap) { return LoadDDSTextureFromMemoryEx( d3dDevice, ddsData, ddsDataSize, maxsize, D3D12_RESOURCE_FLAG_NONE, DDS_LOADER_DEFAULT, texture, subresources, alphaMode, isCubeMap); } _Use_decl_annotations_ HRESULT DirectX::LoadDDSTextureFromMemoryEx( ID3D12Device* d3dDevice, const uint8_t* ddsData, size_t ddsDataSize, size_t maxsize, D3D12_RESOURCE_FLAGS resFlags, DDS_LOADER_FLAGS loadFlags, ID3D12Resource** texture, std::vector& subresources, DDS_ALPHA_MODE* alphaMode, bool* isCubeMap) { if (texture) { *texture = nullptr; } if (alphaMode) { *alphaMode = DDS_ALPHA_MODE_UNKNOWN; } if (isCubeMap) { *isCubeMap = false; } if (!d3dDevice || !ddsData || !texture) { return E_INVALIDARG; } // Validate DDS file in memory const DDS_HEADER* header = nullptr; const uint8_t* bitData = nullptr; size_t bitSize = 0; HRESULT hr = LoadTextureDataFromMemory(ddsData, ddsDataSize, &header, &bitData, &bitSize ); if (FAILED(hr)) { return hr; } hr = CreateTextureFromDDS(d3dDevice, header, bitData, bitSize, maxsize, resFlags, loadFlags, texture, subresources, isCubeMap); if (SUCCEEDED(hr)) { if (texture && *texture) { SetDebugObjectName(*texture, L"DDSTextureLoader"); } if (alphaMode) *alphaMode = GetAlphaMode(header); } return hr; } //-------------------------------------------------------------------------------------- _Use_decl_annotations_ HRESULT DirectX::LoadDDSTextureFromFile( ID3D12Device* d3dDevice, const wchar_t* fileName, ID3D12Resource** texture, std::unique_ptr& ddsData, std::vector& subresources, size_t maxsize, DDS_ALPHA_MODE* alphaMode, bool* isCubeMap) { return LoadDDSTextureFromFileEx( d3dDevice, fileName, maxsize, D3D12_RESOURCE_FLAG_NONE, DDS_LOADER_DEFAULT, texture, ddsData, subresources, alphaMode, isCubeMap); } _Use_decl_annotations_ HRESULT DirectX::LoadDDSTextureFromFileEx( ID3D12Device* d3dDevice, const wchar_t* fileName, size_t maxsize, D3D12_RESOURCE_FLAGS resFlags, DDS_LOADER_FLAGS loadFlags, ID3D12Resource** texture, std::unique_ptr& ddsData, std::vector& subresources, DDS_ALPHA_MODE* alphaMode, bool* isCubeMap) { if (texture) { *texture = nullptr; } if (alphaMode) { *alphaMode = DDS_ALPHA_MODE_UNKNOWN; } if (isCubeMap) { *isCubeMap = false; } if (!d3dDevice || !fileName || !texture) { return E_INVALIDARG; } const DDS_HEADER* header = nullptr; const uint8_t* bitData = nullptr; size_t bitSize = 0; HRESULT hr = LoadTextureDataFromFile(fileName, ddsData, &header, &bitData, &bitSize ); if (FAILED(hr)) { return hr; } hr = CreateTextureFromDDS(d3dDevice, header, bitData, bitSize, maxsize, resFlags, loadFlags, texture, subresources, isCubeMap); if (SUCCEEDED(hr)) { SetDebugTextureInfo(fileName, texture); if (alphaMode) *alphaMode = GetAlphaMode(header); } return hr; } //-------------------------------------------------------------------------------------- _Use_decl_annotations_ HRESULT DirectX::CreateDDSTextureFromMemory( ID3D12Device* d3dDevice, ResourceUploadBatch& resourceUpload, const uint8_t* ddsData, size_t ddsDataSize, ID3D12Resource** texture, bool generateMipsIfMissing, size_t maxsize, DDS_ALPHA_MODE* alphaMode, bool* isCubeMap) { return CreateDDSTextureFromMemoryEx( d3dDevice, resourceUpload, ddsData, ddsDataSize, maxsize, D3D12_RESOURCE_FLAG_NONE, generateMipsIfMissing ? DDS_LOADER_MIP_AUTOGEN : DDS_LOADER_DEFAULT, texture, alphaMode, isCubeMap); } _Use_decl_annotations_ HRESULT DirectX::CreateDDSTextureFromMemoryEx( ID3D12Device* d3dDevice, ResourceUploadBatch& resourceUpload, const uint8_t* ddsData, size_t ddsDataSize, size_t maxsize, D3D12_RESOURCE_FLAGS resFlags, DDS_LOADER_FLAGS loadFlags, ID3D12Resource** texture, DDS_ALPHA_MODE* alphaMode, bool* isCubeMap) { if (texture) { *texture = nullptr; } if (alphaMode) { *alphaMode = DDS_ALPHA_MODE_UNKNOWN; } if (isCubeMap) { *isCubeMap = false; } if (!d3dDevice || !ddsData || !texture) { return E_INVALIDARG; } // Validate DDS file in memory const DDS_HEADER* header = nullptr; const uint8_t* bitData = nullptr; size_t bitSize = 0; HRESULT hr = LoadTextureDataFromMemory(ddsData, ddsDataSize, &header, &bitData, &bitSize ); if (FAILED(hr)) { return hr; } if (loadFlags & DDS_LOADER_MIP_AUTOGEN) { DXGI_FORMAT fmt = GetPixelFormat(header); if (!resourceUpload.IsSupportedForGenerateMips(fmt)) { DebugTrace("WARNING: Autogen of mips ignored (device doesn't support this format (%d) or trying to use a copy queue)\n", static_cast(fmt)); loadFlags &= ~DDS_LOADER_MIP_AUTOGEN; } } std::vector subresources; hr = CreateTextureFromDDS(d3dDevice, header, bitData, bitSize, maxsize, resFlags, loadFlags, texture, subresources, isCubeMap); if (SUCCEEDED(hr)) { if (texture && *texture) { SetDebugObjectName(*texture, L"DDSTextureLoader"); } if (alphaMode) *alphaMode = GetAlphaMode(header); resourceUpload.Upload( *texture, 0, subresources.data(), static_cast(subresources.size())); resourceUpload.Transition( *texture, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); // If it's missing mips, let's generate them if ((loadFlags & DDS_LOADER_MIP_AUTOGEN) && subresources.size() != (*texture)->GetDesc().MipLevels) { resourceUpload.GenerateMips(*texture); } } return hr; } //-------------------------------------------------------------------------------------- _Use_decl_annotations_ HRESULT DirectX::CreateDDSTextureFromFile( ID3D12Device* d3dDevice, ResourceUploadBatch& resourceUpload, const wchar_t* fileName, ID3D12Resource** texture, bool generateMipsIfMissing, size_t maxsize, DDS_ALPHA_MODE* alphaMode, bool* isCubeMap) { return CreateDDSTextureFromFileEx( d3dDevice, resourceUpload, fileName, maxsize, D3D12_RESOURCE_FLAG_NONE, generateMipsIfMissing ? DDS_LOADER_MIP_AUTOGEN : DDS_LOADER_DEFAULT, texture, alphaMode, isCubeMap); } _Use_decl_annotations_ HRESULT DirectX::CreateDDSTextureFromFileEx( ID3D12Device* d3dDevice, ResourceUploadBatch& resourceUpload, const wchar_t* fileName, size_t maxsize, D3D12_RESOURCE_FLAGS resFlags, DDS_LOADER_FLAGS loadFlags, ID3D12Resource** texture, DDS_ALPHA_MODE* alphaMode, bool* isCubeMap) { if (texture) { *texture = nullptr; } if (alphaMode) { *alphaMode = DDS_ALPHA_MODE_UNKNOWN; } if (isCubeMap) { *isCubeMap = false; } if (!d3dDevice || !fileName || !texture) { return E_INVALIDARG; } const DDS_HEADER* header = nullptr; const 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; } if (loadFlags & DDS_LOADER_MIP_AUTOGEN) { DXGI_FORMAT fmt = GetPixelFormat(header); if (!resourceUpload.IsSupportedForGenerateMips(fmt)) { DebugTrace("WARNING: Autogen of mips ignored (device doesn't support this format (%d) or trying to use a copy queue)\n", static_cast(fmt)); loadFlags &= ~DDS_LOADER_MIP_AUTOGEN; } } std::vector subresources; hr = CreateTextureFromDDS(d3dDevice, header, bitData, bitSize, maxsize, resFlags, loadFlags, texture, subresources, isCubeMap); if (SUCCEEDED(hr)) { SetDebugTextureInfo(fileName, texture); if (alphaMode) *alphaMode = GetAlphaMode(header); resourceUpload.Upload( *texture, 0, subresources.data(), static_cast(subresources.size())); resourceUpload.Transition( *texture, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); // If it's missing mips, let's generate them if ((loadFlags & DDS_LOADER_MIP_AUTOGEN) && subresources.size() != (*texture)->GetDesc().MipLevels) { resourceUpload.GenerateMips(*texture); } } return hr; }