diff --git a/CMakeLists.txt b/CMakeLists.txt index 25b31f8..375a50c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,7 @@ project(${TARGET}) set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MD") # No idea who removed /Zi flag from the debug conf. Putting it back set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Zi /MDd") # No idea who removed /Zi flag from the debug conf. Putting it back +add_subdirectory(third_party/DirectX-Headers) # Demo for DirectX 11 @@ -17,12 +18,22 @@ add_executable(${EXE_DX11} WIN32 D3DContext.h D3DContext_DX11.cpp + DDSTextureLoader.h + DDSTextureLoader.cpp + DCompContext.h - DCompContext.cpp D3DContextBase.cpp Base.h GraphicContents.h) + DCompContext.cpp + + D3DContextBase.cpp Base.h GraphicContents.h) target_compile_definitions(${EXE_DX11} PUBLIC WINVER=0x0602 UNICODE _UNICODE USE_DX11) target_compile_features(${EXE_DX11} PUBLIC cxx_std_20) -target_link_libraries(${EXE_DX11} PUBLIC D3D11 dxgi D3DCompiler Dcomp) +target_link_libraries(${EXE_DX11} PUBLIC D3D11 dxgi dxguid D3DCompiler Dcomp) +add_custom_command( + TARGET ${EXE_DX11} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_SOURCE_DIR}/grass.dds + $) set_target_properties(${EXE_DX11} PROPERTIES @@ -40,12 +51,17 @@ add_executable(${EXE_DX12} WIN32 D3DContext.h D3DContext_DX12.cpp + DDSTextureLoader12.h + DDSTextureLoader12.cpp + DCompContext.h DCompContext.cpp D3DContextBase.cpp Base.h GraphicContents.h) -target_compile_definitions(${EXE_DX12} PUBLIC WINVER=0x0602 UNICODE _UNICODE USE_DX12) +add_dependencies(${EXE_DX12} DirectX-Headers) +target_include_directories(${EXE_DX12} PUBLIC ${DirectX-Headers_SOURCE_DIR}/include) +target_compile_definitions(${EXE_DX12} PUBLIC WINVER=0x0602 UNICODE _UNICODE USE_DX12 USING_DIRECTX_HEADERS) target_compile_features(${EXE_DX12} PUBLIC cxx_std_20) -target_link_libraries(${EXE_DX12} PUBLIC D3D12 dxgi D3DCompiler Dcomp) +target_link_libraries(${EXE_DX12} PUBLIC D3D12 dxgi D3DCompiler Dcomp DirectX-Headers DirectX-Guids) set_target_properties(${EXE_DX12} PROPERTIES diff --git a/D3DContext.h b/D3DContext.h index ca572c2..eecfe9f 100644 --- a/D3DContext.h +++ b/D3DContext.h @@ -7,7 +7,7 @@ #include #include #elif defined(USE_DX12) -#include +#include #include #else #error "You should set either USE_DX11 or USE_DX12" @@ -26,7 +26,7 @@ struct D3DContextBase : public Base { #else #error "You should set either USE_DX11 or USE_DX12" #endif - std::shared_ptr contents; + std::shared_ptr contents; IDXGIAdapter* intelAdapter = nullptr; IDXGIFactory2* dxgiFactory = nullptr; @@ -59,6 +59,7 @@ struct D3DContext : public D3DContextBase { #if defined(USE_DX11) ID3D11DeviceContext *deviceContext; IDXGISwapChain1 *swapChain; + ID3D11ShaderResourceView* imageTextureView = nullptr; void DrawTriangle(int width, int height, ID3D11Device* device, ID3D11DeviceContext* device_context, @@ -90,6 +91,9 @@ struct D3DContext : public D3DContextBase { HANDLE g_hSwapChainWaitableObject = nullptr; ID3D12Resource* g_mainRenderTargetResource[NUM_BACK_BUFFERS] = {}; D3D12_CPU_DESCRIPTOR_HANDLE g_mainRenderTargetDescriptor[NUM_BACK_BUFFERS] = {}; + ID3D12Resource* imageTextureView = nullptr; + std::unique_ptr ddsData; + std::vector subresources; private: struct DrawingCache { diff --git a/D3DContextBase.cpp b/D3DContextBase.cpp index b74eae1..e5cf339 100644 --- a/D3DContextBase.cpp +++ b/D3DContextBase.cpp @@ -87,8 +87,14 @@ void D3DContextBase::lookForIntelOutput(const RECT& position) { for (UINT i = 0; intelAdapter->EnumOutputs(i, &output) != DXGI_ERROR_NOT_FOUND; i++) { DXGI_OUTPUT_DESC outputDesc; hr_check(output->GetDesc(&outputDesc)); - if (outputDesc.Monitor != nullptr) { + if (outputDesc.Monitor != nullptr /*&& + outputDesc.DesktopCoordinates.left <= position.right && + outputDesc.DesktopCoordinates.top <= position.bottom && + outputDesc.DesktopCoordinates.right >= position.right && + outputDesc.DesktopCoordinates.bottom >= position.bottom*/) { + intelAdapterFirstOutput = output; + //std::cout << i << std::endl; break; } else { output->Release(); diff --git a/D3DContext_DX11.cpp b/D3DContext_DX11.cpp index cb9cf47..2ee3038 100644 --- a/D3DContext_DX11.cpp +++ b/D3DContext_DX11.cpp @@ -1,10 +1,14 @@ #include "D3DContext.h" +#include "DDSTextureLoader.h" #include "d3dcompiler.h" #include #include #include +#include + +using namespace DirectX; void D3DContext::DrawTriangle(int width, int height, ID3D11Device* device, @@ -16,12 +20,12 @@ void D3DContext::DrawTriangle(int width, int height, D3D11_BUFFER_DESC vb_desc; ZeroMemory(&vb_desc, sizeof(vb_desc)); - vb_desc.ByteWidth = (UINT)(vertices.size() * sizeof(Vertex)); + vb_desc.ByteWidth = (UINT)(vertices.size() * sizeof(TextureVertex)); vb_desc.Usage = D3D11_USAGE_DEFAULT; vb_desc.BindFlags = D3D11_BIND_VERTEX_BUFFER; vb_desc.CPUAccessFlags = 0; vb_desc.MiscFlags = 0; - vb_desc.StructureByteStride = sizeof(Vertex); + vb_desc.StructureByteStride = sizeof(TextureVertex); D3D11_SUBRESOURCE_DATA vb_data; ZeroMemory(&vb_data, sizeof(vb_data)); @@ -29,7 +33,7 @@ void D3DContext::DrawTriangle(int width, int height, { ID3D11Buffer *vertex_buffer; - const UINT stride = sizeof(Vertex); + const UINT stride = sizeof(TextureVertex); const UINT offset = 0; device->CreateBuffer(&vb_desc, &vb_data, &vertex_buffer); device_context->IASetVertexBuffers(0, 1, &vertex_buffer, &stride, &offset); @@ -44,14 +48,16 @@ void D3DContext::DrawTriangle(int width, int height, std::string shader_code = contents->getShader(); - hr_check(D3DCompile2(shader_code.c_str(), shader_code.length(), + auto hresult = D3DCompile2(shader_code.c_str(), shader_code.length(), nullptr, nullptr, nullptr, "PSMain", "ps_4_0", D3DCOMPILE_DEBUG, 0, 0, nullptr, 0, - &ps, &ps_error)); + &ps, &ps_error); if (ps_error != nullptr) { - throw std::runtime_error("Pixel shader compilation error"); + std::string err = "Pixel shader compilation error: "; + err += reinterpret_cast( ps_error->GetBufferPointer()); + throw std::runtime_error(err); } hr_check(device->CreatePixelShader(ps->GetBufferPointer(), ps->GetBufferSize(), nullptr, &pixel_shader)); @@ -69,11 +75,13 @@ void D3DContext::DrawTriangle(int width, int height, hr_check(device->CreateVertexShader(vs->GetBufferPointer(), vs->GetBufferSize(), nullptr, &vertex_shader)); device_context->VSSetShader(vertex_shader, nullptr, 0); + device_context->PSSetShaderResources( 0, 1, &imageTextureView ); D3D11_INPUT_ELEMENT_DESC element_desc[] = { { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, - { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 } + { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + //{ "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 } }; hr_check(device->CreateInputLayout(element_desc, sizeof(element_desc) / sizeof(D3D11_INPUT_ELEMENT_DESC), vs->GetBufferPointer(), vs->GetBufferSize(), &input_layout)); @@ -86,7 +94,7 @@ void D3DContext::DrawTriangle(int width, int height, if (ps_error != nullptr) ps_error->Release(); } - device_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + device_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);//D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); { D3D11_VIEWPORT viewport; @@ -112,9 +120,14 @@ void D3DContext::DrawTriangle(int width, int height, device_context->ClearRenderTargetView(rtv, color); device_context->OMSetRenderTargets(1, &rtv, nullptr); - device_context->Draw(3, 0); + device_context->Draw((UINT)vertices.size(), 0); + + syncIntelOutput(); - buffer->Release(); + // Discard outstanding queued presents and queue a frame with the new size ASAP. + checkDeviceRemoved(swapChain->Present(0, DXGI_PRESENT_RESTART)); + + buffer->Release(); rtv->Release(); } @@ -133,7 +146,7 @@ D3DContext::D3DContext(std::shared_ptr contents): D3DContextBas nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, D3D11_CREATE_DEVICE_BGRA_SUPPORT, nullptr, 0, D3D11_SDK_VERSION, &device, nullptr, &deviceContext)); - // Create the swap chain. + // Create the swap chain. DXGI_SWAP_CHAIN_DESC1 scd = {}; // Just use a minimal size for now. WM_NCCALCSIZE will resize when necessary. scd.Width = 1; @@ -147,6 +160,8 @@ D3DContext::D3DContext(std::shared_ptr contents): D3DContextBas scd.AlphaMode = DXGI_ALPHA_MODE_IGNORE; hr_check(dxgiFactory->CreateSwapChainForComposition(device, &scd, nullptr, &swapChain)); + hr_check(CreateDDSTextureFromFile( this->device, L"grass.dds", nullptr, &imageTextureView )); + this->reposition(getFullDisplayRECT()); } @@ -158,14 +173,10 @@ void D3DContext::reposition(const RECT& position) { // A real app might want to compare these dimensions with the current swap chain // dimensions and skip all this if they're unchanged. - checkDeviceRemoved(swapChain->ResizeBuffers(0, width, height, DXGI_FORMAT_UNKNOWN, 0)); + checkDeviceRemoved(swapChain->ResizeBuffers(0, width, height, DXGI_FORMAT_UNKNOWN, DXGI_SWAP_CHAIN_FLAG_GDI_COMPATIBLE)); DrawTriangle(width, height, device, deviceContext, swapChain, contents); - syncIntelOutput(); - - // Discard outstanding queued presents and queue a frame with the new size ASAP. - checkDeviceRemoved(swapChain->Present(0, DXGI_PRESENT_RESTART)); //Sleep(500); // Wait for a vblank to really make sure our frame with the new size is ready before // the window finishes resizing. @@ -176,6 +187,7 @@ void D3DContext::reposition(const RECT& position) { } D3DContext::~D3DContext() { + if (imageTextureView) { imageTextureView->Release(); imageTextureView = nullptr; } if (swapChain) { swapChain->SetFullscreenState(false, nullptr); swapChain->Release(); swapChain = nullptr; } if (deviceContext) { deviceContext->Release(); deviceContext = nullptr; } if (device) { device->Release(); device = nullptr; } diff --git a/D3DContext_DX12.cpp b/D3DContext_DX12.cpp index f5d8d35..d2e0b57 100644 --- a/D3DContext_DX12.cpp +++ b/D3DContext_DX12.cpp @@ -1,4 +1,5 @@ #include "D3DContext.h" +//#include "DDSTextureLoader12.h" #include @@ -6,6 +7,8 @@ #include #include +//using namespace DirectX; + // The debug layer is broken and crashes the app. Don't enable it //#ifdef _DEBUG //#define DX12_ENABLE_DEBUG_LAYER @@ -32,7 +35,7 @@ void D3DContext::DrawTriangle(int width, int height, D3D12_RESOURCE_DESC vb_desc = { .Dimension = D3D12_RESOURCE_DIMENSION_BUFFER, .Alignment = 0, - .Width = sizeof(Vertex) * vertices.size(), + .Width = sizeof(RGBAVertex) * vertices.size(), .Height = 1, .DepthOrArraySize = 1, .MipLevels = 1, @@ -66,8 +69,8 @@ void D3DContext::DrawTriangle(int width, int height, D3D12_VERTEX_BUFFER_VIEW vertex_buffer_view = { .BufferLocation = drawing_cache->vertex_buffer->GetGPUVirtualAddress(), - .SizeInBytes = static_cast(sizeof(Vertex) * vertices.size()), - .StrideInBytes = sizeof(Vertex) + .SizeInBytes = static_cast(sizeof(RGBAVertex) * vertices.size()), + .StrideInBytes = sizeof(RGBAVertex) }; ID3D12RootSignature *rootSignature; @@ -112,6 +115,7 @@ void D3DContext::DrawTriangle(int width, int height, D3D12_INPUT_ELEMENT_DESC vertexFormat[] = { {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}, + //{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, {"COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0} }; @@ -246,11 +250,12 @@ void D3DContext::DrawTriangle(int width, int height, FLOAT color[] = {0.0f, 0.2f, 0.4f, 1.0f}; graphics_command_list->ClearRenderTargetView(mainRenderTargetDescriptor, color /*clear_color_with_alpha*/, 0, nullptr); - graphics_command_list->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + graphics_command_list->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + //graphics_command_list->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);//D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); graphics_command_list->IASetVertexBuffers(0, 1, &vertex_buffer_view); // Finally drawing the bloody triangle! - graphics_command_list->DrawInstanced(3, 1, 0, 0); + graphics_command_list->DrawInstanced((UINT)vertices.size(), 1, 0, 0); barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT; @@ -276,8 +281,8 @@ bool D3DContext::CreateDeviceD3D(/*HWND hWnd*/) // Setup swap chain DXGI_SWAP_CHAIN_DESC1 scd = {}; // Just use a minimal size for now. WM_NCCALCSIZE will resize when necessary. - scd.Width = 1; - scd.Height = 1; + scd.Width = 1920; + scd.Height = 1080; scd.Format = DXGI_FORMAT_B8G8R8A8_UNORM; scd.SampleDesc.Count = 1; scd.SampleDesc.Quality = 0; @@ -390,7 +395,8 @@ bool D3DContext::CreateDeviceD3D(/*HWND hWnd*/) void D3DContext::CleanupDeviceD3D() { CleanupRenderTarget(); - if (swapChain != nullptr) { swapChain->SetFullscreenState(false, nullptr); swapChain->Release(); swapChain = nullptr; } + if (imageTextureView) { imageTextureView->Release(); imageTextureView = nullptr; } + if (swapChain != nullptr) { swapChain->SetFullscreenState(false, nullptr); swapChain->Release(); swapChain = nullptr; } if (g_hSwapChainWaitableObject != nullptr) { CloseHandle(g_hSwapChainWaitableObject); } for (auto & i : g_frameContext) { if (i.CommandAllocator) { @@ -490,6 +496,18 @@ D3DContext::FrameContext* D3DContext::WaitForNextFrameResources() D3DContext::D3DContext(std::shared_ptr contents): D3DContextBase(contents), swapChain(nullptr), descriptorHeap(nullptr) { bool_check(CreateDeviceD3D()); + +// hr_check(LoadDDSTextureFromFile( this->device, L"grass.dds", &imageTextureView, ddsData, subresources )); +// HRESULT __cdecl LoadDDSTextureFromFile( +// _In_ ID3D12Device* d3dDevice, +// _In_z_ const wchar_t* szFileName, +// _Outptr_ ID3D12Resource** texture, +// std::unique_ptr& ddsData, +// std::vector& subresources, +// size_t maxsize = 0, +// _Out_opt_ DDS_ALPHA_MODE* alphaMode = nullptr, +// _Out_opt_ bool* isCubeMap = nullptr); + this->reposition(getFullDisplayRECT()); } @@ -498,9 +516,8 @@ void D3DContext::reposition(const RECT& position) { int height = position.bottom - position.top; lookForIntelOutput(position); - CleanupRenderTarget(); - checkDeviceRemoved(swapChain->ResizeBuffers(0, width, height, DXGI_FORMAT_UNKNOWN, 0/*DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT*/)); + checkDeviceRemoved(swapChain->ResizeBuffers(0, width, height, DXGI_FORMAT_UNKNOWN, DXGI_SWAP_CHAIN_FLAG_GDI_COMPATIBLE));//0/*DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT*/)); CreateRenderTarget(); FrameContext* frameCtx = WaitForNextFrameResources(); diff --git a/DDSTextureLoader.cpp b/DDSTextureLoader.cpp new file mode 100644 index 0000000..214ef96 --- /dev/null +++ b/DDSTextureLoader.cpp @@ -0,0 +1,2064 @@ +//-------------------------------------------------------------------------------------- +// 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. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248926 +// http://go.microsoft.com/fwlink/?LinkId=248929 +//-------------------------------------------------------------------------------------- + +#include "DDSTextureLoader.h" + +#include +#include +#include + +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wcovered-switch-default" +#pragma clang diagnostic ignored "-Wswitch-enum" +#endif + +using namespace DirectX; + +//-------------------------------------------------------------------------------------- +// Macros +//-------------------------------------------------------------------------------------- +#ifndef MAKEFOURCC +#define MAKEFOURCC(ch0, ch1, ch2, ch3) \ + ((uint32_t)(uint8_t)(ch0) | ((uint32_t)(uint8_t)(ch1) << 8) | \ + ((uint32_t)(uint8_t)(ch2) << 16) | ((uint32_t)(uint8_t)(ch3) << 24 )) +#endif /* defined(MAKEFOURCC) */ + +//-------------------------------------------------------------------------------------- +// DDS file structure definitions +// +// See DDS.h in the 'Texconv' sample and the 'DirectXTex' library +//-------------------------------------------------------------------------------------- +#pragma pack(push,1) + +constexpr uint32_t DDS_MAGIC = 0x20534444; // "DDS " + +struct DDS_PIXELFORMAT +{ + uint32_t size; + uint32_t flags; + uint32_t fourCC; + uint32_t RGBBitCount; + uint32_t RBitMask; + uint32_t GBitMask; + uint32_t BBitMask; + uint32_t ABitMask; +}; + +#define DDS_FOURCC 0x00000004 // DDPF_FOURCC +#define DDS_RGB 0x00000040 // DDPF_RGB +#define DDS_LUMINANCE 0x00020000 // DDPF_LUMINANCE +#define DDS_ALPHA 0x00000002 // DDPF_ALPHA +#define DDS_BUMPDUDV 0x00080000 // DDPF_BUMPDUDV + +#define DDS_HEADER_FLAGS_VOLUME 0x00800000 // DDSD_DEPTH + +#define DDS_HEIGHT 0x00000002 // DDSD_HEIGHT + +#define DDS_CUBEMAP_POSITIVEX 0x00000600 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEX +#define DDS_CUBEMAP_NEGATIVEX 0x00000a00 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEX +#define DDS_CUBEMAP_POSITIVEY 0x00001200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEY +#define DDS_CUBEMAP_NEGATIVEY 0x00002200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEY +#define DDS_CUBEMAP_POSITIVEZ 0x00004200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEZ +#define DDS_CUBEMAP_NEGATIVEZ 0x00008200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEZ + +#define DDS_CUBEMAP_ALLFACES ( DDS_CUBEMAP_POSITIVEX | DDS_CUBEMAP_NEGATIVEX |\ + DDS_CUBEMAP_POSITIVEY | DDS_CUBEMAP_NEGATIVEY |\ + DDS_CUBEMAP_POSITIVEZ | DDS_CUBEMAP_NEGATIVEZ ) + +#define DDS_CUBEMAP 0x00000200 // DDSCAPS2_CUBEMAP + +enum DDS_MISC_FLAGS2 +{ + DDS_MISC_FLAGS2_ALPHA_MODE_MASK = 0x7L, +}; + +struct DDS_HEADER +{ + uint32_t size; + uint32_t flags; + uint32_t height; + uint32_t width; + uint32_t pitchOrLinearSize; + uint32_t depth; // only if DDS_HEADER_FLAGS_VOLUME is set in flags + uint32_t mipMapCount; + uint32_t reserved1[11]; + DDS_PIXELFORMAT ddspf; + uint32_t caps; + uint32_t caps2; + uint32_t caps3; + uint32_t caps4; + uint32_t reserved2; +}; + +struct DDS_HEADER_DXT10 +{ + DXGI_FORMAT dxgiFormat; + uint32_t resourceDimension; + uint32_t miscFlag; // see D3D11_RESOURCE_MISC_FLAG + uint32_t arraySize; + uint32_t miscFlags2; +}; + +#pragma pack(pop) + +//-------------------------------------------------------------------------------------- +namespace +{ + struct handle_closer { void operator()(HANDLE h) noexcept { if (h) CloseHandle(h); } }; + + using ScopedHandle = std::unique_ptr; + + inline HANDLE safe_handle(HANDLE h) noexcept { return (h == INVALID_HANDLE_VALUE) ? nullptr : h; } + +#if defined(_DEBUG) || defined(PROFILE) + template + inline void SetDebugObjectName(_In_ ID3D11DeviceChild* resource, _In_ const char(&name)[TNameLength]) noexcept + { + resource->SetPrivateData(WKPDID_D3DDebugObjectName, TNameLength - 1, name); + } +#else + template + inline void SetDebugObjectName(_In_ ID3D11DeviceChild*, _In_ const char(&)[TNameLength]) noexcept + { + } +#endif + + //-------------------------------------------------------------------------------------- + HRESULT LoadTextureDataFromMemory( + _In_reads_(ddsDataSize) const uint8_t* ddsData, + size_t ddsDataSize, + const DDS_HEADER** header, + const uint8_t** bitData, + size_t* bitSize) noexcept + { + if (!header || !bitData || !bitSize) + { + return E_POINTER; + } + + *bitSize = 0; + + if (ddsDataSize > UINT32_MAX) + { + return E_FAIL; + } + + if (ddsDataSize < (sizeof(uint32_t) + sizeof(DDS_HEADER))) + { + return E_FAIL; + } + + // DDS files always start with the same magic number ("DDS ") + auto const dwMagicNumber = *reinterpret_cast(ddsData); + if (dwMagicNumber != DDS_MAGIC) + { + return E_FAIL; + } + + auto hdr = reinterpret_cast(ddsData + 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 (ddsDataSize < (sizeof(uint32_t) + sizeof(DDS_HEADER) + sizeof(DDS_HEADER_DXT10))) + { + return E_FAIL; + } + + bDXT10Header = true; + } + + // setup the pointers in the process request + *header = hdr; + auto offset = sizeof(uint32_t) + + sizeof(DDS_HEADER) + + (bDXT10Header ? sizeof(DDS_HEADER_DXT10) : 0u); + *bitData = ddsData + offset; + *bitSize = ddsDataSize - offset; + + return S_OK; + } + + + //-------------------------------------------------------------------------------------- + HRESULT LoadTextureDataFromFile( + _In_z_ const wchar_t* fileName, + std::unique_ptr& ddsData, + const DDS_HEADER** header, + const uint8_t** bitData, + size_t* bitSize) noexcept + { + if (!header || !bitData || !bitSize) + { + return E_POINTER; + } + + *bitSize = 0; + + // 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 + FILE_STANDARD_INFO fileInfo; + if (!GetFileInformationByHandleEx(hFile.get(), FileStandardInfo, &fileInfo, sizeof(fileInfo))) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + // File is too big for 32-bit allocation, so reject read + if (fileInfo.EndOfFile.HighPart > 0) + { + return E_FAIL; + } + + // Need at least enough data to fill the header and magic number to be a valid DDS + if (fileInfo.EndOfFile.LowPart < (sizeof(uint32_t) + sizeof(DDS_HEADER))) + { + return E_FAIL; + } + + // create enough space for the file data + ddsData.reset(new (std::nothrow) uint8_t[fileInfo.EndOfFile.LowPart]); + if (!ddsData) + { + return E_OUTOFMEMORY; + } + + // read the data in + DWORD bytesRead = 0; + if (!ReadFile(hFile.get(), + ddsData.get(), + fileInfo.EndOfFile.LowPart, + &bytesRead, + nullptr + )) + { + ddsData.reset(); + return HRESULT_FROM_WIN32(GetLastError()); + } + + if (bytesRead < fileInfo.EndOfFile.LowPart) + { + ddsData.reset(); + return E_FAIL; + } + + // DDS files always start with the same magic number ("DDS ") + auto const dwMagicNumber = *reinterpret_cast(ddsData.get()); + if (dwMagicNumber != DDS_MAGIC) + { + ddsData.reset(); + 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)) + { + ddsData.reset(); + 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 (fileInfo.EndOfFile.LowPart < (sizeof(uint32_t) + sizeof(DDS_HEADER) + sizeof(DDS_HEADER_DXT10))) + { + ddsData.reset(); + return E_FAIL; + } + + bDXT10Header = true; + } + + // setup the pointers in the process request + *header = hdr; + auto offset = sizeof(uint32_t) + sizeof(DDS_HEADER) + + (bDXT10Header ? sizeof(DDS_HEADER_DXT10) : 0u); + *bitData = ddsData.get() + offset; + *bitSize = fileInfo.EndOfFile.LowPart - offset; + + return S_OK; + } + + + //-------------------------------------------------------------------------------------- + // Return the BPP for a particular format + //-------------------------------------------------------------------------------------- + size_t BitsPerPixel(_In_ DXGI_FORMAT fmt) noexcept + { + 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_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: + 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; + + default: + return 0; + } + } + + + //-------------------------------------------------------------------------------------- + // Get surface information for a particular format + //-------------------------------------------------------------------------------------- + HRESULT GetSurfaceInfo( + _In_ size_t width, + _In_ size_t height, + _In_ DXGI_FORMAT fmt, + size_t* outNumBytes, + _Out_opt_ size_t* outRowBytes, + _Out_opt_ size_t* outNumRows) noexcept + { + uint64_t numBytes = 0; + uint64_t rowBytes = 0; + uint64_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: + if ((height % 2) != 0) + { + // Requires a height alignment of 2. + return E_INVALIDARG; + } + planar = true; + bpe = 2; + break; + + case DXGI_FORMAT_P010: + case DXGI_FORMAT_P016: + if ((height % 2) != 0) + { + // Requires a height alignment of 2. + return E_INVALIDARG; + } + planar = true; + bpe = 4; + break; + + default: + break; + } + + if (bc) + { + uint64_t numBlocksWide = 0; + if (width > 0) + { + numBlocksWide = std::max(1u, (uint64_t(width) + 3u) / 4u); + } + uint64_t numBlocksHigh = 0; + if (height > 0) + { + numBlocksHigh = std::max(1u, (uint64_t(height) + 3u) / 4u); + } + rowBytes = numBlocksWide * bpe; + numRows = numBlocksHigh; + numBytes = rowBytes * numBlocksHigh; + } + else if (packed) + { + rowBytes = ((uint64_t(width) + 1u) >> 1) * bpe; + numRows = uint64_t(height); + numBytes = rowBytes * height; + } + else if (fmt == DXGI_FORMAT_NV11) + { + rowBytes = ((uint64_t(width) + 3u) >> 2) * 4u; + numRows = uint64_t(height) * 2u; // Direct3D makes this simplifying assumption, although it is larger than the 4:1:1 data + numBytes = rowBytes * numRows; + } + else if (planar) + { + rowBytes = ((uint64_t(width) + 1u) >> 1) * bpe; + numBytes = (rowBytes * uint64_t(height)) + ((rowBytes * uint64_t(height) + 1u) >> 1); + numRows = height + ((uint64_t(height) + 1u) >> 1); + } + else + { + const size_t bpp = BitsPerPixel(fmt); + if (!bpp) + return E_INVALIDARG; + + rowBytes = (uint64_t(width) * bpp + 7u) / 8u; // round up to nearest byte + numRows = uint64_t(height); + numBytes = rowBytes * height; + } + +#if defined(_M_IX86) || defined(_M_ARM) || defined(_M_HYBRID_X86_ARM64) + static_assert(sizeof(size_t) == 4, "Not a 32-bit platform!"); + if (numBytes > UINT32_MAX || rowBytes > UINT32_MAX || numRows > UINT32_MAX) + return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); +#else + static_assert(sizeof(size_t) == 8, "Not a 64-bit platform!"); +#endif + + if (outNumBytes) + { + *outNumBytes = static_cast(numBytes); + } + if (outRowBytes) + { + *outRowBytes = static_cast(rowBytes); + } + if (outNumRows) + { + *outNumRows = static_cast(numRows); + } + + return S_OK; + } + + + //-------------------------------------------------------------------------------------- +#define ISBITMASK( r,g,b,a ) ( ddpf.RBitMask == r && ddpf.GBitMask == g && ddpf.BBitMask == b && ddpf.ABitMask == a ) + + DXGI_FORMAT GetDXGIFormat(const DDS_PIXELFORMAT& ddpf) noexcept + { + 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, 0)) + { + return DXGI_FORMAT_B8G8R8X8_UNORM; + } + + // No DXGI format maps to ISBITMASK(0x000000ff,0x0000ff00,0x00ff0000,0) 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, 0, 0)) + { + return DXGI_FORMAT_R16G16_UNORM; + } + + if (ISBITMASK(0xffffffff, 0, 0, 0)) + { + // 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, 0)) + { + return DXGI_FORMAT_B5G6R5_UNORM; + } + + // No DXGI format maps to ISBITMASK(0x7c00,0x03e0,0x001f,0) aka D3DFMT_X1R5G5B5 + + if (ISBITMASK(0x0f00, 0x00f0, 0x000f, 0xf000)) + { + return DXGI_FORMAT_B4G4R4A4_UNORM; + } + + // NVTT versions 1.x wrote this as RGB instead of LUMINANCE + if (ISBITMASK(0x00ff, 0, 0, 0xff00)) + { + return DXGI_FORMAT_R8G8_UNORM; + } + if (ISBITMASK(0xffff, 0, 0, 0)) + { + return DXGI_FORMAT_R16_UNORM; + } + + // No DXGI format maps to ISBITMASK(0x0f00,0x00f0,0x000f,0) aka D3DFMT_X4R4G4B4 + + // No 3:3:2:8 or paletted DXGI formats aka D3DFMT_A8R3G3B2, D3DFMT_A8P8, etc. + break; + + case 8: + // NVTT versions 1.x wrote this as RGB instead of LUMINANCE + if (ISBITMASK(0xff, 0, 0, 0)) + { + return DXGI_FORMAT_R8_UNORM; + } + + // No 3:3:2 or paletted DXGI formats aka D3DFMT_R3G3B2, D3DFMT_P8 + break; + } + } + else if (ddpf.flags & DDS_LUMINANCE) + { + switch (ddpf.RGBBitCount) + { + case 16: + if (ISBITMASK(0xffff, 0, 0, 0)) + { + return DXGI_FORMAT_R16_UNORM; // D3DX10/11 writes this out as DX10 extension + } + if (ISBITMASK(0x00ff, 0, 0, 0xff00)) + { + return DXGI_FORMAT_R8G8_UNORM; // D3DX10/11 writes this out as DX10 extension + } + break; + + case 8: + if (ISBITMASK(0xff, 0, 0, 0)) + { + return DXGI_FORMAT_R8_UNORM; // D3DX10/11 writes this out as DX10 extension + } + + // No DXGI format maps to ISBITMASK(0x0f,0,0,0xf0) aka D3DFMT_A4L4 + + if (ISBITMASK(0x00ff, 0, 0, 0xff00)) + { + return DXGI_FORMAT_R8G8_UNORM; // Some DDS writers assume the bitcount should be 8 instead of 16 + } + break; + } + } + else if (ddpf.flags & DDS_ALPHA) + { + if (8 == ddpf.RGBBitCount) + { + return DXGI_FORMAT_A8_UNORM; + } + } + else if (ddpf.flags & DDS_BUMPDUDV) + { + switch (ddpf.RGBBitCount) + { + case 32: + if (ISBITMASK(0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000)) + { + return DXGI_FORMAT_R8G8B8A8_SNORM; // D3DX10/11 writes this out as DX10 extension + } + if (ISBITMASK(0x0000ffff, 0xffff0000, 0, 0)) + { + 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 + break; + + case 16: + if (ISBITMASK(0x00ff, 0xff00, 0, 0)) + { + return DXGI_FORMAT_R8G8_SNORM; // D3DX10/11 writes this out as DX10 extension + } + break; + } + + // No DXGI format maps to DDPF_BUMPLUMINANCE aka D3DFMT_L6V5U5, D3DFMT_X8L8V8U8 + } + 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; + + // No DXGI format maps to D3DFMT_CxV8U8 + } + } + + return DXGI_FORMAT_UNKNOWN; + } + +#undef ISBITMASK + + + //-------------------------------------------------------------------------------------- + DXGI_FORMAT MakeSRGB(_In_ DXGI_FORMAT format) noexcept + { + 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; + } + } + + + //-------------------------------------------------------------------------------------- + inline DXGI_FORMAT MakeLinear(_In_ DXGI_FORMAT format) noexcept + { + switch (format) + { + case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: + return DXGI_FORMAT_R8G8B8A8_UNORM; + + case DXGI_FORMAT_BC1_UNORM_SRGB: + return DXGI_FORMAT_BC1_UNORM; + + case DXGI_FORMAT_BC2_UNORM_SRGB: + return DXGI_FORMAT_BC2_UNORM; + + case DXGI_FORMAT_BC3_UNORM_SRGB: + return DXGI_FORMAT_BC3_UNORM; + + case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: + return DXGI_FORMAT_B8G8R8A8_UNORM; + + case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: + return DXGI_FORMAT_B8G8R8X8_UNORM; + + case DXGI_FORMAT_BC7_UNORM_SRGB: + return DXGI_FORMAT_BC7_UNORM; + + default: + return format; + } + } + + + //-------------------------------------------------------------------------------------- + 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) noexcept + { + 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++) + { + 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; + } + + assert(index < mipCount * arraySize); + _Analysis_assume_(index < mipCount * arraySize); + initData[index].pSysMem = 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; + } + + + //-------------------------------------------------------------------------------------- + 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_ DDS_LOADER_FLAGS loadFlags, + _In_ bool isCubeMap, + _In_reads_opt_(mipCount*arraySize) const D3D11_SUBRESOURCE_DATA* initData, + _Outptr_opt_ ID3D11Resource** texture, + _Outptr_opt_ ID3D11ShaderResourceView** textureView) noexcept + { + if (!d3dDevice) + return E_POINTER; + + HRESULT hr = E_FAIL; + + if (loadFlags & DDS_LOADER_FORCE_SRGB) + { + format = MakeSRGB(format); + } + else if (loadFlags & DDS_LOADER_IGNORE_SRGB) + { + format = MakeLinear(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 & ~static_cast(D3D11_RESOURCE_MISC_TEXTURECUBE); + + ID3D11Texture1D* tex = nullptr; + hr = d3dDevice->CreateTexture1D(&desc, + initData, + &tex + ); + if (SUCCEEDED(hr) && tex) + { + if (textureView) + { + D3D11_SHADER_RESOURCE_VIEW_DESC SRVDesc = {}; + SRVDesc.Format = format; + + if (arraySize > 1) + { + SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE1DARRAY; + SRVDesc.Texture1DArray.MipLevels = (!mipCount) ? UINT(-1) : desc.MipLevels; + SRVDesc.Texture1DArray.ArraySize = static_cast(arraySize); + } + else + { + SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE1D; + SRVDesc.Texture1D.MipLevels = (!mipCount) ? UINT(-1) : desc.MipLevels; + } + + hr = d3dDevice->CreateShaderResourceView(tex, + &SRVDesc, + textureView + ); + if (FAILED(hr)) + { + tex->Release(); + return hr; + } + } + + if (texture) + { + *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 & ~static_cast(D3D11_RESOURCE_MISC_TEXTURECUBE); + } + + ID3D11Texture2D* tex = nullptr; + hr = d3dDevice->CreateTexture2D(&desc, + initData, + &tex + ); + if (SUCCEEDED(hr) && tex) + { + if (textureView) + { + D3D11_SHADER_RESOURCE_VIEW_DESC SRVDesc = {}; + SRVDesc.Format = format; + + if (isCubeMap) + { + if (arraySize > 6) + { + SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBEARRAY; + SRVDesc.TextureCubeArray.MipLevels = (!mipCount) ? UINT(-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) ? UINT(-1) : desc.MipLevels; + } + } + else if (arraySize > 1) + { + SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY; + SRVDesc.Texture2DArray.MipLevels = (!mipCount) ? UINT(-1) : desc.MipLevels; + SRVDesc.Texture2DArray.ArraySize = static_cast(arraySize); + } + else + { + SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; + SRVDesc.Texture2D.MipLevels = (!mipCount) ? UINT(-1) : desc.MipLevels; + } + + hr = d3dDevice->CreateShaderResourceView(tex, + &SRVDesc, + textureView + ); + if (FAILED(hr)) + { + tex->Release(); + return hr; + } + } + + if (texture) + { + *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 & ~static_cast(D3D11_RESOURCE_MISC_TEXTURECUBE); + + ID3D11Texture3D* tex = nullptr; + hr = d3dDevice->CreateTexture3D(&desc, + initData, + &tex + ); + if (SUCCEEDED(hr) && tex) + { + if (textureView) + { + D3D11_SHADER_RESOURCE_VIEW_DESC SRVDesc = {}; + SRVDesc.Format = format; + + SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D; + SRVDesc.Texture3D.MipLevels = (!mipCount) ? UINT(-1) : desc.MipLevels; + + hr = d3dDevice->CreateShaderResourceView(tex, + &SRVDesc, + textureView + ); + if (FAILED(hr)) + { + tex->Release(); + return hr; + } + } + + if (texture) + { + *texture = tex; + } + else + { + SetDebugObjectName(tex, "DDSTextureLoader"); + tex->Release(); + } + } + } + break; + } + + return hr; + } + + //-------------------------------------------------------------------------------------- + HRESULT CreateTextureFromDDS( + _In_ ID3D11Device* d3dDevice, + _In_opt_ ID3D11DeviceContext* d3dContext, + _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_ DDS_LOADER_FLAGS loadFlags, + _Outptr_opt_ ID3D11Resource** texture, + _Outptr_opt_ ID3D11ShaderResourceView** textureView) noexcept + { + HRESULT hr = S_OK; + + const 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(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_NV12: + case DXGI_FORMAT_P010: + case DXGI_FORMAT_P016: + case DXGI_FORMAT_420_OPAQUE: + if ((d3d10ext->resourceDimension != D3D11_RESOURCE_DIMENSION_TEXTURE2D) + || (width % 2) != 0 || (height % 2) != 0) + { + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + break; + + case DXGI_FORMAT_YUY2: + case DXGI_FORMAT_Y210: + case DXGI_FORMAT_Y216: + case DXGI_FORMAT_P208: + if ((width % 2) != 0) + { + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + break; + + case DXGI_FORMAT_NV11: + if ((width % 4) != 0) + { + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + break; + + 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 && textureView) // 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 (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, + loadFlags, + isCubeMap, + nullptr, + &tex, textureView); + if (SUCCEEDED(hr)) + { + size_t numBytes = 0; + size_t rowBytes = 0; + hr = GetSurfaceInfo(width, height, format, &numBytes, &rowBytes, nullptr); + if (FAILED(hr)) + return hr; + + if (numBytes > bitSize) + { + (*textureView)->Release(); + *textureView = nullptr; + tex->Release(); + return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF); + } + + if (numBytes > UINT32_MAX || rowBytes > UINT32_MAX) + return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); + + 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 (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); + } + + const 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)); + } + + 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, + loadFlags, + 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 = 512u /*D3D_FL9_1_REQ_TEXTURECUBE_DIMENSION*/; + } + else + { + maxsize = (resDim == D3D11_RESOURCE_DIMENSION_TEXTURE3D) + ? 256u /*D3D_FL9_1_REQ_TEXTURE3D_U_V_OR_W_DIMENSION*/ + : 2048u /*D3D_FL9_1_REQ_TEXTURE2D_U_OR_V_DIMENSION*/; + } + break; + + case D3D_FEATURE_LEVEL_9_3: + maxsize = (resDim == D3D11_RESOURCE_DIMENSION_TEXTURE3D) + ? 256u /*D3D_FL9_1_REQ_TEXTURE3D_U_V_OR_W_DIMENSION*/ + : 4096u /*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) + ? 2048u /*D3D10_REQ_TEXTURE3D_U_V_OR_W_DIMENSION*/ + : 8192u /*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, + loadFlags, + isCubeMap, + initData.get(), + texture, textureView); + } + } + } + } + + return hr; + } + + + //-------------------------------------------------------------------------------------- + DDS_ALPHA_MODE GetAlphaMode(_In_ const DDS_HEADER* header) noexcept + { + if (header->ddspf.flags & DDS_FOURCC) + { + if (MAKEFOURCC('D', 'X', '1', '0') == header->ddspf.fourCC) + { + auto d3d10ext = reinterpret_cast(reinterpret_cast(header) + sizeof(DDS_HEADER)); + auto const 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; + + case DDS_ALPHA_MODE_UNKNOWN: + default: + break; + } + } + 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; + } + + //-------------------------------------------------------------------------------------- + void SetDebugTextureInfo( + _In_z_ const wchar_t* fileName, + _In_opt_ ID3D11Resource** texture, + _In_opt_ ID3D11ShaderResourceView** textureView) noexcept + { +#if !defined(NO_D3D11_DEBUG_NAME) && ( defined(_DEBUG) || defined(PROFILE) ) + if (texture || textureView) + { + CHAR strFileA[MAX_PATH]; + const int result = WideCharToMultiByte(CP_UTF8, + WC_NO_BEST_FIT_CHARS, + fileName, + -1, + strFileA, + MAX_PATH, + nullptr, + nullptr + ); + if (result > 0) + { + const char* pstrName = strrchr(strFileA, '\\'); + if (!pstrName) + { + pstrName = strFileA; + } + else + { + pstrName++; + } + + if (texture && *texture) + { + (*texture)->SetPrivateData(WKPDID_D3DDebugObjectName, + static_cast(strnlen_s(pstrName, MAX_PATH)), + pstrName + ); + } + + if (textureView && *textureView) + { + (*textureView)->SetPrivateData(WKPDID_D3DDebugObjectName, + static_cast(strnlen_s(pstrName, MAX_PATH)), + pstrName + ); + } + } + } +#else + UNREFERENCED_PARAMETER(fileName); + UNREFERENCED_PARAMETER(texture); + UNREFERENCED_PARAMETER(textureView); +#endif + } +} // anonymous namespace + +//-------------------------------------------------------------------------------------- +_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) noexcept +{ + return CreateDDSTextureFromMemoryEx(d3dDevice, nullptr, + ddsData, ddsDataSize, + maxsize, + D3D11_USAGE_DEFAULT, D3D11_BIND_SHADER_RESOURCE, 0, 0, + DDS_LOADER_DEFAULT, + texture, textureView, alphaMode); +} + +_Use_decl_annotations_ +HRESULT DirectX::CreateDDSTextureFromMemory( + ID3D11Device* d3dDevice, + ID3D11DeviceContext* d3dContext, + const uint8_t* ddsData, + size_t ddsDataSize, + ID3D11Resource** texture, + ID3D11ShaderResourceView** textureView, + size_t maxsize, + DDS_ALPHA_MODE* alphaMode) noexcept +{ + return CreateDDSTextureFromMemoryEx(d3dDevice, d3dContext, + ddsData, ddsDataSize, + maxsize, + D3D11_USAGE_DEFAULT, D3D11_BIND_SHADER_RESOURCE, 0, 0, + DDS_LOADER_DEFAULT, + 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, + DDS_LOADER_FLAGS loadFlags, + ID3D11Resource** texture, + ID3D11ShaderResourceView** textureView, + DDS_ALPHA_MODE* alphaMode) noexcept +{ + return CreateDDSTextureFromMemoryEx(d3dDevice, nullptr, + ddsData, ddsDataSize, + maxsize, + usage, bindFlags, cpuAccessFlags, miscFlags, + loadFlags, + texture, textureView, alphaMode); +} + +_Use_decl_annotations_ +HRESULT DirectX::CreateDDSTextureFromMemoryEx( + ID3D11Device* d3dDevice, + ID3D11DeviceContext* d3dContext, + const uint8_t* ddsData, + size_t ddsDataSize, + size_t maxsize, + D3D11_USAGE usage, + unsigned int bindFlags, + unsigned int cpuAccessFlags, + unsigned int miscFlags, + DDS_LOADER_FLAGS loadFlags, + ID3D11Resource** texture, + ID3D11ShaderResourceView** textureView, + DDS_ALPHA_MODE* alphaMode) noexcept +{ + if (texture) + { + *texture = nullptr; + } + if (textureView) + { + *textureView = nullptr; + } + if (alphaMode) + { + *alphaMode = DDS_ALPHA_MODE_UNKNOWN; + } + + if (!d3dDevice || !ddsData || (!texture && !textureView)) + { + return E_INVALIDARG; + } + + if (textureView && !(bindFlags & D3D11_BIND_SHADER_RESOURCE)) + { + 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, d3dContext, + header, bitData, bitSize, + maxsize, + usage, bindFlags, cpuAccessFlags, miscFlags, + loadFlags, + texture, textureView); + if (SUCCEEDED(hr)) + { + if (texture && *texture) + { + SetDebugObjectName(*texture, "DDSTextureLoader"); + } + + if (textureView && *textureView) + { + 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) noexcept +{ + return CreateDDSTextureFromFileEx(d3dDevice, nullptr, + fileName, maxsize, + D3D11_USAGE_DEFAULT, D3D11_BIND_SHADER_RESOURCE, 0, 0, + DDS_LOADER_DEFAULT, + texture, textureView, alphaMode); +} + +_Use_decl_annotations_ +HRESULT DirectX::CreateDDSTextureFromFile( + ID3D11Device* d3dDevice, + ID3D11DeviceContext* d3dContext, + const wchar_t* fileName, + ID3D11Resource** texture, + ID3D11ShaderResourceView** textureView, + size_t maxsize, + DDS_ALPHA_MODE* alphaMode) noexcept +{ + return CreateDDSTextureFromFileEx(d3dDevice, d3dContext, + fileName, + maxsize, + D3D11_USAGE_DEFAULT, D3D11_BIND_SHADER_RESOURCE, 0, 0, + DDS_LOADER_DEFAULT, + 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, + DDS_LOADER_FLAGS loadFlags, + ID3D11Resource** texture, + ID3D11ShaderResourceView** textureView, + DDS_ALPHA_MODE* alphaMode) noexcept +{ + return CreateDDSTextureFromFileEx(d3dDevice, nullptr, + fileName, + maxsize, + usage, bindFlags, cpuAccessFlags, miscFlags, + loadFlags, + texture, textureView, alphaMode); +} + +_Use_decl_annotations_ +HRESULT DirectX::CreateDDSTextureFromFileEx( + ID3D11Device* d3dDevice, + ID3D11DeviceContext* d3dContext, + const wchar_t* fileName, + size_t maxsize, + D3D11_USAGE usage, + unsigned int bindFlags, + unsigned int cpuAccessFlags, + unsigned int miscFlags, + DDS_LOADER_FLAGS loadFlags, + ID3D11Resource** texture, + ID3D11ShaderResourceView** textureView, + DDS_ALPHA_MODE* alphaMode) noexcept +{ + if (texture) + { + *texture = nullptr; + } + if (textureView) + { + *textureView = nullptr; + } + if (alphaMode) + { + *alphaMode = DDS_ALPHA_MODE_UNKNOWN; + } + + if (!d3dDevice || !fileName || (!texture && !textureView)) + { + return E_INVALIDARG; + } + + if (textureView && !(bindFlags & D3D11_BIND_SHADER_RESOURCE)) + { + 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; + } + + hr = CreateTextureFromDDS(d3dDevice, d3dContext, + header, bitData, bitSize, + maxsize, + usage, bindFlags, cpuAccessFlags, miscFlags, + loadFlags, + texture, textureView); + + if (SUCCEEDED(hr)) + { + SetDebugTextureInfo(fileName, texture, textureView); + + if (alphaMode) + *alphaMode = GetAlphaMode(header); + } + + return hr; +} \ No newline at end of file diff --git a/DDSTextureLoader.h b/DDSTextureLoader.h new file mode 100644 index 0000000..d476205 --- /dev/null +++ b/DDSTextureLoader.h @@ -0,0 +1,155 @@ +//-------------------------------------------------------------------------------------- +// File: DDSTextureLoader.h +// +// 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. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248926 +// http://go.microsoft.com/fwlink/?LinkId=248929 +//-------------------------------------------------------------------------------------- + +#pragma once + +#include + +#include + + +namespace DirectX +{ +#ifndef DDS_ALPHA_MODE_DEFINED +#define DDS_ALPHA_MODE_DEFINED + enum DDS_ALPHA_MODE : uint32_t + { + DDS_ALPHA_MODE_UNKNOWN = 0, + DDS_ALPHA_MODE_STRAIGHT = 1, + DDS_ALPHA_MODE_PREMULTIPLIED = 2, + DDS_ALPHA_MODE_OPAQUE = 3, + DDS_ALPHA_MODE_CUSTOM = 4, + }; +#endif + +#ifndef DDS_LOADER_FLAGS_DEFINED +#define DDS_LOADER_FLAGS_DEFINED + + enum DDS_LOADER_FLAGS : uint32_t + { + DDS_LOADER_DEFAULT = 0, + DDS_LOADER_FORCE_SRGB = 0x1, + DDS_LOADER_IGNORE_SRGB = 0x2, + }; + +#ifdef __clang__ + #pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-dynamic-exception-spec" +#endif + + DEFINE_ENUM_FLAG_OPERATORS(DDS_LOADER_FLAGS); + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +#endif + + // Standard version + HRESULT CreateDDSTextureFromMemory( + _In_ ID3D11Device* d3dDevice, + _In_reads_bytes_(ddsDataSize) const uint8_t* ddsData, + _In_ size_t ddsDataSize, + _Outptr_opt_ ID3D11Resource** texture, + _Outptr_opt_ ID3D11ShaderResourceView** textureView, + _In_ size_t maxsize = 0, + _Out_opt_ DDS_ALPHA_MODE* alphaMode = nullptr) noexcept; + + HRESULT CreateDDSTextureFromFile( + _In_ ID3D11Device* d3dDevice, + _In_z_ const wchar_t* szFileName, + _Outptr_opt_ ID3D11Resource** texture, + _Outptr_opt_ ID3D11ShaderResourceView** textureView, + _In_ size_t maxsize = 0, + _Out_opt_ DDS_ALPHA_MODE* alphaMode = nullptr) noexcept; + + // Standard version with optional auto-gen mipmap support + HRESULT CreateDDSTextureFromMemory( + _In_ ID3D11Device* d3dDevice, + _In_opt_ ID3D11DeviceContext* d3dContext, + _In_reads_bytes_(ddsDataSize) const uint8_t* ddsData, + _In_ size_t ddsDataSize, + _Outptr_opt_ ID3D11Resource** texture, + _Outptr_opt_ ID3D11ShaderResourceView** textureView, + _In_ size_t maxsize = 0, + _Out_opt_ DDS_ALPHA_MODE* alphaMode = nullptr) noexcept; + + HRESULT CreateDDSTextureFromFile( + _In_ ID3D11Device* d3dDevice, + _In_opt_ ID3D11DeviceContext* d3dContext, + _In_z_ const wchar_t* szFileName, + _Outptr_opt_ ID3D11Resource** texture, + _Outptr_opt_ ID3D11ShaderResourceView** textureView, + _In_ size_t maxsize = 0, + _Out_opt_ DDS_ALPHA_MODE* alphaMode = nullptr) noexcept; + + // Extended version + HRESULT CreateDDSTextureFromMemoryEx( + _In_ ID3D11Device* d3dDevice, + _In_reads_bytes_(ddsDataSize) const uint8_t* ddsData, + _In_ size_t ddsDataSize, + _In_ size_t maxsize, + _In_ D3D11_USAGE usage, + _In_ unsigned int bindFlags, + _In_ unsigned int cpuAccessFlags, + _In_ unsigned int miscFlags, + _In_ DDS_LOADER_FLAGS loadFlags, + _Outptr_opt_ ID3D11Resource** texture, + _Outptr_opt_ ID3D11ShaderResourceView** textureView, + _Out_opt_ DDS_ALPHA_MODE* alphaMode = nullptr) noexcept; + + HRESULT CreateDDSTextureFromFileEx( + _In_ ID3D11Device* d3dDevice, + _In_z_ const wchar_t* szFileName, + _In_ size_t maxsize, + _In_ D3D11_USAGE usage, + _In_ unsigned int bindFlags, + _In_ unsigned int cpuAccessFlags, + _In_ unsigned int miscFlags, + _In_ DDS_LOADER_FLAGS loadFlags, + _Outptr_opt_ ID3D11Resource** texture, + _Outptr_opt_ ID3D11ShaderResourceView** textureView, + _Out_opt_ DDS_ALPHA_MODE* alphaMode = nullptr) noexcept; + + // Extended version with optional auto-gen mipmap support + HRESULT CreateDDSTextureFromMemoryEx( + _In_ ID3D11Device* d3dDevice, + _In_opt_ ID3D11DeviceContext* d3dContext, + _In_reads_bytes_(ddsDataSize) const uint8_t* ddsData, + _In_ size_t ddsDataSize, + _In_ size_t maxsize, + _In_ D3D11_USAGE usage, + _In_ unsigned int bindFlags, + _In_ unsigned int cpuAccessFlags, + _In_ unsigned int miscFlags, + _In_ DDS_LOADER_FLAGS loadFlags, + _Outptr_opt_ ID3D11Resource** texture, + _Outptr_opt_ ID3D11ShaderResourceView** textureView, + _Out_opt_ DDS_ALPHA_MODE* alphaMode = nullptr) noexcept; + + HRESULT CreateDDSTextureFromFileEx( + _In_ ID3D11Device* d3dDevice, + _In_opt_ ID3D11DeviceContext* d3dContext, + _In_z_ const wchar_t* szFileName, + _In_ size_t maxsize, + _In_ D3D11_USAGE usage, + _In_ unsigned int bindFlags, + _In_ unsigned int cpuAccessFlags, + _In_ unsigned int miscFlags, + _In_ DDS_LOADER_FLAGS loadFlags, + _Outptr_opt_ ID3D11Resource** texture, + _Outptr_opt_ ID3D11ShaderResourceView** textureView, + _Out_opt_ DDS_ALPHA_MODE* alphaMode = nullptr) noexcept; +} \ No newline at end of file diff --git a/DDSTextureLoader12.cpp b/DDSTextureLoader12.cpp new file mode 100644 index 0000000..df909a4 --- /dev/null +++ b/DDSTextureLoader12.cpp @@ -0,0 +1,1837 @@ +//-------------------------------------------------------------------------------------- +// File: DDSTextureLoader12.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. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248926 +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "DDSTextureLoader12.h" + +#include +#include +#include +#include + +#ifndef _WIN32 +#include +#include +#endif + +#ifdef _MSC_VER +// Off by default warnings +#pragma warning(disable : 4619 4616 4061 4062 4623 4626 5027) +// C4619/4616 #pragma warning warnings +// C4061 enumerator 'x' in switch of enum 'y' is not explicitly handled by a case label +// C4062 enumerator 'x' in switch of enum 'y' is not handled +// C4623 default constructor was implicitly defined as deleted +// C4626 assignment operator was implicitly defined as deleted +// C5027 move assignment operator was implicitly defined as deleted +#endif + +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wtautological-type-limit-compare" +#pragma clang diagnostic ignored "-Wcovered-switch-default" +#pragma clang diagnostic ignored "-Wswitch" +#pragma clang diagnostic ignored "-Wswitch-enum" +#pragma clang diagnostic ignored "-Wunused-macros" +#endif + +#define D3DX12_NO_STATE_OBJECT_HELPERS +#define D3DX12_NO_CHECK_FEATURE_SUPPORT_CLASS +#if !defined(_WIN32) || defined(USING_DIRECTX_HEADERS) +#include "directx/d3dx12.h" +#else +#include "d3dx12.h" +#endif + +using namespace DirectX; + +//-------------------------------------------------------------------------------------- +// Macros +//-------------------------------------------------------------------------------------- +#ifndef MAKEFOURCC +#define MAKEFOURCC(ch0, ch1, ch2, ch3) \ + ((uint32_t)(uint8_t)(ch0) | ((uint32_t)(uint8_t)(ch1) << 8) | \ + ((uint32_t)(uint8_t)(ch2) << 16) | ((uint32_t)(uint8_t)(ch3) << 24 )) +#endif /* defined(MAKEFOURCC) */ + +// HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW) +#define HRESULT_E_ARITHMETIC_OVERFLOW static_cast(0x80070216L) + +// HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED) +#define HRESULT_E_NOT_SUPPORTED static_cast(0x80070032L) + +// HRESULT_FROM_WIN32(ERROR_HANDLE_EOF) +#define HRESULT_E_HANDLE_EOF static_cast(0x80070026L) + +// HRESULT_FROM_WIN32(ERROR_INVALID_DATA) +#define HRESULT_E_INVALID_DATA static_cast(0x8007000DL) + +//-------------------------------------------------------------------------------------- +// DDS file structure definitions +// +// See DDS.h in the 'Texconv' sample and the 'DirectXTex' library +//-------------------------------------------------------------------------------------- +#pragma pack(push,1) + +constexpr uint32_t DDS_MAGIC = 0x20534444; // "DDS " + +struct DDS_PIXELFORMAT +{ + uint32_t size; + uint32_t flags; + uint32_t fourCC; + uint32_t RGBBitCount; + uint32_t RBitMask; + uint32_t GBitMask; + uint32_t BBitMask; + uint32_t ABitMask; +}; + +#define DDS_FOURCC 0x00000004 // DDPF_FOURCC +#define DDS_RGB 0x00000040 // DDPF_RGB +#define DDS_LUMINANCE 0x00020000 // DDPF_LUMINANCE +#define DDS_ALPHA 0x00000002 // DDPF_ALPHA +#define DDS_BUMPDUDV 0x00080000 // DDPF_BUMPDUDV + +#define DDS_HEADER_FLAGS_VOLUME 0x00800000 // DDSD_DEPTH + +#define DDS_HEIGHT 0x00000002 // DDSD_HEIGHT + +#define DDS_CUBEMAP_POSITIVEX 0x00000600 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEX +#define DDS_CUBEMAP_NEGATIVEX 0x00000a00 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEX +#define DDS_CUBEMAP_POSITIVEY 0x00001200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEY +#define DDS_CUBEMAP_NEGATIVEY 0x00002200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEY +#define DDS_CUBEMAP_POSITIVEZ 0x00004200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEZ +#define DDS_CUBEMAP_NEGATIVEZ 0x00008200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEZ + +#define DDS_CUBEMAP_ALLFACES ( DDS_CUBEMAP_POSITIVEX | DDS_CUBEMAP_NEGATIVEX |\ + DDS_CUBEMAP_POSITIVEY | DDS_CUBEMAP_NEGATIVEY |\ + DDS_CUBEMAP_POSITIVEZ | DDS_CUBEMAP_NEGATIVEZ ) + +#define DDS_CUBEMAP 0x00000200 // DDSCAPS2_CUBEMAP + +enum DDS_MISC_FLAGS2 +{ + DDS_MISC_FLAGS2_ALPHA_MODE_MASK = 0x7L, +}; + +struct DDS_HEADER +{ + uint32_t size; + uint32_t flags; + uint32_t height; + uint32_t width; + uint32_t pitchOrLinearSize; + uint32_t depth; // only if DDS_HEADER_FLAGS_VOLUME is set in flags + uint32_t mipMapCount; + uint32_t reserved1[11]; + DDS_PIXELFORMAT ddspf; + uint32_t caps; + uint32_t caps2; + uint32_t caps3; + uint32_t caps4; + uint32_t reserved2; +}; + +struct DDS_HEADER_DXT10 +{ + DXGI_FORMAT dxgiFormat; + uint32_t resourceDimension; + uint32_t miscFlag; // see D3D11_RESOURCE_MISC_FLAG + uint32_t arraySize; + uint32_t miscFlags2; +}; + +#pragma pack(pop) + +//-------------------------------------------------------------------------------------- +namespace +{ +#ifdef _WIN32 + struct handle_closer { void operator()(HANDLE h) noexcept { if (h) CloseHandle(h); } }; + + using ScopedHandle = std::unique_ptr; + + inline HANDLE safe_handle(HANDLE h) noexcept { return (h == INVALID_HANDLE_VALUE) ? nullptr : h; } +#endif + + #if !defined(NO_D3D12_DEBUG_NAME) && ( defined(_DEBUG) || defined(PROFILE) ) + template + inline void SetDebugObjectName(_In_ ID3D12DeviceChild* resource, _In_z_ const wchar_t(&name)[TNameLength]) noexcept + { + resource->SetName(name); + } + #else + template + inline void SetDebugObjectName(_In_ ID3D12DeviceChild*, _In_z_ const wchar_t(&)[TNameLength]) noexcept + { + } + #endif + + inline uint32_t CountMips(uint32_t width, uint32_t height) noexcept + { + if (width == 0 || height == 0) + return 0; + + uint32_t count = 1; + while (width > 1 || height > 1) + { + width >>= 1; + height >>= 1; + count++; + } + return count; + } + + + //-------------------------------------------------------------------------------------- + HRESULT LoadTextureDataFromMemory( + _In_reads_(ddsDataSize) const uint8_t* ddsData, + size_t ddsDataSize, + const DDS_HEADER** header, + const uint8_t** bitData, + size_t* bitSize) noexcept + { + if (!header || !bitData || !bitSize) + { + return E_POINTER; + } + + *bitSize = 0; + + if (ddsDataSize > UINT32_MAX) + { + return E_FAIL; + } + + if (ddsDataSize < (sizeof(uint32_t) + sizeof(DDS_HEADER))) + { + return E_FAIL; + } + + // DDS files always start with the same magic number ("DDS ") + auto const dwMagicNumber = *reinterpret_cast(ddsData); + if (dwMagicNumber != DDS_MAGIC) + { + return E_FAIL; + } + + auto hdr = reinterpret_cast(ddsData + 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 (ddsDataSize < (sizeof(uint32_t) + sizeof(DDS_HEADER) + sizeof(DDS_HEADER_DXT10))) + { + return E_FAIL; + } + + bDXT10Header = true; + } + + // setup the pointers in the process request + *header = hdr; + auto offset = sizeof(uint32_t) + + sizeof(DDS_HEADER) + + (bDXT10Header ? sizeof(DDS_HEADER_DXT10) : 0u); + *bitData = ddsData + offset; + *bitSize = ddsDataSize - offset; + + return S_OK; + } + + + //-------------------------------------------------------------------------------------- + HRESULT LoadTextureDataFromFile( + _In_z_ const wchar_t* fileName, + std::unique_ptr& ddsData, + const DDS_HEADER** header, + const uint8_t** bitData, + size_t* bitSize) noexcept + { + if (!header || !bitData || !bitSize) + { + return E_POINTER; + } + + *bitSize = 0; + + #ifdef _WIN32 + // open the file + ScopedHandle hFile(safe_handle(CreateFile2( + fileName, + GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, + nullptr))); + + if (!hFile) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + // Get the file size + FILE_STANDARD_INFO fileInfo; + if (!GetFileInformationByHandleEx(hFile.get(), FileStandardInfo, &fileInfo, sizeof(fileInfo))) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + // File is too big for 32-bit allocation, so reject read + if (fileInfo.EndOfFile.HighPart > 0) + { + return E_FAIL; + } + + // Need at least enough data to fill the header and magic number to be a valid DDS + if (fileInfo.EndOfFile.LowPart < (sizeof(uint32_t) + sizeof(DDS_HEADER))) + { + return E_FAIL; + } + + // create enough space for the file data + ddsData.reset(new (std::nothrow) uint8_t[fileInfo.EndOfFile.LowPart]); + if (!ddsData) + { + return E_OUTOFMEMORY; + } + + // read the data in + DWORD bytesRead = 0; + if (!ReadFile(hFile.get(), + ddsData.get(), + fileInfo.EndOfFile.LowPart, + &bytesRead, + nullptr + )) + { + ddsData.reset(); + return HRESULT_FROM_WIN32(GetLastError()); + } + + if (bytesRead < fileInfo.EndOfFile.LowPart) + { + ddsData.reset(); + return E_FAIL; + } + + size_t len = fileInfo.EndOfFile.LowPart; + + #else // !WIN32 + std::ifstream inFile(std::filesystem::path(fileName), std::ios::in | std::ios::binary | std::ios::ate); + if (!inFile) + return E_FAIL; + + std::streampos fileLen = inFile.tellg(); + if (!inFile) + return E_FAIL; + + // Need at least enough data to fill the header and magic number to be a valid DDS + if (fileLen < (sizeof(uint32_t) + sizeof(DDS_HEADER))) + return E_FAIL; + + ddsData.reset(new (std::nothrow) uint8_t[size_t(fileLen)]); + if (!ddsData) + return E_OUTOFMEMORY; + + inFile.seekg(0, std::ios::beg); + if (!inFile) + { + ddsData.reset(); + return E_FAIL; + } + + inFile.read(reinterpret_cast(ddsData.get()), fileLen); + if (!inFile) + { + ddsData.reset(); + return E_FAIL; + } + + inFile.close(); + + size_t len = fileLen; + #endif + + // DDS files always start with the same magic number ("DDS ") + auto const dwMagicNumber = *reinterpret_cast(ddsData.get()); + if (dwMagicNumber != DDS_MAGIC) + { + ddsData.reset(); + 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)) + { + ddsData.reset(); + 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 (len < (sizeof(uint32_t) + sizeof(DDS_HEADER) + sizeof(DDS_HEADER_DXT10))) + { + ddsData.reset(); + return E_FAIL; + } + + bDXT10Header = true; + } + + // setup the pointers in the process request + *header = hdr; + auto offset = sizeof(uint32_t) + sizeof(DDS_HEADER) + + (bDXT10Header ? sizeof(DDS_HEADER_DXT10) : 0u); + *bitData = ddsData.get() + offset; + *bitSize = len - offset; + + return S_OK; + } + + + //-------------------------------------------------------------------------------------- + // Return the BPP for a particular format + //-------------------------------------------------------------------------------------- + size_t BitsPerPixel(_In_ DXGI_FORMAT fmt) noexcept + { + 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: + case DXGI_FORMAT_V408: + 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: + case DXGI_FORMAT_P208: + case DXGI_FORMAT_V208: + 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_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: + 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; + + default: + return 0; + } + } + + + //-------------------------------------------------------------------------------------- + // Get surface information for a particular format + //-------------------------------------------------------------------------------------- + HRESULT GetSurfaceInfo( + _In_ size_t width, + _In_ size_t height, + _In_ DXGI_FORMAT fmt, + size_t* outNumBytes, + _Out_opt_ size_t* outRowBytes, + _Out_opt_ size_t* outNumRows) noexcept + { + uint64_t numBytes = 0; + uint64_t rowBytes = 0; + uint64_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: + if ((height % 2) != 0) + { + // Requires a height alignment of 2. + return E_INVALIDARG; + } + planar = true; + bpe = 2; + break; + + case DXGI_FORMAT_P208: + planar = true; + bpe = 2; + break; + + case DXGI_FORMAT_P010: + case DXGI_FORMAT_P016: + if ((height % 2) != 0) + { + // Requires a height alignment of 2. + return E_INVALIDARG; + } + planar = true; + bpe = 4; + break; + + default: + break; + } + + if (bc) + { + uint64_t numBlocksWide = 0; + if (width > 0) + { + numBlocksWide = std::max(1u, (uint64_t(width) + 3u) / 4u); + } + uint64_t numBlocksHigh = 0; + if (height > 0) + { + numBlocksHigh = std::max(1u, (uint64_t(height) + 3u) / 4u); + } + rowBytes = numBlocksWide * bpe; + numRows = numBlocksHigh; + numBytes = rowBytes * numBlocksHigh; + } + else if (packed) + { + rowBytes = ((uint64_t(width) + 1u) >> 1) * bpe; + numRows = uint64_t(height); + numBytes = rowBytes * height; + } + else if (fmt == DXGI_FORMAT_NV11) + { + rowBytes = ((uint64_t(width) + 3u) >> 2) * 4u; + numRows = uint64_t(height) * 2u; // Direct3D makes this simplifying assumption, although it is larger than the 4:1:1 data + numBytes = rowBytes * numRows; + } + else if (planar) + { + rowBytes = ((uint64_t(width) + 1u) >> 1) * bpe; + numBytes = (rowBytes * uint64_t(height)) + ((rowBytes * uint64_t(height) + 1u) >> 1); + numRows = height + ((uint64_t(height) + 1u) >> 1); + } + else + { + const size_t bpp = BitsPerPixel(fmt); + if (!bpp) + return E_INVALIDARG; + + rowBytes = (uint64_t(width) * bpp + 7u) / 8u; // round up to nearest byte + numRows = uint64_t(height); + numBytes = rowBytes * height; + } + + #if defined(_M_IX86) || defined(_M_ARM) || defined(_M_HYBRID_X86_ARM64) + static_assert(sizeof(size_t) == 4, "Not a 32-bit platform!"); + if (numBytes > UINT32_MAX || rowBytes > UINT32_MAX || numRows > UINT32_MAX) + return HRESULT_E_ARITHMETIC_OVERFLOW; + #else + static_assert(sizeof(size_t) == 8, "Not a 64-bit platform!"); + #endif + + if (outNumBytes) + { + *outNumBytes = static_cast(numBytes); + } + if (outRowBytes) + { + *outRowBytes = static_cast(rowBytes); + } + if (outNumRows) + { + *outNumRows = static_cast(numRows); + } + + return S_OK; + } + + + //-------------------------------------------------------------------------------------- +#define ISBITMASK( r,g,b,a ) ( ddpf.RBitMask == r && ddpf.GBitMask == g && ddpf.BBitMask == b && ddpf.ABitMask == a ) + + DXGI_FORMAT GetDXGIFormat(const DDS_PIXELFORMAT& ddpf) noexcept + { + 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, 0)) + { + return DXGI_FORMAT_B8G8R8X8_UNORM; + } + + // No DXGI format maps to ISBITMASK(0x000000ff,0x0000ff00,0x00ff0000,0) 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, 0, 0)) + { + return DXGI_FORMAT_R16G16_UNORM; + } + + if (ISBITMASK(0xffffffff, 0, 0, 0)) + { + // 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, 0)) + { + return DXGI_FORMAT_B5G6R5_UNORM; + } + + // No DXGI format maps to ISBITMASK(0x7c00,0x03e0,0x001f,0) aka D3DFMT_X1R5G5B5 + + if (ISBITMASK(0x0f00, 0x00f0, 0x000f, 0xf000)) + { + return DXGI_FORMAT_B4G4R4A4_UNORM; + } + + // NVTT versions 1.x wrote this as RGB instead of LUMINANCE + if (ISBITMASK(0x00ff, 0, 0, 0xff00)) + { + return DXGI_FORMAT_R8G8_UNORM; + } + if (ISBITMASK(0xffff, 0, 0, 0)) + { + return DXGI_FORMAT_R16_UNORM; + } + + // No DXGI format maps to ISBITMASK(0x0f00,0x00f0,0x000f,0) aka D3DFMT_X4R4G4B4 + + // No 3:3:2:8 or paletted DXGI formats aka D3DFMT_A8R3G3B2, D3DFMT_A8P8, etc. + break; + + case 8: + // NVTT versions 1.x wrote this as RGB instead of LUMINANCE + if (ISBITMASK(0xff, 0, 0, 0)) + { + return DXGI_FORMAT_R8_UNORM; + } + + // No 3:3:2 or paletted DXGI formats aka D3DFMT_R3G3B2, D3DFMT_P8 + break; + + default: + return DXGI_FORMAT_UNKNOWN; + } + } + else if (ddpf.flags & DDS_LUMINANCE) + { + switch (ddpf.RGBBitCount) + { + case 16: + if (ISBITMASK(0xffff, 0, 0, 0)) + { + return DXGI_FORMAT_R16_UNORM; // D3DX10/11 writes this out as DX10 extension + } + if (ISBITMASK(0x00ff, 0, 0, 0xff00)) + { + return DXGI_FORMAT_R8G8_UNORM; // D3DX10/11 writes this out as DX10 extension + } + break; + + case 8: + if (ISBITMASK(0xff, 0, 0, 0)) + { + return DXGI_FORMAT_R8_UNORM; // D3DX10/11 writes this out as DX10 extension + } + + // No DXGI format maps to ISBITMASK(0x0f,0,0,0xf0) aka D3DFMT_A4L4 + + if (ISBITMASK(0x00ff, 0, 0, 0xff00)) + { + return DXGI_FORMAT_R8G8_UNORM; // Some DDS writers assume the bitcount should be 8 instead of 16 + } + break; + + default: + return DXGI_FORMAT_UNKNOWN; + } + } + else if (ddpf.flags & DDS_ALPHA) + { + if (8 == ddpf.RGBBitCount) + { + return DXGI_FORMAT_A8_UNORM; + } + } + else if (ddpf.flags & DDS_BUMPDUDV) + { + switch (ddpf.RGBBitCount) + { + case 32: + if (ISBITMASK(0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000)) + { + return DXGI_FORMAT_R8G8B8A8_SNORM; // D3DX10/11 writes this out as DX10 extension + } + if (ISBITMASK(0x0000ffff, 0xffff0000, 0, 0)) + { + 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 + break; + + case 16: + if (ISBITMASK(0x00ff, 0xff00, 0, 0)) + { + return DXGI_FORMAT_R8G8_SNORM; // D3DX10/11 writes this out as DX10 extension + } + break; + + default: + return DXGI_FORMAT_UNKNOWN; + } + + // No DXGI format maps to DDPF_BUMPLUMINANCE aka D3DFMT_L6V5U5, D3DFMT_X8L8V8U8 + } + 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; + + // No DXGI format maps to D3DFMT_CxV8U8 + + default: + return DXGI_FORMAT_UNKNOWN; + } + } + + return DXGI_FORMAT_UNKNOWN; + } + +#undef ISBITMASK + + + //-------------------------------------------------------------------------------------- + DXGI_FORMAT MakeSRGB(_In_ DXGI_FORMAT format) noexcept + { + 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; + } + } + + + //-------------------------------------------------------------------------------------- + inline DXGI_FORMAT MakeLinear(_In_ DXGI_FORMAT format) noexcept + { + switch (format) + { + case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: + return DXGI_FORMAT_R8G8B8A8_UNORM; + + case DXGI_FORMAT_BC1_UNORM_SRGB: + return DXGI_FORMAT_BC1_UNORM; + + case DXGI_FORMAT_BC2_UNORM_SRGB: + return DXGI_FORMAT_BC2_UNORM; + + case DXGI_FORMAT_BC3_UNORM_SRGB: + return DXGI_FORMAT_BC3_UNORM; + + case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: + return DXGI_FORMAT_B8G8R8A8_UNORM; + + case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: + return DXGI_FORMAT_B8G8R8X8_UNORM; + + case DXGI_FORMAT_BC7_UNORM_SRGB: + return DXGI_FORMAT_BC7_UNORM; + + default: + return format; + } + } + + + //-------------------------------------------------------------------------------------- + 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: + 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 (!slicePlane) + { + // Plane 0 + res.SlicePitch = res.RowPitch * static_cast(height); + } + else + { + // Plane 1 + res.pData = reinterpret_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 = reinterpret_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_E_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_E_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); + } + else if (loadFlags & DDS_LOADER_IGNORE_SRGB) + { + format = MakeLinear(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; + + const CD3DX12_HEAP_PROPERTIES defaultHeapProperties(D3D12_HEAP_TYPE_DEFAULT); + + hr = d3dDevice->CreateCommittedResource( + &defaultHeapProperties, + D3D12_HEAP_FLAG_NONE, + &desc, + D3D12_RESOURCE_STATE_COMMON, + nullptr, + IID_ID3D12Resource, reinterpret_cast(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; + + const 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_E_INVALID_DATA; + } + + switch (d3d10ext->dxgiFormat) + { + case DXGI_FORMAT_NV12: + case DXGI_FORMAT_P010: + case DXGI_FORMAT_P016: + case DXGI_FORMAT_420_OPAQUE: + if ((d3d10ext->resourceDimension != D3D12_RESOURCE_DIMENSION_TEXTURE2D) + || (width % 2) != 0 || (height % 2) != 0) + { + return HRESULT_E_NOT_SUPPORTED; + } + break; + + case DXGI_FORMAT_YUY2: + case DXGI_FORMAT_Y210: + case DXGI_FORMAT_Y216: + case DXGI_FORMAT_P208: + if ((width % 2) != 0) + { + return HRESULT_E_NOT_SUPPORTED; + } + break; + + case DXGI_FORMAT_NV11: + if ((width % 4) != 0) + { + return HRESULT_E_NOT_SUPPORTED; + } + break; + + case DXGI_FORMAT_AI44: + case DXGI_FORMAT_IA44: + case DXGI_FORMAT_P8: + case DXGI_FORMAT_A8P8: + return HRESULT_E_NOT_SUPPORTED; + + case DXGI_FORMAT_V208: + if ((d3d10ext->resourceDimension != D3D12_RESOURCE_DIMENSION_TEXTURE2D) + || (height % 2) != 0) + { + return HRESULT_E_NOT_SUPPORTED; + } + break; + + default: + if (BitsPerPixel(d3d10ext->dxgiFormat) == 0) + { + return HRESULT_E_NOT_SUPPORTED; + } + } + + 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_E_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_E_INVALID_DATA; + } + + if (arraySize > 1) + { + return HRESULT_E_NOT_SUPPORTED; + } + break; + + default: + return HRESULT_E_NOT_SUPPORTED; + } + + resDim = static_cast(d3d10ext->resourceDimension); + } + else + { + format = GetDXGIFormat(header->ddspf); + + if (format == DXGI_FORMAT_UNKNOWN) + { + return HRESULT_E_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) + { + return HRESULT_E_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) + { + return HRESULT_E_NOT_SUPPORTED; + } + + switch (resDim) + { + case D3D12_RESOURCE_DIMENSION_TEXTURE1D: + if ((arraySize > D3D12_REQ_TEXTURE1D_ARRAY_AXIS_DIMENSION) || + (width > D3D12_REQ_TEXTURE1D_U_DIMENSION)) + { + return HRESULT_E_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)) + { + return HRESULT_E_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)) + { + return HRESULT_E_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)) + { + return HRESULT_E_NOT_SUPPORTED; + } + break; + + default: + return HRESULT_E_NOT_SUPPORTED; + } + + const 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_E_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_RESERVE) + { + reservedMips = std::min(D3D12_REQ_MIP_LEVELS, + 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; + } + + //-------------------------------------------------------------------------------------- + DDS_ALPHA_MODE GetAlphaMode(_In_ const DDS_HEADER* header) noexcept + { + if (header->ddspf.flags & DDS_FOURCC) + { + if (MAKEFOURCC('D', 'X', '1', '0') == header->ddspf.fourCC) + { + auto d3d10ext = reinterpret_cast(reinterpret_cast(header) + sizeof(DDS_HEADER)); + auto const 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; + + case DDS_ALPHA_MODE_UNKNOWN: + default: + break; + } + } + 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; + } + + //-------------------------------------------------------------------------------------- + void SetDebugTextureInfo( + _In_z_ const wchar_t* fileName, + _In_ ID3D12Resource* texture) noexcept + { + #if !defined(NO_D3D12_DEBUG_NAME) && ( defined(_DEBUG) || defined(PROFILE) ) + const wchar_t* pstrName = wcsrchr(fileName, '\\'); + if (!pstrName) + { + pstrName = fileName; + } + else + { + pstrName++; + } + texture->SetName(pstrName); + #else + UNREFERENCED_PARAMETER(fileName); + UNREFERENCED_PARAMETER(texture); + #endif + } +} // 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)) + { + 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; +} diff --git a/DDSTextureLoader12.h b/DDSTextureLoader12.h new file mode 100644 index 0000000..ee31fe5 --- /dev/null +++ b/DDSTextureLoader12.h @@ -0,0 +1,125 @@ +//-------------------------------------------------------------------------------------- +// File: DDSTextureLoader12.h +// +// 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. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248926 +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#pragma once + +#ifdef __MINGW32__ +#include +#endif + +#ifndef _WIN32 +#include +#include +#endif + +#if !defined(_WIN32) || defined(USING_DIRECTX_HEADERS) +#include +#include +#else +#include +#pragma comment(lib,"dxguid.lib") +#endif + +#include +#include +#include +#include + + +namespace DirectX +{ +#ifndef DDS_ALPHA_MODE_DEFINED +#define DDS_ALPHA_MODE_DEFINED + enum DDS_ALPHA_MODE : uint32_t + { + DDS_ALPHA_MODE_UNKNOWN = 0, + DDS_ALPHA_MODE_STRAIGHT = 1, + DDS_ALPHA_MODE_PREMULTIPLIED = 2, + DDS_ALPHA_MODE_OPAQUE = 3, + DDS_ALPHA_MODE_CUSTOM = 4, + }; + +#endif + +#ifndef DDS_LOADER_FLAGS_DEFINED +#define DDS_LOADER_FLAGS_DEFINED + + enum DDS_LOADER_FLAGS : uint32_t + { + DDS_LOADER_DEFAULT = 0, + DDS_LOADER_FORCE_SRGB = 0x1, + DDS_LOADER_IGNORE_SRGB = 0x2, + DDS_LOADER_MIP_RESERVE = 0x8, + }; + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-dynamic-exception-spec" +#endif + + DEFINE_ENUM_FLAG_OPERATORS(DDS_LOADER_FLAGS); + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +#endif + + // Standard version + HRESULT __cdecl LoadDDSTextureFromMemory( + _In_ ID3D12Device* d3dDevice, + _In_reads_bytes_(ddsDataSize) const uint8_t* ddsData, + size_t ddsDataSize, + _Outptr_ ID3D12Resource** texture, + std::vector& subresources, + size_t maxsize = 0, + _Out_opt_ DDS_ALPHA_MODE* alphaMode = nullptr, + _Out_opt_ bool* isCubeMap = nullptr); + + HRESULT __cdecl LoadDDSTextureFromFile( + _In_ ID3D12Device* d3dDevice, + _In_z_ const wchar_t* szFileName, + _Outptr_ ID3D12Resource** texture, + std::unique_ptr& ddsData, + std::vector& subresources, + size_t maxsize = 0, + _Out_opt_ DDS_ALPHA_MODE* alphaMode = nullptr, + _Out_opt_ bool* isCubeMap = nullptr); + + // Extended version + HRESULT __cdecl LoadDDSTextureFromMemoryEx( + _In_ ID3D12Device* d3dDevice, + _In_reads_bytes_(ddsDataSize) const uint8_t* ddsData, + size_t ddsDataSize, + size_t maxsize, + D3D12_RESOURCE_FLAGS resFlags, + DDS_LOADER_FLAGS loadFlags, + _Outptr_ ID3D12Resource** texture, + std::vector& subresources, + _Out_opt_ DDS_ALPHA_MODE* alphaMode = nullptr, + _Out_opt_ bool* isCubeMap = nullptr); + + HRESULT __cdecl LoadDDSTextureFromFileEx( + _In_ ID3D12Device* d3dDevice, + _In_z_ const wchar_t* szFileName, + size_t maxsize, + D3D12_RESOURCE_FLAGS resFlags, + DDS_LOADER_FLAGS loadFlags, + _Outptr_ ID3D12Resource** texture, + std::unique_ptr& ddsData, + std::vector& subresources, + _Out_opt_ DDS_ALPHA_MODE* alphaMode = nullptr, + _Out_opt_ bool* isCubeMap = nullptr); +} diff --git a/GraphicContents.h b/GraphicContents.h index 0277a9a..c4377f2 100644 --- a/GraphicContents.h +++ b/GraphicContents.h @@ -3,12 +3,23 @@ #include #include -struct Vertex { - [[maybe_unused]] float x, y, z, r, g, b, a; // "Maybe unused" because all the data is passed to the GPU +struct TextureVertex { + [[maybe_unused]] float x, y, z, tx, ty; //, r, g, b, a; // "Maybe unused" because all the data is passed to the GPU +}; +struct RGBAVertex { + [[maybe_unused]] float x, y, z, r, g, b, a; // "Maybe unused" because all the data is passed to the GPU }; -struct GraphicContents { +template struct _GraphicContents { virtual void updateLayout(int width, int height) = 0; - virtual std::vector getVertices() = 0; + virtual std::vector getVertices() = 0; virtual std::string getShader() = 0; }; + +#if defined(USE_DX11) +typedef _GraphicContents GraphicContents; +#elif defined(USE_DX12) +typedef _GraphicContents GraphicContents; +#else +#error "You should set either USE_DX11 or USE_DX12" +#endif diff --git a/grass.dds b/grass.dds new file mode 100644 index 0000000..d7c17c1 Binary files /dev/null and b/grass.dds differ diff --git a/main.cpp b/main.cpp index 18d3b02..6ec32d4 100644 --- a/main.cpp +++ b/main.cpp @@ -10,6 +10,7 @@ #include #include +#if defined(USE_DX12) class TriangleGraphicContents : public GraphicContents { private: int width = 0, height = 0; @@ -24,18 +25,43 @@ class TriangleGraphicContents : public GraphicContents { // Sleep(100); } - std::vector getVertices() override { + std::vector getVertices() override { float aspect = (float) width / (float) height; float k = 760.f / (float) width; float sin60 = sqrtf(3.f) / 2; - std::vector vertices = { - {0.0f * k, 0.5f * sin60 * aspect * k, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f}, - {0.5f * k, -0.5f * sin60 * aspect * k, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f}, - {-0.5f * k, -0.5f * sin60 * aspect * k, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f} + float d = 0.3f; + std::vector vertices = { + {0.0f * k, 0.5f * sin60 * aspect * k, 0.0f, 0.5f, 0.0f, 0.5f}, + {0.5f * k, -0.5f * sin60 * aspect * k, 0.0f, 0.5f + d, 1.0f, 0.5f}, + {-0.5f * k, -0.5f * sin60 * aspect * k, 0.0f, 0.5f - d, 1.0f, 0.5f} }; return vertices; } +// std::string getShader() override { +// return { +// "Texture2D txDiffuse : register( t0 );\n" +// "SamplerState samLinear : register( s0 );\n" +// "\n" +// "struct VSInput {\n" +// " float4 position : POSITION;\n" +// " float2 Tex : TEXCOORD0;\n" +// "};\n" +// "struct PSInput {\n" +// " float4 position : SV_POSITION;\n" +// " float2 Tex : TEXCOORD0;\n" +// "};\n" +// "PSInput VSMain(VSInput input) {\n" +// " PSInput output;\n" +// " output.position = input.position;\n" +// " output.Tex = input.Tex;\n" +// " return output;\n" +// "}\n" +// "float4 PSMain(PSInput input) : SV_TARGET {\n" +// " return txDiffuse.Sample( samLinear, input.Tex );\n" +// "}\n" +// }; +// } std::string getShader() override { return { "struct PSInput {\n" @@ -54,11 +80,70 @@ class TriangleGraphicContents : public GraphicContents { }; } }; +#elif defined(USE_DX11) +class FullScreenImageGraphicContents : public GraphicContents { +private: + int width = 0, height = 0; + +public: + void updateLayout(int width, int height) override { + this->width = width; this->height = height; + + // Uncomment this fake resizing load here to see how the app handles it + // 100ms is a huge time pretty enough to recalculate even a very complicated layout + // + //Sleep(100); + } + + std::vector getVertices() override { + float aspect = (float) width / (float) height; + float k = 1;//760.f / (float) width; + //float sin60 = sqrtf(3.f) / 2; + std::vector vertices = { + {-1.0f * k, 1.0f * k, 0.0f, 0.0f, 0.0f}, + { 1.0f * k, 1.0f * k, 0.0f, 1.0f, 0.0f}, + { 1.0f * k, -1.0f * k, 0.0f, 1.0f, 1.0f}, + + {-1.0f * k, -1.0f * k, 0.0f, 0.0f, 1.0f}, + { 1.0f * k, -1.0f * k, 0.0f, 1.0f, 1.0f}, + {-1.0f * k, 1.0f * k, 0.0f, 0.0f, 0.0f}, + }; + return vertices; + } + + std::string getShader() override { + return { + "Texture2D txDiffuse : register( t0 );\n" + "SamplerState samLinear : register( s0 );\n" + "\n" + "struct VSInput {\n" + " float4 position : POSITION;\n" + " float2 Tex : TEXCOORD0;\n" + "};\n" + "struct PSInput {\n" + " float4 position : SV_POSITION;\n" + " float2 Tex : TEXCOORD0;\n" + "};\n" + "PSInput VSMain(VSInput input) {\n" + " PSInput output;\n" + " output.position = input.position;\n" + " output.Tex = input.Tex;\n" + " return output;\n" + "}\n" + "float4 PSMain(PSInput input) : SV_TARGET {\n" + " return txDiffuse.Sample( samLinear, input.Tex );\n" + "}\n" + }; + } +}; +#else + #error "You should set either USE_DX11 or USE_DX12" +#endif // Global declarations std::shared_ptr context; std::shared_ptr dcompContext; -std::shared_ptr contents; +std::shared_ptr contents; bool exitPending; // Passthrough (t) if truthy. Crash otherwise. @@ -110,7 +195,14 @@ LRESULT window_proc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) // The app entry point. int WinMain(HINSTANCE hinstance, HINSTANCE, LPSTR, int) { +#if defined(USE_DX12) contents = std::make_shared(); +#elif defined(USE_DX11) + contents = std::make_shared(); +#else + #error "You should set either USE_DX11 or USE_DX12" +#endif + context = std::make_shared(contents); // Register the window class.