├── .gitignore ├── grass.dds ├── res └── resize_demo.gif ├── .gitmodules ├── Base.h ├── DCompContext.h ├── GraphicContents.h ├── DCompContext.cpp ├── .github └── workflows │ └── cmake-single-platform.yml ├── CMakeLists.txt ├── DDSTextureLoader12.h ├── D3DContextBase.cpp ├── D3DContext.h ├── README.md ├── DDSTextureLoader.h ├── D3DContext_DX11.cpp ├── main.cpp ├── D3DContext_DX12.cpp ├── DDSTextureLoader.cpp └── DDSTextureLoader12.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | /cmake-* 2 | /.idea 3 | -------------------------------------------------------------------------------- /grass.dds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigfatbrowncat/noflicker_directx_window/HEAD/grass.dds -------------------------------------------------------------------------------- /res/resize_demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigfatbrowncat/noflicker_directx_window/HEAD/res/resize_demo.gif -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "third_party/DirectX-Headers"] 2 | path = third_party/DirectX-Headers 3 | url = https://github.com/microsoft/DirectX-Headers.git 4 | -------------------------------------------------------------------------------- /Base.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class Base { 6 | public: 7 | // Crash if hr != S_OK. 8 | static void hr_check(HRESULT hr) 9 | { 10 | // Ignore the "occluded" state as a success 11 | if (hr == DXGI_STATUS_OCCLUDED) return; 12 | 13 | if (hr == S_OK) return; 14 | while (true) __debugbreak(); 15 | } 16 | }; -------------------------------------------------------------------------------- /DCompContext.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Local headers 4 | #include "D3DContext.h" 5 | #include "Base.h" 6 | 7 | // OS headers 8 | #include 9 | #include 10 | 11 | // C++ stl 12 | #include 13 | 14 | struct DCompContext : public Base { 15 | IDCompositionDevice* dcomp; 16 | IDCompositionTarget* target; 17 | IDCompositionVisual* visual; 18 | 19 | // This function should NOT be called from the destructor, instead it has to be called in WM_DESTROY 20 | void unbind(); 21 | 22 | // Call this function immediately after the CreateWindowEx(WS_EX_NOREDIRECTIONBITMAP, ...) 23 | DCompContext(HWND hwnd, const std::shared_ptr& context); 24 | }; 25 | -------------------------------------------------------------------------------- /GraphicContents.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | struct TextureVertex { 7 | [[maybe_unused]] float x, y, z, tx, ty; //, r, g, b, a; // "Maybe unused" because all the data is passed to the GPU 8 | }; 9 | struct RGBAVertex { 10 | [[maybe_unused]] float x, y, z, r, g, b, a; // "Maybe unused" because all the data is passed to the GPU 11 | }; 12 | 13 | template struct _GraphicContents { 14 | virtual void updateLayout(int width, int height) = 0; 15 | virtual std::vector getVertices() = 0; 16 | virtual std::string getShader() = 0; 17 | }; 18 | 19 | #if defined(USE_DX11) 20 | typedef _GraphicContents GraphicContents; 21 | #elif defined(USE_DX12) 22 | typedef _GraphicContents GraphicContents; 23 | #else 24 | #error "You should set either USE_DX11 or USE_DX12" 25 | #endif 26 | -------------------------------------------------------------------------------- /DCompContext.cpp: -------------------------------------------------------------------------------- 1 | #include "DCompContext.h" 2 | 3 | void DCompContext::unbind() { 4 | if (visual != nullptr) { visual->Release(); visual = nullptr; } 5 | if (target != nullptr) { target->Release(); target = nullptr; } 6 | if (dcomp != nullptr) { dcomp->Release(); dcomp = nullptr; } 7 | } 8 | 9 | DCompContext::DCompContext(HWND hwnd, const std::shared_ptr& context): dcomp(nullptr), target(nullptr), visual(nullptr) { 10 | // Bind our swap chain to the window. 11 | // TODO: Determine what DCompositionCreateDevice(nullptr, ...) actually does. 12 | // I assume it creates a minimal IDCompositionDevice for use with D3D that can't actually 13 | // do any adapter-specific resource allocations itself, but I'm yet to verify this. 14 | hr_check(DCompositionCreateDevice(nullptr, IID_PPV_ARGS(&dcomp))); 15 | hr_check(dcomp->CreateTargetForHwnd(hwnd, FALSE, &target)); 16 | hr_check(dcomp->CreateVisual(&visual)); 17 | hr_check(target->SetRoot(visual)); 18 | hr_check(visual->SetContent(context->swapChain)); 19 | hr_check(dcomp->Commit()); 20 | } -------------------------------------------------------------------------------- /.github/workflows/cmake-single-platform.yml: -------------------------------------------------------------------------------- 1 | # This starter workflow is for a CMake project running on a single platform. There is a different starter workflow if you need cross-platform coverage. 2 | # See: https://github.com/actions/starter-workflows/blob/main/ci/cmake-multi-platform.yml 3 | name: CMake on Windows 2019 4 | 5 | on: 6 | push: 7 | branches: [ "master" ] 8 | pull_request: 9 | branches: [ "master" ] 10 | 11 | env: 12 | # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) 13 | BUILD_TYPE: Release 14 | 15 | jobs: 16 | build: 17 | # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac. 18 | # You can convert this to a matrix build if you need cross-platform coverage. 19 | # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix 20 | runs-on: windows-2019 21 | 22 | steps: 23 | - name: Checkout 24 | uses: actions/checkout@v3 25 | with: 26 | submodules: true 27 | 28 | # Update references 29 | - name: Git Submodule Update 30 | run: | 31 | git pull --recurse-submodules 32 | git submodule update --remote --recursive 33 | 34 | - name: Configure CMake 35 | # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. 36 | # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type 37 | run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} 38 | 39 | - name: Build 40 | # Build your program with the given configuration 41 | run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} 42 | 43 | # - name: Test 44 | # working-directory: ${{github.workspace}}/build 45 | # # Execute tests defined by the CMake configuration. 46 | # # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail 47 | # run: ctest -C ${{env.BUILD_TYPE}} 48 | 49 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | 3 | set(TARGET noflicker_directx_window) 4 | project(${TARGET}) 5 | 6 | # Global flags 7 | 8 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MD") # No idea who removed /Zi flag from the debug conf. Putting it back 9 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Zi /MDd") # No idea who removed /Zi flag from the debug conf. Putting it back 10 | add_subdirectory(third_party/DirectX-Headers) 11 | 12 | # Demo for DirectX 11 13 | 14 | set(EXE_DX11 noflicker_directx11_window) 15 | add_executable(${EXE_DX11} WIN32 16 | main.cpp 17 | 18 | D3DContext.h 19 | D3DContext_DX11.cpp 20 | 21 | DDSTextureLoader.h 22 | DDSTextureLoader.cpp 23 | 24 | DCompContext.h 25 | DCompContext.cpp 26 | 27 | D3DContextBase.cpp Base.h GraphicContents.h) 28 | 29 | target_compile_definitions(${EXE_DX11} PUBLIC WINVER=0x0602 UNICODE _UNICODE USE_DX11) 30 | target_compile_features(${EXE_DX11} PUBLIC cxx_std_20) 31 | target_link_libraries(${EXE_DX11} PUBLIC D3D11 dxgi dxguid D3DCompiler Dcomp) 32 | add_custom_command( 33 | TARGET ${EXE_DX11} POST_BUILD 34 | COMMAND ${CMAKE_COMMAND} -E copy 35 | ${CMAKE_CURRENT_SOURCE_DIR}/grass.dds 36 | $) 37 | 38 | set_target_properties(${EXE_DX11} 39 | PROPERTIES 40 | LINK_FLAGS_DEBUG "/SUBSYSTEM:CONSOLE /ENTRY:WinMainCRTStartup /DDEBUG /D_DEBUG" 41 | LINK_FLAGS_RELEASE "/SUBSYSTEM:windows /ENTRY:WinMainCRTStartup" 42 | LINK_FLAGS_RELWITHDEBINFO "/SUBSYSTEM:windows /ENTRY:WinMainCRTStartup" 43 | LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:windows /ENTRY:WinMainCRTStartup" 44 | ) 45 | 46 | # Demo for DirectX 12 47 | 48 | set(EXE_DX12 noflicker_directx12_window) 49 | add_executable(${EXE_DX12} WIN32 50 | main.cpp 51 | D3DContext.h 52 | D3DContext_DX12.cpp 53 | 54 | DDSTextureLoader12.h 55 | DDSTextureLoader12.cpp 56 | 57 | DCompContext.h 58 | DCompContext.cpp D3DContextBase.cpp Base.h GraphicContents.h) 59 | 60 | add_dependencies(${EXE_DX12} DirectX-Headers) 61 | target_include_directories(${EXE_DX12} PUBLIC ${DirectX-Headers_SOURCE_DIR}/include) 62 | target_compile_definitions(${EXE_DX12} PUBLIC WINVER=0x0602 UNICODE _UNICODE USE_DX12 USING_DIRECTX_HEADERS) 63 | target_compile_features(${EXE_DX12} PUBLIC cxx_std_20) 64 | target_link_libraries(${EXE_DX12} PUBLIC D3D12 dxgi D3DCompiler Dcomp DirectX-Headers DirectX-Guids) 65 | 66 | set_target_properties(${EXE_DX12} 67 | PROPERTIES 68 | LINK_FLAGS_DEBUG "/SUBSYSTEM:CONSOLE /ENTRY:WinMainCRTStartup /DDEBUG /D_DEBUG" 69 | LINK_FLAGS_RELEASE "/SUBSYSTEM:windows /ENTRY:WinMainCRTStartup" 70 | LINK_FLAGS_RELWITHDEBINFO "/SUBSYSTEM:windows /ENTRY:WinMainCRTStartup" 71 | LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:windows /ENTRY:WinMainCRTStartup" 72 | ) -------------------------------------------------------------------------------- /DDSTextureLoader12.h: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------------------------------------------- 2 | // File: DDSTextureLoader12.h 3 | // 4 | // Functions for loading a DDS texture and creating a Direct3D runtime resource for it 5 | // 6 | // Note these functions are useful as a light-weight runtime loader for DDS files. For 7 | // a full-featured DDS file reader, writer, and texture processing pipeline see 8 | // the 'Texconv' sample and the 'DirectXTex' library. 9 | // 10 | // Copyright (c) Microsoft Corporation. 11 | // Licensed under the MIT License. 12 | // 13 | // http://go.microsoft.com/fwlink/?LinkId=248926 14 | // http://go.microsoft.com/fwlink/?LinkID=615561 15 | //-------------------------------------------------------------------------------------- 16 | 17 | #pragma once 18 | 19 | #ifdef __MINGW32__ 20 | #include 21 | #endif 22 | 23 | #ifndef _WIN32 24 | #include 25 | #include 26 | #endif 27 | 28 | #if !defined(_WIN32) || defined(USING_DIRECTX_HEADERS) 29 | #include 30 | #include 31 | #else 32 | #include 33 | #pragma comment(lib,"dxguid.lib") 34 | #endif 35 | 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | 42 | namespace DirectX 43 | { 44 | #ifndef DDS_ALPHA_MODE_DEFINED 45 | #define DDS_ALPHA_MODE_DEFINED 46 | enum DDS_ALPHA_MODE : uint32_t 47 | { 48 | DDS_ALPHA_MODE_UNKNOWN = 0, 49 | DDS_ALPHA_MODE_STRAIGHT = 1, 50 | DDS_ALPHA_MODE_PREMULTIPLIED = 2, 51 | DDS_ALPHA_MODE_OPAQUE = 3, 52 | DDS_ALPHA_MODE_CUSTOM = 4, 53 | }; 54 | 55 | #endif 56 | 57 | #ifndef DDS_LOADER_FLAGS_DEFINED 58 | #define DDS_LOADER_FLAGS_DEFINED 59 | 60 | enum DDS_LOADER_FLAGS : uint32_t 61 | { 62 | DDS_LOADER_DEFAULT = 0, 63 | DDS_LOADER_FORCE_SRGB = 0x1, 64 | DDS_LOADER_IGNORE_SRGB = 0x2, 65 | DDS_LOADER_MIP_RESERVE = 0x8, 66 | }; 67 | 68 | #ifdef __clang__ 69 | #pragma clang diagnostic push 70 | #pragma clang diagnostic ignored "-Wdeprecated-dynamic-exception-spec" 71 | #endif 72 | 73 | DEFINE_ENUM_FLAG_OPERATORS(DDS_LOADER_FLAGS); 74 | 75 | #ifdef __clang__ 76 | #pragma clang diagnostic pop 77 | #endif 78 | #endif 79 | 80 | // Standard version 81 | HRESULT __cdecl LoadDDSTextureFromMemory( 82 | _In_ ID3D12Device* d3dDevice, 83 | _In_reads_bytes_(ddsDataSize) const uint8_t* ddsData, 84 | size_t ddsDataSize, 85 | _Outptr_ ID3D12Resource** texture, 86 | std::vector& subresources, 87 | size_t maxsize = 0, 88 | _Out_opt_ DDS_ALPHA_MODE* alphaMode = nullptr, 89 | _Out_opt_ bool* isCubeMap = nullptr); 90 | 91 | HRESULT __cdecl LoadDDSTextureFromFile( 92 | _In_ ID3D12Device* d3dDevice, 93 | _In_z_ const wchar_t* szFileName, 94 | _Outptr_ ID3D12Resource** texture, 95 | std::unique_ptr& ddsData, 96 | std::vector& subresources, 97 | size_t maxsize = 0, 98 | _Out_opt_ DDS_ALPHA_MODE* alphaMode = nullptr, 99 | _Out_opt_ bool* isCubeMap = nullptr); 100 | 101 | // Extended version 102 | HRESULT __cdecl LoadDDSTextureFromMemoryEx( 103 | _In_ ID3D12Device* d3dDevice, 104 | _In_reads_bytes_(ddsDataSize) const uint8_t* ddsData, 105 | size_t ddsDataSize, 106 | size_t maxsize, 107 | D3D12_RESOURCE_FLAGS resFlags, 108 | DDS_LOADER_FLAGS loadFlags, 109 | _Outptr_ ID3D12Resource** texture, 110 | std::vector& subresources, 111 | _Out_opt_ DDS_ALPHA_MODE* alphaMode = nullptr, 112 | _Out_opt_ bool* isCubeMap = nullptr); 113 | 114 | HRESULT __cdecl LoadDDSTextureFromFileEx( 115 | _In_ ID3D12Device* d3dDevice, 116 | _In_z_ const wchar_t* szFileName, 117 | size_t maxsize, 118 | D3D12_RESOURCE_FLAGS resFlags, 119 | DDS_LOADER_FLAGS loadFlags, 120 | _Outptr_ ID3D12Resource** texture, 121 | std::unique_ptr& ddsData, 122 | std::vector& subresources, 123 | _Out_opt_ DDS_ALPHA_MODE* alphaMode = nullptr, 124 | _Out_opt_ bool* isCubeMap = nullptr); 125 | } 126 | -------------------------------------------------------------------------------- /D3DContextBase.cpp: -------------------------------------------------------------------------------- 1 | #include "D3DContext.h" 2 | 3 | #include 4 | #include 5 | 6 | bool D3DContextBase::checkRECTsIntersect(const RECT& r1, const RECT& r2) { 7 | return r1.left < r2.right && r2.left < r1.right && 8 | r1.top < r2.bottom && r2.top < r1.bottom; 9 | } 10 | 11 | bool D3DContextBase::checkRECTContainsPoint(const RECT& r, LONG x, LONG y) { 12 | return r.left < x && r.right > x && 13 | r.top < y && r.bottom > y; 14 | } 15 | 16 | void D3DContextBase::checkDeviceRemoved(HRESULT hr) const { 17 | if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) 18 | { 19 | #ifdef _DEBUG 20 | char buff[64] = {}; 21 | sprintf_s(buff, "Device Lost: reason code 0x%08X\n", 22 | (hr == DXGI_ERROR_DEVICE_REMOVED) ? device->GetDeviceRemovedReason() : hr); 23 | std::cerr << buff << std::endl; 24 | #endif 25 | // If the device was removed for any reason, a new device 26 | // and swap chain will need to be created. 27 | // TODO HandleDeviceLost(); 28 | } 29 | else 30 | { 31 | // Any other failed result is a fatal fast-fail 32 | hr_check(hr); 33 | } 34 | } 35 | 36 | D3DContextBase::D3DContextBase(std::shared_ptr contents) : contents(std::move(contents)), device(nullptr) { 37 | // Create the DXGI factory. 38 | hr_check(CreateDXGIFactory1(IID_PPV_ARGS(&dxgiFactory))); 39 | 40 | // Look for the adapters (and specifically an Intel GPU) in the system 41 | const UINT INTEL_VENDOR_ID = 0x8086; 42 | IDXGIAdapter* adapter; 43 | for (UINT i = 0; dxgiFactory->EnumAdapters(i, &adapter) != DXGI_ERROR_NOT_FOUND; i++) { 44 | DXGI_ADAPTER_DESC desc; 45 | adapters.push_back(adapter); 46 | hr_check(adapter->GetDesc(&desc)); 47 | if (desc.VendorId == INTEL_VENDOR_ID) { 48 | this->intelAdapter = adapter; 49 | return; 50 | } 51 | } 52 | 53 | dxgiFactory->Release(); 54 | } 55 | 56 | RECT D3DContextBase::getFullDisplayRECT() const { 57 | // Calculating the whole virtual desktop size 58 | IDXGIOutput* output; 59 | LONG leftmost = 0, topmost = 0, rightmost = 0, bottommost = 0; 60 | for (IDXGIAdapter* a : adapters) { 61 | for (UINT i = 0; a->EnumOutputs(i, &output) != DXGI_ERROR_NOT_FOUND; i++) { 62 | DXGI_OUTPUT_DESC outputDesc; 63 | hr_check(output->GetDesc(&outputDesc)); 64 | if (outputDesc.DesktopCoordinates.left < leftmost) leftmost = outputDesc.DesktopCoordinates.left; 65 | if (outputDesc.DesktopCoordinates.top < topmost) topmost = outputDesc.DesktopCoordinates.top; 66 | if (outputDesc.DesktopCoordinates.right > rightmost) rightmost = outputDesc.DesktopCoordinates.right; 67 | if (outputDesc.DesktopCoordinates.bottom > bottommost) bottommost = outputDesc.DesktopCoordinates.bottom; 68 | output->Release(); 69 | } 70 | } 71 | 72 | return { leftmost, topmost, rightmost, bottommost }; 73 | } 74 | 75 | // If there is an Intel adapter in the system, we have to synchronize with it manually, 76 | // because we can face flickering instead. No idea, why, but "immediate" 77 | // rendering on Intel GPU is still not immediate. 78 | // 79 | // This one should be called between the drawing function and the swapChain->Present() call 80 | void D3DContextBase::lookForIntelOutput(const RECT& position) { 81 | if (intelAdapterFirstOutput != nullptr) { 82 | intelAdapterFirstOutput->Release(); 83 | intelAdapterFirstOutput = nullptr; 84 | } 85 | if (intelAdapter != nullptr) { 86 | IDXGIOutput* output; 87 | for (UINT i = 0; intelAdapter->EnumOutputs(i, &output) != DXGI_ERROR_NOT_FOUND; i++) { 88 | DXGI_OUTPUT_DESC outputDesc; 89 | hr_check(output->GetDesc(&outputDesc)); 90 | if (outputDesc.Monitor != nullptr /*&& 91 | outputDesc.DesktopCoordinates.left <= position.right && 92 | outputDesc.DesktopCoordinates.top <= position.bottom && 93 | outputDesc.DesktopCoordinates.right >= position.right && 94 | outputDesc.DesktopCoordinates.bottom >= position.bottom*/) { 95 | 96 | intelAdapterFirstOutput = output; 97 | //std::cout << i << std::endl; 98 | break; 99 | } else { 100 | output->Release(); 101 | } 102 | } 103 | } 104 | } 105 | 106 | void D3DContextBase::syncIntelOutput() const { 107 | if (intelAdapterFirstOutput != nullptr) { 108 | hr_check(intelAdapterFirstOutput->WaitForVBlank()); 109 | } 110 | } 111 | 112 | D3DContextBase::~D3DContextBase() { 113 | if (dxgiFactory != nullptr) dxgiFactory->Release(); 114 | if (intelAdapterFirstOutput != nullptr) { intelAdapterFirstOutput->Release(); } 115 | for (IDXGIAdapter* a : adapters) { a->Release(); } 116 | } 117 | 118 | -------------------------------------------------------------------------------- /D3DContext.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Base.h" 4 | #include "GraphicContents.h" 5 | 6 | #if defined(USE_DX11) 7 | #include 8 | #include 9 | #elif defined(USE_DX12) 10 | #include 11 | #include 12 | #else 13 | #error "You should set either USE_DX11 or USE_DX12" 14 | #endif 15 | 16 | #include 17 | #include 18 | 19 | // The base class for the Direct3D contexts. 20 | // Contains common (mostly DXGI) logic between the DirectX versions 21 | struct D3DContextBase : public Base { 22 | #if defined(USE_DX11) 23 | ID3D11Device *device; 24 | #elif defined(USE_DX12) 25 | ID3D12Device *device; 26 | #else 27 | #error "You should set either USE_DX11 or USE_DX12" 28 | #endif 29 | std::shared_ptr contents; 30 | 31 | IDXGIAdapter* intelAdapter = nullptr; 32 | IDXGIFactory2* dxgiFactory = nullptr; 33 | 34 | IDXGIOutput* intelAdapterFirstOutput = nullptr; 35 | std::vector adapters; 36 | 37 | void checkDeviceRemoved(HRESULT hr) const; 38 | static bool checkRECTsIntersect(const RECT& r1, const RECT& r2); 39 | static bool checkRECTContainsPoint(const RECT& r, LONG x, LONG y); 40 | 41 | explicit D3DContextBase(std::shared_ptr contents); 42 | 43 | // If there is an Intel adapter in the system, we have to synchronize with it manually, 44 | // because we can face flickering instead. No idea, why, but "immediate" 45 | // rendering on Intel GPU is still not immediate. 46 | void lookForIntelOutput(const RECT& position); 47 | 48 | // This one should be called between the drawing function and the swapChain->Present() call 49 | void syncIntelOutput() const; 50 | 51 | RECT getFullDisplayRECT() const; 52 | 53 | virtual ~D3DContextBase(); 54 | }; 55 | 56 | 57 | // The Direct3D-specific context. Depends on the DirectX version flags 58 | struct D3DContext : public D3DContextBase { 59 | #if defined(USE_DX11) 60 | ID3D11DeviceContext *deviceContext; 61 | IDXGISwapChain1 *swapChain; 62 | ID3D11ShaderResourceView* imageTextureView = nullptr; 63 | void DrawTriangle(int width, int height, 64 | ID3D11Device* device, 65 | ID3D11DeviceContext* device_context, 66 | IDXGISwapChain1* swap_chain, 67 | std::shared_ptr contents); 68 | 69 | #elif defined(USE_DX12) 70 | struct FrameContext 71 | { 72 | ID3D12CommandAllocator* CommandAllocator; 73 | UINT64 FenceValue; 74 | }; 75 | 76 | static int const NUM_BACK_BUFFERS = 3; 77 | static int const NUM_FRAMES_IN_FLIGHT = 3; 78 | 79 | FrameContext g_frameContext[NUM_FRAMES_IN_FLIGHT] = {}; 80 | UINT g_frameIndex = 0; 81 | 82 | ID3D12DescriptorHeap* descriptorHeap; 83 | ID3D12DescriptorHeap* g_pd3dRtvDescHeap = nullptr; 84 | ID3D12DescriptorHeap* g_pd3dSrvDescHeap = nullptr; 85 | ID3D12CommandQueue* g_pd3dCommandQueue = nullptr; 86 | ID3D12GraphicsCommandList* g_pd3dCommandList = nullptr; 87 | ID3D12Fence* g_fence = nullptr; 88 | HANDLE g_fenceEvent = nullptr; 89 | UINT64 g_fenceLastSignaledValue = 0; 90 | IDXGISwapChain3* swapChain = nullptr; 91 | HANDLE g_hSwapChainWaitableObject = nullptr; 92 | ID3D12Resource* g_mainRenderTargetResource[NUM_BACK_BUFFERS] = {}; 93 | D3D12_CPU_DESCRIPTOR_HANDLE g_mainRenderTargetDescriptor[NUM_BACK_BUFFERS] = {}; 94 | ID3D12Resource* imageTextureView = nullptr; 95 | std::unique_ptr ddsData; 96 | std::vector subresources; 97 | 98 | private: 99 | struct DrawingCache { 100 | ID3D12Resource* vertex_buffer = nullptr; 101 | ID3D12PipelineState *pipeline = nullptr; 102 | ~DrawingCache() { 103 | if (vertex_buffer != nullptr) { vertex_buffer->Release(); } 104 | if (pipeline != nullptr) { pipeline->Release(); } 105 | } 106 | }; 107 | DrawingCache drawingCache; 108 | 109 | bool CreateDeviceD3D(); 110 | void CleanupDeviceD3D(); 111 | void CreateRenderTarget(); 112 | void CleanupRenderTarget(); 113 | void WaitForLastSubmittedFrame(); 114 | void FlushGPU(); 115 | FrameContext* WaitForNextFrameResources(); 116 | static void DrawTriangle(int width, int height, 117 | ID3D12Device* device, 118 | ID3D12GraphicsCommandList* graphics_command_list, 119 | ID3D12CommandQueue* command_queue, 120 | IDXGISwapChain3* swap_chain, 121 | ID3D12Resource* mainRenderTargetResource, 122 | D3D12_CPU_DESCRIPTOR_HANDLE& mainRenderTargetDescriptor, 123 | FrameContext* frameCtx, DrawingCache* vertex_buffer, 124 | std::shared_ptr contents); 125 | 126 | public: 127 | #else 128 | #error "You should set either USE_DX11 or USE_DX12" 129 | #endif 130 | 131 | explicit D3DContext(std::shared_ptr contents); 132 | void reposition(const RECT& position); 133 | ~D3DContext() override; 134 | }; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # A Never Flickering Direct3D 11/12 Window POC 2 | ![CMake on Windows 2019](https://github.com/bigfatbrowncat/noflicker_directx_window/actions/workflows/cmake-single-platform.yml/badge.svg) 3 | 4 | This application is originated from a [solution posted on gamedev.net](https://www.gamedev.net/forums/topic/708865-dxgi-flip-model-flickering-during-live-resize/) by the user named [jbatez](https://www.gamedev.net/jbatez): 5 | 6 | The original annotation: 7 | >For years now I've had this annoying little problem where the right and bottom edges of windows backed by DXGI flip model swap chains flicker during live window resizing (e.g. when grabbing the bottom right corner of a window and dragging). I've tried dozens if not hundreds of different techniques and today I finally stumbled on one that works! 8 | 9 |
10 | 11 |
12 |

The original code provided was built upon DirectX 11 API and included only empty screen rendering. This project adds some features to it. Currently, it provides:

