diff --git a/.gitmodules b/.gitmodules index 40411be..ec54d46 100644 --- a/.gitmodules +++ b/.gitmodules @@ -14,3 +14,11 @@ 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"] + path = thirdparty/zlib + url = https://github.com/madler/zlib.git + branch = master diff --git a/CMakeLists.txt b/CMakeLists.txt index 1b63b15..c1013d1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,7 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "bin") set_property(GLOBAL PROPERTY USE_FOLDERS ON) -# Add Vulkan headers on ARM since there is no official Vulkan SDK +# Add Vulkan headers on ARM since there is no official ARM Vulkan SDK if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "aarch64") add_subdirectory(thirdparty/Vulkan-Headers) set(RTXGI_API_VULKAN_SDK "1") @@ -47,6 +47,26 @@ 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 + set(CMAKE_WARN_DEPRECATED OFF CACHE BOOL "" FORCE) + set(CMAKE_DEBUG_POSTFIX "" CACHE STRING "override debug postfix") + option(ASM686 "" OFF) + option(AMD64 "" OFF) + add_subdirectory(thirdparty/zlib) + target_compile_options(zlib BEFORE PUBLIC /wd4267) # suppress the implicit conversion warning + set_target_properties(zlib zlibstatic example minigzip PROPERTIES FOLDER "Thirdparty/zlib" DEBUG_POSTFIX "") + + # 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) + set(ZLIB_INCLUDE_DIRS "${CMAKE_SOURCE_DIR}/thirdparty/zlib" "${CMAKE_BINARY_DIR}/thirdparty/zlib") + add_subdirectory(thirdparty/libpng) + set_target_properties(png_static genfiles PROPERTIES FOLDER "Thirdparty/libpng" DEBUG_POSTFIX "") + # Samples add_subdirectory(samples) endif() diff --git a/ChangeLog.md b/ChangeLog.md index 50f40e4..2b224bf 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,5 +1,34 @@ # RTXGI SDK Change Log +## 1.2.07 + +### SDK + +Features and Improvements: +- Shader improvements for probe data reads/writes + - The probe data texture now uses the same layout as the irradiance and distance textures + - This change makes probe data visualizations easier to understand +- Adds a RWTexture2D variant of ```DDGILoadProbeState()``` +- Uses the ```DDGILoadProbeState()``` convenience function where appropriate +- Moved the ```data``` member of the ```DDGIVolumeDescGPU``` struct inside the ```GetPackedData()``` function so the struct size is the same on the CPU and GPU +- Updated stale code comments (non-functional) +- Bumped SDK revision number and version string + +Bug Fixes: +- ```DDGIGetVolumeBlendWeight```: fixed regression related to volume edge fade not respecting rotations + +### Test Harness + +Features and Improvements: +- ```ProbeTraceRGS.hlsl```: no longer performs lighting for fixed rays when probe relocation is enabled (performance optimization) +- Adds a performance benchmark mode that collects CPU and GPU performance data and outputs the results to ```*.csv``` files + - Press the ```F2``` key to run a benchmark +- Adds the ability to store intermediate textures (back buffer, GBuffer, RTAO, and DDGIVolume textures) to disk + - Uses the zlib and libpng libraries for cross-platform compatibility + - Press the ```F1``` key to save the current image data to disk +- ```VolumeTexturesCS.hlsl```: minor updates to the layout order of visualized textures +- Config file and documentation updates + ## 1.2.04 ### SDK diff --git a/docs/DDGIVolume.md b/docs/DDGIVolume.md index e137e3e..0ab3e72 100644 --- a/docs/DDGIVolume.md +++ b/docs/DDGIVolume.md @@ -369,11 +369,11 @@ This texture stores world-space offsets and classification states for all probes - ```ProbeDataCommon.hlsl``` contains helper functions for reading and writing world-space offset data. - Probe classification state is stored in the W channel. -The texture's layout is the same as the irradiance and distance texture altases, except each probe uses just *one texel*. Below is a visualization of the probe data texture's world-space offsets (top) and probe states (bottom). +The texture's layout is the same as the irradiance and distance texture altases, except here each probe is represented by just *one texel*. Below is a visualization of the probe data texture's world-space offsets (top) and probe states (bottom).
-
Figure 6: The Probe Data texture (zoomed) from the Crytek Sponza scene
+
Figure 6: A visualization of the Probe Data texture (zoomed) for the Crytek Sponza scene
diff --git a/docs/ShaderAPI.md b/docs/ShaderAPI.md index 9ad2008..1042bc1 100644 --- a/docs/ShaderAPI.md +++ b/docs/ShaderAPI.md @@ -52,9 +52,14 @@ The Test Harness sample application provides [an example ray generation shader]( Computes the 3D grid-space coordinates for the probe at a given probe index in the range [0, numProbes]. Provide these coordinates to ```DDGIGetProbeWorldPosition()``` and ```DDGIGetScrollingProbeIndex()```. -#### ```DDGIGetProbeState(...)``` +#### ```DDGILoadProbeState(...)``` - float DDGIGetProbeState( + float DDGILoadProbeState( + int probeIndex, + RWTexture2D probeData, + DDGIVolumeDescGPU volume) + + float DDGILoadProbeState( int probeIndex, Texture2D probeData, DDGIVolumeDescGPU volume) diff --git a/docs/images/ddgivolume-textures-probedata.jpg b/docs/images/ddgivolume-textures-probedata.jpg index 7b067eb..af31fee 100644 Binary files a/docs/images/ddgivolume-textures-probedata.jpg and b/docs/images/ddgivolume-textures-probedata.jpg differ diff --git a/rtxgi-sdk/.gitignore b/rtxgi-sdk/.gitignore deleted file mode 100644 index a007fea..0000000 --- a/rtxgi-sdk/.gitignore +++ /dev/null @@ -1 +0,0 @@ -build/* diff --git a/rtxgi-sdk/include/rtxgi/Common.h b/rtxgi-sdk/include/rtxgi/Common.h index 7167b65..b0e5cdb 100644 --- a/rtxgi-sdk/include/rtxgi/Common.h +++ b/rtxgi-sdk/include/rtxgi/Common.h @@ -20,11 +20,11 @@ namespace rtxgi { static const int major = 1; static const int minor = 2; - static const int revision = 4; + static const int revision = 7; inline static const char* getVersionString() { - return "1.2.04"; + return "1.2.07"; } }; diff --git a/rtxgi-sdk/include/rtxgi/ddgi/DDGIVolumeDescGPU.h b/rtxgi-sdk/include/rtxgi/ddgi/DDGIVolumeDescGPU.h index 1f9474a..94158bf 100644 --- a/rtxgi-sdk/include/rtxgi/ddgi/DDGIVolumeDescGPU.h +++ b/rtxgi-sdk/include/rtxgi/ddgi/DDGIVolumeDescGPU.h @@ -95,9 +95,9 @@ struct DDGIVolumeDescGPU bool probeScrollClear[3]; // whether probes of a plane need to be cleared due to scrolling movement #ifndef HLSL - rtxgi::DDGIVolumeDescGPUPacked data; rtxgi::DDGIVolumeDescGPUPacked GetPackedData() { + rtxgi::DDGIVolumeDescGPUPacked data; data.origin = origin; data.probeHysteresis = probeHysteresis; data.rotation = rotation; diff --git a/rtxgi-sdk/include/rtxgi/ddgi/gfx/DDGIVolume_D3D12.h b/rtxgi-sdk/include/rtxgi/ddgi/gfx/DDGIVolume_D3D12.h index 760edeb..136e70c 100644 --- a/rtxgi-sdk/include/rtxgi/ddgi/gfx/DDGIVolume_D3D12.h +++ b/rtxgi-sdk/include/rtxgi/ddgi/gfx/DDGIVolume_D3D12.h @@ -128,7 +128,7 @@ namespace rtxgi ID3D12Resource* constantsBuffer = nullptr; // Constants structured buffer resource pointer (device) - // Provide these resources if you use DDGIVolume::Upload() to transfer constants to the GPU + // Provide these resources if you use UploadDDGIVolumeConstants() to transfer constants to the GPU ID3D12Resource* constantsBufferUpload = nullptr; // [Optional] Constants structured buffer resource pointer (upload) UINT64 constantsBufferSizeInBytes = 0; // [Optional] Size (in bytes) of the constants structured buffer }; @@ -261,7 +261,7 @@ namespace rtxgi ID3D12Device* m_device = nullptr; // D3D12 device pointer #endif - // Constants (if you use the DDGIVolume::Upload() to transfer constants to the GPU) + // Constants (if you use UploadDDGIVolumeConstants() to transfer constants to the GPU) ID3D12Resource* m_constantsBuffer = nullptr; // Structured buffer that stores the volume's constants (device) ID3D12Resource* m_constantsBufferUpload = nullptr; // Structured buffer that stores the volume's constants (upload) UINT64 m_constantsBufferSizeInBytes = 0; // Size (in bytes) of the structured buffer that stores *all* volumes constants diff --git a/rtxgi-sdk/include/rtxgi/ddgi/gfx/DDGIVolume_VK.h b/rtxgi-sdk/include/rtxgi/ddgi/gfx/DDGIVolume_VK.h index 79a2226..951b379 100644 --- a/rtxgi-sdk/include/rtxgi/ddgi/gfx/DDGIVolume_VK.h +++ b/rtxgi-sdk/include/rtxgi/ddgi/gfx/DDGIVolume_VK.h @@ -142,7 +142,7 @@ namespace rtxgi VkBuffer constantsBuffer = nullptr; // Constants structured buffer (device) - // Provide these resources if you use DDGIVolume::Upload() to transfer constants to the GPU + // Provide these resources if you use UploadDDGIVolumeConstants() to transfer constants to the GPU VkBuffer constantsBufferUpload = nullptr; // [Optional] Constants structured buffer (upload) VkDeviceMemory constantsBufferUploadMemory = nullptr; // [Optional] Constants structured buffer memory (upload) uint64_t constantsBufferSizeInBytes = 0; // [Optional] Size (in bytes) of the constants structured buffer @@ -307,7 +307,7 @@ namespace rtxgi VkDescriptorPool m_descriptorPool = nullptr; // Vulkan descriptor pool handle #endif - // Constants (if you use the DDGIVolume::Upload() to transfer constants to the GPU) + // Constants (if you use UploadDDGIVolumeConstants() to transfer constants to the GPU) VkBuffer m_constantsBuffer = nullptr; // Structured buffer that stores the volume's constants (device) VkBuffer m_constantsBufferUpload = nullptr; // Structured buffer that stores the volume's constants (upload) VkDeviceMemory m_constantsBufferUploadMemory = nullptr; // Memory for the volume's constants upload structured buffer diff --git a/rtxgi-sdk/shaders/ddgi/Irradiance.hlsl b/rtxgi-sdk/shaders/ddgi/Irradiance.hlsl index 78521a9..05364be 100644 --- a/rtxgi-sdk/shaders/ddgi/Irradiance.hlsl +++ b/rtxgi-sdk/shaders/ddgi/Irradiance.hlsl @@ -38,12 +38,14 @@ float3 DDGIGetSurfaceBias(float3 surfaceNormal, float3 cameraDirection, DDGIVolu */ float DDGIGetVolumeBlendWeight(float3 worldPosition, DDGIVolumeDescGPU volume) { - // Get the volume's origin + // Get the volume's origin and extent float3 origin = volume.origin + (volume.probeScrollOffsets * volume.probeSpacing); - - // Get spatial delta between the world position and the volume float3 extent = (volume.probeSpacing * (volume.probeCounts - 1)) * 0.5f; - float3 position = abs(worldPosition - origin); + + // Get the delta between the (rotated volume) and the world-space position + float3 position = (worldPosition - origin); + position = abs(RTXGIQuaternionRotate(position, RTXGIQuaternionConjugate(volume.rotation))); + float3 delta = position - extent; if(all(delta < 0)) return 1.f; @@ -99,13 +101,8 @@ float3 DDGIGetVolumeIrradiance( int adjacentProbeIndex = DDGIGetScrollingProbeIndex(adjacentProbeCoords, volume); // Early Out: don't allow inactive probes to contribute to irradiance - if (volume.probeClassificationEnabled) - { - // Get the probe state - int2 probeDataCoords = DDGIGetProbeDataTexelCoords(adjacentProbeIndex, volume); - int probeState = resources.probeData[probeDataCoords].w; - if (probeState == RTXGI_DDGI_PROBE_STATE_INACTIVE) continue; - } + int probeState = DDGILoadProbeState(adjacentProbeIndex, resources.probeData, volume); + if (probeState == RTXGI_DDGI_PROBE_STATE_INACTIVE) continue; // Get the adjacent probe's world position float3 adjacentProbeWorldPosition = DDGIGetProbeWorldPosition(adjacentProbeCoords, volume, resources.probeData); diff --git a/rtxgi-sdk/shaders/ddgi/ProbeBlendingCS.hlsl b/rtxgi-sdk/shaders/ddgi/ProbeBlendingCS.hlsl index ccff9da..0b82f0a 100644 --- a/rtxgi-sdk/shaders/ddgi/ProbeBlendingCS.hlsl +++ b/rtxgi-sdk/shaders/ddgi/ProbeBlendingCS.hlsl @@ -406,13 +406,8 @@ void DDGIProbeBlendingCS(uint3 DispatchThreadID : SV_DispatchThreadID, uint Grou } // Early Out: don't blend rays for probes that are inactive - if(volume.probeClassificationEnabled) - { - // Get the probe state - int2 probeDataCoords = DDGIGetProbeDataTexelCoords(probeIndex, volume); - int probeState = ProbeData[probeDataCoords].w; - if (probeState == RTXGI_DDGI_PROBE_STATE_INACTIVE) return; - } + int probeState = DDGILoadProbeState(probeIndex, ProbeData, volume); + if (probeState == RTXGI_DDGI_PROBE_STATE_INACTIVE) return; // Visualize the probe index #if RTXGI_DDGI_BLEND_RADIANCE && RTXGI_DDGI_DEBUG_PROBE_INDEXING diff --git a/rtxgi-sdk/shaders/ddgi/ProbeClassificationCS.hlsl b/rtxgi-sdk/shaders/ddgi/ProbeClassificationCS.hlsl index a55d453..c71c43b 100644 --- a/rtxgi-sdk/shaders/ddgi/ProbeClassificationCS.hlsl +++ b/rtxgi-sdk/shaders/ddgi/ProbeClassificationCS.hlsl @@ -288,7 +288,7 @@ void DDGIProbeClassificationCS(uint3 DispatchThreadID : SV_DispatchThreadID) } // Get the probe's texel coordinates in the Probe Data texture - uint2 probeDataCoords = DDGIGetProbeDataTexelCoords(probeIndex, volume); + uint2 probeDataCoords = DDGIGetProbeTexelCoords(probeIndex, volume); // Early out: number of backface hits has been exceeded. The probe is probably inside geometry. if(((float)backfaceCount / (float)RTXGI_DDGI_NUM_FIXED_RAYS) > volume.probeBackfaceThreshold) @@ -369,7 +369,7 @@ void DDGIProbeClassificationResetCS(uint3 DispatchThreadID : SV_DispatchThreadID #endif // Get the probe's texel coordinates in the Probe Data texture - uint2 probeDataCoords = DDGIGetProbeDataTexelCoords(DispatchThreadID.x, volume); + uint2 probeDataCoords = DDGIGetProbeTexelCoords(DispatchThreadID.x, volume); ProbeData[probeDataCoords].w = RTXGI_DDGI_PROBE_STATE_ACTIVE; } diff --git a/rtxgi-sdk/shaders/ddgi/ProbeRelocationCS.hlsl b/rtxgi-sdk/shaders/ddgi/ProbeRelocationCS.hlsl index ba26c9b..b6c4224 100644 --- a/rtxgi-sdk/shaders/ddgi/ProbeRelocationCS.hlsl +++ b/rtxgi-sdk/shaders/ddgi/ProbeRelocationCS.hlsl @@ -268,7 +268,7 @@ void DDGIProbeRelocationCS(uint3 DispatchThreadID : SV_DispatchThreadID) #endif // Get the probe's texel coordinates in the Probe Data texture - uint2 coords = DDGIGetProbeDataTexelCoords(probeIndex, volume); + uint2 coords = DDGIGetProbeTexelCoords(probeIndex, volume); // Read the current world position offset float3 offset = DDGILoadProbeDataOffset(ProbeData, coords, volume); @@ -387,7 +387,7 @@ void DDGIProbeRelocationResetCS(uint3 DispatchThreadID : SV_DispatchThreadID) #endif // Get the probe's texel coordinates in the Probe Data texture - uint2 probeDataCoords = DDGIGetProbeDataTexelCoords(DispatchThreadID.x, volume); + uint2 probeDataCoords = DDGIGetProbeTexelCoords(DispatchThreadID.x, volume); // Write the probe offset ProbeData[probeDataCoords].xyz = float3(0.f, 0.f, 0.f); diff --git a/rtxgi-sdk/shaders/ddgi/include/ProbeCommon.hlsl b/rtxgi-sdk/shaders/ddgi/include/ProbeCommon.hlsl index a08e345..8856cfe 100644 --- a/rtxgi-sdk/shaders/ddgi/include/ProbeCommon.hlsl +++ b/rtxgi-sdk/shaders/ddgi/include/ProbeCommon.hlsl @@ -62,7 +62,7 @@ float3 DDGIGetProbeWorldPosition(int3 probeCoords, DDGIVolumeDescGPU volume, Tex int probeIndex = DDGIGetScrollingProbeIndex(probeCoords, volume); // Find the texture coordinates of the probe in the Probe Data texture - uint2 coords = DDGIGetProbeDataTexelCoords(probeIndex, volume); + uint2 coords = DDGIGetProbeTexelCoords(probeIndex, volume); // Load the probe's world-space position offset and add it to the current world position probeWorldPosition += DDGILoadProbeDataOffset(probeData, coords, volume); @@ -88,7 +88,7 @@ float3 DDGIGetProbeWorldPosition(int3 probeCoords, DDGIVolumeDescGPU volume, RWT int probeIndex = DDGIGetScrollingProbeIndex(probeCoords, volume); // Find the texture coordinates of the probe in the Probe Data texture - uint2 coords = DDGIGetProbeDataTexelCoords(probeIndex, volume); + uint2 coords = DDGIGetProbeTexelCoords(probeIndex, volume); // Load the probe's world-space position offset and add it to the current world position probeWorldPosition += DDGILoadProbeDataOffset(probeData, coords, volume); diff --git a/rtxgi-sdk/shaders/ddgi/include/ProbeIndexing.hlsl b/rtxgi-sdk/shaders/ddgi/include/ProbeIndexing.hlsl index 70b1894..660ae5e 100644 --- a/rtxgi-sdk/shaders/ddgi/include/ProbeIndexing.hlsl +++ b/rtxgi-sdk/shaders/ddgi/include/ProbeIndexing.hlsl @@ -172,22 +172,17 @@ int3 DDGIGetBaseProbeGridCoords(float3 worldPosition, DDGIVolumeDescGPU volume) //------------------------------------------------------------------------ /** - * Computes the normalized texture UVs for the Probe Irradiance and Probe Distance textures - * used in blending given the probe index and 2D normalized octant coordinates [-1, 1]. - * + * Computes the 2D texture coordinates of the probe at the given probe index. + * * When infinite scrolling is enbled, probeIndex is expected to be the scroll adjusted probe index. * Obtain the adjusted index with DDGIGetScrollingProbeIndex(). */ -float2 DDGIGetProbeUV(int probeIndex, float2 octantCoordinates, int numTexels, DDGIVolumeDescGPU volume) +uint2 DDGIGetProbeTexelCoords(int probeIndex, DDGIVolumeDescGPU volume) { // Find the probe's plane index int probesPerPlane = DDGIGetProbesPerPlane(volume.probeCounts); int planeIndex = int(probeIndex / probesPerPlane); - // Account for the border texels - float probeInteriorTexels = float(numTexels); - float probeTexels = (probeInteriorTexels + 2.f); - #if RTXGI_COORDINATE_SYSTEM == RTXGI_COORDINATE_SYSTEM_LEFT || RTXGI_COORDINATE_SYSTEM == RTXGI_COORDINATE_SYSTEM_RIGHT int gridSpaceX = (probeIndex % volume.probeCounts.x); int gridSpaceY = (probeIndex / volume.probeCounts.x); @@ -208,6 +203,25 @@ float2 DDGIGetProbeUV(int probeIndex, float2 octantCoordinates, int numTexels, D int y = gridSpaceY % volume.probeCounts.y; #endif + return uint2(x, y); +} + +/** + * Computes the normalized texture UVs for the Probe Irradiance and Probe Distance textures + * (used in blending) given the probe index and 2D normalized octant coordinates [-1, 1]. + * + * When infinite scrolling is enbled, probeIndex is expected to be the scroll adjusted probe index. + * Obtain the adjusted index with DDGIGetScrollingProbeIndex(). + */ +float2 DDGIGetProbeUV(int probeIndex, float2 octantCoordinates, int numTexels, DDGIVolumeDescGPU volume) +{ + // Get the probe's texel coordinates, assuming one texel per probe + uint2 coords = DDGIGetProbeTexelCoords(probeIndex, volume); + + // Adjust for the number of interior and border texels + float probeInteriorTexels = float(numTexels); + float probeTexels = (probeInteriorTexels + 2.f); + #if RTXGI_COORDINATE_SYSTEM == RTXGI_COORDINATE_SYSTEM_LEFT || RTXGI_COORDINATE_SYSTEM == RTXGI_COORDINATE_SYSTEM_RIGHT float textureWidth = probeTexels * (volume.probeCounts.x * volume.probeCounts.y); float textureHeight = probeTexels * volume.probeCounts.z; @@ -219,34 +233,33 @@ float2 DDGIGetProbeUV(int probeIndex, float2 octantCoordinates, int numTexels, D float textureHeight = probeTexels * volume.probeCounts.y; #endif - float2 uv = float2(x * probeTexels, y * probeTexels) + (probeTexels * 0.5f); + float2 uv = float2(coords.x * probeTexels, coords.y * probeTexels) + (probeTexels * 0.5f); uv += octantCoordinates.xy * (probeInteriorTexels * 0.5f); uv /= float2(textureWidth, textureHeight); return uv; } +//------------------------------------------------------------------------ +// Probe Classification +//------------------------------------------------------------------------ + /** - * Computes the 2D texture coordinates of the probe at the given probe index - * for the Probe Data texture used in relocation and classification. - * - * When infinite scrolling is enbled, probeIndex is expected to be the scroll adjusted probe index. - * Obtain the adjusted index with DDGIGetScrollingProbeIndex(). + * Loads and returns the probe's classification state (from a RWTexture2D). */ -uint2 DDGIGetProbeDataTexelCoords(int probeIndex, DDGIVolumeDescGPU volume) +float DDGILoadProbeState(int probeIndex, RWTexture2D probeData, DDGIVolumeDescGPU volume) { - // Compute the probe texel coordinates for this probe -#if RTXGI_COORDINATE_SYSTEM == RTXGI_COORDINATE_SYSTEM_LEFT || RTXGI_COORDINATE_SYSTEM == RTXGI_COORDINATE_SYSTEM_RIGHT - return uint2(probeIndex % (volume.probeCounts.x * volume.probeCounts.y), probeIndex / (volume.probeCounts.x * volume.probeCounts.y)); -#elif RTXGI_COORDINATE_SYSTEM == RTXGI_COORDINATE_SYSTEM_LEFT_Z_UP - return uint2(probeIndex % (volume.probeCounts.y * volume.probeCounts.z), probeIndex / (volume.probeCounts.y * volume.probeCounts.z)); -#elif RTXGI_COORDINATE_SYSTEM == RTXGI_COORDINATE_SYSTEM_RIGHT_Z_UP - return uint2(probeIndex % (volume.probeCounts.x * volume.probeCounts.z), probeIndex / (volume.probeCounts.x * volume.probeCounts.z)); -#endif -} + float state = RTXGI_DDGI_PROBE_STATE_ACTIVE; + if (volume.probeClassificationEnabled) + { + // Get the probe's texel coordinates in the Probe Data texture + int2 probeDataCoords = DDGIGetProbeTexelCoords(probeIndex, volume); -//------------------------------------------------------------------------ -// Probe Classification -//------------------------------------------------------------------------ + // Get the probe's classification state + state = probeData[probeDataCoords].w; + } + + return state; +} /** * Loads and returns the probe's classification state (from a Texture2D). @@ -257,7 +270,7 @@ float DDGILoadProbeState(int probeIndex, Texture2D probeData, DDGIVolume if (volume.probeClassificationEnabled) { // Get the probe's texel coordinates in the Probe Data texture - int2 probeDataCoords = DDGIGetProbeDataTexelCoords(probeIndex, volume); + int2 probeDataCoords = DDGIGetProbeTexelCoords(probeIndex, volume); // Get the probe's classification state state = probeData.Load(int3(probeDataCoords, 0)).w; diff --git a/samples/test-harness/CMakeLists.txt b/samples/test-harness/CMakeLists.txt index a49e52e..b6e0374 100644 --- a/samples/test-harness/CMakeLists.txt +++ b/samples/test-harness/CMakeLists.txt @@ -35,21 +35,25 @@ file(GLOB TEST_HARNESS_CONFIG ) file(GLOB TEST_HARNESS_SOURCE + "include/Benchmark.h" "include/Caches.h" "include/Common.h" "include/Configs.h" "include/Geometry.h" "include/Graphics.h" + "include/ImageCapture.h" "include/Inputs.h" "include/Instrumentation.h" "include/Scenes.h" "include/Shaders.h" "include/Textures.h" "include/Window.h" + "src/Benchmark.cpp" "src/Caches.cpp" "src/Configs.cpp" "src/Geometry.cpp" "src/Inputs.cpp" + "src/ImageCapture.cpp" "src/Instrumentation.cpp" "src/main.cpp" "src/Scenes.cpp" @@ -264,6 +268,7 @@ 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 -------------------------------------------------------------------------------------- @@ -294,7 +299,7 @@ if(RTXGI_API_D3D12_ENABLE) ) # Add dependencies - add_dependencies(${TARGET_EXE} glfw tinygltf) + add_dependencies(${TARGET_EXE} glfw tinygltf png_static zlibstatic) # Add the include directories target_include_directories(${TARGET_EXE} PRIVATE @@ -309,6 +314,7 @@ if(RTXGI_API_D3D12_ENABLE) ${IMGUI_INCLUDE} ${IMGUI_BACKENDS_INCLUDE} ${TINYGLTF_INCLUDE} + ${LIBPNG_INCLUDE} ) # Add statically linked libs @@ -318,6 +324,8 @@ if(RTXGI_API_D3D12_ENABLE) d3d11 d3d12 dxgi + ../../thirdparty/libpng/$/libpng16_static + ../../thirdparty/zlib/$/zlibstatic ) # Add common compiler definitions for exposed Test Harness options @@ -330,7 +338,7 @@ if(RTXGI_API_D3D12_ENABLE) set_target_properties(${TARGET_EXE} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ../bin/d3d12/$) # Set the default scene config - set_target_properties(${TARGET_EXE} PROPERTIES VS_DEBUGGER_COMMAND_ARGUMENTS ../../../samples/test-harness/config/cornell.ini) + set_target_properties(${TARGET_EXE} PROPERTIES VS_DEBUGGER_COMMAND_ARGUMENTS ${CMAKE_SOURCE_DIR}/samples/test-harness/config/cornell.ini) # Add to the samples folder set_target_properties(${TARGET_EXE} PROPERTIES FOLDER "RTXGI Samples") @@ -376,6 +384,7 @@ if(RTXGI_API_VULKAN_ENABLE) ${IMGUI_INCLUDE} ${IMGUI_BACKENDS_INCLUDE} ${TINYGLTF_INCLUDE} + ${LIBPNG_INCLUDE} ) # Add VS filters @@ -425,12 +434,13 @@ if(RTXGI_API_VULKAN_ENABLE) ${IMGUI_INCLUDE} ${IMGUI_BACKENDS_INCLUDE} ${TINYGLTF_INCLUDE} + ${LIBPNG_INCLUDE} ) endif() # Add dependencies - add_dependencies(${TARGET_EXE} glfw tinygltf) + add_dependencies(${TARGET_EXE} glfw tinygltf png_static zlibstatic) # Add compiler definitions target_compile_definitions(${TARGET_EXE} PUBLIC API_VULKAN) @@ -444,17 +454,17 @@ 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} ../../thirdparty/glfw/src/$/glfw3 d3d11) + target_link_libraries(${TARGET_EXE} RTXGI-VK ${Vulkan_LIBRARY} ../../thirdparty/glfw/src/$/glfw3 d3d11 ../../thirdparty/libpng/$/libpng16_static ../../thirdparty/zlib/$/zlibstatic) 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) + target_link_libraries(${TARGET_EXE} RTXGI-VK -llibpng16_static -lz -lglfw -lvulkan -ldl -lpthread -lX11 -lXrandr -lXi -lstdc++fs) endif() # Set the binary output directory set_target_properties(${TARGET_EXE} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ../bin/vulkan/$) # Set the default scene config - set_target_properties(${TARGET_EXE} PROPERTIES VS_DEBUGGER_COMMAND_ARGUMENTS ../../../samples/test-harness/config/cornell.ini) + set_target_properties(${TARGET_EXE} PROPERTIES VS_DEBUGGER_COMMAND_ARGUMENTS ${CMAKE_SOURCE_DIR}/samples/test-harness/config/cornell.ini) # Add to the samples folder set_target_properties(${TARGET_EXE} PROPERTIES FOLDER "RTXGI Samples") @@ -514,14 +524,14 @@ if(UNIX AND RTXGI_API_VULKAN_ENABLE) if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "aarch64") 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_SOURCE_DIR}/build/samples/test-harness + 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_SOURCE_DIR}/build/samples/test-harness + 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 ) diff --git a/samples/test-harness/config/cornell.ini b/samples/test-harness/config/cornell.ini index ec736bf..5b4ec11 100644 --- a/samples/test-harness/config/cornell.ini +++ b/samples/test-harness/config/cornell.ini @@ -27,14 +27,14 @@ scene.lights.0.radius=4.0 scene.lights.1.name=Sun scene.lights.1.type=0 -scene.lights.1.direction=0.6 -0.435, -0.816 # right hand, y-up +scene.lights.1.direction=0.6 -0.435 -0.816 # right hand, y-up scene.lights.1.color=1.0 1.0 1.0 scene.lights.1.power=1.0 scene.lights.2.name=Spot Light 1 scene.lights.2.type=1 scene.lights.2.position=0.0 1.0 0.5 -scene.lights.2.direction=0.6 -0.435, -0.816 # right hand, y-up +scene.lights.2.direction=0.6 -0.435 -0.816 # right hand, y-up scene.lights.2.color=1.0 1.0 1.0 scene.lights.2.power=0.0 scene.lights.2.radius=4.0 @@ -96,8 +96,8 @@ ddgi.volume.0.vis.showProbes=1 ddgi.volume.0.vis.texture.rayDataScale=0.75 ddgi.volume.0.vis.texture.irradianceScale=2.1 ddgi.volume.0.vis.texture.distanceScale=1.05 -ddgi.volume.0.vis.texture.relocationOffsetScale=5.9 -ddgi.volume.0.vis.texture.classificationStateScale=5.9 +ddgi.volume.0.vis.texture.relocationOffsetScale=16.8 +ddgi.volume.0.vis.texture.classificationStateScale=16.8 # ray traced ambient occlusion rtao.enable=1 diff --git a/samples/test-harness/config/furnace.ini b/samples/test-harness/config/furnace.ini index 118238b..91d8fe2 100644 --- a/samples/test-harness/config/furnace.ini +++ b/samples/test-harness/config/furnace.ini @@ -53,8 +53,8 @@ ddgi.volume.0.textures.rayData.format=0 ddgi.volume.0.textures.irradiance.format=0 ddgi.volume.0.textures.distance.format=0 ddgi.volume.0.textures.data.format=0 -ddgi.volume.0.origin=0.0, 0.5, 0.0 # left and right hand, y-up -ddgi.volume.0.probeCounts=8, 3, 8 +ddgi.volume.0.origin=0.0 0.5 0.0 # left and right hand, y-up +ddgi.volume.0.probeCounts=8 3 8 ddgi.volume.0.probeSpacing=2 1 2 ddgi.volume.0.probeNumRays=288 ddgi.volume.0.probeNumIrradianceTexels=6 @@ -68,9 +68,9 @@ ddgi.volume.0.probeBrightnessThreshold=1.0 ddgi.volume.0.vis.probeRadius=0.2 ddgi.volume.0.vis.probeDistanceDivisor=30 ddgi.volume.0.vis.showProbes=1 -ddgi.volume.0.vis.texture.rayDataScale=0.25 +ddgi.volume.0.vis.texture.rayDataScale=1.0 ddgi.volume.0.vis.texture.irradianceScale=1.0 -ddgi.volume.0.vis.texture.distanceScale=1.3 +ddgi.volume.0.vis.texture.distanceScale=1.0 ddgi.volume.0.vis.texture.relocationOffsetScale=7.0 ddgi.volume.0.vis.texture.classificationStateScale=7.0 diff --git a/samples/test-harness/config/multi-cornell.ini b/samples/test-harness/config/multi-cornell.ini index 124360c..a4ffe0f 100644 --- a/samples/test-harness/config/multi-cornell.ini +++ b/samples/test-harness/config/multi-cornell.ini @@ -72,8 +72,8 @@ ddgi.volume.0.vis.showProbes=1 ddgi.volume.0.vis.texture.rayDataScale=0.75 ddgi.volume.0.vis.texture.irradianceScale=2.1 ddgi.volume.0.vis.texture.distanceScale=1.05 -ddgi.volume.0.vis.texture.relocationOffsetScale=5.9 -ddgi.volume.0.vis.texture.classificationStateScale=5.9 +ddgi.volume.0.vis.texture.relocationOffsetScale=16.81 +ddgi.volume.0.vis.texture.classificationStateScale=16.81 ddgi.volume.1.name=Cornell Box 2 Volume ddgi.volume.1.probeRelocation.enabled=1 @@ -103,8 +103,8 @@ ddgi.volume.1.vis.showProbes=1 ddgi.volume.1.vis.texture.rayDataScale=0.75 ddgi.volume.1.vis.texture.irradianceScale=2.1 ddgi.volume.1.vis.texture.distanceScale=1.05 -ddgi.volume.1.vis.texture.relocationOffsetScale=5.9 -ddgi.volume.1.vis.texture.classificationStateScale=5.9 +ddgi.volume.1.vis.texture.relocationOffsetScale=16.81 +ddgi.volume.1.vis.texture.classificationStateScale=16.81 ddgi.volume.2.name=Cornell Box 3 Volume ddgi.volume.2.probeRelocation.enabled=1 @@ -134,8 +134,8 @@ ddgi.volume.2.vis.showProbes=1 ddgi.volume.2.vis.texture.rayDataScale=0.75 ddgi.volume.2.vis.texture.irradianceScale=2.1 ddgi.volume.2.vis.texture.distanceScale=1.05 -ddgi.volume.2.vis.texture.relocationOffsetScale=5.9 -ddgi.volume.2.vis.texture.classificationStateScale=5.9 +ddgi.volume.2.vis.texture.relocationOffsetScale=16.81 +ddgi.volume.2.vis.texture.classificationStateScale=16.81 ddgi.volume.3.name=Cornell Box 4 Volume ddgi.volume.3.probeRelocation.enabled=1 @@ -165,8 +165,8 @@ ddgi.volume.3.vis.showProbes=1 ddgi.volume.3.vis.texture.rayDataScale=0.75 ddgi.volume.3.vis.texture.irradianceScale=2.1 ddgi.volume.3.vis.texture.distanceScale=1.05 -ddgi.volume.3.vis.texture.relocationOffsetScale=5.9 -ddgi.volume.3.vis.texture.classificationStateScale=5.9 +ddgi.volume.3.vis.texture.relocationOffsetScale=16.81 +ddgi.volume.3.vis.texture.classificationStateScale=16.81 ddgi.volume.4.name=Cornell Box 5 Volume ddgi.volume.4.probeRelocation.enabled=1 @@ -196,8 +196,8 @@ ddgi.volume.4.vis.showProbes=1 ddgi.volume.4.vis.texture.rayDataScale=0.75 ddgi.volume.4.vis.texture.irradianceScale=2.1 ddgi.volume.4.vis.texture.distanceScale=1.05 -ddgi.volume.4.vis.texture.relocationOffsetScale=5.9 -ddgi.volume.4.vis.texture.classificationStateScale=5.9 +ddgi.volume.4.vis.texture.relocationOffsetScale=16.81 +ddgi.volume.4.vis.texture.classificationStateScale=16.81 ddgi.volume.5.name=Cornell Box 6 Volume ddgi.volume.5.probeRelocation.enabled=1 @@ -227,8 +227,8 @@ ddgi.volume.5.vis.showProbes=1 ddgi.volume.5.vis.texture.rayDataScale=0.75 ddgi.volume.5.vis.texture.irradianceScale=2.1 ddgi.volume.5.vis.texture.distanceScale=1.05 -ddgi.volume.5.vis.texture.relocationOffsetScale=5.9 -ddgi.volume.5.vis.texture.classificationStateScale=5.9 +ddgi.volume.5.vis.texture.relocationOffsetScale=16.81 +ddgi.volume.5.vis.texture.classificationStateScale=16.81 # ray traced ambient occlusion rtao.enable=1 diff --git a/samples/test-harness/config/sponza.ini b/samples/test-harness/config/sponza.ini index 4d3fd4b..6f815a8 100644 --- a/samples/test-harness/config/sponza.ini +++ b/samples/test-harness/config/sponza.ini @@ -77,7 +77,7 @@ ddgi.volume.0.vis.probeRadius=0.1 ddgi.volume.0.vis.probeAlpha=0.9 ddgi.volume.0.vis.probeDistanceDivisor=200 ddgi.volume.0.vis.showProbes=1 -ddgi.volume.0.vis.texture.rayDataScale=0.067 +ddgi.volume.0.vis.texture.rayDataScale=0.4 ddgi.volume.0.vis.texture.irradianceScale=0.395 ddgi.volume.0.vis.texture.distanceScale=0.24 ddgi.volume.0.vis.texture.relocationOffsetScale=3.9 diff --git a/samples/test-harness/config/tunnel.ini b/samples/test-harness/config/tunnel.ini index 1b4f64d..176bbc3 100644 --- a/samples/test-harness/config/tunnel.ini +++ b/samples/test-harness/config/tunnel.ini @@ -20,7 +20,7 @@ scene.skyIntensity=1.0 # scene lights scene.lights.0.name=Sun scene.lights.0.type=0 -scene.lights.0.direction=1.0 -1.0, -0.7 # right hand, y-up +scene.lights.0.direction=1.0 -1.0 -0.7 # right hand, y-up scene.lights.0.color=1.0 1.0 1.0 scene.lights.0.power=2.2 @@ -70,10 +70,10 @@ ddgi.volume.0.vis.probeRadius=1.0 ddgi.volume.0.vis.probeDistanceDivisor=3 ddgi.volume.0.vis.showProbes=1 ddgi.volume.0.vis.texture.rayDataScale=0.3 -ddgi.volume.0.vis.texture.irradianceScale=0.805 -ddgi.volume.0.vis.texture.distanceScale=0.403 -ddgi.volume.0.vis.texture.relocationOffsetScale=4.5 -ddgi.volume.0.vis.texture.classificationStateScale=4.5 +ddgi.volume.0.vis.texture.irradianceScale=0.8 +ddgi.volume.0.vis.texture.distanceScale=0.4 +ddgi.volume.0.vis.texture.relocationOffsetScale=6.4 +ddgi.volume.0.vis.texture.classificationStateScale=6.4 # ray traced ambient occlusion rtao.enable=1 diff --git a/samples/test-harness/config/two-rooms.ini b/samples/test-harness/config/two-rooms.ini index 400cc29..1b125de 100644 --- a/samples/test-harness/config/two-rooms.ini +++ b/samples/test-harness/config/two-rooms.ini @@ -95,11 +95,11 @@ ddgi.volume.0.probeBrightnessThreshold=2.0 ddgi.volume.0.vis.probeRadius=2.0 ddgi.volume.0.vis.probeDistanceDivisor=30 ddgi.volume.0.vis.showProbes=1 -ddgi.volume.0.vis.texture.rayDataScale=0.25 -ddgi.volume.0.vis.texture.irradianceScale=1.2 -ddgi.volume.0.vis.texture.distanceScale=0.6 -ddgi.volume.0.vis.texture.relocationOffsetScale=4.0 -ddgi.volume.0.vis.texture.classificationStateScale=4.0 +ddgi.volume.0.vis.texture.rayDataScale=0.15 +ddgi.volume.0.vis.texture.irradianceScale=0.7 +ddgi.volume.0.vis.texture.distanceScale=0.35 +ddgi.volume.0.vis.texture.relocationOffsetScale=5.6 +ddgi.volume.0.vis.texture.classificationStateScale=5.6 # ray traced ambient occlusion rtao.enable=1 diff --git a/samples/test-harness/include/Benchmark.h b/samples/test-harness/include/Benchmark.h new file mode 100644 index 0000000..c44cd15 --- /dev/null +++ b/samples/test-harness/include/Benchmark.h @@ -0,0 +1,30 @@ +/* +* 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 +* and any modifications thereto. Any use, reproduction, disclosure or +* distribution of this software and related documentation without an express +* license agreement from NVIDIA CORPORATION is strictly prohibited. +*/ + +#pragma once + +#include "Instrumentation.h" +#include "Configs.h" +#include "Graphics.h" + +#include + +namespace Benchmark +{ + const static uint32_t NumBenchmarkFrames = 1024; + + struct BenchmarkRun + { + std::stringstream cpuTimingCsv; + std::stringstream gpuTimingCsv; + }; + void StartBenchmark(BenchmarkRun& benchmarkRun, Instrumentation::Performance& perf, Configs::Config& config, Graphics::Globals& gfx); + bool UpdateBenchmark(BenchmarkRun& benchmarkRun, Instrumentation::Performance& perf, Configs::Config& config, Graphics::Globals& gfx, std::ofstream& log); +} \ No newline at end of file diff --git a/samples/test-harness/include/Configs.h b/samples/test-harness/include/Configs.h index 9826d2f..d41cc6c 100644 --- a/samples/test-harness/include/Configs.h +++ b/samples/test-harness/include/Configs.h @@ -33,6 +33,7 @@ namespace Configs { std::string name = ""; uint32_t index = 0; + uint32_t rngSeed = 0; bool insertPerfMarkers = false; bool showProbes = false; diff --git a/samples/test-harness/include/Graphics.h b/samples/test-harness/include/Graphics.h index 7a8e19c..6580f44 100644 --- a/samples/test-harness/include/Graphics.h +++ b/samples/test-harness/include/Graphics.h @@ -62,7 +62,7 @@ namespace Graphics bool UpdateTimestamps(Globals& gfx, GlobalResources& gfxResources, Instrumentation::Performance& performance); #endif - bool WriteGBufferToDisk(Globals& gfx, GlobalResources& gfxResources, std::string directory); + bool WriteBackBufferToDisk(Globals& gfx, std::string directory); namespace BindlessResourceOffsets { diff --git a/samples/test-harness/include/ImageCapture.h b/samples/test-harness/include/ImageCapture.h new file mode 100644 index 0000000..f36ff4c --- /dev/null +++ b/samples/test-harness/include/ImageCapture.h @@ -0,0 +1,20 @@ +/* +* 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 +* and any modifications thereto. Any use, reproduction, disclosure or +* distribution of this software and related documentation without an express +* license agreement from NVIDIA CORPORATION is strictly prohibited. +*/ + +#pragma once + +#include +#include +#include + +namespace ImageCapture +{ + bool CapturePng(std::string file, uint32_t width, uint32_t height, std::vector& rows); +} \ No newline at end of file diff --git a/samples/test-harness/include/Inputs.h b/samples/test-harness/include/Inputs.h index f7262e3..af4e7f0 100644 --- a/samples/test-harness/include/Inputs.h +++ b/samples/test-harness/include/Inputs.h @@ -24,6 +24,7 @@ namespace Inputs SAVE_IMAGE, CAMERA_MOVEMENT, FULLSCREEN_CHANGE, + RUN_BENCHMARK, COUNT }; @@ -34,7 +35,7 @@ namespace Inputs DirectX::XMINT2 prevMousePos = { INT_MAX, INT_MAX }; bool mouseLeftBtnDown = false; bool mouseRightBtnDown = false; - bool saveImages = false; + bool runBenchmark = false; }; bool Initialize(GLFWwindow* window, Input& input, Configs::Config& config, Scenes::Scene& scene); diff --git a/samples/test-harness/include/Instrumentation.h b/samples/test-harness/include/Instrumentation.h index 28885e7..91dddf5 100644 --- a/samples/test-harness/include/Instrumentation.h +++ b/samples/test-harness/include/Instrumentation.h @@ -14,6 +14,7 @@ #include #include +#include namespace Instrumentation { @@ -42,12 +43,14 @@ namespace Instrumentation this->sampleSize = sampleSize; } + const static uint32_t FallbackSampleSize = 10; + std::string name = ""; EStatType type; uint32_t index = 0; uint64_t timestamp = 0; - uint32_t sampleSize = 10; + uint32_t sampleSize = FallbackSampleSize; double elapsed = 0; // milliseconds double average = 0; double total = 0; @@ -56,6 +59,15 @@ namespace Instrumentation uint32_t GetQueryBeginIndex() { return (index * 2); } uint32_t GetQueryEndIndex() { return (index * 2) + 1; } + void Reset(uint32_t sampleSize = FallbackSampleSize) + { + elapsed = 0; + average = 0; + total = 0; + samples = {}; + this->sampleSize = sampleSize; + } + }; struct Performance @@ -63,27 +75,42 @@ namespace Instrumentation std::vector gpuTimes; std::vector cpuTimes; + const static uint32_t DefaultSampleSize = 50; + uint32_t GetNumGPUQueries() { return static_cast(gpuTimes.size() * 2); }; - Stat*& AddCPUStat(std::string name, uint32_t sampleSize = 50) + Stat*& AddCPUStat(std::string name, uint32_t sampleSize = DefaultSampleSize) { cpuTimes.emplace_back(new Stat(EStatType::CPU, name, sampleSize)); return cpuTimes.back(); } - Stat*& AddGPUStat(std::string name, uint32_t sampleSize = 50) + Stat*& AddGPUStat(std::string name, uint32_t sampleSize = DefaultSampleSize) { uint32_t index = static_cast(gpuTimes.size()); gpuTimes.emplace_back(new Stat(EStatType::GPU, index, name, sampleSize)); return gpuTimes.back(); } - void AddStat(std::string name, Stat*& cpu, Stat*& gpu, uint32_t sampleSize = 50) + void AddStat(std::string name, Stat*& cpu, Stat*& gpu, uint32_t sampleSize = DefaultSampleSize) { cpu = AddCPUStat(name, sampleSize); gpu = AddGPUStat(name, sampleSize); } + void Reset(uint32_t sampleSize = DefaultSampleSize) + { + for (Stat* stat : cpuTimes) + { + stat->Reset(sampleSize); + } + + for (Stat* stat : gpuTimes) + { + stat->Reset(sampleSize); + } + } + void Cleanup() { size_t index; @@ -102,6 +129,7 @@ namespace Instrumentation cpuTimes.clear(); gpuTimes.clear(); } + }; void Begin(Stat* s); @@ -109,6 +137,9 @@ namespace Instrumentation void Resolve(Stat* s); void EndAndResolve(Stat* s); + std::ostream& operator<<(std::ostream& os, const Stat& stat); + std::ostream& operator<<(std::ostream& os, std::vector& stats); + } #define CPU_TIMESTAMP_BEGIN(x) Begin(x) diff --git a/samples/test-harness/include/Vulkan.h b/samples/test-harness/include/Vulkan.h index 0d48dc7..1408a79 100644 --- a/samples/test-harness/include/Vulkan.h +++ b/samples/test-harness/include/Vulkan.h @@ -397,8 +397,7 @@ namespace Graphics void BeginRenderPass(Globals& vk); - /*bool WriteResourceImageToDisk(Globals& gfx, ID3D12Resource* pResource, std::string folder, std::string filename, D3D12_RESOURCE_STATES state); - bool WriteResourceImagesToDisk(Globals& gfx, Resources& resources, std::string folder);*/ + bool WriteResourceToDisk(Globals& vk, std::string file, VkImage image, uint32_t width, uint32_t height, VkFormat imageFormat, VkImageLayout originalLayout); #ifdef GFX_NAME_OBJECTS void SetObjectName(VkDevice device, uint64_t handle, const char* name, VkObjectType type); diff --git a/samples/test-harness/include/graphics/DDGI.h b/samples/test-harness/include/graphics/DDGI.h index 3ede156..f7a5fab 100644 --- a/samples/test-harness/include/graphics/DDGI.h +++ b/samples/test-harness/include/graphics/DDGI.h @@ -36,5 +36,7 @@ namespace Graphics void AddCommonShaderDefines(Shaders::ShaderProgram& shader, const DDGIVolumeDesc& volumeDesc, bool spirv); bool CompileDDGIVolumeShaders(Globals& vk, const DDGIVolumeDesc& volumeDesc, std::vector& volumeShaders, bool spirv, std::ofstream& log); + + bool WriteVolumesToDisk(Globals& globals, GlobalResources& gfxResources, Resources& resources, std::string directory); } } diff --git a/samples/test-harness/include/graphics/RTAO.h b/samples/test-harness/include/graphics/RTAO.h index e7bdf4f..f883ea1 100644 --- a/samples/test-harness/include/graphics/RTAO.h +++ b/samples/test-harness/include/graphics/RTAO.h @@ -32,5 +32,6 @@ namespace Graphics void Update(Globals& globals, GlobalResources& gfxResources, Resources& resources, const Configs::Config& config); void Execute(Globals& globals, GlobalResources& gfxResources, Resources& resources); void Cleanup(Globals& globals, Resources& resources); + bool WriteRTAOBuffersToDisk(Globals& globals, GlobalResources& gfxResources, Resources& resources, std::string directory); } } diff --git a/samples/test-harness/shaders/ddgi/ProbeTraceRGS.hlsl b/samples/test-harness/shaders/ddgi/ProbeTraceRGS.hlsl index 12e2271..576e372 100644 --- a/samples/test-harness/shaders/ddgi/ProbeTraceRGS.hlsl +++ b/samples/test-harness/shaders/ddgi/ProbeTraceRGS.hlsl @@ -107,7 +107,7 @@ void RayGen() // Early out: a "fixed" ray hit a front facing surface. Fixed rays are not blended since their direction // is not random and they would bias the irradiance estimate. Don't perform lighting for these rays. - if(volume.probeClassificationEnabled && rayIndex < RTXGI_DDGI_NUM_FIXED_RAYS) + if((volume.probeRelocationEnabled || volume.probeClassificationEnabled) && rayIndex < RTXGI_DDGI_NUM_FIXED_RAYS) { // Store the ray front face hit distance (only) DDGIStoreProbeRayFrontfaceHit(RayData, texCoords, volume, payload.hitT); diff --git a/samples/test-harness/shaders/ddgi/visualizations/ProbesRGS.hlsl b/samples/test-harness/shaders/ddgi/visualizations/ProbesRGS.hlsl index fd19678..d71cb02 100644 --- a/samples/test-harness/shaders/ddgi/visualizations/ProbesRGS.hlsl +++ b/samples/test-harness/shaders/ddgi/visualizations/ProbesRGS.hlsl @@ -164,7 +164,7 @@ void RayGen() // Get the probe's location in the probe data texture Texture2D ProbeData = GetDDGIVolumeProbeDataSRV(volumeIndex); - uint2 probeStateTexCoords = DDGIGetProbeDataTexelCoords(probeIndex, volume); + uint2 probeStateTexCoords = DDGIGetProbeTexelCoords(probeIndex, volume); // Get the probe's state float probeState = ProbeData[probeStateTexCoords].w; @@ -257,7 +257,7 @@ void RayGenHideInactive() // Get the probe's state Texture2D ProbeData = GetDDGIVolumeProbeDataSRV(volumeIndex); - uint2 probeStateTexCoords = DDGIGetProbeDataTexelCoords(probeIndex, volume); + uint2 probeStateTexCoords = DDGIGetProbeTexelCoords(probeIndex, volume); float probeState = ProbeData[probeStateTexCoords].w; if(probeState == RTXGI_DDGI_PROBE_STATE_INACTIVE) continue; diff --git a/samples/test-harness/shaders/ddgi/visualizations/VolumeTexturesCS.hlsl b/samples/test-harness/shaders/ddgi/visualizations/VolumeTexturesCS.hlsl index f33220f..2464bea 100644 --- a/samples/test-harness/shaders/ddgi/visualizations/VolumeTexturesCS.hlsl +++ b/samples/test-harness/shaders/ddgi/visualizations/VolumeTexturesCS.hlsl @@ -114,36 +114,9 @@ void CS(uint3 DispatchThreadID : SV_DispatchThreadID) return; } - // Ray Data - float2 radianceRect = float2(DDGIVolume.probeNumRays, DDGIVolume.probeCounts.x * DDGIVolume.probeCounts.y * DDGIVolume.probeCounts.z) *GetGlobalConst(ddgivis, rayDataTextureScale); - xmax = radianceRect.x; - ymin += distanceRect.y + 5; - ymax = (ymin + radianceRect.y); - if (DispatchThreadID.x <= xmax && DispatchThreadID.y > ymin && DispatchThreadID.y <= ymax) - { - Texture2D RayData = GetDDGIVolumeRayDataSRV(volumeIndex); - - // Sample the ray data texture - coords = float2(DispatchThreadID.x, (DispatchThreadID.y - ymin)) / radianceRect.xy; - - if(DDGIVolume.probeRayDataFormat == RTXGI_DDGI_FORMAT_PROBE_RAY_DATA_R32G32B32A32_FLOAT) - { - color = RayData.SampleLevel(PointClampSampler, coords, 0).rgb; - } - else if(DDGIVolume.probeRayDataFormat == RTXGI_DDGI_FORMAT_PROBE_RAY_DATA_R32G32_FLOAT) - { - color = RTXGIUintToFloat3(asuint(RayData.SampleLevel(PointClampSampler, coords, 0).r)); - } - - // Overwrite GBufferA's albedo and mark the pixel to not be lit - GBufferA[DispatchThreadID.xy] = float4(color, 0.f); - - return; - } - // Relocation offsets float2 offsetRect = 0; - ymin += radianceRect.y + 5; + ymin += distanceRect.y + 5; if (DDGIVolume.probeRelocationEnabled) { offsetRect = numProbes.xy * GetGlobalConst(ddgivis, relocationOffsetTextureScale); @@ -204,4 +177,31 @@ void CS(uint3 DispatchThreadID : SV_DispatchThreadID) } } + // Ray Data + float2 radianceRect = float2(DDGIVolume.probeNumRays, DDGIVolume.probeCounts.x * DDGIVolume.probeCounts.y * DDGIVolume.probeCounts.z) *GetGlobalConst(ddgivis, rayDataTextureScale); + xmax = radianceRect.x; + ymin = ymax + 5; + ymax = (ymin + radianceRect.y); + if (DispatchThreadID.x <= xmax && DispatchThreadID.y > ymin && DispatchThreadID.y <= ymax) + { + Texture2D RayData = GetDDGIVolumeRayDataSRV(volumeIndex); + + // Sample the ray data texture + coords = float2(DispatchThreadID.x, (DispatchThreadID.y - ymin)) / radianceRect.xy; + + if (DDGIVolume.probeRayDataFormat == RTXGI_DDGI_FORMAT_PROBE_RAY_DATA_R32G32B32A32_FLOAT) + { + color = RayData.SampleLevel(PointClampSampler, coords, 0).rgb; + } + else if (DDGIVolume.probeRayDataFormat == RTXGI_DDGI_FORMAT_PROBE_RAY_DATA_R32G32_FLOAT) + { + color = RTXGIUintToFloat3(asuint(RayData.SampleLevel(PointClampSampler, coords, 0).r)); + } + + // Overwrite GBufferA's albedo and mark the pixel to not be lit + GBufferA[DispatchThreadID.xy] = float4(color, 0.f); + + return; + } + } diff --git a/samples/test-harness/src/Benchmark.cpp b/samples/test-harness/src/Benchmark.cpp new file mode 100644 index 0000000..8cac600 --- /dev/null +++ b/samples/test-harness/src/Benchmark.cpp @@ -0,0 +1,92 @@ +/* +* 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 +* and any modifications thereto. Any use, reproduction, disclosure or +* distribution of this software and related documentation without an express +* license agreement from NVIDIA CORPORATION is strictly prohibited. +*/ + +#include "Benchmark.h" + +namespace Benchmark +{ + void StartBenchmark(BenchmarkRun& benchmarkRun, Instrumentation::Performance& perf, Configs::Config& config, Graphics::Globals& gfx) + { + benchmarkRun.cpuTimingCsv.str(""); + benchmarkRun.gpuTimingCsv.str(""); + // Clear timer history if beginning benchmark mode, this should not break timers that are currently running + perf.Reset(NumBenchmarkFrames); + if (config.app.renderMode == ERenderMode::DDGI) + { + // reload ddgi configs in order to reset RNG state + config.ddgi.reload = true; + } + gfx.frameNumber = 1; + } + + bool UpdateBenchmark(BenchmarkRun& benchmarkRun, Instrumentation::Performance& perf, Configs::Config& config, Graphics::Globals& gfx, std::ofstream& log) + { + // if benchmark is currently running, make a csv row for the frame's timings + benchmarkRun.cpuTimingCsv << gfx.frameNumber << ","; + benchmarkRun.gpuTimingCsv << gfx.frameNumber << ","; + + // print the timer values to the row + benchmarkRun.cpuTimingCsv << perf.cpuTimes; + benchmarkRun.gpuTimingCsv << perf.gpuTimes; + + // if benchmark is done, print the timing results to files + if (gfx.frameNumber >= NumBenchmarkFrames) + { + // generate the header row of the csvs + std::stringstream header; + header << "FrameIndex,"; + for (std::vector::const_iterator& it = perf.cpuTimes.cbegin(); it != perf.cpuTimes.cend(); it++) + { + Instrumentation::Stat* stat = *it; + header << stat->name << ","; + } + std::string cpuHeader = header.str(); + + header.str(""); + header << "FrameIndex,"; + for (std::vector::const_iterator& it = perf.gpuTimes.cbegin(); it != perf.gpuTimes.cend(); it++) + { + Instrumentation::Stat* stat = *it; + header << stat->name << ","; + } + std::string gpuHeader = header.str(); + + // write csv to file + std::ofstream csv; + csv.open(config.scene.screenshotPath + "\\benchmarkCpu.csv", std::ios::out); + if (csv.is_open()) + { + csv << cpuHeader << std::endl << benchmarkRun.cpuTimingCsv.str(); + } + csv.close(); + csv.open(config.scene.screenshotPath + "\\benchmarkGpu.csv", std::ios::out); + if (csv.is_open()) + { + csv << gpuHeader << std::endl << benchmarkRun.gpuTimingCsv.str(); + } + csv.close(); + log << "wrote benchmark results to csv." << std::endl; + + // print averages to the log file + log << "Benchmark Timings:" << std::endl; + for (Instrumentation::Stat* stat : perf.cpuTimes) + { + log << "\t" << stat->name << "=" << stat->average << "ms(CPU)" << std::endl;; + } + for (Instrumentation::Stat* stat : perf.gpuTimes) + { + log << "\t" << stat->name << "=" << stat->average << "ms(GPU)" << std::endl;; + } + + return false; + } + return true; + } +} \ No newline at end of file diff --git a/samples/test-harness/src/Configs.cpp b/samples/test-harness/src/Configs.cpp index b961ac2..fb42656 100644 --- a/samples/test-harness/src/Configs.cpp +++ b/samples/test-harness/src/Configs.cpp @@ -172,6 +172,7 @@ namespace Configs if (tokens[3].compare("probeMaxRayDistance") == 0) { Store(data, config.ddgi.volumes[volumeIndex].probeMaxRayDistance); return true; } if (tokens[3].compare("probeIrradianceThreshold") == 0) { Store(data, config.ddgi.volumes[volumeIndex].probeIrradianceThreshold); return true; } if (tokens[3].compare("probeBrightnessThreshold") == 0) { Store(data, config.ddgi.volumes[volumeIndex].probeBrightnessThreshold); return true; } + if (tokens[3].compare("rngSeed") == 0) { Store(data, config.ddgi.volumes[volumeIndex].rngSeed); return true; } if (tokens[3].compare("probeRelocation") == 0) { diff --git a/samples/test-harness/src/Direct3D12.cpp b/samples/test-harness/src/Direct3D12.cpp index 0b3f9ae..d4956fe 100644 --- a/samples/test-harness/src/Direct3D12.cpp +++ b/samples/test-harness/src/Direct3D12.cpp @@ -10,6 +10,7 @@ #include "Graphics.h" #include "UI.h" +#include "ImageCapture.h" #include #include @@ -1592,6 +1593,25 @@ 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. */ @@ -1599,7 +1619,208 @@ namespace Graphics { 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; + //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 + const D3D12_RESOURCE_DESC desc = pResource->GetDesc(); + UINT64 totalResourceSize = 0, fpRowPitch = 0; + UINT fpRowCount = 0; + // Get the rowcount, pitch and size of the top mip + d3d.device->GetCopyableFootprints( + &desc, + 0, + 1, + 0, + nullptr, + &fpRowCount, + &fpRowPitch, + &totalResourceSize); + // Round up the srcPitch to multiples of 256 + UINT64 dstRowPitch = (fpRowPitch + 255) & ~0xFF; + ID3D12Resource* pStaging = nullptr; + + D3D12_HEAP_PROPERTIES sourceHeapProperties = {}; + D3D12_HEAP_FLAGS sourceHeapFlags = {}; + HRESULT hr = pResource->GetHeapProperties(&sourceHeapProperties, &sourceHeapFlags); + ID3D12CommandAllocator* commandAlloc = nullptr; + hr = d3d.device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&commandAlloc)); + ID3D12GraphicsCommandList* commandList = nullptr; + hr = d3d.device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, commandAlloc, nullptr, IID_PPV_ARGS(&commandList)); + 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 + 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 = dstRowPitch * desc.Height; + bufferDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + bufferDesc.MipLevels = 1; + bufferDesc.SampleDesc.Count = 1; + bufferDesc.SampleDesc.Quality = 0; + // Create a staging texture + hr = d3d.device->CreateCommittedResource( + &readBackHeapProperties, + D3D12_HEAP_FLAG_NONE, + &bufferDesc, + D3D12_RESOURCE_STATE_COPY_DEST, + nullptr, + 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); + } + + // 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(dstRowPitch); + bufferFootprint.Footprint.Format = desc.Format; + + D3D12_TEXTURE_COPY_LOCATION copySrc = {}, copyDest = {}; + copySrc.pResource = pResource; + copySrc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; + copySrc.SubresourceIndex = 0; + copyDest.pResource = pStaging; + copyDest.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; + copyDest.PlacedFootprint = bufferFootprint; + + // Copy the texture + 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); + } + + hr = commandList->Close(); + + // Execute the command list + d3d.cmdQueue->ExecuteCommandLists(1, reinterpret_cast(&commandList)); + // Signal the fence + hr = d3d.cmdQueue->Signal(fence, 1); + // Block until the copy is complete + while (fence->GetCompletedValue() < 1) + SwitchToThread(); + + 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); + + // 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; + + 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); + } + + 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; } @@ -2324,6 +2545,15 @@ namespace Graphics Cleanup(d3d); } + /** + * Write the back buffer texture resources to disk. + */ + 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; + } } /** @@ -2435,4 +2665,12 @@ namespace Graphics { Graphics::D3D12::Cleanup(gfx, gfxResources); } + + /** + * Write the back buffer texture resources to disk. + */ + bool WriteBackBufferToDisk(Globals& d3d, std::string directory) + { + return Graphics::D3D12::WriteBackBufferToDisk(d3d, directory); + } } diff --git a/samples/test-harness/src/ImageCapture.cpp b/samples/test-harness/src/ImageCapture.cpp new file mode 100644 index 0000000..4dee7fc --- /dev/null +++ b/samples/test-harness/src/ImageCapture.cpp @@ -0,0 +1,68 @@ +/* +* 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 +* and any modifications thereto. Any use, reproduction, disclosure or +* distribution of this software and related documentation without an express +* license agreement from NVIDIA CORPORATION is strictly prohibited. +*/ + +#include "ImageCapture.h" +#include +#include + +namespace ImageCapture +{ + bool CapturePng(std::string file, uint32_t width, uint32_t height, std::vector& rows) + { + FILE* fp = nullptr; + errno_t ferror = fopen_s(&fp, file.c_str(), "wb"); + if (ferror != 0) + { + return false; + } + + 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) + { + png_destroy_write_struct(&pngWrite, (png_infopp)nullptr); + return false; + } + + if (setjmp(png_jmpbuf(pngWrite))) + { + png_destroy_write_struct(&pngWrite, &pngInfo); + fclose(fp); + return false; + } + + 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); + // stripping 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; + } +} \ No newline at end of file diff --git a/samples/test-harness/src/Inputs.cpp b/samples/test-harness/src/Inputs.cpp index 14c6f81..bcc22b3 100644 --- a/samples/test-harness/src/Inputs.cpp +++ b/samples/test-harness/src/Inputs.cpp @@ -64,8 +64,16 @@ void KeyHandler(GLFWwindow* window, int key, int scancode, int action, int mods) // Save debug images if(IsKeyReleased(key, action, GLFW_KEY_F1)) { - inputPtr->saveImages = true; inputPtr->event = Inputs::EInputEvent::SAVE_IMAGE; + return; + } + + // Run benchmark + if (IsKeyReleased(key, action, GLFW_KEY_F2)) + { + inputPtr->event = Inputs::EInputEvent::RUN_BENCHMARK; + inputPtr->runBenchmark = true; + return; } // Toggle pan inversion diff --git a/samples/test-harness/src/Instrumentation.cpp b/samples/test-harness/src/Instrumentation.cpp index 3116621..51acd50 100644 --- a/samples/test-harness/src/Instrumentation.cpp +++ b/samples/test-harness/src/Instrumentation.cpp @@ -96,4 +96,20 @@ namespace Instrumentation Resolve(s); } + std::ostream& operator<<(std::ostream& os, const Stat& stat) + { + os << stat.elapsed; + return os; + } + + std::ostream& operator<<(std::ostream& os, std::vector& stats) + { + for (std::vector::const_iterator& it = stats.cbegin(); it != stats.cend(); it++) + { + os << **it << ","; + } + os << std::endl; + return os; + } + } diff --git a/samples/test-harness/src/Vulkan.cpp b/samples/test-harness/src/Vulkan.cpp index 9d0cb82..f9ec73e 100644 --- a/samples/test-harness/src/Vulkan.cpp +++ b/samples/test-harness/src/Vulkan.cpp @@ -11,6 +11,7 @@ #include "Graphics.h" #include "VulkanExtensions.h" #include "UI.h" +#include "ImageCapture.h" namespace Graphics { @@ -1364,7 +1365,7 @@ namespace Graphics bool CreateRenderTargets(Globals& vk, Resources& resources) { // Create the GBufferA (R8G8B8A8_UNORM) texture resource - TextureDesc desc = { static_cast(vk.width), static_cast(vk.height), 1, VK_FORMAT_B8G8R8A8_UNORM, VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT }; + TextureDesc desc = { static_cast(vk.width), static_cast(vk.height), 1, VK_FORMAT_B8G8R8A8_UNORM, VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT }; if(!CreateTexture(vk, desc, &resources.rt.GBufferA, &resources.rt.GBufferAMemory, &resources.rt.GBufferAView)) return false; #ifdef GFX_NAME_OBJECTS SetObjectName(vk.device, reinterpret_cast(resources.rt.GBufferA), "GBufferA", VK_OBJECT_TYPE_IMAGE); @@ -1994,10 +1995,223 @@ namespace Graphics /** * Write an image to disk from the given Vulkan resource. */ - bool WriteResourceToDisk(Globals& d3d, std::string file) + bool WriteResourceToDisk(Globals& vk, std::string file, VkImage image, uint32_t width, uint32_t height, VkFormat imageFormat, VkImageLayout originalLayout) { - // TODO - return false; + VkCommandPool pool; + VkCommandBuffer cmd; + + // create custom 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 + } + + // create custom command buffer + { + uint32_t numCommandBuffers = 1; + + VkCommandBufferAllocateInfo commandBufferAllocateInfo = {}; + commandBufferAllocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + commandBufferAllocateInfo.commandBufferCount = numCommandBuffers; + commandBufferAllocateInfo.commandPool = pool; + commandBufferAllocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + + VKCHECK(vkAllocateCommandBuffers(vk.device, &commandBufferAllocateInfo, &cmd)); + +#ifdef GFX_NAME_OBJECTS + SetObjectName(vk.device, reinterpret_cast(cmd), "Image capture Command Buffer", VK_OBJECT_TYPE_COMMAND_BUFFER); +#endif + VkCommandBufferBeginInfo commandBufferBeginInfo = {}; + commandBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + VKCHECK(vkBeginCommandBuffer(cmd, &commandBufferBeginInfo)); + } + + // using vr_sli_vk demo for reference + VkImage linearScreenshotImage, optimalScreenshotImage; + VkDeviceMemory linearScreenshotImageMemory, optimalScreenshotImageMemory; + { + // same as CreateTexture() but don't create a view + VkImageCreateInfo imageCreateInfo = {}; + imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; + imageCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM; + imageCreateInfo.extent.width = width; + imageCreateInfo.extent.height = height; + imageCreateInfo.extent.depth = 1; + imageCreateInfo.mipLevels = 1; + imageCreateInfo.arrayLayers = 1; + imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; + imageCreateInfo.tiling = VK_IMAGE_TILING_LINEAR; + imageCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT; + imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + + VKCHECK(vkCreateImage(vk.device, &imageCreateInfo, nullptr, &linearScreenshotImage)); + 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)); + + AllocateMemoryDesc desc = {}; + vkGetImageMemoryRequirements(vk.device, linearScreenshotImage, &desc.requirements); + desc.properties = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; + desc.flags = 0; + + if (!AllocateMemory(vk, desc, &linearScreenshotImageMemory)) return false; + VKCHECK(vkBindImageMemory(vk.device, linearScreenshotImage, linearScreenshotImageMemory, 0)); + + vkGetImageMemoryRequirements(vk.device, optimalScreenshotImage, &desc.requirements); + desc.properties = 0; + desc.flags = 0; + if (!AllocateMemory(vk, desc, &optimalScreenshotImageMemory)) return false; + VKCHECK(vkBindImageMemory(vk.device, optimalScreenshotImage, optimalScreenshotImageMemory, 0)); + } + + { + ImageBarrierDesc barrier = + { + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 } + }; + SetImageMemoryBarrier(cmd, linearScreenshotImage, barrier); + SetImageMemoryBarrier(cmd, optimalScreenshotImage, barrier); + barrier.oldLayout = originalLayout; + barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + SetImageMemoryBarrier(cmd, image, barrier); + } + + { + // blit image (format conversion if necessary) + VkImageBlit region = {}; + VkImageSubresourceLayers subres = {}; + subres.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + subres.mipLevel = 0; + subres.baseArrayLayer = 0; + subres.layerCount = 1; + region.srcSubresource = subres; + region.dstSubresource = subres; + region.srcOffsets[0] = {}; + 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); + } + + { + ImageBarrierDesc barrier = + { + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 } + }; + SetImageMemoryBarrier(cmd, optimalScreenshotImage, barrier); + barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + barrier.newLayout = originalLayout; + SetImageMemoryBarrier(cmd, image, barrier); + } + + { + // 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; + region.srcOffset = { 0, 0, 0 }; + region.dstOffset = { 0, 0, 0 }; + region.extent.width = width; + region.extent.height = height; + region.extent.depth = 1; + vkCmdCopyImage( + cmd, + optimalScreenshotImage, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + linearScreenshotImage, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, ®ion + ); + } + + { + ImageBarrierDesc barrier = + { + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_IMAGE_LAYOUT_GENERAL, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 } + }; + SetImageMemoryBarrier(cmd, linearScreenshotImage, barrier); + } + + { + // Execute GPU work to finish initialization + VKCHECK(vkEndCommandBuffer(cmd)); + + VkSubmitInfo submitInfo = {}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &cmd; + + VKCHECK(vkQueueSubmit(vk.queue, 1, &submitInfo, VK_NULL_HANDLE)); + VKCHECK(vkQueueWaitIdle(vk.queue)); + + WaitForGPU(vk); + } + + // copy screenshot image to cpu-side 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; + } + + // 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; + } + + // output the image file to disk + ImageCapture::CapturePng(file, width, height, rows); + + vkUnmapMemory(vk.device, linearScreenshotImageMemory); + } + + { + // 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); + } + + return true; } #ifdef GFX_NAME_OBJECTS @@ -3033,15 +3247,6 @@ namespace Graphics } #endif - /** - * Write the GBuffer texture resources to disk. - */ - bool WriteGBufferToDisk(Globals& vk, Resources& resources, std::string directory) - { - // TODO: implement - return true; - } - /** * Release Vulkan resources. */ @@ -3051,6 +3256,16 @@ namespace Graphics Cleanup(vk); } + /** + * Write the back buffer texture resources to disk. + */ + bool WriteBackBufferToDisk(Globals& vk, std::string directory) + { + CoInitialize(NULL); + bool success = WriteResourceToDisk(vk, directory + "\\backbuffer.png", vk.swapChainImage[vk.frameIndex], vk.width, vk.height, vk.swapChainFormat, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); + return success; + } + } /** @@ -3156,19 +3371,19 @@ namespace Graphics #endif /** - * Write GBuffer resources to disk. + * Cleanup global graphics resources. */ - bool WriteGBufferToDisk(Globals& gfx, GlobalResources& gfxResources, std::string directory) + void Cleanup(Globals& gfx, GlobalResources& gfxResources) { - return Graphics::Vulkan::WriteGBufferToDisk(gfx, gfxResources, directory); + Graphics::Vulkan::Cleanup(gfx, gfxResources); } /** - * Cleanup global graphics resources. + * Write the back buffer texture resources to disk. */ - void Cleanup(Globals& gfx, GlobalResources& gfxResources) + bool WriteBackBufferToDisk(Globals& vk, std::string directory) { - Graphics::Vulkan::Cleanup(gfx, gfxResources); + return Graphics::Vulkan::WriteBackBufferToDisk(vk, directory); } } diff --git a/samples/test-harness/src/graphics/DDGI_D3D12.cpp b/samples/test-harness/src/graphics/DDGI_D3D12.cpp index 32d1557..299d818 100644 --- a/samples/test-harness/src/graphics/DDGI_D3D12.cpp +++ b/samples/test-harness/src/graphics/DDGI_D3D12.cpp @@ -372,6 +372,7 @@ namespace Graphics { volumeDesc.name = config.name; volumeDesc.index = config.index; + volumeDesc.rngSeed = config.rngSeed; volumeDesc.origin = { config.origin.x, config.origin.y, config.origin.z }; volumeDesc.eulerAngles = { config.eulerAngles.x, config.eulerAngles.y, config.eulerAngles.z, }; volumeDesc.probeSpacing = { config.probeSpacing.x, config.probeSpacing.y, config.probeSpacing.z }; @@ -940,8 +941,8 @@ namespace Graphics // Validate the SDK version assert(RTXGI_VERSION::major == 1); assert(RTXGI_VERSION::minor == 2); - assert(RTXGI_VERSION::revision == 4); - assert(std::strcmp(RTXGI_VERSION::getVersionString(), "1.2.04") == 0); + assert(RTXGI_VERSION::revision == 7); + assert(std::strcmp(RTXGI_VERSION::getVersionString(), "1.2.07") == 0); UINT numVolumes = static_cast(config.ddgi.volumes.size()); @@ -1181,6 +1182,31 @@ namespace Graphics } } + /** + * Write the DDGI Volume texture resources to disk. + */ + bool WriteVolumesToDisk(Globals& globals, GlobalResources& gfxResources, Resources& resources, std::string directory) + { + CoInitialize(NULL); + bool success = true; + for (rtxgi::DDGIVolumeBase* volumeBase : resources.volumes) + { + std::string baseName = directory + "\\" + volumeBase->GetName(); + std::string filename = baseName + "-irradiance.png"; + + rtxgi::d3d12::DDGIVolume* volume = static_cast(volumeBase); + success &= WriteResourceToDisk(globals, filename, volume->GetProbeIrradiance(), D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); + + // not capturing distances because WIC doesn't like two-channel textures + //filename = baseName + "-distance.png"; + //success &= WriteResourceToDisk(globals, filename, volume->GetProbeDistance(), D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); + + filename = baseName + "-data.png"; + success &= WriteResourceToDisk(globals, filename, volume->GetProbeData(), D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); + } + return success; + } + } // namespace Graphics::D3D12::RTAO } // namespace Graphics::D3D12 @@ -1218,5 +1244,10 @@ namespace Graphics Graphics::D3D12::DDGI::Cleanup(resources); } + bool WriteVolumesToDisk(Globals& globals, GlobalResources& gfxResources, Resources& resources, std::string directory) + { + return Graphics::D3D12::DDGI::WriteVolumesToDisk(globals, gfxResources, resources, directory); + } + } // namespace Graphics::DDGI } diff --git a/samples/test-harness/src/graphics/DDGI_VK.cpp b/samples/test-harness/src/graphics/DDGI_VK.cpp index 0277923..794f93c 100644 --- a/samples/test-harness/src/graphics/DDGI_VK.cpp +++ b/samples/test-harness/src/graphics/DDGI_VK.cpp @@ -189,7 +189,7 @@ namespace Graphics GetDDGIVolumeTextureDimensions(volumeDesc, EDDGIVolumeTextureType::Irradiance, width, height); format = GetDDGIVolumeTextureFormat(EDDGIVolumeTextureType::Irradiance, volumeDesc.probeIrradianceFormat); - TextureDesc desc = { width, height, 1, format, VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT }; + TextureDesc desc = { width, height, 1, format, VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT }; CHECK(CreateTexture(vk, desc, &volumeResources.unmanaged.probeIrradiance, &volumeResources.unmanaged.probeIrradianceMemory, &volumeResources.unmanaged.probeIrradianceView), "create DDGIVolume irradiance texture!", log); #ifdef GFX_NAME_OBJECTS std::string n = "DDGIVolume[" + std::to_string(volumeDesc.index) + "], Probe Irradiance"; @@ -205,7 +205,7 @@ namespace Graphics GetDDGIVolumeTextureDimensions(volumeDesc, EDDGIVolumeTextureType::Distance, width, height); format = GetDDGIVolumeTextureFormat(EDDGIVolumeTextureType::Distance, volumeDesc.probeDistanceFormat); - TextureDesc desc = { width, height, 1, format, VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT }; + TextureDesc desc = { width, height, 1, format, VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT }; CHECK(CreateTexture(vk, desc, &volumeResources.unmanaged.probeDistance, &volumeResources.unmanaged.probeDistanceMemory, &volumeResources.unmanaged.probeDistanceView), "create DDGIVolume distance texture!", log); #ifdef GFX_NAME_OBJECTS std::string n = "DDGIVolume[" + std::to_string(volumeDesc.index) + "], Probe Distance"; @@ -222,7 +222,7 @@ namespace Graphics if (width <= 0) return false; format = GetDDGIVolumeTextureFormat(EDDGIVolumeTextureType::Data, volumeDesc.probeDataFormat); - TextureDesc desc = { width, height, 1, format, VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT }; + TextureDesc desc = { width, height, 1, format, VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT }; CHECK(CreateTexture(vk, desc, &volumeResources.unmanaged.probeData, &volumeResources.unmanaged.probeDataMemory, &volumeResources.unmanaged.probeDataView), "", log); #ifdef GFX_NAME_OBJECTS std::string n = "DDGIVolume[" + std::to_string(volumeDesc.index) + "], Probe Data"; @@ -579,6 +579,7 @@ namespace Graphics { volumeDesc.name = config.name; volumeDesc.index = config.index; + volumeDesc.rngSeed = config.rngSeed; volumeDesc.origin = { config.origin.x, config.origin.y, config.origin.z }; volumeDesc.eulerAngles = { config.eulerAngles.x, config.eulerAngles.y, config.eulerAngles.z, }; volumeDesc.probeSpacing = { config.probeSpacing.x, config.probeSpacing.y, config.probeSpacing.z }; @@ -795,7 +796,7 @@ namespace Graphics vkFreeMemory(vk.device, resources.outputMemory, nullptr); // Create the output (R16G16B16A16_FLOAT) texture resource - TextureDesc desc = { static_cast(vk.width), static_cast(vk.height), 1, VK_FORMAT_R16G16B16A16_SFLOAT, VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT }; + TextureDesc desc = { static_cast(vk.width), static_cast(vk.height), 1, VK_FORMAT_R16G16B16A16_SFLOAT, VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT }; CHECK(CreateTexture(vk, desc, &resources.output, &resources.outputMemory, &resources.outputView), "create DDGI output texture resource!\n", log); #ifdef GFX_NAME_OBJECTS SetObjectName(vk.device, reinterpret_cast(resources.output), "DDGI Output", VK_OBJECT_TYPE_IMAGE); @@ -1367,8 +1368,8 @@ namespace Graphics // Validate the SDK version assert(RTXGI_VERSION::major == 1); assert(RTXGI_VERSION::minor == 2); - assert(RTXGI_VERSION::revision == 4); - assert(std::strcmp(RTXGI_VERSION::getVersionString(), "1.2.04") == 0); + assert(RTXGI_VERSION::revision == 7); + assert(std::strcmp(RTXGI_VERSION::getVersionString(), "1.2.07") == 0); // Reset the command list before initialization CHECK(ResetCmdList(vk), "reset command list!", log); @@ -1650,6 +1651,37 @@ namespace Graphics } } + /** + * Write the DDGI Volume texture resources to disk. + */ + bool WriteVolumesToDisk(Globals& vk, GlobalResources& vkResources, Resources& resources, std::string directory) + { + CoInitialize(NULL); + bool success = true; + for (rtxgi::DDGIVolumeBase* volumeBase : resources.volumes) + { + std::string baseName = directory + "\\" + volumeBase->GetName(); + std::string filename = baseName + "-irradiance.png"; + + rtxgi::vulkan::DDGIVolume* volume = static_cast(volumeBase); + rtxgi::DDGIVolumeDesc desc = volumeBase->GetDesc(); + uint32_t width = 0, height = 0; + GetDDGIVolumeTextureDimensions(desc, EDDGIVolumeTextureType::Irradiance, width, height); + VkFormat format = GetDDGIVolumeTextureFormat(EDDGIVolumeTextureType::Irradiance, desc.probeIrradianceFormat); + success &= WriteResourceToDisk(vk, filename, volume->GetProbeIrradiance(), width, height, format, VK_IMAGE_LAYOUT_GENERAL); + + // not capturing distances because WIC doesn't like two-channel textures + //filename = baseName + "-distance.png"; + //success &= WriteResourceToDisk(globals, filename, volume->GetProbeDistance(), D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); + + filename = baseName + "-data.png"; + GetDDGIVolumeTextureDimensions(desc, EDDGIVolumeTextureType::Data, width, height); + format = GetDDGIVolumeTextureFormat(EDDGIVolumeTextureType::RayData, desc.probeDataFormat); + success &= WriteResourceToDisk(vk, filename, volume->GetProbeData(), width, height, format,VK_IMAGE_LAYOUT_GENERAL); + } + return success; + } + } // namespace Graphics::Vulkan::DDGI } // namespace Graphics::Vulkan @@ -1687,5 +1719,10 @@ namespace Graphics Graphics::Vulkan::DDGI::Cleanup(vk.device, resources); } + bool WriteVolumesToDisk(Globals& vk, GlobalResources& vkResources, Resources& resources, std::string directory) + { + return Graphics::Vulkan::DDGI::WriteVolumesToDisk(vk, vkResources, resources, directory); + } + } // namespace Graphics::DDGI } diff --git a/samples/test-harness/src/graphics/GBuffer_D3D12.cpp b/samples/test-harness/src/graphics/GBuffer_D3D12.cpp index a59653a..3ccbfe9 100644 --- a/samples/test-harness/src/graphics/GBuffer_D3D12.cpp +++ b/samples/test-harness/src/graphics/GBuffer_D3D12.cpp @@ -304,19 +304,6 @@ namespace Graphics #endif } - /** - * Write the GBuffer texture resources to disk. - */ - bool WriteGBufferToDisk(Globals& d3d, GlobalResources& d3dResources, std::string directory) - { - CoInitialize(NULL); - bool success = WriteResourceToDisk(d3d, directory + "GBufferA.png", d3dResources.rt.GBufferA, D3D12_RESOURCE_STATE_UNORDERED_ACCESS); - success &= WriteResourceToDisk(d3d, directory + "GBufferB.png", d3dResources.rt.GBufferB, D3D12_RESOURCE_STATE_UNORDERED_ACCESS); - success &= WriteResourceToDisk(d3d, directory + "GBufferC.png", d3dResources.rt.GBufferC, D3D12_RESOURCE_STATE_UNORDERED_ACCESS); - success &= WriteResourceToDisk(d3d, directory + "GBufferD.png", d3dResources.rt.GBufferD, D3D12_RESOURCE_STATE_UNORDERED_ACCESS); - return success; - } - /** * Release resources. */ @@ -332,6 +319,19 @@ namespace Graphics SAFE_RELEASE(resources.rtpso); } + /** + * Write the GBuffer texture resources to disk. + */ + bool WriteGBufferToDisk(Globals& d3d, GlobalResources& d3dResources, std::string directory) + { + CoInitialize(NULL); + bool success = WriteResourceToDisk(d3d, directory + "\\GBufferA.png", d3dResources.rt.GBufferA, D3D12_RESOURCE_STATE_UNORDERED_ACCESS); + success &= WriteResourceToDisk(d3d, directory + "\\GBufferB.png", d3dResources.rt.GBufferB, D3D12_RESOURCE_STATE_UNORDERED_ACCESS); + success &= WriteResourceToDisk(d3d, directory + "\\GBufferC.png", d3dResources.rt.GBufferC, D3D12_RESOURCE_STATE_UNORDERED_ACCESS); + success &= WriteResourceToDisk(d3d, directory + "\\GBufferD.png", d3dResources.rt.GBufferD, D3D12_RESOURCE_STATE_UNORDERED_ACCESS); + return success; + } + } // namespace Graphics::D3D12::GBuffer } // namespace Graphics::D3D12 diff --git a/samples/test-harness/src/graphics/GBuffer_VK.cpp b/samples/test-harness/src/graphics/GBuffer_VK.cpp index fb70524..9a302a2 100644 --- a/samples/test-harness/src/graphics/GBuffer_VK.cpp +++ b/samples/test-harness/src/graphics/GBuffer_VK.cpp @@ -536,15 +536,6 @@ namespace Graphics CPU_TIMESTAMP_ENDANDRESOLVE(resources.cpuStat); } - /** - * Write the GBuffer texture resources to disk. - */ - bool WriteGBufferToDisk(Globals& d3d, GlobalResources& vkResources, std::string directory) - { - // TODO: implement - return true; - } - /** * Release resources. */ @@ -568,6 +559,20 @@ namespace Graphics resources.shaderTableHitGroupTableSize = 0; } + /** + * Write the GBuffer texture resources to disk. + */ + bool WriteGBufferToDisk(Globals& vk, GlobalResources& vkResources, std::string directory) + { + CoInitialize(NULL); + // formats should match those from Graphics::Vulkan::CreateRenderTargets() in Vulkan.cpp + bool success = WriteResourceToDisk(vk, directory + "\\GBufferA.png", vkResources.rt.GBufferA, vk.width, vk.height, VK_FORMAT_B8G8R8A8_UNORM, VK_IMAGE_LAYOUT_GENERAL); + success &= WriteResourceToDisk(vk, directory + "\\GBufferB.png", vkResources.rt.GBufferB, vk.width, vk.height, VK_FORMAT_R32G32B32A32_SFLOAT, VK_IMAGE_LAYOUT_GENERAL); + success &= WriteResourceToDisk(vk, directory + "\\GBufferC.png", vkResources.rt.GBufferC, vk.width, vk.height, VK_FORMAT_R32G32B32A32_SFLOAT, VK_IMAGE_LAYOUT_GENERAL); + success &= WriteResourceToDisk(vk, directory + "\\GBufferD.png", vkResources.rt.GBufferD, vk.width, vk.height, VK_FORMAT_R32G32B32A32_SFLOAT, VK_IMAGE_LAYOUT_GENERAL); + return success; + } + } // namespace Graphics::Vulkan::GBuffer } // namespace Graphics::Vulkan @@ -600,14 +605,14 @@ namespace Graphics return Graphics::Vulkan::GBuffer::Execute(vk, vkResources, resources); } - bool WriteGBufferToDisk(Globals& vk, GlobalResources& vkResources, std::string directory) + void Cleanup(Globals& vk, Resources& resources) { - return Graphics::Vulkan::GBuffer::WriteGBufferToDisk(vk, vkResources, directory); + Graphics::Vulkan::GBuffer::Cleanup(vk.device, resources); } - void Cleanup(Globals& vk, Resources& resources) + bool WriteGBufferToDisk(Globals& vk, GlobalResources& vkResources, std::string directory) { - Graphics::Vulkan::GBuffer::Cleanup(vk.device, resources); + return Graphics::Vulkan::GBuffer::WriteGBufferToDisk(vk, vkResources, directory); } } // namespace Graphics::GBuffer diff --git a/samples/test-harness/src/graphics/RTAO_D3D12.cpp b/samples/test-harness/src/graphics/RTAO_D3D12.cpp index 2cd0b9a..20a8099 100644 --- a/samples/test-harness/src/graphics/RTAO_D3D12.cpp +++ b/samples/test-harness/src/graphics/RTAO_D3D12.cpp @@ -434,6 +434,17 @@ namespace Graphics resources.shaderTableHitGroupTableStartAddress = 0; } + /** + * Write the RTAO texture resources to disk. + */ + bool WriteRTAOBuffersToDisk(Globals& d3d, GlobalResources& d3dResources, Resources& resources, std::string directory) + { + CoInitialize(NULL); + bool success = WriteResourceToDisk(d3d, directory + "\\rtaoraw.png", resources.RTAORaw, D3D12_RESOURCE_STATE_UNORDERED_ACCESS); + success &= WriteResourceToDisk(d3d, directory + "\\rtaofiltered.png", resources.RTAOOutput, D3D12_RESOURCE_STATE_UNORDERED_ACCESS); + return success; + } + } // namespace Graphics::D3D12::RTAO } // namespace Graphics::D3D12 @@ -471,5 +482,10 @@ namespace Graphics Graphics::D3D12::RTAO::Cleanup(resources); } + bool WriteRTAOBuffersToDisk(Globals& d3d, GlobalResources& d3dResources, Resources& resources, std::string directory) + { + return Graphics::D3D12::RTAO::WriteRTAOBuffersToDisk(d3d, d3dResources, resources, directory); + } + } // namespace Graphics::RTAO } diff --git a/samples/test-harness/src/graphics/RTAO_VK.cpp b/samples/test-harness/src/graphics/RTAO_VK.cpp index d086818..a449a82 100644 --- a/samples/test-harness/src/graphics/RTAO_VK.cpp +++ b/samples/test-harness/src/graphics/RTAO_VK.cpp @@ -25,7 +25,7 @@ namespace Graphics bool CreateTextures(Globals& vk, GlobalResources& vkResources, Resources& resources, std::ofstream& log) { // Create the output (R8G8B8A8_UNORM) texture resource - TextureDesc desc = { static_cast(vk.width), static_cast(vk.height), 1, VK_FORMAT_R8_UNORM, VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT }; + TextureDesc desc = { static_cast(vk.width), static_cast(vk.height), 1, VK_FORMAT_R8_UNORM, VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT }; CHECK(CreateTexture(vk, desc, &resources.RTAOOutput, &resources.RTAOOutputMemory, &resources.RTAOOutputView), "create RTAO output texture resource!\n", log); #ifdef GFX_NAME_OBJECTS SetObjectName(vk.device, reinterpret_cast(resources.RTAOOutput), "RTAO Output", VK_OBJECT_TYPE_IMAGE); @@ -651,6 +651,18 @@ namespace Graphics resources.shaderTableHitGroupTableStartAddress = 0; } + /** + * Write the RTAO texture resources to disk. + */ + bool WriteRTAOBuffersToDisk(Globals& vk, GlobalResources& vkResources, Resources& resources, std::string directory) + { + CoInitialize(NULL); + // format should match those from CreateTextures() function above + bool success = WriteResourceToDisk(vk, directory + "\\rtaoraw.png", resources.RTAORaw, vk.width, vk.height, VK_FORMAT_R8_UNORM, VK_IMAGE_LAYOUT_GENERAL); + success &= WriteResourceToDisk(vk, directory + "\\rtaofiltered.png", resources.RTAOOutput, vk.width, vk.height, VK_FORMAT_R8_UNORM, VK_IMAGE_LAYOUT_GENERAL); + return success; + } + } // namespace Graphics::Vulkan::RTAO } // namespace Graphics::Vulkan @@ -688,5 +700,10 @@ namespace Graphics Graphics::Vulkan::RTAO::Cleanup(vk.device, resources); } + bool WriteRTAOBuffersToDisk(Globals& vk, GlobalResources& vkResources, Resources& resources, std::string directory) + { + return Graphics::Vulkan::RTAO::WriteRTAOBuffersToDisk(vk, vkResources, resources, directory); + } + } // namespace Graphics::RTAO } diff --git a/samples/test-harness/src/main.cpp b/samples/test-harness/src/main.cpp index 1ead7ce..9e37068 100644 --- a/samples/test-harness/src/main.cpp +++ b/samples/test-harness/src/main.cpp @@ -16,6 +16,7 @@ #include "Graphics.h" #include "UI.h" #include "Window.h" +#include "Benchmark.h" #include "graphics/PathTracing.h" #include "graphics/GBuffer.h" @@ -24,6 +25,8 @@ #include "graphics/RTAO.h" #include "graphics/Composite.h" +#include + /** * Run the Test Harness. */ @@ -57,6 +60,7 @@ int Run(const std::vector& arguments) perf.AddGPUStat("Frame"); perf.AddCPUStat("Input"); perf.AddCPUStat("Update"); + Benchmark::BenchmarkRun benchmarkRun; CPU_TIMESTAMP_BEGIN(&startupShutdown); @@ -200,6 +204,12 @@ int Run(const std::vector& arguments) continue; } + if (input.event == Inputs::EInputEvent::RUN_BENCHMARK) + { + Benchmark::StartBenchmark(benchmarkRun, perf, config, gfx); + input.event = Inputs::EInputEvent::NONE; + } + // Reload shaders and PSOs for graphics workloads { if (config.pathTrace.reload) @@ -325,17 +335,32 @@ int Run(const std::vector& arguments) if (!Graphics::SubmitCmdList(gfx)) break; if (!Graphics::Present(gfx)) continue; if (!Graphics::WaitForGPU(gfx)) break; + + bool saveImages = (input.event == Inputs::EInputEvent::SAVE_IMAGE) || (input.runBenchmark && gfx.frameNumber >= Benchmark::NumBenchmarkFrames); + + if (saveImages) + { + CreateDirectory(config.scene.screenshotPath.c_str(), NULL); + Graphics::WriteBackBufferToDisk(gfx, config.scene.screenshotPath); + Graphics::GBuffer::WriteGBufferToDisk(gfx, gfxResources, config.scene.screenshotPath); + Graphics::RTAO::WriteRTAOBuffersToDisk(gfx, gfxResources, rtao, config.scene.screenshotPath); + Graphics::DDGI::WriteVolumesToDisk(gfx, gfxResources, ddgi, config.scene.screenshotPath); + input.event = Inputs::EInputEvent::NONE; + } + if (!Graphics::MoveToNextFrame(gfx)) break; if (!Graphics::ResetCmdList(gfx)) break; CPU_TIMESTAMP_ENDANDRESOLVE(perf.cpuTimes.back()); #ifdef GFX_PERF_INSTRUMENTATION if (!Graphics::UpdateTimestamps(gfx, gfxResources, perf)) break; + if (input.runBenchmark) + { + input.runBenchmark = Benchmark::UpdateBenchmark(benchmarkRun, perf, config, gfx, log); + } Graphics::BeginFrame(gfx, gfxResources, perf); #endif - // TODO: add GBuffer image dump code for debugging - CPU_TIMESTAMP_ENDANDRESOLVE(perf.cpuTimes[0]); } diff --git a/thirdparty/libpng b/thirdparty/libpng new file mode 160000 index 0000000..a37d483 --- /dev/null +++ b/thirdparty/libpng @@ -0,0 +1 @@ +Subproject commit a37d4836519517bdce6cb9d956092321eca3e73b diff --git a/thirdparty/zlib b/thirdparty/zlib new file mode 160000 index 0000000..cacf7f1 --- /dev/null +++ b/thirdparty/zlib @@ -0,0 +1 @@ +Subproject commit cacf7f1d4e3d44d871b605da3b647f07d718623f