diff --git a/.gitmodules b/.gitmodules index 6565c10..40411be 100644 --- a/.gitmodules +++ b/.gitmodules @@ -14,11 +14,3 @@ path = thirdparty/Vulkan-Headers url = https://github.com/KhronosGroup/Vulkan-Headers.git branch = sdk-1.2.170 -[submodule "thirdparty/libpng"] - path = thirdparty/libpng - url = https://github.com/glennrp/libpng.git - branch = libpng16 -[submodule "thirdparty/zlib-ng"] - path = thirdparty/zlib-ng - url = https://github.com/zlib-ng/zlib-ng.git - branch = stable diff --git a/CMakeLists.txt b/CMakeLists.txt index c868510..d8b4ca1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,26 +47,13 @@ if(RTXGI_BUILD_SAMPLES) target_compile_definitions(tinygltf PUBLIC _CRT_SECURE_NO_WARNINGS) # suppress the sprintf CRT warnings set_target_properties(tinygltf PROPERTIES FOLDER "Thirdparty/") - # zlib-ng - option(ZLIB_COMPAT "" ON) - option(ZLIB_ENABLE_TESTS "" OFF) - add_subdirectory(thirdparty/zlib-ng) - set_target_properties(zlib PROPERTIES FOLDER "Thirdparty/") - - # libpng - option(PNG_BUILD_ZLIB "" ON) - option(PNG_STATIC "" ON) - option(PNG_SHARED "" OFF) - option(PNG_EXECUTABLES "" OFF) - option(PNG_FRAMEWORK "" OFF) - option(PNG_TESTS "" OFF) - if(UNIX AND NOT APPLE) - set(ZLIB_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/thirdparty/zlib-ng" "${CMAKE_BINARY_DIR}/thirdparty/zlib-ng") - endif() - set(ZLIB_INCLUDE_DIRS "${CMAKE_SOURCE_DIR}/thirdparty/zlib-ng" "${CMAKE_BINARY_DIR}/thirdparty/zlib-ng") - add_subdirectory(thirdparty/libpng) - set_target_properties(png_static genfiles PROPERTIES FOLDER "Thirdparty/libpng") - # Samples add_subdirectory(samples) + + # Set the default project. If D3D is an option, set it as default. + if(RTXGI_API_D3D12_ENABLE) + set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT TestHarness-D3D12) + elseif(NOT RTXGI_API_D3D12_ENABLE AND RTXGI_API_VULKAN_ENABLE) + set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT TestHarness-VK) + endif() endif() diff --git a/ChangeLog.md b/ChangeLog.md index 78b4e50..86f55c5 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,5 +1,26 @@ # RTXGI SDK Change Log +## 1.2.12a + +### SDK +- No changes + +### Test Harness +Features and Improvements: +- Uses stb (stb_image_write) for image saving and screenshot functionality (included with the tinygltf dependency) +- Removes zlib-ng and libpng dependencies +- Updates Windows DXC (packman) dependencies to version 1.6.2112 which includes ```dxil.dll``` +- Now using std::filesystem on Windows and Linux +- CMake + - Adds option to select in CMake whether to use the DXC & DXIL binaries from Packman or an installed Win10 SDK (Windows only) + - Properly sets the default startup project for RTXGI.sln (it really works now!) + - Adds arguments to automatically set the available config ini files for the SmartCmdArgs extension + - Removes DirectXTK ```ScreenGrab[.h|.cpp]``` files + +Bug Fixes: +- Fixes VK validation layer error related to the back buffer not being marked as a possible copy source +- Fixes D3D12 and VK swapchain creation failures (causing a crash) when alt+tabbing or minimizing the application when in fullscreen mode + ## 1.2.12 ### SDK diff --git a/packman/dependencies.xml b/packman/dependencies.xml index 95ef49a..1f59b72 100644 --- a/packman/dependencies.xml +++ b/packman/dependencies.xml @@ -1,6 +1,7 @@ - + + - \ No newline at end of file + diff --git a/rtxgi-sdk/FindSDKs.cmake b/rtxgi-sdk/FindSDKs.cmake index 2e5737c..58b2ddc 100644 --- a/rtxgi-sdk/FindSDKs.cmake +++ b/rtxgi-sdk/FindSDKs.cmake @@ -1,5 +1,5 @@ # -# Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2019-2022, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index a6658e0..efaa6b9 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2019-2022, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation diff --git a/samples/test-harness/.args b/samples/test-harness/.args new file mode 100644 index 0000000..4b738de --- /dev/null +++ b/samples/test-harness/.args @@ -0,0 +1,29 @@ +{ + "FileVersion": 2, + "Items": [ + { + "Command": "Scene Configs", + "Items": [ + { + "Command": "../../../samples/test-harness/config/cornell.ini", + "DefaultChecked": true + }, + { + "Command": "../../../samples/test-harness/config/furnace.ini" + }, + { + "Command": "../../../samples/test-harness/config/multi-cornell.ini" + }, + { + "Command": "../../../samples/test-harness/config/sponza.ini" + }, + { + "Command": "../../../samples/test-harness/config/tunnel.ini" + }, + { + "Command": "../../../samples/test-harness/config/two-rooms.ini" + } + ] + } + ] +} \ No newline at end of file diff --git a/samples/test-harness/CMakeLists.txt b/samples/test-harness/CMakeLists.txt index 3b31653..f956094 100644 --- a/samples/test-harness/CMakeLists.txt +++ b/samples/test-harness/CMakeLists.txt @@ -191,14 +191,6 @@ file(GLOB THIRD_PARTY_DIRECTXTEX_GPU_COMPRESS_SOURCE "src/thirdparty/directxtex/BCDirectCompute.cpp" ) -file(GLOB THIRD_PARTY_DIRECTXTK_INCLUDE - "include/thirdparty/directxtk/ScreenGrab12.h" -) - -file(GLOB THIRD_PARTY_DIRECTXTK_SOURCE - "src/thirdparty/directxtk/ScreenGrab12.cpp" -) - file(GLOB THIRD_PARTY_IMGUI_SOURCE "../../thirdparty/imgui/imgui.cpp" "../../thirdparty/imgui/imgui_demo.cpp" @@ -235,6 +227,13 @@ option(RTXGISAMPLES_TEST_HARNESS_DDGI_DEBUG_BORDER_COPY_INDEXING "Enable a borde # Setup the Test Harness options function(SetupOptions ARG_TARGET_EXE) + if(WIN32) + # Setup the dxcompiler and dxil binary options + set(RTXGISAMPLES_DXC_BINARIES "Packman" CACHE STRING "The dxcompiler and dxil binaries to use") + set(RTXGISAMPLES_DXC_BINARIES "Win10 SDK" CACHE STRING "The dxcompiler and dxil binaries to use") + set_property(CACHE RTXGISAMPLES_DXC_BINARIES PROPERTY STRINGS "Packman" "Win10 SDK") + endif() + # Set GFX object naming if(RTXGISAMPLES_GFX_NAME_OBJECTS) target_compile_definitions(${ARG_TARGET_EXE} PRIVATE GFX_NAME_OBJECTS) @@ -268,7 +267,6 @@ set(GLFW_INCLUDE "${ROOT_DIR}/thirdparty/glfw/include") set(IMGUI_INCLUDE "${ROOT_DIR}/thirdparty/imgui") set(IMGUI_BACKENDS_INCLUDE "${ROOT_DIR}/thirdparty/imgui/backends") set(TINYGLTF_INCLUDE "${ROOT_DIR}/thirdparty/tinygltf") -set(LIBPNG_INCLUDE "${ROOT_DIR}/thirdparty/libpng" "${CMAKE_BINARY_DIR}/thirdparty/libpng") # ---- WINDOWS / D3D12 -------------------------------------------------------------------------------------- @@ -293,13 +291,12 @@ if(RTXGI_API_D3D12_ENABLE) ${THIRD_PARTY_DIRECTXTEX_SOURCE} ${THIRD_PARTY_DIRECTXTEX_GPU_COMPRESS_SOURCE} ${THIRD_PARTY_DIRECTXTK_INCLUDE} - ${THIRD_PARTY_DIRECTXTK_SOURCE} ${THIRD_PARTY_IMGUI_SOURCE} ${THIRD_PARTY_IMGUI_D3D12_SOURCE} ) # Add dependencies - add_dependencies(${TARGET_EXE} glfw tinygltf png_static zlib) + add_dependencies(${TARGET_EXE} glfw tinygltf) # Add the include directories target_include_directories(${TARGET_EXE} PRIVATE @@ -309,16 +306,16 @@ if(RTXGI_API_D3D12_ENABLE) "include/thirdparty/directxtex" "include/thirdparty/directxtk" "include/thirdparty/dxc" + "include/thirdparty/stb" ${DIRECTXMATH_INCLUDE} ${GLFW_INCLUDE} ${IMGUI_INCLUDE} ${IMGUI_BACKENDS_INCLUDE} ${TINYGLTF_INCLUDE} - ${LIBPNG_INCLUDE} ) # Add statically linked libs - target_link_libraries(${TARGET_EXE} RTXGI-D3D12 glfw png_static zlib d3d11 d3d12 dxgi) + target_link_libraries(${TARGET_EXE} RTXGI-D3D12 glfw d3d11 d3d12 dxgi) # Add common compiler definitions for exposed Test Harness options SetupOptions(${TARGET_EXE}) @@ -371,12 +368,12 @@ if(RTXGI_API_VULKAN_ENABLE) "include/thirdparty" "include/thirdparty/directxtex" "include/thirdparty/dxc" + "include/thirdparty/stb" ${DIRECTXMATH_INCLUDE} ${GLFW_INCLUDE} ${IMGUI_INCLUDE} ${IMGUI_BACKENDS_INCLUDE} ${TINYGLTF_INCLUDE} - ${LIBPNG_INCLUDE} ) # Add VS filters @@ -422,17 +419,17 @@ if(RTXGI_API_VULKAN_ENABLE) "include/thirdparty/directxmath" "include/thirdparty/directxtex" "include/thirdparty/dxc" + "include/thirdparty/stb" ${GLFW_INCLUDE} ${IMGUI_INCLUDE} ${IMGUI_BACKENDS_INCLUDE} ${TINYGLTF_INCLUDE} - ${LIBPNG_INCLUDE} ) endif() # Add dependencies - add_dependencies(${TARGET_EXE} glfw tinygltf png_static zlib) + add_dependencies(${TARGET_EXE} glfw tinygltf) # Add compiler definitions target_compile_definitions(${TARGET_EXE} PUBLIC API_VULKAN) @@ -446,7 +443,7 @@ if(RTXGI_API_VULKAN_ENABLE) # Add statically linked libs if(WIN32) # Note: Even when targeting Vulkan, Windows uses D3D11 GPU-based texture compression with DirectXTex - target_link_libraries(${TARGET_EXE} RTXGI-VK ${Vulkan_LIBRARY} glfw png_static zlib d3d11) + target_link_libraries(${TARGET_EXE} RTXGI-VK ${Vulkan_LIBRARY} glfw d3d11) elseif(UNIX AND NOT APPLE) # Note: UNIX can't use D3D11 GPU-based texture compression with DirectXTex target_link_libraries(${TARGET_EXE} RTXGI-VK -lglfw -lvulkan -ldl -lpthread -lX11 -lXrandr -lXi -lstdc++fs) @@ -462,7 +459,7 @@ if(RTXGI_API_VULKAN_ENABLE) set_target_properties(${TARGET_EXE} PROPERTIES FOLDER "RTXGI Samples") endif() # VULKAN_ENABLE -if(WIN32) +if(WIN32 AND MSVC) # Add VS filters source_group("Config" FILES ${TEST_HARNESS_CONFIG}) @@ -477,55 +474,62 @@ if(WIN32) source_group("Source Files/Thirdparty/DirectXTex" FILES ${THIRD_PARTY_DIRECTXTEX_SOURCE} ${THIRD_PARTY_DIRECTXTEX_GPU_COMPRESS_SOURCE}) source_group("Source Files/Thirdparty/ImGui" FILES ${THIRD_PARTY_IMGUI_SOURCE} ${THIRD_PARTY_IMGUI_D3D12_SOURCE} ${THIRD_PARTY_IMGUI_VULKAN_SOURCE}) - # D3D12 only toolkit files - if(RTXGI_API_D3D12_ENABLE) - source_group("Header Files/Thirdparty/DirectXTK" FILES ${THIRD_PARTY_DIRECTXTK_INCLUDE}) - source_group("Source Files/Thirdparty/DirectXTK" FILES ${THIRD_PARTY_DIRECTXTK_SOURCE}) - endif() + # Set arguments for Visual Studio Smart Command Line Arguments extension + # https://github.com/MBulli/SmartCommandlineArgs + configure_file("${ROOT_DIR}/samples/test-harness/.args" "${CMAKE_BINARY_DIR}/samples/test-harness/TestHarness-D3D12.args.json" COPYONLY) + configure_file("${ROOT_DIR}/samples/test-harness/.args" "${CMAKE_BINARY_DIR}/samples/test-harness/TestHarness-VK.args.json" COPYONLY) +endif() # WIN32 and MSVC - # Set the default project. If D3D is an option, set it as default. - if(RTXGI_API_D3D12_ENABLE) - set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT TestHarness-D3D12) - elseif(NOT RTXGI_API_D3D12_ENABLE AND RTXGI_API_VULKAN_ENABLE) - set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT TestHarness-VK) +# Set the dxc binaries location (Windows only) +if(WIN32) + if(${RTXGISAMPLES_DXC_BINARIES} MATCHES "Packman") + set(DXC_BINARY ${ROOT_DIR}/dxc/bin/dxcompiler.dll) + set(DXIL_BINARY ${ROOT_DIR}/dxc/bin/dxil.dll) + elseif(${RTXGISAMPLES_DXC_BINARIES} MATCHES "Win10 SDK") + set(DXC_BINARY ${RTXGI_API_D3D12_DXIL_PATH}/dxcompiler.dll) + set(DXIL_BINARY ${RTXGI_API_D3D12_DXIL_PATH}/dxil.dll) endif() - -endif() # WIN32 +endif() if(WIN32 AND RTXGI_API_D3D12_ENABLE) - # Copy required DLLs and logo images to the working directories + # Copy required DLLs next to the executable + add_custom_command( TARGET TestHarness-D3D12 POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${DXIL_BINARY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/d3d12/$ + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${DXC_BINARY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/d3d12/$ + ) + + # Copy logo image to the working directories add_custom_command( TARGET TestHarness-D3D12 POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${RTXGI_API_D3D12_DXIL_PATH}/dxil.dll ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/d3d12/$ - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${ROOT_DIR}/dxc/bin/dxcompiler.dll ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/d3d12/$ COMMAND ${CMAKE_COMMAND} -E copy_if_different ${ROOT_DIR}/docs/nvidia.jpg ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/d3d12/$ COMMAND ${CMAKE_COMMAND} -E copy_if_different ${ROOT_DIR}/docs/nvidia.jpg ${CMAKE_BINARY_DIR}/samples/test-harness/ ) endif() if(WIN32 AND RTXGI_API_VULKAN_ENABLE) - # Copy required DLLs and logo images to the working directories + # Copy required DLLs next to the executable + add_custom_command( TARGET TestHarness-VK POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${DXC_BINARY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/vulkan/$ + ) + + # Copy logo image to the working directories add_custom_command( TARGET TestHarness-VK POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${ROOT_DIR}/dxc/bin/dxcompiler.dll ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/vulkan/$ COMMAND ${CMAKE_COMMAND} -E copy_if_different ${ROOT_DIR}/docs/nvidia.jpg ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/vulkan/$ COMMAND ${CMAKE_COMMAND} -E copy_if_different ${ROOT_DIR}/docs/nvidia.jpg ${CMAKE_BINARY_DIR}/samples/test-harness/ ) endif() if(UNIX AND RTXGI_API_VULKAN_ENABLE) - # Copy required shared libraries and logo images to the working directories - if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "aarch64") + if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "aarch64" OR ${CMAKE_SYSTEM_PROCESSOR} MATCHES "x86_64") + # Copy required shared libraries next to the executable add_custom_command( TARGET TestHarness-VK POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${ROOT_DIR}/dxc/lib/libdxcompiler.so ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/vulkan/$ COMMAND ${CMAKE_COMMAND} -E copy_if_different ${ROOT_DIR}/dxc/lib/libdxcompiler.so ${CMAKE_BINARY_DIR}/samples/test-harness - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${ROOT_DIR}/docs/nvidia.jpg ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/vulkan/$ - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${ROOT_DIR}/docs/nvidia.jpg ${CMAKE_BINARY_DIR}/samples/test-harness ) - elseif(${CMAKE_SYSTEM_PROCESSOR} MATCHES "x86_64") - add_custom_command( TARGET TestHarness-VK POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${ROOT_DIR}/dxc/lib/libdxcompiler.so ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/vulkan/$ - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${ROOT_DIR}/dxc/lib/libdxcompiler.so ${CMAKE_BINARY_DIR}/samples/test-harness + + # Copy logo image to the working directories + add_custom_command( TARGET TestHarness-VK POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${ROOT_DIR}/docs/nvidia.jpg ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/vulkan/$ COMMAND ${CMAKE_COMMAND} -E copy_if_different ${ROOT_DIR}/docs/nvidia.jpg ${CMAKE_BINARY_DIR}/samples/test-harness - ) + ) endif() endif() diff --git a/samples/test-harness/include/Direct3D12.h b/samples/test-harness/include/Direct3D12.h index 71cc58f..490fd15 100644 --- a/samples/test-harness/include/Direct3D12.h +++ b/samples/test-harness/include/Direct3D12.h @@ -25,6 +25,7 @@ namespace Graphics { static const D3D12_HEAP_PROPERTIES defaultHeapProps = { D3D12_HEAP_TYPE_DEFAULT, D3D12_CPU_PAGE_PROPERTY_UNKNOWN, D3D12_MEMORY_POOL_UNKNOWN, 0, 0 }; static const D3D12_HEAP_PROPERTIES uploadHeapProps = { D3D12_HEAP_TYPE_UPLOAD, D3D12_CPU_PAGE_PROPERTY_UNKNOWN, D3D12_MEMORY_POOL_UNKNOWN, 0, 0 }; + static const D3D12_HEAP_PROPERTIES readbackHeapProps = { D3D12_HEAP_TYPE_READBACK, D3D12_CPU_PAGE_PROPERTY_UNKNOWN, D3D12_MEMORY_POOL_UNKNOWN, 0, 0 }; bool Check(HRESULT hr, std::string fileName, uint32_t lineNumber); #define D3DCHECK(hr) if(!Check(hr, __FILE__, __LINE__)) { return false; } @@ -40,6 +41,7 @@ namespace Graphics { DEFAULT = 0, UPLOAD = 1, + READBACK = 2 }; struct BufferDesc diff --git a/samples/test-harness/include/ImageCapture.h b/samples/test-harness/include/ImageCapture.h index f36ff4c..a2158c2 100644 --- a/samples/test-harness/include/ImageCapture.h +++ b/samples/test-harness/include/ImageCapture.h @@ -12,9 +12,21 @@ #include #include + +#if defined(_WIN32) || defined(WIN32) +#include +#include #include +#endif namespace ImageCapture { - bool CapturePng(std::string file, uint32_t width, uint32_t height, std::vector& rows); -} \ No newline at end of file + const static uint32_t NumChannels = 4; + bool CapturePng(std::string file, uint32_t width, uint32_t height, const unsigned char* data); + +#if defined(_WIN32) || defined(WIN32) + IWICImagingFactory2* CreateWICImagingFactory(); + HRESULT ConvertTextureResource(const D3D12_RESOURCE_DESC desc, UINT64 imageSize, UINT64 dstRowPitch, unsigned char* pMappedMemory, std::vector& converted); +#endif + +} diff --git a/samples/test-harness/include/thirdparty/directxtk/ScreenGrab12.h b/samples/test-harness/include/thirdparty/directxtk/ScreenGrab12.h deleted file mode 100644 index c7864cf..0000000 --- a/samples/test-harness/include/thirdparty/directxtk/ScreenGrab12.h +++ /dev/null @@ -1,45 +0,0 @@ -//-------------------------------------------------------------------------------------- -// File: ScreenGrab12.h -// -// Function for capturing a 2D texture and saving it to a file (aka a 'screenshot' -// when used on a Direct3D 12 Render Target). -// -// Note these functions are useful as a light-weight runtime screen grabber. For -// full-featured texture capture, DDS writer, and texture processing pipeline, -// see the 'Texconv' sample and the 'DirectXTex' library. -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -// -// http://go.microsoft.com/fwlink/?LinkId=248926 -// http://go.microsoft.com/fwlink/?LinkID=615561 -//-------------------------------------------------------------------------------------- - -#pragma once - -#include - -#include -#include -#include - - -namespace DirectX -{ - HRESULT __cdecl SaveDDSTextureToFile( - _In_ ID3D12CommandQueue* pCommandQueue, - _In_ ID3D12Resource* pSource, - _In_z_ const wchar_t* fileName, - D3D12_RESOURCE_STATES beforeState = D3D12_RESOURCE_STATE_RENDER_TARGET, - D3D12_RESOURCE_STATES afterState = D3D12_RESOURCE_STATE_RENDER_TARGET); - - HRESULT __cdecl SaveWICTextureToFile( - _In_ ID3D12CommandQueue* pCommandQ, - _In_ ID3D12Resource* pSource, - REFGUID guidContainerFormat, - _In_z_ const wchar_t* fileName, - D3D12_RESOURCE_STATES beforeState = D3D12_RESOURCE_STATE_RENDER_TARGET, - D3D12_RESOURCE_STATES afterState = D3D12_RESOURCE_STATE_RENDER_TARGET, - _In_opt_ const GUID* targetFormat = nullptr, - _In_opt_ std::function setCustomProps = nullptr); -} \ No newline at end of file diff --git a/samples/test-harness/src/Benchmark.cpp b/samples/test-harness/src/Benchmark.cpp index 9866004..3ddf416 100644 --- a/samples/test-harness/src/Benchmark.cpp +++ b/samples/test-harness/src/Benchmark.cpp @@ -11,15 +11,12 @@ #include "Benchmark.h" #include + namespace Benchmark { void StartBenchmark(BenchmarkRun& benchmarkRun, Instrumentation::Performance& perf, Configs::Config& config, Graphics::Globals& gfx) { - #if defined(_WIN32) || defined(WIN32) - CreateDirectory(config.scene.screenshotPath.c_str(), NULL); - #elif __linux__ std::filesystem::create_directories(config.scene.screenshotPath.c_str()); - #endif benchmarkRun.numFramesBenched = 0; benchmarkRun.cpuTimingCsv.str(""); diff --git a/samples/test-harness/src/Direct3D12.cpp b/samples/test-harness/src/Direct3D12.cpp index 0d60c30..a30d172 100644 --- a/samples/test-harness/src/Direct3D12.cpp +++ b/samples/test-harness/src/Direct3D12.cpp @@ -12,9 +12,6 @@ #include "UI.h" #include "ImageCapture.h" -#include -#include - namespace Graphics { using namespace DirectX; @@ -766,8 +763,8 @@ namespace Graphics } /** - * Create the global (bindless) root signature. - */ + * Create the global (bindless) root signature. + */ bool CreateGlobalRootSignature(Globals& d3d, Resources& resources) { D3D12_DESCRIPTOR_RANGE ranges[9]; @@ -899,8 +896,8 @@ namespace Graphics } /** - * Create the shared render targets. - */ + * Create the shared render targets. + */ bool CreateRenderTargets(Globals& d3d, Resources& resources) { // Create the GBufferA (R8G8B8A8_UNORM) texture resource @@ -1030,8 +1027,8 @@ namespace Graphics } /** - * Release core D3D12 resources. - */ + * Release core D3D12 resources. + */ void Cleanup(Globals& d3d) { // Leave fullscreen mode if necessary @@ -1593,39 +1590,18 @@ namespace Graphics // Debug Functions //---------------------------------------------------------------------------------------------------------- - IWICImagingFactory2* _GetWIC() - { - static INIT_ONCE s_initOnce = INIT_ONCE_STATIC_INIT; - - IWICImagingFactory2* factory = nullptr; - (void)InitOnceExecuteOnce(&s_initOnce, - [](PINIT_ONCE, PVOID, PVOID* ifactory) -> BOOL - { - return SUCCEEDED(CoCreateInstance( - CLSID_WICImagingFactory2, - nullptr, - CLSCTX_INPROC_SERVER, - __uuidof(IWICImagingFactory2), - ifactory)) ? TRUE : FALSE; - }, nullptr, reinterpret_cast(&factory)); - - return factory; - } - /** * Write an image to disk from the given D3D12 resource. */ bool WriteResourceToDisk(Globals& d3d, std::string file, ID3D12Resource* pResource, D3D12_RESOURCE_STATES state) { - CoInitialize(NULL); - std::wstring filename = std::wstring(file.begin(), file.end()); - //if(FAILED(SaveWICTextureToFile(d3d.cmdQueue, pResource, GUID_ContainerFormatPng, filename.c_str(), state, state))) return false; - - // copied from SaveWICTextureToFile() from DirectXTK, but using libpng instead of WIC + // Get the resource descriptor const D3D12_RESOURCE_DESC desc = pResource->GetDesc(); - UINT64 totalResourceSize = 0, fpRowPitch = 0; + + // Get the row count, pitch, and size of the top mip level + UINT64 totalResourceSize = 0; + UINT64 fpRowPitch = 0; UINT fpRowCount = 0; - // Get the rowcount, pitch and size of the top mip d3d.device->GetCopyableFootprints( &desc, 0, @@ -1635,31 +1611,28 @@ namespace Graphics &fpRowCount, &fpRowPitch, &totalResourceSize); + // Round up the srcPitch to multiples of 256 UINT64 dstRowPitch = (fpRowPitch + 255) & ~0xFF; - ID3D12Resource* pStaging = nullptr; + // Get the heap properties D3D12_HEAP_PROPERTIES sourceHeapProperties = {}; D3D12_HEAP_FLAGS sourceHeapFlags = {}; - HRESULT hr = pResource->GetHeapProperties(&sourceHeapProperties, &sourceHeapFlags); + D3DCHECK(pResource->GetHeapProperties(&sourceHeapProperties, &sourceHeapFlags)); + + // Create a command allocator ID3D12CommandAllocator* commandAlloc = nullptr; - hr = d3d.device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&commandAlloc)); + D3DCHECK(d3d.device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&commandAlloc))); + + // Create a command list ID3D12GraphicsCommandList* commandList = nullptr; - hr = d3d.device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, commandAlloc, nullptr, IID_PPV_ARGS(&commandList)); + D3DCHECK(d3d.device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, commandAlloc, nullptr, IID_PPV_ARGS(&commandList))); + + // Create fence ID3D12Fence* fence = nullptr; - hr = d3d.device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence)); - D3D12_HEAP_PROPERTIES defaultHeapProperties = {}, readBackHeapProperties = {}; - defaultHeapProperties.Type = D3D12_HEAP_TYPE_DEFAULT; - defaultHeapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; - defaultHeapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; - defaultHeapProperties.CreationNodeMask = 1; - defaultHeapProperties.VisibleNodeMask = 1; - readBackHeapProperties.Type = D3D12_HEAP_TYPE_READBACK; - readBackHeapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; - readBackHeapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; - readBackHeapProperties.CreationNodeMask = 1; - readBackHeapProperties.VisibleNodeMask = 1; - // Readback resources must be buffers + D3DCHECK(d3d.device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence))); + + // Describe the read-back buffer resource D3D12_RESOURCE_DESC bufferDesc = {}; bufferDesc.Alignment = desc.Alignment; bufferDesc.DepthOrArraySize = 1; @@ -1672,26 +1645,29 @@ namespace Graphics bufferDesc.MipLevels = 1; bufferDesc.SampleDesc.Count = 1; bufferDesc.SampleDesc.Quality = 0; - // Create a staging texture - hr = d3d.device->CreateCommittedResource( - &readBackHeapProperties, + + // Create a staging texture resource + ID3D12Resource* pStaging = nullptr; + D3DCHECK(d3d.device->CreateCommittedResource( + &readbackHeapProps, D3D12_HEAP_FLAG_NONE, &bufferDesc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr, - IID_PPV_ARGS(&pStaging)); + IID_PPV_ARGS(&pStaging))); { - D3D12_RESOURCE_BARRIER barrierDesc = {}; - barrierDesc.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; - barrierDesc.Transition.pResource = pResource; - barrierDesc.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; - barrierDesc.Transition.StateBefore = state; - barrierDesc.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE; - commandList->ResourceBarrier(1, &barrierDesc); + // Transition the staging texture resource to a copy source + D3D12_RESOURCE_BARRIER barrier = {}; + barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier.Transition.pResource = pResource; + barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + barrier.Transition.StateBefore = state; + barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE; + commandList->ResourceBarrier(1, &barrier); } - // Get the copy target location + // Describe the copy footprint of the resource D3D12_PLACED_SUBRESOURCE_FOOTPRINT bufferFootprint = {}; bufferFootprint.Footprint.Width = static_cast(desc.Width); bufferFootprint.Footprint.Height = desc.Height; @@ -1699,129 +1675,68 @@ namespace Graphics bufferFootprint.Footprint.RowPitch = static_cast(dstRowPitch); bufferFootprint.Footprint.Format = desc.Format; - D3D12_TEXTURE_COPY_LOCATION copySrc = {}, copyDest = {}; + // Describe the copy source resource + D3D12_TEXTURE_COPY_LOCATION copySrc = {}; copySrc.pResource = pResource; copySrc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; copySrc.SubresourceIndex = 0; + + // Describe the copy destination resource + D3D12_TEXTURE_COPY_LOCATION copyDest = {}; copyDest.pResource = pStaging; copyDest.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; copyDest.PlacedFootprint = bufferFootprint; - // Copy the texture + // Schedule the texture copy commandList->CopyTextureRegion(©Dest, 0, 0, 0, ©Src, nullptr); { - D3D12_RESOURCE_BARRIER barrierDesc = {}; - barrierDesc.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; - barrierDesc.Transition.pResource = pResource; - barrierDesc.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; - barrierDesc.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_SOURCE; - barrierDesc.Transition.StateAfter = state; - commandList->ResourceBarrier(1, &barrierDesc); + // Transition the staging texture resource to the specified state + D3D12_RESOURCE_BARRIER barrier = {}; + barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier.Transition.pResource = pResource; + barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_SOURCE; + barrier.Transition.StateAfter = state; + commandList->ResourceBarrier(1, &barrier); } - hr = commandList->Close(); + // Close the command list + D3DCHECK(commandList->Close()); // Execute the command list d3d.cmdQueue->ExecuteCommandLists(1, reinterpret_cast(&commandList)); + // Signal the fence - hr = d3d.cmdQueue->Signal(fence, 1); + D3DCHECK(d3d.cmdQueue->Signal(fence, 1)); + // Block until the copy is complete while (fence->GetCompletedValue() < 1) SwitchToThread(); + // Map the staging texture resource + unsigned char* pData = nullptr; UINT64 imageSize = dstRowPitch * fpRowCount; - unsigned char* pMappedMemory = nullptr; D3D12_RANGE readRange = { 0, static_cast(imageSize) }; - D3D12_RANGE writeRange = { 0, 0 }; - hr = pStaging->Map(0, &readRange, (void**)&pMappedMemory); + D3DCHECK(pStaging->Map(0, &readRange, (void**)&pData)); - // convert to RGBA8 UNORM using WIC - std::vector converted(desc.Width * desc.Height * 4); - { - // Determine source format's WIC equivalent - WICPixelFormatGUID pfGuid; - bool sRGB = false; - switch (desc.Format) - { - case DXGI_FORMAT_R32G32B32A32_FLOAT: pfGuid = GUID_WICPixelFormat128bppRGBAFloat; break; - case DXGI_FORMAT_R16G16B16A16_FLOAT: pfGuid = GUID_WICPixelFormat64bppRGBAHalf; break; - case DXGI_FORMAT_R16G16B16A16_UNORM: pfGuid = GUID_WICPixelFormat64bppRGBA; break; - case DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM: pfGuid = GUID_WICPixelFormat32bppRGBA1010102XR; break; - case DXGI_FORMAT_R10G10B10A2_UNORM: pfGuid = GUID_WICPixelFormat32bppRGBA1010102; break; - case DXGI_FORMAT_B5G5R5A1_UNORM: pfGuid = GUID_WICPixelFormat16bppBGRA5551; break; - case DXGI_FORMAT_B5G6R5_UNORM: pfGuid = GUID_WICPixelFormat16bppBGR565; break; - case DXGI_FORMAT_R32_FLOAT: pfGuid = GUID_WICPixelFormat32bppGrayFloat; break; - case DXGI_FORMAT_R16_FLOAT: pfGuid = GUID_WICPixelFormat16bppGrayHalf; break; - case DXGI_FORMAT_R16_UNORM: pfGuid = GUID_WICPixelFormat16bppGray; break; - case DXGI_FORMAT_R8_UNORM: pfGuid = GUID_WICPixelFormat8bppGray; break; - case DXGI_FORMAT_A8_UNORM: pfGuid = GUID_WICPixelFormat8bppAlpha; break; - - case DXGI_FORMAT_R8G8B8A8_UNORM: - pfGuid = GUID_WICPixelFormat32bppRGBA; - break; + // Convert the resource to RGBA8 UNORM (using WIC) + std::vector converted(desc.Width * desc.Height * ImageCapture::NumChannels); + D3DCHECK(ImageCapture::ConvertTextureResource(desc, imageSize, dstRowPitch, pData, converted)); - case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: - pfGuid = GUID_WICPixelFormat32bppRGBA; - sRGB = true; - break; + // Write the resource to disk as a PNG file (using STB) + bool result = ImageCapture::CapturePng(file, static_cast(desc.Width), static_cast(desc.Height), converted.data()); - case DXGI_FORMAT_B8G8R8A8_UNORM: - pfGuid = GUID_WICPixelFormat32bppBGRA; - break; + // Unmap the staging texture + pStaging->Unmap(0, nullptr); - case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: - pfGuid = GUID_WICPixelFormat32bppBGRA; - sRGB = true; - break; + // Clean up + SAFE_RELEASE(pStaging); + SAFE_RELEASE(fence); + SAFE_RELEASE(commandList); + SAFE_RELEASE(commandAlloc); - case DXGI_FORMAT_B8G8R8X8_UNORM: - pfGuid = GUID_WICPixelFormat32bppBGR; - break; - - case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: - pfGuid = GUID_WICPixelFormat32bppBGR; - sRGB = true; - break; - - default: - return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); - } - - IWICImagingFactory2* pWIC = _GetWIC(); - IWICBitmap* bitmap = nullptr; - hr = pWIC->CreateBitmapFromMemory(static_cast(desc.Width), desc.Height, pfGuid, - static_cast(dstRowPitch), static_cast(imageSize), - static_cast(pMappedMemory), &bitmap); - - IWICFormatConverter* converter = nullptr; - hr = pWIC->CreateFormatConverter(&converter); - hr = converter->Initialize(bitmap, GUID_WICPixelFormat32bppRGBA, WICBitmapDitherTypeNone, nullptr, 0, WICBitmapPaletteTypeMedianCut); - - WICRect rect = { 0, 0, static_cast(desc.Width), static_cast(desc.Height) }; - converter->CopyPixels(&rect, static_cast(desc.Width * 4), static_cast(converted.size()), converted.data()); - - converter->Release(); - bitmap->Release(); - } - - { - // libpng wants pointers to each row - std::vector rows(desc.Height, nullptr); - for (uint32_t i = 0; i < desc.Height; i++) - { - rows[i] = &converted[desc.Width * 4 * i]; - } - ImageCapture::CapturePng(file, static_cast(desc.Width), static_cast(desc.Height), rows); - } - pStaging->Unmap(0, &writeRange); - - pStaging->Release(); - fence->Release(); - commandList->Release(); - commandAlloc->Release(); - - return true; + return result; } //---------------------------------------------------------------------------------------------------------- @@ -1881,8 +1796,8 @@ namespace Graphics } /** - * Create a D3D12 root signature. - */ + * Create a D3D12 root signature. + */ ID3D12RootSignature* CreateRootSignature(Globals& d3d, const D3D12_ROOT_SIGNATURE_DESC& desc) { ID3DBlob* sig = nullptr; @@ -1907,8 +1822,8 @@ namespace Graphics } /** - * Create a buffer resource. - */ + * Create a buffer resource. + */ bool CreateBuffer(Globals& d3d, const BufferDesc& info, ID3D12Resource** ppResource) { // Describe the upload buffer resource @@ -1942,8 +1857,8 @@ namespace Graphics } /** - * Create a texture resource on the default heap. - */ + * Create a texture resource on the default heap. + */ bool CreateTexture(Globals& d3d, const TextureDesc& info, ID3D12Resource** resource) { // Describe the texture resource @@ -2232,8 +2147,8 @@ namespace Graphics } /* - * Initialize D3D12. - */ + * Initialize D3D12. + */ bool Initialize(const Configs::Config& config, Scenes::Scene& scene, Globals& d3d, Resources& resources, std::ofstream& log) { // Set config variables @@ -2417,8 +2332,8 @@ namespace Graphics } /** - * Reset the command list. - */ + * Reset the command list. + */ bool ResetCmdList(Globals& d3d) { // Reset the command allocator for the current frame @@ -2431,8 +2346,8 @@ namespace Graphics } /** - * Submit the command list. - */ + * Submit the command list. + */ bool SubmitCmdList(Globals& d3d) { // Close the command list @@ -2446,8 +2361,8 @@ namespace Graphics } /** - * Swap the back buffers. - */ + * Swap the back buffers. + */ bool Present(Globals& d3d) { HRESULT hr; @@ -2458,8 +2373,8 @@ namespace Graphics } /* - * Wait for pending GPU work to complete. - */ + * Wait for pending GPU work to complete. + */ bool WaitForGPU(Globals& d3d) { // Increment the fence value @@ -2480,8 +2395,8 @@ namespace Graphics } /** - * Prepare to render the next frame. - */ + * Prepare to render the next frame. + */ bool MoveToNextFrame(Globals& d3d) { // Set the frame index for the next frame @@ -2537,8 +2452,8 @@ namespace Graphics #endif /** - * Release D3D12 resources. - */ + * Release D3D12 resources. + */ void Cleanup(Globals& d3d, GlobalResources& resources) { Cleanup(resources); @@ -2550,9 +2465,7 @@ namespace Graphics */ bool WriteBackBufferToDisk(Globals& d3d, std::string directory) { - CoInitialize(NULL); - bool success = WriteResourceToDisk(d3d, directory + "/backbuffer.png", d3d.backBuffer[d3d.frameIndex], D3D12_RESOURCE_STATE_PRESENT); - return success; + return WriteResourceToDisk(d3d, directory + "/backbuffer.png", d3d.backBuffer[d3d.frameIndex], D3D12_RESOURCE_STATE_PRESENT); } } diff --git a/samples/test-harness/src/ImageCapture.cpp b/samples/test-harness/src/ImageCapture.cpp index 4d20058..3cb5bbb 100644 --- a/samples/test-harness/src/ImageCapture.cpp +++ b/samples/test-harness/src/ImageCapture.cpp @@ -9,65 +9,133 @@ */ #include "ImageCapture.h" -#include -#include +#include "Common.h" + +#if defined(_WIN32) || defined(WIN32) +#define STBI_MSC_SECURE_CRT +#endif + +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include namespace ImageCapture { + /** * Write image data to a PNG format file. */ - bool CapturePng(std::string file, uint32_t width, uint32_t height, std::vector& rows) + bool CapturePng(std::string file, uint32_t width, uint32_t height, const unsigned char* data) + { + int result = stbi_write_png(file.c_str(), width, height, NumChannels, data, width * NumChannels); + return result != 0; + } + +#if defined(_WIN32) || defined(WIN32) + /** + * Create a Windows Image Component (WIC) imaging factory. + */ + IWICImagingFactory2* CreateWICImagingFactory() + { + static INIT_ONCE s_initOnce = INIT_ONCE_STATIC_INIT; + + IWICImagingFactory2* factory = nullptr; + (void)InitOnceExecuteOnce(&s_initOnce, + [](PINIT_ONCE, PVOID, PVOID* ifactory) -> BOOL + { + return SUCCEEDED(CoCreateInstance( + CLSID_WICImagingFactory2, + nullptr, + CLSCTX_INPROC_SERVER, + __uuidof(IWICImagingFactory2), + ifactory)) ? TRUE : FALSE; + }, nullptr, reinterpret_cast(&factory)); + + return factory; + } + + /** + * Convert the data format of a D3D resource using Windows Imaging Component (WIC). + */ + HRESULT ConvertTextureResource( + const D3D12_RESOURCE_DESC desc, + UINT64 imageSize, + UINT64 dstRowPitch, + unsigned char* pMappedMemory, + std::vector& converted) { - #if (defined(_WIN32) || defined(WIN32)) - FILE* fp = nullptr; - errno_t ferror = fopen_s(&fp, file.c_str(), "wb"); - if (ferror != 0) return false; - #elif __linux__ - FILE* fp = fopen(file.c_str(), "wb"); - if (ferror != nullptr) return false; - #endif - - png_structp pngWrite = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if (!pngWrite) return false; - - png_infop pngInfo = png_create_info_struct(pngWrite); - if (!pngInfo) + bool sRGB = false; + WICPixelFormatGUID pfGuid; + + // Determine source format's WIC equivalent + switch (desc.Format) { - png_destroy_write_struct(&pngWrite, (png_infopp)nullptr); - return false; + case DXGI_FORMAT_R32G32B32A32_FLOAT: pfGuid = GUID_WICPixelFormat128bppRGBAFloat; break; + case DXGI_FORMAT_R16G16B16A16_FLOAT: pfGuid = GUID_WICPixelFormat64bppRGBAHalf; break; + case DXGI_FORMAT_R16G16B16A16_UNORM: pfGuid = GUID_WICPixelFormat64bppRGBA; break; + case DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM: pfGuid = GUID_WICPixelFormat32bppRGBA1010102XR; break; + case DXGI_FORMAT_R10G10B10A2_UNORM: pfGuid = GUID_WICPixelFormat32bppRGBA1010102; break; + case DXGI_FORMAT_B5G5R5A1_UNORM: pfGuid = GUID_WICPixelFormat16bppBGRA5551; break; + case DXGI_FORMAT_B5G6R5_UNORM: pfGuid = GUID_WICPixelFormat16bppBGR565; break; + case DXGI_FORMAT_R32_FLOAT: pfGuid = GUID_WICPixelFormat32bppGrayFloat; break; + case DXGI_FORMAT_R16_FLOAT: pfGuid = GUID_WICPixelFormat16bppGrayHalf; break; + case DXGI_FORMAT_R16_UNORM: pfGuid = GUID_WICPixelFormat16bppGray; break; + case DXGI_FORMAT_R8_UNORM: pfGuid = GUID_WICPixelFormat8bppGray; break; + case DXGI_FORMAT_A8_UNORM: pfGuid = GUID_WICPixelFormat8bppAlpha; break; + case DXGI_FORMAT_R8G8B8A8_UNORM: pfGuid = GUID_WICPixelFormat32bppRGBA; break; + case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: pfGuid = GUID_WICPixelFormat32bppRGBA; sRGB = true; break; + case DXGI_FORMAT_B8G8R8A8_UNORM: pfGuid = GUID_WICPixelFormat32bppBGRA; break; + case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: pfGuid = GUID_WICPixelFormat32bppBGRA; sRGB = true; break; + case DXGI_FORMAT_B8G8R8X8_UNORM: pfGuid = GUID_WICPixelFormat32bppBGR; break; + case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: pfGuid = GUID_WICPixelFormat32bppBGR; sRGB = true; break; + default: + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); } - if (setjmp(png_jmpbuf(pngWrite))) + // Create an imaging factory + IWICImagingFactory2* pWIC = CreateWICImagingFactory(); + + // Create a WIC bitmap from the D3D resource + IWICBitmap* bitmap = nullptr; + HRESULT hr = pWIC->CreateBitmapFromMemory( + static_cast(desc.Width), + static_cast(desc.Height), + pfGuid, + static_cast(dstRowPitch), + static_cast(imageSize), + static_cast(pMappedMemory), + &bitmap); + + if(FAILED(hr)) return hr; + + // Create the WIC converter + IWICFormatConverter* converter = nullptr; + hr = pWIC->CreateFormatConverter(&converter); + if(FAILED(hr)) { - png_destroy_write_struct(&pngWrite, &pngInfo); - fclose(fp); - return false; + SAFE_RELEASE(bitmap); + return hr; } - png_init_io(pngWrite, fp); - png_set_IHDR( - pngWrite, - pngInfo, - width, - height, - 8, - PNG_COLOR_TYPE_RGB, - PNG_INTERLACE_NONE, - PNG_COMPRESSION_TYPE_DEFAULT, - PNG_FILTER_TYPE_DEFAULT - ); - png_write_info(pngWrite, pngInfo); - - // Removing the alpha channel for captured images - // too bad if there's useful data there... - png_set_filler(pngWrite, 0, PNG_FILLER_AFTER); - png_write_image(pngWrite, rows.data()); - png_write_end(pngWrite, NULL); - fclose(fp); - - png_destroy_write_struct(&pngWrite, &pngInfo); - - return true; + // Initialize the WIC converter + hr = converter->Initialize(bitmap, GUID_WICPixelFormat32bppRGBA, WICBitmapDitherTypeNone, nullptr, 0, WICBitmapPaletteTypeMedianCut); + if(FAILED(hr)) + { + SAFE_RELEASE(converter); + SAFE_RELEASE(bitmap); + return hr; + } + + // Convert the texels + WICRect rect = { 0, 0, static_cast(desc.Width), static_cast(desc.Height) }; + hr = converter->CopyPixels(&rect, static_cast(desc.Width * 4), static_cast(converted.size()), converted.data()); + + // Clean up + SAFE_RELEASE(converter); + SAFE_RELEASE(bitmap); + + return hr; } -} \ No newline at end of file + +#endif + +} diff --git a/samples/test-harness/src/Vulkan.cpp b/samples/test-harness/src/Vulkan.cpp index 838a4f9..8d538a7 100644 --- a/samples/test-harness/src/Vulkan.cpp +++ b/samples/test-harness/src/Vulkan.cpp @@ -420,8 +420,8 @@ namespace Graphics } /** - * Create the fences. - */ + * Create the fences. + */ bool CreateFences(Globals& vk) { VkFenceCreateInfo fenceCreateInfo = {}; @@ -476,7 +476,7 @@ namespace Graphics surfaceCapabilities.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR ? VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR : surfaceCapabilities.currentTransform; // Get the swap chain's format and color space - // TODO: Get rid of this and use B8G8R8A8Unorm and SrgbNonlinear? + // TODO: use B8G8R8A8Unorm and SrgbNonlinear? if (!GetSwapChainFormatAndColorSpace(vk.physicalDevice, vk.surface, &vk.swapChainFormat, &vk.swapChainColorSpace)) return false; // Describe the swap chain @@ -500,9 +500,16 @@ namespace Graphics if (surfaceCapabilities.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT) { + // Allow the back buffer to be a copy destination swapchainCreateInfo.imageUsage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; } + if (surfaceCapabilities.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_SRC_BIT) + { + // Allow the back buffer to be a copy source + swapchainCreateInfo.imageUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + } + // Create the swap chain VKCHECK(vkCreateSwapchainKHR(vk.device, &swapchainCreateInfo, nullptr, &vk.swapChain)); #ifdef GFX_NAME_OBJECTS @@ -558,8 +565,8 @@ namespace Graphics } /** - * Create the render pass. - */ + * Create the render pass. + */ bool CreateRenderPass(Globals& vk) { // Describe the render pass @@ -599,8 +606,8 @@ namespace Graphics } /** - * Create the frame buffers. - */ + * Create the frame buffers. + */ bool CreateFrameBuffers(Globals& vk) { for (uint32_t bufferIndex = 0; bufferIndex < 2; bufferIndex++) @@ -626,8 +633,8 @@ namespace Graphics } /** - * Create the command pool. - */ + * Create the command pool. + */ bool CreateCommandPool(Globals& vk) { // Describe the command pool @@ -645,8 +652,8 @@ namespace Graphics } /** - * Create the command buffers. - */ + * Create the command buffers. + */ bool CreateCommandBuffers(Globals& vk) { uint32_t numCommandBuffers = 2; @@ -780,8 +787,8 @@ namespace Graphics } /** - * Create the samplers. - */ + * Create the samplers. + */ bool CreateSamplers(Globals& vk, Resources& resources) { // Describe a bilinear sampler @@ -1360,8 +1367,8 @@ namespace Graphics } /** - * Create the shared render targets. - */ + * Create the shared render targets. + */ bool CreateRenderTargets(Globals& vk, Resources& resources) { // Create the GBufferA (R8G8B8A8_UNORM) texture resource @@ -1436,8 +1443,8 @@ namespace Graphics } /** - * Release Vulkan resources. - */ + * Release Vulkan resources. + */ void Cleanup(VkDevice& device, Resources& resources) { // Buffers @@ -1541,8 +1548,8 @@ namespace Graphics } /** - * Release core Vulkan resources. - */ + * Release core Vulkan resources. + */ void Cleanup(Globals& vk) { uint32_t resourceIndex; @@ -1930,8 +1937,8 @@ namespace Graphics } /** - * Create the scene textures. - */ + * Create the scene textures. + */ bool CreateSceneTextures(Globals& vk, Resources &resources, const Scenes::Scene &scene, std::ofstream& log) { // Early out if there are no scene textures @@ -1997,47 +2004,51 @@ namespace Graphics */ bool WriteResourceToDisk(Globals& vk, std::string file, VkImage image, uint32_t width, uint32_t height, VkFormat imageFormat, VkImageLayout originalLayout) { - VkCommandPool pool; - VkCommandBuffer cmd; + bool result = false; + VkCommandPool commandPool = nullptr; + VkCommandBuffer commandBuffer = nullptr; + VkImage linearScreenshotImage = nullptr; + VkImage optimalScreenshotImage = nullptr; + VkDeviceMemory linearScreenshotImageMemory; + VkDeviceMemory optimalScreenshotImageMemory; - // create custom command pool + // Create a command pool { VkCommandPoolCreateInfo commandPoolCreateInfo = {}; commandPoolCreateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; commandPoolCreateInfo.queueFamilyIndex = vk.queueFamilyIndex; commandPoolCreateInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - VKCHECK(vkCreateCommandPool(vk.device, &commandPoolCreateInfo, nullptr, &pool)); -#ifdef GFX_NAME_OBJECTS - SetObjectName(vk.device, reinterpret_cast(pool), "Image capture Command Pool", VK_OBJECT_TYPE_COMMAND_POOL); -#endif + VKCHECK(vkCreateCommandPool(vk.device, &commandPoolCreateInfo, nullptr, &commandPool)); + #ifdef GFX_NAME_OBJECTS + SetObjectName(vk.device, reinterpret_cast(commandPool), "Image Capture Command Pool", VK_OBJECT_TYPE_COMMAND_POOL); + #endif } - // create custom command buffer + // Create and begin the command buffer { uint32_t numCommandBuffers = 1; VkCommandBufferAllocateInfo commandBufferAllocateInfo = {}; commandBufferAllocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; commandBufferAllocateInfo.commandBufferCount = numCommandBuffers; - commandBufferAllocateInfo.commandPool = pool; + commandBufferAllocateInfo.commandPool = commandPool; commandBufferAllocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - VKCHECK(vkAllocateCommandBuffers(vk.device, &commandBufferAllocateInfo, &cmd)); + VKCHECK(vkAllocateCommandBuffers(vk.device, &commandBufferAllocateInfo, &commandBuffer)); + #ifdef GFX_NAME_OBJECTS + SetObjectName(vk.device, reinterpret_cast(commandBuffer), "Image capture Command Buffer", VK_OBJECT_TYPE_COMMAND_BUFFER); + #endif -#ifdef GFX_NAME_OBJECTS - SetObjectName(vk.device, reinterpret_cast(cmd), "Image capture Command Buffer", VK_OBJECT_TYPE_COMMAND_BUFFER); -#endif + // Begin the command buffer VkCommandBufferBeginInfo commandBufferBeginInfo = {}; commandBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - VKCHECK(vkBeginCommandBuffer(cmd, &commandBufferBeginInfo)); + VKCHECK(vkBeginCommandBuffer(commandBuffer, &commandBufferBeginInfo)); } - // using vr_sli_vk demo for reference - VkImage linearScreenshotImage, optimalScreenshotImage; - VkDeviceMemory linearScreenshotImageMemory, optimalScreenshotImageMemory; + // Create intermediate texture resources { - // same as CreateTexture() but don't create a view + // Describe the image VkImageCreateInfo imageCreateInfo = {}; imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; @@ -2053,26 +2064,35 @@ namespace Graphics imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + // Create the image (linear layout) VKCHECK(vkCreateImage(vk.device, &imageCreateInfo, nullptr, &linearScreenshotImage)); + + // Create the image (optimal tiling) imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; imageCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; VKCHECK(vkCreateImage(vk.device, &imageCreateInfo, nullptr, &optimalScreenshotImage)); + // Get the memory requirements for the linear image AllocateMemoryDesc desc = {}; vkGetImageMemoryRequirements(vk.device, linearScreenshotImage, &desc.requirements); desc.properties = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; desc.flags = 0; + // Allocate and bind the memory for the linear image if (!AllocateMemory(vk, desc, &linearScreenshotImageMemory)) return false; VKCHECK(vkBindImageMemory(vk.device, linearScreenshotImage, linearScreenshotImageMemory, 0)); + // Get the memory requirements for the optimal tiled image vkGetImageMemoryRequirements(vk.device, optimalScreenshotImage, &desc.requirements); desc.properties = 0; desc.flags = 0; + + // Allocate and bind the memory for the optimal tiled image if (!AllocateMemory(vk, desc, &optimalScreenshotImageMemory)) return false; VKCHECK(vkBindImageMemory(vk.device, optimalScreenshotImage, optimalScreenshotImageMemory, 0)); } + // Barriers { ImageBarrierDesc barrier = { @@ -2082,15 +2102,19 @@ namespace Graphics VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 } }; - SetImageMemoryBarrier(cmd, linearScreenshotImage, barrier); - SetImageMemoryBarrier(cmd, optimalScreenshotImage, barrier); + + // Transition the intermediate images to copy destinations + SetImageMemoryBarrier(commandBuffer, linearScreenshotImage, barrier); + SetImageMemoryBarrier(commandBuffer, optimalScreenshotImage, barrier); + + // Transition the source image to a copy source barrier.oldLayout = originalLayout; barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - SetImageMemoryBarrier(cmd, image, barrier); + SetImageMemoryBarrier(commandBuffer, image, barrier); } + // Copy the source image to the optimal tiled image { - // blit image (format conversion if necessary) VkImageBlit region = {}; VkImageSubresourceLayers subres = {}; subres.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; @@ -2103,9 +2127,17 @@ namespace Graphics region.srcOffsets[1] = { (int32_t) width, (int32_t) height, 1 }; region.dstOffsets[0] = {}; region.dstOffsets[1] = { (int32_t) width, (int32_t) height, 1 }; - vkCmdBlitImage(cmd, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, optimalScreenshotImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion, VK_FILTER_NEAREST); + + vkCmdBlitImage( + commandBuffer, + image, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + optimalScreenshotImage, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, ®ion, VK_FILTER_NEAREST); } + // Barriers { ImageBarrierDesc barrier = { @@ -2115,19 +2147,24 @@ namespace Graphics VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 } }; - SetImageMemoryBarrier(cmd, optimalScreenshotImage, barrier); + + // Transition the optimal tiled image to a copy source + SetImageMemoryBarrier(commandBuffer, optimalScreenshotImage, barrier); + + // Transition the source image to a copy source barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; barrier.newLayout = originalLayout; - SetImageMemoryBarrier(cmd, image, barrier); + SetImageMemoryBarrier(commandBuffer, image, barrier); } + // Copy optimal tiled image to linear image (for CPU copy) { - // copy to linear tiling image for CPU copy VkImageSubresourceLayers subResource = {}; subResource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; subResource.baseArrayLayer = 0; subResource.mipLevel = 0; subResource.layerCount = 1; + VkImageCopy region = {}; region.srcSubresource = subResource; region.dstSubresource = subResource; @@ -2136,16 +2173,17 @@ namespace Graphics region.extent.width = width; region.extent.height = height; region.extent.depth = 1; + vkCmdCopyImage( - cmd, + commandBuffer, optimalScreenshotImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, linearScreenshotImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - 1, ®ion - ); + 1, ®ion); } + // Transition the linear image to general read { ImageBarrierDesc barrier = { @@ -2155,17 +2193,17 @@ namespace Graphics VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 } }; - SetImageMemoryBarrier(cmd, linearScreenshotImage, barrier); + SetImageMemoryBarrier(commandBuffer, linearScreenshotImage, barrier); } + // Execute GPU work { - // Execute GPU work to finish initialization - VKCHECK(vkEndCommandBuffer(cmd)); + VKCHECK(vkEndCommandBuffer(commandBuffer)); VkSubmitInfo submitInfo = {}; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &cmd; + submitInfo.pCommandBuffers = &commandBuffer; VKCHECK(vkQueueSubmit(vk.queue, 1, &submitInfo, VK_NULL_HANDLE)); VKCHECK(vkQueueWaitIdle(vk.queue)); @@ -2173,45 +2211,39 @@ namespace Graphics WaitForGPU(vk); } - // copy screenshot image to cpu-side memory + // Copy the linear image to CPU memory { - unsigned char* rawData = nullptr; - VkImageSubresource subResource{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 0 }; VkSubresourceLayout subResourceLayout; vkGetImageSubresourceLayout(vk.device, linearScreenshotImage, &subResource, &subResourceLayout); - VkResult result = vkMapMemory(vk.device, linearScreenshotImageMemory, 0, VK_WHOLE_SIZE, 0, (void**)&rawData); - if (result != VK_SUCCESS) - { - return false; - } + // Map the linear image memory + unsigned char* pData = nullptr; + VKCHECK(vkMapMemory(vk.device, linearScreenshotImageMemory, 0, VK_WHOLE_SIZE, 0, (void**)&pData)); - // libpng wants pointers to each row - std::vector rows(height, nullptr); - for (uint32_t i = 0; i < height; i++) - { - rows[i] = rawData + i * subResourceLayout.rowPitch; - } + // Copy linear image to CPU memory + std::vector converted(width * height * ImageCapture::NumChannels); + memcpy(converted.data(), pData, converted.size()); - // output the image file to disk - ImageCapture::CapturePng(file, width, height, rows); + // Write the resource to disk as a PNG file (using STB) + result = ImageCapture::CapturePng(file, width, height, converted.data()); + // Unmap the linear image memory vkUnmapMemory(vk.device, linearScreenshotImageMemory); } + // Clean up { - // tear-down screenshot images vkFreeMemory(vk.device, linearScreenshotImageMemory, nullptr); vkDestroyImage(vk.device, linearScreenshotImage, nullptr); vkFreeMemory(vk.device, optimalScreenshotImageMemory, nullptr); vkDestroyImage(vk.device, optimalScreenshotImage, nullptr); - // tear-down temporary command list and pool - vkFreeCommandBuffers(vk.device, pool, 1, &cmd); - vkDestroyCommandPool(vk.device, pool, nullptr); + + vkFreeCommandBuffers(vk.device, commandPool, 1, &commandBuffer); + vkDestroyCommandPool(vk.device, commandPool, nullptr); } - return true; + return result; } #ifdef GFX_NAME_OBJECTS @@ -2844,7 +2876,7 @@ namespace Graphics vkCmdBeginRenderPass(vk.cmdBuffer[vk.frameIndex], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); } - /* + /** * Initialize Vulkan. */ bool Initialize(const Configs::Config& config, Scenes::Scene& scene, Globals& vk, Resources& resources, std::ofstream& log) @@ -3007,6 +3039,9 @@ namespace Graphics vk.scissor.extent.width = vk.width; vk.scissor.extent.height = vk.height; + // Wait for the GPU to finish up any work + VKCHECK(vkDeviceWaitIdle(vk.device)); + // Release the swapchain and associated resources CleanupSwapchain(vk); @@ -3081,8 +3116,8 @@ namespace Graphics } /** - * Reset the command list. - */ + * Reset the command list. + */ bool ResetCmdList(Globals& vk) { // Start the command buffer for the next frame @@ -3093,8 +3128,8 @@ namespace Graphics } /** - * Submit the command list. - */ + * Submit the command list. + */ bool SubmitCmdList(Globals& vk) { // Close the command buffer @@ -3119,8 +3154,8 @@ namespace Graphics } /** - * Swap the back buffers. - */ + * Swap the back buffers. + */ bool Present(Globals& vk) { // Present @@ -3143,16 +3178,16 @@ namespace Graphics } /* - * Wait for pending GPU work to complete. - */ + * Wait for pending GPU work to complete. + */ bool WaitForGPU(Globals& vk) { return (vkDeviceWaitIdle(vk.device) == VK_SUCCESS); } /** - * Prepare to render the next frame. - */ + * Prepare to render the next frame. + */ bool MoveToNextFrame(Globals& vk) { if (vk.vsyncChanged) @@ -3261,9 +3296,6 @@ namespace Graphics */ bool WriteBackBufferToDisk(Globals& vk, std::string directory) { - #if (defined(_WIN32) || defined(WIN32)) - CoInitialize(NULL); - #endif return WriteResourceToDisk(vk, directory + "/backbuffer.png", vk.swapChainImage[vk.frameIndex], vk.width, vk.height, vk.swapChainFormat, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); } diff --git a/samples/test-harness/src/main.cpp b/samples/test-harness/src/main.cpp index 5ceddb1..0b28d22 100644 --- a/samples/test-harness/src/main.cpp +++ b/samples/test-harness/src/main.cpp @@ -35,11 +35,7 @@ void WriteImages( Graphics::RTAO::Resources& rtao, Graphics::DDGI::Resources& ddgi) { -#if defined(_WIN32) || defined(WIN32) - CreateDirectory(config.scene.screenshotPath.c_str(), NULL); -#elif __linux__ std::filesystem::create_directories(config.scene.screenshotPath.c_str()); -#endif Graphics::WriteBackBufferToDisk(gfx, config.scene.screenshotPath); Graphics::GBuffer::WriteGBufferToDisk(gfx, gfxResources, config.scene.screenshotPath); @@ -210,6 +206,13 @@ int Run(const std::vector& arguments) int width, height; glfwGetFramebufferSize(gfx.window, &width, &height); + // Wait for the window to have valid dimensions + while(width == 0 || height == 0) + { + glfwGetFramebufferSize(gfx.window, &width, &height); + glfwWaitEvents(); + } + // Resize all screen-space buffers if (!Graphics::Resize(gfx, gfxResources, width, height, log)) break; // Back buffers and GBuffer textures if (!Graphics::PathTracing::Resize(gfx, gfxResources, pt, log)) break; // PT Output and Accumulation diff --git a/samples/test-harness/src/thirdparty/directxtk/ScreenGrab12.cpp b/samples/test-harness/src/thirdparty/directxtk/ScreenGrab12.cpp deleted file mode 100644 index 4faffd2..0000000 --- a/samples/test-harness/src/thirdparty/directxtk/ScreenGrab12.cpp +++ /dev/null @@ -1,1356 +0,0 @@ -//-------------------------------------------------------------------------------------- -// File: ScreenGrab12.cpp -// -// Function for capturing a 2D texture and saving it to a file (aka a 'screenshot' -// when used on a Direct3D 12 Render Target). -// -// Note these functions are useful as a light-weight runtime screen grabber. For -// full-featured texture capture, DDS writer, and texture processing pipeline, -// see the 'Texconv' sample and the 'DirectXTex' library. -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -// -// http://go.microsoft.com/fwlink/?LinkId=248926 -// http://go.microsoft.com/fwlink/?LinkID=615561 -//-------------------------------------------------------------------------------------- - -// Does not capture 1D textures or 3D textures (volume maps) - -// Does not capture mipmap chains, only the top-most texture level is saved - -// For 2D array textures and cubemaps, it captures only the first image in the array - -#include "ScreenGrab12.h" - -#include -#include -#include - -#include - -#include - -#include "d3dx12.h" - -using Microsoft::WRL::ComPtr; - -//-------------------------------------------------------------------------------------- -// Macros -//-------------------------------------------------------------------------------------- -#ifndef MAKEFOURCC -#define MAKEFOURCC(ch0, ch1, ch2, ch3) \ - ((uint32_t)(uint8_t)(ch0) | ((uint32_t)(uint8_t)(ch1) << 8) | \ - ((uint32_t)(uint8_t)(ch2) << 16) | ((uint32_t)(uint8_t)(ch3) << 24 )) -#endif /* defined(MAKEFOURCC) */ - -//-------------------------------------------------------------------------------------- -// DDS file structure definitions -// -// See DDS.h in the 'Texconv' sample and the 'DirectXTex' library -//-------------------------------------------------------------------------------------- -namespace -{ - #pragma pack(push,1) - - #define DDS_MAGIC 0x20534444 // "DDS " - - struct DDS_PIXELFORMAT - { - uint32_t size; - uint32_t flags; - uint32_t fourCC; - uint32_t RGBBitCount; - uint32_t RBitMask; - uint32_t GBitMask; - uint32_t BBitMask; - uint32_t ABitMask; - }; - - #define DDS_FOURCC 0x00000004 // DDPF_FOURCC - #define DDS_RGB 0x00000040 // DDPF_RGB - #define DDS_RGBA 0x00000041 // DDPF_RGB | DDPF_ALPHAPIXELS - #define DDS_LUMINANCE 0x00020000 // DDPF_LUMINANCE - #define DDS_LUMINANCEA 0x00020001 // DDPF_LUMINANCE | DDPF_ALPHAPIXELS - #define DDS_ALPHA 0x00000002 // DDPF_ALPHA - #define DDS_BUMPDUDV 0x00080000 // DDPF_BUMPDUDV - - #define DDS_HEADER_FLAGS_TEXTURE 0x00001007 // DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT - #define DDS_HEADER_FLAGS_MIPMAP 0x00020000 // DDSD_MIPMAPCOUNT - #define DDS_HEADER_FLAGS_PITCH 0x00000008 // DDSD_PITCH - #define DDS_HEADER_FLAGS_LINEARSIZE 0x00080000 // DDSD_LINEARSIZE - - #define DDS_HEIGHT 0x00000002 // DDSD_HEIGHT - #define DDS_WIDTH 0x00000004 // DDSD_WIDTH - - #define DDS_SURFACE_FLAGS_TEXTURE 0x00001000 // DDSCAPS_TEXTURE - - typedef struct - { - uint32_t size; - uint32_t flags; - uint32_t height; - uint32_t width; - uint32_t pitchOrLinearSize; - uint32_t depth; // only if DDS_HEADER_FLAGS_VOLUME is set in flags - uint32_t mipMapCount; - uint32_t reserved1[11]; - DDS_PIXELFORMAT ddspf; - uint32_t caps; - uint32_t caps2; - uint32_t caps3; - uint32_t caps4; - uint32_t reserved2; - } DDS_HEADER; - - typedef struct - { - DXGI_FORMAT dxgiFormat; - uint32_t resourceDimension; - uint32_t miscFlag; // see D3D11_RESOURCE_MISC_FLAG - uint32_t arraySize; - uint32_t reserved; - } DDS_HEADER_DXT10; - - #pragma pack(pop) - - const DDS_PIXELFORMAT DDSPF_DXT1 = - { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('D','X','T','1'), 0, 0, 0, 0, 0 }; - - const DDS_PIXELFORMAT DDSPF_DXT3 = - { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('D','X','T','3'), 0, 0, 0, 0, 0 }; - - const DDS_PIXELFORMAT DDSPF_DXT5 = - { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('D','X','T','5'), 0, 0, 0, 0, 0 }; - - const DDS_PIXELFORMAT DDSPF_BC4_UNORM = - { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('B','C','4','U'), 0, 0, 0, 0, 0 }; - - const DDS_PIXELFORMAT DDSPF_BC4_SNORM = - { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('B','C','4','S'), 0, 0, 0, 0, 0 }; - - const DDS_PIXELFORMAT DDSPF_BC5_UNORM = - { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('B','C','5','U'), 0, 0, 0, 0, 0 }; - - const DDS_PIXELFORMAT DDSPF_BC5_SNORM = - { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('B','C','5','S'), 0, 0, 0, 0, 0 }; - - const DDS_PIXELFORMAT DDSPF_R8G8_B8G8 = - { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('R','G','B','G'), 0, 0, 0, 0, 0 }; - - const DDS_PIXELFORMAT DDSPF_G8R8_G8B8 = - { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('G','R','G','B'), 0, 0, 0, 0, 0 }; - - const DDS_PIXELFORMAT DDSPF_YUY2 = - { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('Y','U','Y','2'), 0, 0, 0, 0, 0 }; - - const DDS_PIXELFORMAT DDSPF_A8R8G8B8 = - { sizeof(DDS_PIXELFORMAT), DDS_RGBA, 0, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000 }; - - const DDS_PIXELFORMAT DDSPF_X8R8G8B8 = - { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000 }; - - const DDS_PIXELFORMAT DDSPF_A8B8G8R8 = - { sizeof(DDS_PIXELFORMAT), DDS_RGBA, 0, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000 }; - - const DDS_PIXELFORMAT DDSPF_G16R16 = - { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 32, 0x0000ffff, 0xffff0000, 0x00000000, 0x00000000 }; - - const DDS_PIXELFORMAT DDSPF_R5G6B5 = - { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 16, 0x0000f800, 0x000007e0, 0x0000001f, 0x00000000 }; - - const DDS_PIXELFORMAT DDSPF_A1R5G5B5 = - { sizeof(DDS_PIXELFORMAT), DDS_RGBA, 0, 16, 0x00007c00, 0x000003e0, 0x0000001f, 0x00008000 }; - - const DDS_PIXELFORMAT DDSPF_A4R4G4B4 = - { sizeof(DDS_PIXELFORMAT), DDS_RGBA, 0, 16, 0x00000f00, 0x000000f0, 0x0000000f, 0x0000f000 }; - - const DDS_PIXELFORMAT DDSPF_L8 = - { sizeof(DDS_PIXELFORMAT), DDS_LUMINANCE, 0, 8, 0xff, 0x00, 0x00, 0x00 }; - - const DDS_PIXELFORMAT DDSPF_L16 = - { sizeof(DDS_PIXELFORMAT), DDS_LUMINANCE, 0, 16, 0xffff, 0x0000, 0x0000, 0x0000 }; - - const DDS_PIXELFORMAT DDSPF_A8L8 = - { sizeof(DDS_PIXELFORMAT), DDS_LUMINANCEA, 0, 16, 0x00ff, 0x0000, 0x0000, 0xff00 }; - - const DDS_PIXELFORMAT DDSPF_A8 = - { sizeof(DDS_PIXELFORMAT), DDS_ALPHA, 0, 8, 0x00, 0x00, 0x00, 0xff }; - - const DDS_PIXELFORMAT DDSPF_V8U8 = - { sizeof(DDS_PIXELFORMAT), DDS_BUMPDUDV, 0, 16, 0x00ff, 0xff00, 0x0000, 0x0000 }; - - const DDS_PIXELFORMAT DDSPF_Q8W8V8U8 = - { sizeof(DDS_PIXELFORMAT), DDS_BUMPDUDV, 0, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000 }; - - const DDS_PIXELFORMAT DDSPF_V16U16 = - { sizeof(DDS_PIXELFORMAT), DDS_BUMPDUDV, 0, 32, 0x0000ffff, 0xffff0000, 0x00000000, 0x00000000 }; - - // DXGI_FORMAT_R10G10B10A2_UNORM should be written using DX10 extension to avoid D3DX 10:10:10:2 reversal issue - - // This indicates the DDS_HEADER_DXT10 extension is present (the format is in dxgiFormat) - const DDS_PIXELFORMAT DDSPF_DX10 = - { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('D','X','1','0'), 0, 0, 0, 0, 0 }; - - //----------------------------------------------------------------------------- - struct handle_closer { void operator()(HANDLE h) { if (h) CloseHandle(h); } }; - - typedef std::unique_ptr ScopedHandle; - - inline HANDLE safe_handle( HANDLE h ) { return (h == INVALID_HANDLE_VALUE) ? nullptr : h; } - - class auto_delete_file - { - public: - auto_delete_file(HANDLE hFile) : m_handle(hFile) {} - ~auto_delete_file() - { - if (m_handle) - { - FILE_DISPOSITION_INFO info = {}; - info.DeleteFile = TRUE; - (void)SetFileInformationByHandle(m_handle, FileDispositionInfo, &info, sizeof(info)); - } - } - - void clear() { m_handle = 0; } - - private: - HANDLE m_handle; - - auto_delete_file(const auto_delete_file&) = delete; - auto_delete_file& operator=(const auto_delete_file&) = delete; - }; - - class auto_delete_file_wic - { - public: - auto_delete_file_wic(ComPtr& hFile, LPCWSTR szFile) : m_handle(hFile), m_filename(szFile) {} - ~auto_delete_file_wic() - { - if (m_filename) - { - m_handle.Reset(); - DeleteFileW(m_filename); - } - } - - void clear() { m_filename = 0; } - - private: - LPCWSTR m_filename; - ComPtr& m_handle; - - auto_delete_file_wic(const auto_delete_file_wic&) = delete; - auto_delete_file_wic& operator=(const auto_delete_file_wic&) = delete; - }; - - //-------------------------------------------------------------------------------------- - // Return the BPP for a particular format - //-------------------------------------------------------------------------------------- - size_t BitsPerPixel( _In_ DXGI_FORMAT fmt ) - { - switch( fmt ) - { - case DXGI_FORMAT_R32G32B32A32_TYPELESS: - case DXGI_FORMAT_R32G32B32A32_FLOAT: - case DXGI_FORMAT_R32G32B32A32_UINT: - case DXGI_FORMAT_R32G32B32A32_SINT: - return 128; - - case DXGI_FORMAT_R32G32B32_TYPELESS: - case DXGI_FORMAT_R32G32B32_FLOAT: - case DXGI_FORMAT_R32G32B32_UINT: - case DXGI_FORMAT_R32G32B32_SINT: - return 96; - - case DXGI_FORMAT_R16G16B16A16_TYPELESS: - case DXGI_FORMAT_R16G16B16A16_FLOAT: - case DXGI_FORMAT_R16G16B16A16_UNORM: - case DXGI_FORMAT_R16G16B16A16_UINT: - case DXGI_FORMAT_R16G16B16A16_SNORM: - case DXGI_FORMAT_R16G16B16A16_SINT: - case DXGI_FORMAT_R32G32_TYPELESS: - case DXGI_FORMAT_R32G32_FLOAT: - case DXGI_FORMAT_R32G32_UINT: - case DXGI_FORMAT_R32G32_SINT: - case DXGI_FORMAT_R32G8X24_TYPELESS: - case DXGI_FORMAT_D32_FLOAT_S8X24_UINT: - case DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS: - case DXGI_FORMAT_X32_TYPELESS_G8X24_UINT: - case DXGI_FORMAT_Y416: - case DXGI_FORMAT_Y210: - case DXGI_FORMAT_Y216: - return 64; - - case DXGI_FORMAT_R10G10B10A2_TYPELESS: - case DXGI_FORMAT_R10G10B10A2_UNORM: - case DXGI_FORMAT_R10G10B10A2_UINT: - case DXGI_FORMAT_R11G11B10_FLOAT: - case DXGI_FORMAT_R8G8B8A8_TYPELESS: - case DXGI_FORMAT_R8G8B8A8_UNORM: - case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: - case DXGI_FORMAT_R8G8B8A8_UINT: - case DXGI_FORMAT_R8G8B8A8_SNORM: - case DXGI_FORMAT_R8G8B8A8_SINT: - case DXGI_FORMAT_R16G16_TYPELESS: - case DXGI_FORMAT_R16G16_FLOAT: - case DXGI_FORMAT_R16G16_UNORM: - case DXGI_FORMAT_R16G16_UINT: - case DXGI_FORMAT_R16G16_SNORM: - case DXGI_FORMAT_R16G16_SINT: - case DXGI_FORMAT_R32_TYPELESS: - case DXGI_FORMAT_D32_FLOAT: - case DXGI_FORMAT_R32_FLOAT: - case DXGI_FORMAT_R32_UINT: - case DXGI_FORMAT_R32_SINT: - case DXGI_FORMAT_R24G8_TYPELESS: - case DXGI_FORMAT_D24_UNORM_S8_UINT: - case DXGI_FORMAT_R24_UNORM_X8_TYPELESS: - case DXGI_FORMAT_X24_TYPELESS_G8_UINT: - case DXGI_FORMAT_R9G9B9E5_SHAREDEXP: - case DXGI_FORMAT_R8G8_B8G8_UNORM: - case DXGI_FORMAT_G8R8_G8B8_UNORM: - case DXGI_FORMAT_B8G8R8A8_UNORM: - case DXGI_FORMAT_B8G8R8X8_UNORM: - case DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM: - case DXGI_FORMAT_B8G8R8A8_TYPELESS: - case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: - case DXGI_FORMAT_B8G8R8X8_TYPELESS: - case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: - case DXGI_FORMAT_AYUV: - case DXGI_FORMAT_Y410: - case DXGI_FORMAT_YUY2: - return 32; - - case DXGI_FORMAT_P010: - case DXGI_FORMAT_P016: - case DXGI_FORMAT_V408: - return 24; - - case DXGI_FORMAT_R8G8_TYPELESS: - case DXGI_FORMAT_R8G8_UNORM: - case DXGI_FORMAT_R8G8_UINT: - case DXGI_FORMAT_R8G8_SNORM: - case DXGI_FORMAT_R8G8_SINT: - case DXGI_FORMAT_R16_TYPELESS: - case DXGI_FORMAT_R16_FLOAT: - case DXGI_FORMAT_D16_UNORM: - case DXGI_FORMAT_R16_UNORM: - case DXGI_FORMAT_R16_UINT: - case DXGI_FORMAT_R16_SNORM: - case DXGI_FORMAT_R16_SINT: - case DXGI_FORMAT_B5G6R5_UNORM: - case DXGI_FORMAT_B5G5R5A1_UNORM: - case DXGI_FORMAT_A8P8: - case DXGI_FORMAT_B4G4R4A4_UNORM: - case DXGI_FORMAT_P208: - case DXGI_FORMAT_V208: - return 16; - - case DXGI_FORMAT_NV12: - case DXGI_FORMAT_420_OPAQUE: - case DXGI_FORMAT_NV11: - return 12; - - case DXGI_FORMAT_R8_TYPELESS: - case DXGI_FORMAT_R8_UNORM: - case DXGI_FORMAT_R8_UINT: - case DXGI_FORMAT_R8_SNORM: - case DXGI_FORMAT_R8_SINT: - case DXGI_FORMAT_A8_UNORM: - case DXGI_FORMAT_AI44: - case DXGI_FORMAT_IA44: - case DXGI_FORMAT_P8: - return 8; - - case DXGI_FORMAT_R1_UNORM: - return 1; - - case DXGI_FORMAT_BC1_TYPELESS: - case DXGI_FORMAT_BC1_UNORM: - case DXGI_FORMAT_BC1_UNORM_SRGB: - case DXGI_FORMAT_BC4_TYPELESS: - case DXGI_FORMAT_BC4_UNORM: - case DXGI_FORMAT_BC4_SNORM: - return 4; - - case DXGI_FORMAT_BC2_TYPELESS: - case DXGI_FORMAT_BC2_UNORM: - case DXGI_FORMAT_BC2_UNORM_SRGB: - case DXGI_FORMAT_BC3_TYPELESS: - case DXGI_FORMAT_BC3_UNORM: - case DXGI_FORMAT_BC3_UNORM_SRGB: - case DXGI_FORMAT_BC5_TYPELESS: - case DXGI_FORMAT_BC5_UNORM: - case DXGI_FORMAT_BC5_SNORM: - case DXGI_FORMAT_BC6H_TYPELESS: - case DXGI_FORMAT_BC6H_UF16: - case DXGI_FORMAT_BC6H_SF16: - case DXGI_FORMAT_BC7_TYPELESS: - case DXGI_FORMAT_BC7_UNORM: - case DXGI_FORMAT_BC7_UNORM_SRGB: - return 8; - - default: - return 0; - } - } - - - //-------------------------------------------------------------------------------------- - // Determines if the format is block compressed - //-------------------------------------------------------------------------------------- - bool IsCompressed( _In_ DXGI_FORMAT fmt ) - { - switch ( fmt ) - { - case DXGI_FORMAT_BC1_TYPELESS: - case DXGI_FORMAT_BC1_UNORM: - case DXGI_FORMAT_BC1_UNORM_SRGB: - case DXGI_FORMAT_BC2_TYPELESS: - case DXGI_FORMAT_BC2_UNORM: - case DXGI_FORMAT_BC2_UNORM_SRGB: - case DXGI_FORMAT_BC3_TYPELESS: - case DXGI_FORMAT_BC3_UNORM: - case DXGI_FORMAT_BC3_UNORM_SRGB: - case DXGI_FORMAT_BC4_TYPELESS: - case DXGI_FORMAT_BC4_UNORM: - case DXGI_FORMAT_BC4_SNORM: - case DXGI_FORMAT_BC5_TYPELESS: - case DXGI_FORMAT_BC5_UNORM: - case DXGI_FORMAT_BC5_SNORM: - case DXGI_FORMAT_BC6H_TYPELESS: - case DXGI_FORMAT_BC6H_UF16: - case DXGI_FORMAT_BC6H_SF16: - case DXGI_FORMAT_BC7_TYPELESS: - case DXGI_FORMAT_BC7_UNORM: - case DXGI_FORMAT_BC7_UNORM_SRGB: - return true; - - default: - return false; - } - } - - - //-------------------------------------------------------------------------------------- - // Get surface information for a particular format - //-------------------------------------------------------------------------------------- - HRESULT GetSurfaceInfo( - _In_ size_t width, - _In_ size_t height, - _In_ DXGI_FORMAT fmt, - _Out_opt_ size_t* outNumBytes, - _Out_opt_ size_t* outRowBytes, - _Out_opt_ size_t* outNumRows) - { - uint64_t numBytes = 0; - uint64_t rowBytes = 0; - uint64_t numRows = 0; - - bool bc = false; - bool packed = false; - bool planar = false; - size_t bpe = 0; - switch (fmt) - { - case DXGI_FORMAT_BC1_TYPELESS: - case DXGI_FORMAT_BC1_UNORM: - case DXGI_FORMAT_BC1_UNORM_SRGB: - case DXGI_FORMAT_BC4_TYPELESS: - case DXGI_FORMAT_BC4_UNORM: - case DXGI_FORMAT_BC4_SNORM: - bc = true; - bpe = 8; - break; - - case DXGI_FORMAT_BC2_TYPELESS: - case DXGI_FORMAT_BC2_UNORM: - case DXGI_FORMAT_BC2_UNORM_SRGB: - case DXGI_FORMAT_BC3_TYPELESS: - case DXGI_FORMAT_BC3_UNORM: - case DXGI_FORMAT_BC3_UNORM_SRGB: - case DXGI_FORMAT_BC5_TYPELESS: - case DXGI_FORMAT_BC5_UNORM: - case DXGI_FORMAT_BC5_SNORM: - case DXGI_FORMAT_BC6H_TYPELESS: - case DXGI_FORMAT_BC6H_UF16: - case DXGI_FORMAT_BC6H_SF16: - case DXGI_FORMAT_BC7_TYPELESS: - case DXGI_FORMAT_BC7_UNORM: - case DXGI_FORMAT_BC7_UNORM_SRGB: - bc = true; - bpe = 16; - break; - - case DXGI_FORMAT_R8G8_B8G8_UNORM: - case DXGI_FORMAT_G8R8_G8B8_UNORM: - case DXGI_FORMAT_YUY2: - packed = true; - bpe = 4; - break; - - case DXGI_FORMAT_Y210: - case DXGI_FORMAT_Y216: - packed = true; - bpe = 8; - break; - - case DXGI_FORMAT_NV12: - case DXGI_FORMAT_420_OPAQUE: - case DXGI_FORMAT_P208: - planar = true; - bpe = 2; - break; - - case DXGI_FORMAT_P010: - case DXGI_FORMAT_P016: - planar = true; - bpe = 4; - break; - - default: - break; - } - - if (bc) - { - uint64_t numBlocksWide = 0; - if (width > 0) - { - numBlocksWide = std::max(1u, (uint64_t(width) + 3u) / 4u); - } - uint64_t numBlocksHigh = 0; - if (height > 0) - { - numBlocksHigh = std::max(1u, (uint64_t(height) + 3u) / 4u); - } - rowBytes = numBlocksWide * bpe; - numRows = numBlocksHigh; - numBytes = rowBytes * numBlocksHigh; - } - else if (packed) - { - rowBytes = ((uint64_t(width) + 1u) >> 1) * bpe; - numRows = uint64_t(height); - numBytes = rowBytes * height; - } - else if (fmt == DXGI_FORMAT_NV11) - { - rowBytes = ((uint64_t(width) + 3u) >> 2) * 4u; - numRows = uint64_t(height) * 2u; // Direct3D makes this simplifying assumption, although it is larger than the 4:1:1 data - numBytes = rowBytes * numRows; - } - else if (planar) - { - rowBytes = ((uint64_t(width) + 1u) >> 1) * bpe; - numBytes = (rowBytes * uint64_t(height)) + ((rowBytes * uint64_t(height) + 1u) >> 1); - numRows = height + ((uint64_t(height) + 1u) >> 1); - } - else - { - size_t bpp = BitsPerPixel(fmt); - if (!bpp) - return E_INVALIDARG; - - rowBytes = (uint64_t(width) * bpp + 7u) / 8u; // round up to nearest byte - numRows = uint64_t(height); - numBytes = rowBytes * height; - } - -#if defined(_M_IX86) || defined(_M_ARM) || defined(_M_HYBRID_X86_ARM64) - static_assert(sizeof(size_t) == 4, "Not a 32-bit platform!"); - if (numBytes > UINT32_MAX || rowBytes > UINT32_MAX || numRows > UINT32_MAX) - return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); -#else - static_assert(sizeof(size_t) == 8, "Not a 64-bit platform!"); -#endif - - if (outNumBytes) - { - *outNumBytes = static_cast(numBytes); - } - if (outRowBytes) - { - *outRowBytes = static_cast(rowBytes); - } - if (outNumRows) - { - *outNumRows = static_cast(numRows); - } - - return S_OK; - } - - - //-------------------------------------------------------------------------------------- - DXGI_FORMAT EnsureNotTypeless( DXGI_FORMAT fmt ) - { - // Assumes UNORM or FLOAT; doesn't use UINT or SINT - switch( fmt ) - { - case DXGI_FORMAT_R32G32B32A32_TYPELESS: return DXGI_FORMAT_R32G32B32A32_FLOAT; - case DXGI_FORMAT_R32G32B32_TYPELESS: return DXGI_FORMAT_R32G32B32_FLOAT; - case DXGI_FORMAT_R16G16B16A16_TYPELESS: return DXGI_FORMAT_R16G16B16A16_UNORM; - case DXGI_FORMAT_R32G32_TYPELESS: return DXGI_FORMAT_R32G32_FLOAT; - case DXGI_FORMAT_R10G10B10A2_TYPELESS: return DXGI_FORMAT_R10G10B10A2_UNORM; - case DXGI_FORMAT_R8G8B8A8_TYPELESS: return DXGI_FORMAT_R8G8B8A8_UNORM; - case DXGI_FORMAT_R16G16_TYPELESS: return DXGI_FORMAT_R16G16_UNORM; - case DXGI_FORMAT_R32_TYPELESS: return DXGI_FORMAT_R32_FLOAT; - case DXGI_FORMAT_R8G8_TYPELESS: return DXGI_FORMAT_R8G8_UNORM; - case DXGI_FORMAT_R16_TYPELESS: return DXGI_FORMAT_R16_UNORM; - case DXGI_FORMAT_R8_TYPELESS: return DXGI_FORMAT_R8_UNORM; - case DXGI_FORMAT_BC1_TYPELESS: return DXGI_FORMAT_BC1_UNORM; - case DXGI_FORMAT_BC2_TYPELESS: return DXGI_FORMAT_BC2_UNORM; - case DXGI_FORMAT_BC3_TYPELESS: return DXGI_FORMAT_BC3_UNORM; - case DXGI_FORMAT_BC4_TYPELESS: return DXGI_FORMAT_BC4_UNORM; - case DXGI_FORMAT_BC5_TYPELESS: return DXGI_FORMAT_BC5_UNORM; - case DXGI_FORMAT_B8G8R8A8_TYPELESS: return DXGI_FORMAT_B8G8R8A8_UNORM; - case DXGI_FORMAT_B8G8R8X8_TYPELESS: return DXGI_FORMAT_B8G8R8X8_UNORM; - case DXGI_FORMAT_BC7_TYPELESS: return DXGI_FORMAT_BC7_UNORM; - default: return fmt; - } - } - - - //-------------------------------------------------------------------------------------- - inline void TransitionResource( - _In_ ID3D12GraphicsCommandList* commandList, - _In_ ID3D12Resource* resource, - _In_ D3D12_RESOURCE_STATES stateBefore, - _In_ D3D12_RESOURCE_STATES stateAfter) - { - assert(commandList != nullptr); - assert(resource != nullptr); - - if (stateBefore == stateAfter) - return; - - D3D12_RESOURCE_BARRIER desc = {}; - desc.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; - desc.Transition.pResource = resource; - desc.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; - desc.Transition.StateBefore = stateBefore; - desc.Transition.StateAfter = stateAfter; - - commandList->ResourceBarrier(1, &desc); - } - - - //-------------------------------------------------------------------------------------- - HRESULT CaptureTexture(_In_ ID3D12Device* device, - _In_ ID3D12CommandQueue* pCommandQ, - _In_ ID3D12Resource* pSource, - UINT64 srcPitch, - const D3D12_RESOURCE_DESC& desc, - ComPtr& pStaging, - D3D12_RESOURCE_STATES beforeState, - D3D12_RESOURCE_STATES afterState) - { - if (!pCommandQ || !pSource) - return E_INVALIDARG; - - if (desc.Dimension != D3D12_RESOURCE_DIMENSION_TEXTURE2D) - return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); - - if (srcPitch > UINT32_MAX) - return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); - - UINT numberOfPlanes = D3D12GetFormatPlaneCount(device, desc.Format); - if (numberOfPlanes != 1) - return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); - - D3D12_HEAP_PROPERTIES sourceHeapProperties; - D3D12_HEAP_FLAGS sourceHeapFlags; - HRESULT hr = pSource->GetHeapProperties(&sourceHeapProperties, &sourceHeapFlags); - if (FAILED(hr)) - return hr; - - if (sourceHeapProperties.Type == D3D12_HEAP_TYPE_READBACK) - { - // Handle case where the source is already a staging texture we can use directly - pStaging = pSource; - return S_OK; - } - - // Create a command allocator - ComPtr commandAlloc; - hr = device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(commandAlloc.GetAddressOf())); - if (FAILED(hr)) - return hr; - - // Spin up a new command list - ComPtr commandList; - hr = device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, commandAlloc.Get(), nullptr, IID_PPV_ARGS(commandList.GetAddressOf())); - if (FAILED(hr)) - return hr; - - // Create a fence - ComPtr fence; - hr = device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(fence.GetAddressOf())); - if (FAILED(hr)) - return hr; - - assert((srcPitch & 0xFF) == 0); - - CD3DX12_HEAP_PROPERTIES defaultHeapProperties(D3D12_HEAP_TYPE_DEFAULT); - CD3DX12_HEAP_PROPERTIES readBackHeapProperties(D3D12_HEAP_TYPE_READBACK); - - // Readback resources must be buffers - D3D12_RESOURCE_DESC bufferDesc = {}; - bufferDesc.Alignment = desc.Alignment; - bufferDesc.DepthOrArraySize = 1; - bufferDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; - bufferDesc.Flags = D3D12_RESOURCE_FLAG_NONE; - bufferDesc.Format = DXGI_FORMAT_UNKNOWN; - bufferDesc.Height = 1; - bufferDesc.Width = srcPitch * desc.Height; - bufferDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; - bufferDesc.MipLevels = 1; - bufferDesc.SampleDesc.Count = 1; - bufferDesc.SampleDesc.Quality = 0; - - ComPtr copySource(pSource); - if (desc.SampleDesc.Count > 1) - { - // MSAA content must be resolved before being copied to a staging texture - auto descCopy = desc; - descCopy.SampleDesc.Count = 1; - descCopy.SampleDesc.Quality = 0; - - ComPtr pTemp; - hr = device->CreateCommittedResource( - &defaultHeapProperties, - D3D12_HEAP_FLAG_NONE, - &descCopy, - D3D12_RESOURCE_STATE_COPY_DEST, - nullptr, - IID_PPV_ARGS(pTemp.GetAddressOf())); - if (FAILED(hr)) - return hr; - - assert(pTemp); - - DXGI_FORMAT fmt = EnsureNotTypeless(desc.Format); - - D3D12_FEATURE_DATA_FORMAT_SUPPORT formatInfo = { fmt, D3D12_FORMAT_SUPPORT1_NONE, D3D12_FORMAT_SUPPORT2_NONE }; - hr = device->CheckFeatureSupport(D3D12_FEATURE_FORMAT_SUPPORT, &formatInfo, sizeof(formatInfo)); - if (FAILED(hr)) - return hr; - - if (!(formatInfo.Support1 & D3D12_FORMAT_SUPPORT1_TEXTURE2D)) - return E_FAIL; - - for (UINT item = 0; item < desc.DepthOrArraySize; ++item) - { - for (UINT level = 0; level < desc.MipLevels; ++level) - { - UINT index = D3D12CalcSubresource(level, item, 0, desc.MipLevels, desc.DepthOrArraySize); - commandList->ResolveSubresource(pTemp.Get(), index, pSource, index, fmt); - } - } - - copySource = pTemp; - } - - // Create a staging texture - hr = device->CreateCommittedResource( - &readBackHeapProperties, - D3D12_HEAP_FLAG_NONE, - &bufferDesc, - D3D12_RESOURCE_STATE_COPY_DEST, - nullptr, - IID_PPV_ARGS(pStaging.ReleaseAndGetAddressOf())); - if (FAILED(hr)) - return hr; - - assert(pStaging); - - // Transition the resource if necessary - TransitionResource(commandList.Get(), pSource, beforeState, D3D12_RESOURCE_STATE_COPY_SOURCE); - - // Get the copy target location - D3D12_PLACED_SUBRESOURCE_FOOTPRINT bufferFootprint = {}; - bufferFootprint.Footprint.Width = static_cast(desc.Width); - bufferFootprint.Footprint.Height = desc.Height; - bufferFootprint.Footprint.Depth = 1; - bufferFootprint.Footprint.RowPitch = static_cast(srcPitch); - bufferFootprint.Footprint.Format = desc.Format; - - CD3DX12_TEXTURE_COPY_LOCATION copyDest(pStaging.Get(), bufferFootprint); - CD3DX12_TEXTURE_COPY_LOCATION copySrc(copySource.Get(), 0); - - // Copy the texture - commandList->CopyTextureRegion(©Dest, 0, 0, 0, ©Src, nullptr); - - // Transition the resource to the next state - TransitionResource(commandList.Get(), pSource, D3D12_RESOURCE_STATE_COPY_SOURCE, afterState); - - hr = commandList->Close(); - if (FAILED(hr)) - return hr; - - // Execute the command list - pCommandQ->ExecuteCommandLists(1, CommandListCast(commandList.GetAddressOf())); - - // Signal the fence - hr = pCommandQ->Signal(fence.Get(), 1); - if (FAILED(hr)) - return hr; - - // Block until the copy is complete - while (fence->GetCompletedValue() < 1) - SwitchToThread(); - - return S_OK; - } - - IWICImagingFactory2* _GetWIC() - { - static INIT_ONCE s_initOnce = INIT_ONCE_STATIC_INIT; - - IWICImagingFactory2* factory = nullptr; - (void)InitOnceExecuteOnce(&s_initOnce, - [](PINIT_ONCE, PVOID, PVOID *ifactory) -> BOOL - { - return SUCCEEDED( CoCreateInstance( - CLSID_WICImagingFactory2, - nullptr, - CLSCTX_INPROC_SERVER, - __uuidof(IWICImagingFactory2), - ifactory) ) ? TRUE : FALSE; - }, nullptr, reinterpret_cast(&factory)); - - return factory; - } -} // anonymous namespace - - -//-------------------------------------------------------------------------------------- -_Use_decl_annotations_ -HRESULT DirectX::SaveDDSTextureToFile( - ID3D12CommandQueue* pCommandQ, - ID3D12Resource* pSource, - const wchar_t* fileName, - D3D12_RESOURCE_STATES beforeState, - D3D12_RESOURCE_STATES afterState) -{ - if ( !fileName ) - return E_INVALIDARG; - - ComPtr device; - pCommandQ->GetDevice(IID_PPV_ARGS(device.GetAddressOf())); - - // Get the size of the image - const auto desc = pSource->GetDesc(); - - if (desc.Width > UINT32_MAX) - return E_INVALIDARG; - - UINT64 totalResourceSize = 0; - UINT64 fpRowPitch = 0; - UINT fpRowCount = 0; - // Get the rowcount, pitch and size of the top mip - device->GetCopyableFootprints( - &desc, - 0, - 1, - 0, - nullptr, - &fpRowCount, - &fpRowPitch, - &totalResourceSize); - - // Round up the srcPitch to multiples of 256 - UINT64 dstRowPitch = (fpRowPitch + 255) & ~0xFF; - - if (dstRowPitch > UINT32_MAX) - return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); - - ComPtr pStaging; - HRESULT hr = CaptureTexture( device.Get(), pCommandQ, pSource, dstRowPitch, desc, pStaging, beforeState, afterState ); - if ( FAILED(hr) ) - return hr; - - // Create file - ScopedHandle hFile( safe_handle( CreateFile2( fileName, GENERIC_WRITE, 0, CREATE_ALWAYS, nullptr ) ) ); - if ( !hFile ) - return HRESULT_FROM_WIN32( GetLastError() ); - - auto_delete_file delonfail(hFile.get()); - - // Setup header - const size_t MAX_HEADER_SIZE = sizeof(uint32_t) + sizeof(DDS_HEADER) + sizeof(DDS_HEADER_DXT10); - uint8_t fileHeader[ MAX_HEADER_SIZE ]; - - *reinterpret_cast(&fileHeader[0]) = DDS_MAGIC; - - auto header = reinterpret_cast( &fileHeader[0] + sizeof(uint32_t) ); - size_t headerSize = sizeof(uint32_t) + sizeof(DDS_HEADER); - memset( header, 0, sizeof(DDS_HEADER) ); - header->size = sizeof( DDS_HEADER ); - header->flags = DDS_HEADER_FLAGS_TEXTURE | DDS_HEADER_FLAGS_MIPMAP; - header->height = desc.Height; - header->width = static_cast(desc.Width); - header->mipMapCount = 1; - header->caps = DDS_SURFACE_FLAGS_TEXTURE; - - // Try to use a legacy .DDS pixel format for better tools support, otherwise fallback to 'DX10' header extension - DDS_HEADER_DXT10* extHeader = nullptr; - switch( desc.Format ) - { - case DXGI_FORMAT_R8G8B8A8_UNORM: memcpy_s( &header->ddspf, sizeof(header->ddspf), &DDSPF_A8B8G8R8, sizeof(DDS_PIXELFORMAT) ); break; - case DXGI_FORMAT_R16G16_UNORM: memcpy_s( &header->ddspf, sizeof(header->ddspf), &DDSPF_G16R16, sizeof(DDS_PIXELFORMAT) ); break; - case DXGI_FORMAT_R8G8_UNORM: memcpy_s( &header->ddspf, sizeof(header->ddspf), &DDSPF_A8L8, sizeof(DDS_PIXELFORMAT) ); break; - case DXGI_FORMAT_R16_UNORM: memcpy_s( &header->ddspf, sizeof(header->ddspf), &DDSPF_L16, sizeof(DDS_PIXELFORMAT) ); break; - case DXGI_FORMAT_R8_UNORM: memcpy_s( &header->ddspf, sizeof(header->ddspf), &DDSPF_L8, sizeof(DDS_PIXELFORMAT) ); break; - case DXGI_FORMAT_A8_UNORM: memcpy_s( &header->ddspf, sizeof(header->ddspf), &DDSPF_A8, sizeof(DDS_PIXELFORMAT) ); break; - case DXGI_FORMAT_R8G8_B8G8_UNORM: memcpy_s( &header->ddspf, sizeof(header->ddspf), &DDSPF_R8G8_B8G8, sizeof(DDS_PIXELFORMAT) ); break; - case DXGI_FORMAT_G8R8_G8B8_UNORM: memcpy_s( &header->ddspf, sizeof(header->ddspf), &DDSPF_G8R8_G8B8, sizeof(DDS_PIXELFORMAT) ); break; - case DXGI_FORMAT_BC1_UNORM: memcpy_s( &header->ddspf, sizeof(header->ddspf), &DDSPF_DXT1, sizeof(DDS_PIXELFORMAT) ); break; - case DXGI_FORMAT_BC2_UNORM: memcpy_s( &header->ddspf, sizeof(header->ddspf), &DDSPF_DXT3, sizeof(DDS_PIXELFORMAT) ); break; - case DXGI_FORMAT_BC3_UNORM: memcpy_s( &header->ddspf, sizeof(header->ddspf), &DDSPF_DXT5, sizeof(DDS_PIXELFORMAT) ); break; - case DXGI_FORMAT_BC4_UNORM: memcpy_s( &header->ddspf, sizeof(header->ddspf), &DDSPF_BC4_UNORM, sizeof(DDS_PIXELFORMAT) ); break; - case DXGI_FORMAT_BC4_SNORM: memcpy_s( &header->ddspf, sizeof(header->ddspf), &DDSPF_BC4_SNORM, sizeof(DDS_PIXELFORMAT) ); break; - case DXGI_FORMAT_BC5_UNORM: memcpy_s( &header->ddspf, sizeof(header->ddspf), &DDSPF_BC5_UNORM, sizeof(DDS_PIXELFORMAT) ); break; - case DXGI_FORMAT_BC5_SNORM: memcpy_s( &header->ddspf, sizeof(header->ddspf), &DDSPF_BC5_SNORM, sizeof(DDS_PIXELFORMAT) ); break; - case DXGI_FORMAT_B5G6R5_UNORM: memcpy_s( &header->ddspf, sizeof(header->ddspf), &DDSPF_R5G6B5, sizeof(DDS_PIXELFORMAT) ); break; - case DXGI_FORMAT_B5G5R5A1_UNORM: memcpy_s( &header->ddspf, sizeof(header->ddspf), &DDSPF_A1R5G5B5, sizeof(DDS_PIXELFORMAT) ); break; - case DXGI_FORMAT_R8G8_SNORM: memcpy_s( &header->ddspf, sizeof(header->ddspf), &DDSPF_V8U8, sizeof(DDS_PIXELFORMAT) ); break; - case DXGI_FORMAT_R8G8B8A8_SNORM: memcpy_s( &header->ddspf, sizeof(header->ddspf), &DDSPF_Q8W8V8U8, sizeof(DDS_PIXELFORMAT) ); break; - case DXGI_FORMAT_R16G16_SNORM: memcpy_s( &header->ddspf, sizeof(header->ddspf), &DDSPF_V16U16, sizeof(DDS_PIXELFORMAT) ); break; - case DXGI_FORMAT_B8G8R8A8_UNORM: memcpy_s( &header->ddspf, sizeof(header->ddspf), &DDSPF_A8R8G8B8, sizeof(DDS_PIXELFORMAT) ); break; - case DXGI_FORMAT_B8G8R8X8_UNORM: memcpy_s( &header->ddspf, sizeof(header->ddspf), &DDSPF_X8R8G8B8, sizeof(DDS_PIXELFORMAT) ); break; - case DXGI_FORMAT_YUY2: memcpy_s( &header->ddspf, sizeof(header->ddspf), &DDSPF_YUY2, sizeof(DDS_PIXELFORMAT) ); break; - case DXGI_FORMAT_B4G4R4A4_UNORM: memcpy_s( &header->ddspf, sizeof(header->ddspf), &DDSPF_A4R4G4B4, sizeof(DDS_PIXELFORMAT) ); break; - - // Legacy D3DX formats using D3DFMT enum value as FourCC - case DXGI_FORMAT_R32G32B32A32_FLOAT: header->ddspf.size = sizeof(DDS_PIXELFORMAT); header->ddspf.flags = DDS_FOURCC; header->ddspf.fourCC = 116; break; // D3DFMT_A32B32G32R32F - case DXGI_FORMAT_R16G16B16A16_FLOAT: header->ddspf.size = sizeof(DDS_PIXELFORMAT); header->ddspf.flags = DDS_FOURCC; header->ddspf.fourCC = 113; break; // D3DFMT_A16B16G16R16F - case DXGI_FORMAT_R16G16B16A16_UNORM: header->ddspf.size = sizeof(DDS_PIXELFORMAT); header->ddspf.flags = DDS_FOURCC; header->ddspf.fourCC = 36; break; // D3DFMT_A16B16G16R16 - case DXGI_FORMAT_R16G16B16A16_SNORM: header->ddspf.size = sizeof(DDS_PIXELFORMAT); header->ddspf.flags = DDS_FOURCC; header->ddspf.fourCC = 110; break; // D3DFMT_Q16W16V16U16 - case DXGI_FORMAT_R32G32_FLOAT: header->ddspf.size = sizeof(DDS_PIXELFORMAT); header->ddspf.flags = DDS_FOURCC; header->ddspf.fourCC = 115; break; // D3DFMT_G32R32F - case DXGI_FORMAT_R16G16_FLOAT: header->ddspf.size = sizeof(DDS_PIXELFORMAT); header->ddspf.flags = DDS_FOURCC; header->ddspf.fourCC = 112; break; // D3DFMT_G16R16F - case DXGI_FORMAT_R32_FLOAT: header->ddspf.size = sizeof(DDS_PIXELFORMAT); header->ddspf.flags = DDS_FOURCC; header->ddspf.fourCC = 114; break; // D3DFMT_R32F - case DXGI_FORMAT_R16_FLOAT: header->ddspf.size = sizeof(DDS_PIXELFORMAT); header->ddspf.flags = DDS_FOURCC; header->ddspf.fourCC = 111; break; // D3DFMT_R16F - - case DXGI_FORMAT_AI44: - case DXGI_FORMAT_IA44: - case DXGI_FORMAT_P8: - case DXGI_FORMAT_A8P8: - return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); - - default: - memcpy_s( &header->ddspf, sizeof(header->ddspf), &DDSPF_DX10, sizeof(DDS_PIXELFORMAT) ); - - headerSize += sizeof(DDS_HEADER_DXT10); - extHeader = reinterpret_cast(fileHeader + sizeof(uint32_t) + sizeof(DDS_HEADER) ); - memset( extHeader, 0, sizeof(DDS_HEADER_DXT10) ); - extHeader->dxgiFormat = desc.Format; - extHeader->resourceDimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; - extHeader->arraySize = 1; - break; - } - - size_t rowPitch, slicePitch, rowCount; - hr = GetSurfaceInfo(static_cast(desc.Width), desc.Height, desc.Format, &slicePitch, &rowPitch, &rowCount); - if (FAILED(hr)) - return hr; - - if (rowPitch > UINT32_MAX || slicePitch > UINT32_MAX) - return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); - - if ( IsCompressed( desc.Format ) ) - { - header->flags |= DDS_HEADER_FLAGS_LINEARSIZE; - header->pitchOrLinearSize = static_cast( slicePitch ); - } - else - { - header->flags |= DDS_HEADER_FLAGS_PITCH; - header->pitchOrLinearSize = static_cast( rowPitch ); - } - - // Setup pixels - std::unique_ptr pixels( new (std::nothrow) uint8_t[ slicePitch ] ); - if (!pixels) - return E_OUTOFMEMORY; - - assert(fpRowCount == rowCount); - assert(fpRowPitch == rowPitch); - - UINT64 imageSize = dstRowPitch * UINT64(rowCount); - if (imageSize > UINT32_MAX) - return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); - - void* pMappedMemory = nullptr; - D3D12_RANGE readRange = { 0, static_cast(imageSize) }; - D3D12_RANGE writeRange = { 0, 0 }; - hr = pStaging->Map(0, &readRange, &pMappedMemory ); - if ( FAILED(hr) ) - return hr; - - auto sptr = static_cast(pMappedMemory); - if ( !sptr ) - { - pStaging->Unmap(0, &writeRange); - return E_POINTER; - } - - uint8_t* dptr = pixels.get(); - - size_t msize = std::min( rowPitch, rowPitch ); - for( size_t h = 0; h < rowCount; ++h ) - { - memcpy_s( dptr, rowPitch, sptr, msize ); - sptr += dstRowPitch; - dptr += rowPitch; - } - - pStaging->Unmap( 0, &writeRange ); - - // Write header & pixels - DWORD bytesWritten; - if ( !WriteFile( hFile.get(), fileHeader, static_cast( headerSize ), &bytesWritten, nullptr ) ) - return HRESULT_FROM_WIN32( GetLastError() ); - - if ( bytesWritten != headerSize ) - return E_FAIL; - - if ( !WriteFile( hFile.get(), pixels.get(), static_cast( slicePitch ), &bytesWritten, nullptr ) ) - return HRESULT_FROM_WIN32( GetLastError() ); - - if ( bytesWritten != slicePitch ) - return E_FAIL; - - delonfail.clear(); - - return S_OK; -} - -//-------------------------------------------------------------------------------------- -_Use_decl_annotations_ -HRESULT DirectX::SaveWICTextureToFile( - ID3D12CommandQueue* pCommandQ, - ID3D12Resource* pSource, - REFGUID guidContainerFormat, - const wchar_t* fileName, - D3D12_RESOURCE_STATES beforeState, - D3D12_RESOURCE_STATES afterState, - const GUID* targetFormat, - std::function setCustomProps ) -{ - if ( !fileName ) - return E_INVALIDARG; - - ComPtr device; - pCommandQ->GetDevice(IID_PPV_ARGS(device.GetAddressOf())); - - // Get the size of the image - const auto desc = pSource->GetDesc(); - - if (desc.Width > UINT32_MAX) - return E_INVALIDARG; - - UINT64 totalResourceSize = 0; - UINT64 fpRowPitch = 0; - UINT fpRowCount = 0; - // Get the rowcount, pitch and size of the top mip - device->GetCopyableFootprints( - &desc, - 0, - 1, - 0, - nullptr, - &fpRowCount, - &fpRowPitch, - &totalResourceSize); - - // Round up the srcPitch to multiples of 256 - UINT64 dstRowPitch = (fpRowPitch + 255) & ~0xFF; - - if (dstRowPitch > UINT32_MAX) - return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); - - ComPtr pStaging; - HRESULT hr = CaptureTexture(device.Get(), pCommandQ, pSource, dstRowPitch, desc, pStaging, beforeState, afterState); - if (FAILED(hr)) - return hr; - - // Determine source format's WIC equivalent - WICPixelFormatGUID pfGuid; - bool sRGB = false; - switch ( desc.Format ) - { - case DXGI_FORMAT_R32G32B32A32_FLOAT: pfGuid = GUID_WICPixelFormat128bppRGBAFloat; break; - case DXGI_FORMAT_R16G16B16A16_FLOAT: pfGuid = GUID_WICPixelFormat64bppRGBAHalf; break; - case DXGI_FORMAT_R16G16B16A16_UNORM: pfGuid = GUID_WICPixelFormat64bppRGBA; break; - case DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM: pfGuid = GUID_WICPixelFormat32bppRGBA1010102XR; break; - case DXGI_FORMAT_R10G10B10A2_UNORM: pfGuid = GUID_WICPixelFormat32bppRGBA1010102; break; - case DXGI_FORMAT_B5G5R5A1_UNORM: pfGuid = GUID_WICPixelFormat16bppBGRA5551; break; - case DXGI_FORMAT_B5G6R5_UNORM: pfGuid = GUID_WICPixelFormat16bppBGR565; break; - case DXGI_FORMAT_R32_FLOAT: pfGuid = GUID_WICPixelFormat32bppGrayFloat; break; - case DXGI_FORMAT_R16_FLOAT: pfGuid = GUID_WICPixelFormat16bppGrayHalf; break; - case DXGI_FORMAT_R16_UNORM: pfGuid = GUID_WICPixelFormat16bppGray; break; - case DXGI_FORMAT_R8_UNORM: pfGuid = GUID_WICPixelFormat8bppGray; break; - case DXGI_FORMAT_A8_UNORM: pfGuid = GUID_WICPixelFormat8bppAlpha; break; - - case DXGI_FORMAT_R8G8B8A8_UNORM: - pfGuid = GUID_WICPixelFormat32bppRGBA; - break; - - case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: - pfGuid = GUID_WICPixelFormat32bppRGBA; - sRGB = true; - break; - - case DXGI_FORMAT_B8G8R8A8_UNORM: - pfGuid = GUID_WICPixelFormat32bppBGRA; - break; - - case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: - pfGuid = GUID_WICPixelFormat32bppBGRA; - sRGB = true; - break; - - case DXGI_FORMAT_B8G8R8X8_UNORM: - pfGuid = GUID_WICPixelFormat32bppBGR; - break; - - case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: - pfGuid = GUID_WICPixelFormat32bppBGR; - sRGB = true; - break; - - default: - return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); - } - - auto pWIC = _GetWIC(); - if ( !pWIC ) - return E_NOINTERFACE; - - ComPtr stream; - hr = pWIC->CreateStream( stream.GetAddressOf() ); - if ( FAILED(hr) ) - return hr; - - hr = stream->InitializeFromFilename( fileName, GENERIC_WRITE ); - if ( FAILED(hr) ) - return hr; - - auto_delete_file_wic delonfail(stream, fileName); - - ComPtr encoder; - hr = pWIC->CreateEncoder( guidContainerFormat, nullptr, encoder.GetAddressOf() ); - if ( FAILED(hr) ) - return hr; - - hr = encoder->Initialize( stream.Get(), WICBitmapEncoderNoCache ); - if ( FAILED(hr) ) - return hr; - - ComPtr frame; - ComPtr props; - hr = encoder->CreateNewFrame( frame.GetAddressOf(), props.GetAddressOf() ); - if ( FAILED(hr) ) - return hr; - - if ( targetFormat && memcmp( &guidContainerFormat, &GUID_ContainerFormatBmp, sizeof(WICPixelFormatGUID) ) == 0 ) - { - // Opt-in to the WIC2 support for writing 32-bit Windows BMP files with an alpha channel - PROPBAG2 option = {}; - option.pstrName = const_cast(L"EnableV5Header32bppBGRA"); - - VARIANT varValue; - varValue.vt = VT_BOOL; - varValue.boolVal = VARIANT_TRUE; - (void)props->Write( 1, &option, &varValue ); - } - - if ( setCustomProps ) - { - setCustomProps( props.Get() ); - } - - hr = frame->Initialize( props.Get() ); - if ( FAILED(hr) ) - return hr; - - hr = frame->SetSize(static_cast(desc.Width), desc.Height ); - if ( FAILED(hr) ) - return hr; - - hr = frame->SetResolution( 72, 72 ); - if ( FAILED(hr) ) - return hr; - - // Pick a target format - WICPixelFormatGUID targetGuid; - if ( targetFormat ) - { - targetGuid = *targetFormat; - } - else - { - // Screenshots don't typically include the alpha channel of the render target - switch ( desc.Format ) - { - case DXGI_FORMAT_R32G32B32A32_FLOAT: - case DXGI_FORMAT_R16G16B16A16_FLOAT: - targetGuid = GUID_WICPixelFormat96bppRGBFloat; // WIC 2 - break; - - case DXGI_FORMAT_R16G16B16A16_UNORM: targetGuid = GUID_WICPixelFormat48bppBGR; break; - case DXGI_FORMAT_B5G5R5A1_UNORM: targetGuid = GUID_WICPixelFormat16bppBGR555; break; - case DXGI_FORMAT_B5G6R5_UNORM: targetGuid = GUID_WICPixelFormat16bppBGR565; break; - - case DXGI_FORMAT_R32_FLOAT: - case DXGI_FORMAT_R16_FLOAT: - case DXGI_FORMAT_R16_UNORM: - case DXGI_FORMAT_R8_UNORM: - case DXGI_FORMAT_A8_UNORM: - targetGuid = GUID_WICPixelFormat8bppGray; - break; - - default: - targetGuid = GUID_WICPixelFormat24bppBGR; - break; - } - } - - hr = frame->SetPixelFormat( &targetGuid ); - if ( FAILED(hr) ) - return hr; - - if ( targetFormat && memcmp( targetFormat, &targetGuid, sizeof(WICPixelFormatGUID) ) != 0 ) - { - // Requested output pixel format is not supported by the WIC codec - return E_FAIL; - } - - // Encode WIC metadata - ComPtr metawriter; - if ( SUCCEEDED( frame->GetMetadataQueryWriter( metawriter.GetAddressOf() ) ) ) - { - PROPVARIANT value; - PropVariantInit( &value ); - - value.vt = VT_LPSTR; - value.pszVal = const_cast("DirectXTK"); - - if ( memcmp( &guidContainerFormat, &GUID_ContainerFormatPng, sizeof(GUID) ) == 0 ) - { - // Set Software name - (void)metawriter->SetMetadataByName( L"/tEXt/{str=Software}", &value ); - - // Set sRGB chunk - if (sRGB) - { - value.vt = VT_UI1; - value.bVal = 0; - (void)metawriter->SetMetadataByName(L"/sRGB/RenderingIntent", &value); - } - else - { - // add gAMA chunk with gamma 1.0 - value.vt = VT_UI4; - value.uintVal = 100000; // gama value * 100,000 -- i.e. gamma 1.0 - (void)metawriter->SetMetadataByName(L"/gAMA/ImageGamma", &value); - - // remove sRGB chunk which is added by default. - (void)metawriter->RemoveMetadataByName(L"/sRGB/RenderingIntent"); - } - } - else - { - // Set Software name - (void)metawriter->SetMetadataByName( L"System.ApplicationName", &value ); - - if ( sRGB ) - { - // Set EXIF Colorspace of sRGB - value.vt = VT_UI2; - value.uiVal = 1; - (void)metawriter->SetMetadataByName( L"System.Image.ColorSpace", &value ); - } - } - } - - UINT64 imageSize = dstRowPitch * UINT64(desc.Height); - if (imageSize > UINT32_MAX) - return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); - - void* pMappedMemory = nullptr; - D3D12_RANGE readRange = {0, static_cast(imageSize)}; - D3D12_RANGE writeRange = {0, 0}; - hr = pStaging->Map(0, &readRange, &pMappedMemory); - if (FAILED(hr)) - return hr; - - if ( memcmp( &targetGuid, &pfGuid, sizeof(WICPixelFormatGUID) ) != 0 ) - { - // Conversion required to write - ComPtr source; - hr = pWIC->CreateBitmapFromMemory(static_cast(desc.Width), desc.Height, pfGuid, - static_cast(dstRowPitch), static_cast(imageSize), - static_cast(pMappedMemory), source.GetAddressOf() ); - if ( FAILED(hr) ) - { - pStaging->Unmap( 0, &writeRange ); - return hr; - } - - ComPtr FC; - hr = pWIC->CreateFormatConverter( FC.GetAddressOf() ); - if ( FAILED(hr) ) - { - pStaging->Unmap(0, &writeRange); - return hr; - } - - BOOL canConvert = FALSE; - hr = FC->CanConvert( pfGuid, targetGuid, &canConvert ); - if ( FAILED(hr) || !canConvert ) - { - return E_UNEXPECTED; - } - - hr = FC->Initialize( source.Get(), targetGuid, WICBitmapDitherTypeNone, nullptr, 0, WICBitmapPaletteTypeMedianCut ); - if ( FAILED(hr) ) - { - pStaging->Unmap(0, &writeRange); - return hr; - } - - WICRect rect = { 0, 0, static_cast( desc.Width ), static_cast( desc.Height ) }; - hr = frame->WriteSource( FC.Get(), &rect ); - if ( FAILED(hr) ) - { - pStaging->Unmap(0, &writeRange); - return hr; - } - } - else - { - // No conversion required - hr = frame->WritePixels( desc.Height, static_cast(dstRowPitch), static_cast(imageSize), static_cast( pMappedMemory ) ); - if ( FAILED(hr) ) - return hr; - } - - pStaging->Unmap(0, &writeRange); - - hr = frame->Commit(); - if ( FAILED(hr) ) - return hr; - - hr = encoder->Commit(); - if ( FAILED(hr) ) - return hr; - - delonfail.clear(); - - return S_OK; -} diff --git a/thirdparty/libpng b/thirdparty/libpng deleted file mode 160000 index a37d483..0000000 --- a/thirdparty/libpng +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a37d4836519517bdce6cb9d956092321eca3e73b diff --git a/thirdparty/zlib-ng b/thirdparty/zlib-ng deleted file mode 160000 index b56a2fd..0000000 --- a/thirdparty/zlib-ng +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b56a2fd0b126cfe5f13e68ab9090cd4f6a773286