From e4ccf75686f946ec4358ecf40d63b19a81919e1d Mon Sep 17 00:00:00 2001 From: W298 Date: Mon, 27 Nov 2023 23:35:06 +0900 Subject: [PATCH] Modify arguments and Change Submodule to Direct --- .gitmodules | 3 - Common/DeviceResources.cpp | 34 +- Common/DeviceResources.h | 4 +- Common/DirectXTK12/.gitignore | 29 + .../.nuget/directxtk12_desktop_2019.nuspec | 77 + .../.nuget/directxtk12_desktop_2019.targets | 35 + .../DirectXTK12/.nuget/directxtk12_uwp.nuspec | 77 + .../.nuget/directxtk12_uwp.targets | 35 + Common/DirectXTK12/.nuget/icon.jpg | Bin 0 -> 3479 bytes Common/DirectXTK12/Audio/AudioEngine.cpp | 1922 +++++ .../Audio/DynamicSoundEffectInstance.cpp | 377 + Common/DirectXTK12/Audio/SoundCommon.cpp | 982 +++ Common/DirectXTK12/Audio/SoundCommon.h | 393 + Common/DirectXTK12/Audio/SoundEffect.cpp | 629 ++ .../DirectXTK12/Audio/SoundEffectInstance.cpp | 337 + .../DirectXTK12/Audio/SoundStreamInstance.cpp | 860 +++ Common/DirectXTK12/Audio/WAVFileReader.cpp | 749 ++ Common/DirectXTK12/Audio/WAVFileReader.h | 58 + Common/DirectXTK12/Audio/WaveBank.cpp | 618 ++ Common/DirectXTK12/Audio/WaveBankReader.cpp | 1423 ++++ Common/DirectXTK12/Audio/WaveBankReader.h | 83 + Common/DirectXTK12/CMakeLists.txt | 539 ++ Common/DirectXTK12/CMakePresets.json | 294 + .../DirectXTK_Desktop_2022_Win10.sln | 42 + .../DirectXTK_Desktop_2022_Win10.vcxproj | 480 ++ ...rectXTK_Desktop_2022_Win10.vcxproj.filters | 381 + Common/DirectXTK12/HISTORY.md | 446 ++ Common/DirectXTK12/Inc/Audio.h | 822 +++ Common/DirectXTK12/Inc/BufferHelpers.h | 129 + Common/DirectXTK12/Inc/CommonStates.h | 98 + Common/DirectXTK12/Inc/DDSTextureLoader.h | 168 + Common/DirectXTK12/Inc/DescriptorHeap.h | 221 + Common/DirectXTK12/Inc/DirectXHelpers.h | 363 + .../Inc/EffectPipelineStateDescription.h | 121 + Common/DirectXTK12/Inc/Effects.h | 937 +++ Common/DirectXTK12/Inc/GamePad.h | 320 + Common/DirectXTK12/Inc/GeometricPrimitive.h | 98 + Common/DirectXTK12/Inc/GraphicsMemory.h | 226 + Common/DirectXTK12/Inc/Keyboard.h | 526 ++ Common/DirectXTK12/Inc/Model.h | 718 ++ Common/DirectXTK12/Inc/Mouse.h | 166 + Common/DirectXTK12/Inc/PostProcess.h | 217 + Common/DirectXTK12/Inc/PrimitiveBatch.h | 147 + Common/DirectXTK12/Inc/RenderTargetState.h | 106 + Common/DirectXTK12/Inc/ResourceUploadBatch.h | 88 + Common/DirectXTK12/Inc/ScreenGrab.h | 60 + Common/DirectXTK12/Inc/SimpleMath.h | 1142 +++ Common/DirectXTK12/Inc/SimpleMath.inl | 3826 ++++++++++ Common/DirectXTK12/Inc/SpriteBatch.h | 157 + Common/DirectXTK12/Inc/SpriteFont.h | 124 + Common/DirectXTK12/Inc/VertexTypes.h | 360 + Common/DirectXTK12/Inc/WICTextureLoader.h | 151 + Common/DirectXTK12/Inc/XboxDDSTextureLoader.h | 97 + Common/DirectXTK12/LICENSE | 21 + Common/DirectXTK12/NuGet.Config | 6 + Common/DirectXTK12/README.md | 145 + Common/DirectXTK12/SECURITY.md | 41 + Common/DirectXTK12/Src/AlignedNew.h | 81 + Common/DirectXTK12/Src/AlphaTestEffect.cpp | 519 ++ Common/DirectXTK12/Src/BasicEffect.cpp | 782 ++ Common/DirectXTK12/Src/BasicPostProcess.cpp | 616 ++ Common/DirectXTK12/Src/Bezier.h | 193 + Common/DirectXTK12/Src/BinaryReader.cpp | 106 + Common/DirectXTK12/Src/BinaryReader.h | 72 + Common/DirectXTK12/Src/BufferHelpers.cpp | 407 ++ Common/DirectXTK12/Src/CommonStates.cpp | 582 ++ Common/DirectXTK12/Src/DDS.h | 291 + Common/DirectXTK12/Src/DDSTextureLoader.cpp | 1128 +++ Common/DirectXTK12/Src/DebugEffect.cpp | 459 ++ Common/DirectXTK12/Src/DemandCreate.h | 58 + Common/DirectXTK12/Src/DescriptorHeap.cpp | 230 + Common/DirectXTK12/Src/DirectXHelpers.cpp | 333 + Common/DirectXTK12/Src/DualPostProcess.cpp | 356 + Common/DirectXTK12/Src/DualTextureEffect.cpp | 432 ++ Common/DirectXTK12/Src/EffectCommon.cpp | 402 + Common/DirectXTK12/Src/EffectCommon.h | 239 + Common/DirectXTK12/Src/EffectFactory.cpp | 589 ++ .../Src/EffectPipelineStateDescription.cpp | 114 + .../DirectXTK12/Src/EffectTextureFactory.cpp | 324 + .../DirectXTK12/Src/EnvironmentMapEffect.cpp | 754 ++ Common/DirectXTK12/Src/GamePad.cpp | 1780 +++++ Common/DirectXTK12/Src/GeometricPrimitive.cpp | 706 ++ Common/DirectXTK12/Src/Geometry.cpp | 1193 +++ Common/DirectXTK12/Src/Geometry.h | 29 + Common/DirectXTK12/Src/GraphicsMemory.cpp | 635 ++ Common/DirectXTK12/Src/Keyboard.cpp | 675 ++ Common/DirectXTK12/Src/LinearAllocator.cpp | 498 ++ Common/DirectXTK12/Src/LinearAllocator.h | 168 + Common/DirectXTK12/Src/LoaderHelpers.h | 1096 +++ Common/DirectXTK12/Src/Model.cpp | 850 +++ Common/DirectXTK12/Src/ModelLoadCMO.cpp | 1018 +++ Common/DirectXTK12/Src/ModelLoadSDKMESH.cpp | 812 +++ Common/DirectXTK12/Src/ModelLoadVBO.cpp | 172 + Common/DirectXTK12/Src/Mouse.cpp | 1648 +++++ Common/DirectXTK12/Src/NormalMapEffect.cpp | 887 +++ Common/DirectXTK12/Src/PBREffect.cpp | 906 +++ Common/DirectXTK12/Src/PBREffectFactory.cpp | 321 + Common/DirectXTK12/Src/PlatformHelpers.h | 92 + Common/DirectXTK12/Src/PrimitiveBatch.cpp | 274 + .../DirectXTK12/Src/ResourceUploadBatch.cpp | 1129 +++ Common/DirectXTK12/Src/SDKMesh.h | 360 + Common/DirectXTK12/Src/ScreenGrab.cpp | 856 +++ .../Src/Shaders/AlphaTestEffect.fx | 135 + Common/DirectXTK12/Src/Shaders/BasicEffect.fx | 531 ++ Common/DirectXTK12/Src/Shaders/Common.fxh | 57 + .../Src/Shaders/CompileShaders.cmd | 362 + Common/DirectXTK12/Src/Shaders/DebugEffect.fx | 217 + .../Src/Shaders/DualTextureEffect.fx | 121 + .../Src/Shaders/EnvironmentMapEffect.fx | 431 ++ .../DirectXTK12/Src/Shaders/GenerateMips.hlsl | 30 + Common/DirectXTK12/Src/Shaders/Lighting.fxh | 95 + .../Src/Shaders/NormalMapEffect.fx | 392 + Common/DirectXTK12/Src/Shaders/PBRCommon.fxh | 169 + Common/DirectXTK12/Src/Shaders/PBREffect.fx | 372 + .../Src/Shaders/PixelPacking_Velocity.hlsli | 95 + Common/DirectXTK12/Src/Shaders/PostProcess.fx | 202 + Common/DirectXTK12/Src/Shaders/RootSig.fxh | 374 + .../DirectXTK12/Src/Shaders/SkinnedEffect.fx | 295 + Common/DirectXTK12/Src/Shaders/Skinning.fxh | 20 + .../DirectXTK12/Src/Shaders/SpriteEffect.fx | 46 + Common/DirectXTK12/Src/Shaders/Structures.fxh | 243 + Common/DirectXTK12/Src/Shaders/ToneMap.fx | 245 + Common/DirectXTK12/Src/Shaders/Utilities.fxh | 123 + Common/DirectXTK12/Src/SharedResourcePool.h | 111 + Common/DirectXTK12/Src/SimpleMath.cpp | 247 + Common/DirectXTK12/Src/SkinnedEffect.cpp | 562 ++ Common/DirectXTK12/Src/SpriteBatch.cpp | 1243 ++++ Common/DirectXTK12/Src/SpriteFont.cpp | 753 ++ Common/DirectXTK12/Src/TeapotData.inc | 182 + Common/DirectXTK12/Src/ToneMapPostProcess.cpp | 503 ++ Common/DirectXTK12/Src/VertexTypes.cpp | 167 + Common/DirectXTK12/Src/WICTextureLoader.cpp | 1040 +++ .../DirectXTK12/Src/XboxDDSTextureLoader.cpp | 650 ++ Common/DirectXTK12/Src/d3dx12.h | 6477 +++++++++++++++++ Common/DirectXTK12/Src/pch.cpp | 10 + Common/DirectXTK12/Src/pch.h | 270 + Common/DirectXTK12/Src/vbo.h | 35 + Common/DirectXTK12/build/CopyASan.targets | 24 + .../build/DirectXTK12-GitHub-CMake-Dev17.yml | 167 + .../build/DirectXTK12-GitHub-CMake.yml | 172 + .../build/DirectXTK12-GitHub-Dev17.yml | 151 + .../build/DirectXTK12-GitHub-GDK-Dev17.yml | 141 + .../build/DirectXTK12-GitHub-GDK.yml | 326 + .../build/DirectXTK12-GitHub-MinGW.yml | 204 + .../DirectXTK12-GitHub-SDK-prerelease.yml | 253 + .../build/DirectXTK12-GitHub-SDK-release.yml | 253 + .../build/DirectXTK12-GitHub-Test-Dev17.yml | 330 + .../build/DirectXTK12-GitHub-Test.yml | 347 + .../DirectXTK12/build/DirectXTK12-GitHub.yml | 152 + .../DirectXTK12/build/DirectXTK12-OneFuzz.yml | 129 + Common/DirectXTK12/build/DirectXTK12-SDL.yml | 98 + .../build/DirectXTK12-config.cmake.in | 14 + .../DirectXTK12/build/Directory.Build.props | 92 + Common/DirectXTK12/build/OneFuzzConfig.json | 33 + Common/DirectXTK12/build/SetupBWOI.targets | 138 + Common/DirectXTK12/build/placeholder.xvd | 1 + DirectXTK12 | 1 - Game.cpp | 15 +- Game.h | 4 +- Main.cpp | 40 +- apollo.sln | 2 +- apollo.vcxproj | 18 +- apollo.vcxproj.user | 12 +- 163 files changed, 67547 insertions(+), 55 deletions(-) delete mode 100644 .gitmodules create mode 100644 Common/DirectXTK12/.gitignore create mode 100644 Common/DirectXTK12/.nuget/directxtk12_desktop_2019.nuspec create mode 100644 Common/DirectXTK12/.nuget/directxtk12_desktop_2019.targets create mode 100644 Common/DirectXTK12/.nuget/directxtk12_uwp.nuspec create mode 100644 Common/DirectXTK12/.nuget/directxtk12_uwp.targets create mode 100644 Common/DirectXTK12/.nuget/icon.jpg create mode 100644 Common/DirectXTK12/Audio/AudioEngine.cpp create mode 100644 Common/DirectXTK12/Audio/DynamicSoundEffectInstance.cpp create mode 100644 Common/DirectXTK12/Audio/SoundCommon.cpp create mode 100644 Common/DirectXTK12/Audio/SoundCommon.h create mode 100644 Common/DirectXTK12/Audio/SoundEffect.cpp create mode 100644 Common/DirectXTK12/Audio/SoundEffectInstance.cpp create mode 100644 Common/DirectXTK12/Audio/SoundStreamInstance.cpp create mode 100644 Common/DirectXTK12/Audio/WAVFileReader.cpp create mode 100644 Common/DirectXTK12/Audio/WAVFileReader.h create mode 100644 Common/DirectXTK12/Audio/WaveBank.cpp create mode 100644 Common/DirectXTK12/Audio/WaveBankReader.cpp create mode 100644 Common/DirectXTK12/Audio/WaveBankReader.h create mode 100644 Common/DirectXTK12/CMakeLists.txt create mode 100644 Common/DirectXTK12/CMakePresets.json create mode 100644 Common/DirectXTK12/DirectXTK_Desktop_2022_Win10.sln create mode 100644 Common/DirectXTK12/DirectXTK_Desktop_2022_Win10.vcxproj create mode 100644 Common/DirectXTK12/DirectXTK_Desktop_2022_Win10.vcxproj.filters create mode 100644 Common/DirectXTK12/HISTORY.md create mode 100644 Common/DirectXTK12/Inc/Audio.h create mode 100644 Common/DirectXTK12/Inc/BufferHelpers.h create mode 100644 Common/DirectXTK12/Inc/CommonStates.h create mode 100644 Common/DirectXTK12/Inc/DDSTextureLoader.h create mode 100644 Common/DirectXTK12/Inc/DescriptorHeap.h create mode 100644 Common/DirectXTK12/Inc/DirectXHelpers.h create mode 100644 Common/DirectXTK12/Inc/EffectPipelineStateDescription.h create mode 100644 Common/DirectXTK12/Inc/Effects.h create mode 100644 Common/DirectXTK12/Inc/GamePad.h create mode 100644 Common/DirectXTK12/Inc/GeometricPrimitive.h create mode 100644 Common/DirectXTK12/Inc/GraphicsMemory.h create mode 100644 Common/DirectXTK12/Inc/Keyboard.h create mode 100644 Common/DirectXTK12/Inc/Model.h create mode 100644 Common/DirectXTK12/Inc/Mouse.h create mode 100644 Common/DirectXTK12/Inc/PostProcess.h create mode 100644 Common/DirectXTK12/Inc/PrimitiveBatch.h create mode 100644 Common/DirectXTK12/Inc/RenderTargetState.h create mode 100644 Common/DirectXTK12/Inc/ResourceUploadBatch.h create mode 100644 Common/DirectXTK12/Inc/ScreenGrab.h create mode 100644 Common/DirectXTK12/Inc/SimpleMath.h create mode 100644 Common/DirectXTK12/Inc/SimpleMath.inl create mode 100644 Common/DirectXTK12/Inc/SpriteBatch.h create mode 100644 Common/DirectXTK12/Inc/SpriteFont.h create mode 100644 Common/DirectXTK12/Inc/VertexTypes.h create mode 100644 Common/DirectXTK12/Inc/WICTextureLoader.h create mode 100644 Common/DirectXTK12/Inc/XboxDDSTextureLoader.h create mode 100644 Common/DirectXTK12/LICENSE create mode 100644 Common/DirectXTK12/NuGet.Config create mode 100644 Common/DirectXTK12/README.md create mode 100644 Common/DirectXTK12/SECURITY.md create mode 100644 Common/DirectXTK12/Src/AlignedNew.h create mode 100644 Common/DirectXTK12/Src/AlphaTestEffect.cpp create mode 100644 Common/DirectXTK12/Src/BasicEffect.cpp create mode 100644 Common/DirectXTK12/Src/BasicPostProcess.cpp create mode 100644 Common/DirectXTK12/Src/Bezier.h create mode 100644 Common/DirectXTK12/Src/BinaryReader.cpp create mode 100644 Common/DirectXTK12/Src/BinaryReader.h create mode 100644 Common/DirectXTK12/Src/BufferHelpers.cpp create mode 100644 Common/DirectXTK12/Src/CommonStates.cpp create mode 100644 Common/DirectXTK12/Src/DDS.h create mode 100644 Common/DirectXTK12/Src/DDSTextureLoader.cpp create mode 100644 Common/DirectXTK12/Src/DebugEffect.cpp create mode 100644 Common/DirectXTK12/Src/DemandCreate.h create mode 100644 Common/DirectXTK12/Src/DescriptorHeap.cpp create mode 100644 Common/DirectXTK12/Src/DirectXHelpers.cpp create mode 100644 Common/DirectXTK12/Src/DualPostProcess.cpp create mode 100644 Common/DirectXTK12/Src/DualTextureEffect.cpp create mode 100644 Common/DirectXTK12/Src/EffectCommon.cpp create mode 100644 Common/DirectXTK12/Src/EffectCommon.h create mode 100644 Common/DirectXTK12/Src/EffectFactory.cpp create mode 100644 Common/DirectXTK12/Src/EffectPipelineStateDescription.cpp create mode 100644 Common/DirectXTK12/Src/EffectTextureFactory.cpp create mode 100644 Common/DirectXTK12/Src/EnvironmentMapEffect.cpp create mode 100644 Common/DirectXTK12/Src/GamePad.cpp create mode 100644 Common/DirectXTK12/Src/GeometricPrimitive.cpp create mode 100644 Common/DirectXTK12/Src/Geometry.cpp create mode 100644 Common/DirectXTK12/Src/Geometry.h create mode 100644 Common/DirectXTK12/Src/GraphicsMemory.cpp create mode 100644 Common/DirectXTK12/Src/Keyboard.cpp create mode 100644 Common/DirectXTK12/Src/LinearAllocator.cpp create mode 100644 Common/DirectXTK12/Src/LinearAllocator.h create mode 100644 Common/DirectXTK12/Src/LoaderHelpers.h create mode 100644 Common/DirectXTK12/Src/Model.cpp create mode 100644 Common/DirectXTK12/Src/ModelLoadCMO.cpp create mode 100644 Common/DirectXTK12/Src/ModelLoadSDKMESH.cpp create mode 100644 Common/DirectXTK12/Src/ModelLoadVBO.cpp create mode 100644 Common/DirectXTK12/Src/Mouse.cpp create mode 100644 Common/DirectXTK12/Src/NormalMapEffect.cpp create mode 100644 Common/DirectXTK12/Src/PBREffect.cpp create mode 100644 Common/DirectXTK12/Src/PBREffectFactory.cpp create mode 100644 Common/DirectXTK12/Src/PlatformHelpers.h create mode 100644 Common/DirectXTK12/Src/PrimitiveBatch.cpp create mode 100644 Common/DirectXTK12/Src/ResourceUploadBatch.cpp create mode 100644 Common/DirectXTK12/Src/SDKMesh.h create mode 100644 Common/DirectXTK12/Src/ScreenGrab.cpp create mode 100644 Common/DirectXTK12/Src/Shaders/AlphaTestEffect.fx create mode 100644 Common/DirectXTK12/Src/Shaders/BasicEffect.fx create mode 100644 Common/DirectXTK12/Src/Shaders/Common.fxh create mode 100644 Common/DirectXTK12/Src/Shaders/CompileShaders.cmd create mode 100644 Common/DirectXTK12/Src/Shaders/DebugEffect.fx create mode 100644 Common/DirectXTK12/Src/Shaders/DualTextureEffect.fx create mode 100644 Common/DirectXTK12/Src/Shaders/EnvironmentMapEffect.fx create mode 100644 Common/DirectXTK12/Src/Shaders/GenerateMips.hlsl create mode 100644 Common/DirectXTK12/Src/Shaders/Lighting.fxh create mode 100644 Common/DirectXTK12/Src/Shaders/NormalMapEffect.fx create mode 100644 Common/DirectXTK12/Src/Shaders/PBRCommon.fxh create mode 100644 Common/DirectXTK12/Src/Shaders/PBREffect.fx create mode 100644 Common/DirectXTK12/Src/Shaders/PixelPacking_Velocity.hlsli create mode 100644 Common/DirectXTK12/Src/Shaders/PostProcess.fx create mode 100644 Common/DirectXTK12/Src/Shaders/RootSig.fxh create mode 100644 Common/DirectXTK12/Src/Shaders/SkinnedEffect.fx create mode 100644 Common/DirectXTK12/Src/Shaders/Skinning.fxh create mode 100644 Common/DirectXTK12/Src/Shaders/SpriteEffect.fx create mode 100644 Common/DirectXTK12/Src/Shaders/Structures.fxh create mode 100644 Common/DirectXTK12/Src/Shaders/ToneMap.fx create mode 100644 Common/DirectXTK12/Src/Shaders/Utilities.fxh create mode 100644 Common/DirectXTK12/Src/SharedResourcePool.h create mode 100644 Common/DirectXTK12/Src/SimpleMath.cpp create mode 100644 Common/DirectXTK12/Src/SkinnedEffect.cpp create mode 100644 Common/DirectXTK12/Src/SpriteBatch.cpp create mode 100644 Common/DirectXTK12/Src/SpriteFont.cpp create mode 100644 Common/DirectXTK12/Src/TeapotData.inc create mode 100644 Common/DirectXTK12/Src/ToneMapPostProcess.cpp create mode 100644 Common/DirectXTK12/Src/VertexTypes.cpp create mode 100644 Common/DirectXTK12/Src/WICTextureLoader.cpp create mode 100644 Common/DirectXTK12/Src/XboxDDSTextureLoader.cpp create mode 100644 Common/DirectXTK12/Src/d3dx12.h create mode 100644 Common/DirectXTK12/Src/pch.cpp create mode 100644 Common/DirectXTK12/Src/pch.h create mode 100644 Common/DirectXTK12/Src/vbo.h create mode 100644 Common/DirectXTK12/build/CopyASan.targets create mode 100644 Common/DirectXTK12/build/DirectXTK12-GitHub-CMake-Dev17.yml create mode 100644 Common/DirectXTK12/build/DirectXTK12-GitHub-CMake.yml create mode 100644 Common/DirectXTK12/build/DirectXTK12-GitHub-Dev17.yml create mode 100644 Common/DirectXTK12/build/DirectXTK12-GitHub-GDK-Dev17.yml create mode 100644 Common/DirectXTK12/build/DirectXTK12-GitHub-GDK.yml create mode 100644 Common/DirectXTK12/build/DirectXTK12-GitHub-MinGW.yml create mode 100644 Common/DirectXTK12/build/DirectXTK12-GitHub-SDK-prerelease.yml create mode 100644 Common/DirectXTK12/build/DirectXTK12-GitHub-SDK-release.yml create mode 100644 Common/DirectXTK12/build/DirectXTK12-GitHub-Test-Dev17.yml create mode 100644 Common/DirectXTK12/build/DirectXTK12-GitHub-Test.yml create mode 100644 Common/DirectXTK12/build/DirectXTK12-GitHub.yml create mode 100644 Common/DirectXTK12/build/DirectXTK12-OneFuzz.yml create mode 100644 Common/DirectXTK12/build/DirectXTK12-SDL.yml create mode 100644 Common/DirectXTK12/build/DirectXTK12-config.cmake.in create mode 100644 Common/DirectXTK12/build/Directory.Build.props create mode 100644 Common/DirectXTK12/build/OneFuzzConfig.json create mode 100644 Common/DirectXTK12/build/SetupBWOI.targets create mode 100644 Common/DirectXTK12/build/placeholder.xvd delete mode 160000 DirectXTK12 diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index a3628e2..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "DirectXTK12"] - path = DirectXTK12 - url = git@github.com:microsoft/DirectXTK12.git diff --git a/Common/DeviceResources.cpp b/Common/DeviceResources.cpp index c0b7f10..45ed2c0 100644 --- a/Common/DeviceResources.cpp +++ b/Common/DeviceResources.cpp @@ -76,13 +76,14 @@ DeviceResources::DeviceResources( // Destructor for DeviceResources. DeviceResources::~DeviceResources() { - ThrowIfFailed(m_swapChain->SetFullscreenState(FALSE, NULL)); - // Ensure that the GPU is no longer referencing resources that are about to be destroyed. + // Ensure that the GPU is no longer referencing resources that are about to be destroyed. WaitForGpu(); + + if (m_fullScreenMode) ThrowIfFailed(m_swapChain->SetFullscreenState(FALSE, nullptr)); } // Configures the Direct3D device, and stores handles to it and the device context. -void DeviceResources::CreateDeviceResources() +void DeviceResources::CreateDeviceResources(BOOL fullScreenMode) { #if defined(_DEBUG) // Enable the debug layer (requires the Graphics Tools "optional feature"). @@ -123,6 +124,10 @@ void DeviceResources::CreateDeviceResources() ThrowIfFailed(CreateDXGIFactory2(m_dxgiFactoryFlags, IID_PPV_ARGS(m_dxgiFactory.ReleaseAndGetAddressOf()))); + m_fullScreenMode = fullScreenMode; + if (m_fullScreenMode) + m_options = 0x0; + // Determines whether tearing support is available for fullscreen borderless windows. if (m_options & c_AllowTearing) { @@ -337,7 +342,7 @@ void DeviceResources::CreateWindowSizeDependentResources() swapChainDesc.Scaling = DXGI_SCALING_STRETCH; swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_IGNORE; - swapChainDesc.Flags = m_options; + swapChainDesc.Flags = (m_options & c_AllowTearing) ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0u; DXGI_SWAP_CHAIN_FULLSCREEN_DESC fsSwapChainDesc = {}; fsSwapChainDesc.Windowed = TRUE; @@ -357,15 +362,18 @@ void DeviceResources::CreateWindowSizeDependentResources() // This class does not support exclusive full-screen mode and prevents DXGI from responding to the ALT+ENTER shortcut ThrowIfFailed(m_dxgiFactory->MakeWindowAssociation(m_window, DXGI_MWA_NO_ALT_ENTER)); - ThrowIfFailed(m_swapChain->SetFullscreenState(TRUE, NULL)); - ThrowIfFailed(m_swapChain->ResizeBuffers( - m_backBufferCount, - backBufferWidth, - backBufferHeight, - backBufferFormat, - m_options - )); + if (m_fullScreenMode) + { + ThrowIfFailed(m_swapChain->SetFullscreenState(TRUE, NULL)); + ThrowIfFailed(m_swapChain->ResizeBuffers( + m_backBufferCount, + backBufferWidth, + backBufferHeight, + backBufferFormat, + m_options + )); + } } // Handle color space settings for HDR @@ -512,7 +520,7 @@ void DeviceResources::HandleDeviceLost() m_d3dDevice.Reset(); m_dxgiFactory.Reset(); - CreateDeviceResources(); + CreateDeviceResources(m_fullScreenMode); CreateWindowSizeDependentResources(); if (m_deviceNotify) diff --git a/Common/DeviceResources.h b/Common/DeviceResources.h index a97ca7d..40e6f23 100644 --- a/Common/DeviceResources.h +++ b/Common/DeviceResources.h @@ -37,7 +37,7 @@ namespace DX DeviceResources(DeviceResources const&) = delete; DeviceResources& operator= (DeviceResources const&) = delete; - void CreateDeviceResources(); + void CreateDeviceResources(BOOL fullScreenMode); void CreateWindowSizeDependentResources(); void SetWindow(HWND window, int width, int height) noexcept; bool WindowSizeChanged(int width, int height); @@ -147,5 +147,7 @@ namespace DX // The IDeviceNotify can be held directly as it owns the DeviceResources. IDeviceNotify* m_deviceNotify; + + BOOL m_fullScreenMode; }; } diff --git a/Common/DirectXTK12/.gitignore b/Common/DirectXTK12/.gitignore new file mode 100644 index 0000000..716bf49 --- /dev/null +++ b/Common/DirectXTK12/.gitignore @@ -0,0 +1,29 @@ +*.psess +*.vsp +*.log +*.err +*.wrn +*.suo +*.sdf +*.user +*.i +*.vspscc +*.opensdf +*.opendb +*.ipch +*.cache +*.tlog +*.lastbuildstate +*.ilk +*.VC.db +*.nupkg +.vs +Bin +/Src/Shaders/Compiled/*.inc +/Src/Shaders/Compiled/*.pdb +/ipch +/Tests +/Testing +/wiki +/out +/CMakeUserPresets.json diff --git a/Common/DirectXTK12/.nuget/directxtk12_desktop_2019.nuspec b/Common/DirectXTK12/.nuget/directxtk12_desktop_2019.nuspec new file mode 100644 index 0000000..34ddb68 --- /dev/null +++ b/Common/DirectXTK12/.nuget/directxtk12_desktop_2019.nuspec @@ -0,0 +1,77 @@ + + + + directxtk12_desktop_2019 + 0.0.0-SpecifyVersionOnCommandline + DirectX Tool Kit for DirectX 12 (VS 2019/2022 Win32 for Windows 10/11) + Microsoft + microsoft,directxtk + The DirectX Tool Kit (aka DirectXTK) is a collection of helper classes for writing Direct3D 12 code in C++. + This version is for Windows desktop applications using Visual Studio 2019 (16.11) or Visual Studio 2022. + +Features: +Audio - low-level audio API using XAudio2 +BufferHelpers - C++ helpers for creating D3D resources from CPU data +CommonStates - common D3D state combinations +DDSTextureLoader - light-weight DDS file texture loader +DescriptorHeap - helper for managing DX12 descriptor heaps +DirectXHelpers - misc C++ helpers for D3D programming +Effects - set of built-in shaders for common rendering tasks +EffectPipelineStateDescription - helper for creating PSOs +GamePad - gamepad controller helper using Windows.Gaming.Input +GeometricPrimitive - draws basic shapes such as cubes and spheres +GraphicsMemory - helper for managing graphics memory allocation +Keyboard - keyboard state tracking helper +Model - draws meshes loaded from .CMO, .SDKMESH, or .VBO files +Mouse - mouse helper +PostProcess - set of built-in shaders for common post-processing operations +PrimitiveBatch - simple and efficient way to draw user primitives +RenderTargetState - helper for communicating render target requirements when creating PSOs +ResourceUploadBatch - helper for managing texture resource upload to the GPU +ScreenGrab - light-weight screen shot saver +SimpleMath - simplified C++ wrapper for DirectXMath +SpriteBatch - simple & efficient 2D sprite rendering +SpriteFont - bitmap based text rendering +VertexTypes - structures for commonly used vertex data formats +WICTextureLoader - WIC-based image file texture loader + Matches the October 28, 2023 release on GitHub. + http://go.microsoft.com/fwlink/?LinkID=615561 + + images\icon.jpg + docs\README.md + MIT + false + © Microsoft Corporation. All rights reserved. + DirectX DirectXTK DirectXTK12 native nativepackage + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Common/DirectXTK12/.nuget/directxtk12_desktop_2019.targets b/Common/DirectXTK12/.nuget/directxtk12_desktop_2019.targets new file mode 100644 index 0000000..e7dc68b --- /dev/null +++ b/Common/DirectXTK12/.nuget/directxtk12_desktop_2019.targets @@ -0,0 +1,35 @@ + + + + + Debug + + + Release + + + Release + + + Release + + + + $(MSBuildThisFileDirectory)..\..\native\lib\$(PlatformTarget)\$(NuGetConfiguration) + + + + + $(directxtk12-LibPath);%(AdditionalLibraryDirectories) + DirectXTK12.lib;%(AdditionalDependencies) + + + + + + HAS_DIRECTXTK12;%(PreprocessorDefinitions) + $(MSBuildThisFileDirectory)..\..\include;%(AdditionalIncludeDirectories) + + + + diff --git a/Common/DirectXTK12/.nuget/directxtk12_uwp.nuspec b/Common/DirectXTK12/.nuget/directxtk12_uwp.nuspec new file mode 100644 index 0000000..36cd9f4 --- /dev/null +++ b/Common/DirectXTK12/.nuget/directxtk12_uwp.nuspec @@ -0,0 +1,77 @@ + + + + directxtk12_uwp + 0.0.0-SpecifyVersionOnCommandline + DirectX Tool Kit for DirectX 12 (UWP) + Microsoft + microsoft,directxtk + The DirectX Tool Kit (aka DirectXTK) is a collection of helper classes for writing Direct3D 12 code in C++. + This version is for Universal Windows Platform apps on Windows 10 / Windows 11 using Visual Studio 2019 (16.11) or Visual Studio 2022. + +Features: +Audio - low-level audio API using XAudio2 +BufferHelpers - C++ helpers for creating D3D resources from CPU data +CommonStates - common D3D state combinations +DDSTextureLoader - light-weight DDS file texture loader +DescriptorHeap - helper for managing DX12 descriptor heaps +DirectXHelpers - misc C++ helpers for D3D programming +Effects - set of built-in shaders for common rendering tasks +EffectPipelineStateDescription - helper for creating PSOs +GamePad - gamepad controller helper using Windows.Gaming.Input +GeometricPrimitive - draws basic shapes such as cubes and spheres +GraphicsMemory - helper for managing graphics memory allocation +Keyboard - keyboard state tracking helper +Model - draws meshes loaded from .CMO, .SDKMESH, or .VBO files +Mouse - mouse helper +PostProcess - set of built-in shaders for common post-processing operations +PrimitiveBatch - simple and efficient way to draw user primitives +RenderTargetState - helper for communicating render target requirements when creating PSOs +ResourceUploadBatch - helper for managing texture resource upload to the GPU +ScreenGrab - light-weight screen shot saver +SimpleMath - simplified C++ wrapper for DirectXMath +SpriteBatch - simple & efficient 2D sprite rendering +SpriteFont - bitmap based text rendering +VertexTypes - structures for commonly used vertex data formats +WICTextureLoader - WIC-based image file texture loader + Matches the October 28, 2023 release on GitHub. + http://go.microsoft.com/fwlink/?LinkID=615561 + + images\icon.jpg + docs\README.md + MIT + false + © Microsoft Corporation. All rights reserved. + DirectX DirectXTK DirectXTK12 native nativepackage + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Common/DirectXTK12/.nuget/directxtk12_uwp.targets b/Common/DirectXTK12/.nuget/directxtk12_uwp.targets new file mode 100644 index 0000000..e7dc68b --- /dev/null +++ b/Common/DirectXTK12/.nuget/directxtk12_uwp.targets @@ -0,0 +1,35 @@ + + + + + Debug + + + Release + + + Release + + + Release + + + + $(MSBuildThisFileDirectory)..\..\native\lib\$(PlatformTarget)\$(NuGetConfiguration) + + + + + $(directxtk12-LibPath);%(AdditionalLibraryDirectories) + DirectXTK12.lib;%(AdditionalDependencies) + + + + + + HAS_DIRECTXTK12;%(PreprocessorDefinitions) + $(MSBuildThisFileDirectory)..\..\include;%(AdditionalIncludeDirectories) + + + + diff --git a/Common/DirectXTK12/.nuget/icon.jpg b/Common/DirectXTK12/.nuget/icon.jpg new file mode 100644 index 0000000000000000000000000000000000000000..08fe1faeb7f6e45d796cf1e67bf1cbb1347c514a GIT binary patch literal 3479 zcmbW$XHe5?mjLj82q7Rr2u1}(Y6PT8@1RJ((ximmf=CfjB1P&|i4>8JSGXbwVrWvN zBb`vCw;<9%dPy)y2mxMvcjxZR&VJb4^UV45oO$N_<~-+Q;$#kBGtkl30YD%S;BN{b_aR={-OXhp_WW z-DEgt>I~(7gphuk_?}Tfv#jO(ogpGp#>Mv;)0qnwIXEv}6%-P_CL${*ub`-;d`nAP zM^{hZz|7pj5@ls=gLb{^cF*0z6Z6>5KOpc)(DMlFi^!{3zLA% z9pZTx-=Q-Xkh1evNxy0Tmi_Nw&;DPse_;RRngn2A(CP5Na6kjtrwV8Pc}BQcs3RN^ zqB7U+Ui?6PBs5OkNy(>k_)is2(G@0jHLtCOS0Y}8aNO!gr(rF$N^Zmt{cf&BrNx?( z9Mf?vlnyKwWC`Vsw*mYRsHZdoN?XxtnDTKSk(O9tSs;+c364gpHQ^ATofK3Y<&VYf zDPvP_;AF|$jI{K^lUTY(K zn7ol&+vJ?@x!p3k5Qg%{L_nLq_y)VA%^lJCoSmpImpHmZ3b6aSpC&W#*ReWo%~tZ9 zYy>Um(b`C+!7j-vP3_udwd#}+zpdFfuGw~R-gRZvcQgh$?x=1|-w{OGYN4?N`84mG zKLJ{`=M;-@$$r}@+T}~T=+5o$y9eQOa=rF>b=A|#uf^ZoWue)HNAh4FHl3C8Q0Jyu zT?!pZDXVw(;zM^3T3($1;OckHl@4^((ho!BM8T+ZVi<0iTbcxOxIoN3{v--G#~z>$ z`-52f8_~oEZ|nLtW<0}XJY_g@eWhn^-{-)RC3!8NrDpa`o!()FUieD^&x>3+EZOb2 zh#cgBQcl6bz_1qY&8^}bY=pEyKDUk(8=%v4M?q;HWABISiK{FBpC* z@~Fi%UCbw5^|Y~>#cGG3m=#sN#n9k+^{P6fQe@p=?)^=u3P>YN^45C8QM??%&UL!y zh}LoJ$55F4o$^wvrs_*H$iMf_B7@)+EB3JGB)Junj!IFrc;z^$eCy5NPuQd4d}qjB z?3!B9Dl*1g35IL1D#-|Aq%^LyH6k;csr8vP={kqkg!q-}QlW>%O5Bv=z0mUxbpd1C zp997qA+Hu^Ig86l7L{zI%G}si6^!%+?a45bY4^(8fc8*~9WJJki-PVD?kn)l z@_gSnp2D_4RzjrLmC9i{@7L>lV4Qy1@jI0Y?oP)hoB+*DF*3)*&Ib36+lkS9Z#cT> zrR6Omeb818{1ncGcv$~rB(}uX1gpf%xx0-BLM!?-|6H@P$c&Mw_|h%DC+e{&ky<9& z!rhih^;JHfFY}f@mG~?Oc-gj$}>%ZBKOsxb_9h5?*OCWMaL?K1B0w6=h>g$sw?hFN*f~$kIm~x z{Db};GeV8{;yuo&-}O=T!;8N?)*z7cD(&xx{((`$4_Y_Z*lF|tn+#8UTI-ijfCZN2 zkI#*UWSeGt+V0Od9Q4byVmXdjd;$aJd|s*=O3zSYmexuYga*`Imx2W!HbwIo^mCl0 z3JA{yuf~bC2OVYXM0v|y;mmj-UX`h4=C)#%>ZH6Bjt#}m9~o9=o?V+vZY4I1)ftfL zQibt!I;`*eFf#~>?YuD~ebQHB+n4Ppz+YdA^aVq8@6v$c{su;EO%dUp2y>V0XECDf zA=adM?~>f%5SfpL0U^a`Nq#N{E;fX(p{Z`!^o*^QZDsp3{-I{h)g`Aa6gI?!k~nAR zHch5fMD2Z;#I9H0x8a51=xJ(c$|0)yu}Y731@B({_V zIS*1Qa*aQrGY9AzQwBAyqS)Qsm-L$972((IoF0(-l~HiWRMJ)zHYDngP&_7$bw8u7 z>CfPwPcy~44!b56RiBCKqW0rd z^lB|CwnlfuK{TyeikDK^XuT3xl&gM5FK+I1n|yerss8&VSu{x!PaC(mw!MalR;`*h zP6atox*856#LrOt$B)HJMYJztmfqBV4GX>a3OCJI%wtJ3Ekmo-m$2n!NqM zI48cN_?-y5e~{D&zSTx;piHEohpWt05jAUO4hc`0^ZuV5XU>p#c8 zh-3}%lUoYA`g+G^{L|xLbGN$s3|IFr2T4v^kw>GVWU;Z;8L82nsyIC)J|Tk;06iSc zzl=I&_<>rhD?g^A_&c%%_V2f&HX6jDh4BwZy5EIEx_|W$j2L1J2dKwQb5|5aEbIF* zrbEjVGm@0p$CR(Gt2zC5o0EjN-iCi}J^^Os*2H;ZOpI<^<&uswf1aRQ5}qfQPdaSP z{LT%Jx2=v>u(7>MRJvZxa=^v&(JPW@S>YECYjyi->=A`@9(4*SG9I}Sl;E+L-yov< z-fGgvAaT{lu6calw{^XMb%(BG8b}_Qd>2(Nq1yZzO|PKCy+ZTcTJEea{T}It{R}M0 zW8zH{yQqXT%1HZ5cKO6ym4@CXWN(8xw>->Gu=ZX=%6g-H+E+0#9BO)^*YIk{BNG4j zTf^_;RLm4!55Syn+`u3s!Gm03+?Br|mGzdd%WQ8v<-^juKhMtN0PmK_P-KYn^;dG# zaryX*){UmM#U1xYqp#6wx)RK4+CP3FRqt)9P^eYdFc&o2S^F)1a%5&s>K`MbhNdwzh(4!tY~Jy zq~gW;q;UhH>x%K5rx0VGjlohsJiQ{3go_TboLscKc|Q(alg7bb;~mo^7RVjjAGoep zQqag1Xcef@ObvU}1sQTwUZwU-RsU!`LRiu}f@uO?3tr-$Jt}{Pm){mV6iRCpbnZI* zCIE#NE0!4G7==Z7BM|2kS*D8=#xXW_yh%)p?n}3ZQesaRzN=@KCQaJ@^kD`XY+5JN zIqTSPd2v;D*=vJ3U++jPY&(`CeDbq5Ew-3{+2cOv^vvX}&SESnc?4k~V$kawlT@xA zSW9})w9rk^BJM?eOZ{BPC@up-BToRvPBSW@j3z;mf?_|sv?kCBKRJaAX{oz!#g`l! z?+p6ymFN!E%VbmBMmFo@|AxH1xAVm7r^aFL6CeC|CHz@jYpl8d8R6`_tn3(J;D1Q) H$+v$2rQD|6 literal 0 HcmV?d00001 diff --git a/Common/DirectXTK12/Audio/AudioEngine.cpp b/Common/DirectXTK12/Audio/AudioEngine.cpp new file mode 100644 index 0000000..6dc8042 --- /dev/null +++ b/Common/DirectXTK12/Audio/AudioEngine.cpp @@ -0,0 +1,1922 @@ +//-------------------------------------------------------------------------------------- +// File: AudioEngine.cpp +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248929 +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "pch.h" +#include "Audio.h" +#include "SoundCommon.h" + +#include + +using namespace DirectX; +using Microsoft::WRL::ComPtr; + +//#define VERBOSE_TRACE + +#ifdef VERBOSE_TRACE +#pragma message("NOTE: Verbose tracing enabled") +#endif + +namespace +{ + struct EngineCallback : public IXAudio2EngineCallback + { + EngineCallback() noexcept(false) + { + mCriticalError.reset(CreateEventEx(nullptr, nullptr, 0, EVENT_MODIFY_STATE | SYNCHRONIZE)); + if (!mCriticalError) + { + throw std::system_error(std::error_code(static_cast(GetLastError()), std::system_category()), "CreateEventEx"); + } + } + + EngineCallback(EngineCallback&&) = default; + EngineCallback& operator= (EngineCallback&&) = default; + + EngineCallback(EngineCallback const&) = delete; + EngineCallback& operator= (EngineCallback const&) = delete; + + virtual ~EngineCallback() = default; + + STDMETHOD_(void, OnProcessingPassStart) () override {} + STDMETHOD_(void, OnProcessingPassEnd)() override {} + + STDMETHOD_(void, OnCriticalError) (THIS_ HRESULT error) + { + #ifndef _DEBUG + UNREFERENCED_PARAMETER(error); + #endif + DebugTrace("ERROR: AudioEngine encountered critical error (%08X)\n", static_cast(error)); + SetEvent(mCriticalError.get()); + } + + ScopedHandle mCriticalError; + }; + + struct VoiceCallback : public IXAudio2VoiceCallback + { + VoiceCallback() noexcept(false) + { + mBufferEnd.reset(CreateEventEx(nullptr, nullptr, 0, EVENT_MODIFY_STATE | SYNCHRONIZE)); + if (!mBufferEnd) + { + throw std::system_error(std::error_code(static_cast(GetLastError()), std::system_category()), "CreateEventEx"); + } + } + + VoiceCallback(VoiceCallback&&) = default; + VoiceCallback& operator=(VoiceCallback&&) = default; + + VoiceCallback(const VoiceCallback&) = delete; + VoiceCallback& operator=(const VoiceCallback&) = delete; + + virtual ~VoiceCallback() + { + } + + STDMETHOD_(void, OnVoiceProcessingPassStart) (UINT32) override {} + STDMETHOD_(void, OnVoiceProcessingPassEnd)() override {} + STDMETHOD_(void, OnStreamEnd)() override {} + STDMETHOD_(void, OnBufferStart)(void*) override {} + + STDMETHOD_(void, OnBufferEnd)(void* context) override + { + if (context) + { + auto inotify = static_cast(context); + inotify->OnBufferEnd(); + SetEvent(mBufferEnd.get()); + } + } + + STDMETHOD_(void, OnLoopEnd)(void*) override {} + STDMETHOD_(void, OnVoiceError)(void*, HRESULT) override {} + + ScopedHandle mBufferEnd; + }; + + static const XAUDIO2FX_REVERB_I3DL2_PARAMETERS gReverbPresets[] = + { + XAUDIO2FX_I3DL2_PRESET_DEFAULT, // Reverb_Off + XAUDIO2FX_I3DL2_PRESET_DEFAULT, // Reverb_Default + XAUDIO2FX_I3DL2_PRESET_GENERIC, // Reverb_Generic + XAUDIO2FX_I3DL2_PRESET_FOREST, // Reverb_Forest + XAUDIO2FX_I3DL2_PRESET_PADDEDCELL, // Reverb_PaddedCell + XAUDIO2FX_I3DL2_PRESET_ROOM, // Reverb_Room + XAUDIO2FX_I3DL2_PRESET_BATHROOM, // Reverb_Bathroom + XAUDIO2FX_I3DL2_PRESET_LIVINGROOM, // Reverb_LivingRoom + XAUDIO2FX_I3DL2_PRESET_STONEROOM, // Reverb_StoneRoom + XAUDIO2FX_I3DL2_PRESET_AUDITORIUM, // Reverb_Auditorium + XAUDIO2FX_I3DL2_PRESET_CONCERTHALL, // Reverb_ConcertHall + XAUDIO2FX_I3DL2_PRESET_CAVE, // Reverb_Cave + XAUDIO2FX_I3DL2_PRESET_ARENA, // Reverb_Arena + XAUDIO2FX_I3DL2_PRESET_HANGAR, // Reverb_Hangar + XAUDIO2FX_I3DL2_PRESET_CARPETEDHALLWAY, // Reverb_CarpetedHallway + XAUDIO2FX_I3DL2_PRESET_HALLWAY, // Reverb_Hallway + XAUDIO2FX_I3DL2_PRESET_STONECORRIDOR, // Reverb_StoneCorridor + XAUDIO2FX_I3DL2_PRESET_ALLEY, // Reverb_Alley + XAUDIO2FX_I3DL2_PRESET_CITY, // Reverb_City + XAUDIO2FX_I3DL2_PRESET_MOUNTAINS, // Reverb_Mountains + XAUDIO2FX_I3DL2_PRESET_QUARRY, // Reverb_Quarry + XAUDIO2FX_I3DL2_PRESET_PLAIN, // Reverb_Plain + XAUDIO2FX_I3DL2_PRESET_PARKINGLOT, // Reverb_ParkingLot + XAUDIO2FX_I3DL2_PRESET_SEWERPIPE, // Reverb_SewerPipe + XAUDIO2FX_I3DL2_PRESET_UNDERWATER, // Reverb_Underwater + XAUDIO2FX_I3DL2_PRESET_SMALLROOM, // Reverb_SmallRoom + XAUDIO2FX_I3DL2_PRESET_MEDIUMROOM, // Reverb_MediumRoom + XAUDIO2FX_I3DL2_PRESET_LARGEROOM, // Reverb_LargeRoom + XAUDIO2FX_I3DL2_PRESET_MEDIUMHALL, // Reverb_MediumHall + XAUDIO2FX_I3DL2_PRESET_LARGEHALL, // Reverb_LargeHall + XAUDIO2FX_I3DL2_PRESET_PLATE, // Reverb_Plate + }; + + inline unsigned int makeVoiceKey(_In_ const WAVEFORMATEX* wfx) noexcept + { + assert(IsValid(wfx)); + + if (wfx->nChannels > 0x7F) + return 0; + + // This hash does not use nSamplesPerSec because voice reuse can change the source sample rate. + + // nAvgBytesPerSec and nBlockAlign are derived from other values in XAudio2 supported formats. + + union KeyGen + { + struct + { + unsigned int tag : 9; + unsigned int channels : 7; + unsigned int bitsPerSample : 8; + } pcm; + + struct + { + unsigned int tag : 9; + unsigned int channels : 7; + unsigned int samplesPerBlock : 16; + } adpcm; + + #ifdef DIRECTX_ENABLE_XMA2 + struct + { + unsigned int tag : 9; + unsigned int channels : 7; + unsigned int encoderVersion : 8; + } xma; + #endif + + unsigned int key; + } result; + + static_assert(sizeof(KeyGen) == sizeof(unsigned int), "KeyGen is invalid"); + + result.key = 0; + + if (wfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE) + { + // We reuse EXTENSIBLE only if it is equivalent to the standard form + auto wfex = reinterpret_cast(wfx); + if (wfex->Samples.wValidBitsPerSample != 0 && wfex->Samples.wValidBitsPerSample != wfx->wBitsPerSample) + return 0; + + if (wfex->dwChannelMask != 0 && wfex->dwChannelMask != GetDefaultChannelMask(wfx->nChannels)) + return 0; + } + + const uint32_t tag = GetFormatTag(wfx); + switch (tag) + { + case WAVE_FORMAT_PCM: + static_assert(WAVE_FORMAT_PCM < 0x1ff, "KeyGen tag is too small"); + result.pcm.tag = WAVE_FORMAT_PCM; + result.pcm.channels = wfx->nChannels; + result.pcm.bitsPerSample = wfx->wBitsPerSample; + break; + + case WAVE_FORMAT_IEEE_FLOAT: + static_assert(WAVE_FORMAT_IEEE_FLOAT < 0x1ff, "KeyGen tag is too small"); + + if (wfx->wBitsPerSample != 32) + return 0; + + result.pcm.tag = WAVE_FORMAT_IEEE_FLOAT; + result.pcm.channels = wfx->nChannels; + result.pcm.bitsPerSample = 32; + break; + + case WAVE_FORMAT_ADPCM: + static_assert(WAVE_FORMAT_ADPCM < 0x1ff, "KeyGen tag is too small"); + result.adpcm.tag = WAVE_FORMAT_ADPCM; + result.adpcm.channels = wfx->nChannels; + + { + auto wfadpcm = reinterpret_cast(wfx); + result.adpcm.samplesPerBlock = wfadpcm->wSamplesPerBlock; + } + break; + + #ifdef DIRECTX_ENABLE_XMA2 + case WAVE_FORMAT_XMA2: + static_assert(WAVE_FORMAT_XMA2 < 0x1ff, "KeyGen tag is too small"); + result.xma.tag = WAVE_FORMAT_XMA2; + result.xma.channels = wfx->nChannels; + + { + auto xmaFmt = reinterpret_cast(wfx); + + if ((xmaFmt->LoopBegin > 0) + || (xmaFmt->PlayBegin > 0)) + return 0; + + result.xma.encoderVersion = xmaFmt->EncoderVersion; + } + break; + #endif + + default: + return 0; + } + + return result.key; + } + + void GetDeviceOutputFormat(const wchar_t* deviceId, WAVEFORMATEX& wfx); +} + +static_assert(static_cast(std::size(gReverbPresets)) == Reverb_MAX, "AUDIO_ENGINE_REVERB enum mismatch"); + + +//====================================================================================== +// AudioEngine +//====================================================================================== + +#define SAFE_DESTROY_VOICE(voice) if ( voice ) { voice->DestroyVoice(); voice = nullptr; } + +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wextra-semi-stmt" +#endif + +// Internal object implementation class. +class AudioEngine::Impl +{ +public: + Impl() noexcept : + mMasterVoice(nullptr), + mReverbVoice(nullptr), + masterChannelMask(0), + masterChannels(0), + masterRate(0), + defaultRate(44100), + maxVoiceOneshots(SIZE_MAX), + maxVoiceInstances(SIZE_MAX), + mMasterVolume(1.f), + mX3DAudio{}, + mCriticalError(false), + mReverbEnabled(false), + mEngineFlags(AudioEngine_Default), + mOutputFormat{}, + mCategory(AudioCategory_GameEffects), + mVoiceInstances(0) + { + } + + ~Impl() = default; + + Impl(Impl&&) = default; + Impl& operator= (Impl&&) = default; + + Impl(Impl const&) = delete; + Impl& operator= (Impl const&) = delete; + + HRESULT Initialize(AUDIO_ENGINE_FLAGS flags, + _In_opt_ const WAVEFORMATEX* wfx, + _In_opt_z_ const wchar_t* deviceId, + AUDIO_STREAM_CATEGORY category); + + HRESULT Reset(_In_opt_ const WAVEFORMATEX* wfx, _In_opt_z_ const wchar_t* deviceId); + + void SetSilentMode(); + + void Shutdown() noexcept; + + bool Update(); + + void SetReverb(_In_opt_ const XAUDIO2FX_REVERB_PARAMETERS* native) noexcept; + + void SetMasteringLimit(int release, int loudness); + + AudioStatistics GetStatistics() const; + + void TrimVoicePool(); + + void AllocateVoice(_In_ const WAVEFORMATEX* wfx, + SOUND_EFFECT_INSTANCE_FLAGS flags, bool oneshot, + _Outptr_result_maybenull_ IXAudio2SourceVoice** voice); + void DestroyVoice(_In_ IXAudio2SourceVoice* voice) noexcept; + + void RegisterNotify(_In_ IVoiceNotify* notify, bool usesUpdate); + void UnregisterNotify(_In_ IVoiceNotify* notify, bool oneshots, bool usesUpdate); + + ComPtr xaudio2; + IXAudio2MasteringVoice* mMasterVoice; + IXAudio2SubmixVoice* mReverbVoice; + + uint32_t masterChannelMask; + uint32_t masterChannels; + uint32_t masterRate; + + int defaultRate; + size_t maxVoiceOneshots; + size_t maxVoiceInstances; + float mMasterVolume; + + X3DAUDIO_HANDLE mX3DAudio; + + bool mCriticalError; + bool mReverbEnabled; + + AUDIO_ENGINE_FLAGS mEngineFlags; + WAVEFORMATEX mOutputFormat; + +private: + using notifylist_t = std::set; + using oneshotlist_t = std::list>; + using voicepool_t = std::unordered_multimap; + + AUDIO_STREAM_CATEGORY mCategory; + ComPtr mReverbEffect; + ComPtr mVolumeLimiter; + oneshotlist_t mOneShots; + voicepool_t mVoicePool; + notifylist_t mNotifyObjects; + notifylist_t mNotifyUpdates; + size_t mVoiceInstances; + VoiceCallback mVoiceCallback; + EngineCallback mEngineCallback; +}; + + +_Use_decl_annotations_ +HRESULT AudioEngine::Impl::Initialize( + AUDIO_ENGINE_FLAGS flags, + const WAVEFORMATEX* wfx, + const wchar_t* deviceId, + AUDIO_STREAM_CATEGORY category) +{ + mEngineFlags = flags; + mCategory = category; + + return Reset(wfx, deviceId); +} + + +_Use_decl_annotations_ +HRESULT AudioEngine::Impl::Reset(const WAVEFORMATEX* wfx, const wchar_t* deviceId) +{ + if (wfx) + { + if (wfx->wFormatTag != WAVE_FORMAT_PCM) + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + + if (!wfx->nChannels || wfx->nChannels > XAUDIO2_MAX_AUDIO_CHANNELS) + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + + if (wfx->nSamplesPerSec < XAUDIO2_MIN_SAMPLE_RATE || wfx->nSamplesPerSec > XAUDIO2_MAX_SAMPLE_RATE) + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + + // We don't use other data members of WAVEFORMATEX here to describe the device format, so no need to fully validate + } + + assert(!xaudio2); + assert(mMasterVoice == nullptr); + assert(mReverbVoice == nullptr); + + masterChannelMask = masterChannels = masterRate = 0; + mOutputFormat = {}; + + memset(&mX3DAudio, 0, X3DAUDIO_HANDLE_BYTESIZE); + + mCriticalError = false; + mReverbEnabled = false; + + // + // Create XAudio2 engine + // + HRESULT hr = XAudio2Create(xaudio2.ReleaseAndGetAddressOf(), 0u); + if (FAILED(hr)) + return hr; + + if (mEngineFlags & AudioEngine_Debug) + { + XAUDIO2_DEBUG_CONFIGURATION debug = {}; + debug.TraceMask = XAUDIO2_LOG_ERRORS | XAUDIO2_LOG_WARNINGS; + debug.BreakMask = XAUDIO2_LOG_ERRORS; + xaudio2->SetDebugConfiguration(&debug, nullptr); + #ifdef USING_XAUDIO2_9 + DebugTrace("INFO: XAudio 2.9 debugging enabled\n"); + #else // USING_XAUDIO2_8 + // To see the trace output, you need to view ETW logs for this application: + // Go to Control Panel, Administrative Tools, Event Viewer. + // View->Show Analytic and Debug Logs. + // Applications and Services Logs / Microsoft / Windows / XAudio2. + // Right click on Microsoft Windows XAudio2 debug logging, Properties, then Enable Logging, and hit OK + DebugTrace("INFO: XAudio 2.8 debugging enabled\n"); + #endif + } + + if (mEngineFlags & AudioEngine_DisableVoiceReuse) + { + DebugTrace("INFO: Voice reuse is disabled\n"); + } + + hr = xaudio2->RegisterForCallbacks(&mEngineCallback); + if (FAILED(hr)) + { + xaudio2.Reset(); + return hr; + } + + // + // Create mastering voice for device + // + hr = xaudio2->CreateMasteringVoice(&mMasterVoice, + (wfx) ? wfx->nChannels : 0u /*XAUDIO2_DEFAULT_CHANNELS */, + (wfx) ? wfx->nSamplesPerSec : 0u /* XAUDIO2_DEFAULT_SAMPLERATE */, + 0u, deviceId, nullptr, mCategory); + if (FAILED(hr)) + { + xaudio2.Reset(); + return hr; + } + + DWORD dwChannelMask; + hr = mMasterVoice->GetChannelMask(&dwChannelMask); + if (FAILED(hr)) + { + SAFE_DESTROY_VOICE(mMasterVoice); + xaudio2.Reset(); + return hr; + } + + XAUDIO2_VOICE_DETAILS details; + mMasterVoice->GetVoiceDetails(&details); + + masterChannelMask = dwChannelMask; + masterChannels = details.InputChannels; + masterRate = details.InputSampleRate; + + DebugTrace("INFO: mastering voice has %u channels, %u sample rate, %08X channel mask\n", + masterChannels, masterRate, masterChannelMask); + + if (mMasterVolume != 1.f) + { + hr = mMasterVoice->SetVolume(mMasterVolume); + if (FAILED(hr)) + { + SAFE_DESTROY_VOICE(mMasterVoice); + xaudio2.Reset(); + return hr; + } + } + + mOutputFormat.wFormatTag = WAVE_FORMAT_PCM; + mOutputFormat.nChannels = static_cast(details.InputChannels); + mOutputFormat.nSamplesPerSec = details.InputSampleRate; + mOutputFormat.wBitsPerSample = 16; + GetDeviceOutputFormat(deviceId, mOutputFormat); + + // + // Setup mastering volume limiter (optional) + // + if (mEngineFlags & AudioEngine_UseMasteringLimiter) + { + FXMASTERINGLIMITER_PARAMETERS params = {}; + params.Release = FXMASTERINGLIMITER_DEFAULT_RELEASE; + params.Loudness = FXMASTERINGLIMITER_DEFAULT_LOUDNESS; + + hr = CreateFX(__uuidof(FXMasteringLimiter), mVolumeLimiter.ReleaseAndGetAddressOf(), ¶ms, sizeof(params)); + if (FAILED(hr)) + { + SAFE_DESTROY_VOICE(mMasterVoice); + xaudio2.Reset(); + return hr; + } + + XAUDIO2_EFFECT_DESCRIPTOR desc = {}; + desc.InitialState = TRUE; + desc.OutputChannels = masterChannels; + desc.pEffect = mVolumeLimiter.Get(); + + XAUDIO2_EFFECT_CHAIN chain = { 1, &desc }; + hr = mMasterVoice->SetEffectChain(&chain); + if (FAILED(hr)) + { + SAFE_DESTROY_VOICE(mMasterVoice); + mVolumeLimiter.Reset(); + xaudio2.Reset(); + return hr; + } + + DebugTrace("INFO: Mastering volume limiter enabled\n"); + } + + // + // Setup environmental reverb for 3D audio (optional) + // + if (mEngineFlags & AudioEngine_EnvironmentalReverb) + { + hr = XAudio2CreateReverb(mReverbEffect.ReleaseAndGetAddressOf(), 0u); + if (FAILED(hr)) + { + SAFE_DESTROY_VOICE(mMasterVoice); + mVolumeLimiter.Reset(); + xaudio2.Reset(); + return hr; + } + + XAUDIO2_EFFECT_DESCRIPTOR effects[] = { { mReverbEffect.Get(), TRUE, 1 } }; + XAUDIO2_EFFECT_CHAIN effectChain = { 1, effects }; + + mReverbEnabled = true; + + hr = xaudio2->CreateSubmixVoice(&mReverbVoice, 1, masterRate, + (mEngineFlags & AudioEngine_ReverbUseFilters) ? XAUDIO2_VOICE_USEFILTER : 0u, 0u, + nullptr, &effectChain); + if (FAILED(hr)) + { + SAFE_DESTROY_VOICE(mMasterVoice); + mReverbEffect.Reset(); + mVolumeLimiter.Reset(); + xaudio2.Reset(); + return hr; + } + + XAUDIO2FX_REVERB_PARAMETERS native; + ReverbConvertI3DL2ToNative(&gReverbPresets[Reverb_Default], &native); + hr = mReverbVoice->SetEffectParameters(0, &native, sizeof(XAUDIO2FX_REVERB_PARAMETERS)); + if (FAILED(hr)) + { + SAFE_DESTROY_VOICE(mReverbVoice); + SAFE_DESTROY_VOICE(mMasterVoice); + mReverbEffect.Reset(); + mVolumeLimiter.Reset(); + xaudio2.Reset(); + return hr; + } + + DebugTrace("INFO: I3DL2 reverb effect enabled for 3D positional audio\n"); + } + + // + // Setup 3D audio + // + constexpr float SPEEDOFSOUND = X3DAUDIO_SPEED_OF_SOUND; + + hr = X3DAudioInitialize(masterChannelMask, SPEEDOFSOUND, mX3DAudio); + if (FAILED(hr)) + { + SAFE_DESTROY_VOICE(mReverbVoice); + SAFE_DESTROY_VOICE(mMasterVoice); + mReverbEffect.Reset(); + mVolumeLimiter.Reset(); + xaudio2.Reset(); + return hr; + } + + // + // Inform any notify objects we are ready to go again + // + for (auto it : mNotifyObjects) + { + assert(it != nullptr); + it->OnReset(); + } + + return S_OK; +} + + +void AudioEngine::Impl::SetSilentMode() +{ + for (auto it : mNotifyObjects) + { + assert(it != nullptr); + it->OnCriticalError(); + } + + for (auto& it : mOneShots) + { + assert(it.second != nullptr); + it.second->DestroyVoice(); + } + mOneShots.clear(); + + for (auto& it : mVoicePool) + { + assert(it.second != nullptr); + it.second->DestroyVoice(); + } + mVoicePool.clear(); + + mVoiceInstances = 0; + + SAFE_DESTROY_VOICE(mReverbVoice); + SAFE_DESTROY_VOICE(mMasterVoice); + + mReverbEffect.Reset(); + mVolumeLimiter.Reset(); + xaudio2.Reset(); +} + + +void AudioEngine::Impl::Shutdown() noexcept +{ + for (auto it : mNotifyObjects) + { + assert(it != nullptr); + it->OnDestroyEngine(); + } + + if (xaudio2) + { + xaudio2->UnregisterForCallbacks(&mEngineCallback); + + xaudio2->StopEngine(); + + for (auto& it : mOneShots) + { + assert(it.second != nullptr); + it.second->DestroyVoice(); + } + mOneShots.clear(); + + for (auto& it : mVoicePool) + { + assert(it.second != nullptr); + it.second->DestroyVoice(); + } + mVoicePool.clear(); + + mVoiceInstances = 0; + + SAFE_DESTROY_VOICE(mReverbVoice); + SAFE_DESTROY_VOICE(mMasterVoice); + + mReverbEffect.Reset(); + mVolumeLimiter.Reset(); + xaudio2.Reset(); + + masterChannelMask = masterChannels = masterRate = 0; + mOutputFormat = {}; + + mCriticalError = false; + mReverbEnabled = false; + + memset(&mX3DAudio, 0, X3DAUDIO_HANDLE_BYTESIZE); + } +} + + +bool AudioEngine::Impl::Update() +{ + if (!xaudio2) + return false; + + HANDLE events[2] = { mEngineCallback.mCriticalError.get(), mVoiceCallback.mBufferEnd.get() }; + switch (WaitForMultipleObjectsEx(static_cast(std::size(events)), events, FALSE, 0, FALSE)) + { + default: + case WAIT_TIMEOUT: + break; + + case WAIT_OBJECT_0: // OnCritialError + mCriticalError = true; + + SetSilentMode(); + return false; + + case WAIT_OBJECT_0 + 1: // OnBufferEnd + // Scan for completed one-shot voices + for (auto it = mOneShots.begin(); it != mOneShots.end(); ) + { + assert(it->second != nullptr); + + XAUDIO2_VOICE_STATE xstate; + it->second->GetState(&xstate, XAUDIO2_VOICE_NOSAMPLESPLAYED); + + if (!xstate.BuffersQueued) + { + std::ignore = it->second->Stop(0); + if (it->first) + { + // Put voice back into voice pool for reuse since it has a non-zero voiceKey + #ifdef VERBOSE_TRACE + DebugTrace("INFO: One-shot voice being saved for reuse (%08X)\n", it->first); + #endif + voicepool_t::value_type v(it->first, it->second); + mVoicePool.emplace(v); + } + else + { + // Voice is to be destroyed rather than reused + #ifdef VERBOSE_TRACE + DebugTrace("INFO: Destroying one-shot voice\n"); + #endif + it->second->DestroyVoice(); + } + it = mOneShots.erase(it); + } + else + ++it; + } + break; + + case WAIT_FAILED: + throw std::system_error(std::error_code(static_cast(GetLastError()), std::system_category()), "WaitForMultipleObjectsEx"); + } + + // + // Inform any notify objects of updates + // + for (auto it : mNotifyUpdates) + { + assert(it != nullptr); + it->OnUpdate(); + } + + return true; +} + + +_Use_decl_annotations_ +void AudioEngine::Impl::SetReverb(const XAUDIO2FX_REVERB_PARAMETERS* native) noexcept +{ + if (!mReverbVoice) + return; + + if (native) + { + if (!mReverbEnabled) + { + mReverbEnabled = true; + std::ignore = mReverbVoice->EnableEffect(0); + } + + std::ignore = mReverbVoice->SetEffectParameters(0, native, sizeof(XAUDIO2FX_REVERB_PARAMETERS)); + } + else if (mReverbEnabled) + { + mReverbEnabled = false; + std::ignore = mReverbVoice->DisableEffect(0); + } +} + + +void AudioEngine::Impl::SetMasteringLimit(int release, int loudness) +{ + if (!mVolumeLimiter || !mMasterVoice) + return; + + if ((release < FXMASTERINGLIMITER_MIN_RELEASE) || (release > FXMASTERINGLIMITER_MAX_RELEASE)) + throw std::out_of_range("AudioEngine::SetMasteringLimit"); + + if ((loudness < FXMASTERINGLIMITER_MIN_LOUDNESS) || (loudness > FXMASTERINGLIMITER_MAX_LOUDNESS)) + throw std::out_of_range("AudioEngine::SetMasteringLimit"); + + FXMASTERINGLIMITER_PARAMETERS params = {}; + params.Release = static_cast(release); + params.Loudness = static_cast(loudness); + + HRESULT hr = mMasterVoice->SetEffectParameters(0, ¶ms, sizeof(params)); + ThrowIfFailed(hr); +} + + +AudioStatistics AudioEngine::Impl::GetStatistics() const +{ + AudioStatistics stats = {}; + + stats.allocatedVoices = stats.allocatedVoicesOneShot = mOneShots.size() + mVoicePool.size(); + stats.allocatedVoicesIdle = mVoicePool.size(); + + for (const auto it : mNotifyObjects) + { + assert(it != nullptr); + it->GatherStatistics(stats); + } + + assert(stats.allocatedVoices == (mOneShots.size() + mVoicePool.size() + mVoiceInstances)); + + return stats; +} + + +void AudioEngine::Impl::TrimVoicePool() +{ + for (auto it : mNotifyObjects) + { + assert(it != nullptr); + it->OnTrim(); + } + + for (auto& it : mVoicePool) + { + assert(it.second != nullptr); + it.second->DestroyVoice(); + } + mVoicePool.clear(); +} + + +_Use_decl_annotations_ +void AudioEngine::Impl::AllocateVoice( + const WAVEFORMATEX* wfx, + SOUND_EFFECT_INSTANCE_FLAGS flags, + bool oneshot, + IXAudio2SourceVoice** voice) +{ + if (!wfx) + throw std::invalid_argument("Wave format is required\n"); + + // No need to call IsValid on wfx because CreateSourceVoice will do that + + if (!voice) + throw std::invalid_argument("Voice pointer must be non-null"); + + *voice = nullptr; + + if (!xaudio2 || mCriticalError) + return; + +#ifndef NDEBUG + const float maxFrequencyRatio = XAudio2SemitonesToFrequencyRatio(12); + assert(maxFrequencyRatio <= XAUDIO2_DEFAULT_FREQ_RATIO); +#endif + + unsigned int voiceKey = 0; + if (oneshot) + { + if (flags & (SoundEffectInstance_Use3D | SoundEffectInstance_ReverbUseFilters | SoundEffectInstance_NoSetPitch)) + { + DebugTrace((flags & SoundEffectInstance_NoSetPitch) + ? "ERROR: One-shot voices must support pitch-shifting for voice reuse\n" + : "ERROR: One-use voices cannot use 3D positional audio\n"); + throw std::invalid_argument("Invalid flags for one-shot voice"); + } + + #ifdef VERBOSE_TRACE + if (wfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE) + { + DebugTrace("INFO: Requesting one-shot: Format Tag EXTENSIBLE %u, %u channels, %u-bit, %u blkalign, %u Hz\n", + GetFormatTag(wfx), wfx->nChannels, wfx->wBitsPerSample, wfx->nBlockAlign, wfx->nSamplesPerSec); + } + else + { + DebugTrace("INFO: Requesting one-shot: Format Tag %u, %u channels, %u-bit, %u blkalign, %u Hz\n", + wfx->wFormatTag, wfx->nChannels, wfx->wBitsPerSample, wfx->nBlockAlign, wfx->nSamplesPerSec); + } + #endif + + if (!(mEngineFlags & AudioEngine_DisableVoiceReuse)) + { + voiceKey = makeVoiceKey(wfx); + if (voiceKey != 0) + { + auto it = mVoicePool.find(voiceKey); + if (it != mVoicePool.end()) + { + // Found a matching (stopped) voice to reuse + assert(it->second != nullptr); + *voice = it->second; + mVoicePool.erase(it); + + // Reset any volume/pitch-shifting + HRESULT hr = (*voice)->SetVolume(1.f); + ThrowIfFailed(hr); + + hr = (*voice)->SetFrequencyRatio(1.f); + ThrowIfFailed(hr); + + if (wfx->nChannels == 1 || wfx->nChannels == 2) + { + // Reset any panning + float matrix[16] = {}; + ComputePan(0.f, wfx->nChannels, matrix); + + hr = (*voice)->SetOutputMatrix(nullptr, wfx->nChannels, masterChannels, matrix); + ThrowIfFailed(hr); + } + } + else if ((mVoicePool.size() + mOneShots.size() + 1) >= maxVoiceOneshots) + { + DebugTrace("WARNING: Too many one-shot voices in use (%zu + %zu >= %zu); one-shot not played\n", + mVoicePool.size(), mOneShots.size() + 1, maxVoiceOneshots); + return; + } + else + { + // makeVoiceKey already constrained the supported wfx formats to those supported for reuse + + char buff[64] = {}; + auto wfmt = reinterpret_cast(buff); + + const uint32_t tag = GetFormatTag(wfx); + switch (tag) + { + case WAVE_FORMAT_PCM: + CreateIntegerPCM(wfmt, defaultRate, wfx->nChannels, wfx->wBitsPerSample); + break; + + case WAVE_FORMAT_IEEE_FLOAT: + CreateFloatPCM(wfmt, defaultRate, wfx->nChannels); + break; + + case WAVE_FORMAT_ADPCM: + { + auto wfadpcm = reinterpret_cast(wfx); + CreateADPCM(wfmt, sizeof(buff), defaultRate, wfx->nChannels, wfadpcm->wSamplesPerBlock); + } + break; + + #ifdef DIRECTX_ENABLE_XMA2 + case WAVE_FORMAT_XMA2: + CreateXMA2(wfmt, sizeof(buff), defaultRate, wfx->nChannels, 65536, 2, 0); + break; + #endif + } + + #ifdef VERBOSE_TRACE + DebugTrace("INFO: Allocate reuse voice: Format Tag %u, %u channels, %u-bit, %u blkalign, %u Hz\n", + wfmt->wFormatTag, wfmt->nChannels, wfmt->wBitsPerSample, wfmt->nBlockAlign, wfmt->nSamplesPerSec); + #endif + + assert(voiceKey == makeVoiceKey(wfmt)); + + HRESULT hr = xaudio2->CreateSourceVoice(voice, wfmt, 0, XAUDIO2_DEFAULT_FREQ_RATIO, &mVoiceCallback, nullptr, nullptr); + if (FAILED(hr)) + { + DebugTrace("ERROR: CreateSourceVoice (reuse) failed with error %08X\n", static_cast(hr)); + throw std::runtime_error("CreateSourceVoice"); + } + } + + assert(*voice != nullptr); + HRESULT hr = (*voice)->SetSourceSampleRate(wfx->nSamplesPerSec); + if (FAILED(hr)) + { + DebugTrace("ERROR: SetSourceSampleRate failed with error %08X\n", static_cast(hr)); + throw std::runtime_error("SetSourceSampleRate"); + } + } + } + } + + if (!*voice) + { + if (oneshot) + { + if ((mVoicePool.size() + mOneShots.size() + 1) >= maxVoiceOneshots) + { + DebugTrace("WARNING: Too many one-shot voices in use (%zu + %zu >= %zu); one-shot not played; see TrimVoicePool\n", + mVoicePool.size(), mOneShots.size() + 1, maxVoiceOneshots); + return; + } + } + else if ((mVoiceInstances + 1) >= maxVoiceInstances) + { + DebugTrace("ERROR: Too many instance voices (%zu >= %zu); see TrimVoicePool\n", + mVoiceInstances + 1, maxVoiceInstances); + throw std::runtime_error("Too many instance voices"); + } + + const UINT32 vflags = (flags & SoundEffectInstance_NoSetPitch) ? XAUDIO2_VOICE_NOPITCH : 0u; + + HRESULT hr; + if (flags & SoundEffectInstance_Use3D) + { + XAUDIO2_SEND_DESCRIPTOR sendDescriptors[2] = {}; + sendDescriptors[0].Flags = sendDescriptors[1].Flags = (flags & SoundEffectInstance_ReverbUseFilters) + ? XAUDIO2_SEND_USEFILTER : 0u; + sendDescriptors[0].pOutputVoice = mMasterVoice; + sendDescriptors[1].pOutputVoice = mReverbVoice; + const XAUDIO2_VOICE_SENDS sendList = { mReverbVoice ? 2U : 1U, sendDescriptors }; + + #ifdef VERBOSE_TRACE + DebugTrace("INFO: Allocate voice 3D: Format Tag %u, %u channels, %u-bit, %u blkalign, %u Hz\n", + wfx->wFormatTag, wfx->nChannels, wfx->wBitsPerSample, wfx->nBlockAlign, wfx->nSamplesPerSec); + #endif + + hr = xaudio2->CreateSourceVoice(voice, wfx, vflags, XAUDIO2_DEFAULT_FREQ_RATIO, &mVoiceCallback, &sendList, nullptr); + } + else + { + #ifdef VERBOSE_TRACE + DebugTrace("INFO: Allocate voice: Format Tag %u, %u channels, %u-bit, %u blkalign, %u Hz\n", + wfx->wFormatTag, wfx->nChannels, wfx->wBitsPerSample, wfx->nBlockAlign, wfx->nSamplesPerSec); + #endif + + hr = xaudio2->CreateSourceVoice(voice, wfx, vflags, XAUDIO2_DEFAULT_FREQ_RATIO, &mVoiceCallback, nullptr, nullptr); + } + + if (FAILED(hr)) + { + DebugTrace("ERROR: CreateSourceVoice failed with error %08X\n", static_cast(hr)); + throw std::runtime_error("CreateSourceVoice"); + } + else if (!oneshot) + { + ++mVoiceInstances; + } + } + + if (oneshot) + { + assert(*voice != nullptr); + mOneShots.emplace_back(std::make_pair(voiceKey, *voice)); + } +} + + +void AudioEngine::Impl::DestroyVoice(_In_ IXAudio2SourceVoice* voice) noexcept +{ + if (!voice) + return; + +#ifndef NDEBUG + for (const auto& it : mOneShots) + { + if (it.second == voice) + { + DebugTrace("ERROR: DestroyVoice should not be called for a one-shot voice\n"); + return; + } + } + + for (const auto& it : mVoicePool) + { + if (it.second == voice) + { + DebugTrace("ERROR: DestroyVoice should not be called for a one-shot voice; see TrimVoicePool\n"); + return; + } + } +#endif + + assert(mVoiceInstances > 0); + --mVoiceInstances; + voice->DestroyVoice(); +} + + +void AudioEngine::Impl::RegisterNotify(_In_ IVoiceNotify* notify, bool usesUpdate) +{ + assert(notify != nullptr); + mNotifyObjects.insert(notify); + + if (usesUpdate) + { + mNotifyUpdates.insert(notify); + } +} + + +void AudioEngine::Impl::UnregisterNotify(_In_ IVoiceNotify* notify, bool usesOneShots, bool usesUpdate) +{ + assert(notify != nullptr); + mNotifyObjects.erase(notify); + + // Check for any pending one-shots for this notification object + if (usesOneShots) + { + bool setevent = false; + + for (auto& it : mOneShots) + { + assert(it.second != nullptr); + + XAUDIO2_VOICE_STATE state; + it.second->GetState(&state, XAUDIO2_VOICE_NOSAMPLESPLAYED); + + if (state.pCurrentBufferContext == notify) + { + std::ignore = it.second->Stop(0); + std::ignore = it.second->FlushSourceBuffers(); + setevent = true; + } + } + + if (setevent) + { + // Trigger scan on next call to Update... + SetEvent(mVoiceCallback.mBufferEnd.get()); + } + } + + if (usesUpdate) + { + mNotifyUpdates.erase(notify); + } +} + + +//-------------------------------------------------------------------------------------- +// AudioEngine +//-------------------------------------------------------------------------------------- + +// Public constructor. +_Use_decl_annotations_ +AudioEngine::AudioEngine( + AUDIO_ENGINE_FLAGS flags, + const WAVEFORMATEX* wfx, + const wchar_t* deviceId, + AUDIO_STREAM_CATEGORY category) noexcept(false) + : pImpl(std::make_unique()) +{ + HRESULT hr = pImpl->Initialize(flags, wfx, deviceId, category); + if (FAILED(hr)) + { + const wchar_t* deviceName = (deviceId) ? deviceId : L"default"; + if (hr == HRESULT_FROM_WIN32(ERROR_NOT_FOUND)) + { + if (flags & AudioEngine_ThrowOnNoAudioHW) + { + DebugTrace("ERROR: AudioEngine found no default audio device\n"); + throw std::runtime_error("AudioEngineNoAudioHW"); + } + else + { + DebugTrace("WARNING: AudioEngine found no default audio device; running in 'silent mode'\n"); + } + } + else if (hr == AUDCLNT_E_DEVICE_IN_USE) + { + if (flags & AudioEngine_ThrowOnNoAudioHW) + { + DebugTrace("ERROR: AudioEngine audio device [%ls] was already in use\n", deviceName); + throw std::runtime_error("AudioEngineNoAudioHW"); + } + else + { + DebugTrace("WARNING: AudioEngine audio device [%ls] already in use; running in 'silent mode'\n", deviceName); + } + } + else + { + DebugTrace("ERROR: AudioEngine failed (%08X) to initialize using device [%ls]\n", + static_cast(hr), deviceName); + throw std::runtime_error("AudioEngine"); + } + } +} + + +// Move ctor/operator. +AudioEngine::AudioEngine(AudioEngine&&) noexcept = default; +AudioEngine& AudioEngine::operator= (AudioEngine&&) noexcept = default; + + +// Public destructor. +AudioEngine::~AudioEngine() +{ + if (pImpl) + { + pImpl->Shutdown(); + } +} + + +// Public methods. +bool AudioEngine::Update() +{ + return pImpl->Update(); +} + + +_Use_decl_annotations_ +bool AudioEngine::Reset(const WAVEFORMATEX* wfx, const wchar_t* deviceId) +{ + if (pImpl->xaudio2) + { + DebugTrace("WARNING: Called Reset for active audio graph; going silent in preparation for migration\n"); + pImpl->SetSilentMode(); + } + + HRESULT hr = pImpl->Reset(wfx, deviceId); + if (FAILED(hr)) + { + const wchar_t* deviceName = (deviceId) ? deviceId : L"default"; + if (hr == HRESULT_FROM_WIN32(ERROR_NOT_FOUND)) + { + if (pImpl->mEngineFlags & AudioEngine_ThrowOnNoAudioHW) + { + DebugTrace("ERROR: AudioEngine found no default audio device on Reset\n"); + throw std::runtime_error("AudioEngineNoAudioHW"); + } + else + { + DebugTrace("WARNING: AudioEngine found no default audio device on Reset; running in 'silent mode'\n"); + return false; + } + } + else if (hr == AUDCLNT_E_DEVICE_IN_USE) + { + if (pImpl->mEngineFlags & AudioEngine_ThrowOnNoAudioHW) + { + DebugTrace("ERROR: AudioEngine failed to initialize using device [%ls] because it was already in use.\n", deviceName); + throw std::runtime_error("AudioEngineNoAudioHW"); + } + else + { + DebugTrace("WARNING: AudioEngine failed to initialize using device [%ls] because it was already in use.\n", deviceName); + return false; + } + } + else + { + DebugTrace("ERROR: AudioEngine failed (%08X) to Reset using device [%ls]\n", + static_cast(hr), deviceName); + throw std::runtime_error("AudioEngine::Reset"); + } + } + + DebugTrace("INFO: AudioEngine Reset using device [%ls]\n", (deviceId) ? deviceId : L"default"); + + return true; +} + + +void AudioEngine::Suspend() noexcept +{ + if (!pImpl->xaudio2) + return; + + pImpl->xaudio2->StopEngine(); +} + + +void AudioEngine::Resume() +{ + if (!pImpl->xaudio2) + return; + + HRESULT hr = pImpl->xaudio2->StartEngine(); + if (FAILED(hr)) + { + DebugTrace("WARNING: Resume of the audio engine failed; running in 'silent mode'\n"); + pImpl->SetSilentMode(); + } +} + + +float AudioEngine::GetMasterVolume() const noexcept +{ + return pImpl->mMasterVolume; +} + + +void AudioEngine::SetMasterVolume(float volume) +{ + assert(volume >= -XAUDIO2_MAX_VOLUME_LEVEL && volume <= XAUDIO2_MAX_VOLUME_LEVEL); + + pImpl->mMasterVolume = volume; + + if (pImpl->mMasterVoice) + { + HRESULT hr = pImpl->mMasterVoice->SetVolume(volume); + ThrowIfFailed(hr); + } +} + + +void AudioEngine::SetReverb(AUDIO_ENGINE_REVERB reverb) +{ + if (reverb >= Reverb_MAX) + throw std::invalid_argument("reverb parameter is invalid"); + + if (reverb == Reverb_Off) + { + pImpl->SetReverb(nullptr); + } + else + { + XAUDIO2FX_REVERB_PARAMETERS native; + ReverbConvertI3DL2ToNative(&gReverbPresets[reverb], &native); + pImpl->SetReverb(&native); + } +} + + +_Use_decl_annotations_ +void AudioEngine::SetReverb(const XAUDIO2FX_REVERB_PARAMETERS* native) +{ + pImpl->SetReverb(native); +} + + +void AudioEngine::SetMasteringLimit(int release, int loudness) +{ + pImpl->SetMasteringLimit(release, loudness); +} + + +// Public accessors. +AudioStatistics AudioEngine::GetStatistics() const +{ + return pImpl->GetStatistics(); +} + + +WAVEFORMATEXTENSIBLE AudioEngine::GetOutputFormat() const noexcept +{ + WAVEFORMATEXTENSIBLE wfx = {}; + + if (!pImpl->xaudio2) + return wfx; + + wfx.Format = pImpl->mOutputFormat; + wfx.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX); + wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + + wfx.Samples.wValidBitsPerSample = wfx.Format.wBitsPerSample; + wfx.dwChannelMask = pImpl->masterChannelMask; + + wfx.Format.nBlockAlign = static_cast(wfx.Format.nChannels * wfx.Format.wBitsPerSample / 8); + wfx.Format.nAvgBytesPerSec = wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign; + + static const GUID s_wfexBase = { 0x00000000, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 } }; + memcpy(&wfx.SubFormat, &s_wfexBase, sizeof(GUID)); + wfx.SubFormat.Data1 = wfx.Format.wFormatTag; + + return wfx; +} + + +uint32_t AudioEngine::GetChannelMask() const noexcept +{ + return pImpl->masterChannelMask; +} + + +int AudioEngine::GetOutputSampleRate() const noexcept +{ + return static_cast(pImpl->masterRate); +} + + +unsigned int AudioEngine::GetOutputChannels() const noexcept +{ + return pImpl->masterChannels; +} + + +bool AudioEngine::IsAudioDevicePresent() const noexcept +{ + return pImpl->xaudio2 && !pImpl->mCriticalError; +} + + +bool AudioEngine::IsCriticalError() const noexcept +{ + return pImpl->mCriticalError; +} + + +// Voice management. +void AudioEngine::SetDefaultSampleRate(int sampleRate) +{ + if ((sampleRate < XAUDIO2_MIN_SAMPLE_RATE) || (sampleRate > XAUDIO2_MAX_SAMPLE_RATE)) + throw std::out_of_range("Default sample rate is out of range"); + + pImpl->defaultRate = sampleRate; +} + + +void AudioEngine::SetMaxVoicePool(size_t maxOneShots, size_t maxInstances) +{ + if (maxOneShots > 0) + pImpl->maxVoiceOneshots = maxOneShots; + + if (maxInstances > 0) + pImpl->maxVoiceInstances = maxInstances; +} + + +void AudioEngine::TrimVoicePool() +{ + pImpl->TrimVoicePool(); +} + + +_Use_decl_annotations_ +void AudioEngine::AllocateVoice( + const WAVEFORMATEX* wfx, + SOUND_EFFECT_INSTANCE_FLAGS flags, + bool oneshot, + IXAudio2SourceVoice** voice) +{ + pImpl->AllocateVoice(wfx, flags, oneshot, voice); +} + + +void AudioEngine::DestroyVoice(_In_ IXAudio2SourceVoice* voice) noexcept +{ + pImpl->DestroyVoice(voice); +} + + +void AudioEngine::RegisterNotify(_In_ IVoiceNotify* notify, bool usesUpdate) +{ + pImpl->RegisterNotify(notify, usesUpdate); +} + + +void AudioEngine::UnregisterNotify(_In_ IVoiceNotify* notify, bool oneshots, bool usesUpdate) +{ + pImpl->UnregisterNotify(notify, oneshots, usesUpdate); +} + + +IXAudio2* AudioEngine::GetInterface() const noexcept +{ + return pImpl->xaudio2.Get(); +} + + +IXAudio2MasteringVoice* AudioEngine::GetMasterVoice() const noexcept +{ + return pImpl->mMasterVoice; +} + + +IXAudio2SubmixVoice* AudioEngine::GetReverbVoice() const noexcept +{ + return pImpl->mReverbVoice; +} + + +X3DAUDIO_HANDLE& AudioEngine::Get3DHandle() const noexcept +{ + return pImpl->mX3DAudio; +} + + +// Static methods. +#if (defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP)) || defined(USING_XAUDIO2_8) +//--- Use Windows Runtime device enumeration --- + +// Note that this form of enumeration would also be needed for XAudio2.9 prior to Windows 10 (18362). +// +// If you care about supporting Windows 10 (17763), Windows Server 2019, or earlier Windows 10 builds, +// you will need to modify the library to use this codepath for Windows desktop +// -or- use XAudio2Redist -or- use XAudio 2.8. + +#pragma comment(lib,"runtimeobject.lib") +#pragma warning(push) +#pragma warning(disable: 4471 5204 5256) +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wnonportable-system-include-path" +#endif +#include +#include +#pragma warning(pop) + +#include + +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wdeprecated-dynamic-exception-spec" +#endif + +namespace +{ + const wchar_t* c_PKEY_AudioEngine_DeviceFormat = L"{f19f064d-082c-4e27-bc73-6882a1bb8e4c} 0"; + + class PropertyIterator : public Microsoft::WRL::RuntimeClass> + { + #if !defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP) + InspectableClass(L"AudioEngine.PropertyIterator", FullTrust) + #else + InspectableClass(L"AudioEngine.PropertyIterator", BaseTrust) + #endif + + public: + PropertyIterator() : mFirst(true), mString(c_PKEY_AudioEngine_DeviceFormat) {} + + HRESULT STDMETHODCALLTYPE get_Current(HSTRING *current) override + { + if (!current) + return E_INVALIDARG; + + if (mFirst) + { + *current = mString.Get(); + } + + return S_OK; + } + + HRESULT STDMETHODCALLTYPE get_HasCurrent(boolean *hasCurrent) override + { + if (!hasCurrent) + return E_INVALIDARG; + + *hasCurrent = (mFirst) ? TRUE : FALSE; + return S_OK; + } + + HRESULT STDMETHODCALLTYPE MoveNext(boolean *hasCurrent) override + { + if (!hasCurrent) + return E_INVALIDARG; + + *hasCurrent = FALSE; + mFirst = false; + return S_OK; + } + + private: + bool mFirst; + Microsoft::WRL::Wrappers::HStringReference mString; + + ~PropertyIterator() override = default; + }; + + class PropertyList : public Microsoft::WRL::RuntimeClass> + { + #if !defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP) + InspectableClass(L"AudioEngine.PropertyList", FullTrust) + #else + InspectableClass(L"AudioEngine.PropertyList", BaseTrust) + #endif + + public: + HRESULT STDMETHODCALLTYPE First(ABI::Windows::Foundation::Collections::IIterator **first) override + { + if (!first) + return E_INVALIDARG; + + ComPtr p = Microsoft::WRL::Make(); + *first = p.Detach(); + return S_OK; + } + + private: + ~PropertyList() override = default; + }; + + void GetDeviceOutputFormat(const wchar_t* deviceId, WAVEFORMATEX& wfx) + { + using namespace Microsoft::WRL; + using namespace Microsoft::WRL::Wrappers; + using namespace ABI::Windows::Foundation; + using namespace ABI::Windows::Foundation::Collections; + using namespace ABI::Windows::Devices::Enumeration; + + #if !defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP) + RoInitializeWrapper initialize(RO_INIT_MULTITHREADED); + ThrowIfFailed(initialize); + #endif + + ComPtr diFactory; + HRESULT hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_Devices_Enumeration_DeviceInformation).Get(), &diFactory); + ThrowIfFailed(hr); + + HString id; + if (!deviceId) + { + using namespace ABI::Windows::Media::Devices; + + ComPtr mdStatics; + hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_Media_Devices_MediaDevice).Get(), &mdStatics); + ThrowIfFailed(hr); + + hr = mdStatics->GetDefaultAudioRenderId(AudioDeviceRole_Default, id.GetAddressOf()); + ThrowIfFailed(hr); + } + else + { + id.Set(deviceId); + } + + ComPtr> operation; + ComPtr> props = Make(); + + hr = diFactory->CreateFromIdAsyncAdditionalProperties(id.Get(), props.Get(), operation.GetAddressOf()); + if (FAILED(hr)) + return; + + ComPtr asyncinfo; + hr = operation.As(&asyncinfo); + ThrowIfFailed(hr); + + AsyncStatus status; + hr = asyncinfo->get_Status(&status); + ThrowIfFailed(hr); + + while (status == ABI::Windows::Foundation::AsyncStatus::Started) + { + Sleep(100); + hr = asyncinfo->get_Status(&status); + ThrowIfFailed(hr); + } + + if (status != ABI::Windows::Foundation::AsyncStatus::Completed) + { + throw std::runtime_error("CreateFromIdAsync"); + } + + ComPtr devInfo; + hr = operation->GetResults(devInfo.GetAddressOf()); + ThrowIfFailed(hr); + + ComPtr> map; + hr = devInfo->get_Properties(map.GetAddressOf()); + ThrowIfFailed(hr); + + ComPtr value; + hr = map->Lookup(HStringReference(c_PKEY_AudioEngine_DeviceFormat).Get(), value.GetAddressOf()); + if (SUCCEEDED(hr)) + { + ComPtr pvalue; + if (SUCCEEDED(value.As(&pvalue))) + { + PropertyType ptype; + ThrowIfFailed(pvalue->get_Type(&ptype)); + + if (ptype == PropertyType_UInt8Array) + { + UINT32 length = 0; + BYTE* ptr; + ThrowIfFailed(pvalue->GetUInt8Array(&length, &ptr)); + + if (length >= sizeof(WAVEFORMATEX)) + { + auto devicefx = reinterpret_cast(ptr); + memcpy(&wfx, devicefx, sizeof(WAVEFORMATEX)); + wfx.wFormatTag = static_cast(GetFormatTag(devicefx)); + } + } + } + } + } +} + +std::vector AudioEngine::GetRendererDetails() +{ + std::vector list; + + // Enumerating with WinRT using WRL (Win32 desktop app for Windows 8.x) + using namespace Microsoft::WRL::Wrappers; + using namespace ABI::Windows::Foundation; + using namespace ABI::Windows::Foundation::Collections; + using namespace ABI::Windows::Devices::Enumeration; + +#if !defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP) + RoInitializeWrapper initialize(RO_INIT_MULTITHREADED); + ThrowIfFailed(initialize); +#endif + + ComPtr diFactory; + HRESULT hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_Devices_Enumeration_DeviceInformation).Get(), &diFactory); + ThrowIfFailed(hr); + + ComPtr> operation; + hr = diFactory->FindAllAsyncDeviceClass(DeviceClass_AudioRender, operation.GetAddressOf()); + ThrowIfFailed(hr); + + ComPtr asyncinfo; + hr = operation.As(&asyncinfo); + ThrowIfFailed(hr); + + AsyncStatus status; + hr = asyncinfo->get_Status(&status); + ThrowIfFailed(hr); + + while (status == ABI::Windows::Foundation::AsyncStatus::Started) + { + Sleep(100); + hr = asyncinfo->get_Status(&status); + ThrowIfFailed(hr); + } + + if (status != ABI::Windows::Foundation::AsyncStatus::Completed) + { + throw std::runtime_error("FindAllAsyncDeviceClass"); + } + + ComPtr> devices; + hr = operation->GetResults(devices.GetAddressOf()); + ThrowIfFailed(hr); + + unsigned int count = 0; + hr = devices->get_Size(&count); + ThrowIfFailed(hr); + + if (!count) + return list; + + for (unsigned int j = 0; j < count; ++j) + { + ComPtr deviceInfo; + hr = devices->GetAt(j, deviceInfo.GetAddressOf()); + if (SUCCEEDED(hr)) + { + RendererDetail device; + + HString id; + if (SUCCEEDED(deviceInfo->get_Id(id.GetAddressOf()))) + { + device.deviceId = id.GetRawBuffer(nullptr); + } + + HString name; + if (SUCCEEDED(deviceInfo->get_Name(name.GetAddressOf()))) + { + device.description = name.GetRawBuffer(nullptr); + } + + list.emplace_back(device); + } + } + + return list; +} + + +#elif defined(_XBOX_ONE) +//--- Use legacy Xbox One XDK device enumeration --- + +#include +#include + +namespace +{ + void GetDeviceOutputFormat(const wchar_t*, WAVEFORMATEX& wfx) + { + wfx.nSamplesPerSec = 48000; + wfx.wBitsPerSample = 24; + } +} + +std::vector AudioEngine::GetRendererDetails() +{ + std::vector list; + + using namespace Microsoft::WRL; + using namespace Microsoft::WRL::Wrappers; + using namespace ABI::Windows::Foundation; + using namespace ABI::Windows::Media::Devices; + + ComPtr mdStatics; + HRESULT hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_Media_Devices_MediaDevice).Get(), &mdStatics); + ThrowIfFailed(hr); + + HString id; + hr = mdStatics->GetDefaultAudioRenderId(AudioDeviceRole_Default, id.GetAddressOf()); + ThrowIfFailed(hr); + + RendererDetail device; + device.deviceId = id.GetRawBuffer(nullptr); + device.description = L"Default"; + list.emplace_back(device); + + return list; +} + + +#elif defined(USING_XAUDIO2_9) || defined(USING_XAUDIO2_REDIST) || defined(_GAMING_DESKTOP) +#include +//--- Use WASAPI device enumeration --- + +namespace +{ + void GetDeviceOutputFormat(const wchar_t* deviceId, WAVEFORMATEX& wfx) + { + ComPtr devEnum; + if (FAILED(CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(devEnum.GetAddressOf())))) + return; + + ComPtr endpoint; + if (!deviceId) + { + if (FAILED(devEnum->GetDefaultAudioEndpoint(eRender, eConsole, endpoint.GetAddressOf()))) + return; + } + else + { + if (FAILED(devEnum->GetDevice(deviceId, endpoint.GetAddressOf()))) + return; + } + + // Value matches Windows SDK header um\mmdeviceapi.h + constexpr static PROPERTYKEY s_PKEY_AudioEngine_DeviceFormat = { { 0xf19f064d, 0x82c, 0x4e27, { 0xbc, 0x73, 0x68, 0x82, 0xa1, 0xbb, 0x8e, 0x4c } }, 0 }; + + ComPtr props; + if (SUCCEEDED(endpoint->OpenPropertyStore(STGM_READ, props.GetAddressOf()))) + { + PROPVARIANT var; + PropVariantInit(&var); + + if (SUCCEEDED(props->GetValue(s_PKEY_AudioEngine_DeviceFormat, &var))) + { + if (var.vt == VT_BLOB && var.blob.cbSize >= sizeof(WAVEFORMATEX)) + { + auto devicefx = reinterpret_cast(var.blob.pBlobData); + memcpy(&wfx, devicefx, sizeof(WAVEFORMATEX)); + wfx.wFormatTag = static_cast(GetFormatTag(devicefx)); + } + PropVariantClear(&var); + } + } + } +} + +std::vector AudioEngine::GetRendererDetails() +{ + std::vector list; + + // Value matches Windows SDK header shared\devpkey.h + constexpr static PROPERTYKEY s_PKEY_Device_FriendlyName = { { 0xa45c254e, 0xdf1c, 0x4efd, { 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0 } }, 14 }; + + ComPtr devEnum; + HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(devEnum.GetAddressOf())); + ThrowIfFailed(hr); + + ComPtr devices; + hr = devEnum->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &devices); + ThrowIfFailed(hr); + + UINT count = 0; + ThrowIfFailed(devices->GetCount(&count)); + + if (!count) + return list; + + for (UINT j = 0; j < count; ++j) + { + ComPtr endpoint; + hr = devices->Item(j, endpoint.GetAddressOf()); + ThrowIfFailed(hr); + + LPWSTR id = nullptr; + ThrowIfFailed(endpoint->GetId(&id)); + + RendererDetail device; + device.deviceId = id; + CoTaskMemFree(id); + + ComPtr props; + if (SUCCEEDED(endpoint->OpenPropertyStore(STGM_READ, props.GetAddressOf()))) + { + PROPVARIANT var; + PropVariantInit(&var); + + if (SUCCEEDED(props->GetValue(s_PKEY_Device_FriendlyName, &var))) + { + if (var.vt == VT_LPWSTR) + { + device.description = var.pwszVal; + } + PropVariantClear(&var); + } + } + + list.emplace_back(device); + } + + return list; +} + + +#else +#error DirectX Tool Kit for Audio not supported on this platform +#endif + + +//-------------------------------------------------------------------------------------- +// Adapters for /Zc:wchar_t- clients +#if defined(_MSC_VER) && !defined(_NATIVE_WCHAR_T_DEFINED) + +_Use_decl_annotations_ +AudioEngine::AudioEngine( + AUDIO_ENGINE_FLAGS flags, + const WAVEFORMATEX* wfx, + const __wchar_t* deviceId, + AUDIO_STREAM_CATEGORY category) noexcept(false) : + AudioEngine(flags, wfx, reinterpret_cast(deviceId), category) +{ +} + +_Use_decl_annotations_ +bool AudioEngine::Reset(const WAVEFORMATEX* wfx, const __wchar_t* deviceId) +{ + return Reset(wfx, reinterpret_cast(deviceId)); +} + +#endif // !_NATIVE_WCHAR_T_DEFINED diff --git a/Common/DirectXTK12/Audio/DynamicSoundEffectInstance.cpp b/Common/DirectXTK12/Audio/DynamicSoundEffectInstance.cpp new file mode 100644 index 0000000..473e950 --- /dev/null +++ b/Common/DirectXTK12/Audio/DynamicSoundEffectInstance.cpp @@ -0,0 +1,377 @@ +//-------------------------------------------------------------------------------------- +// File: DynamicSoundEffectInstance.cpp +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248929 +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "pch.h" +#include "SoundCommon.h" + +using namespace DirectX; + + +//====================================================================================== +// DynamicSoundEffectInstance +//====================================================================================== + +// Internal object implementation class. +class DynamicSoundEffectInstance::Impl : public IVoiceNotify +{ +public: + Impl(_In_ AudioEngine* engine, + _In_ DynamicSoundEffectInstance* object, + std::function& bufferNeeded, + int sampleRate, int channels, int sampleBits, + SOUND_EFFECT_INSTANCE_FLAGS flags) : + mBase(), + mBufferNeeded(nullptr), + mObject(object) + { + if ((sampleRate < XAUDIO2_MIN_SAMPLE_RATE) + || (sampleRate > XAUDIO2_MAX_SAMPLE_RATE)) + { + DebugTrace("DynamicSoundEffectInstance sampleRate must be in range %u...%u\n", XAUDIO2_MIN_SAMPLE_RATE, XAUDIO2_MAX_SAMPLE_RATE); + throw std::out_of_range("DynamicSoundEffectInstance"); + } + + if (!channels || (channels > 8)) + { + DebugTrace("DynamicSoundEffectInstance channels must be in range 1...8\n"); + throw std::out_of_range("DynamicSoundEffectInstance channel count out of range"); + } + + switch (sampleBits) + { + case 8: + case 16: + break; + + default: + DebugTrace("DynamicSoundEffectInstance sampleBits must be 8-bit or 16-bit\n"); + throw std::invalid_argument("DynamicSoundEffectInstance supports 8 or 16 bit"); + } + + mBufferEvent.reset(CreateEventEx(nullptr, nullptr, 0, EVENT_MODIFY_STATE | SYNCHRONIZE)); + if (!mBufferEvent) + { + throw std::system_error(std::error_code(static_cast(GetLastError()), std::system_category()), "CreateEventEx"); + } + + CreateIntegerPCM(&mWaveFormat, sampleRate, channels, sampleBits); + + assert(engine != nullptr); + engine->RegisterNotify(this, true); + + mBase.Initialize(engine, &mWaveFormat, flags); + + mBufferNeeded = bufferNeeded; + } + + Impl(Impl&&) = default; + Impl& operator= (Impl&&) = default; + + Impl(Impl const&) = delete; + Impl& operator= (Impl const&) = delete; + + ~Impl() override + { + mBase.DestroyVoice(); + + if (mBase.engine) + { + mBase.engine->UnregisterNotify(this, false, true); + mBase.engine = nullptr; + } + } + + void Play(); + + void Resume(); + + void SubmitBuffer(_In_reads_bytes_(audioBytes) const uint8_t* pAudioData, uint32_t offset, size_t audioBytes); + + const WAVEFORMATEX* GetFormat() const noexcept { return &mWaveFormat; } + + // IVoiceNotify + void __cdecl OnBufferEnd() override + { + SetEvent(mBufferEvent.get()); + } + + void __cdecl OnCriticalError() override + { + mBase.OnCriticalError(); + } + + void __cdecl OnReset() override + { + mBase.OnReset(); + } + + void __cdecl OnUpdate() override; + + void __cdecl OnDestroyEngine() noexcept override + { + mBase.OnDestroy(); + } + + void __cdecl OnTrim() override + { + mBase.OnTrim(); + } + + void __cdecl GatherStatistics(AudioStatistics& stats) const noexcept override + { + mBase.GatherStatistics(stats); + } + + void __cdecl OnDestroyParent() noexcept override + { + } + + SoundEffectInstanceBase mBase; + +private: + ScopedHandle mBufferEvent; + std::function mBufferNeeded; + DynamicSoundEffectInstance* mObject; + WAVEFORMATEX mWaveFormat; +}; + + +void DynamicSoundEffectInstance::Impl::Play() +{ + if (!mBase.voice) + { + mBase.AllocateVoice(&mWaveFormat); + } + + std::ignore = mBase.Play(); + + if (mBase.voice && (mBase.state == PLAYING) && (mBase.GetPendingBufferCount() <= 2)) + { + SetEvent(mBufferEvent.get()); + } +} + + +void DynamicSoundEffectInstance::Impl::Resume() +{ + if (mBase.voice && (mBase.state == PAUSED)) + { + mBase.Resume(); + + if ((mBase.state == PLAYING) && (mBase.GetPendingBufferCount() <= 2)) + { + SetEvent(mBufferEvent.get()); + } + } +} + + +_Use_decl_annotations_ +void DynamicSoundEffectInstance::Impl::SubmitBuffer(const uint8_t* pAudioData, uint32_t offset, size_t audioBytes) +{ + if (!pAudioData || !audioBytes) + throw std::invalid_argument("Invalid audio data buffer"); + + if (audioBytes > UINT32_MAX) + throw std::out_of_range("SubmitBuffer"); + + XAUDIO2_BUFFER buffer = {}; + buffer.AudioBytes = static_cast(audioBytes); + buffer.pAudioData = pAudioData; + + if (offset) + { + assert(mWaveFormat.wFormatTag == WAVE_FORMAT_PCM); + buffer.PlayBegin = offset / mWaveFormat.nBlockAlign; + buffer.PlayLength = static_cast((audioBytes - offset) / mWaveFormat.nBlockAlign); + } + + buffer.pContext = this; + + HRESULT hr = mBase.voice->SubmitSourceBuffer(&buffer, nullptr); + if (FAILED(hr)) + { + #ifdef _DEBUG + DebugTrace("ERROR: DynamicSoundEffectInstance failed (%08X) when submitting buffer:\n", static_cast(hr)); + + DebugTrace("\tFormat Tag %u, %u channels, %u-bit, %u Hz, %zu bytes [%u offset)\n", + mWaveFormat.wFormatTag, mWaveFormat.nChannels, mWaveFormat.wBitsPerSample, mWaveFormat.nSamplesPerSec, audioBytes, offset); + #endif + throw std::runtime_error("SubmitSourceBuffer"); + } +} + + +void DynamicSoundEffectInstance::Impl::OnUpdate() +{ + const DWORD result = WaitForSingleObjectEx(mBufferEvent.get(), 0, FALSE); + switch (result) + { + case WAIT_TIMEOUT: + break; + + case WAIT_OBJECT_0: + if (mBufferNeeded) + { + // This callback happens on the same thread that called AudioEngine::Update() + mBufferNeeded(mObject); + } + break; + + case WAIT_FAILED: + throw std::system_error(std::error_code(static_cast(GetLastError()), std::system_category()), "WaitForSingleObjectEx"); + } +} + + + +//-------------------------------------------------------------------------------------- +// DynamicSoundEffectInstance +//-------------------------------------------------------------------------------------- + +#pragma warning( disable : 4355 ) + +// Public constructors +_Use_decl_annotations_ +DynamicSoundEffectInstance::DynamicSoundEffectInstance( + AudioEngine* engine, + std::function bufferNeeded, + int sampleRate, + int channels, + int sampleBits, + SOUND_EFFECT_INSTANCE_FLAGS flags) : + pImpl(std::make_unique(engine, this, bufferNeeded, sampleRate, channels, sampleBits, flags)) +{ +} + + +DynamicSoundEffectInstance::DynamicSoundEffectInstance(DynamicSoundEffectInstance&&) noexcept = default; +DynamicSoundEffectInstance& DynamicSoundEffectInstance::operator= (DynamicSoundEffectInstance&&) noexcept = default; +DynamicSoundEffectInstance::~DynamicSoundEffectInstance() = default; + + +// Public methods. +void DynamicSoundEffectInstance::Play() +{ + pImpl->Play(); +} + + +void DynamicSoundEffectInstance::Stop(bool immediate) noexcept +{ + bool looped = false; + pImpl->mBase.Stop(immediate, looped); +} + + +void DynamicSoundEffectInstance::Pause() noexcept +{ + pImpl->mBase.Pause(); +} + + +void DynamicSoundEffectInstance::Resume() +{ + pImpl->Resume(); +} + + +void DynamicSoundEffectInstance::SetVolume(float volume) +{ + pImpl->mBase.SetVolume(volume); +} + + +void DynamicSoundEffectInstance::SetPitch(float pitch) +{ + pImpl->mBase.SetPitch(pitch); +} + + +void DynamicSoundEffectInstance::SetPan(float pan) +{ + pImpl->mBase.SetPan(pan); +} + + +void DynamicSoundEffectInstance::Apply3D(const X3DAUDIO_LISTENER& listener, const X3DAUDIO_EMITTER& emitter, bool rhcoords) +{ + pImpl->mBase.Apply3D(listener, emitter, rhcoords); +} + + +_Use_decl_annotations_ +void DynamicSoundEffectInstance::SubmitBuffer(const uint8_t* pAudioData, size_t audioBytes) +{ + pImpl->SubmitBuffer(pAudioData, 0, audioBytes); +} + + +_Use_decl_annotations_ +void DynamicSoundEffectInstance::SubmitBuffer(const uint8_t* pAudioData, uint32_t offset, size_t audioBytes) +{ + pImpl->SubmitBuffer(pAudioData, offset, audioBytes); +} + + +// Public accessors. +SoundState DynamicSoundEffectInstance::GetState() noexcept +{ + return pImpl->mBase.GetState(false); +} + + +size_t DynamicSoundEffectInstance::GetSampleDuration(size_t bytes) const noexcept +{ + auto wfx = pImpl->GetFormat(); + if (!wfx || !wfx->wBitsPerSample || !wfx->nChannels) + return 0; + + return static_cast((uint64_t(bytes) * 8) + / (uint64_t(wfx->wBitsPerSample) * uint64_t(wfx->nChannels))); +} + + +size_t DynamicSoundEffectInstance::GetSampleDurationMS(size_t bytes) const noexcept +{ + auto wfx = pImpl->GetFormat(); + if (!wfx || !wfx->nAvgBytesPerSec) + return 0; + + return static_cast((uint64_t(bytes) * 1000) / wfx->nAvgBytesPerSec); +} + + +size_t DynamicSoundEffectInstance::GetSampleSizeInBytes(uint64_t duration) const noexcept +{ + auto wfx = pImpl->GetFormat(); + if (!wfx || !wfx->nSamplesPerSec) + return 0; + + return static_cast(((duration * wfx->nSamplesPerSec) / 1000) * wfx->nBlockAlign); +} + + +int DynamicSoundEffectInstance::GetPendingBufferCount() const noexcept +{ + return pImpl->mBase.GetPendingBufferCount(); +} + + +unsigned int DynamicSoundEffectInstance::GetChannelCount() const noexcept +{ + return pImpl->mBase.GetChannelCount(); +} + + +const WAVEFORMATEX* DynamicSoundEffectInstance::GetFormat() const noexcept +{ + return pImpl->GetFormat(); +} diff --git a/Common/DirectXTK12/Audio/SoundCommon.cpp b/Common/DirectXTK12/Audio/SoundCommon.cpp new file mode 100644 index 0000000..9b59a36 --- /dev/null +++ b/Common/DirectXTK12/Audio/SoundCommon.cpp @@ -0,0 +1,982 @@ +//-------------------------------------------------------------------------------------- +// File: SoundCommon.cpp +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248929 +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "pch.h" +#include "SoundCommon.h" + +using namespace DirectX; + + +namespace +{ + template WORD ChannelsSpecifiedInMask(T x) noexcept + { + WORD bitCount = 0; + while (x) { ++bitCount; x &= (x - 1); } + return bitCount; + } + + constexpr int MSADPCM_HEADER_LENGTH = 7; + + constexpr uint16_t MSADPCM_FORMAT_EXTRA_BYTES = 32; + + constexpr uint16_t MSADPCM_BITS_PER_SAMPLE = 4; + constexpr uint16_t MSADPCM_NUM_COEFFICIENTS = 7; + + constexpr uint16_t MSADPCM_MIN_SAMPLES_PER_BLOCK = 4; + constexpr uint16_t MSADPCM_MAX_SAMPLES_PER_BLOCK = 64000; +} + + +//====================================================================================== +// Wave format utilities +//====================================================================================== + +bool DirectX::IsValid(_In_ const WAVEFORMATEX* wfx) noexcept +{ + if (!wfx) + return false; + + if (!wfx->nChannels) + { + DebugTrace("ERROR: Wave format must have at least 1 channel\n"); + return false; + } + + if (wfx->nChannels > XAUDIO2_MAX_AUDIO_CHANNELS) + { + DebugTrace("ERROR: Wave format must have less than %u channels (%u)\n", XAUDIO2_MAX_AUDIO_CHANNELS, wfx->nChannels); + return false; + } + + if (!wfx->nSamplesPerSec) + { + DebugTrace("ERROR: Wave format cannot have a sample rate of 0\n"); + return false; + } + + if ((wfx->nSamplesPerSec < XAUDIO2_MIN_SAMPLE_RATE) + || (wfx->nSamplesPerSec > XAUDIO2_MAX_SAMPLE_RATE)) + { + DebugTrace("ERROR: Wave format channel count must be in range %u..%u (%u)\n", + XAUDIO2_MIN_SAMPLE_RATE, XAUDIO2_MAX_SAMPLE_RATE, wfx->nSamplesPerSec); + return false; + } + + switch (wfx->wFormatTag) + { + case WAVE_FORMAT_PCM: + + switch (wfx->wBitsPerSample) + { + case 8: + case 16: + case 24: + case 32: + break; + + default: + DebugTrace("ERROR: Wave format integer PCM must have 8, 16, 24, or 32 bits per sample (%u)\n", wfx->wBitsPerSample); + return false; + } + + if (wfx->nBlockAlign != (wfx->nChannels * wfx->wBitsPerSample / 8)) + { + DebugTrace("ERROR: Wave format integer PCM - nBlockAlign (%u) != nChannels (%u) * wBitsPerSample (%u) / 8\n", + wfx->nBlockAlign, wfx->nChannels, wfx->wBitsPerSample); + return false; + } + + if (wfx->nAvgBytesPerSec != (wfx->nSamplesPerSec * wfx->nBlockAlign)) + { + DebugTrace("ERROR: Wave format integer PCM - nAvgBytesPerSec (%lu) != nSamplesPerSec (%lu) * nBlockAlign (%u)\n", + wfx->nAvgBytesPerSec, wfx->nSamplesPerSec, wfx->nBlockAlign); + return false; + } + + return true; + + case WAVE_FORMAT_IEEE_FLOAT: + + if (wfx->wBitsPerSample != 32) + { + DebugTrace("ERROR: Wave format float PCM must have 32-bits per sample (%u)\n", wfx->wBitsPerSample); + return false; + } + + if (wfx->nBlockAlign != (wfx->nChannels * wfx->wBitsPerSample / 8)) + { + DebugTrace("ERROR: Wave format float PCM - nBlockAlign (%u) != nChannels (%u) * wBitsPerSample (%u) / 8\n", + wfx->nBlockAlign, wfx->nChannels, wfx->wBitsPerSample); + return false; + } + + if (wfx->nAvgBytesPerSec != (wfx->nSamplesPerSec * wfx->nBlockAlign)) + { + DebugTrace("ERROR: Wave format float PCM - nAvgBytesPerSec (%lu) != nSamplesPerSec (%lu) * nBlockAlign (%u)\n", + wfx->nAvgBytesPerSec, wfx->nSamplesPerSec, wfx->nBlockAlign); + return false; + } + + return true; + + case WAVE_FORMAT_ADPCM: + + if ((wfx->nChannels != 1) && (wfx->nChannels != 2)) + { + DebugTrace("ERROR: Wave format ADPCM must have 1 or 2 channels (%u)\n", wfx->nChannels); + return false; + } + + if (wfx->wBitsPerSample != MSADPCM_BITS_PER_SAMPLE) + { + DebugTrace("ERROR: Wave format ADPCM must have 4 bits per sample (%u)\n", wfx->wBitsPerSample); + return false; + } + + if (wfx->cbSize != MSADPCM_FORMAT_EXTRA_BYTES) + { + DebugTrace("ERROR: Wave format ADPCM must have cbSize = 32 (%u)\n", wfx->cbSize); + return false; + } + else + { + auto wfadpcm = reinterpret_cast(wfx); + + if (wfadpcm->wNumCoef != MSADPCM_NUM_COEFFICIENTS) + { + DebugTrace("ERROR: Wave format ADPCM must have 7 coefficients (%u)\n", wfadpcm->wNumCoef); + return false; + } + + bool valid = true; + for (size_t j = 0; j < MSADPCM_NUM_COEFFICIENTS; ++j) + { + // Microsoft ADPCM standard encoding coefficients + static const short g_pAdpcmCoefficients1[] = { 256, 512, 0, 192, 240, 460, 392 }; + static const short g_pAdpcmCoefficients2[] = { 0, -256, 0, 64, 0, -208, -232 }; + + if (wfadpcm->aCoef[j].iCoef1 != g_pAdpcmCoefficients1[j] + || wfadpcm->aCoef[j].iCoef2 != g_pAdpcmCoefficients2[j]) + { + valid = false; + } + } + + if (!valid) + { + DebugTrace("ERROR: Wave formt ADPCM found non-standard coefficients\n"); + return false; + } + + if ((wfadpcm->wSamplesPerBlock < MSADPCM_MIN_SAMPLES_PER_BLOCK) + || (wfadpcm->wSamplesPerBlock > MSADPCM_MAX_SAMPLES_PER_BLOCK)) + { + DebugTrace("ERROR: Wave format ADPCM wSamplesPerBlock must be 4..64000 (%u)\n", wfadpcm->wSamplesPerBlock); + return false; + } + + if (wfadpcm->wfx.nChannels == 1 && (wfadpcm->wSamplesPerBlock % 2)) + { + DebugTrace("ERROR: Wave format ADPCM mono files must have even wSamplesPerBlock\n"); + return false; + } + + const int nHeaderBytes = MSADPCM_HEADER_LENGTH * wfx->nChannels; + const int nBitsPerFrame = MSADPCM_BITS_PER_SAMPLE * wfx->nChannels; + const int nPcmFramesPerBlock = (wfx->nBlockAlign - nHeaderBytes) * 8 / nBitsPerFrame + 2; + + if (wfadpcm->wSamplesPerBlock != nPcmFramesPerBlock) + { + DebugTrace("ERROR: Wave format ADPCM %u-channel with nBlockAlign = %u must have wSamplesPerBlock = %d (%u)\n", + wfx->nChannels, wfx->nBlockAlign, nPcmFramesPerBlock, wfadpcm->wSamplesPerBlock); + return false; + } + } + return true; + + case WAVE_FORMAT_WMAUDIO2: + case WAVE_FORMAT_WMAUDIO3: + + #ifdef DIRECTX_ENABLE_XWMA + + if (wfx->wBitsPerSample != 16) + { + DebugTrace("ERROR: Wave format xWMA only supports 16-bit data\n"); + return false; + } + + if (!wfx->nBlockAlign) + { + DebugTrace("ERROR: Wave format xWMA must have a non-zero nBlockAlign\n"); + return false; + } + + if (!wfx->nAvgBytesPerSec) + { + DebugTrace("ERROR: Wave format xWMA must have a non-zero nAvgBytesPerSec\n"); + return false; + } + + return true; + + #else + DebugTrace("ERROR: Wave format xWMA not supported by this version of DirectXTK for Audio\n"); + return false; + #endif + + case 0x166 /* WAVE_FORMAT_XMA2 */: + + #ifdef DIRECTX_ENABLE_XMA2 + + static_assert(WAVE_FORMAT_XMA2 == 0x166, "Unrecognized XMA2 tag"); + + if (wfx->nBlockAlign != wfx->nChannels * XMA_OUTPUT_SAMPLE_BYTES) + { + DebugTrace("ERROR: Wave format XMA2 - nBlockAlign (%u) != nChannels(%u) * %u\n", wfx->nBlockAlign, wfx->nChannels, XMA_OUTPUT_SAMPLE_BYTES); + return false; + } + + if (wfx->wBitsPerSample != XMA_OUTPUT_SAMPLE_BITS) + { + DebugTrace("ERROR: Wave format XMA2 wBitsPerSample (%u) should be %u\n", wfx->wBitsPerSample, XMA_OUTPUT_SAMPLE_BITS); + return false; + } + + if (wfx->cbSize != (sizeof(XMA2WAVEFORMATEX) - sizeof(WAVEFORMATEX))) + { + DebugTrace("ERROR: Wave format XMA2 - cbSize must be %zu (%u)\n", (sizeof(XMA2WAVEFORMATEX) - sizeof(WAVEFORMATEX)), wfx->cbSize); + return false; + } + else + { + auto xmaFmt = reinterpret_cast(wfx); + + if (xmaFmt->EncoderVersion < 3) + { + DebugTrace("ERROR: Wave format XMA2 encoder version (%u) - 3 or higher is required\n", xmaFmt->EncoderVersion); + return false; + } + + if (!xmaFmt->BlockCount) + { + DebugTrace("ERROR: Wave format XMA2 BlockCount must be non-zero\n"); + return false; + } + + if (!xmaFmt->BytesPerBlock || (xmaFmt->BytesPerBlock > XMA_READBUFFER_MAX_BYTES)) + { + DebugTrace("ERROR: Wave format XMA2 BytesPerBlock (%u) is invalid\n", xmaFmt->BytesPerBlock); + return false; + } + + if (xmaFmt->ChannelMask) + { + auto channelBits = ChannelsSpecifiedInMask(xmaFmt->ChannelMask); + if (channelBits != wfx->nChannels) + { + DebugTrace("ERROR: Wave format XMA2 - nChannels=%u but ChannelMask (%08X) has %u bits set\n", + xmaFmt->ChannelMask, wfx->nChannels, channelBits); + return false; + } + } + + if (xmaFmt->NumStreams != ((wfx->nChannels + 1) / 2)) + { + DebugTrace("ERROR: Wave format XMA2 - NumStreams (%u) != ( nChannels(%u) + 1 ) / 2\n", + xmaFmt->NumStreams, wfx->nChannels); + return false; + } + + if ((xmaFmt->PlayBegin + xmaFmt->PlayLength) > xmaFmt->SamplesEncoded) + { + DebugTrace("ERROR: Wave format XMA2 play region too large (%u + %u > %u)\n", + xmaFmt->PlayBegin, xmaFmt->PlayLength, xmaFmt->SamplesEncoded); + return false; + } + + if ((xmaFmt->LoopBegin + xmaFmt->LoopLength) > xmaFmt->SamplesEncoded) + { + DebugTrace("ERROR: Wave format XMA2 loop region too large (%u + %u > %u)\n", + xmaFmt->LoopBegin, xmaFmt->LoopLength, xmaFmt->SamplesEncoded); + return false; + } + } + return true; + + #else + DebugTrace("ERROR: Wave format XMA2 not supported by this version of DirectXTK for Audio\n"); + return false; + #endif + + case WAVE_FORMAT_EXTENSIBLE: + if (wfx->cbSize < (sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))) + { + DebugTrace("ERROR: Wave format WAVE_FORMAT_EXTENSIBLE - cbSize must be %zu (%u)\n", + (sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)), wfx->cbSize); + return false; + } + else + { + static const GUID s_wfexBase = { 0x00000000, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 } }; + + auto wfex = reinterpret_cast(wfx); + + if (memcmp(reinterpret_cast(&wfex->SubFormat) + sizeof(DWORD), + reinterpret_cast(&s_wfexBase) + sizeof(DWORD), sizeof(GUID) - sizeof(DWORD)) != 0) + { + DebugTrace("ERROR: Wave format WAVEFORMATEXTENSIBLE encountered with unknown GUID ({%8.8lX-%4.4X-%4.4X-%2.2X%2.2X-%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X})\n", + wfex->SubFormat.Data1, wfex->SubFormat.Data2, wfex->SubFormat.Data3, + wfex->SubFormat.Data4[0], wfex->SubFormat.Data4[1], wfex->SubFormat.Data4[2], wfex->SubFormat.Data4[3], + wfex->SubFormat.Data4[4], wfex->SubFormat.Data4[5], wfex->SubFormat.Data4[6], wfex->SubFormat.Data4[7]); + return false; + } + + switch (wfex->SubFormat.Data1) + { + case WAVE_FORMAT_PCM: + + switch (wfx->wBitsPerSample) + { + case 8: + case 16: + case 24: + case 32: + break; + + default: + DebugTrace("ERROR: Wave format integer PCM must have 8, 16, 24, or 32 bits per sample (%u)\n", + wfx->wBitsPerSample); + return false; + } + + switch (wfex->Samples.wValidBitsPerSample) + { + case 0: + case 8: + case 16: + case 20: + case 24: + case 32: + break; + + default: + DebugTrace("ERROR: Wave format integer PCM must have 8, 16, 20, 24, or 32 valid bits per sample (%u)\n", + wfex->Samples.wValidBitsPerSample); + return false; + } + + if (wfex->Samples.wValidBitsPerSample + && (wfex->Samples.wValidBitsPerSample > wfx->wBitsPerSample)) + { + DebugTrace("ERROR: Wave format ingter PCM wValidBitsPerSample (%u) is greater than wBitsPerSample (%u)\n", + wfex->Samples.wValidBitsPerSample, wfx->wBitsPerSample); + return false; + } + + if (wfx->nBlockAlign != (wfx->nChannels * wfx->wBitsPerSample / 8)) + { + DebugTrace("ERROR: Wave format integer PCM - nBlockAlign (%u) != nChannels (%u) * wBitsPerSample (%u) / 8\n", + wfx->nBlockAlign, wfx->nChannels, wfx->wBitsPerSample); + return false; + } + + if (wfx->nAvgBytesPerSec != (wfx->nSamplesPerSec * wfx->nBlockAlign)) + { + DebugTrace("ERROR: Wave format integer PCM - nAvgBytesPerSec (%lu) != nSamplesPerSec (%lu) * nBlockAlign (%u)\n", + wfx->nAvgBytesPerSec, wfx->nSamplesPerSec, wfx->nBlockAlign); + return false; + } + + break; + + case WAVE_FORMAT_IEEE_FLOAT: + + if (wfx->wBitsPerSample != 32) + { + DebugTrace("ERROR: Wave format float PCM must have 32-bits per sample (%u)\n", wfx->wBitsPerSample); + return false; + } + + switch (wfex->Samples.wValidBitsPerSample) + { + case 0: + case 32: + break; + + default: + DebugTrace("ERROR: Wave format float PCM must have 32 valid bits per sample (%u)\n", + wfex->Samples.wValidBitsPerSample); + return false; + } + + if (wfx->nBlockAlign != (wfx->nChannels * wfx->wBitsPerSample / 8)) + { + DebugTrace("ERROR: Wave format float PCM - nBlockAlign (%u) != nChannels (%u) * wBitsPerSample (%u) / 8\n", + wfx->nBlockAlign, wfx->nChannels, wfx->wBitsPerSample); + return false; + } + + if (wfx->nAvgBytesPerSec != (wfx->nSamplesPerSec * wfx->nBlockAlign)) + { + DebugTrace("ERROR: Wave format float PCM - nAvgBytesPerSec (%lu) != nSamplesPerSec (%lu) * nBlockAlign (%u)\n", + wfx->nAvgBytesPerSec, wfx->nSamplesPerSec, wfx->nBlockAlign); + return false; + } + + break; + + case WAVE_FORMAT_ADPCM: + DebugTrace("ERROR: Wave format ADPCM is not supported as a WAVEFORMATEXTENSIBLE\n"); + return false; + + case WAVE_FORMAT_WMAUDIO2: + case WAVE_FORMAT_WMAUDIO3: + + #ifdef DIRECTX_ENABLE_XWMA + + if (wfx->wBitsPerSample != 16) + { + DebugTrace("ERROR: Wave format xWMA only supports 16-bit data\n"); + return false; + } + + if (!wfx->nBlockAlign) + { + DebugTrace("ERROR: Wave format xWMA must have a non-zero nBlockAlign\n"); + return false; + } + + if (!wfx->nAvgBytesPerSec) + { + DebugTrace("ERROR: Wave format xWMA must have a non-zero nAvgBytesPerSec\n"); + return false; + } + + break; + + #else + DebugTrace("ERROR: Wave format xWMA not supported by this version of DirectXTK for Audio\n"); + return false; + #endif + + case 0x166 /* WAVE_FORMAT_XMA2 */: + DebugTrace("ERROR: Wave format XMA2 is not supported as a WAVEFORMATEXTENSIBLE\n"); + return false; + + default: + DebugTrace("ERROR: Unknown WAVEFORMATEXTENSIBLE format tag (%u)\n", wfex->SubFormat.Data1); + return false; + } + + if (wfex->dwChannelMask) + { + auto const channelBits = ChannelsSpecifiedInMask(wfex->dwChannelMask); + if (channelBits != wfx->nChannels) + { + DebugTrace("ERROR: WAVEFORMATEXTENSIBLE: nChannels=%u but ChannelMask has %u bits set\n", + wfx->nChannels, channelBits); + return false; + } + } + + return true; + } + + default: + DebugTrace("ERROR: Unknown WAVEFORMATEX format tag (%u)\n", wfx->wFormatTag); + return false; + } +} + + +uint32_t DirectX::GetDefaultChannelMask(int channels) noexcept +{ + switch (channels) + { + case 1: return SPEAKER_MONO; + case 2: return SPEAKER_STEREO; + case 3: return SPEAKER_2POINT1; + case 4: return SPEAKER_QUAD; + case 5: return SPEAKER_4POINT1; + case 6: return SPEAKER_5POINT1; + case 7: return SPEAKER_5POINT1 | SPEAKER_BACK_CENTER; + case 8: return SPEAKER_7POINT1; + default: return 0; + } +} + + +_Use_decl_annotations_ +void DirectX::CreateIntegerPCM( + WAVEFORMATEX* wfx, + int sampleRate, + int channels, + int sampleBits) noexcept +{ + const int blockAlign = channels * sampleBits / 8; + + wfx->wFormatTag = WAVE_FORMAT_PCM; + wfx->nChannels = static_cast(channels); + wfx->nSamplesPerSec = static_cast(sampleRate); + wfx->nAvgBytesPerSec = static_cast(blockAlign * sampleRate); + wfx->nBlockAlign = static_cast(blockAlign); + wfx->wBitsPerSample = static_cast(sampleBits); + wfx->cbSize = 0; + + assert(IsValid(wfx)); +} + + +_Use_decl_annotations_ +void DirectX::CreateFloatPCM( + WAVEFORMATEX* wfx, + int sampleRate, + int channels) noexcept +{ + const int blockAlign = channels * 4; + + wfx->wFormatTag = WAVE_FORMAT_IEEE_FLOAT; + wfx->nChannels = static_cast(channels); + wfx->nSamplesPerSec = static_cast(sampleRate); + wfx->nAvgBytesPerSec = static_cast(blockAlign * sampleRate); + wfx->nBlockAlign = static_cast(blockAlign); + wfx->wBitsPerSample = 32; + wfx->cbSize = 0; + + assert(IsValid(wfx)); +} + + +_Use_decl_annotations_ +void DirectX::CreateADPCM( + WAVEFORMATEX* wfx, + size_t wfxSize, + int sampleRate, + int channels, + int samplesPerBlock) noexcept(false) +{ + if (wfxSize < (sizeof(WAVEFORMATEX) + MSADPCM_FORMAT_EXTRA_BYTES)) + { + DebugTrace("CreateADPCM needs at least %zu bytes for the result\n", + (sizeof(WAVEFORMATEX) + MSADPCM_FORMAT_EXTRA_BYTES)); + throw std::invalid_argument("ADPCMWAVEFORMAT"); + } + + if (!samplesPerBlock) + { + DebugTrace("CreateADPCM needs a non-zero samples per block count\n"); + throw std::invalid_argument("ADPCMWAVEFORMAT"); + } + + const int blockAlign = MSADPCM_HEADER_LENGTH * channels + + (samplesPerBlock - 2) * MSADPCM_BITS_PER_SAMPLE * channels / 8; + + wfx->wFormatTag = WAVE_FORMAT_ADPCM; + wfx->nChannels = static_cast(channels); + wfx->nSamplesPerSec = static_cast(sampleRate); + wfx->nAvgBytesPerSec = static_cast(blockAlign * sampleRate / samplesPerBlock); + wfx->nBlockAlign = static_cast(blockAlign); + wfx->wBitsPerSample = MSADPCM_BITS_PER_SAMPLE; + wfx->cbSize = MSADPCM_FORMAT_EXTRA_BYTES; + + auto adpcm = reinterpret_cast(wfx); + adpcm->wSamplesPerBlock = static_cast(samplesPerBlock); + adpcm->wNumCoef = MSADPCM_NUM_COEFFICIENTS; + + static ADPCMCOEFSET aCoef[7] = { { 256, 0}, {512, -256}, {0,0}, {192,64}, {240,0}, {460, -208}, {392,-232} }; + memcpy(&adpcm->aCoef, aCoef, sizeof(aCoef)); + + assert(IsValid(wfx)); +} + + +#ifdef DIRECTX_ENABLE_XWMA +_Use_decl_annotations_ +void DirectX::CreateXWMA( + WAVEFORMATEX* wfx, + int sampleRate, + int channels, + int blockAlign, + int avgBytes, + bool wma3) noexcept +{ + wfx->wFormatTag = static_cast((wma3) ? WAVE_FORMAT_WMAUDIO3 : WAVE_FORMAT_WMAUDIO2); + wfx->nChannels = static_cast(channels); + wfx->nSamplesPerSec = static_cast(sampleRate); + wfx->nAvgBytesPerSec = static_cast(avgBytes); + wfx->nBlockAlign = static_cast(blockAlign); + wfx->wBitsPerSample = 16; + wfx->cbSize = 0; + + assert(IsValid(wfx)); +} +#endif + + +#ifdef DIRECTX_ENABLE_XMA2 +_Use_decl_annotations_ +void DirectX::CreateXMA2( + WAVEFORMATEX* wfx, + size_t wfxSize, + int sampleRate, + int channels, + int bytesPerBlock, + int blockCount, + int samplesEncoded) noexcept(false) +{ + if (wfxSize < sizeof(XMA2WAVEFORMATEX)) + { + DebugTrace("XMA2 needs at least %zu bytes for the result\n", sizeof(XMA2WAVEFORMATEX)); + throw std::invalid_argument("XMA2WAVEFORMATEX"); + } + + if ((bytesPerBlock < 1) || (bytesPerBlock > int(XMA_READBUFFER_MAX_BYTES))) + { + DebugTrace("XMA2 needs a valid bytes per block\n"); + throw std::invalid_argument("XMA2WAVEFORMATEX"); + } + + int blockAlign = (channels * XMA_OUTPUT_SAMPLE_BITS) / 8; + + wfx->wFormatTag = WAVE_FORMAT_XMA2; + wfx->nChannels = static_cast(channels); + wfx->nSamplesPerSec = static_cast(sampleRate); + wfx->nAvgBytesPerSec = static_cast(blockAlign * sampleRate); + wfx->nBlockAlign = static_cast(blockAlign); + wfx->wBitsPerSample = XMA_OUTPUT_SAMPLE_BITS; + wfx->cbSize = sizeof(XMA2WAVEFORMATEX) - sizeof(WAVEFORMATEX); + + auto xmaFmt = reinterpret_cast(wfx); + + xmaFmt->NumStreams = static_cast((channels + 1) / 2); + + xmaFmt->ChannelMask = GetDefaultChannelMask(channels); + + xmaFmt->SamplesEncoded = static_cast(samplesEncoded); + xmaFmt->BytesPerBlock = static_cast(bytesPerBlock); + xmaFmt->PlayBegin = xmaFmt->PlayLength = + xmaFmt->LoopBegin = xmaFmt->LoopLength = xmaFmt->LoopCount = 0; + xmaFmt->EncoderVersion = 4 /* XMAENCODER_VERSION_XMA2 */; + xmaFmt->BlockCount = static_cast(blockCount); + + assert(IsValid(wfx)); +} +#endif // XMA2 + + +_Use_decl_annotations_ +bool DirectX::ComputePan(float pan, unsigned int channels, float* matrix) noexcept +{ + memset(matrix, 0, sizeof(float) * 16); + + if (channels == 1) + { + // Mono panning + float left = 1.f - pan; + left = std::min(1.f, left); + left = std::max(0.f, left); + + float right = pan + 1.f; + right = std::min(1.f, right); + right = std::max(0.f, right); + + matrix[0] = left; + matrix[1] = right; + } + else if (channels == 2) + { + // Stereo panning + if (-1.f <= pan && pan <= 0.f) + { + matrix[0] = .5f * pan + 1.f; // .5 when pan is -1, 1 when pan is 0 + matrix[1] = .5f * -pan; // .5 when pan is -1, 0 when pan is 0 + matrix[2] = 0.f; // 0 when pan is -1, 0 when pan is 0 + matrix[3] = pan + 1.f; // 0 when pan is -1, 1 when pan is 0 + } + else + { + matrix[0] = -pan + 1.f; // 1 when pan is 0, 0 when pan is 1 + matrix[1] = 0.f; // 0 when pan is 0, 0 when pan is 1 + matrix[2] = .5f * pan; // 0 when pan is 0, .5f when pan is 1 + matrix[3] = .5f * -pan + 1.f; // 1 when pan is 0. .5f when pan is 1 + } + } + else + { + if (pan != 0.f) + { + DebugTrace("WARNING: Only supports panning on mono or stereo source data, ignored\n"); + } + return false; + } + + return true; +} + + +//====================================================================================== +// SoundEffectInstanceBase +//====================================================================================== + +void SoundEffectInstanceBase::SetPan(float pan) +{ + assert(pan >= -1.f && pan <= 1.f); + + mPan = pan; + + if (!voice) + return; + + float matrix[16]; + if (ComputePan(pan, mDSPSettings.SrcChannelCount, matrix)) + { + HRESULT hr = voice->SetOutputMatrix(nullptr, mDSPSettings.SrcChannelCount, mDSPSettings.DstChannelCount, matrix); + ThrowIfFailed(hr); + } +} + + +void SoundEffectInstanceBase::Apply3D(const X3DAUDIO_LISTENER& listener, const X3DAUDIO_EMITTER& emitter, bool rhcoords) +{ + if (!voice) + return; + + if (!(mFlags & SoundEffectInstance_Use3D)) + { + DebugTrace("ERROR: Apply3D called for an instance created without SoundEffectInstance_Use3D set\n"); + throw std::runtime_error("Apply3D"); + } + + DWORD dwCalcFlags = X3DAUDIO_CALCULATE_MATRIX | X3DAUDIO_CALCULATE_DOPPLER | X3DAUDIO_CALCULATE_LPF_DIRECT; + + if (mFlags & SoundEffectInstance_UseRedirectLFE) + { + // On devices with an LFE channel, allow the mono source data to be routed to the LFE destination channel. + dwCalcFlags |= X3DAUDIO_CALCULATE_REDIRECT_TO_LFE; + } + + auto reverb = mReverbVoice; + if (reverb) + { + dwCalcFlags |= X3DAUDIO_CALCULATE_LPF_REVERB | X3DAUDIO_CALCULATE_REVERB; + } + + float matrix[XAUDIO2_MAX_AUDIO_CHANNELS * 8] = {}; + assert(mDSPSettings.SrcChannelCount <= XAUDIO2_MAX_AUDIO_CHANNELS); + assert(mDSPSettings.DstChannelCount <= 8); + mDSPSettings.pMatrixCoefficients = matrix; + + assert(engine != nullptr); + if (rhcoords) + { + X3DAUDIO_EMITTER lhEmitter; + memcpy(&lhEmitter, &emitter, sizeof(X3DAUDIO_EMITTER)); + lhEmitter.OrientFront.z = -emitter.OrientFront.z; + lhEmitter.OrientTop.z = -emitter.OrientTop.z; + lhEmitter.Position.z = -emitter.Position.z; + lhEmitter.Velocity.z = -emitter.Velocity.z; + + X3DAUDIO_LISTENER lhListener; + memcpy(&lhListener, &listener, sizeof(X3DAUDIO_LISTENER)); + lhListener.OrientFront.z = -listener.OrientFront.z; + lhListener.OrientTop.z = -listener.OrientTop.z; + lhListener.Position.z = -listener.Position.z; + lhListener.Velocity.z = -listener.Velocity.z; + + X3DAudioCalculate(engine->Get3DHandle(), &lhListener, &lhEmitter, dwCalcFlags, &mDSPSettings); + } + else + { + X3DAudioCalculate(engine->Get3DHandle(), &listener, &emitter, dwCalcFlags, &mDSPSettings); + } + + mDSPSettings.pMatrixCoefficients = nullptr; + + std::ignore = voice->SetFrequencyRatio(mFreqRatio * mDSPSettings.DopplerFactor); + + auto direct = mDirectVoice; + assert(direct != nullptr); + std::ignore = voice->SetOutputMatrix(direct, mDSPSettings.SrcChannelCount, mDSPSettings.DstChannelCount, matrix); + + if (reverb) + { + for (size_t j = 0; (j < mDSPSettings.SrcChannelCount) && (j < XAUDIO2_MAX_AUDIO_CHANNELS); ++j) + { + matrix[j] = mDSPSettings.ReverbLevel; + } + std::ignore = voice->SetOutputMatrix(reverb, mDSPSettings.SrcChannelCount, 1, matrix); + } + + if (mFlags & SoundEffectInstance_ReverbUseFilters) + { + XAUDIO2_FILTER_PARAMETERS filterDirect = { LowPassFilter, 2.0f * sinf(X3DAUDIO_PI / 6.0f * mDSPSettings.LPFDirectCoefficient), 1.0f }; + // see XAudio2CutoffFrequencyToRadians() in XAudio2.h for more information on the formula used here + std::ignore = voice->SetOutputFilterParameters(direct, &filterDirect); + + if (reverb) + { + XAUDIO2_FILTER_PARAMETERS filterReverb = { LowPassFilter, 2.0f * sinf(X3DAUDIO_PI / 6.0f * mDSPSettings.LPFReverbCoefficient), 1.0f }; + // see XAudio2CutoffFrequencyToRadians() in XAudio2.h for more information on the formula used here + std::ignore = voice->SetOutputFilterParameters(reverb, &filterReverb); + } + } +} + + +//====================================================================================== +// AudioListener/Emitter helpers +//====================================================================================== + +namespace +{ + inline bool IsValid(const X3DAUDIO_CONE& cone) noexcept + { + // These match the validation ranges in X3DAudio. + if (cone.InnerAngle < 0.f || cone.InnerAngle > X3DAUDIO_2PI) + return false; + + if (cone.OuterAngle < 0.f || cone.OuterAngle > X3DAUDIO_2PI) + return false; + + if (cone.InnerAngle > cone.OuterAngle) + return false; + + if (cone.InnerVolume < 0.f || cone.InnerVolume > 2.f) + return false; + + if (cone.OuterVolume < 0.f || cone.OuterVolume > 2.f) + return false; + + if (cone.InnerLPF < 0.f || cone.InnerLPF > 1.f) + return false; + + if (cone.OuterLPF < 0.f || cone.OuterLPF > 1.f) + return false; + + if (cone.InnerReverb < 0.f || cone.InnerReverb > 2.f) + return false; + + if (cone.OuterReverb < 0.f || cone.OuterReverb > 2.f) + return false; + + return true; + } +} + +void AudioListener::SetCone(const X3DAUDIO_CONE& listenerCone) +{ + if (!::IsValid(listenerCone)) + throw std::invalid_argument("X3DAUDIO_CONE values out of range"); + + ListenerCone = listenerCone; + pCone = &ListenerCone; +} + +void AudioEmitter::SetCone(const X3DAUDIO_CONE& emitterCone) +{ + if (!::IsValid(emitterCone)) + throw std::invalid_argument("X3DAUDIO_CONE values out of range"); + + EmitterCone = emitterCone; + pCone = &EmitterCone; +} + +namespace +{ + // **Note these constants came from xact3d3.h in the legacy DirectX SDK** + // + // Supported speaker positions, represented as azimuth angles. + // + // Here's a picture of the azimuth angles for the 8 cardinal points, + // seen from above. The emitter's base position is at the origin 0. + // + // FRONT + // | 0 <-- azimuth + // | + // 7pi/4 \ | / pi/4 + // \ | / + // LEFT \|/ RIGHT + // 3pi/2-------0-------pi/2 + // /|\ + // / | \ + // 5pi/4 / | \ 3pi/4 + // | + // | pi + // BACK + // + + constexpr float LEFT_AZIMUTH = 3 * X3DAUDIO_PI / 2; + constexpr float RIGHT_AZIMUTH = X3DAUDIO_PI / 2; + constexpr float FRONT_LEFT_AZIMUTH = 7 * X3DAUDIO_PI / 4; + constexpr float FRONT_RIGHT_AZIMUTH = X3DAUDIO_PI / 4; + constexpr float FRONT_CENTER_AZIMUTH = 0.0f; + constexpr float LOW_FREQUENCY_AZIMUTH = X3DAUDIO_2PI; + constexpr float BACK_LEFT_AZIMUTH = 5 * X3DAUDIO_PI / 4; + constexpr float BACK_RIGHT_AZIMUTH = 3 * X3DAUDIO_PI / 4; + constexpr float BACK_CENTER_AZIMUTH = X3DAUDIO_PI; + + constexpr float c_channelAzimuths[9][8] = + { + /* 0 */ { 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f }, + /* 1 */ { 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f }, + /* 2 */ { FRONT_LEFT_AZIMUTH, FRONT_RIGHT_AZIMUTH, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f }, + /* 2.1 */ { FRONT_LEFT_AZIMUTH, FRONT_RIGHT_AZIMUTH, LOW_FREQUENCY_AZIMUTH, 0.f, 0.f, 0.f, 0.f, 0.f }, + /* 4.0 */ { FRONT_LEFT_AZIMUTH, FRONT_RIGHT_AZIMUTH, BACK_LEFT_AZIMUTH, BACK_RIGHT_AZIMUTH, 0.f, 0.f, 0.f, 0.f }, + /* 4.1 */ { FRONT_LEFT_AZIMUTH, FRONT_RIGHT_AZIMUTH, LOW_FREQUENCY_AZIMUTH, BACK_LEFT_AZIMUTH, BACK_RIGHT_AZIMUTH, 0.f, 0.f, 0.f }, + /* 5.1 */ { FRONT_LEFT_AZIMUTH, FRONT_RIGHT_AZIMUTH, FRONT_CENTER_AZIMUTH, LOW_FREQUENCY_AZIMUTH, BACK_LEFT_AZIMUTH, BACK_RIGHT_AZIMUTH, 0.f, 0.f }, + /* 6.1 */ { FRONT_LEFT_AZIMUTH, FRONT_RIGHT_AZIMUTH, FRONT_CENTER_AZIMUTH, LOW_FREQUENCY_AZIMUTH, BACK_LEFT_AZIMUTH, BACK_RIGHT_AZIMUTH, BACK_CENTER_AZIMUTH, 0.f }, + /* 7.1 */ { FRONT_LEFT_AZIMUTH, FRONT_RIGHT_AZIMUTH, FRONT_CENTER_AZIMUTH, LOW_FREQUENCY_AZIMUTH, BACK_LEFT_AZIMUTH, BACK_RIGHT_AZIMUTH, LEFT_AZIMUTH, RIGHT_AZIMUTH } + }; +} + +void AudioEmitter::EnableDefaultMultiChannel(unsigned int channels, float radius) +{ + if (channels > XAUDIO2_MAX_AUDIO_CHANNELS) + throw std::invalid_argument("Invalid channel count"); + + ChannelCount = channels; + ChannelRadius = radius; + pChannelAzimuths = EmitterAzimuths; + + if (channels <= 8) + { + memcpy(EmitterAzimuths, &c_channelAzimuths[channels][0], sizeof(float) * 8); + } + else + { + memset(EmitterAzimuths, 0, sizeof(float) * size_t(channels)); + } +} + +namespace +{ + // **Note these match the defaults from xact3d3.h in the legacy DirectX SDK** + constexpr X3DAUDIO_DISTANCE_CURVE_POINT c_defaultCurvePoints[2] = { { 0.0f, 1.0f }, { 1.0f, 1.0f } }; + constexpr X3DAUDIO_DISTANCE_CURVE c_defaultCurve = { const_cast(c_defaultCurvePoints), 2 }; + + // **Note these match X3DAudioDefault_LinearCurvePoints from x3daudio.h** + constexpr X3DAUDIO_DISTANCE_CURVE_POINT c_linearCurvePoints[2] = { { 0.0f, 1.0f }, { 1.0f, 0.0f } }; + constexpr X3DAUDIO_DISTANCE_CURVE c_linearCurve = { const_cast(c_linearCurvePoints), 2 }; +} + +void AudioEmitter::EnableDefaultCurves() noexcept +{ + pVolumeCurve = const_cast(&c_defaultCurve); + pLFECurve = const_cast(&c_defaultCurve); + pLPFDirectCurve = pLPFReverbCurve = pReverbCurve = nullptr; +} + +void AudioEmitter::EnableLinearCurves() noexcept +{ + pVolumeCurve = const_cast(&c_linearCurve); + pLFECurve = const_cast(&c_linearCurve); + pLPFDirectCurve = pLPFReverbCurve = pReverbCurve = nullptr; +} diff --git a/Common/DirectXTK12/Audio/SoundCommon.h b/Common/DirectXTK12/Audio/SoundCommon.h new file mode 100644 index 0000000..1fefe19 --- /dev/null +++ b/Common/DirectXTK12/Audio/SoundCommon.h @@ -0,0 +1,393 @@ +//-------------------------------------------------------------------------------------- +// File: SoundCommon.h +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248929 +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#pragma once + +#include "Audio.h" +#include "PlatformHelpers.h" + +#ifdef USING_XAUDIO2_9 +#define DIRECTX_ENABLE_XWMA +#endif + +#if (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) +#define DIRECTX_ENABLE_XMA2 +#endif + +#if defined(DIRECTX_ENABLE_XWMA) || defined(DIRECTX_ENABLE_XMA2) +#define DIRECTX_ENABLE_SEEK_TABLES +#endif + +namespace DirectX +{ + // Helper for getting a format tag from a WAVEFORMATEX + inline uint32_t GetFormatTag(const WAVEFORMATEX* wfx) noexcept + { + if (wfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE) + { + if (wfx->cbSize < (sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))) + return 0; + + static const GUID s_wfexBase = { 0x00000000, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 } }; + + auto wfex = reinterpret_cast(wfx); + + if (memcmp(reinterpret_cast(&wfex->SubFormat) + sizeof(DWORD), + reinterpret_cast(&s_wfexBase) + sizeof(DWORD), sizeof(GUID) - sizeof(DWORD)) != 0) + { + return 0; + } + + return wfex->SubFormat.Data1; + } + else + { + return wfx->wFormatTag; + } + } + + + // Helper for validating wave format structure + bool IsValid(_In_ const WAVEFORMATEX* wfx) noexcept; + + + // Helper for getting a default channel mask from channels + uint32_t GetDefaultChannelMask(int channels) noexcept; + + + // Helpers for creating various wave format structures + void CreateIntegerPCM(_Out_ WAVEFORMATEX* wfx, + int sampleRate, int channels, int sampleBits) noexcept; + void CreateFloatPCM(_Out_ WAVEFORMATEX* wfx, + int sampleRate, int channels) noexcept; + void CreateADPCM(_Out_writes_bytes_(wfxSize) WAVEFORMATEX* wfx, size_t wfxSize, + int sampleRate, int channels, int samplesPerBlock) noexcept(false); +#ifdef DIRECTX_ENABLE_XWMA + void CreateXWMA(_Out_ WAVEFORMATEX* wfx, + int sampleRate, int channels, int blockAlign, int avgBytes, bool wma3) noexcept; +#endif +#ifdef DIRECTX_ENABLE_XMA2 + void CreateXMA2(_Out_writes_bytes_(wfxSize) WAVEFORMATEX* wfx, size_t wfxSize, + int sampleRate, int channels, int bytesPerBlock, int blockCount, int samplesEncoded) noexcept(false); +#endif + + // Helper for computing pan volume matrix + bool ComputePan(float pan, unsigned int channels, _Out_writes_(16) float* matrix) noexcept; + + // Helper class for implementing SoundEffectInstance + class SoundEffectInstanceBase + { + public: + SoundEffectInstanceBase() noexcept : + voice(nullptr), + state(STOPPED), + engine(nullptr), + mVolume(1.f), + mPitch(0.f), + mFreqRatio(1.f), + mPan(0.f), + mFlags(SoundEffectInstance_Default), + mDirectVoice(nullptr), + mReverbVoice(nullptr), + mDSPSettings{} + { + } + + SoundEffectInstanceBase(SoundEffectInstanceBase&&) = default; + SoundEffectInstanceBase& operator= (SoundEffectInstanceBase&&) = default; + + SoundEffectInstanceBase(SoundEffectInstanceBase const&) = delete; + SoundEffectInstanceBase& operator= (SoundEffectInstanceBase const&) = delete; + + ~SoundEffectInstanceBase() + { + assert(voice == nullptr); + } + + void Initialize(_In_ AudioEngine* eng, _In_ const WAVEFORMATEX* wfx, SOUND_EFFECT_INSTANCE_FLAGS flags) noexcept + { + assert(eng != nullptr); + engine = eng; + mDirectVoice = eng->GetMasterVoice(); + mReverbVoice = eng->GetReverbVoice(); + + if (eng->GetChannelMask() & SPEAKER_LOW_FREQUENCY) + mFlags = flags | SoundEffectInstance_UseRedirectLFE; + else + mFlags = flags & ~SoundEffectInstance_UseRedirectLFE; + + memset(&mDSPSettings, 0, sizeof(X3DAUDIO_DSP_SETTINGS)); + assert(wfx != nullptr); + mDSPSettings.SrcChannelCount = wfx->nChannels; + mDSPSettings.DstChannelCount = eng->GetOutputChannels(); + } + + void AllocateVoice(_In_ const WAVEFORMATEX* wfx) + { + if (voice) + return; + + assert(engine != nullptr); + engine->AllocateVoice(wfx, mFlags, false, &voice); + } + + void DestroyVoice() noexcept + { + if (voice) + { + assert(engine != nullptr); + engine->DestroyVoice(voice); + voice = nullptr; + } + } + + bool Play() // Returns true if STOPPED -> PLAYING + { + if (voice) + { + if (state == PAUSED) + { + HRESULT hr = voice->Start(0); + ThrowIfFailed(hr); + state = PLAYING; + } + else if (state != PLAYING) + { + if (mVolume != 1.f) + { + HRESULT hr = voice->SetVolume(mVolume); + ThrowIfFailed(hr); + } + + if (mPitch != 0.f) + { + mFreqRatio = XAudio2SemitonesToFrequencyRatio(mPitch * 12.f); + + HRESULT hr = voice->SetFrequencyRatio(mFreqRatio); + ThrowIfFailed(hr); + } + + if (mPan != 0.f) + { + SetPan(mPan); + } + + HRESULT hr = voice->Start(0); + ThrowIfFailed(hr); + state = PLAYING; + return true; + } + } + return false; + } + + void Stop(bool immediate, bool& looped) noexcept + { + if (!voice) + { + state = STOPPED; + return; + } + + if (immediate) + { + state = STOPPED; + std::ignore = voice->Stop(0); + std::ignore = voice->FlushSourceBuffers(); + } + else if (looped) + { + looped = false; + std::ignore = voice->ExitLoop(); + } + else + { + std::ignore = voice->Stop(XAUDIO2_PLAY_TAILS); + } + } + + void Pause() noexcept + { + if (voice && state == PLAYING) + { + state = PAUSED; + + std::ignore = voice->Stop(0); + } + } + + void Resume() + { + if (voice && state == PAUSED) + { + HRESULT hr = voice->Start(0); + ThrowIfFailed(hr); + state = PLAYING; + } + } + + void SetVolume(float volume) + { + assert(volume >= -XAUDIO2_MAX_VOLUME_LEVEL && volume <= XAUDIO2_MAX_VOLUME_LEVEL); + + mVolume = volume; + + if (voice) + { + HRESULT hr = voice->SetVolume(volume); + ThrowIfFailed(hr); + } + } + + void SetPitch(float pitch) + { + assert(pitch >= -1.f && pitch <= 1.f); + + if ((mFlags & SoundEffectInstance_NoSetPitch) && pitch != 0.f) + { + DebugTrace("ERROR: Sound effect instance was created with the NoSetPitch flag\n"); + throw std::runtime_error("SetPitch"); + } + + mPitch = pitch; + + if (voice) + { + mFreqRatio = XAudio2SemitonesToFrequencyRatio(mPitch * 12.f); + + HRESULT hr = voice->SetFrequencyRatio(mFreqRatio); + ThrowIfFailed(hr); + } + } + + void SetPan(float pan); + + void Apply3D(const X3DAUDIO_LISTENER& listener, const X3DAUDIO_EMITTER& emitter, bool rhcoords); + + SoundState GetState(bool autostop) noexcept + { + if (autostop && voice && (state == PLAYING)) + { + XAUDIO2_VOICE_STATE xstate; + voice->GetState(&xstate, XAUDIO2_VOICE_NOSAMPLESPLAYED); + + if (!xstate.BuffersQueued) + { + // Automatic stop if the buffer has finished playing + std::ignore = voice->Stop(); + state = STOPPED; + } + } + + return state; + } + + int GetPendingBufferCount() const noexcept + { + if (!voice) + return 0; + + XAUDIO2_VOICE_STATE xstate; + voice->GetState(&xstate, XAUDIO2_VOICE_NOSAMPLESPLAYED); + return static_cast(xstate.BuffersQueued); + } + + unsigned int GetChannelCount() const noexcept + { + return mDSPSettings.SrcChannelCount; + } + + void OnCriticalError() noexcept + { + if (voice) + { + voice->DestroyVoice(); + voice = nullptr; + } + state = STOPPED; + mDirectVoice = nullptr; + mReverbVoice = nullptr; + } + + void OnReset() noexcept + { + assert(engine != nullptr); + mDirectVoice = engine->GetMasterVoice(); + mReverbVoice = engine->GetReverbVoice(); + + if (engine->GetChannelMask() & SPEAKER_LOW_FREQUENCY) + mFlags = mFlags | SoundEffectInstance_UseRedirectLFE; + else + mFlags = mFlags & ~SoundEffectInstance_UseRedirectLFE; + + mDSPSettings.DstChannelCount = engine->GetOutputChannels(); + } + + void OnDestroy() noexcept + { + if (voice) + { + std::ignore = voice->Stop(0); + std::ignore = voice->FlushSourceBuffers(); + voice->DestroyVoice(); + voice = nullptr; + } + state = STOPPED; + engine = nullptr; + mDirectVoice = nullptr; + mReverbVoice = nullptr; + } + + void OnTrim() + { + if (voice && (state == STOPPED)) + { + engine->DestroyVoice(voice); + voice = nullptr; + } + } + + void GatherStatistics(AudioStatistics& stats) const noexcept + { + ++stats.allocatedInstances; + if (voice) + { + ++stats.allocatedVoices; + + if (mFlags & SoundEffectInstance_Use3D) + ++stats.allocatedVoices3d; + + if (state == PLAYING) + ++stats.playingInstances; + } + } + + IXAudio2SourceVoice* voice; + SoundState state; + AudioEngine* engine; + + private: + float mVolume; + float mPitch; + float mFreqRatio; + float mPan; + SOUND_EFFECT_INSTANCE_FLAGS mFlags; + IXAudio2Voice* mDirectVoice; + IXAudio2Voice* mReverbVoice; + X3DAUDIO_DSP_SETTINGS mDSPSettings; + }; + + struct WaveBankSeekData + { + uint32_t seekCount; + const uint32_t* seekTable; + uint32_t tag; + }; +} diff --git a/Common/DirectXTK12/Audio/SoundEffect.cpp b/Common/DirectXTK12/Audio/SoundEffect.cpp new file mode 100644 index 0000000..5985212 --- /dev/null +++ b/Common/DirectXTK12/Audio/SoundEffect.cpp @@ -0,0 +1,629 @@ +//-------------------------------------------------------------------------------------- +// File: SoundEffect.cpp +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248929 +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "pch.h" +#include "WAVFileReader.h" +#include "SoundCommon.h" + +#include + +#if (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wnonportable-system-include-path" +#endif + +#include +#include +#endif + +using namespace DirectX; + + +//====================================================================================== +// SoundEffect +//====================================================================================== + +// Internal object implementation class. +class SoundEffect::Impl : public IVoiceNotify +{ +public: + explicit Impl(_In_ AudioEngine* engine) : + mWaveFormat(nullptr), + mStartAudio(nullptr), + mAudioBytes(0), + mLoopStart(0), + mLoopLength(0), + mEngine(engine), + mOneShots(0) + #ifdef DIRECTX_ENABLE_SEEK_TABLES + , mSeekCount(0) + , mSeekTable(nullptr) + #endif + #ifdef DIRECTX_ENABLE_XMA2 + , mXMAMemory(nullptr) + #endif + { + assert(mEngine != nullptr); + mEngine->RegisterNotify(this, false); + } + + Impl(Impl&&) = default; + Impl& operator= (Impl&&) = default; + + Impl(Impl const&) = delete; + Impl& operator= (Impl const&) = delete; + + ~Impl() override + { + if (!mInstances.empty()) + { + DebugTrace("WARNING: Destroying SoundEffect with %zu outstanding SoundEffectInstances\n", mInstances.size()); + + for (auto it : mInstances) + { + assert(it != nullptr); + it->OnDestroyParent(); + } + + mInstances.clear(); + } + + if (mOneShots > 0) + { + DebugTrace("WARNING: Destroying SoundEffect with %u outstanding one shot effects\n", mOneShots); + } + + if (mEngine) + { + mEngine->UnregisterNotify(this, true, false); + mEngine = nullptr; + } + + #ifdef DIRECTX_ENABLE_XMA2 + if (mXMAMemory) + { + ApuFree(mXMAMemory); + mXMAMemory = nullptr; + } + #endif + } + + HRESULT Initialize( + _In_ const AudioEngine* engine, + _Inout_ std::unique_ptr& wavData, + _In_ const WAVEFORMATEX* wfx, + _In_reads_bytes_(audioBytes) const uint8_t* startAudio, size_t audioBytes, + #ifdef DIRECTX_ENABLE_SEEK_TABLES + _In_reads_opt_(seekCount) const uint32_t* seekTable, size_t seekCount, + #endif + uint32_t loopStart, uint32_t loopLength) noexcept; + + void Play(float volume, float pitch, float pan); + + // IVoiceNotify + void __cdecl OnBufferEnd() override + { + InterlockedDecrement(&mOneShots); + } + + void __cdecl OnCriticalError() override + { + mOneShots = 0; + } + + void __cdecl OnReset() override + { + // No action required + } + + void __cdecl OnUpdate() override + { + // We do not register for update notification + assert(false); + } + + void __cdecl OnDestroyEngine() noexcept override + { + mEngine = nullptr; + mOneShots = 0; + } + + void __cdecl OnTrim() override + { + // No action required + } + + void __cdecl GatherStatistics(AudioStatistics& stats) const noexcept override + { + stats.playingOneShots += mOneShots; + stats.audioBytes += mAudioBytes; + + #ifdef DIRECTX_ENABLE_XMA2 + if (mXMAMemory) + stats.xmaAudioBytes += mAudioBytes; + #endif + } + + void __cdecl OnDestroyParent() noexcept override + { + } + + const WAVEFORMATEX* mWaveFormat; + const uint8_t* mStartAudio; + uint32_t mAudioBytes; + uint32_t mLoopStart; + uint32_t mLoopLength; + AudioEngine* mEngine; + std::list mInstances; + uint32_t mOneShots; + +#ifdef DIRECTX_ENABLE_SEEK_TABLES + uint32_t mSeekCount; + const uint32_t* mSeekTable; +#endif + +private: + std::unique_ptr mWavData; + +#ifdef DIRECTX_ENABLE_XMA2 + void* mXMAMemory; +#endif +}; + + +_Use_decl_annotations_ +HRESULT SoundEffect::Impl::Initialize( + const AudioEngine* engine, + std::unique_ptr& wavData, + const WAVEFORMATEX* wfx, + const uint8_t* startAudio, size_t audioBytes, +#ifdef DIRECTX_ENABLE_SEEK_TABLES + const uint32_t* seekTable, size_t seekCount, +#endif + uint32_t loopStart, uint32_t loopLength) noexcept +{ + if (!engine || !IsValid(wfx) || !startAudio || !audioBytes || !wavData) + return E_INVALIDARG; + + if (audioBytes > UINT32_MAX) + return E_INVALIDARG; + + switch (GetFormatTag(wfx)) + { + case WAVE_FORMAT_PCM: + case WAVE_FORMAT_IEEE_FLOAT: + case WAVE_FORMAT_ADPCM: + // Take ownership of the buffer + mWavData.reset(wavData.release()); + + // WARNING: We assume the wfx and startAudio parameters are pointers into the wavData memory buffer + mWaveFormat = wfx; + mStartAudio = startAudio; + break; + + #ifdef DIRECTX_ENABLE_XWMA + + case WAVE_FORMAT_WMAUDIO2: + case WAVE_FORMAT_WMAUDIO3: + if (!seekCount || !seekTable) + { + DebugTrace("ERROR: SoundEffect format xWMA requires seek table\n"); + return E_FAIL; + } + + if (seekCount > UINT32_MAX) + return E_INVALIDARG; + + // Take ownership of the buffer + mWavData.reset(wavData.release()); + + // WARNING: We assume the wfx, startAudio, and mSeekTable parameters are pointers into the wavData memory buffer + mWaveFormat = wfx; + mStartAudio = startAudio; + mSeekCount = static_cast(seekCount); + mSeekTable = seekTable; + break; + + #endif // xWMA + + #ifdef DIRECTX_ENABLE_XMA2 + + case WAVE_FORMAT_XMA2: + if (!seekCount || !seekTable) + { + DebugTrace("ERROR: SoundEffect format XMA2 requires seek table\n"); + return E_FAIL; + } + + if (seekCount > UINT32_MAX) + return E_INVALIDARG; + + { + HRESULT hr = ApuAlloc(&mXMAMemory, nullptr, + static_cast(audioBytes), SHAPE_XMA_INPUT_BUFFER_ALIGNMENT); + if (FAILED(hr)) + { + DebugTrace("ERROR: ApuAlloc failed. Did you allocate a large enough heap with ApuCreateHeap for all your XMA wave data?\n"); + return hr; + } + } + + memcpy(mXMAMemory, startAudio, audioBytes); + mStartAudio = reinterpret_cast(mXMAMemory); + + mWavData.reset(new (std::nothrow) uint8_t[sizeof(XMA2WAVEFORMATEX) + (seekCount * sizeof(uint32_t))]); + if (!mWavData) + return E_OUTOFMEMORY; + + memcpy(mWavData.get(), wfx, sizeof(XMA2WAVEFORMATEX)); + mWaveFormat = reinterpret_cast(mWavData.get()); + + // XMA seek table is Big-Endian + { + auto dest = reinterpret_cast(mWavData.get() + sizeof(XMA2WAVEFORMATEX)); + for (size_t k = 0; k < seekCount; ++k) + { + dest[k] = _byteswap_ulong(seekTable[k]); + } + } + + mSeekCount = static_cast(seekCount); + mSeekTable = reinterpret_cast(mWavData.get() + sizeof(XMA2WAVEFORMATEX)); + + wavData.reset(); + break; + + #endif // XMA2 + + default: + { + DebugTrace("ERROR: SoundEffect encountered an unsupported format tag (%u)\n", wfx->wFormatTag); + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + } + + mAudioBytes = static_cast(audioBytes); + mLoopStart = loopStart; + mLoopLength = loopLength; + + return S_OK; +} + + +void SoundEffect::Impl::Play(float volume, float pitch, float pan) +{ + assert(volume >= -XAUDIO2_MAX_VOLUME_LEVEL && volume <= XAUDIO2_MAX_VOLUME_LEVEL); + assert(pitch >= -1.f && pitch <= 1.f); + assert(pan >= -1.f && pan <= 1.f); + + IXAudio2SourceVoice* voice = nullptr; + mEngine->AllocateVoice(mWaveFormat, SoundEffectInstance_Default, true, &voice); + + if (!voice) + return; + + if (volume != 1.f) + { + HRESULT hr = voice->SetVolume(volume); + ThrowIfFailed(hr); + } + + if (pitch != 0.f) + { + const float fr = XAudio2SemitonesToFrequencyRatio(pitch * 12.f); + + HRESULT hr = voice->SetFrequencyRatio(fr); + ThrowIfFailed(hr); + } + + if (pan != 0.f) + { + float matrix[16]; + if (ComputePan(pan, mWaveFormat->nChannels, matrix)) + { + HRESULT hr = voice->SetOutputMatrix(nullptr, mWaveFormat->nChannels, mEngine->GetOutputChannels(), matrix); + ThrowIfFailed(hr); + } + } + + HRESULT hr = voice->Start(0); + ThrowIfFailed(hr); + + XAUDIO2_BUFFER buffer = {}; + buffer.AudioBytes = mAudioBytes; + buffer.pAudioData = mStartAudio; + buffer.Flags = XAUDIO2_END_OF_STREAM; + buffer.pContext = this; + +#ifdef DIRECTX_ENABLE_XWMA + const uint32_t tag = GetFormatTag(mWaveFormat); + if (tag == WAVE_FORMAT_WMAUDIO2 || tag == WAVE_FORMAT_WMAUDIO3) + { + XAUDIO2_BUFFER_WMA wmaBuffer = {}; + wmaBuffer.PacketCount = mSeekCount; + wmaBuffer.pDecodedPacketCumulativeBytes = mSeekTable; + + hr = voice->SubmitSourceBuffer(&buffer, &wmaBuffer); + } + else + #endif // xWMA + { + hr = voice->SubmitSourceBuffer(&buffer, nullptr); + } + if (FAILED(hr)) + { + DebugTrace("ERROR: SoundEffect failed (%08X) when submitting buffer:\n", static_cast(hr)); + DebugTrace("\tFormat Tag %u, %u channels, %u-bit, %u Hz, %u bytes\n", + mWaveFormat->wFormatTag, mWaveFormat->nChannels, mWaveFormat->wBitsPerSample, mWaveFormat->nSamplesPerSec, mAudioBytes); + throw std::runtime_error("SubmitSourceBuffer"); + } + + InterlockedIncrement(&mOneShots); +} + + +//-------------------------------------------------------------------------------------- +// SoundEffect +//-------------------------------------------------------------------------------------- + +// Public constructors. +_Use_decl_annotations_ +SoundEffect::SoundEffect(AudioEngine* engine, const wchar_t* waveFileName) + : pImpl(std::make_unique(engine)) +{ + WAVData wavInfo; + std::unique_ptr wavData; + HRESULT hr = LoadWAVAudioFromFileEx(waveFileName, wavData, wavInfo); + if (FAILED(hr)) + { + DebugTrace("ERROR: SoundEffect failed (%08X) to load from .wav file \"%ls\"\n", + static_cast(hr), waveFileName); + throw std::runtime_error("SoundEffect"); + } + +#ifdef DIRECTX_ENABLE_SEEK_TABLES + hr = pImpl->Initialize(engine, wavData, wavInfo.wfx, wavInfo.startAudio, wavInfo.audioBytes, + wavInfo.seek, wavInfo.seekCount, + wavInfo.loopStart, wavInfo.loopLength); +#else + hr = pImpl->Initialize(engine, wavData, wavInfo.wfx, wavInfo.startAudio, wavInfo.audioBytes, + wavInfo.loopStart, wavInfo.loopLength); +#endif + + if (FAILED(hr)) + { + DebugTrace("ERROR: SoundEffect failed (%08X) to intialize from .wav file \"%ls\"\n", + static_cast(hr), waveFileName); + throw std::runtime_error("SoundEffect"); + } +} + + +_Use_decl_annotations_ +SoundEffect::SoundEffect(AudioEngine* engine, std::unique_ptr& wavData, + const WAVEFORMATEX* wfx, const uint8_t* startAudio, size_t audioBytes) + : pImpl(std::make_unique(engine)) +{ +#ifdef DIRECTX_ENABLE_SEEK_TABLES + HRESULT hr = pImpl->Initialize(engine, wavData, wfx, startAudio, audioBytes, nullptr, 0, 0, 0); +#else + HRESULT hr = pImpl->Initialize(engine, wavData, wfx, startAudio, audioBytes, 0, 0); +#endif + if (FAILED(hr)) + { + DebugTrace("ERROR: SoundEffect failed (%08X) to intialize\n", static_cast(hr)); + throw std::runtime_error("SoundEffect"); + } +} + + +_Use_decl_annotations_ +SoundEffect::SoundEffect(AudioEngine* engine, std::unique_ptr& wavData, + const WAVEFORMATEX* wfx, const uint8_t* startAudio, size_t audioBytes, + uint32_t loopStart, uint32_t loopLength) + : pImpl(std::make_unique(engine)) +{ +#ifdef DIRECTX_ENABLE_SEEK_TABLES + HRESULT hr = pImpl->Initialize(engine, wavData, wfx, startAudio, audioBytes, nullptr, 0, loopStart, loopLength); +#else + HRESULT hr = pImpl->Initialize(engine, wavData, wfx, startAudio, audioBytes, loopStart, loopLength); +#endif + if (FAILED(hr)) + { + DebugTrace("ERROR: SoundEffect failed (%08X) to intialize\n", static_cast(hr)); + throw std::runtime_error("SoundEffect"); + } +} + + +#ifdef DIRECTX_ENABLE_SEEK_TABLES + +_Use_decl_annotations_ +SoundEffect::SoundEffect(AudioEngine* engine, std::unique_ptr& wavData, + const WAVEFORMATEX* wfx, const uint8_t* startAudio, size_t audioBytes, + const uint32_t* seekTable, size_t seekCount) +{ + HRESULT hr = pImpl->Initialize(engine, wavData, wfx, startAudio, audioBytes, seekTable, seekCount, 0, 0); + if (FAILED(hr)) + { + DebugTrace("ERROR: SoundEffect failed (%08X) to intialize\n", static_cast(hr)); + throw std::runtime_error("SoundEffect"); + } +} + +#endif + + +SoundEffect::SoundEffect(SoundEffect&&) noexcept = default; +SoundEffect& SoundEffect::operator= (SoundEffect&&) noexcept = default; +SoundEffect::~SoundEffect() = default; + + +// Public methods. +void SoundEffect::Play() +{ + pImpl->Play(1.f, 0.f, 0.f); +} + + +void SoundEffect::Play(float volume, float pitch, float pan) +{ + pImpl->Play(volume, pitch, pan); +} + + +std::unique_ptr SoundEffect::CreateInstance(SOUND_EFFECT_INSTANCE_FLAGS flags) +{ + auto effect = new SoundEffectInstance(pImpl->mEngine, this, flags); + assert(effect != nullptr); + pImpl->mInstances.emplace_back(effect->GetVoiceNotify()); + return std::unique_ptr(effect); +} + + +void SoundEffect::UnregisterInstance(_In_ IVoiceNotify* instance) +{ + auto it = std::find(pImpl->mInstances.begin(), pImpl->mInstances.end(), instance); + if (it == pImpl->mInstances.end()) + return; + + pImpl->mInstances.erase(it); +} + + +// Public accessors. +bool SoundEffect::IsInUse() const noexcept +{ + return (pImpl->mOneShots > 0) || !pImpl->mInstances.empty(); +} + + +size_t SoundEffect::GetSampleSizeInBytes() const noexcept +{ + return pImpl->mAudioBytes; +} + + +size_t SoundEffect::GetSampleDuration() const noexcept +{ + if (!pImpl->mWaveFormat || !pImpl->mWaveFormat->nChannels) + return 0; + + switch (GetFormatTag(pImpl->mWaveFormat)) + { + case WAVE_FORMAT_ADPCM: + { + auto adpcmFmt = reinterpret_cast(pImpl->mWaveFormat); + + uint64_t duration = uint64_t(pImpl->mAudioBytes / adpcmFmt->wfx.nBlockAlign) * adpcmFmt->wSamplesPerBlock; + const unsigned int partial = pImpl->mAudioBytes % adpcmFmt->wfx.nBlockAlign; + if (partial) + { + if (partial >= (7u * adpcmFmt->wfx.nChannels)) + duration += (uint64_t(partial) * 2 / uint64_t(adpcmFmt->wfx.nChannels - 12)); + } + return static_cast(duration); + } + + #ifdef DIRECTX_ENABLE_XWMA + + case WAVE_FORMAT_WMAUDIO2: + case WAVE_FORMAT_WMAUDIO3: + if (pImpl->mSeekTable && pImpl->mSeekCount > 0) + { + return pImpl->mSeekTable[pImpl->mSeekCount - 1] / uint32_t(2 * pImpl->mWaveFormat->nChannels); + } + break; + + #endif + + #ifdef DIRECTX_ENABLE_XMA2 + + case WAVE_FORMAT_XMA2: + return reinterpret_cast(pImpl->mWaveFormat)->SamplesEncoded; + + #endif + + default: + if (pImpl->mWaveFormat->wBitsPerSample > 0) + { + return static_cast((uint64_t(pImpl->mAudioBytes) * 8) + / (uint64_t(pImpl->mWaveFormat->wBitsPerSample) * uint64_t(pImpl->mWaveFormat->nChannels))); + } + } + + return 0; +} + + +size_t SoundEffect::GetSampleDurationMS() const noexcept +{ + if (!pImpl->mWaveFormat || !pImpl->mWaveFormat->nSamplesPerSec) + return 0; + + const uint64_t samples = GetSampleDuration(); + return static_cast((samples * 1000) / pImpl->mWaveFormat->nSamplesPerSec); +} + + +const WAVEFORMATEX* SoundEffect::GetFormat() const noexcept +{ + return pImpl->mWaveFormat; +} + + +#ifdef DIRECTX_ENABLE_XWMA + +bool SoundEffect::FillSubmitBuffer(_Out_ XAUDIO2_BUFFER& buffer, _Out_ XAUDIO2_BUFFER_WMA& wmaBuffer) const +{ + memset(&buffer, 0, sizeof(buffer)); + memset(&wmaBuffer, 0, sizeof(wmaBuffer)); + + buffer.AudioBytes = pImpl->mAudioBytes; + buffer.pAudioData = pImpl->mStartAudio; + buffer.LoopBegin = pImpl->mLoopStart; + buffer.LoopLength = pImpl->mLoopLength; + + const uint32_t tag = GetFormatTag(pImpl->mWaveFormat); + if (tag == WAVE_FORMAT_WMAUDIO2 || tag == WAVE_FORMAT_WMAUDIO3) + { + wmaBuffer.PacketCount = pImpl->mSeekCount; + wmaBuffer.pDecodedPacketCumulativeBytes = pImpl->mSeekTable; + return true; + } + + return false; +} + +#else // !xWMA + +void SoundEffect::FillSubmitBuffer(_Out_ XAUDIO2_BUFFER& buffer) const +{ + memset(&buffer, 0, sizeof(buffer)); + buffer.AudioBytes = pImpl->mAudioBytes; + buffer.pAudioData = pImpl->mStartAudio; + buffer.LoopBegin = pImpl->mLoopStart; + buffer.LoopLength = pImpl->mLoopLength; +} + +#endif + + +//-------------------------------------------------------------------------------------- +// Adapters for /Zc:wchar_t- clients +#if defined(_MSC_VER) && !defined(_NATIVE_WCHAR_T_DEFINED) + +_Use_decl_annotations_ +SoundEffect::SoundEffect(AudioEngine* engine, const __wchar_t* waveFileName) : + SoundEffect(engine, reinterpret_cast(waveFileName)) +{ +} + +#endif // !_NATIVE_WCHAR_T_DEFINED diff --git a/Common/DirectXTK12/Audio/SoundEffectInstance.cpp b/Common/DirectXTK12/Audio/SoundEffectInstance.cpp new file mode 100644 index 0000000..7022bcc --- /dev/null +++ b/Common/DirectXTK12/Audio/SoundEffectInstance.cpp @@ -0,0 +1,337 @@ +//-------------------------------------------------------------------------------------- +// File: SoundEffectInstance.cpp +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248929 +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "pch.h" +#include "SoundCommon.h" + +using namespace DirectX; + + +//====================================================================================== +// SoundEffectInstance +//====================================================================================== + +// Internal object implementation class. +class SoundEffectInstance::Impl : public IVoiceNotify +{ +public: + Impl(_In_ AudioEngine* engine, _In_ SoundEffect* effect, SOUND_EFFECT_INSTANCE_FLAGS flags) : + mBase(), + mEffect(effect), + mWaveBank(nullptr), + mIndex(0), + mLooped(false) + { + assert(engine != nullptr); + engine->RegisterNotify(this, false); + + assert(mEffect != nullptr); + mBase.Initialize(engine, effect->GetFormat(), flags); + } + + Impl(_In_ AudioEngine* engine, _In_ WaveBank* waveBank, uint32_t index, SOUND_EFFECT_INSTANCE_FLAGS flags) : + mBase(), + mEffect(nullptr), + mWaveBank(waveBank), + mIndex(index), + mLooped(false) + { + assert(engine != nullptr); + engine->RegisterNotify(this, false); + + char buff[64] = {}; + auto wfx = reinterpret_cast(buff); + assert(mWaveBank != nullptr); + mBase.Initialize(engine, mWaveBank->GetFormat(index, wfx, sizeof(buff)), flags); + } + + Impl(Impl&&) = default; + Impl& operator= (Impl&&) = default; + + Impl(Impl const&) = delete; + Impl& operator= (Impl const&) = delete; + + ~Impl() override + { + mBase.DestroyVoice(); + + if (mBase.engine) + { + mBase.engine->UnregisterNotify(this, false, false); + mBase.engine = nullptr; + } + } + + void Play(bool loop); + + // IVoiceNotify + void __cdecl OnBufferEnd() override + { + // We don't register for this notification for SoundEffectInstances, so this should not be invoked + assert(false); + } + + void __cdecl OnCriticalError() override + { + mBase.OnCriticalError(); + } + + void __cdecl OnReset() override + { + mBase.OnReset(); + } + + void __cdecl OnUpdate() override + { + // We do not register for update notification + assert(false); + } + + void __cdecl OnDestroyEngine() noexcept override + { + mBase.OnDestroy(); + } + + void __cdecl OnTrim() override + { + mBase.OnTrim(); + } + + void __cdecl GatherStatistics(AudioStatistics& stats) const noexcept override + { + mBase.GatherStatistics(stats); + } + + void __cdecl OnDestroyParent() noexcept override + { + mBase.OnDestroy(); + mWaveBank = nullptr; + mEffect = nullptr; + } + + SoundEffectInstanceBase mBase; + SoundEffect* mEffect; + WaveBank* mWaveBank; + uint32_t mIndex; + bool mLooped; +}; + + +void SoundEffectInstance::Impl::Play(bool loop) +{ + if (!mBase.voice) + { + if (mWaveBank) + { + char buff[64] = {}; + auto wfx = reinterpret_cast(buff); + mBase.AllocateVoice(mWaveBank->GetFormat(mIndex, wfx, sizeof(buff))); + } + else + { + assert(mEffect != nullptr); + mBase.AllocateVoice(mEffect->GetFormat()); + } + } + + if (!mBase.Play()) + return; + + // Submit audio data for STOPPED -> PLAYING state transition + XAUDIO2_BUFFER buffer = {}; + +#ifdef DIRECTX_ENABLE_XWMA + + bool iswma = false; + XAUDIO2_BUFFER_WMA wmaBuffer = {}; + if (mWaveBank) + { + iswma = mWaveBank->FillSubmitBuffer(mIndex, buffer, wmaBuffer); + } + else + { + assert(mEffect != nullptr); + iswma = mEffect->FillSubmitBuffer(buffer, wmaBuffer); + } + +#else // !xWMA + + if (mWaveBank) + { + mWaveBank->FillSubmitBuffer(mIndex, buffer); + } + else + { + assert(mEffect != nullptr); + mEffect->FillSubmitBuffer(buffer); + } + +#endif + + buffer.Flags = XAUDIO2_END_OF_STREAM; + if (loop) + { + mLooped = true; + buffer.LoopCount = XAUDIO2_LOOP_INFINITE; + } + else + { + mLooped = false; + buffer.LoopCount = buffer.LoopBegin = buffer.LoopLength = 0; + } + buffer.pContext = nullptr; + + HRESULT hr; +#ifdef DIRECTX_ENABLE_XWMA + if (iswma) + { + hr = mBase.voice->SubmitSourceBuffer(&buffer, &wmaBuffer); + } + else + #endif + { + hr = mBase.voice->SubmitSourceBuffer(&buffer, nullptr); + } + + if (FAILED(hr)) + { + #ifdef _DEBUG + DebugTrace("ERROR: SoundEffectInstance failed (%08X) when submitting buffer:\n", static_cast(hr)); + + char buff[64] = {}; + auto wfx = (mWaveBank) ? mWaveBank->GetFormat(mIndex, reinterpret_cast(buff), sizeof(buff)) + : mEffect->GetFormat(); + + const size_t length = (mWaveBank) ? mWaveBank->GetSampleSizeInBytes(mIndex) : mEffect->GetSampleSizeInBytes(); + + DebugTrace("\tFormat Tag %u, %u channels, %u-bit, %u Hz, %zu bytes\n", + wfx->wFormatTag, wfx->nChannels, wfx->wBitsPerSample, wfx->nSamplesPerSec, length); + #endif + mBase.Stop(true, mLooped); + throw std::runtime_error("SubmitSourceBuffer"); + } +} + + +//-------------------------------------------------------------------------------------- +// SoundEffectInstance +//-------------------------------------------------------------------------------------- + +// Private constructors +_Use_decl_annotations_ +SoundEffectInstance::SoundEffectInstance(AudioEngine* engine, SoundEffect* effect, SOUND_EFFECT_INSTANCE_FLAGS flags) : + pImpl(std::make_unique(engine, effect, flags)) +{ +} + +_Use_decl_annotations_ +SoundEffectInstance::SoundEffectInstance(AudioEngine* engine, WaveBank* waveBank, unsigned int index, SOUND_EFFECT_INSTANCE_FLAGS flags) : + pImpl(std::make_unique(engine, waveBank, index, flags)) +{ +} + + +// Move ctor/operator. +SoundEffectInstance::SoundEffectInstance(SoundEffectInstance&&) noexcept = default; +SoundEffectInstance& SoundEffectInstance::operator= (SoundEffectInstance&&) noexcept = default; + + +// Public destructor. +SoundEffectInstance::~SoundEffectInstance() +{ + if (pImpl) + { + if (pImpl->mWaveBank) + { + pImpl->mWaveBank->UnregisterInstance(pImpl.get()); + pImpl->mWaveBank = nullptr; + } + + if (pImpl->mEffect) + { + pImpl->mEffect->UnregisterInstance(pImpl.get()); + pImpl->mEffect = nullptr; + } + } +} + + +// Public methods. +void SoundEffectInstance::Play(bool loop) +{ + pImpl->Play(loop); +} + + +void SoundEffectInstance::Stop(bool immediate) noexcept +{ + pImpl->mBase.Stop(immediate, pImpl->mLooped); +} + + +void SoundEffectInstance::Pause() noexcept +{ + pImpl->mBase.Pause(); +} + + +void SoundEffectInstance::Resume() +{ + pImpl->mBase.Resume(); +} + + +void SoundEffectInstance::SetVolume(float volume) +{ + pImpl->mBase.SetVolume(volume); +} + + +void SoundEffectInstance::SetPitch(float pitch) +{ + pImpl->mBase.SetPitch(pitch); +} + + +void SoundEffectInstance::SetPan(float pan) +{ + pImpl->mBase.SetPan(pan); +} + + +void SoundEffectInstance::Apply3D(const X3DAUDIO_LISTENER& listener, const X3DAUDIO_EMITTER& emitter, bool rhcoords) +{ + pImpl->mBase.Apply3D(listener, emitter, rhcoords); +} + + +// Public accessors. +bool SoundEffectInstance::IsLooped() const noexcept +{ + return pImpl->mLooped; +} + + +SoundState SoundEffectInstance::GetState() noexcept +{ + return pImpl->mBase.GetState(true); +} + + +unsigned int SoundEffectInstance::GetChannelCount() const noexcept +{ + return pImpl->mBase.GetChannelCount(); +} + + +IVoiceNotify* SoundEffectInstance::GetVoiceNotify() const noexcept +{ + return pImpl.get(); +} diff --git a/Common/DirectXTK12/Audio/SoundStreamInstance.cpp b/Common/DirectXTK12/Audio/SoundStreamInstance.cpp new file mode 100644 index 0000000..aef9a83 --- /dev/null +++ b/Common/DirectXTK12/Audio/SoundStreamInstance.cpp @@ -0,0 +1,860 @@ +//-------------------------------------------------------------------------------------- +// File: SoundStreamInstance.cpp +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248929 +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "pch.h" +#include "DirectXHelpers.h" +#include "WaveBankReader.h" +#include "PlatformHelpers.h" +#include "SoundCommon.h" + +#if (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wnonportable-system-include-path" +#endif + +#include +#include +#endif + +using namespace DirectX; + +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wcovered-switch-default" +#endif + +#pragma warning(disable : 4061 4062) + +//#define VERBOSE_TRACE + +#ifdef VERBOSE_TRACE +#pragma message("NOTE: Verbose tracing enabled") +#endif + +namespace +{ + constexpr size_t DVD_SECTOR_SIZE = 2048; + constexpr size_t ADVANCED_FORMAT_SECTOR_SIZE = 4096; + constexpr size_t MAX_BUFFER_COUNT = 3; + +#ifdef DIRECTX_ENABLE_SEEK_TABLES + constexpr size_t MAX_STREAMING_SEEK_PACKETS = 2048; +#endif + +#ifdef DIRECTX_ENABLE_XMA2 + constexpr size_t XMA2_64KBLOCKINBYTES = 65536; + + struct apu_deleter { void operator()(void* p) noexcept { if (p) ApuFree(p); } }; +#endif + + size_t ComputeAsyncPacketSize(_In_ const WAVEFORMATEX* wfx, uint32_t tag, uint32_t alignment) + { + if (!wfx) + return 0; + + size_t buffer = size_t(wfx->nAvgBytesPerSec) * 2u; + + #ifdef DIRECTX_ENABLE_XMA2 + if (tag == WAVE_FORMAT_XMA2) + { + buffer = AlignUp(buffer, XMA2_64KBLOCKINBYTES); + buffer = std::max(XMA2_64KBLOCKINBYTES, buffer); + return buffer; + } + #else + UNREFERENCED_PARAMETER(tag); + #endif + + buffer = AlignUp(buffer, size_t(alignment) * 2); + buffer = std::max(65536u, buffer); + return buffer; + } +} + + +//====================================================================================== +// SoundStreamInstance +//====================================================================================== + +// Internal object implementation class. +class SoundStreamInstance::Impl : public IVoiceNotify +{ +public: + Impl(_In_ AudioEngine* engine, + WaveBank* waveBank, + uint32_t index, + SOUND_EFFECT_INSTANCE_FLAGS flags) noexcept(false) : + mBase(), + mWaveBank(waveBank), + mIndex(index), + mPlaying(false), + mLooped(false), + mEndStream(false), + mPrefetch(false), + mSitching(false), + mPackets{}, + mCurrentDiskReadBuffer(0), + mCurrentPlayBuffer(0), + mBlockAlign(0), + mAsyncAlign(DVD_SECTOR_SIZE), + mCurrentPosition(0), + mOffsetBytes(0), + mLengthInBytes(0), + mPacketSize(0), + mTotalSize(0) + #ifdef DIRECTX_ENABLE_SEEK_TABLES + , mSeekCount(0), + mSeekTable(nullptr), + mSeekTableCopy{} + #endif + { + assert(engine != nullptr); + engine->RegisterNotify(this, true); + + char buff[64] = {}; + auto wfx = reinterpret_cast(buff); + assert(mWaveBank != nullptr); + mBase.Initialize(engine, mWaveBank->GetFormat(index, wfx, sizeof(buff)), flags); + + WaveBankReader::Metadata metadata = {}; + std::ignore = mWaveBank->GetPrivateData(index, &metadata, sizeof(metadata)); + + mOffsetBytes = metadata.offsetBytes; + mLengthInBytes = metadata.lengthBytes; + mAsyncAlign = mWaveBank->IsAdvancedFormat() ? ADVANCED_FORMAT_SECTOR_SIZE : DVD_SECTOR_SIZE; + + #ifdef DIRECTX_ENABLE_SEEK_TABLES + WaveBankSeekData seekData = {}; + std::ignore = mWaveBank->GetPrivateData(index, &seekData, sizeof(seekData)); + if (seekData.tag == WAVE_FORMAT_WMAUDIO2 || seekData.tag == WAVE_FORMAT_WMAUDIO3) + { + mSeekCount = seekData.seekCount; + mSeekTable = seekData.seekTable; + } + #endif + + mBufferEnd.reset(CreateEventEx(nullptr, nullptr, 0, EVENT_MODIFY_STATE | SYNCHRONIZE)); + mBufferRead.reset(CreateEventEx(nullptr, nullptr, 0, EVENT_MODIFY_STATE | SYNCHRONIZE)); + if (!mBufferEnd || !mBufferRead) + { + throw std::system_error(std::error_code(static_cast(GetLastError()), std::system_category()), "CreateEventEx"); + } + + ThrowIfFailed(AllocateStreamingBuffers(wfx)); + + #ifdef VERBOSE_TRACE + DebugTrace("INFO (Streaming): packet size %zu, play length %zu\n", mPacketSize, mLengthInBytes); + #endif + + mPrefetch = true; + ThrowIfFailed(ReadBuffers()); + } + + virtual ~Impl() override + { + mBase.DestroyVoice(); + + if (mWaveBank && mWaveBank->GetAsyncHandle()) + { + for (size_t j = 0; j < MAX_BUFFER_COUNT; ++j) + { + std::ignore = CancelIoEx(mWaveBank->GetAsyncHandle(), &mPackets[j].request); + } + } + + if (mBase.engine) + { + mBase.engine->UnregisterNotify(this, false, true); + mBase.engine = nullptr; + } + + for (size_t j = 0; j < MAX_BUFFER_COUNT; ++j) + { + mPackets[j] = {}; + } + mPacketSize = 0; + } + + Impl(Impl&&) = default; + Impl& operator= (Impl&&) = default; + + Impl(Impl const&) = delete; + Impl& operator= (Impl const&) = delete; + + void Play(bool loop) + { + if (!mBase.voice) + { + if (!mWaveBank) + return; + + char buff[64] = {}; + auto wfx = reinterpret_cast(buff); + mBase.AllocateVoice(mWaveBank->GetFormat(mIndex, wfx, sizeof(buff))); + } + + if (!mBase.Play()) + return; + + mLooped = loop; + mEndStream = false; + + if (!mPrefetch) + { + mCurrentPosition = 0; + } + + ThrowIfFailed(PlayBuffers()); + } + + // IVoiceNotify + virtual void __cdecl OnBufferEnd() override + { + // Not used + } + + virtual void __cdecl OnCriticalError() override + { + mBase.OnCriticalError(); + } + + virtual void __cdecl OnReset() override + { + mBase.OnReset(); + } + + virtual void __cdecl OnUpdate() override + { + if (!mPlaying) + return; + + HANDLE events[] = { mBufferRead.get(), mBufferEnd.get() }; + switch (WaitForMultipleObjectsEx(static_cast(std::size(events)), events, FALSE, 0, FALSE)) + { + case WAIT_TIMEOUT: + break; + + case WAIT_OBJECT_0: // Read completed + #ifdef VERBOSE_TRACE + DebugTrace("INFO (Streaming): Playing... (readpos %zu) [", mCurrentPosition); + for (uint32_t k = 0; k < MAX_BUFFER_COUNT; ++k) + { + DebugTrace("%ls ", s_debugState[static_cast(mPackets[k].state)]); + } + DebugTrace("]\n"); + #endif + mPrefetch = false; + ThrowIfFailed(PlayBuffers()); + break; + + case (WAIT_OBJECT_0 + 1): // Play completed + #ifdef VERBOSE_TRACE + DebugTrace("INFO (Streaming): Reading... (readpos %zu) [", mCurrentPosition); + for (uint32_t k = 0; k < MAX_BUFFER_COUNT; ++k) + { + DebugTrace("%ls ", s_debugState[static_cast(mPackets[k].state)]); + } + DebugTrace("]\n"); + #endif + ThrowIfFailed(ReadBuffers()); + break; + + case WAIT_FAILED: + throw std::system_error(std::error_code(static_cast(GetLastError()), std::system_category()), "WaitForMultipleObjectsEx"); + } + } + + virtual void __cdecl OnDestroyEngine() noexcept override + { + mBase.OnDestroy(); + } + + virtual void __cdecl OnTrim() override + { + mBase.OnTrim(); + } + + virtual void __cdecl GatherStatistics(AudioStatistics& stats) const noexcept override + { + mBase.GatherStatistics(stats); + + stats.streamingBytes += mPacketSize * MAX_BUFFER_COUNT; + } + + virtual void __cdecl OnDestroyParent() noexcept override + { + mBase.OnDestroy(); + mWaveBank = nullptr; + } + + SoundEffectInstanceBase mBase; + WaveBank* mWaveBank; + uint32_t mIndex; + bool mPlaying; + bool mLooped; + bool mEndStream; + bool mPrefetch; + bool mSitching; + + ScopedHandle mBufferEnd; + ScopedHandle mBufferRead; + + enum class State : uint32_t + { + FREE = 0, + PENDING, + READY, + PLAYING, + }; + +#ifdef VERBOSE_TRACE + static const wchar_t* s_debugState[4]; +#endif + + struct BufferNotify : public IVoiceNotify + { + BufferNotify() : mParent(nullptr), mIndex(0) {} + + void Set(SoundStreamInstance::Impl* parent, size_t index) noexcept(true) { mParent = parent; mIndex = index; } + + void __cdecl OnBufferEnd() override + { + assert(mParent != nullptr); + mParent->mPackets[mIndex].state = State::FREE; + SetEvent(mParent->mBufferEnd.get()); + } + + void __cdecl OnCriticalError() override { assert(mParent != nullptr); mParent->OnCriticalError(); } + void __cdecl OnReset() override { assert(mParent != nullptr); mParent->OnReset(); } + void __cdecl OnUpdate() override { assert(mParent != nullptr); mParent->OnUpdate(); } + void __cdecl OnDestroyEngine() noexcept override { assert(mParent != nullptr); mParent->OnDestroyEngine(); } + void __cdecl OnTrim() override { assert(mParent != nullptr); mParent->OnTrim(); } + void __cdecl GatherStatistics(AudioStatistics& stats) const override { assert(mParent != nullptr); mParent->GatherStatistics(stats); } + void __cdecl OnDestroyParent() noexcept override { assert(mParent != nullptr); mParent->OnDestroyParent(); } + + private: + SoundStreamInstance::Impl* mParent; + size_t mIndex; + }; + + struct Packets + { + State state; + uint8_t* buffer; + uint8_t* stitchBuffer; + uint32_t valid; + uint32_t audioBytes; + uint32_t startPosition; + OVERLAPPED request; + BufferNotify notify; + + Packets() : + state(State::FREE), + buffer(nullptr), + stitchBuffer(nullptr), + valid(0), + audioBytes(0), + startPosition(0), + request{}, + notify{} {} + }; + + Packets mPackets[MAX_BUFFER_COUNT]; + +private: + uint32_t mCurrentDiskReadBuffer; + uint32_t mCurrentPlayBuffer; + uint32_t mBlockAlign; + uint32_t mAsyncAlign; + size_t mCurrentPosition; + size_t mOffsetBytes; + size_t mLengthInBytes; + + size_t mPacketSize; + size_t mTotalSize; + std::unique_ptr mStreamBuffer; + +#ifdef DIRECTX_ENABLE_SEEK_TABLES + uint32_t mSeekCount; + const uint32_t* mSeekTable; + uint32_t mSeekTableCopy[MAX_STREAMING_SEEK_PACKETS]; +#endif + +#ifdef DIRECTX_ENABLE_XMA2 + std::unique_ptr mXMAMemory; +#endif + + HRESULT AllocateStreamingBuffers(const WAVEFORMATEX* wfx) noexcept; + HRESULT ReadBuffers() noexcept; + HRESULT PlayBuffers() noexcept; +}; + + +HRESULT SoundStreamInstance::Impl::AllocateStreamingBuffers(const WAVEFORMATEX* wfx) noexcept +{ + if (!wfx) + return E_INVALIDARG; + + const uint32_t tag = GetFormatTag(wfx); + + size_t packetSize = ComputeAsyncPacketSize(wfx, tag, mAsyncAlign); + if (!packetSize) + return E_UNEXPECTED; + + uint64_t totalSize = uint64_t(packetSize) * uint64_t(MAX_BUFFER_COUNT); + if (totalSize > UINT32_MAX) + return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); + + mPacketSize = packetSize; + mBlockAlign = wfx->nBlockAlign; + mSitching = false; + + size_t stitchSize = 0; + if ((packetSize % wfx->nBlockAlign) != 0) + { + mSitching = true; + + stitchSize = AlignUp(wfx->nBlockAlign, mAsyncAlign); + totalSize += uint64_t(stitchSize) * uint64_t(MAX_BUFFER_COUNT); + if (totalSize > UINT32_MAX) + return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); + } + +#ifdef DIRECTX_ENABLE_XMA2 + if ((mTotalSize < totalSize) || (tag == WAVE_FORMAT_XMA2 && !mXMAMemory) || (tag != WAVE_FORMAT_XMA2 && !mStreamBuffer)) + #else + if (mTotalSize < totalSize) + #endif + { + mStreamBuffer.reset(); + #ifdef DIRECTX_ENABLE_XMA2 + mXMAMemory.reset(); + if (tag == WAVE_FORMAT_XMA2) + { + void* xmaMemory = nullptr; + HRESULT hr = ApuAlloc(&xmaMemory, nullptr, static_cast(totalSize), SHAPE_XMA_INPUT_BUFFER_ALIGNMENT); + if (FAILED(hr)) + { + DebugTrace("ERROR: ApuAlloc failed (%llu bytes). Did you allocate a large enough heap with ApuCreateHeap for all your XMA wave data?\n", totalSize); + return hr; + } + mXMAMemory.reset(static_cast(xmaMemory)); + } + else + #endif + { + mStreamBuffer.reset(reinterpret_cast( + VirtualAlloc(nullptr, static_cast(totalSize), MEM_COMMIT, PAGE_READWRITE) + )); + + if (!mStreamBuffer) + { + DebugTrace("ERROR: Failed allocating %llu bytes for SoundStreamInstance\n", totalSize); + mPacketSize = 0; + totalSize = 0; + return E_OUTOFMEMORY; + } + } + + mTotalSize = static_cast(totalSize); + + #ifdef DIRECTX_ENABLE_XMA2 + uint8_t* ptr = (tag == WAVE_FORMAT_XMA2) ? mXMAMemory.get() : mStreamBuffer.get(); + #else + uint8_t* ptr = mStreamBuffer.get(); + #endif + for (size_t j = 0; j < MAX_BUFFER_COUNT; ++j) + { + mPackets[j].buffer = ptr; + mPackets[j].stitchBuffer = nullptr; + mPackets[j].request.hEvent = mBufferRead.get(); + mPackets[j].notify.Set(this, j); + ptr += packetSize; + } + + if (stitchSize > 0) + { + for (size_t j = 0; j < MAX_BUFFER_COUNT; ++j) + { + mPackets[j].stitchBuffer = ptr; + ptr += stitchSize; + } + } + } + + return S_OK; +} + + +HRESULT SoundStreamInstance::Impl::ReadBuffers() noexcept +{ + if (mCurrentPosition >= mLengthInBytes) + { + if (!mLooped) + { + mEndStream = true; + return S_FALSE; + } + + #ifdef VERBOSE_TRACE + DebugTrace("INFO (Streaming): Loop restart\n"); + #endif + + mCurrentPosition = 0; + } + + HANDLE async = mWaveBank->GetAsyncHandle(); + if (!async) + return E_POINTER; + + const uint32_t readBuffer = mCurrentDiskReadBuffer; + for (uint32_t j = 0; j < MAX_BUFFER_COUNT; ++j) + { + uint32_t entry = (j + readBuffer) % uint32_t(MAX_BUFFER_COUNT); + if (mPackets[entry].state == State::FREE) + { + if (mCurrentPosition < mLengthInBytes) + { + auto const cbValid = static_cast(std::min(mPacketSize, mLengthInBytes - mCurrentPosition)); + + mPackets[entry].valid = cbValid; + mPackets[entry].audioBytes = 0; + mPackets[entry].startPosition = static_cast(mCurrentPosition); + mPackets[entry].request.Offset = static_cast(mOffsetBytes + mCurrentPosition); + + if (!ReadFile(async, mPackets[entry].buffer, uint32_t(mPacketSize), nullptr, &mPackets[entry].request)) + { + const DWORD error = GetLastError(); + if (error != ERROR_IO_PENDING) + { + #ifdef _DEBUG + if (error == ERROR_INVALID_PARAMETER) + { + // May be due to Advanced Format (4Kn) vs. DVD sector size. See the xwbtool -af switch. + OutputDebugStringA("ERROR: non-buffered async I/O failed: check disk sector size vs. streaming wave bank alignment!\n"); + } + #endif + return HRESULT_FROM_WIN32(error); + } + } + + mCurrentPosition += cbValid; + + mCurrentDiskReadBuffer = (entry + 1) % uint32_t(MAX_BUFFER_COUNT); + + mPackets[entry].state = State::PENDING; + + if ((cbValid < mPacketSize) && mLooped) + { + #ifdef VERBOSE_TRACE + DebugTrace("INFO (Streaming): Loop restart\n"); + #endif + mCurrentPosition = 0; + } + } + } + } + + return S_OK; +} + + +HRESULT SoundStreamInstance::Impl::PlayBuffers() noexcept +{ + HANDLE async = mWaveBank->GetAsyncHandle(); + if (!async) + return E_POINTER; + + for (uint32_t j = 0; j < MAX_BUFFER_COUNT; ++j) + { + if (mPackets[j].state == State::PENDING) + { + DWORD cb = 0; + #if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) + const BOOL result = GetOverlappedResultEx(async, &mPackets[j].request, &cb, 0, FALSE); + #else + const BOOL result = GetOverlappedResult(async, &mPackets[j].request, &cb, FALSE); + #endif + if (result) + { + mPackets[j].state = State::READY; + } + else + { + const DWORD error = GetLastError(); + if (error != ERROR_IO_INCOMPLETE) + { + ThrowIfFailed(HRESULT_FROM_WIN32(error)); + } + } + } + } + + if (!mBase.voice || !mPlaying) + return S_FALSE; + + for (uint32_t j = 0; j < MAX_BUFFER_COUNT; ++j) + { + if (mPackets[mCurrentPlayBuffer].state != State::READY) + break; + + const uint8_t* ptr = mPackets[mCurrentPlayBuffer].buffer; + uint32_t valid = mPackets[mCurrentPlayBuffer].valid; + + bool endstream = false; + if (valid < mPacketSize) + { + endstream = true; + #ifdef VERBOSE_TRACE + DebugTrace("INFO (Streaming): End of stream (%u of %zu bytes)\n", mPackets[mCurrentPlayBuffer].valid, mPacketSize); + #endif + } + + uint32_t thisFrameStitch = 0; + if (mSitching) + { + // Compute how many left-over bytes at the end of the previous packet (if any, they form the head of a partial block). + uint32_t prevFrameStitch = (mPackets[mCurrentPlayBuffer].startPosition % mBlockAlign); + + if (prevFrameStitch > 0) + { + auto buffer = mPackets[mCurrentPlayBuffer].stitchBuffer; + + // Compute how many bytes at the start of our current packet are the tail of the partial block. + thisFrameStitch = mBlockAlign - prevFrameStitch; + + const uint32_t k = (mCurrentPlayBuffer + MAX_BUFFER_COUNT - 1) % MAX_BUFFER_COUNT; + if (mPackets[k].state == State::READY || mPackets[k].state == State::PLAYING) + { + // Compute how many bytes at the start of the previous packet were the tail of the previous stitch block. + uint32_t prevFrameStitchOffset = (mPackets[k].startPosition % mBlockAlign); + prevFrameStitchOffset = (prevFrameStitchOffset > 0) ? (mBlockAlign - prevFrameStitchOffset) : 0u; + + // Point to the start of the partial block's head in the previous packet. + const auto *prevBuffer = mPackets[k].buffer + prevFrameStitchOffset + mPackets[k].audioBytes; + + // Merge the the head partial block in the previous packet with the tail partial block at the start of our packet. + memcpy(buffer, prevBuffer, prevFrameStitch); + memcpy(buffer + prevFrameStitch, ptr, thisFrameStitch); + + // Submit stitch packet (only need to get notified if we aren't submitting another packet for this buffer). + XAUDIO2_BUFFER buf = {}; + buf.AudioBytes = mBlockAlign; + buf.pAudioData = buffer; + + if (endstream && (valid <= thisFrameStitch)) + { + buf.Flags = XAUDIO2_END_OF_STREAM; + buf.pContext = &mPackets[mCurrentPlayBuffer].notify; + } + #ifdef VERBOSE_TRACE + DebugTrace("INFO (Streaming): Stitch packet (%u + %u = %u)\n", prevFrameStitch, thisFrameStitch, mBlockAlign); + #endif + #ifdef DIRECTX_ENABLE_XWMA + if (mSeekCount > 0) + { + XAUDIO2_BUFFER_WMA wmaBuf = {}; + wmaBuf.pDecodedPacketCumulativeBytes = mSeekTableCopy; + wmaBuf.PacketCount = 1; + + const uint32_t seekOffset = (mPackets[k].startPosition + prevFrameStitchOffset + mPackets[k].audioBytes) / mBlockAlign; + assert(seekOffset > 0); + mSeekTableCopy[0] = mSeekTable[seekOffset] - mSeekTable[seekOffset - 1]; + + ThrowIfFailed(mBase.voice->SubmitSourceBuffer(&buf, &wmaBuf)); + } + else + #endif // XWMA + { + ThrowIfFailed(mBase.voice->SubmitSourceBuffer(&buf)); + } + } + + ptr += thisFrameStitch; + } + + // Compute valid audio bytes in our current packet. + valid = ((valid - thisFrameStitch) / mBlockAlign) * mBlockAlign; + } + + if (valid > 0) + { + // Record the audioBytes we actually submitted... + mPackets[mCurrentPlayBuffer].audioBytes = valid; + + XAUDIO2_BUFFER buf = {}; + buf.Flags = (endstream) ? XAUDIO2_END_OF_STREAM : 0; + buf.AudioBytes = valid; + buf.pAudioData = ptr; + buf.pContext = &mPackets[mCurrentPlayBuffer].notify; + + #ifdef DIRECTX_ENABLE_XWMA + if (mSeekCount > 0) + { + XAUDIO2_BUFFER_WMA wmaBuf = {}; + + wmaBuf.PacketCount = valid / mBlockAlign; + + const uint32_t seekOffset = mPackets[mCurrentPlayBuffer].startPosition / mBlockAlign; + if (seekOffset > MAX_STREAMING_SEEK_PACKETS) + { + DebugTrace("ERROR: xWMA packet seek count exceeds %zu\n", MAX_STREAMING_SEEK_PACKETS); + return E_FAIL; + } + else if (seekOffset > 0) + { + for (uint32_t i = 0; i < wmaBuf.PacketCount; ++i) + { + mSeekTableCopy[i] = mSeekTable[i + seekOffset] - mSeekTable[seekOffset - 1]; + } + + wmaBuf.pDecodedPacketCumulativeBytes = mSeekTableCopy; + } + else + { + wmaBuf.pDecodedPacketCumulativeBytes = mSeekTable; + } + + ThrowIfFailed(mBase.voice->SubmitSourceBuffer(&buf, &wmaBuf)); + } + else + #endif // xWMA + { + ThrowIfFailed(mBase.voice->SubmitSourceBuffer(&buf)); + } + } + + mPackets[mCurrentPlayBuffer].state = State::PLAYING; + mCurrentPlayBuffer = (mCurrentPlayBuffer + 1) % uint32_t(MAX_BUFFER_COUNT); + } + + return S_OK; +} + +#ifdef VERBOSE_TRACE +const wchar_t* SoundStreamInstance::Impl::s_debugState[4] = +{ + L"FREE", + L"PENDING", + L"READY", + L"PLAYING" +}; +#endif + + +//-------------------------------------------------------------------------------------- +// SoundStreamInstance +//-------------------------------------------------------------------------------------- + +// Private constructors +_Use_decl_annotations_ +SoundStreamInstance::SoundStreamInstance(AudioEngine* engine, WaveBank* waveBank, unsigned int index, SOUND_EFFECT_INSTANCE_FLAGS flags) : + pImpl(std::make_unique(engine, waveBank, index, flags)) +{ +} + + +// Move ctor/operator. +SoundStreamInstance::SoundStreamInstance(SoundStreamInstance&&) noexcept = default; +SoundStreamInstance& SoundStreamInstance::operator= (SoundStreamInstance&&) noexcept = default; + + +// Public destructor. +SoundStreamInstance::~SoundStreamInstance() +{ + if (pImpl) + { + if (pImpl->mWaveBank) + { + pImpl->mWaveBank->UnregisterInstance(pImpl.get()); + pImpl->mWaveBank = nullptr; + } + } +} + + +// Public methods. +void SoundStreamInstance::Play(bool loop) +{ + pImpl->Play(loop); + pImpl->mPlaying = true; +} + + +void SoundStreamInstance::Stop(bool immediate) noexcept +{ + pImpl->mBase.Stop(immediate, pImpl->mLooped); + pImpl->mPlaying = !immediate; +} + + +void SoundStreamInstance::Pause() noexcept +{ + pImpl->mBase.Pause(); +} + + +void SoundStreamInstance::Resume() +{ + pImpl->mBase.Resume(); +} + + +void SoundStreamInstance::SetVolume(float volume) +{ + pImpl->mBase.SetVolume(volume); +} + + +void SoundStreamInstance::SetPitch(float pitch) +{ + pImpl->mBase.SetPitch(pitch); +} + + +void SoundStreamInstance::SetPan(float pan) +{ + pImpl->mBase.SetPan(pan); +} + + +void SoundStreamInstance::Apply3D(const X3DAUDIO_LISTENER& listener, const X3DAUDIO_EMITTER& emitter, bool rhcoords) +{ + pImpl->mBase.Apply3D(listener, emitter, rhcoords); +} + + +// Public accessors. +bool SoundStreamInstance::IsLooped() const noexcept +{ + return pImpl->mLooped; +} + + +SoundState SoundStreamInstance::GetState() noexcept +{ + const SoundState state = pImpl->mBase.GetState(pImpl->mEndStream); + if (state == STOPPED) + { + pImpl->mPlaying = false; + } + return state; +} + + +unsigned int SoundStreamInstance::GetChannelCount() const noexcept +{ + return pImpl->mBase.GetChannelCount(); +} + + +IVoiceNotify* SoundStreamInstance::GetVoiceNotify() const noexcept +{ + return pImpl.get(); +} diff --git a/Common/DirectXTK12/Audio/WAVFileReader.cpp b/Common/DirectXTK12/Audio/WAVFileReader.cpp new file mode 100644 index 0000000..e05c7b4 --- /dev/null +++ b/Common/DirectXTK12/Audio/WAVFileReader.cpp @@ -0,0 +1,749 @@ +//-------------------------------------------------------------------------------------- +// File: WAVFileReader.cpp +// +// Functions for loading WAV audio files +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248929 +// http://go.microsoft.com/fwlink/?LinkID=615561 +//------------------------------------------------------------------------------------- + +#include "pch.h" +#include "PlatformHelpers.h" +#include "WAVFileReader.h" + +using namespace DirectX; + + +namespace +{ + //--------------------------------------------------------------------------------- + // .WAV files + //--------------------------------------------------------------------------------- + constexpr uint32_t FOURCC_RIFF_TAG = MAKEFOURCC('R', 'I', 'F', 'F'); + constexpr uint32_t FOURCC_FORMAT_TAG = MAKEFOURCC('f', 'm', 't', ' '); + constexpr uint32_t FOURCC_DATA_TAG = MAKEFOURCC('d', 'a', 't', 'a'); + constexpr uint32_t FOURCC_WAVE_FILE_TAG = MAKEFOURCC('W', 'A', 'V', 'E'); + constexpr uint32_t FOURCC_XWMA_FILE_TAG = MAKEFOURCC('X', 'W', 'M', 'A'); + constexpr uint32_t FOURCC_DLS_SAMPLE = MAKEFOURCC('w', 's', 'm', 'p'); + constexpr uint32_t FOURCC_MIDI_SAMPLE = MAKEFOURCC('s', 'm', 'p', 'l'); + constexpr uint32_t FOURCC_XWMA_DPDS = MAKEFOURCC('d', 'p', 'd', 's'); + constexpr uint32_t FOURCC_XMA_SEEK = MAKEFOURCC('s', 'e', 'e', 'k'); + + constexpr size_t SIZEOF_XMA2WAVEFORMATEX = 52; + + constexpr uint16_t MSADPCM_FORMAT_EXTRA_BYTES = 32; + +#pragma pack(push,1) + struct RIFFChunk + { + uint32_t tag; + uint32_t size; + }; + + struct RIFFChunkHeader + { + uint32_t tag; + uint32_t size; + uint32_t riff; + }; + + struct DLSLoop + { + static constexpr uint32_t LOOP_TYPE_FORWARD = 0x00000000; + static constexpr uint32_t LOOP_TYPE_RELEASE = 0x00000001; + + uint32_t size; + uint32_t loopType; + uint32_t loopStart; + uint32_t loopLength; + }; + + struct RIFFDLSSample + { + static constexpr uint32_t OPTIONS_NOTRUNCATION = 0x00000001; + static constexpr uint32_t OPTIONS_NOCOMPRESSION = 0x00000002; + + uint32_t size; + uint16_t unityNote; + int16_t fineTune; + int32_t gain; + uint32_t options; + uint32_t loopCount; + }; + + struct MIDILoop + { + static constexpr uint32_t LOOP_TYPE_FORWARD = 0x00000000; + static constexpr uint32_t LOOP_TYPE_ALTERNATING = 0x00000001; + static constexpr uint32_t LOOP_TYPE_BACKWARD = 0x00000002; + + uint32_t cuePointId; + uint32_t type; + uint32_t start; + uint32_t end; + uint32_t fraction; + uint32_t playCount; + }; + + struct RIFFMIDISample + { + uint32_t manufacturerId; + uint32_t productId; + uint32_t samplePeriod; + uint32_t unityNode; + uint32_t pitchFraction; + uint32_t SMPTEFormat; + uint32_t SMPTEOffset; + uint32_t loopCount; + uint32_t samplerData; + }; +#pragma pack(pop) + + static_assert(sizeof(RIFFChunk) == 8, "structure size mismatch"); + static_assert(sizeof(RIFFChunkHeader) == 12, "structure size mismatch"); + static_assert(sizeof(DLSLoop) == 16, "structure size mismatch"); + static_assert(sizeof(RIFFDLSSample) == 20, "structure size mismatch"); + static_assert(sizeof(MIDILoop) == 24, "structure size mismatch"); + static_assert(sizeof(RIFFMIDISample) == 36, "structure size mismatch"); + + //--------------------------------------------------------------------------------- + const RIFFChunk* FindChunk( + _In_reads_bytes_(sizeBytes) const uint8_t* data, + _In_ size_t sizeBytes, + _In_ const uint8_t* upperBound, + _In_ uint32_t tag) noexcept + { + if (!data) + return nullptr; + + if (sizeBytes < sizeof(RIFFChunk)) + return nullptr; + + const uint8_t* ptr = data; + const uint8_t* end = data + sizeBytes; + + uint64_t current = 0; + + while (end > (ptr + sizeof(RIFFChunk))) + { + if ((current + sizeof(RIFFChunk)) >= sizeBytes) + return nullptr; + + auto header = reinterpret_cast(ptr); + if (header->tag == tag) + return header; + + const uint64_t offset = static_cast(header->size) + sizeof(RIFFChunk); + current += offset; + if (current >= sizeBytes) + return nullptr; + + ptr += static_cast(offset); + + if (ptr >= upperBound) + return nullptr; + } + + return nullptr; + } + + + //--------------------------------------------------------------------------------- + HRESULT WaveFindFormatAndData( + _In_reads_bytes_(wavDataSize) const uint8_t* wavData, + _In_ size_t wavDataSize, + _Outptr_ const WAVEFORMATEX** pwfx, + _Outptr_ const uint8_t** pdata, + _Out_ uint32_t* dataSize, + _Out_ bool& dpds, + _Out_ bool& seek) noexcept + { + if (!wavData || !pwfx) + return E_POINTER; + + dpds = seek = false; + + if (wavDataSize < (sizeof(RIFFChunk) * 2 + sizeof(uint32_t) + sizeof(WAVEFORMAT))) + { + return E_FAIL; + } + + const uint8_t* wavEnd = wavData + wavDataSize; + + // Locate RIFF 'WAVE' + auto riffChunk = FindChunk(wavData, wavDataSize, wavEnd, FOURCC_RIFF_TAG); + if (!riffChunk || riffChunk->size < 4) + { + return E_FAIL; + } + + if ((reinterpret_cast(riffChunk) + sizeof(RIFFChunkHeader)) > wavEnd) + { + return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF); + } + + auto riffHeader = reinterpret_cast(riffChunk); + if (riffHeader->riff != FOURCC_WAVE_FILE_TAG && riffHeader->riff != FOURCC_XWMA_FILE_TAG) + { + return E_FAIL; + } + + // Locate 'fmt ' + auto ptr = reinterpret_cast(riffHeader) + sizeof(RIFFChunkHeader); + + auto fmtChunk = FindChunk(ptr, riffHeader->size, wavEnd, FOURCC_FORMAT_TAG); + if (!fmtChunk || fmtChunk->size < sizeof(PCMWAVEFORMAT)) + { + return E_FAIL; + } + + if ((reinterpret_cast(fmtChunk) + sizeof(RIFFChunk) + sizeof(PCMWAVEFORMAT)) > wavEnd) + { + return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF); + } + + ptr = reinterpret_cast(fmtChunk) + sizeof(RIFFChunk); + if (ptr + fmtChunk->size > wavEnd) + { + return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF); + } + + auto wf = reinterpret_cast(ptr); + + // Validate WAVEFORMAT (focused on chunk size and format tag, not other data that XAUDIO2 will validate) + switch (wf->wFormatTag) + { + case WAVE_FORMAT_PCM: + case WAVE_FORMAT_IEEE_FLOAT: + // Can be a PCMWAVEFORMAT (16 bytes) or WAVEFORMATEX (18 bytes) + // We validiated chunk as at least sizeof(PCMWAVEFORMAT) above + break; + + default: + { + if (fmtChunk->size < sizeof(WAVEFORMATEX)) + { + return E_FAIL; + } + + if ((ptr + sizeof(WAVEFORMATEX)) > wavEnd) + { + return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF); + } + + auto wfx = reinterpret_cast(ptr); + + if (fmtChunk->size < (sizeof(WAVEFORMATEX) + wfx->cbSize)) + { + return E_FAIL; + } + + if ((ptr + (sizeof(WAVEFORMATEX) + wfx->cbSize)) > wavEnd) + { + return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF); + } + + switch (wfx->wFormatTag) + { + case WAVE_FORMAT_WMAUDIO2: + case WAVE_FORMAT_WMAUDIO3: + dpds = true; + break; + + case 0x166 /*WAVE_FORMAT_XMA2*/: // XMA2 is supported by Xbox One & Xbox Series X|S + if ((fmtChunk->size < SIZEOF_XMA2WAVEFORMATEX) || (wfx->cbSize < (SIZEOF_XMA2WAVEFORMATEX - sizeof(WAVEFORMATEX)))) + { + return E_FAIL; + } + + if ((ptr + SIZEOF_XMA2WAVEFORMATEX) > wavEnd) + { + return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF); + } + + seek = true; + break; + + case WAVE_FORMAT_ADPCM: + if ((fmtChunk->size < (sizeof(WAVEFORMATEX) + MSADPCM_FORMAT_EXTRA_BYTES)) || (wfx->cbSize < MSADPCM_FORMAT_EXTRA_BYTES)) + { + return E_FAIL; + } + + if ((ptr + sizeof(WAVEFORMATEX) + MSADPCM_FORMAT_EXTRA_BYTES) > wavEnd) + { + return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF); + } + break; + + case WAVE_FORMAT_EXTENSIBLE: + if ((fmtChunk->size < sizeof(WAVEFORMATEXTENSIBLE)) || (wfx->cbSize < (sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)))) + { + return E_FAIL; + } + else + { + static const GUID s_wfexBase = { 0x00000000, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 } }; + + if ((ptr + sizeof(WAVEFORMATEXTENSIBLE)) > wavEnd) + { + return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF); + } + + auto wfex = reinterpret_cast(ptr); + + if (memcmp(reinterpret_cast(&wfex->SubFormat) + sizeof(DWORD), + reinterpret_cast(&s_wfexBase) + sizeof(DWORD), sizeof(GUID) - sizeof(DWORD)) != 0) + { + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + + switch (wfex->SubFormat.Data1) + { + case WAVE_FORMAT_PCM: + case WAVE_FORMAT_IEEE_FLOAT: + break; + + // MS-ADPCM and XMA2 are not supported as WAVEFORMATEXTENSIBLE + + case WAVE_FORMAT_WMAUDIO2: + case WAVE_FORMAT_WMAUDIO3: + dpds = true; + break; + + default: + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + + } + break; + + default: + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + } + } + + // Locate 'data' + ptr = reinterpret_cast(riffHeader) + sizeof(RIFFChunkHeader); + if ((ptr + sizeof(RIFFChunk)) > wavEnd) + { + return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF); + } + + auto dataChunk = FindChunk(ptr, riffChunk->size, wavEnd, FOURCC_DATA_TAG); + if (!dataChunk || !dataChunk->size) + { + return HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + } + + ptr = reinterpret_cast(dataChunk) + sizeof(RIFFChunk); + if (ptr + dataChunk->size > wavEnd) + { + return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF); + } + + *pwfx = reinterpret_cast(wf); + *pdata = ptr; + *dataSize = dataChunk->size; + return S_OK; + } + + + //--------------------------------------------------------------------------------- + HRESULT WaveFindLoopInfo( + _In_reads_bytes_(wavDataSize) const uint8_t* wavData, + _In_ size_t wavDataSize, + _Out_ uint32_t* pLoopStart, + _Out_ uint32_t* pLoopLength) noexcept + { + if (!wavData || !pLoopStart || !pLoopLength) + return E_POINTER; + + if (wavDataSize < (sizeof(RIFFChunk) + sizeof(uint32_t))) + { + return E_FAIL; + } + + *pLoopStart = 0; + *pLoopLength = 0; + + const uint8_t* wavEnd = wavData + wavDataSize; + + // Locate RIFF 'WAVE' + auto riffChunk = FindChunk(wavData, wavDataSize, wavEnd, FOURCC_RIFF_TAG); + if (!riffChunk || riffChunk->size < 4) + { + return E_FAIL; + } + + auto riffHeader = reinterpret_cast(riffChunk); + if (riffHeader->riff == FOURCC_XWMA_FILE_TAG) + { + // xWMA files do not contain loop information + return S_OK; + } + + if (riffHeader->riff != FOURCC_WAVE_FILE_TAG) + { + return E_FAIL; + } + + // Locate 'wsmp' (DLS Chunk) + auto ptr = reinterpret_cast(riffHeader) + sizeof(RIFFChunkHeader); + if ((ptr + sizeof(RIFFChunk)) > wavEnd) + { + return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF); + } + + auto dlsChunk = FindChunk(ptr, riffChunk->size, wavEnd, FOURCC_DLS_SAMPLE); + if (dlsChunk) + { + ptr = reinterpret_cast(dlsChunk) + sizeof(RIFFChunk); + if (ptr + dlsChunk->size > wavEnd) + { + return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF); + } + + if (dlsChunk->size >= sizeof(RIFFDLSSample)) + { + auto dlsSample = reinterpret_cast(ptr); + + if (dlsChunk->size >= (dlsSample->size + dlsSample->loopCount * sizeof(DLSLoop))) + { + auto loops = reinterpret_cast(ptr + dlsSample->size); + for (uint32_t j = 0; j < dlsSample->loopCount; ++j) + { + if ((loops[j].loopType == DLSLoop::LOOP_TYPE_FORWARD || loops[j].loopType == DLSLoop::LOOP_TYPE_RELEASE)) + { + // Return 'forward' loop + *pLoopStart = loops[j].loopStart; + *pLoopLength = loops[j].loopLength; + return S_OK; + } + } + } + } + } + + // Locate 'smpl' (Sample Chunk) + auto midiChunk = FindChunk(ptr, riffChunk->size, wavEnd, FOURCC_MIDI_SAMPLE); + if (midiChunk) + { + ptr = reinterpret_cast(midiChunk) + sizeof(RIFFChunk); + if (ptr + midiChunk->size > wavEnd) + { + return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF); + } + + if (midiChunk->size >= sizeof(RIFFMIDISample)) + { + auto midiSample = reinterpret_cast(ptr); + + if (midiChunk->size >= (sizeof(RIFFMIDISample) + midiSample->loopCount * sizeof(MIDILoop))) + { + auto loops = reinterpret_cast(ptr + sizeof(RIFFMIDISample)); + for (uint32_t j = 0; j < midiSample->loopCount; ++j) + { + if (loops[j].type == MIDILoop::LOOP_TYPE_FORWARD) + { + // Return 'forward' loop + *pLoopStart = loops[j].start; + *pLoopLength = loops[j].end - loops[j].start + 1; + return S_OK; + } + } + } + } + } + + return S_OK; + } + + + //--------------------------------------------------------------------------------- + HRESULT WaveFindTable( + _In_reads_bytes_(wavDataSize) const uint8_t* wavData, + _In_ size_t wavDataSize, + _In_ uint32_t tag, + _Outptr_result_maybenull_ const uint32_t** pData, + _Out_ uint32_t* dataCount) noexcept + { + if (!wavData || !pData || !dataCount) + return E_POINTER; + + if (wavDataSize < (sizeof(RIFFChunk) + sizeof(uint32_t))) + { + return E_FAIL; + } + + *pData = nullptr; + *dataCount = 0; + + const uint8_t* wavEnd = wavData + wavDataSize; + + // Locate RIFF 'WAVE' + auto riffChunk = FindChunk(wavData, wavDataSize, wavEnd, FOURCC_RIFF_TAG); + if (!riffChunk || riffChunk->size < 4) + { + return E_FAIL; + } + + auto riffHeader = reinterpret_cast(riffChunk); + if (riffHeader->riff != FOURCC_WAVE_FILE_TAG && riffHeader->riff != FOURCC_XWMA_FILE_TAG) + { + return E_FAIL; + } + + // Locate tag + auto ptr = reinterpret_cast(riffHeader) + sizeof(RIFFChunkHeader); + if ((ptr + sizeof(RIFFChunk)) > wavEnd) + { + return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF); + } + + auto tableChunk = FindChunk(ptr, riffChunk->size, wavEnd, tag); + if (tableChunk) + { + ptr = reinterpret_cast(tableChunk) + sizeof(RIFFChunk); + if (ptr + tableChunk->size > wavEnd) + { + return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF); + } + + if ((tableChunk->size % sizeof(uint32_t)) != 0) + { + return E_FAIL; + } + + *pData = reinterpret_cast(ptr); + *dataCount = tableChunk->size / 4; + } + + return S_OK; + } + + + //--------------------------------------------------------------------------------- + HRESULT LoadAudioFromFile( + _In_z_ const wchar_t* szFileName, + _Inout_ std::unique_ptr& wavData, + _Out_ DWORD* bytesRead) noexcept + { + if (!szFileName) + return E_INVALIDARG; + + // open the file + #if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) + ScopedHandle hFile(safe_handle(CreateFile2( + szFileName, + GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, + nullptr))); + #else + ScopedHandle hFile(safe_handle(CreateFileW( + szFileName, + GENERIC_READ, FILE_SHARE_READ, + nullptr, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, + nullptr))); + #endif + + if (!hFile) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + // Get the file size + FILE_STANDARD_INFO fileInfo; + if (!GetFileInformationByHandleEx(hFile.get(), FileStandardInfo, &fileInfo, sizeof(fileInfo))) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + // File is too big for 32-bit allocation, so reject read + if (fileInfo.EndOfFile.HighPart > 0) + { + return E_FAIL; + } + + // Need at least enough data to have a valid minimal WAV file + if (fileInfo.EndOfFile.LowPart < (sizeof(RIFFChunk) * 2 + sizeof(DWORD) + sizeof(WAVEFORMAT))) + { + return E_FAIL; + } + + // create enough space for the file data + wavData.reset(new (std::nothrow) uint8_t[fileInfo.EndOfFile.LowPart]); + if (!wavData) + { + return E_OUTOFMEMORY; + } + + // read the data in + if (!ReadFile(hFile.get(), + wavData.get(), + fileInfo.EndOfFile.LowPart, + bytesRead, + nullptr + )) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + return (*bytesRead < fileInfo.EndOfFile.LowPart) ? E_FAIL : S_OK; + } +} + +//------------------------------------------------------------------------------------- +_Use_decl_annotations_ +HRESULT DirectX::LoadWAVAudioInMemory( + const uint8_t* wavData, + size_t wavDataSize, + const WAVEFORMATEX** wfx, + const uint8_t** startAudio, + uint32_t* audioBytes) noexcept +{ + if (!wavData || !wfx || !startAudio || !audioBytes) + return E_INVALIDARG; + + *wfx = nullptr; + *startAudio = nullptr; + *audioBytes = 0; + + // Need at least enough data to have a valid minimal WAV file + if (wavDataSize < (sizeof(RIFFChunk) * 2 + sizeof(DWORD) + sizeof(WAVEFORMAT))) + { + return E_FAIL; + } + + bool dpds, seek; + HRESULT hr = WaveFindFormatAndData(wavData, wavDataSize, wfx, startAudio, audioBytes, dpds, seek); + if (FAILED(hr)) + return hr; + + return (dpds || seek) ? E_FAIL : S_OK; +} + + +//------------------------------------------------------------------------------------- +_Use_decl_annotations_ +HRESULT DirectX::LoadWAVAudioFromFile( + const wchar_t* szFileName, + std::unique_ptr& wavData, + const WAVEFORMATEX** wfx, + const uint8_t** startAudio, + uint32_t* audioBytes) noexcept +{ + if (!szFileName || !wfx || !startAudio || !audioBytes) + return E_INVALIDARG; + + *wfx = nullptr; + *startAudio = nullptr; + *audioBytes = 0; + + DWORD bytesRead = 0; + HRESULT hr = LoadAudioFromFile(szFileName, wavData, &bytesRead); + if (FAILED(hr)) + { + return hr; + } + + bool dpds, seek; + hr = WaveFindFormatAndData(wavData.get(), bytesRead, wfx, startAudio, audioBytes, dpds, seek); + if (FAILED(hr)) + return hr; + + return (dpds || seek) ? E_FAIL : S_OK; +} + + +//------------------------------------------------------------------------------------- +_Use_decl_annotations_ +HRESULT DirectX::LoadWAVAudioInMemoryEx( + const uint8_t* wavData, + size_t wavDataSize, + DirectX::WAVData& result) noexcept +{ + if (!wavData) + return E_INVALIDARG; + + memset(&result, 0, sizeof(result)); + + // Need at least enough data to have a valid minimal WAV file + if (wavDataSize < (sizeof(RIFFChunk) * 2 + sizeof(DWORD) + sizeof(WAVEFORMAT))) + { + return E_FAIL; + } + + bool dpds, seek; + HRESULT hr = WaveFindFormatAndData(wavData, wavDataSize, &result.wfx, &result.startAudio, &result.audioBytes, dpds, seek); + if (FAILED(hr)) + return hr; + + hr = WaveFindLoopInfo(wavData, wavDataSize, &result.loopStart, &result.loopLength); + if (FAILED(hr)) + return hr; + + if (dpds) + { + hr = WaveFindTable(wavData, wavDataSize, FOURCC_XWMA_DPDS, &result.seek, &result.seekCount); + if (FAILED(hr)) + return hr; + } + else if (seek) + { + hr = WaveFindTable(wavData, wavDataSize, FOURCC_XMA_SEEK, &result.seek, &result.seekCount); + if (FAILED(hr)) + return hr; + } + + return S_OK; +} + + +//------------------------------------------------------------------------------------- +_Use_decl_annotations_ +HRESULT DirectX::LoadWAVAudioFromFileEx( + const wchar_t* szFileName, + std::unique_ptr& wavData, + DirectX::WAVData& result) noexcept +{ + if (!szFileName) + return E_INVALIDARG; + + memset(&result, 0, sizeof(result)); + + DWORD bytesRead = 0; + HRESULT hr = LoadAudioFromFile(szFileName, wavData, &bytesRead); + if (FAILED(hr)) + { + return hr; + } + + bool dpds, seek; + hr = WaveFindFormatAndData(wavData.get(), bytesRead, &result.wfx, &result.startAudio, &result.audioBytes, dpds, seek); + if (FAILED(hr)) + return hr; + + hr = WaveFindLoopInfo(wavData.get(), bytesRead, &result.loopStart, &result.loopLength); + if (FAILED(hr)) + return hr; + + if (dpds) + { + hr = WaveFindTable(wavData.get(), bytesRead, FOURCC_XWMA_DPDS, &result.seek, &result.seekCount); + if (FAILED(hr)) + return hr; + } + else if (seek) + { + hr = WaveFindTable(wavData.get(), bytesRead, FOURCC_XMA_SEEK, &result.seek, &result.seekCount); + if (FAILED(hr)) + return hr; + } + + return S_OK; +} diff --git a/Common/DirectXTK12/Audio/WAVFileReader.h b/Common/DirectXTK12/Audio/WAVFileReader.h new file mode 100644 index 0000000..c919179 --- /dev/null +++ b/Common/DirectXTK12/Audio/WAVFileReader.h @@ -0,0 +1,58 @@ +//-------------------------------------------------------------------------------------- +// File: WAVFileReader.h +// +// Functions for loading WAV audio files +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248929 +// http://go.microsoft.com/fwlink/?LinkID=615561 +//------------------------------------------------------------------------------------- + +#pragma once + +#include + +#include +#include +#include + + +namespace DirectX +{ + HRESULT LoadWAVAudioInMemory( + _In_reads_bytes_(wavDataSize) const uint8_t* wavData, + _In_ size_t wavDataSize, + _Outptr_ const WAVEFORMATEX** wfx, + _Outptr_ const uint8_t** startAudio, + _Out_ uint32_t* audioBytes) noexcept; + + HRESULT LoadWAVAudioFromFile( + _In_z_ const wchar_t* szFileName, + _Inout_ std::unique_ptr& wavData, + _Outptr_ const WAVEFORMATEX** wfx, + _Outptr_ const uint8_t** startAudio, + _Out_ uint32_t* audioBytes) noexcept; + + struct WAVData + { + const WAVEFORMATEX* wfx; + const uint8_t* startAudio; + uint32_t audioBytes; + uint32_t loopStart; + uint32_t loopLength; + const uint32_t* seek; // Note: XMA Seek data is Big-Endian + uint32_t seekCount; + }; + + HRESULT LoadWAVAudioInMemoryEx( + _In_reads_bytes_(wavDataSize) const uint8_t* wavData, + _In_ size_t wavDataSize, + _Out_ WAVData& result) noexcept; + + HRESULT LoadWAVAudioFromFileEx( + _In_z_ const wchar_t* szFileName, + _Inout_ std::unique_ptr& wavData, + _Out_ WAVData& result) noexcept; +} diff --git a/Common/DirectXTK12/Audio/WaveBank.cpp b/Common/DirectXTK12/Audio/WaveBank.cpp new file mode 100644 index 0000000..f169b18 --- /dev/null +++ b/Common/DirectXTK12/Audio/WaveBank.cpp @@ -0,0 +1,618 @@ +//-------------------------------------------------------------------------------------- +// File: WaveBank.cpp +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248929 +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "pch.h" +#include "Audio.h" +#include "WaveBankReader.h" +#include "SoundCommon.h" +#include "PlatformHelpers.h" + +#include + +using namespace DirectX; + + +//====================================================================================== +// WaveBank +//====================================================================================== + +// Internal object implementation class. +class WaveBank::Impl : public IVoiceNotify +{ +public: + explicit Impl(_In_ AudioEngine* engine) : + mEngine(engine), + mOneShots(0), + mPrepared(false), + mStreaming(false) + { + assert(mEngine != nullptr); + mEngine->RegisterNotify(this, false); + } + + Impl(Impl&&) = default; + Impl& operator= (Impl&&) = default; + + Impl(Impl const&) = delete; + Impl& operator= (Impl const&) = delete; + + ~Impl() override + { + if (!mInstances.empty()) + { + DebugTrace("WARNING: Destroying WaveBank \"%hs\" with %zu outstanding instances\n", + mReader.BankName(), mInstances.size()); + + for (auto it : mInstances) + { + assert(it != nullptr); + it->OnDestroyParent(); + } + + mInstances.clear(); + } + + if (mOneShots > 0) + { + DebugTrace("WARNING: Destroying WaveBank \"%hs\" with %u outstanding one shot effects\n", + mReader.BankName(), mOneShots); + } + + if (mEngine) + { + mEngine->UnregisterNotify(this, true, false); + mEngine = nullptr; + } + } + + HRESULT Initialize(_In_ const AudioEngine* engine, _In_z_ const wchar_t* wbFileName) noexcept; + + void Play(unsigned int index, float volume, float pitch, float pan); + + // IVoiceNotify + void __cdecl OnBufferEnd() override + { + InterlockedDecrement(&mOneShots); + } + + void __cdecl OnCriticalError() override + { + mOneShots = 0; + } + + void __cdecl OnReset() override + { + // No action required + } + + void __cdecl OnUpdate() override + { + // We do not register for update notification + assert(false); + } + + void __cdecl OnDestroyEngine() noexcept override + { + mEngine = nullptr; + mOneShots = 0; + } + + void __cdecl OnTrim() override + { + // No action required + } + + void __cdecl GatherStatistics(AudioStatistics& stats) const noexcept override + { + stats.playingOneShots += mOneShots; + + if (!mStreaming) + { + stats.audioBytes += mReader.BankAudioSize(); + + #ifdef DIRECTX_ENABLE_XMA2 + if (mReader.HasXMA()) + stats.xmaAudioBytes += mReader.BankAudioSize(); + #endif + } + } + + void __cdecl OnDestroyParent() noexcept override + { + } + + AudioEngine* mEngine; + std::list mInstances; + WaveBankReader mReader; + uint32_t mOneShots; + bool mPrepared; + bool mStreaming; +}; + + +_Use_decl_annotations_ +HRESULT WaveBank::Impl::Initialize(const AudioEngine* engine, const wchar_t* wbFileName) noexcept +{ + if (!engine || !wbFileName) + return E_INVALIDARG; + + HRESULT hr = mReader.Open(wbFileName); + if (FAILED(hr)) + return hr; + + mStreaming = mReader.IsStreamingBank(); + + return S_OK; +} + + +void WaveBank::Impl::Play(unsigned int index, float volume, float pitch, float pan) +{ + assert(volume >= -XAUDIO2_MAX_VOLUME_LEVEL && volume <= XAUDIO2_MAX_VOLUME_LEVEL); + assert(pitch >= -1.f && pitch <= 1.f); + assert(pan >= -1.f && pan <= 1.f); + + if (mStreaming) + { + DebugTrace("ERROR: One-shots can only be created from an in-memory wave bank\n"); + throw std::runtime_error("WaveBank::Play"); + } + + if (index >= mReader.Count()) + { + DebugTrace("WARNING: Index %u not found in wave bank with only %u entries, one-shot not triggered\n", + index, mReader.Count()); + return; + } + + if (!mPrepared) + { + mReader.WaitOnPrepare(); + mPrepared = true; + } + + char wfxbuff[64] = {}; + auto wfx = reinterpret_cast(wfxbuff); + HRESULT hr = mReader.GetFormat(index, wfx, sizeof(wfxbuff)); + ThrowIfFailed(hr); + + IXAudio2SourceVoice* voice = nullptr; + mEngine->AllocateVoice(wfx, SoundEffectInstance_Default, true, &voice); + + if (!voice) + return; + + if (volume != 1.f) + { + hr = voice->SetVolume(volume); + ThrowIfFailed(hr); + } + + if (pitch != 0.f) + { + const float fr = XAudio2SemitonesToFrequencyRatio(pitch * 12.f); + + hr = voice->SetFrequencyRatio(fr); + ThrowIfFailed(hr); + } + + if (pan != 0.f) + { + float matrix[16]; + if (ComputePan(pan, wfx->nChannels, matrix)) + { + hr = voice->SetOutputMatrix(nullptr, wfx->nChannels, mEngine->GetOutputChannels(), matrix); + ThrowIfFailed(hr); + } + } + + hr = voice->Start(0); + ThrowIfFailed(hr); + + XAUDIO2_BUFFER buffer = {}; + hr = mReader.GetWaveData(index, &buffer.pAudioData, buffer.AudioBytes); + ThrowIfFailed(hr); + + WaveBankReader::Metadata metadata; + hr = mReader.GetMetadata(index, metadata); + ThrowIfFailed(hr); + + buffer.Flags = XAUDIO2_END_OF_STREAM; + buffer.pContext = this; + +#ifdef DIRECTX_ENABLE_XWMA + + XAUDIO2_BUFFER_WMA wmaBuffer = {}; + + uint32_t tag; + hr = mReader.GetSeekTable(index, &wmaBuffer.pDecodedPacketCumulativeBytes, wmaBuffer.PacketCount, tag); + ThrowIfFailed(hr); + + if (tag == WAVE_FORMAT_WMAUDIO2 || tag == WAVE_FORMAT_WMAUDIO3) + { + hr = voice->SubmitSourceBuffer(&buffer, &wmaBuffer); + } + else + #endif // xWMA + { + hr = voice->SubmitSourceBuffer(&buffer, nullptr); + } + if (FAILED(hr)) + { + DebugTrace("ERROR: WaveBank failed (%08X) when submitting buffer:\n", static_cast(hr)); + DebugTrace("\tFormat Tag %u, %u channels, %u-bit, %u Hz, %u bytes\n", + wfx->wFormatTag, wfx->nChannels, wfx->wBitsPerSample, wfx->nSamplesPerSec, metadata.lengthBytes); + throw std::runtime_error("SubmitSourceBuffer"); + } + + InterlockedIncrement(&mOneShots); +} + + +//-------------------------------------------------------------------------------------- +// WaveBank +//-------------------------------------------------------------------------------------- + +// Public constructors. +_Use_decl_annotations_ +WaveBank::WaveBank(AudioEngine* engine, const wchar_t* wbFileName) + : pImpl(std::make_unique(engine)) +{ + HRESULT hr = pImpl->Initialize(engine, wbFileName); + if (FAILED(hr)) + { + DebugTrace("ERROR: WaveBank failed (%08X) to intialize from .xwb file \"%ls\"\n", + static_cast(hr), wbFileName); + throw std::runtime_error("WaveBank"); + } + + DebugTrace("INFO: WaveBank \"%hs\" with %u entries loaded from .xwb file \"%ls\"\n", + pImpl->mReader.BankName(), pImpl->mReader.Count(), wbFileName); +} + + +WaveBank::WaveBank(WaveBank&&) noexcept = default; +WaveBank& WaveBank::operator= (WaveBank&&) noexcept = default; +WaveBank::~WaveBank() = default; + + +// Public methods (one-shots) +void WaveBank::Play(unsigned int index) +{ + pImpl->Play(index, 1.f, 0.f, 0.f); +} + + +void WaveBank::Play(unsigned int index, float volume, float pitch, float pan) +{ + pImpl->Play(index, volume, pitch, pan); +} + + +void WaveBank::Play(_In_z_ const char* name) +{ + const unsigned int index = pImpl->mReader.Find(name); + if (index == unsigned(-1)) + { + DebugTrace("WARNING: Name '%hs' not found in wave bank, one-shot not triggered\n", name); + return; + } + + pImpl->Play(index, 1.f, 0.f, 0.f); +} + + +void WaveBank::Play(_In_z_ const char* name, float volume, float pitch, float pan) +{ + const unsigned int index = pImpl->mReader.Find(name); + if (index == unsigned(-1)) + { + DebugTrace("WARNING: Name '%hs' not found in wave bank, one-shot not triggered\n", name); + return; + } + + pImpl->Play(index, volume, pitch, pan); +} + + +// Public methods (sound effect instance) +std::unique_ptr WaveBank::CreateInstance(unsigned int index, SOUND_EFFECT_INSTANCE_FLAGS flags) +{ + auto& wb = pImpl->mReader; + + if (pImpl->mStreaming) + { + DebugTrace("ERROR: SoundEffectInstances can only be created from an in-memory wave bank\n"); + throw std::runtime_error("WaveBank::CreateInstance"); + } + + if (index >= wb.Count()) + { + // We don't throw an exception here as titles often simply ignore missing assets rather than fail + return std::unique_ptr(); + } + + if (!pImpl->mPrepared) + { + wb.WaitOnPrepare(); + pImpl->mPrepared = true; + } + + auto effect = new SoundEffectInstance(pImpl->mEngine, this, index, flags); + assert(effect != nullptr); + pImpl->mInstances.emplace_back(effect->GetVoiceNotify()); + return std::unique_ptr(effect); +} + + +std::unique_ptr WaveBank::CreateInstance(_In_z_ const char* name, SOUND_EFFECT_INSTANCE_FLAGS flags) +{ + const unsigned int index = pImpl->mReader.Find(name); + if (index == unsigned(-1)) + { + // We don't throw an exception here as titles often simply ignore missing assets rather than fail + return std::unique_ptr(); + } + + return CreateInstance(index, flags); +} + + +// Public methods (sound stream instance) +std::unique_ptr WaveBank::CreateStreamInstance(unsigned int index, SOUND_EFFECT_INSTANCE_FLAGS flags) +{ + auto& wb = pImpl->mReader; + + if (!pImpl->mStreaming) + { + DebugTrace("ERROR: SoundStreamInstances can only be created from a streaming wave bank\n"); + throw std::runtime_error("WaveBank::CreateStreamInstance"); + } + + if (index >= wb.Count()) + { + // We don't throw an exception here as titles often simply ignore missing assets rather than fail + return std::unique_ptr(); + } + + if (!pImpl->mPrepared) + { + wb.WaitOnPrepare(); + pImpl->mPrepared = true; + } + + auto effect = new SoundStreamInstance(pImpl->mEngine, this, index, flags); + assert(effect != nullptr); + pImpl->mInstances.emplace_back(effect->GetVoiceNotify()); + return std::unique_ptr(effect); +} + + +std::unique_ptr WaveBank::CreateStreamInstance(_In_z_ const char* name, SOUND_EFFECT_INSTANCE_FLAGS flags) +{ + const unsigned int index = pImpl->mReader.Find(name); + if (index == unsigned(-1)) + { + // We don't throw an exception here as titles often simply ignore missing assets rather than fail + return std::unique_ptr(); + } + + return CreateStreamInstance(index, flags); +} + + +void WaveBank::UnregisterInstance(_In_ IVoiceNotify* instance) +{ + auto it = std::find(pImpl->mInstances.begin(), pImpl->mInstances.end(), instance); + if (it == pImpl->mInstances.end()) + return; + + pImpl->mInstances.erase(it); +} + + +// Public accessors. +bool WaveBank::IsPrepared() const noexcept +{ + if (pImpl->mPrepared) + return true; + + if (!pImpl->mReader.IsPrepared()) + return false; + + pImpl->mPrepared = true; + return true; +} + + +bool WaveBank::IsInUse() const noexcept +{ + return (pImpl->mOneShots > 0) || !pImpl->mInstances.empty(); +} + + +bool WaveBank::IsStreamingBank() const noexcept +{ + return pImpl->mReader.IsStreamingBank(); +} + + +bool WaveBank::IsAdvancedFormat() const noexcept +{ + return (pImpl->mReader.GetWaveAlignment() == 4096); +} + + +size_t WaveBank::GetSampleSizeInBytes(unsigned int index) const noexcept +{ + if (index >= pImpl->mReader.Count()) + return 0; + + WaveBankReader::Metadata metadata; + HRESULT hr = pImpl->mReader.GetMetadata(index, metadata); + if (FAILED(hr)) + return 0; + + return metadata.lengthBytes; +} + + +size_t WaveBank::GetSampleDuration(unsigned int index) const noexcept +{ + if (index >= pImpl->mReader.Count()) + return 0; + + WaveBankReader::Metadata metadata; + HRESULT hr = pImpl->mReader.GetMetadata(index, metadata); + if (FAILED(hr)) + return 0; + + return metadata.duration; +} + + +size_t WaveBank::GetSampleDurationMS(unsigned int index) const noexcept +{ + if (index >= pImpl->mReader.Count()) + return 0; + + char buff[64] = {}; + auto wfx = reinterpret_cast(buff); + HRESULT hr = pImpl->mReader.GetFormat(index, wfx, sizeof(buff)); + if (FAILED(hr)) + return 0; + + WaveBankReader::Metadata metadata; + hr = pImpl->mReader.GetMetadata(index, metadata); + if (FAILED(hr)) + return 0; + + return static_cast((uint64_t(metadata.duration) * 1000) / wfx->nSamplesPerSec); +} + + +_Use_decl_annotations_ +const WAVEFORMATEX* WaveBank::GetFormat(unsigned int index, WAVEFORMATEX* wfx, size_t maxsize) const noexcept +{ + if (index >= pImpl->mReader.Count()) + return nullptr; + + HRESULT hr = pImpl->mReader.GetFormat(index, wfx, maxsize); + if (FAILED(hr)) + return nullptr; + + return wfx; +} + + +_Use_decl_annotations_ +int WaveBank::Find(const char* name) const +{ + return static_cast(pImpl->mReader.Find(name)); +} + + +#ifdef DIRECTX_ENABLE_XWMA + +_Use_decl_annotations_ +bool WaveBank::FillSubmitBuffer(unsigned int index, XAUDIO2_BUFFER& buffer, XAUDIO2_BUFFER_WMA& wmaBuffer) const +{ + memset(&buffer, 0, sizeof(buffer)); + memset(&wmaBuffer, 0, sizeof(wmaBuffer)); + + HRESULT hr = pImpl->mReader.GetWaveData(index, &buffer.pAudioData, buffer.AudioBytes); + ThrowIfFailed(hr); + + WaveBankReader::Metadata metadata; + hr = pImpl->mReader.GetMetadata(index, metadata); + ThrowIfFailed(hr); + + buffer.LoopBegin = metadata.loopStart; + buffer.LoopLength = metadata.loopLength; + + uint32_t tag; + hr = pImpl->mReader.GetSeekTable(index, &wmaBuffer.pDecodedPacketCumulativeBytes, wmaBuffer.PacketCount, tag); + ThrowIfFailed(hr); + + return (tag == WAVE_FORMAT_WMAUDIO2 || tag == WAVE_FORMAT_WMAUDIO3); +} + +#else // !xWMA + +_Use_decl_annotations_ +void WaveBank::FillSubmitBuffer(unsigned int index, XAUDIO2_BUFFER& buffer) const +{ + memset(&buffer, 0, sizeof(buffer)); + + HRESULT hr = pImpl->mReader.GetWaveData(index, &buffer.pAudioData, buffer.AudioBytes); + ThrowIfFailed(hr); + + WaveBankReader::Metadata metadata; + hr = pImpl->mReader.GetMetadata(index, metadata); + ThrowIfFailed(hr); + + buffer.LoopBegin = metadata.loopStart; + buffer.LoopLength = metadata.loopLength; +} + +#endif + + +HANDLE WaveBank::GetAsyncHandle() const noexcept +{ + if (pImpl) + { + return pImpl->mReader.GetAsyncHandle(); + } + + return nullptr; +} + + +_Use_decl_annotations_ +bool WaveBank::GetPrivateData(unsigned int index, void* data, size_t datasize) +{ + if (index >= pImpl->mReader.Count()) + return false; + + if (!data) + return false; + + switch (datasize) + { + case sizeof(WaveBankReader::Metadata) : + { + auto ptr = reinterpret_cast(data); + return SUCCEEDED(pImpl->mReader.GetMetadata(index, *ptr)); + } + + case sizeof(WaveBankSeekData) : + { + auto ptr = reinterpret_cast(data); + return SUCCEEDED(pImpl->mReader.GetSeekTable(index, &ptr->seekTable, ptr->seekCount, ptr->tag)); + } + + default: + return false; + } +} + + +//-------------------------------------------------------------------------------------- +// Adapters for /Zc:wchar_t- clients +#if defined(_MSC_VER) && !defined(_NATIVE_WCHAR_T_DEFINED) + +_Use_decl_annotations_ +WaveBank::WaveBank(AudioEngine* engine, const __wchar_t* wbFileName) : + WaveBank(engine, reinterpret_cast(wbFileName)) +{ +} + +#endif // !_NATIVE_WCHAR_T_DEFINED diff --git a/Common/DirectXTK12/Audio/WaveBankReader.cpp b/Common/DirectXTK12/Audio/WaveBankReader.cpp new file mode 100644 index 0000000..7e4cfc3 --- /dev/null +++ b/Common/DirectXTK12/Audio/WaveBankReader.cpp @@ -0,0 +1,1423 @@ +//-------------------------------------------------------------------------------------- +// File: WaveBankReader.cpp +// +// Functions for loading audio data from Wave Banks +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248929 +// http://go.microsoft.com/fwlink/?LinkID=615561 +//------------------------------------------------------------------------------------- + +#include "pch.h" +#include "WaveBankReader.h" +#include "Audio.h" +#include "PlatformHelpers.h" +#include "SoundCommon.h" + +#if (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wnonportable-system-include-path" +#endif + +#include +#include +#endif + + +namespace +{ +#pragma pack(push, 1) + + constexpr uint16_t MSADPCM_FORMAT_EXTRA_BYTES = 32; + constexpr uint16_t MSADPCM_NUM_COEFFICIENTS = 7; + + constexpr size_t DVD_SECTOR_SIZE = 2048; + constexpr size_t DVD_BLOCK_SIZE = DVD_SECTOR_SIZE * 16; + + constexpr size_t ALIGNMENT_MIN = 4; + constexpr size_t ALIGNMENT_DVD = DVD_SECTOR_SIZE; + + constexpr size_t MAX_DATA_SEGMENT_SIZE = 0xFFFFFFFF; + constexpr size_t MAX_COMPACT_DATA_SEGMENT_SIZE = 0x001FFFFF; + + struct REGION + { + uint32_t dwOffset; // Region offset, in bytes. + uint32_t dwLength; // Region length, in bytes. + + void BigEndian() noexcept + { + dwOffset = _byteswap_ulong(dwOffset); + dwLength = _byteswap_ulong(dwLength); + } + }; + + struct SAMPLEREGION + { + uint32_t dwStartSample; // Start sample for the region. + uint32_t dwTotalSamples; // Region length in samples. + + void BigEndian() noexcept + { + dwStartSample = _byteswap_ulong(dwStartSample); + dwTotalSamples = _byteswap_ulong(dwTotalSamples); + } + }; + + struct HEADER + { + static constexpr uint32_t SIGNATURE = MAKEFOURCC('W', 'B', 'N', 'D'); + static constexpr uint32_t BE_SIGNATURE = MAKEFOURCC('D', 'N', 'B', 'W'); + static constexpr uint32_t VERSION = 44; + + enum SEGIDX + { + SEGIDX_BANKDATA = 0, // Bank data + SEGIDX_ENTRYMETADATA, // Entry meta-data + SEGIDX_SEEKTABLES, // Storage for seek tables for the encoded waves. + SEGIDX_ENTRYNAMES, // Entry friendly names + SEGIDX_ENTRYWAVEDATA, // Entry wave data + SEGIDX_COUNT + }; + + uint32_t dwSignature; // File signature + uint32_t dwVersion; // Version of the tool that created the file + uint32_t dwHeaderVersion; // Version of the file format + REGION Segments[SEGIDX_COUNT]; // Segment lookup table + + void BigEndian() noexcept + { + // Leave dwSignature alone as indicator of BE vs. LE + + dwVersion = _byteswap_ulong(dwVersion); + dwHeaderVersion = _byteswap_ulong(dwHeaderVersion); + for (size_t j = 0; j < SEGIDX_COUNT; ++j) + { + Segments[j].BigEndian(); + } + } + }; + +#pragma warning( disable : 4201 4203 ) + + union MINIWAVEFORMAT + { + static constexpr uint32_t TAG_PCM = 0x0; + static constexpr uint32_t TAG_XMA = 0x1; + static constexpr uint32_t TAG_ADPCM = 0x2; + static constexpr uint32_t TAG_WMA = 0x3; + + static constexpr uint32_t BITDEPTH_8 = 0x0; // PCM only + static constexpr uint32_t BITDEPTH_16 = 0x1; // PCM only + + static constexpr size_t ADPCM_BLOCKALIGN_CONVERSION_OFFSET = 22; + + struct + { + uint32_t wFormatTag : 2; // Format tag + uint32_t nChannels : 3; // Channel count (1 - 6) + uint32_t nSamplesPerSec : 18; // Sampling rate + uint32_t wBlockAlign : 8; // Block alignment. For WMA, lower 6 bits block alignment index, upper 2 bits bytes-per-second index. + uint32_t wBitsPerSample : 1; // Bits per sample (8 vs. 16, PCM only); WMAudio2/WMAudio3 (for WMA) + }; + + uint32_t dwValue; + + void BigEndian() noexcept + { + dwValue = _byteswap_ulong(dwValue); + } + + WORD BitsPerSample() const noexcept + { + if (wFormatTag == TAG_XMA) + return 16; // XMA_OUTPUT_SAMPLE_BITS == 16 + if (wFormatTag == TAG_WMA) + return 16; + if (wFormatTag == TAG_ADPCM) + return 4; // MSADPCM_BITS_PER_SAMPLE == 4 + + // wFormatTag must be TAG_PCM (2 bits can only represent 4 different values) + return (wBitsPerSample == BITDEPTH_16) ? 16u : 8u; + } + + DWORD BlockAlign() const noexcept + { + switch (wFormatTag) + { + case TAG_PCM: + return wBlockAlign; + + case TAG_XMA: + return (nChannels * 16 / 8); // XMA_OUTPUT_SAMPLE_BITS = 16 + + case TAG_ADPCM: + return (wBlockAlign + ADPCM_BLOCKALIGN_CONVERSION_OFFSET) * nChannels; + + case TAG_WMA: + { + static const uint32_t aWMABlockAlign[17] = + { + 929, + 1487, + 1280, + 2230, + 8917, + 8192, + 4459, + 5945, + 2304, + 1536, + 1485, + 1008, + 2731, + 4096, + 6827, + 5462, + 1280 + }; + + const uint32_t dwBlockAlignIndex = wBlockAlign & 0x1F; + if (dwBlockAlignIndex < 17) + return aWMABlockAlign[dwBlockAlignIndex]; + } + break; + } + + return 0; + } + + DWORD AvgBytesPerSec() const noexcept + { + switch (wFormatTag) + { + case TAG_PCM: + return nSamplesPerSec * wBlockAlign; + + case TAG_XMA: + return nSamplesPerSec * BlockAlign(); + + case TAG_ADPCM: + { + const uint32_t blockAlign = BlockAlign(); + const uint32_t samplesPerAdpcmBlock = AdpcmSamplesPerBlock(); + return blockAlign * nSamplesPerSec / samplesPerAdpcmBlock; + } + + case TAG_WMA: + { + static const uint32_t aWMAAvgBytesPerSec[7] = + { + 12000, + 24000, + 4000, + 6000, + 8000, + 20000, + 2500 + }; + // bitrate = entry * 8 + + const uint32_t dwBytesPerSecIndex = wBlockAlign >> 5; + if (dwBytesPerSecIndex < 7) + return aWMAAvgBytesPerSec[dwBytesPerSecIndex]; + } + break; + } + + return 0; + } + + DWORD AdpcmSamplesPerBlock() const noexcept + { + const uint32_t nBlockAlign = (wBlockAlign + ADPCM_BLOCKALIGN_CONVERSION_OFFSET) * nChannels; + return nBlockAlign * 2 / uint32_t(nChannels) - 12; + } + + void AdpcmFillCoefficientTable(ADPCMWAVEFORMAT *fmt) const noexcept + { + // These are fixed since we are always using MS ADPCM + fmt->wNumCoef = MSADPCM_NUM_COEFFICIENTS; + + static ADPCMCOEFSET aCoef[7] = { { 256, 0}, {512, -256}, {0,0}, {192,64}, {240,0}, {460, -208}, {392,-232} }; + memcpy(&fmt->aCoef, aCoef, sizeof(aCoef)); + } + }; + + struct BANKDATA + { + static constexpr size_t BANKNAME_LENGTH = 64; + + static constexpr uint32_t TYPE_BUFFER = 0x00000000; + static constexpr uint32_t TYPE_STREAMING = 0x00000001; + static constexpr uint32_t TYPE_MASK = 0x00000001; + + static constexpr uint32_t FLAGS_ENTRYNAMES = 0x00010000; + static constexpr uint32_t FLAGS_COMPACT = 0x00020000; + static constexpr uint32_t FLAGS_SYNC_DISABLED = 0x00040000; + static constexpr uint32_t FLAGS_SEEKTABLES = 0x00080000; + static constexpr uint32_t FLAGS_MASK = 0x000F0000; + + uint32_t dwFlags; // Bank flags + uint32_t dwEntryCount; // Number of entries in the bank + char szBankName[BANKNAME_LENGTH]; // Bank friendly name + uint32_t dwEntryMetaDataElementSize; // Size of each entry meta-data element, in bytes + uint32_t dwEntryNameElementSize; // Size of each entry name element, in bytes + uint32_t dwAlignment; // Entry alignment, in bytes + MINIWAVEFORMAT CompactFormat; // Format data for compact bank + FILETIME BuildTime; // Build timestamp + + void BigEndian() noexcept + { + dwFlags = _byteswap_ulong(dwFlags); + dwEntryCount = _byteswap_ulong(dwEntryCount); + dwEntryMetaDataElementSize = _byteswap_ulong(dwEntryMetaDataElementSize); + dwEntryNameElementSize = _byteswap_ulong(dwEntryNameElementSize); + dwAlignment = _byteswap_ulong(dwAlignment); + CompactFormat.BigEndian(); + BuildTime.dwLowDateTime = _byteswap_ulong(BuildTime.dwLowDateTime); + BuildTime.dwHighDateTime = _byteswap_ulong(BuildTime.dwHighDateTime); + } + }; + + struct ENTRY + { + static constexpr uint32_t FLAGS_READAHEAD = 0x00000001; // Enable stream read-ahead + static constexpr uint32_t FLAGS_LOOPCACHE = 0x00000002; // One or more looping sounds use this wave + static constexpr uint32_t FLAGS_REMOVELOOPTAIL = 0x00000004;// Remove data after the end of the loop region + static constexpr uint32_t FLAGS_IGNORELOOP = 0x00000008; // Used internally when the loop region can't be used + static constexpr uint32_t FLAGS_MASK = 0x00000008; + + union + { + struct + { + // Entry flags + uint32_t dwFlags : 4; + + // Duration of the wave, in units of one sample. + // For instance, a ten second long wave sampled + // at 48KHz would have a duration of 480,000. + // This value is not affected by the number of + // channels, the number of bits per sample, or the + // compression format of the wave. + uint32_t Duration : 28; + }; + uint32_t dwFlagsAndDuration; + }; + + MINIWAVEFORMAT Format; // Entry format. + REGION PlayRegion; // Region within the wave data segment that contains this entry. + SAMPLEREGION LoopRegion; // Region within the wave data (in samples) that should loop. + + void BigEndian() noexcept + { + dwFlagsAndDuration = _byteswap_ulong(dwFlagsAndDuration); + Format.BigEndian(); + PlayRegion.BigEndian(); + LoopRegion.BigEndian(); + } + }; + + struct ENTRYCOMPACT + { + uint32_t dwOffset : 21; // Data offset, in multiplies of the bank alignment + uint32_t dwLengthDeviation : 11; // Data length deviation, in bytes + + void BigEndian() noexcept + { + *reinterpret_cast(this) = _byteswap_ulong(*reinterpret_cast(this)); + } + + void ComputeLocations(DWORD& offset, DWORD& length, uint32_t index, const HEADER& header, const BANKDATA& data, const ENTRYCOMPACT* entries) const noexcept + { + offset = dwOffset * data.dwAlignment; + + if (index < (data.dwEntryCount - 1)) + { + length = (entries[index + 1].dwOffset * data.dwAlignment) - offset - dwLengthDeviation; + } + else + { + length = header.Segments[HEADER::SEGIDX_ENTRYWAVEDATA].dwLength - offset - dwLengthDeviation; + } + } + + static uint32_t GetDuration(DWORD length, const BANKDATA& data, _In_opt_ const uint32_t* seekTable) noexcept + { + switch (data.CompactFormat.wFormatTag) + { + case MINIWAVEFORMAT::TAG_ADPCM: + { + uint32_t duration = (length / data.CompactFormat.BlockAlign()) * data.CompactFormat.AdpcmSamplesPerBlock(); + const uint32_t partial = length % data.CompactFormat.BlockAlign(); + if (partial) + { + if (partial >= (7u * data.CompactFormat.nChannels)) + duration += (partial * 2 / data.CompactFormat.nChannels - 12); + } + return duration; + } + + case MINIWAVEFORMAT::TAG_WMA: + if (seekTable) + { + const uint32_t seekCount = *seekTable; + if (seekCount > 0) + { + return seekTable[seekCount] / uint32_t(2 * data.CompactFormat.nChannels); + } + } + return 0; + + case MINIWAVEFORMAT::TAG_XMA: + if (seekTable) + { + const uint32_t seekCount = *seekTable; + if (seekCount > 0) + { + return seekTable[seekCount]; + } + } + return 0; + + default: + return uint32_t((uint64_t(length) * 8) + / (uint64_t(data.CompactFormat.BitsPerSample()) * uint64_t(data.CompactFormat.nChannels))); + } + } + }; + +#pragma pack(pop) + + inline const uint32_t* FindSeekTable(uint32_t index, _In_opt_ const uint8_t* seekTable, const HEADER& header, const BANKDATA& data) noexcept + { + if (!seekTable || index >= data.dwEntryCount) + return nullptr; + + const uint32_t seekSize = header.Segments[HEADER::SEGIDX_SEEKTABLES].dwLength; + + if ((index * sizeof(uint32_t)) > seekSize) + return nullptr; + + auto table = reinterpret_cast(seekTable); + uint32_t offset = table[index]; + if (offset == uint32_t(-1)) + return nullptr; + + offset += sizeof(uint32_t) * data.dwEntryCount; + + if (offset > seekSize) + return nullptr; + + return reinterpret_cast(seekTable + offset); + } +} + +static_assert(sizeof(REGION) == 8, "Mismatch with xact3wb.h"); +static_assert(sizeof(SAMPLEREGION) == 8, "Mismatch with xact3wb.h"); +static_assert(sizeof(HEADER) == 52, "Mismatch with xact3wb.h"); +static_assert(sizeof(ENTRY) == 24, "Mismatch with xact3wb.h"); +static_assert(sizeof(MINIWAVEFORMAT) == 4, "Mismatch with xact3wb.h"); +static_assert(sizeof(ENTRY) == 24, "Mismatch with xact3wb.h"); +static_assert(sizeof(ENTRYCOMPACT) == 4, "Mismatch with xact3wb.h"); +static_assert(sizeof(BANKDATA) == 96, "Mismatch with xact3wb.h"); + +using namespace DirectX; + +//-------------------------------------------------------------------------------------- +class WaveBankReader::Impl +{ +public: + Impl() noexcept : + m_async(INVALID_HANDLE_VALUE), + m_request{}, + m_prepared(false), + m_header{}, + m_data{} + #ifdef DIRECTX_ENABLE_XMA2 + , m_xmaMemory(nullptr) + #endif + { + } + + Impl(Impl&&) = default; + Impl& operator= (Impl&&) = default; + + Impl(Impl const&) = delete; + Impl& operator= (Impl const&) = delete; + + ~Impl() { Close(); } + + HRESULT Open(_In_z_ const wchar_t* szFileName) noexcept(false); + void Close() noexcept; + + HRESULT GetFormat(_In_ uint32_t index, _Out_writes_bytes_(maxsize) WAVEFORMATEX* pFormat, _In_ size_t maxsize) const noexcept; + + HRESULT GetWaveData(_In_ uint32_t index, _Outptr_ const uint8_t** pData, _Out_ uint32_t& dataSize) const noexcept; + + HRESULT GetSeekTable(_In_ uint32_t index, _Out_ const uint32_t** pData, _Out_ uint32_t& dataCount, _Out_ uint32_t& tag) const noexcept; + + HRESULT GetMetadata(_In_ uint32_t index, _Out_ Metadata& metadata) const noexcept; + + bool UpdatePrepared() noexcept; + + void Clear() noexcept + { + memset(&m_header, 0, sizeof(HEADER)); + memset(&m_data, 0, sizeof(BANKDATA)); + + m_names.clear(); + m_entries.reset(); + m_seekData.reset(); + m_waveData.reset(); + + #ifdef DIRECTX_ENABLE_XMA2 + if (m_xmaMemory) + { + ApuFree(m_xmaMemory); + m_xmaMemory = nullptr; + } + #endif + } + + HANDLE m_async; + ScopedHandle m_event; + OVERLAPPED m_request; + bool m_prepared; + + HEADER m_header; + BANKDATA m_data; + std::map m_names; + +private: + std::unique_ptr m_entries; + std::unique_ptr m_seekData; + std::unique_ptr m_waveData; + +#ifdef DIRECTX_ENABLE_XMA2 +public: + void* m_xmaMemory; +#endif +}; + + +_Use_decl_annotations_ +HRESULT WaveBankReader::Impl::Open(const wchar_t* szFileName) noexcept(false) +{ + Close(); + Clear(); + + m_prepared = false; + + m_event.reset(CreateEventEx(nullptr, nullptr, CREATE_EVENT_MANUAL_RESET, EVENT_MODIFY_STATE | SYNCHRONIZE)); + if (!m_event) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + +#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) + CREATEFILE2_EXTENDED_PARAMETERS params = { sizeof(CREATEFILE2_EXTENDED_PARAMETERS), 0, 0, 0, {}, nullptr }; + params.dwFileAttributes = FILE_ATTRIBUTE_NORMAL; + params.dwFileFlags = FILE_FLAG_OVERLAPPED | FILE_FLAG_SEQUENTIAL_SCAN; + ScopedHandle hFile(safe_handle(CreateFile2( + szFileName, + GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, + ¶ms))); +#else + ScopedHandle hFile(safe_handle(CreateFileW( + szFileName, + GENERIC_READ, FILE_SHARE_READ, + nullptr, + OPEN_EXISTING, FILE_FLAG_OVERLAPPED | FILE_FLAG_SEQUENTIAL_SCAN, + nullptr))); +#endif + + if (!hFile) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + // Read and verify header + OVERLAPPED request = {}; + request.hEvent = m_event.get(); + + bool wait = false; + if (!ReadFile(hFile.get(), &m_header, sizeof(m_header), nullptr, &request)) + { + const DWORD error = GetLastError(); + if (error != ERROR_IO_PENDING) + return HRESULT_FROM_WIN32(error); + wait = true; + } + + DWORD bytes; +#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) + std::ignore = wait; + + BOOL result = GetOverlappedResultEx(hFile.get(), &request, &bytes, INFINITE, FALSE); +#else + if (wait) + { + std::ignore = WaitForSingleObject(m_event.get(), INFINITE); + } + + BOOL result = GetOverlappedResult(hFile.get(), &request, &bytes, FALSE); +#endif + + if (!result || (bytes != sizeof(m_header))) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + if (m_header.dwSignature != HEADER::SIGNATURE && m_header.dwSignature != HEADER::BE_SIGNATURE) + { + return E_FAIL; + } + + const bool be = (m_header.dwSignature == HEADER::BE_SIGNATURE); + if (be) + { + DebugTrace("INFO: \"%ls\" is a big-endian (Xbox 360) wave bank\n", szFileName); + m_header.BigEndian(); + } + + if (m_header.dwHeaderVersion != HEADER::VERSION) + { + return E_FAIL; + } + + // Load bank data + memset(&request, 0, sizeof(request)); + request.Offset = m_header.Segments[HEADER::SEGIDX_BANKDATA].dwOffset; + request.hEvent = m_event.get(); + + wait = false; + if (!ReadFile(hFile.get(), &m_data, sizeof(m_data), nullptr, &request)) + { + const DWORD error = GetLastError(); + if (error != ERROR_IO_PENDING) + return HRESULT_FROM_WIN32(error); + wait = true; + } + +#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) + result = GetOverlappedResultEx(hFile.get(), &request, &bytes, INFINITE, FALSE); +#else + if (wait) + { + std::ignore = WaitForSingleObject(m_event.get(), INFINITE); + } + + result = GetOverlappedResult(hFile.get(), &request, &bytes, FALSE); +#endif + + if (!result || (bytes != sizeof(m_data))) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + if (be) + m_data.BigEndian(); + + if (!m_data.dwEntryCount) + { + return HRESULT_FROM_WIN32(ERROR_NO_DATA); + } + + if (m_data.dwFlags & BANKDATA::TYPE_STREAMING) + { + if (m_data.dwAlignment < ALIGNMENT_DVD) + return E_FAIL; + if (m_data.dwAlignment % DVD_SECTOR_SIZE) + return E_FAIL; + } + else if (m_data.dwAlignment < ALIGNMENT_MIN) + { + return E_FAIL; + } + + if (m_data.dwFlags & BANKDATA::FLAGS_COMPACT) + { + if (m_data.dwEntryMetaDataElementSize != sizeof(ENTRYCOMPACT)) + { + return E_FAIL; + } + + if (m_header.Segments[HEADER::SEGIDX_ENTRYWAVEDATA].dwLength > (MAX_COMPACT_DATA_SEGMENT_SIZE * m_data.dwAlignment)) + { + // Data segment is too large to be valid compact wavebank + return E_FAIL; + } + } + else + { + if (m_data.dwEntryMetaDataElementSize != sizeof(ENTRY)) + { + return E_FAIL; + } + } + + const DWORD metadataBytes = m_header.Segments[HEADER::SEGIDX_ENTRYMETADATA].dwLength; + if (metadataBytes != (m_data.dwEntryCount * m_data.dwEntryMetaDataElementSize)) + { + return E_FAIL; + } + + // Load names + const DWORD namesBytes = m_header.Segments[HEADER::SEGIDX_ENTRYNAMES].dwLength; + if (namesBytes > 0) + { + if (namesBytes >= (m_data.dwEntryNameElementSize * m_data.dwEntryCount)) + { + std::unique_ptr temp(new (std::nothrow) char[namesBytes]); + if (!temp) + return E_OUTOFMEMORY; + + memset(&request, 0, sizeof(request)); + request.Offset = m_header.Segments[HEADER::SEGIDX_ENTRYNAMES].dwOffset; + request.hEvent = m_event.get(); + + wait = false; + if (!ReadFile(hFile.get(), temp.get(), namesBytes, nullptr, &request)) + { + const DWORD error = GetLastError(); + if (error != ERROR_IO_PENDING) + return HRESULT_FROM_WIN32(error); + wait = true; + } + + #if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) + result = GetOverlappedResultEx(hFile.get(), &request, &bytes, INFINITE, FALSE); + #else + if (wait) + { + std::ignore = WaitForSingleObject(m_event.get(), INFINITE); + } + + result = GetOverlappedResult(hFile.get(), &request, &bytes, FALSE); + #endif + + if (!result || (namesBytes != bytes)) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + for (uint32_t j = 0; j < m_data.dwEntryCount; ++j) + { + const DWORD n = m_data.dwEntryNameElementSize * j; + + char name[64] = {}; + strncpy_s(name, &temp[n], sizeof(name)); + + m_names[name] = j; + } + } + } + + // Load entries + if (m_data.dwFlags & BANKDATA::FLAGS_COMPACT) + { + m_entries.reset(reinterpret_cast(new (std::nothrow) ENTRYCOMPACT[m_data.dwEntryCount])); + } + else + { + m_entries.reset(reinterpret_cast(new (std::nothrow) ENTRY[m_data.dwEntryCount])); + } + if (!m_entries) + return E_OUTOFMEMORY; + + memset(&request, 0, sizeof(request)); + request.Offset = m_header.Segments[HEADER::SEGIDX_ENTRYMETADATA].dwOffset; + request.hEvent = m_event.get(); + + wait = false; + if (!ReadFile(hFile.get(), m_entries.get(), metadataBytes, nullptr, &request)) + { + const DWORD error = GetLastError(); + if (error != ERROR_IO_PENDING) + return HRESULT_FROM_WIN32(error); + wait = true; + } + +#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) + result = GetOverlappedResultEx(hFile.get(), &request, &bytes, INFINITE, FALSE); +#else + if (wait) + { + std::ignore = WaitForSingleObject(m_event.get(), INFINITE); + } + + result = GetOverlappedResult(hFile.get(), &request, &bytes, FALSE); +#endif + + if (!result || (metadataBytes != bytes)) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + if (be) + { + if (m_data.dwFlags & BANKDATA::FLAGS_COMPACT) + { + auto ptr = reinterpret_cast(m_entries.get()); + for (size_t j = 0; j < m_data.dwEntryCount; ++j, ++ptr) + ptr->BigEndian(); + } + else + { + auto ptr = reinterpret_cast(m_entries.get()); + for (size_t j = 0; j < m_data.dwEntryCount; ++j, ++ptr) + ptr->BigEndian(); + } + } + + // Load seek tables (XMA2 / xWMA) + const DWORD seekLen = m_header.Segments[HEADER::SEGIDX_SEEKTABLES].dwLength; + if (seekLen > 0) + { + m_seekData.reset(new (std::nothrow) uint8_t[seekLen]); + if (!m_seekData) + return E_OUTOFMEMORY; + + memset(&request, 0, sizeof(OVERLAPPED)); + request.Offset = m_header.Segments[HEADER::SEGIDX_SEEKTABLES].dwOffset; + request.hEvent = m_event.get(); + + wait = false; + if (!ReadFile(hFile.get(), m_seekData.get(), seekLen, nullptr, &request)) + { + const DWORD error = GetLastError(); + if (error != ERROR_IO_PENDING) + return HRESULT_FROM_WIN32(error); + wait = true; + } + + #if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) + result = GetOverlappedResultEx(hFile.get(), &request, &bytes, INFINITE, FALSE); + #else + if (wait) + { + std::ignore = WaitForSingleObject(m_event.get(), INFINITE); + } + + result = GetOverlappedResult(hFile.get(), &request, &bytes, FALSE); + #endif + + if (!result || (seekLen != bytes)) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + if (be) + { + auto ptr = reinterpret_cast(m_seekData.get()); + for (size_t j = 0; j < seekLen; j += 4, ++ptr) + { + *ptr = _byteswap_ulong(*ptr); + } + } + } + + const DWORD waveLen = m_header.Segments[HEADER::SEGIDX_ENTRYWAVEDATA].dwLength; + if (!waveLen) + { + return HRESULT_FROM_WIN32(ERROR_NO_DATA); + } + + if (m_data.dwFlags & BANKDATA::TYPE_STREAMING) + { + // If streaming, reopen without buffering + hFile.reset(); + + #if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) + CREATEFILE2_EXTENDED_PARAMETERS params2 = { sizeof(CREATEFILE2_EXTENDED_PARAMETERS), 0, 0, 0, {}, nullptr }; + params2.dwFileAttributes = FILE_ATTRIBUTE_NORMAL; + params2.dwFileFlags = FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING; + m_async = CreateFile2(szFileName, + GENERIC_READ, + FILE_SHARE_READ, + OPEN_EXISTING, + ¶ms2); + #else + m_async = CreateFileW(szFileName, + GENERIC_READ, + FILE_SHARE_READ, + nullptr, + OPEN_EXISTING, + FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, + nullptr); + #endif + + if (m_async == INVALID_HANDLE_VALUE) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + m_prepared = true; + } + else + { + // If in-memory, kick off read of wave data + void* dest = nullptr; + + #ifdef DIRECTX_ENABLE_XMA2 + bool xma = false; + if (m_data.dwFlags & BANKDATA::FLAGS_COMPACT) + { + if (m_data.CompactFormat.wFormatTag == MINIWAVEFORMAT::TAG_XMA) + xma = true; + } + else + { + for (uint32_t j = 0; j < m_data.dwEntryCount; ++j) + { + auto& entry = reinterpret_cast(m_entries.get())[j]; + if (entry.Format.wFormatTag == MINIWAVEFORMAT::TAG_XMA) + { + xma = true; + break; + } + } + } + + if (xma) + { + HRESULT hr = ApuAlloc(&m_xmaMemory, nullptr, waveLen, SHAPE_XMA_INPUT_BUFFER_ALIGNMENT); + if (FAILED(hr)) + { + DebugTrace("ERROR: ApuAlloc failed. Did you allocate a large enough heap with ApuCreateHeap for all your XMA wave data?\n"); + return hr; + } + + dest = m_xmaMemory; + } + else + #endif // XMA2 + { + m_waveData.reset(new (std::nothrow) uint8_t[waveLen]); + if (!m_waveData) + return E_OUTOFMEMORY; + + dest = m_waveData.get(); + } + + memset(&m_request, 0, sizeof(OVERLAPPED)); + m_request.Offset = m_header.Segments[HEADER::SEGIDX_ENTRYWAVEDATA].dwOffset; + m_request.hEvent = m_event.get(); + + if (!ReadFile(hFile.get(), dest, waveLen, nullptr, &m_request)) + { + const DWORD error = GetLastError(); + if (error != ERROR_IO_PENDING) + return HRESULT_FROM_WIN32(error); + } + else + { + m_prepared = true; + memset(&m_request, 0, sizeof(OVERLAPPED)); + } + + m_async = hFile.release(); + } + + return S_OK; +} + + +void WaveBankReader::Impl::Close() noexcept +{ + if (m_async != INVALID_HANDLE_VALUE) + { + if (m_request.hEvent) + { + DWORD bytes; + #if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) + std::ignore = GetOverlappedResultEx(m_async, &m_request, &bytes, INFINITE, FALSE); + #else + std::ignore = WaitForSingleObject(m_request.hEvent, INFINITE); + + std::ignore = GetOverlappedResult(m_async, &m_request, &bytes, FALSE); + #endif + } + + CloseHandle(m_async); + m_async = INVALID_HANDLE_VALUE; + } + m_event.reset(); + +#ifdef DIRECTX_ENABLE_XMA2 + if (m_xmaMemory) + { + ApuFree(m_xmaMemory); + m_xmaMemory = nullptr; + } +#endif +} + + +_Use_decl_annotations_ +HRESULT WaveBankReader::Impl::GetFormat(uint32_t index, WAVEFORMATEX* pFormat, size_t maxsize) const noexcept +{ + if (!pFormat || !maxsize) + return E_INVALIDARG; + + if (index >= m_data.dwEntryCount || !m_entries) + { + return E_FAIL; + } + + auto& miniFmt = (m_data.dwFlags & BANKDATA::FLAGS_COMPACT) ? m_data.CompactFormat : (reinterpret_cast(m_entries.get())[index].Format); + + switch (miniFmt.wFormatTag) + { + case MINIWAVEFORMAT::TAG_PCM: + if (maxsize < sizeof(PCMWAVEFORMAT)) + return HRESULT_FROM_WIN32(ERROR_MORE_DATA); + + pFormat->wFormatTag = WAVE_FORMAT_PCM; + + if (maxsize >= sizeof(WAVEFORMATEX)) + { + pFormat->cbSize = 0; + } + break; + + case MINIWAVEFORMAT::TAG_ADPCM: + if (maxsize < (sizeof(WAVEFORMATEX) + MSADPCM_FORMAT_EXTRA_BYTES)) + return HRESULT_FROM_WIN32(ERROR_MORE_DATA); + + pFormat->wFormatTag = WAVE_FORMAT_ADPCM; + pFormat->cbSize = MSADPCM_FORMAT_EXTRA_BYTES; + { + auto adpcmFmt = reinterpret_cast(pFormat); + adpcmFmt->wSamplesPerBlock = static_cast(miniFmt.AdpcmSamplesPerBlock()); + miniFmt.AdpcmFillCoefficientTable(adpcmFmt); + } + break; + + case MINIWAVEFORMAT::TAG_WMA: + if (maxsize < sizeof(WAVEFORMATEX)) + return HRESULT_FROM_WIN32(ERROR_MORE_DATA); + + pFormat->wFormatTag = static_cast((miniFmt.wBitsPerSample & 0x1) ? WAVE_FORMAT_WMAUDIO3 : WAVE_FORMAT_WMAUDIO2); + pFormat->cbSize = 0; + break; + + case MINIWAVEFORMAT::TAG_XMA: // XMA2 is supported by Xbox One + #ifdef DIRECTX_ENABLE_XMA2 + if (maxsize < sizeof(XMA2WAVEFORMATEX)) + return HRESULT_FROM_WIN32(ERROR_MORE_DATA); + + pFormat->wFormatTag = WAVE_FORMAT_XMA2; + pFormat->cbSize = sizeof(XMA2WAVEFORMATEX) - sizeof(WAVEFORMATEX); + { + auto xmaFmt = reinterpret_cast(pFormat); + + xmaFmt->NumStreams = static_cast((miniFmt.nChannels + 1) / 2); + xmaFmt->BytesPerBlock = 65536 /* XACT_FIXED_XMA_BLOCK_SIZE */; + xmaFmt->EncoderVersion = 4 /* XMAENCODER_VERSION_XMA2 */; + + auto seekTable = FindSeekTable(index, m_seekData.get(), m_header, m_data); + if (seekTable) + { + xmaFmt->BlockCount = static_cast(*seekTable); + } + else + { + xmaFmt->BlockCount = 0; + } + + switch (miniFmt.nChannels) + { + case 1: xmaFmt->ChannelMask = SPEAKER_MONO; break; + case 2: xmaFmt->ChannelMask = SPEAKER_STEREO; break; + case 3: xmaFmt->ChannelMask = SPEAKER_2POINT1; break; + case 4: xmaFmt->ChannelMask = SPEAKER_QUAD; break; + case 5: xmaFmt->ChannelMask = SPEAKER_4POINT1; break; + case 6: xmaFmt->ChannelMask = SPEAKER_5POINT1; break; + case 7: xmaFmt->ChannelMask = SPEAKER_5POINT1 | SPEAKER_BACK_CENTER; break; + case 8: xmaFmt->ChannelMask = SPEAKER_7POINT1; break; + default: xmaFmt->ChannelMask = DWORD(-1); break; + } + + if (m_data.dwFlags & BANKDATA::FLAGS_COMPACT) + { + auto& entry = reinterpret_cast(m_entries.get())[index]; + + DWORD dwOffset, dwLength; + entry.ComputeLocations(dwOffset, dwLength, index, m_header, m_data, reinterpret_cast(m_entries.get())); + + xmaFmt->SamplesEncoded = entry.GetDuration(dwLength, m_data, seekTable); + + xmaFmt->PlayBegin = xmaFmt->PlayLength = + xmaFmt->LoopBegin = xmaFmt->LoopLength = xmaFmt->LoopCount = 0; + } + else + { + auto& entry = reinterpret_cast(m_entries.get())[index]; + + xmaFmt->SamplesEncoded = entry.Duration; + xmaFmt->PlayBegin = 0; + xmaFmt->PlayLength = entry.PlayRegion.dwLength; + + if (entry.LoopRegion.dwTotalSamples > 0) + { + xmaFmt->LoopBegin = entry.LoopRegion.dwStartSample; + xmaFmt->LoopLength = entry.LoopRegion.dwTotalSamples; + xmaFmt->LoopCount = 0xff /* XACTLOOPCOUNT_INFINITE */; + } + else + { + xmaFmt->LoopBegin = xmaFmt->LoopLength = xmaFmt->LoopCount = 0; + } + } + } + break; + #else + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + #endif + + default: + return E_FAIL; + } + + pFormat->nChannels = miniFmt.nChannels; + pFormat->wBitsPerSample = miniFmt.BitsPerSample(); + pFormat->nBlockAlign = static_cast(miniFmt.BlockAlign()); + pFormat->nSamplesPerSec = miniFmt.nSamplesPerSec; + pFormat->nAvgBytesPerSec = miniFmt.AvgBytesPerSec(); + + return S_OK; +} + + +_Use_decl_annotations_ +HRESULT WaveBankReader::Impl::GetWaveData(uint32_t index, const uint8_t** pData, uint32_t& dataSize) const noexcept +{ + if (!pData) + return E_INVALIDARG; + + if (index >= m_data.dwEntryCount || !m_entries) + { + return E_FAIL; + } + +#ifdef DIRECTX_ENABLE_XMA2 + const uint8_t* waveData = (m_xmaMemory) ? reinterpret_cast(m_xmaMemory) : m_waveData.get(); +#else + const uint8_t* waveData = m_waveData.get(); +#endif + + if (!waveData) + return E_FAIL; + + if (m_data.dwFlags & BANKDATA::TYPE_STREAMING) + { + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + + if (!m_prepared) + { + return HRESULT_FROM_WIN32(ERROR_IO_INCOMPLETE); + } + + if (m_data.dwFlags & BANKDATA::FLAGS_COMPACT) + { + auto& entry = reinterpret_cast(m_entries.get())[index]; + + DWORD dwOffset, dwLength; + entry.ComputeLocations(dwOffset, dwLength, index, m_header, m_data, reinterpret_cast(m_entries.get())); + + if ((uint64_t(dwOffset) + uint64_t(dwLength)) > uint64_t(m_header.Segments[HEADER::SEGIDX_ENTRYWAVEDATA].dwLength)) + { + return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF); + } + + *pData = &waveData[dwOffset]; + dataSize = dwLength; + } + else + { + auto& entry = reinterpret_cast(m_entries.get())[index]; + + if ((uint64_t(entry.PlayRegion.dwOffset) + uint64_t(entry.PlayRegion.dwLength)) > uint64_t(m_header.Segments[HEADER::SEGIDX_ENTRYWAVEDATA].dwLength)) + { + return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF); + } + + *pData = &waveData[entry.PlayRegion.dwOffset]; + dataSize = entry.PlayRegion.dwLength; + } + + return S_OK; +} + + +_Use_decl_annotations_ +HRESULT WaveBankReader::Impl::GetSeekTable(uint32_t index, const uint32_t** pData, uint32_t& dataCount, uint32_t& tag) const noexcept +{ + if (!pData) + return E_INVALIDARG; + + *pData = nullptr; + dataCount = 0; + tag = 0; + + if (index >= m_data.dwEntryCount || !m_entries) + { + return E_FAIL; + } + + if (!m_seekData) + return S_OK; + + auto& miniFmt = (m_data.dwFlags & BANKDATA::FLAGS_COMPACT) ? m_data.CompactFormat : (reinterpret_cast(m_entries.get())[index].Format); + + switch (miniFmt.wFormatTag) + { + case MINIWAVEFORMAT::TAG_WMA: + tag = static_cast((miniFmt.wBitsPerSample & 0x1) ? WAVE_FORMAT_WMAUDIO3 : WAVE_FORMAT_WMAUDIO2); + break; + + case MINIWAVEFORMAT::TAG_XMA: + tag = 0x166 /* WAVE_FORMAT_XMA2 */; + break; + + default: + return S_OK; + } + + auto seekTable = FindSeekTable(index, m_seekData.get(), m_header, m_data); + if (!seekTable) + return S_OK; + + dataCount = *seekTable; + *pData = seekTable + 1; + + return S_OK; +} + + +_Use_decl_annotations_ +HRESULT WaveBankReader::Impl::GetMetadata(uint32_t index, Metadata& metadata) const noexcept +{ + if (index >= m_data.dwEntryCount || !m_entries) + { + return E_FAIL; + } + + if (m_data.dwFlags & BANKDATA::FLAGS_COMPACT) + { + auto& entry = reinterpret_cast(m_entries.get())[index]; + + DWORD dwOffset, dwLength; + entry.ComputeLocations(dwOffset, dwLength, index, m_header, m_data, reinterpret_cast(m_entries.get())); + + if (m_seekData) + { + auto seekTable = FindSeekTable(index, m_seekData.get(), m_header, m_data); + if (seekTable) + { + metadata.duration = entry.GetDuration(dwLength, m_data, seekTable); + } + else + { + metadata.duration = entry.GetDuration(dwLength, m_data, nullptr); + } + } + else + { + metadata.duration = entry.GetDuration(dwLength, m_data, nullptr); + } + metadata.loopStart = metadata.loopLength = 0; + metadata.offsetBytes = dwOffset; + metadata.lengthBytes = dwLength; + } + else + { + auto& entry = reinterpret_cast(m_entries.get())[index]; + + metadata.duration = entry.Duration; + metadata.loopStart = entry.LoopRegion.dwStartSample; + metadata.loopLength = entry.LoopRegion.dwTotalSamples; + metadata.offsetBytes = entry.PlayRegion.dwOffset; + metadata.lengthBytes = entry.PlayRegion.dwLength; + } + + if (m_data.dwFlags & BANKDATA::TYPE_STREAMING) + { + const uint64_t offset = uint64_t(metadata.offsetBytes) + uint64_t(m_header.Segments[HEADER::SEGIDX_ENTRYWAVEDATA].dwOffset); + if (offset > UINT32_MAX) + return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); + + metadata.offsetBytes = static_cast(offset); + } + + return S_OK; +} + + +bool WaveBankReader::Impl::UpdatePrepared() noexcept +{ + if (m_prepared) + return true; + + if (m_async == INVALID_HANDLE_VALUE) + return false; + + if (m_request.hEvent) + { + + #if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) + DWORD bytes; + const BOOL result = GetOverlappedResultEx(m_async, &m_request, &bytes, 0, FALSE); + #else + const bool result = HasOverlappedIoCompleted(&m_request); + #endif + if (result) + { + m_prepared = true; + + memset(&m_request, 0, sizeof(OVERLAPPED)); + } + } + + return m_prepared; +} + + + +//-------------------------------------------------------------------------------------- +WaveBankReader::WaveBankReader() noexcept(false) : + pImpl(std::make_unique()) +{ +} + + +WaveBankReader::~WaveBankReader() +{ +} + + +_Use_decl_annotations_ +HRESULT WaveBankReader::Open(const wchar_t* szFileName) noexcept +{ + return pImpl->Open(szFileName); +} + + +_Use_decl_annotations_ +uint32_t WaveBankReader::Find(const char* name) const +{ + auto it = pImpl->m_names.find(name); + if (it != pImpl->m_names.cend()) + { + return it->second; + } + + return uint32_t(-1); +} + + +bool WaveBankReader::IsPrepared() noexcept +{ + if (pImpl->m_prepared) + return true; + + return pImpl->UpdatePrepared(); +} + + +void WaveBankReader::WaitOnPrepare() noexcept +{ + if (pImpl->m_prepared) + return; + + if (pImpl->m_request.hEvent) + { + std::ignore = WaitForSingleObjectEx(pImpl->m_request.hEvent, INFINITE, FALSE); + + pImpl->UpdatePrepared(); + } +} + + +bool WaveBankReader::HasNames() const noexcept +{ + return !pImpl->m_names.empty(); +} + + +bool WaveBankReader::IsStreamingBank() const noexcept +{ + return (pImpl->m_data.dwFlags & BANKDATA::TYPE_STREAMING) != 0; +} + + +#ifdef DIRECTX_ENABLE_XMA2 +bool WaveBankReader::HasXMA() const noexcept +{ + return (pImpl->m_xmaMemory != nullptr); +} +#endif + + +const char* WaveBankReader::BankName() const noexcept +{ + return pImpl->m_data.szBankName; +} + + +uint32_t WaveBankReader::Count() const noexcept +{ + return pImpl->m_data.dwEntryCount; +} + + +uint32_t WaveBankReader::BankAudioSize() const noexcept +{ + return pImpl->m_header.Segments[HEADER::SEGIDX_ENTRYWAVEDATA].dwLength; +} + + +_Use_decl_annotations_ +HRESULT WaveBankReader::GetFormat(uint32_t index, WAVEFORMATEX* pFormat, size_t maxsize) const noexcept +{ + return pImpl->GetFormat(index, pFormat, maxsize); +} + + +_Use_decl_annotations_ +HRESULT WaveBankReader::GetWaveData(uint32_t index, const uint8_t** pData, uint32_t& dataSize) const noexcept +{ + return pImpl->GetWaveData(index, pData, dataSize); +} + + +_Use_decl_annotations_ +HRESULT WaveBankReader::GetSeekTable(uint32_t index, const uint32_t** pData, uint32_t& dataCount, uint32_t& tag) const noexcept +{ + return pImpl->GetSeekTable(index, pData, dataCount, tag); +} + + +_Use_decl_annotations_ +HRESULT WaveBankReader::GetMetadata(uint32_t index, Metadata& metadata) const noexcept +{ + return pImpl->GetMetadata(index, metadata); +} + + +HANDLE WaveBankReader::GetAsyncHandle() const noexcept +{ + return (pImpl->m_data.dwFlags & BANKDATA::TYPE_STREAMING) ? pImpl->m_async : INVALID_HANDLE_VALUE; +} + + +uint32_t WaveBankReader::GetWaveAlignment() const noexcept +{ + return pImpl->m_data.dwAlignment; +} diff --git a/Common/DirectXTK12/Audio/WaveBankReader.h b/Common/DirectXTK12/Audio/WaveBankReader.h new file mode 100644 index 0000000..b67e864 --- /dev/null +++ b/Common/DirectXTK12/Audio/WaveBankReader.h @@ -0,0 +1,83 @@ +//-------------------------------------------------------------------------------------- +// File: WaveBankReader.h +// +// Functions for loading audio data from Wave Banks +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248929 +// http://go.microsoft.com/fwlink/?LinkID=615561 +//------------------------------------------------------------------------------------- + +#pragma once + +#include +#include + +#include +#include + + +namespace DirectX +{ + class WaveBankReader + { + public: + WaveBankReader() noexcept(false); + + WaveBankReader(WaveBankReader&&) = default; + WaveBankReader& operator= (WaveBankReader&&) = default; + + WaveBankReader(WaveBankReader const&) = delete; + WaveBankReader& operator= (WaveBankReader const&) = delete; + + ~WaveBankReader(); + + HRESULT Open(_In_z_ const wchar_t* szFileName) noexcept; + + uint32_t Find(_In_z_ const char* name) const; + + bool IsPrepared() noexcept; + void WaitOnPrepare() noexcept; + + bool HasNames() const noexcept; + bool IsStreamingBank() const noexcept; + + #if (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) + bool HasXMA() const noexcept; + #endif + + const char* BankName() const noexcept; + + uint32_t Count() const noexcept; + + uint32_t BankAudioSize() const noexcept; + + HRESULT GetFormat(_In_ uint32_t index, _Out_writes_bytes_(maxsize) WAVEFORMATEX* pFormat, _In_ size_t maxsize) const noexcept; + + HRESULT GetWaveData(_In_ uint32_t index, _Outptr_ const uint8_t** pData, _Out_ uint32_t& dataSize) const noexcept; + + HRESULT GetSeekTable(_In_ uint32_t index, _Out_ const uint32_t** pData, _Out_ uint32_t& dataCount, _Out_ uint32_t& tag) const noexcept; + + HANDLE GetAsyncHandle() const noexcept; + + uint32_t GetWaveAlignment() const noexcept; + + struct Metadata + { + uint32_t duration; + uint32_t loopStart; + uint32_t loopLength; + uint32_t offsetBytes; + uint32_t lengthBytes; + }; + HRESULT GetMetadata(_In_ uint32_t index, _Out_ Metadata& metadata) const noexcept; + + private: + // Private implementation. + class Impl; + + std::unique_ptr pImpl; + }; +} diff --git a/Common/DirectXTK12/CMakeLists.txt b/Common/DirectXTK12/CMakeLists.txt new file mode 100644 index 0000000..6961616 --- /dev/null +++ b/Common/DirectXTK12/CMakeLists.txt @@ -0,0 +1,539 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +cmake_minimum_required (VERSION 3.20) + +set(DIRECTXTK12_VERSION 1.5.6) + +if(WINDOWS_STORE OR (DEFINED XBOX_CONSOLE_TARGET)) + set(CMAKE_TRY_COMPILE_TARGET_TYPE "STATIC_LIBRARY") +endif() + +project (DirectXTK12 + VERSION ${DIRECTXTK12_VERSION} + DESCRIPTION "DirectX Tool Kit for DirectX 12" + HOMEPAGE_URL "https://go.microsoft.com/fwlink/?LinkID=615561" + LANGUAGES CXX) + +option(BUILD_XAUDIO_WIN10 "Build for XAudio 2.9" ON) +option(BUILD_XAUDIO_REDIST "Build for XAudio2Redist" OFF) + +option(BUILD_GAMEINPUT "Build for GameInput" OFF) +option(BUILD_WGI "Build for Windows.Gaming.Input" OFF) +option(BUILD_XINPUT "Build for XInput" OFF) + +# https://devblogs.microsoft.com/cppblog/spectre-mitigations-in-msvc/ +option(ENABLE_SPECTRE_MITIGATION "Build using /Qspectre for MSVC" OFF) + +option(DISABLE_MSVC_ITERATOR_DEBUGGING "Disable iterator debugging in Debug configurations with the MSVC CRT" OFF) + +option(ENABLE_CODE_ANALYSIS "Use Static Code Analysis on build" OFF) + +option(USE_PREBUILT_SHADERS "Use externally built HLSL shaders" OFF) + +option(BUILD_DXIL_SHADERS "Use DXC Shader Model 6 for shaders" ON) + +option(NO_WCHAR_T "Use legacy wide-character as unsigned short" OFF) + +option(BUILD_FUZZING "Build for fuzz testing" OFF) + +option(BUILD_MIXED_DX11 "Support linking with DX11 version of toolkit" OFF) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/CMake") +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/CMake") +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/CMake") + +if(DEFINED VCPKG_TARGET_ARCHITECTURE) + set(DIRECTX_ARCH ${VCPKG_TARGET_ARCHITECTURE}) +elseif(CMAKE_GENERATOR_PLATFORM MATCHES "^[Ww][Ii][Nn]32$") + set(DIRECTX_ARCH x86) +elseif(CMAKE_GENERATOR_PLATFORM MATCHES "^[Xx]64$") + set(DIRECTX_ARCH x64) +elseif(CMAKE_GENERATOR_PLATFORM MATCHES "^[Aa][Rr][Mm]$") + set(DIRECTX_ARCH arm) +elseif(CMAKE_GENERATOR_PLATFORM MATCHES "^[Aa][Rr][Mm]64$") + set(DIRECTX_ARCH arm64) +endif() + +if(XBOX_CONSOLE_TARGET STREQUAL "scarlett") + set(BUILD_GAMEINPUT ON) + set(BUILD_SCARLETT_SHADERS ON) + set(BUILD_XAUDIO_REDIST OFF) +elseif(XBOX_CONSOLE_TARGET STREQUAL "xboxone") + set(BUILD_GAMEINPUT ON) + set(BUILD_XBOXONE_SHADERS ON) + set(BUILD_XAUDIO_REDIST OFF) +elseif(XBOX_CONSOLE_TARGET STREQUAL "durango") + set(BUILD_GAMEINPUT OFF) + set(BUILD_WGI OFF) + set(BUILD_XINPUT OFF) + set(BUILD_XBOXONE_SHADERS ON) + set(BUILD_DXIL_SHADERS OFF) + set(BUILD_XAUDIO_WIN10 OFF) + set(BUILD_XAUDIO_WIN8 ON) +elseif(WINDOWS_STORE) + set(BUILD_GAMEINPUT OFF) + set(BUILD_WGI ON) + set(BUILD_XAUDIO_REDIST OFF) +endif() + +include(GNUInstallDirs) + +#--- Library +set(LIBRARY_HEADERS + Inc/BufferHelpers.h + Inc/CommonStates.h + Inc/DDSTextureLoader.h + Inc/DescriptorHeap.h + Inc/DirectXHelpers.h + Inc/Effects.h + Inc/EffectPipelineStateDescription.h + Inc/GeometricPrimitive.h + Inc/GraphicsMemory.h + Inc/Model.h + Inc/PostProcess.h + Inc/PrimitiveBatch.h + Inc/RenderTargetState.h + Inc/ResourceUploadBatch.h + Inc/ScreenGrab.h + Inc/SpriteBatch.h + Inc/SpriteFont.h + Inc/VertexTypes.h + Inc/WICTextureLoader.h) + +set(LIBRARY_SOURCES + Src/AlphaTestEffect.cpp + Src/BasicEffect.cpp + Src/BasicPostProcess.cpp + Src/BufferHelpers.cpp + Src/CommonStates.cpp + Src/d3dx12.h + Src/DDSTextureLoader.cpp + Src/DebugEffect.cpp + Src/DescriptorHeap.cpp + Src/DirectXHelpers.cpp + Src/DualPostProcess.cpp + Src/DualTextureEffect.cpp + Src/EffectCommon.cpp + Src/EffectCommon.h + Src/EffectFactory.cpp + Src/EffectPipelineStateDescription.cpp + Src/EffectTextureFactory.cpp + Src/EnvironmentMapEffect.cpp + Src/GeometricPrimitive.cpp + Src/GraphicsMemory.cpp + Src/LinearAllocator.cpp + Src/LinearAllocator.h + Src/Model.cpp + Src/ModelLoadCMO.cpp + Src/ModelLoadSDKMESH.cpp + Src/ModelLoadVBO.cpp + Src/NormalMapEffect.cpp + Src/PBREffect.cpp + Src/PBREffectFactory.cpp + Src/pch.h + Src/PrimitiveBatch.cpp + Src/ResourceUploadBatch.cpp + Src/ScreenGrab.cpp + Src/SkinnedEffect.cpp + Src/SpriteBatch.cpp + Src/SpriteFont.cpp + Src/ToneMapPostProcess.cpp + Src/VertexTypes.cpp + Src/WICTextureLoader.cpp) + +set(SHADER_SOURCES + Src/Shaders/AlphaTestEffect.fx + Src/Shaders/BasicEffect.fx + Src/Shaders/DebugEffect.fx + Src/Shaders/DualTextureEffect.fx + Src/Shaders/EnvironmentMapEffect.fx + Src/Shaders/GenerateMips.hlsl + Src/Shaders/NormalMapEffect.fx + Src/Shaders/PBREffect.fx + Src/Shaders/PostProcess.fx + Src/Shaders/RootSig.fxh + Src/Shaders/SkinnedEffect.fx + Src/Shaders/SpriteEffect.fx + Src/Shaders/ToneMap.fx) + +# These source files are identical in both DX11 and DX12 version. +if(NOT BUILD_MIXED_DX11) + set(LIBRARY_HEADERS ${LIBRARY_HEADERS} + Inc/GamePad.h + Inc/Keyboard.h + Inc/Mouse.h + Inc/SimpleMath.h + Inc/SimpleMath.inl) + + set(LIBRARY_SOURCES ${LIBRARY_SOURCES} + Src/BinaryReader.cpp + Src/GamePad.cpp + Src/Geometry.cpp + Src/Keyboard.cpp + Src/Mouse.cpp + Src/SimpleMath.cpp) +endif() + +set(LIBRARY_SOURCES ${LIBRARY_SOURCES} + Src/AlignedNew.h + Src/Bezier.h + Src/BinaryReader.h + Src/DDS.h + Src/DemandCreate.h + Src/Geometry.h + Src/LoaderHelpers.h + Src/PlatformHelpers.h + Src/SDKMesh.h + Src/SharedResourcePool.h + Src/vbo.h + Src/TeapotData.inc) + +set(SHADER_SOURCES ${SHADER_SOURCES} + Src/Shaders/Common.fxh + Src/Shaders/Lighting.fxh + Src/Shaders/PBRCommon.fxh + Src/Shaders/PixelPacking_Velocity.hlsli + Src/Shaders/Skinning.fxh + Src/Shaders/Structures.fxh + Src/Shaders/Utilities.fxh) + +if(MINGW) + set(BUILD_XAUDIO_WIN10 OFF) +endif() + +if(WINDOWS_STORE OR BUILD_XAUDIO_WIN10 OR BUILD_XAUDIO_WIN8 OR BUILD_XAUDIO_REDIST) + set(LIBRARY_HEADERS ${LIBRARY_HEADERS} + Inc/Audio.h) + + set(LIBRARY_SOURCES ${LIBRARY_SOURCES} + Audio/AudioEngine.cpp + Audio/DynamicSoundEffectInstance.cpp + Audio/SoundCommon.cpp + Audio/SoundCommon.h + Audio/SoundEffect.cpp + Audio/SoundEffectInstance.cpp + Audio/SoundStreamInstance.cpp + Audio/WaveBank.cpp + Audio/WaveBankReader.cpp + Audio/WaveBankReader.h + Audio/WAVFileReader.cpp + Audio/WAVFileReader.h) +endif() + +if(NOT COMPILED_SHADERS) + if(USE_PREBUILT_SHADERS) + message(FATAL_ERROR "ERROR: Using prebuilt shaders requires the COMPILED_SHADERS variable is set.") + endif() + set(COMPILED_SHADERS ${CMAKE_CURRENT_BINARY_DIR}/Shaders/Compiled) + file(MAKE_DIRECTORY ${COMPILED_SHADERS}) +else() + file(TO_CMAKE_PATH ${COMPILED_SHADERS} COMPILED_SHADERS) +endif() + +set(LIBRARY_SOURCES ${LIBRARY_SOURCES} + ${COMPILED_SHADERS}/SpriteEffect_SpriteVertexShader.inc) + +if(BUILD_SCARLETT_SHADERS) + message(STATUS "Using Shader Model 6 for Xbox Series X|S for shaders") + set(ShaderOpts gxdk scarlett) +elseif(BUILD_XBOXONE_SHADERS) + if(BUILD_DXIL_SHADERS) + message(STATUS "Using Shader Model 6 for Xbox One for shaders") + set(ShaderOpts gxdk) + else() + message(STATUS "Using Shader Model 5.1 for Xbox One for shaders") + set(ShaderOpts xbox) + endif() +elseif(BUILD_DXIL_SHADERS) + message(STATUS "Using Shader Model 6.0 (DXC.EXE) for shaders.") + set(ShaderOpts dxil) +else() + message(STATUS "Using Shader Model 5.1 (FXC.EXE) for shaders.") + set(ShaderOpts "") +endif() + +if(NOT USE_PREBUILT_SHADERS) + if(BUILD_DXIL_SHADERS AND VCPKG_TOOLCHAIN AND (NOT BUILD_SCARLETT_SHADERS) AND (NOT BUILD_XBOXONE_SHADERS)) + message(STATUS "Using VCPKG for DirectXShaderCompiler (${VCPKG_HOST_TRIPLET}).") + find_program(DIRECTX_DXC_TOOL DXC.EXE + REQUIRED NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH NO_DEFAULT_PATH + HINTS ${DIRECTX_DXC_PATH}) + endif() + add_custom_command( + OUTPUT "${COMPILED_SHADERS}/SpriteEffect_SpriteVertexShader.inc" + MAIN_DEPENDENCY "${PROJECT_SOURCE_DIR}/Src/Shaders/CompileShaders.cmd" + DEPENDS ${SHADER_SOURCES} + COMMENT "Generating HLSL shaders..." + COMMAND ${CMAKE_COMMAND} -E env CompileShadersOutput="${COMPILED_SHADERS}" $<$:DirectXShaderCompiler=${DIRECTX_DXC_TOOL}> CompileShaders.cmd ARGS ${ShaderOpts} > "${COMPILED_SHADERS}/compileshaders.log" + WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/Src/Shaders" + USES_TERMINAL) +endif() + +add_library(${PROJECT_NAME} STATIC ${LIBRARY_SOURCES} ${LIBRARY_HEADERS}) + +target_include_directories(${PROJECT_NAME} PRIVATE ${COMPILED_SHADERS} Src) + +if(NOT MINGW) + target_precompile_headers(${PROJECT_NAME} PRIVATE Src/pch.h) +endif() + +source_group(Audio REGULAR_EXPRESSION Audio/*.*) +source_group(Inc REGULAR_EXPRESSION Inc/*.*) +source_group(Src REGULAR_EXPRESSION Src/*.*) + +target_include_directories(${PROJECT_NAME} PUBLIC + $ + $) + +target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_11) + +if(WINDOWS_STORE OR BUILD_XAUDIO_WIN10 OR BUILD_XAUDIO_WIN8 OR BUILD_XAUDIO_REDIST) + target_include_directories(${PROJECT_NAME} PRIVATE Audio) +endif() + +if(MINGW) + find_package(directxmath CONFIG REQUIRED) + find_package(directx-headers CONFIG REQUIRED) +else() + find_package(directxmath CONFIG QUIET) + find_package(directx-headers CONFIG QUIET) +endif() + +if(directxmath_FOUND) + message(STATUS "Using DirectXMath package") + target_link_libraries(${PROJECT_NAME} PUBLIC Microsoft::DirectXMath) +endif() + +if(directx-headers_FOUND) + message(STATUS "Using DirectX-Headers package") + target_link_libraries(${PROJECT_NAME} PUBLIC Microsoft::DirectX-Headers) + target_compile_definitions(${PROJECT_NAME} PUBLIC USING_DIRECTX_HEADERS) +endif() + +if(BUILD_XAUDIO_REDIST AND (NOT BUILD_XAUDIO_WIN10) AND (NOT BUILD_XAUDIO_WIN8) AND (NOT WINDOWS_STORE)) + message(STATUS "Using XAudio2Redist for DirectX Tool Kit for Audio.") + find_package(xaudio2redist CONFIG REQUIRED) + target_link_libraries(${PROJECT_NAME} PUBLIC Microsoft::XAudio2Redist) + target_compile_definitions(${PROJECT_NAME} PUBLIC USING_XAUDIO2_REDIST) +endif() + +include(CheckIncludeFileCXX) + +if(BUILD_GAMEINPUT) + message(STATUS "Using GameInput for GamePad/Keyboard/Mouse.") + set(CMAKE_REQUIRED_QUIET ON) + CHECK_INCLUDE_FILE_CXX(GameInput.h GAMEINPUT_HEADER) + if(NOT GAMEINPUT_HEADER) + message(FATAL_ERROR "Microsoft GDK required to build GameInput. See https://aka.ms/gdk") + endif() + target_compile_definitions(${PROJECT_NAME} PUBLIC USING_GAMEINPUT) +elseif(BUILD_WGI) + message(STATUS "Using Windows.Gaming.Input for GamePad.") + target_compile_definitions(${PROJECT_NAME} PUBLIC USING_WINDOWS_GAMING_INPUT) +elseif(BUILD_XINPUT) + message(STATUS "Using XInput for GamePad.") + target_compile_definitions(${PROJECT_NAME} PUBLIC USING_XINPUT) +endif() + +if(DEFINED XBOX_CONSOLE_TARGET) + message(STATUS "Building for Xbox Console Target: ${XBOX_CONSOLE_TARGET}") + set(CMAKE_REQUIRED_QUIET ON) + if(XBOX_CONSOLE_TARGET STREQUAL "durango") + CHECK_INCLUDE_FILE_CXX(xdk.h XDKLegacy_HEADER) + if(NOT XDKLegacy_HEADER) + message(FATAL_ERROR "Legacy Xbox One XDK required to build for Durango.") + endif() + target_compile_definitions(${PROJECT_NAME} PUBLIC WINAPI_FAMILY=WINAPI_FAMILY_TV_TITLE _XBOX_ONE _TITLE MONOLITHIC=1) + else() + CHECK_INCLUDE_FILE_CXX(gxdk.h GXDK_HEADER) + if(NOT GXDK_HEADER) + message(FATAL_ERROR "Microsoft GDK with Xbox Extensions required to build for Xbox. See https://aka.ms/gdkx") + endif() + target_compile_definitions(${PROJECT_NAME} PUBLIC WINAPI_FAMILY=WINAPI_FAMILY_GAMES) + if(XBOX_CONSOLE_TARGET STREQUAL "scarlett") + CHECK_INCLUDE_FILE_CXX(d3d12_xs.h D3D12XS_HEADER) + if(NOT D3D12XS_HEADER) + message(FATAL_ERROR "Microsoft GDK with Xbox Extensions environment needs to be set for Xbox Series X|S.") + endif() + target_compile_definitions(${PROJECT_NAME} PUBLIC _GAMING_XBOX _GAMING_XBOX_SCARLETT) + elseif(XBOX_CONSOLE_TARGET STREQUAL "xboxone") + CHECK_INCLUDE_FILE_CXX(d3d12_x.h D3D12X_HEADER) + if(NOT D3D12X_HEADER) + message(FATAL_ERROR "Microsoft GDK with Xbox Extensions environment needs to be set for Xbox One.") + endif() + target_compile_definitions(${PROJECT_NAME} PUBLIC _GAMING_XBOX _GAMING_XBOX_XBOXONE) + else() + message(FATAL_ERROR "Unknown XBOX_CONSOLE_TARGET") + endif() + endif() +elseif(WINDOWS_STORE) + target_compile_definitions(${PROJECT_NAME} PUBLIC WINAPI_FAMILY=WINAPI_FAMILY_APP) +endif() + +#--- Package +include(CMakePackageConfigHelpers) + +string(TOLOWER ${PROJECT_NAME} PACKAGE_NAME) + +write_basic_package_version_file( + ${PACKAGE_NAME}-config-version.cmake + VERSION ${DIRECTXTK12_VERSION} + COMPATIBILITY AnyNewerVersion) + +install(TARGETS ${PROJECT_NAME} + EXPORT ${PROJECT_NAME}-targets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + +configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/build/${PROJECT_NAME}-config.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/${PACKAGE_NAME}-config.cmake + INSTALL_DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/${PACKAGE_NAME}) + +install(EXPORT ${PROJECT_NAME}-targets + FILE ${PROJECT_NAME}-targets.cmake + NAMESPACE Microsoft:: + DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/${PACKAGE_NAME}) + +install(FILES ${LIBRARY_HEADERS} + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/directxtk12) + +install(FILES + ${CMAKE_CURRENT_BINARY_DIR}/${PACKAGE_NAME}-config.cmake + ${CMAKE_CURRENT_BINARY_DIR}/${PACKAGE_NAME}-config-version.cmake + DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/${PACKAGE_NAME}) + +# Model uses dynamic_cast, so we need /GR (Enable RTTI) +if(MSVC) + target_compile_options(${PROJECT_NAME} PRIVATE /Wall /GR /fp:fast "$<$>:/guard:cf>") + target_link_options(${PROJECT_NAME} PRIVATE /DYNAMICBASE /NXCOMPAT /INCREMENTAL:NO) + + if((CMAKE_SIZEOF_VOID_P EQUAL 4) AND (NOT ${DIRECTX_ARCH} MATCHES "^arm")) + target_link_options(${PROJECT_NAME} PRIVATE /SAFESEH) + endif() + + if(ENABLE_SPECTRE_MITIGATION + AND (MSVC_VERSION GREATER_EQUAL 1913) + AND (NOT WINDOWS_STORE) + AND (NOT (CMAKE_CXX_COMPILER_ID MATCHES "Clang"))) + message(STATUS "Building Spectre-mitigated libraries.") + target_compile_options(${PROJECT_NAME} PRIVATE "/Qspectre") + endif() + + if((MSVC_VERSION GREATER_EQUAL 1924) + AND ((NOT (CMAKE_CXX_COMPILER_ID MATCHES "Clang")) OR (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 16.0))) + target_compile_options(${PROJECT_NAME} PRIVATE /ZH:SHA_256) + endif() + + if((MSVC_VERSION GREATER_EQUAL 1928) + AND (CMAKE_SIZEOF_VOID_P EQUAL 8) + AND ((NOT (CMAKE_CXX_COMPILER_ID MATCHES "Clang")) OR (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 13.0))) + target_compile_options(${PROJECT_NAME} PRIVATE "$<$>:/guard:ehcont>") + target_link_options(${PROJECT_NAME} PRIVATE "$<$>:/guard:ehcont>") + endif() + + if(NO_WCHAR_T) + message(STATUS "Using non-native wchar_t as unsigned short") + target_compile_options(${PROJECT_NAME} PRIVATE "/Zc:wchar_t-") + endif() +else() + target_compile_definitions(${PROJECT_NAME} PRIVATE $,_DEBUG,NDEBUG>) +endif() + +if(XBOX_CONSOLE_TARGET STREQUAL "scarlett") + target_compile_options(${PROJECT_NAME} PRIVATE $,/favor:AMD64 /arch:AVX2,-march=znver2>) +elseif(XBOX_CONSOLE_TARGET MATCHES "(xboxone|durango)") + target_compile_options(${PROJECT_NAME} PRIVATE $,/favor:AMD64 /arch:AVX,-march=btver2>) +elseif(NOT (${DIRECTX_ARCH} MATCHES "^arm")) + if(CMAKE_SIZEOF_VOID_P EQUAL 4) + set(ARCH_SSE2 $<$:/arch:SSE2> $<$>:-msse2>) + else() + set(ARCH_SSE2 $<$>:-msse2>) + endif() + + target_compile_options(${PROJECT_NAME} PRIVATE ${ARCH_SSE2}) +endif() + +if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wpedantic -Wextra) + if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 16.0) + target_compile_options(${PROJECT_NAME} PRIVATE "-Wno-unsafe-buffer-usage") + endif() +elseif(MINGW) + target_compile_options(${PROJECT_NAME} PRIVATE -Wno-ignored-attributes) +elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") + target_compile_options(${PROJECT_NAME} PRIVATE /sdl /permissive- /JMC- /Zc:__cplusplus /Zc:inline) + + if(ENABLE_CODE_ANALYSIS) + target_compile_options(${PROJECT_NAME} PRIVATE /analyze) + endif() + + if(CMAKE_INTERPROCEDURAL_OPTIMIZATION) + message(STATUS "Building using Whole Program Optimization") + target_compile_options(${PROJECT_NAME} PRIVATE /Gy /Gw) + endif() + + if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19.26) + target_compile_options(${PROJECT_NAME} PRIVATE /Zc:preprocessor /wd5104 /wd5105) + endif() + + if((CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19.27) AND (NOT (${DIRECTX_ARCH} MATCHES "^arm"))) + target_link_options(${PROJECT_NAME} PRIVATE /CETCOMPAT) + endif() + + if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19.28) + target_compile_options(${PROJECT_NAME} PRIVATE /Zc:lambda) + endif() + + if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19.34) + target_compile_options(${PROJECT_NAME} PRIVATE /wd5262) + endif() + + if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19.35) + if(NOT (DEFINED XBOX_CONSOLE_TARGET)) + target_compile_options(${PROJECT_NAME} PRIVATE /Zc:checkGwOdr $<$:/Zc:templateScope>) + endif() + endif() + + if (BUILD_FUZZING AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19.32) + target_compile_options(${PROJECT_NAME} PRIVATE /fsanitize=address /fsanitize-coverage=inline-8bit-counters /fsanitize-coverage=edge /fsanitize-coverage=trace-cmp /fsanitize-coverage=trace-div) + target_link_libraries(${PROJECT_NAME} PRIVATE sancov.lib) + endif() +endif() + +if(WIN32) + if (XBOX_CONSOLE_TARGET STREQUAL "durango") + set(WINVER 0x0602) + else() + set(WINVER 0x0A00) + endif() + + target_compile_definitions(${PROJECT_NAME} PRIVATE _UNICODE UNICODE _WIN32_WINNT=${WINVER}) + + if(WINDOWS_STORE OR BUILD_XAUDIO_WIN10) + message(STATUS "Using DirectX Tool Kit for Audio on XAudio 2.9 (Windows 10/Windows 11).") + endif() + + if(DISABLE_MSVC_ITERATOR_DEBUGGING) + target_compile_definitions(${PROJECT_NAME} PRIVATE _ITERATOR_DEBUG_LEVEL=0) + endif() +endif() + +set_property(DIRECTORY PROPERTY VS_STARTUP_PROJECT ${PROJECT_NAME}) + +#--- Test suite +include(CTest) +if(BUILD_TESTING AND WIN32 AND (NOT WINDOWS_STORE) AND (NOT (DEFINED XBOX_CONSOLE_TARGET)) + AND (EXISTS "${CMAKE_CURRENT_LIST_DIR}/Tests/CMakeLists.txt")) + enable_testing() + add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/Tests) +elseif(BUILD_FUZZING AND WIN32 AND (NOT WINDOWS_STORE) AND (NOT (DEFINED XBOX_CONSOLE_TARGET)) + AND (EXISTS "${CMAKE_CURRENT_LIST_DIR}/Tests/fuzzloaders/CMakeLists.txt")) + message(STATUS "Building for fuzzing") + add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/Tests/fuzzloaders) +endif() diff --git a/Common/DirectXTK12/CMakePresets.json b/Common/DirectXTK12/CMakePresets.json new file mode 100644 index 0000000..cdf44a5 --- /dev/null +++ b/Common/DirectXTK12/CMakePresets.json @@ -0,0 +1,294 @@ +{ + "version": 2, + "configurePresets": [ + { + "name": "base", + "displayName": "Basic Config", + "description": "Basic build using Ninja generator", + "generator": "Ninja", + "hidden": true, + "binaryDir": "${sourceDir}/out/build/${presetName}", + "cacheVariables": { "CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}" } + }, + + { + "name": "x64", + "architecture": { + "value": "x64", + "strategy": "external" + }, + "cacheVariables": { "DIRECTX_ARCH": "x64" }, + "hidden": true + }, + { + "name": "x86", + "architecture": { + "value": "x86", + "strategy": "external" + }, + "cacheVariables": { "DIRECTX_ARCH": "x86" }, + "hidden": true + }, + { + "name": "ARM64", + "architecture": { + "value": "arm64", + "strategy": "external" + }, + "cacheVariables": { "DIRECTX_ARCH": "arm64" }, + "hidden": true + }, + + { + "name": "Debug", + "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug" }, + "hidden": true + }, + { + "name": "Release", + "cacheVariables": + { + "CMAKE_BUILD_TYPE": "RelWithDebInfo", + "CMAKE_INTERPROCEDURAL_OPTIMIZATION": true + }, + "hidden": true + }, + + { + "name": "MSVC", + "hidden": true, + "cacheVariables": { + "CMAKE_CXX_COMPILER": "cl.exe" + }, + "toolset": { + "value": "host=x64", + "strategy": "external" + } + }, + { + "name": "Clang", + "hidden": true, + "cacheVariables": { + "CMAKE_CXX_COMPILER": "clang-cl.exe" + }, + "toolset": { + "value": "host=x64", + "strategy": "external" + } + }, + { + "name": "GNUC", + "hidden": true, + "cacheVariables": { + "CMAKE_CXX_COMPILER": "g++.exe" + }, + "toolset": { + "value": "host=x64", + "strategy": "external" + } + }, + + { + "name": "UWP", + "cacheVariables": { + "CMAKE_SYSTEM_NAME": "WindowsStore", + "CMAKE_SYSTEM_VERSION": "10.0" + }, + "hidden": true + }, + { + "name": "GDK", + "cacheVariables": { + "BUILD_XAUDIO_WIN10": true, + "BUILD_GAMEINPUT": true + }, + "hidden": true + }, + { + "name": "Scarlett", + "cacheVariables": { + "XBOX_CONSOLE_TARGET": "scarlett", + "BUILD_TESTING": false + }, + "hidden": true + }, + { + "name": "XboxOne", + "cacheVariables": { + "XBOX_CONSOLE_TARGET": "xboxone", + "BUILD_TESTING": false + }, + "hidden": true + }, + { + "name": "Durango", + "cacheVariables": { + "XBOX_CONSOLE_TARGET": "durango", + "BUILD_TESTING": false + }, + "hidden": true + }, + { + "name": "VCPKG", + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": { + "value": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", + "type": "FILEPATH" + } + }, + "hidden": true + }, + { + "name": "X64_X64", + "hidden": true, + "cacheVariables": { + "VCPKG_TARGET_TRIPLET": "x64-windows", + "VCPKG_HOST_TRIPLET": "x64-windows", + "DIRECTX_DXC_PATH": "$env{VCPKG_ROOT}/installed/x64-windows/tools/directx-dxc" + } + }, + { + "name": "X64_X86", + "hidden": true, + "cacheVariables": { + "VCPKG_TARGET_TRIPLET": "x86-windows", + "VCPKG_HOST_TRIPLET": "x64-windows", + "DIRECTX_DXC_PATH": "$env{VCPKG_ROOT}/installed/x64-windows/tools/directx-dxc" + } + }, + { + "name": "X64_ARM64", + "hidden": true, + "cacheVariables": { + "VCPKG_TARGET_TRIPLET": "arm64-windows", + "VCPKG_HOST_TRIPLET": "x64-windows", + "DIRECTX_DXC_PATH": "$env{VCPKG_ROOT}/installed/x64-windows/tools/directx-dxc" + } + }, + { + "name": "ARM64_ARM64", + "hidden": true, + "cacheVariables": { + "VCPKG_TARGET_TRIPLET": "arm64-windows", + "VCPKG_HOST_TRIPLET": "arm64-windows", + "DIRECTX_DXC_PATH": "$env{VCPKG_ROOT}/installed/arm64-windows/tools/directx-dxc" + } + }, + { + "name": "MinGW32", + "hidden": true, + "environment": { + "PATH": "$penv{PATH};c:/mingw32/bin;c:/mingw32/libexec/gcc/i686-w64-mingw32/12.2.0" + }, + "cacheVariables": { + "VCPKG_TARGET_TRIPLET": "x86-mingw-static", + "VCPKG_HOST_TRIPLET": "x64-mingw-static", + "DIRECTX_DXC_PATH": "$env{VCPKG_ROOT}/installed/x64-mingw-static/tools/directx-dxc" + } + }, + { + "name": "MinGW64", + "hidden": true, + "environment": { + "PATH": "$penv{PATH};c:/mingw64/bin;c:/mingw64/libexec/gcc/x86_64-w64-mingw32/12.2.0" + }, + "cacheVariables": { + "VCPKG_TARGET_TRIPLET": "x64-mingw-static", + "VCPKG_HOST_TRIPLET": "x64-mingw-static", + "DIRECTX_DXC_PATH": "$env{VCPKG_ROOT}/installed/x64-mingw-static/tools/directx-dxc" + } + }, + { + "name": "XAudio2Redist", + "cacheVariables": { + "BUILD_XAUDIO_WIN10": false, + "BUILD_XAUDIO_REDIST": true + }, + "hidden": true + }, + { + "name": "Fuzzing", + "cacheVariables": { + "BUILD_FUZZING": true, + "BUILD_TESTING": false + }, + "hidden": true + }, + + { "name": "x64-Debug" , "description": "MSVC for x64 (Debug) for Windows 10", "inherits": [ "base", "x64", "Debug", "MSVC" ] }, + { "name": "x64-Release" , "description": "MSVC for x64 (Release) for Windows 10", "inherits": [ "base", "x64", "Release", "MSVC" ] }, + { "name": "x86-Debug" , "description": "MSVC for x86 (Debug) for Windows 10", "inherits": [ "base", "x86", "Debug", "MSVC" ] }, + { "name": "x86-Release" , "description": "MSVC for x86 (Release) for Windows 10", "inherits": [ "base", "x86", "Release", "MSVC" ] }, + { "name": "arm64-Debug" , "description": "MSVC for ARM64 (Debug) for Windows 10", "inherits": [ "base", "ARM64", "Debug", "MSVC" ] }, + { "name": "arm64-Release" , "description": "MSVC for ARM64 (Release) for Windows 10", "inherits": [ "base", "ARM64", "Release", "MSVC" ] }, + + { "name": "x64-Debug-UWP" , "description": "MSVC for x64 (Debug) for UWP", "inherits": [ "base", "x64", "Debug", "MSVC", "UWP" ] }, + { "name": "x64-Release-UWP" , "description": "MSVC for x64 (Release) for UWP", "inherits": [ "base", "x64", "Release", "MSVC", "UWP" ] }, + { "name": "x86-Debug-UWP" , "description": "MSVC for x86 (Debug) for UWP", "inherits": [ "base", "x86", "Debug", "MSVC", "UWP" ] }, + { "name": "x86-Release-UWP" , "description": "MSVC for x86 (Release) for UWP", "inherits": [ "base", "x86", "Release", "MSVC", "UWP" ] }, + { "name": "arm64-Debug-UWP" , "description": "MSVC for ARM64 (Debug) for UWP", "inherits": [ "base", "ARM64", "Debug", "MSVC", "UWP" ] }, + { "name": "arm64-Release-UWP", "description": "MSVC for ARM64 (Release) for UWP", "inherits": [ "base", "ARM64", "Release", "MSVC", "UWP" ] }, + + { "name": "x64-Debug-GDK" , "description": "MSVC for x64 (Debug) with Microsoft GDK", "inherits": [ "base", "x64", "Debug", "MSVC", "GDK" ] }, + { "name": "x64-Release-GDK" , "description": "MSVC for x64 (Release) with Microsoft GDK", "inherits": [ "base", "x64", "Release", "MSVC", "GDK" ] }, + + { "name": "x64-Debug-Scarlett" , "description": "MSVC for x64 (Debug) for Xbox Series X|S", "inherits": [ "base", "x64", "Debug", "MSVC", "Scarlett" ] }, + { "name": "x64-Release-Scarlett" , "description": "MSVC for x64 (Release) for Xbox Series X|S", "inherits": [ "base", "x64", "Release", "MSVC", "Scarlett" ] }, + + { "name": "x64-Debug-XboxOne" , "description": "MSVC for x64 (Debug) for Xbox One", "inherits": [ "base", "x64", "Debug", "MSVC", "XboxOne" ] }, + { "name": "x64-Release-XboxOne" , "description": "MSVC for x64 (Release) for Xbox One", "inherits": [ "base", "x64", "Release", "MSVC", "XboxOne" ] }, + + { "name": "x64-Debug-Durango" , "description": "MSVC for x64 (Debug) for legacy Xbox One XDK", "inherits": [ "base", "x64", "Debug", "MSVC", "Durango" ] }, + { "name": "x64-Release-Durango" , "description": "MSVC for x64 (Release) for legacy Xbox One XDK", "inherits": [ "base", "x64", "Release", "MSVC", "Durango" ] }, + + { "name": "x64-Debug-VCPKG" , "description": "MSVC for x64 (Debug)", "inherits": [ "base", "x64", "Debug", "MSVC", "VCPKG", "XAudio2Redist", "X64_X64" ] }, + { "name": "x64-Release-VCPKG" , "description": "MSVC for x64 (Release)", "inherits": [ "base", "x64", "Release", "MSVC", "VCPKG", "XAudio2Redist", "X64_X64" ] }, + { "name": "x86-Debug-VCPKG" , "description": "MSVC for x86 (Debug)", "inherits": [ "base", "x86", "Debug", "MSVC", "VCPKG", "XAudio2Redist", "X64_X86" ] }, + { "name": "x86-Release-VCPKG" , "description": "MSVC for x86 (Release)", "inherits": [ "base", "x86", "Release", "MSVC", "VCPKG", "XAudio2Redist", "X64_X86" ] }, + { "name": "arm64-Debug-VCPKG" , "description": "MSVC for ARM64 (Debug)", "inherits": [ "base", "ARM64", "Debug", "MSVC", "VCPKG", "X64_ARM64" ] }, + { "name": "arm64-Release-VCPKG" , "description": "MSVC for ARM64 (Release)", "inherits": [ "base", "ARM64", "Release", "MSVC", "VCPKG", "X64_ARM64" ] }, + { "name": "arm64-Native-Debug-VCPKG" , "description": "MSVC for ARM64 Native (Debug)", "inherits": [ "base", "ARM64", "Debug", "MSVC", "VCPKG", "ARM64_ARM64" ] }, + { "name": "arm64-Native-Release-VCPKG", "description": "MSVC for ARM64 Native (Release)", "inherits": [ "base", "ARM64", "Release", "MSVC", "VCPKG", "ARM64_ARM64" ] }, + + { "name": "x64-Debug-Clang" , "description": "Clang/LLVM for x64 (Debug) for Windows 10", "inherits": [ "base", "x64", "Debug", "Clang" ] }, + { "name": "x64-Release-Clang" , "description": "Clang/LLVM for x64 (Release) for Windows 10", "inherits": [ "base", "x64", "Release", "Clang" ] }, + { "name": "x86-Debug-Clang" , "description": "Clang/LLVM for x86 (Debug) for Windows 10", "inherits": [ "base", "x86", "Debug", "Clang" ], "environment": { "CXXFLAGS": "-m32" } }, + { "name": "x86-Release-Clang" , "description": "Clang/LLVM for x86 (Debug) for Windows 10", "inherits": [ "base", "x86", "Release", "Clang" ], "environment": { "CXXFLAGS": "-m32" } }, + { "name": "arm64-Debug-Clang" , "description": "Clang/LLVM for AArch64 (Debug) for Windows 10", "inherits": [ "base", "ARM64", "Debug", "Clang" ], "environment": { "CXXFLAGS": "--target=arm64-pc-windows-msvc" } }, + { "name": "arm64-Release-Clang", "description": "Clang/LLVM for AArch64 (Debug) for Windows 10", "inherits": [ "base", "ARM64", "Release", "Clang" ], "environment": { "CXXFLAGS": "--target=arm64-pc-windows-msvc" } }, + + { "name": "x64-Debug-UWP-Clang" , "description": "Clang/LLVM for x64 (Debug) for UWP", "inherits": [ "base", "x64", "Debug", "Clang", "UWP" ] }, + { "name": "x64-Release-UWP-Clang" , "description": "Clang/LLVM for x64 (Release) for UWP", "inherits": [ "base", "x64", "Release", "Clang", "UWP" ] }, + { "name": "x86-Debug-UWP-Clang" , "description": "Clang/LLVM for x86 (Debug) for UWP", "inherits": [ "base", "x86", "Debug", "Clang", "UWP" ], "environment": { "CXXFLAGS": "-m32" } }, + { "name": "x86-Release-UWP-Clang" , "description": "Clang/LLVM for x86 (Release) for UWP", "inherits": [ "base", "x86", "Release", "Clang", "UWP" ], "environment": { "CXXFLAGS": "-m32" } }, + { "name": "arm64-Debug-UWP-Clang" , "description": "Clang/LLVM for AArch64 (Debug) for UWP", "inherits": [ "base", "ARM64", "Debug", "Clang", "UWP" ], "environment": { "CXXFLAGS": "--target=arm64-pc-windows-msvc" } }, + { "name": "arm64-Release-UWP-Clang", "description": "Clang/LLVM for AArch64 (Release) for UWP", "inherits": [ "base", "ARM64", "Release", "Clang", "UWP" ], "environment": { "CXXFLAGS": "--target=arm64-pc-windows-msvc" } }, + + { "name": "x64-Debug-MinGW" , "description": "MinG-W64 (Debug)", "inherits": [ "base", "x64", "Debug", "GNUC", "VCPKG", "XAudio2Redist", "MinGW64" ] }, + { "name": "x64-Release-MinGW", "description": "MinG-W64 (Release)", "inherits": [ "base", "x64", "Release", "GNUC", "VCPKG", "XAudio2Redist", "MinGW64" ] }, + { "name": "x86-Debug-MinGW" , "description": "MinG-W32 (Debug)", "inherits": [ "base", "x86", "Debug", "GNUC", "VCPKG", "XAudio2Redist", "MinGW32" ] }, + { "name": "x86-Release-MinGW", "description": "MinG-W32 (Release)", "inherits": [ "base", "x86", "Release", "GNUC", "VCPKG", "XAudio2Redist", "MinGW32" ] }, + + { "name": "x64-Fuzzing" , "description": "MSVC for x64 (Release) with ASan", "inherits": [ "base", "x64", "Release", "MSVC", "Fuzzing" ] } + ], + "testPresets": [ + { "name": "x64-Debug" , "configurePreset": "x64-Debug" }, + { "name": "x64-Release" , "configurePreset": "x64-Release" }, + { "name": "x86-Debug" , "configurePreset": "x86-Debug" }, + { "name": "x86-Release" , "configurePreset": "x86-Release" }, + { "name": "arm64-Debug" , "configurePreset": "arm64-Debug" }, + { "name": "arm64-Release", "configurePreset": "arm64-Release" }, + + { "name": "x64-Debug-Clang" , "configurePreset": "x64-Debug-Clang" }, + { "name": "x64-Release-Clang" , "configurePreset": "x64-Release-Clang" }, + { "name": "x86-Debug-Clang" , "configurePreset": "x86-Debug-Clang" }, + { "name": "x86-Release-Clang" , "configurePreset": "x86-Release-Clang" }, + { "name": "arm64-Debug-Clang" , "configurePreset": "arm64-Debug-Clang" }, + { "name": "arm64-Release-Clang", "configurePreset": "arm64-Release-Clang" }, + + { "name": "x64-Debug-MinGW" , "configurePreset": "x64-Debug-MinGW" }, + { "name": "x64-Release-MinGW" , "configurePreset": "x64-Release-MinGW" }, + { "name": "x86-Debug-MinGW" , "configurePreset": "x86-Debug-MinGW" }, + { "name": "x86-Release-MinGW" , "configurePreset": "x86-Release-MinGW" } + ] +} \ No newline at end of file diff --git a/Common/DirectXTK12/DirectXTK_Desktop_2022_Win10.sln b/Common/DirectXTK12/DirectXTK_Desktop_2022_Win10.sln new file mode 100644 index 0000000..9c7b8be --- /dev/null +++ b/Common/DirectXTK12/DirectXTK_Desktop_2022_Win10.sln @@ -0,0 +1,42 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 16.0.29609.76 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DirectXTK12", "DirectXTK_Desktop_2022_Win10.vcxproj", "{3E0E8608-CD9B-4C76-AF33-29CA38F2C9F0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{5C7C55AD-C54A-49B1-8259-D6DD0F6F4356}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM64 = Debug|ARM64 + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|ARM64 = Release|ARM64 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3E0E8608-CD9B-4C76-AF33-29CA38F2C9F0}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {3E0E8608-CD9B-4C76-AF33-29CA38F2C9F0}.Debug|ARM64.Build.0 = Debug|ARM64 + {3E0E8608-CD9B-4C76-AF33-29CA38F2C9F0}.Debug|x64.ActiveCfg = Debug|x64 + {3E0E8608-CD9B-4C76-AF33-29CA38F2C9F0}.Debug|x64.Build.0 = Debug|x64 + {3E0E8608-CD9B-4C76-AF33-29CA38F2C9F0}.Debug|x86.ActiveCfg = Debug|Win32 + {3E0E8608-CD9B-4C76-AF33-29CA38F2C9F0}.Debug|x86.Build.0 = Debug|Win32 + {3E0E8608-CD9B-4C76-AF33-29CA38F2C9F0}.Release|ARM64.ActiveCfg = Release|ARM64 + {3E0E8608-CD9B-4C76-AF33-29CA38F2C9F0}.Release|ARM64.Build.0 = Release|ARM64 + {3E0E8608-CD9B-4C76-AF33-29CA38F2C9F0}.Release|x64.ActiveCfg = Release|x64 + {3E0E8608-CD9B-4C76-AF33-29CA38F2C9F0}.Release|x64.Build.0 = Release|x64 + {3E0E8608-CD9B-4C76-AF33-29CA38F2C9F0}.Release|x86.ActiveCfg = Release|Win32 + {3E0E8608-CD9B-4C76-AF33-29CA38F2C9F0}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {3CC6BC6C-3DB1-4D71-8F4F-4FC99A6ABCC6} + EndGlobalSection +EndGlobal diff --git a/Common/DirectXTK12/DirectXTK_Desktop_2022_Win10.vcxproj b/Common/DirectXTK12/DirectXTK_Desktop_2022_Win10.vcxproj new file mode 100644 index 0000000..9cbcb88 --- /dev/null +++ b/Common/DirectXTK12/DirectXTK_Desktop_2022_Win10.vcxproj @@ -0,0 +1,480 @@ + + + + + Debug + ARM64 + + + Debug + Win32 + + + Debug + x64 + + + Release + ARM64 + + + Release + Win32 + + + Release + x64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Create + Create + Create + Create + Create + Create + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Document + + + + + Document + + + Document + + + Document + + + Document + + + + + Document + + + + + Document + + + + + Document + + + Document + + + + + Document + + + + + Document + + + + {3E0E8608-CD9B-4C76-AF33-29CA38F2C9F0} + Win32Proj + DirectXTK12 + DirectXTK12 + 10.0 + x64 + + + + StaticLibrary + true + v143 + Unicode + + + StaticLibrary + true + v143 + Unicode + + + StaticLibrary + true + v143 + Unicode + + + StaticLibrary + false + v143 + Unicode + + + StaticLibrary + false + v143 + Unicode + + + StaticLibrary + false + v143 + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + Bin\Desktop_2022_Win10\$(Platform)\$(Configuration)\ + Bin\Desktop_2022_Win10\$(Platform)\$(Configuration)\ + DirectXTK12 + + + Bin\Desktop_2022_Win10\$(Platform)\$(Configuration)\ + Bin\Desktop_2022_Win10\$(Platform)\$(Configuration)\ + DirectXTK12 + + + Bin\Desktop_2022_Win10\$(Platform)\$(Configuration)\ + Bin\Desktop_2022_Win10\$(Platform)\$(Configuration)\ + DirectXTK12 + + + Bin\Desktop_2022_Win10\$(Platform)\$(Configuration)\ + Bin\Desktop_2022_Win10\$(Platform)\$(Configuration)\ + DirectXTK12 + + + Bin\Desktop_2022_Win10\$(Platform)\$(Configuration)\ + Bin\Desktop_2022_Win10\$(Platform)\$(Configuration)\ + DirectXTK12 + + + Bin\Desktop_2022_Win10\$(Platform)\$(Configuration)\ + Bin\Desktop_2022_Win10\$(Platform)\$(Configuration)\ + DirectXTK12 + + + + Use + EnableAllWarnings + Disabled + _WIN32_WINNT=0x0A00;WIN32;_DEBUG;_LIB;_CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS;%(PreprocessorDefinitions) + pch.h + $(ProjectDir)Inc;$(ProjectDir)Src;$(ProjectDir)Src\Shaders\Compiled;%(AdditionalIncludeDirectories) + Fast + $(IntDir)$(TargetName).pdb + true + true + /Zc:__cplusplus %(AdditionalOptions) + ProgramDatabase + false + Level4 + + + 6.0 + true + /Fd "$(OutDir)%(Filename).pdb" %(AdditionalOptions) + + + Windows + true + + + + + Use + EnableAllWarnings + Disabled + _WIN32_WINNT=0x0A00;WIN32;_DEBUG;_LIB;_CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS;%(PreprocessorDefinitions) + pch.h + $(ProjectDir)Inc;$(ProjectDir)Src;$(ProjectDir)Src\Shaders\Compiled;%(AdditionalIncludeDirectories) + Fast + $(IntDir)$(TargetName).pdb + true + true + /Zc:__cplusplus %(AdditionalOptions) + ProgramDatabase + false + Level4 + + + 6.0 + true + /Fd "$(OutDir)%(Filename).pdb" %(AdditionalOptions) + + + Windows + true + + + + + Use + EnableAllWarnings + Disabled + _WIN32_WINNT=0x0A00;WIN32;_DEBUG;_LIB;_CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS;%(PreprocessorDefinitions) + pch.h + $(ProjectDir)Inc;$(ProjectDir)Src;$(ProjectDir)Src\Shaders\Compiled;%(AdditionalIncludeDirectories) + Fast + StreamingSIMDExtensions2 + $(IntDir)$(TargetName).pdb + true + true + /Zc:__cplusplus %(AdditionalOptions) + ProgramDatabase + false + Level4 + + + 6.0 + true + /Fd "$(OutDir)%(Filename).pdb" %(AdditionalOptions) + + + Windows + true + + + + + EnableAllWarnings + Use + MaxSpeed + _WIN32_WINNT=0x0A00;WIN32;NDEBUG;_LIB;_CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS;%(PreprocessorDefinitions) + pch.h + $(ProjectDir)Inc;$(ProjectDir)Src;$(ProjectDir)Src\Shaders\Compiled;%(AdditionalIncludeDirectories) + Fast + $(IntDir)$(TargetName).pdb + true + true + /Zc:__cplusplus %(AdditionalOptions) + Level4 + true + + + 6.0 + true + /Fd "$(OutDir)%(Filename).pdb" %(AdditionalOptions) + + + Windows + true + true + true + + + + + EnableAllWarnings + Use + MaxSpeed + _WIN32_WINNT=0x0A00;WIN32;NDEBUG;_LIB;_CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS;%(PreprocessorDefinitions) + pch.h + $(ProjectDir)Inc;$(ProjectDir)Src;$(ProjectDir)Src\Shaders\Compiled;%(AdditionalIncludeDirectories) + Fast + $(IntDir)$(TargetName).pdb + true + true + /Zc:__cplusplus %(AdditionalOptions) + Level4 + true + + + 6.0 + true + /Fd "$(OutDir)%(Filename).pdb" %(AdditionalOptions) + + + Windows + true + true + true + + + + + EnableAllWarnings + Use + MaxSpeed + _WIN32_WINNT=0x0A00;WIN32;NDEBUG;_LIB;_CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS;%(PreprocessorDefinitions) + pch.h + $(ProjectDir)Inc;$(ProjectDir)Src;$(ProjectDir)Src\Shaders\Compiled;%(AdditionalIncludeDirectories) + Fast + StreamingSIMDExtensions2 + $(IntDir)$(TargetName).pdb + true + true + /Zc:__cplusplus %(AdditionalOptions) + Level4 + + + 6.0 + true + /Fd "$(OutDir)%(Filename).pdb" %(AdditionalOptions) + + + Windows + true + true + true + + + + + + + + <_ATGFXCPath>$(WindowsSDK_ExecutablePath_x64.Split(';')[0]) + <_ATGFXCPath>$(_ATGFXCPath.Replace("x64","")) + <_ATGFXCPath Condition="'$(_ATGFXCPath)' != '' and !HasTrailingSlash('$(_ATGFXCPath)')">$(_ATGFXCPath)\ + + + + <_ATGFXCPath /> + + + + + <_ATGShaderHeaders Include="$(ProjectDir)src/Shaders/Compiled/*.inc" Exclude="$(ProjectDir)src/Shaders/Compiled/*Xbox*.inc" /> + <_ATGShaderSymbols Include="$(ProjectDir)src/Shaders/Compiled/*.pdb" Exclude="$(ProjectDir)src/Shaders/Compiled/*Xbox*.pdb" /> + + + + + \ No newline at end of file diff --git a/Common/DirectXTK12/DirectXTK_Desktop_2022_Win10.vcxproj.filters b/Common/DirectXTK12/DirectXTK_Desktop_2022_Win10.vcxproj.filters new file mode 100644 index 0000000..79196c8 --- /dev/null +++ b/Common/DirectXTK12/DirectXTK_Desktop_2022_Win10.vcxproj.filters @@ -0,0 +1,381 @@ + + + + + {771f5f80-d173-49c3-8afb-790e8f7cb0ce} + + + {c52e19b6-8703-49a1-9b36-101a05b4745d} + + + {28d5fa16-99e2-471c-8cd8-2020e81f0024} + + + {16e1c974-4e01-4cff-948c-076e9914e033} + + + {5c972584-03dd-4b7d-9026-0e53ac731582} + + + {95983e6d-a96c-4fb5-b014-8e0527c1d68b} + + + {bb6c909b-3081-409a-a30f-5129414801ce} + + + + + Inc + + + Inc + + + Inc + + + Inc + + + Inc + + + Inc + + + Src + + + Inc + + + Src + + + Inc + + + Inc + + + Inc + + + Inc + + + Inc + + + Src + + + Inc + + + Inc + + + Inc + + + Src + + + Audio + + + Audio + + + Audio + + + Audio + + + Src\Shared + + + Src\Shared + + + Src\Shared + + + Src\Shared + + + Src\Shared + + + Src\Shared + + + Inc\Shared + + + Inc\Shared + + + Inc\Shared + + + Inc\Shared + + + Inc\Shared + + + Src\Shared + + + Src\Shared + + + Src\Shared + + + Src\Shared + + + Src\Shared + + + Inc + + + Inc + + + Inc + + + Inc + + + + + Src + + + Src + + + Src + + + Src + + + Src + + + Src + + + Src + + + Src + + + Src + + + Src + + + Src + + + Src + + + Src + + + Src + + + Src + + + Src + + + Src + + + Src + + + Src + + + Src + + + Src + + + Src + + + Src + + + Src + + + Src + + + Audio + + + Audio + + + Audio + + + Audio + + + Audio + + + Audio + + + Audio + + + Audio + + + Src\Shared + + + Src\Shared + + + Src\Shared + + + Src\Shared + + + Src\Shared + + + Src\Shared + + + Src + + + Src + + + Src + + + Src + + + Src + + + Src + + + Src + + + Src + + + Src + + + Audio + + + Src + + + Src + + + + + Src\Shaders + + + Src\Shaders + + + Src\Shaders + + + Src\Shaders + + + Src\Shaders + + + Src\Shaders + + + Src\Shaders + + + Src\Shaders + + + Src\Shared + + + Src\Shaders\Shared + + + Src\Shaders\Shared + + + Src\Shaders\Shared + + + Src\Shaders + + + Src\Shaders + + + Src\Shaders + + + Src\Shaders\Shared + + + Src\Shaders + + + Src\Shaders\Shared + + + Src\Shaders\Shared + + + Src\Shaders + + + Src\Shaders + + + + Src\Shaders\Shared + + + \ No newline at end of file diff --git a/Common/DirectXTK12/HISTORY.md b/Common/DirectXTK12/HISTORY.md new file mode 100644 index 0000000..e76b632 --- /dev/null +++ b/Common/DirectXTK12/HISTORY.md @@ -0,0 +1,446 @@ +# DirectX Tool Kit for DirectX 12 + +http://go.microsoft.com/fwlink/?LinkID=615561 + +Release available for download on [GitHub](https://github.com/microsoft/DirectXTK12/releases) + +## Release History + +### October 28, 2023 +* Added ``CreateUploadBuffer`` and ``CreateUAVBuffer`` helpers +* Fixed validation bug with WIC loader when using autogen and force RGBA together +* Fixed minor link issue with a Xbox PIX custom memory method +* Xbox shader compilation now uses ``-HV 2021`` +* Additional methods for *DirectX Tool Kit for Audio* emitter for linear and inverse-square falloff curves + +### September 1, 2023 +* GraphicsMemory updated to use Xbox PIX custom memory events with optional tags +* Retired ARM (32-bit) support for the UWP platform +* CMake project updates + +### June 13, 2023 +* Root Signature updates to resolve some warnings from Xbox PIX +* CMake project updates + +### April 28, 2023 +* Updated D3DX12 internal copy with latest changes from DirectX-Headers GitHub +* CMake project updates and fixes for clang/LLVM v16 warnings + +### March 30, 2023 +* *DirectX Tool Kit for Audio* updates + * Reworked audio device enumeration for XAudio 2.9 to use MMDeviceEnumerator rather than Windows Runtime APIs + * ``GetOutputFormat`` now reports sample rate and bit-depth from the audio device properties + * New method ``GetOutputSampleRate`` added to return the input sample rate of the mastering voice + * ``Resume`` now handles device failure by switching to silent mode +* CMake project updates +* Retired VS 2017 legacy Xbox One XDK projects + +### February 6, 2023 +* Fixed warnings when using GPU-based validation on PC +* Mouse relative mode now accumulates multiple delta updates per frame. Added new optional but recommended method ``EndOfInputFrame``. +* Fixed out-of-bounds read bug in the .WAV file reader. +* Additional checks added to DDSTextureLoader for planar video formats. +* CMake project updates + +### December 15, 2022 +* DirectXHelpers updated with **CreateUnorderedAccessView**, **CreateRenderTargetView**, **CreateBufferShaderResourceView**, and **CreateBufferUnorderedAccessView** helpers +* Added **EnableLighting** method to ``EffectFactory`` to support creating unlit model materials +* GamePad, Keyboard, and Mouse headers have ``USING_XINPUT``, ``USING_GAMEINPUT``, ``USING_WINDOWS_GAMING_INPUT`` defines +* Updates for *GameInputCreate* failure handling on PC +* Updated D3DX12 internal copy with latest changes from DirectX-Headers GitHub +* CMake project updated to require 3.20 or later +* CMake and MSBuild project updates +* Minor MinGW code changes +* Added Azure Dev Ops Pipeline YAML files +* Test suite updated with CTest support + +### October 17, 2022 +* Additional methods for *DirectX Tool Kit for Audio* emitter and listener for cone and falloff curves +* Added use of C++11 inline namespaces to make it possible to link both DX11 and DX12 versions at once +* Minor fix for ``CompileShaders.cmd`` to address additional 'paths with spaces' issues +* Minor CMake update + +### July 29, 2022 +* DDSTextureLoader ``Ex`` functions now support a ``DDS_LOADER_IGNORE_SRGB`` flag. +* Fixed Mouse race-condition with changing mode and resetting scroll wheel at the same time. +* Updates for MinGW ABI fixes in the latest DirectX-Headers. +* CMake and MSBuild project updates +* Minor code review + +### June 15, 2022 +* GamePad, Keyboard, and Mouse updated to use GameInput on PC for the Gaming.Desktop.x64 platform +* CMake project updates +* Minor code review + +### May 9, 2022 +* C++20 spaceship operator updates for SimpleMath +* Fixed missing VertexPositionNormal::InputLayout +* Minor updates for VS 2022 (17.2) +* CMake project updates (now supports MSVC, clang/LLVM, and MinGW) +* Updated D3DX12 internal copy with latest changes from DirectX-Headers GitHub +* Retired VS 2017 projects +* Minor code review +* Reformat source using updated .editorconfig settings + +## March 24, 2022 +* Fixed bug in UWP implementation of Mouse that combined vertical/horizontal scroll-wheel input +* Code refactoring for input classes (GamePad, Keyboard, and Mouse) +* Update build switches for SDL recommendations +* CMake project updates and UWP platform CMakePresets +* Fixed constexpr compat issue with DEFINE_ENUM_FLAG_OPERATORS in legacy Xbox One XDK +* Dropped support for legacy Xbox One XDK prior to April 2018 + +### February 28, 2022 +* Minor fix to DescriptorHeap Increment() to return uint32_t instead of size_t +* Updated D3DX12 internal copy with latest changes from GitHub +* SimpleMath Matrix updated with ToEuler and Vector3 version of CreateFromYawPitchRoll methods +* SimpleMath Quaternion updated with ToEuler, RotateTowards, FromToRotation, LookRotation, and Angle methods +* Keyboard updated with new IME On/Off v-keys +* Win32 Mouse now uses ``WM_ACTIVATE`` for more robust behavior +* *DirectX Tool Kit for Audio* updated for Advanced Format (4Kn) wavebank streaming +* Code and project review including fixing clang v13 warnings +* Added CMakePresets.json + +### November 8, 2021 +* VS 2022 support +* Updated D3DX12 internal copy with latest change from GitHub +* Minor code and project review + +### October 18, 2021 +* Fixed loading of skinned PBR models from SDKMESH v2 +* Minor code review updates + +### October 13, 2021 +* Added skinning support for **NormalMapEffect** and **PBREffect** +* Common states updated with support for reverse z-buffer rendering with **DepthReverseZ** and **DepthReadReverseZ** methods. +* Effect factory updates + * Updated to use ``SkinnedNormalMapEffect`` / ``SkinnedPBREffect`` as appropriate. + * PBR now supports 'untextured' models (always requires texture coordinates) with use of diffuse color for constant albedo, and specular power for an estimated constant roughness. +* Model loader updates + * SDKMESH loader no longer requires precomputed vertex tangents for normal mapping as we don't use them. + * Added ``ModelLoader_DisableSkinning`` flag when dealing with legacy SDKMESH files with too many skinning bone influences for _MaxBone_ +* Fix for BGRA auto-generation of mipmaps on some hardware +* Minor update for the Mouse implementation for GameInput +* Project and code cleanup + +### September 30, 2021 +* Added ModelBone support for transformation hierarchies + * Rigid-body & skinned animation Draw support added to Model +* Support for loading Visual Studio ``CMO`` models added using BasicEffect or SkinnedEffect materials +* Added type aliases ``Model::EffectCollection``, ``ModelMeshPart::InputLayoutCollection``, ``GeometricPrimitive::VertexCollection`` and ``IndexCollection`` +* Fixed handle leak in ResourceUploadBatch +* Updated ScopeBarrier to conform with C++14 ``std::initializer_list`` +* VS 2017 projects updated to require the Windows 10 SDK (19401) and make use of Shader Model 6.0 +* Code review updates + +### August 1, 2021 +* DebugEffect, NormalMapEffect, and PBREffect updated with instancing support +* GeometricPrimitive updated with DrawInstanced method +* ToneMapPostProcess updated with SetColorRotation method +* Added VS 2022 Preview projects +* Minor code review + +### June 9, 2021 +* VS 2019 projects now use Shader Model 6 to build shaders (CMake has build option) +* DirectX Tool Kit for Audio updates: + * Fixed mono source panning + * Added ``EnableDefaultMultiChannel`` helper to AudioEmitter for multi-channel source setup + * Added ``GetChannelCount`` accessor to SoundEffectInstance and SoundStreamInstance + * ``Apply3D`` can now use X3DAUDIO_LISTENER and X3DAUDIO_EMITTER directly or the library helper structs. +* Minor code review + +### April 6, 2021 +* DDSTextureLoader updated to accept nVidia Texture Tool v1 single-channel and dual-channel files marked as RGB instead of LUMINANCE +* Fixed ScreenGrab for reserved and MSAA resources +* Minor code and project cleanup + +### January 9, 2021 +* Code review for improved conformance +* CMake updated to support package install + +### November 11, 2020 +* Fixed ``/analyze`` warnings in GameInput usage +* Updated D3DX12 internal copy with latest change from GitHub +* Sync'd DirectX Tool Kit for Audio with DX11 version +* Minor code and project cleanup + +### September 30, 2020 +* GamePad class updated with ``c_MostRecent`` constant for ``-1`` player index special behavior + * For GameInput API implementation, also added ``c_MergedInput`` +* Fixed bug in WICTextureLoader that resulted in ``WINCODEC_ERR_INSUFFICIENTBUFFER`` for some resize requests +* Fixed ``.wav`` file reading of MIDILoop chunk +* Minor code cleanup + +### August 15, 2020 +* *breaking change* Converted default bool parameters on some effects to ``EffectFlags``: + * Added new effects flags ``Specular``, ``Emissive``, ``Fresnel``, and ``Velocity`` + * Removed EnvironmentMapEffect ``fresnelEnabled``, ``specularEnabled`` parameters + * Removed NormalMapEffect ``specularMap`` parameter + * Removed PBREffect ``emissive``, ``generateVelocity`` parameters + * Removed SkinnedEffect ``weightsPerVertex`` parameter (always uses 4 bones) +* EnvironmentMapEffect now supports cubemaps, spherical, and dual-parabola environment maps +* Fixed bug with ScreenGrab with 'small alignment' textures +* Code review and project updates +* Added GDK projects + +### July 2, 2020 +* Improved SpriteFont drawing performance in Debug builds +* Regenerated shaders using Windows 10 May 2020 Update SDK (19041) +* Code cleanup for some new VC++ 16.7 warnings +* CMake updates + +### June 15, 2020 +* DescriptorHeap / DescriptorPile updated with additional ctor +* EffectTextureFactory ctor parameter updated with default value +* Code cleanup for some new VC++ 16.7 warnings and static code analysis + +### June 1, 2020 +* Added BufferHelpers header with functions **CreateStaticBuffer** and **CreateTextureFromMemory** +* Added **IsPowerOf2** helper to DirectXHelpers +* SpriteBatch now supports providing a new heap-based sampler on calls to **Begin** +* Converted to typed enum bitmask flags (see release notes for details on this potential *breaking change*) + + ``AUDIO_ENGINE_FLAGS``, ``DDS_LOADER_FLAGS``, ``ModelLoaderFlags``, ``SOUND_EFFECT_INSTANCE_FLAGS``, and ``WIC_LOADER_FLAGS`` +* WICTextureLoader for ``PNG`` codec now checks ``gAMA`` chunk to determine colorspace if the ``sRGB`` chunk is not found for legacy sRGB detection. +* ``WIC_LOADER_SRGB_DEFAULT`` flag added when loading image via WIC without explicit colorspace metadata +* CMake project updates + +### May 10, 2020 +* **ResourceUploadBatch** updated to support usage with copy & compute queues +* **Transition** methods added for GeometricPrimtive and Model for use with static VBs/IBs +* WICTextureLoader updated with new loader flags: ``FORCE_RGBA32``, ``FIT_POW2``, and ``MAKE_SQUARE`` +* SimpleMath no longer forces use of d3d11.h or d3d12.h (can be used with d3d9.h for example) +* *DirectX Tool Kit for Audio* updated with **SoundStreamInstance** class for async I/O playback from XACT-style streaming wavebanks +* Code cleanup +* Updated D3DX12 internal copy to Windows 10 SDK (19041) version + +### April 3, 2020 +* Updated D3DX12 internal copy to latest version +* SpriteFont **MeasureString** / **MeasureDrawBounds** fixes for !ignoreWhitespace +* Regenerated shaders using Windows 10 SDK (19041) + * Upgraded to use root signature 1.1 which requires Windows 10 (14393) or later +* Code review (``constexpr`` / ``noexcept`` usage) +* CMake updated for PCH usage with 3.16 or later + +### February 24, 2020 +* *breaking change* **Model::CreateFromxxx** parameter order changed and added ModelLoaderFlags +* Added ``ignoreWhitespace`` defaulted parameter to SpriteFont Measure methods +* Sync'd DirectX Tool Kit for Audio and GamePad with DX11 version +* Fixed encoding issue with Utilities.fxh +* Code and project cleanup +* Retired VS 2015 projects + +### December 17, 2019 +* Added ARM64 platform to VS 2019 Win32 desktop Win10 project +* Added Vector ``operator/`` by float scalar to SimpleMath +* Added **GetStatistics** method to GraphicsMemory +* Reduced fence object usage in GraphicsMemory's **LinearAllocator** +* Updated CMake project +* Code cleaup + +### October 17, 2019 +* Added optional ``forceSRGB`` parameter to SaveWICTextureToFile +* GamePad updated to report VID/PID (when supported) +* Minor code cleanup + +### August 21, 2019 +* Updated D3DX12 internal copy to latest version +* Code cleanup + +### June 30, 2019 +* Clang/LLVM warning cleanup +* Renamed ``DirectXTK_Windows10.vcxproj`` to ``_Windows10_2017.vcxproj`` +* Added VS 2019 UWP project + +### May 30, 2019 +* PBREffect updated with additional set methods +* Additional debugging output for GraphicsMemory in error cases +* Added CMake project files +* Code cleanup + +### April 26, 2019 +* Updated auto-generated mipmaps support to make it more robust +* Added optional **LoadStaticBuffers** method for GeometricPrimitive +* Added VS 2019 desktop projects +* Fixed guards w.r.t. to windows.h usage in Keyboard/Mouse headers +* Added C++/WinRT SetWindow helper to Keyboard/Mouse +* Update HLSL script to use Shader Model 5.1 instead of 5.0 +* Code cleanup + +### February 7, 2019 +* Model now supports loading ``SDKMESH v2`` models +* **PBREffectFactory** added to support PBR materials +* PBREffect and NormalMapEffect shaders updated to support ``BC5_UNORM`` compressed normal maps +* SpriteFont: **DrawString** overloads for UTF-8 chars in addition to UTF-16LE wide chars +* Fixed bug with GraphicsMemory dtor introduced with mGPU handling +* Made library agnostic to legacy Windows SDK pix.h vs. latest pix3.h from NuGet + +### November 16, 2018 +* VS 2017 updated for Windows 10 October 2018 Update SDK (17763) +* ARM64 platform configurations added to UWP projects +* Minor code review + +### October 31, 2018 +* Model loader for SDKMESH now attempts to use legacy DE3CN compressed normals + + This is an approximation only and emits a warning in debug builds +* IEffectTextureFactory's CreateTexture interface method now returns the 'slot' + + This is for use with **GetResource** method +* Minor code review + +### October 25, 2018 +* Use UTF-8 instead of ANSI for narrow strings +* Updated D3DX12 internal copy to latest version +* Improved debug diagnostics +* Minor code review + +### September 13, 2018 +* Broke DescriptorHeap header dependency on D3DX12.H + +### August 17, 2018 +* Improved validation for 16k textures and other large resources +* Improved debug output for failed texture loads and screengrabs +* Updated for VS 2017 15.8 +* Code cleanup + +### July 3, 2018 +* Model **LoadStaticBuffers** method to use static vs. dynamic VB/IB +* *breaking change* Custom Model loaders and renderers should be updated for changes to ModelMeshPart +* ModelMeshPart **DrawInstanced** method added +* Code and project cleanup + +### May 31, 2018 +* VS 2017 updated for Windows 10 April 2018 Update SDK (17134) +* Regenerated shaders using Windows 10 April 2018 Update SDK (17134) + +### May 14, 2018 +* EffectPipelineStateDescription updated with **GetDesc** method +* Updated for VS 2017 15.7 update warnings +* Code and project cleanup + +### April 23, 2018 +* **AlignUp**, **AlignDown** template functions in DirectXHelpers.h +* **ScopedBarrier** added to DirectXHelpers.h +* Mouse support for cursor visibility +* SimpleMath and VertexTypes updated with default copy and move ctors +* SimpleMath updates to use constexpr +* Basic multi-GPU support added +* More debug object naming for PIX +* PostProcess updated with 'big triangle' optimization +* Code and project file cleanup + +### February 7, 2018 +* Mouse fix for cursor behavior when using Remote Desktop for Win32 +* Updated for a few more VS 2017 warnings + +### December 13, 2017 +* **PBREffect** and **DebugEffect** added +* **NormalMapEffect** no longer requires or uses explicit vertex tangents +* Updated for VS 2017 15.5 update warnings +* Code cleanup + +### November 1, 2017 +* VS 2017 updated for Windows 10 Fall Creators Update SDK (16299) +* Regenerated shaders using Windows 10 Fall Creators Update SDK (16299) +* Updated D3DX12 internal copy to latest version + +### September 22, 2017 +* Updated for VS 2017 15.3 update ``/permissive-`` changes +* ScreenGrab updated to use non-sRGB metadata for PNG +* Mouse use of ``WM_INPUT`` updated for Remote Desktop scenarios + +### July 28, 2017 +* Fix for WIC writer when codec target format requires a palette +* Fix for error detection in ResourceUploadBatch::End method +* Code cleanup + +### June 21, 2017 +* Post-processing support with the **BasicPostProcess**, **DualPostProcess**, and **ToneMapPostProcess** classes +* Added **DescriptorPile** utility class +* SDKMESH loader fix when loading legacy files with all zero materials +* DirectXTK for Audio: Minor fixes for environmental audio +* Optimized root signatures for Effects shaders +* Minor code cleanup + +### April 24, 2017 +* Regenerated shaders using Windows 10 Creators Update SDK (15063) +* Fixed NormalMapEffect shader selection for specular texture usage +* Fixed Direct3D validation layer issues when using Creators Update +* Fixed AudioEngine enumeration when using Single Threaded Apartment (STA) +* Fixed bug with GamePad (``Windows.Gaming.Input``) when no user bound +* Updated D3DX12 internal copy to latest version + +### April 7, 2017 +* VS 2017 updated for Windows Creators Update SDK (15063) +* XboxDDSTextureLoader updates + +### February 10, 2017 +* SpriteBatch default rasterizer state now matches DirectX 11 version +* DDSTextureLoader now supports loading planar video format textures +* **GamePad** now supports special value of -1 for 'most recently connected controller' +* WIC format 40bppCMYKAlpha should be converted to RGBA8 rather than RGBA16 +* DDS support for L8A8 with bitcount 8 rather than 16 +* Updated D3DX12 internal copy to latest version +* Minor code cleanup + +### December 5, 2016 +* Mouse and Keyboard classes updated with **IsConnected** method +* Windows10 project ``/ZW`` switch removed to support use in C++/WinRT projection apps +* VS 2017 RC projects added +* Updated D3DX12 internal copy to latest version +* Minor code cleanup + +### October 6, 2016 +* SDKMESH loader and BasicEffects support for compressed vertex normals with biasing +* *breaking change* + + DDSTextureLoader Ex bool ``forceSRGB`` and ``generateMipsIfMissing`` parmeters are now a ``DDS_LOADER`` flag + + WICTextureLoader Ex bool ``forceSRGB`` and ``generateMips`` parameters are now a ``WIC_LOADER`` flag + + Add ``vertexCount`` member to ModelMeshPart +* Minor code cleanup + +### September 15, 2016 +* Rebuild shaders using 1.0 Root Signature for improved compatibility +* Minor code cleanup + +### September 1, 2016 +* **EffectPipelineStateDescription** is now in it's own header +* Additional debug object naming +* Fixed Tier 1 hardware support issues with BasicEffect and generating mipmaps +* Fixed default graphics memory alignment to resolve rendering problems on some hardware +* Added ``forceSRGB`` optional parameter to SpriteFont ctor +* EffectFactory method **EnableForceSRGB** added +* Removed problematic ABI::Windows::Foundation::Rect interop for SimpleMath +* Updated D3DX12 internal copy for the Windows 10 Anniversary Update SDK (14393) +* Minor code cleanup + +### August 4, 2016 +* GraphicsMemory fix for robustness during cleanup +* Regenerated shaders using Windows 10 Anniversary Update SDK (14393) + +### August 2, 2016 +* Updated for VS 2015 Update 3 and Windows 10 SDK (14393) + +### August 1, 2016 +* Model effects array is now indexed by part rather than by material +* GamePad capabilities information updated for Universal Windows and Xbox One platforms +* Specular falloff lighting computation fix in shaders + +### July 18, 2016 +* *breaking changes* to CommonStates, DescriptorHeap, Effects, Model, EffectPipelineStateDescription, and SpriteBatchPipelineStateDescription + + added texture sampler control to Effects and SpriteBatch + + fixed Model control of blend and rasterizer state + + fixed problems with PerPixelLighting control (EffectFactory defaults to per-pixel lighting) + + fixed control of weights-per-vertex optimization for SkinnedEffect + + removed unnecesary "one-light" shader permutations + + fixed bug in AlphaTestEfect implementation + + improved debug messages for misconfigured effects NormalMapEffect for normal-map with optional specular map rendering +* **EnvironmentMapEffect** now supports per-pixel lighting +* Effects updated with **SetMatrices** and **SetColorAndAlpha** methods +* GraphicsMemory support for SharedGraphicsResource shared_ptr style smart-pointer +* PrimitiveBatch fix for DrawQuad +* ScreenGrab handles resource state transition +* SimpleMath: improved interop with DirectXMath constants +* WICTextureLoader module LoadWICTexture* methods +* Fixed bugs with GenerateMips for sRGB and BGRA formats +* Code cleanup + +### June 30, 2016 +* Original release diff --git a/Common/DirectXTK12/Inc/Audio.h b/Common/DirectXTK12/Inc/Audio.h new file mode 100644 index 0000000..609792f --- /dev/null +++ b/Common/DirectXTK12/Inc/Audio.h @@ -0,0 +1,822 @@ +//-------------------------------------------------------------------------------------- +// File: Audio.h +// +// DirectXTK for Audio header +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248929 +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#if (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) +#include +#pragma comment(lib,"acphal.lib") +#endif + +#ifndef XAUDIO2_HELPER_FUNCTIONS +#define XAUDIO2_HELPER_FUNCTIONS +#endif + +#if defined(USING_XAUDIO2_REDIST) || (_WIN32_WINNT >= 0x0A00 /*_WIN32_WINNT_WIN10*/) || defined(_XBOX_ONE) +#define USING_XAUDIO2_9 +#elif (_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/) +#define USING_XAUDIO2_8 +#elif (_WIN32_WINNT >= 0x0601 /*_WIN32_WINNT_WIN7*/) +#error Windows 7 SP1 requires the XAudio2Redist NuGet package https://aka.ms/xaudio2redist +#else +#error DirectX Tool Kit for Audio not supported on this platform +#endif + +#include +#include + +#pragma warning(push) +#pragma warning(disable : 4619 4616 5246) +#include +#pragma warning(pop) + +#include + +#ifndef USING_XAUDIO2_REDIST +#if defined(USING_XAUDIO2_8) && defined(NTDDI_WIN10) && !defined(_M_IX86) +// The xaudio2_8.lib in the Windows 10 SDK for x86 is incorrectly annotated as __cdecl instead of __stdcall, so avoid using it in this case. +#pragma comment(lib,"xaudio2_8.lib") +#else +#pragma comment(lib,"xaudio2.lib") +#endif +#endif + +#include + + +namespace DirectX +{ + class SoundEffectInstance; + class SoundStreamInstance; + + //---------------------------------------------------------------------------------- + struct AudioStatistics + { + size_t playingOneShots; // Number of one-shot sounds currently playing + size_t playingInstances; // Number of sound effect instances currently playing + size_t allocatedInstances; // Number of SoundEffectInstance allocated + size_t allocatedVoices; // Number of XAudio2 voices allocated (standard, 3D, one-shots, and idle one-shots) + size_t allocatedVoices3d; // Number of XAudio2 voices allocated for 3D + size_t allocatedVoicesOneShot; // Number of XAudio2 voices allocated for one-shot sounds + size_t allocatedVoicesIdle; // Number of XAudio2 voices allocated for one-shot sounds but not currently in use + size_t audioBytes; // Total wave data (in bytes) in SoundEffects and in-memory WaveBanks + #if (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) + size_t xmaAudioBytes; // Total wave data (in bytes) in SoundEffects and in-memory WaveBanks allocated with ApuAlloc + #endif + size_t streamingBytes; // Total size of streaming buffers (in bytes) in streaming WaveBanks + }; + + + //---------------------------------------------------------------------------------- + class IVoiceNotify + { + public: + virtual ~IVoiceNotify() = default; + + IVoiceNotify(const IVoiceNotify&) = delete; + IVoiceNotify& operator=(const IVoiceNotify&) = delete; + + IVoiceNotify(IVoiceNotify&&) = default; + IVoiceNotify& operator=(IVoiceNotify&&) = default; + + virtual void __cdecl OnBufferEnd() = 0; + // Notfication that a voice buffer has finished + // Note this is called from XAudio2's worker thread, so it should perform very minimal and thread-safe operations + + virtual void __cdecl OnCriticalError() = 0; + // Notification that the audio engine encountered a critical error + + virtual void __cdecl OnReset() = 0; + // Notification of an audio engine reset + + virtual void __cdecl OnUpdate() = 0; + // Notification of an audio engine per-frame update (opt-in) + + virtual void __cdecl OnDestroyEngine() noexcept = 0; + // Notification that the audio engine is being destroyed + + virtual void __cdecl OnTrim() = 0; + // Notification of a request to trim the voice pool + + virtual void __cdecl GatherStatistics(AudioStatistics& stats) const = 0; + // Contribute to statistics request + + virtual void __cdecl OnDestroyParent() noexcept = 0; + // Optional notification used by some objects + + protected: + IVoiceNotify() = default; + }; + + //---------------------------------------------------------------------------------- + enum AUDIO_ENGINE_FLAGS : uint32_t + { + AudioEngine_Default = 0x0, + + AudioEngine_EnvironmentalReverb = 0x1, + AudioEngine_ReverbUseFilters = 0x2, + AudioEngine_UseMasteringLimiter = 0x4, + + AudioEngine_Debug = 0x10000, + AudioEngine_ThrowOnNoAudioHW = 0x20000, + AudioEngine_DisableVoiceReuse = 0x40000, + }; + + enum SOUND_EFFECT_INSTANCE_FLAGS : uint32_t + { + SoundEffectInstance_Default = 0x0, + + SoundEffectInstance_Use3D = 0x1, + SoundEffectInstance_ReverbUseFilters = 0x2, + SoundEffectInstance_NoSetPitch = 0x4, + + SoundEffectInstance_UseRedirectLFE = 0x10000, + }; + + enum AUDIO_ENGINE_REVERB : unsigned int + { + Reverb_Off, + Reverb_Default, + Reverb_Generic, + Reverb_Forest, + Reverb_PaddedCell, + Reverb_Room, + Reverb_Bathroom, + Reverb_LivingRoom, + Reverb_StoneRoom, + Reverb_Auditorium, + Reverb_ConcertHall, + Reverb_Cave, + Reverb_Arena, + Reverb_Hangar, + Reverb_CarpetedHallway, + Reverb_Hallway, + Reverb_StoneCorridor, + Reverb_Alley, + Reverb_City, + Reverb_Mountains, + Reverb_Quarry, + Reverb_Plain, + Reverb_ParkingLot, + Reverb_SewerPipe, + Reverb_Underwater, + Reverb_SmallRoom, + Reverb_MediumRoom, + Reverb_LargeRoom, + Reverb_MediumHall, + Reverb_LargeHall, + Reverb_Plate, + Reverb_MAX + }; + + enum SoundState + { + STOPPED = 0, + PLAYING, + PAUSED + }; + + + //---------------------------------------------------------------------------------- + class AudioEngine + { + public: + explicit AudioEngine( + AUDIO_ENGINE_FLAGS flags = AudioEngine_Default, + _In_opt_ const WAVEFORMATEX* wfx = nullptr, + _In_opt_z_ const wchar_t* deviceId = nullptr, + AUDIO_STREAM_CATEGORY category = AudioCategory_GameEffects) noexcept(false); + + AudioEngine(AudioEngine&&) noexcept; + AudioEngine& operator= (AudioEngine&&) noexcept; + + AudioEngine(AudioEngine const&) = delete; + AudioEngine& operator= (AudioEngine const&) = delete; + + virtual ~AudioEngine(); + + bool __cdecl Update(); + // Performs per-frame processing for the audio engine, returns false if in 'silent mode' + + bool __cdecl Reset(_In_opt_ const WAVEFORMATEX* wfx = nullptr, _In_opt_z_ const wchar_t* deviceId = nullptr); + // Reset audio engine from critical error/silent mode using a new device; can also 'migrate' the graph + // Returns true if succesfully reset, false if in 'silent mode' due to no default device + // Note: One shots are lost, all SoundEffectInstances are in the STOPPED state after successful reset + + void __cdecl Suspend() noexcept; + void __cdecl Resume(); + // Suspend/resumes audio processing (i.e. global pause/resume) + + float __cdecl GetMasterVolume() const noexcept; + void __cdecl SetMasterVolume(float volume); + // Master volume property for all sounds + + void __cdecl SetReverb(AUDIO_ENGINE_REVERB reverb); + void __cdecl SetReverb(_In_opt_ const XAUDIO2FX_REVERB_PARAMETERS* native); + // Sets environmental reverb for 3D positional audio (if active) + + void __cdecl SetMasteringLimit(int release, int loudness); + // Sets the mastering volume limiter properties (if active) + + AudioStatistics __cdecl GetStatistics() const; + // Gathers audio engine statistics + + WAVEFORMATEXTENSIBLE __cdecl GetOutputFormat() const noexcept; + // Returns the format of the audio output device associated with the mastering voice. + + uint32_t __cdecl GetChannelMask() const noexcept; + // Returns the output channel mask + + int __cdecl GetOutputSampleRate() const noexcept; + // Returns the sample rate going into the mastering voice + + unsigned int __cdecl GetOutputChannels() const noexcept; + // Returns the number of channels going into the mastering voice + + bool __cdecl IsAudioDevicePresent() const noexcept; + // Returns true if the audio graph is operating normally, false if in 'silent mode' + + bool __cdecl IsCriticalError() const noexcept; + // Returns true if the audio graph is halted due to a critical error (which also places the engine into 'silent mode') + + // Voice pool management. + void __cdecl SetDefaultSampleRate(int sampleRate); + // Sample rate for voices in the reuse pool (defaults to 44100) + + void __cdecl SetMaxVoicePool(size_t maxOneShots, size_t maxInstances); + // Maximum number of voices to allocate for one-shots and instances + // Note: one-shots over this limit are ignored; too many instance voices throws an exception + + void __cdecl TrimVoicePool(); + // Releases any currently unused voices + + // Internal-use functions + void __cdecl AllocateVoice(_In_ const WAVEFORMATEX* wfx, + SOUND_EFFECT_INSTANCE_FLAGS flags, bool oneshot, _Outptr_result_maybenull_ IXAudio2SourceVoice** voice); + + void __cdecl DestroyVoice(_In_ IXAudio2SourceVoice* voice) noexcept; + // Should only be called for instance voices, not one-shots + + void __cdecl RegisterNotify(_In_ IVoiceNotify* notify, bool usesUpdate); + void __cdecl UnregisterNotify(_In_ IVoiceNotify* notify, bool usesOneShots, bool usesUpdate); + + // XAudio2 interface access + IXAudio2* __cdecl GetInterface() const noexcept; + IXAudio2MasteringVoice* __cdecl GetMasterVoice() const noexcept; + IXAudio2SubmixVoice* __cdecl GetReverbVoice() const noexcept; + X3DAUDIO_HANDLE& __cdecl Get3DHandle() const noexcept; + + // Static functions + struct RendererDetail + { + std::wstring deviceId; + std::wstring description; + }; + + static std::vector __cdecl GetRendererDetails(); + // Returns a list of valid audio endpoint devices + +#if defined(_MSC_VER) && !defined(_NATIVE_WCHAR_T_DEFINED) + explicit AudioEngine( + AUDIO_ENGINE_FLAGS flags = AudioEngine_Default, + _In_opt_ const WAVEFORMATEX* wfx = nullptr, + _In_opt_z_ const __wchar_t* deviceId = nullptr, + AUDIO_STREAM_CATEGORY category = AudioCategory_GameEffects) noexcept(false); + + bool __cdecl Reset(_In_opt_ const WAVEFORMATEX* wfx = nullptr, _In_opt_z_ const __wchar_t* deviceId = nullptr); +#endif + + private: + // Private implementation. + class Impl; + std::unique_ptr pImpl; + }; + + + //---------------------------------------------------------------------------------- + class WaveBank + { + public: + WaveBank(_In_ AudioEngine* engine, _In_z_ const wchar_t* wbFileName); + + WaveBank(WaveBank&&) noexcept; + WaveBank& operator= (WaveBank&&) noexcept; + + WaveBank(WaveBank const&) = delete; + WaveBank& operator= (WaveBank const&) = delete; + + virtual ~WaveBank(); + + void __cdecl Play(unsigned int index); + void __cdecl Play(unsigned int index, float volume, float pitch, float pan); + + void __cdecl Play(_In_z_ const char* name); + void __cdecl Play(_In_z_ const char* name, float volume, float pitch, float pan); + + std::unique_ptr __cdecl CreateInstance(unsigned int index, + SOUND_EFFECT_INSTANCE_FLAGS flags = SoundEffectInstance_Default); + std::unique_ptr __cdecl CreateInstance(_In_z_ const char* name, + SOUND_EFFECT_INSTANCE_FLAGS flags = SoundEffectInstance_Default); + + std::unique_ptr __cdecl CreateStreamInstance(unsigned int index, + SOUND_EFFECT_INSTANCE_FLAGS flags = SoundEffectInstance_Default); + std::unique_ptr __cdecl CreateStreamInstance(_In_z_ const char* name, + SOUND_EFFECT_INSTANCE_FLAGS flags = SoundEffectInstance_Default); + + bool __cdecl IsPrepared() const noexcept; + bool __cdecl IsInUse() const noexcept; + bool __cdecl IsStreamingBank() const noexcept; + bool __cdecl IsAdvancedFormat() const noexcept; + + size_t __cdecl GetSampleSizeInBytes(unsigned int index) const noexcept; + // Returns size of wave audio data + + size_t __cdecl GetSampleDuration(unsigned int index) const noexcept; + // Returns the duration in samples + + size_t __cdecl GetSampleDurationMS(unsigned int index) const noexcept; + // Returns the duration in milliseconds + + const WAVEFORMATEX* __cdecl GetFormat(unsigned int index, _Out_writes_bytes_(maxsize) WAVEFORMATEX* wfx, size_t maxsize) const noexcept; + + int __cdecl Find(_In_z_ const char* name) const; + + #ifdef USING_XAUDIO2_9 + bool __cdecl FillSubmitBuffer(unsigned int index, _Out_ XAUDIO2_BUFFER& buffer, _Out_ XAUDIO2_BUFFER_WMA& wmaBuffer) const; + #else + void __cdecl FillSubmitBuffer(unsigned int index, _Out_ XAUDIO2_BUFFER& buffer) const; + #endif + + void __cdecl UnregisterInstance(_In_ IVoiceNotify* instance); + + HANDLE __cdecl GetAsyncHandle() const noexcept; + + bool __cdecl GetPrivateData(unsigned int index, _Out_writes_bytes_(datasize) void* data, size_t datasize); + +#if defined(_MSC_VER) && !defined(_NATIVE_WCHAR_T_DEFINED) + WaveBank(_In_ AudioEngine* engine, _In_z_ const __wchar_t* wbFileName); +#endif + + private: + // Private implementation. + class Impl; + + std::unique_ptr pImpl; + }; + + + //---------------------------------------------------------------------------------- + class SoundEffect + { + public: + SoundEffect(_In_ AudioEngine* engine, _In_z_ const wchar_t* waveFileName); + + SoundEffect(_In_ AudioEngine* engine, _Inout_ std::unique_ptr& wavData, + _In_ const WAVEFORMATEX* wfx, _In_reads_bytes_(audioBytes) const uint8_t* startAudio, size_t audioBytes); + + SoundEffect(_In_ AudioEngine* engine, _Inout_ std::unique_ptr& wavData, + _In_ const WAVEFORMATEX* wfx, _In_reads_bytes_(audioBytes) const uint8_t* startAudio, size_t audioBytes, + uint32_t loopStart, uint32_t loopLength); + + #ifdef USING_XAUDIO2_9 + + SoundEffect(_In_ AudioEngine* engine, _Inout_ std::unique_ptr& wavData, + _In_ const WAVEFORMATEX* wfx, _In_reads_bytes_(audioBytes) const uint8_t* startAudio, size_t audioBytes, + _In_reads_(seekCount) const uint32_t* seekTable, size_t seekCount); + + #endif + + SoundEffect(SoundEffect&&) noexcept; + SoundEffect& operator= (SoundEffect&&) noexcept; + + SoundEffect(SoundEffect const&) = delete; + SoundEffect& operator= (SoundEffect const&) = delete; + + virtual ~SoundEffect(); + + void __cdecl Play(); + void __cdecl Play(float volume, float pitch, float pan); + + std::unique_ptr __cdecl CreateInstance(SOUND_EFFECT_INSTANCE_FLAGS flags = SoundEffectInstance_Default); + + bool __cdecl IsInUse() const noexcept; + + size_t __cdecl GetSampleSizeInBytes() const noexcept; + // Returns size of wave audio data + + size_t __cdecl GetSampleDuration() const noexcept; + // Returns the duration in samples + + size_t __cdecl GetSampleDurationMS() const noexcept; + // Returns the duration in milliseconds + + const WAVEFORMATEX* __cdecl GetFormat() const noexcept; + + #ifdef USING_XAUDIO2_9 + bool __cdecl FillSubmitBuffer(_Out_ XAUDIO2_BUFFER& buffer, _Out_ XAUDIO2_BUFFER_WMA& wmaBuffer) const; + #else + void __cdecl FillSubmitBuffer(_Out_ XAUDIO2_BUFFER& buffer) const; + #endif + + void __cdecl UnregisterInstance(_In_ IVoiceNotify* instance); + +#if defined(_MSC_VER) && !defined(_NATIVE_WCHAR_T_DEFINED) + SoundEffect(_In_ AudioEngine* engine, _In_z_ const __wchar_t* waveFileName); +#endif + + private: + // Private implementation. + class Impl; + + std::unique_ptr pImpl; + }; + + + //---------------------------------------------------------------------------------- + struct AudioListener : public X3DAUDIO_LISTENER + { + X3DAUDIO_CONE ListenerCone; + + AudioListener() noexcept : + X3DAUDIO_LISTENER{}, + ListenerCone{} + { + OrientFront.z = -1.f; + + OrientTop.y = 1.f; + } + + void XM_CALLCONV SetPosition(FXMVECTOR v) noexcept + { + XMStoreFloat3(reinterpret_cast(&Position), v); + } + void __cdecl SetPosition(const XMFLOAT3& pos) noexcept + { + Position.x = pos.x; + Position.y = pos.y; + Position.z = pos.z; + } + + void XM_CALLCONV SetVelocity(FXMVECTOR v) noexcept + { + XMStoreFloat3(reinterpret_cast(&Velocity), v); + } + void __cdecl SetVelocity(const XMFLOAT3& vel) noexcept + { + Velocity.x = vel.x; + Velocity.y = vel.y; + Velocity.z = vel.z; + } + + void XM_CALLCONV SetOrientation(FXMVECTOR forward, FXMVECTOR up) noexcept + { + XMStoreFloat3(reinterpret_cast(&OrientFront), forward); + XMStoreFloat3(reinterpret_cast(&OrientTop), up); + } + void __cdecl SetOrientation(const XMFLOAT3& forward, const XMFLOAT3& up) noexcept + { + OrientFront.x = forward.x; OrientTop.x = up.x; + OrientFront.y = forward.y; OrientTop.y = up.y; + OrientFront.z = forward.z; OrientTop.z = up.z; + } + + void XM_CALLCONV SetOrientationFromQuaternion(FXMVECTOR quat) noexcept + { + const XMVECTOR forward = XMVector3Rotate(g_XMIdentityR2, quat); + XMStoreFloat3(reinterpret_cast(&OrientFront), forward); + + const XMVECTOR up = XMVector3Rotate(g_XMIdentityR1, quat); + XMStoreFloat3(reinterpret_cast(&OrientTop), up); + } + + // Updates velocity and orientation by tracking changes in position over time. + void XM_CALLCONV Update(FXMVECTOR newPos, XMVECTOR upDir, float dt) noexcept + { + if (dt > 0.f) + { + const XMVECTOR lastPos = XMLoadFloat3(reinterpret_cast(&Position)); + + XMVECTOR vDelta = XMVectorSubtract(newPos, lastPos); + const XMVECTOR vt = XMVectorReplicate(dt); + XMVECTOR v = XMVectorDivide(vDelta, vt); + XMStoreFloat3(reinterpret_cast(&Velocity), v); + + vDelta = XMVector3Normalize(vDelta); + XMStoreFloat3(reinterpret_cast(&OrientFront), vDelta); + + v = XMVector3Cross(upDir, vDelta); + v = XMVector3Normalize(v); + + v = XMVector3Cross(vDelta, v); + v = XMVector3Normalize(v); + XMStoreFloat3(reinterpret_cast(&OrientTop), v); + + XMStoreFloat3(reinterpret_cast(&Position), newPos); + } + } + + void __cdecl SetOmnidirectional() noexcept + { + pCone = nullptr; + } + + void __cdecl SetCone(const X3DAUDIO_CONE& listenerCone); + }; + + + //---------------------------------------------------------------------------------- + struct AudioEmitter : public X3DAUDIO_EMITTER + { + X3DAUDIO_CONE EmitterCone; + float EmitterAzimuths[XAUDIO2_MAX_AUDIO_CHANNELS]; + + AudioEmitter() noexcept : + X3DAUDIO_EMITTER{}, + EmitterCone{}, + EmitterAzimuths{} + { + OrientFront.z = -1.f; + + OrientTop.y = + ChannelRadius = + CurveDistanceScaler = + DopplerScaler = 1.f; + + ChannelCount = 1; + pChannelAzimuths = EmitterAzimuths; + + InnerRadiusAngle = X3DAUDIO_PI / 4.0f; + } + + void XM_CALLCONV SetPosition(FXMVECTOR v) noexcept + { + XMStoreFloat3(reinterpret_cast(&Position), v); + } + void __cdecl SetPosition(const XMFLOAT3& pos) noexcept + { + Position.x = pos.x; + Position.y = pos.y; + Position.z = pos.z; + } + + void XM_CALLCONV SetVelocity(FXMVECTOR v) noexcept + { + XMStoreFloat3(reinterpret_cast(&Velocity), v); + } + void __cdecl SetVelocity(const XMFLOAT3& vel) noexcept + { + Velocity.x = vel.x; + Velocity.y = vel.y; + Velocity.z = vel.z; + } + + void XM_CALLCONV SetOrientation(FXMVECTOR forward, FXMVECTOR up) noexcept + { + XMStoreFloat3(reinterpret_cast(&OrientFront), forward); + XMStoreFloat3(reinterpret_cast(&OrientTop), up); + } + void __cdecl SetOrientation(const XMFLOAT3& forward, const XMFLOAT3& up) noexcept + { + OrientFront.x = forward.x; OrientTop.x = up.x; + OrientFront.y = forward.y; OrientTop.y = up.y; + OrientFront.z = forward.z; OrientTop.z = up.z; + } + + void XM_CALLCONV SetOrientationFromQuaternion(FXMVECTOR quat) noexcept + { + const XMVECTOR forward = XMVector3Rotate(g_XMIdentityR2, quat); + XMStoreFloat3(reinterpret_cast(&OrientFront), forward); + + const XMVECTOR up = XMVector3Rotate(g_XMIdentityR1, quat); + XMStoreFloat3(reinterpret_cast(&OrientTop), up); + } + + // Updates velocity and orientation by tracking changes in position over time. + void XM_CALLCONV Update(FXMVECTOR newPos, XMVECTOR upDir, float dt) noexcept + { + if (dt > 0.f) + { + const XMVECTOR lastPos = XMLoadFloat3(reinterpret_cast(&Position)); + + XMVECTOR vDelta = XMVectorSubtract(newPos, lastPos); + const XMVECTOR vt = XMVectorReplicate(dt); + XMVECTOR v = XMVectorDivide(vDelta, vt); + XMStoreFloat3(reinterpret_cast(&Velocity), v); + + vDelta = XMVector3Normalize(vDelta); + XMStoreFloat3(reinterpret_cast(&OrientFront), vDelta); + + v = XMVector3Cross(upDir, vDelta); + v = XMVector3Normalize(v); + + v = XMVector3Cross(vDelta, v); + v = XMVector3Normalize(v); + XMStoreFloat3(reinterpret_cast(&OrientTop), v); + + XMStoreFloat3(reinterpret_cast(&Position), newPos); + } + } + + void __cdecl SetOmnidirectional() noexcept + { + pCone = nullptr; + } + + // Only used for single-channel emitters. + void __cdecl SetCone(const X3DAUDIO_CONE& emitterCone); + + // Set multi-channel emitter azimuths based on speaker configuration geometry. + void __cdecl EnableDefaultMultiChannel(unsigned int channels, float radius = 1.f); + + // Set default volume, LFE, LPF, and reverb curves. + void __cdecl EnableDefaultCurves() noexcept; + void __cdecl EnableLinearCurves() noexcept; + + void __cdecl EnableInverseSquareCurves() noexcept + { + pVolumeCurve = nullptr; + pLFECurve = nullptr; + pLPFDirectCurve = nullptr; + pLPFReverbCurve = nullptr; + pReverbCurve = nullptr; + } + }; + + + //---------------------------------------------------------------------------------- + class SoundEffectInstance + { + public: + SoundEffectInstance(SoundEffectInstance&&) noexcept; + SoundEffectInstance& operator= (SoundEffectInstance&&) noexcept; + + SoundEffectInstance(SoundEffectInstance const&) = delete; + SoundEffectInstance& operator= (SoundEffectInstance const&) = delete; + + virtual ~SoundEffectInstance(); + + void __cdecl Play(bool loop = false); + void __cdecl Stop(bool immediate = true) noexcept; + void __cdecl Pause() noexcept; + void __cdecl Resume(); + + void __cdecl SetVolume(float volume); + void __cdecl SetPitch(float pitch); + void __cdecl SetPan(float pan); + + void __cdecl Apply3D(const X3DAUDIO_LISTENER& listener, const X3DAUDIO_EMITTER& emitter, bool rhcoords = true); + + bool __cdecl IsLooped() const noexcept; + + SoundState __cdecl GetState() noexcept; + + unsigned int __cdecl GetChannelCount() const noexcept; + + IVoiceNotify* __cdecl GetVoiceNotify() const noexcept; + + private: + // Private implementation. + class Impl; + + std::unique_ptr pImpl; + + // Private constructors + SoundEffectInstance(_In_ AudioEngine* engine, _In_ SoundEffect* effect, SOUND_EFFECT_INSTANCE_FLAGS flags); + SoundEffectInstance(_In_ AudioEngine* engine, _In_ WaveBank* effect, unsigned int index, SOUND_EFFECT_INSTANCE_FLAGS flags); + + friend std::unique_ptr __cdecl SoundEffect::CreateInstance(SOUND_EFFECT_INSTANCE_FLAGS); + friend std::unique_ptr __cdecl WaveBank::CreateInstance(unsigned int, SOUND_EFFECT_INSTANCE_FLAGS); + }; + + + //---------------------------------------------------------------------------------- + class SoundStreamInstance + { + public: + SoundStreamInstance(SoundStreamInstance&&) noexcept; + SoundStreamInstance& operator= (SoundStreamInstance&&) noexcept; + + SoundStreamInstance(SoundStreamInstance const&) = delete; + SoundStreamInstance& operator= (SoundStreamInstance const&) = delete; + + virtual ~SoundStreamInstance(); + + void __cdecl Play(bool loop = false); + void __cdecl Stop(bool immediate = true) noexcept; + void __cdecl Pause() noexcept; + void __cdecl Resume(); + + void __cdecl SetVolume(float volume); + void __cdecl SetPitch(float pitch); + void __cdecl SetPan(float pan); + + void __cdecl Apply3D(const X3DAUDIO_LISTENER& listener, const X3DAUDIO_EMITTER& emitter, bool rhcoords = true); + + bool __cdecl IsLooped() const noexcept; + + SoundState __cdecl GetState() noexcept; + + unsigned int __cdecl GetChannelCount() const noexcept; + + IVoiceNotify* __cdecl GetVoiceNotify() const noexcept; + + private: + // Private implementation. + class Impl; + + std::unique_ptr pImpl; + + // Private constructors + SoundStreamInstance(_In_ AudioEngine* engine, _In_ WaveBank* effect, unsigned int index, SOUND_EFFECT_INSTANCE_FLAGS flags); + + friend std::unique_ptr __cdecl WaveBank::CreateStreamInstance(unsigned int, SOUND_EFFECT_INSTANCE_FLAGS); + }; + + + //---------------------------------------------------------------------------------- + class DynamicSoundEffectInstance + { + public: + DynamicSoundEffectInstance(_In_ AudioEngine* engine, + _In_ std::function bufferNeeded, + int sampleRate, int channels, int sampleBits = 16, + SOUND_EFFECT_INSTANCE_FLAGS flags = SoundEffectInstance_Default); + + DynamicSoundEffectInstance(DynamicSoundEffectInstance&&) noexcept; + DynamicSoundEffectInstance& operator= (DynamicSoundEffectInstance&&) noexcept; + + DynamicSoundEffectInstance(DynamicSoundEffectInstance const&) = delete; + DynamicSoundEffectInstance& operator= (DynamicSoundEffectInstance const&) = delete; + + virtual ~DynamicSoundEffectInstance(); + + void __cdecl Play(); + void __cdecl Stop(bool immediate = true) noexcept; + void __cdecl Pause() noexcept; + void __cdecl Resume(); + + void __cdecl SetVolume(float volume); + void __cdecl SetPitch(float pitch); + void __cdecl SetPan(float pan); + + void __cdecl Apply3D(const X3DAUDIO_LISTENER& listener, const X3DAUDIO_EMITTER& emitter, bool rhcoords = true); + + void __cdecl SubmitBuffer(_In_reads_bytes_(audioBytes) const uint8_t* pAudioData, size_t audioBytes); + void __cdecl SubmitBuffer(_In_reads_bytes_(audioBytes) const uint8_t* pAudioData, uint32_t offset, size_t audioBytes); + + SoundState __cdecl GetState() noexcept; + + size_t __cdecl GetSampleDuration(size_t bytes) const noexcept; + // Returns duration in samples of a buffer of a given size + + size_t __cdecl GetSampleDurationMS(size_t bytes) const noexcept; + // Returns duration in milliseconds of a buffer of a given size + + size_t __cdecl GetSampleSizeInBytes(uint64_t duration) const noexcept; + // Returns size of a buffer for a duration given in milliseconds + + int __cdecl GetPendingBufferCount() const noexcept; + + const WAVEFORMATEX* __cdecl GetFormat() const noexcept; + + unsigned int __cdecl GetChannelCount() const noexcept; + + private: + // Private implementation. + class Impl; + + std::unique_ptr pImpl; + }; + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-dynamic-exception-spec" +#endif + + DEFINE_ENUM_FLAG_OPERATORS(AUDIO_ENGINE_FLAGS); + DEFINE_ENUM_FLAG_OPERATORS(SOUND_EFFECT_INSTANCE_FLAGS); + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +} diff --git a/Common/DirectXTK12/Inc/BufferHelpers.h b/Common/DirectXTK12/Inc/BufferHelpers.h new file mode 100644 index 0000000..01a7124 --- /dev/null +++ b/Common/DirectXTK12/Inc/BufferHelpers.h @@ -0,0 +1,129 @@ +//-------------------------------------------------------------------------------------- +// File: BufferHelpers.h +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#pragma once + +#ifdef _GAMING_XBOX_SCARLETT +#include +#elif (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) +#include +#elif defined(USING_DIRECTX_HEADERS) +#include +#include +#else +#include +#endif + +#include +#include + + +namespace DirectX +{ + class ResourceUploadBatch; + + // Helpers for creating initialized Direct3D buffer resources. + HRESULT __cdecl CreateStaticBuffer(_In_ ID3D12Device* device, + ResourceUploadBatch& resourceUpload, + _In_reads_bytes_(count* stride) const void* ptr, + size_t count, + size_t stride, + D3D12_RESOURCE_STATES afterState, + _COM_Outptr_ ID3D12Resource** pBuffer, + D3D12_RESOURCE_FLAGS resFlags = D3D12_RESOURCE_FLAG_NONE) noexcept; + + template + HRESULT CreateStaticBuffer(_In_ ID3D12Device* device, + ResourceUploadBatch& resourceUpload, + _In_reads_(count) T const* data, + size_t count, + D3D12_RESOURCE_STATES afterState, + _COM_Outptr_ ID3D12Resource** pBuffer, + D3D12_RESOURCE_FLAGS resFlags = D3D12_RESOURCE_FLAG_NONE) noexcept + { + return CreateStaticBuffer(device, resourceUpload, data, count, sizeof(T), afterState, pBuffer, resFlags); + } + + template + HRESULT CreateStaticBuffer(_In_ ID3D12Device* device, + ResourceUploadBatch& resourceUpload, + T const& data, + D3D12_RESOURCE_STATES afterState, + _COM_Outptr_ ID3D12Resource** pBuffer, + D3D12_RESOURCE_FLAGS resFlags = D3D12_RESOURCE_FLAG_NONE) noexcept + { + return CreateStaticBuffer(device, resourceUpload, data.data(), data.size(), sizeof(typename T::value_type), + afterState, pBuffer, resFlags); + } + + HRESULT __cdecl CreateUAVBuffer(_In_ ID3D12Device* device, + uint64_t bufferSize, + _COM_Outptr_ ID3D12Resource** pBuffer, + D3D12_RESOURCE_STATES initialState = D3D12_RESOURCE_STATE_COMMON, + D3D12_RESOURCE_FLAGS additionalResFlags = D3D12_RESOURCE_FLAG_NONE) noexcept; + + HRESULT __cdecl CreateUploadBuffer(_In_ ID3D12Device* device, + _In_reads_bytes_opt_(count* stride) const void* ptr, + size_t count, + size_t stride, + _COM_Outptr_ ID3D12Resource** pBuffer, + D3D12_RESOURCE_STATES initialState = D3D12_RESOURCE_STATE_GENERIC_READ, + D3D12_RESOURCE_FLAGS resFlags = D3D12_RESOURCE_FLAG_NONE) noexcept; + + template + HRESULT CreateUploadBuffer(_In_ ID3D12Device* device, + _In_reads_(count) T const* data, + size_t count, + _COM_Outptr_ ID3D12Resource** pBuffer, + D3D12_RESOURCE_STATES initialState = D3D12_RESOURCE_STATE_GENERIC_READ, + D3D12_RESOURCE_FLAGS resFlags = D3D12_RESOURCE_FLAG_NONE) noexcept + { + return CreateUploadBuffer(device, data, count, sizeof(T), pBuffer, initialState, resFlags); + } + + template + HRESULT CreateUploadBuffer(_In_ ID3D12Device* device, + T const& data, + _COM_Outptr_ ID3D12Resource** pBuffer, + D3D12_RESOURCE_STATES initialState = D3D12_RESOURCE_STATE_GENERIC_READ, + D3D12_RESOURCE_FLAGS resFlags = D3D12_RESOURCE_FLAG_NONE) noexcept + { + return CreateUploadBuffer(device, data.data(), data.size(), sizeof(typename T::value_type), + pBuffer, initialState, resFlags); + } + + // Helpers for creating texture from memory arrays. + HRESULT __cdecl CreateTextureFromMemory(_In_ ID3D12Device* device, + ResourceUploadBatch& resourceUpload, + size_t width, + DXGI_FORMAT format, + const D3D12_SUBRESOURCE_DATA& initData, + _COM_Outptr_ ID3D12Resource** texture, + D3D12_RESOURCE_STATES afterState = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, + D3D12_RESOURCE_FLAGS resFlags = D3D12_RESOURCE_FLAG_NONE) noexcept; + + HRESULT __cdecl CreateTextureFromMemory(_In_ ID3D12Device* device, + ResourceUploadBatch& resourceUpload, + size_t width, size_t height, + DXGI_FORMAT format, + const D3D12_SUBRESOURCE_DATA& initData, + _COM_Outptr_ ID3D12Resource** texture, + bool generateMips = false, + D3D12_RESOURCE_STATES afterState = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, + D3D12_RESOURCE_FLAGS resFlags = D3D12_RESOURCE_FLAG_NONE) noexcept; + + HRESULT __cdecl CreateTextureFromMemory(_In_ ID3D12Device* device, + ResourceUploadBatch& resourceUpload, + size_t width, size_t height, size_t depth, + DXGI_FORMAT format, + const D3D12_SUBRESOURCE_DATA& initData, + _COM_Outptr_ ID3D12Resource** texture, + D3D12_RESOURCE_STATES afterState = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, + D3D12_RESOURCE_FLAGS resFlags = D3D12_RESOURCE_FLAG_NONE) noexcept; +} diff --git a/Common/DirectXTK12/Inc/CommonStates.h b/Common/DirectXTK12/Inc/CommonStates.h new file mode 100644 index 0000000..7aba709 --- /dev/null +++ b/Common/DirectXTK12/Inc/CommonStates.h @@ -0,0 +1,98 @@ +//-------------------------------------------------------------------------------------- +// File: CommonStates.h +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#pragma once + +#ifdef _GAMING_XBOX_SCARLETT +#include +#elif (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) +#include +#elif defined(USING_DIRECTX_HEADERS) +#include +#include +#else +#include +#endif + +#include + + +namespace DirectX +{ + inline namespace DX12 + { + class CommonStates + { + public: + explicit CommonStates(_In_ ID3D12Device* device); + + CommonStates(CommonStates&&) noexcept; + CommonStates& operator = (CommonStates&&) noexcept; + + CommonStates(const CommonStates&) = delete; + CommonStates& operator = (const CommonStates&) = delete; + + virtual ~CommonStates(); + + // Blend states. + static const D3D12_BLEND_DESC Opaque; + static const D3D12_BLEND_DESC AlphaBlend; + static const D3D12_BLEND_DESC Additive; + static const D3D12_BLEND_DESC NonPremultiplied; + + // Depth stencil states. + static const D3D12_DEPTH_STENCIL_DESC DepthNone; + static const D3D12_DEPTH_STENCIL_DESC DepthDefault; + static const D3D12_DEPTH_STENCIL_DESC DepthRead; + static const D3D12_DEPTH_STENCIL_DESC DepthReverseZ; + static const D3D12_DEPTH_STENCIL_DESC DepthReadReverseZ; + + // Rasterizer states. + static const D3D12_RASTERIZER_DESC CullNone; + static const D3D12_RASTERIZER_DESC CullClockwise; + static const D3D12_RASTERIZER_DESC CullCounterClockwise; + static const D3D12_RASTERIZER_DESC Wireframe; + + // Static sampler states. + static const D3D12_STATIC_SAMPLER_DESC StaticPointWrap(unsigned int shaderRegister, D3D12_SHADER_VISIBILITY shaderVisibility = D3D12_SHADER_VISIBILITY_ALL, unsigned int registerSpace = 0) noexcept; + static const D3D12_STATIC_SAMPLER_DESC StaticPointClamp(unsigned int shaderRegister, D3D12_SHADER_VISIBILITY shaderVisibility = D3D12_SHADER_VISIBILITY_ALL, unsigned int registerSpace = 0) noexcept; + static const D3D12_STATIC_SAMPLER_DESC StaticLinearWrap(unsigned int shaderRegister, D3D12_SHADER_VISIBILITY shaderVisibility = D3D12_SHADER_VISIBILITY_ALL, unsigned int registerSpace = 0) noexcept; + static const D3D12_STATIC_SAMPLER_DESC StaticLinearClamp(unsigned int shaderRegister, D3D12_SHADER_VISIBILITY shaderVisibility = D3D12_SHADER_VISIBILITY_ALL, unsigned int registerSpace = 0) noexcept; + static const D3D12_STATIC_SAMPLER_DESC StaticAnisotropicWrap(unsigned int shaderRegister, D3D12_SHADER_VISIBILITY shaderVisibility = D3D12_SHADER_VISIBILITY_ALL, unsigned int registerSpace = 0) noexcept; + static const D3D12_STATIC_SAMPLER_DESC StaticAnisotropicClamp(unsigned int shaderRegister, D3D12_SHADER_VISIBILITY shaderVisibility = D3D12_SHADER_VISIBILITY_ALL, unsigned int registerSpace = 0) noexcept; + + // Sampler states. + D3D12_GPU_DESCRIPTOR_HANDLE PointWrap() const; + D3D12_GPU_DESCRIPTOR_HANDLE PointClamp() const; + D3D12_GPU_DESCRIPTOR_HANDLE LinearWrap() const; + D3D12_GPU_DESCRIPTOR_HANDLE LinearClamp() const; + D3D12_GPU_DESCRIPTOR_HANDLE AnisotropicWrap() const; + D3D12_GPU_DESCRIPTOR_HANDLE AnisotropicClamp() const; + + // These index into the heap returned by SamplerDescriptorHeap + enum class SamplerIndex + { + PointWrap, + PointClamp, + LinearWrap, + LinearClamp, + AnisotropicWrap, + AnisotropicClamp, + Count + }; + + ID3D12DescriptorHeap* Heap() const noexcept; + + private: + class Impl; + + std::unique_ptr pImpl; + }; + } +} diff --git a/Common/DirectXTK12/Inc/DDSTextureLoader.h b/Common/DirectXTK12/Inc/DDSTextureLoader.h new file mode 100644 index 0000000..414c767 --- /dev/null +++ b/Common/DirectXTK12/Inc/DDSTextureLoader.h @@ -0,0 +1,168 @@ +//-------------------------------------------------------------------------------------- +// File: DDSTextureLoader.h +// +// Functions for loading a DDS texture and creating a Direct3D runtime resource for it +// +// Note these functions are useful as a light-weight runtime loader for DDS files. For +// a full-featured DDS file reader, writer, and texture processing pipeline see +// the 'Texconv' sample and the 'DirectXTex' library. +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#pragma once + +#ifdef _GAMING_XBOX_SCARLETT +#include +#elif (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) +#include +#elif defined(USING_DIRECTX_HEADERS) +#include +#include +#else +#include +#endif + +#include +#include +#include +#include + + +namespace DirectX +{ + class ResourceUploadBatch; + +#ifndef DDS_ALPHA_MODE_DEFINED +#define DDS_ALPHA_MODE_DEFINED + enum DDS_ALPHA_MODE : uint32_t + { + DDS_ALPHA_MODE_UNKNOWN = 0, + DDS_ALPHA_MODE_STRAIGHT = 1, + DDS_ALPHA_MODE_PREMULTIPLIED = 2, + DDS_ALPHA_MODE_OPAQUE = 3, + DDS_ALPHA_MODE_CUSTOM = 4, + }; +#endif + + inline namespace DX12 + { + enum DDS_LOADER_FLAGS : uint32_t + { + DDS_LOADER_DEFAULT = 0, + DDS_LOADER_FORCE_SRGB = 0x1, + DDS_LOADER_IGNORE_SRGB = 0x2, + DDS_LOADER_MIP_AUTOGEN = 0x8, + DDS_LOADER_MIP_RESERVE = 0x10, + }; + } + + // Standard version + HRESULT __cdecl LoadDDSTextureFromMemory( + _In_ ID3D12Device* d3dDevice, + _In_reads_bytes_(ddsDataSize) const uint8_t* ddsData, + size_t ddsDataSize, + _Outptr_ ID3D12Resource** texture, + std::vector& subresources, + size_t maxsize = 0, + _Out_opt_ DDS_ALPHA_MODE* alphaMode = nullptr, + _Out_opt_ bool* isCubeMap = nullptr); + + HRESULT __cdecl LoadDDSTextureFromFile( + _In_ ID3D12Device* d3dDevice, + _In_z_ const wchar_t* szFileName, + _Outptr_ ID3D12Resource** texture, + std::unique_ptr& ddsData, + std::vector& subresources, + size_t maxsize = 0, + _Out_opt_ DDS_ALPHA_MODE* alphaMode = nullptr, + _Out_opt_ bool* isCubeMap = nullptr); + + // Standard version with resource upload + HRESULT __cdecl CreateDDSTextureFromMemory( + _In_ ID3D12Device* device, + ResourceUploadBatch& resourceUpload, + _In_reads_bytes_(ddsDataSize) const uint8_t* ddsData, + size_t ddsDataSize, + _Outptr_ ID3D12Resource** texture, + bool generateMipsIfMissing = false, + size_t maxsize = 0, + _Out_opt_ DDS_ALPHA_MODE* alphaMode = nullptr, + _Out_opt_ bool* isCubeMap = nullptr); + + HRESULT __cdecl CreateDDSTextureFromFile( + _In_ ID3D12Device* device, + ResourceUploadBatch& resourceUpload, + _In_z_ const wchar_t* szFileName, + _Outptr_ ID3D12Resource** texture, + bool generateMipsIfMissing = false, + size_t maxsize = 0, + _Out_opt_ DDS_ALPHA_MODE* alphaMode = nullptr, + _Out_opt_ bool* isCubeMap = nullptr); + + // Extended version + HRESULT __cdecl LoadDDSTextureFromMemoryEx( + _In_ ID3D12Device* d3dDevice, + _In_reads_bytes_(ddsDataSize) const uint8_t* ddsData, + size_t ddsDataSize, + size_t maxsize, + D3D12_RESOURCE_FLAGS resFlags, + DDS_LOADER_FLAGS loadFlags, + _Outptr_ ID3D12Resource** texture, + std::vector& subresources, + _Out_opt_ DDS_ALPHA_MODE* alphaMode = nullptr, + _Out_opt_ bool* isCubeMap = nullptr); + + HRESULT __cdecl LoadDDSTextureFromFileEx( + _In_ ID3D12Device* d3dDevice, + _In_z_ const wchar_t* szFileName, + size_t maxsize, + D3D12_RESOURCE_FLAGS resFlags, + DDS_LOADER_FLAGS loadFlags, + _Outptr_ ID3D12Resource** texture, + std::unique_ptr& ddsData, + std::vector& subresources, + _Out_opt_ DDS_ALPHA_MODE* alphaMode = nullptr, + _Out_opt_ bool* isCubeMap = nullptr); + + // Extended version with resource upload + HRESULT __cdecl CreateDDSTextureFromMemoryEx( + _In_ ID3D12Device* device, + ResourceUploadBatch& resourceUpload, + _In_reads_bytes_(ddsDataSize) const uint8_t* ddsData, + size_t ddsDataSize, + size_t maxsize, + D3D12_RESOURCE_FLAGS resFlags, + DDS_LOADER_FLAGS loadFlags, + _Outptr_ ID3D12Resource** texture, + _Out_opt_ DDS_ALPHA_MODE* alphaMode = nullptr, + _Out_opt_ bool* isCubeMap = nullptr); + + HRESULT __cdecl CreateDDSTextureFromFileEx( + _In_ ID3D12Device* device, + ResourceUploadBatch& resourceUpload, + _In_z_ const wchar_t* szFileName, + size_t maxsize, + D3D12_RESOURCE_FLAGS resFlags, + DDS_LOADER_FLAGS loadFlags, + _Outptr_ ID3D12Resource** texture, + _Out_opt_ DDS_ALPHA_MODE* alphaMode = nullptr, + _Out_opt_ bool* isCubeMap = nullptr); + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-dynamic-exception-spec" +#endif + + inline namespace DX12 + { + DEFINE_ENUM_FLAG_OPERATORS(DDS_LOADER_FLAGS); + } + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +} diff --git a/Common/DirectXTK12/Inc/DescriptorHeap.h b/Common/DirectXTK12/Inc/DescriptorHeap.h new file mode 100644 index 0000000..c8271d8 --- /dev/null +++ b/Common/DirectXTK12/Inc/DescriptorHeap.h @@ -0,0 +1,221 @@ +//-------------------------------------------------------------------------------------- +// File: DescriptorHeap.h +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#pragma once + +#ifdef _GAMING_XBOX_SCARLETT +#include +#elif (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) +#include +#elif defined(USING_DIRECTX_HEADERS) +#include +#include +#else +#include +#endif + +#include +#include +#include +#include + +#include + + +namespace DirectX +{ + // A contiguous linear random-access descriptor heap + class DescriptorHeap + { + public: + DescriptorHeap( + _In_ ID3D12DescriptorHeap* pExistingHeap) noexcept; + DescriptorHeap( + _In_ ID3D12Device* device, + _In_ const D3D12_DESCRIPTOR_HEAP_DESC* pDesc) noexcept(false); + DescriptorHeap( + _In_ ID3D12Device* device, + D3D12_DESCRIPTOR_HEAP_TYPE type, + D3D12_DESCRIPTOR_HEAP_FLAGS flags, + size_t count) noexcept(false); + DescriptorHeap( + _In_ ID3D12Device* device, + size_t count) noexcept(false) : + DescriptorHeap(device, + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, + D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, count) + { + } + + DescriptorHeap(DescriptorHeap&&) = default; + DescriptorHeap& operator=(DescriptorHeap&&) = default; + + DescriptorHeap(const DescriptorHeap&) = delete; + DescriptorHeap& operator=(const DescriptorHeap&) = delete; + + D3D12_GPU_DESCRIPTOR_HANDLE __cdecl WriteDescriptors( + _In_ ID3D12Device* device, + uint32_t offsetIntoHeap, + uint32_t totalDescriptorCount, + _In_reads_(descriptorRangeCount) const D3D12_CPU_DESCRIPTOR_HANDLE* pDescriptorRangeStarts, + _In_reads_(descriptorRangeCount) const uint32_t* pDescriptorRangeSizes, + uint32_t descriptorRangeCount); + + D3D12_GPU_DESCRIPTOR_HANDLE __cdecl WriteDescriptors( + _In_ ID3D12Device* device, + uint32_t offsetIntoHeap, + _In_reads_(descriptorRangeCount) const D3D12_CPU_DESCRIPTOR_HANDLE* pDescriptorRangeStarts, + _In_reads_(descriptorRangeCount) const uint32_t* pDescriptorRangeSizes, + uint32_t descriptorRangeCount); + + D3D12_GPU_DESCRIPTOR_HANDLE __cdecl WriteDescriptors( + _In_ ID3D12Device* device, + uint32_t offsetIntoHeap, + _In_reads_(descriptorCount) const D3D12_CPU_DESCRIPTOR_HANDLE* pDescriptors, + uint32_t descriptorCount); + + D3D12_GPU_DESCRIPTOR_HANDLE GetFirstGpuHandle() const noexcept + { + assert(m_desc.Flags & D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE); + assert(m_pHeap != nullptr); + return m_hGPU; + } + + D3D12_CPU_DESCRIPTOR_HANDLE GetFirstCpuHandle() const noexcept + { + assert(m_pHeap != nullptr); + return m_hCPU; + } + + D3D12_GPU_DESCRIPTOR_HANDLE GetGpuHandle(_In_ size_t index) const + { + assert(m_pHeap != nullptr); + if (index >= m_desc.NumDescriptors) + { + throw std::out_of_range("D3DX12_GPU_DESCRIPTOR_HANDLE"); + } + assert(m_desc.Flags & D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE); + + D3D12_GPU_DESCRIPTOR_HANDLE handle; + handle.ptr = m_hGPU.ptr + UINT64(index) * UINT64(m_increment); + return handle; + } + + D3D12_CPU_DESCRIPTOR_HANDLE GetCpuHandle(_In_ size_t index) const + { + assert(m_pHeap != nullptr); + if (index >= m_desc.NumDescriptors) + { + throw std::out_of_range("D3DX12_CPU_DESCRIPTOR_HANDLE"); + } + + D3D12_CPU_DESCRIPTOR_HANDLE handle; + handle.ptr = static_cast(m_hCPU.ptr + UINT64(index) * UINT64(m_increment)); + return handle; + } + + size_t Count() const noexcept { return m_desc.NumDescriptors; } + unsigned int Flags() const noexcept { return m_desc.Flags; } + D3D12_DESCRIPTOR_HEAP_TYPE Type() const noexcept { return m_desc.Type; } + uint32_t Increment() const noexcept { return m_increment; } + ID3D12DescriptorHeap* Heap() const noexcept { return m_pHeap.Get(); } + + static void __cdecl DefaultDesc( + _In_ D3D12_DESCRIPTOR_HEAP_TYPE type, + _Out_ D3D12_DESCRIPTOR_HEAP_DESC* pDesc) noexcept; + + private: + void Create(_In_ ID3D12Device* pDevice, _In_ const D3D12_DESCRIPTOR_HEAP_DESC* pDesc); + + Microsoft::WRL::ComPtr m_pHeap; + D3D12_DESCRIPTOR_HEAP_DESC m_desc; + D3D12_CPU_DESCRIPTOR_HANDLE m_hCPU; + D3D12_GPU_DESCRIPTOR_HANDLE m_hGPU; + uint32_t m_increment; + }; + + + // Helper class for dynamically allocating descriptor indices. + // The pile is statically sized and will throw an exception if it becomes full. + class DescriptorPile : public DescriptorHeap + { + public: + using IndexType = size_t; + static constexpr IndexType INVALID_INDEX = size_t(-1); + + DescriptorPile( + _In_ ID3D12DescriptorHeap* pExistingHeap, + size_t reserve = 0) noexcept(false) + : DescriptorHeap(pExistingHeap), + m_top(reserve) + { + if (reserve > 0 && m_top >= Count()) + { + throw std::out_of_range("Reserve descriptor range is too large"); + } + } + + DescriptorPile( + _In_ ID3D12Device* device, + _In_ const D3D12_DESCRIPTOR_HEAP_DESC* pDesc, + size_t reserve = 0) noexcept(false) + : DescriptorHeap(device, pDesc), + m_top(reserve) + { + if (reserve > 0 && m_top >= Count()) + { + throw std::out_of_range("Reserve descriptor range is too large"); + } + } + + DescriptorPile( + _In_ ID3D12Device* device, + D3D12_DESCRIPTOR_HEAP_TYPE type, + D3D12_DESCRIPTOR_HEAP_FLAGS flags, + size_t capacity, + size_t reserve = 0) noexcept(false) + : DescriptorHeap(device, type, flags, capacity), + m_top(reserve) + { + if (reserve > 0 && m_top >= Count()) + { + throw std::out_of_range("Reserve descriptor range is too large"); + } + } + + DescriptorPile( + _In_ ID3D12Device* device, + size_t count, + size_t reserve = 0) noexcept(false) : + DescriptorPile(device, + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, + D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, count, reserve) + { + } + + DescriptorPile(const DescriptorPile&) = delete; + DescriptorPile& operator=(const DescriptorPile&) = delete; + + DescriptorPile(DescriptorPile&&) = default; + DescriptorPile& operator=(DescriptorPile&&) = default; + + IndexType Allocate() + { + IndexType start, end; + AllocateRange(1, start, end); + + return start; + } + + void AllocateRange(size_t numDescriptors, _Out_ IndexType& start, _Out_ IndexType& end); + + private: + IndexType m_top; + }; +} diff --git a/Common/DirectXTK12/Inc/DirectXHelpers.h b/Common/DirectXTK12/Inc/DirectXHelpers.h new file mode 100644 index 0000000..1fcd912 --- /dev/null +++ b/Common/DirectXTK12/Inc/DirectXHelpers.h @@ -0,0 +1,363 @@ +//-------------------------------------------------------------------------------------- +// File: DirectXHelpers.h +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#pragma once + +#ifdef _GAMING_XBOX_SCARLETT +#include +#elif (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) +#include +#elif defined(USING_DIRECTX_HEADERS) +#include +#include +#else +#include +#endif + +#include +#include +#include +#include +#include + +#include + +#include + +#ifndef _GAMING_XBOX +#pragma comment(lib,"dxguid.lib") +#endif + +// +// The d3dx12.h header includes the following helper C++ classes and functions +// CD3DX12_RECT +// CD3DX12_VIEWPORT +// CD3DX12_BOX +// CD3DX12_DEPTH_STENCIL_DESC / CD3DX12_DEPTH_STENCIL_DESC1 / CD3DX12_DEPTH_STENCIL_DESC2 +// CD3DX12_BLEND_DESC +// CD3DX12_RASTERIZER_DESC / CD3DX12_RASTERIZER_DESC1 +// CD3DX12_RESOURCE_ALLOCATION_INFO +// CD3DX12_HEAP_PROPERTIES +// CD3DX12_HEAP_DESC +// CD3DX12_CLEAR_VALUE +// CD3DX12_RANGE +// CD3DX12_RANGE_UINT64 +// CD3DX12_SUBRESOURCE_RANGE_UINT64 +// CD3DX12_SHADER_BYTECODE +// CD3DX12_TILED_RESOURCE_COORDINATE +// CD3DX12_TILE_REGION_SIZE +// CD3DX12_SUBRESOURCE_TILING +// CD3DX12_TILE_SHAPE +// CD3DX12_RESOURCE_BARRIER +// CD3DX12_PACKED_MIP_INFO +// CD3DX12_SUBRESOURCE_FOOTPRINT +// CD3DX12_TEXTURE_COPY_LOCATION +// CD3DX12_DESCRIPTOR_RANGE / CD3DX12_DESCRIPTOR_RANGE1 +// CD3DX12_ROOT_DESCRIPTOR_TABLE / CD3DX12_ROOT_DESCRIPTOR_TABLE1 +// CD3DX12_ROOT_CONSTANTS +// CD3DX12_ROOT_DESCRIPTOR / CD3DX12_ROOT_DESCRIPTOR1 +// CD3DX12_ROOT_PARAMETER / CD3DX12_ROOT_PARAMETER1 +// CD3DX12_STATIC_SAMPLER_DESC +// CD3DX12_ROOT_SIGNATURE_DESC +// CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC +// CD3DX12_CPU_DESCRIPTOR_HANDLE +// CD3DX12_GPU_DESCRIPTOR_HANDLE +// CD3DX12_RESOURCE_DESC / CD3DX12_RESOURCE_DESC1 +// CD3DX12_VIEW_INSTANCING_DESC +// CD3DX12_RT_FORMAT_ARRAY +// CD3DX12_MESH_SHADER_PIPELINE_STATE_DESC +// CD3DX12_PIPELINE_STATE_STREAM - CD3DX12_PIPELINE_STATE_STREAM4 +// CD3DX12_PIPELINE_MESH_STATE_STREAM +// CD3DX12_PIPELINE_STATE_STREAM_PARSE_HELPER - CD3DX12_PIPELINE_STATE_STREAM4_PARSE_HELPER +// D3D12CalcSubresource +// D3D12DecomposeSubresource +// D3D12GetFormatPlaneCount +// MemcpySubresource +// GetRequiredIntermediateSize +// UpdateSubresources +// D3D12IsLayoutOpaque +// CommandListCast +// D3DX12SerializeVersionedRootSignature +// D3DX12GetBaseSubobjectType +// D3DX12ParsePipelineStream +// +// CD3DX12_STATE_OBJECT_DESC +// CD3DX12_DXIL_LIBRARY_SUBOBJECT +// CD3DX12_EXISTING_COLLECTION_SUBOBJECT +// CD3DX12_SUBOBJECT_TO_EXPORTS_ASSOCIATION_SUBOBJECT +// CD3DX12_DXIL_SUBOBJECT_TO_EXPORTS_ASSOCIATION +// CD3DX12_HIT_GROUP_SUBOBJECT +// CD3DX12_RAYTRACING_SHADER_CONFIG_SUBOBJECT +// CD3DX12_RAYTRACING_PIPELINE_CONFIG_SUBOBJECT // CD3DX12_RAYTRACING_PIPELINE_CONFIG1_SUBOBJECT +// CD3DX12_GLOBAL_ROOT_SIGNATURE_SUBOBJECT +// CD3DX12_LOCAL_ROOT_SIGNATURE_SUBOBJECT +// CD3DX12_STATE_OBJECT_CONFIG_SUBOBJECT +// CD3DX12_NODE_MASK_SUBOBJECT +// +// CD3DX12_BARRIER_SUBRESOURCE_RANGE +// CD3DX12_GLOBAL_BARRIER +// CD3DX12_BUFFER_BARRIER +// CD3DX12_TEXTURE_BARRIER +// CD3DX12_BARRIER_GROUP +// +// CD3DX12FeatureSupport +// + +#ifndef IID_GRAPHICS_PPV_ARGS +#define IID_GRAPHICS_PPV_ARGS(x) IID_PPV_ARGS(x) +#endif + +namespace DirectX +{ +#if (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) + constexpr D3D12_RESOURCE_STATES c_initialCopyTargetState = D3D12_RESOURCE_STATE_COPY_DEST; +#else + constexpr D3D12_RESOURCE_STATES c_initialCopyTargetState = D3D12_RESOURCE_STATE_COMMON; +#endif + + constexpr D3D12_CPU_DESCRIPTOR_HANDLE D3D12_CPU_DESCRIPTOR_HANDLE_ZERO = {}; + + // Creates a shader resource view from an arbitrary resource + void __cdecl CreateShaderResourceView( + _In_ ID3D12Device* device, + _In_ ID3D12Resource* tex, + D3D12_CPU_DESCRIPTOR_HANDLE srvDescriptor, + bool isCubeMap = false); + + // Creates an unordered access view from an arbitrary resource + void __cdecl CreateUnorderedAccessView( + _In_ ID3D12Device* device, + _In_ ID3D12Resource* tex, + D3D12_CPU_DESCRIPTOR_HANDLE uavDescriptor, + uint32_t mipLevel = 0); + + // Creates an render target view from an arbitrary resource + void __cdecl CreateRenderTargetView( + _In_ ID3D12Device* device, + _In_ ID3D12Resource* tex, + D3D12_CPU_DESCRIPTOR_HANDLE rtvDescriptor, + uint32_t mipLevel = 0); + + // Creates a shader resource view from a buffer resource + void __cdecl CreateBufferShaderResourceView( + _In_ ID3D12Device* device, + _In_ ID3D12Resource* buffer, + D3D12_CPU_DESCRIPTOR_HANDLE srvDescriptor, + uint32_t stride = 0); + + // Creates a unordered access view from a buffer resource + void __cdecl CreateBufferUnorderedAccessView( + _In_ ID3D12Device* device, + _In_ ID3D12Resource* buffer, + D3D12_CPU_DESCRIPTOR_HANDLE uavDescriptor, + uint32_t stride, + D3D12_BUFFER_UAV_FLAGS flag = D3D12_BUFFER_UAV_FLAG_NONE, + uint32_t counterOffset = 0, + _In_opt_ ID3D12Resource* counterResource = nullptr); + + // Shorthand for creating a root signature + inline HRESULT CreateRootSignature( + _In_ ID3D12Device* device, + _In_ const D3D12_ROOT_SIGNATURE_DESC* rootSignatureDesc, + _Out_ ID3D12RootSignature** rootSignature) noexcept + { + Microsoft::WRL::ComPtr pSignature; + Microsoft::WRL::ComPtr pError; + HRESULT hr = D3D12SerializeRootSignature(rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, pSignature.GetAddressOf(), pError.GetAddressOf()); + if (SUCCEEDED(hr)) + { + hr = device->CreateRootSignature(0, pSignature->GetBufferPointer(), pSignature->GetBufferSize(), + IID_GRAPHICS_PPV_ARGS(rootSignature) + ); + } + return hr; + } + + // Helper for obtaining texture size + inline XMUINT2 GetTextureSize(_In_ ID3D12Resource* tex) noexcept + { +#if defined(_MSC_VER) || !defined(_WIN32) + const auto desc = tex->GetDesc(); +#else + D3D12_RESOURCE_DESC tmpDesc; + const auto& desc = *tex->GetDesc(&tmpDesc); +#endif + return XMUINT2(static_cast(desc.Width), static_cast(desc.Height)); + } + +#if defined(_PIX_H_) || defined(_PIX3_H_) + // Scoped PIX event. + class ScopedPixEvent + { + public: + ScopedPixEvent(_In_ ID3D12GraphicsCommandList* pCommandList, UINT64 /*metadata*/, PCWSTR pFormat) noexcept + : mCommandList(pCommandList) + { + PIXBeginEvent(pCommandList, 0, pFormat); + } + ~ScopedPixEvent() + { + PIXEndEvent(mCommandList); + } + + private: + ID3D12GraphicsCommandList* mCommandList; + }; +#endif + + // Helper sets a D3D resource name string (used by PIX and debug layer leak reporting). + #if !defined(NO_D3D12_DEBUG_NAME) && (defined(_DEBUG) || defined(PROFILE)) + template + inline void SetDebugObjectName(_In_ ID3D12DeviceChild* resource, _In_z_ const char(&name)[TNameLength]) noexcept + { + wchar_t wname[MAX_PATH]; + int result = MultiByteToWideChar(CP_UTF8, 0, name, TNameLength, wname, MAX_PATH); + if (result > 0) + { + resource->SetName(wname); + } + } + + template + inline void SetDebugObjectName(_In_ ID3D12DeviceChild* resource, _In_z_ const wchar_t(&name)[TNameLength]) noexcept + { + resource->SetName(name); + } + #else + template + inline void SetDebugObjectName(_In_ ID3D12DeviceChild*, _In_z_ const char(&)[TNameLength]) noexcept + { + } + + template + inline void SetDebugObjectName(_In_ ID3D12DeviceChild*, _In_z_ const wchar_t(&)[TNameLength]) noexcept + { + } + #endif + + // Helper for resource barrier. + inline void TransitionResource( + _In_ ID3D12GraphicsCommandList* commandList, + _In_ ID3D12Resource* resource, + D3D12_RESOURCE_STATES stateBefore, + D3D12_RESOURCE_STATES stateAfter) noexcept + { + assert(commandList != nullptr); + assert(resource != nullptr); + + if (stateBefore == stateAfter) + return; + + D3D12_RESOURCE_BARRIER desc = {}; + desc.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + desc.Transition.pResource = resource; + desc.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + desc.Transition.StateBefore = stateBefore; + desc.Transition.StateAfter = stateAfter; + + commandList->ResourceBarrier(1, &desc); + } + + // Helper which applies one or more resources barriers and then reverses them on destruction. + class ScopedBarrier + { + public: + ScopedBarrier( + _In_ ID3D12GraphicsCommandList* commandList, + std::initializer_list barriers) noexcept(false) + : mCommandList(commandList), + mBarriers(barriers) + { + assert(mBarriers.size() <= UINT32_MAX); + + // Set barriers + mCommandList->ResourceBarrier(static_cast(mBarriers.size()), mBarriers.data()); + } + + ScopedBarrier( + _In_ ID3D12GraphicsCommandList* commandList, + _In_reads_(count) const D3D12_RESOURCE_BARRIER *barriers, + size_t count) noexcept(false) + : mCommandList(commandList), + mBarriers(barriers, barriers + count) + { + assert(count <= UINT32_MAX); + + // Set barriers + mCommandList->ResourceBarrier(static_cast(mBarriers.size()), mBarriers.data()); + } + + template + ScopedBarrier( + _In_ ID3D12GraphicsCommandList* commandList, + const D3D12_RESOURCE_BARRIER(&barriers)[TBarrierLength]) noexcept(false) + : mCommandList(commandList), + mBarriers(barriers, barriers + TBarrierLength) + { + assert(TBarrierLength <= UINT32_MAX); + + // Set barriers + mCommandList->ResourceBarrier(static_cast(mBarriers.size()), mBarriers.data()); + } + + ScopedBarrier(ScopedBarrier&&) = default; + ScopedBarrier& operator= (ScopedBarrier&&) = default; + + ScopedBarrier(ScopedBarrier const&) = delete; + ScopedBarrier& operator= (ScopedBarrier const&) = delete; + + ~ScopedBarrier() + { + // reverse barrier inputs and outputs + for (auto& b : mBarriers) + { + std::swap(b.Transition.StateAfter, b.Transition.StateBefore); + } + + // Set barriers + mCommandList->ResourceBarrier(static_cast(mBarriers.size()), mBarriers.data()); + } + + private: + ID3D12GraphicsCommandList* mCommandList; + std::vector mBarriers; + }; + + inline namespace DX12 + { + // Helper to check for power-of-2 + template + constexpr bool IsPowerOf2(T x) noexcept { return ((x != 0) && !(x & (x - 1))); } + + // Helpers for aligning values by a power of 2 + template + inline T AlignDown(T size, size_t alignment) noexcept + { + if (alignment > 0) + { + assert(((alignment - 1) & alignment) == 0); + auto mask = static_cast(alignment - 1); + return size & ~mask; + } + return size; + } + + template + inline T AlignUp(T size, size_t alignment) noexcept + { + if (alignment > 0) + { + assert(((alignment - 1) & alignment) == 0); + auto mask = static_cast(alignment - 1); + return (size + mask) & ~mask; + } + return size; + } + } +} diff --git a/Common/DirectXTK12/Inc/EffectPipelineStateDescription.h b/Common/DirectXTK12/Inc/EffectPipelineStateDescription.h new file mode 100644 index 0000000..60451d8 --- /dev/null +++ b/Common/DirectXTK12/Inc/EffectPipelineStateDescription.h @@ -0,0 +1,121 @@ +//-------------------------------------------------------------------------------------- +// File: EffectPipelineStateDescription.h +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#pragma once + +#ifdef _GAMING_XBOX_SCARLETT +#include +#elif (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) +#include +#elif defined(USING_DIRECTX_HEADERS) +#include +#include +#include +#else +#include +#include +#endif + +#include +#include + +#include "RenderTargetState.h" + + +namespace DirectX +{ + // Pipeline state information for creating effects. + struct EffectPipelineStateDescription + { + EffectPipelineStateDescription( + _In_opt_ const D3D12_INPUT_LAYOUT_DESC* iinputLayout, + const D3D12_BLEND_DESC& blend, + const D3D12_DEPTH_STENCIL_DESC& depthStencil, + const D3D12_RASTERIZER_DESC& rasterizer, + const RenderTargetState& renderTarget, + D3D12_PRIMITIVE_TOPOLOGY_TYPE iprimitiveTopology = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE, + D3D12_INDEX_BUFFER_STRIP_CUT_VALUE istripCutValue = D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_DISABLED) noexcept + : + inputLayout{}, + blendDesc(blend), + depthStencilDesc(depthStencil), + rasterizerDesc(rasterizer), + renderTargetState(renderTarget), + primitiveTopology(iprimitiveTopology), + stripCutValue(istripCutValue) + { + if (iinputLayout) + this->inputLayout = *iinputLayout; + } + + EffectPipelineStateDescription(const EffectPipelineStateDescription&) = default; + EffectPipelineStateDescription& operator=(const EffectPipelineStateDescription&) = default; + + EffectPipelineStateDescription(EffectPipelineStateDescription&&) = default; + EffectPipelineStateDescription& operator=(EffectPipelineStateDescription&&) = default; + + void CreatePipelineState( + _In_ ID3D12Device* device, + _In_ ID3D12RootSignature* rootSignature, + const D3D12_SHADER_BYTECODE& vertexShader, + const D3D12_SHADER_BYTECODE& pixelShader, + _Outptr_ ID3D12PipelineState** pPipelineState) const; + + #if defined(_MSC_VER) || !defined(_WIN32) + D3D12_GRAPHICS_PIPELINE_STATE_DESC GetDesc() const noexcept + { + D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {}; + psoDesc.BlendState = blendDesc; + psoDesc.SampleMask = renderTargetState.sampleMask; + psoDesc.RasterizerState = rasterizerDesc; + psoDesc.DepthStencilState = depthStencilDesc; + psoDesc.InputLayout = inputLayout; + psoDesc.IBStripCutValue = stripCutValue; + psoDesc.PrimitiveTopologyType = primitiveTopology; + psoDesc.NumRenderTargets = renderTargetState.numRenderTargets; + memcpy(psoDesc.RTVFormats, renderTargetState.rtvFormats, sizeof(DXGI_FORMAT) * D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT); + psoDesc.DSVFormat = renderTargetState.dsvFormat; + psoDesc.SampleDesc = renderTargetState.sampleDesc; + psoDesc.NodeMask = renderTargetState.nodeMask; + return psoDesc; + } + #else + D3D12_GRAPHICS_PIPELINE_STATE_DESC* GetDesc(_Out_ D3D12_GRAPHICS_PIPELINE_STATE_DESC* psoDesc) const noexcept + { + if (!psoDesc) + return nullptr; + + *psoDesc = {}; + psoDesc->BlendState = blendDesc; + psoDesc->SampleMask = renderTargetState.sampleMask; + psoDesc->RasterizerState = rasterizerDesc; + psoDesc->DepthStencilState = depthStencilDesc; + psoDesc->InputLayout = inputLayout; + psoDesc->IBStripCutValue = stripCutValue; + psoDesc->PrimitiveTopologyType = primitiveTopology; + psoDesc->NumRenderTargets = renderTargetState.numRenderTargets; + memcpy(psoDesc->RTVFormats, renderTargetState.rtvFormats, sizeof(DXGI_FORMAT) * D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT); + psoDesc->DSVFormat = renderTargetState.dsvFormat; + psoDesc->SampleDesc = renderTargetState.sampleDesc; + psoDesc->NodeMask = renderTargetState.nodeMask; + return psoDesc; + } + #endif + + uint32_t ComputeHash() const noexcept; + + D3D12_INPUT_LAYOUT_DESC inputLayout; + D3D12_BLEND_DESC blendDesc; + D3D12_DEPTH_STENCIL_DESC depthStencilDesc; + D3D12_RASTERIZER_DESC rasterizerDesc; + RenderTargetState renderTargetState; + D3D12_PRIMITIVE_TOPOLOGY_TYPE primitiveTopology; + D3D12_INDEX_BUFFER_STRIP_CUT_VALUE stripCutValue; + }; +} diff --git a/Common/DirectXTK12/Inc/Effects.h b/Common/DirectXTK12/Inc/Effects.h new file mode 100644 index 0000000..0d54f6b --- /dev/null +++ b/Common/DirectXTK12/Inc/Effects.h @@ -0,0 +1,937 @@ +//-------------------------------------------------------------------------------------- +// File: Effects.h +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#pragma once + +#ifndef __DIRECTXTK_EFFECTS_H__ +#define __DIRECTXTK_EFFECTS_H__ + +#ifdef _GAMING_XBOX_SCARLETT +#include +#elif (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) +#include +#elif defined(USING_DIRECTX_HEADERS) +#include +#include +#else +#include +#endif + +#include +#include +#include +#include + +#include + +#include "RenderTargetState.h" +#include "EffectPipelineStateDescription.h" + + +namespace DirectX +{ + class DescriptorHeap; + class ResourceUploadBatch; + + inline namespace DX12 + { + //------------------------------------------------------------------------------ + // Abstract interface representing any effect which can be applied onto a D3D device context. + class IEffect + { + public: + virtual ~IEffect() = default; + + IEffect(const IEffect&) = delete; + IEffect& operator=(const IEffect&) = delete; + + virtual void __cdecl Apply(_In_ ID3D12GraphicsCommandList* commandList) = 0; + + protected: + IEffect() = default; + IEffect(IEffect&&) = default; + IEffect& operator=(IEffect&&) = default; + }; + + + // Abstract interface for effects with world, view, and projection matrices. + class IEffectMatrices + { + public: + virtual ~IEffectMatrices() = default; + + IEffectMatrices(const IEffectMatrices&) = delete; + IEffectMatrices& operator=(const IEffectMatrices&) = delete; + + virtual void XM_CALLCONV SetWorld(FXMMATRIX value) = 0; + virtual void XM_CALLCONV SetView(FXMMATRIX value) = 0; + virtual void XM_CALLCONV SetProjection(FXMMATRIX value) = 0; + virtual void XM_CALLCONV SetMatrices(FXMMATRIX world, CXMMATRIX view, CXMMATRIX projection); + + protected: + IEffectMatrices() = default; + IEffectMatrices(IEffectMatrices&&) = default; + IEffectMatrices& operator=(IEffectMatrices&&) = default; + }; + + + // Abstract interface for effects which support directional lighting. + class IEffectLights + { + public: + virtual ~IEffectLights() = default; + + IEffectLights(const IEffectLights&) = delete; + IEffectLights& operator=(const IEffectLights&) = delete; + + virtual void XM_CALLCONV SetAmbientLightColor(FXMVECTOR value) = 0; + + virtual void __cdecl SetLightEnabled(int whichLight, bool value) = 0; + virtual void XM_CALLCONV SetLightDirection(int whichLight, FXMVECTOR value) = 0; + virtual void XM_CALLCONV SetLightDiffuseColor(int whichLight, FXMVECTOR value) = 0; + virtual void XM_CALLCONV SetLightSpecularColor(int whichLight, FXMVECTOR value) = 0; + + virtual void __cdecl EnableDefaultLighting() = 0; + + static constexpr int MaxDirectionalLights = 3; + + protected: + IEffectLights() = default; + IEffectLights(IEffectLights&&) = default; + IEffectLights& operator=(IEffectLights&&) = default; + }; + + + // Abstract interface for effects which support fog. + class IEffectFog + { + public: + virtual ~IEffectFog() = default; + + IEffectFog(const IEffectFog&) = delete; + IEffectFog& operator=(const IEffectFog&) = delete; + + virtual void __cdecl SetFogStart(float value) = 0; + virtual void __cdecl SetFogEnd(float value) = 0; + virtual void XM_CALLCONV SetFogColor(FXMVECTOR value) = 0; + + protected: + IEffectFog() = default; + IEffectFog(IEffectFog&&) = default; + IEffectFog& operator=(IEffectFog&&) = default; + }; + + + // Abstract interface for effects which support skinning + class IEffectSkinning + { + public: + virtual ~IEffectSkinning() = default; + + IEffectSkinning(const IEffectSkinning&) = delete; + IEffectSkinning& operator=(const IEffectSkinning&) = delete; + + virtual void __cdecl SetBoneTransforms(_In_reads_(count) XMMATRIX const* value, size_t count) = 0; + virtual void __cdecl ResetBoneTransforms() = 0; + + static constexpr int MaxBones = 72; + + protected: + IEffectSkinning() = default; + IEffectSkinning(IEffectSkinning&&) = default; + IEffectSkinning& operator=(IEffectSkinning&&) = default; + }; + + + //------------------------------------------------------------------------------ + namespace EffectFlags + { + constexpr uint32_t None = 0x00; + constexpr uint32_t Fog = 0x01; + constexpr uint32_t Lighting = 0x02; + + constexpr uint32_t PerPixelLighting = 0x04 | Lighting; + // per pixel lighting implies lighting enabled + + constexpr uint32_t VertexColor = 0x08; + constexpr uint32_t Texture = 0x10; + constexpr uint32_t Instancing = 0x20; + + constexpr uint32_t Specular = 0x100; + // enable optional specular/specularMap feature + + constexpr uint32_t Emissive = 0x200; + // enable optional emissive/emissiveMap feature + + constexpr uint32_t Fresnel = 0x400; + // enable optional Fresnel feature + + constexpr uint32_t Velocity = 0x800; + // enable optional velocity feature + + constexpr uint32_t BiasedVertexNormals = 0x10000; + // compressed vertex normals need x2 bias + } + + + //------------------------------------------------------------------------------ + // Built-in shader supports optional texture mapping, vertex coloring, directional lighting, and fog. + class BasicEffect : public IEffect, public IEffectMatrices, public IEffectLights, public IEffectFog + { + public: + BasicEffect(_In_ ID3D12Device* device, uint32_t effectFlags, const EffectPipelineStateDescription& pipelineDescription); + + BasicEffect(BasicEffect&&) noexcept; + BasicEffect& operator= (BasicEffect&&) noexcept; + + BasicEffect(BasicEffect const&) = delete; + BasicEffect& operator= (BasicEffect const&) = delete; + + ~BasicEffect() override; + + // IEffect methods. + void __cdecl Apply(_In_ ID3D12GraphicsCommandList* commandList) override; + + // Camera settings. + void XM_CALLCONV SetWorld(FXMMATRIX value) override; + void XM_CALLCONV SetView(FXMMATRIX value) override; + void XM_CALLCONV SetProjection(FXMMATRIX value) override; + void XM_CALLCONV SetMatrices(FXMMATRIX world, CXMMATRIX view, CXMMATRIX projection) override; + + // Material settings. + void XM_CALLCONV SetDiffuseColor(FXMVECTOR value); + void XM_CALLCONV SetEmissiveColor(FXMVECTOR value); + void XM_CALLCONV SetSpecularColor(FXMVECTOR value); + void __cdecl SetSpecularPower(float value); + void __cdecl DisableSpecular(); + void __cdecl SetAlpha(float value); + void XM_CALLCONV SetColorAndAlpha(FXMVECTOR value); + + // Light settings. + void XM_CALLCONV SetAmbientLightColor(FXMVECTOR value) override; + + void __cdecl SetLightEnabled(int whichLight, bool value) override; + void XM_CALLCONV SetLightDirection(int whichLight, FXMVECTOR value) override; + void XM_CALLCONV SetLightDiffuseColor(int whichLight, FXMVECTOR value) override; + void XM_CALLCONV SetLightSpecularColor(int whichLight, FXMVECTOR value) override; + + void __cdecl EnableDefaultLighting() override; + + // Fog settings. + void __cdecl SetFogStart(float value) override; + void __cdecl SetFogEnd(float value) override; + void XM_CALLCONV SetFogColor(FXMVECTOR value) override; + + // Texture setting. + void __cdecl SetTexture(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor, D3D12_GPU_DESCRIPTOR_HANDLE samplerDescriptor); + + private: + // Private implementation. + class Impl; + + std::unique_ptr pImpl; + }; + + + // Built-in shader supports per-pixel alpha testing. + class AlphaTestEffect : public IEffect, public IEffectMatrices, public IEffectFog + { + public: + AlphaTestEffect(_In_ ID3D12Device* device, uint32_t effectFlags, + const EffectPipelineStateDescription& pipelineDescription, + D3D12_COMPARISON_FUNC alphaFunction = D3D12_COMPARISON_FUNC_GREATER); + + AlphaTestEffect(AlphaTestEffect&&) noexcept; + AlphaTestEffect& operator= (AlphaTestEffect&&) noexcept; + + AlphaTestEffect(AlphaTestEffect const&) = delete; + AlphaTestEffect& operator= (AlphaTestEffect const&) = delete; + + ~AlphaTestEffect() override; + + // IEffect methods. + void __cdecl Apply(_In_ ID3D12GraphicsCommandList* commandList) override; + + // Camera settings. + void XM_CALLCONV SetWorld(FXMMATRIX value) override; + void XM_CALLCONV SetView(FXMMATRIX value) override; + void XM_CALLCONV SetProjection(FXMMATRIX value) override; + void XM_CALLCONV SetMatrices(FXMMATRIX world, CXMMATRIX view, CXMMATRIX projection) override; + + // Material settings. + void XM_CALLCONV SetDiffuseColor(FXMVECTOR value); + void __cdecl SetAlpha(float value); + void XM_CALLCONV SetColorAndAlpha(FXMVECTOR value); + + // Fog settings. + void __cdecl SetFogStart(float value) override; + void __cdecl SetFogEnd(float value) override; + void XM_CALLCONV SetFogColor(FXMVECTOR value) override; + + // Texture setting. + void __cdecl SetTexture(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor, D3D12_GPU_DESCRIPTOR_HANDLE samplerDescriptor); + + // Alpha test settings. + void __cdecl SetReferenceAlpha(int value); + + private: + // Private implementation. + class Impl; + + std::unique_ptr pImpl; + }; + + + // Built-in shader supports two layer multitexturing (eg. for lightmaps or detail textures). + class DualTextureEffect : public IEffect, public IEffectMatrices, public IEffectFog + { + public: + DualTextureEffect(_In_ ID3D12Device* device, uint32_t effectFlags, + const EffectPipelineStateDescription& pipelineDescription); + + DualTextureEffect(DualTextureEffect&&) noexcept; + DualTextureEffect& operator= (DualTextureEffect&&) noexcept; + + DualTextureEffect(DualTextureEffect const&) = delete; + DualTextureEffect& operator= (DualTextureEffect const&) = delete; + + ~DualTextureEffect() override; + + // IEffect methods. + void __cdecl Apply(_In_ ID3D12GraphicsCommandList* commandList) override; + + // Camera settings. + void XM_CALLCONV SetWorld(FXMMATRIX value) override; + void XM_CALLCONV SetView(FXMMATRIX value) override; + void XM_CALLCONV SetProjection(FXMMATRIX value) override; + void XM_CALLCONV SetMatrices(FXMMATRIX world, CXMMATRIX view, CXMMATRIX projection) override; + + // Material settings. + void XM_CALLCONV SetDiffuseColor(FXMVECTOR value); + void __cdecl SetAlpha(float value); + void XM_CALLCONV SetColorAndAlpha(FXMVECTOR value); + + // Fog settings. + void __cdecl SetFogStart(float value) override; + void __cdecl SetFogEnd(float value) override; + void XM_CALLCONV SetFogColor(FXMVECTOR value) override; + + // Texture settings. + void __cdecl SetTexture(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor, D3D12_GPU_DESCRIPTOR_HANDLE samplerDescriptor); + void __cdecl SetTexture2(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor, D3D12_GPU_DESCRIPTOR_HANDLE samplerDescriptor); + + private: + // Private implementation. + class Impl; + + std::unique_ptr pImpl; + }; + + + // Built-in shader supports cubic environment mapping. + class EnvironmentMapEffect : public IEffect, public IEffectMatrices, public IEffectLights, public IEffectFog + { + public: + enum Mapping + { + Mapping_Cube = 0, // Cubic environment map + Mapping_Sphere, // Spherical environment map + Mapping_DualParabola, // Dual-parabola environment map (requires Feature Level 10.0) + }; + + EnvironmentMapEffect(_In_ ID3D12Device* device, uint32_t effectFlags, + const EffectPipelineStateDescription& pipelineDescription, + Mapping mapping = Mapping_Cube); + + EnvironmentMapEffect(EnvironmentMapEffect&&) noexcept; + EnvironmentMapEffect& operator= (EnvironmentMapEffect&&) noexcept; + + EnvironmentMapEffect(EnvironmentMapEffect const&) = delete; + EnvironmentMapEffect& operator= (EnvironmentMapEffect const&) = delete; + + ~EnvironmentMapEffect() override; + + // IEffect methods. + void __cdecl Apply(_In_ ID3D12GraphicsCommandList* commandList) override; + + // Camera settings. + void XM_CALLCONV SetWorld(FXMMATRIX value) override; + void XM_CALLCONV SetView(FXMMATRIX value) override; + void XM_CALLCONV SetProjection(FXMMATRIX value) override; + void XM_CALLCONV SetMatrices(FXMMATRIX world, CXMMATRIX view, CXMMATRIX projection) override; + + // Material settings. + void XM_CALLCONV SetDiffuseColor(FXMVECTOR value); + void XM_CALLCONV SetEmissiveColor(FXMVECTOR value); + void __cdecl SetAlpha(float value); + void XM_CALLCONV SetColorAndAlpha(FXMVECTOR value); + + // Light settings. + void XM_CALLCONV SetAmbientLightColor(FXMVECTOR value) override; + + void __cdecl SetLightEnabled(int whichLight, bool value) override; + void XM_CALLCONV SetLightDirection(int whichLight, FXMVECTOR value) override; + void XM_CALLCONV SetLightDiffuseColor(int whichLight, FXMVECTOR value) override; + + void __cdecl EnableDefaultLighting() override; + + // Fog settings. + void __cdecl SetFogStart(float value) override; + void __cdecl SetFogEnd(float value) override; + void XM_CALLCONV SetFogColor(FXMVECTOR value) override; + + // Texture setting. + void __cdecl SetTexture(D3D12_GPU_DESCRIPTOR_HANDLE texture, D3D12_GPU_DESCRIPTOR_HANDLE sampler); + + // Environment map settings. + void __cdecl SetEnvironmentMap(D3D12_GPU_DESCRIPTOR_HANDLE texture, D3D12_GPU_DESCRIPTOR_HANDLE sampler); + void __cdecl SetEnvironmentMapAmount(float value); + void XM_CALLCONV SetEnvironmentMapSpecular(FXMVECTOR value); + void __cdecl SetFresnelFactor(float value); + + private: + // Private implementation. + class Impl; + + std::unique_ptr pImpl; + + // Unsupported interface methods. + void XM_CALLCONV SetLightSpecularColor(int whichLight, FXMVECTOR value) override; + }; + + + // Built-in shader supports skinned animation. + class SkinnedEffect : public IEffect, public IEffectMatrices, public IEffectLights, public IEffectFog, public IEffectSkinning + { + public: + SkinnedEffect(_In_ ID3D12Device* device, uint32_t effectFlags, + const EffectPipelineStateDescription& pipelineDescription); + + SkinnedEffect(SkinnedEffect&&) noexcept; + SkinnedEffect& operator= (SkinnedEffect&&) noexcept; + + SkinnedEffect(SkinnedEffect const&) = delete; + SkinnedEffect& operator= (SkinnedEffect const&) = delete; + + ~SkinnedEffect() override; + + // IEffect methods. + void __cdecl Apply(_In_ ID3D12GraphicsCommandList* commandList) override; + + // Camera settings. + void XM_CALLCONV SetWorld(FXMMATRIX value) override; + void XM_CALLCONV SetView(FXMMATRIX value) override; + void XM_CALLCONV SetProjection(FXMMATRIX value) override; + void XM_CALLCONV SetMatrices(FXMMATRIX world, CXMMATRIX view, CXMMATRIX projection) override; + + // Material settings. + void XM_CALLCONV SetDiffuseColor(FXMVECTOR value); + void XM_CALLCONV SetEmissiveColor(FXMVECTOR value); + void XM_CALLCONV SetSpecularColor(FXMVECTOR value); + void __cdecl SetSpecularPower(float value); + void __cdecl DisableSpecular(); + void __cdecl SetAlpha(float value); + void XM_CALLCONV SetColorAndAlpha(FXMVECTOR value); + + // Light settings. + void XM_CALLCONV SetAmbientLightColor(FXMVECTOR value) override; + + void __cdecl SetLightEnabled(int whichLight, bool value) override; + void XM_CALLCONV SetLightDirection(int whichLight, FXMVECTOR value) override; + void XM_CALLCONV SetLightDiffuseColor(int whichLight, FXMVECTOR value) override; + void XM_CALLCONV SetLightSpecularColor(int whichLight, FXMVECTOR value) override; + + void __cdecl EnableDefaultLighting() override; + + // Fog settings. + void __cdecl SetFogStart(float value) override; + void __cdecl SetFogEnd(float value) override; + void XM_CALLCONV SetFogColor(FXMVECTOR value) override; + + // Texture setting. + void __cdecl SetTexture(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor, D3D12_GPU_DESCRIPTOR_HANDLE samplerDescriptor); + + // Animation settings. + void __cdecl SetBoneTransforms(_In_reads_(count) XMMATRIX const* value, size_t count) override; + void __cdecl ResetBoneTransforms() override; + + private: + // Private implementation. + class Impl; + + std::unique_ptr pImpl; + }; + + + //------------------------------------------------------------------------------ + // Built-in shader extends BasicEffect with normal map and optional specular map + class NormalMapEffect : public IEffect, public IEffectMatrices, public IEffectLights, public IEffectFog + { + public: + NormalMapEffect(_In_ ID3D12Device* device, uint32_t effectFlags, + const EffectPipelineStateDescription& pipelineDescription) : + NormalMapEffect(device, effectFlags, pipelineDescription, false) + { + } + + NormalMapEffect(NormalMapEffect&&) noexcept; + NormalMapEffect& operator= (NormalMapEffect&&) noexcept; + + NormalMapEffect(NormalMapEffect const&) = delete; + NormalMapEffect& operator= (NormalMapEffect const&) = delete; + + ~NormalMapEffect() override; + + // IEffect methods. + void __cdecl Apply(_In_ ID3D12GraphicsCommandList* commandList) override; + + // Camera settings. + void XM_CALLCONV SetWorld(FXMMATRIX value) override; + void XM_CALLCONV SetView(FXMMATRIX value) override; + void XM_CALLCONV SetProjection(FXMMATRIX value) override; + void XM_CALLCONV SetMatrices(FXMMATRIX world, CXMMATRIX view, CXMMATRIX projection) override; + + // Material settings. + void XM_CALLCONV SetDiffuseColor(FXMVECTOR value); + void XM_CALLCONV SetEmissiveColor(FXMVECTOR value); + void XM_CALLCONV SetSpecularColor(FXMVECTOR value); + void __cdecl SetSpecularPower(float value); + void __cdecl DisableSpecular(); + void __cdecl SetAlpha(float value); + void XM_CALLCONV SetColorAndAlpha(FXMVECTOR value); + + // Light settings. + void XM_CALLCONV SetAmbientLightColor(FXMVECTOR value) override; + + void __cdecl SetLightEnabled(int whichLight, bool value) override; + void XM_CALLCONV SetLightDirection(int whichLight, FXMVECTOR value) override; + void XM_CALLCONV SetLightDiffuseColor(int whichLight, FXMVECTOR value) override; + void XM_CALLCONV SetLightSpecularColor(int whichLight, FXMVECTOR value) override; + + void __cdecl EnableDefaultLighting() override; + + // Fog settings. + void __cdecl SetFogStart(float value) override; + void __cdecl SetFogEnd(float value) override; + void XM_CALLCONV SetFogColor(FXMVECTOR value) override; + + // Texture setting - albedo, normal and specular intensity + void __cdecl SetTexture(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor, D3D12_GPU_DESCRIPTOR_HANDLE samplerDescriptor); + void __cdecl SetNormalTexture(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor); + void __cdecl SetSpecularTexture(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor); + + protected: + // Private implementation. + class Impl; + + std::unique_ptr pImpl; + + NormalMapEffect(_In_ ID3D12Device* device, uint32_t effectFlags, + const EffectPipelineStateDescription& pipelineDescription, bool skinningEnabled); + }; + + class SkinnedNormalMapEffect : public NormalMapEffect, public IEffectSkinning + { + public: + SkinnedNormalMapEffect(_In_ ID3D12Device* device, uint32_t effectFlags, + const EffectPipelineStateDescription& pipelineDescription) : + NormalMapEffect(device, effectFlags, pipelineDescription, true) + { + } + + SkinnedNormalMapEffect(SkinnedNormalMapEffect&&) = default; + SkinnedNormalMapEffect& operator= (SkinnedNormalMapEffect&&) = default; + + SkinnedNormalMapEffect(SkinnedNormalMapEffect const&) = delete; + SkinnedNormalMapEffect& operator= (SkinnedNormalMapEffect const&) = delete; + + // Animation settings. + void __cdecl SetBoneTransforms(_In_reads_(count) XMMATRIX const* value, size_t count) override; + void __cdecl ResetBoneTransforms() override; + }; + + + //------------------------------------------------------------------------------ + // Built-in shader for Physically-Based Rendering (Roughness/Metalness) with Image-based lighting + class PBREffect : public IEffect, public IEffectMatrices, public IEffectLights + { + public: + PBREffect(_In_ ID3D12Device* device, uint32_t effectFlags, + const EffectPipelineStateDescription& pipelineDescription) : + PBREffect(device, effectFlags, pipelineDescription, false) + { + } + + PBREffect(PBREffect&&) noexcept; + PBREffect& operator= (PBREffect&&) noexcept; + + PBREffect(PBREffect const&) = delete; + PBREffect& operator= (PBREffect const&) = delete; + + ~PBREffect() override; + + // IEffect methods. + void __cdecl Apply(_In_ ID3D12GraphicsCommandList* commandList) override; + + // Camera settings. + void XM_CALLCONV SetWorld(FXMMATRIX value) override; + void XM_CALLCONV SetView(FXMMATRIX value) override; + void XM_CALLCONV SetProjection(FXMMATRIX value) override; + void XM_CALLCONV SetMatrices(FXMMATRIX world, CXMMATRIX view, CXMMATRIX projection) override; + + // Light settings. + void __cdecl SetLightEnabled(int whichLight, bool value) override; + void XM_CALLCONV SetLightDirection(int whichLight, FXMVECTOR value) override; + void XM_CALLCONV SetLightDiffuseColor(int whichLight, FXMVECTOR value) override; + + void __cdecl EnableDefaultLighting() override; + + // PBR Settings. + void __cdecl SetAlpha(float value); + void XM_CALLCONV SetConstantAlbedo(FXMVECTOR value); + void __cdecl SetConstantMetallic(float value); + void __cdecl SetConstantRoughness(float value); + + // Texture settings. + void __cdecl SetAlbedoTexture(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor, D3D12_GPU_DESCRIPTOR_HANDLE samplerDescriptor); + void __cdecl SetNormalTexture(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor); + void __cdecl SetRMATexture(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor); + + void __cdecl SetEmissiveTexture(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor); + + void __cdecl SetSurfaceTextures( + D3D12_GPU_DESCRIPTOR_HANDLE albedo, + D3D12_GPU_DESCRIPTOR_HANDLE normal, + D3D12_GPU_DESCRIPTOR_HANDLE roughnessMetallicAmbientOcclusion, + D3D12_GPU_DESCRIPTOR_HANDLE sampler); + + void __cdecl SetIBLTextures( + D3D12_GPU_DESCRIPTOR_HANDLE radiance, + int numRadianceMips, + D3D12_GPU_DESCRIPTOR_HANDLE irradiance, + D3D12_GPU_DESCRIPTOR_HANDLE sampler); + + // Render target size, required for velocity buffer output. + void __cdecl SetRenderTargetSizeInPixels(int width, int height); + + protected: + // Private implementation. + class Impl; + + std::unique_ptr pImpl; + + PBREffect(_In_ ID3D12Device* device, uint32_t effectFlags, + const EffectPipelineStateDescription& pipelineDescription, bool skinningEnabled); + + // Unsupported interface methods. + void XM_CALLCONV SetAmbientLightColor(FXMVECTOR value) override; + void XM_CALLCONV SetLightSpecularColor(int whichLight, FXMVECTOR value) override; + }; + + class SkinnedPBREffect : public PBREffect, public IEffectSkinning + { + public: + SkinnedPBREffect(_In_ ID3D12Device* device, uint32_t effectFlags, + const EffectPipelineStateDescription& pipelineDescription) : + PBREffect(device, effectFlags, pipelineDescription, true) + { + } + + SkinnedPBREffect(SkinnedPBREffect&&) = default; + SkinnedPBREffect& operator= (SkinnedPBREffect&&) = default; + + SkinnedPBREffect(SkinnedPBREffect const&) = delete; + SkinnedPBREffect& operator= (SkinnedPBREffect const&) = delete; + + // Animation settings. + void __cdecl SetBoneTransforms(_In_reads_(count) XMMATRIX const* value, size_t count) override; + void __cdecl ResetBoneTransforms() override; + }; + + + //------------------------------------------------------------------------------ + // Built-in shader for debug visualization of normals, tangents, etc. + class DebugEffect : public IEffect, public IEffectMatrices + { + public: + enum Mode + { + Mode_Default = 0, // Hemispherical ambient lighting + Mode_Normals, // RGB normals + Mode_Tangents, // RGB tangents + Mode_BiTangents, // RGB bi-tangents + }; + + DebugEffect(_In_ ID3D12Device* device, uint32_t effectFlags, + const EffectPipelineStateDescription& pipelineDescription, + Mode debugMode = Mode_Default); + + DebugEffect(DebugEffect&&) noexcept; + DebugEffect& operator= (DebugEffect&&) noexcept; + + DebugEffect(DebugEffect const&) = delete; + DebugEffect& operator= (DebugEffect const&) = delete; + + ~DebugEffect() override; + + // IEffect methods. + void __cdecl Apply(_In_ ID3D12GraphicsCommandList* commandList) override; + + // Camera settings. + void XM_CALLCONV SetWorld(FXMMATRIX value) override; + void XM_CALLCONV SetView(FXMMATRIX value) override; + void XM_CALLCONV SetProjection(FXMMATRIX value) override; + void XM_CALLCONV SetMatrices(FXMMATRIX world, CXMMATRIX view, CXMMATRIX projection) override; + + // Debug Settings. + void XM_CALLCONV SetHemisphericalAmbientColor(FXMVECTOR upper, FXMVECTOR lower); + void __cdecl SetAlpha(float value); + + private: + // Private implementation. + class Impl; + + std::unique_ptr pImpl; + }; + + + //------------------------------------------------------------------------------ + // Abstract interface to factory texture resources + class IEffectTextureFactory + { + public: + virtual ~IEffectTextureFactory() = default; + + IEffectTextureFactory(const IEffectTextureFactory&) = delete; + IEffectTextureFactory& operator=(const IEffectTextureFactory&) = delete; + + virtual size_t __cdecl CreateTexture(_In_z_ const wchar_t* name, int descriptorIndex) = 0; + + protected: + IEffectTextureFactory() = default; + IEffectTextureFactory(IEffectTextureFactory&&) = default; + IEffectTextureFactory& operator=(IEffectTextureFactory&&) = default; + }; + + + // Factory for sharing texture resources + class EffectTextureFactory : public IEffectTextureFactory + { + public: + EffectTextureFactory( + _In_ ID3D12Device* device, + ResourceUploadBatch& resourceUploadBatch, + _In_ ID3D12DescriptorHeap* descriptorHeap) noexcept(false); + + EffectTextureFactory( + _In_ ID3D12Device* device, + ResourceUploadBatch& resourceUploadBatch, + _In_ size_t numDescriptors, + _In_ D3D12_DESCRIPTOR_HEAP_FLAGS descriptorHeapFlags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE) noexcept(false); + + EffectTextureFactory(EffectTextureFactory&&) noexcept; + EffectTextureFactory& operator= (EffectTextureFactory&&) noexcept; + + EffectTextureFactory(EffectTextureFactory const&) = delete; + EffectTextureFactory& operator= (EffectTextureFactory const&) = delete; + + ~EffectTextureFactory() override; + + size_t __cdecl CreateTexture(_In_z_ const wchar_t* name, int descriptorIndex) override; + + ID3D12DescriptorHeap* __cdecl Heap() const noexcept; + + // Shorthand accessors for the descriptor heap + D3D12_CPU_DESCRIPTOR_HANDLE __cdecl GetCpuDescriptorHandle(size_t index) const; + D3D12_GPU_DESCRIPTOR_HANDLE __cdecl GetGpuDescriptorHandle(size_t index) const; + + // How many textures are there in this factory? + size_t __cdecl ResourceCount() const noexcept; + + // Get a resource in a specific slot (note: increases reference count on resource) + void __cdecl GetResource(size_t slot, _Out_ ID3D12Resource** resource, _Out_opt_ bool* isCubeMap = nullptr); + + // Settings. + void __cdecl ReleaseCache(); + + void __cdecl SetSharing(bool enabled) noexcept; + + void __cdecl EnableForceSRGB(bool forceSRGB) noexcept; + void __cdecl EnableAutoGenMips(bool generateMips) noexcept; + + void __cdecl SetDirectory(_In_opt_z_ const wchar_t* path) noexcept; + + private: + // Private implementation + class Impl; + + std::unique_ptr pImpl; + }; + + + //------------------------------------------------------------------------------ + // Abstract interface to factory for sharing effects + class IEffectFactory + { + public: + virtual ~IEffectFactory() = default; + + IEffectFactory(const IEffectFactory&) = delete; + IEffectFactory& operator=(const IEffectFactory&) = delete; + + struct EffectInfo + { + std::wstring name; + bool perVertexColor; + bool enableSkinning; + bool enableDualTexture; + bool enableNormalMaps; + bool biasedVertexNormals; + float specularPower; + float alphaValue; + XMFLOAT3 ambientColor; + XMFLOAT3 diffuseColor; + XMFLOAT3 specularColor; + XMFLOAT3 emissiveColor; + int diffuseTextureIndex; + int specularTextureIndex; + int normalTextureIndex; + int emissiveTextureIndex; + int samplerIndex; + int samplerIndex2; + + EffectInfo() noexcept + : perVertexColor(false) + , enableSkinning(false) + , enableDualTexture(false) + , enableNormalMaps(false) + , biasedVertexNormals(false) + , specularPower(0) + , alphaValue(0) + , ambientColor(0, 0, 0) + , diffuseColor(0, 0, 0) + , specularColor(0, 0, 0) + , emissiveColor(0, 0, 0) + , diffuseTextureIndex(-1) + , specularTextureIndex(-1) + , normalTextureIndex(-1) + , emissiveTextureIndex(-1) + , samplerIndex(-1) + , samplerIndex2(-1) + { + } + }; + + virtual std::shared_ptr __cdecl CreateEffect( + const EffectInfo& info, + const EffectPipelineStateDescription& opaquePipelineState, + const EffectPipelineStateDescription& alphaPipelineState, + const D3D12_INPUT_LAYOUT_DESC& inputLayout, + int textureDescriptorOffset = 0, + int samplerDescriptorOffset = 0) = 0; + + protected: + IEffectFactory() = default; + IEffectFactory(IEffectFactory&&) = default; + IEffectFactory& operator=(IEffectFactory&&) = default; + }; + + + // Factory for sharing effects + class EffectFactory : public IEffectFactory + { + public: + EffectFactory(_In_ ID3D12Device* device); + EffectFactory( + _In_ ID3D12DescriptorHeap* textureDescriptors, + _In_ ID3D12DescriptorHeap* samplerDescriptors); + + EffectFactory(EffectFactory&&) noexcept; + EffectFactory& operator= (EffectFactory&&) noexcept; + + EffectFactory(EffectFactory const&) = delete; + EffectFactory& operator= (EffectFactory const&) = delete; + + ~EffectFactory() override; + + // IEffectFactory methods. + virtual std::shared_ptr __cdecl CreateEffect( + const EffectInfo& info, + const EffectPipelineStateDescription& opaquePipelineState, + const EffectPipelineStateDescription& alphaPipelineState, + const D3D12_INPUT_LAYOUT_DESC& inputLayout, + int textureDescriptorOffset = 0, + int samplerDescriptorOffset = 0) override; + + // Settings. + void __cdecl ReleaseCache(); + + void __cdecl SetSharing(bool enabled) noexcept; + + void __cdecl EnableLighting(bool enabled) noexcept; + + void __cdecl EnablePerPixelLighting(bool enabled) noexcept; + + void __cdecl EnableNormalMapEffect(bool enabled) noexcept; + + void __cdecl EnableFogging(bool enabled) noexcept; + + void __cdecl EnableInstancing(bool enabled) noexcept; + + private: + // Private implementation. + class Impl; + + std::shared_ptr pImpl; + }; + + + // Factory for Physically Based Rendering (PBR) + class PBREffectFactory : public IEffectFactory + { + public: + PBREffectFactory(_In_ ID3D12Device* device) noexcept(false); + PBREffectFactory( + _In_ ID3D12DescriptorHeap* textureDescriptors, + _In_ ID3D12DescriptorHeap* samplerDescriptors) noexcept(false); + + PBREffectFactory(PBREffectFactory&&) noexcept; + PBREffectFactory& operator= (PBREffectFactory&&) noexcept; + + PBREffectFactory(PBREffectFactory const&) = delete; + PBREffectFactory& operator= (PBREffectFactory const&) = delete; + + ~PBREffectFactory() override; + + // IEffectFactory methods. + virtual std::shared_ptr __cdecl CreateEffect( + const EffectInfo& info, + const EffectPipelineStateDescription& opaquePipelineState, + const EffectPipelineStateDescription& alphaPipelineState, + const D3D12_INPUT_LAYOUT_DESC& inputLayout, + int textureDescriptorOffset = 0, + int samplerDescriptorOffset = 0) override; + + // Settings. + void __cdecl ReleaseCache(); + + void __cdecl SetSharing(bool enabled) noexcept; + + void __cdecl EnableInstancing(bool enabled) noexcept; + + private: + // Private implementation. + class Impl; + + std::shared_ptr pImpl; + }; + } +} + +#endif // __DIRECTXTK_EFFECTS_H__ diff --git a/Common/DirectXTK12/Inc/GamePad.h b/Common/DirectXTK12/Inc/GamePad.h new file mode 100644 index 0000000..01423ed --- /dev/null +++ b/Common/DirectXTK12/Inc/GamePad.h @@ -0,0 +1,320 @@ +//-------------------------------------------------------------------------------------- +// File: GamePad.h +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248929 +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#pragma once + +#if !defined(USING_XINPUT) && !defined(USING_GAMEINPUT) && !defined(USING_WINDOWS_GAMING_INPUT) + +#ifdef _GAMING_DESKTOP +#include +#endif + +#if (defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_GAMES)) || (defined(_GAMING_DESKTOP) && (_GRDK_EDITION >= 220600)) +#define USING_GAMEINPUT +#elif (_WIN32_WINNT >= 0x0A00 /*_WIN32_WINNT_WIN10*/) && !defined(_GAMING_DESKTOP) && !defined(__MINGW32__) +#define USING_WINDOWS_GAMING_INPUT +#elif !defined(_XBOX_ONE) +#define USING_XINPUT +#endif + +#endif // !USING_XINPUT && !USING_GAMEINPUT && !USING_WINDOWS_GAMING_INPUT + +#ifdef USING_GAMEINPUT +#include +#ifndef _GAMING_XBOX +#pragma comment(lib,"gameinput.lib") +#endif + +#elif defined(USING_WINDOWS_GAMING_INPUT) +#pragma comment(lib,"runtimeobject.lib") +#include + +#elif defined(_XBOX_ONE) +// Legacy Xbox One XDK uses Windows::Xbox::Input + +#elif defined(USING_XINPUT) +#if (_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/ ) +#pragma comment(lib,"xinput.lib") +#else +#pragma comment(lib,"xinput9_1_0.lib") +#endif + +#endif + +#include +#include + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunknown-pragmas" +#endif + + +namespace DirectX +{ + class GamePad + { + public: + GamePad() noexcept(false); + + GamePad(GamePad&&) noexcept; + GamePad& operator= (GamePad&&) noexcept; + + GamePad(GamePad const&) = delete; + GamePad& operator=(GamePad const&) = delete; + + virtual ~GamePad(); + + #if defined(USING_GAMEINPUT) || defined(USING_WINDOWS_GAMING_INPUT) || defined(_XBOX_ONE) + static constexpr int MAX_PLAYER_COUNT = 8; + #else + static constexpr int MAX_PLAYER_COUNT = 4; + #endif + + static constexpr int c_MostRecent = -1; + + #ifdef USING_GAMEINPUT + static constexpr int c_MergedInput = -2; + #endif + + enum DeadZone + { + DEAD_ZONE_INDEPENDENT_AXES = 0, + DEAD_ZONE_CIRCULAR, + DEAD_ZONE_NONE, + }; + + struct Buttons + { + bool a; + bool b; + bool x; + bool y; + bool leftStick; + bool rightStick; + bool leftShoulder; + bool rightShoulder; + union + { + bool back; + bool view; + }; + union + { + bool start; + bool menu; + }; + }; + + struct DPad + { + bool up; + bool down; + bool right; + bool left; + }; + + struct ThumbSticks + { + float leftX; + float leftY; + float rightX; + float rightY; + }; + + struct Triggers + { + float left; + float right; + }; + + struct State + { + bool connected; + uint64_t packet; + Buttons buttons; + DPad dpad; + ThumbSticks thumbSticks; + Triggers triggers; + + bool __cdecl IsConnected() const noexcept { return connected; } + + // Is the button pressed currently? + bool __cdecl IsAPressed() const noexcept { return buttons.a; } + bool __cdecl IsBPressed() const noexcept { return buttons.b; } + bool __cdecl IsXPressed() const noexcept { return buttons.x; } + bool __cdecl IsYPressed() const noexcept { return buttons.y; } + + bool __cdecl IsLeftStickPressed() const noexcept { return buttons.leftStick; } + bool __cdecl IsRightStickPressed() const noexcept { return buttons.rightStick; } + + bool __cdecl IsLeftShoulderPressed() const noexcept { return buttons.leftShoulder; } + bool __cdecl IsRightShoulderPressed() const noexcept { return buttons.rightShoulder; } + + bool __cdecl IsBackPressed() const noexcept { return buttons.back; } + bool __cdecl IsViewPressed() const noexcept { return buttons.view; } + bool __cdecl IsStartPressed() const noexcept { return buttons.start; } + bool __cdecl IsMenuPressed() const noexcept { return buttons.menu; } + + bool __cdecl IsDPadDownPressed() const noexcept { return dpad.down; } + bool __cdecl IsDPadUpPressed() const noexcept { return dpad.up; } + bool __cdecl IsDPadLeftPressed() const noexcept { return dpad.left; } + bool __cdecl IsDPadRightPressed() const noexcept { return dpad.right; } + + bool __cdecl IsLeftThumbStickUp() const noexcept { return (thumbSticks.leftY > 0.5f) != 0; } + bool __cdecl IsLeftThumbStickDown() const noexcept { return (thumbSticks.leftY < -0.5f) != 0; } + bool __cdecl IsLeftThumbStickLeft() const noexcept { return (thumbSticks.leftX < -0.5f) != 0; } + bool __cdecl IsLeftThumbStickRight() const noexcept { return (thumbSticks.leftX > 0.5f) != 0; } + + bool __cdecl IsRightThumbStickUp() const noexcept { return (thumbSticks.rightY > 0.5f) != 0; } + bool __cdecl IsRightThumbStickDown() const noexcept { return (thumbSticks.rightY < -0.5f) != 0; } + bool __cdecl IsRightThumbStickLeft() const noexcept { return (thumbSticks.rightX < -0.5f) != 0; } + bool __cdecl IsRightThumbStickRight() const noexcept { return (thumbSticks.rightX > 0.5f) != 0; } + + bool __cdecl IsLeftTriggerPressed() const noexcept { return (triggers.left > 0.5f) != 0; } + bool __cdecl IsRightTriggerPressed() const noexcept { return (triggers.right > 0.5f) != 0; } + }; + + struct Capabilities + { + enum Type + { + UNKNOWN = 0, + GAMEPAD, + WHEEL, + ARCADE_STICK, + FLIGHT_STICK, + DANCE_PAD, + GUITAR, + GUITAR_ALTERNATE, + DRUM_KIT, + GUITAR_BASS = 11, + ARCADE_PAD = 19, + }; + + bool connected; + Type gamepadType; + #ifdef USING_GAMEINPUT + APP_LOCAL_DEVICE_ID id; + #elif defined(USING_WINDOWS_GAMING_INPUT) + std::wstring id; + #else + uint64_t id; + #endif + uint16_t vid; + uint16_t pid; + + Capabilities() noexcept : connected(false), gamepadType(UNKNOWN), id{}, vid(0), pid(0) {} + + bool __cdecl IsConnected() const noexcept { return connected; } + }; + + class ButtonStateTracker + { + public: + enum ButtonState + { + UP = 0, // Button is up + HELD = 1, // Button is held down + RELEASED = 2, // Button was just released + PRESSED = 3, // Buton was just pressed + }; + + ButtonState a; + ButtonState b; + ButtonState x; + ButtonState y; + + ButtonState leftStick; + ButtonState rightStick; + + ButtonState leftShoulder; + ButtonState rightShoulder; + + union + { + ButtonState back; + ButtonState view; + }; + + union + { + ButtonState start; + ButtonState menu; + }; + + ButtonState dpadUp; + ButtonState dpadDown; + ButtonState dpadLeft; + ButtonState dpadRight; + + ButtonState leftStickUp; + ButtonState leftStickDown; + ButtonState leftStickLeft; + ButtonState leftStickRight; + + ButtonState rightStickUp; + ButtonState rightStickDown; + ButtonState rightStickLeft; + ButtonState rightStickRight; + + ButtonState leftTrigger; + ButtonState rightTrigger; + + #pragma prefast(suppress: 26495, "Reset() performs the initialization") + ButtonStateTracker() noexcept { Reset(); } + + void __cdecl Update(const State& state) noexcept; + + void __cdecl Reset() noexcept; + + State __cdecl GetLastState() const noexcept { return lastState; } + + private: + State lastState; + }; + + // Retrieve the current state of the gamepad of the associated player index + State __cdecl GetState(int player, DeadZone deadZoneMode = DEAD_ZONE_INDEPENDENT_AXES); + + // Retrieve the current capabilities of the gamepad of the associated player index + Capabilities __cdecl GetCapabilities(int player); + + // Set the vibration motor speeds of the gamepad + bool __cdecl SetVibration(int player, float leftMotor, float rightMotor, float leftTrigger = 0.f, float rightTrigger = 0.f) noexcept; + + // Handle suspending/resuming + void __cdecl Suspend() noexcept; + void __cdecl Resume() noexcept; + + #ifdef USING_GAMEINPUT + void __cdecl RegisterEvents(void* ctrlChanged) noexcept; + + // Underlying device access + _Success_(return) + bool __cdecl GetDevice(int player, _Outptr_ IGameInputDevice * *device) noexcept; + #elif defined(USING_WINDOWS_GAMING_INPUT) || defined(_XBOX_ONE) + void __cdecl RegisterEvents(void* ctrlChanged, void* userChanged) noexcept; + #endif + + // Singleton + static GamePad& __cdecl Get(); + + private: + // Private implementation. + class Impl; + + std::unique_ptr pImpl; + }; +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif diff --git a/Common/DirectXTK12/Inc/GeometricPrimitive.h b/Common/DirectXTK12/Inc/GeometricPrimitive.h new file mode 100644 index 0000000..fa303ff --- /dev/null +++ b/Common/DirectXTK12/Inc/GeometricPrimitive.h @@ -0,0 +1,98 @@ +//-------------------------------------------------------------------------------------- +// File: GeometricPrimitive.h +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#pragma once + +#include "VertexTypes.h" + +#include +#include +#include +#include + + +namespace DirectX +{ + class ResourceUploadBatch; + + inline namespace DX12 + { + class IEffect; + + class GeometricPrimitive + { + public: + GeometricPrimitive(GeometricPrimitive&&) = default; + GeometricPrimitive& operator= (GeometricPrimitive&&) = default; + + GeometricPrimitive(GeometricPrimitive const&) = delete; + GeometricPrimitive& operator= (GeometricPrimitive const&) = delete; + + virtual ~GeometricPrimitive(); + + using VertexType = VertexPositionNormalTexture; + using VertexCollection = std::vector; + using IndexCollection = std::vector; + + // Factory methods. + static std::unique_ptr __cdecl CreateCube(float size = 1, bool rhcoords = true, _In_opt_ ID3D12Device* device = nullptr); + static std::unique_ptr __cdecl CreateBox(const XMFLOAT3& size, bool rhcoords = true, bool invertn = false, _In_opt_ ID3D12Device* device = nullptr); + static std::unique_ptr __cdecl CreateSphere(float diameter = 1, size_t tessellation = 16, bool rhcoords = true, bool invertn = false, _In_opt_ ID3D12Device* device = nullptr); + static std::unique_ptr __cdecl CreateGeoSphere(float diameter = 1, size_t tessellation = 3, bool rhcoords = true, _In_opt_ ID3D12Device* device = nullptr); + static std::unique_ptr __cdecl CreateCylinder(float height = 1, float diameter = 1, size_t tessellation = 32, bool rhcoords = true, _In_opt_ ID3D12Device* device = nullptr); + static std::unique_ptr __cdecl CreateCone(float diameter = 1, float height = 1, size_t tessellation = 32, bool rhcoords = true, _In_opt_ ID3D12Device* device = nullptr); + static std::unique_ptr __cdecl CreateTorus(float diameter = 1, float thickness = 0.333f, size_t tessellation = 32, bool rhcoords = true, _In_opt_ ID3D12Device* device = nullptr); + static std::unique_ptr __cdecl CreateTetrahedron(float size = 1, bool rhcoords = true, _In_opt_ ID3D12Device* device = nullptr); + static std::unique_ptr __cdecl CreateOctahedron(float size = 1, bool rhcoords = true, _In_opt_ ID3D12Device* device = nullptr); + static std::unique_ptr __cdecl CreateDodecahedron(float size = 1, bool rhcoords = true, _In_opt_ ID3D12Device* device = nullptr); + static std::unique_ptr __cdecl CreateIcosahedron(float size = 1, bool rhcoords = true, _In_opt_ ID3D12Device* device = nullptr); + static std::unique_ptr __cdecl CreateTeapot(float size = 1, size_t tessellation = 8, bool rhcoords = true, _In_opt_ ID3D12Device* device = nullptr); + static std::unique_ptr __cdecl CreateCustom(const VertexCollection& vertices, const IndexCollection& indices, _In_opt_ ID3D12Device* device = nullptr); + + static void __cdecl CreateCube(VertexCollection& vertices, IndexCollection& indices, float size = 1, bool rhcoords = true); + static void __cdecl CreateBox(VertexCollection& vertices, IndexCollection& indices, const XMFLOAT3& size, bool rhcoords = true, bool invertn = false); + static void __cdecl CreateSphere(VertexCollection& vertices, IndexCollection& indices, float diameter = 1, size_t tessellation = 16, bool rhcoords = true, bool invertn = false); + static void __cdecl CreateGeoSphere(VertexCollection& vertices, IndexCollection& indices, float diameter = 1, size_t tessellation = 3, bool rhcoords = true); + static void __cdecl CreateCylinder(VertexCollection& vertices, IndexCollection& indices, float height = 1, float diameter = 1, size_t tessellation = 32, bool rhcoords = true); + static void __cdecl CreateCone(VertexCollection& vertices, IndexCollection& indices, float diameter = 1, float height = 1, size_t tessellation = 32, bool rhcoords = true); + static void __cdecl CreateTorus(VertexCollection& vertices, IndexCollection& indices, float diameter = 1, float thickness = 0.333f, size_t tessellation = 32, bool rhcoords = true); + static void __cdecl CreateTetrahedron(VertexCollection& vertices, IndexCollection& indices, float size = 1, bool rhcoords = true); + static void __cdecl CreateOctahedron(VertexCollection& vertices, IndexCollection& indices, float size = 1, bool rhcoords = true); + static void __cdecl CreateDodecahedron(VertexCollection& vertices, IndexCollection& indices, float size = 1, bool rhcoords = true); + static void __cdecl CreateIcosahedron(VertexCollection& vertices, IndexCollection& indices, float size = 1, bool rhcoords = true); + static void __cdecl CreateTeapot(VertexCollection& vertices, IndexCollection& indices, float size = 1, size_t tessellation = 8, bool rhcoords = true); + + // Load VB/IB resources for static geometry. + void __cdecl LoadStaticBuffers( + _In_ ID3D12Device* device, + ResourceUploadBatch& resourceUploadBatch); + + // Transition VB/IB resources for static geometry. + void __cdecl Transition( + _In_ ID3D12GraphicsCommandList* commandList, + D3D12_RESOURCE_STATES stateBeforeVB, + D3D12_RESOURCE_STATES stateAfterVB, + D3D12_RESOURCE_STATES stateBeforeIB, + D3D12_RESOURCE_STATES stateAfterIB); + + // Draw the primitive. + void __cdecl Draw(_In_ ID3D12GraphicsCommandList* commandList) const; + + void __cdecl DrawInstanced(_In_ ID3D12GraphicsCommandList* commandList, uint32_t instanceCount, uint32_t startInstanceLocation = 0) const; + + private: + GeometricPrimitive() noexcept(false); + + // Private implementation. + class Impl; + + std::unique_ptr pImpl; + }; + } +} diff --git a/Common/DirectXTK12/Inc/GraphicsMemory.h b/Common/DirectXTK12/Inc/GraphicsMemory.h new file mode 100644 index 0000000..845b9bd --- /dev/null +++ b/Common/DirectXTK12/Inc/GraphicsMemory.h @@ -0,0 +1,226 @@ +//-------------------------------------------------------------------------------------- +// File: GraphicsMemory.h +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#pragma once + +#ifdef _GAMING_XBOX_SCARLETT +#include +#elif (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) +#include +#elif defined(USING_DIRECTX_HEADERS) +#include +#include +#else +#include +#endif + +#include +#include +#include + +#ifdef _GAMING_XBOX +#include +#endif + +#if defined(_GAMING_XBOX) && (defined(_DEBUG) || defined(PROFILE)) && (_GXDK_VER >= 0x585D073C) /* GDK Edition 221000 */ +#define USING_PIX_CUSTOM_MEMORY_EVENTS +#include +#endif + +namespace DirectX +{ + class LinearAllocatorPage; + + inline namespace DX12 + { + // Works a little like a smart pointer. The memory will only be fenced by the GPU once the pointer + // has been invalidated or the user explicitly marks it for fencing. + class GraphicsResource + { + public: + GraphicsResource() noexcept; + GraphicsResource( + _In_ LinearAllocatorPage* page, + _In_ D3D12_GPU_VIRTUAL_ADDRESS gpuAddress, + _In_ ID3D12Resource* resource, + _In_ void* memory, + _In_ size_t offset, + _In_ size_t size) noexcept; + + GraphicsResource(GraphicsResource&& other) noexcept; + GraphicsResource&& operator= (GraphicsResource&&) noexcept; + + GraphicsResource(const GraphicsResource&) = delete; + GraphicsResource& operator= (const GraphicsResource&) = delete; + + ~GraphicsResource(); + + D3D12_GPU_VIRTUAL_ADDRESS GpuAddress() const noexcept { return mGpuAddress; } + ID3D12Resource* Resource() const noexcept { return mResource; } + void* Memory() const noexcept { return mMemory; } + size_t ResourceOffset() const noexcept { return mBufferOffset; } + size_t Size() const noexcept { return mSize; } + + explicit operator bool() const noexcept { return mResource != nullptr; } + + // Clear the pointer. Using operator -> will produce bad results. + void __cdecl Reset() noexcept; + void __cdecl Reset(GraphicsResource&&) noexcept; + + private: + LinearAllocatorPage* mPage; + D3D12_GPU_VIRTUAL_ADDRESS mGpuAddress; + ID3D12Resource* mResource; + void* mMemory; + size_t mBufferOffset; + size_t mSize; + }; + + class SharedGraphicsResource + { + public: + SharedGraphicsResource() noexcept; + + SharedGraphicsResource(SharedGraphicsResource&&) noexcept; + SharedGraphicsResource&& operator= (SharedGraphicsResource&&) noexcept; + + SharedGraphicsResource(GraphicsResource&&); + SharedGraphicsResource&& operator= (GraphicsResource&&); + + SharedGraphicsResource(const SharedGraphicsResource&) noexcept; + SharedGraphicsResource& operator= (const SharedGraphicsResource&) noexcept; + + SharedGraphicsResource(const GraphicsResource&) = delete; + SharedGraphicsResource& operator= (const GraphicsResource&) = delete; + + ~SharedGraphicsResource(); + + D3D12_GPU_VIRTUAL_ADDRESS GpuAddress() const noexcept { return mSharedResource->GpuAddress(); } + ID3D12Resource* Resource() const noexcept { return mSharedResource->Resource(); } + void* Memory() const noexcept { return mSharedResource->Memory(); } + size_t ResourceOffset() const noexcept { return mSharedResource->ResourceOffset(); } + size_t Size() const noexcept { return mSharedResource->Size(); } + + explicit operator bool() const noexcept { return mSharedResource != nullptr; } + + bool operator == (const SharedGraphicsResource& other) const noexcept { return mSharedResource.get() == other.mSharedResource.get(); } + bool operator != (const SharedGraphicsResource& other) const noexcept { return mSharedResource.get() != other.mSharedResource.get(); } + + // Clear the pointer. Using operator -> will produce bad results. + void __cdecl Reset() noexcept; + void __cdecl Reset(GraphicsResource&&); + void __cdecl Reset(SharedGraphicsResource&&) noexcept; + void __cdecl Reset(const SharedGraphicsResource& resource) noexcept; + + private: + std::shared_ptr mSharedResource; + }; + + //------------------------------------------------------------------------------ + struct GraphicsMemoryStatistics + { + size_t committedMemory; // Bytes of memory currently committed/in-flight + size_t totalMemory; // Total bytes of memory used by the allocators + size_t totalPages; // Total page count + size_t peakCommitedMemory; // Peak commited memory value since last reset + size_t peakTotalMemory; // Peak total bytes + size_t peakTotalPages; // Peak total page count + }; + + //------------------------------------------------------------------------------ + class GraphicsMemory + { + public: + enum Tag + { + TAG_GENERIC = 0, + TAG_CONSTANT, + TAG_VERTEX, + TAG_INDEX, + TAG_SPRITES, + TAG_TEXTURE, + TAG_COMPUTE, + }; + + explicit GraphicsMemory(_In_ ID3D12Device* device); + + GraphicsMemory(GraphicsMemory&&) noexcept; + GraphicsMemory& operator= (GraphicsMemory&&) noexcept; + + GraphicsMemory(GraphicsMemory const&) = delete; + GraphicsMemory& operator=(GraphicsMemory const&) = delete; + + virtual ~GraphicsMemory(); + + // Make sure to keep the GraphicsResource handle alive as long as you need to access + // the memory on the CPU. For example, do not simply cache GpuAddress() and discard + // the GraphicsResource object, or your memory may be overwritten later. + GraphicsResource __cdecl Allocate(size_t size, size_t alignment = 16, uint32_t tag = TAG_GENERIC) + { + auto alloc = AllocateImpl(size, alignment); +#ifdef USING_PIX_CUSTOM_MEMORY_EVENTS + std::ignore = ReportCustomMemoryAlloc(alloc.Memory(), alloc.Size(), tag); +#else + UNREFERENCED_PARAMETER(tag); +#endif + return alloc; + } + + // Version of Allocate that aligns to D3D12 constant buffer requirements + template GraphicsResource AllocateConstant() + { + constexpr size_t alignment = D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT; + constexpr size_t alignedSize = (sizeof(T) + alignment - 1) & ~(alignment - 1); + auto alloc = AllocateImpl(alignedSize, alignment); +#ifdef USING_PIX_CUSTOM_MEMORY_EVENTS + // This cast is needed to capture the type information in the PDB + std::ignore = reinterpret_cast(ReportCustomMemoryAlloc(alloc.Memory(), alloc.Size(), TAG_CONSTANT)); +#endif + return alloc; + } + template GraphicsResource AllocateConstant(const T& setData) + { + GraphicsResource alloc = AllocateConstant(); + memcpy(alloc.Memory(), &setData, sizeof(T)); + return alloc; + } + + // Submits all the pending one-shot memory to the GPU. + // The memory will be recycled once the GPU is done with it. + void __cdecl Commit(_In_ ID3D12CommandQueue* commandQueue); + + // This frees up any unused memory. + // If you want to make sure all memory is reclaimed, idle the GPU before calling this. + // It is not recommended that you call this unless absolutely necessary (e.g. your + // memory budget changes at run-time, or perhaps you're changing levels in your game.) + void __cdecl GarbageCollect(); + + // Memory statistics + GraphicsMemoryStatistics __cdecl GetStatistics(); + void __cdecl ResetStatistics(); + + // Singleton + // Should only use nullptr for single GPU scenarios; mGPU requires a specific device + static GraphicsMemory& __cdecl Get(_In_opt_ ID3D12Device* device = nullptr); + + private: + // Private implementation. + class Impl; + + GraphicsResource __cdecl AllocateImpl(size_t size, size_t alignment); + +#ifdef USING_PIX_CUSTOM_MEMORY_EVENTS + // The declspec is required to ensure the proper information is captured in the PDB + __declspec(allocator) static void* __cdecl ReportCustomMemoryAlloc(void* pMem, size_t size, UINT64 metadata); +#endif + + std::unique_ptr pImpl; + }; + } +} diff --git a/Common/DirectXTK12/Inc/Keyboard.h b/Common/DirectXTK12/Inc/Keyboard.h new file mode 100644 index 0000000..e035cb6 --- /dev/null +++ b/Common/DirectXTK12/Inc/Keyboard.h @@ -0,0 +1,526 @@ +//-------------------------------------------------------------------------------------- +// File: Keyboard.h +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248929 +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#pragma once + +#if !defined(USING_XINPUT) && !defined(USING_GAMEINPUT) && !defined(USING_COREWINDOW) + +#ifdef _GAMING_DESKTOP +#include +#endif + +#if (defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_GAMES)) || (defined(_GAMING_DESKTOP) && (_GRDK_EDITION >= 220600)) +#define USING_GAMEINPUT +#elif (defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP)) || (defined(_XBOX_ONE) && defined(_TITLE)) +#define USING_COREWINDOW +#endif + +#endif // !USING_XINPUT && !USING_GAMEINPUT && !USING_WINDOWS_GAMING_INPUT + +#if defined(USING_GAMEINPUT) && !defined(_GAMING_XBOX) +#pragma comment(lib,"gameinput.lib") +#endif + +#include +#include + +#ifdef USING_COREWINDOW +namespace ABI { namespace Windows { namespace UI { namespace Core { struct ICoreWindow; } } } } +#endif + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunknown-pragmas" +#endif + + +namespace DirectX +{ + class Keyboard + { + public: + Keyboard() noexcept(false); + + Keyboard(Keyboard&&) noexcept; + Keyboard& operator= (Keyboard&&) noexcept; + + Keyboard(Keyboard const&) = delete; + Keyboard& operator=(Keyboard const&) = delete; + + virtual ~Keyboard(); + + enum Keys : unsigned char + { + None = 0, + + Back = 0x8, + Tab = 0x9, + + Enter = 0xd, + + Pause = 0x13, + CapsLock = 0x14, + Kana = 0x15, + ImeOn = 0x16, + + Kanji = 0x19, + + ImeOff = 0x1a, + Escape = 0x1b, + ImeConvert = 0x1c, + ImeNoConvert = 0x1d, + + Space = 0x20, + PageUp = 0x21, + PageDown = 0x22, + End = 0x23, + Home = 0x24, + Left = 0x25, + Up = 0x26, + Right = 0x27, + Down = 0x28, + Select = 0x29, + Print = 0x2a, + Execute = 0x2b, + PrintScreen = 0x2c, + Insert = 0x2d, + Delete = 0x2e, + Help = 0x2f, + D0 = 0x30, + D1 = 0x31, + D2 = 0x32, + D3 = 0x33, + D4 = 0x34, + D5 = 0x35, + D6 = 0x36, + D7 = 0x37, + D8 = 0x38, + D9 = 0x39, + + A = 0x41, + B = 0x42, + C = 0x43, + D = 0x44, + E = 0x45, + F = 0x46, + G = 0x47, + H = 0x48, + I = 0x49, + J = 0x4a, + K = 0x4b, + L = 0x4c, + M = 0x4d, + N = 0x4e, + O = 0x4f, + P = 0x50, + Q = 0x51, + R = 0x52, + S = 0x53, + T = 0x54, + U = 0x55, + V = 0x56, + W = 0x57, + X = 0x58, + Y = 0x59, + Z = 0x5a, + LeftWindows = 0x5b, + RightWindows = 0x5c, + Apps = 0x5d, + + Sleep = 0x5f, + NumPad0 = 0x60, + NumPad1 = 0x61, + NumPad2 = 0x62, + NumPad3 = 0x63, + NumPad4 = 0x64, + NumPad5 = 0x65, + NumPad6 = 0x66, + NumPad7 = 0x67, + NumPad8 = 0x68, + NumPad9 = 0x69, + Multiply = 0x6a, + Add = 0x6b, + Separator = 0x6c, + Subtract = 0x6d, + + Decimal = 0x6e, + Divide = 0x6f, + F1 = 0x70, + F2 = 0x71, + F3 = 0x72, + F4 = 0x73, + F5 = 0x74, + F6 = 0x75, + F7 = 0x76, + F8 = 0x77, + F9 = 0x78, + F10 = 0x79, + F11 = 0x7a, + F12 = 0x7b, + F13 = 0x7c, + F14 = 0x7d, + F15 = 0x7e, + F16 = 0x7f, + F17 = 0x80, + F18 = 0x81, + F19 = 0x82, + F20 = 0x83, + F21 = 0x84, + F22 = 0x85, + F23 = 0x86, + F24 = 0x87, + + NumLock = 0x90, + Scroll = 0x91, + + LeftShift = 0xa0, + RightShift = 0xa1, + LeftControl = 0xa2, + RightControl = 0xa3, + LeftAlt = 0xa4, + RightAlt = 0xa5, + BrowserBack = 0xa6, + BrowserForward = 0xa7, + BrowserRefresh = 0xa8, + BrowserStop = 0xa9, + BrowserSearch = 0xaa, + BrowserFavorites = 0xab, + BrowserHome = 0xac, + VolumeMute = 0xad, + VolumeDown = 0xae, + VolumeUp = 0xaf, + MediaNextTrack = 0xb0, + MediaPreviousTrack = 0xb1, + MediaStop = 0xb2, + MediaPlayPause = 0xb3, + LaunchMail = 0xb4, + SelectMedia = 0xb5, + LaunchApplication1 = 0xb6, + LaunchApplication2 = 0xb7, + + OemSemicolon = 0xba, + OemPlus = 0xbb, + OemComma = 0xbc, + OemMinus = 0xbd, + OemPeriod = 0xbe, + OemQuestion = 0xbf, + OemTilde = 0xc0, + + OemOpenBrackets = 0xdb, + OemPipe = 0xdc, + OemCloseBrackets = 0xdd, + OemQuotes = 0xde, + Oem8 = 0xdf, + + OemBackslash = 0xe2, + + ProcessKey = 0xe5, + + OemCopy = 0xf2, + OemAuto = 0xf3, + OemEnlW = 0xf4, + + Attn = 0xf6, + Crsel = 0xf7, + Exsel = 0xf8, + EraseEof = 0xf9, + Play = 0xfa, + Zoom = 0xfb, + + Pa1 = 0xfd, + OemClear = 0xfe, + }; + + struct State + { + bool Reserved0 : 8; + bool Back : 1; // VK_BACK, 0x8 + bool Tab : 1; // VK_TAB, 0x9 + bool Reserved1 : 3; + bool Enter : 1; // VK_RETURN, 0xD + bool Reserved2 : 2; + bool Reserved3 : 3; + bool Pause : 1; // VK_PAUSE, 0x13 + bool CapsLock : 1; // VK_CAPITAL, 0x14 + bool Kana : 1; // VK_KANA, 0x15 + bool ImeOn : 1; // VK_IME_ON, 0x16 + bool Reserved4 : 1; + bool Reserved5 : 1; + bool Kanji : 1; // VK_KANJI, 0x19 + bool ImeOff : 1; // VK_IME_OFF, 0X1A + bool Escape : 1; // VK_ESCAPE, 0x1B + bool ImeConvert : 1; // VK_CONVERT, 0x1C + bool ImeNoConvert : 1; // VK_NONCONVERT, 0x1D + bool Reserved7 : 2; + bool Space : 1; // VK_SPACE, 0x20 + bool PageUp : 1; // VK_PRIOR, 0x21 + bool PageDown : 1; // VK_NEXT, 0x22 + bool End : 1; // VK_END, 0x23 + bool Home : 1; // VK_HOME, 0x24 + bool Left : 1; // VK_LEFT, 0x25 + bool Up : 1; // VK_UP, 0x26 + bool Right : 1; // VK_RIGHT, 0x27 + bool Down : 1; // VK_DOWN, 0x28 + bool Select : 1; // VK_SELECT, 0x29 + bool Print : 1; // VK_PRINT, 0x2A + bool Execute : 1; // VK_EXECUTE, 0x2B + bool PrintScreen : 1; // VK_SNAPSHOT, 0x2C + bool Insert : 1; // VK_INSERT, 0x2D + bool Delete : 1; // VK_DELETE, 0x2E + bool Help : 1; // VK_HELP, 0x2F + bool D0 : 1; // 0x30 + bool D1 : 1; // 0x31 + bool D2 : 1; // 0x32 + bool D3 : 1; // 0x33 + bool D4 : 1; // 0x34 + bool D5 : 1; // 0x35 + bool D6 : 1; // 0x36 + bool D7 : 1; // 0x37 + bool D8 : 1; // 0x38 + bool D9 : 1; // 0x39 + bool Reserved8 : 6; + bool Reserved9 : 1; + bool A : 1; // 0x41 + bool B : 1; // 0x42 + bool C : 1; // 0x43 + bool D : 1; // 0x44 + bool E : 1; // 0x45 + bool F : 1; // 0x46 + bool G : 1; // 0x47 + bool H : 1; // 0x48 + bool I : 1; // 0x49 + bool J : 1; // 0x4A + bool K : 1; // 0x4B + bool L : 1; // 0x4C + bool M : 1; // 0x4D + bool N : 1; // 0x4E + bool O : 1; // 0x4F + bool P : 1; // 0x50 + bool Q : 1; // 0x51 + bool R : 1; // 0x52 + bool S : 1; // 0x53 + bool T : 1; // 0x54 + bool U : 1; // 0x55 + bool V : 1; // 0x56 + bool W : 1; // 0x57 + bool X : 1; // 0x58 + bool Y : 1; // 0x59 + bool Z : 1; // 0x5A + bool LeftWindows : 1; // VK_LWIN, 0x5B + bool RightWindows : 1; // VK_RWIN, 0x5C + bool Apps : 1; // VK_APPS, 0x5D + bool Reserved10 : 1; + bool Sleep : 1; // VK_SLEEP, 0x5F + bool NumPad0 : 1; // VK_NUMPAD0, 0x60 + bool NumPad1 : 1; // VK_NUMPAD1, 0x61 + bool NumPad2 : 1; // VK_NUMPAD2, 0x62 + bool NumPad3 : 1; // VK_NUMPAD3, 0x63 + bool NumPad4 : 1; // VK_NUMPAD4, 0x64 + bool NumPad5 : 1; // VK_NUMPAD5, 0x65 + bool NumPad6 : 1; // VK_NUMPAD6, 0x66 + bool NumPad7 : 1; // VK_NUMPAD7, 0x67 + bool NumPad8 : 1; // VK_NUMPAD8, 0x68 + bool NumPad9 : 1; // VK_NUMPAD9, 0x69 + bool Multiply : 1; // VK_MULTIPLY, 0x6A + bool Add : 1; // VK_ADD, 0x6B + bool Separator : 1; // VK_SEPARATOR, 0x6C + bool Subtract : 1; // VK_SUBTRACT, 0x6D + bool Decimal : 1; // VK_DECIMANL, 0x6E + bool Divide : 1; // VK_DIVIDE, 0x6F + bool F1 : 1; // VK_F1, 0x70 + bool F2 : 1; // VK_F2, 0x71 + bool F3 : 1; // VK_F3, 0x72 + bool F4 : 1; // VK_F4, 0x73 + bool F5 : 1; // VK_F5, 0x74 + bool F6 : 1; // VK_F6, 0x75 + bool F7 : 1; // VK_F7, 0x76 + bool F8 : 1; // VK_F8, 0x77 + bool F9 : 1; // VK_F9, 0x78 + bool F10 : 1; // VK_F10, 0x79 + bool F11 : 1; // VK_F11, 0x7A + bool F12 : 1; // VK_F12, 0x7B + bool F13 : 1; // VK_F13, 0x7C + bool F14 : 1; // VK_F14, 0x7D + bool F15 : 1; // VK_F15, 0x7E + bool F16 : 1; // VK_F16, 0x7F + bool F17 : 1; // VK_F17, 0x80 + bool F18 : 1; // VK_F18, 0x81 + bool F19 : 1; // VK_F19, 0x82 + bool F20 : 1; // VK_F20, 0x83 + bool F21 : 1; // VK_F21, 0x84 + bool F22 : 1; // VK_F22, 0x85 + bool F23 : 1; // VK_F23, 0x86 + bool F24 : 1; // VK_F24, 0x87 + bool Reserved11 : 8; + bool NumLock : 1; // VK_NUMLOCK, 0x90 + bool Scroll : 1; // VK_SCROLL, 0x91 + bool Reserved12 : 6; + bool Reserved13 : 8; + bool LeftShift : 1; // VK_LSHIFT, 0xA0 + bool RightShift : 1; // VK_RSHIFT, 0xA1 + bool LeftControl : 1; // VK_LCONTROL, 0xA2 + bool RightControl : 1; // VK_RCONTROL, 0xA3 + bool LeftAlt : 1; // VK_LMENU, 0xA4 + bool RightAlt : 1; // VK_RMENU, 0xA5 + bool BrowserBack : 1; // VK_BROWSER_BACK, 0xA6 + bool BrowserForward : 1; // VK_BROWSER_FORWARD, 0xA7 + bool BrowserRefresh : 1; // VK_BROWSER_REFRESH, 0xA8 + bool BrowserStop : 1; // VK_BROWSER_STOP, 0xA9 + bool BrowserSearch : 1; // VK_BROWSER_SEARCH, 0xAA + bool BrowserFavorites : 1; // VK_BROWSER_FAVORITES, 0xAB + bool BrowserHome : 1; // VK_BROWSER_HOME, 0xAC + bool VolumeMute : 1; // VK_VOLUME_MUTE, 0xAD + bool VolumeDown : 1; // VK_VOLUME_DOWN, 0xAE + bool VolumeUp : 1; // VK_VOLUME_UP, 0xAF + bool MediaNextTrack : 1; // VK_MEDIA_NEXT_TRACK, 0xB0 + bool MediaPreviousTrack : 1;// VK_MEDIA_PREV_TRACK, 0xB1 + bool MediaStop : 1; // VK_MEDIA_STOP, 0xB2 + bool MediaPlayPause : 1; // VK_MEDIA_PLAY_PAUSE, 0xB3 + bool LaunchMail : 1; // VK_LAUNCH_MAIL, 0xB4 + bool SelectMedia : 1; // VK_LAUNCH_MEDIA_SELECT, 0xB5 + bool LaunchApplication1 : 1;// VK_LAUNCH_APP1, 0xB6 + bool LaunchApplication2 : 1;// VK_LAUNCH_APP2, 0xB7 + bool Reserved14 : 2; + bool OemSemicolon : 1; // VK_OEM_1, 0xBA + bool OemPlus : 1; // VK_OEM_PLUS, 0xBB + bool OemComma : 1; // VK_OEM_COMMA, 0xBC + bool OemMinus : 1; // VK_OEM_MINUS, 0xBD + bool OemPeriod : 1; // VK_OEM_PERIOD, 0xBE + bool OemQuestion : 1; // VK_OEM_2, 0xBF + bool OemTilde : 1; // VK_OEM_3, 0xC0 + bool Reserved15 : 7; + bool Reserved16 : 8; + bool Reserved17 : 8; + bool Reserved18 : 3; + bool OemOpenBrackets : 1; // VK_OEM_4, 0xDB + bool OemPipe : 1; // VK_OEM_5, 0xDC + bool OemCloseBrackets : 1; // VK_OEM_6, 0xDD + bool OemQuotes : 1; // VK_OEM_7, 0xDE + bool Oem8 : 1; // VK_OEM_8, 0xDF + bool Reserved19 : 2; + bool OemBackslash : 1; // VK_OEM_102, 0xE2 + bool Reserved20 : 2; + bool ProcessKey : 1; // VK_PROCESSKEY, 0xE5 + bool Reserved21 : 2; + bool Reserved22 : 8; + bool Reserved23 : 2; + bool OemCopy : 1; // 0XF2 + bool OemAuto : 1; // 0xF3 + bool OemEnlW : 1; // 0xF4 + bool Reserved24 : 1; + bool Attn : 1; // VK_ATTN, 0xF6 + bool Crsel : 1; // VK_CRSEL, 0xF7 + bool Exsel : 1; // VK_EXSEL, 0xF8 + bool EraseEof : 1; // VK_EREOF, 0xF9 + bool Play : 1; // VK_PLAY, 0xFA + bool Zoom : 1; // VK_ZOOM, 0xFB + bool Reserved25 : 1; + bool Pa1 : 1; // VK_PA1, 0xFD + bool OemClear : 1; // VK_OEM_CLEAR, 0xFE + bool Reserved26 : 1; + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunknown-warning-option" +#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" +#endif + + bool __cdecl IsKeyDown(Keys key) const noexcept + { + if (key <= 0xfe) + { + auto ptr = reinterpret_cast(this); + const unsigned int bf = 1u << (key & 0x1f); + return (ptr[(key >> 5)] & bf) != 0; + } + return false; + } + + bool __cdecl IsKeyUp(Keys key) const noexcept + { + if (key <= 0xfe) + { + auto ptr = reinterpret_cast(this); + const unsigned int bf = 1u << (key & 0x1f); + return (ptr[(key >> 5)] & bf) == 0; + } + return false; + } + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + }; + + class KeyboardStateTracker + { + public: + State released; + State pressed; + + #pragma prefast(suppress: 26495, "Reset() performs the initialization") + KeyboardStateTracker() noexcept { Reset(); } + + void __cdecl Update(const State& state) noexcept; + + void __cdecl Reset() noexcept; + + bool __cdecl IsKeyPressed(Keys key) const noexcept { return pressed.IsKeyDown(key); } + bool __cdecl IsKeyReleased(Keys key) const noexcept { return released.IsKeyDown(key); } + + State __cdecl GetLastState() const noexcept { return lastState; } + + public: + State lastState; + }; + + // Retrieve the current state of the keyboard + State __cdecl GetState() const; + + // Reset the keyboard state + void __cdecl Reset() noexcept; + + // Feature detection + bool __cdecl IsConnected() const; + + #ifdef USING_COREWINDOW + void __cdecl SetWindow(ABI::Windows::UI::Core::ICoreWindow* window); + #ifdef __cplusplus_winrt + void __cdecl SetWindow(Windows::UI::Core::CoreWindow^ window) + { + // See https://msdn.microsoft.com/en-us/library/hh755802.aspx + SetWindow(reinterpret_cast(window)); + } + #endif + #ifdef CPPWINRT_VERSION + void __cdecl SetWindow(winrt::Windows::UI::Core::CoreWindow window) + { + // See https://docs.microsoft.com/en-us/windows/uwp/cpp-and-winrt-apis/interop-winrt-abi + SetWindow(reinterpret_cast(winrt::get_abi(window))); + } + #endif + #elif defined(WM_USER) + static void __cdecl ProcessMessage(UINT message, WPARAM wParam, LPARAM lParam); + #endif + + // Singleton + static Keyboard& __cdecl Get(); + + private: + // Private implementation. + class Impl; + + std::unique_ptr pImpl; + }; +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif diff --git a/Common/DirectXTK12/Inc/Model.h b/Common/DirectXTK12/Inc/Model.h new file mode 100644 index 0000000..0373211 --- /dev/null +++ b/Common/DirectXTK12/Inc/Model.h @@ -0,0 +1,718 @@ +//-------------------------------------------------------------------------------------- +// File: Model.h +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#pragma once + +#ifdef _GAMING_XBOX_SCARLETT +#include +#elif (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) +#include +#elif defined(USING_DIRECTX_HEADERS) +#include +#include +#include +#else +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include + +#include "GraphicsMemory.h" +#include "Effects.h" + + +namespace DirectX +{ + inline namespace DX12 + { + class IEffect; + class IEffectFactory; + class ModelMesh; + + //------------------------------------------------------------------------------ + // Model loading options + enum ModelLoaderFlags : uint32_t + { + ModelLoader_Default = 0x0, + ModelLoader_MaterialColorsSRGB = 0x1, + ModelLoader_AllowLargeModels = 0x2, + ModelLoader_IncludeBones = 0x4, + ModelLoader_DisableSkinning = 0x8, + }; + + //------------------------------------------------------------------------------ + // Frame hierarchy for rigid body and skeletal animation + struct ModelBone + { + ModelBone() noexcept : + parentIndex(c_Invalid), + childIndex(c_Invalid), + siblingIndex(c_Invalid) + { + } + + ModelBone(uint32_t parent, uint32_t child, uint32_t sibling) noexcept : + parentIndex(parent), + childIndex(child), + siblingIndex(sibling) + { + } + + uint32_t parentIndex; + uint32_t childIndex; + uint32_t siblingIndex; + std::wstring name; + + using Collection = std::vector; + + static constexpr uint32_t c_Invalid = uint32_t(-1); + + struct aligned_deleter { void operator()(void* p) noexcept { _aligned_free(p); } }; + + using TransformArray = std::unique_ptr; + + static TransformArray MakeArray(size_t count) + { + void* temp = _aligned_malloc(sizeof(XMMATRIX) * count, 16); + if (!temp) + throw std::bad_alloc(); + return TransformArray(static_cast(temp)); + } + }; + + //------------------------------------------------------------------------------ + // Each mesh part is a submesh with a single effect + class ModelMeshPart + { + public: + ModelMeshPart(uint32_t partIndex) noexcept; + + ModelMeshPart(ModelMeshPart&&) = default; + ModelMeshPart& operator= (ModelMeshPart&&) = default; + + ModelMeshPart(ModelMeshPart const&) = default; + ModelMeshPart& operator= (ModelMeshPart const&) = default; + + virtual ~ModelMeshPart(); + + using Collection = std::vector>; + using DrawCallback = std::function; + using InputLayoutCollection = std::vector; + + uint32_t partIndex; // Unique index assigned per-part in a model. + uint32_t materialIndex; // Index of the material spec to use + uint32_t indexCount; + uint32_t startIndex; + int32_t vertexOffset; + uint32_t vertexStride; + uint32_t vertexCount; + uint32_t indexBufferSize; + uint32_t vertexBufferSize; + D3D_PRIMITIVE_TOPOLOGY primitiveType; + DXGI_FORMAT indexFormat; + SharedGraphicsResource indexBuffer; + SharedGraphicsResource vertexBuffer; + Microsoft::WRL::ComPtr staticIndexBuffer; + Microsoft::WRL::ComPtr staticVertexBuffer; + std::shared_ptr vbDecl; + + // Draw mesh part + void __cdecl Draw(_In_ ID3D12GraphicsCommandList* commandList) const; + + void __cdecl DrawInstanced(_In_ ID3D12GraphicsCommandList* commandList, uint32_t instanceCount, uint32_t startInstance = 0) const; + + // + // Utilities for drawing multiple mesh parts + // + + // Draw the mesh + static void __cdecl DrawMeshParts(_In_ ID3D12GraphicsCommandList* commandList, const Collection& meshParts); + + // Draw the mesh with an effect + static void __cdecl DrawMeshParts(_In_ ID3D12GraphicsCommandList* commandList, const Collection& meshParts, _In_ IEffect* effect); + + // Draw the mesh with a callback for each mesh part + static void __cdecl DrawMeshParts(_In_ ID3D12GraphicsCommandList* commandList, const Collection& meshParts, DrawCallback callback); + + // Draw the mesh with a range of effects that mesh parts will index into. + // Effects can be any IEffect pointer type (including smart pointer). Value or reference types will not compile. + // The iterator passed to this method should have random access capabilities for best performance. + template + static void DrawMeshParts( + _In_ ID3D12GraphicsCommandList* commandList, + const Collection& meshParts, + TEffectIterator partEffects) + { + // This assert is here to prevent accidental use of containers that would cause undesirable performance penalties. + static_assert( + std::is_base_of::value, + "Providing an iterator without random access capabilities -- such as from std::list -- is not supported."); + + for (const auto& it : meshParts) + { + auto part = it.get(); + assert(part != nullptr); + + // Get the effect at the location specified by the part's material + TEffectIterator effect_iterator = partEffects; + std::advance(effect_iterator, part->partIndex); + + // Apply the effect and draw + (*effect_iterator)->Apply(commandList); + part->Draw(commandList); + } + } + + template + static void XM_CALLCONV DrawMeshParts( + _In_ ID3D12GraphicsCommandList* commandList, + const Collection& meshParts, + FXMMATRIX world, + TEffectIterator partEffects) + { + // This assert is here to prevent accidental use of containers that would cause undesirable performance penalties. + static_assert( + std::is_base_of::value, + "Providing an iterator without random access capabilities -- such as from std::list -- is not supported."); + + for (const auto& it : meshParts) + { + auto part = it.get(); + assert(part != nullptr); + + // Get the effect at the location specified by the part's material + TEffectIterator effect_iterator = partEffects; + std::advance(effect_iterator, part->partIndex); + + auto imatrices = dynamic_cast((*effect_iterator).get()); + if (imatrices) + { + imatrices->SetWorld(world); + } + + // Apply the effect and draw + (*effect_iterator)->Apply(commandList); + part->Draw(commandList); + } + } + + template + static void XM_CALLCONV DrawSkinnedMeshParts( + _In_ ID3D12GraphicsCommandList* commandList, + const ModelMesh& mesh, + const Collection& meshParts, + size_t nbones, + _In_reads_(nbones) const XMMATRIX* boneTransforms, + FXMMATRIX world, + TEffectIterator partEffects); + }; + + + //------------------------------------------------------------------------------ + // A mesh consists of one or more model mesh parts + class ModelMesh + { + public: + ModelMesh() noexcept; + + ModelMesh(ModelMesh&&) = default; + ModelMesh& operator= (ModelMesh&&) = default; + + ModelMesh(ModelMesh const&) = default; + ModelMesh& operator= (ModelMesh const&) = default; + + virtual ~ModelMesh(); + + BoundingSphere boundingSphere; + BoundingBox boundingBox; + ModelMeshPart::Collection opaqueMeshParts; + ModelMeshPart::Collection alphaMeshParts; + uint32_t boneIndex; + std::vector boneInfluences; + std::wstring name; + + using Collection = std::vector>; + + // Draw the mesh + void __cdecl DrawOpaque(_In_ ID3D12GraphicsCommandList* commandList) const; + void __cdecl DrawAlpha(_In_ ID3D12GraphicsCommandList* commandList) const; + + // Draw the mesh with an effect + void __cdecl DrawOpaque(_In_ ID3D12GraphicsCommandList* commandList, _In_ IEffect* effect) const; + void __cdecl DrawAlpha(_In_ ID3D12GraphicsCommandList* commandList, _In_ IEffect* effect) const; + + // Draw the mesh with a callback for each mesh part + void __cdecl DrawOpaque(_In_ ID3D12GraphicsCommandList* commandList, ModelMeshPart::DrawCallback callback) const; + void __cdecl DrawAlpha(_In_ ID3D12GraphicsCommandList* commandList, ModelMeshPart::DrawCallback callback) const; + + // Draw the mesh with a range of effects that mesh parts will index into. + // TEffectPtr can be any IEffect pointer type (including smart pointer). Value or reference types will not compile. + template + void DrawOpaque(_In_ ID3D12GraphicsCommandList* commandList, TEffectIterator effects) const + { + ModelMeshPart::DrawMeshParts(commandList, opaqueMeshParts, effects); + } + template + void DrawAlpha(_In_ ID3D12GraphicsCommandList* commandList, TEffectIterator effects) const + { + ModelMeshPart::DrawMeshParts(commandList, alphaMeshParts, effects); + } + + // Draw rigid-body with bones. + template + void XM_CALLCONV DrawOpaque( + _In_ ID3D12GraphicsCommandList* commandList, + size_t nbones, + _In_reads_(nbones) const XMMATRIX* boneTransforms, + FXMMATRIX world, + TEffectIterator effects) const + { + assert(nbones > 0 && boneTransforms != nullptr); + XMMATRIX local; + if (boneIndex != ModelBone::c_Invalid && boneIndex < nbones) + { + local = XMMatrixMultiply(boneTransforms[boneIndex], world); + } + else + { + local = world; + } + + ModelMeshPart::DrawMeshParts(commandList, opaqueMeshParts, local, effects); + } + + template + void XM_CALLCONV DrawAlpha( + _In_ ID3D12GraphicsCommandList* commandList, + size_t nbones, + _In_reads_(nbones) const XMMATRIX* boneTransforms, + FXMMATRIX world, + TEffectIterator effects) const + { + assert(nbones > 0 && boneTransforms != nullptr); + XMMATRIX local; + if (boneIndex != ModelBone::c_Invalid && boneIndex < nbones) + { + local = XMMatrixMultiply(boneTransforms[boneIndex], world); + } + else + { + local = world; + } + + ModelMeshPart::DrawMeshParts(commandList, alphaMeshParts, local, effects); + } + + // Draw using skinning given bone transform array. + template + void XM_CALLCONV DrawSkinnedOpaque( + _In_ ID3D12GraphicsCommandList* commandList, + size_t nbones, + _In_reads_(nbones) const XMMATRIX* boneTransforms, + FXMMATRIX world, + TEffectIterator effects) const + { + ModelMeshPart::DrawSkinnedMeshParts(commandList, *this, opaqueMeshParts, + nbones, boneTransforms, world, effects); + } + + template + void XM_CALLCONV DrawSkinnedAlpha( + _In_ ID3D12GraphicsCommandList* commandList, + size_t nbones, + _In_reads_(nbones) const XMMATRIX* boneTransforms, + FXMMATRIX world, + TEffectIterator effects) const + { + ModelMeshPart::DrawSkinnedMeshParts(commandList, *this, alphaMeshParts, + nbones, boneTransforms, world, effects); + } + }; + + + //------------------------------------------------------------------------------ + // A model consists of one or more meshes + class Model + { + public: + Model() noexcept; + + Model(Model&&) = default; + Model& operator= (Model&&) = default; + + Model(Model const& other); + Model& operator= (Model const& rhs); + + virtual ~Model(); + + using EffectCollection = std::vector>; + using ModelMaterialInfo = IEffectFactory::EffectInfo; + using ModelMaterialInfoCollection = std::vector; + using TextureCollection = std::vector; + + // The Model::Draw* functions use variadic templates and perfect-forwarding in order to support future + // overloads to the ModelMesh::Draw* family of functions. This means that a new ModelMesh overload can be + // added, removed or altered, but the Model routines will still remain compatible. The correct ModelMesh + // overload will be selected by the compiler depending on the arguments you provide to the Model method. + + // Draw all the meshes in the model. + template void DrawOpaque(_In_ ID3D12GraphicsCommandList* commandList, TForwardArgs&&... args) const + { + // Draw opaque parts + for (const auto& it : meshes) + { + auto mesh = it.get(); + assert(mesh != nullptr); + + mesh->DrawOpaque(commandList, std::forward(args)...); + } + } + + template void DrawAlpha(_In_ ID3D12GraphicsCommandList* commandList, TForwardArgs&&... args) const + { + // Draw alpha parts + for (const auto& it : meshes) + { + auto mesh = it.get(); + assert(mesh != nullptr); + + mesh->DrawAlpha(commandList, std::forward(args)...); + } + } + + template void Draw(_In_ ID3D12GraphicsCommandList* commandList, TForwardArgs&&... args) const + { + DrawOpaque(commandList, args...); + DrawAlpha(commandList, std::forward(args)...); + } + + // Draw mesh using skinning given bone transform array. + template void DrawSkinnedOpaque(_In_ ID3D12GraphicsCommandList* commandList, TForwardArgs&&... args) const + { + // Draw opaque parts + for (const auto& it : meshes) + { + auto mesh = it.get(); + assert(mesh != nullptr); + + mesh->DrawSkinnedOpaque(commandList, std::forward(args)...); + } + } + + template void DrawSkinnedAlpha(_In_ ID3D12GraphicsCommandList* commandList, TForwardArgs&&... args) const + { + // Draw alpha parts + for (const auto& it : meshes) + { + auto mesh = it.get(); + assert(mesh != nullptr); + + mesh->DrawSkinnedAlpha(commandList, std::forward(args)...); + } + } + + template void DrawSkinned(_In_ ID3D12GraphicsCommandList* commandList, TForwardArgs&&... args) const + { + DrawSkinnedOpaque(commandList, args...); + DrawSkinnedAlpha(commandList, std::forward(args)...); + } + + // Load texture resources into an existing Effect Texture Factory + int __cdecl LoadTextures(IEffectTextureFactory& texFactory, int destinationDescriptorOffset = 0) const; + + // Load texture resources into a new Effect Texture Factory + std::unique_ptr __cdecl LoadTextures( + _In_ ID3D12Device* device, + ResourceUploadBatch& resourceUploadBatch, + _In_opt_z_ const wchar_t* texturesPath = nullptr, + D3D12_DESCRIPTOR_HEAP_FLAGS flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE) const; + + // Load VB/IB resources for static geometry + void __cdecl LoadStaticBuffers( + _In_ ID3D12Device* device, + ResourceUploadBatch& resourceUploadBatch, + bool keepMemory = false); + + // Create effects using the default effect factory + EffectCollection __cdecl CreateEffects( + const EffectPipelineStateDescription& opaquePipelineState, + const EffectPipelineStateDescription& alphaPipelineState, + _In_ ID3D12DescriptorHeap* textureDescriptorHeap, + _In_ ID3D12DescriptorHeap* samplerDescriptorHeap, + int textureDescriptorOffset = 0, + int samplerDescriptorOffset = 0) const; + + // Create effects using a custom effect factory + EffectCollection __cdecl CreateEffects( + IEffectFactory& fxFactory, + const EffectPipelineStateDescription& opaquePipelineState, + const EffectPipelineStateDescription& alphaPipelineState, + int textureDescriptorOffset = 0, + int samplerDescriptorOffset = 0) const; + + // Compute bone positions based on heirarchy and transform matrices + void __cdecl CopyAbsoluteBoneTransformsTo( + size_t nbones, + _Out_writes_(nbones) XMMATRIX* boneTransforms) const; + + void __cdecl CopyAbsoluteBoneTransforms( + size_t nbones, + _In_reads_(nbones) const XMMATRIX* inBoneTransforms, + _Out_writes_(nbones) XMMATRIX* outBoneTransforms) const; + + // Set bone matrices to a set of relative tansforms + void __cdecl CopyBoneTransformsFrom( + size_t nbones, + _In_reads_(nbones) const XMMATRIX* boneTransforms); + + // Copies the relative bone matrices to a transform array + void __cdecl CopyBoneTransformsTo( + size_t nbones, + _Out_writes_(nbones) XMMATRIX* boneTransforms) const; + + // Loads a model from a Visual Studio Starter Kit .CMO file + static std::unique_ptr __cdecl CreateFromCMO( + _In_opt_ ID3D12Device* device, + _In_reads_bytes_(dataSize) const uint8_t* meshData, _In_ size_t dataSize, + ModelLoaderFlags flags = ModelLoader_Default, + _Out_opt_ size_t* animsOffset = nullptr); + static std::unique_ptr __cdecl CreateFromCMO( + _In_opt_ ID3D12Device* device, + _In_z_ const wchar_t* szFileName, + ModelLoaderFlags flags = ModelLoader_Default, + _Out_opt_ size_t* animsOffset = nullptr); + + // Loads a model from a DirectX SDK .SDKMESH file + static std::unique_ptr __cdecl CreateFromSDKMESH( + _In_opt_ ID3D12Device* device, + _In_reads_bytes_(dataSize) const uint8_t* meshData, _In_ size_t dataSize, + ModelLoaderFlags flags = ModelLoader_Default); + static std::unique_ptr __cdecl CreateFromSDKMESH( + _In_opt_ ID3D12Device* device, + _In_z_ const wchar_t* szFileName, + ModelLoaderFlags flags = ModelLoader_Default); + + // Loads a model from a .VBO file + static std::unique_ptr __cdecl CreateFromVBO( + _In_opt_ ID3D12Device* device, + _In_reads_bytes_(dataSize) const uint8_t* meshData, _In_ size_t dataSize, + ModelLoaderFlags flags = ModelLoader_Default); + static std::unique_ptr __cdecl CreateFromVBO( + _In_opt_ ID3D12Device* device, + _In_z_ const wchar_t* szFileName, + ModelLoaderFlags flags = ModelLoader_Default); + + // Utility function for getting a GPU descriptor for a mesh part/material index. If there is no texture the + // descriptor will be zero. + D3D12_GPU_DESCRIPTOR_HANDLE __cdecl GetGpuTextureHandleForMaterialIndex(uint32_t materialIndex, _In_ ID3D12DescriptorHeap* heap, _In_ size_t descriptorSize, _In_ size_t descriptorOffset) const + { + D3D12_GPU_DESCRIPTOR_HANDLE handle = {}; + + if (materialIndex >= materials.size()) + return handle; + + const int textureIndex = materials[materialIndex].diffuseTextureIndex; + if (textureIndex == -1) + return handle; + + #if defined(_MSC_VER) || !defined(_WIN32) + handle = heap->GetGPUDescriptorHandleForHeapStart(); + #else + std::ignore = heap->GetGPUDescriptorHandleForHeapStart(&handle); + #endif + handle.ptr += static_cast(descriptorSize * (UINT64(textureIndex) + UINT64(descriptorOffset))); + + return handle; + } + + // Utility function for updating the matrices in a list of effects. This will SetWorld, SetView and SetProjection + // on any effect in the list that derives from IEffectMatrices. + static void XM_CALLCONV UpdateEffectMatrices( + EffectCollection& effects, + FXMMATRIX world, + CXMMATRIX view, + CXMMATRIX proj); + + // Utility function to transition VB/IB resources for static geometry. + void __cdecl Transition( + _In_ ID3D12GraphicsCommandList* commandList, + D3D12_RESOURCE_STATES stateBeforeVB, + D3D12_RESOURCE_STATES stateAfterVB, + D3D12_RESOURCE_STATES stateBeforeIB, + D3D12_RESOURCE_STATES stateAfterIB); + + ModelMesh::Collection meshes; + ModelMaterialInfoCollection materials; + TextureCollection textureNames; + ModelBone::Collection bones; + ModelBone::TransformArray boneMatrices; + ModelBone::TransformArray invBindPoseMatrices; + std::wstring name; + +#if defined(_MSC_VER) && !defined(_NATIVE_WCHAR_T_DEFINED) + + std::unique_ptr __cdecl LoadTextures( + _In_ ID3D12Device* device, + ResourceUploadBatch& resourceUploadBatch, + _In_opt_z_ const __wchar_t* texturesPath = nullptr, + D3D12_DESCRIPTOR_HEAP_FLAGS flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE) const; + + static std::unique_ptr __cdecl CreateFromCMO( + _In_opt_ ID3D12Device* device, + _In_z_ const __wchar_t* szFileName, + ModelLoaderFlags flags = ModelLoader_Default, + _Out_opt_ size_t* animsOffset = nullptr); + + static std::unique_ptr __cdecl CreateFromSDKMESH( + _In_opt_ ID3D12Device* device, + _In_z_ const __wchar_t* szFileName, + ModelLoaderFlags flags = ModelLoader_Default); + + static std::unique_ptr __cdecl CreateFromVBO( + _In_opt_ ID3D12Device* device, + _In_z_ const __wchar_t* szFileName, + ModelLoaderFlags flags = ModelLoader_Default); + +#endif // !_NATIVE_WCHAR_T_DEFINED + + private: + std::shared_ptr __cdecl CreateEffectForMeshPart( + IEffectFactory& fxFactory, + const EffectPipelineStateDescription& opaquePipelineState, + const EffectPipelineStateDescription& alphaPipelineState, + int textureDescriptorOffset, + int samplerDescriptorOffset, + _In_ const ModelMeshPart* part) const; + + void __cdecl ComputeAbsolute(uint32_t index, + CXMMATRIX local, size_t nbones, + _In_reads_(nbones) const XMMATRIX* inBoneTransforms, + _Inout_updates_(nbones) XMMATRIX* outBoneTransforms, + size_t& visited) const; + }; + + + template + void XM_CALLCONV ModelMeshPart::DrawSkinnedMeshParts( + _In_ ID3D12GraphicsCommandList* commandList, + const ModelMesh& mesh, + const ModelMeshPart::Collection& meshParts, + size_t nbones, + _In_reads_(nbones) const XMMATRIX* boneTransforms, + FXMMATRIX world, + TEffectIterator partEffects) + { + // This assert is here to prevent accidental use of containers that would cause undesirable performance penalties. + static_assert( + std::is_base_of::value, + "Providing an iterator without random access capabilities -- such as from std::list -- is not supported."); + + assert(nbones > 0 && boneTransforms != nullptr); + + ModelBone::TransformArray temp; + + for (const auto& mit : meshParts) + { + auto part = mit.get(); + assert(part != nullptr); + + // Get the effect at the location specified by the part's material + TEffectIterator effect_iterator = partEffects; + std::advance(effect_iterator, part->partIndex); + + auto imatrices = dynamic_cast((*effect_iterator).get()); + if (imatrices) + { + imatrices->SetWorld(world); + } + + auto iskinning = dynamic_cast((*effect_iterator).get()); + if (iskinning) + { + if (mesh.boneInfluences.empty()) + { + // Direct-mapping of vertex bone indices to our master bone array + iskinning->SetBoneTransforms(boneTransforms, nbones); + } + else + { + if (!temp) + { + // Create the influence mapped bones on-demand. + temp = ModelBone::MakeArray(IEffectSkinning::MaxBones); + + size_t count = 0; + for (auto it : mesh.boneInfluences) + { + ++count; + if (count > IEffectSkinning::MaxBones) + { + throw std::runtime_error("Too many bones for skinning"); + } + + if (it >= nbones) + { + throw std::runtime_error("Invalid bone influence index"); + } + + temp[count - 1] = boneTransforms[it]; + } + + assert(count == mesh.boneInfluences.size()); + } + + iskinning->SetBoneTransforms(temp.get(), mesh.boneInfluences.size()); + } + } + else if (imatrices) + { + // Fallback for if we encounter a non-skinning effect in the model + XMMATRIX bm = (mesh.boneIndex != ModelBone::c_Invalid && mesh.boneIndex < nbones) + ? boneTransforms[mesh.boneIndex] : XMMatrixIdentity(); + + imatrices->SetWorld(XMMatrixMultiply(bm, world)); + } + + // Apply the effect and draw + (*effect_iterator)->Apply(commandList); + part->Draw(commandList); + } + } + + #ifdef __clang__ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-dynamic-exception-spec" + #endif + + DEFINE_ENUM_FLAG_OPERATORS(ModelLoaderFlags); + + #ifdef __clang__ + #pragma clang diagnostic pop + #endif + } +} diff --git a/Common/DirectXTK12/Inc/Mouse.h b/Common/DirectXTK12/Inc/Mouse.h new file mode 100644 index 0000000..69f64be --- /dev/null +++ b/Common/DirectXTK12/Inc/Mouse.h @@ -0,0 +1,166 @@ +//-------------------------------------------------------------------------------------- +// File: Mouse.h +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248929 +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#pragma once + +#if !defined(USING_XINPUT) && !defined(USING_GAMEINPUT) && !defined(USING_COREWINDOW) + +#ifdef _GAMING_DESKTOP +#include +#endif + +#if (defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_GAMES)) || (defined(_GAMING_DESKTOP) && (_GRDK_EDITION >= 220600)) +#define USING_GAMEINPUT +#elif (defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP)) || (defined(_XBOX_ONE) && defined(_TITLE)) +#define USING_COREWINDOW +#endif + +#endif // !USING_XINPUT && !USING_GAMEINPUT && !USING_WINDOWS_GAMING_INPUT + +#if defined(USING_GAMEINPUT) && !defined(_GAMING_XBOX) +#pragma comment(lib,"gameinput.lib") +#endif + +#include + +#ifdef USING_COREWINDOW +namespace ABI { namespace Windows { namespace UI { namespace Core { struct ICoreWindow; } } } } +#endif + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunknown-pragmas" +#endif + + +namespace DirectX +{ + class Mouse + { + public: + Mouse() noexcept(false); + + Mouse(Mouse&&) noexcept; + Mouse& operator= (Mouse&&) noexcept; + + Mouse(Mouse const&) = delete; + Mouse& operator=(Mouse const&) = delete; + + virtual ~Mouse(); + + enum Mode + { + MODE_ABSOLUTE = 0, + MODE_RELATIVE, + }; + + struct State + { + bool leftButton; + bool middleButton; + bool rightButton; + bool xButton1; + bool xButton2; + int x; + int y; + int scrollWheelValue; + Mode positionMode; + }; + + class ButtonStateTracker + { + public: + enum ButtonState + { + UP = 0, // Button is up + HELD = 1, // Button is held down + RELEASED = 2, // Button was just released + PRESSED = 3, // Buton was just pressed + }; + + ButtonState leftButton; + ButtonState middleButton; + ButtonState rightButton; + ButtonState xButton1; + ButtonState xButton2; + + #pragma prefast(suppress: 26495, "Reset() performs the initialization") + ButtonStateTracker() noexcept { Reset(); } + + void __cdecl Update(const State& state) noexcept; + + void __cdecl Reset() noexcept; + + State __cdecl GetLastState() const noexcept { return lastState; } + + private: + State lastState; + }; + + // Retrieve the current state of the mouse + State __cdecl GetState() const; + + // Resets the accumulated scroll wheel value + void __cdecl ResetScrollWheelValue() noexcept; + + // Sets mouse mode (defaults to absolute) + void __cdecl SetMode(Mode mode); + + // Signals the end of frame (recommended, but optional) + void __cdecl EndOfInputFrame() noexcept; + + // Feature detection + bool __cdecl IsConnected() const; + + // Cursor visibility + bool __cdecl IsVisible() const noexcept; + void __cdecl SetVisible(bool visible); + + #ifdef USING_COREWINDOW + void __cdecl SetWindow(ABI::Windows::UI::Core::ICoreWindow* window); + #ifdef __cplusplus_winrt + void __cdecl SetWindow(Windows::UI::Core::CoreWindow^ window) + { + // See https://msdn.microsoft.com/en-us/library/hh755802.aspx + SetWindow(reinterpret_cast(window)); + } + #endif + #ifdef CPPWINRT_VERSION + void __cdecl SetWindow(winrt::Windows::UI::Core::CoreWindow window) + { + // See https://docs.microsoft.com/en-us/windows/uwp/cpp-and-winrt-apis/interop-winrt-abi + SetWindow(reinterpret_cast(winrt::get_abi(window))); + } + #endif + + static void __cdecl SetDpi(float dpi); + #elif defined(WM_USER) + void __cdecl SetWindow(HWND window); + static void __cdecl ProcessMessage(UINT message, WPARAM wParam, LPARAM lParam); + + #ifdef _GAMING_XBOX + static void __cdecl SetResolution(float scale); + #endif + #endif + + // Singleton + static Mouse& __cdecl Get(); + + private: + // Private implementation. + class Impl; + + std::unique_ptr pImpl; + }; +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif diff --git a/Common/DirectXTK12/Inc/PostProcess.h b/Common/DirectXTK12/Inc/PostProcess.h new file mode 100644 index 0000000..ed71b42 --- /dev/null +++ b/Common/DirectXTK12/Inc/PostProcess.h @@ -0,0 +1,217 @@ +//-------------------------------------------------------------------------------------- +// File: PostProcess.h +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#pragma once + +#ifdef _GAMING_XBOX_SCARLETT +#include +#elif (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) +#include +#elif defined(USING_DIRECTX_HEADERS) +#include +#include +#else +#include +#endif + +#include + +#include + +#include "RenderTargetState.h" + + +namespace DirectX +{ + inline namespace DX12 + { + //------------------------------------------------------------------------------ + // Abstract interface representing a post-process pass + class IPostProcess + { + public: + virtual ~IPostProcess() = default; + + IPostProcess(const IPostProcess&) = delete; + IPostProcess& operator=(const IPostProcess&) = delete; + + virtual void __cdecl Process(_In_ ID3D12GraphicsCommandList* commandList) = 0; + + protected: + IPostProcess() = default; + IPostProcess(IPostProcess&&) = default; + IPostProcess& operator=(IPostProcess&&) = default; + }; + + + //------------------------------------------------------------------------------ + // Basic post-process + class BasicPostProcess : public IPostProcess + { + public: + enum Effect : unsigned int + { + Copy, + Monochrome, + Sepia, + DownScale_2x2, + DownScale_4x4, + GaussianBlur_5x5, + BloomExtract, + BloomBlur, + Effect_Max + }; + + BasicPostProcess(_In_ ID3D12Device* device, const RenderTargetState& rtState, Effect fx); + + BasicPostProcess(BasicPostProcess&&) noexcept; + BasicPostProcess& operator= (BasicPostProcess&&) noexcept; + + BasicPostProcess(BasicPostProcess const&) = delete; + BasicPostProcess& operator= (BasicPostProcess const&) = delete; + + ~BasicPostProcess() override; + + // IPostProcess methods. + void __cdecl Process(_In_ ID3D12GraphicsCommandList* commandList) override; + + // Properties + void __cdecl SetSourceTexture(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor, _In_opt_ ID3D12Resource* resource); + + // Sets multiplier for GaussianBlur_5x5 + void __cdecl SetGaussianParameter(float multiplier); + + // Sets parameters for BloomExtract + void __cdecl SetBloomExtractParameter(float threshold); + + // Sets parameters for BloomBlur + void __cdecl SetBloomBlurParameters(bool horizontal, float size, float brightness); + + private: + // Private implementation. + class Impl; + + std::unique_ptr pImpl; + }; + + + //------------------------------------------------------------------------------ + // Dual-texure post-process + class DualPostProcess : public IPostProcess + { + public: + enum Effect : unsigned int + { + Merge, + BloomCombine, + Effect_Max + }; + + DualPostProcess(_In_ ID3D12Device* device, const RenderTargetState& rtState, Effect fx); + + DualPostProcess(DualPostProcess&&) noexcept; + DualPostProcess& operator= (DualPostProcess&&) noexcept; + + DualPostProcess(DualPostProcess const&) = delete; + DualPostProcess& operator= (DualPostProcess const&) = delete; + + ~DualPostProcess() override; + + // IPostProcess methods. + void __cdecl Process(_In_ ID3D12GraphicsCommandList* commandList) override; + + // Properties + void __cdecl SetSourceTexture(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor); + void __cdecl SetSourceTexture2(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor); + + // Sets parameters for Merge + void __cdecl SetMergeParameters(float weight1, float weight2); + + // Sets parameters for BloomCombine + void __cdecl SetBloomCombineParameters(float bloom, float base, float bloomSaturation, float baseSaturation); + + private: + // Private implementation. + class Impl; + + std::unique_ptr pImpl; + }; + + + //------------------------------------------------------------------------------ + // Tone-map post-process + class ToneMapPostProcess : public IPostProcess + { + public: + // Tone-mapping operator + enum Operator : unsigned int + { + None, // Pass-through + Saturate, // Clamp [0,1] + Reinhard, // x/(1+x) + ACESFilmic, + Operator_Max + }; + + // Electro-Optical Transfer Function (EOTF) + enum TransferFunction : unsigned int + { + Linear, // Pass-through + SRGB, // sRGB (Rec.709 and approximate sRGB display curve) + ST2084, // HDR10 (Rec.2020 color primaries and ST.2084 display curve) + TransferFunction_Max + }; + + // Color Rotation Transform for HDR10 + enum ColorPrimaryRotation : unsigned int + { + HDTV_to_UHDTV, // Rec.709 to Rec.2020 + DCI_P3_D65_to_UHDTV, // DCI-P3-D65 (a.k.a Display P3 or P3D65) to Rec.2020 + HDTV_to_DCI_P3_D65, // Rec.709 to DCI-P3-D65 (a.k.a Display P3 or P3D65) + }; + + ToneMapPostProcess(_In_ ID3D12Device* device, const RenderTargetState& rtState, + Operator op, TransferFunction func + #if (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) + , bool mrt = false + #endif + ); + + ToneMapPostProcess(ToneMapPostProcess&&) noexcept; + ToneMapPostProcess& operator= (ToneMapPostProcess&&) noexcept; + + ToneMapPostProcess(ToneMapPostProcess const&) = delete; + ToneMapPostProcess& operator= (ToneMapPostProcess const&) = delete; + + ~ToneMapPostProcess() override; + + // IPostProcess methods. + void __cdecl Process(_In_ ID3D12GraphicsCommandList* commandList) override; + + // Properties + void __cdecl SetHDRSourceTexture(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor); + + // Sets the Color Rotation Transform for HDR10 signal output + void __cdecl SetColorRotation(ColorPrimaryRotation value); + void __cdecl SetColorRotation(CXMMATRIX value); + + // Sets exposure value for LDR tonemap operators + void __cdecl SetExposure(float exposureValue); + + // Sets ST.2084 parameter for how bright white should be in nits + void __cdecl SetST2084Parameter(float paperWhiteNits); + + private: + // Private implementation. + class Impl; + + std::unique_ptr pImpl; + }; + } +} diff --git a/Common/DirectXTK12/Inc/PrimitiveBatch.h b/Common/DirectXTK12/Inc/PrimitiveBatch.h new file mode 100644 index 0000000..2d0aaff --- /dev/null +++ b/Common/DirectXTK12/Inc/PrimitiveBatch.h @@ -0,0 +1,147 @@ +//-------------------------------------------------------------------------------------- +// File: PrimitiveBatch.h +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#pragma once + +#ifdef _GAMING_XBOX_SCARLETT +#include +#elif (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) +#include +#elif defined(USING_DIRECTX_HEADERS) +#include +#include +#else +#include +#endif + +#include +#include +#include +#include +#include + + +namespace DirectX +{ + inline namespace DX12 + { + namespace Private + { + // Base class, not to be used directly: clients should access this via the derived PrimitiveBatch. + class PrimitiveBatchBase + { + protected: + PrimitiveBatchBase(_In_ ID3D12Device* device, size_t maxIndices, size_t maxVertices, size_t vertexSize); + + PrimitiveBatchBase(PrimitiveBatchBase&&) noexcept; + PrimitiveBatchBase& operator= (PrimitiveBatchBase&&) noexcept; + + PrimitiveBatchBase(PrimitiveBatchBase const&) = delete; + PrimitiveBatchBase& operator= (PrimitiveBatchBase const&) = delete; + + virtual ~PrimitiveBatchBase(); + + public: + // Begin/End a batch of primitive drawing operations. + void __cdecl Begin(_In_ ID3D12GraphicsCommandList* cmdList); + void __cdecl End(); + + protected: + // Internal, untyped drawing method. + void __cdecl Draw(D3D_PRIMITIVE_TOPOLOGY topology, bool isIndexed, _In_opt_count_(indexCount) uint16_t const* indices, size_t indexCount, size_t vertexCount, _Outptr_ void** pMappedVertices); + + private: + // Private implementation. + class Impl; + + std::unique_ptr pImpl; + }; + } + + // Template makes the API typesafe, eg. PrimitiveBatch. + template + class PrimitiveBatch : public Private::PrimitiveBatchBase + { + static constexpr size_t DefaultBatchSize = 4096; + + public: + explicit PrimitiveBatch(_In_ ID3D12Device* device, + size_t maxIndices = DefaultBatchSize * 3, + size_t maxVertices = DefaultBatchSize) + : PrimitiveBatchBase(device, maxIndices, maxVertices, sizeof(TVertex)) + { + } + + PrimitiveBatch(PrimitiveBatch&&) = default; + PrimitiveBatch& operator= (PrimitiveBatch&&) = default; + + PrimitiveBatch(PrimitiveBatch const&) = delete; + PrimitiveBatch& operator= (PrimitiveBatch const&) = delete; + + // Similar to the D3D9 API DrawPrimitiveUP. + void Draw(D3D_PRIMITIVE_TOPOLOGY topology, _In_reads_(vertexCount) TVertex const* vertices, size_t vertexCount) + { + void* mappedVertices; + + PrimitiveBatchBase::Draw(topology, false, nullptr, 0, vertexCount, &mappedVertices); + + memcpy(mappedVertices, vertices, vertexCount * sizeof(TVertex)); + } + + + // Similar to the D3D9 API DrawIndexedPrimitiveUP. + void DrawIndexed(D3D_PRIMITIVE_TOPOLOGY topology, _In_reads_(indexCount) uint16_t const* indices, size_t indexCount, _In_reads_(vertexCount) TVertex const* vertices, size_t vertexCount) + { + void* mappedVertices; + + PrimitiveBatchBase::Draw(topology, true, indices, indexCount, vertexCount, &mappedVertices); + + memcpy(mappedVertices, vertices, vertexCount * sizeof(TVertex)); + } + + + void DrawLine(TVertex const& v1, TVertex const& v2) + { + TVertex* mappedVertices; + + PrimitiveBatchBase::Draw(D3D_PRIMITIVE_TOPOLOGY_LINELIST, false, nullptr, 0, 2, reinterpret_cast(&mappedVertices)); + + mappedVertices[0] = v1; + mappedVertices[1] = v2; + } + + + void DrawTriangle(TVertex const& v1, TVertex const& v2, TVertex const& v3) + { + TVertex* mappedVertices; + + PrimitiveBatchBase::Draw(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST, false, nullptr, 0, 3, reinterpret_cast(&mappedVertices)); + + mappedVertices[0] = v1; + mappedVertices[1] = v2; + mappedVertices[2] = v3; + } + + + void DrawQuad(TVertex const& v1, TVertex const& v2, TVertex const& v3, TVertex const& v4) + { + static const uint16_t quadIndices[] = { 0, 1, 2, 0, 2, 3 }; + + TVertex* mappedVertices; + + PrimitiveBatchBase::Draw(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST, true, quadIndices, 6, 4, reinterpret_cast(&mappedVertices)); + + mappedVertices[0] = v1; + mappedVertices[1] = v2; + mappedVertices[2] = v3; + mappedVertices[3] = v4; + } + }; + } +} diff --git a/Common/DirectXTK12/Inc/RenderTargetState.h b/Common/DirectXTK12/Inc/RenderTargetState.h new file mode 100644 index 0000000..c023ff3 --- /dev/null +++ b/Common/DirectXTK12/Inc/RenderTargetState.h @@ -0,0 +1,106 @@ +//-------------------------------------------------------------------------------------- +// File: RenderTargetState.h +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#pragma once + +#ifdef _GAMING_XBOX_SCARLETT +#include +#elif (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) +#include +#else +#ifdef USING_DIRECTX_HEADERS +#include +#include +#else +#include +#endif +#include +#endif + +#include + + +namespace DirectX +{ + // Encapsulates all render target state when creating pipeline state objects + class RenderTargetState + { + public: + RenderTargetState() noexcept + : sampleMask(~0U) + , numRenderTargets(0) + , rtvFormats{} + , dsvFormat(DXGI_FORMAT_UNKNOWN) + , sampleDesc{} + , nodeMask(0) + { + } + + RenderTargetState(const RenderTargetState&) = default; + RenderTargetState& operator=(const RenderTargetState&) = default; + + RenderTargetState(RenderTargetState&&) = default; + RenderTargetState& operator=(RenderTargetState&&) = default; + + // Single render target convenience constructor + RenderTargetState( + _In_ DXGI_FORMAT rtFormat, + _In_ DXGI_FORMAT dsFormat) noexcept + : sampleMask(UINT_MAX) + , numRenderTargets(1) + , rtvFormats{} + , dsvFormat(dsFormat) + , sampleDesc{} + , nodeMask(0) + { + sampleDesc.Count = 1; + rtvFormats[0] = rtFormat; + } + + // Convenience constructors converting from DXGI_SWAPCHAIN_DESC + #if defined(__dxgi_h__) || defined(__d3d11_x_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) + RenderTargetState( + _In_ const DXGI_SWAP_CHAIN_DESC* desc, + _In_ DXGI_FORMAT dsFormat) noexcept + : sampleMask(UINT_MAX) + , numRenderTargets(1) + , rtvFormats{} + , dsvFormat(dsFormat) + , sampleDesc{} + , nodeMask(0) + { + rtvFormats[0] = desc->BufferDesc.Format; + sampleDesc = desc->SampleDesc; + } + #endif + + #if defined(__dxgi1_2_h__) || defined(__d3d11_x_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) + RenderTargetState( + _In_ const DXGI_SWAP_CHAIN_DESC1* desc, + _In_ DXGI_FORMAT dsFormat) noexcept + : sampleMask(UINT_MAX) + , numRenderTargets(1) + , rtvFormats{} + , dsvFormat(dsFormat) + , sampleDesc{} + , nodeMask(0) + { + rtvFormats[0] = desc->Format; + sampleDesc = desc->SampleDesc; + } + #endif + + uint32_t sampleMask; + uint32_t numRenderTargets; + DXGI_FORMAT rtvFormats[D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT]; + DXGI_FORMAT dsvFormat; + DXGI_SAMPLE_DESC sampleDesc; + uint32_t nodeMask; + }; +} diff --git a/Common/DirectXTK12/Inc/ResourceUploadBatch.h b/Common/DirectXTK12/Inc/ResourceUploadBatch.h new file mode 100644 index 0000000..4cef384 --- /dev/null +++ b/Common/DirectXTK12/Inc/ResourceUploadBatch.h @@ -0,0 +1,88 @@ +//-------------------------------------------------------------------------------------- +// File: ResourceUploadBatch.h +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#pragma once + +#ifdef _GAMING_XBOX_SCARLETT +#include +#elif (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) +#include +#elif defined(USING_DIRECTX_HEADERS) +#include +#include +#include +#else +#include +#include +#endif + +#include +#include +#include + +#include "GraphicsMemory.h" + + +namespace DirectX +{ + // Has a command list of it's own so it can upload at any time. + class ResourceUploadBatch + { + public: + explicit ResourceUploadBatch(_In_ ID3D12Device* device) noexcept(false); + + ResourceUploadBatch(ResourceUploadBatch&&) noexcept; + ResourceUploadBatch& operator= (ResourceUploadBatch&&) noexcept; + + ResourceUploadBatch(ResourceUploadBatch const&) = delete; + ResourceUploadBatch& operator= (ResourceUploadBatch const&) = delete; + + virtual ~ResourceUploadBatch(); + + // Call this before your multiple calls to Upload. + void __cdecl Begin(D3D12_COMMAND_LIST_TYPE commandType = D3D12_COMMAND_LIST_TYPE_DIRECT); + + // Asynchronously uploads a resource. The memory in subRes is copied. + // The resource must be in the COPY_DEST state. + void __cdecl Upload( + _In_ ID3D12Resource* resource, + uint32_t subresourceIndexStart, + _In_reads_(numSubresources) const D3D12_SUBRESOURCE_DATA* subRes, + uint32_t numSubresources); + + void __cdecl Upload( + _In_ ID3D12Resource* resource, + const SharedGraphicsResource& buffer + ); + + // Asynchronously generate mips from a resource. + // Resource must be in the PIXEL_SHADER_RESOURCE state + void __cdecl GenerateMips(_In_ ID3D12Resource* resource); + + // Transition a resource once you're done with it + void __cdecl Transition( + _In_ ID3D12Resource* resource, + D3D12_RESOURCE_STATES stateBefore, + D3D12_RESOURCE_STATES stateAfter); + + // Submits all the uploads to the driver. + // No more uploads can happen after this call until Begin is called again. + // This returns a handle to an event that can be waited on. + std::future __cdecl End(_In_ ID3D12CommandQueue* commandQueue); + + // Validates if the given DXGI format is supported for autogen mipmaps + bool __cdecl IsSupportedForGenerateMips(DXGI_FORMAT format) noexcept; + + private: + // Private implementation. + class Impl; + + std::unique_ptr pImpl; + }; +} diff --git a/Common/DirectXTK12/Inc/ScreenGrab.h b/Common/DirectXTK12/Inc/ScreenGrab.h new file mode 100644 index 0000000..6f2403b --- /dev/null +++ b/Common/DirectXTK12/Inc/ScreenGrab.h @@ -0,0 +1,60 @@ +//-------------------------------------------------------------------------------------- +// File: ScreenGrab.h +// +// Function for capturing a 2D texture and saving it to a file (aka a 'screenshot' +// when used on a Direct3D Render Target). +// +// Note these functions are useful as a light-weight runtime screen grabber. For +// full-featured texture capture, DDS writer, and texture processing pipeline, +// see the 'Texconv' sample and the 'DirectXTex' library. +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#pragma once + +#ifdef _GAMING_XBOX_SCARLETT +#include +#elif (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) +#include +#elif defined(USING_DIRECTX_HEADERS) +#include +#include +#else +#include +#endif + +#include + +#if defined(NTDDI_WIN10_FE) || defined(__MINGW32__) +#include +#else +#include +#endif + +#pragma comment(lib,"uuid.lib") + + +namespace DirectX +{ + HRESULT __cdecl SaveDDSTextureToFile( + _In_ ID3D12CommandQueue* pCommandQueue, + _In_ ID3D12Resource* pSource, + _In_z_ const wchar_t* fileName, + D3D12_RESOURCE_STATES beforeState = D3D12_RESOURCE_STATE_RENDER_TARGET, + D3D12_RESOURCE_STATES afterState = D3D12_RESOURCE_STATE_RENDER_TARGET) noexcept; + + HRESULT __cdecl SaveWICTextureToFile( + _In_ ID3D12CommandQueue* pCommandQ, + _In_ ID3D12Resource* pSource, + REFGUID guidContainerFormat, + _In_z_ const wchar_t* fileName, + D3D12_RESOURCE_STATES beforeState = D3D12_RESOURCE_STATE_RENDER_TARGET, + D3D12_RESOURCE_STATES afterState = D3D12_RESOURCE_STATE_RENDER_TARGET, + _In_opt_ const GUID* targetFormat = nullptr, + _In_ std::function setCustomProps = nullptr, + bool forceSRGB = false); +} diff --git a/Common/DirectXTK12/Inc/SimpleMath.h b/Common/DirectXTK12/Inc/SimpleMath.h new file mode 100644 index 0000000..37381bb --- /dev/null +++ b/Common/DirectXTK12/Inc/SimpleMath.h @@ -0,0 +1,1142 @@ +//------------------------------------------------------------------------------------- +// SimpleMath.h -- Simplified C++ Math wrapper for DirectXMath +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248929 +// http://go.microsoft.com/fwlink/?LinkID=615561 +//------------------------------------------------------------------------------------- + +#pragma once + +#if (defined(_WIN32) || defined(WINAPI_FAMILY)) && !(defined(_XBOX_ONE) && defined(_TITLE)) && !defined(_GAMING_XBOX) +#include +#endif + +#include +#include +#include +#include + +#if (__cplusplus >= 202002L) +#include +#endif + +#include +#include +#include + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wfloat-equal" +#pragma clang diagnostic ignored "-Wunknown-warning-option" +#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" +#endif + + +namespace DirectX +{ + namespace SimpleMath + { + struct Vector2; + struct Vector4; + struct Matrix; + struct Quaternion; + struct Plane; + + //------------------------------------------------------------------------------ + // 2D rectangle + struct Rectangle + { + long x; + long y; + long width; + long height; + + // Creators + Rectangle() noexcept : x(0), y(0), width(0), height(0) {} + constexpr Rectangle(long ix, long iy, long iw, long ih) noexcept : x(ix), y(iy), width(iw), height(ih) {} + explicit Rectangle(const RECT& rct) noexcept : x(rct.left), y(rct.top), width(rct.right - rct.left), height(rct.bottom - rct.top) {} + + Rectangle(const Rectangle&) = default; + Rectangle& operator=(const Rectangle&) = default; + + Rectangle(Rectangle&&) = default; + Rectangle& operator=(Rectangle&&) = default; + + operator RECT() noexcept { RECT rct; rct.left = x; rct.top = y; rct.right = (x + width); rct.bottom = (y + height); return rct; } + #ifdef __cplusplus_winrt + operator Windows::Foundation::Rect() noexcept { return Windows::Foundation::Rect(float(x), float(y), float(width), float(height)); } + #endif + + // Comparison operators + #if (__cplusplus >= 202002L) + bool operator == (const Rectangle&) const = default; + auto operator <=> (const Rectangle&) const = default; + #else + bool operator == (const Rectangle& r) const noexcept { return (x == r.x) && (y == r.y) && (width == r.width) && (height == r.height); } + bool operator != (const Rectangle& r) const noexcept { return (x != r.x) || (y != r.y) || (width != r.width) || (height != r.height); } + #endif + bool operator == (const RECT& rct) const noexcept { return (x == rct.left) && (y == rct.top) && (width == (rct.right - rct.left)) && (height == (rct.bottom - rct.top)); } + bool operator != (const RECT& rct) const noexcept { return (x != rct.left) || (y != rct.top) || (width != (rct.right - rct.left)) || (height != (rct.bottom - rct.top)); } + + // Assignment operators + Rectangle& operator=(_In_ const RECT& rct) noexcept { x = rct.left; y = rct.top; width = (rct.right - rct.left); height = (rct.bottom - rct.top); return *this; } + + // Rectangle operations + Vector2 Location() const noexcept; + Vector2 Center() const noexcept; + + bool IsEmpty() const noexcept { return (width == 0 && height == 0 && x == 0 && y == 0); } + + bool Contains(long ix, long iy) const noexcept { return (x <= ix) && (ix < (x + width)) && (y <= iy) && (iy < (y + height)); } + bool Contains(const Vector2& point) const noexcept; + bool Contains(const Rectangle& r) const noexcept { return (x <= r.x) && ((r.x + r.width) <= (x + width)) && (y <= r.y) && ((r.y + r.height) <= (y + height)); } + bool Contains(const RECT& rct) const noexcept { return (x <= rct.left) && (rct.right <= (x + width)) && (y <= rct.top) && (rct.bottom <= (y + height)); } + + void Inflate(long horizAmount, long vertAmount) noexcept; + + bool Intersects(const Rectangle& r) const noexcept { return (r.x < (x + width)) && (x < (r.x + r.width)) && (r.y < (y + height)) && (y < (r.y + r.height)); } + bool Intersects(const RECT& rct) const noexcept { return (rct.left < (x + width)) && (x < rct.right) && (rct.top < (y + height)) && (y < rct.bottom); } + + void Offset(long ox, long oy) noexcept { x += ox; y += oy; } + + // Static functions + static Rectangle Intersect(const Rectangle& ra, const Rectangle& rb) noexcept; + static RECT Intersect(const RECT& rcta, const RECT& rctb) noexcept; + + static Rectangle Union(const Rectangle& ra, const Rectangle& rb) noexcept; + static RECT Union(const RECT& rcta, const RECT& rctb) noexcept; + }; + + //------------------------------------------------------------------------------ + // 2D vector + struct Vector2 : public XMFLOAT2 + { + Vector2() noexcept : XMFLOAT2(0.f, 0.f) {} + constexpr explicit Vector2(float ix) noexcept : XMFLOAT2(ix, ix) {} + constexpr Vector2(float ix, float iy) noexcept : XMFLOAT2(ix, iy) {} + explicit Vector2(_In_reads_(2) const float *pArray) noexcept : XMFLOAT2(pArray) {} + Vector2(FXMVECTOR V) noexcept { XMStoreFloat2(this, V); } + Vector2(const XMFLOAT2& V) noexcept { this->x = V.x; this->y = V.y; } + explicit Vector2(const XMVECTORF32& F) noexcept { this->x = F.f[0]; this->y = F.f[1]; } + + Vector2(const Vector2&) = default; + Vector2& operator=(const Vector2&) = default; + + Vector2(Vector2&&) = default; + Vector2& operator=(Vector2&&) = default; + + operator XMVECTOR() const noexcept { return XMLoadFloat2(this); } + + // Comparison operators + bool operator == (const Vector2& V) const noexcept; + bool operator != (const Vector2& V) const noexcept; + + // Assignment operators + Vector2& operator= (const XMVECTORF32& F) noexcept { x = F.f[0]; y = F.f[1]; return *this; } + Vector2& operator+= (const Vector2& V) noexcept; + Vector2& operator-= (const Vector2& V) noexcept; + Vector2& operator*= (const Vector2& V) noexcept; + Vector2& operator*= (float S) noexcept; + Vector2& operator/= (float S) noexcept; + + // Unary operators + Vector2 operator+ () const noexcept { return *this; } + Vector2 operator- () const noexcept { return Vector2(-x, -y); } + + // Vector operations + bool InBounds(const Vector2& Bounds) const noexcept; + + float Length() const noexcept; + float LengthSquared() const noexcept; + + float Dot(const Vector2& V) const noexcept; + void Cross(const Vector2& V, Vector2& result) const noexcept; + Vector2 Cross(const Vector2& V) const noexcept; + + void Normalize() noexcept; + void Normalize(Vector2& result) const noexcept; + + void Clamp(const Vector2& vmin, const Vector2& vmax) noexcept; + void Clamp(const Vector2& vmin, const Vector2& vmax, Vector2& result) const noexcept; + + // Static functions + static float Distance(const Vector2& v1, const Vector2& v2) noexcept; + static float DistanceSquared(const Vector2& v1, const Vector2& v2) noexcept; + + static void Min(const Vector2& v1, const Vector2& v2, Vector2& result) noexcept; + static Vector2 Min(const Vector2& v1, const Vector2& v2) noexcept; + + static void Max(const Vector2& v1, const Vector2& v2, Vector2& result) noexcept; + static Vector2 Max(const Vector2& v1, const Vector2& v2) noexcept; + + static void Lerp(const Vector2& v1, const Vector2& v2, float t, Vector2& result) noexcept; + static Vector2 Lerp(const Vector2& v1, const Vector2& v2, float t) noexcept; + + static void SmoothStep(const Vector2& v1, const Vector2& v2, float t, Vector2& result) noexcept; + static Vector2 SmoothStep(const Vector2& v1, const Vector2& v2, float t) noexcept; + + static void Barycentric(const Vector2& v1, const Vector2& v2, const Vector2& v3, float f, float g, Vector2& result) noexcept; + static Vector2 Barycentric(const Vector2& v1, const Vector2& v2, const Vector2& v3, float f, float g) noexcept; + + static void CatmullRom(const Vector2& v1, const Vector2& v2, const Vector2& v3, const Vector2& v4, float t, Vector2& result) noexcept; + static Vector2 CatmullRom(const Vector2& v1, const Vector2& v2, const Vector2& v3, const Vector2& v4, float t) noexcept; + + static void Hermite(const Vector2& v1, const Vector2& t1, const Vector2& v2, const Vector2& t2, float t, Vector2& result) noexcept; + static Vector2 Hermite(const Vector2& v1, const Vector2& t1, const Vector2& v2, const Vector2& t2, float t) noexcept; + + static void Reflect(const Vector2& ivec, const Vector2& nvec, Vector2& result) noexcept; + static Vector2 Reflect(const Vector2& ivec, const Vector2& nvec) noexcept; + + static void Refract(const Vector2& ivec, const Vector2& nvec, float refractionIndex, Vector2& result) noexcept; + static Vector2 Refract(const Vector2& ivec, const Vector2& nvec, float refractionIndex) noexcept; + + static void Transform(const Vector2& v, const Quaternion& quat, Vector2& result) noexcept; + static Vector2 Transform(const Vector2& v, const Quaternion& quat) noexcept; + + static void Transform(const Vector2& v, const Matrix& m, Vector2& result) noexcept; + static Vector2 Transform(const Vector2& v, const Matrix& m) noexcept; + static void Transform(_In_reads_(count) const Vector2* varray, size_t count, const Matrix& m, _Out_writes_(count) Vector2* resultArray) noexcept; + + static void Transform(const Vector2& v, const Matrix& m, Vector4& result) noexcept; + static void Transform(_In_reads_(count) const Vector2* varray, size_t count, const Matrix& m, _Out_writes_(count) Vector4* resultArray) noexcept; + + static void TransformNormal(const Vector2& v, const Matrix& m, Vector2& result) noexcept; + static Vector2 TransformNormal(const Vector2& v, const Matrix& m) noexcept; + static void TransformNormal(_In_reads_(count) const Vector2* varray, size_t count, const Matrix& m, _Out_writes_(count) Vector2* resultArray) noexcept; + + // Constants + static const Vector2 Zero; + static const Vector2 One; + static const Vector2 UnitX; + static const Vector2 UnitY; + }; + + // Binary operators + Vector2 operator+ (const Vector2& V1, const Vector2& V2) noexcept; + Vector2 operator- (const Vector2& V1, const Vector2& V2) noexcept; + Vector2 operator* (const Vector2& V1, const Vector2& V2) noexcept; + Vector2 operator* (const Vector2& V, float S) noexcept; + Vector2 operator/ (const Vector2& V1, const Vector2& V2) noexcept; + Vector2 operator/ (const Vector2& V, float S) noexcept; + Vector2 operator* (float S, const Vector2& V) noexcept; + + //------------------------------------------------------------------------------ + // 3D vector + struct Vector3 : public XMFLOAT3 + { + Vector3() noexcept : XMFLOAT3(0.f, 0.f, 0.f) {} + constexpr explicit Vector3(float ix) noexcept : XMFLOAT3(ix, ix, ix) {} + constexpr Vector3(float ix, float iy, float iz) noexcept : XMFLOAT3(ix, iy, iz) {} + explicit Vector3(_In_reads_(3) const float *pArray) noexcept : XMFLOAT3(pArray) {} + Vector3(FXMVECTOR V) noexcept { XMStoreFloat3(this, V); } + Vector3(const XMFLOAT3& V) noexcept { this->x = V.x; this->y = V.y; this->z = V.z; } + explicit Vector3(const XMVECTORF32& F) noexcept { this->x = F.f[0]; this->y = F.f[1]; this->z = F.f[2]; } + + Vector3(const Vector3&) = default; + Vector3& operator=(const Vector3&) = default; + + Vector3(Vector3&&) = default; + Vector3& operator=(Vector3&&) = default; + + operator XMVECTOR() const noexcept { return XMLoadFloat3(this); } + + // Comparison operators + bool operator == (const Vector3& V) const noexcept; + bool operator != (const Vector3& V) const noexcept; + + // Assignment operators + Vector3& operator= (const XMVECTORF32& F) noexcept { x = F.f[0]; y = F.f[1]; z = F.f[2]; return *this; } + Vector3& operator+= (const Vector3& V) noexcept; + Vector3& operator-= (const Vector3& V) noexcept; + Vector3& operator*= (const Vector3& V) noexcept; + Vector3& operator*= (float S) noexcept; + Vector3& operator/= (float S) noexcept; + + // Unary operators + Vector3 operator+ () const noexcept { return *this; } + Vector3 operator- () const noexcept; + + // Vector operations + bool InBounds(const Vector3& Bounds) const noexcept; + + float Length() const noexcept; + float LengthSquared() const noexcept; + + float Dot(const Vector3& V) const noexcept; + void Cross(const Vector3& V, Vector3& result) const noexcept; + Vector3 Cross(const Vector3& V) const noexcept; + + void Normalize() noexcept; + void Normalize(Vector3& result) const noexcept; + + void Clamp(const Vector3& vmin, const Vector3& vmax) noexcept; + void Clamp(const Vector3& vmin, const Vector3& vmax, Vector3& result) const noexcept; + + // Static functions + static float Distance(const Vector3& v1, const Vector3& v2) noexcept; + static float DistanceSquared(const Vector3& v1, const Vector3& v2) noexcept; + + static void Min(const Vector3& v1, const Vector3& v2, Vector3& result) noexcept; + static Vector3 Min(const Vector3& v1, const Vector3& v2) noexcept; + + static void Max(const Vector3& v1, const Vector3& v2, Vector3& result) noexcept; + static Vector3 Max(const Vector3& v1, const Vector3& v2) noexcept; + + static void Lerp(const Vector3& v1, const Vector3& v2, float t, Vector3& result) noexcept; + static Vector3 Lerp(const Vector3& v1, const Vector3& v2, float t) noexcept; + + static void SmoothStep(const Vector3& v1, const Vector3& v2, float t, Vector3& result) noexcept; + static Vector3 SmoothStep(const Vector3& v1, const Vector3& v2, float t) noexcept; + + static void Barycentric(const Vector3& v1, const Vector3& v2, const Vector3& v3, float f, float g, Vector3& result) noexcept; + static Vector3 Barycentric(const Vector3& v1, const Vector3& v2, const Vector3& v3, float f, float g) noexcept; + + static void CatmullRom(const Vector3& v1, const Vector3& v2, const Vector3& v3, const Vector3& v4, float t, Vector3& result) noexcept; + static Vector3 CatmullRom(const Vector3& v1, const Vector3& v2, const Vector3& v3, const Vector3& v4, float t) noexcept; + + static void Hermite(const Vector3& v1, const Vector3& t1, const Vector3& v2, const Vector3& t2, float t, Vector3& result) noexcept; + static Vector3 Hermite(const Vector3& v1, const Vector3& t1, const Vector3& v2, const Vector3& t2, float t) noexcept; + + static void Reflect(const Vector3& ivec, const Vector3& nvec, Vector3& result) noexcept; + static Vector3 Reflect(const Vector3& ivec, const Vector3& nvec) noexcept; + + static void Refract(const Vector3& ivec, const Vector3& nvec, float refractionIndex, Vector3& result) noexcept; + static Vector3 Refract(const Vector3& ivec, const Vector3& nvec, float refractionIndex) noexcept; + + static void Transform(const Vector3& v, const Quaternion& quat, Vector3& result) noexcept; + static Vector3 Transform(const Vector3& v, const Quaternion& quat) noexcept; + + static void Transform(const Vector3& v, const Matrix& m, Vector3& result) noexcept; + static Vector3 Transform(const Vector3& v, const Matrix& m) noexcept; + static void Transform(_In_reads_(count) const Vector3* varray, size_t count, const Matrix& m, _Out_writes_(count) Vector3* resultArray) noexcept; + + static void Transform(const Vector3& v, const Matrix& m, Vector4& result) noexcept; + static void Transform(_In_reads_(count) const Vector3* varray, size_t count, const Matrix& m, _Out_writes_(count) Vector4* resultArray) noexcept; + + static void TransformNormal(const Vector3& v, const Matrix& m, Vector3& result) noexcept; + static Vector3 TransformNormal(const Vector3& v, const Matrix& m) noexcept; + static void TransformNormal(_In_reads_(count) const Vector3* varray, size_t count, const Matrix& m, _Out_writes_(count) Vector3* resultArray) noexcept; + + // Constants + static const Vector3 Zero; + static const Vector3 One; + static const Vector3 UnitX; + static const Vector3 UnitY; + static const Vector3 UnitZ; + static const Vector3 Up; + static const Vector3 Down; + static const Vector3 Right; + static const Vector3 Left; + static const Vector3 Forward; + static const Vector3 Backward; + }; + + // Binary operators + Vector3 operator+ (const Vector3& V1, const Vector3& V2) noexcept; + Vector3 operator- (const Vector3& V1, const Vector3& V2) noexcept; + Vector3 operator* (const Vector3& V1, const Vector3& V2) noexcept; + Vector3 operator* (const Vector3& V, float S) noexcept; + Vector3 operator/ (const Vector3& V1, const Vector3& V2) noexcept; + Vector3 operator/ (const Vector3& V, float S) noexcept; + Vector3 operator* (float S, const Vector3& V) noexcept; + + //------------------------------------------------------------------------------ + // 4D vector + struct Vector4 : public XMFLOAT4 + { + Vector4() noexcept : XMFLOAT4(0.f, 0.f, 0.f, 0.f) {} + constexpr explicit Vector4(float ix) noexcept : XMFLOAT4(ix, ix, ix, ix) {} + constexpr Vector4(float ix, float iy, float iz, float iw) noexcept : XMFLOAT4(ix, iy, iz, iw) {} + explicit Vector4(_In_reads_(4) const float *pArray) noexcept : XMFLOAT4(pArray) {} + Vector4(FXMVECTOR V) noexcept { XMStoreFloat4(this, V); } + Vector4(const XMFLOAT4& V) noexcept { this->x = V.x; this->y = V.y; this->z = V.z; this->w = V.w; } + explicit Vector4(const XMVECTORF32& F) noexcept { this->x = F.f[0]; this->y = F.f[1]; this->z = F.f[2]; this->w = F.f[3]; } + + Vector4(const Vector4&) = default; + Vector4& operator=(const Vector4&) = default; + + Vector4(Vector4&&) = default; + Vector4& operator=(Vector4&&) = default; + + operator XMVECTOR() const noexcept { return XMLoadFloat4(this); } + + // Comparison operators + bool operator == (const Vector4& V) const noexcept; + bool operator != (const Vector4& V) const noexcept; + + // Assignment operators + Vector4& operator= (const XMVECTORF32& F) noexcept { x = F.f[0]; y = F.f[1]; z = F.f[2]; w = F.f[3]; return *this; } + Vector4& operator+= (const Vector4& V) noexcept; + Vector4& operator-= (const Vector4& V) noexcept; + Vector4& operator*= (const Vector4& V) noexcept; + Vector4& operator*= (float S) noexcept; + Vector4& operator/= (float S) noexcept; + + // Unary operators + Vector4 operator+ () const noexcept { return *this; } + Vector4 operator- () const noexcept; + + // Vector operations + bool InBounds(const Vector4& Bounds) const noexcept; + + float Length() const noexcept; + float LengthSquared() const noexcept; + + float Dot(const Vector4& V) const noexcept; + void Cross(const Vector4& v1, const Vector4& v2, Vector4& result) const noexcept; + Vector4 Cross(const Vector4& v1, const Vector4& v2) const noexcept; + + void Normalize() noexcept; + void Normalize(Vector4& result) const noexcept; + + void Clamp(const Vector4& vmin, const Vector4& vmax) noexcept; + void Clamp(const Vector4& vmin, const Vector4& vmax, Vector4& result) const noexcept; + + // Static functions + static float Distance(const Vector4& v1, const Vector4& v2) noexcept; + static float DistanceSquared(const Vector4& v1, const Vector4& v2) noexcept; + + static void Min(const Vector4& v1, const Vector4& v2, Vector4& result) noexcept; + static Vector4 Min(const Vector4& v1, const Vector4& v2) noexcept; + + static void Max(const Vector4& v1, const Vector4& v2, Vector4& result) noexcept; + static Vector4 Max(const Vector4& v1, const Vector4& v2) noexcept; + + static void Lerp(const Vector4& v1, const Vector4& v2, float t, Vector4& result) noexcept; + static Vector4 Lerp(const Vector4& v1, const Vector4& v2, float t) noexcept; + + static void SmoothStep(const Vector4& v1, const Vector4& v2, float t, Vector4& result) noexcept; + static Vector4 SmoothStep(const Vector4& v1, const Vector4& v2, float t) noexcept; + + static void Barycentric(const Vector4& v1, const Vector4& v2, const Vector4& v3, float f, float g, Vector4& result) noexcept; + static Vector4 Barycentric(const Vector4& v1, const Vector4& v2, const Vector4& v3, float f, float g) noexcept; + + static void CatmullRom(const Vector4& v1, const Vector4& v2, const Vector4& v3, const Vector4& v4, float t, Vector4& result) noexcept; + static Vector4 CatmullRom(const Vector4& v1, const Vector4& v2, const Vector4& v3, const Vector4& v4, float t) noexcept; + + static void Hermite(const Vector4& v1, const Vector4& t1, const Vector4& v2, const Vector4& t2, float t, Vector4& result) noexcept; + static Vector4 Hermite(const Vector4& v1, const Vector4& t1, const Vector4& v2, const Vector4& t2, float t) noexcept; + + static void Reflect(const Vector4& ivec, const Vector4& nvec, Vector4& result) noexcept; + static Vector4 Reflect(const Vector4& ivec, const Vector4& nvec) noexcept; + + static void Refract(const Vector4& ivec, const Vector4& nvec, float refractionIndex, Vector4& result) noexcept; + static Vector4 Refract(const Vector4& ivec, const Vector4& nvec, float refractionIndex) noexcept; + + static void Transform(const Vector2& v, const Quaternion& quat, Vector4& result) noexcept; + static Vector4 Transform(const Vector2& v, const Quaternion& quat) noexcept; + + static void Transform(const Vector3& v, const Quaternion& quat, Vector4& result) noexcept; + static Vector4 Transform(const Vector3& v, const Quaternion& quat) noexcept; + + static void Transform(const Vector4& v, const Quaternion& quat, Vector4& result) noexcept; + static Vector4 Transform(const Vector4& v, const Quaternion& quat) noexcept; + + static void Transform(const Vector4& v, const Matrix& m, Vector4& result) noexcept; + static Vector4 Transform(const Vector4& v, const Matrix& m) noexcept; + static void Transform(_In_reads_(count) const Vector4* varray, size_t count, const Matrix& m, _Out_writes_(count) Vector4* resultArray) noexcept; + + // Constants + static const Vector4 Zero; + static const Vector4 One; + static const Vector4 UnitX; + static const Vector4 UnitY; + static const Vector4 UnitZ; + static const Vector4 UnitW; + }; + + // Binary operators + Vector4 operator+ (const Vector4& V1, const Vector4& V2) noexcept; + Vector4 operator- (const Vector4& V1, const Vector4& V2) noexcept; + Vector4 operator* (const Vector4& V1, const Vector4& V2) noexcept; + Vector4 operator* (const Vector4& V, float S) noexcept; + Vector4 operator/ (const Vector4& V1, const Vector4& V2) noexcept; + Vector4 operator/ (const Vector4& V, float S) noexcept; + Vector4 operator* (float S, const Vector4& V) noexcept; + + //------------------------------------------------------------------------------ + // 4x4 Matrix (assumes right-handed cooordinates) + struct Matrix : public XMFLOAT4X4 + { + Matrix() noexcept + : XMFLOAT4X4(1.f, 0, 0, 0, + 0, 1.f, 0, 0, + 0, 0, 1.f, 0, + 0, 0, 0, 1.f) + { + } + constexpr Matrix(float m00, float m01, float m02, float m03, + float m10, float m11, float m12, float m13, + float m20, float m21, float m22, float m23, + float m30, float m31, float m32, float m33) noexcept + : XMFLOAT4X4(m00, m01, m02, m03, + m10, m11, m12, m13, + m20, m21, m22, m23, + m30, m31, m32, m33) + { + } + explicit Matrix(const Vector3& r0, const Vector3& r1, const Vector3& r2) noexcept + : XMFLOAT4X4(r0.x, r0.y, r0.z, 0, + r1.x, r1.y, r1.z, 0, + r2.x, r2.y, r2.z, 0, + 0, 0, 0, 1.f) + { + } + explicit Matrix(const Vector4& r0, const Vector4& r1, const Vector4& r2, const Vector4& r3) noexcept + : XMFLOAT4X4(r0.x, r0.y, r0.z, r0.w, + r1.x, r1.y, r1.z, r1.w, + r2.x, r2.y, r2.z, r2.w, + r3.x, r3.y, r3.z, r3.w) + { + } + Matrix(const XMFLOAT4X4& M) noexcept { memcpy(this, &M, sizeof(XMFLOAT4X4)); } + Matrix(const XMFLOAT3X3& M) noexcept; + Matrix(const XMFLOAT4X3& M) noexcept; + + explicit Matrix(_In_reads_(16) const float *pArray) noexcept : XMFLOAT4X4(pArray) {} + Matrix(CXMMATRIX M) noexcept { XMStoreFloat4x4(this, M); } + + Matrix(const Matrix&) = default; + Matrix& operator=(const Matrix&) = default; + + Matrix(Matrix&&) = default; + Matrix& operator=(Matrix&&) = default; + + operator XMMATRIX() const noexcept { return XMLoadFloat4x4(this); } + + // Comparison operators + bool operator == (const Matrix& M) const noexcept; + bool operator != (const Matrix& M) const noexcept; + + // Assignment operators + Matrix& operator= (const XMFLOAT3X3& M) noexcept; + Matrix& operator= (const XMFLOAT4X3& M) noexcept; + Matrix& operator+= (const Matrix& M) noexcept; + Matrix& operator-= (const Matrix& M) noexcept; + Matrix& operator*= (const Matrix& M) noexcept; + Matrix& operator*= (float S) noexcept; + Matrix& operator/= (float S) noexcept; + + Matrix& operator/= (const Matrix& M) noexcept; + // Element-wise divide + + // Unary operators + Matrix operator+ () const noexcept { return *this; } + Matrix operator- () const noexcept; + + // Properties + Vector3 Up() const noexcept { return Vector3(_21, _22, _23); } + void Up(const Vector3& v) noexcept { _21 = v.x; _22 = v.y; _23 = v.z; } + + Vector3 Down() const noexcept { return Vector3(-_21, -_22, -_23); } + void Down(const Vector3& v) noexcept { _21 = -v.x; _22 = -v.y; _23 = -v.z; } + + Vector3 Right() const noexcept { return Vector3(_11, _12, _13); } + void Right(const Vector3& v) noexcept { _11 = v.x; _12 = v.y; _13 = v.z; } + + Vector3 Left() const noexcept { return Vector3(-_11, -_12, -_13); } + void Left(const Vector3& v) noexcept { _11 = -v.x; _12 = -v.y; _13 = -v.z; } + + Vector3 Forward() const noexcept { return Vector3(-_31, -_32, -_33); } + void Forward(const Vector3& v) noexcept { _31 = -v.x; _32 = -v.y; _33 = -v.z; } + + Vector3 Backward() const noexcept { return Vector3(_31, _32, _33); } + void Backward(const Vector3& v) noexcept { _31 = v.x; _32 = v.y; _33 = v.z; } + + Vector3 Translation() const noexcept { return Vector3(_41, _42, _43); } + void Translation(const Vector3& v) noexcept { _41 = v.x; _42 = v.y; _43 = v.z; } + + // Matrix operations + bool Decompose(Vector3& scale, Quaternion& rotation, Vector3& translation) noexcept; + + Matrix Transpose() const noexcept; + void Transpose(Matrix& result) const noexcept; + + Matrix Invert() const noexcept; + void Invert(Matrix& result) const noexcept; + + float Determinant() const noexcept; + + // Computes rotation about y-axis (y), then x-axis (x), then z-axis (z) + Vector3 ToEuler() const noexcept; + + // Static functions + static Matrix CreateBillboard( + const Vector3& object, const Vector3& cameraPosition, const Vector3& cameraUp, _In_opt_ const Vector3* cameraForward = nullptr) noexcept; + + static Matrix CreateConstrainedBillboard( + const Vector3& object, const Vector3& cameraPosition, const Vector3& rotateAxis, + _In_opt_ const Vector3* cameraForward = nullptr, _In_opt_ const Vector3* objectForward = nullptr) noexcept; + + static Matrix CreateTranslation(const Vector3& position) noexcept; + static Matrix CreateTranslation(float x, float y, float z) noexcept; + + static Matrix CreateScale(const Vector3& scales) noexcept; + static Matrix CreateScale(float xs, float ys, float zs) noexcept; + static Matrix CreateScale(float scale) noexcept; + + static Matrix CreateRotationX(float radians) noexcept; + static Matrix CreateRotationY(float radians) noexcept; + static Matrix CreateRotationZ(float radians) noexcept; + + static Matrix CreateFromAxisAngle(const Vector3& axis, float angle) noexcept; + + static Matrix CreatePerspectiveFieldOfView(float fov, float aspectRatio, float nearPlane, float farPlane) noexcept; + static Matrix CreatePerspective(float width, float height, float nearPlane, float farPlane) noexcept; + static Matrix CreatePerspectiveOffCenter(float left, float right, float bottom, float top, float nearPlane, float farPlane) noexcept; + static Matrix CreateOrthographic(float width, float height, float zNearPlane, float zFarPlane) noexcept; + static Matrix CreateOrthographicOffCenter(float left, float right, float bottom, float top, float zNearPlane, float zFarPlane) noexcept; + + static Matrix CreateLookAt(const Vector3& position, const Vector3& target, const Vector3& up) noexcept; + static Matrix CreateWorld(const Vector3& position, const Vector3& forward, const Vector3& up) noexcept; + + static Matrix CreateFromQuaternion(const Quaternion& quat) noexcept; + + // Rotates about y-axis (yaw), then x-axis (pitch), then z-axis (roll) + static Matrix CreateFromYawPitchRoll(float yaw, float pitch, float roll) noexcept; + + // Rotates about y-axis (angles.y), then x-axis (angles.x), then z-axis (angles.z) + static Matrix CreateFromYawPitchRoll(const Vector3& angles) noexcept; + + static Matrix CreateShadow(const Vector3& lightDir, const Plane& plane) noexcept; + + static Matrix CreateReflection(const Plane& plane) noexcept; + + static void Lerp(const Matrix& M1, const Matrix& M2, float t, Matrix& result) noexcept; + static Matrix Lerp(const Matrix& M1, const Matrix& M2, float t) noexcept; + + static void Transform(const Matrix& M, const Quaternion& rotation, Matrix& result) noexcept; + static Matrix Transform(const Matrix& M, const Quaternion& rotation) noexcept; + + // Constants + static const Matrix Identity; + }; + + // Binary operators + Matrix operator+ (const Matrix& M1, const Matrix& M2) noexcept; + Matrix operator- (const Matrix& M1, const Matrix& M2) noexcept; + Matrix operator* (const Matrix& M1, const Matrix& M2) noexcept; + Matrix operator* (const Matrix& M, float S) noexcept; + Matrix operator/ (const Matrix& M, float S) noexcept; + Matrix operator/ (const Matrix& M1, const Matrix& M2) noexcept; + // Element-wise divide + Matrix operator* (float S, const Matrix& M) noexcept; + + + //----------------------------------------------------------------------------- + // Plane + struct Plane : public XMFLOAT4 + { + Plane() noexcept : XMFLOAT4(0.f, 1.f, 0.f, 0.f) {} + constexpr Plane(float ix, float iy, float iz, float iw) noexcept : XMFLOAT4(ix, iy, iz, iw) {} + Plane(const Vector3& normal, float d) noexcept : XMFLOAT4(normal.x, normal.y, normal.z, d) {} + Plane(const Vector3& point1, const Vector3& point2, const Vector3& point3) noexcept; + Plane(const Vector3& point, const Vector3& normal) noexcept; + explicit Plane(const Vector4& v) noexcept : XMFLOAT4(v.x, v.y, v.z, v.w) {} + explicit Plane(_In_reads_(4) const float *pArray) noexcept : XMFLOAT4(pArray) {} + Plane(FXMVECTOR V) noexcept { XMStoreFloat4(this, V); } + Plane(const XMFLOAT4& p) noexcept { this->x = p.x; this->y = p.y; this->z = p.z; this->w = p.w; } + explicit Plane(const XMVECTORF32& F) noexcept { this->x = F.f[0]; this->y = F.f[1]; this->z = F.f[2]; this->w = F.f[3]; } + + Plane(const Plane&) = default; + Plane& operator=(const Plane&) = default; + + Plane(Plane&&) = default; + Plane& operator=(Plane&&) = default; + + operator XMVECTOR() const noexcept { return XMLoadFloat4(this); } + + // Comparison operators + bool operator == (const Plane& p) const noexcept; + bool operator != (const Plane& p) const noexcept; + + // Assignment operators + Plane& operator= (const XMVECTORF32& F) noexcept { x = F.f[0]; y = F.f[1]; z = F.f[2]; w = F.f[3]; return *this; } + + // Properties + Vector3 Normal() const noexcept { return Vector3(x, y, z); } + void Normal(const Vector3& normal) noexcept { x = normal.x; y = normal.y; z = normal.z; } + + float D() const noexcept { return w; } + void D(float d) noexcept { w = d; } + + // Plane operations + void Normalize() noexcept; + void Normalize(Plane& result) const noexcept; + + float Dot(const Vector4& v) const noexcept; + float DotCoordinate(const Vector3& position) const noexcept; + float DotNormal(const Vector3& normal) const noexcept; + + // Static functions + static void Transform(const Plane& plane, const Matrix& M, Plane& result) noexcept; + static Plane Transform(const Plane& plane, const Matrix& M) noexcept; + + static void Transform(const Plane& plane, const Quaternion& rotation, Plane& result) noexcept; + static Plane Transform(const Plane& plane, const Quaternion& rotation) noexcept; + // Input quaternion must be the inverse transpose of the transformation + }; + + //------------------------------------------------------------------------------ + // Quaternion + struct Quaternion : public XMFLOAT4 + { + Quaternion() noexcept : XMFLOAT4(0, 0, 0, 1.f) {} + constexpr Quaternion(float ix, float iy, float iz, float iw) noexcept : XMFLOAT4(ix, iy, iz, iw) {} + Quaternion(const Vector3& v, float scalar) noexcept : XMFLOAT4(v.x, v.y, v.z, scalar) {} + explicit Quaternion(const Vector4& v) noexcept : XMFLOAT4(v.x, v.y, v.z, v.w) {} + explicit Quaternion(_In_reads_(4) const float *pArray) noexcept : XMFLOAT4(pArray) {} + Quaternion(FXMVECTOR V) noexcept { XMStoreFloat4(this, V); } + Quaternion(const XMFLOAT4& q) noexcept { this->x = q.x; this->y = q.y; this->z = q.z; this->w = q.w; } + explicit Quaternion(const XMVECTORF32& F) noexcept { this->x = F.f[0]; this->y = F.f[1]; this->z = F.f[2]; this->w = F.f[3]; } + + Quaternion(const Quaternion&) = default; + Quaternion& operator=(const Quaternion&) = default; + + Quaternion(Quaternion&&) = default; + Quaternion& operator=(Quaternion&&) = default; + + operator XMVECTOR() const noexcept { return XMLoadFloat4(this); } + + // Comparison operators + bool operator == (const Quaternion& q) const noexcept; + bool operator != (const Quaternion& q) const noexcept; + + // Assignment operators + Quaternion& operator= (const XMVECTORF32& F) noexcept { x = F.f[0]; y = F.f[1]; z = F.f[2]; w = F.f[3]; return *this; } + Quaternion& operator+= (const Quaternion& q) noexcept; + Quaternion& operator-= (const Quaternion& q) noexcept; + Quaternion& operator*= (const Quaternion& q) noexcept; + Quaternion& operator*= (float S) noexcept; + Quaternion& operator/= (const Quaternion& q) noexcept; + + // Unary operators + Quaternion operator+ () const noexcept { return *this; } + Quaternion operator- () const noexcept; + + // Quaternion operations + float Length() const noexcept; + float LengthSquared() const noexcept; + + void Normalize() noexcept; + void Normalize(Quaternion& result) const noexcept; + + void Conjugate() noexcept; + void Conjugate(Quaternion& result) const noexcept; + + void Inverse(Quaternion& result) const noexcept; + + float Dot(const Quaternion& Q) const noexcept; + + void RotateTowards(const Quaternion& target, float maxAngle) noexcept; + void __cdecl RotateTowards(const Quaternion& target, float maxAngle, Quaternion& result) const noexcept; + + // Computes rotation about y-axis (y), then x-axis (x), then z-axis (z) + Vector3 ToEuler() const noexcept; + + // Static functions + static Quaternion CreateFromAxisAngle(const Vector3& axis, float angle) noexcept; + + // Rotates about y-axis (yaw), then x-axis (pitch), then z-axis (roll) + static Quaternion CreateFromYawPitchRoll(float yaw, float pitch, float roll) noexcept; + + // Rotates about y-axis (angles.y), then x-axis (angles.x), then z-axis (angles.z) + static Quaternion CreateFromYawPitchRoll(const Vector3& angles) noexcept; + + static Quaternion CreateFromRotationMatrix(const Matrix& M) noexcept; + + static void Lerp(const Quaternion& q1, const Quaternion& q2, float t, Quaternion& result) noexcept; + static Quaternion Lerp(const Quaternion& q1, const Quaternion& q2, float t) noexcept; + + static void Slerp(const Quaternion& q1, const Quaternion& q2, float t, Quaternion& result) noexcept; + static Quaternion Slerp(const Quaternion& q1, const Quaternion& q2, float t) noexcept; + + static void Concatenate(const Quaternion& q1, const Quaternion& q2, Quaternion& result) noexcept; + static Quaternion Concatenate(const Quaternion& q1, const Quaternion& q2) noexcept; + + static void __cdecl FromToRotation(const Vector3& fromDir, const Vector3& toDir, Quaternion& result) noexcept; + static Quaternion FromToRotation(const Vector3& fromDir, const Vector3& toDir) noexcept; + + static void __cdecl LookRotation(const Vector3& forward, const Vector3& up, Quaternion& result) noexcept; + static Quaternion LookRotation(const Vector3& forward, const Vector3& up) noexcept; + + static float Angle(const Quaternion& q1, const Quaternion& q2) noexcept; + + // Constants + static const Quaternion Identity; + }; + + // Binary operators + Quaternion operator+ (const Quaternion& Q1, const Quaternion& Q2) noexcept; + Quaternion operator- (const Quaternion& Q1, const Quaternion& Q2) noexcept; + Quaternion operator* (const Quaternion& Q1, const Quaternion& Q2) noexcept; + Quaternion operator* (const Quaternion& Q, float S) noexcept; + Quaternion operator/ (const Quaternion& Q1, const Quaternion& Q2) noexcept; + Quaternion operator* (float S, const Quaternion& Q) noexcept; + + //------------------------------------------------------------------------------ + // Color + struct Color : public XMFLOAT4 + { + Color() noexcept : XMFLOAT4(0, 0, 0, 1.f) {} + constexpr Color(float _r, float _g, float _b) noexcept : XMFLOAT4(_r, _g, _b, 1.f) {} + constexpr Color(float _r, float _g, float _b, float _a) noexcept : XMFLOAT4(_r, _g, _b, _a) {} + explicit Color(const Vector3& clr) noexcept : XMFLOAT4(clr.x, clr.y, clr.z, 1.f) {} + explicit Color(const Vector4& clr) noexcept : XMFLOAT4(clr.x, clr.y, clr.z, clr.w) {} + explicit Color(_In_reads_(4) const float *pArray) noexcept : XMFLOAT4(pArray) {} + Color(FXMVECTOR V) noexcept { XMStoreFloat4(this, V); } + Color(const XMFLOAT4& c) noexcept { this->x = c.x; this->y = c.y; this->z = c.z; this->w = c.w; } + explicit Color(const XMVECTORF32& F) noexcept { this->x = F.f[0]; this->y = F.f[1]; this->z = F.f[2]; this->w = F.f[3]; } + + // BGRA Direct3D 9 D3DCOLOR packed color + explicit Color(const DirectX::PackedVector::XMCOLOR& Packed) noexcept; + + // RGBA XNA Game Studio packed color + explicit Color(const DirectX::PackedVector::XMUBYTEN4& Packed) noexcept; + + Color(const Color&) = default; + Color& operator=(const Color&) = default; + + Color(Color&&) = default; + Color& operator=(Color&&) = default; + + operator XMVECTOR() const noexcept { return XMLoadFloat4(this); } + operator const float*() const noexcept { return reinterpret_cast(this); } + + // Comparison operators + bool operator == (const Color& c) const noexcept; + bool operator != (const Color& c) const noexcept; + + // Assignment operators + Color& operator= (const XMVECTORF32& F) noexcept { x = F.f[0]; y = F.f[1]; z = F.f[2]; w = F.f[3]; return *this; } + Color& operator= (const DirectX::PackedVector::XMCOLOR& Packed) noexcept; + Color& operator= (const DirectX::PackedVector::XMUBYTEN4& Packed) noexcept; + Color& operator+= (const Color& c) noexcept; + Color& operator-= (const Color& c) noexcept; + Color& operator*= (const Color& c) noexcept; + Color& operator*= (float S) noexcept; + Color& operator/= (const Color& c) noexcept; + + // Unary operators + Color operator+ () const noexcept { return *this; } + Color operator- () const noexcept; + + // Properties + float R() const noexcept { return x; } + void R(float r) noexcept { x = r; } + + float G() const noexcept { return y; } + void G(float g) noexcept { y = g; } + + float B() const noexcept { return z; } + void B(float b) noexcept { z = b; } + + float A() const noexcept { return w; } + void A(float a) noexcept { w = a; } + + // Color operations + DirectX::PackedVector::XMCOLOR BGRA() const noexcept; + DirectX::PackedVector::XMUBYTEN4 RGBA() const noexcept; + + Vector3 ToVector3() const noexcept; + Vector4 ToVector4() const noexcept; + + void Negate() noexcept; + void Negate(Color& result) const noexcept; + + void Saturate() noexcept; + void Saturate(Color& result) const noexcept; + + void Premultiply() noexcept; + void Premultiply(Color& result) const noexcept; + + void AdjustSaturation(float sat) noexcept; + void AdjustSaturation(float sat, Color& result) const noexcept; + + void AdjustContrast(float contrast) noexcept; + void AdjustContrast(float contrast, Color& result) const noexcept; + + // Static functions + static void Modulate(const Color& c1, const Color& c2, Color& result) noexcept; + static Color Modulate(const Color& c1, const Color& c2) noexcept; + + static void Lerp(const Color& c1, const Color& c2, float t, Color& result) noexcept; + static Color Lerp(const Color& c1, const Color& c2, float t) noexcept; + }; + + // Binary operators + Color operator+ (const Color& C1, const Color& C2) noexcept; + Color operator- (const Color& C1, const Color& C2) noexcept; + Color operator* (const Color& C1, const Color& C2) noexcept; + Color operator* (const Color& C, float S) noexcept; + Color operator/ (const Color& C1, const Color& C2) noexcept; + Color operator* (float S, const Color& C) noexcept; + + //------------------------------------------------------------------------------ + // Ray + class Ray + { + public: + Vector3 position; + Vector3 direction; + + Ray() noexcept : position(0, 0, 0), direction(0, 0, 1) {} + Ray(const Vector3& pos, const Vector3& dir) noexcept : position(pos), direction(dir) {} + + Ray(const Ray&) = default; + Ray& operator=(const Ray&) = default; + + Ray(Ray&&) = default; + Ray& operator=(Ray&&) = default; + + // Comparison operators + bool operator == (const Ray& r) const noexcept; + bool operator != (const Ray& r) const noexcept; + + // Ray operations + bool Intersects(const BoundingSphere& sphere, _Out_ float& Dist) const noexcept; + bool Intersects(const BoundingBox& box, _Out_ float& Dist) const noexcept; + bool Intersects(const Vector3& tri0, const Vector3& tri1, const Vector3& tri2, _Out_ float& Dist) const noexcept; + bool Intersects(const Plane& plane, _Out_ float& Dist) const noexcept; + }; + + //------------------------------------------------------------------------------ + // Viewport + class Viewport + { + public: + float x; + float y; + float width; + float height; + float minDepth; + float maxDepth; + + Viewport() noexcept : + x(0.f), y(0.f), width(0.f), height(0.f), minDepth(0.f), maxDepth(1.f) + { + } + constexpr Viewport(float ix, float iy, float iw, float ih, float iminz = 0.f, float imaxz = 1.f) noexcept : + x(ix), y(iy), width(iw), height(ih), minDepth(iminz), maxDepth(imaxz) + { + } + explicit Viewport(const RECT& rct) noexcept : + x(float(rct.left)), y(float(rct.top)), + width(float(rct.right - rct.left)), + height(float(rct.bottom - rct.top)), + minDepth(0.f), maxDepth(1.f) + { + } + + #if defined(__d3d11_h__) || defined(__d3d11_x_h__) + // Direct3D 11 interop + explicit Viewport(const D3D11_VIEWPORT& vp) noexcept : + x(vp.TopLeftX), y(vp.TopLeftY), + width(vp.Width), height(vp.Height), + minDepth(vp.MinDepth), maxDepth(vp.MaxDepth) + { + } + + operator D3D11_VIEWPORT() noexcept { return *reinterpret_cast(this); } + const D3D11_VIEWPORT* Get11() const noexcept { return reinterpret_cast(this); } + Viewport& operator= (const D3D11_VIEWPORT& vp) noexcept; + #endif + + #if defined(__d3d12_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) + // Direct3D 12 interop + explicit Viewport(const D3D12_VIEWPORT& vp) noexcept : + x(vp.TopLeftX), y(vp.TopLeftY), + width(vp.Width), height(vp.Height), + minDepth(vp.MinDepth), maxDepth(vp.MaxDepth) + { + } + + operator D3D12_VIEWPORT() noexcept { return *reinterpret_cast(this); } + const D3D12_VIEWPORT* Get12() const noexcept { return reinterpret_cast(this); } + Viewport& operator= (const D3D12_VIEWPORT& vp) noexcept; + #endif + + Viewport(const Viewport&) = default; + Viewport& operator=(const Viewport&) = default; + + Viewport(Viewport&&) = default; + Viewport& operator=(Viewport&&) = default; + + // Comparison operators + #if (__cplusplus >= 202002L) + bool operator == (const Viewport&) const = default; + auto operator <=> (const Viewport&) const = default; + #else + bool operator == (const Viewport& vp) const noexcept; + bool operator != (const Viewport& vp) const noexcept; + #endif + + // Assignment operators + Viewport& operator= (const RECT& rct) noexcept; + + // Viewport operations + float AspectRatio() const noexcept; + + Vector3 Project(const Vector3& p, const Matrix& proj, const Matrix& view, const Matrix& world) const noexcept; + void Project(const Vector3& p, const Matrix& proj, const Matrix& view, const Matrix& world, Vector3& result) const noexcept; + + Vector3 Unproject(const Vector3& p, const Matrix& proj, const Matrix& view, const Matrix& world) const noexcept; + void Unproject(const Vector3& p, const Matrix& proj, const Matrix& view, const Matrix& world, Vector3& result) const noexcept; + + // Static methods + #if defined(__dxgi1_2_h__) || defined(__d3d11_x_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) + static RECT __cdecl ComputeDisplayArea(DXGI_SCALING scaling, UINT backBufferWidth, UINT backBufferHeight, int outputWidth, int outputHeight) noexcept; + #endif + static RECT __cdecl ComputeTitleSafeArea(UINT backBufferWidth, UINT backBufferHeight) noexcept; + }; + + #include "SimpleMath.inl" + + } // namespace SimpleMath + +} // namespace DirectX + +//------------------------------------------------------------------------------ +// Support for SimpleMath and Standard C++ Library containers +namespace std +{ + + template<> struct less + { + bool operator()(const DirectX::SimpleMath::Rectangle& r1, const DirectX::SimpleMath::Rectangle& r2) const noexcept + { + return ((r1.x < r2.x) + || ((r1.x == r2.x) && (r1.y < r2.y)) + || ((r1.x == r2.x) && (r1.y == r2.y) && (r1.width < r2.width)) + || ((r1.x == r2.x) && (r1.y == r2.y) && (r1.width == r2.width) && (r1.height < r2.height))); + } + }; + + template<> struct less + { + bool operator()(const DirectX::SimpleMath::Vector2& V1, const DirectX::SimpleMath::Vector2& V2) const noexcept + { + return ((V1.x < V2.x) || ((V1.x == V2.x) && (V1.y < V2.y))); + } + }; + + template<> struct less + { + bool operator()(const DirectX::SimpleMath::Vector3& V1, const DirectX::SimpleMath::Vector3& V2) const noexcept + { + return ((V1.x < V2.x) + || ((V1.x == V2.x) && (V1.y < V2.y)) + || ((V1.x == V2.x) && (V1.y == V2.y) && (V1.z < V2.z))); + } + }; + + template<> struct less + { + bool operator()(const DirectX::SimpleMath::Vector4& V1, const DirectX::SimpleMath::Vector4& V2) const noexcept + { + return ((V1.x < V2.x) + || ((V1.x == V2.x) && (V1.y < V2.y)) + || ((V1.x == V2.x) && (V1.y == V2.y) && (V1.z < V2.z)) + || ((V1.x == V2.x) && (V1.y == V2.y) && (V1.z == V2.z) && (V1.w < V2.w))); + } + }; + + template<> struct less + { + bool operator()(const DirectX::SimpleMath::Matrix& M1, const DirectX::SimpleMath::Matrix& M2) const noexcept + { + if (M1._11 != M2._11) return M1._11 < M2._11; + if (M1._12 != M2._12) return M1._12 < M2._12; + if (M1._13 != M2._13) return M1._13 < M2._13; + if (M1._14 != M2._14) return M1._14 < M2._14; + if (M1._21 != M2._21) return M1._21 < M2._21; + if (M1._22 != M2._22) return M1._22 < M2._22; + if (M1._23 != M2._23) return M1._23 < M2._23; + if (M1._24 != M2._24) return M1._24 < M2._24; + if (M1._31 != M2._31) return M1._31 < M2._31; + if (M1._32 != M2._32) return M1._32 < M2._32; + if (M1._33 != M2._33) return M1._33 < M2._33; + if (M1._34 != M2._34) return M1._34 < M2._34; + if (M1._41 != M2._41) return M1._41 < M2._41; + if (M1._42 != M2._42) return M1._42 < M2._42; + if (M1._43 != M2._43) return M1._43 < M2._43; + if (M1._44 != M2._44) return M1._44 < M2._44; + + return false; + } + }; + + template<> struct less + { + bool operator()(const DirectX::SimpleMath::Plane& P1, const DirectX::SimpleMath::Plane& P2) const noexcept + { + return ((P1.x < P2.x) + || ((P1.x == P2.x) && (P1.y < P2.y)) + || ((P1.x == P2.x) && (P1.y == P2.y) && (P1.z < P2.z)) + || ((P1.x == P2.x) && (P1.y == P2.y) && (P1.z == P2.z) && (P1.w < P2.w))); + } + }; + + template<> struct less + { + bool operator()(const DirectX::SimpleMath::Quaternion& Q1, const DirectX::SimpleMath::Quaternion& Q2) const noexcept + { + return ((Q1.x < Q2.x) + || ((Q1.x == Q2.x) && (Q1.y < Q2.y)) + || ((Q1.x == Q2.x) && (Q1.y == Q2.y) && (Q1.z < Q2.z)) + || ((Q1.x == Q2.x) && (Q1.y == Q2.y) && (Q1.z == Q2.z) && (Q1.w < Q2.w))); + } + }; + + template<> struct less + { + bool operator()(const DirectX::SimpleMath::Color& C1, const DirectX::SimpleMath::Color& C2) const noexcept + { + return ((C1.x < C2.x) + || ((C1.x == C2.x) && (C1.y < C2.y)) + || ((C1.x == C2.x) && (C1.y == C2.y) && (C1.z < C2.z)) + || ((C1.x == C2.x) && (C1.y == C2.y) && (C1.z == C2.z) && (C1.w < C2.w))); + } + }; + + template<> struct less + { + bool operator()(const DirectX::SimpleMath::Ray& R1, const DirectX::SimpleMath::Ray& R2) const noexcept + { + if (R1.position.x != R2.position.x) return R1.position.x < R2.position.x; + if (R1.position.y != R2.position.y) return R1.position.y < R2.position.y; + if (R1.position.z != R2.position.z) return R1.position.z < R2.position.z; + + if (R1.direction.x != R2.direction.x) return R1.direction.x < R2.direction.x; + if (R1.direction.y != R2.direction.y) return R1.direction.y < R2.direction.y; + if (R1.direction.z != R2.direction.z) return R1.direction.z < R2.direction.z; + + return false; + } + }; + + template<> struct less + { + bool operator()(const DirectX::SimpleMath::Viewport& vp1, const DirectX::SimpleMath::Viewport& vp2) const noexcept + { + if (vp1.x != vp2.x) return (vp1.x < vp2.x); + if (vp1.y != vp2.y) return (vp1.y < vp2.y); + + if (vp1.width != vp2.width) return (vp1.width < vp2.width); + if (vp1.height != vp2.height) return (vp1.height < vp2.height); + + if (vp1.minDepth != vp2.minDepth) return (vp1.minDepth < vp2.minDepth); + if (vp1.maxDepth != vp2.maxDepth) return (vp1.maxDepth < vp2.maxDepth); + + return false; + } + }; + +} // namespace std + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif diff --git a/Common/DirectXTK12/Inc/SimpleMath.inl b/Common/DirectXTK12/Inc/SimpleMath.inl new file mode 100644 index 0000000..2cd32dc --- /dev/null +++ b/Common/DirectXTK12/Inc/SimpleMath.inl @@ -0,0 +1,3826 @@ +//------------------------------------------------------------------------------------- +// SimpleMath.inl -- Simplified C++ Math wrapper for DirectXMath +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248929 +// http://go.microsoft.com/fwlink/?LinkID=615561 +//------------------------------------------------------------------------------------- + +#pragma once + +/**************************************************************************** +* +* Rectangle +* +****************************************************************************/ + +//------------------------------------------------------------------------------ +// Rectangle operations +//------------------------------------------------------------------------------ +inline Vector2 Rectangle::Location() const noexcept +{ + return Vector2(float(x), float(y)); +} + +inline Vector2 Rectangle::Center() const noexcept +{ + return Vector2(float(x) + (float(width) / 2.f), float(y) + (float(height) / 2.f)); +} + +inline bool Rectangle::Contains(const Vector2& point) const noexcept +{ + return (float(x) <= point.x) && (point.x < float(x + width)) && (float(y) <= point.y) && (point.y < float(y + height)); +} + +inline void Rectangle::Inflate(long horizAmount, long vertAmount) noexcept +{ + x -= horizAmount; + y -= vertAmount; + width += horizAmount; + height += vertAmount; +} + +//------------------------------------------------------------------------------ +// Static functions +//------------------------------------------------------------------------------ + +inline Rectangle Rectangle::Intersect(const Rectangle& ra, const Rectangle& rb) noexcept +{ + const long righta = ra.x + ra.width; + const long rightb = rb.x + rb.width; + + const long bottoma = ra.y + ra.height; + const long bottomb = rb.y + rb.height; + + const long maxX = ra.x > rb.x ? ra.x : rb.x; + const long maxY = ra.y > rb.y ? ra.y : rb.y; + + const long minRight = righta < rightb ? righta : rightb; + const long minBottom = bottoma < bottomb ? bottoma : bottomb; + + Rectangle result; + + if ((minRight > maxX) && (minBottom > maxY)) + { + result.x = maxX; + result.y = maxY; + result.width = minRight - maxX; + result.height = minBottom - maxY; + } + else + { + result.x = 0; + result.y = 0; + result.width = 0; + result.height = 0; + } + + return result; +} + +inline RECT Rectangle::Intersect(const RECT& rcta, const RECT& rctb) noexcept +{ + const long maxX = rcta.left > rctb.left ? rcta.left : rctb.left; + const long maxY = rcta.top > rctb.top ? rcta.top : rctb.top; + + const long minRight = rcta.right < rctb.right ? rcta.right : rctb.right; + const long minBottom = rcta.bottom < rctb.bottom ? rcta.bottom : rctb.bottom; + + RECT result; + + if ((minRight > maxX) && (minBottom > maxY)) + { + result.left = maxX; + result.top = maxY; + result.right = minRight; + result.bottom = minBottom; + } + else + { + result.left = 0; + result.top = 0; + result.right = 0; + result.bottom = 0; + } + + return result; +} + +inline Rectangle Rectangle::Union(const Rectangle& ra, const Rectangle& rb) noexcept +{ + const long righta = ra.x + ra.width; + const long rightb = rb.x + rb.width; + + const long bottoma = ra.y + ra.height; + const long bottomb = rb.y + rb.height; + + const int minX = ra.x < rb.x ? ra.x : rb.x; + const int minY = ra.y < rb.y ? ra.y : rb.y; + + const int maxRight = righta > rightb ? righta : rightb; + const int maxBottom = bottoma > bottomb ? bottoma : bottomb; + + Rectangle result; + result.x = minX; + result.y = minY; + result.width = maxRight - minX; + result.height = maxBottom - minY; + return result; +} + +inline RECT Rectangle::Union(const RECT& rcta, const RECT& rctb) noexcept +{ + RECT result; + result.left = rcta.left < rctb.left ? rcta.left : rctb.left; + result.top = rcta.top < rctb.top ? rcta.top : rctb.top; + result.right = rcta.right > rctb.right ? rcta.right : rctb.right; + result.bottom = rcta.bottom > rctb.bottom ? rcta.bottom : rctb.bottom; + return result; +} + + +/**************************************************************************** + * + * Vector2 + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ +// Comparision operators +//------------------------------------------------------------------------------ + +inline bool Vector2::operator == (const Vector2& V) const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(this); + const XMVECTOR v2 = XMLoadFloat2(&V); + return XMVector2Equal(v1, v2); +} + +inline bool Vector2::operator != (const Vector2& V) const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(this); + const XMVECTOR v2 = XMLoadFloat2(&V); + return XMVector2NotEqual(v1, v2); +} + +//------------------------------------------------------------------------------ +// Assignment operators +//------------------------------------------------------------------------------ + +inline Vector2& Vector2::operator+= (const Vector2& V) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(this); + const XMVECTOR v2 = XMLoadFloat2(&V); + const XMVECTOR X = XMVectorAdd(v1, v2); + XMStoreFloat2(this, X); + return *this; +} + +inline Vector2& Vector2::operator-= (const Vector2& V) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(this); + const XMVECTOR v2 = XMLoadFloat2(&V); + const XMVECTOR X = XMVectorSubtract(v1, v2); + XMStoreFloat2(this, X); + return *this; +} + +inline Vector2& Vector2::operator*= (const Vector2& V) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(this); + const XMVECTOR v2 = XMLoadFloat2(&V); + const XMVECTOR X = XMVectorMultiply(v1, v2); + XMStoreFloat2(this, X); + return *this; +} + +inline Vector2& Vector2::operator*= (float S) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(this); + const XMVECTOR X = XMVectorScale(v1, S); + XMStoreFloat2(this, X); + return *this; +} + +inline Vector2& Vector2::operator/= (float S) noexcept +{ + using namespace DirectX; + assert(S != 0.0f); + const XMVECTOR v1 = XMLoadFloat2(this); + const XMVECTOR X = XMVectorScale(v1, 1.f / S); + XMStoreFloat2(this, X); + return *this; +} + +//------------------------------------------------------------------------------ +// Binary operators +//------------------------------------------------------------------------------ + +inline Vector2 operator+ (const Vector2& V1, const Vector2& V2) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(&V1); + const XMVECTOR v2 = XMLoadFloat2(&V2); + const XMVECTOR X = XMVectorAdd(v1, v2); + Vector2 R; + XMStoreFloat2(&R, X); + return R; +} + +inline Vector2 operator- (const Vector2& V1, const Vector2& V2) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(&V1); + const XMVECTOR v2 = XMLoadFloat2(&V2); + const XMVECTOR X = XMVectorSubtract(v1, v2); + Vector2 R; + XMStoreFloat2(&R, X); + return R; +} + +inline Vector2 operator* (const Vector2& V1, const Vector2& V2) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(&V1); + const XMVECTOR v2 = XMLoadFloat2(&V2); + const XMVECTOR X = XMVectorMultiply(v1, v2); + Vector2 R; + XMStoreFloat2(&R, X); + return R; +} + +inline Vector2 operator* (const Vector2& V, float S) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(&V); + const XMVECTOR X = XMVectorScale(v1, S); + Vector2 R; + XMStoreFloat2(&R, X); + return R; +} + +inline Vector2 operator/ (const Vector2& V1, const Vector2& V2) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(&V1); + const XMVECTOR v2 = XMLoadFloat2(&V2); + const XMVECTOR X = XMVectorDivide(v1, v2); + Vector2 R; + XMStoreFloat2(&R, X); + return R; +} + +inline Vector2 operator/ (const Vector2& V, float S) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(&V); + const XMVECTOR X = XMVectorScale(v1, 1.f / S); + Vector2 R; + XMStoreFloat2(&R, X); + return R; +} + +inline Vector2 operator* (float S, const Vector2& V) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(&V); + const XMVECTOR X = XMVectorScale(v1, S); + Vector2 R; + XMStoreFloat2(&R, X); + return R; +} + +//------------------------------------------------------------------------------ +// Vector operations +//------------------------------------------------------------------------------ + +inline bool Vector2::InBounds(const Vector2& Bounds) const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(this); + const XMVECTOR v2 = XMLoadFloat2(&Bounds); + return XMVector2InBounds(v1, v2); +} + +inline float Vector2::Length() const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(this); + const XMVECTOR X = XMVector2Length(v1); + return XMVectorGetX(X); +} + +inline float Vector2::LengthSquared() const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(this); + const XMVECTOR X = XMVector2LengthSq(v1); + return XMVectorGetX(X); +} + +inline float Vector2::Dot(const Vector2& V) const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(this); + const XMVECTOR v2 = XMLoadFloat2(&V); + const XMVECTOR X = XMVector2Dot(v1, v2); + return XMVectorGetX(X); +} + +inline void Vector2::Cross(const Vector2& V, Vector2& result) const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(this); + const XMVECTOR v2 = XMLoadFloat2(&V); + const XMVECTOR R = XMVector2Cross(v1, v2); + XMStoreFloat2(&result, R); +} + +inline Vector2 Vector2::Cross(const Vector2& V) const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(this); + const XMVECTOR v2 = XMLoadFloat2(&V); + const XMVECTOR R = XMVector2Cross(v1, v2); + + Vector2 result; + XMStoreFloat2(&result, R); + return result; +} + +inline void Vector2::Normalize() noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(this); + const XMVECTOR X = XMVector2Normalize(v1); + XMStoreFloat2(this, X); +} + +inline void Vector2::Normalize(Vector2& result) const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(this); + const XMVECTOR X = XMVector2Normalize(v1); + XMStoreFloat2(&result, X); +} + +inline void Vector2::Clamp(const Vector2& vmin, const Vector2& vmax) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(this); + const XMVECTOR v2 = XMLoadFloat2(&vmin); + const XMVECTOR v3 = XMLoadFloat2(&vmax); + const XMVECTOR X = XMVectorClamp(v1, v2, v3); + XMStoreFloat2(this, X); +} + +inline void Vector2::Clamp(const Vector2& vmin, const Vector2& vmax, Vector2& result) const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(this); + const XMVECTOR v2 = XMLoadFloat2(&vmin); + const XMVECTOR v3 = XMLoadFloat2(&vmax); + const XMVECTOR X = XMVectorClamp(v1, v2, v3); + XMStoreFloat2(&result, X); +} + +//------------------------------------------------------------------------------ +// Static functions +//------------------------------------------------------------------------------ + +inline float Vector2::Distance(const Vector2& v1, const Vector2& v2) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat2(&v1); + const XMVECTOR x2 = XMLoadFloat2(&v2); + const XMVECTOR V = XMVectorSubtract(x2, x1); + const XMVECTOR X = XMVector2Length(V); + return XMVectorGetX(X); +} + +inline float Vector2::DistanceSquared(const Vector2& v1, const Vector2& v2) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat2(&v1); + const XMVECTOR x2 = XMLoadFloat2(&v2); + const XMVECTOR V = XMVectorSubtract(x2, x1); + const XMVECTOR X = XMVector2LengthSq(V); + return XMVectorGetX(X); +} + +inline void Vector2::Min(const Vector2& v1, const Vector2& v2, Vector2& result) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat2(&v1); + const XMVECTOR x2 = XMLoadFloat2(&v2); + const XMVECTOR X = XMVectorMin(x1, x2); + XMStoreFloat2(&result, X); +} + +inline Vector2 Vector2::Min(const Vector2& v1, const Vector2& v2) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat2(&v1); + const XMVECTOR x2 = XMLoadFloat2(&v2); + const XMVECTOR X = XMVectorMin(x1, x2); + + Vector2 result; + XMStoreFloat2(&result, X); + return result; +} + +inline void Vector2::Max(const Vector2& v1, const Vector2& v2, Vector2& result) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat2(&v1); + const XMVECTOR x2 = XMLoadFloat2(&v2); + const XMVECTOR X = XMVectorMax(x1, x2); + XMStoreFloat2(&result, X); +} + +inline Vector2 Vector2::Max(const Vector2& v1, const Vector2& v2) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat2(&v1); + const XMVECTOR x2 = XMLoadFloat2(&v2); + const XMVECTOR X = XMVectorMax(x1, x2); + + Vector2 result; + XMStoreFloat2(&result, X); + return result; +} + +inline void Vector2::Lerp(const Vector2& v1, const Vector2& v2, float t, Vector2& result) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat2(&v1); + const XMVECTOR x2 = XMLoadFloat2(&v2); + const XMVECTOR X = XMVectorLerp(x1, x2, t); + XMStoreFloat2(&result, X); +} + +inline Vector2 Vector2::Lerp(const Vector2& v1, const Vector2& v2, float t) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat2(&v1); + const XMVECTOR x2 = XMLoadFloat2(&v2); + const XMVECTOR X = XMVectorLerp(x1, x2, t); + + Vector2 result; + XMStoreFloat2(&result, X); + return result; +} + +inline void Vector2::SmoothStep(const Vector2& v1, const Vector2& v2, float t, Vector2& result) noexcept +{ + using namespace DirectX; + t = (t > 1.0f) ? 1.0f : ((t < 0.0f) ? 0.0f : t); // Clamp value to 0 to 1 + t = t * t*(3.f - 2.f*t); + const XMVECTOR x1 = XMLoadFloat2(&v1); + const XMVECTOR x2 = XMLoadFloat2(&v2); + const XMVECTOR X = XMVectorLerp(x1, x2, t); + XMStoreFloat2(&result, X); +} + +inline Vector2 Vector2::SmoothStep(const Vector2& v1, const Vector2& v2, float t) noexcept +{ + using namespace DirectX; + t = (t > 1.0f) ? 1.0f : ((t < 0.0f) ? 0.0f : t); // Clamp value to 0 to 1 + t = t * t*(3.f - 2.f*t); + const XMVECTOR x1 = XMLoadFloat2(&v1); + const XMVECTOR x2 = XMLoadFloat2(&v2); + const XMVECTOR X = XMVectorLerp(x1, x2, t); + + Vector2 result; + XMStoreFloat2(&result, X); + return result; +} + +inline void Vector2::Barycentric(const Vector2& v1, const Vector2& v2, const Vector2& v3, float f, float g, Vector2& result) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat2(&v1); + const XMVECTOR x2 = XMLoadFloat2(&v2); + const XMVECTOR x3 = XMLoadFloat2(&v3); + const XMVECTOR X = XMVectorBaryCentric(x1, x2, x3, f, g); + XMStoreFloat2(&result, X); +} + +inline Vector2 Vector2::Barycentric(const Vector2& v1, const Vector2& v2, const Vector2& v3, float f, float g) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat2(&v1); + const XMVECTOR x2 = XMLoadFloat2(&v2); + const XMVECTOR x3 = XMLoadFloat2(&v3); + const XMVECTOR X = XMVectorBaryCentric(x1, x2, x3, f, g); + + Vector2 result; + XMStoreFloat2(&result, X); + return result; +} + +inline void Vector2::CatmullRom(const Vector2& v1, const Vector2& v2, const Vector2& v3, const Vector2& v4, float t, Vector2& result) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat2(&v1); + const XMVECTOR x2 = XMLoadFloat2(&v2); + const XMVECTOR x3 = XMLoadFloat2(&v3); + const XMVECTOR x4 = XMLoadFloat2(&v4); + const XMVECTOR X = XMVectorCatmullRom(x1, x2, x3, x4, t); + XMStoreFloat2(&result, X); +} + +inline Vector2 Vector2::CatmullRom(const Vector2& v1, const Vector2& v2, const Vector2& v3, const Vector2& v4, float t) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat2(&v1); + const XMVECTOR x2 = XMLoadFloat2(&v2); + const XMVECTOR x3 = XMLoadFloat2(&v3); + const XMVECTOR x4 = XMLoadFloat2(&v4); + const XMVECTOR X = XMVectorCatmullRom(x1, x2, x3, x4, t); + + Vector2 result; + XMStoreFloat2(&result, X); + return result; +} + +inline void Vector2::Hermite(const Vector2& v1, const Vector2& t1, const Vector2& v2, const Vector2& t2, float t, Vector2& result) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat2(&v1); + const XMVECTOR x2 = XMLoadFloat2(&t1); + const XMVECTOR x3 = XMLoadFloat2(&v2); + const XMVECTOR x4 = XMLoadFloat2(&t2); + const XMVECTOR X = XMVectorHermite(x1, x2, x3, x4, t); + XMStoreFloat2(&result, X); +} + +inline Vector2 Vector2::Hermite(const Vector2& v1, const Vector2& t1, const Vector2& v2, const Vector2& t2, float t) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat2(&v1); + const XMVECTOR x2 = XMLoadFloat2(&t1); + const XMVECTOR x3 = XMLoadFloat2(&v2); + const XMVECTOR x4 = XMLoadFloat2(&t2); + const XMVECTOR X = XMVectorHermite(x1, x2, x3, x4, t); + + Vector2 result; + XMStoreFloat2(&result, X); + return result; +} + +inline void Vector2::Reflect(const Vector2& ivec, const Vector2& nvec, Vector2& result) noexcept +{ + using namespace DirectX; + const XMVECTOR i = XMLoadFloat2(&ivec); + const XMVECTOR n = XMLoadFloat2(&nvec); + const XMVECTOR X = XMVector2Reflect(i, n); + XMStoreFloat2(&result, X); +} + +inline Vector2 Vector2::Reflect(const Vector2& ivec, const Vector2& nvec) noexcept +{ + using namespace DirectX; + const XMVECTOR i = XMLoadFloat2(&ivec); + const XMVECTOR n = XMLoadFloat2(&nvec); + const XMVECTOR X = XMVector2Reflect(i, n); + + Vector2 result; + XMStoreFloat2(&result, X); + return result; +} + +inline void Vector2::Refract(const Vector2& ivec, const Vector2& nvec, float refractionIndex, Vector2& result) noexcept +{ + using namespace DirectX; + const XMVECTOR i = XMLoadFloat2(&ivec); + const XMVECTOR n = XMLoadFloat2(&nvec); + const XMVECTOR X = XMVector2Refract(i, n, refractionIndex); + XMStoreFloat2(&result, X); +} + +inline Vector2 Vector2::Refract(const Vector2& ivec, const Vector2& nvec, float refractionIndex) noexcept +{ + using namespace DirectX; + const XMVECTOR i = XMLoadFloat2(&ivec); + const XMVECTOR n = XMLoadFloat2(&nvec); + const XMVECTOR X = XMVector2Refract(i, n, refractionIndex); + + Vector2 result; + XMStoreFloat2(&result, X); + return result; +} + +inline void Vector2::Transform(const Vector2& v, const Quaternion& quat, Vector2& result) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(&v); + const XMVECTOR q = XMLoadFloat4(&quat); + const XMVECTOR X = XMVector3Rotate(v1, q); + XMStoreFloat2(&result, X); +} + +inline Vector2 Vector2::Transform(const Vector2& v, const Quaternion& quat) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(&v); + const XMVECTOR q = XMLoadFloat4(&quat); + const XMVECTOR X = XMVector3Rotate(v1, q); + + Vector2 result; + XMStoreFloat2(&result, X); + return result; +} + +inline void Vector2::Transform(const Vector2& v, const Matrix& m, Vector2& result) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(&v); + const XMMATRIX M = XMLoadFloat4x4(&m); + const XMVECTOR X = XMVector2TransformCoord(v1, M); + XMStoreFloat2(&result, X); +} + +inline Vector2 Vector2::Transform(const Vector2& v, const Matrix& m) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(&v); + const XMMATRIX M = XMLoadFloat4x4(&m); + const XMVECTOR X = XMVector2TransformCoord(v1, M); + + Vector2 result; + XMStoreFloat2(&result, X); + return result; +} + +_Use_decl_annotations_ +inline void Vector2::Transform(const Vector2* varray, size_t count, const Matrix& m, Vector2* resultArray) noexcept +{ + using namespace DirectX; + const XMMATRIX M = XMLoadFloat4x4(&m); + XMVector2TransformCoordStream(resultArray, sizeof(XMFLOAT2), varray, sizeof(XMFLOAT2), count, M); +} + +inline void Vector2::Transform(const Vector2& v, const Matrix& m, Vector4& result) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(&v); + const XMMATRIX M = XMLoadFloat4x4(&m); + const XMVECTOR X = XMVector2Transform(v1, M); + XMStoreFloat4(&result, X); +} + +_Use_decl_annotations_ +inline void Vector2::Transform(const Vector2* varray, size_t count, const Matrix& m, Vector4* resultArray) noexcept +{ + using namespace DirectX; + const XMMATRIX M = XMLoadFloat4x4(&m); + XMVector2TransformStream(resultArray, sizeof(XMFLOAT4), varray, sizeof(XMFLOAT2), count, M); +} + +inline void Vector2::TransformNormal(const Vector2& v, const Matrix& m, Vector2& result) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(&v); + const XMMATRIX M = XMLoadFloat4x4(&m); + const XMVECTOR X = XMVector2TransformNormal(v1, M); + XMStoreFloat2(&result, X); +} + +inline Vector2 Vector2::TransformNormal(const Vector2& v, const Matrix& m) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(&v); + const XMMATRIX M = XMLoadFloat4x4(&m); + const XMVECTOR X = XMVector2TransformNormal(v1, M); + + Vector2 result; + XMStoreFloat2(&result, X); + return result; +} + +_Use_decl_annotations_ +inline void Vector2::TransformNormal(const Vector2* varray, size_t count, const Matrix& m, Vector2* resultArray) noexcept +{ + using namespace DirectX; + const XMMATRIX M = XMLoadFloat4x4(&m); + XMVector2TransformNormalStream(resultArray, sizeof(XMFLOAT2), varray, sizeof(XMFLOAT2), count, M); +} + + +/**************************************************************************** + * + * Vector3 + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ +// Comparision operators +//------------------------------------------------------------------------------ + +inline bool Vector3::operator == (const Vector3& V) const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(this); + const XMVECTOR v2 = XMLoadFloat3(&V); + return XMVector3Equal(v1, v2); +} + +inline bool Vector3::operator != (const Vector3& V) const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(this); + const XMVECTOR v2 = XMLoadFloat3(&V); + return XMVector3NotEqual(v1, v2); +} + +//------------------------------------------------------------------------------ +// Assignment operators +//------------------------------------------------------------------------------ + +inline Vector3& Vector3::operator+= (const Vector3& V) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(this); + const XMVECTOR v2 = XMLoadFloat3(&V); + const XMVECTOR X = XMVectorAdd(v1, v2); + XMStoreFloat3(this, X); + return *this; +} + +inline Vector3& Vector3::operator-= (const Vector3& V) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(this); + const XMVECTOR v2 = XMLoadFloat3(&V); + const XMVECTOR X = XMVectorSubtract(v1, v2); + XMStoreFloat3(this, X); + return *this; +} + +inline Vector3& Vector3::operator*= (const Vector3& V) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(this); + const XMVECTOR v2 = XMLoadFloat3(&V); + const XMVECTOR X = XMVectorMultiply(v1, v2); + XMStoreFloat3(this, X); + return *this; +} + +inline Vector3& Vector3::operator*= (float S) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(this); + const XMVECTOR X = XMVectorScale(v1, S); + XMStoreFloat3(this, X); + return *this; +} + +inline Vector3& Vector3::operator/= (float S) noexcept +{ + using namespace DirectX; + assert(S != 0.0f); + const XMVECTOR v1 = XMLoadFloat3(this); + const XMVECTOR X = XMVectorScale(v1, 1.f / S); + XMStoreFloat3(this, X); + return *this; +} + +//------------------------------------------------------------------------------ +// Urnary operators +//------------------------------------------------------------------------------ + +inline Vector3 Vector3::operator- () const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(this); + const XMVECTOR X = XMVectorNegate(v1); + Vector3 R; + XMStoreFloat3(&R, X); + return R; +} + +//------------------------------------------------------------------------------ +// Binary operators +//------------------------------------------------------------------------------ + +inline Vector3 operator+ (const Vector3& V1, const Vector3& V2) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(&V1); + const XMVECTOR v2 = XMLoadFloat3(&V2); + const XMVECTOR X = XMVectorAdd(v1, v2); + Vector3 R; + XMStoreFloat3(&R, X); + return R; +} + +inline Vector3 operator- (const Vector3& V1, const Vector3& V2) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(&V1); + const XMVECTOR v2 = XMLoadFloat3(&V2); + const XMVECTOR X = XMVectorSubtract(v1, v2); + Vector3 R; + XMStoreFloat3(&R, X); + return R; +} + +inline Vector3 operator* (const Vector3& V1, const Vector3& V2) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(&V1); + const XMVECTOR v2 = XMLoadFloat3(&V2); + const XMVECTOR X = XMVectorMultiply(v1, v2); + Vector3 R; + XMStoreFloat3(&R, X); + return R; +} + +inline Vector3 operator* (const Vector3& V, float S) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(&V); + const XMVECTOR X = XMVectorScale(v1, S); + Vector3 R; + XMStoreFloat3(&R, X); + return R; +} + +inline Vector3 operator/ (const Vector3& V1, const Vector3& V2) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(&V1); + const XMVECTOR v2 = XMLoadFloat3(&V2); + const XMVECTOR X = XMVectorDivide(v1, v2); + Vector3 R; + XMStoreFloat3(&R, X); + return R; +} + +inline Vector3 operator/ (const Vector3& V, float S) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(&V); + const XMVECTOR X = XMVectorScale(v1, 1.f / S); + Vector3 R; + XMStoreFloat3(&R, X); + return R; +} + +inline Vector3 operator* (float S, const Vector3& V) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(&V); + const XMVECTOR X = XMVectorScale(v1, S); + Vector3 R; + XMStoreFloat3(&R, X); + return R; +} + +//------------------------------------------------------------------------------ +// Vector operations +//------------------------------------------------------------------------------ + +inline bool Vector3::InBounds(const Vector3& Bounds) const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(this); + const XMVECTOR v2 = XMLoadFloat3(&Bounds); + return XMVector3InBounds(v1, v2); +} + +inline float Vector3::Length() const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(this); + const XMVECTOR X = XMVector3Length(v1); + return XMVectorGetX(X); +} + +inline float Vector3::LengthSquared() const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(this); + const XMVECTOR X = XMVector3LengthSq(v1); + return XMVectorGetX(X); +} + +inline float Vector3::Dot(const Vector3& V) const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(this); + const XMVECTOR v2 = XMLoadFloat3(&V); + const XMVECTOR X = XMVector3Dot(v1, v2); + return XMVectorGetX(X); +} + +inline void Vector3::Cross(const Vector3& V, Vector3& result) const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(this); + const XMVECTOR v2 = XMLoadFloat3(&V); + const XMVECTOR R = XMVector3Cross(v1, v2); + XMStoreFloat3(&result, R); +} + +inline Vector3 Vector3::Cross(const Vector3& V) const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(this); + const XMVECTOR v2 = XMLoadFloat3(&V); + const XMVECTOR R = XMVector3Cross(v1, v2); + + Vector3 result; + XMStoreFloat3(&result, R); + return result; +} + +inline void Vector3::Normalize() noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(this); + const XMVECTOR X = XMVector3Normalize(v1); + XMStoreFloat3(this, X); +} + +inline void Vector3::Normalize(Vector3& result) const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(this); + const XMVECTOR X = XMVector3Normalize(v1); + XMStoreFloat3(&result, X); +} + +inline void Vector3::Clamp(const Vector3& vmin, const Vector3& vmax) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(this); + const XMVECTOR v2 = XMLoadFloat3(&vmin); + const XMVECTOR v3 = XMLoadFloat3(&vmax); + const XMVECTOR X = XMVectorClamp(v1, v2, v3); + XMStoreFloat3(this, X); +} + +inline void Vector3::Clamp(const Vector3& vmin, const Vector3& vmax, Vector3& result) const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(this); + const XMVECTOR v2 = XMLoadFloat3(&vmin); + const XMVECTOR v3 = XMLoadFloat3(&vmax); + const XMVECTOR X = XMVectorClamp(v1, v2, v3); + XMStoreFloat3(&result, X); +} + +//------------------------------------------------------------------------------ +// Static functions +//------------------------------------------------------------------------------ + +inline float Vector3::Distance(const Vector3& v1, const Vector3& v2) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat3(&v1); + const XMVECTOR x2 = XMLoadFloat3(&v2); + const XMVECTOR V = XMVectorSubtract(x2, x1); + const XMVECTOR X = XMVector3Length(V); + return XMVectorGetX(X); +} + +inline float Vector3::DistanceSquared(const Vector3& v1, const Vector3& v2) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat3(&v1); + const XMVECTOR x2 = XMLoadFloat3(&v2); + const XMVECTOR V = XMVectorSubtract(x2, x1); + const XMVECTOR X = XMVector3LengthSq(V); + return XMVectorGetX(X); +} + +inline void Vector3::Min(const Vector3& v1, const Vector3& v2, Vector3& result) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat3(&v1); + const XMVECTOR x2 = XMLoadFloat3(&v2); + const XMVECTOR X = XMVectorMin(x1, x2); + XMStoreFloat3(&result, X); +} + +inline Vector3 Vector3::Min(const Vector3& v1, const Vector3& v2) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat3(&v1); + const XMVECTOR x2 = XMLoadFloat3(&v2); + const XMVECTOR X = XMVectorMin(x1, x2); + + Vector3 result; + XMStoreFloat3(&result, X); + return result; +} + +inline void Vector3::Max(const Vector3& v1, const Vector3& v2, Vector3& result) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat3(&v1); + const XMVECTOR x2 = XMLoadFloat3(&v2); + const XMVECTOR X = XMVectorMax(x1, x2); + XMStoreFloat3(&result, X); +} + +inline Vector3 Vector3::Max(const Vector3& v1, const Vector3& v2) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat3(&v1); + const XMVECTOR x2 = XMLoadFloat3(&v2); + const XMVECTOR X = XMVectorMax(x1, x2); + + Vector3 result; + XMStoreFloat3(&result, X); + return result; +} + +inline void Vector3::Lerp(const Vector3& v1, const Vector3& v2, float t, Vector3& result) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat3(&v1); + const XMVECTOR x2 = XMLoadFloat3(&v2); + const XMVECTOR X = XMVectorLerp(x1, x2, t); + XMStoreFloat3(&result, X); +} + +inline Vector3 Vector3::Lerp(const Vector3& v1, const Vector3& v2, float t) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat3(&v1); + const XMVECTOR x2 = XMLoadFloat3(&v2); + const XMVECTOR X = XMVectorLerp(x1, x2, t); + + Vector3 result; + XMStoreFloat3(&result, X); + return result; +} + +inline void Vector3::SmoothStep(const Vector3& v1, const Vector3& v2, float t, Vector3& result) noexcept +{ + using namespace DirectX; + t = (t > 1.0f) ? 1.0f : ((t < 0.0f) ? 0.0f : t); // Clamp value to 0 to 1 + t = t * t*(3.f - 2.f*t); + const XMVECTOR x1 = XMLoadFloat3(&v1); + const XMVECTOR x2 = XMLoadFloat3(&v2); + const XMVECTOR X = XMVectorLerp(x1, x2, t); + XMStoreFloat3(&result, X); +} + +inline Vector3 Vector3::SmoothStep(const Vector3& v1, const Vector3& v2, float t) noexcept +{ + using namespace DirectX; + t = (t > 1.0f) ? 1.0f : ((t < 0.0f) ? 0.0f : t); // Clamp value to 0 to 1 + t = t * t*(3.f - 2.f*t); + const XMVECTOR x1 = XMLoadFloat3(&v1); + const XMVECTOR x2 = XMLoadFloat3(&v2); + const XMVECTOR X = XMVectorLerp(x1, x2, t); + + Vector3 result; + XMStoreFloat3(&result, X); + return result; +} + +inline void Vector3::Barycentric(const Vector3& v1, const Vector3& v2, const Vector3& v3, float f, float g, Vector3& result) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat3(&v1); + const XMVECTOR x2 = XMLoadFloat3(&v2); + const XMVECTOR x3 = XMLoadFloat3(&v3); + const XMVECTOR X = XMVectorBaryCentric(x1, x2, x3, f, g); + XMStoreFloat3(&result, X); +} + +inline Vector3 Vector3::Barycentric(const Vector3& v1, const Vector3& v2, const Vector3& v3, float f, float g) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat3(&v1); + const XMVECTOR x2 = XMLoadFloat3(&v2); + const XMVECTOR x3 = XMLoadFloat3(&v3); + const XMVECTOR X = XMVectorBaryCentric(x1, x2, x3, f, g); + + Vector3 result; + XMStoreFloat3(&result, X); + return result; +} + +inline void Vector3::CatmullRom(const Vector3& v1, const Vector3& v2, const Vector3& v3, const Vector3& v4, float t, Vector3& result) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat3(&v1); + const XMVECTOR x2 = XMLoadFloat3(&v2); + const XMVECTOR x3 = XMLoadFloat3(&v3); + const XMVECTOR x4 = XMLoadFloat3(&v4); + const XMVECTOR X = XMVectorCatmullRom(x1, x2, x3, x4, t); + XMStoreFloat3(&result, X); +} + +inline Vector3 Vector3::CatmullRom(const Vector3& v1, const Vector3& v2, const Vector3& v3, const Vector3& v4, float t) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat3(&v1); + const XMVECTOR x2 = XMLoadFloat3(&v2); + const XMVECTOR x3 = XMLoadFloat3(&v3); + const XMVECTOR x4 = XMLoadFloat3(&v4); + const XMVECTOR X = XMVectorCatmullRom(x1, x2, x3, x4, t); + + Vector3 result; + XMStoreFloat3(&result, X); + return result; +} + +inline void Vector3::Hermite(const Vector3& v1, const Vector3& t1, const Vector3& v2, const Vector3& t2, float t, Vector3& result) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat3(&v1); + const XMVECTOR x2 = XMLoadFloat3(&t1); + const XMVECTOR x3 = XMLoadFloat3(&v2); + const XMVECTOR x4 = XMLoadFloat3(&t2); + const XMVECTOR X = XMVectorHermite(x1, x2, x3, x4, t); + XMStoreFloat3(&result, X); +} + +inline Vector3 Vector3::Hermite(const Vector3& v1, const Vector3& t1, const Vector3& v2, const Vector3& t2, float t) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat3(&v1); + const XMVECTOR x2 = XMLoadFloat3(&t1); + const XMVECTOR x3 = XMLoadFloat3(&v2); + const XMVECTOR x4 = XMLoadFloat3(&t2); + const XMVECTOR X = XMVectorHermite(x1, x2, x3, x4, t); + + Vector3 result; + XMStoreFloat3(&result, X); + return result; +} + +inline void Vector3::Reflect(const Vector3& ivec, const Vector3& nvec, Vector3& result) noexcept +{ + using namespace DirectX; + const XMVECTOR i = XMLoadFloat3(&ivec); + const XMVECTOR n = XMLoadFloat3(&nvec); + const XMVECTOR X = XMVector3Reflect(i, n); + XMStoreFloat3(&result, X); +} + +inline Vector3 Vector3::Reflect(const Vector3& ivec, const Vector3& nvec) noexcept +{ + using namespace DirectX; + const XMVECTOR i = XMLoadFloat3(&ivec); + const XMVECTOR n = XMLoadFloat3(&nvec); + const XMVECTOR X = XMVector3Reflect(i, n); + + Vector3 result; + XMStoreFloat3(&result, X); + return result; +} + +inline void Vector3::Refract(const Vector3& ivec, const Vector3& nvec, float refractionIndex, Vector3& result) noexcept +{ + using namespace DirectX; + const XMVECTOR i = XMLoadFloat3(&ivec); + const XMVECTOR n = XMLoadFloat3(&nvec); + const XMVECTOR X = XMVector3Refract(i, n, refractionIndex); + XMStoreFloat3(&result, X); +} + +inline Vector3 Vector3::Refract(const Vector3& ivec, const Vector3& nvec, float refractionIndex) noexcept +{ + using namespace DirectX; + const XMVECTOR i = XMLoadFloat3(&ivec); + const XMVECTOR n = XMLoadFloat3(&nvec); + const XMVECTOR X = XMVector3Refract(i, n, refractionIndex); + + Vector3 result; + XMStoreFloat3(&result, X); + return result; +} + +inline void Vector3::Transform(const Vector3& v, const Quaternion& quat, Vector3& result) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(&v); + const XMVECTOR q = XMLoadFloat4(&quat); + const XMVECTOR X = XMVector3Rotate(v1, q); + XMStoreFloat3(&result, X); +} + +inline Vector3 Vector3::Transform(const Vector3& v, const Quaternion& quat) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(&v); + const XMVECTOR q = XMLoadFloat4(&quat); + const XMVECTOR X = XMVector3Rotate(v1, q); + + Vector3 result; + XMStoreFloat3(&result, X); + return result; +} + +inline void Vector3::Transform(const Vector3& v, const Matrix& m, Vector3& result) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(&v); + const XMMATRIX M = XMLoadFloat4x4(&m); + const XMVECTOR X = XMVector3TransformCoord(v1, M); + XMStoreFloat3(&result, X); +} + +inline Vector3 Vector3::Transform(const Vector3& v, const Matrix& m) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(&v); + const XMMATRIX M = XMLoadFloat4x4(&m); + const XMVECTOR X = XMVector3TransformCoord(v1, M); + + Vector3 result; + XMStoreFloat3(&result, X); + return result; +} + +_Use_decl_annotations_ +inline void Vector3::Transform(const Vector3* varray, size_t count, const Matrix& m, Vector3* resultArray) noexcept +{ + using namespace DirectX; + const XMMATRIX M = XMLoadFloat4x4(&m); + XMVector3TransformCoordStream(resultArray, sizeof(XMFLOAT3), varray, sizeof(XMFLOAT3), count, M); +} + +inline void Vector3::Transform(const Vector3& v, const Matrix& m, Vector4& result) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(&v); + const XMMATRIX M = XMLoadFloat4x4(&m); + const XMVECTOR X = XMVector3Transform(v1, M); + XMStoreFloat4(&result, X); +} + +_Use_decl_annotations_ +inline void Vector3::Transform(const Vector3* varray, size_t count, const Matrix& m, Vector4* resultArray) noexcept +{ + using namespace DirectX; + const XMMATRIX M = XMLoadFloat4x4(&m); + XMVector3TransformStream(resultArray, sizeof(XMFLOAT4), varray, sizeof(XMFLOAT3), count, M); +} + +inline void Vector3::TransformNormal(const Vector3& v, const Matrix& m, Vector3& result) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(&v); + const XMMATRIX M = XMLoadFloat4x4(&m); + const XMVECTOR X = XMVector3TransformNormal(v1, M); + XMStoreFloat3(&result, X); +} + +inline Vector3 Vector3::TransformNormal(const Vector3& v, const Matrix& m) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(&v); + const XMMATRIX M = XMLoadFloat4x4(&m); + const XMVECTOR X = XMVector3TransformNormal(v1, M); + + Vector3 result; + XMStoreFloat3(&result, X); + return result; +} + +_Use_decl_annotations_ +inline void Vector3::TransformNormal(const Vector3* varray, size_t count, const Matrix& m, Vector3* resultArray) noexcept +{ + using namespace DirectX; + const XMMATRIX M = XMLoadFloat4x4(&m); + XMVector3TransformNormalStream(resultArray, sizeof(XMFLOAT3), varray, sizeof(XMFLOAT3), count, M); +} + + +/**************************************************************************** + * + * Vector4 + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ +// Comparision operators +//------------------------------------------------------------------------------ + +inline bool Vector4::operator == (const Vector4& V) const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(this); + const XMVECTOR v2 = XMLoadFloat4(&V); + return XMVector4Equal(v1, v2); +} + +inline bool Vector4::operator != (const Vector4& V) const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(this); + const XMVECTOR v2 = XMLoadFloat4(&V); + return XMVector4NotEqual(v1, v2); +} + +//------------------------------------------------------------------------------ +// Assignment operators +//------------------------------------------------------------------------------ + +inline Vector4& Vector4::operator+= (const Vector4& V) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(this); + const XMVECTOR v2 = XMLoadFloat4(&V); + const XMVECTOR X = XMVectorAdd(v1, v2); + XMStoreFloat4(this, X); + return *this; +} + +inline Vector4& Vector4::operator-= (const Vector4& V) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(this); + const XMVECTOR v2 = XMLoadFloat4(&V); + const XMVECTOR X = XMVectorSubtract(v1, v2); + XMStoreFloat4(this, X); + return *this; +} + +inline Vector4& Vector4::operator*= (const Vector4& V) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(this); + const XMVECTOR v2 = XMLoadFloat4(&V); + const XMVECTOR X = XMVectorMultiply(v1, v2); + XMStoreFloat4(this, X); + return *this; +} + +inline Vector4& Vector4::operator*= (float S) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(this); + const XMVECTOR X = XMVectorScale(v1, S); + XMStoreFloat4(this, X); + return *this; +} + +inline Vector4& Vector4::operator/= (float S) noexcept +{ + using namespace DirectX; + assert(S != 0.0f); + const XMVECTOR v1 = XMLoadFloat4(this); + const XMVECTOR X = XMVectorScale(v1, 1.f / S); + XMStoreFloat4(this, X); + return *this; +} + +//------------------------------------------------------------------------------ +// Urnary operators +//------------------------------------------------------------------------------ + +inline Vector4 Vector4::operator- () const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(this); + const XMVECTOR X = XMVectorNegate(v1); + Vector4 R; + XMStoreFloat4(&R, X); + return R; +} + +//------------------------------------------------------------------------------ +// Binary operators +//------------------------------------------------------------------------------ + +inline Vector4 operator+ (const Vector4& V1, const Vector4& V2) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(&V1); + const XMVECTOR v2 = XMLoadFloat4(&V2); + const XMVECTOR X = XMVectorAdd(v1, v2); + Vector4 R; + XMStoreFloat4(&R, X); + return R; +} + +inline Vector4 operator- (const Vector4& V1, const Vector4& V2) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(&V1); + const XMVECTOR v2 = XMLoadFloat4(&V2); + const XMVECTOR X = XMVectorSubtract(v1, v2); + Vector4 R; + XMStoreFloat4(&R, X); + return R; +} + +inline Vector4 operator* (const Vector4& V1, const Vector4& V2) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(&V1); + const XMVECTOR v2 = XMLoadFloat4(&V2); + const XMVECTOR X = XMVectorMultiply(v1, v2); + Vector4 R; + XMStoreFloat4(&R, X); + return R; +} + +inline Vector4 operator* (const Vector4& V, float S) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(&V); + const XMVECTOR X = XMVectorScale(v1, S); + Vector4 R; + XMStoreFloat4(&R, X); + return R; +} + +inline Vector4 operator/ (const Vector4& V1, const Vector4& V2) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(&V1); + const XMVECTOR v2 = XMLoadFloat4(&V2); + const XMVECTOR X = XMVectorDivide(v1, v2); + Vector4 R; + XMStoreFloat4(&R, X); + return R; +} + +inline Vector4 operator/ (const Vector4& V, float S) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(&V); + const XMVECTOR X = XMVectorScale(v1, 1.f / S); + Vector4 R; + XMStoreFloat4(&R, X); + return R; +} + +inline Vector4 operator* (float S, const Vector4& V) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(&V); + const XMVECTOR X = XMVectorScale(v1, S); + Vector4 R; + XMStoreFloat4(&R, X); + return R; +} + +//------------------------------------------------------------------------------ +// Vector operations +//------------------------------------------------------------------------------ + +inline bool Vector4::InBounds(const Vector4& Bounds) const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(this); + const XMVECTOR v2 = XMLoadFloat4(&Bounds); + return XMVector4InBounds(v1, v2); +} + +inline float Vector4::Length() const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(this); + const XMVECTOR X = XMVector4Length(v1); + return XMVectorGetX(X); +} + +inline float Vector4::LengthSquared() const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(this); + const XMVECTOR X = XMVector4LengthSq(v1); + return XMVectorGetX(X); +} + +inline float Vector4::Dot(const Vector4& V) const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(this); + const XMVECTOR v2 = XMLoadFloat4(&V); + const XMVECTOR X = XMVector4Dot(v1, v2); + return XMVectorGetX(X); +} + +inline void Vector4::Cross(const Vector4& v1, const Vector4& v2, Vector4& result) const noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat4(this); + const XMVECTOR x2 = XMLoadFloat4(&v1); + const XMVECTOR x3 = XMLoadFloat4(&v2); + const XMVECTOR R = XMVector4Cross(x1, x2, x3); + XMStoreFloat4(&result, R); +} + +inline Vector4 Vector4::Cross(const Vector4& v1, const Vector4& v2) const noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat4(this); + const XMVECTOR x2 = XMLoadFloat4(&v1); + const XMVECTOR x3 = XMLoadFloat4(&v2); + const XMVECTOR R = XMVector4Cross(x1, x2, x3); + + Vector4 result; + XMStoreFloat4(&result, R); + return result; +} + +inline void Vector4::Normalize() noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(this); + const XMVECTOR X = XMVector4Normalize(v1); + XMStoreFloat4(this, X); +} + +inline void Vector4::Normalize(Vector4& result) const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(this); + const XMVECTOR X = XMVector4Normalize(v1); + XMStoreFloat4(&result, X); +} + +inline void Vector4::Clamp(const Vector4& vmin, const Vector4& vmax) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(this); + const XMVECTOR v2 = XMLoadFloat4(&vmin); + const XMVECTOR v3 = XMLoadFloat4(&vmax); + const XMVECTOR X = XMVectorClamp(v1, v2, v3); + XMStoreFloat4(this, X); +} + +inline void Vector4::Clamp(const Vector4& vmin, const Vector4& vmax, Vector4& result) const noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(this); + const XMVECTOR v2 = XMLoadFloat4(&vmin); + const XMVECTOR v3 = XMLoadFloat4(&vmax); + const XMVECTOR X = XMVectorClamp(v1, v2, v3); + XMStoreFloat4(&result, X); +} + +//------------------------------------------------------------------------------ +// Static functions +//------------------------------------------------------------------------------ + +inline float Vector4::Distance(const Vector4& v1, const Vector4& v2) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat4(&v1); + const XMVECTOR x2 = XMLoadFloat4(&v2); + const XMVECTOR V = XMVectorSubtract(x2, x1); + const XMVECTOR X = XMVector4Length(V); + return XMVectorGetX(X); +} + +inline float Vector4::DistanceSquared(const Vector4& v1, const Vector4& v2) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat4(&v1); + const XMVECTOR x2 = XMLoadFloat4(&v2); + const XMVECTOR V = XMVectorSubtract(x2, x1); + const XMVECTOR X = XMVector4LengthSq(V); + return XMVectorGetX(X); +} + +inline void Vector4::Min(const Vector4& v1, const Vector4& v2, Vector4& result) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat4(&v1); + const XMVECTOR x2 = XMLoadFloat4(&v2); + const XMVECTOR X = XMVectorMin(x1, x2); + XMStoreFloat4(&result, X); +} + +inline Vector4 Vector4::Min(const Vector4& v1, const Vector4& v2) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat4(&v1); + const XMVECTOR x2 = XMLoadFloat4(&v2); + const XMVECTOR X = XMVectorMin(x1, x2); + + Vector4 result; + XMStoreFloat4(&result, X); + return result; +} + +inline void Vector4::Max(const Vector4& v1, const Vector4& v2, Vector4& result) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat4(&v1); + const XMVECTOR x2 = XMLoadFloat4(&v2); + const XMVECTOR X = XMVectorMax(x1, x2); + XMStoreFloat4(&result, X); +} + +inline Vector4 Vector4::Max(const Vector4& v1, const Vector4& v2) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat4(&v1); + const XMVECTOR x2 = XMLoadFloat4(&v2); + const XMVECTOR X = XMVectorMax(x1, x2); + + Vector4 result; + XMStoreFloat4(&result, X); + return result; +} + +inline void Vector4::Lerp(const Vector4& v1, const Vector4& v2, float t, Vector4& result) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat4(&v1); + const XMVECTOR x2 = XMLoadFloat4(&v2); + const XMVECTOR X = XMVectorLerp(x1, x2, t); + XMStoreFloat4(&result, X); +} + +inline Vector4 Vector4::Lerp(const Vector4& v1, const Vector4& v2, float t) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat4(&v1); + const XMVECTOR x2 = XMLoadFloat4(&v2); + const XMVECTOR X = XMVectorLerp(x1, x2, t); + + Vector4 result; + XMStoreFloat4(&result, X); + return result; +} + +inline void Vector4::SmoothStep(const Vector4& v1, const Vector4& v2, float t, Vector4& result) noexcept +{ + using namespace DirectX; + t = (t > 1.0f) ? 1.0f : ((t < 0.0f) ? 0.0f : t); // Clamp value to 0 to 1 + t = t * t*(3.f - 2.f*t); + const XMVECTOR x1 = XMLoadFloat4(&v1); + const XMVECTOR x2 = XMLoadFloat4(&v2); + const XMVECTOR X = XMVectorLerp(x1, x2, t); + XMStoreFloat4(&result, X); +} + +inline Vector4 Vector4::SmoothStep(const Vector4& v1, const Vector4& v2, float t) noexcept +{ + using namespace DirectX; + t = (t > 1.0f) ? 1.0f : ((t < 0.0f) ? 0.0f : t); // Clamp value to 0 to 1 + t = t * t*(3.f - 2.f*t); + const XMVECTOR x1 = XMLoadFloat4(&v1); + const XMVECTOR x2 = XMLoadFloat4(&v2); + const XMVECTOR X = XMVectorLerp(x1, x2, t); + + Vector4 result; + XMStoreFloat4(&result, X); + return result; +} + +inline void Vector4::Barycentric(const Vector4& v1, const Vector4& v2, const Vector4& v3, float f, float g, Vector4& result) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat4(&v1); + const XMVECTOR x2 = XMLoadFloat4(&v2); + const XMVECTOR x3 = XMLoadFloat4(&v3); + const XMVECTOR X = XMVectorBaryCentric(x1, x2, x3, f, g); + XMStoreFloat4(&result, X); +} + +inline Vector4 Vector4::Barycentric(const Vector4& v1, const Vector4& v2, const Vector4& v3, float f, float g) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat4(&v1); + const XMVECTOR x2 = XMLoadFloat4(&v2); + const XMVECTOR x3 = XMLoadFloat4(&v3); + const XMVECTOR X = XMVectorBaryCentric(x1, x2, x3, f, g); + + Vector4 result; + XMStoreFloat4(&result, X); + return result; +} + +inline void Vector4::CatmullRom(const Vector4& v1, const Vector4& v2, const Vector4& v3, const Vector4& v4, float t, Vector4& result) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat4(&v1); + const XMVECTOR x2 = XMLoadFloat4(&v2); + const XMVECTOR x3 = XMLoadFloat4(&v3); + const XMVECTOR x4 = XMLoadFloat4(&v4); + const XMVECTOR X = XMVectorCatmullRom(x1, x2, x3, x4, t); + XMStoreFloat4(&result, X); +} + +inline Vector4 Vector4::CatmullRom(const Vector4& v1, const Vector4& v2, const Vector4& v3, const Vector4& v4, float t) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat4(&v1); + const XMVECTOR x2 = XMLoadFloat4(&v2); + const XMVECTOR x3 = XMLoadFloat4(&v3); + const XMVECTOR x4 = XMLoadFloat4(&v4); + const XMVECTOR X = XMVectorCatmullRom(x1, x2, x3, x4, t); + + Vector4 result; + XMStoreFloat4(&result, X); + return result; +} + +inline void Vector4::Hermite(const Vector4& v1, const Vector4& t1, const Vector4& v2, const Vector4& t2, float t, Vector4& result) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat4(&v1); + const XMVECTOR x2 = XMLoadFloat4(&t1); + const XMVECTOR x3 = XMLoadFloat4(&v2); + const XMVECTOR x4 = XMLoadFloat4(&t2); + const XMVECTOR X = XMVectorHermite(x1, x2, x3, x4, t); + XMStoreFloat4(&result, X); +} + +inline Vector4 Vector4::Hermite(const Vector4& v1, const Vector4& t1, const Vector4& v2, const Vector4& t2, float t) noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat4(&v1); + const XMVECTOR x2 = XMLoadFloat4(&t1); + const XMVECTOR x3 = XMLoadFloat4(&v2); + const XMVECTOR x4 = XMLoadFloat4(&t2); + const XMVECTOR X = XMVectorHermite(x1, x2, x3, x4, t); + + Vector4 result; + XMStoreFloat4(&result, X); + return result; +} + +inline void Vector4::Reflect(const Vector4& ivec, const Vector4& nvec, Vector4& result) noexcept +{ + using namespace DirectX; + const XMVECTOR i = XMLoadFloat4(&ivec); + const XMVECTOR n = XMLoadFloat4(&nvec); + const XMVECTOR X = XMVector4Reflect(i, n); + XMStoreFloat4(&result, X); +} + +inline Vector4 Vector4::Reflect(const Vector4& ivec, const Vector4& nvec) noexcept +{ + using namespace DirectX; + const XMVECTOR i = XMLoadFloat4(&ivec); + const XMVECTOR n = XMLoadFloat4(&nvec); + const XMVECTOR X = XMVector4Reflect(i, n); + + Vector4 result; + XMStoreFloat4(&result, X); + return result; +} + +inline void Vector4::Refract(const Vector4& ivec, const Vector4& nvec, float refractionIndex, Vector4& result) noexcept +{ + using namespace DirectX; + const XMVECTOR i = XMLoadFloat4(&ivec); + const XMVECTOR n = XMLoadFloat4(&nvec); + const XMVECTOR X = XMVector4Refract(i, n, refractionIndex); + XMStoreFloat4(&result, X); +} + +inline Vector4 Vector4::Refract(const Vector4& ivec, const Vector4& nvec, float refractionIndex) noexcept +{ + using namespace DirectX; + const XMVECTOR i = XMLoadFloat4(&ivec); + const XMVECTOR n = XMLoadFloat4(&nvec); + const XMVECTOR X = XMVector4Refract(i, n, refractionIndex); + + Vector4 result; + XMStoreFloat4(&result, X); + return result; +} + +inline void Vector4::Transform(const Vector2& v, const Quaternion& quat, Vector4& result) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(&v); + const XMVECTOR q = XMLoadFloat4(&quat); + XMVECTOR X = XMVector3Rotate(v1, q); + X = XMVectorSelect(g_XMIdentityR3, X, g_XMSelect1110); // result.w = 1.f + XMStoreFloat4(&result, X); +} + +inline Vector4 Vector4::Transform(const Vector2& v, const Quaternion& quat) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat2(&v); + const XMVECTOR q = XMLoadFloat4(&quat); + XMVECTOR X = XMVector3Rotate(v1, q); + X = XMVectorSelect(g_XMIdentityR3, X, g_XMSelect1110); // result.w = 1.f + + Vector4 result; + XMStoreFloat4(&result, X); + return result; +} + +inline void Vector4::Transform(const Vector3& v, const Quaternion& quat, Vector4& result) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(&v); + const XMVECTOR q = XMLoadFloat4(&quat); + XMVECTOR X = XMVector3Rotate(v1, q); + X = XMVectorSelect(g_XMIdentityR3, X, g_XMSelect1110); // result.w = 1.f + XMStoreFloat4(&result, X); +} + +inline Vector4 Vector4::Transform(const Vector3& v, const Quaternion& quat) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat3(&v); + const XMVECTOR q = XMLoadFloat4(&quat); + XMVECTOR X = XMVector3Rotate(v1, q); + X = XMVectorSelect(g_XMIdentityR3, X, g_XMSelect1110); // result.w = 1.f + + Vector4 result; + XMStoreFloat4(&result, X); + return result; +} + +inline void Vector4::Transform(const Vector4& v, const Quaternion& quat, Vector4& result) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(&v); + const XMVECTOR q = XMLoadFloat4(&quat); + XMVECTOR X = XMVector3Rotate(v1, q); + X = XMVectorSelect(v1, X, g_XMSelect1110); // result.w = v.w + XMStoreFloat4(&result, X); +} + +inline Vector4 Vector4::Transform(const Vector4& v, const Quaternion& quat) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(&v); + const XMVECTOR q = XMLoadFloat4(&quat); + XMVECTOR X = XMVector3Rotate(v1, q); + X = XMVectorSelect(v1, X, g_XMSelect1110); // result.w = v.w + + Vector4 result; + XMStoreFloat4(&result, X); + return result; +} + +inline void Vector4::Transform(const Vector4& v, const Matrix& m, Vector4& result) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(&v); + const XMMATRIX M = XMLoadFloat4x4(&m); + const XMVECTOR X = XMVector4Transform(v1, M); + XMStoreFloat4(&result, X); +} + +inline Vector4 Vector4::Transform(const Vector4& v, const Matrix& m) noexcept +{ + using namespace DirectX; + const XMVECTOR v1 = XMLoadFloat4(&v); + const XMMATRIX M = XMLoadFloat4x4(&m); + const XMVECTOR X = XMVector4Transform(v1, M); + + Vector4 result; + XMStoreFloat4(&result, X); + return result; +} + +_Use_decl_annotations_ +inline void Vector4::Transform(const Vector4* varray, size_t count, const Matrix& m, Vector4* resultArray) noexcept +{ + using namespace DirectX; + const XMMATRIX M = XMLoadFloat4x4(&m); + XMVector4TransformStream(resultArray, sizeof(XMFLOAT4), varray, sizeof(XMFLOAT4), count, M); +} + + +/**************************************************************************** + * + * Matrix + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ +// Comparision operators +//------------------------------------------------------------------------------ + +inline bool Matrix::operator == (const Matrix& M) const noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat4(reinterpret_cast(&_11)); + const XMVECTOR x2 = XMLoadFloat4(reinterpret_cast(&_21)); + const XMVECTOR x3 = XMLoadFloat4(reinterpret_cast(&_31)); + const XMVECTOR x4 = XMLoadFloat4(reinterpret_cast(&_41)); + + const XMVECTOR y1 = XMLoadFloat4(reinterpret_cast(&M._11)); + const XMVECTOR y2 = XMLoadFloat4(reinterpret_cast(&M._21)); + const XMVECTOR y3 = XMLoadFloat4(reinterpret_cast(&M._31)); + const XMVECTOR y4 = XMLoadFloat4(reinterpret_cast(&M._41)); + + return (XMVector4Equal(x1, y1) + && XMVector4Equal(x2, y2) + && XMVector4Equal(x3, y3) + && XMVector4Equal(x4, y4)) != 0; +} + +inline bool Matrix::operator != (const Matrix& M) const noexcept +{ + using namespace DirectX; + const XMVECTOR x1 = XMLoadFloat4(reinterpret_cast(&_11)); + const XMVECTOR x2 = XMLoadFloat4(reinterpret_cast(&_21)); + const XMVECTOR x3 = XMLoadFloat4(reinterpret_cast(&_31)); + const XMVECTOR x4 = XMLoadFloat4(reinterpret_cast(&_41)); + + const XMVECTOR y1 = XMLoadFloat4(reinterpret_cast(&M._11)); + const XMVECTOR y2 = XMLoadFloat4(reinterpret_cast(&M._21)); + const XMVECTOR y3 = XMLoadFloat4(reinterpret_cast(&M._31)); + const XMVECTOR y4 = XMLoadFloat4(reinterpret_cast(&M._41)); + + return (XMVector4NotEqual(x1, y1) + || XMVector4NotEqual(x2, y2) + || XMVector4NotEqual(x3, y3) + || XMVector4NotEqual(x4, y4)) != 0; +} + +//------------------------------------------------------------------------------ +// Assignment operators +//------------------------------------------------------------------------------ + +inline Matrix::Matrix(const XMFLOAT3X3& M) noexcept +{ + _11 = M._11; _12 = M._12; _13 = M._13; _14 = 0.f; + _21 = M._21; _22 = M._22; _23 = M._23; _24 = 0.f; + _31 = M._31; _32 = M._32; _33 = M._33; _34 = 0.f; + _41 = 0.f; _42 = 0.f; _43 = 0.f; _44 = 1.f; +} + +inline Matrix::Matrix(const XMFLOAT4X3& M) noexcept +{ + _11 = M._11; _12 = M._12; _13 = M._13; _14 = 0.f; + _21 = M._21; _22 = M._22; _23 = M._23; _24 = 0.f; + _31 = M._31; _32 = M._32; _33 = M._33; _34 = 0.f; + _41 = M._41; _42 = M._42; _43 = M._43; _44 = 1.f; +} + +inline Matrix& Matrix::operator= (const XMFLOAT3X3& M) noexcept +{ + _11 = M._11; _12 = M._12; _13 = M._13; _14 = 0.f; + _21 = M._21; _22 = M._22; _23 = M._23; _24 = 0.f; + _31 = M._31; _32 = M._32; _33 = M._33; _34 = 0.f; + _41 = 0.f; _42 = 0.f; _43 = 0.f; _44 = 1.f; + return *this; +} + +inline Matrix& Matrix::operator= (const XMFLOAT4X3& M) noexcept +{ + _11 = M._11; _12 = M._12; _13 = M._13; _14 = 0.f; + _21 = M._21; _22 = M._22; _23 = M._23; _24 = 0.f; + _31 = M._31; _32 = M._32; _33 = M._33; _34 = 0.f; + _41 = M._41; _42 = M._42; _43 = M._43; _44 = 1.f; + return *this; +} + +inline Matrix& Matrix::operator+= (const Matrix& M) noexcept +{ + using namespace DirectX; + XMVECTOR x1 = XMLoadFloat4(reinterpret_cast(&_11)); + XMVECTOR x2 = XMLoadFloat4(reinterpret_cast(&_21)); + XMVECTOR x3 = XMLoadFloat4(reinterpret_cast(&_31)); + XMVECTOR x4 = XMLoadFloat4(reinterpret_cast(&_41)); + + const XMVECTOR y1 = XMLoadFloat4(reinterpret_cast(&M._11)); + const XMVECTOR y2 = XMLoadFloat4(reinterpret_cast(&M._21)); + const XMVECTOR y3 = XMLoadFloat4(reinterpret_cast(&M._31)); + const XMVECTOR y4 = XMLoadFloat4(reinterpret_cast(&M._41)); + + x1 = XMVectorAdd(x1, y1); + x2 = XMVectorAdd(x2, y2); + x3 = XMVectorAdd(x3, y3); + x4 = XMVectorAdd(x4, y4); + + XMStoreFloat4(reinterpret_cast(&_11), x1); + XMStoreFloat4(reinterpret_cast(&_21), x2); + XMStoreFloat4(reinterpret_cast(&_31), x3); + XMStoreFloat4(reinterpret_cast(&_41), x4); + return *this; +} + +inline Matrix& Matrix::operator-= (const Matrix& M) noexcept +{ + using namespace DirectX; + XMVECTOR x1 = XMLoadFloat4(reinterpret_cast(&_11)); + XMVECTOR x2 = XMLoadFloat4(reinterpret_cast(&_21)); + XMVECTOR x3 = XMLoadFloat4(reinterpret_cast(&_31)); + XMVECTOR x4 = XMLoadFloat4(reinterpret_cast(&_41)); + + const XMVECTOR y1 = XMLoadFloat4(reinterpret_cast(&M._11)); + const XMVECTOR y2 = XMLoadFloat4(reinterpret_cast(&M._21)); + const XMVECTOR y3 = XMLoadFloat4(reinterpret_cast(&M._31)); + const XMVECTOR y4 = XMLoadFloat4(reinterpret_cast(&M._41)); + + x1 = XMVectorSubtract(x1, y1); + x2 = XMVectorSubtract(x2, y2); + x3 = XMVectorSubtract(x3, y3); + x4 = XMVectorSubtract(x4, y4); + + XMStoreFloat4(reinterpret_cast(&_11), x1); + XMStoreFloat4(reinterpret_cast(&_21), x2); + XMStoreFloat4(reinterpret_cast(&_31), x3); + XMStoreFloat4(reinterpret_cast(&_41), x4); + return *this; +} + +inline Matrix& Matrix::operator*= (const Matrix& M) noexcept +{ + using namespace DirectX; + const XMMATRIX M1 = XMLoadFloat4x4(this); + const XMMATRIX M2 = XMLoadFloat4x4(&M); + const XMMATRIX X = XMMatrixMultiply(M1, M2); + XMStoreFloat4x4(this, X); + return *this; +} + +inline Matrix& Matrix::operator*= (float S) noexcept +{ + using namespace DirectX; + XMVECTOR x1 = XMLoadFloat4(reinterpret_cast(&_11)); + XMVECTOR x2 = XMLoadFloat4(reinterpret_cast(&_21)); + XMVECTOR x3 = XMLoadFloat4(reinterpret_cast(&_31)); + XMVECTOR x4 = XMLoadFloat4(reinterpret_cast(&_41)); + + x1 = XMVectorScale(x1, S); + x2 = XMVectorScale(x2, S); + x3 = XMVectorScale(x3, S); + x4 = XMVectorScale(x4, S); + + XMStoreFloat4(reinterpret_cast(&_11), x1); + XMStoreFloat4(reinterpret_cast(&_21), x2); + XMStoreFloat4(reinterpret_cast(&_31), x3); + XMStoreFloat4(reinterpret_cast(&_41), x4); + return *this; +} + +inline Matrix& Matrix::operator/= (float S) noexcept +{ + using namespace DirectX; + assert(S != 0.f); + XMVECTOR x1 = XMLoadFloat4(reinterpret_cast(&_11)); + XMVECTOR x2 = XMLoadFloat4(reinterpret_cast(&_21)); + XMVECTOR x3 = XMLoadFloat4(reinterpret_cast(&_31)); + XMVECTOR x4 = XMLoadFloat4(reinterpret_cast(&_41)); + + const float rs = 1.f / S; + + x1 = XMVectorScale(x1, rs); + x2 = XMVectorScale(x2, rs); + x3 = XMVectorScale(x3, rs); + x4 = XMVectorScale(x4, rs); + + XMStoreFloat4(reinterpret_cast(&_11), x1); + XMStoreFloat4(reinterpret_cast(&_21), x2); + XMStoreFloat4(reinterpret_cast(&_31), x3); + XMStoreFloat4(reinterpret_cast(&_41), x4); + return *this; +} + +inline Matrix& Matrix::operator/= (const Matrix& M) noexcept +{ + using namespace DirectX; + XMVECTOR x1 = XMLoadFloat4(reinterpret_cast(&_11)); + XMVECTOR x2 = XMLoadFloat4(reinterpret_cast(&_21)); + XMVECTOR x3 = XMLoadFloat4(reinterpret_cast(&_31)); + XMVECTOR x4 = XMLoadFloat4(reinterpret_cast(&_41)); + + const XMVECTOR y1 = XMLoadFloat4(reinterpret_cast(&M._11)); + const XMVECTOR y2 = XMLoadFloat4(reinterpret_cast(&M._21)); + const XMVECTOR y3 = XMLoadFloat4(reinterpret_cast(&M._31)); + const XMVECTOR y4 = XMLoadFloat4(reinterpret_cast(&M._41)); + + x1 = XMVectorDivide(x1, y1); + x2 = XMVectorDivide(x2, y2); + x3 = XMVectorDivide(x3, y3); + x4 = XMVectorDivide(x4, y4); + + XMStoreFloat4(reinterpret_cast(&_11), x1); + XMStoreFloat4(reinterpret_cast(&_21), x2); + XMStoreFloat4(reinterpret_cast(&_31), x3); + XMStoreFloat4(reinterpret_cast(&_41), x4); + return *this; +} + +//------------------------------------------------------------------------------ +// Urnary operators +//------------------------------------------------------------------------------ + +inline Matrix Matrix::operator- () const noexcept +{ + using namespace DirectX; + XMVECTOR v1 = XMLoadFloat4(reinterpret_cast(&_11)); + XMVECTOR v2 = XMLoadFloat4(reinterpret_cast(&_21)); + XMVECTOR v3 = XMLoadFloat4(reinterpret_cast(&_31)); + XMVECTOR v4 = XMLoadFloat4(reinterpret_cast(&_41)); + + v1 = XMVectorNegate(v1); + v2 = XMVectorNegate(v2); + v3 = XMVectorNegate(v3); + v4 = XMVectorNegate(v4); + + Matrix R; + XMStoreFloat4(reinterpret_cast(&R._11), v1); + XMStoreFloat4(reinterpret_cast(&R._21), v2); + XMStoreFloat4(reinterpret_cast(&R._31), v3); + XMStoreFloat4(reinterpret_cast(&R._41), v4); + return R; +} + +//------------------------------------------------------------------------------ +// Binary operators +//------------------------------------------------------------------------------ + +inline Matrix operator+ (const Matrix& M1, const Matrix& M2) noexcept +{ + using namespace DirectX; + XMVECTOR x1 = XMLoadFloat4(reinterpret_cast(&M1._11)); + XMVECTOR x2 = XMLoadFloat4(reinterpret_cast(&M1._21)); + XMVECTOR x3 = XMLoadFloat4(reinterpret_cast(&M1._31)); + XMVECTOR x4 = XMLoadFloat4(reinterpret_cast(&M1._41)); + + const XMVECTOR y1 = XMLoadFloat4(reinterpret_cast(&M2._11)); + const XMVECTOR y2 = XMLoadFloat4(reinterpret_cast(&M2._21)); + const XMVECTOR y3 = XMLoadFloat4(reinterpret_cast(&M2._31)); + const XMVECTOR y4 = XMLoadFloat4(reinterpret_cast(&M2._41)); + + x1 = XMVectorAdd(x1, y1); + x2 = XMVectorAdd(x2, y2); + x3 = XMVectorAdd(x3, y3); + x4 = XMVectorAdd(x4, y4); + + Matrix R; + XMStoreFloat4(reinterpret_cast(&R._11), x1); + XMStoreFloat4(reinterpret_cast(&R._21), x2); + XMStoreFloat4(reinterpret_cast(&R._31), x3); + XMStoreFloat4(reinterpret_cast(&R._41), x4); + return R; +} + +inline Matrix operator- (const Matrix& M1, const Matrix& M2) noexcept +{ + using namespace DirectX; + XMVECTOR x1 = XMLoadFloat4(reinterpret_cast(&M1._11)); + XMVECTOR x2 = XMLoadFloat4(reinterpret_cast(&M1._21)); + XMVECTOR x3 = XMLoadFloat4(reinterpret_cast(&M1._31)); + XMVECTOR x4 = XMLoadFloat4(reinterpret_cast(&M1._41)); + + const XMVECTOR y1 = XMLoadFloat4(reinterpret_cast(&M2._11)); + const XMVECTOR y2 = XMLoadFloat4(reinterpret_cast(&M2._21)); + const XMVECTOR y3 = XMLoadFloat4(reinterpret_cast(&M2._31)); + const XMVECTOR y4 = XMLoadFloat4(reinterpret_cast(&M2._41)); + + x1 = XMVectorSubtract(x1, y1); + x2 = XMVectorSubtract(x2, y2); + x3 = XMVectorSubtract(x3, y3); + x4 = XMVectorSubtract(x4, y4); + + Matrix R; + XMStoreFloat4(reinterpret_cast(&R._11), x1); + XMStoreFloat4(reinterpret_cast(&R._21), x2); + XMStoreFloat4(reinterpret_cast(&R._31), x3); + XMStoreFloat4(reinterpret_cast(&R._41), x4); + return R; +} + +inline Matrix operator* (const Matrix& M1, const Matrix& M2) noexcept +{ + using namespace DirectX; + const XMMATRIX m1 = XMLoadFloat4x4(&M1); + const XMMATRIX m2 = XMLoadFloat4x4(&M2); + const XMMATRIX X = XMMatrixMultiply(m1, m2); + + Matrix R; + XMStoreFloat4x4(&R, X); + return R; +} + +inline Matrix operator* (const Matrix& M, float S) noexcept +{ + using namespace DirectX; + XMVECTOR x1 = XMLoadFloat4(reinterpret_cast(&M._11)); + XMVECTOR x2 = XMLoadFloat4(reinterpret_cast(&M._21)); + XMVECTOR x3 = XMLoadFloat4(reinterpret_cast(&M._31)); + XMVECTOR x4 = XMLoadFloat4(reinterpret_cast(&M._41)); + + x1 = XMVectorScale(x1, S); + x2 = XMVectorScale(x2, S); + x3 = XMVectorScale(x3, S); + x4 = XMVectorScale(x4, S); + + Matrix R; + XMStoreFloat4(reinterpret_cast(&R._11), x1); + XMStoreFloat4(reinterpret_cast(&R._21), x2); + XMStoreFloat4(reinterpret_cast(&R._31), x3); + XMStoreFloat4(reinterpret_cast(&R._41), x4); + return R; +} + +inline Matrix operator/ (const Matrix& M, float S) noexcept +{ + using namespace DirectX; + assert(S != 0.f); + + XMVECTOR x1 = XMLoadFloat4(reinterpret_cast(&M._11)); + XMVECTOR x2 = XMLoadFloat4(reinterpret_cast(&M._21)); + XMVECTOR x3 = XMLoadFloat4(reinterpret_cast(&M._31)); + XMVECTOR x4 = XMLoadFloat4(reinterpret_cast(&M._41)); + + const float rs = 1.f / S; + + x1 = XMVectorScale(x1, rs); + x2 = XMVectorScale(x2, rs); + x3 = XMVectorScale(x3, rs); + x4 = XMVectorScale(x4, rs); + + Matrix R; + XMStoreFloat4(reinterpret_cast(&R._11), x1); + XMStoreFloat4(reinterpret_cast(&R._21), x2); + XMStoreFloat4(reinterpret_cast(&R._31), x3); + XMStoreFloat4(reinterpret_cast(&R._41), x4); + return R; +} + +inline Matrix operator/ (const Matrix& M1, const Matrix& M2) noexcept +{ + using namespace DirectX; + XMVECTOR x1 = XMLoadFloat4(reinterpret_cast(&M1._11)); + XMVECTOR x2 = XMLoadFloat4(reinterpret_cast(&M1._21)); + XMVECTOR x3 = XMLoadFloat4(reinterpret_cast(&M1._31)); + XMVECTOR x4 = XMLoadFloat4(reinterpret_cast(&M1._41)); + + const XMVECTOR y1 = XMLoadFloat4(reinterpret_cast(&M2._11)); + const XMVECTOR y2 = XMLoadFloat4(reinterpret_cast(&M2._21)); + const XMVECTOR y3 = XMLoadFloat4(reinterpret_cast(&M2._31)); + const XMVECTOR y4 = XMLoadFloat4(reinterpret_cast(&M2._41)); + + x1 = XMVectorDivide(x1, y1); + x2 = XMVectorDivide(x2, y2); + x3 = XMVectorDivide(x3, y3); + x4 = XMVectorDivide(x4, y4); + + Matrix R; + XMStoreFloat4(reinterpret_cast(&R._11), x1); + XMStoreFloat4(reinterpret_cast(&R._21), x2); + XMStoreFloat4(reinterpret_cast(&R._31), x3); + XMStoreFloat4(reinterpret_cast(&R._41), x4); + return R; +} + +inline Matrix operator* (float S, const Matrix& M) noexcept +{ + using namespace DirectX; + + XMVECTOR x1 = XMLoadFloat4(reinterpret_cast(&M._11)); + XMVECTOR x2 = XMLoadFloat4(reinterpret_cast(&M._21)); + XMVECTOR x3 = XMLoadFloat4(reinterpret_cast(&M._31)); + XMVECTOR x4 = XMLoadFloat4(reinterpret_cast(&M._41)); + + x1 = XMVectorScale(x1, S); + x2 = XMVectorScale(x2, S); + x3 = XMVectorScale(x3, S); + x4 = XMVectorScale(x4, S); + + Matrix R; + XMStoreFloat4(reinterpret_cast(&R._11), x1); + XMStoreFloat4(reinterpret_cast(&R._21), x2); + XMStoreFloat4(reinterpret_cast(&R._31), x3); + XMStoreFloat4(reinterpret_cast(&R._41), x4); + return R; +} + +//------------------------------------------------------------------------------ +// Matrix operations +//------------------------------------------------------------------------------ + +inline bool Matrix::Decompose(Vector3& scale, Quaternion& rotation, Vector3& translation) noexcept +{ + using namespace DirectX; + + XMVECTOR s, r, t; + + if (!XMMatrixDecompose(&s, &r, &t, *this)) + return false; + + XMStoreFloat3(&scale, s); + XMStoreFloat4(&rotation, r); + XMStoreFloat3(&translation, t); + + return true; +} + +inline Matrix Matrix::Transpose() const noexcept +{ + using namespace DirectX; + const XMMATRIX M = XMLoadFloat4x4(this); + Matrix R; + XMStoreFloat4x4(&R, XMMatrixTranspose(M)); + return R; +} + +inline void Matrix::Transpose(Matrix& result) const noexcept +{ + using namespace DirectX; + const XMMATRIX M = XMLoadFloat4x4(this); + XMStoreFloat4x4(&result, XMMatrixTranspose(M)); +} + +inline Matrix Matrix::Invert() const noexcept +{ + using namespace DirectX; + const XMMATRIX M = XMLoadFloat4x4(this); + Matrix R; + XMVECTOR det; + XMStoreFloat4x4(&R, XMMatrixInverse(&det, M)); + return R; +} + +inline void Matrix::Invert(Matrix& result) const noexcept +{ + using namespace DirectX; + const XMMATRIX M = XMLoadFloat4x4(this); + XMVECTOR det; + XMStoreFloat4x4(&result, XMMatrixInverse(&det, M)); +} + +inline float Matrix::Determinant() const noexcept +{ + using namespace DirectX; + const XMMATRIX M = XMLoadFloat4x4(this); + return XMVectorGetX(XMMatrixDeterminant(M)); +} + +inline Vector3 Matrix::ToEuler() const noexcept +{ + const float cy = sqrtf(_33 * _33 + _31 * _31); + const float cx = atan2f(-_32, cy); + if (cy > 16.f * FLT_EPSILON) + { + return Vector3(cx, atan2f(_31, _33), atan2f(_12, _22)); + } + else + { + return Vector3(cx, 0.f, atan2f(-_21, _11)); + } +} + +//------------------------------------------------------------------------------ +// Static functions +//------------------------------------------------------------------------------ + +_Use_decl_annotations_ +inline Matrix Matrix::CreateBillboard( + const Vector3& object, + const Vector3& cameraPosition, + const Vector3& cameraUp, + const Vector3* cameraForward) noexcept +{ + using namespace DirectX; + const XMVECTOR O = XMLoadFloat3(&object); + const XMVECTOR C = XMLoadFloat3(&cameraPosition); + XMVECTOR Z = XMVectorSubtract(O, C); + + const XMVECTOR N = XMVector3LengthSq(Z); + if (XMVector3Less(N, g_XMEpsilon)) + { + if (cameraForward) + { + const XMVECTOR F = XMLoadFloat3(cameraForward); + Z = XMVectorNegate(F); + } + else + Z = g_XMNegIdentityR2; + } + else + { + Z = XMVector3Normalize(Z); + } + + const XMVECTOR up = XMLoadFloat3(&cameraUp); + XMVECTOR X = XMVector3Cross(up, Z); + X = XMVector3Normalize(X); + + const XMVECTOR Y = XMVector3Cross(Z, X); + + XMMATRIX M; + M.r[0] = X; + M.r[1] = Y; + M.r[2] = Z; + M.r[3] = XMVectorSetW(O, 1.f); + + Matrix R; + XMStoreFloat4x4(&R, M); + return R; +} + +_Use_decl_annotations_ +inline Matrix Matrix::CreateConstrainedBillboard( + const Vector3& object, + const Vector3& cameraPosition, + const Vector3& rotateAxis, + const Vector3* cameraForward, + const Vector3* objectForward) noexcept +{ + using namespace DirectX; + + static const XMVECTORF32 s_minAngle = { { { 0.99825467075f, 0.99825467075f, 0.99825467075f, 0.99825467075f } } }; // 1.0 - XMConvertToRadians( 0.1f ); + + const XMVECTOR O = XMLoadFloat3(&object); + const XMVECTOR C = XMLoadFloat3(&cameraPosition); + XMVECTOR faceDir = XMVectorSubtract(O, C); + + const XMVECTOR N = XMVector3LengthSq(faceDir); + if (XMVector3Less(N, g_XMEpsilon)) + { + if (cameraForward) + { + const XMVECTOR F = XMLoadFloat3(cameraForward); + faceDir = XMVectorNegate(F); + } + else + faceDir = g_XMNegIdentityR2; + } + else + { + faceDir = XMVector3Normalize(faceDir); + } + + const XMVECTOR Y = XMLoadFloat3(&rotateAxis); + XMVECTOR X, Z; + + XMVECTOR dot = XMVectorAbs(XMVector3Dot(Y, faceDir)); + if (XMVector3Greater(dot, s_minAngle)) + { + if (objectForward) + { + Z = XMLoadFloat3(objectForward); + dot = XMVectorAbs(XMVector3Dot(Y, Z)); + if (XMVector3Greater(dot, s_minAngle)) + { + dot = XMVectorAbs(XMVector3Dot(Y, g_XMNegIdentityR2)); + Z = (XMVector3Greater(dot, s_minAngle)) ? g_XMIdentityR0 : g_XMNegIdentityR2; + } + } + else + { + dot = XMVectorAbs(XMVector3Dot(Y, g_XMNegIdentityR2)); + Z = (XMVector3Greater(dot, s_minAngle)) ? g_XMIdentityR0 : g_XMNegIdentityR2; + } + + X = XMVector3Cross(Y, Z); + X = XMVector3Normalize(X); + + Z = XMVector3Cross(X, Y); + Z = XMVector3Normalize(Z); + } + else + { + X = XMVector3Cross(Y, faceDir); + X = XMVector3Normalize(X); + + Z = XMVector3Cross(X, Y); + Z = XMVector3Normalize(Z); + } + + XMMATRIX M; + M.r[0] = X; + M.r[1] = Y; + M.r[2] = Z; + M.r[3] = XMVectorSetW(O, 1.f); + + Matrix R; + XMStoreFloat4x4(&R, M); + return R; +} + +inline Matrix Matrix::CreateTranslation(const Vector3& position) noexcept +{ + using namespace DirectX; + Matrix R; + XMStoreFloat4x4(&R, XMMatrixTranslation(position.x, position.y, position.z)); + return R; +} + +inline Matrix Matrix::CreateTranslation(float x, float y, float z) noexcept +{ + using namespace DirectX; + Matrix R; + XMStoreFloat4x4(&R, XMMatrixTranslation(x, y, z)); + return R; +} + +inline Matrix Matrix::CreateScale(const Vector3& scales) noexcept +{ + using namespace DirectX; + Matrix R; + XMStoreFloat4x4(&R, XMMatrixScaling(scales.x, scales.y, scales.z)); + return R; +} + +inline Matrix Matrix::CreateScale(float xs, float ys, float zs) noexcept +{ + using namespace DirectX; + Matrix R; + XMStoreFloat4x4(&R, XMMatrixScaling(xs, ys, zs)); + return R; +} + +inline Matrix Matrix::CreateScale(float scale) noexcept +{ + using namespace DirectX; + Matrix R; + XMStoreFloat4x4(&R, XMMatrixScaling(scale, scale, scale)); + return R; +} + +inline Matrix Matrix::CreateRotationX(float radians) noexcept +{ + using namespace DirectX; + Matrix R; + XMStoreFloat4x4(&R, XMMatrixRotationX(radians)); + return R; +} + +inline Matrix Matrix::CreateRotationY(float radians) noexcept +{ + using namespace DirectX; + Matrix R; + XMStoreFloat4x4(&R, XMMatrixRotationY(radians)); + return R; +} + +inline Matrix Matrix::CreateRotationZ(float radians) noexcept +{ + using namespace DirectX; + Matrix R; + XMStoreFloat4x4(&R, XMMatrixRotationZ(radians)); + return R; +} + +inline Matrix Matrix::CreateFromAxisAngle(const Vector3& axis, float angle) noexcept +{ + using namespace DirectX; + Matrix R; + const XMVECTOR a = XMLoadFloat3(&axis); + XMStoreFloat4x4(&R, XMMatrixRotationAxis(a, angle)); + return R; +} + +inline Matrix Matrix::CreatePerspectiveFieldOfView(float fov, float aspectRatio, float nearPlane, float farPlane) noexcept +{ + using namespace DirectX; + Matrix R; + XMStoreFloat4x4(&R, XMMatrixPerspectiveFovRH(fov, aspectRatio, nearPlane, farPlane)); + return R; +} + +inline Matrix Matrix::CreatePerspective(float width, float height, float nearPlane, float farPlane) noexcept +{ + using namespace DirectX; + Matrix R; + XMStoreFloat4x4(&R, XMMatrixPerspectiveRH(width, height, nearPlane, farPlane)); + return R; +} + +inline Matrix Matrix::CreatePerspectiveOffCenter(float left, float right, float bottom, float top, float nearPlane, float farPlane) noexcept +{ + using namespace DirectX; + Matrix R; + XMStoreFloat4x4(&R, XMMatrixPerspectiveOffCenterRH(left, right, bottom, top, nearPlane, farPlane)); + return R; +} + +inline Matrix Matrix::CreateOrthographic(float width, float height, float zNearPlane, float zFarPlane) noexcept +{ + using namespace DirectX; + Matrix R; + XMStoreFloat4x4(&R, XMMatrixOrthographicRH(width, height, zNearPlane, zFarPlane)); + return R; +} + +inline Matrix Matrix::CreateOrthographicOffCenter(float left, float right, float bottom, float top, float zNearPlane, float zFarPlane) noexcept +{ + using namespace DirectX; + Matrix R; + XMStoreFloat4x4(&R, XMMatrixOrthographicOffCenterRH(left, right, bottom, top, zNearPlane, zFarPlane)); + return R; +} + +inline Matrix Matrix::CreateLookAt(const Vector3& eye, const Vector3& target, const Vector3& up) noexcept +{ + using namespace DirectX; + Matrix R; + const XMVECTOR eyev = XMLoadFloat3(&eye); + const XMVECTOR targetv = XMLoadFloat3(&target); + const XMVECTOR upv = XMLoadFloat3(&up); + XMStoreFloat4x4(&R, XMMatrixLookAtRH(eyev, targetv, upv)); + return R; +} + +inline Matrix Matrix::CreateWorld(const Vector3& position, const Vector3& forward, const Vector3& up) noexcept +{ + using namespace DirectX; + const XMVECTOR zaxis = XMVector3Normalize(XMVectorNegate(XMLoadFloat3(&forward))); + XMVECTOR yaxis = XMLoadFloat3(&up); + const XMVECTOR xaxis = XMVector3Normalize(XMVector3Cross(yaxis, zaxis)); + yaxis = XMVector3Cross(zaxis, xaxis); + + Matrix R; + XMStoreFloat3(reinterpret_cast(&R._11), xaxis); + XMStoreFloat3(reinterpret_cast(&R._21), yaxis); + XMStoreFloat3(reinterpret_cast(&R._31), zaxis); + R._14 = R._24 = R._34 = 0.f; + R._41 = position.x; R._42 = position.y; R._43 = position.z; + R._44 = 1.f; + return R; +} + +inline Matrix Matrix::CreateFromQuaternion(const Quaternion& rotation) noexcept +{ + using namespace DirectX; + const XMVECTOR quatv = XMLoadFloat4(&rotation); + Matrix R; + XMStoreFloat4x4(&R, XMMatrixRotationQuaternion(quatv)); + return R; +} + +inline Matrix Matrix::CreateFromYawPitchRoll(float yaw, float pitch, float roll) noexcept +{ + using namespace DirectX; + Matrix R; + XMStoreFloat4x4(&R, XMMatrixRotationRollPitchYaw(pitch, yaw, roll)); + return R; +} + +inline Matrix Matrix::CreateFromYawPitchRoll(const Vector3& angles) noexcept +{ + using namespace DirectX; + Matrix R; + XMStoreFloat4x4(&R, XMMatrixRotationRollPitchYawFromVector(angles)); + return R; +} + +inline Matrix Matrix::CreateShadow(const Vector3& lightDir, const Plane& plane) noexcept +{ + using namespace DirectX; + const XMVECTOR light = XMLoadFloat3(&lightDir); + const XMVECTOR planev = XMLoadFloat4(&plane); + Matrix R; + XMStoreFloat4x4(&R, XMMatrixShadow(planev, light)); + return R; +} + +inline Matrix Matrix::CreateReflection(const Plane& plane) noexcept +{ + using namespace DirectX; + const XMVECTOR planev = XMLoadFloat4(&plane); + Matrix R; + XMStoreFloat4x4(&R, XMMatrixReflect(planev)); + return R; +} + +inline void Matrix::Lerp(const Matrix& M1, const Matrix& M2, float t, Matrix& result) noexcept +{ + using namespace DirectX; + XMVECTOR x1 = XMLoadFloat4(reinterpret_cast(&M1._11)); + XMVECTOR x2 = XMLoadFloat4(reinterpret_cast(&M1._21)); + XMVECTOR x3 = XMLoadFloat4(reinterpret_cast(&M1._31)); + XMVECTOR x4 = XMLoadFloat4(reinterpret_cast(&M1._41)); + + const XMVECTOR y1 = XMLoadFloat4(reinterpret_cast(&M2._11)); + const XMVECTOR y2 = XMLoadFloat4(reinterpret_cast(&M2._21)); + const XMVECTOR y3 = XMLoadFloat4(reinterpret_cast(&M2._31)); + const XMVECTOR y4 = XMLoadFloat4(reinterpret_cast(&M2._41)); + + x1 = XMVectorLerp(x1, y1, t); + x2 = XMVectorLerp(x2, y2, t); + x3 = XMVectorLerp(x3, y3, t); + x4 = XMVectorLerp(x4, y4, t); + + XMStoreFloat4(reinterpret_cast(&result._11), x1); + XMStoreFloat4(reinterpret_cast(&result._21), x2); + XMStoreFloat4(reinterpret_cast(&result._31), x3); + XMStoreFloat4(reinterpret_cast(&result._41), x4); +} + +inline Matrix Matrix::Lerp(const Matrix& M1, const Matrix& M2, float t) noexcept +{ + using namespace DirectX; + XMVECTOR x1 = XMLoadFloat4(reinterpret_cast(&M1._11)); + XMVECTOR x2 = XMLoadFloat4(reinterpret_cast(&M1._21)); + XMVECTOR x3 = XMLoadFloat4(reinterpret_cast(&M1._31)); + XMVECTOR x4 = XMLoadFloat4(reinterpret_cast(&M1._41)); + + const XMVECTOR y1 = XMLoadFloat4(reinterpret_cast(&M2._11)); + const XMVECTOR y2 = XMLoadFloat4(reinterpret_cast(&M2._21)); + const XMVECTOR y3 = XMLoadFloat4(reinterpret_cast(&M2._31)); + const XMVECTOR y4 = XMLoadFloat4(reinterpret_cast(&M2._41)); + + x1 = XMVectorLerp(x1, y1, t); + x2 = XMVectorLerp(x2, y2, t); + x3 = XMVectorLerp(x3, y3, t); + x4 = XMVectorLerp(x4, y4, t); + + Matrix result; + XMStoreFloat4(reinterpret_cast(&result._11), x1); + XMStoreFloat4(reinterpret_cast(&result._21), x2); + XMStoreFloat4(reinterpret_cast(&result._31), x3); + XMStoreFloat4(reinterpret_cast(&result._41), x4); + return result; +} + +inline void Matrix::Transform(const Matrix& M, const Quaternion& rotation, Matrix& result) noexcept +{ + using namespace DirectX; + const XMVECTOR quatv = XMLoadFloat4(&rotation); + + const XMMATRIX M0 = XMLoadFloat4x4(&M); + const XMMATRIX M1 = XMMatrixRotationQuaternion(quatv); + + XMStoreFloat4x4(&result, XMMatrixMultiply(M0, M1)); +} + +inline Matrix Matrix::Transform(const Matrix& M, const Quaternion& rotation) noexcept +{ + using namespace DirectX; + const XMVECTOR quatv = XMLoadFloat4(&rotation); + + const XMMATRIX M0 = XMLoadFloat4x4(&M); + const XMMATRIX M1 = XMMatrixRotationQuaternion(quatv); + + Matrix result; + XMStoreFloat4x4(&result, XMMatrixMultiply(M0, M1)); + return result; +} + + +/**************************************************************************** + * + * Plane + * + ****************************************************************************/ + +inline Plane::Plane(const Vector3& point1, const Vector3& point2, const Vector3& point3) noexcept +{ + using namespace DirectX; + const XMVECTOR P0 = XMLoadFloat3(&point1); + const XMVECTOR P1 = XMLoadFloat3(&point2); + const XMVECTOR P2 = XMLoadFloat3(&point3); + XMStoreFloat4(this, XMPlaneFromPoints(P0, P1, P2)); +} + +inline Plane::Plane(const Vector3& point, const Vector3& normal) noexcept +{ + using namespace DirectX; + const XMVECTOR P = XMLoadFloat3(&point); + const XMVECTOR N = XMLoadFloat3(&normal); + XMStoreFloat4(this, XMPlaneFromPointNormal(P, N)); +} + +//------------------------------------------------------------------------------ +// Comparision operators +//------------------------------------------------------------------------------ + +inline bool Plane::operator == (const Plane& p) const noexcept +{ + using namespace DirectX; + const XMVECTOR p1 = XMLoadFloat4(this); + const XMVECTOR p2 = XMLoadFloat4(&p); + return XMPlaneEqual(p1, p2); +} + +inline bool Plane::operator != (const Plane& p) const noexcept +{ + using namespace DirectX; + const XMVECTOR p1 = XMLoadFloat4(this); + const XMVECTOR p2 = XMLoadFloat4(&p); + return XMPlaneNotEqual(p1, p2); +} + +//------------------------------------------------------------------------------ +// Plane operations +//------------------------------------------------------------------------------ + +inline void Plane::Normalize() noexcept +{ + using namespace DirectX; + const XMVECTOR p = XMLoadFloat4(this); + XMStoreFloat4(this, XMPlaneNormalize(p)); +} + +inline void Plane::Normalize(Plane& result) const noexcept +{ + using namespace DirectX; + const XMVECTOR p = XMLoadFloat4(this); + XMStoreFloat4(&result, XMPlaneNormalize(p)); +} + +inline float Plane::Dot(const Vector4& v) const noexcept +{ + using namespace DirectX; + const XMVECTOR p = XMLoadFloat4(this); + const XMVECTOR v0 = XMLoadFloat4(&v); + return XMVectorGetX(XMPlaneDot(p, v0)); +} + +inline float Plane::DotCoordinate(const Vector3& position) const noexcept +{ + using namespace DirectX; + const XMVECTOR p = XMLoadFloat4(this); + const XMVECTOR v0 = XMLoadFloat3(&position); + return XMVectorGetX(XMPlaneDotCoord(p, v0)); +} + +inline float Plane::DotNormal(const Vector3& normal) const noexcept +{ + using namespace DirectX; + const XMVECTOR p = XMLoadFloat4(this); + const XMVECTOR n0 = XMLoadFloat3(&normal); + return XMVectorGetX(XMPlaneDotNormal(p, n0)); +} + +//------------------------------------------------------------------------------ +// Static functions +//------------------------------------------------------------------------------ + +inline void Plane::Transform(const Plane& plane, const Matrix& M, Plane& result) noexcept +{ + using namespace DirectX; + const XMVECTOR p = XMLoadFloat4(&plane); + const XMMATRIX m0 = XMLoadFloat4x4(&M); + XMStoreFloat4(&result, XMPlaneTransform(p, m0)); +} + +inline Plane Plane::Transform(const Plane& plane, const Matrix& M) noexcept +{ + using namespace DirectX; + const XMVECTOR p = XMLoadFloat4(&plane); + const XMMATRIX m0 = XMLoadFloat4x4(&M); + + Plane result; + XMStoreFloat4(&result, XMPlaneTransform(p, m0)); + return result; +} + +inline void Plane::Transform(const Plane& plane, const Quaternion& rotation, Plane& result) noexcept +{ + using namespace DirectX; + const XMVECTOR p = XMLoadFloat4(&plane); + const XMVECTOR q = XMLoadFloat4(&rotation); + XMVECTOR X = XMVector3Rotate(p, q); + X = XMVectorSelect(p, X, g_XMSelect1110); // result.d = plane.d + XMStoreFloat4(&result, X); +} + +inline Plane Plane::Transform(const Plane& plane, const Quaternion& rotation) noexcept +{ + using namespace DirectX; + const XMVECTOR p = XMLoadFloat4(&plane); + const XMVECTOR q = XMLoadFloat4(&rotation); + XMVECTOR X = XMVector3Rotate(p, q); + X = XMVectorSelect(p, X, g_XMSelect1110); // result.d = plane.d + + Plane result; + XMStoreFloat4(&result, X); + return result; +} + + +/**************************************************************************** + * + * Quaternion + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ +// Comparision operators +//------------------------------------------------------------------------------ + +inline bool Quaternion::operator == (const Quaternion& q) const noexcept +{ + using namespace DirectX; + const XMVECTOR q1 = XMLoadFloat4(this); + const XMVECTOR q2 = XMLoadFloat4(&q); + return XMQuaternionEqual(q1, q2); +} + +inline bool Quaternion::operator != (const Quaternion& q) const noexcept +{ + using namespace DirectX; + const XMVECTOR q1 = XMLoadFloat4(this); + const XMVECTOR q2 = XMLoadFloat4(&q); + return XMQuaternionNotEqual(q1, q2); +} + +//------------------------------------------------------------------------------ +// Assignment operators +//------------------------------------------------------------------------------ + +inline Quaternion& Quaternion::operator+= (const Quaternion& q) noexcept +{ + using namespace DirectX; + const XMVECTOR q1 = XMLoadFloat4(this); + const XMVECTOR q2 = XMLoadFloat4(&q); + XMStoreFloat4(this, XMVectorAdd(q1, q2)); + return *this; +} + +inline Quaternion& Quaternion::operator-= (const Quaternion& q) noexcept +{ + using namespace DirectX; + const XMVECTOR q1 = XMLoadFloat4(this); + const XMVECTOR q2 = XMLoadFloat4(&q); + XMStoreFloat4(this, XMVectorSubtract(q1, q2)); + return *this; +} + +inline Quaternion& Quaternion::operator*= (const Quaternion& q) noexcept +{ + using namespace DirectX; + const XMVECTOR q1 = XMLoadFloat4(this); + const XMVECTOR q2 = XMLoadFloat4(&q); + XMStoreFloat4(this, XMQuaternionMultiply(q1, q2)); + return *this; +} + +inline Quaternion& Quaternion::operator*= (float S) noexcept +{ + using namespace DirectX; + const XMVECTOR q = XMLoadFloat4(this); + XMStoreFloat4(this, XMVectorScale(q, S)); + return *this; +} + +inline Quaternion& Quaternion::operator/= (const Quaternion& q) noexcept +{ + using namespace DirectX; + const XMVECTOR q1 = XMLoadFloat4(this); + XMVECTOR q2 = XMLoadFloat4(&q); + q2 = XMQuaternionInverse(q2); + XMStoreFloat4(this, XMQuaternionMultiply(q1, q2)); + return *this; +} + +//------------------------------------------------------------------------------ +// Urnary operators +//------------------------------------------------------------------------------ + +inline Quaternion Quaternion::operator- () const noexcept +{ + using namespace DirectX; + const XMVECTOR q = XMLoadFloat4(this); + + Quaternion R; + XMStoreFloat4(&R, XMVectorNegate(q)); + return R; +} + +//------------------------------------------------------------------------------ +// Binary operators +//------------------------------------------------------------------------------ + +inline Quaternion operator+ (const Quaternion& Q1, const Quaternion& Q2) noexcept +{ + using namespace DirectX; + const XMVECTOR q1 = XMLoadFloat4(&Q1); + const XMVECTOR q2 = XMLoadFloat4(&Q2); + + Quaternion R; + XMStoreFloat4(&R, XMVectorAdd(q1, q2)); + return R; +} + +inline Quaternion operator- (const Quaternion& Q1, const Quaternion& Q2) noexcept +{ + using namespace DirectX; + const XMVECTOR q1 = XMLoadFloat4(&Q1); + const XMVECTOR q2 = XMLoadFloat4(&Q2); + + Quaternion R; + XMStoreFloat4(&R, XMVectorSubtract(q1, q2)); + return R; +} + +inline Quaternion operator* (const Quaternion& Q1, const Quaternion& Q2) noexcept +{ + using namespace DirectX; + const XMVECTOR q1 = XMLoadFloat4(&Q1); + const XMVECTOR q2 = XMLoadFloat4(&Q2); + + Quaternion R; + XMStoreFloat4(&R, XMQuaternionMultiply(q1, q2)); + return R; +} + +inline Quaternion operator* (const Quaternion& Q, float S) noexcept +{ + using namespace DirectX; + const XMVECTOR q = XMLoadFloat4(&Q); + + Quaternion R; + XMStoreFloat4(&R, XMVectorScale(q, S)); + return R; +} + +inline Quaternion operator/ (const Quaternion& Q1, const Quaternion& Q2) noexcept +{ + using namespace DirectX; + const XMVECTOR q1 = XMLoadFloat4(&Q1); + XMVECTOR q2 = XMLoadFloat4(&Q2); + q2 = XMQuaternionInverse(q2); + + Quaternion R; + XMStoreFloat4(&R, XMQuaternionMultiply(q1, q2)); + return R; +} + +inline Quaternion operator* (float S, const Quaternion& Q) noexcept +{ + using namespace DirectX; + const XMVECTOR q1 = XMLoadFloat4(&Q); + + Quaternion R; + XMStoreFloat4(&R, XMVectorScale(q1, S)); + return R; +} + +//------------------------------------------------------------------------------ +// Quaternion operations +//------------------------------------------------------------------------------ + +inline float Quaternion::Length() const noexcept +{ + using namespace DirectX; + const XMVECTOR q = XMLoadFloat4(this); + return XMVectorGetX(XMQuaternionLength(q)); +} + +inline float Quaternion::LengthSquared() const noexcept +{ + using namespace DirectX; + const XMVECTOR q = XMLoadFloat4(this); + return XMVectorGetX(XMQuaternionLengthSq(q)); +} + +inline void Quaternion::Normalize() noexcept +{ + using namespace DirectX; + const XMVECTOR q = XMLoadFloat4(this); + XMStoreFloat4(this, XMQuaternionNormalize(q)); +} + +inline void Quaternion::Normalize(Quaternion& result) const noexcept +{ + using namespace DirectX; + const XMVECTOR q = XMLoadFloat4(this); + XMStoreFloat4(&result, XMQuaternionNormalize(q)); +} + +inline void Quaternion::Conjugate() noexcept +{ + using namespace DirectX; + const XMVECTOR q = XMLoadFloat4(this); + XMStoreFloat4(this, XMQuaternionConjugate(q)); +} + +inline void Quaternion::Conjugate(Quaternion& result) const noexcept +{ + using namespace DirectX; + const XMVECTOR q = XMLoadFloat4(this); + XMStoreFloat4(&result, XMQuaternionConjugate(q)); +} + +inline void Quaternion::Inverse(Quaternion& result) const noexcept +{ + using namespace DirectX; + const XMVECTOR q = XMLoadFloat4(this); + XMStoreFloat4(&result, XMQuaternionInverse(q)); +} + +inline float Quaternion::Dot(const Quaternion& q) const noexcept +{ + using namespace DirectX; + const XMVECTOR q1 = XMLoadFloat4(this); + const XMVECTOR q2 = XMLoadFloat4(&q); + return XMVectorGetX(XMQuaternionDot(q1, q2)); +} + +inline void Quaternion::RotateTowards(const Quaternion& target, float maxAngle) noexcept +{ + RotateTowards(target, maxAngle, *this); +} + +inline Vector3 Quaternion::ToEuler() const noexcept +{ + const float xx = x * x; + const float yy = y * y; + const float zz = z * z; + + const float m31 = 2.f * x * z + 2.f * y * w; + const float m32 = 2.f * y * z - 2.f * x * w; + const float m33 = 1.f - 2.f * xx - 2.f * yy; + + const float cy = sqrtf(m33 * m33 + m31 * m31); + const float cx = atan2f(-m32, cy); + if (cy > 16.f * FLT_EPSILON) + { + const float m12 = 2.f * x * y + 2.f * z * w; + const float m22 = 1.f - 2.f * xx - 2.f * zz; + + return Vector3(cx, atan2f(m31, m33), atan2f(m12, m22)); + } + else + { + const float m11 = 1.f - 2.f * yy - 2.f * zz; + const float m21 = 2.f * x * y - 2.f * z * w; + + return Vector3(cx, 0.f, atan2f(-m21, m11)); + } +} + +//------------------------------------------------------------------------------ +// Static functions +//------------------------------------------------------------------------------ + +inline Quaternion Quaternion::CreateFromAxisAngle(const Vector3& axis, float angle) noexcept +{ + using namespace DirectX; + const XMVECTOR a = XMLoadFloat3(&axis); + + Quaternion R; + XMStoreFloat4(&R, XMQuaternionRotationAxis(a, angle)); + return R; +} + +inline Quaternion Quaternion::CreateFromYawPitchRoll(float yaw, float pitch, float roll) noexcept +{ + using namespace DirectX; + Quaternion R; + XMStoreFloat4(&R, XMQuaternionRotationRollPitchYaw(pitch, yaw, roll)); + return R; +} + +inline Quaternion Quaternion::CreateFromYawPitchRoll(const Vector3& angles) noexcept +{ + using namespace DirectX; + Quaternion R; + XMStoreFloat4(&R, XMQuaternionRotationRollPitchYawFromVector(angles)); + return R; +} + +inline Quaternion Quaternion::CreateFromRotationMatrix(const Matrix& M) noexcept +{ + using namespace DirectX; + const XMMATRIX M0 = XMLoadFloat4x4(&M); + + Quaternion R; + XMStoreFloat4(&R, XMQuaternionRotationMatrix(M0)); + return R; +} + +inline void Quaternion::Lerp(const Quaternion& q1, const Quaternion& q2, float t, Quaternion& result) noexcept +{ + using namespace DirectX; + const XMVECTOR Q0 = XMLoadFloat4(&q1); + const XMVECTOR Q1 = XMLoadFloat4(&q2); + + const XMVECTOR dot = XMVector4Dot(Q0, Q1); + + XMVECTOR R; + if (XMVector4GreaterOrEqual(dot, XMVectorZero())) + { + R = XMVectorLerp(Q0, Q1, t); + } + else + { + const XMVECTOR tv = XMVectorReplicate(t); + const XMVECTOR t1v = XMVectorReplicate(1.f - t); + const XMVECTOR X0 = XMVectorMultiply(Q0, t1v); + const XMVECTOR X1 = XMVectorMultiply(Q1, tv); + R = XMVectorSubtract(X0, X1); + } + + XMStoreFloat4(&result, XMQuaternionNormalize(R)); +} + +inline Quaternion Quaternion::Lerp(const Quaternion& q1, const Quaternion& q2, float t) noexcept +{ + using namespace DirectX; + const XMVECTOR Q0 = XMLoadFloat4(&q1); + const XMVECTOR Q1 = XMLoadFloat4(&q2); + + const XMVECTOR dot = XMVector4Dot(Q0, Q1); + + XMVECTOR R; + if (XMVector4GreaterOrEqual(dot, XMVectorZero())) + { + R = XMVectorLerp(Q0, Q1, t); + } + else + { + const XMVECTOR tv = XMVectorReplicate(t); + const XMVECTOR t1v = XMVectorReplicate(1.f - t); + const XMVECTOR X0 = XMVectorMultiply(Q0, t1v); + const XMVECTOR X1 = XMVectorMultiply(Q1, tv); + R = XMVectorSubtract(X0, X1); + } + + Quaternion result; + XMStoreFloat4(&result, XMQuaternionNormalize(R)); + return result; +} + +inline void Quaternion::Slerp(const Quaternion& q1, const Quaternion& q2, float t, Quaternion& result) noexcept +{ + using namespace DirectX; + const XMVECTOR Q0 = XMLoadFloat4(&q1); + const XMVECTOR Q1 = XMLoadFloat4(&q2); + XMStoreFloat4(&result, XMQuaternionSlerp(Q0, Q1, t)); +} + +inline Quaternion Quaternion::Slerp(const Quaternion& q1, const Quaternion& q2, float t) noexcept +{ + using namespace DirectX; + const XMVECTOR Q0 = XMLoadFloat4(&q1); + const XMVECTOR Q1 = XMLoadFloat4(&q2); + + Quaternion result; + XMStoreFloat4(&result, XMQuaternionSlerp(Q0, Q1, t)); + return result; +} + +inline void Quaternion::Concatenate(const Quaternion& q1, const Quaternion& q2, Quaternion& result) noexcept +{ + using namespace DirectX; + const XMVECTOR Q0 = XMLoadFloat4(&q1); + const XMVECTOR Q1 = XMLoadFloat4(&q2); + XMStoreFloat4(&result, XMQuaternionMultiply(Q1, Q0)); +} + +inline Quaternion Quaternion::Concatenate(const Quaternion& q1, const Quaternion& q2) noexcept +{ + using namespace DirectX; + const XMVECTOR Q0 = XMLoadFloat4(&q1); + const XMVECTOR Q1 = XMLoadFloat4(&q2); + + Quaternion result; + XMStoreFloat4(&result, XMQuaternionMultiply(Q1, Q0)); + return result; +} + +inline Quaternion Quaternion::FromToRotation(const Vector3& fromDir, const Vector3& toDir) noexcept +{ + Quaternion result; + FromToRotation(fromDir, toDir, result); + return result; +} + +inline Quaternion Quaternion::LookRotation(const Vector3& forward, const Vector3& up) noexcept +{ + Quaternion result; + LookRotation(forward, up, result); + return result; +} + +inline float Quaternion::Angle(const Quaternion& q1, const Quaternion& q2) noexcept +{ + using namespace DirectX; + const XMVECTOR Q0 = XMLoadFloat4(&q1); + const XMVECTOR Q1 = XMLoadFloat4(&q2); + + // We can use the conjugate here instead of inverse assuming q1 & q2 are normalized. + XMVECTOR R = XMQuaternionMultiply(XMQuaternionConjugate(Q0), Q1); + + const float rs = XMVectorGetW(R); + R = XMVector3Length(R); + return 2.f * atan2f(XMVectorGetX(R), rs); +} + + +/**************************************************************************** + * + * Color + * + ****************************************************************************/ + +inline Color::Color(const DirectX::PackedVector::XMCOLOR& Packed) noexcept +{ + using namespace DirectX; + XMStoreFloat4(this, PackedVector::XMLoadColor(&Packed)); +} + +inline Color::Color(const DirectX::PackedVector::XMUBYTEN4& Packed) noexcept +{ + using namespace DirectX; + XMStoreFloat4(this, PackedVector::XMLoadUByteN4(&Packed)); +} + +//------------------------------------------------------------------------------ +// Comparision operators +//------------------------------------------------------------------------------ +inline bool Color::operator == (const Color& c) const noexcept +{ + using namespace DirectX; + const XMVECTOR c1 = XMLoadFloat4(this); + const XMVECTOR c2 = XMLoadFloat4(&c); + return XMColorEqual(c1, c2); +} + +inline bool Color::operator != (const Color& c) const noexcept +{ + using namespace DirectX; + const XMVECTOR c1 = XMLoadFloat4(this); + const XMVECTOR c2 = XMLoadFloat4(&c); + return XMColorNotEqual(c1, c2); +} + +//------------------------------------------------------------------------------ +// Assignment operators +//------------------------------------------------------------------------------ + +inline Color& Color::operator= (const DirectX::PackedVector::XMCOLOR& Packed) noexcept +{ + using namespace DirectX; + XMStoreFloat4(this, PackedVector::XMLoadColor(&Packed)); + return *this; +} + +inline Color& Color::operator= (const DirectX::PackedVector::XMUBYTEN4& Packed) noexcept +{ + using namespace DirectX; + XMStoreFloat4(this, PackedVector::XMLoadUByteN4(&Packed)); + return *this; +} + +inline Color& Color::operator+= (const Color& c) noexcept +{ + using namespace DirectX; + const XMVECTOR c1 = XMLoadFloat4(this); + const XMVECTOR c2 = XMLoadFloat4(&c); + XMStoreFloat4(this, XMVectorAdd(c1, c2)); + return *this; +} + +inline Color& Color::operator-= (const Color& c) noexcept +{ + using namespace DirectX; + const XMVECTOR c1 = XMLoadFloat4(this); + const XMVECTOR c2 = XMLoadFloat4(&c); + XMStoreFloat4(this, XMVectorSubtract(c1, c2)); + return *this; +} + +inline Color& Color::operator*= (const Color& c) noexcept +{ + using namespace DirectX; + const XMVECTOR c1 = XMLoadFloat4(this); + const XMVECTOR c2 = XMLoadFloat4(&c); + XMStoreFloat4(this, XMVectorMultiply(c1, c2)); + return *this; +} + +inline Color& Color::operator*= (float S) noexcept +{ + using namespace DirectX; + const XMVECTOR c = XMLoadFloat4(this); + XMStoreFloat4(this, XMVectorScale(c, S)); + return *this; +} + +inline Color& Color::operator/= (const Color& c) noexcept +{ + using namespace DirectX; + const XMVECTOR c1 = XMLoadFloat4(this); + const XMVECTOR c2 = XMLoadFloat4(&c); + XMStoreFloat4(this, XMVectorDivide(c1, c2)); + return *this; +} + +//------------------------------------------------------------------------------ +// Urnary operators +//------------------------------------------------------------------------------ + +inline Color Color::operator- () const noexcept +{ + using namespace DirectX; + const XMVECTOR c = XMLoadFloat4(this); + Color R; + XMStoreFloat4(&R, XMVectorNegate(c)); + return R; +} + +//------------------------------------------------------------------------------ +// Binary operators +//------------------------------------------------------------------------------ + +inline Color operator+ (const Color& C1, const Color& C2) noexcept +{ + using namespace DirectX; + const XMVECTOR c1 = XMLoadFloat4(&C1); + const XMVECTOR c2 = XMLoadFloat4(&C2); + Color R; + XMStoreFloat4(&R, XMVectorAdd(c1, c2)); + return R; +} + +inline Color operator- (const Color& C1, const Color& C2) noexcept +{ + using namespace DirectX; + const XMVECTOR c1 = XMLoadFloat4(&C1); + const XMVECTOR c2 = XMLoadFloat4(&C2); + Color R; + XMStoreFloat4(&R, XMVectorSubtract(c1, c2)); + return R; +} + +inline Color operator* (const Color& C1, const Color& C2) noexcept +{ + using namespace DirectX; + const XMVECTOR c1 = XMLoadFloat4(&C1); + const XMVECTOR c2 = XMLoadFloat4(&C2); + Color R; + XMStoreFloat4(&R, XMVectorMultiply(c1, c2)); + return R; +} + +inline Color operator* (const Color& C, float S) noexcept +{ + using namespace DirectX; + const XMVECTOR c = XMLoadFloat4(&C); + Color R; + XMStoreFloat4(&R, XMVectorScale(c, S)); + return R; +} + +inline Color operator/ (const Color& C1, const Color& C2) noexcept +{ + using namespace DirectX; + const XMVECTOR c1 = XMLoadFloat4(&C1); + const XMVECTOR c2 = XMLoadFloat4(&C2); + Color R; + XMStoreFloat4(&R, XMVectorDivide(c1, c2)); + return R; +} + +inline Color operator* (float S, const Color& C) noexcept +{ + using namespace DirectX; + const XMVECTOR c1 = XMLoadFloat4(&C); + Color R; + XMStoreFloat4(&R, XMVectorScale(c1, S)); + return R; +} + +//------------------------------------------------------------------------------ +// Color operations +//------------------------------------------------------------------------------ + +inline DirectX::PackedVector::XMCOLOR Color::BGRA() const noexcept +{ + using namespace DirectX; + const XMVECTOR clr = XMLoadFloat4(this); + PackedVector::XMCOLOR Packed; + PackedVector::XMStoreColor(&Packed, clr); + return Packed; +} + +inline DirectX::PackedVector::XMUBYTEN4 Color::RGBA() const noexcept +{ + using namespace DirectX; + const XMVECTOR clr = XMLoadFloat4(this); + PackedVector::XMUBYTEN4 Packed; + PackedVector::XMStoreUByteN4(&Packed, clr); + return Packed; +} + +inline Vector3 Color::ToVector3() const noexcept +{ + return Vector3(x, y, z); +} + +inline Vector4 Color::ToVector4() const noexcept +{ + return Vector4(x, y, z, w); +} + +inline void Color::Negate() noexcept +{ + using namespace DirectX; + const XMVECTOR c = XMLoadFloat4(this); + XMStoreFloat4(this, XMColorNegative(c)); +} + +inline void Color::Negate(Color& result) const noexcept +{ + using namespace DirectX; + const XMVECTOR c = XMLoadFloat4(this); + XMStoreFloat4(&result, XMColorNegative(c)); +} + +inline void Color::Saturate() noexcept +{ + using namespace DirectX; + const XMVECTOR c = XMLoadFloat4(this); + XMStoreFloat4(this, XMVectorSaturate(c)); +} + +inline void Color::Saturate(Color& result) const noexcept +{ + using namespace DirectX; + const XMVECTOR c = XMLoadFloat4(this); + XMStoreFloat4(&result, XMVectorSaturate(c)); +} + +inline void Color::Premultiply() noexcept +{ + using namespace DirectX; + const XMVECTOR c = XMLoadFloat4(this); + XMVECTOR a = XMVectorSplatW(c); + a = XMVectorSelect(g_XMIdentityR3, a, g_XMSelect1110); + XMStoreFloat4(this, XMVectorMultiply(c, a)); +} + +inline void Color::Premultiply(Color& result) const noexcept +{ + using namespace DirectX; + const XMVECTOR c = XMLoadFloat4(this); + XMVECTOR a = XMVectorSplatW(c); + a = XMVectorSelect(g_XMIdentityR3, a, g_XMSelect1110); + XMStoreFloat4(&result, XMVectorMultiply(c, a)); +} + +inline void Color::AdjustSaturation(float sat) noexcept +{ + using namespace DirectX; + const XMVECTOR c = XMLoadFloat4(this); + XMStoreFloat4(this, XMColorAdjustSaturation(c, sat)); +} + +inline void Color::AdjustSaturation(float sat, Color& result) const noexcept +{ + using namespace DirectX; + const XMVECTOR c = XMLoadFloat4(this); + XMStoreFloat4(&result, XMColorAdjustSaturation(c, sat)); +} + +inline void Color::AdjustContrast(float contrast) noexcept +{ + using namespace DirectX; + const XMVECTOR c = XMLoadFloat4(this); + XMStoreFloat4(this, XMColorAdjustContrast(c, contrast)); +} + +inline void Color::AdjustContrast(float contrast, Color& result) const noexcept +{ + using namespace DirectX; + const XMVECTOR c = XMLoadFloat4(this); + XMStoreFloat4(&result, XMColorAdjustContrast(c, contrast)); +} + +//------------------------------------------------------------------------------ +// Static functions +//------------------------------------------------------------------------------ + +inline void Color::Modulate(const Color& c1, const Color& c2, Color& result) noexcept +{ + using namespace DirectX; + const XMVECTOR C0 = XMLoadFloat4(&c1); + const XMVECTOR C1 = XMLoadFloat4(&c2); + XMStoreFloat4(&result, XMColorModulate(C0, C1)); +} + +inline Color Color::Modulate(const Color& c1, const Color& c2) noexcept +{ + using namespace DirectX; + const XMVECTOR C0 = XMLoadFloat4(&c1); + const XMVECTOR C1 = XMLoadFloat4(&c2); + + Color result; + XMStoreFloat4(&result, XMColorModulate(C0, C1)); + return result; +} + +inline void Color::Lerp(const Color& c1, const Color& c2, float t, Color& result) noexcept +{ + using namespace DirectX; + const XMVECTOR C0 = XMLoadFloat4(&c1); + const XMVECTOR C1 = XMLoadFloat4(&c2); + XMStoreFloat4(&result, XMVectorLerp(C0, C1, t)); +} + +inline Color Color::Lerp(const Color& c1, const Color& c2, float t) noexcept +{ + using namespace DirectX; + const XMVECTOR C0 = XMLoadFloat4(&c1); + const XMVECTOR C1 = XMLoadFloat4(&c2); + + Color result; + XMStoreFloat4(&result, XMVectorLerp(C0, C1, t)); + return result; +} + + +/**************************************************************************** + * + * Ray + * + ****************************************************************************/ + +//----------------------------------------------------------------------------- +// Comparision operators +//------------------------------------------------------------------------------ +inline bool Ray::operator == (const Ray& r) const noexcept +{ + using namespace DirectX; + const XMVECTOR r1p = XMLoadFloat3(&position); + const XMVECTOR r2p = XMLoadFloat3(&r.position); + const XMVECTOR r1d = XMLoadFloat3(&direction); + const XMVECTOR r2d = XMLoadFloat3(&r.direction); + return XMVector3Equal(r1p, r2p) && XMVector3Equal(r1d, r2d); +} + +inline bool Ray::operator != (const Ray& r) const noexcept +{ + using namespace DirectX; + const XMVECTOR r1p = XMLoadFloat3(&position); + const XMVECTOR r2p = XMLoadFloat3(&r.position); + const XMVECTOR r1d = XMLoadFloat3(&direction); + const XMVECTOR r2d = XMLoadFloat3(&r.direction); + return XMVector3NotEqual(r1p, r2p) && XMVector3NotEqual(r1d, r2d); +} + +//----------------------------------------------------------------------------- +// Ray operators +//------------------------------------------------------------------------------ + +inline bool Ray::Intersects(const BoundingSphere& sphere, _Out_ float& Dist) const noexcept +{ + return sphere.Intersects(position, direction, Dist); +} + +inline bool Ray::Intersects(const BoundingBox& box, _Out_ float& Dist) const noexcept +{ + return box.Intersects(position, direction, Dist); +} + +inline bool Ray::Intersects(const Vector3& tri0, const Vector3& tri1, const Vector3& tri2, _Out_ float& Dist) const noexcept +{ + return DirectX::TriangleTests::Intersects(position, direction, tri0, tri1, tri2, Dist); +} + +inline bool Ray::Intersects(const Plane& plane, _Out_ float& Dist) const noexcept +{ + using namespace DirectX; + + const XMVECTOR p = XMLoadFloat4(&plane); + const XMVECTOR dir = XMLoadFloat3(&direction); + + const XMVECTOR nd = XMPlaneDotNormal(p, dir); + + if (XMVector3LessOrEqual(XMVectorAbs(nd), g_RayEpsilon)) + { + Dist = 0.f; + return false; + } + else + { + // t = -(dot(n,origin) + D) / dot(n,dir) + const XMVECTOR pos = XMLoadFloat3(&position); + XMVECTOR v = XMPlaneDotNormal(p, pos); + v = XMVectorAdd(v, XMVectorSplatW(p)); + v = XMVectorDivide(v, nd); + float dist = -XMVectorGetX(v); + if (dist < 0) + { + Dist = 0.f; + return false; + } + else + { + Dist = dist; + return true; + } + } +} + + +/**************************************************************************** + * + * Viewport + * + ****************************************************************************/ + +//------------------------------------------------------------------------------ +// Comparision operators +//------------------------------------------------------------------------------ + +#if (__cplusplus < 202002L) +inline bool Viewport::operator == (const Viewport& vp) const noexcept +{ + return (x == vp.x && y == vp.y + && width == vp.width && height == vp.height + && minDepth == vp.minDepth && maxDepth == vp.maxDepth); +} + +inline bool Viewport::operator != (const Viewport& vp) const noexcept +{ + return (x != vp.x || y != vp.y + || width != vp.width || height != vp.height + || minDepth != vp.minDepth || maxDepth != vp.maxDepth); +} +#endif + +//------------------------------------------------------------------------------ +// Assignment operators +//------------------------------------------------------------------------------ + +inline Viewport& Viewport::operator= (const RECT& rct) noexcept +{ + x = float(rct.left); y = float(rct.top); + width = float(rct.right - rct.left); + height = float(rct.bottom - rct.top); + minDepth = 0.f; maxDepth = 1.f; + return *this; +} + +#if defined(__d3d11_h__) || defined(__d3d11_x_h__) +inline Viewport& Viewport::operator= (const D3D11_VIEWPORT& vp) noexcept +{ + x = vp.TopLeftX; y = vp.TopLeftY; + width = vp.Width; height = vp.Height; + minDepth = vp.MinDepth; maxDepth = vp.MaxDepth; + return *this; +} +#endif + +#if defined(__d3d12_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) +inline Viewport& Viewport::operator= (const D3D12_VIEWPORT& vp) noexcept +{ + x = vp.TopLeftX; y = vp.TopLeftY; + width = vp.Width; height = vp.Height; + minDepth = vp.MinDepth; maxDepth = vp.MaxDepth; + return *this; +} +#endif + +//------------------------------------------------------------------------------ +// Viewport operations +//------------------------------------------------------------------------------ + +inline float Viewport::AspectRatio() const noexcept +{ + if (width == 0.f || height == 0.f) + return 0.f; + + return (width / height); +} + +inline Vector3 Viewport::Project(const Vector3& p, const Matrix& proj, const Matrix& view, const Matrix& world) const noexcept +{ + using namespace DirectX; + XMVECTOR v = XMLoadFloat3(&p); + const XMMATRIX projection = XMLoadFloat4x4(&proj); + v = XMVector3Project(v, x, y, width, height, minDepth, maxDepth, projection, view, world); + Vector3 result; + XMStoreFloat3(&result, v); + return result; +} + +inline void Viewport::Project(const Vector3& p, const Matrix& proj, const Matrix& view, const Matrix& world, Vector3& result) const noexcept +{ + using namespace DirectX; + XMVECTOR v = XMLoadFloat3(&p); + const XMMATRIX projection = XMLoadFloat4x4(&proj); + v = XMVector3Project(v, x, y, width, height, minDepth, maxDepth, projection, view, world); + XMStoreFloat3(&result, v); +} + +inline Vector3 Viewport::Unproject(const Vector3& p, const Matrix& proj, const Matrix& view, const Matrix& world) const noexcept +{ + using namespace DirectX; + XMVECTOR v = XMLoadFloat3(&p); + const XMMATRIX projection = XMLoadFloat4x4(&proj); + v = XMVector3Unproject(v, x, y, width, height, minDepth, maxDepth, projection, view, world); + Vector3 result; + XMStoreFloat3(&result, v); + return result; +} + +inline void Viewport::Unproject(const Vector3& p, const Matrix& proj, const Matrix& view, const Matrix& world, Vector3& result) const noexcept +{ + using namespace DirectX; + XMVECTOR v = XMLoadFloat3(&p); + const XMMATRIX projection = XMLoadFloat4x4(&proj); + v = XMVector3Unproject(v, x, y, width, height, minDepth, maxDepth, projection, view, world); + XMStoreFloat3(&result, v); +} diff --git a/Common/DirectXTK12/Inc/SpriteBatch.h b/Common/DirectXTK12/Inc/SpriteBatch.h new file mode 100644 index 0000000..1ad353d --- /dev/null +++ b/Common/DirectXTK12/Inc/SpriteBatch.h @@ -0,0 +1,157 @@ +//-------------------------------------------------------------------------------------- +// File: SpriteBatch.h +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#pragma once + +#ifdef _GAMING_XBOX_SCARLETT +#include +#elif (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) +#include +#else +#ifdef USING_DIRECTX_HEADERS +#include +#include +#else +#include +#endif +#include +#endif + +#include +#include +#include + +#include +#include + +#include "RenderTargetState.h" + + +namespace DirectX +{ + class ResourceUploadBatch; + + inline namespace DX12 + { + enum SpriteSortMode + { + SpriteSortMode_Deferred, + SpriteSortMode_Immediate, + SpriteSortMode_Texture, + SpriteSortMode_BackToFront, + SpriteSortMode_FrontToBack, + }; + + enum SpriteEffects : uint32_t + { + SpriteEffects_None = 0, + SpriteEffects_FlipHorizontally = 1, + SpriteEffects_FlipVertically = 2, + SpriteEffects_FlipBoth = SpriteEffects_FlipHorizontally | SpriteEffects_FlipVertically, + }; + + class SpriteBatchPipelineStateDescription + { + public: + explicit SpriteBatchPipelineStateDescription( + const RenderTargetState& renderTarget, + _In_opt_ const D3D12_BLEND_DESC* blend = nullptr, + _In_opt_ const D3D12_DEPTH_STENCIL_DESC* depthStencil = nullptr, + _In_opt_ const D3D12_RASTERIZER_DESC* rasterizer = nullptr, + _In_opt_ const D3D12_GPU_DESCRIPTOR_HANDLE* isamplerDescriptor = nullptr) noexcept + : + blendDesc(blend ? *blend : s_DefaultBlendDesc), + depthStencilDesc(depthStencil ? *depthStencil : s_DefaultDepthStencilDesc), + rasterizerDesc(rasterizer ? *rasterizer : s_DefaultRasterizerDesc), + renderTargetState(renderTarget), + samplerDescriptor{}, + customRootSignature(nullptr), + customVertexShader{}, + customPixelShader{} + { + if (isamplerDescriptor) + this->samplerDescriptor = *isamplerDescriptor; + } + + D3D12_BLEND_DESC blendDesc; + D3D12_DEPTH_STENCIL_DESC depthStencilDesc; + D3D12_RASTERIZER_DESC rasterizerDesc; + RenderTargetState renderTargetState; + D3D12_GPU_DESCRIPTOR_HANDLE samplerDescriptor; + ID3D12RootSignature* customRootSignature; + D3D12_SHADER_BYTECODE customVertexShader; + D3D12_SHADER_BYTECODE customPixelShader; + + private: + static const D3D12_BLEND_DESC s_DefaultBlendDesc; + static const D3D12_RASTERIZER_DESC s_DefaultRasterizerDesc; + static const D3D12_DEPTH_STENCIL_DESC s_DefaultDepthStencilDesc; + }; + + class SpriteBatch + { + public: + SpriteBatch(_In_ ID3D12Device* device, ResourceUploadBatch& upload, + const SpriteBatchPipelineStateDescription& psoDesc, + _In_opt_ const D3D12_VIEWPORT* viewport = nullptr); + + SpriteBatch(SpriteBatch&&) noexcept; + SpriteBatch& operator= (SpriteBatch&&) noexcept; + + SpriteBatch(SpriteBatch const&) = delete; + SpriteBatch& operator= (SpriteBatch const&) = delete; + + virtual ~SpriteBatch(); + + // Begin/End a batch of sprite drawing operations. + void XM_CALLCONV Begin( + _In_ ID3D12GraphicsCommandList* commandList, + SpriteSortMode sortMode = SpriteSortMode_Deferred, + FXMMATRIX transformMatrix = MatrixIdentity); + void XM_CALLCONV Begin( + _In_ ID3D12GraphicsCommandList* commandList, + D3D12_GPU_DESCRIPTOR_HANDLE sampler, + SpriteSortMode sortMode = SpriteSortMode_Deferred, + FXMMATRIX transformMatrix = MatrixIdentity); + void __cdecl End(); + + // Draw overloads specifying position, origin and scale as XMFLOAT2. + void XM_CALLCONV Draw(D3D12_GPU_DESCRIPTOR_HANDLE textureSRV, XMUINT2 const& textureSize, XMFLOAT2 const& position, FXMVECTOR color = Colors::White); + void XM_CALLCONV Draw(D3D12_GPU_DESCRIPTOR_HANDLE textureSRV, XMUINT2 const& textureSize, XMFLOAT2 const& position, _In_opt_ RECT const* sourceRectangle, FXMVECTOR color = Colors::White, float rotation = 0, XMFLOAT2 const& origin = Float2Zero, float scale = 1, SpriteEffects effects = SpriteEffects_None, float layerDepth = 0); + void XM_CALLCONV Draw(D3D12_GPU_DESCRIPTOR_HANDLE textureSRV, XMUINT2 const& textureSize, XMFLOAT2 const& position, _In_opt_ RECT const* sourceRectangle, FXMVECTOR color, float rotation, XMFLOAT2 const& origin, XMFLOAT2 const& scale, SpriteEffects effects = SpriteEffects_None, float layerDepth = 0); + + // Draw overloads specifying position, origin and scale via the first two components of an XMVECTOR. + void XM_CALLCONV Draw(D3D12_GPU_DESCRIPTOR_HANDLE textureSRV, XMUINT2 const& textureSize, FXMVECTOR position, FXMVECTOR color = Colors::White); + void XM_CALLCONV Draw(D3D12_GPU_DESCRIPTOR_HANDLE textureSRV, XMUINT2 const& textureSize, FXMVECTOR position, _In_opt_ RECT const* sourceRectangle, FXMVECTOR color = Colors::White, float rotation = 0, FXMVECTOR origin = g_XMZero, float scale = 1, SpriteEffects effects = SpriteEffects_None, float layerDepth = 0); + void XM_CALLCONV Draw(D3D12_GPU_DESCRIPTOR_HANDLE textureSRV, XMUINT2 const& textureSize, FXMVECTOR position, _In_opt_ RECT const* sourceRectangle, FXMVECTOR color, float rotation, FXMVECTOR origin, GXMVECTOR scale, SpriteEffects effects = SpriteEffects_None, float layerDepth = 0); + + // Draw overloads specifying position as a RECT. + void XM_CALLCONV Draw(D3D12_GPU_DESCRIPTOR_HANDLE textureSRV, XMUINT2 const& textureSize, RECT const& destinationRectangle, FXMVECTOR color = Colors::White); + void XM_CALLCONV Draw(D3D12_GPU_DESCRIPTOR_HANDLE textureSRV, XMUINT2 const& textureSize, RECT const& destinationRectangle, _In_opt_ RECT const* sourceRectangle, FXMVECTOR color = Colors::White, float rotation = 0, XMFLOAT2 const& origin = Float2Zero, SpriteEffects effects = SpriteEffects_None, float layerDepth = 0); + + // Rotation mode to be applied to the sprite transformation + #if defined(__dxgi1_2_h__) || defined(__d3d11_x_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) + void __cdecl SetRotation(DXGI_MODE_ROTATION mode); + DXGI_MODE_ROTATION __cdecl GetRotation() const noexcept; + #endif + + // Set viewport for sprite transformation + void __cdecl SetViewport(const D3D12_VIEWPORT& viewPort); + + private: + // Private implementation. + struct Impl; + + std::unique_ptr pImpl; + + static const XMMATRIX MatrixIdentity; + static const XMFLOAT2 Float2Zero; + }; + } +} diff --git a/Common/DirectXTK12/Inc/SpriteFont.h b/Common/DirectXTK12/Inc/SpriteFont.h new file mode 100644 index 0000000..73cbd10 --- /dev/null +++ b/Common/DirectXTK12/Inc/SpriteFont.h @@ -0,0 +1,124 @@ +//-------------------------------------------------------------------------------------- +// File: SpriteFont.h +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#pragma once + +#include "SpriteBatch.h" + +#include +#include +#include + + +namespace DirectX +{ + inline namespace DX12 + { + class SpriteFont + { + public: + struct Glyph; + + SpriteFont(ID3D12Device* device, ResourceUploadBatch& upload, + _In_z_ wchar_t const* fileName, + D3D12_CPU_DESCRIPTOR_HANDLE cpuDescriptorDest, D3D12_GPU_DESCRIPTOR_HANDLE gpuDescriptor, + bool forceSRGB = false); + SpriteFont(ID3D12Device* device, ResourceUploadBatch& upload, + _In_reads_bytes_(dataSize) uint8_t const* dataBlob, size_t dataSize, + D3D12_CPU_DESCRIPTOR_HANDLE cpuDescriptorDest, D3D12_GPU_DESCRIPTOR_HANDLE gpuDescriptor, + bool forceSRGB = false); + SpriteFont(D3D12_GPU_DESCRIPTOR_HANDLE texture, XMUINT2 textureSize, + _In_reads_(glyphCount) Glyph const* glyphs, size_t glyphCount, float lineSpacing); + + SpriteFont(SpriteFont&&) noexcept; + SpriteFont& operator= (SpriteFont&&) noexcept; + + SpriteFont(SpriteFont const&) = delete; + SpriteFont& operator= (SpriteFont const&) = delete; + + virtual ~SpriteFont(); + + // Wide-character / UTF-16LE + void XM_CALLCONV DrawString(_In_ SpriteBatch* spriteBatch, _In_z_ wchar_t const* text, XMFLOAT2 const& position, FXMVECTOR color = Colors::White, float rotation = 0, XMFLOAT2 const& origin = Float2Zero, float scale = 1, SpriteEffects effects = SpriteEffects_None, float layerDepth = 0) const; + void XM_CALLCONV DrawString(_In_ SpriteBatch* spriteBatch, _In_z_ wchar_t const* text, XMFLOAT2 const& position, FXMVECTOR color, float rotation, XMFLOAT2 const& origin, XMFLOAT2 const& scale, SpriteEffects effects = SpriteEffects_None, float layerDepth = 0) const; + void XM_CALLCONV DrawString(_In_ SpriteBatch* spriteBatch, _In_z_ wchar_t const* text, FXMVECTOR position, FXMVECTOR color = Colors::White, float rotation = 0, FXMVECTOR origin = g_XMZero, float scale = 1, SpriteEffects effects = SpriteEffects_None, float layerDepth = 0) const; + void XM_CALLCONV DrawString(_In_ SpriteBatch* spriteBatch, _In_z_ wchar_t const* text, FXMVECTOR position, FXMVECTOR color, float rotation, FXMVECTOR origin, GXMVECTOR scale, SpriteEffects effects = SpriteEffects_None, float layerDepth = 0) const; + + XMVECTOR XM_CALLCONV MeasureString(_In_z_ wchar_t const* text, bool ignoreWhitespace = true) const; + + RECT __cdecl MeasureDrawBounds(_In_z_ wchar_t const* text, XMFLOAT2 const& position, bool ignoreWhitespace = true) const; + RECT XM_CALLCONV MeasureDrawBounds(_In_z_ wchar_t const* text, FXMVECTOR position, bool ignoreWhitespace = true) const; + + // UTF-8 + void XM_CALLCONV DrawString(_In_ SpriteBatch* spriteBatch, _In_z_ char const* text, XMFLOAT2 const& position, FXMVECTOR color = Colors::White, float rotation = 0, XMFLOAT2 const& origin = Float2Zero, float scale = 1, SpriteEffects effects = SpriteEffects_None, float layerDepth = 0) const; + void XM_CALLCONV DrawString(_In_ SpriteBatch* spriteBatch, _In_z_ char const* text, XMFLOAT2 const& position, FXMVECTOR color, float rotation, XMFLOAT2 const& origin, XMFLOAT2 const& scale, SpriteEffects effects = SpriteEffects_None, float layerDepth = 0) const; + void XM_CALLCONV DrawString(_In_ SpriteBatch* spriteBatch, _In_z_ char const* text, FXMVECTOR position, FXMVECTOR color = Colors::White, float rotation = 0, FXMVECTOR origin = g_XMZero, float scale = 1, SpriteEffects effects = SpriteEffects_None, float layerDepth = 0) const; + void XM_CALLCONV DrawString(_In_ SpriteBatch* spriteBatch, _In_z_ char const* text, FXMVECTOR position, FXMVECTOR color, float rotation, FXMVECTOR origin, GXMVECTOR scale, SpriteEffects effects = SpriteEffects_None, float layerDepth = 0) const; + + XMVECTOR XM_CALLCONV MeasureString(_In_z_ char const* text, bool ignoreWhitespace = true) const; + + RECT __cdecl MeasureDrawBounds(_In_z_ char const* text, XMFLOAT2 const& position, bool ignoreWhitespace = true) const; + RECT XM_CALLCONV MeasureDrawBounds(_In_z_ char const* text, FXMVECTOR position, bool ignoreWhitespace = true) const; + + // Spacing properties + float __cdecl GetLineSpacing() const noexcept; + void __cdecl SetLineSpacing(float spacing); + + // Font properties + wchar_t __cdecl GetDefaultCharacter() const noexcept; + void __cdecl SetDefaultCharacter(wchar_t character); + + bool __cdecl ContainsCharacter(wchar_t character) const; + + // Custom layout/rendering + Glyph const* __cdecl FindGlyph(wchar_t character) const; + D3D12_GPU_DESCRIPTOR_HANDLE __cdecl GetSpriteSheet() const noexcept; + XMUINT2 __cdecl GetSpriteSheetSize() const noexcept; + + // Describes a single character glyph. + struct Glyph + { + uint32_t Character; + RECT Subrect; + float XOffset; + float YOffset; + float XAdvance; + }; + +#if defined(_MSC_VER) && !defined(_NATIVE_WCHAR_T_DEFINED) + SpriteFont(ID3D12Device* device, ResourceUploadBatch& upload, _In_z_ __wchar_t const* fileName, D3D12_CPU_DESCRIPTOR_HANDLE cpuDescriptorDest, D3D12_GPU_DESCRIPTOR_HANDLE gpuDescriptor, bool forceSRGB = false); + + void XM_CALLCONV DrawString(_In_ SpriteBatch* spriteBatch, _In_z_ __wchar_t const* text, XMFLOAT2 const& position, FXMVECTOR color = Colors::White, float rotation = 0, XMFLOAT2 const& origin = Float2Zero, float scale = 1, SpriteEffects effects = SpriteEffects_None, float layerDepth = 0) const; + + void XM_CALLCONV DrawString(_In_ SpriteBatch* spriteBatch, _In_z_ __wchar_t const* text, XMFLOAT2 const& position, FXMVECTOR color, float rotation, XMFLOAT2 const& origin, XMFLOAT2 const& scale, SpriteEffects effects = SpriteEffects_None, float layerDepth = 0) const; + void XM_CALLCONV DrawString(_In_ SpriteBatch* spriteBatch, _In_z_ __wchar_t const* text, FXMVECTOR position, FXMVECTOR color = Colors::White, float rotation = 0, FXMVECTOR origin = g_XMZero, float scale = 1, SpriteEffects effects = SpriteEffects_None, float layerDepth = 0) const; + void XM_CALLCONV DrawString(_In_ SpriteBatch* spriteBatch, _In_z_ __wchar_t const* text, FXMVECTOR position, FXMVECTOR color, float rotation, FXMVECTOR origin, GXMVECTOR scale, SpriteEffects effects = SpriteEffects_None, float layerDepth = 0) const; + + XMVECTOR XM_CALLCONV MeasureString(_In_z_ __wchar_t const* text, bool ignoreWhitespace = true) const; + + RECT __cdecl MeasureDrawBounds(_In_z_ __wchar_t const* text, XMFLOAT2 const& position, bool ignoreWhitespace = true) const; + RECT XM_CALLCONV MeasureDrawBounds(_In_z_ __wchar_t const* text, FXMVECTOR position, bool ignoreWhitespace = true) const; + + void __cdecl SetDefaultCharacter(__wchar_t character); + + bool __cdecl ContainsCharacter(__wchar_t character) const; + + Glyph const* __cdecl FindGlyph(__wchar_t character) const; +#endif // !_NATIVE_WCHAR_T_DEFINED + + private: + // Private implementation. + class Impl; + + std::unique_ptr pImpl; + + static const XMFLOAT2 Float2Zero; + }; + } +} diff --git a/Common/DirectXTK12/Inc/VertexTypes.h b/Common/DirectXTK12/Inc/VertexTypes.h new file mode 100644 index 0000000..78c14db --- /dev/null +++ b/Common/DirectXTK12/Inc/VertexTypes.h @@ -0,0 +1,360 @@ +//-------------------------------------------------------------------------------------- +// File: VertexTypes.h +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#pragma once + +#ifdef _GAMING_XBOX_SCARLETT +#include +#elif (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) +#include +#elif defined(USING_DIRECTX_HEADERS) +#include +#include +#else +#include +#endif + +#include + + +namespace DirectX +{ + inline namespace DX12 + { + // Vertex struct holding position information. + struct VertexPosition + { + VertexPosition() = default; + + VertexPosition(const VertexPosition&) = default; + VertexPosition& operator=(const VertexPosition&) = default; + + VertexPosition(VertexPosition&&) = default; + VertexPosition& operator=(VertexPosition&&) = default; + + VertexPosition(XMFLOAT3 const& iposition) noexcept + : position(iposition) + { + } + + VertexPosition(FXMVECTOR iposition) noexcept + { + XMStoreFloat3(&this->position, iposition); + } + + XMFLOAT3 position; + + static const D3D12_INPUT_LAYOUT_DESC InputLayout; + + private: + static constexpr unsigned int InputElementCount = 1; + static const D3D12_INPUT_ELEMENT_DESC InputElements[InputElementCount]; + }; + + + // Vertex struct holding position and color information. + struct VertexPositionColor + { + VertexPositionColor() = default; + + VertexPositionColor(const VertexPositionColor&) = default; + VertexPositionColor& operator=(const VertexPositionColor&) = default; + + VertexPositionColor(VertexPositionColor&&) = default; + VertexPositionColor& operator=(VertexPositionColor&&) = default; + + VertexPositionColor(XMFLOAT3 const& iposition, XMFLOAT4 const& icolor) noexcept + : position(iposition), + color(icolor) + { + } + + VertexPositionColor(FXMVECTOR iposition, FXMVECTOR icolor) noexcept + { + XMStoreFloat3(&this->position, iposition); + XMStoreFloat4(&this->color, icolor); + } + + XMFLOAT3 position; + XMFLOAT4 color; + + static const D3D12_INPUT_LAYOUT_DESC InputLayout; + + private: + static constexpr unsigned int InputElementCount = 2; + static const D3D12_INPUT_ELEMENT_DESC InputElements[InputElementCount]; + }; + + + // Vertex struct holding position and texture mapping information. + struct VertexPositionTexture + { + VertexPositionTexture() = default; + + VertexPositionTexture(const VertexPositionTexture&) = default; + VertexPositionTexture& operator=(const VertexPositionTexture&) = default; + + VertexPositionTexture(VertexPositionTexture&&) = default; + VertexPositionTexture& operator=(VertexPositionTexture&&) = default; + + VertexPositionTexture(XMFLOAT3 const& iposition, XMFLOAT2 const& itextureCoordinate) noexcept + : position(iposition), + textureCoordinate(itextureCoordinate) + { + } + + VertexPositionTexture(FXMVECTOR iposition, FXMVECTOR itextureCoordinate) noexcept + { + XMStoreFloat3(&this->position, iposition); + XMStoreFloat2(&this->textureCoordinate, itextureCoordinate); + } + + XMFLOAT3 position; + XMFLOAT2 textureCoordinate; + + static const D3D12_INPUT_LAYOUT_DESC InputLayout; + + private: + static constexpr unsigned int InputElementCount = 2; + static const D3D12_INPUT_ELEMENT_DESC InputElements[InputElementCount]; + }; + + + // Vertex struct holding position and dual texture mapping information. + struct VertexPositionDualTexture + { + VertexPositionDualTexture() = default; + + VertexPositionDualTexture(const VertexPositionDualTexture&) = default; + VertexPositionDualTexture& operator=(const VertexPositionDualTexture&) = default; + + VertexPositionDualTexture(VertexPositionDualTexture&&) = default; + VertexPositionDualTexture& operator=(VertexPositionDualTexture&&) = default; + + VertexPositionDualTexture( + XMFLOAT3 const& iposition, + XMFLOAT2 const& itextureCoordinate0, + XMFLOAT2 const& itextureCoordinate1) noexcept + : position(iposition), + textureCoordinate0(itextureCoordinate0), + textureCoordinate1(itextureCoordinate1) + { + } + + VertexPositionDualTexture( + FXMVECTOR iposition, + FXMVECTOR itextureCoordinate0, + FXMVECTOR itextureCoordinate1) noexcept + { + XMStoreFloat3(&this->position, iposition); + XMStoreFloat2(&this->textureCoordinate0, itextureCoordinate0); + XMStoreFloat2(&this->textureCoordinate1, itextureCoordinate1); + } + + XMFLOAT3 position; + XMFLOAT2 textureCoordinate0; + XMFLOAT2 textureCoordinate1; + + static const D3D12_INPUT_LAYOUT_DESC InputLayout; + + private: + static constexpr unsigned int InputElementCount = 3; + static const D3D12_INPUT_ELEMENT_DESC InputElements[InputElementCount]; + }; + + + // Vertex struct holding position and normal vector. + struct VertexPositionNormal + { + VertexPositionNormal() = default; + + VertexPositionNormal(const VertexPositionNormal&) = default; + VertexPositionNormal& operator=(const VertexPositionNormal&) = default; + + VertexPositionNormal(VertexPositionNormal&&) = default; + VertexPositionNormal& operator=(VertexPositionNormal&&) = default; + + VertexPositionNormal(XMFLOAT3 const& iposition, XMFLOAT3 const& inormal) noexcept + : position(iposition), + normal(inormal) + { + } + + VertexPositionNormal(FXMVECTOR iposition, FXMVECTOR inormal) noexcept + { + XMStoreFloat3(&this->position, iposition); + XMStoreFloat3(&this->normal, inormal); + } + + XMFLOAT3 position; + XMFLOAT3 normal; + + static const D3D12_INPUT_LAYOUT_DESC InputLayout; + + private: + static constexpr unsigned int InputElementCount = 2; + static const D3D12_INPUT_ELEMENT_DESC InputElements[InputElementCount]; + }; + + + // Vertex struct holding position, color, and texture mapping information. + struct VertexPositionColorTexture + { + VertexPositionColorTexture() = default; + + VertexPositionColorTexture(const VertexPositionColorTexture&) = default; + VertexPositionColorTexture& operator=(const VertexPositionColorTexture&) = default; + + VertexPositionColorTexture(VertexPositionColorTexture&&) = default; + VertexPositionColorTexture& operator=(VertexPositionColorTexture&&) = default; + + VertexPositionColorTexture(XMFLOAT3 const& iposition, XMFLOAT4 const& icolor, XMFLOAT2 const& itextureCoordinate) noexcept + : position(iposition), + color(icolor), + textureCoordinate(itextureCoordinate) + { + } + + VertexPositionColorTexture(FXMVECTOR iposition, FXMVECTOR icolor, FXMVECTOR itextureCoordinate) noexcept + { + XMStoreFloat3(&this->position, iposition); + XMStoreFloat4(&this->color, icolor); + XMStoreFloat2(&this->textureCoordinate, itextureCoordinate); + } + + XMFLOAT3 position; + XMFLOAT4 color; + XMFLOAT2 textureCoordinate; + + static const D3D12_INPUT_LAYOUT_DESC InputLayout; + + private: + static constexpr unsigned int InputElementCount = 3; + static const D3D12_INPUT_ELEMENT_DESC InputElements[InputElementCount]; + }; + + + // Vertex struct holding position, normal vector, and color information. + struct VertexPositionNormalColor + { + VertexPositionNormalColor() = default; + + VertexPositionNormalColor(const VertexPositionNormalColor&) = default; + VertexPositionNormalColor& operator=(const VertexPositionNormalColor&) = default; + + VertexPositionNormalColor(VertexPositionNormalColor&&) = default; + VertexPositionNormalColor& operator=(VertexPositionNormalColor&&) = default; + + VertexPositionNormalColor(XMFLOAT3 const& iposition, XMFLOAT3 const& inormal, XMFLOAT4 const& icolor) noexcept + : position(iposition), + normal(inormal), + color(icolor) + { + } + + VertexPositionNormalColor(FXMVECTOR iposition, FXMVECTOR inormal, FXMVECTOR icolor) noexcept + { + XMStoreFloat3(&this->position, iposition); + XMStoreFloat3(&this->normal, inormal); + XMStoreFloat4(&this->color, icolor); + } + + XMFLOAT3 position; + XMFLOAT3 normal; + XMFLOAT4 color; + + static const D3D12_INPUT_LAYOUT_DESC InputLayout; + + private: + static constexpr unsigned int InputElementCount = 3; + static const D3D12_INPUT_ELEMENT_DESC InputElements[InputElementCount]; + }; + + + // Vertex struct holding position, normal vector, and texture mapping information. + struct VertexPositionNormalTexture + { + VertexPositionNormalTexture() = default; + + VertexPositionNormalTexture(const VertexPositionNormalTexture&) = default; + VertexPositionNormalTexture& operator=(const VertexPositionNormalTexture&) = default; + + VertexPositionNormalTexture(VertexPositionNormalTexture&&) = default; + VertexPositionNormalTexture& operator=(VertexPositionNormalTexture&&) = default; + + VertexPositionNormalTexture(XMFLOAT3 const& iposition, XMFLOAT3 const& inormal, XMFLOAT2 const& itextureCoordinate) noexcept + : position(iposition), + normal(inormal), + textureCoordinate(itextureCoordinate) + { + } + + VertexPositionNormalTexture(FXMVECTOR iposition, FXMVECTOR inormal, FXMVECTOR itextureCoordinate) noexcept + { + XMStoreFloat3(&this->position, iposition); + XMStoreFloat3(&this->normal, inormal); + XMStoreFloat2(&this->textureCoordinate, itextureCoordinate); + } + + XMFLOAT3 position; + XMFLOAT3 normal; + XMFLOAT2 textureCoordinate; + + static const D3D12_INPUT_LAYOUT_DESC InputLayout; + + private: + static constexpr unsigned int InputElementCount = 3; + static const D3D12_INPUT_ELEMENT_DESC InputElements[InputElementCount]; + }; + + + // Vertex struct holding position, normal vector, color, and texture mapping information. + struct VertexPositionNormalColorTexture + { + VertexPositionNormalColorTexture() = default; + + VertexPositionNormalColorTexture(const VertexPositionNormalColorTexture&) = default; + VertexPositionNormalColorTexture& operator=(const VertexPositionNormalColorTexture&) = default; + + VertexPositionNormalColorTexture(VertexPositionNormalColorTexture&&) = default; + VertexPositionNormalColorTexture& operator=(VertexPositionNormalColorTexture&&) = default; + + VertexPositionNormalColorTexture( + XMFLOAT3 const& iposition, + XMFLOAT3 const& inormal, + XMFLOAT4 const& icolor, + XMFLOAT2 const& itextureCoordinate) noexcept + : position(iposition), + normal(inormal), + color(icolor), + textureCoordinate(itextureCoordinate) + { + } + + VertexPositionNormalColorTexture(FXMVECTOR iposition, FXMVECTOR inormal, FXMVECTOR icolor, CXMVECTOR itextureCoordinate) noexcept + { + XMStoreFloat3(&this->position, iposition); + XMStoreFloat3(&this->normal, inormal); + XMStoreFloat4(&this->color, icolor); + XMStoreFloat2(&this->textureCoordinate, itextureCoordinate); + } + + XMFLOAT3 position; + XMFLOAT3 normal; + XMFLOAT4 color; + XMFLOAT2 textureCoordinate; + + static const D3D12_INPUT_LAYOUT_DESC InputLayout; + + private: + static constexpr unsigned int InputElementCount = 4; + static const D3D12_INPUT_ELEMENT_DESC InputElements[InputElementCount]; + }; + } +} diff --git a/Common/DirectXTK12/Inc/WICTextureLoader.h b/Common/DirectXTK12/Inc/WICTextureLoader.h new file mode 100644 index 0000000..d09406b --- /dev/null +++ b/Common/DirectXTK12/Inc/WICTextureLoader.h @@ -0,0 +1,151 @@ +//-------------------------------------------------------------------------------------- +// File: WICTextureLoader.h +// +// Function for loading a WIC image and creating a Direct3D runtime texture for it +// (auto-generating mipmaps if possible) +// +// Note: Assumes application has already called CoInitializeEx +// +// Note these functions are useful for images created as simple 2D textures. For +// more complex resources, DDSTextureLoader is an excellent light-weight runtime loader. +// For a full-featured DDS file reader, writer, and texture processing pipeline see +// the 'Texconv' sample and the 'DirectXTex' library. +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#pragma once + +#ifdef _GAMING_XBOX_SCARLETT +#include +#elif (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) +#include +#elif defined(USING_DIRECTX_HEADERS) +#include +#include +#else +#include +#endif + +#include +#include +#include + +#pragma comment(lib,"uuid.lib") + + +namespace DirectX +{ + inline namespace DX12 + { + enum WIC_LOADER_FLAGS : uint32_t + { + WIC_LOADER_DEFAULT = 0, + WIC_LOADER_FORCE_SRGB = 0x1, + WIC_LOADER_IGNORE_SRGB = 0x2, + WIC_LOADER_SRGB_DEFAULT = 0x4, + WIC_LOADER_MIP_AUTOGEN = 0x8, + WIC_LOADER_MIP_RESERVE = 0x10, + WIC_LOADER_FIT_POW2 = 0x20, + WIC_LOADER_MAKE_SQUARE = 0x40, + WIC_LOADER_FORCE_RGBA32 = 0x80, + }; + } + + class ResourceUploadBatch; + + // Standard version + HRESULT __cdecl LoadWICTextureFromMemory( + _In_ ID3D12Device* d3dDevice, + _In_reads_bytes_(wicDataSize) const uint8_t* wicData, + size_t wicDataSize, + _Outptr_ ID3D12Resource** texture, + std::unique_ptr& decodedData, + D3D12_SUBRESOURCE_DATA& subresource, + size_t maxsize = 0) noexcept; + + HRESULT __cdecl LoadWICTextureFromFile( + _In_ ID3D12Device* d3dDevice, + _In_z_ const wchar_t* szFileName, + _Outptr_ ID3D12Resource** texture, + std::unique_ptr& decodedData, + D3D12_SUBRESOURCE_DATA& subresource, + size_t maxsize = 0) noexcept; + + // Standard version with resource upload + HRESULT __cdecl CreateWICTextureFromMemory( + _In_ ID3D12Device* d3dDevice, + ResourceUploadBatch& resourceUpload, + _In_reads_bytes_(wicDataSize) const uint8_t* wicData, + size_t wicDataSize, + _Outptr_ ID3D12Resource** texture, + bool generateMips = false, + size_t maxsize = 0); + + HRESULT __cdecl CreateWICTextureFromFile( + _In_ ID3D12Device* d3dDevice, + ResourceUploadBatch& resourceUpload, + _In_z_ const wchar_t* szFileName, + _Outptr_ ID3D12Resource** texture, + bool generateMips = false, + size_t maxsize = 0); + + // Extended version + HRESULT __cdecl LoadWICTextureFromMemoryEx( + _In_ ID3D12Device* d3dDevice, + _In_reads_bytes_(wicDataSize) const uint8_t* wicData, + size_t wicDataSize, + size_t maxsize, + D3D12_RESOURCE_FLAGS resFlags, + WIC_LOADER_FLAGS loadFlags, + _Outptr_ ID3D12Resource** texture, + std::unique_ptr& decodedData, + D3D12_SUBRESOURCE_DATA& subresource) noexcept; + + HRESULT __cdecl LoadWICTextureFromFileEx( + _In_ ID3D12Device* d3dDevice, + _In_z_ const wchar_t* szFileName, + size_t maxsize, + D3D12_RESOURCE_FLAGS resFlags, + WIC_LOADER_FLAGS loadFlags, + _Outptr_ ID3D12Resource** texture, + std::unique_ptr& decodedData, + D3D12_SUBRESOURCE_DATA& subresource) noexcept; + + // Extended version with resource upload + HRESULT __cdecl CreateWICTextureFromMemoryEx( + _In_ ID3D12Device* d3dDevice, + ResourceUploadBatch& resourceUpload, + _In_reads_bytes_(wicDataSize) const uint8_t* wicData, + size_t wicDataSize, + size_t maxsize, + D3D12_RESOURCE_FLAGS resFlags, + WIC_LOADER_FLAGS loadFlags, + _Outptr_ ID3D12Resource** texture); + + HRESULT __cdecl CreateWICTextureFromFileEx( + _In_ ID3D12Device* d3dDevice, + ResourceUploadBatch& resourceUpload, + _In_z_ const wchar_t* szFileName, + size_t maxsize, + D3D12_RESOURCE_FLAGS resFlags, + WIC_LOADER_FLAGS loadFlags, + _Outptr_ ID3D12Resource** texture); + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-dynamic-exception-spec" +#endif + + inline namespace DX12 + { + DEFINE_ENUM_FLAG_OPERATORS(WIC_LOADER_FLAGS); + } + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +} diff --git a/Common/DirectXTK12/Inc/XboxDDSTextureLoader.h b/Common/DirectXTK12/Inc/XboxDDSTextureLoader.h new file mode 100644 index 0000000..44282a5 --- /dev/null +++ b/Common/DirectXTK12/Inc/XboxDDSTextureLoader.h @@ -0,0 +1,97 @@ +//-------------------------------------------------------------------------------------- +// File: XboxDDSTextureLoader.h +// +// Functions for loading a DDS texture using the XBOX extended header and creating a +// Direct3D12.X runtime resource for it via the CreatePlacedResourceX API +// +// Note these functions will not load standard DDS files. Use the DDSTextureLoader +// module in the DirectXTex package or as part of the DirectXTK library to load +// these files which use standard Direct3D resource creation APIs. +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#pragma once + +#if !(defined(_XBOX_ONE) && defined(_TITLE)) && !defined(_GAMING_XBOX) +#error This module only supports Xbox exclusive apps +#endif + +#ifdef _GAMING_XBOX_SCARLETT +#include +#else +#include +#endif + +#ifdef _GAMING_XBOX +#pragma comment(lib,"xmem.lib") +#endif + +#include +#include + +#ifndef DDS_ALPHA_MODE_DEFINED +#define DDS_ALPHA_MODE_DEFINED +namespace DirectX +{ + enum DDS_ALPHA_MODE : uint32_t + { + DDS_ALPHA_MODE_UNKNOWN = 0, + DDS_ALPHA_MODE_STRAIGHT = 1, + DDS_ALPHA_MODE_PREMULTIPLIED = 2, + DDS_ALPHA_MODE_OPAQUE = 3, + DDS_ALPHA_MODE_CUSTOM = 4, + }; +} +#endif + +namespace Xbox +{ + using DirectX::DDS_ALPHA_MODE; + + // + // NOTE: Flush the GPU caches before using textures created + // with these functions. + // + // The simplest means of doing this is: + // + // // Load all your textures: + // CreateDDSTextureFrom... + // CreateDDSTextureFrom... + // CreateDDSTextureFrom... + // + // // Flush the GPU caches + // ID3D12CommandList::FlushPipelineX(D3D12XBOX_FLUSH_IDLE, 0, 0); + // + // // Now it's safe to use the textures + // ... Draw ... + // + // You may wish to consider more fine-grained flushes if + // creating textures at run-time. See the documentation for + // FlushPipelineX. + // + + HRESULT __cdecl CreateDDSTextureFromMemory( + _In_ ID3D12Device* d3dDevice, + _In_reads_bytes_(ddsDataSize) const uint8_t* ddsData, + _In_ size_t ddsDataSize, + _Outptr_opt_ ID3D12Resource** texture, + _Outptr_ void** grfxMemory, + _Out_opt_ DDS_ALPHA_MODE* alphaMode = nullptr, + _In_ bool forceSRGB = false, + _Out_opt_ bool* isCubeMap = nullptr) noexcept; + + HRESULT __cdecl CreateDDSTextureFromFile( + _In_ ID3D12Device* d3dDevice, + _In_z_ const wchar_t* szFileName, + _Outptr_opt_ ID3D12Resource** texture, + _Outptr_ void** grfxMemory, + _Out_opt_ DDS_ALPHA_MODE* alphaMode = nullptr, + _In_ bool forceSRGB = false, + _Out_opt_ bool* isCubeMap = nullptr) noexcept; + + void FreeDDSTextureMemory(_In_opt_ void* grfxMemory) noexcept; +} diff --git a/Common/DirectXTK12/LICENSE b/Common/DirectXTK12/LICENSE new file mode 100644 index 0000000..9e841e7 --- /dev/null +++ b/Common/DirectXTK12/LICENSE @@ -0,0 +1,21 @@ + MIT License + + Copyright (c) Microsoft Corporation. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE diff --git a/Common/DirectXTK12/NuGet.Config b/Common/DirectXTK12/NuGet.Config new file mode 100644 index 0000000..7e49cd0 --- /dev/null +++ b/Common/DirectXTK12/NuGet.Config @@ -0,0 +1,6 @@ + + + + + + diff --git a/Common/DirectXTK12/README.md b/Common/DirectXTK12/README.md new file mode 100644 index 0000000..01ed122 --- /dev/null +++ b/Common/DirectXTK12/README.md @@ -0,0 +1,145 @@ +![DirectX Logo](https://raw.githubusercontent.com/wiki/Microsoft/DirectXTK12/X_jpg.jpg) + +# DirectX Tool Kit for DirectX 12 + +http://go.microsoft.com/fwlink/?LinkID=615561 + +Copyright (c) Microsoft Corporation. + +**October 28, 2023** + +This package contains the "DirectX Tool Kit", a collection of helper classes for writing Direct3D 12 C++ code for Universal Windows Platform (UWP) apps for Windows 11 and Windows 10, game titles for Xbox Series X\|S and Xbox One, and Win32 desktop applications for Windows 11 and Windows 10. + +This code is designed to build with Visual Studio 2019 (16.11), Visual Studio 2022, clang for Windows v12 or later, or MinGW 12.2. Use of the Windows 10 May 2020 Update SDK ([19041](https://walbourn.github.io/windows-10-may-2020-update-sdk/)) or later is required for Visual Studio. + +These components are designed to work without requiring any content from the legacy DirectX SDK. For details, see [Where is the DirectX SDK?](https://aka.ms/dxsdk). + +## Directory Layout + +* ``Inc\`` + + + Public Header Files (in the DirectX C++ namespace): + + * Audio.h - low-level audio API using XAudio2 (DirectXTK for Audio public header) + * BufferHelpers.h - C++ helpers for creating D3D resources from CPU data + * CommonStates.h - common D3D state combinations + * DDSTextureLoader.h - light-weight DDS file texture loader + * DescriptorHeap.h - helper for managing DX12 descriptor heaps + * DirectXHelpers.h - misc C++ helpers for D3D programming + * EffectPipelineStateDescription.h - helper for creating PSOs + * Effects.h - set of built-in shaders for common rendering tasks + * GamePad.h - gamepad controller helper using Windows.Gaming.Input or GameInput + * GeometricPrimitive.h - draws basic shapes such as cubes and spheres + * GraphicsMemory.h - helper for managing dynamic graphics memory allocation + * Keyboard.h - keyboard state tracking helper + * Model.h - draws meshes loaded from .CMO, .SDKMESH, or .VBO files + * Mouse.h - mouse helper + * PostProcess.h - set of built-in shaders for common post-processing operations + * PrimitiveBatch.h - simple and efficient way to draw user primitives + * RenderTargetState.h - helper for communicating render target requirements when creating PSOs + * ResourceUploadBatch.h - helper for managing texture resource upload to the GPU + * ScreenGrab.h - light-weight screen shot saver + * SimpleMath.h - simplified C++ wrapper for DirectXMath + * SpriteBatch.h - simple & efficient 2D sprite rendering + * SpriteFont.h - bitmap based text rendering + * VertexTypes.h - structures for commonly used vertex data formats + * WICTextureLoader.h - WIC-based image file texture loader + * XboxDDSTextureLoader.h - Xbox exclusive apps variant of DDSTextureLoader + +* ``Src\`` + + + DirectXTK source files and internal implementation headers + +* ``Audio\`` + + + DirectXTK for Audio source files and internal implementation headers + +* ``build\`` + + + Contains YAML files for the build pipelines along with some miscellaneous build files and scripts. + +> MakeSpriteFont and XWBTool can be found in the [DirectX Tool Kit for DirectX 11](https://github.com/microsoft/DirectXTK) + +## Documentation + +Documentation is available on the [GitHub wiki](https://github.com/Microsoft/DirectXTK12/wiki). + +## Notices + +All content and source code for this package are subject to the terms of the [MIT License](https://github.com/microsoft/DirectXTK12/blob/main/LICENSE). + +For the latest version of DirectXTK12, bug reports, etc. please visit the project site on [GitHub](https://github.com/microsoft/DirectXTK12). + +## Comparisons to DirectX 11 Version + +* No support for Visual Studio Directed Graph Shader Language (DGSL) effect shaders (i.e. *DGSLEffect*). CMO files are loaded using BasicEffect or SkinnedEffect materials. + +* VertexTypes does not include VertexPositionNormalTangentColorTexture or VertexPositionNormalTangentColorTextureSkinning which were intended for use with the DGSL pipeline. + +* DirectX Tool Kit for DirectX 11 supports Feature Level 9.x, while DirectX 12 requires Direct3D Feature Level 11.0. There are no expected DirectX 12 drivers for any lower feature level devices. + +* The library assumes it is building for Windows 10 (aka ``_WIN32_WINNT=0x0A00``) so it makes use of XAudio 2.9 and WIC2 as well as DirectX 12. + +* DirectX Tool Kit for Audio, GamePad, Keyboard, Mouse, and SimpleMath are identical to the DirectX 11 version. + +## Release Notes + +* Starting with the February 2023 release, the Mouse class implementation of relative mouse movement was updated to accumulate changes between calls to ``GetState``. By default, each time you call ``GetState`` the deltas are reset which works for scenarios where you use relative movement but only call the method once per frame. If you call it more than once per frame, then add an explicit call to ``EndOfInputFrame`` to use an explicit reset model instead. + +* As of the September 2022 release, the library makes use of C++11 inline namespaces for differing types that have the same names in the DirectX 11 and DirectX 12 version of the *DirectX Tool Kit*. This provides a link-unique name such as ``DirectX::DX12::SpriteBatch`` that will appear in linker output messages. In most use cases, however, there is no need to add explicit ``DX12`` namespace resolution in client code. + +* In the June 2021 release or later, the VS 2019 projects of this library build the HLSL shaders with Shader Model 6 via DXC. Since the NuGet still builds using VS 2017, the build-in shaders in that version are currently Shader Model 5.1. See [this wiki page](https://github.com/microsoft/DirectXTK12/wiki/Shader-Model-6) for more information. The Microsoft GDK projects always use Shader Model 6. + +* Starting with the June 2020 release, this library makes use of [typed enum bitmask flags](https://walbourn.github.io/modern-c++-bitmask-types/) per the recommendation of the _C++ Standard_ section *17.5.2.1.3 Bitmask types*. This may have *breaking change* impacts to client code: + + * You cannot pass the ``0`` literal as your flags value. Instead you must make use of the appropriate default enum value: ``AudioEngine_Default``, ``SoundEffectInstance_Default``, ``ModelLoader_Clockwise``, ``DDS_LOADER_DEFAULT``, or ``WIC_LOADER_DEFAULT``. + + * Use the enum type instead of ``DWORD`` if building up flags values locally with bitmask operations. For example, ```WIC_LOADER_FLAGS flags = WIC_LOADER_DEFAULT; if (...) flags |= WIC_LOADER_FORCE_SRGB;``` + +* The UWP projects and the Win10 classic desktop project include configurations for the ARM64 platform. Building these requires installing the ARM64 toolset. + +* When using clang/LLVM for the ARM64 platform, the Windows 11 SDK ([22000](https://walbourn.github.io/windows-sdk-for-windows-11/)) or later is required. + +* The ``CompileShaders.cmd`` script must have Windows-style (CRLF) line-endings. If it is changed to Linux-style (LF) line-endings, it can fail to build all the required shaders. + +## Support + +For questions, consider using [Stack Overflow](https://stackoverflow.com/questions/tagged/directxtk) with the *directxtk* tag, or the [DirectX Discord Server](https://discord.gg/directx) in the *dx12-developers* channel. + +For bug reports and feature requests, please use GitHub [issues](https://github.com/microsoft/DirectXTK12/issues) for this project. + +## Contributing + +This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. + +When you submit a pull request, a CLA bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA. + +## Code of Conduct + +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more informatsion see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. + +## Trademarks + +This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft trademarks or logos is subject to and must follow [Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general). Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. Any use of third-party trademarks or logos are subject to those third-party's policies. + +## Credits + +The _DirectX Tool Kit for DirectX 11_ is the work of Shawn Hargreaves and Chuck Walbourn, with contributions from Aaron Rodriguez Hernandez and Dani Roman. + +The _DirectX Tool Kit for DirectX 12_ is the work of Pete Lewis, Justin Saunders, and Chuck Walbourn based heavily on the DirectX Tool Kit for DirectX 11. + +Thanks to Shanon Drone for the SDKMESH file format. + +Thanks to Adrian Tsai for the geodesic sphere implementation. + +Thanks to Garrett Serack for his help in creating the NuGet packages for DirectX Tool Kit. + +Thanks to Pete Lewis and Justin Saunders for the normal-mapped and PBR shaders implementation. + +Thanks for Travis Johnson for the mGPU support. + +Thanks to Roberto Sonnino for his help with the CMO format and the VS Starter Kit animation. + +Thanks to Richie Meyer for their contribution of Xbox PIX custom memory and type allocation tracking events support. + +Thanks to Andrew Farrier and Scott Matloff for their on-going help with code reviews. diff --git a/Common/DirectXTK12/SECURITY.md b/Common/DirectXTK12/SECURITY.md new file mode 100644 index 0000000..869fdfe --- /dev/null +++ b/Common/DirectXTK12/SECURITY.md @@ -0,0 +1,41 @@ + + +## Security + +Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). + +If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. + +## Reporting Security Issues + +**Please do not report security vulnerabilities through public GitHub issues.** + +Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). + +If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). + +You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). + +Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: + + * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) + * Full paths of source file(s) related to the manifestation of the issue + * The location of the affected source code (tag/branch/commit or direct URL) + * Any special configuration required to reproduce the issue + * Step-by-step instructions to reproduce the issue + * Proof-of-concept or exploit code (if possible) + * Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. + +## Preferred Languages + +We prefer all communications to be in English. + +## Policy + +Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). + + diff --git a/Common/DirectXTK12/Src/AlignedNew.h b/Common/DirectXTK12/Src/AlignedNew.h new file mode 100644 index 0000000..ea2aff5 --- /dev/null +++ b/Common/DirectXTK12/Src/AlignedNew.h @@ -0,0 +1,81 @@ +//-------------------------------------------------------------------------------------- +// File: AlignedNew.h +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248929 +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#pragma once + +#include +#include +#include + +#ifdef _WIN32 +#include +#endif + + +namespace DirectX +{ + // Derive from this to customize operator new and delete for + // types that have special heap alignment requirements. + // + // Example usage: + // + // XM_ALIGNED_STRUCT(16) MyAlignedType : public AlignedNew + + template + struct AlignedNew + { + // Allocate aligned memory. + static void* operator new (size_t size) + { + const size_t alignment = alignof(TDerived); + + static_assert(alignment > 8, "AlignedNew is only useful for types with > 8 byte alignment. Did you forget a __declspec(align) on TDerived?"); + static_assert(((alignment - 1) & alignment) == 0, "AlignedNew only works with power of two alignment"); + + #ifdef _WIN32 + void* ptr = _aligned_malloc(size, alignment); + #else + // This C++17 Standard Library function is currently NOT + // implemented for the Microsoft Standard C++ Library. + void* ptr = aligned_alloc(alignment, size); + #endif + if (!ptr) + throw std::bad_alloc(); + + return ptr; + } + + + // Free aligned memory. + static void operator delete (void* ptr) + { + #ifdef _WIN32 + _aligned_free(ptr); + #else + free(ptr); + #endif + } + + + // Array overloads. + static void* operator new[](size_t size) + { + static_assert((sizeof(TDerived) % alignof(TDerived) == 0), "AlignedNew expects type to be padded to the alignment"); + + return operator new(size); + } + + + static void operator delete[](void* ptr) + { + operator delete(ptr); + } + }; +} diff --git a/Common/DirectXTK12/Src/AlphaTestEffect.cpp b/Common/DirectXTK12/Src/AlphaTestEffect.cpp new file mode 100644 index 0000000..6e46cda --- /dev/null +++ b/Common/DirectXTK12/Src/AlphaTestEffect.cpp @@ -0,0 +1,519 @@ +//-------------------------------------------------------------------------------------- +// File: AlphaTestEffect.cpp +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "pch.h" +#include "EffectCommon.h" + +using namespace DirectX; + +namespace +{ + // Constant buffer layout. Must match the shader! + struct AlphaTestEffectConstants + { + XMVECTOR diffuseColor; + XMVECTOR alphaTest; + XMVECTOR fogColor; + XMVECTOR fogVector; + XMMATRIX worldViewProj; + }; + + static_assert((sizeof(AlphaTestEffectConstants) % 16) == 0, "CB size not padded correctly"); + + + // Traits type describes our characteristics to the EffectBase template. + struct AlphaTestEffectTraits + { + using ConstantBufferType = AlphaTestEffectConstants; + + static constexpr int VertexShaderCount = 4; + static constexpr int PixelShaderCount = 4; + static constexpr int ShaderPermutationCount = 8; + static constexpr int RootSignatureCount = 1; + }; +} + +// Internal AlphaTestEffect implementation class. +class AlphaTestEffect::Impl : public EffectBase +{ +public: + Impl(_In_ ID3D12Device* device, uint32_t effectFlags, const EffectPipelineStateDescription& pipelineDescription, + D3D12_COMPARISON_FUNC alphaFunction); + + enum RootParameterIndex + { + ConstantBuffer, + TextureSRV, + TextureSampler, + RootParameterCount + }; + + D3D12_COMPARISON_FUNC mAlphaFunction; + int referenceAlpha; + + EffectColor color; + + D3D12_GPU_DESCRIPTOR_HANDLE texture; + D3D12_GPU_DESCRIPTOR_HANDLE textureSampler; + + int GetPipelineStatePermutation(uint32_t effectFlags) const noexcept; + + void Apply(_In_ ID3D12GraphicsCommandList* commandList); +}; + + +#pragma region Shaders +// Include the precompiled shader code. +namespace +{ +#ifdef _GAMING_XBOX_SCARLETT +#include "XboxGamingScarlettAlphaTestEffect_VSAlphaTest.inc" +#include "XboxGamingScarlettAlphaTestEffect_VSAlphaTestNoFog.inc" +#include "XboxGamingScarlettAlphaTestEffect_VSAlphaTestVc.inc" +#include "XboxGamingScarlettAlphaTestEffect_VSAlphaTestVcNoFog.inc" + +#include "XboxGamingScarlettAlphaTestEffect_PSAlphaTestLtGt.inc" +#include "XboxGamingScarlettAlphaTestEffect_PSAlphaTestLtGtNoFog.inc" +#include "XboxGamingScarlettAlphaTestEffect_PSAlphaTestEqNe.inc" +#include "XboxGamingScarlettAlphaTestEffect_PSAlphaTestEqNeNoFog.inc" +#elif defined(_GAMING_XBOX) +#include "XboxGamingXboxOneAlphaTestEffect_VSAlphaTest.inc" +#include "XboxGamingXboxOneAlphaTestEffect_VSAlphaTestNoFog.inc" +#include "XboxGamingXboxOneAlphaTestEffect_VSAlphaTestVc.inc" +#include "XboxGamingXboxOneAlphaTestEffect_VSAlphaTestVcNoFog.inc" + +#include "XboxGamingXboxOneAlphaTestEffect_PSAlphaTestLtGt.inc" +#include "XboxGamingXboxOneAlphaTestEffect_PSAlphaTestLtGtNoFog.inc" +#include "XboxGamingXboxOneAlphaTestEffect_PSAlphaTestEqNe.inc" +#include "XboxGamingXboxOneAlphaTestEffect_PSAlphaTestEqNeNoFog.inc" +#elif defined(_XBOX_ONE) && defined(_TITLE) +#include "XboxOneAlphaTestEffect_VSAlphaTest.inc" +#include "XboxOneAlphaTestEffect_VSAlphaTestNoFog.inc" +#include "XboxOneAlphaTestEffect_VSAlphaTestVc.inc" +#include "XboxOneAlphaTestEffect_VSAlphaTestVcNoFog.inc" + +#include "XboxOneAlphaTestEffect_PSAlphaTestLtGt.inc" +#include "XboxOneAlphaTestEffect_PSAlphaTestLtGtNoFog.inc" +#include "XboxOneAlphaTestEffect_PSAlphaTestEqNe.inc" +#include "XboxOneAlphaTestEffect_PSAlphaTestEqNeNoFog.inc" +#else +#include "AlphaTestEffect_VSAlphaTest.inc" +#include "AlphaTestEffect_VSAlphaTestNoFog.inc" +#include "AlphaTestEffect_VSAlphaTestVc.inc" +#include "AlphaTestEffect_VSAlphaTestVcNoFog.inc" + +#include "AlphaTestEffect_PSAlphaTestLtGt.inc" +#include "AlphaTestEffect_PSAlphaTestLtGtNoFog.inc" +#include "AlphaTestEffect_PSAlphaTestEqNe.inc" +#include "AlphaTestEffect_PSAlphaTestEqNeNoFog.inc" +#endif +} + + +template<> +const D3D12_SHADER_BYTECODE EffectBase::VertexShaderBytecode[] = +{ + { AlphaTestEffect_VSAlphaTest, sizeof(AlphaTestEffect_VSAlphaTest) }, + { AlphaTestEffect_VSAlphaTestNoFog, sizeof(AlphaTestEffect_VSAlphaTestNoFog) }, + { AlphaTestEffect_VSAlphaTestVc, sizeof(AlphaTestEffect_VSAlphaTestVc) }, + { AlphaTestEffect_VSAlphaTestVcNoFog, sizeof(AlphaTestEffect_VSAlphaTestVcNoFog) }, +}; + + +template<> +const int EffectBase::VertexShaderIndices[] = +{ + 0, // lt/gt + 1, // lt/gt, no fog + 2, // lt/gt, vertex color + 3, // lt/gt, vertex color, no fog + + 0, // eq/ne + 1, // eq/ne, no fog + 2, // eq/ne, vertex color + 3, // eq/ne, vertex color, no fog +}; + + +template<> +const D3D12_SHADER_BYTECODE EffectBase::PixelShaderBytecode[] = +{ + { AlphaTestEffect_PSAlphaTestLtGt, sizeof(AlphaTestEffect_PSAlphaTestLtGt) }, + { AlphaTestEffect_PSAlphaTestLtGtNoFog, sizeof(AlphaTestEffect_PSAlphaTestLtGtNoFog) }, + { AlphaTestEffect_PSAlphaTestEqNe, sizeof(AlphaTestEffect_PSAlphaTestEqNe) }, + { AlphaTestEffect_PSAlphaTestEqNeNoFog, sizeof(AlphaTestEffect_PSAlphaTestEqNeNoFog) }, +}; + + +template<> +const int EffectBase::PixelShaderIndices[] = +{ + 0, // lt/gt + 1, // lt/gt, no fog + 0, // lt/gt, vertex color + 1, // lt/gt, vertex color, no fog + + 2, // eq/ne + 3, // eq/ne, no fog + 2, // eq/ne, vertex color + 3, // eq/ne, vertex color, no fog +}; +#pragma endregion + +// Global pool of per-device AlphaTestEffect resources. +template<> +SharedResourcePool::DeviceResources> EffectBase::deviceResourcesPool = {}; + +// Constructor. +AlphaTestEffect::Impl::Impl( + _In_ ID3D12Device* device, + uint32_t effectFlags, + const EffectPipelineStateDescription& pipelineDescription, + D3D12_COMPARISON_FUNC alphaFunction) + : EffectBase(device), + mAlphaFunction(alphaFunction), + referenceAlpha(0), + texture{}, + textureSampler{} +{ + static_assert(static_cast(std::size(EffectBase::VertexShaderIndices)) == AlphaTestEffectTraits::ShaderPermutationCount, "array/max mismatch"); + static_assert(static_cast(std::size(EffectBase::VertexShaderBytecode)) == AlphaTestEffectTraits::VertexShaderCount, "array/max mismatch"); + static_assert(static_cast(std::size(EffectBase::PixelShaderBytecode)) == AlphaTestEffectTraits::PixelShaderCount, "array/max mismatch"); + static_assert(static_cast(std::size(EffectBase::PixelShaderIndices)) == AlphaTestEffectTraits::ShaderPermutationCount, "array/max mismatch"); + + // Create root signature. + { + ENUM_FLAGS_CONSTEXPR D3D12_ROOT_SIGNATURE_FLAGS rootSignatureFlags = + D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT + | D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS + | D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS + | D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS +#ifdef _GAMING_XBOX_SCARLETT + | D3D12_ROOT_SIGNATURE_FLAG_DENY_AMPLIFICATION_SHADER_ROOT_ACCESS + | D3D12_ROOT_SIGNATURE_FLAG_DENY_MESH_SHADER_ROOT_ACCESS +#endif + ; + + const CD3DX12_DESCRIPTOR_RANGE textureRange(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0); + const CD3DX12_DESCRIPTOR_RANGE textureSamplerRange(D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, 1, 0); + + CD3DX12_ROOT_PARAMETER rootParameters[RootParameterIndex::RootParameterCount] = {}; + rootParameters[RootParameterIndex::TextureSRV].InitAsDescriptorTable(1, &textureRange, D3D12_SHADER_VISIBILITY_PIXEL); + rootParameters[RootParameterIndex::TextureSampler].InitAsDescriptorTable(1, &textureSamplerRange, D3D12_SHADER_VISIBILITY_PIXEL); + rootParameters[RootParameterIndex::ConstantBuffer].InitAsConstantBufferView(0, 0, D3D12_SHADER_VISIBILITY_ALL); + + CD3DX12_ROOT_SIGNATURE_DESC rsigDesc = {}; + rsigDesc.Init(static_cast(std::size(rootParameters)), rootParameters, 0, nullptr, rootSignatureFlags); + + mRootSignature = GetRootSignature(0, rsigDesc); + } + + assert(mRootSignature != nullptr); + + fog.enabled = (effectFlags & EffectFlags::Fog) != 0; + + if (effectFlags & EffectFlags::PerPixelLightingBit) + { + DebugTrace("ERROR: AlphaTestEffect does not implement EffectFlags::PerPixelLighting\n"); + throw std::invalid_argument("PerPixelLighting effect flag is invalid"); + } + else if (effectFlags & EffectFlags::Lighting) + { + DebugTrace("ERROR: AlphaTestEffect does not implement EffectFlags::Lighting\n"); + throw std::invalid_argument("Lighting effect flag is invalid"); + } + else if (effectFlags & EffectFlags::Instancing) + { + DebugTrace("ERROR: AlphaTestEffect does not implement EffectFlags::Instancing\n"); + throw std::invalid_argument("Instancing effect flag is invalid"); + } + + // Create pipeline state. + const int sp = GetPipelineStatePermutation(effectFlags); + assert(sp >= 0 && sp < AlphaTestEffectTraits::ShaderPermutationCount); + _Analysis_assume_(sp >= 0 && sp < AlphaTestEffectTraits::ShaderPermutationCount); + + const int vi = EffectBase::VertexShaderIndices[sp]; + assert(vi >= 0 && vi < AlphaTestEffectTraits::VertexShaderCount); + _Analysis_assume_(vi >= 0 && vi < AlphaTestEffectTraits::VertexShaderCount); + const int pi = EffectBase::PixelShaderIndices[sp]; + assert(pi >= 0 && pi < AlphaTestEffectTraits::PixelShaderCount); + _Analysis_assume_(pi >= 0 && pi < AlphaTestEffectTraits::PixelShaderCount); + + pipelineDescription.CreatePipelineState( + device, + mRootSignature, + EffectBase::VertexShaderBytecode[vi], + EffectBase::PixelShaderBytecode[pi], + mPipelineState.GetAddressOf()); + + SetDebugObjectName(mPipelineState.Get(), L"AlphaTestEffect"); +} + + +int AlphaTestEffect::Impl::GetPipelineStatePermutation(uint32_t effectFlags) const noexcept +{ + int permutation = 0; + + // Use optimized shaders if fog is disabled. + if (!fog.enabled) + { + permutation += 1; + } + + // Support vertex coloring? + if (effectFlags & EffectFlags::VertexColor) + { + permutation += 2; + } + + // Which alpha compare mode? + if (mAlphaFunction == D3D12_COMPARISON_FUNC_EQUAL || + mAlphaFunction == D3D12_COMPARISON_FUNC_NOT_EQUAL) + { + permutation += 4; + } + + return permutation; +} + + +// Sets our state onto the D3D device. +void AlphaTestEffect::Impl::Apply(_In_ ID3D12GraphicsCommandList* commandList) +{ + // Compute derived parameter values. + matrices.SetConstants(dirtyFlags, constants.worldViewProj); + fog.SetConstants(dirtyFlags, matrices.worldView, constants.fogVector); + color.SetConstants(dirtyFlags, constants.diffuseColor); + + UpdateConstants(); + + // Recompute the alpha test settings? + if (dirtyFlags & EffectDirtyFlags::AlphaTest) + { + // Convert reference alpha from 8 bit integer to 0-1 float format. + auto const reference = static_cast(referenceAlpha) / 255.0f; + + // Comparison tolerance of half the 8 bit integer precision. + constexpr float threshold = 0.5f / 255.0f; + + // What to do if the alpha comparison passes or fails. Positive accepts the pixel, negative clips it. + static const XMVECTORF32 selectIfTrue = { { { 1, -1 } } }; + static const XMVECTORF32 selectIfFalse = { { { -1, 1 } } }; + static const XMVECTORF32 selectNever = { { { -1, -1 } } }; + static const XMVECTORF32 selectAlways = { { { 1, 1 } } }; + + float compareTo; + XMVECTOR resultSelector; + + switch (mAlphaFunction) + { + case D3D12_COMPARISON_FUNC_LESS: + // Shader will evaluate: clip((a < x) ? z : w) + compareTo = reference - threshold; + resultSelector = selectIfTrue; + break; + + case D3D12_COMPARISON_FUNC_LESS_EQUAL: + // Shader will evaluate: clip((a < x) ? z : w) + compareTo = reference + threshold; + resultSelector = selectIfTrue; + break; + + case D3D12_COMPARISON_FUNC_GREATER_EQUAL: + // Shader will evaluate: clip((a < x) ? z : w) + compareTo = reference - threshold; + resultSelector = selectIfFalse; + break; + + case D3D12_COMPARISON_FUNC_GREATER: + // Shader will evaluate: clip((a < x) ? z : w) + compareTo = reference + threshold; + resultSelector = selectIfFalse; + break; + + case D3D12_COMPARISON_FUNC_EQUAL: + // Shader will evaluate: clip((abs(a - x) < y) ? z : w) + compareTo = reference; + resultSelector = selectIfTrue; + break; + + case D3D12_COMPARISON_FUNC_NOT_EQUAL: + // Shader will evaluate: clip((abs(a - x) < y) ? z : w) + compareTo = reference; + resultSelector = selectIfFalse; + break; + + case D3D12_COMPARISON_FUNC_NEVER: + // Shader will evaluate: clip((a < x) ? z : w) + compareTo = 0; + resultSelector = selectNever; + break; + + case D3D12_COMPARISON_FUNC_ALWAYS: + // Shader will evaluate: clip((a < x) ? z : w) + compareTo = 0; + resultSelector = selectAlways; + break; + + default: + throw std::runtime_error("Unknown alpha test function"); + } + + // x = compareTo, y = threshold, zw = resultSelector. + constants.alphaTest = XMVectorPermute<0, 1, 4, 5>(XMVectorSet(compareTo, threshold, 0, 0), resultSelector); + + dirtyFlags &= ~EffectDirtyFlags::AlphaTest; + dirtyFlags |= EffectDirtyFlags::ConstantBuffer; + } + + // Set the root signature + commandList->SetGraphicsRootSignature(mRootSignature); + + // Set the texture + if (!texture.ptr || !textureSampler.ptr) + { + DebugTrace("ERROR: Missing texture or sampler for AlphaTestEffect (texture %llu, sampler %llu)\n", texture.ptr, textureSampler.ptr); + throw std::runtime_error("AlphaTestEffect"); + } + + // **NOTE** If D3D asserts or crashes here, you probably need to call commandList->SetDescriptorHeaps() with the required descriptor heaps. + commandList->SetGraphicsRootDescriptorTable(RootParameterIndex::TextureSRV, texture); + commandList->SetGraphicsRootDescriptorTable(RootParameterIndex::TextureSampler, textureSampler); + + // Set constants + commandList->SetGraphicsRootConstantBufferView(RootParameterIndex::ConstantBuffer, GetConstantBufferGpuAddress()); + + // Set the pipeline state + commandList->SetPipelineState(EffectBase::mPipelineState.Get()); +} + +// Public constructor. +AlphaTestEffect::AlphaTestEffect( + _In_ ID3D12Device* device, + uint32_t effectFlags, + const EffectPipelineStateDescription& pipelineDescription, + D3D12_COMPARISON_FUNC alphaFunction) + : pImpl(std::make_unique(device, effectFlags, pipelineDescription, alphaFunction)) +{ +} + + +AlphaTestEffect::AlphaTestEffect(AlphaTestEffect&&) noexcept = default; +AlphaTestEffect& AlphaTestEffect::operator= (AlphaTestEffect&&) noexcept = default; +AlphaTestEffect::~AlphaTestEffect() = default; + + +// IEffect methods +void AlphaTestEffect::Apply(_In_ ID3D12GraphicsCommandList* commandList) +{ + pImpl->Apply(commandList); +} + + +// Camera settings +void XM_CALLCONV AlphaTestEffect::SetWorld(FXMMATRIX value) +{ + pImpl->matrices.world = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::WorldViewProj | EffectDirtyFlags::WorldInverseTranspose | EffectDirtyFlags::FogVector; +} + + +void XM_CALLCONV AlphaTestEffect::SetView(FXMMATRIX value) +{ + pImpl->matrices.view = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::WorldViewProj | EffectDirtyFlags::EyePosition | EffectDirtyFlags::FogVector; +} + + +void XM_CALLCONV AlphaTestEffect::SetProjection(FXMMATRIX value) +{ + pImpl->matrices.projection = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::WorldViewProj; +} + + +void XM_CALLCONV AlphaTestEffect::SetMatrices(FXMMATRIX world, CXMMATRIX view, CXMMATRIX projection) +{ + pImpl->matrices.world = world; + pImpl->matrices.view = view; + pImpl->matrices.projection = projection; + + pImpl->dirtyFlags |= EffectDirtyFlags::WorldViewProj | EffectDirtyFlags::WorldInverseTranspose | EffectDirtyFlags::EyePosition | EffectDirtyFlags::FogVector; +} + + +// Material settings +void XM_CALLCONV AlphaTestEffect::SetDiffuseColor(FXMVECTOR value) +{ + pImpl->color.diffuseColor = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::MaterialColor; +} + + +void AlphaTestEffect::SetAlpha(float value) +{ + pImpl->color.alpha = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::MaterialColor; +} + + +void XM_CALLCONV AlphaTestEffect::SetColorAndAlpha(FXMVECTOR value) +{ + pImpl->color.diffuseColor = value; + pImpl->color.alpha = XMVectorGetW(value); + + pImpl->dirtyFlags |= EffectDirtyFlags::MaterialColor; +} + + +// Fog settings. +void AlphaTestEffect::SetFogStart(float value) +{ + pImpl->fog.start = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::FogVector; +} + + +void AlphaTestEffect::SetFogEnd(float value) +{ + pImpl->fog.end = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::FogVector; +} + + +void XM_CALLCONV AlphaTestEffect::SetFogColor(FXMVECTOR value) +{ + pImpl->constants.fogColor = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; +} + + +// Texture settings. +void AlphaTestEffect::SetTexture(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor, D3D12_GPU_DESCRIPTOR_HANDLE samplerDescriptor) +{ + pImpl->texture = srvDescriptor; + pImpl->textureSampler = samplerDescriptor; +} + + +void AlphaTestEffect::SetReferenceAlpha(int value) +{ + pImpl->referenceAlpha = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::AlphaTest; +} diff --git a/Common/DirectXTK12/Src/BasicEffect.cpp b/Common/DirectXTK12/Src/BasicEffect.cpp new file mode 100644 index 0000000..4e2fb35 --- /dev/null +++ b/Common/DirectXTK12/Src/BasicEffect.cpp @@ -0,0 +1,782 @@ +//-------------------------------------------------------------------------------------- +// File: BasicEffect.cpp +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "pch.h" +#include "EffectCommon.h" + +using namespace DirectX; + +namespace +{ + // Constant buffer layout. Must match the shader! + struct BasicEffectConstants + { + XMVECTOR diffuseColor; + XMVECTOR emissiveColor; + XMVECTOR specularColorAndPower; + + XMVECTOR lightDirection[IEffectLights::MaxDirectionalLights]; + XMVECTOR lightDiffuseColor[IEffectLights::MaxDirectionalLights]; + XMVECTOR lightSpecularColor[IEffectLights::MaxDirectionalLights]; + + XMVECTOR eyePosition; + + XMVECTOR fogColor; + XMVECTOR fogVector; + + XMMATRIX world; + XMVECTOR worldInverseTranspose[3]; + XMMATRIX worldViewProj; + }; + + static_assert((sizeof(BasicEffectConstants) % 16) == 0, "CB size not padded correctly"); + + + // Traits type describes our characteristics to the EffectBase template. + struct BasicEffectTraits + { + using ConstantBufferType = BasicEffectConstants; + + static constexpr int VertexShaderCount = 24; + static constexpr int PixelShaderCount = 10; + static constexpr int ShaderPermutationCount = 40; + static constexpr int RootSignatureCount = 2; + }; +} + +// Internal BasicEffect implementation class. +class BasicEffect::Impl : public EffectBase +{ +public: + Impl(_In_ ID3D12Device* device, uint32_t effectFlags, const EffectPipelineStateDescription& pipelineDescription); + + enum RootParameterIndex + { + ConstantBuffer, + TextureSRV, + TextureSampler, + RootParameterCount + }; + + bool lightingEnabled; + bool textureEnabled; + + D3D12_GPU_DESCRIPTOR_HANDLE texture; + D3D12_GPU_DESCRIPTOR_HANDLE sampler; + + EffectLights lights; + + int GetPipelineStatePermutation(uint32_t effectFlags) const noexcept; + + void Apply(_In_ ID3D12GraphicsCommandList* commandList); +}; + + +#pragma region Shaders +// Include the precompiled shader code. +namespace +{ +#ifdef _GAMING_XBOX_SCARLETT +#include "XboxGamingScarlettBasicEffect_VSBasic.inc" +#include "XboxGamingScarlettBasicEffect_VSBasicNoFog.inc" +#include "XboxGamingScarlettBasicEffect_VSBasicVc.inc" +#include "XboxGamingScarlettBasicEffect_VSBasicVcNoFog.inc" +#include "XboxGamingScarlettBasicEffect_VSBasicTx.inc" +#include "XboxGamingScarlettBasicEffect_VSBasicTxNoFog.inc" +#include "XboxGamingScarlettBasicEffect_VSBasicTxVc.inc" +#include "XboxGamingScarlettBasicEffect_VSBasicTxVcNoFog.inc" + +#include "XboxGamingScarlettBasicEffect_VSBasicVertexLighting.inc" +#include "XboxGamingScarlettBasicEffect_VSBasicVertexLightingVc.inc" +#include "XboxGamingScarlettBasicEffect_VSBasicVertexLightingTx.inc" +#include "XboxGamingScarlettBasicEffect_VSBasicVertexLightingTxVc.inc" + +#include "XboxGamingScarlettBasicEffect_VSBasicPixelLighting.inc" +#include "XboxGamingScarlettBasicEffect_VSBasicPixelLightingVc.inc" +#include "XboxGamingScarlettBasicEffect_VSBasicPixelLightingTx.inc" +#include "XboxGamingScarlettBasicEffect_VSBasicPixelLightingTxVc.inc" + +#include "XboxGamingScarlettBasicEffect_VSBasicVertexLightingBn.inc" +#include "XboxGamingScarlettBasicEffect_VSBasicVertexLightingVcBn.inc" +#include "XboxGamingScarlettBasicEffect_VSBasicVertexLightingTxBn.inc" +#include "XboxGamingScarlettBasicEffect_VSBasicVertexLightingTxVcBn.inc" + +#include "XboxGamingScarlettBasicEffect_VSBasicPixelLightingBn.inc" +#include "XboxGamingScarlettBasicEffect_VSBasicPixelLightingVcBn.inc" +#include "XboxGamingScarlettBasicEffect_VSBasicPixelLightingTxBn.inc" +#include "XboxGamingScarlettBasicEffect_VSBasicPixelLightingTxVcBn.inc" + +#include "XboxGamingScarlettBasicEffect_PSBasic.inc" +#include "XboxGamingScarlettBasicEffect_PSBasicNoFog.inc" +#include "XboxGamingScarlettBasicEffect_PSBasicTx.inc" +#include "XboxGamingScarlettBasicEffect_PSBasicTxNoFog.inc" + +#include "XboxGamingScarlettBasicEffect_PSBasicVertexLighting.inc" +#include "XboxGamingScarlettBasicEffect_PSBasicVertexLightingNoFog.inc" +#include "XboxGamingScarlettBasicEffect_PSBasicVertexLightingTx.inc" +#include "XboxGamingScarlettBasicEffect_PSBasicVertexLightingTxNoFog.inc" + +#include "XboxGamingScarlettBasicEffect_PSBasicPixelLighting.inc" +#include "XboxGamingScarlettBasicEffect_PSBasicPixelLightingTx.inc" +#elif defined(_GAMING_XBOX) +#include "XboxGamingXboxOneBasicEffect_VSBasic.inc" +#include "XboxGamingXboxOneBasicEffect_VSBasicNoFog.inc" +#include "XboxGamingXboxOneBasicEffect_VSBasicVc.inc" +#include "XboxGamingXboxOneBasicEffect_VSBasicVcNoFog.inc" +#include "XboxGamingXboxOneBasicEffect_VSBasicTx.inc" +#include "XboxGamingXboxOneBasicEffect_VSBasicTxNoFog.inc" +#include "XboxGamingXboxOneBasicEffect_VSBasicTxVc.inc" +#include "XboxGamingXboxOneBasicEffect_VSBasicTxVcNoFog.inc" + +#include "XboxGamingXboxOneBasicEffect_VSBasicVertexLighting.inc" +#include "XboxGamingXboxOneBasicEffect_VSBasicVertexLightingVc.inc" +#include "XboxGamingXboxOneBasicEffect_VSBasicVertexLightingTx.inc" +#include "XboxGamingXboxOneBasicEffect_VSBasicVertexLightingTxVc.inc" + +#include "XboxGamingXboxOneBasicEffect_VSBasicPixelLighting.inc" +#include "XboxGamingXboxOneBasicEffect_VSBasicPixelLightingVc.inc" +#include "XboxGamingXboxOneBasicEffect_VSBasicPixelLightingTx.inc" +#include "XboxGamingXboxOneBasicEffect_VSBasicPixelLightingTxVc.inc" + +#include "XboxGamingXboxOneBasicEffect_VSBasicVertexLightingBn.inc" +#include "XboxGamingXboxOneBasicEffect_VSBasicVertexLightingVcBn.inc" +#include "XboxGamingXboxOneBasicEffect_VSBasicVertexLightingTxBn.inc" +#include "XboxGamingXboxOneBasicEffect_VSBasicVertexLightingTxVcBn.inc" + +#include "XboxGamingXboxOneBasicEffect_VSBasicPixelLightingBn.inc" +#include "XboxGamingXboxOneBasicEffect_VSBasicPixelLightingVcBn.inc" +#include "XboxGamingXboxOneBasicEffect_VSBasicPixelLightingTxBn.inc" +#include "XboxGamingXboxOneBasicEffect_VSBasicPixelLightingTxVcBn.inc" + +#include "XboxGamingXboxOneBasicEffect_PSBasic.inc" +#include "XboxGamingXboxOneBasicEffect_PSBasicNoFog.inc" +#include "XboxGamingXboxOneBasicEffect_PSBasicTx.inc" +#include "XboxGamingXboxOneBasicEffect_PSBasicTxNoFog.inc" + +#include "XboxGamingXboxOneBasicEffect_PSBasicVertexLighting.inc" +#include "XboxGamingXboxOneBasicEffect_PSBasicVertexLightingNoFog.inc" +#include "XboxGamingXboxOneBasicEffect_PSBasicVertexLightingTx.inc" +#include "XboxGamingXboxOneBasicEffect_PSBasicVertexLightingTxNoFog.inc" + +#include "XboxGamingXboxOneBasicEffect_PSBasicPixelLighting.inc" +#include "XboxGamingXboxOneBasicEffect_PSBasicPixelLightingTx.inc" +#elif defined(_XBOX_ONE) && defined(_TITLE) +#include "XboxOneBasicEffect_VSBasic.inc" +#include "XboxOneBasicEffect_VSBasicNoFog.inc" +#include "XboxOneBasicEffect_VSBasicVc.inc" +#include "XboxOneBasicEffect_VSBasicVcNoFog.inc" +#include "XboxOneBasicEffect_VSBasicTx.inc" +#include "XboxOneBasicEffect_VSBasicTxNoFog.inc" +#include "XboxOneBasicEffect_VSBasicTxVc.inc" +#include "XboxOneBasicEffect_VSBasicTxVcNoFog.inc" + +#include "XboxOneBasicEffect_VSBasicVertexLighting.inc" +#include "XboxOneBasicEffect_VSBasicVertexLightingVc.inc" +#include "XboxOneBasicEffect_VSBasicVertexLightingTx.inc" +#include "XboxOneBasicEffect_VSBasicVertexLightingTxVc.inc" + +#include "XboxOneBasicEffect_VSBasicPixelLighting.inc" +#include "XboxOneBasicEffect_VSBasicPixelLightingVc.inc" +#include "XboxOneBasicEffect_VSBasicPixelLightingTx.inc" +#include "XboxOneBasicEffect_VSBasicPixelLightingTxVc.inc" + +#include "XboxOneBasicEffect_VSBasicVertexLightingBn.inc" +#include "XboxOneBasicEffect_VSBasicVertexLightingVcBn.inc" +#include "XboxOneBasicEffect_VSBasicVertexLightingTxBn.inc" +#include "XboxOneBasicEffect_VSBasicVertexLightingTxVcBn.inc" + +#include "XboxOneBasicEffect_VSBasicPixelLightingBn.inc" +#include "XboxOneBasicEffect_VSBasicPixelLightingVcBn.inc" +#include "XboxOneBasicEffect_VSBasicPixelLightingTxBn.inc" +#include "XboxOneBasicEffect_VSBasicPixelLightingTxVcBn.inc" + +#include "XboxOneBasicEffect_PSBasic.inc" +#include "XboxOneBasicEffect_PSBasicNoFog.inc" +#include "XboxOneBasicEffect_PSBasicTx.inc" +#include "XboxOneBasicEffect_PSBasicTxNoFog.inc" + +#include "XboxOneBasicEffect_PSBasicVertexLighting.inc" +#include "XboxOneBasicEffect_PSBasicVertexLightingNoFog.inc" +#include "XboxOneBasicEffect_PSBasicVertexLightingTx.inc" +#include "XboxOneBasicEffect_PSBasicVertexLightingTxNoFog.inc" + +#include "XboxOneBasicEffect_PSBasicPixelLighting.inc" +#include "XboxOneBasicEffect_PSBasicPixelLightingTx.inc" +#else +#include "BasicEffect_VSBasic.inc" +#include "BasicEffect_VSBasicNoFog.inc" +#include "BasicEffect_VSBasicVc.inc" +#include "BasicEffect_VSBasicVcNoFog.inc" +#include "BasicEffect_VSBasicTx.inc" +#include "BasicEffect_VSBasicTxNoFog.inc" +#include "BasicEffect_VSBasicTxVc.inc" +#include "BasicEffect_VSBasicTxVcNoFog.inc" + +#include "BasicEffect_VSBasicVertexLighting.inc" +#include "BasicEffect_VSBasicVertexLightingVc.inc" +#include "BasicEffect_VSBasicVertexLightingTx.inc" +#include "BasicEffect_VSBasicVertexLightingTxVc.inc" + +#include "BasicEffect_VSBasicPixelLighting.inc" +#include "BasicEffect_VSBasicPixelLightingVc.inc" +#include "BasicEffect_VSBasicPixelLightingTx.inc" +#include "BasicEffect_VSBasicPixelLightingTxVc.inc" + +#include "BasicEffect_VSBasicVertexLightingBn.inc" +#include "BasicEffect_VSBasicVertexLightingVcBn.inc" +#include "BasicEffect_VSBasicVertexLightingTxBn.inc" +#include "BasicEffect_VSBasicVertexLightingTxVcBn.inc" + +#include "BasicEffect_VSBasicPixelLightingBn.inc" +#include "BasicEffect_VSBasicPixelLightingVcBn.inc" +#include "BasicEffect_VSBasicPixelLightingTxBn.inc" +#include "BasicEffect_VSBasicPixelLightingTxVcBn.inc" + +#include "BasicEffect_PSBasic.inc" +#include "BasicEffect_PSBasicNoFog.inc" +#include "BasicEffect_PSBasicTx.inc" +#include "BasicEffect_PSBasicTxNoFog.inc" + +#include "BasicEffect_PSBasicVertexLighting.inc" +#include "BasicEffect_PSBasicVertexLightingNoFog.inc" +#include "BasicEffect_PSBasicVertexLightingTx.inc" +#include "BasicEffect_PSBasicVertexLightingTxNoFog.inc" + +#include "BasicEffect_PSBasicPixelLighting.inc" +#include "BasicEffect_PSBasicPixelLightingTx.inc" +#endif +} + + +template<> +const D3D12_SHADER_BYTECODE EffectBase::VertexShaderBytecode[] = +{ + { BasicEffect_VSBasic, sizeof(BasicEffect_VSBasic) }, + { BasicEffect_VSBasicNoFog, sizeof(BasicEffect_VSBasicNoFog) }, + { BasicEffect_VSBasicVc, sizeof(BasicEffect_VSBasicVc) }, + { BasicEffect_VSBasicVcNoFog, sizeof(BasicEffect_VSBasicVcNoFog) }, + { BasicEffect_VSBasicTx, sizeof(BasicEffect_VSBasicTx) }, + { BasicEffect_VSBasicTxNoFog, sizeof(BasicEffect_VSBasicTxNoFog) }, + { BasicEffect_VSBasicTxVc, sizeof(BasicEffect_VSBasicTxVc) }, + { BasicEffect_VSBasicTxVcNoFog, sizeof(BasicEffect_VSBasicTxVcNoFog) }, + + { BasicEffect_VSBasicVertexLighting, sizeof(BasicEffect_VSBasicVertexLighting) }, + { BasicEffect_VSBasicVertexLightingVc, sizeof(BasicEffect_VSBasicVertexLightingVc) }, + { BasicEffect_VSBasicVertexLightingTx, sizeof(BasicEffect_VSBasicVertexLightingTx) }, + { BasicEffect_VSBasicVertexLightingTxVc, sizeof(BasicEffect_VSBasicVertexLightingTxVc) }, + + { BasicEffect_VSBasicPixelLighting, sizeof(BasicEffect_VSBasicPixelLighting) }, + { BasicEffect_VSBasicPixelLightingVc, sizeof(BasicEffect_VSBasicPixelLightingVc) }, + { BasicEffect_VSBasicPixelLightingTx, sizeof(BasicEffect_VSBasicPixelLightingTx) }, + { BasicEffect_VSBasicPixelLightingTxVc, sizeof(BasicEffect_VSBasicPixelLightingTxVc) }, + + { BasicEffect_VSBasicVertexLightingBn, sizeof(BasicEffect_VSBasicVertexLightingBn) }, + { BasicEffect_VSBasicVertexLightingVcBn, sizeof(BasicEffect_VSBasicVertexLightingVcBn) }, + { BasicEffect_VSBasicVertexLightingTxBn, sizeof(BasicEffect_VSBasicVertexLightingTxBn) }, + { BasicEffect_VSBasicVertexLightingTxVcBn, sizeof(BasicEffect_VSBasicVertexLightingTxVcBn) }, + + { BasicEffect_VSBasicPixelLightingBn, sizeof(BasicEffect_VSBasicPixelLightingBn) }, + { BasicEffect_VSBasicPixelLightingVcBn, sizeof(BasicEffect_VSBasicPixelLightingVcBn) }, + { BasicEffect_VSBasicPixelLightingTxBn, sizeof(BasicEffect_VSBasicPixelLightingTxBn) }, + { BasicEffect_VSBasicPixelLightingTxVcBn, sizeof(BasicEffect_VSBasicPixelLightingTxVcBn) }, +}; + + +template<> +const int EffectBase::VertexShaderIndices[] = +{ + 0, // basic + 1, // no fog + 2, // vertex color + 3, // vertex color, no fog + 4, // texture + 5, // texture, no fog + 6, // texture + vertex color + 7, // texture + vertex color, no fog + + 8, // vertex lighting + 8, // vertex lighting, no fog + 9, // vertex lighting + vertex color + 9, // vertex lighting + vertex color, no fog + 10, // vertex lighting + texture + 10, // vertex lighting + texture, no fog + 11, // vertex lighting + texture + vertex color + 11, // vertex lighting + texture + vertex color, no fog + + 12, // pixel lighting + 12, // pixel lighting, no fog + 13, // pixel lighting + vertex color + 13, // pixel lighting + vertex color, no fog + 14, // pixel lighting + texture + 14, // pixel lighting + texture, no fog + 15, // pixel lighting + texture + vertex color + 15, // pixel lighting + texture + vertex color, no fog + + 16, // vertex lighting (biased vertex normals) + 16, // vertex lighting (biased vertex normals), no fog + 17, // vertex lighting (biased vertex normals) + vertex color + 17, // vertex lighting (biased vertex normals) + vertex color, no fog + 18, // vertex lighting (biased vertex normals) + texture + 18, // vertex lighting (biased vertex normals) + texture, no fog + 19, // vertex lighting (biased vertex normals) + texture + vertex color + 19, // vertex lighting (biased vertex normals) + texture + vertex color, no fog + + 20, // pixel lighting (biased vertex normals) + 20, // pixel lighting (biased vertex normals), no fog + 21, // pixel lighting (biased vertex normals) + vertex color + 21, // pixel lighting (biased vertex normals) + vertex color, no fog + 22, // pixel lighting (biased vertex normals) + texture + 22, // pixel lighting (biased vertex normals) + texture, no fog + 23, // pixel lighting (biased vertex normals) + texture + vertex color + 23, // pixel lighting (biased vertex normals) + texture + vertex color, no fog +}; + + +template<> +const D3D12_SHADER_BYTECODE EffectBase::PixelShaderBytecode[] = +{ + { BasicEffect_PSBasic, sizeof(BasicEffect_PSBasic) }, + { BasicEffect_PSBasicNoFog, sizeof(BasicEffect_PSBasicNoFog) }, + { BasicEffect_PSBasicTx, sizeof(BasicEffect_PSBasicTx) }, + { BasicEffect_PSBasicTxNoFog, sizeof(BasicEffect_PSBasicTxNoFog) }, + + { BasicEffect_PSBasicVertexLighting, sizeof(BasicEffect_PSBasicVertexLighting) }, + { BasicEffect_PSBasicVertexLightingNoFog, sizeof(BasicEffect_PSBasicVertexLightingNoFog) }, + { BasicEffect_PSBasicVertexLightingTx, sizeof(BasicEffect_PSBasicVertexLightingTx) }, + { BasicEffect_PSBasicVertexLightingTxNoFog, sizeof(BasicEffect_PSBasicVertexLightingTxNoFog) }, + + { BasicEffect_PSBasicPixelLighting, sizeof(BasicEffect_PSBasicPixelLighting) }, + { BasicEffect_PSBasicPixelLightingTx, sizeof(BasicEffect_PSBasicPixelLightingTx) }, +}; + + +template<> +const int EffectBase::PixelShaderIndices[] = +{ + 0, // basic + 1, // no fog + 0, // vertex color + 1, // vertex color, no fog + 2, // texture + 3, // texture, no fog + 2, // texture + vertex color + 3, // texture + vertex color, no fog + + 4, // vertex lighting + 5, // vertex lighting, no fog + 4, // vertex lighting + vertex color + 5, // vertex lighting + vertex color, no fog + 6, // vertex lighting + texture + 7, // vertex lighting + texture, no fog + 6, // vertex lighting + texture + vertex color + 7, // vertex lighting + texture + vertex color, no fog + + 8, // pixel lighting + 8, // pixel lighting, no fog + 8, // pixel lighting + vertex color + 8, // pixel lighting + vertex color, no fog + 9, // pixel lighting + texture + 9, // pixel lighting + texture, no fog + 9, // pixel lighting + texture + vertex color + 9, // pixel lighting + texture + vertex color, no fog + + 4, // vertex lighting (biased vertex normals) + 5, // vertex lighting (biased vertex normals), no fog + 4, // vertex lighting (biased vertex normals) + vertex color + 5, // vertex lighting (biased vertex normals) + vertex color, no fog + 6, // vertex lighting (biased vertex normals) + texture + 7, // vertex lighting (biased vertex normals) + texture, no fog + 6, // vertex lighting (biased vertex normals) + texture + vertex color + 7, // vertex lighting (biased vertex normals) + texture + vertex color, no fog + + 8, // pixel lighting (biased vertex normals) + 8, // pixel lighting (biased vertex normals), no fog + 8, // pixel lighting (biased vertex normals) + vertex color + 8, // pixel lighting (biased vertex normals) + vertex color, no fog + 9, // pixel lighting (biased vertex normals) + texture + 9, // pixel lighting (biased vertex normals) + texture, no fog + 9, // pixel lighting (biased vertex normals) + texture + vertex color + 9, // pixel lighting (biased vertex normals) + texture + vertex color, no fog +}; +#pragma endregion + +// Global pool of per-device BasicEffect resources. +template<> +SharedResourcePool::DeviceResources> EffectBase::deviceResourcesPool = {}; + + +// Constructor. +BasicEffect::Impl::Impl( + _In_ ID3D12Device* device, + uint32_t effectFlags, + const EffectPipelineStateDescription& pipelineDescription) + : EffectBase(device), + texture{}, + sampler{} +{ + static_assert(static_cast(std::size(EffectBase::VertexShaderIndices)) == BasicEffectTraits::ShaderPermutationCount, "array/max mismatch"); + static_assert(static_cast(std::size(EffectBase::VertexShaderBytecode)) == BasicEffectTraits::VertexShaderCount, "array/max mismatch"); + static_assert(static_cast(std::size(EffectBase::PixelShaderBytecode)) == BasicEffectTraits::PixelShaderCount, "array/max mismatch"); + static_assert(static_cast(std::size(EffectBase::PixelShaderIndices)) == BasicEffectTraits::ShaderPermutationCount, "array/max mismatch"); + + if (effectFlags & EffectFlags::Instancing) + { + DebugTrace("ERROR: BasicEffect does not implement EffectFlags::Instancing\n"); + throw std::invalid_argument("Instancing effect flag is invalid"); + } + + lights.InitializeConstants(constants.specularColorAndPower, constants.lightDirection, constants.lightDiffuseColor, constants.lightSpecularColor); + + fog.enabled = (effectFlags & EffectFlags::Fog) != 0; + lightingEnabled = (effectFlags & EffectFlags::Lighting) != 0; + textureEnabled = (effectFlags & EffectFlags::Texture) != 0; + + // Create root signature. + { + ENUM_FLAGS_CONSTEXPR D3D12_ROOT_SIGNATURE_FLAGS rootSignatureFlags = + D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT + | D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS + | D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS + | D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS +#ifdef _GAMING_XBOX_SCARLETT + | D3D12_ROOT_SIGNATURE_FLAG_DENY_AMPLIFICATION_SHADER_ROOT_ACCESS + | D3D12_ROOT_SIGNATURE_FLAG_DENY_MESH_SHADER_ROOT_ACCESS +#endif + ; + + // Create root parameters and initialize first (constants) + CD3DX12_ROOT_PARAMETER rootParameters[RootParameterIndex::RootParameterCount] = {}; + rootParameters[RootParameterIndex::ConstantBuffer].InitAsConstantBufferView(0, 0, D3D12_SHADER_VISIBILITY_ALL); + + // Root parameter descriptor - conditionally initialized + CD3DX12_ROOT_SIGNATURE_DESC rsigDesc = {}; + + if (textureEnabled) + { + // Include texture and srv + const CD3DX12_DESCRIPTOR_RANGE textureSRV(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0); + const CD3DX12_DESCRIPTOR_RANGE textureSampler(D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, 1, 0); + + rootParameters[RootParameterIndex::TextureSRV].InitAsDescriptorTable(1, &textureSRV, D3D12_SHADER_VISIBILITY_PIXEL); + rootParameters[RootParameterIndex::TextureSampler].InitAsDescriptorTable(1, &textureSampler, D3D12_SHADER_VISIBILITY_PIXEL); + + // use all parameters + rsigDesc.Init(static_cast(std::size(rootParameters)), rootParameters, 0, nullptr, rootSignatureFlags); + + mRootSignature = GetRootSignature(1, rsigDesc); + } + else + { + // only use constant + rsigDesc.Init(1, rootParameters, 0, nullptr, rootSignatureFlags); + + mRootSignature = GetRootSignature(0, rsigDesc); + } + } + + assert(mRootSignature != nullptr); + + // Create pipeline state. + const int sp = GetPipelineStatePermutation(effectFlags); + assert(sp >= 0 && sp < BasicEffectTraits::ShaderPermutationCount); + _Analysis_assume_(sp >= 0 && sp < BasicEffectTraits::ShaderPermutationCount); + + const int vi = EffectBase::VertexShaderIndices[sp]; + assert(vi >= 0 && vi < BasicEffectTraits::VertexShaderCount); + _Analysis_assume_(vi >= 0 && vi < BasicEffectTraits::VertexShaderCount); + const int pi = EffectBase::PixelShaderIndices[sp]; + assert(pi >= 0 && pi < BasicEffectTraits::PixelShaderCount); + _Analysis_assume_(pi >= 0 && pi < BasicEffectTraits::PixelShaderCount); + + pipelineDescription.CreatePipelineState( + device, + mRootSignature, + EffectBase::VertexShaderBytecode[vi], + EffectBase::PixelShaderBytecode[pi], + mPipelineState.GetAddressOf()); + + SetDebugObjectName(mPipelineState.Get(), L"BasicEffect"); +} + + +int BasicEffect::Impl::GetPipelineStatePermutation(uint32_t effectFlags) const noexcept +{ + int permutation = 0; + + // Use optimized shaders if fog is disabled. + if (!fog.enabled) + { + permutation += 1; + } + + // Support vertex coloring? + if (effectFlags & EffectFlags::VertexColor) + { + permutation += 2; + } + + // Support texturing? + if (textureEnabled) + { + permutation += 4; + } + + if (lightingEnabled) + { + if (effectFlags & EffectFlags::PerPixelLightingBit) + { + // Do lighting in the pixel shader. + permutation += 16; + } + else + { + permutation += 8; + } + + if (effectFlags & EffectFlags::BiasedVertexNormals) + { + // Compressed normals need to be scaled and biased in the vertex shader. + permutation += 16; + } + } + + return permutation; +} + + +// Sets our state onto the D3D device. +void BasicEffect::Impl::Apply(_In_ ID3D12GraphicsCommandList* commandList) +{ + // Compute derived parameter values. + matrices.SetConstants(dirtyFlags, constants.worldViewProj); + fog.SetConstants(dirtyFlags, matrices.worldView, constants.fogVector); + lights.SetConstants(dirtyFlags, matrices, constants.world, constants.worldInverseTranspose, constants.eyePosition, constants.diffuseColor, constants.emissiveColor, lightingEnabled); + + UpdateConstants(); + + // Set the root signature + commandList->SetGraphicsRootSignature(mRootSignature); + + // Set the texture + if (textureEnabled) + { + if (!texture.ptr || !sampler.ptr) + { + DebugTrace("ERROR: Missing texture or sampler for BasicEffect (texture %llu, sampler %llu)\n", texture.ptr, sampler.ptr); + throw std::runtime_error("BasicEffect"); + } + + // **NOTE** If D3D asserts or crashes here, you probably need to call commandList->SetDescriptorHeaps() with the required descriptor heaps. + commandList->SetGraphicsRootDescriptorTable(RootParameterIndex::TextureSRV, texture); + commandList->SetGraphicsRootDescriptorTable(RootParameterIndex::TextureSampler, sampler); + } + + // Set constants + commandList->SetGraphicsRootConstantBufferView(RootParameterIndex::ConstantBuffer, GetConstantBufferGpuAddress()); + + // Set the pipeline state + commandList->SetPipelineState(EffectBase::mPipelineState.Get()); +} + + +// Public constructor. +BasicEffect::BasicEffect( + _In_ ID3D12Device* device, + uint32_t effectFlags, + const EffectPipelineStateDescription& pipelineDescription) + : pImpl(std::make_unique(device, effectFlags, pipelineDescription)) +{ +} + + +// Move constructor. +BasicEffect::BasicEffect(BasicEffect&&) noexcept = default; +BasicEffect& BasicEffect::operator= (BasicEffect&&) noexcept = default; +BasicEffect::~BasicEffect() = default; + + +// IEffect methods +void BasicEffect::Apply(_In_ ID3D12GraphicsCommandList* commandList) +{ + pImpl->Apply(commandList); +} + + +// Camera settings +void XM_CALLCONV BasicEffect::SetWorld(FXMMATRIX value) +{ + pImpl->matrices.world = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::WorldViewProj | EffectDirtyFlags::WorldInverseTranspose | EffectDirtyFlags::FogVector; +} + + +void XM_CALLCONV BasicEffect::SetView(FXMMATRIX value) +{ + pImpl->matrices.view = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::WorldViewProj | EffectDirtyFlags::EyePosition | EffectDirtyFlags::FogVector; +} + + +void XM_CALLCONV BasicEffect::SetProjection(FXMMATRIX value) +{ + pImpl->matrices.projection = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::WorldViewProj; +} + + +void XM_CALLCONV BasicEffect::SetMatrices(FXMMATRIX world, CXMMATRIX view, CXMMATRIX projection) +{ + pImpl->matrices.world = world; + pImpl->matrices.view = view; + pImpl->matrices.projection = projection; + + pImpl->dirtyFlags |= EffectDirtyFlags::WorldViewProj | EffectDirtyFlags::WorldInverseTranspose | EffectDirtyFlags::EyePosition | EffectDirtyFlags::FogVector; +} + + +// Material settings +void XM_CALLCONV BasicEffect::SetDiffuseColor(FXMVECTOR value) +{ + pImpl->lights.diffuseColor = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::MaterialColor; +} + + +void XM_CALLCONV BasicEffect::SetEmissiveColor(FXMVECTOR value) +{ + pImpl->lights.emissiveColor = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::MaterialColor; +} + + +void XM_CALLCONV BasicEffect::SetSpecularColor(FXMVECTOR value) +{ + // Set xyz to new value, but preserve existing w (specular power). + pImpl->constants.specularColorAndPower = XMVectorSelect(pImpl->constants.specularColorAndPower, value, g_XMSelect1110); + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; +} + + +void BasicEffect::SetSpecularPower(float value) +{ + // Set w to new value, but preserve existing xyz (specular color). + pImpl->constants.specularColorAndPower = XMVectorSetW(pImpl->constants.specularColorAndPower, value); + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; +} + + +void BasicEffect::DisableSpecular() +{ + // Set specular color to black, power to 1 + // Note: Don't use a power of 0 or the shader will generate strange highlights on non-specular materials + + pImpl->constants.specularColorAndPower = g_XMIdentityR3; + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; +} + + +void BasicEffect::SetAlpha(float value) +{ + pImpl->lights.alpha = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::MaterialColor; +} + + +void XM_CALLCONV BasicEffect::SetColorAndAlpha(FXMVECTOR value) +{ + pImpl->lights.diffuseColor = value; + pImpl->lights.alpha = XMVectorGetW(value); + + pImpl->dirtyFlags |= EffectDirtyFlags::MaterialColor; +} + + +// Light settings +void XM_CALLCONV BasicEffect::SetAmbientLightColor(FXMVECTOR value) +{ + pImpl->lights.ambientLightColor = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::MaterialColor; +} + + +void BasicEffect::SetLightEnabled(int whichLight, bool value) +{ + pImpl->dirtyFlags |= pImpl->lights.SetLightEnabled(whichLight, value, pImpl->constants.lightDiffuseColor, pImpl->constants.lightSpecularColor); +} + + +void XM_CALLCONV BasicEffect::SetLightDirection(int whichLight, FXMVECTOR value) +{ + EffectLights::ValidateLightIndex(whichLight); + + pImpl->constants.lightDirection[whichLight] = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; +} + + +void XM_CALLCONV BasicEffect::SetLightDiffuseColor(int whichLight, FXMVECTOR value) +{ + pImpl->dirtyFlags |= pImpl->lights.SetLightDiffuseColor(whichLight, value, pImpl->constants.lightDiffuseColor); +} + + +void XM_CALLCONV BasicEffect::SetLightSpecularColor(int whichLight, FXMVECTOR value) +{ + pImpl->dirtyFlags |= pImpl->lights.SetLightSpecularColor(whichLight, value, pImpl->constants.lightSpecularColor); +} + + +void BasicEffect::EnableDefaultLighting() +{ + EffectLights::EnableDefaultLighting(this); +} + + +// Fog settings. +void BasicEffect::SetFogStart(float value) +{ + pImpl->fog.start = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::FogVector; +} + + +void BasicEffect::SetFogEnd(float value) +{ + pImpl->fog.end = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::FogVector; +} + + +void XM_CALLCONV BasicEffect::SetFogColor(FXMVECTOR value) +{ + pImpl->constants.fogColor = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; +} + + +// Texture settings. +void BasicEffect::SetTexture(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor, D3D12_GPU_DESCRIPTOR_HANDLE samplerDescriptor) +{ + pImpl->texture = srvDescriptor; + pImpl->sampler = samplerDescriptor; +} diff --git a/Common/DirectXTK12/Src/BasicPostProcess.cpp b/Common/DirectXTK12/Src/BasicPostProcess.cpp new file mode 100644 index 0000000..3cccb12 --- /dev/null +++ b/Common/DirectXTK12/Src/BasicPostProcess.cpp @@ -0,0 +1,616 @@ +//-------------------------------------------------------------------------------------- +// File: BasicPostProcess.cpp +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "pch.h" +#include "PostProcess.h" + +#include "AlignedNew.h" +#include "CommonStates.h" +#include "DemandCreate.h" +#include "DirectXHelpers.h" +#include "EffectPipelineStateDescription.h" +#include "GraphicsMemory.h" +#include "PlatformHelpers.h" +#include "SharedResourcePool.h" + +using namespace DirectX; + +using Microsoft::WRL::ComPtr; + +namespace +{ + constexpr int c_MaxSamples = 16; + + constexpr int Dirty_ConstantBuffer = 0x01; + constexpr int Dirty_Parameters = 0x02; + + constexpr int RootSignatureCount = 2; + + // Constant buffer layout. Must match the shader! + XM_ALIGNED_STRUCT(16) PostProcessConstants + { + XMVECTOR sampleOffsets[c_MaxSamples]; + XMVECTOR sampleWeights[c_MaxSamples]; + }; + + static_assert((sizeof(PostProcessConstants) % 16) == 0, "CB size not padded correctly"); + + // 2-parameter Gaussian distribution given standard deviation (rho) + inline float GaussianDistribution(float x, float y, float rho) noexcept + { + return expf(-(x * x + y * y) / (2 * rho * rho)) / sqrtf(2 * XM_PI * rho * rho); + } +} + +#pragma region Shaders +// Include the precompiled shader code. +namespace +{ +#ifdef _GAMING_XBOX_SCARLETT +#include "XboxGamingScarlettPostProcess_VSQuadNoCB.inc" +#include "XboxGamingScarlettPostProcess_VSQuad.inc" + +#include "XboxGamingScarlettPostProcess_PSCopy.inc" +#include "XboxGamingScarlettPostProcess_PSMonochrome.inc" +#include "XboxGamingScarlettPostProcess_PSSepia.inc" +#include "XboxGamingScarlettPostProcess_PSDownScale2x2.inc" +#include "XboxGamingScarlettPostProcess_PSDownScale4x4.inc" +#include "XboxGamingScarlettPostProcess_PSGaussianBlur5x5.inc" +#include "XboxGamingScarlettPostProcess_PSBloomExtract.inc" +#include "XboxGamingScarlettPostProcess_PSBloomBlur.inc" +#elif defined(_GAMING_XBOX) +#include "XboxGamingXboxOnePostProcess_VSQuadNoCB.inc" +#include "XboxGamingXboxOnePostProcess_VSQuad.inc" + +#include "XboxGamingXboxOnePostProcess_PSCopy.inc" +#include "XboxGamingXboxOnePostProcess_PSMonochrome.inc" +#include "XboxGamingXboxOnePostProcess_PSSepia.inc" +#include "XboxGamingXboxOnePostProcess_PSDownScale2x2.inc" +#include "XboxGamingXboxOnePostProcess_PSDownScale4x4.inc" +#include "XboxGamingXboxOnePostProcess_PSGaussianBlur5x5.inc" +#include "XboxGamingXboxOnePostProcess_PSBloomExtract.inc" +#include "XboxGamingXboxOnePostProcess_PSBloomBlur.inc" +#elif defined(_XBOX_ONE) && defined(_TITLE) +#include "XboxOnePostProcess_VSQuadNoCB.inc" +#include "XboxOnePostProcess_VSQuad.inc" + +#include "XboxOnePostProcess_PSCopy.inc" +#include "XboxOnePostProcess_PSMonochrome.inc" +#include "XboxOnePostProcess_PSSepia.inc" +#include "XboxOnePostProcess_PSDownScale2x2.inc" +#include "XboxOnePostProcess_PSDownScale4x4.inc" +#include "XboxOnePostProcess_PSGaussianBlur5x5.inc" +#include "XboxOnePostProcess_PSBloomExtract.inc" +#include "XboxOnePostProcess_PSBloomBlur.inc" +#else +#include "PostProcess_VSQuadNoCB.inc" +#include "PostProcess_VSQuad.inc" + +#include "PostProcess_PSCopy.inc" +#include "PostProcess_PSMonochrome.inc" +#include "PostProcess_PSSepia.inc" +#include "PostProcess_PSDownScale2x2.inc" +#include "PostProcess_PSDownScale4x4.inc" +#include "PostProcess_PSGaussianBlur5x5.inc" +#include "PostProcess_PSBloomExtract.inc" +#include "PostProcess_PSBloomBlur.inc" +#endif +} + +namespace +{ + const D3D12_SHADER_BYTECODE vertexShader[] = + { + { PostProcess_VSQuadNoCB, sizeof(PostProcess_VSQuadNoCB) }, + { PostProcess_VSQuad, sizeof(PostProcess_VSQuad) }, + }; + + const D3D12_SHADER_BYTECODE pixelShaders[] = + { + { PostProcess_PSCopy, sizeof(PostProcess_PSCopy) }, + { PostProcess_PSMonochrome, sizeof(PostProcess_PSMonochrome) }, + { PostProcess_PSSepia, sizeof(PostProcess_PSSepia) }, + { PostProcess_PSDownScale2x2, sizeof(PostProcess_PSDownScale2x2) }, + { PostProcess_PSDownScale4x4, sizeof(PostProcess_PSDownScale4x4) }, + { PostProcess_PSGaussianBlur5x5, sizeof(PostProcess_PSGaussianBlur5x5) }, + { PostProcess_PSBloomExtract, sizeof(PostProcess_PSBloomExtract) }, + { PostProcess_PSBloomBlur, sizeof(PostProcess_PSBloomBlur) }, + }; + + static_assert(static_cast(std::size(pixelShaders)) == BasicPostProcess::Effect_Max, "array/max mismatch"); + + // Factory for lazily instantiating shared root signatures. + class DeviceResources + { + public: + DeviceResources(_In_ ID3D12Device* device) noexcept + : mDevice(device), + mRootSignature{}, + mMutex{} + { } + + ID3D12RootSignature* GetRootSignature(int slot, const D3D12_ROOT_SIGNATURE_DESC& desc) + { + assert(slot >= 0 && slot < RootSignatureCount); + _Analysis_assume_(slot >= 0 && slot < RootSignatureCount); + + return DemandCreate(mRootSignature[slot], mMutex, [&](ID3D12RootSignature** pResult) noexcept -> HRESULT + { + HRESULT hr = CreateRootSignature(mDevice.Get(), &desc, pResult); + + if (SUCCEEDED(hr)) + SetDebugObjectName(*pResult, L"BasicPostProcess"); + + return hr; + }); + } + + ID3D12Device* GetDevice() const noexcept { return mDevice.Get(); } + + protected: + ComPtr mDevice; + ComPtr mRootSignature[RootSignatureCount]; + std::mutex mMutex; + }; +} +#pragma endregion + +class BasicPostProcess::Impl : public AlignedNew +{ +public: + Impl(_In_ ID3D12Device* device, const RenderTargetState& rtState, Effect ifx); + + void Process(_In_ ID3D12GraphicsCommandList* commandList); + + void SetDirtyFlag() noexcept { mDirtyFlags = INT_MAX; } + + enum RootParameterIndex + { + TextureSRV, + ConstantBuffer, + RootParameterCount + }; + + // Fields. + BasicPostProcess::Effect fx; + PostProcessConstants constants; + D3D12_GPU_DESCRIPTOR_HANDLE texture; + unsigned texWidth; + unsigned texHeight; + float guassianMultiplier; + float bloomSize; + float bloomBrightness; + float bloomThreshold; + bool bloomHorizontal; + +private: + bool mUseConstants; + int mDirtyFlags; + + void DownScale2x2(); + void DownScale4x4(); + void GaussianBlur5x5(float multiplier); + void Bloom(bool horizontal, float size, float brightness); + + // D3D constant buffer holds a copy of the same data as the public 'constants' field. + GraphicsResource mConstantBuffer; + + // Per instance cache of PSOs, populated with variants for each shader & layout + ComPtr mPipelineState; + + // Per instance root signature + ID3D12RootSignature* mRootSignature; + + // Per-device resources. + std::shared_ptr mDeviceResources; + + static SharedResourcePool deviceResourcesPool; +}; + + +// Global pool of per-device BasicPostProcess resources. +SharedResourcePool BasicPostProcess::Impl::deviceResourcesPool; + + +// Constructor. +BasicPostProcess::Impl::Impl(_In_ ID3D12Device* device, const RenderTargetState& rtState, Effect ifx) + : fx(ifx), + constants{}, + texture{}, + texWidth(0), + texHeight(0), + guassianMultiplier(1.f), + bloomSize(1.f), + bloomBrightness(1.f), + bloomThreshold(0.25f), + bloomHorizontal(true), + mDirtyFlags(INT_MAX), + mDeviceResources(deviceResourcesPool.DemandCreate(device)) +{ + if (ifx >= Effect_Max) + throw std::invalid_argument("Effect not defined"); + + switch (ifx) + { + case Copy: + case Monochrome: + case Sepia: + // These shaders don't use the constant buffer + mUseConstants = false; + break; + + default: + mUseConstants = true; + break; + } + + // Create root signature. + { + ENUM_FLAGS_CONSTEXPR D3D12_ROOT_SIGNATURE_FLAGS rootSignatureFlags = + D3D12_ROOT_SIGNATURE_FLAG_DENY_VERTEX_SHADER_ROOT_ACCESS + | D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS + | D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS + | D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS +#ifdef _GAMING_XBOX_SCARLETT + | D3D12_ROOT_SIGNATURE_FLAG_DENY_AMPLIFICATION_SHADER_ROOT_ACCESS + | D3D12_ROOT_SIGNATURE_FLAG_DENY_MESH_SHADER_ROOT_ACCESS +#endif + ; + + const CD3DX12_DESCRIPTOR_RANGE textureSRVs(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0); + + // Same as CommonStates::StaticLinearClamp + const CD3DX12_STATIC_SAMPLER_DESC sampler( + 0, // register + D3D12_FILTER_MIN_MAG_MIP_LINEAR, + D3D12_TEXTURE_ADDRESS_MODE_CLAMP, + D3D12_TEXTURE_ADDRESS_MODE_CLAMP, + D3D12_TEXTURE_ADDRESS_MODE_CLAMP, + 0.f, + 16, + D3D12_COMPARISON_FUNC_LESS_EQUAL, + D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE, + 0.f, + D3D12_FLOAT32_MAX, + D3D12_SHADER_VISIBILITY_PIXEL); + + CD3DX12_ROOT_PARAMETER rootParameters[RootParameterIndex::RootParameterCount] = {}; + rootParameters[RootParameterIndex::TextureSRV].InitAsDescriptorTable(1, &textureSRVs, D3D12_SHADER_VISIBILITY_PIXEL); + + // Root parameter descriptor - conditionally initialized + CD3DX12_ROOT_SIGNATURE_DESC rsigDesc = {}; + + if (mUseConstants) + { + // Include constant buffer + rootParameters[RootParameterIndex::ConstantBuffer].InitAsConstantBufferView(0, 0, D3D12_SHADER_VISIBILITY_PIXEL); + + // use all parameters + rsigDesc.Init(static_cast(std::size(rootParameters)), rootParameters, 1, &sampler, rootSignatureFlags); + + mRootSignature = mDeviceResources->GetRootSignature(1, rsigDesc); + } + else + { + // only use constant + rsigDesc.Init(1, rootParameters, 1, &sampler, rootSignatureFlags); + + mRootSignature = mDeviceResources->GetRootSignature(0, rsigDesc); + } + } + + assert(mRootSignature != nullptr); + + // Create pipeline state. + const EffectPipelineStateDescription psd(nullptr, + CommonStates::Opaque, + CommonStates::DepthNone, + CommonStates::CullNone, + rtState, + D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE); + + psd.CreatePipelineState( + device, + mRootSignature, + vertexShader[mUseConstants ? 1 : 0], + pixelShaders[ifx], + mPipelineState.GetAddressOf()); + + SetDebugObjectName(mPipelineState.Get(), L"BasicPostProcess"); +} + + +// Sets our state onto the D3D device. +void BasicPostProcess::Impl::Process(_In_ ID3D12GraphicsCommandList* commandList) +{ + // Set the root signature. + commandList->SetGraphicsRootSignature(mRootSignature); + + // Set the texture. + if (!texture.ptr) + { + DebugTrace("ERROR: Missing texture for BasicPostProcess (texture %llu)\n", texture.ptr); + throw std::runtime_error("BasicPostProcess"); + } + commandList->SetGraphicsRootDescriptorTable(RootParameterIndex::TextureSRV, texture); + + // Set constants. + if (mUseConstants) + { + if (mDirtyFlags & Dirty_Parameters) + { + mDirtyFlags &= ~Dirty_Parameters; + mDirtyFlags |= Dirty_ConstantBuffer; + + switch (fx) + { + case DownScale_2x2: + DownScale2x2(); + break; + + case DownScale_4x4: + DownScale4x4(); + break; + + case GaussianBlur_5x5: + GaussianBlur5x5(guassianMultiplier); + break; + + case BloomExtract: + constants.sampleWeights[0] = XMVectorReplicate(bloomThreshold); + break; + + case BloomBlur: + Bloom(bloomHorizontal, bloomSize, bloomBrightness); + break; + + default: + break; + } + } + + if (mDirtyFlags & Dirty_ConstantBuffer) + { + mDirtyFlags &= ~Dirty_ConstantBuffer; + mConstantBuffer = GraphicsMemory::Get(mDeviceResources->GetDevice()).AllocateConstant(constants); + } + + commandList->SetGraphicsRootConstantBufferView(RootParameterIndex::ConstantBuffer, mConstantBuffer.GpuAddress()); + } + + // Set the pipeline state. + commandList->SetPipelineState(mPipelineState.Get()); + + // Draw quad. + commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + commandList->DrawInstanced(3, 1, 0, 0); +} + + +void BasicPostProcess::Impl::DownScale2x2() +{ + mUseConstants = true; + + if (!texWidth || !texHeight) + { + throw std::logic_error("Call SetSourceTexture before setting post-process effect"); + } + + const float tu = 1.0f / float(texWidth); + const float tv = 1.0f / float(texHeight); + + // Sample from the 4 surrounding points. Since the center point will be in the exact + // center of 4 texels, a 0.5f offset is needed to specify a texel center. + auto ptr = reinterpret_cast(constants.sampleOffsets); + for (int y = 0; y < 2; ++y) + { + for (int x = 0; x < 2; ++x) + { + ptr->x = (float(x) - 0.5f) * tu; + ptr->y = (float(y) - 0.5f) * tv; + ++ptr; + } + } +} + + +void BasicPostProcess::Impl::DownScale4x4() +{ + mUseConstants = true; + + if (!texWidth || !texHeight) + { + throw std::logic_error("Call SetSourceTexture before setting post-process effect"); + } + + const float tu = 1.0f / float(texWidth); + const float tv = 1.0f / float(texHeight); + + // Sample from the 16 surrounding points. Since the center point will be in the + // exact center of 16 texels, a 1.5f offset is needed to specify a texel center. + auto ptr = reinterpret_cast(constants.sampleOffsets); + for (int y = 0; y < 4; ++y) + { + for (int x = 0; x < 4; ++x) + { + ptr->x = (float(x) - 1.5f) * tu; + ptr->y = (float(y) - 1.5f) * tv; + ++ptr; + } + } + +} + + +void BasicPostProcess::Impl::GaussianBlur5x5(float multiplier) +{ + mUseConstants = true; + + if (!texWidth || !texHeight) + { + throw std::logic_error("Call SetSourceTexture before setting post-process effect"); + } + + const float tu = 1.0f / float(texWidth); + const float tv = 1.0f / float(texHeight); + + float totalWeight = 0.0f; + size_t index = 0; + auto offsets = reinterpret_cast(constants.sampleOffsets); + auto weights = constants.sampleWeights; + for (int x = -2; x <= 2; ++x) + { + for (int y = -2; y <= 2; ++y) + { + // Exclude pixels with a block distance greater than 2. This will + // create a kernel which approximates a 5x5 kernel using only 13 + // sample points instead of 25; this is necessary since 2.0 shaders + // only support 16 texture grabs. + if (fabsf(float(x)) + fabsf(float(y)) > 2.0f) + continue; + + // Get the unscaled Gaussian intensity for this offset + offsets[index].x = float(x) * tu; + offsets[index].y = float(y) * tv; + offsets[index].z = 0.0f; + offsets[index].w = 0.0f; + + const float g = GaussianDistribution(float(x), float(y), 1.0f); + weights[index] = XMVectorReplicate(g); + + totalWeight += XMVectorGetX(weights[index]); + + ++index; + } + } + + // Divide the current weight by the total weight of all the samples; Gaussian + // blur kernels add to 1.0f to ensure that the intensity of the image isn't + // changed when the blur occurs. An optional multiplier variable is used to + // add or remove image intensity during the blur. + const XMVECTOR vtw = XMVectorReplicate(totalWeight); + const XMVECTOR vm = XMVectorReplicate(multiplier); + for (size_t i = 0; i < index; ++i) + { + const XMVECTOR w = XMVectorDivide(weights[i], vtw); + weights[i] = XMVectorMultiply(w, vm); + } +} + + +void BasicPostProcess::Impl::Bloom(bool horizontal, float size, float brightness) +{ + mUseConstants = true; + + if (!texWidth || !texHeight) + { + throw std::logic_error("Call SetSourceTexture before setting post-process effect"); + } + + float tu = 0.f; + float tv = 0.f; + if (horizontal) + { + tu = 1.f / float(texWidth); + } + else + { + tv = 1.f / float(texHeight); + } + + auto weights = reinterpret_cast(constants.sampleWeights); + auto offsets = reinterpret_cast(constants.sampleOffsets); + + // Fill the center texel + float weight = brightness * GaussianDistribution(0, 0, size); + weights[0] = XMFLOAT4(weight, weight, weight, 1.0f); + offsets[0].x = offsets[0].y = offsets[0].z = offsets[0].w = 0.f; + + // Fill the first half + for (int i = 1; i < 8; ++i) + { + // Get the Gaussian intensity for this offset + weight = brightness * GaussianDistribution(float(i), 0, size); + weights[i] = XMFLOAT4(weight, weight, weight, 1.0f); + offsets[i] = XMFLOAT4(float(i) * tu, float(i) * tv, 0.f, 0.f); + } + + // Mirror to the second half + for (int i = 8; i < 15; i++) + { + weights[i] = weights[i - 7]; + offsets[i] = XMFLOAT4(-offsets[i - 7].x, -offsets[i - 7].y, 0.f, 0.f); + } +} + + +// Public constructor. +BasicPostProcess::BasicPostProcess(_In_ ID3D12Device* device, const RenderTargetState& rtState, Effect fx) + : pImpl(std::make_unique(device, rtState, fx)) +{ +} + + +// Move constructor. +BasicPostProcess::BasicPostProcess(BasicPostProcess&&) noexcept = default; +BasicPostProcess& BasicPostProcess::operator= (BasicPostProcess&&) noexcept = default; +BasicPostProcess::~BasicPostProcess() = default; + + +// IPostProcess methods. +void BasicPostProcess::Process(_In_ ID3D12GraphicsCommandList* commandList) +{ + pImpl->Process(commandList); +} + + +// Properties +void BasicPostProcess::SetSourceTexture(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor, _In_opt_ ID3D12Resource* resource) +{ + pImpl->texture = srvDescriptor; + + if (resource) + { + #if defined(_MSC_VER) || !defined(_WIN32) + const auto desc = resource->GetDesc(); + #else + D3D12_RESOURCE_DESC tmpDesc; + const auto& desc = *resource->GetDesc(&tmpDesc); + #endif + pImpl->texWidth = static_cast(desc.Width); + pImpl->texHeight = desc.Height; + } + else + { + pImpl->texWidth = pImpl->texHeight = 0; + } +} + + +void BasicPostProcess::SetGaussianParameter(float multiplier) +{ + pImpl->guassianMultiplier = multiplier; + pImpl->SetDirtyFlag(); +} + + +void BasicPostProcess::SetBloomExtractParameter(float threshold) +{ + pImpl->bloomThreshold = threshold; + pImpl->SetDirtyFlag(); +} + + +void BasicPostProcess::SetBloomBlurParameters(bool horizontal, float size, float brightness) +{ + pImpl->bloomSize = size; + pImpl->bloomBrightness = brightness; + pImpl->bloomHorizontal = horizontal; + pImpl->SetDirtyFlag(); +} diff --git a/Common/DirectXTK12/Src/Bezier.h b/Common/DirectXTK12/Src/Bezier.h new file mode 100644 index 0000000..e209d15 --- /dev/null +++ b/Common/DirectXTK12/Src/Bezier.h @@ -0,0 +1,193 @@ +//-------------------------------------------------------------------------------------- +// File: Bezier.h +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248929 +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#pragma once + +#include +#include +#include + + +namespace Bezier +{ + // Performs a cubic bezier interpolation between four control points, + // returning the value at the specified time (t ranges 0 to 1). + template + inline T CubicInterpolate(T const& p1, T const& p2, T const& p3, T const& p4, float t) noexcept + { + return p1 * (1 - t) * (1 - t) * (1 - t) + + p2 * 3 * t * (1 - t) * (1 - t) + + p3 * 3 * t * t * (1 - t) + + p4 * t * t * t; + } + + template<> + inline DirectX::XMVECTOR CubicInterpolate(DirectX::XMVECTOR const& p1, DirectX::XMVECTOR const& p2, DirectX::XMVECTOR const& p3, DirectX::XMVECTOR const& p4, float t) noexcept + { + using namespace DirectX; + + const XMVECTOR T0 = XMVectorReplicate((1 - t) * (1 - t) * (1 - t)); + const XMVECTOR T1 = XMVectorReplicate(3 * t * (1 - t) * (1 - t)); + const XMVECTOR T2 = XMVectorReplicate(3 * t * t * (1 - t)); + const XMVECTOR T3 = XMVectorReplicate(t * t * t); + + XMVECTOR Result = XMVectorMultiply(p1, T0); + Result = XMVectorMultiplyAdd(p2, T1, Result); + Result = XMVectorMultiplyAdd(p3, T2, Result); + Result = XMVectorMultiplyAdd(p4, T3, Result); + + return Result; + } + + + // Computes the tangent of a cubic bezier curve at the specified time. + template + inline T CubicTangent(T const& p1, T const& p2, T const& p3, T const& p4, float t) noexcept + { + return p1 * (-1 + 2 * t - t * t) + + p2 * (1 - 4 * t + 3 * t * t) + + p3 * (2 * t - 3 * t * t) + + p4 * (t * t); + } + + template<> + inline DirectX::XMVECTOR CubicTangent(DirectX::XMVECTOR const& p1, DirectX::XMVECTOR const& p2, DirectX::XMVECTOR const& p3, DirectX::XMVECTOR const& p4, float t) noexcept + { + using namespace DirectX; + + const XMVECTOR T0 = XMVectorReplicate(-1 + 2 * t - t * t); + const XMVECTOR T1 = XMVectorReplicate(1 - 4 * t + 3 * t * t); + const XMVECTOR T2 = XMVectorReplicate(2 * t - 3 * t * t); + const XMVECTOR T3 = XMVectorReplicate(t * t); + + XMVECTOR Result = XMVectorMultiply(p1, T0); + Result = XMVectorMultiplyAdd(p2, T1, Result); + Result = XMVectorMultiplyAdd(p3, T2, Result); + Result = XMVectorMultiplyAdd(p4, T3, Result); + + return Result; + } + + + // Creates vertices for a patch that is tessellated at the specified level. + // Calls the specified outputVertex function for each generated vertex, + // passing the position, normal, and texture coordinate as parameters. + template + void CreatePatchVertices(_In_reads_(16) const DirectX::XMVECTOR patch[16], size_t tessellation, bool isMirrored, TOutputFunc outputVertex) + { + using namespace DirectX; + + for (size_t i = 0; i <= tessellation; i++) + { + const float u = float(i) / float(tessellation); + + for (size_t j = 0; j <= tessellation; j++) + { + const float v = float(j) / float(tessellation); + + // Perform four horizontal bezier interpolations + // between the control points of this patch. + const XMVECTOR p1 = CubicInterpolate(patch[0], patch[1], patch[2], patch[3], u); + const XMVECTOR p2 = CubicInterpolate(patch[4], patch[5], patch[6], patch[7], u); + const XMVECTOR p3 = CubicInterpolate(patch[8], patch[9], patch[10], patch[11], u); + const XMVECTOR p4 = CubicInterpolate(patch[12], patch[13], patch[14], patch[15], u); + + // Perform a vertical interpolation between the results of the + // previous horizontal interpolations, to compute the position. + const XMVECTOR position = CubicInterpolate(p1, p2, p3, p4, v); + + // Perform another four bezier interpolations between the control + // points, but this time vertically rather than horizontally. + const XMVECTOR q1 = CubicInterpolate(patch[0], patch[4], patch[8], patch[12], v); + const XMVECTOR q2 = CubicInterpolate(patch[1], patch[5], patch[9], patch[13], v); + const XMVECTOR q3 = CubicInterpolate(patch[2], patch[6], patch[10], patch[14], v); + const XMVECTOR q4 = CubicInterpolate(patch[3], patch[7], patch[11], patch[15], v); + + // Compute vertical and horizontal tangent vectors. + const XMVECTOR tangent1 = CubicTangent(p1, p2, p3, p4, v); + const XMVECTOR tangent2 = CubicTangent(q1, q2, q3, q4, u); + + // Cross the two tangent vectors to compute the normal. + XMVECTOR normal = XMVector3Cross(tangent1, tangent2); + + if (!XMVector3NearEqual(normal, XMVectorZero(), g_XMEpsilon)) + { + normal = XMVector3Normalize(normal); + + // If this patch is mirrored, we must invert the normal. + if (isMirrored) + { + normal = XMVectorNegate(normal); + } + } + else + { + // In a tidy and well constructed bezier patch, the preceding + // normal computation will always work. But the classic teapot + // model is not tidy or well constructed! At the top and bottom + // of the teapot, it contains degenerate geometry where a patch + // has several control points in the same place, which causes + // the tangent computation to fail and produce a zero normal. + // We 'fix' these cases by just hard-coding a normal that points + // either straight up or straight down, depending on whether we + // are on the top or bottom of the teapot. This is not a robust + // solution for all possible degenerate bezier patches, but hey, + // it's good enough to make the teapot work correctly! + + normal = XMVectorSelect(g_XMIdentityR1, g_XMNegIdentityR1, XMVectorLess(position, XMVectorZero())); + } + + // Compute the texture coordinate. + const float mirroredU = isMirrored ? 1 - u : u; + + const XMVECTOR textureCoordinate = XMVectorSet(mirroredU, v, 0, 0); + + // Output this vertex. + outputVertex(position, normal, textureCoordinate); + } + } + } + + + // Creates indices for a patch that is tessellated at the specified level. + // Calls the specified outputIndex function for each generated index value. + template + void CreatePatchIndices(size_t tessellation, bool isMirrored, TOutputFunc outputIndex) + { + size_t stride = tessellation + 1; + + for (size_t i = 0; i < tessellation; i++) + { + for (size_t j = 0; j < tessellation; j++) + { + // Make a list of six index values (two triangles). + std::array indices = + { + i * stride + j, + (i + 1) * stride + j, + (i + 1) * stride + j + 1, + + i * stride + j, + (i + 1) * stride + j + 1, + i * stride + j + 1, + }; + + // If this patch is mirrored, reverse indices to fix the winding order. + if (isMirrored) + { + std::reverse(indices.begin(), indices.end()); + } + + // Output these index values. + std::for_each(indices.begin(), indices.end(), outputIndex); + } + } + } +} diff --git a/Common/DirectXTK12/Src/BinaryReader.cpp b/Common/DirectXTK12/Src/BinaryReader.cpp new file mode 100644 index 0000000..e1cf143 --- /dev/null +++ b/Common/DirectXTK12/Src/BinaryReader.cpp @@ -0,0 +1,106 @@ +//-------------------------------------------------------------------------------------- +// File: BinaryReader.cpp +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248929 +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "pch.h" + +#include "BinaryReader.h" + +using namespace DirectX; + + +// Constructor reads from the filesystem. +BinaryReader::BinaryReader(_In_z_ wchar_t const* fileName) noexcept(false) : + mPos(nullptr), + mEnd(nullptr) +{ + size_t dataSize; + + HRESULT hr = ReadEntireFile(fileName, mOwnedData, &dataSize); + if (FAILED(hr)) + { + DebugTrace("ERROR: BinaryReader failed (%08X) to load '%ls'\n", + static_cast(hr), fileName); + throw std::runtime_error("BinaryReader"); + } + + mPos = mOwnedData.get(); + mEnd = mOwnedData.get() + dataSize; +} + + +// Constructor reads from an existing memory buffer. +BinaryReader::BinaryReader(_In_reads_bytes_(dataSize) uint8_t const* dataBlob, size_t dataSize) noexcept : + mPos(dataBlob), + mEnd(dataBlob + dataSize) +{ +} + + +// Reads from the filesystem into memory. +HRESULT BinaryReader::ReadEntireFile( + _In_z_ wchar_t const* fileName, + _Inout_ std::unique_ptr& data, + _Out_ size_t* dataSize) +{ + if (!fileName || !dataSize) + return E_INVALIDARG; + + *dataSize = 0; + + // Open the file. +#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) + ScopedHandle hFile(safe_handle(CreateFile2( + fileName, + GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, + nullptr))); +#else + ScopedHandle hFile(safe_handle(CreateFileW( + fileName, + GENERIC_READ, FILE_SHARE_READ, + nullptr, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, + nullptr))); +#endif + + if (!hFile) + return HRESULT_FROM_WIN32(GetLastError()); + + // Get the file size. + FILE_STANDARD_INFO fileInfo; + if (!GetFileInformationByHandleEx(hFile.get(), FileStandardInfo, &fileInfo, sizeof(fileInfo))) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + // File is too big for 32-bit allocation, so reject read. + if (fileInfo.EndOfFile.HighPart > 0) + return E_FAIL; + + // Create enough space for the file data. + data.reset(new uint8_t[fileInfo.EndOfFile.LowPart]); + + if (!data) + return E_OUTOFMEMORY; + + // Read the data in. + DWORD bytesRead = 0; + + if (!ReadFile(hFile.get(), data.get(), fileInfo.EndOfFile.LowPart, &bytesRead, nullptr)) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + if (bytesRead < fileInfo.EndOfFile.LowPart) + return E_FAIL; + + *dataSize = bytesRead; + + return S_OK; +} diff --git a/Common/DirectXTK12/Src/BinaryReader.h b/Common/DirectXTK12/Src/BinaryReader.h new file mode 100644 index 0000000..0d4d96c --- /dev/null +++ b/Common/DirectXTK12/Src/BinaryReader.h @@ -0,0 +1,72 @@ +//-------------------------------------------------------------------------------------- +// File: BinaryReader.h +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248929 +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#pragma once + +#include +#include +#include +#include + +#include "PlatformHelpers.h" + + +namespace DirectX +{ + // Helper for reading binary data, either from the filesystem a memory buffer. + class BinaryReader + { + public: + explicit BinaryReader(_In_z_ wchar_t const* fileName) noexcept(false); + BinaryReader(_In_reads_bytes_(dataSize) uint8_t const* dataBlob, size_t dataSize) noexcept; + + BinaryReader(BinaryReader const&) = delete; + BinaryReader& operator= (BinaryReader const&) = delete; + + // Reads a single value. + template T const& Read() + { + return *ReadArray(1); + } + + + // Reads an array of values. + template T const* ReadArray(size_t elementCount) + { + static_assert(std::is_standard_layout::value, "Can only read plain-old-data types"); + + uint8_t const* newPos = mPos + sizeof(T) * elementCount; + + if (newPos < mPos) + throw std::overflow_error("ReadArray"); + + if (newPos > mEnd) + throw std::runtime_error("End of file"); + + auto result = reinterpret_cast(mPos); + + mPos = newPos; + + return result; + } + + + // Lower level helper reads directly from the filesystem into memory. + static HRESULT ReadEntireFile(_In_z_ wchar_t const* fileName, _Inout_ std::unique_ptr& data, _Out_ size_t* dataSize); + + + private: + // The data currently being read. + uint8_t const* mPos; + uint8_t const* mEnd; + + std::unique_ptr mOwnedData; + }; +} diff --git a/Common/DirectXTK12/Src/BufferHelpers.cpp b/Common/DirectXTK12/Src/BufferHelpers.cpp new file mode 100644 index 0000000..a268c03 --- /dev/null +++ b/Common/DirectXTK12/Src/BufferHelpers.cpp @@ -0,0 +1,407 @@ +//------------------------------------- ------------------------------------------------- +// File: BufferHelpers.cpp +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "pch.h" +#include "BufferHelpers.h" +#include "DirectXHelpers.h" +#include "ResourceUploadBatch.h" +#include "LoaderHelpers.h" +#include "PlatformHelpers.h" + + +using namespace DirectX; +using Microsoft::WRL::ComPtr; + +//-------------------------------------------------------------------------------------- +_Use_decl_annotations_ +HRESULT DirectX::CreateStaticBuffer( + ID3D12Device* device, + ResourceUploadBatch& resourceUpload, + const void* ptr, + size_t count, + size_t stride, + D3D12_RESOURCE_STATES afterState, + ID3D12Resource** pBuffer, + D3D12_RESOURCE_FLAGS resFlags) noexcept +{ + if (!pBuffer) + return E_INVALIDARG; + + *pBuffer = nullptr; + + if (!device || !ptr || !count || !stride) + return E_INVALIDARG; + + const uint64_t sizeInbytes = uint64_t(count) * uint64_t(stride); + + static constexpr uint64_t c_maxBytes = D3D12_REQ_RESOURCE_SIZE_IN_MEGABYTES_EXPRESSION_A_TERM * 1024u * 1024u; + + if (sizeInbytes > c_maxBytes) + { + DebugTrace("ERROR: Resource size too large for DirectX 12 (size %llu)\n", sizeInbytes); + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + + auto const desc = CD3DX12_RESOURCE_DESC::Buffer(sizeInbytes, resFlags); + + const CD3DX12_HEAP_PROPERTIES heapProperties(D3D12_HEAP_TYPE_DEFAULT); + + ComPtr res; + HRESULT hr = device->CreateCommittedResource( + &heapProperties, + D3D12_HEAP_FLAG_NONE, + &desc, + c_initialCopyTargetState, + nullptr, + IID_GRAPHICS_PPV_ARGS(res.GetAddressOf())); + if (FAILED(hr)) + return hr; + + D3D12_SUBRESOURCE_DATA initData = { ptr, 0, 0 }; + + try + { + resourceUpload.Upload(res.Get(), 0, &initData, 1); + + resourceUpload.Transition(res.Get(), D3D12_RESOURCE_STATE_COPY_DEST, afterState); + } + catch (com_exception e) + { + return e.get_result(); + } + catch (...) + { + return E_FAIL; + } + + *pBuffer = res.Detach(); + + return S_OK; +} + + +//-------------------------------------------------------------------------------------- +_Use_decl_annotations_ +HRESULT DirectX::CreateUAVBuffer( + ID3D12Device* device, + uint64_t bufferSize, + ID3D12Resource** pBuffer, + D3D12_RESOURCE_STATES initialState, + D3D12_RESOURCE_FLAGS additionalResFlags) noexcept +{ + if (!pBuffer) + return E_INVALIDARG; + + *pBuffer = nullptr; + + if (!device || !bufferSize) + return E_INVALIDARG; + + static constexpr uint64_t c_maxBytes = D3D12_REQ_RESOURCE_SIZE_IN_MEGABYTES_EXPRESSION_A_TERM * 1024u * 1024u; + + if (bufferSize > c_maxBytes) + { + DebugTrace("ERROR: Resource size too large for DirectX 12 (size %llu)\n", bufferSize); + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + + auto const desc = CD3DX12_RESOURCE_DESC::Buffer(bufferSize, D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS | additionalResFlags); + + const CD3DX12_HEAP_PROPERTIES heapProperties(D3D12_HEAP_TYPE_DEFAULT); + + ComPtr res; + HRESULT hr = device->CreateCommittedResource( + &heapProperties, + D3D12_HEAP_FLAG_NONE, + &desc, + initialState, + nullptr, + IID_GRAPHICS_PPV_ARGS(res.GetAddressOf())); + if (FAILED(hr)) + return hr; + + *pBuffer = res.Detach(); + + return S_OK; +} + + +//-------------------------------------------------------------------------------------- +_Use_decl_annotations_ +HRESULT DirectX::CreateUploadBuffer( + ID3D12Device* device, + const void* ptr, + size_t count, + size_t stride, + ID3D12Resource** pBuffer, + D3D12_RESOURCE_STATES initialState, + D3D12_RESOURCE_FLAGS resFlags) noexcept +{ + if (!pBuffer) + return E_INVALIDARG; + + *pBuffer = nullptr; + + if (!device || !count || !stride) + return E_INVALIDARG; + + const uint64_t sizeInbytes = uint64_t(count) * uint64_t(stride); + + static constexpr uint64_t c_maxBytes = D3D12_REQ_RESOURCE_SIZE_IN_MEGABYTES_EXPRESSION_A_TERM * 1024u * 1024u; + + if (sizeInbytes > c_maxBytes) + { + DebugTrace("ERROR: Resource size too large for DirectX 12 (size %llu)\n", sizeInbytes); + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + + auto const desc = CD3DX12_RESOURCE_DESC::Buffer(sizeInbytes, resFlags); + + const CD3DX12_HEAP_PROPERTIES heapProperties(D3D12_HEAP_TYPE_UPLOAD); + + ComPtr res; + HRESULT hr = device->CreateCommittedResource( + &heapProperties, + D3D12_HEAP_FLAG_NONE, + &desc, + initialState, + nullptr, + IID_GRAPHICS_PPV_ARGS(res.GetAddressOf())); + if (FAILED(hr)) + return hr; + + if (ptr) + { + void* mappedPtr = nullptr; + hr = res->Map(0, nullptr, &mappedPtr); + if (FAILED(hr)) + return hr; + + memcpy(mappedPtr, ptr, static_cast(sizeInbytes)); + res->Unmap(0, nullptr); + } + + *pBuffer = res.Detach(); + + return S_OK; +} + + +//-------------------------------------------------------------------------------------- +_Use_decl_annotations_ +HRESULT DirectX::CreateTextureFromMemory( + ID3D12Device* device, + ResourceUploadBatch& resourceUpload, + size_t width, + DXGI_FORMAT format, + const D3D12_SUBRESOURCE_DATA& initData, + ID3D12Resource** texture, + D3D12_RESOURCE_STATES afterState, + D3D12_RESOURCE_FLAGS resFlags) noexcept +{ + if (!texture) + return E_INVALIDARG; + + *texture = nullptr; + + if (!device || !width || !initData.pData) + return E_INVALIDARG; + + static_assert(D3D12_REQ_TEXTURE1D_U_DIMENSION <= UINT64_MAX, "Exceeded integer limits"); + + if (width > D3D12_REQ_TEXTURE1D_U_DIMENSION) + { + DebugTrace("ERROR: Resource dimensions too large for DirectX 12 (1D: size %zu)\n", width); + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + + auto const desc = CD3DX12_RESOURCE_DESC::Tex1D(format, static_cast(width), 1u, 1u, resFlags); + + const CD3DX12_HEAP_PROPERTIES heapProperties(D3D12_HEAP_TYPE_DEFAULT); + + ComPtr res; + HRESULT hr = device->CreateCommittedResource( + &heapProperties, + D3D12_HEAP_FLAG_NONE, + &desc, + c_initialCopyTargetState, + nullptr, + IID_GRAPHICS_PPV_ARGS(res.GetAddressOf())); + if (FAILED(hr)) + return hr; + + try + { + resourceUpload.Upload(res.Get(), 0, &initData, 1); + + resourceUpload.Transition(res.Get(), D3D12_RESOURCE_STATE_COPY_DEST, afterState); + } + catch (com_exception e) + { + return e.get_result(); + } + catch (...) + { + return E_FAIL; + } + + *texture = res.Detach(); + + return S_OK; +} + +_Use_decl_annotations_ +HRESULT DirectX::CreateTextureFromMemory( + ID3D12Device* device, + ResourceUploadBatch& resourceUpload, + size_t width, + size_t height, + DXGI_FORMAT format, + const D3D12_SUBRESOURCE_DATA& initData, + ID3D12Resource** texture, + bool generateMips, + D3D12_RESOURCE_STATES afterState, + D3D12_RESOURCE_FLAGS resFlags) noexcept +{ + if (!texture) + return E_INVALIDARG; + + *texture = nullptr; + + if (!device || !width || !height + || !initData.pData || !initData.RowPitch) + return E_INVALIDARG; + + static_assert(D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION <= UINT32_MAX, "Exceeded integer limits"); + + if ((width > D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION) + || (height > D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION)) + { + DebugTrace("ERROR: Resource dimensions too large for DirectX 12 (2D: size %zu by %zu)\n", width, height); + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + + uint16_t mipCount = 1; + if (generateMips) + { + generateMips = resourceUpload.IsSupportedForGenerateMips(format); + if (generateMips) + { + mipCount = static_cast(LoaderHelpers::CountMips(static_cast(width), static_cast(height))); + } + } + + auto const desc = CD3DX12_RESOURCE_DESC::Tex2D(format, static_cast(width), static_cast(height), + 1u, mipCount, 1u, 0u, resFlags); + + const CD3DX12_HEAP_PROPERTIES heapProperties(D3D12_HEAP_TYPE_DEFAULT); + + ComPtr res; + HRESULT hr = device->CreateCommittedResource( + &heapProperties, + D3D12_HEAP_FLAG_NONE, + &desc, + c_initialCopyTargetState, + nullptr, + IID_GRAPHICS_PPV_ARGS(res.GetAddressOf())); + if (FAILED(hr)) + return hr; + + try + { + resourceUpload.Upload(res.Get(), 0, &initData, 1); + + resourceUpload.Transition(res.Get(), D3D12_RESOURCE_STATE_COPY_DEST, afterState); + + if (generateMips) + { + resourceUpload.GenerateMips(res.Get()); + } + } + catch (com_exception e) + { + return e.get_result(); + } + catch (...) + { + return E_FAIL; + } + + *texture = res.Detach(); + + return S_OK; +} + + +_Use_decl_annotations_ +HRESULT DirectX::CreateTextureFromMemory( + ID3D12Device* device, + ResourceUploadBatch& resourceUpload, + size_t width, size_t height, size_t depth, + DXGI_FORMAT format, + const D3D12_SUBRESOURCE_DATA& initData, + ID3D12Resource** texture, + D3D12_RESOURCE_STATES afterState, + D3D12_RESOURCE_FLAGS resFlags) noexcept +{ + if (!texture) + return E_INVALIDARG; + + *texture = nullptr; + + if (!device || !width || !height || !depth + || !initData.pData || !initData.RowPitch || !initData.SlicePitch) + return E_INVALIDARG; + + static_assert(D3D12_REQ_TEXTURE3D_U_V_OR_W_DIMENSION <= UINT16_MAX, "Exceeded integer limits"); + + if ((width > D3D12_REQ_TEXTURE3D_U_V_OR_W_DIMENSION) + || (height > D3D12_REQ_TEXTURE3D_U_V_OR_W_DIMENSION) + || (depth > D3D12_REQ_TEXTURE3D_U_V_OR_W_DIMENSION)) + { + DebugTrace("ERROR: Resource dimensions too large for DirectX 12 (3D: size %zu by %zu by %zu)\n", width, height, depth); + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + + auto const desc = CD3DX12_RESOURCE_DESC::Tex3D(format, + static_cast(width), static_cast(height), static_cast(depth), + 1u, resFlags); + + const CD3DX12_HEAP_PROPERTIES heapProperties(D3D12_HEAP_TYPE_DEFAULT); + + ComPtr res; + HRESULT hr = device->CreateCommittedResource( + &heapProperties, + D3D12_HEAP_FLAG_NONE, + &desc, + c_initialCopyTargetState, + nullptr, + IID_GRAPHICS_PPV_ARGS(res.GetAddressOf())); + if (FAILED(hr)) + return hr; + + try + { + resourceUpload.Upload(res.Get(), 0, &initData, 1); + + resourceUpload.Transition(res.Get(), D3D12_RESOURCE_STATE_COPY_DEST, afterState); + } + catch (com_exception e) + { + return e.get_result(); + } + catch (...) + { + return E_FAIL; + } + + *texture = res.Detach(); + + return S_OK; +} diff --git a/Common/DirectXTK12/Src/CommonStates.cpp b/Common/DirectXTK12/Src/CommonStates.cpp new file mode 100644 index 0000000..fb5fdcd --- /dev/null +++ b/Common/DirectXTK12/Src/CommonStates.cpp @@ -0,0 +1,582 @@ +//-------------------------------------------------------------------------------------- +// File: CommonStates.cpp +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "pch.h" +#include "CommonStates.h" +#include "DirectXHelpers.h" +#include "DescriptorHeap.h" + +using namespace DirectX; + +// -------------------------------------------------------------------------- +// Blend States +// -------------------------------------------------------------------------- + +const D3D12_BLEND_DESC CommonStates::Opaque = +{ + FALSE, // AlphaToCoverageEnable + FALSE, // IndependentBlendEnable + { { + FALSE, // BlendEnable + FALSE, // LogicOpEnable + D3D12_BLEND_ONE, // SrcBlend + D3D12_BLEND_ZERO, // DestBlend + D3D12_BLEND_OP_ADD, // BlendOp + D3D12_BLEND_ONE, // SrcBlendAlpha + D3D12_BLEND_ZERO, // DestBlendAlpha + D3D12_BLEND_OP_ADD, // BlendOpAlpha + D3D12_LOGIC_OP_NOOP, + D3D12_COLOR_WRITE_ENABLE_ALL + } } +}; + +const D3D12_BLEND_DESC CommonStates::AlphaBlend = +{ + FALSE, // AlphaToCoverageEnable + FALSE, // IndependentBlendEnable + { { + TRUE, // BlendEnable + FALSE, // LogicOpEnable + D3D12_BLEND_ONE, // SrcBlend + D3D12_BLEND_INV_SRC_ALPHA, // DestBlend + D3D12_BLEND_OP_ADD, // BlendOp + D3D12_BLEND_ONE, // SrcBlendAlpha + D3D12_BLEND_INV_SRC_ALPHA, // DestBlendAlpha + D3D12_BLEND_OP_ADD, // BlendOpAlpha + D3D12_LOGIC_OP_NOOP, + D3D12_COLOR_WRITE_ENABLE_ALL + } } +}; + +const D3D12_BLEND_DESC CommonStates::Additive = +{ + FALSE, // AlphaToCoverageEnable + FALSE, // IndependentBlendEnable + { { + TRUE, // BlendEnable + FALSE, // LogicOpEnable + D3D12_BLEND_SRC_ALPHA, // SrcBlend + D3D12_BLEND_ONE, // DestBlend + D3D12_BLEND_OP_ADD, // BlendOp + D3D12_BLEND_SRC_ALPHA, // SrcBlendAlpha + D3D12_BLEND_ONE, // DestBlendAlpha + D3D12_BLEND_OP_ADD, // BlendOpAlpha + D3D12_LOGIC_OP_NOOP, + D3D12_COLOR_WRITE_ENABLE_ALL + } } +}; + +const D3D12_BLEND_DESC CommonStates::NonPremultiplied = +{ + FALSE, // AlphaToCoverageEnable + FALSE, // IndependentBlendEnable + { { + TRUE, // BlendEnable + FALSE, // LogicOpEnable + D3D12_BLEND_SRC_ALPHA, // SrcBlend + D3D12_BLEND_INV_SRC_ALPHA, // DestBlend + D3D12_BLEND_OP_ADD, // BlendOp + D3D12_BLEND_SRC_ALPHA, // SrcBlendAlpha + D3D12_BLEND_INV_SRC_ALPHA, // DestBlendAlpha + D3D12_BLEND_OP_ADD, // BlendOpAlpha + D3D12_LOGIC_OP_NOOP, + D3D12_COLOR_WRITE_ENABLE_ALL + } } +}; + + +// -------------------------------------------------------------------------- +// Depth-Stencil States +// -------------------------------------------------------------------------- + +const D3D12_DEPTH_STENCIL_DESC CommonStates::DepthNone = +{ + FALSE, // DepthEnable + D3D12_DEPTH_WRITE_MASK_ZERO, + D3D12_COMPARISON_FUNC_LESS_EQUAL, // DepthFunc + FALSE, // StencilEnable + D3D12_DEFAULT_STENCIL_READ_MASK, + D3D12_DEFAULT_STENCIL_WRITE_MASK, + { + D3D12_STENCIL_OP_KEEP, // StencilFailOp + D3D12_STENCIL_OP_KEEP, // StencilDepthFailOp + D3D12_STENCIL_OP_KEEP, // StencilPassOp + D3D12_COMPARISON_FUNC_ALWAYS // StencilFunc + }, // FrontFace + { + D3D12_STENCIL_OP_KEEP, // StencilFailOp + D3D12_STENCIL_OP_KEEP, // StencilDepthFailOp + D3D12_STENCIL_OP_KEEP, // StencilPassOp + D3D12_COMPARISON_FUNC_ALWAYS // StencilFunc + } // BackFace +}; + +const D3D12_DEPTH_STENCIL_DESC CommonStates::DepthDefault = +{ + TRUE, // DepthEnable + D3D12_DEPTH_WRITE_MASK_ALL, + D3D12_COMPARISON_FUNC_LESS_EQUAL, // DepthFunc + FALSE, // StencilEnable + D3D12_DEFAULT_STENCIL_READ_MASK, + D3D12_DEFAULT_STENCIL_WRITE_MASK, + { + D3D12_STENCIL_OP_KEEP, // StencilFailOp + D3D12_STENCIL_OP_KEEP, // StencilDepthFailOp + D3D12_STENCIL_OP_KEEP, // StencilPassOp + D3D12_COMPARISON_FUNC_ALWAYS // StencilFunc + }, // FrontFace + { + D3D12_STENCIL_OP_KEEP, // StencilFailOp + D3D12_STENCIL_OP_KEEP, // StencilDepthFailOp + D3D12_STENCIL_OP_KEEP, // StencilPassOp + D3D12_COMPARISON_FUNC_ALWAYS // StencilFunc + } // BackFace +}; + +const D3D12_DEPTH_STENCIL_DESC CommonStates::DepthRead = +{ + TRUE, // DepthEnable + D3D12_DEPTH_WRITE_MASK_ZERO, + D3D12_COMPARISON_FUNC_LESS_EQUAL, // DepthFunc + FALSE, // StencilEnable + D3D12_DEFAULT_STENCIL_READ_MASK, + D3D12_DEFAULT_STENCIL_WRITE_MASK, + { + D3D12_STENCIL_OP_KEEP, // StencilFailOp + D3D12_STENCIL_OP_KEEP, // StencilDepthFailOp + D3D12_STENCIL_OP_KEEP, // StencilPassOp + D3D12_COMPARISON_FUNC_ALWAYS // StencilFunc + }, // FrontFace + { + D3D12_STENCIL_OP_KEEP, // StencilFailOp + D3D12_STENCIL_OP_KEEP, // StencilDepthFailOp + D3D12_STENCIL_OP_KEEP, // StencilPassOp + D3D12_COMPARISON_FUNC_ALWAYS // StencilFunc + } // BackFace +}; + +const D3D12_DEPTH_STENCIL_DESC CommonStates::DepthReverseZ = +{ + TRUE, // DepthEnable + D3D12_DEPTH_WRITE_MASK_ALL, + D3D12_COMPARISON_FUNC_GREATER_EQUAL, // DepthFunc + FALSE, // StencilEnable + D3D12_DEFAULT_STENCIL_READ_MASK, + D3D12_DEFAULT_STENCIL_WRITE_MASK, + { + D3D12_STENCIL_OP_KEEP, // StencilFailOp + D3D12_STENCIL_OP_KEEP, // StencilDepthFailOp + D3D12_STENCIL_OP_KEEP, // StencilPassOp + D3D12_COMPARISON_FUNC_ALWAYS // StencilFunc + }, // FrontFace + { + D3D12_STENCIL_OP_KEEP, // StencilFailOp + D3D12_STENCIL_OP_KEEP, // StencilDepthFailOp + D3D12_STENCIL_OP_KEEP, // StencilPassOp + D3D12_COMPARISON_FUNC_ALWAYS // StencilFunc + } // BackFace +}; + +const D3D12_DEPTH_STENCIL_DESC CommonStates::DepthReadReverseZ = +{ + TRUE, // DepthEnable + D3D12_DEPTH_WRITE_MASK_ZERO, + D3D12_COMPARISON_FUNC_GREATER_EQUAL, // DepthFunc + FALSE, // StencilEnable + D3D12_DEFAULT_STENCIL_READ_MASK, + D3D12_DEFAULT_STENCIL_WRITE_MASK, + { + D3D12_STENCIL_OP_KEEP, // StencilFailOp + D3D12_STENCIL_OP_KEEP, // StencilDepthFailOp + D3D12_STENCIL_OP_KEEP, // StencilPassOp + D3D12_COMPARISON_FUNC_ALWAYS // StencilFunc + }, // FrontFace + { + D3D12_STENCIL_OP_KEEP, // StencilFailOp + D3D12_STENCIL_OP_KEEP, // StencilDepthFailOp + D3D12_STENCIL_OP_KEEP, // StencilPassOp + D3D12_COMPARISON_FUNC_ALWAYS // StencilFunc + } // BackFace +}; + + +// -------------------------------------------------------------------------- +// Rasterizer States +// -------------------------------------------------------------------------- + +const D3D12_RASTERIZER_DESC CommonStates::CullNone = +{ + D3D12_FILL_MODE_SOLID, + D3D12_CULL_MODE_NONE, + FALSE, // FrontCounterClockwise + D3D12_DEFAULT_DEPTH_BIAS, + D3D12_DEFAULT_DEPTH_BIAS_CLAMP, + D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS, + TRUE, // DepthClipEnable + TRUE, // MultisampleEnable + FALSE, // AntialiasedLineEnable + 0, // ForcedSampleCount + D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF +}; + +const D3D12_RASTERIZER_DESC CommonStates::CullClockwise = +{ + D3D12_FILL_MODE_SOLID, + D3D12_CULL_MODE_FRONT, + FALSE, // FrontCounterClockwise + D3D12_DEFAULT_DEPTH_BIAS, + D3D12_DEFAULT_DEPTH_BIAS_CLAMP, + D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS, + TRUE, // DepthClipEnable + TRUE, // MultisampleEnable + FALSE, // AntialiasedLineEnable + 0, // ForcedSampleCount + D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF +}; + +const D3D12_RASTERIZER_DESC CommonStates::CullCounterClockwise = +{ + D3D12_FILL_MODE_SOLID, + D3D12_CULL_MODE_BACK, + FALSE, // FrontCounterClockwise + D3D12_DEFAULT_DEPTH_BIAS, + D3D12_DEFAULT_DEPTH_BIAS_CLAMP, + D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS, + TRUE, // DepthClipEnable + TRUE, // MultisampleEnable + FALSE, // AntialiasedLineEnable + 0, // ForcedSampleCount + D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF +}; + +const D3D12_RASTERIZER_DESC CommonStates::Wireframe = +{ + D3D12_FILL_MODE_WIREFRAME, + D3D12_CULL_MODE_NONE, + FALSE, // FrontCounterClockwise + D3D12_DEFAULT_DEPTH_BIAS, + D3D12_DEFAULT_DEPTH_BIAS_CLAMP, + D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS, + TRUE, // DepthClipEnable + TRUE, // MultisampleEnable + FALSE, // AntialiasedLineEnable + 0, // ForcedSampleCount + D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF +}; + + +// -------------------------------------------------------------------------- +// Static sampler States +// -------------------------------------------------------------------------- + +const D3D12_STATIC_SAMPLER_DESC CommonStates::StaticPointWrap( + unsigned int shaderRegister, + D3D12_SHADER_VISIBILITY shaderVisibility, + unsigned int registerSpace) noexcept +{ + static const D3D12_STATIC_SAMPLER_DESC s_desc = { + D3D12_FILTER_MIN_MAG_MIP_POINT, + D3D12_TEXTURE_ADDRESS_MODE_WRAP, // AddressU + D3D12_TEXTURE_ADDRESS_MODE_WRAP, // AddressV + D3D12_TEXTURE_ADDRESS_MODE_WRAP, // AddressW + 0, // MipLODBias + D3D12_MAX_MAXANISOTROPY, + D3D12_COMPARISON_FUNC_NEVER, + D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK, + 0, // MinLOD + FLT_MAX, // MaxLOD + 0, // ShaderRegister + 0, // RegisterSpace + D3D12_SHADER_VISIBILITY_ALL, + }; + + D3D12_STATIC_SAMPLER_DESC desc = s_desc; + desc.ShaderRegister = shaderRegister; + desc.ShaderVisibility = shaderVisibility; + desc.RegisterSpace = registerSpace; + return desc; +} + +const D3D12_STATIC_SAMPLER_DESC CommonStates::StaticPointClamp( + unsigned int shaderRegister, + D3D12_SHADER_VISIBILITY shaderVisibility, + unsigned int registerSpace) noexcept +{ + static const D3D12_STATIC_SAMPLER_DESC s_desc = { + D3D12_FILTER_MIN_MAG_MIP_POINT, + D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // AddressU + D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // AddressV + D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // AddressW + 0, // MipLODBias + D3D12_MAX_MAXANISOTROPY, + D3D12_COMPARISON_FUNC_NEVER, + D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK, + 0, // MinLOD + FLT_MAX, // MaxLOD + 0, // ShaderRegister + 0, // RegisterSpace + D3D12_SHADER_VISIBILITY_ALL, + }; + + D3D12_STATIC_SAMPLER_DESC desc = s_desc; + desc.ShaderRegister = shaderRegister; + desc.ShaderVisibility = shaderVisibility; + desc.RegisterSpace = registerSpace; + return desc; +}; + +const D3D12_STATIC_SAMPLER_DESC CommonStates::StaticLinearWrap( + unsigned int shaderRegister, + D3D12_SHADER_VISIBILITY shaderVisibility, + unsigned int registerSpace) noexcept +{ + static const D3D12_STATIC_SAMPLER_DESC s_desc = { + D3D12_FILTER_MIN_MAG_MIP_LINEAR, + D3D12_TEXTURE_ADDRESS_MODE_WRAP, // AddressU + D3D12_TEXTURE_ADDRESS_MODE_WRAP, // AddressV + D3D12_TEXTURE_ADDRESS_MODE_WRAP, // AddressW + 0, // MipLODBias + D3D12_MAX_MAXANISOTROPY, + D3D12_COMPARISON_FUNC_NEVER, + D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK, + 0, // MinLOD + FLT_MAX, // MaxLOD + 0, // ShaderRegister + 0, // RegisterSpace + D3D12_SHADER_VISIBILITY_ALL, + }; + + D3D12_STATIC_SAMPLER_DESC desc = s_desc; + desc.ShaderRegister = shaderRegister; + desc.ShaderVisibility = shaderVisibility; + desc.RegisterSpace = registerSpace; + return desc; +}; + +const D3D12_STATIC_SAMPLER_DESC CommonStates::StaticLinearClamp( + unsigned int shaderRegister, + D3D12_SHADER_VISIBILITY shaderVisibility, + unsigned int registerSpace) noexcept +{ + static const D3D12_STATIC_SAMPLER_DESC s_desc = { + D3D12_FILTER_MIN_MAG_MIP_LINEAR, + D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // AddressU + D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // AddressV + D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // AddressW + 0, // MipLODBias + D3D12_MAX_MAXANISOTROPY, + D3D12_COMPARISON_FUNC_NEVER, + D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK, + 0, // MinLOD + FLT_MAX, // MaxLOD + 0, // ShaderRegister + 0, // RegisterSpace + D3D12_SHADER_VISIBILITY_ALL, + }; + + D3D12_STATIC_SAMPLER_DESC desc = s_desc; + desc.ShaderRegister = shaderRegister; + desc.ShaderVisibility = shaderVisibility; + desc.RegisterSpace = registerSpace; + return desc; +}; + +const D3D12_STATIC_SAMPLER_DESC CommonStates::StaticAnisotropicWrap( + unsigned int shaderRegister, + D3D12_SHADER_VISIBILITY shaderVisibility, + unsigned int registerSpace) noexcept +{ + static const D3D12_STATIC_SAMPLER_DESC s_desc = { + D3D12_FILTER_ANISOTROPIC, + D3D12_TEXTURE_ADDRESS_MODE_WRAP, // AddressU + D3D12_TEXTURE_ADDRESS_MODE_WRAP, // AddressV + D3D12_TEXTURE_ADDRESS_MODE_WRAP, // AddressW + 0, // MipLODBias + D3D12_MAX_MAXANISOTROPY, + D3D12_COMPARISON_FUNC_NEVER, + D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK, + 0, // MinLOD + FLT_MAX, // MaxLOD + 0, // ShaderRegister + 0, // RegisterSpace + D3D12_SHADER_VISIBILITY_ALL, + }; + + D3D12_STATIC_SAMPLER_DESC desc = s_desc; + desc.ShaderRegister = shaderRegister; + desc.ShaderVisibility = shaderVisibility; + desc.RegisterSpace = registerSpace; + return desc; +}; + +const D3D12_STATIC_SAMPLER_DESC CommonStates::StaticAnisotropicClamp( + unsigned int shaderRegister, + D3D12_SHADER_VISIBILITY shaderVisibility, + unsigned int registerSpace) noexcept +{ + static const D3D12_STATIC_SAMPLER_DESC s_desc = { + D3D12_FILTER_ANISOTROPIC, + D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // AddressU + D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // AddressV + D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // AddressW + 0, // MipLODBias + D3D12_MAX_MAXANISOTROPY, + D3D12_COMPARISON_FUNC_NEVER, + D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK, + 0, // MinLOD + FLT_MAX, // MaxLOD + 0, // ShaderRegister + 0, // RegisterSpace + D3D12_SHADER_VISIBILITY_ALL, + }; + + D3D12_STATIC_SAMPLER_DESC desc = s_desc; + desc.ShaderRegister = shaderRegister; + desc.ShaderVisibility = shaderVisibility; + desc.RegisterSpace = registerSpace; + return desc; +}; + +// -------------------------------------------------------------------------- +// Samplers +// -------------------------------------------------------------------------- + +class CommonStates::Impl +{ +public: + + static const D3D12_SAMPLER_DESC SamplerDescs[static_cast(SamplerIndex::Count)]; + + explicit Impl(_In_ ID3D12Device* device) + : mDescriptors(device, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, static_cast(SamplerIndex::Count)) + { + SetDebugObjectName(mDescriptors.Heap(), L"CommonStates"); + + for (size_t i = 0; i < static_cast(SamplerIndex::Count); ++i) + { + device->CreateSampler(&SamplerDescs[i], mDescriptors.GetCpuHandle(i)); + } + } + + D3D12_GPU_DESCRIPTOR_HANDLE Get(SamplerIndex i) const + { + return mDescriptors.GetGpuHandle(static_cast(i)); + } + + ID3D12DescriptorHeap* Heap() const noexcept + { + return mDescriptors.Heap(); + } + +private: + DescriptorHeap mDescriptors; +}; + +const D3D12_SAMPLER_DESC CommonStates::Impl::SamplerDescs[] = +{ + // PointWrap + { + D3D12_FILTER_MIN_MAG_MIP_POINT, + D3D12_TEXTURE_ADDRESS_MODE_WRAP, // AddressU + D3D12_TEXTURE_ADDRESS_MODE_WRAP, // AddressV + D3D12_TEXTURE_ADDRESS_MODE_WRAP, // AddressW + 0, // MipLODBias + D3D12_MAX_MAXANISOTROPY, + D3D12_COMPARISON_FUNC_NEVER, + { 0, 0, 0, 0 }, // BorderColor + 0, // MinLOD + FLT_MAX // MaxLOD + }, + // PointClamp + { + D3D12_FILTER_MIN_MAG_MIP_POINT, + D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // AddressU + D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // AddressV + D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // AddressW + 0, // MipLODBias + D3D12_MAX_MAXANISOTROPY, + D3D12_COMPARISON_FUNC_NEVER, + { 0, 0, 0, 0 }, // BorderColor + 0, // MinLOD + FLT_MAX // MaxLOD + }, + // LinearWrap + { + D3D12_FILTER_MIN_MAG_MIP_LINEAR, + D3D12_TEXTURE_ADDRESS_MODE_WRAP, // AddressU + D3D12_TEXTURE_ADDRESS_MODE_WRAP, // AddressV + D3D12_TEXTURE_ADDRESS_MODE_WRAP, // AddressW + 0, // MipLODBias + D3D12_MAX_MAXANISOTROPY, + D3D12_COMPARISON_FUNC_NEVER, + { 0, 0, 0, 0 }, // BorderColor + 0, // MinLOD + FLT_MAX // MaxLOD + }, + // LinearClamp + { + D3D12_FILTER_MIN_MAG_MIP_LINEAR, + D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // AddressU + D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // AddressV + D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // AddressW + 0, // MipLODBias + D3D12_MAX_MAXANISOTROPY, + D3D12_COMPARISON_FUNC_NEVER, + { 0, 0, 0, 0 }, // BorderColor + 0, // MinLOD + FLT_MAX // MaxLOD + }, + // AnisotropicWrap + { + D3D12_FILTER_ANISOTROPIC, + D3D12_TEXTURE_ADDRESS_MODE_WRAP, // AddressU + D3D12_TEXTURE_ADDRESS_MODE_WRAP, // AddressV + D3D12_TEXTURE_ADDRESS_MODE_WRAP, // AddressW + 0, // MipLODBias + D3D12_MAX_MAXANISOTROPY, + D3D12_COMPARISON_FUNC_NEVER, + { 0, 0, 0, 0 }, // BorderColor + 0, // MinLOD + FLT_MAX // MaxLOD + }, + // AnisotropicClamp + { + D3D12_FILTER_ANISOTROPIC, + D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // AddressU + D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // AddressV + D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // AddressW + 0, // MipLODBias + D3D12_MAX_MAXANISOTROPY, + D3D12_COMPARISON_FUNC_NEVER, + { 0, 0, 0, 0 }, // BorderColor + 0, // MinLOD + FLT_MAX // MaxLOD + } +}; + + +_Use_decl_annotations_ +CommonStates::CommonStates(ID3D12Device* device) : + pImpl(std::make_unique(device)) +{ +} + +CommonStates::CommonStates(CommonStates&&) noexcept = default; +CommonStates& CommonStates::operator = (CommonStates&&) noexcept = default; +CommonStates::~CommonStates() = default; + + +D3D12_GPU_DESCRIPTOR_HANDLE CommonStates::PointWrap() const { return pImpl->Get(SamplerIndex::PointWrap); } +D3D12_GPU_DESCRIPTOR_HANDLE CommonStates::PointClamp() const { return pImpl->Get(SamplerIndex::PointClamp); } +D3D12_GPU_DESCRIPTOR_HANDLE CommonStates::LinearWrap() const { return pImpl->Get(SamplerIndex::LinearWrap); } +D3D12_GPU_DESCRIPTOR_HANDLE CommonStates::LinearClamp() const { return pImpl->Get(SamplerIndex::LinearClamp); } +D3D12_GPU_DESCRIPTOR_HANDLE CommonStates::AnisotropicWrap() const { return pImpl->Get(SamplerIndex::AnisotropicWrap); } +D3D12_GPU_DESCRIPTOR_HANDLE CommonStates::AnisotropicClamp() const { return pImpl->Get(SamplerIndex::AnisotropicClamp); } + +ID3D12DescriptorHeap* CommonStates::Heap() const noexcept { return pImpl->Heap(); } diff --git a/Common/DirectXTK12/Src/DDS.h b/Common/DirectXTK12/Src/DDS.h new file mode 100644 index 0000000..cce220c --- /dev/null +++ b/Common/DirectXTK12/Src/DDS.h @@ -0,0 +1,291 @@ +//-------------------------------------------------------------------------------------- +// DDS.h +// +// This header defines constants and structures that are useful when parsing +// DDS files. DDS files were originally designed to use several structures +// and constants that are native to DirectDraw and are defined in ddraw.h, +// such as DDSURFACEDESC2 and DDSCAPS2. This file defines similar +// (compatible) constants and structures so that one can use DDS files +// without needing to include ddraw.h. +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248926 +// http://go.microsoft.com/fwlink/?LinkId=248929 +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#pragma once + +#include + +namespace DirectX +{ + +#pragma pack(push,1) + + constexpr uint32_t DDS_MAGIC = 0x20534444; // "DDS " + + struct DDS_PIXELFORMAT + { + uint32_t size; + uint32_t flags; + uint32_t fourCC; + uint32_t RGBBitCount; + uint32_t RBitMask; + uint32_t GBitMask; + uint32_t BBitMask; + uint32_t ABitMask; + }; + +#define DDS_FOURCC 0x00000004 // DDPF_FOURCC +#define DDS_RGB 0x00000040 // DDPF_RGB +#define DDS_RGBA 0x00000041 // DDPF_RGB | DDPF_ALPHAPIXELS +#define DDS_LUMINANCE 0x00020000 // DDPF_LUMINANCE +#define DDS_LUMINANCEA 0x00020001 // DDPF_LUMINANCE | DDPF_ALPHAPIXELS +#define DDS_ALPHAPIXELS 0x00000001 // DDPF_ALPHAPIXELS +#define DDS_ALPHA 0x00000002 // DDPF_ALPHA +#define DDS_PAL8 0x00000020 // DDPF_PALETTEINDEXED8 +#define DDS_PAL8A 0x00000021 // DDPF_PALETTEINDEXED8 | DDPF_ALPHAPIXELS +#define DDS_BUMPDUDV 0x00080000 // DDPF_BUMPDUDV +// DDS_BUMPLUMINANCE 0x00040000 + +#ifndef MAKEFOURCC +#define MAKEFOURCC(ch0, ch1, ch2, ch3) \ + (static_cast(static_cast(ch0)) \ + | (static_cast(static_cast(ch1)) << 8) \ + | (static_cast(static_cast(ch2)) << 16) \ + | (static_cast(static_cast(ch3)) << 24)) +#endif /* MAKEFOURCC */ + +#ifndef DDSGLOBALCONST +#if defined(__GNUC__) && !defined(__MINGW32__) +#define DDSGLOBALCONST extern const __attribute__((weak)) +#else +#define DDSGLOBALCONST extern const __declspec(selectany) +#endif +#endif /* DDSGLOBALCONST */ + + DDSGLOBALCONST DDS_PIXELFORMAT DDSPF_DXT1 = + { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('D','X','T','1'), 0, 0, 0, 0, 0 }; + + DDSGLOBALCONST DDS_PIXELFORMAT DDSPF_DXT2 = + { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('D','X','T','2'), 0, 0, 0, 0, 0 }; + + DDSGLOBALCONST DDS_PIXELFORMAT DDSPF_DXT3 = + { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('D','X','T','3'), 0, 0, 0, 0, 0 }; + + DDSGLOBALCONST DDS_PIXELFORMAT DDSPF_DXT4 = + { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('D','X','T','4'), 0, 0, 0, 0, 0 }; + + DDSGLOBALCONST DDS_PIXELFORMAT DDSPF_DXT5 = + { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('D','X','T','5'), 0, 0, 0, 0, 0 }; + + DDSGLOBALCONST DDS_PIXELFORMAT DDSPF_BC4_UNORM = + { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('B','C','4','U'), 0, 0, 0, 0, 0 }; + + DDSGLOBALCONST DDS_PIXELFORMAT DDSPF_BC4_SNORM = + { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('B','C','4','S'), 0, 0, 0, 0, 0 }; + + DDSGLOBALCONST DDS_PIXELFORMAT DDSPF_BC5_UNORM = + { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('B','C','5','U'), 0, 0, 0, 0, 0 }; + + DDSGLOBALCONST DDS_PIXELFORMAT DDSPF_BC5_SNORM = + { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('B','C','5','S'), 0, 0, 0, 0, 0 }; + + DDSGLOBALCONST DDS_PIXELFORMAT DDSPF_R8G8_B8G8 = + { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('R','G','B','G'), 0, 0, 0, 0, 0 }; + + DDSGLOBALCONST DDS_PIXELFORMAT DDSPF_G8R8_G8B8 = + { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('G','R','G','B'), 0, 0, 0, 0, 0 }; + + DDSGLOBALCONST DDS_PIXELFORMAT DDSPF_YUY2 = + { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('Y','U','Y','2'), 0, 0, 0, 0, 0 }; + + DDSGLOBALCONST DDS_PIXELFORMAT DDSPF_UYVY = + { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('U','Y','V','Y'), 0, 0, 0, 0, 0 }; + + DDSGLOBALCONST DDS_PIXELFORMAT DDSPF_A8R8G8B8 = + { sizeof(DDS_PIXELFORMAT), DDS_RGBA, 0, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000 }; + + DDSGLOBALCONST DDS_PIXELFORMAT DDSPF_X8R8G8B8 = + { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0 }; + + DDSGLOBALCONST DDS_PIXELFORMAT DDSPF_A8B8G8R8 = + { sizeof(DDS_PIXELFORMAT), DDS_RGBA, 0, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000 }; + + DDSGLOBALCONST DDS_PIXELFORMAT DDSPF_X8B8G8R8 = + { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0 }; + + DDSGLOBALCONST DDS_PIXELFORMAT DDSPF_G16R16 = + { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 32, 0x0000ffff, 0xffff0000, 0, 0 }; + + DDSGLOBALCONST DDS_PIXELFORMAT DDSPF_R5G6B5 = + { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 16, 0xf800, 0x07e0, 0x001f, 0 }; + + DDSGLOBALCONST DDS_PIXELFORMAT DDSPF_A1R5G5B5 = + { sizeof(DDS_PIXELFORMAT), DDS_RGBA, 0, 16, 0x7c00, 0x03e0, 0x001f, 0x8000 }; + + DDSGLOBALCONST DDS_PIXELFORMAT DDSPF_X1R5G5B5 = + { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 16, 0x7c00, 0x03e0, 0x001f, 0 }; + + DDSGLOBALCONST DDS_PIXELFORMAT DDSPF_A4R4G4B4 = + { sizeof(DDS_PIXELFORMAT), DDS_RGBA, 0, 16, 0x0f00, 0x00f0, 0x000f, 0xf000 }; + + DDSGLOBALCONST DDS_PIXELFORMAT DDSPF_X4R4G4B4 = + { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 16, 0x0f00, 0x00f0, 0x000f, 0 }; + + DDSGLOBALCONST DDS_PIXELFORMAT DDSPF_R8G8B8 = + { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 24, 0xff0000, 0x00ff00, 0x0000ff, 0 }; + + DDSGLOBALCONST DDS_PIXELFORMAT DDSPF_A8R3G3B2 = + { sizeof(DDS_PIXELFORMAT), DDS_RGBA, 0, 16, 0x00e0, 0x001c, 0x0003, 0xff00 }; + + DDSGLOBALCONST DDS_PIXELFORMAT DDSPF_R3G3B2 = + { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 8, 0xe0, 0x1c, 0x03, 0 }; + + DDSGLOBALCONST DDS_PIXELFORMAT DDSPF_A4L4 = + { sizeof(DDS_PIXELFORMAT), DDS_LUMINANCEA, 0, 8, 0x0f, 0, 0, 0xf0 }; + + DDSGLOBALCONST DDS_PIXELFORMAT DDSPF_L8 = + { sizeof(DDS_PIXELFORMAT), DDS_LUMINANCE, 0, 8, 0xff, 0, 0, 0 }; + + DDSGLOBALCONST DDS_PIXELFORMAT DDSPF_L16 = + { sizeof(DDS_PIXELFORMAT), DDS_LUMINANCE, 0, 16, 0xffff, 0, 0, 0 }; + + DDSGLOBALCONST DDS_PIXELFORMAT DDSPF_A8L8 = + { sizeof(DDS_PIXELFORMAT), DDS_LUMINANCEA, 0, 16, 0x00ff, 0, 0, 0xff00 }; + + DDSGLOBALCONST DDS_PIXELFORMAT DDSPF_A8L8_ALT = + { sizeof(DDS_PIXELFORMAT), DDS_LUMINANCEA, 0, 8, 0x00ff, 0, 0, 0xff00 }; + + DDSGLOBALCONST DDS_PIXELFORMAT DDSPF_L8_NVTT1 = + { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 8, 0xff, 0, 0, 0 }; + + DDSGLOBALCONST DDS_PIXELFORMAT DDSPF_L16_NVTT1 = + { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 16, 0xffff, 0, 0, 0 }; + + DDSGLOBALCONST DDS_PIXELFORMAT DDSPF_A8L8_NVTT1 = + { sizeof(DDS_PIXELFORMAT), DDS_RGBA, 0, 16, 0x00ff, 0, 0, 0xff00 }; + + DDSGLOBALCONST DDS_PIXELFORMAT DDSPF_A8 = + { sizeof(DDS_PIXELFORMAT), DDS_ALPHA, 0, 8, 0, 0, 0, 0xff }; + + DDSGLOBALCONST DDS_PIXELFORMAT DDSPF_V8U8 = + { sizeof(DDS_PIXELFORMAT), DDS_BUMPDUDV, 0, 16, 0x00ff, 0xff00, 0, 0 }; + + DDSGLOBALCONST DDS_PIXELFORMAT DDSPF_Q8W8V8U8 = + { sizeof(DDS_PIXELFORMAT), DDS_BUMPDUDV, 0, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000 }; + + DDSGLOBALCONST DDS_PIXELFORMAT DDSPF_V16U16 = + { sizeof(DDS_PIXELFORMAT), DDS_BUMPDUDV, 0, 32, 0x0000ffff, 0xffff0000, 0, 0 }; + +// D3DFMT_A2R10G10B10/D3DFMT_A2B10G10R10 should be written using DX10 extension to avoid D3DX 10:10:10:2 reversal issue + DDSGLOBALCONST DDS_PIXELFORMAT DDSPF_A2R10G10B10 = + { sizeof(DDS_PIXELFORMAT), DDS_RGBA, 0, 32, 0x000003ff, 0x000ffc00, 0x3ff00000, 0xc0000000 }; + DDSGLOBALCONST DDS_PIXELFORMAT DDSPF_A2B10G10R10 = + { sizeof(DDS_PIXELFORMAT), DDS_RGBA, 0, 32, 0x3ff00000, 0x000ffc00, 0x000003ff, 0xc0000000 }; + +// We do not support the following legacy Direct3D 9 formats: +// DDSPF_A2W10V10U10 = { sizeof(DDS_PIXELFORMAT), DDS_BUMPDUDV, 0, 32, 0x3ff00000, 0x000ffc00, 0x000003ff, 0xc0000000 }; +// DDSPF_L6V5U5 = { sizeof(DDS_PIXELFORMAT), DDS_BUMPLUMINANCE, 0, 16, 0x001f, 0x03e0, 0xfc00, 0 }; +// DDSPF_X8L8V8U8 = { sizeof(DDS_PIXELFORMAT), DDS_BUMPLUMINANCE, 0, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0 }; + +// This indicates the DDS_HEADER_DXT10 extension is present (the format is in dxgiFormat) + DDSGLOBALCONST DDS_PIXELFORMAT DDSPF_DX10 = + { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('D','X','1','0'), 0, 0, 0, 0, 0 }; + +#define DDS_HEADER_FLAGS_TEXTURE 0x00001007 // DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT +#define DDS_HEADER_FLAGS_MIPMAP 0x00020000 // DDSD_MIPMAPCOUNT +#define DDS_HEADER_FLAGS_VOLUME 0x00800000 // DDSD_DEPTH +#define DDS_HEADER_FLAGS_PITCH 0x00000008 // DDSD_PITCH +#define DDS_HEADER_FLAGS_LINEARSIZE 0x00080000 // DDSD_LINEARSIZE + +#define DDS_HEIGHT 0x00000002 // DDSD_HEIGHT +#define DDS_WIDTH 0x00000004 // DDSD_WIDTH + +#define DDS_SURFACE_FLAGS_TEXTURE 0x00001000 // DDSCAPS_TEXTURE +#define DDS_SURFACE_FLAGS_MIPMAP 0x00400008 // DDSCAPS_COMPLEX | DDSCAPS_MIPMAP +#define DDS_SURFACE_FLAGS_CUBEMAP 0x00000008 // DDSCAPS_COMPLEX + +#define DDS_CUBEMAP_POSITIVEX 0x00000600 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEX +#define DDS_CUBEMAP_NEGATIVEX 0x00000a00 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEX +#define DDS_CUBEMAP_POSITIVEY 0x00001200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEY +#define DDS_CUBEMAP_NEGATIVEY 0x00002200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEY +#define DDS_CUBEMAP_POSITIVEZ 0x00004200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEZ +#define DDS_CUBEMAP_NEGATIVEZ 0x00008200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEZ + +#define DDS_CUBEMAP_ALLFACES ( DDS_CUBEMAP_POSITIVEX | DDS_CUBEMAP_NEGATIVEX |\ + DDS_CUBEMAP_POSITIVEY | DDS_CUBEMAP_NEGATIVEY |\ + DDS_CUBEMAP_POSITIVEZ | DDS_CUBEMAP_NEGATIVEZ ) + +#define DDS_CUBEMAP 0x00000200 // DDSCAPS2_CUBEMAP + +#define DDS_FLAGS_VOLUME 0x00200000 // DDSCAPS2_VOLUME + +// Subset here matches D3D10_RESOURCE_DIMENSION and D3D11_RESOURCE_DIMENSION + enum DDS_RESOURCE_DIMENSION : uint32_t + { + DDS_DIMENSION_TEXTURE1D = 2, + DDS_DIMENSION_TEXTURE2D = 3, + DDS_DIMENSION_TEXTURE3D = 4, + }; + + // Subset here matches D3D10_RESOURCE_MISC_FLAG and D3D11_RESOURCE_MISC_FLAG + enum DDS_RESOURCE_MISC_FLAG : uint32_t + { + DDS_RESOURCE_MISC_TEXTURECUBE = 0x4L, + }; + + enum DDS_MISC_FLAGS2 : uint32_t + { + DDS_MISC_FLAGS2_ALPHA_MODE_MASK = 0x7L, + }; + +#ifndef DDS_ALPHA_MODE_DEFINED +#define DDS_ALPHA_MODE_DEFINED + enum DDS_ALPHA_MODE : uint32_t + { + DDS_ALPHA_MODE_UNKNOWN = 0, + DDS_ALPHA_MODE_STRAIGHT = 1, + DDS_ALPHA_MODE_PREMULTIPLIED = 2, + DDS_ALPHA_MODE_OPAQUE = 3, + DDS_ALPHA_MODE_CUSTOM = 4, + }; +#endif + + struct DDS_HEADER + { + uint32_t size; + uint32_t flags; + uint32_t height; + uint32_t width; + uint32_t pitchOrLinearSize; + uint32_t depth; // only if DDS_HEADER_FLAGS_VOLUME is set in flags + uint32_t mipMapCount; + uint32_t reserved1[11]; + DDS_PIXELFORMAT ddspf; + uint32_t caps; + uint32_t caps2; + uint32_t caps3; + uint32_t caps4; + uint32_t reserved2; + }; + + struct DDS_HEADER_DXT10 + { + DXGI_FORMAT dxgiFormat; + uint32_t resourceDimension; + uint32_t miscFlag; // see D3D11_RESOURCE_MISC_FLAG + uint32_t arraySize; + uint32_t miscFlags2; // see DDS_MISC_FLAGS2 + }; + +#pragma pack(pop) + + static_assert(sizeof(DDS_PIXELFORMAT) == 32, "DDS pixel format size mismatch"); + static_assert(sizeof(DDS_HEADER) == 124, "DDS Header size mismatch"); + static_assert(sizeof(DDS_HEADER_DXT10) == 20, "DDS DX10 Extended Header size mismatch"); + +} // namespace diff --git a/Common/DirectXTK12/Src/DDSTextureLoader.cpp b/Common/DirectXTK12/Src/DDSTextureLoader.cpp new file mode 100644 index 0000000..1181b10 --- /dev/null +++ b/Common/DirectXTK12/Src/DDSTextureLoader.cpp @@ -0,0 +1,1128 @@ +//-------------------------------------------------------------------------------------- +// File: DDSTextureLoader.cpp +// +// Functions for loading a DDS texture and creating a Direct3D runtime resource for it +// +// Note these functions are useful as a light-weight runtime loader for DDS files. For +// a full-featured DDS file reader, writer, and texture processing pipeline see +// the 'Texconv' sample and the 'DirectXTex' library. +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "pch.h" + +#include "DDSTextureLoader.h" + +#include "PlatformHelpers.h" +#include "DDS.h" +#include "DirectXHelpers.h" +#include "LoaderHelpers.h" +#include "ResourceUploadBatch.h" + +using namespace DirectX; +using namespace DirectX::LoaderHelpers; + +static_assert(static_cast(DDS_DIMENSION_TEXTURE1D) == static_cast(D3D12_RESOURCE_DIMENSION_TEXTURE1D), "dds mismatch"); +static_assert(static_cast(DDS_DIMENSION_TEXTURE2D) == static_cast(D3D12_RESOURCE_DIMENSION_TEXTURE2D), "dds mismatch"); +static_assert(static_cast(DDS_DIMENSION_TEXTURE3D) == static_cast(D3D12_RESOURCE_DIMENSION_TEXTURE3D), "dds mismatch"); + +namespace +{ + inline bool IsDepthStencil(DXGI_FORMAT fmt) noexcept + { + switch (fmt) + { + case DXGI_FORMAT_R32G8X24_TYPELESS: + case DXGI_FORMAT_D32_FLOAT_S8X24_UINT: + case DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS: + case DXGI_FORMAT_X32_TYPELESS_G8X24_UINT: + case DXGI_FORMAT_D32_FLOAT: + case DXGI_FORMAT_R24G8_TYPELESS: + case DXGI_FORMAT_D24_UNORM_S8_UINT: + case DXGI_FORMAT_R24_UNORM_X8_TYPELESS: + case DXGI_FORMAT_X24_TYPELESS_G8_UINT: + case DXGI_FORMAT_D16_UNORM: + + #if (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) + case DXGI_FORMAT_D16_UNORM_S8_UINT: + case DXGI_FORMAT_R16_UNORM_X8_TYPELESS: + case DXGI_FORMAT_X16_TYPELESS_G8_UINT: + #endif + return true; + + default: + return false; + } + } + + //-------------------------------------------------------------------------------------- + inline void AdjustPlaneResource( + _In_ DXGI_FORMAT fmt, + _In_ size_t height, + _In_ size_t slicePlane, + _Inout_ D3D12_SUBRESOURCE_DATA& res) noexcept + { + switch (fmt) + { + case DXGI_FORMAT_NV12: + case DXGI_FORMAT_P010: + case DXGI_FORMAT_P016: + + #if (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) + case DXGI_FORMAT_D16_UNORM_S8_UINT: + case DXGI_FORMAT_R16_UNORM_X8_TYPELESS: + case DXGI_FORMAT_X16_TYPELESS_G8_UINT: + #endif + if (!slicePlane) + { + // Plane 0 + res.SlicePitch = res.RowPitch * static_cast(height); + } + else + { + // Plane 1 + res.pData = static_cast(res.pData) + uintptr_t(res.RowPitch) * height; + res.SlicePitch = res.RowPitch * ((static_cast(height) + 1) >> 1); + } + break; + + case DXGI_FORMAT_NV11: + if (!slicePlane) + { + // Plane 0 + res.SlicePitch = res.RowPitch * static_cast(height); + } + else + { + // Plane 1 + res.pData = static_cast(res.pData) + uintptr_t(res.RowPitch) * height; + res.RowPitch = (res.RowPitch >> 1); + res.SlicePitch = res.RowPitch * static_cast(height); + } + break; + + default: + break; + } + } + + //-------------------------------------------------------------------------------------- + HRESULT FillInitData(_In_ size_t width, + _In_ size_t height, + _In_ size_t depth, + _In_ size_t mipCount, + _In_ size_t arraySize, + _In_ size_t numberOfPlanes, + _In_ DXGI_FORMAT format, + _In_ size_t maxsize, + _In_ size_t bitSize, + _In_reads_bytes_(bitSize) const uint8_t* bitData, + _Out_ size_t& twidth, + _Out_ size_t& theight, + _Out_ size_t& tdepth, + _Out_ size_t& skipMip, + std::vector& initData) + { + if (!bitData) + { + return E_POINTER; + } + + skipMip = 0; + twidth = 0; + theight = 0; + tdepth = 0; + + size_t NumBytes = 0; + size_t RowBytes = 0; + const uint8_t* pEndBits = bitData + bitSize; + + initData.clear(); + + for (size_t p = 0; p < numberOfPlanes; ++p) + { + const uint8_t* pSrcBits = bitData; + + for (size_t j = 0; j < arraySize; j++) + { + size_t w = width; + size_t h = height; + size_t d = depth; + for (size_t i = 0; i < mipCount; i++) + { + HRESULT hr = GetSurfaceInfo(w, h, format, &NumBytes, &RowBytes, nullptr); + if (FAILED(hr)) + return hr; + + if (NumBytes > UINT32_MAX || RowBytes > UINT32_MAX) + return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); + + if ((mipCount <= 1) || !maxsize || (w <= maxsize && h <= maxsize && d <= maxsize)) + { + if (!twidth) + { + twidth = w; + theight = h; + tdepth = d; + } + + D3D12_SUBRESOURCE_DATA res = + { + pSrcBits, + static_cast(RowBytes), + static_cast(NumBytes) + }; + + AdjustPlaneResource(format, h, p, res); + + initData.emplace_back(res); + } + else if (!j) + { + // Count number of skipped mipmaps (first item only) + ++skipMip; + } + + if (pSrcBits + (NumBytes*d) > pEndBits) + { + return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF); + } + + pSrcBits += NumBytes * d; + + w = w >> 1; + h = h >> 1; + d = d >> 1; + if (w == 0) + { + w = 1; + } + if (h == 0) + { + h = 1; + } + if (d == 0) + { + d = 1; + } + } + } + } + + return initData.empty() ? E_FAIL : S_OK; + } + + //-------------------------------------------------------------------------------------- + HRESULT CreateTextureResource( + _In_ ID3D12Device* d3dDevice, + D3D12_RESOURCE_DIMENSION resDim, + size_t width, + size_t height, + size_t depth, + size_t mipCount, + size_t arraySize, + DXGI_FORMAT format, + D3D12_RESOURCE_FLAGS resFlags, + DDS_LOADER_FLAGS loadFlags, + _Outptr_ ID3D12Resource** texture) noexcept + { + if (!d3dDevice) + return E_POINTER; + + HRESULT hr = E_FAIL; + + if (loadFlags & DDS_LOADER_FORCE_SRGB) + { + format = MakeSRGB(format); + } + else if (loadFlags & DDS_LOADER_IGNORE_SRGB) + { + format = MakeLinear(format); + } + + D3D12_RESOURCE_DESC desc = {}; + desc.Width = static_cast(width); + desc.Height = static_cast(height); + desc.MipLevels = static_cast(mipCount); + desc.DepthOrArraySize = (resDim == D3D12_RESOURCE_DIMENSION_TEXTURE3D) ? static_cast(depth) : static_cast(arraySize); + desc.Format = format; + desc.Flags = resFlags; + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + desc.Dimension = resDim; + + const CD3DX12_HEAP_PROPERTIES defaultHeapProperties(D3D12_HEAP_TYPE_DEFAULT); + + hr = d3dDevice->CreateCommittedResource( + &defaultHeapProperties, + D3D12_HEAP_FLAG_NONE, + &desc, + c_initialCopyTargetState, + nullptr, + IID_GRAPHICS_PPV_ARGS(texture)); + if (SUCCEEDED(hr)) + { + assert(texture != nullptr && *texture != nullptr); + _Analysis_assume_(texture != nullptr && *texture != nullptr); + + SetDebugObjectName(*texture, L"DDSTextureLoader"); + } + + return hr; + } + + //-------------------------------------------------------------------------------------- + HRESULT CreateTextureFromDDS(_In_ ID3D12Device* d3dDevice, + _In_ const DDS_HEADER* header, + _In_reads_bytes_(bitSize) const uint8_t* bitData, + size_t bitSize, + size_t maxsize, + D3D12_RESOURCE_FLAGS resFlags, + DDS_LOADER_FLAGS loadFlags, + _Outptr_ ID3D12Resource** texture, + std::vector& subresources, + _Out_opt_ bool* outIsCubeMap) noexcept(false) + { + HRESULT hr = S_OK; + + const UINT width = header->width; + UINT height = header->height; + UINT depth = header->depth; + + D3D12_RESOURCE_DIMENSION resDim = D3D12_RESOURCE_DIMENSION_UNKNOWN; + UINT arraySize = 1; + DXGI_FORMAT format = DXGI_FORMAT_UNKNOWN; + bool isCubeMap = false; + + size_t mipCount = header->mipMapCount; + if (0 == mipCount) + { + mipCount = 1; + } + + if ((header->ddspf.flags & DDS_FOURCC) && + (MAKEFOURCC('D', 'X', '1', '0') == header->ddspf.fourCC)) + { + auto d3d10ext = reinterpret_cast(reinterpret_cast(header) + sizeof(DDS_HEADER)); + + arraySize = d3d10ext->arraySize; + if (arraySize == 0) + { + return HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + } + + switch (d3d10ext->dxgiFormat) + { + case DXGI_FORMAT_NV12: + case DXGI_FORMAT_P010: + case DXGI_FORMAT_P016: + case DXGI_FORMAT_420_OPAQUE: + if ((d3d10ext->resourceDimension != D3D12_RESOURCE_DIMENSION_TEXTURE2D) + || (width % 2) != 0 || (height % 2) != 0) + { + DebugTrace("ERROR: Video texture does not meet width/height requirements.\n"); + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + break; + + case DXGI_FORMAT_YUY2: + case DXGI_FORMAT_Y210: + case DXGI_FORMAT_Y216: + case DXGI_FORMAT_P208: + if ((width % 2) != 0) + { + DebugTrace("ERROR: Video texture does not meet width requirements.\n"); + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + break; + + case DXGI_FORMAT_NV11: + if ((width % 4) != 0) + { + DebugTrace("ERROR: Video texture does not meet width requirements.\n"); + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + break; + + case DXGI_FORMAT_AI44: + case DXGI_FORMAT_IA44: + case DXGI_FORMAT_P8: + case DXGI_FORMAT_A8P8: + DebugTrace("ERROR: Legacy stream video texture formats are not supported by Direct3D.\n"); + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + + case DXGI_FORMAT_V208: + if ((d3d10ext->resourceDimension != D3D12_RESOURCE_DIMENSION_TEXTURE2D) + || (height % 2) != 0) + { + DebugTrace("ERROR: Video texture does not meet height requirements.\n"); + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + break; + + default: + if (BitsPerPixel(d3d10ext->dxgiFormat) == 0) + { + DebugTrace("ERROR: Unknown DXGI format (%u)\n", static_cast(d3d10ext->dxgiFormat)); + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + break; + } + + format = d3d10ext->dxgiFormat; + + switch (d3d10ext->resourceDimension) + { + case D3D12_RESOURCE_DIMENSION_TEXTURE1D: + // D3DX writes 1D textures with a fixed Height of 1 + if ((header->flags & DDS_HEIGHT) && height != 1) + { + return HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + } + height = depth = 1; + break; + + case D3D12_RESOURCE_DIMENSION_TEXTURE2D: + if (d3d10ext->miscFlag & 0x4 /* RESOURCE_MISC_TEXTURECUBE */) + { + arraySize *= 6; + isCubeMap = true; + } + depth = 1; + break; + + case D3D12_RESOURCE_DIMENSION_TEXTURE3D: + if (!(header->flags & DDS_HEADER_FLAGS_VOLUME)) + { + return HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + } + + if (arraySize > 1) + { + DebugTrace("ERROR: Volume textures are not texture arrays\n"); + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + break; + + case D3D12_RESOURCE_DIMENSION_BUFFER: + DebugTrace("ERROR: Resource dimension buffer type not supported for textures\n"); + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + + case D3D12_RESOURCE_DIMENSION_UNKNOWN: + default: + DebugTrace("ERROR: Unknown resource dimension (%u)\n", static_cast(d3d10ext->resourceDimension)); + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + + resDim = static_cast(d3d10ext->resourceDimension); + } + else + { + format = GetDXGIFormat(header->ddspf); + + if (format == DXGI_FORMAT_UNKNOWN) + { + DebugTrace("ERROR: DDSTextureLoader does not support all legacy DDS formats. Consider using DirectXTex.\n"); + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + + if (header->flags & DDS_HEADER_FLAGS_VOLUME) + { + resDim = D3D12_RESOURCE_DIMENSION_TEXTURE3D; + } + else + { + if (header->caps2 & DDS_CUBEMAP) + { + // We require all six faces to be defined + if ((header->caps2 & DDS_CUBEMAP_ALLFACES) != DDS_CUBEMAP_ALLFACES) + { + DebugTrace("ERROR: DirectX 12 does not support partial cubemaps\n"); + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + + arraySize = 6; + isCubeMap = true; + } + + depth = 1; + resDim = D3D12_RESOURCE_DIMENSION_TEXTURE2D; + + // Note there's no way for a legacy Direct3D 9 DDS to express a '1D' texture + } + + assert(BitsPerPixel(format) != 0); + } + + // Bound sizes (for security purposes we don't trust DDS file metadata larger than the Direct3D hardware requirements) + if (mipCount > D3D12_REQ_MIP_LEVELS) + { + DebugTrace("ERROR: Too many mipmap levels defined for DirectX 12 (%zu).\n", mipCount); + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + + switch (resDim) + { + case D3D12_RESOURCE_DIMENSION_TEXTURE1D: + if ((arraySize > D3D12_REQ_TEXTURE1D_ARRAY_AXIS_DIMENSION) || + (width > D3D12_REQ_TEXTURE1D_U_DIMENSION)) + { + DebugTrace("ERROR: Resource dimensions too large for DirectX 12 (1D: array %u, size %u)\n", arraySize, width); + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + break; + + case D3D12_RESOURCE_DIMENSION_TEXTURE2D: + if (isCubeMap) + { + // This is the right bound because we set arraySize to (NumCubes*6) above + if ((arraySize > D3D12_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION) || + (width > D3D12_REQ_TEXTURECUBE_DIMENSION) || + (height > D3D12_REQ_TEXTURECUBE_DIMENSION)) + { + DebugTrace("ERROR: Resource dimensions too large for DirectX 12 (2D cubemap: array %u, size %u by %u)\n", arraySize, width, height); + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + } + else if ((arraySize > D3D12_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION) || + (width > D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION) || + (height > D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION)) + { + DebugTrace("ERROR: Resource dimensions too large for DirectX 12 (2D: array %u, size %u by %u)\n", arraySize, width, height); + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + break; + + case D3D12_RESOURCE_DIMENSION_TEXTURE3D: + if ((arraySize > 1) || + (width > D3D12_REQ_TEXTURE3D_U_V_OR_W_DIMENSION) || + (height > D3D12_REQ_TEXTURE3D_U_V_OR_W_DIMENSION) || + (depth > D3D12_REQ_TEXTURE3D_U_V_OR_W_DIMENSION)) + { + DebugTrace("ERROR: Resource dimensions too large for DirectX 12 (3D: array %u, size %u by %u by %u)\n", arraySize, width, height, depth); + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + break; + + case D3D12_RESOURCE_DIMENSION_BUFFER: + DebugTrace("ERROR: Resource dimension buffer type not supported for textures\n"); + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + + default: + DebugTrace("ERROR: Unknown resource dimension (%u)\n", static_cast(resDim)); + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + + const UINT numberOfPlanes = D3D12GetFormatPlaneCount(d3dDevice, format); + if (!numberOfPlanes) + return E_INVALIDARG; + + if ((numberOfPlanes > 1) && IsDepthStencil(format)) + { + // DirectX 12 uses planes for stencil, DirectX 11 does not + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + + if (outIsCubeMap != nullptr) + { + *outIsCubeMap = isCubeMap; + } + + // Create the texture + size_t numberOfResources = (resDim == D3D12_RESOURCE_DIMENSION_TEXTURE3D) + ? 1 : arraySize; + numberOfResources *= mipCount; + numberOfResources *= numberOfPlanes; + + if (numberOfResources > D3D12_REQ_SUBRESOURCES) + return E_INVALIDARG; + + subresources.reserve(numberOfResources); + + size_t skipMip = 0; + size_t twidth = 0; + size_t theight = 0; + size_t tdepth = 0; + hr = FillInitData(width, height, depth, mipCount, arraySize, + numberOfPlanes, format, + maxsize, bitSize, bitData, + twidth, theight, tdepth, skipMip, subresources); + + if (SUCCEEDED(hr)) + { + size_t reservedMips = mipCount; + if (loadFlags & (DDS_LOADER_MIP_AUTOGEN | DDS_LOADER_MIP_RESERVE)) + { + reservedMips = std::min(D3D12_REQ_MIP_LEVELS, + LoaderHelpers::CountMips(width, height)); + } + + hr = CreateTextureResource(d3dDevice, resDim, twidth, theight, tdepth, reservedMips - skipMip, arraySize, + format, resFlags, loadFlags, texture); + + if (FAILED(hr) && !maxsize && (mipCount > 1)) + { + subresources.clear(); + + maxsize = static_cast( + (resDim == D3D12_RESOURCE_DIMENSION_TEXTURE3D) + ? D3D12_REQ_TEXTURE3D_U_V_OR_W_DIMENSION + : D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION); + + hr = FillInitData(width, height, depth, mipCount, arraySize, + numberOfPlanes, format, + maxsize, bitSize, bitData, + twidth, theight, tdepth, skipMip, subresources); + if (SUCCEEDED(hr)) + { + hr = CreateTextureResource(d3dDevice, resDim, twidth, theight, tdepth, mipCount - skipMip, arraySize, + format, resFlags, loadFlags, texture); + } + } + } + + if (FAILED(hr)) + { + subresources.clear(); + } + + return hr; + } + + //-------------------------------------------------------------------------------------- + void SetDebugTextureInfo( + _In_z_ const wchar_t* fileName, + _In_ ID3D12Resource* texture) noexcept + { + #if !defined(NO_D3D12_DEBUG_NAME) && ( defined(_DEBUG) || defined(PROFILE) ) + const wchar_t* pstrName = wcsrchr(fileName, '\\'); + if (!pstrName) + { + pstrName = fileName; + } + else + { + pstrName++; + } + texture->SetName(pstrName); + #else + UNREFERENCED_PARAMETER(fileName); + UNREFERENCED_PARAMETER(texture); + #endif + } + + //-------------------------------------------------------------------------------------- + DXGI_FORMAT GetPixelFormat(const DDS_HEADER* header) noexcept + { + if ((header->ddspf.flags & DDS_FOURCC) && + (MAKEFOURCC('D', 'X', '1', '0') == header->ddspf.fourCC)) + { + auto d3d10ext = reinterpret_cast(reinterpret_cast(header) + sizeof(DDS_HEADER)); + return d3d10ext->dxgiFormat; + } + else + return GetDXGIFormat(header->ddspf); + } +} // anonymous namespace + + +//-------------------------------------------------------------------------------------- +_Use_decl_annotations_ +HRESULT DirectX::LoadDDSTextureFromMemory( + ID3D12Device* d3dDevice, + const uint8_t* ddsData, + size_t ddsDataSize, + ID3D12Resource** texture, + std::vector& subresources, + size_t maxsize, + DDS_ALPHA_MODE* alphaMode, + bool* isCubeMap) +{ + return LoadDDSTextureFromMemoryEx( + d3dDevice, + ddsData, + ddsDataSize, + maxsize, + D3D12_RESOURCE_FLAG_NONE, + DDS_LOADER_DEFAULT, + texture, + subresources, + alphaMode, + isCubeMap); +} + + +_Use_decl_annotations_ +HRESULT DirectX::LoadDDSTextureFromMemoryEx( + ID3D12Device* d3dDevice, + const uint8_t* ddsData, + size_t ddsDataSize, + size_t maxsize, + D3D12_RESOURCE_FLAGS resFlags, + DDS_LOADER_FLAGS loadFlags, + ID3D12Resource** texture, + std::vector& subresources, + DDS_ALPHA_MODE* alphaMode, + bool* isCubeMap) +{ + if (texture) + { + *texture = nullptr; + } + if (alphaMode) + { + *alphaMode = DDS_ALPHA_MODE_UNKNOWN; + } + if (isCubeMap) + { + *isCubeMap = false; + } + + if (!d3dDevice || !ddsData || !texture) + { + return E_INVALIDARG; + } + + // Validate DDS file in memory + const DDS_HEADER* header = nullptr; + const uint8_t* bitData = nullptr; + size_t bitSize = 0; + + HRESULT hr = LoadTextureDataFromMemory(ddsData, + ddsDataSize, + &header, + &bitData, + &bitSize + ); + if (FAILED(hr)) + { + return hr; + } + + hr = CreateTextureFromDDS(d3dDevice, + header, bitData, bitSize, maxsize, + resFlags, loadFlags, + texture, subresources, isCubeMap); + if (SUCCEEDED(hr)) + { + SetDebugObjectName(*texture, L"DDSTextureLoader"); + + if (alphaMode) + *alphaMode = GetAlphaMode(header); + } + + return hr; +} + + +//-------------------------------------------------------------------------------------- +_Use_decl_annotations_ +HRESULT DirectX::LoadDDSTextureFromFile( + ID3D12Device* d3dDevice, + const wchar_t* fileName, + ID3D12Resource** texture, + std::unique_ptr& ddsData, + std::vector& subresources, + size_t maxsize, + DDS_ALPHA_MODE* alphaMode, + bool* isCubeMap) +{ + return LoadDDSTextureFromFileEx( + d3dDevice, + fileName, + maxsize, + D3D12_RESOURCE_FLAG_NONE, + DDS_LOADER_DEFAULT, + texture, + ddsData, + subresources, + alphaMode, + isCubeMap); +} + +_Use_decl_annotations_ +HRESULT DirectX::LoadDDSTextureFromFileEx( + ID3D12Device* d3dDevice, + const wchar_t* fileName, + size_t maxsize, + D3D12_RESOURCE_FLAGS resFlags, + DDS_LOADER_FLAGS loadFlags, + ID3D12Resource** texture, + std::unique_ptr& ddsData, + std::vector& subresources, + DDS_ALPHA_MODE* alphaMode, + bool* isCubeMap) +{ + if (texture) + { + *texture = nullptr; + } + if (alphaMode) + { + *alphaMode = DDS_ALPHA_MODE_UNKNOWN; + } + if (isCubeMap) + { + *isCubeMap = false; + } + + if (!d3dDevice || !fileName || !texture) + { + return E_INVALIDARG; + } + + const DDS_HEADER* header = nullptr; + const uint8_t* bitData = nullptr; + size_t bitSize = 0; + + HRESULT hr = LoadTextureDataFromFile(fileName, + ddsData, + &header, + &bitData, + &bitSize + ); + if (FAILED(hr)) + { + return hr; + } + + hr = CreateTextureFromDDS(d3dDevice, + header, bitData, bitSize, maxsize, + resFlags, loadFlags, + texture, subresources, isCubeMap); + + if (SUCCEEDED(hr)) + { + SetDebugTextureInfo(fileName, *texture); + + if (alphaMode) + *alphaMode = GetAlphaMode(header); + } + + return hr; +} + +//-------------------------------------------------------------------------------------- +_Use_decl_annotations_ +HRESULT DirectX::CreateDDSTextureFromMemory( + ID3D12Device* d3dDevice, + ResourceUploadBatch& resourceUpload, + const uint8_t* ddsData, + size_t ddsDataSize, + ID3D12Resource** texture, + bool generateMipsIfMissing, + size_t maxsize, + DDS_ALPHA_MODE* alphaMode, + bool* isCubeMap) +{ + return CreateDDSTextureFromMemoryEx( + d3dDevice, + resourceUpload, + ddsData, + ddsDataSize, + maxsize, + D3D12_RESOURCE_FLAG_NONE, + generateMipsIfMissing ? DDS_LOADER_MIP_AUTOGEN : DDS_LOADER_DEFAULT, + texture, + alphaMode, + isCubeMap); +} + + +_Use_decl_annotations_ +HRESULT DirectX::CreateDDSTextureFromMemoryEx( + ID3D12Device* d3dDevice, + ResourceUploadBatch& resourceUpload, + const uint8_t* ddsData, + size_t ddsDataSize, + size_t maxsize, + D3D12_RESOURCE_FLAGS resFlags, + DDS_LOADER_FLAGS loadFlags, + ID3D12Resource** texture, + DDS_ALPHA_MODE* alphaMode, + bool* isCubeMap) +{ + if (texture) + { + *texture = nullptr; + } + if (alphaMode) + { + *alphaMode = DDS_ALPHA_MODE_UNKNOWN; + } + if (isCubeMap) + { + *isCubeMap = false; + } + + if (!d3dDevice || !ddsData || !texture) + { + return E_INVALIDARG; + } + + // Validate DDS file in memory + const DDS_HEADER* header = nullptr; + const uint8_t* bitData = nullptr; + size_t bitSize = 0; + + HRESULT hr = LoadTextureDataFromMemory(ddsData, + ddsDataSize, + &header, + &bitData, + &bitSize + ); + if (FAILED(hr)) + { + return hr; + } + + if (loadFlags & DDS_LOADER_MIP_AUTOGEN) + { + const DXGI_FORMAT fmt = GetPixelFormat(header); + if (!resourceUpload.IsSupportedForGenerateMips(fmt)) + { + DebugTrace("WARNING: Autogen of mips ignored (device doesn't support this format (%d) or trying to use a copy queue)\n", static_cast(fmt)); + loadFlags &= ~DDS_LOADER_MIP_AUTOGEN; + } + } + + std::vector subresources; + hr = CreateTextureFromDDS(d3dDevice, + header, bitData, bitSize, maxsize, + resFlags, loadFlags, + texture, subresources, isCubeMap); + + if (SUCCEEDED(hr)) + { + SetDebugObjectName(*texture, L"DDSTextureLoader"); + + if (alphaMode) + *alphaMode = GetAlphaMode(header); + + resourceUpload.Upload( + *texture, + 0, + subresources.data(), + static_cast(subresources.size())); + + resourceUpload.Transition( + *texture, + D3D12_RESOURCE_STATE_COPY_DEST, + D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); + + // If it's missing mips, let's generate them + #if defined(_MSC_VER) || !defined(_WIN32) + const size_t mipLevels = (*texture)->GetDesc().MipLevels; + #else + D3D12_RESOURCE_DESC tmpDesc; + const size_t mipLevels = (*texture)->GetDesc(&tmpDesc)->MipLevels; + #endif + + if ((loadFlags & DDS_LOADER_MIP_AUTOGEN) && subresources.size() != mipLevels) + { + resourceUpload.GenerateMips(*texture); + } + } + + return hr; +} + + +//-------------------------------------------------------------------------------------- +_Use_decl_annotations_ +HRESULT DirectX::CreateDDSTextureFromFile( + ID3D12Device* d3dDevice, + ResourceUploadBatch& resourceUpload, + const wchar_t* fileName, + ID3D12Resource** texture, + bool generateMipsIfMissing, + size_t maxsize, + DDS_ALPHA_MODE* alphaMode, + bool* isCubeMap) +{ + return CreateDDSTextureFromFileEx( + d3dDevice, + resourceUpload, + fileName, + maxsize, + D3D12_RESOURCE_FLAG_NONE, + generateMipsIfMissing ? DDS_LOADER_MIP_AUTOGEN : DDS_LOADER_DEFAULT, + texture, + alphaMode, + isCubeMap); +} + +_Use_decl_annotations_ +HRESULT DirectX::CreateDDSTextureFromFileEx( + ID3D12Device* d3dDevice, + ResourceUploadBatch& resourceUpload, + const wchar_t* fileName, + size_t maxsize, + D3D12_RESOURCE_FLAGS resFlags, + DDS_LOADER_FLAGS loadFlags, + ID3D12Resource** texture, + DDS_ALPHA_MODE* alphaMode, + bool* isCubeMap) +{ + if (texture) + { + *texture = nullptr; + } + if (alphaMode) + { + *alphaMode = DDS_ALPHA_MODE_UNKNOWN; + } + if (isCubeMap) + { + *isCubeMap = false; + } + + if (!d3dDevice || !fileName || !texture) + { + return E_INVALIDARG; + } + + const DDS_HEADER* header = nullptr; + const uint8_t* bitData = nullptr; + size_t bitSize = 0; + + std::unique_ptr ddsData; + HRESULT hr = LoadTextureDataFromFile(fileName, + ddsData, + &header, + &bitData, + &bitSize + ); + if (FAILED(hr)) + { + return hr; + } + + if (loadFlags & DDS_LOADER_MIP_AUTOGEN) + { + const DXGI_FORMAT fmt = GetPixelFormat(header); + if (!resourceUpload.IsSupportedForGenerateMips(fmt)) + { + DebugTrace("WARNING: Autogen of mips ignored (device doesn't support this format (%d) or trying to use a copy queue)\n", static_cast(fmt)); + loadFlags &= ~DDS_LOADER_MIP_AUTOGEN; + } + } + + std::vector subresources; + hr = CreateTextureFromDDS(d3dDevice, + header, bitData, bitSize, maxsize, + resFlags, loadFlags, + texture, subresources, isCubeMap); + + if (SUCCEEDED(hr)) + { + SetDebugTextureInfo(fileName, *texture); + + if (alphaMode) + *alphaMode = GetAlphaMode(header); + + resourceUpload.Upload( + *texture, + 0, + subresources.data(), + static_cast(subresources.size())); + + resourceUpload.Transition( + *texture, + D3D12_RESOURCE_STATE_COPY_DEST, + D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); + + // If it's missing mips, let's generate them + #if defined(_MSC_VER) || !defined(_WIN32) + const size_t mipLevels = (*texture)->GetDesc().MipLevels; + #else + D3D12_RESOURCE_DESC tmpDesc; + const size_t mipLevels = (*texture)->GetDesc(&tmpDesc)->MipLevels; + #endif + + if ((loadFlags & DDS_LOADER_MIP_AUTOGEN) && subresources.size() != mipLevels) + { + resourceUpload.GenerateMips(*texture); + } + } + + return hr; +} + + +//-------------------------------------------------------------------------------------- +// Adapters for /Zc:wchar_t- clients + +#if defined(_MSC_VER) && !defined(_NATIVE_WCHAR_T_DEFINED) + +namespace DirectX +{ + HRESULT __cdecl LoadDDSTextureFromFile( + _In_ ID3D12Device* d3dDevice, + _In_z_ const __wchar_t* szFileName, + _Outptr_ ID3D12Resource** texture, + std::unique_ptr& ddsData, + std::vector& subresources, + size_t maxsize, + _Out_opt_ DDS_ALPHA_MODE* alphaMode, + _Out_opt_ bool* isCubeMap) + { + return LoadDDSTextureFromFile(d3dDevice, + reinterpret_cast(szFileName), + texture, ddsData, subresources, maxsize, alphaMode, isCubeMap); + } + + HRESULT __cdecl CreateDDSTextureFromFile( + _In_ ID3D12Device* device, + ResourceUploadBatch& resourceUpload, + _In_z_ const __wchar_t* szFileName, + _Outptr_ ID3D12Resource** texture, + bool generateMipsIfMissing, + size_t maxsize, + _Out_opt_ DDS_ALPHA_MODE* alphaMode, + _Out_opt_ bool* isCubeMap) + { + return CreateDDSTextureFromFile(device, resourceUpload, + reinterpret_cast(szFileName), + texture, generateMipsIfMissing, maxsize, alphaMode, isCubeMap); + } + + HRESULT __cdecl LoadDDSTextureFromFileEx( + _In_ ID3D12Device* d3dDevice, + _In_z_ const __wchar_t* szFileName, + size_t maxsize, + D3D12_RESOURCE_FLAGS resFlags, + DDS_LOADER_FLAGS loadFlags, + _Outptr_ ID3D12Resource** texture, + std::unique_ptr& ddsData, + std::vector& subresources, + _Out_opt_ DDS_ALPHA_MODE* alphaMode, + _Out_opt_ bool* isCubeMap) + { + return LoadDDSTextureFromFileEx(d3dDevice, + reinterpret_cast(szFileName), + maxsize, resFlags, loadFlags, texture, ddsData, subresources, alphaMode, isCubeMap); + } + + HRESULT __cdecl CreateDDSTextureFromFileEx( + _In_ ID3D12Device* device, + ResourceUploadBatch& resourceUpload, + _In_z_ const __wchar_t* szFileName, + size_t maxsize, + D3D12_RESOURCE_FLAGS resFlags, + DDS_LOADER_FLAGS loadFlags, + _Outptr_ ID3D12Resource** texture, + _Out_opt_ DDS_ALPHA_MODE* alphaMode, + _Out_opt_ bool* isCubeMap) + { + return CreateDDSTextureFromFileEx(device, resourceUpload, + reinterpret_cast(szFileName), + maxsize, resFlags, loadFlags, texture, alphaMode, isCubeMap); + } +} + +#endif // !_NATIVE_WCHAR_T_DEFINED diff --git a/Common/DirectXTK12/Src/DebugEffect.cpp b/Common/DirectXTK12/Src/DebugEffect.cpp new file mode 100644 index 0000000..51e8588 --- /dev/null +++ b/Common/DirectXTK12/Src/DebugEffect.cpp @@ -0,0 +1,459 @@ +//-------------------------------------------------------------------------------------- +// File: DebugEffect.cpp +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "pch.h" +#include "EffectCommon.h" + +using namespace DirectX; + +namespace +{ + // Constant buffer layout. Must match the shader! + struct DebugEffectConstants + { + XMVECTOR ambientDownAndAlpha; + XMVECTOR ambientRange; + + XMMATRIX world; + XMVECTOR worldInverseTranspose[3]; + XMMATRIX worldViewProj; + }; + + static_assert((sizeof(DebugEffectConstants) % 16) == 0, "CB size not padded correctly"); + + + // Traits type describes our characteristics to the EffectBase template. + struct DebugEffectTraits + { + using ConstantBufferType = DebugEffectConstants; + + static constexpr int VertexShaderCount = 8; + static constexpr int PixelShaderCount = 4; + static constexpr int ShaderPermutationCount = 32; + static constexpr int RootSignatureCount = 1; + }; +} + +// Internal DebugEffect implementation class. +class DebugEffect::Impl : public EffectBase +{ +public: + Impl(_In_ ID3D12Device* device, uint32_t effectFlags, const EffectPipelineStateDescription& pipelineDescription, + DebugEffect::Mode debugMode); + + enum RootParameterIndex + { + ConstantBuffer, + RootParameterCount + }; + + int GetPipelineStatePermutation(DebugEffect::Mode debugMode, uint32_t effectFlags) const noexcept; + + void Apply(_In_ ID3D12GraphicsCommandList* commandList); +}; + + +#pragma region Shaders +// Include the precompiled shader code. +namespace +{ +#ifdef _GAMING_XBOX_SCARLETT +#include "XboxGamingScarlettDebugEffect_VSDebug.inc" +#include "XboxGamingScarlettDebugEffect_VSDebugInst.inc" + +#include "XboxGamingScarlettDebugEffect_VSDebugVc.inc" +#include "XboxGamingScarlettDebugEffect_VSDebugVcInst.inc" + +#include "XboxGamingScarlettDebugEffect_VSDebugBn.inc" +#include "XboxGamingScarlettDebugEffect_VSDebugBnInst.inc" + +#include "XboxGamingScarlettDebugEffect_VSDebugVcBn.inc" +#include "XboxGamingScarlettDebugEffect_VSDebugVcBnInst.inc" + +#include "XboxGamingScarlettDebugEffect_PSHemiAmbient.inc" +#include "XboxGamingScarlettDebugEffect_PSRGBNormals.inc" +#include "XboxGamingScarlettDebugEffect_PSRGBTangents.inc" +#include "XboxGamingScarlettDebugEffect_PSRGBBiTangents.inc" +#elif defined(_GAMING_XBOX) +#include "XboxGamingXboxOneDebugEffect_VSDebug.inc" +#include "XboxGamingXboxOneDebugEffect_VSDebugInst.inc" + +#include "XboxGamingXboxOneDebugEffect_VSDebugVc.inc" +#include "XboxGamingXboxOneDebugEffect_VSDebugVcInst.inc" + +#include "XboxGamingXboxOneDebugEffect_VSDebugBn.inc" +#include "XboxGamingXboxOneDebugEffect_VSDebugBnInst.inc" + +#include "XboxGamingXboxOneDebugEffect_VSDebugVcBn.inc" +#include "XboxGamingXboxOneDebugEffect_VSDebugVcBnInst.inc" + +#include "XboxGamingXboxOneDebugEffect_PSHemiAmbient.inc" +#include "XboxGamingXboxOneDebugEffect_PSRGBNormals.inc" +#include "XboxGamingXboxOneDebugEffect_PSRGBTangents.inc" +#include "XboxGamingXboxOneDebugEffect_PSRGBBiTangents.inc" +#elif defined(_XBOX_ONE) && defined(_TITLE) +#include "XboxOneDebugEffect_VSDebug.inc" +#include "XboxOneDebugEffect_VSDebugInst.inc" + +#include "XboxOneDebugEffect_VSDebugVc.inc" +#include "XboxOneDebugEffect_VSDebugVcInst.inc" + +#include "XboxOneDebugEffect_VSDebugBn.inc" +#include "XboxOneDebugEffect_VSDebugBnInst.inc" + +#include "XboxOneDebugEffect_VSDebugVcBn.inc" +#include "XboxOneDebugEffect_VSDebugVcBnInst.inc" + +#include "XboxOneDebugEffect_PSHemiAmbient.inc" +#include "XboxOneDebugEffect_PSRGBNormals.inc" +#include "XboxOneDebugEffect_PSRGBTangents.inc" +#include "XboxOneDebugEffect_PSRGBBiTangents.inc" +#else +#include "DebugEffect_VSDebug.inc" +#include "DebugEffect_VSDebugInst.inc" + +#include "DebugEffect_VSDebugVc.inc" +#include "DebugEffect_VSDebugVcInst.inc" + +#include "DebugEffect_VSDebugBn.inc" +#include "DebugEffect_VSDebugBnInst.inc" + +#include "DebugEffect_VSDebugVcBn.inc" +#include "DebugEffect_VSDebugVcBnInst.inc" + +#include "DebugEffect_PSHemiAmbient.inc" +#include "DebugEffect_PSRGBNormals.inc" +#include "DebugEffect_PSRGBTangents.inc" +#include "DebugEffect_PSRGBBiTangents.inc" +#endif +} + + +template<> +const D3D12_SHADER_BYTECODE EffectBase::VertexShaderBytecode[] = +{ + { DebugEffect_VSDebug, sizeof(DebugEffect_VSDebug) }, + { DebugEffect_VSDebugVc, sizeof(DebugEffect_VSDebugVc) }, + { DebugEffect_VSDebugBn, sizeof(DebugEffect_VSDebugBn) }, + { DebugEffect_VSDebugVcBn, sizeof(DebugEffect_VSDebugVcBn) }, + { DebugEffect_VSDebugInst, sizeof(DebugEffect_VSDebugInst) }, + { DebugEffect_VSDebugVcInst, sizeof(DebugEffect_VSDebugVcInst) }, + { DebugEffect_VSDebugBnInst, sizeof(DebugEffect_VSDebugBnInst) }, + { DebugEffect_VSDebugVcBnInst, sizeof(DebugEffect_VSDebugVcBnInst) }, +}; + + +template<> +const int EffectBase::VertexShaderIndices[] = +{ + 0, // default + 0, // normals + 0, // tangents + 0, // bitangents + + 1, // vertex color + default + 1, // vertex color + normals + 1, // vertex color + tangents + 1, // vertex color + bitangents + + 2, // default (biased vertex normal) + 2, // normals (biased vertex normal) + 2, // tangents (biased vertex normal) + 2, // bitangents (biased vertex normal) + + 3, // vertex color (biased vertex normal) + 3, // vertex color (biased vertex normal) + normals + 3, // vertex color (biased vertex normal) + tangents + 3, // vertex color (biased vertex normal) + bitangents + + 4, // instancing + 4, // instancing + normals + 4, // instancing + tangents + 4, // instancing + bitangents + + 5, // instancing + vertex color + default + 5, // instancing + vertex color + normals + 5, // instancing + vertex color + tangents + 5, // instancing + vertex color + bitangents + + 6, // instancing (biased vertex normal) + 6, // instancing + normals (biased vertex normal) + 6, // instancing + tangents (biased vertex normal) + 6, // instancing + bitangents (biased vertex normal) + + 7, // instancing + vertex color (biased vertex normal) + 7, // instancing + vertex color (biased vertex normal) + normals + 7, // instancing + vertex color (biased vertex normal) + tangents + 7, // instancing + vertex color (biased vertex normal) + bitangents}; +}; + +template<> +const D3D12_SHADER_BYTECODE EffectBase::PixelShaderBytecode[] = +{ + { DebugEffect_PSHemiAmbient, sizeof(DebugEffect_PSHemiAmbient) }, + { DebugEffect_PSRGBNormals, sizeof(DebugEffect_PSRGBNormals) }, + { DebugEffect_PSRGBTangents, sizeof(DebugEffect_PSRGBTangents) }, + { DebugEffect_PSRGBBiTangents, sizeof(DebugEffect_PSRGBBiTangents) }, +}; + + +template<> +const int EffectBase::PixelShaderIndices[] = +{ + 0, // default + 1, // normals + 2, // tangents + 3, // bitangents + + 0, // vertex color + default + 1, // vertex color + normals + 2, // vertex color + tangents + 3, // vertex color + bitangents + + 0, // default (biased vertex normal) + 1, // normals (biased vertex normal) + 2, // tangents (biased vertex normal) + 3, // bitangents (biased vertex normal) + + 0, // vertex color (biased vertex normal) + 1, // vertex color (biased vertex normal) + normals + 2, // vertex color (biased vertex normal) + tangents + 3, // vertex color (biased vertex normal) + bitangents + + 0, // instancing + 1, // instancing + normals + 2, // instancing + tangents + 3, // instancing + bitangents + + 0, // instancing + vertex color + default + 1, // instancing + vertex color + normals + 2, // instancing + vertex color + tangents + 3, // instancing + vertex color + bitangents + + 0, // instancing (biased vertex normal) + 1, // instancing + normals (biased vertex normal) + 2, // instancing + tangents (biased vertex normal) + 3, // instancing + bitangents (biased vertex normal) + + 0, // instancing + vertex color (biased vertex normal) + 1, // instancing + vertex color (biased vertex normal) + normals + 2, // instancing + vertex color (biased vertex normal) + tangents + 3, // instancing + vertex color (biased vertex normal) + bitangents +}; +#pragma endregion + +// Global pool of per-deviceDebugEffect resources. +template<> +SharedResourcePool::DeviceResources> EffectBase::deviceResourcesPool = {}; + + +// Constructor. +DebugEffect::Impl::Impl( + _In_ ID3D12Device* device, + uint32_t effectFlags, + const EffectPipelineStateDescription& pipelineDescription, + DebugEffect::Mode debugMode) + : EffectBase(device) +{ + static_assert(static_cast(std::size(EffectBase::VertexShaderIndices)) == DebugEffectTraits::ShaderPermutationCount, "array/max mismatch"); + static_assert(static_cast(std::size(EffectBase::VertexShaderBytecode)) == DebugEffectTraits::VertexShaderCount, "array/max mismatch"); + static_assert(static_cast(std::size(EffectBase::PixelShaderBytecode)) == DebugEffectTraits::PixelShaderCount, "array/max mismatch"); + static_assert(static_cast(std::size(EffectBase::PixelShaderIndices)) == DebugEffectTraits::ShaderPermutationCount, "array/max mismatch"); + + static const XMVECTORF32 s_lower = { { { 0.f, 0.f, 0.f, 1.f } } }; + + constants.ambientDownAndAlpha = s_lower; + constants.ambientRange = g_XMOne; + + // Create root signature. + { + ENUM_FLAGS_CONSTEXPR D3D12_ROOT_SIGNATURE_FLAGS rootSignatureFlags = + D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT + | D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS + | D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS + | D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS +#ifdef _GAMING_XBOX_SCARLETT + | D3D12_ROOT_SIGNATURE_FLAG_DENY_AMPLIFICATION_SHADER_ROOT_ACCESS + | D3D12_ROOT_SIGNATURE_FLAG_DENY_MESH_SHADER_ROOT_ACCESS +#endif + ; + + // Create root parameters and initialize first (constants) + CD3DX12_ROOT_PARAMETER rootParameters[RootParameterIndex::RootParameterCount] = {}; + rootParameters[RootParameterIndex::ConstantBuffer].InitAsConstantBufferView(0, 0, D3D12_SHADER_VISIBILITY_ALL); + + // Root parameter descriptor - conditionally initialized + CD3DX12_ROOT_SIGNATURE_DESC rsigDesc = {}; + + rsigDesc.Init(1, rootParameters, 0, nullptr, rootSignatureFlags); + + mRootSignature = GetRootSignature(0, rsigDesc); + } + + assert(mRootSignature != nullptr); + + // Create pipeline state. + const int sp = GetPipelineStatePermutation(debugMode, effectFlags); + assert(sp >= 0 && sp < DebugEffectTraits::ShaderPermutationCount); + _Analysis_assume_(sp >= 0 && sp < DebugEffectTraits::ShaderPermutationCount); + + const int vi = EffectBase::VertexShaderIndices[sp]; + assert(vi >= 0 && vi < DebugEffectTraits::VertexShaderCount); + _Analysis_assume_(vi >= 0 && vi < DebugEffectTraits::VertexShaderCount); + const int pi = EffectBase::PixelShaderIndices[sp]; + assert(pi >= 0 && pi < DebugEffectTraits::PixelShaderCount); + _Analysis_assume_(pi >= 0 && pi < DebugEffectTraits::PixelShaderCount); + + pipelineDescription.CreatePipelineState( + device, + mRootSignature, + EffectBase::VertexShaderBytecode[vi], + EffectBase::PixelShaderBytecode[pi], + mPipelineState.GetAddressOf()); + + SetDebugObjectName(mPipelineState.Get(), L"DebugEffect"); +} + + +int DebugEffect::Impl::GetPipelineStatePermutation(DebugEffect::Mode debugMode, uint32_t effectFlags) const noexcept +{ + int permutation = static_cast(debugMode); + + // Support vertex coloring? + if (effectFlags & EffectFlags::VertexColor) + { + permutation += 4; + } + + if (effectFlags & EffectFlags::BiasedVertexNormals) + { + // Compressed normals need to be scaled and biased in the vertex shader. + permutation += 8; + } + + if (effectFlags & EffectFlags::Instancing) + { + // Vertex shader needs to use vertex matrix transform. + permutation += 16; + } + + return permutation; +} + + +// Sets our state onto the D3D device. +void DebugEffect::Impl::Apply(_In_ ID3D12GraphicsCommandList* commandList) +{ + // Compute derived parameter values. + matrices.SetConstants(dirtyFlags, constants.worldViewProj); + + // World inverse transpose matrix. + if (dirtyFlags & EffectDirtyFlags::WorldInverseTranspose) + { + constants.world = XMMatrixTranspose(matrices.world); + + const XMMATRIX worldInverse = XMMatrixInverse(nullptr, matrices.world); + + constants.worldInverseTranspose[0] = worldInverse.r[0]; + constants.worldInverseTranspose[1] = worldInverse.r[1]; + constants.worldInverseTranspose[2] = worldInverse.r[2]; + + dirtyFlags &= ~EffectDirtyFlags::WorldInverseTranspose; + dirtyFlags |= EffectDirtyFlags::ConstantBuffer; + } + + UpdateConstants(); + + // Set the root signature + commandList->SetGraphicsRootSignature(mRootSignature); + + // Set constants + commandList->SetGraphicsRootConstantBufferView(RootParameterIndex::ConstantBuffer, GetConstantBufferGpuAddress()); + + // Set the pipeline state + commandList->SetPipelineState(EffectBase::mPipelineState.Get()); +} + + +// Public constructor. +DebugEffect::DebugEffect( + _In_ ID3D12Device* device, + uint32_t effectFlags, + const EffectPipelineStateDescription& pipelineDescription, + Mode debugMode) + : pImpl(std::make_unique(device, effectFlags, pipelineDescription, debugMode)) +{ +} + + +DebugEffect::DebugEffect(DebugEffect&&) noexcept = default; +DebugEffect& DebugEffect::operator= (DebugEffect&&) noexcept = default; +DebugEffect::~DebugEffect() = default; + + +// IEffect methods. +void DebugEffect::Apply(_In_ ID3D12GraphicsCommandList* commandList) +{ + pImpl->Apply(commandList); +} + + +// Camera settings. +void XM_CALLCONV DebugEffect::SetWorld(FXMMATRIX value) +{ + pImpl->matrices.world = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::WorldViewProj | EffectDirtyFlags::WorldInverseTranspose; +} + + +void XM_CALLCONV DebugEffect::SetView(FXMMATRIX value) +{ + pImpl->matrices.view = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::WorldViewProj; +} + + +void XM_CALLCONV DebugEffect::SetProjection(FXMMATRIX value) +{ + pImpl->matrices.projection = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::WorldViewProj; +} + + +void XM_CALLCONV DebugEffect::SetMatrices(FXMMATRIX world, CXMMATRIX view, CXMMATRIX projection) +{ + pImpl->matrices.world = world; + pImpl->matrices.view = view; + pImpl->matrices.projection = projection; + + pImpl->dirtyFlags |= EffectDirtyFlags::WorldViewProj | EffectDirtyFlags::WorldInverseTranspose; +} + + +// Material settings. +void XM_CALLCONV DebugEffect::SetHemisphericalAmbientColor(FXMVECTOR upper, FXMVECTOR lower) +{ + // Set xyz to new value, but preserve existing w (alpha). + pImpl->constants.ambientDownAndAlpha = XMVectorSelect(pImpl->constants.ambientDownAndAlpha, lower, g_XMSelect1110); + + pImpl->constants.ambientRange = XMVectorSubtract(upper, lower); + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; +} + +void DebugEffect::SetAlpha(float value) +{ + // Set w to new value, but preserve existing xyz (ambient down). + pImpl->constants.ambientDownAndAlpha = XMVectorSetW(pImpl->constants.ambientDownAndAlpha, value); + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; +} diff --git a/Common/DirectXTK12/Src/DemandCreate.h b/Common/DirectXTK12/Src/DemandCreate.h new file mode 100644 index 0000000..7d9355a --- /dev/null +++ b/Common/DirectXTK12/Src/DemandCreate.h @@ -0,0 +1,58 @@ +//-------------------------------------------------------------------------------------- +// File: DemandCreate.h +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248929 +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#pragma once + +#include "PlatformHelpers.h" + + +namespace DirectX +{ + // Helper for lazily creating a D3D resource. + template + inline T* DemandCreate(Microsoft::WRL::ComPtr& comPtr, std::mutex& mutex, TCreateFunc createFunc) + { + T* result = comPtr.Get(); + + // Double-checked lock pattern. + #ifdef _MSC_VER + MemoryBarrier(); + #elif defined(__GNUC__) + __sync_synchronize(); + #else + #error Unknown memory barrier syntax + #endif + + if (!result) + { + std::lock_guard lock(mutex); + + result = comPtr.Get(); + + if (!result) + { + // Create the new object. + ThrowIfFailed( + createFunc(&result) + ); + + #ifdef _MSC_VER + MemoryBarrier(); + #elif defined(__GNUC__) + __sync_synchronize(); + #endif + + comPtr.Attach(result); + } + } + + return result; + } +} diff --git a/Common/DirectXTK12/Src/DescriptorHeap.cpp b/Common/DirectXTK12/Src/DescriptorHeap.cpp new file mode 100644 index 0000000..3c912ee --- /dev/null +++ b/Common/DirectXTK12/Src/DescriptorHeap.cpp @@ -0,0 +1,230 @@ +//-------------------------------------------------------------------------------------- +// File: DescriptorHeap.cpp +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "pch.h" +#include "PlatformHelpers.h" +#include "DirectXHelpers.h" +#include "DescriptorHeap.h" + +using namespace DirectX; +using Microsoft::WRL::ComPtr; + +namespace +{ + struct DescriptorHeapDesc + { + D3D12_DESCRIPTOR_HEAP_TYPE Type; + D3D12_DESCRIPTOR_HEAP_FLAGS Flags; + }; + + static const DescriptorHeapDesc c_DescriptorHeapDescs[D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES] = + { + { D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE }, + { D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE }, + { D3D12_DESCRIPTOR_HEAP_TYPE_RTV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE }, + { D3D12_DESCRIPTOR_HEAP_TYPE_DSV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE } + }; +} + +_Use_decl_annotations_ +DescriptorHeap::DescriptorHeap( + ID3D12DescriptorHeap* pExistingHeap) noexcept + : m_pHeap(pExistingHeap) +{ +#if defined(_MSC_VER) || !defined(_WIN32) + m_hCPU = pExistingHeap->GetCPUDescriptorHandleForHeapStart(); + m_hGPU = pExistingHeap->GetGPUDescriptorHandleForHeapStart(); + m_desc = pExistingHeap->GetDesc(); +#else + std::ignore = pExistingHeap->GetCPUDescriptorHandleForHeapStart(&m_hCPU); + std::ignore = pExistingHeap->GetGPUDescriptorHandleForHeapStart(&m_hGPU); + std::ignore = pExistingHeap->GetDesc(&m_desc); +#endif + + ComPtr device; + pExistingHeap->GetDevice(IID_GRAPHICS_PPV_ARGS(device.GetAddressOf())); + + m_increment = device->GetDescriptorHandleIncrementSize(m_desc.Type); +} + +_Use_decl_annotations_ +DescriptorHeap::DescriptorHeap( + ID3D12Device* device, + const D3D12_DESCRIPTOR_HEAP_DESC* pDesc) noexcept(false) : + m_desc{}, + m_hCPU{}, + m_hGPU{}, + m_increment(0) +{ + Create(device, pDesc); +} + +_Use_decl_annotations_ +DescriptorHeap::DescriptorHeap( + ID3D12Device* device, + D3D12_DESCRIPTOR_HEAP_TYPE type, + D3D12_DESCRIPTOR_HEAP_FLAGS flags, + size_t count) noexcept(false) : + m_desc{}, + m_hCPU{}, + m_hGPU{}, + m_increment(0) +{ + if (count > UINT32_MAX) + throw std::invalid_argument("Too many descriptors"); + + D3D12_DESCRIPTOR_HEAP_DESC desc = {}; + desc.Flags = flags; + desc.NumDescriptors = static_cast(count); + desc.Type = type; + Create(device, &desc); +} + +_Use_decl_annotations_ +D3D12_GPU_DESCRIPTOR_HANDLE DescriptorHeap::WriteDescriptors( + ID3D12Device* device, + uint32_t offsetIntoHeap, + uint32_t totalDescriptorCount, + const D3D12_CPU_DESCRIPTOR_HANDLE* pDescriptorRangeStarts, + const uint32_t* pDescriptorRangeSizes, + uint32_t descriptorRangeCount) +{ + assert((size_t(offsetIntoHeap) + size_t(totalDescriptorCount)) <= size_t(m_desc.NumDescriptors)); + + const D3D12_CPU_DESCRIPTOR_HANDLE cpuHandle = GetCpuHandle(offsetIntoHeap); + + device->CopyDescriptors( + 1, + &cpuHandle, + &totalDescriptorCount, + descriptorRangeCount, + pDescriptorRangeStarts, + pDescriptorRangeSizes, + m_desc.Type); + + auto gpuHandle = GetGpuHandle(offsetIntoHeap); + + return gpuHandle; +} + +_Use_decl_annotations_ +D3D12_GPU_DESCRIPTOR_HANDLE DescriptorHeap::WriteDescriptors( + ID3D12Device* device, + uint32_t offsetIntoHeap, + const D3D12_CPU_DESCRIPTOR_HANDLE* pDescriptorRangeStarts, + const uint32_t* pDescriptorRangeSizes, + uint32_t descriptorRangeCount) +{ + uint32_t totalDescriptorCount = 0; + for (uint32_t i = 0; i < descriptorRangeCount; ++i) + totalDescriptorCount += pDescriptorRangeSizes[i]; + + return WriteDescriptors( + device, + offsetIntoHeap, + totalDescriptorCount, + pDescriptorRangeStarts, + pDescriptorRangeSizes, + descriptorRangeCount); +} + +_Use_decl_annotations_ +D3D12_GPU_DESCRIPTOR_HANDLE DescriptorHeap::WriteDescriptors( + ID3D12Device* device, + uint32_t offsetIntoHeap, + const D3D12_CPU_DESCRIPTOR_HANDLE* pDescriptors, + uint32_t descriptorCount) +{ + return WriteDescriptors( + device, + offsetIntoHeap, + descriptorCount, + pDescriptors, + &descriptorCount, + 1); +} + +_Use_decl_annotations_ +void DescriptorHeap::Create( + ID3D12Device* pDevice, + const D3D12_DESCRIPTOR_HEAP_DESC* pDesc) +{ + assert(pDesc != nullptr); + + m_desc = *pDesc; + m_increment = pDevice->GetDescriptorHandleIncrementSize(pDesc->Type); + + if (pDesc->NumDescriptors == 0) + { + m_pHeap.Reset(); + m_hCPU.ptr = 0; + m_hGPU.ptr = 0; + } + else + { + ThrowIfFailed(pDevice->CreateDescriptorHeap( + pDesc, + IID_GRAPHICS_PPV_ARGS(m_pHeap.ReleaseAndGetAddressOf()))); + + SetDebugObjectName(m_pHeap.Get(), L"DescriptorHeap"); + + #if defined(_MSC_VER) || !defined(_WIN32) + m_hCPU = m_pHeap->GetCPUDescriptorHandleForHeapStart(); + if (pDesc->Flags & D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE) + { + m_hGPU = m_pHeap->GetGPUDescriptorHandleForHeapStart(); + } + #else + std::ignore = m_pHeap->GetCPUDescriptorHandleForHeapStart(&m_hCPU); + if (pDesc->Flags & D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE) + { + std::ignore = m_pHeap->GetGPUDescriptorHandleForHeapStart(&m_hGPU); + } + #endif + } +} + +_Use_decl_annotations_ +void DescriptorHeap::DefaultDesc( + D3D12_DESCRIPTOR_HEAP_TYPE type, + D3D12_DESCRIPTOR_HEAP_DESC* pDesc) noexcept +{ + assert(c_DescriptorHeapDescs[type].Type == type); + pDesc->Flags = c_DescriptorHeapDescs[type].Flags; + pDesc->NumDescriptors = 0; + pDesc->Type = type; +} + + +//====================================================================================== +// DescriptorPile +//====================================================================================== + +void DescriptorPile::AllocateRange(size_t numDescriptors, _Out_ IndexType& start, _Out_ IndexType& end) +{ + // make sure we didn't allocate zero + if (numDescriptors == 0) + { + throw std::invalid_argument("Can't allocate zero descriptors"); + } + + // get the current top + start = m_top; + + // increment top with new request + m_top += numDescriptors; + end = m_top; + + // make sure we have enough room + if (m_top > Count()) + { + DebugTrace("DescriptorPile has %zu of %zu descriptors; failed request for %zu more\n", start, Count(), numDescriptors); + throw std::runtime_error("Can't allocate more descriptors"); + } +} diff --git a/Common/DirectXTK12/Src/DirectXHelpers.cpp b/Common/DirectXTK12/Src/DirectXHelpers.cpp new file mode 100644 index 0000000..b958d9e --- /dev/null +++ b/Common/DirectXTK12/Src/DirectXHelpers.cpp @@ -0,0 +1,333 @@ +//-------------------------------------------------------------------------------------- +// File: DirectXHelpers.cpp +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "pch.h" +#include "DirectXHelpers.h" +#include "GraphicsMemory.h" +#include "PlatformHelpers.h" + +using namespace DirectX; + +_Use_decl_annotations_ +void DirectX::CreateShaderResourceView( + ID3D12Device* device, + ID3D12Resource* tex, + D3D12_CPU_DESCRIPTOR_HANDLE srvDescriptor, + bool isCubeMap) +{ +#if defined(_MSC_VER) || !defined(_WIN32) + const auto desc = tex->GetDesc(); +#else + D3D12_RESOURCE_DESC tmpDesc; + const auto& desc = *tex->GetDesc(&tmpDesc); +#endif + + if ((desc.Flags & D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE) != 0) + { + DebugTrace("ERROR: CreateShaderResourceView called on a resource created without support for SRV.\n"); + throw std::runtime_error("Can't have D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE"); + } + + D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {}; + srvDesc.Format = desc.Format; + srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + + const UINT mipLevels = (desc.MipLevels) ? static_cast(desc.MipLevels) : static_cast(-1); + + switch (desc.Dimension) + { + case D3D12_RESOURCE_DIMENSION_TEXTURE1D: + if (desc.DepthOrArraySize > 1) + { + srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE1DARRAY; + srvDesc.Texture1DArray.MipLevels = mipLevels; + srvDesc.Texture1DArray.ArraySize = static_cast(desc.DepthOrArraySize); + } + else + { + srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE1D; + srvDesc.Texture1D.MipLevels = mipLevels; + } + break; + + case D3D12_RESOURCE_DIMENSION_TEXTURE2D: + if (isCubeMap) + { + if (desc.DepthOrArraySize > 6) + { + srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBEARRAY; + srvDesc.TextureCubeArray.MipLevels = mipLevels; + srvDesc.TextureCubeArray.NumCubes = static_cast(desc.DepthOrArraySize / 6); + } + else + { + srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE; + srvDesc.TextureCube.MipLevels = mipLevels; + } + } + else if (desc.DepthOrArraySize > 1) + { + srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY; + srvDesc.Texture2DArray.MipLevels = mipLevels; + srvDesc.Texture2DArray.ArraySize = static_cast(desc.DepthOrArraySize); + } + else + { + srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; + srvDesc.Texture2D.MipLevels = mipLevels; + } + break; + + case D3D12_RESOURCE_DIMENSION_TEXTURE3D: + srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE3D; + srvDesc.Texture3D.MipLevels = mipLevels; + break; + + case D3D12_RESOURCE_DIMENSION_BUFFER: + DebugTrace("ERROR: CreateShaderResourceView cannot be used with DIMENSION_BUFFER.\n\tUse CreateBufferShaderResourceView.\n"); + throw std::invalid_argument("buffer resources not supported"); + + case D3D12_RESOURCE_DIMENSION_UNKNOWN: + default: + DebugTrace("ERROR: CreateShaderResourceView cannot be used with DIMENSION_UNKNOWN (%d).\n", desc.Dimension); + throw std::invalid_argument("unknown resource dimension"); + } + + device->CreateShaderResourceView(tex, &srvDesc, srvDescriptor); +} + +_Use_decl_annotations_ +void DirectX::CreateUnorderedAccessView( + ID3D12Device* device, + ID3D12Resource* tex, + D3D12_CPU_DESCRIPTOR_HANDLE uavDescriptor, + uint32_t mipLevel) +{ +#if defined(_MSC_VER) || !defined(_WIN32) + const auto desc = tex->GetDesc(); +#else + D3D12_RESOURCE_DESC tmpDesc; + const auto& desc = *tex->GetDesc(&tmpDesc); +#endif + + if ((desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS) == 0) + { + DebugTrace("ERROR: CreateUnorderedResourceView called on a resource created without support for UAV.\n"); + throw std::runtime_error("Requires D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS"); + } + + D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc = {}; + uavDesc.Format = desc.Format; + + switch (desc.Dimension) + { + case D3D12_RESOURCE_DIMENSION_TEXTURE1D: + if (desc.DepthOrArraySize > 1) + { + uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE1DARRAY; + uavDesc.Texture1DArray.MipSlice = mipLevel; + uavDesc.Texture1DArray.FirstArraySlice = 0; + uavDesc.Texture1DArray.ArraySize = desc.DepthOrArraySize; + } + else + { + uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE1D; + uavDesc.Texture1D.MipSlice = mipLevel; + } + break; + + case D3D12_RESOURCE_DIMENSION_TEXTURE2D: + if (desc.DepthOrArraySize > 1) + { + uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2DARRAY; + uavDesc.Texture2DArray.MipSlice = mipLevel; + uavDesc.Texture2DArray.ArraySize = desc.DepthOrArraySize; + } + else + { + uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D; + uavDesc.Texture2D.MipSlice = mipLevel; + } + break; + + case D3D12_RESOURCE_DIMENSION_TEXTURE3D: + uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE3D; + uavDesc.Texture3D.MipSlice = mipLevel; + uavDesc.Texture3D.WSize = desc.DepthOrArraySize; + break; + + case D3D12_RESOURCE_DIMENSION_BUFFER: + DebugTrace("ERROR: CreateUnorderedResourceView cannot be used with DIMENSION_BUFFER.\n\tUse CreateBufferUnorderedAccessView.\n"); + throw std::invalid_argument("buffer resources not supported"); + + case D3D12_RESOURCE_DIMENSION_UNKNOWN: + default: + DebugTrace("ERROR: CreateUnorderedResourceView cannot be used with DIMENSION_UNKNOWN (%d).\n", desc.Dimension); + throw std::invalid_argument("unknown resource dimension"); + + } + device->CreateUnorderedAccessView(tex, nullptr, &uavDesc, uavDescriptor); +} + +_Use_decl_annotations_ +void DirectX::CreateRenderTargetView( + ID3D12Device* device, + ID3D12Resource* tex, + D3D12_CPU_DESCRIPTOR_HANDLE rtvDescriptor, + uint32_t mipLevel) +{ +#if defined(_MSC_VER) || !defined(_WIN32) + const auto desc = tex->GetDesc(); +#else + D3D12_RESOURCE_DESC tmpDesc; + const auto& desc = *tex->GetDesc(&tmpDesc); +#endif + + if ((desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET) == 0) + { + DebugTrace("ERROR: CreateRenderTargetView called on a resource created without support for RTV.\n"); + throw std::runtime_error("Requires D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET"); + } + + D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {}; + rtvDesc.Format = desc.Format; + + switch (desc.Dimension) + { + case D3D12_RESOURCE_DIMENSION_TEXTURE1D: + if (desc.DepthOrArraySize > 1) + { + rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE1DARRAY; + rtvDesc.Texture1DArray.MipSlice = mipLevel; + rtvDesc.Texture1DArray.FirstArraySlice = 0; + rtvDesc.Texture1DArray.ArraySize = desc.DepthOrArraySize; + } + else + { + rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE1D; + rtvDesc.Texture1D.MipSlice = mipLevel; + } + break; + + case D3D12_RESOURCE_DIMENSION_TEXTURE2D: + if (desc.SampleDesc.Count > 1) + { + if (desc.DepthOrArraySize > 1) + { + rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DMSARRAY; + rtvDesc.Texture2DMSArray.ArraySize = desc.DepthOrArraySize; + } + else + { + rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DMS; + } + } + else if (desc.DepthOrArraySize > 1) + { + rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DARRAY; + rtvDesc.Texture2DArray.MipSlice = mipLevel; + rtvDesc.Texture2DArray.ArraySize = desc.DepthOrArraySize; + } + else + { + rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; + rtvDesc.Texture2D.MipSlice = mipLevel; + } + break; + + case D3D12_RESOURCE_DIMENSION_TEXTURE3D: + rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE3D; + rtvDesc.Texture3D.MipSlice = mipLevel; + rtvDesc.Texture3D.WSize = desc.DepthOrArraySize; + break; + + case D3D12_RESOURCE_DIMENSION_BUFFER: + DebugTrace("ERROR: CreateRenderTargetView cannot be used with DIMENSION_BUFFER.\n"); + throw std::invalid_argument("buffer resources not supported"); + + case D3D12_RESOURCE_DIMENSION_UNKNOWN: + default: + DebugTrace("ERROR: CreateRenderTargetView cannot be used with DIMENSION_UNKNOWN (%d).\n", desc.Dimension); + throw std::invalid_argument("unknown resource dimension"); + + } + device->CreateRenderTargetView(tex, &rtvDesc, rtvDescriptor); +} + +_Use_decl_annotations_ +void DirectX::CreateBufferShaderResourceView( + ID3D12Device* device, + ID3D12Resource* buffer, + D3D12_CPU_DESCRIPTOR_HANDLE srvDescriptor, + uint32_t stride) +{ +#if defined(_MSC_VER) || !defined(_WIN32) + const auto desc = buffer->GetDesc(); +#else + D3D12_RESOURCE_DESC tmpDesc; + const auto& desc = *buffer->GetDesc(&tmpDesc); +#endif + + if (desc.Dimension != D3D12_RESOURCE_DIMENSION_BUFFER + || (desc.Flags & D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE) != 0) + { + DebugTrace("ERROR: CreateBufferShaderResourceView called on an unsupported resource.\n"); + throw std::runtime_error("invalid buffer resource"); + } + + D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {}; + srvDesc.Format = DXGI_FORMAT_UNKNOWN; + srvDesc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER; + srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + srvDesc.Buffer.FirstElement = 0; + srvDesc.Buffer.NumElements = (stride > 0) + ? static_cast(desc.Width / stride) + : static_cast(desc.Width); + srvDesc.Buffer.StructureByteStride = stride; + + device->CreateShaderResourceView(buffer, &srvDesc, srvDescriptor); +} + +_Use_decl_annotations_ +void DirectX::CreateBufferUnorderedAccessView( + ID3D12Device* device, + ID3D12Resource* buffer, + D3D12_CPU_DESCRIPTOR_HANDLE uavDescriptor, + uint32_t stride, + D3D12_BUFFER_UAV_FLAGS flag, + uint32_t counterOffset, + ID3D12Resource* counterResource) +{ +#if defined(_MSC_VER) || !defined(_WIN32) + const auto desc = buffer->GetDesc(); +#else + D3D12_RESOURCE_DESC tmpDesc; + const auto& desc = *buffer->GetDesc(&tmpDesc); +#endif + + if (desc.Dimension != D3D12_RESOURCE_DIMENSION_BUFFER + || (desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS) == 0) + { + DebugTrace("ERROR: CreateBufferUnorderedAccessView called on an unsupported resource.\n"); + throw std::runtime_error("invalid buffer resource"); + } + + D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc = {}; + uavDesc.Format = DXGI_FORMAT_UNKNOWN; + uavDesc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER; + uavDesc.Buffer.FirstElement = 0; + uavDesc.Buffer.NumElements = (stride > 0) + ? static_cast(desc.Width / stride) + : static_cast(desc.Width); + uavDesc.Buffer.StructureByteStride = stride; + uavDesc.Buffer.CounterOffsetInBytes = counterOffset; + uavDesc.Buffer.Flags = flag; + + device->CreateUnorderedAccessView(buffer, counterResource, &uavDesc, uavDescriptor); +} diff --git a/Common/DirectXTK12/Src/DualPostProcess.cpp b/Common/DirectXTK12/Src/DualPostProcess.cpp new file mode 100644 index 0000000..617b6e7 --- /dev/null +++ b/Common/DirectXTK12/Src/DualPostProcess.cpp @@ -0,0 +1,356 @@ +//-------------------------------------------------------------------------------------- +// File: DualPostProcess.cpp +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "pch.h" +#include "PostProcess.h" + +#include "AlignedNew.h" +#include "CommonStates.h" +#include "DemandCreate.h" +#include "DirectXHelpers.h" +#include "EffectPipelineStateDescription.h" +#include "GraphicsMemory.h" +#include "PlatformHelpers.h" +#include "SharedResourcePool.h" + +using namespace DirectX; + +using Microsoft::WRL::ComPtr; + +namespace +{ + constexpr int c_MaxSamples = 16; + + constexpr int Dirty_ConstantBuffer = 0x01; + constexpr int Dirty_Parameters = 0x02; + + // Constant buffer layout. Must match the shader! + XM_ALIGNED_STRUCT(16) PostProcessConstants + { + XMVECTOR sampleOffsets[c_MaxSamples]; + XMVECTOR sampleWeights[c_MaxSamples]; + }; + + static_assert((sizeof(PostProcessConstants) % 16) == 0, "CB size not padded correctly"); +} + +#pragma region Shaders +// Include the precompiled shader code. +namespace +{ +#ifdef _GAMING_XBOX_SCARLETT +#include "XboxGamingScarlettPostProcess_VSQuadDual.inc" + +#include "XboxGamingScarlettPostProcess_PSMerge.inc" +#include "XboxGamingScarlettPostProcess_PSBloomCombine.inc" +#elif defined(_GAMING_XBOX) +#include "XboxGamingXboxOnePostProcess_VSQuadDual.inc" + +#include "XboxGamingXboxOnePostProcess_PSMerge.inc" +#include "XboxGamingXboxOnePostProcess_PSBloomCombine.inc" +#elif defined(_XBOX_ONE) && defined(_TITLE) +#include "XboxOnePostProcess_VSQuadDual.inc" + +#include "XboxOnePostProcess_PSMerge.inc" +#include "XboxOnePostProcess_PSBloomCombine.inc" +#else +#include "PostProcess_VSQuadDual.inc" + +#include "PostProcess_PSMerge.inc" +#include "PostProcess_PSBloomCombine.inc" +#endif +} + +namespace +{ + const D3D12_SHADER_BYTECODE vertexShader = + { PostProcess_VSQuadDual, sizeof(PostProcess_VSQuadDual) }; + + const D3D12_SHADER_BYTECODE pixelShaders[] = + { + { PostProcess_PSMerge, sizeof(PostProcess_PSMerge) }, + { PostProcess_PSBloomCombine, sizeof(PostProcess_PSBloomCombine) }, + }; + + static_assert(std::size(pixelShaders) == DualPostProcess::Effect_Max, "array/max mismatch"); + + // Factory for lazily instantiating shared root signatures. + class DeviceResources + { + public: + DeviceResources(_In_ ID3D12Device* device) noexcept + : mDevice(device) + { + } + + ID3D12RootSignature* GetRootSignature(const D3D12_ROOT_SIGNATURE_DESC& desc) + { + return DemandCreate(mRootSignature, mMutex, [&](ID3D12RootSignature** pResult) noexcept -> HRESULT + { + HRESULT hr = CreateRootSignature(mDevice.Get(), &desc, pResult); + + if (SUCCEEDED(hr)) + SetDebugObjectName(*pResult, L"DualPostProcess"); + + return hr; + }); + } + + ID3D12Device* GetDevice() const noexcept { return mDevice.Get(); } + + protected: + ComPtr mDevice; + ComPtr mRootSignature; + std::mutex mMutex; + }; +} +#pragma endregion + +class DualPostProcess::Impl : public AlignedNew +{ +public: + Impl(_In_ ID3D12Device* device, const RenderTargetState& rtState, Effect ifx); + + void Process(_In_ ID3D12GraphicsCommandList* commandList); + + void SetDirtyFlag() noexcept { mDirtyFlags = INT_MAX; } + + enum RootParameterIndex + { + TextureSRV, + TextureSRV2, + ConstantBuffer, + RootParameterCount + }; + + // Fields. + DualPostProcess::Effect fx; + PostProcessConstants constants; + D3D12_GPU_DESCRIPTOR_HANDLE texture; + D3D12_GPU_DESCRIPTOR_HANDLE texture2; + float mergeWeight1; + float mergeWeight2; + float bloomIntensity; + float bloomBaseIntensity; + float bloomSaturation; + float bloomBaseSaturation; + +private: + int mDirtyFlags; + + // D3D constant buffer holds a copy of the same data as the public 'constants' field. + GraphicsResource mConstantBuffer; + + // Per instance cache of PSOs, populated with variants for each shader & layout + Microsoft::WRL::ComPtr mPipelineState; + + // Per instance root signature + ID3D12RootSignature* mRootSignature; + + // Per-device resources. + std::shared_ptr mDeviceResources; + + static SharedResourcePool deviceResourcesPool; +}; + + +// Global pool of per-device DualPostProcess resources. +SharedResourcePool DualPostProcess::Impl::deviceResourcesPool; + + +// Constructor. +DualPostProcess::Impl::Impl(_In_ ID3D12Device* device, const RenderTargetState& rtState, Effect ifx) + : fx(ifx), + constants{}, + texture{}, + texture2{}, + mergeWeight1(0.5f), + mergeWeight2(0.5f), + bloomIntensity(1.25f), + bloomBaseIntensity(1.f), + bloomSaturation(1.f), + bloomBaseSaturation(1.f), + mDirtyFlags(INT_MAX), + mDeviceResources(deviceResourcesPool.DemandCreate(device)) +{ + if (ifx >= Effect_Max) + throw std::invalid_argument("Effect not defined"); + + // Create root signature. + { + ENUM_FLAGS_CONSTEXPR D3D12_ROOT_SIGNATURE_FLAGS rootSignatureFlags = + D3D12_ROOT_SIGNATURE_FLAG_DENY_VERTEX_SHADER_ROOT_ACCESS + | D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS + | D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS + | D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS +#ifdef _GAMING_XBOX_SCARLETT + | D3D12_ROOT_SIGNATURE_FLAG_DENY_AMPLIFICATION_SHADER_ROOT_ACCESS + | D3D12_ROOT_SIGNATURE_FLAG_DENY_MESH_SHADER_ROOT_ACCESS +#endif + ; + + // Same as CommonStates::StaticLinearClamp + const CD3DX12_STATIC_SAMPLER_DESC sampler( + 0, // register + D3D12_FILTER_MIN_MAG_MIP_LINEAR, + D3D12_TEXTURE_ADDRESS_MODE_CLAMP, + D3D12_TEXTURE_ADDRESS_MODE_CLAMP, + D3D12_TEXTURE_ADDRESS_MODE_CLAMP, + 0.f, + 16, + D3D12_COMPARISON_FUNC_LESS_EQUAL, + D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE, + 0.f, + D3D12_FLOAT32_MAX, + D3D12_SHADER_VISIBILITY_PIXEL); + + CD3DX12_ROOT_PARAMETER rootParameters[RootParameterIndex::RootParameterCount] = {}; + + const CD3DX12_DESCRIPTOR_RANGE texture1Range(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0); + rootParameters[RootParameterIndex::TextureSRV].InitAsDescriptorTable(1, &texture1Range, D3D12_SHADER_VISIBILITY_PIXEL); + + const CD3DX12_DESCRIPTOR_RANGE texture2Range(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 1); + rootParameters[RootParameterIndex::TextureSRV2].InitAsDescriptorTable(1, &texture2Range, D3D12_SHADER_VISIBILITY_PIXEL); + + // Root parameter descriptor + CD3DX12_ROOT_SIGNATURE_DESC rsigDesc = {}; + + // Constant buffer + rootParameters[RootParameterIndex::ConstantBuffer].InitAsConstantBufferView(0, 0, D3D12_SHADER_VISIBILITY_PIXEL); + + rsigDesc.Init(static_cast(std::size(rootParameters)), rootParameters, 1, &sampler, rootSignatureFlags); + + mRootSignature = mDeviceResources->GetRootSignature(rsigDesc); + } + + assert(mRootSignature != nullptr); + + // Create pipeline state. + const EffectPipelineStateDescription psd(nullptr, + CommonStates::Opaque, + CommonStates::DepthNone, + CommonStates::CullNone, + rtState, + D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE); + + psd.CreatePipelineState( + device, + mRootSignature, + vertexShader, + pixelShaders[ifx], + mPipelineState.GetAddressOf()); + + SetDebugObjectName(mPipelineState.Get(), L"DualPostProcess"); +} + + +// Sets our state onto the D3D device. +void DualPostProcess::Impl::Process(_In_ ID3D12GraphicsCommandList* commandList) +{ + // Set the root signature. + commandList->SetGraphicsRootSignature(mRootSignature); + + // Set the texture. + if (!texture.ptr || !texture2.ptr) + { + DebugTrace("ERROR: Missing texture(s) for DualPostProcess (%llu, %llu)\n", texture.ptr, texture2.ptr); + throw std::runtime_error("DualPostProcess"); + } + commandList->SetGraphicsRootDescriptorTable(RootParameterIndex::TextureSRV, texture); + commandList->SetGraphicsRootDescriptorTable(RootParameterIndex::TextureSRV2, texture2); + + // Set constants. + if (mDirtyFlags & Dirty_Parameters) + { + mDirtyFlags &= ~Dirty_Parameters; + mDirtyFlags |= Dirty_ConstantBuffer; + + switch (fx) + { + case Merge: + constants.sampleWeights[0] = XMVectorReplicate(mergeWeight1); + constants.sampleWeights[1] = XMVectorReplicate(mergeWeight2); + break; + + case BloomCombine: + constants.sampleWeights[0] = XMVectorSet(bloomBaseSaturation, bloomSaturation, 0.f, 0.f); + constants.sampleWeights[1] = XMVectorReplicate(bloomBaseIntensity); + constants.sampleWeights[2] = XMVectorReplicate(bloomIntensity); + break; + + default: + break; + } + } + + if (mDirtyFlags & Dirty_ConstantBuffer) + { + mDirtyFlags &= ~Dirty_ConstantBuffer; + mConstantBuffer = GraphicsMemory::Get(mDeviceResources->GetDevice()).AllocateConstant(constants); + } + + commandList->SetGraphicsRootConstantBufferView(RootParameterIndex::ConstantBuffer, mConstantBuffer.GpuAddress()); + + // Set the pipeline state. + commandList->SetPipelineState(mPipelineState.Get()); + + // Draw quad. + commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + commandList->DrawInstanced(3, 1, 0, 0); +} + + +// Public constructor. +DualPostProcess::DualPostProcess(_In_ ID3D12Device* device, const RenderTargetState& rtState, Effect fx) + : pImpl(std::make_unique(device, rtState, fx)) +{ +} + + +DualPostProcess::DualPostProcess(DualPostProcess&&) noexcept = default; +DualPostProcess& DualPostProcess::operator= (DualPostProcess&&) noexcept = default; +DualPostProcess::~DualPostProcess() = default; + + +// IPostProcess methods. +void DualPostProcess::Process(_In_ ID3D12GraphicsCommandList* commandList) +{ + pImpl->Process(commandList); +} + + +// Properties +void DualPostProcess::SetSourceTexture(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor) +{ + pImpl->texture = srvDescriptor; +} + + +void DualPostProcess::SetSourceTexture2(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor) +{ + pImpl->texture2 = srvDescriptor; +} + + +void DualPostProcess::SetMergeParameters(float weight1, float weight2) +{ + pImpl->mergeWeight1 = weight1; + pImpl->mergeWeight2 = weight2; + pImpl->SetDirtyFlag(); +} + + +void DualPostProcess::SetBloomCombineParameters(float bloom, float base, float bloomSaturation, float baseSaturation) +{ + pImpl->bloomIntensity = bloom; + pImpl->bloomBaseIntensity = base; + pImpl->bloomSaturation = bloomSaturation; + pImpl->bloomBaseSaturation = baseSaturation; + pImpl->SetDirtyFlag(); +} diff --git a/Common/DirectXTK12/Src/DualTextureEffect.cpp b/Common/DirectXTK12/Src/DualTextureEffect.cpp new file mode 100644 index 0000000..d78917c --- /dev/null +++ b/Common/DirectXTK12/Src/DualTextureEffect.cpp @@ -0,0 +1,432 @@ +//-------------------------------------------------------------------------------------- +// File: DualTextureEffect.cpp +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "pch.h" +#include "EffectCommon.h" + +using namespace DirectX; +using Microsoft::WRL::ComPtr; + +namespace +{ + // Constant buffer layout. Must match the shader! + struct DualTextureEffectConstants + { + XMVECTOR diffuseColor; + XMVECTOR fogColor; + XMVECTOR fogVector; + XMMATRIX worldViewProj; + }; + + static_assert((sizeof(DualTextureEffectConstants) % 16) == 0, "CB size not padded correctly"); + + + // Traits type describes our characteristics to the EffectBase template. + struct DualTextureEffectTraits + { + using ConstantBufferType = DualTextureEffectConstants; + + static constexpr int VertexShaderCount = 4; + static constexpr int PixelShaderCount = 2; + static constexpr int ShaderPermutationCount = 4; + static constexpr int RootSignatureCount = 1; + }; +} + +// Internal DualTextureEffect implementation class. +class DualTextureEffect::Impl : public EffectBase +{ +public: + Impl(_In_ ID3D12Device* device, uint32_t effectFlags, const EffectPipelineStateDescription& pipelineDescription); + + enum RootParameterIndex + { + Texture1SRV, + Texture1Sampler, + Texture2SRV, + Texture2Sampler, + ConstantBuffer, + RootParameterCount + }; + + EffectColor color; + + D3D12_GPU_DESCRIPTOR_HANDLE texture1; + D3D12_GPU_DESCRIPTOR_HANDLE texture1Sampler; + D3D12_GPU_DESCRIPTOR_HANDLE texture2; + D3D12_GPU_DESCRIPTOR_HANDLE texture2Sampler; + + int GetPipelineStatePermutation(uint32_t effectFlags) const noexcept; + + void Apply(_In_ ID3D12GraphicsCommandList* commandList); +}; + + +#pragma region Shaders +// Include the precompiled shader code. +namespace +{ +#ifdef _GAMING_XBOX_SCARLETT +#include "XboxGamingScarlettDualTextureEffect_VSDualTexture.inc" +#include "XboxGamingScarlettDualTextureEffect_VSDualTextureNoFog.inc" +#include "XboxGamingScarlettDualTextureEffect_VSDualTextureVc.inc" +#include "XboxGamingScarlettDualTextureEffect_VSDualTextureVcNoFog.inc" + +#include "XboxGamingScarlettDualTextureEffect_PSDualTexture.inc" +#include "XboxGamingScarlettDualTextureEffect_PSDualTextureNoFog.inc" +#elif defined(_GAMING_XBOX) +#include "XboxGamingXboxOneDualTextureEffect_VSDualTexture.inc" +#include "XboxGamingXboxOneDualTextureEffect_VSDualTextureNoFog.inc" +#include "XboxGamingXboxOneDualTextureEffect_VSDualTextureVc.inc" +#include "XboxGamingXboxOneDualTextureEffect_VSDualTextureVcNoFog.inc" + +#include "XboxGamingXboxOneDualTextureEffect_PSDualTexture.inc" +#include "XboxGamingXboxOneDualTextureEffect_PSDualTextureNoFog.inc" +#elif defined(_XBOX_ONE) && defined(_TITLE) +#include "XboxOneDualTextureEffect_VSDualTexture.inc" +#include "XboxOneDualTextureEffect_VSDualTextureNoFog.inc" +#include "XboxOneDualTextureEffect_VSDualTextureVc.inc" +#include "XboxOneDualTextureEffect_VSDualTextureVcNoFog.inc" + +#include "XboxOneDualTextureEffect_PSDualTexture.inc" +#include "XboxOneDualTextureEffect_PSDualTextureNoFog.inc" +#else +#include "DualTextureEffect_VSDualTexture.inc" +#include "DualTextureEffect_VSDualTextureNoFog.inc" +#include "DualTextureEffect_VSDualTextureVc.inc" +#include "DualTextureEffect_VSDualTextureVcNoFog.inc" + +#include "DualTextureEffect_PSDualTexture.inc" +#include "DualTextureEffect_PSDualTextureNoFog.inc" +#endif +} + + +template<> +const D3D12_SHADER_BYTECODE EffectBase::VertexShaderBytecode[] = +{ + { DualTextureEffect_VSDualTexture, sizeof(DualTextureEffect_VSDualTexture) }, + { DualTextureEffect_VSDualTextureNoFog, sizeof(DualTextureEffect_VSDualTextureNoFog) }, + { DualTextureEffect_VSDualTextureVc, sizeof(DualTextureEffect_VSDualTextureVc) }, + { DualTextureEffect_VSDualTextureVcNoFog, sizeof(DualTextureEffect_VSDualTextureVcNoFog) }, + +}; + + +template<> +const int EffectBase::VertexShaderIndices[] = +{ + 0, // basic + 1, // no fog + 2, // vertex color + 3, // vertex color, no fog +}; + + +template<> +const D3D12_SHADER_BYTECODE EffectBase::PixelShaderBytecode[] = +{ + { DualTextureEffect_PSDualTexture, sizeof(DualTextureEffect_PSDualTexture) }, + { DualTextureEffect_PSDualTextureNoFog, sizeof(DualTextureEffect_PSDualTextureNoFog) }, + +}; + + +template<> +const int EffectBase::PixelShaderIndices[] = +{ + 0, // basic + 1, // no fog + 0, // vertex color + 1, // vertex color, no fog +}; +#pragma endregion + +// Global pool of per-device DualTextureEffect resources. +template<> +SharedResourcePool::DeviceResources> EffectBase::deviceResourcesPool = {}; + + +// Constructor. +DualTextureEffect::Impl::Impl( + _In_ ID3D12Device* device, + uint32_t effectFlags, + const EffectPipelineStateDescription& pipelineDescription) + : EffectBase(device), + texture1{}, + texture1Sampler{}, + texture2{}, + texture2Sampler{} +{ + static_assert(static_cast(std::size(EffectBase::VertexShaderIndices)) == DualTextureEffectTraits::ShaderPermutationCount, "array/max mismatch"); + static_assert(static_cast(std::size(EffectBase::VertexShaderBytecode)) == DualTextureEffectTraits::VertexShaderCount, "array/max mismatch"); + static_assert(static_cast(std::size(EffectBase::PixelShaderBytecode)) == DualTextureEffectTraits::PixelShaderCount, "array/max mismatch"); + static_assert(static_cast(std::size(EffectBase::PixelShaderIndices)) == DualTextureEffectTraits::ShaderPermutationCount, "array/max mismatch"); + + // Create root signature. + { + ENUM_FLAGS_CONSTEXPR D3D12_ROOT_SIGNATURE_FLAGS rootSignatureFlags = + D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT + | D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS + | D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS + | D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS +#ifdef _GAMING_XBOX_SCARLETT + | D3D12_ROOT_SIGNATURE_FLAG_DENY_AMPLIFICATION_SHADER_ROOT_ACCESS + | D3D12_ROOT_SIGNATURE_FLAG_DENY_MESH_SHADER_ROOT_ACCESS +#endif + ; + + CD3DX12_ROOT_PARAMETER rootParameters[RootParameterIndex::RootParameterCount] = {}; + rootParameters[RootParameterIndex::ConstantBuffer].InitAsConstantBufferView(0, 0, D3D12_SHADER_VISIBILITY_ALL); + + // Texture 1 + const CD3DX12_DESCRIPTOR_RANGE texture1Range(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0); + const CD3DX12_DESCRIPTOR_RANGE texture1SamplerRange(D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, 1, 0); + rootParameters[RootParameterIndex::Texture1SRV].InitAsDescriptorTable(1, &texture1Range, D3D12_SHADER_VISIBILITY_PIXEL); + rootParameters[RootParameterIndex::Texture1Sampler].InitAsDescriptorTable(1, &texture1SamplerRange, D3D12_SHADER_VISIBILITY_PIXEL); + + // Texture 2 + const CD3DX12_DESCRIPTOR_RANGE texture2Range(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 1); + const CD3DX12_DESCRIPTOR_RANGE texture2SamplerRange(D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, 1, 1); + rootParameters[RootParameterIndex::Texture2SRV].InitAsDescriptorTable(1, &texture2Range, D3D12_SHADER_VISIBILITY_PIXEL); + rootParameters[RootParameterIndex::Texture2Sampler].InitAsDescriptorTable(1, &texture2SamplerRange, D3D12_SHADER_VISIBILITY_PIXEL); + + // Create the root signature + CD3DX12_ROOT_SIGNATURE_DESC rsigDesc = {}; + rsigDesc.Init(static_cast(std::size(rootParameters)), rootParameters, 0, nullptr, rootSignatureFlags); + + mRootSignature = GetRootSignature(0, rsigDesc); + } + + assert(mRootSignature != nullptr); + + // Validate flags & state. + fog.enabled = (effectFlags & EffectFlags::Fog) != 0; + + if (effectFlags & EffectFlags::PerPixelLightingBit) + { + DebugTrace("ERROR: DualTextureEffect does not implement EffectFlags::PerPixelLighting\n"); + throw std::invalid_argument("PerPixelLighting effect flag is invalid"); + } + else if (effectFlags & EffectFlags::Lighting) + { + DebugTrace("ERROR: DualTextureEffect does not implement EffectFlags::Lighting\n"); + throw std::invalid_argument("Lighting effect flag is invalid"); + } + else if (effectFlags & EffectFlags::Instancing) + { + DebugTrace("ERROR: DualTextureEffect does not implement EffectFlags::Instancing\n"); + throw std::invalid_argument("Instancing effect flag is invalid"); + } + + // Create pipeline state. + const int sp = GetPipelineStatePermutation(effectFlags); + assert(sp >= 0 && sp < DualTextureEffectTraits::ShaderPermutationCount); + _Analysis_assume_(sp >= 0 && sp < DualTextureEffectTraits::ShaderPermutationCount); + + const int vi = EffectBase::VertexShaderIndices[sp]; + assert(vi >= 0 && vi < DualTextureEffectTraits::VertexShaderCount); + _Analysis_assume_(vi >= 0 && vi < DualTextureEffectTraits::VertexShaderCount); + const int pi = EffectBase::PixelShaderIndices[sp]; + assert(pi >= 0 && pi < DualTextureEffectTraits::PixelShaderCount); + _Analysis_assume_(pi >= 0 && pi < DualTextureEffectTraits::PixelShaderCount); + + pipelineDescription.CreatePipelineState( + device, + mRootSignature, + EffectBase::VertexShaderBytecode[vi], + EffectBase::PixelShaderBytecode[pi], + mPipelineState.GetAddressOf()); + + SetDebugObjectName(mPipelineState.Get(), L"DualTextureEffect"); +} + + +int DualTextureEffect::Impl::GetPipelineStatePermutation(uint32_t effectFlags) const noexcept +{ + int permutation = 0; + + // Use optimized shaders if fog is disabled. + if (!fog.enabled) + { + permutation += 1; + } + + // Support vertex coloring? + if (effectFlags & EffectFlags::VertexColor) + { + permutation += 2; + } + + return permutation; +} + + +// Sets our state onto the D3D device. +void DualTextureEffect::Impl::Apply(_In_ ID3D12GraphicsCommandList* commandList) +{ + // Compute derived parameter values. + matrices.SetConstants(dirtyFlags, constants.worldViewProj); + + fog.SetConstants(dirtyFlags, matrices.worldView, constants.fogVector); + + color.SetConstants(dirtyFlags, constants.diffuseColor); + + UpdateConstants(); + + // Set the root signature + commandList->SetGraphicsRootSignature(mRootSignature); + + // Set the textures + if (!texture1.ptr || !texture2.ptr) + { + DebugTrace("ERROR: Missing texture(s) for DualTextureEffect (texture1 %llu, texture2 %llu)\n", texture1.ptr, texture2.ptr); + throw std::runtime_error("DualTextureEffect"); + } + if (!texture1Sampler.ptr || !texture2Sampler.ptr) + { + DebugTrace("ERROR: Missing sampler(s) for DualTextureEffect (samplers1 %llu, samplers2 %llu)\n", texture2Sampler.ptr, texture2Sampler.ptr); + throw std::runtime_error("DualTextureEffect"); + } + + // **NOTE** If D3D asserts or crashes here, you probably need to call commandList->SetDescriptorHeaps() with the required descriptor heaps. + commandList->SetGraphicsRootDescriptorTable(RootParameterIndex::Texture1SRV, texture1); + commandList->SetGraphicsRootDescriptorTable(RootParameterIndex::Texture1Sampler, texture1Sampler); + commandList->SetGraphicsRootDescriptorTable(RootParameterIndex::Texture2SRV, texture2); + commandList->SetGraphicsRootDescriptorTable(RootParameterIndex::Texture2Sampler, texture2Sampler); + + // Set constants + commandList->SetGraphicsRootConstantBufferView(RootParameterIndex::ConstantBuffer, GetConstantBufferGpuAddress()); + + // Set the pipeline state + commandList->SetPipelineState(EffectBase::mPipelineState.Get()); +} + + +// Public constructor. +DualTextureEffect::DualTextureEffect( + _In_ ID3D12Device* device, + uint32_t effectFlags, + const EffectPipelineStateDescription& pipelineDescription) + : pImpl(std::make_unique(device, effectFlags, pipelineDescription)) +{ +} + + +DualTextureEffect::DualTextureEffect(DualTextureEffect&&) noexcept = default; +DualTextureEffect& DualTextureEffect::operator= (DualTextureEffect&&) noexcept = default; +DualTextureEffect::~DualTextureEffect() = default; + + +// IEffect methods +void DualTextureEffect::Apply(_In_ ID3D12GraphicsCommandList* commandList) +{ + pImpl->Apply(commandList); +} + + +// Camera settings +void XM_CALLCONV DualTextureEffect::SetWorld(FXMMATRIX value) +{ + pImpl->matrices.world = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::WorldViewProj | EffectDirtyFlags::WorldInverseTranspose | EffectDirtyFlags::FogVector; +} + + +void XM_CALLCONV DualTextureEffect::SetView(FXMMATRIX value) +{ + pImpl->matrices.view = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::WorldViewProj | EffectDirtyFlags::EyePosition | EffectDirtyFlags::FogVector; +} + + +void XM_CALLCONV DualTextureEffect::SetProjection(FXMMATRIX value) +{ + pImpl->matrices.projection = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::WorldViewProj; +} + + +void XM_CALLCONV DualTextureEffect::SetMatrices(FXMMATRIX world, CXMMATRIX view, CXMMATRIX projection) +{ + pImpl->matrices.world = world; + pImpl->matrices.view = view; + pImpl->matrices.projection = projection; + + pImpl->dirtyFlags |= EffectDirtyFlags::WorldViewProj | EffectDirtyFlags::WorldInverseTranspose | EffectDirtyFlags::EyePosition | EffectDirtyFlags::FogVector; +} + + +// Material settings +void XM_CALLCONV DualTextureEffect::SetDiffuseColor(FXMVECTOR value) +{ + pImpl->color.diffuseColor = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::MaterialColor; +} + + +void DualTextureEffect::SetAlpha(float value) +{ + pImpl->color.alpha = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::MaterialColor; +} + + +void XM_CALLCONV DualTextureEffect::SetColorAndAlpha(FXMVECTOR value) +{ + pImpl->color.diffuseColor = value; + pImpl->color.alpha = XMVectorGetW(value); + + pImpl->dirtyFlags |= EffectDirtyFlags::MaterialColor; +} + + +// Fog settings. +void DualTextureEffect::SetFogStart(float value) +{ + pImpl->fog.start = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::FogVector; +} + + +void DualTextureEffect::SetFogEnd(float value) +{ + pImpl->fog.end = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::FogVector; +} + + +void XM_CALLCONV DualTextureEffect::SetFogColor(FXMVECTOR value) +{ + pImpl->constants.fogColor = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; +} + + +// Texture settings. +void DualTextureEffect::SetTexture(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor, D3D12_GPU_DESCRIPTOR_HANDLE samplerDescriptor) +{ + pImpl->texture1 = srvDescriptor; + pImpl->texture1Sampler = samplerDescriptor; +} + + +void DualTextureEffect::SetTexture2(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor, D3D12_GPU_DESCRIPTOR_HANDLE samplerDescriptor) +{ + pImpl->texture2 = srvDescriptor; + pImpl->texture2Sampler = samplerDescriptor; +} diff --git a/Common/DirectXTK12/Src/EffectCommon.cpp b/Common/DirectXTK12/Src/EffectCommon.cpp new file mode 100644 index 0000000..51a533d --- /dev/null +++ b/Common/DirectXTK12/Src/EffectCommon.cpp @@ -0,0 +1,402 @@ +//-------------------------------------------------------------------------------------- +// File: EffectCommon.cpp +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "pch.h" +#include "EffectCommon.h" +#include "DemandCreate.h" +#include "ResourceUploadBatch.h" + +using namespace DirectX; +using Microsoft::WRL::ComPtr; + + +// IEffectMatrices default method +void XM_CALLCONV IEffectMatrices::SetMatrices(FXMMATRIX world, CXMMATRIX view, CXMMATRIX projection) +{ + SetWorld(world); + SetView(view); + SetProjection(projection); +} + + +// Constructor initializes default matrix values. +EffectMatrices::EffectMatrices() noexcept +{ + const XMMATRIX id = XMMatrixIdentity(); + world = id; + view = id; + projection = id; + worldView = id; +} + + +// Lazily recomputes the combined world+view+projection matrix. +_Use_decl_annotations_ void EffectMatrices::SetConstants(int& dirtyFlags, XMMATRIX& worldViewProjConstant) +{ + if (dirtyFlags & EffectDirtyFlags::WorldViewProj) + { + worldView = XMMatrixMultiply(world, view); + + worldViewProjConstant = XMMatrixTranspose(XMMatrixMultiply(worldView, projection)); + + dirtyFlags &= ~EffectDirtyFlags::WorldViewProj; + dirtyFlags |= EffectDirtyFlags::ConstantBuffer; + } +} + + +// Constructor initializes default fog settings. +EffectFog::EffectFog() noexcept : + enabled(false), + start(0), + end(1.f) +{ +} + + +// Lazily recomputes the derived vector used by shader fog calculations. +_Use_decl_annotations_ +void XM_CALLCONV EffectFog::SetConstants(int& dirtyFlags, FXMMATRIX worldView, XMVECTOR& fogVectorConstant) +{ + if (enabled) + { + if (dirtyFlags & (EffectDirtyFlags::FogVector | EffectDirtyFlags::FogEnable)) + { + if (start == end) + { + // Degenerate case: force everything to 100% fogged if start and end are the same. + static const XMVECTORF32 fullyFogged = { { { 0, 0, 0, 1 } } }; + + fogVectorConstant = fullyFogged; + } + else + { + // We want to transform vertex positions into view space, take the resulting + // Z value, then scale and offset according to the fog start/end distances. + // Because we only care about the Z component, the shader can do all this + // with a single dot product, using only the Z row of the world+view matrix. + + // _13, _23, _33, _43 + const XMVECTOR worldViewZ = XMVectorMergeXY( + XMVectorMergeZW(worldView.r[0], worldView.r[2]), + XMVectorMergeZW(worldView.r[1], worldView.r[3])); + + // 0, 0, 0, fogStart + const XMVECTOR wOffset = XMVectorSwizzle<1, 2, 3, 0>(XMLoadFloat(&start)); + + // (worldViewZ + wOffset) / (start - end); + fogVectorConstant = XMVectorDivide(XMVectorAdd(worldViewZ, wOffset), XMVectorReplicate(start - end)); + } + + dirtyFlags &= ~(EffectDirtyFlags::FogVector | EffectDirtyFlags::FogEnable); + dirtyFlags |= EffectDirtyFlags::ConstantBuffer; + } + } + else + { + // When fog is disabled, make sure the fog vector is reset to zero. + if (dirtyFlags & EffectDirtyFlags::FogEnable) + { + fogVectorConstant = g_XMZero; + + dirtyFlags &= ~EffectDirtyFlags::FogEnable; + dirtyFlags |= EffectDirtyFlags::ConstantBuffer; + } + } +} + + +// Constructor initializes default material color settings. +EffectColor::EffectColor() noexcept : + diffuseColor(g_XMOne), + alpha(1.f) +{ +} + + +// Lazily recomputes the material color parameter for shaders that do not support realtime lighting. +void EffectColor::SetConstants(_Inout_ int& dirtyFlags, _Inout_ XMVECTOR& diffuseColorConstant) +{ + if (dirtyFlags & EffectDirtyFlags::MaterialColor) + { + const XMVECTOR alphaVector = XMVectorReplicate(alpha); + + // xyz = diffuse * alpha, w = alpha. + diffuseColorConstant = XMVectorSelect(alphaVector, XMVectorMultiply(diffuseColor, alphaVector), g_XMSelect1110); + + dirtyFlags &= ~EffectDirtyFlags::MaterialColor; + dirtyFlags |= EffectDirtyFlags::ConstantBuffer; + } +} + + +// Constructor initializes default light settings. +EffectLights::EffectLights() noexcept : + emissiveColor{}, + ambientLightColor{}, + lightEnabled{}, + lightDiffuseColor{}, + lightSpecularColor{} +{ + for (int i = 0; i < MaxDirectionalLights; i++) + { + lightEnabled[i] = (i == 0); + lightDiffuseColor[i] = g_XMOne; + } +} + + +#ifdef _PREFAST_ +#pragma prefast(push) +#pragma prefast(disable:22103, "PREFAST doesn't understand buffer is bounded by a static const value even with SAL" ) +#endif + +// Initializes constant buffer fields to match the current lighting state. +_Use_decl_annotations_ void EffectLights::InitializeConstants(XMVECTOR& specularColorAndPowerConstant, XMVECTOR* lightDirectionConstant, XMVECTOR* lightDiffuseConstant, XMVECTOR* lightSpecularConstant) const +{ + static const XMVECTORF32 defaultSpecular = { { { 1, 1, 1, 16 } } }; + static const XMVECTORF32 defaultLightDirection = { { { 0, -1, 0, 0 } } }; + + specularColorAndPowerConstant = defaultSpecular; + + for (int i = 0; i < MaxDirectionalLights; i++) + { + lightDirectionConstant[i] = defaultLightDirection; + + lightDiffuseConstant[i] = lightEnabled[i] ? lightDiffuseColor[i] : g_XMZero; + lightSpecularConstant[i] = lightEnabled[i] ? lightSpecularColor[i] : g_XMZero; + } +} + +#ifdef _PREFAST_ +#pragma prefast(pop) +#endif + + +// Lazily recomputes derived parameter values used by shader lighting calculations. +_Use_decl_annotations_ +void EffectLights::SetConstants(int& dirtyFlags, EffectMatrices const& matrices, XMMATRIX& worldConstant, XMVECTOR worldInverseTransposeConstant[3], XMVECTOR& eyePositionConstant, XMVECTOR& diffuseColorConstant, XMVECTOR& emissiveColorConstant, bool lightingEnabled) +{ + if (lightingEnabled) + { + // World inverse transpose matrix. + if (dirtyFlags & EffectDirtyFlags::WorldInverseTranspose) + { + worldConstant = XMMatrixTranspose(matrices.world); + + const XMMATRIX worldInverse = XMMatrixInverse(nullptr, matrices.world); + + worldInverseTransposeConstant[0] = worldInverse.r[0]; + worldInverseTransposeConstant[1] = worldInverse.r[1]; + worldInverseTransposeConstant[2] = worldInverse.r[2]; + + dirtyFlags &= ~EffectDirtyFlags::WorldInverseTranspose; + dirtyFlags |= EffectDirtyFlags::ConstantBuffer; + } + + // Eye position vector. + if (dirtyFlags & EffectDirtyFlags::EyePosition) + { + XMMATRIX viewInverse = XMMatrixInverse(nullptr, matrices.view); + + eyePositionConstant = viewInverse.r[3]; + + dirtyFlags &= ~EffectDirtyFlags::EyePosition; + dirtyFlags |= EffectDirtyFlags::ConstantBuffer; + } + } + + // Material color parameters. The desired lighting model is: + // + // ((ambientLightColor + sum(diffuse directional light)) * diffuseColor) + emissiveColor + // + // When lighting is disabled, ambient and directional lights are ignored, leaving: + // + // diffuseColor + emissiveColor + // + // For the lighting disabled case, we can save one shader instruction by precomputing + // diffuse+emissive on the CPU, after which the shader can use diffuseColor directly, + // ignoring its emissive parameter. + // + // When lighting is enabled, we can merge the ambient and emissive settings. If we + // set our emissive parameter to emissive+(ambient*diffuse), the shader no longer + // needs to bother adding the ambient contribution, simplifying its computation to: + // + // (sum(diffuse directional light) * diffuseColor) + emissiveColor + // + // For futher optimization goodness, we merge material alpha with the diffuse + // color parameter, and premultiply all color values by this alpha. + + if (dirtyFlags & EffectDirtyFlags::MaterialColor) + { + XMVECTOR diffuse = diffuseColor; + const XMVECTOR alphaVector = XMVectorReplicate(alpha); + + if (lightingEnabled) + { + // Merge emissive and ambient light contributions. + // (emissiveColor + ambientLightColor * diffuse) * alphaVector; + emissiveColorConstant = XMVectorMultiply(XMVectorMultiplyAdd(ambientLightColor, diffuse, emissiveColor), alphaVector); + } + else + { + // Merge diffuse and emissive light contributions. + diffuse = XMVectorAdd(diffuse, emissiveColor); + } + + // xyz = diffuse * alpha, w = alpha. + diffuseColorConstant = XMVectorSelect(alphaVector, XMVectorMultiply(diffuse, alphaVector), g_XMSelect1110); + + dirtyFlags &= ~EffectDirtyFlags::MaterialColor; + dirtyFlags |= EffectDirtyFlags::ConstantBuffer; + } +} + + +#ifdef _PREFAST_ +#pragma prefast(push) +#pragma prefast(disable:26015, "PREFAST doesn't understand that ValidateLightIndex bounds whichLight" ) +#endif + +// Helper for turning one of the directional lights on or off. +_Use_decl_annotations_ int EffectLights::SetLightEnabled(int whichLight, bool value, XMVECTOR* lightDiffuseConstant, XMVECTOR* lightSpecularConstant) +{ + ValidateLightIndex(whichLight); + + if (lightEnabled[whichLight] == value) + return 0; + + lightEnabled[whichLight] = value; + + if (value) + { + // If this light is now on, store its color in the constant buffer. + lightDiffuseConstant[whichLight] = lightDiffuseColor[whichLight]; + lightSpecularConstant[whichLight] = lightSpecularColor[whichLight]; + } + else + { + // If the light is off, reset constant buffer colors to zero. + lightDiffuseConstant[whichLight] = g_XMZero; + lightSpecularConstant[whichLight] = g_XMZero; + } + + return EffectDirtyFlags::ConstantBuffer; +} + + +// Helper for setting diffuse color of one of the directional lights. +_Use_decl_annotations_ +int XM_CALLCONV EffectLights::SetLightDiffuseColor(int whichLight, FXMVECTOR value, XMVECTOR* lightDiffuseConstant) +{ + ValidateLightIndex(whichLight); + + // Locally store the new color. + lightDiffuseColor[whichLight] = value; + + // If this light is currently on, also update the constant buffer. + if (lightEnabled[whichLight]) + { + lightDiffuseConstant[whichLight] = value; + + return EffectDirtyFlags::ConstantBuffer; + } + + return 0; +} + + +// Helper for setting specular color of one of the directional lights. +_Use_decl_annotations_ +int XM_CALLCONV EffectLights::SetLightSpecularColor(int whichLight, FXMVECTOR value, XMVECTOR* lightSpecularConstant) +{ + ValidateLightIndex(whichLight); + + // Locally store the new color. + lightSpecularColor[whichLight] = value; + + // If this light is currently on, also update the constant buffer. + if (lightEnabled[whichLight]) + { + lightSpecularConstant[whichLight] = value; + + return EffectDirtyFlags::ConstantBuffer; + } + + return 0; +} + +#ifdef _PREFAST_ +#pragma prefast(pop) +#endif + + +// Parameter validation helper. +void EffectLights::ValidateLightIndex(int whichLight) +{ + if (whichLight < 0 || whichLight >= MaxDirectionalLights) + { + throw std::invalid_argument("whichLight parameter invalid"); + } +} + + +// Activates the default lighting rig (key, fill, and back lights). +void EffectLights::EnableDefaultLighting(_In_ IEffectLights* effect) +{ + static const XMVECTORF32 defaultDirections[MaxDirectionalLights] = + { + { { { -0.5265408f, -0.5735765f, -0.6275069f, 0 } } }, + { { { 0.7198464f, 0.3420201f, 0.6040227f, 0 } } }, + { { { 0.4545195f, -0.7660444f, 0.4545195f, 0 } } }, + }; + + static const XMVECTORF32 defaultDiffuse[MaxDirectionalLights] = + { + { { { 1.0000000f, 0.9607844f, 0.8078432f, 0 } } }, + { { { 0.9647059f, 0.7607844f, 0.4078432f, 0 } } }, + { { { 0.3231373f, 0.3607844f, 0.3937255f, 0 } } }, + }; + + static const XMVECTORF32 defaultSpecular[MaxDirectionalLights] = + { + { { { 1.0000000f, 0.9607844f, 0.8078432f, 0 } } }, + { { { 0.0000000f, 0.0000000f, 0.0000000f, 0 } } }, + { { { 0.3231373f, 0.3607844f, 0.3937255f, 0 } } }, + }; + + static const XMVECTORF32 defaultAmbient = { { { 0.05333332f, 0.09882354f, 0.1819608f, 0 } } }; + + effect->SetAmbientLightColor(defaultAmbient); + + for (int i = 0; i < MaxDirectionalLights; i++) + { + effect->SetLightEnabled(i, true); + effect->SetLightDirection(i, defaultDirections[i]); + effect->SetLightDiffuseColor(i, defaultDiffuse[i]); + effect->SetLightSpecularColor(i, defaultSpecular[i]); + } +} + + +// Gets or lazily creates the specified root signature. +ID3D12RootSignature* EffectDeviceResources::DemandCreateRootSig( + _Inout_ ComPtr& rootSig, + D3D12_ROOT_SIGNATURE_DESC const& desc) +{ + return DemandCreate(rootSig, mMutex, [&](ID3D12RootSignature** pResult) noexcept -> HRESULT + { + HRESULT hr = CreateRootSignature(mDevice.Get(), &desc, pResult); + + if (SUCCEEDED(hr)) + SetDebugObjectName(*pResult, L"DirectXTK:Effect"); + + return hr; + }); +} diff --git a/Common/DirectXTK12/Src/EffectCommon.h b/Common/DirectXTK12/Src/EffectCommon.h new file mode 100644 index 0000000..c30aa67 --- /dev/null +++ b/Common/DirectXTK12/Src/EffectCommon.h @@ -0,0 +1,239 @@ +//-------------------------------------------------------------------------------------- +// File: EffectCommon.h +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#pragma once + +#include + +#include "Effects.h" +#include "PlatformHelpers.h" +#include "SharedResourcePool.h" +#include "AlignedNew.h" +#include "DescriptorHeap.h" +#include "GraphicsMemory.h" +#include "DirectXHelpers.h" +#include "RenderTargetState.h" + +// BasicEffect, SkinnedEffect, et al, have many things in common, but also significant +// differences (for instance, not all the effects support lighting). This header breaks +// out common functionality into a set of helpers which can be assembled in different +// combinations to build up whatever subset is needed by each effect. + + +namespace DirectX +{ + inline namespace DX12 + { + // Internal effect flags + namespace EffectFlags + { + constexpr int PerPixelLightingBit = 0x04; + } + + static_assert(((EffectFlags::PerPixelLighting)& EffectFlags::PerPixelLightingBit) != 0, "PerPixelLighting enum flags mismatch"); + } + + // Bitfield tracks which derived parameter values need to be recomputed. + namespace EffectDirtyFlags + { + constexpr int ConstantBuffer = 0x01; + constexpr int WorldViewProj = 0x02; + constexpr int WorldInverseTranspose = 0x04; + constexpr int EyePosition = 0x08; + constexpr int MaterialColor = 0x10; + constexpr int FogVector = 0x20; + constexpr int FogEnable = 0x40; + constexpr int AlphaTest = 0x80; + } + + // Helper stores matrix parameter values, and computes derived matrices. + struct EffectMatrices + { + EffectMatrices() noexcept; + + XMMATRIX world; + XMMATRIX view; + XMMATRIX projection; + XMMATRIX worldView; + + void SetConstants(_Inout_ int& dirtyFlags, _Inout_ XMMATRIX& worldViewProjConstant); + }; + + + // Helper stores the current fog settings, and computes derived shader parameters. + struct EffectFog + { + EffectFog() noexcept; + + bool enabled; + float start; + float end; + + void XM_CALLCONV SetConstants(_Inout_ int& dirtyFlags, _In_ FXMMATRIX worldView, _Inout_ XMVECTOR& fogVectorConstant); + }; + + + // Helper stores material color settings, and computes derived parameters for shaders that do not support realtime lighting. + struct EffectColor + { + EffectColor() noexcept; + + XMVECTOR diffuseColor; + float alpha; + + void SetConstants(_Inout_ int& dirtyFlags, _Inout_ XMVECTOR& diffuseColorConstant); + }; + + + // Helper stores the current light settings, and computes derived shader parameters. + struct EffectLights : public EffectColor + { + EffectLights() noexcept; + + static constexpr int MaxDirectionalLights = IEffectLights::MaxDirectionalLights; + + + // Fields. + XMVECTOR emissiveColor; + XMVECTOR ambientLightColor; + + bool lightEnabled[MaxDirectionalLights]; + XMVECTOR lightDiffuseColor[MaxDirectionalLights]; + XMVECTOR lightSpecularColor[MaxDirectionalLights]; + + + // Methods. + void InitializeConstants(_Out_ XMVECTOR& specularColorAndPowerConstant, _Out_writes_all_(MaxDirectionalLights) XMVECTOR* lightDirectionConstant, _Out_writes_all_(MaxDirectionalLights) XMVECTOR* lightDiffuseConstant, _Out_writes_all_(MaxDirectionalLights) XMVECTOR* lightSpecularConstant) const; + void SetConstants(_Inout_ int& dirtyFlags, _In_ EffectMatrices const& matrices, _Inout_ XMMATRIX& worldConstant, _Inout_updates_(3) XMVECTOR worldInverseTransposeConstant[3], _Inout_ XMVECTOR& eyePositionConstant, _Inout_ XMVECTOR& diffuseColorConstant, _Inout_ XMVECTOR& emissiveColorConstant, bool lightingEnabled); + + int SetLightEnabled(int whichLight, bool value, _Inout_updates_(MaxDirectionalLights) XMVECTOR* lightDiffuseConstant, _Inout_updates_(MaxDirectionalLights) XMVECTOR* lightSpecularConstant); + int XM_CALLCONV SetLightDiffuseColor(int whichLight, FXMVECTOR value, _Inout_updates_(MaxDirectionalLights) XMVECTOR* lightDiffuseConstant); + int XM_CALLCONV SetLightSpecularColor(int whichLight, FXMVECTOR value, _Inout_updates_(MaxDirectionalLights) XMVECTOR* lightSpecularConstant); + + static void ValidateLightIndex(int whichLight); + static void EnableDefaultLighting(_In_ IEffectLights* effect); + }; + + // Factory for lazily instantiating shared root signatures. + class EffectDeviceResources + { + public: + EffectDeviceResources(_In_ ID3D12Device* device) noexcept + : mDevice(device) + { + } + + ID3D12RootSignature* DemandCreateRootSig(_Inout_ Microsoft::WRL::ComPtr& rootSig, D3D12_ROOT_SIGNATURE_DESC const& desc); + + protected: + Microsoft::WRL::ComPtr mDevice; + + std::mutex mMutex; + }; + + // Templated base class provides functionality common to all the built-in effects. + template + class EffectBase : public AlignedNew + { + public: + typename Traits::ConstantBufferType constants; + + // Constructor. + EffectBase(_In_ ID3D12Device* device) + : constants{}, + dirtyFlags(INT_MAX), + mRootSignature(nullptr), + mDeviceResources(deviceResourcesPool.DemandCreate(device)) + { + // Initialize the constant buffer data + mConstantBuffer = GraphicsMemory::Get(device).AllocateConstant(constants); + } + + // Commits constants to the constant buffer memory + void UpdateConstants() + { + // Make sure the constant buffer is up to date. + if (dirtyFlags & EffectDirtyFlags::ConstantBuffer) + { + mConstantBuffer = GraphicsMemory::Get(mDeviceResources->GetDevice()).AllocateConstant(constants); + + dirtyFlags &= ~EffectDirtyFlags::ConstantBuffer; + } + } + + D3D12_GPU_VIRTUAL_ADDRESS GetConstantBufferGpuAddress() noexcept + { + return mConstantBuffer.GpuAddress(); + } + + ID3D12RootSignature* GetRootSignature(int slot, CD3DX12_ROOT_SIGNATURE_DESC const& rootSig) + { + return mDeviceResources->GetRootSignature(slot, rootSig); + } + + ID3D12Device* GetDevice() const noexcept + { + return mDeviceResources->GetDevice(); + } + + // Fields. + EffectMatrices matrices; + EffectFog fog; + int dirtyFlags; + + protected: + // Static arrays hold all the precompiled shader permutations. + static const D3D12_SHADER_BYTECODE VertexShaderBytecode[Traits::VertexShaderCount]; + static const D3D12_SHADER_BYTECODE PixelShaderBytecode[Traits::PixelShaderCount]; + // .. and shader entry points + static const int VertexShaderIndices[Traits::ShaderPermutationCount]; + static const int PixelShaderIndices[Traits::ShaderPermutationCount]; + // ... and vertex layout tables + static const D3D12_INPUT_LAYOUT_DESC VertexShaderInputLayouts[Traits::ShaderPermutationCount]; + + // Per instance cache of PSOs, populated with variants for each shader & layout + Microsoft::WRL::ComPtr mPipelineState; + + // Per instance root signature + ID3D12RootSignature* mRootSignature; + + private: + // D3D constant buffer holds a copy of the same data as the public 'constants' field. + GraphicsResource mConstantBuffer; + + // Only one of these helpers is allocated per D3D device, even if there are multiple effect instances. + class DeviceResources : public EffectDeviceResources + { + public: + DeviceResources(_In_ ID3D12Device* device) noexcept + : EffectDeviceResources(device), + mRootSignature{} + { } + + // Gets or lazily creates the specified root signature + ID3D12RootSignature* GetRootSignature(int slot, D3D12_ROOT_SIGNATURE_DESC const& desc) + { + assert(slot >= 0 && slot < Traits::RootSignatureCount); + _Analysis_assume_(slot >= 0 && slot < Traits::RootSignatureCount); + + return DemandCreateRootSig(mRootSignature[slot], desc); + } + + ID3D12Device* GetDevice() const noexcept { return mDevice.Get(); } + + private: + Microsoft::WRL::ComPtr mRootSignature[Traits::RootSignatureCount]; + }; + + // Per-device resources. + std::shared_ptr mDeviceResources; + + static SharedResourcePool deviceResourcesPool; + }; +} diff --git a/Common/DirectXTK12/Src/EffectFactory.cpp b/Common/DirectXTK12/Src/EffectFactory.cpp new file mode 100644 index 0000000..9b6989b --- /dev/null +++ b/Common/DirectXTK12/Src/EffectFactory.cpp @@ -0,0 +1,589 @@ +//-------------------------------------------------------------------------------------- +// File: EffectFactory.cpp +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "pch.h" +#include "Effects.h" +#include "CommonStates.h" +#include "DirectXHelpers.h" +#include "PlatformHelpers.h" +#include "DescriptorHeap.h" + +#include + + +using namespace DirectX; +using Microsoft::WRL::ComPtr; + +namespace +{ + template + void SetMaterialProperties(_In_ T* effect, const EffectFactory::EffectInfo& info) + { + effect->EnableDefaultLighting(); + + effect->SetAlpha(info.alphaValue); + + // Most DirectX Tool Kit effects do not have an ambient material color. + + XMVECTOR color = XMLoadFloat3(&info.diffuseColor); + effect->SetDiffuseColor(color); + + if (info.specularColor.x != 0 || info.specularColor.y != 0 || info.specularColor.z != 0) + { + color = XMLoadFloat3(&info.specularColor); + effect->SetSpecularColor(color); + effect->SetSpecularPower(info.specularPower); + } + else + { + effect->DisableSpecular(); + } + + if (info.emissiveColor.x != 0 || info.emissiveColor.y != 0 || info.emissiveColor.z != 0) + { + color = XMLoadFloat3(&info.emissiveColor); + effect->SetEmissiveColor(color); + } + } +} + +// Internal EffectFactory implementation class. Only one of these helpers is allocated +// per D3D device, even if there are multiple public facing EffectFactory instances. +class EffectFactory::Impl +{ +public: + Impl(_In_ ID3D12Device* device, _In_ ID3D12DescriptorHeap* textureDescriptors, _In_ ID3D12DescriptorHeap* samplerDescriptors) noexcept(false) + : mTextureDescriptors(nullptr) + , mSamplerDescriptors(nullptr) + , mSharing(true) + , mUseNormalMapEffect(true) + , mEnableLighting(true) + , mEnablePerPixelLighting(true) + , mEnableFog(false) + , mEnableInstancing(false) + , mDevice(device) + { + if (textureDescriptors) + mTextureDescriptors = std::make_unique(textureDescriptors); + if (samplerDescriptors) + mSamplerDescriptors = std::make_unique(samplerDescriptors); + } + + std::shared_ptr CreateEffect( + const EffectInfo& info, + const EffectPipelineStateDescription& opaquePipelineState, + const EffectPipelineStateDescription& alphaPipelineState, + const D3D12_INPUT_LAYOUT_DESC& inputLayout, + int textureDescriptorOffset, + int samplerDescriptorOffset); + + void ReleaseCache(); + + std::unique_ptr mTextureDescriptors; + std::unique_ptr mSamplerDescriptors; + + bool mSharing; + bool mUseNormalMapEffect; + bool mEnableLighting; + bool mEnablePerPixelLighting; + bool mEnableFog; + bool mEnableInstancing; + +private: + ComPtr mDevice; + + using EffectCache = std::map< std::wstring, std::shared_ptr >; + + EffectCache mEffectCache; + EffectCache mEffectCacheSkinning; + EffectCache mEffectCacheDualTexture; + EffectCache mEffectCacheNormalMap; + EffectCache mEffectCacheNormalMapSkinned; + + std::mutex mutex; +}; + + +std::shared_ptr EffectFactory::Impl::CreateEffect( + const EffectInfo& info, + const EffectPipelineStateDescription& opaquePipelineState, + const EffectPipelineStateDescription& alphaPipelineState, + const D3D12_INPUT_LAYOUT_DESC& inputLayoutDesc, + int textureDescriptorOffset, + int samplerDescriptorOffset) +{ + // If textures are required, make sure we have a descriptor heap + if (!mTextureDescriptors && (info.diffuseTextureIndex != -1 || info.specularTextureIndex != -1 || info.normalTextureIndex != -1 || info.emissiveTextureIndex != -1)) + { + DebugTrace("ERROR: EffectFactory created without texture descriptor heap with texture index set (diffuse %d, specular %d, normal %d, emissive %d)!\n", + info.diffuseTextureIndex, info.specularTextureIndex, info.normalTextureIndex, info.emissiveTextureIndex); + throw std::runtime_error("EffectFactory"); + } + if (!mSamplerDescriptors && (info.samplerIndex != -1 || info.samplerIndex2 != -1)) + { + DebugTrace("ERROR: EffectFactory created without sampler descriptor heap with sampler index set (samplerIndex %d, samplerIndex2 %d)!\n", + info.samplerIndex, info.samplerIndex2); + throw std::runtime_error("EffectFactory"); + } + + // If we have descriptors, make sure we have both texture and sampler descriptors + if ((mTextureDescriptors == nullptr) != (mSamplerDescriptors == nullptr)) + { + DebugTrace("ERROR: A texture or sampler descriptor heap was provided, but both are required.\n"); + throw std::runtime_error("EffectFactory"); + } + + // Validate the we have either both texture and sampler descriptors, or neither + if ((info.diffuseTextureIndex == -1) != (info.samplerIndex == -1)) + { + DebugTrace("ERROR: Material provides either a texture or sampler, but both are required.\n"); + throw std::runtime_error("EffectFactory"); + } + + const int diffuseTextureIndex = (info.diffuseTextureIndex != -1 && mTextureDescriptors != nullptr) ? info.diffuseTextureIndex + textureDescriptorOffset : -1; + const int specularTextureIndex = (info.specularTextureIndex != -1 && mTextureDescriptors != nullptr) ? info.specularTextureIndex + textureDescriptorOffset : -1; + const int emissiveTextureIndex = (info.emissiveTextureIndex != -1 && mTextureDescriptors != nullptr) ? info.emissiveTextureIndex + textureDescriptorOffset : -1; + const int normalTextureIndex = (info.normalTextureIndex != -1 && mTextureDescriptors != nullptr) ? info.normalTextureIndex + textureDescriptorOffset : -1; + const int samplerIndex = (info.samplerIndex != -1 && mSamplerDescriptors != nullptr) ? info.samplerIndex + samplerDescriptorOffset : -1; + const int samplerIndex2 = (info.samplerIndex2 != -1 && mSamplerDescriptors != nullptr) ? info.samplerIndex2 + samplerDescriptorOffset : -1; + + // Modify base pipeline state + EffectPipelineStateDescription derivedPSD = (info.alphaValue < 1.0f) ? alphaPipelineState : opaquePipelineState; + derivedPSD.inputLayout = inputLayoutDesc; + + std::wstring cacheName; + if (info.enableSkinning) + { + int effectflags = (mEnablePerPixelLighting) ? EffectFlags::PerPixelLighting : EffectFlags::Lighting; + + if (mEnableFog) + { + effectflags |= EffectFlags::Fog; + } + + if (info.biasedVertexNormals) + { + effectflags |= EffectFlags::BiasedVertexNormals; + } + + if (info.enableNormalMaps && mUseNormalMapEffect) + { + // SkinnedNormalMapEffect + if (specularTextureIndex != -1) + { + effectflags |= EffectFlags::Specular; + } + + if (mSharing && !info.name.empty()) + { + const uint32_t hash = derivedPSD.ComputeHash(); + cacheName = std::to_wstring(effectflags) + info.name + std::to_wstring(hash); + + auto it = mEffectCacheNormalMapSkinned.find(cacheName); + if (mSharing && it != mEffectCacheNormalMapSkinned.end()) + { + return it->second; + } + } + + auto effect = std::make_shared(mDevice.Get(), effectflags, derivedPSD); + + SetMaterialProperties(effect.get(), info); + + if (diffuseTextureIndex != -1) + { + effect->SetTexture( + mTextureDescriptors->GetGpuHandle(static_cast(diffuseTextureIndex)), + mSamplerDescriptors->GetGpuHandle(static_cast(samplerIndex))); + } + + if (specularTextureIndex != -1) + { + effect->SetSpecularTexture(mTextureDescriptors->GetGpuHandle(static_cast(specularTextureIndex))); + } + + if (normalTextureIndex != -1) + { + effect->SetNormalTexture(mTextureDescriptors->GetGpuHandle(static_cast(normalTextureIndex))); + } + + if (mSharing && !info.name.empty()) + { + std::lock_guard lock(mutex); + EffectCache::value_type v(cacheName, effect); + mEffectCacheNormalMapSkinned.insert(v); + } + + return std::move(effect); + } + else + { + // SkinnedEffect + if (mSharing && !info.name.empty()) + { + const uint32_t hash = derivedPSD.ComputeHash(); + cacheName = std::to_wstring(effectflags) + info.name + std::to_wstring(hash); + + auto it = mEffectCacheSkinning.find(cacheName); + if (mSharing && it != mEffectCacheSkinning.end()) + { + return it->second; + } + } + + auto effect = std::make_shared(mDevice.Get(), effectflags, derivedPSD); + + SetMaterialProperties(effect.get(), info); + + if (diffuseTextureIndex != -1) + { + effect->SetTexture( + mTextureDescriptors->GetGpuHandle(static_cast(diffuseTextureIndex)), + mSamplerDescriptors->GetGpuHandle(static_cast(samplerIndex))); + } + + if (mSharing && !info.name.empty()) + { + std::lock_guard lock(mutex); + EffectCache::value_type v(cacheName, effect); + mEffectCacheSkinning.insert(v); + } + + return std::move(effect); + } + } + else if (info.enableDualTexture) + { + // DualTextureEffect + int effectflags = EffectFlags::None; + + if (mEnableFog) + { + effectflags |= EffectFlags::Fog; + } + + if (mSharing && !info.name.empty()) + { + const uint32_t hash = derivedPSD.ComputeHash(); + cacheName = std::to_wstring(effectflags) + info.name + std::to_wstring(hash); + + auto it = mEffectCacheDualTexture.find(cacheName); + if (mSharing && it != mEffectCacheDualTexture.end()) + { + return it->second; + } + } + + if (info.perVertexColor) + { + effectflags |= EffectFlags::VertexColor; + } + + auto effect = std::make_shared(mDevice.Get(), effectflags, derivedPSD); + + // Dual texture effect doesn't support lighting (usually it's lightmaps) + effect->SetAlpha(info.alphaValue); + + const XMVECTOR color = XMLoadFloat3(&info.diffuseColor); + effect->SetDiffuseColor(color); + + if (diffuseTextureIndex != -1) + { + effect->SetTexture( + mTextureDescriptors->GetGpuHandle(static_cast(diffuseTextureIndex)), + mSamplerDescriptors->GetGpuHandle(static_cast(samplerIndex))); + } + + if (emissiveTextureIndex != -1) + { + if (samplerIndex2 == -1) + { + DebugTrace("ERROR: Dual-texture requires a second sampler (emissive %d)\n", emissiveTextureIndex); + throw std::runtime_error("EffectFactory"); + } + + effect->SetTexture2( + mTextureDescriptors->GetGpuHandle(static_cast(emissiveTextureIndex)), + mSamplerDescriptors->GetGpuHandle(static_cast(samplerIndex2))); + } + else if (specularTextureIndex != -1) + { + // If there's no emissive texture specified, use the specular texture as the second texture + if (samplerIndex2 == -1) + { + DebugTrace("ERROR: Dual-texture requires a second sampler (specular %d)\n", specularTextureIndex); + throw std::runtime_error("EffectFactory"); + } + + effect->SetTexture2( + mTextureDescriptors->GetGpuHandle(static_cast(specularTextureIndex)), + mSamplerDescriptors->GetGpuHandle(static_cast(samplerIndex2))); + } + + if (mSharing && !info.name.empty()) + { + std::lock_guard lock(mutex); + EffectCache::value_type v(cacheName, effect); + mEffectCacheDualTexture.insert(v); + } + + return std::move(effect); + } + else if (info.enableNormalMaps && mUseNormalMapEffect) + { + // NormalMapEffect + int effectflags = EffectFlags::PerPixelLighting; + + if (mEnableFog) + { + effectflags |= EffectFlags::Fog; + } + + if (mEnableInstancing) + { + effectflags |= EffectFlags::Instancing; + } + + if (info.perVertexColor) + { + effectflags |= EffectFlags::VertexColor; + } + + if (info.biasedVertexNormals) + { + effectflags |= EffectFlags::BiasedVertexNormals; + } + + if (specularTextureIndex != -1) + { + effectflags |= EffectFlags::Specular; + } + + if (mSharing && !info.name.empty()) + { + const uint32_t hash = derivedPSD.ComputeHash(); + cacheName = std::to_wstring(effectflags) + info.name + std::to_wstring(hash); + + auto it = mEffectCacheNormalMap.find(cacheName); + if (mSharing && it != mEffectCacheNormalMap.end()) + { + return it->second; + } + } + + auto effect = std::make_shared(mDevice.Get(), effectflags, derivedPSD); + + SetMaterialProperties(effect.get(), info); + + if (diffuseTextureIndex != -1) + { + effect->SetTexture( + mTextureDescriptors->GetGpuHandle(static_cast(diffuseTextureIndex)), + mSamplerDescriptors->GetGpuHandle(static_cast(samplerIndex))); + } + + if (specularTextureIndex != -1) + { + effect->SetSpecularTexture(mTextureDescriptors->GetGpuHandle(static_cast(specularTextureIndex))); + } + + if (normalTextureIndex != -1) + { + effect->SetNormalTexture(mTextureDescriptors->GetGpuHandle(static_cast(normalTextureIndex))); + } + + if (mSharing && !info.name.empty()) + { + std::lock_guard lock(mutex); + EffectCache::value_type v(cacheName, effect); + mEffectCacheNormalMap.insert(v); + } + + return std::move(effect); + } + else + { + // set effect flags for creation + int effectflags = EffectFlags::None; + + if (mEnableLighting) + { + effectflags = (mEnablePerPixelLighting) ? EffectFlags::PerPixelLighting : EffectFlags::Lighting; + } + + if (mEnableFog) + { + effectflags |= EffectFlags::Fog; + } + + if (info.perVertexColor) + { + effectflags |= EffectFlags::VertexColor; + } + + if (diffuseTextureIndex != -1) + { + effectflags |= EffectFlags::Texture; + } + + if (info.biasedVertexNormals) + { + effectflags |= EffectFlags::BiasedVertexNormals; + } + + // BasicEffect + if (mSharing && !info.name.empty()) + { + const uint32_t hash = derivedPSD.ComputeHash(); + cacheName = std::to_wstring(effectflags) + info.name + std::to_wstring(hash); + + auto it = mEffectCache.find(cacheName); + if (mSharing && it != mEffectCache.end()) + { + return it->second; + } + } + + auto effect = std::make_shared(mDevice.Get(), effectflags, derivedPSD); + + SetMaterialProperties(effect.get(), info); + + if (diffuseTextureIndex != -1) + { + effect->SetTexture( + mTextureDescriptors->GetGpuHandle(static_cast(diffuseTextureIndex)), + mSamplerDescriptors->GetGpuHandle(static_cast(samplerIndex))); + } + + if (mSharing && !info.name.empty()) + { + std::lock_guard lock(mutex); + EffectCache::value_type v(cacheName, effect); + mEffectCache.insert(v); + } + + return std::move(effect); + } +} + +void EffectFactory::Impl::ReleaseCache() +{ + std::lock_guard lock(mutex); + mEffectCache.clear(); + mEffectCacheSkinning.clear(); + mEffectCacheDualTexture.clear(); + mEffectCacheNormalMap.clear(); + mEffectCacheNormalMapSkinned.clear(); +} + + + +//-------------------------------------------------------------------------------------- +// EffectFactory +//-------------------------------------------------------------------------------------- + +EffectFactory::EffectFactory(_In_ ID3D12Device* device) : + pImpl(std::make_shared(device, nullptr, nullptr)) +{ +} + +EffectFactory::EffectFactory(_In_ ID3D12DescriptorHeap* textureDescriptors, _In_ ID3D12DescriptorHeap* samplerDescriptors) +{ + if (!textureDescriptors) + { + throw std::invalid_argument("Texture descriptor heap cannot be null if no device is provided. Use the alternative EffectFactory constructor instead."); + } + if (!samplerDescriptors) + { + throw std::invalid_argument("Descriptor heap cannot be null if no device is provided. Use the alternative EffectFactory constructor instead."); + } + +#if defined(_MSC_VER) || !defined(_WIN32) + const D3D12_DESCRIPTOR_HEAP_TYPE textureHeapType = textureDescriptors->GetDesc().Type; + const D3D12_DESCRIPTOR_HEAP_TYPE samplerHeapType = samplerDescriptors->GetDesc().Type; +#else + D3D12_DESCRIPTOR_HEAP_DESC tmpDesc1, tmpDesc2; + const D3D12_DESCRIPTOR_HEAP_TYPE textureHeapType = textureDescriptors->GetDesc(&tmpDesc1)->Type; + const D3D12_DESCRIPTOR_HEAP_TYPE samplerHeapType = samplerDescriptors->GetDesc(&tmpDesc2)->Type; +#endif + + if (textureHeapType != D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV) + { + throw std::invalid_argument("EffectFactory::CreateEffect requires a CBV_SRV_UAV descriptor heap for textureDescriptors."); + } + if (samplerHeapType != D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER) + { + throw std::invalid_argument("EffectFactory::CreateEffect requires a SAMPLER descriptor heap for samplerDescriptors."); + } + + ComPtr device; +#if (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) + textureDescriptors->GetDevice(IID_GRAPHICS_PPV_ARGS(device.GetAddressOf())); +#else + HRESULT hr = textureDescriptors->GetDevice(IID_PPV_ARGS(device.GetAddressOf())); + ThrowIfFailed(hr); +#endif + + pImpl = std::make_shared(device.Get(), textureDescriptors, samplerDescriptors); +} + + +EffectFactory::EffectFactory(EffectFactory&&) noexcept = default; +EffectFactory& EffectFactory::operator= (EffectFactory&&) noexcept = default; +EffectFactory::~EffectFactory() = default; + + +std::shared_ptr EffectFactory::CreateEffect( + const EffectInfo& info, + const EffectPipelineStateDescription& opaquePipelineState, + const EffectPipelineStateDescription& alphaPipelineState, + const D3D12_INPUT_LAYOUT_DESC& inputLayout, + int textureDescriptorOffset, + int samplerDescriptorOffset) +{ + return pImpl->CreateEffect(info, opaquePipelineState, alphaPipelineState, inputLayout, textureDescriptorOffset, samplerDescriptorOffset); +} + + +void EffectFactory::ReleaseCache() +{ + pImpl->ReleaseCache(); +} + + +// Properties. +void EffectFactory::SetSharing(bool enabled) noexcept +{ + pImpl->mSharing = enabled; +} + +void EffectFactory::EnableLighting(bool enabled) noexcept +{ + pImpl->mEnableLighting = enabled; +} + +void EffectFactory::EnablePerPixelLighting(bool enabled) noexcept +{ + pImpl->mEnablePerPixelLighting = enabled; +} + +void EffectFactory::EnableFogging(bool enabled) noexcept +{ + pImpl->mEnableFog = enabled; +} + +void EffectFactory::EnableInstancing(bool enabled) noexcept +{ + pImpl->mEnableInstancing = enabled; +} + +void EffectFactory::EnableNormalMapEffect(bool enabled) noexcept +{ + pImpl->mUseNormalMapEffect = enabled; +} diff --git a/Common/DirectXTK12/Src/EffectPipelineStateDescription.cpp b/Common/DirectXTK12/Src/EffectPipelineStateDescription.cpp new file mode 100644 index 0000000..b645ae4 --- /dev/null +++ b/Common/DirectXTK12/Src/EffectPipelineStateDescription.cpp @@ -0,0 +1,114 @@ +//-------------------------------------------------------------------------------------- +// File: EffectPipelineStateDescription.cpp +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "pch.h" +#include "EffectPipelineStateDescription.h" + +#include "DirectXHelpers.h" +#include "PlatformHelpers.h" + + +using namespace DirectX; + +namespace +{ + // 0x04C11DB7 is the official polynomial used by PKZip, WinZip and Ethernet + static const uint32_t s_crc32[] = + { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, + }; +} + + +uint32_t EffectPipelineStateDescription::ComputeHash() const noexcept +{ + // Computes a CRC32 of the structure + uint32_t crc = 0xFFFFFFFF; + + // Ignores the input layout which contains pointers + auto ptr = reinterpret_cast(this) + sizeof(D3D12_INPUT_LAYOUT_DESC); + for (size_t j = 0; j < (sizeof(EffectPipelineStateDescription) - sizeof(D3D12_INPUT_LAYOUT_DESC)); ++j) + { + crc = (crc >> 8) ^ s_crc32[(crc & 0xff) ^ *ptr++]; + } + + return crc ^ 0xFFFFFFFF; +} + + +void EffectPipelineStateDescription::CreatePipelineState( + _In_ ID3D12Device* device, + _In_ ID3D12RootSignature* rootSignature, + const D3D12_SHADER_BYTECODE& vertexShader, + const D3D12_SHADER_BYTECODE& pixelShader, + _Outptr_ ID3D12PipelineState** pPipelineState) const +{ +#if defined(_MSC_VER) || !defined(_WIN32) + auto psoDesc = GetDesc(); +#else + D3D12_GRAPHICS_PIPELINE_STATE_DESC tmpPSODesc; + auto& psoDesc = *GetDesc(&tmpPSODesc); +#endif + + psoDesc.pRootSignature = rootSignature; + psoDesc.VS = vertexShader; + psoDesc.PS = pixelShader; + + HRESULT hr = device->CreateGraphicsPipelineState( + &psoDesc, + IID_GRAPHICS_PPV_ARGS(pPipelineState)); + + if (FAILED(hr)) + { + DebugTrace("ERROR: CreatePipelineState failed to create a PSO. Enable the Direct3D Debug Layer for more information (%08X)\n", static_cast(hr)); + throw std::runtime_error("CreateGraphicsPipelineState"); + } +} diff --git a/Common/DirectXTK12/Src/EffectTextureFactory.cpp b/Common/DirectXTK12/Src/EffectTextureFactory.cpp new file mode 100644 index 0000000..4118257 --- /dev/null +++ b/Common/DirectXTK12/Src/EffectTextureFactory.cpp @@ -0,0 +1,324 @@ +//-------------------------------------------------------------------------------------- +// File: EffectTextureFactory.cpp +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "pch.h" + +#include "Effects.h" +#include "DirectXHelpers.h" +#include "DDSTextureLoader.h" +#include "DescriptorHeap.h" +#include "PlatformHelpers.h" +#include "ResourceUploadBatch.h" +#include "WICTextureLoader.h" + +#include + + +using namespace DirectX; +using Microsoft::WRL::ComPtr; + + +class EffectTextureFactory::Impl +{ +public: + struct TextureCacheEntry + { + ComPtr mResource; + bool mIsCubeMap; + size_t slot; + + TextureCacheEntry() noexcept : mIsCubeMap(false), slot(0) {} + }; + + using TextureCache = std::map< std::wstring, TextureCacheEntry >; + + Impl( + _In_ ID3D12Device* device, + ResourceUploadBatch& resourceUploadBatch, + _In_ ID3D12DescriptorHeap* descriptorHeap) + : mPath{} + , mTextureDescriptorHeap(descriptorHeap) + , mDevice(device) + , mResourceUploadBatch(resourceUploadBatch) + , mSharing(true) + , mForceSRGB(false) + , mAutoGenMips(false) + { + *mPath = 0; + } + + Impl( + _In_ ID3D12Device* device, + ResourceUploadBatch& resourceUploadBatch, + _In_ size_t numDescriptors, + _In_ D3D12_DESCRIPTOR_HEAP_FLAGS descriptorHeapFlags) + : mPath{} + , mTextureDescriptorHeap(device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, descriptorHeapFlags, numDescriptors) + , mDevice(device) + , mResourceUploadBatch(resourceUploadBatch) + , mSharing(true) + , mForceSRGB(false) + , mAutoGenMips(false) + { + SetDebugObjectName(mTextureDescriptorHeap.Heap(), L"EffectTextureFactory"); + } + + size_t CreateTexture(_In_z_ const wchar_t* name, int descriptorSlot); + + void ReleaseCache(); + void SetSharing(bool enabled) noexcept { mSharing = enabled; } + void EnableForceSRGB(bool forceSRGB) noexcept { mForceSRGB = forceSRGB; } + void EnableAutoGenMips(bool generateMips) noexcept { mAutoGenMips = generateMips; } + + wchar_t mPath[MAX_PATH]; + + ::DescriptorHeap mTextureDescriptorHeap; + std::vector mResources; // flat list of unique resources so we can index into it + +private: + ComPtr mDevice; + ResourceUploadBatch& mResourceUploadBatch; + + TextureCache mTextureCache; + + bool mSharing; + bool mForceSRGB; + bool mAutoGenMips; + + std::mutex mutex; +}; + + +_Use_decl_annotations_ +size_t EffectTextureFactory::Impl::CreateTexture(_In_z_ const wchar_t* name, int descriptorSlot) +{ + if (!name) + throw std::invalid_argument("name required for CreateTexture"); + + auto it = mTextureCache.find(name); + + TextureCacheEntry textureEntry = {}; + + if (mSharing && it != mTextureCache.end()) + { + textureEntry = it->second; + } + else + { + wchar_t fullName[MAX_PATH] = {}; + wcscpy_s(fullName, mPath); + wcscat_s(fullName, name); + + WIN32_FILE_ATTRIBUTE_DATA fileAttr = {}; + if (!GetFileAttributesExW(fullName, GetFileExInfoStandard, &fileAttr)) + { + // Try Current Working Directory (CWD) + wcscpy_s(fullName, name); + if (!GetFileAttributesExW(fullName, GetFileExInfoStandard, &fileAttr)) + { + DebugTrace("ERROR: EffectTextureFactory could not find texture file '%ls'\n", name); + throw std::runtime_error("EffectTextureFactory::CreateTexture"); + } + } + + wchar_t ext[_MAX_EXT] = {}; + _wsplitpath_s(name, nullptr, 0, nullptr, 0, nullptr, 0, ext, _MAX_EXT); + const bool isdds = _wcsicmp(ext, L".dds") == 0; + + DDS_LOADER_FLAGS loadFlags = DDS_LOADER_DEFAULT; + if (mForceSRGB) + loadFlags |= DDS_LOADER_FORCE_SRGB; + if (mAutoGenMips) + loadFlags |= DDS_LOADER_MIP_AUTOGEN; + + if (isdds) + { + HRESULT hr = CreateDDSTextureFromFileEx( + mDevice.Get(), + mResourceUploadBatch, + fullName, + 0u, + D3D12_RESOURCE_FLAG_NONE, + loadFlags, + textureEntry.mResource.ReleaseAndGetAddressOf(), + nullptr, + &textureEntry.mIsCubeMap); + if (FAILED(hr)) + { + DebugTrace("ERROR: CreateDDSTextureFromFile failed (%08X) for '%ls'\n", + static_cast(hr), fullName); + throw std::runtime_error("EffectTextureFactory::CreateDDSTextureFromFile"); + } + } + else + { + static_assert(static_cast(DDS_LOADER_DEFAULT) == static_cast(WIC_LOADER_DEFAULT), "DDS/WIC Load flags mismatch"); + static_assert(static_cast(DDS_LOADER_FORCE_SRGB) == static_cast(WIC_LOADER_FORCE_SRGB), "DDS/WIC Load flags mismatch"); + static_assert(static_cast(DDS_LOADER_MIP_AUTOGEN) == static_cast(WIC_LOADER_MIP_AUTOGEN), "DDS/WIC Load flags mismatch"); + static_assert(static_cast(DDS_LOADER_MIP_RESERVE) == static_cast(WIC_LOADER_MIP_RESERVE), "DDS/WIC Load flags mismatch"); + + textureEntry.mIsCubeMap = false; + + HRESULT hr = CreateWICTextureFromFileEx( + mDevice.Get(), + mResourceUploadBatch, + fullName, + 0u, + D3D12_RESOURCE_FLAG_NONE, + static_cast(loadFlags), + textureEntry.mResource.ReleaseAndGetAddressOf()); + if (FAILED(hr)) + { + DebugTrace("ERROR: CreateWICTextureFromFile failed (%08X) for '%ls'\n", + static_cast(hr), fullName); + throw std::runtime_error("EffectTextureFactory::CreateWICTextureFromFile"); + } + } + + std::lock_guard lock(mutex); + textureEntry.slot = mResources.size(); + if (mSharing) + { + TextureCache::value_type v(name, textureEntry); + mTextureCache.insert(v); + } + mResources.push_back(textureEntry); + } + + assert(textureEntry.mResource != nullptr); + + // bind a new descriptor in slot + auto const textureDescriptor = mTextureDescriptorHeap.GetCpuHandle(static_cast(descriptorSlot)); + DirectX::CreateShaderResourceView(mDevice.Get(), textureEntry.mResource.Get(), textureDescriptor, textureEntry.mIsCubeMap); + + return textureEntry.slot; +} + +void EffectTextureFactory::Impl::ReleaseCache() +{ + std::lock_guard lock(mutex); + mTextureCache.clear(); +} + + + +//-------------------------------------------------------------------------------------- +// EffectTextureFactory +//-------------------------------------------------------------------------------------- + +_Use_decl_annotations_ +EffectTextureFactory::EffectTextureFactory( + ID3D12Device* device, + ResourceUploadBatch& resourceUploadBatch, + ID3D12DescriptorHeap* descriptorHeap) noexcept(false) : + pImpl(std::make_unique(device, resourceUploadBatch, descriptorHeap)) +{ +} + +_Use_decl_annotations_ +EffectTextureFactory::EffectTextureFactory( + ID3D12Device* device, + ResourceUploadBatch& resourceUploadBatch, + size_t numDescriptors, + D3D12_DESCRIPTOR_HEAP_FLAGS descriptorHeapFlags) noexcept(false) : + pImpl(std::make_unique(device, resourceUploadBatch, numDescriptors, descriptorHeapFlags)) +{ +} + + +EffectTextureFactory::EffectTextureFactory(EffectTextureFactory&&) noexcept = default; +EffectTextureFactory& EffectTextureFactory::operator= (EffectTextureFactory&&) noexcept = default; +EffectTextureFactory::~EffectTextureFactory() = default; + + +_Use_decl_annotations_ +size_t EffectTextureFactory::CreateTexture(_In_z_ const wchar_t* name, int descriptorIndex) +{ + return pImpl->CreateTexture(name, descriptorIndex); +} + + +void EffectTextureFactory::ReleaseCache() +{ + pImpl->ReleaseCache(); +} + + +// Properties. +void EffectTextureFactory::SetSharing(bool enabled) noexcept +{ + pImpl->SetSharing(enabled); +} + +void EffectTextureFactory::EnableForceSRGB(bool forceSRGB) noexcept +{ + pImpl->EnableForceSRGB(forceSRGB); +} + +void EffectTextureFactory::EnableAutoGenMips(bool generateMips) noexcept +{ + pImpl->EnableAutoGenMips(generateMips); +} + +void EffectTextureFactory::SetDirectory(_In_opt_z_ const wchar_t* path) noexcept +{ + if (path && *path != 0) + { + wcscpy_s(pImpl->mPath, path); + size_t len = wcsnlen(pImpl->mPath, MAX_PATH); + if (len > 0 && len < (MAX_PATH - 1)) + { + // Ensure it has a trailing slash + if (pImpl->mPath[len - 1] != L'\\') + { + pImpl->mPath[len] = L'\\'; + pImpl->mPath[len + 1] = 0; + } + } + } + else + *pImpl->mPath = 0; +} + +ID3D12DescriptorHeap* EffectTextureFactory::Heap() const noexcept +{ + return pImpl->mTextureDescriptorHeap.Heap(); +} + +// Shorthand accessors for the descriptor heap +D3D12_CPU_DESCRIPTOR_HANDLE EffectTextureFactory::GetCpuDescriptorHandle(size_t index) const +{ + return pImpl->mTextureDescriptorHeap.GetCpuHandle(index); +} + +D3D12_GPU_DESCRIPTOR_HANDLE EffectTextureFactory::GetGpuDescriptorHandle(size_t index) const +{ + return pImpl->mTextureDescriptorHeap.GetGpuHandle(index); +} + +size_t EffectTextureFactory::ResourceCount() const noexcept +{ + return pImpl->mResources.size(); +} + +_Use_decl_annotations_ +void EffectTextureFactory::GetResource(size_t slot, ID3D12Resource** resource, bool* isCubeMap) +{ + if (slot >= pImpl->mResources.size()) + throw std::invalid_argument("Resource slot is invalid"); + + const auto& textureEntry = pImpl->mResources[slot]; + + textureEntry.mResource.CopyTo(resource); + + if (isCubeMap) + { + *isCubeMap = textureEntry.mIsCubeMap; + } +} diff --git a/Common/DirectXTK12/Src/EnvironmentMapEffect.cpp b/Common/DirectXTK12/Src/EnvironmentMapEffect.cpp new file mode 100644 index 0000000..69864ba --- /dev/null +++ b/Common/DirectXTK12/Src/EnvironmentMapEffect.cpp @@ -0,0 +1,754 @@ +//-------------------------------------------------------------------------------------- +// File: EnvironmentMapEffect.cpp +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "pch.h" +#include "EffectCommon.h" + +using namespace DirectX; +using Microsoft::WRL::ComPtr; + +namespace +{ + // Constant buffer layout. Must match the shader! + struct EnvironmentMapEffectConstants + { + XMVECTOR environmentMapSpecular; + float environmentMapAmount; + float fresnelFactor; + float pad[2]; + + XMVECTOR diffuseColor; + XMVECTOR emissiveColor; + + XMVECTOR lightDirection[IEffectLights::MaxDirectionalLights]; + XMVECTOR lightDiffuseColor[IEffectLights::MaxDirectionalLights]; + + XMVECTOR eyePosition; + + XMVECTOR fogColor; + XMVECTOR fogVector; + + XMMATRIX world; + XMVECTOR worldInverseTranspose[3]; + XMMATRIX worldViewProj; + }; + + static_assert((sizeof(EnvironmentMapEffectConstants) % 16) == 0, "CB size not padded correctly"); + + + // Traits type describes our characteristics to the EffectBase template. + struct EnvironmentMapEffectTraits + { + using ConstantBufferType = EnvironmentMapEffectConstants; + + static constexpr int VertexShaderCount = 6; + static constexpr int PixelShaderCount = 16; + static constexpr int ShaderPermutationCount = 40; + static constexpr int RootSignatureCount = 1; + }; +} + +// Internal EnvironmentMapEffect implementation class. +class EnvironmentMapEffect::Impl : public EffectBase +{ +public: + Impl(_In_ ID3D12Device* device, + uint32_t effectFlags, + const EffectPipelineStateDescription& pipelineDescription, + EnvironmentMapEffect::Mapping mapping); + + enum RootParameterIndex + { + TextureSRV, + TextureSampler, + CubemapSRV, + CubemapSampler, + ConstantBuffer, + RootParameterCount + }; + + EffectLights lights; + + D3D12_GPU_DESCRIPTOR_HANDLE texture; + D3D12_GPU_DESCRIPTOR_HANDLE textureSampler; + D3D12_GPU_DESCRIPTOR_HANDLE environmentMap; + D3D12_GPU_DESCRIPTOR_HANDLE environmentMapSampler; + + int GetPipelineStatePermutation(EnvironmentMapEffect::Mapping mapping, uint32_t effectFlags) const noexcept; + + void Apply(_In_ ID3D12GraphicsCommandList* commandList); +}; + + +#pragma region Shaders +// Include the precompiled shader code. +namespace +{ +#ifdef _GAMING_XBOX_SCARLETT +#include "XboxGamingScarlettEnvironmentMapEffect_VSEnvMap.inc" +#include "XboxGamingScarlettEnvironmentMapEffect_VSEnvMapFresnel.inc" +#include "XboxGamingScarlettEnvironmentMapEffect_VSEnvMapPixelLighting.inc" + +#include "XboxGamingScarlettEnvironmentMapEffect_VSEnvMapBn.inc" +#include "XboxGamingScarlettEnvironmentMapEffect_VSEnvMapFresnelBn.inc" +#include "XboxGamingScarlettEnvironmentMapEffect_VSEnvMapPixelLightingBn.inc" + +#include "XboxGamingScarlettEnvironmentMapEffect_PSEnvMap.inc" +#include "XboxGamingScarlettEnvironmentMapEffect_PSEnvMapNoFog.inc" +#include "XboxGamingScarlettEnvironmentMapEffect_PSEnvMapSpecular.inc" +#include "XboxGamingScarlettEnvironmentMapEffect_PSEnvMapSpecularNoFog.inc" +#include "XboxGamingScarlettEnvironmentMapEffect_PSEnvMapPixelLighting.inc" +#include "XboxGamingScarlettEnvironmentMapEffect_PSEnvMapPixelLightingNoFog.inc" +#include "XboxGamingScarlettEnvironmentMapEffect_PSEnvMapPixelLightingFresnel.inc" +#include "XboxGamingScarlettEnvironmentMapEffect_PSEnvMapPixelLightingFresnelNoFog.inc" + +#include "XboxGamingScarlettEnvironmentMapEffect_PSEnvMapSpherePixelLighting.inc" +#include "XboxGamingScarlettEnvironmentMapEffect_PSEnvMapSpherePixelLightingNoFog.inc" +#include "XboxGamingScarlettEnvironmentMapEffect_PSEnvMapSpherePixelLightingFresnel.inc" +#include "XboxGamingScarlettEnvironmentMapEffect_PSEnvMapSpherePixelLightingFresnelNoFog.inc" + +#include "XboxGamingScarlettEnvironmentMapEffect_PSEnvMapDualParabolaPixelLighting.inc" +#include "XboxGamingScarlettEnvironmentMapEffect_PSEnvMapDualParabolaPixelLightingNoFog.inc" +#include "XboxGamingScarlettEnvironmentMapEffect_PSEnvMapDualParabolaPixelLightingFresnel.inc" +#include "XboxGamingScarlettEnvironmentMapEffect_PSEnvMapDualParabolaPixelLightingFresnelNoFog.inc" +#elif defined(_GAMING_XBOX) +#include "XboxGamingXboxOneEnvironmentMapEffect_VSEnvMap.inc" +#include "XboxGamingXboxOneEnvironmentMapEffect_VSEnvMapFresnel.inc" +#include "XboxGamingXboxOneEnvironmentMapEffect_VSEnvMapPixelLighting.inc" + +#include "XboxGamingXboxOneEnvironmentMapEffect_VSEnvMapBn.inc" +#include "XboxGamingXboxOneEnvironmentMapEffect_VSEnvMapFresnelBn.inc" +#include "XboxGamingXboxOneEnvironmentMapEffect_VSEnvMapPixelLightingBn.inc" + +#include "XboxGamingXboxOneEnvironmentMapEffect_PSEnvMap.inc" +#include "XboxGamingXboxOneEnvironmentMapEffect_PSEnvMapNoFog.inc" +#include "XboxGamingXboxOneEnvironmentMapEffect_PSEnvMapSpecular.inc" +#include "XboxGamingXboxOneEnvironmentMapEffect_PSEnvMapSpecularNoFog.inc" +#include "XboxGamingXboxOneEnvironmentMapEffect_PSEnvMapPixelLighting.inc" +#include "XboxGamingXboxOneEnvironmentMapEffect_PSEnvMapPixelLightingNoFog.inc" +#include "XboxGamingXboxOneEnvironmentMapEffect_PSEnvMapPixelLightingFresnel.inc" +#include "XboxGamingXboxOneEnvironmentMapEffect_PSEnvMapPixelLightingFresnelNoFog.inc" + +#include "XboxGamingXboxOneEnvironmentMapEffect_PSEnvMapSpherePixelLighting.inc" +#include "XboxGamingXboxOneEnvironmentMapEffect_PSEnvMapSpherePixelLightingNoFog.inc" +#include "XboxGamingXboxOneEnvironmentMapEffect_PSEnvMapSpherePixelLightingFresnel.inc" +#include "XboxGamingXboxOneEnvironmentMapEffect_PSEnvMapSpherePixelLightingFresnelNoFog.inc" + +#include "XboxGamingXboxOneEnvironmentMapEffect_PSEnvMapDualParabolaPixelLighting.inc" +#include "XboxGamingXboxOneEnvironmentMapEffect_PSEnvMapDualParabolaPixelLightingNoFog.inc" +#include "XboxGamingXboxOneEnvironmentMapEffect_PSEnvMapDualParabolaPixelLightingFresnel.inc" +#include "XboxGamingXboxOneEnvironmentMapEffect_PSEnvMapDualParabolaPixelLightingFresnelNoFog.inc" +#elif defined(_XBOX_ONE) && defined(_TITLE) +#include "XboxOneEnvironmentMapEffect_VSEnvMap.inc" +#include "XboxOneEnvironmentMapEffect_VSEnvMapFresnel.inc" +#include "XboxOneEnvironmentMapEffect_VSEnvMapPixelLighting.inc" + +#include "XboxOneEnvironmentMapEffect_VSEnvMapBn.inc" +#include "XboxOneEnvironmentMapEffect_VSEnvMapFresnelBn.inc" +#include "XboxOneEnvironmentMapEffect_VSEnvMapPixelLightingBn.inc" + +#include "XboxOneEnvironmentMapEffect_PSEnvMap.inc" +#include "XboxOneEnvironmentMapEffect_PSEnvMapNoFog.inc" +#include "XboxOneEnvironmentMapEffect_PSEnvMapSpecular.inc" +#include "XboxOneEnvironmentMapEffect_PSEnvMapSpecularNoFog.inc" +#include "XboxOneEnvironmentMapEffect_PSEnvMapPixelLighting.inc" +#include "XboxOneEnvironmentMapEffect_PSEnvMapPixelLightingNoFog.inc" +#include "XboxOneEnvironmentMapEffect_PSEnvMapPixelLightingFresnel.inc" +#include "XboxOneEnvironmentMapEffect_PSEnvMapPixelLightingFresnelNoFog.inc" + +#include "XboxOneEnvironmentMapEffect_PSEnvMapSpherePixelLighting.inc" +#include "XboxOneEnvironmentMapEffect_PSEnvMapSpherePixelLightingNoFog.inc" +#include "XboxOneEnvironmentMapEffect_PSEnvMapSpherePixelLightingFresnel.inc" +#include "XboxOneEnvironmentMapEffect_PSEnvMapSpherePixelLightingFresnelNoFog.inc" + +#include "XboxOneEnvironmentMapEffect_PSEnvMapDualParabolaPixelLighting.inc" +#include "XboxOneEnvironmentMapEffect_PSEnvMapDualParabolaPixelLightingNoFog.inc" +#include "XboxOneEnvironmentMapEffect_PSEnvMapDualParabolaPixelLightingFresnel.inc" +#include "XboxOneEnvironmentMapEffect_PSEnvMapDualParabolaPixelLightingFresnelNoFog.inc" +#else +#include "EnvironmentMapEffect_VSEnvMap.inc" +#include "EnvironmentMapEffect_VSEnvMapFresnel.inc" +#include "EnvironmentMapEffect_VSEnvMapPixelLighting.inc" + +#include "EnvironmentMapEffect_VSEnvMapBn.inc" +#include "EnvironmentMapEffect_VSEnvMapFresnelBn.inc" +#include "EnvironmentMapEffect_VSEnvMapPixelLightingBn.inc" + +#include "EnvironmentMapEffect_PSEnvMap.inc" +#include "EnvironmentMapEffect_PSEnvMapNoFog.inc" +#include "EnvironmentMapEffect_PSEnvMapSpecular.inc" +#include "EnvironmentMapEffect_PSEnvMapSpecularNoFog.inc" +#include "EnvironmentMapEffect_PSEnvMapPixelLighting.inc" +#include "EnvironmentMapEffect_PSEnvMapPixelLightingNoFog.inc" +#include "EnvironmentMapEffect_PSEnvMapPixelLightingFresnel.inc" +#include "EnvironmentMapEffect_PSEnvMapPixelLightingFresnelNoFog.inc" + +#include "EnvironmentMapEffect_PSEnvMapSpherePixelLighting.inc" +#include "EnvironmentMapEffect_PSEnvMapSpherePixelLightingNoFog.inc" +#include "EnvironmentMapEffect_PSEnvMapSpherePixelLightingFresnel.inc" +#include "EnvironmentMapEffect_PSEnvMapSpherePixelLightingFresnelNoFog.inc" + +#include "EnvironmentMapEffect_PSEnvMapDualParabolaPixelLighting.inc" +#include "EnvironmentMapEffect_PSEnvMapDualParabolaPixelLightingNoFog.inc" +#include "EnvironmentMapEffect_PSEnvMapDualParabolaPixelLightingFresnel.inc" +#include "EnvironmentMapEffect_PSEnvMapDualParabolaPixelLightingFresnelNoFog.inc" +#endif +} + + +template<> +const D3D12_SHADER_BYTECODE EffectBase::VertexShaderBytecode[] = +{ + { EnvironmentMapEffect_VSEnvMap, sizeof(EnvironmentMapEffect_VSEnvMap) }, + { EnvironmentMapEffect_VSEnvMapFresnel, sizeof(EnvironmentMapEffect_VSEnvMapFresnel) }, + { EnvironmentMapEffect_VSEnvMapPixelLighting, sizeof(EnvironmentMapEffect_VSEnvMapPixelLighting) }, + + { EnvironmentMapEffect_VSEnvMapBn, sizeof(EnvironmentMapEffect_VSEnvMapBn) }, + { EnvironmentMapEffect_VSEnvMapFresnelBn, sizeof(EnvironmentMapEffect_VSEnvMapFresnelBn) }, + { EnvironmentMapEffect_VSEnvMapPixelLightingBn, sizeof(EnvironmentMapEffect_VSEnvMapPixelLightingBn) }, +}; + + +template<> +const int EffectBase::VertexShaderIndices[] = +{ + 0, // basic + 0, // basic, no fog + 1, // fresnel + 1, // fresnel, no fog + 0, // specular + 0, // specular, no fog + 1, // fresnel + specular + 1, // fresnel + specular, no fog + + 2, // pixel lighting + 2, // pixel lighting, no fog + 2, // pixel lighting, fresnel + 2, // pixel lighting, fresnel, no fog + + 3, // basic (biased vertex normals) + 3, // basic (biased vertex normals), no fog + 4, // fresnel (biased vertex normals) + 4, // fresnel (biased vertex normals), no fog + 3, // specular (biased vertex normals) + 3, // specular (biased vertex normals), no fog + 4, // fresnel + specular (biased vertex normals) + 4, // fresnel + specular (biased vertex normals), no fog + + 5, // pixel lighting (biased vertex normals) + 5, // pixel lighting (biased vertex normals), no fog + 5, // pixel lighting (biased vertex normals), fresnel + 5, // pixel lighting (biased vertex normals), fresnel, no fog + + 2, // spheremap pixel lighting + 2, // spheremap pixel lighting, no fog + 2, // spheremap pixel lighting, fresnel + 2, // spheremap pixel lighting, fresnel, no fog + + 5, // spheremap pixel lighting (biased vertex normals) + 5, // spheremap pixel lighting (biased vertex normals), no fog + 5, // spheremap pixel lighting (biased vertex normals), fresnel + 5, // spheremap pixel lighting (biased vertex normals), fresnel, no fog + + 2, // dual-parabola pixel lighting + 2, // dual-parabola pixel lighting, no fog + 2, // dual-parabola pixel lighting, fresnel + 2, // dual-parabola pixel lighting, fresnel, no fog + + 5, // dual-parabola pixel lighting (biased vertex normals) + 5, // dual-parabola pixel lighting (biased vertex normals), no fog + 5, // dual-parabola pixel lighting (biased vertex normals), fresnel + 5, // dual-parabola pixel lighting (biased vertex normals), fresnel, no fog +}; + + +template<> +const D3D12_SHADER_BYTECODE EffectBase::PixelShaderBytecode[] = +{ + { EnvironmentMapEffect_PSEnvMap, sizeof(EnvironmentMapEffect_PSEnvMap) }, + { EnvironmentMapEffect_PSEnvMapNoFog, sizeof(EnvironmentMapEffect_PSEnvMapNoFog) }, + { EnvironmentMapEffect_PSEnvMapSpecular, sizeof(EnvironmentMapEffect_PSEnvMapSpecular) }, + { EnvironmentMapEffect_PSEnvMapSpecularNoFog, sizeof(EnvironmentMapEffect_PSEnvMapSpecularNoFog) }, + { EnvironmentMapEffect_PSEnvMapPixelLighting, sizeof(EnvironmentMapEffect_PSEnvMapPixelLighting) }, + { EnvironmentMapEffect_PSEnvMapPixelLightingNoFog, sizeof(EnvironmentMapEffect_PSEnvMapPixelLightingNoFog) }, + { EnvironmentMapEffect_PSEnvMapPixelLightingFresnel, sizeof(EnvironmentMapEffect_PSEnvMapPixelLightingFresnel) }, + { EnvironmentMapEffect_PSEnvMapPixelLightingFresnelNoFog, sizeof(EnvironmentMapEffect_PSEnvMapPixelLightingFresnelNoFog) }, + + { EnvironmentMapEffect_PSEnvMapSpherePixelLighting, sizeof(EnvironmentMapEffect_PSEnvMapSpherePixelLighting) }, + { EnvironmentMapEffect_PSEnvMapSpherePixelLightingNoFog, sizeof(EnvironmentMapEffect_PSEnvMapSpherePixelLightingNoFog) }, + { EnvironmentMapEffect_PSEnvMapSpherePixelLightingFresnel, sizeof(EnvironmentMapEffect_PSEnvMapSpherePixelLightingFresnel) }, + { EnvironmentMapEffect_PSEnvMapSpherePixelLightingFresnelNoFog, sizeof(EnvironmentMapEffect_PSEnvMapSpherePixelLightingFresnelNoFog) }, + + { EnvironmentMapEffect_PSEnvMapDualParabolaPixelLighting, sizeof(EnvironmentMapEffect_PSEnvMapDualParabolaPixelLighting) }, + { EnvironmentMapEffect_PSEnvMapDualParabolaPixelLightingNoFog, sizeof(EnvironmentMapEffect_PSEnvMapDualParabolaPixelLightingNoFog) }, + { EnvironmentMapEffect_PSEnvMapDualParabolaPixelLightingFresnel, sizeof(EnvironmentMapEffect_PSEnvMapDualParabolaPixelLightingFresnel) }, + { EnvironmentMapEffect_PSEnvMapDualParabolaPixelLightingFresnelNoFog, sizeof(EnvironmentMapEffect_PSEnvMapDualParabolaPixelLightingFresnelNoFog) }, +}; + + +template<> +const int EffectBase::PixelShaderIndices[] = +{ + 0, // basic + 1, // basic, no fog + 0, // fresnel + 1, // fresnel, no fog + 2, // specular + 3, // specular, no fog + 2, // fresnel + specular + 3, // fresnel + specular, no fog + + 4, // per pixel lighting + 5, // per pixel lighting, no fog + 6, // per pixel lighting, fresnel + 7, // per pixel lighting, fresnel, no fog + + 0, // basic (biased vertex normals) + 1, // basic (biased vertex normals), no fog + 0, // fresnel (biased vertex normals) + 1, // fresnel (biased vertex normals), no fog + 2, // specular (biased vertex normals) + 3, // specular (biased vertex normals), no fog + 2, // fresnel + specular (biased vertex normals) + 3, // fresnel + specular (biased vertex normals), no fog + + 4, // per pixel lighting (biased vertex normals) + 5, // per pixel lighting (biased vertex normals), no fog + 6, // per pixel lighting (biased vertex normals), fresnel + 7, // per pixel lighting (biased vertex normals), fresnel, no fog + + 8, // spheremap pixel lighting + 9, // spheremap pixel lighting, no fog + 10, // spheremap pixel lighting, fresnel + 11, // spheremap pixel lighting, fresnel, no fog + + 8, // spheremap pixel lighting (biased vertex normals) + 9, // spheremap pixel lighting (biased vertex normals), no fog + 10, // spheremap pixel lighting (biased vertex normals), fresnel + 11, // spheremap pixel lighting (biased vertex normals), fresnel, no fog + + 12, // dual-parabola pixel lighting + 13, // dual-parabola pixel lighting, no fog + 14, // dual-parabola pixel lighting, fresnel + 15, // dual-parabola pixel lighting, fresnel, no fog + + 12, // dual-parabola pixel lighting (biased vertex normals) + 13, // dual-parabola pixel lighting (biased vertex normals), no fog + 14, // dual-parabola pixel lighting (biased vertex normals), fresnel + 15, // dual-parabola pixel lighting (biased vertex normals), fresnel, no fog +}; +#pragma endregion + +// Global pool of per-device EnvironmentMapEffect resources. +template<> +SharedResourcePool::DeviceResources> EffectBase::deviceResourcesPool = {}; + + +// Constructor. +EnvironmentMapEffect::Impl::Impl( + _In_ ID3D12Device* device, + uint32_t effectFlags, + const EffectPipelineStateDescription& pipelineDescription, + EnvironmentMapEffect::Mapping mapping) + : EffectBase(device), + texture{}, + textureSampler{}, + environmentMap{}, + environmentMapSampler{} +{ + static_assert(static_cast(std::size(EffectBase::VertexShaderIndices)) == EnvironmentMapEffectTraits::ShaderPermutationCount, "array/max mismatch"); + static_assert(static_cast(std::size(EffectBase::VertexShaderBytecode)) == EnvironmentMapEffectTraits::VertexShaderCount, "array/max mismatch"); + static_assert(static_cast(std::size(EffectBase::PixelShaderBytecode)) == EnvironmentMapEffectTraits::PixelShaderCount, "array/max mismatch"); + static_assert(static_cast(std::size(EffectBase::PixelShaderIndices)) == EnvironmentMapEffectTraits::ShaderPermutationCount, "array/max mismatch"); + + // Create root signature. + { + ENUM_FLAGS_CONSTEXPR D3D12_ROOT_SIGNATURE_FLAGS rootSignatureFlags = + D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT + | D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS + | D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS + | D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS +#ifdef _GAMING_XBOX_SCARLETT + | D3D12_ROOT_SIGNATURE_FLAG_DENY_AMPLIFICATION_SHADER_ROOT_ACCESS + | D3D12_ROOT_SIGNATURE_FLAG_DENY_MESH_SHADER_ROOT_ACCESS +#endif + ; + + CD3DX12_ROOT_PARAMETER rootParameters[RootParameterIndex::RootParameterCount] = {}; + rootParameters[RootParameterIndex::ConstantBuffer].InitAsConstantBufferView(0, 0, D3D12_SHADER_VISIBILITY_ALL); + + // Texture 1 + const CD3DX12_DESCRIPTOR_RANGE textureDescriptor(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0); + const CD3DX12_DESCRIPTOR_RANGE textureSamplerDescriptor(D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, 1, 0); + rootParameters[RootParameterIndex::TextureSRV].InitAsDescriptorTable(1, &textureDescriptor, D3D12_SHADER_VISIBILITY_PIXEL); + rootParameters[RootParameterIndex::TextureSampler].InitAsDescriptorTable(1, &textureSamplerDescriptor, D3D12_SHADER_VISIBILITY_PIXEL); + + // Texture 2 + const CD3DX12_DESCRIPTOR_RANGE cubemapDescriptor(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 1); + const CD3DX12_DESCRIPTOR_RANGE cubemapSamplerDescriptor(D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, 1, 1); + rootParameters[RootParameterIndex::CubemapSRV].InitAsDescriptorTable(1, &cubemapDescriptor, D3D12_SHADER_VISIBILITY_PIXEL); + rootParameters[RootParameterIndex::CubemapSampler].InitAsDescriptorTable(1, &cubemapSamplerDescriptor, D3D12_SHADER_VISIBILITY_PIXEL); + + // Create the root signature + CD3DX12_ROOT_SIGNATURE_DESC rsigDesc = {}; + rsigDesc.Init(static_cast(std::size(rootParameters)), rootParameters, 0, nullptr, rootSignatureFlags); + + mRootSignature = GetRootSignature(0, rsigDesc); + } + + assert(mRootSignature != nullptr); + + fog.enabled = (effectFlags & EffectFlags::Fog) != 0; + + if (effectFlags & EffectFlags::VertexColor) + { + DebugTrace("ERROR: EnvironmentMapEffect does not implement EffectFlags::VertexColor\n"); + throw std::invalid_argument("VertexColor effect flag is invalid"); + } + else if (effectFlags & EffectFlags::Instancing) + { + DebugTrace("ERROR: EnvironmentMapEffect does not implement EffectFlags::Instancing\n"); + throw std::invalid_argument("Instancing effect flag is invalid"); + } + + constants.environmentMapAmount = 1; + constants.fresnelFactor = 1; + + XMVECTOR unwantedOutput[MaxDirectionalLights]; + + lights.InitializeConstants(unwantedOutput[0], constants.lightDirection, constants.lightDiffuseColor, unwantedOutput); + + // Create pipeline state. + const int sp = GetPipelineStatePermutation(mapping, effectFlags); + assert(sp >= 0 && sp < EnvironmentMapEffectTraits::ShaderPermutationCount); + _Analysis_assume_(sp >= 0 && sp < EnvironmentMapEffectTraits::ShaderPermutationCount); + + const int vi = EffectBase::VertexShaderIndices[sp]; + assert(vi >= 0 && vi < EnvironmentMapEffectTraits::VertexShaderCount); + _Analysis_assume_(vi >= 0 && vi < EnvironmentMapEffectTraits::VertexShaderCount); + const int pi = EffectBase::PixelShaderIndices[sp]; + assert(pi >= 0 && pi < EnvironmentMapEffectTraits::PixelShaderCount); + _Analysis_assume_(pi >= 0 && pi < EnvironmentMapEffectTraits::PixelShaderCount); + + pipelineDescription.CreatePipelineState( + device, + mRootSignature, + EffectBase::VertexShaderBytecode[vi], + EffectBase::PixelShaderBytecode[pi], + mPipelineState.GetAddressOf()); + + SetDebugObjectName(mPipelineState.Get(), L"EnvironmentMapEffect"); +} + + +int EnvironmentMapEffect::Impl::GetPipelineStatePermutation( + EnvironmentMapEffect::Mapping mapping, + uint32_t effectFlags) const noexcept +{ + const bool biasedVertexNormals = (effectFlags & EffectFlags::BiasedVertexNormals) != 0; + + int permutation = 0; + + // Use optimized shaders if fog is disabled. + if (!fog.enabled) + { + permutation += 1; + } + + // Support fresnel? + if (effectFlags & EffectFlags::Fresnel) + { + permutation += 2; + } + + if (mapping == Mapping_Sphere) + { + permutation += 24; + + if (biasedVertexNormals) + { + permutation += 4; + } + } + else if (mapping == Mapping_DualParabola) + { + permutation += 32; + + if (biasedVertexNormals) + { + permutation += 4; + } + } + else // Mapping_Cube + { + if (effectFlags & EffectFlags::PerPixelLightingBit) + { + permutation += 8; + } + else + { + if (effectFlags & EffectFlags::Specular) + { + permutation += 4; + } + } + + if (biasedVertexNormals) + { + // Compressed normals need to be scaled and biased in the vertex shader. + permutation += 12; + } + } + + return permutation; +} + + +// Sets our state onto the D3D device. +void EnvironmentMapEffect::Impl::Apply(_In_ ID3D12GraphicsCommandList* commandList) +{ + // Compute derived parameter values. + matrices.SetConstants(dirtyFlags, constants.worldViewProj); + + fog.SetConstants(dirtyFlags, matrices.worldView, constants.fogVector); + + lights.SetConstants(dirtyFlags, matrices, constants.world, constants.worldInverseTranspose, constants.eyePosition, constants.diffuseColor, constants.emissiveColor, true); + + UpdateConstants(); + + // Set the resources and state + commandList->SetGraphicsRootSignature(mRootSignature); + + // Set the textures + if (!texture.ptr || !environmentMap.ptr) + { + DebugTrace("ERROR: Missing texture(s) for EnvironmentMapEffect (texture %llu, environmentMap %llu)\n", texture.ptr, environmentMap.ptr); + throw std::runtime_error("EnvironmentMapEffect"); + } + if (!textureSampler.ptr || !environmentMapSampler.ptr) + { + DebugTrace("ERROR: Missing sampler(s) for EnvironmentMapEffect (sampler %llu, environmentMap %llu)\n", textureSampler.ptr, environmentMapSampler.ptr); + throw std::runtime_error("EnvironmentMapEffect"); + } + + // **NOTE** If D3D asserts or crashes here, you probably need to call commandList->SetDescriptorHeaps() with the required descriptor heaps. + commandList->SetGraphicsRootDescriptorTable(RootParameterIndex::TextureSRV, texture); + commandList->SetGraphicsRootDescriptorTable(RootParameterIndex::TextureSampler, textureSampler); + commandList->SetGraphicsRootDescriptorTable(RootParameterIndex::CubemapSRV, environmentMap); + commandList->SetGraphicsRootDescriptorTable(RootParameterIndex::CubemapSampler, environmentMapSampler); + + // Set constants + commandList->SetGraphicsRootConstantBufferView(RootParameterIndex::ConstantBuffer, GetConstantBufferGpuAddress()); + + // Set the pipeline state + commandList->SetPipelineState(EffectBase::mPipelineState.Get()); +} + + +// Public constructor. +EnvironmentMapEffect::EnvironmentMapEffect( + _In_ ID3D12Device* device, + uint32_t effectFlags, + const EffectPipelineStateDescription& pipelineDescription, + EnvironmentMapEffect::Mapping mapping) + : pImpl(std::make_unique(device, effectFlags, pipelineDescription, mapping)) +{ +} + + +EnvironmentMapEffect::EnvironmentMapEffect(EnvironmentMapEffect&&) noexcept = default; +EnvironmentMapEffect& EnvironmentMapEffect::operator= (EnvironmentMapEffect&&) noexcept = default; +EnvironmentMapEffect::~EnvironmentMapEffect() = default; + + +// IEffect methods. +void EnvironmentMapEffect::Apply( + _In_ ID3D12GraphicsCommandList* cmdList) +{ + pImpl->Apply(cmdList); +} + + +// Camera settings. +void XM_CALLCONV EnvironmentMapEffect::SetWorld(FXMMATRIX value) +{ + pImpl->matrices.world = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::WorldViewProj | EffectDirtyFlags::WorldInverseTranspose | EffectDirtyFlags::FogVector; +} + + +void XM_CALLCONV EnvironmentMapEffect::SetView(FXMMATRIX value) +{ + pImpl->matrices.view = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::WorldViewProj | EffectDirtyFlags::EyePosition | EffectDirtyFlags::FogVector; +} + + +void XM_CALLCONV EnvironmentMapEffect::SetProjection(FXMMATRIX value) +{ + pImpl->matrices.projection = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::WorldViewProj; +} + + +void XM_CALLCONV EnvironmentMapEffect::SetMatrices(FXMMATRIX world, CXMMATRIX view, CXMMATRIX projection) +{ + pImpl->matrices.world = world; + pImpl->matrices.view = view; + pImpl->matrices.projection = projection; + + pImpl->dirtyFlags |= EffectDirtyFlags::WorldViewProj | EffectDirtyFlags::WorldInverseTranspose | EffectDirtyFlags::EyePosition | EffectDirtyFlags::FogVector; +} + + +// Material settings. +void XM_CALLCONV EnvironmentMapEffect::SetDiffuseColor(FXMVECTOR value) +{ + pImpl->lights.diffuseColor = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::MaterialColor; +} + + +void XM_CALLCONV EnvironmentMapEffect::SetEmissiveColor(FXMVECTOR value) +{ + pImpl->lights.emissiveColor = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::MaterialColor; +} + + +void EnvironmentMapEffect::SetAlpha(float value) +{ + pImpl->lights.alpha = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::MaterialColor; +} + + +void XM_CALLCONV EnvironmentMapEffect::SetColorAndAlpha(FXMVECTOR value) +{ + pImpl->lights.diffuseColor = value; + pImpl->lights.alpha = XMVectorGetW(value); + + pImpl->dirtyFlags |= EffectDirtyFlags::MaterialColor; +} + + +// Light settings. +void XM_CALLCONV EnvironmentMapEffect::SetAmbientLightColor(FXMVECTOR value) +{ + pImpl->lights.ambientLightColor = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::MaterialColor; +} + + +void EnvironmentMapEffect::SetLightEnabled(int whichLight, bool value) +{ + XMVECTOR unwantedOutput[MaxDirectionalLights] = {}; + + pImpl->dirtyFlags |= pImpl->lights.SetLightEnabled(whichLight, value, pImpl->constants.lightDiffuseColor, unwantedOutput); +} + + +void XM_CALLCONV EnvironmentMapEffect::SetLightDirection(int whichLight, FXMVECTOR value) +{ + EffectLights::ValidateLightIndex(whichLight); + + pImpl->constants.lightDirection[whichLight] = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; +} + + +void XM_CALLCONV EnvironmentMapEffect::SetLightDiffuseColor(int whichLight, FXMVECTOR value) +{ + pImpl->dirtyFlags |= pImpl->lights.SetLightDiffuseColor(whichLight, value, pImpl->constants.lightDiffuseColor); +} + + +void XM_CALLCONV EnvironmentMapEffect::SetLightSpecularColor(int, FXMVECTOR) +{ + // Unsupported interface method. +} + + +void EnvironmentMapEffect::EnableDefaultLighting() +{ + EffectLights::EnableDefaultLighting(this); +} + + +// Fog settings. +void EnvironmentMapEffect::SetFogStart(float value) +{ + pImpl->fog.start = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::FogVector; +} + + +void EnvironmentMapEffect::SetFogEnd(float value) +{ + pImpl->fog.end = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::FogVector; +} + + +void XM_CALLCONV EnvironmentMapEffect::SetFogColor(FXMVECTOR value) +{ + pImpl->constants.fogColor = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; +} + + +// Texture settings. +void EnvironmentMapEffect::SetTexture(D3D12_GPU_DESCRIPTOR_HANDLE texture, D3D12_GPU_DESCRIPTOR_HANDLE sampler) +{ + pImpl->texture = texture; + pImpl->textureSampler = sampler; +} + + +void EnvironmentMapEffect::SetEnvironmentMap(D3D12_GPU_DESCRIPTOR_HANDLE texture, D3D12_GPU_DESCRIPTOR_HANDLE sampler) +{ + pImpl->environmentMap = texture; + pImpl->environmentMapSampler = sampler; +} + + +// Additional settings. +void EnvironmentMapEffect::SetEnvironmentMapAmount(float value) +{ + pImpl->constants.environmentMapAmount = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; +} + + +void XM_CALLCONV EnvironmentMapEffect::SetEnvironmentMapSpecular(FXMVECTOR value) +{ + pImpl->constants.environmentMapSpecular = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; +} + + +void EnvironmentMapEffect::SetFresnelFactor(float value) +{ + pImpl->constants.fresnelFactor = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; +} diff --git a/Common/DirectXTK12/Src/GamePad.cpp b/Common/DirectXTK12/Src/GamePad.cpp new file mode 100644 index 0000000..8769892 --- /dev/null +++ b/Common/DirectXTK12/Src/GamePad.cpp @@ -0,0 +1,1780 @@ +//-------------------------------------------------------------------------------------- +// File: GamePad.cpp +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248929 +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "pch.h" + +#include "GamePad.h" +#include "PlatformHelpers.h" + +using namespace DirectX; +using Microsoft::WRL::ComPtr; + + +namespace +{ + constexpr float c_XboxOneThumbDeadZone = .24f; // Recommended Xbox One controller deadzone + + float ApplyLinearDeadZone(float value, float maxValue, float deadZoneSize) noexcept + { + if (value < -deadZoneSize) + { + // Increase negative values to remove the deadzone discontinuity. + value += deadZoneSize; + } + else if (value > deadZoneSize) + { + // Decrease positive values to remove the deadzone discontinuity. + value -= deadZoneSize; + } + else + { + // Values inside the deadzone come out zero. + return 0; + } + + // Scale into 0-1 range. + const float scaledValue = value / (maxValue - deadZoneSize); + return std::max(-1.f, std::min(scaledValue, 1.f)); + } + + void ApplyStickDeadZone( + float x, + float y, + GamePad::DeadZone deadZoneMode, + float maxValue, + float deadZoneSize, + _Out_ float& resultX, + _Out_ float& resultY) noexcept + { + switch (deadZoneMode) + { + case GamePad::DEAD_ZONE_INDEPENDENT_AXES: + resultX = ApplyLinearDeadZone(x, maxValue, deadZoneSize); + resultY = ApplyLinearDeadZone(y, maxValue, deadZoneSize); + break; + + case GamePad::DEAD_ZONE_CIRCULAR: + { + const float dist = sqrtf(x*x + y * y); + const float wanted = ApplyLinearDeadZone(dist, maxValue, deadZoneSize); + + const float scale = (wanted > 0.f) ? (wanted / dist) : 0.f; + + resultX = std::max(-1.f, std::min(x * scale, 1.f)); + resultY = std::max(-1.f, std::min(y * scale, 1.f)); + } + break; + + default: // GamePad::DEAD_ZONE_NONE + resultX = ApplyLinearDeadZone(x, maxValue, 0); + resultY = ApplyLinearDeadZone(y, maxValue, 0); + break; + } + } +} + + +#pragma region Implementations +#ifdef USING_GAMEINPUT + +//====================================================================================== +// GameInput +//====================================================================================== + +class GamePad::Impl +{ +public: + Impl(GamePad* owner) : + mOwner(owner), + mCtrlChanged(INVALID_HANDLE_VALUE), + mDeviceToken(0), + mMostRecentGamepad(0) + { + if (s_gamePad) + { + throw std::logic_error("GamePad is a singleton"); + } + + s_gamePad = this; + + HRESULT hr = GameInputCreate(mGameInput.GetAddressOf()); + if (SUCCEEDED(hr)) + { + ThrowIfFailed(mGameInput->RegisterDeviceCallback( + nullptr, + GameInputKindGamepad, + GameInputDeviceConnected, + GameInputBlockingEnumeration, + this, + OnGameInputDevice, + &mDeviceToken)); + } + else + { + DebugTrace("ERROR: GameInputCreate [gamepad] failed with %08X\n", static_cast(hr)); + #ifdef _GAMING_XBOX + ThrowIfFailed(hr); + #elif defined(_DEBUG) + DebugTrace( + "\t**** Check that the 'GameInput Service' is running on this system. ****\n" + "\t**** NOTE: All calls to GetState will be reported as 'not connected'. ****\n"); + #endif + } + } + + Impl(Impl&&) = default; + Impl& operator= (Impl&&) = default; + + Impl(Impl const&) = delete; + Impl& operator= (Impl const&) = delete; + + ~Impl() + { + if (mDeviceToken) + { + if (mGameInput) + { + if (!mGameInput->UnregisterCallback(mDeviceToken, UINT64_MAX)) + { + DebugTrace("ERROR: GameInput::UnregisterCallback [gamepad] failed"); + } + } + + mDeviceToken = 0; + } + + s_gamePad = nullptr; + } + + void GetState(int player, _Out_ State& state, DeadZone deadZoneMode) + { + memset(&state, 0, sizeof(State)); + + IGameInputDevice* device = nullptr; + + if (player >= 0 && player < MAX_PLAYER_COUNT) + { + device = mInputDevices[player].Get(); + if (!device) + return; + + assert(mGameInput != nullptr); + } + else if (player == c_MostRecent) + { + player = mMostRecentGamepad; + assert(player >= 0 && player < MAX_PLAYER_COUNT); + device = mInputDevices[player].Get(); + if (!device) + return; + + assert(mGameInput != nullptr); + } + else if (player != c_MergedInput || !mGameInput) + { + return; + } + + ComPtr reading; + if (SUCCEEDED(mGameInput->GetCurrentReading(GameInputKindGamepad, device, reading.GetAddressOf()))) + { + GameInputGamepadState pad; + if (reading->GetGamepadState(&pad)) + { + state.connected = true; + state.packet = reading->GetSequenceNumber(GameInputKindGamepad); + + state.buttons.a = (pad.buttons & GameInputGamepadA) != 0; + state.buttons.b = (pad.buttons & GameInputGamepadB) != 0; + state.buttons.x = (pad.buttons & GameInputGamepadX) != 0; + state.buttons.y = (pad.buttons & GameInputGamepadY) != 0; + state.buttons.leftStick = (pad.buttons & GameInputGamepadLeftThumbstick) != 0; + state.buttons.rightStick = (pad.buttons & GameInputGamepadRightThumbstick) != 0; + state.buttons.leftShoulder = (pad.buttons & GameInputGamepadLeftShoulder) != 0; + state.buttons.rightShoulder = (pad.buttons & GameInputGamepadRightShoulder) != 0; + state.buttons.view = (pad.buttons & GameInputGamepadView) != 0; + state.buttons.menu = (pad.buttons & GameInputGamepadMenu) != 0; + + state.dpad.up = (pad.buttons & GameInputGamepadDPadUp) != 0; + state.dpad.down = (pad.buttons & GameInputGamepadDPadDown) != 0; + state.dpad.right = (pad.buttons & GameInputGamepadDPadRight) != 0; + state.dpad.left = (pad.buttons & GameInputGamepadDPadLeft) != 0; + + ApplyStickDeadZone(pad.leftThumbstickX, pad.leftThumbstickY, + deadZoneMode, 1.f, c_XboxOneThumbDeadZone, + state.thumbSticks.leftX, state.thumbSticks.leftY); + + ApplyStickDeadZone(pad.rightThumbstickX, pad.rightThumbstickY, + deadZoneMode, 1.f, c_XboxOneThumbDeadZone, + state.thumbSticks.rightX, state.thumbSticks.rightY); + + state.triggers.left = pad.leftTrigger; + state.triggers.right = pad.rightTrigger; + } + } + } + + void GetCapabilities(int player, _Out_ Capabilities& caps) + { + if (player == c_MostRecent) + player = mMostRecentGamepad; + + if (player >= 0 && player < MAX_PLAYER_COUNT) + { + IGameInputDevice* device = mInputDevices[player].Get(); + if (device) + { + if (device->GetDeviceStatus() & GameInputDeviceConnected) + { + auto deviceInfo = device->GetDeviceInfo(); + caps.connected = true; + caps.gamepadType = Capabilities::GAMEPAD; + caps.id = deviceInfo->deviceId; + caps.vid = deviceInfo->vendorId; + caps.pid = deviceInfo->productId; + return; + } + else + { + mInputDevices[player].Reset(); + } + } + } + + memset(&caps, 0, sizeof(Capabilities)); + } + + bool SetVibration(int player, float leftMotor, float rightMotor, float leftTrigger, float rightTrigger) noexcept + { + if (player == c_MostRecent) + player = mMostRecentGamepad; + + if (player >= 0 && player < MAX_PLAYER_COUNT) + { + IGameInputDevice* device = mInputDevices[player].Get(); + if (device) + { + GameInputRumbleParams const params = + { + leftMotor, + rightMotor, + leftTrigger, + rightTrigger + }; + + device->SetRumbleState(¶ms); + return true; + } + } + + return false; + } + + void Suspend() noexcept + { + for (int player = 0; player < MAX_PLAYER_COUNT; ++player) + { + IGameInputDevice* device = mInputDevices[player].Get(); + if (device) + { + device->SetRumbleState(nullptr); + } + } + } + + void Resume() noexcept + { + for (int player = 0; player < MAX_PLAYER_COUNT; ++player) + { + IGameInputDevice* device = mInputDevices[player].Get(); + if (device) + { + if (!(device->GetDeviceStatus() & GameInputDeviceConnected)) + { + mInputDevices[player].Reset(); + } + } + } + } + + _Success_(return) + bool GetDevice(int player, _Outptr_ IGameInputDevice** device) noexcept + { + if (!device) + return false; + + if (player == c_MostRecent) + player = mMostRecentGamepad; + + *device = nullptr; + + if (player >= 0 && player < MAX_PLAYER_COUNT) + { + IGameInputDevice* dev = mInputDevices[player].Get(); + if (dev) + { + dev->AddRef(); + *device = dev; + return true; + } + } + + return false; + } + + GamePad* mOwner; + + static GamePad::Impl* s_gamePad; + + HANDLE mCtrlChanged; + +private: + ComPtr mGameInput; + ComPtr mInputDevices[MAX_PLAYER_COUNT]; + + GameInputCallbackToken mDeviceToken; + + int mMostRecentGamepad; + + static void CALLBACK OnGameInputDevice( + _In_ GameInputCallbackToken, + _In_ void * context, + _In_ IGameInputDevice * device, + _In_ uint64_t, + _In_ GameInputDeviceStatus currentStatus, + _In_ GameInputDeviceStatus) noexcept + { + auto impl = reinterpret_cast(context); + + if (currentStatus & GameInputDeviceConnected) + { + size_t empty = MAX_PLAYER_COUNT; + size_t k = 0; + for (; k < MAX_PLAYER_COUNT; ++k) + { + if (impl->mInputDevices[k].Get() == device) + { + impl->mMostRecentGamepad = static_cast(k); + break; + } + else if (!impl->mInputDevices[k]) + { + if (empty >= MAX_PLAYER_COUNT) + empty = k; + } + } + + if (k >= MAX_PLAYER_COUNT) + { + // Silently ignore "extra" gamepads as there's no hard limit + if (empty < MAX_PLAYER_COUNT) + { + impl->mInputDevices[empty] = device; + impl->mMostRecentGamepad = static_cast(empty); + } + } + } + else + { + for (size_t k = 0; k < MAX_PLAYER_COUNT; ++k) + { + if (impl->mInputDevices[k].Get() == device) + { + impl->mInputDevices[k].Reset(); + break; + } + } + } + + if (impl->mCtrlChanged != INVALID_HANDLE_VALUE) + { + SetEvent(impl->mCtrlChanged); + } + } +}; + +GamePad::Impl* GamePad::Impl::s_gamePad = nullptr; + +void GamePad::RegisterEvents(HANDLE ctrlChanged) noexcept +{ + pImpl->mCtrlChanged = (!ctrlChanged) ? INVALID_HANDLE_VALUE : ctrlChanged; +} + +_Success_(return) +bool GamePad::GetDevice(int player, _Outptr_ IGameInputDevice * *device) noexcept +{ + return pImpl->GetDevice(player, device); +} + + +#elif defined(USING_WINDOWS_GAMING_INPUT) + +//====================================================================================== +// Windows::Gaming::Input (Windows 10) +//====================================================================================== + +#pragma warning(push) +#pragma warning(disable : 4471 5204 5256) +#include +#pragma warning(pop) + +class GamePad::Impl +{ +public: + Impl(GamePad* owner) : + mOwner(owner), + mCtrlChanged(INVALID_HANDLE_VALUE), + mUserChanged(INVALID_HANDLE_VALUE), + mMostRecentGamepad(0), + mStatics{}, + mGamePad{}, + mUserChangeToken{}, + mAddedToken{}, + mRemovedToken{}, + mChanged{} + { + using namespace Microsoft::WRL; + using namespace Microsoft::WRL::Wrappers; + using namespace ABI::Windows::Foundation; + + if (s_gamePad) + { + throw std::logic_error("GamePad is a singleton"); + } + + s_gamePad = this; + + mChanged.reset(CreateEventEx(nullptr, nullptr, 0, EVENT_MODIFY_STATE | SYNCHRONIZE)); + if (!mChanged) + { + throw std::system_error(std::error_code(static_cast(GetLastError()), std::system_category()), "CreateEventEx"); + } + + ThrowIfFailed(GetActivationFactory(HStringReference(RuntimeClass_Windows_Gaming_Input_Gamepad).Get(), mStatics.GetAddressOf())); + + typedef __FIEventHandler_1_Windows__CGaming__CInput__CGamepad AddedHandler; + ThrowIfFailed(mStatics->add_GamepadAdded(Callback(GamepadAdded).Get(), &mAddedToken)); + + typedef __FIEventHandler_1_Windows__CGaming__CInput__CGamepad RemovedHandler; + ThrowIfFailed(mStatics->add_GamepadRemoved(Callback(GamepadRemoved).Get(), &mRemovedToken)); + + ScanGamePads(); + } + + Impl(Impl&&) = default; + Impl& operator= (Impl&&) = default; + + Impl(Impl const&) = delete; + Impl& operator= (Impl const&) = delete; + + ~Impl() + { + using namespace ABI::Windows::Gaming::Input; + + for (size_t j = 0; j < MAX_PLAYER_COUNT; ++j) + { + if (mGamePad[j]) + { + ComPtr ctrl; + HRESULT hr = mGamePad[j].As(&ctrl); + if (SUCCEEDED(hr) && ctrl) + { + std::ignore = ctrl->remove_UserChanged(mUserChangeToken[j]); + mUserChangeToken[j].value = 0; + } + + mGamePad[j].Reset(); + } + } + + if (mStatics) + { + std::ignore = mStatics->remove_GamepadAdded(mAddedToken); + mAddedToken.value = 0; + + std::ignore = mStatics->remove_GamepadRemoved(mRemovedToken); + mRemovedToken.value = 0; + + mStatics.Reset(); + } + + s_gamePad = nullptr; + } + + void GetState(int player, _Out_ State& state, DeadZone deadZoneMode) + { + using namespace Microsoft::WRL; + using namespace ABI::Windows::Gaming::Input; + + if (WaitForSingleObjectEx(mChanged.get(), 0, FALSE) == WAIT_OBJECT_0) + { + ScanGamePads(); + } + + if (player == c_MostRecent) + player = mMostRecentGamepad; + + if ((player >= 0) && (player < MAX_PLAYER_COUNT)) + { + if (mGamePad[player]) + { + GamepadReading reading; + HRESULT hr = mGamePad[player]->GetCurrentReading(&reading); + if (SUCCEEDED(hr)) + { + state.connected = true; + state.packet = reading.Timestamp; + + state.buttons.a = (reading.Buttons & GamepadButtons::GamepadButtons_A) != 0; + state.buttons.b = (reading.Buttons & GamepadButtons::GamepadButtons_B) != 0; + state.buttons.x = (reading.Buttons & GamepadButtons::GamepadButtons_X) != 0; + state.buttons.y = (reading.Buttons & GamepadButtons::GamepadButtons_Y) != 0; + + state.buttons.leftStick = (reading.Buttons & GamepadButtons::GamepadButtons_LeftThumbstick) != 0; + state.buttons.rightStick = (reading.Buttons & GamepadButtons::GamepadButtons_RightThumbstick) != 0; + + state.buttons.leftShoulder = (reading.Buttons & GamepadButtons::GamepadButtons_LeftShoulder) != 0; + state.buttons.rightShoulder = (reading.Buttons & GamepadButtons::GamepadButtons_RightShoulder) != 0; + + state.buttons.back = (reading.Buttons & GamepadButtons::GamepadButtons_View) != 0; + state.buttons.start = (reading.Buttons & GamepadButtons::GamepadButtons_Menu) != 0; + + state.dpad.up = (reading.Buttons & GamepadButtons::GamepadButtons_DPadUp) != 0; + state.dpad.down = (reading.Buttons & GamepadButtons::GamepadButtons_DPadDown) != 0; + state.dpad.right = (reading.Buttons & GamepadButtons::GamepadButtons_DPadRight) != 0; + state.dpad.left = (reading.Buttons & GamepadButtons::GamepadButtons_DPadLeft) != 0; + + ApplyStickDeadZone(static_cast(reading.LeftThumbstickX), static_cast(reading.LeftThumbstickY), + deadZoneMode, 1.f, c_XboxOneThumbDeadZone, + state.thumbSticks.leftX, state.thumbSticks.leftY); + + ApplyStickDeadZone(static_cast(reading.RightThumbstickX), static_cast(reading.RightThumbstickY), + deadZoneMode, 1.f, c_XboxOneThumbDeadZone, + state.thumbSticks.rightX, state.thumbSticks.rightY); + + state.triggers.left = static_cast(reading.LeftTrigger); + state.triggers.right = static_cast(reading.RightTrigger); + + return; + } + } + } + + memset(&state, 0, sizeof(State)); + } + + void GetCapabilities(int player, Capabilities& caps) + { + using namespace Microsoft::WRL; + using namespace Microsoft::WRL::Wrappers; + using namespace ABI::Windows::Foundation; + using namespace ABI::Windows::System; + using namespace ABI::Windows::Gaming::Input; + + if (WaitForSingleObjectEx(mChanged.get(), 0, FALSE) == WAIT_OBJECT_0) + { + ScanGamePads(); + } + + if (player == c_MostRecent) + player = mMostRecentGamepad; + + if ((player >= 0) && (player < MAX_PLAYER_COUNT)) + { + if (mGamePad[player]) + { + caps.connected = true; + caps.gamepadType = Capabilities::GAMEPAD; + caps.id.clear(); + caps.vid = caps.pid = 0; + + ComPtr ctrl; + HRESULT hr = mGamePad[player].As(&ctrl); + if (SUCCEEDED(hr) && ctrl) + { + ComPtr user; + hr = ctrl->get_User(user.GetAddressOf()); + if (SUCCEEDED(hr) && user != nullptr) + { + HString str; + hr = user->get_NonRoamableId(str.GetAddressOf()); + if (SUCCEEDED(hr)) + { + caps.id = str.GetRawBuffer(nullptr); + } + } + + // Requires the Windows 10 Creators Update SDK (15063) + #if defined(NTDDI_WIN10_RS2) && (NTDDI_VERSION >= NTDDI_WIN10_RS2) + ComPtr rawStatics; + hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_Gaming_Input_RawGameController).Get(), rawStatics.GetAddressOf()); + if (SUCCEEDED(hr)) + { + ComPtr raw; + hr = rawStatics->FromGameController(ctrl.Get(), raw.GetAddressOf()); + if (SUCCEEDED(hr) && raw) + { + if (FAILED(raw->get_HardwareVendorId(&caps.vid))) + caps.vid = 0; + + if (FAILED(raw->get_HardwareProductId(&caps.pid))) + caps.pid = 0; + } + } + #endif // NTDDI_WIN10_RS2 + } + return; + } + } + + caps.id.clear(); + caps = {}; + } + + bool SetVibration(int player, float leftMotor, float rightMotor, float leftTrigger, float rightTrigger) noexcept + { + using namespace ABI::Windows::Gaming::Input; + + if (player == c_MostRecent) + player = mMostRecentGamepad; + + if ((player >= 0) && (player < MAX_PLAYER_COUNT)) + { + if (mGamePad[player]) + { + GamepadVibration vib; + vib.LeftMotor = double(leftMotor); + vib.RightMotor = double(rightMotor); + vib.LeftTrigger = double(leftTrigger); + vib.RightTrigger = double(rightTrigger); + HRESULT hr = mGamePad[player]->put_Vibration(vib); + + if (SUCCEEDED(hr)) + return true; + } + } + + return false; + } + + void Suspend() noexcept + { + for (size_t j = 0; j < MAX_PLAYER_COUNT; ++j) + { + mGamePad[j].Reset(); + } + } + + void Resume() noexcept + { + // Make sure we rescan gamepads + SetEvent(mChanged.get()); + } + + GamePad* mOwner; + + static GamePad::Impl* s_gamePad; + + HANDLE mCtrlChanged; + HANDLE mUserChanged; + +private: + int mMostRecentGamepad; + + void ScanGamePads() + { + using namespace Microsoft::WRL; + using namespace ABI::Windows::Foundation::Collections; + using namespace ABI::Windows::Gaming::Input; + + ComPtr> pads; + ThrowIfFailed(mStatics->get_Gamepads(pads.GetAddressOf())); + + unsigned int count = 0; + ThrowIfFailed(pads->get_Size(&count)); + + // Check for removed gamepads + for (size_t j = 0; j < MAX_PLAYER_COUNT; ++j) + { + if (mGamePad[j]) + { + unsigned int k = 0; + for (; k < count; ++k) + { + ComPtr pad; + HRESULT hr = pads->GetAt(k, pad.GetAddressOf()); + if (SUCCEEDED(hr) && (pad == mGamePad[j])) + { + break; + } + } + + if (k >= count) + { + ComPtr ctrl; + HRESULT hr = mGamePad[j].As(&ctrl); + if (SUCCEEDED(hr) && ctrl) + { + std::ignore = ctrl->remove_UserChanged(mUserChangeToken[j]); + mUserChangeToken[j].value = 0; + } + + mGamePad[j].Reset(); + } + } + } + + // Check for added gamepads + for (unsigned int j = 0; j < count; ++j) + { + ComPtr pad; + HRESULT hr = pads->GetAt(j, pad.GetAddressOf()); + if (SUCCEEDED(hr)) + { + size_t empty = MAX_PLAYER_COUNT; + size_t k = 0; + for (; k < MAX_PLAYER_COUNT; ++k) + { + if (mGamePad[k] == pad) + { + if (j == (count - 1)) + mMostRecentGamepad = static_cast(k); + break; + } + else if (!mGamePad[k]) + { + if (empty >= MAX_PLAYER_COUNT) + empty = k; + } + } + + if (k >= MAX_PLAYER_COUNT) + { + // Silently ignore "extra" gamepads as there's no hard limit + if (empty < MAX_PLAYER_COUNT) + { + mGamePad[empty] = pad; + if (j == (count - 1)) + mMostRecentGamepad = static_cast(empty); + + ComPtr ctrl; + hr = pad.As(&ctrl); + if (SUCCEEDED(hr) && ctrl) + { + typedef __FITypedEventHandler_2_Windows__CGaming__CInput__CIGameController_Windows__CSystem__CUserChangedEventArgs UserHandler; + ThrowIfFailed(ctrl->add_UserChanged(Callback(UserChanged).Get(), &mUserChangeToken[empty])); + } + } + } + } + } + } + + ComPtr mStatics; + ComPtr mGamePad[MAX_PLAYER_COUNT]; + EventRegistrationToken mUserChangeToken[MAX_PLAYER_COUNT]; + + EventRegistrationToken mAddedToken; + EventRegistrationToken mRemovedToken; + + ScopedHandle mChanged; + + static HRESULT GamepadAdded(IInspectable *, ABI::Windows::Gaming::Input::IGamepad*) + { + if (s_gamePad) + { + SetEvent(s_gamePad->mChanged.get()); + + if (s_gamePad->mCtrlChanged != INVALID_HANDLE_VALUE) + { + SetEvent(s_gamePad->mCtrlChanged); + } + } + return S_OK; + } + + static HRESULT GamepadRemoved(IInspectable *, ABI::Windows::Gaming::Input::IGamepad*) + { + if (s_gamePad) + { + SetEvent(s_gamePad->mChanged.get()); + + if (s_gamePad->mCtrlChanged != INVALID_HANDLE_VALUE) + { + SetEvent(s_gamePad->mCtrlChanged); + } + } + return S_OK; + } + + static HRESULT UserChanged(ABI::Windows::Gaming::Input::IGameController*, ABI::Windows::System::IUserChangedEventArgs*) + { + if (s_gamePad) + { + if (s_gamePad->mUserChanged != INVALID_HANDLE_VALUE) + { + SetEvent(s_gamePad->mUserChanged); + } + } + return S_OK; + } +}; + +GamePad::Impl* GamePad::Impl::s_gamePad = nullptr; + +void GamePad::RegisterEvents(HANDLE ctrlChanged, HANDLE userChanged) noexcept +{ + pImpl->mCtrlChanged = (!ctrlChanged) ? INVALID_HANDLE_VALUE : ctrlChanged; + pImpl->mUserChanged = (!userChanged) ? INVALID_HANDLE_VALUE : userChanged; +} + + +#elif defined(_XBOX_ONE) + +//====================================================================================== +// Windows::Xbox::Input (Xbox One XDK) +//====================================================================================== + +#include + +#include + +class GamePad::Impl +{ +public: + class GamepadAddedListener : public Microsoft::WRL::RuntimeClass, + ABI::Windows::Foundation::IEventHandler, + Microsoft::WRL::FtmBase> + { + public: + GamepadAddedListener(HANDLE event) : mEvent(event) {} + + STDMETHOD(Invoke)(_In_ IInspectable *, _In_ ABI::Windows::Xbox::Input::IGamepadAddedEventArgs *) override + { + SetEvent(mEvent); + + auto pad = GamePad::Impl::s_gamePad; + + if (pad && pad->mCtrlChanged != INVALID_HANDLE_VALUE) + { + SetEvent(pad->mCtrlChanged); + } + return S_OK; + } + + private: + HANDLE mEvent; + }; + + class GamepadRemovedListener : public Microsoft::WRL::RuntimeClass, + ABI::Windows::Foundation::IEventHandler, + Microsoft::WRL::FtmBase> + { + public: + GamepadRemovedListener(HANDLE event) : mEvent(event) {} + + STDMETHOD(Invoke)(_In_ IInspectable *, _In_ ABI::Windows::Xbox::Input::IGamepadRemovedEventArgs *) override + { + SetEvent(mEvent); + + auto pad = GamePad::Impl::s_gamePad; + + if (pad && pad->mCtrlChanged != INVALID_HANDLE_VALUE) + { + SetEvent(pad->mCtrlChanged); + } + return S_OK; + } + + private: + HANDLE mEvent; + }; + + class UserPairingListener : public Microsoft::WRL::RuntimeClass, + ABI::Windows::Foundation::IEventHandler, + Microsoft::WRL::FtmBase> + { + public: + UserPairingListener() noexcept {} + + STDMETHOD(Invoke)(_In_ IInspectable *, _In_ ABI::Windows::Xbox::Input::IControllerPairingChangedEventArgs *) override + { + auto pad = GamePad::Impl::s_gamePad; + + if (pad && pad->mUserChanged != INVALID_HANDLE_VALUE) + { + SetEvent(pad->mUserChanged); + } + return S_OK; + } + }; + + Impl(GamePad *owner) : + mOwner(owner), + mCtrlChanged(INVALID_HANDLE_VALUE), + mUserChanged(INVALID_HANDLE_VALUE), + mMostRecentGamepad(0), + mStatics{}, + mStaticsCtrl{}, + mGamePad{}, + mAddedToken{}, + mRemovedToken{}, + mUserParingToken{}, + mChanged{} + { + using namespace Microsoft::WRL; + using namespace Microsoft::WRL::Wrappers; + using namespace ABI::Windows::Foundation; + + if (s_gamePad) + { + throw std::logic_error("GamePad is a singleton"); + } + + s_gamePad = this; + + mChanged.reset(CreateEventEx(nullptr, nullptr, 0, EVENT_MODIFY_STATE | SYNCHRONIZE)); + if (!mChanged) + { + throw std::system_error(std::error_code(static_cast(GetLastError()), std::system_category()), "CreateEventEx"); + } + + ThrowIfFailed(GetActivationFactory(HStringReference(RuntimeClass_Windows_Xbox_Input_Gamepad).Get(), mStatics.GetAddressOf())); + + ThrowIfFailed(GetActivationFactory(HStringReference(RuntimeClass_Windows_Xbox_Input_Controller).Get(), mStaticsCtrl.GetAddressOf())); + + ThrowIfFailed(mStatics->add_GamepadAdded(Make(mChanged.get()).Get(), &mAddedToken)); + + ThrowIfFailed(mStatics->add_GamepadRemoved(Make(mChanged.get()).Get(), &mRemovedToken)); + + ThrowIfFailed(mStaticsCtrl->add_ControllerPairingChanged(Make().Get(), &mUserParingToken)); + + ScanGamePads(); + } + + ~Impl() + { + if (mStatics) + { + std::ignore = mStatics->remove_GamepadAdded(mAddedToken); + mAddedToken.value = 0; + + std::ignore = mStatics->remove_GamepadRemoved(mRemovedToken); + mRemovedToken.value = 0; + + mStatics.Reset(); + } + + if (mStaticsCtrl) + { + std::ignore = mStaticsCtrl->remove_ControllerPairingChanged(mUserParingToken); + mUserParingToken.value = 0; + + mStaticsCtrl.Reset(); + } + + s_gamePad = nullptr; + } + + void GetState(int player, _Out_ State& state, DeadZone deadZoneMode) + { + using namespace Microsoft::WRL; + using namespace ABI::Windows::Xbox::Input; + + if (WaitForSingleObjectEx(mChanged.get(), 0, FALSE) == WAIT_OBJECT_0) + { + ScanGamePads(); + } + + if (player == c_MostRecent) + player = mMostRecentGamepad; + + if ((player >= 0) && (player < MAX_PLAYER_COUNT)) + { + if (mGamePad[player]) + { + RawGamepadReading reading; + HRESULT hr = mGamePad[player]->GetRawCurrentReading(&reading); + if (SUCCEEDED(hr)) + { + state.connected = true; + state.packet = reading.Timestamp; + + state.buttons.a = (reading.Buttons & GamepadButtons::GamepadButtons_A) != 0; + state.buttons.b = (reading.Buttons & GamepadButtons::GamepadButtons_B) != 0; + state.buttons.x = (reading.Buttons & GamepadButtons::GamepadButtons_X) != 0; + state.buttons.y = (reading.Buttons & GamepadButtons::GamepadButtons_Y) != 0; + + state.buttons.leftStick = (reading.Buttons & GamepadButtons::GamepadButtons_LeftThumbstick) != 0; + state.buttons.rightStick = (reading.Buttons & GamepadButtons::GamepadButtons_RightThumbstick) != 0; + + state.buttons.leftShoulder = (reading.Buttons & GamepadButtons::GamepadButtons_LeftShoulder) != 0; + state.buttons.rightShoulder = (reading.Buttons & GamepadButtons::GamepadButtons_RightShoulder) != 0; + + state.buttons.back = (reading.Buttons & GamepadButtons::GamepadButtons_View) != 0; + state.buttons.start = (reading.Buttons & GamepadButtons::GamepadButtons_Menu) != 0; + + state.dpad.up = (reading.Buttons & GamepadButtons::GamepadButtons_DPadUp) != 0; + state.dpad.down = (reading.Buttons & GamepadButtons::GamepadButtons_DPadDown) != 0; + state.dpad.right = (reading.Buttons & GamepadButtons::GamepadButtons_DPadRight) != 0; + state.dpad.left = (reading.Buttons & GamepadButtons::GamepadButtons_DPadLeft) != 0; + + ApplyStickDeadZone(reading.LeftThumbstickX, reading.LeftThumbstickY, + deadZoneMode, 1.f, c_XboxOneThumbDeadZone, + state.thumbSticks.leftX, state.thumbSticks.leftY); + + ApplyStickDeadZone(reading.RightThumbstickX, reading.RightThumbstickY, + deadZoneMode, 1.f, c_XboxOneThumbDeadZone, + state.thumbSticks.rightX, state.thumbSticks.rightY); + + state.triggers.left = reading.LeftTrigger; + state.triggers.right = reading.RightTrigger; + + return; + } + } + } + + memset(&state, 0, sizeof(State)); + } + + void GetCapabilities(int player, _Out_ Capabilities& caps) + { + using namespace Microsoft::WRL; + using namespace ABI::Windows::Xbox::Input; + + if (WaitForSingleObjectEx(mChanged.get(), 0, FALSE) == WAIT_OBJECT_0) + { + ScanGamePads(); + } + + if (player == c_MostRecent) + player = mMostRecentGamepad; + + if ((player >= 0) && (player < MAX_PLAYER_COUNT)) + { + if (mGamePad[player]) + { + caps.connected = true; + caps.gamepadType = Capabilities::UNKNOWN; + caps.id = 0; + caps.vid = caps.pid = 0; + + ComPtr ctrl; + HRESULT hr = mGamePad[player].As(&ctrl); + if (SUCCEEDED(hr) && ctrl) + { + hr = ctrl->get_Id(&caps.id); + if (FAILED(hr)) + caps.id = 0; + + Wrappers::HString str; + hr = ctrl->get_Type(str.GetAddressOf()); + if (SUCCEEDED(hr)) + { + const wchar_t* typeStr = str.GetRawBuffer(nullptr); + if (_wcsicmp(typeStr, L"Windows.Xbox.Input.Gamepad") == 0) + { + caps.gamepadType = Capabilities::GAMEPAD; + } + else if (_wcsicmp(typeStr, L"Microsoft.Xbox.Input.ArcadeStick") == 0) + { + caps.gamepadType = Capabilities::ARCADE_STICK; + } + else if (_wcsicmp(typeStr, L"Microsoft.Xbox.Input.Wheel") == 0) + { + caps.gamepadType = Capabilities::WHEEL; + } + } + } + + ComPtr ctrl3; + hr = mGamePad[player].As(&ctrl3); + if (SUCCEEDED(hr) && ctrl3) + { + if (FAILED(ctrl3->get_HardwareVendorId(&caps.vid))) + caps.vid = 0; + + if (FAILED(ctrl3->get_HardwareProductId(&caps.pid))) + caps.pid = 0; + } + + return; + } + } + + memset(&caps, 0, sizeof(Capabilities)); + } + + bool SetVibration(int player, float leftMotor, float rightMotor, float leftTrigger, float rightTrigger) noexcept + { + using namespace ABI::Windows::Xbox::Input; + + if (player == c_MostRecent) + player = mMostRecentGamepad; + + if ((player >= 0) && (player < MAX_PLAYER_COUNT)) + { + if (mGamePad[player]) + { + HRESULT hr; + try + { + GamepadVibration vib; + vib.LeftMotorLevel = leftMotor; + vib.RightMotorLevel = rightMotor; + vib.LeftTriggerLevel = leftTrigger; + vib.RightTriggerLevel = rightTrigger; + hr = mGamePad[player]->SetVibration(vib); + } + catch (...) + { + // Handle case where gamepad might be invalid + hr = E_FAIL; + } + + if (SUCCEEDED(hr)) + return true; + } + } + + return false; + } + + void Suspend() noexcept + { + for (size_t j = 0; j < MAX_PLAYER_COUNT; ++j) + { + mGamePad[j].Reset(); + } + } + + void Resume() noexcept + { + // Make sure we rescan gamepads + SetEvent(mChanged.get()); + } + + GamePad* mOwner; + + static GamePad::Impl* s_gamePad; + + HANDLE mCtrlChanged; + HANDLE mUserChanged; + +private: + int mMostRecentGamepad; + + void ScanGamePads() + { + using namespace ABI::Windows::Foundation::Collections; + using namespace ABI::Windows::Xbox::Input; + + ComPtr> pads; + ThrowIfFailed(mStatics->get_Gamepads(pads.GetAddressOf())); + + unsigned int count = 0; + ThrowIfFailed(pads->get_Size(&count)); + + // Check for removed gamepads + for (size_t j = 0; j < MAX_PLAYER_COUNT; ++j) + { + if (mGamePad[j]) + { + unsigned int k = 0; + for (; k < count; ++k) + { + ComPtr pad; + HRESULT hr = pads->GetAt(k, pad.GetAddressOf()); + if (SUCCEEDED(hr) && (pad == mGamePad[j])) + { + break; + } + } + + if (k >= count) + { + mGamePad[j].Reset(); + } + } + } + + // Check for added gamepads + for (unsigned int j = 0; j < count; ++j) + { + ComPtr pad; + HRESULT hr = pads->GetAt(j, pad.GetAddressOf()); + if (SUCCEEDED(hr)) + { + size_t empty = MAX_PLAYER_COUNT; + size_t k = 0; + for (; k < MAX_PLAYER_COUNT; ++k) + { + if (mGamePad[k] == pad) + { + if (!j) + mMostRecentGamepad = static_cast(k); + break; + } + else if (!mGamePad[k]) + { + if (empty >= MAX_PLAYER_COUNT) + empty = k; + } + } + + if (k >= MAX_PLAYER_COUNT) + { + if (empty >= MAX_PLAYER_COUNT) + { + throw std::runtime_error("Too many gamepads found"); + } + + mGamePad[empty] = pad; + if (!j) + mMostRecentGamepad = static_cast(empty); + } + } + } + } + + ComPtr mStatics; + ComPtr mStaticsCtrl; + ComPtr mGamePad[MAX_PLAYER_COUNT]; + + EventRegistrationToken mAddedToken; + EventRegistrationToken mRemovedToken; + EventRegistrationToken mUserParingToken; + + ScopedHandle mChanged; +}; + +GamePad::Impl* GamePad::Impl::s_gamePad = nullptr; + +void GamePad::RegisterEvents(HANDLE ctrlChanged, HANDLE userChanged) noexcept +{ + pImpl->mCtrlChanged = (!ctrlChanged) ? INVALID_HANDLE_VALUE : ctrlChanged; + pImpl->mUserChanged = (!userChanged) ? INVALID_HANDLE_VALUE : userChanged; +} + + +#elif defined(USING_XINPUT) + +//====================================================================================== +// XInput +//====================================================================================== + +#include + +static_assert(GamePad::MAX_PLAYER_COUNT == XUSER_MAX_COUNT, "xinput.h mismatch"); + +class GamePad::Impl +{ +public: + Impl(GamePad* owner) : + mOwner(owner), + mConnected{}, + mLastReadTime{} + #if (_WIN32_WINNT < _WIN32_WINNT_WIN8) + , mLeftMotor{} + , mRightMotor{} + , mSuspended(false) + #endif + { + for (int j = 0; j < XUSER_MAX_COUNT; ++j) + { + ClearSlot(j, 0); + } + + if (s_gamePad) + { + throw std::logic_error("GamePad is a singleton"); + } + + s_gamePad = this; + } + + ~Impl() + { + s_gamePad = nullptr; + } + + void GetState(int player, _Out_ State& state, DeadZone deadZoneMode) + { + if (player == c_MostRecent) + player = GetMostRecent(); + + const ULONGLONG time = GetTickCount64(); + + if (!ThrottleRetry(player, time)) + { + #if (_WIN32_WINNT < _WIN32_WINNT_WIN8) + if (mSuspended) + { + memset(&state, 0, sizeof(State)); + state.connected = mConnected[player]; + return; + } + #endif + + XINPUT_STATE xstate; + const DWORD result = XInputGetState(DWORD(player), &xstate); + if (result == ERROR_DEVICE_NOT_CONNECTED) + { + ClearSlot(player, time); + } + else + { + if (!mConnected[player]) + mLastReadTime[player] = time; + + mConnected[player] = true; + + state.connected = true; + state.packet = xstate.dwPacketNumber; + + const WORD xbuttons = xstate.Gamepad.wButtons; + state.buttons.a = (xbuttons & XINPUT_GAMEPAD_A) != 0; + state.buttons.b = (xbuttons & XINPUT_GAMEPAD_B) != 0; + state.buttons.x = (xbuttons & XINPUT_GAMEPAD_X) != 0; + state.buttons.y = (xbuttons & XINPUT_GAMEPAD_Y) != 0; + state.buttons.leftStick = (xbuttons & XINPUT_GAMEPAD_LEFT_THUMB) != 0; + state.buttons.rightStick = (xbuttons & XINPUT_GAMEPAD_RIGHT_THUMB) != 0; + state.buttons.leftShoulder = (xbuttons & XINPUT_GAMEPAD_LEFT_SHOULDER) != 0; + state.buttons.rightShoulder = (xbuttons & XINPUT_GAMEPAD_RIGHT_SHOULDER) != 0; + state.buttons.back = (xbuttons & XINPUT_GAMEPAD_BACK) != 0; + state.buttons.start = (xbuttons & XINPUT_GAMEPAD_START) != 0; + + state.dpad.up = (xbuttons & XINPUT_GAMEPAD_DPAD_UP) != 0; + state.dpad.down = (xbuttons & XINPUT_GAMEPAD_DPAD_DOWN) != 0; + state.dpad.right = (xbuttons & XINPUT_GAMEPAD_DPAD_RIGHT) != 0; + state.dpad.left = (xbuttons & XINPUT_GAMEPAD_DPAD_LEFT) != 0; + + if (deadZoneMode == DEAD_ZONE_NONE) + { + state.triggers.left = ApplyLinearDeadZone(float(xstate.Gamepad.bLeftTrigger), 255.f, 0.f); + state.triggers.right = ApplyLinearDeadZone(float(xstate.Gamepad.bRightTrigger), 255.f, 0.f); + } + else + { + state.triggers.left = ApplyLinearDeadZone(float(xstate.Gamepad.bLeftTrigger), 255.f, float(XINPUT_GAMEPAD_TRIGGER_THRESHOLD)); + state.triggers.right = ApplyLinearDeadZone(float(xstate.Gamepad.bRightTrigger), 255.f, float(XINPUT_GAMEPAD_TRIGGER_THRESHOLD)); + } + + ApplyStickDeadZone(float(xstate.Gamepad.sThumbLX), float(xstate.Gamepad.sThumbLY), + deadZoneMode, 32767.f, float(XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE), + state.thumbSticks.leftX, state.thumbSticks.leftY); + + ApplyStickDeadZone(float(xstate.Gamepad.sThumbRX), float(xstate.Gamepad.sThumbRY), + deadZoneMode, 32767.f, float(XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE), + state.thumbSticks.rightX, state.thumbSticks.rightY); + + return; + } + } + + memset(&state, 0, sizeof(State)); + } + + void GetCapabilities(int player, _Out_ Capabilities& caps) + { + if (player == c_MostRecent) + player = GetMostRecent(); + + const ULONGLONG time = GetTickCount64(); + + if (!ThrottleRetry(player, time)) + { + XINPUT_CAPABILITIES xcaps; + const DWORD result = XInputGetCapabilities(DWORD(player), 0, &xcaps); + if (result == ERROR_DEVICE_NOT_CONNECTED) + { + ClearSlot(player, time); + } + else + { + if (!mConnected[player]) + mLastReadTime[player] = time; + + mConnected[player] = true; + + caps.connected = true; + caps.id = uint64_t(player); + if (xcaps.Type == XINPUT_DEVTYPE_GAMEPAD) + { + static_assert(Capabilities::GAMEPAD == XINPUT_DEVSUBTYPE_GAMEPAD, "xinput.h mismatch"); + #if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) + static_assert(XINPUT_DEVSUBTYPE_WHEEL == Capabilities::WHEEL, "xinput.h mismatch"); + static_assert(XINPUT_DEVSUBTYPE_ARCADE_STICK == Capabilities::ARCADE_STICK, "xinput.h mismatch"); + #ifndef __MINGW32__ + static_assert(XINPUT_DEVSUBTYPE_FLIGHT_STICK == Capabilities::FLIGHT_STICK, "xinput.h mismatch"); + #endif + static_assert(XINPUT_DEVSUBTYPE_DANCE_PAD == Capabilities::DANCE_PAD, "xinput.h mismatch"); + static_assert(XINPUT_DEVSUBTYPE_GUITAR == Capabilities::GUITAR, "xinput.h mismatch"); + static_assert(XINPUT_DEVSUBTYPE_GUITAR_ALTERNATE == Capabilities::GUITAR_ALTERNATE, "xinput.h mismatch"); + static_assert(XINPUT_DEVSUBTYPE_DRUM_KIT == Capabilities::DRUM_KIT, "xinput.h mismatch"); + static_assert(XINPUT_DEVSUBTYPE_GUITAR_BASS == Capabilities::GUITAR_BASS, "xinput.h mismatch"); + static_assert(XINPUT_DEVSUBTYPE_ARCADE_PAD == Capabilities::ARCADE_PAD, "xinput.h mismatch"); + #endif + + caps.gamepadType = Capabilities::Type(xcaps.SubType); + } + + // Hard-coded VID/PID + caps.vid = 0x045E; + #if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) + caps.pid = (xcaps.Flags & XINPUT_CAPS_WIRELESS) ? 0x0719 : 0; + #else + caps.pid = 0; + #endif + + return; + } + } + + memset(&caps, 0, sizeof(Capabilities)); + } + + bool SetVibration(int player, float leftMotor, float rightMotor, float leftTrigger, float rightTrigger) noexcept + { + if (player == c_MostRecent) + player = GetMostRecent(); + + const ULONGLONG time = GetTickCount64(); + + if (ThrottleRetry(player, time)) + { + return false; + } + + // XInput does not provide a way to set the left/right trigger impulse motors on the Xbox One Controller, + // and these motors are not present on the Xbox 360 Common Controller + UNREFERENCED_PARAMETER(leftTrigger); + UNREFERENCED_PARAMETER(rightTrigger); + + #if (_WIN32_WINNT < _WIN32_WINNT_WIN8) + mLeftMotor[player] = leftMotor; + mRightMotor[player] = rightMotor; + + if (mSuspended) + return mConnected[player]; + #endif + + XINPUT_VIBRATION xvibration; + xvibration.wLeftMotorSpeed = WORD(leftMotor * 0xFFFF); + xvibration.wRightMotorSpeed = WORD(rightMotor * 0xFFFF); + const DWORD result = XInputSetState(DWORD(player), &xvibration); + if (result == ERROR_DEVICE_NOT_CONNECTED) + { + ClearSlot(player, time); + return false; + } + else + { + if (!mConnected[player]) + mLastReadTime[player] = time; + + mConnected[player] = true; + return (result == ERROR_SUCCESS); + } + } + + void Suspend() noexcept + { + #if (_WIN32_WINNT >= _WIN32_WINNT_WIN10) + // XInput focus is handled automatically on Windows 10 + #elif (_WIN32_WINNT >= _WIN32_WINNT_WIN8) + XInputEnable(FALSE); + #else + // For XInput 9.1.0, we have to emulate the behavior of XInputEnable( FALSE ) + if (!mSuspended) + { + for (size_t j = 0; j < XUSER_MAX_COUNT; ++j) + { + if (mConnected[j]) + { + XINPUT_VIBRATION xvibration; + xvibration.wLeftMotorSpeed = xvibration.wRightMotorSpeed = 0; + std::ignore = XInputSetState(DWORD(j), &xvibration); + } + } + + mSuspended = true; + } + #endif + } + + void Resume() noexcept + { + #if (_WIN32_WINNT >= _WIN32_WINNT_WIN10) + // XInput focus is handled automatically on Windows 10 + #elif (_WIN32_WINNT >= _WIN32_WINNT_WIN8) + XInputEnable(TRUE); + #else + // For XInput 9.1.0, we have to emulate the behavior of XInputEnable( TRUE ) + if (mSuspended) + { + const ULONGLONG time = GetTickCount64(); + + for (int j = 0; j < XUSER_MAX_COUNT; ++j) + { + if (mConnected[j]) + { + XINPUT_VIBRATION xvibration; + xvibration.wLeftMotorSpeed = WORD(mLeftMotor[j] * 0xFFFF); + xvibration.wRightMotorSpeed = WORD(mRightMotor[j] * 0xFFFF); + const DWORD result = XInputSetState(DWORD(j), &xvibration); + if (result == ERROR_DEVICE_NOT_CONNECTED) + { + ClearSlot(j, time); + } + } + } + + mSuspended = false; + } + #endif + } + + GamePad* mOwner; + + static GamePad::Impl* s_gamePad; + +private: + bool mConnected[XUSER_MAX_COUNT]; + ULONGLONG mLastReadTime[XUSER_MAX_COUNT]; + +#if (_WIN32_WINNT < _WIN32_WINNT_WIN8) + // Variables for emulating XInputEnable on XInput 9.1.0 + float mLeftMotor[XUSER_MAX_COUNT]; + float mRightMotor[XUSER_MAX_COUNT]; + bool mSuspended; +#endif + + bool ThrottleRetry(int player, ULONGLONG time) + { + // This function minimizes a potential performance issue with XInput on Windows when + // checking a disconnected controller slot which requires device enumeration. + // This throttling keeps checks for newly connected gamepads to about once a second + + if ((player < 0) || (player >= XUSER_MAX_COUNT)) + return true; + + if (mConnected[player]) + return false; + + for (int j = 0; j < XUSER_MAX_COUNT; ++j) + { + if (!mConnected[j]) + { + const LONGLONG delta = LONGLONG(time) - LONGLONG(mLastReadTime[j]); + + LONGLONG interval = 1000; + if (j != player) + interval /= 4; + + if ((delta >= 0) && (delta < interval)) + return true; + } + } + + return false; + } + + void ClearSlot(int player, ULONGLONG time) + { + mConnected[player] = false; + mLastReadTime[player] = time; + #if (_WIN32_WINNT < _WIN32_WINNT_WIN8) + mLeftMotor[player] = mRightMotor[player] = 0.f; + #endif + } + + int GetMostRecent() + { + int player = -1; + ULONGLONG time = 0; + + for (size_t j = 0; j < XUSER_MAX_COUNT; ++j) + { + if (mConnected[j] && (mLastReadTime[j] > time)) + { + time = mLastReadTime[j]; + player = static_cast(j); + } + } + + return player; + } +}; + +GamePad::Impl* GamePad::Impl::s_gamePad = nullptr; + +#else + +#error Unknown GamePad implementation + +#endif +#pragma endregion + +#pragma warning( disable : 4355 ) + +// Public constructor. +GamePad::GamePad() noexcept(false) + : pImpl(std::make_unique(this)) +{ +} + + +// Move constructor. +GamePad::GamePad(GamePad&& moveFrom) noexcept + : pImpl(std::move(moveFrom.pImpl)) +{ + pImpl->mOwner = this; +} + + +// Move assignment. +GamePad& GamePad::operator= (GamePad&& moveFrom) noexcept +{ + pImpl = std::move(moveFrom.pImpl); + pImpl->mOwner = this; + return *this; +} + + +// Public destructor. +GamePad::~GamePad() = default; + + +GamePad::State GamePad::GetState(int player, DeadZone deadZoneMode) +{ + State state; + pImpl->GetState(player, state, deadZoneMode); + return state; +} + + +GamePad::Capabilities GamePad::GetCapabilities(int player) +{ + Capabilities caps; + pImpl->GetCapabilities(player, caps); + return caps; +} + + +bool GamePad::SetVibration(int player, float leftMotor, float rightMotor, float leftTrigger, float rightTrigger) noexcept +{ + return pImpl->SetVibration(player, leftMotor, rightMotor, leftTrigger, rightTrigger); +} + + +void GamePad::Suspend() noexcept +{ + pImpl->Suspend(); +} + + +void GamePad::Resume() noexcept +{ + pImpl->Resume(); +} + + +GamePad& GamePad::Get() +{ + if (!Impl::s_gamePad || !Impl::s_gamePad->mOwner) + throw std::logic_error("GamePad singleton not created"); + + return *Impl::s_gamePad->mOwner; +} + + + +//====================================================================================== +// ButtonStateTracker +//====================================================================================== + +#define UPDATE_BUTTON_STATE(field) field = static_cast( ( !!state.buttons.field ) | ( ( !!state.buttons.field ^ !!lastState.buttons.field ) << 1 ) ) + +void GamePad::ButtonStateTracker::Update(const GamePad::State& state) noexcept +{ + UPDATE_BUTTON_STATE(a); + + assert((!state.buttons.a && !lastState.buttons.a) == (a == UP)); + assert((state.buttons.a && lastState.buttons.a) == (a == HELD)); + assert((!state.buttons.a && lastState.buttons.a) == (a == RELEASED)); + assert((state.buttons.a && !lastState.buttons.a) == (a == PRESSED)); + + UPDATE_BUTTON_STATE(b); + UPDATE_BUTTON_STATE(x); + UPDATE_BUTTON_STATE(y); + + UPDATE_BUTTON_STATE(leftStick); + UPDATE_BUTTON_STATE(rightStick); + + UPDATE_BUTTON_STATE(leftShoulder); + UPDATE_BUTTON_STATE(rightShoulder); + + UPDATE_BUTTON_STATE(back); + UPDATE_BUTTON_STATE(start); + + dpadUp = static_cast((!!state.dpad.up) | ((!!state.dpad.up ^ !!lastState.dpad.up) << 1)); + dpadDown = static_cast((!!state.dpad.down) | ((!!state.dpad.down ^ !!lastState.dpad.down) << 1)); + dpadLeft = static_cast((!!state.dpad.left) | ((!!state.dpad.left ^ !!lastState.dpad.left) << 1)); + dpadRight = static_cast((!!state.dpad.right) | ((!!state.dpad.right ^ !!lastState.dpad.right) << 1)); + + assert((!state.dpad.up && !lastState.dpad.up) == (dpadUp == UP)); + assert((state.dpad.up && lastState.dpad.up) == (dpadUp == HELD)); + assert((!state.dpad.up && lastState.dpad.up) == (dpadUp == RELEASED)); + assert((state.dpad.up && !lastState.dpad.up) == (dpadUp == PRESSED)); + + // Handle 'threshold' tests which emulate buttons + + bool threshold = state.IsLeftThumbStickUp(); + leftStickUp = static_cast((!!threshold) | ((!!threshold ^ !!lastState.IsLeftThumbStickUp()) << 1)); + + threshold = state.IsLeftThumbStickDown(); + leftStickDown = static_cast((!!threshold) | ((!!threshold ^ !!lastState.IsLeftThumbStickDown()) << 1)); + + threshold = state.IsLeftThumbStickLeft(); + leftStickLeft = static_cast((!!threshold) | ((!!threshold ^ !!lastState.IsLeftThumbStickLeft()) << 1)); + + threshold = state.IsLeftThumbStickRight(); + leftStickRight = static_cast((!!threshold) | ((!!threshold ^ !!lastState.IsLeftThumbStickRight()) << 1)); + + threshold = state.IsRightThumbStickUp(); + rightStickUp = static_cast((!!threshold) | ((!!threshold ^ !!lastState.IsRightThumbStickUp()) << 1)); + + threshold = state.IsRightThumbStickDown(); + rightStickDown = static_cast((!!threshold) | ((!!threshold ^ !!lastState.IsRightThumbStickDown()) << 1)); + + threshold = state.IsRightThumbStickLeft(); + rightStickLeft = static_cast((!!threshold) | ((!!threshold ^ !!lastState.IsRightThumbStickLeft()) << 1)); + + threshold = state.IsRightThumbStickRight(); + rightStickRight = static_cast((!!threshold) | ((!!threshold ^ !!lastState.IsRightThumbStickRight()) << 1)); + + threshold = state.IsLeftTriggerPressed(); + leftTrigger = static_cast((!!threshold) | ((!!threshold ^ !!lastState.IsLeftTriggerPressed()) << 1)); + + threshold = state.IsRightTriggerPressed(); + rightTrigger = static_cast((!!threshold) | ((!!threshold ^ !!lastState.IsRightTriggerPressed()) << 1)); + + lastState = state; +} + +#undef UPDATE_BUTTON_STATE + + +void GamePad::ButtonStateTracker::Reset() noexcept +{ + memset(this, 0, sizeof(ButtonStateTracker)); +} diff --git a/Common/DirectXTK12/Src/GeometricPrimitive.cpp b/Common/DirectXTK12/Src/GeometricPrimitive.cpp new file mode 100644 index 0000000..c4aa073 --- /dev/null +++ b/Common/DirectXTK12/Src/GeometricPrimitive.cpp @@ -0,0 +1,706 @@ +//-------------------------------------------------------------------------------------- +// File: GeometricPrimitive.cpp +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "pch.h" +#include "GeometricPrimitive.h" + +#include "CommonStates.h" +#include "DirectXHelpers.h" +#include "Effects.h" +#include "Geometry.h" +#include "GraphicsMemory.h" +#include "PlatformHelpers.h" +#include "ResourceUploadBatch.h" + +using namespace DirectX; +using Microsoft::WRL::ComPtr; + +// Internal GeometricPrimitive implementation class. +class GeometricPrimitive::Impl +{ +public: + Impl() noexcept : mIndexCount(0), mVertexBufferView{}, mIndexBufferView{} {} + + void Initialize(const VertexCollection& vertices, const IndexCollection& indices, _In_opt_ ID3D12Device* device); + + void LoadStaticBuffers( + _In_ ID3D12Device* device, + ResourceUploadBatch& resourceUploadBatch); + + void Transition( + _In_ ID3D12GraphicsCommandList* commandList, + D3D12_RESOURCE_STATES stateBeforeVB, + D3D12_RESOURCE_STATES stateAfterVB, + D3D12_RESOURCE_STATES stateBeforeIB, + D3D12_RESOURCE_STATES stateAfterIB); + + void DrawInstanced(_In_ ID3D12GraphicsCommandList* commandList, uint32_t instanceCount, uint32_t startInstanceLocation) const; + + UINT mIndexCount; + SharedGraphicsResource mIndexBuffer; + SharedGraphicsResource mVertexBuffer; + ComPtr mStaticIndexBuffer; + ComPtr mStaticVertexBuffer; + D3D12_VERTEX_BUFFER_VIEW mVertexBufferView; + D3D12_INDEX_BUFFER_VIEW mIndexBufferView; +}; + + +// Initializes a geometric primitive instance that will draw the specified vertex and index data. +void GeometricPrimitive::Impl::Initialize( + const VertexCollection& vertices, + const IndexCollection& indices, + _In_opt_ ID3D12Device* device) +{ + if (vertices.size() >= USHRT_MAX) + throw std::invalid_argument("Too many vertices for 16-bit index buffer"); + + if (indices.size() > UINT32_MAX) + throw std::invalid_argument("Too many indices"); + + // Vertex data + uint64_t sizeInBytes = uint64_t(vertices.size()) * sizeof(vertices[0]); + if (sizeInBytes > uint64_t(D3D12_REQ_RESOURCE_SIZE_IN_MEGABYTES_EXPRESSION_A_TERM * 1024u * 1024u)) + throw std::invalid_argument("VB too large for DirectX 12"); + + auto const vertSizeBytes = static_cast(sizeInBytes); + + mVertexBuffer = GraphicsMemory::Get(device).Allocate(vertSizeBytes, 16, GraphicsMemory::TAG_VERTEX); + + auto verts = reinterpret_cast(vertices.data()); + memcpy(mVertexBuffer.Memory(), verts, vertSizeBytes); + + // Index data + sizeInBytes = uint64_t(indices.size()) * sizeof(indices[0]); + if (sizeInBytes > uint64_t(D3D12_REQ_RESOURCE_SIZE_IN_MEGABYTES_EXPRESSION_A_TERM * 1024u * 1024u)) + throw std::invalid_argument("IB too large for DirectX 12"); + + auto const indSizeBytes = static_cast(sizeInBytes); + + mIndexBuffer = GraphicsMemory::Get(device).Allocate(indSizeBytes, 16, GraphicsMemory::TAG_INDEX); + + auto ind = reinterpret_cast(indices.data()); + memcpy(mIndexBuffer.Memory(), ind, indSizeBytes); + + // Record index count for draw + mIndexCount = static_cast(indices.size()); + + // Create views + mVertexBufferView.BufferLocation = mVertexBuffer.GpuAddress(); + mVertexBufferView.StrideInBytes = static_cast(sizeof(VertexCollection::value_type)); + mVertexBufferView.SizeInBytes = static_cast(mVertexBuffer.Size()); + + mIndexBufferView.BufferLocation = mIndexBuffer.GpuAddress(); + mIndexBufferView.SizeInBytes = static_cast(mIndexBuffer.Size()); + mIndexBufferView.Format = DXGI_FORMAT_R16_UINT; +} + + +// Load VB/IB resources for static geometry. +_Use_decl_annotations_ +void GeometricPrimitive::Impl::LoadStaticBuffers( + ID3D12Device* device, + ResourceUploadBatch& resourceUploadBatch) +{ + const CD3DX12_HEAP_PROPERTIES heapProperties(D3D12_HEAP_TYPE_DEFAULT); + + // Convert dynamic VB to static VB + if (!mStaticVertexBuffer) + { + assert(mVertexBuffer); + + auto const desc = CD3DX12_RESOURCE_DESC::Buffer(mVertexBuffer.Size()); + + ThrowIfFailed(device->CreateCommittedResource( + &heapProperties, + D3D12_HEAP_FLAG_NONE, + &desc, + c_initialCopyTargetState, + nullptr, + IID_GRAPHICS_PPV_ARGS(mStaticVertexBuffer.GetAddressOf()) + )); + + SetDebugObjectName(mStaticVertexBuffer.Get(), L"GeometricPrimitive"); + + resourceUploadBatch.Upload(mStaticVertexBuffer.Get(), mVertexBuffer); + + resourceUploadBatch.Transition(mStaticVertexBuffer.Get(), + D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER); + + // Update view + mVertexBufferView.BufferLocation = mStaticVertexBuffer->GetGPUVirtualAddress(); + + mVertexBuffer.Reset(); + } + + // Convert dynamic IB to static IB + if (!mStaticIndexBuffer) + { + assert(mIndexBuffer); + + auto const desc = CD3DX12_RESOURCE_DESC::Buffer(mIndexBuffer.Size()); + + ThrowIfFailed(device->CreateCommittedResource( + &heapProperties, + D3D12_HEAP_FLAG_NONE, + &desc, + c_initialCopyTargetState, + nullptr, + IID_GRAPHICS_PPV_ARGS(mStaticIndexBuffer.GetAddressOf()) + )); + + SetDebugObjectName(mStaticIndexBuffer.Get(), L"GeometricPrimitive"); + + resourceUploadBatch.Upload(mStaticIndexBuffer.Get(), mIndexBuffer); + + resourceUploadBatch.Transition(mStaticIndexBuffer.Get(), + D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_INDEX_BUFFER); + + // Update view + mIndexBufferView.BufferLocation = mStaticIndexBuffer->GetGPUVirtualAddress(); + + mIndexBuffer.Reset(); + } +} + + +// Transition VB/IB resources for static geometry. +_Use_decl_annotations_ +void GeometricPrimitive::Impl::Transition( + ID3D12GraphicsCommandList* commandList, + D3D12_RESOURCE_STATES stateBeforeVB, + D3D12_RESOURCE_STATES stateAfterVB, + D3D12_RESOURCE_STATES stateBeforeIB, + D3D12_RESOURCE_STATES stateAfterIB) +{ + UINT start = 0; + UINT count = 0; + + D3D12_RESOURCE_BARRIER barrier[2] = {}; + barrier[0].Type = barrier[1].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier[0].Transition.Subresource = barrier[1].Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + barrier[0].Transition.pResource = mStaticIndexBuffer.Get(); + barrier[0].Transition.StateBefore = stateBeforeIB; + barrier[0].Transition.StateAfter = stateAfterIB; + + barrier[1].Transition.pResource = mStaticVertexBuffer.Get(); + barrier[1].Transition.StateBefore = stateBeforeVB; + barrier[1].Transition.StateAfter = stateAfterVB; + + if (stateBeforeIB == stateAfterIB || !mStaticIndexBuffer) + { + ++start; + } + else + { + ++count; + } + + if (stateBeforeVB != stateAfterVB && mStaticVertexBuffer) + { + ++count; + } + + if (count > 0) + { + commandList->ResourceBarrier(count, &barrier[start]); + } +} + + +// Draws the primitive. +_Use_decl_annotations_ +void GeometricPrimitive::Impl::DrawInstanced(ID3D12GraphicsCommandList* commandList, uint32_t instanceCount, uint32_t startInstanceLocation) const +{ + commandList->IASetVertexBuffers(0, 1, &mVertexBufferView); + commandList->IASetIndexBuffer(&mIndexBufferView); + commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + + commandList->DrawIndexedInstanced(mIndexCount, instanceCount, 0, 0, startInstanceLocation); +} + +//-------------------------------------------------------------------------------------- +// GeometricPrimitive +//-------------------------------------------------------------------------------------- + +// Constructor. +GeometricPrimitive::GeometricPrimitive() noexcept(false) + : pImpl(std::make_unique()) +{ +} + + +// Destructor. +GeometricPrimitive::~GeometricPrimitive() +{ +} + + +// Public entrypoints. +_Use_decl_annotations_ +void GeometricPrimitive::LoadStaticBuffers(ID3D12Device* device, ResourceUploadBatch& resourceUploadBatch) +{ + pImpl->LoadStaticBuffers(device, resourceUploadBatch); +} + + +void GeometricPrimitive::Transition( + _In_ ID3D12GraphicsCommandList* commandList, + D3D12_RESOURCE_STATES stateBeforeVB, + D3D12_RESOURCE_STATES stateAfterVB, + D3D12_RESOURCE_STATES stateBeforeIB, + D3D12_RESOURCE_STATES stateAfterIB) +{ + pImpl->Transition(commandList, stateBeforeVB, stateAfterVB, stateBeforeIB, stateAfterIB); +} + + + +_Use_decl_annotations_ +void GeometricPrimitive::Draw(ID3D12GraphicsCommandList* commandList) const +{ + pImpl->DrawInstanced(commandList, 1, 0); +} + + +_Use_decl_annotations_ +void GeometricPrimitive::DrawInstanced(ID3D12GraphicsCommandList* commandList, uint32_t instanceCount, uint32_t startInstanceLocation) const +{ + pImpl->DrawInstanced(commandList, instanceCount, startInstanceLocation); +} + + +//-------------------------------------------------------------------------------------- +// Cube (aka a Hexahedron) or Box +//-------------------------------------------------------------------------------------- + +// Creates a cube primitive. +std::unique_ptr GeometricPrimitive::CreateCube( + float size, + bool rhcoords, + _In_opt_ ID3D12Device* device) +{ + VertexCollection vertices; + IndexCollection indices; + ComputeBox(vertices, indices, XMFLOAT3(size, size, size), rhcoords, false); + + // Create the primitive object. + std::unique_ptr primitive(new GeometricPrimitive()); + + primitive->pImpl->Initialize(vertices, indices, device); + + return primitive; +} + +void GeometricPrimitive::CreateCube( + VertexCollection& vertices, + IndexCollection& indices, + float size, + bool rhcoords) +{ + ComputeBox(vertices, indices, XMFLOAT3(size, size, size), rhcoords, false); +} + + +// Creates a box primitive. +std::unique_ptr GeometricPrimitive::CreateBox( + const XMFLOAT3& size, + bool rhcoords, + bool invertn, + _In_opt_ ID3D12Device* device) +{ + VertexCollection vertices; + IndexCollection indices; + ComputeBox(vertices, indices, size, rhcoords, invertn); + + // Create the primitive object. + std::unique_ptr primitive(new GeometricPrimitive()); + + primitive->pImpl->Initialize(vertices, indices, device); + + return primitive; +} + +void GeometricPrimitive::CreateBox( + VertexCollection& vertices, + IndexCollection& indices, + const XMFLOAT3& size, + bool rhcoords, + bool invertn) +{ + ComputeBox(vertices, indices, size, rhcoords, invertn); +} + + +//-------------------------------------------------------------------------------------- +// Sphere +//-------------------------------------------------------------------------------------- + +// Creates a sphere primitive. +std::unique_ptr GeometricPrimitive::CreateSphere( + float diameter, + size_t tessellation, + bool rhcoords, + bool invertn, + _In_opt_ ID3D12Device* device) +{ + // Create the primitive object. + std::unique_ptr primitive(new GeometricPrimitive()); + + VertexCollection vertices; + IndexCollection indices; + + ComputeSphere(vertices, indices, diameter, tessellation, rhcoords, invertn); + + primitive->pImpl->Initialize(vertices, indices, device); + + return primitive; +} + +void GeometricPrimitive::CreateSphere( + VertexCollection& vertices, + IndexCollection& indices, + float diameter, + size_t tessellation, + bool rhcoords, + bool invertn) +{ + ComputeSphere(vertices, indices, diameter, tessellation, rhcoords, invertn); +} + + +//-------------------------------------------------------------------------------------- +// Geodesic sphere +//-------------------------------------------------------------------------------------- + +// Creates a geosphere primitive. +std::unique_ptr GeometricPrimitive::CreateGeoSphere( + float diameter, + size_t tessellation, + bool rhcoords, + _In_opt_ ID3D12Device* device) +{ + // Create the primitive object. + std::unique_ptr primitive(new GeometricPrimitive()); + + VertexCollection vertices; + IndexCollection indices; + ComputeGeoSphere(vertices, indices, diameter, tessellation, rhcoords); + + primitive->pImpl->Initialize(vertices, indices, device); + + return primitive; +} + +void GeometricPrimitive::CreateGeoSphere( + VertexCollection& vertices, + IndexCollection& indices, + float diameter, + size_t tessellation, + bool rhcoords) +{ + ComputeGeoSphere(vertices, indices, diameter, tessellation, rhcoords); +} + + +//-------------------------------------------------------------------------------------- +// Cylinder / Cone +//-------------------------------------------------------------------------------------- + +// Creates a cylinder primitive. +std::unique_ptr GeometricPrimitive::CreateCylinder( + float height, + float diameter, + size_t tessellation, + bool rhcoords, + _In_opt_ ID3D12Device* device) +{ + // Create the primitive object. + std::unique_ptr primitive(new GeometricPrimitive()); + + VertexCollection vertices; + IndexCollection indices; + ComputeCylinder(vertices, indices, height, diameter, tessellation, rhcoords); + + primitive->pImpl->Initialize(vertices, indices, device); + + return primitive; +} + +void GeometricPrimitive::CreateCylinder( + VertexCollection& vertices, + IndexCollection& indices, + float height, float diameter, + size_t tessellation, + bool rhcoords) +{ + ComputeCylinder(vertices, indices, height, diameter, tessellation, rhcoords); +} + + +// Creates a cone primitive. +std::unique_ptr GeometricPrimitive::CreateCone( + float diameter, + float height, + size_t tessellation, + bool rhcoords, + _In_opt_ ID3D12Device* device) +{ + // Create the primitive object. + std::unique_ptr primitive(new GeometricPrimitive()); + + VertexCollection vertices; + IndexCollection indices; + ComputeCone(vertices, indices, diameter, height, tessellation, rhcoords); + + primitive->pImpl->Initialize(vertices, indices, device); + + return primitive; +} + +void GeometricPrimitive::CreateCone( + VertexCollection& vertices, + IndexCollection& indices, + float diameter, + float height, + size_t tessellation, + bool rhcoords) +{ + ComputeCone(vertices, indices, diameter, height, tessellation, rhcoords); +} + + +//-------------------------------------------------------------------------------------- +// Torus +//-------------------------------------------------------------------------------------- + +// Creates a torus primitive. +std::unique_ptr GeometricPrimitive::CreateTorus( + float diameter, + float thickness, + size_t tessellation, + bool rhcoords, + _In_opt_ ID3D12Device* device) +{ + // Create the primitive object. + std::unique_ptr primitive(new GeometricPrimitive()); + + VertexCollection vertices; + IndexCollection indices; + ComputeTorus(vertices, indices, diameter, thickness, tessellation, rhcoords); + + primitive->pImpl->Initialize(vertices, indices, device); + + return primitive; +} + +void GeometricPrimitive::CreateTorus( + VertexCollection& vertices, + IndexCollection& indices, + float diameter, + float thickness, + size_t tessellation, + bool rhcoords) +{ + ComputeTorus(vertices, indices, diameter, thickness, tessellation, rhcoords); +} + + +//-------------------------------------------------------------------------------------- +// Tetrahedron +//-------------------------------------------------------------------------------------- + +std::unique_ptr GeometricPrimitive::CreateTetrahedron( + float size, + bool rhcoords, + _In_opt_ ID3D12Device* device) +{ + // Create the primitive object. + std::unique_ptr primitive(new GeometricPrimitive()); + + VertexCollection vertices; + IndexCollection indices; + ComputeTetrahedron(vertices, indices, size, rhcoords); + + primitive->pImpl->Initialize(vertices, indices, device); + + return primitive; +} + +void GeometricPrimitive::CreateTetrahedron( + VertexCollection& vertices, + IndexCollection& indices, + float size, + bool rhcoords) +{ + ComputeTetrahedron(vertices, indices, size, rhcoords); +} + + +//-------------------------------------------------------------------------------------- +// Octahedron +//-------------------------------------------------------------------------------------- + +std::unique_ptr GeometricPrimitive::CreateOctahedron( + float size, + bool rhcoords, + _In_opt_ ID3D12Device* device) +{ + // Create the primitive object. + std::unique_ptr primitive(new GeometricPrimitive()); + + VertexCollection vertices; + IndexCollection indices; + ComputeOctahedron(vertices, indices, size, rhcoords); + + primitive->pImpl->Initialize(vertices, indices, device); + + return primitive; +} + +void GeometricPrimitive::CreateOctahedron( + VertexCollection& vertices, + IndexCollection& indices, + float size, + bool rhcoords) +{ + ComputeOctahedron(vertices, indices, size, rhcoords); +} + + +//-------------------------------------------------------------------------------------- +// Dodecahedron +//-------------------------------------------------------------------------------------- + +std::unique_ptr GeometricPrimitive::CreateDodecahedron( + float size, + bool rhcoords, + _In_opt_ ID3D12Device* device) +{ + // Create the primitive object. + std::unique_ptr primitive(new GeometricPrimitive()); + + VertexCollection vertices; + IndexCollection indices; + ComputeDodecahedron(vertices, indices, size, rhcoords); + + primitive->pImpl->Initialize(vertices, indices, device); + + return primitive; +} + +void GeometricPrimitive::CreateDodecahedron( + VertexCollection& vertices, + IndexCollection& indices, + float size, + bool rhcoords) +{ + ComputeDodecahedron(vertices, indices, size, rhcoords); +} + + +//-------------------------------------------------------------------------------------- +// Icosahedron +//-------------------------------------------------------------------------------------- + +std::unique_ptr GeometricPrimitive::CreateIcosahedron( + float size, + bool rhcoords, + _In_opt_ ID3D12Device* device) +{ + // Create the primitive object. + std::unique_ptr primitive(new GeometricPrimitive()); + + VertexCollection vertices; + IndexCollection indices; + ComputeIcosahedron(vertices, indices, size, rhcoords); + + primitive->pImpl->Initialize(vertices, indices, device); + + return primitive; +} + +void GeometricPrimitive::CreateIcosahedron( + VertexCollection& vertices, + IndexCollection& indices, + float size, + bool rhcoords) +{ + ComputeIcosahedron(vertices, indices, size, rhcoords); +} + + +//-------------------------------------------------------------------------------------- +// Teapot +//-------------------------------------------------------------------------------------- + +// Creates a teapot primitive. +std::unique_ptr GeometricPrimitive::CreateTeapot( + float size, + size_t tessellation, + bool rhcoords, + _In_opt_ ID3D12Device* device) +{ + // Create the primitive object. + std::unique_ptr primitive(new GeometricPrimitive()); + + VertexCollection vertices; + IndexCollection indices; + ComputeTeapot(vertices, indices, size, tessellation, rhcoords); + + primitive->pImpl->Initialize(vertices, indices, device); + + return primitive; +} + +void GeometricPrimitive::CreateTeapot( + VertexCollection& vertices, + IndexCollection& indices, + float size, size_t tessellation, + bool rhcoords) +{ + ComputeTeapot(vertices, indices, size, tessellation, rhcoords); +} + + +//-------------------------------------------------------------------------------------- +// Custom +//-------------------------------------------------------------------------------------- + +std::unique_ptr GeometricPrimitive::CreateCustom( + const VertexCollection& vertices, + const IndexCollection& indices, + _In_opt_ ID3D12Device* device) +{ + // Extra validation + if (vertices.empty() || indices.empty()) + throw std::invalid_argument("Requires both vertices and indices"); + + if (indices.size() % 3) + throw std::invalid_argument("Expected triangular faces"); + + const size_t nVerts = vertices.size(); + if (nVerts >= USHRT_MAX) + throw std::invalid_argument("Too many vertices for 16-bit index buffer"); + + for (auto it : indices) + { + if (it >= nVerts) + { + throw std::out_of_range("Index not in vertices list"); + } + } + // Create the primitive object. + std::unique_ptr primitive(new GeometricPrimitive()); + + // copy geometry + primitive->pImpl->Initialize(vertices, indices, device); + + return primitive; +} diff --git a/Common/DirectXTK12/Src/Geometry.cpp b/Common/DirectXTK12/Src/Geometry.cpp new file mode 100644 index 0000000..8b2adf2 --- /dev/null +++ b/Common/DirectXTK12/Src/Geometry.cpp @@ -0,0 +1,1193 @@ +//-------------------------------------------------------------------------------------- +// File: Geometry.cpp +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248929 +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "pch.h" +#include "Geometry.h" +#include "Bezier.h" + +using namespace DirectX; + +namespace +{ + constexpr float SQRT2 = 1.41421356237309504880f; + constexpr float SQRT3 = 1.73205080756887729352f; + constexpr float SQRT6 = 2.44948974278317809820f; + + inline void CheckIndexOverflow(size_t value) + { + // Use >=, not > comparison, because some D3D level 9_x hardware does not support 0xFFFF index values. + if (value >= USHRT_MAX) + throw std::out_of_range("Index value out of range: cannot tesselate primitive so finely"); + } + + + // Collection types used when generating the geometry. + inline void index_push_back(IndexCollection& indices, size_t value) + { + CheckIndexOverflow(value); + indices.push_back(static_cast(value)); + } + + + // Helper for flipping winding of geometric primitives for LH vs. RH coords + inline void ReverseWinding(IndexCollection& indices, VertexCollection& vertices) + { + assert((indices.size() % 3) == 0); + for (auto it = indices.begin(); it != indices.end(); it += 3) + { + std::swap(*it, *(it + 2)); + } + + for (auto& it : vertices) + { + it.textureCoordinate.x = (1.f - it.textureCoordinate.x); + } + } + + + // Helper for inverting normals of geometric primitives for 'inside' vs. 'outside' viewing + inline void InvertNormals(VertexCollection& vertices) + { + for (auto& it : vertices) + { + it.normal.x = -it.normal.x; + it.normal.y = -it.normal.y; + it.normal.z = -it.normal.z; + } + } +} + + +//-------------------------------------------------------------------------------------- +// Cube (aka a Hexahedron) or Box +//-------------------------------------------------------------------------------------- +void DirectX::ComputeBox(VertexCollection& vertices, IndexCollection& indices, const XMFLOAT3& size, bool rhcoords, bool invertn) +{ + vertices.clear(); + indices.clear(); + + // A box has six faces, each one pointing in a different direction. + constexpr int FaceCount = 6; + + static const XMVECTORF32 faceNormals[FaceCount] = + { + { { { 0, 0, 1, 0 } } }, + { { { 0, 0, -1, 0 } } }, + { { { 1, 0, 0, 0 } } }, + { { { -1, 0, 0, 0 } } }, + { { { 0, 1, 0, 0 } } }, + { { { 0, -1, 0, 0 } } }, + }; + + static const XMVECTORF32 textureCoordinates[4] = + { + { { { 1, 0, 0, 0 } } }, + { { { 1, 1, 0, 0 } } }, + { { { 0, 1, 0, 0 } } }, + { { { 0, 0, 0, 0 } } }, + }; + + XMVECTOR tsize = XMLoadFloat3(&size); + tsize = XMVectorDivide(tsize, g_XMTwo); + + // Create each face in turn. + for (int i = 0; i < FaceCount; i++) + { + const XMVECTOR normal = faceNormals[i]; + + // Get two vectors perpendicular both to the face normal and to each other. + const XMVECTOR basis = (i >= 4) ? g_XMIdentityR2 : g_XMIdentityR1; + + const XMVECTOR side1 = XMVector3Cross(normal, basis); + const XMVECTOR side2 = XMVector3Cross(normal, side1); + + // Six indices (two triangles) per face. + const size_t vbase = vertices.size(); + index_push_back(indices, vbase + 0); + index_push_back(indices, vbase + 1); + index_push_back(indices, vbase + 2); + + index_push_back(indices, vbase + 0); + index_push_back(indices, vbase + 2); + index_push_back(indices, vbase + 3); + + // Four vertices per face. + // (normal - side1 - side2) * tsize // normal // t0 + vertices.push_back(VertexPositionNormalTexture(XMVectorMultiply(XMVectorSubtract(XMVectorSubtract(normal, side1), side2), tsize), normal, textureCoordinates[0])); + + // (normal - side1 + side2) * tsize // normal // t1 + vertices.push_back(VertexPositionNormalTexture(XMVectorMultiply(XMVectorAdd(XMVectorSubtract(normal, side1), side2), tsize), normal, textureCoordinates[1])); + + // (normal + side1 + side2) * tsize // normal // t2 + vertices.push_back(VertexPositionNormalTexture(XMVectorMultiply(XMVectorAdd(normal, XMVectorAdd(side1, side2)), tsize), normal, textureCoordinates[2])); + + // (normal + side1 - side2) * tsize // normal // t3 + vertices.push_back(VertexPositionNormalTexture(XMVectorMultiply(XMVectorSubtract(XMVectorAdd(normal, side1), side2), tsize), normal, textureCoordinates[3])); + } + + // Build RH above + if (!rhcoords) + ReverseWinding(indices, vertices); + + if (invertn) + InvertNormals(vertices); +} + + +//-------------------------------------------------------------------------------------- +// Sphere +//-------------------------------------------------------------------------------------- +void DirectX::ComputeSphere(VertexCollection& vertices, IndexCollection& indices, float diameter, size_t tessellation, bool rhcoords, bool invertn) +{ + vertices.clear(); + indices.clear(); + + if (tessellation < 3) + throw std::invalid_argument("tesselation parameter must be at least 3"); + + const size_t verticalSegments = tessellation; + const size_t horizontalSegments = tessellation * 2; + + const float radius = diameter / 2; + + // Create rings of vertices at progressively higher latitudes. + for (size_t i = 0; i <= verticalSegments; i++) + { + const float v = 1 - float(i) / float(verticalSegments); + + const float latitude = (float(i) * XM_PI / float(verticalSegments)) - XM_PIDIV2; + float dy, dxz; + + XMScalarSinCos(&dy, &dxz, latitude); + + // Create a single ring of vertices at this latitude. + for (size_t j = 0; j <= horizontalSegments; j++) + { + const float u = float(j) / float(horizontalSegments); + + const float longitude = float(j) * XM_2PI / float(horizontalSegments); + float dx, dz; + + XMScalarSinCos(&dx, &dz, longitude); + + dx *= dxz; + dz *= dxz; + + const XMVECTOR normal = XMVectorSet(dx, dy, dz, 0); + const XMVECTOR textureCoordinate = XMVectorSet(u, v, 0, 0); + + vertices.push_back(VertexPositionNormalTexture(XMVectorScale(normal, radius), normal, textureCoordinate)); + } + } + + // Fill the index buffer with triangles joining each pair of latitude rings. + const size_t stride = horizontalSegments + 1; + + for (size_t i = 0; i < verticalSegments; i++) + { + for (size_t j = 0; j <= horizontalSegments; j++) + { + const size_t nextI = i + 1; + const size_t nextJ = (j + 1) % stride; + + index_push_back(indices, i * stride + j); + index_push_back(indices, nextI * stride + j); + index_push_back(indices, i * stride + nextJ); + + index_push_back(indices, i * stride + nextJ); + index_push_back(indices, nextI * stride + j); + index_push_back(indices, nextI * stride + nextJ); + } + } + + // Build RH above + if (!rhcoords) + ReverseWinding(indices, vertices); + + if (invertn) + InvertNormals(vertices); +} + + +//-------------------------------------------------------------------------------------- +// Geodesic sphere +//-------------------------------------------------------------------------------------- +void DirectX::ComputeGeoSphere(VertexCollection& vertices, IndexCollection& indices, float diameter, size_t tessellation, bool rhcoords) +{ + vertices.clear(); + indices.clear(); + + // An undirected edge between two vertices, represented by a pair of indexes into a vertex array. + // Becuse this edge is undirected, (a,b) is the same as (b,a). + using UndirectedEdge = std::pair; + + // Makes an undirected edge. Rather than overloading comparison operators to give us the (a,b)==(b,a) property, + // we'll just ensure that the larger of the two goes first. This'll simplify things greatly. + auto makeUndirectedEdge = [](uint16_t a, uint16_t b) noexcept + { + return std::make_pair(std::max(a, b), std::min(a, b)); + }; + + // Key: an edge + // Value: the index of the vertex which lies midway between the two vertices pointed to by the key value + // This map is used to avoid duplicating vertices when subdividing triangles along edges. + using EdgeSubdivisionMap = std::map; + + + static const XMFLOAT3 OctahedronVertices[] = + { + // when looking down the negative z-axis (into the screen) + XMFLOAT3(0, 1, 0), // 0 top + XMFLOAT3(0, 0, -1), // 1 front + XMFLOAT3(1, 0, 0), // 2 right + XMFLOAT3(0, 0, 1), // 3 back + XMFLOAT3(-1, 0, 0), // 4 left + XMFLOAT3(0, -1, 0), // 5 bottom + }; + static const uint16_t OctahedronIndices[] = + { + 0, 1, 2, // top front-right face + 0, 2, 3, // top back-right face + 0, 3, 4, // top back-left face + 0, 4, 1, // top front-left face + 5, 1, 4, // bottom front-left face + 5, 4, 3, // bottom back-left face + 5, 3, 2, // bottom back-right face + 5, 2, 1, // bottom front-right face + }; + + const float radius = diameter / 2.0f; + + // Start with an octahedron; copy the data into the vertex/index collection. + + std::vector vertexPositions(std::begin(OctahedronVertices), std::end(OctahedronVertices)); + + indices.insert(indices.begin(), std::begin(OctahedronIndices), std::end(OctahedronIndices)); + + // We know these values by looking at the above index list for the octahedron. Despite the subdivisions that are + // about to go on, these values aren't ever going to change because the vertices don't move around in the array. + // We'll need these values later on to fix the singularities that show up at the poles. + constexpr uint16_t northPoleIndex = 0; + constexpr uint16_t southPoleIndex = 5; + + for (size_t iSubdivision = 0; iSubdivision < tessellation; ++iSubdivision) + { + assert(indices.size() % 3 == 0); // sanity + + // We use this to keep track of which edges have already been subdivided. + EdgeSubdivisionMap subdividedEdges; + + // The new index collection after subdivision. + IndexCollection newIndices; + + const size_t triangleCount = indices.size() / 3; + for (size_t iTriangle = 0; iTriangle < triangleCount; ++iTriangle) + { + // For each edge on this triangle, create a new vertex in the middle of that edge. + // The winding order of the triangles we output are the same as the winding order of the inputs. + + // Indices of the vertices making up this triangle + const uint16_t iv0 = indices[iTriangle * 3 + 0]; + const uint16_t iv1 = indices[iTriangle * 3 + 1]; + const uint16_t iv2 = indices[iTriangle * 3 + 2]; + + // Get the new vertices + XMFLOAT3 v01; // vertex on the midpoint of v0 and v1 + XMFLOAT3 v12; // ditto v1 and v2 + XMFLOAT3 v20; // ditto v2 and v0 + uint16_t iv01; // index of v01 + uint16_t iv12; // index of v12 + uint16_t iv20; // index of v20 + + // Function that, when given the index of two vertices, creates a new vertex at the midpoint of those vertices. + auto const divideEdge = [&](uint16_t i0, uint16_t i1, XMFLOAT3& outVertex, uint16_t& outIndex) + { + const UndirectedEdge edge = makeUndirectedEdge(i0, i1); + + // Check to see if we've already generated this vertex + auto it = subdividedEdges.find(edge); + if (it != subdividedEdges.end()) + { + // We've already generated this vertex before + outIndex = it->second; // the index of this vertex + outVertex = vertexPositions[outIndex]; // and the vertex itself + } + else + { + // Haven't generated this vertex before: so add it now + + // outVertex = (vertices[i0] + vertices[i1]) / 2 + XMStoreFloat3( + &outVertex, + XMVectorScale( + XMVectorAdd(XMLoadFloat3(&vertexPositions[i0]), XMLoadFloat3(&vertexPositions[i1])), + 0.5f + ) + ); + + outIndex = static_cast(vertexPositions.size()); + CheckIndexOverflow(outIndex); + vertexPositions.push_back(outVertex); + + // Now add it to the map. + auto entry = std::make_pair(edge, outIndex); + subdividedEdges.insert(entry); + } + }; + + // Add/get new vertices and their indices + divideEdge(iv0, iv1, v01, iv01); + divideEdge(iv1, iv2, v12, iv12); + divideEdge(iv0, iv2, v20, iv20); + + // Add the new indices. We have four new triangles from our original one: + // v0 + // o + // /a\ + // v20 o---o v01 + // /b\c/d\ + // v2 o---o---o v1 + // v12 + const uint16_t indicesToAdd[] = + { + iv0, iv01, iv20, // a + iv20, iv12, iv2, // b + iv20, iv01, iv12, // c + iv01, iv1, iv12, // d + }; + newIndices.insert(newIndices.end(), std::begin(indicesToAdd), std::end(indicesToAdd)); + } + + indices = std::move(newIndices); + } + + // Now that we've completed subdivision, fill in the final vertex collection + vertices.reserve(vertexPositions.size()); + for (const auto& it : vertexPositions) + { + auto const normal = XMVector3Normalize(XMLoadFloat3(&it)); + auto const pos = XMVectorScale(normal, radius); + + XMFLOAT3 normalFloat3; + XMStoreFloat3(&normalFloat3, normal); + + // calculate texture coordinates for this vertex + const float longitude = atan2f(normalFloat3.x, -normalFloat3.z); + const float latitude = acosf(normalFloat3.y); + + const float u = longitude / XM_2PI + 0.5f; + const float v = latitude / XM_PI; + + auto const texcoord = XMVectorSet(1.0f - u, v, 0.0f, 0.0f); + vertices.push_back(VertexPositionNormalTexture(pos, normal, texcoord)); + } + + // There are a couple of fixes to do. One is a texture coordinate wraparound fixup. At some point, there will be + // a set of triangles somewhere in the mesh with texture coordinates such that the wraparound across 0.0/1.0 + // occurs across that triangle. Eg. when the left hand side of the triangle has a U coordinate of 0.98 and the + // right hand side has a U coordinate of 0.0. The intent is that such a triangle should render with a U of 0.98 to + // 1.0, not 0.98 to 0.0. If we don't do this fixup, there will be a visible seam across one side of the sphere. + // + // Luckily this is relatively easy to fix. There is a straight edge which runs down the prime meridian of the + // completed sphere. If you imagine the vertices along that edge, they circumscribe a semicircular arc starting at + // y=1 and ending at y=-1, and sweeping across the range of z=0 to z=1. x stays zero. It's along this edge that we + // need to duplicate our vertices - and provide the correct texture coordinates. + const size_t preFixupVertexCount = vertices.size(); + for (size_t i = 0; i < preFixupVertexCount; ++i) + { + // This vertex is on the prime meridian if position.x and texcoord.u are both zero (allowing for small epsilon). + const bool isOnPrimeMeridian = XMVector2NearEqual( + XMVectorSet(vertices[i].position.x, vertices[i].textureCoordinate.x, 0.0f, 0.0f), + XMVectorZero(), + XMVectorSplatEpsilon()); + + if (isOnPrimeMeridian) + { + size_t newIndex = vertices.size(); // the index of this vertex that we're about to add + CheckIndexOverflow(newIndex); + + // copy this vertex, correct the texture coordinate, and add the vertex + VertexPositionNormalTexture v = vertices[i]; + v.textureCoordinate.x = 1.0f; + vertices.push_back(v); + + // Now find all the triangles which contain this vertex and update them if necessary + for (size_t j = 0; j < indices.size(); j += 3) + { + uint16_t* triIndex0 = &indices[j + 0]; + uint16_t* triIndex1 = &indices[j + 1]; + uint16_t* triIndex2 = &indices[j + 2]; + + if (*triIndex0 == i) + { + // nothing; just keep going + } + else if (*triIndex1 == i) + { + std::swap(triIndex0, triIndex1); // swap the pointers (not the values) + } + else if (*triIndex2 == i) + { + std::swap(triIndex0, triIndex2); // swap the pointers (not the values) + } + else + { + // this triangle doesn't use the vertex we're interested in + continue; + } + + // If we got to this point then triIndex0 is the pointer to the index to the vertex we're looking at + assert(*triIndex0 == i); + assert(*triIndex1 != i && *triIndex2 != i); // assume no degenerate triangles + + const VertexPositionNormalTexture& v0 = vertices[*triIndex0]; + const VertexPositionNormalTexture& v1 = vertices[*triIndex1]; + const VertexPositionNormalTexture& v2 = vertices[*triIndex2]; + + // check the other two vertices to see if we might need to fix this triangle + + if (abs(v0.textureCoordinate.x - v1.textureCoordinate.x) > 0.5f || + abs(v0.textureCoordinate.x - v2.textureCoordinate.x) > 0.5f) + { + // yep; replace the specified index to point to the new, corrected vertex + *triIndex0 = static_cast(newIndex); + } + } + } + } + + // And one last fix we need to do: the poles. A common use-case of a sphere mesh is to map a rectangular texture onto + // it. If that happens, then the poles become singularities which map the entire top and bottom rows of the texture + // onto a single point. In general there's no real way to do that right. But to match the behavior of non-geodesic + // spheres, we need to duplicate the pole vertex for every triangle that uses it. This will introduce seams near the + // poles, but reduce stretching. + auto const fixPole = [&](size_t poleIndex) + { + const auto& poleVertex = vertices[poleIndex]; + bool overwrittenPoleVertex = false; // overwriting the original pole vertex saves us one vertex + + for (size_t i = 0; i < indices.size(); i += 3) + { + // These pointers point to the three indices which make up this triangle. pPoleIndex is the pointer to the + // entry in the index array which represents the pole index, and the other two pointers point to the other + // two indices making up this triangle. + uint16_t* pPoleIndex; + uint16_t* pOtherIndex0; + uint16_t* pOtherIndex1; + if (indices[i + 0] == poleIndex) + { + pPoleIndex = &indices[i + 0]; + pOtherIndex0 = &indices[i + 1]; + pOtherIndex1 = &indices[i + 2]; + } + else if (indices[i + 1] == poleIndex) + { + pPoleIndex = &indices[i + 1]; + pOtherIndex0 = &indices[i + 2]; + pOtherIndex1 = &indices[i + 0]; + } + else if (indices[i + 2] == poleIndex) + { + pPoleIndex = &indices[i + 2]; + pOtherIndex0 = &indices[i + 0]; + pOtherIndex1 = &indices[i + 1]; + } + else + { + continue; + } + + const auto& otherVertex0 = vertices[*pOtherIndex0]; + const auto& otherVertex1 = vertices[*pOtherIndex1]; + + // Calculate the texcoords for the new pole vertex, add it to the vertices and update the index + VertexPositionNormalTexture newPoleVertex = poleVertex; + newPoleVertex.textureCoordinate.x = (otherVertex0.textureCoordinate.x + otherVertex1.textureCoordinate.x) / 2; + newPoleVertex.textureCoordinate.y = poleVertex.textureCoordinate.y; + + if (!overwrittenPoleVertex) + { + vertices[poleIndex] = newPoleVertex; + overwrittenPoleVertex = true; + } + else + { + CheckIndexOverflow(vertices.size()); + + *pPoleIndex = static_cast(vertices.size()); + vertices.push_back(newPoleVertex); + } + } + }; + + fixPole(northPoleIndex); + fixPole(southPoleIndex); + + // Build RH above + if (!rhcoords) + ReverseWinding(indices, vertices); +} + + +//-------------------------------------------------------------------------------------- +// Cylinder / Cone +//-------------------------------------------------------------------------------------- +namespace +{ + // Helper computes a point on a unit circle, aligned to the x/z plane and centered on the origin. + inline XMVECTOR GetCircleVector(size_t i, size_t tessellation) noexcept + { + const float angle = float(i) * XM_2PI / float(tessellation); + float dx, dz; + + XMScalarSinCos(&dx, &dz, angle); + + const XMVECTORF32 v = { { { dx, 0, dz, 0 } } }; + return v; + } + + inline XMVECTOR GetCircleTangent(size_t i, size_t tessellation) noexcept + { + const float angle = (float(i) * XM_2PI / float(tessellation)) + XM_PIDIV2; + float dx, dz; + + XMScalarSinCos(&dx, &dz, angle); + + const XMVECTORF32 v = { { { dx, 0, dz, 0 } } }; + return v; + } + + + // Helper creates a triangle fan to close the end of a cylinder / cone + void CreateCylinderCap(VertexCollection& vertices, IndexCollection& indices, size_t tessellation, float height, float radius, bool isTop) + { + // Create cap indices. + for (size_t i = 0; i < tessellation - 2; i++) + { + size_t i1 = (i + 1) % tessellation; + size_t i2 = (i + 2) % tessellation; + + if (isTop) + { + std::swap(i1, i2); + } + + const size_t vbase = vertices.size(); + index_push_back(indices, vbase); + index_push_back(indices, vbase + i1); + index_push_back(indices, vbase + i2); + } + + // Which end of the cylinder is this? + XMVECTOR normal = g_XMIdentityR1; + XMVECTOR textureScale = g_XMNegativeOneHalf; + + if (!isTop) + { + normal = XMVectorNegate(normal); + textureScale = XMVectorMultiply(textureScale, g_XMNegateX); + } + + // Create cap vertices. + for (size_t i = 0; i < tessellation; i++) + { + const XMVECTOR circleVector = GetCircleVector(i, tessellation); + + const XMVECTOR position = XMVectorAdd(XMVectorScale(circleVector, radius), XMVectorScale(normal, height)); + + const XMVECTOR textureCoordinate = XMVectorMultiplyAdd(XMVectorSwizzle<0, 2, 3, 3>(circleVector), textureScale, g_XMOneHalf); + + vertices.push_back(VertexPositionNormalTexture(position, normal, textureCoordinate)); + } + } +} + +void DirectX::ComputeCylinder(VertexCollection& vertices, IndexCollection& indices, float height, float diameter, size_t tessellation, bool rhcoords) +{ + vertices.clear(); + indices.clear(); + + if (tessellation < 3) + throw std::invalid_argument("tesselation parameter must be at least 3"); + + height /= 2; + + const XMVECTOR topOffset = XMVectorScale(g_XMIdentityR1, height); + + const float radius = diameter / 2; + const size_t stride = tessellation + 1; + + // Create a ring of triangles around the outside of the cylinder. + for (size_t i = 0; i <= tessellation; i++) + { + const XMVECTOR normal = GetCircleVector(i, tessellation); + + const XMVECTOR sideOffset = XMVectorScale(normal, radius); + + const float u = float(i) / float(tessellation); + + const XMVECTOR textureCoordinate = XMLoadFloat(&u); + + vertices.push_back(VertexPositionNormalTexture(XMVectorAdd(sideOffset, topOffset), normal, textureCoordinate)); + vertices.push_back(VertexPositionNormalTexture(XMVectorSubtract(sideOffset, topOffset), normal, XMVectorAdd(textureCoordinate, g_XMIdentityR1))); + + index_push_back(indices, i * 2); + index_push_back(indices, (i * 2 + 2) % (stride * 2)); + index_push_back(indices, i * 2 + 1); + + index_push_back(indices, i * 2 + 1); + index_push_back(indices, (i * 2 + 2) % (stride * 2)); + index_push_back(indices, (i * 2 + 3) % (stride * 2)); + } + + // Create flat triangle fan caps to seal the top and bottom. + CreateCylinderCap(vertices, indices, tessellation, height, radius, true); + CreateCylinderCap(vertices, indices, tessellation, height, radius, false); + + // Build RH above + if (!rhcoords) + ReverseWinding(indices, vertices); +} + + +// Creates a cone primitive. +void DirectX::ComputeCone(VertexCollection& vertices, IndexCollection& indices, float diameter, float height, size_t tessellation, bool rhcoords) +{ + vertices.clear(); + indices.clear(); + + if (tessellation < 3) + throw std::invalid_argument("tesselation parameter must be at least 3"); + + height /= 2; + + const XMVECTOR topOffset = XMVectorScale(g_XMIdentityR1, height); + + const float radius = diameter / 2; + const size_t stride = tessellation + 1; + + // Create a ring of triangles around the outside of the cone. + for (size_t i = 0; i <= tessellation; i++) + { + const XMVECTOR circlevec = GetCircleVector(i, tessellation); + + const XMVECTOR sideOffset = XMVectorScale(circlevec, radius); + + const float u = float(i) / float(tessellation); + + const XMVECTOR textureCoordinate = XMLoadFloat(&u); + + const XMVECTOR pt = XMVectorSubtract(sideOffset, topOffset); + + XMVECTOR normal = XMVector3Cross( + GetCircleTangent(i, tessellation), + XMVectorSubtract(topOffset, pt)); + normal = XMVector3Normalize(normal); + + // Duplicate the top vertex for distinct normals + vertices.push_back(VertexPositionNormalTexture(topOffset, normal, g_XMZero)); + vertices.push_back(VertexPositionNormalTexture(pt, normal, XMVectorAdd(textureCoordinate, g_XMIdentityR1))); + + index_push_back(indices, i * 2); + index_push_back(indices, (i * 2 + 3) % (stride * 2)); + index_push_back(indices, (i * 2 + 1) % (stride * 2)); + } + + // Create flat triangle fan caps to seal the bottom. + CreateCylinderCap(vertices, indices, tessellation, height, radius, false); + + // Build RH above + if (!rhcoords) + ReverseWinding(indices, vertices); +} + + +//-------------------------------------------------------------------------------------- +// Torus +//-------------------------------------------------------------------------------------- +void DirectX::ComputeTorus(VertexCollection& vertices, IndexCollection& indices, float diameter, float thickness, size_t tessellation, bool rhcoords) +{ + vertices.clear(); + indices.clear(); + + if (tessellation < 3) + throw std::invalid_argument("tesselation parameter must be at least 3"); + + const size_t stride = tessellation + 1; + + // First we loop around the main ring of the torus. + for (size_t i = 0; i <= tessellation; i++) + { + const float u = float(i) / float(tessellation); + + const float outerAngle = float(i) * XM_2PI / float(tessellation) - XM_PIDIV2; + + // Create a transform matrix that will align geometry to + // slice perpendicularly though the current ring position. + const XMMATRIX transform = XMMatrixTranslation(diameter / 2, 0, 0) * XMMatrixRotationY(outerAngle); + + // Now we loop along the other axis, around the side of the tube. + for (size_t j = 0; j <= tessellation; j++) + { + const float v = 1 - float(j) / float(tessellation); + + const float innerAngle = float(j) * XM_2PI / float(tessellation) + XM_PI; + float dx, dy; + + XMScalarSinCos(&dy, &dx, innerAngle); + + // Create a vertex. + XMVECTOR normal = XMVectorSet(dx, dy, 0, 0); + XMVECTOR position = XMVectorScale(normal, thickness / 2); + const XMVECTOR textureCoordinate = XMVectorSet(u, v, 0, 0); + + position = XMVector3Transform(position, transform); + normal = XMVector3TransformNormal(normal, transform); + + vertices.push_back(VertexPositionNormalTexture(position, normal, textureCoordinate)); + + // And create indices for two triangles. + const size_t nextI = (i + 1) % stride; + const size_t nextJ = (j + 1) % stride; + + index_push_back(indices, i * stride + j); + index_push_back(indices, i * stride + nextJ); + index_push_back(indices, nextI * stride + j); + + index_push_back(indices, i * stride + nextJ); + index_push_back(indices, nextI * stride + nextJ); + index_push_back(indices, nextI * stride + j); + } + } + + // Build RH above + if (!rhcoords) + ReverseWinding(indices, vertices); +} + + +//-------------------------------------------------------------------------------------- +// Tetrahedron +//-------------------------------------------------------------------------------------- +void DirectX::ComputeTetrahedron(VertexCollection& vertices, IndexCollection& indices, float size, bool rhcoords) +{ + vertices.clear(); + indices.clear(); + + static const XMVECTORF32 verts[4] = + { + { { { 0.f, 0.f, 1.f, 0 } } }, + { { { 2.f*SQRT2 / 3.f, 0.f, -1.f / 3.f, 0 } } }, + { { { -SQRT2 / 3.f, SQRT6 / 3.f, -1.f / 3.f, 0 } } }, + { { { -SQRT2 / 3.f, -SQRT6 / 3.f, -1.f / 3.f, 0 } } } + }; + + static const uint32_t faces[4 * 3] = + { + 0, 1, 2, + 0, 2, 3, + 0, 3, 1, + 1, 3, 2, + }; + + for (size_t j = 0; j < std::size(faces); j += 3) + { + const uint32_t v0 = faces[j]; + const uint32_t v1 = faces[j + 1]; + const uint32_t v2 = faces[j + 2]; + + XMVECTOR normal = XMVector3Cross( + XMVectorSubtract(verts[v1].v, verts[v0].v), + XMVectorSubtract(verts[v2].v, verts[v0].v)); + normal = XMVector3Normalize(normal); + + const size_t base = vertices.size(); + index_push_back(indices, base); + index_push_back(indices, base + 1); + index_push_back(indices, base + 2); + + // Duplicate vertices to use face normals + XMVECTOR position = XMVectorScale(verts[v0], size); + vertices.push_back(VertexPositionNormalTexture(position, normal, g_XMZero /* 0, 0 */)); + + position = XMVectorScale(verts[v1], size); + vertices.push_back(VertexPositionNormalTexture(position, normal, g_XMIdentityR0 /* 1, 0 */)); + + position = XMVectorScale(verts[v2], size); + vertices.push_back(VertexPositionNormalTexture(position, normal, g_XMIdentityR1 /* 0, 1 */)); + } + + // Built LH above + if (rhcoords) + ReverseWinding(indices, vertices); + + assert(vertices.size() == 4 * 3); + assert(indices.size() == 4 * 3); +} + + +//-------------------------------------------------------------------------------------- +// Octahedron +//-------------------------------------------------------------------------------------- +void DirectX::ComputeOctahedron(VertexCollection& vertices, IndexCollection& indices, float size, bool rhcoords) +{ + vertices.clear(); + indices.clear(); + + static const XMVECTORF32 verts[6] = + { + { { { 1, 0, 0, 0 } } }, + { { { -1, 0, 0, 0 } } }, + { { { 0, 1, 0, 0 } } }, + { { { 0, -1, 0, 0 } } }, + { { { 0, 0, 1, 0 } } }, + { { { 0, 0, -1, 0 } } } + }; + + static const uint32_t faces[8 * 3] = + { + 4, 0, 2, + 4, 2, 1, + 4, 1, 3, + 4, 3, 0, + 5, 2, 0, + 5, 1, 2, + 5, 3, 1, + 5, 0, 3 + }; + + for (size_t j = 0; j < std::size(faces); j += 3) + { + const uint32_t v0 = faces[j]; + const uint32_t v1 = faces[j + 1]; + const uint32_t v2 = faces[j + 2]; + + XMVECTOR normal = XMVector3Cross( + XMVectorSubtract(verts[v1].v, verts[v0].v), + XMVectorSubtract(verts[v2].v, verts[v0].v)); + normal = XMVector3Normalize(normal); + + const size_t base = vertices.size(); + index_push_back(indices, base); + index_push_back(indices, base + 1); + index_push_back(indices, base + 2); + + // Duplicate vertices to use face normals + XMVECTOR position = XMVectorScale(verts[v0], size); + vertices.push_back(VertexPositionNormalTexture(position, normal, g_XMZero /* 0, 0 */)); + + position = XMVectorScale(verts[v1], size); + vertices.push_back(VertexPositionNormalTexture(position, normal, g_XMIdentityR0 /* 1, 0 */)); + + position = XMVectorScale(verts[v2], size); + vertices.push_back(VertexPositionNormalTexture(position, normal, g_XMIdentityR1 /* 0, 1*/)); + } + + // Built LH above + if (rhcoords) + ReverseWinding(indices, vertices); + + assert(vertices.size() == 8 * 3); + assert(indices.size() == 8 * 3); +} + + +//-------------------------------------------------------------------------------------- +// Dodecahedron +//-------------------------------------------------------------------------------------- +void DirectX::ComputeDodecahedron(VertexCollection& vertices, IndexCollection& indices, float size, bool rhcoords) +{ + vertices.clear(); + indices.clear(); + + constexpr float a = 1.f / SQRT3; + constexpr float b = 0.356822089773089931942f; // sqrt( ( 3 - sqrt(5) ) / 6 ) + constexpr float c = 0.934172358962715696451f; // sqrt( ( 3 + sqrt(5) ) / 6 ); + + static const XMVECTORF32 verts[20] = + { + { { { a, a, a, 0 } } }, + { { { a, a, -a, 0 } } }, + { { { a, -a, a, 0 } } }, + { { { a, -a, -a, 0 } } }, + { { { -a, a, a, 0 } } }, + { { { -a, a, -a, 0 } } }, + { { { -a, -a, a, 0 } } }, + { { { -a, -a, -a, 0 } } }, + { { { b, c, 0, 0 } } }, + { { { -b, c, 0, 0 } } }, + { { { b, -c, 0, 0 } } }, + { { { -b, -c, 0, 0 } } }, + { { { c, 0, b, 0 } } }, + { { { c, 0, -b, 0 } } }, + { { { -c, 0, b, 0 } } }, + { { { -c, 0, -b, 0 } } }, + { { { 0, b, c, 0 } } }, + { { { 0, -b, c, 0 } } }, + { { { 0, b, -c, 0 } } }, + { { { 0, -b, -c, 0 } } } + }; + + static const uint32_t faces[12 * 5] = + { + 0, 8, 9, 4, 16, + 0, 16, 17, 2, 12, + 12, 2, 10, 3, 13, + 9, 5, 15, 14, 4, + 3, 19, 18, 1, 13, + 7, 11, 6, 14, 15, + 0, 12, 13, 1, 8, + 8, 1, 18, 5, 9, + 16, 4, 14, 6, 17, + 6, 11, 10, 2, 17, + 7, 15, 5, 18, 19, + 7, 19, 3, 10, 11, + }; + + static const XMVECTORF32 textureCoordinates[5] = + { + { { { 0.654508f, 0.0244717f, 0, 0 } } }, + { { { 0.0954915f, 0.206107f, 0, 0 } } }, + { { { 0.0954915f, 0.793893f, 0, 0 } } }, + { { { 0.654508f, 0.975528f, 0, 0 } } }, + { { { 1.f, 0.5f, 0, 0 } } } + }; + + static const uint32_t textureIndex[12][5] = + { + { 0, 1, 2, 3, 4 }, + { 2, 3, 4, 0, 1 }, + { 4, 0, 1, 2, 3 }, + { 1, 2, 3, 4, 0 }, + { 2, 3, 4, 0, 1 }, + { 0, 1, 2, 3, 4 }, + { 1, 2, 3, 4, 0 }, + { 4, 0, 1, 2, 3 }, + { 4, 0, 1, 2, 3 }, + { 1, 2, 3, 4, 0 }, + { 0, 1, 2, 3, 4 }, + { 2, 3, 4, 0, 1 }, + }; + + size_t t = 0; + for (size_t j = 0; j < std::size(faces); j += 5, ++t) + { + const uint32_t v0 = faces[j]; + const uint32_t v1 = faces[j + 1]; + const uint32_t v2 = faces[j + 2]; + const uint32_t v3 = faces[j + 3]; + const uint32_t v4 = faces[j + 4]; + + XMVECTOR normal = XMVector3Cross( + XMVectorSubtract(verts[v1].v, verts[v0].v), + XMVectorSubtract(verts[v2].v, verts[v0].v)); + normal = XMVector3Normalize(normal); + + const size_t base = vertices.size(); + + index_push_back(indices, base); + index_push_back(indices, base + 1); + index_push_back(indices, base + 2); + + index_push_back(indices, base); + index_push_back(indices, base + 2); + index_push_back(indices, base + 3); + + index_push_back(indices, base); + index_push_back(indices, base + 3); + index_push_back(indices, base + 4); + + // Duplicate vertices to use face normals + XMVECTOR position = XMVectorScale(verts[v0], size); + vertices.push_back(VertexPositionNormalTexture(position, normal, textureCoordinates[textureIndex[t][0]])); + + position = XMVectorScale(verts[v1], size); + vertices.push_back(VertexPositionNormalTexture(position, normal, textureCoordinates[textureIndex[t][1]])); + + position = XMVectorScale(verts[v2], size); + vertices.push_back(VertexPositionNormalTexture(position, normal, textureCoordinates[textureIndex[t][2]])); + + position = XMVectorScale(verts[v3], size); + vertices.push_back(VertexPositionNormalTexture(position, normal, textureCoordinates[textureIndex[t][3]])); + + position = XMVectorScale(verts[v4], size); + vertices.push_back(VertexPositionNormalTexture(position, normal, textureCoordinates[textureIndex[t][4]])); + } + + // Built LH above + if (rhcoords) + ReverseWinding(indices, vertices); + + assert(vertices.size() == 12 * 5); + assert(indices.size() == 12 * 3 * 3); +} + + +//-------------------------------------------------------------------------------------- +// Icosahedron +//-------------------------------------------------------------------------------------- +void DirectX::ComputeIcosahedron(VertexCollection& vertices, IndexCollection& indices, float size, bool rhcoords) +{ + vertices.clear(); + indices.clear(); + + constexpr float t = 1.618033988749894848205f; // (1 + sqrt(5)) / 2 + constexpr float t2 = 1.519544995837552493271f; // sqrt( 1 + sqr( (1 + sqrt(5)) / 2 ) ) + + static const XMVECTORF32 verts[12] = + { + { { { t / t2, 1.f / t2, 0, 0 } } }, + { { { -t / t2, 1.f / t2, 0, 0 } } }, + { { { t / t2, -1.f / t2, 0, 0 } } }, + { { { -t / t2, -1.f / t2, 0, 0 } } }, + { { { 1.f / t2, 0, t / t2, 0 } } }, + { { { 1.f / t2, 0, -t / t2, 0 } } }, + { { { -1.f / t2, 0, t / t2, 0 } } }, + { { { -1.f / t2, 0, -t / t2, 0 } } }, + { { { 0, t / t2, 1.f / t2, 0 } } }, + { { { 0, -t / t2, 1.f / t2, 0 } } }, + { { { 0, t / t2, -1.f / t2, 0 } } }, + { { { 0, -t / t2, -1.f / t2, 0 } } } + }; + + static const uint32_t faces[20 * 3] = + { + 0, 8, 4, + 0, 5, 10, + 2, 4, 9, + 2, 11, 5, + 1, 6, 8, + 1, 10, 7, + 3, 9, 6, + 3, 7, 11, + 0, 10, 8, + 1, 8, 10, + 2, 9, 11, + 3, 11, 9, + 4, 2, 0, + 5, 0, 2, + 6, 1, 3, + 7, 3, 1, + 8, 6, 4, + 9, 4, 6, + 10, 5, 7, + 11, 7, 5 + }; + + for (size_t j = 0; j < std::size(faces); j += 3) + { + const uint32_t v0 = faces[j]; + const uint32_t v1 = faces[j + 1]; + const uint32_t v2 = faces[j + 2]; + + XMVECTOR normal = XMVector3Cross( + XMVectorSubtract(verts[v1].v, verts[v0].v), + XMVectorSubtract(verts[v2].v, verts[v0].v)); + normal = XMVector3Normalize(normal); + + const size_t base = vertices.size(); + index_push_back(indices, base); + index_push_back(indices, base + 1); + index_push_back(indices, base + 2); + + // Duplicate vertices to use face normals + XMVECTOR position = XMVectorScale(verts[v0], size); + vertices.push_back(VertexPositionNormalTexture(position, normal, g_XMZero /* 0, 0 */)); + + position = XMVectorScale(verts[v1], size); + vertices.push_back(VertexPositionNormalTexture(position, normal, g_XMIdentityR0 /* 1, 0 */)); + + position = XMVectorScale(verts[v2], size); + vertices.push_back(VertexPositionNormalTexture(position, normal, g_XMIdentityR1 /* 0, 1 */)); + } + + // Built LH above + if (rhcoords) + ReverseWinding(indices, vertices); + + assert(vertices.size() == 20 * 3); + assert(indices.size() == 20 * 3); +} + + +//-------------------------------------------------------------------------------------- +// Teapot +//-------------------------------------------------------------------------------------- + +// Include the teapot control point data. +namespace +{ +#include "TeapotData.inc" + + // Tessellates the specified bezier patch. + void XM_CALLCONV TessellatePatch(VertexCollection& vertices, IndexCollection& indices, TeapotPatch const& patch, size_t tessellation, FXMVECTOR scale, bool isMirrored) + { + // Look up the 16 control points for this patch. + XMVECTOR controlPoints[16] = {}; + + for (int i = 0; i < 16; i++) + { + controlPoints[i] = XMVectorMultiply(TeapotControlPoints[patch.indices[i]], scale); + } + + // Create the index data. + size_t vbase = vertices.size(); + Bezier::CreatePatchIndices(tessellation, isMirrored, [&](size_t index) + { + index_push_back(indices, vbase + index); + }); + + // Create the vertex data. + Bezier::CreatePatchVertices(controlPoints, tessellation, isMirrored, [&](FXMVECTOR position, FXMVECTOR normal, FXMVECTOR textureCoordinate) + { + vertices.push_back(VertexPositionNormalTexture(position, normal, textureCoordinate)); + }); + } +} + + +// Creates a teapot primitive. +void DirectX::ComputeTeapot(VertexCollection& vertices, IndexCollection& indices, float size, size_t tessellation, bool rhcoords) +{ + vertices.clear(); + indices.clear(); + + if (tessellation < 1) + throw std::invalid_argument("tesselation parameter must be non-zero"); + + const XMVECTOR scaleVector = XMVectorReplicate(size); + + const XMVECTOR scaleNegateX = XMVectorMultiply(scaleVector, g_XMNegateX); + const XMVECTOR scaleNegateZ = XMVectorMultiply(scaleVector, g_XMNegateZ); + const XMVECTOR scaleNegateXZ = XMVectorMultiply(scaleVector, XMVectorMultiply(g_XMNegateX, g_XMNegateZ)); + + for (size_t i = 0; i < std::size(TeapotPatches); i++) + { + TeapotPatch const& patch = TeapotPatches[i]; + + // Because the teapot is symmetrical from left to right, we only store + // data for one side, then tessellate each patch twice, mirroring in X. + TessellatePatch(vertices, indices, patch, tessellation, scaleVector, false); + TessellatePatch(vertices, indices, patch, tessellation, scaleNegateX, true); + + if (patch.mirrorZ) + { + // Some parts of the teapot (the body, lid, and rim, but not the + // handle or spout) are also symmetrical from front to back, so + // we tessellate them four times, mirroring in Z as well as X. + TessellatePatch(vertices, indices, patch, tessellation, scaleNegateZ, true); + TessellatePatch(vertices, indices, patch, tessellation, scaleNegateXZ, false); + } + } + + // Built RH above + if (!rhcoords) + ReverseWinding(indices, vertices); +} diff --git a/Common/DirectXTK12/Src/Geometry.h b/Common/DirectXTK12/Src/Geometry.h new file mode 100644 index 0000000..c5a5475 --- /dev/null +++ b/Common/DirectXTK12/Src/Geometry.h @@ -0,0 +1,29 @@ +//-------------------------------------------------------------------------------------- +// File: Geometry.h +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248929 +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "VertexTypes.h" + +namespace DirectX +{ + using VertexCollection = std::vector; + using IndexCollection = std::vector; + + void ComputeBox(VertexCollection& vertices, IndexCollection& indices, const XMFLOAT3& size, bool rhcoords, bool invertn); + void ComputeSphere(VertexCollection& vertices, IndexCollection& indices, float diameter, size_t tessellation, bool rhcoords, bool invertn); + void ComputeGeoSphere(VertexCollection& vertices, IndexCollection& indices, float diameter, size_t tessellation, bool rhcoords); + void ComputeCylinder(VertexCollection& vertices, IndexCollection& indices, float height, float diameter, size_t tessellation, bool rhcoords); + void ComputeCone(VertexCollection& vertices, IndexCollection& indices, float diameter, float height, size_t tessellation, bool rhcoords); + void ComputeTorus(VertexCollection& vertices, IndexCollection& indices, float diameter, float thickness, size_t tessellation, bool rhcoords); + void ComputeTetrahedron(VertexCollection& vertices, IndexCollection& indices, float size, bool rhcoords); + void ComputeOctahedron(VertexCollection& vertices, IndexCollection& indices, float size, bool rhcoords); + void ComputeDodecahedron(VertexCollection& vertices, IndexCollection& indices, float size, bool rhcoords); + void ComputeIcosahedron(VertexCollection& vertices, IndexCollection& indices, float size, bool rhcoords); + void ComputeTeapot(VertexCollection& vertices, IndexCollection& indices, float size, size_t tessellation, bool rhcoords); +} diff --git a/Common/DirectXTK12/Src/GraphicsMemory.cpp b/Common/DirectXTK12/Src/GraphicsMemory.cpp new file mode 100644 index 0000000..b3dbed1 --- /dev/null +++ b/Common/DirectXTK12/Src/GraphicsMemory.cpp @@ -0,0 +1,635 @@ +//-------------------------------------------------------------------------------------- +// File: GraphicsMemory.cpp +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "pch.h" +#include "GraphicsMemory.h" +#include "PlatformHelpers.h" +#include "LinearAllocator.h" + +#ifdef USING_PIX_CUSTOM_MEMORY_EVENTS +#include +#include +#endif + +using namespace DirectX; +using Microsoft::WRL::ComPtr; +using ScopedLock = std::lock_guard; + +namespace +{ + constexpr size_t MinPageSize = 64 * 1024; + constexpr size_t MinAllocSize = 4 * 1024; + constexpr size_t AllocatorIndexShift = 12; // start block sizes at 4KB + constexpr size_t AllocatorPoolCount = 21; // allocation sizes up to 2GB supported + constexpr size_t PoolIndexScale = 1; // multiply the allocation size this amount to push large values into the next bucket + + static_assert((1 << AllocatorIndexShift) == MinAllocSize, "1 << AllocatorIndexShift must == MinPageSize (in KiB)"); + static_assert((MinPageSize & (MinPageSize - 1)) == 0, "MinPageSize size must be a power of 2"); + static_assert((MinAllocSize & (MinAllocSize - 1)) == 0, "MinAllocSize size must be a power of 2"); + static_assert(MinAllocSize >= (4 * 1024), "MinAllocSize size must be greater than 4K"); + + constexpr size_t NextPow2(size_t x) noexcept + { + x--; + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + #ifdef _WIN64 + x |= x >> 32; + #endif + return ++x; + } + + inline size_t GetPoolIndexFromSize(size_t x) noexcept + { + const size_t allocatorPageSize = x >> AllocatorIndexShift; + // gives a value from range: + // 0 - sub-4k allocator + // 1 - 4k allocator + // 2 - 8k allocator + // 4 - 16k allocator + // etc... + // Need to convert to an index. + + #ifdef _MSC_VER + unsigned long bitIndex = 0; + + #ifdef _WIN64 + return _BitScanForward64(&bitIndex, allocatorPageSize) ? bitIndex + 1 : 0; + #else + return _BitScanForward(&bitIndex, static_cast(allocatorPageSize)) ? bitIndex + 1 : 0; + #endif + + #elif defined(__GNUC__) + + #ifdef __LP64__ + return static_cast(__builtin_ffsll(static_cast(allocatorPageSize))); + #else + return static_cast(__builtin_ffs(static_cast(allocatorPageSize))); + #endif + + #else + #error Unknown forward bit-scan syntax + #endif + } + + inline size_t GetPageSizeFromPoolIndex(size_t x) noexcept + { + x = (x == 0) ? 0 : x - 1; // clamp to zero + return std::max(MinPageSize, size_t(1) << (x + AllocatorIndexShift)); + } + + //-------------------------------------------------------------------------------------- + // DeviceAllocator : honors memory requests associated with a particular device + //-------------------------------------------------------------------------------------- + class DeviceAllocator + { + public: + DeviceAllocator(_In_ ID3D12Device* device) noexcept(false) + : mDevice(device) + { + if (!device) + throw std::invalid_argument("Invalid device parameter"); + + for (size_t i = 0; i < mPools.size(); ++i) + { + size_t pageSize = GetPageSizeFromPoolIndex(i); + mPools[i] = std::make_unique( + mDevice.Get(), + pageSize); + } + } + + DeviceAllocator(DeviceAllocator&&) = delete; + DeviceAllocator& operator= (DeviceAllocator&&) = delete; + + DeviceAllocator(DeviceAllocator const&) = delete; + DeviceAllocator& operator= (DeviceAllocator const&) = delete; + + // Explicitly destroy LinearAllocators inside a critical section + ~DeviceAllocator() + { + const ScopedLock lock(mMutex); + + for (auto& allocator : mPools) + { + allocator.reset(); + } + } + + GraphicsResource Alloc(_In_ size_t size, _In_ size_t alignment) + { + ScopedLock lock(mMutex); + + // Which memory pool does it live in? + const size_t poolSize = NextPow2((alignment + size) * PoolIndexScale); + const size_t poolIndex = GetPoolIndexFromSize(poolSize); + assert(poolIndex < mPools.size()); + + // If the allocator isn't initialized yet, do so now + auto& allocator = mPools[poolIndex]; + assert(allocator != nullptr); + assert(poolSize < MinPageSize || poolSize == allocator->PageSize()); + + auto page = allocator->FindPageForAlloc(size, alignment); + if (!page) + { + DebugTrace("GraphicsMemory failed to allocate page (%zu requested bytes, %zu alignment)\n", size, alignment); + throw std::bad_alloc(); + } + + size_t offset = page->Suballocate(size, alignment); + + // Return the information to the user + return GraphicsResource( + page, + page->GpuAddress() + offset, + page->UploadResource(), + static_cast(page->BaseMemory()) + offset, + offset, + size); + } + + // Submit page fences to the command queue + void KickFences(_In_ ID3D12CommandQueue* commandQueue) + { + ScopedLock lock(mMutex); + + for (auto& i : mPools) + { + if (i) + { + i->RetirePendingPages(); + i->FenceCommittedPages(commandQueue); + } + } + } + + void GarbageCollect() + { + ScopedLock lock(mMutex); + + for (auto& i : mPools) + { + if (i) + { + i->Shrink(); + } + } + } + + void GetStatistics(GraphicsMemoryStatistics& stats) const + { + size_t totalPageCount = 0; + size_t committedMemoryUsage = 0; + size_t totalMemoryUsage = 0; + + ScopedLock lock(mMutex); + + for (auto& i : mPools) + { + if (i) + { + totalPageCount += i->TotalPageCount(); + committedMemoryUsage += i->CommittedMemoryUsage(); + totalMemoryUsage += i->TotalMemoryUsage(); + } + } + + stats = {}; + stats.committedMemory = committedMemoryUsage; + stats.totalMemory = totalMemoryUsage; + stats.totalPages = totalPageCount; + } + + #if !(defined(_XBOX_ONE) && defined(_TITLE)) && !defined(_GAMING_XBOX) + ID3D12Device* GetDevice() const noexcept { return mDevice.Get(); } + #endif + + private: + ComPtr mDevice; + std::array, AllocatorPoolCount> mPools; + mutable std::mutex mMutex; + }; + +#ifdef USING_PIX_CUSTOM_MEMORY_EVENTS + constexpr uint16_t c_PIXAllocatorID = 1001; +#endif +} // anonymous namespace + + +//-------------------------------------------------------------------------------------- +// GraphicsMemory::Impl +//-------------------------------------------------------------------------------------- + +class GraphicsMemory::Impl +{ +public: + Impl(GraphicsMemory* owner) noexcept(false) + : mOwner(owner) + , m_peakCommited(0) + , m_peakBytes(0) + , m_peakPages(0) + { + #if (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) + if (s_graphicsMemory) + { + throw std::logic_error("GraphicsMemory is a singleton"); + } + + s_graphicsMemory = this; + + #endif + #ifdef USING_PIX_CUSTOM_MEMORY_EVENTS + DebugTrace("INFO: GraphicsMemory PIX custom memory tracking events enabled (Allocator ID %u)\n", c_PIXAllocatorID); + #endif + } + + Impl(Impl&&) = default; + Impl& operator= (Impl&&) = default; + + Impl(Impl const&) = delete; + Impl& operator= (Impl const&) = delete; + + ~Impl() + { + #if (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) + s_graphicsMemory = nullptr; + #else + if (mDeviceAllocator && mDeviceAllocator->GetDevice()) + { + s_graphicsMemory.erase(mDeviceAllocator->GetDevice()); + } + #endif + mDeviceAllocator.reset(); + } + + void Initialize(_In_ ID3D12Device* device) + { + mDeviceAllocator = std::make_unique(device); + + #if !(defined(_XBOX_ONE) && defined(_TITLE)) && !defined(_GAMING_XBOX) + if (s_graphicsMemory.find(device) != s_graphicsMemory.cend()) + { + throw std::logic_error("GraphicsMemory is a per-device singleton"); + } + s_graphicsMemory[device] = this; + #endif + } + + GraphicsResource Allocate(size_t size, size_t alignment) + { + return mDeviceAllocator->Alloc(size, alignment); + } + + void Commit(_In_ ID3D12CommandQueue* commandQueue) + { + mDeviceAllocator->KickFences(commandQueue); + } + + void GarbageCollect() + { + mDeviceAllocator->GarbageCollect(); + } + + void GetStatistics(GraphicsMemoryStatistics& stats) + { + mDeviceAllocator->GetStatistics(stats); + + if (stats.committedMemory > m_peakCommited) + { + m_peakCommited = stats.committedMemory; + } + stats.peakCommitedMemory = m_peakCommited; + + if (stats.totalMemory > m_peakBytes) + { + m_peakBytes = stats.totalMemory; + } + stats.peakTotalMemory = m_peakBytes; + + if (stats.totalPages > m_peakPages) + { + m_peakPages = stats.totalPages; + } + stats.peakTotalPages = m_peakPages; + } + + void ResetStatistics() + { + m_peakCommited = 0; + m_peakBytes = 0; + m_peakPages = 0; + } + + GraphicsMemory* mOwner; +#if (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) + static GraphicsMemory::Impl* s_graphicsMemory; +#else + static std::map s_graphicsMemory; +#endif + +private: + std::unique_ptr mDeviceAllocator; + + size_t m_peakCommited; + size_t m_peakBytes; + size_t m_peakPages; +}; + +#if (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) +GraphicsMemory::Impl* GraphicsMemory::Impl::s_graphicsMemory = nullptr; +#else +std::map GraphicsMemory::Impl::s_graphicsMemory; +#endif + + +//-------------------------------------------------------------------------------------- +// GraphicsMemory +//-------------------------------------------------------------------------------------- + +// Public constructor. +GraphicsMemory::GraphicsMemory(_In_ ID3D12Device* device) + : pImpl(std::make_unique(this)) +{ + pImpl->Initialize(device); +} + + +// Move constructor. +GraphicsMemory::GraphicsMemory(GraphicsMemory&& moveFrom) noexcept + : pImpl(std::move(moveFrom.pImpl)) +{ + pImpl->mOwner = this; +} + + +// Move assignment. +GraphicsMemory& GraphicsMemory::operator= (GraphicsMemory&& moveFrom) noexcept +{ + pImpl = std::move(moveFrom.pImpl); + pImpl->mOwner = this; + return *this; +} + + +// Public destructor. +GraphicsMemory::~GraphicsMemory() = default; + + +GraphicsResource GraphicsMemory::AllocateImpl(size_t size, size_t alignment) +{ + assert(alignment >= 4); // Should use at least DWORD alignment + return pImpl->Allocate(size, alignment); +} + + +void GraphicsMemory::Commit(_In_ ID3D12CommandQueue* commandQueue) +{ + pImpl->Commit(commandQueue); +} + + +void GraphicsMemory::GarbageCollect() +{ + pImpl->GarbageCollect(); +} + +GraphicsMemoryStatistics GraphicsMemory::GetStatistics() +{ + GraphicsMemoryStatistics stats; + pImpl->GetStatistics(stats); + return stats; +} + +void GraphicsMemory::ResetStatistics() +{ + pImpl->ResetStatistics(); +} + +#if (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) +GraphicsMemory& GraphicsMemory::Get(_In_opt_ ID3D12Device*) +{ + if (!Impl::s_graphicsMemory || !Impl::s_graphicsMemory->mOwner) + throw std::logic_error("GraphicsMemory singleton not created"); + + return *Impl::s_graphicsMemory->mOwner; +} +#else +GraphicsMemory& GraphicsMemory::Get(_In_opt_ ID3D12Device* device) +{ + if (Impl::s_graphicsMemory.empty()) + throw std::logic_error("GraphicsMemory singleton not created"); + + std::map::const_iterator it; + if (!device) + { + // Should only use nullptr for device for single GPU usage + assert(Impl::s_graphicsMemory.size() == 1); + + it = Impl::s_graphicsMemory.cbegin(); + } + else + { + it = Impl::s_graphicsMemory.find(device); + } + + if (it == Impl::s_graphicsMemory.cend() || !it->second->mOwner) + throw std::logic_error("GraphicsMemory per-device singleton not created"); + + return *it->second->mOwner; +} +#endif + +#ifdef USING_PIX_CUSTOM_MEMORY_EVENTS +__declspec(allocator) +void* GraphicsMemory::ReportCustomMemoryAlloc(void* pMem, size_t size, UINT64 metadata) +{ + PIXRecordMemoryAllocationEvent(c_PIXAllocatorID, pMem, size, metadata); + return pMem; +} +#endif + + +//-------------------------------------------------------------------------------------- +// GraphicsResource smart-pointer interface +//-------------------------------------------------------------------------------------- + +GraphicsResource::GraphicsResource() noexcept + : mPage(nullptr) + , mGpuAddress{} + , mResource(nullptr) + , mMemory(nullptr) + , mBufferOffset(0) + , mSize(0) +{ +} + +GraphicsResource::GraphicsResource( + _In_ LinearAllocatorPage* page, + _In_ D3D12_GPU_VIRTUAL_ADDRESS gpuAddress, + _In_ ID3D12Resource* resource, + _In_ void* memory, + _In_ size_t offset, + _In_ size_t size) noexcept + : mPage(page) + , mGpuAddress(gpuAddress) + , mResource(resource) + , mMemory(memory) + , mBufferOffset(offset) + , mSize(size) +{ + assert(mPage != nullptr); + mPage->AddRef(); +} + +GraphicsResource::GraphicsResource(GraphicsResource&& other) noexcept + : mPage(nullptr) + , mGpuAddress{} + , mResource(nullptr) + , mMemory(nullptr) + , mBufferOffset(0) + , mSize(0) +{ + Reset(std::move(other)); +} + +GraphicsResource::~GraphicsResource() +{ + if (mPage) + { +#ifdef USING_PIX_CUSTOM_MEMORY_EVENTS + PIXRecordMemoryFreeEvent(c_PIXAllocatorID, reinterpret_cast(mGpuAddress), mSize, 0); +#endif + + mPage->Release(); + mPage = nullptr; + } +} + +GraphicsResource&& GraphicsResource::operator= (GraphicsResource&& other) noexcept +{ + Reset(std::move(other)); + return std::move(*this); +} + +void GraphicsResource::Reset() noexcept +{ + if (mPage) + { +#ifdef USING_PIX_CUSTOM_MEMORY_EVENTS + PIXRecordMemoryFreeEvent(c_PIXAllocatorID, reinterpret_cast(mGpuAddress), mSize, 0); +#endif + + mPage->Release(); + mPage = nullptr; + } + + mGpuAddress = {}; + mResource = nullptr; + mMemory = nullptr; + mBufferOffset = 0; + mSize = 0; +} + +void GraphicsResource::Reset(GraphicsResource&& alloc) noexcept +{ + if (mPage) + { +#ifdef USING_PIX_CUSTOM_MEMORY_EVENTS + PIXRecordMemoryFreeEvent(c_PIXAllocatorID, reinterpret_cast(mGpuAddress), mSize, 0); +#endif + + mPage->Release(); + mPage = nullptr; + } + + mGpuAddress = alloc.GpuAddress(); + mResource = alloc.Resource(); + mMemory = alloc.Memory(); + mBufferOffset = alloc.ResourceOffset(); + mSize = alloc.Size(); + mPage = alloc.mPage; + + alloc.mGpuAddress = {}; + alloc.mResource = nullptr; + alloc.mMemory = nullptr; + alloc.mBufferOffset = 0; + alloc.mSize = 0; + alloc.mPage = nullptr; +} + + +//-------------------------------------------------------------------------------------- +// SharedGraphicsResource +//-------------------------------------------------------------------------------------- + +SharedGraphicsResource::SharedGraphicsResource() noexcept + : mSharedResource(nullptr) +{ +} + +SharedGraphicsResource::SharedGraphicsResource(GraphicsResource&& resource) noexcept(false) + : mSharedResource(std::make_shared(std::move(resource))) +{ +} + +SharedGraphicsResource::SharedGraphicsResource(SharedGraphicsResource&& resource) noexcept + : mSharedResource(std::move(resource.mSharedResource)) +{ +} + +SharedGraphicsResource::SharedGraphicsResource(const SharedGraphicsResource& resource) noexcept + : mSharedResource(resource.mSharedResource) +{ +} + +SharedGraphicsResource::~SharedGraphicsResource() +{ +} + +SharedGraphicsResource&& SharedGraphicsResource::operator= (SharedGraphicsResource&& resource) noexcept +{ + mSharedResource = std::move(resource.mSharedResource); + return std::move(*this); +} + +SharedGraphicsResource&& SharedGraphicsResource::operator= (GraphicsResource&& resource) +{ + mSharedResource = std::make_shared(std::move(resource)); + return std::move(*this); +} + +SharedGraphicsResource& SharedGraphicsResource::operator= (const SharedGraphicsResource& resource) noexcept +{ + mSharedResource = resource.mSharedResource; + return *this; +} + +void SharedGraphicsResource::Reset() noexcept +{ + mSharedResource.reset(); +} + +void SharedGraphicsResource::Reset(GraphicsResource&& resource) +{ + mSharedResource = std::make_shared(std::move(resource)); +} + +void SharedGraphicsResource::Reset(SharedGraphicsResource&& resource) noexcept +{ + mSharedResource = std::move(resource.mSharedResource); +} + +void SharedGraphicsResource::Reset(const SharedGraphicsResource& resource) noexcept +{ + mSharedResource = resource.mSharedResource; +} diff --git a/Common/DirectXTK12/Src/Keyboard.cpp b/Common/DirectXTK12/Src/Keyboard.cpp new file mode 100644 index 0000000..ec3c502 --- /dev/null +++ b/Common/DirectXTK12/Src/Keyboard.cpp @@ -0,0 +1,675 @@ +//-------------------------------------------------------------------------------------- +// File: Keyboard.cpp +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248929 +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "pch.h" +#include "Keyboard.h" + +#include "PlatformHelpers.h" + +using namespace DirectX; +using Microsoft::WRL::ComPtr; + +static_assert(sizeof(Keyboard::State) == (256 / 8), "Size mismatch for State"); + +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wunused-function" +#endif + +namespace +{ + inline void KeyDown(int key, Keyboard::State& state) noexcept + { + if (key < 0 || key > 0xfe) + return; + + auto ptr = reinterpret_cast(&state); + + const unsigned int bf = 1u << (key & 0x1f); + ptr[(key >> 5)] |= bf; + } + + inline void KeyUp(int key, Keyboard::State& state) noexcept + { + if (key < 0 || key > 0xfe) + return; + + auto ptr = reinterpret_cast(&state); + + const unsigned int bf = 1u << (key & 0x1f); + ptr[(key >> 5)] &= ~bf; + } +} + + +#pragma region Implementations +#ifdef USING_GAMEINPUT + +#include + +//====================================================================================== +// GameInput +//====================================================================================== + +class Keyboard::Impl +{ +public: + Impl(Keyboard* owner) : + mOwner(owner), + mConnected(0), + mDeviceToken(0), + mKeyState{} + { + if (s_keyboard) + { + throw std::logic_error("Keyboard is a singleton"); + } + + s_keyboard = this; + + HRESULT hr = GameInputCreate(mGameInput.GetAddressOf()); + if (SUCCEEDED(hr)) + { + ThrowIfFailed(mGameInput->RegisterDeviceCallback( + nullptr, + GameInputKindKeyboard, + GameInputDeviceConnected, + GameInputBlockingEnumeration, + this, + OnGameInputDevice, + &mDeviceToken)); + } + else + { + DebugTrace("ERROR: GameInputCreate [keyboard] failed with %08X\n", static_cast(hr)); + #ifdef _GAMING_XBOX + ThrowIfFailed(hr); + #elif defined(_DEBUG) + DebugTrace( + "\t**** Check that the 'GameInput Service' is running on this system. ****\n" + "\t**** NOTE: No keys will be returned and IsConnected will return false. ****\n" + ); + #endif + } + } + + Impl(Impl&&) = default; + Impl& operator= (Impl&&) = default; + + Impl(Impl const&) = delete; + Impl& operator= (Impl const&) = delete; + + ~Impl() + { + if (mDeviceToken) + { + if (mGameInput) + { + if (!mGameInput->UnregisterCallback(mDeviceToken, UINT64_MAX)) + { + DebugTrace("ERROR: GameInput::UnregisterCallback [keyboard] failed"); + } + } + + mDeviceToken = 0; + } + + s_keyboard = nullptr; + } + + void GetState(State& state) const + { + state = {}; + + if (!mGameInput) + return; + + ComPtr reading; + if (SUCCEEDED(mGameInput->GetCurrentReading(GameInputKindKeyboard, nullptr, reading.GetAddressOf()))) + { + uint32_t readCount = reading->GetKeyState(c_MaxSimultaneousKeys, mKeyState); + for (size_t j = 0; j < readCount; ++j) + { + int vk = static_cast(mKeyState[j].virtualKey); + + // Workaround for known issues with VK_RSHIFT and VK_NUMLOCK + if (vk == 0) + { + switch (mKeyState[j].scanCode) + { + case 0xe036: vk = VK_RSHIFT; break; + case 0xe045: vk = VK_NUMLOCK; break; + default: break; + } + } + + KeyDown(vk, state); + } + } + } + + void Reset() noexcept + { + } + + bool IsConnected() const + { + return mConnected > 0; + } + + Keyboard* mOwner; + uint32_t mConnected; + + static Keyboard::Impl* s_keyboard; + +private: + static constexpr size_t c_MaxSimultaneousKeys = 16; + + ComPtr mGameInput; + GameInputCallbackToken mDeviceToken; + + mutable GameInputKeyState mKeyState[c_MaxSimultaneousKeys]; + + static void CALLBACK OnGameInputDevice( + _In_ GameInputCallbackToken, + _In_ void * context, + _In_ IGameInputDevice *, + _In_ uint64_t, + _In_ GameInputDeviceStatus currentStatus, + _In_ GameInputDeviceStatus) noexcept + { + auto impl = reinterpret_cast(context); + + if (currentStatus & GameInputDeviceConnected) + { + ++impl->mConnected; + } + else if (impl->mConnected > 0) + { + --impl->mConnected; + } + } +}; + + +Keyboard::Impl* Keyboard::Impl::s_keyboard = nullptr; + + +void Keyboard::ProcessMessage(UINT, WPARAM, LPARAM) +{ + // GameInput for Keyboard doesn't require Win32 messages, but this simplifies integration. +} + + +#elif defined(USING_COREWINDOW) + +//====================================================================================== +// Windows Store or Universal Windows Platform (UWP) app implementation +//====================================================================================== + +// +// For a Windows Store app or Universal Windows Platform (UWP) app, add the following: +// +// void App::SetWindow(CoreWindow^ window ) +// { +// m_keyboard->SetWindow(window); +// } +// + +#include + +class Keyboard::Impl +{ +public: + Impl(Keyboard* owner) : + mState{}, + mOwner(owner), + mAcceleratorKeyToken{}, + mActivatedToken{} + { + if (s_keyboard) + { + throw std::logic_error("Keyboard is a singleton"); + } + + s_keyboard = this; + } + + ~Impl() + { + s_keyboard = nullptr; + + RemoveHandlers(); + } + + void GetState(State& state) const + { + memcpy(&state, &mState, sizeof(State)); + } + + void Reset() noexcept + { + memset(&mState, 0, sizeof(State)); + } + + bool IsConnected() const + { + using namespace Microsoft::WRL; + using namespace Microsoft::WRL::Wrappers; + using namespace ABI::Windows::Devices::Input; + using namespace ABI::Windows::Foundation; + + ComPtr caps; + HRESULT hr = RoActivateInstance(HStringReference(RuntimeClass_Windows_Devices_Input_KeyboardCapabilities).Get(), &caps); + ThrowIfFailed(hr); + + INT32 value; + if (SUCCEEDED(caps->get_KeyboardPresent(&value))) + { + return value != 0; + } + + return false; + } + + void SetWindow(ABI::Windows::UI::Core::ICoreWindow* window) + { + using namespace Microsoft::WRL; + using namespace Microsoft::WRL::Wrappers; + using namespace ABI::Windows::UI::Core; + + if (mWindow.Get() == window) + return; + + RemoveHandlers(); + + mWindow = window; + + if (!window) + return; + + typedef __FITypedEventHandler_2_Windows__CUI__CCore__CCoreWindow_Windows__CUI__CCore__CWindowActivatedEventArgs ActivatedHandler; + HRESULT hr = window->add_Activated(Callback(Activated).Get(), &mActivatedToken); + ThrowIfFailed(hr); + + ComPtr dispatcher; + hr = window->get_Dispatcher(dispatcher.GetAddressOf()); + ThrowIfFailed(hr); + + ComPtr keys; + hr = dispatcher.As(&keys); + ThrowIfFailed(hr); + + typedef __FITypedEventHandler_2_Windows__CUI__CCore__CCoreDispatcher_Windows__CUI__CCore__CAcceleratorKeyEventArgs AcceleratorKeyHandler; + hr = keys->add_AcceleratorKeyActivated(Callback(AcceleratorKeyEvent).Get(), &mAcceleratorKeyToken); + ThrowIfFailed(hr); + } + + State mState; + Keyboard* mOwner; + + static Keyboard::Impl* s_keyboard; + +private: + ComPtr mWindow; + + EventRegistrationToken mAcceleratorKeyToken; + EventRegistrationToken mActivatedToken; + + void RemoveHandlers() + { + if (mWindow) + { + using namespace ABI::Windows::UI::Core; + + ComPtr dispatcher; + HRESULT hr = mWindow->get_Dispatcher(dispatcher.GetAddressOf()); + ThrowIfFailed(hr); + + std::ignore = mWindow->remove_Activated(mActivatedToken); + mActivatedToken.value = 0; + + ComPtr keys; + hr = dispatcher.As(&keys); + ThrowIfFailed(hr); + + std::ignore = keys->remove_AcceleratorKeyActivated(mAcceleratorKeyToken); + mAcceleratorKeyToken.value = 0; + } + } + + static HRESULT Activated(IInspectable*, ABI::Windows::UI::Core::IWindowActivatedEventArgs*) + { + auto pImpl = Impl::s_keyboard; + + if (!pImpl) + return S_OK; + + pImpl->Reset(); + + return S_OK; + } + + static HRESULT AcceleratorKeyEvent(IInspectable*, ABI::Windows::UI::Core::IAcceleratorKeyEventArgs* args) + { + using namespace ABI::Windows::System; + using namespace ABI::Windows::UI::Core; + + auto pImpl = Impl::s_keyboard; + + if (!pImpl) + return S_OK; + + CoreAcceleratorKeyEventType evtType; + HRESULT hr = args->get_EventType(&evtType); + ThrowIfFailed(hr); + + bool down = false; + + switch (evtType) + { + case CoreAcceleratorKeyEventType_KeyDown: + case CoreAcceleratorKeyEventType_SystemKeyDown: + down = true; + break; + + case CoreAcceleratorKeyEventType_KeyUp: + case CoreAcceleratorKeyEventType_SystemKeyUp: + break; + + default: + return S_OK; + } + + CorePhysicalKeyStatus status; + hr = args->get_KeyStatus(&status); + ThrowIfFailed(hr); + + VirtualKey virtualKey; + hr = args->get_VirtualKey(&virtualKey); + ThrowIfFailed(hr); + + int vk = static_cast(virtualKey); + + switch (vk) + { + case VK_SHIFT: + vk = (status.ScanCode == 0x36) ? VK_RSHIFT : VK_LSHIFT; + if (!down) + { + // Workaround to ensure left vs. right shift get cleared when both were pressed at same time + KeyUp(VK_LSHIFT, pImpl->mState); + KeyUp(VK_RSHIFT, pImpl->mState); + } + break; + + case VK_CONTROL: + vk = (status.IsExtendedKey) ? VK_RCONTROL : VK_LCONTROL; + break; + + case VK_MENU: + vk = (status.IsExtendedKey) ? VK_RMENU : VK_LMENU; + break; + } + + if (down) + { + KeyDown(vk, pImpl->mState); + } + else + { + KeyUp(vk, pImpl->mState); + } + + return S_OK; + } +}; + + +Keyboard::Impl* Keyboard::Impl::s_keyboard = nullptr; + + +void Keyboard::SetWindow(ABI::Windows::UI::Core::ICoreWindow* window) +{ + pImpl->SetWindow(window); +} + + +#else + +//====================================================================================== +// Win32 desktop implementation +//====================================================================================== + +// +// For a Win32 desktop application, call this function from your Window Message Procedure +// +// LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +// { +// switch (message) +// { +// +// case WM_ACTIVATE: +// case WM_ACTIVATEAPP: +// Keyboard::ProcessMessage(message, wParam, lParam); +// break; +// +// case WM_KEYDOWN: +// case WM_SYSKEYDOWN: +// case WM_KEYUP: +// case WM_SYSKEYUP: +// Keyboard::ProcessMessage(message, wParam, lParam); +// break; +// +// } +// } +// + +class Keyboard::Impl +{ +public: + Impl(Keyboard* owner) : + mState{}, + mOwner(owner) + { + if (s_keyboard) + { + throw std::logic_error("Keyboard is a singleton"); + } + + s_keyboard = this; + } + + Impl(Impl&&) = default; + Impl& operator= (Impl&&) = default; + + Impl(Impl const&) = delete; + Impl& operator= (Impl const&) = delete; + + ~Impl() + { + s_keyboard = nullptr; + } + + void GetState(State& state) const + { + memcpy(&state, &mState, sizeof(State)); + } + + void Reset() noexcept + { + memset(&mState, 0, sizeof(State)); + } + + bool IsConnected() const + { + return true; + } + + State mState; + Keyboard* mOwner; + + static Keyboard::Impl* s_keyboard; +}; + + +Keyboard::Impl* Keyboard::Impl::s_keyboard = nullptr; + + +void Keyboard::ProcessMessage(UINT message, WPARAM wParam, LPARAM lParam) +{ + auto pImpl = Impl::s_keyboard; + + if (!pImpl) + return; + + bool down = false; + + switch (message) + { + case WM_ACTIVATE: + case WM_ACTIVATEAPP: + pImpl->Reset(); + return; + + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + down = true; + break; + + case WM_KEYUP: + case WM_SYSKEYUP: + break; + + default: + return; + } + + int vk = LOWORD(wParam); + // We want to distinguish left and right shift/ctrl/alt keys + switch (vk) + { + case VK_SHIFT: + case VK_CONTROL: + case VK_MENU: + { + if (vk == VK_SHIFT && !down) + { + // Workaround to ensure left vs. right shift get cleared when both were pressed at same time + KeyUp(VK_LSHIFT, pImpl->mState); + KeyUp(VK_RSHIFT, pImpl->mState); + } + + bool isExtendedKey = (HIWORD(lParam) & KF_EXTENDED) == KF_EXTENDED; + int scanCode = LOBYTE(HIWORD(lParam)) | (isExtendedKey ? 0xe000 : 0); + vk = LOWORD(MapVirtualKeyW(static_cast(scanCode), MAPVK_VSC_TO_VK_EX)); + } + break; + } + + if (down) + { + KeyDown(vk, pImpl->mState); + } + else + { + KeyUp(vk, pImpl->mState); + } +} + +#endif +#pragma endregion + +#pragma warning( disable : 4355 ) + +// Public constructor. +Keyboard::Keyboard() noexcept(false) + : pImpl(std::make_unique(this)) +{ +} + + +// Move constructor. +Keyboard::Keyboard(Keyboard&& moveFrom) noexcept + : pImpl(std::move(moveFrom.pImpl)) +{ + pImpl->mOwner = this; +} + + +// Move assignment. +Keyboard& Keyboard::operator= (Keyboard&& moveFrom) noexcept +{ + pImpl = std::move(moveFrom.pImpl); + pImpl->mOwner = this; + return *this; +} + + +// Public destructor. +Keyboard::~Keyboard() = default; + + +Keyboard::State Keyboard::GetState() const +{ + State state; + pImpl->GetState(state); + return state; +} + + +void Keyboard::Reset() noexcept +{ + pImpl->Reset(); +} + + +bool Keyboard::IsConnected() const +{ + return pImpl->IsConnected(); +} + +Keyboard& Keyboard::Get() +{ + if (!Impl::s_keyboard || !Impl::s_keyboard->mOwner) + throw std::logic_error("Keyboard singleton not created"); + + return *Impl::s_keyboard->mOwner; +} + + + +//====================================================================================== +// KeyboardStateTracker +//====================================================================================== + +void Keyboard::KeyboardStateTracker::Update(const State& state) noexcept +{ + auto currPtr = reinterpret_cast(&state); + auto prevPtr = reinterpret_cast(&lastState); + auto releasedPtr = reinterpret_cast(&released); + auto pressedPtr = reinterpret_cast(&pressed); + for (size_t j = 0; j < (256 / 32); ++j) + { + *pressedPtr = *currPtr & ~(*prevPtr); + *releasedPtr = ~(*currPtr) & *prevPtr; + + ++currPtr; + ++prevPtr; + ++releasedPtr; + ++pressedPtr; + } + + lastState = state; +} + +void Keyboard::KeyboardStateTracker::Reset() noexcept +{ + memset(this, 0, sizeof(KeyboardStateTracker)); +} diff --git a/Common/DirectXTK12/Src/LinearAllocator.cpp b/Common/DirectXTK12/Src/LinearAllocator.cpp new file mode 100644 index 0000000..fc556e7 --- /dev/null +++ b/Common/DirectXTK12/Src/LinearAllocator.cpp @@ -0,0 +1,498 @@ +//-------------------------------------------------------------------------------------- +// File: LinearAllocator.cpp +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "pch.h" +#include "DirectXHelpers.h" +#include "PlatformHelpers.h" +#include "LinearAllocator.h" + +// Set this to 1 to enable some additional debug validation +#define VALIDATE_LISTS 0 + +#if VALIDATE_LISTS +# include +#endif + +using namespace DirectX; +using Microsoft::WRL::ComPtr; + +LinearAllocatorPage::LinearAllocatorPage() noexcept + : pPrevPage(nullptr) + , pNextPage(nullptr) + , mMemory(nullptr) + , mPendingFence(0) + , mGpuAddress{} + , mOffset(0) + , mSize(0) + , mRefCount(1) +{ +} + +size_t LinearAllocatorPage::Suballocate(_In_ size_t size, _In_ size_t alignment) +{ + const size_t offset = AlignUp(mOffset, alignment); + if (offset + size > mSize) + { + // Use of suballocate should be limited to pages with free space, + // so really shouldn't happen. + throw std::runtime_error("LinearAllocatorPage::Suballocate"); + } + mOffset = offset + size; + return offset; +} + +void LinearAllocatorPage::Release() noexcept +{ + assert(mRefCount > 0); + + if (mRefCount.fetch_sub(1) == 1) + { + mUploadResource->Unmap(0, nullptr); + delete this; + } +} + + +//-------------------------------------------------------------------------------------- +LinearAllocator::LinearAllocator( + _In_ ID3D12Device* pDevice, + _In_ size_t pageSize, + _In_ size_t preallocateBytes) noexcept(false) + : m_pendingPages(nullptr) + , m_usedPages(nullptr) + , m_unusedPages(nullptr) + , m_increment(pageSize) + , m_numPending(0) + , m_totalPages(0) + , m_fenceCount(0) + , m_device(pDevice) +{ + assert(pDevice != nullptr); +#if defined(_DEBUG) || defined(PROFILE) + m_debugName = L"LinearAllocator"; +#endif + + const size_t preallocatePageCount = ((preallocateBytes + pageSize - 1) / pageSize); + for (size_t preallocatePages = 0; preallocateBytes != 0 && preallocatePages < preallocatePageCount; ++preallocatePages) + { + if (GetNewPage() == nullptr) + { + DebugTrace("LinearAllocator failed to preallocate pages (%zu required bytes, %zu pages)\n", + preallocatePageCount * m_increment, preallocatePageCount); + throw std::bad_alloc(); + } + } + + ThrowIfFailed(pDevice->CreateFence( + 0, + D3D12_FENCE_FLAG_NONE, + IID_GRAPHICS_PPV_ARGS(m_fence.ReleaseAndGetAddressOf()))); +} + +LinearAllocator::~LinearAllocator() +{ + // Must wait for all pending fences! + while (m_pendingPages != nullptr) + { + RetirePendingPages(); + } + + assert(m_pendingPages == nullptr); + + // Return all the memory + FreePages(m_unusedPages); + FreePages(m_usedPages); + + m_pendingPages = nullptr; + m_usedPages = nullptr; + m_unusedPages = nullptr; + m_increment = 0; +} + +LinearAllocatorPage* LinearAllocator::FindPageForAlloc(_In_ size_t size, _In_ size_t alignment) +{ +#ifdef _DEBUG + if (size > m_increment) + throw std::out_of_range("Size must be less or equal to the allocator's increment"); + if (alignment > m_increment) + throw std::out_of_range("Alignment must be less or equal to the allocator's increment"); + if (size == 0) + throw std::invalid_argument("Cannot honor zero size allocation request."); +#endif + + auto page = GetPageForAlloc(size, alignment); + if (!page) + { + return nullptr; + } + + return page; +} + +// Call this after you submit your work to the driver. +void LinearAllocator::FenceCommittedPages(_In_ ID3D12CommandQueue* commandQueue) +{ + // No pending pages + if (m_usedPages == nullptr) + return; + + // For all the used pages, fence them + UINT numReady = 0; + LinearAllocatorPage* readyPages = nullptr; + LinearAllocatorPage* unreadyPages = nullptr; + LinearAllocatorPage* nextPage = nullptr; + for (auto page = m_usedPages; page != nullptr; page = nextPage) + { + nextPage = page->pNextPage; + + // Disconnect from the list + page->pPrevPage = nullptr; + + // This implies the allocator is the only remaining reference to the page, and therefore the memory is ready for re-use. + if (page->RefCount() == 1) + { + // Signal the fence + numReady++; + page->mPendingFence = ++m_fenceCount; + ThrowIfFailed(commandQueue->Signal(m_fence.Get(), m_fenceCount)); + + // Link to the ready pages list + page->pNextPage = readyPages; + if (readyPages) readyPages->pPrevPage = page; + readyPages = page; + } + else + { + // Link to the unready list + page->pNextPage = unreadyPages; + if (unreadyPages) unreadyPages->pPrevPage = page; + unreadyPages = page; + } + } + + // Replace the used pages list with the new unready list + m_usedPages = unreadyPages; + + // Append all those pages from the ready list to the pending list + if (numReady > 0) + { + m_numPending += numReady; + LinkPageChain(readyPages, m_pendingPages); + } + +#if VALIDATE_LISTS + ValidatePageLists(); +#endif +} + +// Call this once a frame after all of your driver submissions. +// (immediately before or after Present-time) +void LinearAllocator::RetirePendingPages() noexcept +{ + const uint64_t fenceValue = m_fence->GetCompletedValue(); + + // For each page that we know has a fence pending, check it. If the fence has passed, + // we can mark the page for re-use. + auto page = m_pendingPages; + while (page != nullptr) + { + auto nextPage = page->pNextPage; + + assert(page->mPendingFence != 0); + + if (fenceValue >= page->mPendingFence) + { + // Fence has passed. It is safe to use this page again. + ReleasePage(page); + } + + page = nextPage; + } +} + +void LinearAllocator::Shrink() noexcept +{ + FreePages(m_unusedPages); + m_unusedPages = nullptr; + +#if VALIDATE_LISTS + ValidatePageLists(); +#endif +} + +LinearAllocatorPage* LinearAllocator::GetCleanPageForAlloc() +{ + // Grab the first unused page, if one exists. Else, allocate a new page. + auto page = m_unusedPages; + if (!page) + { + // Allocate a new page + page = GetNewPage(); + if (!page) + { + return nullptr; + } + } + + // Mark this page as used + UnlinkPage(page); + LinkPage(page, m_usedPages); + + assert(page->mOffset == 0); + + return page; +} + +LinearAllocatorPage* LinearAllocator::GetPageForAlloc( + size_t sizeBytes, + size_t alignment) +{ + // Fast path + if (sizeBytes == m_increment && (alignment == 0 || alignment == m_increment)) + { + return GetCleanPageForAlloc(); + } + + // Find a page in the pending pages list that has space. + auto page = FindPageForAlloc(m_usedPages, sizeBytes, alignment); + if (!page) + { + page = GetCleanPageForAlloc(); + } + + return page; +} + +LinearAllocatorPage* LinearAllocator::FindPageForAlloc( + LinearAllocatorPage* list, + size_t sizeBytes, + size_t alignment) noexcept +{ + for (auto page = list; page != nullptr; page = page->pNextPage) + { + const size_t offset = AlignUp(page->mOffset, alignment); + if (offset + sizeBytes <= m_increment) + return page; + } + return nullptr; +} + +LinearAllocatorPage* LinearAllocator::GetNewPage() +{ + const CD3DX12_HEAP_PROPERTIES uploadHeapProperties(D3D12_HEAP_TYPE_UPLOAD); + const CD3DX12_RESOURCE_DESC bufferDesc = CD3DX12_RESOURCE_DESC::Buffer(m_increment); + + // Allocate the upload heap + ComPtr spResource; + HRESULT hr = m_device->CreateCommittedResource( + &uploadHeapProperties, + D3D12_HEAP_FLAG_NONE, + &bufferDesc, + D3D12_RESOURCE_STATE_GENERIC_READ, + nullptr, + IID_GRAPHICS_PPV_ARGS(spResource.ReleaseAndGetAddressOf())); + if (FAILED(hr)) + { + if (hr != E_OUTOFMEMORY) + { + DebugTrace("LinearAllocator::GetNewPage resource allocation failed due to unexpected error %08X\n", static_cast(hr)); + } + return nullptr; + } + +#if defined(_DEBUG) || defined(PROFILE) + spResource->SetName(m_debugName.empty() ? L"LinearAllocator" : m_debugName.c_str()); +#endif + + // Get a pointer to the memory + void* pMemory = nullptr; + ThrowIfFailed(spResource->Map(0, nullptr, &pMemory)); + memset(pMemory, 0, m_increment); + + // Add the page to the page list + auto page = new LinearAllocatorPage; + page->mMemory = pMemory; + page->mGpuAddress = spResource->GetGPUVirtualAddress(); + page->mSize = m_increment; + page->mUploadResource.Swap(spResource); + + // Set as head of the list + page->pNextPage = m_unusedPages; + if (m_unusedPages) m_unusedPages->pPrevPage = page; + m_unusedPages = page; + m_totalPages++; + +#if VALIDATE_LISTS + ValidatePageLists(); +#endif + + return page; +} + +void LinearAllocator::UnlinkPage(LinearAllocatorPage* page) noexcept +{ + if (page->pPrevPage) + page->pPrevPage->pNextPage = page->pNextPage; + + // Check that it isn't the head of any of our tracked lists + else if (page == m_unusedPages) + m_unusedPages = page->pNextPage; + else if (page == m_usedPages) + m_usedPages = page->pNextPage; + else if (page == m_pendingPages) + m_pendingPages = page->pNextPage; + + if (page->pNextPage) + page->pNextPage->pPrevPage = page->pPrevPage; + + page->pNextPage = nullptr; + page->pPrevPage = nullptr; + +#if VALIDATE_LISTS + ValidatePageLists(); +#endif +} + +void LinearAllocator::LinkPageChain(LinearAllocatorPage* page, LinearAllocatorPage*& list) noexcept +{ +#if VALIDATE_LISTS + // Walk the chain and ensure it's not in the list twice + for (LinearAllocatorPage* cur = list; cur != nullptr; cur = cur->pNextPage) + { + assert(cur != page); + } +#endif + assert(page->pPrevPage == nullptr); + assert(list == nullptr || list->pPrevPage == nullptr); + + // Follow chain to the end and append + LinearAllocatorPage* lastPage = nullptr; + for (lastPage = page; lastPage->pNextPage != nullptr; lastPage = lastPage->pNextPage) {} + + lastPage->pNextPage = list; + if (list) + list->pPrevPage = lastPage; + + list = page; + +#if VALIDATE_LISTS + ValidatePageLists(); +#endif +} + +void LinearAllocator::LinkPage(LinearAllocatorPage* page, LinearAllocatorPage*& list) noexcept +{ +#if VALIDATE_LISTS + // Walk the chain and ensure it's not in the list twice + for (LinearAllocatorPage* cur = list; cur != nullptr; cur = cur->pNextPage) + { + assert(cur != page); + } +#endif + assert(page->pNextPage == nullptr); + assert(page->pPrevPage == nullptr); + assert(list == nullptr || list->pPrevPage == nullptr); + + page->pNextPage = list; + if (list) + list->pPrevPage = page; + + list = page; + +#if VALIDATE_LISTS + ValidatePageLists(); +#endif +} + +void LinearAllocator::ReleasePage(LinearAllocatorPage* page) noexcept +{ + assert(m_numPending > 0); + m_numPending--; + + UnlinkPage(page); + LinkPage(page, m_unusedPages); + + // Reset the page offset (effectively erasing the memory) + page->mOffset = 0; + +#ifdef _DEBUG + memset(page->mMemory, 0, m_increment); +#endif + +#if VALIDATE_LISTS + ValidatePageLists(); +#endif +} + +void LinearAllocator::FreePages(LinearAllocatorPage* page) noexcept +{ + while (page != nullptr) + { + auto nextPage = page->pNextPage; + + page->Release(); + + page = nextPage; + assert(m_totalPages > 0); + m_totalPages--; + } +} + +#if VALIDATE_LISTS +void LinearAllocator::ValidateList(LinearAllocatorPage* list) +{ + for (auto page = list, *lastPage = nullptr; + page != nullptr; + lastPage = page, page = page->pNextPage) + { + if (page->pPrevPage != lastPage) + { + throw std::runtime_error("Broken link to previous"); + } + } +} + +void LinearAllocator::ValidatePageLists() +{ + ValidateList(m_pendingPages); + ValidateList(m_usedPages); + ValidateList(m_unusedPages); +} +#endif + +#if defined(_DEBUG) || defined(PROFILE) +void LinearAllocator::SetDebugName(const char* name) +{ + wchar_t wname[MAX_PATH] = {}; + const int result = MultiByteToWideChar(CP_UTF8, 0, name, static_cast(strlen(name)), wname, MAX_PATH); + if (result > 0) + { + SetDebugName(wname); + } +} + +void LinearAllocator::SetDebugName(const wchar_t* name) +{ + m_debugName = name; + + // Rename existing pages + m_fence->SetName(name); + SetPageDebugName(m_pendingPages); + SetPageDebugName(m_usedPages); + SetPageDebugName(m_unusedPages); +} + +void LinearAllocator::SetPageDebugName(LinearAllocatorPage* list) noexcept +{ + for (auto page = list; page != nullptr; page = page->pNextPage) + { + page->mUploadResource->SetName(m_debugName.c_str()); + } +} +#endif diff --git a/Common/DirectXTK12/Src/LinearAllocator.h b/Common/DirectXTK12/Src/LinearAllocator.h new file mode 100644 index 0000000..f97547d --- /dev/null +++ b/Common/DirectXTK12/Src/LinearAllocator.h @@ -0,0 +1,168 @@ +//-------------------------------------------------------------------------------------- +// LinearAllocator.h +// +// A linear allocator. When Allocate is called it will try to return you a pointer into +// existing graphics memory. If there is no space left from what is allocated, more +// pages are allocated on-the-fly. +// +// Each allocation must be smaller or equal to pageSize. It is not necessary but is most +// efficient for the sizes to be some fraction of pageSize. pageSize does not determine +// the size of the physical pages underneath the virtual memory (that's given by the +// XMemAttributes) but is how much additional memory the allocator should allocate +// each time you run out of space. +// +// preallocatePages specifies how many pages to initially allocate. Specifying zero will +// preallocate two pages by default. +// +// This class is NOT thread safe. You should protect this with the appropriate sync +// primitives or, even better, use one linear allocator per thread. +// +// Pages are freed once the GPU is done with them. As such, you need to specify when a +// page is in use and when it is no longer in use. Use RetirePages to prompt the +// allocator to check if pages are no longer being used by the GPU. Use InsertFences to +// mark all used pages as in-use by the GPU, removing them from the available pages +// list. It is recommended you call RetirePages and InsertFences once a frame, usually +// just before Present(). +// +// Why is RetirePages decoupled from InsertFences? It's possible that you might want to +// reclaim pages more frequently than locking used pages. For example, if you find the +// allocator is burning through pages too quickly you can call RetirePages to reclaim +// some that the GPU has finished with. However, this adds additional CPU overhead so it +// is left to you to decide. In most cases this is sufficient: +// +// allocator.RetirePages(); +// allocator.InsertFences( pContext, 0 ); +// Present(...); +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#pragma once + +#include + + +namespace DirectX +{ + class LinearAllocatorPage + { + public: + LinearAllocatorPage() noexcept; + + LinearAllocatorPage(LinearAllocatorPage&&) = delete; + LinearAllocatorPage& operator= (LinearAllocatorPage&&) = delete; + + LinearAllocatorPage(LinearAllocatorPage const&) = delete; + LinearAllocatorPage& operator=(LinearAllocatorPage const&) = delete; + + size_t Suballocate(_In_ size_t size, _In_ size_t alignment); + + void* BaseMemory() const noexcept { return mMemory; } + ID3D12Resource* UploadResource() const noexcept { return mUploadResource.Get(); } + D3D12_GPU_VIRTUAL_ADDRESS GpuAddress() const noexcept { return mGpuAddress; } + size_t BytesUsed() const noexcept { return mOffset; } + size_t Size() const noexcept { return mSize; } + + void AddRef() noexcept { mRefCount.fetch_add(1); } + int32_t RefCount() const noexcept { return mRefCount.load(); } + void Release() noexcept; + + protected: + friend class LinearAllocator; + + LinearAllocatorPage* pPrevPage; + LinearAllocatorPage* pNextPage; + + void* mMemory; + uint64_t mPendingFence; + D3D12_GPU_VIRTUAL_ADDRESS mGpuAddress; + size_t mOffset; + size_t mSize; + Microsoft::WRL::ComPtr mUploadResource; + + private: + std::atomic mRefCount; + }; + + class LinearAllocator + { + public: + // These values will be rounded up to the nearest 64k. + // You can specify zero for incrementalSizeBytes to increment + // by 1 page (64k). + LinearAllocator( + _In_ ID3D12Device* pDevice, + _In_ size_t pageSize, + _In_ size_t preallocateBytes = 0) noexcept(false); + + LinearAllocator(LinearAllocator&&) = default; + LinearAllocator& operator= (LinearAllocator&&) = default; + + LinearAllocator(LinearAllocator const&) = delete; + LinearAllocator& operator=(LinearAllocator const&) = delete; + + ~LinearAllocator(); + + LinearAllocatorPage* FindPageForAlloc(_In_ size_t requestedSize, _In_ size_t alignment); + + // Call this at least once a frame to check if pages have become available. + void RetirePendingPages() noexcept; + + // Call this after you submit your work to the driver. + // (e.g. immediately before Present.) + void FenceCommittedPages(_In_ ID3D12CommandQueue* commandQueue); + + // Throws away all currently unused pages + void Shrink() noexcept; + + // Statistics + size_t CommittedPageCount() const noexcept { return m_numPending; } + size_t TotalPageCount() const noexcept { return m_totalPages; } + size_t CommittedMemoryUsage() const noexcept { return m_numPending * m_increment; } + size_t TotalMemoryUsage() const noexcept { return m_totalPages * m_increment; } + size_t PageSize() const noexcept { return m_increment; } + + #if defined(_DEBUG) || defined(PROFILE) + // Debug info + const wchar_t* GetDebugName() const noexcept { return m_debugName.c_str(); } + void SetDebugName(const wchar_t* name); + void SetDebugName(const char* name); + #endif + + private: + LinearAllocatorPage* m_pendingPages; // Pages in use by the GPU + LinearAllocatorPage* m_usedPages; // Pages to be submitted to the GPU + LinearAllocatorPage* m_unusedPages; // Pages not being used right now + size_t m_increment; + size_t m_numPending; + size_t m_totalPages; + uint64_t m_fenceCount; + Microsoft::WRL::ComPtr m_device; + Microsoft::WRL::ComPtr m_fence; + + LinearAllocatorPage* GetPageForAlloc(size_t sizeBytes, size_t alignment); + LinearAllocatorPage* GetCleanPageForAlloc(); + + LinearAllocatorPage* FindPageForAlloc(LinearAllocatorPage* list, size_t sizeBytes, size_t alignment) noexcept; + + LinearAllocatorPage* GetNewPage(); + + void UnlinkPage(LinearAllocatorPage* page) noexcept; + void LinkPage(LinearAllocatorPage* page, LinearAllocatorPage*& list) noexcept; + void LinkPageChain(LinearAllocatorPage* page, LinearAllocatorPage*& list) noexcept; + void ReleasePage(LinearAllocatorPage* page) noexcept; + void FreePages(LinearAllocatorPage* list) noexcept; + + #if defined(_DEBUG) || defined(PROFILE) + std::wstring m_debugName; + + static void ValidateList(LinearAllocatorPage* list); + void ValidatePageLists(); + + void SetPageDebugName(LinearAllocatorPage* list) noexcept; + #endif + }; +} diff --git a/Common/DirectXTK12/Src/LoaderHelpers.h b/Common/DirectXTK12/Src/LoaderHelpers.h new file mode 100644 index 0000000..eeec50f --- /dev/null +++ b/Common/DirectXTK12/Src/LoaderHelpers.h @@ -0,0 +1,1096 @@ +//-------------------------------------------------------------------------------------- +// File: LoaderHelpers.h +// +// Helper functions for texture loaders and screen grabber +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248929 +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#pragma once + +#include "DDS.h" +#include "DDSTextureLoader.h" +#include "PlatformHelpers.h" + + +namespace DirectX +{ + namespace LoaderHelpers + { + //-------------------------------------------------------------------------------------- + // Return the BPP for a particular format + //-------------------------------------------------------------------------------------- + inline size_t BitsPerPixel(_In_ DXGI_FORMAT fmt) noexcept + { + switch (fmt) + { + case DXGI_FORMAT_R32G32B32A32_TYPELESS: + case DXGI_FORMAT_R32G32B32A32_FLOAT: + case DXGI_FORMAT_R32G32B32A32_UINT: + case DXGI_FORMAT_R32G32B32A32_SINT: + return 128; + + case DXGI_FORMAT_R32G32B32_TYPELESS: + case DXGI_FORMAT_R32G32B32_FLOAT: + case DXGI_FORMAT_R32G32B32_UINT: + case DXGI_FORMAT_R32G32B32_SINT: + return 96; + + case DXGI_FORMAT_R16G16B16A16_TYPELESS: + case DXGI_FORMAT_R16G16B16A16_FLOAT: + case DXGI_FORMAT_R16G16B16A16_UNORM: + case DXGI_FORMAT_R16G16B16A16_UINT: + case DXGI_FORMAT_R16G16B16A16_SNORM: + case DXGI_FORMAT_R16G16B16A16_SINT: + case DXGI_FORMAT_R32G32_TYPELESS: + case DXGI_FORMAT_R32G32_FLOAT: + case DXGI_FORMAT_R32G32_UINT: + case DXGI_FORMAT_R32G32_SINT: + case DXGI_FORMAT_R32G8X24_TYPELESS: + case DXGI_FORMAT_D32_FLOAT_S8X24_UINT: + case DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS: + case DXGI_FORMAT_X32_TYPELESS_G8X24_UINT: + case DXGI_FORMAT_Y416: + case DXGI_FORMAT_Y210: + case DXGI_FORMAT_Y216: + return 64; + + case DXGI_FORMAT_R10G10B10A2_TYPELESS: + case DXGI_FORMAT_R10G10B10A2_UNORM: + case DXGI_FORMAT_R10G10B10A2_UINT: + case DXGI_FORMAT_R11G11B10_FLOAT: + case DXGI_FORMAT_R8G8B8A8_TYPELESS: + case DXGI_FORMAT_R8G8B8A8_UNORM: + case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: + case DXGI_FORMAT_R8G8B8A8_UINT: + case DXGI_FORMAT_R8G8B8A8_SNORM: + case DXGI_FORMAT_R8G8B8A8_SINT: + case DXGI_FORMAT_R16G16_TYPELESS: + case DXGI_FORMAT_R16G16_FLOAT: + case DXGI_FORMAT_R16G16_UNORM: + case DXGI_FORMAT_R16G16_UINT: + case DXGI_FORMAT_R16G16_SNORM: + case DXGI_FORMAT_R16G16_SINT: + case DXGI_FORMAT_R32_TYPELESS: + case DXGI_FORMAT_D32_FLOAT: + case DXGI_FORMAT_R32_FLOAT: + case DXGI_FORMAT_R32_UINT: + case DXGI_FORMAT_R32_SINT: + case DXGI_FORMAT_R24G8_TYPELESS: + case DXGI_FORMAT_D24_UNORM_S8_UINT: + case DXGI_FORMAT_R24_UNORM_X8_TYPELESS: + case DXGI_FORMAT_X24_TYPELESS_G8_UINT: + case DXGI_FORMAT_R9G9B9E5_SHAREDEXP: + case DXGI_FORMAT_R8G8_B8G8_UNORM: + case DXGI_FORMAT_G8R8_G8B8_UNORM: + case DXGI_FORMAT_B8G8R8A8_UNORM: + case DXGI_FORMAT_B8G8R8X8_UNORM: + case DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM: + case DXGI_FORMAT_B8G8R8A8_TYPELESS: + case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: + case DXGI_FORMAT_B8G8R8X8_TYPELESS: + case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: + case DXGI_FORMAT_AYUV: + case DXGI_FORMAT_Y410: + case DXGI_FORMAT_YUY2: + #if (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) + case DXGI_FORMAT_R10G10B10_7E3_A2_FLOAT: + case DXGI_FORMAT_R10G10B10_6E4_A2_FLOAT: + case DXGI_FORMAT_R10G10B10_SNORM_A2_UNORM: + #endif + return 32; + + case DXGI_FORMAT_P010: + case DXGI_FORMAT_P016: + #if (_WIN32_WINNT >= _WIN32_WINNT_WIN10) + case DXGI_FORMAT_V408: + #endif + #if (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) + case DXGI_FORMAT_D16_UNORM_S8_UINT: + case DXGI_FORMAT_R16_UNORM_X8_TYPELESS: + case DXGI_FORMAT_X16_TYPELESS_G8_UINT: + #endif + return 24; + + case DXGI_FORMAT_R8G8_TYPELESS: + case DXGI_FORMAT_R8G8_UNORM: + case DXGI_FORMAT_R8G8_UINT: + case DXGI_FORMAT_R8G8_SNORM: + case DXGI_FORMAT_R8G8_SINT: + case DXGI_FORMAT_R16_TYPELESS: + case DXGI_FORMAT_R16_FLOAT: + case DXGI_FORMAT_D16_UNORM: + case DXGI_FORMAT_R16_UNORM: + case DXGI_FORMAT_R16_UINT: + case DXGI_FORMAT_R16_SNORM: + case DXGI_FORMAT_R16_SINT: + case DXGI_FORMAT_B5G6R5_UNORM: + case DXGI_FORMAT_B5G5R5A1_UNORM: + case DXGI_FORMAT_A8P8: + case DXGI_FORMAT_B4G4R4A4_UNORM: + #if (_WIN32_WINNT >= _WIN32_WINNT_WIN10) + case DXGI_FORMAT_P208: + case DXGI_FORMAT_V208: + #endif + return 16; + + case DXGI_FORMAT_NV12: + case DXGI_FORMAT_420_OPAQUE: + case DXGI_FORMAT_NV11: + return 12; + + case DXGI_FORMAT_R8_TYPELESS: + case DXGI_FORMAT_R8_UNORM: + case DXGI_FORMAT_R8_UINT: + case DXGI_FORMAT_R8_SNORM: + case DXGI_FORMAT_R8_SINT: + case DXGI_FORMAT_A8_UNORM: + case DXGI_FORMAT_BC2_TYPELESS: + case DXGI_FORMAT_BC2_UNORM: + case DXGI_FORMAT_BC2_UNORM_SRGB: + case DXGI_FORMAT_BC3_TYPELESS: + case DXGI_FORMAT_BC3_UNORM: + case DXGI_FORMAT_BC3_UNORM_SRGB: + case DXGI_FORMAT_BC5_TYPELESS: + case DXGI_FORMAT_BC5_UNORM: + case DXGI_FORMAT_BC5_SNORM: + case DXGI_FORMAT_BC6H_TYPELESS: + case DXGI_FORMAT_BC6H_UF16: + case DXGI_FORMAT_BC6H_SF16: + case DXGI_FORMAT_BC7_TYPELESS: + case DXGI_FORMAT_BC7_UNORM: + case DXGI_FORMAT_BC7_UNORM_SRGB: + case DXGI_FORMAT_AI44: + case DXGI_FORMAT_IA44: + case DXGI_FORMAT_P8: + #if (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) + case DXGI_FORMAT_R4G4_UNORM: + #endif + return 8; + + case DXGI_FORMAT_R1_UNORM: + return 1; + + case DXGI_FORMAT_BC1_TYPELESS: + case DXGI_FORMAT_BC1_UNORM: + case DXGI_FORMAT_BC1_UNORM_SRGB: + case DXGI_FORMAT_BC4_TYPELESS: + case DXGI_FORMAT_BC4_UNORM: + case DXGI_FORMAT_BC4_SNORM: + return 4; + + case DXGI_FORMAT_UNKNOWN: + case DXGI_FORMAT_FORCE_UINT: + default: + return 0; + } + } + + //-------------------------------------------------------------------------------------- + inline DXGI_FORMAT MakeSRGB(_In_ DXGI_FORMAT format) noexcept + { + switch (format) + { + case DXGI_FORMAT_R8G8B8A8_UNORM: + return DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; + + case DXGI_FORMAT_BC1_UNORM: + return DXGI_FORMAT_BC1_UNORM_SRGB; + + case DXGI_FORMAT_BC2_UNORM: + return DXGI_FORMAT_BC2_UNORM_SRGB; + + case DXGI_FORMAT_BC3_UNORM: + return DXGI_FORMAT_BC3_UNORM_SRGB; + + case DXGI_FORMAT_B8G8R8A8_UNORM: + return DXGI_FORMAT_B8G8R8A8_UNORM_SRGB; + + case DXGI_FORMAT_B8G8R8X8_UNORM: + return DXGI_FORMAT_B8G8R8X8_UNORM_SRGB; + + case DXGI_FORMAT_BC7_UNORM: + return DXGI_FORMAT_BC7_UNORM_SRGB; + + default: + return format; + } + } + + //-------------------------------------------------------------------------------------- + inline DXGI_FORMAT MakeLinear(_In_ DXGI_FORMAT format) noexcept + { + switch (format) + { + case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: + return DXGI_FORMAT_R8G8B8A8_UNORM; + + case DXGI_FORMAT_BC1_UNORM_SRGB: + return DXGI_FORMAT_BC1_UNORM; + + case DXGI_FORMAT_BC2_UNORM_SRGB: + return DXGI_FORMAT_BC2_UNORM; + + case DXGI_FORMAT_BC3_UNORM_SRGB: + return DXGI_FORMAT_BC3_UNORM; + + case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: + return DXGI_FORMAT_B8G8R8A8_UNORM; + + case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: + return DXGI_FORMAT_B8G8R8X8_UNORM; + + case DXGI_FORMAT_BC7_UNORM_SRGB: + return DXGI_FORMAT_BC7_UNORM; + + default: + return format; + } + } + + //-------------------------------------------------------------------------------------- + inline bool IsCompressed(_In_ DXGI_FORMAT fmt) noexcept + { + switch (fmt) + { + case DXGI_FORMAT_BC1_TYPELESS: + case DXGI_FORMAT_BC1_UNORM: + case DXGI_FORMAT_BC1_UNORM_SRGB: + case DXGI_FORMAT_BC2_TYPELESS: + case DXGI_FORMAT_BC2_UNORM: + case DXGI_FORMAT_BC2_UNORM_SRGB: + case DXGI_FORMAT_BC3_TYPELESS: + case DXGI_FORMAT_BC3_UNORM: + case DXGI_FORMAT_BC3_UNORM_SRGB: + case DXGI_FORMAT_BC4_TYPELESS: + case DXGI_FORMAT_BC4_UNORM: + case DXGI_FORMAT_BC4_SNORM: + case DXGI_FORMAT_BC5_TYPELESS: + case DXGI_FORMAT_BC5_UNORM: + case DXGI_FORMAT_BC5_SNORM: + case DXGI_FORMAT_BC6H_TYPELESS: + case DXGI_FORMAT_BC6H_UF16: + case DXGI_FORMAT_BC6H_SF16: + case DXGI_FORMAT_BC7_TYPELESS: + case DXGI_FORMAT_BC7_UNORM: + case DXGI_FORMAT_BC7_UNORM_SRGB: + return true; + + default: + return false; + } + } + + //-------------------------------------------------------------------------------------- + inline DXGI_FORMAT EnsureNotTypeless(DXGI_FORMAT fmt) noexcept + { + // Assumes UNORM or FLOAT; doesn't use UINT or SINT + switch (fmt) + { + case DXGI_FORMAT_R32G32B32A32_TYPELESS: return DXGI_FORMAT_R32G32B32A32_FLOAT; + case DXGI_FORMAT_R32G32B32_TYPELESS: return DXGI_FORMAT_R32G32B32_FLOAT; + case DXGI_FORMAT_R16G16B16A16_TYPELESS: return DXGI_FORMAT_R16G16B16A16_UNORM; + case DXGI_FORMAT_R32G32_TYPELESS: return DXGI_FORMAT_R32G32_FLOAT; + case DXGI_FORMAT_R10G10B10A2_TYPELESS: return DXGI_FORMAT_R10G10B10A2_UNORM; + case DXGI_FORMAT_R8G8B8A8_TYPELESS: return DXGI_FORMAT_R8G8B8A8_UNORM; + case DXGI_FORMAT_R16G16_TYPELESS: return DXGI_FORMAT_R16G16_UNORM; + case DXGI_FORMAT_R32_TYPELESS: return DXGI_FORMAT_R32_FLOAT; + case DXGI_FORMAT_R8G8_TYPELESS: return DXGI_FORMAT_R8G8_UNORM; + case DXGI_FORMAT_R16_TYPELESS: return DXGI_FORMAT_R16_UNORM; + case DXGI_FORMAT_R8_TYPELESS: return DXGI_FORMAT_R8_UNORM; + case DXGI_FORMAT_BC1_TYPELESS: return DXGI_FORMAT_BC1_UNORM; + case DXGI_FORMAT_BC2_TYPELESS: return DXGI_FORMAT_BC2_UNORM; + case DXGI_FORMAT_BC3_TYPELESS: return DXGI_FORMAT_BC3_UNORM; + case DXGI_FORMAT_BC4_TYPELESS: return DXGI_FORMAT_BC4_UNORM; + case DXGI_FORMAT_BC5_TYPELESS: return DXGI_FORMAT_BC5_UNORM; + case DXGI_FORMAT_B8G8R8A8_TYPELESS: return DXGI_FORMAT_B8G8R8A8_UNORM; + case DXGI_FORMAT_B8G8R8X8_TYPELESS: return DXGI_FORMAT_B8G8R8X8_UNORM; + case DXGI_FORMAT_BC7_TYPELESS: return DXGI_FORMAT_BC7_UNORM; + default: return fmt; + } + } + + //-------------------------------------------------------------------------------------- + inline HRESULT LoadTextureDataFromMemory( + _In_reads_(ddsDataSize) const uint8_t* ddsData, + size_t ddsDataSize, + const DDS_HEADER** header, + const uint8_t** bitData, + size_t* bitSize) noexcept + { + if (!header || !bitData || !bitSize) + { + return E_POINTER; + } + + *bitSize = 0; + + if (ddsDataSize > UINT32_MAX) + { + return E_FAIL; + } + + if (ddsDataSize < (sizeof(uint32_t) + sizeof(DDS_HEADER))) + { + return E_FAIL; + } + + // DDS files always start with the same magic number ("DDS ") + auto const dwMagicNumber = *reinterpret_cast(ddsData); + if (dwMagicNumber != DDS_MAGIC) + { + return E_FAIL; + } + + auto hdr = reinterpret_cast(ddsData + sizeof(uint32_t)); + + // Verify header to validate DDS file + if (hdr->size != sizeof(DDS_HEADER) || + hdr->ddspf.size != sizeof(DDS_PIXELFORMAT)) + { + return E_FAIL; + } + + // Check for DX10 extension + bool bDXT10Header = false; + if ((hdr->ddspf.flags & DDS_FOURCC) && + (MAKEFOURCC('D', 'X', '1', '0') == hdr->ddspf.fourCC)) + { + // Must be long enough for both headers and magic value + if (ddsDataSize < (sizeof(uint32_t) + sizeof(DDS_HEADER) + sizeof(DDS_HEADER_DXT10))) + { + return E_FAIL; + } + + bDXT10Header = true; + } + + // setup the pointers in the process request + *header = hdr; + auto offset = sizeof(uint32_t) + + sizeof(DDS_HEADER) + + (bDXT10Header ? sizeof(DDS_HEADER_DXT10) : 0u); + *bitData = ddsData + offset; + *bitSize = ddsDataSize - offset; + + return S_OK; + } + + //-------------------------------------------------------------------------------------- + inline HRESULT LoadTextureDataFromFile( + _In_z_ const wchar_t* fileName, + std::unique_ptr& ddsData, + const DDS_HEADER** header, + const uint8_t** bitData, + size_t* bitSize) noexcept + { + if (!header || !bitData || !bitSize) + { + return E_POINTER; + } + + *bitSize = 0; + + // open the file + #if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) + ScopedHandle hFile(safe_handle(CreateFile2( + fileName, + GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, + nullptr))); + #else + ScopedHandle hFile(safe_handle(CreateFileW( + fileName, + GENERIC_READ, FILE_SHARE_READ, + nullptr, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, + nullptr))); + #endif + + if (!hFile) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + // Get the file size + FILE_STANDARD_INFO fileInfo; + if (!GetFileInformationByHandleEx(hFile.get(), FileStandardInfo, &fileInfo, sizeof(fileInfo))) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + // File is too big for 32-bit allocation, so reject read + if (fileInfo.EndOfFile.HighPart > 0) + { + return E_FAIL; + } + + // Need at least enough data to fill the header and magic number to be a valid DDS + if (fileInfo.EndOfFile.LowPart < (sizeof(uint32_t) + sizeof(DDS_HEADER))) + { + return E_FAIL; + } + + // create enough space for the file data + ddsData.reset(new (std::nothrow) uint8_t[fileInfo.EndOfFile.LowPart]); + if (!ddsData) + { + return E_OUTOFMEMORY; + } + + // read the data in + DWORD bytesRead = 0; + if (!ReadFile(hFile.get(), + ddsData.get(), + fileInfo.EndOfFile.LowPart, + &bytesRead, + nullptr + )) + { + ddsData.reset(); + return HRESULT_FROM_WIN32(GetLastError()); + } + + if (bytesRead < fileInfo.EndOfFile.LowPart) + { + ddsData.reset(); + return E_FAIL; + } + + // DDS files always start with the same magic number ("DDS ") + auto const dwMagicNumber = *reinterpret_cast(ddsData.get()); + if (dwMagicNumber != DDS_MAGIC) + { + ddsData.reset(); + return E_FAIL; + } + + auto hdr = reinterpret_cast(ddsData.get() + sizeof(uint32_t)); + + // Verify header to validate DDS file + if (hdr->size != sizeof(DDS_HEADER) || + hdr->ddspf.size != sizeof(DDS_PIXELFORMAT)) + { + ddsData.reset(); + return E_FAIL; + } + + // Check for DX10 extension + bool bDXT10Header = false; + if ((hdr->ddspf.flags & DDS_FOURCC) && + (MAKEFOURCC('D', 'X', '1', '0') == hdr->ddspf.fourCC)) + { + // Must be long enough for both headers and magic value + if (fileInfo.EndOfFile.LowPart < (sizeof(uint32_t) + sizeof(DDS_HEADER) + sizeof(DDS_HEADER_DXT10))) + { + ddsData.reset(); + return E_FAIL; + } + + bDXT10Header = true; + } + + // setup the pointers in the process request + *header = hdr; + auto offset = sizeof(uint32_t) + sizeof(DDS_HEADER) + + (bDXT10Header ? sizeof(DDS_HEADER_DXT10) : 0u); + *bitData = ddsData.get() + offset; + *bitSize = fileInfo.EndOfFile.LowPart - offset; + + return S_OK; + } + + //-------------------------------------------------------------------------------------- + // Get surface information for a particular format + //-------------------------------------------------------------------------------------- + inline HRESULT GetSurfaceInfo( + _In_ size_t width, + _In_ size_t height, + _In_ DXGI_FORMAT fmt, + _Out_opt_ size_t* outNumBytes, + _Out_opt_ size_t* outRowBytes, + _Out_opt_ size_t* outNumRows) noexcept + { + uint64_t numBytes = 0; + uint64_t rowBytes = 0; + uint64_t numRows = 0; + + bool bc = false; + bool packed = false; + bool planar = false; + size_t bpe = 0; + switch (fmt) + { + case DXGI_FORMAT_BC1_TYPELESS: + case DXGI_FORMAT_BC1_UNORM: + case DXGI_FORMAT_BC1_UNORM_SRGB: + case DXGI_FORMAT_BC4_TYPELESS: + case DXGI_FORMAT_BC4_UNORM: + case DXGI_FORMAT_BC4_SNORM: + bc = true; + bpe = 8; + break; + + case DXGI_FORMAT_BC2_TYPELESS: + case DXGI_FORMAT_BC2_UNORM: + case DXGI_FORMAT_BC2_UNORM_SRGB: + case DXGI_FORMAT_BC3_TYPELESS: + case DXGI_FORMAT_BC3_UNORM: + case DXGI_FORMAT_BC3_UNORM_SRGB: + case DXGI_FORMAT_BC5_TYPELESS: + case DXGI_FORMAT_BC5_UNORM: + case DXGI_FORMAT_BC5_SNORM: + case DXGI_FORMAT_BC6H_TYPELESS: + case DXGI_FORMAT_BC6H_UF16: + case DXGI_FORMAT_BC6H_SF16: + case DXGI_FORMAT_BC7_TYPELESS: + case DXGI_FORMAT_BC7_UNORM: + case DXGI_FORMAT_BC7_UNORM_SRGB: + bc = true; + bpe = 16; + break; + + case DXGI_FORMAT_R8G8_B8G8_UNORM: + case DXGI_FORMAT_G8R8_G8B8_UNORM: + case DXGI_FORMAT_YUY2: + packed = true; + bpe = 4; + break; + + case DXGI_FORMAT_Y210: + case DXGI_FORMAT_Y216: + packed = true; + bpe = 8; + break; + + case DXGI_FORMAT_NV12: + case DXGI_FORMAT_420_OPAQUE: + if ((height % 2) != 0) + { + // Requires a height alignment of 2. + return E_INVALIDARG; + } + planar = true; + bpe = 2; + break; + + #if (_WIN32_WINNT >= _WIN32_WINNT_WIN10) + + case DXGI_FORMAT_P208: + planar = true; + bpe = 2; + break; + + #endif + + case DXGI_FORMAT_P010: + case DXGI_FORMAT_P016: + if ((height % 2) != 0) + { + // Requires a height alignment of 2. + return E_INVALIDARG; + } + planar = true; + bpe = 4; + break; + + #if (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) + + case DXGI_FORMAT_D16_UNORM_S8_UINT: + case DXGI_FORMAT_R16_UNORM_X8_TYPELESS: + case DXGI_FORMAT_X16_TYPELESS_G8_UINT: + planar = true; + bpe = 4; + break; + + #endif + + default: + break; + } + + if (bc) + { + uint64_t numBlocksWide = 0; + if (width > 0) + { + numBlocksWide = std::max(1u, (uint64_t(width) + 3u) / 4u); + } + uint64_t numBlocksHigh = 0; + if (height > 0) + { + numBlocksHigh = std::max(1u, (uint64_t(height) + 3u) / 4u); + } + rowBytes = numBlocksWide * bpe; + numRows = numBlocksHigh; + numBytes = rowBytes * numBlocksHigh; + } + else if (packed) + { + rowBytes = ((uint64_t(width) + 1u) >> 1) * bpe; + numRows = uint64_t(height); + numBytes = rowBytes * height; + } + else if (fmt == DXGI_FORMAT_NV11) + { + rowBytes = ((uint64_t(width) + 3u) >> 2) * 4u; + numRows = uint64_t(height) * 2u; // Direct3D makes this simplifying assumption, although it is larger than the 4:1:1 data + numBytes = rowBytes * numRows; + } + else if (planar) + { + rowBytes = ((uint64_t(width) + 1u) >> 1) * bpe; + numBytes = (rowBytes * uint64_t(height)) + ((rowBytes * uint64_t(height) + 1u) >> 1); + numRows = height + ((uint64_t(height) + 1u) >> 1); + } + else + { + const size_t bpp = BitsPerPixel(fmt); + if (!bpp) + return E_INVALIDARG; + + rowBytes = (uint64_t(width) * bpp + 7u) / 8u; // round up to nearest byte + numRows = uint64_t(height); + numBytes = rowBytes * height; + } + + #if defined(_M_IX86) || defined(_M_ARM) || defined(_M_HYBRID_X86_ARM64) + static_assert(sizeof(size_t) == 4, "Not a 32-bit platform!"); + if (numBytes > UINT32_MAX || rowBytes > UINT32_MAX || numRows > UINT32_MAX) + return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); + #else + static_assert(sizeof(size_t) == 8, "Not a 64-bit platform!"); + #endif + + if (outNumBytes) + { + *outNumBytes = static_cast(numBytes); + } + if (outRowBytes) + { + *outRowBytes = static_cast(rowBytes); + } + if (outNumRows) + { + *outNumRows = static_cast(numRows); + } + + return S_OK; + } + + //-------------------------------------------------------------------------------------- + #define ISBITMASK( r,g,b,a ) ( ddpf.RBitMask == r && ddpf.GBitMask == g && ddpf.BBitMask == b && ddpf.ABitMask == a ) + + inline DXGI_FORMAT GetDXGIFormat(const DDS_PIXELFORMAT& ddpf) noexcept + { + if (ddpf.flags & DDS_RGB) + { + // Note that sRGB formats are written using the "DX10" extended header + + switch (ddpf.RGBBitCount) + { + case 32: + if (ISBITMASK(0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000)) + { + return DXGI_FORMAT_R8G8B8A8_UNORM; + } + + if (ISBITMASK(0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000)) + { + return DXGI_FORMAT_B8G8R8A8_UNORM; + } + + if (ISBITMASK(0x00ff0000, 0x0000ff00, 0x000000ff, 0)) + { + return DXGI_FORMAT_B8G8R8X8_UNORM; + } + + // No DXGI format maps to ISBITMASK(0x000000ff,0x0000ff00,0x00ff0000,0) aka D3DFMT_X8B8G8R8 + + // Note that many common DDS reader/writers (including D3DX) swap the + // the RED/BLUE masks for 10:10:10:2 formats. We assume + // below that the 'backwards' header mask is being used since it is most + // likely written by D3DX. The more robust solution is to use the 'DX10' + // header extension and specify the DXGI_FORMAT_R10G10B10A2_UNORM format directly + + // For 'correct' writers, this should be 0x000003ff,0x000ffc00,0x3ff00000 for RGB data + if (ISBITMASK(0x3ff00000, 0x000ffc00, 0x000003ff, 0xc0000000)) + { + return DXGI_FORMAT_R10G10B10A2_UNORM; + } + + // No DXGI format maps to ISBITMASK(0x000003ff,0x000ffc00,0x3ff00000,0xc0000000) aka D3DFMT_A2R10G10B10 + + if (ISBITMASK(0x0000ffff, 0xffff0000, 0, 0)) + { + return DXGI_FORMAT_R16G16_UNORM; + } + + if (ISBITMASK(0xffffffff, 0, 0, 0)) + { + // Only 32-bit color channel format in D3D9 was R32F + return DXGI_FORMAT_R32_FLOAT; // D3DX writes this out as a FourCC of 114 + } + break; + + case 24: + // No 24bpp DXGI formats aka D3DFMT_R8G8B8 + break; + + case 16: + if (ISBITMASK(0x7c00, 0x03e0, 0x001f, 0x8000)) + { + return DXGI_FORMAT_B5G5R5A1_UNORM; + } + if (ISBITMASK(0xf800, 0x07e0, 0x001f, 0)) + { + return DXGI_FORMAT_B5G6R5_UNORM; + } + + // No DXGI format maps to ISBITMASK(0x7c00,0x03e0,0x001f,0) aka D3DFMT_X1R5G5B5 + + if (ISBITMASK(0x0f00, 0x00f0, 0x000f, 0xf000)) + { + return DXGI_FORMAT_B4G4R4A4_UNORM; + } + + // NVTT versions 1.x wrote this as RGB instead of LUMINANCE + if (ISBITMASK(0x00ff, 0, 0, 0xff00)) + { + return DXGI_FORMAT_R8G8_UNORM; + } + if (ISBITMASK(0xffff, 0, 0, 0)) + { + return DXGI_FORMAT_R16_UNORM; + } + + // No DXGI format maps to ISBITMASK(0x0f00,0x00f0,0x000f,0) aka D3DFMT_X4R4G4B4 + + // No 3:3:2:8 or paletted DXGI formats aka D3DFMT_A8R3G3B2, D3DFMT_A8P8, etc. + break; + + case 8: + // NVTT versions 1.x wrote this as RGB instead of LUMINANCE + if (ISBITMASK(0xff, 0, 0, 0)) + { + return DXGI_FORMAT_R8_UNORM; + } + + // No 3:3:2 or paletted DXGI formats aka D3DFMT_R3G3B2, D3DFMT_P8 + break; + } + } + else if (ddpf.flags & DDS_LUMINANCE) + { + switch (ddpf.RGBBitCount) + { + case 16: + if (ISBITMASK(0xffff, 0, 0, 0)) + { + return DXGI_FORMAT_R16_UNORM; // D3DX10/11 writes this out as DX10 extension + } + if (ISBITMASK(0x00ff, 0, 0, 0xff00)) + { + return DXGI_FORMAT_R8G8_UNORM; // D3DX10/11 writes this out as DX10 extension + } + break; + + case 8: + if (ISBITMASK(0xff, 0, 0, 0)) + { + return DXGI_FORMAT_R8_UNORM; // D3DX10/11 writes this out as DX10 extension + } + + // No DXGI format maps to ISBITMASK(0x0f,0,0,0xf0) aka D3DFMT_A4L4 + + if (ISBITMASK(0x00ff, 0, 0, 0xff00)) + { + return DXGI_FORMAT_R8G8_UNORM; // Some DDS writers assume the bitcount should be 8 instead of 16 + } + break; + } + } + else if (ddpf.flags & DDS_ALPHA) + { + if (8 == ddpf.RGBBitCount) + { + return DXGI_FORMAT_A8_UNORM; + } + } + else if (ddpf.flags & DDS_BUMPDUDV) + { + switch (ddpf.RGBBitCount) + { + case 32: + if (ISBITMASK(0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000)) + { + return DXGI_FORMAT_R8G8B8A8_SNORM; // D3DX10/11 writes this out as DX10 extension + } + if (ISBITMASK(0x0000ffff, 0xffff0000, 0, 0)) + { + return DXGI_FORMAT_R16G16_SNORM; // D3DX10/11 writes this out as DX10 extension + } + + // No DXGI format maps to ISBITMASK(0x3ff00000, 0x000ffc00, 0x000003ff, 0xc0000000) aka D3DFMT_A2W10V10U10 + break; + + case 16: + if (ISBITMASK(0x00ff, 0xff00, 0, 0)) + { + return DXGI_FORMAT_R8G8_SNORM; // D3DX10/11 writes this out as DX10 extension + } + break; + } + + // No DXGI format maps to DDPF_BUMPLUMINANCE aka D3DFMT_L6V5U5, D3DFMT_X8L8V8U8 + } + else if (ddpf.flags & DDS_FOURCC) + { + if (MAKEFOURCC('D', 'X', 'T', '1') == ddpf.fourCC) + { + return DXGI_FORMAT_BC1_UNORM; + } + if (MAKEFOURCC('D', 'X', 'T', '3') == ddpf.fourCC) + { + return DXGI_FORMAT_BC2_UNORM; + } + if (MAKEFOURCC('D', 'X', 'T', '5') == ddpf.fourCC) + { + return DXGI_FORMAT_BC3_UNORM; + } + + // While pre-multiplied alpha isn't directly supported by the DXGI formats, + // they are basically the same as these BC formats so they can be mapped + if (MAKEFOURCC('D', 'X', 'T', '2') == ddpf.fourCC) + { + return DXGI_FORMAT_BC2_UNORM; + } + if (MAKEFOURCC('D', 'X', 'T', '4') == ddpf.fourCC) + { + return DXGI_FORMAT_BC3_UNORM; + } + + if (MAKEFOURCC('A', 'T', 'I', '1') == ddpf.fourCC) + { + return DXGI_FORMAT_BC4_UNORM; + } + if (MAKEFOURCC('B', 'C', '4', 'U') == ddpf.fourCC) + { + return DXGI_FORMAT_BC4_UNORM; + } + if (MAKEFOURCC('B', 'C', '4', 'S') == ddpf.fourCC) + { + return DXGI_FORMAT_BC4_SNORM; + } + + if (MAKEFOURCC('A', 'T', 'I', '2') == ddpf.fourCC) + { + return DXGI_FORMAT_BC5_UNORM; + } + if (MAKEFOURCC('B', 'C', '5', 'U') == ddpf.fourCC) + { + return DXGI_FORMAT_BC5_UNORM; + } + if (MAKEFOURCC('B', 'C', '5', 'S') == ddpf.fourCC) + { + return DXGI_FORMAT_BC5_SNORM; + } + + // BC6H and BC7 are written using the "DX10" extended header + + if (MAKEFOURCC('R', 'G', 'B', 'G') == ddpf.fourCC) + { + return DXGI_FORMAT_R8G8_B8G8_UNORM; + } + if (MAKEFOURCC('G', 'R', 'G', 'B') == ddpf.fourCC) + { + return DXGI_FORMAT_G8R8_G8B8_UNORM; + } + + if (MAKEFOURCC('Y', 'U', 'Y', '2') == ddpf.fourCC) + { + return DXGI_FORMAT_YUY2; + } + + // Check for D3DFORMAT enums being set here + switch (ddpf.fourCC) + { + case 36: // D3DFMT_A16B16G16R16 + return DXGI_FORMAT_R16G16B16A16_UNORM; + + case 110: // D3DFMT_Q16W16V16U16 + return DXGI_FORMAT_R16G16B16A16_SNORM; + + case 111: // D3DFMT_R16F + return DXGI_FORMAT_R16_FLOAT; + + case 112: // D3DFMT_G16R16F + return DXGI_FORMAT_R16G16_FLOAT; + + case 113: // D3DFMT_A16B16G16R16F + return DXGI_FORMAT_R16G16B16A16_FLOAT; + + case 114: // D3DFMT_R32F + return DXGI_FORMAT_R32_FLOAT; + + case 115: // D3DFMT_G32R32F + return DXGI_FORMAT_R32G32_FLOAT; + + case 116: // D3DFMT_A32B32G32R32F + return DXGI_FORMAT_R32G32B32A32_FLOAT; + + // No DXGI format maps to D3DFMT_CxV8U8 + } + } + + return DXGI_FORMAT_UNKNOWN; + } + + #undef ISBITMASK + + //-------------------------------------------------------------------------------------- + inline DirectX::DDS_ALPHA_MODE GetAlphaMode(_In_ const DDS_HEADER* header) noexcept + { + if (header->ddspf.flags & DDS_FOURCC) + { + if (MAKEFOURCC('D', 'X', '1', '0') == header->ddspf.fourCC) + { + auto d3d10ext = reinterpret_cast(reinterpret_cast(header) + sizeof(DDS_HEADER)); + auto const mode = static_cast(d3d10ext->miscFlags2 & DDS_MISC_FLAGS2_ALPHA_MODE_MASK); + switch (mode) + { + case DDS_ALPHA_MODE_STRAIGHT: + case DDS_ALPHA_MODE_PREMULTIPLIED: + case DDS_ALPHA_MODE_OPAQUE: + case DDS_ALPHA_MODE_CUSTOM: + return mode; + + case DDS_ALPHA_MODE_UNKNOWN: + default: + break; + } + } + else if ((MAKEFOURCC('D', 'X', 'T', '2') == header->ddspf.fourCC) + || (MAKEFOURCC('D', 'X', 'T', '4') == header->ddspf.fourCC)) + { + return DDS_ALPHA_MODE_PREMULTIPLIED; + } + } + + return DDS_ALPHA_MODE_UNKNOWN; + } + + //-------------------------------------------------------------------------------------- + class auto_delete_file + { + public: + auto_delete_file(HANDLE hFile) noexcept : m_handle(hFile) {} + + auto_delete_file(const auto_delete_file&) = delete; + auto_delete_file& operator=(const auto_delete_file&) = delete; + + auto_delete_file(const auto_delete_file&&) = delete; + auto_delete_file& operator=(const auto_delete_file&&) = delete; + + ~auto_delete_file() + { + if (m_handle) + { + FILE_DISPOSITION_INFO info = {}; + info.DeleteFile = TRUE; + std::ignore = SetFileInformationByHandle(m_handle, FileDispositionInfo, &info, sizeof(info)); + } + } + + void clear() noexcept { m_handle = nullptr; } + + private: + HANDLE m_handle; + }; + + class auto_delete_file_wic + { + public: + auto_delete_file_wic(Microsoft::WRL::ComPtr& hFile, LPCWSTR szFile) noexcept : m_filename(szFile), m_handle(hFile) {} + + auto_delete_file_wic(const auto_delete_file_wic&) = delete; + auto_delete_file_wic& operator=(const auto_delete_file_wic&) = delete; + + auto_delete_file_wic(const auto_delete_file_wic&&) = delete; + auto_delete_file_wic& operator=(const auto_delete_file_wic&&) = delete; + + ~auto_delete_file_wic() + { + if (m_filename) + { + m_handle.Reset(); + DeleteFileW(m_filename); + } + } + + void clear() noexcept { m_filename = nullptr; } + + private: + LPCWSTR m_filename; + Microsoft::WRL::ComPtr& m_handle; + }; + + inline uint32_t CountMips(uint32_t width, uint32_t height) noexcept + { + if (width == 0 || height == 0) + return 0; + + uint32_t count = 1; + while (width > 1 || height > 1) + { + width >>= 1; + height >>= 1; + count++; + } + return count; + } + + inline void FitPowerOf2(UINT origx, UINT origy, _Inout_ UINT& targetx, _Inout_ UINT& targety, size_t maxsize) + { + const float origAR = float(origx) / float(origy); + + if (origx > origy) + { + size_t x; + for (x = maxsize; x > 1; x >>= 1) { if (x <= targetx) break; } + targetx = UINT(x); + + float bestScore = FLT_MAX; + for (size_t y = maxsize; y > 0; y >>= 1) + { + const float score = fabsf((float(x) / float(y)) - origAR); + if (score < bestScore) + { + bestScore = score; + targety = UINT(y); + } + } + } + else + { + size_t y; + for (y = maxsize; y > 1; y >>= 1) { if (y <= targety) break; } + targety = UINT(y); + + float bestScore = FLT_MAX; + for (size_t x = maxsize; x > 0; x >>= 1) + { + const float score = fabsf((float(x) / float(y)) - origAR); + if (score < bestScore) + { + bestScore = score; + targetx = UINT(x); + } + } + } + } + } +} diff --git a/Common/DirectXTK12/Src/Model.cpp b/Common/DirectXTK12/Src/Model.cpp new file mode 100644 index 0000000..db7cbeb --- /dev/null +++ b/Common/DirectXTK12/Src/Model.cpp @@ -0,0 +1,850 @@ +//-------------------------------------------------------------------------------------- +// File: Model.cpp +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "pch.h" +#include "Model.h" + +#include "CommonStates.h" +#include "DescriptorHeap.h" +#include "DirectXHelpers.h" +#include "Effects.h" +#include "PlatformHelpers.h" +#include "ResourceUploadBatch.h" + +using namespace DirectX; + +#if !defined(_CPPRTTI) && !defined(__GXX_RTTI) +#error Model requires RTTI +#endif + + +//-------------------------------------------------------------------------------------- +// ModelMeshPart +//-------------------------------------------------------------------------------------- + +ModelMeshPart::ModelMeshPart(uint32_t ipartIndex) noexcept : + partIndex(ipartIndex), + materialIndex(0), + indexCount(0), + startIndex(0), + vertexOffset(0), + vertexStride(0), + vertexCount(0), + indexBufferSize(0), + vertexBufferSize(0), + primitiveType(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST), + indexFormat(DXGI_FORMAT_R16_UINT) +{ +} + + +ModelMeshPart::~ModelMeshPart() +{ +} + + +_Use_decl_annotations_ +void ModelMeshPart::Draw(_In_ ID3D12GraphicsCommandList* commandList) const +{ + if (!indexBufferSize || !vertexBufferSize) + { + DebugTrace("ERROR: Model part missing values for vertex and/or index buffer size (indexBufferSize %u, vertexBufferSize %u)!\n", indexBufferSize, vertexBufferSize); + throw std::runtime_error("ModelMeshPart"); + } + + if (!staticIndexBuffer && !indexBuffer) + { + DebugTrace("ERROR: Model part missing index buffer!\n"); + throw std::runtime_error("ModelMeshPart"); + } + + if (!staticVertexBuffer && !vertexBuffer) + { + DebugTrace("ERROR: Model part missing vertex buffer!\n"); + throw std::runtime_error("ModelMeshPart"); + } + + D3D12_VERTEX_BUFFER_VIEW vbv; + vbv.BufferLocation = staticVertexBuffer ? staticVertexBuffer->GetGPUVirtualAddress() : vertexBuffer.GpuAddress(); + vbv.StrideInBytes = vertexStride; + vbv.SizeInBytes = vertexBufferSize; + commandList->IASetVertexBuffers(0, 1, &vbv); + + D3D12_INDEX_BUFFER_VIEW ibv; + ibv.BufferLocation = staticIndexBuffer ? staticIndexBuffer->GetGPUVirtualAddress() : indexBuffer.GpuAddress(); + ibv.SizeInBytes = indexBufferSize; + ibv.Format = indexFormat; + commandList->IASetIndexBuffer(&ibv); + + commandList->IASetPrimitiveTopology(primitiveType); + + commandList->DrawIndexedInstanced(indexCount, 1, startIndex, vertexOffset, 0); +} + + +_Use_decl_annotations_ +void ModelMeshPart::DrawInstanced( + ID3D12GraphicsCommandList* commandList, + uint32_t instanceCount, + uint32_t startInstance) const +{ + if (!indexBufferSize || !vertexBufferSize) + { + DebugTrace("ERROR: Model part missing values for vertex and/or index buffer size (indexBufferSize %u, vertexBufferSize %u)!\n", indexBufferSize, vertexBufferSize); + throw std::runtime_error("ModelMeshPart"); + } + + if (!staticIndexBuffer && !indexBuffer) + { + DebugTrace("ERROR: Model part missing index buffer!\n"); + throw std::runtime_error("ModelMeshPart"); + } + + if (!staticVertexBuffer && !vertexBuffer) + { + DebugTrace("ERROR: Model part missing vertex buffer!\n"); + throw std::runtime_error("ModelMeshPart"); + } + + D3D12_VERTEX_BUFFER_VIEW vbv; + vbv.BufferLocation = staticVertexBuffer ? staticVertexBuffer->GetGPUVirtualAddress() : vertexBuffer.GpuAddress(); + vbv.StrideInBytes = vertexStride; + vbv.SizeInBytes = vertexBufferSize; + commandList->IASetVertexBuffers(0, 1, &vbv); + + D3D12_INDEX_BUFFER_VIEW ibv; + ibv.BufferLocation = staticIndexBuffer ? staticIndexBuffer->GetGPUVirtualAddress() : indexBuffer.GpuAddress(); + ibv.SizeInBytes = indexBufferSize; + ibv.Format = indexFormat; + commandList->IASetIndexBuffer(&ibv); + + commandList->IASetPrimitiveTopology(primitiveType); + + commandList->DrawIndexedInstanced(indexCount, instanceCount, startIndex, vertexOffset, startInstance); +} + + +_Use_decl_annotations_ +void ModelMeshPart::DrawMeshParts( + ID3D12GraphicsCommandList* commandList, + const ModelMeshPart::Collection& meshParts) +{ + for (const auto& it : meshParts) + { + auto part = it.get(); + assert(part != nullptr); + + part->Draw(commandList); + } +} + + +_Use_decl_annotations_ +void ModelMeshPart::DrawMeshParts( + ID3D12GraphicsCommandList* commandList, + const ModelMeshPart::Collection& meshParts, + ModelMeshPart::DrawCallback callback) +{ + for (const auto& it : meshParts) + { + auto part = it.get(); + assert(part != nullptr); + + callback(commandList, *part); + part->Draw(commandList); + } +} + + +_Use_decl_annotations_ +void ModelMeshPart::DrawMeshParts( + ID3D12GraphicsCommandList* commandList, + const ModelMeshPart::Collection& meshParts, + IEffect* effect) +{ + effect->Apply(commandList); + DrawMeshParts(commandList, meshParts); +} + + +//-------------------------------------------------------------------------------------- +// ModelMesh +//-------------------------------------------------------------------------------------- + +ModelMesh::ModelMesh() noexcept : + boneIndex(ModelBone::c_Invalid) +{ +} + + +ModelMesh::~ModelMesh() +{ +} + +// Draw the mesh +void ModelMesh::DrawOpaque(_In_ ID3D12GraphicsCommandList* commandList) const +{ + ModelMeshPart::DrawMeshParts(commandList, opaqueMeshParts); +} + +void ModelMesh::DrawAlpha(_In_ ID3D12GraphicsCommandList* commandList) const +{ + ModelMeshPart::DrawMeshParts(commandList, alphaMeshParts); +} + + +// Draw the mesh with an effect +void ModelMesh::DrawOpaque(_In_ ID3D12GraphicsCommandList* commandList, _In_ IEffect* effect) const +{ + ModelMeshPart::DrawMeshParts(commandList, opaqueMeshParts, effect); +} + +void ModelMesh::DrawAlpha(_In_ ID3D12GraphicsCommandList* commandList, _In_ IEffect* effect) const +{ + ModelMeshPart::DrawMeshParts(commandList, alphaMeshParts, effect); +} + + +// Draw the mesh with a callback for each mesh part +void ModelMesh::DrawOpaque(_In_ ID3D12GraphicsCommandList* commandList, ModelMeshPart::DrawCallback callback) const +{ + ModelMeshPart::DrawMeshParts(commandList, opaqueMeshParts, callback); +} + +void ModelMesh::DrawAlpha(_In_ ID3D12GraphicsCommandList* commandList, ModelMeshPart::DrawCallback callback) const +{ + ModelMeshPart::DrawMeshParts(commandList, alphaMeshParts, callback); +} + + +//-------------------------------------------------------------------------------------- +// Model +//-------------------------------------------------------------------------------------- + +Model::Model() noexcept +{ +} + +Model::~Model() +{ +} + +Model::Model(Model const& other) : + meshes(other.meshes), + materials(other.materials), + textureNames(other.textureNames), + bones(other.bones), + name(other.name) +{ + const size_t nbones = other.bones.size(); + if (nbones > 0) + { + if (other.boneMatrices) + { + boneMatrices = ModelBone::MakeArray(nbones); + memcpy(boneMatrices.get(), other.boneMatrices.get(), sizeof(XMMATRIX) * nbones); + } + if (other.invBindPoseMatrices) + { + invBindPoseMatrices = ModelBone::MakeArray(nbones); + memcpy(invBindPoseMatrices.get(), other.invBindPoseMatrices.get(), sizeof(XMMATRIX) * nbones); + } + } +} + +Model& Model::operator= (Model const& rhs) +{ + if (this != &rhs) + { + Model tmp(rhs); + std::swap(meshes, tmp.meshes); + std::swap(materials, tmp.materials); + std::swap(textureNames, tmp.textureNames); + std::swap(bones, tmp.bones); + std::swap(boneMatrices, tmp.boneMatrices); + std::swap(invBindPoseMatrices, tmp.invBindPoseMatrices); + std::swap(name, tmp.name); + } + return *this; +} + + +// Load texture resources. +int Model::LoadTextures(IEffectTextureFactory& texFactory, int destinationDescriptorOffset) const +{ + for (size_t i = 0; i < textureNames.size(); ++i) + { + texFactory.CreateTexture(textureNames[i].c_str(), destinationDescriptorOffset + static_cast(i)); + } + + return static_cast(textureNames.size()); +} + + +// Load texture resources (helper function). +_Use_decl_annotations_ +std::unique_ptr Model::LoadTextures( + ID3D12Device* device, + ResourceUploadBatch& resourceUploadBatch, + const wchar_t* texturesPath, + D3D12_DESCRIPTOR_HEAP_FLAGS flags) const +{ + if (textureNames.empty()) + return nullptr; + + std::unique_ptr texFactory = std::make_unique( + device, + resourceUploadBatch, + textureNames.size(), + flags); + if (texturesPath != nullptr && *texturesPath != 0) + { + texFactory->SetDirectory(texturesPath); + } + + LoadTextures(*texFactory); + + return texFactory; +} + + +// Load VB/IB resources for static geometry. +_Use_decl_annotations_ +void Model::LoadStaticBuffers( + ID3D12Device* device, + ResourceUploadBatch& resourceUploadBatch, + bool keepMemory) +{ + // Gather all unique parts + std::set uniqueParts; + for (const auto& mesh : meshes) + { + for (const auto& part : mesh->opaqueMeshParts) + { + uniqueParts.insert(part.get()); + } + for (const auto& part : mesh->alphaMeshParts) + { + uniqueParts.insert(part.get()); + } + } + + const CD3DX12_HEAP_PROPERTIES heapProperties(D3D12_HEAP_TYPE_DEFAULT); + + for (auto it = uniqueParts.cbegin(); it != uniqueParts.cend(); ++it) + { + auto part = *it; + + // Convert dynamic VB to static VB + if (!part->staticVertexBuffer) + { + if (!part->vertexBuffer) + { + DebugTrace("ERROR: Model part missing vertex buffer!\n"); + throw std::runtime_error("ModelMeshPart"); + } + + part->vertexBufferSize = static_cast(part->vertexBuffer.Size()); + + auto const desc = CD3DX12_RESOURCE_DESC::Buffer(part->vertexBuffer.Size()); + + ThrowIfFailed(device->CreateCommittedResource( + &heapProperties, + D3D12_HEAP_FLAG_NONE, + &desc, + c_initialCopyTargetState, + nullptr, + IID_GRAPHICS_PPV_ARGS(part->staticVertexBuffer.GetAddressOf()) + )); + + SetDebugObjectName(part->staticVertexBuffer.Get(), L"ModelMeshPart"); + + resourceUploadBatch.Upload(part->staticVertexBuffer.Get(), part->vertexBuffer); + + resourceUploadBatch.Transition(part->staticVertexBuffer.Get(), + D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER); + + // Scan for any other part with the same vertex buffer for sharing + for (auto sit = std::next(it); sit != uniqueParts.cend(); ++sit) + { + auto sharePart = *sit; + assert(sharePart != part); + + if (sharePart->staticVertexBuffer) + continue; + + if (sharePart->vertexBuffer == part->vertexBuffer) + { + sharePart->vertexBufferSize = part->vertexBufferSize; + sharePart->staticVertexBuffer = part->staticVertexBuffer; + + if (!keepMemory) + { + sharePart->vertexBuffer.Reset(); + } + } + } + + if (!keepMemory) + { + part->vertexBuffer.Reset(); + } + } + + // Convert dynamic IB to static IB + if (!part->staticIndexBuffer) + { + if (!part->indexBuffer) + { + DebugTrace("ERROR: Model part missing index buffer!\n"); + throw std::runtime_error("ModelMeshPart"); + } + + part->indexBufferSize = static_cast(part->indexBuffer.Size()); + + auto const desc = CD3DX12_RESOURCE_DESC::Buffer(part->indexBuffer.Size()); + + ThrowIfFailed(device->CreateCommittedResource( + &heapProperties, + D3D12_HEAP_FLAG_NONE, + &desc, + c_initialCopyTargetState, + nullptr, + IID_GRAPHICS_PPV_ARGS(part->staticIndexBuffer.GetAddressOf()) + )); + + SetDebugObjectName(part->staticIndexBuffer.Get(), L"ModelMeshPart"); + + resourceUploadBatch.Upload(part->staticIndexBuffer.Get(), part->indexBuffer); + + resourceUploadBatch.Transition(part->staticIndexBuffer.Get(), + D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_INDEX_BUFFER); + + // Scan for any other part with the same index buffer for sharing + for (auto sit = std::next(it); sit != uniqueParts.cend(); ++sit) + { + auto sharePart = *sit; + assert(sharePart != part); + + if (sharePart->staticIndexBuffer) + continue; + + if (sharePart->indexBuffer == part->indexBuffer) + { + sharePart->indexBufferSize = part->indexBufferSize; + sharePart->staticIndexBuffer = part->staticIndexBuffer; + + if (!keepMemory) + { + sharePart->indexBuffer.Reset(); + } + } + } + + if (!keepMemory) + { + part->indexBuffer.Reset(); + } + } + } +} + + +// Create effects for each mesh piece. +Model::EffectCollection Model::CreateEffects( + IEffectFactory& fxFactory, + const EffectPipelineStateDescription& opaquePipelineState, + const EffectPipelineStateDescription& alphaPipelineState, + int textureDescriptorOffset, + int samplerDescriptorOffset) const +{ + if (materials.empty()) + { + DebugTrace("ERROR: Model has no material information to create effects!\n"); + throw std::runtime_error("CreateEffects"); + } + + EffectCollection effects; + + // Count the number of parts + uint32_t partCount = 0; + for (const auto& mesh : meshes) + { + for (const auto& part : mesh->opaqueMeshParts) + partCount = std::max(part->partIndex + 1, partCount); + for (const auto& part : mesh->alphaMeshParts) + partCount = std::max(part->partIndex + 1, partCount); + } + + if (partCount == 0) + return effects; + + // Create an array of effects for each part. We need to have an effect per part because the part's vertex layout + // combines with the material spec to create a unique effect. We rely on the EffectFactory to de-duplicate if it + // wants to. + effects.resize(partCount); + + for (const auto& mesh : meshes) + { + assert(mesh != nullptr); + + for (const auto& part : mesh->opaqueMeshParts) + { + assert(part != nullptr); + + if (part->materialIndex == uint32_t(-1)) + continue; + + // If this fires, you have multiple parts with the same unique ID + assert(effects[part->partIndex] == nullptr); + + effects[part->partIndex] = CreateEffectForMeshPart(fxFactory, opaquePipelineState, alphaPipelineState, textureDescriptorOffset, samplerDescriptorOffset, part.get()); + } + + for (const auto& part : mesh->alphaMeshParts) + { + assert(part != nullptr); + + if (part->materialIndex == uint32_t(-1)) + continue; + + // If this fires, you have multiple parts with the same unique ID + assert(effects[part->partIndex] == nullptr); + + effects[part->partIndex] = CreateEffectForMeshPart(fxFactory, opaquePipelineState, alphaPipelineState, textureDescriptorOffset, samplerDescriptorOffset, part.get()); + } + } + + return effects; +} + + +// Private helper for creating an effect for a mesh part. +_Use_decl_annotations_ +std::shared_ptr Model::CreateEffectForMeshPart( + IEffectFactory& fxFactory, + const EffectPipelineStateDescription& opaquePipelineState, + const EffectPipelineStateDescription& alphaPipelineState, + int textureDescriptorOffset, + int samplerDescriptorOffset, + const ModelMeshPart* part) const +{ + assert(part->materialIndex < materials.size()); + const auto& m = materials[part->materialIndex]; + + if (!part->vbDecl || part->vbDecl->empty()) + throw std::runtime_error("Model mesh part missing vertex buffer input elements data"); + + if (part->vbDecl->size() > D3D12_IA_VERTEX_INPUT_STRUCTURE_ELEMENT_COUNT) + throw std::runtime_error("Model mesh part input layout size is too large for DirectX 12"); + + D3D12_INPUT_LAYOUT_DESC il = {}; + il.NumElements = static_cast(part->vbDecl->size()); + il.pInputElementDescs = part->vbDecl->data(); + + return fxFactory.CreateEffect(m, opaquePipelineState, alphaPipelineState, il, textureDescriptorOffset, samplerDescriptorOffset); +} + + +// Create effects for each mesh piece with the default factory. +_Use_decl_annotations_ +Model::EffectCollection Model::CreateEffects( + const EffectPipelineStateDescription& opaquePipelineState, + const EffectPipelineStateDescription& alphaPipelineState, + ID3D12DescriptorHeap* textureDescriptorHeap, + ID3D12DescriptorHeap* samplerDescriptorHeap, + int textureDescriptorOffset, + int samplerDescriptorOffset) const +{ + EffectFactory fxFactory(textureDescriptorHeap, samplerDescriptorHeap); + return CreateEffects(fxFactory, opaquePipelineState, alphaPipelineState, textureDescriptorOffset, samplerDescriptorOffset); +} + + +// Compute using bone hierarchy from model bone matrices to an array. +_Use_decl_annotations_ +void Model::CopyAbsoluteBoneTransformsTo( + size_t nbones, + XMMATRIX* boneTransforms) const +{ + if (!nbones || !boneTransforms) + { + throw std::invalid_argument("Bone transforms array required"); + } + + if (nbones < bones.size()) + { + throw std::invalid_argument("Bone transforms array is too small"); + } + + if (bones.empty() || !boneMatrices) + { + throw std::runtime_error("Model is missing bones"); + } + + memset(boneTransforms, 0, sizeof(XMMATRIX) * nbones); + + const XMMATRIX id = XMMatrixIdentity(); + size_t visited = 0; + ComputeAbsolute(0, id, bones.size(), boneMatrices.get(), boneTransforms, visited); +} + + +// Compute using bone hierarchy from one array to another array. +_Use_decl_annotations_ +void Model::CopyAbsoluteBoneTransforms( + size_t nbones, + const XMMATRIX* inBoneTransforms, + XMMATRIX* outBoneTransforms) const +{ + if (!nbones || !inBoneTransforms || !outBoneTransforms) + { + throw std::invalid_argument("Bone transforms arrays required"); + } + + if (nbones < bones.size()) + { + throw std::invalid_argument("Bone transforms arrays are too small"); + } + + if (bones.empty()) + { + throw std::runtime_error("Model is missing bones"); + } + + memset(outBoneTransforms, 0, sizeof(XMMATRIX) * nbones); + + const XMMATRIX id = XMMatrixIdentity(); + size_t visited = 0; + ComputeAbsolute(0, id, bones.size(), inBoneTransforms, outBoneTransforms, visited); +} + + +// Private helper for computing hierarchical transforms using bones via recursion. +_Use_decl_annotations_ +void Model::ComputeAbsolute( + uint32_t index, + CXMMATRIX parent, + size_t nbones, + const XMMATRIX* inBoneTransforms, + XMMATRIX* outBoneTransforms, + size_t& visited) const +{ + if (index == ModelBone::c_Invalid || index >= nbones) + return; + + assert(inBoneTransforms != nullptr && outBoneTransforms != nullptr); + + ++visited; // Cycle detection safety! + if (visited > bones.size()) + { + DebugTrace("ERROR: Model::CopyAbsoluteBoneTransformsTo encountered a cycle in the bones!\n"); + throw std::runtime_error("Model bones form an invalid graph"); + } + + XMMATRIX local = inBoneTransforms[index]; + local = XMMatrixMultiply(local, parent); + outBoneTransforms[index] = local; + + if (bones[index].siblingIndex != ModelBone::c_Invalid) + { + ComputeAbsolute(bones[index].siblingIndex, parent, nbones, + inBoneTransforms, outBoneTransforms, visited); + } + + if (bones[index].childIndex != ModelBone::c_Invalid) + { + ComputeAbsolute(bones[index].childIndex, local, nbones, + inBoneTransforms, outBoneTransforms, visited); + } +} + + +// Copy the model bone matrices from an array. +_Use_decl_annotations_ +void Model::CopyBoneTransformsFrom(size_t nbones, const XMMATRIX* boneTransforms) +{ + if (!nbones || !boneTransforms) + { + throw std::invalid_argument("Bone transforms array required"); + } + + if (nbones < bones.size()) + { + throw std::invalid_argument("Bone transforms array is too small"); + } + + if (bones.empty()) + { + throw std::runtime_error("Model is missing bones"); + } + + if (!boneMatrices) + { + boneMatrices = ModelBone::MakeArray(bones.size()); + } + + memcpy(boneMatrices.get(), boneTransforms, bones.size() * sizeof(XMMATRIX)); +} + + +// Copy the model bone matrices to an array. +_Use_decl_annotations_ +void Model::CopyBoneTransformsTo(size_t nbones, XMMATRIX* boneTransforms) const +{ + if (!nbones || !boneTransforms) + { + throw std::invalid_argument("Bone transforms array required"); + } + + if (nbones < bones.size()) + { + throw std::invalid_argument("Bone transforms array is too small"); + } + + if (bones.empty()) + { + throw std::runtime_error("Model is missing bones"); + } + + memcpy(boneTransforms, boneMatrices.get(), bones.size() * sizeof(XMMATRIX)); +} + + +// Updates effect matrices (if applicable). +void XM_CALLCONV Model::UpdateEffectMatrices( + EffectCollection& effects, + FXMMATRIX world, + CXMMATRIX view, + CXMMATRIX proj) +{ + for (auto& fx : effects) + { + auto imatrices = dynamic_cast(fx.get()); + if (imatrices) + { + imatrices->SetMatrices(world, view, proj); + } + } +} + + +// Transition static VB/IB resources (if applicable). +void Model::Transition( + _In_ ID3D12GraphicsCommandList* commandList, + D3D12_RESOURCE_STATES stateBeforeVB, + D3D12_RESOURCE_STATES stateAfterVB, + D3D12_RESOURCE_STATES stateBeforeIB, + D3D12_RESOURCE_STATES stateAfterIB) +{ + UINT count = 0; + D3D12_RESOURCE_BARRIER barrier[64] = {}; + + for (auto& mit : meshes) + { + for (auto& pit : mit->opaqueMeshParts) + { + assert(count < std::size(barrier)); + _Analysis_assume_(count < std::size(barrier)); + + if (stateBeforeIB != stateAfterIB && pit->staticIndexBuffer) + { + barrier[count].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier[count].Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + barrier[count].Transition.pResource = pit->staticIndexBuffer.Get(); + barrier[count].Transition.StateBefore = stateBeforeIB; + barrier[count].Transition.StateAfter = stateAfterIB; + ++count; + + if (count >= std::size(barrier)) + { + commandList->ResourceBarrier(count, barrier); + count = 0; + } + } + + if (stateBeforeVB != stateAfterVB && pit->staticVertexBuffer) + { + barrier[count].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier[count].Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + barrier[count].Transition.pResource = pit->staticVertexBuffer.Get(); + barrier[count].Transition.StateBefore = stateBeforeVB; + barrier[count].Transition.StateAfter = stateAfterVB; + ++count; + + if (count >= std::size(barrier)) + { + commandList->ResourceBarrier(count, barrier); + count = 0; + } + } + } + + for (auto& pit : mit->alphaMeshParts) + { + assert(count < std::size(barrier)); + _Analysis_assume_(count < std::size(barrier)); + + if (stateBeforeIB != stateAfterIB && pit->staticIndexBuffer) + { + barrier[count].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier[count].Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + barrier[count].Transition.pResource = pit->staticIndexBuffer.Get(); + barrier[count].Transition.StateBefore = stateBeforeIB; + barrier[count].Transition.StateAfter = stateAfterIB; + ++count; + + if (count >= std::size(barrier)) + { + commandList->ResourceBarrier(count, barrier); + count = 0; + } + } + + if (stateBeforeVB != stateAfterVB && pit->staticVertexBuffer) + { + barrier[count].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier[count].Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + barrier[count].Transition.pResource = pit->staticVertexBuffer.Get(); + barrier[count].Transition.StateBefore = stateBeforeVB; + barrier[count].Transition.StateAfter = stateAfterVB; + ++count; + + if (count >= std::size(barrier)) + { + commandList->ResourceBarrier(count, barrier); + count = 0; + } + } + } + } + + if (count > 0) + { + commandList->ResourceBarrier(count, barrier); + } +} + + +//-------------------------------------------------------------------------------------- +// Adapters for /Zc:wchar_t- clients + +#if defined(_MSC_VER) && !defined(_NATIVE_WCHAR_T_DEFINED) + +_Use_decl_annotations_ +std::unique_ptr Model::LoadTextures( + ID3D12Device* device, + ResourceUploadBatch& resourceUploadBatch, + const __wchar_t* texturesPath, + D3D12_DESCRIPTOR_HEAP_FLAGS flags) const +{ + return LoadTextures(device, resourceUploadBatch, reinterpret_cast(texturesPath), flags); +} + +#endif diff --git a/Common/DirectXTK12/Src/ModelLoadCMO.cpp b/Common/DirectXTK12/Src/ModelLoadCMO.cpp new file mode 100644 index 0000000..b537871 --- /dev/null +++ b/Common/DirectXTK12/Src/ModelLoadCMO.cpp @@ -0,0 +1,1018 @@ +//-------------------------------------------------------------------------------------- +// File: ModelLoadCMO.cpp +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "pch.h" +#include "CommonStates.h" +#include "Model.h" +#include "DirectXHelpers.h" +#include "BinaryReader.h" +#include "PlatformHelpers.h" + +using namespace DirectX; +using Microsoft::WRL::ComPtr; + + +//-------------------------------------------------------------------------------------- +// .CMO files are built by Visual Studio's MeshContentTask and an example renderer was +// provided in the VS Direct3D Starter Kit +// https://devblogs.microsoft.com/cppblog/developing-an-app-with-the-visual-studio-3d-starter-kit-part-1-of-3/ +// https://devblogs.microsoft.com/cppblog/developing-an-app-with-the-visual-studio-3d-starter-kit-part-2-of-3/ +// https://devblogs.microsoft.com/cppblog/developing-an-app-with-the-visual-studio-3d-starter-kit-part-3-of-3/ +//-------------------------------------------------------------------------------------- + +namespace VSD3DStarter +{ + // .CMO files + + // UINT - Mesh count + // { [Mesh count] + // UINT - Length of name + // wchar_t[] - Name of mesh (if length > 0) + // UINT - Material count + // { [Material count] + // UINT - Length of material name + // wchar_t[] - Name of material (if length > 0) + // Material structure + // UINT - Length of pixel shader name + // wchar_t[] - Name of pixel shader (if length > 0) + // { [8] + // UINT - Length of texture name + // wchar_t[] - Name of texture (if length > 0) + // } + // } + // BYTE - 1 if there is skeletal animation data present + // UINT - SubMesh count + // { [SubMesh count] + // SubMesh structure + // } + // UINT - IB Count + // { [IB Count] + // UINT - Number of USHORTs in IB + // USHORT[] - Array of indices + // } + // UINT - VB Count + // { [VB Count] + // UINT - Number of verts in VB + // Vertex[] - Array of vertices + // } + // UINT - Skinning VB Count + // { [Skinning VB Count] + // UINT - Number of verts in Skinning VB + // SkinningVertex[] - Array of skinning verts + // } + // MeshExtents structure + // [If skeleton animation data is not present, file ends here] + // UINT - Bone count + // { [Bone count] + // UINT - Length of bone name + // wchar_t[] - Bone name (if length > 0) + // Bone structure + // } + // UINT - Animation clip count + // { [Animation clip count] + // UINT - Length of clip name + // wchar_t[] - Clip name (if length > 0) + // float - Start time + // float - End time + // UINT - Keyframe count + // { [Keyframe count] + // Keyframe structure + // } + // } + // } + +#pragma pack(push,1) + + struct Material + { + DirectX::XMFLOAT4 Ambient; + DirectX::XMFLOAT4 Diffuse; + DirectX::XMFLOAT4 Specular; + float SpecularPower; + DirectX::XMFLOAT4 Emissive; + DirectX::XMFLOAT4X4 UVTransform; + }; + + constexpr uint32_t MAX_TEXTURE = 8; + + struct SubMesh + { + uint32_t MaterialIndex; + uint32_t IndexBufferIndex; + uint32_t VertexBufferIndex; + uint32_t StartIndex; + uint32_t PrimCount; + }; + + constexpr uint32_t NUM_BONE_INFLUENCES = 4; + + struct SkinningVertex + { + uint32_t boneIndex[NUM_BONE_INFLUENCES]; + float boneWeight[NUM_BONE_INFLUENCES]; + }; + + struct MeshExtents + { + float CenterX, CenterY, CenterZ; + float Radius; + + float MinX, MinY, MinZ; + float MaxX, MaxY, MaxZ; + }; + + struct Bone + { + int32_t ParentIndex; + DirectX::XMFLOAT4X4 InvBindPos; + DirectX::XMFLOAT4X4 BindPos; + DirectX::XMFLOAT4X4 LocalTransform; + }; + + struct Clip + { + float StartTime; + float EndTime; + uint32_t keys; + }; + + struct Keyframe + { + uint32_t BoneIndex; + float Time; + DirectX::XMFLOAT4X4 Transform; + }; + +#pragma pack(pop) + + const Material s_defMaterial = + { + { 0.2f, 0.2f, 0.2f, 1.f }, + { 0.8f, 0.8f, 0.8f, 1.f }, + { 0.0f, 0.0f, 0.0f, 1.f }, + 1.f, + { 0.0f, 0.0f, 0.0f, 1.0f }, + { 1.f, 0.f, 0.f, 0.f, + 0.f, 1.f, 0.f, 0.f, + 0.f, 0.f, 1.f, 0.f, + 0.f, 0.f, 0.f, 1.f }, + }; +} // namespace + +static_assert(sizeof(VSD3DStarter::Material) == 132, "CMO Mesh structure size incorrect"); +static_assert(sizeof(VSD3DStarter::SubMesh) == 20, "CMO Mesh structure size incorrect"); +static_assert(sizeof(VSD3DStarter::SkinningVertex) == 32, "CMO Mesh structure size incorrect"); +static_assert(sizeof(VSD3DStarter::MeshExtents) == 40, "CMO Mesh structure size incorrect"); +static_assert(sizeof(VSD3DStarter::Bone) == 196, "CMO Mesh structure size incorrect"); +static_assert(sizeof(VSD3DStarter::Clip) == 12, "CMO Mesh structure size incorrect"); +static_assert(sizeof(VSD3DStarter::Keyframe) == 72, "CMO Mesh structure size incorrect"); + +namespace +{ + int GetUniqueTextureIndex(const wchar_t* textureName, std::map& textureDictionary) + { + if (textureName == nullptr || !textureName[0]) + return -1; + + auto i = textureDictionary.find(textureName); + if (i == std::cend(textureDictionary)) + { + int index = static_cast(textureDictionary.size()); + textureDictionary[textureName] = index; + return index; + } + else + { + return i->second; + } + } + + struct VertexPositionNormalTangentColorTexture + { + XMFLOAT3 position; + XMFLOAT3 normal; + XMFLOAT4 tangent; + uint32_t color; + XMFLOAT2 textureCoordinate; + + static const D3D12_INPUT_LAYOUT_DESC InputLayout; + + private: + static constexpr unsigned int InputElementCount = 5; + static const D3D12_INPUT_ELEMENT_DESC InputElements[InputElementCount]; + }; + + const D3D12_INPUT_ELEMENT_DESC VertexPositionNormalTangentColorTexture::InputElements[] = + { + { "SV_Position", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + { "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + { "TANGENT", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + }; + + static_assert(sizeof(VertexPositionNormalTangentColorTexture) == 52, "mismatch with CMO vertex type"); + + const D3D12_INPUT_LAYOUT_DESC VertexPositionNormalTangentColorTexture::InputLayout = + { + VertexPositionNormalTangentColorTexture::InputElements, + VertexPositionNormalTangentColorTexture::InputElementCount + }; + + struct VertexPositionNormalTangentColorTextureSkinning : public VertexPositionNormalTangentColorTexture + { + uint32_t indices; + uint32_t weights; + + void SetBlendIndices(XMUINT4 const& iindices) noexcept + { + this->indices = ((iindices.w & 0xff) << 24) | ((iindices.z & 0xff) << 16) | ((iindices.y & 0xff) << 8) | (iindices.x & 0xff); + } + + void SetBlendWeights(XMFLOAT4 const& iweights) noexcept { SetBlendWeights(XMLoadFloat4(&iweights)); } + void XM_CALLCONV SetBlendWeights(FXMVECTOR iweights) noexcept + { + using namespace DirectX::PackedVector; + + XMUBYTEN4 packed; + XMStoreUByteN4(&packed, iweights); + this->weights = packed.v; + } + + static const D3D12_INPUT_LAYOUT_DESC InputLayout; + + private: + static constexpr unsigned int InputElementCount = 7; + static const D3D12_INPUT_ELEMENT_DESC InputElements[InputElementCount]; + }; + + const D3D12_INPUT_ELEMENT_DESC VertexPositionNormalTangentColorTextureSkinning::InputElements[] = + { + { "SV_Position", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + { "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + { "TANGENT", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + { "BLENDINDICES",0, DXGI_FORMAT_R8G8B8A8_UINT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + { "BLENDWEIGHT", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + }; + + static_assert(sizeof(VertexPositionNormalTangentColorTextureSkinning) == 60, "Vertex struct/layout mismatch"); + + const D3D12_INPUT_LAYOUT_DESC VertexPositionNormalTangentColorTextureSkinning::InputLayout = + { + VertexPositionNormalTangentColorTextureSkinning::InputElements, + VertexPositionNormalTangentColorTextureSkinning::InputElementCount + }; + + //---------------------------------------------------------------------------------- + struct MaterialRecordCMO + { + const VSD3DStarter::Material* pMaterial; + uint32_t materialIndex; + std::wstring name; + std::wstring pixelShader; + std::wstring texture[VSD3DStarter::MAX_TEXTURE]; + + MaterialRecordCMO() noexcept : + pMaterial(nullptr), + materialIndex(0), + texture{} {} + }; + + // Shared VB input element description + INIT_ONCE g_InitOnce = INIT_ONCE_STATIC_INIT; + std::shared_ptr g_vbdecl; + std::shared_ptr g_vbdeclSkinning; + + BOOL CALLBACK InitializeDecl(PINIT_ONCE initOnce, PVOID Parameter, PVOID *lpContext) + { + UNREFERENCED_PARAMETER(initOnce); + UNREFERENCED_PARAMETER(Parameter); + UNREFERENCED_PARAMETER(lpContext); + + g_vbdecl = std::make_shared( + VertexPositionNormalTangentColorTexture::InputLayout.pInputElementDescs, + VertexPositionNormalTangentColorTexture::InputLayout.pInputElementDescs + + VertexPositionNormalTangentColorTexture::InputLayout.NumElements); + + g_vbdeclSkinning = std::make_shared( + VertexPositionNormalTangentColorTextureSkinning::InputLayout.pInputElementDescs, + VertexPositionNormalTangentColorTextureSkinning::InputLayout.pInputElementDescs + + VertexPositionNormalTangentColorTextureSkinning::InputLayout.NumElements); + return TRUE; + } + + inline XMFLOAT3 GetMaterialColor(float r, float g, float b, bool srgb) + { + if (srgb) + { + XMVECTOR v = XMVectorSet(r, g, b, 1.f); + v = XMColorSRGBToRGB(v); + + XMFLOAT3 result; + XMStoreFloat3(&result, v); + return result; + } + else + { + return XMFLOAT3(r, g, b); + } + } +} + + +//====================================================================================== +// Model Loader +//====================================================================================== + +_Use_decl_annotations_ +std::unique_ptr DirectX::Model::CreateFromCMO( + ID3D12Device* device, + const uint8_t* meshData, size_t dataSize, + ModelLoaderFlags flags, + size_t* animsOffset) +{ + if (animsOffset) + { + *animsOffset = 0; + } + + if (!InitOnceExecuteOnce(&g_InitOnce, InitializeDecl, nullptr, nullptr)) + throw std::system_error(std::error_code(static_cast(GetLastError()), std::system_category()), "InitOnceExecuteOnce"); + + if (!device || !meshData) + throw std::invalid_argument("Device and meshData cannot be null"); + + // Meshes + auto nMesh = reinterpret_cast(meshData); + size_t usedSize = sizeof(uint32_t); + if (dataSize < usedSize) + throw std::runtime_error("End of file"); + + if (!*nMesh) + throw std::runtime_error("No meshes found"); + + std::map textureDictionary; + std::vector modelmats; + + auto model = std::make_unique(); + model->meshes.reserve(*nMesh); + + uint32_t partCount = 0; + + for (size_t meshIndex = 0; meshIndex < *nMesh; ++meshIndex) + { + // Mesh name + auto nName = reinterpret_cast(meshData + usedSize); + usedSize += sizeof(uint32_t); + if (dataSize < usedSize) + throw std::runtime_error("End of file"); + + auto meshName = reinterpret_cast(static_cast(meshData + usedSize)); + + usedSize += sizeof(wchar_t)*(*nName); + if (dataSize < usedSize) + throw std::runtime_error("End of file"); + + auto mesh = std::make_shared(); + mesh->name.assign(meshName, *nName); + + // Materials + auto nMats = reinterpret_cast(meshData + usedSize); + usedSize += sizeof(uint32_t); + if (dataSize < usedSize) + throw std::runtime_error("End of file"); + + std::vector materials; + materials.reserve(*nMats); + const size_t baseMaterialIndex = modelmats.size(); + for (size_t j = 0; j < *nMats; ++j) + { + MaterialRecordCMO m; + m.materialIndex = static_cast(baseMaterialIndex + j); + + // Material name + nName = reinterpret_cast(meshData + usedSize); + usedSize += sizeof(uint32_t); + if (dataSize < usedSize) + throw std::runtime_error("End of file"); + + auto matName = reinterpret_cast(static_cast(meshData + usedSize)); + + usedSize += sizeof(wchar_t)*(*nName); + if (dataSize < usedSize) + throw std::runtime_error("End of file"); + + m.name.assign(matName, *nName); + + // Material settings + auto matSetting = reinterpret_cast(meshData + usedSize); + usedSize += sizeof(VSD3DStarter::Material); + if (dataSize < usedSize) + throw std::runtime_error("End of file"); + + m.pMaterial = matSetting; + + // Pixel shader name + nName = reinterpret_cast(meshData + usedSize); + usedSize += sizeof(uint32_t); + if (dataSize < usedSize) + throw std::runtime_error("End of file"); + + auto psName = reinterpret_cast(static_cast(meshData + usedSize)); + + usedSize += sizeof(wchar_t)*(*nName); + if (dataSize < usedSize) + throw std::runtime_error("End of file"); + + m.pixelShader.assign(psName, *nName); + + for (size_t t = 0; t < VSD3DStarter::MAX_TEXTURE; ++t) + { + nName = reinterpret_cast(meshData + usedSize); + usedSize += sizeof(uint32_t); + if (dataSize < usedSize) + throw std::runtime_error("End of file"); + + auto txtName = reinterpret_cast(static_cast(meshData + usedSize)); + + usedSize += sizeof(wchar_t)*(*nName); + if (dataSize < usedSize) + throw std::runtime_error("End of file"); + + m.texture[t].assign(txtName, *nName); + } + + materials.emplace_back(m); + } + + assert(materials.size() == *nMats); + + if (materials.empty()) + { + // Add default material if none defined + MaterialRecordCMO m; + m.materialIndex = static_cast(baseMaterialIndex); + m.pMaterial = &VSD3DStarter::s_defMaterial; + m.name = L"Default"; + materials.emplace_back(m); + } + + // Skeletal data? + const uint8_t* bSkeleton = meshData + usedSize; + usedSize += sizeof(uint8_t); + if (dataSize < usedSize) + throw std::runtime_error("End of file"); + + // Submeshes + auto nSubmesh = reinterpret_cast(meshData + usedSize); + usedSize += sizeof(uint32_t); + if (dataSize < usedSize) + throw std::runtime_error("End of file"); + + if (!*nSubmesh) + throw std::runtime_error("No submeshes found\n"); + + auto subMesh = reinterpret_cast(meshData + usedSize); + usedSize += sizeof(VSD3DStarter::SubMesh) * (*nSubmesh); + if (dataSize < usedSize) + throw std::runtime_error("End of file"); + + // Index buffers + auto nIBs = reinterpret_cast(meshData + usedSize); + usedSize += sizeof(uint32_t); + if (dataSize < usedSize) + throw std::runtime_error("End of file"); + + if (!*nIBs) + throw std::runtime_error("No index buffers found\n"); + + struct IBData + { + size_t nIndices; + const uint16_t* ptr; + }; + + std::vector ibData; + ibData.reserve(*nIBs); + + std::vector ibs; + ibs.resize(*nIBs); + + for (size_t j = 0; j < *nIBs; ++j) + { + auto nIndexes = reinterpret_cast(meshData + usedSize); + usedSize += sizeof(uint32_t); + if (dataSize < usedSize) + throw std::runtime_error("End of file"); + + if (!*nIndexes) + throw std::runtime_error("Empty index buffer found\n"); + + const uint64_t sizeInBytes = uint64_t(*(nIndexes)) * sizeof(uint16_t); + + if (sizeInBytes > UINT32_MAX) + throw std::runtime_error("IB too large"); + + if (!(flags & ModelLoader_AllowLargeModels)) + { + if (sizeInBytes > (D3D12_REQ_RESOURCE_SIZE_IN_MEGABYTES_EXPRESSION_A_TERM * 1024u * 1024u)) + throw std::runtime_error("IB too large for DirectX 12"); + } + + auto const ibBytes = static_cast(sizeInBytes); + + auto indexes = reinterpret_cast(meshData + usedSize); + usedSize += ibBytes; + if (dataSize < usedSize) + throw std::runtime_error("End of file"); + + IBData ib; + ib.nIndices = *nIndexes; + ib.ptr = indexes; + ibData.emplace_back(ib); + + ibs[j] = GraphicsMemory::Get(device).Allocate(ibBytes, 16, GraphicsMemory::TAG_INDEX); + memcpy(ibs[j].Memory(), indexes, ibBytes); + } + + assert(ibData.size() == *nIBs); + assert(ibs.size() == *nIBs); + + // Vertex buffers + auto nVBs = reinterpret_cast(meshData + usedSize); + usedSize += sizeof(uint32_t); + if (dataSize < usedSize) + throw std::runtime_error("End of file"); + + if (!*nVBs) + throw std::runtime_error("No vertex buffers found\n"); + + struct VBData + { + size_t nVerts; + const VertexPositionNormalTangentColorTexture* ptr; + const VSD3DStarter::SkinningVertex* skinPtr; + }; + + std::vector vbData; + vbData.reserve(*nVBs); + for (size_t j = 0; j < *nVBs; ++j) + { + auto nVerts = reinterpret_cast(meshData + usedSize); + usedSize += sizeof(uint32_t); + if (dataSize < usedSize) + throw std::runtime_error("End of file"); + + if (!*nVerts) + throw std::runtime_error("Empty vertex buffer found\n"); + + const size_t vbBytes = sizeof(VertexPositionNormalTangentColorTexture) * (*(nVerts)); + + auto verts = reinterpret_cast(meshData + usedSize); + usedSize += vbBytes; + if (dataSize < usedSize) + throw std::runtime_error("End of file"); + + VBData vb; + vb.nVerts = *nVerts; + vb.ptr = verts; + vb.skinPtr = nullptr; + vbData.emplace_back(vb); + } + + assert(vbData.size() == *nVBs); + + // Skinning vertex buffers + auto nSkinVBs = reinterpret_cast(meshData + usedSize); + usedSize += sizeof(uint32_t); + if (dataSize < usedSize) + throw std::runtime_error("End of file"); + + if (*nSkinVBs) + { + if (*nSkinVBs != *nVBs) + throw std::runtime_error("Number of VBs not equal to number of skin VBs"); + + for (size_t j = 0; j < *nSkinVBs; ++j) + { + auto nVerts = reinterpret_cast(meshData + usedSize); + usedSize += sizeof(uint32_t); + if (dataSize < usedSize) + throw std::runtime_error("End of file"); + + if (!*nVerts) + throw std::runtime_error("Empty skinning vertex buffer found\n"); + + if (vbData[j].nVerts != *nVerts) + throw std::runtime_error("Mismatched number of verts for skin VBs"); + + const size_t vbBytes = sizeof(VSD3DStarter::SkinningVertex) * (*(nVerts)); + + auto verts = reinterpret_cast(meshData + usedSize); + usedSize += vbBytes; + if (dataSize < usedSize) + throw std::runtime_error("End of file"); + + vbData[j].skinPtr = verts; + } + } + + // Extents + auto extents = reinterpret_cast(meshData + usedSize); + usedSize += sizeof(VSD3DStarter::MeshExtents); + if (dataSize < usedSize) + throw std::runtime_error("End of file"); + + mesh->boundingSphere.Center.x = extents->CenterX; + mesh->boundingSphere.Center.y = extents->CenterY; + mesh->boundingSphere.Center.z = extents->CenterZ; + mesh->boundingSphere.Radius = extents->Radius; + + const XMVECTOR min = XMVectorSet(extents->MinX, extents->MinY, extents->MinZ, 0.f); + const XMVECTOR max = XMVectorSet(extents->MaxX, extents->MaxY, extents->MaxZ, 0.f); + BoundingBox::CreateFromPoints(mesh->boundingBox, min, max); + + // Load model bones (if present and requested) + if (*bSkeleton && (flags & ModelLoader_IncludeBones)) + { + // Bones + auto nBones = reinterpret_cast(meshData + usedSize); + usedSize += sizeof(uint32_t); + if (dataSize < usedSize) + throw std::runtime_error("End of file"); + + if (!*nBones) + throw std::runtime_error("Animation bone data is missing\n"); + + ModelBone::Collection bones; + bones.resize(*nBones); + auto transforms = ModelBone::MakeArray(*nBones); + auto invTransforms = ModelBone::MakeArray(*nBones); + + for (uint32_t j = 0; j < *nBones; ++j) + { + // Bone name + nName = reinterpret_cast(meshData + usedSize); + usedSize += sizeof(uint32_t); + if (dataSize < usedSize) + throw std::runtime_error("End of file"); + + auto boneName = reinterpret_cast(static_cast(meshData + usedSize)); + + usedSize += sizeof(wchar_t) * (*nName); + if (dataSize < usedSize) + throw std::runtime_error("End of file"); + + bones[j].name = boneName; + + // Bone settings + auto cmobones = reinterpret_cast(meshData + usedSize); + usedSize += sizeof(VSD3DStarter::Bone); + if (dataSize < usedSize) + throw std::runtime_error("End of file"); + + transforms[j] = XMLoadFloat4x4(&cmobones->LocalTransform); + invTransforms[j] = XMLoadFloat4x4(&cmobones->InvBindPos); + + if (cmobones->ParentIndex < 0) + { + if (!j) + continue; + + // Add as a sibling of the root bone + uint32_t index = 0; + for (size_t visited = 0;; ++visited) + { + if (visited >= *nBones) + throw std::runtime_error("Skeleton bones form an invalid graph"); + + const uint32_t sibling = bones[index].siblingIndex; + if (sibling == ModelBone::c_Invalid) + { + bones[index].siblingIndex = j; + break; + } + + if (sibling >= *nBones) + throw std::runtime_error("Skeleton bones corrupt"); + + index = sibling; + } + } + else if (static_cast(cmobones->ParentIndex) >= *nBones) + { + throw std::runtime_error("Skeleton bones corrupt"); + } + else + { + if (!j) + throw std::runtime_error("First bone must be root!"); + + auto index = static_cast(cmobones->ParentIndex); + + bones[j].parentIndex = index; + + // Add as the only child of the parent + if (bones[index].childIndex == ModelBone::c_Invalid) + { + bones[index].childIndex = j; + } + else + { + // Otherwise add as a sibling of the parent's other children + index = bones[index].childIndex; + for (size_t visited = 0;; ++visited) + { + if (visited >= *nBones) + throw std::runtime_error("Skeleton bones form an invalid graph"); + + const uint32_t sibling = bones[index].siblingIndex; + if (sibling == ModelBone::c_Invalid) + { + bones[index].siblingIndex = j; + break; + } + + if (sibling >= *nBones) + throw std::runtime_error("Skeleton bones corrupt"); + + index = sibling; + } + } + } + } + + std::swap(model->bones, bones); + std::swap(model->boneMatrices, transforms); + std::swap(model->invBindPoseMatrices, invTransforms); + + // Animation Clips + if (animsOffset) + { + // Optional return for offset to start of animation clips in the CMO. + + size_t offset = usedSize; + + auto nClips = reinterpret_cast(meshData + usedSize); + usedSize += sizeof(uint32_t); + if (dataSize < usedSize) + throw std::runtime_error("End of file"); + + if (*nClips > 0) + { + *animsOffset = offset; + } + } + } + + const bool enableSkinning = (*nSkinVBs) != 0 && !(flags & ModelLoader_DisableSkinning); + + // Build vertex buffers + std::vector vbs; + vbs.resize(*nVBs); + + const size_t stride = enableSkinning ? sizeof(VertexPositionNormalTangentColorTextureSkinning) + : sizeof(VertexPositionNormalTangentColorTexture); + + for (size_t j = 0; j < *nVBs; ++j) + { + const size_t nVerts = vbData[j].nVerts; + + const uint64_t sizeInBytes = uint64_t(stride) * uint64_t(nVerts); + + if (sizeInBytes > UINT32_MAX) + throw std::runtime_error("VB too large"); + + if (!(flags & ModelLoader_AllowLargeModels)) + { + if (sizeInBytes > uint64_t(D3D12_REQ_RESOURCE_SIZE_IN_MEGABYTES_EXPRESSION_A_TERM * 1024u * 1024u)) + throw std::runtime_error("VB too large for DirectX 12"); + } + + const size_t bytes = static_cast(sizeInBytes); + + { + auto temp = std::make_unique(bytes + (sizeof(uint32_t) * nVerts)); + + auto visited = reinterpret_cast(temp.get() + bytes); + memset(visited, 0xff, sizeof(uint32_t) * nVerts); + + assert(vbData[j].ptr != nullptr); + + if (enableSkinning) + { + // Combine CMO multi-stream data into a single stream + auto skinptr = vbData[j].skinPtr; + assert(skinptr != nullptr); + + uint8_t* ptr = temp.get(); + + auto sptr = vbData[j].ptr; + + for (size_t v = 0; v < nVerts; ++v) + { + *reinterpret_cast(ptr) = sptr[v]; + + auto skinv = reinterpret_cast(ptr); + skinv->SetBlendIndices(*reinterpret_cast(skinptr[v].boneIndex)); + skinv->SetBlendWeights(*reinterpret_cast(skinptr[v].boneWeight)); + + ptr += stride; + } + } + else + { + memcpy(temp.get(), vbData[j].ptr, bytes); + } + + { + // Need to fix up VB tex coords for UV transform which is not supported by basic effects + for (size_t k = 0; k < *nSubmesh; ++k) + { + auto& sm = subMesh[k]; + + if (sm.VertexBufferIndex != j) + continue; + + if ((sm.IndexBufferIndex >= *nIBs) + || (sm.MaterialIndex >= materials.size())) + throw std::out_of_range("Invalid submesh found\n"); + + const XMMATRIX uvTransform = XMLoadFloat4x4(&materials[sm.MaterialIndex].pMaterial->UVTransform); + + auto ib = ibData[sm.IndexBufferIndex].ptr; + + const size_t count = ibData[sm.IndexBufferIndex].nIndices; + + for (size_t q = 0; q < count; ++q) + { + size_t v = ib[q]; + + if (v >= nVerts) + throw std::out_of_range("Invalid index found\n"); + + auto verts = reinterpret_cast(temp.get() + (v * stride)); + if (visited[v] == uint32_t(-1)) + { + visited[v] = sm.MaterialIndex; + + XMVECTOR t = XMLoadFloat2(&verts->textureCoordinate); + + t = XMVectorSelect(g_XMIdentityR3, t, g_XMSelect1110); + + t = XMVector4Transform(t, uvTransform); + + XMStoreFloat2(&verts->textureCoordinate, t); + } + else if (visited[v] != sm.MaterialIndex) + { + #ifdef _DEBUG + const XMMATRIX uv2 = XMLoadFloat4x4(&materials[visited[v]].pMaterial->UVTransform); + + if (XMVector4NotEqual(uvTransform.r[0], uv2.r[0]) + || XMVector4NotEqual(uvTransform.r[1], uv2.r[1]) + || XMVector4NotEqual(uvTransform.r[2], uv2.r[2]) + || XMVector4NotEqual(uvTransform.r[3], uv2.r[3])) + { + DebugTrace("WARNING: %ls - mismatched UV transforms for the same vertex; texture coordinates may not be correct\n", mesh->name.c_str()); + } + #endif + } + } + } + } + + vbs[j] = GraphicsMemory::Get(device).Allocate(bytes, 16, GraphicsMemory::TAG_VERTEX); + memcpy(vbs[j].Memory(), temp.get(), bytes); + } + } + + assert(vbs.size() == *nVBs); + + // Create model materials + const bool srgb = (flags & ModelLoader_MaterialColorsSRGB) != 0; + + for (size_t j = 0; j < materials.size(); ++j) + { + auto& m = materials[j]; + + ModelMaterialInfo info; + info.name = m.name.c_str(); + info.specularPower = m.pMaterial->SpecularPower; + info.perVertexColor = true; + info.enableSkinning = enableSkinning; + info.alphaValue = m.pMaterial->Diffuse.w; + info.ambientColor = GetMaterialColor(m.pMaterial->Ambient.x, m.pMaterial->Ambient.y, m.pMaterial->Ambient.z, srgb); + info.diffuseColor = GetMaterialColor(m.pMaterial->Diffuse.x, m.pMaterial->Diffuse.y, m.pMaterial->Diffuse.z, srgb); + info.specularColor = GetMaterialColor(m.pMaterial->Specular.x, m.pMaterial->Specular.y, m.pMaterial->Specular.z, srgb); + info.emissiveColor = GetMaterialColor(m.pMaterial->Emissive.x, m.pMaterial->Emissive.y, m.pMaterial->Emissive.z, srgb); + info.diffuseTextureIndex = GetUniqueTextureIndex(m.texture[0].c_str(), textureDictionary); + info.samplerIndex = (info.diffuseTextureIndex == -1) ? -1 : static_cast(CommonStates::SamplerIndex::AnisotropicWrap); + + modelmats.emplace_back(info); + } + + // Build mesh parts + for (size_t j = 0; j < *nSubmesh; ++j) + { + auto& sm = subMesh[j]; + + if ((sm.IndexBufferIndex >= *nIBs) + || (sm.VertexBufferIndex >= *nVBs) + || (sm.MaterialIndex >= materials.size())) + throw std::out_of_range("Invalid submesh found\n"); + + auto& mat = materials[sm.MaterialIndex]; + + auto part = new ModelMeshPart(partCount++); + + part->indexCount = sm.PrimCount * 3; + part->materialIndex = mat.materialIndex; + part->startIndex = sm.StartIndex; + part->vertexStride = static_cast(stride); + part->indexBuffer = ibs[sm.IndexBufferIndex]; + part->indexBufferSize = static_cast(ibs[sm.IndexBufferIndex].Size()); + part->vertexBuffer = vbs[sm.VertexBufferIndex]; + part->vertexBufferSize = static_cast(vbs[sm.VertexBufferIndex].Size()); + part->vbDecl = enableSkinning ? g_vbdeclSkinning : g_vbdecl; + + if (mat.pMaterial->Diffuse.w < 1) + { + mesh->alphaMeshParts.emplace_back(part); + } + else + { + mesh->opaqueMeshParts.emplace_back(part); + } + } + + model->meshes.emplace_back(mesh); + } + + // Copy the materials and texture names into contiguous arrays + model->materials = std::move(modelmats); + model->textureNames.resize(textureDictionary.size()); + for (auto texture = std::cbegin(textureDictionary); texture != std::cend(textureDictionary); ++texture) + { + model->textureNames[static_cast(texture->second)] = texture->first; + } + + return model; +} + + +//-------------------------------------------------------------------------------------- +_Use_decl_annotations_ +std::unique_ptr DirectX::Model::CreateFromCMO( + ID3D12Device* device, + const wchar_t* szFileName, + ModelLoaderFlags flags, + size_t* animsOffset) +{ + if (animsOffset) + { + *animsOffset = 0; + } + + size_t dataSize = 0; + std::unique_ptr data; + HRESULT hr = BinaryReader::ReadEntireFile(szFileName, data, &dataSize); + if (FAILED(hr)) + { + DebugTrace("ERROR: CreateFromCMO failed (%08X) loading '%ls'\n", + static_cast(hr), szFileName); + throw std::runtime_error("CreateFromCMO"); + } + + auto model = CreateFromCMO(device, data.get(), dataSize, flags, animsOffset); + + model->name = szFileName; + + return model; +} + + +//-------------------------------------------------------------------------------------- +// Adapters for /Zc:wchar_t- clients + +#if defined(_MSC_VER) && !defined(_NATIVE_WCHAR_T_DEFINED) + +_Use_decl_annotations_ +std::unique_ptr DirectX::Model::CreateFromCMO( + ID3D12Device* device, + const __wchar_t* szFileName, + ModelLoaderFlags flags, + size_t* animsOffset) +{ + return CreateFromCMO(device, reinterpret_cast(szFileName), flags, animsOffset); +} + +#endif diff --git a/Common/DirectXTK12/Src/ModelLoadSDKMESH.cpp b/Common/DirectXTK12/Src/ModelLoadSDKMESH.cpp new file mode 100644 index 0000000..f205df6 --- /dev/null +++ b/Common/DirectXTK12/Src/ModelLoadSDKMESH.cpp @@ -0,0 +1,812 @@ +//-------------------------------------------------------------------------------------- +// File: ModelLoadSDKMESH.cpp +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "pch.h" +#include "Model.h" + +#include "Effects.h" +#include "VertexTypes.h" + +#include "DirectXHelpers.h" +#include "PlatformHelpers.h" +#include "BinaryReader.h" +#include "DescriptorHeap.h" +#include "CommonStates.h" + +#include "SDKMesh.h" + +using namespace DirectX; +using Microsoft::WRL::ComPtr; + +namespace +{ + enum : unsigned int + { + PER_VERTEX_COLOR = 0x1, + SKINNING = 0x2, + DUAL_TEXTURE = 0x4, + NORMAL_MAPS = 0x8, + BIASED_VERTEX_NORMALS = 0x10, + USES_OBSOLETE_DEC3N = 0x20, + }; + + int GetUniqueTextureIndex(const wchar_t* textureName, std::map& textureDictionary) + { + if (textureName == nullptr || !textureName[0]) + return -1; + + auto i = textureDictionary.find(textureName); + if (i == std::cend(textureDictionary)) + { + int index = static_cast(textureDictionary.size()); + textureDictionary[textureName] = index; + return index; + } + else + { + return i->second; + } + } + + inline XMFLOAT3 GetMaterialColor(float r, float g, float b, bool srgb) noexcept + { + if (srgb) + { + XMVECTOR v = XMVectorSet(r, g, b, 1.f); + v = XMColorSRGBToRGB(v); + + XMFLOAT3 result; + XMStoreFloat3(&result, v); + return result; + } + else + { + return XMFLOAT3(r, g, b); + } + } + + template + inline void ASCIIToWChar(wchar_t(&buffer)[sizeOfBuffer], const char *ascii) + { + MultiByteToWideChar(CP_UTF8, 0, ascii, -1, buffer, sizeOfBuffer); + } + + void InitMaterial( + const DXUT::SDKMESH_MATERIAL& mh, + unsigned int flags, + _Out_ Model::ModelMaterialInfo& m, + _Inout_ std::map& textureDictionary, + bool srgb) + { + wchar_t matName[DXUT::MAX_MATERIAL_NAME] = {}; + ASCIIToWChar(matName, mh.Name); + + wchar_t diffuseName[DXUT::MAX_TEXTURE_NAME] = {}; + ASCIIToWChar(diffuseName, mh.DiffuseTexture); + + wchar_t specularName[DXUT::MAX_TEXTURE_NAME] = {}; + ASCIIToWChar(specularName, mh.SpecularTexture); + + wchar_t normalName[DXUT::MAX_TEXTURE_NAME] = {}; + ASCIIToWChar(normalName, mh.NormalTexture); + + if ((flags & DUAL_TEXTURE) && !mh.SpecularTexture[0]) + { + DebugTrace("WARNING: Material '%s' has multiple texture coords but not multiple textures\n", mh.Name); + flags &= ~static_cast(DUAL_TEXTURE); + } + + if (mh.NormalTexture[0]) + { + flags |= NORMAL_MAPS; + } + + m = {}; + m.name = matName; + m.perVertexColor = (flags & PER_VERTEX_COLOR) != 0; + m.enableSkinning = (flags & SKINNING) != 0; + m.enableDualTexture = (flags & DUAL_TEXTURE) != 0; + m.enableNormalMaps = (flags & NORMAL_MAPS) != 0; + m.biasedVertexNormals = (flags & BIASED_VERTEX_NORMALS) != 0; + + if (mh.Ambient.x == 0 && mh.Ambient.y == 0 && mh.Ambient.z == 0 && mh.Ambient.w == 0 + && mh.Diffuse.x == 0 && mh.Diffuse.y == 0 && mh.Diffuse.z == 0 && mh.Diffuse.w == 0) + { + // SDKMESH material color block is uninitalized; assume defaults + m.diffuseColor = XMFLOAT3(1.f, 1.f, 1.f); + m.alphaValue = 1.f; + } + else + { + m.ambientColor = GetMaterialColor(mh.Ambient.x, mh.Ambient.y, mh.Ambient.z, srgb); + m.diffuseColor = GetMaterialColor(mh.Diffuse.x, mh.Diffuse.y, mh.Diffuse.z, srgb); + m.emissiveColor = GetMaterialColor(mh.Emissive.x, mh.Emissive.y, mh.Emissive.z, srgb); + + if (mh.Diffuse.w != 1.f && mh.Diffuse.w != 0.f) + { + m.alphaValue = mh.Diffuse.w; + } + else + m.alphaValue = 1.f; + + if (mh.Power > 0) + { + m.specularPower = mh.Power; + m.specularColor = XMFLOAT3(mh.Specular.x, mh.Specular.y, mh.Specular.z); + } + } + + m.diffuseTextureIndex = GetUniqueTextureIndex(diffuseName, textureDictionary); + m.specularTextureIndex = GetUniqueTextureIndex(specularName, textureDictionary); + m.normalTextureIndex = GetUniqueTextureIndex(normalName, textureDictionary); + + m.samplerIndex = (m.diffuseTextureIndex == -1) ? -1 : static_cast(CommonStates::SamplerIndex::AnisotropicWrap); + m.samplerIndex2 = (flags & DUAL_TEXTURE) ? static_cast(CommonStates::SamplerIndex::AnisotropicWrap) : -1; + } + + void InitMaterial( + const DXUT::SDKMESH_MATERIAL_V2& mh, + unsigned int flags, + _Out_ Model::ModelMaterialInfo& m, + _Inout_ std::map& textureDictionary) + { + wchar_t matName[DXUT::MAX_MATERIAL_NAME] = {}; + ASCIIToWChar(matName, mh.Name); + + wchar_t albedoTexture[DXUT::MAX_TEXTURE_NAME] = {}; + ASCIIToWChar(albedoTexture, mh.AlbedoTexture); + + wchar_t normalName[DXUT::MAX_TEXTURE_NAME] = {}; + ASCIIToWChar(normalName, mh.NormalTexture); + + wchar_t rmaName[DXUT::MAX_TEXTURE_NAME] = {}; + ASCIIToWChar(rmaName, mh.RMATexture); + + wchar_t emissiveName[DXUT::MAX_TEXTURE_NAME] = {}; + ASCIIToWChar(emissiveName, mh.EmissiveTexture); + + m = {}; + m.name = matName; + m.perVertexColor = false; + m.enableSkinning = (flags & SKINNING) != 0; + m.enableDualTexture = false; + m.enableNormalMaps = true; + m.biasedVertexNormals = (flags & BIASED_VERTEX_NORMALS) != 0; + m.alphaValue = (mh.Alpha == 0.f) ? 1.f : mh.Alpha; + + m.diffuseTextureIndex = GetUniqueTextureIndex(albedoTexture, textureDictionary); + m.specularTextureIndex = GetUniqueTextureIndex(rmaName, textureDictionary); + m.normalTextureIndex = GetUniqueTextureIndex(normalName, textureDictionary); + m.emissiveTextureIndex = GetUniqueTextureIndex(emissiveName, textureDictionary); + + m.samplerIndex = m.samplerIndex2 = static_cast(CommonStates::SamplerIndex::AnisotropicWrap); + } + + + //-------------------------------------------------------------------------------------- + // Direct3D 9 Vertex Declaration to Direct3D 12 Input Layout mapping + + static_assert(D3D12_IA_VERTEX_INPUT_STRUCTURE_ELEMENT_COUNT >= 32, "SDKMESH supports decls up to 32 entries"); + + unsigned int GetInputLayoutDesc( + _In_reads_(32) const DXUT::D3DVERTEXELEMENT9 decl[], + ModelMeshPart::InputLayoutCollection& inputDesc) + { + static const D3D12_INPUT_ELEMENT_DESC s_elements[] = + { + { "SV_Position", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + { "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + { "COLOR", 0, DXGI_FORMAT_B8G8R8A8_UNORM, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + { "TANGENT", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + { "BINORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + { "BLENDINDICES", 0, DXGI_FORMAT_R8G8B8A8_UINT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + { "BLENDWEIGHT", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + }; + + using namespace DXUT; + + uint32_t offset = 0; + uint32_t texcoords = 0; + unsigned int flags = 0; + + bool posfound = false; + + for (size_t index = 0; index < DXUT::MAX_VERTEX_ELEMENTS; ++index) + { + if (decl[index].Usage == 0xFF) + break; + + if (decl[index].Type == D3DDECLTYPE_UNUSED) + break; + + if (decl[index].Offset != offset) + break; + + if (decl[index].Usage == D3DDECLUSAGE_POSITION) + { + if (decl[index].Type == D3DDECLTYPE_FLOAT3) + { + inputDesc.push_back(s_elements[0]); + offset += 12; + posfound = true; + } + else + break; + } + else if (decl[index].Usage == D3DDECLUSAGE_NORMAL + || decl[index].Usage == D3DDECLUSAGE_TANGENT + || decl[index].Usage == D3DDECLUSAGE_BINORMAL) + { + size_t base = 1; + if (decl[index].Usage == D3DDECLUSAGE_TANGENT) + base = 3; + else if (decl[index].Usage == D3DDECLUSAGE_BINORMAL) + base = 4; + + D3D12_INPUT_ELEMENT_DESC desc = s_elements[base]; + + bool unk = false; + switch (decl[index].Type) + { + case D3DDECLTYPE_FLOAT3: assert(desc.Format == DXGI_FORMAT_R32G32B32_FLOAT); offset += 12; break; + case D3DDECLTYPE_UBYTE4N: desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; flags |= BIASED_VERTEX_NORMALS; offset += 4; break; + case D3DDECLTYPE_SHORT4N: desc.Format = DXGI_FORMAT_R16G16B16A16_SNORM; offset += 8; break; + case D3DDECLTYPE_FLOAT16_4: desc.Format = DXGI_FORMAT_R16G16B16A16_FLOAT; offset += 8; break; + case D3DDECLTYPE_DXGI_R10G10B10A2_UNORM: desc.Format = DXGI_FORMAT_R10G10B10A2_UNORM; flags |= BIASED_VERTEX_NORMALS; offset += 4; break; + case D3DDECLTYPE_DXGI_R11G11B10_FLOAT: desc.Format = DXGI_FORMAT_R11G11B10_FLOAT; flags |= BIASED_VERTEX_NORMALS; offset += 4; break; + case D3DDECLTYPE_DXGI_R8G8B8A8_SNORM: desc.Format = DXGI_FORMAT_R8G8B8A8_SNORM; offset += 4; break; + + #if (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) + case D3DDECLTYPE_DEC3N: desc.Format = DXGI_FORMAT_R10G10B10_SNORM_A2_UNORM; offset += 4; break; + case (32 + DXGI_FORMAT_R10G10B10_SNORM_A2_UNORM): desc.Format = DXGI_FORMAT_R10G10B10_SNORM_A2_UNORM; offset += 4; break; + #else + case D3DDECLTYPE_DEC3N: desc.Format = DXGI_FORMAT_R10G10B10A2_UNORM; flags |= USES_OBSOLETE_DEC3N; offset += 4; break; + #endif + + default: + unk = true; + break; + } + + if (unk) + break; + + inputDesc.push_back(desc); + } + else if (decl[index].Usage == D3DDECLUSAGE_COLOR) + { + D3D12_INPUT_ELEMENT_DESC desc = s_elements[2]; + + bool unk = false; + switch (decl[index].Type) + { + case D3DDECLTYPE_FLOAT4: desc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT; offset += 16; break; + case D3DDECLTYPE_D3DCOLOR: assert(desc.Format == DXGI_FORMAT_B8G8R8A8_UNORM); offset += 4; break; + case D3DDECLTYPE_UBYTE4N: desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; offset += 4; break; + case D3DDECLTYPE_FLOAT16_4: desc.Format = DXGI_FORMAT_R16G16B16A16_FLOAT; offset += 8; break; + case D3DDECLTYPE_DXGI_R10G10B10A2_UNORM: desc.Format = DXGI_FORMAT_R10G10B10A2_UNORM; offset += 4; break; + case D3DDECLTYPE_DXGI_R11G11B10_FLOAT: desc.Format = DXGI_FORMAT_R11G11B10_FLOAT; offset += 4; break; + + default: + unk = true; + break; + } + + if (unk) + break; + + flags |= PER_VERTEX_COLOR; + + inputDesc.push_back(desc); + } + else if (decl[index].Usage == D3DDECLUSAGE_TEXCOORD) + { + D3D12_INPUT_ELEMENT_DESC desc = s_elements[5]; + desc.SemanticIndex = decl[index].UsageIndex; + + bool unk = false; + switch (decl[index].Type) + { + case D3DDECLTYPE_FLOAT1: desc.Format = DXGI_FORMAT_R32_FLOAT; offset += 4; break; + case D3DDECLTYPE_FLOAT2: assert(desc.Format == DXGI_FORMAT_R32G32_FLOAT); offset += 8; break; + case D3DDECLTYPE_FLOAT3: desc.Format = DXGI_FORMAT_R32G32B32_FLOAT; offset += 12; break; + case D3DDECLTYPE_FLOAT4: desc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT; offset += 16; break; + case D3DDECLTYPE_FLOAT16_2: desc.Format = DXGI_FORMAT_R16G16_FLOAT; offset += 4; break; + case D3DDECLTYPE_FLOAT16_4: desc.Format = DXGI_FORMAT_R16G16B16A16_FLOAT; offset += 8; break; + + default: + unk = true; + break; + } + + if (unk) + break; + + ++texcoords; + + inputDesc.push_back(desc); + } + else if (decl[index].Usage == D3DDECLUSAGE_BLENDINDICES) + { + if (decl[index].Type == D3DDECLTYPE_UBYTE4) + { + flags |= SKINNING; + inputDesc.push_back(s_elements[6]); + offset += 4; + } + else + break; + } + else if (decl[index].Usage == D3DDECLUSAGE_BLENDWEIGHT) + { + if (decl[index].Type == D3DDECLTYPE_UBYTE4N) + { + flags |= SKINNING; + inputDesc.push_back(s_elements[7]); + offset += 4; + } + else + break; + } + else + break; + } + + if (!posfound) + throw std::runtime_error("SV_Position is required"); + + if (texcoords == 2) + { + flags |= DUAL_TEXTURE; + } + + return flags; + } +} + +//====================================================================================== +// Model Loader +//====================================================================================== + +_Use_decl_annotations_ +std::unique_ptr DirectX::Model::CreateFromSDKMESH( + ID3D12Device* device, + const uint8_t* meshData, + size_t idataSize, + ModelLoaderFlags flags) +{ + if (!meshData) + throw std::invalid_argument("meshData cannot be null"); + + const uint64_t dataSize = idataSize; + + // File Headers + if (dataSize < sizeof(DXUT::SDKMESH_HEADER)) + throw std::runtime_error("End of file"); + auto header = reinterpret_cast(meshData); + + const size_t headerSize = sizeof(DXUT::SDKMESH_HEADER) + + header->NumVertexBuffers * sizeof(DXUT::SDKMESH_VERTEX_BUFFER_HEADER) + + header->NumIndexBuffers * sizeof(DXUT::SDKMESH_INDEX_BUFFER_HEADER); + if (header->HeaderSize != headerSize) + throw std::runtime_error("Not a valid SDKMESH file"); + + if (dataSize < header->HeaderSize) + throw std::runtime_error("End of file"); + + if (header->Version != DXUT::SDKMESH_FILE_VERSION && header->Version != DXUT::SDKMESH_FILE_VERSION_V2) + throw std::runtime_error("Not a supported SDKMESH version"); + + if (header->IsBigEndian) + throw std::runtime_error("Loading BigEndian SDKMESH files not supported"); + + if (!header->NumMeshes) + throw std::runtime_error("No meshes found"); + + if (!header->NumVertexBuffers) + throw std::runtime_error("No vertex buffers found"); + + if (!header->NumIndexBuffers) + throw std::runtime_error("No index buffers found"); + + if (!header->NumTotalSubsets) + throw std::runtime_error("No subsets found"); + + if (!header->NumMaterials) + throw std::runtime_error("No materials found"); + + // Sub-headers + if (dataSize < header->VertexStreamHeadersOffset + || (dataSize < (header->VertexStreamHeadersOffset + uint64_t(header->NumVertexBuffers) * sizeof(DXUT::SDKMESH_VERTEX_BUFFER_HEADER)))) + throw std::runtime_error("End of file"); + auto vbArray = reinterpret_cast(meshData + header->VertexStreamHeadersOffset); + + if (dataSize < header->IndexStreamHeadersOffset + || (dataSize < (header->IndexStreamHeadersOffset + uint64_t(header->NumIndexBuffers) * sizeof(DXUT::SDKMESH_INDEX_BUFFER_HEADER)))) + throw std::runtime_error("End of file"); + auto ibArray = reinterpret_cast(meshData + header->IndexStreamHeadersOffset); + + if (dataSize < header->MeshDataOffset + || (dataSize < (header->MeshDataOffset + uint64_t(header->NumMeshes) * sizeof(DXUT::SDKMESH_MESH)))) + throw std::runtime_error("End of file"); + auto meshArray = reinterpret_cast(meshData + header->MeshDataOffset); + + if (dataSize < header->SubsetDataOffset + || (dataSize < (header->SubsetDataOffset + uint64_t(header->NumTotalSubsets) * sizeof(DXUT::SDKMESH_SUBSET)))) + throw std::runtime_error("End of file"); + auto subsetArray = reinterpret_cast(meshData + header->SubsetDataOffset); + + const DXUT::SDKMESH_FRAME* frameArray = nullptr; + if (header->NumFrames > 0) + { + if (dataSize < header->FrameDataOffset + || (dataSize < (header->FrameDataOffset + uint64_t(header->NumFrames) * sizeof(DXUT::SDKMESH_FRAME)))) + throw std::runtime_error("End of file"); + + if (flags & ModelLoader_IncludeBones) + { + frameArray = reinterpret_cast(meshData + header->FrameDataOffset); + } + } + + if (dataSize < header->MaterialDataOffset + || (dataSize < (header->MaterialDataOffset + uint64_t(header->NumMaterials) * sizeof(DXUT::SDKMESH_MATERIAL)))) + throw std::runtime_error("End of file"); + + const DXUT::SDKMESH_MATERIAL* materialArray = nullptr; + const DXUT::SDKMESH_MATERIAL_V2* materialArray_v2 = nullptr; + if (header->Version == DXUT::SDKMESH_FILE_VERSION_V2) + { + materialArray_v2 = reinterpret_cast(meshData + header->MaterialDataOffset); + } + else + { + materialArray = reinterpret_cast(meshData + header->MaterialDataOffset); + } + + // Buffer data + const uint64_t bufferDataOffset = header->HeaderSize + header->NonBufferDataSize; + if ((dataSize < bufferDataOffset) + || (dataSize < bufferDataOffset + header->BufferDataSize)) + throw std::runtime_error("End of file"); + const uint8_t* bufferData = meshData + bufferDataOffset; + + // Create vertex buffers + std::vector> vbDecls; + vbDecls.resize(header->NumVertexBuffers); + + std::vector materialFlags; + materialFlags.resize(header->NumVertexBuffers); + + bool dec3nwarning = false; + for (size_t j = 0; j < header->NumVertexBuffers; ++j) + { + auto& vh = vbArray[j]; + + if (vh.SizeBytes > UINT32_MAX) + throw std::runtime_error("VB too large"); + + if (!(flags & ModelLoader_AllowLargeModels)) + { + if (vh.SizeBytes > (D3D12_REQ_RESOURCE_SIZE_IN_MEGABYTES_EXPRESSION_A_TERM * 1024u * 1024u)) + throw std::runtime_error("VB too large for DirectX 12"); + } + + if (dataSize < vh.DataOffset + || (dataSize < vh.DataOffset + vh.SizeBytes)) + throw std::runtime_error("End of file"); + + vbDecls[j] = std::make_shared(); + unsigned int ilflags = GetInputLayoutDesc(vh.Decl, *vbDecls[j].get()); + + if (flags & ModelLoader_DisableSkinning) + { + ilflags &= ~static_cast(SKINNING); + } + + if (ilflags & SKINNING) + { + ilflags &= ~static_cast(DUAL_TEXTURE); + } + if (ilflags & USES_OBSOLETE_DEC3N) + { + dec3nwarning = true; + } + + materialFlags[j] = ilflags; + } + + if (dec3nwarning) + { + DebugTrace("WARNING: Vertex declaration uses legacy Direct3D 9 D3DDECLTYPE_DEC3N which has no DXGI equivalent\n" + " (treating as DXGI_FORMAT_R10G10B10A2_UNORM which is not a signed format)\n"); + } + + // Validate index buffers + for (size_t j = 0; j < header->NumIndexBuffers; ++j) + { + auto& ih = ibArray[j]; + + if (ih.SizeBytes > UINT32_MAX) + throw std::runtime_error("IB too large"); + + if (!(flags & ModelLoader_AllowLargeModels)) + { + if (ih.SizeBytes > (D3D12_REQ_RESOURCE_SIZE_IN_MEGABYTES_EXPRESSION_A_TERM * 1024u * 1024u)) + throw std::runtime_error("IB too large for DirectX 12"); + } + + if (dataSize < ih.DataOffset + || (dataSize < ih.DataOffset + ih.SizeBytes)) + throw std::runtime_error("End of file"); + + if (ih.IndexType != DXUT::IT_16BIT && ih.IndexType != DXUT::IT_32BIT) + throw std::runtime_error("Invalid index buffer type found"); + } + + // Create meshes + std::vector materials; + materials.resize(header->NumMaterials); + + std::map textureDictionary; + + auto model = std::make_unique(); + model->meshes.reserve(header->NumMeshes); + + uint32_t partCount = 0; + + for (size_t meshIndex = 0; meshIndex < header->NumMeshes; ++meshIndex) + { + auto& mh = meshArray[meshIndex]; + + if (!mh.NumSubsets + || !mh.NumVertexBuffers + || mh.IndexBuffer >= header->NumIndexBuffers + || mh.VertexBuffers[0] >= header->NumVertexBuffers) + throw std::out_of_range("Invalid mesh found"); + + // mh.NumVertexBuffers is sometimes not what you'd expect, so we skip validating it + + if (dataSize < mh.SubsetOffset + || (dataSize < mh.SubsetOffset + uint64_t(mh.NumSubsets) * sizeof(uint32_t))) + throw std::runtime_error("End of file"); + + auto subsets = reinterpret_cast(meshData + mh.SubsetOffset); + + const uint32_t* influences = nullptr; + if (mh.NumFrameInfluences > 0) + { + if (dataSize < mh.FrameInfluenceOffset + || (dataSize < mh.FrameInfluenceOffset + uint64_t(mh.NumFrameInfluences) * sizeof(uint32_t))) + throw std::runtime_error("End of file"); + + if (flags & ModelLoader_IncludeBones) + { + influences = reinterpret_cast(meshData + mh.FrameInfluenceOffset); + } + } + + auto mesh = std::make_shared(); + wchar_t meshName[DXUT::MAX_MESH_NAME] = {}; + ASCIIToWChar(meshName, mh.Name); + + mesh->name = meshName; + + // Extents + mesh->boundingBox.Center = mh.BoundingBoxCenter; + mesh->boundingBox.Extents = mh.BoundingBoxExtents; + BoundingSphere::CreateFromBoundingBox(mesh->boundingSphere, mesh->boundingBox); + + if (influences) + { + mesh->boneInfluences.resize(mh.NumFrameInfluences); + memcpy(mesh->boneInfluences.data(), influences, sizeof(uint32_t) * mh.NumFrameInfluences); + } + + // Create subsets + for (size_t j = 0; j < mh.NumSubsets; ++j) + { + auto const sIndex = subsets[j]; + if (sIndex >= header->NumTotalSubsets) + throw std::out_of_range("Invalid mesh found"); + + auto& subset = subsetArray[sIndex]; + + D3D_PRIMITIVE_TOPOLOGY primType; + switch (subset.PrimitiveType) + { + case DXUT::PT_TRIANGLE_LIST: primType = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST; break; + case DXUT::PT_TRIANGLE_STRIP: primType = D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP; break; + case DXUT::PT_LINE_LIST: primType = D3D_PRIMITIVE_TOPOLOGY_LINELIST; break; + case DXUT::PT_LINE_STRIP: primType = D3D_PRIMITIVE_TOPOLOGY_LINESTRIP; break; + case DXUT::PT_POINT_LIST: primType = D3D_PRIMITIVE_TOPOLOGY_POINTLIST; break; + case DXUT::PT_TRIANGLE_LIST_ADJ: primType = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST_ADJ; break; + case DXUT::PT_TRIANGLE_STRIP_ADJ: primType = D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP_ADJ; break; + case DXUT::PT_LINE_LIST_ADJ: primType = D3D_PRIMITIVE_TOPOLOGY_LINELIST_ADJ; break; + case DXUT::PT_LINE_STRIP_ADJ: primType = D3D_PRIMITIVE_TOPOLOGY_LINESTRIP_ADJ; break; + + case DXUT::PT_QUAD_PATCH_LIST: + case DXUT::PT_TRIANGLE_PATCH_LIST: + throw std::runtime_error("Direct3D9 era tessellation not supported"); + + default: + throw std::runtime_error("Unknown primitive type"); + } + + if (subset.MaterialID >= header->NumMaterials) + throw std::out_of_range("Invalid mesh found"); + + auto& mat = materials[subset.MaterialID]; + + const size_t vi = mh.VertexBuffers[0]; + if (materialArray_v2) + { + InitMaterial( + materialArray_v2[subset.MaterialID], + materialFlags[vi], + mat, + textureDictionary); + } + else + { + InitMaterial( + materialArray[subset.MaterialID], + materialFlags[vi], + mat, + textureDictionary, + (flags & ModelLoader_MaterialColorsSRGB) != 0); + } + + auto part = new ModelMeshPart(partCount++); + + const auto& vh = vbArray[mh.VertexBuffers[0]]; + const auto& ih = ibArray[mh.IndexBuffer]; + + part->indexCount = static_cast(subset.IndexCount); + part->startIndex = static_cast(subset.IndexStart); + part->vertexOffset = static_cast(subset.VertexStart); + part->vertexStride = static_cast(vh.StrideBytes); + part->vertexCount = static_cast(subset.VertexCount); + part->primitiveType = primType; + part->indexFormat = (ibArray[mh.IndexBuffer].IndexType == DXUT::IT_32BIT) ? DXGI_FORMAT_R32_UINT : DXGI_FORMAT_R16_UINT; + + // Vertex data + auto verts = bufferData + (vh.DataOffset - bufferDataOffset); + auto const vbytes = static_cast(vh.SizeBytes); + part->vertexBufferSize = static_cast(vh.SizeBytes); + part->vertexBuffer = GraphicsMemory::Get(device).Allocate(vbytes, 16, GraphicsMemory::TAG_VERTEX); + memcpy(part->vertexBuffer.Memory(), verts, vbytes); + + // Index data + auto indices = bufferData + (ih.DataOffset - bufferDataOffset); + auto const ibytes = static_cast(ih.SizeBytes); + part->indexBufferSize = static_cast(ih.SizeBytes); + part->indexBuffer = GraphicsMemory::Get(device).Allocate(ibytes, 16, GraphicsMemory::TAG_INDEX); + memcpy(part->indexBuffer.Memory(), indices, ibytes); + + part->materialIndex = subset.MaterialID; + part->vbDecl = vbDecls[mh.VertexBuffers[0]]; + + if (mat.alphaValue < 1.0f) + mesh->alphaMeshParts.emplace_back(part); + else + mesh->opaqueMeshParts.emplace_back(part); + } + + model->meshes.emplace_back(mesh); + } + + // Copy the materials and texture names into contiguous arrays + model->materials = std::move(materials); + model->textureNames.resize(textureDictionary.size()); + for (auto texture = std::cbegin(textureDictionary); texture != std::cend(textureDictionary); ++texture) + { + model->textureNames[static_cast(texture->second)] = texture->first; + } + + // Load model bones (if present and requested) + if (frameArray) + { + static_assert(DXUT::INVALID_FRAME == ModelBone::c_Invalid, "ModelBone invalid type mismatch"); + + ModelBone::Collection bones; + bones.reserve(header->NumFrames); + auto transforms = ModelBone::MakeArray(header->NumFrames); + + for (uint32_t j = 0; j < header->NumFrames; ++j) + { + ModelBone bone( + frameArray[j].ParentFrame, + frameArray[j].ChildFrame, + frameArray[j].SiblingFrame); + + wchar_t boneName[DXUT::MAX_FRAME_NAME] = {}; + ASCIIToWChar(boneName, frameArray[j].Name); + bone.name = boneName; + bones.emplace_back(bone); + + transforms[j] = XMLoadFloat4x4(&frameArray[j].Matrix); + + const uint32_t index = frameArray[j].Mesh; + if (index != DXUT::INVALID_MESH) + { + if (index >= model->meshes.size()) + { + throw std::out_of_range("Invalid mesh index found in frame data"); + } + + if (model->meshes[index]->boneIndex == ModelBone::c_Invalid) + { + // Bind the first bone that links to a given mesh + model->meshes[index]->boneIndex = j; + } + } + } + + std::swap(model->bones, bones); + + // Compute inverse bind pose matrices for the model + auto bindPose = ModelBone::MakeArray(header->NumFrames); + model->CopyAbsoluteBoneTransforms(header->NumFrames, transforms.get(), bindPose.get()); + + auto invBoneTransforms = ModelBone::MakeArray(header->NumFrames); + for (size_t j = 0; j < header->NumFrames; ++j) + { + invBoneTransforms[j] = XMMatrixInverse(nullptr, bindPose[j]); + } + + std::swap(model->boneMatrices, transforms); + std::swap(model->invBindPoseMatrices, invBoneTransforms); + } + + return model; +} + + +//-------------------------------------------------------------------------------------- +_Use_decl_annotations_ +std::unique_ptr DirectX::Model::CreateFromSDKMESH( + ID3D12Device* device, + const wchar_t* szFileName, + ModelLoaderFlags flags) +{ + size_t dataSize = 0; + std::unique_ptr data; + HRESULT hr = BinaryReader::ReadEntireFile(szFileName, data, &dataSize); + if (FAILED(hr)) + { + DebugTrace("ERROR: CreateFromSDKMESH failed (%08X) loading '%ls'\n", + static_cast(hr), szFileName); + throw std::runtime_error("CreateFromSDKMESH"); + } + + auto model = CreateFromSDKMESH(device, data.get(), dataSize, flags); + + model->name = szFileName; + + return model; +} + + +//-------------------------------------------------------------------------------------- +// Adapters for /Zc:wchar_t- clients + +#if defined(_MSC_VER) && !defined(_NATIVE_WCHAR_T_DEFINED) + +_Use_decl_annotations_ +std::unique_ptr DirectX::Model::CreateFromSDKMESH( + ID3D12Device* device, + const __wchar_t* szFileName, + ModelLoaderFlags flags) +{ + return CreateFromSDKMESH(device, reinterpret_cast(szFileName), flags); +} + +#endif diff --git a/Common/DirectXTK12/Src/ModelLoadVBO.cpp b/Common/DirectXTK12/Src/ModelLoadVBO.cpp new file mode 100644 index 0000000..af0b775 --- /dev/null +++ b/Common/DirectXTK12/Src/ModelLoadVBO.cpp @@ -0,0 +1,172 @@ +//-------------------------------------------------------------------------------------- +// File: ModelLoadVBO.cpp +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "pch.h" +#include "Model.h" + +#include "Effects.h" +#include "VertexTypes.h" + +#include "DirectXHelpers.h" +#include "PlatformHelpers.h" +#include "BinaryReader.h" + +#include "vbo.h" + +using namespace DirectX; +using Microsoft::WRL::ComPtr; + +static_assert(sizeof(VertexPositionNormalTexture) == 32, "VBO vertex size mismatch"); + +namespace +{ + //-------------------------------------------------------------------------------------- + // Shared VB input element description + INIT_ONCE g_InitOnce = INIT_ONCE_STATIC_INIT; + std::shared_ptr g_vbdecl; + + BOOL CALLBACK InitializeDecl(PINIT_ONCE initOnce, PVOID Parameter, PVOID *lpContext) + { + UNREFERENCED_PARAMETER(initOnce); + UNREFERENCED_PARAMETER(Parameter); + UNREFERENCED_PARAMETER(lpContext); + + g_vbdecl = std::make_shared(VertexPositionNormalTexture::InputLayout.pInputElementDescs, + VertexPositionNormalTexture::InputLayout.pInputElementDescs + VertexPositionNormalTexture::InputLayout.NumElements); + + return TRUE; + } +} + + +//-------------------------------------------------------------------------------------- +_Use_decl_annotations_ +std::unique_ptr DirectX::Model::CreateFromVBO( + ID3D12Device* device, + const uint8_t* meshData, size_t dataSize, + ModelLoaderFlags flags) +{ + if (!InitOnceExecuteOnce(&g_InitOnce, InitializeDecl, nullptr, nullptr)) + throw std::system_error(std::error_code(static_cast(GetLastError()), std::system_category()), "InitOnceExecuteOnce"); + + if (!meshData) + throw std::invalid_argument("meshData cannot be null"); + + // File Header + if (dataSize < sizeof(VBO::header_t)) + throw std::runtime_error("End of file"); + auto header = reinterpret_cast(meshData); + + if (!header->numVertices || !header->numIndices) + throw std::runtime_error("No vertices or indices found"); + + uint64_t sizeInBytes = uint64_t(header->numVertices) * sizeof(VertexPositionNormalTexture); + if (sizeInBytes > UINT32_MAX) + throw std::runtime_error("VB too large"); + + if (!(flags & ModelLoader_AllowLargeModels)) + { + if (sizeInBytes > uint64_t(D3D12_REQ_RESOURCE_SIZE_IN_MEGABYTES_EXPRESSION_A_TERM * 1024u * 1024u)) + throw std::runtime_error("VB too large for DirectX 12"); + } + + auto const vertSize = static_cast(sizeInBytes); + + if (dataSize < (vertSize + sizeof(VBO::header_t))) + throw std::runtime_error("End of file"); + auto verts = reinterpret_cast(meshData + sizeof(VBO::header_t)); + + sizeInBytes = uint64_t(header->numIndices) * sizeof(uint16_t); + if (sizeInBytes > UINT32_MAX) + throw std::runtime_error("IB too large"); + + if (!(flags & ModelLoader_AllowLargeModels)) + { + if (sizeInBytes > uint64_t(D3D12_REQ_RESOURCE_SIZE_IN_MEGABYTES_EXPRESSION_A_TERM * 1024u * 1024u)) + throw std::runtime_error("IB too large for DirectX 12"); + } + + auto const indexSize = static_cast(sizeInBytes); + + if (dataSize < (sizeof(VBO::header_t) + vertSize + indexSize)) + throw std::runtime_error("End of file"); + auto indices = reinterpret_cast(meshData + sizeof(VBO::header_t) + vertSize); + + // Create vertex buffer + auto vb = GraphicsMemory::Get(device).Allocate(vertSize, 16, GraphicsMemory::TAG_VERTEX); + memcpy(vb.Memory(), verts, vertSize); + + // Create index buffer + auto ib = GraphicsMemory::Get(device).Allocate(indexSize, 16, GraphicsMemory::TAG_INDEX); + memcpy(ib.Memory(), indices, indexSize); + + auto part = new ModelMeshPart(0); + part->materialIndex = 0; + part->indexCount = header->numIndices; + part->startIndex = 0; + part->vertexStride = static_cast(sizeof(VertexPositionNormalTexture)); + part->vertexCount = header->numVertices; + part->indexBufferSize = static_cast(indexSize); + part->vertexBufferSize = static_cast(vertSize); + part->indexBuffer = std::move(ib); + part->vertexBuffer = std::move(vb); + part->vbDecl = g_vbdecl; + + auto mesh = std::make_shared(); + BoundingSphere::CreateFromPoints(mesh->boundingSphere, header->numVertices, &verts->position, sizeof(VertexPositionNormalTexture)); + BoundingBox::CreateFromPoints(mesh->boundingBox, header->numVertices, &verts->position, sizeof(VertexPositionNormalTexture)); + mesh->opaqueMeshParts.emplace_back(part); + + auto model = std::make_unique(); + model->meshes.emplace_back(mesh); + + return model; +} + + +//-------------------------------------------------------------------------------------- +_Use_decl_annotations_ +std::unique_ptr DirectX::Model::CreateFromVBO( + ID3D12Device* device, + const wchar_t* szFileName, + ModelLoaderFlags flags) +{ + size_t dataSize = 0; + std::unique_ptr data; + HRESULT hr = BinaryReader::ReadEntireFile(szFileName, data, &dataSize); + if (FAILED(hr)) + { + DebugTrace("ERROR: CreateFromVBO failed (%08X) loading '%ls'\n", + static_cast(hr), szFileName); + throw std::runtime_error("CreateFromVBO"); + } + + auto model = CreateFromVBO(device, data.get(), dataSize, flags); + + model->name = szFileName; + + return model; +} + + +//-------------------------------------------------------------------------------------- +// Adapters for /Zc:wchar_t- clients + +#if defined(_MSC_VER) && !defined(_NATIVE_WCHAR_T_DEFINED) + +_Use_decl_annotations_ +std::unique_ptr DirectX::Model::CreateFromVBO( + ID3D12Device* device, + const __wchar_t* szFileName, + ModelLoaderFlags flags) +{ + return CreateFromVBO(device, reinterpret_cast(szFileName), flags); +} + +#endif diff --git a/Common/DirectXTK12/Src/Mouse.cpp b/Common/DirectXTK12/Src/Mouse.cpp new file mode 100644 index 0000000..8f57e76 --- /dev/null +++ b/Common/DirectXTK12/Src/Mouse.cpp @@ -0,0 +1,1648 @@ +//-------------------------------------------------------------------------------------- +// File: Mouse.cpp +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248929 +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "pch.h" +#include "Mouse.h" + +#include "PlatformHelpers.h" + +using namespace DirectX; +using Microsoft::WRL::ComPtr; + +#pragma region Implementations +#ifdef USING_GAMEINPUT + +#include + +//====================================================================================== +// Win32 + GameInput implementation +//====================================================================================== + +// +// Call this static function from your Window Message Procedure +// +// LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +// { +// switch (message) +// { +// case WM_ACTIVATE: +// case WM_ACTIVATEAPP: +// case WM_MOUSEMOVE: +// case WM_LBUTTONDOWN: +// case WM_LBUTTONUP: +// case WM_RBUTTONDOWN: +// case WM_RBUTTONUP: +// case WM_MBUTTONDOWN: +// case WM_MBUTTONUP: +// case WM_MOUSEWHEEL: +// case WM_XBUTTONDOWN: +// case WM_XBUTTONUP: +// Mouse::ProcessMessage(message, wParam, lParam); +// break; +// +// } +// } +// + +class Mouse::Impl +{ +public: + explicit Impl(Mouse* owner) noexcept(false) : + mState{}, + mOwner(owner), + mScale(1.f), + mConnected(0), + mDeviceToken(0), + mWindow(nullptr), + mMode(MODE_ABSOLUTE), + mAutoReset(true), + mScrollWheelCurrent(0), + mRelativeX(INT64_MAX), + mRelativeY(INT64_MAX), + mLastX(INT64_MAX), + mLastY(INT64_MAX), + mRelativeWheelY(INT64_MAX) + { + if (s_mouse) + { + throw std::logic_error("Mouse is a singleton"); + } + + s_mouse = this; + + HRESULT hr = GameInputCreate(mGameInput.GetAddressOf()); + if (SUCCEEDED(hr)) + { + ThrowIfFailed(mGameInput->RegisterDeviceCallback( + nullptr, + GameInputKindMouse, + GameInputDeviceConnected, + GameInputBlockingEnumeration, + this, + OnGameInputDevice, + &mDeviceToken)); + } + else + { + DebugTrace("ERROR: GameInputCreate [mouse] failed with %08X\n", static_cast(hr)); + #ifdef _GAMING_XBOX + ThrowIfFailed(hr); + #elif defined(_DEBUG) + DebugTrace( + "\t**** Check that the 'GameInput Service' is running on this system. ****\n" + "\t**** NOTE: No relative movement be returned and IsConnected will return false. ****\n" + ); + #endif + } + + mScrollWheelValue.reset(CreateEventEx(nullptr, nullptr, CREATE_EVENT_MANUAL_RESET, EVENT_MODIFY_STATE | SYNCHRONIZE)); + if (!mScrollWheelValue) + { + throw std::system_error(std::error_code(static_cast(GetLastError()), std::system_category()), "CreateEventEx"); + } + } + + Impl(Impl&&) = default; + Impl& operator= (Impl&&) = default; + + Impl(Impl const&) = delete; + Impl& operator= (Impl const&) = delete; + + ~Impl() + { + if (mDeviceToken) + { + if (mGameInput) + { + if (!mGameInput->UnregisterCallback(mDeviceToken, UINT64_MAX)) + { + DebugTrace("ERROR: GameInput::UnregisterCallback [mouse] failed"); + } + } + + mDeviceToken = 0; + } + + s_mouse = nullptr; + } + + void GetState(State& state) const + { + memcpy(&state, &mState, sizeof(State)); + state.positionMode = mMode; + + DWORD result = WaitForSingleObjectEx(mScrollWheelValue.get(), 0, FALSE); + if (result == WAIT_FAILED) + throw std::system_error(std::error_code(static_cast(GetLastError()), std::system_category()), "WaitForSingleObjectEx"); + + if (result == WAIT_OBJECT_0) + { + mScrollWheelCurrent = 0; + } + + if (state.positionMode == MODE_RELATIVE) + { + state.x = state.y = 0; + + if (mGameInput) + { + ComPtr reading; + if (SUCCEEDED(mGameInput->GetCurrentReading(GameInputKindMouse, nullptr, reading.GetAddressOf()))) + { + GameInputMouseState mouse; + if (reading->GetMouseState(&mouse)) + { + state.leftButton = (mouse.buttons & GameInputMouseLeftButton) != 0; + state.middleButton = (mouse.buttons & GameInputMouseMiddleButton) != 0; + state.rightButton = (mouse.buttons & GameInputMouseRightButton) != 0; + state.xButton1 = (mouse.buttons & GameInputMouseButton4) != 0; + state.xButton2 = (mouse.buttons & GameInputMouseButton5) != 0; + + if (mRelativeX != INT64_MAX) + { + state.x = static_cast(mouse.positionX - mRelativeX); + state.y = static_cast(mouse.positionY - mRelativeY); + int scrollDelta = static_cast(mouse.wheelY - mRelativeWheelY); + mScrollWheelCurrent += scrollDelta; + } + + if (mAutoReset) + { + mRelativeX = mouse.positionX; + mRelativeY = mouse.positionY; + } + + mLastX = mouse.positionX; + mLastY = mouse.positionY; + mRelativeWheelY = mouse.wheelY; + } + } + } + } + + state.scrollWheelValue = mScrollWheelCurrent; + } + + void ResetScrollWheelValue() noexcept + { + SetEvent(mScrollWheelValue.get()); + } + + void SetWindow(HWND window) + { + mWindow = window; + } + + void SetMode(Mode mode) + { + if (mMode == mode) + return; + + mMode = mode; + mLastX = mRelativeX = INT64_MAX; + mLastY = mRelativeY = INT64_MAX; + mRelativeWheelY = INT64_MAX; + + if (mode == MODE_RELATIVE) + { + ShowCursor(FALSE); + ClipToWindow(); + } + else + { + ShowCursor(TRUE); + +#ifndef _GAMING_XBOX + POINT point; + point.x = mState.x; + point.y = mState.y; + + if (MapWindowPoints(mWindow, nullptr, &point, 1)) + { + SetCursorPos(point.x, point.y); + } + + ClipCursor(nullptr); +#endif + } + } + + void EndOfInputFrame() + { + mAutoReset = false; + + if (mMode == MODE_RELATIVE) + { + mRelativeX = mLastX; + mRelativeY = mLastY; + } + } + + bool IsConnected() const noexcept + { + return mConnected > 0; + } + + bool IsVisible() const noexcept + { + if (mMode == MODE_RELATIVE) + return false; + + CURSORINFO info = { sizeof(CURSORINFO), 0, nullptr, {} }; + if (!GetCursorInfo(&info)) + return false; + + return (info.flags & CURSOR_SHOWING) != 0; + } + + void SetVisible(bool visible) + { + if (mMode == MODE_RELATIVE) + return; + + CURSORINFO info = { sizeof(CURSORINFO), 0, nullptr, {} }; + if (!GetCursorInfo(&info)) + { + throw std::system_error(std::error_code(static_cast(GetLastError()), std::system_category()), "GetCursorInfo"); + } + + bool isvisible = (info.flags & CURSOR_SHOWING) != 0; + if (isvisible != visible) + { + ShowCursor(visible); + } + } + + State mState; + Mouse* mOwner; + float mScale; + uint32_t mConnected; + + static Mouse::Impl* s_mouse; + +private: + ComPtr mGameInput; + GameInputCallbackToken mDeviceToken; + + HWND mWindow; + Mode mMode; + bool mAutoReset; + + ScopedHandle mScrollWheelValue; + + mutable int mScrollWheelCurrent; + mutable int64_t mRelativeX; + mutable int64_t mRelativeY; + mutable int64_t mLastX; + mutable int64_t mLastY; + mutable int64_t mRelativeWheelY; + + friend void Mouse::ProcessMessage(UINT message, WPARAM wParam, LPARAM lParam); + + static void CALLBACK OnGameInputDevice( + _In_ GameInputCallbackToken, + _In_ void * context, + _In_ IGameInputDevice *, + _In_ uint64_t, + _In_ GameInputDeviceStatus currentStatus, + _In_ GameInputDeviceStatus) noexcept + { + auto impl = reinterpret_cast(context); + + if (currentStatus & GameInputDeviceConnected) + { + ++impl->mConnected; + } + else if (impl->mConnected > 0) + { + --impl->mConnected; + } + } + + void ClipToWindow() noexcept + { +#ifndef _GAMING_XBOX + assert(mWindow != nullptr); + + RECT rect; + GetClientRect(mWindow, &rect); + + POINT ul; + ul.x = rect.left; + ul.y = rect.top; + + POINT lr; + lr.x = rect.right; + lr.y = rect.bottom; + + std::ignore = MapWindowPoints(mWindow, nullptr, &ul, 1); + std::ignore = MapWindowPoints(mWindow, nullptr, &lr, 1); + + rect.left = ul.x; + rect.top = ul.y; + + rect.right = lr.x; + rect.bottom = lr.y; + + ClipCursor(&rect); +#endif + } +}; + + +Mouse::Impl* Mouse::Impl::s_mouse = nullptr; + + +void Mouse::ProcessMessage(UINT message, WPARAM wParam, LPARAM lParam) +{ + auto pImpl = Impl::s_mouse; + + if (!pImpl) + return; + + DWORD result = WaitForSingleObjectEx(pImpl->mScrollWheelValue.get(), 0, FALSE); + if (result == WAIT_FAILED) + throw std::system_error(std::error_code(static_cast(GetLastError()), std::system_category()), "WaitForSingleObjectEx"); + + if (result == WAIT_OBJECT_0) + { + pImpl->mScrollWheelCurrent = 0; + } + + switch (message) + { + case WM_ACTIVATE: + case WM_ACTIVATEAPP: + if (wParam) + { + if (pImpl->mMode == MODE_RELATIVE) + { + pImpl->mLastX = pImpl->mRelativeX = INT64_MAX; + pImpl->mLastY = pImpl->mRelativeY = INT64_MAX; + + ShowCursor(FALSE); + + pImpl->ClipToWindow(); + } + else + { +#ifndef _GAMING_XBOX + ClipCursor(nullptr); +#endif + } + } + else + { + memset(&pImpl->mState, 0, sizeof(State)); + } + return; + + case WM_MOUSEMOVE: + break; + + case WM_LBUTTONDOWN: + pImpl->mState.leftButton = true; + break; + + case WM_LBUTTONUP: + pImpl->mState.leftButton = false; + break; + + case WM_RBUTTONDOWN: + pImpl->mState.rightButton = true; + break; + + case WM_RBUTTONUP: + pImpl->mState.rightButton = false; + break; + + case WM_MBUTTONDOWN: + pImpl->mState.middleButton = true; + break; + + case WM_MBUTTONUP: + pImpl->mState.middleButton = false; + break; + + case WM_MOUSEWHEEL: + if (pImpl->mMode == MODE_ABSOLUTE) + { + pImpl->mScrollWheelCurrent += GET_WHEEL_DELTA_WPARAM(wParam); + } + return; + + case WM_XBUTTONDOWN: + switch (GET_XBUTTON_WPARAM(wParam)) + { + case XBUTTON1: + pImpl->mState.xButton1 = true; + break; + + case XBUTTON2: + pImpl->mState.xButton2 = true; + break; + } + break; + + case WM_XBUTTONUP: + switch (GET_XBUTTON_WPARAM(wParam)) + { + case XBUTTON1: + pImpl->mState.xButton1 = false; + break; + + case XBUTTON2: + pImpl->mState.xButton2 = false; + break; + } + break; + + default: + // Not a mouse message, so exit + return; + } + + if (pImpl->mMode == MODE_ABSOLUTE) + { + // All mouse messages provide a new pointer position + int xPos = static_cast(LOWORD(lParam)); // GET_X_LPARAM(lParam); + int yPos = static_cast(HIWORD(lParam)); // GET_Y_LPARAM(lParam); + + pImpl->mState.x = static_cast(static_cast(xPos) * pImpl->mScale); + pImpl->mState.y = static_cast(static_cast(yPos) * pImpl->mScale); + } +} + +#ifdef _GAMING_XBOX +void Mouse::SetResolution(float scale) +{ + auto pImpl = Impl::s_mouse; + + if (!pImpl) + return; + + pImpl->mScale = scale; +} +#endif + +void Mouse::SetWindow(HWND window) +{ + pImpl->SetWindow(window); +} + + +#elif defined(USING_COREWINDOW) + +//====================================================================================== +// Windows Store or Universal Windows Platform (UWP) app implementation +//====================================================================================== + +// +// For a Windows Store app or Universal Windows Platform (UWP) app, add the following to your existing +// application methods: +// +// void App::SetWindow(CoreWindow^ window ) +// { +// m_mouse->SetWindow(window); +// } +// +// void App::OnDpiChanged(DisplayInformation^ sender, Object^ args) +// { +// m_mouse->SetDpi(sender->LogicalDpi); +// } +// + +#include + +class Mouse::Impl +{ +public: + explicit Impl(Mouse* owner) noexcept(false) : + mState{}, + mOwner(owner), + mDPI(96.f), + mMode(MODE_ABSOLUTE), + mAutoReset(true), + mLastX(0), + mLastY(0), + mPointerPressedToken{}, + mPointerReleasedToken{}, + mPointerMovedToken{}, + mPointerWheelToken{}, + mPointerMouseMovedToken{}, + mActivatedToken{} + { + if (s_mouse) + { + throw std::logic_error("Mouse is a singleton"); + } + + s_mouse = this; + + mScrollWheelValue.reset(CreateEventEx(nullptr, nullptr, CREATE_EVENT_MANUAL_RESET, EVENT_MODIFY_STATE | SYNCHRONIZE)); + mRelativeRead.reset(CreateEventEx(nullptr, nullptr, CREATE_EVENT_MANUAL_RESET, EVENT_MODIFY_STATE | SYNCHRONIZE)); + if (!mScrollWheelValue + || !mRelativeRead) + { + throw std::system_error(std::error_code(static_cast(GetLastError()), std::system_category()), "CreateEventEx"); + } + } + + ~Impl() + { + s_mouse = nullptr; + + RemoveHandlers(); + } + + void GetState(State& state) const + { + memcpy(&state, &mState, sizeof(State)); + + DWORD result = WaitForSingleObjectEx(mScrollWheelValue.get(), 0, FALSE); + if (result == WAIT_FAILED) + throw std::system_error(std::error_code(static_cast(GetLastError()), std::system_category()), "WaitForSingleObjectEx"); + + if (result == WAIT_OBJECT_0) + { + state.scrollWheelValue = 0; + } + + if (mMode == MODE_RELATIVE) + { + result = WaitForSingleObjectEx(mRelativeRead.get(), 0, FALSE); + + if (result == WAIT_FAILED) + throw std::system_error(std::error_code(static_cast(GetLastError()), std::system_category()), "WaitForSingleObjectEx"); + + if (result == WAIT_OBJECT_0) + { + state.x = state.y = 0; + } + else + { + SetEvent(mRelativeRead.get()); + } + + if (mAutoReset) + { + mState.x = mState.y = 0; + } + } + + state.positionMode = mMode; + } + + void ResetScrollWheelValue() noexcept + { + SetEvent(mScrollWheelValue.get()); + } + + void SetMode(Mode mode) + { + using namespace Microsoft::WRL; + using namespace Microsoft::WRL::Wrappers; + using namespace ABI::Windows::UI::Core; + using namespace ABI::Windows::Foundation; + + if (mMode == mode) + return; + + ComPtr statics; + HRESULT hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_UI_Core_CoreWindow).Get(), statics.GetAddressOf()); + ThrowIfFailed(hr); + + ComPtr window; + hr = statics->GetForCurrentThread(window.GetAddressOf()); + ThrowIfFailed(hr); + + if (mode == MODE_RELATIVE) + { + hr = window->get_PointerCursor(mCursor.ReleaseAndGetAddressOf()); + ThrowIfFailed(hr); + + hr = window->put_PointerCursor(nullptr); + ThrowIfFailed(hr); + + SetEvent(mRelativeRead.get()); + + mLastX = mState.x; + mLastY = mState.y; + + mState.x = mState.y = 0; + + mMode = MODE_RELATIVE; + } + else + { + if (!mCursor) + { + ComPtr factory; + hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_UI_Core_CoreCursor).Get(), factory.GetAddressOf()); + ThrowIfFailed(hr); + + hr = factory->CreateCursor(CoreCursorType_Arrow, 0, mCursor.GetAddressOf()); + ThrowIfFailed(hr); + } + + hr = window->put_PointerCursor(mCursor.Get()); + ThrowIfFailed(hr); + + mState.x = mLastX; + mState.y = mLastY; + + mCursor.Reset(); + + mMode = MODE_ABSOLUTE; + } + } + + void EndOfInputFrame() + { + mAutoReset = false; + + if (mMode == MODE_RELATIVE) + { + mState.x = mState.y = 0; + } + } + + bool IsConnected() const + { + using namespace Microsoft::WRL; + using namespace Microsoft::WRL::Wrappers; + using namespace ABI::Windows::Devices::Input; + using namespace ABI::Windows::Foundation; + + ComPtr caps; + HRESULT hr = RoActivateInstance(HStringReference(RuntimeClass_Windows_Devices_Input_MouseCapabilities).Get(), &caps); + ThrowIfFailed(hr); + + INT32 value; + if (SUCCEEDED(caps->get_MousePresent(&value))) + { + return value != 0; + } + + return false; + } + + bool IsVisible() const noexcept + { + if (mMode == MODE_RELATIVE) + return false; + + ComPtr cursor; + if (FAILED(mWindow->get_PointerCursor(cursor.GetAddressOf()))) + return false; + + return cursor != nullptr; + } + + void SetVisible(bool visible) + { + using namespace Microsoft::WRL::Wrappers; + using namespace ABI::Windows::Foundation; + using namespace ABI::Windows::UI::Core; + + if (mMode == MODE_RELATIVE) + return; + + if (visible) + { + if (!mCursor) + { + ComPtr factory; + HRESULT hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_UI_Core_CoreCursor).Get(), factory.GetAddressOf()); + ThrowIfFailed(hr); + + hr = factory->CreateCursor(CoreCursorType_Arrow, 0, mCursor.GetAddressOf()); + ThrowIfFailed(hr); + } + + HRESULT hr = mWindow->put_PointerCursor(mCursor.Get()); + ThrowIfFailed(hr); + } + else + { + HRESULT hr = mWindow->put_PointerCursor(nullptr); + ThrowIfFailed(hr); + } + } + + void SetWindow(ABI::Windows::UI::Core::ICoreWindow* window) + { + using namespace Microsoft::WRL; + using namespace Microsoft::WRL::Wrappers; + using namespace ABI::Windows::Foundation; + using namespace ABI::Windows::Devices::Input; + using namespace ABI::Windows::UI::Core; + + if (mWindow.Get() == window) + return; + + RemoveHandlers(); + + mWindow = window; + + if (!window) + { + mCursor.Reset(); + mMouse.Reset(); + return; + } + + ComPtr mouseStatics; + HRESULT hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_Devices_Input_MouseDevice).Get(), mouseStatics.GetAddressOf()); + ThrowIfFailed(hr); + + hr = mouseStatics->GetForCurrentView(mMouse.ReleaseAndGetAddressOf()); + ThrowIfFailed(hr); + + using MouseMovedHandler = __FITypedEventHandler_2_Windows__CDevices__CInput__CMouseDevice_Windows__CDevices__CInput__CMouseEventArgs; + hr = mMouse->add_MouseMoved(Callback(MouseMovedEvent).Get(), &mPointerMouseMovedToken); + ThrowIfFailed(hr); + + using PointerHandler = __FITypedEventHandler_2_Windows__CUI__CCore__CCoreWindow_Windows__CUI__CCore__CPointerEventArgs; + auto cb = Callback(PointerEvent); + + hr = window->add_PointerPressed(cb.Get(), &mPointerPressedToken); + ThrowIfFailed(hr); + + hr = window->add_PointerReleased(cb.Get(), &mPointerReleasedToken); + ThrowIfFailed(hr); + + hr = window->add_PointerMoved(cb.Get(), &mPointerMovedToken); + ThrowIfFailed(hr); + + hr = window->add_PointerWheelChanged(Callback(PointerWheel).Get(), &mPointerWheelToken); + ThrowIfFailed(hr); + + using ActivatedHandler = __FITypedEventHandler_2_Windows__CUI__CCore__CCoreWindow_Windows__CUI__CCore__CWindowActivatedEventArgs; + hr = window->add_Activated(Callback(ActivatedEvent).Get(), &mActivatedToken); + ThrowIfFailed(hr); + } + + mutable State mState; + Mouse* mOwner; + float mDPI; + + static Mouse::Impl* s_mouse; + +private: + Mode mMode; + bool mAutoReset; + int mLastX; + int mLastY; + + ComPtr mWindow; + ComPtr mMouse; + ComPtr mCursor; + + ScopedHandle mScrollWheelValue; + ScopedHandle mRelativeRead; + + EventRegistrationToken mPointerPressedToken; + EventRegistrationToken mPointerReleasedToken; + EventRegistrationToken mPointerMovedToken; + EventRegistrationToken mPointerWheelToken; + EventRegistrationToken mPointerMouseMovedToken; + EventRegistrationToken mActivatedToken; + + void RemoveHandlers() + { + if (mWindow) + { + std::ignore = mWindow->remove_PointerPressed(mPointerPressedToken); + mPointerPressedToken.value = 0; + + std::ignore = mWindow->remove_PointerReleased(mPointerReleasedToken); + mPointerReleasedToken.value = 0; + + std::ignore = mWindow->remove_PointerMoved(mPointerMovedToken); + mPointerMovedToken.value = 0; + + std::ignore = mWindow->remove_PointerWheelChanged(mPointerWheelToken); + mPointerWheelToken.value = 0; + + std::ignore = mWindow->remove_Activated(mActivatedToken); + mActivatedToken.value = 0; + } + + if (mMouse) + { + std::ignore = mMouse->remove_MouseMoved(mPointerMouseMovedToken); + mPointerMouseMovedToken.value = 0; + } + } + + static HRESULT PointerEvent(IInspectable*, ABI::Windows::UI::Core::IPointerEventArgs* args) + { + using namespace ABI::Windows::Foundation; + using namespace ABI::Windows::UI::Input; + using namespace ABI::Windows::Devices::Input; + + if (!s_mouse) + return S_OK; + + ComPtr currentPoint; + HRESULT hr = args->get_CurrentPoint(currentPoint.GetAddressOf()); + ThrowIfFailed(hr); + + ComPtr pointerDevice; + hr = currentPoint->get_PointerDevice(pointerDevice.GetAddressOf()); + ThrowIfFailed(hr); + + PointerDeviceType devType; + hr = pointerDevice->get_PointerDeviceType(&devType); + ThrowIfFailed(hr); + + if (devType == PointerDeviceType::PointerDeviceType_Mouse) + { + ComPtr props; + hr = currentPoint->get_Properties(props.GetAddressOf()); + ThrowIfFailed(hr); + + boolean value; + hr = props->get_IsLeftButtonPressed(&value); + ThrowIfFailed(hr); + s_mouse->mState.leftButton = value != 0; + + hr = props->get_IsRightButtonPressed(&value); + ThrowIfFailed(hr); + s_mouse->mState.rightButton = value != 0; + + hr = props->get_IsMiddleButtonPressed(&value); + ThrowIfFailed(hr); + s_mouse->mState.middleButton = value != 0; + + hr = props->get_IsXButton1Pressed(&value); + ThrowIfFailed(hr); + s_mouse->mState.xButton1 = value != 0; + + hr = props->get_IsXButton2Pressed(&value); + ThrowIfFailed(hr); + s_mouse->mState.xButton2 = value != 0; + } + + if (s_mouse->mMode == MODE_ABSOLUTE) + { + Point pos; + hr = currentPoint->get_Position(&pos); + ThrowIfFailed(hr); + + const float dpi = s_mouse->mDPI; + + s_mouse->mState.x = static_cast(pos.X * dpi / 96.f + 0.5f); + s_mouse->mState.y = static_cast(pos.Y * dpi / 96.f + 0.5f); + } + + return S_OK; + } + + static HRESULT PointerWheel(IInspectable*, ABI::Windows::UI::Core::IPointerEventArgs* args) + { + using namespace ABI::Windows::Foundation; + using namespace ABI::Windows::UI::Input; + using namespace ABI::Windows::Devices::Input; + + if (!s_mouse) + return S_OK; + + ComPtr currentPoint; + HRESULT hr = args->get_CurrentPoint(currentPoint.GetAddressOf()); + ThrowIfFailed(hr); + + ComPtr pointerDevice; + hr = currentPoint->get_PointerDevice(pointerDevice.GetAddressOf()); + ThrowIfFailed(hr); + + PointerDeviceType devType; + hr = pointerDevice->get_PointerDeviceType(&devType); + ThrowIfFailed(hr); + + if (devType == PointerDeviceType::PointerDeviceType_Mouse) + { + ComPtr props; + hr = currentPoint->get_Properties(props.GetAddressOf()); + ThrowIfFailed(hr); + + boolean ishorz; + hr = props->get_IsHorizontalMouseWheel(&ishorz); + ThrowIfFailed(hr); + if (ishorz) + { + // Mouse only exposes the vertical scroll wheel. + return S_OK; + } + + INT32 value; + hr = props->get_MouseWheelDelta(&value); + ThrowIfFailed(hr); + + HANDLE evt = s_mouse->mScrollWheelValue.get(); + if (WaitForSingleObjectEx(evt, 0, FALSE) == WAIT_OBJECT_0) + { + s_mouse->mState.scrollWheelValue = 0; + ResetEvent(evt); + } + + s_mouse->mState.scrollWheelValue += value; + + if (s_mouse->mMode == MODE_ABSOLUTE) + { + Point pos; + hr = currentPoint->get_Position(&pos); + ThrowIfFailed(hr); + + float dpi = s_mouse->mDPI; + + s_mouse->mState.x = static_cast(pos.X * dpi / 96.f + 0.5f); + s_mouse->mState.y = static_cast(pos.Y * dpi / 96.f + 0.5f); + } + } + + return S_OK; + } + + static HRESULT MouseMovedEvent(IInspectable*, ABI::Windows::Devices::Input::IMouseEventArgs* args) + { + using namespace ABI::Windows::Devices::Input; + + if (!s_mouse) + return S_OK; + + if (s_mouse->mMode == MODE_RELATIVE) + { + MouseDelta delta; + HRESULT hr = args->get_MouseDelta(&delta); + ThrowIfFailed(hr); + + s_mouse->mState.x += delta.X; + s_mouse->mState.y += delta.Y; + + ResetEvent(s_mouse->mRelativeRead.get()); + } + + return S_OK; + } + + static HRESULT ActivatedEvent(IInspectable*, ABI::Windows::UI::Core::IWindowActivatedEventArgs*) + { + if (!s_mouse) + return S_OK; + + if (s_mouse->mMode == MODE_RELATIVE) + { + s_mouse->mState.x = s_mouse->mState.y = 0; + } + + return S_OK; + } +}; + + +Mouse::Impl* Mouse::Impl::s_mouse = nullptr; + + +void Mouse::SetWindow(ABI::Windows::UI::Core::ICoreWindow* window) +{ + pImpl->SetWindow(window); +} + + +void Mouse::SetDpi(float dpi) +{ + auto pImpl = Impl::s_mouse; + + if (!pImpl) + return; + + pImpl->mDPI = dpi; +} + + +#else + +//====================================================================================== +// Win32 desktop implementation +//====================================================================================== + +// +// For a Win32 desktop application, in your window setup be sure to call this method: +// +// m_mouse->SetWindow(hwnd); +// +// And call this static function from your Window Message Procedure +// +// LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +// { +// switch (message) +// { +// case WM_ACTIVATE: +// case WM_ACTIVATEAPP: +// case WM_INPUT: +// case WM_MOUSEMOVE: +// case WM_LBUTTONDOWN: +// case WM_LBUTTONUP: +// case WM_RBUTTONDOWN: +// case WM_RBUTTONUP: +// case WM_MBUTTONDOWN: +// case WM_MBUTTONUP: +// case WM_MOUSEWHEEL: +// case WM_XBUTTONDOWN: +// case WM_XBUTTONUP: +// case WM_MOUSEHOVER: +// Mouse::ProcessMessage(message, wParam, lParam); +// break; +// +// } +// } +// + +class Mouse::Impl +{ +public: + explicit Impl(Mouse* owner) noexcept(false) : + mState{}, + mOwner(owner), + mWindow(nullptr), + mMode(MODE_ABSOLUTE), + mLastX(0), + mLastY(0), + mRelativeX(INT32_MAX), + mRelativeY(INT32_MAX), + mInFocus(true), + mAutoReset(true) + { + if (s_mouse) + { + throw std::logic_error("Mouse is a singleton"); + } + + s_mouse = this; + + mScrollWheelValue.reset(CreateEventEx(nullptr, nullptr, CREATE_EVENT_MANUAL_RESET, EVENT_MODIFY_STATE | SYNCHRONIZE)); + mRelativeRead.reset(CreateEventEx(nullptr, nullptr, CREATE_EVENT_MANUAL_RESET, EVENT_MODIFY_STATE | SYNCHRONIZE)); + mAbsoluteMode.reset(CreateEventEx(nullptr, nullptr, 0, EVENT_MODIFY_STATE | SYNCHRONIZE)); + mRelativeMode.reset(CreateEventEx(nullptr, nullptr, 0, EVENT_MODIFY_STATE | SYNCHRONIZE)); + if (!mScrollWheelValue + || !mRelativeRead + || !mAbsoluteMode + || !mRelativeMode) + { + throw std::system_error(std::error_code(static_cast(GetLastError()), std::system_category()), "CreateEventEx"); + } + } + + Impl(Impl&&) = default; + Impl& operator= (Impl&&) = default; + + Impl(Impl const&) = delete; + Impl& operator= (Impl const&) = delete; + + ~Impl() + { + s_mouse = nullptr; + } + + void GetState(State& state) const + { + memcpy(&state, &mState, sizeof(State)); + state.positionMode = mMode; + + DWORD result = WaitForSingleObjectEx(mScrollWheelValue.get(), 0, FALSE); + if (result == WAIT_FAILED) + throw std::system_error(std::error_code(static_cast(GetLastError()), std::system_category()), "WaitForSingleObjectEx"); + + if (result == WAIT_OBJECT_0) + { + state.scrollWheelValue = 0; + } + + if (state.positionMode == MODE_RELATIVE) + { + result = WaitForSingleObjectEx(mRelativeRead.get(), 0, FALSE); + + if (result == WAIT_FAILED) + throw std::system_error(std::error_code(static_cast(GetLastError()), std::system_category()), "WaitForSingleObjectEx"); + + if (result == WAIT_OBJECT_0) + { + state.x = state.y = 0; + } + else + { + SetEvent(mRelativeRead.get()); + } + + if (mAutoReset) + { + mState.x = mState.y = 0; + } + } + } + + void ResetScrollWheelValue() noexcept + { + SetEvent(mScrollWheelValue.get()); + } + + void SetMode(Mode mode) + { + if (mMode == mode) + return; + + SetEvent((mode == MODE_ABSOLUTE) ? mAbsoluteMode.get() : mRelativeMode.get()); + + assert(mWindow != nullptr); + + // Send a WM_HOVER as a way to 'kick' the message processing even if the mouse is still. + TRACKMOUSEEVENT tme; + tme.cbSize = sizeof(tme); + tme.dwFlags = TME_HOVER; + tme.hwndTrack = mWindow; + tme.dwHoverTime = 1; + if (!TrackMouseEvent(&tme)) + { + throw std::system_error(std::error_code(static_cast(GetLastError()), std::system_category()), "TrackMouseEvent"); + } + } + + void EndOfInputFrame() + { + mAutoReset = false; + + if (mMode == MODE_RELATIVE) + { + mState.x = mState.y = 0; + } + } + + bool IsConnected() const noexcept + { + return GetSystemMetrics(SM_MOUSEPRESENT) != 0; + } + + bool IsVisible() const noexcept + { + if (mMode == MODE_RELATIVE) + return false; + + CURSORINFO info = { sizeof(CURSORINFO), 0, nullptr, {} }; + if (!GetCursorInfo(&info)) + return false; + + return (info.flags & CURSOR_SHOWING) != 0; + } + + void SetVisible(bool visible) + { + if (mMode == MODE_RELATIVE) + return; + + CURSORINFO info = { sizeof(CURSORINFO), 0, nullptr, {} }; + if (!GetCursorInfo(&info)) + { + throw std::system_error(std::error_code(static_cast(GetLastError()), std::system_category()), "GetCursorInfo"); + } + + const bool isvisible = (info.flags & CURSOR_SHOWING) != 0; + if (isvisible != visible) + { + ShowCursor(visible); + } + } + + void SetWindow(HWND window) + { + if (mWindow == window) + return; + + assert(window != nullptr); + + RAWINPUTDEVICE Rid; + Rid.usUsagePage = 0x1 /* HID_USAGE_PAGE_GENERIC */; + Rid.usUsage = 0x2 /* HID_USAGE_GENERIC_MOUSE */; + Rid.dwFlags = RIDEV_INPUTSINK; + Rid.hwndTarget = window; + if (!RegisterRawInputDevices(&Rid, 1, sizeof(RAWINPUTDEVICE))) + { + throw std::system_error(std::error_code(static_cast(GetLastError()), std::system_category()), "RegisterRawInputDevices"); + } + + mWindow = window; + } + + mutable State mState; + + Mouse* mOwner; + + static Mouse::Impl* s_mouse; + +private: + HWND mWindow; + Mode mMode; + + ScopedHandle mScrollWheelValue; + ScopedHandle mRelativeRead; + ScopedHandle mAbsoluteMode; + ScopedHandle mRelativeMode; + + int mLastX; + int mLastY; + int mRelativeX; + int mRelativeY; + + bool mInFocus; + bool mAutoReset; + + friend void Mouse::ProcessMessage(UINT message, WPARAM wParam, LPARAM lParam); + + void ClipToWindow() noexcept + { + assert(mWindow != nullptr); + + RECT rect = {}; + std::ignore = GetClientRect(mWindow, &rect); + + POINT ul; + ul.x = rect.left; + ul.y = rect.top; + + POINT lr; + lr.x = rect.right; + lr.y = rect.bottom; + + std::ignore = MapWindowPoints(mWindow, nullptr, &ul, 1); + std::ignore = MapWindowPoints(mWindow, nullptr, &lr, 1); + + rect.left = ul.x; + rect.top = ul.y; + + rect.right = lr.x; + rect.bottom = lr.y; + + ClipCursor(&rect); + } +}; + + +Mouse::Impl* Mouse::Impl::s_mouse = nullptr; + + +void Mouse::SetWindow(HWND window) +{ + pImpl->SetWindow(window); +} + + +void Mouse::ProcessMessage(UINT message, WPARAM wParam, LPARAM lParam) +{ + auto pImpl = Impl::s_mouse; + + if (!pImpl) + return; + + // First handle any pending scroll wheel reset event. + switch (WaitForSingleObjectEx(pImpl->mScrollWheelValue.get(), 0, FALSE)) + { + default: + case WAIT_TIMEOUT: + break; + + case WAIT_OBJECT_0: + pImpl->mState.scrollWheelValue = 0; + ResetEvent(pImpl->mScrollWheelValue.get()); + break; + + case WAIT_FAILED: + throw std::system_error(std::error_code(static_cast(GetLastError()), std::system_category()), "WaitForMultipleObjectsEx"); + } + + // Next handle mode change events. + HANDLE events[2] = { pImpl->mAbsoluteMode.get(), pImpl->mRelativeMode.get() }; + switch (WaitForMultipleObjectsEx(static_cast(std::size(events)), events, FALSE, 0, FALSE)) + { + default: + case WAIT_TIMEOUT: + break; + + case WAIT_OBJECT_0: + { + pImpl->mMode = MODE_ABSOLUTE; + ClipCursor(nullptr); + + POINT point; + point.x = pImpl->mLastX; + point.y = pImpl->mLastY; + + // We show the cursor before moving it to support Remote Desktop + ShowCursor(TRUE); + + if (MapWindowPoints(pImpl->mWindow, nullptr, &point, 1)) + { + SetCursorPos(point.x, point.y); + } + pImpl->mState.x = pImpl->mLastX; + pImpl->mState.y = pImpl->mLastY; + } + break; + + case (WAIT_OBJECT_0 + 1): + { + ResetEvent(pImpl->mRelativeRead.get()); + + pImpl->mMode = MODE_RELATIVE; + pImpl->mState.x = pImpl->mState.y = 0; + pImpl->mRelativeX = INT32_MAX; + pImpl->mRelativeY = INT32_MAX; + + ShowCursor(FALSE); + + pImpl->ClipToWindow(); + } + break; + + case WAIT_FAILED: + throw std::system_error(std::error_code(static_cast(GetLastError()), std::system_category()), "WaitForMultipleObjectsEx"); + } + + switch (message) + { + case WM_ACTIVATE: + case WM_ACTIVATEAPP: + if (wParam) + { + pImpl->mInFocus = true; + + if (pImpl->mMode == MODE_RELATIVE) + { + pImpl->mState.x = pImpl->mState.y = 0; + + ShowCursor(FALSE); + + pImpl->ClipToWindow(); + } + } + else + { + const int scrollWheel = pImpl->mState.scrollWheelValue; + memset(&pImpl->mState, 0, sizeof(State)); + pImpl->mState.scrollWheelValue = scrollWheel; + + if (pImpl->mMode == MODE_RELATIVE) + { + ClipCursor(nullptr); + } + + pImpl->mInFocus = false; + } + return; + + case WM_INPUT: + if (pImpl->mInFocus && pImpl->mMode == MODE_RELATIVE) + { + RAWINPUT raw; + UINT rawSize = sizeof(raw); + + const UINT resultData = GetRawInputData(reinterpret_cast(lParam), RID_INPUT, &raw, &rawSize, sizeof(RAWINPUTHEADER)); + if (resultData == UINT(-1)) + { + throw std::runtime_error("GetRawInputData"); + } + + if (raw.header.dwType == RIM_TYPEMOUSE) + { + if (!(raw.data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE)) + { + pImpl->mState.x += raw.data.mouse.lLastX; + pImpl->mState.y += raw.data.mouse.lLastY; + + ResetEvent(pImpl->mRelativeRead.get()); + } + else if (raw.data.mouse.usFlags & MOUSE_VIRTUAL_DESKTOP) + { + // This is used to make Remote Desktop sessons work + const int width = GetSystemMetrics(SM_CXVIRTUALSCREEN); + const int height = GetSystemMetrics(SM_CYVIRTUALSCREEN); + + auto const x = static_cast((float(raw.data.mouse.lLastX) / 65535.0f) * float(width)); + auto const y = static_cast((float(raw.data.mouse.lLastY) / 65535.0f) * float(height)); + + if (pImpl->mRelativeX == INT32_MAX) + { + pImpl->mState.x = pImpl->mState.y = 0; + } + else + { + pImpl->mState.x = x - pImpl->mRelativeX; + pImpl->mState.y = y - pImpl->mRelativeY; + } + + pImpl->mRelativeX = x; + pImpl->mRelativeY = y; + + ResetEvent(pImpl->mRelativeRead.get()); + } + } + } + return; + + case WM_MOUSEMOVE: + break; + + case WM_LBUTTONDOWN: + pImpl->mState.leftButton = true; + break; + + case WM_LBUTTONUP: + pImpl->mState.leftButton = false; + break; + + case WM_RBUTTONDOWN: + pImpl->mState.rightButton = true; + break; + + case WM_RBUTTONUP: + pImpl->mState.rightButton = false; + break; + + case WM_MBUTTONDOWN: + pImpl->mState.middleButton = true; + break; + + case WM_MBUTTONUP: + pImpl->mState.middleButton = false; + break; + + case WM_MOUSEWHEEL: + pImpl->mState.scrollWheelValue += GET_WHEEL_DELTA_WPARAM(wParam); + return; + + case WM_XBUTTONDOWN: + switch (GET_XBUTTON_WPARAM(wParam)) + { + case XBUTTON1: + pImpl->mState.xButton1 = true; + break; + + case XBUTTON2: + pImpl->mState.xButton2 = true; + break; + } + break; + + case WM_XBUTTONUP: + switch (GET_XBUTTON_WPARAM(wParam)) + { + case XBUTTON1: + pImpl->mState.xButton1 = false; + break; + + case XBUTTON2: + pImpl->mState.xButton2 = false; + break; + } + break; + + case WM_MOUSEHOVER: + break; + + default: + // Not a mouse message, so exit + return; + } + + if (pImpl->mMode == MODE_ABSOLUTE) + { + // All mouse messages provide a new pointer position + const int xPos = static_cast(LOWORD(lParam)); // GET_X_LPARAM(lParam); + const int yPos = static_cast(HIWORD(lParam)); // GET_Y_LPARAM(lParam); + + pImpl->mState.x = pImpl->mLastX = xPos; + pImpl->mState.y = pImpl->mLastY = yPos; + } +} + +#endif +#pragma endregion + +#pragma warning( disable : 4355 ) + +// Public constructor. +Mouse::Mouse() noexcept(false) + : pImpl(std::make_unique(this)) +{ +} + + +// Move constructor. +Mouse::Mouse(Mouse&& moveFrom) noexcept + : pImpl(std::move(moveFrom.pImpl)) +{ + pImpl->mOwner = this; +} + + +// Move assignment. +Mouse& Mouse::operator= (Mouse&& moveFrom) noexcept +{ + pImpl = std::move(moveFrom.pImpl); + pImpl->mOwner = this; + return *this; +} + + +// Public destructor. +Mouse::~Mouse() = default; + + +Mouse::State Mouse::GetState() const +{ + State state; + pImpl->GetState(state); + return state; +} + + +void Mouse::ResetScrollWheelValue() noexcept +{ + pImpl->ResetScrollWheelValue(); +} + + +void Mouse::SetMode(Mode mode) +{ + pImpl->SetMode(mode); +} + + +void Mouse::EndOfInputFrame() noexcept +{ + pImpl->EndOfInputFrame(); +} + + +bool Mouse::IsConnected() const +{ + return pImpl->IsConnected(); +} + +bool Mouse::IsVisible() const noexcept +{ + return pImpl->IsVisible(); +} + +void Mouse::SetVisible(bool visible) +{ + pImpl->SetVisible(visible); +} + +Mouse& Mouse::Get() +{ + if (!Impl::s_mouse || !Impl::s_mouse->mOwner) + throw std::logic_error("Mouse singleton not created"); + + return *Impl::s_mouse->mOwner; +} + + + +//====================================================================================== +// ButtonStateTracker +//====================================================================================== + +#define UPDATE_BUTTON_STATE(field) field = static_cast( ( !!state.field ) | ( ( !!state.field ^ !!lastState.field ) << 1 ) ) + +void Mouse::ButtonStateTracker::Update(const Mouse::State& state) noexcept +{ + UPDATE_BUTTON_STATE(leftButton); + + assert((!state.leftButton && !lastState.leftButton) == (leftButton == UP)); + assert((state.leftButton && lastState.leftButton) == (leftButton == HELD)); + assert((!state.leftButton && lastState.leftButton) == (leftButton == RELEASED)); + assert((state.leftButton && !lastState.leftButton) == (leftButton == PRESSED)); + + UPDATE_BUTTON_STATE(middleButton); + UPDATE_BUTTON_STATE(rightButton); + UPDATE_BUTTON_STATE(xButton1); + UPDATE_BUTTON_STATE(xButton2); + + lastState = state; +} + +#undef UPDATE_BUTTON_STATE + + +void Mouse::ButtonStateTracker::Reset() noexcept +{ + memset(this, 0, sizeof(ButtonStateTracker)); +} diff --git a/Common/DirectXTK12/Src/NormalMapEffect.cpp b/Common/DirectXTK12/Src/NormalMapEffect.cpp new file mode 100644 index 0000000..2916032 --- /dev/null +++ b/Common/DirectXTK12/Src/NormalMapEffect.cpp @@ -0,0 +1,887 @@ +//-------------------------------------------------------------------------------------- +// File: NormalMapEffect.cpp +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "pch.h" +#include "EffectCommon.h" + +namespace DirectX +{ + namespace EffectDirtyFlags + { + constexpr int ConstantBufferBones = 0x100000; + } +} + +using namespace DirectX; + +namespace +{ + // Constant buffer layout. Must match the shader! + struct NormalMapEffectConstants + { + XMVECTOR diffuseColor; + XMVECTOR emissiveColor; + XMVECTOR specularColorAndPower; + + XMVECTOR lightDirection[IEffectLights::MaxDirectionalLights]; + XMVECTOR lightDiffuseColor[IEffectLights::MaxDirectionalLights]; + XMVECTOR lightSpecularColor[IEffectLights::MaxDirectionalLights]; + + XMVECTOR eyePosition; + + XMVECTOR fogColor; + XMVECTOR fogVector; + + XMMATRIX world; + XMVECTOR worldInverseTranspose[3]; + XMMATRIX worldViewProj; + }; + + static_assert((sizeof(NormalMapEffectConstants) % 16) == 0, "CB size not padded correctly"); + + XM_ALIGNED_STRUCT(16) BoneConstants + { + XMVECTOR Bones[SkinnedNormalMapEffect::MaxBones][3]; + }; + + static_assert((sizeof(BoneConstants) % 16) == 0, "CB size not padded correctly"); + + + // Traits type describes our characteristics to the EffectBase template. + struct NormalMapEffectTraits + { + using ConstantBufferType = NormalMapEffectConstants; + + static constexpr int VertexShaderCount = 20; + static constexpr int PixelShaderCount = 4; + static constexpr int ShaderPermutationCount = 40; + static constexpr int RootSignatureCount = 2; + }; +} + +// Internal NormalMapEffect implementation class. +class NormalMapEffect::Impl : public EffectBase +{ +public: + explicit Impl(_In_ ID3D12Device* device); + + void Initialize( + _In_ ID3D12Device* device, + uint32_t effectFlags, + const EffectPipelineStateDescription& pipelineDescription, + bool enableSkinning); + + enum RootParameterIndex + { + TextureSRV, + TextureNormalSRV, + TextureSampler, + ConstantBuffer, + ConstantBufferBones, + TextureSpecularSRV, + RootParameterCount + }; + + int weightsPerVertex; + bool specularMap; + + D3D12_GPU_DESCRIPTOR_HANDLE texture; + D3D12_GPU_DESCRIPTOR_HANDLE normal; + D3D12_GPU_DESCRIPTOR_HANDLE specular; + D3D12_GPU_DESCRIPTOR_HANDLE sampler; + + EffectLights lights; + + int GetPipelineStatePermutation(uint32_t effectFlags) const noexcept; + + void Apply(_In_ ID3D12GraphicsCommandList* commandList); + + BoneConstants boneConstants; + +private: + GraphicsResource mBones; +}; + + +#pragma region Shaders +// Include the precompiled shader code. +namespace +{ +#ifdef _GAMING_XBOX_SCARLETT +#include "XboxGamingScarlettNormalMapEffect_VSNormalPixelLightingTx.inc" +#include "XboxGamingScarlettNormalMapEffect_VSNormalPixelLightingTxVc.inc" +#include "XboxGamingScarlettNormalMapEffect_VSNormalPixelLightingTxNoSpec.inc" +#include "XboxGamingScarlettNormalMapEffect_VSNormalPixelLightingTxVcNoSpec.inc" + +#include "XboxGamingScarlettNormalMapEffect_VSNormalPixelLightingTxBn.inc" +#include "XboxGamingScarlettNormalMapEffect_VSNormalPixelLightingTxVcBn.inc" +#include "XboxGamingScarlettNormalMapEffect_VSNormalPixelLightingTxNoSpecBn.inc" +#include "XboxGamingScarlettNormalMapEffect_VSNormalPixelLightingTxVcNoSpecBn.inc" + +#include "XboxGamingScarlettNormalMapEffect_VSNormalPixelLightingTxInst.inc" +#include "XboxGamingScarlettNormalMapEffect_VSNormalPixelLightingTxVcInst.inc" +#include "XboxGamingScarlettNormalMapEffect_VSNormalPixelLightingTxNoSpecInst.inc" +#include "XboxGamingScarlettNormalMapEffect_VSNormalPixelLightingTxVcNoSpecInst.inc" + +#include "XboxGamingScarlettNormalMapEffect_VSNormalPixelLightingTxBnInst.inc" +#include "XboxGamingScarlettNormalMapEffect_VSNormalPixelLightingTxVcBnInst.inc" +#include "XboxGamingScarlettNormalMapEffect_VSNormalPixelLightingTxNoSpecBnInst.inc" +#include "XboxGamingScarlettNormalMapEffect_VSNormalPixelLightingTxVcNoSpecBnInst.inc" + +#include "XboxGamingScarlettNormalMapEffect_VSSkinnedPixelLightingTx.inc" +#include "XboxGamingScarlettNormalMapEffect_VSSkinnedPixelLightingTxBn.inc" +#include "XboxGamingScarlettNormalMapEffect_VSSkinnedPixelLightingTxNoSpec.inc" +#include "XboxGamingScarlettNormalMapEffect_VSSkinnedPixelLightingTxNoSpecBn.inc" + +#include "XboxGamingScarlettNormalMapEffect_PSNormalPixelLightingTx.inc" +#include "XboxGamingScarlettNormalMapEffect_PSNormalPixelLightingTxNoFog.inc" +#include "XboxGamingScarlettNormalMapEffect_PSNormalPixelLightingTxNoSpec.inc" +#include "XboxGamingScarlettNormalMapEffect_PSNormalPixelLightingTxNoFogSpec.inc" +#elif defined(_GAMING_XBOX) +#include "XboxGamingXboxOneNormalMapEffect_VSNormalPixelLightingTx.inc" +#include "XboxGamingXboxOneNormalMapEffect_VSNormalPixelLightingTxVc.inc" +#include "XboxGamingXboxOneNormalMapEffect_VSNormalPixelLightingTxNoSpec.inc" +#include "XboxGamingXboxOneNormalMapEffect_VSNormalPixelLightingTxVcNoSpec.inc" + +#include "XboxGamingXboxOneNormalMapEffect_VSNormalPixelLightingTxBn.inc" +#include "XboxGamingXboxOneNormalMapEffect_VSNormalPixelLightingTxVcBn.inc" +#include "XboxGamingXboxOneNormalMapEffect_VSNormalPixelLightingTxNoSpecBn.inc" +#include "XboxGamingXboxOneNormalMapEffect_VSNormalPixelLightingTxVcNoSpecBn.inc" + +#include "XboxGamingXboxOneNormalMapEffect_VSNormalPixelLightingTxInst.inc" +#include "XboxGamingXboxOneNormalMapEffect_VSNormalPixelLightingTxVcInst.inc" +#include "XboxGamingXboxOneNormalMapEffect_VSNormalPixelLightingTxNoSpecInst.inc" +#include "XboxGamingXboxOneNormalMapEffect_VSNormalPixelLightingTxVcNoSpecInst.inc" + +#include "XboxGamingXboxOneNormalMapEffect_VSNormalPixelLightingTxBnInst.inc" +#include "XboxGamingXboxOneNormalMapEffect_VSNormalPixelLightingTxVcBnInst.inc" +#include "XboxGamingXboxOneNormalMapEffect_VSNormalPixelLightingTxNoSpecBnInst.inc" +#include "XboxGamingXboxOneNormalMapEffect_VSNormalPixelLightingTxVcNoSpecBnInst.inc" + +#include "XboxGamingXboxOneNormalMapEffect_VSSkinnedPixelLightingTx.inc" +#include "XboxGamingXboxOneNormalMapEffect_VSSkinnedPixelLightingTxBn.inc" +#include "XboxGamingXboxOneNormalMapEffect_VSSkinnedPixelLightingTxNoSpec.inc" +#include "XboxGamingXboxOneNormalMapEffect_VSSkinnedPixelLightingTxNoSpecBn.inc" + +#include "XboxGamingXboxOneNormalMapEffect_PSNormalPixelLightingTx.inc" +#include "XboxGamingXboxOneNormalMapEffect_PSNormalPixelLightingTxNoFog.inc" +#include "XboxGamingXboxOneNormalMapEffect_PSNormalPixelLightingTxNoSpec.inc" +#include "XboxGamingXboxOneNormalMapEffect_PSNormalPixelLightingTxNoFogSpec.inc" +#elif defined(_XBOX_ONE) && defined(_TITLE) +#include "XboxOneNormalMapEffect_VSNormalPixelLightingTx.inc" +#include "XboxOneNormalMapEffect_VSNormalPixelLightingTxVc.inc" +#include "XboxOneNormalMapEffect_VSNormalPixelLightingTxNoSpec.inc" +#include "XboxOneNormalMapEffect_VSNormalPixelLightingTxVcNoSpec.inc" + +#include "XboxOneNormalMapEffect_VSNormalPixelLightingTxBn.inc" +#include "XboxOneNormalMapEffect_VSNormalPixelLightingTxVcBn.inc" +#include "XboxOneNormalMapEffect_VSNormalPixelLightingTxNoSpecBn.inc" +#include "XboxOneNormalMapEffect_VSNormalPixelLightingTxVcNoSpecBn.inc" + +#include "XboxOneNormalMapEffect_VSNormalPixelLightingTxInst.inc" +#include "XboxOneNormalMapEffect_VSNormalPixelLightingTxVcInst.inc" +#include "XboxOneNormalMapEffect_VSNormalPixelLightingTxNoSpecInst.inc" +#include "XboxOneNormalMapEffect_VSNormalPixelLightingTxVcNoSpecInst.inc" + +#include "XboxOneNormalMapEffect_VSNormalPixelLightingTxBnInst.inc" +#include "XboxOneNormalMapEffect_VSNormalPixelLightingTxVcBnInst.inc" +#include "XboxOneNormalMapEffect_VSNormalPixelLightingTxNoSpecBnInst.inc" +#include "XboxOneNormalMapEffect_VSNormalPixelLightingTxVcNoSpecBnInst.inc" + +#include "XboxOneNormalMapEffect_VSSkinnedPixelLightingTx.inc" +#include "XboxOneNormalMapEffect_VSSkinnedPixelLightingTxBn.inc" +#include "XboxOneNormalMapEffect_VSSkinnedPixelLightingTxNoSpec.inc" +#include "XboxOneNormalMapEffect_VSSkinnedPixelLightingTxNoSpecBn.inc" + +#include "XboxOneNormalMapEffect_PSNormalPixelLightingTx.inc" +#include "XboxOneNormalMapEffect_PSNormalPixelLightingTxNoFog.inc" +#include "XboxOneNormalMapEffect_PSNormalPixelLightingTxNoSpec.inc" +#include "XboxOneNormalMapEffect_PSNormalPixelLightingTxNoFogSpec.inc" +#else +#include "NormalMapEffect_VSNormalPixelLightingTx.inc" +#include "NormalMapEffect_VSNormalPixelLightingTxVc.inc" +#include "NormalMapEffect_VSNormalPixelLightingTxNoSpec.inc" +#include "NormalMapEffect_VSNormalPixelLightingTxVcNoSpec.inc" + +#include "NormalMapEffect_VSNormalPixelLightingTxBn.inc" +#include "NormalMapEffect_VSNormalPixelLightingTxVcBn.inc" +#include "NormalMapEffect_VSNormalPixelLightingTxNoSpecBn.inc" +#include "NormalMapEffect_VSNormalPixelLightingTxVcNoSpecBn.inc" + +#include "NormalMapEffect_VSNormalPixelLightingTxInst.inc" +#include "NormalMapEffect_VSNormalPixelLightingTxVcInst.inc" +#include "NormalMapEffect_VSNormalPixelLightingTxNoSpecInst.inc" +#include "NormalMapEffect_VSNormalPixelLightingTxVcNoSpecInst.inc" + +#include "NormalMapEffect_VSNormalPixelLightingTxBnInst.inc" +#include "NormalMapEffect_VSNormalPixelLightingTxVcBnInst.inc" +#include "NormalMapEffect_VSNormalPixelLightingTxNoSpecBnInst.inc" +#include "NormalMapEffect_VSNormalPixelLightingTxVcNoSpecBnInst.inc" + +#include "NormalMapEffect_VSSkinnedPixelLightingTx.inc" +#include "NormalMapEffect_VSSkinnedPixelLightingTxBn.inc" +#include "NormalMapEffect_VSSkinnedPixelLightingTxNoSpec.inc" +#include "NormalMapEffect_VSSkinnedPixelLightingTxNoSpecBn.inc" + +#include "NormalMapEffect_PSNormalPixelLightingTx.inc" +#include "NormalMapEffect_PSNormalPixelLightingTxNoFog.inc" +#include "NormalMapEffect_PSNormalPixelLightingTxNoSpec.inc" +#include "NormalMapEffect_PSNormalPixelLightingTxNoFogSpec.inc" +#endif +} + + +template<> +const D3D12_SHADER_BYTECODE EffectBase::VertexShaderBytecode[] = +{ + { NormalMapEffect_VSNormalPixelLightingTx, sizeof(NormalMapEffect_VSNormalPixelLightingTx) }, + { NormalMapEffect_VSNormalPixelLightingTxVc, sizeof(NormalMapEffect_VSNormalPixelLightingTxVc) }, + + { NormalMapEffect_VSNormalPixelLightingTxBn, sizeof(NormalMapEffect_VSNormalPixelLightingTxBn) }, + { NormalMapEffect_VSNormalPixelLightingTxVcBn, sizeof(NormalMapEffect_VSNormalPixelLightingTxVcBn) }, + + { NormalMapEffect_VSNormalPixelLightingTxNoSpec, sizeof(NormalMapEffect_VSNormalPixelLightingTxNoSpec) }, + { NormalMapEffect_VSNormalPixelLightingTxVcNoSpec, sizeof(NormalMapEffect_VSNormalPixelLightingTxVcNoSpec) }, + + { NormalMapEffect_VSNormalPixelLightingTxNoSpecBn, sizeof(NormalMapEffect_VSNormalPixelLightingTxNoSpecBn) }, + { NormalMapEffect_VSNormalPixelLightingTxVcNoSpecBn, sizeof(NormalMapEffect_VSNormalPixelLightingTxVcNoSpecBn) }, + + { NormalMapEffect_VSNormalPixelLightingTxInst, sizeof(NormalMapEffect_VSNormalPixelLightingTxInst) }, + { NormalMapEffect_VSNormalPixelLightingTxVcInst, sizeof(NormalMapEffect_VSNormalPixelLightingTxVcInst) }, + + { NormalMapEffect_VSNormalPixelLightingTxBnInst, sizeof(NormalMapEffect_VSNormalPixelLightingTxBnInst) }, + { NormalMapEffect_VSNormalPixelLightingTxVcBnInst, sizeof(NormalMapEffect_VSNormalPixelLightingTxVcBnInst) }, + + { NormalMapEffect_VSNormalPixelLightingTxNoSpecInst, sizeof(NormalMapEffect_VSNormalPixelLightingTxNoSpecInst) }, + { NormalMapEffect_VSNormalPixelLightingTxVcNoSpecInst, sizeof(NormalMapEffect_VSNormalPixelLightingTxVcNoSpecInst) }, + + { NormalMapEffect_VSNormalPixelLightingTxNoSpecBnInst, sizeof(NormalMapEffect_VSNormalPixelLightingTxNoSpecBnInst) }, + { NormalMapEffect_VSNormalPixelLightingTxVcNoSpecBnInst, sizeof(NormalMapEffect_VSNormalPixelLightingTxVcNoSpecBnInst) }, + + { NormalMapEffect_VSSkinnedPixelLightingTx, sizeof(NormalMapEffect_VSSkinnedPixelLightingTx) }, + { NormalMapEffect_VSSkinnedPixelLightingTxBn, sizeof(NormalMapEffect_VSSkinnedPixelLightingTxBn) }, + { NormalMapEffect_VSSkinnedPixelLightingTxNoSpec, sizeof(NormalMapEffect_VSSkinnedPixelLightingTxNoSpec) }, + { NormalMapEffect_VSSkinnedPixelLightingTxNoSpecBn, sizeof(NormalMapEffect_VSSkinnedPixelLightingTxNoSpecBn) }, +}; + + +template<> +const int EffectBase::VertexShaderIndices[] = +{ + 0, // pixel lighting + texture + 0, // pixel lighting + texture, no fog + 4, // pixel lighting + texture, no specular + 4, // pixel lighting + texture, no fog or specular + + 2, // pixel lighting (biased vertex normal) + texture + 2, // pixel lighting (biased vertex normal) + texture, no fog + 6, // pixel lighting (biased vertex normal) + texture, no specular + 6, // pixel lighting (biased vertex normal) + texture, no fog or specular + + 1, // pixel lighting + texture + vertex color + 1, // pixel lighting + texture + vertex color, no fog + 5, // pixel lighting + texture + vertex color, no specular + 5, // pixel lighting + texture + vertex color, no fog or specular + + 3, // pixel lighting (biased vertex normal) + texture + vertex color + 3, // pixel lighting (biased vertex normal) + texture + vertex color, no fog + 7, // pixel lighting (biased vertex normal) + texture + vertex color, no specular + 7, // pixel lighting (biased vertex normal) + texture + vertex color, no fog or specular + + 8, // instancing + pixel lighting + texture + 8, // instancing + pixel lighting + texture, no fog + 12, // instancing + pixel lighting + texture, no specular + 12, // instancing + pixel lighting + texture, no fog or specular + + 10, // instancing + pixel lighting (biased vertex normal) + texture + 10, // instancing + pixel lighting (biased vertex normal) + texture, no fog + 14, // instancing + pixel lighting (biased vertex normal) + texture, no specular + 14, // instancing + pixel lighting (biased vertex normal) + texture, no fog or specular + + 9, // instancing + pixel lighting + texture + vertex color + 9, // instancing + pixel lighting + texture + vertex color, no fog + 13, // instancing + pixel lighting + texture + vertex color, no specular + 13, // instancing + pixel lighting + texture + vertex color, no fog or specular + + 11, // instancing + pixel lighting (biased vertex normal) + texture + vertex color + 11, // instancing + pixel lighting (biased vertex normal) + texture + vertex color, no fog + 15, // instancing + pixel lighting (biased vertex normal) + texture + vertex color, no specular + 15, // instancing + pixel lighting (biased vertex normal) + texture + vertex color, no fog or specular + + 16, // skinning + pixel lighting + texture + 16, // skinning + pixel lighting + texture, no fog + 18, // skinning + pixel lighting + texture, no specular + 18, // skinning + pixel lighting + texture, no fog or specular + + 17, // skinning + pixel lighting (biased vertex normal) + texture + 17, // skinning + pixel lighting (biased vertex normal) + texture, no fog + 19, // skinning + pixel lighting (biased vertex normal) + texture, no specular + 19, // skinning + pixel lighting (biased vertex normal) + texture, no fog or specular +}; + + +template<> +const D3D12_SHADER_BYTECODE EffectBase::PixelShaderBytecode[] = +{ + { NormalMapEffect_PSNormalPixelLightingTx, sizeof(NormalMapEffect_PSNormalPixelLightingTx) }, + { NormalMapEffect_PSNormalPixelLightingTxNoFog, sizeof(NormalMapEffect_PSNormalPixelLightingTxNoFog) }, + { NormalMapEffect_PSNormalPixelLightingTxNoSpec, sizeof(NormalMapEffect_PSNormalPixelLightingTxNoSpec) }, + { NormalMapEffect_PSNormalPixelLightingTxNoFogSpec, sizeof(NormalMapEffect_PSNormalPixelLightingTxNoFogSpec) }, +}; + + +template<> +const int EffectBase::PixelShaderIndices[] = +{ + 0, // pixel lighting + texture + 1, // pixel lighting + texture, no fog + 2, // pixel lighting + texture, no specular + 3, // pixel lighting + texture, no fog or specular + + 0, // pixel lighting (biased vertex normal) + texture + 1, // pixel lighting (biased vertex normal) + texture, no fog + 2, // pixel lighting (biased vertex normal) + texture, no specular + 3, // pixel lighting (biased vertex normal) + texture, no fog or specular + + 0, // pixel lighting + texture + vertex color + 1, // pixel lighting + texture + vertex color, no fog + 2, // pixel lighting + texture + vertex color, no specular + 3, // pixel lighting + texture + vertex color, no fog or specular + + 0, // pixel lighting (biased vertex normal) + texture + vertex color + 1, // pixel lighting (biased vertex normal) + texture + vertex color, no fog + 2, // pixel lighting (biased vertex normal) + texture + vertex color, no specular + 3, // pixel lighting (biased vertex normal) + texture + vertex color, no fog or specular + + 0, // instancing + pixel lighting + texture + 1, // instancing + pixel lighting + texture, no fog + 2, // instancing + pixel lighting + texture, no specular + 3, // instancing + pixel lighting + texture, no fog or specular + + 0, // instancing + pixel lighting (biased vertex normal) + texture + 1, // instancing + pixel lighting (biased vertex normal) + texture, no fog + 2, // instancing + pixel lighting (biased vertex normal) + texture, no specular + 3, // instancing + pixel lighting (biased vertex normal) + texture, no fog or specular + + 0, // instancing + pixel lighting + texture + vertex color + 1, // instancing + pixel lighting + texture + vertex color, no fog + 2, // instancing + pixel lighting + texture + vertex color, no specular + 3, // instancing + pixel lighting + texture + vertex color, no fog or specular + + 0, // instancing + pixel lighting (biased vertex normal) + texture + vertex color + 1, // instancing + pixel lighting (biased vertex normal) + texture + vertex color, no fog + 2, // instancing + pixel lighting (biased vertex normal) + texture + vertex color, no specular + 3, // instancing + pixel lighting (biased vertex normal) + texture + vertex color, no fog or specular + + 0, // skinning + pixel lighting + texture + 1, // skinning + pixel lighting + texture, no fog + 2, // skinning + pixel lighting + texture, no specular + 3, // skinning + pixel lighting + texture, no fog or specular + + 0, // skinning + pixel lighting (biased vertex normal) + texture + 1, // skinning + pixel lighting (biased vertex normal) + texture, no fog + 2, // skinning + pixel lighting (biased vertex normal) + texture, no specular + 3, // skinning + pixel lighting (biased vertex normal) + texture, no fog or specular +}; +#pragma endregion + +// Global pool of per-device NormalMapEffect resources. +template<> +SharedResourcePool::DeviceResources> EffectBase::deviceResourcesPool = {}; + + +// Constructor. +NormalMapEffect::Impl::Impl( + _In_ ID3D12Device* device) + : EffectBase(device), + weightsPerVertex(0), + specularMap(false), + texture{}, + normal{}, + specular{}, + sampler{}, + boneConstants{} +{ + static_assert(static_cast(std::size(EffectBase::VertexShaderIndices)) == NormalMapEffectTraits::ShaderPermutationCount, "array/max mismatch"); + static_assert(static_cast(std::size(EffectBase::VertexShaderBytecode)) == NormalMapEffectTraits::VertexShaderCount, "array/max mismatch"); + static_assert(static_cast(std::size(EffectBase::PixelShaderBytecode)) == NormalMapEffectTraits::PixelShaderCount, "array/max mismatch"); + static_assert(static_cast(std::size(EffectBase::PixelShaderIndices)) == NormalMapEffectTraits::ShaderPermutationCount, "array/max mismatch"); +} + +void NormalMapEffect::Impl::Initialize( + _In_ ID3D12Device* device, + uint32_t effectFlags, + const EffectPipelineStateDescription& pipelineDescription, + bool enableSkinning) +{ + specularMap = (effectFlags & EffectFlags::Specular) != 0; + + lights.InitializeConstants(constants.specularColorAndPower, constants.lightDirection, constants.lightDiffuseColor, constants.lightSpecularColor); + + if (enableSkinning) + { + if (effectFlags & EffectFlags::VertexColor) + { + DebugTrace("ERROR: SkinnedNormalMapEffect does not implement EffectFlags::VertexColor\n"); + throw std::invalid_argument("VertexColor effect flag is invalid"); + } + else if (effectFlags & EffectFlags::Instancing) + { + DebugTrace("ERROR: SkinnedNormalMapEffect does not implement EffectFlags::Instancing\n"); + throw std::invalid_argument("Instancing effect flag is invalid"); + } + + weightsPerVertex = 4; + + for (size_t j = 0; j < SkinnedNormalMapEffect::MaxBones; ++j) + { + boneConstants.Bones[j][0] = g_XMIdentityR0; + boneConstants.Bones[j][1] = g_XMIdentityR1; + boneConstants.Bones[j][2] = g_XMIdentityR2; + } + } + + // Create root signature. + { + ENUM_FLAGS_CONSTEXPR D3D12_ROOT_SIGNATURE_FLAGS rootSignatureFlags = + D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT + | D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS + | D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS + | D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS +#ifdef _GAMING_XBOX_SCARLETT + | D3D12_ROOT_SIGNATURE_FLAG_DENY_AMPLIFICATION_SHADER_ROOT_ACCESS + | D3D12_ROOT_SIGNATURE_FLAG_DENY_MESH_SHADER_ROOT_ACCESS +#endif + ; + + CD3DX12_ROOT_PARAMETER rootParameters[RootParameterIndex::RootParameterCount] = {}; + const CD3DX12_DESCRIPTOR_RANGE textureSRV(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0); + rootParameters[RootParameterIndex::TextureSRV].InitAsDescriptorTable(1, &textureSRV, D3D12_SHADER_VISIBILITY_PIXEL); + + const CD3DX12_DESCRIPTOR_RANGE textureSRV2(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 1); + rootParameters[RootParameterIndex::TextureNormalSRV].InitAsDescriptorTable(1, &textureSRV2, D3D12_SHADER_VISIBILITY_PIXEL); + + const CD3DX12_DESCRIPTOR_RANGE textureSampler(D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, 1, 0); + rootParameters[RootParameterIndex::TextureSampler].InitAsDescriptorTable(1, &textureSampler, D3D12_SHADER_VISIBILITY_PIXEL); + rootParameters[RootParameterIndex::ConstantBuffer].InitAsConstantBufferView(0, 0, D3D12_SHADER_VISIBILITY_ALL); + rootParameters[RootParameterIndex::ConstantBufferBones].InitAsConstantBufferView(1, 0, D3D12_SHADER_VISIBILITY_VERTEX); + + CD3DX12_ROOT_SIGNATURE_DESC rsigDesc = {}; + + if (specularMap) + { + const CD3DX12_DESCRIPTOR_RANGE textureSRV3(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 2); + rootParameters[RootParameterIndex::TextureSpecularSRV].InitAsDescriptorTable(1, &textureSRV3, D3D12_SHADER_VISIBILITY_PIXEL); + + rsigDesc.Init(static_cast(std::size(rootParameters)), rootParameters, 0, nullptr, rootSignatureFlags); + + mRootSignature = GetRootSignature(1, rsigDesc); + } + else + { + rsigDesc.Init(static_cast(std::size(rootParameters) - 1), rootParameters, 0, nullptr, rootSignatureFlags); + + mRootSignature = GetRootSignature(0, rsigDesc); + } + } + + assert(mRootSignature != nullptr); + + fog.enabled = (effectFlags & EffectFlags::Fog) != 0; + + // Create pipeline state. + const int sp = GetPipelineStatePermutation(effectFlags); + assert(sp >= 0 && sp < NormalMapEffectTraits::ShaderPermutationCount); + _Analysis_assume_(sp >= 0 && sp < NormalMapEffectTraits::ShaderPermutationCount); + + const int vi = EffectBase::VertexShaderIndices[sp]; + assert(vi >= 0 && vi < NormalMapEffectTraits::VertexShaderCount); + _Analysis_assume_(vi >= 0 && vi < NormalMapEffectTraits::VertexShaderCount); + const int pi = EffectBase::PixelShaderIndices[sp]; + assert(pi >= 0 && pi < NormalMapEffectTraits::PixelShaderCount); + _Analysis_assume_(pi >= 0 && pi < NormalMapEffectTraits::PixelShaderCount); + + pipelineDescription.CreatePipelineState( + device, + mRootSignature, + EffectBase::VertexShaderBytecode[vi], + EffectBase::PixelShaderBytecode[pi], + mPipelineState.GetAddressOf()); + + if (enableSkinning) + { + SetDebugObjectName(mPipelineState.Get(), L"SkinnedNormalMapEffect"); + } + else + { + SetDebugObjectName(mPipelineState.Get(), L"NormalMapEffect"); + } +} + + +int NormalMapEffect::Impl::GetPipelineStatePermutation(uint32_t effectFlags) const noexcept +{ + int permutation = 0; + + // Use optimized shaders if fog is disabled. + if (!fog.enabled) + { + permutation += 1; + } + + if (!specularMap) + { + permutation += 2; + } + + if (effectFlags & EffectFlags::BiasedVertexNormals) + { + // Compressed normals need to be scaled and biased in the vertex shader. + permutation += 4; + } + + if (weightsPerVertex > 0) + { + // Vertex skinning. + permutation += 32; + } + else + { + // Support vertex coloring? + if (effectFlags & EffectFlags::VertexColor) + { + permutation += 8; + } + + if (effectFlags & EffectFlags::Instancing) + { + // Vertex shader needs to use vertex matrix transform. + permutation += 16; + } + } + + return permutation; +} + + +// Sets our state onto the D3D device. +void NormalMapEffect::Impl::Apply(_In_ ID3D12GraphicsCommandList* commandList) +{ + // Compute derived parameter values. + matrices.SetConstants(dirtyFlags, constants.worldViewProj); + fog.SetConstants(dirtyFlags, matrices.worldView, constants.fogVector); + lights.SetConstants(dirtyFlags, matrices, constants.world, constants.worldInverseTranspose, constants.eyePosition, constants.diffuseColor, constants.emissiveColor, true); + + UpdateConstants(); + + if (weightsPerVertex > 0) + { + if (dirtyFlags & EffectDirtyFlags::ConstantBufferBones) + { + mBones = GraphicsMemory::Get(GetDevice()).AllocateConstant(boneConstants); + dirtyFlags &= ~EffectDirtyFlags::ConstantBufferBones; + } + } + + // Set the root signature + commandList->SetGraphicsRootSignature(mRootSignature); + + // Set the texture + if (!texture.ptr || !sampler.ptr || !normal.ptr) + { + DebugTrace("ERROR: Missing texture(s) or sampler for NormalMapEffect (texture %llu, normal %llu, sampler %llu)\n", texture.ptr, normal.ptr, sampler.ptr); + throw std::runtime_error("NormalMapEffect"); + } + + // **NOTE** If D3D asserts or crashes here, you probably need to call commandList->SetDescriptorHeaps() with the required descriptor heaps. + commandList->SetGraphicsRootDescriptorTable(RootParameterIndex::TextureSRV, texture); + commandList->SetGraphicsRootDescriptorTable(RootParameterIndex::TextureNormalSRV, normal); + commandList->SetGraphicsRootDescriptorTable(RootParameterIndex::TextureSampler, sampler); + + if (specularMap) + { + if (!specular.ptr) + { + DebugTrace("ERROR: Missing specular texure NormalMapEffect (texture %llu)\n", specular.ptr); + throw std::runtime_error("NormalMapEffect"); + } + commandList->SetGraphicsRootDescriptorTable(RootParameterIndex::TextureSpecularSRV, specular); + } + + // Set constants + auto const cbuffer = GetConstantBufferGpuAddress(); + commandList->SetGraphicsRootConstantBufferView(RootParameterIndex::ConstantBuffer, cbuffer); + commandList->SetGraphicsRootConstantBufferView(RootParameterIndex::ConstantBufferBones, + (weightsPerVertex > 0) ? mBones.GpuAddress() : cbuffer); + + // Set the pipeline state + commandList->SetPipelineState(EffectBase::mPipelineState.Get()); +} + + +//-------------------------------------------------------------------------------------- +// NormalMapEffect +//-------------------------------------------------------------------------------------- + +NormalMapEffect::NormalMapEffect( + _In_ ID3D12Device* device, + uint32_t effectFlags, + const EffectPipelineStateDescription& pipelineDescription, + bool skinningEnabled) + : pImpl(std::make_unique(device)) +{ + pImpl->Initialize(device, effectFlags, pipelineDescription, skinningEnabled); +} + +NormalMapEffect::NormalMapEffect(NormalMapEffect&&) noexcept = default; +NormalMapEffect& NormalMapEffect::operator= (NormalMapEffect&&) noexcept = default; +NormalMapEffect::~NormalMapEffect() = default; + + +// IEffect methods +void NormalMapEffect::Apply(_In_ ID3D12GraphicsCommandList* commandList) +{ + pImpl->Apply(commandList); +} + + +// Camera settings +void XM_CALLCONV NormalMapEffect::SetWorld(FXMMATRIX value) +{ + pImpl->matrices.world = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::WorldViewProj | EffectDirtyFlags::WorldInverseTranspose | EffectDirtyFlags::FogVector; +} + + +void XM_CALLCONV NormalMapEffect::SetView(FXMMATRIX value) +{ + pImpl->matrices.view = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::WorldViewProj | EffectDirtyFlags::EyePosition | EffectDirtyFlags::FogVector; +} + + +void XM_CALLCONV NormalMapEffect::SetProjection(FXMMATRIX value) +{ + pImpl->matrices.projection = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::WorldViewProj; +} + + +void XM_CALLCONV NormalMapEffect::SetMatrices(FXMMATRIX world, CXMMATRIX view, CXMMATRIX projection) +{ + pImpl->matrices.world = world; + pImpl->matrices.view = view; + pImpl->matrices.projection = projection; + + pImpl->dirtyFlags |= EffectDirtyFlags::WorldViewProj | EffectDirtyFlags::WorldInverseTranspose | EffectDirtyFlags::EyePosition | EffectDirtyFlags::FogVector; +} + + +// Material settings +void XM_CALLCONV NormalMapEffect::SetDiffuseColor(FXMVECTOR value) +{ + pImpl->lights.diffuseColor = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::MaterialColor; +} + + +void XM_CALLCONV NormalMapEffect::SetEmissiveColor(FXMVECTOR value) +{ + pImpl->lights.emissiveColor = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::MaterialColor; +} + + +void XM_CALLCONV NormalMapEffect::SetSpecularColor(FXMVECTOR value) +{ + // Set xyz to new value, but preserve existing w (specular power). + pImpl->constants.specularColorAndPower = XMVectorSelect(pImpl->constants.specularColorAndPower, value, g_XMSelect1110); + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; +} + + +void NormalMapEffect::SetSpecularPower(float value) +{ + // Set w to new value, but preserve existing xyz (specular color). + pImpl->constants.specularColorAndPower = XMVectorSetW(pImpl->constants.specularColorAndPower, value); + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; +} + + +void NormalMapEffect::DisableSpecular() +{ + // Set specular color to black, power to 1 + // Note: Don't use a power of 0 or the shader will generate strange highlights on non-specular materials + + pImpl->constants.specularColorAndPower = g_XMIdentityR3; + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; +} + + +void NormalMapEffect::SetAlpha(float value) +{ + pImpl->lights.alpha = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::MaterialColor; +} + + +void XM_CALLCONV NormalMapEffect::SetColorAndAlpha(FXMVECTOR value) +{ + pImpl->lights.diffuseColor = value; + pImpl->lights.alpha = XMVectorGetW(value); + + pImpl->dirtyFlags |= EffectDirtyFlags::MaterialColor; +} + + +// Light settings +void XM_CALLCONV NormalMapEffect::SetAmbientLightColor(FXMVECTOR value) +{ + pImpl->lights.ambientLightColor = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::MaterialColor; +} + + +void NormalMapEffect::SetLightEnabled(int whichLight, bool value) +{ + pImpl->dirtyFlags |= pImpl->lights.SetLightEnabled(whichLight, value, pImpl->constants.lightDiffuseColor, pImpl->constants.lightSpecularColor); +} + + +void XM_CALLCONV NormalMapEffect::SetLightDirection(int whichLight, FXMVECTOR value) +{ + EffectLights::ValidateLightIndex(whichLight); + + pImpl->constants.lightDirection[whichLight] = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; +} + + +void XM_CALLCONV NormalMapEffect::SetLightDiffuseColor(int whichLight, FXMVECTOR value) +{ + pImpl->dirtyFlags |= pImpl->lights.SetLightDiffuseColor(whichLight, value, pImpl->constants.lightDiffuseColor); +} + + +void XM_CALLCONV NormalMapEffect::SetLightSpecularColor(int whichLight, FXMVECTOR value) +{ + pImpl->dirtyFlags |= pImpl->lights.SetLightSpecularColor(whichLight, value, pImpl->constants.lightSpecularColor); +} + + +void NormalMapEffect::EnableDefaultLighting() +{ + EffectLights::EnableDefaultLighting(this); +} + + +// Fog settings. +void NormalMapEffect::SetFogStart(float value) +{ + pImpl->fog.start = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::FogVector; +} + + +void NormalMapEffect::SetFogEnd(float value) +{ + pImpl->fog.end = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::FogVector; +} + + +void XM_CALLCONV NormalMapEffect::SetFogColor(FXMVECTOR value) +{ + pImpl->constants.fogColor = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; +} + + +// Texture settings. +void NormalMapEffect::SetTexture(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor, D3D12_GPU_DESCRIPTOR_HANDLE samplerDescriptor) +{ + pImpl->texture = srvDescriptor; + pImpl->sampler = samplerDescriptor; +} + + +void NormalMapEffect::SetNormalTexture(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor) +{ + pImpl->normal = srvDescriptor; +} + + +void NormalMapEffect::SetSpecularTexture(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor) +{ + if (!pImpl->specularMap) + { + DebugTrace("WARNING: Specular texture set on NormalMapEffect instance created without specular shader (texture %llu)\n", srvDescriptor.ptr); + } + + pImpl->specular = srvDescriptor; +} + + +//-------------------------------------------------------------------------------------- +// SkinnedNormalMapEffect +//-------------------------------------------------------------------------------------- + +// Animation settings. +void SkinnedNormalMapEffect::SetBoneTransforms(_In_reads_(count) XMMATRIX const* value, size_t count) +{ + if (count > MaxBones) + throw std::invalid_argument("count parameter exceeds MaxBones"); + + auto boneConstant = pImpl->boneConstants.Bones; + + for (size_t i = 0; i < count; i++) + { + #if DIRECTX_MATH_VERSION >= 313 + XMStoreFloat3x4A(reinterpret_cast(&boneConstant[i]), value[i]); + #else + // Xbox One XDK has an older version of DirectXMath + XMMATRIX boneMatrix = XMMatrixTranspose(value[i]); + + boneConstant[i][0] = boneMatrix.r[0]; + boneConstant[i][1] = boneMatrix.r[1]; + boneConstant[i][2] = boneMatrix.r[2]; + #endif + } + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBufferBones; +} + + +void SkinnedNormalMapEffect::ResetBoneTransforms() +{ + auto boneConstant = pImpl->boneConstants.Bones; + + for (size_t i = 0; i < MaxBones; ++i) + { + boneConstant[i][0] = g_XMIdentityR0; + boneConstant[i][1] = g_XMIdentityR1; + boneConstant[i][2] = g_XMIdentityR2; + } + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBufferBones; +} diff --git a/Common/DirectXTK12/Src/PBREffect.cpp b/Common/DirectXTK12/Src/PBREffect.cpp new file mode 100644 index 0000000..c37c1d1 --- /dev/null +++ b/Common/DirectXTK12/Src/PBREffect.cpp @@ -0,0 +1,906 @@ +//-------------------------------------------------------------------------------------- +// File: PBREffect.cpp +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "pch.h" +#include "EffectCommon.h" + +namespace DirectX +{ + namespace EffectDirtyFlags + { + constexpr int ConstantBufferBones = 0x100000; + } +} + +using namespace DirectX; + +namespace +{ + // Constant buffer layout. Must match the shader! + struct PBREffectConstants + { + XMVECTOR eyePosition; + XMMATRIX world; + XMVECTOR worldInverseTranspose[3]; + XMMATRIX worldViewProj; + XMMATRIX prevWorldViewProj; // for velocity generation + + XMVECTOR lightDirection[IEffectLights::MaxDirectionalLights]; + XMVECTOR lightDiffuseColor[IEffectLights::MaxDirectionalLights]; + + // PBR Parameters + XMVECTOR Albedo; + float Metallic; + float Roughness; + int numRadianceMipLevels; + + // Size of render target + float targetWidth; + float targetHeight; + }; + + static_assert((sizeof(PBREffectConstants) % 16) == 0, "CB size not padded correctly"); + + XM_ALIGNED_STRUCT(16) BoneConstants + { + XMVECTOR Bones[SkinnedPBREffect::MaxBones][3]; + }; + + static_assert((sizeof(BoneConstants) % 16) == 0, "CB size not padded correctly"); + + + // Traits type describes our characteristics to the EffectBase template. + struct PBREffectTraits + { + using ConstantBufferType = PBREffectConstants; + + static constexpr int VertexShaderCount = 8; + static constexpr int PixelShaderCount = 5; + static constexpr int ShaderPermutationCount = 22; + static constexpr int RootSignatureCount = 1; + }; +} + +// Internal PBREffect implementation class. +class PBREffect::Impl : public EffectBase +{ +public: + explicit Impl(_In_ ID3D12Device* device); + + void Initialize( + _In_ ID3D12Device* device, + uint32_t effectFlags, + const EffectPipelineStateDescription& pipelineDescription, + bool enableSkinning); + + enum RootParameterIndex + { + AlbedoTexture, + NormalTexture, + RMATexture, + EmissiveTexture, + RadianceTexture, + IrradianceTexture, + SurfaceSampler, + RadianceSampler, + ConstantBuffer, + ConstantBufferBones, + RootParametersCount + }; + + int weightsPerVertex; + bool textureEnabled; + bool emissiveMap; + + D3D12_GPU_DESCRIPTOR_HANDLE descriptors[RootParametersCount]; + + XMVECTOR lightColor[MaxDirectionalLights]; + + void Apply(_In_ ID3D12GraphicsCommandList* commandList); + + int GetPipelineStatePermutation(uint32_t effectFlags) const noexcept; + + BoneConstants boneConstants; + +private: + GraphicsResource mBones; +}; + + +#pragma region Shaders +// Include the precompiled shader code. +namespace +{ +#ifdef _GAMING_XBOX_SCARLETT +#include "XboxGamingScarlettPBREffect_VSConstant.inc" +#include "XboxGamingScarlettPBREffect_VSConstantBn.inc" + +#include "XboxGamingScarlettPBREffect_VSConstantInst.inc" +#include "XboxGamingScarlettPBREffect_VSConstantBnInst.inc" + +#include "XboxGamingScarlettPBREffect_VSConstantVelocity.inc" +#include "XboxGamingScarlettPBREffect_VSConstantVelocityBn.inc" + +#include "XboxGamingScarlettPBREffect_VSSkinned.inc" +#include "XboxGamingScarlettPBREffect_VSSkinnedBn.inc" + +#include "XboxGamingScarlettPBREffect_PSConstant.inc" +#include "XboxGamingScarlettPBREffect_PSTextured.inc" +#include "XboxGamingScarlettPBREffect_PSTexturedEmissive.inc" +#include "XboxGamingScarlettPBREffect_PSTexturedVelocity.inc" +#include "XboxGamingScarlettPBREffect_PSTexturedEmissiveVelocity.inc" +#elif defined(_GAMING_XBOX) +#include "XboxGamingXboxOnePBREffect_VSConstant.inc" +#include "XboxGamingXboxOnePBREffect_VSConstantBn.inc" + +#include "XboxGamingXboxOnePBREffect_VSConstantInst.inc" +#include "XboxGamingXboxOnePBREffect_VSConstantBnInst.inc" + +#include "XboxGamingXboxOnePBREffect_VSConstantVelocity.inc" +#include "XboxGamingXboxOnePBREffect_VSConstantVelocityBn.inc" + +#include "XboxGamingXboxOnePBREffect_VSSkinned.inc" +#include "XboxGamingXboxOnePBREffect_VSSkinnedBn.inc" + +#include "XboxGamingXboxOnePBREffect_PSConstant.inc" +#include "XboxGamingXboxOnePBREffect_PSTextured.inc" +#include "XboxGamingXboxOnePBREffect_PSTexturedEmissive.inc" +#include "XboxGamingXboxOnePBREffect_PSTexturedVelocity.inc" +#include "XboxGamingXboxOnePBREffect_PSTexturedEmissiveVelocity.inc" +#elif defined(_XBOX_ONE) && defined(_TITLE) +#include "XboxOnePBREffect_VSConstant.inc" +#include "XboxOnePBREffect_VSConstantBn.inc" + +#include "XboxOnePBREffect_VSConstantInst.inc" +#include "XboxOnePBREffect_VSConstantBnInst.inc" + +#include "XboxOnePBREffect_VSConstantVelocity.inc" +#include "XboxOnePBREffect_VSConstantVelocityBn.inc" + +#include "XboxOnePBREffect_VSSkinned.inc" +#include "XboxOnePBREffect_VSSkinnedBn.inc" + +#include "XboxOnePBREffect_PSConstant.inc" +#include "XboxOnePBREffect_PSTextured.inc" +#include "XboxOnePBREffect_PSTexturedEmissive.inc" +#include "XboxOnePBREffect_PSTexturedVelocity.inc" +#include "XboxOnePBREffect_PSTexturedEmissiveVelocity.inc" +#else +#include "PBREffect_VSConstant.inc" +#include "PBREffect_VSConstantBn.inc" + +#include "PBREffect_VSConstantInst.inc" +#include "PBREffect_VSConstantBnInst.inc" + +#include "PBREffect_VSConstantVelocity.inc" +#include "PBREffect_VSConstantVelocityBn.inc" + +#include "PBREffect_VSSkinned.inc" +#include "PBREffect_VSSkinnedBn.inc" + +#include "PBREffect_PSConstant.inc" +#include "PBREffect_PSTextured.inc" +#include "PBREffect_PSTexturedEmissive.inc" +#include "PBREffect_PSTexturedVelocity.inc" +#include "PBREffect_PSTexturedEmissiveVelocity.inc" +#endif +} + + +template<> +const D3D12_SHADER_BYTECODE EffectBase::VertexShaderBytecode[] = +{ + { PBREffect_VSConstant, sizeof(PBREffect_VSConstant) }, + { PBREffect_VSConstantVelocity, sizeof(PBREffect_VSConstantVelocity) }, + { PBREffect_VSConstantBn, sizeof(PBREffect_VSConstantBn) }, + { PBREffect_VSConstantVelocityBn, sizeof(PBREffect_VSConstantVelocityBn) }, + + { PBREffect_VSConstantInst, sizeof(PBREffect_VSConstantInst) }, + { PBREffect_VSConstantBnInst, sizeof(PBREffect_VSConstantBnInst) }, + + { PBREffect_VSSkinned, sizeof(PBREffect_VSSkinned) }, + { PBREffect_VSSkinnedBn, sizeof(PBREffect_VSSkinnedBn) }, +}; + + +template<> +const int EffectBase::VertexShaderIndices[] = +{ + 0, // constant + 0, // textured + 0, // textured + emissive + + 4, // instancing + constant + 4, // instancing + textured + 4, // instancing + textured + emissive + + 6, // skinning + constant + 6, // skinning + textured + 6, // skinning + textured + emissive + + 1, // textured + velocity + 1, // textured + emissive + velocity + + 2, // constant (biased vertex normals) + 2, // textured (biased vertex normals) + 2, // textured + emissive (biased vertex normals) + + 5, // instancing + constant (biased vertex normals) + 5, // instancing + textured (biased vertex normals) + 5, // instancing + textured + emissive (biased vertex normals) + + 7, // skinning + constant (biased vertex normals) + 7, // skinning + textured (biased vertex normals) + 7, // skinning + textured + emissive (biased vertex normals) + + 3, // textured + velocity (biased vertex normals) + 3, // textured + emissive + velocity (biasoed vertex normals) +}; + + +template<> +const D3D12_SHADER_BYTECODE EffectBase::PixelShaderBytecode[] = +{ + { PBREffect_PSConstant, sizeof(PBREffect_PSConstant) }, + { PBREffect_PSTextured, sizeof(PBREffect_PSTextured) }, + { PBREffect_PSTexturedEmissive, sizeof(PBREffect_PSTexturedEmissive) }, + { PBREffect_PSTexturedVelocity, sizeof(PBREffect_PSTexturedVelocity) }, + { PBREffect_PSTexturedEmissiveVelocity, sizeof(PBREffect_PSTexturedEmissiveVelocity) }, +}; + + +template<> +const int EffectBase::PixelShaderIndices[] = +{ + 0, // constant + 1, // textured + 2, // textured + emissive + + 0, // instancing + constant + 1, // instancing + textured + 2, // instancing + textured + emissive + + 0, // skinning + constant + 1, // skinning + textured + 2, // skinning + textured + emissive + + 3, // textured + velocity + 4, // textured + emissive + velocity + + 0, // constant (biased vertex normals) + 1, // textured (biased vertex normals) + 2, // textured + emissive (biased vertex normals) + + 0, // instancing + constant (biased vertex normals) + 1, // instancing + textured (biased vertex normals) + 2, // instancing + textured + emissive (biased vertex normals) + + 0, // skinning + constant (biased vertex normals) + 1, // skinning + textured (biased vertex normals) + 2, // skinning + textured + emissive (biased vertex normals) + + 3, // textured + velocity (biased vertex normals) + 4, // textured + emissive + velocity (biased vertex normals) +}; +#pragma endregion + +// Global pool of per-device PBREffect resources. Required by EffectBase<>, but not used. +template<> +SharedResourcePool::DeviceResources> EffectBase::deviceResourcesPool = {}; + +// Constructor. +PBREffect::Impl::Impl(_In_ ID3D12Device* device) + : EffectBase(device), + weightsPerVertex(0), + textureEnabled(false), + emissiveMap(false), + descriptors{}, + lightColor{}, + boneConstants{} +{ + static_assert(static_cast(std::size(EffectBase::VertexShaderIndices)) == PBREffectTraits::ShaderPermutationCount, "array/max mismatch"); + static_assert(static_cast(std::size(EffectBase::VertexShaderBytecode)) == PBREffectTraits::VertexShaderCount, "array/max mismatch"); + static_assert(static_cast(std::size(EffectBase::PixelShaderBytecode)) == PBREffectTraits::PixelShaderCount, "array/max mismatch"); + static_assert(static_cast(std::size(EffectBase::PixelShaderIndices)) == PBREffectTraits::ShaderPermutationCount, "array/max mismatch"); +} + +void PBREffect::Impl::Initialize( + _In_ ID3D12Device* device, + uint32_t effectFlags, + const EffectPipelineStateDescription& pipelineDescription, + bool enableSkinning) +{ + emissiveMap = (effectFlags & EffectFlags::Emissive) != 0; + + // Lighting + static const XMVECTORF32 defaultLightDirection = { { { 0, -1, 0, 0 } } }; + for (int i = 0; i < MaxDirectionalLights; i++) + { + lightColor[i] = g_XMOne; + constants.lightDirection[i] = defaultLightDirection; + constants.lightDiffuseColor[i] = g_XMZero; + } + + if (effectFlags & EffectFlags::Texture) + { + textureEnabled = true; + } + else + { + textureEnabled = false; + + if (effectFlags & (EffectFlags::Emissive | EffectFlags::Velocity)) + { + DebugTrace("ERROR: PBREffect does not support emissive or velocity without surface textures\n"); + throw std::invalid_argument("Specified effects flags requires Texture"); + } + } + + // Default PBR values + constants.Albedo = g_XMOne; + constants.Metallic = 0.5f; + constants.Roughness = 0.2f; + constants.numRadianceMipLevels = 1; + + if (enableSkinning) + { + if (effectFlags & EffectFlags::Instancing) + { + DebugTrace("ERROR: SkinnedPBREffect does not implement EffectFlags::Instancing\n"); + throw std::invalid_argument("Instancing effect flag is invalid"); + } + else if (effectFlags & EffectFlags::Velocity) + { + DebugTrace("ERROR: SkinnedPBREffect does not implement EffectFlags::Velocity\n"); + throw std::invalid_argument("Velocity generation effect flag is invalid"); + } + + weightsPerVertex = 4; + + for (size_t j = 0; j < SkinnedPBREffect::MaxBones; ++j) + { + boneConstants.Bones[j][0] = g_XMIdentityR0; + boneConstants.Bones[j][1] = g_XMIdentityR1; + boneConstants.Bones[j][2] = g_XMIdentityR2; + } + } + + // Create root signature + { + ENUM_FLAGS_CONSTEXPR D3D12_ROOT_SIGNATURE_FLAGS rootSignatureFlags = + D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT + | D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS + | D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS + | D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS +#ifdef _GAMING_XBOX_SCARLETT + | D3D12_ROOT_SIGNATURE_FLAG_DENY_AMPLIFICATION_SHADER_ROOT_ACCESS + | D3D12_ROOT_SIGNATURE_FLAG_DENY_MESH_SHADER_ROOT_ACCESS +#endif + ; + + CD3DX12_ROOT_PARAMETER rootParameters[RootParametersCount] = {}; + CD3DX12_DESCRIPTOR_RANGE textureSRV[6] = { + CD3DX12_DESCRIPTOR_RANGE(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0), + CD3DX12_DESCRIPTOR_RANGE(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 1), + CD3DX12_DESCRIPTOR_RANGE(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 2), + CD3DX12_DESCRIPTOR_RANGE(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 3), + CD3DX12_DESCRIPTOR_RANGE(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 4), + CD3DX12_DESCRIPTOR_RANGE(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 5), + }; + + CD3DX12_DESCRIPTOR_RANGE textureSampler[2] = { + CD3DX12_DESCRIPTOR_RANGE(D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, 1, 0), + CD3DX12_DESCRIPTOR_RANGE(D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, 1, 1) + }; + + for (size_t i = 0; i < std::size(textureSRV); i++) + { + rootParameters[i].InitAsDescriptorTable(1, &textureSRV[i]); + } + + for (size_t i = 0; i < std::size(textureSampler); i++) + { + rootParameters[i + SurfaceSampler].InitAsDescriptorTable(1, &textureSampler[i]); + } + + rootParameters[ConstantBuffer].InitAsConstantBufferView(0, 0, D3D12_SHADER_VISIBILITY_ALL); + rootParameters[RootParameterIndex::ConstantBufferBones].InitAsConstantBufferView(1, 0, D3D12_SHADER_VISIBILITY_VERTEX); + + CD3DX12_ROOT_SIGNATURE_DESC rsigDesc; + rsigDesc.Init(static_cast(std::size(rootParameters)), rootParameters, 0, nullptr, rootSignatureFlags); + + mRootSignature = GetRootSignature(0, rsigDesc); + } + + assert(mRootSignature != nullptr); + + if (effectFlags & EffectFlags::Fog) + { + DebugTrace("ERROR: PBEffect does not implement EffectFlags::Fog\n"); + throw std::invalid_argument("Fog effect flag is invalid"); + } + else if (effectFlags & EffectFlags::VertexColor) + { + DebugTrace("ERROR: PBEffect does not implement EffectFlags::VertexColor\n"); + throw std::invalid_argument("VertexColor effect flag is invalid"); + } + else if ((effectFlags & (EffectFlags::Velocity | EffectFlags::Instancing)) == (EffectFlags::Velocity | EffectFlags::Instancing)) + { + DebugTrace("ERROR: PBEffect cannot use Instancing and Velocity at the same time.\n"); + throw std::invalid_argument("Velocity effect flag is invalid"); + } + + // Create pipeline state. + const int sp = GetPipelineStatePermutation(effectFlags); + assert(sp >= 0 && sp < PBREffectTraits::ShaderPermutationCount); + _Analysis_assume_(sp >= 0 && sp < PBREffectTraits::ShaderPermutationCount); + + const int vi = EffectBase::VertexShaderIndices[sp]; + assert(vi >= 0 && vi < PBREffectTraits::VertexShaderCount); + _Analysis_assume_(vi >= 0 && vi < PBREffectTraits::VertexShaderCount); + const int pi = EffectBase::PixelShaderIndices[sp]; + assert(pi >= 0 && pi < PBREffectTraits::PixelShaderCount); + _Analysis_assume_(pi >= 0 && pi < PBREffectTraits::PixelShaderCount); + + pipelineDescription.CreatePipelineState( + device, + mRootSignature, + EffectBase::VertexShaderBytecode[vi], + EffectBase::PixelShaderBytecode[pi], + mPipelineState.ReleaseAndGetAddressOf()); + + if (enableSkinning) + { + SetDebugObjectName(mPipelineState.Get(), L"SkinnedPBREffect"); + } + else + { + SetDebugObjectName(mPipelineState.Get(), L"PBREffect"); + } +} + + +int PBREffect::Impl::GetPipelineStatePermutation(uint32_t effectFlags) const noexcept +{ + int permutation = 0; + + // Using an emissive texture? + if (emissiveMap) + { + permutation += 1; + } + + if (effectFlags & EffectFlags::BiasedVertexNormals) + { + // Compressed normals need to be scaled and biased in the vertex shader. + permutation += 11; + } + + if (weightsPerVertex > 0) + { + // Vertex skinning. + permutation += 6; + } + else if (effectFlags & EffectFlags::Instancing) + { + // Vertex shader needs to use vertex matrix transform. + permutation += 3; + } + else if (effectFlags & EffectFlags::Velocity) + { + // Optional velocity buffer (implies textured). + permutation += 9; + } + + if (textureEnabled && !(effectFlags & EffectFlags::Velocity)) + { + // Textured RMA vs. constant albedo/roughness/metalness. + permutation += 1; + } + + return permutation; +} + + +// Sets our state onto the D3D device. +void PBREffect::Impl::Apply(_In_ ID3D12GraphicsCommandList* commandList) +{ + // Store old wvp for velocity calculation in shader + constants.prevWorldViewProj = constants.worldViewProj; + + // Compute derived parameter values. + matrices.SetConstants(dirtyFlags, constants.worldViewProj); + + // World inverse transpose matrix. + if (dirtyFlags & EffectDirtyFlags::WorldInverseTranspose) + { + constants.world = XMMatrixTranspose(matrices.world); + + const XMMATRIX worldInverse = XMMatrixInverse(nullptr, matrices.world); + + constants.worldInverseTranspose[0] = worldInverse.r[0]; + constants.worldInverseTranspose[1] = worldInverse.r[1]; + constants.worldInverseTranspose[2] = worldInverse.r[2]; + + dirtyFlags &= ~EffectDirtyFlags::WorldInverseTranspose; + dirtyFlags |= EffectDirtyFlags::ConstantBuffer; + } + + // Eye position vector. + if (dirtyFlags & EffectDirtyFlags::EyePosition) + { + const XMMATRIX viewInverse = XMMatrixInverse(nullptr, matrices.view); + + constants.eyePosition = viewInverse.r[3]; + + dirtyFlags &= ~EffectDirtyFlags::EyePosition; + dirtyFlags |= EffectDirtyFlags::ConstantBuffer; + } + + // Set constants to GPU + UpdateConstants(); + + if (weightsPerVertex > 0) + { + if (dirtyFlags & EffectDirtyFlags::ConstantBufferBones) + { + mBones = GraphicsMemory::Get(GetDevice()).AllocateConstant(boneConstants); + dirtyFlags &= ~EffectDirtyFlags::ConstantBufferBones; + } + } + + // Set the root signature + commandList->SetGraphicsRootSignature(mRootSignature); + + if (!descriptors[RadianceTexture].ptr || !descriptors[RadianceSampler].ptr) + { + DebugTrace("ERROR: Missing radiance texture or sampler for PBREffect (texture %llu, sampler %llu)\n", descriptors[RadianceTexture].ptr, descriptors[RadianceSampler].ptr); + throw std::runtime_error("PBREffect"); + } + + if (!descriptors[IrradianceTexture].ptr) + { + DebugTrace("ERROR: Missing irradiance texture for PBREffect (texture %llu)\n", descriptors[IrradianceTexture].ptr); + throw std::runtime_error("PBREffect"); + } + + // Set the root parameters + if (!textureEnabled) + { + // only update radiance/irradiance texture and samplers + + // **NOTE** If D3D asserts or crashes here, you probably need to call commandList->SetDescriptorHeaps() with the required descriptor heaps. + commandList->SetGraphicsRootDescriptorTable(RadianceTexture, descriptors[RadianceTexture]); + commandList->SetGraphicsRootDescriptorTable(IrradianceTexture, descriptors[IrradianceTexture]); + commandList->SetGraphicsRootDescriptorTable(RadianceSampler, descriptors[RadianceSampler]); + + // Bind 'empty' textures to avoid warnings on PC + commandList->SetGraphicsRootDescriptorTable(AlbedoTexture, descriptors[RadianceTexture]); + commandList->SetGraphicsRootDescriptorTable(NormalTexture, descriptors[RadianceTexture]); + commandList->SetGraphicsRootDescriptorTable(RMATexture, descriptors[RadianceTexture]); + commandList->SetGraphicsRootDescriptorTable(EmissiveTexture, descriptors[RadianceTexture]); + commandList->SetGraphicsRootDescriptorTable(SurfaceSampler, descriptors[RadianceSampler]); + } + else + { + if (!descriptors[AlbedoTexture].ptr || !descriptors[SurfaceSampler].ptr) + { + DebugTrace("ERROR: Missing albedo texture or sampler for PBREffect (texture %llu, sampler %llu)\n", descriptors[AlbedoTexture].ptr, descriptors[SurfaceSampler].ptr); + throw std::runtime_error("PBREffect"); + } + + if (!descriptors[NormalTexture].ptr) + { + DebugTrace("ERROR: Missing normal map texture for PBREffect (texture %llu)\n", descriptors[NormalTexture].ptr); + throw std::runtime_error("PBREffect"); + } + + if (!descriptors[RMATexture].ptr) + { + DebugTrace("ERROR: Missing roughness/metalness texture for PBREffect (texture %llu)\n", descriptors[RMATexture].ptr); + throw std::runtime_error("PBREffect"); + } + + for (unsigned i = 0; i < ConstantBuffer; i++) + { + if (i == EmissiveTexture) + continue; + + // **NOTE** If D3D asserts or crashes here, you probably need to call commandList->SetDescriptorHeaps() with the required descriptor heaps. + commandList->SetGraphicsRootDescriptorTable(i, descriptors[i]); + } + + if (emissiveMap) + { + if (!descriptors[EmissiveTexture].ptr) + { + DebugTrace("ERROR: Missing emissive map texture for PBREffect (texture %llu)\n", descriptors[NormalTexture].ptr); + throw std::runtime_error("PBREffect"); + } + + commandList->SetGraphicsRootDescriptorTable(EmissiveTexture, descriptors[EmissiveTexture]); + } + else + { + // Bind 'empty' textures to avoid warnings on PC + commandList->SetGraphicsRootDescriptorTable(EmissiveTexture, descriptors[AlbedoTexture]); + } + + } + + // Set constants + auto const cbuffer = GetConstantBufferGpuAddress(); + commandList->SetGraphicsRootConstantBufferView(RootParameterIndex::ConstantBuffer, cbuffer); + commandList->SetGraphicsRootConstantBufferView(RootParameterIndex::ConstantBufferBones, + (weightsPerVertex > 0) ? mBones.GpuAddress() : cbuffer); + + // Set the pipeline state + commandList->SetPipelineState(EffectBase::mPipelineState.Get()); +} + + +//-------------------------------------------------------------------------------------- +// PBREffect +//-------------------------------------------------------------------------------------- + +PBREffect::PBREffect( + _In_ ID3D12Device* device, + uint32_t effectFlags, + const EffectPipelineStateDescription& pipelineDescription, + bool skinningEnabled) + : pImpl(std::make_unique(device)) +{ + pImpl->Initialize(device, effectFlags, pipelineDescription, skinningEnabled); +} + +PBREffect::PBREffect(PBREffect&&) noexcept = default; +PBREffect& PBREffect::operator= (PBREffect&&) noexcept = default; +PBREffect::~PBREffect() = default; + + +// IEffect methods. +void PBREffect::Apply(_In_ ID3D12GraphicsCommandList* commandList) +{ + pImpl->Apply(commandList); +} + + +// Camera settings. +void XM_CALLCONV PBREffect::SetWorld(FXMMATRIX value) +{ + pImpl->matrices.world = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::WorldViewProj | EffectDirtyFlags::WorldInverseTranspose; +} + + +void XM_CALLCONV PBREffect::SetView(FXMMATRIX value) +{ + pImpl->matrices.view = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::WorldViewProj | EffectDirtyFlags::EyePosition; +} + + +void XM_CALLCONV PBREffect::SetProjection(FXMMATRIX value) +{ + pImpl->matrices.projection = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::WorldViewProj; +} + + +void XM_CALLCONV PBREffect::SetMatrices(FXMMATRIX world, CXMMATRIX view, CXMMATRIX projection) +{ + pImpl->matrices.world = world; + pImpl->matrices.view = view; + pImpl->matrices.projection = projection; + + pImpl->dirtyFlags |= EffectDirtyFlags::WorldViewProj | EffectDirtyFlags::WorldInverseTranspose | EffectDirtyFlags::EyePosition; +} + + +// Light settings +void XM_CALLCONV PBREffect::SetAmbientLightColor(FXMVECTOR) +{ + // Unsupported interface. +} + + +void PBREffect::SetLightEnabled(int whichLight, bool value) +{ + EffectLights::ValidateLightIndex(whichLight); + + pImpl->constants.lightDiffuseColor[whichLight] = (value) ? pImpl->lightColor[whichLight] : g_XMZero; + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; +} + + +void XM_CALLCONV PBREffect::SetLightDirection(int whichLight, FXMVECTOR value) +{ + EffectLights::ValidateLightIndex(whichLight); + + pImpl->constants.lightDirection[whichLight] = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; +} + + +void XM_CALLCONV PBREffect::SetLightDiffuseColor(int whichLight, FXMVECTOR value) +{ + EffectLights::ValidateLightIndex(whichLight); + + pImpl->lightColor[whichLight] = value; + pImpl->constants.lightDiffuseColor[whichLight] = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; +} + + +void XM_CALLCONV PBREffect::SetLightSpecularColor(int, FXMVECTOR) +{ + // Unsupported interface. +} + + +void PBREffect::EnableDefaultLighting() +{ + EffectLights::EnableDefaultLighting(this); +} + + +// PBR Settings +void PBREffect::SetAlpha(float value) +{ + // Set w to new value, but preserve existing xyz (constant albedo). + pImpl->constants.Albedo = XMVectorSetW(pImpl->constants.Albedo, value); + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; +} + + +void PBREffect::SetConstantAlbedo(FXMVECTOR value) +{ + // Set xyz to new value, but preserve existing w (alpha). + pImpl->constants.Albedo = XMVectorSelect(pImpl->constants.Albedo, value, g_XMSelect1110); + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; +} + + +void PBREffect::SetConstantMetallic(float value) +{ + pImpl->constants.Metallic = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; +} + + +void PBREffect::SetConstantRoughness(float value) +{ + pImpl->constants.Roughness = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; +} + + +// Texture settings. +void PBREffect::SetAlbedoTexture(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor, D3D12_GPU_DESCRIPTOR_HANDLE samplerDescriptor) +{ + pImpl->descriptors[Impl::RootParameterIndex::AlbedoTexture] = srvDescriptor; + pImpl->descriptors[Impl::RootParameterIndex::SurfaceSampler] = samplerDescriptor; +} + + +void PBREffect::SetNormalTexture(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor) +{ + pImpl->descriptors[Impl::RootParameterIndex::NormalTexture] = srvDescriptor; +} + + +void PBREffect::SetRMATexture(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor) +{ + pImpl->descriptors[Impl::RootParameterIndex::RMATexture] = srvDescriptor; +} + + +void PBREffect::SetEmissiveTexture(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor) +{ + if (!pImpl->emissiveMap) + { + DebugTrace("WARNING: Emissive texture set on PBREffect instance created without emissive shader (texture %llu)\n", srvDescriptor.ptr); + } + + pImpl->descriptors[Impl::RootParameterIndex::EmissiveTexture] = srvDescriptor; +} + + +void PBREffect::SetSurfaceTextures( + D3D12_GPU_DESCRIPTOR_HANDLE albedo, + D3D12_GPU_DESCRIPTOR_HANDLE normal, + D3D12_GPU_DESCRIPTOR_HANDLE roughnessMetallicAmbientOcclusion, + D3D12_GPU_DESCRIPTOR_HANDLE sampler) +{ + pImpl->descriptors[Impl::RootParameterIndex::AlbedoTexture] = albedo; + pImpl->descriptors[Impl::RootParameterIndex::NormalTexture] = normal; + pImpl->descriptors[Impl::RootParameterIndex::RMATexture] = roughnessMetallicAmbientOcclusion; + pImpl->descriptors[Impl::RootParameterIndex::SurfaceSampler] = sampler; +} + + +void PBREffect::SetIBLTextures( + D3D12_GPU_DESCRIPTOR_HANDLE radiance, + int numRadianceMips, + D3D12_GPU_DESCRIPTOR_HANDLE irradiance, + D3D12_GPU_DESCRIPTOR_HANDLE sampler) +{ + pImpl->descriptors[Impl::RootParameterIndex::RadianceTexture] = radiance; + pImpl->descriptors[Impl::RootParameterIndex::RadianceSampler] = sampler; + pImpl->constants.numRadianceMipLevels = numRadianceMips; + + pImpl->descriptors[Impl::RootParameterIndex::IrradianceTexture] = irradiance; + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; +} + + +// Additional settings. +void PBREffect::SetRenderTargetSizeInPixels(int width, int height) +{ + pImpl->constants.targetWidth = static_cast(width); + pImpl->constants.targetHeight = static_cast(height); + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; +} + + +//-------------------------------------------------------------------------------------- +// SkinnedPBREffect +//-------------------------------------------------------------------------------------- + +// Animation settings. +void SkinnedPBREffect::SetBoneTransforms(_In_reads_(count) XMMATRIX const* value, size_t count) +{ + if (count > MaxBones) + throw std::invalid_argument("count parameter exceeds MaxBones"); + + auto boneConstant = pImpl->boneConstants.Bones; + + for (size_t i = 0; i < count; i++) + { + #if DIRECTX_MATH_VERSION >= 313 + XMStoreFloat3x4A(reinterpret_cast(&boneConstant[i]), value[i]); + #else + // Xbox One XDK has an older version of DirectXMath + XMMATRIX boneMatrix = XMMatrixTranspose(value[i]); + + boneConstant[i][0] = boneMatrix.r[0]; + boneConstant[i][1] = boneMatrix.r[1]; + boneConstant[i][2] = boneMatrix.r[2]; + #endif + } + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBufferBones; +} + + +void SkinnedPBREffect::ResetBoneTransforms() +{ + auto boneConstant = pImpl->boneConstants.Bones; + + for (size_t i = 0; i < MaxBones; ++i) + { + boneConstant[i][0] = g_XMIdentityR0; + boneConstant[i][1] = g_XMIdentityR1; + boneConstant[i][2] = g_XMIdentityR2; + } + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBufferBones; +} diff --git a/Common/DirectXTK12/Src/PBREffectFactory.cpp b/Common/DirectXTK12/Src/PBREffectFactory.cpp new file mode 100644 index 0000000..734a13b --- /dev/null +++ b/Common/DirectXTK12/Src/PBREffectFactory.cpp @@ -0,0 +1,321 @@ +//-------------------------------------------------------------------------------------- +// File: PBREffectFactory.cpp +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "pch.h" +#include "Effects.h" +#include "CommonStates.h" +#include "DirectXHelpers.h" +#include "PlatformHelpers.h" +#include "DescriptorHeap.h" + +#include + + +using namespace DirectX; +using Microsoft::WRL::ComPtr; + +namespace +{ + template + void SetPBRProperties( + _In_ T* effect, + const EffectFactory::EffectInfo& info, + _In_ const DescriptorHeap* textures, + int textureDescriptorOffset, + _In_ const DescriptorHeap* samplers, + int samplerDescriptorOffset) + { + // We don't use EnableDefaultLighting generally for PBR as it uses Image-Based Lighting instead. + + effect->SetAlpha(info.alphaValue); + + if (info.diffuseTextureIndex != -1) + { + // Textured PBR material + const int albedoTextureIndex = info.diffuseTextureIndex + textureDescriptorOffset; + const int rmaTextureIndex = (info.specularTextureIndex != -1) ? info.specularTextureIndex + textureDescriptorOffset : -1; + const int normalTextureIndex = (info.normalTextureIndex != -1) ? info.normalTextureIndex + textureDescriptorOffset : -1; + const int samplerIndex = (info.samplerIndex != -1) ? info.samplerIndex + samplerDescriptorOffset : -1; + + effect->SetSurfaceTextures( + textures->GetGpuHandle(static_cast(albedoTextureIndex)), + textures->GetGpuHandle(static_cast(normalTextureIndex)), + textures->GetGpuHandle(static_cast(rmaTextureIndex)), + samplers->GetGpuHandle(static_cast(samplerIndex))); + + const int emissiveTextureIndex = (info.emissiveTextureIndex != -1) ? info.emissiveTextureIndex + textureDescriptorOffset : -1; + + if (emissiveTextureIndex != -1) + { + effect->SetEmissiveTexture(textures->GetGpuHandle(static_cast(emissiveTextureIndex))); + } + } + else + { + // Untextured material (for PBR this still requires texture coordinates) + const XMVECTOR color = XMLoadFloat3(&info.diffuseColor); + effect->SetConstantAlbedo(color); + + if (info.specularColor.x != 0 || info.specularColor.y != 0 || info.specularColor.z != 0) + { + // Derived from specularPower = 2 / roughness ^ 4 - 2 + // http://graphicrants.blogspot.com/2013/08/specular-brdf-reference.html + + const float roughness = powf(2.f / (info.specularPower + 2.f), 1.f / 4.f); + effect->SetConstantRoughness(roughness); + } + + // info.ambientColor, info.specularColor, and info.emissiveColor are unused by PBR. + } + } +} + +// Internal PBREffectFactory implementation class. Only one of these helpers is allocated +// per D3D device, even if there are multiple public facing PBREffectFactory instances. +class PBREffectFactory::Impl +{ +public: + Impl(_In_ ID3D12Device* device, _In_ ID3D12DescriptorHeap* textureDescriptors, _In_ ID3D12DescriptorHeap* samplerDescriptors) noexcept(false) + : mSharing(true) + , mEnableInstancing(false) + , mTextureDescriptors(nullptr) + , mSamplerDescriptors(nullptr) + , mDevice(device) + { + if (textureDescriptors) + mTextureDescriptors = std::make_unique(textureDescriptors); + if (samplerDescriptors) + mSamplerDescriptors = std::make_unique(samplerDescriptors); + } + + std::shared_ptr CreateEffect( + const EffectInfo& info, + const EffectPipelineStateDescription& opaquePipelineState, + const EffectPipelineStateDescription& alphaPipelineState, + const D3D12_INPUT_LAYOUT_DESC& inputLayout, + int textureDescriptorOffset, + int samplerDescriptorOffset); + + void ReleaseCache(); + + bool mSharing; + bool mEnableInstancing; + + std::unique_ptr mTextureDescriptors; + std::unique_ptr mSamplerDescriptors; + +private: + ComPtr mDevice; + + using EffectCache = std::map< std::wstring, std::shared_ptr >; + + EffectCache mEffectCache; + EffectCache mEffectCacheSkinning; + + std::mutex mutex; +}; + + +std::shared_ptr PBREffectFactory::Impl::CreateEffect( + const EffectInfo& info, + const EffectPipelineStateDescription& opaquePipelineState, + const EffectPipelineStateDescription& alphaPipelineState, + const D3D12_INPUT_LAYOUT_DESC& inputLayoutDesc, + int textureDescriptorOffset, + int samplerDescriptorOffset) +{ + if (!mTextureDescriptors) + { + DebugTrace("ERROR: PBREffectFactory created without texture descriptor heap!\n"); + throw std::logic_error("PBREffectFactory"); + } + if (!mSamplerDescriptors) + { + DebugTrace("ERROR: PBREffectFactory created without sampler descriptor heap!\n"); + throw std::logic_error("PBREffectFactory"); + } + + // Modify base pipeline state + EffectPipelineStateDescription derivedPSD = (info.alphaValue < 1.0f) ? alphaPipelineState : opaquePipelineState; + derivedPSD.inputLayout = inputLayoutDesc; + + // set effect flags for creation + uint32_t effectflags = (info.diffuseTextureIndex != -1) ? EffectFlags::Texture : EffectFlags::None; + + if (info.biasedVertexNormals) + { + effectflags |= EffectFlags::BiasedVertexNormals; + } + + if (info.emissiveTextureIndex != -1) + { + effectflags |= EffectFlags::Emissive; + } + + // info.perVertexColor and info.enableDualTexture are ignored by PBREffectFactory + + if (info.enableSkinning) + { + // SkinnedPBREffect + std::wstring cacheName; + if (mSharing && !info.name.empty()) + { + const uint32_t hash = derivedPSD.ComputeHash(); + cacheName = std::to_wstring(effectflags) + info.name + std::to_wstring(hash); + + auto it = mEffectCacheSkinning.find(cacheName); + if (mSharing && it != mEffectCacheSkinning.end()) + { + return it->second; + } + } + + auto effect = std::make_shared(mDevice.Get(), effectflags, derivedPSD); + + SetPBRProperties(effect.get(), info, + mTextureDescriptors.get(), textureDescriptorOffset, + mSamplerDescriptors.get(), samplerDescriptorOffset); + + if (mSharing && !info.name.empty()) + { + std::lock_guard lock(mutex); + EffectCache::value_type v(cacheName, effect); + mEffectCacheSkinning.insert(v); + } + + return std::move(effect); + } + else + { + // PBREffect + if (mEnableInstancing) + { + effectflags |= EffectFlags::Instancing; + } + + std::wstring cacheName; + if (mSharing && !info.name.empty()) + { + const uint32_t hash = derivedPSD.ComputeHash(); + cacheName = std::to_wstring(effectflags) + info.name + std::to_wstring(hash); + + auto it = mEffectCache.find(cacheName); + if (mSharing && it != mEffectCache.end()) + { + return it->second; + } + } + + auto effect = std::make_shared(mDevice.Get(), effectflags, derivedPSD); + + SetPBRProperties(effect.get(), info, + mTextureDescriptors.get(), textureDescriptorOffset, + mSamplerDescriptors.get(), samplerDescriptorOffset); + + if (mSharing && !info.name.empty()) + { + std::lock_guard lock(mutex); + EffectCache::value_type v(cacheName, effect); + mEffectCache.insert(v); + } + + return std::move(effect); + } +} + +void PBREffectFactory::Impl::ReleaseCache() +{ + std::lock_guard lock(mutex); + mEffectCache.clear(); + mEffectCacheSkinning.clear(); +} + + +//-------------------------------------------------------------------------------------- +// PBREffectFactory +//-------------------------------------------------------------------------------------- + +PBREffectFactory::PBREffectFactory(_In_ ID3D12Device* device) noexcept(false) : + pImpl(std::make_shared(device, nullptr, nullptr)) +{ +} + +PBREffectFactory::PBREffectFactory(_In_ ID3D12DescriptorHeap* textureDescriptors, _In_ ID3D12DescriptorHeap* samplerDescriptors) noexcept(false) +{ + if (!textureDescriptors) + { + throw std::invalid_argument("Texture descriptor heap cannot be null if no device is provided. Use the alternative PBREffectFactory constructor instead."); + } + if (!samplerDescriptors) + { + throw std::invalid_argument("Descriptor heap cannot be null if no device is provided. Use the alternative PBREffectFactory constructor instead."); + } + +#if defined(_MSC_VER) || !defined(_WIN32) + const D3D12_DESCRIPTOR_HEAP_TYPE textureHeapType = textureDescriptors->GetDesc().Type; + const D3D12_DESCRIPTOR_HEAP_TYPE samplerHeapType = samplerDescriptors->GetDesc().Type; +#else + D3D12_DESCRIPTOR_HEAP_DESC tmpDesc1, tmpDesc2; + const D3D12_DESCRIPTOR_HEAP_TYPE textureHeapType = textureDescriptors->GetDesc(&tmpDesc1)->Type; + const D3D12_DESCRIPTOR_HEAP_TYPE samplerHeapType = samplerDescriptors->GetDesc(&tmpDesc2)->Type; +#endif + + if (textureHeapType != D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV) + { + throw std::invalid_argument("PBREffectFactory::CreateEffect requires a CBV_SRV_UAV descriptor heap for textureDescriptors."); + } + if (samplerHeapType != D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER) + { + throw std::invalid_argument("PBREffectFactory::CreateEffect requires a SAMPLER descriptor heap for samplerDescriptors."); + } + + ComPtr device; +#if (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) + textureDescriptors->GetDevice(IID_GRAPHICS_PPV_ARGS(device.GetAddressOf())); +#else + HRESULT hr = textureDescriptors->GetDevice(IID_PPV_ARGS(device.GetAddressOf())); + ThrowIfFailed(hr); +#endif + + pImpl = std::make_shared(device.Get(), textureDescriptors, samplerDescriptors); +} + +PBREffectFactory::PBREffectFactory(PBREffectFactory&&) noexcept = default; +PBREffectFactory& PBREffectFactory::operator= (PBREffectFactory&&) noexcept = default; +PBREffectFactory::~PBREffectFactory() = default; + + +std::shared_ptr PBREffectFactory::CreateEffect( + const EffectInfo& info, + const EffectPipelineStateDescription& opaquePipelineState, + const EffectPipelineStateDescription& alphaPipelineState, + const D3D12_INPUT_LAYOUT_DESC& inputLayout, + int textureDescriptorOffset, + int samplerDescriptorOffset) +{ + return pImpl->CreateEffect(info, opaquePipelineState, alphaPipelineState, inputLayout, textureDescriptorOffset, samplerDescriptorOffset); +} + + +void PBREffectFactory::ReleaseCache() +{ + pImpl->ReleaseCache(); +} + + +// Properties. +void PBREffectFactory::SetSharing(bool enabled) noexcept +{ + pImpl->mSharing = enabled; +} + +void PBREffectFactory::EnableInstancing(bool enabled) noexcept +{ + pImpl->mEnableInstancing = enabled; +} diff --git a/Common/DirectXTK12/Src/PlatformHelpers.h b/Common/DirectXTK12/Src/PlatformHelpers.h new file mode 100644 index 0000000..15bba6c --- /dev/null +++ b/Common/DirectXTK12/Src/PlatformHelpers.h @@ -0,0 +1,92 @@ +//-------------------------------------------------------------------------------------- +// File: PlatformHelpers.h +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248929 +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#pragma once + +#pragma warning(disable : 4324) + +#include +#include + +#ifndef MAKEFOURCC +#define MAKEFOURCC(ch0, ch1, ch2, ch3) \ + (static_cast(static_cast(ch0)) \ + | (static_cast(static_cast(ch1)) << 8) \ + | (static_cast(static_cast(ch2)) << 16) \ + | (static_cast(static_cast(ch3)) << 24)) +#endif /* defined(MAKEFOURCC) */ + +// See https://walbourn.github.io/modern-c++-bitmask-types/ +#ifndef ENUM_FLAGS_CONSTEXPR +#if defined(NTDDI_WIN10_RS1) && !defined(__MINGW32__) +#define ENUM_FLAGS_CONSTEXPR constexpr +#else +#define ENUM_FLAGS_CONSTEXPR const +#endif +#endif + +namespace DirectX +{ + // Helper class for COM exceptions + class com_exception : public std::exception + { + public: + com_exception(HRESULT hr) noexcept : result(hr) {} + + const char* what() const noexcept override + { + static char s_str[64] = {}; + sprintf_s(s_str, "Failure with HRESULT of %08X", static_cast(result)); + return s_str; + } + + HRESULT get_result() const noexcept { return result; } + + private: + HRESULT result; + }; + + // Helper utility converts D3D API failures into exceptions. + inline void ThrowIfFailed(HRESULT hr) noexcept(false) + { + if (FAILED(hr)) + { + throw com_exception(hr); + } + } + + + // Helper for output debug tracing + inline void DebugTrace(_In_z_ _Printf_format_string_ const char* format, ...) noexcept + { + #ifdef _DEBUG + va_list args; + va_start(args, format); + + char buff[1024] = {}; + vsprintf_s(buff, format, args); + OutputDebugStringA(buff); + va_end(args); + #else + UNREFERENCED_PARAMETER(format); + #endif + } + + // Helper smart-pointers +#if (_WIN32_WINNT >= _WIN32_WINNT_WIN10) || (defined(_XBOX_ONE) && defined(_TITLE)) || !defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP) + struct virtual_deleter { void operator()(void* p) noexcept { if (p) VirtualFree(p, 0, MEM_RELEASE); } }; +#endif + + struct handle_closer { void operator()(HANDLE h) noexcept { if (h) CloseHandle(h); } }; + + using ScopedHandle = std::unique_ptr; + + inline HANDLE safe_handle(HANDLE h) noexcept { return (h == INVALID_HANDLE_VALUE) ? nullptr : h; } +} diff --git a/Common/DirectXTK12/Src/PrimitiveBatch.cpp b/Common/DirectXTK12/Src/PrimitiveBatch.cpp new file mode 100644 index 0000000..60cdc34 --- /dev/null +++ b/Common/DirectXTK12/Src/PrimitiveBatch.cpp @@ -0,0 +1,274 @@ +//-------------------------------------------------------------------------------------- +// File: PrimitiveBatch.cpp +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "pch.h" +#include "PrimitiveBatch.h" +#include "DirectXHelpers.h" +#include "PlatformHelpers.h" +#include "GraphicsMemory.h" + +using namespace DirectX; +using namespace DirectX::DX12::Private; +using Microsoft::WRL::ComPtr; + + +// Internal PrimitiveBatch implementation class. +class PrimitiveBatchBase::Impl +{ +public: + Impl(_In_ ID3D12Device* device, size_t maxIndices, size_t maxVertices, size_t vertexSize); + + void Begin(_In_ ID3D12GraphicsCommandList* cmdList); + void End(); + + void Draw(D3D_PRIMITIVE_TOPOLOGY topology, bool isIndexed, _In_opt_count_(indexCount) uint16_t const* indices, size_t indexCount, size_t vertexCount, _Outptr_ void** pMappedVertices); + +private: + void FlushBatch(); + + GraphicsResource mVertexSegment; + GraphicsResource mIndexSegment; + + ComPtr mDevice; + ComPtr mCommandList; + + size_t mMaxIndices; + size_t mMaxVertices; + size_t mVertexSize; + size_t mVertexPageSize; + size_t mIndexPageSize; + + D3D_PRIMITIVE_TOPOLOGY mCurrentTopology; + bool mInBeginEndPair; + bool mCurrentlyIndexed; + + size_t mIndexCount; + size_t mVertexCount; + + size_t mBaseIndex; + size_t mBaseVertex; +}; + + +// Constructor. +PrimitiveBatchBase::Impl::Impl(_In_ ID3D12Device* device, size_t maxIndices, size_t maxVertices, size_t vertexSize) + : mDevice(device), + mCommandList(nullptr), + mMaxIndices(maxIndices), + mMaxVertices(maxVertices), + mVertexSize(vertexSize), + mVertexPageSize(maxVertices * vertexSize), + mIndexPageSize(maxIndices * sizeof(uint16_t)), + mCurrentTopology(D3D_PRIMITIVE_TOPOLOGY_UNDEFINED), + mInBeginEndPair(false), + mCurrentlyIndexed(false), + mIndexCount(0), + mVertexCount(0), + mBaseIndex(0), + mBaseVertex(0) +{ + if (!maxVertices) + throw std::invalid_argument("maxVertices must be greater than 0"); + + if (vertexSize > D3D12_REQ_MULTI_ELEMENT_STRUCTURE_SIZE_IN_BYTES) + throw std::invalid_argument("Vertex size is too large for DirectX 12"); + + if ((uint64_t(maxIndices) * sizeof(uint16_t)) > uint64_t(D3D12_REQ_RESOURCE_SIZE_IN_MEGABYTES_EXPRESSION_A_TERM * 1024u * 1024u)) + throw std::invalid_argument("IB too large for DirectX 12"); + + if ((uint64_t(maxVertices) * uint64_t(vertexSize)) > uint64_t(D3D12_REQ_RESOURCE_SIZE_IN_MEGABYTES_EXPRESSION_A_TERM * 1024u * 1024u)) + throw std::invalid_argument("VB too large for DirectX 12"); +} + + +// Begins a batch of primitive drawing operations. + +void PrimitiveBatchBase::Impl::Begin(_In_ ID3D12GraphicsCommandList* cmdList) +{ + if (mInBeginEndPair) + throw std::logic_error("Cannot nest Begin calls"); + + mCommandList = cmdList; + mInBeginEndPair = true; +} + + +// Ends a batch of primitive drawing operations. +void PrimitiveBatchBase::Impl::End() +{ + if (!mInBeginEndPair) + throw std::logic_error("Begin must be called before End"); + + FlushBatch(); + + // Release our smart pointers and end the block + mIndexSegment.Reset(); + mVertexSegment.Reset(); + mCommandList.Reset(); + mInBeginEndPair = false; +} + + +// Can we combine adjacent primitives using this topology into a single draw call? +static bool CanBatchPrimitives(D3D_PRIMITIVE_TOPOLOGY topology) noexcept +{ + switch (topology) + { + case D3D_PRIMITIVE_TOPOLOGY_POINTLIST: + case D3D_PRIMITIVE_TOPOLOGY_LINELIST: + case D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST: + // Lists can easily be merged. + return true; + + default: + // Strips cannot. + return false; + } + + // We could also merge indexed strips by inserting degenerates, + // but that's not always a perf win, so let's keep things simple. +} + + +// Adds new geometry to the batch. +_Use_decl_annotations_ +void PrimitiveBatchBase::Impl::Draw(D3D_PRIMITIVE_TOPOLOGY topology, bool isIndexed, uint16_t const* indices, size_t indexCount, size_t vertexCount, void** pMappedVertices) +{ + if (isIndexed && !indices) + throw std::invalid_argument("Indices cannot be null"); + + if (indexCount >= mMaxIndices) + throw std::invalid_argument("Too many indices"); + + if (vertexCount >= mMaxVertices) + throw std::invalid_argument("Too many vertices"); + + if (!mInBeginEndPair) + throw std::logic_error("Begin must be called before Draw"); + + assert(pMappedVertices != nullptr); + + // Can we merge this primitive in with an existing batch, or must we flush first? + const bool wrapIndexBuffer = (mIndexCount + indexCount > mMaxIndices); + const bool wrapVertexBuffer = (mVertexCount + vertexCount > mMaxVertices); + + if ((topology != mCurrentTopology) || + (isIndexed != mCurrentlyIndexed) || + !CanBatchPrimitives(topology) || + wrapIndexBuffer || wrapVertexBuffer) + { + FlushBatch(); + } + + // If we are not already in a batch, lock the buffers. + if (mCurrentTopology == D3D_PRIMITIVE_TOPOLOGY_UNDEFINED) + { + mIndexCount = 0; + mVertexCount = 0; + mBaseIndex = 0; + mBaseVertex = 0; + mCurrentTopology = topology; + mCurrentlyIndexed = isIndexed; + + // Allocate a page for the primitive data + if (isIndexed) + { + mIndexSegment = GraphicsMemory::Get(mDevice.Get()).Allocate(mIndexPageSize, 16, GraphicsMemory::TAG_INDEX); + } + + mVertexSegment = GraphicsMemory::Get(mDevice.Get()).Allocate(mVertexPageSize, 16, GraphicsMemory::TAG_VERTEX); + } + + // Copy over the index data. + if (isIndexed) + { + auto outputIndices = static_cast(mIndexSegment.Memory()) + mIndexCount; + + for (size_t i = 0; i < indexCount; i++) + { + outputIndices[i] = static_cast(indices[i] + mVertexCount - mBaseIndex); + } + + mIndexCount += indexCount; + } + + // Return the output vertex data location. + *pMappedVertices = static_cast(mVertexSegment.Memory()) + mVertexSize * mVertexCount; + + mVertexCount += vertexCount; +} + + +// Sends queued primitives to the graphics device. +void PrimitiveBatchBase::Impl::FlushBatch() +{ + // Early out if there is nothing to flush. + if (mCurrentTopology == D3D_PRIMITIVE_TOPOLOGY_UNDEFINED) + return; + + mCommandList->IASetPrimitiveTopology(mCurrentTopology); + + // Set the vertex buffer view + D3D12_VERTEX_BUFFER_VIEW vbv; + vbv.BufferLocation = mVertexSegment.GpuAddress(); + vbv.SizeInBytes = static_cast(mVertexSize * (mVertexCount - mBaseVertex)); + vbv.StrideInBytes = static_cast(mVertexSize); + mCommandList->IASetVertexBuffers(0, 1, &vbv); + + if (mCurrentlyIndexed) + { + // Set the index buffer view + D3D12_INDEX_BUFFER_VIEW ibv; + ibv.BufferLocation = mIndexSegment.GpuAddress(); + ibv.Format = DXGI_FORMAT_R16_UINT; + ibv.SizeInBytes = static_cast(mIndexCount - mBaseIndex) * sizeof(uint16_t); + mCommandList->IASetIndexBuffer(&ibv); + + // Draw indexed geometry. + mCommandList->DrawIndexedInstanced(static_cast(mIndexCount - mBaseIndex), 1, 0, 0, 0); + } + else + { + // Draw non-indexed geometry. + mCommandList->DrawInstanced(static_cast(mVertexCount - mBaseVertex), 1, 0, 0); + } + + mCurrentTopology = D3D_PRIMITIVE_TOPOLOGY_UNDEFINED; +} + + +// Public constructor. +PrimitiveBatchBase::PrimitiveBatchBase(_In_ ID3D12Device* device, size_t maxIndices, size_t maxVertices, size_t vertexSize) + : pImpl(std::make_unique(device, maxIndices, maxVertices, vertexSize)) +{ +} + + +PrimitiveBatchBase::PrimitiveBatchBase(PrimitiveBatchBase&&) noexcept = default; +PrimitiveBatchBase& PrimitiveBatchBase::operator= (PrimitiveBatchBase&&) noexcept = default; +PrimitiveBatchBase::~PrimitiveBatchBase() = default; + + +void PrimitiveBatchBase::Begin(_In_ ID3D12GraphicsCommandList* cmdList) +{ + pImpl->Begin(cmdList); +} + + +void PrimitiveBatchBase::End() +{ + pImpl->End(); +} + + +_Use_decl_annotations_ +void PrimitiveBatchBase::Draw(D3D12_PRIMITIVE_TOPOLOGY topology, bool isIndexed, uint16_t const* indices, size_t indexCount, size_t vertexCount, void** pMappedVertices) +{ + pImpl->Draw(topology, isIndexed, indices, indexCount, vertexCount, pMappedVertices); +} diff --git a/Common/DirectXTK12/Src/ResourceUploadBatch.cpp b/Common/DirectXTK12/Src/ResourceUploadBatch.cpp new file mode 100644 index 0000000..519f8e9 --- /dev/null +++ b/Common/DirectXTK12/Src/ResourceUploadBatch.cpp @@ -0,0 +1,1129 @@ +//-------------------------------------------------------------------------------------- +// File: ResourceUploadBatch.cpp +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "pch.h" +#include "ResourceUploadBatch.h" + +#include "DirectXHelpers.h" +#include "LoaderHelpers.h" +#include "PlatformHelpers.h" + +using namespace DirectX; +using Microsoft::WRL::ComPtr; + +#ifdef __MINGW32__ +const GUID IID_ID3D12Device = { 0x189819f1, 0x1db6, 0x4b57, { 0xbe, 0x54, 0x18, 0x21, 0x33, 0x9b, 0x85, 0xf7 } }; +#endif + +// Include the precompiled shader code. +namespace +{ +#ifdef _GAMING_XBOX_SCARLETT +#include "XboxGamingScarlettGenerateMips_main.inc" +#elif defined(_GAMING_XBOX) +#include "XboxGamingXboxOneGenerateMips_main.inc" +#elif defined(_XBOX_ONE) && defined(_TITLE) +#include "XboxOneGenerateMips_main.inc" +#else +#include "GenerateMips_main.inc" +#endif + + bool FormatIsUAVCompatible(_In_ ID3D12Device* device, bool typedUAVLoadAdditionalFormats, DXGI_FORMAT format) noexcept + { + switch (format) + { + case DXGI_FORMAT_R32_FLOAT: + case DXGI_FORMAT_R32_UINT: + case DXGI_FORMAT_R32_SINT: + // Unconditionally supported. + return true; + + case DXGI_FORMAT_R32G32B32A32_FLOAT: + case DXGI_FORMAT_R32G32B32A32_UINT: + case DXGI_FORMAT_R32G32B32A32_SINT: + case DXGI_FORMAT_R16G16B16A16_FLOAT: + case DXGI_FORMAT_R16G16B16A16_UINT: + case DXGI_FORMAT_R16G16B16A16_SINT: + case DXGI_FORMAT_R8G8B8A8_UNORM: + case DXGI_FORMAT_R8G8B8A8_UINT: + case DXGI_FORMAT_R8G8B8A8_SINT: + case DXGI_FORMAT_R16_FLOAT: + case DXGI_FORMAT_R16_UINT: + case DXGI_FORMAT_R16_SINT: + case DXGI_FORMAT_R8_UNORM: + case DXGI_FORMAT_R8_UINT: + case DXGI_FORMAT_R8_SINT: + // All these are supported if this optional feature is set. + return typedUAVLoadAdditionalFormats; + + case DXGI_FORMAT_R16G16B16A16_UNORM: + case DXGI_FORMAT_R16G16B16A16_SNORM: + case DXGI_FORMAT_R32G32_FLOAT: + case DXGI_FORMAT_R32G32_UINT: + case DXGI_FORMAT_R32G32_SINT: + case DXGI_FORMAT_R10G10B10A2_UNORM: + case DXGI_FORMAT_R10G10B10A2_UINT: + case DXGI_FORMAT_R11G11B10_FLOAT: + case DXGI_FORMAT_R8G8B8A8_SNORM: + case DXGI_FORMAT_R16G16_FLOAT: + case DXGI_FORMAT_R16G16_UNORM: + case DXGI_FORMAT_R16G16_UINT: + case DXGI_FORMAT_R16G16_SNORM: + case DXGI_FORMAT_R16G16_SINT: + case DXGI_FORMAT_R8G8_UNORM: + case DXGI_FORMAT_R8G8_UINT: + case DXGI_FORMAT_R8G8_SNORM: + case DXGI_FORMAT_R8G8_SINT: + case DXGI_FORMAT_R16_UNORM: + case DXGI_FORMAT_R16_SNORM: + case DXGI_FORMAT_R8_SNORM: + case DXGI_FORMAT_A8_UNORM: + case DXGI_FORMAT_B5G6R5_UNORM: + case DXGI_FORMAT_B5G5R5A1_UNORM: + case DXGI_FORMAT_B4G4R4A4_UNORM: + // Conditionally supported by specific devices. + if (typedUAVLoadAdditionalFormats) + { + D3D12_FEATURE_DATA_FORMAT_SUPPORT formatSupport = { format, D3D12_FORMAT_SUPPORT1_NONE, D3D12_FORMAT_SUPPORT2_NONE }; + if (SUCCEEDED(device->CheckFeatureSupport(D3D12_FEATURE_FORMAT_SUPPORT, &formatSupport, sizeof(formatSupport)))) + { + const DWORD mask = D3D12_FORMAT_SUPPORT2_UAV_TYPED_LOAD | D3D12_FORMAT_SUPPORT2_UAV_TYPED_STORE; + return ((formatSupport.Support2 & mask) == mask); + } + } + return false; + + default: + return false; + } + } + + bool FormatIsBGR(DXGI_FORMAT format) noexcept + { + switch (format) + { + case DXGI_FORMAT_B8G8R8A8_UNORM: + case DXGI_FORMAT_B8G8R8X8_UNORM: + case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: + case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: + return true; + default: + return false; + } + } + + bool FormatIsSRGB(DXGI_FORMAT format) noexcept + { + switch (format) + { + case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: + case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: + case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: + return true; + default: + return false; + } + } + + DXGI_FORMAT ConvertSRVtoResourceFormat(DXGI_FORMAT format) noexcept + { + switch (format) + { + case DXGI_FORMAT_R32G32B32A32_FLOAT: + case DXGI_FORMAT_R32G32B32A32_UINT: + case DXGI_FORMAT_R32G32B32A32_SINT: + return DXGI_FORMAT_R32G32B32A32_TYPELESS; + + case DXGI_FORMAT_R16G16B16A16_FLOAT: + case DXGI_FORMAT_R16G16B16A16_UNORM: + case DXGI_FORMAT_R16G16B16A16_UINT: + case DXGI_FORMAT_R16G16B16A16_SNORM: + case DXGI_FORMAT_R16G16B16A16_SINT: + return DXGI_FORMAT_R16G16B16A16_TYPELESS; + + case DXGI_FORMAT_R32G32_FLOAT: + case DXGI_FORMAT_R32G32_UINT: + case DXGI_FORMAT_R32G32_SINT: + return DXGI_FORMAT_R32G32_TYPELESS; + + case DXGI_FORMAT_R10G10B10A2_UNORM: + case DXGI_FORMAT_R10G10B10A2_UINT: + return DXGI_FORMAT_R10G10B10A2_TYPELESS; + + case DXGI_FORMAT_R8G8B8A8_UNORM: + case DXGI_FORMAT_R8G8B8A8_UINT: + case DXGI_FORMAT_R8G8B8A8_SNORM: + case DXGI_FORMAT_R8G8B8A8_SINT: + return DXGI_FORMAT_R8G8B8A8_TYPELESS; + + case DXGI_FORMAT_R16G16_FLOAT: + case DXGI_FORMAT_R16G16_UNORM: + case DXGI_FORMAT_R16G16_UINT: + case DXGI_FORMAT_R16G16_SNORM: + case DXGI_FORMAT_R16G16_SINT: + return DXGI_FORMAT_R16G16_TYPELESS; + + case DXGI_FORMAT_R32_FLOAT: + case DXGI_FORMAT_R32_UINT: + case DXGI_FORMAT_R32_SINT: + return DXGI_FORMAT_R32_TYPELESS; + + case DXGI_FORMAT_R8G8_UNORM: + case DXGI_FORMAT_R8G8_UINT: + case DXGI_FORMAT_R8G8_SNORM: + case DXGI_FORMAT_R8G8_SINT: + return DXGI_FORMAT_R8G8_TYPELESS; + + case DXGI_FORMAT_R16_FLOAT: + case DXGI_FORMAT_R16_UNORM: + case DXGI_FORMAT_R16_UINT: + case DXGI_FORMAT_R16_SNORM: + case DXGI_FORMAT_R16_SINT: + return DXGI_FORMAT_R16_TYPELESS; + + case DXGI_FORMAT_R8_UNORM: + case DXGI_FORMAT_R8_UINT: + case DXGI_FORMAT_R8_SNORM: + case DXGI_FORMAT_R8_SINT: + return DXGI_FORMAT_R8_TYPELESS; + + default: + return format; + } + } + + class GenerateMipsResources + { + public: + enum RootParameterIndex + { + Constants, + SourceTexture, + TargetTexture, + RootParameterCount + }; + + #pragma pack(push, 4) + struct ConstantData + { + XMFLOAT2 InvOutTexelSize; + uint32_t SrcMipIndex; + }; + #pragma pack(pop) + + static constexpr uint32_t Num32BitConstants = static_cast(sizeof(ConstantData) / sizeof(uint32_t)); + static constexpr uint32_t ThreadGroupSize = 8; + + ComPtr rootSignature; + ComPtr generateMipsPSO; + + GenerateMipsResources( + _In_ ID3D12Device* device) + { + rootSignature = CreateGenMipsRootSignature(device); + generateMipsPSO = CreateGenMipsPipelineState(device, rootSignature.Get(), GenerateMips_main, sizeof(GenerateMips_main)); + } + + private: + static ComPtr CreateGenMipsRootSignature( + _In_ ID3D12Device* device) + { + ENUM_FLAGS_CONSTEXPR D3D12_ROOT_SIGNATURE_FLAGS rootSignatureFlags = + D3D12_ROOT_SIGNATURE_FLAG_DENY_VERTEX_SHADER_ROOT_ACCESS + | D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS + | D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS + | D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS +#ifdef _GAMING_XBOX_SCARLETT + | D3D12_ROOT_SIGNATURE_FLAG_DENY_AMPLIFICATION_SHADER_ROOT_ACCESS + | D3D12_ROOT_SIGNATURE_FLAG_DENY_MESH_SHADER_ROOT_ACCESS +#endif + | D3D12_ROOT_SIGNATURE_FLAG_DENY_PIXEL_SHADER_ROOT_ACCESS; + + const CD3DX12_STATIC_SAMPLER_DESC sampler( + 0, // register + D3D12_FILTER_MIN_MAG_LINEAR_MIP_POINT, + D3D12_TEXTURE_ADDRESS_MODE_CLAMP, + D3D12_TEXTURE_ADDRESS_MODE_CLAMP, + D3D12_TEXTURE_ADDRESS_MODE_CLAMP); + + const CD3DX12_DESCRIPTOR_RANGE sourceDescriptorRange(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0); + const CD3DX12_DESCRIPTOR_RANGE targetDescriptorRange(D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 1, 0); + + CD3DX12_ROOT_PARAMETER rootParameters[RootParameterIndex::RootParameterCount] = {}; + rootParameters[RootParameterIndex::Constants].InitAsConstants(Num32BitConstants, 0); + rootParameters[RootParameterIndex::SourceTexture].InitAsDescriptorTable(1, &sourceDescriptorRange); + rootParameters[RootParameterIndex::TargetTexture].InitAsDescriptorTable(1, &targetDescriptorRange); + + CD3DX12_ROOT_SIGNATURE_DESC rsigDesc; + rsigDesc.Init(static_cast(std::size(rootParameters)), rootParameters, 1, &sampler, rootSignatureFlags); + + ComPtr rootSignature; + ThrowIfFailed(CreateRootSignature(device, &rsigDesc, rootSignature.ReleaseAndGetAddressOf())); + + SetDebugObjectName(rootSignature.Get(), L"GenerateMips RootSignature"); + + return rootSignature; + } + + static ComPtr CreateGenMipsPipelineState( + _In_ ID3D12Device* device, + _In_ ID3D12RootSignature* rootSignature, + _In_reads_(bytecodeSize) const uint8_t* bytecode, + _In_ size_t bytecodeSize) + { + D3D12_COMPUTE_PIPELINE_STATE_DESC desc = {}; + desc.CS.BytecodeLength = bytecodeSize; + desc.CS.pShaderBytecode = bytecode; + desc.pRootSignature = rootSignature; + + ComPtr pso; + ThrowIfFailed(device->CreateComputePipelineState(&desc, IID_GRAPHICS_PPV_ARGS(pso.GetAddressOf()))); + + SetDebugObjectName(pso.Get(), L"GenerateMips PSO"); + + return pso; + } + }; +} // anonymous namespace + +class ResourceUploadBatch::Impl +{ +public: + Impl( + _In_ ID3D12Device* device) noexcept + : mDevice(device) + , mCommandType(D3D12_COMMAND_LIST_TYPE_DIRECT) + , mInBeginEndBlock(false) + , mTypedUAVLoadAdditionalFormats(false) + , mStandardSwizzle64KBSupported(false) + { + assert(device != nullptr); + D3D12_FEATURE_DATA_D3D12_OPTIONS options = {}; + if (SUCCEEDED(device->CheckFeatureSupport( + D3D12_FEATURE_D3D12_OPTIONS, + &options, + sizeof(options)))) + { + mTypedUAVLoadAdditionalFormats = options.TypedUAVLoadAdditionalFormats != 0; + mStandardSwizzle64KBSupported = options.StandardSwizzle64KBSupported != 0; + } + } + + // Call this before your multiple calls to Upload. + void Begin(D3D12_COMMAND_LIST_TYPE commandType) + { + if (mInBeginEndBlock) + throw std::logic_error("Can't Begin: already in a Begin-End block."); + + switch (commandType) + { + case D3D12_COMMAND_LIST_TYPE_DIRECT: + case D3D12_COMMAND_LIST_TYPE_COMPUTE: + case D3D12_COMMAND_LIST_TYPE_COPY: + break; + + default: + DebugTrace("ResourceUploadBatch only supports Direct, Compute, and Copy command queues\n"); + throw std::invalid_argument("commandType parameter is invalid"); + } + + ThrowIfFailed(mDevice->CreateCommandAllocator(commandType, IID_GRAPHICS_PPV_ARGS(mCmdAlloc.ReleaseAndGetAddressOf()))); + + SetDebugObjectName(mCmdAlloc.Get(), L"ResourceUploadBatch"); + + ThrowIfFailed(mDevice->CreateCommandList(1, commandType, mCmdAlloc.Get(), nullptr, IID_GRAPHICS_PPV_ARGS(mList.ReleaseAndGetAddressOf()))); + + SetDebugObjectName(mList.Get(), L"ResourceUploadBatch"); + + mCommandType = commandType; + mInBeginEndBlock = true; + } + + // Asynchronously uploads a resource. The memory in subRes is copied. + // The resource must be in the COPY_DEST state. + void Upload( + _In_ ID3D12Resource* resource, + uint32_t subresourceIndexStart, + _In_reads_(numSubresources) const D3D12_SUBRESOURCE_DATA* subRes, + uint32_t numSubresources) + { + if (!mInBeginEndBlock) + throw std::logic_error("Can't call Upload on a closed ResourceUploadBatch."); + + const UINT64 uploadSize = GetRequiredIntermediateSize( + resource, + subresourceIndexStart, + numSubresources); + + const CD3DX12_HEAP_PROPERTIES heapProps(D3D12_HEAP_TYPE_UPLOAD); + auto const resDesc = CD3DX12_RESOURCE_DESC::Buffer(uploadSize); + + // Create a temporary buffer + ComPtr scratchResource = nullptr; + ThrowIfFailed(mDevice->CreateCommittedResource( + &heapProps, + D3D12_HEAP_FLAG_NONE, + &resDesc, + D3D12_RESOURCE_STATE_GENERIC_READ, + nullptr, // D3D12_CLEAR_VALUE* pOptimizedClearValue + IID_GRAPHICS_PPV_ARGS(scratchResource.GetAddressOf()))); + + SetDebugObjectName(scratchResource.Get(), L"ResourceUploadBatch Temporary"); + + // Submit resource copy to command list + UpdateSubresources(mList.Get(), resource, scratchResource.Get(), 0, subresourceIndexStart, numSubresources, + #if defined(_XBOX_ONE) && defined(_TITLE) + // Workaround for header constness issue + const_cast(subRes) + #else + subRes + #endif + ); + + // Remember this upload object for delayed release + mTrackedObjects.push_back(scratchResource); + } + + void Upload( + _In_ ID3D12Resource* resource, + const SharedGraphicsResource& buffer) + { + if (!mInBeginEndBlock) + throw std::logic_error("Can't call Upload on a closed ResourceUploadBatch."); + + // Submit resource copy to command list + mList->CopyBufferRegion(resource, 0, buffer.Resource(), buffer.ResourceOffset(), buffer.Size()); + + // Remember this upload resource for delayed release + mTrackedMemoryResources.push_back(buffer); + } + + // Asynchronously generate mips from a resource. + // Resource must be in the PIXEL_SHADER_RESOURCE state + void GenerateMips(_In_ ID3D12Resource* resource) + { + if (resource == nullptr) + { + throw std::invalid_argument("Nullptr passed to GenerateMips"); + } + + if (!mInBeginEndBlock) + throw std::logic_error("Can't call GenerateMips on a closed ResourceUploadBatch."); + + if (mCommandType == D3D12_COMMAND_LIST_TYPE_COPY) + { + DebugTrace("ERROR: GenerateMips cannot operate on a copy queue\n"); + throw std::runtime_error("GenerateMips cannot operate on a copy queue"); + } + + #if defined(_MSC_VER) || !defined(_WIN32) + const auto desc = resource->GetDesc(); + #else + D3D12_RESOURCE_DESC tmpDesc; + const auto& desc = *resource->GetDesc(&tmpDesc); + #endif + + if (desc.MipLevels == 1) + { + // Nothing to do + return; + } + if (desc.MipLevels == 0) + { + throw std::runtime_error("GenerateMips: texture has no mips"); + } + if (desc.Dimension != D3D12_RESOURCE_DIMENSION_TEXTURE2D) + { + throw std::runtime_error("GenerateMips only supports Texture2D resources"); + } + if (desc.DepthOrArraySize != 1) + { + throw std::runtime_error("GenerateMips only supports 2D textures of array size 1"); + } + + const bool uavCompat = FormatIsUAVCompatible(mDevice.Get(), mTypedUAVLoadAdditionalFormats, desc.Format); + + if (!uavCompat && !FormatIsSRGB(desc.Format) && !FormatIsBGR(desc.Format)) + { + throw std::runtime_error("GenerateMips doesn't support this texture format on this device"); + } + + // Ensure that we have valid generate mips data + if (mGenMipsResources == nullptr) + { + mGenMipsResources = std::make_unique(mDevice.Get()); + } + + // If the texture's format doesn't support UAVs we'll have to copy it to a texture that does first. + // This is true of BGRA or sRGB textures, for example. + if (uavCompat) + { + GenerateMips_UnorderedAccessPath(resource); + } + else if (!mTypedUAVLoadAdditionalFormats) + { + throw std::runtime_error("GenerateMips needs TypedUAVLoadAdditionalFormats device support for sRGB/BGR"); + } + else if (FormatIsBGR(desc.Format)) + { + #if !defined(_GAMING_XBOX) && !(defined(_XBOX_ONE) && defined(_TITLE)) + if (!mStandardSwizzle64KBSupported) + { + throw std::runtime_error("GenerateMips needs StandardSwizzle64KBSupported device support for BGR"); + } + #endif + + GenerateMips_TexturePathBGR(resource); + } + else + { + GenerateMips_TexturePath(resource); + } + } + + // Transition a resource once you're done with it + void Transition( + _In_ ID3D12Resource* resource, + _In_ D3D12_RESOURCE_STATES stateBefore, + _In_ D3D12_RESOURCE_STATES stateAfter) + { + if (!mInBeginEndBlock) + throw std::logic_error("Can't call Upload on a closed ResourceUploadBatch."); + + if (mCommandType == D3D12_COMMAND_LIST_TYPE_COPY) + { + switch (stateAfter) + { + case D3D12_RESOURCE_STATE_COPY_DEST: + case D3D12_RESOURCE_STATE_COPY_SOURCE: + break; + + default: + // Ignore other states for copy queues. + return; + } + } + else if (mCommandType == D3D12_COMMAND_LIST_TYPE_COMPUTE) + { + switch (stateAfter) + { + case D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER: + case D3D12_RESOURCE_STATE_UNORDERED_ACCESS: + case D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE: + case D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT: + case D3D12_RESOURCE_STATE_COPY_DEST: + case D3D12_RESOURCE_STATE_COPY_SOURCE: + break; + + default: + // Ignore other states for compute queues. + return; + } + } + + TransitionResource(mList.Get(), resource, stateBefore, stateAfter); + } + + // Submits all the uploads to the driver. + // No more uploads can happen after this call until Begin is called again. + // This returns a handle to an event that can be waited on. + std::future End( + _In_ ID3D12CommandQueue* commandQueue) + { + if (!mInBeginEndBlock) + throw std::logic_error("ResourceUploadBatch already closed."); + + ThrowIfFailed(mList->Close()); + + // Submit the job to the GPU + commandQueue->ExecuteCommandLists(1, CommandListCast(mList.GetAddressOf())); + + // Set an event so we get notified when the GPU has completed all its work + ComPtr fence; + ThrowIfFailed(mDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_GRAPHICS_PPV_ARGS(fence.GetAddressOf()))); + + SetDebugObjectName(fence.Get(), L"ResourceUploadBatch"); + + HANDLE gpuCompletedEvent = CreateEventEx(nullptr, nullptr, 0, EVENT_MODIFY_STATE | SYNCHRONIZE); + if (!gpuCompletedEvent) + throw std::system_error(std::error_code(static_cast(GetLastError()), std::system_category()), "CreateEventEx"); + + ThrowIfFailed(commandQueue->Signal(fence.Get(), 1ULL)); + ThrowIfFailed(fence->SetEventOnCompletion(1ULL, gpuCompletedEvent)); + + // Create a packet of data that'll be passed to our waiting upload thread + auto uploadBatch = new UploadBatch(); + uploadBatch->CommandList = mList; + uploadBatch->Fence = fence; + uploadBatch->GpuCompleteEvent.reset(gpuCompletedEvent); + std::swap(mTrackedObjects, uploadBatch->TrackedObjects); + std::swap(mTrackedMemoryResources, uploadBatch->TrackedMemoryResources); + + // Kick off a thread that waits for the upload to complete on the GPU timeline. + // Let the thread run autonomously, but provide a future the user can wait on. + std::future future = std::async(std::launch::async, [uploadBatch]() + { + // Wait on the GPU-complete notification + const DWORD wr = WaitForSingleObject(uploadBatch->GpuCompleteEvent.get(), INFINITE); + if (wr != WAIT_OBJECT_0) + { + if (wr == WAIT_FAILED) + { + throw std::system_error(std::error_code(static_cast(GetLastError()), std::system_category()), "WaitForSingleObject"); + } + else + { + throw std::runtime_error("WaitForSingleObject"); + } + } + + // Delete the batch + // Because the vectors contain smart-pointers, their destructors will + // fire and the resources will be released. + delete uploadBatch; + }); + + // Reset our state + mCommandType = D3D12_COMMAND_LIST_TYPE_DIRECT; + mInBeginEndBlock = false; + mList.Reset(); + mCmdAlloc.Reset(); + + // Swap above should have cleared these + assert(mTrackedObjects.empty()); + assert(mTrackedMemoryResources.empty()); + + return future; + } + + bool IsSupportedForGenerateMips(DXGI_FORMAT format) noexcept + { + if (mCommandType == D3D12_COMMAND_LIST_TYPE_COPY) + return false; + + if (FormatIsUAVCompatible(mDevice.Get(), mTypedUAVLoadAdditionalFormats, format)) + return true; + + if (FormatIsBGR(format)) + { + #if defined(_GAMING_XBOX) || (defined(_XBOX_ONE) && defined(_TITLE)) + // We know the RGB and BGR memory layouts match for Xbox One + return true; + #else + // BGR path requires DXGI_FORMAT_R8G8B8A8_UNORM support for UAV load/store plus matching layouts + return mTypedUAVLoadAdditionalFormats && mStandardSwizzle64KBSupported; + #endif + } + + if (FormatIsSRGB(format)) + { + // sRGB path requires DXGI_FORMAT_R8G8B8A8_UNORM support for UAV load/store + return mTypedUAVLoadAdditionalFormats; + } + + return false; + } + +private: + // Resource is UAV compatible + void GenerateMips_UnorderedAccessPath( + _In_ ID3D12Resource* resource) + { + #if defined(_MSC_VER) || !defined(_WIN32) + const auto desc = resource->GetDesc(); + #else + D3D12_RESOURCE_DESC tmpDesc; + const auto& desc = *resource->GetDesc(&tmpDesc); + #endif + assert(!FormatIsBGR(desc.Format) && !FormatIsSRGB(desc.Format)); + + const CD3DX12_HEAP_PROPERTIES defaultHeapProperties(D3D12_HEAP_TYPE_DEFAULT); + + assert(mCommandType != D3D12_COMMAND_LIST_TYPE_COPY); + const D3D12_RESOURCE_STATES originalState = (mCommandType == D3D12_COMMAND_LIST_TYPE_COMPUTE) + ? D3D12_RESOURCE_STATE_COPY_DEST : D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + + // Create a staging resource if we have to + ComPtr staging; + if ((desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS) == 0) + { + D3D12_RESOURCE_DESC stagingDesc = desc; + stagingDesc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; + stagingDesc.Format = ConvertSRVtoResourceFormat(desc.Format); + + ThrowIfFailed(mDevice->CreateCommittedResource( + &defaultHeapProperties, + D3D12_HEAP_FLAG_NONE, + &stagingDesc, + D3D12_RESOURCE_STATE_COPY_DEST, + nullptr, + IID_GRAPHICS_PPV_ARGS(staging.GetAddressOf()))); + + SetDebugObjectName(staging.Get(), L"GenerateMips Staging"); + + // Copy the top mip of resource to staging + TransitionResource(mList.Get(), resource, originalState, D3D12_RESOURCE_STATE_COPY_SOURCE); + + const CD3DX12_TEXTURE_COPY_LOCATION src(resource, 0); + const CD3DX12_TEXTURE_COPY_LOCATION dst(staging.Get(), 0); + mList->CopyTextureRegion(&dst, 0, 0, 0, &src, nullptr); + + TransitionResource(mList.Get(), staging.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE); + } + else + { + // Resource is already a UAV so we can do this in-place + staging = resource; + + TransitionResource(mList.Get(), staging.Get(), originalState, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE); + } + + // Create a descriptor heap that holds our resource descriptors + ComPtr descriptorHeap; + D3D12_DESCRIPTOR_HEAP_DESC descriptorHeapDesc = {}; + descriptorHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; + descriptorHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; + descriptorHeapDesc.NumDescriptors = desc.MipLevels; + mDevice->CreateDescriptorHeap(&descriptorHeapDesc, IID_GRAPHICS_PPV_ARGS(descriptorHeap.GetAddressOf())); + + SetDebugObjectName(descriptorHeap.Get(), L"ResourceUploadBatch"); + + auto const descriptorSize = static_cast(mDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV)); + + // Create the top-level SRV + #if defined(_MSC_VER) || !defined(_WIN32) + CD3DX12_CPU_DESCRIPTOR_HANDLE handleIt(descriptorHeap->GetCPUDescriptorHandleForHeapStart()); + #else + CD3DX12_CPU_DESCRIPTOR_HANDLE handleIt; + std::ignore = descriptorHeap->GetCPUDescriptorHandleForHeapStart(&handleIt); + #endif + D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {}; + srvDesc.Format = desc.Format; + srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; + srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + srvDesc.Texture2D.MostDetailedMip = 0; + srvDesc.Texture2D.MipLevels = desc.MipLevels; + + mDevice->CreateShaderResourceView(staging.Get(), &srvDesc, handleIt); + + // Create the UAVs for the tail + for (uint16_t mip = 1; mip < desc.MipLevels; ++mip) + { + D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc = {}; + uavDesc.Format = desc.Format; + uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D; + uavDesc.Texture2D.MipSlice = mip; + + handleIt.Offset(descriptorSize); + mDevice->CreateUnorderedAccessView(staging.Get(), nullptr, &uavDesc, handleIt); + } + + // Set up UAV barrier (used in loop) + D3D12_RESOURCE_BARRIER barrierUAV = {}; + barrierUAV.Type = D3D12_RESOURCE_BARRIER_TYPE_UAV; + barrierUAV.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + barrierUAV.UAV.pResource = staging.Get(); + + // Barrier for transitioning the subresources to UAVs + D3D12_RESOURCE_BARRIER srv2uavDesc = {}; + srv2uavDesc.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + srv2uavDesc.Transition.pResource = staging.Get(); + srv2uavDesc.Transition.Subresource = 0; + srv2uavDesc.Transition.StateBefore = D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE; + srv2uavDesc.Transition.StateAfter = D3D12_RESOURCE_STATE_UNORDERED_ACCESS; + + // Barrier for transitioning the subresources to SRVs + D3D12_RESOURCE_BARRIER uav2srvDesc = {}; + uav2srvDesc.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + uav2srvDesc.Transition.pResource = staging.Get(); + uav2srvDesc.Transition.Subresource = 0; + uav2srvDesc.Transition.StateBefore = D3D12_RESOURCE_STATE_UNORDERED_ACCESS; + uav2srvDesc.Transition.StateAfter = D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE; + + // based on format, select srgb or not + ComPtr pso = mGenMipsResources->generateMipsPSO; + + // Set up state + mList->SetComputeRootSignature(mGenMipsResources->rootSignature.Get()); + mList->SetPipelineState(pso.Get()); + mList->SetDescriptorHeaps(1, descriptorHeap.GetAddressOf()); + + #if defined(_MSC_VER) || !defined(_WIN32) + D3D12_GPU_DESCRIPTOR_HANDLE handle(descriptorHeap->GetGPUDescriptorHandleForHeapStart()); + #else + D3D12_GPU_DESCRIPTOR_HANDLE handle; + std::ignore = descriptorHeap->GetGPUDescriptorHandleForHeapStart(&handle); + #endif + mList->SetComputeRootDescriptorTable(GenerateMipsResources::SourceTexture, handle); + + // Get the descriptor handle -- uavH will increment over each loop + CD3DX12_GPU_DESCRIPTOR_HANDLE uavH(handle, descriptorSize); // offset by 1 descriptor + + // Process each mip + auto mipWidth = static_cast(desc.Width); + uint32_t mipHeight = desc.Height; + for (uint32_t mip = 1; mip < desc.MipLevels; ++mip) + { + mipWidth = std::max(1, mipWidth >> 1); + mipHeight = std::max(1, mipHeight >> 1); + + // Transition the mip to a UAV + srv2uavDesc.Transition.Subresource = mip; + mList->ResourceBarrier(1, &srv2uavDesc); + + // Bind the mip subresources + mList->SetComputeRootDescriptorTable(GenerateMipsResources::TargetTexture, uavH); + + // Set constants + GenerateMipsResources::ConstantData constants; + constants.SrcMipIndex = mip - 1; + constants.InvOutTexelSize = XMFLOAT2(1 / float(mipWidth), 1 / float(mipHeight)); + mList->SetComputeRoot32BitConstants( + GenerateMipsResources::Constants, + GenerateMipsResources::Num32BitConstants, + &constants, + 0); + + // Process this mip + mList->Dispatch( + (mipWidth + GenerateMipsResources::ThreadGroupSize - 1) / GenerateMipsResources::ThreadGroupSize, + (mipHeight + GenerateMipsResources::ThreadGroupSize - 1) / GenerateMipsResources::ThreadGroupSize, + 1); + + mList->ResourceBarrier(1, &barrierUAV); + + // Transition the mip to an SRV + uav2srvDesc.Transition.Subresource = mip; + mList->ResourceBarrier(1, &uav2srvDesc); + + // Offset the descriptor heap handles + uavH.Offset(descriptorSize); + } + + // If the staging resource is NOT the same as the resource, we need to copy everything back + if (staging.Get() != resource) + { + // Transition the resources ready for copy + D3D12_RESOURCE_BARRIER barrier[2] = {}; + barrier[0].Type = barrier[1].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier[0].Transition.Subresource = barrier[1].Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + barrier[0].Transition.pResource = staging.Get(); + barrier[0].Transition.StateBefore = D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE; + barrier[0].Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE; + + barrier[1].Transition.pResource = resource; + barrier[1].Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_SOURCE; + barrier[1].Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_DEST; + + mList->ResourceBarrier(2, barrier); + + // Copy the entire resource back + mList->CopyResource(resource, staging.Get()); + + // Transition the target resource back to pixel shader resource + TransitionResource(mList.Get(), resource, D3D12_RESOURCE_STATE_COPY_DEST, originalState); + + mTrackedObjects.push_back(staging); + } + else + { + TransitionResource(mList.Get(), staging.Get(), D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, originalState); + } + + // Add our temporary objects to the deferred deletion queue + mTrackedObjects.push_back(mGenMipsResources->rootSignature); + mTrackedObjects.push_back(pso); + mTrackedObjects.push_back(resource); + mTrackedObjects.push_back(descriptorHeap); + } + + // Resource is not UAV compatible + void GenerateMips_TexturePath( + _In_ ID3D12Resource* resource) + { + #if defined(_MSC_VER) || !defined(_WIN32) + const auto resourceDesc = resource->GetDesc(); + #else + D3D12_RESOURCE_DESC tmpDesc; + const auto& resourceDesc = *resource->GetDesc(&tmpDesc); + #endif + assert(!FormatIsBGR(resourceDesc.Format) || FormatIsSRGB(resourceDesc.Format)); + + auto copyDesc = resourceDesc; + copyDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + copyDesc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; + + const CD3DX12_HEAP_PROPERTIES heapProperties(D3D12_HEAP_TYPE_DEFAULT); + + // Create a resource with the same description, but without SRGB, and with UAV flags + ComPtr resourceCopy; + ThrowIfFailed(mDevice->CreateCommittedResource( + &heapProperties, + D3D12_HEAP_FLAG_NONE, + ©Desc, + D3D12_RESOURCE_STATE_COPY_DEST, + nullptr, + IID_GRAPHICS_PPV_ARGS(resourceCopy.GetAddressOf()))); + + SetDebugObjectName(resourceCopy.Get(), L"GenerateMips Resource Copy"); + + assert(mCommandType != D3D12_COMMAND_LIST_TYPE_COPY); + const D3D12_RESOURCE_STATES originalState = mCommandType == D3D12_COMMAND_LIST_TYPE_COMPUTE + ? D3D12_RESOURCE_STATE_COPY_DEST : D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + + // Copy the top mip of resource data + TransitionResource(mList.Get(), resource, originalState, D3D12_RESOURCE_STATE_COPY_SOURCE); + + const CD3DX12_TEXTURE_COPY_LOCATION src(resource, 0); + const CD3DX12_TEXTURE_COPY_LOCATION dst(resourceCopy.Get(), 0); + mList->CopyTextureRegion(&dst, 0, 0, 0, &src, nullptr); + + TransitionResource(mList.Get(), resourceCopy.Get(), D3D12_RESOURCE_STATE_COPY_DEST, originalState); + + // Generate the mips + GenerateMips_UnorderedAccessPath(resourceCopy.Get()); + + // Direct copy back + D3D12_RESOURCE_BARRIER barrier[2] = {}; + barrier[0].Type = barrier[1].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier[0].Transition.Subresource = barrier[1].Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + barrier[0].Transition.pResource = resourceCopy.Get(); + barrier[0].Transition.StateBefore = originalState; + barrier[0].Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE; + + barrier[1].Transition.pResource = resource; + barrier[1].Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_SOURCE; + barrier[1].Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_DEST; + + mList->ResourceBarrier(2, barrier); + + // Copy the entire resource back + mList->CopyResource(resource, resourceCopy.Get()); + + TransitionResource(mList.Get(), resource, D3D12_RESOURCE_STATE_COPY_DEST, originalState); + + // Track these object lifetimes on the GPU + mTrackedObjects.push_back(resourceCopy); + mTrackedObjects.push_back(resource); + } + + // Resource is not UAV compatible (copy via alias to avoid validation failure) + void GenerateMips_TexturePathBGR( + _In_ ID3D12Resource* resource) + { + #if defined(_MSC_VER) || !defined(_WIN32) + const auto resourceDesc = resource->GetDesc(); + #else + D3D12_RESOURCE_DESC tmpDesc; + const auto& resourceDesc = *resource->GetDesc(&tmpDesc); + #endif + assert(FormatIsBGR(resourceDesc.Format)); + + // Create a resource with the same description with RGB and with UAV flags + auto copyDesc = resourceDesc; + copyDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + copyDesc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; + #if !defined(_GAMING_XBOX) && !(defined(_XBOX_ONE) && defined(_TITLE)) + copyDesc.Layout = D3D12_TEXTURE_LAYOUT_64KB_STANDARD_SWIZZLE; + #endif + + D3D12_HEAP_DESC heapDesc = {}; + #if defined(_MSC_VER) || !defined(_WIN32) + auto const allocInfo = mDevice->GetResourceAllocationInfo(0, 1, ©Desc); + #else + D3D12_RESOURCE_ALLOCATION_INFO allocInfo; + std::ignore = mDevice->GetResourceAllocationInfo(&allocInfo, 0, 1, ©Desc); + #endif + heapDesc.SizeInBytes = allocInfo.SizeInBytes; + heapDesc.Flags = D3D12_HEAP_FLAG_ALLOW_ONLY_NON_RT_DS_TEXTURES; + heapDesc.Properties.Type = D3D12_HEAP_TYPE_DEFAULT; + + ComPtr heap; + ThrowIfFailed(mDevice->CreateHeap(&heapDesc, IID_GRAPHICS_PPV_ARGS(heap.GetAddressOf()))); + + SetDebugObjectName(heap.Get(), L"ResourceUploadBatch"); + + ComPtr resourceCopy; + ThrowIfFailed(mDevice->CreatePlacedResource( + heap.Get(), + 0, + ©Desc, + D3D12_RESOURCE_STATE_COPY_DEST, + nullptr, + IID_GRAPHICS_PPV_ARGS(resourceCopy.GetAddressOf()))); + + SetDebugObjectName(resourceCopy.Get(), L"GenerateMips Resource Copy"); + + // Create a BGRA alias + auto aliasDesc = resourceDesc; + aliasDesc.Format = (resourceDesc.Format == DXGI_FORMAT_B8G8R8X8_UNORM || resourceDesc.Format == DXGI_FORMAT_B8G8R8X8_UNORM_SRGB) ? DXGI_FORMAT_B8G8R8X8_UNORM : DXGI_FORMAT_B8G8R8A8_UNORM; + aliasDesc.Layout = copyDesc.Layout; + aliasDesc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; + + ComPtr aliasCopy; + ThrowIfFailed(mDevice->CreatePlacedResource( + heap.Get(), + 0, + &aliasDesc, + D3D12_RESOURCE_STATE_COPY_DEST, + nullptr, + IID_GRAPHICS_PPV_ARGS(aliasCopy.GetAddressOf()))); + + SetDebugObjectName(aliasCopy.Get(), L"GenerateMips BGR Alias Copy"); + + assert(mCommandType != D3D12_COMMAND_LIST_TYPE_COPY); + const D3D12_RESOURCE_STATES originalState = (mCommandType == D3D12_COMMAND_LIST_TYPE_COMPUTE) + ? D3D12_RESOURCE_STATE_COPY_DEST : D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + + // Copy the top mip of the resource data BGR to RGB + D3D12_RESOURCE_BARRIER aliasBarrier[3] = {}; + aliasBarrier[0].Type = D3D12_RESOURCE_BARRIER_TYPE_ALIASING; + aliasBarrier[0].Aliasing.pResourceAfter = aliasCopy.Get(); + + aliasBarrier[1].Type = aliasBarrier[2].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + aliasBarrier[1].Transition.Subresource = aliasBarrier[2].Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + aliasBarrier[1].Transition.pResource = resource; + aliasBarrier[1].Transition.StateBefore = originalState; + aliasBarrier[1].Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE; + + mList->ResourceBarrier(2, aliasBarrier); + + const CD3DX12_TEXTURE_COPY_LOCATION src(resource, 0); + const CD3DX12_TEXTURE_COPY_LOCATION dst(aliasCopy.Get(), 0); + mList->CopyTextureRegion(&dst, 0, 0, 0, &src, nullptr); + + // Generate the mips + aliasBarrier[0].Aliasing.pResourceBefore = aliasCopy.Get(); + aliasBarrier[0].Aliasing.pResourceAfter = resourceCopy.Get(); + + aliasBarrier[1].Transition.pResource = resourceCopy.Get(); + aliasBarrier[1].Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST; + aliasBarrier[1].Transition.StateAfter = originalState; + + mList->ResourceBarrier(2, aliasBarrier); + GenerateMips_UnorderedAccessPath(resourceCopy.Get()); + + // Direct copy back RGB to BGR + aliasBarrier[0].Aliasing.pResourceBefore = resourceCopy.Get(); + aliasBarrier[0].Aliasing.pResourceAfter = aliasCopy.Get(); + + aliasBarrier[1].Transition.pResource = aliasCopy.Get(); + aliasBarrier[1].Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST; + aliasBarrier[1].Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE; + + aliasBarrier[2].Transition.pResource = resource; + aliasBarrier[2].Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_SOURCE; + aliasBarrier[2].Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_DEST; + + mList->ResourceBarrier(3, aliasBarrier); + + // Copy the entire resource back + mList->CopyResource(resource, aliasCopy.Get()); + TransitionResource(mList.Get(), resource, D3D12_RESOURCE_STATE_COPY_DEST, originalState); + + // Track these object lifetimes on the GPU + mTrackedObjects.push_back(heap); + mTrackedObjects.push_back(resourceCopy); + mTrackedObjects.push_back(aliasCopy); + mTrackedObjects.push_back(resource); + } + + struct UploadBatch + { + std::vector> TrackedObjects; + std::vector TrackedMemoryResources; + ComPtr CommandList; + ComPtr Fence; + ScopedHandle GpuCompleteEvent; + + UploadBatch() noexcept {} + }; + + ComPtr mDevice; + ComPtr mCmdAlloc; + ComPtr mList; + std::unique_ptr mGenMipsResources; + + std::vector> mTrackedObjects; + std::vector mTrackedMemoryResources; + + D3D12_COMMAND_LIST_TYPE mCommandType; + bool mInBeginEndBlock; + bool mTypedUAVLoadAdditionalFormats; + bool mStandardSwizzle64KBSupported; +}; + + + +// Public constructor. +ResourceUploadBatch::ResourceUploadBatch(_In_ ID3D12Device* device) noexcept(false) + : pImpl(std::make_unique(device)) +{ +} + + +ResourceUploadBatch::ResourceUploadBatch(ResourceUploadBatch&&) noexcept = default; +ResourceUploadBatch& ResourceUploadBatch::operator= (ResourceUploadBatch&&) noexcept = default; +ResourceUploadBatch::~ResourceUploadBatch() = default; + + +void ResourceUploadBatch::Begin(D3D12_COMMAND_LIST_TYPE commandType) +{ + pImpl->Begin(commandType); +} + + +_Use_decl_annotations_ +void ResourceUploadBatch::Upload( + ID3D12Resource* resource, + uint32_t subresourceIndexStart, + const D3D12_SUBRESOURCE_DATA* subRes, + uint32_t numSubresources) +{ + pImpl->Upload(resource, subresourceIndexStart, subRes, numSubresources); +} + + +_Use_decl_annotations_ +void ResourceUploadBatch::Upload( + ID3D12Resource* resource, + const SharedGraphicsResource& buffer +) +{ + pImpl->Upload(resource, buffer); +} + + + +void ResourceUploadBatch::GenerateMips(_In_ ID3D12Resource* resource) +{ + pImpl->GenerateMips(resource); +} + + +_Use_decl_annotations_ +void ResourceUploadBatch::Transition( + ID3D12Resource* resource, + D3D12_RESOURCE_STATES stateBefore, + D3D12_RESOURCE_STATES stateAfter) +{ + pImpl->Transition(resource, stateBefore, stateAfter); +} + + +std::future ResourceUploadBatch::End(_In_ ID3D12CommandQueue* commandQueue) +{ + return pImpl->End(commandQueue); +} + + +bool ResourceUploadBatch::IsSupportedForGenerateMips(DXGI_FORMAT format) noexcept +{ + return pImpl->IsSupportedForGenerateMips(format); +} diff --git a/Common/DirectXTK12/Src/SDKMesh.h b/Common/DirectXTK12/Src/SDKMesh.h new file mode 100644 index 0000000..94fef8e --- /dev/null +++ b/Common/DirectXTK12/Src/SDKMesh.h @@ -0,0 +1,360 @@ +//-------------------------------------------------------------------------------------- +// File: SDKMesh.h +// +// SDKMESH format is generated by the legacy DirectX SDK's Content Exporter and +// originally rendered by the DXUT helper class SDKMesh +// +// http://go.microsoft.com/fwlink/?LinkId=226208 +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248929 +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#pragma once + +#include + +namespace DXUT +{ + // .SDKMESH files + + // SDKMESH_HEADER + // SDKMESH_VERTEX_BUFFER_HEADER header->VertexStreamHeadersOffset + // SDKMESH_INDEX_BUFFER_HEADER header->IndexStreamHeadersOffset + // SDKMESH_MESH header->MeshDataOffset + // SDKMESH_SUBSET header->SubsetDataOffset + // SDKMESH_FRAME header->FrameDataOffset + // SDKMESH_MATERIAL header->MaterialDataOffset + // [header->NonBufferDataSize] + // { [ header->NumVertexBuffers] + // VB data + // } + // { [ header->NumIndexBuffers] + // IB data + // } + + + // .SDDKANIM files + + // SDKANIMATION_FILE_HEADER + // uint8_t[] - Length of fileheader->AnimationDataSize + + // .SDKMESH uses Direct3D 9 decls, but only a subset of these is ever generated by the legacy DirectX SDK Content Exporter + + // D3DDECLUSAGE_POSITION / D3DDECLTYPE_FLOAT3 + // (D3DDECLUSAGE_BLENDWEIGHT / D3DDECLTYPE_UBYTE4N + // D3DDECLUSAGE_BLENDINDICES / D3DDECLTYPE_UBYTE4)? + // (D3DDECLUSAGE_NORMAL / D3DDECLTYPE_FLOAT3, D3DDECLTYPE_FLOAT16_4, D3DDECLTYPE_SHORT4N, D3DDECLTYPE_UBYTE4N, or D3DDECLTYPE_DEC3N)? + // (D3DDECLUSAGE_COLOR / D3DDECLTYPE_D3DCOLOR)? + // (D3DDECLUSAGE_TEXCOORD / D3DDECLTYPE_FLOAT1, D3DDECLTYPE_FLOAT2 or D3DDECLTYPE_FLOAT16_2, D3DDECLTYPE_FLOAT3 or D3DDECLTYPE_FLOAT16_4, D3DDECLTYPE_FLOAT4 or D3DDECLTYPE_FLOAT16_4)* + // (D3DDECLUSAGE_TANGENT / same as D3DDECLUSAGE_NORMAL)? + // (D3DDECLUSAGE_BINORMAL / same as D3DDECLUSAGE_NORMAL)? + + enum D3DDECLUSAGE + { + D3DDECLUSAGE_POSITION = 0, + D3DDECLUSAGE_BLENDWEIGHT = 1, + D3DDECLUSAGE_BLENDINDICES = 2, + D3DDECLUSAGE_NORMAL = 3, + D3DDECLUSAGE_TEXCOORD = 5, + D3DDECLUSAGE_TANGENT = 6, + D3DDECLUSAGE_BINORMAL = 7, + D3DDECLUSAGE_COLOR = 10, + }; + + enum D3DDECLTYPE + { + D3DDECLTYPE_FLOAT1 = 0, + // 1D float expanded to (value, 0., 0., 1.) + + D3DDECLTYPE_FLOAT2 = 1, + // 2D float expanded to (value, value, 0., 1.) + + D3DDECLTYPE_FLOAT3 = 2, + // 3D float expanded to (value, value, value, 1.) + + D3DDECLTYPE_FLOAT4 = 3, + // 4D float + + D3DDECLTYPE_D3DCOLOR = 4, + // 4D packed unsigned bytes mapped to 0. to 1. range + // Input is in D3DCOLOR format (ARGB) expanded to (R, G, B, A) + + D3DDECLTYPE_UBYTE4 = 5, + // 4D unsigned uint8_t + + D3DDECLTYPE_UBYTE4N = 8, + // Each of 4 bytes is normalized by dividing to 255.0 + + D3DDECLTYPE_SHORT4N = 10, + // 4D signed short normalized (v[0]/32767.0,v[1]/32767.0,v[2]/32767.0,v[3]/32767.0) + + D3DDECLTYPE_DEC3N = 14, + // 3D signed normalized (v[0]/511.0, v[1]/511.0, v[2]/511.0, 1.) + // Note: There is no equivalent to D3DDECLTYPE_DEC3N (14) as a DXGI_FORMAT + + D3DDECLTYPE_FLOAT16_2 = 15, + // Two 16-bit floating point values, expanded to (value, value, 0, 1) + + D3DDECLTYPE_FLOAT16_4 = 16, + // Four 16-bit floating point values + + D3DDECLTYPE_UNUSED = 17, + // When the type field in a decl is unused. + + // These are extensions for DXGI-based versions of Direct3D + D3DDECLTYPE_DXGI_R10G10B10A2_UNORM = 32 + DXGI_FORMAT_R10G10B10A2_UNORM, + D3DDECLTYPE_DXGI_R11G11B10_FLOAT = 32 + DXGI_FORMAT_R11G11B10_FLOAT, + D3DDECLTYPE_DXGI_R8G8B8A8_SNORM = 32 + DXGI_FORMAT_R8G8B8A8_SNORM, + }; + +#pragma pack(push,4) + + struct D3DVERTEXELEMENT9 + { + uint16_t Stream; // Stream index + uint16_t Offset; // Offset in the stream in bytes + uint8_t Type; // Data type + uint8_t Method; // Processing method + uint8_t Usage; // Semantics + uint8_t UsageIndex; // Semantic index + }; + +#pragma pack(pop) + +//-------------------------------------------------------------------------------------- +// Hard Defines for the various structures +//-------------------------------------------------------------------------------------- + constexpr uint32_t SDKMESH_FILE_VERSION = 101; + constexpr uint32_t SDKMESH_FILE_VERSION_V2 = 200; + + constexpr uint32_t MAX_VERTEX_ELEMENTS = 32; + constexpr uint32_t MAX_VERTEX_STREAMS = 16; + constexpr uint32_t MAX_FRAME_NAME = 100; + constexpr uint32_t MAX_MESH_NAME = 100; + constexpr uint32_t MAX_SUBSET_NAME = 100; + constexpr uint32_t MAX_MATERIAL_NAME = 100; + constexpr uint32_t MAX_TEXTURE_NAME = MAX_PATH; + constexpr uint32_t MAX_MATERIAL_PATH = MAX_PATH; + constexpr uint32_t INVALID_FRAME = uint32_t(-1); + constexpr uint32_t INVALID_MESH = uint32_t(-1); + constexpr uint32_t INVALID_MATERIAL = uint32_t(-1); + constexpr uint32_t INVALID_SUBSET = uint32_t(-1); + constexpr uint32_t INVALID_ANIMATION_DATA = uint32_t(-1); + + //-------------------------------------------------------------------------------------- + // Enumerated Types. + //-------------------------------------------------------------------------------------- + enum SDKMESH_PRIMITIVE_TYPE + { + PT_TRIANGLE_LIST = 0, + PT_TRIANGLE_STRIP, + PT_LINE_LIST, + PT_LINE_STRIP, + PT_POINT_LIST, + PT_TRIANGLE_LIST_ADJ, + PT_TRIANGLE_STRIP_ADJ, + PT_LINE_LIST_ADJ, + PT_LINE_STRIP_ADJ, + PT_QUAD_PATCH_LIST, + PT_TRIANGLE_PATCH_LIST, + }; + + enum SDKMESH_INDEX_TYPE + { + IT_16BIT = 0, + IT_32BIT, + }; + + enum FRAME_TRANSFORM_TYPE + { + FTT_RELATIVE = 0, + FTT_ABSOLUTE, // This is not currently used but is here to support absolute transformations in the future + }; + + //-------------------------------------------------------------------------------------- + // Structures. + //-------------------------------------------------------------------------------------- +#pragma pack(push,8) + + struct SDKMESH_HEADER + { + //Basic Info and sizes + uint32_t Version; + uint8_t IsBigEndian; + uint64_t HeaderSize; + uint64_t NonBufferDataSize; + uint64_t BufferDataSize; + + //Stats + uint32_t NumVertexBuffers; + uint32_t NumIndexBuffers; + uint32_t NumMeshes; + uint32_t NumTotalSubsets; + uint32_t NumFrames; + uint32_t NumMaterials; + + //Offsets to Data + uint64_t VertexStreamHeadersOffset; + uint64_t IndexStreamHeadersOffset; + uint64_t MeshDataOffset; + uint64_t SubsetDataOffset; + uint64_t FrameDataOffset; + uint64_t MaterialDataOffset; + }; + + struct SDKMESH_VERTEX_BUFFER_HEADER + { + uint64_t NumVertices; + uint64_t SizeBytes; + uint64_t StrideBytes; + D3DVERTEXELEMENT9 Decl[MAX_VERTEX_ELEMENTS]; + uint64_t DataOffset; + }; + + struct SDKMESH_INDEX_BUFFER_HEADER + { + uint64_t NumIndices; + uint64_t SizeBytes; + uint32_t IndexType; + uint64_t DataOffset; + }; + + struct SDKMESH_MESH + { + char Name[MAX_MESH_NAME]; + uint8_t NumVertexBuffers; + uint32_t VertexBuffers[MAX_VERTEX_STREAMS]; + uint32_t IndexBuffer; + uint32_t NumSubsets; + uint32_t NumFrameInfluences; //aka bones + + DirectX::XMFLOAT3 BoundingBoxCenter; + DirectX::XMFLOAT3 BoundingBoxExtents; + + union + { + uint64_t SubsetOffset; + INT* pSubsets; + }; + union + { + uint64_t FrameInfluenceOffset; + uint32_t* pFrameInfluences; + }; + }; + + struct SDKMESH_SUBSET + { + char Name[MAX_SUBSET_NAME]; + uint32_t MaterialID; + uint32_t PrimitiveType; + uint64_t IndexStart; + uint64_t IndexCount; + uint64_t VertexStart; + uint64_t VertexCount; + }; + + struct SDKMESH_FRAME + { + char Name[MAX_FRAME_NAME]; + uint32_t Mesh; + uint32_t ParentFrame; + uint32_t ChildFrame; + uint32_t SiblingFrame; + DirectX::XMFLOAT4X4 Matrix; + uint32_t AnimationDataIndex; //Used to index which set of keyframes transforms this frame + }; + + struct SDKMESH_MATERIAL + { + char Name[MAX_MATERIAL_NAME]; + + // Use MaterialInstancePath + char MaterialInstancePath[MAX_MATERIAL_PATH]; + + // Or fall back to d3d8-type materials + char DiffuseTexture[MAX_TEXTURE_NAME]; + char NormalTexture[MAX_TEXTURE_NAME]; + char SpecularTexture[MAX_TEXTURE_NAME]; + + DirectX::XMFLOAT4 Diffuse; + DirectX::XMFLOAT4 Ambient; + DirectX::XMFLOAT4 Specular; + DirectX::XMFLOAT4 Emissive; + float Power; + + uint64_t Force64_1; + uint64_t Force64_2; + uint64_t Force64_3; + uint64_t Force64_4; + uint64_t Force64_5; + uint64_t Force64_6; + }; + + struct SDKMESH_MATERIAL_V2 + { + char Name[MAX_MATERIAL_NAME]; + + // PBR materials + char RMATexture[MAX_TEXTURE_NAME]; + char AlbedoTexture[MAX_TEXTURE_NAME]; + char NormalTexture[MAX_TEXTURE_NAME]; + char EmissiveTexture[MAX_TEXTURE_NAME]; + + float Alpha; + + char Reserved[60]; + + uint64_t Force64_1; + uint64_t Force64_2; + uint64_t Force64_3; + uint64_t Force64_4; + uint64_t Force64_5; + uint64_t Force64_6; + }; + + struct SDKANIMATION_FILE_HEADER + { + uint32_t Version; + uint8_t IsBigEndian; + uint32_t FrameTransformType; + uint32_t NumFrames; + uint32_t NumAnimationKeys; + uint32_t AnimationFPS; + uint64_t AnimationDataSize; + uint64_t AnimationDataOffset; + }; + + struct SDKANIMATION_DATA + { + DirectX::XMFLOAT3 Translation; + DirectX::XMFLOAT4 Orientation; + DirectX::XMFLOAT3 Scaling; + }; + + struct SDKANIMATION_FRAME_DATA + { + char FrameName[MAX_FRAME_NAME]; + uint64_t DataOffset; + }; + +#pragma pack(pop) + +} // namespace + +static_assert(sizeof(DXUT::D3DVERTEXELEMENT9) == 8, "Direct3D9 Decl structure size incorrect"); +static_assert(sizeof(DXUT::SDKMESH_HEADER)== 104, "SDK Mesh structure size incorrect"); +static_assert(sizeof(DXUT::SDKMESH_VERTEX_BUFFER_HEADER) == 288, "SDK Mesh structure size incorrect"); +static_assert(sizeof(DXUT::SDKMESH_INDEX_BUFFER_HEADER) == 32, "SDK Mesh structure size incorrect"); +static_assert(sizeof(DXUT::SDKMESH_MESH) == 224, "SDK Mesh structure size incorrect"); +static_assert(sizeof(DXUT::SDKMESH_SUBSET) == 144, "SDK Mesh structure size incorrect"); +static_assert(sizeof(DXUT::SDKMESH_FRAME) == 184, "SDK Mesh structure size incorrect"); +static_assert(sizeof(DXUT::SDKMESH_MATERIAL) == 1256, "SDK Mesh structure size incorrect"); +static_assert(sizeof(DXUT::SDKMESH_MATERIAL_V2) == sizeof(DXUT::SDKMESH_MATERIAL), "SDK Mesh structure size incorrect"); +static_assert(sizeof(DXUT::SDKANIMATION_FILE_HEADER) == 40, "SDK Mesh structure size incorrect"); +static_assert(sizeof(DXUT::SDKANIMATION_DATA) == 40, "SDK Mesh structure size incorrect"); +static_assert(sizeof(DXUT::SDKANIMATION_FRAME_DATA) == 112, "SDK Mesh structure size incorrect"); diff --git a/Common/DirectXTK12/Src/ScreenGrab.cpp b/Common/DirectXTK12/Src/ScreenGrab.cpp new file mode 100644 index 0000000..ca7344c --- /dev/null +++ b/Common/DirectXTK12/Src/ScreenGrab.cpp @@ -0,0 +1,856 @@ +//-------------------------------------------------------------------------------------- +// File: ScreenGrab.cpp +// +// Function for capturing a 2D texture and saving it to a file (aka a 'screenshot' +// when used on a Direct3D Render Target). +// +// Note these functions are useful as a light-weight runtime screen grabber. For +// full-featured texture capture, DDS writer, and texture processing pipeline, +// see the 'Texconv' sample and the 'DirectXTex' library. +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +// Does not capture 1D textures or 3D textures (volume maps) + +// Does not capture mipmap chains, only the top-most texture level is saved + +// For 2D array textures and cubemaps, it captures only the first image in the array + +#include "pch.h" + +#include "ScreenGrab.h" +#include "DirectXHelpers.h" + +#include "PlatformHelpers.h" +#include "DDS.h" +#include "LoaderHelpers.h" + +using Microsoft::WRL::ComPtr; +using namespace DirectX; +using namespace DirectX::LoaderHelpers; + +namespace +{ + //-------------------------------------------------------------------------------------- + HRESULT CaptureTexture(_In_ ID3D12Device* device, + _In_ ID3D12CommandQueue* pCommandQ, + _In_ ID3D12Resource* pSource, + UINT64 srcPitch, + const D3D12_RESOURCE_DESC& desc, + _COM_Outptr_ ID3D12Resource** pStaging, + D3D12_RESOURCE_STATES beforeState, + D3D12_RESOURCE_STATES afterState) noexcept + { + if (pStaging) + { + *pStaging = nullptr; + } + + if (!pCommandQ || !pSource || !pStaging) + return E_INVALIDARG; + + if (desc.Dimension != D3D12_RESOURCE_DIMENSION_TEXTURE2D) + { + DebugTrace("ERROR: ScreenGrab does not support 1D or volume textures. Consider using DirectXTex instead.\n"); + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + + if (desc.DepthOrArraySize > 1 || desc.MipLevels > 1) + { + DebugTrace("WARNING: ScreenGrab does not support 2D arrays, cubemaps, or mipmaps; only the first surface is written. Consider using DirectXTex instead.\n"); + } + + if (srcPitch > UINT32_MAX) + return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); + + const UINT numberOfPlanes = D3D12GetFormatPlaneCount(device, desc.Format); + if (numberOfPlanes != 1) + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + + D3D12_HEAP_PROPERTIES sourceHeapProperties; + HRESULT hr = pSource->GetHeapProperties(&sourceHeapProperties, nullptr); + if (SUCCEEDED(hr) && sourceHeapProperties.Type == D3D12_HEAP_TYPE_READBACK) + { + // Handle case where the source is already a staging texture we can use directly + *pStaging = pSource; + pSource->AddRef(); + return S_OK; + } + + // Create a command allocator + ComPtr commandAlloc; + hr = device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_GRAPHICS_PPV_ARGS(commandAlloc.GetAddressOf())); + if (FAILED(hr)) + return hr; + + SetDebugObjectName(commandAlloc.Get(), L"ScreenGrab"); + + // Spin up a new command list + ComPtr commandList; + hr = device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, commandAlloc.Get(), nullptr, IID_GRAPHICS_PPV_ARGS(commandList.GetAddressOf())); + if (FAILED(hr)) + return hr; + + SetDebugObjectName(commandList.Get(), L"ScreenGrab"); + + // Create a fence + ComPtr fence; + hr = device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_GRAPHICS_PPV_ARGS(fence.GetAddressOf())); + if (FAILED(hr)) + return hr; + + SetDebugObjectName(fence.Get(), L"ScreenGrab"); + + assert((srcPitch & 0xFF) == 0); + + const CD3DX12_HEAP_PROPERTIES defaultHeapProperties(D3D12_HEAP_TYPE_DEFAULT); + const CD3DX12_HEAP_PROPERTIES readBackHeapProperties(D3D12_HEAP_TYPE_READBACK); + + // Readback resources must be buffers + D3D12_RESOURCE_DESC bufferDesc = {}; + bufferDesc.DepthOrArraySize = 1; + bufferDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + bufferDesc.Flags = D3D12_RESOURCE_FLAG_NONE; + bufferDesc.Format = DXGI_FORMAT_UNKNOWN; + bufferDesc.Height = 1; + bufferDesc.Width = srcPitch * desc.Height; + bufferDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + bufferDesc.MipLevels = 1; + bufferDesc.SampleDesc.Count = 1; + + ComPtr copySource(pSource); + if (desc.SampleDesc.Count > 1) + { + // MSAA content must be resolved before being copied to a staging texture + auto descCopy = desc; + descCopy.SampleDesc.Count = 1; + descCopy.SampleDesc.Quality = 0; + descCopy.Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT; + + ComPtr pTemp; + hr = device->CreateCommittedResource( + &defaultHeapProperties, + D3D12_HEAP_FLAG_NONE, + &descCopy, + D3D12_RESOURCE_STATE_COPY_DEST, + nullptr, + IID_GRAPHICS_PPV_ARGS(pTemp.GetAddressOf())); + if (FAILED(hr)) + return hr; + + assert(pTemp); + + SetDebugObjectName(pTemp.Get(), L"ScreenGrab temporary"); + + const DXGI_FORMAT fmt = EnsureNotTypeless(desc.Format); + + D3D12_FEATURE_DATA_FORMAT_SUPPORT formatInfo = { fmt, D3D12_FORMAT_SUPPORT1_NONE, D3D12_FORMAT_SUPPORT2_NONE }; + hr = device->CheckFeatureSupport(D3D12_FEATURE_FORMAT_SUPPORT, &formatInfo, sizeof(formatInfo)); + if (FAILED(hr)) + return hr; + + if (!(formatInfo.Support1 & D3D12_FORMAT_SUPPORT1_TEXTURE2D)) + return E_FAIL; + + for (UINT item = 0; item < desc.DepthOrArraySize; ++item) + { + for (UINT level = 0; level < desc.MipLevels; ++level) + { + const UINT index = D3D12CalcSubresource(level, item, 0, desc.MipLevels, desc.DepthOrArraySize); + commandList->ResolveSubresource(pTemp.Get(), index, pSource, index, fmt); + } + } + + copySource = pTemp; + } + + // Create a staging texture + hr = device->CreateCommittedResource( + &readBackHeapProperties, + D3D12_HEAP_FLAG_NONE, + &bufferDesc, + D3D12_RESOURCE_STATE_COPY_DEST, + nullptr, + IID_GRAPHICS_PPV_ARGS(pStaging)); + if (FAILED(hr)) + return hr; + + SetDebugObjectName(*pStaging, L"ScreenGrab staging"); + + assert(*pStaging); + + // Transition the resource if necessary + TransitionResource(commandList.Get(), pSource, beforeState, D3D12_RESOURCE_STATE_COPY_SOURCE); + + // Get the copy target location + D3D12_PLACED_SUBRESOURCE_FOOTPRINT bufferFootprint = {}; + bufferFootprint.Footprint.Width = static_cast(desc.Width); + bufferFootprint.Footprint.Height = desc.Height; + bufferFootprint.Footprint.Depth = 1; + bufferFootprint.Footprint.RowPitch = static_cast(srcPitch); + bufferFootprint.Footprint.Format = desc.Format; + + const CD3DX12_TEXTURE_COPY_LOCATION copyDest(*pStaging, bufferFootprint); + const CD3DX12_TEXTURE_COPY_LOCATION copySrc(copySource.Get(), 0); + + // Copy the texture + commandList->CopyTextureRegion(©Dest, 0, 0, 0, ©Src, nullptr); + + // Transition the resource to the next state + TransitionResource(commandList.Get(), pSource, D3D12_RESOURCE_STATE_COPY_SOURCE, afterState); + + hr = commandList->Close(); + if (FAILED(hr)) + return hr; + + // Execute the command list + pCommandQ->ExecuteCommandLists(1, CommandListCast(commandList.GetAddressOf())); + + // Signal the fence + hr = pCommandQ->Signal(fence.Get(), 1); + if (FAILED(hr)) + return hr; + + // Block until the copy is complete + while (fence->GetCompletedValue() < 1) + SwitchToThread(); + + return S_OK; + } +} // anonymous namespace + + +//-------------------------------------------------------------------------------------- +_Use_decl_annotations_ +HRESULT DirectX::SaveDDSTextureToFile( + ID3D12CommandQueue* pCommandQ, + ID3D12Resource* pSource, + const wchar_t* fileName, + D3D12_RESOURCE_STATES beforeState, + D3D12_RESOURCE_STATES afterState) noexcept +{ + if (!fileName) + return E_INVALIDARG; + + ComPtr device; + pCommandQ->GetDevice(IID_GRAPHICS_PPV_ARGS(device.GetAddressOf())); + + // Get the size of the image +#if defined(_MSC_VER) || !defined(_WIN32) + const auto desc = pSource->GetDesc(); +#else + D3D12_RESOURCE_DESC tmpDesc; + const auto& desc = *pSource->GetDesc(&tmpDesc); +#endif + + if (desc.Width > UINT32_MAX) + return E_INVALIDARG; + + UINT64 totalResourceSize = 0; + UINT64 fpRowPitch = 0; + UINT fpRowCount = 0; + // Get the rowcount, pitch and size of the top mip + device->GetCopyableFootprints( + &desc, + 0, + 1, + 0, + nullptr, + &fpRowCount, + &fpRowPitch, + &totalResourceSize); + +#if (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) + // Round up the srcPitch to multiples of 1024 + const UINT64 dstRowPitch = (fpRowPitch + static_cast(D3D12XBOX_TEXTURE_DATA_PITCH_ALIGNMENT) - 1u) & ~(static_cast(D3D12XBOX_TEXTURE_DATA_PITCH_ALIGNMENT) - 1u); +#else + // Round up the srcPitch to multiples of 256 + const UINT64 dstRowPitch = (fpRowPitch + 255) & ~0xFFu; +#endif + + if (dstRowPitch > UINT32_MAX) + return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); + + ComPtr pStaging; + HRESULT hr = CaptureTexture(device.Get(), pCommandQ, pSource, dstRowPitch, desc, pStaging.GetAddressOf(), beforeState, afterState); + if (FAILED(hr)) + return hr; + + // Create file + ScopedHandle hFile(safe_handle(CreateFile2( + fileName, + GENERIC_WRITE, 0, CREATE_ALWAYS, + nullptr))); + if (!hFile) + return HRESULT_FROM_WIN32(GetLastError()); + + auto_delete_file delonfail(hFile.get()); + + // Setup header + constexpr size_t MAX_HEADER_SIZE = sizeof(uint32_t) + sizeof(DDS_HEADER) + sizeof(DDS_HEADER_DXT10); + uint8_t fileHeader[MAX_HEADER_SIZE] = {}; + + *reinterpret_cast(&fileHeader[0]) = DDS_MAGIC; + + auto header = reinterpret_cast(&fileHeader[0] + sizeof(uint32_t)); + size_t headerSize = sizeof(uint32_t) + sizeof(DDS_HEADER); + header->size = sizeof(DDS_HEADER); + header->flags = DDS_HEADER_FLAGS_TEXTURE | DDS_HEADER_FLAGS_MIPMAP; + header->height = desc.Height; + header->width = static_cast(desc.Width); + header->mipMapCount = 1; + header->caps = DDS_SURFACE_FLAGS_TEXTURE; + + // Try to use a legacy .DDS pixel format for better tools support, otherwise fallback to 'DX10' header extension + DDS_HEADER_DXT10* extHeader = nullptr; + switch (desc.Format) + { + case DXGI_FORMAT_R8G8B8A8_UNORM: memcpy(&header->ddspf, &DDSPF_A8B8G8R8, sizeof(DDS_PIXELFORMAT)); break; + case DXGI_FORMAT_R16G16_UNORM: memcpy(&header->ddspf, &DDSPF_G16R16, sizeof(DDS_PIXELFORMAT)); break; + case DXGI_FORMAT_R8G8_UNORM: memcpy(&header->ddspf, &DDSPF_A8L8, sizeof(DDS_PIXELFORMAT)); break; + case DXGI_FORMAT_R16_UNORM: memcpy(&header->ddspf, &DDSPF_L16, sizeof(DDS_PIXELFORMAT)); break; + case DXGI_FORMAT_R8_UNORM: memcpy(&header->ddspf, &DDSPF_L8, sizeof(DDS_PIXELFORMAT)); break; + case DXGI_FORMAT_A8_UNORM: memcpy(&header->ddspf, &DDSPF_A8, sizeof(DDS_PIXELFORMAT)); break; + case DXGI_FORMAT_R8G8_B8G8_UNORM: memcpy(&header->ddspf, &DDSPF_R8G8_B8G8, sizeof(DDS_PIXELFORMAT)); break; + case DXGI_FORMAT_G8R8_G8B8_UNORM: memcpy(&header->ddspf, &DDSPF_G8R8_G8B8, sizeof(DDS_PIXELFORMAT)); break; + case DXGI_FORMAT_BC1_UNORM: memcpy(&header->ddspf, &DDSPF_DXT1, sizeof(DDS_PIXELFORMAT)); break; + case DXGI_FORMAT_BC2_UNORM: memcpy(&header->ddspf, &DDSPF_DXT3, sizeof(DDS_PIXELFORMAT)); break; + case DXGI_FORMAT_BC3_UNORM: memcpy(&header->ddspf, &DDSPF_DXT5, sizeof(DDS_PIXELFORMAT)); break; + case DXGI_FORMAT_BC4_UNORM: memcpy(&header->ddspf, &DDSPF_BC4_UNORM, sizeof(DDS_PIXELFORMAT)); break; + case DXGI_FORMAT_BC4_SNORM: memcpy(&header->ddspf, &DDSPF_BC4_SNORM, sizeof(DDS_PIXELFORMAT)); break; + case DXGI_FORMAT_BC5_UNORM: memcpy(&header->ddspf, &DDSPF_BC5_UNORM, sizeof(DDS_PIXELFORMAT)); break; + case DXGI_FORMAT_BC5_SNORM: memcpy(&header->ddspf, &DDSPF_BC5_SNORM, sizeof(DDS_PIXELFORMAT)); break; + case DXGI_FORMAT_B5G6R5_UNORM: memcpy(&header->ddspf, &DDSPF_R5G6B5, sizeof(DDS_PIXELFORMAT)); break; + case DXGI_FORMAT_B5G5R5A1_UNORM: memcpy(&header->ddspf, &DDSPF_A1R5G5B5, sizeof(DDS_PIXELFORMAT)); break; + case DXGI_FORMAT_R8G8_SNORM: memcpy(&header->ddspf, &DDSPF_V8U8, sizeof(DDS_PIXELFORMAT)); break; + case DXGI_FORMAT_R8G8B8A8_SNORM: memcpy(&header->ddspf, &DDSPF_Q8W8V8U8, sizeof(DDS_PIXELFORMAT)); break; + case DXGI_FORMAT_R16G16_SNORM: memcpy(&header->ddspf, &DDSPF_V16U16, sizeof(DDS_PIXELFORMAT)); break; + case DXGI_FORMAT_B8G8R8A8_UNORM: memcpy(&header->ddspf, &DDSPF_A8R8G8B8, sizeof(DDS_PIXELFORMAT)); break; + case DXGI_FORMAT_B8G8R8X8_UNORM: memcpy(&header->ddspf, &DDSPF_X8R8G8B8, sizeof(DDS_PIXELFORMAT)); break; + case DXGI_FORMAT_YUY2: memcpy(&header->ddspf, &DDSPF_YUY2, sizeof(DDS_PIXELFORMAT)); break; + case DXGI_FORMAT_B4G4R4A4_UNORM: memcpy(&header->ddspf, &DDSPF_A4R4G4B4, sizeof(DDS_PIXELFORMAT)); break; + + // Legacy D3DX formats using D3DFMT enum value as FourCC + case DXGI_FORMAT_R32G32B32A32_FLOAT: header->ddspf.size = sizeof(DDS_PIXELFORMAT); header->ddspf.flags = DDS_FOURCC; header->ddspf.fourCC = 116; break; // D3DFMT_A32B32G32R32F + case DXGI_FORMAT_R16G16B16A16_FLOAT: header->ddspf.size = sizeof(DDS_PIXELFORMAT); header->ddspf.flags = DDS_FOURCC; header->ddspf.fourCC = 113; break; // D3DFMT_A16B16G16R16F + case DXGI_FORMAT_R16G16B16A16_UNORM: header->ddspf.size = sizeof(DDS_PIXELFORMAT); header->ddspf.flags = DDS_FOURCC; header->ddspf.fourCC = 36; break; // D3DFMT_A16B16G16R16 + case DXGI_FORMAT_R16G16B16A16_SNORM: header->ddspf.size = sizeof(DDS_PIXELFORMAT); header->ddspf.flags = DDS_FOURCC; header->ddspf.fourCC = 110; break; // D3DFMT_Q16W16V16U16 + case DXGI_FORMAT_R32G32_FLOAT: header->ddspf.size = sizeof(DDS_PIXELFORMAT); header->ddspf.flags = DDS_FOURCC; header->ddspf.fourCC = 115; break; // D3DFMT_G32R32F + case DXGI_FORMAT_R16G16_FLOAT: header->ddspf.size = sizeof(DDS_PIXELFORMAT); header->ddspf.flags = DDS_FOURCC; header->ddspf.fourCC = 112; break; // D3DFMT_G16R16F + case DXGI_FORMAT_R32_FLOAT: header->ddspf.size = sizeof(DDS_PIXELFORMAT); header->ddspf.flags = DDS_FOURCC; header->ddspf.fourCC = 114; break; // D3DFMT_R32F + case DXGI_FORMAT_R16_FLOAT: header->ddspf.size = sizeof(DDS_PIXELFORMAT); header->ddspf.flags = DDS_FOURCC; header->ddspf.fourCC = 111; break; // D3DFMT_R16F + + case DXGI_FORMAT_AI44: + case DXGI_FORMAT_IA44: + case DXGI_FORMAT_P8: + case DXGI_FORMAT_A8P8: + DebugTrace("ERROR: ScreenGrab does not support video textures. Consider using DirectXTex.\n"); + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + + default: + memcpy(&header->ddspf, &DDSPF_DX10, sizeof(DDS_PIXELFORMAT)); + + headerSize += sizeof(DDS_HEADER_DXT10); + extHeader = reinterpret_cast(fileHeader + sizeof(uint32_t) + sizeof(DDS_HEADER)); + extHeader->dxgiFormat = desc.Format; + extHeader->resourceDimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; + extHeader->arraySize = 1; + break; + } + + size_t rowPitch, slicePitch, rowCount; + hr = GetSurfaceInfo(static_cast(desc.Width), desc.Height, desc.Format, &slicePitch, &rowPitch, &rowCount); + if (FAILED(hr)) + return hr; + + if (rowPitch > UINT32_MAX || slicePitch > UINT32_MAX) + return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); + + if (IsCompressed(desc.Format)) + { + header->flags |= DDS_HEADER_FLAGS_LINEARSIZE; + header->pitchOrLinearSize = static_cast(slicePitch); + } + else + { + header->flags |= DDS_HEADER_FLAGS_PITCH; + header->pitchOrLinearSize = static_cast(rowPitch); + } + + // Setup pixels + std::unique_ptr pixels(new (std::nothrow) uint8_t[slicePitch]); + if (!pixels) + return E_OUTOFMEMORY; + + assert(fpRowCount == rowCount); + assert(fpRowPitch == rowPitch); + + const UINT64 imageSize = dstRowPitch * UINT64(rowCount); + if (imageSize > UINT32_MAX) + return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); + + void* pMappedMemory = nullptr; + D3D12_RANGE readRange = { 0, static_cast(imageSize) }; + D3D12_RANGE writeRange = { 0, 0 }; + hr = pStaging->Map(0, &readRange, &pMappedMemory); + if (FAILED(hr)) + return hr; + + auto sptr = static_cast(pMappedMemory); + if (!sptr) + { + pStaging->Unmap(0, &writeRange); + return E_POINTER; + } + + uint8_t* dptr = pixels.get(); + + const size_t msize = std::min(rowPitch, size_t(dstRowPitch)); + for (size_t h = 0; h < rowCount; ++h) + { + memcpy(dptr, sptr, msize); + sptr += dstRowPitch; + dptr += rowPitch; + } + + pStaging->Unmap(0, &writeRange); + + // Write header & pixels + DWORD bytesWritten; + if (!WriteFile(hFile.get(), fileHeader, static_cast(headerSize), &bytesWritten, nullptr)) + return HRESULT_FROM_WIN32(GetLastError()); + + if (bytesWritten != headerSize) + return E_FAIL; + + if (!WriteFile(hFile.get(), pixels.get(), static_cast(slicePitch), &bytesWritten, nullptr)) + return HRESULT_FROM_WIN32(GetLastError()); + + if (bytesWritten != slicePitch) + return E_FAIL; + + delonfail.clear(); + + return S_OK; +} + +//-------------------------------------------------------------------------------------- +namespace DirectX +{ + inline namespace DX12 + { + namespace Internal + { + extern IWICImagingFactory2* GetWIC() noexcept; + } + } +} + +_Use_decl_annotations_ +HRESULT DirectX::SaveWICTextureToFile( + ID3D12CommandQueue* pCommandQ, + ID3D12Resource* pSource, + REFGUID guidContainerFormat, + const wchar_t* fileName, + D3D12_RESOURCE_STATES beforeState, + D3D12_RESOURCE_STATES afterState, + const GUID* targetFormat, + std::function setCustomProps, + bool forceSRGB) +{ + using namespace DirectX::DX12::Internal; + + if (!fileName) + return E_INVALIDARG; + + ComPtr device; + pCommandQ->GetDevice(IID_GRAPHICS_PPV_ARGS(device.GetAddressOf())); + + // Get the size of the image +#if defined(_MSC_VER) || !defined(_WIN32) + const auto desc = pSource->GetDesc(); +#else + D3D12_RESOURCE_DESC tmpDesc; + const auto& desc = *pSource->GetDesc(&tmpDesc); +#endif + + if (desc.Width > UINT32_MAX) + return E_INVALIDARG; + + UINT64 totalResourceSize = 0; + UINT64 fpRowPitch = 0; + UINT fpRowCount = 0; + // Get the rowcount, pitch and size of the top mip + device->GetCopyableFootprints( + &desc, + 0, + 1, + 0, + nullptr, + &fpRowCount, + &fpRowPitch, + &totalResourceSize); + +#if (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) + // Round up the srcPitch to multiples of 1024 + UINT64 dstRowPitch = (fpRowPitch + static_cast(D3D12XBOX_TEXTURE_DATA_PITCH_ALIGNMENT) - 1u) & ~(static_cast(D3D12XBOX_TEXTURE_DATA_PITCH_ALIGNMENT) - 1u); +#else + // Round up the srcPitch to multiples of 256 (D3D12_TEXTURE_DATA_PITCH_ALIGNMENT) + const UINT64 dstRowPitch = (fpRowPitch + 255) & ~0xFFu; +#endif + + if (dstRowPitch > UINT32_MAX) + return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); + + ComPtr pStaging; + HRESULT hr = CaptureTexture(device.Get(), pCommandQ, pSource, dstRowPitch, desc, pStaging.GetAddressOf(), beforeState, afterState); + if (FAILED(hr)) + return hr; + + // Determine source format's WIC equivalent + WICPixelFormatGUID pfGuid = {}; + bool sRGB = forceSRGB; + 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: + DebugTrace("ERROR: ScreenGrab does not support all DXGI formats (%u). Consider using DirectXTex.\n", static_cast(desc.Format)); + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + + auto pWIC = GetWIC(); + if (!pWIC) + return E_NOINTERFACE; + + ComPtr stream; + hr = pWIC->CreateStream(stream.GetAddressOf()); + if (FAILED(hr)) + return hr; + + hr = stream->InitializeFromFilename(fileName, GENERIC_WRITE); + if (FAILED(hr)) + return hr; + + auto_delete_file_wic delonfail(stream, fileName); + + ComPtr encoder; + hr = pWIC->CreateEncoder(guidContainerFormat, nullptr, encoder.GetAddressOf()); + if (FAILED(hr)) + return hr; + + hr = encoder->Initialize(stream.Get(), WICBitmapEncoderNoCache); + if (FAILED(hr)) + return hr; + + ComPtr frame; + ComPtr props; + hr = encoder->CreateNewFrame(frame.GetAddressOf(), props.GetAddressOf()); + if (FAILED(hr)) + return hr; + + if (targetFormat && memcmp(&guidContainerFormat, &GUID_ContainerFormatBmp, sizeof(WICPixelFormatGUID)) == 0) + { + // Opt-in to the WIC2 support for writing 32-bit Windows BMP files with an alpha channel + PROPBAG2 option = {}; + option.pstrName = const_cast(L"EnableV5Header32bppBGRA"); + + VARIANT varValue; + varValue.vt = VT_BOOL; + varValue.boolVal = VARIANT_TRUE; + std::ignore = props->Write(1, &option, &varValue); + } + + if (setCustomProps) + { + setCustomProps(props.Get()); + } + + hr = frame->Initialize(props.Get()); + if (FAILED(hr)) + return hr; + + hr = frame->SetSize(static_cast(desc.Width), desc.Height); + if (FAILED(hr)) + return hr; + + hr = frame->SetResolution(72, 72); + if (FAILED(hr)) + return hr; + + // Pick a target format + WICPixelFormatGUID targetGuid = {}; + if (targetFormat) + { + targetGuid = *targetFormat; + } + else + { + // Screenshots don't typically include the alpha channel of the render target + switch (desc.Format) + { + case DXGI_FORMAT_R32G32B32A32_FLOAT: + case DXGI_FORMAT_R16G16B16A16_FLOAT: + targetGuid = GUID_WICPixelFormat96bppRGBFloat; // WIC 2 + break; + + case DXGI_FORMAT_R16G16B16A16_UNORM: targetGuid = GUID_WICPixelFormat48bppBGR; break; + case DXGI_FORMAT_B5G5R5A1_UNORM: targetGuid = GUID_WICPixelFormat16bppBGR555; break; + case DXGI_FORMAT_B5G6R5_UNORM: targetGuid = GUID_WICPixelFormat16bppBGR565; break; + + case DXGI_FORMAT_R32_FLOAT: + case DXGI_FORMAT_R16_FLOAT: + case DXGI_FORMAT_R16_UNORM: + case DXGI_FORMAT_R8_UNORM: + case DXGI_FORMAT_A8_UNORM: + targetGuid = GUID_WICPixelFormat8bppGray; + break; + + default: + targetGuid = GUID_WICPixelFormat24bppBGR; + break; + } + } + + hr = frame->SetPixelFormat(&targetGuid); + if (FAILED(hr)) + return hr; + + if (targetFormat && memcmp(targetFormat, &targetGuid, sizeof(WICPixelFormatGUID)) != 0) + { + // Requested output pixel format is not supported by the WIC codec + return E_FAIL; + } + + // Encode WIC metadata + ComPtr metawriter; + if (SUCCEEDED(frame->GetMetadataQueryWriter(metawriter.GetAddressOf()))) + { + PROPVARIANT value; + PropVariantInit(&value); + + value.vt = VT_LPSTR; + value.pszVal = const_cast("DirectXTK"); + + if (memcmp(&guidContainerFormat, &GUID_ContainerFormatPng, sizeof(GUID)) == 0) + { + // Set Software name + std::ignore = metawriter->SetMetadataByName(L"/tEXt/{str=Software}", &value); + + // Set sRGB chunk + if (sRGB) + { + value.vt = VT_UI1; + value.bVal = 0; + std::ignore = metawriter->SetMetadataByName(L"/sRGB/RenderingIntent", &value); + } + else + { + // add gAMA chunk with gamma 1.0 + value.vt = VT_UI4; + value.uintVal = 100000; // gama value * 100,000 -- i.e. gamma 1.0 + std::ignore = metawriter->SetMetadataByName(L"/gAMA/ImageGamma", &value); + + // remove sRGB chunk which is added by default. + std::ignore = metawriter->RemoveMetadataByName(L"/sRGB/RenderingIntent"); + } + } + #if (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) + else if (memcmp(&guidContainerFormat, &GUID_ContainerFormatJpeg, sizeof(GUID)) == 0) + { + // Set Software name + std::ignore = metawriter->SetMetadataByName(L"/app1/ifd/{ushort=305}", &value); + + if (sRGB) + { + // Set EXIF Colorspace of sRGB + value.vt = VT_UI2; + value.uiVal = 1; + std::ignore = metawriter->SetMetadataByName(L"/app1/ifd/exif/{ushort=40961}", &value); + } + } + else if (memcmp(&guidContainerFormat, &GUID_ContainerFormatTiff, sizeof(GUID)) == 0) + { + // Set Software name + std::ignore = metawriter->SetMetadataByName(L"/ifd/{ushort=305}", &value); + + if (sRGB) + { + // Set EXIF Colorspace of sRGB + value.vt = VT_UI2; + value.uiVal = 1; + std::ignore = metawriter->SetMetadataByName(L"/ifd/exif/{ushort=40961}", &value); + } + } + #else + else + { + // Set Software name + std::ignore = metawriter->SetMetadataByName(L"System.ApplicationName", &value); + + if (sRGB) + { + // Set EXIF Colorspace of sRGB + value.vt = VT_UI2; + value.uiVal = 1; + std::ignore = metawriter->SetMetadataByName(L"System.Image.ColorSpace", &value); + } + } + #endif + } + + const UINT64 imageSize = dstRowPitch * UINT64(desc.Height); + if (imageSize > UINT32_MAX) + return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); + + void* pMappedMemory = nullptr; + D3D12_RANGE readRange = { 0, static_cast(imageSize) }; + D3D12_RANGE writeRange = { 0, 0 }; + hr = pStaging->Map(0, &readRange, &pMappedMemory); + if (FAILED(hr)) + return hr; + + if (memcmp(&targetGuid, &pfGuid, sizeof(WICPixelFormatGUID)) != 0) + { + // Conversion required to write + ComPtr source; + hr = pWIC->CreateBitmapFromMemory(static_cast(desc.Width), desc.Height, + pfGuid, + static_cast(dstRowPitch), static_cast(imageSize), + static_cast(pMappedMemory), source.GetAddressOf()); + if (FAILED(hr)) + { + pStaging->Unmap(0, &writeRange); + return hr; + } + + ComPtr FC; + hr = pWIC->CreateFormatConverter(FC.GetAddressOf()); + if (FAILED(hr)) + { + pStaging->Unmap(0, &writeRange); + return hr; + } + + BOOL canConvert = FALSE; + hr = FC->CanConvert(pfGuid, targetGuid, &canConvert); + if (FAILED(hr) || !canConvert) + { + pStaging->Unmap(0, &writeRange); + return E_UNEXPECTED; + } + + hr = FC->Initialize(source.Get(), targetGuid, WICBitmapDitherTypeNone, nullptr, 0, WICBitmapPaletteTypeMedianCut); + if (FAILED(hr)) + { + pStaging->Unmap(0, &writeRange); + return hr; + } + + WICRect rect = { 0, 0, static_cast(desc.Width), static_cast(desc.Height) }; + hr = frame->WriteSource(FC.Get(), &rect); + } + else + { + // No conversion required + hr = frame->WritePixels(desc.Height, static_cast(dstRowPitch), static_cast(imageSize), static_cast(pMappedMemory)); + } + + pStaging->Unmap(0, &writeRange); + + if (FAILED(hr)) + return hr; + + hr = frame->Commit(); + if (FAILED(hr)) + return hr; + + hr = encoder->Commit(); + if (FAILED(hr)) + return hr; + + delonfail.clear(); + + return S_OK; +} + + +//-------------------------------------------------------------------------------------- +// Adapters for /Zc:wchar_t- clients + +#if defined(_MSC_VER) && !defined(_NATIVE_WCHAR_T_DEFINED) + +namespace DirectX +{ + HRESULT __cdecl SaveDDSTextureToFile( + _In_ ID3D12CommandQueue* pCommandQueue, + _In_ ID3D12Resource* pSource, + _In_z_ const __wchar_t* fileName, + D3D12_RESOURCE_STATES beforeState, + D3D12_RESOURCE_STATES afterState) noexcept + { + return SaveDDSTextureToFile(pCommandQueue, pSource, + reinterpret_cast(fileName), + beforeState, afterState); + } + + HRESULT __cdecl SaveWICTextureToFile( + _In_ ID3D12CommandQueue* pCommandQ, + _In_ ID3D12Resource* pSource, + REFGUID guidContainerFormat, + _In_z_ const __wchar_t* fileName, + D3D12_RESOURCE_STATES beforeState, + D3D12_RESOURCE_STATES afterState, + _In_opt_ const GUID* targetFormat, + _In_ std::function setCustomProps, + bool forceSRGB) + { + return SaveWICTextureToFile(pCommandQ, pSource, guidContainerFormat, + reinterpret_cast(fileName), + beforeState, afterState, targetFormat, setCustomProps, forceSRGB); + } +} + +#endif // !_NATIVE_WCHAR_T_DEFINED diff --git a/Common/DirectXTK12/Src/Shaders/AlphaTestEffect.fx b/Common/DirectXTK12/Src/Shaders/AlphaTestEffect.fx new file mode 100644 index 0000000..f084740 --- /dev/null +++ b/Common/DirectXTK12/Src/Shaders/AlphaTestEffect.fx @@ -0,0 +1,135 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 + + +Texture2D Texture : register(t0); +sampler Sampler : register(s0); + + +cbuffer Parameters : register(b0) +{ + float4 DiffuseColor : packoffset(c0); + float4 AlphaTest : packoffset(c1); + float3 FogColor : packoffset(c2); + float4 FogVector : packoffset(c3); + float4x4 WorldViewProj : packoffset(c4); +}; + +#include "Structures.fxh" +#include "Common.fxh" +#include "RootSig.fxh" + +// Vertex shader: basic. +[RootSignature(MainRS)] +VSOutputTx VSAlphaTest(VSInputTx vin) +{ + VSOutputTx vout; + + CommonVSOutput cout = ComputeCommonVSOutput(vin.Position); + SetCommonVSOutputParams; + + vout.TexCoord = vin.TexCoord; + + return vout; +} + + +// Vertex shader: no fog. +[RootSignature(MainRS)] +VSOutputTxNoFog VSAlphaTestNoFog(VSInputTx vin) +{ + VSOutputTxNoFog vout; + + CommonVSOutput cout = ComputeCommonVSOutput(vin.Position); + SetCommonVSOutputParamsNoFog; + + vout.TexCoord = vin.TexCoord; + + return vout; +} + + +// Vertex shader: vertex color. +[RootSignature(MainRS)] +VSOutputTx VSAlphaTestVc(VSInputTxVc vin) +{ + VSOutputTx vout; + + CommonVSOutput cout = ComputeCommonVSOutput(vin.Position); + SetCommonVSOutputParams; + + vout.TexCoord = vin.TexCoord; + vout.Diffuse *= vin.Color; + + return vout; +} + + +// Vertex shader: vertex color, no fog. +[RootSignature(MainRS)] +VSOutputTxNoFog VSAlphaTestVcNoFog(VSInputTxVc vin) +{ + VSOutputTxNoFog vout; + + CommonVSOutput cout = ComputeCommonVSOutput(vin.Position); + SetCommonVSOutputParamsNoFog; + + vout.TexCoord = vin.TexCoord; + vout.Diffuse *= vin.Color; + + return vout; +} + + +// Pixel shader: less/greater compare function. +[RootSignature(MainRS)] +float4 PSAlphaTestLtGt(PSInputTx pin) : SV_Target0 +{ + float4 color = Texture.Sample(Sampler, pin.TexCoord) * pin.Diffuse; + + clip((color.a < AlphaTest.x) ? AlphaTest.z : AlphaTest.w); + + ApplyFog(color, pin.Specular.w); + + return color; +} + + +// Pixel shader: less/greater compare function, no fog. +[RootSignature(MainRS)] +float4 PSAlphaTestLtGtNoFog(PSInputTxNoFog pin) : SV_Target0 +{ + float4 color = Texture.Sample(Sampler, pin.TexCoord) * pin.Diffuse; + + clip((color.a < AlphaTest.x) ? AlphaTest.z : AlphaTest.w); + + return color; +} + + +// Pixel shader: equal/notequal compare function. +[RootSignature(MainRS)] +float4 PSAlphaTestEqNe(PSInputTx pin) : SV_Target0 +{ + float4 color = Texture.Sample(Sampler, pin.TexCoord) * pin.Diffuse; + + clip((abs(color.a - AlphaTest.x) < AlphaTest.y) ? AlphaTest.z : AlphaTest.w); + + ApplyFog(color, pin.Specular.w); + + return color; +} + + +// Pixel shader: equal/notequal compare function, no fog. +[RootSignature(MainRS)] +float4 PSAlphaTestEqNeNoFog(PSInputTxNoFog pin) : SV_Target0 +{ + float4 color = Texture.Sample(Sampler, pin.TexCoord) * pin.Diffuse; + + clip((abs(color.a - AlphaTest.x) < AlphaTest.y) ? AlphaTest.z : AlphaTest.w); + + return color; +} diff --git a/Common/DirectXTK12/Src/Shaders/BasicEffect.fx b/Common/DirectXTK12/Src/Shaders/BasicEffect.fx new file mode 100644 index 0000000..43f3f2c --- /dev/null +++ b/Common/DirectXTK12/Src/Shaders/BasicEffect.fx @@ -0,0 +1,531 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 + + +Texture2D Texture : register(t0); +sampler Sampler : register(s0); + + +cbuffer Parameters : register(b0) +{ + float4 DiffuseColor : packoffset(c0); + float3 EmissiveColor : packoffset(c1); + float3 SpecularColor : packoffset(c2); + float SpecularPower : packoffset(c2.w); + + float3 LightDirection[3] : packoffset(c3); + float3 LightDiffuseColor[3] : packoffset(c6); + float3 LightSpecularColor[3] : packoffset(c9); + + float3 EyePosition : packoffset(c12); + + float3 FogColor : packoffset(c13); + float4 FogVector : packoffset(c14); + + float4x4 World : packoffset(c15); + float3x3 WorldInverseTranspose : packoffset(c19); + float4x4 WorldViewProj : packoffset(c22); +}; + + +#include "Structures.fxh" +#include "Common.fxh" +#include "RootSig.fxh" +#include "Lighting.fxh" +#include "Utilities.fxh" + + +// Vertex shader: basic. +[RootSignature(NoTextureRS)] +VSOutput VSBasic(VSInput vin) +{ + VSOutput vout; + + CommonVSOutput cout = ComputeCommonVSOutput(vin.Position); + SetCommonVSOutputParams; + + return vout; +} + + +// Vertex shader: no fog. +[RootSignature(NoTextureRS)] +VSOutputNoFog VSBasicNoFog(VSInput vin) +{ + VSOutputNoFog vout; + + CommonVSOutput cout = ComputeCommonVSOutput(vin.Position); + SetCommonVSOutputParamsNoFog; + + return vout; +} + + +// Vertex shader: vertex color. +[RootSignature(NoTextureRS)] +VSOutput VSBasicVc(VSInputVc vin) +{ + VSOutput vout; + + CommonVSOutput cout = ComputeCommonVSOutput(vin.Position); + SetCommonVSOutputParams; + + vout.Diffuse *= vin.Color; + + return vout; +} + + +// Vertex shader: vertex color, no fog. +[RootSignature(NoTextureRS)] +VSOutputNoFog VSBasicVcNoFog(VSInputVc vin) +{ + VSOutputNoFog vout; + + CommonVSOutput cout = ComputeCommonVSOutput(vin.Position); + SetCommonVSOutputParamsNoFog; + + vout.Diffuse *= vin.Color; + + return vout; +} + + +// Vertex shader: texture. +[RootSignature(MainRS)] +VSOutputTx VSBasicTx(VSInputTx vin) +{ + VSOutputTx vout; + + CommonVSOutput cout = ComputeCommonVSOutput(vin.Position); + SetCommonVSOutputParams; + + vout.TexCoord = vin.TexCoord; + + return vout; +} + + +// Vertex shader: texture, no fog. +[RootSignature(MainRS)] +VSOutputTxNoFog VSBasicTxNoFog(VSInputTx vin) +{ + VSOutputTxNoFog vout; + + CommonVSOutput cout = ComputeCommonVSOutput(vin.Position); + SetCommonVSOutputParamsNoFog; + + vout.TexCoord = vin.TexCoord; + + return vout; +} + + +// Vertex shader: texture + vertex color. +[RootSignature(MainRS)] +VSOutputTx VSBasicTxVc(VSInputTxVc vin) +{ + VSOutputTx vout; + + CommonVSOutput cout = ComputeCommonVSOutput(vin.Position); + SetCommonVSOutputParams; + + vout.TexCoord = vin.TexCoord; + vout.Diffuse *= vin.Color; + + return vout; +} + + +// Vertex shader: texture + vertex color, no fog. +[RootSignature(MainRS)] +VSOutputTxNoFog VSBasicTxVcNoFog(VSInputTxVc vin) +{ + VSOutputTxNoFog vout; + + CommonVSOutput cout = ComputeCommonVSOutput(vin.Position); + SetCommonVSOutputParamsNoFog; + + vout.TexCoord = vin.TexCoord; + vout.Diffuse *= vin.Color; + + return vout; +} + + +// Vertex shader: vertex lighting. +[RootSignature(NoTextureRS)] +VSOutput VSBasicVertexLighting(VSInputNm vin) +{ + VSOutput vout; + + CommonVSOutput cout = ComputeCommonVSOutputWithLighting(vin.Position, vin.Normal, 3); + SetCommonVSOutputParams; + + return vout; +} + +[RootSignature(NoTextureRS)] +VSOutput VSBasicVertexLightingBn(VSInputNm vin) +{ + VSOutput vout; + + float3 normal = BiasX2(vin.Normal); + + CommonVSOutput cout = ComputeCommonVSOutputWithLighting(vin.Position, normal, 3); + SetCommonVSOutputParams; + + return vout; +} + + +// Vertex shader: vertex lighting + vertex color. +[RootSignature(NoTextureRS)] +VSOutput VSBasicVertexLightingVc(VSInputNmVc vin) +{ + VSOutput vout; + + CommonVSOutput cout = ComputeCommonVSOutputWithLighting(vin.Position, vin.Normal, 3); + SetCommonVSOutputParams; + + vout.Diffuse *= vin.Color; + + return vout; +} + +[RootSignature(NoTextureRS)] +VSOutput VSBasicVertexLightingVcBn(VSInputNmVc vin) +{ + VSOutput vout; + + float3 normal = BiasX2(vin.Normal); + + CommonVSOutput cout = ComputeCommonVSOutputWithLighting(vin.Position, normal, 3); + SetCommonVSOutputParams; + + vout.Diffuse *= vin.Color; + + return vout; +} + + +// Vertex shader: vertex lighting + texture. +[RootSignature(MainRS)] +VSOutputTx VSBasicVertexLightingTx(VSInputNmTx vin) +{ + VSOutputTx vout; + + CommonVSOutput cout = ComputeCommonVSOutputWithLighting(vin.Position, vin.Normal, 3); + SetCommonVSOutputParams; + + vout.TexCoord = vin.TexCoord; + + return vout; +} + +[RootSignature(MainRS)] +VSOutputTx VSBasicVertexLightingTxBn(VSInputNmTx vin) +{ + VSOutputTx vout; + + float3 normal = BiasX2(vin.Normal); + + CommonVSOutput cout = ComputeCommonVSOutputWithLighting(vin.Position, normal, 3); + SetCommonVSOutputParams; + + vout.TexCoord = vin.TexCoord; + + return vout; +} + + +// Vertex shader: vertex lighting + texture + vertex color. +[RootSignature(MainRS)] +VSOutputTx VSBasicVertexLightingTxVc(VSInputNmTxVc vin) +{ + VSOutputTx vout; + + CommonVSOutput cout = ComputeCommonVSOutputWithLighting(vin.Position, vin.Normal, 3); + SetCommonVSOutputParams; + + vout.TexCoord = vin.TexCoord; + vout.Diffuse *= vin.Color; + + return vout; +} + +[RootSignature(MainRS)] +VSOutputTx VSBasicVertexLightingTxVcBn(VSInputNmTxVc vin) +{ + VSOutputTx vout; + + float3 normal = BiasX2(vin.Normal); + + CommonVSOutput cout = ComputeCommonVSOutputWithLighting(vin.Position, normal, 3); + SetCommonVSOutputParams; + + vout.TexCoord = vin.TexCoord; + vout.Diffuse *= vin.Color; + + return vout; +} + + +// Vertex shader: pixel lighting. +[RootSignature(NoTextureRS)] +VSOutputPixelLighting VSBasicPixelLighting(VSInputNm vin) +{ + VSOutputPixelLighting vout; + + CommonVSOutputPixelLighting cout = ComputeCommonVSOutputPixelLighting(vin.Position, vin.Normal); + SetCommonVSOutputParamsPixelLighting; + + vout.Diffuse = float4(1, 1, 1, DiffuseColor.a); + + return vout; +} + +[RootSignature(NoTextureRS)] +VSOutputPixelLighting VSBasicPixelLightingBn(VSInputNm vin) +{ + VSOutputPixelLighting vout; + + float3 normal = BiasX2(vin.Normal); + + CommonVSOutputPixelLighting cout = ComputeCommonVSOutputPixelLighting(vin.Position, normal); + SetCommonVSOutputParamsPixelLighting; + + vout.Diffuse = float4(1, 1, 1, DiffuseColor.a); + + return vout; +} + + +// Vertex shader: pixel lighting + vertex color. +[RootSignature(NoTextureRS)] +VSOutputPixelLighting VSBasicPixelLightingVc(VSInputNmVc vin) +{ + VSOutputPixelLighting vout; + + CommonVSOutputPixelLighting cout = ComputeCommonVSOutputPixelLighting(vin.Position, vin.Normal); + SetCommonVSOutputParamsPixelLighting; + + vout.Diffuse.rgb = vin.Color.rgb; + vout.Diffuse.a = vin.Color.a * DiffuseColor.a; + + return vout; +} + +[RootSignature(NoTextureRS)] +VSOutputPixelLighting VSBasicPixelLightingVcBn(VSInputNmVc vin) +{ + VSOutputPixelLighting vout; + + float3 normal = BiasX2(vin.Normal); + + CommonVSOutputPixelLighting cout = ComputeCommonVSOutputPixelLighting(vin.Position, normal); + SetCommonVSOutputParamsPixelLighting; + + vout.Diffuse.rgb = vin.Color.rgb; + vout.Diffuse.a = vin.Color.a * DiffuseColor.a; + + return vout; +} + + +// Vertex shader: pixel lighting + texture. +[RootSignature(MainRS)] +VSOutputPixelLightingTx VSBasicPixelLightingTx(VSInputNmTx vin) +{ + VSOutputPixelLightingTx vout; + + CommonVSOutputPixelLighting cout = ComputeCommonVSOutputPixelLighting(vin.Position, vin.Normal); + SetCommonVSOutputParamsPixelLighting; + + vout.Diffuse = float4(1, 1, 1, DiffuseColor.a); + vout.TexCoord = vin.TexCoord; + + return vout; +} + +[RootSignature(MainRS)] +VSOutputPixelLightingTx VSBasicPixelLightingTxBn(VSInputNmTx vin) +{ + VSOutputPixelLightingTx vout; + + float3 normal = BiasX2(vin.Normal); + + CommonVSOutputPixelLighting cout = ComputeCommonVSOutputPixelLighting(vin.Position, normal); + SetCommonVSOutputParamsPixelLighting; + + vout.Diffuse = float4(1, 1, 1, DiffuseColor.a); + vout.TexCoord = vin.TexCoord; + + return vout; +} + + +// Vertex shader: pixel lighting + texture + vertex color. +[RootSignature(MainRS)] +VSOutputPixelLightingTx VSBasicPixelLightingTxVc(VSInputNmTxVc vin) +{ + VSOutputPixelLightingTx vout; + + CommonVSOutputPixelLighting cout = ComputeCommonVSOutputPixelLighting(vin.Position, vin.Normal); + SetCommonVSOutputParamsPixelLighting; + + vout.Diffuse.rgb = vin.Color.rgb; + vout.Diffuse.a = vin.Color.a * DiffuseColor.a; + vout.TexCoord = vin.TexCoord; + + return vout; +} + +[RootSignature(MainRS)] +VSOutputPixelLightingTx VSBasicPixelLightingTxVcBn(VSInputNmTxVc vin) +{ + VSOutputPixelLightingTx vout; + + float3 normal = BiasX2(vin.Normal); + + CommonVSOutputPixelLighting cout = ComputeCommonVSOutputPixelLighting(vin.Position, normal); + SetCommonVSOutputParamsPixelLighting; + + vout.Diffuse.rgb = vin.Color.rgb; + vout.Diffuse.a = vin.Color.a * DiffuseColor.a; + vout.TexCoord = vin.TexCoord; + + return vout; +} + + +// Pixel shader: basic. +[RootSignature(NoTextureRS)] +float4 PSBasic(PSInput pin) : SV_Target0 +{ + float4 color = pin.Diffuse; + + ApplyFog(color, pin.Specular.w); + + return color; +} + + +// Pixel shader: no fog. +[RootSignature(NoTextureRS)] +float4 PSBasicNoFog(PSInputNoFog pin) : SV_Target0 +{ + return pin.Diffuse; +} + + +// Pixel shader: texture. +[RootSignature(MainRS)] +float4 PSBasicTx(PSInputTx pin) : SV_Target0 +{ + float4 color = Texture.Sample(Sampler, pin.TexCoord) * pin.Diffuse; + + ApplyFog(color, pin.Specular.w); + + return color; +} + + +// Pixel shader: texture, no fog. +[RootSignature(MainRS)] +float4 PSBasicTxNoFog(PSInputTxNoFog pin) : SV_Target0 +{ + return Texture.Sample(Sampler, pin.TexCoord) * pin.Diffuse; +} + + +// Pixel shader: vertex lighting. +[RootSignature(NoTextureRS)] +float4 PSBasicVertexLighting(PSInput pin) : SV_Target0 +{ + float4 color = pin.Diffuse; + + AddSpecular(color, pin.Specular.rgb); + ApplyFog(color, pin.Specular.w); + + return color; +} + + +// Pixel shader: vertex lighting, no fog. +[RootSignature(NoTextureRS)] +float4 PSBasicVertexLightingNoFog(PSInput pin) : SV_Target0 +{ + float4 color = pin.Diffuse; + + AddSpecular(color, pin.Specular.rgb); + + return color; +} + + +// Pixel shader: vertex lighting + texture. +[RootSignature(MainRS)] +float4 PSBasicVertexLightingTx(PSInputTx pin) : SV_Target0 +{ + float4 color = Texture.Sample(Sampler, pin.TexCoord) * pin.Diffuse; + + AddSpecular(color, pin.Specular.rgb); + ApplyFog(color, pin.Specular.w); + + return color; +} + + +// Pixel shader: vertex lighting + texture, no fog. +[RootSignature(MainRS)] +float4 PSBasicVertexLightingTxNoFog(PSInputTx pin) : SV_Target0 +{ + float4 color = Texture.Sample(Sampler, pin.TexCoord) * pin.Diffuse; + + AddSpecular(color, pin.Specular.rgb); + + return color; +} + + +// Pixel shader: pixel lighting. +[RootSignature(NoTextureRS)] +float4 PSBasicPixelLighting(PSInputPixelLighting pin) : SV_Target0 +{ + float4 color = pin.Diffuse; + + float3 eyeVector = normalize(EyePosition - pin.PositionWS.xyz); + float3 worldNormal = normalize(pin.NormalWS); + + ColorPair lightResult = ComputeLights(eyeVector, worldNormal, 3); + + color.rgb *= lightResult.Diffuse; + + AddSpecular(color, lightResult.Specular); + ApplyFog(color, pin.PositionWS.w); + + return color; +} + + +// Pixel shader: pixel lighting + texture. +[RootSignature(MainRS)] +float4 PSBasicPixelLightingTx(PSInputPixelLightingTx pin) : SV_Target0 +{ + float4 color = Texture.Sample(Sampler, pin.TexCoord) * pin.Diffuse; + + float3 eyeVector = normalize(EyePosition - pin.PositionWS.xyz); + float3 worldNormal = normalize(pin.NormalWS); + + ColorPair lightResult = ComputeLights(eyeVector, worldNormal, 3); + + color.rgb *= lightResult.Diffuse; + + AddSpecular(color, lightResult.Specular); + ApplyFog(color, pin.PositionWS.w); + + return color; +} diff --git a/Common/DirectXTK12/Src/Shaders/Common.fxh b/Common/DirectXTK12/Src/Shaders/Common.fxh new file mode 100644 index 0000000..a161594 --- /dev/null +++ b/Common/DirectXTK12/Src/Shaders/Common.fxh @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248926 +// http://go.microsoft.com/fwlink/?LinkId=248929 +// http://go.microsoft.com/fwlink/?LinkID=615561 + + +float ComputeFogFactor(float4 position) +{ + return saturate(dot(position, FogVector)); +} + + +void ApplyFog(inout float4 color, float fogFactor) +{ + color.rgb = lerp(color.rgb, FogColor * color.a, fogFactor); +} + + +void AddSpecular(inout float4 color, float3 specular) +{ + color.rgb += specular * color.a; +} + + +struct CommonVSOutput +{ + float4 Pos_ps; + float4 Diffuse; + float3 Specular; + float FogFactor; +}; + + +CommonVSOutput ComputeCommonVSOutput(float4 position) +{ + CommonVSOutput vout; + + vout.Pos_ps = mul(position, WorldViewProj); + vout.Diffuse = DiffuseColor; + vout.Specular = 0; + vout.FogFactor = ComputeFogFactor(position); + + return vout; +} + + +#define SetCommonVSOutputParams \ + vout.PositionPS = cout.Pos_ps; \ + vout.Diffuse = cout.Diffuse; \ + vout.Specular = float4(cout.Specular, cout.FogFactor); + + +#define SetCommonVSOutputParamsNoFog \ + vout.PositionPS = cout.Pos_ps; \ + vout.Diffuse = cout.Diffuse; diff --git a/Common/DirectXTK12/Src/Shaders/CompileShaders.cmd b/Common/DirectXTK12/Src/Shaders/CompileShaders.cmd new file mode 100644 index 0000000..86fd7f2 --- /dev/null +++ b/Common/DirectXTK12/Src/Shaders/CompileShaders.cmd @@ -0,0 +1,362 @@ +@echo off +rem Copyright (c) Microsoft Corporation. +rem Licensed under the MIT License. + +setlocal +set error=0 + +if %PROCESSOR_ARCHITECTURE%.==ARM64. (set FXCARCH=arm64) else (if %PROCESSOR_ARCHITECTURE%.==AMD64. (set FXCARCH=x64) else (set FXCARCH=x86)) + +set FXCOPTS=/nologo /WX /Ges /Zi /Zpc /Qstrip_reflect /Qstrip_debug + +if %1.==xbox. goto continuexbox +if %1.==dxil. goto continuedxil +if %1.==gxdk. goto continuegxdk +if %1.==. goto continuepc +echo usage: CompileShaders [xbox | dxil | gxdk] +exit /b + +:continuexbox +set XBOXPREFIX=XboxOne +set XBOXOPTS=/D__XBOX_DISABLE_SHADER_NAME_EMPLACEMENT +if NOT %2.==noprecompile. goto skipnoprecompile +set XBOXOPTS=%XBOXOPTS% /D__XBOX_DISABLE_PRECOMPILE=1 +:skipnoprecompile + +set XBOXFXC="%XboxOneXDKLatest%\xdk\FXC\amd64\FXC.exe" +if exist %XBOXFXC% goto continue +set XBOXFXC="%XboxOneXDKLatest%xdk\FXC\amd64\FXC.exe" +if exist %XBOXFXC% goto continue +set XBOXFXC="%XboxOneXDKBuild%xdk\FXC\amd64\FXC.exe" +if exist %XBOXFXC% goto continue +set XBOXFXC="%DurangoXDK%xdk\FXC\amd64\FXC.exe" +if not exist %XBOXFXC% goto needxdk +goto continue + +:continuegxdk +if %2.==scarlett. ( +set XBOXPREFIX=XboxGamingScarlett +set XBOXDXC="%GameDKLatest%\GXDK\bin\Scarlett\DXC.exe" +) else ( +set XBOXPREFIX=XboxGamingXboxOne +set XBOXDXC="%GameDKLatest%\GXDK\bin\XboxOne\DXC.exe" +) + +if exist %XBOXDXC% goto continue +set XBOXDXC="%GameDKLatest%\GXDK\bin\DXC.exe" +if not exist %XBOXDXC% goto needgxdk +goto continue + +:continuedxil +if defined DirectXShaderCompiler goto dxcviaenv +set PCDXC="%WindowsSdkVerBinPath%%FXCARCH%\dxc.exe" +if exist %PCDXC% goto continue +set PCDXC="%WindowsSdkBinPath%%WindowsSDKVersion%\%FXCARCH%\dxc.exe" +if exist %PCDXC% goto continue + +set PCDXC=dxc.exe +goto continue + +:dxcviaenv +set PCDXC="%DirectXShaderCompiler%" +if exist %PCDXC% goto continue +goto needdxil + +:continuepc +set PCOPTS= + +set PCFXC="%WindowsSdkVerBinPath%%FXCARCH%\fxc.exe" +if exist %PCFXC% goto continue +set PCFXC="%WindowsSdkBinPath%%WindowsSDKVersion%\%FXCARCH%\fxc.exe" +if exist %PCFXC% goto continue +set PCFXC="%WindowsSdkDir%bin\%WindowsSDKVersion%\%FXCARCH%\fxc.exe" +if exist %PCFXC% goto continue + +set PCFXC=fxc.exe + +:continue +if not defined CompileShadersOutput set CompileShadersOutput=Compiled +set StrTrim=%CompileShadersOutput%## +set StrTrim=%StrTrim: ##=% +set CompileShadersOutput=%StrTrim:##=% +@if not exist "%CompileShadersOutput%" mkdir "%CompileShadersOutput%" +call :CompileShader%1 AlphaTestEffect vs VSAlphaTest +call :CompileShader%1 AlphaTestEffect vs VSAlphaTestNoFog +call :CompileShader%1 AlphaTestEffect vs VSAlphaTestVc +call :CompileShader%1 AlphaTestEffect vs VSAlphaTestVcNoFog + +call :CompileShader%1 AlphaTestEffect ps PSAlphaTestLtGt +call :CompileShader%1 AlphaTestEffect ps PSAlphaTestLtGtNoFog +call :CompileShader%1 AlphaTestEffect ps PSAlphaTestEqNe +call :CompileShader%1 AlphaTestEffect ps PSAlphaTestEqNeNoFog + +call :CompileShader%1 BasicEffect vs VSBasic +call :CompileShader%1 BasicEffect vs VSBasicNoFog +call :CompileShader%1 BasicEffect vs VSBasicVc +call :CompileShader%1 BasicEffect vs VSBasicVcNoFog +call :CompileShader%1 BasicEffect vs VSBasicTx +call :CompileShader%1 BasicEffect vs VSBasicTxNoFog +call :CompileShader%1 BasicEffect vs VSBasicTxVc +call :CompileShader%1 BasicEffect vs VSBasicTxVcNoFog + +call :CompileShader%1 BasicEffect vs VSBasicVertexLighting +call :CompileShader%1 BasicEffect vs VSBasicVertexLightingBn +call :CompileShader%1 BasicEffect vs VSBasicVertexLightingVc +call :CompileShader%1 BasicEffect vs VSBasicVertexLightingVcBn +call :CompileShader%1 BasicEffect vs VSBasicVertexLightingTx +call :CompileShader%1 BasicEffect vs VSBasicVertexLightingTxBn +call :CompileShader%1 BasicEffect vs VSBasicVertexLightingTxVc +call :CompileShader%1 BasicEffect vs VSBasicVertexLightingTxVcBn + +call :CompileShader%1 BasicEffect vs VSBasicPixelLighting +call :CompileShader%1 BasicEffect vs VSBasicPixelLightingBn +call :CompileShader%1 BasicEffect vs VSBasicPixelLightingVc +call :CompileShader%1 BasicEffect vs VSBasicPixelLightingVcBn +call :CompileShader%1 BasicEffect vs VSBasicPixelLightingTx +call :CompileShader%1 BasicEffect vs VSBasicPixelLightingTxBn +call :CompileShader%1 BasicEffect vs VSBasicPixelLightingTxVc +call :CompileShader%1 BasicEffect vs VSBasicPixelLightingTxVcBn + +call :CompileShader%1 BasicEffect ps PSBasic +call :CompileShader%1 BasicEffect ps PSBasicNoFog +call :CompileShader%1 BasicEffect ps PSBasicTx +call :CompileShader%1 BasicEffect ps PSBasicTxNoFog + +call :CompileShader%1 BasicEffect ps PSBasicVertexLighting +call :CompileShader%1 BasicEffect ps PSBasicVertexLightingNoFog +call :CompileShader%1 BasicEffect ps PSBasicVertexLightingTx +call :CompileShader%1 BasicEffect ps PSBasicVertexLightingTxNoFog + +call :CompileShader%1 BasicEffect ps PSBasicPixelLighting +call :CompileShader%1 BasicEffect ps PSBasicPixelLightingTx + +call :CompileShader%1 DualTextureEffect vs VSDualTexture +call :CompileShader%1 DualTextureEffect vs VSDualTextureNoFog +call :CompileShader%1 DualTextureEffect vs VSDualTextureVc +call :CompileShader%1 DualTextureEffect vs VSDualTextureVcNoFog + +call :CompileShader%1 DualTextureEffect ps PSDualTexture +call :CompileShader%1 DualTextureEffect ps PSDualTextureNoFog + +call :CompileShader%1 EnvironmentMapEffect vs VSEnvMap +call :CompileShader%1 EnvironmentMapEffect vs VSEnvMapBn +call :CompileShader%1 EnvironmentMapEffect vs VSEnvMapFresnel +call :CompileShader%1 EnvironmentMapEffect vs VSEnvMapFresnelBn +call :CompileShader%1 EnvironmentMapEffect vs VSEnvMapPixelLighting +call :CompileShader%1 EnvironmentMapEffect vs VSEnvMapPixelLightingBn + +call :CompileShader%1 EnvironmentMapEffect ps PSEnvMap +call :CompileShader%1 EnvironmentMapEffect ps PSEnvMapNoFog +call :CompileShader%1 EnvironmentMapEffect ps PSEnvMapSpecular +call :CompileShader%1 EnvironmentMapEffect ps PSEnvMapSpecularNoFog +call :CompileShader%1 EnvironmentMapEffect ps PSEnvMapPixelLighting +call :CompileShader%1 EnvironmentMapEffect ps PSEnvMapPixelLightingNoFog +call :CompileShader%1 EnvironmentMapEffect ps PSEnvMapPixelLightingFresnel +call :CompileShader%1 EnvironmentMapEffect ps PSEnvMapPixelLightingFresnelNoFog + +call :CompileShader%1 EnvironmentMapEffect ps PSEnvMapSpherePixelLighting +call :CompileShader%1 EnvironmentMapEffect ps PSEnvMapSpherePixelLightingNoFog +call :CompileShader%1 EnvironmentMapEffect ps PSEnvMapSpherePixelLightingFresnel +call :CompileShader%1 EnvironmentMapEffect ps PSEnvMapSpherePixelLightingFresnelNoFog + +call :CompileShader%1 EnvironmentMapEffect ps PSEnvMapDualParabolaPixelLighting +call :CompileShader%1 EnvironmentMapEffect ps PSEnvMapDualParabolaPixelLightingNoFog +call :CompileShader%1 EnvironmentMapEffect ps PSEnvMapDualParabolaPixelLightingFresnel +call :CompileShader%1 EnvironmentMapEffect ps PSEnvMapDualParabolaPixelLightingFresnelNoFog + +call :CompileShader%1 SkinnedEffect vs VSSkinnedVertexLightingFourBones +call :CompileShader%1 SkinnedEffect vs VSSkinnedVertexLightingFourBonesBn + +call :CompileShader%1 SkinnedEffect vs VSSkinnedPixelLightingFourBones +call :CompileShader%1 SkinnedEffect vs VSSkinnedPixelLightingFourBonesBn + +call :CompileShader%1 SkinnedEffect ps PSSkinnedVertexLighting +call :CompileShader%1 SkinnedEffect ps PSSkinnedVertexLightingNoFog +call :CompileShader%1 SkinnedEffect ps PSSkinnedPixelLighting + +call :CompileShader%1 NormalMapEffect vs VSNormalPixelLightingTx +call :CompileShader%1 NormalMapEffect vs VSNormalPixelLightingTxBn +call :CompileShader%1 NormalMapEffect vs VSNormalPixelLightingTxVc +call :CompileShader%1 NormalMapEffect vs VSNormalPixelLightingTxVcBn + +call :CompileShader%1 NormalMapEffect vs VSNormalPixelLightingTxNoSpec +call :CompileShader%1 NormalMapEffect vs VSNormalPixelLightingTxNoSpecBn +call :CompileShader%1 NormalMapEffect vs VSNormalPixelLightingTxVcNoSpec +call :CompileShader%1 NormalMapEffect vs VSNormalPixelLightingTxVcNoSpecBn + +call :CompileShader%1 NormalMapEffect vs VSNormalPixelLightingTxInst +call :CompileShader%1 NormalMapEffect vs VSNormalPixelLightingTxBnInst +call :CompileShader%1 NormalMapEffect vs VSNormalPixelLightingTxVcInst +call :CompileShader%1 NormalMapEffect vs VSNormalPixelLightingTxVcBnInst + +call :CompileShader%1 NormalMapEffect vs VSNormalPixelLightingTxNoSpecInst +call :CompileShader%1 NormalMapEffect vs VSNormalPixelLightingTxNoSpecBnInst +call :CompileShader%1 NormalMapEffect vs VSNormalPixelLightingTxVcNoSpecInst +call :CompileShader%1 NormalMapEffect vs VSNormalPixelLightingTxVcNoSpecBnInst + +call :CompileShader%1 NormalMapEffect vs VSSkinnedPixelLightingTx +call :CompileShader%1 NormalMapEffect vs VSSkinnedPixelLightingTxBn +call :CompileShader%1 NormalMapEffect vs VSSkinnedPixelLightingTxNoSpec +call :CompileShader%1 NormalMapEffect vs VSSkinnedPixelLightingTxNoSpecBn + +call :CompileShader%1 NormalMapEffect ps PSNormalPixelLightingTx +call :CompileShader%1 NormalMapEffect ps PSNormalPixelLightingTxNoFog +call :CompileShader%1 NormalMapEffect ps PSNormalPixelLightingTxNoSpec +call :CompileShader%1 NormalMapEffect ps PSNormalPixelLightingTxNoFogSpec + +call :CompileShader%1 PBREffect vs VSConstant +call :CompileShader%1 PBREffect vs VSConstantInst +call :CompileShader%1 PBREffect vs VSConstantVelocity +call :CompileShader%1 PBREffect vs VSConstantBn +call :CompileShader%1 PBREffect vs VSConstantBnInst +call :CompileShader%1 PBREffect vs VSConstantVelocityBn +call :CompileShader%1 PBREffect vs VSSkinned +call :CompileShader%1 PBREffect vs VSSkinnedBn + +call :CompileShader%1 PBREffect ps PSConstant +call :CompileShader%1 PBREffect ps PSTextured +call :CompileShader%1 PBREffect ps PSTexturedEmissive +call :CompileShader%1 PBREffect ps PSTexturedVelocity +call :CompileShader%1 PBREffect ps PSTexturedEmissiveVelocity + +call :CompileShader%1 DebugEffect vs VSDebug +call :CompileShader%1 DebugEffect vs VSDebugBn +call :CompileShader%1 DebugEffect vs VSDebugVc +call :CompileShader%1 DebugEffect vs VSDebugVcBn + +call :CompileShader%1 DebugEffect vs VSDebugInst +call :CompileShader%1 DebugEffect vs VSDebugBnInst +call :CompileShader%1 DebugEffect vs VSDebugVcInst +call :CompileShader%1 DebugEffect vs VSDebugVcBnInst + +call :CompileShader%1 DebugEffect ps PSHemiAmbient +call :CompileShader%1 DebugEffect ps PSRGBNormals +call :CompileShader%1 DebugEffect ps PSRGBTangents +call :CompileShader%1 DebugEffect ps PSRGBBiTangents + +call :CompileShader%1 SpriteEffect vs SpriteVertexShader +call :CompileShader%1 SpriteEffect ps SpritePixelShader + +call :CompileShader%1 SpriteEffect vs SpriteVertexShaderHeap +call :CompileShader%1 SpriteEffect ps SpritePixelShaderHeap + +call :CompileShader%1 PostProcess vs VSQuad +call :CompileShader%1 PostProcess vs VSQuadNoCB +call :CompileShader%1 PostProcess vs VSQuadDual +call :CompileShader%1 PostProcess ps PSCopy +call :CompileShader%1 PostProcess ps PSMonochrome +call :CompileShader%1 PostProcess ps PSSepia +call :CompileShader%1 PostProcess ps PSDownScale2x2 +call :CompileShader%1 PostProcess ps PSDownScale4x4 +call :CompileShader%1 PostProcess ps PSGaussianBlur5x5 +call :CompileShader%1 PostProcess ps PSBloomExtract +call :CompileShader%1 PostProcess ps PSBloomBlur +call :CompileShader%1 PostProcess ps PSMerge +call :CompileShader%1 PostProcess ps PSBloomCombine + +call :CompileComputeShader%1 GenerateMips main + +call :CompileShader%1 ToneMap vs VSQuad +call :CompileShader%1 ToneMap ps PSCopy +call :CompileShader%1 ToneMap ps PSSaturate +call :CompileShader%1 ToneMap ps PSReinhard +call :CompileShader%1 ToneMap ps PSACESFilmic +call :CompileShader%1 ToneMap ps PS_SRGB +call :CompileShader%1 ToneMap ps PSSaturate_SRGB +call :CompileShader%1 ToneMap ps PSReinhard_SRGB +call :CompileShader%1 ToneMap ps PSACESFilmic_SRGB +call :CompileShader%1 ToneMap ps PSHDR10 + +if %1.==. goto skipxboxonly +if %1.==dxil. goto skipxboxonly + +call :CompileShader%1 ToneMap ps PSHDR10_Saturate +call :CompileShader%1 ToneMap ps PSHDR10_Reinhard +call :CompileShader%1 ToneMap ps PSHDR10_ACESFilmic +call :CompileShader%1 ToneMap ps PSHDR10_Saturate_SRGB +call :CompileShader%1 ToneMap ps PSHDR10_Reinhard_SRGB +call :CompileShader%1 ToneMap ps PSHDR10_ACESFilmic_SRGB + +:skipxboxonly + +echo. + +if %error% == 0 ( + echo Shaders compiled ok +) else ( + echo There were shader compilation errors! + exit /b 1 +) + +endlocal +exit /b 0 + +:CompileShader +set fxc=%PCFXC% "%1.fx" %FXCOPTS% /T%2_5_1 %PCOPTS% /E%3 "/Fh%CompileShadersOutput%\%1_%3.inc" "/Fd%CompileShadersOutput%\%1_%3.pdb" /Vn%1_%3 +echo. +echo %fxc% +%fxc% || set error=1 +exit /b + +:CompileComputeShader +set fxc=%PCFXC% "%1.hlsl" %FXCOPTS% /Tcs_5_1 %PCOPTS% /E%2 "/Fh%CompileShadersOutput%\%1_%2.inc" "/Fd%CompileShadersOutput%\%1_%2.pdb" /Vn%1_%2 +echo. +echo %fxc% +%fxc% || set error=1 +exit /b + +:CompileShaderdxil +set dxc=%PCDXC% "%1.fx" %FXCOPTS% /T%2_6_0 /E%3 "/Fh%CompileShadersOutput%\%1_%3.inc" "/Fd%CompileShadersOutput%\%1_%3.pdb" /Vn%1_%3 +echo. +echo %dxc% +%dxc% || set error=1 +exit /b + +:CompileComputeShaderdxil +set dxc=%PCDXC% "%1.hlsl" %FXCOPTS% /Tcs_6_0 /E%2 "/Fh%CompileShadersOutput%\%1_%2.inc" "/Fd%CompileShadersOutput%\%1_%2.pdb" /Vn%1_%2 +echo. +echo %dxc% +%dxc% || set error=1 +exit /b + +:CompileShaderxbox +set fxc=%XBOXFXC% "%1.fx" %FXCOPTS% /T%2_5_1 %XBOXOPTS% /E%3 "/Fh%CompileShadersOutput%\%XBOXPREFIX%%1_%3.inc" "/Fd%CompileShadersOutput%\%XBOXPREFIX%%1_%3.pdb" /Vn%1_%3 +echo. +echo %fxc% +%fxc% || set error=1 +exit /b + +:CompileComputeShaderxbox +set fxc==%XBOXFXC% "%1.hlsl" %FXCOPTS% /Tcs_5_1 %XBOXOPTS% /E%2 "/Fh%CompileShadersOutput%\%XBOXPREFIX%%1_%2.inc" "/Fd%CompileShadersOutput%\%XBOXPREFIX%%1_%2.pdb" /Vn%1_%2 +echo. +echo %fxc% +%fxc% || set error=1 +exit /b + +:CompileShadergxdk +set dxc=%XBOXDXC% "%1.fx" %FXCOPTS% -HV 2021 /T%2_6_0 /E%3 "/Fh%CompileShadersOutput%\%XBOXPREFIX%%1_%3.inc" "/Fd%CompileShadersOutput%\%XBOXPREFIX%%1_%3.pdb" /Vn%1_%3 +echo. +echo %dxc% +%dxc% || set error=1 +exit /b + +:CompileComputeShadergxdk +set dxc=%XBOXDXC% "%1.hlsl" %FXCOPTS% -HV 2021 /Tcs_6_0 /E%2 "/Fh%CompileShadersOutput%\%XBOXPREFIX%%1_%2.inc" "/Fd%CompileShadersOutput%\%XBOXPREFIX%%1_%2.pdb" /Vn%1_%2 +echo. +echo %dxc% +%dxc% || set error=1 +exit /b + +:needxdk +echo ERROR: CompileShaders xbox requires the Microsoft Xbox One XDK +echo (try re-running from the XDK Command Prompt) +exit /b 1 + +:needgxdk +echo ERROR: CompileShaders gxdk requires the Microsoft Gaming SDK +echo (try re-running from the Microsoft GDKX Gaming Command Prompt) +exit /b 1 + +:needdxil +echo ERROR: CompileShaders dxil requires DXC.EXE +exit /b 1 diff --git a/Common/DirectXTK12/Src/Shaders/DebugEffect.fx b/Common/DirectXTK12/Src/Shaders/DebugEffect.fx new file mode 100644 index 0000000..536f386 --- /dev/null +++ b/Common/DirectXTK12/Src/Shaders/DebugEffect.fx @@ -0,0 +1,217 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248929 + + +cbuffer Parameters : register(b0) +{ + float3 AmbientDown : packoffset(c0); + float Alpha : packoffset(c0.w); + float3 AmbientRange : packoffset(c1); + + float4x4 World : packoffset(c2); + float3x3 WorldInverseTranspose : packoffset(c6); + float4x4 WorldViewProj : packoffset(c9); +}; + + +#include "RootSig.fxh" +#include "Structures.fxh" +#include "Utilities.fxh" + + +// Vertex shader: basic +[RootSignature(DebugEffectRS)] +VSOutputPixelLightingTx VSDebug(VSInputNmTx vin) +{ + VSOutputPixelLightingTx vout; + + vout.PositionPS = mul(vin.Position, WorldViewProj); + vout.PositionWS = float4(mul(vin.Position, World).xyz, 1); + vout.NormalWS = normalize(mul(vin.Normal, WorldInverseTranspose)); + vout.Diffuse = float4(1, 1, 1, Alpha); + vout.TexCoord = vin.TexCoord; + + return vout; +} + +[RootSignature(DebugEffectRS)] +VSOutputPixelLightingTx VSDebugBn(VSInputNmTx vin) +{ + VSOutputPixelLightingTx vout; + + float3 normal = BiasX2(vin.Normal); + + vout.PositionPS = mul(vin.Position, WorldViewProj); + vout.PositionWS = float4(mul(vin.Position, World).xyz, 1); + vout.NormalWS = normalize(mul(normal, WorldInverseTranspose)); + vout.Diffuse = float4(1, 1, 1, Alpha); + vout.TexCoord = vin.TexCoord; + + return vout; +} + + +// Vertex shader: vertex color. +[RootSignature(DebugEffectRS)] +VSOutputPixelLightingTx VSDebugVc(VSInputNmTxVc vin) +{ + VSOutputPixelLightingTx vout; + + vout.PositionPS = mul(vin.Position, WorldViewProj); + vout.PositionWS = float4(mul(vin.Position, World).xyz, 1); + vout.NormalWS = normalize(mul(vin.Normal, WorldInverseTranspose)); + vout.Diffuse.rgb = vin.Color.rgb; + vout.Diffuse.a = vin.Color.a * Alpha; + vout.TexCoord = vin.TexCoord; + + return vout; +} + +[RootSignature(DebugEffectRS)] +VSOutputPixelLightingTx VSDebugVcBn(VSInputNmTxVc vin) +{ + VSOutputPixelLightingTx vout; + + float3 normal = BiasX2(vin.Normal); + + vout.PositionPS = mul(vin.Position, WorldViewProj); + vout.PositionWS = float4(mul(vin.Position, World).xyz, 1); + vout.NormalWS = normalize(mul(normal, WorldInverseTranspose)); + vout.Diffuse.rgb = vin.Color.rgb; + vout.Diffuse.a = vin.Color.a * Alpha; + vout.TexCoord = vin.TexCoord; + + return vout; +} + + +// Vertex shader: instancing +[RootSignature(DebugEffectRS)] +VSOutputPixelLightingTx VSDebugInst(VSInputNmTxInst vin) +{ + VSOutputPixelLightingTx vout; + + CommonInstancing inst = ComputeCommonInstancing(vin.Position, vin.Normal, vin.Transform); + + vout.PositionPS = mul(inst.Position, WorldViewProj); + vout.PositionWS = float4(mul(inst.Position, World).xyz, 1); + vout.NormalWS = normalize(mul(inst.Normal, WorldInverseTranspose)); + vout.Diffuse = float4(1, 1, 1, Alpha); + vout.TexCoord = vin.TexCoord; + + return vout; +} + +[RootSignature(DebugEffectRS)] +VSOutputPixelLightingTx VSDebugBnInst(VSInputNmTxInst vin) +{ + VSOutputPixelLightingTx vout; + + float3 normal = BiasX2(vin.Normal); + + CommonInstancing inst = ComputeCommonInstancing(vin.Position, normal, vin.Transform); + + vout.PositionPS = mul(inst.Position, WorldViewProj); + vout.PositionWS = float4(mul(inst.Position, World).xyz, 1); + vout.NormalWS = normalize(mul(inst.Normal, WorldInverseTranspose)); + vout.Diffuse = float4(1, 1, 1, Alpha); + vout.TexCoord = vin.TexCoord; + + return vout; +} + + +// Vertex shader: vertex color + instancing +[RootSignature(DebugEffectRS)] +VSOutputPixelLightingTx VSDebugVcInst(VSInputNmTxVcInst vin) +{ + VSOutputPixelLightingTx vout; + + CommonInstancing inst = ComputeCommonInstancing(vin.Position, vin.Normal, vin.Transform); + + vout.PositionPS = mul(inst.Position, WorldViewProj); + vout.PositionWS = float4(mul(inst.Position, World).xyz, 1); + vout.NormalWS = normalize(mul(inst.Normal, WorldInverseTranspose)); + vout.Diffuse.rgb = vin.Color.rgb; + vout.Diffuse.a = vin.Color.a * Alpha; + vout.TexCoord = vin.TexCoord; + + return vout; +} + +[RootSignature(DebugEffectRS)] +VSOutputPixelLightingTx VSDebugVcBnInst(VSInputNmTxVcInst vin) +{ + VSOutputPixelLightingTx vout; + + float3 normal = BiasX2(vin.Normal); + + CommonInstancing inst = ComputeCommonInstancing(vin.Position, normal, vin.Transform); + + vout.PositionPS = mul(inst.Position, WorldViewProj); + vout.PositionWS = float4(mul(inst.Position, World).xyz, 1); + vout.NormalWS = normalize(mul(inst.Normal, WorldInverseTranspose)); + vout.Diffuse.rgb = vin.Color.rgb; + vout.Diffuse.a = vin.Color.a * Alpha; + vout.TexCoord = vin.TexCoord; + + return vout; +} + + +// Pixel shader: default +float3 CalcHemiAmbient(float3 normal, float3 color) +{ + float3 up = BiasD2(normal); + float3 ambient = AmbientDown + up.y * AmbientRange; + return ambient * color; +} + +[RootSignature(DebugEffectRS)] +float4 PSHemiAmbient(PSInputPixelLightingTx pin) : SV_Target0 +{ + float3 normal = normalize(pin.NormalWS); + + // Do lighting + float3 color = CalcHemiAmbient(normal, pin.Diffuse.rgb); + + return float4(color, pin.Diffuse.a); +} + + +// Pixel shader: RGB normals +[RootSignature(DebugEffectRS)] +float4 PSRGBNormals(PSInputPixelLightingTx pin) : SV_Target0 +{ + float3 normal = normalize(pin.NormalWS); + + float3 color = BiasD2(normal); + + return float4(color, pin.Diffuse.a); +} + +// Pixel shader: RGB tangents +[RootSignature(DebugEffectRS)] +float4 PSRGBTangents(PSInputPixelLightingTx pin) : SV_Target0 +{ + const float3x3 TBN = CalculateTBN(pin.PositionWS.xyz, pin.NormalWS, pin.TexCoord); + float3 tangent = normalize(TBN[0]); + + float3 color = BiasD2(tangent); + + return float4(color, pin.Diffuse.a); +} + +// Pixel shader: RGB bi-tangents +[RootSignature(DebugEffectRS)] +float4 PSRGBBiTangents(PSInputPixelLightingTx pin) : SV_Target0 +{ + const float3x3 TBN = CalculateTBN(pin.PositionWS.xyz, pin.NormalWS, pin.TexCoord); + float3 bitangent = normalize(TBN[1]); + + float3 color = BiasD2(bitangent); + + return float4(color, pin.Diffuse.a); +} diff --git a/Common/DirectXTK12/Src/Shaders/DualTextureEffect.fx b/Common/DirectXTK12/Src/Shaders/DualTextureEffect.fx new file mode 100644 index 0000000..206af80 --- /dev/null +++ b/Common/DirectXTK12/Src/Shaders/DualTextureEffect.fx @@ -0,0 +1,121 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 + + +Texture2D Texture : register(t0); +Texture2D Texture2 : register(t1); + +sampler Sampler : register(s0); +sampler Sampler2 : register(s1); + + +cbuffer Parameters : register(b0) +{ + float4 DiffuseColor : packoffset(c0); + float3 FogColor : packoffset(c1); + float4 FogVector : packoffset(c2); + float4x4 WorldViewProj : packoffset(c3); +}; + + +#include "Structures.fxh" +#include "RootSig.fxh" +#include "Common.fxh" + + +// Vertex shader: basic. +[RootSignature(DualTextureRS)] +VSOutputTx2 VSDualTexture(VSInputTx2 vin) +{ + VSOutputTx2 vout; + + CommonVSOutput cout = ComputeCommonVSOutput(vin.Position); + SetCommonVSOutputParams; + + vout.TexCoord = vin.TexCoord; + vout.TexCoord2 = vin.TexCoord2; + + return vout; +} + + +// Vertex shader: no fog. +[RootSignature(DualTextureRS)] +VSOutputTx2NoFog VSDualTextureNoFog(VSInputTx2 vin) +{ + VSOutputTx2NoFog vout; + + CommonVSOutput cout = ComputeCommonVSOutput(vin.Position); + SetCommonVSOutputParamsNoFog; + + vout.TexCoord = vin.TexCoord; + vout.TexCoord2 = vin.TexCoord2; + + return vout; +} + + +// Vertex shader: vertex color. +[RootSignature(DualTextureRS)] +VSOutputTx2 VSDualTextureVc(VSInputTx2Vc vin) +{ + VSOutputTx2 vout; + + CommonVSOutput cout = ComputeCommonVSOutput(vin.Position); + SetCommonVSOutputParams; + + vout.TexCoord = vin.TexCoord; + vout.TexCoord2 = vin.TexCoord2; + vout.Diffuse *= vin.Color; + + return vout; +} + + +// Vertex shader: vertex color, no fog. +[RootSignature(DualTextureRS)] +VSOutputTx2NoFog VSDualTextureVcNoFog(VSInputTx2Vc vin) +{ + VSOutputTx2NoFog vout; + + CommonVSOutput cout = ComputeCommonVSOutput(vin.Position); + SetCommonVSOutputParamsNoFog; + + vout.TexCoord = vin.TexCoord; + vout.TexCoord2 = vin.TexCoord2; + vout.Diffuse *= vin.Color; + + return vout; +} + + +// Pixel shader: basic. +[RootSignature(DualTextureRS)] +float4 PSDualTexture(PSInputTx2 pin) : SV_Target0 +{ + float4 color = Texture.Sample(Sampler, pin.TexCoord); + float4 overlay = Texture2.Sample(Sampler2, pin.TexCoord2); + + color.rgb *= 2; + color *= overlay * pin.Diffuse; + + ApplyFog(color, pin.Specular.w); + + return color; +} + + +// Pixel shader: no fog. +[RootSignature(DualTextureRS)] +float4 PSDualTextureNoFog(PSInputTx2NoFog pin) : SV_Target0 +{ + float4 color = Texture.Sample(Sampler, pin.TexCoord); + float4 overlay = Texture2.Sample(Sampler2, pin.TexCoord2); + + color.rgb *= 2; + color *= overlay * pin.Diffuse; + + return color; +} diff --git a/Common/DirectXTK12/Src/Shaders/EnvironmentMapEffect.fx b/Common/DirectXTK12/Src/Shaders/EnvironmentMapEffect.fx new file mode 100644 index 0000000..a8d1b50 --- /dev/null +++ b/Common/DirectXTK12/Src/Shaders/EnvironmentMapEffect.fx @@ -0,0 +1,431 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 + + +Texture2D Texture : register(t0); +TextureCube EnvironmentMap : register(t1); +Texture2D SphereMap : register(t1); +Texture2DArray DualParabolaMap : register(t1); + +sampler Sampler : register(s0); +sampler EnvMapSampler : register(s1); + + +cbuffer Parameters : register(b0) +{ + float3 EnvironmentMapSpecular : packoffset(c0); + float EnvironmentMapAmount : packoffset(c1.x); + float FresnelFactor : packoffset(c1.y); + + float4 DiffuseColor : packoffset(c2); + float3 EmissiveColor : packoffset(c3); + + float3 LightDirection[3] : packoffset(c4); + float3 LightDiffuseColor[3] : packoffset(c7); + + float3 EyePosition : packoffset(c10); + + float3 FogColor : packoffset(c11); + float4 FogVector : packoffset(c12); + + float4x4 World : packoffset(c13); + float3x3 WorldInverseTranspose : packoffset(c17); + float4x4 WorldViewProj : packoffset(c20); +}; + + +// We don't use these parameters, but Lighting.fxh won't compile without them. +#define SpecularPower 0 +#define SpecularColor 0 +#define LightSpecularColor float3(0, 0, 0) + + +#include "Structures.fxh" +#include "Common.fxh" +#include "RootSig.fxh" +#include "Lighting.fxh" +#include "Utilities.fxh" + + +float ComputeFresnelFactor(float3 eyeVector, float3 worldNormal) +{ + float viewAngle = dot(eyeVector, worldNormal); + + return pow(max(1 - abs(viewAngle), 0), FresnelFactor) * EnvironmentMapAmount; +} + + +VSOutputTxEnvMap ComputeEnvMapVSOutput(VSInputNmTx vin, float3 normal, uniform bool useFresnel, uniform int numLights) +{ + VSOutputTxEnvMap vout; + + float4 pos_ws = mul(vin.Position, World); + float3 eyeVector = normalize(EyePosition - pos_ws.xyz); + float3 worldNormal = normalize(mul(normal, WorldInverseTranspose)); + + ColorPair lightResult = ComputeLights(eyeVector, worldNormal, numLights); + + vout.PositionPS = mul(vin.Position, WorldViewProj); + vout.Diffuse = float4(lightResult.Diffuse, DiffuseColor.a); + + if (useFresnel) + vout.Specular.rgb = ComputeFresnelFactor(eyeVector, worldNormal); + else + vout.Specular.rgb = EnvironmentMapAmount; + + vout.Specular.a = ComputeFogFactor(vin.Position); + vout.TexCoord = vin.TexCoord; + vout.EnvCoord = reflect(-eyeVector, worldNormal); + + return vout; +} + + +// Cubic environment mapping +// Greene, "Environment Mapping and Other Applications of World Projections", IEEE Computer Graphics and Applications. 1986. +float4 ComputeEnvMapPSOutput(PSInputPixelLightingTx pin, uniform bool useFresnel) +{ + float4 color = Texture.Sample(Sampler, pin.TexCoord) * pin.Diffuse; + + float3 eyeVector = normalize(EyePosition - pin.PositionWS.xyz); + float3 worldNormal = normalize(pin.NormalWS); + + ColorPair lightResult = ComputeLights(eyeVector, worldNormal, 3); + + color.rgb *= lightResult.Diffuse; + + float3 envcoord = reflect(-eyeVector, worldNormal); + + float4 envmap = EnvironmentMap.Sample(EnvMapSampler, envcoord) * color.a; + + float3 amount; + if (useFresnel) + amount = ComputeFresnelFactor(eyeVector, worldNormal); + else + amount = EnvironmentMapAmount; + + color.rgb = lerp(color.rgb, envmap.rgb, amount.rgb); + color.rgb += EnvironmentMapSpecular * envmap.a; + + return color; +} + + +// Spherical environment mapping +// Blinn & Newell, "Texture and Reflection in Computer Generated Images", Communications of the ACM. 1976. +float4 ComputeEnvMapSpherePSOutput(PSInputPixelLightingTx pin, uniform bool useFresnel) +{ + float4 color = Texture.Sample(Sampler, pin.TexCoord) * pin.Diffuse; + + float3 eyeVector = normalize(EyePosition - pin.PositionWS.xyz); + float3 worldNormal = normalize(pin.NormalWS); + + ColorPair lightResult = ComputeLights(eyeVector, worldNormal, 3); + + color.rgb *= lightResult.Diffuse; + + float3 r = reflect(-eyeVector, worldNormal); + float m = 2.0 * sqrt(r.x*r.x + r.y*r.y + (r.z + 1.0)*(r.z + 1.0)); + float2 envcoord = float2(r.x / m + 0.5, r.y / m + 0.5); + + float4 envmap = SphereMap.Sample(EnvMapSampler, envcoord) * color.a; + + float3 amount; + if (useFresnel) + amount = ComputeFresnelFactor(eyeVector, worldNormal); + else + amount = EnvironmentMapAmount; + + color.rgb = lerp(color.rgb, envmap.rgb, amount.rgb); + color.rgb += EnvironmentMapSpecular * envmap.a; + + return color; +} + + +// Dual-parabola environment mapping +// Heidrich & Seidel, "View-independent Environment Maps", Eurographics Workshop on Graphics Hardware, 1998. +float4 ComputeEnvMapDualParabolaPSOutput(PSInputPixelLightingTx pin, uniform bool useFresnel) +{ + float4 color = Texture.Sample(Sampler, pin.TexCoord) * pin.Diffuse; + + float3 eyeVector = normalize(EyePosition - pin.PositionWS.xyz); + float3 worldNormal = normalize(pin.NormalWS); + + ColorPair lightResult = ComputeLights(eyeVector, worldNormal, 3); + + color.rgb *= lightResult.Diffuse; + + float3 r = reflect(-eyeVector, worldNormal); + float m = 2.0 * (1.0 + abs(r.z)); + float3 envcoord = float3(r.x / m + 0.5, r.y / m + 0.5, (r.z > 0) ? 0 : 1); + + float4 envmap = DualParabolaMap.Sample(EnvMapSampler, envcoord) * color.a; + + float3 amount; + if (useFresnel) + amount = ComputeFresnelFactor(eyeVector, worldNormal); + else + amount = EnvironmentMapAmount; + + color.rgb = lerp(color.rgb, envmap.rgb, amount.rgb); + color.rgb += EnvironmentMapSpecular * envmap.a; + + return color; +} + + +// Vertex shader: basic. +[RootSignature(DualTextureRS)] +VSOutputTxEnvMap VSEnvMap(VSInputNmTx vin) +{ + return ComputeEnvMapVSOutput(vin, vin.Normal, false, 3); +} + +[RootSignature(DualTextureRS)] +VSOutputTxEnvMap VSEnvMapBn(VSInputNmTx vin) +{ + float3 normal = BiasX2(vin.Normal); + + return ComputeEnvMapVSOutput(vin, normal, false, 3); +} + + +// Vertex shader: fresnel. +[RootSignature(DualTextureRS)] +VSOutputTxEnvMap VSEnvMapFresnel(VSInputNmTx vin) +{ + return ComputeEnvMapVSOutput(vin, vin.Normal, true, 3); +} + +[RootSignature(DualTextureRS)] +VSOutputTxEnvMap VSEnvMapFresnelBn(VSInputNmTx vin) +{ + float3 normal = BiasX2(vin.Normal); + + return ComputeEnvMapVSOutput(vin, normal, true, 3); +} + + +// Vertex shader: pixel lighting. +[RootSignature(DualTextureRS)] +VSOutputPixelLightingTx VSEnvMapPixelLighting(VSInputNmTx vin) +{ + VSOutputPixelLightingTx vout; + + CommonVSOutputPixelLighting cout = ComputeCommonVSOutputPixelLighting(vin.Position, vin.Normal); + SetCommonVSOutputParamsPixelLighting; + + vout.Diffuse = float4(1, 1, 1, DiffuseColor.a); + vout.TexCoord = vin.TexCoord; + + return vout; +} + +[RootSignature(DualTextureRS)] +VSOutputPixelLightingTx VSEnvMapPixelLightingBn(VSInputNmTx vin) +{ + VSOutputPixelLightingTx vout; + + float3 normal = BiasX2(vin.Normal); + + CommonVSOutputPixelLighting cout = ComputeCommonVSOutputPixelLighting(vin.Position, normal); + SetCommonVSOutputParamsPixelLighting; + + vout.Diffuse = float4(1, 1, 1, DiffuseColor.a); + vout.TexCoord = vin.TexCoord; + + return vout; +} + + +// Pixel shader (cube mapping): basic. +[RootSignature(DualTextureRS)] +float4 PSEnvMap(PSInputTxEnvMap pin) : SV_Target0 +{ + float4 color = Texture.Sample(Sampler, pin.TexCoord) * pin.Diffuse; + float4 envmap = EnvironmentMap.Sample(EnvMapSampler, pin.EnvCoord) * color.a; + + color.rgb = lerp(color.rgb, envmap.rgb, pin.Specular.rgb); + + ApplyFog(color, pin.Specular.w); + + return color; +} + + +// Pixel shader (cube mapping): no fog. +[RootSignature(DualTextureRS)] +float4 PSEnvMapNoFog(PSInputTxEnvMap pin) : SV_Target0 +{ + float4 color = Texture.Sample(Sampler, pin.TexCoord) * pin.Diffuse; + float4 envmap = EnvironmentMap.Sample(EnvMapSampler, pin.EnvCoord) * color.a; + + color.rgb = lerp(color.rgb, envmap.rgb, pin.Specular.rgb); + + return color; +} + + +// Pixel shader (cube mapping): specular. +[RootSignature(DualTextureRS)] +float4 PSEnvMapSpecular(PSInputTxEnvMap pin) : SV_Target0 +{ + float4 color = Texture.Sample(Sampler, pin.TexCoord) * pin.Diffuse; + float4 envmap = EnvironmentMap.Sample(EnvMapSampler, pin.EnvCoord) * color.a; + + color.rgb = lerp(color.rgb, envmap.rgb, pin.Specular.rgb); + color.rgb += EnvironmentMapSpecular * envmap.a; + + ApplyFog(color, pin.Specular.w); + + return color; +} + + +// Pixel shader (cube mapping): specular, no fog. +[RootSignature(DualTextureRS)] +float4 PSEnvMapSpecularNoFog(PSInputTxEnvMap pin) : SV_Target0 +{ + float4 color = Texture.Sample(Sampler, pin.TexCoord) * pin.Diffuse; + float4 envmap = EnvironmentMap.Sample(EnvMapSampler, pin.EnvCoord) * color.a; + + color.rgb = lerp(color.rgb, envmap.rgb, pin.Specular.rgb); + color.rgb += EnvironmentMapSpecular * envmap.a; + + return color; +} + + +// Pixel shader (cube mapping): pixel lighting. +[RootSignature(DualTextureRS)] +float4 PSEnvMapPixelLighting(PSInputPixelLightingTx pin) : SV_Target0 +{ + float4 color = ComputeEnvMapPSOutput(pin, false); + + ApplyFog(color, pin.PositionWS.w); + + return color; +} + + +// Pixel shader (cube mapping): pixel lighting + no fog. +[RootSignature(DualTextureRS)] +float4 PSEnvMapPixelLightingNoFog(PSInputPixelLightingTx pin) : SV_Target0 +{ + float4 color = ComputeEnvMapPSOutput(pin, false); + + return color; +} + + +// Pixel shader (cube mapping): pixel lighting + fresnel +[RootSignature(DualTextureRS)] +float4 PSEnvMapPixelLightingFresnel(PSInputPixelLightingTx pin) : SV_Target0 +{ + float4 color = ComputeEnvMapPSOutput(pin, true); + + ApplyFog(color, pin.PositionWS.w); + + return color; +} + + +// Pixel shader (cube mapping): pixel lighting + fresnel + no fog. +[RootSignature(DualTextureRS)] +float4 PSEnvMapPixelLightingFresnelNoFog(PSInputPixelLightingTx pin) : SV_Target0 +{ + float4 color = ComputeEnvMapPSOutput(pin, true); + + return color; +} + + +// Pixel shader (sphere mapping): pixel lighting. +[RootSignature(DualTextureRS)] +float4 PSEnvMapSpherePixelLighting(PSInputPixelLightingTx pin) : SV_Target0 +{ + float4 color = ComputeEnvMapSpherePSOutput(pin, false); + + ApplyFog(color, pin.PositionWS.w); + + return color; +} + + +// Pixel shader (sphere mapping): pixel lighting + no fog. +[RootSignature(DualTextureRS)] +float4 PSEnvMapSpherePixelLightingNoFog(PSInputPixelLightingTx pin) : SV_Target0 +{ + float4 color = ComputeEnvMapSpherePSOutput(pin, false); + + return color; +} + + +// Pixel shader (sphere mapping): pixel lighting + fresnel +[RootSignature(DualTextureRS)] +float4 PSEnvMapSpherePixelLightingFresnel(PSInputPixelLightingTx pin) : SV_Target0 +{ + float4 color = ComputeEnvMapSpherePSOutput(pin, true); + + ApplyFog(color, pin.PositionWS.w); + + return color; +} + + +// Pixel shader (sphere mapping): pixel lighting + fresnel + no fog. +[RootSignature(DualTextureRS)] +float4 PSEnvMapSpherePixelLightingFresnelNoFog(PSInputPixelLightingTx pin) : SV_Target0 +{ + float4 color = ComputeEnvMapSpherePSOutput(pin, true); + + return color; +} + + +// Pixel shader (dual parabola mapping): pixel lighting. +[RootSignature(DualTextureRS)] +float4 PSEnvMapDualParabolaPixelLighting(PSInputPixelLightingTx pin) : SV_Target0 +{ + float4 color = ComputeEnvMapDualParabolaPSOutput(pin, false); + + ApplyFog(color, pin.PositionWS.w); + + return color; +} + + +// Pixel shader (dual parabola mapping): pixel lighting + no fog. +[RootSignature(DualTextureRS)] +float4 PSEnvMapDualParabolaPixelLightingNoFog(PSInputPixelLightingTx pin) : SV_Target0 +{ + float4 color = ComputeEnvMapDualParabolaPSOutput(pin, false); + + return color; +} + + +// Pixel shader (dual parabola mapping): pixel lighting + fresnel +[RootSignature(DualTextureRS)] +float4 PSEnvMapDualParabolaPixelLightingFresnel(PSInputPixelLightingTx pin) : SV_Target0 +{ + float4 color = ComputeEnvMapDualParabolaPSOutput(pin, true); + + ApplyFog(color, pin.PositionWS.w); + + return color; +} + + +// Pixel shader (dual parabola mapping): pixel lighting + fresnel + no fog. +[RootSignature(DualTextureRS)] +float4 PSEnvMapDualParabolaPixelLightingFresnelNoFog(PSInputPixelLightingTx pin) : SV_Target0 +{ + float4 color = ComputeEnvMapDualParabolaPSOutput(pin, true); + + return color; +} diff --git a/Common/DirectXTK12/Src/Shaders/GenerateMips.hlsl b/Common/DirectXTK12/Src/Shaders/GenerateMips.hlsl new file mode 100644 index 0000000..a34aae1 --- /dev/null +++ b/Common/DirectXTK12/Src/Shaders/GenerateMips.hlsl @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 + +#include "Structures.fxh" +#include "RootSig.fxh" + +SamplerState Sampler : register(s0); +Texture2D SrcMip : register(t0); +RWTexture2D OutMip : register(u0); + +cbuffer MipConstants : register(b0) +{ + float2 InvOutTexelSize; // texel size for OutMip (NOT SrcMip) + uint SrcMipIndex; +} + +float4 Mip(uint2 coord) +{ + float2 uv = (coord.xy + 0.5) * InvOutTexelSize; + return SrcMip.SampleLevel(Sampler, uv, SrcMipIndex); +} + +[RootSignature(GenerateMipsRS)] +[numthreads(8, 8, 1)] +void main(uint3 DTid : SV_DispatchThreadID) +{ + OutMip[DTid.xy] = Mip(DTid.xy); +} diff --git a/Common/DirectXTK12/Src/Shaders/Lighting.fxh b/Common/DirectXTK12/Src/Shaders/Lighting.fxh new file mode 100644 index 0000000..32f6671 --- /dev/null +++ b/Common/DirectXTK12/Src/Shaders/Lighting.fxh @@ -0,0 +1,95 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248926 +// http://go.microsoft.com/fwlink/?LinkId=248929 +// http://go.microsoft.com/fwlink/?LinkID=615561 + + +struct ColorPair +{ + float3 Diffuse; + float3 Specular; +}; + + +ColorPair ComputeLights(float3 eyeVector, float3 worldNormal, uniform int numLights) +{ + float3x3 lightDirections = 0; + float3x3 lightDiffuse = 0; + float3x3 lightSpecular = 0; + float3x3 halfVectors = 0; + + [unroll] + for (int i = 0; i < numLights; i++) + { + lightDirections[i] = LightDirection[i]; + lightDiffuse[i] = LightDiffuseColor[i]; + lightSpecular[i] = LightSpecularColor[i]; + + halfVectors[i] = normalize(eyeVector - lightDirections[i]); + } + + float3 dotL = mul(-lightDirections, worldNormal); + float3 dotH = mul(halfVectors, worldNormal); + + float3 zeroL = step(0, dotL); + + float3 diffuse = zeroL * dotL; + float3 specular = pow(max(dotH, 0) * zeroL, SpecularPower) * dotL; + + ColorPair result; + + result.Diffuse = mul(diffuse, lightDiffuse) * DiffuseColor.rgb + EmissiveColor; + result.Specular = mul(specular, lightSpecular) * SpecularColor; + + return result; +} + + +CommonVSOutput ComputeCommonVSOutputWithLighting(float4 position, float3 normal, uniform int numLights) +{ + CommonVSOutput vout; + + float4 pos_ws = mul(position, World); + float3 eyeVector = normalize(EyePosition - pos_ws.xyz); + float3 worldNormal = normalize(mul(normal, WorldInverseTranspose)); + + ColorPair lightResult = ComputeLights(eyeVector, worldNormal, numLights); + + vout.Pos_ps = mul(position, WorldViewProj); + vout.Diffuse = float4(lightResult.Diffuse, DiffuseColor.a); + vout.Specular = lightResult.Specular; + vout.FogFactor = ComputeFogFactor(position); + + return vout; +} + + +struct CommonVSOutputPixelLighting +{ + float4 Pos_ps; + float3 Pos_ws; + float3 Normal_ws; + float FogFactor; +}; + + +CommonVSOutputPixelLighting ComputeCommonVSOutputPixelLighting(float4 position, float3 normal) +{ + CommonVSOutputPixelLighting vout; + + vout.Pos_ps = mul(position, WorldViewProj); + vout.Pos_ws = mul(position, World).xyz; + vout.Normal_ws = normalize(mul(normal, WorldInverseTranspose)); + vout.FogFactor = ComputeFogFactor(position); + + return vout; +} + + +#define SetCommonVSOutputParamsPixelLighting \ + vout.PositionPS = cout.Pos_ps; \ + vout.PositionWS = float4(cout.Pos_ws, cout.FogFactor); \ + vout.NormalWS = cout.Normal_ws; + diff --git a/Common/DirectXTK12/Src/Shaders/NormalMapEffect.fx b/Common/DirectXTK12/Src/Shaders/NormalMapEffect.fx new file mode 100644 index 0000000..63fea68 --- /dev/null +++ b/Common/DirectXTK12/Src/Shaders/NormalMapEffect.fx @@ -0,0 +1,392 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 + + +Texture2D Texture : register(t0); +Texture2D NormalTexture : register(t1); +Texture2D SpecularTexture : register(t2); + +sampler Sampler : register(s0); + +cbuffer Parameters : register(b0) +{ + float4 DiffuseColor : packoffset(c0); + float3 EmissiveColor : packoffset(c1); + float3 SpecularColor : packoffset(c2); + float SpecularPower : packoffset(c2.w); + + float3 LightDirection[3] : packoffset(c3); + float3 LightDiffuseColor[3] : packoffset(c6); + float3 LightSpecularColor[3] : packoffset(c9); + + float3 EyePosition : packoffset(c12); + + float3 FogColor : packoffset(c13); + float4 FogVector : packoffset(c14); + + float4x4 World : packoffset(c15); + float3x3 WorldInverseTranspose : packoffset(c19); + float4x4 WorldViewProj : packoffset(c22); +}; + +cbuffer SkinningParameters : register(b1) +{ + float4x3 Bones[72]; +} + + +#include "Structures.fxh" +#include "Common.fxh" +#include "RootSig.fxh" +#include "Lighting.fxh" +#include "Utilities.fxh" +#include "Skinning.fxh" + + +// Vertex shader: pixel lighting + texture. +[RootSignature(NormalMapRS)] +VSOutputPixelLightingTx VSNormalPixelLightingTx(VSInputNmTx vin) +{ + VSOutputPixelLightingTx vout; + + CommonVSOutputPixelLighting cout = ComputeCommonVSOutputPixelLighting(vin.Position, vin.Normal); + SetCommonVSOutputParamsPixelLighting; + + vout.Diffuse = float4(1, 1, 1, DiffuseColor.a); + vout.TexCoord = vin.TexCoord; + + return vout; +} + +[RootSignature(NormalMapRSNoSpec)] +VSOutputPixelLightingTx VSNormalPixelLightingTxNoSpec(VSInputNmTx vin) +{ + return VSNormalPixelLightingTx(vin); +} + + +// Vertex shader: pixel lighting + texture (biased normal). +[RootSignature(NormalMapRS)] +VSOutputPixelLightingTx VSNormalPixelLightingTxBn(VSInputNmTx vin) +{ + VSOutputPixelLightingTx vout; + + float3 normal = BiasX2(vin.Normal); + + CommonVSOutputPixelLighting cout = ComputeCommonVSOutputPixelLighting(vin.Position, normal); + SetCommonVSOutputParamsPixelLighting; + + vout.Diffuse = float4(1, 1, 1, DiffuseColor.a); + vout.TexCoord = vin.TexCoord; + + return vout; +} + +[RootSignature(NormalMapRSNoSpec)] +VSOutputPixelLightingTx VSNormalPixelLightingTxNoSpecBn(VSInputNmTx vin) +{ + return VSNormalPixelLightingTxBn(vin); +} + + +// Vertex shader: pixel lighting + texture + vertex color. +[RootSignature(NormalMapRS)] +VSOutputPixelLightingTx VSNormalPixelLightingTxVc(VSInputNmTxVc vin) +{ + VSOutputPixelLightingTx vout; + + CommonVSOutputPixelLighting cout = ComputeCommonVSOutputPixelLighting(vin.Position, vin.Normal); + SetCommonVSOutputParamsPixelLighting; + + vout.Diffuse.rgb = vin.Color.rgb; + vout.Diffuse.a = vin.Color.a * DiffuseColor.a; + vout.TexCoord = vin.TexCoord; + + return vout; +} + +[RootSignature(NormalMapRSNoSpec)] +VSOutputPixelLightingTx VSNormalPixelLightingTxVcNoSpec(VSInputNmTxVc vin) +{ + return VSNormalPixelLightingTxVc(vin); +} + + +// Vertex shader: pixel lighting + texture + vertex color (biased normal). +[RootSignature(NormalMapRS)] +VSOutputPixelLightingTx VSNormalPixelLightingTxVcBn(VSInputNmTxVc vin) +{ + VSOutputPixelLightingTx vout; + + float3 normal = BiasX2(vin.Normal); + + CommonVSOutputPixelLighting cout = ComputeCommonVSOutputPixelLighting(vin.Position, normal); + SetCommonVSOutputParamsPixelLighting; + + vout.Diffuse.rgb = vin.Color.rgb; + vout.Diffuse.a = vin.Color.a * DiffuseColor.a; + vout.TexCoord = vin.TexCoord; + + return vout; +} + +[RootSignature(NormalMapRSNoSpec)] +VSOutputPixelLightingTx VSNormalPixelLightingTxVcNoSpecBn(VSInputNmTxVc vin) +{ + return VSNormalPixelLightingTxVcBn(vin); +} + + +// Vertex shader: pixel lighting + texture + instancing. +[RootSignature(NormalMapRS)] +VSOutputPixelLightingTx VSNormalPixelLightingTxInst(VSInputNmTxInst vin) +{ + VSOutputPixelLightingTx vout; + + CommonInstancing inst = ComputeCommonInstancing(vin.Position, vin.Normal, vin.Transform); + + CommonVSOutputPixelLighting cout = ComputeCommonVSOutputPixelLighting(inst.Position, inst.Normal); + SetCommonVSOutputParamsPixelLighting; + + vout.Diffuse = float4(1, 1, 1, DiffuseColor.a); + vout.TexCoord = vin.TexCoord; + + return vout; +} + +[RootSignature(NormalMapRSNoSpec)] +VSOutputPixelLightingTx VSNormalPixelLightingTxNoSpecInst(VSInputNmTxInst vin) +{ + return VSNormalPixelLightingTxInst(vin); +} + + +// Vertex shader: pixel lighting + texture + instancing (biased normal). +[RootSignature(NormalMapRS)] +VSOutputPixelLightingTx VSNormalPixelLightingTxBnInst(VSInputNmTxInst vin) +{ + VSOutputPixelLightingTx vout; + + float3 normal = BiasX2(vin.Normal); + + CommonInstancing inst = ComputeCommonInstancing(vin.Position, normal, vin.Transform); + + CommonVSOutputPixelLighting cout = ComputeCommonVSOutputPixelLighting(inst.Position, inst.Normal); + SetCommonVSOutputParamsPixelLighting; + + vout.Diffuse = float4(1, 1, 1, DiffuseColor.a); + vout.TexCoord = vin.TexCoord; + + return vout; +} + +[RootSignature(NormalMapRSNoSpec)] +VSOutputPixelLightingTx VSNormalPixelLightingTxNoSpecBnInst(VSInputNmTxInst vin) +{ + return VSNormalPixelLightingTxBnInst(vin); +} + + +// Vertex shader: pixel lighting + texture + vertex color + instancing. +[RootSignature(NormalMapRS)] +VSOutputPixelLightingTx VSNormalPixelLightingTxVcInst(VSInputNmTxVcInst vin) +{ + VSOutputPixelLightingTx vout; + + CommonInstancing inst = ComputeCommonInstancing(vin.Position, vin.Normal, vin.Transform); + + CommonVSOutputPixelLighting cout = ComputeCommonVSOutputPixelLighting(inst.Position, inst.Normal); + SetCommonVSOutputParamsPixelLighting; + + vout.Diffuse.rgb = vin.Color.rgb; + vout.Diffuse.a = vin.Color.a * DiffuseColor.a; + vout.TexCoord = vin.TexCoord; + + return vout; +} + +[RootSignature(NormalMapRSNoSpec)] +VSOutputPixelLightingTx VSNormalPixelLightingTxVcNoSpecInst(VSInputNmTxVcInst vin) +{ + return VSNormalPixelLightingTxVcInst(vin); +} + + +// Vertex shader: pixel lighting + texture + vertex color + instancing (biased normal). +[RootSignature(NormalMapRS)] +VSOutputPixelLightingTx VSNormalPixelLightingTxVcBnInst(VSInputNmTxVcInst vin) +{ + VSOutputPixelLightingTx vout; + + float3 normal = BiasX2(vin.Normal); + + CommonInstancing inst = ComputeCommonInstancing(vin.Position, normal, vin.Transform); + + CommonVSOutputPixelLighting cout = ComputeCommonVSOutputPixelLighting(inst.Position, inst.Normal); + SetCommonVSOutputParamsPixelLighting; + + vout.Diffuse.rgb = vin.Color.rgb; + vout.Diffuse.a = vin.Color.a * DiffuseColor.a; + vout.TexCoord = vin.TexCoord; + + return vout; +} + +[RootSignature(NormalMapRSNoSpec)] +VSOutputPixelLightingTx VSNormalPixelLightingTxVcNoSpecBnInst(VSInputNmTxVcInst vin) +{ + return VSNormalPixelLightingTxVcBnInst(vin); +} + + +// Vertex shader: skinning (four bones) + pixel lighting + texture +[RootSignature(NormalMapRS)] +VSOutputPixelLightingTx VSSkinnedPixelLightingTx(VSInputNmTxWeights vin) +{ + VSOutputPixelLightingTx vout; + + float3 normal = Skin(vin, vin.Normal, 4); + + CommonVSOutputPixelLighting cout = ComputeCommonVSOutputPixelLighting(vin.Position, normal); + SetCommonVSOutputParamsPixelLighting; + + vout.Diffuse = float4(1, 1, 1, DiffuseColor.a); + vout.TexCoord = vin.TexCoord; + + return vout; +} + +[RootSignature(NormalMapRSNoSpec)] +VSOutputPixelLightingTx VSSkinnedPixelLightingTxNoSpec(VSInputNmTxWeights vin) +{ + return VSSkinnedPixelLightingTx(vin); +} + +[RootSignature(NormalMapRS)] +VSOutputPixelLightingTx VSSkinnedPixelLightingTxBn(VSInputNmTxWeights vin) +{ + VSOutputPixelLightingTx vout; + + float3 normal = BiasX2(vin.Normal); + + normal = Skin(vin, normal, 4); + + CommonVSOutputPixelLighting cout = ComputeCommonVSOutputPixelLighting(vin.Position, normal); + SetCommonVSOutputParamsPixelLighting; + + vout.Diffuse = float4(1, 1, 1, DiffuseColor.a); + vout.TexCoord = vin.TexCoord; + + return vout; +} + +[RootSignature(NormalMapRSNoSpec)] +VSOutputPixelLightingTx VSSkinnedPixelLightingTxNoSpecBn(VSInputNmTxWeights vin) +{ + return VSSkinnedPixelLightingTxBn(vin); +} + + +// Pixel shader: pixel lighting + texture + no fog +[RootSignature(NormalMapRS)] +float4 PSNormalPixelLightingTxNoFog(PSInputPixelLightingTx pin) : SV_Target0 +{ + float4 color = Texture.Sample(Sampler, pin.TexCoord) * pin.Diffuse; + + float3 eyeVector = normalize(EyePosition - pin.PositionWS.xyz); + float3 worldNormal = normalize(pin.NormalWS); + + // Before lighting, peturb the surface's normal by the one given in normal map. + float3 localNormal = TwoChannelNormalX2(NormalTexture.Sample(Sampler, pin.TexCoord).xy); + float3 normal = PeturbNormal(localNormal, pin.PositionWS.xyz, worldNormal, pin.TexCoord); + + // Do lighting + ColorPair lightResult = ComputeLights(eyeVector, normal, 3); + + color.rgb *= lightResult.Diffuse; + + // Apply specular, modulated by the intensity given in the specular map + float3 specIntensity = SpecularTexture.Sample(Sampler, pin.TexCoord); + AddSpecular(color, lightResult.Specular * specIntensity); + + return color; +} + + +// Pixel shader: pixel lighting + texture +[RootSignature(NormalMapRS)] +float4 PSNormalPixelLightingTx(PSInputPixelLightingTx pin) : SV_Target0 +{ + float4 color = Texture.Sample(Sampler, pin.TexCoord) * pin.Diffuse; + + float3 eyeVector = normalize(EyePosition - pin.PositionWS.xyz); + float3 worldNormal = normalize(pin.NormalWS); + + // Before lighting, peturb the surface's normal by the one given in normal map. + float3 localNormal = TwoChannelNormalX2(NormalTexture.Sample(Sampler, pin.TexCoord).xy); + float3 normal = PeturbNormal(localNormal, pin.PositionWS.xyz, worldNormal, pin.TexCoord); + + // Do lighting + ColorPair lightResult = ComputeLights(eyeVector, normal, 3); + + color.rgb *= lightResult.Diffuse; + + // Apply specular, modulated by the intensity given in the specular map + float3 specIntensity = SpecularTexture.Sample(Sampler, pin.TexCoord); + AddSpecular(color, lightResult.Specular * specIntensity); + + ApplyFog(color, pin.PositionWS.w); + + return color; +} + + +// Pixel shader: pixel lighting + texture + no fog + no specular map +[RootSignature(NormalMapRSNoSpec)] +float4 PSNormalPixelLightingTxNoFogSpec(PSInputPixelLightingTx pin) : SV_Target0 +{ + float4 color = Texture.Sample(Sampler, pin.TexCoord) * pin.Diffuse; + + float3 eyeVector = normalize(EyePosition - pin.PositionWS.xyz); + float3 worldNormal = normalize(pin.NormalWS); + + // Before lighting, peturb the surface's normal by the one given in normal map. + float3 localNormal = TwoChannelNormalX2(NormalTexture.Sample(Sampler, pin.TexCoord).xy); + float3 normal = PeturbNormal(localNormal, pin.PositionWS.xyz, worldNormal, pin.TexCoord); + + // Do lighting + ColorPair lightResult = ComputeLights(eyeVector, normal, 3); + + color.rgb *= lightResult.Diffuse; + + AddSpecular(color, lightResult.Specular); + + return color; +} + + +// Pixel shader: pixel lighting + texture + no specular map +[RootSignature(NormalMapRSNoSpec)] +float4 PSNormalPixelLightingTxNoSpec(PSInputPixelLightingTx pin) : SV_Target0 +{ + float4 color = Texture.Sample(Sampler, pin.TexCoord) * pin.Diffuse; + + float3 eyeVector = normalize(EyePosition - pin.PositionWS.xyz); + float3 worldNormal = normalize(pin.NormalWS); + + // Before lighting, peturb the surface's normal by the one given in normal map. + float3 localNormal = TwoChannelNormalX2(NormalTexture.Sample(Sampler, pin.TexCoord).xy); + float3 normal = PeturbNormal(localNormal, pin.PositionWS.xyz, worldNormal, pin.TexCoord); + + // Do lighting + ColorPair lightResult = ComputeLights(eyeVector, normal, 3); + + color.rgb *= lightResult.Diffuse; + + AddSpecular(color, lightResult.Specular); + ApplyFog(color, pin.PositionWS.w); + + return color; +} diff --git a/Common/DirectXTK12/Src/Shaders/PBRCommon.fxh b/Common/DirectXTK12/Src/Shaders/PBRCommon.fxh new file mode 100644 index 0000000..df96eaf --- /dev/null +++ b/Common/DirectXTK12/Src/Shaders/PBRCommon.fxh @@ -0,0 +1,169 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248926 +// http://go.microsoft.com/fwlink/?LinkId=248929 +// http://go.microsoft.com/fwlink/?LinkID=615561 + + +struct CommonVSOutputPixelLighting +{ + float4 Pos_ps; + float3 Pos_ws; + float3 Normal_ws; +}; + +struct VSOut_Velocity +{ + VSOutputPixelLightingTx current; + float4 prevPosition : TEXCOORD4; +}; + +CommonVSOutputPixelLighting ComputeCommonVSOutputPixelLighting(float4 position, float3 normal) +{ + CommonVSOutputPixelLighting vout; + + vout.Pos_ps = mul(position, WorldViewProj); + vout.Pos_ws = mul(position, World).xyz; + vout.Normal_ws = normalize(mul(normal, WorldInverseTranspose)); + + return vout; +} + +static const float PI = 3.14159265f; +static const float EPSILON = 1e-6f; + +// Shlick's approximation of Fresnel +// https://en.wikipedia.org/wiki/Schlick%27s_approximation +float3 Fresnel_Shlick(in float3 f0, in float3 f90, in float x) +{ + return f0 + (f90 - f0) * pow(1.f - x, 5.f); +} + +// Burley B. "Physically Based Shading at Disney" +// SIGGRAPH 2012 Course: Practical Physically Based Shading in Film and Game Production, 2012. +float Diffuse_Burley(in float NdotL, in float NdotV, in float LdotH, in float roughness) +{ + float fd90 = 0.5f + 2.f * roughness * LdotH * LdotH; + return Fresnel_Shlick(1, fd90, NdotL).x * Fresnel_Shlick(1, fd90, NdotV).x; +} + +// GGX specular D (normal distribution) +// https://www.cs.cornell.edu/~srm/publications/EGSR07-btdf.pdf +float Specular_D_GGX(in float alpha, in float NdotH) +{ + const float alpha2 = alpha * alpha; + const float lower = (NdotH * NdotH * (alpha2 - 1)) + 1; + return alpha2 / max(EPSILON, PI * lower * lower); +} + +// Schlick-Smith specular G (visibility) with Hable's LdotH optimization +// http://www.cs.virginia.edu/~jdl/bib/appearance/analytic%20models/schlick94b.pdf +// http://graphicrants.blogspot.se/2013/08/specular-brdf-reference.html +float G_Shlick_Smith_Hable(float alpha, float LdotH) +{ + return rcp(lerp(LdotH * LdotH, 1, alpha * alpha * 0.25f)); +} + +// A microfacet based BRDF. +// +// alpha: This is roughness * roughness as in the "Disney" PBR model by Burley et al. +// +// specularColor: The F0 reflectance value - 0.04 for non-metals, or RGB for metals. This follows model +// used by Unreal Engine 4. +// +// NdotV, NdotL, LdotH, NdotH: vector relationships between, +// N - surface normal +// V - eye normal +// L - light normal +// H - half vector between L & V. +float3 Specular_BRDF(in float alpha, in float3 specularColor, in float NdotV, in float NdotL, in float LdotH, in float NdotH) +{ + // Specular D (microfacet normal distribution) component + float specular_D = Specular_D_GGX(alpha, NdotH); + + // Specular Fresnel + float3 specular_F = Fresnel_Shlick(specularColor, 1, LdotH); + + // Specular G (visibility) component + float specular_G = G_Shlick_Smith_Hable(alpha, LdotH); + + return specular_D * specular_F * specular_G; +} + +// Diffuse irradiance +float3 Diffuse_IBL(in float3 N) +{ + return IrradianceTexture.Sample(IBLSampler, N); +} + +// Approximate specular image based lighting by sampling radiance map at lower mips +// according to roughness, then modulating by Fresnel term. +float3 Specular_IBL(in float3 N, in float3 V, in float lodBias) +{ + float mip = lodBias * NumRadianceMipLevels; + float3 dir = reflect(-V, N); + return RadianceTexture.SampleLevel(IBLSampler, dir, mip); +} + +// Apply Disney-style physically based rendering to a surface with: +// +// V, N: Eye and surface normals +// +// numLights: Number of directional lights. +// +// lightColor[]: Color and intensity of directional light. +// +// lightDirection[]: Light direction. +float3 LightSurface( + in float3 V, in float3 N, + in int numLights, in float3 lightColor[3], in float3 lightDirection[3], + in float3 albedo, in float roughness, in float metallic, in float ambientOcclusion) +{ + // Specular coefficiant - fixed reflectance value for non-metals + static const float kSpecularCoefficient = 0.04; + + const float NdotV = saturate(dot(N, V)); + + // Burley roughness bias + const float alpha = roughness * roughness; + + // Blend base colors + const float3 c_diff = lerp(albedo, float3(0, 0, 0), metallic) * ambientOcclusion; + const float3 c_spec = lerp(kSpecularCoefficient, albedo, metallic) * ambientOcclusion; + + // Output color + float3 acc_color = 0; + + // Accumulate light values + for (int i = 0; i < numLights; i++) + { + // light vector (to light) + const float3 L = normalize(-lightDirection[i]); + + // Half vector + const float3 H = normalize(L + V); + + // products + const float NdotL = saturate(dot(N, L)); + const float LdotH = saturate(dot(L, H)); + const float NdotH = saturate(dot(N, H)); + + // Diffuse & specular factors + float diffuse_factor = Diffuse_Burley(NdotL, NdotV, LdotH, roughness); + float3 specular = Specular_BRDF(alpha, c_spec, NdotV, NdotL, LdotH, NdotH); + + // Directional light + acc_color += NdotL * lightColor[i] * (((c_diff * diffuse_factor) + specular)); + } + + // Add diffuse irradiance + float3 diffuse_env = Diffuse_IBL(N); + acc_color += c_diff * diffuse_env; + + // Add specular radiance + float3 specular_env = Specular_IBL(N, V, roughness); + acc_color += c_spec * specular_env; + + return acc_color; +} diff --git a/Common/DirectXTK12/Src/Shaders/PBREffect.fx b/Common/DirectXTK12/Src/Shaders/PBREffect.fx new file mode 100644 index 0000000..6adfb90 --- /dev/null +++ b/Common/DirectXTK12/Src/Shaders/PBREffect.fx @@ -0,0 +1,372 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 + + +Texture2D AlbedoTexture : register(t0); +Texture2D NormalTexture : register(t1); +Texture2D RMATexture : register(t2); + +Texture2D EmissiveTexture : register(t3); + +TextureCube RadianceTexture : register(t4); +TextureCube IrradianceTexture : register(t5); + +sampler SurfaceSampler : register(s0); +sampler IBLSampler : register(s1); + +cbuffer Constants : register(b0) +{ + float3 EyePosition : packoffset(c0); + float4x4 World : packoffset(c1); + float3x3 WorldInverseTranspose : packoffset(c5); + float4x4 WorldViewProj : packoffset(c8); + float4x4 PrevWorldViewProj : packoffset(c12); + + float3 LightDirection[3] : packoffset(c16); + float3 LightColor[3] : packoffset(c19); // "Specular and diffuse light" in PBR + + float3 ConstantAlbedo : packoffset(c22); // Constant values if not a textured effect + float Alpha : packoffset(c22.w); + float ConstantMetallic : packoffset(c23.x); + float ConstantRoughness : packoffset(c23.y); + + int NumRadianceMipLevels : packoffset(c23.z); + + // Size of render target + float TargetWidth : packoffset(c23.w); + float TargetHeight : packoffset(c24.x); +}; + +cbuffer SkinningParameters : register(b1) +{ + float4x3 Bones[72]; +} + + +#include "Structures.fxh" +#include "PBRCommon.fxh" +#include "RootSig.fxh" +#include "Utilities.fxh" +#include "Skinning.fxh" + + +// Vertex shader: pbr +[RootSignature(PBREffectRS)] +VSOutputPixelLightingTx VSConstant(VSInputNmTx vin) +{ + VSOutputPixelLightingTx vout; + + CommonVSOutputPixelLighting cout = ComputeCommonVSOutputPixelLighting(vin.Position, vin.Normal); + + vout.PositionPS = cout.Pos_ps; + vout.PositionWS = float4(cout.Pos_ws, 1); + vout.NormalWS = cout.Normal_ws; + vout.Diffuse = float4(ConstantAlbedo, Alpha); + vout.TexCoord = vin.TexCoord; + + return vout; +} + + +// Vertex shader: pbr + instancing +[RootSignature(PBREffectRS)] +VSOutputPixelLightingTx VSConstantInst(VSInputNmTxInst vin) +{ + VSOutputPixelLightingTx vout; + + CommonInstancing inst = ComputeCommonInstancing(vin.Position, vin.Normal, vin.Transform); + + CommonVSOutputPixelLighting cout = ComputeCommonVSOutputPixelLighting(inst.Position, inst.Normal); + + vout.PositionPS = cout.Pos_ps; + vout.PositionWS = float4(cout.Pos_ws, 1); + vout.NormalWS = cout.Normal_ws; + vout.Diffuse = float4(ConstantAlbedo, Alpha); + vout.TexCoord = vin.TexCoord; + + return vout; +} + + +// Vertex shader: pbr + velocity +[RootSignature(PBREffectRS)] +VSOut_Velocity VSConstantVelocity(VSInputNmTx vin) +{ + VSOut_Velocity vout; + + CommonVSOutputPixelLighting cout = ComputeCommonVSOutputPixelLighting(vin.Position, vin.Normal); + + vout.current.PositionPS = cout.Pos_ps; + vout.current.PositionWS = float4(cout.Pos_ws, 1); + vout.current.NormalWS = cout.Normal_ws; + vout.current.Diffuse = float4(ConstantAlbedo, Alpha); + vout.current.TexCoord = vin.TexCoord; + vout.prevPosition = mul(vin.Position, PrevWorldViewProj); + + return vout; +} + + +// Vertex shader: pbr (biased normal) +[RootSignature(PBREffectRS)] +VSOutputPixelLightingTx VSConstantBn(VSInputNmTx vin) +{ + VSOutputPixelLightingTx vout; + + float3 normal = BiasX2(vin.Normal); + + CommonVSOutputPixelLighting cout = ComputeCommonVSOutputPixelLighting(vin.Position, normal); + + vout.PositionPS = cout.Pos_ps; + vout.PositionWS = float4(cout.Pos_ws, 1); + vout.NormalWS = cout.Normal_ws; + vout.Diffuse = float4(ConstantAlbedo, Alpha); + vout.TexCoord = vin.TexCoord; + + return vout; +} + + +// Vertex shader: pbr + instancing (biased normal) +[RootSignature(PBREffectRS)] +VSOutputPixelLightingTx VSConstantBnInst(VSInputNmTxInst vin) +{ + VSOutputPixelLightingTx vout; + + float3 normal = BiasX2(vin.Normal); + + CommonInstancing inst = ComputeCommonInstancing(vin.Position, normal, vin.Transform); + + CommonVSOutputPixelLighting cout = ComputeCommonVSOutputPixelLighting(inst.Position, inst.Normal); + + vout.PositionPS = cout.Pos_ps; + vout.PositionWS = float4(cout.Pos_ws, 1); + vout.NormalWS = cout.Normal_ws; + vout.Diffuse = float4(ConstantAlbedo, Alpha); + vout.TexCoord = vin.TexCoord; + + return vout; +} + + +// Vertex shader: pbr + velocity (biased normal) +[RootSignature(PBREffectRS)] +VSOut_Velocity VSConstantVelocityBn(VSInputNmTx vin) +{ + VSOut_Velocity vout; + + float3 normal = BiasX2(vin.Normal); + + CommonVSOutputPixelLighting cout = ComputeCommonVSOutputPixelLighting(vin.Position, normal); + + vout.current.PositionPS = cout.Pos_ps; + vout.current.PositionWS = float4(cout.Pos_ws, 1); + vout.current.NormalWS = cout.Normal_ws; + vout.current.Diffuse = float4(ConstantAlbedo, Alpha); + vout.current.TexCoord = vin.TexCoord; + + vout.prevPosition = mul(vin.Position, PrevWorldViewProj); + + return vout; +} + + +// Vertex shader: pbr + skinning (four bones) +[RootSignature(PBREffectRS)] +VSOutputPixelLightingTx VSSkinned(VSInputNmTxWeights vin) +{ + VSOutputPixelLightingTx vout; + + float3 normal = Skin(vin, vin.Normal, 4); + + CommonVSOutputPixelLighting cout = ComputeCommonVSOutputPixelLighting(vin.Position, normal); + + vout.PositionPS = cout.Pos_ps; + vout.PositionWS = float4(cout.Pos_ws, 1); + vout.NormalWS = cout.Normal_ws; + vout.Diffuse = float4(ConstantAlbedo, Alpha); + vout.TexCoord = vin.TexCoord; + + return vout; +} + + +// Vertex shader: pbr + skinning (four bones) (biased normal) +[RootSignature(PBREffectRS)] +VSOutputPixelLightingTx VSSkinnedBn(VSInputNmTxWeights vin) +{ + VSOutputPixelLightingTx vout; + + float3 normal = BiasX2(vin.Normal); + + normal = Skin(vin, normal, 4); + + CommonVSOutputPixelLighting cout = ComputeCommonVSOutputPixelLighting(vin.Position, normal); + + vout.PositionPS = cout.Pos_ps; + vout.PositionWS = float4(cout.Pos_ws, 1); + vout.NormalWS = cout.Normal_ws; + vout.Diffuse = float4(ConstantAlbedo, Alpha); + vout.TexCoord = vin.TexCoord; + + return vout; +} + + +// Pixel shader: pbr (constants) + image-based lighting +[RootSignature(PBREffectRS)] +float4 PSConstant(PSInputPixelLightingTx pin) : SV_Target0 +{ + // vectors + const float3 V = normalize(EyePosition - pin.PositionWS.xyz); // view vector + const float3 N = normalize(pin.NormalWS); // surface normal + const float AO = 1; // ambient term + + float3 color = LightSurface(V, N, 3, + LightColor, LightDirection, + ConstantAlbedo, ConstantRoughness, ConstantMetallic, AO); + + return float4(color, Alpha); +} + + +// Pixel shader: pbr (textures) + image-based lighting +[RootSignature(PBREffectRS)] +float4 PSTextured(PSInputPixelLightingTx pin) : SV_Target0 +{ + const float3 V = normalize(EyePosition - pin.PositionWS.xyz); // view vector + const float3 L = normalize(-LightDirection[0]); // light vector ("to light" opposite of light's direction) + + // Before lighting, peturb the surface's normal by the one given in normal map. + float3 localNormal = TwoChannelNormalX2(NormalTexture.Sample(SurfaceSampler, pin.TexCoord).xy); + float3 N = PeturbNormal(localNormal, pin.PositionWS.xyz, pin.NormalWS, pin.TexCoord); + + // Get albedo + float4 albedo = AlbedoTexture.Sample(SurfaceSampler, pin.TexCoord); + + // Get roughness, metalness, and ambient occlusion + float3 RMA = RMATexture.Sample(SurfaceSampler, pin.TexCoord); + + // glTF2 defines metalness as B channel, roughness as G channel, and occlusion as R channel + + // Shade surface + float3 color = LightSurface(V, N, 3, LightColor, LightDirection, albedo.rgb, RMA.g, RMA.b, RMA.r); + + return float4(color, albedo.w * Alpha); +} + + +// Pixel shader: pbr (textures) + emissive + image-based lighting +[RootSignature(PBREffectRS)] +float4 PSTexturedEmissive(PSInputPixelLightingTx pin) : SV_Target0 +{ + const float3 V = normalize(EyePosition - pin.PositionWS.xyz); // view vector + const float3 L = normalize(-LightDirection[0]); // light vector ("to light" opposite of light's direction) + + // Before lighting, peturb the surface's normal by the one given in normal map. + float3 localNormal = TwoChannelNormalX2(NormalTexture.Sample(SurfaceSampler, pin.TexCoord).xy); + float3 N = PeturbNormal(localNormal, pin.PositionWS.xyz, pin.NormalWS, pin.TexCoord); + + // Get albedo + float4 albedo = AlbedoTexture.Sample(SurfaceSampler, pin.TexCoord); + + // Get roughness, metalness, and ambient occlusion + float3 RMA = RMATexture.Sample(SurfaceSampler, pin.TexCoord); + + // glTF2 defines metalness as B channel, roughness as G channel, and occlusion as R channel + + // Shade surface + float3 color = LightSurface(V, N, 3, LightColor, LightDirection, albedo.rgb, RMA.g, RMA.b, RMA.r); + + color += EmissiveTexture.Sample(SurfaceSampler, pin.TexCoord).rgb; + + return float4(color, albedo.w * Alpha); +} + + +// Pixel shader: pbr (textures) + image-based lighting + velocity +#include "PixelPacking_Velocity.hlsli" + +struct PSOut_Velocity +{ + float4 color : SV_Target0; + packed_velocity_t velocity : SV_Target1; +}; + +[RootSignature(PBREffectRS)] +PSOut_Velocity PSTexturedVelocity(VSOut_Velocity pin) +{ + PSOut_Velocity output; + + const float3 V = normalize(EyePosition - pin.current.PositionWS.xyz); // view vector + const float3 L = normalize(-LightDirection[0]); // light vector ("to light" opposite of light's direction) + + // Before lighting, peturb the surface's normal by the one given in normal map. + float3 localNormal = TwoChannelNormalX2(NormalTexture.Sample(SurfaceSampler, pin.current.TexCoord).xy); + float3 N = PeturbNormal(localNormal, pin.current.PositionWS.xyz, pin.current.NormalWS, pin.current.TexCoord); + + // Get albedo + float4 albedo = AlbedoTexture.Sample(SurfaceSampler, pin.current.TexCoord); + + // Get roughness, metalness, and ambient occlusion + float3 RMA = RMATexture.Sample(SurfaceSampler, pin.current.TexCoord); + + // glTF2 defines metalness as B channel, roughness as G channel, and occlusion as R channel + + // Shade surface + float3 color = LightSurface(V, N, 3, LightColor, LightDirection, albedo.rgb, RMA.g, RMA.b, RMA.r); + + output.color = float4(color, albedo.w * Alpha); + + // Calculate velocity of this point + float4 prevPos = pin.prevPosition; + prevPos.xyz /= prevPos.w; + prevPos.xy *= float2(0.5f, -0.5f); + prevPos.xy += 0.5f; + prevPos.xy *= float2(TargetWidth, TargetHeight); + + output.velocity = PackVelocity(prevPos.xyz - pin.current.PositionPS.xyz); + + return output; +} + +[RootSignature(PBREffectRS)] +PSOut_Velocity PSTexturedEmissiveVelocity(VSOut_Velocity pin) +{ + PSOut_Velocity output; + + const float3 V = normalize(EyePosition - pin.current.PositionWS.xyz); // view vector + const float3 L = normalize(-LightDirection[0]); // light vector ("to light" opposite of light's direction) + + // Before lighting, peturb the surface's normal by the one given in normal map. + float3 localNormal = TwoChannelNormalX2(NormalTexture.Sample(SurfaceSampler, pin.current.TexCoord).xy); + float3 N = PeturbNormal(localNormal, pin.current.PositionWS.xyz, pin.current.NormalWS, pin.current.TexCoord); + + // Get albedo + float4 albedo = AlbedoTexture.Sample(SurfaceSampler, pin.current.TexCoord); + + // Get roughness, metalness, and ambient occlusion + float3 RMA = RMATexture.Sample(SurfaceSampler, pin.current.TexCoord); + + // glTF2 defines metalness as B channel, roughness as G channel, and occlusion as R channel + + // Shade surface + float3 color = LightSurface(V, N, 3, LightColor, LightDirection, albedo.rgb, RMA.g, RMA.b, RMA.r); + + color += EmissiveTexture.Sample(SurfaceSampler, pin.current.TexCoord).rgb; + + output.color = float4(color, albedo.w * Alpha); + + // Calculate velocity of this point + float4 prevPos = pin.prevPosition; + prevPos.xyz /= prevPos.w; + prevPos.xy *= float2(0.5f, -0.5f); + prevPos.xy += 0.5f; + prevPos.xy *= float2(TargetWidth, TargetHeight); + + output.velocity = PackVelocity(prevPos.xyz - pin.current.PositionPS.xyz); + + return output; +} diff --git a/Common/DirectXTK12/Src/Shaders/PixelPacking_Velocity.hlsli b/Common/DirectXTK12/Src/Shaders/PixelPacking_Velocity.hlsli new file mode 100644 index 0000000..5daf6eb --- /dev/null +++ b/Common/DirectXTK12/Src/Shaders/PixelPacking_Velocity.hlsli @@ -0,0 +1,95 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +// Developed by Minigraph +// +// Author: James Stanard +// + +#ifndef __PIXEL_PACKING_VELOCITY_HLSLI__ +#define __PIXEL_PACKING_VELOCITY_HLSLI__ + +#if 1 +// This is a custom packing that devotes 10 bits each to X and Y velocity but 12 bits to Z velocity. Floats +// are used instead of SNORM to increase precision around small deltas, which are the majority of deltas. +// With TAA and Motion Blur, velocities are clamped, giving little reason to express them precisely in terms +// of the size of the screen. +#define packed_velocity_t uint + +// Designed to compress (-256.0, +256.0) with a signed 6e3 float +uint PackXY(float x) +{ + uint signbit = asuint(x) >> 31; + x = clamp(abs(x / 32768.0), 0, asfloat(0x3BFFE000)); + return (f32tof16(x) + 8) >> 4 | signbit << 9; +} + +float UnpackXY(uint x) +{ + return f16tof32((x & 0x1FF) << 4 | (x >> 9) << 15) * 32768.0; +} + +// Designed to compress (-1.0, 1.0) with a signed 8e3 float +uint PackZ(float x) +{ + uint signbit = asuint(x) >> 31; + x = clamp(abs(x / 128.0), 0, asfloat(0x3BFFE000)); + return (f32tof16(x) + 2) >> 2 | signbit << 11; +} + +float UnpackZ(uint x) +{ + return f16tof32((x & 0x7FF) << 2 | (x >> 11) << 15) * 128.0; +} + +// Pack the velocity to write to R10G10B10A2_UNORM +packed_velocity_t PackVelocity(float3 Velocity) +{ + return PackXY(Velocity.x) | PackXY(Velocity.y) << 10 | PackZ(Velocity.z) << 20; +} + +// Unpack the velocity from R10G10B10A2_UNORM +float3 UnpackVelocity(packed_velocity_t Velocity) +{ + return float3(UnpackXY(Velocity & 0x3FF), UnpackXY((Velocity >> 10) & 0x3FF), UnpackZ(Velocity >> 20)); +} + +#elif 1 +#define packed_velocity_t float4 + +// Pack the velocity to write to R10G10B10A2_UNORM +packed_velocity_t PackVelocity(float3 Velocity) +{ + // Stretch dx,dy from [-64, 63.875] to [-512, 511] to [-0.5, 0.5) to [0, 1) + // Velocity.xy = (0,0) must be representable. + return float4(Velocity * float3(8, 8, 4096) / 1024.0 + 512 / 1023.0, 0); +} + +// Unpack the velocity from R10G10B10A2_UNORM +float3 UnpackVelocity(packed_velocity_t Velocity) +{ + return (Velocity.xyz - 512.0 / 1023.0) * float3(1024, 1024, 2) / 8.0; +} +#else +#define packed_velocity_t float4 + +// Pack the velocity to write to R16G16B16A16_FLOAT +packed_velocity_t PackVelocity(float3 Velocity) +{ + return float4(Velocity * float3(16, 16, 32*1024), 0); +} + +// Unpack the velocity from R10G10B10A2_UNORM +float3 UnpackVelocity(packed_velocity_t Velocity) +{ + return Velocity.xyz / float3(16, 16, 32*1024); +} + +#endif + +#endif // __PIXEL_PACKING_HLSLI__ diff --git a/Common/DirectXTK12/Src/Shaders/PostProcess.fx b/Common/DirectXTK12/Src/Shaders/PostProcess.fx new file mode 100644 index 0000000..ce7f335 --- /dev/null +++ b/Common/DirectXTK12/Src/Shaders/PostProcess.fx @@ -0,0 +1,202 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 + +static const int MAX_SAMPLES = 16; + + +Texture2D Texture : register(t0); +sampler Sampler : register(s0); + + +cbuffer Parameters : register(b0) +{ + float4 sampleOffsets[MAX_SAMPLES]; + float4 sampleWeights[MAX_SAMPLES]; +}; + + +#include "Structures.fxh" +#include "RootSig.fxh" + + +// Vertex shader: self-created quad. +[RootSignature(PostProcessRS)] +VSInputTx VSQuad(uint vI : SV_VertexId) +{ + VSInputTx vout; + + // We use the 'big triangle' optimization so you only Draw 3 verticies instead of 4. + float2 texcoord = float2((vI << 1) & 2, vI & 2); + vout.TexCoord = texcoord; + + vout.Position = float4(texcoord.x * 2 - 1, -texcoord.y * 2 + 1, 0, 1); + return vout; +} + +[RootSignature(PostProcessRSNoCB)] +VSInputTx VSQuadNoCB(uint vI : SV_VertexId) +{ + return VSQuad(vI); +} + +[RootSignature(DualPostProcessRS)] +VSInputTx VSQuadDual(uint vI : SV_VertexId) +{ + return VSQuad(vI); +} + + +//-------------------------------------------------------------------------------------- +// Pixel shader: copy. +[RootSignature(PostProcessRSNoCB)] +float4 PSCopy(VSInputTx pin) : SV_Target0 +{ + float4 color = Texture.Sample(Sampler, pin.TexCoord); + return color; +} + + +// Pixel shader: monochrome. +[RootSignature(PostProcessRSNoCB)] +float4 PSMonochrome(VSInputTx pin) : SV_Target0 +{ + float4 color = Texture.Sample(Sampler, pin.TexCoord); + float3 grayscale = float3(0.2125f, 0.7154f, 0.0721f); + float3 output = dot(color.rgb, grayscale); + return float4(output, color.a); +} + + +// Pixel shader: sepia. +[RootSignature(PostProcessRSNoCB)] +float4 PSSepia(VSInputTx pin) : SV_Target0 +{ + float4 color = Texture.Sample(Sampler, pin.TexCoord); + + float3 red = float3(0.393f, 0.769f, 0.189f); + float3 green = float3(0.349f, 0.686f, 0.168f); + float3 blue = float3(0.272f, 0.534f, 0.131f); + + float3 output; + output.r = dot(color.rgb, red); + output.g = dot(color.rgb, green); + output.b = dot(color.rgb, blue); + return float4(output, color.a); +} + + +// Pixel shader: down-sample 2x2. +[RootSignature(PostProcessRS)] +float4 PSDownScale2x2(VSInputTx pin) : SV_Target0 +{ + const int NUM_SAMPLES = 4; + float4 vColor = 0.0f; + + for (int i = 0; i < NUM_SAMPLES; i++) + { + vColor += Texture.Sample(Sampler, pin.TexCoord + sampleOffsets[i].xy); + } + + return vColor / NUM_SAMPLES; +} + + +// Pixel shader: down-sample 4x4. +[RootSignature(PostProcessRS)] +float4 PSDownScale4x4(VSInputTx pin) : SV_Target0 +{ + const int NUM_SAMPLES = 16; + float4 vColor = 0.0f; + + for (int i = 0; i < NUM_SAMPLES; i++) + { + vColor += Texture.Sample(Sampler, pin.TexCoord + sampleOffsets[i].xy); + } + + return vColor / NUM_SAMPLES; +} + + +// Pixel shader: gaussian blur 5x5. +[RootSignature(PostProcessRS)] +float4 PSGaussianBlur5x5(VSInputTx pin) : SV_Target0 +{ + float4 vColor = 0.0f; + + for (int i = 0; i < 13; i++) + { + vColor += sampleWeights[i] * Texture.Sample(Sampler, pin.TexCoord + sampleOffsets[i].xy); + } + + return vColor; +} + + +// Pixel shader: bloom (extract) +[RootSignature(PostProcessRS)] +float4 PSBloomExtract(VSInputTx pin) : SV_Target0 +{ + // Uses sampleWeights[0] as 'bloom threshold' + float4 c = Texture.Sample(Sampler, pin.TexCoord); + return saturate((c - sampleWeights[0]) / (1 - sampleWeights[0])); +} + + +// Pixel shader: bloom (blur) +[RootSignature(PostProcessRS)] +float4 PSBloomBlur(VSInputTx pin) : SV_Target0 +{ + float4 vColor = 0.0f; + + // Perform a one-directional gaussian blur + for (int i = 0; i < 15; i++) + { + vColor += sampleWeights[i] * Texture.Sample(Sampler, pin.TexCoord + sampleOffsets[i].xy); + } + + return vColor; +} + + +//-------------------------------------------------------------------------------------- +Texture2D Texture2 : register(t1); + +// Pixel shader: merge +[RootSignature(DualPostProcessRS)] +float4 PSMerge(VSInputTx pin) : SV_Target0 +{ + float4 vColor = sampleWeights[0] * Texture.Sample(Sampler, pin.TexCoord); + vColor += sampleWeights[1] * Texture2.Sample(Sampler, pin.TexCoord); + return vColor; +} + + +// Pixel shader: bloom (combine) +float4 AdjustSaturation(float4 color, float saturation) +{ + float3 grayscale = float3(0.2125f, 0.7154f, 0.0721f); + float gray = dot(color.rgb, grayscale); + return lerp(gray, color, saturation); +} + +[RootSignature(DualPostProcessRS)] +float4 PSBloomCombine(VSInputTx pin) : SV_Target0 +{ + // Uses sampleWeights[0].x as base saturation, sampleWeights[0].y as bloom saturation + // Uses sampleWeights[1] as base intensity; sampleWeights[2] as bloom intensity + float4 base = Texture.Sample(Sampler, pin.TexCoord); + float4 bloom = Texture2.Sample(Sampler, pin.TexCoord); + + // Adjust color saturation and intensity. + base = AdjustSaturation(base, sampleWeights[0].x) * sampleWeights[1]; + bloom = AdjustSaturation(bloom, sampleWeights[0].y) * sampleWeights[2]; + + // Darken down the base image in areas where there is a lot of bloom, + // to prevent things looking excessively burned-out. + base *= (1 - saturate(bloom)); + + // Combine the two images. + return base + bloom; +} diff --git a/Common/DirectXTK12/Src/Shaders/RootSig.fxh b/Common/DirectXTK12/Src/Shaders/RootSig.fxh new file mode 100644 index 0000000..bffa33e --- /dev/null +++ b/Common/DirectXTK12/Src/Shaders/RootSig.fxh @@ -0,0 +1,374 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 + +// Root signatures must match definition in each effect, or shaders will be recompiled on Xbox when PSO loads + +#ifdef __XBOX_SCARLETT + +#define NoTextureRS \ +"RootFlags ( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |" \ +" DENY_AMPLIFICATION_SHADER_ROOT_ACCESS |" \ +" DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ +" DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ +" DENY_HULL_SHADER_ROOT_ACCESS |" \ +" DENY_MESH_SHADER_ROOT_ACCESS )," \ +"CBV(b0)" + +#define MainRS \ +"RootFlags ( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |" \ +" DENY_AMPLIFICATION_SHADER_ROOT_ACCESS |" \ +" DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ +" DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ +" DENY_HULL_SHADER_ROOT_ACCESS |" \ +" DENY_MESH_SHADER_ROOT_ACCESS )," \ +"CBV(b0),"\ +"DescriptorTable ( SRV(t0), visibility = SHADER_VISIBILITY_PIXEL ),"\ +"DescriptorTable ( Sampler(s0), visibility = SHADER_VISIBILITY_PIXEL )" + +#define DualTextureRS \ +"RootFlags ( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |" \ +" DENY_AMPLIFICATION_SHADER_ROOT_ACCESS |" \ +" DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ +" DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ +" DENY_HULL_SHADER_ROOT_ACCESS |" \ +" DENY_MESH_SHADER_ROOT_ACCESS )," \ +"DescriptorTable ( SRV(t0), visibility = SHADER_VISIBILITY_PIXEL )," \ +"DescriptorTable ( Sampler(s0), visibility = SHADER_VISIBILITY_PIXEL )," \ +"DescriptorTable ( SRV(t1), visibility = SHADER_VISIBILITY_PIXEL )," \ +"DescriptorTable ( Sampler(s1), visibility = SHADER_VISIBILITY_PIXEL )," \ +"CBV(b0)" + +#define NormalMapRS \ +"RootFlags ( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |" \ +" DENY_AMPLIFICATION_SHADER_ROOT_ACCESS |" \ +" DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ +" DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ +" DENY_HULL_SHADER_ROOT_ACCESS |" \ +" DENY_MESH_SHADER_ROOT_ACCESS )," \ +"DescriptorTable ( SRV(t0), visibility = SHADER_VISIBILITY_PIXEL )," \ +"DescriptorTable ( SRV(t1), visibility = SHADER_VISIBILITY_PIXEL )," \ +"DescriptorTable ( Sampler(s0), visibility = SHADER_VISIBILITY_PIXEL )," \ +"CBV(b0)," \ +"CBV(b1, visibility = SHADER_VISIBILITY_VERTEX )," \ +"DescriptorTable ( SRV(t2), visibility = SHADER_VISIBILITY_PIXEL )" + +#define NormalMapRSNoSpec \ +"RootFlags ( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |" \ +" DENY_AMPLIFICATION_SHADER_ROOT_ACCESS |" \ +" DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ +" DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ +" DENY_HULL_SHADER_ROOT_ACCESS |" \ +" DENY_MESH_SHADER_ROOT_ACCESS )," \ +"DescriptorTable ( SRV(t0), visibility = SHADER_VISIBILITY_PIXEL )," \ +"DescriptorTable ( SRV(t1), visibility = SHADER_VISIBILITY_PIXEL )," \ +"DescriptorTable ( Sampler(s0), visibility = SHADER_VISIBILITY_PIXEL )," \ +"CBV(b0)," \ +"CBV(b1, visibility = SHADER_VISIBILITY_VERTEX )" + +#define GenerateMipsRS \ +"RootFlags ( DENY_VERTEX_SHADER_ROOT_ACCESS |" \ +" DENY_AMPLIFICATION_SHADER_ROOT_ACCESS |" \ +" DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ +" DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ +" DENY_HULL_SHADER_ROOT_ACCESS |" \ +" DENY_MESH_SHADER_ROOT_ACCESS |" \ +" DENY_PIXEL_SHADER_ROOT_ACCESS )," \ +"RootConstants(num32BitConstants=3, b0)," \ +"DescriptorTable ( SRV(t0) )," \ +"DescriptorTable ( UAV(u0) )," \ +"StaticSampler(s0,"\ +" filter = FILTER_MIN_MAG_LINEAR_MIP_POINT,"\ +" addressU = TEXTURE_ADDRESS_CLAMP,"\ +" addressV = TEXTURE_ADDRESS_CLAMP,"\ +" addressW = TEXTURE_ADDRESS_CLAMP )" + +#define SpriteStaticRS \ +"RootFlags ( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |" \ +" DENY_AMPLIFICATION_SHADER_ROOT_ACCESS |" \ +" DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ +" DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ +" DENY_HULL_SHADER_ROOT_ACCESS |" \ +" DENY_MESH_SHADER_ROOT_ACCESS )," \ +"DescriptorTable ( SRV(t0), visibility = SHADER_VISIBILITY_PIXEL ),"\ +"CBV(b0), "\ +"StaticSampler(s0,"\ +" filter = FILTER_MIN_MAG_MIP_LINEAR,"\ +" addressU = TEXTURE_ADDRESS_CLAMP,"\ +" addressV = TEXTURE_ADDRESS_CLAMP,"\ +" addressW = TEXTURE_ADDRESS_CLAMP,"\ +" visibility = SHADER_VISIBILITY_PIXEL )" + +#define SpriteHeapRS \ +"RootFlags ( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |" \ +" DENY_AMPLIFICATION_SHADER_ROOT_ACCESS |" \ +" DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ +" DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ +" DENY_HULL_SHADER_ROOT_ACCESS |" \ +" DENY_MESH_SHADER_ROOT_ACCESS )," \ +"DescriptorTable ( SRV(t0), visibility = SHADER_VISIBILITY_PIXEL ),"\ +"CBV(b0), " \ +"DescriptorTable ( Sampler(s0), visibility = SHADER_VISIBILITY_PIXEL )" + +#define PostProcessRS \ +"RootFlags ( DENY_VERTEX_SHADER_ROOT_ACCESS |" \ +" DENY_AMPLIFICATION_SHADER_ROOT_ACCESS |" \ +" DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ +" DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ +" DENY_HULL_SHADER_ROOT_ACCESS |" \ +" DENY_MESH_SHADER_ROOT_ACCESS )," \ +"DescriptorTable ( SRV(t0), visibility = SHADER_VISIBILITY_PIXEL ),"\ +"CBV(b0, visibility = SHADER_VISIBILITY_PIXEL ), " \ +"StaticSampler(s0,"\ +" filter = FILTER_MIN_MAG_MIP_LINEAR,"\ +" addressU = TEXTURE_ADDRESS_CLAMP,"\ +" addressV = TEXTURE_ADDRESS_CLAMP,"\ +" addressW = TEXTURE_ADDRESS_CLAMP,"\ +" visibility = SHADER_VISIBILITY_PIXEL )" + +#define PostProcessRSNoCB \ +"RootFlags ( DENY_VERTEX_SHADER_ROOT_ACCESS |" \ +" DENY_AMPLIFICATION_SHADER_ROOT_ACCESS |" \ +" DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ +" DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ +" DENY_HULL_SHADER_ROOT_ACCESS |" \ +" DENY_MESH_SHADER_ROOT_ACCESS )," \ +"DescriptorTable ( SRV(t0), visibility = SHADER_VISIBILITY_PIXEL ),"\ +"StaticSampler(s0,"\ +" filter = FILTER_MIN_MAG_MIP_LINEAR,"\ +" addressU = TEXTURE_ADDRESS_CLAMP,"\ +" addressV = TEXTURE_ADDRESS_CLAMP,"\ +" addressW = TEXTURE_ADDRESS_CLAMP,"\ +" visibility = SHADER_VISIBILITY_PIXEL )" + +#define DualPostProcessRS \ +"RootFlags ( DENY_VERTEX_SHADER_ROOT_ACCESS |" \ +" DENY_AMPLIFICATION_SHADER_ROOT_ACCESS |" \ +" DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ +" DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ +" DENY_HULL_SHADER_ROOT_ACCESS |" \ +" DENY_MESH_SHADER_ROOT_ACCESS )," \ +"DescriptorTable ( SRV(t0), visibility = SHADER_VISIBILITY_PIXEL ),"\ +"DescriptorTable ( SRV(t1), visibility = SHADER_VISIBILITY_PIXEL ),"\ +"CBV(b0, visibility = SHADER_VISIBILITY_PIXEL ), " \ +"StaticSampler(s0,"\ +" filter = FILTER_MIN_MAG_MIP_LINEAR,"\ +" addressU = TEXTURE_ADDRESS_CLAMP,"\ +" addressV = TEXTURE_ADDRESS_CLAMP,"\ +" addressW = TEXTURE_ADDRESS_CLAMP,"\ +" visibility = SHADER_VISIBILITY_PIXEL )" + +#define ToneMapRS \ +"RootFlags ( DENY_VERTEX_SHADER_ROOT_ACCESS |" \ +" DENY_AMPLIFICATION_SHADER_ROOT_ACCESS |" \ +" DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ +" DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ +" DENY_HULL_SHADER_ROOT_ACCESS |" \ +" DENY_MESH_SHADER_ROOT_ACCESS )," \ +"DescriptorTable ( SRV(t0), visibility = SHADER_VISIBILITY_PIXEL ),"\ +"CBV(b0, visibility = SHADER_VISIBILITY_PIXEL ), " \ +"StaticSampler(s0,"\ +" filter = FILTER_MIN_MAG_MIP_POINT,"\ +" addressU = TEXTURE_ADDRESS_CLAMP,"\ +" addressV = TEXTURE_ADDRESS_CLAMP,"\ +" addressW = TEXTURE_ADDRESS_CLAMP,"\ +" visibility = SHADER_VISIBILITY_PIXEL )" + +#define PBREffectRS \ +"RootFlags ( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |" \ +" DENY_AMPLIFICATION_SHADER_ROOT_ACCESS |" \ +" DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ +" DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ +" DENY_HULL_SHADER_ROOT_ACCESS |" \ +" DENY_MESH_SHADER_ROOT_ACCESS )," \ +"DescriptorTable ( SRV(t0) ),"\ +"DescriptorTable ( SRV(t1) ),"\ +"DescriptorTable ( SRV(t2) ),"\ +"DescriptorTable ( SRV(t3) ),"\ +"DescriptorTable ( SRV(t4) ),"\ +"DescriptorTable ( SRV(t5) ),"\ +"DescriptorTable ( Sampler(s0) ),"\ +"DescriptorTable ( Sampler(s1) ),"\ +"CBV(b0)," \ +"CBV(b1, visibility = SHADER_VISIBILITY_VERTEX )" + +#define DebugEffectRS \ +"RootFlags ( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |" \ +" DENY_AMPLIFICATION_SHADER_ROOT_ACCESS |" \ +" DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ +" DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ +" DENY_HULL_SHADER_ROOT_ACCESS |" \ +" DENY_MESH_SHADER_ROOT_ACCESS )," \ +"CBV(b0)" + +#else // !__XBOX_SCARLETT + +#define NoTextureRS \ +"RootFlags ( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |" \ +" DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ +" DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ +" DENY_HULL_SHADER_ROOT_ACCESS )," \ +"CBV(b0)" + +#define MainRS \ +"RootFlags ( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |" \ +" DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ +" DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ +" DENY_HULL_SHADER_ROOT_ACCESS )," \ +"CBV(b0),"\ +"DescriptorTable ( SRV(t0), visibility = SHADER_VISIBILITY_PIXEL ),"\ +"DescriptorTable ( Sampler(s0), visibility = SHADER_VISIBILITY_PIXEL )" + +#define DualTextureRS \ +"RootFlags ( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |" \ +" DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ +" DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ +" DENY_HULL_SHADER_ROOT_ACCESS )," \ +"DescriptorTable ( SRV(t0), visibility = SHADER_VISIBILITY_PIXEL )," \ +"DescriptorTable ( Sampler(s0), visibility = SHADER_VISIBILITY_PIXEL )," \ +"DescriptorTable ( SRV(t1), visibility = SHADER_VISIBILITY_PIXEL )," \ +"DescriptorTable ( Sampler(s1), visibility = SHADER_VISIBILITY_PIXEL )," \ +"CBV(b0)" + +#define NormalMapRS \ +"RootFlags ( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |" \ +" DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ +" DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ +" DENY_HULL_SHADER_ROOT_ACCESS )," \ +"DescriptorTable ( SRV(t0), visibility = SHADER_VISIBILITY_PIXEL )," \ +"DescriptorTable ( SRV(t1), visibility = SHADER_VISIBILITY_PIXEL )," \ +"DescriptorTable ( Sampler(s0), visibility = SHADER_VISIBILITY_PIXEL )," \ +"CBV(b0)," \ +"CBV(b1, visibility = SHADER_VISIBILITY_VERTEX )," \ +"DescriptorTable ( SRV(t2), visibility = SHADER_VISIBILITY_PIXEL )" + +#define NormalMapRSNoSpec \ +"RootFlags ( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |" \ +" DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ +" DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ +" DENY_HULL_SHADER_ROOT_ACCESS )," \ +"DescriptorTable ( SRV(t0), visibility = SHADER_VISIBILITY_PIXEL )," \ +"DescriptorTable ( SRV(t1), visibility = SHADER_VISIBILITY_PIXEL )," \ +"DescriptorTable ( Sampler(s0), visibility = SHADER_VISIBILITY_PIXEL )," \ +"CBV(b0)," \ +"CBV(b1, visibility = SHADER_VISIBILITY_VERTEX )" + +#define GenerateMipsRS \ +"RootFlags ( DENY_VERTEX_SHADER_ROOT_ACCESS |" \ +" DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ +" DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ +" DENY_HULL_SHADER_ROOT_ACCESS |" \ +" DENY_PIXEL_SHADER_ROOT_ACCESS )," \ +"RootConstants(num32BitConstants=3, b0)," \ +"DescriptorTable ( SRV(t0) )," \ +"DescriptorTable ( UAV(u0) )," \ +"StaticSampler(s0,"\ +" filter = FILTER_MIN_MAG_LINEAR_MIP_POINT,"\ +" addressU = TEXTURE_ADDRESS_CLAMP,"\ +" addressV = TEXTURE_ADDRESS_CLAMP,"\ +" addressW = TEXTURE_ADDRESS_CLAMP )" + +#define SpriteStaticRS \ +"RootFlags ( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |" \ +" DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ +" DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ +" DENY_HULL_SHADER_ROOT_ACCESS )," \ +"DescriptorTable ( SRV(t0), visibility = SHADER_VISIBILITY_PIXEL ),"\ +"CBV(b0), "\ +"StaticSampler(s0,"\ +" filter = FILTER_MIN_MAG_MIP_LINEAR,"\ +" addressU = TEXTURE_ADDRESS_CLAMP,"\ +" addressV = TEXTURE_ADDRESS_CLAMP,"\ +" addressW = TEXTURE_ADDRESS_CLAMP,"\ +" visibility = SHADER_VISIBILITY_PIXEL )" + +#define SpriteHeapRS \ +"RootFlags ( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |" \ +" DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ +" DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ +" DENY_HULL_SHADER_ROOT_ACCESS )," \ +"DescriptorTable ( SRV(t0), visibility = SHADER_VISIBILITY_PIXEL ),"\ +"CBV(b0), " \ +"DescriptorTable ( Sampler(s0), visibility = SHADER_VISIBILITY_PIXEL )" + +#define PostProcessRS \ +"RootFlags ( DENY_VERTEX_SHADER_ROOT_ACCESS |" \ +" DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ +" DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ +" DENY_HULL_SHADER_ROOT_ACCESS )," \ +"DescriptorTable ( SRV(t0), visibility = SHADER_VISIBILITY_PIXEL ),"\ +"CBV(b0, visibility = SHADER_VISIBILITY_PIXEL ), " \ +"StaticSampler(s0,"\ +" filter = FILTER_MIN_MAG_MIP_LINEAR,"\ +" addressU = TEXTURE_ADDRESS_CLAMP,"\ +" addressV = TEXTURE_ADDRESS_CLAMP,"\ +" addressW = TEXTURE_ADDRESS_CLAMP,"\ +" visibility = SHADER_VISIBILITY_PIXEL )" + +#define PostProcessRSNoCB \ +"RootFlags ( DENY_VERTEX_SHADER_ROOT_ACCESS |" \ +" DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ +" DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ +" DENY_HULL_SHADER_ROOT_ACCESS )," \ +"DescriptorTable ( SRV(t0), visibility = SHADER_VISIBILITY_PIXEL ),"\ +"StaticSampler(s0,"\ +" filter = FILTER_MIN_MAG_MIP_LINEAR,"\ +" addressU = TEXTURE_ADDRESS_CLAMP,"\ +" addressV = TEXTURE_ADDRESS_CLAMP,"\ +" addressW = TEXTURE_ADDRESS_CLAMP,"\ +" visibility = SHADER_VISIBILITY_PIXEL )" + +#define DualPostProcessRS \ +"RootFlags ( DENY_VERTEX_SHADER_ROOT_ACCESS |" \ +" DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ +" DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ +" DENY_HULL_SHADER_ROOT_ACCESS )," \ +"DescriptorTable ( SRV(t0), visibility = SHADER_VISIBILITY_PIXEL ),"\ +"DescriptorTable ( SRV(t1), visibility = SHADER_VISIBILITY_PIXEL ),"\ +"CBV(b0, visibility = SHADER_VISIBILITY_PIXEL ), " \ +"StaticSampler(s0,"\ +" filter = FILTER_MIN_MAG_MIP_LINEAR,"\ +" addressU = TEXTURE_ADDRESS_CLAMP,"\ +" addressV = TEXTURE_ADDRESS_CLAMP,"\ +" addressW = TEXTURE_ADDRESS_CLAMP,"\ +" visibility = SHADER_VISIBILITY_PIXEL )" + +#define ToneMapRS \ +"RootFlags ( DENY_VERTEX_SHADER_ROOT_ACCESS |" \ +" DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ +" DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ +" DENY_HULL_SHADER_ROOT_ACCESS )," \ +"DescriptorTable ( SRV(t0), visibility = SHADER_VISIBILITY_PIXEL ),"\ +"CBV(b0, visibility = SHADER_VISIBILITY_PIXEL ), " \ +"StaticSampler(s0,"\ +" filter = FILTER_MIN_MAG_MIP_POINT,"\ +" addressU = TEXTURE_ADDRESS_CLAMP,"\ +" addressV = TEXTURE_ADDRESS_CLAMP,"\ +" addressW = TEXTURE_ADDRESS_CLAMP,"\ +" visibility = SHADER_VISIBILITY_PIXEL )" + +#define PBREffectRS \ +"RootFlags ( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |" \ +" DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ +" DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ +" DENY_HULL_SHADER_ROOT_ACCESS )," \ +"DescriptorTable ( SRV(t0) ),"\ +"DescriptorTable ( SRV(t1) ),"\ +"DescriptorTable ( SRV(t2) ),"\ +"DescriptorTable ( SRV(t3) ),"\ +"DescriptorTable ( SRV(t4) ),"\ +"DescriptorTable ( SRV(t5) ),"\ +"DescriptorTable ( Sampler(s0) ),"\ +"DescriptorTable ( Sampler(s1) ),"\ +"CBV(b0)," \ +"CBV(b1, visibility = SHADER_VISIBILITY_VERTEX )" + +#define DebugEffectRS \ +"RootFlags ( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |" \ +" DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ +" DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ +" DENY_HULL_SHADER_ROOT_ACCESS )," \ +"CBV(b0)" + +#endif diff --git a/Common/DirectXTK12/Src/Shaders/SkinnedEffect.fx b/Common/DirectXTK12/Src/Shaders/SkinnedEffect.fx new file mode 100644 index 0000000..d638246 --- /dev/null +++ b/Common/DirectXTK12/Src/Shaders/SkinnedEffect.fx @@ -0,0 +1,295 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 + + +Texture2D Texture : register(t0); +sampler Sampler : register(s0); + + +cbuffer Parameters : register(b0) +{ + float4 DiffuseColor : packoffset(c0); + float3 EmissiveColor : packoffset(c1); + float3 SpecularColor : packoffset(c2); + float SpecularPower : packoffset(c2.w); + + float3 LightDirection[3] : packoffset(c3); + float3 LightDiffuseColor[3] : packoffset(c6); + float3 LightSpecularColor[3] : packoffset(c9); + + float3 EyePosition : packoffset(c12); + + float3 FogColor : packoffset(c13); + float4 FogVector : packoffset(c14); + + float4x4 World : packoffset(c15); + float3x3 WorldInverseTranspose : packoffset(c19); + float4x4 WorldViewProj : packoffset(c22); + + float4x3 Bones[72] : packoffset(c26); +}; + + +#include "Structures.fxh" +#include "Common.fxh" +#include "RootSig.fxh" +#include "Lighting.fxh" +#include "Utilities.fxh" +#include "Skinning.fxh" + + +// Vertex shader: vertex lighting, one bone. +[RootSignature(MainRS)] +VSOutputTx VSSkinnedVertexLightingOneBone(VSInputNmTxWeights vin) +{ + VSOutputTx vout; + + float3 normal = Skin(vin, vin.Normal, 1); + + CommonVSOutput cout = ComputeCommonVSOutputWithLighting(vin.Position, normal, 3); + SetCommonVSOutputParams; + + vout.TexCoord = vin.TexCoord; + + return vout; +} + +[RootSignature(MainRS)] +VSOutputTx VSSkinnedVertexLightingOneBoneBn(VSInputNmTxWeights vin) +{ + VSOutputTx vout; + + float3 normal = BiasX2(vin.Normal); + + normal = Skin(vin, normal, 1); + + CommonVSOutput cout = ComputeCommonVSOutputWithLighting(vin.Position, normal, 3); + SetCommonVSOutputParams; + + vout.TexCoord = vin.TexCoord; + + return vout; +} + + +// Vertex shader: vertex lighting, two bones. +[RootSignature(MainRS)] +VSOutputTx VSSkinnedVertexLightingTwoBones(VSInputNmTxWeights vin) +{ + VSOutputTx vout; + + float3 normal = Skin(vin, vin.Normal, 2); + + CommonVSOutput cout = ComputeCommonVSOutputWithLighting(vin.Position, normal, 3); + SetCommonVSOutputParams; + + vout.TexCoord = vin.TexCoord; + + return vout; +} + +[RootSignature(MainRS)] +VSOutputTx VSSkinnedVertexLightingTwoBonesBn(VSInputNmTxWeights vin) +{ + VSOutputTx vout; + + float3 normal = BiasX2(vin.Normal); + + normal = Skin(vin, normal, 2); + + CommonVSOutput cout = ComputeCommonVSOutputWithLighting(vin.Position, normal, 3); + SetCommonVSOutputParams; + + vout.TexCoord = vin.TexCoord; + + return vout; +} + + +// Vertex shader: vertex lighting, four bones. +[RootSignature(MainRS)] +VSOutputTx VSSkinnedVertexLightingFourBones(VSInputNmTxWeights vin) +{ + VSOutputTx vout; + + float3 normal = Skin(vin, vin.Normal, 4); + + CommonVSOutput cout = ComputeCommonVSOutputWithLighting(vin.Position, normal, 3); + SetCommonVSOutputParams; + + vout.TexCoord = vin.TexCoord; + + return vout; +} + +[RootSignature(MainRS)] +VSOutputTx VSSkinnedVertexLightingFourBonesBn(VSInputNmTxWeights vin) +{ + VSOutputTx vout; + + float3 normal = BiasX2(vin.Normal); + + normal = Skin(vin, normal, 4); + + CommonVSOutput cout = ComputeCommonVSOutputWithLighting(vin.Position, normal, 3); + SetCommonVSOutputParams; + + vout.TexCoord = vin.TexCoord; + + return vout; +} + + +// Vertex shader: pixel lighting, one bone. +[RootSignature(MainRS)] +VSOutputPixelLightingTx VSSkinnedPixelLightingOneBone(VSInputNmTxWeights vin) +{ + VSOutputPixelLightingTx vout; + + float3 normal = Skin(vin, vin.Normal, 1); + + CommonVSOutputPixelLighting cout = ComputeCommonVSOutputPixelLighting(vin.Position, normal); + SetCommonVSOutputParamsPixelLighting; + + vout.Diffuse = float4(1, 1, 1, DiffuseColor.a); + vout.TexCoord = vin.TexCoord; + + return vout; +} + +[RootSignature(MainRS)] +VSOutputPixelLightingTx VSSkinnedPixelLightingOneBoneBn(VSInputNmTxWeights vin) +{ + VSOutputPixelLightingTx vout; + + float3 normal = BiasX2(vin.Normal); + + normal = Skin(vin, normal, 1); + + CommonVSOutputPixelLighting cout = ComputeCommonVSOutputPixelLighting(vin.Position, normal); + SetCommonVSOutputParamsPixelLighting; + + vout.Diffuse = float4(1, 1, 1, DiffuseColor.a); + vout.TexCoord = vin.TexCoord; + + return vout; +} + + +// Vertex shader: pixel lighting, two bones. +[RootSignature(MainRS)] +VSOutputPixelLightingTx VSSkinnedPixelLightingTwoBones(VSInputNmTxWeights vin) +{ + VSOutputPixelLightingTx vout; + + float3 normal = Skin(vin, vin.Normal, 2); + + CommonVSOutputPixelLighting cout = ComputeCommonVSOutputPixelLighting(vin.Position, normal); + SetCommonVSOutputParamsPixelLighting; + + vout.Diffuse = float4(1, 1, 1, DiffuseColor.a); + vout.TexCoord = vin.TexCoord; + + return vout; +} + +[RootSignature(MainRS)] +VSOutputPixelLightingTx VSSkinnedPixelLightingTwoBonesBn(VSInputNmTxWeights vin) +{ + VSOutputPixelLightingTx vout; + + float3 normal = BiasX2(vin.Normal); + + normal = Skin(vin, normal, 2); + + CommonVSOutputPixelLighting cout = ComputeCommonVSOutputPixelLighting(vin.Position, normal); + SetCommonVSOutputParamsPixelLighting; + + vout.Diffuse = float4(1, 1, 1, DiffuseColor.a); + vout.TexCoord = vin.TexCoord; + + return vout; +} + + +// Vertex shader: pixel lighting, four bones. +[RootSignature(MainRS)] +VSOutputPixelLightingTx VSSkinnedPixelLightingFourBones(VSInputNmTxWeights vin) +{ + VSOutputPixelLightingTx vout; + + float3 normal = Skin(vin, vin.Normal, 4); + + CommonVSOutputPixelLighting cout = ComputeCommonVSOutputPixelLighting(vin.Position, normal); + SetCommonVSOutputParamsPixelLighting; + + vout.Diffuse = float4(1, 1, 1, DiffuseColor.a); + vout.TexCoord = vin.TexCoord; + + return vout; +} + +[RootSignature(MainRS)] +VSOutputPixelLightingTx VSSkinnedPixelLightingFourBonesBn(VSInputNmTxWeights vin) +{ + VSOutputPixelLightingTx vout; + + float3 normal = BiasX2(vin.Normal); + + normal = Skin(vin, normal, 4); + + CommonVSOutputPixelLighting cout = ComputeCommonVSOutputPixelLighting(vin.Position, normal); + SetCommonVSOutputParamsPixelLighting; + + vout.Diffuse = float4(1, 1, 1, DiffuseColor.a); + vout.TexCoord = vin.TexCoord; + + return vout; +} + + +// Pixel shader: vertex lighting. +[RootSignature(MainRS)] +float4 PSSkinnedVertexLighting(PSInputTx pin) : SV_Target0 +{ + float4 color = Texture.Sample(Sampler, pin.TexCoord) * pin.Diffuse; + + AddSpecular(color, pin.Specular.rgb); + ApplyFog(color, pin.Specular.w); + + return color; +} + + +// Pixel shader: vertex lighting, no fog. +[RootSignature(MainRS)] +float4 PSSkinnedVertexLightingNoFog(PSInputTx pin) : SV_Target0 +{ + float4 color = Texture.Sample(Sampler, pin.TexCoord) * pin.Diffuse; + + AddSpecular(color, pin.Specular.rgb); + + return color; +} + + +// Pixel shader: pixel lighting. +[RootSignature(MainRS)] +float4 PSSkinnedPixelLighting(PSInputPixelLightingTx pin) : SV_Target0 +{ + float4 color = Texture.Sample(Sampler, pin.TexCoord) * pin.Diffuse; + + float3 eyeVector = normalize(EyePosition - pin.PositionWS.xyz); + float3 worldNormal = normalize(pin.NormalWS); + + ColorPair lightResult = ComputeLights(eyeVector, worldNormal, 3); + + color.rgb *= lightResult.Diffuse; + + AddSpecular(color, lightResult.Specular); + ApplyFog(color, pin.PositionWS.w); + + return color; +} diff --git a/Common/DirectXTK12/Src/Shaders/Skinning.fxh b/Common/DirectXTK12/Src/Shaders/Skinning.fxh new file mode 100644 index 0000000..1a1bc6b --- /dev/null +++ b/Common/DirectXTK12/Src/Shaders/Skinning.fxh @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248929 +// http://go.microsoft.com/fwlink/?LinkID=615561 + + +float3 Skin(inout VSInputNmTxWeights vin, float3 normal, uniform int boneCount) +{ + float4x3 skinning = 0; + + [unroll] + for (int i = 0; i < boneCount; i++) + { + skinning += Bones[vin.Indices[i]] * vin.Weights[i]; + } + + vin.Position.xyz = mul(vin.Position, skinning); + return mul(normal, (float3x3) skinning); +} diff --git a/Common/DirectXTK12/Src/Shaders/SpriteEffect.fx b/Common/DirectXTK12/Src/Shaders/SpriteEffect.fx new file mode 100644 index 0000000..b4961cf --- /dev/null +++ b/Common/DirectXTK12/Src/Shaders/SpriteEffect.fx @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 + +#include "Structures.fxh" +#include "RootSig.fxh" + +Texture2D Texture : register(t0); +sampler TextureSampler : register(s0); + + +cbuffer Parameters : register(b0) +{ + row_major float4x4 MatrixTransform; +}; + +[RootSignature(SpriteStaticRS)] +void SpriteVertexShader(inout float4 color : COLOR0, + inout float2 texCoord : TEXCOORD0, + inout float4 position : SV_Position) +{ + position = mul(position, MatrixTransform); +} + +[RootSignature(SpriteStaticRS)] +float4 SpritePixelShader(float4 color : COLOR0, + float2 texCoord : TEXCOORD0) : SV_Target0 +{ + return Texture.Sample(TextureSampler, texCoord) * color; +} + +[RootSignature(SpriteHeapRS)] +void SpriteVertexShaderHeap(inout float4 color : COLOR0, + inout float2 texCoord : TEXCOORD0, + inout float4 position : SV_Position) +{ + position = mul(position, MatrixTransform); +} + +[RootSignature(SpriteHeapRS)] +float4 SpritePixelShaderHeap(float4 color : COLOR0, + float2 texCoord : TEXCOORD0) : SV_Target0 +{ + return Texture.Sample(TextureSampler, texCoord) * color; +} diff --git a/Common/DirectXTK12/Src/Shaders/Structures.fxh b/Common/DirectXTK12/Src/Shaders/Structures.fxh new file mode 100644 index 0000000..d18eb0f --- /dev/null +++ b/Common/DirectXTK12/Src/Shaders/Structures.fxh @@ -0,0 +1,243 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248926 +// http://go.microsoft.com/fwlink/?LinkId=248929 +// http://go.microsoft.com/fwlink/?LinkID=615561 + + +// Vertex shader input structures. + +struct VSInput +{ + float4 Position : SV_Position; +}; + +struct VSInputVc +{ + float4 Position : SV_Position; + float4 Color : COLOR; +}; + +struct VSInputTx +{ + float4 Position : SV_Position; + float2 TexCoord : TEXCOORD0; +}; + +struct VSInputTxVc +{ + float4 Position : SV_Position; + float2 TexCoord : TEXCOORD0; + float4 Color : COLOR; +}; + +struct VSInputNm +{ + float4 Position : SV_Position; + float3 Normal : NORMAL; +}; + +struct VSInputNmVc +{ + float4 Position : SV_Position; + float3 Normal : NORMAL; + float4 Color : COLOR; +}; + +struct VSInputNmTx +{ + float4 Position : SV_Position; + float3 Normal : NORMAL; + float2 TexCoord : TEXCOORD0; +}; + +struct VSInputNmTxVc +{ + float4 Position : SV_Position; + float3 Normal : NORMAL; + float2 TexCoord : TEXCOORD0; + float4 Color : COLOR; +}; + +struct VSInputNmTxInst +{ + float4 Position : SV_Position; + float3 Normal : NORMAL; + float2 TexCoord : TEXCOORD0; + float4x3 Transform : InstMatrix; +}; + +struct VSInputNmTxVcInst +{ + float4 Position : SV_Position; + float3 Normal : NORMAL; + float2 TexCoord : TEXCOORD0; + float4 Color : COLOR; + float4x3 Transform : InstMatrix; +}; + +struct VSInputTx2 +{ + float4 Position : SV_Position; + float2 TexCoord : TEXCOORD0; + float2 TexCoord2 : TEXCOORD1; +}; + +struct VSInputTx2Vc +{ + float4 Position : SV_Position; + float2 TexCoord : TEXCOORD0; + float2 TexCoord2 : TEXCOORD1; + float4 Color : COLOR; +}; + +struct VSInputNmTxWeights +{ + float4 Position : SV_Position; + float3 Normal : NORMAL; + float2 TexCoord : TEXCOORD0; + uint4 Indices : BLENDINDICES0; + float4 Weights : BLENDWEIGHT0; +}; + + + +// Vertex shader output structures. + +struct VSOutput +{ + float4 Diffuse : COLOR0; + float4 Specular : COLOR1; + float4 PositionPS : SV_Position; +}; + +struct VSOutputNoFog +{ + float4 Diffuse : COLOR0; + float4 PositionPS : SV_Position; +}; + +struct VSOutputTx +{ + float4 Diffuse : COLOR0; + float4 Specular : COLOR1; + float2 TexCoord : TEXCOORD0; + float4 PositionPS : SV_Position; +}; + +struct VSOutputTxNoFog +{ + float4 Diffuse : COLOR0; + float2 TexCoord : TEXCOORD0; + float4 PositionPS : SV_Position; +}; + +struct VSOutputPixelLighting +{ + float4 PositionWS : TEXCOORD0; + float3 NormalWS : TEXCOORD1; + float4 Diffuse : COLOR0; + float4 PositionPS : SV_Position; +}; + +struct VSOutputPixelLightingTx +{ + float2 TexCoord : TEXCOORD0; + float4 PositionWS : TEXCOORD1; + float3 NormalWS : TEXCOORD2; + float4 Diffuse : COLOR0; + float4 PositionPS : SV_Position; +}; + +struct VSOutputTx2 +{ + float4 Diffuse : COLOR0; + float4 Specular : COLOR1; + float2 TexCoord : TEXCOORD0; + float2 TexCoord2 : TEXCOORD1; + float4 PositionPS : SV_Position; +}; + +struct VSOutputTx2NoFog +{ + float4 Diffuse : COLOR0; + float2 TexCoord : TEXCOORD0; + float2 TexCoord2 : TEXCOORD1; + float4 PositionPS : SV_Position; +}; + +struct VSOutputTxEnvMap +{ + float4 Diffuse : COLOR0; + float4 Specular : COLOR1; + float2 TexCoord : TEXCOORD0; + float3 EnvCoord : TEXCOORD1; + float4 PositionPS : SV_Position; +}; + + + +// Pixel shader input structures. + +struct PSInput +{ + float4 Diffuse : COLOR0; + float4 Specular : COLOR1; +}; + +struct PSInputNoFog +{ + float4 Diffuse : COLOR0; +}; + +struct PSInputTx +{ + float4 Diffuse : COLOR0; + float4 Specular : COLOR1; + float2 TexCoord : TEXCOORD0; +}; + +struct PSInputTxNoFog +{ + float4 Diffuse : COLOR0; + float2 TexCoord : TEXCOORD0; +}; + +struct PSInputPixelLighting +{ + float4 PositionWS : TEXCOORD0; + float3 NormalWS : TEXCOORD1; + float4 Diffuse : COLOR0; +}; + +struct PSInputPixelLightingTx +{ + float2 TexCoord : TEXCOORD0; + float4 PositionWS : TEXCOORD1; + float3 NormalWS : TEXCOORD2; + float4 Diffuse : COLOR0; +}; + +struct PSInputTx2 +{ + float4 Diffuse : COLOR0; + float4 Specular : COLOR1; + float2 TexCoord : TEXCOORD0; + float2 TexCoord2 : TEXCOORD1; +}; + +struct PSInputTx2NoFog +{ + float4 Diffuse : COLOR0; + float2 TexCoord : TEXCOORD0; + float2 TexCoord2 : TEXCOORD1; +}; + +struct PSInputTxEnvMap +{ + float4 Diffuse : COLOR0; + float4 Specular : COLOR1; + float2 TexCoord : TEXCOORD0; + float3 EnvCoord : TEXCOORD1; +}; diff --git a/Common/DirectXTK12/Src/Shaders/ToneMap.fx b/Common/DirectXTK12/Src/Shaders/ToneMap.fx new file mode 100644 index 0000000..c25aa29 --- /dev/null +++ b/Common/DirectXTK12/Src/Shaders/ToneMap.fx @@ -0,0 +1,245 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 + +Texture2D HDRTexture : register(t0); +sampler Sampler : register(s0); + + +cbuffer Parameters : register(b0) +{ + float linearExposure : packoffset(c0.x); + float paperWhiteNits : packoffset(c0.y); + float4x3 colorRotation : packoffset(c1); +}; + + +#include "Structures.fxh" +#include "RootSig.fxh" +#include "Utilities.fxh" + + +// Vertex shader: self-created quad. +[RootSignature(ToneMapRS)] +VSInputTx VSQuad(uint vI : SV_VertexId) +{ + VSInputTx vout; + + // We use the 'big triangle' optimization so you only Draw 3 verticies instead of 4. + float2 texcoord = float2((vI << 1) & 2, vI & 2); + vout.TexCoord = texcoord; + + vout.Position = float4(texcoord.x * 2 - 1, -texcoord.y * 2 + 1, 0, 1); + return vout; +} + + +//-------------------------------------------------------------------------------------- +// Pixel shader: pass-through +[RootSignature(ToneMapRS)] +float4 PSCopy(VSInputTx pin) : SV_Target0 +{ + return HDRTexture.Sample(Sampler, pin.TexCoord); +} + + +// Pixel shader: saturate (clips above 1.0) +[RootSignature(ToneMapRS)] +float4 PSSaturate(VSInputTx pin) : SV_Target0 +{ + float4 hdr = HDRTexture.Sample(Sampler, pin.TexCoord); + float3 sdr = saturate(hdr.xyz * linearExposure); + return float4(sdr, hdr.a); +} + + +// Pixel shader: reinhard operator +[RootSignature(ToneMapRS)] +float4 PSReinhard(VSInputTx pin) : SV_Target0 +{ + float4 hdr = HDRTexture.Sample(Sampler, pin.TexCoord); + float3 sdr = ToneMapReinhard(hdr.xyz * linearExposure); + return float4(sdr, hdr.a); +} + + +// Pixel shader: ACES filmic operator +[RootSignature(ToneMapRS)] +float4 PSACESFilmic(VSInputTx pin) : SV_Target0 +{ + float4 hdr = HDRTexture.Sample(Sampler, pin.TexCoord); + float3 sdr = ToneMapACESFilmic(hdr.xyz * linearExposure); + return float4(sdr, hdr.a); +} + + +//-------------------------------------------------------------------------------------- +// SRGB, using Rec.709 color primaries and a gamma 2.2 curve + +// Pixel shader: sRGB +[RootSignature(ToneMapRS)] +float4 PS_SRGB(VSInputTx pin) : SV_Target0 +{ + float4 hdr = HDRTexture.Sample(Sampler, pin.TexCoord); + float3 srgb = LinearToSRGBEst(hdr.xyz); + return float4(srgb, hdr.a); +} + + +// Pixel shader: saturate (clips above 1.0) +[RootSignature(ToneMapRS)] +float4 PSSaturate_SRGB(VSInputTx pin) : SV_Target0 +{ + float4 hdr = HDRTexture.Sample(Sampler, pin.TexCoord); + float3 sdr = saturate(hdr.xyz * linearExposure); + float3 srgb = LinearToSRGBEst(sdr); + return float4(srgb, hdr.a); +} + + +// Pixel shader: reinhard operator +[RootSignature(ToneMapRS)] +float4 PSReinhard_SRGB(VSInputTx pin) : SV_Target0 +{ + float4 hdr = HDRTexture.Sample(Sampler, pin.TexCoord); + float3 sdr = ToneMapReinhard(hdr.xyz * linearExposure); + float3 srgb = LinearToSRGBEst(sdr); + return float4(srgb, hdr.a); +} + + +// Pixel shader: ACES filmic operator +[RootSignature(ToneMapRS)] +float4 PSACESFilmic_SRGB(VSInputTx pin) : SV_Target0 +{ + float4 hdr = HDRTexture.Sample(Sampler, pin.TexCoord); + float3 sdr = ToneMapACESFilmic(hdr.xyz * linearExposure); + float3 srgb = LinearToSRGBEst(sdr); + return float4(srgb, hdr.a); +} + + +//-------------------------------------------------------------------------------------- +// HDR10, using Rec.2020 color primaries and ST.2084 curve + +float3 HDR10(float3 color) +{ + // Rotate from Rec.709 to Rec.2020 primaries + float3 rgb = mul(color, (float3x3)colorRotation); + + // ST.2084 spec defines max nits as 10,000 nits + float3 normalized = rgb * paperWhiteNits / 10000.f; + + // Apply ST.2084 curve + return LinearToST2084(normalized); +} + +[RootSignature(ToneMapRS)] +float4 PSHDR10(VSInputTx pin) : SV_Target0 +{ + float4 hdr = HDRTexture.Sample(Sampler, pin.TexCoord); + float3 rgb = HDR10(hdr.xyz); + return float4(rgb, hdr.a); +} + + +//-------------------------------------------------------------------------------------- +struct MRTOut +{ + float4 hdr : SV_Target0; + float4 sdr : SV_Target1; +}; + +[RootSignature(ToneMapRS)] +MRTOut PSHDR10_Saturate(VSInputTx pin) +{ + MRTOut output; + + float4 hdr = HDRTexture.Sample(Sampler, pin.TexCoord); + float3 rgb = HDR10(hdr.xyz); + output.hdr = float4(rgb, hdr.a); + + float3 sdr = saturate(hdr.xyz * linearExposure); + output.sdr = float4(sdr, hdr.a); + + return output; +} + +[RootSignature(ToneMapRS)] +MRTOut PSHDR10_Reinhard(VSInputTx pin) +{ + MRTOut output; + + float4 hdr = HDRTexture.Sample(Sampler, pin.TexCoord); + float3 rgb = HDR10(hdr.xyz); + output.hdr = float4(rgb, hdr.a); + + float3 sdr = ToneMapReinhard(hdr.xyz * linearExposure); + output.sdr = float4(sdr, hdr.a); + + return output; +} + +[RootSignature(ToneMapRS)] +MRTOut PSHDR10_ACESFilmic(VSInputTx pin) +{ + MRTOut output; + + float4 hdr = HDRTexture.Sample(Sampler, pin.TexCoord); + float3 rgb = HDR10(hdr.xyz); + output.hdr = float4(rgb, hdr.a); + + float3 sdr = ToneMapACESFilmic(hdr.xyz * linearExposure); + output.sdr = float4(sdr, hdr.a); + + return output; +} + +[RootSignature(ToneMapRS)] +MRTOut PSHDR10_Saturate_SRGB(VSInputTx pin) +{ + MRTOut output; + + float4 hdr = HDRTexture.Sample(Sampler, pin.TexCoord); + float3 rgb = HDR10(hdr.xyz); + output.hdr = float4(rgb, hdr.a); + + float3 sdr = saturate(hdr.xyz * linearExposure); + float3 srgb = LinearToSRGBEst(sdr); + output.sdr = float4(srgb, hdr.a); + + return output; +} + +[RootSignature(ToneMapRS)] +MRTOut PSHDR10_Reinhard_SRGB(VSInputTx pin) +{ + MRTOut output; + + float4 hdr = HDRTexture.Sample(Sampler, pin.TexCoord); + float3 rgb = HDR10(hdr.xyz); + output.hdr = float4(rgb, hdr.a); + + float3 sdr = ToneMapReinhard(hdr.xyz * linearExposure); + float3 srgb = LinearToSRGBEst(sdr); + output.sdr = float4(srgb, hdr.a); + + return output; +} + +[RootSignature(ToneMapRS)] +MRTOut PSHDR10_ACESFilmic_SRGB(VSInputTx pin) +{ + MRTOut output; + + float4 hdr = HDRTexture.Sample(Sampler, pin.TexCoord); + float3 rgb = HDR10(hdr.xyz); + output.hdr = float4(rgb, hdr.a); + + float3 sdr = ToneMapACESFilmic(hdr.xyz * linearExposure); + float3 srgb = LinearToSRGBEst(sdr); + output.sdr = float4(srgb, hdr.a); + + return output; +} diff --git a/Common/DirectXTK12/Src/Shaders/Utilities.fxh b/Common/DirectXTK12/Src/Shaders/Utilities.fxh new file mode 100644 index 0000000..4f72cdc --- /dev/null +++ b/Common/DirectXTK12/Src/Shaders/Utilities.fxh @@ -0,0 +1,123 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248926 +// http://go.microsoft.com/fwlink/?LinkId=248929 +// http://go.microsoft.com/fwlink/?LinkID=615561 + + +float3 BiasX2(float3 x) +{ + return 2.0f * x - 1.0f; +} + +float3 BiasD2(float3 x) +{ + return 0.5f * x + 0.5f; +} + + +// Christian Schuler, "Normal Mapping without Precomputed Tangents", ShaderX 5, Chapter 2.6, pp. 131-140 +// See also follow-up blog post: http://www.thetenthplanet.de/archives/1180 +float3x3 CalculateTBN(float3 p, float3 n, float2 tex) +{ + float3 dp1 = ddx(p); + float3 dp2 = ddy(p); + float2 duv1 = ddx(tex); + float2 duv2 = ddy(tex); + + float3x3 M = float3x3(dp1, dp2, cross(dp1, dp2)); + float2x3 inverseM = float2x3(cross(M[1], M[2]), cross(M[2], M[0])); + float3 t = normalize(mul(float2(duv1.x, duv2.x), inverseM)); + float3 b = normalize(mul(float2(duv1.y, duv2.y), inverseM)); + return float3x3(t, b, n); +} + +float3 PeturbNormal(float3 localNormal, float3 position, float3 normal, float2 texCoord) +{ + const float3x3 TBN = CalculateTBN(position, normal, texCoord); + return normalize(mul(localNormal, TBN)); +} + +float3 TwoChannelNormalX2(float2 normal) +{ + float2 xy = 2.0f * normal - 1.0f; + float z = sqrt(1 - dot(xy, xy)); + return float3(xy.x, xy.y, z); +} + + +// sRGB +// https://en.wikipedia.org/wiki/SRGB + +// Apply the (approximate) sRGB curve to linear values +float3 LinearToSRGBEst(float3 color) +{ + return pow(abs(color), 1/2.2f); +} + + +// (Approximate) sRGB to linear +float3 SRGBToLinearEst(float3 srgb) +{ + return pow(abs(srgb), 2.2f); +} + + +// HDR10 Media Profile +// https://en.wikipedia.org/wiki/High-dynamic-range_video#HDR10 + + +// Apply the ST.2084 curve to normalized linear values and outputs normalized non-linear values +float3 LinearToST2084(float3 normalizedLinearValue) +{ + return pow((0.8359375f + 18.8515625f * pow(abs(normalizedLinearValue), 0.1593017578f)) / (1.0f + 18.6875f * pow(abs(normalizedLinearValue), 0.1593017578f)), 78.84375f); +} + + +// ST.2084 to linear, resulting in a linear normalized value +float3 ST2084ToLinear(float3 ST2084) +{ + return pow(max(pow(abs(ST2084), 1.0f / 78.84375f) - 0.8359375f, 0.0f) / (18.8515625f - 18.6875f * pow(abs(ST2084), 1.0f / 78.84375f)), 1.0f / 0.1593017578f); +} + + +// Reinhard tonemap operator +// Reinhard et al. "Photographic tone reproduction for digital images." ACM Transactions on Graphics. 21. 2002. +// http://www.cs.utah.edu/~reinhard/cdrom/tonemap.pdf +float3 ToneMapReinhard(float3 color) +{ + return color / (1.0f + color); +} + + +// ACES Filmic tonemap operator +// https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/ +float3 ToneMapACESFilmic(float3 x) +{ + float a = 2.51f; + float b = 0.03f; + float c = 2.43f; + float d = 0.59f; + float e = 0.14f; + return saturate((x*(a*x+b))/(x*(c*x+d)+e)); +} + + +// Instancing +struct CommonInstancing +{ + float4 Position; + float3 Normal; +}; + + +CommonInstancing ComputeCommonInstancing(float4 position, float3 normal, float4x3 itransform) +{ + CommonInstancing vout; + + vout.Position = float4(mul(position, itransform), position.w); + vout.Normal = mul(normal, (float3x3)itransform); + + return vout; +} diff --git a/Common/DirectXTK12/Src/SharedResourcePool.h b/Common/DirectXTK12/Src/SharedResourcePool.h new file mode 100644 index 0000000..d7e0d89 --- /dev/null +++ b/Common/DirectXTK12/Src/SharedResourcePool.h @@ -0,0 +1,111 @@ +//-------------------------------------------------------------------------------------- +// File: SharedResourcePool.h +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248929 +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#pragma once + +#include +#include + +#include "PlatformHelpers.h" + + +namespace DirectX +{ + // Pool manager ensures that only a single TData instance is created for each unique TKey. + // This is used to avoid duplicate resource creation, so that for instance a caller can + // create any number of SpriteBatch instances, but these can internally share shaders and + // vertex buffer if more than one SpriteBatch uses the same underlying D3D device. + template + class SharedResourcePool + { + public: + SharedResourcePool() noexcept(false) + : mResourceMap(std::make_shared()) + { + } + + SharedResourcePool(SharedResourcePool const&) = delete; + SharedResourcePool& operator= (SharedResourcePool const&) = delete; + + // Allocates or looks up the shared TData instance for the specified key. + std::shared_ptr DemandCreate(TKey key, TConstructorArgs... args) + { + std::lock_guard lock(mResourceMap->mutex); + + // Return an existing instance? + auto pos = mResourceMap->find(key); + + if (pos != mResourceMap->end()) + { + auto existingValue = pos->second.lock(); + + if (existingValue) + return existingValue; + else + mResourceMap->erase(pos); + } + + // Allocate a new instance. + auto newValue = std::make_shared(key, mResourceMap, args...); + + auto entry = std::make_pair(key, newValue); + mResourceMap->insert(entry); + + return std::move(newValue); + } + + + private: + // Keep track of all allocated TData instances. + struct ResourceMap : public std::map> + { + std::mutex mutex; + }; + + std::shared_ptr mResourceMap; + + + // Wrap TData with our own subclass, so we can hook the destructor + // to remove instances from our pool before they are freed. + struct WrappedData : public TData + { + WrappedData(TKey key, std::shared_ptr const& resourceMap, TConstructorArgs... args) + : TData(key, args...), + mKey(key), + mResourceMap(resourceMap) + { + } + + WrappedData(WrappedData&&) = default; + WrappedData& operator= (WrappedData&&) = default; + + WrappedData(WrappedData const&) = delete; + WrappedData& operator= (WrappedData const&) = delete; + + ~WrappedData() + { + const std::lock_guard lock(mResourceMap->mutex); + + auto const pos = mResourceMap->find(mKey); + + // Check for weak reference expiry before erasing, in case DemandCreate runs on + // a different thread at the same time as a previous instance is being destroyed. + // We mustn't erase replacement objects that have just been added! + if (pos != mResourceMap->end() && pos->second.expired()) + { + mResourceMap->erase(pos); + } + } + + TKey mKey; + std::shared_ptr mResourceMap; + }; + }; +} diff --git a/Common/DirectXTK12/Src/SimpleMath.cpp b/Common/DirectXTK12/Src/SimpleMath.cpp new file mode 100644 index 0000000..3449516 --- /dev/null +++ b/Common/DirectXTK12/Src/SimpleMath.cpp @@ -0,0 +1,247 @@ +//------------------------------------------------------------------------------------- +// SimpleMath.cpp -- Simplified C++ Math wrapper for DirectXMath +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248929 +// http://go.microsoft.com/fwlink/?LinkID=615561 +//------------------------------------------------------------------------------------- + +#include "pch.h" +#include "SimpleMath.h" + +/**************************************************************************** + * + * Constants + * + ****************************************************************************/ + +namespace DirectX +{ + namespace SimpleMath + { + const Vector2 Vector2::Zero = { 0.f, 0.f }; + const Vector2 Vector2::One = { 1.f, 1.f }; + const Vector2 Vector2::UnitX = { 1.f, 0.f }; + const Vector2 Vector2::UnitY = { 0.f, 1.f }; + + const Vector3 Vector3::Zero = { 0.f, 0.f, 0.f }; + const Vector3 Vector3::One = { 1.f, 1.f, 1.f }; + const Vector3 Vector3::UnitX = { 1.f, 0.f, 0.f }; + const Vector3 Vector3::UnitY = { 0.f, 1.f, 0.f }; + const Vector3 Vector3::UnitZ = { 0.f, 0.f, 1.f }; + const Vector3 Vector3::Up = { 0.f, 1.f, 0.f }; + const Vector3 Vector3::Down = { 0.f, -1.f, 0.f }; + const Vector3 Vector3::Right = { 1.f, 0.f, 0.f }; + const Vector3 Vector3::Left = { -1.f, 0.f, 0.f }; + const Vector3 Vector3::Forward = { 0.f, 0.f, -1.f }; + const Vector3 Vector3::Backward = { 0.f, 0.f, 1.f }; + + const Vector4 Vector4::Zero = { 0.f, 0.f, 0.f, 0.f }; + const Vector4 Vector4::One = { 1.f, 1.f, 1.f, 1.f }; + const Vector4 Vector4::UnitX = { 1.f, 0.f, 0.f, 0.f }; + const Vector4 Vector4::UnitY = { 0.f, 1.f, 0.f, 0.f }; + const Vector4 Vector4::UnitZ = { 0.f, 0.f, 1.f, 0.f }; + const Vector4 Vector4::UnitW = { 0.f, 0.f, 0.f, 1.f }; + + const Matrix Matrix::Identity = { 1.f, 0.f, 0.f, 0.f, + 0.f, 1.f, 0.f, 0.f, + 0.f, 0.f, 1.f, 0.f, + 0.f, 0.f, 0.f, 1.f }; + + const Quaternion Quaternion::Identity = { 0.f, 0.f, 0.f, 1.f }; + } +} + +using namespace DirectX; +using namespace DirectX::SimpleMath; + +/**************************************************************************** + * + * Quaternion + * + ****************************************************************************/ + +void Quaternion::RotateTowards(const Quaternion& target, float maxAngle, Quaternion& result) const noexcept +{ + const XMVECTOR T = XMLoadFloat4(this); + + // We can use the conjugate here instead of inverse assuming q1 & q2 are normalized. + const XMVECTOR R = XMQuaternionMultiply(XMQuaternionConjugate(T), target); + + const float rs = XMVectorGetW(R); + const XMVECTOR L = XMVector3Length(R); + const float angle = 2.f * atan2f(XMVectorGetX(L), rs); + if (angle > maxAngle) + { + const XMVECTOR delta = XMQuaternionRotationAxis(R, maxAngle); + const XMVECTOR Q = XMQuaternionMultiply(delta, T); + XMStoreFloat4(&result, Q); + } + else + { + // Don't overshoot. + result = target; + } +} + +void Quaternion::FromToRotation(const Vector3& fromDir, const Vector3& toDir, Quaternion& result) noexcept +{ + // Melax, "The Shortest Arc Quaternion", Game Programming Gems, Charles River Media (2000). + + const XMVECTOR F = XMVector3Normalize(fromDir); + const XMVECTOR T = XMVector3Normalize(toDir); + + const float dot = XMVectorGetX(XMVector3Dot(F, T)); + if (dot >= 1.f) + { + result = Identity; + } + else if (dot <= -1.f) + { + XMVECTOR axis = XMVector3Cross(F, Vector3::Right); + if (XMVector3NearEqual(XMVector3LengthSq(axis), g_XMZero, g_XMEpsilon)) + { + axis = XMVector3Cross(F, Vector3::Up); + } + + const XMVECTOR Q = XMQuaternionRotationAxis(axis, XM_PI); + XMStoreFloat4(&result, Q); + } + else + { + const XMVECTOR C = XMVector3Cross(F, T); + XMStoreFloat4(&result, C); + + const float s = sqrtf((1.f + dot) * 2.f); + result.x /= s; + result.y /= s; + result.z /= s; + result.w = s * 0.5f; + } +} + +void Quaternion::LookRotation(const Vector3& forward, const Vector3& up, Quaternion& result) noexcept +{ + Quaternion q1; + FromToRotation(Vector3::Forward, forward, q1); + + const XMVECTOR C = XMVector3Cross(forward, up); + if (XMVector3NearEqual(XMVector3LengthSq(C), g_XMZero, g_XMEpsilon)) + { + // forward and up are co-linear + result = q1; + return; + } + + const XMVECTOR U = XMQuaternionMultiply(q1, Vector3::Up); + + Quaternion q2; + FromToRotation(U, up, q2); + + XMStoreFloat4(&result, XMQuaternionMultiply(q2, q1)); +} + + + /**************************************************************************** + * + * Viewport + * + ****************************************************************************/ + +#if defined(__d3d11_h__) || defined(__d3d11_x_h__) +static_assert(sizeof(DirectX::SimpleMath::Viewport) == sizeof(D3D11_VIEWPORT), "Size mismatch"); +static_assert(offsetof(DirectX::SimpleMath::Viewport, x) == offsetof(D3D11_VIEWPORT, TopLeftX), "Layout mismatch"); +static_assert(offsetof(DirectX::SimpleMath::Viewport, y) == offsetof(D3D11_VIEWPORT, TopLeftY), "Layout mismatch"); +static_assert(offsetof(DirectX::SimpleMath::Viewport, width) == offsetof(D3D11_VIEWPORT, Width), "Layout mismatch"); +static_assert(offsetof(DirectX::SimpleMath::Viewport, height) == offsetof(D3D11_VIEWPORT, Height), "Layout mismatch"); +static_assert(offsetof(DirectX::SimpleMath::Viewport, minDepth) == offsetof(D3D11_VIEWPORT, MinDepth), "Layout mismatch"); +static_assert(offsetof(DirectX::SimpleMath::Viewport, maxDepth) == offsetof(D3D11_VIEWPORT, MaxDepth), "Layout mismatch"); +#endif + +#if defined(__d3d12_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) +static_assert(sizeof(DirectX::SimpleMath::Viewport) == sizeof(D3D12_VIEWPORT), "Size mismatch"); +static_assert(offsetof(DirectX::SimpleMath::Viewport, x) == offsetof(D3D12_VIEWPORT, TopLeftX), "Layout mismatch"); +static_assert(offsetof(DirectX::SimpleMath::Viewport, y) == offsetof(D3D12_VIEWPORT, TopLeftY), "Layout mismatch"); +static_assert(offsetof(DirectX::SimpleMath::Viewport, width) == offsetof(D3D12_VIEWPORT, Width), "Layout mismatch"); +static_assert(offsetof(DirectX::SimpleMath::Viewport, height) == offsetof(D3D12_VIEWPORT, Height), "Layout mismatch"); +static_assert(offsetof(DirectX::SimpleMath::Viewport, minDepth) == offsetof(D3D12_VIEWPORT, MinDepth), "Layout mismatch"); +static_assert(offsetof(DirectX::SimpleMath::Viewport, maxDepth) == offsetof(D3D12_VIEWPORT, MaxDepth), "Layout mismatch"); +#endif + +#if defined(__dxgi1_2_h__) || defined(__d3d11_x_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) +RECT Viewport::ComputeDisplayArea(DXGI_SCALING scaling, UINT backBufferWidth, UINT backBufferHeight, int outputWidth, int outputHeight) noexcept +{ + RECT rct = {}; + + switch (int(scaling)) + { + case DXGI_SCALING_STRETCH: + // Output fills the entire window area + rct.top = 0; + rct.left = 0; + rct.right = outputWidth; + rct.bottom = outputHeight; + break; + + case 2 /*DXGI_SCALING_ASPECT_RATIO_STRETCH*/: + // Output fills the window area but respects the original aspect ratio, using pillar boxing or letter boxing as required + // Note: This scaling option is not supported for legacy Win32 windows swap chains + { + assert(backBufferHeight > 0); + const float aspectRatio = float(backBufferWidth) / float(backBufferHeight); + + // Horizontal fill + float scaledWidth = float(outputWidth); + float scaledHeight = float(outputWidth) / aspectRatio; + if (scaledHeight >= float(outputHeight)) + { + // Do vertical fill + scaledWidth = float(outputHeight) * aspectRatio; + scaledHeight = float(outputHeight); + } + + const float offsetX = (float(outputWidth) - scaledWidth) * 0.5f; + const float offsetY = (float(outputHeight) - scaledHeight) * 0.5f; + + rct.left = static_cast(offsetX); + rct.top = static_cast(offsetY); + rct.right = static_cast(offsetX + scaledWidth); + rct.bottom = static_cast(offsetY + scaledHeight); + + // Clip to display window + rct.left = std::max(0, rct.left); + rct.top = std::max(0, rct.top); + rct.right = std::min(outputWidth, rct.right); + rct.bottom = std::min(outputHeight, rct.bottom); + } + break; + + case DXGI_SCALING_NONE: + default: + // Output is displayed in the upper left corner of the window area + rct.top = 0; + rct.left = 0; + rct.right = std::min(static_cast(backBufferWidth), outputWidth); + rct.bottom = std::min(static_cast(backBufferHeight), outputHeight); + break; + } + + return rct; +} +#endif + +RECT Viewport::ComputeTitleSafeArea(UINT backBufferWidth, UINT backBufferHeight) noexcept +{ + const float safew = (float(backBufferWidth) + 19.f) / 20.f; + const float safeh = (float(backBufferHeight) + 19.f) / 20.f; + + RECT rct; + rct.left = static_cast(safew); + rct.top = static_cast(safeh); + rct.right = static_cast(float(backBufferWidth) - safew + 0.5f); + rct.bottom = static_cast(float(backBufferHeight) - safeh + 0.5f); + + return rct; +} diff --git a/Common/DirectXTK12/Src/SkinnedEffect.cpp b/Common/DirectXTK12/Src/SkinnedEffect.cpp new file mode 100644 index 0000000..eff159a --- /dev/null +++ b/Common/DirectXTK12/Src/SkinnedEffect.cpp @@ -0,0 +1,562 @@ +//-------------------------------------------------------------------------------------- +// File: SkinnedEffect.cpp +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "pch.h" +#include "EffectCommon.h" + +using namespace DirectX; + +namespace +{ + // Constant buffer layout. Must match the shader! + struct SkinnedEffectConstants + { + XMVECTOR diffuseColor; + XMVECTOR emissiveColor; + XMVECTOR specularColorAndPower; + + XMVECTOR lightDirection[IEffectLights::MaxDirectionalLights]; + XMVECTOR lightDiffuseColor[IEffectLights::MaxDirectionalLights]; + XMVECTOR lightSpecularColor[IEffectLights::MaxDirectionalLights]; + + XMVECTOR eyePosition; + + XMVECTOR fogColor; + XMVECTOR fogVector; + + XMMATRIX world; + XMVECTOR worldInverseTranspose[3]; + XMMATRIX worldViewProj; + + XMVECTOR bones[SkinnedEffect::MaxBones][3]; + }; + + static_assert((sizeof(SkinnedEffectConstants) % 16) == 0, "CB size not padded correctly"); + + + // Traits type describes our characteristics to the EffectBase template. + struct SkinnedEffectTraits + { + using ConstantBufferType = SkinnedEffectConstants; + + static constexpr int VertexShaderCount = 4; + static constexpr int PixelShaderCount = 3; + static constexpr int ShaderPermutationCount = 8; + static constexpr int RootSignatureCount = 1; + }; +} + +// Internal SkinnedEffect implementation class. +class SkinnedEffect::Impl : public EffectBase +{ +public: + Impl(_In_ ID3D12Device* device, uint32_t effectFlags, const EffectPipelineStateDescription& pipelineDescription); + + enum RootParameterIndex + { + ConstantBuffer, + TextureSRV, + TextureSampler, + RootParameterCount + }; + + D3D12_GPU_DESCRIPTOR_HANDLE texture; + D3D12_GPU_DESCRIPTOR_HANDLE sampler; + + EffectLights lights; + + int GetPipelineStatePermutation(uint32_t effectFlags) const noexcept; + + void Apply(_In_ ID3D12GraphicsCommandList* commandList); +}; + + +#pragma region Shaders +// Include the precompiled shader code. +namespace +{ +#ifdef _GAMING_XBOX_SCARLETT +#include "XboxGamingScarlettSkinnedEffect_VSSkinnedVertexLightingFourBones.inc" +#include "XboxGamingScarlettSkinnedEffect_VSSkinnedPixelLightingFourBones.inc" +#include "XboxGamingScarlettSkinnedEffect_VSSkinnedVertexLightingFourBonesBn.inc" +#include "XboxGamingScarlettSkinnedEffect_VSSkinnedPixelLightingFourBonesBn.inc" + +#include "XboxGamingScarlettSkinnedEffect_PSSkinnedVertexLighting.inc" +#include "XboxGamingScarlettSkinnedEffect_PSSkinnedVertexLightingNoFog.inc" +#include "XboxGamingScarlettSkinnedEffect_PSSkinnedPixelLighting.inc" +#elif defined(_GAMING_XBOX) +#include "XboxGamingXboxOneSkinnedEffect_VSSkinnedVertexLightingFourBones.inc" +#include "XboxGamingXboxOneSkinnedEffect_VSSkinnedPixelLightingFourBones.inc" +#include "XboxGamingXboxOneSkinnedEffect_VSSkinnedVertexLightingFourBonesBn.inc" +#include "XboxGamingXboxOneSkinnedEffect_VSSkinnedPixelLightingFourBonesBn.inc" + +#include "XboxGamingXboxOneSkinnedEffect_PSSkinnedVertexLighting.inc" +#include "XboxGamingXboxOneSkinnedEffect_PSSkinnedVertexLightingNoFog.inc" +#include "XboxGamingXboxOneSkinnedEffect_PSSkinnedPixelLighting.inc" +#elif defined(_XBOX_ONE) && defined(_TITLE) +#include "XboxOneSkinnedEffect_VSSkinnedVertexLightingFourBones.inc" +#include "XboxOneSkinnedEffect_VSSkinnedPixelLightingFourBones.inc" +#include "XboxOneSkinnedEffect_VSSkinnedVertexLightingFourBonesBn.inc" +#include "XboxOneSkinnedEffect_VSSkinnedPixelLightingFourBonesBn.inc" + +#include "XboxOneSkinnedEffect_PSSkinnedVertexLighting.inc" +#include "XboxOneSkinnedEffect_PSSkinnedVertexLightingNoFog.inc" +#include "XboxOneSkinnedEffect_PSSkinnedPixelLighting.inc" +#else +#include "SkinnedEffect_VSSkinnedVertexLightingFourBones.inc" +#include "SkinnedEffect_VSSkinnedPixelLightingFourBones.inc" +#include "SkinnedEffect_VSSkinnedVertexLightingFourBonesBn.inc" +#include "SkinnedEffect_VSSkinnedPixelLightingFourBonesBn.inc" + +#include "SkinnedEffect_PSSkinnedVertexLighting.inc" +#include "SkinnedEffect_PSSkinnedVertexLightingNoFog.inc" +#include "SkinnedEffect_PSSkinnedPixelLighting.inc" +#endif +} + + +template<> +const D3D12_SHADER_BYTECODE EffectBase::VertexShaderBytecode[] = +{ + { SkinnedEffect_VSSkinnedVertexLightingFourBones, sizeof(SkinnedEffect_VSSkinnedVertexLightingFourBones) }, + { SkinnedEffect_VSSkinnedPixelLightingFourBones, sizeof(SkinnedEffect_VSSkinnedPixelLightingFourBones) }, + { SkinnedEffect_VSSkinnedVertexLightingFourBonesBn, sizeof(SkinnedEffect_VSSkinnedVertexLightingFourBonesBn) }, + { SkinnedEffect_VSSkinnedPixelLightingFourBonesBn, sizeof(SkinnedEffect_VSSkinnedPixelLightingFourBonesBn) }, +}; + + +template<> +const int EffectBase::VertexShaderIndices[] = +{ + 0, // vertex lighting, four bones + 0, // vertex lighting, four bones, no fog + + 1, // pixel lighting, four bones + 1, // pixel lighting, four bones, no fog + + 2, // vertex lighting (biased vertex normals), four bones + 2, // vertex lighting (biased vertex normals), four bones, no fog + + 3, // pixel lighting (biased vertex normals), four bones + 3, // pixel lighting (biased vertex normals), four bones, no fog +}; + + +template<> +const D3D12_SHADER_BYTECODE EffectBase::PixelShaderBytecode[] = +{ + { SkinnedEffect_PSSkinnedVertexLighting, sizeof(SkinnedEffect_PSSkinnedVertexLighting) }, + { SkinnedEffect_PSSkinnedVertexLightingNoFog, sizeof(SkinnedEffect_PSSkinnedVertexLightingNoFog) }, + { SkinnedEffect_PSSkinnedPixelLighting, sizeof(SkinnedEffect_PSSkinnedPixelLighting) }, +}; + + +template<> +const int EffectBase::PixelShaderIndices[] = +{ + 0, // vertex lighting, four bones + 1, // vertex lighting, four bones, no fog + + 2, // pixel lighting, four bones + 2, // pixel lighting, four bones, no fog + + 0, // vertex lighting (biased vertex normals), four bones + 1, // vertex lighting (biased vertex normals), four bones, no fog + + 2, // pixel lighting (biased vertex normals), four bones + 2, // pixel lighting (biased vertex normals), four bones, no fog +}; +#pragma endregion + +// Global pool of per-device SkinnedEffect resources. +template<> +SharedResourcePool::DeviceResources> EffectBase::deviceResourcesPool = {}; + + +// Constructor. +SkinnedEffect::Impl::Impl( + _In_ ID3D12Device* device, + uint32_t effectFlags, + const EffectPipelineStateDescription& pipelineDescription) + : EffectBase(device), + texture{}, + sampler{} +{ + static_assert(static_cast(std::size(EffectBase::VertexShaderIndices)) == SkinnedEffectTraits::ShaderPermutationCount, "array/max mismatch"); + static_assert(static_cast(std::size(EffectBase::VertexShaderBytecode)) == SkinnedEffectTraits::VertexShaderCount, "array/max mismatch"); + static_assert(static_cast(std::size(EffectBase::PixelShaderBytecode)) == SkinnedEffectTraits::PixelShaderCount, "array/max mismatch"); + static_assert(static_cast(std::size(EffectBase::PixelShaderIndices)) == SkinnedEffectTraits::ShaderPermutationCount, "array/max mismatch"); + + lights.InitializeConstants(constants.specularColorAndPower, constants.lightDirection, constants.lightDiffuseColor, constants.lightSpecularColor); + + for (int i = 0; i < MaxBones; i++) + { + constants.bones[i][0] = g_XMIdentityR0; + constants.bones[i][1] = g_XMIdentityR1; + constants.bones[i][2] = g_XMIdentityR2; + } + + // Create root signature. + { + ENUM_FLAGS_CONSTEXPR D3D12_ROOT_SIGNATURE_FLAGS rootSignatureFlags = + D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT + | D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS + | D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS + | D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS +#ifdef _GAMING_XBOX_SCARLETT + | D3D12_ROOT_SIGNATURE_FLAG_DENY_AMPLIFICATION_SHADER_ROOT_ACCESS + | D3D12_ROOT_SIGNATURE_FLAG_DENY_MESH_SHADER_ROOT_ACCESS +#endif + ; + + const CD3DX12_DESCRIPTOR_RANGE textureSrvDescriptorRange(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0); + const CD3DX12_DESCRIPTOR_RANGE textureSamplerDescriptorRange(D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, 1, 0); + + CD3DX12_ROOT_PARAMETER rootParameters[RootParameterIndex::RootParameterCount] = {}; + rootParameters[RootParameterIndex::TextureSRV].InitAsDescriptorTable(1, &textureSrvDescriptorRange, D3D12_SHADER_VISIBILITY_PIXEL); + rootParameters[RootParameterIndex::TextureSampler].InitAsDescriptorTable(1, &textureSamplerDescriptorRange, D3D12_SHADER_VISIBILITY_PIXEL); + rootParameters[RootParameterIndex::ConstantBuffer].InitAsConstantBufferView(0, 0, D3D12_SHADER_VISIBILITY_ALL); + + CD3DX12_ROOT_SIGNATURE_DESC rsigDesc = {}; + rsigDesc.Init(static_cast(std::size(rootParameters)), rootParameters, 0, nullptr, rootSignatureFlags); + + mRootSignature = GetRootSignature(0, rsigDesc); + } + + assert(mRootSignature != nullptr); + + fog.enabled = (effectFlags & EffectFlags::Fog) != 0; + + if (effectFlags & EffectFlags::VertexColor) + { + DebugTrace("ERROR: SkinnedEffect does not implement EffectFlags::VertexColor\n"); + throw std::invalid_argument("VertexColor effect flag is invalid"); + } + else if (effectFlags & EffectFlags::Instancing) + { + DebugTrace("ERROR: SkinnedEffect does not implement EffectFlags::Instancing\n"); + throw std::invalid_argument("Instancing effect flag is invalid"); + } + + // Create pipeline state. + const int sp = GetPipelineStatePermutation(effectFlags); + assert(sp >= 0 && sp < SkinnedEffectTraits::ShaderPermutationCount); + _Analysis_assume_(sp >= 0 && sp < SkinnedEffectTraits::ShaderPermutationCount); + + const int vi = EffectBase::VertexShaderIndices[sp]; + assert(vi >= 0 && vi < SkinnedEffectTraits::VertexShaderCount); + _Analysis_assume_(vi >= 0 && vi < SkinnedEffectTraits::VertexShaderCount); + const int pi = EffectBase::PixelShaderIndices[sp]; + assert(pi >= 0 && pi < SkinnedEffectTraits::PixelShaderCount); + _Analysis_assume_(pi >= 0 && pi < SkinnedEffectTraits::PixelShaderCount); + + pipelineDescription.CreatePipelineState( + device, + mRootSignature, + EffectBase::VertexShaderBytecode[vi], + EffectBase::PixelShaderBytecode[pi], + mPipelineState.GetAddressOf()); + + SetDebugObjectName(mPipelineState.Get(), L"SkinnedEffect"); +} + + +int SkinnedEffect::Impl::GetPipelineStatePermutation(uint32_t effectFlags) const noexcept +{ + int permutation = 0; + + // Use optimized shaders if fog is disabled. + if (!fog.enabled) + { + permutation += 1; + } + + if (effectFlags & EffectFlags::PerPixelLightingBit) + { + // Do lighting in the pixel shader. + permutation += 2; + } + + if (effectFlags & EffectFlags::BiasedVertexNormals) + { + // Compressed normals need to be scaled and biased in the vertex shader. + permutation += 4; + } + + return permutation; +} + + +// Sets our state onto the D3D device. +void SkinnedEffect::Impl::Apply(_In_ ID3D12GraphicsCommandList* commandList) +{ + // Compute derived parameter values. + matrices.SetConstants(dirtyFlags, constants.worldViewProj); + fog.SetConstants(dirtyFlags, matrices.worldView, constants.fogVector); + lights.SetConstants(dirtyFlags, matrices, constants.world, constants.worldInverseTranspose, constants.eyePosition, constants.diffuseColor, constants.emissiveColor, true); + + UpdateConstants(); + + // Set the root signature + commandList->SetGraphicsRootSignature(mRootSignature); + + // Set the texture + if (!texture.ptr || !sampler.ptr) + { + DebugTrace("ERROR: Missing texture or sampler for SkinnedEffect (texture %llu, sampler %llu)\n", texture.ptr, sampler.ptr); + throw std::runtime_error("SkinnedEffect"); + } + + // **NOTE** If D3D asserts or crashes here, you probably need to call commandList->SetDescriptorHeaps() with the required descriptor heaps. + commandList->SetGraphicsRootDescriptorTable(RootParameterIndex::TextureSRV, texture); + commandList->SetGraphicsRootDescriptorTable(RootParameterIndex::TextureSampler, sampler); + + // Set constants + commandList->SetGraphicsRootConstantBufferView(RootParameterIndex::ConstantBuffer, GetConstantBufferGpuAddress()); + + // Set the pipeline state + commandList->SetPipelineState(EffectBase::mPipelineState.Get()); +} + + +// Public constructor. +SkinnedEffect::SkinnedEffect( + _In_ ID3D12Device* device, + uint32_t effectFlags, + const EffectPipelineStateDescription& pipelineDescription) + : pImpl(std::make_unique(device, effectFlags, pipelineDescription)) +{ +} + + +SkinnedEffect::SkinnedEffect(SkinnedEffect&&) noexcept = default; +SkinnedEffect& SkinnedEffect::operator= (SkinnedEffect&&) noexcept = default; +SkinnedEffect::~SkinnedEffect() = default; + + +// IEffect methods. +void SkinnedEffect::Apply(_In_ ID3D12GraphicsCommandList* commandList) +{ + pImpl->Apply(commandList); +} + + +// Camera settings. +void XM_CALLCONV SkinnedEffect::SetWorld(FXMMATRIX value) +{ + pImpl->matrices.world = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::WorldViewProj | EffectDirtyFlags::WorldInverseTranspose | EffectDirtyFlags::FogVector; +} + + +void XM_CALLCONV SkinnedEffect::SetView(FXMMATRIX value) +{ + pImpl->matrices.view = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::WorldViewProj | EffectDirtyFlags::EyePosition | EffectDirtyFlags::FogVector; +} + + +void XM_CALLCONV SkinnedEffect::SetProjection(FXMMATRIX value) +{ + pImpl->matrices.projection = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::WorldViewProj; +} + + +void XM_CALLCONV SkinnedEffect::SetMatrices(FXMMATRIX world, CXMMATRIX view, CXMMATRIX projection) +{ + pImpl->matrices.world = world; + pImpl->matrices.view = view; + pImpl->matrices.projection = projection; + + pImpl->dirtyFlags |= EffectDirtyFlags::WorldViewProj | EffectDirtyFlags::WorldInverseTranspose | EffectDirtyFlags::EyePosition | EffectDirtyFlags::FogVector; +} + + +// Material settings. +void XM_CALLCONV SkinnedEffect::SetDiffuseColor(FXMVECTOR value) +{ + pImpl->lights.diffuseColor = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::MaterialColor; +} + + +void XM_CALLCONV SkinnedEffect::SetEmissiveColor(FXMVECTOR value) +{ + pImpl->lights.emissiveColor = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::MaterialColor; +} + + +void XM_CALLCONV SkinnedEffect::SetSpecularColor(FXMVECTOR value) +{ + // Set xyz to new value, but preserve existing w (specular power). + pImpl->constants.specularColorAndPower = XMVectorSelect(pImpl->constants.specularColorAndPower, value, g_XMSelect1110); + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; +} + + +void SkinnedEffect::SetSpecularPower(float value) +{ + // Set w to new value, but preserve existing xyz (specular color). + pImpl->constants.specularColorAndPower = XMVectorSetW(pImpl->constants.specularColorAndPower, value); + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; +} + + +void SkinnedEffect::DisableSpecular() +{ + // Set specular color to black, power to 1 + // Note: Don't use a power of 0 or the shader will generate strange highlights on non-specular materials + + pImpl->constants.specularColorAndPower = g_XMIdentityR3; + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; +} + + +void SkinnedEffect::SetAlpha(float value) +{ + pImpl->lights.alpha = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::MaterialColor; +} + + +void XM_CALLCONV SkinnedEffect::SetColorAndAlpha(FXMVECTOR value) +{ + pImpl->lights.diffuseColor = value; + pImpl->lights.alpha = XMVectorGetW(value); + + pImpl->dirtyFlags |= EffectDirtyFlags::MaterialColor; +} + + +// Light settings. +void XM_CALLCONV SkinnedEffect::SetAmbientLightColor(FXMVECTOR value) +{ + pImpl->lights.ambientLightColor = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::MaterialColor; +} + + +void SkinnedEffect::SetLightEnabled(int whichLight, bool value) +{ + pImpl->dirtyFlags |= pImpl->lights.SetLightEnabled(whichLight, value, pImpl->constants.lightDiffuseColor, pImpl->constants.lightSpecularColor); +} + + +void XM_CALLCONV SkinnedEffect::SetLightDirection(int whichLight, FXMVECTOR value) +{ + EffectLights::ValidateLightIndex(whichLight); + + pImpl->constants.lightDirection[whichLight] = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; +} + + +void XM_CALLCONV SkinnedEffect::SetLightDiffuseColor(int whichLight, FXMVECTOR value) +{ + pImpl->dirtyFlags |= pImpl->lights.SetLightDiffuseColor(whichLight, value, pImpl->constants.lightDiffuseColor); +} + + +void XM_CALLCONV SkinnedEffect::SetLightSpecularColor(int whichLight, FXMVECTOR value) +{ + pImpl->dirtyFlags |= pImpl->lights.SetLightSpecularColor(whichLight, value, pImpl->constants.lightSpecularColor); +} + + +void SkinnedEffect::EnableDefaultLighting() +{ + EffectLights::EnableDefaultLighting(this); +} + + +// Fog settings. +void SkinnedEffect::SetFogStart(float value) +{ + pImpl->fog.start = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::FogVector; +} + + +void SkinnedEffect::SetFogEnd(float value) +{ + pImpl->fog.end = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::FogVector; +} + + +void XM_CALLCONV SkinnedEffect::SetFogColor(FXMVECTOR value) +{ + pImpl->constants.fogColor = value; + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; +} + + +// Texture settings. +void SkinnedEffect::SetTexture(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor, D3D12_GPU_DESCRIPTOR_HANDLE samplerDescriptor) +{ + pImpl->texture = srvDescriptor; + pImpl->sampler = samplerDescriptor; +} + + +// Animation settings. +void SkinnedEffect::SetBoneTransforms(_In_reads_(count) XMMATRIX const* value, size_t count) +{ + if (count > MaxBones) + throw std::invalid_argument("count parameter exceeds MaxBones"); + + auto boneConstant = pImpl->constants.bones; + + for (size_t i = 0; i < count; i++) + { + #if DIRECTX_MATH_VERSION >= 313 + XMStoreFloat3x4A(reinterpret_cast(&boneConstant[i]), value[i]); + #else + // Xbox One XDK has an older version of DirectXMath + XMMATRIX boneMatrix = XMMatrixTranspose(value[i]); + + boneConstant[i][0] = boneMatrix.r[0]; + boneConstant[i][1] = boneMatrix.r[1]; + boneConstant[i][2] = boneMatrix.r[2]; + #endif + } + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; +} + + +void SkinnedEffect::ResetBoneTransforms() +{ + auto boneConstant = pImpl->constants.bones; + + for (size_t i = 0; i < MaxBones; ++i) + { + boneConstant[i][0] = g_XMIdentityR0; + boneConstant[i][1] = g_XMIdentityR1; + boneConstant[i][2] = g_XMIdentityR2; + } + + pImpl->dirtyFlags |= EffectDirtyFlags::ConstantBuffer; +} diff --git a/Common/DirectXTK12/Src/SpriteBatch.cpp b/Common/DirectXTK12/Src/SpriteBatch.cpp new file mode 100644 index 0000000..526aa8d --- /dev/null +++ b/Common/DirectXTK12/Src/SpriteBatch.cpp @@ -0,0 +1,1243 @@ +//-------------------------------------------------------------------------------------- +// File: SpriteBatch.cpp +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "pch.h" +#include "SpriteBatch.h" + +#include "AlignedNew.h" +#include "CommonStates.h" +#include "DirectXHelpers.h" +#include "GraphicsMemory.h" +#include "PlatformHelpers.h" +#include "ResourceUploadBatch.h" +#include "SharedResourcePool.h" +#include "VertexTypes.h" + +using namespace DirectX; +using Microsoft::WRL::ComPtr; + +namespace +{ + // Include the precompiled shader code. +#ifdef _GAMING_XBOX_SCARLETT +#include "XboxGamingScarlettSpriteEffect_SpriteVertexShader.inc" +#include "XboxGamingScarlettSpriteEffect_SpritePixelShader.inc" +#include "XboxGamingScarlettSpriteEffect_SpriteVertexShaderHeap.inc" +#include "XboxGamingScarlettSpriteEffect_SpritePixelShaderHeap.inc" +#elif defined(_GAMING_XBOX) +#include "XboxGamingXboxOneSpriteEffect_SpriteVertexShader.inc" +#include "XboxGamingXboxOneSpriteEffect_SpritePixelShader.inc" +#include "XboxGamingXboxOneSpriteEffect_SpriteVertexShaderHeap.inc" +#include "XboxGamingXboxOneSpriteEffect_SpritePixelShaderHeap.inc" +#elif defined(_XBOX_ONE) && defined(_TITLE) +#include "XboxOneSpriteEffect_SpriteVertexShader.inc" +#include "XboxOneSpriteEffect_SpritePixelShader.inc" +#include "XboxOneSpriteEffect_SpriteVertexShaderHeap.inc" +#include "XboxOneSpriteEffect_SpritePixelShaderHeap.inc" +#else +#include "SpriteEffect_SpriteVertexShader.inc" +#include "SpriteEffect_SpritePixelShader.inc" +#include "SpriteEffect_SpriteVertexShaderHeap.inc" +#include "SpriteEffect_SpritePixelShaderHeap.inc" +#endif + + inline bool operator != (D3D12_GPU_DESCRIPTOR_HANDLE a, D3D12_GPU_DESCRIPTOR_HANDLE b) noexcept + { + return a.ptr != b.ptr; + } + inline bool operator < (D3D12_GPU_DESCRIPTOR_HANDLE a, D3D12_GPU_DESCRIPTOR_HANDLE b) noexcept + { + return a.ptr < b.ptr; + } + + // Helper converts a RECT to XMVECTOR. + inline XMVECTOR LoadRect(_In_ RECT const* rect) noexcept + { + XMVECTOR v = XMLoadInt4(reinterpret_cast(rect)); + + v = XMConvertVectorIntToFloat(v, 0); + + // Convert right/bottom to width/height. + v = XMVectorSubtract(v, XMVectorPermute<0, 1, 4, 5>(g_XMZero, v)); + + return v; + } +} + +// Internal SpriteBatch implementation class. +XM_ALIGNED_STRUCT(16) SpriteBatch::Impl : public AlignedNew +{ +public: + Impl(_In_ ID3D12Device* device, + ResourceUploadBatch& upload, + const SpriteBatchPipelineStateDescription& psoDesc, + const D3D12_VIEWPORT* viewport); + + void XM_CALLCONV Begin( + _In_ ID3D12GraphicsCommandList* commandList, + SpriteSortMode sortMode = SpriteSortMode_Deferred, + FXMMATRIX transformMatrix = MatrixIdentity); + void End(); + + void XM_CALLCONV Draw( + D3D12_GPU_DESCRIPTOR_HANDLE texture, + XMUINT2 const& textureSize, + FXMVECTOR destination, + _In_opt_ RECT const* sourceRectangle, + FXMVECTOR color, + FXMVECTOR originRotationDepth, + unsigned int flags); + + // Info about a single sprite that is waiting to be drawn. + XM_ALIGNED_STRUCT(16) SpriteInfo : public AlignedNew + { + XMFLOAT4A source; + XMFLOAT4A destination; + XMFLOAT4A color; + XMFLOAT4A originRotationDepth; + D3D12_GPU_DESCRIPTOR_HANDLE texture; + XMVECTOR textureSize; + unsigned int flags; + + // Combine values from the public SpriteEffects enum with these internal-only flags. + static constexpr unsigned int SourceInTexels = 4; + static constexpr unsigned int DestSizeInPixels = 8; + + static_assert((SpriteEffects_FlipBoth & (SourceInTexels | DestSizeInPixels)) == 0, "Flag bits must not overlap"); + }; + + DXGI_MODE_ROTATION mRotation; + + bool mSetViewport; + D3D12_VIEWPORT mViewPort; + D3D12_GPU_DESCRIPTOR_HANDLE mSampler; + +private: + // Implementation helper methods. + void GrowSpriteQueue(); + void PrepareForRendering(); + void FlushBatch(); + void SortSprites(); + void GrowSortedSprites(); + + void RenderBatch( + D3D12_GPU_DESCRIPTOR_HANDLE texture, + XMVECTOR textureSize, + _In_reads_(count) SpriteInfo const* const* sprites, + size_t count); + + static void XM_CALLCONV RenderSprite(_In_ SpriteInfo const* sprite, + _Out_writes_(VerticesPerSprite) VertexPositionColorTexture* vertices, + FXMVECTOR textureSize, + FXMVECTOR inverseTextureSize) noexcept; + + XMMATRIX GetViewportTransform(_In_ DXGI_MODE_ROTATION rotation); + + // Constants. + static constexpr size_t MaxBatchSize = 2048; + static constexpr size_t MinBatchSize = 128; + static constexpr size_t InitialQueueSize = 64; + static constexpr size_t VerticesPerSprite = 4; + static constexpr size_t IndicesPerSprite = 6; + + // + // The following functions and members are used to create the default pipeline state objects. + // + static const D3D12_SHADER_BYTECODE s_DefaultVertexShaderByteCodeStatic; + static const D3D12_SHADER_BYTECODE s_DefaultPixelShaderByteCodeStatic; + static const D3D12_SHADER_BYTECODE s_DefaultVertexShaderByteCodeHeap; + static const D3D12_SHADER_BYTECODE s_DefaultPixelShaderByteCodeHeap; + static const D3D12_INPUT_LAYOUT_DESC s_DefaultInputLayoutDesc; + + + // Queue of sprites waiting to be drawn. + std::unique_ptr mSpriteQueue; + + size_t mSpriteQueueCount; + size_t mSpriteQueueArraySize; + + + // To avoid needlessly copying around bulky SpriteInfo structures, we leave that + // actual data alone and just sort this array of pointers instead. But we want contiguous + // memory for cache efficiency, so these pointers are just shortcuts into the single + // mSpriteQueue array, and we take care to keep them in order when sorting is disabled. + std::vector mSortedSprites; + + + // Mode settings from the last Begin call. + bool mInBeginEndPair; + + SpriteSortMode mSortMode; + ComPtr mPSO; + ComPtr mRootSignature; + XMMATRIX mTransformMatrix; + ComPtr mCommandList; + + // Batched data + GraphicsResource mVertexSegment; + size_t mVertexPageSize; + size_t mSpriteCount; + GraphicsResource mConstantBuffer; + + enum RootParameterIndex + { + TextureSRV, + ConstantBuffer, + TextureSampler, + RootParameterCount + }; + + // Only one of these helpers is allocated per D3D device, even if there are multiple SpriteBatch instances. + struct DeviceResources + { + DeviceResources(_In_ ID3D12Device* device, ResourceUploadBatch& upload); + + ComPtr indexBuffer; + D3D12_INDEX_BUFFER_VIEW indexBufferView; + ComPtr rootSignatureStatic; + ComPtr rootSignatureHeap; + ID3D12Device* mDevice; + + private: + void CreateIndexBuffer(_In_ ID3D12Device* device, ResourceUploadBatch& upload); + void CreateRootSignatures(_In_ ID3D12Device* device); + + static std::vector CreateIndexValues(); + }; + + // Per-device data. + std::shared_ptr mDeviceResources; + static SharedResourcePool deviceResourcesPool; +}; + + +// Global pools of per-device and per-context SpriteBatch resources. +SharedResourcePool SpriteBatch::Impl::deviceResourcesPool; + + +// Constants. +const XMMATRIX SpriteBatch::MatrixIdentity = XMMatrixIdentity(); +const XMFLOAT2 SpriteBatch::Float2Zero(0, 0); + +const D3D12_SHADER_BYTECODE SpriteBatch::Impl::s_DefaultVertexShaderByteCodeStatic = { SpriteEffect_SpriteVertexShader, sizeof(SpriteEffect_SpriteVertexShader) }; +const D3D12_SHADER_BYTECODE SpriteBatch::Impl::s_DefaultPixelShaderByteCodeStatic = { SpriteEffect_SpritePixelShader, sizeof(SpriteEffect_SpritePixelShader) }; + +const D3D12_SHADER_BYTECODE SpriteBatch::Impl::s_DefaultVertexShaderByteCodeHeap = { SpriteEffect_SpriteVertexShaderHeap, sizeof(SpriteEffect_SpriteVertexShaderHeap) }; +const D3D12_SHADER_BYTECODE SpriteBatch::Impl::s_DefaultPixelShaderByteCodeHeap = { SpriteEffect_SpritePixelShaderHeap, sizeof(SpriteEffect_SpritePixelShaderHeap) }; + +const D3D12_INPUT_LAYOUT_DESC SpriteBatch::Impl::s_DefaultInputLayoutDesc = VertexPositionColorTexture::InputLayout; + +// Matches CommonStates::AlphaBlend +const D3D12_BLEND_DESC SpriteBatchPipelineStateDescription::s_DefaultBlendDesc = +{ + FALSE, // AlphaToCoverageEnable + FALSE, // IndependentBlendEnable + { { + TRUE, // BlendEnable + FALSE, // LogicOpEnable + D3D12_BLEND_ONE, // SrcBlend + D3D12_BLEND_INV_SRC_ALPHA, // DestBlend + D3D12_BLEND_OP_ADD, // BlendOp + D3D12_BLEND_ONE, // SrcBlendAlpha + D3D12_BLEND_INV_SRC_ALPHA, // DestBlendAlpha + D3D12_BLEND_OP_ADD, // BlendOpAlpha + D3D12_LOGIC_OP_NOOP, + D3D12_COLOR_WRITE_ENABLE_ALL + } } +}; + +// Same to CommonStates::CullCounterClockwise +const D3D12_RASTERIZER_DESC SpriteBatchPipelineStateDescription::s_DefaultRasterizerDesc = +{ + D3D12_FILL_MODE_SOLID, + D3D12_CULL_MODE_BACK, + FALSE, // FrontCounterClockwise + D3D12_DEFAULT_DEPTH_BIAS, + D3D12_DEFAULT_DEPTH_BIAS_CLAMP, + D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS, + TRUE, // DepthClipEnable + TRUE, // MultisampleEnable + FALSE, // AntialiasedLineEnable + 0, // ForcedSampleCount + D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF +}; + +// Same as CommonStates::DepthNone +const D3D12_DEPTH_STENCIL_DESC SpriteBatchPipelineStateDescription::s_DefaultDepthStencilDesc = +{ + FALSE, // DepthEnable + D3D12_DEPTH_WRITE_MASK_ZERO, + D3D12_COMPARISON_FUNC_LESS_EQUAL, // DepthFunc + FALSE, // StencilEnable + D3D12_DEFAULT_STENCIL_READ_MASK, + D3D12_DEFAULT_STENCIL_WRITE_MASK, + { + D3D12_STENCIL_OP_KEEP, // StencilFailOp + D3D12_STENCIL_OP_KEEP, // StencilDepthFailOp + D3D12_STENCIL_OP_KEEP, // StencilPassOp + D3D12_COMPARISON_FUNC_ALWAYS // StencilFunc + }, // FrontFace + { + D3D12_STENCIL_OP_KEEP, // StencilFailOp + D3D12_STENCIL_OP_KEEP, // StencilDepthFailOp + D3D12_STENCIL_OP_KEEP, // StencilPassOp + D3D12_COMPARISON_FUNC_ALWAYS // StencilFunc + } // BackFace +}; + +// Per-device constructor. +SpriteBatch::Impl::DeviceResources::DeviceResources(_In_ ID3D12Device* device, ResourceUploadBatch& upload) : + indexBufferView{}, + mDevice(device) +{ + CreateIndexBuffer(device, upload); + CreateRootSignatures(device); +} + +// Creates the SpriteBatch index buffer. +void SpriteBatch::Impl::DeviceResources::CreateIndexBuffer(_In_ ID3D12Device* device, ResourceUploadBatch& upload) +{ + static_assert((MaxBatchSize * VerticesPerSprite) < USHRT_MAX, "MaxBatchSize too large for 16-bit indices"); + + const CD3DX12_HEAP_PROPERTIES heapProps(D3D12_HEAP_TYPE_DEFAULT); + auto const bufferDesc = CD3DX12_RESOURCE_DESC::Buffer(sizeof(short) * MaxBatchSize * IndicesPerSprite); + + // Create the constant buffer. + ThrowIfFailed(device->CreateCommittedResource( + &heapProps, + D3D12_HEAP_FLAG_NONE, + &bufferDesc, + c_initialCopyTargetState, + nullptr, + IID_GRAPHICS_PPV_ARGS(indexBuffer.ReleaseAndGetAddressOf()))); + + SetDebugObjectName(indexBuffer.Get(), L"SpriteBatch"); + + auto indexValues = CreateIndexValues(); + + D3D12_SUBRESOURCE_DATA indexDataDesc = {}; + indexDataDesc.pData = indexValues.data(); + indexDataDesc.RowPitch = static_cast(bufferDesc.Width); + indexDataDesc.SlicePitch = indexDataDesc.RowPitch; + + // Upload the resource + upload.Upload(indexBuffer.Get(), 0, &indexDataDesc, 1); + upload.Transition(indexBuffer.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_INDEX_BUFFER); + SetDebugObjectName(indexBuffer.Get(), L"DirectXTK:SpriteBatch Index Buffer"); + + // Create the index buffer view + indexBufferView.BufferLocation = indexBuffer->GetGPUVirtualAddress(); + indexBufferView.Format = DXGI_FORMAT_R16_UINT; + indexBufferView.SizeInBytes = static_cast(bufferDesc.Width); +} + +void SpriteBatch::Impl::DeviceResources::CreateRootSignatures(_In_ ID3D12Device* device) +{ + ENUM_FLAGS_CONSTEXPR D3D12_ROOT_SIGNATURE_FLAGS rootSignatureFlags = + D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT + | D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS + | D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS + | D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS +#ifdef _GAMING_XBOX_SCARLETT + | D3D12_ROOT_SIGNATURE_FLAG_DENY_AMPLIFICATION_SHADER_ROOT_ACCESS + | D3D12_ROOT_SIGNATURE_FLAG_DENY_MESH_SHADER_ROOT_ACCESS +#endif + ; + + const CD3DX12_DESCRIPTOR_RANGE textureSRV(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0); + + { + // Same as CommonStates::StaticLinearClamp + const CD3DX12_STATIC_SAMPLER_DESC sampler( + 0, // register + D3D12_FILTER_MIN_MAG_MIP_LINEAR, + D3D12_TEXTURE_ADDRESS_MODE_CLAMP, + D3D12_TEXTURE_ADDRESS_MODE_CLAMP, + D3D12_TEXTURE_ADDRESS_MODE_CLAMP, + 0.f, + 16, + D3D12_COMPARISON_FUNC_LESS_EQUAL, + D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE, + 0.f, + D3D12_FLOAT32_MAX, + D3D12_SHADER_VISIBILITY_PIXEL); + + CD3DX12_ROOT_PARAMETER rootParameters[RootParameterIndex::RootParameterCount - 1] = {}; + rootParameters[RootParameterIndex::TextureSRV].InitAsDescriptorTable(1, &textureSRV, D3D12_SHADER_VISIBILITY_PIXEL); + rootParameters[RootParameterIndex::ConstantBuffer].InitAsConstantBufferView(0, 0, D3D12_SHADER_VISIBILITY_ALL); + + CD3DX12_ROOT_SIGNATURE_DESC rsigDesc; + rsigDesc.Init(static_cast(std::size(rootParameters)), rootParameters, 1, &sampler, rootSignatureFlags); + + ThrowIfFailed(::CreateRootSignature(device, &rsigDesc, rootSignatureStatic.ReleaseAndGetAddressOf())); + + SetDebugObjectName(rootSignatureStatic.Get(), L"SpriteBatch"); + } + + { + const CD3DX12_DESCRIPTOR_RANGE textureSampler(D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, 1, 0); + + CD3DX12_ROOT_PARAMETER rootParameters[RootParameterIndex::RootParameterCount] = {}; + rootParameters[RootParameterIndex::TextureSRV].InitAsDescriptorTable(1, &textureSRV, D3D12_SHADER_VISIBILITY_PIXEL); + rootParameters[RootParameterIndex::ConstantBuffer].InitAsConstantBufferView(0, 0, D3D12_SHADER_VISIBILITY_ALL); + rootParameters[RootParameterIndex::TextureSampler].InitAsDescriptorTable(1, &textureSampler, D3D12_SHADER_VISIBILITY_PIXEL); + + CD3DX12_ROOT_SIGNATURE_DESC rsigDesc; + rsigDesc.Init(static_cast(std::size(rootParameters)), rootParameters, 0, nullptr, rootSignatureFlags); + + ThrowIfFailed(::CreateRootSignature(device, &rsigDesc, rootSignatureHeap.ReleaseAndGetAddressOf())); + + SetDebugObjectName(rootSignatureHeap.Get(), L"SpriteBatch"); + } +} + +// Helper for populating the SpriteBatch index buffer. +std::vector SpriteBatch::Impl::DeviceResources::CreateIndexValues() +{ + std::vector indices; + + indices.reserve(MaxBatchSize * IndicesPerSprite); + + for (size_t j = 0; j < MaxBatchSize * VerticesPerSprite; j += VerticesPerSprite) + { + auto const i = static_cast(j); + + indices.push_back(i); + indices.push_back(i + 1); + indices.push_back(i + 2); + + indices.push_back(i + 1); + indices.push_back(i + 3); + indices.push_back(i + 2); + } + + return indices; +} + +// Per-SpriteBatch constructor. +_Use_decl_annotations_ +SpriteBatch::Impl::Impl(ID3D12Device* device, ResourceUploadBatch& upload, const SpriteBatchPipelineStateDescription& psoDesc, const D3D12_VIEWPORT* viewport) + : mRotation(DXGI_MODE_ROTATION_IDENTITY), + mSetViewport(false), + mViewPort{}, + mSampler{}, + mSpriteQueueCount(0), + mSpriteQueueArraySize(0), + mInBeginEndPair(false), + mSortMode(SpriteSortMode_Deferred), + mTransformMatrix(MatrixIdentity), + mVertexSegment{}, + mVertexPageSize(sizeof(VertexPositionColorTexture) * MaxBatchSize * VerticesPerSprite), + mSpriteCount(0), + mDeviceResources(deviceResourcesPool.DemandCreate(device, upload)) +{ + if (viewport != nullptr) + { + mViewPort = *viewport; + mSetViewport = true; + } + + D3D12_GRAPHICS_PIPELINE_STATE_DESC d3dDesc = {}; + d3dDesc.InputLayout = s_DefaultInputLayoutDesc; + d3dDesc.BlendState = psoDesc.blendDesc; + d3dDesc.DepthStencilState = psoDesc.depthStencilDesc; + d3dDesc.RasterizerState = psoDesc.rasterizerDesc; + d3dDesc.DSVFormat = psoDesc.renderTargetState.dsvFormat; + d3dDesc.NodeMask = psoDesc.renderTargetState.nodeMask; + d3dDesc.NumRenderTargets = psoDesc.renderTargetState.numRenderTargets; + memcpy(d3dDesc.RTVFormats, psoDesc.renderTargetState.rtvFormats, sizeof(DXGI_FORMAT) * D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT); + d3dDesc.SampleDesc = psoDesc.renderTargetState.sampleDesc; + d3dDesc.SampleMask = psoDesc.renderTargetState.sampleMask; + d3dDesc.IBStripCutValue = D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_DISABLED; + d3dDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; + + // Three choices: (1) static sampler, (2) heap sampler, or (3) custom signature & shaders + if (psoDesc.customRootSignature) + { + mRootSignature = psoDesc.customRootSignature; + } + else + { + mRootSignature = (psoDesc.samplerDescriptor.ptr) ? mDeviceResources->rootSignatureHeap.Get() : mDeviceResources->rootSignatureStatic.Get(); + } + d3dDesc.pRootSignature = mRootSignature.Get(); + + if (psoDesc.customVertexShader.pShaderBytecode) + { + d3dDesc.VS = psoDesc.customVertexShader; + } + else + { + d3dDesc.VS = (psoDesc.samplerDescriptor.ptr) ? s_DefaultVertexShaderByteCodeHeap : s_DefaultVertexShaderByteCodeStatic; + } + + if (psoDesc.customPixelShader.pShaderBytecode) + { + d3dDesc.PS = psoDesc.customPixelShader; + } + else + { + d3dDesc.PS = (psoDesc.samplerDescriptor.ptr) ? s_DefaultPixelShaderByteCodeHeap : s_DefaultPixelShaderByteCodeStatic; + } + + if (psoDesc.samplerDescriptor.ptr) + { + mSampler = psoDesc.samplerDescriptor; + } + + ThrowIfFailed(device->CreateGraphicsPipelineState( + &d3dDesc, + IID_GRAPHICS_PPV_ARGS(mPSO.GetAddressOf()))); + + SetDebugObjectName(mPSO.Get(), L"SpriteBatch"); +} + +// Begins a batch of sprite drawing operations. +_Use_decl_annotations_ +void XM_CALLCONV SpriteBatch::Impl::Begin( + ID3D12GraphicsCommandList* commandList, + SpriteSortMode sortMode, + FXMMATRIX transformMatrix) +{ + if (mInBeginEndPair) + { + DebugTrace("ERROR: Cannot nest Begin calls on a single SpriteBatch\n"); + throw std::logic_error("SpriteBatch::Begin"); + } + + mSortMode = sortMode; + mTransformMatrix = transformMatrix; + mCommandList = commandList; + mSpriteCount = 0; + + if (sortMode == SpriteSortMode_Immediate) + { + PrepareForRendering(); + } + + mInBeginEndPair = true; +} + +// Ends a batch of sprite drawing operations. +void SpriteBatch::Impl::End() +{ + if (!mInBeginEndPair) + { + DebugTrace("ERROR: Begin must be called before End\n"); + throw std::logic_error("SpriteBatch::End"); + } + + if (mSortMode != SpriteSortMode_Immediate) + { + PrepareForRendering(); + FlushBatch(); + } + + // Release this memory + mVertexSegment.Reset(); + + // Break circular reference chains, in case the state lambda closed + // over an object that holds a reference to this SpriteBatch. + mCommandList = nullptr; + mInBeginEndPair = false; +} + + +// Adds a single sprite to the queue. +_Use_decl_annotations_ +void XM_CALLCONV SpriteBatch::Impl::Draw(D3D12_GPU_DESCRIPTOR_HANDLE texture, + XMUINT2 const& textureSize, + FXMVECTOR destination, + RECT const* sourceRectangle, + FXMVECTOR color, + FXMVECTOR originRotationDepth, + unsigned int flags) +{ + if (!mInBeginEndPair) + { + DebugTrace("ERROR: Begin must be called before Draw\n"); + throw std::logic_error("SpriteBatch::Draw"); + } + + if (!texture.ptr) + throw std::invalid_argument("Invalid texture for Draw"); + + // Get a pointer to the output sprite. + if (mSpriteQueueCount >= mSpriteQueueArraySize) + { + GrowSpriteQueue(); + } + + SpriteInfo* sprite = &mSpriteQueue[mSpriteQueueCount]; + + XMVECTOR dest = destination; + + if (sourceRectangle) + { + // User specified an explicit source region. + const XMVECTOR source = LoadRect(sourceRectangle); + + XMStoreFloat4A(&sprite->source, source); + + // If the destination size is relative to the source region, convert it to pixels. + if (!(flags & SpriteInfo::DestSizeInPixels)) + { + dest = XMVectorPermute<0, 1, 6, 7>(dest, XMVectorMultiply(dest, source)); // dest.zw *= source.zw + } + + flags |= SpriteInfo::SourceInTexels | SpriteInfo::DestSizeInPixels; + } + else + { + // No explicit source region, so use the entire texture. + static const XMVECTORF32 wholeTexture = { { {0, 0, 1, 1} } }; + + XMStoreFloat4A(&sprite->source, wholeTexture); + } + + // Convert texture size + const XMVECTOR textureSizeV = XMLoadUInt2(&textureSize); + + // Store sprite parameters. + XMStoreFloat4A(&sprite->destination, dest); + XMStoreFloat4A(&sprite->color, color); + XMStoreFloat4A(&sprite->originRotationDepth, originRotationDepth); + + sprite->texture = texture; + sprite->textureSize = textureSizeV; + sprite->flags = flags; + + if (mSortMode == SpriteSortMode_Immediate) + { + // If we are in immediate mode, draw this sprite straight away. + RenderBatch(texture, textureSizeV, &sprite, 1); + } + else + { + // Queue this sprite for later sorting and batched rendering. + mSpriteQueueCount++; + } +} + + +// Dynamically expands the array used to store pending sprite information. +void SpriteBatch::Impl::GrowSpriteQueue() +{ + // Grow by a factor of 2. + const size_t newSize = std::max(InitialQueueSize, mSpriteQueueArraySize * 2); + + // Allocate the new array. + auto newArray = std::make_unique(newSize); + + // Copy over any existing sprites. + for (size_t i = 0; i < mSpriteQueueCount; i++) + { + newArray[i] = mSpriteQueue[i]; + } + + // Replace the previous array with the new one. + mSpriteQueue = std::move(newArray); + mSpriteQueueArraySize = newSize; + + // Clear any dangling SpriteInfo pointers left over from previous rendering. + mSortedSprites.clear(); +} + + +// Sets up D3D device state ready for drawing sprites. +void SpriteBatch::Impl::PrepareForRendering() +{ + auto commandList = mCommandList.Get(); + + // Set root signature + commandList->SetGraphicsRootSignature(mRootSignature.Get()); + + // Set render state + commandList->SetPipelineState(mPSO.Get()); + commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + + // Set the index buffer. + commandList->IASetIndexBuffer(&mDeviceResources->indexBufferView); + + // Set the transform matrix. + const XMMATRIX transformMatrix = (mRotation == DXGI_MODE_ROTATION_UNSPECIFIED) + ? mTransformMatrix + : (mTransformMatrix * GetViewportTransform(mRotation)); + + mConstantBuffer = GraphicsMemory::Get(mDeviceResources->mDevice).AllocateConstant(transformMatrix); + commandList->SetGraphicsRootConstantBufferView(RootParameterIndex::ConstantBuffer, mConstantBuffer.GpuAddress()); +} + + +// Sends queued sprites to the graphics device. +void SpriteBatch::Impl::FlushBatch() +{ + if (!mSpriteQueueCount) + return; + + SortSprites(); + + // Walk through the sorted sprite list, looking for adjacent entries that share a texture. + D3D12_GPU_DESCRIPTOR_HANDLE batchTexture = {}; + XMVECTOR batchTextureSize = {}; + size_t batchStart = 0; + + for (size_t pos = 0; pos < mSpriteQueueCount; pos++) + { + const D3D12_GPU_DESCRIPTOR_HANDLE texture = mSortedSprites[pos]->texture; + assert(texture.ptr != 0); + const XMVECTOR textureSize = mSortedSprites[pos]->textureSize; + + // Flush whenever the texture changes. + if (texture != batchTexture) + { + if (pos > batchStart) + { + RenderBatch(batchTexture, batchTextureSize, &mSortedSprites[batchStart], pos - batchStart); + } + + batchTexture = texture; + batchTextureSize = textureSize; + batchStart = pos; + } + } + + // Flush the final batch. + RenderBatch(batchTexture, batchTextureSize, &mSortedSprites[batchStart], mSpriteQueueCount - batchStart); + + // Reset the queue. + mSpriteQueueCount = 0; + + // When sorting is disabled, we persist mSortedSprites data from one batch to the next, to avoid + // uneccessary work in GrowSortedSprites. But we never reuse these when sorting, because re-sorting + // previously sorted items gives unstable ordering if some sprites have identical sort keys. + if (mSortMode != SpriteSortMode_Deferred) + { + mSortedSprites.clear(); + } +} + + +// Sorts the array of queued sprites. +void SpriteBatch::Impl::SortSprites() +{ + // Fill the mSortedSprites vector. + if (mSortedSprites.size() < mSpriteQueueCount) + { + GrowSortedSprites(); + } + + switch (mSortMode) + { + case SpriteSortMode_Texture: + // Sort by texture. + std::sort(mSortedSprites.begin(), + mSortedSprites.begin() + static_cast(mSpriteQueueCount), + [](SpriteInfo const* x, SpriteInfo const* y) noexcept -> bool + { + return x->texture < y->texture; + }); + break; + + case SpriteSortMode_BackToFront: + // Sort back to front. + std::sort(mSortedSprites.begin(), + mSortedSprites.begin() + static_cast(mSpriteQueueCount), + [](SpriteInfo const* x, SpriteInfo const* y) noexcept -> bool + { + return x->originRotationDepth.w > y->originRotationDepth.w; + }); + break; + + case SpriteSortMode_FrontToBack: + // Sort front to back. + std::sort(mSortedSprites.begin(), + mSortedSprites.begin() + static_cast(mSpriteQueueCount), + [](SpriteInfo const* x, SpriteInfo const* y) noexcept -> bool + { + return x->originRotationDepth.w < y->originRotationDepth.w; + }); + break; + + default: + break; + } +} + + +// Populates the mSortedSprites vector with pointers to individual elements of the mSpriteQueue array. +void SpriteBatch::Impl::GrowSortedSprites() +{ + const size_t previousSize = mSortedSprites.size(); + + mSortedSprites.resize(mSpriteQueueCount); + + for (size_t i = previousSize; i < mSpriteQueueCount; i++) + { + mSortedSprites[i] = &mSpriteQueue[i]; + } +} + + +// Submits a batch of sprites to the GPU. +_Use_decl_annotations_ +void SpriteBatch::Impl::RenderBatch(D3D12_GPU_DESCRIPTOR_HANDLE texture, XMVECTOR textureSize, SpriteInfo const* const* sprites, size_t count) +{ + auto commandList = mCommandList.Get(); + + // Draw using the specified texture. + // **NOTE** If D3D asserts or crashes here, you probably need to call commandList->SetDescriptorHeaps() with the required descriptor heap(s) + commandList->SetGraphicsRootDescriptorTable(RootParameterIndex::TextureSRV, texture); + + if (mSampler.ptr) + { + commandList->SetGraphicsRootDescriptorTable(RootParameterIndex::TextureSampler, mSampler); + } + + // Convert to vector format. + const XMVECTOR inverseTextureSize = XMVectorReciprocal(textureSize); + + while (count > 0) + { + // How many sprites do we want to draw? + size_t batchSize = count; + + // How many sprites does the D3D vertex buffer have room for? + const size_t remainingSpace = MaxBatchSize - mSpriteCount; + + if (batchSize > remainingSpace) + { + if (remainingSpace < MinBatchSize) + { + // If we are out of room, or about to submit an excessively small batch, wrap back to the start of the vertex buffer. + mSpriteCount = 0; + + batchSize = std::min(count, MaxBatchSize); + } + else + { + // Take however many sprites fit in what's left of the vertex buffer. + batchSize = remainingSpace; + } + } + + // Allocate a new page of vertex memory if we're starting the batch + if (mSpriteCount == 0) + { + mVertexSegment = GraphicsMemory::Get(mDeviceResources->mDevice).Allocate(mVertexPageSize, 16, GraphicsMemory::TAG_SPRITES); + } + + auto vertices = static_cast(mVertexSegment.Memory()) + mSpriteCount * VerticesPerSprite; + + // Generate sprite vertex data. + for (size_t i = 0; i < batchSize; i++) + { + assert(i < count); + _Analysis_assume_(i < count); + RenderSprite(sprites[i], vertices, textureSize, inverseTextureSize); + + vertices += VerticesPerSprite; + } + + // Set the vertex buffer view + D3D12_VERTEX_BUFFER_VIEW vbv; + constexpr size_t spriteVertexTotalSize = sizeof(VertexPositionColorTexture) * VerticesPerSprite; + vbv.BufferLocation = mVertexSegment.GpuAddress() + (UINT64(mSpriteCount) * UINT64(spriteVertexTotalSize)); + vbv.StrideInBytes = sizeof(VertexPositionColorTexture); + vbv.SizeInBytes = static_cast(batchSize * spriteVertexTotalSize); + commandList->IASetVertexBuffers(0, 1, &vbv); + + // Ok lads, the time has come for us draw ourselves some sprites! + const UINT indexCount = static_cast(batchSize * IndicesPerSprite); + + commandList->DrawIndexedInstanced(indexCount, 1, 0, 0, 0); + + // Advance the buffer position. + mSpriteCount += batchSize; + + sprites += batchSize; + count -= batchSize; + } +} + + +// Generates vertex data for drawing a single sprite. +_Use_decl_annotations_ +void XM_CALLCONV SpriteBatch::Impl::RenderSprite(SpriteInfo const* sprite, VertexPositionColorTexture* vertices, FXMVECTOR textureSize, FXMVECTOR inverseTextureSize) noexcept +{ + // Load sprite parameters into SIMD registers. + XMVECTOR source = XMLoadFloat4A(&sprite->source); + const XMVECTOR destination = XMLoadFloat4A(&sprite->destination); + const XMVECTOR color = XMLoadFloat4A(&sprite->color); + const XMVECTOR originRotationDepth = XMLoadFloat4A(&sprite->originRotationDepth); + + const float rotation = sprite->originRotationDepth.z; + const unsigned int flags = sprite->flags; + + // Extract the source and destination sizes into separate vectors. + XMVECTOR sourceSize = XMVectorSwizzle<2, 3, 2, 3>(source); + XMVECTOR destinationSize = XMVectorSwizzle<2, 3, 2, 3>(destination); + + // Scale the origin offset by source size, taking care to avoid overflow if the source region is zero. + const XMVECTOR isZeroMask = XMVectorEqual(sourceSize, XMVectorZero()); + const XMVECTOR nonZeroSourceSize = XMVectorSelect(sourceSize, g_XMEpsilon, isZeroMask); + + XMVECTOR origin = XMVectorDivide(originRotationDepth, nonZeroSourceSize); + + // Convert the source region from texels to mod-1 texture coordinate format. + if (flags & SpriteInfo::SourceInTexels) + { + source = XMVectorMultiply(source, inverseTextureSize); + sourceSize = XMVectorMultiply(sourceSize, inverseTextureSize); + } + else + { + origin = XMVectorMultiply(origin, inverseTextureSize); + } + + // If the destination size is relative to the source region, convert it to pixels. + if (!(flags & SpriteInfo::DestSizeInPixels)) + { + destinationSize = XMVectorMultiply(destinationSize, textureSize); + } + + // Compute a 2x2 rotation matrix. + XMVECTOR rotationMatrix1; + XMVECTOR rotationMatrix2; + + if (rotation != 0) + { + float sin, cos; + + XMScalarSinCos(&sin, &cos, rotation); + + const XMVECTOR sinV = XMLoadFloat(&sin); + const XMVECTOR cosV = XMLoadFloat(&cos); + + rotationMatrix1 = XMVectorMergeXY(cosV, sinV); + rotationMatrix2 = XMVectorMergeXY(XMVectorNegate(sinV), cosV); + } + else + { + rotationMatrix1 = g_XMIdentityR0; + rotationMatrix2 = g_XMIdentityR1; + } + + // The four corner vertices are computed by transforming these unit-square positions. + static XMVECTORF32 cornerOffsets[VerticesPerSprite] = + { + { { { 0, 0, 0, 0 } } }, + { { { 1, 0, 0, 0 } } }, + { { { 0, 1, 0, 0 } } }, + { { { 1, 1, 0, 0 } } }, + }; + + // Tricksy alert! Texture coordinates are computed from the same cornerOffsets + // table as vertex positions, but if the sprite is mirrored, this table + // must be indexed in a different order. This is done as follows: + // + // position = cornerOffsets[i] + // texcoord = cornerOffsets[i ^ SpriteEffects] + + static_assert(SpriteEffects_FlipHorizontally == 1 && + SpriteEffects_FlipVertically == 2, "If you change these enum values, the mirroring implementation must be updated to match"); + + const unsigned int mirrorBits = flags & 3u; + + // Generate the four output vertices. + for (size_t i = 0; i < VerticesPerSprite; i++) + { + // Calculate position. + const XMVECTOR cornerOffset = XMVectorMultiply(XMVectorSubtract(cornerOffsets[i], origin), destinationSize); + + // Apply 2x2 rotation matrix. + const XMVECTOR position1 = XMVectorMultiplyAdd(XMVectorSplatX(cornerOffset), rotationMatrix1, destination); + const XMVECTOR position2 = XMVectorMultiplyAdd(XMVectorSplatY(cornerOffset), rotationMatrix2, position1); + + // Set z = depth. + const XMVECTOR position = XMVectorPermute<0, 1, 7, 6>(position2, originRotationDepth); + + // Write position as a Float4, even though VertexPositionColor::position is an XMFLOAT3. + // This is faster, and harmless as we are just clobbering the first element of the + // following color field, which will immediately be overwritten with its correct value. + XMStoreFloat4(reinterpret_cast(&vertices[i].position), position); + + // Write the color. + XMStoreFloat4(&vertices[i].color, color); + + // Compute and write the texture coordinate. + const XMVECTOR textureCoordinate = XMVectorMultiplyAdd(cornerOffsets[static_cast(i) ^ mirrorBits], sourceSize, source); + + XMStoreFloat2(&vertices[i].textureCoordinate, textureCoordinate); + } +} + + +// Generates a viewport transform matrix for rendering sprites using x-right y-down screen pixel coordinates. +XMMATRIX SpriteBatch::Impl::GetViewportTransform(_In_ DXGI_MODE_ROTATION rotation) +{ + if (!mSetViewport) + { + DebugTrace("ERROR: SpriteBatch requires viewport information via SetViewport\n"); + throw std::runtime_error("Viewport not set."); + } + + // Compute the matrix. + const float xScale = (mViewPort.Width > 0) ? 2.0f / mViewPort.Width : 0.0f; + const float yScale = (mViewPort.Height > 0) ? 2.0f / mViewPort.Height : 0.0f; + + switch (rotation) + { + case DXGI_MODE_ROTATION_ROTATE90: + return XMMATRIX + ( + 0, -yScale, 0, 0, + -xScale, 0, 0, 0, + 0, 0, 1, 0, + 1, 1, 0, 1 + ); + + case DXGI_MODE_ROTATION_ROTATE270: + return XMMATRIX + ( + 0, yScale, 0, 0, + xScale, 0, 0, 0, + 0, 0, 1, 0, + -1, -1, 0, 1 + ); + + case DXGI_MODE_ROTATION_ROTATE180: + return XMMATRIX + ( + -xScale, 0, 0, 0, + 0, yScale, 0, 0, + 0, 0, 1, 0, + 1, -1, 0, 1 + ); + + default: + return XMMATRIX + ( + xScale, 0, 0, 0, + 0, -yScale, 0, 0, + 0, 0, 1, 0, + -1, 1, 0, 1 + ); + } +} + + +// Public constructor. +_Use_decl_annotations_ +SpriteBatch::SpriteBatch(ID3D12Device* device, + ResourceUploadBatch& upload, + const SpriteBatchPipelineStateDescription& psoDesc, + const D3D12_VIEWPORT* viewport) + : pImpl(std::make_unique(device, upload, psoDesc, viewport)) +{ +} + + +SpriteBatch::SpriteBatch(SpriteBatch&&) noexcept = default; +SpriteBatch& SpriteBatch::operator= (SpriteBatch&&) noexcept = default; +SpriteBatch::~SpriteBatch() = default; + + +_Use_decl_annotations_ +void XM_CALLCONV SpriteBatch::Begin( + ID3D12GraphicsCommandList* commandList, + SpriteSortMode sortMode, + FXMMATRIX transformMatrix) +{ + pImpl->Begin(commandList, sortMode, transformMatrix); +} + + +_Use_decl_annotations_ +void XM_CALLCONV SpriteBatch::Begin( + ID3D12GraphicsCommandList* commandList, + D3D12_GPU_DESCRIPTOR_HANDLE sampler, + SpriteSortMode sortMode, + FXMMATRIX transformMatrix) +{ + if (!sampler.ptr) + throw std::invalid_argument("Invalid heap-based sampler for Begin"); + + if (!pImpl->mSampler.ptr) + { + DebugTrace("ERROR: sampler version of Begin requires SpriteBatch was created with a heap-based sampler\n"); + throw std::runtime_error("SpriteBatch::Begin"); + } + + pImpl->mSampler = sampler; + + pImpl->Begin(commandList, sortMode, transformMatrix); +} + + +void SpriteBatch::End() +{ + pImpl->End(); +} + + +void XM_CALLCONV SpriteBatch::Draw(D3D12_GPU_DESCRIPTOR_HANDLE texture, + XMUINT2 const& textureSize, + XMFLOAT2 const& position, + FXMVECTOR color) +{ + const XMVECTOR destination = XMVectorPermute<0, 1, 4, 5>(XMLoadFloat2(&position), g_XMOne); // x, y, 1, 1 + + pImpl->Draw(texture, textureSize, destination, nullptr, color, g_XMZero, 0); +} + + +_Use_decl_annotations_ +void XM_CALLCONV SpriteBatch::Draw(D3D12_GPU_DESCRIPTOR_HANDLE texture, + XMUINT2 const& textureSize, + XMFLOAT2 const& position, + RECT const* sourceRectangle, + FXMVECTOR color, + float rotation, + XMFLOAT2 const& origin, + float scale, + SpriteEffects effects, + float layerDepth) +{ + const XMVECTOR destination = XMVectorPermute<0, 1, 4, 4>(XMLoadFloat2(&position), XMLoadFloat(&scale)); // x, y, scale, scale + + const XMVECTOR originRotationDepth = XMVectorSet(origin.x, origin.y, rotation, layerDepth); + + pImpl->Draw(texture, textureSize, destination, sourceRectangle, color, originRotationDepth, static_cast(effects)); +} + + +_Use_decl_annotations_ +void XM_CALLCONV SpriteBatch::Draw(D3D12_GPU_DESCRIPTOR_HANDLE texture, + XMUINT2 const& textureSize, + XMFLOAT2 const& position, + RECT const* sourceRectangle, + FXMVECTOR color, + float rotation, + XMFLOAT2 const& origin, + XMFLOAT2 const& scale, + SpriteEffects effects, + float layerDepth) +{ + const XMVECTOR destination = XMVectorPermute<0, 1, 4, 5>(XMLoadFloat2(&position), XMLoadFloat2(&scale)); // x, y, scale.x, scale.y + + const XMVECTOR originRotationDepth = XMVectorSet(origin.x, origin.y, rotation, layerDepth); + + pImpl->Draw(texture, textureSize, destination, sourceRectangle, color, originRotationDepth, static_cast(effects)); +} + + +void XM_CALLCONV SpriteBatch::Draw(D3D12_GPU_DESCRIPTOR_HANDLE texture, XMUINT2 const& textureSize, FXMVECTOR position, FXMVECTOR color) +{ + const XMVECTOR destination = XMVectorPermute<0, 1, 4, 5>(position, g_XMOne); // x, y, 1, 1 + + pImpl->Draw(texture, textureSize, destination, nullptr, color, g_XMZero, 0); +} + + +_Use_decl_annotations_ +void XM_CALLCONV SpriteBatch::Draw(D3D12_GPU_DESCRIPTOR_HANDLE texture, + XMUINT2 const& textureSize, + FXMVECTOR position, + RECT const* sourceRectangle, + FXMVECTOR color, + float rotation, + FXMVECTOR origin, + float scale, + SpriteEffects effects, + float layerDepth) +{ + const XMVECTOR destination = XMVectorPermute<0, 1, 4, 4>(position, XMLoadFloat(&scale)); // x, y, scale, scale + + const XMVECTOR rotationDepth = XMVectorMergeXY( + XMVectorReplicate(rotation), + XMVectorReplicate(layerDepth)); + + const XMVECTOR originRotationDepth = XMVectorPermute<0, 1, 4, 5>(origin, rotationDepth); + + pImpl->Draw(texture, textureSize, destination, sourceRectangle, color, originRotationDepth, static_cast(effects)); +} + + +_Use_decl_annotations_ +void XM_CALLCONV SpriteBatch::Draw(D3D12_GPU_DESCRIPTOR_HANDLE texture, + XMUINT2 const& textureSize, + FXMVECTOR position, + RECT const* sourceRectangle, + FXMVECTOR color, + float rotation, + FXMVECTOR origin, + GXMVECTOR scale, + SpriteEffects effects, + float layerDepth) +{ + const XMVECTOR destination = XMVectorPermute<0, 1, 4, 5>(position, scale); // x, y, scale.x, scale.y + + const XMVECTOR rotationDepth = XMVectorMergeXY( + XMVectorReplicate(rotation), + XMVectorReplicate(layerDepth)); + + const XMVECTOR originRotationDepth = XMVectorPermute<0, 1, 4, 5>(origin, rotationDepth); + + pImpl->Draw(texture, textureSize, destination, sourceRectangle, color, originRotationDepth, static_cast(effects)); +} + + +void XM_CALLCONV SpriteBatch::Draw(D3D12_GPU_DESCRIPTOR_HANDLE texture, + XMUINT2 const& textureSize, + RECT const& destinationRectangle, + FXMVECTOR color) +{ + const XMVECTOR destination = LoadRect(&destinationRectangle); // x, y, w, h + + pImpl->Draw(texture, textureSize, destination, nullptr, color, g_XMZero, Impl::SpriteInfo::DestSizeInPixels); +} + + +_Use_decl_annotations_ +void XM_CALLCONV SpriteBatch::Draw(D3D12_GPU_DESCRIPTOR_HANDLE texture, + XMUINT2 const& textureSize, + RECT const& destinationRectangle, + RECT const* sourceRectangle, + FXMVECTOR color, + float rotation, + XMFLOAT2 const& origin, + SpriteEffects effects, + float layerDepth) +{ + const XMVECTOR destination = LoadRect(&destinationRectangle); // x, y, w, h + + const XMVECTOR originRotationDepth = XMVectorSet(origin.x, origin.y, rotation, layerDepth); + + pImpl->Draw(texture, textureSize, destination, sourceRectangle, color, originRotationDepth, static_cast(effects) | Impl::SpriteInfo::DestSizeInPixels); +} + + +void SpriteBatch::SetRotation(DXGI_MODE_ROTATION mode) +{ + pImpl->mRotation = mode; +} + + +DXGI_MODE_ROTATION SpriteBatch::GetRotation() const noexcept +{ + return pImpl->mRotation; +} + + +void SpriteBatch::SetViewport(const D3D12_VIEWPORT& viewPort) +{ + pImpl->mSetViewport = true; + pImpl->mViewPort = viewPort; +} diff --git a/Common/DirectXTK12/Src/SpriteFont.cpp b/Common/DirectXTK12/Src/SpriteFont.cpp new file mode 100644 index 0000000..bd24374 --- /dev/null +++ b/Common/DirectXTK12/Src/SpriteFont.cpp @@ -0,0 +1,753 @@ +//-------------------------------------------------------------------------------------- +// File: SpriteFont.cpp +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "pch.h" + +#include +#include + +#include "SpriteFont.h" +#include "DirectXHelpers.h" +#include "BinaryReader.h" +#include "LoaderHelpers.h" +#include "ResourceUploadBatch.h" +#include "DescriptorHeap.h" + +using namespace DirectX; +using Microsoft::WRL::ComPtr; + + +// Internal SpriteFont implementation class. +class SpriteFont::Impl +{ +public: + Impl(_In_ ID3D12Device* device, + ResourceUploadBatch& upload, + _In_ BinaryReader* reader, + D3D12_CPU_DESCRIPTOR_HANDLE cpuDesc, + D3D12_GPU_DESCRIPTOR_HANDLE gpuDesc, + bool forceSRGB) noexcept(false); + Impl(D3D12_GPU_DESCRIPTOR_HANDLE texture, + XMUINT2 textureSize, + _In_reads_(glyphCount) Glyph const* glyphs, + size_t glyphCount, + float lineSpacing) noexcept(false); + + Glyph const* FindGlyph(wchar_t character) const; + + void SetDefaultCharacter(wchar_t character); + + template + void ForEachGlyph(_In_z_ wchar_t const* text, TAction action, bool ignoreWhitespace) const; + + void CreateTextureResource(_In_ ID3D12Device* device, + ResourceUploadBatch& upload, + uint32_t width, uint32_t height, + DXGI_FORMAT format, + uint32_t stride, uint32_t rows, + _In_reads_(stride * rows) const uint8_t* data) noexcept(false); + + const wchar_t* ConvertUTF8(_In_z_ const char *text) noexcept(false); + + // Fields. + ComPtr textureResource; + D3D12_GPU_DESCRIPTOR_HANDLE texture; + XMUINT2 textureSize; + std::vector glyphs; + std::vector glyphsIndex; + Glyph const* defaultGlyph; + float lineSpacing; + +private: + size_t utfBufferSize; + std::unique_ptr utfBuffer; +}; + + +// Constants. +const XMFLOAT2 SpriteFont::Float2Zero(0, 0); + +static const char spriteFontMagic[] = "DXTKfont"; + + +// Comparison operators make our sorted glyph vector work with std::binary_search and lower_bound. +namespace DirectX +{ + static inline bool operator< (SpriteFont::Glyph const& left, SpriteFont::Glyph const& right) noexcept + { + return left.Character < right.Character; + } + + static inline bool operator< (wchar_t left, SpriteFont::Glyph const& right) noexcept + { + return left < right.Character; + } + + static inline bool operator< (SpriteFont::Glyph const& left, wchar_t right) noexcept + { + return left.Character < right; + } +} + + +// Reads a SpriteFont from the binary format created by the MakeSpriteFont utility. +_Use_decl_annotations_ +SpriteFont::Impl::Impl( + ID3D12Device* device, + ResourceUploadBatch& upload, + BinaryReader* reader, + D3D12_CPU_DESCRIPTOR_HANDLE cpuDesc, + D3D12_GPU_DESCRIPTOR_HANDLE gpuDesc, + bool forceSRGB) noexcept(false) : + texture{}, + textureSize{}, + defaultGlyph(nullptr), + lineSpacing(0), + utfBufferSize(0) +{ + // Validate the header. + for (char const* magic = spriteFontMagic; *magic; magic++) + { + if (reader->Read() != *magic) + { + DebugTrace("ERROR: SpriteFont provided with an invalid .spritefont file\n"); + throw std::runtime_error("Not a MakeSpriteFont output binary"); + } + } + + // Read the glyph data. + auto glyphCount = reader->Read(); + auto glyphData = reader->ReadArray(glyphCount); + + glyphs.assign(glyphData, glyphData + glyphCount); + glyphsIndex.reserve(glyphs.size()); + + for (auto& glyph : glyphs) + { + glyphsIndex.emplace_back(glyph.Character); + } + + // Read font properties. + lineSpacing = reader->Read(); + + SetDefaultCharacter(static_cast(reader->Read())); + + // Read the texture data. + auto textureWidth = reader->Read(); + auto textureHeight = reader->Read(); + auto textureFormat = reader->Read(); + auto textureStride = reader->Read(); + auto textureRows = reader->Read(); + + const uint64_t dataSize = uint64_t(textureStride) * uint64_t(textureRows); + if (dataSize > UINT32_MAX) + { + DebugTrace("ERROR: SpriteFont provided with an invalid .spritefont file\n"); + throw std::overflow_error("Invalid .spritefont file"); + } + + auto textureData = reader->ReadArray(static_cast(dataSize)); + + if (forceSRGB) + { + textureFormat = LoaderHelpers::MakeSRGB(textureFormat); + } + + // Create the D3D texture. + CreateTextureResource( + device, + upload, + textureWidth, textureHeight, + textureFormat, + textureStride, textureRows, + textureData); + + // Create the shader resource view + CreateShaderResourceView( + device, textureResource.Get(), + cpuDesc, false); + + // Save off the GPU descriptor pointer and size. + texture = gpuDesc; + textureSize = XMUINT2(textureWidth, textureHeight); +} + + +// Constructs a SpriteFont from arbitrary user specified glyph data. +_Use_decl_annotations_ +SpriteFont::Impl::Impl( + D3D12_GPU_DESCRIPTOR_HANDLE itexture, + XMUINT2 itextureSize, + Glyph const* iglyphs, + size_t glyphCount, + float ilineSpacing) noexcept(false) : + texture(itexture), + textureSize(itextureSize), + glyphs(iglyphs, iglyphs + glyphCount), + defaultGlyph(nullptr), + lineSpacing(ilineSpacing), + utfBufferSize(0) +{ + if (!std::is_sorted(iglyphs, iglyphs + glyphCount)) + { + throw std::runtime_error("Glyphs must be in ascending codepoint order"); + } + + glyphsIndex.reserve(glyphs.size()); + + for (auto& glyph : glyphs) + { + glyphsIndex.emplace_back(glyph.Character); + } +} + + +// Looks up the requested glyph, falling back to the default character if it is not in the font. +SpriteFont::Glyph const* SpriteFont::Impl::FindGlyph(wchar_t character) const +{ + // Rather than use std::lower_bound (which includes a slow debug path when built for _DEBUG), + // we implement a binary search inline to ensure sufficient Debug build performance to be useful + // for text-heavy applications. + + size_t lower = 0; + size_t higher = glyphs.size() - 1; + size_t index = higher / 2; + const size_t size = glyphs.size(); + + while (index < size) + { + const auto curChar = glyphsIndex[index]; + if (curChar == character) { return &glyphs[index]; } + if (curChar < character) + { + lower = index + 1; + } + else + { + higher = index - 1; + } + if (higher < lower) { break; } + else if (higher - lower <= 4) + { + for (index = lower; index <= higher; index++) + { + if (glyphsIndex[index] == character) + { + return &glyphs[index]; + } + } + } + index = lower + ((higher - lower) / 2); + } + + if (defaultGlyph) + { + return defaultGlyph; + } + + DebugTrace("ERROR: SpriteFont encountered a character not in the font (%u, %C), and no default glyph was provided\n", character, character); + throw std::runtime_error("Character not in font"); +} + + +// Sets the missing-character fallback glyph. +void SpriteFont::Impl::SetDefaultCharacter(wchar_t character) +{ + defaultGlyph = nullptr; + + if (character) + { + defaultGlyph = FindGlyph(character); + } +} + + +// The core glyph layout algorithm, shared between DrawString and MeasureString. +template +void SpriteFont::Impl::ForEachGlyph(_In_z_ wchar_t const* text, TAction action, bool ignoreWhitespace) const +{ + float x = 0; + float y = 0; + + for (; *text; text++) + { + const wchar_t character = *text; + + switch (character) + { + case '\r': + // Skip carriage returns. + continue; + + case '\n': + // New line. + x = 0; + y += lineSpacing; + break; + + default: + // Output this character. + auto glyph = FindGlyph(character); + + x += glyph->XOffset; + + if (x < 0) + x = 0; + + const float advance = float(glyph->Subrect.right) - float(glyph->Subrect.left) + glyph->XAdvance; + + if (!ignoreWhitespace + || !iswspace(character) + || ((glyph->Subrect.right - glyph->Subrect.left) > 1) + || ((glyph->Subrect.bottom - glyph->Subrect.top) > 1)) + { + action(glyph, x, y, advance); + } + + x += advance; + break; + } + } +} + + +_Use_decl_annotations_ +void SpriteFont::Impl::CreateTextureResource( + ID3D12Device* device, + ResourceUploadBatch& upload, + uint32_t width, uint32_t height, + DXGI_FORMAT format, + uint32_t stride, uint32_t rows, + const uint8_t* data) noexcept(false) +{ + const uint64_t sliceBytes = uint64_t(stride) * uint64_t(rows); + if (sliceBytes > UINT32_MAX) + { + DebugTrace("ERROR: SpriteFont provided with an invalid .spritefont file\n"); + throw std::overflow_error("Invalid .spritefont file"); + } + + D3D12_RESOURCE_DESC desc = {}; + desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; + desc.Width = width; + desc.Height = height; + desc.DepthOrArraySize = 1; + desc.MipLevels = 1; + desc.Format = format; + desc.SampleDesc.Count = 1; + + const CD3DX12_HEAP_PROPERTIES defaultHeapProperties(D3D12_HEAP_TYPE_DEFAULT); + + ThrowIfFailed(device->CreateCommittedResource( + &defaultHeapProperties, + D3D12_HEAP_FLAG_NONE, + &desc, + c_initialCopyTargetState, + nullptr, + IID_GRAPHICS_PPV_ARGS(textureResource.ReleaseAndGetAddressOf()))); + + SetDebugObjectName(textureResource.Get(), L"SpriteFont:Texture"); + + D3D12_SUBRESOURCE_DATA initData = { data, static_cast(stride), static_cast(sliceBytes) }; + + upload.Upload( + textureResource.Get(), + 0, + &initData, + 1); + + upload.Transition( + textureResource.Get(), + D3D12_RESOURCE_STATE_COPY_DEST, + D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); +} + + +const wchar_t* SpriteFont::Impl::ConvertUTF8(_In_z_ const char *text) noexcept(false) +{ + if (!utfBuffer) + { + utfBufferSize = 1024; + utfBuffer.reset(new wchar_t[1024]); + } + + int result = MultiByteToWideChar(CP_UTF8, 0, text, -1, utfBuffer.get(), static_cast(utfBufferSize)); + if (!result && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) + { + // Compute required buffer size + result = MultiByteToWideChar(CP_UTF8, 0, text, -1, nullptr, 0); + utfBufferSize = AlignUp(static_cast(result), 1024u); + utfBuffer.reset(new wchar_t[utfBufferSize]); + + // Retry conversion + result = MultiByteToWideChar(CP_UTF8, 0, text, -1, utfBuffer.get(), static_cast(utfBufferSize)); + } + + if (!result) + { + DebugTrace("ERROR: MultiByteToWideChar failed with error %u.\n", GetLastError()); + throw std::system_error(std::error_code(static_cast(GetLastError()), std::system_category()), "MultiByteToWideChar"); + } + + return utfBuffer.get(); +} + + +// Construct from a binary file created by the MakeSpriteFont utility. +_Use_decl_annotations_ +SpriteFont::SpriteFont(ID3D12Device* device, ResourceUploadBatch& upload, wchar_t const* fileName, D3D12_CPU_DESCRIPTOR_HANDLE cpuDescriptorDest, D3D12_GPU_DESCRIPTOR_HANDLE gpuDescriptorDest, bool forceSRGB) +{ + BinaryReader reader(fileName); + + pImpl = std::make_unique(device, upload, &reader, cpuDescriptorDest, gpuDescriptorDest, forceSRGB); +} + + +// Construct from a binary blob created by the MakeSpriteFont utility and already loaded into memory. +_Use_decl_annotations_ +SpriteFont::SpriteFont(ID3D12Device* device, ResourceUploadBatch& upload, uint8_t const* dataBlob, size_t dataSize, D3D12_CPU_DESCRIPTOR_HANDLE cpuDescriptorDest, D3D12_GPU_DESCRIPTOR_HANDLE gpuDescriptorDest, bool forceSRGB) +{ + BinaryReader reader(dataBlob, dataSize); + + pImpl = std::make_unique(device, upload, &reader, cpuDescriptorDest, gpuDescriptorDest, forceSRGB); +} + + +// Construct from arbitrary user specified glyph data (for those not using the MakeSpriteFont utility). +_Use_decl_annotations_ +SpriteFont::SpriteFont(D3D12_GPU_DESCRIPTOR_HANDLE texture, XMUINT2 textureSize, Glyph const* glyphs, size_t glyphCount, float lineSpacing) + : pImpl(std::make_unique(texture, textureSize, glyphs, glyphCount, lineSpacing)) +{ +} + + +SpriteFont::SpriteFont(SpriteFont&&) noexcept = default; +SpriteFont& SpriteFont::operator= (SpriteFont&&) noexcept = default; +SpriteFont::~SpriteFont() = default; + +// Wide-character / UTF-16LE +void XM_CALLCONV SpriteFont::DrawString(_In_ SpriteBatch* spriteBatch, _In_z_ wchar_t const* text, XMFLOAT2 const& position, FXMVECTOR color, float rotation, XMFLOAT2 const& origin, float scale, SpriteEffects effects, float layerDepth) const +{ + DrawString(spriteBatch, text, XMLoadFloat2(&position), color, rotation, XMLoadFloat2(&origin), XMVectorReplicate(scale), effects, layerDepth); +} + + +void XM_CALLCONV SpriteFont::DrawString(_In_ SpriteBatch* spriteBatch, _In_z_ wchar_t const* text, XMFLOAT2 const& position, FXMVECTOR color, float rotation, XMFLOAT2 const& origin, XMFLOAT2 const& scale, SpriteEffects effects, float layerDepth) const +{ + DrawString(spriteBatch, text, XMLoadFloat2(&position), color, rotation, XMLoadFloat2(&origin), XMLoadFloat2(&scale), effects, layerDepth); +} + + +void XM_CALLCONV SpriteFont::DrawString(_In_ SpriteBatch* spriteBatch, _In_z_ wchar_t const* text, FXMVECTOR position, FXMVECTOR color, float rotation, FXMVECTOR origin, float scale, SpriteEffects effects, float layerDepth) const +{ + DrawString(spriteBatch, text, position, color, rotation, origin, XMVectorReplicate(scale), effects, layerDepth); +} + + +void XM_CALLCONV SpriteFont::DrawString(_In_ SpriteBatch* spriteBatch, _In_z_ wchar_t const* text, FXMVECTOR position, FXMVECTOR color, float rotation, FXMVECTOR origin, GXMVECTOR scale, SpriteEffects effects, float layerDepth) const +{ + static_assert(SpriteEffects_FlipHorizontally == 1 && + SpriteEffects_FlipVertically == 2, "If you change these enum values, the following tables must be updated to match"); + +// Lookup table indicates which way to move along each axis per SpriteEffects enum value. + static XMVECTORF32 axisDirectionTable[4] = + { + { { { -1, -1, 0, 0 } } }, + { { { 1, -1, 0, 0 } } }, + { { { -1, 1, 0, 0 } } }, + { { { 1, 1, 0, 0 } } }, + }; + + // Lookup table indicates which axes are mirrored for each SpriteEffects enum value. + static XMVECTORF32 axisIsMirroredTable[4] = + { + { { { 0, 0, 0, 0 } } }, + { { { 1, 0, 0, 0 } } }, + { { { 0, 1, 0, 0 } } }, + { { { 1, 1, 0, 0 } } }, + }; + + XMVECTOR baseOffset = origin; + + // If the text is mirrored, offset the start position accordingly. + if (effects) + { + baseOffset = XMVectorNegativeMultiplySubtract( + MeasureString(text), + axisIsMirroredTable[effects & 3], + baseOffset); + } + + // Draw each character in turn. + pImpl->ForEachGlyph(text, [&](Glyph const* glyph, float x, float y, float advance) + { + UNREFERENCED_PARAMETER(advance); + + XMVECTOR offset = XMVectorMultiplyAdd(XMVectorSet(x, y + glyph->YOffset, 0, 0), axisDirectionTable[effects & 3], baseOffset); + + if (effects) + { + // For mirrored characters, specify bottom and/or right instead of top left. + XMVECTOR glyphRect = XMConvertVectorIntToFloat(XMLoadInt4(reinterpret_cast(&glyph->Subrect)), 0); + + // xy = glyph width/height. + glyphRect = XMVectorSubtract(XMVectorSwizzle<2, 3, 0, 1>(glyphRect), glyphRect); + + offset = XMVectorMultiplyAdd(glyphRect, axisIsMirroredTable[effects & 3], offset); + } + + spriteBatch->Draw(pImpl->texture, pImpl->textureSize, position, &glyph->Subrect, color, rotation, offset, scale, effects, layerDepth); + }, true); +} + + +XMVECTOR XM_CALLCONV SpriteFont::MeasureString(_In_z_ wchar_t const* text, bool ignoreWhitespace) const +{ + XMVECTOR result = XMVectorZero(); + + pImpl->ForEachGlyph(text, [&](Glyph const* glyph, float x, float y, float advance) + { + UNREFERENCED_PARAMETER(advance); + + auto const w = static_cast(glyph->Subrect.right - glyph->Subrect.left); + auto h = static_cast(glyph->Subrect.bottom - glyph->Subrect.top) + glyph->YOffset; + + h = iswspace(wchar_t(glyph->Character)) ? + pImpl->lineSpacing : + std::max(h, pImpl->lineSpacing); + + result = XMVectorMax(result, XMVectorSet(x + w, y + h, 0, 0)); + }, ignoreWhitespace); + + return result; +} + + +RECT SpriteFont::MeasureDrawBounds(_In_z_ wchar_t const* text, XMFLOAT2 const& position, bool ignoreWhitespace) const +{ + RECT result = { LONG_MAX, LONG_MAX, 0, 0 }; + + pImpl->ForEachGlyph(text, [&](Glyph const* glyph, float x, float y, float advance) noexcept + { + auto const isWhitespace = iswspace(wchar_t(glyph->Character)); + auto const w = static_cast(glyph->Subrect.right - glyph->Subrect.left); + auto const h = isWhitespace ? + pImpl->lineSpacing : + static_cast(glyph->Subrect.bottom - glyph->Subrect.top); + + const float minX = position.x + x; + const float minY = position.y + y + (isWhitespace ? 0.0f : glyph->YOffset); + + const float maxX = std::max(minX + advance, minX + w); + const float maxY = minY + h; + + if (minX < float(result.left)) + result.left = long(minX); + + if (minY < float(result.top)) + result.top = long(minY); + + if (float(result.right) < maxX) + result.right = long(maxX); + + if (float(result.bottom) < maxY) + result.bottom = long(maxY); + }, ignoreWhitespace); + + if (result.left == LONG_MAX) + { + result.left = 0; + result.top = 0; + } + + return result; +} + + +RECT XM_CALLCONV SpriteFont::MeasureDrawBounds(_In_z_ wchar_t const* text, FXMVECTOR position, bool ignoreWhitespace) const +{ + XMFLOAT2 pos; + XMStoreFloat2(&pos, position); + + return MeasureDrawBounds(text, pos, ignoreWhitespace); +} + + +// UTF-8 +void XM_CALLCONV SpriteFont::DrawString(_In_ SpriteBatch* spriteBatch, _In_z_ char const* text, XMFLOAT2 const& position, FXMVECTOR color, float rotation, XMFLOAT2 const& origin, float scale, SpriteEffects effects, float layerDepth) const +{ + DrawString(spriteBatch, pImpl->ConvertUTF8(text), XMLoadFloat2(&position), color, rotation, XMLoadFloat2(&origin), XMVectorReplicate(scale), effects, layerDepth); +} + + +void XM_CALLCONV SpriteFont::DrawString(_In_ SpriteBatch* spriteBatch, _In_z_ char const* text, XMFLOAT2 const& position, FXMVECTOR color, float rotation, XMFLOAT2 const& origin, XMFLOAT2 const& scale, SpriteEffects effects, float layerDepth) const +{ + DrawString(spriteBatch, pImpl->ConvertUTF8(text), XMLoadFloat2(&position), color, rotation, XMLoadFloat2(&origin), XMLoadFloat2(&scale), effects, layerDepth); +} + + +void XM_CALLCONV SpriteFont::DrawString(_In_ SpriteBatch* spriteBatch, _In_z_ char const* text, FXMVECTOR position, FXMVECTOR color, float rotation, FXMVECTOR origin, float scale, SpriteEffects effects, float layerDepth) const +{ + DrawString(spriteBatch, pImpl->ConvertUTF8(text), position, color, rotation, origin, XMVectorReplicate(scale), effects, layerDepth); +} + + +void XM_CALLCONV SpriteFont::DrawString(_In_ SpriteBatch* spriteBatch, _In_z_ char const* text, FXMVECTOR position, FXMVECTOR color, float rotation, FXMVECTOR origin, GXMVECTOR scale, SpriteEffects effects, float layerDepth) const +{ + DrawString(spriteBatch, pImpl->ConvertUTF8(text), position, color, rotation, origin, scale, effects, layerDepth); +} + + +XMVECTOR XM_CALLCONV SpriteFont::MeasureString(_In_z_ char const* text, bool ignoreWhitespace) const +{ + return MeasureString(pImpl->ConvertUTF8(text), ignoreWhitespace); +} + + +RECT SpriteFont::MeasureDrawBounds(_In_z_ char const* text, XMFLOAT2 const& position, bool ignoreWhitespace) const +{ + return MeasureDrawBounds(pImpl->ConvertUTF8(text), position, ignoreWhitespace); +} + + +RECT XM_CALLCONV SpriteFont::MeasureDrawBounds(_In_z_ char const* text, FXMVECTOR position, bool ignoreWhitespace) const +{ + XMFLOAT2 pos; + XMStoreFloat2(&pos, position); + + return MeasureDrawBounds(pImpl->ConvertUTF8(text), pos, ignoreWhitespace); +} + + +// Spacing properties +float SpriteFont::GetLineSpacing() const noexcept +{ + return pImpl->lineSpacing; +} + + +void SpriteFont::SetLineSpacing(float spacing) +{ + pImpl->lineSpacing = spacing; +} + + +// Font properties +wchar_t SpriteFont::GetDefaultCharacter() const noexcept +{ + return static_cast(pImpl->defaultGlyph ? pImpl->defaultGlyph->Character : 0); +} + + +void SpriteFont::SetDefaultCharacter(wchar_t character) +{ + pImpl->SetDefaultCharacter(character); +} + + +bool SpriteFont::ContainsCharacter(wchar_t character) const +{ + return std::binary_search(pImpl->glyphs.begin(), pImpl->glyphs.end(), character); +} + + +// Custom layout/rendering +SpriteFont::Glyph const* SpriteFont::FindGlyph(wchar_t character) const +{ + return pImpl->FindGlyph(character); +} + + +D3D12_GPU_DESCRIPTOR_HANDLE SpriteFont::GetSpriteSheet() const noexcept +{ + return pImpl->texture; +} + + +XMUINT2 SpriteFont::GetSpriteSheetSize() const noexcept +{ + return pImpl->textureSize; +} + + +//-------------------------------------------------------------------------------------- +// Adapters for /Zc:wchar_t- clients + +#if defined(_MSC_VER) && !defined(_NATIVE_WCHAR_T_DEFINED) + +SpriteFont::SpriteFont( + ID3D12Device* device, + ResourceUploadBatch& upload, + _In_z_ __wchar_t const* fileName, + D3D12_CPU_DESCRIPTOR_HANDLE cpuDescriptorDest, + D3D12_GPU_DESCRIPTOR_HANDLE gpuDescriptor, + bool forceSRGB) : + SpriteFont( + device, upload, + reinterpret_cast(fileName), + cpuDescriptorDest, gpuDescriptor, forceSRGB) +{ +} + +void SpriteFont::DrawString(_In_ SpriteBatch* spriteBatch, _In_z_ __wchar_t const* text, XMFLOAT2 const& position, FXMVECTOR color, float rotation, XMFLOAT2 const& origin, float scale, SpriteEffects effects, float layerDepth) const +{ + DrawString(spriteBatch, reinterpret_cast(text), XMLoadFloat2(&position), color, rotation, XMLoadFloat2(&origin), XMVectorReplicate(scale), effects, layerDepth); +} + +void SpriteFont::DrawString(_In_ SpriteBatch* spriteBatch, _In_z_ __wchar_t const* text, XMFLOAT2 const& position, FXMVECTOR color, float rotation, XMFLOAT2 const& origin, XMFLOAT2 const& scale, SpriteEffects effects, float layerDepth) const +{ + DrawString(spriteBatch, reinterpret_cast(text), XMLoadFloat2(&position), color, rotation, XMLoadFloat2(&origin), XMLoadFloat2(&scale), effects, layerDepth); +} + +void SpriteFont::DrawString(_In_ SpriteBatch* spriteBatch, _In_z_ __wchar_t const* text, FXMVECTOR position, FXMVECTOR color, float rotation, FXMVECTOR origin, float scale, SpriteEffects effects, float layerDepth) const +{ + DrawString(spriteBatch, reinterpret_cast(text), position, color, rotation, origin, XMVectorReplicate(scale), effects, layerDepth); +} + +void XM_CALLCONV SpriteFont::DrawString(_In_ SpriteBatch* spriteBatch, _In_z_ __wchar_t const* text, FXMVECTOR position, FXMVECTOR color, float rotation, FXMVECTOR origin, GXMVECTOR scale, SpriteEffects effects, float layerDepth) const +{ + DrawString(spriteBatch, reinterpret_cast(text), position, color, rotation, origin, scale, effects, layerDepth); +} + +XMVECTOR SpriteFont::MeasureString(_In_z_ __wchar_t const* text, bool ignoreWhitespace) const +{ + return MeasureString(reinterpret_cast(text), ignoreWhitespace); +} + +RECT SpriteFont::MeasureDrawBounds(_In_z_ __wchar_t const* text, XMFLOAT2 const& position, bool ignoreWhitespace) const +{ + return MeasureDrawBounds(reinterpret_cast(text), position, ignoreWhitespace); +} + +RECT SpriteFont::MeasureDrawBounds(_In_z_ __wchar_t const* text, FXMVECTOR position, bool ignoreWhitespace) const +{ + XMFLOAT2 pos; + XMStoreFloat2(&pos, position); + + return MeasureDrawBounds(reinterpret_cast(text), pos, ignoreWhitespace); +} + +// Can't do this for GetDefaultCharacter since it only differs by return type. + +void SpriteFont::SetDefaultCharacter(__wchar_t character) +{ + pImpl->SetDefaultCharacter(static_cast(character)); +} + +bool SpriteFont::ContainsCharacter(__wchar_t character) const +{ + return ContainsCharacter(static_cast(character)); +} + +SpriteFont::Glyph const* SpriteFont::FindGlyph(__wchar_t character) const +{ + return pImpl->FindGlyph(static_cast(character)); +} + +#endif // !_NATIVE_WCHAR_T_DEFINED diff --git a/Common/DirectXTK12/Src/TeapotData.inc b/Common/DirectXTK12/Src/TeapotData.inc new file mode 100644 index 0000000..29dfd4f --- /dev/null +++ b/Common/DirectXTK12/Src/TeapotData.inc @@ -0,0 +1,182 @@ +//-------------------------------------------------------------------------------------- +// File: TeapotData.inc +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248929 +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + + +// The teapot model consists of 10 bezier patches. Each patch has 16 control +// points, plus a flag indicating whether it should be mirrored in the Z axis +// as well as in X (all of the teapot is symmetrical from left to right, but +// only some parts are symmetrical from front to back). The control points +// are stored as integer indices into the TeapotControlPoints array. + +struct TeapotPatch +{ + bool mirrorZ; + int indices[16]; +}; + + +// Static data array defines the bezier patches that make up the teapot. +const TeapotPatch TeapotPatches[] = +{ + // Rim. + { true, { 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } }, + + // Body. + { true, { 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27 } }, + { true, { 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 } }, + + // Lid. + { true, { 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3 } }, + { true, { 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117 } }, + + // Handle. + { false, { 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56 } }, + { false, { 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 28, 65, 66, 67 } }, + + // Spout. + { false, { 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83 } }, + { false, { 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95 } }, + + // Bottom. + { true, { 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37 } }, +}; + + +// Static array defines the control point positions that make up the teapot. +const DirectX::XMVECTORF32 TeapotControlPoints[] = +{ + { { { 0, 0.345f, -0.05f, 0 } } }, + { { { -0.028f, 0.345f, -0.05f, 0 } } }, + { { { -0.05f, 0.345f, -0.028f, 0 } } }, + { { { -0.05f, 0.345f, -0, 0 } } }, + { { { 0, 0.3028125f, -0.334375f, 0 } } }, + { { { -0.18725f, 0.3028125f, -0.334375f, 0 } } }, + { { { -0.334375f, 0.3028125f, -0.18725f, 0 } } }, + { { { -0.334375f, 0.3028125f, -0, 0 } } }, + { { { 0, 0.3028125f, -0.359375f, 0 } } }, + { { { -0.20125f, 0.3028125f, -0.359375f, 0 } } }, + { { { -0.359375f, 0.3028125f, -0.20125f, 0 } } }, + { { { -0.359375f, 0.3028125f, -0, 0 } } }, + { { { 0, 0.27f, -0.375f, 0 } } }, + { { { -0.21f, 0.27f, -0.375f, 0 } } }, + { { { -0.375f, 0.27f, -0.21f, 0 } } }, + { { { -0.375f, 0.27f, -0, 0 } } }, + { { { 0, 0.13875f, -0.4375f, 0 } } }, + { { { -0.245f, 0.13875f, -0.4375f, 0 } } }, + { { { -0.4375f, 0.13875f, -0.245f, 0 } } }, + { { { -0.4375f, 0.13875f, -0, 0 } } }, + { { { 0, 0.007499993f, -0.5f, 0 } } }, + { { { -0.28f, 0.007499993f, -0.5f, 0 } } }, + { { { -0.5f, 0.007499993f, -0.28f, 0 } } }, + { { { -0.5f, 0.007499993f, -0, 0 } } }, + { { { 0, -0.105f, -0.5f, 0 } } }, + { { { -0.28f, -0.105f, -0.5f, 0 } } }, + { { { -0.5f, -0.105f, -0.28f, 0 } } }, + { { { -0.5f, -0.105f, -0, 0 } } }, + { { { 0, -0.105f, 0.5f, 0 } } }, + { { { 0, -0.2175f, -0.5f, 0 } } }, + { { { -0.28f, -0.2175f, -0.5f, 0 } } }, + { { { -0.5f, -0.2175f, -0.28f, 0 } } }, + { { { -0.5f, -0.2175f, -0, 0 } } }, + { { { 0, -0.27375f, -0.375f, 0 } } }, + { { { -0.21f, -0.27375f, -0.375f, 0 } } }, + { { { -0.375f, -0.27375f, -0.21f, 0 } } }, + { { { -0.375f, -0.27375f, -0, 0 } } }, + { { { 0, -0.2925f, -0.375f, 0 } } }, + { { { -0.21f, -0.2925f, -0.375f, 0 } } }, + { { { -0.375f, -0.2925f, -0.21f, 0 } } }, + { { { -0.375f, -0.2925f, -0, 0 } } }, + { { { 0, 0.17625f, 0.4f, 0 } } }, + { { { -0.075f, 0.17625f, 0.4f, 0 } } }, + { { { -0.075f, 0.2325f, 0.375f, 0 } } }, + { { { 0, 0.2325f, 0.375f, 0 } } }, + { { { 0, 0.17625f, 0.575f, 0 } } }, + { { { -0.075f, 0.17625f, 0.575f, 0 } } }, + { { { -0.075f, 0.2325f, 0.625f, 0 } } }, + { { { 0, 0.2325f, 0.625f, 0 } } }, + { { { 0, 0.17625f, 0.675f, 0 } } }, + { { { -0.075f, 0.17625f, 0.675f, 0 } } }, + { { { -0.075f, 0.2325f, 0.75f, 0 } } }, + { { { 0, 0.2325f, 0.75f, 0 } } }, + { { { 0, 0.12f, 0.675f, 0 } } }, + { { { -0.075f, 0.12f, 0.675f, 0 } } }, + { { { -0.075f, 0.12f, 0.75f, 0 } } }, + { { { 0, 0.12f, 0.75f, 0 } } }, + { { { 0, 0.06375f, 0.675f, 0 } } }, + { { { -0.075f, 0.06375f, 0.675f, 0 } } }, + { { { -0.075f, 0.007499993f, 0.75f, 0 } } }, + { { { 0, 0.007499993f, 0.75f, 0 } } }, + { { { 0, -0.04875001f, 0.625f, 0 } } }, + { { { -0.075f, -0.04875001f, 0.625f, 0 } } }, + { { { -0.075f, -0.09562501f, 0.6625f, 0 } } }, + { { { 0, -0.09562501f, 0.6625f, 0 } } }, + { { { -0.075f, -0.105f, 0.5f, 0 } } }, + { { { -0.075f, -0.18f, 0.475f, 0 } } }, + { { { 0, -0.18f, 0.475f, 0 } } }, + { { { 0, 0.02624997f, -0.425f, 0 } } }, + { { { -0.165f, 0.02624997f, -0.425f, 0 } } }, + { { { -0.165f, -0.18f, -0.425f, 0 } } }, + { { { 0, -0.18f, -0.425f, 0 } } }, + { { { 0, 0.02624997f, -0.65f, 0 } } }, + { { { -0.165f, 0.02624997f, -0.65f, 0 } } }, + { { { -0.165f, -0.12375f, -0.775f, 0 } } }, + { { { 0, -0.12375f, -0.775f, 0 } } }, + { { { 0, 0.195f, -0.575f, 0 } } }, + { { { -0.0625f, 0.195f, -0.575f, 0 } } }, + { { { -0.0625f, 0.17625f, -0.6f, 0 } } }, + { { { 0, 0.17625f, -0.6f, 0 } } }, + { { { 0, 0.27f, -0.675f, 0 } } }, + { { { -0.0625f, 0.27f, -0.675f, 0 } } }, + { { { -0.0625f, 0.27f, -0.825f, 0 } } }, + { { { 0, 0.27f, -0.825f, 0 } } }, + { { { 0, 0.28875f, -0.7f, 0 } } }, + { { { -0.0625f, 0.28875f, -0.7f, 0 } } }, + { { { -0.0625f, 0.2934375f, -0.88125f, 0 } } }, + { { { 0, 0.2934375f, -0.88125f, 0 } } }, + { { { 0, 0.28875f, -0.725f, 0 } } }, + { { { -0.0375f, 0.28875f, -0.725f, 0 } } }, + { { { -0.0375f, 0.298125f, -0.8625f, 0 } } }, + { { { 0, 0.298125f, -0.8625f, 0 } } }, + { { { 0, 0.27f, -0.7f, 0 } } }, + { { { -0.0375f, 0.27f, -0.7f, 0 } } }, + { { { -0.0375f, 0.27f, -0.8f, 0 } } }, + { { { 0, 0.27f, -0.8f, 0 } } }, + { { { 0, 0.4575f, -0, 0 } } }, + { { { 0, 0.4575f, -0.2f, 0 } } }, + { { { -0.1125f, 0.4575f, -0.2f, 0 } } }, + { { { -0.2f, 0.4575f, -0.1125f, 0 } } }, + { { { -0.2f, 0.4575f, -0, 0 } } }, + { { { 0, 0.3825f, -0, 0 } } }, + { { { 0, 0.27f, -0.35f, 0 } } }, + { { { -0.196f, 0.27f, -0.35f, 0 } } }, + { { { -0.35f, 0.27f, -0.196f, 0 } } }, + { { { -0.35f, 0.27f, -0, 0 } } }, + { { { 0, 0.3075f, -0.1f, 0 } } }, + { { { -0.056f, 0.3075f, -0.1f, 0 } } }, + { { { -0.1f, 0.3075f, -0.056f, 0 } } }, + { { { -0.1f, 0.3075f, -0, 0 } } }, + { { { 0, 0.3075f, -0.325f, 0 } } }, + { { { -0.182f, 0.3075f, -0.325f, 0 } } }, + { { { -0.325f, 0.3075f, -0.182f, 0 } } }, + { { { -0.325f, 0.3075f, -0, 0 } } }, + { { { 0, 0.27f, -0.325f, 0 } } }, + { { { -0.182f, 0.27f, -0.325f, 0 } } }, + { { { -0.325f, 0.27f, -0.182f, 0 } } }, + { { { -0.325f, 0.27f, -0, 0 } } }, + { { { 0, -0.33f, -0, 0 } } }, + { { { -0.1995f, -0.33f, -0.35625f, 0 } } }, + { { { 0, -0.31125f, -0.375f, 0 } } }, + { { { 0, -0.33f, -0.35625f, 0 } } }, + { { { -0.35625f, -0.33f, -0.1995f, 0 } } }, + { { { -0.375f, -0.31125f, -0, 0 } } }, + { { { -0.35625f, -0.33f, -0, 0 } } }, + { { { -0.21f, -0.31125f, -0.375f, 0 } } }, + { { { -0.375f, -0.31125f, -0.21f, 0 } } }, +}; diff --git a/Common/DirectXTK12/Src/ToneMapPostProcess.cpp b/Common/DirectXTK12/Src/ToneMapPostProcess.cpp new file mode 100644 index 0000000..3c4e8cf --- /dev/null +++ b/Common/DirectXTK12/Src/ToneMapPostProcess.cpp @@ -0,0 +1,503 @@ +//-------------------------------------------------------------------------------------- +// File: ToneMapPostProcess.cpp +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "pch.h" +#include "PostProcess.h" + +#include "AlignedNew.h" +#include "CommonStates.h" +#include "DemandCreate.h" +#include "DirectXHelpers.h" +#include "EffectPipelineStateDescription.h" +#include "GraphicsMemory.h" +#include "PlatformHelpers.h" +#include "SharedResourcePool.h" + +using namespace DirectX; + +using Microsoft::WRL::ComPtr; + +namespace +{ + constexpr int Dirty_ConstantBuffer = 0x01; + constexpr int Dirty_Parameters = 0x02; + +#if (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) + constexpr int PixelShaderCount = 15; + constexpr int ShaderPermutationCount = 24; +#else + constexpr int PixelShaderCount = 9; + constexpr int ShaderPermutationCount = 12; +#endif + + // Constant buffer layout. Must match the shader! + XM_ALIGNED_STRUCT(16) ToneMapConstants + { + // linearExposure is .x + // paperWhiteNits is .y + XMVECTOR parameters; + XMVECTOR colorRotation[3]; + }; + + static_assert((sizeof(ToneMapConstants) % 16) == 0, "CB size not padded correctly"); + + // HDTV to UHDTV (Rec.709 color primaries into Rec.2020) + constexpr float c_from709to2020[12] = + { + 0.6274040f, 0.3292820f, 0.0433136f, 0.f, + 0.0690970f, 0.9195400f, 0.0113612f, 0.f, + 0.0163916f, 0.0880132f, 0.8955950f, 0.f, + }; + + // DCI-P3-D65 https://en.wikipedia.org/wiki/DCI-P3 to UHDTV (DCI-P3-D65 color primaries into Rec.2020) + constexpr float c_fromP3D65to2020[12] = + { + 0.753845f, 0.198593f, 0.047562f, 0.f, + 0.0457456f, 0.941777f, 0.0124772f, 0.f, + -0.00121055f, 0.0176041f, 0.983607f, 0.f, + }; + + // HDTV to DCI-P3-D65 (a.k.a. Display P3 or P3D65) + constexpr float c_from709toP3D65[12] = + { + 0.822461969f, 0.1775380f, 0.f, 0.f, + 0.033194199f, 0.9668058f, 0.f, 0.f, + 0.017082631f, 0.0723974f, 0.9105199f, 0.f, + }; +} + +#pragma region Shaders +// Include the precompiled shader code. +namespace +{ +#ifdef _GAMING_XBOX_SCARLETT +#include "XboxGamingScarlettToneMap_VSQuad.inc" + +#include "XboxGamingScarlettToneMap_PSCopy.inc" +#include "XboxGamingScarlettToneMap_PSSaturate.inc" +#include "XboxGamingScarlettToneMap_PSReinhard.inc" +#include "XboxGamingScarlettToneMap_PSACESFilmic.inc" +#include "XboxGamingScarlettToneMap_PS_SRGB.inc" +#include "XboxGamingScarlettToneMap_PSSaturate_SRGB.inc" +#include "XboxGamingScarlettToneMap_PSReinhard_SRGB.inc" +#include "XboxGamingScarlettToneMap_PSACESFilmic_SRGB.inc" +#include "XboxGamingScarlettToneMap_PSHDR10.inc" +#include "XboxGamingScarlettToneMap_PSHDR10_Saturate.inc" +#include "XboxGamingScarlettToneMap_PSHDR10_Reinhard.inc" +#include "XboxGamingScarlettToneMap_PSHDR10_ACESFilmic.inc" +#include "XboxGamingScarlettToneMap_PSHDR10_Saturate_SRGB.inc" +#include "XboxGamingScarlettToneMap_PSHDR10_Reinhard_SRGB.inc" +#include "XboxGamingScarlettToneMap_PSHDR10_ACESFilmic_SRGB.inc" +#elif defined(_GAMING_XBOX) +#include "XboxGamingXboxOneToneMap_VSQuad.inc" + +#include "XboxGamingXboxOneToneMap_PSCopy.inc" +#include "XboxGamingXboxOneToneMap_PSSaturate.inc" +#include "XboxGamingXboxOneToneMap_PSReinhard.inc" +#include "XboxGamingXboxOneToneMap_PSACESFilmic.inc" +#include "XboxGamingXboxOneToneMap_PS_SRGB.inc" +#include "XboxGamingXboxOneToneMap_PSSaturate_SRGB.inc" +#include "XboxGamingXboxOneToneMap_PSReinhard_SRGB.inc" +#include "XboxGamingXboxOneToneMap_PSACESFilmic_SRGB.inc" +#include "XboxGamingXboxOneToneMap_PSHDR10.inc" +#include "XboxGamingXboxOneToneMap_PSHDR10_Saturate.inc" +#include "XboxGamingXboxOneToneMap_PSHDR10_Reinhard.inc" +#include "XboxGamingXboxOneToneMap_PSHDR10_ACESFilmic.inc" +#include "XboxGamingXboxOneToneMap_PSHDR10_Saturate_SRGB.inc" +#include "XboxGamingXboxOneToneMap_PSHDR10_Reinhard_SRGB.inc" +#include "XboxGamingXboxOneToneMap_PSHDR10_ACESFilmic_SRGB.inc" +#elif defined(_XBOX_ONE) && defined(_TITLE) +#include "XboxOneToneMap_VSQuad.inc" + +#include "XboxOneToneMap_PSCopy.inc" +#include "XboxOneToneMap_PSSaturate.inc" +#include "XboxOneToneMap_PSReinhard.inc" +#include "XboxOneToneMap_PSACESFilmic.inc" +#include "XboxOneToneMap_PS_SRGB.inc" +#include "XboxOneToneMap_PSSaturate_SRGB.inc" +#include "XboxOneToneMap_PSReinhard_SRGB.inc" +#include "XboxOneToneMap_PSACESFilmic_SRGB.inc" +#include "XboxOneToneMap_PSHDR10.inc" +#include "XboxOneToneMap_PSHDR10_Saturate.inc" +#include "XboxOneToneMap_PSHDR10_Reinhard.inc" +#include "XboxOneToneMap_PSHDR10_ACESFilmic.inc" +#include "XboxOneToneMap_PSHDR10_Saturate_SRGB.inc" +#include "XboxOneToneMap_PSHDR10_Reinhard_SRGB.inc" +#include "XboxOneToneMap_PSHDR10_ACESFilmic_SRGB.inc" +#else +#include "ToneMap_VSQuad.inc" + +#include "ToneMap_PSCopy.inc" +#include "ToneMap_PSSaturate.inc" +#include "ToneMap_PSReinhard.inc" +#include "ToneMap_PSACESFilmic.inc" +#include "ToneMap_PS_SRGB.inc" +#include "ToneMap_PSSaturate_SRGB.inc" +#include "ToneMap_PSReinhard_SRGB.inc" +#include "ToneMap_PSACESFilmic_SRGB.inc" +#include "ToneMap_PSHDR10.inc" +#endif +} + +namespace +{ + const D3D12_SHADER_BYTECODE vertexShader = + { ToneMap_VSQuad, sizeof(ToneMap_VSQuad) }; + + const D3D12_SHADER_BYTECODE pixelShaders[] = + { + { ToneMap_PSCopy, sizeof(ToneMap_PSCopy) }, + { ToneMap_PSSaturate, sizeof(ToneMap_PSSaturate) }, + { ToneMap_PSReinhard, sizeof(ToneMap_PSReinhard) }, + { ToneMap_PSACESFilmic, sizeof(ToneMap_PSACESFilmic) }, + { ToneMap_PS_SRGB, sizeof(ToneMap_PS_SRGB) }, + { ToneMap_PSSaturate_SRGB, sizeof(ToneMap_PSSaturate_SRGB) }, + { ToneMap_PSReinhard_SRGB, sizeof(ToneMap_PSReinhard_SRGB) }, + { ToneMap_PSACESFilmic_SRGB, sizeof(ToneMap_PSACESFilmic_SRGB) }, + { ToneMap_PSHDR10, sizeof(ToneMap_PSHDR10) }, + +#if (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) + // Shaders that generate both HDR10 and GameDVR SDR signals via Multiple Render Targets. + { ToneMap_PSHDR10_Saturate, sizeof(ToneMap_PSHDR10_Saturate) }, + { ToneMap_PSHDR10_Reinhard, sizeof(ToneMap_PSHDR10_Reinhard) }, + { ToneMap_PSHDR10_ACESFilmic, sizeof(ToneMap_PSHDR10_ACESFilmic) }, + { ToneMap_PSHDR10_Saturate_SRGB, sizeof(ToneMap_PSHDR10_Saturate_SRGB) }, + { ToneMap_PSHDR10_Reinhard_SRGB, sizeof(ToneMap_PSHDR10_Reinhard_SRGB) }, + { ToneMap_PSHDR10_ACESFilmic_SRGB, sizeof(ToneMap_PSHDR10_ACESFilmic_SRGB) }, +#endif + }; + + static_assert(static_cast(std::size(pixelShaders)) == PixelShaderCount, "array/max mismatch"); + + const int pixelShaderIndices[] = + { + // Linear EOTF + 0, // Copy + 1, // Saturate + 2, // Reinhard + 3, // ACES Filmic + + // Gamam22 EOTF + 4, // SRGB + 5, // Saturate_SRGB + 6, // Reinhard_SRGB + 7, // ACES Filmic + + // ST.2084 EOTF + 8, // HDR10 + 8, // HDR10 + 8, // HDR10 + 8, // HDR10 + +#if (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) + // MRT Linear EOTF + 9, // HDR10+Saturate + 9, // HDR10+Saturate + 10, // HDR10+Reinhard + 11, // HDR10+ACESFilmic + + // MRT Gamma22 EOTF + 12, // HDR10+Saturate_SRGB + 12, // HDR10+Saturate_SRGB + 13, // HDR10+Reinhard_SRGB + 14, // HDR10+ACESFilmic + + // MRT ST.2084 EOTF + 9, // HDR10+Saturate + 9, // HDR10+Saturate + 10, // HDR10+Reinhard + 11, // HDR10+ACESFilmic +#endif + }; + + static_assert(static_cast(std::size(pixelShaderIndices)) == ShaderPermutationCount, "array/max mismatch"); + + // Factory for lazily instantiating shared root signatures. + class DeviceResources + { + public: + DeviceResources(_In_ ID3D12Device* device) noexcept + : mDevice(device) + { + } + + ID3D12RootSignature* GetRootSignature(const D3D12_ROOT_SIGNATURE_DESC& desc) + { + return DemandCreate(mRootSignature, mMutex, [&](ID3D12RootSignature** pResult) noexcept -> HRESULT + { + HRESULT hr = CreateRootSignature(mDevice.Get(), &desc, pResult); + + if (SUCCEEDED(hr)) + SetDebugObjectName(*pResult, L"ToneMapPostProcess"); + + return hr; + }); + } + + ID3D12Device* GetDevice() const noexcept { return mDevice.Get(); } + + protected: + ComPtr mDevice; + ComPtr mRootSignature; + std::mutex mMutex; + }; +} +#pragma endregion + +class ToneMapPostProcess::Impl : public AlignedNew +{ +public: + Impl(_In_ ID3D12Device* device, const RenderTargetState& rtState, Operator op, TransferFunction func, bool mrt = false); + + void Process(_In_ ID3D12GraphicsCommandList* commandList); + + void SetDirtyFlag() noexcept { mDirtyFlags = INT_MAX; } + + enum RootParameterIndex + { + TextureSRV, + ConstantBuffer, + RootParameterCount + }; + + // Fields. + ToneMapConstants constants; + D3D12_GPU_DESCRIPTOR_HANDLE texture; + float linearExposure; + float paperWhiteNits; + +private: + int mDirtyFlags; + + // D3D constant buffer holds a copy of the same data as the public 'constants' field. + GraphicsResource mConstantBuffer; + + // Per instance cache of PSOs, populated with variants for each shader & layout + Microsoft::WRL::ComPtr mPipelineState; + + // Per instance root signature + ID3D12RootSignature* mRootSignature; + + // Per-device resources. + std::shared_ptr mDeviceResources; + + static SharedResourcePool deviceResourcesPool; +}; + + +// Global pool of per-device ToneMapPostProcess resources. +SharedResourcePool ToneMapPostProcess::Impl::deviceResourcesPool; + + +// Constructor. +ToneMapPostProcess::Impl::Impl(_In_ ID3D12Device* device, const RenderTargetState& rtState, Operator op, TransferFunction func, bool mrt) + : constants{}, + texture{}, + linearExposure(1.f), + paperWhiteNits(200.f), + mDirtyFlags(INT_MAX), + mDeviceResources(deviceResourcesPool.DemandCreate(device)) +{ + if (op >= Operator_Max) + throw std::invalid_argument("Tonemap operator not defined"); + + if (func > TransferFunction_Max) + throw std::invalid_argument("Transfer function not defined"); + + // Create root signature. + { + ENUM_FLAGS_CONSTEXPR D3D12_ROOT_SIGNATURE_FLAGS rootSignatureFlags = + D3D12_ROOT_SIGNATURE_FLAG_DENY_VERTEX_SHADER_ROOT_ACCESS + | D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS + | D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS + | D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS +#ifdef _GAMING_XBOX_SCARLETT + | D3D12_ROOT_SIGNATURE_FLAG_DENY_AMPLIFICATION_SHADER_ROOT_ACCESS + | D3D12_ROOT_SIGNATURE_FLAG_DENY_MESH_SHADER_ROOT_ACCESS +#endif + ; + + const CD3DX12_DESCRIPTOR_RANGE textureSRVs(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0); + + // Same as CommonStates::StaticPointClamp + const CD3DX12_STATIC_SAMPLER_DESC sampler( + 0, // register + D3D12_FILTER_MIN_MAG_MIP_POINT, + D3D12_TEXTURE_ADDRESS_MODE_CLAMP, + D3D12_TEXTURE_ADDRESS_MODE_CLAMP, + D3D12_TEXTURE_ADDRESS_MODE_CLAMP, + 0.f, + 16, + D3D12_COMPARISON_FUNC_LESS_EQUAL, + D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE, + 0.f, + D3D12_FLOAT32_MAX, + D3D12_SHADER_VISIBILITY_PIXEL); + + CD3DX12_ROOT_PARAMETER rootParameters[RootParameterIndex::RootParameterCount] = {}; + + const CD3DX12_DESCRIPTOR_RANGE texture1Range(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0); + rootParameters[RootParameterIndex::TextureSRV].InitAsDescriptorTable(1, &textureSRVs, D3D12_SHADER_VISIBILITY_PIXEL); + + // Root parameter descriptor + CD3DX12_ROOT_SIGNATURE_DESC rsigDesc = {}; + + // Constant buffer + rootParameters[RootParameterIndex::ConstantBuffer].InitAsConstantBufferView(0, 0, D3D12_SHADER_VISIBILITY_PIXEL); + + rsigDesc.Init(static_cast(std::size(rootParameters)), rootParameters, 1, &sampler, rootSignatureFlags); + + mRootSignature = mDeviceResources->GetRootSignature(rsigDesc); + } + + assert(mRootSignature != nullptr); + + // Determine shader permutation. +#if (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) + int permutation = (mrt) ? 12 : 0; + permutation += (static_cast(func) * static_cast(Operator_Max)) + static_cast(op); +#else + UNREFERENCED_PARAMETER(mrt); + const int permutation = (static_cast(func) * static_cast(Operator_Max)) + static_cast(op); +#endif + + assert(permutation >= 0 && permutation < ShaderPermutationCount); + _Analysis_assume_(permutation >= 0 && permutation < ShaderPermutationCount); + + const int shaderIndex = pixelShaderIndices[permutation]; + assert(shaderIndex >= 0 && shaderIndex < PixelShaderCount); + _Analysis_assume_(shaderIndex >= 0 && shaderIndex < PixelShaderCount); + + // Create pipeline state. + const EffectPipelineStateDescription psd(nullptr, + CommonStates::Opaque, + CommonStates::DepthNone, + CommonStates::CullNone, + rtState, + D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE); + + psd.CreatePipelineState( + device, + mRootSignature, + vertexShader, + pixelShaders[shaderIndex], + mPipelineState.GetAddressOf()); + + memcpy(constants.colorRotation, c_from709to2020, sizeof(c_from709to2020)); + + SetDebugObjectName(mPipelineState.Get(), L"ToneMapPostProcess"); +} + + +// Sets our state onto the D3D device. +void ToneMapPostProcess::Impl::Process(_In_ ID3D12GraphicsCommandList* commandList) +{ + // Set the root signature. + commandList->SetGraphicsRootSignature(mRootSignature); + + // Set the texture. + if (!texture.ptr) + { + DebugTrace("ERROR: Missing texture for ToneMapPostProcess (texture %llu)\n", texture.ptr); + throw std::runtime_error("ToneMapPostProcess"); + } + commandList->SetGraphicsRootDescriptorTable(RootParameterIndex::TextureSRV, texture); + + // Set constants. + if (mDirtyFlags & Dirty_Parameters) + { + mDirtyFlags &= ~Dirty_Parameters; + mDirtyFlags |= Dirty_ConstantBuffer; + + constants.parameters = XMVectorSet(linearExposure, paperWhiteNits, 0.f, 0.f); + } + + if (mDirtyFlags & Dirty_ConstantBuffer) + { + mDirtyFlags &= ~Dirty_ConstantBuffer; + mConstantBuffer = GraphicsMemory::Get(mDeviceResources->GetDevice()).AllocateConstant(constants); + } + + commandList->SetGraphicsRootConstantBufferView(RootParameterIndex::ConstantBuffer, mConstantBuffer.GpuAddress()); + + // Set the pipeline state. + commandList->SetPipelineState(mPipelineState.Get()); + + // Draw quad. + commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + commandList->DrawInstanced(3, 1, 0, 0); +} + + +// Public constructor. +#if (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) +ToneMapPostProcess::ToneMapPostProcess(_In_ ID3D12Device* device, const RenderTargetState& rtState, Operator op, TransferFunction func, bool mrt) + : pImpl(std::make_unique(device, rtState, op, func, mrt)) +#else +ToneMapPostProcess::ToneMapPostProcess(_In_ ID3D12Device* device, const RenderTargetState& rtState, Operator op, TransferFunction func) + : pImpl(std::make_unique(device, rtState, op, func)) +#endif +{ +} + + +ToneMapPostProcess::ToneMapPostProcess(ToneMapPostProcess&&) noexcept = default; +ToneMapPostProcess& ToneMapPostProcess::operator= (ToneMapPostProcess&&) noexcept = default; +ToneMapPostProcess::~ToneMapPostProcess() = default; + + +// IPostProcess methods. +void ToneMapPostProcess::Process(_In_ ID3D12GraphicsCommandList* commandList) +{ + pImpl->Process(commandList); +} + + +// Properties +void ToneMapPostProcess::SetHDRSourceTexture(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor) +{ + pImpl->texture = srvDescriptor; +} + + +void ToneMapPostProcess::SetColorRotation(ColorPrimaryRotation value) +{ + switch (value) + { + case DCI_P3_D65_to_UHDTV: memcpy(pImpl->constants.colorRotation, c_fromP3D65to2020, sizeof(c_fromP3D65to2020)); break; + case HDTV_to_DCI_P3_D65: memcpy(pImpl->constants.colorRotation, c_from709toP3D65, sizeof(c_from709toP3D65)); break; + default: memcpy(pImpl->constants.colorRotation, c_from709to2020, sizeof(c_from709to2020)); break; + } + + pImpl->SetDirtyFlag(); +} + + +void ToneMapPostProcess::SetColorRotation(CXMMATRIX value) +{ + const XMMATRIX transpose = XMMatrixTranspose(value); + pImpl->constants.colorRotation[0] = transpose.r[0]; + pImpl->constants.colorRotation[1] = transpose.r[1]; + pImpl->constants.colorRotation[2] = transpose.r[2]; + pImpl->SetDirtyFlag(); +} + + +void ToneMapPostProcess::SetExposure(float exposureValue) +{ + pImpl->linearExposure = powf(2.f, exposureValue); + pImpl->SetDirtyFlag(); +} + + +void ToneMapPostProcess::SetST2084Parameter(float paperWhiteNits) +{ + pImpl->paperWhiteNits = paperWhiteNits; + pImpl->SetDirtyFlag(); +} diff --git a/Common/DirectXTK12/Src/VertexTypes.cpp b/Common/DirectXTK12/Src/VertexTypes.cpp new file mode 100644 index 0000000..c302139 --- /dev/null +++ b/Common/DirectXTK12/Src/VertexTypes.cpp @@ -0,0 +1,167 @@ +//-------------------------------------------------------------------------------------- +// File: VertexTypes.cpp +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "pch.h" +#include "VertexTypes.h" + +using namespace DirectX; + +//-------------------------------------------------------------------------------------- +// Vertex struct holding position information. +const D3D12_INPUT_ELEMENT_DESC VertexPosition::InputElements[] = +{ + { "SV_Position", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, +}; + +static_assert(sizeof(VertexPosition) == 12, "Vertex struct/layout mismatch"); + +const D3D12_INPUT_LAYOUT_DESC VertexPosition::InputLayout = +{ + VertexPosition::InputElements, + VertexPosition::InputElementCount +}; + +//-------------------------------------------------------------------------------------- +// Vertex struct holding position and color information. +const D3D12_INPUT_ELEMENT_DESC VertexPositionColor::InputElements[] = +{ + { "SV_Position", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, +}; + +static_assert(sizeof(VertexPositionColor) == 28, "Vertex struct/layout mismatch"); + +const D3D12_INPUT_LAYOUT_DESC VertexPositionColor::InputLayout = +{ + VertexPositionColor::InputElements, + VertexPositionColor::InputElementCount +}; + +//-------------------------------------------------------------------------------------- +// Vertex struct holding position and texture mapping information. +const D3D12_INPUT_ELEMENT_DESC VertexPositionTexture::InputElements[] = +{ + { "SV_Position", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, +}; + +static_assert(sizeof(VertexPositionTexture) == 20, "Vertex struct/layout mismatch"); + +const D3D12_INPUT_LAYOUT_DESC VertexPositionTexture::InputLayout = +{ + VertexPositionTexture::InputElements, + VertexPositionTexture::InputElementCount +}; + +//-------------------------------------------------------------------------------------- +// Vertex struct holding position and dual texture mapping information. +const D3D12_INPUT_ELEMENT_DESC VertexPositionDualTexture::InputElements[] = +{ + { "SV_Position", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + { "TEXCOORD", 1, DXGI_FORMAT_R32G32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, +}; + +static_assert(sizeof(VertexPositionDualTexture) == 28, "Vertex struct/layout mismatch"); + +const D3D12_INPUT_LAYOUT_DESC VertexPositionDualTexture::InputLayout = +{ + VertexPositionDualTexture::InputElements, + VertexPositionDualTexture::InputElementCount +}; + +//-------------------------------------------------------------------------------------- +// Vertex struct holding position and normal vector. +const D3D12_INPUT_ELEMENT_DESC VertexPositionNormal::InputElements[] = +{ + { "SV_Position", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + { "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, +}; + +static_assert(sizeof(VertexPositionNormal) == 24, "Vertex struct/layout mismatch"); + +const D3D12_INPUT_LAYOUT_DESC VertexPositionNormal::InputLayout = +{ + VertexPositionNormal::InputElements, + VertexPositionNormal::InputElementCount +}; + +//-------------------------------------------------------------------------------------- +// Vertex struct holding position, color, and texture mapping information. +const D3D12_INPUT_ELEMENT_DESC VertexPositionColorTexture::InputElements[] = +{ + { "SV_Position", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, +}; + +static_assert(sizeof(VertexPositionColorTexture) == 36, "Vertex struct/layout mismatch"); + +const D3D12_INPUT_LAYOUT_DESC VertexPositionColorTexture::InputLayout = +{ + VertexPositionColorTexture::InputElements, + VertexPositionColorTexture::InputElementCount +}; + + +//-------------------------------------------------------------------------------------- +// Vertex struct holding position, normal vector, and color information. +const D3D12_INPUT_ELEMENT_DESC VertexPositionNormalColor::InputElements[] = +{ + { "SV_Position", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + { "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, +}; + +static_assert(sizeof(VertexPositionNormalColor) == 40, "Vertex struct/layout mismatch"); + +const D3D12_INPUT_LAYOUT_DESC VertexPositionNormalColor::InputLayout = +{ + VertexPositionNormalColor::InputElements, + VertexPositionNormalColor::InputElementCount +}; + +//-------------------------------------------------------------------------------------- +// Vertex struct holding position, normal vector, and texture mapping information. +const D3D12_INPUT_ELEMENT_DESC VertexPositionNormalTexture::InputElements[] = +{ + { "SV_Position", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + { "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, +}; + +static_assert(sizeof(VertexPositionNormalTexture) == 32, "Vertex struct/layout mismatch"); + +const D3D12_INPUT_LAYOUT_DESC VertexPositionNormalTexture::InputLayout = +{ + VertexPositionNormalTexture::InputElements, + VertexPositionNormalTexture::InputElementCount +}; + +//-------------------------------------------------------------------------------------- +// Vertex struct holding position, normal vector, color, and texture mapping information. +const D3D12_INPUT_ELEMENT_DESC VertexPositionNormalColorTexture::InputElements[] = +{ + { "SV_Position", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + { "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, +}; + +static_assert(sizeof(VertexPositionNormalColorTexture) == 48, "Vertex struct/layout mismatch"); + +const D3D12_INPUT_LAYOUT_DESC VertexPositionNormalColorTexture::InputLayout = +{ + VertexPositionNormalColorTexture::InputElements, + VertexPositionNormalColorTexture::InputElementCount +}; + +//-------------------------------------------------------------------------------------- +// VertexPositionNormalTangentColorTexture, VertexPositionNormalTangentColorTextureSkinning are not +// supported for DirectX 12 since they were only present for DGSL diff --git a/Common/DirectXTK12/Src/WICTextureLoader.cpp b/Common/DirectXTK12/Src/WICTextureLoader.cpp new file mode 100644 index 0000000..36089a7 --- /dev/null +++ b/Common/DirectXTK12/Src/WICTextureLoader.cpp @@ -0,0 +1,1040 @@ +//-------------------------------------------------------------------------------------- +// File: WICTextureLoader.cpp +// +// Function for loading a WIC image and creating a Direct3D runtime texture for it +// (auto-generating mipmaps if possible) +// +// Note: Assumes application has already called CoInitializeEx +// +// Note these functions are useful for images created as simple 2D textures. For +// more complex resources, DDSTextureLoader is an excellent light-weight runtime loader. +// For a full-featured DDS file reader, writer, and texture processing pipeline see +// the 'Texconv' sample and the 'DirectXTex' library. +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +// We could load multi-frame images (TIFF/GIF) into a texture array. +// For now, we just load the first frame (note: DirectXTex supports multi-frame images) + +#include "pch.h" + +#include "WICTextureLoader.h" + +#include "DirectXHelpers.h" +#include "PlatformHelpers.h" +#include "LoaderHelpers.h" +#include "ResourceUploadBatch.h" + +using namespace DirectX; +using Microsoft::WRL::ComPtr; + +namespace +{ + //------------------------------------------------------------------------------------- + // WIC Pixel Format Translation Data + //------------------------------------------------------------------------------------- + struct WICTranslate + { + const GUID& wic; + DXGI_FORMAT format; + }; + + constexpr WICTranslate g_WICFormats[] = + { + { GUID_WICPixelFormat128bppRGBAFloat, DXGI_FORMAT_R32G32B32A32_FLOAT }, + + { GUID_WICPixelFormat64bppRGBAHalf, DXGI_FORMAT_R16G16B16A16_FLOAT }, + { GUID_WICPixelFormat64bppRGBA, DXGI_FORMAT_R16G16B16A16_UNORM }, + + { GUID_WICPixelFormat32bppRGBA, DXGI_FORMAT_R8G8B8A8_UNORM }, + { GUID_WICPixelFormat32bppBGRA, DXGI_FORMAT_B8G8R8A8_UNORM }, + { GUID_WICPixelFormat32bppBGR, DXGI_FORMAT_B8G8R8X8_UNORM }, + + { GUID_WICPixelFormat32bppRGBA1010102XR, DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM }, + { GUID_WICPixelFormat32bppRGBA1010102, DXGI_FORMAT_R10G10B10A2_UNORM }, + + { GUID_WICPixelFormat16bppBGRA5551, DXGI_FORMAT_B5G5R5A1_UNORM }, + { GUID_WICPixelFormat16bppBGR565, DXGI_FORMAT_B5G6R5_UNORM }, + + { GUID_WICPixelFormat32bppGrayFloat, DXGI_FORMAT_R32_FLOAT }, + { GUID_WICPixelFormat16bppGrayHalf, DXGI_FORMAT_R16_FLOAT }, + { GUID_WICPixelFormat16bppGray, DXGI_FORMAT_R16_UNORM }, + { GUID_WICPixelFormat8bppGray, DXGI_FORMAT_R8_UNORM }, + + { GUID_WICPixelFormat8bppAlpha, DXGI_FORMAT_A8_UNORM }, + + { GUID_WICPixelFormat96bppRGBFloat, DXGI_FORMAT_R32G32B32_FLOAT }, + }; + + //------------------------------------------------------------------------------------- + // WIC Pixel Format nearest conversion table + //------------------------------------------------------------------------------------- + + struct WICConvert + { + const GUID& source; + const GUID& target; + }; + + constexpr WICConvert g_WICConvert[] = + { + // Note target GUID in this conversion table must be one of those directly supported formats (above). + + { GUID_WICPixelFormatBlackWhite, GUID_WICPixelFormat8bppGray }, // DXGI_FORMAT_R8_UNORM + + { GUID_WICPixelFormat1bppIndexed, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat2bppIndexed, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat4bppIndexed, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat8bppIndexed, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + + { GUID_WICPixelFormat2bppGray, GUID_WICPixelFormat8bppGray }, // DXGI_FORMAT_R8_UNORM + { GUID_WICPixelFormat4bppGray, GUID_WICPixelFormat8bppGray }, // DXGI_FORMAT_R8_UNORM + + { GUID_WICPixelFormat16bppGrayFixedPoint, GUID_WICPixelFormat16bppGrayHalf }, // DXGI_FORMAT_R16_FLOAT + { GUID_WICPixelFormat32bppGrayFixedPoint, GUID_WICPixelFormat32bppGrayFloat }, // DXGI_FORMAT_R32_FLOAT + + { GUID_WICPixelFormat16bppBGR555, GUID_WICPixelFormat16bppBGRA5551 }, // DXGI_FORMAT_B5G5R5A1_UNORM + + { GUID_WICPixelFormat32bppBGR101010, GUID_WICPixelFormat32bppRGBA1010102 }, // DXGI_FORMAT_R10G10B10A2_UNORM + + { GUID_WICPixelFormat24bppBGR, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat24bppRGB, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat32bppPBGRA, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat32bppPRGBA, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + + { GUID_WICPixelFormat48bppRGB, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM + { GUID_WICPixelFormat48bppBGR, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM + { GUID_WICPixelFormat64bppBGRA, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM + { GUID_WICPixelFormat64bppPRGBA, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM + { GUID_WICPixelFormat64bppPBGRA, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM + + { GUID_WICPixelFormat48bppRGBFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + { GUID_WICPixelFormat48bppBGRFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + { GUID_WICPixelFormat64bppRGBAFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + { GUID_WICPixelFormat64bppBGRAFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + { GUID_WICPixelFormat64bppRGBFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + { GUID_WICPixelFormat64bppRGBHalf, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + { GUID_WICPixelFormat48bppRGBHalf, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + + { GUID_WICPixelFormat128bppPRGBAFloat, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT + { GUID_WICPixelFormat128bppRGBFloat, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT + { GUID_WICPixelFormat128bppRGBAFixedPoint, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT + { GUID_WICPixelFormat128bppRGBFixedPoint, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT + { GUID_WICPixelFormat32bppRGBE, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT + + { GUID_WICPixelFormat32bppCMYK, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat64bppCMYK, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM + { GUID_WICPixelFormat40bppCMYKAlpha, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat80bppCMYKAlpha, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM + + { GUID_WICPixelFormat32bppRGB, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat64bppRGB, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM + { GUID_WICPixelFormat64bppPRGBAHalf, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + + { GUID_WICPixelFormat96bppRGBFixedPoint, GUID_WICPixelFormat96bppRGBFloat }, // DXGI_FORMAT_R32G32B32_FLOAT + + // We don't support n-channel formats + }; + + BOOL WINAPI InitializeWICFactory(PINIT_ONCE, PVOID, PVOID *ifactory) noexcept + { + return SUCCEEDED(CoCreateInstance( + CLSID_WICImagingFactory2, + nullptr, + CLSCTX_INPROC_SERVER, + __uuidof(IWICImagingFactory2), + ifactory)) ? TRUE : FALSE; + } +} + +//-------------------------------------------------------------------------------------- +namespace DirectX +{ + inline namespace DX12 + { + namespace Internal + { + IWICImagingFactory2* GetWIC() noexcept; + // Also used by ScreenGrab + } + } +} + +IWICImagingFactory2* DirectX::DX12::Internal::GetWIC() noexcept +{ + static INIT_ONCE s_initOnce = INIT_ONCE_STATIC_INIT; + + IWICImagingFactory2* factory = nullptr; + if (!InitOnceExecuteOnce( + &s_initOnce, + InitializeWICFactory, + nullptr, + reinterpret_cast(&factory))) + { + return nullptr; + } + + return factory; +} + +using namespace DirectX::DX12::Internal; + +namespace +{ + //--------------------------------------------------------------------------------- + DXGI_FORMAT WICToDXGI(const GUID& guid) noexcept + { + for (size_t i = 0; i < std::size(g_WICFormats); ++i) + { + if (memcmp(&g_WICFormats[i].wic, &guid, sizeof(GUID)) == 0) + return g_WICFormats[i].format; + } + + return DXGI_FORMAT_UNKNOWN; + } + + //--------------------------------------------------------------------------------- + size_t WICBitsPerPixel(REFGUID targetGuid) noexcept + { + auto pWIC = GetWIC(); + if (!pWIC) + return 0; + + ComPtr cinfo; + if (FAILED(pWIC->CreateComponentInfo(targetGuid, cinfo.GetAddressOf()))) + return 0; + + WICComponentType type; + if (FAILED(cinfo->GetComponentType(&type))) + return 0; + + if (type != WICPixelFormat) + return 0; + + ComPtr pfinfo; + if (FAILED(cinfo.As(&pfinfo))) + return 0; + + UINT bpp; + if (FAILED(pfinfo->GetBitsPerPixel(&bpp))) + return 0; + + return bpp; + } + + //--------------------------------------------------------------------------------- + HRESULT CreateTextureFromWIC(_In_ ID3D12Device* d3dDevice, + _In_ IWICBitmapFrameDecode *frame, + size_t maxsize, + D3D12_RESOURCE_FLAGS resFlags, + WIC_LOADER_FLAGS loadFlags, + _Outptr_ ID3D12Resource** texture, + std::unique_ptr& decodedData, + D3D12_SUBRESOURCE_DATA& subresource) noexcept + { + UINT width, height; + HRESULT hr = frame->GetSize(&width, &height); + if (FAILED(hr)) + return hr; + + assert(width > 0 && height > 0); + + if (maxsize > UINT32_MAX) + return E_INVALIDARG; + + if (!maxsize) + { + maxsize = size_t(D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION); + } + + UINT twidth = width; + UINT theight = height; + if (loadFlags & WIC_LOADER_FIT_POW2) + { + LoaderHelpers::FitPowerOf2(width, height, twidth, theight, maxsize); + } + else if (width > maxsize || height > maxsize) + { + const float ar = static_cast(height) / static_cast(width); + if (width > height) + { + twidth = static_cast(maxsize); + theight = std::max(1, static_cast(static_cast(maxsize) * ar)); + } + else + { + theight = static_cast(maxsize); + twidth = std::max(1, static_cast(static_cast(maxsize) / ar)); + } + assert(twidth <= maxsize && theight <= maxsize); + } + + if (loadFlags & WIC_LOADER_MAKE_SQUARE) + { + twidth = std::max(twidth, theight); + theight = twidth; + } + + // Determine format + WICPixelFormatGUID pixelFormat; + hr = frame->GetPixelFormat(&pixelFormat); + if (FAILED(hr)) + return hr; + + WICPixelFormatGUID convertGUID; + memcpy_s(&convertGUID, sizeof(WICPixelFormatGUID), &pixelFormat, sizeof(GUID)); + + size_t bpp = 0; + + DXGI_FORMAT format = WICToDXGI(pixelFormat); + if (format == DXGI_FORMAT_UNKNOWN) + { + for (size_t i = 0; i < std::size(g_WICConvert); ++i) + { + if (memcmp(&g_WICConvert[i].source, &pixelFormat, sizeof(WICPixelFormatGUID)) == 0) + { + memcpy_s(&convertGUID, sizeof(WICPixelFormatGUID), &g_WICConvert[i].target, sizeof(GUID)); + + format = WICToDXGI(g_WICConvert[i].target); + assert(format != DXGI_FORMAT_UNKNOWN); + bpp = WICBitsPerPixel(convertGUID); + break; + } + } + + if (format == DXGI_FORMAT_UNKNOWN) + { + DebugTrace("ERROR: WICTextureLoader does not support all DXGI formats (WIC GUID {%8.8lX-%4.4X-%4.4X-%2.2X%2.2X-%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X}). Consider using DirectXTex.\n", + pixelFormat.Data1, pixelFormat.Data2, pixelFormat.Data3, + pixelFormat.Data4[0], pixelFormat.Data4[1], pixelFormat.Data4[2], pixelFormat.Data4[3], + pixelFormat.Data4[4], pixelFormat.Data4[5], pixelFormat.Data4[6], pixelFormat.Data4[7]); + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + } + else + { + bpp = WICBitsPerPixel(pixelFormat); + } + + if (loadFlags & WIC_LOADER_FORCE_RGBA32) + { + memcpy_s(&convertGUID, sizeof(WICPixelFormatGUID), &GUID_WICPixelFormat32bppRGBA, sizeof(GUID)); + format = DXGI_FORMAT_R8G8B8A8_UNORM; + bpp = 32; + } + + if (!bpp) + return E_FAIL; + + // Handle sRGB formats + if (loadFlags & WIC_LOADER_FORCE_SRGB) + { + format = LoaderHelpers::MakeSRGB(format); + } + else if (!(loadFlags & WIC_LOADER_IGNORE_SRGB)) + { + ComPtr metareader; + if (SUCCEEDED(frame->GetMetadataQueryReader(metareader.GetAddressOf()))) + { + GUID containerFormat; + if (SUCCEEDED(metareader->GetContainerFormat(&containerFormat))) + { + bool sRGB = false; + + PROPVARIANT value; + PropVariantInit(&value); + + // Check for colorspace chunks + if (memcmp(&containerFormat, &GUID_ContainerFormatPng, sizeof(GUID)) == 0) + { + // Check for sRGB chunk + if (SUCCEEDED(metareader->GetMetadataByName(L"/sRGB/RenderingIntent", &value)) && value.vt == VT_UI1) + { + sRGB = true; + } + else if (SUCCEEDED(metareader->GetMetadataByName(L"/gAMA/ImageGamma", &value)) && value.vt == VT_UI4) + { + sRGB = (value.uintVal == 45455); + } + else + { + sRGB = (loadFlags & WIC_LOADER_SRGB_DEFAULT) != 0; + } + } + #if (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) + else if (memcmp(&containerFormat, &GUID_ContainerFormatJpeg, sizeof(GUID)) == 0) + { + if (SUCCEEDED(metareader->GetMetadataByName(L"/app1/ifd/exif/{ushort=40961}", &value)) && value.vt == VT_UI2) + { + sRGB = (value.uiVal == 1); + } + else + { + sRGB = (loadFlags & WIC_LOADER_SRGB_DEFAULT) != 0; + } + } + else if (memcmp(&containerFormat, &GUID_ContainerFormatTiff, sizeof(GUID)) == 0) + { + if (SUCCEEDED(metareader->GetMetadataByName(L"/ifd/exif/{ushort=40961}", &value)) && value.vt == VT_UI2) + { + sRGB = (value.uiVal == 1); + } + else + { + sRGB = (loadFlags & WIC_LOADER_SRGB_DEFAULT) != 0; + } + } + #else + else if (SUCCEEDED(metareader->GetMetadataByName(L"System.Image.ColorSpace", &value)) && value.vt == VT_UI2) + { + sRGB = (value.uiVal == 1); + } + else + { + sRGB = (loadFlags & WIC_LOADER_SRGB_DEFAULT) != 0; + } + #endif + + std::ignore = PropVariantClear(&value); + + if (sRGB) + format = LoaderHelpers::MakeSRGB(format); + } + } + } + + // Allocate memory for decoded image + const uint64_t rowBytes = (uint64_t(twidth) * uint64_t(bpp) + 7u) / 8u; + const uint64_t numBytes = rowBytes * uint64_t(theight); + + if (rowBytes > UINT32_MAX || numBytes > UINT32_MAX) + return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); + + auto const rowPitch = static_cast(rowBytes); + auto const imageSize = static_cast(numBytes); + + decodedData.reset(new (std::nothrow) uint8_t[imageSize]); + if (!decodedData) + return E_OUTOFMEMORY; + + // Load image data + if (memcmp(&convertGUID, &pixelFormat, sizeof(GUID)) == 0 + && twidth == width + && theight == height) + { + // No format conversion or resize needed + hr = frame->CopyPixels(nullptr, static_cast(rowPitch), static_cast(imageSize), decodedData.get()); + if (FAILED(hr)) + return hr; + } + else if (twidth != width || theight != height) + { + // Resize + auto pWIC = GetWIC(); + if (!pWIC) + return E_NOINTERFACE; + + ComPtr scaler; + hr = pWIC->CreateBitmapScaler(scaler.GetAddressOf()); + if (FAILED(hr)) + return hr; + + hr = scaler->Initialize(frame, twidth, theight, WICBitmapInterpolationModeFant); + if (FAILED(hr)) + return hr; + + WICPixelFormatGUID pfScaler; + hr = scaler->GetPixelFormat(&pfScaler); + if (FAILED(hr)) + return hr; + + if (memcmp(&convertGUID, &pfScaler, sizeof(GUID)) == 0) + { + // No format conversion needed + hr = scaler->CopyPixels(nullptr, static_cast(rowPitch), static_cast(imageSize), decodedData.get()); + if (FAILED(hr)) + return hr; + } + else + { + ComPtr FC; + hr = pWIC->CreateFormatConverter(FC.GetAddressOf()); + if (FAILED(hr)) + return hr; + + BOOL canConvert = FALSE; + hr = FC->CanConvert(pfScaler, convertGUID, &canConvert); + if (FAILED(hr) || !canConvert) + { + return E_UNEXPECTED; + } + + hr = FC->Initialize(scaler.Get(), convertGUID, WICBitmapDitherTypeErrorDiffusion, nullptr, 0, WICBitmapPaletteTypeMedianCut); + if (FAILED(hr)) + return hr; + + hr = FC->CopyPixels(nullptr, static_cast(rowPitch), static_cast(imageSize), decodedData.get()); + if (FAILED(hr)) + return hr; + } + } + else + { + // Format conversion but no resize + auto pWIC = GetWIC(); + if (!pWIC) + return E_NOINTERFACE; + + ComPtr FC; + hr = pWIC->CreateFormatConverter(FC.GetAddressOf()); + if (FAILED(hr)) + return hr; + + BOOL canConvert = FALSE; + hr = FC->CanConvert(pixelFormat, convertGUID, &canConvert); + if (FAILED(hr) || !canConvert) + { + return E_UNEXPECTED; + } + + hr = FC->Initialize(frame, convertGUID, WICBitmapDitherTypeErrorDiffusion, nullptr, 0, WICBitmapPaletteTypeMedianCut); + if (FAILED(hr)) + return hr; + + hr = FC->CopyPixels(nullptr, static_cast(rowPitch), static_cast(imageSize), decodedData.get()); + if (FAILED(hr)) + return hr; + } + + // Count the number of mips + const uint32_t mipCount = (loadFlags & (WIC_LOADER_MIP_AUTOGEN | WIC_LOADER_MIP_RESERVE)) + ? LoaderHelpers::CountMips(twidth, theight) : 1u; + + // Create texture + D3D12_RESOURCE_DESC desc = {}; + desc.Width = twidth; + desc.Height = theight; + desc.MipLevels = static_cast(mipCount); + desc.DepthOrArraySize = 1; + desc.Format = format; + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + desc.Flags = resFlags; + desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; + + const CD3DX12_HEAP_PROPERTIES defaultHeapProperties(D3D12_HEAP_TYPE_DEFAULT); + + ID3D12Resource* tex = nullptr; + hr = d3dDevice->CreateCommittedResource( + &defaultHeapProperties, + D3D12_HEAP_FLAG_NONE, + &desc, + c_initialCopyTargetState, + nullptr, + IID_GRAPHICS_PPV_ARGS(&tex)); + + if (FAILED(hr)) + { + return hr; + } + + _Analysis_assume_(tex != nullptr); + + subresource.pData = decodedData.get(); + subresource.RowPitch = static_cast(rowPitch); + subresource.SlicePitch = static_cast(imageSize); + + *texture = tex; + return hr; + } + + //-------------------------------------------------------------------------------------- + void SetDebugTextureInfo( + _In_z_ const wchar_t* fileName, + _In_ ID3D12Resource* texture) noexcept + { + #if !defined(NO_D3D12_DEBUG_NAME) && ( defined(_DEBUG) || defined(PROFILE) ) + const wchar_t* pstrName = wcsrchr(fileName, '\\'); + if (!pstrName) + { + pstrName = fileName; + } + else + { + pstrName++; + } + texture->SetName(pstrName); + #else + UNREFERENCED_PARAMETER(fileName); + UNREFERENCED_PARAMETER(texture); + #endif + } + + //-------------------------------------------------------------------------------------- + DXGI_FORMAT GetPixelFormat(_In_ IWICBitmapFrameDecode* frame) noexcept + { + WICPixelFormatGUID pixelFormat; + if (FAILED(frame->GetPixelFormat(&pixelFormat))) + return DXGI_FORMAT_UNKNOWN; + + const DXGI_FORMAT format = WICToDXGI(pixelFormat); + if (format == DXGI_FORMAT_UNKNOWN) + { + for (size_t i = 0; i < std::size(g_WICConvert); ++i) + { + if (memcmp(&g_WICConvert[i].source, &pixelFormat, sizeof(WICPixelFormatGUID)) == 0) + { + return WICToDXGI(g_WICConvert[i].target); + } + } + } + + return format; + } +} // anonymous namespace + + +//-------------------------------------------------------------------------------------- +_Use_decl_annotations_ +HRESULT DirectX::LoadWICTextureFromMemory( + ID3D12Device* d3dDevice, + const uint8_t* wicData, + size_t wicDataSize, + ID3D12Resource** texture, + std::unique_ptr& decodedData, + D3D12_SUBRESOURCE_DATA& subresource, + size_t maxsize) noexcept +{ + return LoadWICTextureFromMemoryEx( + d3dDevice, + wicData, + wicDataSize, + maxsize, + D3D12_RESOURCE_FLAG_NONE, + WIC_LOADER_DEFAULT, + texture, + decodedData, + subresource); +} + +_Use_decl_annotations_ +HRESULT DirectX::CreateWICTextureFromMemory( + ID3D12Device* d3dDevice, + ResourceUploadBatch& resourceUpload, + const uint8_t* wicData, + size_t wicDataSize, + ID3D12Resource** texture, + bool generateMips, + size_t maxsize) +{ + return CreateWICTextureFromMemoryEx( + d3dDevice, + resourceUpload, + wicData, + wicDataSize, + maxsize, + D3D12_RESOURCE_FLAG_NONE, + (generateMips) ? WIC_LOADER_MIP_AUTOGEN : WIC_LOADER_DEFAULT, + texture); +} + + +//-------------------------------------------------------------------------------------- +_Use_decl_annotations_ +HRESULT DirectX::LoadWICTextureFromMemoryEx( + ID3D12Device* d3dDevice, + const uint8_t* wicData, + size_t wicDataSize, + size_t maxsize, + D3D12_RESOURCE_FLAGS resFlags, + WIC_LOADER_FLAGS loadFlags, + ID3D12Resource** texture, + std::unique_ptr& decodedData, + D3D12_SUBRESOURCE_DATA& subresource) noexcept +{ + if (texture) + { + *texture = nullptr; + } + + if (!d3dDevice || !wicData || !texture) + return E_INVALIDARG; + + if (!wicDataSize) + return E_FAIL; + + if (wicDataSize > UINT32_MAX) + return HRESULT_FROM_WIN32(ERROR_FILE_TOO_LARGE); + + auto pWIC = GetWIC(); + if (!pWIC) + return E_NOINTERFACE; + + // Create input stream for memory + ComPtr stream; + HRESULT hr = pWIC->CreateStream(stream.GetAddressOf()); + if (FAILED(hr)) + return hr; + + hr = stream->InitializeFromMemory(const_cast(wicData), static_cast(wicDataSize)); + if (FAILED(hr)) + return hr; + + // Initialize WIC + ComPtr decoder; + hr = pWIC->CreateDecoderFromStream(stream.Get(), nullptr, WICDecodeMetadataCacheOnDemand, decoder.GetAddressOf()); + if (FAILED(hr)) + return hr; + + ComPtr frame; + hr = decoder->GetFrame(0, frame.GetAddressOf()); + if (FAILED(hr)) + return hr; + + hr = CreateTextureFromWIC(d3dDevice, + frame.Get(), maxsize, + resFlags, loadFlags, + texture, decodedData, subresource); + if (FAILED(hr)) + return hr; + + _Analysis_assume_(*texture != nullptr); + SetDebugObjectName(*texture, L"WICTextureLoader"); + + return hr; +} + +_Use_decl_annotations_ +HRESULT DirectX::CreateWICTextureFromMemoryEx( + ID3D12Device* d3dDevice, + ResourceUploadBatch& resourceUpload, + const uint8_t* wicData, + size_t wicDataSize, + size_t maxsize, + D3D12_RESOURCE_FLAGS resFlags, + WIC_LOADER_FLAGS loadFlags, + ID3D12Resource** texture) +{ + if (texture) + { + *texture = nullptr; + } + + if (!d3dDevice || !wicData || !texture) + return E_INVALIDARG; + + if (!wicDataSize) + return E_FAIL; + + if (wicDataSize > UINT32_MAX) + return HRESULT_FROM_WIN32(ERROR_FILE_TOO_LARGE); + + auto pWIC = GetWIC(); + if (!pWIC) + return E_NOINTERFACE; + + // Create input stream for memory + ComPtr stream; + HRESULT hr = pWIC->CreateStream(stream.GetAddressOf()); + if (FAILED(hr)) + return hr; + + hr = stream->InitializeFromMemory(const_cast(wicData), static_cast(wicDataSize)); + if (FAILED(hr)) + return hr; + + // Initialize WIC + ComPtr decoder; + hr = pWIC->CreateDecoderFromStream(stream.Get(), nullptr, WICDecodeMetadataCacheOnDemand, decoder.GetAddressOf()); + if (FAILED(hr)) + return hr; + + ComPtr frame; + hr = decoder->GetFrame(0, frame.GetAddressOf()); + if (FAILED(hr)) + return hr; + + if ((loadFlags & (WIC_LOADER_MIP_AUTOGEN | WIC_LOADER_FORCE_RGBA32)) == WIC_LOADER_MIP_AUTOGEN) + { + const DXGI_FORMAT fmt = GetPixelFormat(frame.Get()); + if (!resourceUpload.IsSupportedForGenerateMips(fmt)) + { + DebugTrace("WARNING: Autogen of mips ignored (device doesn't support this format (%d) or trying to use a copy queue)\n", static_cast(fmt)); + loadFlags &= ~WIC_LOADER_MIP_AUTOGEN; + } + } + + std::unique_ptr decodedData; + D3D12_SUBRESOURCE_DATA initData; + hr = CreateTextureFromWIC(d3dDevice, + frame.Get(), maxsize, + resFlags, loadFlags, + texture, decodedData, initData); + + if (SUCCEEDED(hr)) + { + assert(texture != nullptr && *texture != nullptr); + _Analysis_assume_(texture != nullptr && *texture != nullptr); + SetDebugObjectName(*texture, L"WICTextureLoader"); + + resourceUpload.Upload( + *texture, + 0, + &initData, + 1); + + resourceUpload.Transition( + *texture, + D3D12_RESOURCE_STATE_COPY_DEST, + D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); + + // Generate mips? + if (loadFlags & WIC_LOADER_MIP_AUTOGEN) + { + resourceUpload.GenerateMips(*texture); + } + } + + return hr; +} + + +//-------------------------------------------------------------------------------------- +_Use_decl_annotations_ +HRESULT DirectX::LoadWICTextureFromFile( + ID3D12Device* d3dDevice, + const wchar_t* fileName, + ID3D12Resource** texture, + std::unique_ptr& wicData, + D3D12_SUBRESOURCE_DATA& subresource, + size_t maxsize) noexcept +{ + return LoadWICTextureFromFileEx( + d3dDevice, + fileName, + maxsize, + D3D12_RESOURCE_FLAG_NONE, + WIC_LOADER_DEFAULT, + texture, + wicData, + subresource); +} + +_Use_decl_annotations_ +HRESULT DirectX::CreateWICTextureFromFile( + ID3D12Device* d3dDevice, + ResourceUploadBatch& resourceUpload, + const wchar_t* fileName, + ID3D12Resource** texture, + bool generateMips, + size_t maxsize) +{ + return CreateWICTextureFromFileEx( + d3dDevice, + resourceUpload, + fileName, + maxsize, + D3D12_RESOURCE_FLAG_NONE, + generateMips ? WIC_LOADER_MIP_AUTOGEN : WIC_LOADER_DEFAULT, + texture); +} + + +//-------------------------------------------------------------------------------------- +_Use_decl_annotations_ +HRESULT DirectX::LoadWICTextureFromFileEx( + ID3D12Device* d3dDevice, + const wchar_t* fileName, + size_t maxsize, + D3D12_RESOURCE_FLAGS resFlags, + WIC_LOADER_FLAGS loadFlags, + ID3D12Resource** texture, + std::unique_ptr& decodedData, + D3D12_SUBRESOURCE_DATA& subresource) noexcept +{ + if (texture) + { + *texture = nullptr; + } + + if (!d3dDevice || !fileName || !texture) + return E_INVALIDARG; + + auto pWIC = GetWIC(); + if (!pWIC) + return E_NOINTERFACE; + + // Initialize WIC + ComPtr decoder; + HRESULT hr = pWIC->CreateDecoderFromFilename(fileName, + nullptr, + GENERIC_READ, + WICDecodeMetadataCacheOnDemand, + decoder.GetAddressOf()); + if (FAILED(hr)) + return hr; + + ComPtr frame; + hr = decoder->GetFrame(0, frame.GetAddressOf()); + if (FAILED(hr)) + return hr; + + hr = CreateTextureFromWIC(d3dDevice, frame.Get(), maxsize, + resFlags, loadFlags, + texture, decodedData, subresource); + + if (SUCCEEDED(hr)) + { + SetDebugTextureInfo(fileName, *texture); + } + + return hr; +} + +_Use_decl_annotations_ +HRESULT DirectX::CreateWICTextureFromFileEx( + ID3D12Device* d3dDevice, + ResourceUploadBatch& resourceUpload, + const wchar_t* fileName, + size_t maxsize, + D3D12_RESOURCE_FLAGS resFlags, + WIC_LOADER_FLAGS loadFlags, + ID3D12Resource** texture) +{ + if (texture) + { + *texture = nullptr; + } + + if (!d3dDevice || !fileName || !texture) + return E_INVALIDARG; + + auto pWIC = GetWIC(); + if (!pWIC) + return E_NOINTERFACE; + + // Initialize WIC + ComPtr decoder; + HRESULT hr = pWIC->CreateDecoderFromFilename(fileName, + nullptr, + GENERIC_READ, + WICDecodeMetadataCacheOnDemand, + decoder.GetAddressOf()); + if (FAILED(hr)) + return hr; + + ComPtr frame; + hr = decoder->GetFrame(0, frame.GetAddressOf()); + if (FAILED(hr)) + return hr; + + if ((loadFlags & (WIC_LOADER_MIP_AUTOGEN | WIC_LOADER_FORCE_RGBA32)) == WIC_LOADER_MIP_AUTOGEN) + { + const DXGI_FORMAT fmt = GetPixelFormat(frame.Get()); + if (!resourceUpload.IsSupportedForGenerateMips(fmt)) + { + DebugTrace("WARNING: Autogen of mips ignored (device doesn't support this format (%d) or trying to use a copy queue)\n", static_cast(fmt)); + loadFlags &= ~WIC_LOADER_MIP_AUTOGEN; + } + } + + std::unique_ptr decodedData; + D3D12_SUBRESOURCE_DATA initData; + hr = CreateTextureFromWIC(d3dDevice, frame.Get(), maxsize, + resFlags, loadFlags, + texture, decodedData, initData); + + if (SUCCEEDED(hr)) + { + SetDebugTextureInfo(fileName, *texture); + + resourceUpload.Upload( + *texture, + 0, + &initData, + 1); + + resourceUpload.Transition( + *texture, + D3D12_RESOURCE_STATE_COPY_DEST, + D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); + + // Generate mips? + if (loadFlags & WIC_LOADER_MIP_AUTOGEN) + { + resourceUpload.GenerateMips(*texture); + } + } + + return hr; +} + + +//-------------------------------------------------------------------------------------- +// Adapters for /Zc:wchar_t- clients + +#if defined(_MSC_VER) && !defined(_NATIVE_WCHAR_T_DEFINED) + +namespace DirectX +{ + HRESULT __cdecl LoadWICTextureFromFile( + _In_ ID3D12Device* d3dDevice, + _In_z_ const __wchar_t* szFileName, + _Outptr_ ID3D12Resource** texture, + std::unique_ptr& decodedData, + D3D12_SUBRESOURCE_DATA& subresource, + size_t maxsize) noexcept + { + return LoadWICTextureFromFile(d3dDevice, + reinterpret_cast(szFileName), + texture, decodedData, subresource, maxsize); + } + + HRESULT __cdecl CreateWICTextureFromFile( + _In_ ID3D12Device* d3dDevice, + ResourceUploadBatch& resourceUpload, + _In_z_ const __wchar_t* szFileName, + _Outptr_ ID3D12Resource** texture, + bool generateMips, + size_t maxsize) + { + return CreateWICTextureFromFile(d3dDevice, resourceUpload, + reinterpret_cast(szFileName), + texture, generateMips, maxsize); + } + + HRESULT __cdecl LoadWICTextureFromFileEx( + _In_ ID3D12Device* d3dDevice, + _In_z_ const __wchar_t* szFileName, + size_t maxsize, + D3D12_RESOURCE_FLAGS resFlags, + WIC_LOADER_FLAGS loadFlags, + _Outptr_ ID3D12Resource** texture, + std::unique_ptr& decodedData, + D3D12_SUBRESOURCE_DATA& subresource) noexcept + { + return LoadWICTextureFromFileEx(d3dDevice, + reinterpret_cast(szFileName), + maxsize, resFlags, loadFlags, texture, decodedData, subresource); + } + + HRESULT __cdecl CreateWICTextureFromFileEx( + _In_ ID3D12Device* d3dDevice, + ResourceUploadBatch& resourceUpload, + _In_z_ const __wchar_t* szFileName, + size_t maxsize, + D3D12_RESOURCE_FLAGS resFlags, + WIC_LOADER_FLAGS loadFlags, + _Outptr_ ID3D12Resource** texture) + { + return CreateWICTextureFromFileEx(d3dDevice, resourceUpload, + reinterpret_cast(szFileName), + maxsize, resFlags, loadFlags, texture); + } +} + +#endif // !_NATIVE_WCHAR_T_DEFINED diff --git a/Common/DirectXTK12/Src/XboxDDSTextureLoader.cpp b/Common/DirectXTK12/Src/XboxDDSTextureLoader.cpp new file mode 100644 index 0000000..a2551bb --- /dev/null +++ b/Common/DirectXTK12/Src/XboxDDSTextureLoader.cpp @@ -0,0 +1,650 @@ +//-------------------------------------------------------------------------------------- +// File: XboxDDSTextureLoader.cpp +// +// Functions for loading a DDS texture using the XBOX extended header and creating a +// Direct3D12.X runtime resource for it via the CreatePlacedResourceX API +// +// Note these functions will not load standard DDS files. Use the DDSTextureLoader +// module in the DirectXTex package or as part of the DirectXTK library to load +// these files which use standard Direct3D resource creation APIs. +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "pch.h" + +#include "XboxDDSTextureLoader.h" + +#include "PlatformHelpers.h" +#include "DDS.h" +#include "DirectXHelpers.h" + +#ifdef _GAMING_XBOX +#include +#endif + +using namespace DirectX; +using namespace Xbox; + +namespace +{ + //-------------------------------------------------------------------------------------- + // Default XMemAlloc attributes for texture loading + //-------------------------------------------------------------------------------------- + const uint64_t c_XMemAllocAttributes = MAKE_XALLOC_ATTRIBUTES( + eXALLOCAllocatorId_MiddlewareReservedMin, + 0, + XALLOC_MEMTYPE_GRAPHICS_WRITECOMBINE_GPU_READONLY, + XALLOC_PAGESIZE_64KB, + XALLOC_ALIGNMENT_64K +#ifdef _GAMING_XBOX + , 0 +#endif + ); + + //-------------------------------------------------------------------------------------- + // DDS file structure definitions + // + // See DDS.h in the 'Texconv' sample and the 'DirectXTex' library + //-------------------------------------------------------------------------------------- + #pragma pack(push,1) + + struct DDS_HEADER_XBOX + // Must match structure defined in xtexconv tool + { + DXGI_FORMAT dxgiFormat; + uint32_t resourceDimension; + uint32_t miscFlag; // see DDS_RESOURCE_MISC_FLAG + uint32_t arraySize; + uint32_t miscFlags2; // see DDS_MISC_FLAGS2 + uint32_t tileMode; // see XG_TILE_MODE / XG_SWIZZLE_MODE + uint32_t baseAlignment; + uint32_t dataSize; + uint32_t xdkVer; // matching _XDK_VER / _GXDK_VER + }; + + constexpr uint32_t XBOX_TILEMODE_SCARLETT = 0x1000000; + + static_assert(sizeof(DDS_HEADER_XBOX) == 36, "DDS XBOX Header size mismatch"); + + #pragma pack(pop) + + //-------------------------------------------------------------------------------------- + HRESULT LoadTextureDataFromFile(_In_z_ const wchar_t* fileName, + std::unique_ptr& ddsData, + DDS_HEADER** header, + uint8_t** bitData, + size_t* bitSize) noexcept + { + if (!header || !bitData || !bitSize) + { + return E_POINTER; + } + + // open the file + ScopedHandle hFile(safe_handle(CreateFile2( + fileName, + GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, + nullptr))); + + if (!hFile) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + // Get the file size + FILE_STANDARD_INFO fileInfo; + if (!GetFileInformationByHandleEx(hFile.get(), FileStandardInfo, &fileInfo, sizeof(fileInfo))) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + // File is too big for 32-bit allocation, so reject read + if (fileInfo.EndOfFile.HighPart > 0) + { + return E_FAIL; + } + + // Need at least enough data to fill the header and magic number to be a valid DDS + if (fileInfo.EndOfFile.LowPart < (sizeof(DDS_HEADER) + sizeof(uint32_t))) + { + return E_FAIL; + } + + // create enough space for the file data + ddsData.reset(new (std::nothrow) uint8_t[fileInfo.EndOfFile.LowPart]); + if (!ddsData) + { + return E_OUTOFMEMORY; + } + + // read the data in + DWORD BytesRead = 0; + if (!ReadFile(hFile.get(), + ddsData.get(), + fileInfo.EndOfFile.LowPart, + &BytesRead, + nullptr + )) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + if (BytesRead < fileInfo.EndOfFile.LowPart) + { + return E_FAIL; + } + + // DDS files always start with the same magic number ("DDS ") + auto dwMagicNumber = *reinterpret_cast(ddsData.get()); + if (dwMagicNumber != DDS_MAGIC) + { + return E_FAIL; + } + + auto hdr = reinterpret_cast(ddsData.get() + sizeof(uint32_t)); + + // Verify header to validate DDS file + if (hdr->size != sizeof(DDS_HEADER) || + hdr->ddspf.size != sizeof(DDS_PIXELFORMAT)) + { + return E_FAIL; + } + + // Check for XBOX extension + if (!(hdr->ddspf.flags & DDS_FOURCC) + || (MAKEFOURCC('X', 'B', 'O', 'X') != hdr->ddspf.fourCC)) + { + // Use standard DDSTextureLoader instead + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + + // Must be long enough for both headers and magic value + if (fileInfo.EndOfFile.LowPart < (sizeof(DDS_HEADER) + sizeof(uint32_t) + sizeof(DDS_HEADER_XBOX))) + { + return E_FAIL; + } + + // setup the pointers in the process request + *header = hdr; + auto offset = sizeof(uint32_t) + sizeof(DDS_HEADER) + sizeof(DDS_HEADER_XBOX); + *bitData = ddsData.get() + offset; + *bitSize = fileInfo.EndOfFile.LowPart - offset; + + return S_OK; + } + + //-------------------------------------------------------------------------------------- + DXGI_FORMAT MakeSRGB(_In_ DXGI_FORMAT format) noexcept + { + switch (format) + { + case DXGI_FORMAT_R8G8B8A8_UNORM: + return DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; + + case DXGI_FORMAT_BC1_UNORM: + return DXGI_FORMAT_BC1_UNORM_SRGB; + + case DXGI_FORMAT_BC2_UNORM: + return DXGI_FORMAT_BC2_UNORM_SRGB; + + case DXGI_FORMAT_BC3_UNORM: + return DXGI_FORMAT_BC3_UNORM_SRGB; + + case DXGI_FORMAT_B8G8R8A8_UNORM: + return DXGI_FORMAT_B8G8R8A8_UNORM_SRGB; + + case DXGI_FORMAT_B8G8R8X8_UNORM: + return DXGI_FORMAT_B8G8R8X8_UNORM_SRGB; + + case DXGI_FORMAT_BC7_UNORM: + return DXGI_FORMAT_BC7_UNORM_SRGB; + + default: + return format; + } + } + + //-------------------------------------------------------------------------------------- + HRESULT CreateD3DResources(_In_ ID3D12Device* d3dDevice, + _In_ const DDS_HEADER_XBOX* xboxext, + _In_ uint32_t width, + _In_ uint32_t height, + _In_ uint32_t depth, + _In_ uint32_t mipCount, + _In_ uint32_t arraySize, + _In_ bool forceSRGB, + _In_ void* grfxMemory, + _Outptr_ ID3D12Resource** texture) noexcept + { + if (!d3dDevice || !grfxMemory) + return E_POINTER; + + HRESULT hr = E_FAIL; + + DXGI_FORMAT format = xboxext->dxgiFormat; + if (forceSRGB) + { + format = MakeSRGB(format); + } + + D3D12_RESOURCE_DESC desc = {}; + desc.Width = static_cast(width); + desc.Height = static_cast(height); + desc.MipLevels = static_cast(mipCount); + desc.DepthOrArraySize = (xboxext->resourceDimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D) ? static_cast(depth) : static_cast(arraySize); + desc.Format = format; + desc.Flags = D3D12_RESOURCE_FLAG_NONE; + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + desc.Dimension = static_cast(xboxext->resourceDimension); + desc.Layout = static_cast((0x100 | xboxext->tileMode) & ~XBOX_TILEMODE_SCARLETT); + + hr = d3dDevice->CreatePlacedResourceX( + reinterpret_cast(grfxMemory), + &desc, + D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, + nullptr, + IID_GRAPHICS_PPV_ARGS(texture)); + if (SUCCEEDED(hr)) + { + _Analysis_assume_(*texture != nullptr); + SetDebugObjectName(*texture, L"XboxDDSTextureLoader"); + } + + return hr; + } + + //-------------------------------------------------------------------------------------- + HRESULT CreateTextureFromDDS(_In_ ID3D12Device* d3dDevice, + _In_ const DDS_HEADER* header, + _In_reads_bytes_(bitSize) const uint8_t* bitData, + _In_ size_t bitSize, + _In_ bool forceSRGB, + _Outptr_ ID3D12Resource** texture, + _Outptr_ void** grfxMemory, + _Out_opt_ bool* outIsCubeMap) noexcept + { + HRESULT hr = S_OK; + + uint32_t width = header->width; + uint32_t height = header->height; + uint32_t depth = header->depth; + + uint32_t mipCount = header->mipMapCount; + if (0 == mipCount) + { + mipCount = 1; + } + + if (!(header->ddspf.flags & DDS_FOURCC) + || (MAKEFOURCC('X', 'B', 'O', 'X') != header->ddspf.fourCC)) + { + // Use standard DDSTextureLoader instead + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + + auto xboxext = reinterpret_cast(reinterpret_cast(header) + sizeof(DDS_HEADER)); + +#if !defined(NDEBUG) && defined(_GXDK_VER) + if (xboxext->xdkVer < _GXDK_VER) + { + OutputDebugStringA("WARNING: DDS XBOX file may be outdated and need regeneration\n"); + } +#elif !defined(NDEBUG) && defined(_XDK_VER) + if (xboxext->xdkVer < _XDK_VER) + { + OutputDebugStringA("WARNING: DDS XBOX file may be outdated and need regeneration\n"); + } +#endif + + uint32_t arraySize = xboxext->arraySize; + if (arraySize == 0) + { + return HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + } + + bool isCubeMap = false; + + switch (xboxext->resourceDimension) + { + case D3D12_RESOURCE_DIMENSION_TEXTURE1D: + if ((header->flags & DDS_HEIGHT) && height != 1) + { + return HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + } + height = depth = 1; + break; + + case D3D12_RESOURCE_DIMENSION_TEXTURE2D: + if (xboxext->miscFlag & 0x4 /* RESOURCE_MISC_TEXTURECUBE */) + { + arraySize *= 6; + isCubeMap = true; + } + depth = 1; + break; + + case D3D12_RESOURCE_DIMENSION_TEXTURE3D: + if (!(header->flags & DDS_HEADER_FLAGS_VOLUME)) + { + return HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + } + + if (arraySize > 1) + { + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + break; + + default: + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + + if (xboxext->tileMode == uint32_t(-1)) + { + return HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + } +#if defined(_GAMING_XBOX_SCARLETT) + else if (!(xboxext->tileMode & XBOX_TILEMODE_SCARLETT)) + { + DebugTrace("ERROR: XboxDDSTextureLoader for Scarlett cannot load textures tiled for Xbox One"); + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } +#else + else if (xboxext->tileMode & XBOX_TILEMODE_SCARLETT) + { + DebugTrace("ERROR: XboxDDSTextureLoader for Xbox One cannot load textures tiled for Scarlett"); + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } +#endif + + // Bound sizes + if (mipCount > D3D11_REQ_MIP_LEVELS) + { + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + + switch (xboxext->resourceDimension) + { + case D3D12_RESOURCE_DIMENSION_TEXTURE1D: + if ((arraySize > D3D12_REQ_TEXTURE1D_ARRAY_AXIS_DIMENSION) || + (width > D3D12_REQ_TEXTURE1D_U_DIMENSION)) + { + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + break; + + case D3D12_RESOURCE_DIMENSION_TEXTURE2D: + if (isCubeMap) + { + // This is the right bound because we set arraySize to (NumCubes*6) above + if ((arraySize > D3D12_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION) || + (width > D3D12_REQ_TEXTURECUBE_DIMENSION) || + (height > D3D12_REQ_TEXTURECUBE_DIMENSION)) + { + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + } + else if ((arraySize > D3D12_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION) || + (width > D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION) || + (height > D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION)) + { + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + break; + + case D3D12_RESOURCE_DIMENSION_TEXTURE3D: + if ((arraySize > 1) || + (width > D3D12_REQ_TEXTURE3D_U_V_OR_W_DIMENSION) || + (height > D3D12_REQ_TEXTURE3D_U_V_OR_W_DIMENSION) || + (depth > D3D12_REQ_TEXTURE3D_U_V_OR_W_DIMENSION)) + { + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + break; + } + + if (xboxext->dxgiFormat == DXGI_FORMAT_UNKNOWN) + { + return E_FAIL; + } + + if (!xboxext->dataSize || !xboxext->baseAlignment) + { + return E_FAIL; + } + + if (xboxext->dataSize > bitSize) + { + return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF); + } + + // Allocate graphics memory. Depending on the data size it uses 4MB or 64K pages. + *grfxMemory = XMemAlloc(xboxext->dataSize, c_XMemAllocAttributes); + if (!*grfxMemory) + return E_OUTOFMEMORY; + + // Copy tiled data into graphics memory + memcpy(*grfxMemory, bitData, xboxext->dataSize); + + // Create the texture + hr = CreateD3DResources(d3dDevice, xboxext, + width, height, depth, mipCount, arraySize, + forceSRGB, *grfxMemory, + texture); + if (FAILED(hr)) + { + XMemFree(*grfxMemory, c_XMemAllocAttributes); + *grfxMemory = nullptr; + } + + if (outIsCubeMap) + { + *outIsCubeMap = isCubeMap; + } + + return hr; + } + + //-------------------------------------------------------------------------------------- + DDS_ALPHA_MODE GetAlphaMode(_In_ const DDS_HEADER* header) noexcept + { + if (header->ddspf.flags & DDS_FOURCC) + { + if (MAKEFOURCC('X', 'B', 'O', 'X') == header->ddspf.fourCC) + { + auto xboxext = reinterpret_cast(reinterpret_cast(header) + sizeof(DDS_HEADER)); + auto mode = static_cast(xboxext->miscFlags2 & DDS_MISC_FLAGS2_ALPHA_MODE_MASK); + switch (mode) + { + case DDS_ALPHA_MODE_STRAIGHT: + case DDS_ALPHA_MODE_PREMULTIPLIED: + case DDS_ALPHA_MODE_OPAQUE: + case DDS_ALPHA_MODE_CUSTOM: + return mode; + + default: + break; + } + } + } + + return DDS_ALPHA_MODE_UNKNOWN; + } +} // anonymous namespace + + +//-------------------------------------------------------------------------------------- +_Use_decl_annotations_ +HRESULT Xbox::CreateDDSTextureFromMemory( + ID3D12Device* d3dDevice, + const uint8_t* ddsData, + size_t ddsDataSize, + ID3D12Resource** texture, + void** grfxMemory, + DDS_ALPHA_MODE* alphaMode, + bool forceSRGB, + bool* isCubeMap ) noexcept +{ + if (texture) + { + *texture = nullptr; + } + + if (grfxMemory) + { + *grfxMemory = nullptr; + } + + if (alphaMode) + { + *alphaMode = DDS_ALPHA_MODE_UNKNOWN; + } + + if (isCubeMap) + { + *isCubeMap = false; + } + + if ( !d3dDevice || !ddsData || !texture || !grfxMemory ) + { + return E_INVALIDARG; + } + + // Validate DDS file in memory + if (ddsDataSize < (sizeof(uint32_t) + sizeof(DDS_HEADER))) + { + return E_FAIL; + } + + auto dwMagicNumber = *reinterpret_cast(ddsData); + if (dwMagicNumber != DDS_MAGIC) + { + return E_FAIL; + } + + auto header = reinterpret_cast( ddsData + sizeof( uint32_t ) ); + + // Verify header to validate DDS file + if (header->size != sizeof(DDS_HEADER) || + header->ddspf.size != sizeof(DDS_PIXELFORMAT)) + { + return E_FAIL; + } + + // Check for XBOX extension + if ( !( header->ddspf.flags & DDS_FOURCC ) + || ( MAKEFOURCC( 'X', 'B', 'O', 'X' ) != header->ddspf.fourCC ) ) + { + // Use standard DDSTextureLoader instead + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + } + + // Must be long enough for both headers and magic value + if (ddsDataSize < (sizeof(DDS_HEADER) + sizeof(uint32_t) + sizeof(DDS_HEADER_XBOX))) + { + return E_FAIL; + } + + auto offset = sizeof( uint32_t ) + sizeof( DDS_HEADER ) + sizeof( DDS_HEADER_XBOX ); + + HRESULT hr = CreateTextureFromDDS( d3dDevice, header, + ddsData + offset, ddsDataSize - offset, forceSRGB, + texture, grfxMemory, isCubeMap ); + if ( SUCCEEDED(hr) ) + { + _Analysis_assume_(*texture != nullptr); + SetDebugObjectName(*texture, L"XboxDDSTextureLoader"); + + if ( alphaMode ) + *alphaMode = GetAlphaMode( header ); + } + + return hr; +} + +//-------------------------------------------------------------------------------------- +_Use_decl_annotations_ +HRESULT Xbox::CreateDDSTextureFromFile( + ID3D12Device* d3dDevice, + const wchar_t* fileName, + ID3D12Resource** texture, + void** grfxMemory, + DDS_ALPHA_MODE* alphaMode, + bool forceSRGB, + bool* isCubeMap ) noexcept +{ + if (texture) + { + *texture = nullptr; + } + + if (grfxMemory) + { + *grfxMemory = nullptr; + } + + if (alphaMode) + { + *alphaMode = DDS_ALPHA_MODE_UNKNOWN; + } + + if (isCubeMap) + { + *isCubeMap = false; + } + + if ( !d3dDevice || !fileName || !texture || !grfxMemory ) + { + return E_INVALIDARG; + } + + DDS_HEADER* header = nullptr; + uint8_t* bitData = nullptr; + size_t bitSize = 0; + + std::unique_ptr ddsData; + HRESULT hr = LoadTextureDataFromFile( fileName, + ddsData, + &header, + &bitData, + &bitSize + ); + if (FAILED(hr)) + { + return hr; + } + + hr = CreateTextureFromDDS( d3dDevice, header, + bitData, bitSize, forceSRGB, + texture, grfxMemory, isCubeMap ); + + if ( SUCCEEDED(hr) ) + { +#if !defined(NO_D3D11_DEBUG_NAME) && ( defined(_DEBUG) || defined(PROFILE) ) + if (texture != nullptr && *texture != nullptr) + { + (*texture)->SetName( fileName ); + } +#endif + + if ( alphaMode ) + *alphaMode = GetAlphaMode( header ); + } + + return hr; +} + +//-------------------------------------------------------------------------------------- +_Use_decl_annotations_ +void Xbox::FreeDDSTextureMemory(void* grfxMemory) noexcept +{ + if (grfxMemory) + { + XMemFree(grfxMemory, c_XMemAllocAttributes); + } +} diff --git a/Common/DirectXTK12/Src/d3dx12.h b/Common/DirectXTK12/Src/d3dx12.h new file mode 100644 index 0000000..4ffa673 --- /dev/null +++ b/Common/DirectXTK12/Src/d3dx12.h @@ -0,0 +1,6477 @@ +//********************************************************* +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License (MIT). +// +//********************************************************* + +#ifndef __D3DX12_H__ +#define __D3DX12_H__ + +#include "d3d12.h" + +#if defined( __cplusplus ) + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wfloat-equal" +#pragma clang diagnostic ignored "-Wunknown-warning-option" +#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" +#endif + +struct CD3DX12_DEFAULT {}; +extern const DECLSPEC_SELECTANY CD3DX12_DEFAULT D3D12_DEFAULT; + +//------------------------------------------------------------------------------------------------ +inline bool operator==( const D3D12_VIEWPORT& l, const D3D12_VIEWPORT& r ) noexcept +{ + return l.TopLeftX == r.TopLeftX && l.TopLeftY == r.TopLeftY && l.Width == r.Width && + l.Height == r.Height && l.MinDepth == r.MinDepth && l.MaxDepth == r.MaxDepth; +} + +//------------------------------------------------------------------------------------------------ +inline bool operator!=( const D3D12_VIEWPORT& l, const D3D12_VIEWPORT& r ) noexcept +{ return !( l == r ); } + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_RECT : public D3D12_RECT +{ + CD3DX12_RECT() = default; + explicit CD3DX12_RECT( const D3D12_RECT& o ) noexcept : + D3D12_RECT( o ) + {} + explicit CD3DX12_RECT( + LONG Left, + LONG Top, + LONG Right, + LONG Bottom ) noexcept + { + left = Left; + top = Top; + right = Right; + bottom = Bottom; + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_VIEWPORT : public D3D12_VIEWPORT +{ + CD3DX12_VIEWPORT() = default; + explicit CD3DX12_VIEWPORT( const D3D12_VIEWPORT& o ) noexcept : + D3D12_VIEWPORT( o ) + {} + explicit CD3DX12_VIEWPORT( + FLOAT topLeftX, + FLOAT topLeftY, + FLOAT width, + FLOAT height, + FLOAT minDepth = D3D12_MIN_DEPTH, + FLOAT maxDepth = D3D12_MAX_DEPTH ) noexcept + { + TopLeftX = topLeftX; + TopLeftY = topLeftY; + Width = width; + Height = height; + MinDepth = minDepth; + MaxDepth = maxDepth; + } + explicit CD3DX12_VIEWPORT( + _In_ ID3D12Resource* pResource, + UINT mipSlice = 0, + FLOAT topLeftX = 0.0f, + FLOAT topLeftY = 0.0f, + FLOAT minDepth = D3D12_MIN_DEPTH, + FLOAT maxDepth = D3D12_MAX_DEPTH ) noexcept + { +#if defined(_MSC_VER) || !defined(_WIN32) + const auto Desc = pResource->GetDesc(); +#else + D3D12_RESOURCE_DESC tmpDesc; + const auto& Desc = *pResource->GetDesc(&tmpDesc); +#endif + const UINT64 SubresourceWidth = Desc.Width >> mipSlice; + const UINT64 SubresourceHeight = Desc.Height >> mipSlice; + switch (Desc.Dimension) + { + case D3D12_RESOURCE_DIMENSION_BUFFER: + TopLeftX = topLeftX; + TopLeftY = 0.0f; + Width = float(Desc.Width) - topLeftX; + Height = 1.0f; + break; + case D3D12_RESOURCE_DIMENSION_TEXTURE1D: + TopLeftX = topLeftX; + TopLeftY = 0.0f; + Width = (SubresourceWidth ? float(SubresourceWidth) : 1.0f) - topLeftX; + Height = 1.0f; + break; + case D3D12_RESOURCE_DIMENSION_TEXTURE2D: + case D3D12_RESOURCE_DIMENSION_TEXTURE3D: + TopLeftX = topLeftX; + TopLeftY = topLeftY; + Width = (SubresourceWidth ? float(SubresourceWidth) : 1.0f) - topLeftX; + Height = (SubresourceHeight ? float(SubresourceHeight) : 1.0f) - topLeftY; + break; + default: break; + } + + MinDepth = minDepth; + MaxDepth = maxDepth; + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_BOX : public D3D12_BOX +{ + CD3DX12_BOX() = default; + explicit CD3DX12_BOX( const D3D12_BOX& o ) noexcept : + D3D12_BOX( o ) + {} + explicit CD3DX12_BOX( + LONG Left, + LONG Right ) noexcept + { + left = static_cast(Left); + top = 0; + front = 0; + right = static_cast(Right); + bottom = 1; + back = 1; + } + explicit CD3DX12_BOX( + LONG Left, + LONG Top, + LONG Right, + LONG Bottom ) noexcept + { + left = static_cast(Left); + top = static_cast(Top); + front = 0; + right = static_cast(Right); + bottom = static_cast(Bottom); + back = 1; + } + explicit CD3DX12_BOX( + LONG Left, + LONG Top, + LONG Front, + LONG Right, + LONG Bottom, + LONG Back ) noexcept + { + left = static_cast(Left); + top = static_cast(Top); + front = static_cast(Front); + right = static_cast(Right); + bottom = static_cast(Bottom); + back = static_cast(Back); + } +}; +inline bool operator==( const D3D12_BOX& l, const D3D12_BOX& r ) noexcept +{ + return l.left == r.left && l.top == r.top && l.front == r.front && + l.right == r.right && l.bottom == r.bottom && l.back == r.back; +} +inline bool operator!=( const D3D12_BOX& l, const D3D12_BOX& r ) noexcept +{ return !( l == r ); } + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_DEPTH_STENCIL_DESC : public D3D12_DEPTH_STENCIL_DESC +{ + CD3DX12_DEPTH_STENCIL_DESC() = default; + explicit CD3DX12_DEPTH_STENCIL_DESC( const D3D12_DEPTH_STENCIL_DESC& o ) noexcept : + D3D12_DEPTH_STENCIL_DESC( o ) + {} + explicit CD3DX12_DEPTH_STENCIL_DESC( CD3DX12_DEFAULT ) noexcept + { + DepthEnable = TRUE; + DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL; + DepthFunc = D3D12_COMPARISON_FUNC_LESS; + StencilEnable = FALSE; + StencilReadMask = D3D12_DEFAULT_STENCIL_READ_MASK; + StencilWriteMask = D3D12_DEFAULT_STENCIL_WRITE_MASK; + const D3D12_DEPTH_STENCILOP_DESC defaultStencilOp = + { D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP, D3D12_COMPARISON_FUNC_ALWAYS }; + FrontFace = defaultStencilOp; + BackFace = defaultStencilOp; + } + explicit CD3DX12_DEPTH_STENCIL_DESC( + BOOL depthEnable, + D3D12_DEPTH_WRITE_MASK depthWriteMask, + D3D12_COMPARISON_FUNC depthFunc, + BOOL stencilEnable, + UINT8 stencilReadMask, + UINT8 stencilWriteMask, + D3D12_STENCIL_OP frontStencilFailOp, + D3D12_STENCIL_OP frontStencilDepthFailOp, + D3D12_STENCIL_OP frontStencilPassOp, + D3D12_COMPARISON_FUNC frontStencilFunc, + D3D12_STENCIL_OP backStencilFailOp, + D3D12_STENCIL_OP backStencilDepthFailOp, + D3D12_STENCIL_OP backStencilPassOp, + D3D12_COMPARISON_FUNC backStencilFunc ) noexcept + { + DepthEnable = depthEnable; + DepthWriteMask = depthWriteMask; + DepthFunc = depthFunc; + StencilEnable = stencilEnable; + StencilReadMask = stencilReadMask; + StencilWriteMask = stencilWriteMask; + FrontFace.StencilFailOp = frontStencilFailOp; + FrontFace.StencilDepthFailOp = frontStencilDepthFailOp; + FrontFace.StencilPassOp = frontStencilPassOp; + FrontFace.StencilFunc = frontStencilFunc; + BackFace.StencilFailOp = backStencilFailOp; + BackFace.StencilDepthFailOp = backStencilDepthFailOp; + BackFace.StencilPassOp = backStencilPassOp; + BackFace.StencilFunc = backStencilFunc; + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_DEPTH_STENCIL_DESC1 : public D3D12_DEPTH_STENCIL_DESC1 +{ + CD3DX12_DEPTH_STENCIL_DESC1() = default; + explicit CD3DX12_DEPTH_STENCIL_DESC1( const D3D12_DEPTH_STENCIL_DESC1& o ) noexcept : + D3D12_DEPTH_STENCIL_DESC1( o ) + {} + explicit CD3DX12_DEPTH_STENCIL_DESC1( const D3D12_DEPTH_STENCIL_DESC& o ) noexcept + { + DepthEnable = o.DepthEnable; + DepthWriteMask = o.DepthWriteMask; + DepthFunc = o.DepthFunc; + StencilEnable = o.StencilEnable; + StencilReadMask = o.StencilReadMask; + StencilWriteMask = o.StencilWriteMask; + FrontFace.StencilFailOp = o.FrontFace.StencilFailOp; + FrontFace.StencilDepthFailOp = o.FrontFace.StencilDepthFailOp; + FrontFace.StencilPassOp = o.FrontFace.StencilPassOp; + FrontFace.StencilFunc = o.FrontFace.StencilFunc; + BackFace.StencilFailOp = o.BackFace.StencilFailOp; + BackFace.StencilDepthFailOp = o.BackFace.StencilDepthFailOp; + BackFace.StencilPassOp = o.BackFace.StencilPassOp; + BackFace.StencilFunc = o.BackFace.StencilFunc; + DepthBoundsTestEnable = FALSE; + } + explicit CD3DX12_DEPTH_STENCIL_DESC1( CD3DX12_DEFAULT ) noexcept + { + DepthEnable = TRUE; + DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL; + DepthFunc = D3D12_COMPARISON_FUNC_LESS; + StencilEnable = FALSE; + StencilReadMask = D3D12_DEFAULT_STENCIL_READ_MASK; + StencilWriteMask = D3D12_DEFAULT_STENCIL_WRITE_MASK; + const D3D12_DEPTH_STENCILOP_DESC defaultStencilOp = + { D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP, D3D12_COMPARISON_FUNC_ALWAYS }; + FrontFace = defaultStencilOp; + BackFace = defaultStencilOp; + DepthBoundsTestEnable = FALSE; + } + explicit CD3DX12_DEPTH_STENCIL_DESC1( + BOOL depthEnable, + D3D12_DEPTH_WRITE_MASK depthWriteMask, + D3D12_COMPARISON_FUNC depthFunc, + BOOL stencilEnable, + UINT8 stencilReadMask, + UINT8 stencilWriteMask, + D3D12_STENCIL_OP frontStencilFailOp, + D3D12_STENCIL_OP frontStencilDepthFailOp, + D3D12_STENCIL_OP frontStencilPassOp, + D3D12_COMPARISON_FUNC frontStencilFunc, + D3D12_STENCIL_OP backStencilFailOp, + D3D12_STENCIL_OP backStencilDepthFailOp, + D3D12_STENCIL_OP backStencilPassOp, + D3D12_COMPARISON_FUNC backStencilFunc, + BOOL depthBoundsTestEnable ) noexcept + { + DepthEnable = depthEnable; + DepthWriteMask = depthWriteMask; + DepthFunc = depthFunc; + StencilEnable = stencilEnable; + StencilReadMask = stencilReadMask; + StencilWriteMask = stencilWriteMask; + FrontFace.StencilFailOp = frontStencilFailOp; + FrontFace.StencilDepthFailOp = frontStencilDepthFailOp; + FrontFace.StencilPassOp = frontStencilPassOp; + FrontFace.StencilFunc = frontStencilFunc; + BackFace.StencilFailOp = backStencilFailOp; + BackFace.StencilDepthFailOp = backStencilDepthFailOp; + BackFace.StencilPassOp = backStencilPassOp; + BackFace.StencilFunc = backStencilFunc; + DepthBoundsTestEnable = depthBoundsTestEnable; + } + operator D3D12_DEPTH_STENCIL_DESC() const noexcept + { + D3D12_DEPTH_STENCIL_DESC D; + D.DepthEnable = DepthEnable; + D.DepthWriteMask = DepthWriteMask; + D.DepthFunc = DepthFunc; + D.StencilEnable = StencilEnable; + D.StencilReadMask = StencilReadMask; + D.StencilWriteMask = StencilWriteMask; + D.FrontFace.StencilFailOp = FrontFace.StencilFailOp; + D.FrontFace.StencilDepthFailOp = FrontFace.StencilDepthFailOp; + D.FrontFace.StencilPassOp = FrontFace.StencilPassOp; + D.FrontFace.StencilFunc = FrontFace.StencilFunc; + D.BackFace.StencilFailOp = BackFace.StencilFailOp; + D.BackFace.StencilDepthFailOp = BackFace.StencilDepthFailOp; + D.BackFace.StencilPassOp = BackFace.StencilPassOp; + D.BackFace.StencilFunc = BackFace.StencilFunc; + return D; + } +}; + +//------------------------------------------------------------------------------------------------ +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 606) +struct CD3DX12_DEPTH_STENCIL_DESC2 : public D3D12_DEPTH_STENCIL_DESC2 +{ + CD3DX12_DEPTH_STENCIL_DESC2() = default; + explicit CD3DX12_DEPTH_STENCIL_DESC2( const D3D12_DEPTH_STENCIL_DESC2& o ) noexcept : + D3D12_DEPTH_STENCIL_DESC2( o ) + {} + explicit CD3DX12_DEPTH_STENCIL_DESC2( const D3D12_DEPTH_STENCIL_DESC1& o ) noexcept + { + DepthEnable = o.DepthEnable; + DepthWriteMask = o.DepthWriteMask; + DepthFunc = o.DepthFunc; + StencilEnable = o.StencilEnable; + FrontFace.StencilFailOp = o.FrontFace.StencilFailOp; + FrontFace.StencilDepthFailOp = o.FrontFace.StencilDepthFailOp; + FrontFace.StencilPassOp = o.FrontFace.StencilPassOp; + FrontFace.StencilFunc = o.FrontFace.StencilFunc; + FrontFace.StencilReadMask = o.StencilReadMask; + FrontFace.StencilWriteMask = o.StencilWriteMask; + + BackFace.StencilFailOp = o.BackFace.StencilFailOp; + BackFace.StencilDepthFailOp = o.BackFace.StencilDepthFailOp; + BackFace.StencilPassOp = o.BackFace.StencilPassOp; + BackFace.StencilFunc = o.BackFace.StencilFunc; + BackFace.StencilReadMask = o.StencilReadMask; + BackFace.StencilWriteMask = o.StencilWriteMask; + DepthBoundsTestEnable = o.DepthBoundsTestEnable; + } + explicit CD3DX12_DEPTH_STENCIL_DESC2( const D3D12_DEPTH_STENCIL_DESC& o ) noexcept + { + DepthEnable = o.DepthEnable; + DepthWriteMask = o.DepthWriteMask; + DepthFunc = o.DepthFunc; + StencilEnable = o.StencilEnable; + + FrontFace.StencilFailOp = o.FrontFace.StencilFailOp; + FrontFace.StencilDepthFailOp = o.FrontFace.StencilDepthFailOp; + FrontFace.StencilPassOp = o.FrontFace.StencilPassOp; + FrontFace.StencilFunc = o.FrontFace.StencilFunc; + FrontFace.StencilReadMask = o.StencilReadMask; + FrontFace.StencilWriteMask = o.StencilWriteMask; + + BackFace.StencilFailOp = o.BackFace.StencilFailOp; + BackFace.StencilDepthFailOp = o.BackFace.StencilDepthFailOp; + BackFace.StencilPassOp = o.BackFace.StencilPassOp; + BackFace.StencilFunc = o.BackFace.StencilFunc; + BackFace.StencilReadMask = o.StencilReadMask; + BackFace.StencilWriteMask = o.StencilWriteMask; + + DepthBoundsTestEnable = FALSE; + } + explicit CD3DX12_DEPTH_STENCIL_DESC2( CD3DX12_DEFAULT ) noexcept + { + DepthEnable = TRUE; + DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL; + DepthFunc = D3D12_COMPARISON_FUNC_LESS; + StencilEnable = FALSE; + const D3D12_DEPTH_STENCILOP_DESC1 defaultStencilOp = + { D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP, D3D12_COMPARISON_FUNC_ALWAYS, D3D12_DEFAULT_STENCIL_READ_MASK, D3D12_DEFAULT_STENCIL_WRITE_MASK }; + FrontFace = defaultStencilOp; + BackFace = defaultStencilOp; + DepthBoundsTestEnable = FALSE; + } + explicit CD3DX12_DEPTH_STENCIL_DESC2( + BOOL depthEnable, + D3D12_DEPTH_WRITE_MASK depthWriteMask, + D3D12_COMPARISON_FUNC depthFunc, + BOOL stencilEnable, + D3D12_STENCIL_OP frontStencilFailOp, + D3D12_STENCIL_OP frontStencilDepthFailOp, + D3D12_STENCIL_OP frontStencilPassOp, + D3D12_COMPARISON_FUNC frontStencilFunc, + UINT8 frontStencilReadMask, + UINT8 frontStencilWriteMask, + D3D12_STENCIL_OP backStencilFailOp, + D3D12_STENCIL_OP backStencilDepthFailOp, + D3D12_STENCIL_OP backStencilPassOp, + D3D12_COMPARISON_FUNC backStencilFunc, + UINT8 backStencilReadMask, + UINT8 backStencilWriteMask, + BOOL depthBoundsTestEnable ) noexcept + { + DepthEnable = depthEnable; + DepthWriteMask = depthWriteMask; + DepthFunc = depthFunc; + StencilEnable = stencilEnable; + + FrontFace.StencilFailOp = frontStencilFailOp; + FrontFace.StencilDepthFailOp = frontStencilDepthFailOp; + FrontFace.StencilPassOp = frontStencilPassOp; + FrontFace.StencilFunc = frontStencilFunc; + FrontFace.StencilReadMask = frontStencilReadMask; + FrontFace.StencilWriteMask = frontStencilWriteMask; + + BackFace.StencilFailOp = backStencilFailOp; + BackFace.StencilDepthFailOp = backStencilDepthFailOp; + BackFace.StencilPassOp = backStencilPassOp; + BackFace.StencilFunc = backStencilFunc; + BackFace.StencilReadMask = backStencilReadMask; + BackFace.StencilWriteMask = backStencilWriteMask; + + DepthBoundsTestEnable = depthBoundsTestEnable; + } + + operator D3D12_DEPTH_STENCIL_DESC() const noexcept + { + D3D12_DEPTH_STENCIL_DESC D; + D.DepthEnable = DepthEnable; + D.DepthWriteMask = DepthWriteMask; + D.DepthFunc = DepthFunc; + D.StencilEnable = StencilEnable; + D.StencilReadMask = FrontFace.StencilReadMask; + D.StencilWriteMask = FrontFace.StencilWriteMask; + D.FrontFace.StencilFailOp = FrontFace.StencilFailOp; + D.FrontFace.StencilDepthFailOp = FrontFace.StencilDepthFailOp; + D.FrontFace.StencilPassOp = FrontFace.StencilPassOp; + D.FrontFace.StencilFunc = FrontFace.StencilFunc; + D.BackFace.StencilFailOp = BackFace.StencilFailOp; + D.BackFace.StencilDepthFailOp = BackFace.StencilDepthFailOp; + D.BackFace.StencilPassOp = BackFace.StencilPassOp; + D.BackFace.StencilFunc = BackFace.StencilFunc; + return D; + } +}; +#endif // D3D12_SDK_VERSION >= 606 + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_BLEND_DESC : public D3D12_BLEND_DESC +{ + CD3DX12_BLEND_DESC() = default; + explicit CD3DX12_BLEND_DESC( const D3D12_BLEND_DESC& o ) noexcept : + D3D12_BLEND_DESC( o ) + {} + explicit CD3DX12_BLEND_DESC( CD3DX12_DEFAULT ) noexcept + { + AlphaToCoverageEnable = FALSE; + IndependentBlendEnable = FALSE; + const D3D12_RENDER_TARGET_BLEND_DESC defaultRenderTargetBlendDesc = + { + FALSE,FALSE, + D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD, + D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD, + D3D12_LOGIC_OP_NOOP, + D3D12_COLOR_WRITE_ENABLE_ALL, + }; + for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i) + RenderTarget[ i ] = defaultRenderTargetBlendDesc; + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_RASTERIZER_DESC : public D3D12_RASTERIZER_DESC +{ + CD3DX12_RASTERIZER_DESC() = default; + explicit CD3DX12_RASTERIZER_DESC( const D3D12_RASTERIZER_DESC& o ) noexcept : + D3D12_RASTERIZER_DESC( o ) + {} + explicit CD3DX12_RASTERIZER_DESC( CD3DX12_DEFAULT ) noexcept + { + FillMode = D3D12_FILL_MODE_SOLID; + CullMode = D3D12_CULL_MODE_BACK; + FrontCounterClockwise = FALSE; + DepthBias = D3D12_DEFAULT_DEPTH_BIAS; + DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP; + SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS; + DepthClipEnable = TRUE; + MultisampleEnable = FALSE; + AntialiasedLineEnable = FALSE; + ForcedSampleCount = 0; + ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF; + } + explicit CD3DX12_RASTERIZER_DESC( + D3D12_FILL_MODE fillMode, + D3D12_CULL_MODE cullMode, + BOOL frontCounterClockwise, + INT depthBias, + FLOAT depthBiasClamp, + FLOAT slopeScaledDepthBias, + BOOL depthClipEnable, + BOOL multisampleEnable, + BOOL antialiasedLineEnable, + UINT forcedSampleCount, + D3D12_CONSERVATIVE_RASTERIZATION_MODE conservativeRaster) noexcept + { + FillMode = fillMode; + CullMode = cullMode; + FrontCounterClockwise = frontCounterClockwise; + DepthBias = depthBias; + DepthBiasClamp = depthBiasClamp; + SlopeScaledDepthBias = slopeScaledDepthBias; + DepthClipEnable = depthClipEnable; + MultisampleEnable = multisampleEnable; + AntialiasedLineEnable = antialiasedLineEnable; + ForcedSampleCount = forcedSampleCount; + ConservativeRaster = conservativeRaster; + } +}; + +//------------------------------------------------------------------------------------------------ +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 608) +struct CD3DX12_RASTERIZER_DESC1 : public D3D12_RASTERIZER_DESC1 +{ + CD3DX12_RASTERIZER_DESC1() = default; + explicit CD3DX12_RASTERIZER_DESC1(const D3D12_RASTERIZER_DESC1& o) noexcept : + D3D12_RASTERIZER_DESC1(o) + + { + } + explicit CD3DX12_RASTERIZER_DESC1(const D3D12_RASTERIZER_DESC& o) noexcept + { + FillMode = o.FillMode; + CullMode = o.CullMode; + FrontCounterClockwise = o.FrontCounterClockwise; + DepthBias = static_cast(o.DepthBias); + DepthBiasClamp = o.DepthBiasClamp; + SlopeScaledDepthBias = o.SlopeScaledDepthBias; + DepthClipEnable = o.DepthClipEnable; + MultisampleEnable = o.MultisampleEnable; + AntialiasedLineEnable = o.AntialiasedLineEnable; + ForcedSampleCount = o.ForcedSampleCount; + ConservativeRaster = o.ConservativeRaster; + } + explicit CD3DX12_RASTERIZER_DESC1(CD3DX12_DEFAULT) noexcept + { + FillMode = D3D12_FILL_MODE_SOLID; + CullMode = D3D12_CULL_MODE_BACK; + FrontCounterClockwise = FALSE; + DepthBias = D3D12_DEFAULT_DEPTH_BIAS; + DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP; + SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS; + DepthClipEnable = TRUE; + MultisampleEnable = FALSE; + AntialiasedLineEnable = FALSE; + ForcedSampleCount = 0; + ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF; + } + explicit CD3DX12_RASTERIZER_DESC1( + D3D12_FILL_MODE fillMode, + D3D12_CULL_MODE cullMode, + BOOL frontCounterClockwise, + FLOAT depthBias, + FLOAT depthBiasClamp, + FLOAT slopeScaledDepthBias, + BOOL depthClipEnable, + BOOL multisampleEnable, + BOOL antialiasedLineEnable, + UINT forcedSampleCount, + D3D12_CONSERVATIVE_RASTERIZATION_MODE conservativeRaster) noexcept + { + FillMode = fillMode; + CullMode = cullMode; + FrontCounterClockwise = frontCounterClockwise; + DepthBias = depthBias; + DepthBiasClamp = depthBiasClamp; + SlopeScaledDepthBias = slopeScaledDepthBias; + DepthClipEnable = depthClipEnable; + MultisampleEnable = multisampleEnable; + AntialiasedLineEnable = antialiasedLineEnable; + ForcedSampleCount = forcedSampleCount; + ConservativeRaster = conservativeRaster; + } + + + operator D3D12_RASTERIZER_DESC() const noexcept + { + D3D12_RASTERIZER_DESC o; + + o.FillMode = FillMode; + o.CullMode = CullMode; + o.FrontCounterClockwise = FrontCounterClockwise; + o.DepthBias = static_cast(DepthBias); + o.DepthBiasClamp = DepthBiasClamp; + o.SlopeScaledDepthBias = SlopeScaledDepthBias; + o.DepthClipEnable = DepthClipEnable; + o.MultisampleEnable = MultisampleEnable; + o.AntialiasedLineEnable = AntialiasedLineEnable; + o.ForcedSampleCount = ForcedSampleCount; + o.ConservativeRaster = ConservativeRaster; + + return o; + } +}; +#endif // D3D12_SDK_VERSION >= 608 + +//------------------------------------------------------------------------------------------------ +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 610) +struct CD3DX12_RASTERIZER_DESC2 : public D3D12_RASTERIZER_DESC2 +{ + CD3DX12_RASTERIZER_DESC2() = default; + explicit CD3DX12_RASTERIZER_DESC2(const D3D12_RASTERIZER_DESC2& o) noexcept : + D3D12_RASTERIZER_DESC2(o) + + { + } + explicit CD3DX12_RASTERIZER_DESC2(const D3D12_RASTERIZER_DESC1& o) noexcept + { + FillMode = o.FillMode; + CullMode = o.CullMode; + FrontCounterClockwise = o.FrontCounterClockwise; + DepthBias = o.DepthBias; + DepthBiasClamp = o.DepthBiasClamp; + SlopeScaledDepthBias = o.SlopeScaledDepthBias; + DepthClipEnable = o.DepthClipEnable; + LineRasterizationMode = D3D12_LINE_RASTERIZATION_MODE_ALIASED; + if (o.MultisampleEnable) + { + LineRasterizationMode = D3D12_LINE_RASTERIZATION_MODE_QUADRILATERAL_WIDE; + } + else if (o.AntialiasedLineEnable) + { + LineRasterizationMode = D3D12_LINE_RASTERIZATION_MODE_ALPHA_ANTIALIASED; + } + ForcedSampleCount = o.ForcedSampleCount; + ConservativeRaster = o.ConservativeRaster; + } + explicit CD3DX12_RASTERIZER_DESC2(const D3D12_RASTERIZER_DESC& o) noexcept + : CD3DX12_RASTERIZER_DESC2(CD3DX12_RASTERIZER_DESC1(o)) + { + } + explicit CD3DX12_RASTERIZER_DESC2(CD3DX12_DEFAULT) noexcept + { + FillMode = D3D12_FILL_MODE_SOLID; + CullMode = D3D12_CULL_MODE_BACK; + FrontCounterClockwise = FALSE; + DepthBias = D3D12_DEFAULT_DEPTH_BIAS; + DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP; + SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS; + DepthClipEnable = TRUE; + LineRasterizationMode = D3D12_LINE_RASTERIZATION_MODE_ALIASED; + ForcedSampleCount = 0; + ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF; + } + explicit CD3DX12_RASTERIZER_DESC2( + D3D12_FILL_MODE fillMode, + D3D12_CULL_MODE cullMode, + BOOL frontCounterClockwise, + FLOAT depthBias, + FLOAT depthBiasClamp, + FLOAT slopeScaledDepthBias, + BOOL depthClipEnable, + D3D12_LINE_RASTERIZATION_MODE lineRasterizationMode, + UINT forcedSampleCount, + D3D12_CONSERVATIVE_RASTERIZATION_MODE conservativeRaster) noexcept + { + FillMode = fillMode; + CullMode = cullMode; + FrontCounterClockwise = frontCounterClockwise; + DepthBias = depthBias; + DepthBiasClamp = depthBiasClamp; + SlopeScaledDepthBias = slopeScaledDepthBias; + DepthClipEnable = depthClipEnable; + LineRasterizationMode = lineRasterizationMode; + ForcedSampleCount = forcedSampleCount; + ConservativeRaster = conservativeRaster; + } + + + operator D3D12_RASTERIZER_DESC1() const noexcept + { + D3D12_RASTERIZER_DESC1 o; + + o.FillMode = FillMode; + o.CullMode = CullMode; + o.FrontCounterClockwise = FrontCounterClockwise; + o.DepthBias = DepthBias; + o.DepthBiasClamp = DepthBiasClamp; + o.SlopeScaledDepthBias = SlopeScaledDepthBias; + o.DepthClipEnable = DepthClipEnable; + o.MultisampleEnable = FALSE; + o.AntialiasedLineEnable = FALSE; + if (LineRasterizationMode == D3D12_LINE_RASTERIZATION_MODE_ALPHA_ANTIALIASED) + { + o.AntialiasedLineEnable = TRUE; + } + else if (LineRasterizationMode != D3D12_LINE_RASTERIZATION_MODE_ALIASED) + { + o.MultisampleEnable = TRUE; + } + o.ForcedSampleCount = ForcedSampleCount; + o.ConservativeRaster = ConservativeRaster; + + return o; + } + operator D3D12_RASTERIZER_DESC() const noexcept + { + return static_cast(CD3DX12_RASTERIZER_DESC1(static_cast(*this))); + } +}; +#endif // D3D12_SDK_VERSION >= 610 + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_RESOURCE_ALLOCATION_INFO : public D3D12_RESOURCE_ALLOCATION_INFO +{ + CD3DX12_RESOURCE_ALLOCATION_INFO() = default; + explicit CD3DX12_RESOURCE_ALLOCATION_INFO( const D3D12_RESOURCE_ALLOCATION_INFO& o ) noexcept : + D3D12_RESOURCE_ALLOCATION_INFO( o ) + {} + CD3DX12_RESOURCE_ALLOCATION_INFO( + UINT64 size, + UINT64 alignment ) noexcept + { + SizeInBytes = size; + Alignment = alignment; + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_HEAP_PROPERTIES : public D3D12_HEAP_PROPERTIES +{ + CD3DX12_HEAP_PROPERTIES() = default; + explicit CD3DX12_HEAP_PROPERTIES(const D3D12_HEAP_PROPERTIES &o) noexcept : + D3D12_HEAP_PROPERTIES(o) + {} + CD3DX12_HEAP_PROPERTIES( + D3D12_CPU_PAGE_PROPERTY cpuPageProperty, + D3D12_MEMORY_POOL memoryPoolPreference, + UINT creationNodeMask = 1, + UINT nodeMask = 1 ) noexcept + { + Type = D3D12_HEAP_TYPE_CUSTOM; + CPUPageProperty = cpuPageProperty; + MemoryPoolPreference = memoryPoolPreference; + CreationNodeMask = creationNodeMask; + VisibleNodeMask = nodeMask; + } + explicit CD3DX12_HEAP_PROPERTIES( + D3D12_HEAP_TYPE type, + UINT creationNodeMask = 1, + UINT nodeMask = 1 ) noexcept + { + Type = type; + CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + CreationNodeMask = creationNodeMask; + VisibleNodeMask = nodeMask; + } + bool IsCPUAccessible() const noexcept + { + return Type == D3D12_HEAP_TYPE_UPLOAD || Type == D3D12_HEAP_TYPE_READBACK +#if 0 + || Type == D3D12_HEAP_TYPE_GPU_UPLOAD +#endif + || (Type == D3D12_HEAP_TYPE_CUSTOM && + (CPUPageProperty == D3D12_CPU_PAGE_PROPERTY_WRITE_COMBINE || CPUPageProperty == D3D12_CPU_PAGE_PROPERTY_WRITE_BACK)); + } +}; +inline bool operator==( const D3D12_HEAP_PROPERTIES& l, const D3D12_HEAP_PROPERTIES& r ) noexcept +{ + return l.Type == r.Type && l.CPUPageProperty == r.CPUPageProperty && + l.MemoryPoolPreference == r.MemoryPoolPreference && + l.CreationNodeMask == r.CreationNodeMask && + l.VisibleNodeMask == r.VisibleNodeMask; +} +inline bool operator!=( const D3D12_HEAP_PROPERTIES& l, const D3D12_HEAP_PROPERTIES& r ) noexcept +{ return !( l == r ); } + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_HEAP_DESC : public D3D12_HEAP_DESC +{ + CD3DX12_HEAP_DESC() = default; + explicit CD3DX12_HEAP_DESC(const D3D12_HEAP_DESC &o) noexcept : + D3D12_HEAP_DESC(o) + {} + CD3DX12_HEAP_DESC( + UINT64 size, + D3D12_HEAP_PROPERTIES properties, + UINT64 alignment = 0, + D3D12_HEAP_FLAGS flags = D3D12_HEAP_FLAG_NONE ) noexcept + { + SizeInBytes = size; + Properties = properties; + Alignment = alignment; + Flags = flags; + } + CD3DX12_HEAP_DESC( + UINT64 size, + D3D12_HEAP_TYPE type, + UINT64 alignment = 0, + D3D12_HEAP_FLAGS flags = D3D12_HEAP_FLAG_NONE ) noexcept + { + SizeInBytes = size; + Properties = CD3DX12_HEAP_PROPERTIES( type ); + Alignment = alignment; + Flags = flags; + } + CD3DX12_HEAP_DESC( + UINT64 size, + D3D12_CPU_PAGE_PROPERTY cpuPageProperty, + D3D12_MEMORY_POOL memoryPoolPreference, + UINT64 alignment = 0, + D3D12_HEAP_FLAGS flags = D3D12_HEAP_FLAG_NONE ) noexcept + { + SizeInBytes = size; + Properties = CD3DX12_HEAP_PROPERTIES( cpuPageProperty, memoryPoolPreference ); + Alignment = alignment; + Flags = flags; + } + CD3DX12_HEAP_DESC( + const D3D12_RESOURCE_ALLOCATION_INFO& resAllocInfo, + D3D12_HEAP_PROPERTIES properties, + D3D12_HEAP_FLAGS flags = D3D12_HEAP_FLAG_NONE ) noexcept + { + SizeInBytes = resAllocInfo.SizeInBytes; + Properties = properties; + Alignment = resAllocInfo.Alignment; + Flags = flags; + } + CD3DX12_HEAP_DESC( + const D3D12_RESOURCE_ALLOCATION_INFO& resAllocInfo, + D3D12_HEAP_TYPE type, + D3D12_HEAP_FLAGS flags = D3D12_HEAP_FLAG_NONE ) noexcept + { + SizeInBytes = resAllocInfo.SizeInBytes; + Properties = CD3DX12_HEAP_PROPERTIES( type ); + Alignment = resAllocInfo.Alignment; + Flags = flags; + } + CD3DX12_HEAP_DESC( + const D3D12_RESOURCE_ALLOCATION_INFO& resAllocInfo, + D3D12_CPU_PAGE_PROPERTY cpuPageProperty, + D3D12_MEMORY_POOL memoryPoolPreference, + D3D12_HEAP_FLAGS flags = D3D12_HEAP_FLAG_NONE ) noexcept + { + SizeInBytes = resAllocInfo.SizeInBytes; + Properties = CD3DX12_HEAP_PROPERTIES( cpuPageProperty, memoryPoolPreference ); + Alignment = resAllocInfo.Alignment; + Flags = flags; + } + bool IsCPUAccessible() const noexcept + { return static_cast< const CD3DX12_HEAP_PROPERTIES* >( &Properties )->IsCPUAccessible(); } +}; +inline bool operator==( const D3D12_HEAP_DESC& l, const D3D12_HEAP_DESC& r ) noexcept +{ + return l.SizeInBytes == r.SizeInBytes && + l.Properties == r.Properties && + l.Alignment == r.Alignment && + l.Flags == r.Flags; +} +inline bool operator!=( const D3D12_HEAP_DESC& l, const D3D12_HEAP_DESC& r ) noexcept +{ return !( l == r ); } + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_CLEAR_VALUE : public D3D12_CLEAR_VALUE +{ + CD3DX12_CLEAR_VALUE() = default; + explicit CD3DX12_CLEAR_VALUE(const D3D12_CLEAR_VALUE &o) noexcept : + D3D12_CLEAR_VALUE(o) + {} + CD3DX12_CLEAR_VALUE( + DXGI_FORMAT format, + const FLOAT color[4] ) noexcept + { + Format = format; + memcpy( Color, color, sizeof( Color ) ); + } + CD3DX12_CLEAR_VALUE( + DXGI_FORMAT format, + FLOAT depth, + UINT8 stencil ) noexcept + { + Format = format; + memset( &Color, 0, sizeof( Color ) ); + /* Use memcpy to preserve NAN values */ + memcpy( &DepthStencil.Depth, &depth, sizeof( depth ) ); + DepthStencil.Stencil = stencil; + } +}; + +//------------------------------------------------------------------------------------------------ +inline bool operator==( const D3D12_CLEAR_VALUE &a, const D3D12_CLEAR_VALUE &b) noexcept +{ + if (a.Format != b.Format) return false; + if (a.Format == DXGI_FORMAT_D24_UNORM_S8_UINT + || a.Format == DXGI_FORMAT_D16_UNORM + || a.Format == DXGI_FORMAT_D32_FLOAT + || a.Format == DXGI_FORMAT_D32_FLOAT_S8X24_UINT) + { + return (a.DepthStencil.Depth == b.DepthStencil.Depth) && + (a.DepthStencil.Stencil == b.DepthStencil.Stencil); + } else { + return (a.Color[0] == b.Color[0]) && + (a.Color[1] == b.Color[1]) && + (a.Color[2] == b.Color[2]) && + (a.Color[3] == b.Color[3]); + } +} + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_RANGE : public D3D12_RANGE +{ + CD3DX12_RANGE() = default; + explicit CD3DX12_RANGE(const D3D12_RANGE &o) noexcept : + D3D12_RANGE(o) + {} + CD3DX12_RANGE( + SIZE_T begin, + SIZE_T end ) noexcept + { + Begin = begin; + End = end; + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_RANGE_UINT64 : public D3D12_RANGE_UINT64 +{ + CD3DX12_RANGE_UINT64() = default; + explicit CD3DX12_RANGE_UINT64(const D3D12_RANGE_UINT64 &o) noexcept : + D3D12_RANGE_UINT64(o) + {} + CD3DX12_RANGE_UINT64( + UINT64 begin, + UINT64 end ) noexcept + { + Begin = begin; + End = end; + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_SUBRESOURCE_RANGE_UINT64 : public D3D12_SUBRESOURCE_RANGE_UINT64 +{ + CD3DX12_SUBRESOURCE_RANGE_UINT64() = default; + explicit CD3DX12_SUBRESOURCE_RANGE_UINT64(const D3D12_SUBRESOURCE_RANGE_UINT64 &o) noexcept : + D3D12_SUBRESOURCE_RANGE_UINT64(o) + {} + CD3DX12_SUBRESOURCE_RANGE_UINT64( + UINT subresource, + const D3D12_RANGE_UINT64& range ) noexcept + { + Subresource = subresource; + Range = range; + } + CD3DX12_SUBRESOURCE_RANGE_UINT64( + UINT subresource, + UINT64 begin, + UINT64 end ) noexcept + { + Subresource = subresource; + Range.Begin = begin; + Range.End = end; + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_SHADER_BYTECODE : public D3D12_SHADER_BYTECODE +{ + CD3DX12_SHADER_BYTECODE() = default; + explicit CD3DX12_SHADER_BYTECODE(const D3D12_SHADER_BYTECODE &o) noexcept : + D3D12_SHADER_BYTECODE(o) + {} + CD3DX12_SHADER_BYTECODE( + _In_ ID3DBlob* pShaderBlob ) noexcept + { + pShaderBytecode = pShaderBlob->GetBufferPointer(); + BytecodeLength = pShaderBlob->GetBufferSize(); + } + CD3DX12_SHADER_BYTECODE( + const void* _pShaderBytecode, + SIZE_T bytecodeLength ) noexcept + { + pShaderBytecode = _pShaderBytecode; + BytecodeLength = bytecodeLength; + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_TILED_RESOURCE_COORDINATE : public D3D12_TILED_RESOURCE_COORDINATE +{ + CD3DX12_TILED_RESOURCE_COORDINATE() = default; + explicit CD3DX12_TILED_RESOURCE_COORDINATE(const D3D12_TILED_RESOURCE_COORDINATE &o) noexcept : + D3D12_TILED_RESOURCE_COORDINATE(o) + {} + CD3DX12_TILED_RESOURCE_COORDINATE( + UINT x, + UINT y, + UINT z, + UINT subresource ) noexcept + { + X = x; + Y = y; + Z = z; + Subresource = subresource; + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_TILE_REGION_SIZE : public D3D12_TILE_REGION_SIZE +{ + CD3DX12_TILE_REGION_SIZE() = default; + explicit CD3DX12_TILE_REGION_SIZE(const D3D12_TILE_REGION_SIZE &o) noexcept : + D3D12_TILE_REGION_SIZE(o) + {} + CD3DX12_TILE_REGION_SIZE( + UINT numTiles, + BOOL useBox, + UINT width, + UINT16 height, + UINT16 depth ) noexcept + { + NumTiles = numTiles; + UseBox = useBox; + Width = width; + Height = height; + Depth = depth; + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_SUBRESOURCE_TILING : public D3D12_SUBRESOURCE_TILING +{ + CD3DX12_SUBRESOURCE_TILING() = default; + explicit CD3DX12_SUBRESOURCE_TILING(const D3D12_SUBRESOURCE_TILING &o) noexcept : + D3D12_SUBRESOURCE_TILING(o) + {} + CD3DX12_SUBRESOURCE_TILING( + UINT widthInTiles, + UINT16 heightInTiles, + UINT16 depthInTiles, + UINT startTileIndexInOverallResource ) noexcept + { + WidthInTiles = widthInTiles; + HeightInTiles = heightInTiles; + DepthInTiles = depthInTiles; + StartTileIndexInOverallResource = startTileIndexInOverallResource; + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_TILE_SHAPE : public D3D12_TILE_SHAPE +{ + CD3DX12_TILE_SHAPE() = default; + explicit CD3DX12_TILE_SHAPE(const D3D12_TILE_SHAPE &o) noexcept : + D3D12_TILE_SHAPE(o) + {} + CD3DX12_TILE_SHAPE( + UINT widthInTexels, + UINT heightInTexels, + UINT depthInTexels ) noexcept + { + WidthInTexels = widthInTexels; + HeightInTexels = heightInTexels; + DepthInTexels = depthInTexels; + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_RESOURCE_BARRIER : public D3D12_RESOURCE_BARRIER +{ + CD3DX12_RESOURCE_BARRIER() = default; + explicit CD3DX12_RESOURCE_BARRIER(const D3D12_RESOURCE_BARRIER &o) noexcept : + D3D12_RESOURCE_BARRIER(o) + {} + static inline CD3DX12_RESOURCE_BARRIER Transition( + _In_ ID3D12Resource* pResource, + D3D12_RESOURCE_STATES stateBefore, + D3D12_RESOURCE_STATES stateAfter, + UINT subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, + D3D12_RESOURCE_BARRIER_FLAGS flags = D3D12_RESOURCE_BARRIER_FLAG_NONE) noexcept + { + CD3DX12_RESOURCE_BARRIER result = {}; + D3D12_RESOURCE_BARRIER &barrier = result; + result.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + result.Flags = flags; + barrier.Transition.pResource = pResource; + barrier.Transition.StateBefore = stateBefore; + barrier.Transition.StateAfter = stateAfter; + barrier.Transition.Subresource = subresource; + return result; + } + static inline CD3DX12_RESOURCE_BARRIER Aliasing( + _In_opt_ ID3D12Resource* pResourceBefore, + _In_opt_ ID3D12Resource* pResourceAfter) noexcept + { + CD3DX12_RESOURCE_BARRIER result = {}; + D3D12_RESOURCE_BARRIER &barrier = result; + result.Type = D3D12_RESOURCE_BARRIER_TYPE_ALIASING; + barrier.Aliasing.pResourceBefore = pResourceBefore; + barrier.Aliasing.pResourceAfter = pResourceAfter; + return result; + } + static inline CD3DX12_RESOURCE_BARRIER UAV( + _In_opt_ ID3D12Resource* pResource) noexcept + { + CD3DX12_RESOURCE_BARRIER result = {}; + D3D12_RESOURCE_BARRIER &barrier = result; + result.Type = D3D12_RESOURCE_BARRIER_TYPE_UAV; + barrier.UAV.pResource = pResource; + return result; + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_PACKED_MIP_INFO : public D3D12_PACKED_MIP_INFO +{ + CD3DX12_PACKED_MIP_INFO() = default; + explicit CD3DX12_PACKED_MIP_INFO(const D3D12_PACKED_MIP_INFO &o) noexcept : + D3D12_PACKED_MIP_INFO(o) + {} + CD3DX12_PACKED_MIP_INFO( + UINT8 numStandardMips, + UINT8 numPackedMips, + UINT numTilesForPackedMips, + UINT startTileIndexInOverallResource ) noexcept + { + NumStandardMips = numStandardMips; + NumPackedMips = numPackedMips; + NumTilesForPackedMips = numTilesForPackedMips; + StartTileIndexInOverallResource = startTileIndexInOverallResource; + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_SUBRESOURCE_FOOTPRINT : public D3D12_SUBRESOURCE_FOOTPRINT +{ + CD3DX12_SUBRESOURCE_FOOTPRINT() = default; + explicit CD3DX12_SUBRESOURCE_FOOTPRINT(const D3D12_SUBRESOURCE_FOOTPRINT &o) noexcept : + D3D12_SUBRESOURCE_FOOTPRINT(o) + {} + CD3DX12_SUBRESOURCE_FOOTPRINT( + DXGI_FORMAT format, + UINT width, + UINT height, + UINT depth, + UINT rowPitch ) noexcept + { + Format = format; + Width = width; + Height = height; + Depth = depth; + RowPitch = rowPitch; + } + explicit CD3DX12_SUBRESOURCE_FOOTPRINT( + const D3D12_RESOURCE_DESC& resDesc, + UINT rowPitch ) noexcept + { + Format = resDesc.Format; + Width = UINT( resDesc.Width ); + Height = resDesc.Height; + Depth = (resDesc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D ? resDesc.DepthOrArraySize : 1u); + RowPitch = rowPitch; + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_TEXTURE_COPY_LOCATION : public D3D12_TEXTURE_COPY_LOCATION +{ + CD3DX12_TEXTURE_COPY_LOCATION() = default; + explicit CD3DX12_TEXTURE_COPY_LOCATION(const D3D12_TEXTURE_COPY_LOCATION &o) noexcept : + D3D12_TEXTURE_COPY_LOCATION(o) + {} + CD3DX12_TEXTURE_COPY_LOCATION(_In_ ID3D12Resource* pRes) noexcept + { + pResource = pRes; + Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; + PlacedFootprint = {}; + } + CD3DX12_TEXTURE_COPY_LOCATION(_In_ ID3D12Resource* pRes, D3D12_PLACED_SUBRESOURCE_FOOTPRINT const& Footprint) noexcept + { + pResource = pRes; + Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; + PlacedFootprint = Footprint; + } + CD3DX12_TEXTURE_COPY_LOCATION(_In_ ID3D12Resource* pRes, UINT Sub) noexcept + { + pResource = pRes; + Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; + PlacedFootprint = {}; + SubresourceIndex = Sub; + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_DESCRIPTOR_RANGE : public D3D12_DESCRIPTOR_RANGE +{ + CD3DX12_DESCRIPTOR_RANGE() = default; + explicit CD3DX12_DESCRIPTOR_RANGE(const D3D12_DESCRIPTOR_RANGE &o) noexcept : + D3D12_DESCRIPTOR_RANGE(o) + {} + CD3DX12_DESCRIPTOR_RANGE( + D3D12_DESCRIPTOR_RANGE_TYPE rangeType, + UINT numDescriptors, + UINT baseShaderRegister, + UINT registerSpace = 0, + UINT offsetInDescriptorsFromTableStart = + D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND) noexcept + { + Init(rangeType, numDescriptors, baseShaderRegister, registerSpace, offsetInDescriptorsFromTableStart); + } + + inline void Init( + D3D12_DESCRIPTOR_RANGE_TYPE rangeType, + UINT numDescriptors, + UINT baseShaderRegister, + UINT registerSpace = 0, + UINT offsetInDescriptorsFromTableStart = + D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND) noexcept + { + Init(*this, rangeType, numDescriptors, baseShaderRegister, registerSpace, offsetInDescriptorsFromTableStart); + } + + static inline void Init( + _Out_ D3D12_DESCRIPTOR_RANGE &range, + D3D12_DESCRIPTOR_RANGE_TYPE rangeType, + UINT numDescriptors, + UINT baseShaderRegister, + UINT registerSpace = 0, + UINT offsetInDescriptorsFromTableStart = + D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND) noexcept + { + range.RangeType = rangeType; + range.NumDescriptors = numDescriptors; + range.BaseShaderRegister = baseShaderRegister; + range.RegisterSpace = registerSpace; + range.OffsetInDescriptorsFromTableStart = offsetInDescriptorsFromTableStart; + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_ROOT_DESCRIPTOR_TABLE : public D3D12_ROOT_DESCRIPTOR_TABLE +{ + CD3DX12_ROOT_DESCRIPTOR_TABLE() = default; + explicit CD3DX12_ROOT_DESCRIPTOR_TABLE(const D3D12_ROOT_DESCRIPTOR_TABLE &o) noexcept : + D3D12_ROOT_DESCRIPTOR_TABLE(o) + {} + CD3DX12_ROOT_DESCRIPTOR_TABLE( + UINT numDescriptorRanges, + _In_reads_opt_(numDescriptorRanges) const D3D12_DESCRIPTOR_RANGE* _pDescriptorRanges) noexcept + { + Init(numDescriptorRanges, _pDescriptorRanges); + } + + inline void Init( + UINT numDescriptorRanges, + _In_reads_opt_(numDescriptorRanges) const D3D12_DESCRIPTOR_RANGE* _pDescriptorRanges) noexcept + { + Init(*this, numDescriptorRanges, _pDescriptorRanges); + } + + static inline void Init( + _Out_ D3D12_ROOT_DESCRIPTOR_TABLE &rootDescriptorTable, + UINT numDescriptorRanges, + _In_reads_opt_(numDescriptorRanges) const D3D12_DESCRIPTOR_RANGE* _pDescriptorRanges) noexcept + { + rootDescriptorTable.NumDescriptorRanges = numDescriptorRanges; + rootDescriptorTable.pDescriptorRanges = _pDescriptorRanges; + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_ROOT_CONSTANTS : public D3D12_ROOT_CONSTANTS +{ + CD3DX12_ROOT_CONSTANTS() = default; + explicit CD3DX12_ROOT_CONSTANTS(const D3D12_ROOT_CONSTANTS &o) noexcept : + D3D12_ROOT_CONSTANTS(o) + {} + CD3DX12_ROOT_CONSTANTS( + UINT num32BitValues, + UINT shaderRegister, + UINT registerSpace = 0) noexcept + { + Init(num32BitValues, shaderRegister, registerSpace); + } + + inline void Init( + UINT num32BitValues, + UINT shaderRegister, + UINT registerSpace = 0) noexcept + { + Init(*this, num32BitValues, shaderRegister, registerSpace); + } + + static inline void Init( + _Out_ D3D12_ROOT_CONSTANTS &rootConstants, + UINT num32BitValues, + UINT shaderRegister, + UINT registerSpace = 0) noexcept + { + rootConstants.Num32BitValues = num32BitValues; + rootConstants.ShaderRegister = shaderRegister; + rootConstants.RegisterSpace = registerSpace; + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_ROOT_DESCRIPTOR : public D3D12_ROOT_DESCRIPTOR +{ + CD3DX12_ROOT_DESCRIPTOR() = default; + explicit CD3DX12_ROOT_DESCRIPTOR(const D3D12_ROOT_DESCRIPTOR &o) noexcept : + D3D12_ROOT_DESCRIPTOR(o) + {} + CD3DX12_ROOT_DESCRIPTOR( + UINT shaderRegister, + UINT registerSpace = 0) noexcept + { + Init(shaderRegister, registerSpace); + } + + inline void Init( + UINT shaderRegister, + UINT registerSpace = 0) noexcept + { + Init(*this, shaderRegister, registerSpace); + } + + static inline void Init(_Out_ D3D12_ROOT_DESCRIPTOR &table, UINT shaderRegister, UINT registerSpace = 0) noexcept + { + table.ShaderRegister = shaderRegister; + table.RegisterSpace = registerSpace; + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_ROOT_PARAMETER : public D3D12_ROOT_PARAMETER +{ + CD3DX12_ROOT_PARAMETER() = default; + explicit CD3DX12_ROOT_PARAMETER(const D3D12_ROOT_PARAMETER &o) noexcept : + D3D12_ROOT_PARAMETER(o) + {} + + static inline void InitAsDescriptorTable( + _Out_ D3D12_ROOT_PARAMETER &rootParam, + UINT numDescriptorRanges, + _In_reads_(numDescriptorRanges) const D3D12_DESCRIPTOR_RANGE* pDescriptorRanges, + D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept + { + rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + rootParam.ShaderVisibility = visibility; + CD3DX12_ROOT_DESCRIPTOR_TABLE::Init(rootParam.DescriptorTable, numDescriptorRanges, pDescriptorRanges); + } + + static inline void InitAsConstants( + _Out_ D3D12_ROOT_PARAMETER &rootParam, + UINT num32BitValues, + UINT shaderRegister, + UINT registerSpace = 0, + D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept + { + rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS; + rootParam.ShaderVisibility = visibility; + CD3DX12_ROOT_CONSTANTS::Init(rootParam.Constants, num32BitValues, shaderRegister, registerSpace); + } + + static inline void InitAsConstantBufferView( + _Out_ D3D12_ROOT_PARAMETER &rootParam, + UINT shaderRegister, + UINT registerSpace = 0, + D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept + { + rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV; + rootParam.ShaderVisibility = visibility; + CD3DX12_ROOT_DESCRIPTOR::Init(rootParam.Descriptor, shaderRegister, registerSpace); + } + + static inline void InitAsShaderResourceView( + _Out_ D3D12_ROOT_PARAMETER &rootParam, + UINT shaderRegister, + UINT registerSpace = 0, + D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept + { + rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_SRV; + rootParam.ShaderVisibility = visibility; + CD3DX12_ROOT_DESCRIPTOR::Init(rootParam.Descriptor, shaderRegister, registerSpace); + } + + static inline void InitAsUnorderedAccessView( + _Out_ D3D12_ROOT_PARAMETER &rootParam, + UINT shaderRegister, + UINT registerSpace = 0, + D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept + { + rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV; + rootParam.ShaderVisibility = visibility; + CD3DX12_ROOT_DESCRIPTOR::Init(rootParam.Descriptor, shaderRegister, registerSpace); + } + + inline void InitAsDescriptorTable( + UINT numDescriptorRanges, + _In_reads_(numDescriptorRanges) const D3D12_DESCRIPTOR_RANGE* pDescriptorRanges, + D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept + { + InitAsDescriptorTable(*this, numDescriptorRanges, pDescriptorRanges, visibility); + } + + inline void InitAsConstants( + UINT num32BitValues, + UINT shaderRegister, + UINT registerSpace = 0, + D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept + { + InitAsConstants(*this, num32BitValues, shaderRegister, registerSpace, visibility); + } + + inline void InitAsConstantBufferView( + UINT shaderRegister, + UINT registerSpace = 0, + D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept + { + InitAsConstantBufferView(*this, shaderRegister, registerSpace, visibility); + } + + inline void InitAsShaderResourceView( + UINT shaderRegister, + UINT registerSpace = 0, + D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept + { + InitAsShaderResourceView(*this, shaderRegister, registerSpace, visibility); + } + + inline void InitAsUnorderedAccessView( + UINT shaderRegister, + UINT registerSpace = 0, + D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept + { + InitAsUnorderedAccessView(*this, shaderRegister, registerSpace, visibility); + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_STATIC_SAMPLER_DESC : public D3D12_STATIC_SAMPLER_DESC +{ + CD3DX12_STATIC_SAMPLER_DESC() = default; + explicit CD3DX12_STATIC_SAMPLER_DESC(const D3D12_STATIC_SAMPLER_DESC &o) noexcept : + D3D12_STATIC_SAMPLER_DESC(o) + {} + CD3DX12_STATIC_SAMPLER_DESC( + UINT shaderRegister, + D3D12_FILTER filter = D3D12_FILTER_ANISOTROPIC, + D3D12_TEXTURE_ADDRESS_MODE addressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP, + D3D12_TEXTURE_ADDRESS_MODE addressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP, + D3D12_TEXTURE_ADDRESS_MODE addressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP, + FLOAT mipLODBias = 0, + UINT maxAnisotropy = 16, + D3D12_COMPARISON_FUNC comparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL, + D3D12_STATIC_BORDER_COLOR borderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE, + FLOAT minLOD = 0.f, + FLOAT maxLOD = D3D12_FLOAT32_MAX, + D3D12_SHADER_VISIBILITY shaderVisibility = D3D12_SHADER_VISIBILITY_ALL, + UINT registerSpace = 0) noexcept + { + Init( + shaderRegister, + filter, + addressU, + addressV, + addressW, + mipLODBias, + maxAnisotropy, + comparisonFunc, + borderColor, + minLOD, + maxLOD, + shaderVisibility, + registerSpace); + } + + static inline void Init( + _Out_ D3D12_STATIC_SAMPLER_DESC &samplerDesc, + UINT shaderRegister, + D3D12_FILTER filter = D3D12_FILTER_ANISOTROPIC, + D3D12_TEXTURE_ADDRESS_MODE addressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP, + D3D12_TEXTURE_ADDRESS_MODE addressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP, + D3D12_TEXTURE_ADDRESS_MODE addressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP, + FLOAT mipLODBias = 0, + UINT maxAnisotropy = 16, + D3D12_COMPARISON_FUNC comparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL, + D3D12_STATIC_BORDER_COLOR borderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE, + FLOAT minLOD = 0.f, + FLOAT maxLOD = D3D12_FLOAT32_MAX, + D3D12_SHADER_VISIBILITY shaderVisibility = D3D12_SHADER_VISIBILITY_ALL, + UINT registerSpace = 0) noexcept + { + samplerDesc.ShaderRegister = shaderRegister; + samplerDesc.Filter = filter; + samplerDesc.AddressU = addressU; + samplerDesc.AddressV = addressV; + samplerDesc.AddressW = addressW; + samplerDesc.MipLODBias = mipLODBias; + samplerDesc.MaxAnisotropy = maxAnisotropy; + samplerDesc.ComparisonFunc = comparisonFunc; + samplerDesc.BorderColor = borderColor; + samplerDesc.MinLOD = minLOD; + samplerDesc.MaxLOD = maxLOD; + samplerDesc.ShaderVisibility = shaderVisibility; + samplerDesc.RegisterSpace = registerSpace; + } + inline void Init( + UINT shaderRegister, + D3D12_FILTER filter = D3D12_FILTER_ANISOTROPIC, + D3D12_TEXTURE_ADDRESS_MODE addressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP, + D3D12_TEXTURE_ADDRESS_MODE addressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP, + D3D12_TEXTURE_ADDRESS_MODE addressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP, + FLOAT mipLODBias = 0, + UINT maxAnisotropy = 16, + D3D12_COMPARISON_FUNC comparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL, + D3D12_STATIC_BORDER_COLOR borderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE, + FLOAT minLOD = 0.f, + FLOAT maxLOD = D3D12_FLOAT32_MAX, + D3D12_SHADER_VISIBILITY shaderVisibility = D3D12_SHADER_VISIBILITY_ALL, + UINT registerSpace = 0) noexcept + { + Init( + *this, + shaderRegister, + filter, + addressU, + addressV, + addressW, + mipLODBias, + maxAnisotropy, + comparisonFunc, + borderColor, + minLOD, + maxLOD, + shaderVisibility, + registerSpace); + } + +}; + +//------------------------------------------------------------------------------------------------ +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 609) +struct CD3DX12_STATIC_SAMPLER_DESC1 : public D3D12_STATIC_SAMPLER_DESC1 +{ + CD3DX12_STATIC_SAMPLER_DESC1() = default; + explicit CD3DX12_STATIC_SAMPLER_DESC1(const D3D12_STATIC_SAMPLER_DESC &o) noexcept + { + memcpy(this, &o, sizeof(D3D12_STATIC_SAMPLER_DESC)); + Flags = D3D12_SAMPLER_FLAGS::D3D12_SAMPLER_FLAG_NONE; + } + explicit CD3DX12_STATIC_SAMPLER_DESC1(const D3D12_STATIC_SAMPLER_DESC1 & o) noexcept : + D3D12_STATIC_SAMPLER_DESC1(o) + {} + CD3DX12_STATIC_SAMPLER_DESC1( + UINT shaderRegister, + D3D12_FILTER filter = D3D12_FILTER_ANISOTROPIC, + D3D12_TEXTURE_ADDRESS_MODE addressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP, + D3D12_TEXTURE_ADDRESS_MODE addressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP, + D3D12_TEXTURE_ADDRESS_MODE addressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP, + FLOAT mipLODBias = 0, + UINT maxAnisotropy = 16, + D3D12_COMPARISON_FUNC comparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL, + D3D12_STATIC_BORDER_COLOR borderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE, + FLOAT minLOD = 0.f, + FLOAT maxLOD = D3D12_FLOAT32_MAX, + D3D12_SHADER_VISIBILITY shaderVisibility = D3D12_SHADER_VISIBILITY_ALL, + UINT registerSpace = 0, + D3D12_SAMPLER_FLAGS flags = D3D12_SAMPLER_FLAGS::D3D12_SAMPLER_FLAG_NONE) noexcept + { + Init( + shaderRegister, + filter, + addressU, + addressV, + addressW, + mipLODBias, + maxAnisotropy, + comparisonFunc, + borderColor, + minLOD, + maxLOD, + shaderVisibility, + registerSpace, + flags); + } + + static inline void Init( + _Out_ D3D12_STATIC_SAMPLER_DESC1 &samplerDesc, + UINT shaderRegister, + D3D12_FILTER filter = D3D12_FILTER_ANISOTROPIC, + D3D12_TEXTURE_ADDRESS_MODE addressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP, + D3D12_TEXTURE_ADDRESS_MODE addressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP, + D3D12_TEXTURE_ADDRESS_MODE addressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP, + FLOAT mipLODBias = 0, + UINT maxAnisotropy = 16, + D3D12_COMPARISON_FUNC comparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL, + D3D12_STATIC_BORDER_COLOR borderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE, + FLOAT minLOD = 0.f, + FLOAT maxLOD = D3D12_FLOAT32_MAX, + D3D12_SHADER_VISIBILITY shaderVisibility = D3D12_SHADER_VISIBILITY_ALL, + UINT registerSpace = 0, + D3D12_SAMPLER_FLAGS flags = D3D12_SAMPLER_FLAGS::D3D12_SAMPLER_FLAG_NONE) noexcept + { + samplerDesc.ShaderRegister = shaderRegister; + samplerDesc.Filter = filter; + samplerDesc.AddressU = addressU; + samplerDesc.AddressV = addressV; + samplerDesc.AddressW = addressW; + samplerDesc.MipLODBias = mipLODBias; + samplerDesc.MaxAnisotropy = maxAnisotropy; + samplerDesc.ComparisonFunc = comparisonFunc; + samplerDesc.BorderColor = borderColor; + samplerDesc.MinLOD = minLOD; + samplerDesc.MaxLOD = maxLOD; + samplerDesc.ShaderVisibility = shaderVisibility; + samplerDesc.RegisterSpace = registerSpace; + samplerDesc.Flags = flags; + } + inline void Init( + UINT shaderRegister, + D3D12_FILTER filter = D3D12_FILTER_ANISOTROPIC, + D3D12_TEXTURE_ADDRESS_MODE addressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP, + D3D12_TEXTURE_ADDRESS_MODE addressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP, + D3D12_TEXTURE_ADDRESS_MODE addressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP, + FLOAT mipLODBias = 0, + UINT maxAnisotropy = 16, + D3D12_COMPARISON_FUNC comparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL, + D3D12_STATIC_BORDER_COLOR borderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE, + FLOAT minLOD = 0.f, + FLOAT maxLOD = D3D12_FLOAT32_MAX, + D3D12_SHADER_VISIBILITY shaderVisibility = D3D12_SHADER_VISIBILITY_ALL, + UINT registerSpace = 0, + D3D12_SAMPLER_FLAGS flags = D3D12_SAMPLER_FLAGS::D3D12_SAMPLER_FLAG_NONE) noexcept + { + Init( + *this, + shaderRegister, + filter, + addressU, + addressV, + addressW, + mipLODBias, + maxAnisotropy, + comparisonFunc, + borderColor, + minLOD, + maxLOD, + shaderVisibility, + registerSpace, + flags); + } +}; +#endif // D3D12_SDK_VERSION >= 609 + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_ROOT_SIGNATURE_DESC : public D3D12_ROOT_SIGNATURE_DESC +{ + CD3DX12_ROOT_SIGNATURE_DESC() = default; + explicit CD3DX12_ROOT_SIGNATURE_DESC(const D3D12_ROOT_SIGNATURE_DESC &o) noexcept : + D3D12_ROOT_SIGNATURE_DESC(o) + {} + CD3DX12_ROOT_SIGNATURE_DESC( + UINT numParameters, + _In_reads_opt_(numParameters) const D3D12_ROOT_PARAMETER* _pParameters, + UINT numStaticSamplers = 0, + _In_reads_opt_(numStaticSamplers) const D3D12_STATIC_SAMPLER_DESC* _pStaticSamplers = nullptr, + D3D12_ROOT_SIGNATURE_FLAGS flags = D3D12_ROOT_SIGNATURE_FLAG_NONE) noexcept + { + Init(numParameters, _pParameters, numStaticSamplers, _pStaticSamplers, flags); + } + CD3DX12_ROOT_SIGNATURE_DESC(CD3DX12_DEFAULT) noexcept + { + Init(0, nullptr, 0, nullptr, D3D12_ROOT_SIGNATURE_FLAG_NONE); + } + + inline void Init( + UINT numParameters, + _In_reads_opt_(numParameters) const D3D12_ROOT_PARAMETER* _pParameters, + UINT numStaticSamplers = 0, + _In_reads_opt_(numStaticSamplers) const D3D12_STATIC_SAMPLER_DESC* _pStaticSamplers = nullptr, + D3D12_ROOT_SIGNATURE_FLAGS flags = D3D12_ROOT_SIGNATURE_FLAG_NONE) noexcept + { + Init(*this, numParameters, _pParameters, numStaticSamplers, _pStaticSamplers, flags); + } + + static inline void Init( + _Out_ D3D12_ROOT_SIGNATURE_DESC &desc, + UINT numParameters, + _In_reads_opt_(numParameters) const D3D12_ROOT_PARAMETER* _pParameters, + UINT numStaticSamplers = 0, + _In_reads_opt_(numStaticSamplers) const D3D12_STATIC_SAMPLER_DESC* _pStaticSamplers = nullptr, + D3D12_ROOT_SIGNATURE_FLAGS flags = D3D12_ROOT_SIGNATURE_FLAG_NONE) noexcept + { + desc.NumParameters = numParameters; + desc.pParameters = _pParameters; + desc.NumStaticSamplers = numStaticSamplers; + desc.pStaticSamplers = _pStaticSamplers; + desc.Flags = flags; + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_DESCRIPTOR_RANGE1 : public D3D12_DESCRIPTOR_RANGE1 +{ + CD3DX12_DESCRIPTOR_RANGE1() = default; + explicit CD3DX12_DESCRIPTOR_RANGE1(const D3D12_DESCRIPTOR_RANGE1 &o) noexcept : + D3D12_DESCRIPTOR_RANGE1(o) + {} + CD3DX12_DESCRIPTOR_RANGE1( + D3D12_DESCRIPTOR_RANGE_TYPE rangeType, + UINT numDescriptors, + UINT baseShaderRegister, + UINT registerSpace = 0, + D3D12_DESCRIPTOR_RANGE_FLAGS flags = D3D12_DESCRIPTOR_RANGE_FLAG_NONE, + UINT offsetInDescriptorsFromTableStart = + D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND) noexcept + { + Init(rangeType, numDescriptors, baseShaderRegister, registerSpace, flags, offsetInDescriptorsFromTableStart); + } + + inline void Init( + D3D12_DESCRIPTOR_RANGE_TYPE rangeType, + UINT numDescriptors, + UINT baseShaderRegister, + UINT registerSpace = 0, + D3D12_DESCRIPTOR_RANGE_FLAGS flags = D3D12_DESCRIPTOR_RANGE_FLAG_NONE, + UINT offsetInDescriptorsFromTableStart = + D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND) noexcept + { + Init(*this, rangeType, numDescriptors, baseShaderRegister, registerSpace, flags, offsetInDescriptorsFromTableStart); + } + + static inline void Init( + _Out_ D3D12_DESCRIPTOR_RANGE1 &range, + D3D12_DESCRIPTOR_RANGE_TYPE rangeType, + UINT numDescriptors, + UINT baseShaderRegister, + UINT registerSpace = 0, + D3D12_DESCRIPTOR_RANGE_FLAGS flags = D3D12_DESCRIPTOR_RANGE_FLAG_NONE, + UINT offsetInDescriptorsFromTableStart = + D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND) noexcept + { + range.RangeType = rangeType; + range.NumDescriptors = numDescriptors; + range.BaseShaderRegister = baseShaderRegister; + range.RegisterSpace = registerSpace; + range.Flags = flags; + range.OffsetInDescriptorsFromTableStart = offsetInDescriptorsFromTableStart; + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_ROOT_DESCRIPTOR_TABLE1 : public D3D12_ROOT_DESCRIPTOR_TABLE1 +{ + CD3DX12_ROOT_DESCRIPTOR_TABLE1() = default; + explicit CD3DX12_ROOT_DESCRIPTOR_TABLE1(const D3D12_ROOT_DESCRIPTOR_TABLE1 &o) noexcept : + D3D12_ROOT_DESCRIPTOR_TABLE1(o) + {} + CD3DX12_ROOT_DESCRIPTOR_TABLE1( + UINT numDescriptorRanges, + _In_reads_opt_(numDescriptorRanges) const D3D12_DESCRIPTOR_RANGE1* _pDescriptorRanges) noexcept + { + Init(numDescriptorRanges, _pDescriptorRanges); + } + + inline void Init( + UINT numDescriptorRanges, + _In_reads_opt_(numDescriptorRanges) const D3D12_DESCRIPTOR_RANGE1* _pDescriptorRanges) noexcept + { + Init(*this, numDescriptorRanges, _pDescriptorRanges); + } + + static inline void Init( + _Out_ D3D12_ROOT_DESCRIPTOR_TABLE1 &rootDescriptorTable, + UINT numDescriptorRanges, + _In_reads_opt_(numDescriptorRanges) const D3D12_DESCRIPTOR_RANGE1* _pDescriptorRanges) noexcept + { + rootDescriptorTable.NumDescriptorRanges = numDescriptorRanges; + rootDescriptorTable.pDescriptorRanges = _pDescriptorRanges; + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_ROOT_DESCRIPTOR1 : public D3D12_ROOT_DESCRIPTOR1 +{ + CD3DX12_ROOT_DESCRIPTOR1() = default; + explicit CD3DX12_ROOT_DESCRIPTOR1(const D3D12_ROOT_DESCRIPTOR1 &o) noexcept : + D3D12_ROOT_DESCRIPTOR1(o) + {} + CD3DX12_ROOT_DESCRIPTOR1( + UINT shaderRegister, + UINT registerSpace = 0, + D3D12_ROOT_DESCRIPTOR_FLAGS flags = D3D12_ROOT_DESCRIPTOR_FLAG_NONE) noexcept + { + Init(shaderRegister, registerSpace, flags); + } + + inline void Init( + UINT shaderRegister, + UINT registerSpace = 0, + D3D12_ROOT_DESCRIPTOR_FLAGS flags = D3D12_ROOT_DESCRIPTOR_FLAG_NONE) noexcept + { + Init(*this, shaderRegister, registerSpace, flags); + } + + static inline void Init( + _Out_ D3D12_ROOT_DESCRIPTOR1 &table, + UINT shaderRegister, + UINT registerSpace = 0, + D3D12_ROOT_DESCRIPTOR_FLAGS flags = D3D12_ROOT_DESCRIPTOR_FLAG_NONE) noexcept + { + table.ShaderRegister = shaderRegister; + table.RegisterSpace = registerSpace; + table.Flags = flags; + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_ROOT_PARAMETER1 : public D3D12_ROOT_PARAMETER1 +{ + CD3DX12_ROOT_PARAMETER1() = default; + explicit CD3DX12_ROOT_PARAMETER1(const D3D12_ROOT_PARAMETER1 &o) noexcept : + D3D12_ROOT_PARAMETER1(o) + {} + + static inline void InitAsDescriptorTable( + _Out_ D3D12_ROOT_PARAMETER1 &rootParam, + UINT numDescriptorRanges, + _In_reads_(numDescriptorRanges) const D3D12_DESCRIPTOR_RANGE1* pDescriptorRanges, + D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept + { + rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + rootParam.ShaderVisibility = visibility; + CD3DX12_ROOT_DESCRIPTOR_TABLE1::Init(rootParam.DescriptorTable, numDescriptorRanges, pDescriptorRanges); + } + + static inline void InitAsConstants( + _Out_ D3D12_ROOT_PARAMETER1 &rootParam, + UINT num32BitValues, + UINT shaderRegister, + UINT registerSpace = 0, + D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept + { + rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS; + rootParam.ShaderVisibility = visibility; + CD3DX12_ROOT_CONSTANTS::Init(rootParam.Constants, num32BitValues, shaderRegister, registerSpace); + } + + static inline void InitAsConstantBufferView( + _Out_ D3D12_ROOT_PARAMETER1 &rootParam, + UINT shaderRegister, + UINT registerSpace = 0, + D3D12_ROOT_DESCRIPTOR_FLAGS flags = D3D12_ROOT_DESCRIPTOR_FLAG_NONE, + D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept + { + rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV; + rootParam.ShaderVisibility = visibility; + CD3DX12_ROOT_DESCRIPTOR1::Init(rootParam.Descriptor, shaderRegister, registerSpace, flags); + } + + static inline void InitAsShaderResourceView( + _Out_ D3D12_ROOT_PARAMETER1 &rootParam, + UINT shaderRegister, + UINT registerSpace = 0, + D3D12_ROOT_DESCRIPTOR_FLAGS flags = D3D12_ROOT_DESCRIPTOR_FLAG_NONE, + D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept + { + rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_SRV; + rootParam.ShaderVisibility = visibility; + CD3DX12_ROOT_DESCRIPTOR1::Init(rootParam.Descriptor, shaderRegister, registerSpace, flags); + } + + static inline void InitAsUnorderedAccessView( + _Out_ D3D12_ROOT_PARAMETER1 &rootParam, + UINT shaderRegister, + UINT registerSpace = 0, + D3D12_ROOT_DESCRIPTOR_FLAGS flags = D3D12_ROOT_DESCRIPTOR_FLAG_NONE, + D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept + { + rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV; + rootParam.ShaderVisibility = visibility; + CD3DX12_ROOT_DESCRIPTOR1::Init(rootParam.Descriptor, shaderRegister, registerSpace, flags); + } + + inline void InitAsDescriptorTable( + UINT numDescriptorRanges, + _In_reads_(numDescriptorRanges) const D3D12_DESCRIPTOR_RANGE1* pDescriptorRanges, + D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept + { + InitAsDescriptorTable(*this, numDescriptorRanges, pDescriptorRanges, visibility); + } + + inline void InitAsConstants( + UINT num32BitValues, + UINT shaderRegister, + UINT registerSpace = 0, + D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept + { + InitAsConstants(*this, num32BitValues, shaderRegister, registerSpace, visibility); + } + + inline void InitAsConstantBufferView( + UINT shaderRegister, + UINT registerSpace = 0, + D3D12_ROOT_DESCRIPTOR_FLAGS flags = D3D12_ROOT_DESCRIPTOR_FLAG_NONE, + D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept + { + InitAsConstantBufferView(*this, shaderRegister, registerSpace, flags, visibility); + } + + inline void InitAsShaderResourceView( + UINT shaderRegister, + UINT registerSpace = 0, + D3D12_ROOT_DESCRIPTOR_FLAGS flags = D3D12_ROOT_DESCRIPTOR_FLAG_NONE, + D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept + { + InitAsShaderResourceView(*this, shaderRegister, registerSpace, flags, visibility); + } + + inline void InitAsUnorderedAccessView( + UINT shaderRegister, + UINT registerSpace = 0, + D3D12_ROOT_DESCRIPTOR_FLAGS flags = D3D12_ROOT_DESCRIPTOR_FLAG_NONE, + D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept + { + InitAsUnorderedAccessView(*this, shaderRegister, registerSpace, flags, visibility); + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC : public D3D12_VERSIONED_ROOT_SIGNATURE_DESC +{ + CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC() = default; + explicit CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC(const D3D12_VERSIONED_ROOT_SIGNATURE_DESC &o) noexcept : + D3D12_VERSIONED_ROOT_SIGNATURE_DESC(o) + {} + explicit CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC(const D3D12_ROOT_SIGNATURE_DESC &o) noexcept + { + Version = D3D_ROOT_SIGNATURE_VERSION_1_0; + Desc_1_0 = o; + } + explicit CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC(const D3D12_ROOT_SIGNATURE_DESC1 &o) noexcept + { + Version = D3D_ROOT_SIGNATURE_VERSION_1_1; + Desc_1_1 = o; + } +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 609) + explicit CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC(const D3D12_ROOT_SIGNATURE_DESC2& o) noexcept + { + Version = D3D_ROOT_SIGNATURE_VERSION_1_2; + Desc_1_2 = o; + } +#endif + CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC( + UINT numParameters, + _In_reads_opt_(numParameters) const D3D12_ROOT_PARAMETER* _pParameters, + UINT numStaticSamplers = 0, + _In_reads_opt_(numStaticSamplers) const D3D12_STATIC_SAMPLER_DESC* _pStaticSamplers = nullptr, + D3D12_ROOT_SIGNATURE_FLAGS flags = D3D12_ROOT_SIGNATURE_FLAG_NONE) noexcept + { + Init_1_0(numParameters, _pParameters, numStaticSamplers, _pStaticSamplers, flags); + } + CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC( + UINT numParameters, + _In_reads_opt_(numParameters) const D3D12_ROOT_PARAMETER1* _pParameters, + UINT numStaticSamplers = 0, + _In_reads_opt_(numStaticSamplers) const D3D12_STATIC_SAMPLER_DESC* _pStaticSamplers = nullptr, + D3D12_ROOT_SIGNATURE_FLAGS flags = D3D12_ROOT_SIGNATURE_FLAG_NONE) noexcept + { + Init_1_1(numParameters, _pParameters, numStaticSamplers, _pStaticSamplers, flags); + } + CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC(CD3DX12_DEFAULT) noexcept + { + Init_1_1(0, nullptr, 0, nullptr, D3D12_ROOT_SIGNATURE_FLAG_NONE); + } + + inline void Init_1_0( + UINT numParameters, + _In_reads_opt_(numParameters) const D3D12_ROOT_PARAMETER* _pParameters, + UINT numStaticSamplers = 0, + _In_reads_opt_(numStaticSamplers) const D3D12_STATIC_SAMPLER_DESC* _pStaticSamplers = nullptr, + D3D12_ROOT_SIGNATURE_FLAGS flags = D3D12_ROOT_SIGNATURE_FLAG_NONE) noexcept + { + Init_1_0(*this, numParameters, _pParameters, numStaticSamplers, _pStaticSamplers, flags); + } + + static inline void Init_1_0( + _Out_ D3D12_VERSIONED_ROOT_SIGNATURE_DESC &desc, + UINT numParameters, + _In_reads_opt_(numParameters) const D3D12_ROOT_PARAMETER* _pParameters, + UINT numStaticSamplers = 0, + _In_reads_opt_(numStaticSamplers) const D3D12_STATIC_SAMPLER_DESC* _pStaticSamplers = nullptr, + D3D12_ROOT_SIGNATURE_FLAGS flags = D3D12_ROOT_SIGNATURE_FLAG_NONE) noexcept + { + desc.Version = D3D_ROOT_SIGNATURE_VERSION_1_0; + desc.Desc_1_0.NumParameters = numParameters; + desc.Desc_1_0.pParameters = _pParameters; + desc.Desc_1_0.NumStaticSamplers = numStaticSamplers; + desc.Desc_1_0.pStaticSamplers = _pStaticSamplers; + desc.Desc_1_0.Flags = flags; + } + + inline void Init_1_1( + UINT numParameters, + _In_reads_opt_(numParameters) const D3D12_ROOT_PARAMETER1* _pParameters, + UINT numStaticSamplers = 0, + _In_reads_opt_(numStaticSamplers) const D3D12_STATIC_SAMPLER_DESC* _pStaticSamplers = nullptr, + D3D12_ROOT_SIGNATURE_FLAGS flags = D3D12_ROOT_SIGNATURE_FLAG_NONE) noexcept + { + Init_1_1(*this, numParameters, _pParameters, numStaticSamplers, _pStaticSamplers, flags); + } + + static inline void Init_1_1( + _Out_ D3D12_VERSIONED_ROOT_SIGNATURE_DESC &desc, + UINT numParameters, + _In_reads_opt_(numParameters) const D3D12_ROOT_PARAMETER1* _pParameters, + UINT numStaticSamplers = 0, + _In_reads_opt_(numStaticSamplers) const D3D12_STATIC_SAMPLER_DESC* _pStaticSamplers = nullptr, + D3D12_ROOT_SIGNATURE_FLAGS flags = D3D12_ROOT_SIGNATURE_FLAG_NONE) noexcept + { + desc.Version = D3D_ROOT_SIGNATURE_VERSION_1_1; + desc.Desc_1_1.NumParameters = numParameters; + desc.Desc_1_1.pParameters = _pParameters; + desc.Desc_1_1.NumStaticSamplers = numStaticSamplers; + desc.Desc_1_1.pStaticSamplers = _pStaticSamplers; + desc.Desc_1_1.Flags = flags; + } +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 609) + static inline void Init_1_2( + _Out_ D3D12_VERSIONED_ROOT_SIGNATURE_DESC& desc, + UINT numParameters, + _In_reads_opt_(numParameters) const D3D12_ROOT_PARAMETER1* _pParameters, + UINT numStaticSamplers = 0, + _In_reads_opt_(numStaticSamplers) const D3D12_STATIC_SAMPLER_DESC1* _pStaticSamplers = nullptr, + D3D12_ROOT_SIGNATURE_FLAGS flags = D3D12_ROOT_SIGNATURE_FLAG_NONE) noexcept + { + desc.Version = D3D_ROOT_SIGNATURE_VERSION_1_2; + desc.Desc_1_2.NumParameters = numParameters; + desc.Desc_1_2.pParameters = _pParameters; + desc.Desc_1_2.NumStaticSamplers = numStaticSamplers; + desc.Desc_1_2.pStaticSamplers = _pStaticSamplers; + desc.Desc_1_2.Flags = flags; + } +#endif +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_CPU_DESCRIPTOR_HANDLE : public D3D12_CPU_DESCRIPTOR_HANDLE +{ + CD3DX12_CPU_DESCRIPTOR_HANDLE() = default; + explicit CD3DX12_CPU_DESCRIPTOR_HANDLE(const D3D12_CPU_DESCRIPTOR_HANDLE &o) noexcept : + D3D12_CPU_DESCRIPTOR_HANDLE(o) + {} + CD3DX12_CPU_DESCRIPTOR_HANDLE(CD3DX12_DEFAULT) noexcept { ptr = 0; } + CD3DX12_CPU_DESCRIPTOR_HANDLE(_In_ const D3D12_CPU_DESCRIPTOR_HANDLE &other, INT offsetScaledByIncrementSize) noexcept + { + InitOffsetted(other, offsetScaledByIncrementSize); + } + CD3DX12_CPU_DESCRIPTOR_HANDLE(_In_ const D3D12_CPU_DESCRIPTOR_HANDLE &other, INT offsetInDescriptors, UINT descriptorIncrementSize) noexcept + { + InitOffsetted(other, offsetInDescriptors, descriptorIncrementSize); + } + CD3DX12_CPU_DESCRIPTOR_HANDLE& Offset(INT offsetInDescriptors, UINT descriptorIncrementSize) noexcept + { + ptr = SIZE_T(INT64(ptr) + INT64(offsetInDescriptors) * INT64(descriptorIncrementSize)); + return *this; + } + CD3DX12_CPU_DESCRIPTOR_HANDLE& Offset(INT offsetScaledByIncrementSize) noexcept + { + ptr = SIZE_T(INT64(ptr) + INT64(offsetScaledByIncrementSize)); + return *this; + } + bool operator==(_In_ const D3D12_CPU_DESCRIPTOR_HANDLE& other) const noexcept + { + return (ptr == other.ptr); + } + bool operator!=(_In_ const D3D12_CPU_DESCRIPTOR_HANDLE& other) const noexcept + { + return (ptr != other.ptr); + } + CD3DX12_CPU_DESCRIPTOR_HANDLE &operator=(const D3D12_CPU_DESCRIPTOR_HANDLE &other) noexcept + { + ptr = other.ptr; + return *this; + } + + inline void InitOffsetted(_In_ const D3D12_CPU_DESCRIPTOR_HANDLE &base, INT offsetScaledByIncrementSize) noexcept + { + InitOffsetted(*this, base, offsetScaledByIncrementSize); + } + + inline void InitOffsetted(_In_ const D3D12_CPU_DESCRIPTOR_HANDLE &base, INT offsetInDescriptors, UINT descriptorIncrementSize) noexcept + { + InitOffsetted(*this, base, offsetInDescriptors, descriptorIncrementSize); + } + + static inline void InitOffsetted(_Out_ D3D12_CPU_DESCRIPTOR_HANDLE &handle, _In_ const D3D12_CPU_DESCRIPTOR_HANDLE &base, INT offsetScaledByIncrementSize) noexcept + { + handle.ptr = SIZE_T(INT64(base.ptr) + INT64(offsetScaledByIncrementSize)); + } + + static inline void InitOffsetted(_Out_ D3D12_CPU_DESCRIPTOR_HANDLE &handle, _In_ const D3D12_CPU_DESCRIPTOR_HANDLE &base, INT offsetInDescriptors, UINT descriptorIncrementSize) noexcept + { + handle.ptr = SIZE_T(INT64(base.ptr) + INT64(offsetInDescriptors) * INT64(descriptorIncrementSize)); + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_GPU_DESCRIPTOR_HANDLE : public D3D12_GPU_DESCRIPTOR_HANDLE +{ + CD3DX12_GPU_DESCRIPTOR_HANDLE() = default; + explicit CD3DX12_GPU_DESCRIPTOR_HANDLE(const D3D12_GPU_DESCRIPTOR_HANDLE &o) noexcept : + D3D12_GPU_DESCRIPTOR_HANDLE(o) + {} + CD3DX12_GPU_DESCRIPTOR_HANDLE(CD3DX12_DEFAULT) noexcept { ptr = 0; } + CD3DX12_GPU_DESCRIPTOR_HANDLE(_In_ const D3D12_GPU_DESCRIPTOR_HANDLE &other, INT offsetScaledByIncrementSize) noexcept + { + InitOffsetted(other, offsetScaledByIncrementSize); + } + CD3DX12_GPU_DESCRIPTOR_HANDLE(_In_ const D3D12_GPU_DESCRIPTOR_HANDLE &other, INT offsetInDescriptors, UINT descriptorIncrementSize) noexcept + { + InitOffsetted(other, offsetInDescriptors, descriptorIncrementSize); + } + CD3DX12_GPU_DESCRIPTOR_HANDLE& Offset(INT offsetInDescriptors, UINT descriptorIncrementSize) noexcept + { + ptr = UINT64(INT64(ptr) + INT64(offsetInDescriptors) * INT64(descriptorIncrementSize)); + return *this; + } + CD3DX12_GPU_DESCRIPTOR_HANDLE& Offset(INT offsetScaledByIncrementSize) noexcept + { + ptr = UINT64(INT64(ptr) + INT64(offsetScaledByIncrementSize)); + return *this; + } + inline bool operator==(_In_ const D3D12_GPU_DESCRIPTOR_HANDLE& other) const noexcept + { + return (ptr == other.ptr); + } + inline bool operator!=(_In_ const D3D12_GPU_DESCRIPTOR_HANDLE& other) const noexcept + { + return (ptr != other.ptr); + } + CD3DX12_GPU_DESCRIPTOR_HANDLE &operator=(const D3D12_GPU_DESCRIPTOR_HANDLE &other) noexcept + { + ptr = other.ptr; + return *this; + } + + inline void InitOffsetted(_In_ const D3D12_GPU_DESCRIPTOR_HANDLE &base, INT offsetScaledByIncrementSize) noexcept + { + InitOffsetted(*this, base, offsetScaledByIncrementSize); + } + + inline void InitOffsetted(_In_ const D3D12_GPU_DESCRIPTOR_HANDLE &base, INT offsetInDescriptors, UINT descriptorIncrementSize) noexcept + { + InitOffsetted(*this, base, offsetInDescriptors, descriptorIncrementSize); + } + + static inline void InitOffsetted(_Out_ D3D12_GPU_DESCRIPTOR_HANDLE &handle, _In_ const D3D12_GPU_DESCRIPTOR_HANDLE &base, INT offsetScaledByIncrementSize) noexcept + { + handle.ptr = UINT64(INT64(base.ptr) + INT64(offsetScaledByIncrementSize)); + } + + static inline void InitOffsetted(_Out_ D3D12_GPU_DESCRIPTOR_HANDLE &handle, _In_ const D3D12_GPU_DESCRIPTOR_HANDLE &base, INT offsetInDescriptors, UINT descriptorIncrementSize) noexcept + { + handle.ptr = UINT64(INT64(base.ptr) + INT64(offsetInDescriptors) * INT64(descriptorIncrementSize)); + } +}; + +//------------------------------------------------------------------------------------------------ +constexpr UINT D3D12CalcSubresource( UINT MipSlice, UINT ArraySlice, UINT PlaneSlice, UINT MipLevels, UINT ArraySize ) noexcept +{ + return MipSlice + ArraySlice * MipLevels + PlaneSlice * MipLevels * ArraySize; +} + +//------------------------------------------------------------------------------------------------ +template +inline void D3D12DecomposeSubresource( UINT Subresource, UINT MipLevels, UINT ArraySize, _Out_ T& MipSlice, _Out_ U& ArraySlice, _Out_ V& PlaneSlice ) noexcept +{ + MipSlice = static_cast(Subresource % MipLevels); + ArraySlice = static_cast((Subresource / MipLevels) % ArraySize); + PlaneSlice = static_cast(Subresource / (MipLevels * ArraySize)); +} + +//------------------------------------------------------------------------------------------------ +inline UINT8 D3D12GetFormatPlaneCount( + _In_ ID3D12Device* pDevice, + DXGI_FORMAT Format + ) noexcept +{ + D3D12_FEATURE_DATA_FORMAT_INFO formatInfo = { Format, 0 }; + if (FAILED(pDevice->CheckFeatureSupport(D3D12_FEATURE_FORMAT_INFO, &formatInfo, sizeof(formatInfo)))) + { + return 0; + } + return formatInfo.PlaneCount; +} + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_RESOURCE_DESC : public D3D12_RESOURCE_DESC +{ + CD3DX12_RESOURCE_DESC() = default; + explicit CD3DX12_RESOURCE_DESC( const D3D12_RESOURCE_DESC& o ) noexcept : + D3D12_RESOURCE_DESC( o ) + {} + CD3DX12_RESOURCE_DESC( + D3D12_RESOURCE_DIMENSION dimension, + UINT64 alignment, + UINT64 width, + UINT height, + UINT16 depthOrArraySize, + UINT16 mipLevels, + DXGI_FORMAT format, + UINT sampleCount, + UINT sampleQuality, + D3D12_TEXTURE_LAYOUT layout, + D3D12_RESOURCE_FLAGS flags ) noexcept + { + Dimension = dimension; + Alignment = alignment; + Width = width; + Height = height; + DepthOrArraySize = depthOrArraySize; + MipLevels = mipLevels; + Format = format; + SampleDesc.Count = sampleCount; + SampleDesc.Quality = sampleQuality; + Layout = layout; + Flags = flags; + } + static inline CD3DX12_RESOURCE_DESC Buffer( + const D3D12_RESOURCE_ALLOCATION_INFO& resAllocInfo, + D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE ) noexcept + { + return CD3DX12_RESOURCE_DESC( D3D12_RESOURCE_DIMENSION_BUFFER, resAllocInfo.Alignment, resAllocInfo.SizeInBytes, + 1, 1, 1, DXGI_FORMAT_UNKNOWN, 1, 0, D3D12_TEXTURE_LAYOUT_ROW_MAJOR, flags ); + } + static inline CD3DX12_RESOURCE_DESC Buffer( + UINT64 width, + D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE, + UINT64 alignment = 0 ) noexcept + { + return CD3DX12_RESOURCE_DESC( D3D12_RESOURCE_DIMENSION_BUFFER, alignment, width, 1, 1, 1, + DXGI_FORMAT_UNKNOWN, 1, 0, D3D12_TEXTURE_LAYOUT_ROW_MAJOR, flags ); + } + static inline CD3DX12_RESOURCE_DESC Tex1D( + DXGI_FORMAT format, + UINT64 width, + UINT16 arraySize = 1, + UINT16 mipLevels = 0, + D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE, + D3D12_TEXTURE_LAYOUT layout = D3D12_TEXTURE_LAYOUT_UNKNOWN, + UINT64 alignment = 0 ) noexcept + { + return CD3DX12_RESOURCE_DESC( D3D12_RESOURCE_DIMENSION_TEXTURE1D, alignment, width, 1, arraySize, + mipLevels, format, 1, 0, layout, flags ); + } + static inline CD3DX12_RESOURCE_DESC Tex2D( + DXGI_FORMAT format, + UINT64 width, + UINT height, + UINT16 arraySize = 1, + UINT16 mipLevels = 0, + UINT sampleCount = 1, + UINT sampleQuality = 0, + D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE, + D3D12_TEXTURE_LAYOUT layout = D3D12_TEXTURE_LAYOUT_UNKNOWN, + UINT64 alignment = 0 ) noexcept + { + return CD3DX12_RESOURCE_DESC( D3D12_RESOURCE_DIMENSION_TEXTURE2D, alignment, width, height, arraySize, + mipLevels, format, sampleCount, sampleQuality, layout, flags ); + } + static inline CD3DX12_RESOURCE_DESC Tex3D( + DXGI_FORMAT format, + UINT64 width, + UINT height, + UINT16 depth, + UINT16 mipLevels = 0, + D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE, + D3D12_TEXTURE_LAYOUT layout = D3D12_TEXTURE_LAYOUT_UNKNOWN, + UINT64 alignment = 0 ) noexcept + { + return CD3DX12_RESOURCE_DESC( D3D12_RESOURCE_DIMENSION_TEXTURE3D, alignment, width, height, depth, + mipLevels, format, 1, 0, layout, flags ); + } + inline UINT16 Depth() const noexcept + { return (Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D ? DepthOrArraySize : 1u); } + inline UINT16 ArraySize() const noexcept + { return (Dimension != D3D12_RESOURCE_DIMENSION_TEXTURE3D ? DepthOrArraySize : 1u); } + inline UINT8 PlaneCount(_In_ ID3D12Device* pDevice) const noexcept + { return D3D12GetFormatPlaneCount(pDevice, Format); } + inline UINT Subresources(_In_ ID3D12Device* pDevice) const noexcept + { return static_cast(MipLevels) * ArraySize() * PlaneCount(pDevice); } + inline UINT CalcSubresource(UINT MipSlice, UINT ArraySlice, UINT PlaneSlice) noexcept + { return D3D12CalcSubresource(MipSlice, ArraySlice, PlaneSlice, MipLevels, ArraySize()); } +}; +inline bool operator==( const D3D12_RESOURCE_DESC& l, const D3D12_RESOURCE_DESC& r ) noexcept +{ + return l.Dimension == r.Dimension && + l.Alignment == r.Alignment && + l.Width == r.Width && + l.Height == r.Height && + l.DepthOrArraySize == r.DepthOrArraySize && + l.MipLevels == r.MipLevels && + l.Format == r.Format && + l.SampleDesc.Count == r.SampleDesc.Count && + l.SampleDesc.Quality == r.SampleDesc.Quality && + l.Layout == r.Layout && + l.Flags == r.Flags; +} +inline bool operator!=( const D3D12_RESOURCE_DESC& l, const D3D12_RESOURCE_DESC& r ) noexcept +{ return !( l == r ); } + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_RESOURCE_DESC1 : public D3D12_RESOURCE_DESC1 +{ + CD3DX12_RESOURCE_DESC1() = default; + explicit CD3DX12_RESOURCE_DESC1( const D3D12_RESOURCE_DESC1& o ) noexcept : + D3D12_RESOURCE_DESC1( o ) + {} + explicit CD3DX12_RESOURCE_DESC1( const D3D12_RESOURCE_DESC& o ) noexcept + { + Dimension = o.Dimension; + Alignment = o.Alignment; + Width = o.Width; + Height = o.Height; + DepthOrArraySize = o.DepthOrArraySize; + MipLevels = o.MipLevels; + Format = o.Format; + SampleDesc = o.SampleDesc; + Layout = o.Layout; + Flags = o.Flags; + SamplerFeedbackMipRegion = {}; + } + CD3DX12_RESOURCE_DESC1( + D3D12_RESOURCE_DIMENSION dimension, + UINT64 alignment, + UINT64 width, + UINT height, + UINT16 depthOrArraySize, + UINT16 mipLevels, + DXGI_FORMAT format, + UINT sampleCount, + UINT sampleQuality, + D3D12_TEXTURE_LAYOUT layout, + D3D12_RESOURCE_FLAGS flags, + UINT samplerFeedbackMipRegionWidth = 0, + UINT samplerFeedbackMipRegionHeight = 0, + UINT samplerFeedbackMipRegionDepth = 0) noexcept + { + Dimension = dimension; + Alignment = alignment; + Width = width; + Height = height; + DepthOrArraySize = depthOrArraySize; + MipLevels = mipLevels; + Format = format; + SampleDesc.Count = sampleCount; + SampleDesc.Quality = sampleQuality; + Layout = layout; + Flags = flags; + SamplerFeedbackMipRegion.Width = samplerFeedbackMipRegionWidth; + SamplerFeedbackMipRegion.Height = samplerFeedbackMipRegionHeight; + SamplerFeedbackMipRegion.Depth = samplerFeedbackMipRegionDepth; + } + static inline CD3DX12_RESOURCE_DESC1 Buffer( + const D3D12_RESOURCE_ALLOCATION_INFO& resAllocInfo, + D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE ) noexcept + { + return CD3DX12_RESOURCE_DESC1( D3D12_RESOURCE_DIMENSION_BUFFER, resAllocInfo.Alignment, resAllocInfo.SizeInBytes, + 1, 1, 1, DXGI_FORMAT_UNKNOWN, 1, 0, D3D12_TEXTURE_LAYOUT_ROW_MAJOR, flags, 0, 0, 0 ); + } + static inline CD3DX12_RESOURCE_DESC1 Buffer( + UINT64 width, + D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE, + UINT64 alignment = 0 ) noexcept + { + return CD3DX12_RESOURCE_DESC1( D3D12_RESOURCE_DIMENSION_BUFFER, alignment, width, 1, 1, 1, + DXGI_FORMAT_UNKNOWN, 1, 0, D3D12_TEXTURE_LAYOUT_ROW_MAJOR, flags, 0, 0, 0 ); + } + static inline CD3DX12_RESOURCE_DESC1 Tex1D( + DXGI_FORMAT format, + UINT64 width, + UINT16 arraySize = 1, + UINT16 mipLevels = 0, + D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE, + D3D12_TEXTURE_LAYOUT layout = D3D12_TEXTURE_LAYOUT_UNKNOWN, + UINT64 alignment = 0 ) noexcept + { + return CD3DX12_RESOURCE_DESC1( D3D12_RESOURCE_DIMENSION_TEXTURE1D, alignment, width, 1, arraySize, + mipLevels, format, 1, 0, layout, flags, 0, 0, 0 ); + } + static inline CD3DX12_RESOURCE_DESC1 Tex2D( + DXGI_FORMAT format, + UINT64 width, + UINT height, + UINT16 arraySize = 1, + UINT16 mipLevels = 0, + UINT sampleCount = 1, + UINT sampleQuality = 0, + D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE, + D3D12_TEXTURE_LAYOUT layout = D3D12_TEXTURE_LAYOUT_UNKNOWN, + UINT64 alignment = 0, + UINT samplerFeedbackMipRegionWidth = 0, + UINT samplerFeedbackMipRegionHeight = 0, + UINT samplerFeedbackMipRegionDepth = 0) noexcept + { + return CD3DX12_RESOURCE_DESC1( D3D12_RESOURCE_DIMENSION_TEXTURE2D, alignment, width, height, arraySize, + mipLevels, format, sampleCount, sampleQuality, layout, flags, samplerFeedbackMipRegionWidth, + samplerFeedbackMipRegionHeight, samplerFeedbackMipRegionDepth ); + } + static inline CD3DX12_RESOURCE_DESC1 Tex3D( + DXGI_FORMAT format, + UINT64 width, + UINT height, + UINT16 depth, + UINT16 mipLevels = 0, + D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE, + D3D12_TEXTURE_LAYOUT layout = D3D12_TEXTURE_LAYOUT_UNKNOWN, + UINT64 alignment = 0 ) noexcept + { + return CD3DX12_RESOURCE_DESC1( D3D12_RESOURCE_DIMENSION_TEXTURE3D, alignment, width, height, depth, + mipLevels, format, 1, 0, layout, flags, 0, 0, 0 ); + } + inline UINT16 Depth() const noexcept + { return (Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D ? DepthOrArraySize : 1u); } + inline UINT16 ArraySize() const noexcept + { return (Dimension != D3D12_RESOURCE_DIMENSION_TEXTURE3D ? DepthOrArraySize : 1u); } + inline UINT8 PlaneCount(_In_ ID3D12Device* pDevice) const noexcept + { return D3D12GetFormatPlaneCount(pDevice, Format); } + inline UINT Subresources(_In_ ID3D12Device* pDevice) const noexcept + { return static_cast(MipLevels) * ArraySize() * PlaneCount(pDevice); } + inline UINT CalcSubresource(UINT MipSlice, UINT ArraySlice, UINT PlaneSlice) noexcept + { return D3D12CalcSubresource(MipSlice, ArraySlice, PlaneSlice, MipLevels, ArraySize()); } +}; +inline bool operator==( const D3D12_RESOURCE_DESC1& l, const D3D12_RESOURCE_DESC1& r ) noexcept +{ + return l.Dimension == r.Dimension && + l.Alignment == r.Alignment && + l.Width == r.Width && + l.Height == r.Height && + l.DepthOrArraySize == r.DepthOrArraySize && + l.MipLevels == r.MipLevels && + l.Format == r.Format && + l.SampleDesc.Count == r.SampleDesc.Count && + l.SampleDesc.Quality == r.SampleDesc.Quality && + l.Layout == r.Layout && + l.Flags == r.Flags && + l.SamplerFeedbackMipRegion.Width == r.SamplerFeedbackMipRegion.Width && + l.SamplerFeedbackMipRegion.Height == r.SamplerFeedbackMipRegion.Height && + l.SamplerFeedbackMipRegion.Depth == r.SamplerFeedbackMipRegion.Depth; +} +inline bool operator!=( const D3D12_RESOURCE_DESC1& l, const D3D12_RESOURCE_DESC1& r ) noexcept +{ return !( l == r ); } + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_VIEW_INSTANCING_DESC : public D3D12_VIEW_INSTANCING_DESC +{ + CD3DX12_VIEW_INSTANCING_DESC() = default; + explicit CD3DX12_VIEW_INSTANCING_DESC( const D3D12_VIEW_INSTANCING_DESC& o ) noexcept : + D3D12_VIEW_INSTANCING_DESC( o ) + {} + explicit CD3DX12_VIEW_INSTANCING_DESC( CD3DX12_DEFAULT ) noexcept + { + ViewInstanceCount = 0; + pViewInstanceLocations = nullptr; + Flags = D3D12_VIEW_INSTANCING_FLAG_NONE; + } + explicit CD3DX12_VIEW_INSTANCING_DESC( + UINT InViewInstanceCount, + const D3D12_VIEW_INSTANCE_LOCATION* InViewInstanceLocations, + D3D12_VIEW_INSTANCING_FLAGS InFlags) noexcept + { + ViewInstanceCount = InViewInstanceCount; + pViewInstanceLocations = InViewInstanceLocations; + Flags = InFlags; + } +}; + +//------------------------------------------------------------------------------------------------ +// Row-by-row memcpy +inline void MemcpySubresource( + _In_ const D3D12_MEMCPY_DEST* pDest, + _In_ const D3D12_SUBRESOURCE_DATA* pSrc, + SIZE_T RowSizeInBytes, + UINT NumRows, + UINT NumSlices) noexcept +{ + for (UINT z = 0; z < NumSlices; ++z) + { + auto pDestSlice = static_cast(pDest->pData) + pDest->SlicePitch * z; + auto pSrcSlice = static_cast(pSrc->pData) + pSrc->SlicePitch * LONG_PTR(z); + for (UINT y = 0; y < NumRows; ++y) + { + memcpy(pDestSlice + pDest->RowPitch * y, + pSrcSlice + pSrc->RowPitch * LONG_PTR(y), + RowSizeInBytes); + } + } +} + +//------------------------------------------------------------------------------------------------ +// Row-by-row memcpy +inline void MemcpySubresource( + _In_ const D3D12_MEMCPY_DEST* pDest, + _In_ const void* pResourceData, + _In_ const D3D12_SUBRESOURCE_INFO* pSrc, + SIZE_T RowSizeInBytes, + UINT NumRows, + UINT NumSlices) noexcept +{ + for (UINT z = 0; z < NumSlices; ++z) + { + auto pDestSlice = static_cast(pDest->pData) + pDest->SlicePitch * z; + auto pSrcSlice = (static_cast(pResourceData) + pSrc->Offset) + pSrc->DepthPitch * ULONG_PTR(z); + for (UINT y = 0; y < NumRows; ++y) + { + memcpy(pDestSlice + pDest->RowPitch * y, + pSrcSlice + pSrc->RowPitch * ULONG_PTR(y), + RowSizeInBytes); + } + } +} + +//------------------------------------------------------------------------------------------------ +// Returns required size of a buffer to be used for data upload +inline UINT64 GetRequiredIntermediateSize( + _In_ ID3D12Resource* pDestinationResource, + _In_range_(0,D3D12_REQ_SUBRESOURCES) UINT FirstSubresource, + _In_range_(0,D3D12_REQ_SUBRESOURCES-FirstSubresource) UINT NumSubresources) noexcept +{ +#if defined(_MSC_VER) || !defined(_WIN32) + const auto Desc = pDestinationResource->GetDesc(); +#else + D3D12_RESOURCE_DESC tmpDesc; + const auto& Desc = *pDestinationResource->GetDesc(&tmpDesc); +#endif + UINT64 RequiredSize = 0; + + ID3D12Device* pDevice = nullptr; + pDestinationResource->GetDevice(IID_ID3D12Device, reinterpret_cast(&pDevice)); + pDevice->GetCopyableFootprints(&Desc, FirstSubresource, NumSubresources, 0, nullptr, nullptr, nullptr, &RequiredSize); + pDevice->Release(); + + return RequiredSize; +} + +//------------------------------------------------------------------------------------------------ +// All arrays must be populated (e.g. by calling GetCopyableFootprints) +inline UINT64 UpdateSubresources( + _In_ ID3D12GraphicsCommandList* pCmdList, + _In_ ID3D12Resource* pDestinationResource, + _In_ ID3D12Resource* pIntermediate, + _In_range_(0,D3D12_REQ_SUBRESOURCES) UINT FirstSubresource, + _In_range_(0,D3D12_REQ_SUBRESOURCES-FirstSubresource) UINT NumSubresources, + UINT64 RequiredSize, + _In_reads_(NumSubresources) const D3D12_PLACED_SUBRESOURCE_FOOTPRINT* pLayouts, + _In_reads_(NumSubresources) const UINT* pNumRows, + _In_reads_(NumSubresources) const UINT64* pRowSizesInBytes, + _In_reads_(NumSubresources) const D3D12_SUBRESOURCE_DATA* pSrcData) noexcept +{ + // Minor validation +#if defined(_MSC_VER) || !defined(_WIN32) + const auto IntermediateDesc = pIntermediate->GetDesc(); + const auto DestinationDesc = pDestinationResource->GetDesc(); +#else + D3D12_RESOURCE_DESC tmpDesc1, tmpDesc2; + const auto& IntermediateDesc = *pIntermediate->GetDesc(&tmpDesc1); + const auto& DestinationDesc = *pDestinationResource->GetDesc(&tmpDesc2); +#endif + if (IntermediateDesc.Dimension != D3D12_RESOURCE_DIMENSION_BUFFER || + IntermediateDesc.Width < RequiredSize + pLayouts[0].Offset || + RequiredSize > SIZE_T(-1) || + (DestinationDesc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER && + (FirstSubresource != 0 || NumSubresources != 1))) + { + return 0; + } + + BYTE* pData; + HRESULT hr = pIntermediate->Map(0, nullptr, reinterpret_cast(&pData)); + if (FAILED(hr)) + { + return 0; + } + + for (UINT i = 0; i < NumSubresources; ++i) + { + if (pRowSizesInBytes[i] > SIZE_T(-1)) return 0; + D3D12_MEMCPY_DEST DestData = { pData + pLayouts[i].Offset, pLayouts[i].Footprint.RowPitch, SIZE_T(pLayouts[i].Footprint.RowPitch) * SIZE_T(pNumRows[i]) }; + MemcpySubresource(&DestData, &pSrcData[i], static_cast(pRowSizesInBytes[i]), pNumRows[i], pLayouts[i].Footprint.Depth); + } + pIntermediate->Unmap(0, nullptr); + + if (DestinationDesc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER) + { + pCmdList->CopyBufferRegion( + pDestinationResource, 0, pIntermediate, pLayouts[0].Offset, pLayouts[0].Footprint.Width); + } + else + { + for (UINT i = 0; i < NumSubresources; ++i) + { + const CD3DX12_TEXTURE_COPY_LOCATION Dst(pDestinationResource, i + FirstSubresource); + const CD3DX12_TEXTURE_COPY_LOCATION Src(pIntermediate, pLayouts[i]); + pCmdList->CopyTextureRegion(&Dst, 0, 0, 0, &Src, nullptr); + } + } + return RequiredSize; +} + +//------------------------------------------------------------------------------------------------ +// All arrays must be populated (e.g. by calling GetCopyableFootprints) +inline UINT64 UpdateSubresources( + _In_ ID3D12GraphicsCommandList* pCmdList, + _In_ ID3D12Resource* pDestinationResource, + _In_ ID3D12Resource* pIntermediate, + _In_range_(0,D3D12_REQ_SUBRESOURCES) UINT FirstSubresource, + _In_range_(0,D3D12_REQ_SUBRESOURCES-FirstSubresource) UINT NumSubresources, + UINT64 RequiredSize, + _In_reads_(NumSubresources) const D3D12_PLACED_SUBRESOURCE_FOOTPRINT* pLayouts, + _In_reads_(NumSubresources) const UINT* pNumRows, + _In_reads_(NumSubresources) const UINT64* pRowSizesInBytes, + _In_ const void* pResourceData, + _In_reads_(NumSubresources) const D3D12_SUBRESOURCE_INFO* pSrcData) noexcept +{ + // Minor validation +#if defined(_MSC_VER) || !defined(_WIN32) + const auto IntermediateDesc = pIntermediate->GetDesc(); + const auto DestinationDesc = pDestinationResource->GetDesc(); +#else + D3D12_RESOURCE_DESC tmpDesc1, tmpDesc2; + const auto& IntermediateDesc = *pIntermediate->GetDesc(&tmpDesc1); + const auto& DestinationDesc = *pDestinationResource->GetDesc(&tmpDesc2); +#endif + if (IntermediateDesc.Dimension != D3D12_RESOURCE_DIMENSION_BUFFER || + IntermediateDesc.Width < RequiredSize + pLayouts[0].Offset || + RequiredSize > SIZE_T(-1) || + (DestinationDesc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER && + (FirstSubresource != 0 || NumSubresources != 1))) + { + return 0; + } + + BYTE* pData; + HRESULT hr = pIntermediate->Map(0, nullptr, reinterpret_cast(&pData)); + if (FAILED(hr)) + { + return 0; + } + + for (UINT i = 0; i < NumSubresources; ++i) + { + if (pRowSizesInBytes[i] > SIZE_T(-1)) return 0; + D3D12_MEMCPY_DEST DestData = { pData + pLayouts[i].Offset, pLayouts[i].Footprint.RowPitch, SIZE_T(pLayouts[i].Footprint.RowPitch) * SIZE_T(pNumRows[i]) }; + MemcpySubresource(&DestData, pResourceData, &pSrcData[i], static_cast(pRowSizesInBytes[i]), pNumRows[i], pLayouts[i].Footprint.Depth); + } + pIntermediate->Unmap(0, nullptr); + + if (DestinationDesc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER) + { + pCmdList->CopyBufferRegion( + pDestinationResource, 0, pIntermediate, pLayouts[0].Offset, pLayouts[0].Footprint.Width); + } + else + { + for (UINT i = 0; i < NumSubresources; ++i) + { + const CD3DX12_TEXTURE_COPY_LOCATION Dst(pDestinationResource, i + FirstSubresource); + const CD3DX12_TEXTURE_COPY_LOCATION Src(pIntermediate, pLayouts[i]); + pCmdList->CopyTextureRegion(&Dst, 0, 0, 0, &Src, nullptr); + } + } + return RequiredSize; +} + +//------------------------------------------------------------------------------------------------ +// Heap-allocating UpdateSubresources implementation +inline UINT64 UpdateSubresources( + _In_ ID3D12GraphicsCommandList* pCmdList, + _In_ ID3D12Resource* pDestinationResource, + _In_ ID3D12Resource* pIntermediate, + UINT64 IntermediateOffset, + _In_range_(0,D3D12_REQ_SUBRESOURCES) UINT FirstSubresource, + _In_range_(0,D3D12_REQ_SUBRESOURCES-FirstSubresource) UINT NumSubresources, + _In_reads_(NumSubresources) const D3D12_SUBRESOURCE_DATA* pSrcData) noexcept +{ + UINT64 RequiredSize = 0; + const auto MemToAlloc = static_cast(sizeof(D3D12_PLACED_SUBRESOURCE_FOOTPRINT) + sizeof(UINT) + sizeof(UINT64)) * NumSubresources; + if (MemToAlloc > SIZE_MAX) + { + return 0; + } + void* pMem = HeapAlloc(GetProcessHeap(), 0, static_cast(MemToAlloc)); + if (pMem == nullptr) + { + return 0; + } + auto pLayouts = static_cast(pMem); + auto pRowSizesInBytes = reinterpret_cast(pLayouts + NumSubresources); + auto pNumRows = reinterpret_cast(pRowSizesInBytes + NumSubresources); + +#if defined(_MSC_VER) || !defined(_WIN32) + const auto Desc = pDestinationResource->GetDesc(); +#else + D3D12_RESOURCE_DESC tmpDesc; + const auto& Desc = *pDestinationResource->GetDesc(&tmpDesc); +#endif + ID3D12Device* pDevice = nullptr; + pDestinationResource->GetDevice(IID_ID3D12Device, reinterpret_cast(&pDevice)); + pDevice->GetCopyableFootprints(&Desc, FirstSubresource, NumSubresources, IntermediateOffset, pLayouts, pNumRows, pRowSizesInBytes, &RequiredSize); + pDevice->Release(); + + const UINT64 Result = UpdateSubresources(pCmdList, pDestinationResource, pIntermediate, FirstSubresource, NumSubresources, RequiredSize, pLayouts, pNumRows, pRowSizesInBytes, pSrcData); + HeapFree(GetProcessHeap(), 0, pMem); + return Result; +} + +//------------------------------------------------------------------------------------------------ +// Heap-allocating UpdateSubresources implementation +inline UINT64 UpdateSubresources( + _In_ ID3D12GraphicsCommandList* pCmdList, + _In_ ID3D12Resource* pDestinationResource, + _In_ ID3D12Resource* pIntermediate, + UINT64 IntermediateOffset, + _In_range_(0,D3D12_REQ_SUBRESOURCES) UINT FirstSubresource, + _In_range_(0,D3D12_REQ_SUBRESOURCES-FirstSubresource) UINT NumSubresources, + _In_ const void* pResourceData, + _In_reads_(NumSubresources) const D3D12_SUBRESOURCE_INFO* pSrcData) noexcept +{ + UINT64 RequiredSize = 0; + const auto MemToAlloc = static_cast(sizeof(D3D12_PLACED_SUBRESOURCE_FOOTPRINT) + sizeof(UINT) + sizeof(UINT64)) * NumSubresources; + if (MemToAlloc > SIZE_MAX) + { + return 0; + } + void* pMem = HeapAlloc(GetProcessHeap(), 0, static_cast(MemToAlloc)); + if (pMem == nullptr) + { + return 0; + } + auto pLayouts = static_cast(pMem); + auto pRowSizesInBytes = reinterpret_cast(pLayouts + NumSubresources); + auto pNumRows = reinterpret_cast(pRowSizesInBytes + NumSubresources); + +#if defined(_MSC_VER) || !defined(_WIN32) + const auto Desc = pDestinationResource->GetDesc(); +#else + D3D12_RESOURCE_DESC tmpDesc; + const auto& Desc = *pDestinationResource->GetDesc(&tmpDesc); +#endif + ID3D12Device* pDevice = nullptr; + pDestinationResource->GetDevice(IID_ID3D12Device, reinterpret_cast(&pDevice)); + pDevice->GetCopyableFootprints(&Desc, FirstSubresource, NumSubresources, IntermediateOffset, pLayouts, pNumRows, pRowSizesInBytes, &RequiredSize); + pDevice->Release(); + + const UINT64 Result = UpdateSubresources(pCmdList, pDestinationResource, pIntermediate, FirstSubresource, NumSubresources, RequiredSize, pLayouts, pNumRows, pRowSizesInBytes, pResourceData, pSrcData); + HeapFree(GetProcessHeap(), 0, pMem); + return Result; +} + +//------------------------------------------------------------------------------------------------ +// Stack-allocating UpdateSubresources implementation +template +inline UINT64 UpdateSubresources( + _In_ ID3D12GraphicsCommandList* pCmdList, + _In_ ID3D12Resource* pDestinationResource, + _In_ ID3D12Resource* pIntermediate, + UINT64 IntermediateOffset, + _In_range_(0,MaxSubresources) UINT FirstSubresource, + _In_range_(1,MaxSubresources-FirstSubresource) UINT NumSubresources, + _In_reads_(NumSubresources) const D3D12_SUBRESOURCE_DATA* pSrcData) noexcept +{ + UINT64 RequiredSize = 0; + D3D12_PLACED_SUBRESOURCE_FOOTPRINT Layouts[MaxSubresources]; + UINT NumRows[MaxSubresources]; + UINT64 RowSizesInBytes[MaxSubresources]; + +#if defined(_MSC_VER) || !defined(_WIN32) + const auto Desc = pDestinationResource->GetDesc(); +#else + D3D12_RESOURCE_DESC tmpDesc; + const auto& Desc = *pDestinationResource->GetDesc(&tmpDesc); +#endif + ID3D12Device* pDevice = nullptr; + pDestinationResource->GetDevice(IID_ID3D12Device, reinterpret_cast(&pDevice)); + pDevice->GetCopyableFootprints(&Desc, FirstSubresource, NumSubresources, IntermediateOffset, Layouts, NumRows, RowSizesInBytes, &RequiredSize); + pDevice->Release(); + + return UpdateSubresources(pCmdList, pDestinationResource, pIntermediate, FirstSubresource, NumSubresources, RequiredSize, Layouts, NumRows, RowSizesInBytes, pSrcData); +} + +//------------------------------------------------------------------------------------------------ +// Stack-allocating UpdateSubresources implementation +template +inline UINT64 UpdateSubresources( + _In_ ID3D12GraphicsCommandList* pCmdList, + _In_ ID3D12Resource* pDestinationResource, + _In_ ID3D12Resource* pIntermediate, + UINT64 IntermediateOffset, + _In_range_(0,MaxSubresources) UINT FirstSubresource, + _In_range_(1,MaxSubresources-FirstSubresource) UINT NumSubresources, + _In_ const void* pResourceData, + _In_reads_(NumSubresources) const D3D12_SUBRESOURCE_INFO* pSrcData) noexcept +{ + UINT64 RequiredSize = 0; + D3D12_PLACED_SUBRESOURCE_FOOTPRINT Layouts[MaxSubresources]; + UINT NumRows[MaxSubresources]; + UINT64 RowSizesInBytes[MaxSubresources]; + +#if defined(_MSC_VER) || !defined(_WIN32) + const auto Desc = pDestinationResource->GetDesc(); +#else + D3D12_RESOURCE_DESC tmpDesc; + const auto& Desc = *pDestinationResource->GetDesc(&tmpDesc); +#endif + ID3D12Device* pDevice = nullptr; + pDestinationResource->GetDevice(IID_ID3D12Device, reinterpret_cast(&pDevice)); + pDevice->GetCopyableFootprints(&Desc, FirstSubresource, NumSubresources, IntermediateOffset, Layouts, NumRows, RowSizesInBytes, &RequiredSize); + pDevice->Release(); + + return UpdateSubresources(pCmdList, pDestinationResource, pIntermediate, FirstSubresource, NumSubresources, RequiredSize, Layouts, NumRows, RowSizesInBytes, pResourceData, pSrcData); +} + +//------------------------------------------------------------------------------------------------ +constexpr bool D3D12IsLayoutOpaque( D3D12_TEXTURE_LAYOUT Layout ) noexcept +{ return Layout == D3D12_TEXTURE_LAYOUT_UNKNOWN || Layout == D3D12_TEXTURE_LAYOUT_64KB_UNDEFINED_SWIZZLE; } + +//------------------------------------------------------------------------------------------------ +template +inline ID3D12CommandList * const * CommandListCast(t_CommandListType * const * pp) noexcept +{ + // This cast is useful for passing strongly typed command list pointers into + // ExecuteCommandLists. + // This cast is valid as long as the const-ness is respected. D3D12 APIs do + // respect the const-ness of their arguments. + return reinterpret_cast(pp); +} + +//------------------------------------------------------------------------------------------------ +// D3D12 exports a new method for serializing root signatures in the Windows 10 Anniversary Update. +// To help enable root signature 1.1 features when they are available and not require maintaining +// two code paths for building root signatures, this helper method reconstructs a 1.0 signature when +// 1.1 is not supported. +inline HRESULT D3DX12SerializeVersionedRootSignature( + _In_ const D3D12_VERSIONED_ROOT_SIGNATURE_DESC* pRootSignatureDesc, + D3D_ROOT_SIGNATURE_VERSION MaxVersion, + _Outptr_ ID3DBlob** ppBlob, + _Always_(_Outptr_opt_result_maybenull_) ID3DBlob** ppErrorBlob) noexcept +{ + if (ppErrorBlob != nullptr) + { + *ppErrorBlob = nullptr; + } + + switch (MaxVersion) + { + case D3D_ROOT_SIGNATURE_VERSION_1_0: + switch (pRootSignatureDesc->Version) + { + case D3D_ROOT_SIGNATURE_VERSION_1_0: + return D3D12SerializeRootSignature(&pRootSignatureDesc->Desc_1_0, D3D_ROOT_SIGNATURE_VERSION_1, ppBlob, ppErrorBlob); + + case D3D_ROOT_SIGNATURE_VERSION_1_1: +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 609) + case D3D_ROOT_SIGNATURE_VERSION_1_2: +#endif + { + HRESULT hr = S_OK; + const D3D12_ROOT_SIGNATURE_DESC1& desc_1_1 = pRootSignatureDesc->Desc_1_1; + + const SIZE_T ParametersSize = sizeof(D3D12_ROOT_PARAMETER) * desc_1_1.NumParameters; + void* pParameters = (ParametersSize > 0) ? HeapAlloc(GetProcessHeap(), 0, ParametersSize) : nullptr; + if (ParametersSize > 0 && pParameters == nullptr) + { + hr = E_OUTOFMEMORY; + } + auto pParameters_1_0 = static_cast(pParameters); + + if (SUCCEEDED(hr)) + { + for (UINT n = 0; n < desc_1_1.NumParameters; n++) + { + __analysis_assume(ParametersSize == sizeof(D3D12_ROOT_PARAMETER) * desc_1_1.NumParameters); + pParameters_1_0[n].ParameterType = desc_1_1.pParameters[n].ParameterType; + pParameters_1_0[n].ShaderVisibility = desc_1_1.pParameters[n].ShaderVisibility; + + switch (desc_1_1.pParameters[n].ParameterType) + { + case D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS: + pParameters_1_0[n].Constants.Num32BitValues = desc_1_1.pParameters[n].Constants.Num32BitValues; + pParameters_1_0[n].Constants.RegisterSpace = desc_1_1.pParameters[n].Constants.RegisterSpace; + pParameters_1_0[n].Constants.ShaderRegister = desc_1_1.pParameters[n].Constants.ShaderRegister; + break; + + case D3D12_ROOT_PARAMETER_TYPE_CBV: + case D3D12_ROOT_PARAMETER_TYPE_SRV: + case D3D12_ROOT_PARAMETER_TYPE_UAV: + pParameters_1_0[n].Descriptor.RegisterSpace = desc_1_1.pParameters[n].Descriptor.RegisterSpace; + pParameters_1_0[n].Descriptor.ShaderRegister = desc_1_1.pParameters[n].Descriptor.ShaderRegister; + break; + + case D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE: + const D3D12_ROOT_DESCRIPTOR_TABLE1& table_1_1 = desc_1_1.pParameters[n].DescriptorTable; + + const SIZE_T DescriptorRangesSize = sizeof(D3D12_DESCRIPTOR_RANGE) * table_1_1.NumDescriptorRanges; + void* pDescriptorRanges = (DescriptorRangesSize > 0 && SUCCEEDED(hr)) ? HeapAlloc(GetProcessHeap(), 0, DescriptorRangesSize) : nullptr; + if (DescriptorRangesSize > 0 && pDescriptorRanges == nullptr) + { + hr = E_OUTOFMEMORY; + } + auto pDescriptorRanges_1_0 = static_cast(pDescriptorRanges); + + if (SUCCEEDED(hr)) + { + for (UINT x = 0; x < table_1_1.NumDescriptorRanges; x++) + { + __analysis_assume(DescriptorRangesSize == sizeof(D3D12_DESCRIPTOR_RANGE) * table_1_1.NumDescriptorRanges); + pDescriptorRanges_1_0[x].BaseShaderRegister = table_1_1.pDescriptorRanges[x].BaseShaderRegister; + pDescriptorRanges_1_0[x].NumDescriptors = table_1_1.pDescriptorRanges[x].NumDescriptors; + pDescriptorRanges_1_0[x].OffsetInDescriptorsFromTableStart = table_1_1.pDescriptorRanges[x].OffsetInDescriptorsFromTableStart; + pDescriptorRanges_1_0[x].RangeType = table_1_1.pDescriptorRanges[x].RangeType; + pDescriptorRanges_1_0[x].RegisterSpace = table_1_1.pDescriptorRanges[x].RegisterSpace; + } + } + + D3D12_ROOT_DESCRIPTOR_TABLE& table_1_0 = pParameters_1_0[n].DescriptorTable; + table_1_0.NumDescriptorRanges = table_1_1.NumDescriptorRanges; + table_1_0.pDescriptorRanges = pDescriptorRanges_1_0; + } + } + } + + if (SUCCEEDED(hr)) + { + const CD3DX12_ROOT_SIGNATURE_DESC desc_1_0(desc_1_1.NumParameters, pParameters_1_0, desc_1_1.NumStaticSamplers, desc_1_1.pStaticSamplers, desc_1_1.Flags); + hr = D3D12SerializeRootSignature(&desc_1_0, D3D_ROOT_SIGNATURE_VERSION_1, ppBlob, ppErrorBlob); + } + + if (pParameters) + { + for (UINT n = 0; n < desc_1_1.NumParameters; n++) + { + if (desc_1_1.pParameters[n].ParameterType == D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE) + { + auto pDescriptorRanges_1_0 = pParameters_1_0[n].DescriptorTable.pDescriptorRanges; + HeapFree(GetProcessHeap(), 0, reinterpret_cast(const_cast(pDescriptorRanges_1_0))); + } + } + HeapFree(GetProcessHeap(), 0, pParameters); + } + return hr; + } + } + break; + + case D3D_ROOT_SIGNATURE_VERSION_1_1: +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 609) + case D3D_ROOT_SIGNATURE_VERSION_1_2: +#endif + return D3D12SerializeVersionedRootSignature(pRootSignatureDesc, ppBlob, ppErrorBlob); + } + + return E_INVALIDARG; +} + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_RT_FORMAT_ARRAY : public D3D12_RT_FORMAT_ARRAY +{ + CD3DX12_RT_FORMAT_ARRAY() = default; + explicit CD3DX12_RT_FORMAT_ARRAY(const D3D12_RT_FORMAT_ARRAY& o) noexcept + : D3D12_RT_FORMAT_ARRAY(o) + {} + explicit CD3DX12_RT_FORMAT_ARRAY(_In_reads_(NumFormats) const DXGI_FORMAT* pFormats, UINT NumFormats) noexcept + { + NumRenderTargets = NumFormats; + memcpy(RTFormats, pFormats, sizeof(RTFormats)); + // assumes ARRAY_SIZE(pFormats) == ARRAY_SIZE(RTFormats) + } +}; + +//------------------------------------------------------------------------------------------------ +// Pipeline State Stream Helpers +//------------------------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------------------------ +// Stream Subobjects, i.e. elements of a stream + +struct DefaultSampleMask { operator UINT() noexcept { return UINT_MAX; } }; +struct DefaultSampleDesc { operator DXGI_SAMPLE_DESC() noexcept { return DXGI_SAMPLE_DESC{1, 0}; } }; + +#pragma warning(push) +#pragma warning(disable : 4324) +template +class alignas(void*) CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT +{ +private: + D3D12_PIPELINE_STATE_SUBOBJECT_TYPE pssType; + InnerStructType pssInner; +public: + CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT() noexcept : pssType(Type), pssInner(DefaultArg()) {} + CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT(InnerStructType const& i) noexcept : pssType(Type), pssInner(i) {} + CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT& operator=(InnerStructType const& i) noexcept { pssType = Type; pssInner = i; return *this; } + operator InnerStructType const&() const noexcept { return pssInner; } + operator InnerStructType&() noexcept { return pssInner; } + InnerStructType* operator&() noexcept { return &pssInner; } + InnerStructType const* operator&() const noexcept { return &pssInner; } +}; +#pragma warning(pop) +typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_PIPELINE_STATE_FLAGS, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_FLAGS> CD3DX12_PIPELINE_STATE_STREAM_FLAGS; +typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< UINT, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_NODE_MASK> CD3DX12_PIPELINE_STATE_STREAM_NODE_MASK; +typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< ID3D12RootSignature*, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_ROOT_SIGNATURE> CD3DX12_PIPELINE_STATE_STREAM_ROOT_SIGNATURE; +typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_INPUT_LAYOUT_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_INPUT_LAYOUT> CD3DX12_PIPELINE_STATE_STREAM_INPUT_LAYOUT; +typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_INDEX_BUFFER_STRIP_CUT_VALUE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_IB_STRIP_CUT_VALUE> CD3DX12_PIPELINE_STATE_STREAM_IB_STRIP_CUT_VALUE; +typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_PRIMITIVE_TOPOLOGY_TYPE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_PRIMITIVE_TOPOLOGY> CD3DX12_PIPELINE_STATE_STREAM_PRIMITIVE_TOPOLOGY; +typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_VS> CD3DX12_PIPELINE_STATE_STREAM_VS; +typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_GS> CD3DX12_PIPELINE_STATE_STREAM_GS; +typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_STREAM_OUTPUT_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_STREAM_OUTPUT> CD3DX12_PIPELINE_STATE_STREAM_STREAM_OUTPUT; +typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_HS> CD3DX12_PIPELINE_STATE_STREAM_HS; +typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DS> CD3DX12_PIPELINE_STATE_STREAM_DS; +typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_PS> CD3DX12_PIPELINE_STATE_STREAM_PS; +typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_AS> CD3DX12_PIPELINE_STATE_STREAM_AS; +typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_MS> CD3DX12_PIPELINE_STATE_STREAM_MS; +typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_CS> CD3DX12_PIPELINE_STATE_STREAM_CS; +typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< CD3DX12_BLEND_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_BLEND, CD3DX12_DEFAULT> CD3DX12_PIPELINE_STATE_STREAM_BLEND_DESC; +typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< CD3DX12_DEPTH_STENCIL_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL, CD3DX12_DEFAULT> CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL; +typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< CD3DX12_DEPTH_STENCIL_DESC1, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL1, CD3DX12_DEFAULT> CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL1; +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 606) +typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< CD3DX12_DEPTH_STENCIL_DESC2, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL2, CD3DX12_DEFAULT> CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL2; +#endif +typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< DXGI_FORMAT, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL_FORMAT> CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL_FORMAT; +typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< CD3DX12_RASTERIZER_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_RASTERIZER, CD3DX12_DEFAULT> CD3DX12_PIPELINE_STATE_STREAM_RASTERIZER; +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 608) +typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< CD3DX12_RASTERIZER_DESC1, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_RASTERIZER1, CD3DX12_DEFAULT> CD3DX12_PIPELINE_STATE_STREAM_RASTERIZER1; +#endif +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 610) +typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< CD3DX12_RASTERIZER_DESC2, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_RASTERIZER2, CD3DX12_DEFAULT> CD3DX12_PIPELINE_STATE_STREAM_RASTERIZER2; +#endif +typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_RT_FORMAT_ARRAY, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_RENDER_TARGET_FORMATS> CD3DX12_PIPELINE_STATE_STREAM_RENDER_TARGET_FORMATS; +typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< DXGI_SAMPLE_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_SAMPLE_DESC, DefaultSampleDesc> CD3DX12_PIPELINE_STATE_STREAM_SAMPLE_DESC; +typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< UINT, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_SAMPLE_MASK, DefaultSampleMask> CD3DX12_PIPELINE_STATE_STREAM_SAMPLE_MASK; +typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_CACHED_PIPELINE_STATE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_CACHED_PSO> CD3DX12_PIPELINE_STATE_STREAM_CACHED_PSO; +typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< CD3DX12_VIEW_INSTANCING_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_VIEW_INSTANCING, CD3DX12_DEFAULT> CD3DX12_PIPELINE_STATE_STREAM_VIEW_INSTANCING; + +//------------------------------------------------------------------------------------------------ +// Stream Parser Helpers + +struct ID3DX12PipelineParserCallbacks +{ + // Subobject Callbacks + virtual void FlagsCb(D3D12_PIPELINE_STATE_FLAGS) {} + virtual void NodeMaskCb(UINT) {} + virtual void RootSignatureCb(ID3D12RootSignature*) {} + virtual void InputLayoutCb(const D3D12_INPUT_LAYOUT_DESC&) {} + virtual void IBStripCutValueCb(D3D12_INDEX_BUFFER_STRIP_CUT_VALUE) {} + virtual void PrimitiveTopologyTypeCb(D3D12_PRIMITIVE_TOPOLOGY_TYPE) {} + virtual void VSCb(const D3D12_SHADER_BYTECODE&) {} + virtual void GSCb(const D3D12_SHADER_BYTECODE&) {} + virtual void StreamOutputCb(const D3D12_STREAM_OUTPUT_DESC&) {} + virtual void HSCb(const D3D12_SHADER_BYTECODE&) {} + virtual void DSCb(const D3D12_SHADER_BYTECODE&) {} + virtual void PSCb(const D3D12_SHADER_BYTECODE&) {} + virtual void CSCb(const D3D12_SHADER_BYTECODE&) {} + virtual void ASCb(const D3D12_SHADER_BYTECODE&) {} + virtual void MSCb(const D3D12_SHADER_BYTECODE&) {} + virtual void BlendStateCb(const D3D12_BLEND_DESC&) {} + virtual void DepthStencilStateCb(const D3D12_DEPTH_STENCIL_DESC&) {} + virtual void DepthStencilState1Cb(const D3D12_DEPTH_STENCIL_DESC1&) {} +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 606) + virtual void DepthStencilState2Cb(const D3D12_DEPTH_STENCIL_DESC2&) {} +#endif + virtual void DSVFormatCb(DXGI_FORMAT) {} + virtual void RasterizerStateCb(const D3D12_RASTERIZER_DESC&) {} +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 608) + virtual void RasterizerState1Cb(const D3D12_RASTERIZER_DESC1&) {} +#endif +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 610) + virtual void RasterizerState2Cb(const D3D12_RASTERIZER_DESC2&) {} +#endif + virtual void RTVFormatsCb(const D3D12_RT_FORMAT_ARRAY&) {} + virtual void SampleDescCb(const DXGI_SAMPLE_DESC&) {} + virtual void SampleMaskCb(UINT) {} + virtual void ViewInstancingCb(const D3D12_VIEW_INSTANCING_DESC&) {} + virtual void CachedPSOCb(const D3D12_CACHED_PIPELINE_STATE&) {} + + // Error Callbacks + virtual void ErrorBadInputParameter(UINT /*ParameterIndex*/) {} + virtual void ErrorDuplicateSubobject(D3D12_PIPELINE_STATE_SUBOBJECT_TYPE /*DuplicateType*/) {} + virtual void ErrorUnknownSubobject(UINT /*UnknownTypeValue*/) {} + + virtual ~ID3DX12PipelineParserCallbacks() = default; +}; + +struct D3DX12_MESH_SHADER_PIPELINE_STATE_DESC +{ + ID3D12RootSignature* pRootSignature; + D3D12_SHADER_BYTECODE AS; + D3D12_SHADER_BYTECODE MS; + D3D12_SHADER_BYTECODE PS; + D3D12_BLEND_DESC BlendState; + UINT SampleMask; + D3D12_RASTERIZER_DESC RasterizerState; + D3D12_DEPTH_STENCIL_DESC DepthStencilState; + D3D12_PRIMITIVE_TOPOLOGY_TYPE PrimitiveTopologyType; + UINT NumRenderTargets; + DXGI_FORMAT RTVFormats[ D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT ]; + DXGI_FORMAT DSVFormat; + DXGI_SAMPLE_DESC SampleDesc; + UINT NodeMask; + D3D12_CACHED_PIPELINE_STATE CachedPSO; + D3D12_PIPELINE_STATE_FLAGS Flags; +}; + +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 610) +// Use CD3DX12_PIPELINE_STATE_STREAM5 for D3D12_RASTERIZER_DESC2 when CheckFeatureSupport returns true for Options19::RasterizerDesc2Supported is true +// Use CD3DX12_PIPELINE_STATE_STREAM4 for D3D12_RASTERIZER_DESC1 when CheckFeatureSupport returns true for Options16::DynamicDepthBiasSupported is true +// Use CD3DX12_PIPELINE_STATE_STREAM3 for D3D12_DEPTH_STENCIL_DESC2 when CheckFeatureSupport returns true for Options14::IndependentFrontAndBackStencilSupported is true +// Use CD3DX12_PIPELINE_STATE_STREAM2 for OS Build 19041+ (where there is a new mesh shader pipeline). +// Use CD3DX12_PIPELINE_STATE_STREAM1 for OS Build 16299+ (where there is a new view instancing subobject). +// Use CD3DX12_PIPELINE_STATE_STREAM for OS Build 15063+ support. +struct CD3DX12_PIPELINE_STATE_STREAM5 +{ + CD3DX12_PIPELINE_STATE_STREAM5() = default; + // Mesh and amplification shaders must be set manually, since they do not have representation in D3D12_GRAPHICS_PIPELINE_STATE_DESC + CD3DX12_PIPELINE_STATE_STREAM5(const D3D12_GRAPHICS_PIPELINE_STATE_DESC& Desc) noexcept + : Flags(Desc.Flags) + , NodeMask(Desc.NodeMask) + , pRootSignature(Desc.pRootSignature) + , InputLayout(Desc.InputLayout) + , IBStripCutValue(Desc.IBStripCutValue) + , PrimitiveTopologyType(Desc.PrimitiveTopologyType) + , VS(Desc.VS) + , GS(Desc.GS) + , StreamOutput(Desc.StreamOutput) + , HS(Desc.HS) + , DS(Desc.DS) + , PS(Desc.PS) + , BlendState(CD3DX12_BLEND_DESC(Desc.BlendState)) + , DepthStencilState(CD3DX12_DEPTH_STENCIL_DESC2(Desc.DepthStencilState)) + , DSVFormat(Desc.DSVFormat) + , RasterizerState(CD3DX12_RASTERIZER_DESC2(Desc.RasterizerState)) + , RTVFormats(CD3DX12_RT_FORMAT_ARRAY(Desc.RTVFormats, Desc.NumRenderTargets)) + , SampleDesc(Desc.SampleDesc) + , SampleMask(Desc.SampleMask) + , CachedPSO(Desc.CachedPSO) + , ViewInstancingDesc(CD3DX12_VIEW_INSTANCING_DESC(CD3DX12_DEFAULT())) + {} + CD3DX12_PIPELINE_STATE_STREAM5(const D3DX12_MESH_SHADER_PIPELINE_STATE_DESC& Desc) noexcept + : Flags(Desc.Flags) + , NodeMask(Desc.NodeMask) + , pRootSignature(Desc.pRootSignature) + , PrimitiveTopologyType(Desc.PrimitiveTopologyType) + , PS(Desc.PS) + , AS(Desc.AS) + , MS(Desc.MS) + , BlendState(CD3DX12_BLEND_DESC(Desc.BlendState)) + , DepthStencilState(CD3DX12_DEPTH_STENCIL_DESC2(Desc.DepthStencilState)) + , DSVFormat(Desc.DSVFormat) + , RasterizerState(CD3DX12_RASTERIZER_DESC2(Desc.RasterizerState)) + , RTVFormats(CD3DX12_RT_FORMAT_ARRAY(Desc.RTVFormats, Desc.NumRenderTargets)) + , SampleDesc(Desc.SampleDesc) + , SampleMask(Desc.SampleMask) + , CachedPSO(Desc.CachedPSO) + , ViewInstancingDesc(CD3DX12_VIEW_INSTANCING_DESC(CD3DX12_DEFAULT())) + {} + CD3DX12_PIPELINE_STATE_STREAM5(const D3D12_COMPUTE_PIPELINE_STATE_DESC& Desc) noexcept + : Flags(Desc.Flags) + , NodeMask(Desc.NodeMask) + , pRootSignature(Desc.pRootSignature) + , CS(CD3DX12_SHADER_BYTECODE(Desc.CS)) + , CachedPSO(Desc.CachedPSO) + { + static_cast(DepthStencilState).DepthEnable = false; + } + CD3DX12_PIPELINE_STATE_STREAM_FLAGS Flags; + CD3DX12_PIPELINE_STATE_STREAM_NODE_MASK NodeMask; + CD3DX12_PIPELINE_STATE_STREAM_ROOT_SIGNATURE pRootSignature; + CD3DX12_PIPELINE_STATE_STREAM_INPUT_LAYOUT InputLayout; + CD3DX12_PIPELINE_STATE_STREAM_IB_STRIP_CUT_VALUE IBStripCutValue; + CD3DX12_PIPELINE_STATE_STREAM_PRIMITIVE_TOPOLOGY PrimitiveTopologyType; + CD3DX12_PIPELINE_STATE_STREAM_VS VS; + CD3DX12_PIPELINE_STATE_STREAM_GS GS; + CD3DX12_PIPELINE_STATE_STREAM_STREAM_OUTPUT StreamOutput; + CD3DX12_PIPELINE_STATE_STREAM_HS HS; + CD3DX12_PIPELINE_STATE_STREAM_DS DS; + CD3DX12_PIPELINE_STATE_STREAM_PS PS; + CD3DX12_PIPELINE_STATE_STREAM_AS AS; + CD3DX12_PIPELINE_STATE_STREAM_MS MS; + CD3DX12_PIPELINE_STATE_STREAM_CS CS; + CD3DX12_PIPELINE_STATE_STREAM_BLEND_DESC BlendState; + CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL2 DepthStencilState; + CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL_FORMAT DSVFormat; + CD3DX12_PIPELINE_STATE_STREAM_RASTERIZER2 RasterizerState; + CD3DX12_PIPELINE_STATE_STREAM_RENDER_TARGET_FORMATS RTVFormats; + CD3DX12_PIPELINE_STATE_STREAM_SAMPLE_DESC SampleDesc; + CD3DX12_PIPELINE_STATE_STREAM_SAMPLE_MASK SampleMask; + CD3DX12_PIPELINE_STATE_STREAM_CACHED_PSO CachedPSO; + CD3DX12_PIPELINE_STATE_STREAM_VIEW_INSTANCING ViewInstancingDesc; + + D3D12_GRAPHICS_PIPELINE_STATE_DESC GraphicsDescV0() const noexcept + { + D3D12_GRAPHICS_PIPELINE_STATE_DESC D; + D.Flags = this->Flags; + D.NodeMask = this->NodeMask; + D.pRootSignature = this->pRootSignature; + D.InputLayout = this->InputLayout; + D.IBStripCutValue = this->IBStripCutValue; + D.PrimitiveTopologyType = this->PrimitiveTopologyType; + D.VS = this->VS; + D.GS = this->GS; + D.StreamOutput = this->StreamOutput; + D.HS = this->HS; + D.DS = this->DS; + D.PS = this->PS; + D.BlendState = this->BlendState; + D.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC2(D3D12_DEPTH_STENCIL_DESC2(this->DepthStencilState)); + D.DSVFormat = this->DSVFormat; + D.RasterizerState = CD3DX12_RASTERIZER_DESC2(D3D12_RASTERIZER_DESC2(this->RasterizerState)); + D.NumRenderTargets = D3D12_RT_FORMAT_ARRAY(this->RTVFormats).NumRenderTargets; + memcpy(D.RTVFormats, D3D12_RT_FORMAT_ARRAY(this->RTVFormats).RTFormats, sizeof(D.RTVFormats)); + D.SampleDesc = this->SampleDesc; + D.SampleMask = this->SampleMask; + D.CachedPSO = this->CachedPSO; + return D; + } + D3D12_COMPUTE_PIPELINE_STATE_DESC ComputeDescV0() const noexcept + { + D3D12_COMPUTE_PIPELINE_STATE_DESC D; + D.Flags = this->Flags; + D.NodeMask = this->NodeMask; + D.pRootSignature = this->pRootSignature; + D.CS = this->CS; + D.CachedPSO = this->CachedPSO; + return D; + } +}; +#endif // D3D12_SDK_VERSION >= 610 + +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 608) +// Use CD3DX12_PIPELINE_STATE_STREAM4 for D3D12_RASTERIZER_DESC1 when CheckFeatureSupport returns true for Options16::DynamicDepthBiasSupported is true +// Use CD3DX12_PIPELINE_STATE_STREAM3 for D3D12_DEPTH_STENCIL_DESC2 when CheckFeatureSupport returns true for Options14::IndependentFrontAndBackStencilSupported is true +// Use CD3DX12_PIPELINE_STATE_STREAM2 for OS Build 19041+ (where there is a new mesh shader pipeline). +// Use CD3DX12_PIPELINE_STATE_STREAM1 for OS Build 16299+ (where there is a new view instancing subobject). +// Use CD3DX12_PIPELINE_STATE_STREAM for OS Build 15063+ support. +struct CD3DX12_PIPELINE_STATE_STREAM4 +{ + CD3DX12_PIPELINE_STATE_STREAM4() = default; + // Mesh and amplification shaders must be set manually, since they do not have representation in D3D12_GRAPHICS_PIPELINE_STATE_DESC + CD3DX12_PIPELINE_STATE_STREAM4(const D3D12_GRAPHICS_PIPELINE_STATE_DESC& Desc) noexcept + : Flags(Desc.Flags) + , NodeMask(Desc.NodeMask) + , pRootSignature(Desc.pRootSignature) + , InputLayout(Desc.InputLayout) + , IBStripCutValue(Desc.IBStripCutValue) + , PrimitiveTopologyType(Desc.PrimitiveTopologyType) + , VS(Desc.VS) + , GS(Desc.GS) + , StreamOutput(Desc.StreamOutput) + , HS(Desc.HS) + , DS(Desc.DS) + , PS(Desc.PS) + , BlendState(CD3DX12_BLEND_DESC(Desc.BlendState)) + , DepthStencilState(CD3DX12_DEPTH_STENCIL_DESC2(Desc.DepthStencilState)) + , DSVFormat(Desc.DSVFormat) + , RasterizerState(CD3DX12_RASTERIZER_DESC1(Desc.RasterizerState)) + , RTVFormats(CD3DX12_RT_FORMAT_ARRAY(Desc.RTVFormats, Desc.NumRenderTargets)) + , SampleDesc(Desc.SampleDesc) + , SampleMask(Desc.SampleMask) + , CachedPSO(Desc.CachedPSO) + , ViewInstancingDesc(CD3DX12_VIEW_INSTANCING_DESC(CD3DX12_DEFAULT())) + {} + CD3DX12_PIPELINE_STATE_STREAM4(const D3DX12_MESH_SHADER_PIPELINE_STATE_DESC& Desc) noexcept + : Flags(Desc.Flags) + , NodeMask(Desc.NodeMask) + , pRootSignature(Desc.pRootSignature) + , PrimitiveTopologyType(Desc.PrimitiveTopologyType) + , PS(Desc.PS) + , AS(Desc.AS) + , MS(Desc.MS) + , BlendState(CD3DX12_BLEND_DESC(Desc.BlendState)) + , DepthStencilState(CD3DX12_DEPTH_STENCIL_DESC2(Desc.DepthStencilState)) + , DSVFormat(Desc.DSVFormat) + , RasterizerState(CD3DX12_RASTERIZER_DESC1(Desc.RasterizerState)) + , RTVFormats(CD3DX12_RT_FORMAT_ARRAY(Desc.RTVFormats, Desc.NumRenderTargets)) + , SampleDesc(Desc.SampleDesc) + , SampleMask(Desc.SampleMask) + , CachedPSO(Desc.CachedPSO) + , ViewInstancingDesc(CD3DX12_VIEW_INSTANCING_DESC(CD3DX12_DEFAULT())) + {} + CD3DX12_PIPELINE_STATE_STREAM4(const D3D12_COMPUTE_PIPELINE_STATE_DESC& Desc) noexcept + : Flags(Desc.Flags) + , NodeMask(Desc.NodeMask) + , pRootSignature(Desc.pRootSignature) + , CS(CD3DX12_SHADER_BYTECODE(Desc.CS)) + , CachedPSO(Desc.CachedPSO) + { + static_cast(DepthStencilState).DepthEnable = false; + } + CD3DX12_PIPELINE_STATE_STREAM_FLAGS Flags; + CD3DX12_PIPELINE_STATE_STREAM_NODE_MASK NodeMask; + CD3DX12_PIPELINE_STATE_STREAM_ROOT_SIGNATURE pRootSignature; + CD3DX12_PIPELINE_STATE_STREAM_INPUT_LAYOUT InputLayout; + CD3DX12_PIPELINE_STATE_STREAM_IB_STRIP_CUT_VALUE IBStripCutValue; + CD3DX12_PIPELINE_STATE_STREAM_PRIMITIVE_TOPOLOGY PrimitiveTopologyType; + CD3DX12_PIPELINE_STATE_STREAM_VS VS; + CD3DX12_PIPELINE_STATE_STREAM_GS GS; + CD3DX12_PIPELINE_STATE_STREAM_STREAM_OUTPUT StreamOutput; + CD3DX12_PIPELINE_STATE_STREAM_HS HS; + CD3DX12_PIPELINE_STATE_STREAM_DS DS; + CD3DX12_PIPELINE_STATE_STREAM_PS PS; + CD3DX12_PIPELINE_STATE_STREAM_AS AS; + CD3DX12_PIPELINE_STATE_STREAM_MS MS; + CD3DX12_PIPELINE_STATE_STREAM_CS CS; + CD3DX12_PIPELINE_STATE_STREAM_BLEND_DESC BlendState; + CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL2 DepthStencilState; + CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL_FORMAT DSVFormat; + CD3DX12_PIPELINE_STATE_STREAM_RASTERIZER1 RasterizerState; + CD3DX12_PIPELINE_STATE_STREAM_RENDER_TARGET_FORMATS RTVFormats; + CD3DX12_PIPELINE_STATE_STREAM_SAMPLE_DESC SampleDesc; + CD3DX12_PIPELINE_STATE_STREAM_SAMPLE_MASK SampleMask; + CD3DX12_PIPELINE_STATE_STREAM_CACHED_PSO CachedPSO; + CD3DX12_PIPELINE_STATE_STREAM_VIEW_INSTANCING ViewInstancingDesc; + + D3D12_GRAPHICS_PIPELINE_STATE_DESC GraphicsDescV0() const noexcept + { + D3D12_GRAPHICS_PIPELINE_STATE_DESC D; + D.Flags = this->Flags; + D.NodeMask = this->NodeMask; + D.pRootSignature = this->pRootSignature; + D.InputLayout = this->InputLayout; + D.IBStripCutValue = this->IBStripCutValue; + D.PrimitiveTopologyType = this->PrimitiveTopologyType; + D.VS = this->VS; + D.GS = this->GS; + D.StreamOutput = this->StreamOutput; + D.HS = this->HS; + D.DS = this->DS; + D.PS = this->PS; + D.BlendState = this->BlendState; + D.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC2(D3D12_DEPTH_STENCIL_DESC2(this->DepthStencilState)); + D.DSVFormat = this->DSVFormat; + D.RasterizerState = CD3DX12_RASTERIZER_DESC1(D3D12_RASTERIZER_DESC1(this->RasterizerState)); + D.NumRenderTargets = D3D12_RT_FORMAT_ARRAY(this->RTVFormats).NumRenderTargets; + memcpy(D.RTVFormats, D3D12_RT_FORMAT_ARRAY(this->RTVFormats).RTFormats, sizeof(D.RTVFormats)); + D.SampleDesc = this->SampleDesc; + D.SampleMask = this->SampleMask; + D.CachedPSO = this->CachedPSO; + return D; + } + D3D12_COMPUTE_PIPELINE_STATE_DESC ComputeDescV0() const noexcept + { + D3D12_COMPUTE_PIPELINE_STATE_DESC D; + D.Flags = this->Flags; + D.NodeMask = this->NodeMask; + D.pRootSignature = this->pRootSignature; + D.CS = this->CS; + D.CachedPSO = this->CachedPSO; + return D; + } +}; +#endif // D3D12_SDK_VERSION >= 608 + +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 606) +// Use CD3DX12_PIPELINE_STATE_STREAM3 for D3D12_DEPTH_STENCIL_DESC2 when CheckFeatureSupport returns true for Options14::IndependentFrontAndBackStencilSupported is true +// Use CD3DX12_PIPELINE_STATE_STREAM2 for OS Build 19041+ (where there is a new mesh shader pipeline). +// Use CD3DX12_PIPELINE_STATE_STREAM1 for OS Build 16299+ (where there is a new view instancing subobject). +// Use CD3DX12_PIPELINE_STATE_STREAM for OS Build 15063+ support. +struct CD3DX12_PIPELINE_STATE_STREAM3 +{ + CD3DX12_PIPELINE_STATE_STREAM3() = default; + // Mesh and amplification shaders must be set manually, since they do not have representation in D3D12_GRAPHICS_PIPELINE_STATE_DESC + CD3DX12_PIPELINE_STATE_STREAM3(const D3D12_GRAPHICS_PIPELINE_STATE_DESC& Desc) noexcept + : Flags(Desc.Flags) + , NodeMask(Desc.NodeMask) + , pRootSignature(Desc.pRootSignature) + , InputLayout(Desc.InputLayout) + , IBStripCutValue(Desc.IBStripCutValue) + , PrimitiveTopologyType(Desc.PrimitiveTopologyType) + , VS(Desc.VS) + , GS(Desc.GS) + , StreamOutput(Desc.StreamOutput) + , HS(Desc.HS) + , DS(Desc.DS) + , PS(Desc.PS) + , BlendState(CD3DX12_BLEND_DESC(Desc.BlendState)) + , DepthStencilState(CD3DX12_DEPTH_STENCIL_DESC2(Desc.DepthStencilState)) + , DSVFormat(Desc.DSVFormat) + , RasterizerState(CD3DX12_RASTERIZER_DESC(Desc.RasterizerState)) + , RTVFormats(CD3DX12_RT_FORMAT_ARRAY(Desc.RTVFormats, Desc.NumRenderTargets)) + , SampleDesc(Desc.SampleDesc) + , SampleMask(Desc.SampleMask) + , CachedPSO(Desc.CachedPSO) + , ViewInstancingDesc(CD3DX12_VIEW_INSTANCING_DESC(CD3DX12_DEFAULT())) + {} + CD3DX12_PIPELINE_STATE_STREAM3(const D3DX12_MESH_SHADER_PIPELINE_STATE_DESC& Desc) noexcept + : Flags(Desc.Flags) + , NodeMask(Desc.NodeMask) + , pRootSignature(Desc.pRootSignature) + , PrimitiveTopologyType(Desc.PrimitiveTopologyType) + , PS(Desc.PS) + , AS(Desc.AS) + , MS(Desc.MS) + , BlendState(CD3DX12_BLEND_DESC(Desc.BlendState)) + , DepthStencilState(CD3DX12_DEPTH_STENCIL_DESC2(Desc.DepthStencilState)) + , DSVFormat(Desc.DSVFormat) + , RasterizerState(CD3DX12_RASTERIZER_DESC(Desc.RasterizerState)) + , RTVFormats(CD3DX12_RT_FORMAT_ARRAY(Desc.RTVFormats, Desc.NumRenderTargets)) + , SampleDesc(Desc.SampleDesc) + , SampleMask(Desc.SampleMask) + , CachedPSO(Desc.CachedPSO) + , ViewInstancingDesc(CD3DX12_VIEW_INSTANCING_DESC(CD3DX12_DEFAULT())) + {} + CD3DX12_PIPELINE_STATE_STREAM3(const D3D12_COMPUTE_PIPELINE_STATE_DESC& Desc) noexcept + : Flags(Desc.Flags) + , NodeMask(Desc.NodeMask) + , pRootSignature(Desc.pRootSignature) + , CS(CD3DX12_SHADER_BYTECODE(Desc.CS)) + , CachedPSO(Desc.CachedPSO) + { + static_cast(DepthStencilState).DepthEnable = false; + } + CD3DX12_PIPELINE_STATE_STREAM_FLAGS Flags; + CD3DX12_PIPELINE_STATE_STREAM_NODE_MASK NodeMask; + CD3DX12_PIPELINE_STATE_STREAM_ROOT_SIGNATURE pRootSignature; + CD3DX12_PIPELINE_STATE_STREAM_INPUT_LAYOUT InputLayout; + CD3DX12_PIPELINE_STATE_STREAM_IB_STRIP_CUT_VALUE IBStripCutValue; + CD3DX12_PIPELINE_STATE_STREAM_PRIMITIVE_TOPOLOGY PrimitiveTopologyType; + CD3DX12_PIPELINE_STATE_STREAM_VS VS; + CD3DX12_PIPELINE_STATE_STREAM_GS GS; + CD3DX12_PIPELINE_STATE_STREAM_STREAM_OUTPUT StreamOutput; + CD3DX12_PIPELINE_STATE_STREAM_HS HS; + CD3DX12_PIPELINE_STATE_STREAM_DS DS; + CD3DX12_PIPELINE_STATE_STREAM_PS PS; + CD3DX12_PIPELINE_STATE_STREAM_AS AS; + CD3DX12_PIPELINE_STATE_STREAM_MS MS; + CD3DX12_PIPELINE_STATE_STREAM_CS CS; + CD3DX12_PIPELINE_STATE_STREAM_BLEND_DESC BlendState; + CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL2 DepthStencilState; + CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL_FORMAT DSVFormat; + CD3DX12_PIPELINE_STATE_STREAM_RASTERIZER RasterizerState; + CD3DX12_PIPELINE_STATE_STREAM_RENDER_TARGET_FORMATS RTVFormats; + CD3DX12_PIPELINE_STATE_STREAM_SAMPLE_DESC SampleDesc; + CD3DX12_PIPELINE_STATE_STREAM_SAMPLE_MASK SampleMask; + CD3DX12_PIPELINE_STATE_STREAM_CACHED_PSO CachedPSO; + CD3DX12_PIPELINE_STATE_STREAM_VIEW_INSTANCING ViewInstancingDesc; + + D3D12_GRAPHICS_PIPELINE_STATE_DESC GraphicsDescV0() const noexcept + { + D3D12_GRAPHICS_PIPELINE_STATE_DESC D; + D.Flags = this->Flags; + D.NodeMask = this->NodeMask; + D.pRootSignature = this->pRootSignature; + D.InputLayout = this->InputLayout; + D.IBStripCutValue = this->IBStripCutValue; + D.PrimitiveTopologyType = this->PrimitiveTopologyType; + D.VS = this->VS; + D.GS = this->GS; + D.StreamOutput = this->StreamOutput; + D.HS = this->HS; + D.DS = this->DS; + D.PS = this->PS; + D.BlendState = this->BlendState; + D.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC2(D3D12_DEPTH_STENCIL_DESC2(this->DepthStencilState)); + D.DSVFormat = this->DSVFormat; + D.RasterizerState = this->RasterizerState; + D.NumRenderTargets = D3D12_RT_FORMAT_ARRAY(this->RTVFormats).NumRenderTargets; + memcpy(D.RTVFormats, D3D12_RT_FORMAT_ARRAY(this->RTVFormats).RTFormats, sizeof(D.RTVFormats)); + D.SampleDesc = this->SampleDesc; + D.SampleMask = this->SampleMask; + D.CachedPSO = this->CachedPSO; + return D; + } + D3D12_COMPUTE_PIPELINE_STATE_DESC ComputeDescV0() const noexcept + { + D3D12_COMPUTE_PIPELINE_STATE_DESC D; + D.Flags = this->Flags; + D.NodeMask = this->NodeMask; + D.pRootSignature = this->pRootSignature; + D.CS = this->CS; + D.CachedPSO = this->CachedPSO; + return D; + } +}; +#endif // D3D12_SDK_VERSION >= 606 + +// CD3DX12_PIPELINE_STATE_STREAM2 Works on OS Build 19041+ (where there is a new mesh shader pipeline). +// Use CD3DX12_PIPELINE_STATE_STREAM1 for OS Build 16299+ (where there is a new view instancing subobject). +// Use CD3DX12_PIPELINE_STATE_STREAM for OS Build 15063+ support. +struct CD3DX12_PIPELINE_STATE_STREAM2 +{ + CD3DX12_PIPELINE_STATE_STREAM2() = default; + // Mesh and amplification shaders must be set manually, since they do not have representation in D3D12_GRAPHICS_PIPELINE_STATE_DESC + CD3DX12_PIPELINE_STATE_STREAM2(const D3D12_GRAPHICS_PIPELINE_STATE_DESC& Desc) noexcept + : Flags(Desc.Flags) + , NodeMask(Desc.NodeMask) + , pRootSignature(Desc.pRootSignature) + , InputLayout(Desc.InputLayout) + , IBStripCutValue(Desc.IBStripCutValue) + , PrimitiveTopologyType(Desc.PrimitiveTopologyType) + , VS(Desc.VS) + , GS(Desc.GS) + , StreamOutput(Desc.StreamOutput) + , HS(Desc.HS) + , DS(Desc.DS) + , PS(Desc.PS) + , BlendState(CD3DX12_BLEND_DESC(Desc.BlendState)) + , DepthStencilState(CD3DX12_DEPTH_STENCIL_DESC1(Desc.DepthStencilState)) + , DSVFormat(Desc.DSVFormat) + , RasterizerState(CD3DX12_RASTERIZER_DESC(Desc.RasterizerState)) + , RTVFormats(CD3DX12_RT_FORMAT_ARRAY(Desc.RTVFormats, Desc.NumRenderTargets)) + , SampleDesc(Desc.SampleDesc) + , SampleMask(Desc.SampleMask) + , CachedPSO(Desc.CachedPSO) + , ViewInstancingDesc(CD3DX12_VIEW_INSTANCING_DESC(CD3DX12_DEFAULT())) + {} + CD3DX12_PIPELINE_STATE_STREAM2(const D3DX12_MESH_SHADER_PIPELINE_STATE_DESC& Desc) noexcept + : Flags(Desc.Flags) + , NodeMask(Desc.NodeMask) + , pRootSignature(Desc.pRootSignature) + , PrimitiveTopologyType(Desc.PrimitiveTopologyType) + , PS(Desc.PS) + , AS(Desc.AS) + , MS(Desc.MS) + , BlendState(CD3DX12_BLEND_DESC(Desc.BlendState)) + , DepthStencilState(CD3DX12_DEPTH_STENCIL_DESC1(Desc.DepthStencilState)) + , DSVFormat(Desc.DSVFormat) + , RasterizerState(CD3DX12_RASTERIZER_DESC(Desc.RasterizerState)) + , RTVFormats(CD3DX12_RT_FORMAT_ARRAY(Desc.RTVFormats, Desc.NumRenderTargets)) + , SampleDesc(Desc.SampleDesc) + , SampleMask(Desc.SampleMask) + , CachedPSO(Desc.CachedPSO) + , ViewInstancingDesc(CD3DX12_VIEW_INSTANCING_DESC(CD3DX12_DEFAULT())) + {} + CD3DX12_PIPELINE_STATE_STREAM2(const D3D12_COMPUTE_PIPELINE_STATE_DESC& Desc) noexcept + : Flags(Desc.Flags) + , NodeMask(Desc.NodeMask) + , pRootSignature(Desc.pRootSignature) + , CS(CD3DX12_SHADER_BYTECODE(Desc.CS)) + , CachedPSO(Desc.CachedPSO) + { + static_cast(DepthStencilState).DepthEnable = false; + } + CD3DX12_PIPELINE_STATE_STREAM_FLAGS Flags; + CD3DX12_PIPELINE_STATE_STREAM_NODE_MASK NodeMask; + CD3DX12_PIPELINE_STATE_STREAM_ROOT_SIGNATURE pRootSignature; + CD3DX12_PIPELINE_STATE_STREAM_INPUT_LAYOUT InputLayout; + CD3DX12_PIPELINE_STATE_STREAM_IB_STRIP_CUT_VALUE IBStripCutValue; + CD3DX12_PIPELINE_STATE_STREAM_PRIMITIVE_TOPOLOGY PrimitiveTopologyType; + CD3DX12_PIPELINE_STATE_STREAM_VS VS; + CD3DX12_PIPELINE_STATE_STREAM_GS GS; + CD3DX12_PIPELINE_STATE_STREAM_STREAM_OUTPUT StreamOutput; + CD3DX12_PIPELINE_STATE_STREAM_HS HS; + CD3DX12_PIPELINE_STATE_STREAM_DS DS; + CD3DX12_PIPELINE_STATE_STREAM_PS PS; + CD3DX12_PIPELINE_STATE_STREAM_AS AS; + CD3DX12_PIPELINE_STATE_STREAM_MS MS; + CD3DX12_PIPELINE_STATE_STREAM_CS CS; + CD3DX12_PIPELINE_STATE_STREAM_BLEND_DESC BlendState; + CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL1 DepthStencilState; + CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL_FORMAT DSVFormat; + CD3DX12_PIPELINE_STATE_STREAM_RASTERIZER RasterizerState; + CD3DX12_PIPELINE_STATE_STREAM_RENDER_TARGET_FORMATS RTVFormats; + CD3DX12_PIPELINE_STATE_STREAM_SAMPLE_DESC SampleDesc; + CD3DX12_PIPELINE_STATE_STREAM_SAMPLE_MASK SampleMask; + CD3DX12_PIPELINE_STATE_STREAM_CACHED_PSO CachedPSO; + CD3DX12_PIPELINE_STATE_STREAM_VIEW_INSTANCING ViewInstancingDesc; + D3D12_GRAPHICS_PIPELINE_STATE_DESC GraphicsDescV0() const noexcept + { + D3D12_GRAPHICS_PIPELINE_STATE_DESC D; + D.Flags = this->Flags; + D.NodeMask = this->NodeMask; + D.pRootSignature = this->pRootSignature; + D.InputLayout = this->InputLayout; + D.IBStripCutValue = this->IBStripCutValue; + D.PrimitiveTopologyType = this->PrimitiveTopologyType; + D.VS = this->VS; + D.GS = this->GS; + D.StreamOutput = this->StreamOutput; + D.HS = this->HS; + D.DS = this->DS; + D.PS = this->PS; + D.BlendState = this->BlendState; + D.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC1(D3D12_DEPTH_STENCIL_DESC1(this->DepthStencilState)); + D.DSVFormat = this->DSVFormat; + D.RasterizerState = this->RasterizerState; + D.NumRenderTargets = D3D12_RT_FORMAT_ARRAY(this->RTVFormats).NumRenderTargets; + memcpy(D.RTVFormats, D3D12_RT_FORMAT_ARRAY(this->RTVFormats).RTFormats, sizeof(D.RTVFormats)); + D.SampleDesc = this->SampleDesc; + D.SampleMask = this->SampleMask; + D.CachedPSO = this->CachedPSO; + return D; + } + D3D12_COMPUTE_PIPELINE_STATE_DESC ComputeDescV0() const noexcept + { + D3D12_COMPUTE_PIPELINE_STATE_DESC D; + D.Flags = this->Flags; + D.NodeMask = this->NodeMask; + D.pRootSignature = this->pRootSignature; + D.CS = this->CS; + D.CachedPSO = this->CachedPSO; + return D; + } +}; + +// CD3DX12_PIPELINE_STATE_STREAM1 Works on OS Build 16299+ (where there is a new view instancing subobject). +// Use CD3DX12_PIPELINE_STATE_STREAM for OS Build 15063+ support. +struct CD3DX12_PIPELINE_STATE_STREAM1 +{ + CD3DX12_PIPELINE_STATE_STREAM1() = default; + // Mesh and amplification shaders must be set manually, since they do not have representation in D3D12_GRAPHICS_PIPELINE_STATE_DESC + CD3DX12_PIPELINE_STATE_STREAM1(const D3D12_GRAPHICS_PIPELINE_STATE_DESC& Desc) noexcept + : Flags(Desc.Flags) + , NodeMask(Desc.NodeMask) + , pRootSignature(Desc.pRootSignature) + , InputLayout(Desc.InputLayout) + , IBStripCutValue(Desc.IBStripCutValue) + , PrimitiveTopologyType(Desc.PrimitiveTopologyType) + , VS(Desc.VS) + , GS(Desc.GS) + , StreamOutput(Desc.StreamOutput) + , HS(Desc.HS) + , DS(Desc.DS) + , PS(Desc.PS) + , BlendState(CD3DX12_BLEND_DESC(Desc.BlendState)) + , DepthStencilState(CD3DX12_DEPTH_STENCIL_DESC1(Desc.DepthStencilState)) + , DSVFormat(Desc.DSVFormat) + , RasterizerState(CD3DX12_RASTERIZER_DESC(Desc.RasterizerState)) + , RTVFormats(CD3DX12_RT_FORMAT_ARRAY(Desc.RTVFormats, Desc.NumRenderTargets)) + , SampleDesc(Desc.SampleDesc) + , SampleMask(Desc.SampleMask) + , CachedPSO(Desc.CachedPSO) + , ViewInstancingDesc(CD3DX12_VIEW_INSTANCING_DESC(CD3DX12_DEFAULT())) + {} + CD3DX12_PIPELINE_STATE_STREAM1(const D3DX12_MESH_SHADER_PIPELINE_STATE_DESC& Desc) noexcept + : Flags(Desc.Flags) + , NodeMask(Desc.NodeMask) + , pRootSignature(Desc.pRootSignature) + , PrimitiveTopologyType(Desc.PrimitiveTopologyType) + , PS(Desc.PS) + , BlendState(CD3DX12_BLEND_DESC(Desc.BlendState)) + , DepthStencilState(CD3DX12_DEPTH_STENCIL_DESC1(Desc.DepthStencilState)) + , DSVFormat(Desc.DSVFormat) + , RasterizerState(CD3DX12_RASTERIZER_DESC(Desc.RasterizerState)) + , RTVFormats(CD3DX12_RT_FORMAT_ARRAY(Desc.RTVFormats, Desc.NumRenderTargets)) + , SampleDesc(Desc.SampleDesc) + , SampleMask(Desc.SampleMask) + , CachedPSO(Desc.CachedPSO) + , ViewInstancingDesc(CD3DX12_VIEW_INSTANCING_DESC(CD3DX12_DEFAULT())) + {} + CD3DX12_PIPELINE_STATE_STREAM1(const D3D12_COMPUTE_PIPELINE_STATE_DESC& Desc) noexcept + : Flags(Desc.Flags) + , NodeMask(Desc.NodeMask) + , pRootSignature(Desc.pRootSignature) + , CS(CD3DX12_SHADER_BYTECODE(Desc.CS)) + , CachedPSO(Desc.CachedPSO) + { + static_cast(DepthStencilState).DepthEnable = false; + } + CD3DX12_PIPELINE_STATE_STREAM_FLAGS Flags; + CD3DX12_PIPELINE_STATE_STREAM_NODE_MASK NodeMask; + CD3DX12_PIPELINE_STATE_STREAM_ROOT_SIGNATURE pRootSignature; + CD3DX12_PIPELINE_STATE_STREAM_INPUT_LAYOUT InputLayout; + CD3DX12_PIPELINE_STATE_STREAM_IB_STRIP_CUT_VALUE IBStripCutValue; + CD3DX12_PIPELINE_STATE_STREAM_PRIMITIVE_TOPOLOGY PrimitiveTopologyType; + CD3DX12_PIPELINE_STATE_STREAM_VS VS; + CD3DX12_PIPELINE_STATE_STREAM_GS GS; + CD3DX12_PIPELINE_STATE_STREAM_STREAM_OUTPUT StreamOutput; + CD3DX12_PIPELINE_STATE_STREAM_HS HS; + CD3DX12_PIPELINE_STATE_STREAM_DS DS; + CD3DX12_PIPELINE_STATE_STREAM_PS PS; + CD3DX12_PIPELINE_STATE_STREAM_CS CS; + CD3DX12_PIPELINE_STATE_STREAM_BLEND_DESC BlendState; + CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL1 DepthStencilState; + CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL_FORMAT DSVFormat; + CD3DX12_PIPELINE_STATE_STREAM_RASTERIZER RasterizerState; + CD3DX12_PIPELINE_STATE_STREAM_RENDER_TARGET_FORMATS RTVFormats; + CD3DX12_PIPELINE_STATE_STREAM_SAMPLE_DESC SampleDesc; + CD3DX12_PIPELINE_STATE_STREAM_SAMPLE_MASK SampleMask; + CD3DX12_PIPELINE_STATE_STREAM_CACHED_PSO CachedPSO; + CD3DX12_PIPELINE_STATE_STREAM_VIEW_INSTANCING ViewInstancingDesc; + D3D12_GRAPHICS_PIPELINE_STATE_DESC GraphicsDescV0() const noexcept + { + D3D12_GRAPHICS_PIPELINE_STATE_DESC D; + D.Flags = this->Flags; + D.NodeMask = this->NodeMask; + D.pRootSignature = this->pRootSignature; + D.InputLayout = this->InputLayout; + D.IBStripCutValue = this->IBStripCutValue; + D.PrimitiveTopologyType = this->PrimitiveTopologyType; + D.VS = this->VS; + D.GS = this->GS; + D.StreamOutput = this->StreamOutput; + D.HS = this->HS; + D.DS = this->DS; + D.PS = this->PS; + D.BlendState = this->BlendState; + D.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC1(D3D12_DEPTH_STENCIL_DESC1(this->DepthStencilState)); + D.DSVFormat = this->DSVFormat; + D.RasterizerState = this->RasterizerState; + D.NumRenderTargets = D3D12_RT_FORMAT_ARRAY(this->RTVFormats).NumRenderTargets; + memcpy(D.RTVFormats, D3D12_RT_FORMAT_ARRAY(this->RTVFormats).RTFormats, sizeof(D.RTVFormats)); + D.SampleDesc = this->SampleDesc; + D.SampleMask = this->SampleMask; + D.CachedPSO = this->CachedPSO; + return D; + } + D3D12_COMPUTE_PIPELINE_STATE_DESC ComputeDescV0() const noexcept + { + D3D12_COMPUTE_PIPELINE_STATE_DESC D; + D.Flags = this->Flags; + D.NodeMask = this->NodeMask; + D.pRootSignature = this->pRootSignature; + D.CS = this->CS; + D.CachedPSO = this->CachedPSO; + return D; + } +}; + + +struct CD3DX12_PIPELINE_MESH_STATE_STREAM +{ + CD3DX12_PIPELINE_MESH_STATE_STREAM() = default; + CD3DX12_PIPELINE_MESH_STATE_STREAM(const D3DX12_MESH_SHADER_PIPELINE_STATE_DESC& Desc) noexcept + : Flags(Desc.Flags) + , NodeMask(Desc.NodeMask) + , pRootSignature(Desc.pRootSignature) + , PS(Desc.PS) + , AS(Desc.AS) + , MS(Desc.MS) + , BlendState(CD3DX12_BLEND_DESC(Desc.BlendState)) + , DepthStencilState(CD3DX12_DEPTH_STENCIL_DESC1(Desc.DepthStencilState)) + , DSVFormat(Desc.DSVFormat) + , RasterizerState(CD3DX12_RASTERIZER_DESC(Desc.RasterizerState)) + , RTVFormats(CD3DX12_RT_FORMAT_ARRAY(Desc.RTVFormats, Desc.NumRenderTargets)) + , SampleDesc(Desc.SampleDesc) + , SampleMask(Desc.SampleMask) + , CachedPSO(Desc.CachedPSO) + , ViewInstancingDesc(CD3DX12_VIEW_INSTANCING_DESC(CD3DX12_DEFAULT())) + {} + CD3DX12_PIPELINE_STATE_STREAM_FLAGS Flags; + CD3DX12_PIPELINE_STATE_STREAM_NODE_MASK NodeMask; + CD3DX12_PIPELINE_STATE_STREAM_ROOT_SIGNATURE pRootSignature; + CD3DX12_PIPELINE_STATE_STREAM_PS PS; + CD3DX12_PIPELINE_STATE_STREAM_AS AS; + CD3DX12_PIPELINE_STATE_STREAM_MS MS; + CD3DX12_PIPELINE_STATE_STREAM_BLEND_DESC BlendState; + CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL1 DepthStencilState; + CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL_FORMAT DSVFormat; + CD3DX12_PIPELINE_STATE_STREAM_RASTERIZER RasterizerState; + CD3DX12_PIPELINE_STATE_STREAM_RENDER_TARGET_FORMATS RTVFormats; + CD3DX12_PIPELINE_STATE_STREAM_SAMPLE_DESC SampleDesc; + CD3DX12_PIPELINE_STATE_STREAM_SAMPLE_MASK SampleMask; + CD3DX12_PIPELINE_STATE_STREAM_CACHED_PSO CachedPSO; + CD3DX12_PIPELINE_STATE_STREAM_VIEW_INSTANCING ViewInstancingDesc; + D3DX12_MESH_SHADER_PIPELINE_STATE_DESC MeshShaderDescV0() const noexcept + { + D3DX12_MESH_SHADER_PIPELINE_STATE_DESC D; + D.Flags = this->Flags; + D.NodeMask = this->NodeMask; + D.pRootSignature = this->pRootSignature; + D.PS = this->PS; + D.AS = this->AS; + D.MS = this->MS; + D.BlendState = this->BlendState; + D.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC1(D3D12_DEPTH_STENCIL_DESC1(this->DepthStencilState)); + D.DSVFormat = this->DSVFormat; + D.RasterizerState = this->RasterizerState; + D.NumRenderTargets = D3D12_RT_FORMAT_ARRAY(this->RTVFormats).NumRenderTargets; + memcpy(D.RTVFormats, D3D12_RT_FORMAT_ARRAY(this->RTVFormats).RTFormats, sizeof(D.RTVFormats)); + D.SampleDesc = this->SampleDesc; + D.SampleMask = this->SampleMask; + D.CachedPSO = this->CachedPSO; + return D; + } +}; + +// CD3DX12_PIPELINE_STATE_STREAM works on OS Build 15063+ but does not support new subobject(s) added in OS Build 16299+. +// See CD3DX12_PIPELINE_STATE_STREAM1 for instance. +struct CD3DX12_PIPELINE_STATE_STREAM +{ + CD3DX12_PIPELINE_STATE_STREAM() = default; + CD3DX12_PIPELINE_STATE_STREAM(const D3D12_GRAPHICS_PIPELINE_STATE_DESC& Desc) noexcept + : Flags(Desc.Flags) + , NodeMask(Desc.NodeMask) + , pRootSignature(Desc.pRootSignature) + , InputLayout(Desc.InputLayout) + , IBStripCutValue(Desc.IBStripCutValue) + , PrimitiveTopologyType(Desc.PrimitiveTopologyType) + , VS(Desc.VS) + , GS(Desc.GS) + , StreamOutput(Desc.StreamOutput) + , HS(Desc.HS) + , DS(Desc.DS) + , PS(Desc.PS) + , BlendState(CD3DX12_BLEND_DESC(Desc.BlendState)) + , DepthStencilState(CD3DX12_DEPTH_STENCIL_DESC1(Desc.DepthStencilState)) + , DSVFormat(Desc.DSVFormat) + , RasterizerState(CD3DX12_RASTERIZER_DESC(Desc.RasterizerState)) + , RTVFormats(CD3DX12_RT_FORMAT_ARRAY(Desc.RTVFormats, Desc.NumRenderTargets)) + , SampleDesc(Desc.SampleDesc) + , SampleMask(Desc.SampleMask) + , CachedPSO(Desc.CachedPSO) + {} + CD3DX12_PIPELINE_STATE_STREAM(const D3D12_COMPUTE_PIPELINE_STATE_DESC& Desc) noexcept + : Flags(Desc.Flags) + , NodeMask(Desc.NodeMask) + , pRootSignature(Desc.pRootSignature) + , CS(CD3DX12_SHADER_BYTECODE(Desc.CS)) + , CachedPSO(Desc.CachedPSO) + {} + CD3DX12_PIPELINE_STATE_STREAM_FLAGS Flags; + CD3DX12_PIPELINE_STATE_STREAM_NODE_MASK NodeMask; + CD3DX12_PIPELINE_STATE_STREAM_ROOT_SIGNATURE pRootSignature; + CD3DX12_PIPELINE_STATE_STREAM_INPUT_LAYOUT InputLayout; + CD3DX12_PIPELINE_STATE_STREAM_IB_STRIP_CUT_VALUE IBStripCutValue; + CD3DX12_PIPELINE_STATE_STREAM_PRIMITIVE_TOPOLOGY PrimitiveTopologyType; + CD3DX12_PIPELINE_STATE_STREAM_VS VS; + CD3DX12_PIPELINE_STATE_STREAM_GS GS; + CD3DX12_PIPELINE_STATE_STREAM_STREAM_OUTPUT StreamOutput; + CD3DX12_PIPELINE_STATE_STREAM_HS HS; + CD3DX12_PIPELINE_STATE_STREAM_DS DS; + CD3DX12_PIPELINE_STATE_STREAM_PS PS; + CD3DX12_PIPELINE_STATE_STREAM_CS CS; + CD3DX12_PIPELINE_STATE_STREAM_BLEND_DESC BlendState; + CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL1 DepthStencilState; + CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL_FORMAT DSVFormat; + CD3DX12_PIPELINE_STATE_STREAM_RASTERIZER RasterizerState; + CD3DX12_PIPELINE_STATE_STREAM_RENDER_TARGET_FORMATS RTVFormats; + CD3DX12_PIPELINE_STATE_STREAM_SAMPLE_DESC SampleDesc; + CD3DX12_PIPELINE_STATE_STREAM_SAMPLE_MASK SampleMask; + CD3DX12_PIPELINE_STATE_STREAM_CACHED_PSO CachedPSO; + D3D12_GRAPHICS_PIPELINE_STATE_DESC GraphicsDescV0() const noexcept + { + D3D12_GRAPHICS_PIPELINE_STATE_DESC D; + D.Flags = this->Flags; + D.NodeMask = this->NodeMask; + D.pRootSignature = this->pRootSignature; + D.InputLayout = this->InputLayout; + D.IBStripCutValue = this->IBStripCutValue; + D.PrimitiveTopologyType = this->PrimitiveTopologyType; + D.VS = this->VS; + D.GS = this->GS; + D.StreamOutput = this->StreamOutput; + D.HS = this->HS; + D.DS = this->DS; + D.PS = this->PS; + D.BlendState = this->BlendState; + D.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC1(D3D12_DEPTH_STENCIL_DESC1(this->DepthStencilState)); + D.DSVFormat = this->DSVFormat; + D.RasterizerState = this->RasterizerState; + D.NumRenderTargets = D3D12_RT_FORMAT_ARRAY(this->RTVFormats).NumRenderTargets; + memcpy(D.RTVFormats, D3D12_RT_FORMAT_ARRAY(this->RTVFormats).RTFormats, sizeof(D.RTVFormats)); + D.SampleDesc = this->SampleDesc; + D.SampleMask = this->SampleMask; + D.CachedPSO = this->CachedPSO; + return D; + } + D3D12_COMPUTE_PIPELINE_STATE_DESC ComputeDescV0() const noexcept + { + D3D12_COMPUTE_PIPELINE_STATE_DESC D; + D.Flags = this->Flags; + D.NodeMask = this->NodeMask; + D.pRootSignature = this->pRootSignature; + D.CS = this->CS; + D.CachedPSO = this->CachedPSO; + return D; + } +}; + +struct CD3DX12_PIPELINE_STATE_STREAM2_PARSE_HELPER : public ID3DX12PipelineParserCallbacks +{ + CD3DX12_PIPELINE_STATE_STREAM2 PipelineStream; + CD3DX12_PIPELINE_STATE_STREAM2_PARSE_HELPER() noexcept + : SeenDSS(false) + { + // Adjust defaults to account for absent members. + PipelineStream.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; + + // Depth disabled if no DSV format specified. + static_cast(PipelineStream.DepthStencilState).DepthEnable = false; + } + + // ID3DX12PipelineParserCallbacks + void FlagsCb(D3D12_PIPELINE_STATE_FLAGS Flags) override {PipelineStream.Flags = Flags;} + void NodeMaskCb(UINT NodeMask) override {PipelineStream.NodeMask = NodeMask;} + void RootSignatureCb(ID3D12RootSignature* pRootSignature) override {PipelineStream.pRootSignature = pRootSignature;} + void InputLayoutCb(const D3D12_INPUT_LAYOUT_DESC& InputLayout) override {PipelineStream.InputLayout = InputLayout;} + void IBStripCutValueCb(D3D12_INDEX_BUFFER_STRIP_CUT_VALUE IBStripCutValue) override {PipelineStream.IBStripCutValue = IBStripCutValue;} + void PrimitiveTopologyTypeCb(D3D12_PRIMITIVE_TOPOLOGY_TYPE PrimitiveTopologyType) override {PipelineStream.PrimitiveTopologyType = PrimitiveTopologyType;} + void VSCb(const D3D12_SHADER_BYTECODE& VS) override {PipelineStream.VS = VS;} + void GSCb(const D3D12_SHADER_BYTECODE& GS) override {PipelineStream.GS = GS;} + void StreamOutputCb(const D3D12_STREAM_OUTPUT_DESC& StreamOutput) override {PipelineStream.StreamOutput = StreamOutput;} + void HSCb(const D3D12_SHADER_BYTECODE& HS) override {PipelineStream.HS = HS;} + void DSCb(const D3D12_SHADER_BYTECODE& DS) override {PipelineStream.DS = DS;} + void PSCb(const D3D12_SHADER_BYTECODE& PS) override {PipelineStream.PS = PS;} + void CSCb(const D3D12_SHADER_BYTECODE& CS) override {PipelineStream.CS = CS;} + void ASCb(const D3D12_SHADER_BYTECODE& AS) override {PipelineStream.AS = AS;} + void MSCb(const D3D12_SHADER_BYTECODE& MS) override {PipelineStream.MS = MS;} + void BlendStateCb(const D3D12_BLEND_DESC& BlendState) override {PipelineStream.BlendState = CD3DX12_BLEND_DESC(BlendState);} + void DepthStencilStateCb(const D3D12_DEPTH_STENCIL_DESC& DepthStencilState) override + { + PipelineStream.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC1(DepthStencilState); + SeenDSS = true; + } + void DepthStencilState1Cb(const D3D12_DEPTH_STENCIL_DESC1& DepthStencilState) override + { + PipelineStream.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC1(DepthStencilState); + SeenDSS = true; + } + void DSVFormatCb(DXGI_FORMAT DSVFormat) override + { + PipelineStream.DSVFormat = DSVFormat; + if (!SeenDSS && DSVFormat != DXGI_FORMAT_UNKNOWN) + { + // Re-enable depth for the default state. + static_cast(PipelineStream.DepthStencilState).DepthEnable = true; + } + } + void RasterizerStateCb(const D3D12_RASTERIZER_DESC& RasterizerState) override {PipelineStream.RasterizerState = CD3DX12_RASTERIZER_DESC(RasterizerState);} + void RTVFormatsCb(const D3D12_RT_FORMAT_ARRAY& RTVFormats) override {PipelineStream.RTVFormats = RTVFormats;} + void SampleDescCb(const DXGI_SAMPLE_DESC& SampleDesc) override {PipelineStream.SampleDesc = SampleDesc;} + void SampleMaskCb(UINT SampleMask) override {PipelineStream.SampleMask = SampleMask;} + void ViewInstancingCb(const D3D12_VIEW_INSTANCING_DESC& ViewInstancingDesc) override {PipelineStream.ViewInstancingDesc = CD3DX12_VIEW_INSTANCING_DESC(ViewInstancingDesc);} + void CachedPSOCb(const D3D12_CACHED_PIPELINE_STATE& CachedPSO) override {PipelineStream.CachedPSO = CachedPSO;} + +private: + bool SeenDSS; +}; + +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 606) +struct CD3DX12_PIPELINE_STATE_STREAM3_PARSE_HELPER : public ID3DX12PipelineParserCallbacks +{ + CD3DX12_PIPELINE_STATE_STREAM3 PipelineStream; + CD3DX12_PIPELINE_STATE_STREAM3_PARSE_HELPER() noexcept + : SeenDSS(false) + { + // Adjust defaults to account for absent members. + PipelineStream.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; + + // Depth disabled if no DSV format specified. + static_cast(PipelineStream.DepthStencilState).DepthEnable = false; + } + + // ID3DX12PipelineParserCallbacks + void FlagsCb(D3D12_PIPELINE_STATE_FLAGS Flags) override { PipelineStream.Flags = Flags; } + void NodeMaskCb(UINT NodeMask) override { PipelineStream.NodeMask = NodeMask; } + void RootSignatureCb(ID3D12RootSignature* pRootSignature) override { PipelineStream.pRootSignature = pRootSignature; } + void InputLayoutCb(const D3D12_INPUT_LAYOUT_DESC& InputLayout) override { PipelineStream.InputLayout = InputLayout; } + void IBStripCutValueCb(D3D12_INDEX_BUFFER_STRIP_CUT_VALUE IBStripCutValue) override { PipelineStream.IBStripCutValue = IBStripCutValue; } + void PrimitiveTopologyTypeCb(D3D12_PRIMITIVE_TOPOLOGY_TYPE PrimitiveTopologyType) override { PipelineStream.PrimitiveTopologyType = PrimitiveTopologyType; } + void VSCb(const D3D12_SHADER_BYTECODE& VS) override { PipelineStream.VS = VS; } + void GSCb(const D3D12_SHADER_BYTECODE& GS) override { PipelineStream.GS = GS; } + void StreamOutputCb(const D3D12_STREAM_OUTPUT_DESC& StreamOutput) override { PipelineStream.StreamOutput = StreamOutput; } + void HSCb(const D3D12_SHADER_BYTECODE& HS) override { PipelineStream.HS = HS; } + void DSCb(const D3D12_SHADER_BYTECODE& DS) override { PipelineStream.DS = DS; } + void PSCb(const D3D12_SHADER_BYTECODE& PS) override { PipelineStream.PS = PS; } + void CSCb(const D3D12_SHADER_BYTECODE& CS) override { PipelineStream.CS = CS; } + void ASCb(const D3D12_SHADER_BYTECODE& AS) override { PipelineStream.AS = AS; } + void MSCb(const D3D12_SHADER_BYTECODE& MS) override { PipelineStream.MS = MS; } + void BlendStateCb(const D3D12_BLEND_DESC& BlendState) override { PipelineStream.BlendState = CD3DX12_BLEND_DESC(BlendState); } + void DepthStencilStateCb(const D3D12_DEPTH_STENCIL_DESC& DepthStencilState) override + { + PipelineStream.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC2(DepthStencilState); + SeenDSS = true; + } + void DepthStencilState1Cb(const D3D12_DEPTH_STENCIL_DESC1& DepthStencilState) override + { + PipelineStream.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC2(DepthStencilState); + SeenDSS = true; + } + void DepthStencilState2Cb(const D3D12_DEPTH_STENCIL_DESC2& DepthStencilState) override + { + PipelineStream.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC2(DepthStencilState); + SeenDSS = true; + } + void DSVFormatCb(DXGI_FORMAT DSVFormat) override + { + PipelineStream.DSVFormat = DSVFormat; + if (!SeenDSS && DSVFormat != DXGI_FORMAT_UNKNOWN) + { + // Re-enable depth for the default state. + static_cast(PipelineStream.DepthStencilState).DepthEnable = true; + } + } + void RasterizerStateCb(const D3D12_RASTERIZER_DESC& RasterizerState) override { PipelineStream.RasterizerState = CD3DX12_RASTERIZER_DESC(RasterizerState); } + void RTVFormatsCb(const D3D12_RT_FORMAT_ARRAY& RTVFormats) override { PipelineStream.RTVFormats = RTVFormats; } + void SampleDescCb(const DXGI_SAMPLE_DESC& SampleDesc) override { PipelineStream.SampleDesc = SampleDesc; } + void SampleMaskCb(UINT SampleMask) override { PipelineStream.SampleMask = SampleMask; } + void ViewInstancingCb(const D3D12_VIEW_INSTANCING_DESC& ViewInstancingDesc) override { PipelineStream.ViewInstancingDesc = CD3DX12_VIEW_INSTANCING_DESC(ViewInstancingDesc); } + void CachedPSOCb(const D3D12_CACHED_PIPELINE_STATE& CachedPSO) override { PipelineStream.CachedPSO = CachedPSO; } + +private: + bool SeenDSS; +}; +#endif // D3D12_SDK_VERSION >= 606 + +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 608) +struct CD3DX12_PIPELINE_STATE_STREAM4_PARSE_HELPER : public ID3DX12PipelineParserCallbacks +{ + CD3DX12_PIPELINE_STATE_STREAM4 PipelineStream; + CD3DX12_PIPELINE_STATE_STREAM4_PARSE_HELPER() noexcept + : SeenDSS(false) + { + // Adjust defaults to account for absent members. + PipelineStream.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; + + // Depth disabled if no DSV format specified. + static_cast(PipelineStream.DepthStencilState).DepthEnable = false; + } + + // ID3DX12PipelineParserCallbacks + void FlagsCb(D3D12_PIPELINE_STATE_FLAGS Flags) override { PipelineStream.Flags = Flags; } + void NodeMaskCb(UINT NodeMask) override { PipelineStream.NodeMask = NodeMask; } + void RootSignatureCb(ID3D12RootSignature* pRootSignature) override { PipelineStream.pRootSignature = pRootSignature; } + void InputLayoutCb(const D3D12_INPUT_LAYOUT_DESC& InputLayout) override { PipelineStream.InputLayout = InputLayout; } + void IBStripCutValueCb(D3D12_INDEX_BUFFER_STRIP_CUT_VALUE IBStripCutValue) override { PipelineStream.IBStripCutValue = IBStripCutValue; } + void PrimitiveTopologyTypeCb(D3D12_PRIMITIVE_TOPOLOGY_TYPE PrimitiveTopologyType) override { PipelineStream.PrimitiveTopologyType = PrimitiveTopologyType; } + void VSCb(const D3D12_SHADER_BYTECODE& VS) override { PipelineStream.VS = VS; } + void GSCb(const D3D12_SHADER_BYTECODE& GS) override { PipelineStream.GS = GS; } + void StreamOutputCb(const D3D12_STREAM_OUTPUT_DESC& StreamOutput) override { PipelineStream.StreamOutput = StreamOutput; } + void HSCb(const D3D12_SHADER_BYTECODE& HS) override { PipelineStream.HS = HS; } + void DSCb(const D3D12_SHADER_BYTECODE& DS) override { PipelineStream.DS = DS; } + void PSCb(const D3D12_SHADER_BYTECODE& PS) override { PipelineStream.PS = PS; } + void CSCb(const D3D12_SHADER_BYTECODE& CS) override { PipelineStream.CS = CS; } + void ASCb(const D3D12_SHADER_BYTECODE& AS) override { PipelineStream.AS = AS; } + void MSCb(const D3D12_SHADER_BYTECODE& MS) override { PipelineStream.MS = MS; } + void BlendStateCb(const D3D12_BLEND_DESC& BlendState) override { PipelineStream.BlendState = CD3DX12_BLEND_DESC(BlendState); } + void DepthStencilStateCb(const D3D12_DEPTH_STENCIL_DESC& DepthStencilState) override + { + PipelineStream.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC2(DepthStencilState); + SeenDSS = true; + } + void DepthStencilState1Cb(const D3D12_DEPTH_STENCIL_DESC1& DepthStencilState) override + { + PipelineStream.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC2(DepthStencilState); + SeenDSS = true; + } + void DepthStencilState2Cb(const D3D12_DEPTH_STENCIL_DESC2& DepthStencilState) override + { + PipelineStream.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC2(DepthStencilState); + SeenDSS = true; + } + void DSVFormatCb(DXGI_FORMAT DSVFormat) override + { + PipelineStream.DSVFormat = DSVFormat; + if (!SeenDSS && DSVFormat != DXGI_FORMAT_UNKNOWN) + { + // Re-enable depth for the default state. + static_cast(PipelineStream.DepthStencilState).DepthEnable = true; + } + } + void RasterizerStateCb(const D3D12_RASTERIZER_DESC& RasterizerState) override { PipelineStream.RasterizerState = CD3DX12_RASTERIZER_DESC1(RasterizerState); } + void RasterizerState1Cb(const D3D12_RASTERIZER_DESC1& RasterizerState) override { PipelineStream.RasterizerState = CD3DX12_RASTERIZER_DESC1(RasterizerState); } + void RTVFormatsCb(const D3D12_RT_FORMAT_ARRAY& RTVFormats) override { PipelineStream.RTVFormats = RTVFormats; } + void SampleDescCb(const DXGI_SAMPLE_DESC& SampleDesc) override { PipelineStream.SampleDesc = SampleDesc; } + void SampleMaskCb(UINT SampleMask) override { PipelineStream.SampleMask = SampleMask; } + void ViewInstancingCb(const D3D12_VIEW_INSTANCING_DESC& ViewInstancingDesc) override { PipelineStream.ViewInstancingDesc = CD3DX12_VIEW_INSTANCING_DESC(ViewInstancingDesc); } + void CachedPSOCb(const D3D12_CACHED_PIPELINE_STATE& CachedPSO) override { PipelineStream.CachedPSO = CachedPSO; } + +private: + bool SeenDSS; +}; +#endif // D3D12_SDK_VERSION >= 608 + +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 610) +struct CD3DX12_PIPELINE_STATE_STREAM5_PARSE_HELPER : public ID3DX12PipelineParserCallbacks +{ + CD3DX12_PIPELINE_STATE_STREAM5 PipelineStream; + CD3DX12_PIPELINE_STATE_STREAM5_PARSE_HELPER() noexcept + : SeenDSS(false) + { + // Adjust defaults to account for absent members. + PipelineStream.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; + + // Depth disabled if no DSV format specified. + static_cast(PipelineStream.DepthStencilState).DepthEnable = false; + } + + // ID3DX12PipelineParserCallbacks + void FlagsCb(D3D12_PIPELINE_STATE_FLAGS Flags) override { PipelineStream.Flags = Flags; } + void NodeMaskCb(UINT NodeMask) override { PipelineStream.NodeMask = NodeMask; } + void RootSignatureCb(ID3D12RootSignature* pRootSignature) override { PipelineStream.pRootSignature = pRootSignature; } + void InputLayoutCb(const D3D12_INPUT_LAYOUT_DESC& InputLayout) override { PipelineStream.InputLayout = InputLayout; } + void IBStripCutValueCb(D3D12_INDEX_BUFFER_STRIP_CUT_VALUE IBStripCutValue) override { PipelineStream.IBStripCutValue = IBStripCutValue; } + void PrimitiveTopologyTypeCb(D3D12_PRIMITIVE_TOPOLOGY_TYPE PrimitiveTopologyType) override { PipelineStream.PrimitiveTopologyType = PrimitiveTopologyType; } + void VSCb(const D3D12_SHADER_BYTECODE& VS) override { PipelineStream.VS = VS; } + void GSCb(const D3D12_SHADER_BYTECODE& GS) override { PipelineStream.GS = GS; } + void StreamOutputCb(const D3D12_STREAM_OUTPUT_DESC& StreamOutput) override { PipelineStream.StreamOutput = StreamOutput; } + void HSCb(const D3D12_SHADER_BYTECODE& HS) override { PipelineStream.HS = HS; } + void DSCb(const D3D12_SHADER_BYTECODE& DS) override { PipelineStream.DS = DS; } + void PSCb(const D3D12_SHADER_BYTECODE& PS) override { PipelineStream.PS = PS; } + void CSCb(const D3D12_SHADER_BYTECODE& CS) override { PipelineStream.CS = CS; } + void ASCb(const D3D12_SHADER_BYTECODE& AS) override { PipelineStream.AS = AS; } + void MSCb(const D3D12_SHADER_BYTECODE& MS) override { PipelineStream.MS = MS; } + void BlendStateCb(const D3D12_BLEND_DESC& BlendState) override { PipelineStream.BlendState = CD3DX12_BLEND_DESC(BlendState); } + void DepthStencilStateCb(const D3D12_DEPTH_STENCIL_DESC& DepthStencilState) override + { + PipelineStream.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC2(DepthStencilState); + SeenDSS = true; + } + void DepthStencilState1Cb(const D3D12_DEPTH_STENCIL_DESC1& DepthStencilState) override + { + PipelineStream.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC2(DepthStencilState); + SeenDSS = true; + } + void DepthStencilState2Cb(const D3D12_DEPTH_STENCIL_DESC2& DepthStencilState) override + { + PipelineStream.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC2(DepthStencilState); + SeenDSS = true; + } + void DSVFormatCb(DXGI_FORMAT DSVFormat) override + { + PipelineStream.DSVFormat = DSVFormat; + if (!SeenDSS && DSVFormat != DXGI_FORMAT_UNKNOWN) + { + // Re-enable depth for the default state. + static_cast(PipelineStream.DepthStencilState).DepthEnable = true; + } + } + void RasterizerStateCb(const D3D12_RASTERIZER_DESC& RasterizerState) override { PipelineStream.RasterizerState = CD3DX12_RASTERIZER_DESC2(RasterizerState); } + void RasterizerState1Cb(const D3D12_RASTERIZER_DESC1& RasterizerState) override { PipelineStream.RasterizerState = CD3DX12_RASTERIZER_DESC2(RasterizerState); } + void RasterizerState2Cb(const D3D12_RASTERIZER_DESC2& RasterizerState) override { PipelineStream.RasterizerState = CD3DX12_RASTERIZER_DESC2(RasterizerState); } + void RTVFormatsCb(const D3D12_RT_FORMAT_ARRAY& RTVFormats) override { PipelineStream.RTVFormats = RTVFormats; } + void SampleDescCb(const DXGI_SAMPLE_DESC& SampleDesc) override { PipelineStream.SampleDesc = SampleDesc; } + void SampleMaskCb(UINT SampleMask) override { PipelineStream.SampleMask = SampleMask; } + void ViewInstancingCb(const D3D12_VIEW_INSTANCING_DESC& ViewInstancingDesc) override { PipelineStream.ViewInstancingDesc = CD3DX12_VIEW_INSTANCING_DESC(ViewInstancingDesc); } + void CachedPSOCb(const D3D12_CACHED_PIPELINE_STATE& CachedPSO) override { PipelineStream.CachedPSO = CachedPSO; } + +private: + bool SeenDSS; +}; +#endif // D3D12_SDK_VERSION >= 610 + +struct CD3DX12_PIPELINE_STATE_STREAM_PARSE_HELPER : public ID3DX12PipelineParserCallbacks +{ + CD3DX12_PIPELINE_STATE_STREAM1 PipelineStream; + CD3DX12_PIPELINE_STATE_STREAM_PARSE_HELPER() noexcept + : SeenDSS(false) + { + // Adjust defaults to account for absent members. + PipelineStream.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; + + // Depth disabled if no DSV format specified. + static_cast(PipelineStream.DepthStencilState).DepthEnable = false; + } + + // ID3DX12PipelineParserCallbacks + void FlagsCb(D3D12_PIPELINE_STATE_FLAGS Flags) override {PipelineStream.Flags = Flags;} + void NodeMaskCb(UINT NodeMask) override {PipelineStream.NodeMask = NodeMask;} + void RootSignatureCb(ID3D12RootSignature* pRootSignature) override {PipelineStream.pRootSignature = pRootSignature;} + void InputLayoutCb(const D3D12_INPUT_LAYOUT_DESC& InputLayout) override {PipelineStream.InputLayout = InputLayout;} + void IBStripCutValueCb(D3D12_INDEX_BUFFER_STRIP_CUT_VALUE IBStripCutValue) override {PipelineStream.IBStripCutValue = IBStripCutValue;} + void PrimitiveTopologyTypeCb(D3D12_PRIMITIVE_TOPOLOGY_TYPE PrimitiveTopologyType) override {PipelineStream.PrimitiveTopologyType = PrimitiveTopologyType;} + void VSCb(const D3D12_SHADER_BYTECODE& VS) override {PipelineStream.VS = VS;} + void GSCb(const D3D12_SHADER_BYTECODE& GS) override {PipelineStream.GS = GS;} + void StreamOutputCb(const D3D12_STREAM_OUTPUT_DESC& StreamOutput) override {PipelineStream.StreamOutput = StreamOutput;} + void HSCb(const D3D12_SHADER_BYTECODE& HS) override {PipelineStream.HS = HS;} + void DSCb(const D3D12_SHADER_BYTECODE& DS) override {PipelineStream.DS = DS;} + void PSCb(const D3D12_SHADER_BYTECODE& PS) override {PipelineStream.PS = PS;} + void CSCb(const D3D12_SHADER_BYTECODE& CS) override {PipelineStream.CS = CS;} + void BlendStateCb(const D3D12_BLEND_DESC& BlendState) override {PipelineStream.BlendState = CD3DX12_BLEND_DESC(BlendState);} + void DepthStencilStateCb(const D3D12_DEPTH_STENCIL_DESC& DepthStencilState) override + { + PipelineStream.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC1(DepthStencilState); + SeenDSS = true; + } + void DepthStencilState1Cb(const D3D12_DEPTH_STENCIL_DESC1& DepthStencilState) override + { + PipelineStream.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC1(DepthStencilState); + SeenDSS = true; + } + void DSVFormatCb(DXGI_FORMAT DSVFormat) override + { + PipelineStream.DSVFormat = DSVFormat; + if (!SeenDSS && DSVFormat != DXGI_FORMAT_UNKNOWN) + { + // Re-enable depth for the default state. + static_cast(PipelineStream.DepthStencilState).DepthEnable = true; + } + } + void RasterizerStateCb(const D3D12_RASTERIZER_DESC& RasterizerState) override {PipelineStream.RasterizerState = CD3DX12_RASTERIZER_DESC(RasterizerState);} + void RTVFormatsCb(const D3D12_RT_FORMAT_ARRAY& RTVFormats) override {PipelineStream.RTVFormats = RTVFormats;} + void SampleDescCb(const DXGI_SAMPLE_DESC& SampleDesc) override {PipelineStream.SampleDesc = SampleDesc;} + void SampleMaskCb(UINT SampleMask) override {PipelineStream.SampleMask = SampleMask;} + void ViewInstancingCb(const D3D12_VIEW_INSTANCING_DESC& ViewInstancingDesc) override {PipelineStream.ViewInstancingDesc = CD3DX12_VIEW_INSTANCING_DESC(ViewInstancingDesc);} + void CachedPSOCb(const D3D12_CACHED_PIPELINE_STATE& CachedPSO) override {PipelineStream.CachedPSO = CachedPSO;} + +private: + bool SeenDSS; +}; + +inline D3D12_PIPELINE_STATE_SUBOBJECT_TYPE D3DX12GetBaseSubobjectType(D3D12_PIPELINE_STATE_SUBOBJECT_TYPE SubobjectType) noexcept +{ + switch (SubobjectType) + { + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL1: + return D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL; +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 606) + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL2: + return D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL; +#endif +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 608) + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_RASTERIZER1: + return D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_RASTERIZER; +#endif + default: + return SubobjectType; + } +} + +inline HRESULT D3DX12ParsePipelineStream(const D3D12_PIPELINE_STATE_STREAM_DESC& Desc, ID3DX12PipelineParserCallbacks* pCallbacks) +{ + if (pCallbacks == nullptr) + { + return E_INVALIDARG; + } + + if (Desc.SizeInBytes == 0 || Desc.pPipelineStateSubobjectStream == nullptr) + { + pCallbacks->ErrorBadInputParameter(1); // first parameter issue + return E_INVALIDARG; + } + + bool SubobjectSeen[D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_MAX_VALID] = {}; + for (SIZE_T CurOffset = 0, SizeOfSubobject = 0; CurOffset < Desc.SizeInBytes; CurOffset += SizeOfSubobject) + { + BYTE* pStream = static_cast(Desc.pPipelineStateSubobjectStream)+CurOffset; + auto SubobjectType = *reinterpret_cast(pStream); + if (SubobjectType < 0 || SubobjectType >= D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_MAX_VALID) + { + pCallbacks->ErrorUnknownSubobject(SubobjectType); + return E_INVALIDARG; + } + if (SubobjectSeen[D3DX12GetBaseSubobjectType(SubobjectType)]) + { + pCallbacks->ErrorDuplicateSubobject(SubobjectType); + return E_INVALIDARG; // disallow subobject duplicates in a stream + } + SubobjectSeen[SubobjectType] = true; + switch (SubobjectType) + { + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_ROOT_SIGNATURE: + pCallbacks->RootSignatureCb(*reinterpret_cast(pStream)); + SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::pRootSignature); + break; + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_VS: + pCallbacks->VSCb(*reinterpret_cast(pStream)); + SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::VS); + break; + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_PS: + pCallbacks->PSCb(*reinterpret_cast(pStream)); + SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::PS); + break; + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DS: + pCallbacks->DSCb(*reinterpret_cast(pStream)); + SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::DS); + break; + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_HS: + pCallbacks->HSCb(*reinterpret_cast(pStream)); + SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::HS); + break; + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_GS: + pCallbacks->GSCb(*reinterpret_cast(pStream)); + SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::GS); + break; + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_CS: + pCallbacks->CSCb(*reinterpret_cast(pStream)); + SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::CS); + break; + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_AS: + pCallbacks->ASCb(*reinterpret_cast(pStream)); + SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM2::AS); + break; + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_MS: + pCallbacks->MSCb(*reinterpret_cast(pStream)); + SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM2::MS); + break; + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_STREAM_OUTPUT: + pCallbacks->StreamOutputCb(*reinterpret_cast(pStream)); + SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::StreamOutput); + break; + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_BLEND: + pCallbacks->BlendStateCb(*reinterpret_cast(pStream)); + SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::BlendState); + break; + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_SAMPLE_MASK: + pCallbacks->SampleMaskCb(*reinterpret_cast(pStream)); + SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::SampleMask); + break; + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_RASTERIZER: + pCallbacks->RasterizerStateCb(*reinterpret_cast(pStream)); + SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::RasterizerState); + break; +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 608) + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_RASTERIZER1: + pCallbacks->RasterizerState1Cb(*reinterpret_cast(pStream)); + SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM4::RasterizerState); + break; +#endif +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 610) + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_RASTERIZER2: + pCallbacks->RasterizerState2Cb(*reinterpret_cast(pStream)); + SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM5::RasterizerState); + break; +#endif + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL: + pCallbacks->DepthStencilStateCb(*reinterpret_cast(pStream)); + SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL); + break; + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL1: + pCallbacks->DepthStencilState1Cb(*reinterpret_cast(pStream)); + SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::DepthStencilState); + break; +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 606) + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL2: + pCallbacks->DepthStencilState2Cb(*reinterpret_cast(pStream)); + SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM3::DepthStencilState); + break; +#endif + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_INPUT_LAYOUT: + pCallbacks->InputLayoutCb(*reinterpret_cast(pStream)); + SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::InputLayout); + break; + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_IB_STRIP_CUT_VALUE: + pCallbacks->IBStripCutValueCb(*reinterpret_cast(pStream)); + SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::IBStripCutValue); + break; + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_PRIMITIVE_TOPOLOGY: + pCallbacks->PrimitiveTopologyTypeCb(*reinterpret_cast(pStream)); + SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::PrimitiveTopologyType); + break; + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_RENDER_TARGET_FORMATS: + pCallbacks->RTVFormatsCb(*reinterpret_cast(pStream)); + SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::RTVFormats); + break; + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL_FORMAT: + pCallbacks->DSVFormatCb(*reinterpret_cast(pStream)); + SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::DSVFormat); + break; + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_SAMPLE_DESC: + pCallbacks->SampleDescCb(*reinterpret_cast(pStream)); + SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::SampleDesc); + break; + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_NODE_MASK: + pCallbacks->NodeMaskCb(*reinterpret_cast(pStream)); + SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::NodeMask); + break; + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_CACHED_PSO: + pCallbacks->CachedPSOCb(*reinterpret_cast(pStream)); + SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::CachedPSO); + break; + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_FLAGS: + pCallbacks->FlagsCb(*reinterpret_cast(pStream)); + SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::Flags); + break; + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_VIEW_INSTANCING: + pCallbacks->ViewInstancingCb(*reinterpret_cast(pStream)); + SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM1::ViewInstancingDesc); + break; + default: + pCallbacks->ErrorUnknownSubobject(SubobjectType); + return E_INVALIDARG; + } + } + + return S_OK; +} + +//------------------------------------------------------------------------------------------------ +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 609) +inline bool operator==(const D3D12_RENDER_PASS_BEGINNING_ACCESS_PRESERVE_LOCAL_PARAMETERS& a, const D3D12_RENDER_PASS_ENDING_ACCESS_PRESERVE_LOCAL_PARAMETERS& b) noexcept +{ + return ((a.AdditionalWidth == b.AdditionalWidth) && (a.AdditionalHeight == b.AdditionalHeight)); +} + +inline bool operator==(const D3D12_RENDER_PASS_BEGINNING_ACCESS_PRESERVE_LOCAL_PARAMETERS& a, const D3D12_RENDER_PASS_BEGINNING_ACCESS_PRESERVE_LOCAL_PARAMETERS& b) noexcept +{ + return ((a.AdditionalWidth == b.AdditionalWidth) && (a.AdditionalHeight == b.AdditionalHeight)); +} + +inline bool operator==(const D3D12_RENDER_PASS_ENDING_ACCESS_PRESERVE_LOCAL_PARAMETERS& a, const D3D12_RENDER_PASS_ENDING_ACCESS_PRESERVE_LOCAL_PARAMETERS& b) noexcept +{ + return ((a.AdditionalWidth == b.AdditionalWidth) && (a.AdditionalHeight == b.AdditionalHeight)); +} +#endif + +inline bool operator==( const D3D12_RENDER_PASS_BEGINNING_ACCESS_CLEAR_PARAMETERS &a, const D3D12_RENDER_PASS_BEGINNING_ACCESS_CLEAR_PARAMETERS &b) noexcept +{ + return a.ClearValue == b.ClearValue; +} + +inline bool operator==( const D3D12_RENDER_PASS_ENDING_ACCESS_RESOLVE_PARAMETERS &a, const D3D12_RENDER_PASS_ENDING_ACCESS_RESOLVE_PARAMETERS &b) noexcept +{ + if (a.pSrcResource != b.pSrcResource) return false; + if (a.pDstResource != b.pDstResource) return false; + if (a.SubresourceCount != b.SubresourceCount) return false; + if (a.Format != b.Format) return false; + if (a.ResolveMode != b.ResolveMode) return false; + if (a.PreserveResolveSource != b.PreserveResolveSource) return false; + return true; +} + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4062) +#endif + +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wswitch" +#endif + +inline bool operator==( const D3D12_RENDER_PASS_BEGINNING_ACCESS &a, const D3D12_RENDER_PASS_BEGINNING_ACCESS &b) noexcept +{ + if (a.Type != b.Type) return false; + switch (a.Type) + { + case D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR: + if (!(a.Clear == b.Clear)) return false; + break; +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 609) + case D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_PRESERVE_LOCAL_RENDER: + case D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_PRESERVE_LOCAL_SRV: + case D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_PRESERVE_LOCAL_UAV: + if (!(a.PreserveLocal == b.PreserveLocal)) return false; + break; +#endif + } + return true; +} + +inline bool operator==(const D3D12_RENDER_PASS_ENDING_ACCESS& a, const D3D12_RENDER_PASS_ENDING_ACCESS& b) noexcept +{ + if (a.Type != b.Type) return false; + switch (a.Type) + { + case D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_RESOLVE: + if (!(a.Resolve == b.Resolve)) return false; + break; +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 609) + case D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE_LOCAL_RENDER: + case D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE_LOCAL_SRV: + case D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE_LOCAL_UAV: + if (!(a.PreserveLocal == b.PreserveLocal)) return false; + break; +#endif + } + + return true; +} + +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic pop +#endif + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +inline bool operator==( const D3D12_RENDER_PASS_RENDER_TARGET_DESC &a, const D3D12_RENDER_PASS_RENDER_TARGET_DESC &b) noexcept +{ + if (a.cpuDescriptor.ptr != b.cpuDescriptor.ptr) return false; + if (!(a.BeginningAccess == b.BeginningAccess)) return false; + if (!(a.EndingAccess == b.EndingAccess)) return false; + return true; +} +inline bool operator==( const D3D12_RENDER_PASS_DEPTH_STENCIL_DESC &a, const D3D12_RENDER_PASS_DEPTH_STENCIL_DESC &b) noexcept +{ + if (a.cpuDescriptor.ptr != b.cpuDescriptor.ptr) return false; + if (!(a.DepthBeginningAccess == b.DepthBeginningAccess)) return false; + if (!(a.StencilBeginningAccess == b.StencilBeginningAccess)) return false; + if (!(a.DepthEndingAccess == b.DepthEndingAccess)) return false; + if (!(a.StencilEndingAccess == b.StencilEndingAccess)) return false; + return true; +} + + +#ifndef D3DX12_NO_STATE_OBJECT_HELPERS + +//================================================================================================ +// D3DX12 State Object Creation Helpers +// +// Helper classes for creating new style state objects out of an arbitrary set of subobjects. +// Uses STL +// +// Start by instantiating CD3DX12_STATE_OBJECT_DESC (see its public methods). +// One of its methods is CreateSubobject(), which has a comment showing a couple of options for +// defining subobjects using the helper classes for each subobject (CD3DX12_DXIL_LIBRARY_SUBOBJECT +// etc.). The subobject helpers each have methods specific to the subobject for configuring its +// contents. +// +//================================================================================================ +#include +#include +#include +#include +#ifndef D3DX12_USE_ATL +#include +#define D3DX12_COM_PTR Microsoft::WRL::ComPtr +#define D3DX12_COM_PTR_GET(x) x.Get() +#define D3DX12_COM_PTR_ADDRESSOF(x) x.GetAddressOf() +#else +#include +#define D3DX12_COM_PTR ATL::CComPtr +#define D3DX12_COM_PTR_GET(x) x.p +#define D3DX12_COM_PTR_ADDRESSOF(x) &x.p +#endif + +//------------------------------------------------------------------------------------------------ +class CD3DX12_STATE_OBJECT_DESC +{ +public: + CD3DX12_STATE_OBJECT_DESC() noexcept + { + Init(D3D12_STATE_OBJECT_TYPE_COLLECTION); + } + CD3DX12_STATE_OBJECT_DESC(D3D12_STATE_OBJECT_TYPE Type) noexcept + { + Init(Type); + } + void SetStateObjectType(D3D12_STATE_OBJECT_TYPE Type) noexcept { m_Desc.Type = Type; } + operator const D3D12_STATE_OBJECT_DESC&() + { + // Do final preparation work + m_RepointedAssociations.clear(); + m_SubobjectArray.clear(); + m_SubobjectArray.reserve(m_Desc.NumSubobjects); + // Flatten subobjects into an array (each flattened subobject still has a + // member that's a pointer to its desc that's not flattened) + for (auto Iter = m_SubobjectList.begin(); + Iter != m_SubobjectList.end(); Iter++) + { + m_SubobjectArray.push_back(*Iter); + // Store new location in array so we can redirect pointers contained in subobjects + Iter->pSubobjectArrayLocation = &m_SubobjectArray.back(); + } + // For subobjects with pointer fields, create a new copy of those subobject definitions + // with fixed pointers + for (UINT i = 0; i < m_Desc.NumSubobjects; i++) + { + if (m_SubobjectArray[i].Type == D3D12_STATE_SUBOBJECT_TYPE_SUBOBJECT_TO_EXPORTS_ASSOCIATION) + { + auto pOriginalSubobjectAssociation = + static_cast(m_SubobjectArray[i].pDesc); + D3D12_SUBOBJECT_TO_EXPORTS_ASSOCIATION Repointed = *pOriginalSubobjectAssociation; + auto pWrapper = + static_cast(pOriginalSubobjectAssociation->pSubobjectToAssociate); + Repointed.pSubobjectToAssociate = pWrapper->pSubobjectArrayLocation; + m_RepointedAssociations.push_back(Repointed); + m_SubobjectArray[i].pDesc = &m_RepointedAssociations.back(); + } + } + // Below: using ugly way to get pointer in case .data() is not defined + m_Desc.pSubobjects = m_Desc.NumSubobjects ? &m_SubobjectArray[0] : nullptr; + return m_Desc; + } + operator const D3D12_STATE_OBJECT_DESC*() + { + // Cast calls the above final preparation work + return &static_cast(*this); + } + + // CreateSubobject creates a sububject helper (e.g. CD3DX12_HIT_GROUP_SUBOBJECT) + // whose lifetime is owned by this class. + // e.g. + // + // CD3DX12_STATE_OBJECT_DESC Collection1(D3D12_STATE_OBJECT_TYPE_COLLECTION); + // auto Lib0 = Collection1.CreateSubobject(); + // Lib0->SetDXILLibrary(&pMyAppDxilLibs[0]); + // Lib0->DefineExport(L"rayGenShader0"); // in practice these export listings might be + // // data/engine driven + // etc. + // + // Alternatively, users can instantiate sububject helpers explicitly, such as via local + // variables instead, passing the state object desc that should point to it into the helper + // constructor (or call mySubobjectHelper.AddToStateObject(Collection1)). + // In this alternative scenario, the user must keep the subobject alive as long as the state + // object it is associated with is alive, else its pointer references will be stale. + // e.g. + // + // CD3DX12_STATE_OBJECT_DESC RaytracingState2(D3D12_STATE_OBJECT_TYPE_RAYTRACING_PIPELINE); + // CD3DX12_DXIL_LIBRARY_SUBOBJECT LibA(RaytracingState2); + // LibA.SetDXILLibrary(&pMyAppDxilLibs[4]); // not manually specifying exports + // // - meaning all exports in the libraries + // // are exported + // etc. + + template + T* CreateSubobject() + { + T* pSubobject = new T(*this); + m_OwnedSubobjectHelpers.emplace_back(pSubobject); + return pSubobject; + } + +private: + D3D12_STATE_SUBOBJECT* TrackSubobject(D3D12_STATE_SUBOBJECT_TYPE Type, void* pDesc) + { + SUBOBJECT_WRAPPER Subobject; + Subobject.pSubobjectArrayLocation = nullptr; + Subobject.Type = Type; + Subobject.pDesc = pDesc; + m_SubobjectList.push_back(Subobject); + m_Desc.NumSubobjects++; + return &m_SubobjectList.back(); + } + void Init(D3D12_STATE_OBJECT_TYPE Type) noexcept + { + SetStateObjectType(Type); + m_Desc.pSubobjects = nullptr; + m_Desc.NumSubobjects = 0; + m_SubobjectList.clear(); + m_SubobjectArray.clear(); + m_RepointedAssociations.clear(); + } + typedef struct SUBOBJECT_WRAPPER : public D3D12_STATE_SUBOBJECT + { + D3D12_STATE_SUBOBJECT* pSubobjectArrayLocation; // new location when flattened into array + // for repointing pointers in subobjects + } SUBOBJECT_WRAPPER; + D3D12_STATE_OBJECT_DESC m_Desc; + std::list m_SubobjectList; // Pointers to list nodes handed out so + // these can be edited live + std::vector m_SubobjectArray; // Built at the end, copying list contents + + std::list + m_RepointedAssociations; // subobject type that contains pointers to other subobjects, + // repointed to flattened array + + class StringContainer + { + public: + LPCWSTR LocalCopy(LPCWSTR string, bool bSingleString = false) + { + if (string) + { + if (bSingleString) + { + m_Strings.clear(); + m_Strings.push_back(string); + } + else + { + m_Strings.push_back(string); + } + return m_Strings.back().c_str(); + } + else + { + return nullptr; + } + } + void clear() noexcept { m_Strings.clear(); } + private: + std::list m_Strings; + }; + + class SUBOBJECT_HELPER_BASE + { + public: + SUBOBJECT_HELPER_BASE() noexcept { Init(); } + virtual ~SUBOBJECT_HELPER_BASE() = default; + virtual D3D12_STATE_SUBOBJECT_TYPE Type() const noexcept = 0; + void AddToStateObject(CD3DX12_STATE_OBJECT_DESC& ContainingStateObject) + { + m_pSubobject = ContainingStateObject.TrackSubobject(Type(), Data()); + } + protected: + virtual void* Data() noexcept = 0; + void Init() noexcept { m_pSubobject = nullptr; } + D3D12_STATE_SUBOBJECT* m_pSubobject; + }; + +#if(__cplusplus >= 201103L) + std::list> m_OwnedSubobjectHelpers; +#else + class OWNED_HELPER + { + public: + OWNED_HELPER(const SUBOBJECT_HELPER_BASE* pHelper) noexcept { m_pHelper = pHelper; } + ~OWNED_HELPER() { delete m_pHelper; } + const SUBOBJECT_HELPER_BASE* m_pHelper; + }; + + std::list m_OwnedSubobjectHelpers; +#endif + + friend class CD3DX12_DXIL_LIBRARY_SUBOBJECT; + friend class CD3DX12_EXISTING_COLLECTION_SUBOBJECT; + friend class CD3DX12_SUBOBJECT_TO_EXPORTS_ASSOCIATION_SUBOBJECT; + friend class CD3DX12_DXIL_SUBOBJECT_TO_EXPORTS_ASSOCIATION; + friend class CD3DX12_HIT_GROUP_SUBOBJECT; + friend class CD3DX12_RAYTRACING_SHADER_CONFIG_SUBOBJECT; + friend class CD3DX12_RAYTRACING_PIPELINE_CONFIG_SUBOBJECT; + friend class CD3DX12_RAYTRACING_PIPELINE_CONFIG1_SUBOBJECT; + friend class CD3DX12_GLOBAL_ROOT_SIGNATURE_SUBOBJECT; + friend class CD3DX12_LOCAL_ROOT_SIGNATURE_SUBOBJECT; + friend class CD3DX12_STATE_OBJECT_CONFIG_SUBOBJECT; + friend class CD3DX12_NODE_MASK_SUBOBJECT; +}; + +//------------------------------------------------------------------------------------------------ +class CD3DX12_DXIL_LIBRARY_SUBOBJECT + : public CD3DX12_STATE_OBJECT_DESC::SUBOBJECT_HELPER_BASE +{ +public: + CD3DX12_DXIL_LIBRARY_SUBOBJECT() noexcept + { + Init(); + } + CD3DX12_DXIL_LIBRARY_SUBOBJECT(CD3DX12_STATE_OBJECT_DESC& ContainingStateObject) + { + Init(); + AddToStateObject(ContainingStateObject); + } + void SetDXILLibrary(const D3D12_SHADER_BYTECODE* pCode) noexcept + { + static const D3D12_SHADER_BYTECODE Default = {}; + m_Desc.DXILLibrary = pCode ? *pCode : Default; + } + void DefineExport( + LPCWSTR Name, + LPCWSTR ExportToRename = nullptr, + D3D12_EXPORT_FLAGS Flags = D3D12_EXPORT_FLAG_NONE) + { + D3D12_EXPORT_DESC Export; + Export.Name = m_Strings.LocalCopy(Name); + Export.ExportToRename = m_Strings.LocalCopy(ExportToRename); + Export.Flags = Flags; + m_Exports.push_back(Export); + m_Desc.pExports = &m_Exports[0]; // using ugly way to get pointer in case .data() is not defined + m_Desc.NumExports = static_cast(m_Exports.size()); + } + template + void DefineExports(LPCWSTR(&Exports)[N]) + { + for (UINT i = 0; i < N; i++) + { + DefineExport(Exports[i]); + } + } + void DefineExports(const LPCWSTR* Exports, UINT N) + { + for (UINT i = 0; i < N; i++) + { + DefineExport(Exports[i]); + } + } + D3D12_STATE_SUBOBJECT_TYPE Type() const noexcept override + { + return D3D12_STATE_SUBOBJECT_TYPE_DXIL_LIBRARY; + } + operator const D3D12_STATE_SUBOBJECT&() const noexcept { return *m_pSubobject; } + operator const D3D12_DXIL_LIBRARY_DESC&() const noexcept { return m_Desc; } +private: + void Init() noexcept + { + SUBOBJECT_HELPER_BASE::Init(); + m_Desc = {}; + m_Strings.clear(); + m_Exports.clear(); + } + void* Data() noexcept override { return &m_Desc; } + D3D12_DXIL_LIBRARY_DESC m_Desc; + CD3DX12_STATE_OBJECT_DESC::StringContainer m_Strings; + std::vector m_Exports; +}; + +//------------------------------------------------------------------------------------------------ +class CD3DX12_EXISTING_COLLECTION_SUBOBJECT + : public CD3DX12_STATE_OBJECT_DESC::SUBOBJECT_HELPER_BASE +{ +public: + CD3DX12_EXISTING_COLLECTION_SUBOBJECT() noexcept + { + Init(); + } + CD3DX12_EXISTING_COLLECTION_SUBOBJECT(CD3DX12_STATE_OBJECT_DESC& ContainingStateObject) + { + Init(); + AddToStateObject(ContainingStateObject); + } + void SetExistingCollection(ID3D12StateObject*pExistingCollection) noexcept + { + m_Desc.pExistingCollection = pExistingCollection; + m_CollectionRef = pExistingCollection; + } + void DefineExport( + LPCWSTR Name, + LPCWSTR ExportToRename = nullptr, + D3D12_EXPORT_FLAGS Flags = D3D12_EXPORT_FLAG_NONE) + { + D3D12_EXPORT_DESC Export; + Export.Name = m_Strings.LocalCopy(Name); + Export.ExportToRename = m_Strings.LocalCopy(ExportToRename); + Export.Flags = Flags; + m_Exports.push_back(Export); + m_Desc.pExports = &m_Exports[0]; // using ugly way to get pointer in case .data() is not defined + m_Desc.NumExports = static_cast(m_Exports.size()); + } + template + void DefineExports(LPCWSTR(&Exports)[N]) + { + for (UINT i = 0; i < N; i++) + { + DefineExport(Exports[i]); + } + } + void DefineExports(const LPCWSTR* Exports, UINT N) + { + for (UINT i = 0; i < N; i++) + { + DefineExport(Exports[i]); + } + } + D3D12_STATE_SUBOBJECT_TYPE Type() const noexcept override + { + return D3D12_STATE_SUBOBJECT_TYPE_EXISTING_COLLECTION; + } + operator const D3D12_STATE_SUBOBJECT&() const noexcept { return *m_pSubobject; } + operator const D3D12_EXISTING_COLLECTION_DESC&() const noexcept { return m_Desc; } +private: + void Init() noexcept + { + SUBOBJECT_HELPER_BASE::Init(); + m_Desc = {}; + m_CollectionRef = nullptr; + m_Strings.clear(); + m_Exports.clear(); + } + void* Data() noexcept override { return &m_Desc; } + D3D12_EXISTING_COLLECTION_DESC m_Desc; + D3DX12_COM_PTR m_CollectionRef; + CD3DX12_STATE_OBJECT_DESC::StringContainer m_Strings; + std::vector m_Exports; +}; + +//------------------------------------------------------------------------------------------------ +class CD3DX12_SUBOBJECT_TO_EXPORTS_ASSOCIATION_SUBOBJECT + : public CD3DX12_STATE_OBJECT_DESC::SUBOBJECT_HELPER_BASE +{ +public: + CD3DX12_SUBOBJECT_TO_EXPORTS_ASSOCIATION_SUBOBJECT() noexcept + { + Init(); + } + CD3DX12_SUBOBJECT_TO_EXPORTS_ASSOCIATION_SUBOBJECT(CD3DX12_STATE_OBJECT_DESC& ContainingStateObject) + { + Init(); + AddToStateObject(ContainingStateObject); + } + void SetSubobjectToAssociate(const D3D12_STATE_SUBOBJECT& SubobjectToAssociate) noexcept + { + m_Desc.pSubobjectToAssociate = &SubobjectToAssociate; + } + void AddExport(LPCWSTR Export) + { + m_Desc.NumExports++; + m_Exports.push_back(m_Strings.LocalCopy(Export)); + m_Desc.pExports = &m_Exports[0]; // using ugly way to get pointer in case .data() is not defined + } + template + void AddExports(LPCWSTR (&Exports)[N]) + { + for (UINT i = 0; i < N; i++) + { + AddExport(Exports[i]); + } + } + void AddExports(const LPCWSTR* Exports, UINT N) + { + for (UINT i = 0; i < N; i++) + { + AddExport(Exports[i]); + } + } + D3D12_STATE_SUBOBJECT_TYPE Type() const noexcept override + { + return D3D12_STATE_SUBOBJECT_TYPE_SUBOBJECT_TO_EXPORTS_ASSOCIATION; + } + operator const D3D12_STATE_SUBOBJECT&() const noexcept { return *m_pSubobject; } + operator const D3D12_SUBOBJECT_TO_EXPORTS_ASSOCIATION&() const noexcept { return m_Desc; } +private: + void Init() noexcept + { + SUBOBJECT_HELPER_BASE::Init(); + m_Desc = {}; + m_Strings.clear(); + m_Exports.clear(); + } + void* Data() noexcept override { return &m_Desc; } + D3D12_SUBOBJECT_TO_EXPORTS_ASSOCIATION m_Desc; + CD3DX12_STATE_OBJECT_DESC::StringContainer m_Strings; + std::vector m_Exports; +}; + +//------------------------------------------------------------------------------------------------ +class CD3DX12_DXIL_SUBOBJECT_TO_EXPORTS_ASSOCIATION + : public CD3DX12_STATE_OBJECT_DESC::SUBOBJECT_HELPER_BASE +{ +public: + CD3DX12_DXIL_SUBOBJECT_TO_EXPORTS_ASSOCIATION() noexcept + { + Init(); + } + CD3DX12_DXIL_SUBOBJECT_TO_EXPORTS_ASSOCIATION(CD3DX12_STATE_OBJECT_DESC& ContainingStateObject) + { + Init(); + AddToStateObject(ContainingStateObject); + } + void SetSubobjectNameToAssociate(LPCWSTR SubobjectToAssociate) + { + m_Desc.SubobjectToAssociate = m_SubobjectName.LocalCopy(SubobjectToAssociate, true); + } + void AddExport(LPCWSTR Export) + { + m_Desc.NumExports++; + m_Exports.push_back(m_Strings.LocalCopy(Export)); + m_Desc.pExports = &m_Exports[0]; // using ugly way to get pointer in case .data() is not defined + } + template + void AddExports(LPCWSTR (&Exports)[N]) + { + for (UINT i = 0; i < N; i++) + { + AddExport(Exports[i]); + } + } + void AddExports(const LPCWSTR* Exports, UINT N) + { + for (UINT i = 0; i < N; i++) + { + AddExport(Exports[i]); + } + } + D3D12_STATE_SUBOBJECT_TYPE Type() const noexcept override + { + return D3D12_STATE_SUBOBJECT_TYPE_DXIL_SUBOBJECT_TO_EXPORTS_ASSOCIATION; + } + operator const D3D12_STATE_SUBOBJECT&() const noexcept { return *m_pSubobject; } + operator const D3D12_DXIL_SUBOBJECT_TO_EXPORTS_ASSOCIATION&() const noexcept { return m_Desc; } +private: + void Init() noexcept + { + SUBOBJECT_HELPER_BASE::Init(); + m_Desc = {}; + m_Strings.clear(); + m_SubobjectName.clear(); + m_Exports.clear(); + } + void* Data() noexcept override { return &m_Desc; } + D3D12_DXIL_SUBOBJECT_TO_EXPORTS_ASSOCIATION m_Desc; + CD3DX12_STATE_OBJECT_DESC::StringContainer m_Strings; + CD3DX12_STATE_OBJECT_DESC::StringContainer m_SubobjectName; + std::vector m_Exports; +}; + +//------------------------------------------------------------------------------------------------ +class CD3DX12_HIT_GROUP_SUBOBJECT + : public CD3DX12_STATE_OBJECT_DESC::SUBOBJECT_HELPER_BASE +{ +public: + CD3DX12_HIT_GROUP_SUBOBJECT() noexcept + { + Init(); + } + CD3DX12_HIT_GROUP_SUBOBJECT(CD3DX12_STATE_OBJECT_DESC& ContainingStateObject) + { + Init(); + AddToStateObject(ContainingStateObject); + } + void SetHitGroupExport(LPCWSTR exportName) + { + m_Desc.HitGroupExport = m_Strings[0].LocalCopy(exportName, true); + } + void SetHitGroupType(D3D12_HIT_GROUP_TYPE Type) noexcept { m_Desc.Type = Type; } + void SetAnyHitShaderImport(LPCWSTR importName) + { + m_Desc.AnyHitShaderImport = m_Strings[1].LocalCopy(importName, true); + } + void SetClosestHitShaderImport(LPCWSTR importName) + { + m_Desc.ClosestHitShaderImport = m_Strings[2].LocalCopy(importName, true); + } + void SetIntersectionShaderImport(LPCWSTR importName) + { + m_Desc.IntersectionShaderImport = m_Strings[3].LocalCopy(importName, true); + } + D3D12_STATE_SUBOBJECT_TYPE Type() const noexcept override + { + return D3D12_STATE_SUBOBJECT_TYPE_HIT_GROUP; + } + operator const D3D12_STATE_SUBOBJECT&() const noexcept { return *m_pSubobject; } + operator const D3D12_HIT_GROUP_DESC&() const noexcept { return m_Desc; } +private: + void Init() noexcept + { + SUBOBJECT_HELPER_BASE::Init(); + m_Desc = {}; + for (UINT i = 0; i < m_NumStrings; i++) + { + m_Strings[i].clear(); + } + } + void* Data() noexcept override { return &m_Desc; } + D3D12_HIT_GROUP_DESC m_Desc; + static constexpr UINT m_NumStrings = 4; + CD3DX12_STATE_OBJECT_DESC::StringContainer + m_Strings[m_NumStrings]; // one string for every entrypoint name +}; + +//------------------------------------------------------------------------------------------------ +class CD3DX12_RAYTRACING_SHADER_CONFIG_SUBOBJECT + : public CD3DX12_STATE_OBJECT_DESC::SUBOBJECT_HELPER_BASE +{ +public: + CD3DX12_RAYTRACING_SHADER_CONFIG_SUBOBJECT() noexcept + { + Init(); + } + CD3DX12_RAYTRACING_SHADER_CONFIG_SUBOBJECT(CD3DX12_STATE_OBJECT_DESC& ContainingStateObject) + { + Init(); + AddToStateObject(ContainingStateObject); + } + void Config(UINT MaxPayloadSizeInBytes, UINT MaxAttributeSizeInBytes) noexcept + { + m_Desc.MaxPayloadSizeInBytes = MaxPayloadSizeInBytes; + m_Desc.MaxAttributeSizeInBytes = MaxAttributeSizeInBytes; + } + D3D12_STATE_SUBOBJECT_TYPE Type() const noexcept override + { + return D3D12_STATE_SUBOBJECT_TYPE_RAYTRACING_SHADER_CONFIG; + } + operator const D3D12_STATE_SUBOBJECT&() const noexcept { return *m_pSubobject; } + operator const D3D12_RAYTRACING_SHADER_CONFIG&() const noexcept { return m_Desc; } +private: + void Init() noexcept + { + SUBOBJECT_HELPER_BASE::Init(); + m_Desc = {}; + } + void* Data() noexcept override { return &m_Desc; } + D3D12_RAYTRACING_SHADER_CONFIG m_Desc; +}; + +//------------------------------------------------------------------------------------------------ +class CD3DX12_RAYTRACING_PIPELINE_CONFIG_SUBOBJECT + : public CD3DX12_STATE_OBJECT_DESC::SUBOBJECT_HELPER_BASE +{ +public: + CD3DX12_RAYTRACING_PIPELINE_CONFIG_SUBOBJECT() noexcept + { + Init(); + } + CD3DX12_RAYTRACING_PIPELINE_CONFIG_SUBOBJECT(CD3DX12_STATE_OBJECT_DESC& ContainingStateObject) + { + Init(); + AddToStateObject(ContainingStateObject); + } + void Config(UINT MaxTraceRecursionDepth) noexcept + { + m_Desc.MaxTraceRecursionDepth = MaxTraceRecursionDepth; + } + D3D12_STATE_SUBOBJECT_TYPE Type() const noexcept override + { + return D3D12_STATE_SUBOBJECT_TYPE_RAYTRACING_PIPELINE_CONFIG; + } + operator const D3D12_STATE_SUBOBJECT&() const noexcept { return *m_pSubobject; } + operator const D3D12_RAYTRACING_PIPELINE_CONFIG&() const noexcept { return m_Desc; } +private: + void Init() noexcept + { + SUBOBJECT_HELPER_BASE::Init(); + m_Desc = {}; + } + void* Data() noexcept override { return &m_Desc; } + D3D12_RAYTRACING_PIPELINE_CONFIG m_Desc; +}; + +//------------------------------------------------------------------------------------------------ +class CD3DX12_RAYTRACING_PIPELINE_CONFIG1_SUBOBJECT + : public CD3DX12_STATE_OBJECT_DESC::SUBOBJECT_HELPER_BASE +{ +public: + CD3DX12_RAYTRACING_PIPELINE_CONFIG1_SUBOBJECT() noexcept + { + Init(); + } + CD3DX12_RAYTRACING_PIPELINE_CONFIG1_SUBOBJECT(CD3DX12_STATE_OBJECT_DESC& ContainingStateObject) + { + Init(); + AddToStateObject(ContainingStateObject); + } + void Config(UINT MaxTraceRecursionDepth, D3D12_RAYTRACING_PIPELINE_FLAGS Flags) noexcept + { + m_Desc.MaxTraceRecursionDepth = MaxTraceRecursionDepth; + m_Desc.Flags = Flags; + } + D3D12_STATE_SUBOBJECT_TYPE Type() const noexcept override + { + return D3D12_STATE_SUBOBJECT_TYPE_RAYTRACING_PIPELINE_CONFIG1; + } + operator const D3D12_STATE_SUBOBJECT&() const noexcept { return *m_pSubobject; } + operator const D3D12_RAYTRACING_PIPELINE_CONFIG1&() const noexcept { return m_Desc; } +private: + void Init() noexcept + { + SUBOBJECT_HELPER_BASE::Init(); + m_Desc = {}; + } + void* Data() noexcept override { return &m_Desc; } + D3D12_RAYTRACING_PIPELINE_CONFIG1 m_Desc; +}; + +//------------------------------------------------------------------------------------------------ +class CD3DX12_GLOBAL_ROOT_SIGNATURE_SUBOBJECT + : public CD3DX12_STATE_OBJECT_DESC::SUBOBJECT_HELPER_BASE +{ +public: + CD3DX12_GLOBAL_ROOT_SIGNATURE_SUBOBJECT() noexcept + { + Init(); + } + CD3DX12_GLOBAL_ROOT_SIGNATURE_SUBOBJECT(CD3DX12_STATE_OBJECT_DESC& ContainingStateObject) + { + Init(); + AddToStateObject(ContainingStateObject); + } + void SetRootSignature(ID3D12RootSignature* pRootSig) noexcept + { + m_pRootSig = pRootSig; + } + D3D12_STATE_SUBOBJECT_TYPE Type() const noexcept override + { + return D3D12_STATE_SUBOBJECT_TYPE_GLOBAL_ROOT_SIGNATURE; + } + operator const D3D12_STATE_SUBOBJECT&() const noexcept { return *m_pSubobject; } + operator ID3D12RootSignature*() const noexcept { return D3DX12_COM_PTR_GET(m_pRootSig); } +private: + void Init() noexcept + { + SUBOBJECT_HELPER_BASE::Init(); + m_pRootSig = nullptr; + } + void* Data() noexcept override { return D3DX12_COM_PTR_ADDRESSOF(m_pRootSig); } + D3DX12_COM_PTR m_pRootSig; +}; + +//------------------------------------------------------------------------------------------------ +class CD3DX12_LOCAL_ROOT_SIGNATURE_SUBOBJECT + : public CD3DX12_STATE_OBJECT_DESC::SUBOBJECT_HELPER_BASE +{ +public: + CD3DX12_LOCAL_ROOT_SIGNATURE_SUBOBJECT() noexcept + { + Init(); + } + CD3DX12_LOCAL_ROOT_SIGNATURE_SUBOBJECT(CD3DX12_STATE_OBJECT_DESC& ContainingStateObject) + { + Init(); + AddToStateObject(ContainingStateObject); + } + void SetRootSignature(ID3D12RootSignature* pRootSig) noexcept + { + m_pRootSig = pRootSig; + } + D3D12_STATE_SUBOBJECT_TYPE Type() const noexcept override + { + return D3D12_STATE_SUBOBJECT_TYPE_LOCAL_ROOT_SIGNATURE; + } + operator const D3D12_STATE_SUBOBJECT&() const noexcept { return *m_pSubobject; } + operator ID3D12RootSignature*() const noexcept { return D3DX12_COM_PTR_GET(m_pRootSig); } +private: + void Init() noexcept + { + SUBOBJECT_HELPER_BASE::Init(); + m_pRootSig = nullptr; + } + void* Data() noexcept override { return D3DX12_COM_PTR_ADDRESSOF(m_pRootSig); } + D3DX12_COM_PTR m_pRootSig; +}; + +//------------------------------------------------------------------------------------------------ +class CD3DX12_STATE_OBJECT_CONFIG_SUBOBJECT + : public CD3DX12_STATE_OBJECT_DESC::SUBOBJECT_HELPER_BASE +{ +public: + CD3DX12_STATE_OBJECT_CONFIG_SUBOBJECT() noexcept + { + Init(); + } + CD3DX12_STATE_OBJECT_CONFIG_SUBOBJECT(CD3DX12_STATE_OBJECT_DESC& ContainingStateObject) + { + Init(); + AddToStateObject(ContainingStateObject); + } + void SetFlags(D3D12_STATE_OBJECT_FLAGS Flags) noexcept + { + m_Desc.Flags = Flags; + } + D3D12_STATE_SUBOBJECT_TYPE Type() const noexcept override + { + return D3D12_STATE_SUBOBJECT_TYPE_STATE_OBJECT_CONFIG; + } + operator const D3D12_STATE_SUBOBJECT&() const noexcept { return *m_pSubobject; } + operator const D3D12_STATE_OBJECT_CONFIG&() const noexcept { return m_Desc; } +private: + void Init() noexcept + { + SUBOBJECT_HELPER_BASE::Init(); + m_Desc = {}; + } + void* Data() noexcept override { return &m_Desc; } + D3D12_STATE_OBJECT_CONFIG m_Desc; +}; + +//------------------------------------------------------------------------------------------------ +class CD3DX12_NODE_MASK_SUBOBJECT + : public CD3DX12_STATE_OBJECT_DESC::SUBOBJECT_HELPER_BASE +{ +public: + CD3DX12_NODE_MASK_SUBOBJECT() noexcept + { + Init(); + } + CD3DX12_NODE_MASK_SUBOBJECT(CD3DX12_STATE_OBJECT_DESC& ContainingStateObject) + { + Init(); + AddToStateObject(ContainingStateObject); + } + void SetNodeMask(UINT NodeMask) noexcept + { + m_Desc.NodeMask = NodeMask; + } + D3D12_STATE_SUBOBJECT_TYPE Type() const noexcept override + { + return D3D12_STATE_SUBOBJECT_TYPE_NODE_MASK; + } + operator const D3D12_STATE_SUBOBJECT&() const noexcept { return *m_pSubobject; } + operator const D3D12_NODE_MASK&() const noexcept { return m_Desc; } +private: + void Init() noexcept + { + SUBOBJECT_HELPER_BASE::Init(); + m_Desc = {}; + } + void* Data() noexcept override { return &m_Desc; } + D3D12_NODE_MASK m_Desc; +}; + +#endif // !D3DX12_NO_STATE_OBJECT_HELPERS + + +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 608) + +//================================================================================================ +// D3DX12 Enhanced Barrier Helpers +//================================================================================================ + +class CD3DX12_BARRIER_SUBRESOURCE_RANGE : public D3D12_BARRIER_SUBRESOURCE_RANGE +{ +public: + CD3DX12_BARRIER_SUBRESOURCE_RANGE() = default; + CD3DX12_BARRIER_SUBRESOURCE_RANGE(const D3D12_BARRIER_SUBRESOURCE_RANGE &o) noexcept : + D3D12_BARRIER_SUBRESOURCE_RANGE(o) + {} + explicit CD3DX12_BARRIER_SUBRESOURCE_RANGE(UINT Subresource) noexcept : + D3D12_BARRIER_SUBRESOURCE_RANGE{ Subresource, 0, 0, 0, 0, 0 } + {} + CD3DX12_BARRIER_SUBRESOURCE_RANGE( + UINT firstMipLevel, + UINT numMips, + UINT firstArraySlice, + UINT numArraySlices, + UINT firstPlane = 0, + UINT numPlanes = 1) noexcept : + D3D12_BARRIER_SUBRESOURCE_RANGE + { + firstMipLevel, + numMips, + firstArraySlice, + numArraySlices, + firstPlane, + numPlanes + } + {} +}; + +class CD3DX12_GLOBAL_BARRIER : public D3D12_GLOBAL_BARRIER +{ +public: + CD3DX12_GLOBAL_BARRIER() = default; + CD3DX12_GLOBAL_BARRIER(const D3D12_GLOBAL_BARRIER &o) noexcept : D3D12_GLOBAL_BARRIER(o){} + CD3DX12_GLOBAL_BARRIER( + D3D12_BARRIER_SYNC syncBefore, + D3D12_BARRIER_SYNC syncAfter, + D3D12_BARRIER_ACCESS accessBefore, + D3D12_BARRIER_ACCESS accessAfter) noexcept : D3D12_GLOBAL_BARRIER { + syncBefore, + syncAfter, + accessBefore, + accessAfter + } + {} +}; + +class CD3DX12_BUFFER_BARRIER : public D3D12_BUFFER_BARRIER +{ +public: + CD3DX12_BUFFER_BARRIER() = default; + CD3DX12_BUFFER_BARRIER(const D3D12_BUFFER_BARRIER &o) noexcept : D3D12_BUFFER_BARRIER(o){} + CD3DX12_BUFFER_BARRIER( + D3D12_BARRIER_SYNC syncBefore, + D3D12_BARRIER_SYNC syncAfter, + D3D12_BARRIER_ACCESS accessBefore, + D3D12_BARRIER_ACCESS accessAfter, + ID3D12Resource *pRes) noexcept : D3D12_BUFFER_BARRIER { + syncBefore, + syncAfter, + accessBefore, + accessAfter, + pRes, + 0, ULLONG_MAX + } + {} +}; + +class CD3DX12_TEXTURE_BARRIER : public D3D12_TEXTURE_BARRIER +{ +public: + CD3DX12_TEXTURE_BARRIER() = default; + CD3DX12_TEXTURE_BARRIER(const D3D12_TEXTURE_BARRIER &o) noexcept : D3D12_TEXTURE_BARRIER(o){} + CD3DX12_TEXTURE_BARRIER( + D3D12_BARRIER_SYNC syncBefore, + D3D12_BARRIER_SYNC syncAfter, + D3D12_BARRIER_ACCESS accessBefore, + D3D12_BARRIER_ACCESS accessAfter, + D3D12_BARRIER_LAYOUT layoutBefore, + D3D12_BARRIER_LAYOUT layoutAfter, + ID3D12Resource *pRes, + const D3D12_BARRIER_SUBRESOURCE_RANGE &subresources, + D3D12_TEXTURE_BARRIER_FLAGS flag = D3D12_TEXTURE_BARRIER_FLAG_NONE) noexcept : D3D12_TEXTURE_BARRIER { + syncBefore, + syncAfter, + accessBefore, + accessAfter, + layoutBefore, + layoutAfter, + pRes, + subresources, + flag + } + {} +}; + +class CD3DX12_BARRIER_GROUP : public D3D12_BARRIER_GROUP +{ +public: + CD3DX12_BARRIER_GROUP() = default; + CD3DX12_BARRIER_GROUP(const D3D12_BARRIER_GROUP &o) noexcept : D3D12_BARRIER_GROUP(o){} + CD3DX12_BARRIER_GROUP(UINT32 numBarriers, const D3D12_BUFFER_BARRIER *pBarriers) noexcept + { + Type = D3D12_BARRIER_TYPE_BUFFER; + NumBarriers = numBarriers; + pBufferBarriers = pBarriers; + } + CD3DX12_BARRIER_GROUP(UINT32 numBarriers, const D3D12_TEXTURE_BARRIER *pBarriers) noexcept + { + Type = D3D12_BARRIER_TYPE_TEXTURE; + NumBarriers = numBarriers; + pTextureBarriers = pBarriers; + } + CD3DX12_BARRIER_GROUP(UINT32 numBarriers, const D3D12_GLOBAL_BARRIER *pBarriers) noexcept + { + Type = D3D12_BARRIER_TYPE_GLOBAL; + NumBarriers = numBarriers; + pGlobalBarriers = pBarriers; + } +}; + +#endif // D3D12_SDK_VERSION >= 608 + + +#ifndef D3DX12_NO_CHECK_FEATURE_SUPPORT_CLASS + +//================================================================================================ +// D3DX12 Check Feature Support +//================================================================================================ + +#include + +class CD3DX12FeatureSupport +{ +public: // Function declaration + // Default constructor that creates an empty object + CD3DX12FeatureSupport() noexcept; + + // Initialize data from the given device + HRESULT Init(ID3D12Device* pDevice); + + // Retreives the status of the object. If an error occurred in the initialization process, the function returns the error code. + HRESULT GetStatus() const noexcept { return m_hStatus; } + + // Getter functions for each feature class + // D3D12_OPTIONS + BOOL DoublePrecisionFloatShaderOps() const noexcept; + BOOL OutputMergerLogicOp() const noexcept; + D3D12_SHADER_MIN_PRECISION_SUPPORT MinPrecisionSupport() const noexcept; + D3D12_TILED_RESOURCES_TIER TiledResourcesTier() const noexcept; + D3D12_RESOURCE_BINDING_TIER ResourceBindingTier() const noexcept; + BOOL PSSpecifiedStencilRefSupported() const noexcept; + BOOL TypedUAVLoadAdditionalFormats() const noexcept; + BOOL ROVsSupported() const noexcept; + D3D12_CONSERVATIVE_RASTERIZATION_TIER ConservativeRasterizationTier() const noexcept; + BOOL StandardSwizzle64KBSupported() const noexcept; + BOOL CrossAdapterRowMajorTextureSupported() const noexcept; + BOOL VPAndRTArrayIndexFromAnyShaderFeedingRasterizerSupportedWithoutGSEmulation() const noexcept; + D3D12_RESOURCE_HEAP_TIER ResourceHeapTier() const noexcept; + D3D12_CROSS_NODE_SHARING_TIER CrossNodeSharingTier() const noexcept; + UINT MaxGPUVirtualAddressBitsPerResource() const noexcept; + + // FEATURE_LEVELS + D3D_FEATURE_LEVEL MaxSupportedFeatureLevel() const noexcept; + + // FORMAT_SUPPORT + HRESULT FormatSupport(DXGI_FORMAT Format, D3D12_FORMAT_SUPPORT1& Support1, D3D12_FORMAT_SUPPORT2& Support2) const; + + // MUTLTISAMPLE_QUALITY_LEVELS + HRESULT MultisampleQualityLevels(DXGI_FORMAT Format, UINT SampleCount, D3D12_MULTISAMPLE_QUALITY_LEVEL_FLAGS Flags, UINT& NumQualityLevels) const; + + // FORMAT_INFO + HRESULT FormatInfo(DXGI_FORMAT Format, UINT8& PlaneCount) const; + + // GPU_VIRTUAL_ADDRESS_SUPPORT + UINT MaxGPUVirtualAddressBitsPerProcess() const noexcept; + + // SHADER_MODEL + D3D_SHADER_MODEL HighestShaderModel() const noexcept; + + // D3D12_OPTIONS1 + BOOL WaveOps() const noexcept; + UINT WaveLaneCountMin() const noexcept; + UINT WaveLaneCountMax() const noexcept; + UINT TotalLaneCount() const noexcept; + BOOL ExpandedComputeResourceStates() const noexcept; + BOOL Int64ShaderOps() const noexcept; + + // PROTECTED_RESOURCE_SESSION_SUPPORT + D3D12_PROTECTED_RESOURCE_SESSION_SUPPORT_FLAGS ProtectedResourceSessionSupport(UINT NodeIndex = 0) const; + + // ROOT_SIGNATURE + D3D_ROOT_SIGNATURE_VERSION HighestRootSignatureVersion() const noexcept; + + // ARCHITECTURE1 + BOOL TileBasedRenderer(UINT NodeIndex = 0) const; + BOOL UMA(UINT NodeIndex = 0) const; + BOOL CacheCoherentUMA(UINT NodeIndex = 0) const; + BOOL IsolatedMMU(UINT NodeIndex = 0) const; + + // D3D12_OPTIONS2 + BOOL DepthBoundsTestSupported() const noexcept; + D3D12_PROGRAMMABLE_SAMPLE_POSITIONS_TIER ProgrammableSamplePositionsTier() const noexcept; + + // SHADER_CACHE + D3D12_SHADER_CACHE_SUPPORT_FLAGS ShaderCacheSupportFlags() const noexcept; + + // COMMAND_QUEUE_PRIORITY + BOOL CommandQueuePrioritySupported(D3D12_COMMAND_LIST_TYPE CommandListType, UINT Priority); + + // D3D12_OPTIONS3 + BOOL CopyQueueTimestampQueriesSupported() const noexcept; + BOOL CastingFullyTypedFormatSupported() const noexcept; + D3D12_COMMAND_LIST_SUPPORT_FLAGS WriteBufferImmediateSupportFlags() const noexcept; + D3D12_VIEW_INSTANCING_TIER ViewInstancingTier() const noexcept; + BOOL BarycentricsSupported() const noexcept; + + // EXISTING_HEAPS + BOOL ExistingHeapsSupported() const noexcept; + + // D3D12_OPTIONS4 + BOOL MSAA64KBAlignedTextureSupported() const noexcept; + D3D12_SHARED_RESOURCE_COMPATIBILITY_TIER SharedResourceCompatibilityTier() const noexcept; + BOOL Native16BitShaderOpsSupported() const noexcept; + + // SERIALIZATION + D3D12_HEAP_SERIALIZATION_TIER HeapSerializationTier(UINT NodeIndex = 0) const; + + // CROSS_NODE + // CrossNodeSharingTier handled in D3D12Options + BOOL CrossNodeAtomicShaderInstructions() const noexcept; + + // D3D12_OPTIONS5 + BOOL SRVOnlyTiledResourceTier3() const noexcept; + D3D12_RENDER_PASS_TIER RenderPassesTier() const noexcept; + D3D12_RAYTRACING_TIER RaytracingTier() const noexcept; + +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 4) + // DISPLAYABLE + BOOL DisplayableTexture() const noexcept; + // SharedResourceCompatibilityTier handled in D3D12Options4 +#endif + + // D3D12_OPTIONS6 + BOOL AdditionalShadingRatesSupported() const noexcept; + BOOL PerPrimitiveShadingRateSupportedWithViewportIndexing() const noexcept; + D3D12_VARIABLE_SHADING_RATE_TIER VariableShadingRateTier() const noexcept; + UINT ShadingRateImageTileSize() const noexcept; + BOOL BackgroundProcessingSupported() const noexcept; + + // QUERY_META_COMMAND + HRESULT QueryMetaCommand(D3D12_FEATURE_DATA_QUERY_META_COMMAND& dQueryMetaCommand) const; + + // D3D12_OPTIONS7 + D3D12_MESH_SHADER_TIER MeshShaderTier() const noexcept; + D3D12_SAMPLER_FEEDBACK_TIER SamplerFeedbackTier() const noexcept; + + // PROTECTED_RESOURCE_SESSION_TYPE_COUNT + UINT ProtectedResourceSessionTypeCount(UINT NodeIndex = 0) const; + + // PROTECTED_RESOURCE_SESSION_TYPES + std::vector ProtectedResourceSessionTypes(UINT NodeIndex = 0) const; + +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 3) + // D3D12_OPTIONS8 + BOOL UnalignedBlockTexturesSupported() const noexcept; + + // D3D12_OPTIONS9 + BOOL MeshShaderPipelineStatsSupported() const noexcept; + BOOL MeshShaderSupportsFullRangeRenderTargetArrayIndex() const noexcept; + BOOL AtomicInt64OnTypedResourceSupported() const noexcept; + BOOL AtomicInt64OnGroupSharedSupported() const noexcept; + BOOL DerivativesInMeshAndAmplificationShadersSupported() const noexcept; + D3D12_WAVE_MMA_TIER WaveMMATier() const noexcept; +#endif + +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 4) + // D3D12_OPTIONS10 + BOOL VariableRateShadingSumCombinerSupported() const noexcept; + BOOL MeshShaderPerPrimitiveShadingRateSupported() const noexcept; + + // D3D12_OPTIONS11 + BOOL AtomicInt64OnDescriptorHeapResourceSupported() const noexcept; +#endif + +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 600) + // D3D12_OPTIONS12 + D3D12_TRI_STATE MSPrimitivesPipelineStatisticIncludesCulledPrimitives() const noexcept; + BOOL EnhancedBarriersSupported() const noexcept; + BOOL RelaxedFormatCastingSupported() const noexcept; +#endif + +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 602) + // D3D12_OPTIONS13 + BOOL UnrestrictedBufferTextureCopyPitchSupported() const noexcept; + BOOL UnrestrictedVertexElementAlignmentSupported() const noexcept; + BOOL InvertedViewportHeightFlipsYSupported() const noexcept; + BOOL InvertedViewportDepthFlipsZSupported() const noexcept; + BOOL TextureCopyBetweenDimensionsSupported() const noexcept; + BOOL AlphaBlendFactorSupported() const noexcept; +#endif + +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 606) + // D3D12_OPTIONS14 + BOOL AdvancedTextureOpsSupported() const noexcept; + BOOL WriteableMSAATexturesSupported() const noexcept; + BOOL IndependentFrontAndBackStencilRefMaskSupported() const noexcept; + + // D3D12_OPTIONS15 + BOOL TriangleFanSupported() const noexcept; + BOOL DynamicIndexBufferStripCutSupported() const noexcept; +#endif + +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 608) + // D3D12_OPTIONS16 + BOOL DynamicDepthBiasSupported() const noexcept; +#endif + +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 609) +#if 0 + BOOL GPUUploadHeapSupported() const noexcept; +#endif + + // D3D12_OPTIONS17 + BOOL NonNormalizedCoordinateSamplersSupported() const noexcept; + BOOL ManualWriteTrackingResourceSupported() const noexcept; + + // D3D12_OPTIONS18 + BOOL RenderPassesValid() const noexcept; +#endif + +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 610) + BOOL MismatchingOutputDimensionsSupported() const noexcept; + UINT SupportedSampleCountsWithNoOutputs() const noexcept; + BOOL PointSamplingAddressesNeverRoundUp() const noexcept; + BOOL RasterizerDesc2Supported() const noexcept; + BOOL NarrowQuadrilateralLinesSupported() const noexcept; + BOOL AnisoFilterWithPointMipSupported() const noexcept; + UINT MaxSamplerDescriptorHeapSize() const noexcept; + UINT MaxSamplerDescriptorHeapSizeWithStaticSamplers() const noexcept; + UINT MaxViewDescriptorHeapSize() const noexcept; +#endif + +private: // Private structs and helpers declaration + struct ProtectedResourceSessionTypesLocal : D3D12_FEATURE_DATA_PROTECTED_RESOURCE_SESSION_TYPES + { + std::vector TypeVec; + }; + + // Helper function to decide the highest shader model supported by the system + // Stores the result in m_dShaderModel + // Must be updated whenever a new shader model is added to the d3d12.h header + HRESULT QueryHighestShaderModel(); + + // Helper function to decide the highest root signature supported + // Must be updated whenever a new root signature version is added to the d3d12.h header + HRESULT QueryHighestRootSignatureVersion(); + + // Helper funcion to decide the highest feature level + HRESULT QueryHighestFeatureLevel(); + + // Helper function to initialize local protected resource session types structs + HRESULT QueryProtectedResourceSessionTypes(UINT NodeIndex, UINT Count); + +private: // Member data + // Pointer to the underlying device + ID3D12Device* m_pDevice; + + // Stores the error code from initialization + HRESULT m_hStatus; + + // Feature support data structs + D3D12_FEATURE_DATA_D3D12_OPTIONS m_dOptions; + D3D_FEATURE_LEVEL m_eMaxFeatureLevel; + D3D12_FEATURE_DATA_GPU_VIRTUAL_ADDRESS_SUPPORT m_dGPUVASupport; + D3D12_FEATURE_DATA_SHADER_MODEL m_dShaderModel; + D3D12_FEATURE_DATA_D3D12_OPTIONS1 m_dOptions1; + std::vector m_dProtectedResourceSessionSupport; + D3D12_FEATURE_DATA_ROOT_SIGNATURE m_dRootSignature; + std::vector m_dArchitecture1; + D3D12_FEATURE_DATA_D3D12_OPTIONS2 m_dOptions2; + D3D12_FEATURE_DATA_SHADER_CACHE m_dShaderCache; + D3D12_FEATURE_DATA_COMMAND_QUEUE_PRIORITY m_dCommandQueuePriority; + D3D12_FEATURE_DATA_D3D12_OPTIONS3 m_dOptions3; + D3D12_FEATURE_DATA_EXISTING_HEAPS m_dExistingHeaps; + D3D12_FEATURE_DATA_D3D12_OPTIONS4 m_dOptions4; + std::vector m_dSerialization; // Cat2 NodeIndex + D3D12_FEATURE_DATA_CROSS_NODE m_dCrossNode; + D3D12_FEATURE_DATA_D3D12_OPTIONS5 m_dOptions5; +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 4) + D3D12_FEATURE_DATA_DISPLAYABLE m_dDisplayable; +#endif + D3D12_FEATURE_DATA_D3D12_OPTIONS6 m_dOptions6; + D3D12_FEATURE_DATA_D3D12_OPTIONS7 m_dOptions7; + std::vector m_dProtectedResourceSessionTypeCount; // Cat2 NodeIndex + std::vector m_dProtectedResourceSessionTypes; // Cat3 +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 3) + D3D12_FEATURE_DATA_D3D12_OPTIONS8 m_dOptions8; + D3D12_FEATURE_DATA_D3D12_OPTIONS9 m_dOptions9; +#endif +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 4) + D3D12_FEATURE_DATA_D3D12_OPTIONS10 m_dOptions10; + D3D12_FEATURE_DATA_D3D12_OPTIONS11 m_dOptions11; +#endif +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 600) + D3D12_FEATURE_DATA_D3D12_OPTIONS12 m_dOptions12; +#endif +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 602) + D3D12_FEATURE_DATA_D3D12_OPTIONS13 m_dOptions13; +#endif +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 606) + D3D12_FEATURE_DATA_D3D12_OPTIONS14 m_dOptions14; + D3D12_FEATURE_DATA_D3D12_OPTIONS15 m_dOptions15; +#endif +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 608) + D3D12_FEATURE_DATA_D3D12_OPTIONS16 m_dOptions16; +#endif +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 609) + D3D12_FEATURE_DATA_D3D12_OPTIONS17 m_dOptions17; +#endif +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 609) + D3D12_FEATURE_DATA_D3D12_OPTIONS18 m_dOptions18; +#endif +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 610) + D3D12_FEATURE_DATA_D3D12_OPTIONS19 m_dOptions19; +#endif +}; + +// Implementations for CD3DX12FeatureSupport functions + +// Macro to set up a getter function for each entry in feature support data +// The getter function will have the same name as the feature option name +#define FEATURE_SUPPORT_GET(RETTYPE,FEATURE,OPTION) \ +inline RETTYPE CD3DX12FeatureSupport::OPTION() const noexcept \ +{ \ + return FEATURE.OPTION; \ +} + +// Macro to set up a getter function for each entry in feature support data +// Also specifies the name for the function which can be different from the feature name +#define FEATURE_SUPPORT_GET_NAME(RETTYPE,FEATURE,OPTION,NAME) \ +inline RETTYPE CD3DX12FeatureSupport::NAME() const noexcept \ +{\ + return FEATURE.OPTION; \ +} + +// Macro to set up a getter function for feature data indexed by the graphics node ID +// The default parameter is 0, or the first availabe graphics device node +#define FEATURE_SUPPORT_GET_NODE_INDEXED(RETTYPE,FEATURE,OPTION) \ +inline RETTYPE CD3DX12FeatureSupport::OPTION(UINT NodeIndex) const \ +{\ + return FEATURE[NodeIndex].OPTION; \ +} + +// Macro to set up a getter function for feature data indexed by NodeIndex +// Allows a custom name for the getter function +#define FEATURE_SUPPORT_GET_NODE_INDEXED_NAME(RETTYPE,FEATURE,OPTION,NAME) \ +inline RETTYPE CD3DX12FeatureSupport::NAME(UINT NodeIndex) const \ +{\ + return FEATURE[NodeIndex].OPTION; \ +} + +inline CD3DX12FeatureSupport::CD3DX12FeatureSupport() noexcept +: m_pDevice(nullptr) +, m_hStatus(E_INVALIDARG) +, m_dOptions{} +, m_eMaxFeatureLevel{} +, m_dGPUVASupport{} +, m_dShaderModel{} +, m_dOptions1{} +, m_dRootSignature{} +, m_dOptions2{} +, m_dShaderCache{} +, m_dCommandQueuePriority{} +, m_dOptions3{} +, m_dExistingHeaps{} +, m_dOptions4{} +, m_dCrossNode{} +, m_dOptions5{} +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 4) +, m_dDisplayable{} +#endif +, m_dOptions6{} +, m_dOptions7{} +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 3) +, m_dOptions8{} +, m_dOptions9{} +#endif +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 4) +, m_dOptions10{} +, m_dOptions11{} +#endif +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 600) +, m_dOptions12{} +#endif +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 602) +, m_dOptions13{} +#endif +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 606) +, m_dOptions14{} +, m_dOptions15{} +#endif +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 608) +, m_dOptions16{} +#endif +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 609) +, m_dOptions17{} +#endif +#if defined (D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 609) +, m_dOptions18{} +#endif +#if defined (D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 610) +, m_dOptions19{} +#endif +{} + +inline HRESULT CD3DX12FeatureSupport::Init(ID3D12Device* pDevice) +{ + if (!pDevice) + { + m_hStatus = E_INVALIDARG; + return m_hStatus; + } + + m_pDevice = pDevice; + + // Initialize static feature support data structures + if (FAILED(m_pDevice->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS, &m_dOptions, sizeof(m_dOptions)))) + { + m_dOptions = {}; + } + + if (FAILED(m_pDevice->CheckFeatureSupport(D3D12_FEATURE_GPU_VIRTUAL_ADDRESS_SUPPORT, &m_dGPUVASupport, sizeof(m_dGPUVASupport)))) + { + m_dGPUVASupport = {}; + } + + if (FAILED(m_pDevice->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS1, &m_dOptions1, sizeof(m_dOptions1)))) + { + m_dOptions1 = {}; + } + + if (FAILED(m_pDevice->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS2, &m_dOptions2, sizeof(m_dOptions2)))) + { + m_dOptions2 = {}; + } + + if (FAILED(m_pDevice->CheckFeatureSupport(D3D12_FEATURE_SHADER_CACHE, &m_dShaderCache, sizeof(m_dShaderCache)))) + { + m_dShaderCache = {}; + } + + if (FAILED(m_pDevice->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS3, &m_dOptions3, sizeof(m_dOptions3)))) + { + m_dOptions3 = {}; + } + + if (FAILED(m_pDevice->CheckFeatureSupport(D3D12_FEATURE_EXISTING_HEAPS, &m_dExistingHeaps, sizeof(m_dExistingHeaps)))) + { + m_dExistingHeaps = {}; + } + + if (FAILED(m_pDevice->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS4, &m_dOptions4, sizeof(m_dOptions4)))) + { + m_dOptions4 = {}; + } + + if (FAILED(m_pDevice->CheckFeatureSupport(D3D12_FEATURE_CROSS_NODE, &m_dCrossNode, sizeof(m_dCrossNode)))) + { + m_dCrossNode = {}; + } + + if (FAILED(m_pDevice->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS5, &m_dOptions5, sizeof(m_dOptions5)))) + { + m_dOptions5 = {}; + } + +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 4) + if (FAILED(m_pDevice->CheckFeatureSupport(D3D12_FEATURE_DISPLAYABLE, &m_dDisplayable, sizeof(m_dDisplayable)))) + { + m_dDisplayable = {}; + } +#endif + + if (FAILED(m_pDevice->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS6, &m_dOptions6, sizeof(m_dOptions6)))) + { + m_dOptions6 = {}; + } + + if (FAILED(m_pDevice->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS7, &m_dOptions7, sizeof(m_dOptions7)))) + { + m_dOptions7 = {}; + } + +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 3) + if (FAILED(m_pDevice->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS8, &m_dOptions8, sizeof(m_dOptions8)))) + { + m_dOptions8 = {}; + } + + if (FAILED(m_pDevice->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS9, &m_dOptions9, sizeof(m_dOptions9)))) + { + m_dOptions9 = {}; + } +#endif + +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 4) + if (FAILED(m_pDevice->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS10, &m_dOptions10, sizeof(m_dOptions10)))) + { + m_dOptions10 = {}; + } + + if (FAILED(m_pDevice->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS11, &m_dOptions11, sizeof(m_dOptions11)))) + { + m_dOptions11 = {}; + } +#endif + +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 600) + if (FAILED(m_pDevice->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS12, &m_dOptions12, sizeof(m_dOptions12)))) + { + m_dOptions12 = {}; + m_dOptions12.MSPrimitivesPipelineStatisticIncludesCulledPrimitives = D3D12_TRI_STATE::D3D12_TRI_STATE_UNKNOWN; + } +#endif + +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 602) + if (FAILED(m_pDevice->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS13, &m_dOptions13, sizeof(m_dOptions13)))) + { + m_dOptions13 = {}; + } +#endif + +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 606) + if (FAILED(m_pDevice->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS14, &m_dOptions14, sizeof(m_dOptions14)))) + { + m_dOptions14 = {}; + } + + if (FAILED(m_pDevice->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS15, &m_dOptions15, sizeof(m_dOptions15)))) + { + m_dOptions15 = {}; + } +#endif + +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 608) + if (FAILED(m_pDevice->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS16, &m_dOptions16, sizeof(m_dOptions16)))) + { + m_dOptions16 = {}; + } +#endif + +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 609) + if (FAILED(m_pDevice->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS17, &m_dOptions17, sizeof(m_dOptions17)))) + { + m_dOptions17 = {}; + } + + if (FAILED(m_pDevice->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS18, &m_dOptions18, sizeof(m_dOptions18)))) + { + m_dOptions18.RenderPassesValid = false; + } +#endif + +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 610) + if (FAILED(m_pDevice->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS19, &m_dOptions19, sizeof(m_dOptions19)))) + { + m_dOptions19 = {}; + m_dOptions19.SupportedSampleCountsWithNoOutputs = 1; + m_dOptions19.MaxSamplerDescriptorHeapSize = D3D12_MAX_SHADER_VISIBLE_SAMPLER_HEAP_SIZE; + m_dOptions19.MaxSamplerDescriptorHeapSizeWithStaticSamplers = D3D12_MAX_SHADER_VISIBLE_SAMPLER_HEAP_SIZE; + m_dOptions19.MaxViewDescriptorHeapSize = D3D12_MAX_SHADER_VISIBLE_DESCRIPTOR_HEAP_SIZE_TIER_1; + } +#endif + + // Initialize per-node feature support data structures + const UINT uNodeCount = m_pDevice->GetNodeCount(); + m_dProtectedResourceSessionSupport.resize(uNodeCount); + m_dArchitecture1.resize(uNodeCount); + m_dSerialization.resize(uNodeCount); + m_dProtectedResourceSessionTypeCount.resize(uNodeCount); + m_dProtectedResourceSessionTypes.resize(uNodeCount); + for (UINT NodeIndex = 0; NodeIndex < uNodeCount; NodeIndex++) + { + m_dProtectedResourceSessionSupport[NodeIndex].NodeIndex = NodeIndex; + if (FAILED(m_pDevice->CheckFeatureSupport(D3D12_FEATURE_PROTECTED_RESOURCE_SESSION_SUPPORT, &m_dProtectedResourceSessionSupport[NodeIndex], sizeof(m_dProtectedResourceSessionSupport[NodeIndex])))) + { + m_dProtectedResourceSessionSupport[NodeIndex].Support = D3D12_PROTECTED_RESOURCE_SESSION_SUPPORT_FLAG_NONE; + } + + m_dArchitecture1[NodeIndex].NodeIndex = NodeIndex; + if (FAILED(m_pDevice->CheckFeatureSupport(D3D12_FEATURE_ARCHITECTURE1, &m_dArchitecture1[NodeIndex], sizeof(m_dArchitecture1[NodeIndex])))) + { + D3D12_FEATURE_DATA_ARCHITECTURE dArchLocal = {}; + dArchLocal.NodeIndex = NodeIndex; + if (FAILED(m_pDevice->CheckFeatureSupport(D3D12_FEATURE_ARCHITECTURE, &dArchLocal, sizeof(dArchLocal)))) + { + dArchLocal.TileBasedRenderer = false; + dArchLocal.UMA = false; + dArchLocal.CacheCoherentUMA = false; + } + + m_dArchitecture1[NodeIndex].TileBasedRenderer = dArchLocal.TileBasedRenderer; + m_dArchitecture1[NodeIndex].UMA = dArchLocal.UMA; + m_dArchitecture1[NodeIndex].CacheCoherentUMA = dArchLocal.CacheCoherentUMA; + m_dArchitecture1[NodeIndex].IsolatedMMU = false; + } + + m_dSerialization[NodeIndex].NodeIndex = NodeIndex; + if (FAILED(m_pDevice->CheckFeatureSupport(D3D12_FEATURE_SERIALIZATION, &m_dSerialization[NodeIndex], sizeof(m_dSerialization[NodeIndex])))) + { + m_dSerialization[NodeIndex].HeapSerializationTier = D3D12_HEAP_SERIALIZATION_TIER_0; + } + + m_dProtectedResourceSessionTypeCount[NodeIndex].NodeIndex = NodeIndex; + if (FAILED(m_pDevice->CheckFeatureSupport(D3D12_FEATURE_PROTECTED_RESOURCE_SESSION_TYPE_COUNT, &m_dProtectedResourceSessionTypeCount[NodeIndex], sizeof(m_dProtectedResourceSessionTypeCount[NodeIndex])))) + { + m_dProtectedResourceSessionTypeCount[NodeIndex].Count = 0; + } + + // Special procedure to initialize local protected resource session types structs + // Must wait until session type count initialized + QueryProtectedResourceSessionTypes(NodeIndex, m_dProtectedResourceSessionTypeCount[NodeIndex].Count); + } + + // Initialize features that requires highest version check + if (FAILED(m_hStatus = QueryHighestShaderModel())) + { + return m_hStatus; + } + + if (FAILED(m_hStatus = QueryHighestRootSignatureVersion())) + { + return m_hStatus; + } + + // Initialize Feature Levels data + if (FAILED(m_hStatus = QueryHighestFeatureLevel())) + { + return m_hStatus; + } + + return m_hStatus; +} + +// 0: D3D12_OPTIONS +FEATURE_SUPPORT_GET(BOOL, m_dOptions, DoublePrecisionFloatShaderOps); +FEATURE_SUPPORT_GET(BOOL, m_dOptions, OutputMergerLogicOp); +FEATURE_SUPPORT_GET(D3D12_SHADER_MIN_PRECISION_SUPPORT, m_dOptions, MinPrecisionSupport); +FEATURE_SUPPORT_GET(D3D12_TILED_RESOURCES_TIER, m_dOptions, TiledResourcesTier); +FEATURE_SUPPORT_GET(D3D12_RESOURCE_BINDING_TIER, m_dOptions, ResourceBindingTier); +FEATURE_SUPPORT_GET(BOOL, m_dOptions, PSSpecifiedStencilRefSupported); +FEATURE_SUPPORT_GET(BOOL, m_dOptions, TypedUAVLoadAdditionalFormats); +FEATURE_SUPPORT_GET(BOOL, m_dOptions, ROVsSupported); +FEATURE_SUPPORT_GET(D3D12_CONSERVATIVE_RASTERIZATION_TIER, m_dOptions, ConservativeRasterizationTier); +FEATURE_SUPPORT_GET(BOOL, m_dOptions, StandardSwizzle64KBSupported); +FEATURE_SUPPORT_GET(BOOL, m_dOptions, CrossAdapterRowMajorTextureSupported); +FEATURE_SUPPORT_GET(BOOL, m_dOptions, VPAndRTArrayIndexFromAnyShaderFeedingRasterizerSupportedWithoutGSEmulation); +FEATURE_SUPPORT_GET(D3D12_RESOURCE_HEAP_TIER, m_dOptions, ResourceHeapTier); + +// Special procedure for handling caps that is also part of other features +inline D3D12_CROSS_NODE_SHARING_TIER CD3DX12FeatureSupport::CrossNodeSharingTier() const noexcept +{ + if (m_dCrossNode.SharingTier > D3D12_CROSS_NODE_SHARING_TIER_NOT_SUPPORTED) + { + return m_dCrossNode.SharingTier; + } + else + { + return m_dOptions.CrossNodeSharingTier; + } +} + +inline UINT CD3DX12FeatureSupport::MaxGPUVirtualAddressBitsPerResource() const noexcept +{ + if (m_dOptions.MaxGPUVirtualAddressBitsPerResource > 0) + { + return m_dOptions.MaxGPUVirtualAddressBitsPerResource; + } + else + { + return m_dGPUVASupport.MaxGPUVirtualAddressBitsPerResource; + } +} + +// 1: Architecture +// Combined with Architecture1 + +// 2: Feature Levels +// Simply returns the highest supported feature level +inline D3D_FEATURE_LEVEL CD3DX12FeatureSupport::MaxSupportedFeatureLevel() const noexcept +{ + return m_eMaxFeatureLevel; +} + +// 3: Feature Format Support +inline HRESULT CD3DX12FeatureSupport::FormatSupport(DXGI_FORMAT Format, D3D12_FORMAT_SUPPORT1& Support1, D3D12_FORMAT_SUPPORT2& Support2) const +{ + D3D12_FEATURE_DATA_FORMAT_SUPPORT dFormatSupport; + dFormatSupport.Format = Format; + + // It is possible that the function call returns an error + HRESULT result = m_pDevice->CheckFeatureSupport(D3D12_FEATURE_FORMAT_SUPPORT, &dFormatSupport, sizeof(D3D12_FEATURE_DATA_FORMAT_SUPPORT)); + + Support1 = dFormatSupport.Support1; + Support2 = dFormatSupport.Support2; // Two outputs. Probably better just to take in the struct as an argument? + + return result; +} + +// 4: Multisample Quality Levels +inline HRESULT CD3DX12FeatureSupport::MultisampleQualityLevels(DXGI_FORMAT Format, UINT SampleCount, D3D12_MULTISAMPLE_QUALITY_LEVEL_FLAGS Flags, UINT& NumQualityLevels) const +{ + D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS dMultisampleQualityLevels; + dMultisampleQualityLevels.Format = Format; + dMultisampleQualityLevels.SampleCount = SampleCount; + dMultisampleQualityLevels.Flags = Flags; + + HRESULT result = m_pDevice->CheckFeatureSupport(D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS, &dMultisampleQualityLevels, sizeof(D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS)); + + if (SUCCEEDED(result)) + { + NumQualityLevels = dMultisampleQualityLevels.NumQualityLevels; + } + else + { + NumQualityLevels = 0; + } + + return result; +} + +// 5: Format Info +inline HRESULT CD3DX12FeatureSupport::FormatInfo(DXGI_FORMAT Format, UINT8& PlaneCount) const +{ + D3D12_FEATURE_DATA_FORMAT_INFO dFormatInfo; + dFormatInfo.Format = Format; + + HRESULT result = m_pDevice->CheckFeatureSupport(D3D12_FEATURE_FORMAT_INFO, &dFormatInfo, sizeof(D3D12_FEATURE_DATA_FORMAT_INFO)); + if (FAILED(result)) + { + PlaneCount = 0; + } + else + { + PlaneCount = dFormatInfo.PlaneCount; + } + return result; +} + +// 6: GPU Virtual Address Support +// MaxGPUVirtualAddressBitsPerResource handled in D3D12Options +FEATURE_SUPPORT_GET(UINT, m_dGPUVASupport, MaxGPUVirtualAddressBitsPerProcess); + +// 7: Shader Model +inline D3D_SHADER_MODEL CD3DX12FeatureSupport::HighestShaderModel() const noexcept +{ + return m_dShaderModel.HighestShaderModel; +} + +// 8: D3D12 Options1 +FEATURE_SUPPORT_GET(BOOL, m_dOptions1, WaveOps); +FEATURE_SUPPORT_GET(UINT, m_dOptions1, WaveLaneCountMin); +FEATURE_SUPPORT_GET(UINT, m_dOptions1, WaveLaneCountMax); +FEATURE_SUPPORT_GET(UINT, m_dOptions1, TotalLaneCount); +FEATURE_SUPPORT_GET(BOOL, m_dOptions1, ExpandedComputeResourceStates); +FEATURE_SUPPORT_GET(BOOL, m_dOptions1, Int64ShaderOps); + +// 10: Protected Resource Session Support +inline D3D12_PROTECTED_RESOURCE_SESSION_SUPPORT_FLAGS CD3DX12FeatureSupport::ProtectedResourceSessionSupport(UINT NodeIndex) const +{ + return m_dProtectedResourceSessionSupport[NodeIndex].Support; +} + +// 12: Root Signature +inline D3D_ROOT_SIGNATURE_VERSION CD3DX12FeatureSupport::HighestRootSignatureVersion() const noexcept +{ + return m_dRootSignature.HighestVersion; +} + +// 16: Architecture1 +// Same data fields can be queried from m_dArchitecture +FEATURE_SUPPORT_GET_NODE_INDEXED(BOOL, m_dArchitecture1, TileBasedRenderer); +FEATURE_SUPPORT_GET_NODE_INDEXED(BOOL, m_dArchitecture1, UMA); +FEATURE_SUPPORT_GET_NODE_INDEXED(BOOL, m_dArchitecture1, CacheCoherentUMA); +FEATURE_SUPPORT_GET_NODE_INDEXED(BOOL, m_dArchitecture1, IsolatedMMU); + +// 18: D3D12 Options2 +FEATURE_SUPPORT_GET(BOOL, m_dOptions2, DepthBoundsTestSupported); +FEATURE_SUPPORT_GET(D3D12_PROGRAMMABLE_SAMPLE_POSITIONS_TIER, m_dOptions2, ProgrammableSamplePositionsTier); + +// 19: Shader Cache +FEATURE_SUPPORT_GET_NAME(D3D12_SHADER_CACHE_SUPPORT_FLAGS, m_dShaderCache, SupportFlags, ShaderCacheSupportFlags); + +// 20: Command Queue Priority +inline BOOL CD3DX12FeatureSupport::CommandQueuePrioritySupported(D3D12_COMMAND_LIST_TYPE CommandListType, UINT Priority) +{ + m_dCommandQueuePriority.CommandListType = CommandListType; + m_dCommandQueuePriority.Priority = Priority; + + if (FAILED(m_pDevice->CheckFeatureSupport(D3D12_FEATURE_COMMAND_QUEUE_PRIORITY, &m_dCommandQueuePriority, sizeof(D3D12_FEATURE_DATA_COMMAND_QUEUE_PRIORITY)))) + { + return false; + } + + return m_dCommandQueuePriority.PriorityForTypeIsSupported; +} + +// 21: D3D12 Options3 +FEATURE_SUPPORT_GET(BOOL, m_dOptions3, CopyQueueTimestampQueriesSupported); +FEATURE_SUPPORT_GET(BOOL, m_dOptions3, CastingFullyTypedFormatSupported); +FEATURE_SUPPORT_GET(D3D12_COMMAND_LIST_SUPPORT_FLAGS, m_dOptions3, WriteBufferImmediateSupportFlags); +FEATURE_SUPPORT_GET(D3D12_VIEW_INSTANCING_TIER, m_dOptions3, ViewInstancingTier); +FEATURE_SUPPORT_GET(BOOL, m_dOptions3, BarycentricsSupported); + +// 22: Existing Heaps +FEATURE_SUPPORT_GET_NAME(BOOL, m_dExistingHeaps, Supported, ExistingHeapsSupported); + +// 23: D3D12 Options4 +FEATURE_SUPPORT_GET(BOOL, m_dOptions4, MSAA64KBAlignedTextureSupported); +FEATURE_SUPPORT_GET(D3D12_SHARED_RESOURCE_COMPATIBILITY_TIER, m_dOptions4, SharedResourceCompatibilityTier); +FEATURE_SUPPORT_GET(BOOL, m_dOptions4, Native16BitShaderOpsSupported); + +// 24: Serialization +FEATURE_SUPPORT_GET_NODE_INDEXED(D3D12_HEAP_SERIALIZATION_TIER, m_dSerialization, HeapSerializationTier); + +// 25: Cross Node +// CrossNodeSharingTier handled in D3D12Options +FEATURE_SUPPORT_GET_NAME(BOOL, m_dCrossNode, AtomicShaderInstructions, CrossNodeAtomicShaderInstructions); + +// 27: D3D12 Options5 +FEATURE_SUPPORT_GET(BOOL, m_dOptions5, SRVOnlyTiledResourceTier3); +FEATURE_SUPPORT_GET(D3D12_RENDER_PASS_TIER, m_dOptions5, RenderPassesTier); +FEATURE_SUPPORT_GET(D3D12_RAYTRACING_TIER, m_dOptions5, RaytracingTier); + +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 4) +// 28: Displayable +FEATURE_SUPPORT_GET(BOOL, m_dDisplayable, DisplayableTexture); +// SharedResourceCompatibilityTier handled in D3D12Options4 +#endif + +// 30: D3D12 Options6 +FEATURE_SUPPORT_GET(BOOL, m_dOptions6, AdditionalShadingRatesSupported); +FEATURE_SUPPORT_GET(BOOL, m_dOptions6, PerPrimitiveShadingRateSupportedWithViewportIndexing); +FEATURE_SUPPORT_GET(D3D12_VARIABLE_SHADING_RATE_TIER, m_dOptions6, VariableShadingRateTier); +FEATURE_SUPPORT_GET(UINT, m_dOptions6, ShadingRateImageTileSize); +FEATURE_SUPPORT_GET(BOOL, m_dOptions6, BackgroundProcessingSupported); + +// 31: Query Meta Command +// Keep the original call routine +inline HRESULT CD3DX12FeatureSupport::QueryMetaCommand(D3D12_FEATURE_DATA_QUERY_META_COMMAND& dQueryMetaCommand) const +{ + return m_pDevice->CheckFeatureSupport(D3D12_FEATURE_QUERY_META_COMMAND, &dQueryMetaCommand, sizeof(D3D12_FEATURE_DATA_QUERY_META_COMMAND)); +} + +// 32: D3D12 Options7 +FEATURE_SUPPORT_GET(D3D12_MESH_SHADER_TIER, m_dOptions7, MeshShaderTier); +FEATURE_SUPPORT_GET(D3D12_SAMPLER_FEEDBACK_TIER, m_dOptions7, SamplerFeedbackTier); + +// 33: Protected Resource Session Type Count +FEATURE_SUPPORT_GET_NODE_INDEXED_NAME(UINT, m_dProtectedResourceSessionTypeCount, Count, ProtectedResourceSessionTypeCount); + +// 34: Protected Resource Session Types +FEATURE_SUPPORT_GET_NODE_INDEXED_NAME(std::vector, m_dProtectedResourceSessionTypes, TypeVec, ProtectedResourceSessionTypes); + +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 3) +// 36: Options8 +FEATURE_SUPPORT_GET(BOOL, m_dOptions8, UnalignedBlockTexturesSupported); + +// 37: Options9 +FEATURE_SUPPORT_GET(BOOL, m_dOptions9, MeshShaderPipelineStatsSupported); +FEATURE_SUPPORT_GET(BOOL, m_dOptions9, MeshShaderSupportsFullRangeRenderTargetArrayIndex); +FEATURE_SUPPORT_GET(BOOL, m_dOptions9, AtomicInt64OnTypedResourceSupported); +FEATURE_SUPPORT_GET(BOOL, m_dOptions9, AtomicInt64OnGroupSharedSupported); +FEATURE_SUPPORT_GET(BOOL, m_dOptions9, DerivativesInMeshAndAmplificationShadersSupported); +FEATURE_SUPPORT_GET(D3D12_WAVE_MMA_TIER, m_dOptions9, WaveMMATier); +#endif + +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 4) +// 39: Options10 +FEATURE_SUPPORT_GET(BOOL, m_dOptions10, VariableRateShadingSumCombinerSupported); +FEATURE_SUPPORT_GET(BOOL, m_dOptions10, MeshShaderPerPrimitiveShadingRateSupported); + +// 40: Options11 +FEATURE_SUPPORT_GET(BOOL, m_dOptions11, AtomicInt64OnDescriptorHeapResourceSupported); +#endif + +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 600) +// 41: Options12 +FEATURE_SUPPORT_GET(D3D12_TRI_STATE, m_dOptions12, MSPrimitivesPipelineStatisticIncludesCulledPrimitives); +FEATURE_SUPPORT_GET(BOOL, m_dOptions12, EnhancedBarriersSupported); +FEATURE_SUPPORT_GET(BOOL, m_dOptions12, RelaxedFormatCastingSupported); +#endif + +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 602) +// 42: Options13 +FEATURE_SUPPORT_GET(BOOL, m_dOptions13, UnrestrictedBufferTextureCopyPitchSupported); +FEATURE_SUPPORT_GET(BOOL, m_dOptions13, UnrestrictedVertexElementAlignmentSupported); +FEATURE_SUPPORT_GET(BOOL, m_dOptions13, InvertedViewportHeightFlipsYSupported); +FEATURE_SUPPORT_GET(BOOL, m_dOptions13, InvertedViewportDepthFlipsZSupported); +FEATURE_SUPPORT_GET(BOOL, m_dOptions13, TextureCopyBetweenDimensionsSupported); +FEATURE_SUPPORT_GET(BOOL, m_dOptions13, AlphaBlendFactorSupported); +#endif + +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 606) +// 43: Options14 +FEATURE_SUPPORT_GET(BOOL, m_dOptions14, AdvancedTextureOpsSupported); +FEATURE_SUPPORT_GET(BOOL, m_dOptions14, WriteableMSAATexturesSupported); +FEATURE_SUPPORT_GET(BOOL, m_dOptions14, IndependentFrontAndBackStencilRefMaskSupported); + +// 44: Options15 +FEATURE_SUPPORT_GET(BOOL, m_dOptions15, TriangleFanSupported); +FEATURE_SUPPORT_GET(BOOL, m_dOptions15, DynamicIndexBufferStripCutSupported); +#endif + +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 608) +// 45: Options16 +FEATURE_SUPPORT_GET(BOOL, m_dOptions16, DynamicDepthBiasSupported); +#endif +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 609) +#if 0 +FEATURE_SUPPORT_GET(BOOL, m_dOptions16, GPUUploadHeapSupported); +#endif + +// 46: Options17 +FEATURE_SUPPORT_GET(BOOL, m_dOptions17, NonNormalizedCoordinateSamplersSupported); +FEATURE_SUPPORT_GET(BOOL, m_dOptions17, ManualWriteTrackingResourceSupported); + +// 47: Option18 +FEATURE_SUPPORT_GET(BOOL, m_dOptions18, RenderPassesValid); +#endif + +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 610) +FEATURE_SUPPORT_GET(BOOL, m_dOptions19, MismatchingOutputDimensionsSupported); +FEATURE_SUPPORT_GET(UINT, m_dOptions19, SupportedSampleCountsWithNoOutputs); +FEATURE_SUPPORT_GET(BOOL, m_dOptions19, PointSamplingAddressesNeverRoundUp); +FEATURE_SUPPORT_GET(BOOL, m_dOptions19, RasterizerDesc2Supported); +FEATURE_SUPPORT_GET(BOOL, m_dOptions19, NarrowQuadrilateralLinesSupported); +FEATURE_SUPPORT_GET(BOOL, m_dOptions19, AnisoFilterWithPointMipSupported); +FEATURE_SUPPORT_GET(UINT, m_dOptions19, MaxSamplerDescriptorHeapSize); +FEATURE_SUPPORT_GET(UINT, m_dOptions19, MaxSamplerDescriptorHeapSizeWithStaticSamplers); +FEATURE_SUPPORT_GET(UINT, m_dOptions19, MaxViewDescriptorHeapSize); +#endif + +// Helper function to decide the highest shader model supported by the system +// Stores the result in m_dShaderModel +// Must be updated whenever a new shader model is added to the d3d12.h header +inline HRESULT CD3DX12FeatureSupport::QueryHighestShaderModel() +{ + // Check support in descending order + HRESULT result; + + const D3D_SHADER_MODEL allModelVersions[] = + { +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 606) + D3D_SHADER_MODEL_6_8, +#endif +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 3) + D3D_SHADER_MODEL_6_7, +#endif + D3D_SHADER_MODEL_6_6, + D3D_SHADER_MODEL_6_5, + D3D_SHADER_MODEL_6_4, + D3D_SHADER_MODEL_6_3, + D3D_SHADER_MODEL_6_2, + D3D_SHADER_MODEL_6_1, + D3D_SHADER_MODEL_6_0, + D3D_SHADER_MODEL_5_1 + }; + constexpr size_t numModelVersions = sizeof(allModelVersions) / sizeof(D3D_SHADER_MODEL); + + for (size_t i = 0; i < numModelVersions; i++) + { + m_dShaderModel.HighestShaderModel = allModelVersions[i]; + result = m_pDevice->CheckFeatureSupport(D3D12_FEATURE_SHADER_MODEL, &m_dShaderModel, sizeof(D3D12_FEATURE_DATA_SHADER_MODEL)); + if (result != E_INVALIDARG) + { + // Indicates that the version is recognizable by the runtime and stored in the struct + // Also terminate on unexpected error code + if (FAILED(result)) + { + m_dShaderModel.HighestShaderModel = static_cast(0); + } + return result; + } + } + + // Shader model may not be supported. Continue the rest initializations + m_dShaderModel.HighestShaderModel = static_cast(0); + return S_OK; +} + +// Helper function to decide the highest root signature supported +// Must be updated whenever a new root signature version is added to the d3d12.h header +inline HRESULT CD3DX12FeatureSupport::QueryHighestRootSignatureVersion() +{ + HRESULT result; + + const D3D_ROOT_SIGNATURE_VERSION allRootSignatureVersions[] = + { +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 609) + D3D_ROOT_SIGNATURE_VERSION_1_2, +#endif + D3D_ROOT_SIGNATURE_VERSION_1_1, + D3D_ROOT_SIGNATURE_VERSION_1_0, + D3D_ROOT_SIGNATURE_VERSION_1, + }; + constexpr size_t numRootSignatureVersions = sizeof(allRootSignatureVersions) / sizeof(D3D_ROOT_SIGNATURE_VERSION); + + for (size_t i = 0; i < numRootSignatureVersions; i++) + { + m_dRootSignature.HighestVersion = allRootSignatureVersions[i]; + result = m_pDevice->CheckFeatureSupport(D3D12_FEATURE_ROOT_SIGNATURE, &m_dRootSignature, sizeof(D3D12_FEATURE_DATA_ROOT_SIGNATURE)); + if (result != E_INVALIDARG) + { + if (FAILED(result)) + { + m_dRootSignature.HighestVersion = static_cast(0); + } + // If succeeded, the highest version is already written into the member struct + return result; + } + } + + // No version left. Set to invalid value and continue. + m_dRootSignature.HighestVersion = static_cast(0); + return S_OK; +} + +// Helper funcion to decide the highest feature level +inline HRESULT CD3DX12FeatureSupport::QueryHighestFeatureLevel() +{ + HRESULT result; + + // Check against a list of all feature levels present in d3dcommon.h + // Needs to be updated for future feature levels + const D3D_FEATURE_LEVEL allLevels[] = + { +#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 3) + D3D_FEATURE_LEVEL_12_2, +#endif + D3D_FEATURE_LEVEL_12_1, + D3D_FEATURE_LEVEL_12_0, + D3D_FEATURE_LEVEL_11_1, + D3D_FEATURE_LEVEL_11_0, + D3D_FEATURE_LEVEL_10_1, + D3D_FEATURE_LEVEL_10_0, + D3D_FEATURE_LEVEL_9_3, + D3D_FEATURE_LEVEL_9_2, + D3D_FEATURE_LEVEL_9_1, + D3D_FEATURE_LEVEL_1_0_CORE + }; + + D3D12_FEATURE_DATA_FEATURE_LEVELS dFeatureLevel; + dFeatureLevel.NumFeatureLevels = static_cast(sizeof(allLevels) / sizeof(D3D_FEATURE_LEVEL)); + dFeatureLevel.pFeatureLevelsRequested = allLevels; + + result = m_pDevice->CheckFeatureSupport(D3D12_FEATURE_FEATURE_LEVELS, &dFeatureLevel, sizeof(D3D12_FEATURE_DATA_FEATURE_LEVELS)); + if (SUCCEEDED(result)) + { + m_eMaxFeatureLevel = dFeatureLevel.MaxSupportedFeatureLevel; + } + else + { + m_eMaxFeatureLevel = static_cast(0); + + if (result == DXGI_ERROR_UNSUPPORTED) + { + // Indicates that none supported. Continue initialization + result = S_OK; + } + } + return result; +} + +// Helper function to initialize local protected resource session types structs +inline HRESULT CD3DX12FeatureSupport::QueryProtectedResourceSessionTypes(UINT NodeIndex, UINT Count) +{ + auto& CurrentPRSTypes = m_dProtectedResourceSessionTypes[NodeIndex]; + CurrentPRSTypes.NodeIndex = NodeIndex; + CurrentPRSTypes.Count = Count; + CurrentPRSTypes.TypeVec.resize(CurrentPRSTypes.Count); + CurrentPRSTypes.pTypes = CurrentPRSTypes.TypeVec.data(); + + HRESULT result = m_pDevice->CheckFeatureSupport(D3D12_FEATURE_PROTECTED_RESOURCE_SESSION_TYPES, &m_dProtectedResourceSessionTypes[NodeIndex], sizeof(D3D12_FEATURE_DATA_PROTECTED_RESOURCE_SESSION_TYPES)); + if (FAILED(result)) + { + // Resize TypeVec to empty + CurrentPRSTypes.TypeVec.clear(); + } + + return result; +} + +#undef FEATURE_SUPPORT_GET +#undef FEATURE_SUPPORT_GET_NAME +#undef FEATURE_SUPPORT_GET_NODE_INDEXED +#undef FEATURE_SUPPORT_GET_NODE_INDEXED_NAME + +// end CD3DX12FeatureSupport + +#endif // !D3DX12_NO_CHECK_FEATURE_SUPPORT_CLASS + +#undef D3DX12_COM_PTR +#undef D3DX12_COM_PTR_GET +#undef D3DX12_COM_PTR_ADDRESSOF + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#endif // defined( __cplusplus ) + +#endif //__D3DX12_H__ diff --git a/Common/DirectXTK12/Src/pch.cpp b/Common/DirectXTK12/Src/pch.cpp new file mode 100644 index 0000000..80aab27 --- /dev/null +++ b/Common/DirectXTK12/Src/pch.cpp @@ -0,0 +1,10 @@ +//-------------------------------------------------------------------------------------- +// File: pch.cpp +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "pch.h" diff --git a/Common/DirectXTK12/Src/pch.h b/Common/DirectXTK12/Src/pch.h new file mode 100644 index 0000000..dcfd33e --- /dev/null +++ b/Common/DirectXTK12/Src/pch.h @@ -0,0 +1,270 @@ +//-------------------------------------------------------------------------------------- +// File: pch.h +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#pragma once + +// Off by default warnings +#pragma warning(disable : 4619 4061 4265 4355 4365 4571 4623 4625 4626 4628 4668 4710 4711 4746 4774 4820 4987 5026 5027 5031 5032 5039 5045 5219 5246 5264 26812) +// C4619 #pragma warning: there is no warning number 'X' +// C4061 enumerator 'X' in switch of enum 'X' is not explicitly handled by a case label +// C4265 class has virtual functions, but destructor is not virtual +// C4355 'this': used in base member initializer list +// C4365 signed/unsigned mismatch +// C4571 behavior change +// C4623 default constructor was implicitly defined as deleted +// C4625 copy constructor was implicitly defined as deleted +// C4626 assignment operator was implicitly defined as deleted +// C4628 digraphs not supported +// C4668 not defined as a preprocessor macro +// C4710 function not inlined +// C4711 selected for automatic inline expansion +// C4746 volatile access of '' is subject to /volatile: setting +// C4774 format string expected in argument 3 is not a string literal +// C4820 padding added after data member +// C4987 nonstandard extension used +// C5026 move constructor was implicitly defined as deleted +// C5027 move assignment operator was implicitly defined as deleted +// C5031/5032 push/pop mismatches in windows headers +// C5039 pointer or reference to potentially throwing function passed to extern C function under - EHc +// C5045 Spectre mitigation warning +// C5219 implicit conversion from 'int' to 'float', possible loss of data +// C5246 the initialization of a subobject should be wrapped in braces +// C5264 'const' variable is not used +// 26812: The enum type 'x' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). + +// XBox One XDK related Off by default warnings +#pragma warning(disable : 4471 4643 4917 4986 5029 5043) +// C4471 forward declaration of an unscoped enumeration must have an underlying type +// C4643 Forward declaring in namespace std is not permitted by the C++ Standard +// C4917 a GUID can only be associated with a class, interface or namespace +// C4986 exception specification does not match previous declaration +// C5029 nonstandard extension used +// C5043 exception specification does not match previous declaration + +#ifdef __INTEL_COMPILER +#pragma warning(disable : 161 2960 3280) +// warning #161: unrecognized #pragma +// message #2960: allocation may not satisfy the type's alignment; consider using header +// message #3280: declaration hides member +#endif + +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wc++98-compat" +#pragma clang diagnostic ignored "-Wc++98-compat-pedantic" +#pragma clang diagnostic ignored "-Wc++98-compat-local-type-template-args" +#pragma clang diagnostic ignored "-Wcovered-switch-default" +#pragma clang diagnostic ignored "-Wexit-time-destructors" +#pragma clang diagnostic ignored "-Wfloat-equal" +#pragma clang diagnostic ignored "-Wglobal-constructors" +#pragma clang diagnostic ignored "-Wgnu-anonymous-struct" +#pragma clang diagnostic ignored "-Wlanguage-extension-token" +#pragma clang diagnostic ignored "-Wmissing-variable-declarations" +#pragma clang diagnostic ignored "-Wnested-anon-types" +#pragma clang diagnostic ignored "-Wreserved-id-macro" +#pragma clang diagnostic ignored "-Wswitch-enum" +#pragma clang diagnostic ignored "-Wunknown-pragmas" +#pragma clang diagnostic ignored "-Wunused-const-variable" +#pragma clang diagnostic ignored "-Wunused-member-function" +#pragma clang diagnostic ignored "-Wunknown-warning-option" +#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" +#endif + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + +#pragma warning(push) +#pragma warning(disable : 4005) +#define NOMINMAX 1 +#define NODRAWTEXT +#define NOGDI +#define NOBITMAP +#define NOMCX +#define NOSERVICE +#define NOHELP +#pragma warning(pop) + +#include + +#ifdef __MINGW32__ +#include +#endif + +#ifndef _WIN32_WINNT_WIN10 +#define _WIN32_WINNT_WIN10 0x0A00 +#endif + +#define D3DX12_NO_STATE_OBJECT_HELPERS +#define D3DX12_NO_CHECK_FEATURE_SUPPORT_CLASS + +#ifdef _GAMING_XBOX +#include + +#if _GXDK_VER < 0x55F00C58 /* GXDK Edition 220300 */ +#error DirectX Tool Kit requires the March 2022 GDK or later +#endif + +#ifdef _GAMING_XBOX_SCARLETT +#pragma warning(push) +#pragma warning(disable: 5204 5249) +#include +#pragma warning(pop) +#include +#else +#pragma warning(push) +#pragma warning(disable: 5204) +#include +#pragma warning(pop) +#include +#endif +#elif defined(_XBOX_ONE) && defined(_TITLE) +#include + +#if _XDK_VER < 0x42ED07E4 /* XDK Edition 180400 */ +#error DirectX Tool Kit for Direct3D 12 requires the April 2018 XDK or later +#endif + +#include +#include +#else + +#ifdef _GAMING_DESKTOP +#include + +#if _GRDK_VER < 0x4A611B35 /* GDK Edition 210600 */ +#error DirectX Tool Kit requires June 2021 GDK or later +#endif +#endif + +#ifdef USING_DIRECTX_HEADERS +#include +#include +#include +#else +#include +#endif + +#include + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wsign-conversion" +#pragma clang diagnostic ignored "-Wtautological-type-limit-compare" +#endif + +#ifdef USING_DIRECTX_HEADERS +#include +#else +#include "d3dx12.h" +#endif +#endif + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#if (defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP)) || (defined(_XBOX_ONE) && defined(_TITLE)) +#pragma warning(push) +#pragma warning(disable: 4471 5204 5256) +#include +#pragma warning(pop) +#endif + +#define _USE_MATH_DEFINES +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#pragma warning(push) +#pragma warning(disable : 5204 5220) +#include +#pragma warning(pop) + +#pragma warning(push) +#pragma warning(disable : 4702) +#include +#pragma warning(pop) + +#include + +#define _XM_NO_XMVECTOR_OVERLOADS_ + +#include +#include +#include + +#if (DIRECTX_MATH_VERSION < 315) +#define XM_ALIGNED_STRUCT(x) __declspec(align(x)) struct +#endif + +#pragma warning(push) +#pragma warning(disable : 4467 5038 5204 5220) +#ifdef __MINGW32__ +#include +#else +#include +#endif +#pragma warning(pop) + +#include + +#if defined(NTDDI_WIN10_FE) || defined(__MINGW32__) +#include +#else +#include +#endif + +#ifndef __MINGW32__ +// DirectX Tool Kit for Audio is in all versions of DirectXTK12 +#include +#include + +#ifndef XAUDIO2_HELPER_FUNCTIONS +#define XAUDIO2_HELPER_FUNCTIONS +#endif + +#include +#include + +#pragma warning(push) +#pragma warning(disable : 4619 4616 5246) +#include +#pragma warning(pop) + +#include + +#if (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) +#include +#include +#include +#endif +#endif diff --git a/Common/DirectXTK12/Src/vbo.h b/Common/DirectXTK12/Src/vbo.h new file mode 100644 index 0000000..d79955b --- /dev/null +++ b/Common/DirectXTK12/Src/vbo.h @@ -0,0 +1,35 @@ +//-------------------------------------------------------------------------------------- +// File: vbo.h +// +// The VBO file format was introduced in the Windows 8.0 ResourceLoading sample. It's +// a simple binary file containing a 16-bit index buffer and a fixed-format vertex buffer. +// +// The meshconvert sample tool for DirectXMesh can produce this file type +// http://go.microsoft.com/fwlink/?LinkID=324981 +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248929 +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#pragma once + +#include + +namespace VBO +{ +#pragma pack(push,1) + + struct header_t + { + uint32_t numVertices; + uint32_t numIndices; + }; + +#pragma pack(pop) + +} // namespace + +static_assert(sizeof(VBO::header_t) == 8, "VBO header size mismatch"); diff --git a/Common/DirectXTK12/build/CopyASan.targets b/Common/DirectXTK12/build/CopyASan.targets new file mode 100644 index 0000000..b11e5a0 --- /dev/null +++ b/Common/DirectXTK12/build/CopyASan.targets @@ -0,0 +1,24 @@ + + + + + .drop + + $(VsInstallRoot)\ + $(VSInstallDir)VC\ + $(VCInstallDir)Auxiliary\Build\Microsoft.VCToolsVersion.default.props + + + + + + + + + + + + + + + diff --git a/Common/DirectXTK12/build/DirectXTK12-GitHub-CMake-Dev17.yml b/Common/DirectXTK12/build/DirectXTK12-GitHub-CMake-Dev17.yml new file mode 100644 index 0000000..ca80b89 --- /dev/null +++ b/Common/DirectXTK12/build/DirectXTK12-GitHub-CMake-Dev17.yml @@ -0,0 +1,167 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# +# http://go.microsoft.com/fwlink/?LinkID=615561 + +# Builds the library using CMake. + +schedules: +- cron: "0 6 * * *" + displayName: 'Nightly build' + branches: + include: + - main + +trigger: none +pr: none + +resources: + repositories: + - repository: self + type: git + ref: refs/heads/main + trigger: + branches: + include: + - main + paths: + include: + - CMakeLists.txt + +name: $(Year:yyyy).$(Month).$(DayOfMonth)$(Rev:.r) + +variables: + VS_GENERATOR: 'Visual Studio 17 2022' + WIN10_SDK: '10.0.19041.0' + WIN11_SDK: '10.0.22000.0' + +pool: + vmImage: windows-2022 + +jobs: +- job: CMAKE_BUILD + displayName: CMake using VS Generator + steps: + - checkout: self + clean: true + fetchTags: false + - task: CMake@1 + displayName: 'CMake (MSVC): Config x64' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: '-G "$(VS_GENERATOR)" -A x64 -B out -DCMAKE_SYSTEM_VERSION=$(WIN10_SDK)' + - task: CMake@1 + displayName: 'CMake (MSVC): Build x64 Debug' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: --build out -v --config Debug + - task: CMake@1 + displayName: 'CMake (MSVC): Build x64 Release' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: --build out -v --config RelWithDebInfo + - task: CMake@1 + displayName: 'CMake (MSVC): Config x86' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: '-G "$(VS_GENERATOR)" -A Win32 -B out2 -DCMAKE_SYSTEM_VERSION=$(WIN10_SDK)' + - task: CMake@1 + displayName: 'CMake (MSVC): Build x86 Debug' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: --build out2 -v --config Debug + - task: CMake@1 + displayName: 'CMake (MSVC): Build x86 Release' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: --build out2 -v --config RelWithDebInfo + - task: CMake@1 + displayName: 'CMake (MSVC): Config ARM64' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: '-G "$(VS_GENERATOR)" -A ARM64 -B out3 -DCMAKE_SYSTEM_VERSION=$(WIN10_SDK)' + - task: CMake@1 + displayName: 'CMake (MSVC): Build ARM64 Debug' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: --build out3 -v --config Debug + - task: CMake@1 + displayName: 'CMake (MSVC): Build ARM64 Release' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: --build out3 -v --config RelWithDebInfo + - task: CMake@1 + displayName: 'CMake (UWP): Config x64' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: '-G "$(VS_GENERATOR)" -A x64 -B out4 -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION=10.0' + - task: CMake@1 + displayName: 'CMake (UWP): Build x64' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: --build out4 -v + - task: CMake@1 + displayName: 'CMake (ClangCl): Config x64' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: '-G "$(VS_GENERATOR)" -A x64 -T clangcl -B out6 -DCMAKE_SYSTEM_VERSION=$(WIN10_SDK)' + - task: CMake@1 + displayName: 'CMake (ClangCl): Build x64 Debug' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: --build out6 -v --config Debug + - task: CMake@1 + displayName: 'CMake (ClangCl): Build x64 Release' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: --build out6 -v --config RelWithDebInfo + - task: CMake@1 + displayName: 'CMake (ClangCl): Config ARM64' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: '-G "$(VS_GENERATOR)" -A ARM64 -T clangcl -B out7 -DCMAKE_SYSTEM_VERSION=$(WIN11_SDK)' + - task: CMake@1 + displayName: 'CMake (ClangCl): Build ARM64' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: --build out7 -v --config Debug + - task: CMake@1 + displayName: 'CMake (MSVC Spectre): Config x64' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: '-G "$(VS_GENERATOR)" -A x64 -B out8 -DENABLE_SPECTRE_MITIGATION=ON -DCMAKE_SYSTEM_VERSION=$(WIN10_SDK)' + - task: CMake@1 + displayName: 'CMake (MSVC Spectre): Build x64 Debug' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: --build out8 -v --config Debug + - task: CMake@1 + displayName: 'CMake (MSVC Spectre): Build x64 Release' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: --build out8 -v --config RelWithDebInfo + - task: CMake@1 + displayName: 'CMake (MSVC Spectre): Config ARM64' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: '-G "$(VS_GENERATOR)" -A ARM64 -B out9 -DENABLE_SPECTRE_MITIGATION=ON -DCMAKE_SYSTEM_VERSION=$(WIN10_SDK)' + - task: CMake@1 + displayName: 'CMake (MSVC Spectre): Build ARM64 Debug' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: --build out9 -v --config Debug + - task: CMake@1 + displayName: 'CMake (MSVC Spectre): Build ARM64 Release' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: --build out9 -v --config RelWithDebInfo + - task: CMake@1 + displayName: 'CMake (NO_WCHAR_T): Config' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: '-G "$(VS_GENERATOR)" -A x64 -B out10 -DNO_WCHAR_T=ON -DCMAKE_SYSTEM_VERSION=$(WIN11_SDK)' + - task: CMake@1 + displayName: 'CMake (NO_WCHAR_T): Build' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: --build out10 -v --config Debug diff --git a/Common/DirectXTK12/build/DirectXTK12-GitHub-CMake.yml b/Common/DirectXTK12/build/DirectXTK12-GitHub-CMake.yml new file mode 100644 index 0000000..066e370 --- /dev/null +++ b/Common/DirectXTK12/build/DirectXTK12-GitHub-CMake.yml @@ -0,0 +1,172 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# +# http://go.microsoft.com/fwlink/?LinkID=615561 + +# Builds the library using CMake. + +schedules: +- cron: "0 6 * * *" + displayName: 'Nightly build' + branches: + include: + - main + +trigger: + branches: + include: + - main + paths: + include: + - CMakeLists.txt +pr: + branches: + include: + - main + paths: + include: + - CMakeLists.txt + +resources: + repositories: + - repository: self + type: git + ref: refs/heads/main + +name: $(Year:yyyy).$(Month).$(DayOfMonth)$(Rev:.r) + +variables: + VS_GENERATOR: 'Visual Studio 16 2019' + WIN10_SDK: '10.0.19041.0' + WIN11_SDK: '10.0.22000.0' + +pool: + vmImage: windows-2019 + +jobs: +- job: CMAKE_BUILD + displayName: CMake using VS Generator + steps: + - checkout: self + clean: true + fetchTags: false + - task: CMake@1 + displayName: 'CMake (MSVC): Config x64' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: '-G "$(VS_GENERATOR)" -A x64 -B out -DCMAKE_SYSTEM_VERSION=$(WIN10_SDK)' + - task: CMake@1 + displayName: 'CMake (MSVC): Build x64 Debug' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: --build out -v --config Debug + - task: CMake@1 + displayName: 'CMake (MSVC): Build x64 Release' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: --build out -v --config RelWithDebInfo + - task: CMake@1 + displayName: 'CMake (MSVC): Config x86' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: '-G "$(VS_GENERATOR)" -A Win32 -B out2 -DCMAKE_SYSTEM_VERSION=$(WIN10_SDK)' + - task: CMake@1 + displayName: 'CMake (MSVC): Build x86 Debug' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: --build out2 -v --config Debug + - task: CMake@1 + displayName: 'CMake (MSVC): Build x86 Release' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: --build out2 -v --config RelWithDebInfo + - task: CMake@1 + displayName: 'CMake (MSVC): Config ARM64' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: '-G "$(VS_GENERATOR)" -A ARM64 -B out3 -DCMAKE_SYSTEM_VERSION=$(WIN10_SDK)' + - task: CMake@1 + displayName: 'CMake (MSVC): Build ARM64 Debug' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: --build out3 -v --config Debug + - task: CMake@1 + displayName: 'CMake (MSVC): Build ARM64 Release' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: --build out3 -v --config RelWithDebInfo + - task: CMake@1 + displayName: 'CMake (UWP): Config x64' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: '-G "$(VS_GENERATOR)" -A x64 -B out4 -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION=10.0' + - task: CMake@1 + displayName: 'CMake (UWP): Build x64' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: --build out4 -v + - task: CMake@1 + displayName: 'CMake (ClangCl): Config x64' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: '-G "$(VS_GENERATOR)" -A x64 -T clangcl -B out6 -DCMAKE_SYSTEM_VERSION=$(WIN10_SDK)' + - task: CMake@1 + displayName: 'CMake (ClangCl): Build x64 Debug' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: --build out6 -v --config Debug + - task: CMake@1 + displayName: 'CMake (ClangCl): Build x64 Release' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: --build out6 -v --config RelWithDebInfo + - task: CMake@1 + displayName: 'CMake (ClangCl): Config ARM64' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: '-G "$(VS_GENERATOR)" -A ARM64 -T clangcl -B out7 -DCMAKE_SYSTEM_VERSION=$(WIN11_SDK)' + - task: CMake@1 + displayName: 'CMake (ClangCl): Build ARM64' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: --build out7 -v --config Debug + - task: CMake@1 + displayName: 'CMake (MSVC Spectre): Config x64' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: '-G "$(VS_GENERATOR)" -A x64 -B out8 -DENABLE_SPECTRE_MITIGATION=ON -DCMAKE_SYSTEM_VERSION=$(WIN10_SDK)' + - task: CMake@1 + displayName: 'CMake (MSVC Spectre): Build x64 Debug' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: --build out8 -v --config Debug + - task: CMake@1 + displayName: 'CMake (MSVC Spectre): Build x64 Release' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: --build out8 -v --config RelWithDebInfo + - task: CMake@1 + displayName: 'CMake (MSVC Spectre): Config ARM64' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: '-G "$(VS_GENERATOR)" -A ARM64 -B out9 -DENABLE_SPECTRE_MITIGATION=ON -DCMAKE_SYSTEM_VERSION=$(WIN10_SDK)' + - task: CMake@1 + displayName: 'CMake (MSVC Spectre): Build ARM64 Debug' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: --build out9 -v --config Debug + - task: CMake@1 + displayName: 'CMake (MSVC Spectre): Build ARM64 Release' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: --build out9 -v --config RelWithDebInfo + - task: CMake@1 + displayName: 'CMake (NO_WCHAR_T): Config' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: '-G "$(VS_GENERATOR)" -A x64 -B out10 -DNO_WCHAR_T=ON -DCMAKE_SYSTEM_VERSION=$(WIN11_SDK)' + - task: CMake@1 + displayName: 'CMake (NO_WCHAR_T): Build' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: --build out10 -v --config Debug diff --git a/Common/DirectXTK12/build/DirectXTK12-GitHub-Dev17.yml b/Common/DirectXTK12/build/DirectXTK12-GitHub-Dev17.yml new file mode 100644 index 0000000..d83e4c9 --- /dev/null +++ b/Common/DirectXTK12/build/DirectXTK12-GitHub-Dev17.yml @@ -0,0 +1,151 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# +# http://go.microsoft.com/fwlink/?LinkID=615561 + +# Builds the library for Windows Desktop and UWP. + +schedules: +- cron: "0 5 * * *" + displayName: 'Nightly build' + branches: + include: + - main + +trigger: none +pr: none + +resources: + repositories: + - repository: self + type: git + ref: refs/heads/main + trigger: + branches: + include: + - main + paths: + exclude: + - README.md + - HISTORY.md + - SECURITY.md + +name: $(Year:yyyy).$(Month).$(DayOfMonth)$(Rev:.r) + +pool: + vmImage: windows-2022 + +jobs: +- job: DESKTOP_BUILD + displayName: 'Win32 Desktop' + timeoutInMinutes: 120 + cancelTimeoutInMinutes: 1 + steps: + - checkout: self + clean: true + fetchTags: false + - task: VSBuild@1 + displayName: Build solution DirectXTK_Desktop_2022_Win10.sln 32dbg + inputs: + solution: DirectXTK_Desktop_2022_Win10.sln + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: x86 + configuration: Debug + msbuildArchitecture: x64 + - task: VSBuild@1 + displayName: Build solution DirectXTK_Desktop_2022_Win10.sln 32rel + inputs: + solution: DirectXTK_Desktop_2022_Win10.sln + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: x86 + configuration: Release + msbuildArchitecture: x64 + - task: VSBuild@1 + displayName: Build solution DirectXTK_Desktop_2022_Win10.sln 64dbg + inputs: + solution: DirectXTK_Desktop_2022_Win10.sln + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: x64 + configuration: Debug + msbuildArchitecture: x64 + - task: VSBuild@1 + displayName: Build solution DirectXTK_Desktop_2022_Win10.sln 64rel + inputs: + solution: DirectXTK_Desktop_2022_Win10.sln + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: x64 + configuration: Release + msbuildArchitecture: x64 + - task: VSBuild@1 + displayName: Build solution DirectXTK_Desktop_2022_Win10.sln arm64dbg + inputs: + solution: DirectXTK_Desktop_2022_Win10.sln + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: ARM64 + configuration: Debug + msbuildArchitecture: x64 + - task: VSBuild@1 + displayName: Build solution DirectXTK_Desktop_2022_Win10.sln arm64rel + inputs: + solution: DirectXTK_Desktop_2022_Win10.sln + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: ARM64 + configuration: Release + msbuildArchitecture: x64 + +- job: UWP_BUILD + displayName: 'Universal Windows Platform (UWP)' + timeoutInMinutes: 120 + cancelTimeoutInMinutes: 1 + steps: + - checkout: self + clean: true + fetchTags: false + - task: VSBuild@1 + displayName: Build solution DirectXTK_Windows10_2022.sln 32dbg + inputs: + solution: DirectXTK_Windows10_2022.sln + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: x86 + configuration: Debug + msbuildArchitecture: x64 + - task: VSBuild@1 + displayName: Build solution DirectXTK_Windows10_2022.sln 32rel + inputs: + solution: DirectXTK_Windows10_2022.sln + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: x86 + configuration: Release + msbuildArchitecture: x64 + - task: VSBuild@1 + displayName: Build solution DirectXTK_Windows10_2022.sln 64dbg + inputs: + solution: DirectXTK_Windows10_2022.sln + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: x64 + configuration: Debug + msbuildArchitecture: x64 + - task: VSBuild@1 + displayName: Build solution DirectXTK_Windows10_2022.sln 64rel + inputs: + solution: DirectXTK_Windows10_2022.sln + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: x64 + configuration: Release + msbuildArchitecture: x64 + - task: VSBuild@1 + displayName: Build solution DirectXTK_Windows10_2022.sln arm64dbg + inputs: + solution: DirectXTK_Windows10_2022.sln + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: ARM64 + configuration: Debug + msbuildArchitecture: x64 + - task: VSBuild@1 + displayName: Build solution DirectXTK_Windows10_2022.sln arm64rel + inputs: + solution: DirectXTK_Windows10_2022.sln + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: ARM64 + configuration: Release + msbuildArchitecture: x64 diff --git a/Common/DirectXTK12/build/DirectXTK12-GitHub-GDK-Dev17.yml b/Common/DirectXTK12/build/DirectXTK12-GitHub-GDK-Dev17.yml new file mode 100644 index 0000000..40c2eb4 --- /dev/null +++ b/Common/DirectXTK12/build/DirectXTK12-GitHub-GDK-Dev17.yml @@ -0,0 +1,141 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# +# http://go.microsoft.com/fwlink/?LinkID=615561 + +# Builds the library using the Microsoft GDK. + +schedules: +- cron: "30 5 * * *" + displayName: 'Nightly build' + branches: + include: + - main + +trigger: none +pr: none + +resources: + repositories: + - repository: self + type: git + ref: refs/heads/main + trigger: none + +name: $(Year:yyyy).$(Month).$(DayOfMonth)$(Rev:.r) + +pool: + vmImage: windows-2022 + +variables: + EXTRACTED_FOLDER: $(ExtractedFolder) + GDK_EDITION: $(GDKEditionNumber) + GDKEnableBWOI: true + URL_FEED: $(ADOFeedURL) + +jobs: +- job: BUILD_GDK + displayName: 'Microsoft Game Development Kit (GDK)' + timeoutInMinutes: 120 + cancelTimeoutInMinutes: 1 + steps: + - checkout: self + clean: true + fetchTags: false + - task: NuGetToolInstaller@1 + displayName: 'Use NuGet' + inputs: + versionSpec: '6.5.x' + - task: NuGetCommand@2 + # We have to use a nuget.config to provide the feed for the 'nuget install' option. + displayName: 'NuGet set package source to ADO feed' + inputs: + command: custom + arguments: sources add -Name xboxgdk -Source $(URL_FEED) -ConfigFile $(Build.SourcesDirectory)\NuGet.config + - task: PowerShell@2 + displayName: 'Set nuget.config to single source' + inputs: + targetType: inline + script: | + $file = '.\NuGet.Config' + $doc = [xml](Get-Content $file) + $newelement = $doc.CreateElement("clear") + $clearadd = $doc.configuration.packageSources.PrependChild($newelement) + $doc.OuterXml | Set-Content $file + + - task: NuGetCommand@2 + displayName: NuGet install PGDK + inputs: + command: custom + arguments: install -prerelease Microsoft.GDK.PC.$(GDK_EDITION) -ExcludeVersion -OutputDirectory $(EXTRACTED_FOLDER)\GDK + - task: NuGetCommand@2 + displayName: NuGet install GDKX + inputs: + command: custom + arguments: install -prerelease Microsoft.GDK.Xbox.$(GDK_EDITION) -ExcludeVersion -OutputDirectory $(EXTRACTED_FOLDER)\GDK + - task: CopyFiles@2 + displayName: Set up Directory.Build.props + inputs: + SourceFolder: build + Contents: 'Directory.Build.props' + TargetFolder: $(Build.SourcesDirectory) + - task: MSBuild@1 + displayName: Setup BWOI VCTargets + inputs: + solution: build/SetupBWOI.targets + msbuildVersion: 17.0 + msbuildArchitecture: x64 + - task: VSBuild@1 + displayName: Build solution DirectXTK_GDK_2022 pcdbg + continueOnError: True + inputs: + solution: DirectXTK_GDK_2022.sln + vsVersion: 17.0 + platform: Gaming.Desktop.x64 + configuration: Debug + msbuildArchitecture: x64 + - task: VSBuild@1 + displayName: Build solution DirectXTK_GDK_2022 pcrel + continueOnError: True + inputs: + solution: DirectXTK_GDK_2022.sln + vsVersion: 17.0 + platform: Gaming.Desktop.x64 + configuration: Release + msbuildArchitecture: x64 + - task: VSBuild@1 + displayName: Build solution DirectXTK_GDK_2022 xbdbg + continueOnError: True + inputs: + solution: DirectXTK_GDK_2022.sln + vsVersion: 17.0 + platform: Gaming.Xbox.XboxOne.x64 + configuration: Debug + msbuildArchitecture: x64 + - task: VSBuild@1 + displayName: Build solution DirectXTK_GDK_2022 xbrel + continueOnError: True + inputs: + solution: DirectXTK_GDK_2022.sln + vsVersion: 17.0 + platform: Gaming.Xbox.XboxOne.x64 + configuration: Release + msbuildArchitecture: x64 + - task: VSBuild@1 + displayName: Build solution DirectXTK_GDK_2022 scardbg + continueOnError: True + inputs: + solution: DirectXTK_GDK_2022.sln + vsVersion: 17.0 + platform: Gaming.Xbox.Scarlett.x64 + configuration: Debug + msbuildArchitecture: x64 + - task: VSBuild@1 + displayName: Build solution DirectXTK_GDK_2022 scarrel + continueOnError: True + inputs: + solution: DirectXTK_GDK_2022.sln + vsVersion: 17.0 + platform: Gaming.Xbox.Scarlett.x64 + configuration: Release + msbuildArchitecture: x64 diff --git a/Common/DirectXTK12/build/DirectXTK12-GitHub-GDK.yml b/Common/DirectXTK12/build/DirectXTK12-GitHub-GDK.yml new file mode 100644 index 0000000..77c3509 --- /dev/null +++ b/Common/DirectXTK12/build/DirectXTK12-GitHub-GDK.yml @@ -0,0 +1,326 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# +# http://go.microsoft.com/fwlink/?LinkID=615561 + +# Builds the library and test suite using the Microsoft GDK. + +# NOTE: We use x64 MSBuild for the GDK as the NuGets don't include 32-bit support to avoid cross-arch dependencies. + +schedules: +- cron: "30 5 * * *" + displayName: 'Nightly build' + branches: + include: + - main + +trigger: + branches: + include: + - main + paths: + exclude: + - README.md + - HISTORY.md + - SECURITY.md + - LICENSE +pr: + branches: + include: + - main + paths: + exclude: + - README.md + - HISTORY.md + - SECURITY.md + - LICENSE + drafts: false + +resources: + repositories: + - repository: self + type: git + ref: refs/heads/main + +name: $(Year:yyyy).$(Month).$(DayOfMonth)$(Rev:.r) + +pool: + vmImage: windows-2019 + +variables: + EXTRACTED_FOLDER: $(ExtractedFolder) + GDK_EDITION: $(GDKEditionNumber) + GDKEnableBWOI: true + GITHUB_PAT: $(GITHUBPUBLICTOKEN) + URL_FEED: $(ADOFeedURL) + +jobs: +- job: BUILD_GDK + displayName: 'Microsoft Game Development Kit (GDK)' + timeoutInMinutes: 120 + cancelTimeoutInMinutes: 1 + steps: + - checkout: self + clean: true + fetchTags: false + - task: NuGetToolInstaller@1 + displayName: 'Use NuGet' + inputs: + versionSpec: '6.5.x' + - task: NuGetCommand@2 + # We have to use a nuget.config to provide the feed for the 'nuget install' option. + displayName: 'NuGet set package source to ADO feed' + inputs: + command: custom + arguments: sources add -Name xboxgdk -Source $(URL_FEED) -ConfigFile $(Build.SourcesDirectory)\NuGet.config + - task: PowerShell@2 + displayName: 'Set nuget.config to single source' + inputs: + targetType: inline + script: | + $file = '.\NuGet.Config' + $doc = [xml](Get-Content $file) + $newelement = $doc.CreateElement("clear") + $clearadd = $doc.configuration.packageSources.PrependChild($newelement) + $doc.OuterXml | Set-Content $file + + - task: NuGetCommand@2 + displayName: NuGet install PGDK + inputs: + command: custom + arguments: install -prerelease Microsoft.GDK.PC.$(GDK_EDITION) -ExcludeVersion -OutputDirectory $(EXTRACTED_FOLDER)\GDK + - task: NuGetCommand@2 + displayName: NuGet install GDKX + inputs: + command: custom + arguments: install -prerelease Microsoft.GDK.Xbox.$(GDK_EDITION) -ExcludeVersion -OutputDirectory $(EXTRACTED_FOLDER)\GDK + - task: CopyFiles@2 + displayName: Set up Directory.Build.props + inputs: + SourceFolder: build + Contents: 'Directory.Build.props' + TargetFolder: $(Build.SourcesDirectory) + - task: MSBuild@1 + displayName: Setup BWOI VCTargets + inputs: + solution: build/SetupBWOI.targets + msbuildVersion: 16.0 + msbuildArchitecture: x64 + - task: VSBuild@1 + displayName: Build solution DirectXTK_GDK_2019 pcdbg + continueOnError: True + inputs: + solution: DirectXTK_GDK_2019.sln + vsVersion: 16.0 + platform: Gaming.Desktop.x64 + configuration: Debug + msbuildArchitecture: x64 + - task: VSBuild@1 + displayName: Build solution DirectXTK_GDK_2019 pcrel + continueOnError: True + inputs: + solution: DirectXTK_GDK_2019.sln + vsVersion: 16.0 + platform: Gaming.Desktop.x64 + configuration: Release + msbuildArchitecture: x64 + - task: VSBuild@1 + displayName: Build solution DirectXTK_GDK_2019 xbdbg + continueOnError: True + inputs: + solution: DirectXTK_GDK_2019.sln + vsVersion: 16.0 + platform: Gaming.Xbox.XboxOne.x64 + configuration: Debug + msbuildArchitecture: x64 + - task: VSBuild@1 + displayName: Build solution DirectXTK_GDK_2019 xbrel + continueOnError: True + inputs: + solution: DirectXTK_GDK_2019.sln + vsVersion: 16.0 + platform: Gaming.Xbox.XboxOne.x64 + configuration: Release + msbuildArchitecture: x64 + - task: VSBuild@1 + displayName: Build solution DirectXTK_GDK_2019 scardbg + continueOnError: True + inputs: + solution: DirectXTK_GDK_2019.sln + vsVersion: 16.0 + platform: Gaming.Xbox.Scarlett.x64 + configuration: Debug + msbuildArchitecture: x64 + - task: VSBuild@1 + displayName: Build solution DirectXTK_GDK_2019 scarrel + continueOnError: True + inputs: + solution: DirectXTK_GDK_2019.sln + vsVersion: 16.0 + platform: Gaming.Xbox.Scarlett.x64 + configuration: Release + msbuildArchitecture: x64 + +- job: BUILD_TEST_XBOXONE + displayName: 'Test suite for Xbox One' + timeoutInMinutes: 120 + cancelTimeoutInMinutes: 1 + steps: + - checkout: self + clean: true + fetchTags: false + - task: CmdLine@2 + displayName: Fetch Tests + inputs: + script: git clone --no-tags --quiet https://%GITHUB_PAT%@github.com/walbourn/directxtk12test.git Tests + workingDirectory: $(Build.SourcesDirectory) + failOnStderr: true + - task: NuGetToolInstaller@1 + displayName: 'Use NuGet' + inputs: + versionSpec: '6.5.x' + - task: NuGetCommand@2 + displayName: 'NuGet set package source to ADO feed' + inputs: + command: custom + arguments: sources add -Name xboxgdk -Source $(URL_FEED) -ConfigFile $(Build.SourcesDirectory)\NuGet.config + - task: PowerShell@2 + displayName: 'Set nuget.config to single source' + inputs: + targetType: inline + script: | + $file = '.\NuGet.Config' + $doc = [xml](Get-Content $file) + $newelement = $doc.CreateElement("clear") + $clearadd = $doc.configuration.packageSources.PrependChild($newelement) + $doc.OuterXml | Set-Content $file + + - task: NuGetCommand@2 + displayName: NuGet install PGDK + inputs: + command: custom + arguments: install -prerelease Microsoft.GDK.PC.$(GDK_EDITION) -ExcludeVersion -OutputDirectory $(EXTRACTED_FOLDER)\GDK + - task: NuGetCommand@2 + displayName: NuGet install GDKX + inputs: + command: custom + arguments: install -prerelease Microsoft.GDK.Xbox.$(GDK_EDITION) -ExcludeVersion -OutputDirectory $(EXTRACTED_FOLDER)\GDK + - task: NuGetCommand@2 + displayName: NuGet restore + inputs: + solution: Tests\D3D12Test\D3D12Test_GDK_2019.sln + selectOrConfig: config + nugetConfigPath: NuGet.Config + packagesDirectory: $(Build.SourcesDirectory)\Tests\packages + - task: CopyFiles@2 + displayName: Set up Directory.Build.props + inputs: + SourceFolder: build + Contents: 'Directory.Build.props' + TargetFolder: $(Build.SourcesDirectory) + - task: MSBuild@1 + displayName: Setup BWOI VCTargets + inputs: + solution: build/SetupBWOI.targets + msbuildVersion: 16.0 + msbuildArchitecture: x64 + - task: VSBuild@1 + displayName: Build Tests xbdbg + continueOnError: True + inputs: + solution: '**\*GDK_2019*.sln' + vsVersion: 16.0 + platform: Gaming.Xbox.XboxOne.x64 + configuration: Debug + msbuildArchitecture: x64 + - task: VSBuild@1 + displayName: Build Tests xbrel + continueOnError: True + inputs: + solution: '**\*GDK_2019*.sln' + vsVersion: 16.0 + platform: Gaming.Xbox.XboxOne.x64 + configuration: Release + msbuildArchitecture: x64 + +- job: BUILD_TEST_SCARLETT + displayName: 'Test suite for Xbox Series X|S' + timeoutInMinutes: 120 + cancelTimeoutInMinutes: 1 + steps: + - checkout: self + clean: true + fetchTags: false + - task: CmdLine@2 + displayName: Fetch Tests + inputs: + script: git clone --quiet https://%GITHUB_PAT%@github.com/walbourn/directxtk12test.git Tests + workingDirectory: $(Build.SourcesDirectory) + failOnStderr: true + - task: NuGetToolInstaller@1 + displayName: 'Use NuGet' + inputs: + versionSpec: '6.5.x' + - task: NuGetCommand@2 + displayName: 'NuGet set package source to ADO feed' + inputs: + command: custom + arguments: sources add -Name xboxgdk -Source $(URL_FEED) -ConfigFile $(Build.SourcesDirectory)\NuGet.config + - task: PowerShell@2 + displayName: 'Set nuget.config to single source' + inputs: + targetType: inline + script: | + $file = '.\NuGet.Config' + $doc = [xml](Get-Content $file) + $newelement = $doc.CreateElement("clear") + $clearadd = $doc.configuration.packageSources.PrependChild($newelement) + $doc.OuterXml | Set-Content $file + + - task: NuGetCommand@2 + displayName: NuGet install PGDK + inputs: + command: custom + arguments: install -prerelease Microsoft.GDK.PC.$(GDK_EDITION) -ExcludeVersion -OutputDirectory $(EXTRACTED_FOLDER)\GDK + - task: NuGetCommand@2 + displayName: NuGet install GDKX + inputs: + command: custom + arguments: install -prerelease Microsoft.GDK.Xbox.$(GDK_EDITION) -ExcludeVersion -OutputDirectory $(EXTRACTED_FOLDER)\GDK + - task: NuGetCommand@2 + displayName: NuGet restore + inputs: + solution: Tests\D3D12Test\D3D12Test_GDK_2019.sln + selectOrConfig: config + nugetConfigPath: NuGet.Config + packagesDirectory: $(Build.SourcesDirectory)\Tests\packages + - task: CopyFiles@2 + displayName: Set up Directory.Build.props + inputs: + SourceFolder: build + Contents: 'Directory.Build.props' + TargetFolder: $(Build.SourcesDirectory) + - task: MSBuild@1 + displayName: Setup BWOI VCTargets + inputs: + solution: build/SetupBWOI.targets + msbuildVersion: 16.0 + msbuildArchitecture: x64 + - task: VSBuild@1 + displayName: Build Tests scardbg + continueOnError: True + inputs: + solution: '**\*GDK_2019*.sln' + vsVersion: 16.0 + platform: Gaming.Xbox.Scarlett.x64 + configuration: Debug + msbuildArchitecture: x64 + - task: VSBuild@1 + displayName: Build Tests scarrel + continueOnError: True + inputs: + solution: '**\*GDK_2019*.sln' + vsVersion: 16.0 + platform: Gaming.Xbox.Scarlett.x64 + configuration: Release + msbuildArchitecture: x64 diff --git a/Common/DirectXTK12/build/DirectXTK12-GitHub-MinGW.yml b/Common/DirectXTK12/build/DirectXTK12-GitHub-MinGW.yml new file mode 100644 index 0000000..3e4eba6 --- /dev/null +++ b/Common/DirectXTK12/build/DirectXTK12-GitHub-MinGW.yml @@ -0,0 +1,204 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# +# http://go.microsoft.com/fwlink/?LinkID=615561 + +# Builds the library and test suite using the MinGW compiler. + +schedules: +- cron: "0 6 * * *" + displayName: 'Nightly build' + branches: + include: + - main + +trigger: + branches: + include: + - main + paths: + exclude: + - README.md + - HISTORY.md + - SECURITY.md + - LICENSE +pr: + branches: + include: + - main + paths: + exclude: + - README.md + - HISTORY.md + - SECURITY.md + - LICENSE + drafts: false + +resources: + repositories: + - repository: self + type: git + ref: refs/heads/main + +name: $(Year:yyyy).$(Month).$(DayOfMonth)$(Rev:.r) + +pool: + vmImage: windows-2022 + +variables: + DIRECTX_DXC_DIR: '$(VCPKG_ROOT)/installed/x64-mingw-static/tools/directx-dxc' + VCPKG_CMAKE_DIR: '$(VCPKG_ROOT)/scripts/buildsystems/vcpkg.cmake' + GITHUB_PAT: $(GITHUBPUBLICTOKEN) + WIN11_SDK: '10.0.22000.0' + URL_MINGW32: https://github.com/brechtsanders/winlibs_mingw/releases/download/12.2.0-14.0.6-10.0.0-ucrt-r2/winlibs-i686-posix-dwarf-gcc-12.2.0-llvm-14.0.6-mingw-w64ucrt-10.0.0-r2.zip + HASH_MINGW32: 'fcd1e11b896190da01c83d5b5fb0d37b7c61585e53446c2dab0009debc3915e757213882c35e35396329338de6f0222ba012e23a5af86932db45186a225d1272' + +jobs: +- job: MINGW32_BUILD + displayName: 'Minimalist GNU for Windows (MinGW32) BUILD_TESTING=ON' + steps: + - checkout: self + clean: true + fetchTags: false + - task: CmdLine@2 + displayName: Fetch VCPKG + inputs: + script: git clone --quiet --no-tags https://%GITHUB_PAT%@github.com/microsoft/vcpkg.git + workingDirectory: $(Build.SourcesDirectory) + - task: CmdLine@2 + displayName: Fetch Tests + inputs: + script: git clone --quiet --no-tags https://%GITHUB_PAT%@github.com/walbourn/directxtk12test.git Tests + - task: CmdLine@2 + # Note we have to use x64-mingw-static for the host to use the directx-dxc port. + displayName: VCPKG Bootstrap + inputs: + script: | + call bootstrap-vcpkg.bat + echo ##vso[task.setvariable variable=VCPKG_DEFAULT_TRIPLET;]x86-mingw-static + echo ##vso[task.setvariable variable=VCPKG_DEFAULT_HOST_TRIPLET;]x64-mingw-static + + workingDirectory: $(Build.SourcesDirectory)\vcpkg + - task: PowerShell@2 + displayName: Install MinGW32 and setup for Windows 11 SDK + inputs: + targetType: inline + script: | + $ProgressPreference = 'SilentlyContinue' + Write-Host "Downloading winlibs..." + Invoke-WebRequest -Uri "$(URL_MINGW32)" -OutFile "gw32.zip" + Write-Host "Downloaded." + $fileHash = Get-FileHash -Algorithm SHA512 gw32.zip | ForEach { $_.Hash} | Out-String + $filehash = $fileHash.Trim() + Write-Host "##[debug]SHA512: " $fileHash + if ($fileHash -ne '$(HASH_MINGW32)') { + Write-Error -Message "##[error]Computed hash does not match!" -ErrorAction Stop + } + Write-Host "Extracting winlibs..." + Expand-Archive -LiteralPath 'gw32.zip' + Write-Host "Extracted." + Write-Host "Added to path: $env:BUILD_SOURCESDIRECTORY\gw32\mingw32\bin" + Write-Host "##vso[task.prependpath]$env:BUILD_SOURCESDIRECTORY\gw32\mingw32\bin" + $sdkroot = Get-ItemProperty -Path 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows Kits\Installed Roots' | Select-Object -ExpandProperty KitsRoot10 + $windows11sdk = "{0}bin\$(WIN11_SDK)\" -f $sdkroot + if (Test-Path "$windows11sdk") { + Write-Host "##vso[task.setvariable variable=WindowsSdkVerBinPath;]$windows11sdk" + } + else { + Write-Error -Message "##[error]Can't find Windows SDK ($(WIN11_SDK))" -ErrorAction Stop + } + + workingDirectory: $(Build.SourcesDirectory) + - task: CmdLine@2 + displayName: GCC version + inputs: + script: g++ --version + - task: CmdLine@2 + displayName: VCPKG install headers + inputs: + script: | + call vcpkg install directxmath + @if ERRORLEVEL 1 goto error + call vcpkg install directx-headers + @if ERRORLEVEL 1 goto error + call vcpkg install xaudio2redist + @if ERRORLEVEL 1 goto error + call vcpkg install directx-dxc:x64-mingw-static + @if ERRORLEVEL 1 goto error + :finish + @echo --- VCPKG COMPLETE --- + exit /b 0 + :error + @echo --- ERROR: VCPKG FAILED --- + exit /b 1 + + workingDirectory: $(Build.SourcesDirectory)\vcpkg + - task: CMake@1 + displayName: CMake (MinGW32) + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: -B out -DCMAKE_BUILD_TYPE="Debug" -DDIRECTX_ARCH=x86 -DCMAKE_TOOLCHAIN_FILE="$(VCPKG_CMAKE_DIR)" -DCMAKE_CXX_COMPILER="g++.exe" -G "MinGW Makefiles" -DVCPKG_TARGET_TRIPLET=x86-mingw-static -DVCPKG_HOST_TRIPLET=x64-mingw-static -DBUILD_XAUDIO_REDIST=ON -DDIRECTX_DXC_PATH="$(DIRECTX_DXC_DIR)" + - task: CMake@1 + displayName: CMake (MinGW32) Build + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: --build out + +- job: MINGW64_BUILD + displayName: 'Minimalist GNU for Windows (MinGW-W64) BUILD_TESTING=ON' + steps: + - checkout: self + clean: true + fetchTags: false + - task: CmdLine@2 + displayName: Fetch VCPKG + inputs: + script: git clone --quiet --no-tags https://%GITHUB_PAT%@github.com/microsoft/vcpkg.git + workingDirectory: $(Build.SourcesDirectory) + - task: CmdLine@2 + displayName: Fetch Tests + inputs: + script: git clone --quiet --no-tags https://%GITHUB_PAT%@github.com/walbourn/directxtk12test.git Tests + - task: CmdLine@2 + displayName: VCPKG Bootstrap + inputs: + script: | + call bootstrap-vcpkg.bat + echo ##vso[task.setvariable variable=VCPKG_DEFAULT_TRIPLET;]x64-mingw-static + echo ##vso[task.setvariable variable=VCPKG_DEFAULT_HOST_TRIPLET;]x64-mingw-static + + workingDirectory: $(Build.SourcesDirectory)\vcpkg + - task: CmdLine@2 + displayName: GCC version + inputs: + script: g++ --version + - task: CmdLine@2 + displayName: VCPKG install headers + inputs: + script: | + call vcpkg install directxmath + @if ERRORLEVEL 1 goto error + call vcpkg install directx-headers + @if ERRORLEVEL 1 goto error + call vcpkg install xaudio2redist + @if ERRORLEVEL 1 goto error + call vcpkg install directx-dxc + @if ERRORLEVEL 1 goto error + :finish + @echo --- VCPKG COMPLETE --- + exit /b 0 + :error + @echo --- ERROR: VCPKG FAILED --- + exit /b 1 + + workingDirectory: $(Build.SourcesDirectory)\vcpkg + - task: CMake@1 + displayName: CMake (MinGW-W64) + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: -B out -DCMAKE_BUILD_TYPE="Debug" -DDIRECTX_ARCH=x64 -DCMAKE_TOOLCHAIN_FILE="$(VCPKG_CMAKE_DIR)" -DCMAKE_CXX_COMPILER="g++.exe" -G "MinGW Makefiles" -DVCPKG_TARGET_TRIPLET=x64-mingw-static -DBUILD_XAUDIO_REDIST=ON -DDIRECTX_DXC_PATH="$(DIRECTX_DXC_DIR)" + - task: CMake@1 + displayName: CMake (MinGW-W64) Build + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: --build out diff --git a/Common/DirectXTK12/build/DirectXTK12-GitHub-SDK-prerelease.yml b/Common/DirectXTK12/build/DirectXTK12-GitHub-SDK-prerelease.yml new file mode 100644 index 0000000..4fca771 --- /dev/null +++ b/Common/DirectXTK12/build/DirectXTK12-GitHub-SDK-prerelease.yml @@ -0,0 +1,253 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# +# http://go.microsoft.com/fwlink/?LinkID=615561 + +# Builds the library using the latest prerelease of the Windows SDK from nuget.org. + +schedules: +- cron: "0 5 * * 6" + displayName: 'Saturday night build' + branches: + include: + - main + always: true + +trigger: none +pr: none + +resources: + repositories: + - repository: self + type: git + ref: refs/heads/main + trigger: none + +name: $(Year:yyyy).$(Month).$(DayOfMonth)$(Rev:.r) + +variables: + EXTRACTED_FOLDER: $(ExtractedFolder) + WSDKEnableBWOI: true + URL_FEED: $(ADOFeedURL) + +pool: + vmImage: windows-2019 + +jobs: +- job: DESKTOP_BUILD + displayName: 'Win32 Desktop' + timeoutInMinutes: 120 + cancelTimeoutInMinutes: 1 + steps: + - checkout: self + clean: true + fetchTags: false + - task: NuGetToolInstaller@1 + displayName: 'Use NuGet' + inputs: + versionSpec: '6.5.x' + - task: NuGetCommand@2 + # We have to use a nuget.config to provide the feed for the 'nuget install' option. + displayName: 'NuGet set package source to ADO feed' + inputs: + command: custom + arguments: sources add -Name xboxgdk -Source $(URL_FEED) -ConfigFile $(Build.SourcesDirectory)\NuGet.config + - task: PowerShell@2 + displayName: 'Set nuget.config to single source' + inputs: + targetType: inline + script: | + $file = '.\NuGet.Config' + $doc = [xml](Get-Content $file) + $newelement = $doc.CreateElement("clear") + $clearadd = $doc.configuration.packageSources.PrependChild($newelement) + $doc.OuterXml | Set-Content $file + + - task: NuGetCommand@2 + displayName: NuGet Install WSDK x64 + inputs: + command: custom + arguments: install -prerelease Microsoft.Windows.SDK.CPP.x64 -ExcludeVersion -OutputDirectory $(EXTRACTED_FOLDER)\SDK + - task: NuGetCommand@2 + displayName: NuGet Install WSDK x86 + inputs: + command: custom + arguments: install -prerelease Microsoft.Windows.SDK.CPP.x86 -ExcludeVersion -OutputDirectory $(EXTRACTED_FOLDER)\SDK + - task: NuGetCommand@2 + displayName: NuGet Install WSDK arm64 + inputs: + command: custom + arguments: install -prerelease Microsoft.Windows.SDK.CPP.arm64 -ExcludeVersion -OutputDirectory $(EXTRACTED_FOLDER)\SDK + - task: CopyFiles@2 + displayName: Set up Directory.Build.props + inputs: + SourceFolder: build + Contents: 'Directory.Build.props' + TargetFolder: $(Build.SourcesDirectory) + - task: VSBuild@1 + displayName: Build solution DirectXTK_Desktop_2019_Win10.sln 64rel + inputs: + solution: DirectXTK_Desktop_2019_Win10.sln + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: x64 + configuration: Release + - task: VSBuild@1 + displayName: Build solution DirectXTK_Desktop_2019_Win10.sln 32dbg + inputs: + solution: DirectXTK_Desktop_2019_Win10.sln + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: x86 + configuration: Debug + - task: VSBuild@1 + displayName: Build solution DirectXTK_Desktop_2019_Win10.sln 32rel + inputs: + solution: DirectXTK_Desktop_2019_Win10.sln + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: x86 + configuration: Release + - task: VSBuild@1 + displayName: Build solution DirectXTK_Desktop_2019_Win10.sln 64dbg + inputs: + solution: DirectXTK_Desktop_2019_Win10.sln + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: x64 + configuration: Debug + - task: VSBuild@1 + displayName: Build solution DirectXTK_Desktop_2019_Win10.sln arm64dbg + inputs: + solution: DirectXTK_Desktop_2019_Win10.sln + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: ARM64 + configuration: Debug + - task: VSBuild@1 + displayName: Build solution DirectXTK_Desktop_2019_Win10.sln arm64rel + inputs: + solution: DirectXTK_Desktop_2019_Win10.sln + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: ARM64 + configuration: Release + +- job: UWP_BUILD + displayName: 'Universal Windows Platform (UWP)' + timeoutInMinutes: 120 + cancelTimeoutInMinutes: 1 + steps: + - checkout: self + clean: true + fetchTags: false + - task: NuGetToolInstaller@1 + displayName: 'Use NuGet' + inputs: + versionSpec: '6.5.x' + - task: NuGetCommand@2 + displayName: 'NuGet set package source to ADO feed' + inputs: + command: custom + arguments: sources add -Name xboxgdk -Source $(URL_FEED) -ConfigFile $(Build.SourcesDirectory)\NuGet.config + - task: PowerShell@2 + displayName: 'Set nuget.config to single source' + inputs: + targetType: inline + script: | + $file = '.\NuGet.Config' + $doc = [xml](Get-Content $file) + $newelement = $doc.CreateElement("clear") + $clearadd = $doc.configuration.packageSources.PrependChild($newelement) + $doc.OuterXml | Set-Content $file + + - task: NuGetCommand@2 + displayName: NuGet Install WSDK x64 + inputs: + command: custom + arguments: install -prerelease Microsoft.Windows.SDK.CPP.x64 -ExcludeVersion -OutputDirectory $(EXTRACTED_FOLDER)\SDK + - task: NuGetCommand@2 + displayName: NuGet Install WSDK x86 + inputs: + command: custom + arguments: install -prerelease Microsoft.Windows.SDK.CPP.x86 -ExcludeVersion -OutputDirectory $(ExtractedFolder)\SDK + - task: CopyFiles@2 + displayName: Set up Directory.Build.props + inputs: + SourceFolder: build + Contents: 'Directory.Build.props' + TargetFolder: $(Build.SourcesDirectory) + - task: VSBuild@1 + displayName: Build solution DirectXTK_Windows10_2019.sln 32dbg + inputs: + solution: DirectXTK_Windows10_2019.sln + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: x86 + configuration: Debug + - task: VSBuild@1 + displayName: Build solution DirectXTK_Windows10_2019.sln 32rel + inputs: + solution: DirectXTK_Windows10_2019.sln + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: x86 + configuration: Release + - task: VSBuild@1 + displayName: Build solution DirectXTK_Windows10_2019.sln 64dbg + inputs: + solution: DirectXTK_Windows10_2019.sln + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: x64 + configuration: Debug + - task: VSBuild@1 + displayName: Build solution DirectXTK_Windows10_2019.sln 64rel + inputs: + solution: DirectXTK_Windows10_2019.sln + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: x64 + configuration: Release + +- job: UWP_BUILD_ARM_ARM64 + displayName: 'Universal Windows Platform (UWP) for ARM/ARM64' + steps: + - checkout: self + clean: true + fetchTags: false + - task: NuGetToolInstaller@1 + displayName: 'Use NuGet' + inputs: + versionSpec: '6.5.x' + - task: NuGetCommand@2 + displayName: 'NuGet set package source to ADO feed' + inputs: + command: custom + arguments: sources add -Name xboxgdk -Source $(URL_FEED) -ConfigFile $(Build.SourcesDirectory)\NuGet.config + - task: PowerShell@2 + displayName: 'Set nuget.config to single source' + inputs: + targetType: inline + script: | + $file = '.\NuGet.Config' + $doc = [xml](Get-Content $file) + $newelement = $doc.CreateElement("clear") + $clearadd = $doc.configuration.packageSources.PrependChild($newelement) + $doc.OuterXml | Set-Content $file + + - task: NuGetCommand@2 + displayName: NuGet Install WSDK arm64 + inputs: + command: custom + arguments: install -prerelease Microsoft.Windows.SDK.CPP.arm64 -ExcludeVersion -OutputDirectory $(EXTRACTED_FOLDER)\SDK + - task: CopyFiles@2 + displayName: Set up Directory.Build.props + inputs: + SourceFolder: build + Contents: 'Directory.Build.props' + TargetFolder: $(Build.SourcesDirectory) + - task: VSBuild@1 + displayName: Build solution DirectXTK_Windows10_2019.sln arm64dbg + inputs: + solution: DirectXTK_Windows10_2019.sln + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: ARM64 + configuration: Debug + - task: VSBuild@1 + displayName: Build solution DirectXTK_Windows10_2019.sln arm64rel + inputs: + solution: DirectXTK_Windows10_2019.sln + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: ARM64 + configuration: Release diff --git a/Common/DirectXTK12/build/DirectXTK12-GitHub-SDK-release.yml b/Common/DirectXTK12/build/DirectXTK12-GitHub-SDK-release.yml new file mode 100644 index 0000000..1a19681 --- /dev/null +++ b/Common/DirectXTK12/build/DirectXTK12-GitHub-SDK-release.yml @@ -0,0 +1,253 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# +# http://go.microsoft.com/fwlink/?LinkID=615561 + +# Builds the library using the latest release of the Windows SDK from nuget.org. + +schedules: +- cron: "0 5 * * 0" + displayName: 'Sunday night build' + branches: + include: + - main + always: true + +trigger: none +pr: none + +resources: + repositories: + - repository: self + type: git + ref: refs/heads/main + trigger: none + +name: $(Year:yyyy).$(Month).$(DayOfMonth)$(Rev:.r) + +variables: + EXTRACTED_FOLDER: $(ExtractedFolder) + WSDKEnableBWOI: true + URL_FEED: $(ADOFeedURL) + +pool: + vmImage: windows-2019 + +jobs: +- job: DESKTOP_BUILD + displayName: 'Win32 Desktop' + timeoutInMinutes: 120 + cancelTimeoutInMinutes: 1 + steps: + - checkout: self + clean: true + fetchTags: false + - task: NuGetToolInstaller@1 + displayName: 'Use NuGet' + inputs: + versionSpec: '6.5.x' + - task: NuGetCommand@2 + # We have to use a nuget.config to provide the feed for the 'nuget install' option. + displayName: 'NuGet set package source to ADO feed' + inputs: + command: custom + arguments: sources add -Name xboxgdk -Source $(URL_FEED) -ConfigFile $(Build.SourcesDirectory)\NuGet.config + - task: PowerShell@2 + displayName: 'Set nuget.config to single source' + inputs: + targetType: inline + script: | + $file = '.\NuGet.Config' + $doc = [xml](Get-Content $file) + $newelement = $doc.CreateElement("clear") + $clearadd = $doc.configuration.packageSources.PrependChild($newelement) + $doc.OuterXml | Set-Content $file + + - task: NuGetCommand@2 + displayName: NuGet Install WSDK x64 + inputs: + command: custom + arguments: install Microsoft.Windows.SDK.CPP.x64 -ExcludeVersion -OutputDirectory $(EXTRACTED_FOLDER)\SDK + - task: NuGetCommand@2 + displayName: NuGet Install WSDK x86 + inputs: + command: custom + arguments: install Microsoft.Windows.SDK.CPP.x86 -ExcludeVersion -OutputDirectory $(EXTRACTED_FOLDER)\SDK + - task: NuGetCommand@2 + displayName: NuGet Install WSDK arm64 + inputs: + command: custom + arguments: install Microsoft.Windows.SDK.CPP.arm64 -ExcludeVersion -OutputDirectory $(EXTRACTED_FOLDER)\SDK + - task: CopyFiles@2 + displayName: Set up Directory.Build.props + inputs: + SourceFolder: build + Contents: 'Directory.Build.props' + TargetFolder: $(Build.SourcesDirectory) + - task: VSBuild@1 + displayName: Build solution DirectXTK_Desktop_2019_Win10.sln 64rel + inputs: + solution: DirectXTK_Desktop_2019_Win10.sln + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: x64 + configuration: Release + - task: VSBuild@1 + displayName: Build solution DirectXTK_Desktop_2019_Win10.sln 32dbg + inputs: + solution: DirectXTK_Desktop_2019_Win10.sln + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: x86 + configuration: Debug + - task: VSBuild@1 + displayName: Build solution DirectXTK_Desktop_2019_Win10.sln 32rel + inputs: + solution: DirectXTK_Desktop_2019_Win10.sln + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: x86 + configuration: Release + - task: VSBuild@1 + displayName: Build solution DirectXTK_Desktop_2019_Win10.sln 64dbg + inputs: + solution: DirectXTK_Desktop_2019_Win10.sln + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: x64 + configuration: Debug + - task: VSBuild@1 + displayName: Build solution DirectXTK_Desktop_2019_Win10.sln arm64dbg + inputs: + solution: DirectXTK_Desktop_2019_Win10.sln + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: ARM64 + configuration: Debug + - task: VSBuild@1 + displayName: Build solution DirectXTK_Desktop_2019_Win10.sln arm64rel + inputs: + solution: DirectXTK_Desktop_2019_Win10.sln + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: ARM64 + configuration: Release + +- job: UWP_BUILD + displayName: 'Universal Windows Platform (UWP)' + timeoutInMinutes: 120 + cancelTimeoutInMinutes: 1 + steps: + - checkout: self + clean: true + fetchTags: false + - task: NuGetToolInstaller@1 + displayName: 'Use NuGet' + inputs: + versionSpec: '6.5.x' + - task: NuGetCommand@2 + displayName: 'NuGet set package source to ADO feed' + inputs: + command: custom + arguments: sources add -Name xboxgdk -Source $(URL_FEED) -ConfigFile $(Build.SourcesDirectory)\NuGet.config + - task: PowerShell@2 + displayName: 'Set nuget.config to single source' + inputs: + targetType: inline + script: | + $file = '.\NuGet.Config' + $doc = [xml](Get-Content $file) + $newelement = $doc.CreateElement("clear") + $clearadd = $doc.configuration.packageSources.PrependChild($newelement) + $doc.OuterXml | Set-Content $file + + - task: NuGetCommand@2 + displayName: NuGet Install WSDK x64 + inputs: + command: custom + arguments: install Microsoft.Windows.SDK.CPP.x64 -ExcludeVersion -OutputDirectory $(EXTRACTED_FOLDER)\SDK + - task: NuGetCommand@2 + displayName: NuGet Install WSDK x86 + inputs: + command: custom + arguments: install Microsoft.Windows.SDK.CPP.x86 -ExcludeVersion -OutputDirectory $(EXTRACTED_FOLDER)\SDK + - task: CopyFiles@2 + displayName: Set up Directory.Build.props + inputs: + SourceFolder: build + Contents: 'Directory.Build.props' + TargetFolder: $(Build.SourcesDirectory) + - task: VSBuild@1 + displayName: Build solution DirectXTK_Windows10_2019.sln 32dbg + inputs: + solution: DirectXTK_Windows10_2019.sln + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: x86 + configuration: Debug + - task: VSBuild@1 + displayName: Build solution DirectXTK_Windows10_2019.sln 32rel + inputs: + solution: DirectXTK_Windows10_2019.sln + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: x86 + configuration: Release + - task: VSBuild@1 + displayName: Build solution DirectXTK_Windows10_2019.sln 64dbg + inputs: + solution: DirectXTK_Windows10_2019.sln + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: x64 + configuration: Debug + - task: VSBuild@1 + displayName: Build solution DirectXTK_Windows10_2019.sln 64rel + inputs: + solution: DirectXTK_Windows10_2019.sln + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: x64 + configuration: Release + +- job: UWP_BUILD_ARM_ARM64 + displayName: 'Universal Windows Platform (UWP) for ARM/ARM64' + steps: + - checkout: self + clean: true + fetchTags: false + - task: NuGetToolInstaller@1 + displayName: 'Use NuGet' + inputs: + versionSpec: '6.5.x' + - task: NuGetCommand@2 + displayName: 'NuGet set package source to ADO feed' + inputs: + command: custom + arguments: sources add -Name xboxgdk -Source $(URL_FEED) -ConfigFile $(Build.SourcesDirectory)\NuGet.config + - task: PowerShell@2 + displayName: 'Set nuget.config to single source' + inputs: + targetType: inline + script: | + $file = '.\NuGet.Config' + $doc = [xml](Get-Content $file) + $newelement = $doc.CreateElement("clear") + $clearadd = $doc.configuration.packageSources.PrependChild($newelement) + $doc.OuterXml | Set-Content $file + + - task: NuGetCommand@2 + displayName: NuGet Install WSDK arm64 + inputs: + command: custom + arguments: install Microsoft.Windows.SDK.CPP.arm64 -ExcludeVersion -OutputDirectory $(EXTRACTED_FOLDER)\SDK + - task: CopyFiles@2 + displayName: Set up Directory.Build.props + inputs: + SourceFolder: build + Contents: 'Directory.Build.props' + TargetFolder: $(Build.SourcesDirectory) + - task: VSBuild@1 + displayName: Build solution DirectXTK_Windows10_2019.sln arm64dbg + inputs: + solution: DirectXTK_Windows10_2019.sln + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: ARM64 + configuration: Debug + - task: VSBuild@1 + displayName: Build solution DirectXTK_Windows10_2019.sln arm64rel + inputs: + solution: DirectXTK_Windows10_2019.sln + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: ARM64 + configuration: Release diff --git a/Common/DirectXTK12/build/DirectXTK12-GitHub-Test-Dev17.yml b/Common/DirectXTK12/build/DirectXTK12-GitHub-Test-Dev17.yml new file mode 100644 index 0000000..e8f3871 --- /dev/null +++ b/Common/DirectXTK12/build/DirectXTK12-GitHub-Test-Dev17.yml @@ -0,0 +1,330 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# +# http://go.microsoft.com/fwlink/?LinkID=615561 + +# Builds the library and test suite. + +schedules: +- cron: "30 5 * * *" + displayName: 'Nightly build' + branches: + include: + - main + +trigger: none +pr: none + +resources: + repositories: + - repository: self + type: git + ref: refs/heads/main + +name: $(Year:yyyy).$(Month).$(DayOfMonth)$(Rev:.r) + +pool: + vmImage: windows-2022 + +variables: + GITHUB_PAT: $(GITHUBPUBLICTOKEN) + GUID_FEED: $(ADOFeedGUID) + +jobs: +- job: DESKTOP_BUILD + displayName: 'Win32 Desktop for x64/x86' + timeoutInMinutes: 60 + cancelTimeoutInMinutes: 1 + steps: + - checkout: self + clean: true + fetchTags: false + - task: DeleteFiles@1 + displayName: Delete files from Tests + inputs: + SourceFolder: Tests + Contents: '**' + RemoveSourceFolder: true + RemoveDotFiles: true + - task: CmdLine@2 + displayName: Fetch Tests + inputs: + script: git clone --quiet --no-tags https://%GITHUB_PAT%@github.com/walbourn/directxtk12test.git Tests + workingDirectory: $(Build.SourcesDirectory) + failOnStderr: true + - task: NuGetToolInstaller@1 + displayName: 'Use NuGet' + - task: NuGetCommand@2 + displayName: NuGet restore tests + inputs: + solution: Tests/D3D12Test/packages.config + feedRestore: $(GUID_FEED) + includeNuGetOrg: false + packagesDirectory: ../packages + - task: VSBuild@1 + displayName: Build solution DirectXTK_Tests_Desktop_2022_Win10.sln 32dbg + inputs: + solution: Tests/DirectXTK_Tests_Desktop_2022_Win10.sln + vsVersion: 17.0 + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: x86 + configuration: Debug + msbuildArchitecture: x64 + - task: VSBuild@1 + displayName: Build solution DirectXTK_Tests_Desktop_2022_Win10.sln 32rel + inputs: + solution: Tests/DirectXTK_Tests_Desktop_2022_Win10.sln + vsVersion: 17.0 + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: x86 + configuration: Release + msbuildArchitecture: x64 + - task: VSBuild@1 + displayName: Build solution DirectXTK_Tests_Desktop_2022_Win10.sln 64dbg + inputs: + solution: Tests/DirectXTK_Tests_Desktop_2022_Win10.sln + vsVersion: 17.0 + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: x64 + configuration: Debug + msbuildArchitecture: x64 + - task: VSBuild@1 + displayName: Build solution DirectXTK_Tests_Desktop_2022_Win10.sln 64rel + inputs: + solution: Tests/DirectXTK_Tests_Desktop_2022_Win10.sln + vsVersion: 17.0 + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: x64 + configuration: Release + msbuildArchitecture: x64 + - task: VSBuild@1 + displayName: Build solution DirectXTK_Tests_Desktop_2022_AgilitySDK.sln 32dbg + inputs: + solution: Tests/DirectXTK_Tests_Desktop_2022_AgilitySDK.sln + vsVersion: 17.0 + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: x86 + configuration: Debug + msbuildArchitecture: x64 + - task: VSBuild@1 + displayName: Build solution DirectXTK_Tests_Desktop_2022_AgilitySDK.sln 32rel + inputs: + solution: Tests/DirectXTK_Tests_Desktop_2022_AgilitySDK.sln + vsVersion: 17.0 + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: x86 + configuration: Release + msbuildArchitecture: x64 + - task: VSBuild@1 + displayName: Build solution DirectXTK_Tests_Desktop_2022_AgilitySDK.sln 64dbg + inputs: + solution: Tests/DirectXTK_Tests_Desktop_2022_AgilitySDK.sln + vsVersion: 17.0 + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: x64 + configuration: Debug + msbuildArchitecture: x64 + - task: VSBuild@1 + displayName: Build solution DirectXTK_Tests_Desktop_2022_AgilitySDK.sln 64rel + inputs: + solution: Tests/DirectXTK_Tests_Desktop_2022_AgilitySDK.sln + vsVersion: 17.0 + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: x64 + configuration: Release + msbuildArchitecture: x64 + +- job: DESKTOP_BUILD_ARM64 + displayName: 'Win32 Desktop for ARM64' + timeoutInMinutes: 60 + steps: + - checkout: self + clean: true + fetchTags: false + - task: DeleteFiles@1 + displayName: Delete files from Tests + inputs: + SourceFolder: Tests + Contents: '**' + RemoveSourceFolder: true + RemoveDotFiles: true + - task: CmdLine@2 + displayName: Fetch Tests + inputs: + script: git clone --quiet --no-tags https://%GITHUB_PAT%@github.com/walbourn/directxtk12test.git Tests + workingDirectory: $(Build.SourcesDirectory) + failOnStderr: true + - task: NuGetToolInstaller@1 + displayName: 'Use NuGet' + - task: NuGetCommand@2 + displayName: NuGet restore tests + inputs: + solution: Tests/D3D12Test/packages.config + feedRestore: $(GUID_FEED) + includeNuGetOrg: false + packagesDirectory: ../packages + - task: VSBuild@1 + displayName: Build solution DirectXTK_Tests_Desktop_2022_Win10.sln arm64dbg + inputs: + solution: Tests/DirectXTK_Tests_Desktop_2022_Win10.sln + vsVersion: 17.0 + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: ARM64 + configuration: Debug + msbuildArchitecture: x64 + - task: VSBuild@1 + displayName: Build solution DirectXTK_Tests_Desktop_2022_Win10.sln arm64rel + inputs: + solution: Tests/DirectXTK_Tests_Desktop_2022_Win10.sln + vsVersion: 17.0 + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: ARM64 + configuration: Release + msbuildArchitecture: x64 + - task: VSBuild@1 + displayName: Build solution DirectXTK_Tests_Desktop_2022_AgilitySDK.sln arm64dbg + inputs: + solution: Tests/DirectXTK_Tests_Desktop_2022_AgilitySDK.sln + vsVersion: 17.0 + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: ARM64 + configuration: Debug + msbuildArchitecture: x64 + - task: VSBuild@1 + displayName: Build solution DirectXTK_Tests_Desktop_2022_AgilitySDK.sln arm64rel + inputs: + solution: Tests/DirectXTK_Tests_Desktop_2022_AgilitySDK.sln + vsVersion: 17.0 + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: ARM64 + configuration: Release + msbuildArchitecture: x64 + +- job: CMAKE_BUILD + displayName: 'CMake BUILD_TESTING=ON' + timeoutInMinutes: 60 + steps: + - checkout: self + clean: true + fetchTags: false + - task: DeleteFiles@1 + displayName: Delete files from Tests + inputs: + SourceFolder: Tests + Contents: '**' + RemoveSourceFolder: true + RemoveDotFiles: true + - task: CmdLine@2 + displayName: Fetch Tests + inputs: + script: git clone --quiet --no-tags https://%GITHUB_PAT%@github.com/walbourn/directxtk12test.git Tests + workingDirectory: $(Build.SourcesDirectory) + failOnStderr: true + - task: ChocolateyCommand@0 + displayName: Install Ninja + inputs: + command: 'install' + installPackageId: 'ninja' + - task: CmdLine@2 + displayName: Setup environment for CMake to use VS + inputs: + script: | + @echo off + pushd "C:\Program Files (x86)\Microsoft Visual Studio\Installer\" + for /f "delims=" %%x in ('.\vswhere.exe -latest -property InstallationPath') do set VSPATH=%%x + popd + call "%VSPATH%\VC\Auxiliary\Build\vcvarsall.bat" x64 + echo ##vso[task.prependpath]%VCINSTALLDIR%Tools\Llvm\x64\bin + echo ##vso[task.prependpath]%WindowsSdkBinPath%x64 + echo ##vso[task.prependpath]%WindowsSdkVerBinPath%x64 + echo ##vso[task.prependpath]%VCToolsInstallDir%bin\Hostx64\x64 + echo ##vso[task.setvariable variable=EXTERNAL_INCLUDE;]%EXTERNAL_INCLUDE% + echo ##vso[task.setvariable variable=INCLUDE;]%INCLUDE% + echo ##vso[task.setvariable variable=LIB;]%LIB% + + - task: CMake@1 + displayName: CMake (MSVC; x64-Debug) Config + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: --preset=x64-Debug + - task: CMake@1 + displayName: CMake (MSVC; x64-Debug) Build + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: --build out/build/x64-Debug -v + - task: DeleteFiles@1 + inputs: + Contents: 'out' + - task: CMake@1 + displayName: CMake (MSVC; x64-Release) Config + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: --preset=x64-Release + - task: CMake@1 + displayName: CMake (MSVC; x64-Release) Build + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: --build out/build/x64-Release -v + - task: DeleteFiles@1 + inputs: + Contents: 'out' + - task: CMake@1 + displayName: CMake (clang/LLVM; x64-Debug) Config + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: --preset=x64-Debug-Clang + - task: CMake@1 + displayName: CMake (clang/LLVM; x64-Debug) Build + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: --build out/build/x64-Debug-Clang -v + - task: DeleteFiles@1 + inputs: + Contents: 'out' + - task: CMake@1 + displayName: CMake (clang/LLVM; x64-Release) Config + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: --preset=x64-Release-Clang + - task: CMake@1 + displayName: CMake (clang/LLVM; x64-Release) Build + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: --build out/build/x64-Release-Clang -v + - task: DeleteFiles@1 + inputs: + Contents: 'out' + - task: CmdLine@2 + displayName: Set LIB for ARM64 + inputs: + script: | + @echo off + pushd "C:\Program Files (x86)\Microsoft Visual Studio\Installer\" + for /f "delims=" %%x in ('.\vswhere.exe -latest -property InstallationPath') do set VSPATH=%%x + popd + call "%VSPATH%\VC\Auxiliary\Build\vcvarsall.bat" arm64 + echo ##vso[task.setvariable variable=LIB;]%LIB% + + - task: CMake@1 + displayName: CMake (clang/LLVM; arm64-Debug) Config + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: --preset=arm64-Debug-Clang + - task: CMake@1 + displayName: CMake (clang/LLVM; arm64-Debug) Build + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: --build out/build/arm64-Debug-Clang -v + - task: DeleteFiles@1 + inputs: + Contents: 'out' + - task: CMake@1 + displayName: CMake (clang/LLVM; arm64-Release) Config + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: --preset=arm64-Release-Clang + - task: CMake@1 + displayName: CMake (clang/LLVM; arm64-Release) Build + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: --build out/build/arm64-Release-Clang -v diff --git a/Common/DirectXTK12/build/DirectXTK12-GitHub-Test.yml b/Common/DirectXTK12/build/DirectXTK12-GitHub-Test.yml new file mode 100644 index 0000000..d84169c --- /dev/null +++ b/Common/DirectXTK12/build/DirectXTK12-GitHub-Test.yml @@ -0,0 +1,347 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# +# http://go.microsoft.com/fwlink/?LinkID=615561 + +# Builds the library and test suite. + +schedules: +- cron: "30 5 * * *" + displayName: 'Nightly build' + branches: + include: + - main + +trigger: + branches: + include: + - main + paths: + exclude: + - README.md + - HISTORY.md + - SECURITY.md + - LICENSE +pr: + branches: + include: + - main + paths: + exclude: + - README.md + - HISTORY.md + - SECURITY.md + - LICENSE + drafts: false + +resources: + repositories: + - repository: self + type: git + ref: refs/heads/main + +name: $(Year:yyyy).$(Month).$(DayOfMonth)$(Rev:.r) + +pool: + vmImage: windows-2019 + +variables: + GITHUB_PAT: $(GITHUBPUBLICTOKEN) + GUID_FEED: $(ADOFeedGUID) + +jobs: +- job: DESKTOP_BUILD + displayName: 'Win32 Desktop for x64/x86' + timeoutInMinutes: 120 + cancelTimeoutInMinutes: 1 + steps: + - checkout: self + clean: true + fetchTags: false + - task: DeleteFiles@1 + displayName: Delete files from Tests + inputs: + SourceFolder: Tests + Contents: '**' + RemoveSourceFolder: true + RemoveDotFiles: true + - task: CmdLine@2 + displayName: Fetch Tests + inputs: + script: git clone --quiet --no-tags https://%GITHUB_PAT%@github.com/walbourn/directxtk12test.git Tests + workingDirectory: $(Build.SourcesDirectory) + failOnStderr: true + - task: NuGetToolInstaller@1 + displayName: 'Use NuGet' + - task: NuGetCommand@2 + displayName: NuGet restore tests + inputs: + solution: Tests/D3D12Test/packages.config + feedRestore: $(GUID_FEED) + includeNuGetOrg: false + packagesDirectory: ../packages + - task: VSBuild@1 + displayName: Build solution DirectXTK_Tests_Desktop_2019_Win10.sln 32dbg + inputs: + solution: Tests/DirectXTK_Tests_Desktop_2019_Win10.sln + vsVersion: 16.0 + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: x86 + configuration: Debug + - task: VSBuild@1 + displayName: Build solution DirectXTK_Tests_Desktop_2019_Win10.sln 32rel + inputs: + solution: Tests/DirectXTK_Tests_Desktop_2019_Win10.sln + vsVersion: 16.0 + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: x86 + configuration: Release + - task: VSBuild@1 + displayName: Build solution DirectXTK_Tests_Desktop_2019_Win10.sln 64dbg + inputs: + solution: Tests/DirectXTK_Tests_Desktop_2019_Win10.sln + vsVersion: 16.0 + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: x64 + configuration: Debug + - task: VSBuild@1 + displayName: Build solution DirectXTK_Tests_Desktop_2019_Win10.sln 64rel + inputs: + solution: Tests/DirectXTK_Tests_Desktop_2019_Win10.sln + vsVersion: 16.0 + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: x64 + configuration: Release + - task: VSBuild@1 + displayName: Build solution DirectXTK_Tests_Desktop_2019_AgilitySDK.sln 32dbg + inputs: + solution: Tests/DirectXTK_Tests_Desktop_2019_AgilitySDK.sln + vsVersion: 16.0 + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: x86 + configuration: Debug + - task: VSBuild@1 + displayName: Build solution DirectXTK_Tests_Desktop_2019_AgilitySDK.sln 32rel + inputs: + solution: Tests/DirectXTK_Tests_Desktop_2019_AgilitySDK.sln + vsVersion: 16.0 + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: x86 + configuration: Release + - task: VSBuild@1 + displayName: Build solution DirectXTK_Tests_Desktop_2019_AgilitySDK.sln 64dbg + inputs: + solution: Tests/DirectXTK_Tests_Desktop_2019_AgilitySDK.sln + vsVersion: 16.0 + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: x64 + configuration: Debug + - task: VSBuild@1 + displayName: Build solution DirectXTK_Tests_Desktop_2019_AgilitySDK.sln 64rel + inputs: + solution: Tests/DirectXTK_Tests_Desktop_2019_AgilitySDK.sln + vsVersion: 16.0 + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: x64 + configuration: Release + +- job: DESKTOP_BUILD_ARM64 + displayName: 'Win32 Desktop for ARM64' + timeoutInMinutes: 120 + steps: + - checkout: self + clean: true + fetchTags: false + - task: DeleteFiles@1 + displayName: Delete files from Tests + inputs: + SourceFolder: Tests + Contents: '**' + RemoveSourceFolder: true + RemoveDotFiles: true + - task: CmdLine@2 + displayName: Fetch Tests + inputs: + script: git clone --quiet --no-tags https://%GITHUB_PAT%@github.com/walbourn/directxtk12test.git Tests + workingDirectory: $(Build.SourcesDirectory) + failOnStderr: true + - task: NuGetToolInstaller@1 + displayName: 'Use NuGet' + - task: NuGetCommand@2 + displayName: NuGet restore tests + inputs: + solution: Tests/D3D12Test/packages.config + feedRestore: $(GUID_FEED) + includeNuGetOrg: false + packagesDirectory: ../packages + - task: VSBuild@1 + displayName: Build solution DirectXTK_Tests_Desktop_2019_Win10.sln arm64dbg + inputs: + solution: Tests/DirectXTK_Tests_Desktop_2019_Win10.sln + vsVersion: 16.0 + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: ARM64 + configuration: Debug + - task: VSBuild@1 + displayName: Build solution DirectXTK_Tests_Desktop_2019_Win10.sln arm64rel + inputs: + solution: Tests/DirectXTK_Tests_Desktop_2019_Win10.sln + vsVersion: 16.0 + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: ARM64 + configuration: Release + - task: VSBuild@1 + displayName: Build solution DirectXTK_Tests_Desktop_2019_AgilitySDK.sln arm64dbg + inputs: + solution: Tests/DirectXTK_Tests_Desktop_2019_AgilitySDK.sln + vsVersion: 16.0 + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: ARM64 + configuration: Debug + - task: VSBuild@1 + displayName: Build solution DirectXTK_Tests_Desktop_2019_AgilitySDK.sln arm64rel + inputs: + solution: Tests/DirectXTK_Tests_Desktop_2019_AgilitySDK.sln + vsVersion: 16.0 + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: ARM64 + configuration: Release + +- job: UWP_BUILD_X64 + displayName: 'Universal Windows Platform (UWP) for x64' + timeoutInMinutes: 120 + cancelTimeoutInMinutes: 1 + steps: + - checkout: self + clean: true + fetchTags: false + - task: DeleteFiles@1 + displayName: Delete files from Tests + inputs: + SourceFolder: Tests + Contents: '**' + RemoveSourceFolder: true + RemoveDotFiles: true + - task: CmdLine@2 + displayName: Fetch Tests + inputs: + script: git clone --quiet --no-tags https://%GITHUB_PAT%@github.com/walbourn/directxtk12test.git Tests + workingDirectory: $(Build.SourcesDirectory) + failOnStderr: true + - task: NuGetToolInstaller@1 + displayName: 'Use NuGet' + - task: NuGetCommand@2 + displayName: NuGet restore tests + inputs: + solution: Tests/D3D12Test/packages.config + feedRestore: $(GUID_FEED) + includeNuGetOrg: false + packagesDirectory: ../packages + - task: VSBuild@1 + displayName: Build solution DirectXTK_Tests_Windows10.sln 64dbg + inputs: + solution: Tests/DirectXTK_Tests_Windows10.sln + vsVersion: 16.0 + msbuildArgs: /p:PreferredToolArchitecture=x64 /p:AppxBundle=Never + platform: x64 + configuration: Debug + - task: VSBuild@1 + displayName: Build solution DirectXTK_Tests_Windows10.sln 64rel + inputs: + solution: Tests/DirectXTK_Tests_Windows10.sln + vsVersion: 16.0 + msbuildArgs: /p:PreferredToolArchitecture=x64 /p:AppxBundle=Never + platform: x64 + configuration: Release + +- job: UWP_BUILD_X86 + displayName: 'Universal Windows Platform (UWP) for x86' + timeoutInMinutes: 120 + steps: + - checkout: self + clean: true + fetchTags: false + - task: DeleteFiles@1 + displayName: Delete files from Tests + inputs: + SourceFolder: Tests + Contents: '**' + RemoveSourceFolder: true + RemoveDotFiles: true + - task: CmdLine@2 + displayName: Fetch Tests + inputs: + script: git clone --quiet --no-tags https://%GITHUB_PAT%@github.com/walbourn/directxtk12test.git Tests + workingDirectory: $(Build.SourcesDirectory) + failOnStderr: true + - task: NuGetToolInstaller@1 + displayName: 'Use NuGet' + - task: NuGetCommand@2 + displayName: NuGet restore tests + inputs: + solution: Tests/D3D12Test/packages.config + feedRestore: $(GUID_FEED) + includeNuGetOrg: false + packagesDirectory: ../packages + - task: VSBuild@1 + displayName: Build solution DirectXTK_Tests_Windows10.sln 32dbg + inputs: + solution: Tests/DirectXTK_Tests_Windows10.sln + vsVersion: 16.0 + msbuildArgs: /p:PreferredToolArchitecture=x64 /p:AppxBundle=Never + platform: x86 + configuration: Debug + - task: VSBuild@1 + displayName: Build solution DirectXTK_Tests_Windows10.sln 32rel + inputs: + solution: Tests/DirectXTK_Tests_Windows10.sln + vsVersion: 16.0 + msbuildArgs: /p:PreferredToolArchitecture=x64 /p:AppxBundle=Never + platform: x86 + configuration: Release + +- job: UWP_BUILD_ARM64 + displayName: 'Universal Windows Platform (UWP) for ARM64' + timeoutInMinutes: 120 + steps: + - checkout: self + clean: true + fetchTags: false + - task: DeleteFiles@1 + displayName: Delete files from Tests + inputs: + SourceFolder: Tests + Contents: '**' + RemoveSourceFolder: true + RemoveDotFiles: true + - task: CmdLine@2 + displayName: Fetch Tests + inputs: + script: git clone --quiet --no-tags https://%GITHUB_PAT%@github.com/walbourn/directxtk12test.git Tests + workingDirectory: $(Build.SourcesDirectory) + failOnStderr: true + - task: NuGetToolInstaller@1 + displayName: 'Use NuGet' + - task: NuGetCommand@2 + displayName: NuGet restore tests + inputs: + solution: Tests/D3D12Test/packages.config + feedRestore: $(GUID_FEED) + includeNuGetOrg: false + packagesDirectory: ../packages + - task: VSBuild@1 + displayName: Build solution DirectXTK_Tests_Windows10.sln arm64dbg + inputs: + solution: Tests/DirectXTK_Tests_Windows10.sln + vsVersion: 16.0 + msbuildArgs: /p:PreferredToolArchitecture=x64 /p:AppxBundle=Never + platform: ARM64 + configuration: Debug + - task: VSBuild@1 + displayName: Build solution DirectXTK_Tests_Windows10.sln arm64rel + inputs: + solution: Tests/DirectXTK_Tests_Windows10.sln + vsVersion: 16.0 + msbuildArgs: /p:PreferredToolArchitecture=x64 /p:AppxBundle=Never + platform: ARM64 + configuration: Release diff --git a/Common/DirectXTK12/build/DirectXTK12-GitHub.yml b/Common/DirectXTK12/build/DirectXTK12-GitHub.yml new file mode 100644 index 0000000..98c88fb --- /dev/null +++ b/Common/DirectXTK12/build/DirectXTK12-GitHub.yml @@ -0,0 +1,152 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# +# http://go.microsoft.com/fwlink/?LinkID=615561 + +# Builds the library for Windows Desktop and UWP. + +schedules: +- cron: "0 5 * * *" + displayName: 'Nightly build' + branches: + include: + - main + +trigger: + branches: + include: + - main + paths: + exclude: + - README.md + - HISTORY.md + - SECURITY.md + - LICENSE +pr: + branches: + include: + - main + paths: + exclude: + - README.md + - HISTORY.md + - SECURITY.md + - LICENSE + drafts: false + +resources: + repositories: + - repository: self + type: git + ref: refs/heads/main + +name: $(Year:yyyy).$(Month).$(DayOfMonth)$(Rev:.r) + +variables: + Codeql.Enabled: true + +pool: + vmImage: windows-2019 + +jobs: +- job: DESKTOP_BUILD + displayName: 'Win32 Desktop' + timeoutInMinutes: 120 + cancelTimeoutInMinutes: 1 + steps: + - checkout: self + clean: true + fetchTags: false + - task: VSBuild@1 + displayName: Build solution DirectXTK_Desktop_2019_Win10.sln 32dbg + inputs: + solution: DirectXTK_Desktop_2019_Win10.sln + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: x86 + configuration: Debug + - task: VSBuild@1 + displayName: Build solution DirectXTK_Desktop_2019_Win10.sln 32rel + inputs: + solution: DirectXTK_Desktop_2019_Win10.sln + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: x86 + configuration: Release + - task: VSBuild@1 + displayName: Build solution DirectXTK_Desktop_2019_Win10.sln 64dbg + inputs: + solution: DirectXTK_Desktop_2019_Win10.sln + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: x64 + configuration: Debug + - task: VSBuild@1 + displayName: Build solution DirectXTK_Desktop_2019_Win10.sln 64rel + inputs: + solution: DirectXTK_Desktop_2019_Win10.sln + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: x64 + configuration: Release + - task: VSBuild@1 + displayName: Build solution DirectXTK_Desktop_2019_Win10.sln arm64dbg + inputs: + solution: DirectXTK_Desktop_2019_Win10.sln + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: ARM64 + configuration: Debug + - task: VSBuild@1 + displayName: Build solution DirectXTK_Desktop_2019_Win10.sln arm64rel + inputs: + solution: DirectXTK_Desktop_2019_Win10.sln + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: ARM64 + configuration: Release + +- job: UWP_BUILD + displayName: 'Universal Windows Platform (UWP)' + timeoutInMinutes: 120 + cancelTimeoutInMinutes: 1 + steps: + - checkout: self + clean: true + fetchTags: false + - task: VSBuild@1 + displayName: Build solution DirectXTK_Windows10_2019.sln 32dbg + inputs: + solution: DirectXTK_Windows10_2019.sln + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: x86 + configuration: Debug + - task: VSBuild@1 + displayName: Build solution DirectXTK_Windows10_2019.sln 32rel + inputs: + solution: DirectXTK_Windows10_2019.sln + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: x86 + configuration: Release + - task: VSBuild@1 + displayName: Build solution DirectXTK_Windows10_2019.sln 64dbg + inputs: + solution: DirectXTK_Windows10_2019.sln + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: x64 + configuration: Debug + - task: VSBuild@1 + displayName: Build solution DirectXTK_Windows10_2019.sln 64rel + inputs: + solution: DirectXTK_Windows10_2019.sln + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: x64 + configuration: Release + - task: VSBuild@1 + displayName: Build solution DirectXTK_Windows10_2019.sln arm64dbg + inputs: + solution: DirectXTK_Windows10_2019.sln + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: ARM64 + configuration: Debug + - task: VSBuild@1 + displayName: Build solution DirectXTK_Windows10_2019.sln arm64rel + inputs: + solution: DirectXTK_Windows10_2019.sln + msbuildArgs: /p:PreferredToolArchitecture=x64 + platform: ARM64 + configuration: Release diff --git a/Common/DirectXTK12/build/DirectXTK12-OneFuzz.yml b/Common/DirectXTK12/build/DirectXTK12-OneFuzz.yml new file mode 100644 index 0000000..09d6407 --- /dev/null +++ b/Common/DirectXTK12/build/DirectXTK12-OneFuzz.yml @@ -0,0 +1,129 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# +# http://go.microsoft.com/fwlink/?LinkID=615561 + +# Builds the library using CMake and submit for file fuzzing + +schedules: +- cron: "0 12 * * 0" + displayName: 'Submit for File Fuzzing' + branches: + include: + - main + always: true + +trigger: none +pr: none + +resources: + repositories: + - repository: self + type: git + ref: refs/heads/main + +name: $(Year:yyyy).$(Month).$(DayOfMonth)$(Rev:.r) + +variables: + VS_GENERATOR: 'Visual Studio 17 2022' + WIN11_SDK: '10.0.22000.0' + BUGFILING_PAT: $(BugFilingPAT) + GITHUB_PAT: $(GITHUBPUBLICTOKEN) + ONEFUZZ_PAT: $(OneFuzzPAT) + +pool: + vmImage: windows-2022 + +jobs: +- job: FUZZ_BUILD + displayName: 'Build for file fuzzing' + steps: + - checkout: self + clean: true + fetchTags: false + - task: DeleteFiles@1 + displayName: Delete files from Tests + inputs: + SourceFolder: Tests + Contents: '**' + RemoveSourceFolder: true + RemoveDotFiles: true + - task: CmdLine@2 + displayName: Fetch Tests + inputs: + script: git clone --quiet --no-tags https://%GITHUB_PAT%@github.com/walbourn/directxtk12test.git Tests + workingDirectory: $(Build.SourcesDirectory) + failOnStderr: true + - task: CMake@1 + displayName: 'CMake (MSVC): Config with ASan' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: '-G "$(VS_GENERATOR)" -A x64 -B out -DCMAKE_SYSTEM_VERSION=$(WIN11_SDK) -DBUILD_XAUDIO_WIN10=ON -DBUILD_FUZZING=ON -DBUILD_TESTING=OFF' + - task: CMake@1 + displayName: 'CMake (MSVC): Build with ASan' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: --build out -v --config RelWithDebInfo + - task: CopyFiles@2 + displayName: Copy fuzzer + inputs: + Contents: | + build\OneFuzzConfig.json + out\bin\CMake\RelWithDebInfo\fuzzloaders.exe + TargetFolder: .drop + OverWrite: true + flattenFolders: true + - task: CopyFiles@2 + displayName: Copy symbols + inputs: + Contents: | + out\bin\CMake\RelWithDebInfo\fuzzloaders.pdb + TargetFolder: .drop\symbols + OverWrite: true + flattenFolders: true + - task: PowerShell@2 + displayName: Download seed files + inputs: + targetType: inline + script: | + $seedfiles = "AlphaEdge.dds", + "cubea8r8g8b8.dds", + "default_texture_nm.dds", + "dx5_logo.dds", + "hdrtest.dds", + "normalmap.dds"; + + New-Item -ItemType Directory -Force -Path .drop\seeds\ + + foreach($filename in $seedfiles) + { + Write-Host "Fetching: $filename" + $url = "https://raw.githubusercontent.com/walbourn/directxtexmedia/main/" + $filename + $target = [System.IO.Path]::Combine(".drop\seeds\", $filename) + Invoke-WebRequest $url -o $target + } + + - task: MSBuild@1 + displayName: 'Copy ASan binaries' + inputs: + solution: build/CopyASAN.targets + msbuildArguments: /p:TargetFolder=$(Build.SourcesDirectory)\.drop + msbuildVersion: 17.0 + msbuildArchitecture: x64 + - task: PowerShell@2 + displayName: List drop files + inputs: + targetType: inline + script: | + Get-ChildItem ".drop" -Recurse | select FullName + + - task: onefuzz-task@0 + displayName: 'Submit to OneFuzz' + inputs: + onefuzzOSes: 'Windows' + env: + onefuzzDropDirectory: $(Build.SourcesDirectory)\.drop + onefuzzDropPAT: $(ONEFUZZ_PAT) + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + onefuzzBugFilingPAT: $(BUGFILING_PAT) + \ No newline at end of file diff --git a/Common/DirectXTK12/build/DirectXTK12-SDL.yml b/Common/DirectXTK12/build/DirectXTK12-SDL.yml new file mode 100644 index 0000000..fed2901 --- /dev/null +++ b/Common/DirectXTK12/build/DirectXTK12-SDL.yml @@ -0,0 +1,98 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# +# http://go.microsoft.com/fwlink/?LinkID=615561 + +# Runs various SDL recommended tools on the code. + +schedules: +- cron: "0 3 * * 0,3,5" + displayName: 'Three times a week' + branches: + include: + - main + +trigger: none +pr: none + +resources: + repositories: + - repository: self + type: git + ref: refs/heads/main + +name: $(Year:yyyy).$(Month).$(DayOfMonth)$(Rev:.r) + +variables: + VS_GENERATOR: 'Visual Studio 17 2022' + +pool: + vmImage: windows-2022 + +jobs: +- job: SDL_BUILD + displayName: 'Build using required SDL tools' + workspace: + clean: all + steps: + - checkout: self + clean: true + fetchTags: false + - task: NodeTool@0 + displayName: 'NPM install' + inputs: + versionSpec: 14.x + - task: securedevelopmentteam.vss-secure-development-tools.build-task-credscan.CredScan@3 + displayName: 'Run Credential Scanner' + inputs: + debugMode: false + folderSuppression: false + - task: PoliCheck@2 + displayName: 'Run PoliCheck' + inputs: + result: PoliCheck.xml + - task: Armory@2 + displayName: Run ARMory + - task: CMake@1 + displayName: 'CMake (MSVC): Config x64' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: '-G "$(VS_GENERATOR)" -A x64 -B out -DENABLE_SPECTRE_MITIGATION=ON' + - task: CodeQL3000Init@0 + inputs: + Enabled: true + - task: VSBuild@1 + displayName: 'Build C++ with CodeQL' + inputs: + solution: '$(Build.SourcesDirectory)/out/DirectXTK12.sln' + vsVersion: 17.0 + platform: x64 + configuration: Release + msbuildArchitecture: x64 + - task: CodeQL3000Finalize@0 + condition: always() + - task: CMake@1 + displayName: 'CMake (MSVC): Build x64 Release' + inputs: + cwd: '$(Build.SourcesDirectory)' + cmakeArgs: --build out -v --config RelWithDebInfo + - task: securedevelopmentteam.vss-secure-development-tools.build-task-antimalware.AntiMalware@4 + displayName: 'Run AntiMalware' + inputs: + InputType: 'Basic' + ScanType: 'CustomScan' + FileDirPath: $(Agent.BuildDirectory) + EnableSERVICEs: true + SupportLogOnError: false + TreatSignatureUpdateFailureAs: 'Warning' + SignatureFreshness: 'OneDay' + TreatStaleSignatureAs: 'Error' + condition: always() + - task: securedevelopmentteam.vss-secure-development-tools.build-task-postanalysis.PostAnalysis@2 + displayName: 'Post Analysis' + inputs: + GdnBreakAllTools: true + GdnBreakPolicy: 'Microsoft' + GdnBreakPolicyMinSev: 'Error' + - task: ComponentGovernanceComponentDetection@0 + displayName: Component Detection diff --git a/Common/DirectXTK12/build/DirectXTK12-config.cmake.in b/Common/DirectXTK12/build/DirectXTK12-config.cmake.in new file mode 100644 index 0000000..f71a11f --- /dev/null +++ b/Common/DirectXTK12/build/DirectXTK12-config.cmake.in @@ -0,0 +1,14 @@ +@PACKAGE_INIT@ + +include(${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@-targets.cmake) +include(CMakeFindDependencyMacro) + +if(MINGW) + find_dependency(directx-headers) + find_dependency(directxmath) +else() + find_package(directx-headers CONFIG QUIET) + find_package(directxmath CONFIG QUIET) +endif() + +check_required_components("@PROJECT_NAME@") diff --git a/Common/DirectXTK12/build/Directory.Build.props b/Common/DirectXTK12/build/Directory.Build.props new file mode 100644 index 0000000..9c3935c --- /dev/null +++ b/Common/DirectXTK12/build/Directory.Build.props @@ -0,0 +1,92 @@ + + + + + C:\xtracted\ + $(ExtractedFolder)\ + + <_AlternativeVCTargetsPath170>$(ExtractedFolder)VCTargets170\ + <_AlternativeVCTargetsPath160>$(ExtractedFolder)VCTargets160\ + <_AlternativeVCTargetsPath150>$(ExtractedFolder)VCTargets150\ + + + 15.0 + + + + + + + + + + + + + + + + + + + $(MSBuildThisFileDirectory)build\placeholder.xvd + + + + + $(_AlternativeVCTargetsPath160) + true + $(_AlternativeVCTargetsPath150) + $(_AlternativeVCTargetsPath160) + + + + $(_AlternativeVCTargetsPath160) + true + $(_AlternativeVCTargetsPath150) + $(_AlternativeVCTargetsPath160) + + + + $(_AlternativeVCTargetsPath160) + true + $(_AlternativeVCTargetsPath150) + $(_AlternativeVCTargetsPath160) + + + + + true + $(_AlternativeVCTargetsPath150) + $(_AlternativeVCTargetsPath160) + $(_AlternativeVCTargetsPath170) + + + + true + $(_AlternativeVCTargetsPath150) + $(_AlternativeVCTargetsPath160) + $(_AlternativeVCTargetsPath170) + + + + true + $(_AlternativeVCTargetsPath150) + $(_AlternativeVCTargetsPath160) + $(_AlternativeVCTargetsPath170) + + \ No newline at end of file diff --git a/Common/DirectXTK12/build/OneFuzzConfig.json b/Common/DirectXTK12/build/OneFuzzConfig.json new file mode 100644 index 0000000..fa9e4f2 --- /dev/null +++ b/Common/DirectXTK12/build/OneFuzzConfig.json @@ -0,0 +1,33 @@ +{ + "ConfigVersion": 3, + "Entries": [ + { + "JobNotificationEmail": "directxtkdev@microsoft.com", + "Skip": false, + "Fuzzer": { + "$type": "libfuzzer", + "FuzzingHarnessExecutableName": "fuzzloaders.exe" + }, + "RebootAfterSetup": false, + "OneFuzzJobs": [ + { + "ProjectName": "Direct3D", + "TargetName": "DirectXTK12" + } + ], + "JobDependencies": [ + "fuzzloaders.exe", + "fuzzloaders.pdb", + "clang_rt.asan_dynamic-x86_64.dll", + "msdia140.dll" + ], + "AdoTemplate": { + "Org": "microsoft", + "Project": "OS", + "AssignedTo": "chuckw@microsoft.com", + "AreaPath": "OS\\Core\\SiGMa\\GRFX-Graphics", + "IterationPath": "OS\\Future" + } + } + ] +} \ No newline at end of file diff --git a/Common/DirectXTK12/build/SetupBWOI.targets b/Common/DirectXTK12/build/SetupBWOI.targets new file mode 100644 index 0000000..501a4fa --- /dev/null +++ b/Common/DirectXTK12/build/SetupBWOI.targets @@ -0,0 +1,138 @@ + + + + + C:\xtracted\ + $(ExtractedFolder)\ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_BWOIFolder>%(BWOIVCFolders.Identity) + + + + <_MissingBWOIVCFolders Condition="'$(ForceVCTargetsBWOIRefresh)'=='true' or !Exists($(_BWOIFolder))" Include="$(_BWOIFolder)" /> + + + + <_ExtractedOutOfDate Condition="'@(_MissingBWOIVCFolders)' != ''">true + <_BWOIFolder /> + + + + + + + + + + <_VSFolder Condition="'$(VisualStudioVersion)' == '17.0'">VS2022 + <_VSFolder Condition="'$(VisualStudioVersion)' == '16.0'">VS2019 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Common/DirectXTK12/build/placeholder.xvd b/Common/DirectXTK12/build/placeholder.xvd new file mode 100644 index 0000000..a1352b5 --- /dev/null +++ b/Common/DirectXTK12/build/placeholder.xvd @@ -0,0 +1 @@ +This file is a placeholder for gameos.xvd to speed up ADO pipeline builds. \ No newline at end of file diff --git a/DirectXTK12 b/DirectXTK12 deleted file mode 160000 index c41a922..0000000 --- a/DirectXTK12 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c41a922a1df4c146b121a8b17ec83c7cd3eb4671 diff --git a/Game.cpp b/Game.cpp index a72bc9d..3855ceb 100644 --- a/Game.cpp +++ b/Game.cpp @@ -29,7 +29,7 @@ Game::Game() noexcept(false) DXGI_FORMAT_D24_UNORM_S8_UINT, // depthBufferFormat (DXGI_FORMAT) 3, // backBufferCount (UINT) D3D_FEATURE_LEVEL_11_0, // minFeatureLevel (D3D_FEATURE_LEVEL) - DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH // flags (unsigned int) + DX::DeviceResources::c_AllowTearing // flags (unsigned int) ); m_deviceResources->RegisterDeviceNotify(this); @@ -44,7 +44,7 @@ Game::~Game() } // Initialize the Direct3D resources required to run. -void Game::Initialize(HWND window, int width, int height, UINT subDivideCount) +void Game::Initialize(HWND window, int width, int height, UINT subDivideCount, UINT shadowMapSize, BOOL fullScreenMode) { // Initialize input devices. m_keyboard = std::make_unique(); @@ -58,7 +58,9 @@ void Game::Initialize(HWND window, int width, int height, UINT subDivideCount) m_height = height; m_aspectRatio = static_cast(width) / static_cast(height); - m_deviceResources->CreateDeviceResources(); + m_shadowMapSize = shadowMapSize; + + m_deviceResources->CreateDeviceResources(fullScreenMode); CreateDeviceDependentResources(); m_deviceResources->CreateWindowSizeDependentResources(); @@ -169,7 +171,7 @@ void Game::Update(DX::StepTimer const& timer) // Light rotation update if (m_lightRotation) - m_lightDirection = XMVector3TransformCoord(m_lightDirection, XMMatrixRotationY(elapsedTime / 26.0f)); + m_lightDirection = XMVector3TransformCoord(m_lightDirection, XMMatrixRotationY(elapsedTime / 24.0f)); // Update Shadow Transform { @@ -392,7 +394,8 @@ void Game::Render() ImGui::Begin("apollo"); ImGui::SetWindowSize(ImVec2(450, 550), ImGuiCond_Always); - ImGui::Text("%d x %d", m_width, m_height); + ImGui::Text("%d x %d (Resolution)", m_width, m_height); + ImGui::Text("%d x %d (Shadow Map Resolution)", m_shadowMapSize, m_shadowMapSize); ImGui::TextColored(ImVec4(1, 1, 0, 1), "%.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); ImGui::Dummy(ImVec2(0.0f, 20.0f)); @@ -555,7 +558,7 @@ void Game::CreateDeviceDependentResources() m_graphicsMemory = std::make_unique(device); // Initialize Shadow Map Instance - m_shadowMap = std::make_unique(m_deviceResources->GetD3DDevice(), 4096, 4096); + m_shadowMap = std::make_unique(m_deviceResources->GetD3DDevice(), m_shadowMapSize, m_shadowMapSize); // Initialize Bounds for Shadow Map m_sceneBounds.Center = XMFLOAT3(0.0f, 0.0f, 0.0f); diff --git a/Game.h b/Game.h index 4c2d2f0..ef39a97 100644 --- a/Game.h +++ b/Game.h @@ -25,7 +25,7 @@ class Game final : public DX::IDeviceNotify Game& operator= (Game const&) = delete; // Initialization and management - void Initialize(HWND window, int width, int height, UINT subDivideCount); + void Initialize(HWND window, int width, int height, UINT subDivideCount, UINT shadowMapSize, BOOL fullScreenMode); // Basic game loop void Tick(); @@ -98,6 +98,8 @@ class Game final : public DX::IDeviceNotify UINT m_height; float m_aspectRatio; + UINT m_shadowMapSize; + // Number of draw calls static const unsigned int c_numDrawCalls = 2; diff --git a/Main.cpp b/Main.cpp index 01202f8..8a44141 100644 --- a/Main.cpp +++ b/Main.cpp @@ -70,38 +70,46 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, if (!RegisterClassExW(&wcex)) return 1; - // Check Arguments - LPWSTR* szArglist = nullptr; - int nArgs; - + // Default values UINT subDivideCount = 8u; UINT width = GetSystemMetrics(SM_CXSCREEN); UINT height = GetSystemMetrics(SM_CYSCREEN); + UINT shadowMapSize = 8192u; + BOOL fullScreenMode = FALSE; - szArglist = CommandLineToArgvW(GetCommandLineW(), &nArgs); + // Check Arguments + int nArgs; + LPWSTR* szArglist = CommandLineToArgvW(GetCommandLineW(), &nArgs); if (szArglist == nullptr) ExitGame(); - if (1 < nArgs && nArgs < 4) + if (nArgs >= 2) { subDivideCount = std::min(9u, std::max(7u, static_cast(std::stoi(szArglist[1])))); } - else if (nArgs == 4) + if (nArgs >= 3) { - subDivideCount = std::min(9u, std::max(7u, static_cast(std::stoi(szArglist[1])))); - width = std::max(1280u, static_cast(std::stoi(szArglist[2]))); - height = std::max(720u, static_cast(std::stoi(szArglist[3]))); + shadowMapSize = std::min(8192u, std::max(4096u, static_cast(std::stoi(szArglist[2])))); + } + if (nArgs >= 4) + { + fullScreenMode = std::stoi(szArglist[3]); + } + if (nArgs >= 6 && !fullScreenMode) + { + width = std::max(1280u, static_cast(std::stoi(szArglist[4]))); + height = std::max(720u, static_cast(std::stoi(szArglist[5]))); } - if (szArglist != nullptr) - LocalFree(szArglist); + if (szArglist != nullptr) LocalFree(szArglist); // Create window RECT rc = { 0, 0, static_cast(width), static_cast(height) }; + const DWORD dwStyle = fullScreenMode ? WS_POPUP : (WS_OVERLAPPEDWINDOW ^ WS_THICKFRAME); - AdjustWindowRect(&rc, WS_POPUP, FALSE); + AdjustWindowRect(&rc, dwStyle, FALSE); - HWND hwnd = CreateWindowExW(WS_EX_TOPMOST, L"apolloWindowClass", g_szAppName, WS_POPUP, + HWND hwnd = CreateWindowExW(WS_EX_TOPMOST, L"apolloWindowClass", g_szAppName, dwStyle, CW_USEDEFAULT, CW_USEDEFAULT, rc.right - rc.left, rc.bottom - rc.top, nullptr, nullptr, hInstance, g_game.get()); @@ -109,12 +117,12 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, if (!hwnd) return 1; - ShowWindow(hwnd, SW_SHOW); + ShowWindow(hwnd, nCmdShow); GetClientRect(hwnd, &rc); SetCursorPos(width/2, height/2); - g_game->Initialize(hwnd, rc.right - rc.left, rc.bottom - rc.top, subDivideCount); + g_game->Initialize(hwnd, rc.right - rc.left, rc.bottom - rc.top, subDivideCount, shadowMapSize, fullScreenMode); } // Main message loop diff --git a/apollo.sln b/apollo.sln index 68f3b11..043f635 100644 --- a/apollo.sln +++ b/apollo.sln @@ -5,7 +5,7 @@ VisualStudioVersion = 17.7.34031.279 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "apollo", "apollo.vcxproj", "{3CA0761E-2E27-4685-9A7B-9401DF3A7E89}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DirectXTK12", "DirectXTK12\DirectXTK_Desktop_2022_Win10.vcxproj", "{3E0E8608-CD9B-4C76-AF33-29CA38F2C9F0}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DirectXTK12", "Common\DirectXTK12\DirectXTK_Desktop_2022_Win10.vcxproj", "{3E0E8608-CD9B-4C76-AF33-29CA38F2C9F0}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/apollo.vcxproj b/apollo.vcxproj index 032db8d..47da190 100644 --- a/apollo.vcxproj +++ b/apollo.vcxproj @@ -85,7 +85,7 @@ - $(ProjectDir);$(SolutionDir)DirectXTK12\Inc;$(SolutionDir)Common;$(SolutionDir)Common\imgui;%(AdditionalIncludeDirectories) + $(ProjectDir);$(SolutionDir)Common\DirectXTK12\Inc;$(SolutionDir)Common;$(SolutionDir)Common\imgui;%(AdditionalIncludeDirectories) Use pch.h Level4 @@ -112,7 +112,7 @@ - $(ProjectDir);$(SolutionDir)DirectXTK12\Inc;$(SolutionDir)Common;$(SolutionDir)Common\imgui;%(AdditionalIncludeDirectories) + $(ProjectDir);$(SolutionDir)Common\DirectXTK12\Inc;$(SolutionDir)Common;$(SolutionDir)Common\imgui;%(AdditionalIncludeDirectories) Use pch.h Level4 @@ -138,7 +138,7 @@ - $(ProjectDir);$(SolutionDir)DirectXTK12\Inc;$(SolutionDir)Common;$(SolutionDir)Common\imgui;%(AdditionalIncludeDirectories) + $(ProjectDir);$(SolutionDir)Common\DirectXTK12\Inc;$(SolutionDir)Common;$(SolutionDir)Common\imgui;%(AdditionalIncludeDirectories) Use pch.h Level4 @@ -169,7 +169,7 @@ - $(ProjectDir);$(SolutionDir)DirectXTK12\Inc;$(SolutionDir)Common;$(SolutionDir)Common\imgui;%(AdditionalIncludeDirectories) + $(ProjectDir);$(SolutionDir)Common\DirectXTK12\Inc;$(SolutionDir)Common;$(SolutionDir)Common\imgui;%(AdditionalIncludeDirectories) Use pch.h Level4 @@ -284,11 +284,6 @@ - - - {3e0e8608-cd9b-4c76-af33-29ca38f2c9f0} - - PS @@ -396,6 +391,11 @@ + + + {3e0e8608-cd9b-4c76-af33-29ca38f2c9f0} + + diff --git a/apollo.vcxproj.user b/apollo.vcxproj.user index 6bb42d2..531c9a5 100644 --- a/apollo.vcxproj.user +++ b/apollo.vcxproj.user @@ -1,19 +1,23 @@  - 8 + + WindowsLocalDebugger - 8 + + WindowsLocalDebugger - 8 + + WindowsLocalDebugger - 8 + + WindowsLocalDebugger \ No newline at end of file