13 | 14 | * The classic "rainbow triangle" render 15 | * Both Direct3D 11 & 12 backends 16 | * A workaround for buggy Intel GPUs (described below in the "Known Issues" paragraph) 17 | 18 | ## The Original Description 19 | 20 | The original solution description by [jbatez](https://www.gamedev.net/jbatez) contains the 21 | explanation of the general design idea: 22 | 23 | >TLDR: 24 | > 25 | >Step 1. Use CreateSwapChainForComposition() instead of CreateSwapChain()/CreateSwapChainForHwnd(). 26 | > 27 | >Step 2. Call ResizeBuffers() in WM_NCCALCSIZE instead of WM_SIZE. 28 | > 29 | >Step 3. Render and present a new frame before returning from WM_NCCALCSIZE. 30 | The long answer: 31 | > 32 | >Grab a modern graphics-accelerated application window (e.g. Steam) by the bottom right corner and drag. You see nasty flickering with black and/or white lines, right? This flickering has haunted me for years. Every time I tried to use the DXGI flip model, no matter what I did, no matter what API's I called in what order or however careful I was with synchronization, I always encountered graphical glitches like this. 33 | > 34 | >Now try the same thing with an ancient application (e.g. Notepad). It's beautiful. No glitches at all. Clearly it's possible to get this right. What the heck's going on? 35 | > 36 | >I have no idea what actually causes this. I've seen it happen on AMD and NVIDIA graphics cards, so I doubt it's a driver problem. I assume it has something to do with how DWM synchronizes with DXGI-based apps, but since it's all closed source and Microsoft's documentation is light on details, it's hard to know for sure. 37 | > 38 | >I recently came across this (https://docs.microsoft.com/en-us/archive/msdn-magazine/2014/june/windows-with-c-high-performance-window-layering-using-the-windows-composition-engine) little gem of an article and decided to give DirectComposition a try. I'd heard of DirectComposition before, but it's documentation makes it sound like it's meant for GUI-based applications with fancy animations, not games that just want an efficient way to get their pixels on the screen. 39 | > 40 | >DirectComposition gave me similar issues at first. Instead of black and white flickering I was seeing through temporary gaps in the window to the desktop behind. And it was dragging from the top/left edges instead of the bottom/right that was causing problems. But these were totally new problems I'd never seen before, so I kept trying. 41 | > 42 | >I stuck with it and, lo and behold, I found a technique that works! The trick is to resize your swap chain buffers and present a frame BEFORE the window actually resizes. In my case, that meant using WM_NCCALCSIZE instead of WM_SIZE. I've tried this before with a CreateSwapChainForHwnd() swap chain, but for some reason this technique only works when using DirectComposition. Example code below. 43 | > 44 | >Be warned: I've read bug reports of capture software not working with DirectComposition. It sounds like there are workarounds, though, so it's probably just a matter of software catching up and becoming DirectComposition-aware. 45 | 46 | ## Known Issues 47 | 48 | Although, the application claims that it never flickers, alas, on some laptops it still flickers a bit. 49 | 50 | The configuration that needed to be fixed specifically was: i7-1195G7 + integrated GPU + Windows 10 aboard. 51 | 52 | It seems to be an unfortunate GPU/drivers behaviour which Intel is infamous for. 53 | The synchronization of Direct3D apparently is slightly broken on this configuration 54 | and the resizing flickers due to a race condition somewhere between Direct3D 55 | and the GPU driver. There is no way (that I could find) to fix this directly, but 56 | there is a workaround that reduces the impact. 57 | 58 | This workaround is represented by two functions: `D3DContextBase::lookForIntelOutput()` and `D3DContextBase::syncIntelOutput()`. 59 | The first one finds the intel GPU in the system, the second one waits for a VBlank on this GPU. It should be called in a 60 | proper place (between the redrawing and the `swapChain->Present()` call). 61 | 62 | This small workaround doesn't affect any discrete GPU-based systems in any way, but reduces the probability of flicker 63 | on the Intel iGPU from 30-50% to less than 1%. Sad, but true, it can't fix the issue completely. 64 | 65 | Especially, the problem is still visible on multidisplay systems, when the window is shown partially 66 | on one screen, partially on another. Luckily, this is a really rare use case. 67 | 68 | Anyway, any solution to this issue is always welcome in this repo as a Pull Request! 69 | -------------------------------------------------------------------------------- /DDSTextureLoader.h: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------------------------------------------- 2 | // File: DDSTextureLoader.h 3 | // 4 | // Functions for loading a DDS texture and creating a Direct3D runtime resource for it 5 | // 6 | // Note these functions are useful as a light-weight runtime loader for DDS files. For 7 | // a full-featured DDS file reader, writer, and texture processing pipeline see 8 | // the 'Texconv' sample and the 'DirectXTex' library. 9 | // 10 | // Copyright (c) Microsoft Corporation. 11 | // Licensed under the MIT License. 12 | // 13 | // http://go.microsoft.com/fwlink/?LinkId=248926 14 | // http://go.microsoft.com/fwlink/?LinkId=248929 15 | //-------------------------------------------------------------------------------------- 16 | 17 | #pragma once 18 | 19 | #include 20 | 21 | #include 22 | 23 | 24 | namespace DirectX 25 | { 26 | #ifndef DDS_ALPHA_MODE_DEFINED 27 | #define DDS_ALPHA_MODE_DEFINED 28 | enum DDS_ALPHA_MODE : uint32_t 29 | { 30 | DDS_ALPHA_MODE_UNKNOWN = 0, 31 | DDS_ALPHA_MODE_STRAIGHT = 1, 32 | DDS_ALPHA_MODE_PREMULTIPLIED = 2, 33 | DDS_ALPHA_MODE_OPAQUE = 3, 34 | DDS_ALPHA_MODE_CUSTOM = 4, 35 | }; 36 | #endif 37 | 38 | #ifndef DDS_LOADER_FLAGS_DEFINED 39 | #define DDS_LOADER_FLAGS_DEFINED 40 | 41 | enum DDS_LOADER_FLAGS : uint32_t 42 | { 43 | DDS_LOADER_DEFAULT = 0, 44 | DDS_LOADER_FORCE_SRGB = 0x1, 45 | DDS_LOADER_IGNORE_SRGB = 0x2, 46 | }; 47 | 48 | #ifdef __clang__ 49 | #pragma clang diagnostic push 50 | #pragma clang diagnostic ignored "-Wdeprecated-dynamic-exception-spec" 51 | #endif 52 | 53 | DEFINE_ENUM_FLAG_OPERATORS(DDS_LOADER_FLAGS); 54 | 55 | #ifdef __clang__ 56 | #pragma clang diagnostic pop 57 | #endif 58 | #endif 59 | 60 | // Standard version 61 | HRESULT CreateDDSTextureFromMemory( 62 | _In_ ID3D11Device* d3dDevice, 63 | _In_reads_bytes_(ddsDataSize) const uint8_t* ddsData, 64 | _In_ size_t ddsDataSize, 65 | _Outptr_opt_ ID3D11Resource** texture, 66 | _Outptr_opt_ ID3D11ShaderResourceView** textureView, 67 | _In_ size_t maxsize = 0, 68 | _Out_opt_ DDS_ALPHA_MODE* alphaMode = nullptr) noexcept; 69 | 70 | HRESULT CreateDDSTextureFromFile( 71 | _In_ ID3D11Device* d3dDevice, 72 | _In_z_ const wchar_t* szFileName, 73 | _Outptr_opt_ ID3D11Resource** texture, 74 | _Outptr_opt_ ID3D11ShaderResourceView** textureView, 75 | _In_ size_t maxsize = 0, 76 | _Out_opt_ DDS_ALPHA_MODE* alphaMode = nullptr) noexcept; 77 | 78 | // Standard version with optional auto-gen mipmap support 79 | HRESULT CreateDDSTextureFromMemory( 80 | _In_ ID3D11Device* d3dDevice, 81 | _In_opt_ ID3D11DeviceContext* d3dContext, 82 | _In_reads_bytes_(ddsDataSize) const uint8_t* ddsData, 83 | _In_ size_t ddsDataSize, 84 | _Outptr_opt_ ID3D11Resource** texture, 85 | _Outptr_opt_ ID3D11ShaderResourceView** textureView, 86 | _In_ size_t maxsize = 0, 87 | _Out_opt_ DDS_ALPHA_MODE* alphaMode = nullptr) noexcept; 88 | 89 | HRESULT CreateDDSTextureFromFile( 90 | _In_ ID3D11Device* d3dDevice, 91 | _In_opt_ ID3D11DeviceContext* d3dContext, 92 | _In_z_ const wchar_t* szFileName, 93 | _Outptr_opt_ ID3D11Resource** texture, 94 | _Outptr_opt_ ID3D11ShaderResourceView** textureView, 95 | _In_ size_t maxsize = 0, 96 | _Out_opt_ DDS_ALPHA_MODE* alphaMode = nullptr) noexcept; 97 | 98 | // Extended version 99 | HRESULT CreateDDSTextureFromMemoryEx( 100 | _In_ ID3D11Device* d3dDevice, 101 | _In_reads_bytes_(ddsDataSize) const uint8_t* ddsData, 102 | _In_ size_t ddsDataSize, 103 | _In_ size_t maxsize, 104 | _In_ D3D11_USAGE usage, 105 | _In_ unsigned int bindFlags, 106 | _In_ unsigned int cpuAccessFlags, 107 | _In_ unsigned int miscFlags, 108 | _In_ DDS_LOADER_FLAGS loadFlags, 109 | _Outptr_opt_ ID3D11Resource** texture, 110 | _Outptr_opt_ ID3D11ShaderResourceView** textureView, 111 | _Out_opt_ DDS_ALPHA_MODE* alphaMode = nullptr) noexcept; 112 | 113 | HRESULT CreateDDSTextureFromFileEx( 114 | _In_ ID3D11Device* d3dDevice, 115 | _In_z_ const wchar_t* szFileName, 116 | _In_ size_t maxsize, 117 | _In_ D3D11_USAGE usage, 118 | _In_ unsigned int bindFlags, 119 | _In_ unsigned int cpuAccessFlags, 120 | _In_ unsigned int miscFlags, 121 | _In_ DDS_LOADER_FLAGS loadFlags, 122 | _Outptr_opt_ ID3D11Resource** texture, 123 | _Outptr_opt_ ID3D11ShaderResourceView** textureView, 124 | _Out_opt_ DDS_ALPHA_MODE* alphaMode = nullptr) noexcept; 125 | 126 | // Extended version with optional auto-gen mipmap support 127 | HRESULT CreateDDSTextureFromMemoryEx( 128 | _In_ ID3D11Device* d3dDevice, 129 | _In_opt_ ID3D11DeviceContext* d3dContext, 130 | _In_reads_bytes_(ddsDataSize) const uint8_t* ddsData, 131 | _In_ size_t ddsDataSize, 132 | _In_ size_t maxsize, 133 | _In_ D3D11_USAGE usage, 134 | _In_ unsigned int bindFlags, 135 | _In_ unsigned int cpuAccessFlags, 136 | _In_ unsigned int miscFlags, 137 | _In_ DDS_LOADER_FLAGS loadFlags, 138 | _Outptr_opt_ ID3D11Resource** texture, 139 | _Outptr_opt_ ID3D11ShaderResourceView** textureView, 140 | _Out_opt_ DDS_ALPHA_MODE* alphaMode = nullptr) noexcept; 141 | 142 | HRESULT CreateDDSTextureFromFileEx( 143 | _In_ ID3D11Device* d3dDevice, 144 | _In_opt_ ID3D11DeviceContext* d3dContext, 145 | _In_z_ const wchar_t* szFileName, 146 | _In_ size_t maxsize, 147 | _In_ D3D11_USAGE usage, 148 | _In_ unsigned int bindFlags, 149 | _In_ unsigned int cpuAccessFlags, 150 | _In_ unsigned int miscFlags, 151 | _In_ DDS_LOADER_FLAGS loadFlags, 152 | _Outptr_opt_ ID3D11Resource** texture, 153 | _Outptr_opt_ ID3D11ShaderResourceView** textureView, 154 | _Out_opt_ DDS_ALPHA_MODE* alphaMode = nullptr) noexcept; 155 | } -------------------------------------------------------------------------------- /D3DContext_DX11.cpp: -------------------------------------------------------------------------------- 1 | #include "D3DContext.h" 2 | #include "DDSTextureLoader.h" 3 | 4 | #include "d3dcompiler.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | using namespace DirectX; 12 | 13 | void D3DContext::DrawTriangle(int width, int height, 14 | ID3D11Device* device, 15 | ID3D11DeviceContext* device_context, 16 | IDXGISwapChain1* swap_chain, 17 | std::shared_ptr contents) { 18 | 19 | auto vertices = contents->getVertices(); 20 | 21 | D3D11_BUFFER_DESC vb_desc; 22 | ZeroMemory(&vb_desc, sizeof(vb_desc)); 23 | vb_desc.ByteWidth = (UINT)(vertices.size() * sizeof(TextureVertex)); 24 | vb_desc.Usage = D3D11_USAGE_DEFAULT; 25 | vb_desc.BindFlags = D3D11_BIND_VERTEX_BUFFER; 26 | vb_desc.CPUAccessFlags = 0; 27 | vb_desc.MiscFlags = 0; 28 | vb_desc.StructureByteStride = sizeof(TextureVertex); 29 | 30 | D3D11_SUBRESOURCE_DATA vb_data; 31 | ZeroMemory(&vb_data, sizeof(vb_data)); 32 | vb_data.pSysMem = &vertices[0]; 33 | 34 | { 35 | ID3D11Buffer *vertex_buffer; 36 | const UINT stride = sizeof(TextureVertex); 37 | const UINT offset = 0; 38 | device->CreateBuffer(&vb_desc, &vb_data, &vertex_buffer); 39 | device_context->IASetVertexBuffers(0, 1, &vertex_buffer, &stride, &offset); 40 | 41 | { 42 | ID3D11InputLayout *input_layout; 43 | ID3D11VertexShader *vertex_shader; 44 | ID3D11PixelShader *pixel_shader; 45 | { 46 | ID3DBlob *vs, *vs_error; 47 | ID3DBlob *ps, *ps_error; 48 | 49 | std::string shader_code = contents->getShader(); 50 | 51 | auto hresult = D3DCompile2(shader_code.c_str(), shader_code.length(), 52 | nullptr, 53 | nullptr, nullptr, "PSMain", "ps_4_0", D3DCOMPILE_DEBUG, 0, 54 | 0, nullptr, 0, 55 | &ps, &ps_error); 56 | 57 | if (ps_error != nullptr) { 58 | std::string err = "Pixel shader compilation error: "; 59 | err += reinterpret_cast( ps_error->GetBufferPointer()); 60 | throw std::runtime_error(err); 61 | } 62 | 63 | hr_check(device->CreatePixelShader(ps->GetBufferPointer(), ps->GetBufferSize(), nullptr, &pixel_shader)); 64 | device_context->PSSetShader(pixel_shader, nullptr, 0); 65 | 66 | hr_check(D3DCompile2(shader_code.c_str(), shader_code.length(), 67 | nullptr, 68 | nullptr, nullptr, "VSMain", "vs_4_0", D3DCOMPILE_DEBUG, 0, 69 | 0, nullptr, 0, 70 | &vs, &vs_error)); 71 | 72 | if (vs_error != nullptr) { 73 | throw std::runtime_error("Vertex shader compilation error"); 74 | } 75 | 76 | hr_check(device->CreateVertexShader(vs->GetBufferPointer(), vs->GetBufferSize(), nullptr, &vertex_shader)); 77 | device_context->VSSetShader(vertex_shader, nullptr, 0); 78 | device_context->PSSetShaderResources( 0, 1, &imageTextureView ); 79 | 80 | D3D11_INPUT_ELEMENT_DESC element_desc[] = 81 | { 82 | { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, 83 | { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 }, 84 | //{ "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 } 85 | }; 86 | 87 | hr_check(device->CreateInputLayout(element_desc, sizeof(element_desc) / sizeof(D3D11_INPUT_ELEMENT_DESC), vs->GetBufferPointer(), vs->GetBufferSize(), &input_layout)); 88 | 89 | device_context->IASetInputLayout(input_layout); 90 | 91 | vs->Release(); 92 | ps->Release(); 93 | if (vs_error != nullptr) vs_error->Release(); 94 | if (ps_error != nullptr) ps_error->Release(); 95 | } 96 | 97 | device_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);//D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); 98 | 99 | { 100 | D3D11_VIEWPORT viewport; 101 | viewport.MinDepth = 0; 102 | viewport.MaxDepth = 1; 103 | viewport.TopLeftX = 0; 104 | viewport.TopLeftY = 0; 105 | viewport.Width = (float) width; 106 | viewport.Height = (float) height; 107 | device_context->RSSetViewports(1u, &viewport); 108 | } 109 | 110 | FLOAT color[] = {0.0f, 0.2f, 0.4f, 1.0f}; 111 | // Render to the target 112 | { 113 | ID3D11RenderTargetView *rtv; 114 | ID3D11Resource *buffer; 115 | 116 | hr_check(swap_chain->GetBuffer(0, IID_PPV_ARGS(&buffer))); 117 | hr_check(device->CreateRenderTargetView(buffer, nullptr, &rtv)); 118 | 119 | // After this point and before swapChin->Present(), we should render as fast as possible 120 | device_context->ClearRenderTargetView(rtv, color); 121 | 122 | device_context->OMSetRenderTargets(1, &rtv, nullptr); 123 | device_context->Draw((UINT)vertices.size(), 0); 124 | 125 | syncIntelOutput(); 126 | 127 | // Discard outstanding queued presents and queue a frame with the new size ASAP. 128 | checkDeviceRemoved(swapChain->Present(0, DXGI_PRESENT_RESTART)); 129 | 130 | buffer->Release(); 131 | rtv->Release(); 132 | } 133 | 134 | vertex_shader->Release(); 135 | pixel_shader->Release(); 136 | input_layout->Release(); 137 | } 138 | vertex_buffer->Release(); 139 | } 140 | } 141 | 142 | 143 | D3DContext::D3DContext(std::shared_ptr contents): D3DContextBase(std::move(contents)), deviceContext(nullptr), swapChain(nullptr) { 144 | // Create the D3D device. 145 | hr_check(D3D11CreateDevice( 146 | nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, D3D11_CREATE_DEVICE_BGRA_SUPPORT, 147 | nullptr, 0, D3D11_SDK_VERSION, &device, nullptr, &deviceContext)); 148 | 149 | // Create the swap chain. 150 | DXGI_SWAP_CHAIN_DESC1 scd = {}; 151 | // Just use a minimal size for now. WM_NCCALCSIZE will resize when necessary. 152 | scd.Width = 1; 153 | scd.Height = 1; 154 | scd.Format = DXGI_FORMAT_B8G8R8A8_UNORM; 155 | scd.SampleDesc.Count = 1; 156 | scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; 157 | scd.BufferCount = 2; 158 | // TODO: Determine if PRESENT_DO_NOT_SEQUENCE is safe to use with SWAP_EFFECT_FLIP_DISCARD. 159 | scd.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; 160 | scd.AlphaMode = DXGI_ALPHA_MODE_IGNORE; 161 | hr_check(dxgiFactory->CreateSwapChainForComposition(device, &scd, nullptr, &swapChain)); 162 | 163 | hr_check(CreateDDSTextureFromFile( this->device, L"grass.dds", nullptr, &imageTextureView )); 164 | 165 | this->reposition(getFullDisplayRECT()); 166 | } 167 | 168 | void D3DContext::reposition(const RECT& position) { 169 | int width = position.right - position.left; 170 | int height = position.bottom - position.top; 171 | 172 | lookForIntelOutput(position); 173 | 174 | // A real app might want to compare these dimensions with the current swap chain 175 | // dimensions and skip all this if they're unchanged. 176 | checkDeviceRemoved(swapChain->ResizeBuffers(0, width, height, DXGI_FORMAT_UNKNOWN, DXGI_SWAP_CHAIN_FLAG_GDI_COMPATIBLE)); 177 | 178 | DrawTriangle(width, height, device, deviceContext, swapChain, contents); 179 | 180 | //Sleep(500); 181 | // Wait for a vblank to really make sure our frame with the new size is ready before 182 | // the window finishes resizing. 183 | // TODO: Determine why this is necessary at all. Why isn't one Present() enough? 184 | // TODO: Determine if there's a way to wait for vblank without calling Present(). 185 | // TODO: Determine if DO_NOT_SEQUENCE is safe to use with SWAP_EFFECT_FLIP_DISCARD. 186 | checkDeviceRemoved(swapChain->Present(1, DXGI_PRESENT_DO_NOT_SEQUENCE)); 187 | } 188 | 189 | D3DContext::~D3DContext() { 190 | if (imageTextureView) { imageTextureView->Release(); imageTextureView = nullptr; } 191 | if (swapChain) { swapChain->SetFullscreenState(false, nullptr); swapChain->Release(); swapChain = nullptr; } 192 | if (deviceContext) { deviceContext->Release(); deviceContext = nullptr; } 193 | if (device) { device->Release(); device = nullptr; } 194 | } -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | // Local headers 2 | #include "D3DContext.h" 3 | #include "DCompContext.h" 4 | #include "GraphicContents.h" 5 | 6 | // OS headers 7 | #include 8 | 9 | // C++ stl 10 | #include 11 | #include 12 | 13 | #if defined(USE_DX12) 14 | class TriangleGraphicContents : public GraphicContents { 15 | private: 16 | int width = 0, height = 0; 17 | 18 | public: 19 | void updateLayout(int width, int height) override { 20 | this->width = width; this->height = height; 21 | 22 | // Uncomment this fake resizing load here to see how the app handles it 23 | // 100ms is a huge time pretty enough to recalculate even a very complicated layout 24 | // 25 | // Sleep(100); 26 | } 27 | 28 | std::vector getVertices() override { 29 | float aspect = (float) width / (float) height; 30 | float k = 760.f / (float) width; 31 | float sin60 = sqrtf(3.f) / 2; 32 | float d = 0.3f; 33 | std::vector vertices = { 34 | {0.0f * k, 0.5f * sin60 * aspect * k, 0.0f, 0.5f, 0.0f, 0.5f}, 35 | {0.5f * k, -0.5f * sin60 * aspect * k, 0.0f, 0.5f + d, 1.0f, 0.5f}, 36 | {-0.5f * k, -0.5f * sin60 * aspect * k, 0.0f, 0.5f - d, 1.0f, 0.5f} 37 | }; 38 | return vertices; 39 | } 40 | 41 | // std::string getShader() override { 42 | // return { 43 | // "Texture2D txDiffuse : register( t0 );\n" 44 | // "SamplerState samLinear : register( s0 );\n" 45 | // "\n" 46 | // "struct VSInput {\n" 47 | // " float4 position : POSITION;\n" 48 | // " float2 Tex : TEXCOORD0;\n" 49 | // "};\n" 50 | // "struct PSInput {\n" 51 | // " float4 position : SV_POSITION;\n" 52 | // " float2 Tex : TEXCOORD0;\n" 53 | // "};\n" 54 | // "PSInput VSMain(VSInput input) {\n" 55 | // " PSInput output;\n" 56 | // " output.position = input.position;\n" 57 | // " output.Tex = input.Tex;\n" 58 | // " return output;\n" 59 | // "}\n" 60 | // "float4 PSMain(PSInput input) : SV_TARGET {\n" 61 | // " return txDiffuse.Sample( samLinear, input.Tex );\n" 62 | // "}\n" 63 | // }; 64 | // } 65 | std::string getShader() override { 66 | return { 67 | "struct PSInput {\n" 68 | " float4 position : SV_POSITION;\n" 69 | " float4 color : COLOR;\n" 70 | "};\n" 71 | "PSInput VSMain(float4 position : POSITION0, float4 color : COLOR0) {\n" 72 | " PSInput result;\n" 73 | " result.position = position;\n" 74 | " result.color = color;\n" 75 | " return result;\n" 76 | "}\n" 77 | "float4 PSMain(PSInput input) : SV_TARGET {\n" 78 | " return input.color;\n" 79 | "}\n" 80 | }; 81 | } 82 | }; 83 | #elif defined(USE_DX11) 84 | class FullScreenImageGraphicContents : public GraphicContents { 85 | private: 86 | int width = 0, height = 0; 87 | 88 | public: 89 | void updateLayout(int width, int height) override { 90 | this->width = width; this->height = height; 91 | 92 | // Uncomment this fake resizing load here to see how the app handles it 93 | // 100ms is a huge time pretty enough to recalculate even a very complicated layout 94 | // 95 | //Sleep(100); 96 | } 97 | 98 | std::vector getVertices() override { 99 | float aspect = (float) width / (float) height; 100 | float k = 1;//760.f / (float) width; 101 | //float sin60 = sqrtf(3.f) / 2; 102 | std::vector vertices = { 103 | {-1.0f * k, 1.0f * k, 0.0f, 0.0f, 0.0f}, 104 | { 1.0f * k, 1.0f * k, 0.0f, 1.0f, 0.0f}, 105 | { 1.0f * k, -1.0f * k, 0.0f, 1.0f, 1.0f}, 106 | 107 | {-1.0f * k, -1.0f * k, 0.0f, 0.0f, 1.0f}, 108 | { 1.0f * k, -1.0f * k, 0.0f, 1.0f, 1.0f}, 109 | {-1.0f * k, 1.0f * k, 0.0f, 0.0f, 0.0f}, 110 | }; 111 | return vertices; 112 | } 113 | 114 | std::string getShader() override { 115 | return { 116 | "Texture2D txDiffuse : register( t0 );\n" 117 | "SamplerState samLinear : register( s0 );\n" 118 | "\n" 119 | "struct VSInput {\n" 120 | " float4 position : POSITION;\n" 121 | " float2 Tex : TEXCOORD0;\n" 122 | "};\n" 123 | "struct PSInput {\n" 124 | " float4 position : SV_POSITION;\n" 125 | " float2 Tex : TEXCOORD0;\n" 126 | "};\n" 127 | "PSInput VSMain(VSInput input) {\n" 128 | " PSInput output;\n" 129 | " output.position = input.position;\n" 130 | " output.Tex = input.Tex;\n" 131 | " return output;\n" 132 | "}\n" 133 | "float4 PSMain(PSInput input) : SV_TARGET {\n" 134 | " return txDiffuse.Sample( samLinear, input.Tex );\n" 135 | "}\n" 136 | }; 137 | } 138 | }; 139 | #else 140 | #error "You should set either USE_DX11 or USE_DX12" 141 | #endif 142 | 143 | // Global declarations 144 | std::shared_ptr context; 145 | std::shared_ptr dcompContext; 146 | std::shared_ptr contents; 147 | bool exitPending; 148 | 149 | // Passthrough (t) if truthy. Crash otherwise. 150 | template T win32_check(T t) 151 | { 152 | if (t) return t; 153 | 154 | // Debuggers are better at displaying HRESULTs than the raw DWORD returned by GetLastError(). 155 | HRESULT hr = HRESULT_FROM_WIN32(GetLastError()); 156 | while (true) __debugbreak(); 157 | } 158 | 159 | 160 | // Win32 message handler. 161 | LRESULT window_proc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) 162 | { 163 | std::mutex draw_mutex; 164 | switch (message) 165 | { 166 | case WM_DESTROY: { 167 | // Destroy the DirectComposition context properly, 168 | // so that the window fades away beautifully. 169 | dcompContext->unbind(); 170 | } 171 | 172 | case WM_CLOSE: { 173 | exitPending = true; 174 | return 0; 175 | } 176 | 177 | case WM_NCCALCSIZE: { 178 | // Use the result of DefWindowProc's WM_NCCALCSIZE handler to get the upcoming client rect. 179 | // Technically, when wparam is TRUE, lparam points to NCCALCSIZE_PARAMS, but its first 180 | // member is a RECT with the same meaning as the one lparam points to when wparam is FALSE. 181 | DefWindowProc(hwnd, message, wparam, lparam); 182 | if (RECT *rect = (RECT *) lparam; rect->right > rect->left && rect->bottom > rect->top) { 183 | contents->updateLayout(rect->right - rect->left, rect->bottom - rect->top); 184 | context->reposition(*rect); 185 | } 186 | // We're never preserving the client area, so we always return 0. 187 | return 0; 188 | } 189 | 190 | default: 191 | return DefWindowProc(hwnd, message, wparam, lparam); 192 | } 193 | } 194 | 195 | // The app entry point. 196 | int WinMain(HINSTANCE hinstance, HINSTANCE, LPSTR, int) 197 | { 198 | #if defined(USE_DX12) 199 | contents = std::make_shared(); 200 | #elif defined(USE_DX11) 201 | contents = std::make_shared(); 202 | #else 203 | #error "You should set either USE_DX11 or USE_DX12" 204 | #endif 205 | 206 | context = std::make_shared(contents); 207 | 208 | // Register the window class. 209 | WNDCLASS wc = {}; 210 | wc.lpfnWndProc = window_proc; 211 | wc.hInstance = hinstance; 212 | wc.hCursor = win32_check(LoadCursor(nullptr, IDC_ARROW)); 213 | wc.lpszClassName = TEXT("D3DWindow"); 214 | wc.cbClsExtra = sizeof(void*); // Extra pointter 215 | win32_check(RegisterClass(&wc)); 216 | 217 | std::wstring windowTitle = L"A Never Flickering DirectX Window"; 218 | #if defined(USE_DX11) 219 | windowTitle += L" [Direct3D 11]"; 220 | #elif defined(USE_DX12) 221 | windowTitle += L" [Direct3D 12]"; 222 | #else 223 | #error "Either USE_DX11 or USE_DX12 should be chosen" 224 | #endif 225 | 226 | // Create the window. We can use WS_EX_NOREDIRECTIONBITMAP 227 | // since all our presentation is happening through DirectComposition. 228 | HWND hwnd = win32_check(CreateWindowEx( 229 | WS_EX_NOREDIRECTIONBITMAP, wc.lpszClassName, windowTitle.c_str(), 230 | WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, nullptr, nullptr, hinstance, nullptr)); 231 | 232 | // The DCompContext creation/destruction is fundamentally asymmetric. 233 | // We are cleaning up the resources in WM_DESTROY, but should NOT create the object in WM_CREATE. 234 | // Instead, we create it here between construction of the window and showing it 235 | dcompContext = std::make_shared(hwnd, context); 236 | 237 | // Show the window and enter the message loop. 238 | ShowWindow(hwnd, SW_SHOWNORMAL); 239 | 240 | exitPending = false; 241 | while (!exitPending) 242 | { 243 | MSG msg; 244 | win32_check(GetMessage(&msg, nullptr, 0, 0) > 0); 245 | TranslateMessage(&msg); 246 | DispatchMessage(&msg); 247 | } 248 | 249 | return 0; 250 | } -------------------------------------------------------------------------------- /D3DContext_DX12.cpp: -------------------------------------------------------------------------------- 1 | #include "D3DContext.h" 2 | //#include "DDSTextureLoader12.h" 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | //using namespace DirectX; 11 | 12 | // The debug layer is broken and crashes the app. Don't enable it 13 | //#ifdef _DEBUG 14 | //#define DX12_ENABLE_DEBUG_LAYER 15 | //#endif 16 | 17 | #ifdef DX12_ENABLE_DEBUG_LAYER 18 | #include 19 | #pragma comment(lib, "dxguid.lib") 20 | #endif 21 | 22 | 23 | void D3DContext::DrawTriangle(int width, int height, 24 | ID3D12Device* device, 25 | ID3D12GraphicsCommandList* graphics_command_list, 26 | ID3D12CommandQueue* command_queue, 27 | IDXGISwapChain3* swap_chain, 28 | ID3D12Resource* mainRenderTargetResource, 29 | D3D12_CPU_DESCRIPTOR_HANDLE& mainRenderTargetDescriptor, 30 | FrameContext* frameCtx, 31 | D3DContext::DrawingCache* drawing_cache, 32 | std::shared_ptr contents) { 33 | auto vertices = contents->getVertices(); 34 | 35 | D3D12_RESOURCE_DESC vb_desc = { 36 | .Dimension = D3D12_RESOURCE_DIMENSION_BUFFER, 37 | .Alignment = 0, 38 | .Width = sizeof(RGBAVertex) * vertices.size(), 39 | .Height = 1, 40 | .DepthOrArraySize = 1, 41 | .MipLevels = 1, 42 | .Format = DXGI_FORMAT_UNKNOWN, 43 | .SampleDesc = { .Count = 1, .Quality = 0 }, 44 | .Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR, 45 | .Flags = D3D12_RESOURCE_FLAG_NONE, 46 | }; 47 | 48 | if (drawing_cache->vertex_buffer == nullptr) { 49 | D3D12_HEAP_PROPERTIES heap_props = { 50 | .Type = D3D12_HEAP_TYPE_UPLOAD 51 | }; 52 | 53 | hr_check(device->CreateCommittedResource( 54 | &heap_props, 55 | D3D12_HEAP_FLAG_NONE, 56 | &vb_desc, 57 | D3D12_RESOURCE_STATE_GENERIC_READ, 58 | nullptr, 59 | IID_PPV_ARGS(&drawing_cache->vertex_buffer))); 60 | } 61 | 62 | { 63 | void *gpu_data = nullptr; 64 | D3D12_RANGE read_range = {0, 0}; // CPU isn't going to read this data, only write 65 | hr_check(drawing_cache->vertex_buffer->Map(0, &read_range, &gpu_data)); 66 | memcpy(gpu_data, &vertices[0], vb_desc.Width); 67 | drawing_cache->vertex_buffer->Unmap(0, nullptr); 68 | } 69 | 70 | D3D12_VERTEX_BUFFER_VIEW vertex_buffer_view = { 71 | .BufferLocation = drawing_cache->vertex_buffer->GetGPUVirtualAddress(), 72 | .SizeInBytes = static_cast(sizeof(RGBAVertex) * vertices.size()), 73 | .StrideInBytes = sizeof(RGBAVertex) 74 | }; 75 | 76 | ID3D12RootSignature *rootSignature; 77 | 78 | { 79 | ID3DBlob *vs, *vs_error; 80 | ID3DBlob *ps, *ps_error; 81 | 82 | std::string shader_code = contents->getShader(); 83 | 84 | HRESULT hr; 85 | hr = D3DCompile2(shader_code.c_str(), shader_code.length(), 86 | nullptr, 87 | nullptr, nullptr, "PSMain", "ps_4_0", D3DCOMPILE_DEBUG, 0, 88 | 0, nullptr, 0, 89 | &ps, &ps_error); 90 | if ( FAILED(hr) ) 91 | { 92 | if ( ps_error ) 93 | { 94 | std::cerr << "Pixel Shader Compilation Failed: " << (char*)ps_error->GetBufferPointer() << std::endl; 95 | ps_error->Release(); 96 | } 97 | hr_check(hr); 98 | } 99 | 100 | hr = D3DCompile2(shader_code.c_str(), shader_code.length(), 101 | nullptr, 102 | nullptr, nullptr, "VSMain", "vs_4_0", D3DCOMPILE_DEBUG, 0, 103 | 0, nullptr, 0, 104 | &vs, &vs_error); 105 | if ( FAILED(hr) ) 106 | { 107 | if ( ps_error ) 108 | { 109 | std::cerr << "Vertex Shader Compilation Failed: " << (char*)ps_error->GetBufferPointer() << std::endl; 110 | ps_error->Release(); 111 | } 112 | hr_check(hr); 113 | } 114 | 115 | D3D12_INPUT_ELEMENT_DESC vertexFormat[] = 116 | { 117 | {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}, 118 | //{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, 119 | {"COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0} 120 | }; 121 | 122 | const D3D12_RENDER_TARGET_BLEND_DESC defaultBlendState = { 123 | .BlendEnable = FALSE, 124 | .LogicOpEnable = FALSE, 125 | 126 | .SrcBlend = D3D12_BLEND_ONE, 127 | .DestBlend = D3D12_BLEND_ZERO, 128 | .BlendOp = D3D12_BLEND_OP_ADD, 129 | 130 | .SrcBlendAlpha = D3D12_BLEND_ONE, 131 | .DestBlendAlpha = D3D12_BLEND_ZERO, 132 | .BlendOpAlpha = D3D12_BLEND_OP_ADD, 133 | 134 | .LogicOp = D3D12_LOGIC_OP_NOOP, 135 | .RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL, 136 | }; 137 | 138 | D3D12_VERSIONED_ROOT_SIGNATURE_DESC desc = { 139 | .Version = D3D_ROOT_SIGNATURE_VERSION_1_0, 140 | .Desc_1_0 = { 141 | .Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT, 142 | }, 143 | }; 144 | 145 | { 146 | ID3DBlob* serializedDesc = nullptr; 147 | hr_check(D3D12SerializeVersionedRootSignature(&desc, &serializedDesc, nullptr)); 148 | 149 | hr_check(device->CreateRootSignature(0, 150 | serializedDesc->GetBufferPointer(), 151 | serializedDesc->GetBufferSize(), 152 | IID_PPV_ARGS(&rootSignature))); 153 | 154 | serializedDesc->Release(); 155 | } 156 | 157 | D3D12_GRAPHICS_PIPELINE_STATE_DESC pipelineStateDesc = { 158 | .pRootSignature = rootSignature, 159 | .VS = { 160 | .pShaderBytecode = vs->GetBufferPointer(), 161 | .BytecodeLength = vs->GetBufferSize(), 162 | }, 163 | .PS = { 164 | .pShaderBytecode = ps->GetBufferPointer(), 165 | .BytecodeLength = ps->GetBufferSize(), 166 | }, 167 | .StreamOutput = {0}, 168 | .BlendState = { 169 | .AlphaToCoverageEnable = FALSE, 170 | .IndependentBlendEnable = FALSE, 171 | .RenderTarget = {defaultBlendState}, 172 | }, 173 | .SampleMask = 0xFFFFFFFF, 174 | .RasterizerState = { 175 | .FillMode = D3D12_FILL_MODE_SOLID, 176 | .CullMode = D3D12_CULL_MODE_BACK, 177 | .FrontCounterClockwise = FALSE, 178 | .DepthBias = 0, 179 | .DepthBiasClamp = 0, 180 | .SlopeScaledDepthBias = 0, 181 | .DepthClipEnable = TRUE, 182 | .MultisampleEnable = FALSE, 183 | .AntialiasedLineEnable = FALSE, 184 | .ForcedSampleCount = 0, 185 | .ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF, 186 | }, 187 | .DepthStencilState = { 188 | .DepthEnable = FALSE, 189 | .StencilEnable = FALSE, 190 | }, 191 | .InputLayout = { 192 | .pInputElementDescs = vertexFormat, 193 | .NumElements = sizeof(vertexFormat) / sizeof(D3D12_INPUT_ELEMENT_DESC)//vertexFormat_count, 194 | }, 195 | .PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE, 196 | .NumRenderTargets = 1, 197 | .RTVFormats = {DXGI_FORMAT_R8G8B8A8_UNORM}, 198 | .DSVFormat = DXGI_FORMAT_UNKNOWN, 199 | .SampleDesc = { 200 | .Count = 1, 201 | .Quality = 0, 202 | }, 203 | }; 204 | 205 | if (drawing_cache->pipeline == nullptr) { 206 | hr_check(device->CreateGraphicsPipelineState( 207 | &pipelineStateDesc, IID_PPV_ARGS(&drawing_cache->pipeline))); 208 | } 209 | 210 | vs->Release(); 211 | ps->Release(); 212 | } 213 | 214 | // Render to the target 215 | { 216 | hr_check(frameCtx->CommandAllocator->Reset()); 217 | hr_check(graphics_command_list->Reset(frameCtx->CommandAllocator, drawing_cache->pipeline)); 218 | 219 | D3D12_VIEWPORT viewport; 220 | viewport.MinDepth = 0; 221 | viewport.MaxDepth = 1; 222 | viewport.TopLeftX = 0; 223 | viewport.TopLeftY = 0; 224 | viewport.Width = (float) width; 225 | viewport.Height = (float) height; 226 | 227 | const D3D12_RECT scissorRect = { 228 | .left = 0, 229 | .top = 0, 230 | .right = width, 231 | .bottom = height, 232 | }; 233 | 234 | graphics_command_list->SetGraphicsRootSignature(rootSignature); 235 | graphics_command_list->RSSetViewports(1, &viewport); 236 | graphics_command_list->RSSetScissorRects(1, &scissorRect); 237 | 238 | D3D12_RESOURCE_BARRIER barrier = {}; 239 | barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; 240 | barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; 241 | barrier.Transition.pResource = mainRenderTargetResource; //g_mainRenderTargetResource[backBufferIdx]; 242 | barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; 243 | barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT; 244 | barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET; 245 | //graphics_command_list->Reset(frameCtx->CommandAllocator, ); 246 | graphics_command_list->ResourceBarrier(1, &barrier); 247 | 248 | graphics_command_list->OMSetRenderTargets(1, &mainRenderTargetDescriptor, FALSE, nullptr); 249 | 250 | FLOAT color[] = {0.0f, 0.2f, 0.4f, 1.0f}; 251 | graphics_command_list->ClearRenderTargetView(mainRenderTargetDescriptor, 252 | color /*clear_color_with_alpha*/, 0, nullptr); 253 | graphics_command_list->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); 254 | //graphics_command_list->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);//D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); 255 | graphics_command_list->IASetVertexBuffers(0, 1, &vertex_buffer_view); 256 | 257 | // Finally drawing the bloody triangle! 258 | graphics_command_list->DrawInstanced((UINT)vertices.size(), 1, 0, 0); 259 | 260 | barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; 261 | barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT; 262 | graphics_command_list->ResourceBarrier(1, &barrier); 263 | graphics_command_list->Close(); 264 | 265 | command_queue->ExecuteCommandLists(1, (ID3D12CommandList *const *) &graphics_command_list); 266 | } 267 | 268 | rootSignature->Release(); 269 | } 270 | 271 | 272 | static void bool_check(bool res) 273 | { 274 | if (res) return; 275 | while (true) __debugbreak(); 276 | } 277 | 278 | 279 | bool D3DContext::CreateDeviceD3D(/*HWND hWnd*/) 280 | { 281 | // Setup swap chain 282 | DXGI_SWAP_CHAIN_DESC1 scd = {}; 283 | // Just use a minimal size for now. WM_NCCALCSIZE will resize when necessary. 284 | scd.Width = 1920; 285 | scd.Height = 1080; 286 | scd.Format = DXGI_FORMAT_B8G8R8A8_UNORM; 287 | scd.SampleDesc.Count = 1; 288 | scd.SampleDesc.Quality = 0; 289 | scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; 290 | scd.BufferCount = NUM_BACK_BUFFERS; 291 | // TODO: Determine if PRESENT_DO_NOT_SEQUENCE is safe to use with SWAP_EFFECT_FLIP_DISCARD. 292 | scd.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; 293 | scd.AlphaMode = DXGI_ALPHA_MODE_IGNORE; 294 | 295 | // [DEBUG] Enable debug interface 296 | #ifdef DX12_ENABLE_DEBUG_LAYER 297 | ID3D12Debug* pdx12Debug = nullptr; 298 | if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&pdx12Debug)))) 299 | pdx12Debug->EnableDebugLayer(); 300 | #endif 301 | 302 | // Create device 303 | D3D_FEATURE_LEVEL featureLevel = D3D_FEATURE_LEVEL_11_0; 304 | if (D3D12CreateDevice(nullptr, featureLevel, IID_PPV_ARGS(&device)) != S_OK) 305 | return false; 306 | 307 | // [DEBUG] Setup debug interface to break on any warnings/errors 308 | #ifdef DX12_ENABLE_DEBUG_LAYER 309 | if (pdx12Debug != nullptr) 310 | { 311 | ID3D12InfoQueue* pInfoQueue = nullptr; 312 | device->QueryInterface(IID_PPV_ARGS(&pInfoQueue)); 313 | pInfoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, true); 314 | pInfoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_CORRUPTION, true); 315 | pInfoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_WARNING, true); 316 | pInfoQueue->Release(); 317 | pdx12Debug->Release(); 318 | } 319 | #endif 320 | 321 | { 322 | D3D12_DESCRIPTOR_HEAP_DESC desc = {}; 323 | desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; 324 | desc.NumDescriptors = NUM_BACK_BUFFERS; 325 | desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; 326 | desc.NodeMask = 1; 327 | if (device->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&g_pd3dRtvDescHeap)) != S_OK) 328 | return false; 329 | 330 | SIZE_T rtvDescriptorSize = device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV); 331 | D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = g_pd3dRtvDescHeap->GetCPUDescriptorHandleForHeapStart(); 332 | for (UINT i = 0; i < NUM_BACK_BUFFERS; i++) 333 | { 334 | g_mainRenderTargetDescriptor[i] = rtvHandle; 335 | rtvHandle.ptr += rtvDescriptorSize; 336 | } 337 | } 338 | 339 | { 340 | D3D12_DESCRIPTOR_HEAP_DESC desc = {}; 341 | desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; 342 | desc.NumDescriptors = 1; 343 | desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; 344 | if (device->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&g_pd3dSrvDescHeap)) != S_OK) 345 | return false; 346 | } 347 | 348 | { 349 | D3D12_COMMAND_QUEUE_DESC desc = {}; 350 | desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; 351 | desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; 352 | desc.NodeMask = 1; 353 | if (device->CreateCommandQueue(&desc, IID_PPV_ARGS(&g_pd3dCommandQueue)) != S_OK) 354 | return false; 355 | } 356 | 357 | for (UINT i = 0; i < NUM_FRAMES_IN_FLIGHT; i++) 358 | if (device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&g_frameContext[i].CommandAllocator)) != S_OK) 359 | return false; 360 | 361 | if (device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, g_frameContext[0].CommandAllocator, nullptr, IID_PPV_ARGS(&g_pd3dCommandList)) != S_OK)// || 362 | /*g_pd3dCommandList->Close() != S_OK)*/ 363 | return false; 364 | 365 | g_pd3dCommandList->SetDescriptorHeaps(1, &g_pd3dSrvDescHeap); 366 | g_pd3dCommandList->Close(); 367 | 368 | if (device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&g_fence)) != S_OK) 369 | return false; 370 | 371 | g_fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); 372 | if (g_fenceEvent == nullptr) 373 | return false; 374 | 375 | { 376 | IDXGIFactory4* dxgiFactory = nullptr; 377 | IDXGISwapChain1* swapChain1 = nullptr; 378 | if (CreateDXGIFactory1(IID_PPV_ARGS(&dxgiFactory)) != S_OK) 379 | return false; 380 | if (dxgiFactory->CreateSwapChainForComposition(g_pd3dCommandQueue, &scd, nullptr, &swapChain1) != S_OK) 381 | return false; 382 | if (swapChain1->QueryInterface(IID_PPV_ARGS(&swapChain)) != S_OK) 383 | return false; 384 | swapChain1->Release(); 385 | dxgiFactory->Release(); 386 | swapChain->SetMaximumFrameLatency(NUM_BACK_BUFFERS); 387 | //g_hSwapChainWaitableObject = swapChain->GetFrameLatencyWaitableObject(); 388 | //g_hSwapChainWaitableObject = swapChain->GetFrameLatencyWaitableObject(); 389 | } 390 | 391 | CreateRenderTarget(); 392 | return true; 393 | } 394 | 395 | void D3DContext::CleanupDeviceD3D() 396 | { 397 | CleanupRenderTarget(); 398 | if (imageTextureView) { imageTextureView->Release(); imageTextureView = nullptr; } 399 | if (swapChain != nullptr) { swapChain->SetFullscreenState(false, nullptr); swapChain->Release(); swapChain = nullptr; } 400 | if (g_hSwapChainWaitableObject != nullptr) { CloseHandle(g_hSwapChainWaitableObject); } 401 | for (auto & i : g_frameContext) { 402 | if (i.CommandAllocator) { 403 | i.CommandAllocator->Release(); 404 | i.CommandAllocator = nullptr; 405 | } 406 | } 407 | if (g_pd3dCommandQueue) { g_pd3dCommandQueue->Release(); g_pd3dCommandQueue = nullptr; } 408 | if (g_pd3dCommandList) { g_pd3dCommandList->Release(); g_pd3dCommandList = nullptr; } 409 | if (g_pd3dRtvDescHeap) { g_pd3dRtvDescHeap->Release(); g_pd3dRtvDescHeap = nullptr; } 410 | if (g_pd3dSrvDescHeap) { g_pd3dSrvDescHeap->Release(); g_pd3dSrvDescHeap = nullptr; } 411 | if (g_fence) { g_fence->Release(); g_fence = nullptr; } 412 | if (g_fenceEvent) { CloseHandle(g_fenceEvent); g_fenceEvent = nullptr; } 413 | if (device) { device->Release(); device = nullptr; } 414 | 415 | #ifdef DX12_ENABLE_DEBUG_LAYER 416 | IDXGIDebug1* pDebug = nullptr; 417 | if (SUCCEEDED(DXGIGetDebugInterface1(0, IID_PPV_ARGS(&pDebug)))) 418 | { 419 | pDebug->ReportLiveObjects(DXGI_DEBUG_ALL, DXGI_DEBUG_RLO_SUMMARY); 420 | pDebug->Release(); 421 | } 422 | #endif 423 | } 424 | 425 | void D3DContext::CreateRenderTarget() 426 | { 427 | for (UINT i = 0; i < NUM_BACK_BUFFERS; i++) 428 | { 429 | ID3D12Resource* pBackBuffer = nullptr; 430 | swapChain->GetBuffer(i, IID_PPV_ARGS(&pBackBuffer)); 431 | device->CreateRenderTargetView(pBackBuffer, nullptr, g_mainRenderTargetDescriptor[i]); 432 | g_mainRenderTargetResource[i] = pBackBuffer; 433 | } 434 | } 435 | 436 | void D3DContext::CleanupRenderTarget() 437 | { 438 | FlushGPU(); 439 | 440 | for (auto & i : g_mainRenderTargetResource) 441 | if (i) { i->Release(); i = nullptr; } 442 | } 443 | 444 | void D3DContext::FlushGPU() { 445 | FrameContext* frameCtx = &g_frameContext[g_frameIndex % NUM_FRAMES_IN_FLIGHT]; 446 | for (int i = 0; i < NUM_BACK_BUFFERS; i++) { 447 | 448 | UINT64 fenceValue = g_fenceLastSignaledValue + 1; 449 | g_pd3dCommandQueue->Signal(g_fence, fenceValue); 450 | g_fenceLastSignaledValue = fenceValue; 451 | frameCtx->FenceValue = fenceValue; 452 | 453 | WaitForLastSubmittedFrame(); 454 | } 455 | } 456 | 457 | void D3DContext::WaitForLastSubmittedFrame() 458 | { 459 | FrameContext* frameCtx = &g_frameContext[g_frameIndex % NUM_FRAMES_IN_FLIGHT]; 460 | 461 | UINT64 fenceValue = frameCtx->FenceValue; 462 | if (fenceValue == 0) 463 | return; // No fence was signaled 464 | 465 | frameCtx->FenceValue = 0; 466 | if (g_fence->GetCompletedValue() >= fenceValue) 467 | return; 468 | 469 | g_fence->SetEventOnCompletion(fenceValue, g_fenceEvent); 470 | WaitForSingleObject(g_fenceEvent, INFINITE); 471 | } 472 | 473 | D3DContext::FrameContext* D3DContext::WaitForNextFrameResources() 474 | { 475 | UINT nextFrameIndex = g_frameIndex + 1; 476 | g_frameIndex = nextFrameIndex; 477 | 478 | g_hSwapChainWaitableObject = swapChain->GetFrameLatencyWaitableObject(); 479 | HANDLE waitableObjects[] = { g_hSwapChainWaitableObject, nullptr }; 480 | DWORD numWaitableObjects = 1; 481 | 482 | FrameContext* frameCtx = &g_frameContext[nextFrameIndex % NUM_FRAMES_IN_FLIGHT]; 483 | UINT64 fenceValue = frameCtx->FenceValue; 484 | if (fenceValue != 0) // means no fence was signaled 485 | { 486 | frameCtx->FenceValue = 0; 487 | g_fence->SetEventOnCompletion(fenceValue, g_fenceEvent); 488 | waitableObjects[1] = g_fenceEvent; 489 | numWaitableObjects = 2; 490 | } 491 | 492 | WaitForMultipleObjects(numWaitableObjects, waitableObjects, TRUE, INFINITE); 493 | 494 | return frameCtx; 495 | } 496 | 497 | D3DContext::D3DContext(std::shared_ptr contents): D3DContextBase(contents), swapChain(nullptr), descriptorHeap(nullptr) { 498 | bool_check(CreateDeviceD3D()); 499 | 500 | // hr_check(LoadDDSTextureFromFile( this->device, L"grass.dds", &imageTextureView, ddsData, subresources )); 501 | // HRESULT __cdecl LoadDDSTextureFromFile( 502 | // _In_ ID3D12Device* d3dDevice, 503 | // _In_z_ const wchar_t* szFileName, 504 | // _Outptr_ ID3D12Resource** texture, 505 | // std::unique_ptr& ddsData, 506 | // std::vector& subresources, 507 | // size_t maxsize = 0, 508 | // _Out_opt_ DDS_ALPHA_MODE* alphaMode = nullptr, 509 | // _Out_opt_ bool* isCubeMap = nullptr); 510 | 511 | this->reposition(getFullDisplayRECT()); 512 | } 513 | 514 | void D3DContext::reposition(const RECT& position) { 515 | int width = position.right - position.left; 516 | int height = position.bottom - position.top; 517 | 518 | lookForIntelOutput(position); 519 | CleanupRenderTarget(); 520 | checkDeviceRemoved(swapChain->ResizeBuffers(0, width, height, DXGI_FORMAT_UNKNOWN, DXGI_SWAP_CHAIN_FLAG_GDI_COMPATIBLE));//0/*DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT*/)); 521 | CreateRenderTarget(); 522 | 523 | FrameContext* frameCtx = WaitForNextFrameResources(); 524 | 525 | UINT backBufferIdx = swapChain->GetCurrentBackBufferIndex(); 526 | DrawTriangle(width, height, device, this->g_pd3dCommandList, this->g_pd3dCommandQueue, swapChain, 527 | g_mainRenderTargetResource[backBufferIdx], 528 | g_mainRenderTargetDescriptor[backBufferIdx], 529 | frameCtx, &this->drawingCache, contents); 530 | 531 | syncIntelOutput(); 532 | 533 | // Discard outstanding queued presents and queue a frame with the new size ASAP. 534 | checkDeviceRemoved(swapChain->Present(0, DXGI_PRESENT_RESTART)); 535 | 536 | UINT64 fenceValue = g_fenceLastSignaledValue + 1; 537 | g_pd3dCommandQueue->Signal(g_fence, fenceValue); 538 | g_fenceLastSignaledValue = fenceValue; 539 | frameCtx->FenceValue = fenceValue; 540 | 541 | // Wait for a vblank to really make sure our frame with the new size is ready before 542 | // the window finishes resizing. 543 | // TODO: Determine why this is necessary at all. Why isn't one Present() enough? 544 | // TODO: Determine if there's a way to wait for vblank without calling Present(). 545 | // TODO: Determine if DO_NOT_SEQUENCE is safe to use with SWAP_EFFECT_FLIP_DISCARD. 546 | checkDeviceRemoved(swapChain->Present(1, DXGI_PRESENT_DO_NOT_SEQUENCE)); 547 | } 548 | 549 | D3DContext::~D3DContext() { 550 | CleanupDeviceD3D(); 551 | } -------------------------------------------------------------------------------- /DDSTextureLoader.cpp: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------------------------------------------- 2 | // File: DDSTextureLoader.cpp 3 | // 4 | // Functions for loading a DDS texture and creating a Direct3D runtime resource for it 5 | // 6 | // Note these functions are useful as a light-weight runtime loader for DDS files. For 7 | // a full-featured DDS file reader, writer, and texture processing pipeline see 8 | // the 'Texconv' sample and the 'DirectXTex' library. 9 | // 10 | // Copyright (c) Microsoft Corporation. 11 | // Licensed under the MIT License. 12 | // 13 | // http://go.microsoft.com/fwlink/?LinkId=248926 14 | // http://go.microsoft.com/fwlink/?LinkId=248929 15 | //-------------------------------------------------------------------------------------- 16 | 17 | #include "DDSTextureLoader.h" 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #ifdef __clang__ 24 | #pragma clang diagnostic ignored "-Wcovered-switch-default" 25 | #pragma clang diagnostic ignored "-Wswitch-enum" 26 | #endif 27 | 28 | using namespace DirectX; 29 | 30 | //-------------------------------------------------------------------------------------- 31 | // Macros 32 | //-------------------------------------------------------------------------------------- 33 | #ifndef MAKEFOURCC 34 | #define MAKEFOURCC(ch0, ch1, ch2, ch3) \ 35 | ((uint32_t)(uint8_t)(ch0) | ((uint32_t)(uint8_t)(ch1) << 8) | \ 36 | ((uint32_t)(uint8_t)(ch2) << 16) | ((uint32_t)(uint8_t)(ch3) << 24 )) 37 | #endif /* defined(MAKEFOURCC) */ 38 | 39 | //-------------------------------------------------------------------------------------- 40 | // DDS file structure definitions 41 | // 42 | // See DDS.h in the 'Texconv' sample and the 'DirectXTex' library 43 | //-------------------------------------------------------------------------------------- 44 | #pragma pack(push,1) 45 | 46 | constexpr uint32_t DDS_MAGIC = 0x20534444; // "DDS " 47 | 48 | struct DDS_PIXELFORMAT 49 | { 50 | uint32_t size; 51 | uint32_t flags; 52 | uint32_t fourCC; 53 | uint32_t RGBBitCount; 54 | uint32_t RBitMask; 55 | uint32_t GBitMask; 56 | uint32_t BBitMask; 57 | uint32_t ABitMask; 58 | }; 59 | 60 | #define DDS_FOURCC 0x00000004 // DDPF_FOURCC 61 | #define DDS_RGB 0x00000040 // DDPF_RGB 62 | #define DDS_LUMINANCE 0x00020000 // DDPF_LUMINANCE 63 | #define DDS_ALPHA 0x00000002 // DDPF_ALPHA 64 | #define DDS_BUMPDUDV 0x00080000 // DDPF_BUMPDUDV 65 | 66 | #define DDS_HEADER_FLAGS_VOLUME 0x00800000 // DDSD_DEPTH 67 | 68 | #define DDS_HEIGHT 0x00000002 // DDSD_HEIGHT 69 | 70 | #define DDS_CUBEMAP_POSITIVEX 0x00000600 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEX 71 | #define DDS_CUBEMAP_NEGATIVEX 0x00000a00 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEX 72 | #define DDS_CUBEMAP_POSITIVEY 0x00001200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEY 73 | #define DDS_CUBEMAP_NEGATIVEY 0x00002200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEY 74 | #define DDS_CUBEMAP_POSITIVEZ 0x00004200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEZ 75 | #define DDS_CUBEMAP_NEGATIVEZ 0x00008200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEZ 76 | 77 | #define DDS_CUBEMAP_ALLFACES ( DDS_CUBEMAP_POSITIVEX | DDS_CUBEMAP_NEGATIVEX |\ 78 | DDS_CUBEMAP_POSITIVEY | DDS_CUBEMAP_NEGATIVEY |\ 79 | DDS_CUBEMAP_POSITIVEZ | DDS_CUBEMAP_NEGATIVEZ ) 80 | 81 | #define DDS_CUBEMAP 0x00000200 // DDSCAPS2_CUBEMAP 82 | 83 | enum DDS_MISC_FLAGS2 84 | { 85 | DDS_MISC_FLAGS2_ALPHA_MODE_MASK = 0x7L, 86 | }; 87 | 88 | struct DDS_HEADER 89 | { 90 | uint32_t size; 91 | uint32_t flags; 92 | uint32_t height; 93 | uint32_t width; 94 | uint32_t pitchOrLinearSize; 95 | uint32_t depth; // only if DDS_HEADER_FLAGS_VOLUME is set in flags 96 | uint32_t mipMapCount; 97 | uint32_t reserved1[11]; 98 | DDS_PIXELFORMAT ddspf; 99 | uint32_t caps; 100 | uint32_t caps2; 101 | uint32_t caps3; 102 | uint32_t caps4; 103 | uint32_t reserved2; 104 | }; 105 | 106 | struct DDS_HEADER_DXT10 107 | { 108 | DXGI_FORMAT dxgiFormat; 109 | uint32_t resourceDimension; 110 | uint32_t miscFlag; // see D3D11_RESOURCE_MISC_FLAG 111 | uint32_t arraySize; 112 | uint32_t miscFlags2; 113 | }; 114 | 115 | #pragma pack(pop) 116 | 117 | //-------------------------------------------------------------------------------------- 118 | namespace 119 | { 120 | struct handle_closer { void operator()(HANDLE h) noexcept { if (h) CloseHandle(h); } }; 121 | 122 | using ScopedHandle = std::unique_ptr; 123 | 124 | inline HANDLE safe_handle(HANDLE h) noexcept { return (h == INVALID_HANDLE_VALUE) ? nullptr : h; } 125 | 126 | #if defined(_DEBUG) || defined(PROFILE) 127 | template 128 | inline void SetDebugObjectName(_In_ ID3D11DeviceChild* resource, _In_ const char(&name)[TNameLength]) noexcept 129 | { 130 | resource->SetPrivateData(WKPDID_D3DDebugObjectName, TNameLength - 1, name); 131 | } 132 | #else 133 | template 134 | inline void SetDebugObjectName(_In_ ID3D11DeviceChild*, _In_ const char(&)[TNameLength]) noexcept 135 | { 136 | } 137 | #endif 138 | 139 | //-------------------------------------------------------------------------------------- 140 | HRESULT LoadTextureDataFromMemory( 141 | _In_reads_(ddsDataSize) const uint8_t* ddsData, 142 | size_t ddsDataSize, 143 | const DDS_HEADER** header, 144 | const uint8_t** bitData, 145 | size_t* bitSize) noexcept 146 | { 147 | if (!header || !bitData || !bitSize) 148 | { 149 | return E_POINTER; 150 | } 151 | 152 | *bitSize = 0; 153 | 154 | if (ddsDataSize > UINT32_MAX) 155 | { 156 | return E_FAIL; 157 | } 158 | 159 | if (ddsDataSize < (sizeof(uint32_t) + sizeof(DDS_HEADER))) 160 | { 161 | return E_FAIL; 162 | } 163 | 164 | // DDS files always start with the same magic number ("DDS ") 165 | auto const dwMagicNumber = *reinterpret_cast(ddsData); 166 | if (dwMagicNumber != DDS_MAGIC) 167 | { 168 | return E_FAIL; 169 | } 170 | 171 | auto hdr = reinterpret_cast(ddsData + sizeof(uint32_t)); 172 | 173 | // Verify header to validate DDS file 174 | if (hdr->size != sizeof(DDS_HEADER) || 175 | hdr->ddspf.size != sizeof(DDS_PIXELFORMAT)) 176 | { 177 | return E_FAIL; 178 | } 179 | 180 | // Check for DX10 extension 181 | bool bDXT10Header = false; 182 | if ((hdr->ddspf.flags & DDS_FOURCC) && 183 | (MAKEFOURCC('D', 'X', '1', '0') == hdr->ddspf.fourCC)) 184 | { 185 | // Must be long enough for both headers and magic value 186 | if (ddsDataSize < (sizeof(uint32_t) + sizeof(DDS_HEADER) + sizeof(DDS_HEADER_DXT10))) 187 | { 188 | return E_FAIL; 189 | } 190 | 191 | bDXT10Header = true; 192 | } 193 | 194 | // setup the pointers in the process request 195 | *header = hdr; 196 | auto offset = sizeof(uint32_t) 197 | + sizeof(DDS_HEADER) 198 | + (bDXT10Header ? sizeof(DDS_HEADER_DXT10) : 0u); 199 | *bitData = ddsData + offset; 200 | *bitSize = ddsDataSize - offset; 201 | 202 | return S_OK; 203 | } 204 | 205 | 206 | //-------------------------------------------------------------------------------------- 207 | HRESULT LoadTextureDataFromFile( 208 | _In_z_ const wchar_t* fileName, 209 | std::unique_ptr& ddsData, 210 | const DDS_HEADER** header, 211 | const uint8_t** bitData, 212 | size_t* bitSize) noexcept 213 | { 214 | if (!header || !bitData || !bitSize) 215 | { 216 | return E_POINTER; 217 | } 218 | 219 | *bitSize = 0; 220 | 221 | // open the file 222 | #if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) 223 | ScopedHandle hFile(safe_handle(CreateFile2( 224 | fileName, 225 | GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, 226 | nullptr))); 227 | #else 228 | ScopedHandle hFile(safe_handle(CreateFileW( 229 | fileName, 230 | GENERIC_READ, FILE_SHARE_READ, 231 | nullptr, 232 | OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 233 | nullptr))); 234 | #endif 235 | 236 | if (!hFile) 237 | { 238 | return HRESULT_FROM_WIN32(GetLastError()); 239 | } 240 | 241 | // Get the file size 242 | FILE_STANDARD_INFO fileInfo; 243 | if (!GetFileInformationByHandleEx(hFile.get(), FileStandardInfo, &fileInfo, sizeof(fileInfo))) 244 | { 245 | return HRESULT_FROM_WIN32(GetLastError()); 246 | } 247 | 248 | // File is too big for 32-bit allocation, so reject read 249 | if (fileInfo.EndOfFile.HighPart > 0) 250 | { 251 | return E_FAIL; 252 | } 253 | 254 | // Need at least enough data to fill the header and magic number to be a valid DDS 255 | if (fileInfo.EndOfFile.LowPart < (sizeof(uint32_t) + sizeof(DDS_HEADER))) 256 | { 257 | return E_FAIL; 258 | } 259 | 260 | // create enough space for the file data 261 | ddsData.reset(new (std::nothrow) uint8_t[fileInfo.EndOfFile.LowPart]); 262 | if (!ddsData) 263 | { 264 | return E_OUTOFMEMORY; 265 | } 266 | 267 | // read the data in 268 | DWORD bytesRead = 0; 269 | if (!ReadFile(hFile.get(), 270 | ddsData.get(), 271 | fileInfo.EndOfFile.LowPart, 272 | &bytesRead, 273 | nullptr 274 | )) 275 | { 276 | ddsData.reset(); 277 | return HRESULT_FROM_WIN32(GetLastError()); 278 | } 279 | 280 | if (bytesRead < fileInfo.EndOfFile.LowPart) 281 | { 282 | ddsData.reset(); 283 | return E_FAIL; 284 | } 285 | 286 | // DDS files always start with the same magic number ("DDS ") 287 | auto const dwMagicNumber = *reinterpret_cast(ddsData.get()); 288 | if (dwMagicNumber != DDS_MAGIC) 289 | { 290 | ddsData.reset(); 291 | return E_FAIL; 292 | } 293 | 294 | auto hdr = reinterpret_cast(ddsData.get() + sizeof(uint32_t)); 295 | 296 | // Verify header to validate DDS file 297 | if (hdr->size != sizeof(DDS_HEADER) || 298 | hdr->ddspf.size != sizeof(DDS_PIXELFORMAT)) 299 | { 300 | ddsData.reset(); 301 | return E_FAIL; 302 | } 303 | 304 | // Check for DX10 extension 305 | bool bDXT10Header = false; 306 | if ((hdr->ddspf.flags & DDS_FOURCC) && 307 | (MAKEFOURCC('D', 'X', '1', '0') == hdr->ddspf.fourCC)) 308 | { 309 | // Must be long enough for both headers and magic value 310 | if (fileInfo.EndOfFile.LowPart < (sizeof(uint32_t) + sizeof(DDS_HEADER) + sizeof(DDS_HEADER_DXT10))) 311 | { 312 | ddsData.reset(); 313 | return E_FAIL; 314 | } 315 | 316 | bDXT10Header = true; 317 | } 318 | 319 | // setup the pointers in the process request 320 | *header = hdr; 321 | auto offset = sizeof(uint32_t) + sizeof(DDS_HEADER) 322 | + (bDXT10Header ? sizeof(DDS_HEADER_DXT10) : 0u); 323 | *bitData = ddsData.get() + offset; 324 | *bitSize = fileInfo.EndOfFile.LowPart - offset; 325 | 326 | return S_OK; 327 | } 328 | 329 | 330 | //-------------------------------------------------------------------------------------- 331 | // Return the BPP for a particular format 332 | //-------------------------------------------------------------------------------------- 333 | size_t BitsPerPixel(_In_ DXGI_FORMAT fmt) noexcept 334 | { 335 | switch (fmt) 336 | { 337 | case DXGI_FORMAT_R32G32B32A32_TYPELESS: 338 | case DXGI_FORMAT_R32G32B32A32_FLOAT: 339 | case DXGI_FORMAT_R32G32B32A32_UINT: 340 | case DXGI_FORMAT_R32G32B32A32_SINT: 341 | return 128; 342 | 343 | case DXGI_FORMAT_R32G32B32_TYPELESS: 344 | case DXGI_FORMAT_R32G32B32_FLOAT: 345 | case DXGI_FORMAT_R32G32B32_UINT: 346 | case DXGI_FORMAT_R32G32B32_SINT: 347 | return 96; 348 | 349 | case DXGI_FORMAT_R16G16B16A16_TYPELESS: 350 | case DXGI_FORMAT_R16G16B16A16_FLOAT: 351 | case DXGI_FORMAT_R16G16B16A16_UNORM: 352 | case DXGI_FORMAT_R16G16B16A16_UINT: 353 | case DXGI_FORMAT_R16G16B16A16_SNORM: 354 | case DXGI_FORMAT_R16G16B16A16_SINT: 355 | case DXGI_FORMAT_R32G32_TYPELESS: 356 | case DXGI_FORMAT_R32G32_FLOAT: 357 | case DXGI_FORMAT_R32G32_UINT: 358 | case DXGI_FORMAT_R32G32_SINT: 359 | case DXGI_FORMAT_R32G8X24_TYPELESS: 360 | case DXGI_FORMAT_D32_FLOAT_S8X24_UINT: 361 | case DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS: 362 | case DXGI_FORMAT_X32_TYPELESS_G8X24_UINT: 363 | case DXGI_FORMAT_Y416: 364 | case DXGI_FORMAT_Y210: 365 | case DXGI_FORMAT_Y216: 366 | return 64; 367 | 368 | case DXGI_FORMAT_R10G10B10A2_TYPELESS: 369 | case DXGI_FORMAT_R10G10B10A2_UNORM: 370 | case DXGI_FORMAT_R10G10B10A2_UINT: 371 | case DXGI_FORMAT_R11G11B10_FLOAT: 372 | case DXGI_FORMAT_R8G8B8A8_TYPELESS: 373 | case DXGI_FORMAT_R8G8B8A8_UNORM: 374 | case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: 375 | case DXGI_FORMAT_R8G8B8A8_UINT: 376 | case DXGI_FORMAT_R8G8B8A8_SNORM: 377 | case DXGI_FORMAT_R8G8B8A8_SINT: 378 | case DXGI_FORMAT_R16G16_TYPELESS: 379 | case DXGI_FORMAT_R16G16_FLOAT: 380 | case DXGI_FORMAT_R16G16_UNORM: 381 | case DXGI_FORMAT_R16G16_UINT: 382 | case DXGI_FORMAT_R16G16_SNORM: 383 | case DXGI_FORMAT_R16G16_SINT: 384 | case DXGI_FORMAT_R32_TYPELESS: 385 | case DXGI_FORMAT_D32_FLOAT: 386 | case DXGI_FORMAT_R32_FLOAT: 387 | case DXGI_FORMAT_R32_UINT: 388 | case DXGI_FORMAT_R32_SINT: 389 | case DXGI_FORMAT_R24G8_TYPELESS: 390 | case DXGI_FORMAT_D24_UNORM_S8_UINT: 391 | case DXGI_FORMAT_R24_UNORM_X8_TYPELESS: 392 | case DXGI_FORMAT_X24_TYPELESS_G8_UINT: 393 | case DXGI_FORMAT_R9G9B9E5_SHAREDEXP: 394 | case DXGI_FORMAT_R8G8_B8G8_UNORM: 395 | case DXGI_FORMAT_G8R8_G8B8_UNORM: 396 | case DXGI_FORMAT_B8G8R8A8_UNORM: 397 | case DXGI_FORMAT_B8G8R8X8_UNORM: 398 | case DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM: 399 | case DXGI_FORMAT_B8G8R8A8_TYPELESS: 400 | case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: 401 | case DXGI_FORMAT_B8G8R8X8_TYPELESS: 402 | case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: 403 | case DXGI_FORMAT_AYUV: 404 | case DXGI_FORMAT_Y410: 405 | case DXGI_FORMAT_YUY2: 406 | return 32; 407 | 408 | case DXGI_FORMAT_P010: 409 | case DXGI_FORMAT_P016: 410 | return 24; 411 | 412 | case DXGI_FORMAT_R8G8_TYPELESS: 413 | case DXGI_FORMAT_R8G8_UNORM: 414 | case DXGI_FORMAT_R8G8_UINT: 415 | case DXGI_FORMAT_R8G8_SNORM: 416 | case DXGI_FORMAT_R8G8_SINT: 417 | case DXGI_FORMAT_R16_TYPELESS: 418 | case DXGI_FORMAT_R16_FLOAT: 419 | case DXGI_FORMAT_D16_UNORM: 420 | case DXGI_FORMAT_R16_UNORM: 421 | case DXGI_FORMAT_R16_UINT: 422 | case DXGI_FORMAT_R16_SNORM: 423 | case DXGI_FORMAT_R16_SINT: 424 | case DXGI_FORMAT_B5G6R5_UNORM: 425 | case DXGI_FORMAT_B5G5R5A1_UNORM: 426 | case DXGI_FORMAT_A8P8: 427 | case DXGI_FORMAT_B4G4R4A4_UNORM: 428 | return 16; 429 | 430 | case DXGI_FORMAT_NV12: 431 | case DXGI_FORMAT_420_OPAQUE: 432 | case DXGI_FORMAT_NV11: 433 | return 12; 434 | 435 | case DXGI_FORMAT_R8_TYPELESS: 436 | case DXGI_FORMAT_R8_UNORM: 437 | case DXGI_FORMAT_R8_UINT: 438 | case DXGI_FORMAT_R8_SNORM: 439 | case DXGI_FORMAT_R8_SINT: 440 | case DXGI_FORMAT_A8_UNORM: 441 | case DXGI_FORMAT_BC2_TYPELESS: 442 | case DXGI_FORMAT_BC2_UNORM: 443 | case DXGI_FORMAT_BC2_UNORM_SRGB: 444 | case DXGI_FORMAT_BC3_TYPELESS: 445 | case DXGI_FORMAT_BC3_UNORM: 446 | case DXGI_FORMAT_BC3_UNORM_SRGB: 447 | case DXGI_FORMAT_BC5_TYPELESS: 448 | case DXGI_FORMAT_BC5_UNORM: 449 | case DXGI_FORMAT_BC5_SNORM: 450 | case DXGI_FORMAT_BC6H_TYPELESS: 451 | case DXGI_FORMAT_BC6H_UF16: 452 | case DXGI_FORMAT_BC6H_SF16: 453 | case DXGI_FORMAT_BC7_TYPELESS: 454 | case DXGI_FORMAT_BC7_UNORM: 455 | case DXGI_FORMAT_BC7_UNORM_SRGB: 456 | case DXGI_FORMAT_AI44: 457 | case DXGI_FORMAT_IA44: 458 | case DXGI_FORMAT_P8: 459 | return 8; 460 | 461 | case DXGI_FORMAT_R1_UNORM: 462 | return 1; 463 | 464 | case DXGI_FORMAT_BC1_TYPELESS: 465 | case DXGI_FORMAT_BC1_UNORM: 466 | case DXGI_FORMAT_BC1_UNORM_SRGB: 467 | case DXGI_FORMAT_BC4_TYPELESS: 468 | case DXGI_FORMAT_BC4_UNORM: 469 | case DXGI_FORMAT_BC4_SNORM: 470 | return 4; 471 | 472 | default: 473 | return 0; 474 | } 475 | } 476 | 477 | 478 | //-------------------------------------------------------------------------------------- 479 | // Get surface information for a particular format 480 | //-------------------------------------------------------------------------------------- 481 | HRESULT GetSurfaceInfo( 482 | _In_ size_t width, 483 | _In_ size_t height, 484 | _In_ DXGI_FORMAT fmt, 485 | size_t* outNumBytes, 486 | _Out_opt_ size_t* outRowBytes, 487 | _Out_opt_ size_t* outNumRows) noexcept 488 | { 489 | uint64_t numBytes = 0; 490 | uint64_t rowBytes = 0; 491 | uint64_t numRows = 0; 492 | 493 | bool bc = false; 494 | bool packed = false; 495 | bool planar = false; 496 | size_t bpe = 0; 497 | switch (fmt) 498 | { 499 | case DXGI_FORMAT_BC1_TYPELESS: 500 | case DXGI_FORMAT_BC1_UNORM: 501 | case DXGI_FORMAT_BC1_UNORM_SRGB: 502 | case DXGI_FORMAT_BC4_TYPELESS: 503 | case DXGI_FORMAT_BC4_UNORM: 504 | case DXGI_FORMAT_BC4_SNORM: 505 | bc = true; 506 | bpe = 8; 507 | break; 508 | 509 | case DXGI_FORMAT_BC2_TYPELESS: 510 | case DXGI_FORMAT_BC2_UNORM: 511 | case DXGI_FORMAT_BC2_UNORM_SRGB: 512 | case DXGI_FORMAT_BC3_TYPELESS: 513 | case DXGI_FORMAT_BC3_UNORM: 514 | case DXGI_FORMAT_BC3_UNORM_SRGB: 515 | case DXGI_FORMAT_BC5_TYPELESS: 516 | case DXGI_FORMAT_BC5_UNORM: 517 | case DXGI_FORMAT_BC5_SNORM: 518 | case DXGI_FORMAT_BC6H_TYPELESS: 519 | case DXGI_FORMAT_BC6H_UF16: 520 | case DXGI_FORMAT_BC6H_SF16: 521 | case DXGI_FORMAT_BC7_TYPELESS: 522 | case DXGI_FORMAT_BC7_UNORM: 523 | case DXGI_FORMAT_BC7_UNORM_SRGB: 524 | bc = true; 525 | bpe = 16; 526 | break; 527 | 528 | case DXGI_FORMAT_R8G8_B8G8_UNORM: 529 | case DXGI_FORMAT_G8R8_G8B8_UNORM: 530 | case DXGI_FORMAT_YUY2: 531 | packed = true; 532 | bpe = 4; 533 | break; 534 | 535 | case DXGI_FORMAT_Y210: 536 | case DXGI_FORMAT_Y216: 537 | packed = true; 538 | bpe = 8; 539 | break; 540 | 541 | case DXGI_FORMAT_NV12: 542 | case DXGI_FORMAT_420_OPAQUE: 543 | if ((height % 2) != 0) 544 | { 545 | // Requires a height alignment of 2. 546 | return E_INVALIDARG; 547 | } 548 | planar = true; 549 | bpe = 2; 550 | break; 551 | 552 | case DXGI_FORMAT_P010: 553 | case DXGI_FORMAT_P016: 554 | if ((height % 2) != 0) 555 | { 556 | // Requires a height alignment of 2. 557 | return E_INVALIDARG; 558 | } 559 | planar = true; 560 | bpe = 4; 561 | break; 562 | 563 | default: 564 | break; 565 | } 566 | 567 | if (bc) 568 | { 569 | uint64_t numBlocksWide = 0; 570 | if (width > 0) 571 | { 572 | numBlocksWide = std::max(1u, (uint64_t(width) + 3u) / 4u); 573 | } 574 | uint64_t numBlocksHigh = 0; 575 | if (height > 0) 576 | { 577 | numBlocksHigh = std::max(1u, (uint64_t(height) + 3u) / 4u); 578 | } 579 | rowBytes = numBlocksWide * bpe; 580 | numRows = numBlocksHigh; 581 | numBytes = rowBytes * numBlocksHigh; 582 | } 583 | else if (packed) 584 | { 585 | rowBytes = ((uint64_t(width) + 1u) >> 1) * bpe; 586 | numRows = uint64_t(height); 587 | numBytes = rowBytes * height; 588 | } 589 | else if (fmt == DXGI_FORMAT_NV11) 590 | { 591 | rowBytes = ((uint64_t(width) + 3u) >> 2) * 4u; 592 | numRows = uint64_t(height) * 2u; // Direct3D makes this simplifying assumption, although it is larger than the 4:1:1 data 593 | numBytes = rowBytes * numRows; 594 | } 595 | else if (planar) 596 | { 597 | rowBytes = ((uint64_t(width) + 1u) >> 1) * bpe; 598 | numBytes = (rowBytes * uint64_t(height)) + ((rowBytes * uint64_t(height) + 1u) >> 1); 599 | numRows = height + ((uint64_t(height) + 1u) >> 1); 600 | } 601 | else 602 | { 603 | const size_t bpp = BitsPerPixel(fmt); 604 | if (!bpp) 605 | return E_INVALIDARG; 606 | 607 | rowBytes = (uint64_t(width) * bpp + 7u) / 8u; // round up to nearest byte 608 | numRows = uint64_t(height); 609 | numBytes = rowBytes * height; 610 | } 611 | 612 | #if defined(_M_IX86) || defined(_M_ARM) || defined(_M_HYBRID_X86_ARM64) 613 | static_assert(sizeof(size_t) == 4, "Not a 32-bit platform!"); 614 | if (numBytes > UINT32_MAX || rowBytes > UINT32_MAX || numRows > UINT32_MAX) 615 | return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); 616 | #else 617 | static_assert(sizeof(size_t) == 8, "Not a 64-bit platform!"); 618 | #endif 619 | 620 | if (outNumBytes) 621 | { 622 | *outNumBytes = static_cast(numBytes); 623 | } 624 | if (outRowBytes) 625 | { 626 | *outRowBytes = static_cast(rowBytes); 627 | } 628 | if (outNumRows) 629 | { 630 | *outNumRows = static_cast(numRows); 631 | } 632 | 633 | return S_OK; 634 | } 635 | 636 | 637 | //-------------------------------------------------------------------------------------- 638 | #define ISBITMASK( r,g,b,a ) ( ddpf.RBitMask == r && ddpf.GBitMask == g && ddpf.BBitMask == b && ddpf.ABitMask == a ) 639 | 640 | DXGI_FORMAT GetDXGIFormat(const DDS_PIXELFORMAT& ddpf) noexcept 641 | { 642 | if (ddpf.flags & DDS_RGB) 643 | { 644 | // Note that sRGB formats are written using the "DX10" extended header 645 | 646 | switch (ddpf.RGBBitCount) 647 | { 648 | case 32: 649 | if (ISBITMASK(0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000)) 650 | { 651 | return DXGI_FORMAT_R8G8B8A8_UNORM; 652 | } 653 | 654 | if (ISBITMASK(0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000)) 655 | { 656 | return DXGI_FORMAT_B8G8R8A8_UNORM; 657 | } 658 | 659 | if (ISBITMASK(0x00ff0000, 0x0000ff00, 0x000000ff, 0)) 660 | { 661 | return DXGI_FORMAT_B8G8R8X8_UNORM; 662 | } 663 | 664 | // No DXGI format maps to ISBITMASK(0x000000ff,0x0000ff00,0x00ff0000,0) aka D3DFMT_X8B8G8R8 665 | 666 | // Note that many common DDS reader/writers (including D3DX) swap the 667 | // the RED/BLUE masks for 10:10:10:2 formats. We assume 668 | // below that the 'backwards' header mask is being used since it is most 669 | // likely written by D3DX. The more robust solution is to use the 'DX10' 670 | // header extension and specify the DXGI_FORMAT_R10G10B10A2_UNORM format directly 671 | 672 | // For 'correct' writers, this should be 0x000003ff,0x000ffc00,0x3ff00000 for RGB data 673 | if (ISBITMASK(0x3ff00000, 0x000ffc00, 0x000003ff, 0xc0000000)) 674 | { 675 | return DXGI_FORMAT_R10G10B10A2_UNORM; 676 | } 677 | 678 | // No DXGI format maps to ISBITMASK(0x000003ff,0x000ffc00,0x3ff00000,0xc0000000) aka D3DFMT_A2R10G10B10 679 | 680 | if (ISBITMASK(0x0000ffff, 0xffff0000, 0, 0)) 681 | { 682 | return DXGI_FORMAT_R16G16_UNORM; 683 | } 684 | 685 | if (ISBITMASK(0xffffffff, 0, 0, 0)) 686 | { 687 | // Only 32-bit color channel format in D3D9 was R32F 688 | return DXGI_FORMAT_R32_FLOAT; // D3DX writes this out as a FourCC of 114 689 | } 690 | break; 691 | 692 | case 24: 693 | // No 24bpp DXGI formats aka D3DFMT_R8G8B8 694 | break; 695 | 696 | case 16: 697 | if (ISBITMASK(0x7c00, 0x03e0, 0x001f, 0x8000)) 698 | { 699 | return DXGI_FORMAT_B5G5R5A1_UNORM; 700 | } 701 | if (ISBITMASK(0xf800, 0x07e0, 0x001f, 0)) 702 | { 703 | return DXGI_FORMAT_B5G6R5_UNORM; 704 | } 705 | 706 | // No DXGI format maps to ISBITMASK(0x7c00,0x03e0,0x001f,0) aka D3DFMT_X1R5G5B5 707 | 708 | if (ISBITMASK(0x0f00, 0x00f0, 0x000f, 0xf000)) 709 | { 710 | return DXGI_FORMAT_B4G4R4A4_UNORM; 711 | } 712 | 713 | // NVTT versions 1.x wrote this as RGB instead of LUMINANCE 714 | if (ISBITMASK(0x00ff, 0, 0, 0xff00)) 715 | { 716 | return DXGI_FORMAT_R8G8_UNORM; 717 | } 718 | if (ISBITMASK(0xffff, 0, 0, 0)) 719 | { 720 | return DXGI_FORMAT_R16_UNORM; 721 | } 722 | 723 | // No DXGI format maps to ISBITMASK(0x0f00,0x00f0,0x000f,0) aka D3DFMT_X4R4G4B4 724 | 725 | // No 3:3:2:8 or paletted DXGI formats aka D3DFMT_A8R3G3B2, D3DFMT_A8P8, etc. 726 | break; 727 | 728 | case 8: 729 | // NVTT versions 1.x wrote this as RGB instead of LUMINANCE 730 | if (ISBITMASK(0xff, 0, 0, 0)) 731 | { 732 | return DXGI_FORMAT_R8_UNORM; 733 | } 734 | 735 | // No 3:3:2 or paletted DXGI formats aka D3DFMT_R3G3B2, D3DFMT_P8 736 | break; 737 | } 738 | } 739 | else if (ddpf.flags & DDS_LUMINANCE) 740 | { 741 | switch (ddpf.RGBBitCount) 742 | { 743 | case 16: 744 | if (ISBITMASK(0xffff, 0, 0, 0)) 745 | { 746 | return DXGI_FORMAT_R16_UNORM; // D3DX10/11 writes this out as DX10 extension 747 | } 748 | if (ISBITMASK(0x00ff, 0, 0, 0xff00)) 749 | { 750 | return DXGI_FORMAT_R8G8_UNORM; // D3DX10/11 writes this out as DX10 extension 751 | } 752 | break; 753 | 754 | case 8: 755 | if (ISBITMASK(0xff, 0, 0, 0)) 756 | { 757 | return DXGI_FORMAT_R8_UNORM; // D3DX10/11 writes this out as DX10 extension 758 | } 759 | 760 | // No DXGI format maps to ISBITMASK(0x0f,0,0,0xf0) aka D3DFMT_A4L4 761 | 762 | if (ISBITMASK(0x00ff, 0, 0, 0xff00)) 763 | { 764 | return DXGI_FORMAT_R8G8_UNORM; // Some DDS writers assume the bitcount should be 8 instead of 16 765 | } 766 | break; 767 | } 768 | } 769 | else if (ddpf.flags & DDS_ALPHA) 770 | { 771 | if (8 == ddpf.RGBBitCount) 772 | { 773 | return DXGI_FORMAT_A8_UNORM; 774 | } 775 | } 776 | else if (ddpf.flags & DDS_BUMPDUDV) 777 | { 778 | switch (ddpf.RGBBitCount) 779 | { 780 | case 32: 781 | if (ISBITMASK(0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000)) 782 | { 783 | return DXGI_FORMAT_R8G8B8A8_SNORM; // D3DX10/11 writes this out as DX10 extension 784 | } 785 | if (ISBITMASK(0x0000ffff, 0xffff0000, 0, 0)) 786 | { 787 | return DXGI_FORMAT_R16G16_SNORM; // D3DX10/11 writes this out as DX10 extension 788 | } 789 | 790 | // No DXGI format maps to ISBITMASK(0x3ff00000, 0x000ffc00, 0x000003ff, 0xc0000000) aka D3DFMT_A2W10V10U10 791 | break; 792 | 793 | case 16: 794 | if (ISBITMASK(0x00ff, 0xff00, 0, 0)) 795 | { 796 | return DXGI_FORMAT_R8G8_SNORM; // D3DX10/11 writes this out as DX10 extension 797 | } 798 | break; 799 | } 800 | 801 | // No DXGI format maps to DDPF_BUMPLUMINANCE aka D3DFMT_L6V5U5, D3DFMT_X8L8V8U8 802 | } 803 | else if (ddpf.flags & DDS_FOURCC) 804 | { 805 | if (MAKEFOURCC('D', 'X', 'T', '1') == ddpf.fourCC) 806 | { 807 | return DXGI_FORMAT_BC1_UNORM; 808 | } 809 | if (MAKEFOURCC('D', 'X', 'T', '3') == ddpf.fourCC) 810 | { 811 | return DXGI_FORMAT_BC2_UNORM; 812 | } 813 | if (MAKEFOURCC('D', 'X', 'T', '5') == ddpf.fourCC) 814 | { 815 | return DXGI_FORMAT_BC3_UNORM; 816 | } 817 | 818 | // While pre-multiplied alpha isn't directly supported by the DXGI formats, 819 | // they are basically the same as these BC formats so they can be mapped 820 | if (MAKEFOURCC('D', 'X', 'T', '2') == ddpf.fourCC) 821 | { 822 | return DXGI_FORMAT_BC2_UNORM; 823 | } 824 | if (MAKEFOURCC('D', 'X', 'T', '4') == ddpf.fourCC) 825 | { 826 | return DXGI_FORMAT_BC3_UNORM; 827 | } 828 | 829 | if (MAKEFOURCC('A', 'T', 'I', '1') == ddpf.fourCC) 830 | { 831 | return DXGI_FORMAT_BC4_UNORM; 832 | } 833 | if (MAKEFOURCC('B', 'C', '4', 'U') == ddpf.fourCC) 834 | { 835 | return DXGI_FORMAT_BC4_UNORM; 836 | } 837 | if (MAKEFOURCC('B', 'C', '4', 'S') == ddpf.fourCC) 838 | { 839 | return DXGI_FORMAT_BC4_SNORM; 840 | } 841 | 842 | if (MAKEFOURCC('A', 'T', 'I', '2') == ddpf.fourCC) 843 | { 844 | return DXGI_FORMAT_BC5_UNORM; 845 | } 846 | if (MAKEFOURCC('B', 'C', '5', 'U') == ddpf.fourCC) 847 | { 848 | return DXGI_FORMAT_BC5_UNORM; 849 | } 850 | if (MAKEFOURCC('B', 'C', '5', 'S') == ddpf.fourCC) 851 | { 852 | return DXGI_FORMAT_BC5_SNORM; 853 | } 854 | 855 | // BC6H and BC7 are written using the "DX10" extended header 856 | 857 | if (MAKEFOURCC('R', 'G', 'B', 'G') == ddpf.fourCC) 858 | { 859 | return DXGI_FORMAT_R8G8_B8G8_UNORM; 860 | } 861 | if (MAKEFOURCC('G', 'R', 'G', 'B') == ddpf.fourCC) 862 | { 863 | return DXGI_FORMAT_G8R8_G8B8_UNORM; 864 | } 865 | 866 | if (MAKEFOURCC('Y', 'U', 'Y', '2') == ddpf.fourCC) 867 | { 868 | return DXGI_FORMAT_YUY2; 869 | } 870 | 871 | // Check for D3DFORMAT enums being set here 872 | switch (ddpf.fourCC) 873 | { 874 | case 36: // D3DFMT_A16B16G16R16 875 | return DXGI_FORMAT_R16G16B16A16_UNORM; 876 | 877 | case 110: // D3DFMT_Q16W16V16U16 878 | return DXGI_FORMAT_R16G16B16A16_SNORM; 879 | 880 | case 111: // D3DFMT_R16F 881 | return DXGI_FORMAT_R16_FLOAT; 882 | 883 | case 112: // D3DFMT_G16R16F 884 | return DXGI_FORMAT_R16G16_FLOAT; 885 | 886 | case 113: // D3DFMT_A16B16G16R16F 887 | return DXGI_FORMAT_R16G16B16A16_FLOAT; 888 | 889 | case 114: // D3DFMT_R32F 890 | return DXGI_FORMAT_R32_FLOAT; 891 | 892 | case 115: // D3DFMT_G32R32F 893 | return DXGI_FORMAT_R32G32_FLOAT; 894 | 895 | case 116: // D3DFMT_A32B32G32R32F 896 | return DXGI_FORMAT_R32G32B32A32_FLOAT; 897 | 898 | // No DXGI format maps to D3DFMT_CxV8U8 899 | } 900 | } 901 | 902 | return DXGI_FORMAT_UNKNOWN; 903 | } 904 | 905 | #undef ISBITMASK 906 | 907 | 908 | //-------------------------------------------------------------------------------------- 909 | DXGI_FORMAT MakeSRGB(_In_ DXGI_FORMAT format) noexcept 910 | { 911 | switch (format) 912 | { 913 | case DXGI_FORMAT_R8G8B8A8_UNORM: 914 | return DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; 915 | 916 | case DXGI_FORMAT_BC1_UNORM: 917 | return DXGI_FORMAT_BC1_UNORM_SRGB; 918 | 919 | case DXGI_FORMAT_BC2_UNORM: 920 | return DXGI_FORMAT_BC2_UNORM_SRGB; 921 | 922 | case DXGI_FORMAT_BC3_UNORM: 923 | return DXGI_FORMAT_BC3_UNORM_SRGB; 924 | 925 | case DXGI_FORMAT_B8G8R8A8_UNORM: 926 | return DXGI_FORMAT_B8G8R8A8_UNORM_SRGB; 927 | 928 | case DXGI_FORMAT_B8G8R8X8_UNORM: 929 | return DXGI_FORMAT_B8G8R8X8_UNORM_SRGB; 930 | 931 | case DXGI_FORMAT_BC7_UNORM: 932 | return DXGI_FORMAT_BC7_UNORM_SRGB; 933 | 934 | default: 935 | return format; 936 | } 937 | } 938 | 939 | 940 | //-------------------------------------------------------------------------------------- 941 | inline DXGI_FORMAT MakeLinear(_In_ DXGI_FORMAT format) noexcept 942 | { 943 | switch (format) 944 | { 945 | case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: 946 | return DXGI_FORMAT_R8G8B8A8_UNORM; 947 | 948 | case DXGI_FORMAT_BC1_UNORM_SRGB: 949 | return DXGI_FORMAT_BC1_UNORM; 950 | 951 | case DXGI_FORMAT_BC2_UNORM_SRGB: 952 | return DXGI_FORMAT_BC2_UNORM; 953 | 954 | case DXGI_FORMAT_BC3_UNORM_SRGB: 955 | return DXGI_FORMAT_BC3_UNORM; 956 | 957 | case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: 958 | return DXGI_FORMAT_B8G8R8A8_UNORM; 959 | 960 | case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: 961 | return DXGI_FORMAT_B8G8R8X8_UNORM; 962 | 963 | case DXGI_FORMAT_BC7_UNORM_SRGB: 964 | return DXGI_FORMAT_BC7_UNORM; 965 | 966 | default: 967 | return format; 968 | } 969 | } 970 | 971 | 972 | //-------------------------------------------------------------------------------------- 973 | HRESULT FillInitData( 974 | _In_ size_t width, 975 | _In_ size_t height, 976 | _In_ size_t depth, 977 | _In_ size_t mipCount, 978 | _In_ size_t arraySize, 979 | _In_ DXGI_FORMAT format, 980 | _In_ size_t maxsize, 981 | _In_ size_t bitSize, 982 | _In_reads_bytes_(bitSize) const uint8_t* bitData, 983 | _Out_ size_t& twidth, 984 | _Out_ size_t& theight, 985 | _Out_ size_t& tdepth, 986 | _Out_ size_t& skipMip, 987 | _Out_writes_(mipCount*arraySize) D3D11_SUBRESOURCE_DATA* initData) noexcept 988 | { 989 | if (!bitData || !initData) 990 | { 991 | return E_POINTER; 992 | } 993 | 994 | skipMip = 0; 995 | twidth = 0; 996 | theight = 0; 997 | tdepth = 0; 998 | 999 | size_t NumBytes = 0; 1000 | size_t RowBytes = 0; 1001 | const uint8_t* pSrcBits = bitData; 1002 | const uint8_t* pEndBits = bitData + bitSize; 1003 | 1004 | size_t index = 0; 1005 | for (size_t j = 0; j < arraySize; j++) 1006 | { 1007 | size_t w = width; 1008 | size_t h = height; 1009 | size_t d = depth; 1010 | for (size_t i = 0; i < mipCount; i++) 1011 | { 1012 | HRESULT hr = GetSurfaceInfo(w, h, format, &NumBytes, &RowBytes, nullptr); 1013 | if (FAILED(hr)) 1014 | return hr; 1015 | 1016 | if (NumBytes > UINT32_MAX || RowBytes > UINT32_MAX) 1017 | return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); 1018 | 1019 | if ((mipCount <= 1) || !maxsize || (w <= maxsize && h <= maxsize && d <= maxsize)) 1020 | { 1021 | if (!twidth) 1022 | { 1023 | twidth = w; 1024 | theight = h; 1025 | tdepth = d; 1026 | } 1027 | 1028 | assert(index < mipCount * arraySize); 1029 | _Analysis_assume_(index < mipCount * arraySize); 1030 | initData[index].pSysMem = pSrcBits; 1031 | initData[index].SysMemPitch = static_cast(RowBytes); 1032 | initData[index].SysMemSlicePitch = static_cast(NumBytes); 1033 | ++index; 1034 | } 1035 | else if (!j) 1036 | { 1037 | // Count number of skipped mipmaps (first item only) 1038 | ++skipMip; 1039 | } 1040 | 1041 | if (pSrcBits + (NumBytes*d) > pEndBits) 1042 | { 1043 | return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF); 1044 | } 1045 | 1046 | pSrcBits += NumBytes * d; 1047 | 1048 | w = w >> 1; 1049 | h = h >> 1; 1050 | d = d >> 1; 1051 | if (w == 0) 1052 | { 1053 | w = 1; 1054 | } 1055 | if (h == 0) 1056 | { 1057 | h = 1; 1058 | } 1059 | if (d == 0) 1060 | { 1061 | d = 1; 1062 | } 1063 | } 1064 | } 1065 | 1066 | return (index > 0) ? S_OK : E_FAIL; 1067 | } 1068 | 1069 | 1070 | //-------------------------------------------------------------------------------------- 1071 | HRESULT CreateD3DResources( 1072 | _In_ ID3D11Device* d3dDevice, 1073 | _In_ uint32_t resDim, 1074 | _In_ size_t width, 1075 | _In_ size_t height, 1076 | _In_ size_t depth, 1077 | _In_ size_t mipCount, 1078 | _In_ size_t arraySize, 1079 | _In_ DXGI_FORMAT format, 1080 | _In_ D3D11_USAGE usage, 1081 | _In_ unsigned int bindFlags, 1082 | _In_ unsigned int cpuAccessFlags, 1083 | _In_ unsigned int miscFlags, 1084 | _In_ DDS_LOADER_FLAGS loadFlags, 1085 | _In_ bool isCubeMap, 1086 | _In_reads_opt_(mipCount*arraySize) const D3D11_SUBRESOURCE_DATA* initData, 1087 | _Outptr_opt_ ID3D11Resource** texture, 1088 | _Outptr_opt_ ID3D11ShaderResourceView** textureView) noexcept 1089 | { 1090 | if (!d3dDevice) 1091 | return E_POINTER; 1092 | 1093 | HRESULT hr = E_FAIL; 1094 | 1095 | if (loadFlags & DDS_LOADER_FORCE_SRGB) 1096 | { 1097 | format = MakeSRGB(format); 1098 | } 1099 | else if (loadFlags & DDS_LOADER_IGNORE_SRGB) 1100 | { 1101 | format = MakeLinear(format); 1102 | } 1103 | 1104 | switch (resDim) 1105 | { 1106 | case D3D11_RESOURCE_DIMENSION_TEXTURE1D: 1107 | { 1108 | D3D11_TEXTURE1D_DESC desc; 1109 | desc.Width = static_cast(width); 1110 | desc.MipLevels = static_cast(mipCount); 1111 | desc.ArraySize = static_cast(arraySize); 1112 | desc.Format = format; 1113 | desc.Usage = usage; 1114 | desc.BindFlags = bindFlags; 1115 | desc.CPUAccessFlags = cpuAccessFlags; 1116 | desc.MiscFlags = miscFlags & ~static_cast(D3D11_RESOURCE_MISC_TEXTURECUBE); 1117 | 1118 | ID3D11Texture1D* tex = nullptr; 1119 | hr = d3dDevice->CreateTexture1D(&desc, 1120 | initData, 1121 | &tex 1122 | ); 1123 | if (SUCCEEDED(hr) && tex) 1124 | { 1125 | if (textureView) 1126 | { 1127 | D3D11_SHADER_RESOURCE_VIEW_DESC SRVDesc = {}; 1128 | SRVDesc.Format = format; 1129 | 1130 | if (arraySize > 1) 1131 | { 1132 | SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE1DARRAY; 1133 | SRVDesc.Texture1DArray.MipLevels = (!mipCount) ? UINT(-1) : desc.MipLevels; 1134 | SRVDesc.Texture1DArray.ArraySize = static_cast(arraySize); 1135 | } 1136 | else 1137 | { 1138 | SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE1D; 1139 | SRVDesc.Texture1D.MipLevels = (!mipCount) ? UINT(-1) : desc.MipLevels; 1140 | } 1141 | 1142 | hr = d3dDevice->CreateShaderResourceView(tex, 1143 | &SRVDesc, 1144 | textureView 1145 | ); 1146 | if (FAILED(hr)) 1147 | { 1148 | tex->Release(); 1149 | return hr; 1150 | } 1151 | } 1152 | 1153 | if (texture) 1154 | { 1155 | *texture = tex; 1156 | } 1157 | else 1158 | { 1159 | SetDebugObjectName(tex, "DDSTextureLoader"); 1160 | tex->Release(); 1161 | } 1162 | } 1163 | } 1164 | break; 1165 | 1166 | case D3D11_RESOURCE_DIMENSION_TEXTURE2D: 1167 | { 1168 | D3D11_TEXTURE2D_DESC desc; 1169 | desc.Width = static_cast(width); 1170 | desc.Height = static_cast(height); 1171 | desc.MipLevels = static_cast(mipCount); 1172 | desc.ArraySize = static_cast(arraySize); 1173 | desc.Format = format; 1174 | desc.SampleDesc.Count = 1; 1175 | desc.SampleDesc.Quality = 0; 1176 | desc.Usage = usage; 1177 | desc.BindFlags = bindFlags; 1178 | desc.CPUAccessFlags = cpuAccessFlags; 1179 | if (isCubeMap) 1180 | { 1181 | desc.MiscFlags = miscFlags | D3D11_RESOURCE_MISC_TEXTURECUBE; 1182 | } 1183 | else 1184 | { 1185 | desc.MiscFlags = miscFlags & ~static_cast(D3D11_RESOURCE_MISC_TEXTURECUBE); 1186 | } 1187 | 1188 | ID3D11Texture2D* tex = nullptr; 1189 | hr = d3dDevice->CreateTexture2D(&desc, 1190 | initData, 1191 | &tex 1192 | ); 1193 | if (SUCCEEDED(hr) && tex) 1194 | { 1195 | if (textureView) 1196 | { 1197 | D3D11_SHADER_RESOURCE_VIEW_DESC SRVDesc = {}; 1198 | SRVDesc.Format = format; 1199 | 1200 | if (isCubeMap) 1201 | { 1202 | if (arraySize > 6) 1203 | { 1204 | SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBEARRAY; 1205 | SRVDesc.TextureCubeArray.MipLevels = (!mipCount) ? UINT(-1) : desc.MipLevels; 1206 | 1207 | // Earlier we set arraySize to (NumCubes * 6) 1208 | SRVDesc.TextureCubeArray.NumCubes = static_cast(arraySize / 6); 1209 | } 1210 | else 1211 | { 1212 | SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE; 1213 | SRVDesc.TextureCube.MipLevels = (!mipCount) ? UINT(-1) : desc.MipLevels; 1214 | } 1215 | } 1216 | else if (arraySize > 1) 1217 | { 1218 | SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY; 1219 | SRVDesc.Texture2DArray.MipLevels = (!mipCount) ? UINT(-1) : desc.MipLevels; 1220 | SRVDesc.Texture2DArray.ArraySize = static_cast(arraySize); 1221 | } 1222 | else 1223 | { 1224 | SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; 1225 | SRVDesc.Texture2D.MipLevels = (!mipCount) ? UINT(-1) : desc.MipLevels; 1226 | } 1227 | 1228 | hr = d3dDevice->CreateShaderResourceView(tex, 1229 | &SRVDesc, 1230 | textureView 1231 | ); 1232 | if (FAILED(hr)) 1233 | { 1234 | tex->Release(); 1235 | return hr; 1236 | } 1237 | } 1238 | 1239 | if (texture) 1240 | { 1241 | *texture = tex; 1242 | } 1243 | else 1244 | { 1245 | SetDebugObjectName(tex, "DDSTextureLoader"); 1246 | tex->Release(); 1247 | } 1248 | } 1249 | } 1250 | break; 1251 | 1252 | case D3D11_RESOURCE_DIMENSION_TEXTURE3D: 1253 | { 1254 | D3D11_TEXTURE3D_DESC desc; 1255 | desc.Width = static_cast(width); 1256 | desc.Height = static_cast(height); 1257 | desc.Depth = static_cast(depth); 1258 | desc.MipLevels = static_cast(mipCount); 1259 | desc.Format = format; 1260 | desc.Usage = usage; 1261 | desc.BindFlags = bindFlags; 1262 | desc.CPUAccessFlags = cpuAccessFlags; 1263 | desc.MiscFlags = miscFlags & ~static_cast(D3D11_RESOURCE_MISC_TEXTURECUBE); 1264 | 1265 | ID3D11Texture3D* tex = nullptr; 1266 | hr = d3dDevice->CreateTexture3D(&desc, 1267 | initData, 1268 | &tex 1269 | ); 1270 | if (SUCCEEDED(hr) && tex) 1271 | { 1272 | if (textureView) 1273 | { 1274 | D3D11_SHADER_RESOURCE_VIEW_DESC SRVDesc = {}; 1275 | SRVDesc.Format = format; 1276 | 1277 | SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D; 1278 | SRVDesc.Texture3D.MipLevels = (!mipCount) ? UINT(-1) : desc.MipLevels; 1279 | 1280 | hr = d3dDevice->CreateShaderResourceView(tex, 1281 | &SRVDesc, 1282 | textureView 1283 | ); 1284 | if (FAILED(hr)) 1285 | { 1286 | tex->Release(); 1287 | return hr; 1288 | } 1289 | } 1290 | 1291 | if (texture) 1292 | { 1293 | *texture = tex; 1294 | } 1295 | else 1296 | { 1297 | SetDebugObjectName(tex, "DDSTextureLoader"); 1298 | tex->Release(); 1299 | } 1300 | } 1301 | } 1302 | break; 1303 | } 1304 | 1305 | return hr; 1306 | } 1307 | 1308 | //-------------------------------------------------------------------------------------- 1309 | HRESULT CreateTextureFromDDS( 1310 | _In_ ID3D11Device* d3dDevice, 1311 | _In_opt_ ID3D11DeviceContext* d3dContext, 1312 | _In_ const DDS_HEADER* header, 1313 | _In_reads_bytes_(bitSize) const uint8_t* bitData, 1314 | _In_ size_t bitSize, 1315 | _In_ size_t maxsize, 1316 | _In_ D3D11_USAGE usage, 1317 | _In_ unsigned int bindFlags, 1318 | _In_ unsigned int cpuAccessFlags, 1319 | _In_ unsigned int miscFlags, 1320 | _In_ DDS_LOADER_FLAGS loadFlags, 1321 | _Outptr_opt_ ID3D11Resource** texture, 1322 | _Outptr_opt_ ID3D11ShaderResourceView** textureView) noexcept 1323 | { 1324 | HRESULT hr = S_OK; 1325 | 1326 | const UINT width = header->width; 1327 | UINT height = header->height; 1328 | UINT depth = header->depth; 1329 | 1330 | uint32_t resDim = D3D11_RESOURCE_DIMENSION_UNKNOWN; 1331 | UINT arraySize = 1; 1332 | DXGI_FORMAT format = DXGI_FORMAT_UNKNOWN; 1333 | bool isCubeMap = false; 1334 | 1335 | size_t mipCount = header->mipMapCount; 1336 | if (0 == mipCount) 1337 | { 1338 | mipCount = 1; 1339 | } 1340 | 1341 | if ((header->ddspf.flags & DDS_FOURCC) && 1342 | (MAKEFOURCC('D', 'X', '1', '0') == header->ddspf.fourCC)) 1343 | { 1344 | auto d3d10ext = reinterpret_cast(reinterpret_cast(header) + sizeof(DDS_HEADER)); 1345 | 1346 | arraySize = d3d10ext->arraySize; 1347 | if (arraySize == 0) 1348 | { 1349 | return HRESULT_FROM_WIN32(ERROR_INVALID_DATA); 1350 | } 1351 | 1352 | switch (d3d10ext->dxgiFormat) 1353 | { 1354 | case DXGI_FORMAT_NV12: 1355 | case DXGI_FORMAT_P010: 1356 | case DXGI_FORMAT_P016: 1357 | case DXGI_FORMAT_420_OPAQUE: 1358 | if ((d3d10ext->resourceDimension != D3D11_RESOURCE_DIMENSION_TEXTURE2D) 1359 | || (width % 2) != 0 || (height % 2) != 0) 1360 | { 1361 | return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); 1362 | } 1363 | break; 1364 | 1365 | case DXGI_FORMAT_YUY2: 1366 | case DXGI_FORMAT_Y210: 1367 | case DXGI_FORMAT_Y216: 1368 | case DXGI_FORMAT_P208: 1369 | if ((width % 2) != 0) 1370 | { 1371 | return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); 1372 | } 1373 | break; 1374 | 1375 | case DXGI_FORMAT_NV11: 1376 | if ((width % 4) != 0) 1377 | { 1378 | return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); 1379 | } 1380 | break; 1381 | 1382 | case DXGI_FORMAT_AI44: 1383 | case DXGI_FORMAT_IA44: 1384 | case DXGI_FORMAT_P8: 1385 | case DXGI_FORMAT_A8P8: 1386 | return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); 1387 | 1388 | default: 1389 | if (BitsPerPixel(d3d10ext->dxgiFormat) == 0) 1390 | { 1391 | return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); 1392 | } 1393 | } 1394 | 1395 | format = d3d10ext->dxgiFormat; 1396 | 1397 | switch (d3d10ext->resourceDimension) 1398 | { 1399 | case D3D11_RESOURCE_DIMENSION_TEXTURE1D: 1400 | // D3DX writes 1D textures with a fixed Height of 1 1401 | if ((header->flags & DDS_HEIGHT) && height != 1) 1402 | { 1403 | return HRESULT_FROM_WIN32(ERROR_INVALID_DATA); 1404 | } 1405 | height = depth = 1; 1406 | break; 1407 | 1408 | case D3D11_RESOURCE_DIMENSION_TEXTURE2D: 1409 | if (d3d10ext->miscFlag & D3D11_RESOURCE_MISC_TEXTURECUBE) 1410 | { 1411 | arraySize *= 6; 1412 | isCubeMap = true; 1413 | } 1414 | depth = 1; 1415 | break; 1416 | 1417 | case D3D11_RESOURCE_DIMENSION_TEXTURE3D: 1418 | if (!(header->flags & DDS_HEADER_FLAGS_VOLUME)) 1419 | { 1420 | return HRESULT_FROM_WIN32(ERROR_INVALID_DATA); 1421 | } 1422 | 1423 | if (arraySize > 1) 1424 | { 1425 | return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); 1426 | } 1427 | break; 1428 | 1429 | default: 1430 | return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); 1431 | } 1432 | 1433 | resDim = d3d10ext->resourceDimension; 1434 | } 1435 | else 1436 | { 1437 | format = GetDXGIFormat(header->ddspf); 1438 | 1439 | if (format == DXGI_FORMAT_UNKNOWN) 1440 | { 1441 | return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); 1442 | } 1443 | 1444 | if (header->flags & DDS_HEADER_FLAGS_VOLUME) 1445 | { 1446 | resDim = D3D11_RESOURCE_DIMENSION_TEXTURE3D; 1447 | } 1448 | else 1449 | { 1450 | if (header->caps2 & DDS_CUBEMAP) 1451 | { 1452 | // We require all six faces to be defined 1453 | if ((header->caps2 & DDS_CUBEMAP_ALLFACES) != DDS_CUBEMAP_ALLFACES) 1454 | { 1455 | return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); 1456 | } 1457 | 1458 | arraySize = 6; 1459 | isCubeMap = true; 1460 | } 1461 | 1462 | depth = 1; 1463 | resDim = D3D11_RESOURCE_DIMENSION_TEXTURE2D; 1464 | 1465 | // Note there's no way for a legacy Direct3D 9 DDS to express a '1D' texture 1466 | } 1467 | 1468 | assert(BitsPerPixel(format) != 0); 1469 | } 1470 | 1471 | // Bound sizes (for security purposes we don't trust DDS file metadata larger than the D3D 11.x hardware requirements) 1472 | if (mipCount > D3D11_REQ_MIP_LEVELS) 1473 | { 1474 | return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); 1475 | } 1476 | 1477 | switch (resDim) 1478 | { 1479 | case D3D11_RESOURCE_DIMENSION_TEXTURE1D: 1480 | if ((arraySize > D3D11_REQ_TEXTURE1D_ARRAY_AXIS_DIMENSION) || 1481 | (width > D3D11_REQ_TEXTURE1D_U_DIMENSION)) 1482 | { 1483 | return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); 1484 | } 1485 | break; 1486 | 1487 | case D3D11_RESOURCE_DIMENSION_TEXTURE2D: 1488 | if (isCubeMap) 1489 | { 1490 | // This is the right bound because we set arraySize to (NumCubes*6) above 1491 | if ((arraySize > D3D11_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION) || 1492 | (width > D3D11_REQ_TEXTURECUBE_DIMENSION) || 1493 | (height > D3D11_REQ_TEXTURECUBE_DIMENSION)) 1494 | { 1495 | return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); 1496 | } 1497 | } 1498 | else if ((arraySize > D3D11_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION) || 1499 | (width > D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION) || 1500 | (height > D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION)) 1501 | { 1502 | return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); 1503 | } 1504 | break; 1505 | 1506 | case D3D11_RESOURCE_DIMENSION_TEXTURE3D: 1507 | if ((arraySize > 1) || 1508 | (width > D3D11_REQ_TEXTURE3D_U_V_OR_W_DIMENSION) || 1509 | (height > D3D11_REQ_TEXTURE3D_U_V_OR_W_DIMENSION) || 1510 | (depth > D3D11_REQ_TEXTURE3D_U_V_OR_W_DIMENSION)) 1511 | { 1512 | return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); 1513 | } 1514 | break; 1515 | 1516 | default: 1517 | return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); 1518 | } 1519 | 1520 | bool autogen = false; 1521 | if (mipCount == 1 && d3dContext && textureView) // Must have context and shader-view to auto generate mipmaps 1522 | { 1523 | // See if format is supported for auto-gen mipmaps (varies by feature level) 1524 | UINT fmtSupport = 0; 1525 | hr = d3dDevice->CheckFormatSupport(format, &fmtSupport); 1526 | if (SUCCEEDED(hr) && (fmtSupport & D3D11_FORMAT_SUPPORT_MIP_AUTOGEN)) 1527 | { 1528 | // 10level9 feature levels do not support auto-gen mipgen for volume textures 1529 | if ((resDim != D3D11_RESOURCE_DIMENSION_TEXTURE3D) 1530 | || (d3dDevice->GetFeatureLevel() >= D3D_FEATURE_LEVEL_10_0)) 1531 | { 1532 | autogen = true; 1533 | } 1534 | } 1535 | } 1536 | 1537 | if (autogen) 1538 | { 1539 | // Create texture with auto-generated mipmaps 1540 | ID3D11Resource* tex = nullptr; 1541 | hr = CreateD3DResources(d3dDevice, 1542 | resDim, width, height, depth, 0, arraySize, 1543 | format, 1544 | usage, 1545 | bindFlags | D3D11_BIND_RENDER_TARGET, 1546 | cpuAccessFlags, 1547 | miscFlags | D3D11_RESOURCE_MISC_GENERATE_MIPS, 1548 | loadFlags, 1549 | isCubeMap, 1550 | nullptr, 1551 | &tex, textureView); 1552 | if (SUCCEEDED(hr)) 1553 | { 1554 | size_t numBytes = 0; 1555 | size_t rowBytes = 0; 1556 | hr = GetSurfaceInfo(width, height, format, &numBytes, &rowBytes, nullptr); 1557 | if (FAILED(hr)) 1558 | return hr; 1559 | 1560 | if (numBytes > bitSize) 1561 | { 1562 | (*textureView)->Release(); 1563 | *textureView = nullptr; 1564 | tex->Release(); 1565 | return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF); 1566 | } 1567 | 1568 | if (numBytes > UINT32_MAX || rowBytes > UINT32_MAX) 1569 | return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); 1570 | 1571 | D3D11_SHADER_RESOURCE_VIEW_DESC desc; 1572 | (*textureView)->GetDesc(&desc); 1573 | 1574 | UINT mipLevels = 1; 1575 | 1576 | switch (desc.ViewDimension) 1577 | { 1578 | case D3D_SRV_DIMENSION_TEXTURE1D: mipLevels = desc.Texture1D.MipLevels; break; 1579 | case D3D_SRV_DIMENSION_TEXTURE1DARRAY: mipLevels = desc.Texture1DArray.MipLevels; break; 1580 | case D3D_SRV_DIMENSION_TEXTURE2D: mipLevels = desc.Texture2D.MipLevels; break; 1581 | case D3D_SRV_DIMENSION_TEXTURE2DARRAY: mipLevels = desc.Texture2DArray.MipLevels; break; 1582 | case D3D_SRV_DIMENSION_TEXTURECUBE: mipLevels = desc.TextureCube.MipLevels; break; 1583 | case D3D_SRV_DIMENSION_TEXTURECUBEARRAY:mipLevels = desc.TextureCubeArray.MipLevels; break; 1584 | case D3D_SRV_DIMENSION_TEXTURE3D: mipLevels = desc.Texture3D.MipLevels; break; 1585 | default: 1586 | (*textureView)->Release(); 1587 | *textureView = nullptr; 1588 | tex->Release(); 1589 | return E_UNEXPECTED; 1590 | } 1591 | 1592 | if (arraySize > 1) 1593 | { 1594 | const uint8_t* pSrcBits = bitData; 1595 | const uint8_t* pEndBits = bitData + bitSize; 1596 | for (UINT item = 0; item < arraySize; ++item) 1597 | { 1598 | if ((pSrcBits + numBytes) > pEndBits) 1599 | { 1600 | (*textureView)->Release(); 1601 | *textureView = nullptr; 1602 | tex->Release(); 1603 | return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF); 1604 | } 1605 | 1606 | const UINT res = D3D11CalcSubresource(0, item, mipLevels); 1607 | d3dContext->UpdateSubresource(tex, res, nullptr, pSrcBits, static_cast(rowBytes), static_cast(numBytes)); 1608 | pSrcBits += numBytes; 1609 | } 1610 | } 1611 | else 1612 | { 1613 | d3dContext->UpdateSubresource(tex, 0, nullptr, bitData, static_cast(rowBytes), static_cast(numBytes)); 1614 | } 1615 | 1616 | d3dContext->GenerateMips(*textureView); 1617 | 1618 | if (texture) 1619 | { 1620 | *texture = tex; 1621 | } 1622 | else 1623 | { 1624 | tex->Release(); 1625 | } 1626 | } 1627 | } 1628 | else 1629 | { 1630 | // Create the texture 1631 | std::unique_ptr initData(new (std::nothrow) D3D11_SUBRESOURCE_DATA[mipCount * arraySize]); 1632 | if (!initData) 1633 | { 1634 | return E_OUTOFMEMORY; 1635 | } 1636 | 1637 | size_t skipMip = 0; 1638 | size_t twidth = 0; 1639 | size_t theight = 0; 1640 | size_t tdepth = 0; 1641 | hr = FillInitData(width, height, depth, mipCount, arraySize, 1642 | format, maxsize, bitSize, bitData, 1643 | twidth, theight, tdepth, skipMip, initData.get()); 1644 | 1645 | if (SUCCEEDED(hr)) 1646 | { 1647 | hr = CreateD3DResources(d3dDevice, 1648 | resDim, twidth, theight, tdepth, mipCount - skipMip, arraySize, 1649 | format, 1650 | usage, bindFlags, cpuAccessFlags, miscFlags, 1651 | loadFlags, 1652 | isCubeMap, 1653 | initData.get(), 1654 | texture, textureView); 1655 | 1656 | if (FAILED(hr) && !maxsize && (mipCount > 1)) 1657 | { 1658 | // Retry with a maxsize determined by feature level 1659 | switch (d3dDevice->GetFeatureLevel()) 1660 | { 1661 | case D3D_FEATURE_LEVEL_9_1: 1662 | case D3D_FEATURE_LEVEL_9_2: 1663 | if (isCubeMap) 1664 | { 1665 | maxsize = 512u /*D3D_FL9_1_REQ_TEXTURECUBE_DIMENSION*/; 1666 | } 1667 | else 1668 | { 1669 | maxsize = (resDim == D3D11_RESOURCE_DIMENSION_TEXTURE3D) 1670 | ? 256u /*D3D_FL9_1_REQ_TEXTURE3D_U_V_OR_W_DIMENSION*/ 1671 | : 2048u /*D3D_FL9_1_REQ_TEXTURE2D_U_OR_V_DIMENSION*/; 1672 | } 1673 | break; 1674 | 1675 | case D3D_FEATURE_LEVEL_9_3: 1676 | maxsize = (resDim == D3D11_RESOURCE_DIMENSION_TEXTURE3D) 1677 | ? 256u /*D3D_FL9_1_REQ_TEXTURE3D_U_V_OR_W_DIMENSION*/ 1678 | : 4096u /*D3D_FL9_3_REQ_TEXTURE2D_U_OR_V_DIMENSION*/; 1679 | break; 1680 | 1681 | default: // D3D_FEATURE_LEVEL_10_0 & D3D_FEATURE_LEVEL_10_1 1682 | maxsize = (resDim == D3D11_RESOURCE_DIMENSION_TEXTURE3D) 1683 | ? 2048u /*D3D10_REQ_TEXTURE3D_U_V_OR_W_DIMENSION*/ 1684 | : 8192u /*D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION*/; 1685 | break; 1686 | } 1687 | 1688 | hr = FillInitData(width, height, depth, mipCount, arraySize, format, maxsize, bitSize, bitData, 1689 | twidth, theight, tdepth, skipMip, initData.get()); 1690 | if (SUCCEEDED(hr)) 1691 | { 1692 | hr = CreateD3DResources(d3dDevice, 1693 | resDim, twidth, theight, tdepth, mipCount - skipMip, arraySize, 1694 | format, 1695 | usage, bindFlags, cpuAccessFlags, miscFlags, 1696 | loadFlags, 1697 | isCubeMap, 1698 | initData.get(), 1699 | texture, textureView); 1700 | } 1701 | } 1702 | } 1703 | } 1704 | 1705 | return hr; 1706 | } 1707 | 1708 | 1709 | //-------------------------------------------------------------------------------------- 1710 | DDS_ALPHA_MODE GetAlphaMode(_In_ const DDS_HEADER* header) noexcept 1711 | { 1712 | if (header->ddspf.flags & DDS_FOURCC) 1713 | { 1714 | if (MAKEFOURCC('D', 'X', '1', '0') == header->ddspf.fourCC) 1715 | { 1716 | auto d3d10ext = reinterpret_cast(reinterpret_cast(header) + sizeof(DDS_HEADER)); 1717 | auto const mode = static_cast(d3d10ext->miscFlags2 & DDS_MISC_FLAGS2_ALPHA_MODE_MASK); 1718 | switch (mode) 1719 | { 1720 | case DDS_ALPHA_MODE_STRAIGHT: 1721 | case DDS_ALPHA_MODE_PREMULTIPLIED: 1722 | case DDS_ALPHA_MODE_OPAQUE: 1723 | case DDS_ALPHA_MODE_CUSTOM: 1724 | return mode; 1725 | 1726 | case DDS_ALPHA_MODE_UNKNOWN: 1727 | default: 1728 | break; 1729 | } 1730 | } 1731 | else if ((MAKEFOURCC('D', 'X', 'T', '2') == header->ddspf.fourCC) 1732 | || (MAKEFOURCC('D', 'X', 'T', '4') == header->ddspf.fourCC)) 1733 | { 1734 | return DDS_ALPHA_MODE_PREMULTIPLIED; 1735 | } 1736 | } 1737 | 1738 | return DDS_ALPHA_MODE_UNKNOWN; 1739 | } 1740 | 1741 | //-------------------------------------------------------------------------------------- 1742 | void SetDebugTextureInfo( 1743 | _In_z_ const wchar_t* fileName, 1744 | _In_opt_ ID3D11Resource** texture, 1745 | _In_opt_ ID3D11ShaderResourceView** textureView) noexcept 1746 | { 1747 | #if !defined(NO_D3D11_DEBUG_NAME) && ( defined(_DEBUG) || defined(PROFILE) ) 1748 | if (texture || textureView) 1749 | { 1750 | CHAR strFileA[MAX_PATH]; 1751 | const int result = WideCharToMultiByte(CP_UTF8, 1752 | WC_NO_BEST_FIT_CHARS, 1753 | fileName, 1754 | -1, 1755 | strFileA, 1756 | MAX_PATH, 1757 | nullptr, 1758 | nullptr 1759 | ); 1760 | if (result > 0) 1761 | { 1762 | const char* pstrName = strrchr(strFileA, '\\'); 1763 | if (!pstrName) 1764 | { 1765 | pstrName = strFileA; 1766 | } 1767 | else 1768 | { 1769 | pstrName++; 1770 | } 1771 | 1772 | if (texture && *texture) 1773 | { 1774 | (*texture)->SetPrivateData(WKPDID_D3DDebugObjectName, 1775 | static_cast(strnlen_s(pstrName, MAX_PATH)), 1776 | pstrName 1777 | ); 1778 | } 1779 | 1780 | if (textureView && *textureView) 1781 | { 1782 | (*textureView)->SetPrivateData(WKPDID_D3DDebugObjectName, 1783 | static_cast(strnlen_s(pstrName, MAX_PATH)), 1784 | pstrName 1785 | ); 1786 | } 1787 | } 1788 | } 1789 | #else 1790 | UNREFERENCED_PARAMETER(fileName); 1791 | UNREFERENCED_PARAMETER(texture); 1792 | UNREFERENCED_PARAMETER(textureView); 1793 | #endif 1794 | } 1795 | } // anonymous namespace 1796 | 1797 | //-------------------------------------------------------------------------------------- 1798 | _Use_decl_annotations_ 1799 | HRESULT DirectX::CreateDDSTextureFromMemory( 1800 | ID3D11Device* d3dDevice, 1801 | const uint8_t* ddsData, 1802 | size_t ddsDataSize, 1803 | ID3D11Resource** texture, 1804 | ID3D11ShaderResourceView** textureView, 1805 | size_t maxsize, 1806 | DDS_ALPHA_MODE* alphaMode) noexcept 1807 | { 1808 | return CreateDDSTextureFromMemoryEx(d3dDevice, nullptr, 1809 | ddsData, ddsDataSize, 1810 | maxsize, 1811 | D3D11_USAGE_DEFAULT, D3D11_BIND_SHADER_RESOURCE, 0, 0, 1812 | DDS_LOADER_DEFAULT, 1813 | texture, textureView, alphaMode); 1814 | } 1815 | 1816 | _Use_decl_annotations_ 1817 | HRESULT DirectX::CreateDDSTextureFromMemory( 1818 | ID3D11Device* d3dDevice, 1819 | ID3D11DeviceContext* d3dContext, 1820 | const uint8_t* ddsData, 1821 | size_t ddsDataSize, 1822 | ID3D11Resource** texture, 1823 | ID3D11ShaderResourceView** textureView, 1824 | size_t maxsize, 1825 | DDS_ALPHA_MODE* alphaMode) noexcept 1826 | { 1827 | return CreateDDSTextureFromMemoryEx(d3dDevice, d3dContext, 1828 | ddsData, ddsDataSize, 1829 | maxsize, 1830 | D3D11_USAGE_DEFAULT, D3D11_BIND_SHADER_RESOURCE, 0, 0, 1831 | DDS_LOADER_DEFAULT, 1832 | texture, textureView, alphaMode); 1833 | } 1834 | 1835 | _Use_decl_annotations_ 1836 | HRESULT DirectX::CreateDDSTextureFromMemoryEx( 1837 | ID3D11Device* d3dDevice, 1838 | const uint8_t* ddsData, 1839 | size_t ddsDataSize, 1840 | size_t maxsize, 1841 | D3D11_USAGE usage, 1842 | unsigned int bindFlags, 1843 | unsigned int cpuAccessFlags, 1844 | unsigned int miscFlags, 1845 | DDS_LOADER_FLAGS loadFlags, 1846 | ID3D11Resource** texture, 1847 | ID3D11ShaderResourceView** textureView, 1848 | DDS_ALPHA_MODE* alphaMode) noexcept 1849 | { 1850 | return CreateDDSTextureFromMemoryEx(d3dDevice, nullptr, 1851 | ddsData, ddsDataSize, 1852 | maxsize, 1853 | usage, bindFlags, cpuAccessFlags, miscFlags, 1854 | loadFlags, 1855 | texture, textureView, alphaMode); 1856 | } 1857 | 1858 | _Use_decl_annotations_ 1859 | HRESULT DirectX::CreateDDSTextureFromMemoryEx( 1860 | ID3D11Device* d3dDevice, 1861 | ID3D11DeviceContext* d3dContext, 1862 | const uint8_t* ddsData, 1863 | size_t ddsDataSize, 1864 | size_t maxsize, 1865 | D3D11_USAGE usage, 1866 | unsigned int bindFlags, 1867 | unsigned int cpuAccessFlags, 1868 | unsigned int miscFlags, 1869 | DDS_LOADER_FLAGS loadFlags, 1870 | ID3D11Resource** texture, 1871 | ID3D11ShaderResourceView** textureView, 1872 | DDS_ALPHA_MODE* alphaMode) noexcept 1873 | { 1874 | if (texture) 1875 | { 1876 | *texture = nullptr; 1877 | } 1878 | if (textureView) 1879 | { 1880 | *textureView = nullptr; 1881 | } 1882 | if (alphaMode) 1883 | { 1884 | *alphaMode = DDS_ALPHA_MODE_UNKNOWN; 1885 | } 1886 | 1887 | if (!d3dDevice || !ddsData || (!texture && !textureView)) 1888 | { 1889 | return E_INVALIDARG; 1890 | } 1891 | 1892 | if (textureView && !(bindFlags & D3D11_BIND_SHADER_RESOURCE)) 1893 | { 1894 | return E_INVALIDARG; 1895 | } 1896 | 1897 | // Validate DDS file in memory 1898 | const DDS_HEADER* header = nullptr; 1899 | const uint8_t* bitData = nullptr; 1900 | size_t bitSize = 0; 1901 | 1902 | HRESULT hr = LoadTextureDataFromMemory(ddsData, ddsDataSize, 1903 | &header, 1904 | &bitData, 1905 | &bitSize 1906 | ); 1907 | if (FAILED(hr)) 1908 | { 1909 | return hr; 1910 | } 1911 | 1912 | hr = CreateTextureFromDDS(d3dDevice, d3dContext, 1913 | header, bitData, bitSize, 1914 | maxsize, 1915 | usage, bindFlags, cpuAccessFlags, miscFlags, 1916 | loadFlags, 1917 | texture, textureView); 1918 | if (SUCCEEDED(hr)) 1919 | { 1920 | if (texture && *texture) 1921 | { 1922 | SetDebugObjectName(*texture, "DDSTextureLoader"); 1923 | } 1924 | 1925 | if (textureView && *textureView) 1926 | { 1927 | SetDebugObjectName(*textureView, "DDSTextureLoader"); 1928 | } 1929 | 1930 | if (alphaMode) 1931 | *alphaMode = GetAlphaMode(header); 1932 | } 1933 | 1934 | return hr; 1935 | } 1936 | 1937 | //-------------------------------------------------------------------------------------- 1938 | _Use_decl_annotations_ 1939 | HRESULT DirectX::CreateDDSTextureFromFile( 1940 | ID3D11Device* d3dDevice, 1941 | const wchar_t* fileName, 1942 | ID3D11Resource** texture, 1943 | ID3D11ShaderResourceView** textureView, 1944 | size_t maxsize, 1945 | DDS_ALPHA_MODE* alphaMode) noexcept 1946 | { 1947 | return CreateDDSTextureFromFileEx(d3dDevice, nullptr, 1948 | fileName, maxsize, 1949 | D3D11_USAGE_DEFAULT, D3D11_BIND_SHADER_RESOURCE, 0, 0, 1950 | DDS_LOADER_DEFAULT, 1951 | texture, textureView, alphaMode); 1952 | } 1953 | 1954 | _Use_decl_annotations_ 1955 | HRESULT DirectX::CreateDDSTextureFromFile( 1956 | ID3D11Device* d3dDevice, 1957 | ID3D11DeviceContext* d3dContext, 1958 | const wchar_t* fileName, 1959 | ID3D11Resource** texture, 1960 | ID3D11ShaderResourceView** textureView, 1961 | size_t maxsize, 1962 | DDS_ALPHA_MODE* alphaMode) noexcept 1963 | { 1964 | return CreateDDSTextureFromFileEx(d3dDevice, d3dContext, 1965 | fileName, 1966 | maxsize, 1967 | D3D11_USAGE_DEFAULT, D3D11_BIND_SHADER_RESOURCE, 0, 0, 1968 | DDS_LOADER_DEFAULT, 1969 | texture, textureView, alphaMode); 1970 | } 1971 | 1972 | _Use_decl_annotations_ 1973 | HRESULT DirectX::CreateDDSTextureFromFileEx( 1974 | ID3D11Device* d3dDevice, 1975 | const wchar_t* fileName, 1976 | size_t maxsize, 1977 | D3D11_USAGE usage, 1978 | unsigned int bindFlags, 1979 | unsigned int cpuAccessFlags, 1980 | unsigned int miscFlags, 1981 | DDS_LOADER_FLAGS loadFlags, 1982 | ID3D11Resource** texture, 1983 | ID3D11ShaderResourceView** textureView, 1984 | DDS_ALPHA_MODE* alphaMode) noexcept 1985 | { 1986 | return CreateDDSTextureFromFileEx(d3dDevice, nullptr, 1987 | fileName, 1988 | maxsize, 1989 | usage, bindFlags, cpuAccessFlags, miscFlags, 1990 | loadFlags, 1991 | texture, textureView, alphaMode); 1992 | } 1993 | 1994 | _Use_decl_annotations_ 1995 | HRESULT DirectX::CreateDDSTextureFromFileEx( 1996 | ID3D11Device* d3dDevice, 1997 | ID3D11DeviceContext* d3dContext, 1998 | const wchar_t* fileName, 1999 | size_t maxsize, 2000 | D3D11_USAGE usage, 2001 | unsigned int bindFlags, 2002 | unsigned int cpuAccessFlags, 2003 | unsigned int miscFlags, 2004 | DDS_LOADER_FLAGS loadFlags, 2005 | ID3D11Resource** texture, 2006 | ID3D11ShaderResourceView** textureView, 2007 | DDS_ALPHA_MODE* alphaMode) noexcept 2008 | { 2009 | if (texture) 2010 | { 2011 | *texture = nullptr; 2012 | } 2013 | if (textureView) 2014 | { 2015 | *textureView = nullptr; 2016 | } 2017 | if (alphaMode) 2018 | { 2019 | *alphaMode = DDS_ALPHA_MODE_UNKNOWN; 2020 | } 2021 | 2022 | if (!d3dDevice || !fileName || (!texture && !textureView)) 2023 | { 2024 | return E_INVALIDARG; 2025 | } 2026 | 2027 | if (textureView && !(bindFlags & D3D11_BIND_SHADER_RESOURCE)) 2028 | { 2029 | return E_INVALIDARG; 2030 | } 2031 | 2032 | const DDS_HEADER* header = nullptr; 2033 | const uint8_t* bitData = nullptr; 2034 | size_t bitSize = 0; 2035 | 2036 | std::unique_ptr ddsData; 2037 | HRESULT hr = LoadTextureDataFromFile(fileName, 2038 | ddsData, 2039 | &header, 2040 | &bitData, 2041 | &bitSize 2042 | ); 2043 | if (FAILED(hr)) 2044 | { 2045 | return hr; 2046 | } 2047 | 2048 | hr = CreateTextureFromDDS(d3dDevice, d3dContext, 2049 | header, bitData, bitSize, 2050 | maxsize, 2051 | usage, bindFlags, cpuAccessFlags, miscFlags, 2052 | loadFlags, 2053 | texture, textureView); 2054 | 2055 | if (SUCCEEDED(hr)) 2056 | { 2057 | SetDebugTextureInfo(fileName, texture, textureView); 2058 | 2059 | if (alphaMode) 2060 | *alphaMode = GetAlphaMode(header); 2061 | } 2062 | 2063 | return hr; 2064 | } -------------------------------------------------------------------------------- /DDSTextureLoader12.cpp: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------------------------------------------- 2 | // File: DDSTextureLoader12.cpp 3 | // 4 | // Functions for loading a DDS texture and creating a Direct3D runtime resource for it 5 | // 6 | // Note these functions are useful as a light-weight runtime loader for DDS files. For 7 | // a full-featured DDS file reader, writer, and texture processing pipeline see 8 | // the 'Texconv' sample and the 'DirectXTex' library. 9 | // 10 | // Copyright (c) Microsoft Corporation. 11 | // Licensed under the MIT License. 12 | // 13 | // http://go.microsoft.com/fwlink/?LinkId=248926 14 | // http://go.microsoft.com/fwlink/?LinkID=615561 15 | //-------------------------------------------------------------------------------------- 16 | 17 | #include "DDSTextureLoader12.h" 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #ifndef _WIN32 25 | #include 26 | #include 27 | #endif 28 | 29 | #ifdef _MSC_VER 30 | // Off by default warnings 31 | #pragma warning(disable : 4619 4616 4061 4062 4623 4626 5027) 32 | // C4619/4616 #pragma warning warnings 33 | // C4061 enumerator 'x' in switch of enum 'y' is not explicitly handled by a case label 34 | // C4062 enumerator 'x' in switch of enum 'y' is not handled 35 | // C4623 default constructor was implicitly defined as deleted 36 | // C4626 assignment operator was implicitly defined as deleted 37 | // C5027 move assignment operator was implicitly defined as deleted 38 | #endif 39 | 40 | #ifdef __clang__ 41 | #pragma clang diagnostic ignored "-Wtautological-type-limit-compare" 42 | #pragma clang diagnostic ignored "-Wcovered-switch-default" 43 | #pragma clang diagnostic ignored "-Wswitch" 44 | #pragma clang diagnostic ignored "-Wswitch-enum" 45 | #pragma clang diagnostic ignored "-Wunused-macros" 46 | #endif 47 | 48 | #define D3DX12_NO_STATE_OBJECT_HELPERS 49 | #define D3DX12_NO_CHECK_FEATURE_SUPPORT_CLASS 50 | #if !defined(_WIN32) || defined(USING_DIRECTX_HEADERS) 51 | #include "directx/d3dx12.h" 52 | #else 53 | #include "d3dx12.h" 54 | #endif 55 | 56 | using namespace DirectX; 57 | 58 | //-------------------------------------------------------------------------------------- 59 | // Macros 60 | //-------------------------------------------------------------------------------------- 61 | #ifndef MAKEFOURCC 62 | #define MAKEFOURCC(ch0, ch1, ch2, ch3) \ 63 | ((uint32_t)(uint8_t)(ch0) | ((uint32_t)(uint8_t)(ch1) << 8) | \ 64 | ((uint32_t)(uint8_t)(ch2) << 16) | ((uint32_t)(uint8_t)(ch3) << 24 )) 65 | #endif /* defined(MAKEFOURCC) */ 66 | 67 | // HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW) 68 | #define HRESULT_E_ARITHMETIC_OVERFLOW static_cast(0x80070216L) 69 | 70 | // HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED) 71 | #define HRESULT_E_NOT_SUPPORTED static_cast(0x80070032L) 72 | 73 | // HRESULT_FROM_WIN32(ERROR_HANDLE_EOF) 74 | #define HRESULT_E_HANDLE_EOF static_cast(0x80070026L) 75 | 76 | // HRESULT_FROM_WIN32(ERROR_INVALID_DATA) 77 | #define HRESULT_E_INVALID_DATA static_cast(0x8007000DL) 78 | 79 | //-------------------------------------------------------------------------------------- 80 | // DDS file structure definitions 81 | // 82 | // See DDS.h in the 'Texconv' sample and the 'DirectXTex' library 83 | //-------------------------------------------------------------------------------------- 84 | #pragma pack(push,1) 85 | 86 | constexpr uint32_t DDS_MAGIC = 0x20534444; // "DDS " 87 | 88 | struct DDS_PIXELFORMAT 89 | { 90 | uint32_t size; 91 | uint32_t flags; 92 | uint32_t fourCC; 93 | uint32_t RGBBitCount; 94 | uint32_t RBitMask; 95 | uint32_t GBitMask; 96 | uint32_t BBitMask; 97 | uint32_t ABitMask; 98 | }; 99 | 100 | #define DDS_FOURCC 0x00000004 // DDPF_FOURCC 101 | #define DDS_RGB 0x00000040 // DDPF_RGB 102 | #define DDS_LUMINANCE 0x00020000 // DDPF_LUMINANCE 103 | #define DDS_ALPHA 0x00000002 // DDPF_ALPHA 104 | #define DDS_BUMPDUDV 0x00080000 // DDPF_BUMPDUDV 105 | 106 | #define DDS_HEADER_FLAGS_VOLUME 0x00800000 // DDSD_DEPTH 107 | 108 | #define DDS_HEIGHT 0x00000002 // DDSD_HEIGHT 109 | 110 | #define DDS_CUBEMAP_POSITIVEX 0x00000600 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEX 111 | #define DDS_CUBEMAP_NEGATIVEX 0x00000a00 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEX 112 | #define DDS_CUBEMAP_POSITIVEY 0x00001200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEY 113 | #define DDS_CUBEMAP_NEGATIVEY 0x00002200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEY 114 | #define DDS_CUBEMAP_POSITIVEZ 0x00004200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEZ 115 | #define DDS_CUBEMAP_NEGATIVEZ 0x00008200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEZ 116 | 117 | #define DDS_CUBEMAP_ALLFACES ( DDS_CUBEMAP_POSITIVEX | DDS_CUBEMAP_NEGATIVEX |\ 118 | DDS_CUBEMAP_POSITIVEY | DDS_CUBEMAP_NEGATIVEY |\ 119 | DDS_CUBEMAP_POSITIVEZ | DDS_CUBEMAP_NEGATIVEZ ) 120 | 121 | #define DDS_CUBEMAP 0x00000200 // DDSCAPS2_CUBEMAP 122 | 123 | enum DDS_MISC_FLAGS2 124 | { 125 | DDS_MISC_FLAGS2_ALPHA_MODE_MASK = 0x7L, 126 | }; 127 | 128 | struct DDS_HEADER 129 | { 130 | uint32_t size; 131 | uint32_t flags; 132 | uint32_t height; 133 | uint32_t width; 134 | uint32_t pitchOrLinearSize; 135 | uint32_t depth; // only if DDS_HEADER_FLAGS_VOLUME is set in flags 136 | uint32_t mipMapCount; 137 | uint32_t reserved1[11]; 138 | DDS_PIXELFORMAT ddspf; 139 | uint32_t caps; 140 | uint32_t caps2; 141 | uint32_t caps3; 142 | uint32_t caps4; 143 | uint32_t reserved2; 144 | }; 145 | 146 | struct DDS_HEADER_DXT10 147 | { 148 | DXGI_FORMAT dxgiFormat; 149 | uint32_t resourceDimension; 150 | uint32_t miscFlag; // see D3D11_RESOURCE_MISC_FLAG 151 | uint32_t arraySize; 152 | uint32_t miscFlags2; 153 | }; 154 | 155 | #pragma pack(pop) 156 | 157 | //-------------------------------------------------------------------------------------- 158 | namespace 159 | { 160 | #ifdef _WIN32 161 | struct handle_closer { void operator()(HANDLE h) noexcept { if (h) CloseHandle(h); } }; 162 | 163 | using ScopedHandle = std::unique_ptr; 164 | 165 | inline HANDLE safe_handle(HANDLE h) noexcept { return (h == INVALID_HANDLE_VALUE) ? nullptr : h; } 166 | #endif 167 | 168 | #if !defined(NO_D3D12_DEBUG_NAME) && ( defined(_DEBUG) || defined(PROFILE) ) 169 | template 170 | inline void SetDebugObjectName(_In_ ID3D12DeviceChild* resource, _In_z_ const wchar_t(&name)[TNameLength]) noexcept 171 | { 172 | resource->SetName(name); 173 | } 174 | #else 175 | template 176 | inline void SetDebugObjectName(_In_ ID3D12DeviceChild*, _In_z_ const wchar_t(&)[TNameLength]) noexcept 177 | { 178 | } 179 | #endif 180 | 181 | inline uint32_t CountMips(uint32_t width, uint32_t height) noexcept 182 | { 183 | if (width == 0 || height == 0) 184 | return 0; 185 | 186 | uint32_t count = 1; 187 | while (width > 1 || height > 1) 188 | { 189 | width >>= 1; 190 | height >>= 1; 191 | count++; 192 | } 193 | return count; 194 | } 195 | 196 | 197 | //-------------------------------------------------------------------------------------- 198 | HRESULT LoadTextureDataFromMemory( 199 | _In_reads_(ddsDataSize) const uint8_t* ddsData, 200 | size_t ddsDataSize, 201 | const DDS_HEADER** header, 202 | const uint8_t** bitData, 203 | size_t* bitSize) noexcept 204 | { 205 | if (!header || !bitData || !bitSize) 206 | { 207 | return E_POINTER; 208 | } 209 | 210 | *bitSize = 0; 211 | 212 | if (ddsDataSize > UINT32_MAX) 213 | { 214 | return E_FAIL; 215 | } 216 | 217 | if (ddsDataSize < (sizeof(uint32_t) + sizeof(DDS_HEADER))) 218 | { 219 | return E_FAIL; 220 | } 221 | 222 | // DDS files always start with the same magic number ("DDS ") 223 | auto const dwMagicNumber = *reinterpret_cast(ddsData); 224 | if (dwMagicNumber != DDS_MAGIC) 225 | { 226 | return E_FAIL; 227 | } 228 | 229 | auto hdr = reinterpret_cast(ddsData + sizeof(uint32_t)); 230 | 231 | // Verify header to validate DDS file 232 | if (hdr->size != sizeof(DDS_HEADER) || 233 | hdr->ddspf.size != sizeof(DDS_PIXELFORMAT)) 234 | { 235 | return E_FAIL; 236 | } 237 | 238 | // Check for DX10 extension 239 | bool bDXT10Header = false; 240 | if ((hdr->ddspf.flags & DDS_FOURCC) && 241 | (MAKEFOURCC('D', 'X', '1', '0') == hdr->ddspf.fourCC)) 242 | { 243 | // Must be long enough for both headers and magic value 244 | if (ddsDataSize < (sizeof(uint32_t) + sizeof(DDS_HEADER) + sizeof(DDS_HEADER_DXT10))) 245 | { 246 | return E_FAIL; 247 | } 248 | 249 | bDXT10Header = true; 250 | } 251 | 252 | // setup the pointers in the process request 253 | *header = hdr; 254 | auto offset = sizeof(uint32_t) 255 | + sizeof(DDS_HEADER) 256 | + (bDXT10Header ? sizeof(DDS_HEADER_DXT10) : 0u); 257 | *bitData = ddsData + offset; 258 | *bitSize = ddsDataSize - offset; 259 | 260 | return S_OK; 261 | } 262 | 263 | 264 | //-------------------------------------------------------------------------------------- 265 | HRESULT LoadTextureDataFromFile( 266 | _In_z_ const wchar_t* fileName, 267 | std::unique_ptr& ddsData, 268 | const DDS_HEADER** header, 269 | const uint8_t** bitData, 270 | size_t* bitSize) noexcept 271 | { 272 | if (!header || !bitData || !bitSize) 273 | { 274 | return E_POINTER; 275 | } 276 | 277 | *bitSize = 0; 278 | 279 | #ifdef _WIN32 280 | // open the file 281 | ScopedHandle hFile(safe_handle(CreateFile2( 282 | fileName, 283 | GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, 284 | nullptr))); 285 | 286 | if (!hFile) 287 | { 288 | return HRESULT_FROM_WIN32(GetLastError()); 289 | } 290 | 291 | // Get the file size 292 | FILE_STANDARD_INFO fileInfo; 293 | if (!GetFileInformationByHandleEx(hFile.get(), FileStandardInfo, &fileInfo, sizeof(fileInfo))) 294 | { 295 | return HRESULT_FROM_WIN32(GetLastError()); 296 | } 297 | 298 | // File is too big for 32-bit allocation, so reject read 299 | if (fileInfo.EndOfFile.HighPart > 0) 300 | { 301 | return E_FAIL; 302 | } 303 | 304 | // Need at least enough data to fill the header and magic number to be a valid DDS 305 | if (fileInfo.EndOfFile.LowPart < (sizeof(uint32_t) + sizeof(DDS_HEADER))) 306 | { 307 | return E_FAIL; 308 | } 309 | 310 | // create enough space for the file data 311 | ddsData.reset(new (std::nothrow) uint8_t[fileInfo.EndOfFile.LowPart]); 312 | if (!ddsData) 313 | { 314 | return E_OUTOFMEMORY; 315 | } 316 | 317 | // read the data in 318 | DWORD bytesRead = 0; 319 | if (!ReadFile(hFile.get(), 320 | ddsData.get(), 321 | fileInfo.EndOfFile.LowPart, 322 | &bytesRead, 323 | nullptr 324 | )) 325 | { 326 | ddsData.reset(); 327 | return HRESULT_FROM_WIN32(GetLastError()); 328 | } 329 | 330 | if (bytesRead < fileInfo.EndOfFile.LowPart) 331 | { 332 | ddsData.reset(); 333 | return E_FAIL; 334 | } 335 | 336 | size_t len = fileInfo.EndOfFile.LowPart; 337 | 338 | #else // !WIN32 339 | std::ifstream inFile(std::filesystem::path(fileName), std::ios::in | std::ios::binary | std::ios::ate); 340 | if (!inFile) 341 | return E_FAIL; 342 | 343 | std::streampos fileLen = inFile.tellg(); 344 | if (!inFile) 345 | return E_FAIL; 346 | 347 | // Need at least enough data to fill the header and magic number to be a valid DDS 348 | if (fileLen < (sizeof(uint32_t) + sizeof(DDS_HEADER))) 349 | return E_FAIL; 350 | 351 | ddsData.reset(new (std::nothrow) uint8_t[size_t(fileLen)]); 352 | if (!ddsData) 353 | return E_OUTOFMEMORY; 354 | 355 | inFile.seekg(0, std::ios::beg); 356 | if (!inFile) 357 | { 358 | ddsData.reset(); 359 | return E_FAIL; 360 | } 361 | 362 | inFile.read(reinterpret_cast(ddsData.get()), fileLen); 363 | if (!inFile) 364 | { 365 | ddsData.reset(); 366 | return E_FAIL; 367 | } 368 | 369 | inFile.close(); 370 | 371 | size_t len = fileLen; 372 | #endif 373 | 374 | // DDS files always start with the same magic number ("DDS ") 375 | auto const dwMagicNumber = *reinterpret_cast(ddsData.get()); 376 | if (dwMagicNumber != DDS_MAGIC) 377 | { 378 | ddsData.reset(); 379 | return E_FAIL; 380 | } 381 | 382 | auto hdr = reinterpret_cast(ddsData.get() + sizeof(uint32_t)); 383 | 384 | // Verify header to validate DDS file 385 | if (hdr->size != sizeof(DDS_HEADER) || 386 | hdr->ddspf.size != sizeof(DDS_PIXELFORMAT)) 387 | { 388 | ddsData.reset(); 389 | return E_FAIL; 390 | } 391 | 392 | // Check for DX10 extension 393 | bool bDXT10Header = false; 394 | if ((hdr->ddspf.flags & DDS_FOURCC) && 395 | (MAKEFOURCC('D', 'X', '1', '0') == hdr->ddspf.fourCC)) 396 | { 397 | // Must be long enough for both headers and magic value 398 | if (len < (sizeof(uint32_t) + sizeof(DDS_HEADER) + sizeof(DDS_HEADER_DXT10))) 399 | { 400 | ddsData.reset(); 401 | return E_FAIL; 402 | } 403 | 404 | bDXT10Header = true; 405 | } 406 | 407 | // setup the pointers in the process request 408 | *header = hdr; 409 | auto offset = sizeof(uint32_t) + sizeof(DDS_HEADER) 410 | + (bDXT10Header ? sizeof(DDS_HEADER_DXT10) : 0u); 411 | *bitData = ddsData.get() + offset; 412 | *bitSize = len - offset; 413 | 414 | return S_OK; 415 | } 416 | 417 | 418 | //-------------------------------------------------------------------------------------- 419 | // Return the BPP for a particular format 420 | //-------------------------------------------------------------------------------------- 421 | size_t BitsPerPixel(_In_ DXGI_FORMAT fmt) noexcept 422 | { 423 | switch (fmt) 424 | { 425 | case DXGI_FORMAT_R32G32B32A32_TYPELESS: 426 | case DXGI_FORMAT_R32G32B32A32_FLOAT: 427 | case DXGI_FORMAT_R32G32B32A32_UINT: 428 | case DXGI_FORMAT_R32G32B32A32_SINT: 429 | return 128; 430 | 431 | case DXGI_FORMAT_R32G32B32_TYPELESS: 432 | case DXGI_FORMAT_R32G32B32_FLOAT: 433 | case DXGI_FORMAT_R32G32B32_UINT: 434 | case DXGI_FORMAT_R32G32B32_SINT: 435 | return 96; 436 | 437 | case DXGI_FORMAT_R16G16B16A16_TYPELESS: 438 | case DXGI_FORMAT_R16G16B16A16_FLOAT: 439 | case DXGI_FORMAT_R16G16B16A16_UNORM: 440 | case DXGI_FORMAT_R16G16B16A16_UINT: 441 | case DXGI_FORMAT_R16G16B16A16_SNORM: 442 | case DXGI_FORMAT_R16G16B16A16_SINT: 443 | case DXGI_FORMAT_R32G32_TYPELESS: 444 | case DXGI_FORMAT_R32G32_FLOAT: 445 | case DXGI_FORMAT_R32G32_UINT: 446 | case DXGI_FORMAT_R32G32_SINT: 447 | case DXGI_FORMAT_R32G8X24_TYPELESS: 448 | case DXGI_FORMAT_D32_FLOAT_S8X24_UINT: 449 | case DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS: 450 | case DXGI_FORMAT_X32_TYPELESS_G8X24_UINT: 451 | case DXGI_FORMAT_Y416: 452 | case DXGI_FORMAT_Y210: 453 | case DXGI_FORMAT_Y216: 454 | return 64; 455 | 456 | case DXGI_FORMAT_R10G10B10A2_TYPELESS: 457 | case DXGI_FORMAT_R10G10B10A2_UNORM: 458 | case DXGI_FORMAT_R10G10B10A2_UINT: 459 | case DXGI_FORMAT_R11G11B10_FLOAT: 460 | case DXGI_FORMAT_R8G8B8A8_TYPELESS: 461 | case DXGI_FORMAT_R8G8B8A8_UNORM: 462 | case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: 463 | case DXGI_FORMAT_R8G8B8A8_UINT: 464 | case DXGI_FORMAT_R8G8B8A8_SNORM: 465 | case DXGI_FORMAT_R8G8B8A8_SINT: 466 | case DXGI_FORMAT_R16G16_TYPELESS: 467 | case DXGI_FORMAT_R16G16_FLOAT: 468 | case DXGI_FORMAT_R16G16_UNORM: 469 | case DXGI_FORMAT_R16G16_UINT: 470 | case DXGI_FORMAT_R16G16_SNORM: 471 | case DXGI_FORMAT_R16G16_SINT: 472 | case DXGI_FORMAT_R32_TYPELESS: 473 | case DXGI_FORMAT_D32_FLOAT: 474 | case DXGI_FORMAT_R32_FLOAT: 475 | case DXGI_FORMAT_R32_UINT: 476 | case DXGI_FORMAT_R32_SINT: 477 | case DXGI_FORMAT_R24G8_TYPELESS: 478 | case DXGI_FORMAT_D24_UNORM_S8_UINT: 479 | case DXGI_FORMAT_R24_UNORM_X8_TYPELESS: 480 | case DXGI_FORMAT_X24_TYPELESS_G8_UINT: 481 | case DXGI_FORMAT_R9G9B9E5_SHAREDEXP: 482 | case DXGI_FORMAT_R8G8_B8G8_UNORM: 483 | case DXGI_FORMAT_G8R8_G8B8_UNORM: 484 | case DXGI_FORMAT_B8G8R8A8_UNORM: 485 | case DXGI_FORMAT_B8G8R8X8_UNORM: 486 | case DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM: 487 | case DXGI_FORMAT_B8G8R8A8_TYPELESS: 488 | case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: 489 | case DXGI_FORMAT_B8G8R8X8_TYPELESS: 490 | case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: 491 | case DXGI_FORMAT_AYUV: 492 | case DXGI_FORMAT_Y410: 493 | case DXGI_FORMAT_YUY2: 494 | return 32; 495 | 496 | case DXGI_FORMAT_P010: 497 | case DXGI_FORMAT_P016: 498 | case DXGI_FORMAT_V408: 499 | return 24; 500 | 501 | case DXGI_FORMAT_R8G8_TYPELESS: 502 | case DXGI_FORMAT_R8G8_UNORM: 503 | case DXGI_FORMAT_R8G8_UINT: 504 | case DXGI_FORMAT_R8G8_SNORM: 505 | case DXGI_FORMAT_R8G8_SINT: 506 | case DXGI_FORMAT_R16_TYPELESS: 507 | case DXGI_FORMAT_R16_FLOAT: 508 | case DXGI_FORMAT_D16_UNORM: 509 | case DXGI_FORMAT_R16_UNORM: 510 | case DXGI_FORMAT_R16_UINT: 511 | case DXGI_FORMAT_R16_SNORM: 512 | case DXGI_FORMAT_R16_SINT: 513 | case DXGI_FORMAT_B5G6R5_UNORM: 514 | case DXGI_FORMAT_B5G5R5A1_UNORM: 515 | case DXGI_FORMAT_A8P8: 516 | case DXGI_FORMAT_B4G4R4A4_UNORM: 517 | case DXGI_FORMAT_P208: 518 | case DXGI_FORMAT_V208: 519 | return 16; 520 | 521 | case DXGI_FORMAT_NV12: 522 | case DXGI_FORMAT_420_OPAQUE: 523 | case DXGI_FORMAT_NV11: 524 | return 12; 525 | 526 | case DXGI_FORMAT_R8_TYPELESS: 527 | case DXGI_FORMAT_R8_UNORM: 528 | case DXGI_FORMAT_R8_UINT: 529 | case DXGI_FORMAT_R8_SNORM: 530 | case DXGI_FORMAT_R8_SINT: 531 | case DXGI_FORMAT_A8_UNORM: 532 | case DXGI_FORMAT_BC2_TYPELESS: 533 | case DXGI_FORMAT_BC2_UNORM: 534 | case DXGI_FORMAT_BC2_UNORM_SRGB: 535 | case DXGI_FORMAT_BC3_TYPELESS: 536 | case DXGI_FORMAT_BC3_UNORM: 537 | case DXGI_FORMAT_BC3_UNORM_SRGB: 538 | case DXGI_FORMAT_BC5_TYPELESS: 539 | case DXGI_FORMAT_BC5_UNORM: 540 | case DXGI_FORMAT_BC5_SNORM: 541 | case DXGI_FORMAT_BC6H_TYPELESS: 542 | case DXGI_FORMAT_BC6H_UF16: 543 | case DXGI_FORMAT_BC6H_SF16: 544 | case DXGI_FORMAT_BC7_TYPELESS: 545 | case DXGI_FORMAT_BC7_UNORM: 546 | case DXGI_FORMAT_BC7_UNORM_SRGB: 547 | case DXGI_FORMAT_AI44: 548 | case DXGI_FORMAT_IA44: 549 | case DXGI_FORMAT_P8: 550 | return 8; 551 | 552 | case DXGI_FORMAT_R1_UNORM: 553 | return 1; 554 | 555 | case DXGI_FORMAT_BC1_TYPELESS: 556 | case DXGI_FORMAT_BC1_UNORM: 557 | case DXGI_FORMAT_BC1_UNORM_SRGB: 558 | case DXGI_FORMAT_BC4_TYPELESS: 559 | case DXGI_FORMAT_BC4_UNORM: 560 | case DXGI_FORMAT_BC4_SNORM: 561 | return 4; 562 | 563 | default: 564 | return 0; 565 | } 566 | } 567 | 568 | 569 | //-------------------------------------------------------------------------------------- 570 | // Get surface information for a particular format 571 | //-------------------------------------------------------------------------------------- 572 | HRESULT GetSurfaceInfo( 573 | _In_ size_t width, 574 | _In_ size_t height, 575 | _In_ DXGI_FORMAT fmt, 576 | size_t* outNumBytes, 577 | _Out_opt_ size_t* outRowBytes, 578 | _Out_opt_ size_t* outNumRows) noexcept 579 | { 580 | uint64_t numBytes = 0; 581 | uint64_t rowBytes = 0; 582 | uint64_t numRows = 0; 583 | 584 | bool bc = false; 585 | bool packed = false; 586 | bool planar = false; 587 | size_t bpe = 0; 588 | switch (fmt) 589 | { 590 | case DXGI_FORMAT_BC1_TYPELESS: 591 | case DXGI_FORMAT_BC1_UNORM: 592 | case DXGI_FORMAT_BC1_UNORM_SRGB: 593 | case DXGI_FORMAT_BC4_TYPELESS: 594 | case DXGI_FORMAT_BC4_UNORM: 595 | case DXGI_FORMAT_BC4_SNORM: 596 | bc = true; 597 | bpe = 8; 598 | break; 599 | 600 | case DXGI_FORMAT_BC2_TYPELESS: 601 | case DXGI_FORMAT_BC2_UNORM: 602 | case DXGI_FORMAT_BC2_UNORM_SRGB: 603 | case DXGI_FORMAT_BC3_TYPELESS: 604 | case DXGI_FORMAT_BC3_UNORM: 605 | case DXGI_FORMAT_BC3_UNORM_SRGB: 606 | case DXGI_FORMAT_BC5_TYPELESS: 607 | case DXGI_FORMAT_BC5_UNORM: 608 | case DXGI_FORMAT_BC5_SNORM: 609 | case DXGI_FORMAT_BC6H_TYPELESS: 610 | case DXGI_FORMAT_BC6H_UF16: 611 | case DXGI_FORMAT_BC6H_SF16: 612 | case DXGI_FORMAT_BC7_TYPELESS: 613 | case DXGI_FORMAT_BC7_UNORM: 614 | case DXGI_FORMAT_BC7_UNORM_SRGB: 615 | bc = true; 616 | bpe = 16; 617 | break; 618 | 619 | case DXGI_FORMAT_R8G8_B8G8_UNORM: 620 | case DXGI_FORMAT_G8R8_G8B8_UNORM: 621 | case DXGI_FORMAT_YUY2: 622 | packed = true; 623 | bpe = 4; 624 | break; 625 | 626 | case DXGI_FORMAT_Y210: 627 | case DXGI_FORMAT_Y216: 628 | packed = true; 629 | bpe = 8; 630 | break; 631 | 632 | case DXGI_FORMAT_NV12: 633 | case DXGI_FORMAT_420_OPAQUE: 634 | if ((height % 2) != 0) 635 | { 636 | // Requires a height alignment of 2. 637 | return E_INVALIDARG; 638 | } 639 | planar = true; 640 | bpe = 2; 641 | break; 642 | 643 | case DXGI_FORMAT_P208: 644 | planar = true; 645 | bpe = 2; 646 | break; 647 | 648 | case DXGI_FORMAT_P010: 649 | case DXGI_FORMAT_P016: 650 | if ((height % 2) != 0) 651 | { 652 | // Requires a height alignment of 2. 653 | return E_INVALIDARG; 654 | } 655 | planar = true; 656 | bpe = 4; 657 | break; 658 | 659 | default: 660 | break; 661 | } 662 | 663 | if (bc) 664 | { 665 | uint64_t numBlocksWide = 0; 666 | if (width > 0) 667 | { 668 | numBlocksWide = std::max(1u, (uint64_t(width) + 3u) / 4u); 669 | } 670 | uint64_t numBlocksHigh = 0; 671 | if (height > 0) 672 | { 673 | numBlocksHigh = std::max(1u, (uint64_t(height) + 3u) / 4u); 674 | } 675 | rowBytes = numBlocksWide * bpe; 676 | numRows = numBlocksHigh; 677 | numBytes = rowBytes * numBlocksHigh; 678 | } 679 | else if (packed) 680 | { 681 | rowBytes = ((uint64_t(width) + 1u) >> 1) * bpe; 682 | numRows = uint64_t(height); 683 | numBytes = rowBytes * height; 684 | } 685 | else if (fmt == DXGI_FORMAT_NV11) 686 | { 687 | rowBytes = ((uint64_t(width) + 3u) >> 2) * 4u; 688 | numRows = uint64_t(height) * 2u; // Direct3D makes this simplifying assumption, although it is larger than the 4:1:1 data 689 | numBytes = rowBytes * numRows; 690 | } 691 | else if (planar) 692 | { 693 | rowBytes = ((uint64_t(width) + 1u) >> 1) * bpe; 694 | numBytes = (rowBytes * uint64_t(height)) + ((rowBytes * uint64_t(height) + 1u) >> 1); 695 | numRows = height + ((uint64_t(height) + 1u) >> 1); 696 | } 697 | else 698 | { 699 | const size_t bpp = BitsPerPixel(fmt); 700 | if (!bpp) 701 | return E_INVALIDARG; 702 | 703 | rowBytes = (uint64_t(width) * bpp + 7u) / 8u; // round up to nearest byte 704 | numRows = uint64_t(height); 705 | numBytes = rowBytes * height; 706 | } 707 | 708 | #if defined(_M_IX86) || defined(_M_ARM) || defined(_M_HYBRID_X86_ARM64) 709 | static_assert(sizeof(size_t) == 4, "Not a 32-bit platform!"); 710 | if (numBytes > UINT32_MAX || rowBytes > UINT32_MAX || numRows > UINT32_MAX) 711 | return HRESULT_E_ARITHMETIC_OVERFLOW; 712 | #else 713 | static_assert(sizeof(size_t) == 8, "Not a 64-bit platform!"); 714 | #endif 715 | 716 | if (outNumBytes) 717 | { 718 | *outNumBytes = static_cast(numBytes); 719 | } 720 | if (outRowBytes) 721 | { 722 | *outRowBytes = static_cast(rowBytes); 723 | } 724 | if (outNumRows) 725 | { 726 | *outNumRows = static_cast(numRows); 727 | } 728 | 729 | return S_OK; 730 | } 731 | 732 | 733 | //-------------------------------------------------------------------------------------- 734 | #define ISBITMASK( r,g,b,a ) ( ddpf.RBitMask == r && ddpf.GBitMask == g && ddpf.BBitMask == b && ddpf.ABitMask == a ) 735 | 736 | DXGI_FORMAT GetDXGIFormat(const DDS_PIXELFORMAT& ddpf) noexcept 737 | { 738 | if (ddpf.flags & DDS_RGB) 739 | { 740 | // Note that sRGB formats are written using the "DX10" extended header 741 | 742 | switch (ddpf.RGBBitCount) 743 | { 744 | case 32: 745 | if (ISBITMASK(0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000)) 746 | { 747 | return DXGI_FORMAT_R8G8B8A8_UNORM; 748 | } 749 | 750 | if (ISBITMASK(0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000)) 751 | { 752 | return DXGI_FORMAT_B8G8R8A8_UNORM; 753 | } 754 | 755 | if (ISBITMASK(0x00ff0000, 0x0000ff00, 0x000000ff, 0)) 756 | { 757 | return DXGI_FORMAT_B8G8R8X8_UNORM; 758 | } 759 | 760 | // No DXGI format maps to ISBITMASK(0x000000ff,0x0000ff00,0x00ff0000,0) aka D3DFMT_X8B8G8R8 761 | 762 | // Note that many common DDS reader/writers (including D3DX) swap the 763 | // the RED/BLUE masks for 10:10:10:2 formats. We assume 764 | // below that the 'backwards' header mask is being used since it is most 765 | // likely written by D3DX. The more robust solution is to use the 'DX10' 766 | // header extension and specify the DXGI_FORMAT_R10G10B10A2_UNORM format directly 767 | 768 | // For 'correct' writers, this should be 0x000003ff,0x000ffc00,0x3ff00000 for RGB data 769 | if (ISBITMASK(0x3ff00000, 0x000ffc00, 0x000003ff, 0xc0000000)) 770 | { 771 | return DXGI_FORMAT_R10G10B10A2_UNORM; 772 | } 773 | 774 | // No DXGI format maps to ISBITMASK(0x000003ff,0x000ffc00,0x3ff00000,0xc0000000) aka D3DFMT_A2R10G10B10 775 | 776 | if (ISBITMASK(0x0000ffff, 0xffff0000, 0, 0)) 777 | { 778 | return DXGI_FORMAT_R16G16_UNORM; 779 | } 780 | 781 | if (ISBITMASK(0xffffffff, 0, 0, 0)) 782 | { 783 | // Only 32-bit color channel format in D3D9 was R32F 784 | return DXGI_FORMAT_R32_FLOAT; // D3DX writes this out as a FourCC of 114 785 | } 786 | break; 787 | 788 | case 24: 789 | // No 24bpp DXGI formats aka D3DFMT_R8G8B8 790 | break; 791 | 792 | case 16: 793 | if (ISBITMASK(0x7c00, 0x03e0, 0x001f, 0x8000)) 794 | { 795 | return DXGI_FORMAT_B5G5R5A1_UNORM; 796 | } 797 | if (ISBITMASK(0xf800, 0x07e0, 0x001f, 0)) 798 | { 799 | return DXGI_FORMAT_B5G6R5_UNORM; 800 | } 801 | 802 | // No DXGI format maps to ISBITMASK(0x7c00,0x03e0,0x001f,0) aka D3DFMT_X1R5G5B5 803 | 804 | if (ISBITMASK(0x0f00, 0x00f0, 0x000f, 0xf000)) 805 | { 806 | return DXGI_FORMAT_B4G4R4A4_UNORM; 807 | } 808 | 809 | // NVTT versions 1.x wrote this as RGB instead of LUMINANCE 810 | if (ISBITMASK(0x00ff, 0, 0, 0xff00)) 811 | { 812 | return DXGI_FORMAT_R8G8_UNORM; 813 | } 814 | if (ISBITMASK(0xffff, 0, 0, 0)) 815 | { 816 | return DXGI_FORMAT_R16_UNORM; 817 | } 818 | 819 | // No DXGI format maps to ISBITMASK(0x0f00,0x00f0,0x000f,0) aka D3DFMT_X4R4G4B4 820 | 821 | // No 3:3:2:8 or paletted DXGI formats aka D3DFMT_A8R3G3B2, D3DFMT_A8P8, etc. 822 | break; 823 | 824 | case 8: 825 | // NVTT versions 1.x wrote this as RGB instead of LUMINANCE 826 | if (ISBITMASK(0xff, 0, 0, 0)) 827 | { 828 | return DXGI_FORMAT_R8_UNORM; 829 | } 830 | 831 | // No 3:3:2 or paletted DXGI formats aka D3DFMT_R3G3B2, D3DFMT_P8 832 | break; 833 | 834 | default: 835 | return DXGI_FORMAT_UNKNOWN; 836 | } 837 | } 838 | else if (ddpf.flags & DDS_LUMINANCE) 839 | { 840 | switch (ddpf.RGBBitCount) 841 | { 842 | case 16: 843 | if (ISBITMASK(0xffff, 0, 0, 0)) 844 | { 845 | return DXGI_FORMAT_R16_UNORM; // D3DX10/11 writes this out as DX10 extension 846 | } 847 | if (ISBITMASK(0x00ff, 0, 0, 0xff00)) 848 | { 849 | return DXGI_FORMAT_R8G8_UNORM; // D3DX10/11 writes this out as DX10 extension 850 | } 851 | break; 852 | 853 | case 8: 854 | if (ISBITMASK(0xff, 0, 0, 0)) 855 | { 856 | return DXGI_FORMAT_R8_UNORM; // D3DX10/11 writes this out as DX10 extension 857 | } 858 | 859 | // No DXGI format maps to ISBITMASK(0x0f,0,0,0xf0) aka D3DFMT_A4L4 860 | 861 | if (ISBITMASK(0x00ff, 0, 0, 0xff00)) 862 | { 863 | return DXGI_FORMAT_R8G8_UNORM; // Some DDS writers assume the bitcount should be 8 instead of 16 864 | } 865 | break; 866 | 867 | default: 868 | return DXGI_FORMAT_UNKNOWN; 869 | } 870 | } 871 | else if (ddpf.flags & DDS_ALPHA) 872 | { 873 | if (8 == ddpf.RGBBitCount) 874 | { 875 | return DXGI_FORMAT_A8_UNORM; 876 | } 877 | } 878 | else if (ddpf.flags & DDS_BUMPDUDV) 879 | { 880 | switch (ddpf.RGBBitCount) 881 | { 882 | case 32: 883 | if (ISBITMASK(0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000)) 884 | { 885 | return DXGI_FORMAT_R8G8B8A8_SNORM; // D3DX10/11 writes this out as DX10 extension 886 | } 887 | if (ISBITMASK(0x0000ffff, 0xffff0000, 0, 0)) 888 | { 889 | return DXGI_FORMAT_R16G16_SNORM; // D3DX10/11 writes this out as DX10 extension 890 | } 891 | 892 | // No DXGI format maps to ISBITMASK(0x3ff00000, 0x000ffc00, 0x000003ff, 0xc0000000) aka D3DFMT_A2W10V10U10 893 | break; 894 | 895 | case 16: 896 | if (ISBITMASK(0x00ff, 0xff00, 0, 0)) 897 | { 898 | return DXGI_FORMAT_R8G8_SNORM; // D3DX10/11 writes this out as DX10 extension 899 | } 900 | break; 901 | 902 | default: 903 | return DXGI_FORMAT_UNKNOWN; 904 | } 905 | 906 | // No DXGI format maps to DDPF_BUMPLUMINANCE aka D3DFMT_L6V5U5, D3DFMT_X8L8V8U8 907 | } 908 | else if (ddpf.flags & DDS_FOURCC) 909 | { 910 | if (MAKEFOURCC('D', 'X', 'T', '1') == ddpf.fourCC) 911 | { 912 | return DXGI_FORMAT_BC1_UNORM; 913 | } 914 | if (MAKEFOURCC('D', 'X', 'T', '3') == ddpf.fourCC) 915 | { 916 | return DXGI_FORMAT_BC2_UNORM; 917 | } 918 | if (MAKEFOURCC('D', 'X', 'T', '5') == ddpf.fourCC) 919 | { 920 | return DXGI_FORMAT_BC3_UNORM; 921 | } 922 | 923 | // While pre-multiplied alpha isn't directly supported by the DXGI formats, 924 | // they are basically the same as these BC formats so they can be mapped 925 | if (MAKEFOURCC('D', 'X', 'T', '2') == ddpf.fourCC) 926 | { 927 | return DXGI_FORMAT_BC2_UNORM; 928 | } 929 | if (MAKEFOURCC('D', 'X', 'T', '4') == ddpf.fourCC) 930 | { 931 | return DXGI_FORMAT_BC3_UNORM; 932 | } 933 | 934 | if (MAKEFOURCC('A', 'T', 'I', '1') == ddpf.fourCC) 935 | { 936 | return DXGI_FORMAT_BC4_UNORM; 937 | } 938 | if (MAKEFOURCC('B', 'C', '4', 'U') == ddpf.fourCC) 939 | { 940 | return DXGI_FORMAT_BC4_UNORM; 941 | } 942 | if (MAKEFOURCC('B', 'C', '4', 'S') == ddpf.fourCC) 943 | { 944 | return DXGI_FORMAT_BC4_SNORM; 945 | } 946 | 947 | if (MAKEFOURCC('A', 'T', 'I', '2') == ddpf.fourCC) 948 | { 949 | return DXGI_FORMAT_BC5_UNORM; 950 | } 951 | if (MAKEFOURCC('B', 'C', '5', 'U') == ddpf.fourCC) 952 | { 953 | return DXGI_FORMAT_BC5_UNORM; 954 | } 955 | if (MAKEFOURCC('B', 'C', '5', 'S') == ddpf.fourCC) 956 | { 957 | return DXGI_FORMAT_BC5_SNORM; 958 | } 959 | 960 | // BC6H and BC7 are written using the "DX10" extended header 961 | 962 | if (MAKEFOURCC('R', 'G', 'B', 'G') == ddpf.fourCC) 963 | { 964 | return DXGI_FORMAT_R8G8_B8G8_UNORM; 965 | } 966 | if (MAKEFOURCC('G', 'R', 'G', 'B') == ddpf.fourCC) 967 | { 968 | return DXGI_FORMAT_G8R8_G8B8_UNORM; 969 | } 970 | 971 | if (MAKEFOURCC('Y', 'U', 'Y', '2') == ddpf.fourCC) 972 | { 973 | return DXGI_FORMAT_YUY2; 974 | } 975 | 976 | // Check for D3DFORMAT enums being set here 977 | switch (ddpf.fourCC) 978 | { 979 | case 36: // D3DFMT_A16B16G16R16 980 | return DXGI_FORMAT_R16G16B16A16_UNORM; 981 | 982 | case 110: // D3DFMT_Q16W16V16U16 983 | return DXGI_FORMAT_R16G16B16A16_SNORM; 984 | 985 | case 111: // D3DFMT_R16F 986 | return DXGI_FORMAT_R16_FLOAT; 987 | 988 | case 112: // D3DFMT_G16R16F 989 | return DXGI_FORMAT_R16G16_FLOAT; 990 | 991 | case 113: // D3DFMT_A16B16G16R16F 992 | return DXGI_FORMAT_R16G16B16A16_FLOAT; 993 | 994 | case 114: // D3DFMT_R32F 995 | return DXGI_FORMAT_R32_FLOAT; 996 | 997 | case 115: // D3DFMT_G32R32F 998 | return DXGI_FORMAT_R32G32_FLOAT; 999 | 1000 | case 116: // D3DFMT_A32B32G32R32F 1001 | return DXGI_FORMAT_R32G32B32A32_FLOAT; 1002 | 1003 | // No DXGI format maps to D3DFMT_CxV8U8 1004 | 1005 | default: 1006 | return DXGI_FORMAT_UNKNOWN; 1007 | } 1008 | } 1009 | 1010 | return DXGI_FORMAT_UNKNOWN; 1011 | } 1012 | 1013 | #undef ISBITMASK 1014 | 1015 | 1016 | //-------------------------------------------------------------------------------------- 1017 | DXGI_FORMAT MakeSRGB(_In_ DXGI_FORMAT format) noexcept 1018 | { 1019 | switch (format) 1020 | { 1021 | case DXGI_FORMAT_R8G8B8A8_UNORM: 1022 | return DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; 1023 | 1024 | case DXGI_FORMAT_BC1_UNORM: 1025 | return DXGI_FORMAT_BC1_UNORM_SRGB; 1026 | 1027 | case DXGI_FORMAT_BC2_UNORM: 1028 | return DXGI_FORMAT_BC2_UNORM_SRGB; 1029 | 1030 | case DXGI_FORMAT_BC3_UNORM: 1031 | return DXGI_FORMAT_BC3_UNORM_SRGB; 1032 | 1033 | case DXGI_FORMAT_B8G8R8A8_UNORM: 1034 | return DXGI_FORMAT_B8G8R8A8_UNORM_SRGB; 1035 | 1036 | case DXGI_FORMAT_B8G8R8X8_UNORM: 1037 | return DXGI_FORMAT_B8G8R8X8_UNORM_SRGB; 1038 | 1039 | case DXGI_FORMAT_BC7_UNORM: 1040 | return DXGI_FORMAT_BC7_UNORM_SRGB; 1041 | 1042 | default: 1043 | return format; 1044 | } 1045 | } 1046 | 1047 | 1048 | //-------------------------------------------------------------------------------------- 1049 | inline DXGI_FORMAT MakeLinear(_In_ DXGI_FORMAT format) noexcept 1050 | { 1051 | switch (format) 1052 | { 1053 | case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: 1054 | return DXGI_FORMAT_R8G8B8A8_UNORM; 1055 | 1056 | case DXGI_FORMAT_BC1_UNORM_SRGB: 1057 | return DXGI_FORMAT_BC1_UNORM; 1058 | 1059 | case DXGI_FORMAT_BC2_UNORM_SRGB: 1060 | return DXGI_FORMAT_BC2_UNORM; 1061 | 1062 | case DXGI_FORMAT_BC3_UNORM_SRGB: 1063 | return DXGI_FORMAT_BC3_UNORM; 1064 | 1065 | case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: 1066 | return DXGI_FORMAT_B8G8R8A8_UNORM; 1067 | 1068 | case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: 1069 | return DXGI_FORMAT_B8G8R8X8_UNORM; 1070 | 1071 | case DXGI_FORMAT_BC7_UNORM_SRGB: 1072 | return DXGI_FORMAT_BC7_UNORM; 1073 | 1074 | default: 1075 | return format; 1076 | } 1077 | } 1078 | 1079 | 1080 | //-------------------------------------------------------------------------------------- 1081 | inline bool IsDepthStencil(DXGI_FORMAT fmt) noexcept 1082 | { 1083 | switch (fmt) 1084 | { 1085 | case DXGI_FORMAT_R32G8X24_TYPELESS: 1086 | case DXGI_FORMAT_D32_FLOAT_S8X24_UINT: 1087 | case DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS: 1088 | case DXGI_FORMAT_X32_TYPELESS_G8X24_UINT: 1089 | case DXGI_FORMAT_D32_FLOAT: 1090 | case DXGI_FORMAT_R24G8_TYPELESS: 1091 | case DXGI_FORMAT_D24_UNORM_S8_UINT: 1092 | case DXGI_FORMAT_R24_UNORM_X8_TYPELESS: 1093 | case DXGI_FORMAT_X24_TYPELESS_G8_UINT: 1094 | case DXGI_FORMAT_D16_UNORM: 1095 | return true; 1096 | 1097 | default: 1098 | return false; 1099 | } 1100 | } 1101 | 1102 | 1103 | //-------------------------------------------------------------------------------------- 1104 | inline void AdjustPlaneResource( 1105 | _In_ DXGI_FORMAT fmt, 1106 | _In_ size_t height, 1107 | _In_ size_t slicePlane, 1108 | _Inout_ D3D12_SUBRESOURCE_DATA& res) noexcept 1109 | { 1110 | switch (fmt) 1111 | { 1112 | case DXGI_FORMAT_NV12: 1113 | case DXGI_FORMAT_P010: 1114 | case DXGI_FORMAT_P016: 1115 | if (!slicePlane) 1116 | { 1117 | // Plane 0 1118 | res.SlicePitch = res.RowPitch * static_cast(height); 1119 | } 1120 | else 1121 | { 1122 | // Plane 1 1123 | res.pData = reinterpret_cast(res.pData) + uintptr_t(res.RowPitch) * height; 1124 | res.SlicePitch = res.RowPitch * ((static_cast(height) + 1) >> 1); 1125 | } 1126 | break; 1127 | 1128 | case DXGI_FORMAT_NV11: 1129 | if (!slicePlane) 1130 | { 1131 | // Plane 0 1132 | res.SlicePitch = res.RowPitch * static_cast(height); 1133 | } 1134 | else 1135 | { 1136 | // Plane 1 1137 | res.pData = reinterpret_cast(res.pData) + uintptr_t(res.RowPitch) * height; 1138 | res.RowPitch = (res.RowPitch >> 1); 1139 | res.SlicePitch = res.RowPitch * static_cast(height); 1140 | } 1141 | break; 1142 | 1143 | default: 1144 | break; 1145 | } 1146 | } 1147 | 1148 | 1149 | //-------------------------------------------------------------------------------------- 1150 | HRESULT FillInitData(_In_ size_t width, 1151 | _In_ size_t height, 1152 | _In_ size_t depth, 1153 | _In_ size_t mipCount, 1154 | _In_ size_t arraySize, 1155 | _In_ size_t numberOfPlanes, 1156 | _In_ DXGI_FORMAT format, 1157 | _In_ size_t maxsize, 1158 | _In_ size_t bitSize, 1159 | _In_reads_bytes_(bitSize) const uint8_t* bitData, 1160 | _Out_ size_t& twidth, 1161 | _Out_ size_t& theight, 1162 | _Out_ size_t& tdepth, 1163 | _Out_ size_t& skipMip, 1164 | std::vector& initData) 1165 | { 1166 | if (!bitData) 1167 | { 1168 | return E_POINTER; 1169 | } 1170 | 1171 | skipMip = 0; 1172 | twidth = 0; 1173 | theight = 0; 1174 | tdepth = 0; 1175 | 1176 | size_t NumBytes = 0; 1177 | size_t RowBytes = 0; 1178 | const uint8_t* pEndBits = bitData + bitSize; 1179 | 1180 | initData.clear(); 1181 | 1182 | for (size_t p = 0; p < numberOfPlanes; ++p) 1183 | { 1184 | const uint8_t* pSrcBits = bitData; 1185 | 1186 | for (size_t j = 0; j < arraySize; j++) 1187 | { 1188 | size_t w = width; 1189 | size_t h = height; 1190 | size_t d = depth; 1191 | for (size_t i = 0; i < mipCount; i++) 1192 | { 1193 | HRESULT hr = GetSurfaceInfo(w, h, format, &NumBytes, &RowBytes, nullptr); 1194 | if (FAILED(hr)) 1195 | return hr; 1196 | 1197 | if (NumBytes > UINT32_MAX || RowBytes > UINT32_MAX) 1198 | return HRESULT_E_ARITHMETIC_OVERFLOW; 1199 | 1200 | if ((mipCount <= 1) || !maxsize || (w <= maxsize && h <= maxsize && d <= maxsize)) 1201 | { 1202 | if (!twidth) 1203 | { 1204 | twidth = w; 1205 | theight = h; 1206 | tdepth = d; 1207 | } 1208 | 1209 | D3D12_SUBRESOURCE_DATA res = 1210 | { 1211 | pSrcBits, 1212 | static_cast(RowBytes), 1213 | static_cast(NumBytes) 1214 | }; 1215 | 1216 | AdjustPlaneResource(format, h, p, res); 1217 | 1218 | initData.emplace_back(res); 1219 | } 1220 | else if (!j) 1221 | { 1222 | // Count number of skipped mipmaps (first item only) 1223 | ++skipMip; 1224 | } 1225 | 1226 | if (pSrcBits + (NumBytes*d) > pEndBits) 1227 | { 1228 | return HRESULT_E_HANDLE_EOF; 1229 | } 1230 | 1231 | pSrcBits += NumBytes * d; 1232 | 1233 | w = w >> 1; 1234 | h = h >> 1; 1235 | d = d >> 1; 1236 | if (w == 0) 1237 | { 1238 | w = 1; 1239 | } 1240 | if (h == 0) 1241 | { 1242 | h = 1; 1243 | } 1244 | if (d == 0) 1245 | { 1246 | d = 1; 1247 | } 1248 | } 1249 | } 1250 | } 1251 | 1252 | return initData.empty() ? E_FAIL : S_OK; 1253 | } 1254 | 1255 | 1256 | //-------------------------------------------------------------------------------------- 1257 | HRESULT CreateTextureResource( 1258 | _In_ ID3D12Device* d3dDevice, 1259 | D3D12_RESOURCE_DIMENSION resDim, 1260 | size_t width, 1261 | size_t height, 1262 | size_t depth, 1263 | size_t mipCount, 1264 | size_t arraySize, 1265 | DXGI_FORMAT format, 1266 | D3D12_RESOURCE_FLAGS resFlags, 1267 | DDS_LOADER_FLAGS loadFlags, 1268 | _Outptr_ ID3D12Resource** texture) noexcept 1269 | { 1270 | if (!d3dDevice) 1271 | return E_POINTER; 1272 | 1273 | HRESULT hr = E_FAIL; 1274 | 1275 | if (loadFlags & DDS_LOADER_FORCE_SRGB) 1276 | { 1277 | format = MakeSRGB(format); 1278 | } 1279 | else if (loadFlags & DDS_LOADER_IGNORE_SRGB) 1280 | { 1281 | format = MakeLinear(format); 1282 | } 1283 | 1284 | D3D12_RESOURCE_DESC desc = {}; 1285 | desc.Width = static_cast(width); 1286 | desc.Height = static_cast(height); 1287 | desc.MipLevels = static_cast(mipCount); 1288 | desc.DepthOrArraySize = (resDim == D3D12_RESOURCE_DIMENSION_TEXTURE3D) ? static_cast(depth) : static_cast(arraySize); 1289 | desc.Format = format; 1290 | desc.Flags = resFlags; 1291 | desc.SampleDesc.Count = 1; 1292 | desc.SampleDesc.Quality = 0; 1293 | desc.Dimension = resDim; 1294 | 1295 | const CD3DX12_HEAP_PROPERTIES defaultHeapProperties(D3D12_HEAP_TYPE_DEFAULT); 1296 | 1297 | hr = d3dDevice->CreateCommittedResource( 1298 | &defaultHeapProperties, 1299 | D3D12_HEAP_FLAG_NONE, 1300 | &desc, 1301 | D3D12_RESOURCE_STATE_COMMON, 1302 | nullptr, 1303 | IID_ID3D12Resource, reinterpret_cast(texture)); 1304 | if (SUCCEEDED(hr)) 1305 | { 1306 | assert(texture != nullptr && *texture != nullptr); 1307 | _Analysis_assume_(texture != nullptr && *texture != nullptr); 1308 | 1309 | SetDebugObjectName(*texture, L"DDSTextureLoader"); 1310 | } 1311 | 1312 | return hr; 1313 | } 1314 | 1315 | //-------------------------------------------------------------------------------------- 1316 | HRESULT CreateTextureFromDDS(_In_ ID3D12Device* d3dDevice, 1317 | _In_ const DDS_HEADER* header, 1318 | _In_reads_bytes_(bitSize) const uint8_t* bitData, 1319 | size_t bitSize, 1320 | size_t maxsize, 1321 | D3D12_RESOURCE_FLAGS resFlags, 1322 | DDS_LOADER_FLAGS loadFlags, 1323 | _Outptr_ ID3D12Resource** texture, 1324 | std::vector& subresources, 1325 | _Out_opt_ bool* outIsCubeMap) noexcept(false) 1326 | { 1327 | HRESULT hr = S_OK; 1328 | 1329 | const UINT width = header->width; 1330 | UINT height = header->height; 1331 | UINT depth = header->depth; 1332 | 1333 | D3D12_RESOURCE_DIMENSION resDim = D3D12_RESOURCE_DIMENSION_UNKNOWN; 1334 | UINT arraySize = 1; 1335 | DXGI_FORMAT format = DXGI_FORMAT_UNKNOWN; 1336 | bool isCubeMap = false; 1337 | 1338 | size_t mipCount = header->mipMapCount; 1339 | if (0 == mipCount) 1340 | { 1341 | mipCount = 1; 1342 | } 1343 | 1344 | if ((header->ddspf.flags & DDS_FOURCC) && 1345 | (MAKEFOURCC('D', 'X', '1', '0') == header->ddspf.fourCC)) 1346 | { 1347 | auto d3d10ext = reinterpret_cast(reinterpret_cast(header) + sizeof(DDS_HEADER)); 1348 | 1349 | arraySize = d3d10ext->arraySize; 1350 | if (arraySize == 0) 1351 | { 1352 | return HRESULT_E_INVALID_DATA; 1353 | } 1354 | 1355 | switch (d3d10ext->dxgiFormat) 1356 | { 1357 | case DXGI_FORMAT_NV12: 1358 | case DXGI_FORMAT_P010: 1359 | case DXGI_FORMAT_P016: 1360 | case DXGI_FORMAT_420_OPAQUE: 1361 | if ((d3d10ext->resourceDimension != D3D12_RESOURCE_DIMENSION_TEXTURE2D) 1362 | || (width % 2) != 0 || (height % 2) != 0) 1363 | { 1364 | return HRESULT_E_NOT_SUPPORTED; 1365 | } 1366 | break; 1367 | 1368 | case DXGI_FORMAT_YUY2: 1369 | case DXGI_FORMAT_Y210: 1370 | case DXGI_FORMAT_Y216: 1371 | case DXGI_FORMAT_P208: 1372 | if ((width % 2) != 0) 1373 | { 1374 | return HRESULT_E_NOT_SUPPORTED; 1375 | } 1376 | break; 1377 | 1378 | case DXGI_FORMAT_NV11: 1379 | if ((width % 4) != 0) 1380 | { 1381 | return HRESULT_E_NOT_SUPPORTED; 1382 | } 1383 | break; 1384 | 1385 | case DXGI_FORMAT_AI44: 1386 | case DXGI_FORMAT_IA44: 1387 | case DXGI_FORMAT_P8: 1388 | case DXGI_FORMAT_A8P8: 1389 | return HRESULT_E_NOT_SUPPORTED; 1390 | 1391 | case DXGI_FORMAT_V208: 1392 | if ((d3d10ext->resourceDimension != D3D12_RESOURCE_DIMENSION_TEXTURE2D) 1393 | || (height % 2) != 0) 1394 | { 1395 | return HRESULT_E_NOT_SUPPORTED; 1396 | } 1397 | break; 1398 | 1399 | default: 1400 | if (BitsPerPixel(d3d10ext->dxgiFormat) == 0) 1401 | { 1402 | return HRESULT_E_NOT_SUPPORTED; 1403 | } 1404 | } 1405 | 1406 | format = d3d10ext->dxgiFormat; 1407 | 1408 | switch (d3d10ext->resourceDimension) 1409 | { 1410 | case D3D12_RESOURCE_DIMENSION_TEXTURE1D: 1411 | // D3DX writes 1D textures with a fixed Height of 1 1412 | if ((header->flags & DDS_HEIGHT) && height != 1) 1413 | { 1414 | return HRESULT_E_INVALID_DATA; 1415 | } 1416 | height = depth = 1; 1417 | break; 1418 | 1419 | case D3D12_RESOURCE_DIMENSION_TEXTURE2D: 1420 | if (d3d10ext->miscFlag & 0x4 /* RESOURCE_MISC_TEXTURECUBE */) 1421 | { 1422 | arraySize *= 6; 1423 | isCubeMap = true; 1424 | } 1425 | depth = 1; 1426 | break; 1427 | 1428 | case D3D12_RESOURCE_DIMENSION_TEXTURE3D: 1429 | if (!(header->flags & DDS_HEADER_FLAGS_VOLUME)) 1430 | { 1431 | return HRESULT_E_INVALID_DATA; 1432 | } 1433 | 1434 | if (arraySize > 1) 1435 | { 1436 | return HRESULT_E_NOT_SUPPORTED; 1437 | } 1438 | break; 1439 | 1440 | default: 1441 | return HRESULT_E_NOT_SUPPORTED; 1442 | } 1443 | 1444 | resDim = static_cast(d3d10ext->resourceDimension); 1445 | } 1446 | else 1447 | { 1448 | format = GetDXGIFormat(header->ddspf); 1449 | 1450 | if (format == DXGI_FORMAT_UNKNOWN) 1451 | { 1452 | return HRESULT_E_NOT_SUPPORTED; 1453 | } 1454 | 1455 | if (header->flags & DDS_HEADER_FLAGS_VOLUME) 1456 | { 1457 | resDim = D3D12_RESOURCE_DIMENSION_TEXTURE3D; 1458 | } 1459 | else 1460 | { 1461 | if (header->caps2 & DDS_CUBEMAP) 1462 | { 1463 | // We require all six faces to be defined 1464 | if ((header->caps2 & DDS_CUBEMAP_ALLFACES) != DDS_CUBEMAP_ALLFACES) 1465 | { 1466 | return HRESULT_E_NOT_SUPPORTED; 1467 | } 1468 | 1469 | arraySize = 6; 1470 | isCubeMap = true; 1471 | } 1472 | 1473 | depth = 1; 1474 | resDim = D3D12_RESOURCE_DIMENSION_TEXTURE2D; 1475 | 1476 | // Note there's no way for a legacy Direct3D 9 DDS to express a '1D' texture 1477 | } 1478 | 1479 | assert(BitsPerPixel(format) != 0); 1480 | } 1481 | 1482 | // Bound sizes (for security purposes we don't trust DDS file metadata larger than the Direct3D hardware requirements) 1483 | if (mipCount > D3D12_REQ_MIP_LEVELS) 1484 | { 1485 | return HRESULT_E_NOT_SUPPORTED; 1486 | } 1487 | 1488 | switch (resDim) 1489 | { 1490 | case D3D12_RESOURCE_DIMENSION_TEXTURE1D: 1491 | if ((arraySize > D3D12_REQ_TEXTURE1D_ARRAY_AXIS_DIMENSION) || 1492 | (width > D3D12_REQ_TEXTURE1D_U_DIMENSION)) 1493 | { 1494 | return HRESULT_E_NOT_SUPPORTED; 1495 | } 1496 | break; 1497 | 1498 | case D3D12_RESOURCE_DIMENSION_TEXTURE2D: 1499 | if (isCubeMap) 1500 | { 1501 | // This is the right bound because we set arraySize to (NumCubes*6) above 1502 | if ((arraySize > D3D12_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION) || 1503 | (width > D3D12_REQ_TEXTURECUBE_DIMENSION) || 1504 | (height > D3D12_REQ_TEXTURECUBE_DIMENSION)) 1505 | { 1506 | return HRESULT_E_NOT_SUPPORTED; 1507 | } 1508 | } 1509 | else if ((arraySize > D3D12_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION) || 1510 | (width > D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION) || 1511 | (height > D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION)) 1512 | { 1513 | return HRESULT_E_NOT_SUPPORTED; 1514 | } 1515 | break; 1516 | 1517 | case D3D12_RESOURCE_DIMENSION_TEXTURE3D: 1518 | if ((arraySize > 1) || 1519 | (width > D3D12_REQ_TEXTURE3D_U_V_OR_W_DIMENSION) || 1520 | (height > D3D12_REQ_TEXTURE3D_U_V_OR_W_DIMENSION) || 1521 | (depth > D3D12_REQ_TEXTURE3D_U_V_OR_W_DIMENSION)) 1522 | { 1523 | return HRESULT_E_NOT_SUPPORTED; 1524 | } 1525 | break; 1526 | 1527 | default: 1528 | return HRESULT_E_NOT_SUPPORTED; 1529 | } 1530 | 1531 | const UINT numberOfPlanes = D3D12GetFormatPlaneCount(d3dDevice, format); 1532 | if (!numberOfPlanes) 1533 | return E_INVALIDARG; 1534 | 1535 | if ((numberOfPlanes > 1) && IsDepthStencil(format)) 1536 | { 1537 | // DirectX 12 uses planes for stencil, DirectX 11 does not 1538 | return HRESULT_E_NOT_SUPPORTED; 1539 | } 1540 | 1541 | if (outIsCubeMap != nullptr) 1542 | { 1543 | *outIsCubeMap = isCubeMap; 1544 | } 1545 | 1546 | // Create the texture 1547 | size_t numberOfResources = (resDim == D3D12_RESOURCE_DIMENSION_TEXTURE3D) 1548 | ? 1 : arraySize; 1549 | numberOfResources *= mipCount; 1550 | numberOfResources *= numberOfPlanes; 1551 | 1552 | if (numberOfResources > D3D12_REQ_SUBRESOURCES) 1553 | return E_INVALIDARG; 1554 | 1555 | subresources.reserve(numberOfResources); 1556 | 1557 | size_t skipMip = 0; 1558 | size_t twidth = 0; 1559 | size_t theight = 0; 1560 | size_t tdepth = 0; 1561 | hr = FillInitData(width, height, depth, mipCount, arraySize, 1562 | numberOfPlanes, format, 1563 | maxsize, bitSize, bitData, 1564 | twidth, theight, tdepth, skipMip, subresources); 1565 | 1566 | if (SUCCEEDED(hr)) 1567 | { 1568 | size_t reservedMips = mipCount; 1569 | if (loadFlags & DDS_LOADER_MIP_RESERVE) 1570 | { 1571 | reservedMips = std::min(D3D12_REQ_MIP_LEVELS, 1572 | CountMips(width, height)); 1573 | } 1574 | 1575 | hr = CreateTextureResource(d3dDevice, resDim, twidth, theight, tdepth, reservedMips - skipMip, arraySize, 1576 | format, resFlags, loadFlags, texture); 1577 | 1578 | if (FAILED(hr) && !maxsize && (mipCount > 1)) 1579 | { 1580 | subresources.clear(); 1581 | 1582 | maxsize = static_cast( 1583 | (resDim == D3D12_RESOURCE_DIMENSION_TEXTURE3D) 1584 | ? D3D12_REQ_TEXTURE3D_U_V_OR_W_DIMENSION 1585 | : D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION); 1586 | 1587 | hr = FillInitData(width, height, depth, mipCount, arraySize, 1588 | numberOfPlanes, format, 1589 | maxsize, bitSize, bitData, 1590 | twidth, theight, tdepth, skipMip, subresources); 1591 | if (SUCCEEDED(hr)) 1592 | { 1593 | hr = CreateTextureResource(d3dDevice, resDim, twidth, theight, tdepth, mipCount - skipMip, arraySize, 1594 | format, resFlags, loadFlags, texture); 1595 | } 1596 | } 1597 | } 1598 | 1599 | if (FAILED(hr)) 1600 | { 1601 | subresources.clear(); 1602 | } 1603 | 1604 | return hr; 1605 | } 1606 | 1607 | //-------------------------------------------------------------------------------------- 1608 | DDS_ALPHA_MODE GetAlphaMode(_In_ const DDS_HEADER* header) noexcept 1609 | { 1610 | if (header->ddspf.flags & DDS_FOURCC) 1611 | { 1612 | if (MAKEFOURCC('D', 'X', '1', '0') == header->ddspf.fourCC) 1613 | { 1614 | auto d3d10ext = reinterpret_cast(reinterpret_cast(header) + sizeof(DDS_HEADER)); 1615 | auto const mode = static_cast(d3d10ext->miscFlags2 & DDS_MISC_FLAGS2_ALPHA_MODE_MASK); 1616 | switch (mode) 1617 | { 1618 | case DDS_ALPHA_MODE_STRAIGHT: 1619 | case DDS_ALPHA_MODE_PREMULTIPLIED: 1620 | case DDS_ALPHA_MODE_OPAQUE: 1621 | case DDS_ALPHA_MODE_CUSTOM: 1622 | return mode; 1623 | 1624 | case DDS_ALPHA_MODE_UNKNOWN: 1625 | default: 1626 | break; 1627 | } 1628 | } 1629 | else if ((MAKEFOURCC('D', 'X', 'T', '2') == header->ddspf.fourCC) 1630 | || (MAKEFOURCC('D', 'X', 'T', '4') == header->ddspf.fourCC)) 1631 | { 1632 | return DDS_ALPHA_MODE_PREMULTIPLIED; 1633 | } 1634 | } 1635 | 1636 | return DDS_ALPHA_MODE_UNKNOWN; 1637 | } 1638 | 1639 | //-------------------------------------------------------------------------------------- 1640 | void SetDebugTextureInfo( 1641 | _In_z_ const wchar_t* fileName, 1642 | _In_ ID3D12Resource* texture) noexcept 1643 | { 1644 | #if !defined(NO_D3D12_DEBUG_NAME) && ( defined(_DEBUG) || defined(PROFILE) ) 1645 | const wchar_t* pstrName = wcsrchr(fileName, '\\'); 1646 | if (!pstrName) 1647 | { 1648 | pstrName = fileName; 1649 | } 1650 | else 1651 | { 1652 | pstrName++; 1653 | } 1654 | texture->SetName(pstrName); 1655 | #else 1656 | UNREFERENCED_PARAMETER(fileName); 1657 | UNREFERENCED_PARAMETER(texture); 1658 | #endif 1659 | } 1660 | } // anonymous namespace 1661 | 1662 | 1663 | //-------------------------------------------------------------------------------------- 1664 | _Use_decl_annotations_ 1665 | HRESULT DirectX::LoadDDSTextureFromMemory( 1666 | ID3D12Device* d3dDevice, 1667 | const uint8_t* ddsData, 1668 | size_t ddsDataSize, 1669 | ID3D12Resource** texture, 1670 | std::vector& subresources, 1671 | size_t maxsize, 1672 | DDS_ALPHA_MODE* alphaMode, 1673 | bool* isCubeMap) 1674 | { 1675 | return LoadDDSTextureFromMemoryEx( 1676 | d3dDevice, 1677 | ddsData, 1678 | ddsDataSize, 1679 | maxsize, 1680 | D3D12_RESOURCE_FLAG_NONE, 1681 | DDS_LOADER_DEFAULT, 1682 | texture, 1683 | subresources, 1684 | alphaMode, 1685 | isCubeMap); 1686 | } 1687 | 1688 | 1689 | _Use_decl_annotations_ 1690 | HRESULT DirectX::LoadDDSTextureFromMemoryEx( 1691 | ID3D12Device* d3dDevice, 1692 | const uint8_t* ddsData, 1693 | size_t ddsDataSize, 1694 | size_t maxsize, 1695 | D3D12_RESOURCE_FLAGS resFlags, 1696 | DDS_LOADER_FLAGS loadFlags, 1697 | ID3D12Resource** texture, 1698 | std::vector& subresources, 1699 | DDS_ALPHA_MODE* alphaMode, 1700 | bool* isCubeMap) 1701 | { 1702 | if (texture) 1703 | { 1704 | *texture = nullptr; 1705 | } 1706 | if (alphaMode) 1707 | { 1708 | *alphaMode = DDS_ALPHA_MODE_UNKNOWN; 1709 | } 1710 | if (isCubeMap) 1711 | { 1712 | *isCubeMap = false; 1713 | } 1714 | 1715 | if (!d3dDevice || !ddsData || !texture) 1716 | { 1717 | return E_INVALIDARG; 1718 | } 1719 | 1720 | // Validate DDS file in memory 1721 | const DDS_HEADER* header = nullptr; 1722 | const uint8_t* bitData = nullptr; 1723 | size_t bitSize = 0; 1724 | 1725 | HRESULT hr = LoadTextureDataFromMemory(ddsData, 1726 | ddsDataSize, 1727 | &header, 1728 | &bitData, 1729 | &bitSize 1730 | ); 1731 | if (FAILED(hr)) 1732 | { 1733 | return hr; 1734 | } 1735 | 1736 | hr = CreateTextureFromDDS(d3dDevice, 1737 | header, bitData, bitSize, maxsize, 1738 | resFlags, loadFlags, 1739 | texture, subresources, isCubeMap); 1740 | if (SUCCEEDED(hr)) 1741 | { 1742 | SetDebugObjectName(*texture, L"DDSTextureLoader"); 1743 | 1744 | if (alphaMode) 1745 | *alphaMode = GetAlphaMode(header); 1746 | } 1747 | 1748 | return hr; 1749 | } 1750 | 1751 | 1752 | //-------------------------------------------------------------------------------------- 1753 | _Use_decl_annotations_ 1754 | HRESULT DirectX::LoadDDSTextureFromFile( 1755 | ID3D12Device* d3dDevice, 1756 | const wchar_t* fileName, 1757 | ID3D12Resource** texture, 1758 | std::unique_ptr& ddsData, 1759 | std::vector& subresources, 1760 | size_t maxsize, 1761 | DDS_ALPHA_MODE* alphaMode, 1762 | bool* isCubeMap) 1763 | { 1764 | return LoadDDSTextureFromFileEx( 1765 | d3dDevice, 1766 | fileName, 1767 | maxsize, 1768 | D3D12_RESOURCE_FLAG_NONE, 1769 | DDS_LOADER_DEFAULT, 1770 | texture, 1771 | ddsData, 1772 | subresources, 1773 | alphaMode, 1774 | isCubeMap); 1775 | } 1776 | 1777 | _Use_decl_annotations_ 1778 | HRESULT DirectX::LoadDDSTextureFromFileEx( 1779 | ID3D12Device* d3dDevice, 1780 | const wchar_t* fileName, 1781 | size_t maxsize, 1782 | D3D12_RESOURCE_FLAGS resFlags, 1783 | DDS_LOADER_FLAGS loadFlags, 1784 | ID3D12Resource** texture, 1785 | std::unique_ptr& ddsData, 1786 | std::vector& subresources, 1787 | DDS_ALPHA_MODE* alphaMode, 1788 | bool* isCubeMap) 1789 | { 1790 | if (texture) 1791 | { 1792 | *texture = nullptr; 1793 | } 1794 | if (alphaMode) 1795 | { 1796 | *alphaMode = DDS_ALPHA_MODE_UNKNOWN; 1797 | } 1798 | if (isCubeMap) 1799 | { 1800 | *isCubeMap = false; 1801 | } 1802 | 1803 | if (!d3dDevice || !fileName || !texture) 1804 | { 1805 | return E_INVALIDARG; 1806 | } 1807 | 1808 | const DDS_HEADER* header = nullptr; 1809 | const uint8_t* bitData = nullptr; 1810 | size_t bitSize = 0; 1811 | 1812 | HRESULT hr = LoadTextureDataFromFile(fileName, 1813 | ddsData, 1814 | &header, 1815 | &bitData, 1816 | &bitSize 1817 | ); 1818 | if (FAILED(hr)) 1819 | { 1820 | return hr; 1821 | } 1822 | 1823 | hr = CreateTextureFromDDS(d3dDevice, 1824 | header, bitData, bitSize, maxsize, 1825 | resFlags, loadFlags, 1826 | texture, subresources, isCubeMap); 1827 | 1828 | if (SUCCEEDED(hr)) 1829 | { 1830 | SetDebugTextureInfo(fileName, *texture); 1831 | 1832 | if (alphaMode) 1833 | *alphaMode = GetAlphaMode(header); 1834 | } 1835 | 1836 | return hr; 1837 | } 1838 | --------------------------------------------------------------------------------