From aa45b831e7a71160a69a7d13e9d74844dc6aa210 Mon Sep 17 00:00:00 2001 From: Scott Matloff Date: Thu, 3 Nov 2022 13:15:17 -0700 Subject: [PATCH] Updated samples for the 2210 GDK release. (#16) * Updated samples for the 2210 GDK release. * Moved samples list to the wiki. * Updated readme to use wiki links for samples list. --- Kits/ATGTK/ControllerHelp.cpp | 14 +- Kits/ATGTK/FrontPanel/FrontPanelDisplay.cpp | 15 +- Kits/ATGTK/Processor.cpp | 77 +- Kits/ATGTK/Processor.h | 5 +- Kits/ATGTK/RDTSCPStopWatch.h | 182 +- Kits/ATGTK/RDTSCPStopwatch.cpp | 6 +- Kits/ATGTK/Random.h | 42 +- Kits/ATGTK/Texture.cpp | 2 +- Kits/ATGTK/UnicodeRendering/GlyphCache.h | 8 +- Kits/ATGTK/UserLockable.h | 169 +- Kits/ATGTK/d3dx12.h | 66 +- Kits/ATGTelemetry/GDK/ATGTelemetry.cpp | 19 + Kits/ATGTelemetry/GDK/ATGTelemetry.h | 4 +- Kits/DirectXTK12/Audio/SoundCommon.cpp | 70 +- Kits/DirectXTK12/DirectXTK12_GDK_2019.vcxproj | 2 + .../DirectXTK12/DirectXTK12_GRDK_2019.vcxproj | 2 + Kits/DirectXTK12/Inc/Audio.h | 38 +- Kits/DirectXTK12/Inc/CommonStates.h | 131 +- Kits/DirectXTK12/Inc/DDSTextureLoader.h | 21 +- Kits/DirectXTK12/Inc/DirectXHelpers.h | 54 +- .../Inc/EffectPipelineStateDescription.h | 23 + Kits/DirectXTK12/Inc/Effects.h | 1691 +++++++++-------- Kits/DirectXTK12/Inc/GeometricPrimitive.h | 118 +- Kits/DirectXTK12/Inc/GraphicsMemory.h | 285 +-- Kits/DirectXTK12/Inc/Model.h | 1197 ++++++------ Kits/DirectXTK12/Inc/PostProcess.h | 339 ++-- Kits/DirectXTK12/Inc/PrimitiveBatch.h | 204 +- Kits/DirectXTK12/Inc/SpriteBatch.h | 197 +- Kits/DirectXTK12/Inc/SpriteFont.h | 157 +- Kits/DirectXTK12/Inc/VertexTypes.h | 579 +++--- Kits/DirectXTK12/Inc/WICTextureLoader.h | 30 +- Kits/DirectXTK12/README.md | 8 +- Kits/DirectXTK12/Src/BasicPostProcess.cpp | 5 + Kits/DirectXTK12/Src/DDSTextureLoader.cpp | 22 +- Kits/DirectXTK12/Src/DescriptorHeap.cpp | 18 +- Kits/DirectXTK12/Src/DirectXHelpers.cpp | 5 + Kits/DirectXTK12/Src/EffectCommon.h | 13 +- Kits/DirectXTK12/Src/EffectFactory.cpp | 13 +- .../Src/EffectPipelineStateDescription.cpp | 6 + Kits/DirectXTK12/Src/GamePad.cpp | 6 +- Kits/DirectXTK12/Src/LoaderHelpers.h | 31 + Kits/DirectXTK12/Src/Mouse.cpp | 28 +- Kits/DirectXTK12/Src/PBREffectFactory.cpp | 13 +- Kits/DirectXTK12/Src/PrimitiveBatch.cpp | 2 +- Kits/DirectXTK12/Src/ResourceUploadBatch.cpp | 43 +- Kits/DirectXTK12/Src/ScreenGrab.cpp | 19 +- .../Src/Shaders/CompileShaders.cmd | 32 +- Kits/DirectXTK12/Src/WICTextureLoader.cpp | 13 +- Kits/DirectXTK12/Src/d3dx12.h | 66 +- Kits/DirectXTK12/Src/pch.h | 9 +- Kits/DirectXTex/BC.h | 1 - Kits/DirectXTex/BC6HBC7.cpp | 102 +- Kits/DirectXTex/DirectXTex.h | 17 +- Kits/DirectXTex/DirectXTex.inl | 1 + Kits/DirectXTex/DirectXTexP.h | 5 +- Kits/DirectXTex/DirectXTexUtil.cpp | 35 + .../DirectXTex_GXDK_PC_2019.vcxproj | 2 + Kits/DirectXTex/README.md | 6 +- Kits/DirectXTex/Shaders/CompileShaders.cmd | 2 +- Kits/DirectXTex/d3dx12.h | 66 +- Kits/LiveTK/LiveResources.cpp | 75 +- Kits/LiveTK/LiveResources.h | 4 +- README.md | 144 +- Samples/Audio/InGameChat/InGameChat.vcxproj | 2 +- .../InGameChat/InGameChat.vcxproj.filters | 4 +- Samples/Audio/InGameChat/Main.cpp | 2 +- ...oftGame.Config => MicrosoftGameConfig.mgc} | 3 +- Samples/Audio/InGameChat/ReadMe.docx | Bin 229596 -> 229837 bytes Samples/Audio/InGameChat/pch.h | 4 +- .../Graphics/AdvancedESRAM/AdvancedESRAM.cpp | 83 +- .../Graphics/AdvancedESRAM/AdvancedESRAM.h | 16 +- .../AdvancedESRAM/DeviceResources.cpp | 215 +-- .../Graphics/AdvancedESRAM/DeviceResources.h | 35 +- Samples/Graphics/AdvancedESRAM/Main.cpp | 79 +- Samples/Graphics/AdvancedESRAM/ReadMe.docx | Bin 370026 -> 370691 bytes Samples/Graphics/AdvancedESRAM/StepTimer.h | 2 +- Samples/Graphics/AdvancedESRAM/pch.h | 34 +- .../Graphics/Antialiasing/Antialiasing.cpp | 34 +- Samples/Graphics/Antialiasing/Antialiasing.h | 5 + .../Graphics/Antialiasing/DeviceResources.cpp | 139 +- .../Graphics/Antialiasing/DeviceResources.h | 17 +- Samples/Graphics/Antialiasing/Main.cpp | 63 +- Samples/Graphics/Antialiasing/Readme.docx | Bin 461465 -> 461904 bytes Samples/Graphics/Antialiasing/StepTimer.h | 2 +- Samples/Graphics/Antialiasing/pch.h | 16 +- .../ComputeParticles/ComputeParticles.cpp | 22 +- .../ComputeParticles/ComputeParticles.h | 10 +- .../ComputeParticles/DeviceResources.cpp | 215 +-- .../ComputeParticles/DeviceResources.h | 28 +- Samples/Graphics/ComputeParticles/Main.cpp | 81 +- Samples/Graphics/ComputeParticles/StepTimer.h | 2 +- Samples/Graphics/ComputeParticles/pch.h | 17 +- Samples/Graphics/ComputeParticles/readme.docx | Bin 723124 -> 723651 bytes .../DXRTriangle/DXRTriangle/DXRTriangle.cpp | 43 +- .../DXRTriangle/DXRTriangle/DXRTriangle.h | 9 +- .../DXRTriangle/DXRTriangle.vcxproj | 3 + .../DXRTriangle/DeviceResources.cpp | 118 +- .../DXRTriangle/DXRTriangle/DeviceResources.h | 19 +- .../Graphics/DXRTriangle/DXRTriangle/Main.cpp | 2 +- .../DXRTriangle/DXRTriangle/StepTimer.h | 2 +- .../Graphics/DXRTriangle/DXRTriangle/pch.h | 4 +- .../DeferredParticles/DeferredParticles.cpp | 34 +- .../DeferredParticles/DeferredParticles.h | 12 +- .../DeferredParticles/DeviceResources.cpp | 215 +-- .../DeferredParticles/DeviceResources.h | 28 +- Samples/Graphics/DeferredParticles/Main.cpp | 81 +- .../Graphics/DeferredParticles/StepTimer.h | 2 +- Samples/Graphics/DeferredParticles/pch.h | 17 +- .../Graphics/DynamicLOD/DeviceResources.cpp | 131 +- Samples/Graphics/DynamicLOD/DeviceResources.h | 15 +- Samples/Graphics/DynamicLOD/DynamicLOD.cpp | 58 +- Samples/Graphics/DynamicLOD/DynamicLOD.h | 2 +- Samples/Graphics/DynamicLOD/Main.cpp | 41 +- Samples/Graphics/DynamicLOD/StepTimer.h | 2 +- Samples/Graphics/DynamicLOD/pch.h | 21 +- .../ExecuteIndirect/DeviceResources.cpp | 215 +-- .../ExecuteIndirect/DeviceResources.h | 28 +- .../ExecuteIndirect/ExecuteIndirect.cpp | 110 +- .../ExecuteIndirect/ExecuteIndirect.h | 11 +- Samples/Graphics/ExecuteIndirect/Main.cpp | 79 +- Samples/Graphics/ExecuteIndirect/Readme.docx | Bin 417427 -> 417745 bytes Samples/Graphics/ExecuteIndirect/StepTimer.h | 2 +- Samples/Graphics/ExecuteIndirect/pch.h | 30 +- .../FastBlockCompress/DeviceResources.cpp | 215 +-- .../FastBlockCompress/DeviceResources.h | 28 +- .../FastBlockCompress/FastBlockCompress.cpp | 34 +- .../FastBlockCompress/FastBlockCompress.h | 17 +- Samples/Graphics/FastBlockCompress/Main.cpp | 79 +- .../Graphics/FastBlockCompress/Readme.docx | Bin 309655 -> 310212 bytes .../Graphics/FastBlockCompress/StepTimer.h | 2 +- Samples/Graphics/FastBlockCompress/pch.h | 18 +- .../GeometricExpansion/DeviceResources.cpp | 133 +- .../GeometricExpansion/DeviceResources.h | 15 +- .../GeometricExpansion/GeometricExpansion.cpp | 62 +- .../GeometricExpansion/GeometricExpansion.h | 12 +- Samples/Graphics/GeometricExpansion/Main.cpp | 41 +- .../Graphics/GeometricExpansion/Particles.cpp | 437 ++--- .../Graphics/GeometricExpansion/Particles.h | 14 +- .../Graphics/GeometricExpansion/ReadMe.docx | Bin 542293 -> 542584 bytes .../Graphics/GeometricExpansion/StepTimer.h | 2 +- Samples/Graphics/GeometricExpansion/pch.h | 25 +- Samples/Graphics/HDR10/DeviceResources.cpp | 81 +- Samples/Graphics/HDR10/DeviceResources.h | 20 +- Samples/Graphics/HDR10/HDR10.cpp | 50 +- Samples/Graphics/HDR10/Main.cpp | 5 +- Samples/Graphics/HDR10/Readme.docx | Bin 197810 -> 198644 bytes Samples/Graphics/HDR10/StepTimer.h | 2 +- Samples/Graphics/HDR10/pch.h | 5 +- .../Graphics/HistogramCS/DeviceResources.cpp | 71 +- .../Graphics/HistogramCS/DeviceResources.h | 10 +- Samples/Graphics/HistogramCS/HistogramCS.cpp | 37 +- Samples/Graphics/HistogramCS/Main.cpp | 4 +- Samples/Graphics/HistogramCS/Readme.docx | Bin 375549 -> 375993 bytes Samples/Graphics/HistogramCS/StepTimer.h | 2 +- Samples/Graphics/HistogramCS/pch.h | 5 +- .../Graphics/HlslCompile/DeviceResources.cpp | 215 +-- .../Graphics/HlslCompile/DeviceResources.h | 28 +- Samples/Graphics/HlslCompile/HlslCompile.cpp | 346 ++-- Samples/Graphics/HlslCompile/HlslCompile.h | 73 +- Samples/Graphics/HlslCompile/Main.cpp | 81 +- Samples/Graphics/HlslCompile/ReadMe.docx | Bin 159097 -> 159459 bytes Samples/Graphics/HlslCompile/StepTimer.h | 2 +- Samples/Graphics/HlslCompile/pch.h | 16 +- .../Graphics/MeshletCull/DeviceResources.cpp | 131 +- .../Graphics/MeshletCull/DeviceResources.h | 15 +- Samples/Graphics/MeshletCull/Main.cpp | 44 +- Samples/Graphics/MeshletCull/MeshletCull.cpp | 42 +- Samples/Graphics/MeshletCull/StepTimer.h | 2 +- Samples/Graphics/MeshletCull/pch.h | 26 +- .../MeshletInstancing/DeviceResources.cpp | 133 +- .../MeshletInstancing/DeviceResources.h | 15 +- Samples/Graphics/MeshletInstancing/Main.cpp | 41 +- .../MeshletInstancing/MeshletInstancing.cpp | 59 +- .../MeshletInstancing/MeshletInstancing.h | 8 +- .../Graphics/MeshletInstancing/ReadMe.docx | Bin 506767 -> 506982 bytes .../Graphics/MeshletInstancing/StepTimer.h | 2 +- Samples/Graphics/MeshletInstancing/pch.h | 37 +- .../Graphics/PointSprites/DeviceResources.cpp | 154 +- .../Graphics/PointSprites/DeviceResources.h | 50 +- Samples/Graphics/PointSprites/Main.cpp | 92 +- .../Graphics/PointSprites/PointSprites.cpp | 172 +- Samples/Graphics/PointSprites/PointSprites.h | 5 + Samples/Graphics/PointSprites/StepTimer.h | 2 +- Samples/Graphics/PointSprites/pch.h | 31 +- Samples/Graphics/PointSprites/readme.docx | Bin 1095083 -> 1095771 bytes .../Graphics/SimpleHDR/DeviceResources.cpp | 87 +- Samples/Graphics/SimpleHDR/DeviceResources.h | 12 +- Samples/Graphics/SimpleHDR/Main.cpp | 8 +- Samples/Graphics/SimpleHDR/SimpleHDR.cpp | 28 +- Samples/Graphics/SimpleHDR/SimpleHDR.h | 2 +- Samples/Graphics/SimpleHDR/StepTimer.h | 2 +- Samples/Graphics/SimpleHDR/pch.h | 5 +- .../SimpleMeshlet/DeviceResources.cpp | 114 +- .../Graphics/SimpleMeshlet/DeviceResources.h | 15 +- Samples/Graphics/SimpleMeshlet/Main.cpp | 10 +- Samples/Graphics/SimpleMeshlet/ReadMe.docx | Bin 542958 -> 543344 bytes .../Graphics/SimpleMeshlet/SimpleMeshlet.cpp | 67 +- .../Graphics/SimpleMeshlet/SimpleMeshlet.h | 4 +- Samples/Graphics/SimpleMeshlet/StepTimer.h | 2 +- Samples/Graphics/SimpleMeshlet/pch.h | 14 +- .../Graphics/SimplePBR/DeviceResources.cpp | 158 +- Samples/Graphics/SimplePBR/DeviceResources.h | 18 +- Samples/Graphics/SimplePBR/Main.cpp | 12 +- Samples/Graphics/SimplePBR/Readme.docx | Bin 1117232 -> 1117691 bytes Samples/Graphics/SimplePBR/SimplePBR.cpp | 90 +- Samples/Graphics/SimplePBR/SimplePBR.h | 11 +- Samples/Graphics/SimplePBR/StepTimer.h | 2 +- Samples/Graphics/SimplePBR/pch.h | 6 +- .../SmokeSimulation/DeviceResources.cpp | 215 +-- .../SmokeSimulation/DeviceResources.h | 28 +- Samples/Graphics/SmokeSimulation/Main.cpp | 81 +- .../SmokeSimulation/SmokeSimulation.cpp | 18 +- .../SmokeSimulation/SmokeSimulation.h | 12 +- Samples/Graphics/SmokeSimulation/StepTimer.h | 2 +- Samples/Graphics/SmokeSimulation/pch.h | 17 +- Samples/Graphics/SmokeSimulation/readme.docx | Bin 198811 -> 199089 bytes .../Source/DeviceResources.cpp | 100 +- .../VisibilityBuffer/Source/DeviceResources.h | 5 +- .../Graphics/VisibilityBuffer/Source/Main.cpp | 10 +- .../VisibilityBuffer/Source/StepTimer.h | 2 +- .../Source/VisibilityBuffer.cpp | 50 +- .../Source/VisibilityBuffer.h | 4 +- .../VisibilityBuffer/Readme.docx | Bin 613428 -> 614083 bytes .../VisibilityBuffer/VisibilityBuffer/pch.h | 6 +- .../SimpleBezier/DeviceResources.cpp | 37 +- .../SimpleBezier/DeviceResources.h | 3 +- .../SimpleBezier/SimpleBezier.cpp | 4 + .../SimpleCompute/DeviceResources.cpp | 37 +- .../SimpleCompute/DeviceResources.h | 3 +- .../SimpleCompute/SimpleCompute.cpp | 2 + .../SimpleDeviceAndSwapChain/Readme.docx | Bin 42964 -> 43476 bytes .../SimpleDeviceAndSwapChain.cpp | 89 +- .../SimpleDeviceAndSwapChain.h | 4 +- .../SimpleDeviceAndSwapChain/pch.h | 6 +- .../DeviceResources.cpp | 75 +- .../SimpleDynamicResources/DeviceResources.h | 5 +- .../SimpleDynamicResources/Main.cpp | 2 +- .../SimpleDynamicResources.cpp | 12 +- .../SimpleDynamicResources.h | 4 +- .../SimpleInstancing/DeviceResources.cpp | 37 +- .../SimpleInstancing/DeviceResources.h | 3 +- .../SimpleInstancing/SimpleInstancing.cpp | 2 + .../SimpleLighting/DeviceResources.cpp | 37 +- .../SimpleLighting/DeviceResources.h | 3 +- .../SimpleLighting/SimpleLighting.cpp | 2 + .../SimpleMSAA/DeviceResources.cpp | 37 +- .../SimpleMSAA/DeviceResources.h | 3 +- .../IntroGraphics/SimpleMSAA/SimpleMSAA.cpp | 2 + .../SimpleMeshShader/DeviceResources.cpp | 75 +- .../SimpleMeshShader/DeviceResources.h | 5 +- .../IntroGraphics/SimpleMeshShader/Main.cpp | 2 +- .../SimpleMeshShader/SimpleMeshShader.cpp | 12 +- .../SimpleMeshShader/SimpleMeshShader.h | 4 +- .../SimpleSamplerFeedback/DeviceResources.cpp | 44 +- .../SimpleSamplerFeedback/DeviceResources.h | 6 +- .../SimpleSamplerFeedback/Main.cpp | 26 + .../SimpleSamplerFeedback.cpp | 2 + .../SimpleSamplerFeedback.h | 3 + .../IntroGraphics/SimpleSamplerFeedback/pch.h | 3 +- .../SimpleTexture/DeviceResources.cpp | 37 +- .../SimpleTexture/DeviceResources.h | 3 +- .../SimpleTexture/SimpleTexture.cpp | 2 + .../SimpleTriangle/DeviceResources.cpp | 37 +- .../SimpleTriangle/DeviceResources.h | 3 +- .../SimpleTriangle/SimpleTriangle.cpp | 2 + .../SimpleTriangleDesktop/DeviceResources.cpp | 2 +- .../SimpleTriangleDesktop/DeviceResources.h | 1 + .../SimpleTriangleDesktop.cpp | 10 +- .../SimpleTriangleDesktop.h | 4 +- .../IntroGraphics/SimpleTriangleDesktop/pch.h | 2 + Samples/Live/Achievements/Achievements.cpp | 2 +- .../Live/Achievements/Achievements.vcxproj | 2 +- .../Achievements/Achievements.vcxproj.filters | 4 +- .../Achievements/Achievements_Readme.docx | Bin 308629 -> 308296 bytes Samples/Live/Achievements/Main.cpp | 5 +- ...oftGame.Config => MicrosoftGameConfig.mgc} | 3 +- Samples/Live/Achievements/pch.h | 4 +- .../Package_Scarlett/MicrosoftGame.config | 26 - .../Package_XboxOne/MicrosoftGame.config | 26 - .../DLCPackagePC/Package/MicrosoftGame.config | 31 - .../DownloadableContent.cpp | 319 ++-- .../DownloadableContent/DownloadableContent.h | 5 + .../DownloadableContent.vcxproj | 39 +- .../DownloadableContent.vcxproj.filters | 16 +- Samples/Live/DownloadableContent/Main.cpp | 2 +- ...Game.Config => MicrosoftGameConfig_PC.mgc} | 8 +- .../MicrosoftGameConfig_Scarlett.mgc | 38 + .../MicrosoftGameConfig_XboxOne.mgc | 38 + Samples/Live/DownloadableContent/ReadMe.docx | Bin 886818 -> 886221 bytes Samples/Live/DownloadableContent/pch.h | 4 +- .../Fundamentals_Desktop.vcxproj | 2 +- .../Fundamentals_Desktop.vcxproj.filters | 4 +- Samples/Live/Fundamentals_Desktop/Main.cpp | 2 +- ...oftGame.Config => MicrosoftGameConfig.mgc} | 3 +- Samples/Live/Fundamentals_Desktop/ReadMe.docx | Bin 924284 -> 923222 bytes Samples/Live/Fundamentals_Desktop/pch.h | 4 +- Samples/Live/InGameStore/InGameStore.cpp | 6 +- Samples/Live/InGameStore/InGameStore.vcxproj | 4 +- .../InGameStore/InGameStore.vcxproj.filters | 4 +- Samples/Live/InGameStore/Main.cpp | 2 +- ...oftGame.Config => MicrosoftGameConfig.mgc} | 3 +- Samples/Live/InGameStore/ReadMe.docx | Bin 2280767 -> 2280249 bytes Samples/Live/InGameStore/pch.h | 4 +- .../Assets/SampleUI.csv | 2 +- .../LeaderboardsEventBased.cpp | 379 ++-- .../LeaderboardsEventBased.h | 30 +- .../LeaderboardsEventBased.vcxproj | 7 +- .../LeaderboardsEventBased.vcxproj.filters | 10 +- .../LeaderboardsEventBased_ReadMe.docx | Bin 732403 -> 731919 bytes Samples/Live/LeaderboardsEventBased/Main.cpp | 4 +- ...oftGame.Config => MicrosoftGameConfig.mgc} | 5 +- Samples/Live/LeaderboardsEventBased/pch.h | 4 +- .../Assets/SampleUI.csv | 2 +- .../DeviceResources.cpp | 479 ++++- .../DeviceResources.h | 44 +- .../LeaderboardsTitleManaged/Leaderboards.cpp | 894 --------- .../LeaderboardsTitleManaged/Leaderboards.h | 159 -- .../LeaderboardsTitleManaged.cpp} | 518 +++-- .../LeaderboardsTitleManaged.h} | 90 +- .../LeaderboardsTitleManaged.sln | 84 +- .../LeaderboardsTitleManaged.vcxproj | 432 +++-- .../LeaderboardsTitleManaged.vcxproj.filters | 90 +- .../Live/LeaderboardsTitleManaged/Main.cpp | 303 ++- .../MicrosoftGame.Config | 30 - .../MicrosoftGameConfig.mgc} | 8 +- .../Live/LeaderboardsTitleManaged/ReadMe.docx | Bin 267095 -> 266729 bytes .../Live/LeaderboardsTitleManaged/Readme.md | 2 +- .../Live/LeaderboardsTitleManaged/StepTimer.h | 2 +- Samples/Live/LeaderboardsTitleManaged/pch.h | 43 +- .../Assets/SampleUI.csv | 22 - .../LeaderboardsTitleManaged_Desktop.sln | 39 - .../LeaderboardsTitleManaged_Desktop.vcxproj | 393 ---- ...boardsTitleManaged_Desktop.vcxproj.filters | 207 -- .../ReadMe.docx | Bin 193842 -> 0 bytes .../ReadMe_ja-jp.docx | Bin 194257 -> 0 bytes .../ReadMe_ko-kr.docx | Bin 194397 -> 0 bytes .../ReadMe_zh-cn.docx | Bin 194507 -> 0 bytes .../MicrosoftStoreServicesClient/Main.cpp | 2 +- ...oftGame.Config => MicrosoftGameConfig.mgc} | 16 +- .../MicrosoftStoreServicesClient.cpp | 16 +- .../MicrosoftStoreServicesClient.vcxproj | 2 +- ...crosoftStoreServicesClient.vcxproj.filters | 4 +- .../MicrosoftStoreServicesClient/ReadMe.docx | Bin 1115553 -> 1113437 bytes .../Live/MicrosoftStoreServicesClient/pch.h | 4 +- .../SimpleCrossGenMPSD/Main.cpp | 2 +- ...oftGame.Config => MicrosoftGameConfig.mgc} | 3 +- .../SimpleCrossGenMPSD/ReadMe.docx | Bin 118692 -> 117883 bytes .../SimpleCrossGenMPSD.vcxproj | 2 +- .../SimpleCrossGenMPSD.vcxproj.filters | 4 +- .../SimpleCrossGenMPSD/pch.h | 4 +- Samples/Live/SimpleHttp/SimpleHttp/Main.cpp | 2 +- ...oftGame.Config => MicrosoftGameConfig.mgc} | 3 +- .../Live/SimpleHttp/SimpleHttp/ReadMe.docx | Bin 162791 -> 162230 bytes .../Live/SimpleHttp/SimpleHttp/SimpleHttp.cpp | 6 +- .../SimpleHttp/SimpleHttp/SimpleHttp.vcxproj | 2 +- .../SimpleHttp/SimpleHttp.vcxproj.filters | 4 +- Samples/Live/SimpleHttp/SimpleHttp/pch.h | 4 +- .../Readme.md | 8 +- Samples/Live/SimpleMPA/SimpleMPA.sln | 80 + .../SimpleMPA/SimpleMPA/Assets/GamerPic.png | Bin 0 -> 766 bytes .../SimpleMPA}/Assets/LargeLogo.png | Bin .../SimpleMPA/Assets/Layouts/UILayout.json | 148 ++ .../Assets/Layouts/async-status.json | 41 + .../Assets/Layouts/console-window.json | 70 + .../SimpleMPA/Assets/Layouts/menu-button.json | 33 + .../Assets/Layouts/sample-styles.json | 110 ++ .../SimpleMPA}/Assets/Logo.png | Bin .../SimpleMPA}/Assets/SmallLogo.png | Bin .../SimpleMPA}/Assets/SplashScreen.png | Bin .../SimpleMPA}/Assets/StoreLogo.png | Bin .../Live/SimpleMPA/SimpleMPA/AsyncHelper.h | 47 + .../SimpleMPA/SimpleMPA/AsyncStatusWidget.cpp | 82 + .../SimpleMPA/SimpleMPA/AsyncStatusWidget.h | 42 + Samples/Live/SimpleMPA/SimpleMPA/Debug.cpp | 143 ++ Samples/Live/SimpleMPA/SimpleMPA/Debug.h | 24 + .../SimpleMPA}/DeviceResources.cpp | 391 ++-- .../SimpleMPA}/DeviceResources.h | 33 +- .../SimpleMPA/SimpleMPA/FriendsManager.cpp | 344 ++++ .../Live/SimpleMPA/SimpleMPA/FriendsManager.h | 46 + Samples/Live/SimpleMPA/SimpleMPA/GuidUtil.h | 30 + .../Live/SimpleMPA/SimpleMPA/MPAManager.cpp | 347 ++++ Samples/Live/SimpleMPA/SimpleMPA/MPAManager.h | 38 + Samples/Live/SimpleMPA/SimpleMPA/Main.cpp | 426 +++++ .../SimpleMPA/MicrosoftGameConfig.mgc | 27 + Samples/Live/SimpleMPA/SimpleMPA/ReadMe.docx | Bin 0 -> 162617 bytes .../SimpleMPA/Scripts/Desktop.PostBuild.bat | 32 + .../Live/SimpleMPA/SimpleMPA/SimpleMPA.cpp | 878 +++++++++ Samples/Live/SimpleMPA/SimpleMPA/SimpleMPA.h | 164 ++ .../SimpleMPA/SimpleMPA/SimpleMPA.vcxproj | 630 ++++++ .../SimpleMPA/SimpleMPA.vcxproj.filters | 179 ++ .../SimpleMPA}/StepTimer.h | 0 .../SimpleMPA}/pch.cpp | 0 .../SimpleMPA}/pch.h | 60 +- .../SimpleWebSockets/Main.cpp | 2 +- ...oftGame.Config => MicrosoftGameConfig.mgc} | 3 +- .../SimpleWebSockets/ReadMe.docx | Bin 141041 -> 140640 bytes .../SimpleWebSockets/SimpleWebSockets.vcxproj | 2 +- .../SimpleWebSockets.vcxproj.filters | 4 +- .../SimpleWebSockets/SimpleWebSockets/pch.h | 4 +- Samples/Live/SocialManager/Main.cpp | 2 +- ...oftGame.Config => MicrosoftGameConfig.mgc} | 3 +- .../Live/SocialManager/SocialManager.vcxproj | 2 +- .../SocialManager.vcxproj.filters | 4 +- .../SocialManager/SocialManager_ReadMe.docx | Bin 429310 -> 428690 bytes Samples/Live/SocialManager/pch.h | 4 +- ...oftGame.Config => MicrosoftGameConfig.mgc} | 2 +- Samples/Live/mDNS/ReadMe.docx | Bin 67385 -> 66628 bytes Samples/Live/mDNS/mDNS.vcxproj | 2 +- Samples/Live/mDNS/mDNS.vcxproj.filters | 4 +- Samples/Live/mDNS/pch.h | 4 +- Samples/Live/mDNS_Desktop/Main.cpp | 2 +- ...oftGame.Config => MicrosoftGameConfig.mgc} | 2 +- Samples/Live/mDNS_Desktop/ReadMe.docx | Bin 67160 -> 66628 bytes .../Live/mDNS_Desktop/mDNS_Desktop.vcxproj | 2 +- .../mDNS_Desktop/mDNS_Desktop.vcxproj.filters | 4 +- Samples/Live/mDNS_Desktop/pch.h | 4 +- .../System/AdvancedExceptionHandling/Main.cpp | 2 +- .../System/AsynchronousProgramming/Main.cpp | 2 +- Samples/System/Collision/Collision.cpp | 38 +- Samples/System/Collision/Collision.h | 4 +- Samples/System/Collision/DeviceResources.cpp | 100 +- Samples/System/Collision/DeviceResources.h | 5 +- Samples/System/Collision/Main.cpp | 10 +- Samples/System/Collision/StepTimer.h | 2 +- Samples/System/Collision/pch.h | 3 +- Samples/System/DataBreakPoints/Main.cpp | 2 +- .../GameInputInterfacing/DeviceResources.cpp | 105 +- .../GameInputInterfacing/DeviceResources.h | 4 +- .../GameInputInterfacing.cpp | 113 +- .../GameInputInterfacing.h | 38 +- Samples/System/GameInputInterfacing/Main.cpp | 42 +- .../System/GameInputInterfacing/StepTimer.h | 2 +- Samples/System/GameInputInterfacing/pch.h | 13 +- .../GameInputSequential/DeviceResources.cpp | 105 +- .../GameInputSequential/DeviceResources.h | 4 +- .../GameInputSequential.cpp | 36 +- .../GameInputSequential/GameInputSequential.h | 12 +- Samples/System/GameInputSequential/Main.cpp | 40 +- .../System/GameInputSequential/StepTimer.h | 2 +- Samples/System/GameInputSequential/pch.h | 11 +- Samples/System/GameSave/Assets.h | 5 +- Samples/System/GameSave/GameSave.vcxproj | 2 +- .../System/GameSave/GameSave.vcxproj.filters | 4 +- ...oftGame.Config => MicrosoftGameConfig.mgc} | 7 +- Samples/System/GameSave/Readme.docx | Bin 289175 -> 289234 bytes Samples/System/GameSave/pch.h | 4 +- .../Assets/Fonts/SegoeUI_18.spritefont | Bin 0 -> 24584 bytes .../XboxOneControllerLegendSmall.spritefont | Bin 0 -> 53736 bytes .../segoeui_18pt_sharp_24_Bold.spritefont | Bin 0 -> 26632 bytes .../Fonts/segoeui_24pt_sharp_32.spritefont | Bin 0 -> 58376 bytes .../segoeui_24pt_sharp_32_Bold.spritefont | Bin 0 -> 45064 bytes .../Fonts/segoeui_27pt_sharp_36.spritefont | Bin 0 -> 55304 bytes .../segoeui_27pt_sharp_36_Bold.spritefont | Bin 0 -> 55304 bytes .../Fonts/segoeui_36pt_sharp_48.spritefont | Bin 0 -> 91144 bytes .../segoeui_36pt_sharp_48_Bold.spritefont | Bin 0 -> 91144 bytes .../Fonts/segoeui_72pt_sharp_96.spritefont | Bin 0 -> 281608 bytes .../Fonts/segoeuisb_27pt_sharp_36.spritefont | Bin 0 -> 52232 bytes .../segoeuisb_27pt_sharp_36_Bold.spritefont | Bin 0 -> 54280 bytes .../System/GameSaveCombo/Assets/LargeLogo.png | Bin 0 -> 85767 bytes .../GameSaveCombo/Assets/Layouts/defs.json | 21 + .../Assets/Layouts/sample_layout.json | 375 ++++ .../GameSaveCombo/Assets/Layouts/styles.json | 155 ++ Samples/System/GameSaveCombo/Assets/Logo.png | Bin 0 -> 50399 bytes .../System/GameSaveCombo/Assets/SmallLogo.png | Bin 0 -> 28699 bytes .../GameSaveCombo/Assets/SplashScreen.png | Bin 0 -> 1379582 bytes .../System/GameSaveCombo/Assets/StoreLogo.png | Bin 0 -> 19335 bytes .../Assets/Textures/Button_Disabled.png | Bin 0 -> 1544 bytes .../Assets/Textures/Button_Focused.png | Bin 0 -> 1355 bytes .../Textures/Button_Focused_NoShade.png | Bin 0 -> 813 bytes .../Assets/Textures/Button_Hovered.png | Bin 0 -> 561 bytes .../Textures/Button_Hovered_NoShade.png | Bin 0 -> 508 bytes .../Assets/Textures/Button_Pressed.png | Bin 0 -> 1167 bytes .../Assets/Textures/Button_Unfocused.png | Bin 0 -> 511 bytes .../Assets/Textures/FocusBorder.png | Bin 0 -> 367 bytes .../Assets/Textures/GFX_Disabled_Button.png | Bin 0 -> 1577 bytes .../Assets/Textures/Input_Panel_Border.png | Bin 0 -> 4052 bytes .../Assets/Textures/LOGO_ATG.png | Bin 0 -> 116314 bytes .../Assets/Textures/LOGO_ATG_SMALL.png | Bin 0 -> 12687 bytes .../Assets/Textures/Slider_Disabled.png | Bin 0 -> 1476 bytes .../Assets/Textures/Slider_Focused.png | Bin 0 -> 602 bytes .../Assets/Textures/Slider_Hovered.png | Bin 0 -> 555 bytes .../Assets/Textures/Slider_Pressed.png | Bin 0 -> 551 bytes .../Assets/Textures/Slider_Unfocused.png | Bin 0 -> 551 bytes .../GameSaveCombo/Assets/Textures/checked.png | Bin 0 -> 551 bytes .../GameSaveCombo/Assets/Textures/crown.png | Bin 0 -> 713 bytes .../Assets/Textures/divider_line.png | Bin 0 -> 1128 bytes .../Assets/Textures/frame_panel_3.png | Bin 0 -> 446 bytes .../Assets/Textures/invis_panel.png | Bin 0 -> 232 bytes .../Assets/Textures/unchecked.png | Bin 0 -> 314 bytes .../System/GameSaveCombo/Assets/UILayout.json | 27 + .../System/GameSaveCombo/DeviceResources.cpp | 812 ++++++++ .../System/GameSaveCombo/DeviceResources.h | 135 ++ .../System/GameSaveCombo/GameSaveCombo.cpp | 803 ++++++++ Samples/System/GameSaveCombo/GameSaveCombo.h | 171 ++ .../System/GameSaveCombo/GameSaveCombo.sln | 80 + .../GameSaveCombo/GameSaveCombo.vcxproj | 728 +++++++ .../GameSaveCombo.vcxproj.filters | 207 ++ .../GameSaveCombo}/Main.cpp | 187 +- .../GameSaveCombo/MicrosoftGameConfig.mgc | 27 + Samples/System/GameSaveCombo/Readme.docx | Bin 0 -> 154230 bytes Samples/System/GameSaveCombo/Readme.md | 28 + Samples/System/GameSaveCombo/StepTimer.h | 190 ++ Samples/System/GameSaveCombo/pch.cpp | 10 + Samples/System/GameSaveCombo/pch.h | 152 ++ .../Assets/Fonts/SegoeUI_18.spritefont | Bin 0 -> 24584 bytes .../XboxOneControllerLegendSmall.spritefont | Bin 0 -> 53736 bytes .../segoeui_18pt_sharp_24_Bold.spritefont | Bin 0 -> 26632 bytes .../Fonts/segoeui_24pt_sharp_32.spritefont | Bin 0 -> 58376 bytes .../segoeui_24pt_sharp_32_Bold.spritefont | Bin 0 -> 45064 bytes .../Fonts/segoeui_27pt_sharp_36.spritefont | Bin 0 -> 55304 bytes .../segoeui_27pt_sharp_36_Bold.spritefont | Bin 0 -> 55304 bytes .../Fonts/segoeui_36pt_sharp_48.spritefont | Bin 0 -> 91144 bytes .../segoeui_36pt_sharp_48_Bold.spritefont | Bin 0 -> 91144 bytes .../Fonts/segoeui_72pt_sharp_96.spritefont | Bin 0 -> 281608 bytes .../Fonts/segoeuisb_27pt_sharp_36.spritefont | Bin 0 -> 52232 bytes .../segoeuisb_27pt_sharp_36_Bold.spritefont | Bin 0 -> 54280 bytes .../GameSaveFilesCombo/Assets/LargeLogo.png | Bin 0 -> 85767 bytes .../Assets/Layouts/defs.json | 21 + .../Assets/Layouts/sample_layout.json | 231 +++ .../Assets/Layouts/styles.json | 155 ++ .../System/GameSaveFilesCombo/Assets/Logo.png | Bin 0 -> 50399 bytes .../GameSaveFilesCombo/Assets/SmallLogo.png | Bin 0 -> 28699 bytes .../Assets/SplashScreen.png | Bin 0 -> 1379582 bytes .../GameSaveFilesCombo/Assets/StoreLogo.png | Bin 0 -> 19335 bytes .../Assets/Textures/Button_Disabled.png | Bin 0 -> 1544 bytes .../Assets/Textures/Button_Focused.png | Bin 0 -> 1355 bytes .../Textures/Button_Focused_NoShade.png | Bin 0 -> 813 bytes .../Assets/Textures/Button_Hovered.png | Bin 0 -> 561 bytes .../Textures/Button_Hovered_NoShade.png | Bin 0 -> 508 bytes .../Assets/Textures/Button_Pressed.png | Bin 0 -> 1167 bytes .../Assets/Textures/Button_Unfocused.png | Bin 0 -> 511 bytes .../Assets/Textures/FocusBorder.png | Bin 0 -> 367 bytes .../Assets/Textures/GFX_Disabled_Button.png | Bin 0 -> 1577 bytes .../Assets/Textures/Input_Panel_Border.png | Bin 0 -> 4052 bytes .../Assets/Textures/LOGO_ATG.png | Bin 0 -> 116314 bytes .../Assets/Textures/LOGO_ATG_SMALL.png | Bin 0 -> 12687 bytes .../Assets/Textures/Slider_Disabled.png | Bin 0 -> 1476 bytes .../Assets/Textures/Slider_Focused.png | Bin 0 -> 602 bytes .../Assets/Textures/Slider_Hovered.png | Bin 0 -> 555 bytes .../Assets/Textures/Slider_Pressed.png | Bin 0 -> 551 bytes .../Assets/Textures/Slider_Unfocused.png | Bin 0 -> 551 bytes .../Assets/Textures/checked.png | Bin 0 -> 551 bytes .../Assets/Textures/crown.png | Bin 0 -> 713 bytes .../Assets/Textures/divider_line.png | Bin 0 -> 1128 bytes .../Assets/Textures/frame_panel_3.png | Bin 0 -> 446 bytes .../Assets/Textures/invis_panel.png | Bin 0 -> 232 bytes .../Assets/Textures/unchecked.png | Bin 0 -> 314 bytes .../GameSaveFilesCombo/DeviceResources.cpp | 812 ++++++++ .../GameSaveFilesCombo/DeviceResources.h | 135 ++ .../GameSaveFilesCombo/GameSaveFilesCombo.cpp | 537 ++++++ .../GameSaveFilesCombo/GameSaveFilesCombo.h | 126 ++ .../GameSaveFilesCombo/GameSaveFilesCombo.sln | 80 + .../GameSaveFilesCombo.vcxproj | 736 +++++++ .../GameSaveFilesCombo.vcxproj.filters | 105 + Samples/System/GameSaveFilesCombo/Main.cpp | 425 +++++ .../MicrosoftGameConfig.mgc | 27 + Samples/System/GameSaveFilesCombo/Readme.docx | Bin 0 -> 116208 bytes Samples/System/GameSaveFilesCombo/Readme.md | 28 + Samples/System/GameSaveFilesCombo/StepTimer.h | 190 ++ Samples/System/GameSaveFilesCombo/pch.cpp | 10 + Samples/System/GameSaveFilesCombo/pch.h | 143 ++ Samples/System/GameSave_Desktop/Assets.h | 5 +- .../GameSave_Desktop/GameSave_Desktop.vcxproj | 2 +- .../GameSave_Desktop.vcxproj.filters | 4 +- Samples/System/GameSave_Desktop/Main.cpp | 2 +- ...oftGame.Config => MicrosoftGameConfig.mgc} | 5 +- Samples/System/GameSave_Desktop/Readme.docx | Bin 289919 -> 288897 bytes Samples/System/GameSave_Desktop/pch.h | 4 +- Samples/System/Gamepad/DeviceResources.cpp | 75 +- Samples/System/Gamepad/DeviceResources.h | 5 +- Samples/System/Gamepad/Gamepad.cpp | 4 + Samples/System/Gamepad/Main.cpp | 2 +- .../GamepadVibration/DeviceResources.cpp | 75 +- .../System/GamepadVibration/DeviceResources.h | 5 +- .../GamepadVibration/GamepadVibration.cpp | 8 +- Samples/System/GamepadVibration/Main.cpp | 2 +- .../CreateInstallPackage_PC.bat | 2 +- .../CreateInstallPackage_Scarlett.bat | 2 +- .../CreateInstallPackage_XboxOne.bat | 2 +- .../IntelligentDelivery/CreateMSIXVC_PC.bat | 2 +- .../CreateXVC_Scarlett.bat | 2 +- .../IntelligentDelivery/CreateXVC_XboxOne.bat | 2 +- .../IntelligentDelivery.vcxproj | 6 +- .../IntelligentDelivery.vcxproj.filters | 6 +- Samples/System/IntelligentDelivery/Main.cpp | 2 +- ...e_PC.Config => MicrosoftGameConfig_PC.mgc} | 0 ...onfig => MicrosoftGameConfig_Scarlett.mgc} | 0 ...Config => MicrosoftGameConfig_XboxOne.mgc} | 0 Samples/System/LocalStorage/Main.cpp | 2 +- Samples/System/LocalStorage/Readme.docx | Bin 694142 -> 694710 bytes Samples/System/LocalStorage/pch.h | 4 +- Samples/System/MouseInput/DeviceResources.cpp | 61 +- Samples/System/MouseInput/DeviceResources.h | 3 +- Samples/System/MouseInput/Main.cpp | 30 +- Samples/System/MouseInput/MouseInput.cpp | 6 +- Samples/System/MouseInput/MouseInput.h | 6 +- Samples/System/MouseInput/pch.h | 1 + Samples/System/NLSAndLocalization/Main.cpp | 2 +- .../System/SimpleDirectStorageCombo/Main.cpp | 2 +- .../SimpleDirectStorageCombo/Readme.docx | Bin 60844 -> 61371 bytes .../SampleImplementations/Cancellation.h | 4 +- .../DesktopCPUDecompression.cpp | 159 ++ .../DesktopCPUDecompression.h | 28 + .../ImplementationBase.h | 2 +- .../SampleImplementations/MultipleQueues.h | 4 +- .../RecommendedPattern.h | 4 +- .../SampleImplementations/SimpleLoad.h | 4 +- .../SampleImplementations/StatusBatch.h | 4 +- .../SampleImplementations/StatusFence.h | 2 +- ....cpp => XBoxInMemoryZLibDecompression.cpp} | 10 +- ...sion.h => XBoxInMemoryZLibDecompression.h} | 8 +- ...pression.cpp => XBoxZLibDecompression.cpp} | 10 +- ...ecompression.h => XBoxZLibDecompression.h} | 8 +- .../SimpleDirectStorageCombo.cpp | 58 +- .../SimpleDirectStorageCombo.h | 5 +- .../SimpleDirectStorageCombo.vcxproj | 14 +- .../SimpleDirectStorageCombo.vcxproj.filters | 16 +- .../SimpleDirectStorageCombo/packages.config | 2 +- .../System/SimpleExceptionHandling/Main.cpp | 2 +- .../System/SimpleFFBWheel/SimpleFFBWheel.cpp | 2 +- .../SimpleUserModel/DeviceResources.cpp | 538 ++++-- .../System/SimpleUserModel/DeviceResources.h | 67 +- Samples/System/SimpleUserModel/Main.cpp | 307 ++- ...oftGame.Config => MicrosoftGameConfig.mgc} | 13 +- .../SimpleUserModel/SimpleUserModel.cpp | 85 +- .../System/SimpleUserModel/SimpleUserModel.h | 19 +- .../SimpleUserModel/SimpleUserModel.sln | 85 +- .../SimpleUserModel/SimpleUserModel.vcxproj | 625 +++--- .../SimpleUserModel.vcxproj.filters | 438 ++--- Samples/System/SimpleUserModel/StepTimer.h | 2 +- Samples/System/SimpleUserModel/pch.h | 39 +- Samples/System/SimpleUserModel/readme.docx | Bin 143950 -> 145296 bytes Samples/System/SystemInfo/DeviceResources.cpp | 100 +- Samples/System/SystemInfo/DeviceResources.h | 5 +- Samples/System/SystemInfo/Main.cpp | 10 +- Samples/System/SystemInfo/Readme.docx | Bin 509687 -> 510532 bytes Samples/System/SystemInfo/StepTimer.h | 2 +- Samples/System/SystemInfo/SystemInfo.cpp | 273 ++- Samples/System/SystemInfo/SystemInfo.h | 8 +- Samples/System/SystemInfo/pch.h | 3 +- .../System/UserManagement/GameInputDevice.cpp | 10 +- Samples/Tools/BWOIExample/DeviceResources.cpp | 89 +- Samples/Tools/BWOIExample/DeviceResources.h | 15 +- .../Tools/BWOIExample/Directory.Build.props | 23 - Samples/Tools/BWOIExample/Game.cpp | 25 +- Samples/Tools/BWOIExample/Main.cpp | 2 +- Samples/Tools/BWOIExample/Readme.docx | Bin 60241 -> 60915 bytes Samples/Tools/BWOIExample/d3dx12.h | 66 +- Samples/Tools/BWOIExample/setenv.cmd | 26 +- .../Desktop/CMakeGDKDesktop.cmake | 96 +- .../Tools/CMakeExample/Desktop/CMakeLists.txt | 36 +- .../CMakeExample/Desktop/CMakePresets.json | 2 +- Samples/Tools/CMakeExample/Desktop/d3dx12.h | 66 +- .../Tools/CMakeExample/DeviceResources.cpp | 89 +- Samples/Tools/CMakeExample/DeviceResources.h | 15 +- Samples/Tools/CMakeExample/Game.cpp | 25 +- Samples/Tools/CMakeExample/Main.cpp | 2 +- Samples/Tools/CMakeExample/Readme.docx | Bin 142040 -> 144645 bytes .../Scarlett/CMakeGDKScarlett.cmake | 99 +- .../CMakeExample/Scarlett/CMakeLists.txt | 19 +- .../CMakeExample/Scarlett/CMakePresets.json | 2 +- .../XboxOne/CMakeGDKXboxOne.cmake | 99 +- .../Tools/CMakeExample/XboxOne/CMakeLists.txt | 19 +- .../CMakeExample/XboxOne/CMakePresets.json | 2 +- Samples/Tools/CMakeGDKExample/CMakeLists.txt | 8 +- .../Tools/CMakeGDKExample/CMakePresets.json | 14 +- .../Tools/CMakeGDKExample/CMakeSettings.json | 106 -- .../Tools/CMakeGDKExample/DeviceResources.cpp | 89 +- .../Tools/CMakeGDKExample/DeviceResources.h | 15 +- Samples/Tools/CMakeGDKExample/Game.cpp | 25 +- Samples/Tools/CMakeGDKExample/Main.cpp | 2 +- ...oftGame.Config => MicrosoftGameConfig.mgc} | 0 Samples/Tools/CMakeGDKExample/Readme.docx | Bin 163735 -> 165541 bytes Samples/Tools/CMakeGDKExample/d3dx12.h | 66 +- .../CMakeGDKExample/grdk_toolchain.cmake | 2 +- .../CMakeGDKExample/gxdk_toolchain.cmake | 2 +- .../CMakeGDKExample/gxdk_xs_toolchain.cmake | 2 +- .../CMakeXboxConsoleApp/CMakeGDKXbox.cmake | 97 +- .../Tools/CMakeXboxConsoleApp/CMakeLists.txt | 20 +- .../CMakeXboxConsoleApp/CMakePresets.json | 2 +- Samples/Tools/CMakeXboxConsoleApp/Readme.docx | Bin 103535 -> 103912 bytes .../Tools/CacheTestCombo/CacheTestCombo.cpp | 10 +- Samples/Tools/CacheTestCombo/Main.cpp | 2 +- .../ConverterApp/ConverterApp.vcxproj | 6 +- .../ConverterApp/ConverterApp.vcxproj.filters | 1 + .../ConverterApp/Importer.cpp | 363 ++++ .../MeshletConverter/ConverterApp/Importer.h | 1 + .../ConverterApp/MeshProcessor.cpp | 101 +- .../ConverterApp/MeshProcessor.h | 21 +- .../MeshletConverter/ConverterApp/SDKMesh.h | 360 ++++ .../MeshletConverter/ConverterApp/main.cpp | 16 +- .../ConverterApp/packages.config | 2 +- Samples/Tools/MeshletConverter/ReadMe.docx | Bin 69609 -> 69894 bytes .../MeshletConverter/Runtime/Runtime.vcxproj | 1 + Samples/Tools/OSPrimitiveTestCombo/Main.cpp | 2 +- .../OSPrimitiveTestCombo.cpp | 10 +- .../Tools/OSPrimitiveTestCombo/PerfRun.cpp | 2 +- .../Assets/LargeLogo.png | Bin 0 -> 75093 bytes .../CloudVariableReplacement/Assets/Logo.png | Bin 0 -> 50631 bytes .../Assets/SmallLogo.png | Bin 0 -> 24172 bytes .../Assets/SplashScreen.png | Bin 0 -> 1219432 bytes .../Assets/StoreLogo.png | Bin 0 -> 19335 bytes .../CloudVariableReplacement.cpp | 428 +++++ .../CloudVariableReplacement.h | 125 ++ .../CloudVariableReplacement.sln | 56 + .../CloudVariableReplacement.vcxproj | 418 ++++ .../CloudVariableReplacement.vcxproj.filters | 114 ++ .../DeviceResources.cpp | 502 +++++ .../DeviceResources.h | 134 ++ .../xCloud/CloudVariableReplacement/Main.cpp | 221 +++ .../MicrosoftGame.Config | 25 + .../CloudVariableReplacement/Readme.docx | Bin 0 -> 104876 bytes .../xCloud/CloudVariableReplacement/Readme.md | 28 + .../Readme_ja-JP.docx | Bin 0 -> 96236 bytes .../Readme_ko-KR.docx | Bin 0 -> 96125 bytes .../Readme_zh-CN.docx | Bin 0 -> 96100 bytes .../CloudVariableReplacement/StepTimer.h | 190 ++ .../xCloud/CloudVariableReplacement/pch.cpp | 10 + Samples/xCloud/CloudVariableReplacement/pch.h | 127 ++ .../sample-layouts/context.json | 9 + .../fighting-variable-replacement.json | 103 + .../standard-variable-replacement.json | 116 ++ .../CustomResolution/Assets/LOGO_ATG.png | Bin 0 -> 116314 bytes .../CustomResolution/Assets/LargeLogo.png | Bin 0 -> 77230 bytes .../xCloud/CustomResolution/Assets/Logo.png | Bin 0 -> 54793 bytes .../CustomResolution/Assets/SmallLogo.png | Bin 0 -> 30559 bytes .../CustomResolution/Assets/SplashScreen.png | Bin 0 -> 1383444 bytes .../CustomResolution/Assets/StoreLogo.png | Bin 0 -> 19335 bytes .../CustomResolution/Assets/menuitem.json | 80 + .../CustomResolution/Assets/ui_layout.json | 267 +++ .../Assets/xboxlogohorizontal.png | Bin 0 -> 152309 bytes .../Assets/xboxlogovertical.png | Bin 0 -> 36091 bytes .../CustomResolution/CustomResolution.cpp | 830 ++++++++ .../CustomResolution/CustomResolution.h | 196 ++ .../CustomResolution/CustomResolution.sln | 62 + .../CustomResolution/CustomResolution.vcxproj | 538 ++++++ .../CustomResolution.vcxproj.filters | 186 ++ .../CustomResolution/DeviceResources.cpp | 470 +++++ .../xCloud/CustomResolution/DeviceResources.h | 138 ++ .../CustomResolution/EnforceGpuTime.hlsl | 41 + .../xCloud/CustomResolution/GetStartTime.hlsl | 30 + Samples/xCloud/CustomResolution/Main.cpp | 177 ++ .../CustomResolution/MicrosoftGameConfig.mgc | 25 + Samples/xCloud/CustomResolution/ReadMe.docx | Bin 0 -> 239522 bytes Samples/xCloud/CustomResolution/Readme.md | 28 + .../Readme/ScreenshotTallAspect.png | Bin 0 -> 103276 bytes .../Readme/ScreenshotWideAspect.png | Bin 0 -> 86383 bytes .../CustomResolution/Readme/atgfooter.png | Bin 0 -> 1757 bytes .../CustomResolution/Readme/atgheader.png | Bin 0 -> 21932 bytes .../xCloud/CustomResolution/ResolutionSet.cpp | 197 ++ .../xCloud/CustomResolution/ResolutionSet.h | 69 + Samples/xCloud/CustomResolution/SpherePS.hlsl | 26 + Samples/xCloud/CustomResolution/SphereVS.hlsl | 32 + Samples/xCloud/CustomResolution/StepTimer.h | 190 ++ Samples/xCloud/CustomResolution/Time.hlsli | 20 + Samples/xCloud/CustomResolution/pch.cpp | 10 + Samples/xCloud/CustomResolution/pch.h | 127 ++ 757 files changed, 32626 insertions(+), 11449 deletions(-) rename Samples/Audio/InGameChat/{MicrosoftGame.Config => MicrosoftGameConfig.mgc} (93%) rename Samples/Live/Achievements/{MicrosoftGame.Config => MicrosoftGameConfig.mgc} (93%) delete mode 100644 Samples/Live/DownloadableContent/DLCPackage/Package_Scarlett/MicrosoftGame.config delete mode 100644 Samples/Live/DownloadableContent/DLCPackage/Package_XboxOne/MicrosoftGame.config delete mode 100644 Samples/Live/DownloadableContent/DLCPackagePC/Package/MicrosoftGame.config rename Samples/Live/DownloadableContent/{MicrosoftGame.Config => MicrosoftGameConfig_PC.mgc} (82%) create mode 100644 Samples/Live/DownloadableContent/MicrosoftGameConfig_Scarlett.mgc create mode 100644 Samples/Live/DownloadableContent/MicrosoftGameConfig_XboxOne.mgc rename Samples/Live/Fundamentals_Desktop/{MicrosoftGame.Config => MicrosoftGameConfig.mgc} (93%) rename Samples/Live/InGameStore/{MicrosoftGame.Config => MicrosoftGameConfig.mgc} (94%) rename Samples/Live/LeaderboardsEventBased/{MicrosoftGame.Config => MicrosoftGameConfig.mgc} (87%) delete mode 100644 Samples/Live/LeaderboardsTitleManaged/Leaderboards.cpp delete mode 100644 Samples/Live/LeaderboardsTitleManaged/Leaderboards.h rename Samples/Live/{LeaderboardsTitleManaged_Desktop/Leaderboards.cpp => LeaderboardsTitleManaged/LeaderboardsTitleManaged.cpp} (66%) rename Samples/Live/{LeaderboardsTitleManaged_Desktop/Leaderboards.h => LeaderboardsTitleManaged/LeaderboardsTitleManaged.h} (66%) delete mode 100644 Samples/Live/LeaderboardsTitleManaged/MicrosoftGame.Config rename Samples/Live/{LeaderboardsTitleManaged_Desktop/MicrosoftGame.Config => LeaderboardsTitleManaged/MicrosoftGameConfig.mgc} (81%) delete mode 100644 Samples/Live/LeaderboardsTitleManaged_Desktop/Assets/SampleUI.csv delete mode 100644 Samples/Live/LeaderboardsTitleManaged_Desktop/LeaderboardsTitleManaged_Desktop.sln delete mode 100644 Samples/Live/LeaderboardsTitleManaged_Desktop/LeaderboardsTitleManaged_Desktop.vcxproj delete mode 100644 Samples/Live/LeaderboardsTitleManaged_Desktop/LeaderboardsTitleManaged_Desktop.vcxproj.filters delete mode 100644 Samples/Live/LeaderboardsTitleManaged_Desktop/ReadMe.docx delete mode 100644 Samples/Live/LeaderboardsTitleManaged_Desktop/ReadMe_ja-jp.docx delete mode 100644 Samples/Live/LeaderboardsTitleManaged_Desktop/ReadMe_ko-kr.docx delete mode 100644 Samples/Live/LeaderboardsTitleManaged_Desktop/ReadMe_zh-cn.docx rename Samples/Live/MicrosoftStoreServicesClient/{MicrosoftGame.Config => MicrosoftGameConfig.mgc} (66%) rename Samples/Live/SimpleCrossGenMPSD/SimpleCrossGenMPSD/{MicrosoftGame.Config => MicrosoftGameConfig.mgc} (93%) rename Samples/Live/SimpleHttp/SimpleHttp/{MicrosoftGame.Config => MicrosoftGameConfig.mgc} (93%) rename Samples/Live/{LeaderboardsTitleManaged_Desktop => SimpleMPA}/Readme.md (75%) create mode 100644 Samples/Live/SimpleMPA/SimpleMPA.sln create mode 100644 Samples/Live/SimpleMPA/SimpleMPA/Assets/GamerPic.png rename Samples/Live/{LeaderboardsTitleManaged_Desktop => SimpleMPA/SimpleMPA}/Assets/LargeLogo.png (100%) create mode 100644 Samples/Live/SimpleMPA/SimpleMPA/Assets/Layouts/UILayout.json create mode 100644 Samples/Live/SimpleMPA/SimpleMPA/Assets/Layouts/async-status.json create mode 100644 Samples/Live/SimpleMPA/SimpleMPA/Assets/Layouts/console-window.json create mode 100644 Samples/Live/SimpleMPA/SimpleMPA/Assets/Layouts/menu-button.json create mode 100644 Samples/Live/SimpleMPA/SimpleMPA/Assets/Layouts/sample-styles.json rename Samples/Live/{LeaderboardsTitleManaged_Desktop => SimpleMPA/SimpleMPA}/Assets/Logo.png (100%) rename Samples/Live/{LeaderboardsTitleManaged_Desktop => SimpleMPA/SimpleMPA}/Assets/SmallLogo.png (100%) rename Samples/Live/{LeaderboardsTitleManaged_Desktop => SimpleMPA/SimpleMPA}/Assets/SplashScreen.png (100%) rename Samples/Live/{LeaderboardsTitleManaged_Desktop => SimpleMPA/SimpleMPA}/Assets/StoreLogo.png (100%) create mode 100644 Samples/Live/SimpleMPA/SimpleMPA/AsyncHelper.h create mode 100644 Samples/Live/SimpleMPA/SimpleMPA/AsyncStatusWidget.cpp create mode 100644 Samples/Live/SimpleMPA/SimpleMPA/AsyncStatusWidget.h create mode 100644 Samples/Live/SimpleMPA/SimpleMPA/Debug.cpp create mode 100644 Samples/Live/SimpleMPA/SimpleMPA/Debug.h rename Samples/Live/{LeaderboardsTitleManaged_Desktop => SimpleMPA/SimpleMPA}/DeviceResources.cpp (68%) rename Samples/Live/{LeaderboardsTitleManaged_Desktop => SimpleMPA/SimpleMPA}/DeviceResources.h (86%) create mode 100644 Samples/Live/SimpleMPA/SimpleMPA/FriendsManager.cpp create mode 100644 Samples/Live/SimpleMPA/SimpleMPA/FriendsManager.h create mode 100644 Samples/Live/SimpleMPA/SimpleMPA/GuidUtil.h create mode 100644 Samples/Live/SimpleMPA/SimpleMPA/MPAManager.cpp create mode 100644 Samples/Live/SimpleMPA/SimpleMPA/MPAManager.h create mode 100644 Samples/Live/SimpleMPA/SimpleMPA/Main.cpp create mode 100644 Samples/Live/SimpleMPA/SimpleMPA/MicrosoftGameConfig.mgc create mode 100644 Samples/Live/SimpleMPA/SimpleMPA/ReadMe.docx create mode 100644 Samples/Live/SimpleMPA/SimpleMPA/Scripts/Desktop.PostBuild.bat create mode 100644 Samples/Live/SimpleMPA/SimpleMPA/SimpleMPA.cpp create mode 100644 Samples/Live/SimpleMPA/SimpleMPA/SimpleMPA.h create mode 100644 Samples/Live/SimpleMPA/SimpleMPA/SimpleMPA.vcxproj create mode 100644 Samples/Live/SimpleMPA/SimpleMPA/SimpleMPA.vcxproj.filters rename Samples/Live/{LeaderboardsTitleManaged_Desktop => SimpleMPA/SimpleMPA}/StepTimer.h (100%) rename Samples/Live/{LeaderboardsTitleManaged_Desktop => SimpleMPA/SimpleMPA}/pch.cpp (100%) rename Samples/Live/{LeaderboardsTitleManaged_Desktop => SimpleMPA/SimpleMPA}/pch.h (81%) rename Samples/Live/SimpleWebSockets/SimpleWebSockets/{MicrosoftGame.Config => MicrosoftGameConfig.mgc} (92%) rename Samples/Live/SocialManager/{MicrosoftGame.Config => MicrosoftGameConfig.mgc} (92%) rename Samples/Live/mDNS/{MicrosoftGame.Config => MicrosoftGameConfig.mgc} (97%) rename Samples/Live/mDNS_Desktop/{MicrosoftGame.Config => MicrosoftGameConfig.mgc} (97%) rename Samples/System/GameSave/{MicrosoftGame.Config => MicrosoftGameConfig.mgc} (87%) create mode 100644 Samples/System/GameSaveCombo/Assets/Fonts/SegoeUI_18.spritefont create mode 100644 Samples/System/GameSaveCombo/Assets/Fonts/XboxOneControllerLegendSmall.spritefont create mode 100644 Samples/System/GameSaveCombo/Assets/Fonts/segoeui_18pt_sharp_24_Bold.spritefont create mode 100644 Samples/System/GameSaveCombo/Assets/Fonts/segoeui_24pt_sharp_32.spritefont create mode 100644 Samples/System/GameSaveCombo/Assets/Fonts/segoeui_24pt_sharp_32_Bold.spritefont create mode 100644 Samples/System/GameSaveCombo/Assets/Fonts/segoeui_27pt_sharp_36.spritefont create mode 100644 Samples/System/GameSaveCombo/Assets/Fonts/segoeui_27pt_sharp_36_Bold.spritefont create mode 100644 Samples/System/GameSaveCombo/Assets/Fonts/segoeui_36pt_sharp_48.spritefont create mode 100644 Samples/System/GameSaveCombo/Assets/Fonts/segoeui_36pt_sharp_48_Bold.spritefont create mode 100644 Samples/System/GameSaveCombo/Assets/Fonts/segoeui_72pt_sharp_96.spritefont create mode 100644 Samples/System/GameSaveCombo/Assets/Fonts/segoeuisb_27pt_sharp_36.spritefont create mode 100644 Samples/System/GameSaveCombo/Assets/Fonts/segoeuisb_27pt_sharp_36_Bold.spritefont create mode 100644 Samples/System/GameSaveCombo/Assets/LargeLogo.png create mode 100644 Samples/System/GameSaveCombo/Assets/Layouts/defs.json create mode 100644 Samples/System/GameSaveCombo/Assets/Layouts/sample_layout.json create mode 100644 Samples/System/GameSaveCombo/Assets/Layouts/styles.json create mode 100644 Samples/System/GameSaveCombo/Assets/Logo.png create mode 100644 Samples/System/GameSaveCombo/Assets/SmallLogo.png create mode 100644 Samples/System/GameSaveCombo/Assets/SplashScreen.png create mode 100644 Samples/System/GameSaveCombo/Assets/StoreLogo.png create mode 100644 Samples/System/GameSaveCombo/Assets/Textures/Button_Disabled.png create mode 100644 Samples/System/GameSaveCombo/Assets/Textures/Button_Focused.png create mode 100644 Samples/System/GameSaveCombo/Assets/Textures/Button_Focused_NoShade.png create mode 100644 Samples/System/GameSaveCombo/Assets/Textures/Button_Hovered.png create mode 100644 Samples/System/GameSaveCombo/Assets/Textures/Button_Hovered_NoShade.png create mode 100644 Samples/System/GameSaveCombo/Assets/Textures/Button_Pressed.png create mode 100644 Samples/System/GameSaveCombo/Assets/Textures/Button_Unfocused.png create mode 100644 Samples/System/GameSaveCombo/Assets/Textures/FocusBorder.png create mode 100644 Samples/System/GameSaveCombo/Assets/Textures/GFX_Disabled_Button.png create mode 100644 Samples/System/GameSaveCombo/Assets/Textures/Input_Panel_Border.png create mode 100644 Samples/System/GameSaveCombo/Assets/Textures/LOGO_ATG.png create mode 100644 Samples/System/GameSaveCombo/Assets/Textures/LOGO_ATG_SMALL.png create mode 100644 Samples/System/GameSaveCombo/Assets/Textures/Slider_Disabled.png create mode 100644 Samples/System/GameSaveCombo/Assets/Textures/Slider_Focused.png create mode 100644 Samples/System/GameSaveCombo/Assets/Textures/Slider_Hovered.png create mode 100644 Samples/System/GameSaveCombo/Assets/Textures/Slider_Pressed.png create mode 100644 Samples/System/GameSaveCombo/Assets/Textures/Slider_Unfocused.png create mode 100644 Samples/System/GameSaveCombo/Assets/Textures/checked.png create mode 100644 Samples/System/GameSaveCombo/Assets/Textures/crown.png create mode 100644 Samples/System/GameSaveCombo/Assets/Textures/divider_line.png create mode 100644 Samples/System/GameSaveCombo/Assets/Textures/frame_panel_3.png create mode 100644 Samples/System/GameSaveCombo/Assets/Textures/invis_panel.png create mode 100644 Samples/System/GameSaveCombo/Assets/Textures/unchecked.png create mode 100644 Samples/System/GameSaveCombo/Assets/UILayout.json create mode 100644 Samples/System/GameSaveCombo/DeviceResources.cpp create mode 100644 Samples/System/GameSaveCombo/DeviceResources.h create mode 100644 Samples/System/GameSaveCombo/GameSaveCombo.cpp create mode 100644 Samples/System/GameSaveCombo/GameSaveCombo.h create mode 100644 Samples/System/GameSaveCombo/GameSaveCombo.sln create mode 100644 Samples/System/GameSaveCombo/GameSaveCombo.vcxproj create mode 100644 Samples/System/GameSaveCombo/GameSaveCombo.vcxproj.filters rename Samples/{Live/LeaderboardsTitleManaged_Desktop => System/GameSaveCombo}/Main.cpp (74%) create mode 100644 Samples/System/GameSaveCombo/MicrosoftGameConfig.mgc create mode 100644 Samples/System/GameSaveCombo/Readme.docx create mode 100644 Samples/System/GameSaveCombo/Readme.md create mode 100644 Samples/System/GameSaveCombo/StepTimer.h create mode 100644 Samples/System/GameSaveCombo/pch.cpp create mode 100644 Samples/System/GameSaveCombo/pch.h create mode 100644 Samples/System/GameSaveFilesCombo/Assets/Fonts/SegoeUI_18.spritefont create mode 100644 Samples/System/GameSaveFilesCombo/Assets/Fonts/XboxOneControllerLegendSmall.spritefont create mode 100644 Samples/System/GameSaveFilesCombo/Assets/Fonts/segoeui_18pt_sharp_24_Bold.spritefont create mode 100644 Samples/System/GameSaveFilesCombo/Assets/Fonts/segoeui_24pt_sharp_32.spritefont create mode 100644 Samples/System/GameSaveFilesCombo/Assets/Fonts/segoeui_24pt_sharp_32_Bold.spritefont create mode 100644 Samples/System/GameSaveFilesCombo/Assets/Fonts/segoeui_27pt_sharp_36.spritefont create mode 100644 Samples/System/GameSaveFilesCombo/Assets/Fonts/segoeui_27pt_sharp_36_Bold.spritefont create mode 100644 Samples/System/GameSaveFilesCombo/Assets/Fonts/segoeui_36pt_sharp_48.spritefont create mode 100644 Samples/System/GameSaveFilesCombo/Assets/Fonts/segoeui_36pt_sharp_48_Bold.spritefont create mode 100644 Samples/System/GameSaveFilesCombo/Assets/Fonts/segoeui_72pt_sharp_96.spritefont create mode 100644 Samples/System/GameSaveFilesCombo/Assets/Fonts/segoeuisb_27pt_sharp_36.spritefont create mode 100644 Samples/System/GameSaveFilesCombo/Assets/Fonts/segoeuisb_27pt_sharp_36_Bold.spritefont create mode 100644 Samples/System/GameSaveFilesCombo/Assets/LargeLogo.png create mode 100644 Samples/System/GameSaveFilesCombo/Assets/Layouts/defs.json create mode 100644 Samples/System/GameSaveFilesCombo/Assets/Layouts/sample_layout.json create mode 100644 Samples/System/GameSaveFilesCombo/Assets/Layouts/styles.json create mode 100644 Samples/System/GameSaveFilesCombo/Assets/Logo.png create mode 100644 Samples/System/GameSaveFilesCombo/Assets/SmallLogo.png create mode 100644 Samples/System/GameSaveFilesCombo/Assets/SplashScreen.png create mode 100644 Samples/System/GameSaveFilesCombo/Assets/StoreLogo.png create mode 100644 Samples/System/GameSaveFilesCombo/Assets/Textures/Button_Disabled.png create mode 100644 Samples/System/GameSaveFilesCombo/Assets/Textures/Button_Focused.png create mode 100644 Samples/System/GameSaveFilesCombo/Assets/Textures/Button_Focused_NoShade.png create mode 100644 Samples/System/GameSaveFilesCombo/Assets/Textures/Button_Hovered.png create mode 100644 Samples/System/GameSaveFilesCombo/Assets/Textures/Button_Hovered_NoShade.png create mode 100644 Samples/System/GameSaveFilesCombo/Assets/Textures/Button_Pressed.png create mode 100644 Samples/System/GameSaveFilesCombo/Assets/Textures/Button_Unfocused.png create mode 100644 Samples/System/GameSaveFilesCombo/Assets/Textures/FocusBorder.png create mode 100644 Samples/System/GameSaveFilesCombo/Assets/Textures/GFX_Disabled_Button.png create mode 100644 Samples/System/GameSaveFilesCombo/Assets/Textures/Input_Panel_Border.png create mode 100644 Samples/System/GameSaveFilesCombo/Assets/Textures/LOGO_ATG.png create mode 100644 Samples/System/GameSaveFilesCombo/Assets/Textures/LOGO_ATG_SMALL.png create mode 100644 Samples/System/GameSaveFilesCombo/Assets/Textures/Slider_Disabled.png create mode 100644 Samples/System/GameSaveFilesCombo/Assets/Textures/Slider_Focused.png create mode 100644 Samples/System/GameSaveFilesCombo/Assets/Textures/Slider_Hovered.png create mode 100644 Samples/System/GameSaveFilesCombo/Assets/Textures/Slider_Pressed.png create mode 100644 Samples/System/GameSaveFilesCombo/Assets/Textures/Slider_Unfocused.png create mode 100644 Samples/System/GameSaveFilesCombo/Assets/Textures/checked.png create mode 100644 Samples/System/GameSaveFilesCombo/Assets/Textures/crown.png create mode 100644 Samples/System/GameSaveFilesCombo/Assets/Textures/divider_line.png create mode 100644 Samples/System/GameSaveFilesCombo/Assets/Textures/frame_panel_3.png create mode 100644 Samples/System/GameSaveFilesCombo/Assets/Textures/invis_panel.png create mode 100644 Samples/System/GameSaveFilesCombo/Assets/Textures/unchecked.png create mode 100644 Samples/System/GameSaveFilesCombo/DeviceResources.cpp create mode 100644 Samples/System/GameSaveFilesCombo/DeviceResources.h create mode 100644 Samples/System/GameSaveFilesCombo/GameSaveFilesCombo.cpp create mode 100644 Samples/System/GameSaveFilesCombo/GameSaveFilesCombo.h create mode 100644 Samples/System/GameSaveFilesCombo/GameSaveFilesCombo.sln create mode 100644 Samples/System/GameSaveFilesCombo/GameSaveFilesCombo.vcxproj create mode 100644 Samples/System/GameSaveFilesCombo/GameSaveFilesCombo.vcxproj.filters create mode 100644 Samples/System/GameSaveFilesCombo/Main.cpp create mode 100644 Samples/System/GameSaveFilesCombo/MicrosoftGameConfig.mgc create mode 100644 Samples/System/GameSaveFilesCombo/Readme.docx create mode 100644 Samples/System/GameSaveFilesCombo/Readme.md create mode 100644 Samples/System/GameSaveFilesCombo/StepTimer.h create mode 100644 Samples/System/GameSaveFilesCombo/pch.cpp create mode 100644 Samples/System/GameSaveFilesCombo/pch.h rename Samples/System/GameSave_Desktop/{MicrosoftGame.Config => MicrosoftGameConfig.mgc} (90%) rename Samples/System/IntelligentDelivery/{MicrosoftGame_PC.Config => MicrosoftGameConfig_PC.mgc} (100%) rename Samples/System/IntelligentDelivery/{MicrosoftGame_Scarlett.Config => MicrosoftGameConfig_Scarlett.mgc} (100%) rename Samples/System/IntelligentDelivery/{MicrosoftGame_XboxOne.Config => MicrosoftGameConfig_XboxOne.mgc} (100%) create mode 100644 Samples/System/SimpleDirectStorageCombo/SampleImplementations/DesktopCPUDecompression.cpp create mode 100644 Samples/System/SimpleDirectStorageCombo/SampleImplementations/DesktopCPUDecompression.h rename Samples/System/SimpleDirectStorageCombo/SampleImplementations/{InMemoryZLibDecompression.cpp => XBoxInMemoryZLibDecompression.cpp} (93%) rename Samples/System/SimpleDirectStorageCombo/SampleImplementations/{InMemoryZLibDecompression.h => XBoxInMemoryZLibDecompression.h} (85%) rename Samples/System/SimpleDirectStorageCombo/SampleImplementations/{ZLibDecompression.cpp => XBoxZLibDecompression.cpp} (91%) rename Samples/System/SimpleDirectStorageCombo/SampleImplementations/{ZLibDecompression.h => XBoxZLibDecompression.h} (82%) rename Samples/System/SimpleUserModel/{MicrosoftGame.Config => MicrosoftGameConfig.mgc} (69%) delete mode 100644 Samples/Tools/CMakeGDKExample/CMakeSettings.json rename Samples/Tools/CMakeGDKExample/{MicrosoftGame.Config => MicrosoftGameConfig.mgc} (100%) create mode 100644 Samples/Tools/MeshletConverter/ConverterApp/SDKMesh.h create mode 100644 Samples/xCloud/CloudVariableReplacement/Assets/LargeLogo.png create mode 100644 Samples/xCloud/CloudVariableReplacement/Assets/Logo.png create mode 100644 Samples/xCloud/CloudVariableReplacement/Assets/SmallLogo.png create mode 100644 Samples/xCloud/CloudVariableReplacement/Assets/SplashScreen.png create mode 100644 Samples/xCloud/CloudVariableReplacement/Assets/StoreLogo.png create mode 100644 Samples/xCloud/CloudVariableReplacement/CloudVariableReplacement.cpp create mode 100644 Samples/xCloud/CloudVariableReplacement/CloudVariableReplacement.h create mode 100644 Samples/xCloud/CloudVariableReplacement/CloudVariableReplacement.sln create mode 100644 Samples/xCloud/CloudVariableReplacement/CloudVariableReplacement.vcxproj create mode 100644 Samples/xCloud/CloudVariableReplacement/CloudVariableReplacement.vcxproj.filters create mode 100644 Samples/xCloud/CloudVariableReplacement/DeviceResources.cpp create mode 100644 Samples/xCloud/CloudVariableReplacement/DeviceResources.h create mode 100644 Samples/xCloud/CloudVariableReplacement/Main.cpp create mode 100644 Samples/xCloud/CloudVariableReplacement/MicrosoftGame.Config create mode 100644 Samples/xCloud/CloudVariableReplacement/Readme.docx create mode 100644 Samples/xCloud/CloudVariableReplacement/Readme.md create mode 100644 Samples/xCloud/CloudVariableReplacement/Readme_ja-JP.docx create mode 100644 Samples/xCloud/CloudVariableReplacement/Readme_ko-KR.docx create mode 100644 Samples/xCloud/CloudVariableReplacement/Readme_zh-CN.docx create mode 100644 Samples/xCloud/CloudVariableReplacement/StepTimer.h create mode 100644 Samples/xCloud/CloudVariableReplacement/pch.cpp create mode 100644 Samples/xCloud/CloudVariableReplacement/pch.h create mode 100644 Samples/xCloud/CloudVariableReplacement/sample-layouts/context.json create mode 100644 Samples/xCloud/CloudVariableReplacement/sample-layouts/layouts/neutral/fighting-variable-replacement.json create mode 100644 Samples/xCloud/CloudVariableReplacement/sample-layouts/layouts/neutral/standard-variable-replacement.json create mode 100644 Samples/xCloud/CustomResolution/Assets/LOGO_ATG.png create mode 100644 Samples/xCloud/CustomResolution/Assets/LargeLogo.png create mode 100644 Samples/xCloud/CustomResolution/Assets/Logo.png create mode 100644 Samples/xCloud/CustomResolution/Assets/SmallLogo.png create mode 100644 Samples/xCloud/CustomResolution/Assets/SplashScreen.png create mode 100644 Samples/xCloud/CustomResolution/Assets/StoreLogo.png create mode 100644 Samples/xCloud/CustomResolution/Assets/menuitem.json create mode 100644 Samples/xCloud/CustomResolution/Assets/ui_layout.json create mode 100644 Samples/xCloud/CustomResolution/Assets/xboxlogohorizontal.png create mode 100644 Samples/xCloud/CustomResolution/Assets/xboxlogovertical.png create mode 100644 Samples/xCloud/CustomResolution/CustomResolution.cpp create mode 100644 Samples/xCloud/CustomResolution/CustomResolution.h create mode 100644 Samples/xCloud/CustomResolution/CustomResolution.sln create mode 100644 Samples/xCloud/CustomResolution/CustomResolution.vcxproj create mode 100644 Samples/xCloud/CustomResolution/CustomResolution.vcxproj.filters create mode 100644 Samples/xCloud/CustomResolution/DeviceResources.cpp create mode 100644 Samples/xCloud/CustomResolution/DeviceResources.h create mode 100644 Samples/xCloud/CustomResolution/EnforceGpuTime.hlsl create mode 100644 Samples/xCloud/CustomResolution/GetStartTime.hlsl create mode 100644 Samples/xCloud/CustomResolution/Main.cpp create mode 100644 Samples/xCloud/CustomResolution/MicrosoftGameConfig.mgc create mode 100644 Samples/xCloud/CustomResolution/ReadMe.docx create mode 100644 Samples/xCloud/CustomResolution/Readme.md create mode 100644 Samples/xCloud/CustomResolution/Readme/ScreenshotTallAspect.png create mode 100644 Samples/xCloud/CustomResolution/Readme/ScreenshotWideAspect.png create mode 100644 Samples/xCloud/CustomResolution/Readme/atgfooter.png create mode 100644 Samples/xCloud/CustomResolution/Readme/atgheader.png create mode 100644 Samples/xCloud/CustomResolution/ResolutionSet.cpp create mode 100644 Samples/xCloud/CustomResolution/ResolutionSet.h create mode 100644 Samples/xCloud/CustomResolution/SpherePS.hlsl create mode 100644 Samples/xCloud/CustomResolution/SphereVS.hlsl create mode 100644 Samples/xCloud/CustomResolution/StepTimer.h create mode 100644 Samples/xCloud/CustomResolution/Time.hlsli create mode 100644 Samples/xCloud/CustomResolution/pch.cpp create mode 100644 Samples/xCloud/CustomResolution/pch.h diff --git a/Kits/ATGTK/ControllerHelp.cpp b/Kits/ATGTK/ControllerHelp.cpp index 485868e..0883782 100644 --- a/Kits/ATGTK/ControllerHelp.cpp +++ b/Kits/ATGTK/ControllerHelp.cpp @@ -974,6 +974,8 @@ void ATG::Help::RestoreDevice(ID3D11DeviceContext* context) ); } + const DDS_LOADER_FLAGS loadFlags = m_linearColors ? DDS_LOADER_FORCE_SRGB : DDS_LOADER_DEFAULT; + #if !defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP) wchar_t buff[MAX_PATH]; DX::FindMediaFile(buff, MAX_PATH, L"SegoeUI_18.spritefont"); @@ -986,21 +988,21 @@ void ATG::Help::RestoreDevice(ID3D11DeviceContext* context) m_spriteFonts[SEGOE_UI_36PT] = std::make_unique(device.Get(), buff); DX::FindMediaFile(buff, MAX_PATH, L"callout_circle.dds"); - DX::ThrowIfFailed(CreateDDSTextureFromFileEx(device.Get(), buff, 0, D3D11_USAGE_DEFAULT, D3D11_BIND_SHADER_RESOURCE, 0, 0, m_linearColors, nullptr, m_circleTex.ReleaseAndGetAddressOf())); + DX::ThrowIfFailed(CreateDDSTextureFromFileEx(device.Get(), buff, 0, D3D11_USAGE_DEFAULT, D3D11_BIND_SHADER_RESOURCE, 0, 0, loadFlags, nullptr, m_circleTex.ReleaseAndGetAddressOf())); DX::FindMediaFile(buff, MAX_PATH, L"gamepad.dds"); - DX::ThrowIfFailed(CreateDDSTextureFromFileEx(device.Get(), buff, 0, D3D11_USAGE_DEFAULT, D3D11_BIND_SHADER_RESOURCE, 0, 0, m_linearColors, nullptr, m_gamepadTex.ReleaseAndGetAddressOf())); + DX::ThrowIfFailed(CreateDDSTextureFromFileEx(device.Get(), buff, 0, D3D11_USAGE_DEFAULT, D3D11_BIND_SHADER_RESOURCE, 0, 0, loadFlags, nullptr, m_gamepadTex.ReleaseAndGetAddressOf())); DX::FindMediaFile(buff, MAX_PATH, L"ATGSampleBackground.DDS"); - DX::ThrowIfFailed(CreateDDSTextureFromFileEx(device.Get(), buff, 0, D3D11_USAGE_DEFAULT, D3D11_BIND_SHADER_RESOURCE, 0, 0, m_linearColors, nullptr, m_backgroundTex.ReleaseAndGetAddressOf())); + DX::ThrowIfFailed(CreateDDSTextureFromFileEx(device.Get(), buff, 0, D3D11_USAGE_DEFAULT, D3D11_BIND_SHADER_RESOURCE, 0, 0, loadFlags, nullptr, m_backgroundTex.ReleaseAndGetAddressOf())); #else m_spriteFonts[SEGOE_UI_18PT] = std::make_unique(device.Get(), L"SegoeUI_18.spritefont"); m_spriteFonts[SEGOE_UI_22PT] = std::make_unique(device.Get(), L"SegoeUI_22.spritefont"); m_spriteFonts[SEGOE_UI_36PT] = std::make_unique(device.Get(), L"SegoeUI_36.spritefont"); - DX::ThrowIfFailed(CreateDDSTextureFromFileEx(device.Get(), L"callout_circle.dds", 0, D3D11_USAGE_DEFAULT, D3D11_BIND_SHADER_RESOURCE, 0, 0, m_linearColors, nullptr, m_circleTex.ReleaseAndGetAddressOf())); - DX::ThrowIfFailed(CreateDDSTextureFromFileEx(device.Get(), L"gamepad.dds", 0, D3D11_USAGE_DEFAULT, D3D11_BIND_SHADER_RESOURCE, 0, 0, m_linearColors, nullptr, m_gamepadTex.ReleaseAndGetAddressOf())); - DX::ThrowIfFailed(CreateDDSTextureFromFileEx(device.Get(), L"ATGSampleBackground.DDS", 0, D3D11_USAGE_DEFAULT, D3D11_BIND_SHADER_RESOURCE, 0, 0, m_linearColors, nullptr, m_backgroundTex.ReleaseAndGetAddressOf())); + DX::ThrowIfFailed(CreateDDSTextureFromFileEx(device.Get(), L"callout_circle.dds", 0, D3D11_USAGE_DEFAULT, D3D11_BIND_SHADER_RESOURCE, 0, 0, loadFlags, nullptr, m_circleTex.ReleaseAndGetAddressOf())); + DX::ThrowIfFailed(CreateDDSTextureFromFileEx(device.Get(), L"gamepad.dds", 0, D3D11_USAGE_DEFAULT, D3D11_BIND_SHADER_RESOURCE, 0, 0, loadFlags, nullptr, m_gamepadTex.ReleaseAndGetAddressOf())); + DX::ThrowIfFailed(CreateDDSTextureFromFileEx(device.Get(), L"ATGSampleBackground.DDS", 0, D3D11_USAGE_DEFAULT, D3D11_BIND_SHADER_RESOURCE, 0, 0, loadFlags, nullptr, m_backgroundTex.ReleaseAndGetAddressOf())); #endif } #endif diff --git a/Kits/ATGTK/FrontPanel/FrontPanelDisplay.cpp b/Kits/ATGTK/FrontPanel/FrontPanelDisplay.cpp index a09e5c8..cbb9fd6 100644 --- a/Kits/ATGTK/FrontPanel/FrontPanelDisplay.cpp +++ b/Kits/ATGTK/FrontPanel/FrontPanelDisplay.cpp @@ -69,18 +69,17 @@ namespace namespace DirectX { - namespace Internal + inline namespace DX12 { - // Reuse the WIC factory function from the DirectX Tool Kit. For implementation details, see WICTextureLoader.cpp -#if defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) - extern IWICImagingFactory2* GetWIC(); -#else - extern IWICImagingFactory* GetWIC(); -#endif + namespace Internal + { + // Reuse the WIC factory function from the DirectX Tool Kit. For implementation details, see WICTextureLoader.cpp + extern IWICImagingFactory2* GetWIC(); + } } } -using namespace DirectX::Internal; +using namespace DirectX::DX12::Internal; // -------------------------------------------------------------------------------- // FrontPanelDisplay methods diff --git a/Kits/ATGTK/Processor.cpp b/Kits/ATGTK/Processor.cpp index 9ebee2e..ea9e640 100644 --- a/Kits/ATGTK/Processor.cpp +++ b/Kits/ATGTK/Processor.cpp @@ -14,7 +14,6 @@ #include #undef max #undef min -#include "intrin.h" #if defined(_GAMING_XBOX) #include @@ -41,7 +40,7 @@ namespace std::wstring g_processorName; // The general processor name. This can be changed by the user to allow easier usage in their code std::wstring g_trueProcessorName; // The actual name of the processor as returned by the OS uint64_t g_processMask = 0; // In general all the cores that exist on the processor, when >64 this will be bound to the group the process is assigned to - std::vector g_numLogicalCores; // How many logical cores exist, if not hyperthreaded == number of physical cores + std::vector g_numLogicalCores; // How many logical cores exist, if SMT is not supported == number of physical cores std::vector g_availableCoresMask; // Which cores are actually available to the caller, for example ERA does not allow the 8th core to be used, index is groupID #if !defined(_XBOX_ONE) // we don't use this on ERA because GetLogicalProcessorInfoEx and GetLogicalProcessorInformation is not supported @@ -65,7 +64,7 @@ namespace SYSTEM_INFO g_systemInfo; // Cached copy of data from GetSystemInfo std::vector g_topLevelCacheMask; // For each top level cache the mask of cores that share id. This tends to be processors that have several internal clusters, for example Durango has two clusters - std::map>> g_physicalCores; // For each physical core a mask of the logical cores that map to that physical core and a flag for whether it's hyperthreaded + std::map>> g_physicalCores; // For each physical core a mask of the logical cores that map to that physical core and a flag for whether it has support for SMT std::vector g_caches; // A list of each cache present on the processor, L1, L2, L3, Instruction, Data, and Trace (haven't seen this on any processor yet) double g_rdtscpFrequencySeconds; // The frequency in seconds of the __rdtscp, measured automatically at startup @@ -315,15 +314,32 @@ void ATG::GetCacheInformation(const UniqueProcessorMask& coreMask, std::vector (((CPUInfo[0] & 0xf00) >> 8) + ((CPUInfo[0] & 0x0ff00000) >> 20)); +} + bool ATG::SetupProcessorData() { + if (g_trueProcessorName.size()) + return true; + #if !defined(_XBOX_ONE) && !defined(_GAMING_XBOX) // Xbox doesn't have this registry entry g_osProcessorInfo.bufferSize = 0; g_osProcessorInfo.rawData = nullptr; - wchar_t processorName[256] = {}; - DWORD dataSize = sizeof(wchar_t) * 256; - RegGetValueW(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", L"ProcessorNameString", RRF_RT_REG_SZ, nullptr, processorName, &dataSize); - g_trueProcessorName = processorName; + { + wchar_t processorName[256]; + DWORD dataSize = sizeof(wchar_t) * 256; + RegGetValueW(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", L"ProcessorNameString", RRF_RT_REG_SZ, nullptr, processorName, &dataSize); + g_trueProcessorName = processorName; + } #elif defined(_GAMING_XBOX) XSystemDeviceType info; info = XSystemGetDeviceType(); @@ -410,7 +426,7 @@ bool ATG::SetupProcessorData() ParseOSProcessorInfo(); #if defined(_GAMING_XBOX) - if (ATG::IsHyperThreaded()) + if (ATG::IsSMTSupported()) g_trueProcessorName += L"_SMT"; g_processorName = g_trueProcessorName; #endif @@ -500,7 +516,7 @@ void ATG::LogProcessorInfo(const std::wstring& logName) swprintf_s(buffer, 256, L"%s_procInfo", g_trueProcessorName.c_str()); fileName = buffer; } - ATG::FileLogger procLog(fileName, false); + ATG::FileLogger procLog(fileName, false, false, false, true, true); #if !defined(_GAMING_XBOX) wchar_t buffer[256]; @@ -531,7 +547,7 @@ void ATG::LogProcessorInfo(const std::wstring& logName) case RelationProcessorCore: procLog.Log(L"Processor Core"); if (procInfo->Processor.Flags == LTP_PC_SMT) - procLog.Log(L" Core is hyper threaded"); + procLog.Log(L" Core supports SMT"); if (procInfo->Processor.EfficiencyClass != 0) procLog.Log(L" Core efficiency class is set"); for (uint32_t i = 0; i < procInfo->Processor.GroupCount; i++) @@ -658,7 +674,7 @@ void ATG::LogProcessorInfo(const std::wstring& logName) case RelationProcessorCore: procLog.Log(L"Processor Core"); if (procInfo->ProcessorCore.Flags == 1) - procLog.Log(L" Core is hyper threaded"); + procLog.Log(L" Core supports SMT"); swprintf(buffer, 256, L" CoreMask - %016I64x", procInfo->ProcessorMask); procLog.Log(buffer); break; @@ -691,7 +707,7 @@ void ATG::LogCoreTests(const std::wstring& suffix, const std::wstring& logName) swprintf_s(buffer, 256, L"%s_coreTests_%s", g_trueProcessorName.c_str(), suffix.c_str()); fileName = buffer; } - ATG::FileLogger testLog(fileName, false); + ATG::FileLogger testLog(fileName, false, false, false, true, true); size_t numTests = g_representativeCoreTests.size(); for (size_t i = 0; i < numTests; i++) { @@ -776,7 +792,7 @@ namespace testNames.push_back(L"all_cores"); // one logical per physical - if (ATG::IsHyperThreaded()) + if (ATG::IsSMTSupported()) { uint64_t oneLogicalMask = 0; for (const auto& iter : cpuSets) @@ -814,7 +830,7 @@ namespace } // single physical core, one per cluster - if (minLowCacheCores > 1) // this means hyperthreaded + if (minLowCacheCores > 1) // this means SMT is supported { for (const auto& cluster : cpuSets) { @@ -935,7 +951,7 @@ namespace } } - // cross cluster tests, pick one physical core even if hyperthreaded + // cross cluster tests, pick one physical core even if SMT is supported { for (const auto& outerCluster : cpuSets) { @@ -976,21 +992,21 @@ namespace testNames.clear(); constexpr uint32_t s_maxSingleCores = 4u; - bool hyperThreaded = false; + bool smtSupported = false; uint64_t allCoresMask(0); wchar_t tempBuffer[256]; for (const auto& iter : g_physicalCores[groupID]) { allCoresMask |= iter.second; if (iter.first) - hyperThreaded = true; + smtSupported = true; } coreTests.push_back(allCoresMask); // do everything testNames.push_back(L"all_cores"); // physical cores { const uint32_t numSingle = std::min(ATG::GetNumPhysicalCores(groupID), s_maxSingleCores); - const uint32_t coreDelta = std::max(hyperThreaded ? 2U : 1U, static_cast ((((float)ATG::GetTotalNumCores(groupID)) / numSingle) + 0.5f)); // assume hyperthreaded are only 2 cores per physical + const uint32_t coreDelta = std::max(smtSupported ? 2U : 1U, static_cast ((((float)ATG::GetTotalNumCores(groupID)) / numSingle) + 0.5f)); // assume SMT supported are only 2 cores per physical uint64_t curMask = 0; for (uint32_t i = 0; i < numSingle; i++, curMask += coreDelta) { @@ -999,10 +1015,10 @@ namespace testNames.push_back(tempBuffer); } } - if (hyperThreaded) + if (smtSupported) { const uint32_t numSingle = std::min(ATG::GetNumPhysicalCores(groupID), s_maxSingleCores); - const uint32_t coreDelta = std::max(2, ATG::GetTotalNumCores(groupID) / numSingle); // assume hyperthreaded are only 2 cores per physical. Code below will set the mask correctly + const uint32_t coreDelta = std::max(2, ATG::GetTotalNumCores(groupID) / numSingle); // assume SMT supported are only 2 cores per physical. Code below will set the mask correctly uint64_t curMask = 0; for (uint32_t i = 0; i < numSingle; i++, curMask += coreDelta) { @@ -1015,7 +1031,6 @@ namespace if (g_dieMask.size() > 1) // numa nodes will show up here { // all cores in a cluster - uint32_t curCluster = 0; for (const auto& iter : g_dieMask) { if (iter.second.groupID != groupID) @@ -1023,11 +1038,9 @@ namespace coreTests.push_back(iter.second.coreMask); swprintf_s(tempBuffer, 256, L"full_cluster_0x%llx", iter.second.coreMask); testNames.push_back(tempBuffer); - curCluster++; } // two cores in a cluster - curCluster = 0; for (const auto& iter : g_dieMask) { if (iter.second.groupID != groupID) @@ -1040,14 +1053,12 @@ namespace swprintf_s(tempBuffer, 256, L"within_cluster_physical_0x%llx", (1ULL << lowBitIndex) | (1ULL << highBitIndex)); testNames.push_back(tempBuffer); - if (hyperThreaded) + if (smtSupported) { coreTests.push_back(ATG::GetLogicalProcessorMask(ATG::UniqueProcessorMask(1ULL << lowBitIndex, groupID)) | ATG::GetLogicalProcessorMask(ATG::UniqueProcessorMask(1ULL << highBitIndex, groupID))); - swprintf_s(tempBuffer, 256, L"within_cluster_hyper_0x%llx", ATG::GetLogicalProcessorMask(ATG::UniqueProcessorMask(1ULL << lowBitIndex, groupID)) | ATG::GetLogicalProcessorMask(ATG::UniqueProcessorMask(1ULL << highBitIndex, groupID))); + swprintf_s(tempBuffer, 256, L"within_cluster_SMT_0x%llx", ATG::GetLogicalProcessorMask(ATG::UniqueProcessorMask(1ULL << lowBitIndex, groupID)) | ATG::GetLogicalProcessorMask(ATG::UniqueProcessorMask(1ULL << highBitIndex, groupID))); testNames.push_back(tempBuffer); } - - curCluster++; } // one physical on all combinations of 2 clusters @@ -1073,7 +1084,7 @@ namespace _BitScanForward64(&outerBitIndex, outer->second.coreMask); _BitScanForward64(&innerBitIndex, inner->second.coreMask); - if (hyperThreaded) + if (smtSupported) { coreTests.push_back(ATG::GetLogicalProcessorMask(ATG::UniqueProcessorMask(1ULL << outerBitIndex, groupID)) | ATG::GetLogicalProcessorMask(ATG::UniqueProcessorMask(1ULL << innerBitIndex, groupID))); swprintf_s(tempBuffer, 256, L"cross_cluster_logical_0x%llx", ATG::GetLogicalProcessorMask(ATG::UniqueProcessorMask(1ULL << outerBitIndex, groupID)) | ATG::GetLogicalProcessorMask(ATG::UniqueProcessorMask(1ULL << innerBitIndex, groupID))); @@ -1091,7 +1102,7 @@ namespace // half the cores in a single const uint32_t halfLogical = ATG::GetNumLogicalCores(groupID) / 2; uint64_t lowMask(0), highMask(0); - for (uint32_t i = 0; i < halfLogical; i += hyperThreaded ? 2 : 1) + for (uint32_t i = 0; i < halfLogical; i += smtSupported ? 2 : 1) { lowMask |= ATG::GetLogicalProcessorMask(ATG::UniqueProcessorMask(1ULL << i, groupID)); highMask |= ATG::GetLogicalProcessorMask(ATG::UniqueProcessorMask(1ULL << (halfLogical + i), groupID)); @@ -1105,7 +1116,7 @@ namespace testNames.push_back(tempBuffer); // two physical cores - if (hyperThreaded && (ATG::GetNumPhysicalCores(groupID) >= 4)) // just assume 2 cores in a hyperthread and at least 4 physical cores + if (smtSupported && (ATG::GetNumPhysicalCores(groupID) >= 4)) // just assume 2 cores in a SMT supported and at least 4 physical cores { coreTests.push_back(0x05); swprintf_s(tempBuffer, 256, L"first two physical 0x05"); @@ -1115,8 +1126,8 @@ namespace swprintf_s(tempBuffer, 256, L"second two physical 0xA0"); testNames.push_back(tempBuffer); } - // handle 2 physical hyperthreaded cores - if (hyperThreaded && (ATG::GetNumPhysicalCores(groupID) == 2)) + // handle 2 physical SMT cores + if (smtSupported && (ATG::GetNumPhysicalCores(groupID) == 2)) { coreTests.push_back(0x05); swprintf_s(tempBuffer, 256, L"Two physical_0x05"); @@ -1309,7 +1320,6 @@ namespace g_numLogicalCores.resize(groupCount, 0); g_availableCoresMask.resize(groupCount, 0); - uint32_t entries = 0; data = g_osProcessorInfo.rawData; dataLeft = g_osProcessorInfo.bufferSize; while (dataLeft) @@ -1367,7 +1377,6 @@ namespace } dataLeft -= procInfo->Size; data = data + procInfo->Size; - entries++; } #endif diff --git a/Kits/ATGTK/Processor.h b/Kits/ATGTK/Processor.h index fee6ae2..00c73f6 100644 --- a/Kits/ATGTK/Processor.h +++ b/Kits/ATGTK/Processor.h @@ -21,6 +21,8 @@ namespace ATG using ProcessorGroupID = uint16_t; using ProcessorNumaNodeID = uint32_t; + constexpr uint32_t JAGUAR_PROCESSOR_FAMILY = 0x16; + constexpr uint32_t ZEN2_PROCESSOR_FAMILY = 0x17; struct UniqueProcessorMask { @@ -86,7 +88,7 @@ namespace ATG uint32_t GetTotalNumCores(ProcessorGroupID groupID = 0); uint64_t GetAvailableCoresMask(ProcessorGroupID groupID = 0); - inline bool IsHyperThreaded(ProcessorGroupID groupID = 0) { return GetNumPhysicalCores(groupID) < GetNumLogicalCores(groupID); } + inline bool IsSMTSupported(ProcessorGroupID groupID = 0) { return GetNumPhysicalCores(groupID) < GetNumLogicalCores(groupID); } double GetRDTSCPFrequencySeconds(); double GetRDTSCPFrequencyMilliseconds(); double GetRDTSCPFrequencyMicroseconds(); @@ -94,6 +96,7 @@ namespace ATG const std::wstring& GetProcessorName(); // will be the override value which is initially the true name const std::wstring& GetTrueProcessorName(); void SetProcessorName(const std::wstring& overrideName); + uint32_t GetProcessorFamily(); uint64_t GetLogicalProcessorMask(uint64_t coreMask); // adjusts the mask to cover all logical processors diff --git a/Kits/ATGTK/RDTSCPStopWatch.h b/Kits/ATGTK/RDTSCPStopWatch.h index 80edfa0..d223ef5 100644 --- a/Kits/ATGTK/RDTSCPStopWatch.h +++ b/Kits/ATGTK/RDTSCPStopWatch.h @@ -10,103 +10,109 @@ #include #include -#include "intrin.h" +#include namespace ATG { - double CalcRDTSCPFrequency(); - extern double s_rdtscpFrequencySecs; - extern double s_rdtscpFrequencyMS; - extern double s_rdtscpFrequencyUS; + double CalcRDTSCPFrequency(); + extern double s_rdtscpFrequencySecs; + extern double s_rdtscpFrequencyMS; + extern double s_rdtscpFrequencyUS; + extern double s_rdtscpFrequencyNS; - class RDTSCPStopWatch - { - private: - uint64_t m_startTimeRaw; - uint64_t m_stopTimeRaw; - bool m_running; + class RDTSCPStopWatch + { + private: + uint64_t m_startTimeRaw; + uint64_t m_stopTimeRaw; + bool m_running; - public: + public: - RDTSCPStopWatch() :m_startTimeRaw(0), m_stopTimeRaw(0), m_running(false) {} - void Start() - { - assert(!m_running); - m_running = true; - m_stopTimeRaw = 0; - uint32_t tempAux; - m_startTimeRaw = __rdtscp(&tempAux); - } - double Stop() - { - assert(m_running); - uint32_t tempAux; - m_stopTimeRaw = __rdtscp(&tempAux); - m_running = false; - return GetTotalSeconds(); - } + RDTSCPStopWatch() :m_startTimeRaw(0), m_stopTimeRaw(0), m_running(false) {} - void Reset() - { - m_startTimeRaw = 0; - m_stopTimeRaw = 0; - } + bool IsRunning() const noexcept { return m_running; } + void Start() + { + assert(!m_running); + m_running = true; + m_stopTimeRaw = 0; + uint32_t tempAux; + m_startTimeRaw = __rdtscp(&tempAux); + } + double Stop() + { + assert(m_running); + uint32_t tempAux; + m_stopTimeRaw = __rdtscp(&tempAux); + m_running = false; + return GetTotalSeconds(); + } - static double GetFrequencyMilliseconds() { return s_rdtscpFrequencyMS; } + void Reset() + { + uint32_t tempAux; + m_startTimeRaw = __rdtscp(&tempAux); + m_stopTimeRaw = 0; + } - double GetCurrentSeconds() const - { - assert(m_running); - uint32_t tempAux; - uint64_t temp; - temp = __rdtscp(&tempAux); - return static_cast(temp - m_startTimeRaw) / s_rdtscpFrequencySecs; - } - double GetCurrentMilliseconds() const - { - assert(m_running); - uint32_t tempAux; - uint64_t temp; - temp = __rdtscp(&tempAux); - return static_cast(temp - m_startTimeRaw) / s_rdtscpFrequencyMS; - } - double GetCurrentMicroseconds() const - { - assert(m_running); - uint32_t tempAux; - uint64_t temp; - temp = __rdtscp(&tempAux); - return static_cast(temp - m_startTimeRaw) / s_rdtscpFrequencyUS; - } - uint64_t GetCurrentRaw() const - { - assert(m_running); - uint32_t tempAux; - uint64_t temp; - temp = __rdtscp(&tempAux); - return temp - m_startTimeRaw; - } + static double GetFrequencyMilliseconds() { return s_rdtscpFrequencyMS; } + static double GetFrequencyMicroseconds() { return s_rdtscpFrequencyUS; } + static double GetFrequencyNanoseconds() { return s_rdtscpFrequencyNS; } - double GetTotalSeconds() const - { - assert(!m_running); - return static_cast(m_stopTimeRaw - m_startTimeRaw) / s_rdtscpFrequencySecs; - } - double GetTotalMilliseconds() const - { - assert(!m_running); - return static_cast(m_stopTimeRaw - m_startTimeRaw) / s_rdtscpFrequencyMS; - } - double GetTotalMicroseconds() const - { - assert(!m_running); - return static_cast(m_stopTimeRaw - m_startTimeRaw) / s_rdtscpFrequencyUS; - } + double GetCurrentSeconds() const + { + assert(m_running); + uint32_t tempAux; + uint64_t temp; + temp = __rdtscp(&tempAux); + return static_cast(temp - m_startTimeRaw) / s_rdtscpFrequencySecs; + } + double GetCurrentMilliseconds() const + { + assert(m_running); + uint32_t tempAux; + uint64_t temp; + temp = __rdtscp(&tempAux); + return static_cast(temp - m_startTimeRaw) / s_rdtscpFrequencyMS; + } + double GetCurrentMicroseconds() const + { + assert(m_running); + uint32_t tempAux; + uint64_t temp; + temp = __rdtscp(&tempAux); + return static_cast(temp - m_startTimeRaw) / s_rdtscpFrequencyUS; + } + uint64_t GetCurrentRaw() const + { + assert(m_running); + uint32_t tempAux; + uint64_t temp; + temp = __rdtscp(&tempAux); + return temp - m_startTimeRaw; + } - uint64_t GetTotalRaw() const - { - assert(!m_running); - return m_stopTimeRaw - m_startTimeRaw; - } - }; + double GetTotalSeconds() const + { + assert(!m_running); + return static_cast(m_stopTimeRaw - m_startTimeRaw) / s_rdtscpFrequencySecs; + } + double GetTotalMilliseconds() const + { + assert(!m_running); + return static_cast(m_stopTimeRaw - m_startTimeRaw) / s_rdtscpFrequencyMS; + } + double GetTotalMicroseconds() const + { + assert(!m_running); + return static_cast(m_stopTimeRaw - m_startTimeRaw) / s_rdtscpFrequencyUS; + } + + uint64_t GetTotalRaw() const + { + assert(!m_running); + return m_stopTimeRaw - m_startTimeRaw; + } + }; } diff --git a/Kits/ATGTK/RDTSCPStopwatch.cpp b/Kits/ATGTK/RDTSCPStopwatch.cpp index eceed3a..30c0146 100644 --- a/Kits/ATGTK/RDTSCPStopwatch.cpp +++ b/Kits/ATGTK/RDTSCPStopwatch.cpp @@ -15,6 +15,7 @@ namespace ATG double s_rdtscpFrequencySecs = CalcRDTSCPFrequency(); // force init at startup double s_rdtscpFrequencyMS; // CalcRDTSCPFrequency auto sets these double s_rdtscpFrequencyUS; // CalcRDTSCPFrequency auto sets these + double s_rdtscpFrequencyNS; // CalcRDTSCPFrequency auto sets these double CalcRDTSCPFrequency() { @@ -64,8 +65,9 @@ namespace ATG SetThreadPriority(GetCurrentThread(), currentPriority); s_rdtscpFrequencySecs = (rdtscFrequencyIdle + rdtscFrequencyLoad) / 2.0; // the numbers should be close to identical so just average - s_rdtscpFrequencyMS = s_rdtscpFrequencySecs / 1000; - s_rdtscpFrequencyUS = s_rdtscpFrequencySecs / 1000000; + s_rdtscpFrequencyMS = s_rdtscpFrequencySecs / 1000.0; + s_rdtscpFrequencyUS = s_rdtscpFrequencySecs / 1000000.0; + s_rdtscpFrequencyNS = s_rdtscpFrequencySecs / 1000000000.0; return s_rdtscpFrequencySecs; } diff --git a/Kits/ATGTK/Random.h b/Kits/ATGTK/Random.h index 6e6140d..6c9e5e5 100644 --- a/Kits/ATGTK/Random.h +++ b/Kits/ATGTK/Random.h @@ -11,26 +11,28 @@ namespace ATG { - template - randomSize GetRandomValue(randomSize range) - { - static std::random_device randomDevice; - static std::mt19937_64 randomEngine(randomDevice()); + template + randomSize GetRandomValue(randomSize range) + { + if (range == 0) + return range; + static std::random_device randomDevice; + static std::mt19937_64 randomEngine(randomDevice()); - using distribution = typename std::conditional< std::is_floating_point_v, std::uniform_real_distribution, std::uniform_int_distribution>::type; - static distribution randomValue(0, std::numeric_limits::max()); + using distribution = typename std::conditional< std::is_floating_point_v, std::uniform_real_distribution, std::uniform_int_distribution>::type; + static distribution randomValue(0, std::numeric_limits::max()); - randomSize toret = randomValue(randomEngine); - if (std::is_floating_point_v) - { - toret /= std::numeric_limits::max(); - toret *= range; - } - else - { - randomSize temp = toret / range; - toret -= (temp * range); - } - return toret; - } + randomSize toret = randomValue(randomEngine); + if (std::is_floating_point_v) + { + toret /= std::numeric_limits::max(); + toret *= range; + } + else + { + randomSize temp = toret / range; + toret -= (temp * range); + } + return toret; + } } diff --git a/Kits/ATGTK/Texture.cpp b/Kits/ATGTK/Texture.cpp index d8361a9..23ddaa3 100644 --- a/Kits/ATGTK/Texture.cpp +++ b/Kits/ATGTK/Texture.cpp @@ -198,7 +198,7 @@ Texture::Texture(ID3D11Device* device, const wchar_t* fileName, bool forceSRGB) ThrowIfFailed( CreateDDSTextureFromFileEx( device, fileName, 0, D3D11_USAGE_DEFAULT, D3D11_BIND_SHADER_RESOURCE, 0, 0, - forceSRGB, + forceSRGB ? DDS_LOADER_FORCE_SRGB : DDS_LOADER_DEFAULT, resource.GetAddressOf(), resourceView.GetAddressOf(), &m_alphaMode) ); } diff --git a/Kits/ATGTK/UnicodeRendering/GlyphCache.h b/Kits/ATGTK/UnicodeRendering/GlyphCache.h index 82db9bd..f7a7b40 100644 --- a/Kits/ATGTK/UnicodeRendering/GlyphCache.h +++ b/Kits/ATGTK/UnicodeRendering/GlyphCache.h @@ -11,11 +11,9 @@ #include "ATGColors.h" -namespace DirectX { - class SpriteBatch; - class DescriptorHeap; - enum SpriteEffects : uint32_t; -} +#include "DescriptorHeap.h" +#include "SpriteBatch.h" + namespace ATG { diff --git a/Kits/ATGTK/UserLockable.h b/Kits/ATGTK/UserLockable.h index e12c039..740c54f 100644 --- a/Kits/ATGTK/UserLockable.h +++ b/Kits/ATGTK/UserLockable.h @@ -7,7 +7,6 @@ #pragma once -#include #include namespace ATG @@ -33,7 +32,7 @@ namespace ATG UserEventLockable(const UserEventLockable&) = delete; UserEventLockable& operator=(const UserEventLockable&) = delete; - UserEventLockable(bool initialState = true) : m_threadsWaiting(0), m_eventFlag(static_cast (initialState ? 1 : 0)) { } + UserEventLockable(bool initialState = true) noexcept : m_threadsWaiting(0), m_eventFlag(initialState ? 1 : 0u) { } UserEventLockable(UserEventLockable&& rhs) = default; ~UserEventLockable() { } @@ -143,7 +142,7 @@ namespace ATG UserSemaphoreLockable(UserSemaphoreLockable&& rhs) = delete; UserSemaphoreLockable& operator=(const UserSemaphoreLockable&&) = delete; - UserSemaphoreLockable(uint32_t intialValue) : m_threadsWaiting(0), m_semaphore(intialValue) { } + UserSemaphoreLockable(uint32_t intialValue) noexcept : m_threadsWaiting(0), m_semaphore(intialValue) { } ~UserSemaphoreLockable() { } void lock() @@ -173,6 +172,7 @@ namespace ATG const uint32_t newValue = currentValue - 1; // locking only reduces the count by 1 if (InterlockedCompareExchange(&m_semaphore, newValue, currentValue) == currentValue) return true; + _mm_pause(); } } @@ -185,6 +185,7 @@ namespace ATG const uint32_t newValue = currentValue + releaseCount; if (InterlockedCompareExchange(&m_semaphore, newValue, currentValue) == currentValue) break; + _mm_pause(); } if (m_threadsWaiting) // only call WakeByAddress if there is a high probability of a thread actually waiting. @@ -241,29 +242,100 @@ namespace ATG ////////////////////////////////////////////////////////////////////////// /// - /// Event object that attempts to stay at user level as much as possible - /// The object will only go to kernel mode if there is actually a thread waiting on the event - /// This allows for the maximum performance. + /// UserBarrier object that attempts to stay at user level as much as possible + /// The object will only go to kernel mode if requested and it's waiting for more threads to hit the barrier + /// The enterBarrier call can be set to pure spin to allow all waiting threads to exit as soon as possible + /// If threads spin care must be taken that no other threads are running on the same core to avoid extremely long waits + /// The threadOwner template parameter provides protection so only a single thread can call reset, the initial thread to enter the barrier + /// + ////////////////////////////////////////////////////////////////////////// + template + class UserBarrier + { + private: + std::atomic m_barrierValue; + uint64_t m_initialValue; + UserEventLockable m_blockEvent; + uint32_t m_threadOwnerID; + + public: + /// The UserBarrier cannot be copied + UserBarrier(const UserBarrier&) = delete; + UserBarrier& operator=(const UserBarrier&) = delete; + + UserBarrier(uint64_t initialValue = 1) noexcept : m_barrierValue(initialValue), m_initialValue(initialValue), m_blockEvent(false), m_threadOwnerID(0) { } + UserBarrier(UserBarrier&& rhs) = default; + ~UserBarrier() { } + + void clearOwner() { m_threadOwnerID = 0; } + + bool enterBarrier(bool block = false) + { + assert(m_barrierValue != 0); + if (threadOwner) + { + if (m_threadOwnerID == 0) + m_threadOwnerID = GetCurrentThreadId(); + } + uint64_t myValue = m_barrierValue.fetch_sub(1); + if (myValue == 1) + { + m_blockEvent.SetEvent(); + return true; + } + else if (block) + { + m_blockEvent.lock(); + } + else + { + while (m_barrierValue.load() != 0) { _mm_pause(); } + } + return false; + } + + bool reset() { return reset(m_initialValue); } + bool reset(uint64_t newValue) + { + if (threadOwner) + { + if (m_threadOwnerID != GetCurrentThreadId()) + { + if (InterlockedCompareExchange(&m_threadOwnerID, GetCurrentThreadId(), 0) != 0) + return false; + } + } + m_initialValue = newValue; + m_barrierValue = newValue; + m_blockEvent.SetEvent(); + m_blockEvent.ResetEvent(); + return true; + } + }; + + ////////////////////////////////////////////////////////////////////////// + /// + /// MonitorXMwaitXLockable object that attempts to stay at user level as much as possible + /// Spin lock implemented using the monitorx/mwaitx instructions + /// This class will stay in user-mode the entire time /// Interfaces follow the C++11 TimeLockable concept - /// The template parameter on manual reset is used to allow the compiler to optimize out the conditional /// ////////////////////////////////////////////////////////////////////////// #if !defined(__clang__) || defined (__MWAITX__) //NOTE: Clang explictly only includes this intrinsic if __MWAITX__ is defined and it needs to be defined in the pch file before the x86intrin.h is included #pragma warning (push) -#pragma warning (disable: 4324) +#pragma warning (disable: 4324) // structure was padded class alignas(64) MonitorXMwaitXLockable { private: uint32_t m_eventFlag; // the current event flag, interlocks are used on it for thread safety uint32_t m_owningThread; - char unusedForceSizeBlock[56]; // force size to be 64 bytes public: - /// The UserSemaphoreLockable cannot be copied + /// The MonitorXMwaitXLockable cannot be copied MonitorXMwaitXLockable(const MonitorXMwaitXLockable&) = delete; MonitorXMwaitXLockable& operator=(const MonitorXMwaitXLockable&) = delete; - MonitorXMwaitXLockable(bool initialState = false) : m_eventFlag(static_cast (initialState ? 1 : 0)), m_owningThread(0), unusedForceSizeBlock{ } { } + MonitorXMwaitXLockable(bool initialState = false) noexcept : m_eventFlag(initialState ? 1u : 0), m_owningThread(0) {} MonitorXMwaitXLockable(MonitorXMwaitXLockable&& rhs) = default; ~MonitorXMwaitXLockable() { } @@ -334,78 +406,5 @@ namespace ATG } }; #pragma warning (pop) -#endif // !__clang__ || __MWAITX__ - - ////////////////////////////////////////////////////////////////////////// - /// - /// Event object that attempts to stay at user level as much as possible - /// The object will only go to kernel mode if there is actually a thread waiting on the event - /// This allows for the maximum performance. - /// Interfaces follow the C++11 TimeLockable concept - /// The template parameter on manual reset is used to allow the compiler to optimize out the conditional - /// - ////////////////////////////////////////////////////////////////////////// - template - class UserBarrier - { - private: - std::atomic m_barrierValue; - uint64_t m_initialValue; - UserEventLockable m_blockEvent; - uint32_t m_threadOwnerID; - - public: - /// The UserBarrier cannot be copied - UserBarrier(const UserBarrier&) = delete; - UserBarrier& operator=(const UserBarrier&) = delete; - - UserBarrier(uint64_t initialValue = 1) : m_barrierValue(initialValue), m_initialValue(initialValue), m_blockEvent(false), m_threadOwnerID(0) { } - UserBarrier(UserBarrier&& rhs) = default; - ~UserBarrier() { } - - void clearOwner() { m_threadOwnerID = 0; } - - bool enterBarrier(bool block = false) - { - assert(m_barrierValue != 0); - if (threadOwner) - { - if (m_threadOwnerID == 0) - m_threadOwnerID = GetCurrentThreadId(); - } - uint64_t myValue = m_barrierValue.fetch_sub(1); - if (myValue == 1) - { - m_blockEvent.SetEvent(); - return true; - } - else if (block) - { - m_blockEvent.lock(); - } - else - { - while (m_barrierValue.load() != 0); - } - return false; - } - - bool reset() { return reset(m_initialValue); } - bool reset(uint64_t newValue) - { - if (threadOwner) - { - if (m_threadOwnerID != GetCurrentThreadId()) - { - if (InterlockedCompareExchange(&m_threadOwnerID, GetCurrentThreadId(), 0) != 0) - return false; - } - } - m_initialValue = newValue; - m_barrierValue = newValue; - m_blockEvent.SetEvent(); - m_blockEvent.ResetEvent(); - return true; - } - }; +#endif } diff --git a/Kits/ATGTK/d3dx12.h b/Kits/ATGTK/d3dx12.h index 42f26a6..e873d96 100644 --- a/Kits/ATGTK/d3dx12.h +++ b/Kits/ATGTK/d3dx12.h @@ -76,7 +76,12 @@ struct CD3DX12_VIEWPORT : public D3D12_VIEWPORT 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) @@ -2040,7 +2045,12 @@ inline UINT64 GetRequiredIntermediateSize( _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; @@ -2066,8 +2076,14 @@ inline UINT64 UpdateSubresources( _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) || @@ -2125,8 +2141,14 @@ inline UINT64 UpdateSubresources( _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) || @@ -2194,7 +2216,12 @@ inline UINT64 UpdateSubresources( 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); @@ -2232,7 +2259,12 @@ inline UINT64 UpdateSubresources( 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); @@ -2260,7 +2292,12 @@ inline UINT64 UpdateSubresources( 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); @@ -2287,7 +2324,12 @@ inline UINT64 UpdateSubresources( 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); @@ -4071,20 +4113,20 @@ public: 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 : + 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 + firstMipLevel, + numMips, + firstArraySlice, + numArraySlices, + firstPlane, + numPlanes } {} }; diff --git a/Kits/ATGTelemetry/GDK/ATGTelemetry.cpp b/Kits/ATGTelemetry/GDK/ATGTelemetry.cpp index 0cbb993..8f4b3db 100644 --- a/Kits/ATGTelemetry/GDK/ATGTelemetry.cpp +++ b/Kits/ATGTelemetry/GDK/ATGTelemetry.cpp @@ -45,6 +45,7 @@ using namespace ATG; class ATGTelemetry { public: + bool m_bHCInitialized{ false }; ~ATGTelemetry() { #if _GAMING_XBOX @@ -56,6 +57,14 @@ public: #endif } + void Cleanup() + { + if (m_bHCInitialized) + { + HCCleanup(); + } + } + void SendTelemetry() { #if _GAMING_XBOX @@ -86,6 +95,7 @@ private: { return; } + m_bHCInitialized = true; auto *async = new XAsyncBlock{}; async->queue = nullptr; @@ -464,4 +474,13 @@ void ATG::SendLaunchTelemetry() s_atgTelem->SendTelemetry(); } + +void ATG::CleanupTelemetry() +{ + if (s_atgTelem) + { + s_atgTelem->Cleanup(); + } + s_atgTelem = nullptr; +} #endif diff --git a/Kits/ATGTelemetry/GDK/ATGTelemetry.h b/Kits/ATGTelemetry/GDK/ATGTelemetry.h index 6a0ff15..bf03c62 100644 --- a/Kits/ATGTelemetry/GDK/ATGTelemetry.h +++ b/Kits/ATGTelemetry/GDK/ATGTelemetry.h @@ -20,8 +20,10 @@ namespace ATG #ifdef ATG_ENABLE_TELEMETRY // Sends sample launch telemetry void SendLaunchTelemetry(); + void CleanupTelemetry(); #else void SendLaunchTelemetry() {}; + void CleanupTelemetry() {}; #endif -} \ No newline at end of file +} diff --git a/Kits/DirectXTK12/Audio/SoundCommon.cpp b/Kits/DirectXTK12/Audio/SoundCommon.cpp index d01af20..418c704 100644 --- a/Kits/DirectXTK12/Audio/SoundCommon.cpp +++ b/Kits/DirectXTK12/Audio/SoundCommon.cpp @@ -822,9 +822,63 @@ void SoundEffectInstanceBase::Apply3D(const X3DAUDIO_LISTENER& listener, const X //====================================================================================== -// AudioEmitter helper +// 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** @@ -891,3 +945,17 @@ void AudioEmitter::EnableDefaultMultiChannel(unsigned int channels, float radius 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 }; +} + +void AudioEmitter::EnableDefaultCurves() noexcept +{ + pVolumeCurve = const_cast(&c_defaultCurve); + pLFECurve = const_cast(&c_defaultCurve); + pLPFDirectCurve = pLPFReverbCurve = pReverbCurve = nullptr; +} diff --git a/Kits/DirectXTK12/DirectXTK12_GDK_2019.vcxproj b/Kits/DirectXTK12/DirectXTK12_GDK_2019.vcxproj index 73020cf..a27cbd7 100644 --- a/Kits/DirectXTK12/DirectXTK12_GDK_2019.vcxproj +++ b/Kits/DirectXTK12/DirectXTK12_GDK_2019.vcxproj @@ -435,6 +435,7 @@ true /Zc:__cplusplus /ZH:SHA_256 %(AdditionalOptions) Level4 + true 6.0 @@ -464,6 +465,7 @@ true /Zc:__cplusplus /ZH:SHA_256 %(AdditionalOptions) Level4 + true 6.0 diff --git a/Kits/DirectXTK12/DirectXTK12_GRDK_2019.vcxproj b/Kits/DirectXTK12/DirectXTK12_GRDK_2019.vcxproj index 37c3850..3e7a15b 100644 --- a/Kits/DirectXTK12/DirectXTK12_GRDK_2019.vcxproj +++ b/Kits/DirectXTK12/DirectXTK12_GRDK_2019.vcxproj @@ -113,6 +113,7 @@ true /Zc:__cplusplus /ZH:SHA_256 %(AdditionalOptions) Level4 + true 6.0 @@ -142,6 +143,7 @@ true /Zc:__cplusplus /ZH:SHA_256 %(AdditionalOptions) Level4 + true 6.0 diff --git a/Kits/DirectXTK12/Inc/Audio.h b/Kits/DirectXTK12/Inc/Audio.h index 6b8a9b3..d63c7c3 100644 --- a/Kits/DirectXTK12/Inc/Audio.h +++ b/Kits/DirectXTK12/Inc/Audio.h @@ -434,10 +434,12 @@ namespace DirectX //---------------------------------------------------------------------------------- struct AudioListener : public X3DAUDIO_LISTENER { - AudioListener() noexcept - { - memset(this, 0, sizeof(X3DAUDIO_LISTENER)); + X3DAUDIO_CONE ListenerCone; + AudioListener() noexcept : + X3DAUDIO_LISTENER{}, + ListenerCone{} + { OrientFront.z = -1.f; OrientTop.y = 1.f; @@ -486,8 +488,8 @@ namespace DirectX 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 - // Updates velocity and orientation by tracking changes in position over time... { if (dt > 0.f) { @@ -511,19 +513,27 @@ namespace DirectX XMStoreFloat3(reinterpret_cast(&Position), newPos); } } + + void __cdecl SetOmnidirectional() noexcept + { + pCone = nullptr; + } + + void __cdecl SetCone(const X3DAUDIO_CONE& listenerCone); }; //---------------------------------------------------------------------------------- struct AudioEmitter : public X3DAUDIO_EMITTER { - float EmitterAzimuths[XAUDIO2_MAX_AUDIO_CHANNELS]; + X3DAUDIO_CONE EmitterCone; + float EmitterAzimuths[XAUDIO2_MAX_AUDIO_CHANNELS]; AudioEmitter() noexcept : + X3DAUDIO_EMITTER{}, + EmitterCone{}, EmitterAzimuths{} { - memset(this, 0, sizeof(X3DAUDIO_EMITTER)); - OrientFront.z = -1.f; OrientTop.y = @@ -580,8 +590,8 @@ namespace DirectX 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 - // Updates velocity and orientation by tracking changes in position over time... { if (dt > 0.f) { @@ -606,7 +616,19 @@ namespace DirectX } } + 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; }; diff --git a/Kits/DirectXTK12/Inc/CommonStates.h b/Kits/DirectXTK12/Inc/CommonStates.h index 8e7e330..7aba709 100644 --- a/Kits/DirectXTK12/Inc/CommonStates.h +++ b/Kits/DirectXTK12/Inc/CommonStates.h @@ -25,71 +25,74 @@ namespace DirectX { - class CommonStates + inline namespace DX12 { - 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 + class CommonStates { - PointWrap, - PointClamp, - LinearWrap, - LinearClamp, - AnisotropicWrap, - AnisotropicClamp, - Count + 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; }; - - ID3D12DescriptorHeap* Heap() const noexcept; - - private: - class Impl; - - std::unique_ptr pImpl; - }; + } } diff --git a/Kits/DirectXTK12/Inc/DDSTextureLoader.h b/Kits/DirectXTK12/Inc/DDSTextureLoader.h index c24d49f..414c767 100644 --- a/Kits/DirectXTK12/Inc/DDSTextureLoader.h +++ b/Kits/DirectXTK12/Inc/DDSTextureLoader.h @@ -48,13 +48,17 @@ namespace DirectX }; #endif - enum DDS_LOADER_FLAGS : uint32_t + inline namespace DX12 { - DDS_LOADER_DEFAULT = 0, - DDS_LOADER_FORCE_SRGB = 0x1, - DDS_LOADER_MIP_AUTOGEN = 0x8, - DDS_LOADER_MIP_RESERVE = 0x10, - }; + 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( @@ -153,7 +157,10 @@ namespace DirectX #pragma clang diagnostic ignored "-Wdeprecated-dynamic-exception-spec" #endif - DEFINE_ENUM_FLAG_OPERATORS(DDS_LOADER_FLAGS); + inline namespace DX12 + { + DEFINE_ENUM_FLAG_OPERATORS(DDS_LOADER_FLAGS); + } #ifdef __clang__ #pragma clang diagnostic pop diff --git a/Kits/DirectXTK12/Inc/DirectXHelpers.h b/Kits/DirectXTK12/Inc/DirectXHelpers.h index b1e6060..7b44118 100644 --- a/Kits/DirectXTK12/Inc/DirectXHelpers.h +++ b/Kits/DirectXTK12/Inc/DirectXHelpers.h @@ -139,7 +139,12 @@ namespace DirectX // 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)); } @@ -281,32 +286,35 @@ namespace DirectX std::vector mBarriers; }; - // 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 + inline namespace DX12 { - if (alignment > 0) - { - assert(((alignment - 1) & alignment) == 0); - auto mask = static_cast(alignment - 1); - return size & ~mask; - } - return size; - } + // Helper to check for power-of-2 + template + constexpr bool IsPowerOf2(T x) noexcept { return ((x != 0) && !(x & (x - 1))); } - template - inline T AlignUp(T size, size_t alignment) noexcept - { - if (alignment > 0) + // Helpers for aligning values by a power of 2 + template + inline T AlignDown(T size, size_t alignment) noexcept { - assert(((alignment - 1) & alignment) == 0); - auto mask = static_cast(alignment - 1); - return (size + mask) & ~mask; + 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; } - return size; } } diff --git a/Kits/DirectXTK12/Inc/EffectPipelineStateDescription.h b/Kits/DirectXTK12/Inc/EffectPipelineStateDescription.h index 96c6327..60451d8 100644 --- a/Kits/DirectXTK12/Inc/EffectPipelineStateDescription.h +++ b/Kits/DirectXTK12/Inc/EffectPipelineStateDescription.h @@ -67,6 +67,7 @@ namespace DirectX 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 = {}; @@ -84,6 +85,28 @@ namespace DirectX 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; diff --git a/Kits/DirectXTK12/Inc/Effects.h b/Kits/DirectXTK12/Inc/Effects.h index 3ecaabc..235181f 100644 --- a/Kits/DirectXTK12/Inc/Effects.h +++ b/Kits/DirectXTK12/Inc/Effects.h @@ -36,892 +36,895 @@ namespace DirectX class DescriptorHeap; class ResourceUploadBatch; - //---------------------------------------------------------------------------------- - // Abstract interface representing any effect which can be applied onto a D3D device context. - class IEffect + inline namespace DX12 { - 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 + //------------------------------------------------------------------------------ + // Abstract interface representing any effect which can be applied onto a D3D device context. + class IEffect { - Mapping_Cube = 0, // Cubic environment map - Mapping_Sphere, // Spherical environment map - Mapping_DualParabola, // Dual-parabola environment map (requires Feature Level 10.0) + 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; }; - 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) + // Abstract interface for effects with world, view, and projection matrices. + class IEffectMatrices { - } + public: + virtual ~IEffectMatrices() = default; - NormalMapEffect(NormalMapEffect&&) noexcept; - NormalMapEffect& operator= (NormalMapEffect&&) noexcept; + IEffectMatrices(const IEffectMatrices&) = delete; + IEffectMatrices& operator=(const IEffectMatrices&) = delete; - NormalMapEffect(NormalMapEffect const&) = delete; - NormalMapEffect& operator= (NormalMapEffect const&) = 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); - ~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 + protected: + IEffectMatrices() = default; + IEffectMatrices(IEffectMatrices&&) = default; + IEffectMatrices& operator=(IEffectMatrices&&) = default; }; - 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 + // Abstract interface for effects which support directional lighting. + class IEffectLights { - 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; + public: + virtual ~IEffectLights() = default; - 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) + 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); }; - 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; + class SkinnedNormalMapEffect : public NormalMapEffect, public IEffectSkinning + { + public: + SkinnedNormalMapEffect(_In_ ID3D12Device* device, uint32_t effectFlags, + const EffectPipelineStateDescription& pipelineDescription) : + NormalMapEffect(device, effectFlags, pipelineDescription, true) + { + } - protected: - IEffectFactory() = default; - IEffectFactory(IEffectFactory&&) = default; - IEffectFactory& operator=(IEffectFactory&&) = default; - }; + 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; + }; - // Factory for sharing effects - class EffectFactory : public IEffectFactory - { - public: - EffectFactory(_In_ ID3D12Device* device); - EffectFactory( - _In_ ID3D12DescriptorHeap* textureDescriptors, - _In_ ID3D12DescriptorHeap* samplerDescriptors); + //------------------------------------------------------------------------------ + // 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) + { + } - EffectFactory(EffectFactory&&) noexcept; - EffectFactory& operator= (EffectFactory&&) noexcept; + PBREffect(PBREffect&&) noexcept; + PBREffect& operator= (PBREffect&&) noexcept; - EffectFactory(EffectFactory const&) = delete; - EffectFactory& operator= (EffectFactory const&) = delete; + PBREffect(PBREffect const&) = delete; + PBREffect& operator= (PBREffect const&) = delete; - ~EffectFactory() override; + ~PBREffect() 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; + // IEffect methods. + void __cdecl Apply(_In_ ID3D12GraphicsCommandList* commandList) override; - // Settings. - void __cdecl ReleaseCache(); + // 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; - void __cdecl SetSharing(bool enabled) noexcept; + // 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 EnablePerPixelLighting(bool enabled) noexcept; + void __cdecl EnableDefaultLighting() override; - void __cdecl EnableNormalMapEffect(bool enabled) noexcept; + // PBR Settings. + void __cdecl SetAlpha(float value); + void XM_CALLCONV SetConstantAlbedo(FXMVECTOR value); + void __cdecl SetConstantMetallic(float value); + void __cdecl SetConstantRoughness(float value); - void __cdecl EnableFogging(bool enabled) noexcept; + // 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 EnableInstancing(bool enabled) noexcept; + void __cdecl SetEmissiveTexture(D3D12_GPU_DESCRIPTOR_HANDLE srvDescriptor); - private: - // Private implementation. - class Impl; + void __cdecl SetSurfaceTextures( + D3D12_GPU_DESCRIPTOR_HANDLE albedo, + D3D12_GPU_DESCRIPTOR_HANDLE normal, + D3D12_GPU_DESCRIPTOR_HANDLE roughnessMetallicAmbientOcclusion, + D3D12_GPU_DESCRIPTOR_HANDLE sampler); - std::shared_ptr pImpl; - }; + 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; + }; - // 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); + //------------------------------------------------------------------------------ + // 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 + }; - PBREffectFactory(PBREffectFactory&&) noexcept; - PBREffectFactory& operator= (PBREffectFactory&&) noexcept; + DebugEffect(_In_ ID3D12Device* device, uint32_t effectFlags, + const EffectPipelineStateDescription& pipelineDescription, + Mode debugMode = Mode_Default); - PBREffectFactory(PBREffectFactory const&) = delete; - PBREffectFactory& operator= (PBREffectFactory const&) = delete; + DebugEffect(DebugEffect&&) noexcept; + DebugEffect& operator= (DebugEffect&&) noexcept; - ~PBREffectFactory() override; + DebugEffect(DebugEffect const&) = delete; + DebugEffect& operator= (DebugEffect const&) = delete; - // 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; + ~DebugEffect() override; - // Settings. - void __cdecl ReleaseCache(); + // IEffect methods. + void __cdecl Apply(_In_ ID3D12GraphicsCommandList* commandList) override; - void __cdecl SetSharing(bool enabled) noexcept; + // 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; - void __cdecl EnableInstancing(bool enabled) noexcept; + // Debug Settings. + void XM_CALLCONV SetHemisphericalAmbientColor(FXMVECTOR upper, FXMVECTOR lower); + void __cdecl SetAlpha(float value); - private: - // Private implementation. - class Impl; + private: + // Private implementation. + class Impl; - std::shared_ptr pImpl; - }; + 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 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; + }; + } } diff --git a/Kits/DirectXTK12/Inc/GeometricPrimitive.h b/Kits/DirectXTK12/Inc/GeometricPrimitive.h index 9699b6e..fa303ff 100644 --- a/Kits/DirectXTK12/Inc/GeometricPrimitive.h +++ b/Kits/DirectXTK12/Inc/GeometricPrimitive.h @@ -19,76 +19,80 @@ namespace DirectX { - class IEffect; class ResourceUploadBatch; - class GeometricPrimitive + inline namespace DX12 { - public: - GeometricPrimitive(GeometricPrimitive&&) = default; - GeometricPrimitive& operator= (GeometricPrimitive&&) = default; + class IEffect; - GeometricPrimitive(GeometricPrimitive const&) = delete; - GeometricPrimitive& operator= (GeometricPrimitive const&) = delete; + class GeometricPrimitive + { + public: + GeometricPrimitive(GeometricPrimitive&&) = default; + GeometricPrimitive& operator= (GeometricPrimitive&&) = default; - virtual ~GeometricPrimitive(); + GeometricPrimitive(GeometricPrimitive const&) = delete; + GeometricPrimitive& operator= (GeometricPrimitive const&) = delete; - using VertexType = VertexPositionNormalTexture; - using VertexCollection = std::vector; - using IndexCollection = std::vector; + virtual ~GeometricPrimitive(); - // 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); + using VertexType = VertexPositionNormalTexture; + using VertexCollection = std::vector; + using IndexCollection = std::vector; - 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); + // 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); - // Load VB/IB resources for static geometry. - void __cdecl LoadStaticBuffers( - _In_ ID3D12Device* device, - ResourceUploadBatch& resourceUploadBatch); + 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); - // 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); + // Load VB/IB resources for static geometry. + void __cdecl LoadStaticBuffers( + _In_ ID3D12Device* device, + ResourceUploadBatch& resourceUploadBatch); - // Draw the primitive. - void __cdecl Draw(_In_ ID3D12GraphicsCommandList* commandList) const; + // 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); - void __cdecl DrawInstanced(_In_ ID3D12GraphicsCommandList* commandList, uint32_t instanceCount, uint32_t startInstanceLocation = 0) const; + // Draw the primitive. + void __cdecl Draw(_In_ ID3D12GraphicsCommandList* commandList) const; - private: - GeometricPrimitive() noexcept(false); + void __cdecl DrawInstanced(_In_ ID3D12GraphicsCommandList* commandList, uint32_t instanceCount, uint32_t startInstanceLocation = 0) const; - // Private implementation. - class Impl; + private: + GeometricPrimitive() noexcept(false); - std::unique_ptr pImpl; - }; + // Private implementation. + class Impl; + + std::unique_ptr pImpl; + }; + } } diff --git a/Kits/DirectXTK12/Inc/GraphicsMemory.h b/Kits/DirectXTK12/Inc/GraphicsMemory.h index d720c4a..25750a3 100644 --- a/Kits/DirectXTK12/Inc/GraphicsMemory.h +++ b/Kits/DirectXTK12/Inc/GraphicsMemory.h @@ -29,155 +29,158 @@ 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: - 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); - - // Special overload of Allocate that aligns to D3D12 constant buffer alignment requirements - template GraphicsResource AllocateConstant() + class GraphicsResource { - constexpr size_t alignment = D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT; - constexpr size_t alignedSize = (sizeof(T) + alignment - 1) & ~(alignment - 1); - return Allocate(alignedSize, alignment); - } - template GraphicsResource AllocateConstant(const T& setData) + 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 { - GraphicsResource alloc = AllocateConstant(); - memcpy(alloc.Memory(), &setData, sizeof(T)); - return alloc; - } + public: + SharedGraphicsResource() noexcept; - // 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); + SharedGraphicsResource(SharedGraphicsResource&&) noexcept; + SharedGraphicsResource&& operator= (SharedGraphicsResource&&) noexcept; - // 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(); + SharedGraphicsResource(GraphicsResource&&); + SharedGraphicsResource&& operator= (GraphicsResource&&); - // Memory statistics - GraphicsMemoryStatistics __cdecl GetStatistics(); - void __cdecl ResetStatistics(); + SharedGraphicsResource(const SharedGraphicsResource&) noexcept; + SharedGraphicsResource& operator= (const SharedGraphicsResource&) noexcept; - // Singleton - // Should only use nullptr for single GPU scenarios; mGPU requires a specific device - static GraphicsMemory& __cdecl Get(_In_opt_ ID3D12Device* device = nullptr); + SharedGraphicsResource(const GraphicsResource&) = delete; + SharedGraphicsResource& operator= (const GraphicsResource&) = delete; - private: - // Private implementation. - class Impl; + ~SharedGraphicsResource(); - std::unique_ptr pImpl; - }; + 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: + 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); + + // Special overload of Allocate that aligns to D3D12 constant buffer alignment requirements + template GraphicsResource AllocateConstant() + { + constexpr size_t alignment = D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT; + constexpr size_t alignedSize = (sizeof(T) + alignment - 1) & ~(alignment - 1); + return Allocate(alignedSize, alignment); + } + 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; + + std::unique_ptr pImpl; + }; + } } diff --git a/Kits/DirectXTK12/Inc/Model.h b/Kits/DirectXTK12/Inc/Model.h index 9e6fe35..7b140f6 100644 --- a/Kits/DirectXTK12/Inc/Model.h +++ b/Kits/DirectXTK12/Inc/Model.h @@ -48,148 +48,556 @@ namespace DirectX { - class IEffect; - class IEffectFactory; - class ModelMesh; - - //---------------------------------------------------------------------------------- - // Model loading options - enum ModelLoaderFlags : uint32_t + inline namespace DX12 { - ModelLoader_Default = 0x0, - ModelLoader_MaterialColorsSRGB = 0x1, - ModelLoader_AllowLargeModels = 0x2, - ModelLoader_IncludeBones = 0x4, - ModelLoader_DisableSkinning = 0x8, - }; + class IEffect; + class IEffectFactory; + class ModelMesh; - //---------------------------------------------------------------------------------- - // Frame hierarchy for rigid body and skeletal animation - struct ModelBone - { - ModelBone() noexcept : - parentIndex(c_Invalid), - childIndex(c_Invalid), - siblingIndex(c_Invalid) + //------------------------------------------------------------------------------ + // Model loading options + enum ModelLoaderFlags : uint32_t { - } + ModelLoader_Default = 0x0, + ModelLoader_MaterialColorsSRGB = 0x1, + ModelLoader_AllowLargeModels = 0x2, + ModelLoader_IncludeBones = 0x4, + ModelLoader_DisableSkinning = 0x8, + }; - ModelBone(uint32_t parent, uint32_t child, uint32_t sibling) noexcept : - parentIndex(parent), - childIndex(child), - siblingIndex(sibling) + //------------------------------------------------------------------------------ + // Frame hierarchy for rigid body and skeletal animation + struct ModelBone { - } - - 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) + ModelBone() noexcept : + parentIndex(c_Invalid), + childIndex(c_Invalid), + siblingIndex(c_Invalid) { - 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( + 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; + + 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 Collection& meshParts, + const ModelMesh& mesh, + const ModelMeshPart::Collection& meshParts, + size_t nbones, + _In_reads_(nbones) const XMMATRIX* boneTransforms, FXMMATRIX world, TEffectIterator partEffects) { @@ -198,9 +606,13 @@ namespace DirectX 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) + assert(nbones > 0 && boneTransforms != nullptr); + + ModelBone::TransformArray temp; + + for (const auto& mit : meshParts) { - auto part = it.get(); + auto part = mit.get(); assert(part != nullptr); // Get the effect at the location specified by the part's material @@ -213,473 +625,68 @@ namespace DirectX 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); } } - 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); - }; + #ifdef __clang__ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-dynamic-exception-spec" + #endif + DEFINE_ENUM_FLAG_OPERATORS(ModelLoaderFlags); - //---------------------------------------------------------------------------------- - // 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; - - handle = heap->GetGPUDescriptorHandleForHeapStart(); - 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; - - 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 pop + #endif } - -#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/Kits/DirectXTK12/Inc/PostProcess.h b/Kits/DirectXTK12/Inc/PostProcess.h index 028257e..ed71b42 100644 --- a/Kits/DirectXTK12/Inc/PostProcess.h +++ b/Kits/DirectXTK12/Inc/PostProcess.h @@ -29,186 +29,189 @@ namespace DirectX { - //---------------------------------------------------------------------------------- - // Abstract interface representing a post-process pass - class IPostProcess + inline namespace DX12 { - 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 + //------------------------------------------------------------------------------ + // Abstract interface representing a post-process pass + class IPostProcess { - Copy, - Monochrome, - Sepia, - DownScale_2x2, - DownScale_4x4, - GaussianBlur_5x5, - BloomExtract, - BloomBlur, - Effect_Max + 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; }; - 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 + //------------------------------------------------------------------------------ + // Basic post-process + class BasicPostProcess : public IPostProcess { - Merge, - BloomCombine, - Effect_Max + 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; }; - 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 + //------------------------------------------------------------------------------ + // Dual-texure post-process + class DualPostProcess : public IPostProcess { - None, // Pass-through - Saturate, // Clamp [0,1] - Reinhard, // x/(1+x) - ACESFilmic, - Operator_Max + 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; }; - // Electro-Optical Transfer Function (EOTF) - enum TransferFunction : unsigned int + + //------------------------------------------------------------------------------ + // Tone-map post-process + class ToneMapPostProcess : public IPostProcess { - 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 + 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; }; - - // 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/Kits/DirectXTK12/Inc/PrimitiveBatch.h b/Kits/DirectXTK12/Inc/PrimitiveBatch.h index be66886..2d0aaff 100644 --- a/Kits/DirectXTK12/Inc/PrimitiveBatch.h +++ b/Kits/DirectXTK12/Inc/PrimitiveBatch.h @@ -29,117 +29,119 @@ namespace DirectX { - namespace Internal + inline namespace DX12 { - // Base class, not to be used directly: clients should access this via the derived PrimitiveBatch. - class PrimitiveBatchBase + namespace Private { - protected: - PrimitiveBatchBase(_In_ ID3D12Device* device, size_t maxIndices, size_t maxVertices, size_t vertexSize); + // 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&&) noexcept; + PrimitiveBatchBase& operator= (PrimitiveBatchBase&&) noexcept; - PrimitiveBatchBase(PrimitiveBatchBase const&) = delete; - PrimitiveBatchBase& operator= (PrimitiveBatchBase const&) = delete; + PrimitiveBatchBase(PrimitiveBatchBase const&) = delete; + PrimitiveBatchBase& operator= (PrimitiveBatchBase const&) = delete; - virtual ~PrimitiveBatchBase(); + 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: - // Begin/End a batch of primitive drawing operations. - void __cdecl Begin(_In_ ID3D12GraphicsCommandList* cmdList); - void __cdecl End(); + explicit PrimitiveBatch(_In_ ID3D12Device* device, + size_t maxIndices = DefaultBatchSize * 3, + size_t maxVertices = DefaultBatchSize) + : PrimitiveBatchBase(device, maxIndices, maxVertices, sizeof(TVertex)) + { + } - 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); + PrimitiveBatch(PrimitiveBatch&&) = default; + PrimitiveBatch& operator= (PrimitiveBatch&&) = default; - private: - // Private implementation. - class Impl; + PrimitiveBatch(PrimitiveBatch const&) = delete; + PrimitiveBatch& operator= (PrimitiveBatch const&) = delete; - std::unique_ptr pImpl; + // 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; + } }; } - - - // Template makes the API typesafe, eg. PrimitiveBatch. - template - class PrimitiveBatch : public Internal::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/Kits/DirectXTK12/Inc/SpriteBatch.h b/Kits/DirectXTK12/Inc/SpriteBatch.h index 4ffb653..1ad353d 100644 --- a/Kits/DirectXTK12/Inc/SpriteBatch.h +++ b/Kits/DirectXTK12/Inc/SpriteBatch.h @@ -37,118 +37,121 @@ namespace DirectX { class ResourceUploadBatch; - enum SpriteSortMode + inline namespace DX12 { - 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{} + enum SpriteSortMode { - if (isamplerDescriptor) - this->samplerDescriptor = *isamplerDescriptor; - } + SpriteSortMode_Deferred, + SpriteSortMode_Immediate, + SpriteSortMode_Texture, + SpriteSortMode_BackToFront, + SpriteSortMode_FrontToBack, + }; - 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; + enum SpriteEffects : uint32_t + { + SpriteEffects_None = 0, + SpriteEffects_FlipHorizontally = 1, + SpriteEffects_FlipVertically = 2, + SpriteEffects_FlipBoth = SpriteEffects_FlipHorizontally | SpriteEffects_FlipVertically, + }; - private: - static const D3D12_BLEND_DESC s_DefaultBlendDesc; - static const D3D12_RASTERIZER_DESC s_DefaultRasterizerDesc; - static const D3D12_DEPTH_STENCIL_DESC s_DefaultDepthStencilDesc; - }; + 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; + } - class SpriteBatch - { - public: - SpriteBatch(_In_ ID3D12Device* device, ResourceUploadBatch& upload, - const SpriteBatchPipelineStateDescription& psoDesc, - _In_opt_ const D3D12_VIEWPORT* viewport = nullptr); + 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; - SpriteBatch(SpriteBatch&&) noexcept; - SpriteBatch& operator= (SpriteBatch&&) noexcept; + private: + static const D3D12_BLEND_DESC s_DefaultBlendDesc; + static const D3D12_RASTERIZER_DESC s_DefaultRasterizerDesc; + static const D3D12_DEPTH_STENCIL_DESC s_DefaultDepthStencilDesc; + }; - SpriteBatch(SpriteBatch const&) = delete; - SpriteBatch& operator= (SpriteBatch const&) = delete; + class SpriteBatch + { + public: + SpriteBatch(_In_ ID3D12Device* device, ResourceUploadBatch& upload, + const SpriteBatchPipelineStateDescription& psoDesc, + _In_opt_ const D3D12_VIEWPORT* viewport = nullptr); - virtual ~SpriteBatch(); + SpriteBatch(SpriteBatch&&) noexcept; + SpriteBatch& operator= (SpriteBatch&&) noexcept; - // 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(); + SpriteBatch(SpriteBatch const&) = delete; + SpriteBatch& operator= (SpriteBatch const&) = delete; - // 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); + virtual ~SpriteBatch(); - // 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); + // 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 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); + // 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); - // 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 + // 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); - // Set viewport for sprite transformation - void __cdecl SetViewport(const D3D12_VIEWPORT& viewPort); + // 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); - private: - // Private implementation. - struct Impl; + // 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 - std::unique_ptr pImpl; + // Set viewport for sprite transformation + void __cdecl SetViewport(const D3D12_VIEWPORT& viewPort); - static const XMMATRIX MatrixIdentity; - static const XMFLOAT2 Float2Zero; - }; + private: + // Private implementation. + struct Impl; + + std::unique_ptr pImpl; + + static const XMMATRIX MatrixIdentity; + static const XMFLOAT2 Float2Zero; + }; + } } diff --git a/Kits/DirectXTK12/Inc/SpriteFont.h b/Kits/DirectXTK12/Inc/SpriteFont.h index f435c27..5f0c362 100644 --- a/Kits/DirectXTK12/Inc/SpriteFont.h +++ b/Kits/DirectXTK12/Inc/SpriteFont.h @@ -18,84 +18,87 @@ namespace DirectX { - class SpriteFont + inline namespace DX12 { - 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 + class SpriteFont { - uint32_t Character; - RECT Subrect; - float XOffset; - float YOffset; - float XAdvance; + 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; + }; + + + private: + // Private implementation. + class Impl; + + std::unique_ptr pImpl; + + static const XMFLOAT2 Float2Zero; }; - - - private: - // Private implementation. - class Impl; - - std::unique_ptr pImpl; - - static const XMFLOAT2 Float2Zero; - }; + } } diff --git a/Kits/DirectXTK12/Inc/VertexTypes.h b/Kits/DirectXTK12/Inc/VertexTypes.h index e1c3892..78c14db 100644 --- a/Kits/DirectXTK12/Inc/VertexTypes.h +++ b/Kits/DirectXTK12/Inc/VertexTypes.h @@ -25,333 +25,336 @@ 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) + struct VertexPosition { - } + VertexPosition() = default; - VertexPosition(FXMVECTOR iposition) noexcept + 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 { - XMStoreFloat3(&this->position, iposition); - } + VertexPositionColor() = default; - XMFLOAT3 position; + VertexPositionColor(const VertexPositionColor&) = default; + VertexPositionColor& operator=(const VertexPositionColor&) = default; - static const D3D12_INPUT_LAYOUT_DESC InputLayout; + VertexPositionColor(VertexPositionColor&&) = default; + VertexPositionColor& operator=(VertexPositionColor&&) = default; - private: - static constexpr unsigned int InputElementCount = 1; - static const D3D12_INPUT_ELEMENT_DESC InputElements[InputElementCount]; - }; + 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 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) + // Vertex struct holding position and texture mapping information. + struct VertexPositionTexture { - } + VertexPositionTexture() = default; - VertexPositionColor(FXMVECTOR iposition, FXMVECTOR icolor) noexcept + 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 { - XMStoreFloat3(&this->position, iposition); - XMStoreFloat4(&this->color, icolor); - } + VertexPositionDualTexture() = default; - XMFLOAT3 position; - XMFLOAT4 color; + VertexPositionDualTexture(const VertexPositionDualTexture&) = default; + VertexPositionDualTexture& operator=(const VertexPositionDualTexture&) = default; - static const D3D12_INPUT_LAYOUT_DESC InputLayout; + VertexPositionDualTexture(VertexPositionDualTexture&&) = default; + VertexPositionDualTexture& operator=(VertexPositionDualTexture&&) = default; - private: - static constexpr unsigned int InputElementCount = 2; - static const D3D12_INPUT_ELEMENT_DESC InputElements[InputElementCount]; - }; + 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 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) + // Vertex struct holding position and normal vector. + struct VertexPositionNormal { - } + VertexPositionNormal() = default; - VertexPositionTexture(FXMVECTOR iposition, FXMVECTOR itextureCoordinate) noexcept + 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 { - XMStoreFloat3(&this->position, iposition); - XMStoreFloat2(&this->textureCoordinate, itextureCoordinate); - } + VertexPositionColorTexture() = default; - XMFLOAT3 position; - XMFLOAT2 textureCoordinate; + VertexPositionColorTexture(const VertexPositionColorTexture&) = default; + VertexPositionColorTexture& operator=(const VertexPositionColorTexture&) = default; - static const D3D12_INPUT_LAYOUT_DESC InputLayout; + VertexPositionColorTexture(VertexPositionColorTexture&&) = default; + VertexPositionColorTexture& operator=(VertexPositionColorTexture&&) = default; - private: - static constexpr unsigned int InputElementCount = 2; - static const D3D12_INPUT_ELEMENT_DESC InputElements[InputElementCount]; - }; + 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 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) + // Vertex struct holding position, normal vector, and color information. + struct VertexPositionNormalColor { - } + VertexPositionNormalColor() = default; - VertexPositionDualTexture( - FXMVECTOR iposition, - FXMVECTOR itextureCoordinate0, - FXMVECTOR itextureCoordinate1) noexcept + 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 { - XMStoreFloat3(&this->position, iposition); - XMStoreFloat2(&this->textureCoordinate0, itextureCoordinate0); - XMStoreFloat2(&this->textureCoordinate1, itextureCoordinate1); - } + VertexPositionNormalTexture() = default; - XMFLOAT3 position; - XMFLOAT2 textureCoordinate0; - XMFLOAT2 textureCoordinate1; + VertexPositionNormalTexture(const VertexPositionNormalTexture&) = default; + VertexPositionNormalTexture& operator=(const VertexPositionNormalTexture&) = default; - static const D3D12_INPUT_LAYOUT_DESC InputLayout; + VertexPositionNormalTexture(VertexPositionNormalTexture&&) = default; + VertexPositionNormalTexture& operator=(VertexPositionNormalTexture&&) = default; - private: - static constexpr unsigned int InputElementCount = 3; - static const D3D12_INPUT_ELEMENT_DESC InputElements[InputElementCount]; - }; + 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 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) + // Vertex struct holding position, normal vector, color, and texture mapping information. + struct VertexPositionNormalColorTexture { - } + VertexPositionNormalColorTexture() = default; - VertexPositionNormal(FXMVECTOR iposition, FXMVECTOR inormal) noexcept - { - XMStoreFloat3(&this->position, iposition); - XMStoreFloat3(&this->normal, inormal); - } + VertexPositionNormalColorTexture(const VertexPositionNormalColorTexture&) = default; + VertexPositionNormalColorTexture& operator=(const VertexPositionNormalColorTexture&) = default; - XMFLOAT3 position; - XMFLOAT3 normal; + VertexPositionNormalColorTexture(VertexPositionNormalColorTexture&&) = default; + VertexPositionNormalColorTexture& operator=(VertexPositionNormalColorTexture&&) = default; - static const D3D12_INPUT_LAYOUT_DESC InputLayout; + VertexPositionNormalColorTexture( + XMFLOAT3 const& iposition, + XMFLOAT3 const& inormal, + XMFLOAT4 const& icolor, + XMFLOAT2 const& itextureCoordinate) noexcept + : position(iposition), + normal(inormal), + color(icolor), + textureCoordinate(itextureCoordinate) + { + } - private: - static constexpr unsigned int InputElementCount = 2; - static const D3D12_INPUT_ELEMENT_DESC InputElements[InputElementCount]; - }; + 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; - // Vertex struct holding position, color, and texture mapping information. - struct VertexPositionColorTexture - { - VertexPositionColorTexture() = default; + static const D3D12_INPUT_LAYOUT_DESC InputLayout; - 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]; - }; + private: + static constexpr unsigned int InputElementCount = 4; + static const D3D12_INPUT_ELEMENT_DESC InputElements[InputElementCount]; + }; + } } diff --git a/Kits/DirectXTK12/Inc/WICTextureLoader.h b/Kits/DirectXTK12/Inc/WICTextureLoader.h index 92824db..d09406b 100644 --- a/Kits/DirectXTK12/Inc/WICTextureLoader.h +++ b/Kits/DirectXTK12/Inc/WICTextureLoader.h @@ -39,18 +39,21 @@ namespace DirectX { - enum WIC_LOADER_FLAGS : uint32_t + inline namespace DX12 { - 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, - }; + 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; @@ -137,7 +140,10 @@ namespace DirectX #pragma clang diagnostic ignored "-Wdeprecated-dynamic-exception-spec" #endif - DEFINE_ENUM_FLAG_OPERATORS(WIC_LOADER_FLAGS); + inline namespace DX12 + { + DEFINE_ENUM_FLAG_OPERATORS(WIC_LOADER_FLAGS); + } #ifdef __clang__ #pragma clang diagnostic pop diff --git a/Kits/DirectXTK12/README.md b/Kits/DirectXTK12/README.md index c920ab8..800ce7b 100644 --- a/Kits/DirectXTK12/README.md +++ b/Kits/DirectXTK12/README.md @@ -6,11 +6,11 @@ http://go.microsoft.com/fwlink/?LinkID=615561 Copyright (c) Microsoft Corporation. -**June 9, 2022** +**October 17, 2022** 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 / Windows 10, game titles for Xbox Series X\|S / Xbox One, and Win32 desktop applications for Windows 11 / Windows 10. -This code is designed to build with Visual Studio 2019 (16.9 or later), Visual Studio 2022, or clang for Windows v11 or later. Use of the Windows 10 May 2020 Update SDK ([19041](https://walbourn.github.io/windows-10-may-2020-update-sdk/)) or later is required. +This code is designed to build with Visual Studio 2019 (16.11), Visual Studio 2022, or clang for Windows v11 or later. Use of the Windows 10 May 2020 Update SDK ([19041](https://walbourn.github.io/windows-10-may-2020-update-sdk/)) or later is required. 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). @@ -80,6 +80,8 @@ For the latest version of DirectXTK12, bug reports, etc. please visit the projec ## Release Notes +* 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. + * As of the March 2022 release, legacy Xbox One XDK support requires the XDK April 2018 release or later. Upgrading to the Microsoft GDKX is strongly recommended. * 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. @@ -116,7 +118,7 @@ This project may contain trademarks or logos for projects, products, or services ## 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 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. diff --git a/Kits/DirectXTK12/Src/BasicPostProcess.cpp b/Kits/DirectXTK12/Src/BasicPostProcess.cpp index 46f0bf0..ac31c63 100644 --- a/Kits/DirectXTK12/Src/BasicPostProcess.cpp +++ b/Kits/DirectXTK12/Src/BasicPostProcess.cpp @@ -572,7 +572,12 @@ void BasicPostProcess::SetSourceTexture(D3D12_GPU_DESCRIPTOR_HANDLE srvDescripto 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; } diff --git a/Kits/DirectXTK12/Src/DDSTextureLoader.cpp b/Kits/DirectXTK12/Src/DDSTextureLoader.cpp index 7b0594b..6dc5360 100644 --- a/Kits/DirectXTK12/Src/DDSTextureLoader.cpp +++ b/Kits/DirectXTK12/Src/DDSTextureLoader.cpp @@ -239,6 +239,10 @@ namespace { format = MakeSRGB(format); } + else if (loadFlags & DDS_LOADER_IGNORE_SRGB) + { + format = MakeLinear(format); + } D3D12_RESOURCE_DESC desc = {}; desc.Width = static_cast(width); @@ -871,7 +875,14 @@ HRESULT DirectX::CreateDDSTextureFromMemoryEx( D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); // If it's missing mips, let's generate them - if ((loadFlags & DDS_LOADER_MIP_AUTOGEN) && subresources.size() != (*texture)->GetDesc().MipLevels) + #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); } @@ -986,7 +997,14 @@ HRESULT DirectX::CreateDDSTextureFromFileEx( D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); // If it's missing mips, let's generate them - if ((loadFlags & DDS_LOADER_MIP_AUTOGEN) && subresources.size() != (*texture)->GetDesc().MipLevels) + #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); } diff --git a/Kits/DirectXTK12/Src/DescriptorHeap.cpp b/Kits/DirectXTK12/Src/DescriptorHeap.cpp index ab0f87f..3c912ee 100644 --- a/Kits/DirectXTK12/Src/DescriptorHeap.cpp +++ b/Kits/DirectXTK12/Src/DescriptorHeap.cpp @@ -37,9 +37,15 @@ 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())); @@ -168,11 +174,19 @@ void DescriptorHeap::Create( 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 } } diff --git a/Kits/DirectXTK12/Src/DirectXHelpers.cpp b/Kits/DirectXTK12/Src/DirectXHelpers.cpp index 424ef7d..1364a2f 100644 --- a/Kits/DirectXTK12/Src/DirectXHelpers.cpp +++ b/Kits/DirectXTK12/Src/DirectXHelpers.cpp @@ -21,7 +21,12 @@ void DirectX::CreateShaderResourceView( 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 D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {}; srvDesc.Format = desc.Format; diff --git a/Kits/DirectXTK12/Src/EffectCommon.h b/Kits/DirectXTK12/Src/EffectCommon.h index 95a48d3..c30aa67 100644 --- a/Kits/DirectXTK12/Src/EffectCommon.h +++ b/Kits/DirectXTK12/Src/EffectCommon.h @@ -28,13 +28,16 @@ namespace DirectX { - // Internal effect flags - namespace EffectFlags + inline namespace DX12 { - constexpr int PerPixelLightingBit = 0x04; - } + // Internal effect flags + namespace EffectFlags + { + constexpr int PerPixelLightingBit = 0x04; + } - static_assert(((EffectFlags::PerPixelLighting)& EffectFlags::PerPixelLightingBit) != 0, "PerPixelLighting enum flags mismatch"); + static_assert(((EffectFlags::PerPixelLighting)& EffectFlags::PerPixelLightingBit) != 0, "PerPixelLighting enum flags mismatch"); + } // Bitfield tracks which derived parameter values need to be recomputed. namespace EffectDirtyFlags diff --git a/Kits/DirectXTK12/Src/EffectFactory.cpp b/Kits/DirectXTK12/Src/EffectFactory.cpp index d20bea6..aa24df9 100644 --- a/Kits/DirectXTK12/Src/EffectFactory.cpp +++ b/Kits/DirectXTK12/Src/EffectFactory.cpp @@ -497,11 +497,20 @@ EffectFactory::EffectFactory(_In_ ID3D12DescriptorHeap* textureDescriptors, _In_ throw std::invalid_argument("Descriptor heap cannot be null if no device is provided. Use the alternative EffectFactory constructor instead."); } - if (textureDescriptors->GetDesc().Type != D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV) +#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 (samplerDescriptors->GetDesc().Type != D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER) + if (samplerHeapType != D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER) { throw std::invalid_argument("EffectFactory::CreateEffect requires a SAMPLER descriptor heap for samplerDescriptors."); } diff --git a/Kits/DirectXTK12/Src/EffectPipelineStateDescription.cpp b/Kits/DirectXTK12/Src/EffectPipelineStateDescription.cpp index 4bebb0e..b645ae4 100644 --- a/Kits/DirectXTK12/Src/EffectPipelineStateDescription.cpp +++ b/Kits/DirectXTK12/Src/EffectPipelineStateDescription.cpp @@ -91,7 +91,13 @@ void EffectPipelineStateDescription::CreatePipelineState( 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; diff --git a/Kits/DirectXTK12/Src/GamePad.cpp b/Kits/DirectXTK12/Src/GamePad.cpp index 116f02d..65b43c4 100644 --- a/Kits/DirectXTK12/Src/GamePad.cpp +++ b/Kits/DirectXTK12/Src/GamePad.cpp @@ -1683,11 +1683,7 @@ GamePad& GamePad::Get() // ButtonStateTracker //====================================================================================== -#define UPDATE_BUTTON_STATE(field) field = static_cast( ( !!state.buttons.field ) | ( ( !!state.buttons.field ^ !!lastState.buttons.field ) << 1 ) ); - -#ifdef __clang__ -#pragma clang diagnostic ignored "-Wextra-semi-stmt" -#endif +#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 { diff --git a/Kits/DirectXTK12/Src/LoaderHelpers.h b/Kits/DirectXTK12/Src/LoaderHelpers.h index 6c3298c..cc32708 100644 --- a/Kits/DirectXTK12/Src/LoaderHelpers.h +++ b/Kits/DirectXTK12/Src/LoaderHelpers.h @@ -221,6 +221,37 @@ namespace DirectX } } + //-------------------------------------------------------------------------------------- + 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 { diff --git a/Kits/DirectXTK12/Src/Mouse.cpp b/Kits/DirectXTK12/Src/Mouse.cpp index a782e3f..480ba50 100644 --- a/Kits/DirectXTK12/Src/Mouse.cpp +++ b/Kits/DirectXTK12/Src/Mouse.cpp @@ -1193,8 +1193,8 @@ void Mouse::ProcessMessage(UINT message, WPARAM wParam, LPARAM lParam) if (!pImpl) return; - HANDLE events[3] = { pImpl->mScrollWheelValue.get(), pImpl->mAbsoluteMode.get(), pImpl->mRelativeMode.get() }; - switch (WaitForMultipleObjectsEx(static_cast(std::size(events)), events, FALSE, 0, FALSE)) + // First handle any pending scroll wheel reset event. + switch (WaitForSingleObjectEx(pImpl->mScrollWheelValue.get(), 0, FALSE)) { default: case WAIT_TIMEOUT: @@ -1202,10 +1202,22 @@ void Mouse::ProcessMessage(UINT message, WPARAM wParam, LPARAM lParam) case WAIT_OBJECT_0: pImpl->mState.scrollWheelValue = 0; - ResetEvent(events[0]); + ResetEvent(pImpl->mScrollWheelValue.get()); break; - case (WAIT_OBJECT_0 + 1): + 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); @@ -1226,7 +1238,7 @@ void Mouse::ProcessMessage(UINT message, WPARAM wParam, LPARAM lParam) } break; - case (WAIT_OBJECT_0 + 2): + case (WAIT_OBJECT_0 + 1): { ResetEvent(pImpl->mRelativeRead.get()); @@ -1484,11 +1496,7 @@ Mouse& Mouse::Get() // ButtonStateTracker //====================================================================================== -#define UPDATE_BUTTON_STATE(field) field = static_cast( ( !!state.field ) | ( ( !!state.field ^ !!lastState.field ) << 1 ) ); - -#ifdef __clang__ -#pragma clang diagnostic ignored "-Wextra-semi-stmt" -#endif +#define UPDATE_BUTTON_STATE(field) field = static_cast( ( !!state.field ) | ( ( !!state.field ^ !!lastState.field ) << 1 ) ) void Mouse::ButtonStateTracker::Update(const Mouse::State& state) noexcept { diff --git a/Kits/DirectXTK12/Src/PBREffectFactory.cpp b/Kits/DirectXTK12/Src/PBREffectFactory.cpp index 6bcbaec..734a13b 100644 --- a/Kits/DirectXTK12/Src/PBREffectFactory.cpp +++ b/Kits/DirectXTK12/Src/PBREffectFactory.cpp @@ -257,11 +257,20 @@ PBREffectFactory::PBREffectFactory(_In_ ID3D12DescriptorHeap* textureDescriptors throw std::invalid_argument("Descriptor heap cannot be null if no device is provided. Use the alternative PBREffectFactory constructor instead."); } - if (textureDescriptors->GetDesc().Type != D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV) +#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 (samplerDescriptors->GetDesc().Type != D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER) + if (samplerHeapType != D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER) { throw std::invalid_argument("PBREffectFactory::CreateEffect requires a SAMPLER descriptor heap for samplerDescriptors."); } diff --git a/Kits/DirectXTK12/Src/PrimitiveBatch.cpp b/Kits/DirectXTK12/Src/PrimitiveBatch.cpp index 1a5c2b3..1dc1e4e 100644 --- a/Kits/DirectXTK12/Src/PrimitiveBatch.cpp +++ b/Kits/DirectXTK12/Src/PrimitiveBatch.cpp @@ -14,7 +14,7 @@ #include "GraphicsMemory.h" using namespace DirectX; -using namespace DirectX::Internal; +using namespace DirectX::DX12::Private; using Microsoft::WRL::ComPtr; diff --git a/Kits/DirectXTK12/Src/ResourceUploadBatch.cpp b/Kits/DirectXTK12/Src/ResourceUploadBatch.cpp index 6510c0d..d8f22f9 100644 --- a/Kits/DirectXTK12/Src/ResourceUploadBatch.cpp +++ b/Kits/DirectXTK12/Src/ResourceUploadBatch.cpp @@ -418,7 +418,12 @@ public: 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) { @@ -626,7 +631,12 @@ private: 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); @@ -683,7 +693,12 @@ private: 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; @@ -734,12 +749,17 @@ private: mList->SetComputeRootSignature(mGenMipsResources->rootSignature.Get()); mList->SetPipelineState(pso.Get()); mList->SetDescriptorHeaps(1, descriptorHeap.GetAddressOf()); - mList->SetComputeRootDescriptorTable(GenerateMipsResources::SourceTexture, descriptorHeap->GetGPUDescriptorHandleForHeapStart()); + + #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( - descriptorHeap->GetGPUDescriptorHandleForHeapStart(), - descriptorSize); // offset by 1 descriptor + CD3DX12_GPU_DESCRIPTOR_HANDLE uavH(handle, descriptorSize); // offset by 1 descriptor // Process each mip auto mipWidth = static_cast(desc.Width); @@ -823,7 +843,12 @@ private: 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; @@ -888,7 +913,12 @@ private: 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 @@ -900,7 +930,12 @@ private: #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; diff --git a/Kits/DirectXTK12/Src/ScreenGrab.cpp b/Kits/DirectXTK12/Src/ScreenGrab.cpp index f63f120..3ce6f68 100644 --- a/Kits/DirectXTK12/Src/ScreenGrab.cpp +++ b/Kits/DirectXTK12/Src/ScreenGrab.cpp @@ -234,7 +234,12 @@ HRESULT DirectX::SaveDDSTextureToFile( 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; @@ -430,9 +435,12 @@ HRESULT DirectX::SaveDDSTextureToFile( //-------------------------------------------------------------------------------------- namespace DirectX { - namespace Internal + inline namespace DX12 { - extern IWICImagingFactory2* GetWIC() noexcept; + namespace Internal + { + extern IWICImagingFactory2* GetWIC() noexcept; + } } } @@ -448,7 +456,7 @@ HRESULT DirectX::SaveWICTextureToFile( std::function setCustomProps, bool forceSRGB) { - using namespace DirectX::Internal; + using namespace DirectX::DX12::Internal; if (!fileName) return E_INVALIDARG; @@ -457,7 +465,12 @@ HRESULT DirectX::SaveWICTextureToFile( 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; diff --git a/Kits/DirectXTK12/Src/Shaders/CompileShaders.cmd b/Kits/DirectXTK12/Src/Shaders/CompileShaders.cmd index b625aed..24c68a4 100644 --- a/Kits/DirectXTK12/Src/Shaders/CompileShaders.cmd +++ b/Kits/DirectXTK12/Src/Shaders/CompileShaders.cmd @@ -46,6 +46,7 @@ if not exist %XBOXDXC% goto needgxdk goto continue :continuedxil +if defined DirectXShaderCompiler goto dxcviaenv set PCDXC="%WindowsSdkVerBinPath%x86\dxc.exe" if exist %PCDXC% goto continue set PCDXC="%WindowsSdkBinPath%%WindowsSDKVersion%\x86\dxc.exe" @@ -54,6 +55,11 @@ 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= @@ -284,56 +290,56 @@ 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 +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 +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 +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 +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 +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 +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% /T%2_6_0 /E%3 "/Fh%CompileShadersOutput%\%XBOXPREFIX%%1_%3.inc" "/Fd%CompileShadersOutput%\%XBOXPREFIX%%1_%3.pdb" /Vn%1_%3 +set dxc=%XBOXDXC% "%1.fx" %FXCOPTS% /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% /Tcs_6_0 /E%2 "/Fh%CompileShadersOutput%\%XBOXPREFIX%%1_%2.inc" "/Fd%CompileShadersOutput%\%XBOXPREFIX%%1_%2.pdb" /Vn%1_%2 +set dxc=%XBOXDXC% "%1.hlsl" %FXCOPTS% /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 @@ -342,9 +348,13 @@ exit /b :needxdk echo ERROR: CompileShaders xbox requires the Microsoft Xbox One XDK echo (try re-running from the XDK Command Prompt) -exit /b +exit /b 1 :needgxdk echo ERROR: CompileShaders gxdk requires the Microsoft Gaming SDK -echo (try re-running from the Gaming GXDK Command Prompt) -exit /b +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/Kits/DirectXTK12/Src/WICTextureLoader.cpp b/Kits/DirectXTK12/Src/WICTextureLoader.cpp index 6bff4b6..fe0ffda 100644 --- a/Kits/DirectXTK12/Src/WICTextureLoader.cpp +++ b/Kits/DirectXTK12/Src/WICTextureLoader.cpp @@ -154,14 +154,17 @@ namespace //-------------------------------------------------------------------------------------- namespace DirectX { - namespace Internal + inline namespace DX12 { - IWICImagingFactory2* GetWIC() noexcept; - // Also used by ScreenGrab + namespace Internal + { + IWICImagingFactory2* GetWIC() noexcept; + // Also used by ScreenGrab + } } } -IWICImagingFactory2* DirectX::Internal::GetWIC() noexcept +IWICImagingFactory2* DirectX::DX12::Internal::GetWIC() noexcept { static INIT_ONCE s_initOnce = INIT_ONCE_STATIC_INIT; @@ -178,7 +181,7 @@ IWICImagingFactory2* DirectX::Internal::GetWIC() noexcept return factory; } -using namespace Internal; +using namespace DirectX::DX12::Internal; namespace { diff --git a/Kits/DirectXTK12/Src/d3dx12.h b/Kits/DirectXTK12/Src/d3dx12.h index 42f26a6..e873d96 100644 --- a/Kits/DirectXTK12/Src/d3dx12.h +++ b/Kits/DirectXTK12/Src/d3dx12.h @@ -76,7 +76,12 @@ struct CD3DX12_VIEWPORT : public D3D12_VIEWPORT 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) @@ -2040,7 +2045,12 @@ inline UINT64 GetRequiredIntermediateSize( _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; @@ -2066,8 +2076,14 @@ inline UINT64 UpdateSubresources( _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) || @@ -2125,8 +2141,14 @@ inline UINT64 UpdateSubresources( _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) || @@ -2194,7 +2216,12 @@ inline UINT64 UpdateSubresources( 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); @@ -2232,7 +2259,12 @@ inline UINT64 UpdateSubresources( 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); @@ -2260,7 +2292,12 @@ inline UINT64 UpdateSubresources( 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); @@ -2287,7 +2324,12 @@ inline UINT64 UpdateSubresources( 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); @@ -4071,20 +4113,20 @@ public: 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 : + 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 + firstMipLevel, + numMips, + firstArraySlice, + numArraySlices, + firstPlane, + numPlanes } {} }; diff --git a/Kits/DirectXTK12/Src/pch.h b/Kits/DirectXTK12/Src/pch.h index a43a70a..e82b091 100644 --- a/Kits/DirectXTK12/Src/pch.h +++ b/Kits/DirectXTK12/Src/pch.h @@ -10,7 +10,7 @@ #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 26812) +#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 @@ -35,6 +35,7 @@ // 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 @@ -234,6 +235,12 @@ #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 diff --git a/Kits/DirectXTex/BC.h b/Kits/DirectXTex/BC.h index 44482b4..cef5404 100644 --- a/Kits/DirectXTex/BC.h +++ b/Kits/DirectXTex/BC.h @@ -60,7 +60,6 @@ namespace DirectX public: HDRColorA() = default; HDRColorA(float _r, float _g, float _b, float _a) noexcept : r(_r), g(_g), b(_b), a(_a) {} - HDRColorA(const HDRColorA& c) noexcept : r(c.r), g(c.g), b(c.b), a(c.a) {} // binary operators HDRColorA operator + (const HDRColorA& c) const noexcept diff --git a/Kits/DirectXTex/BC6HBC7.cpp b/Kits/DirectXTex/BC6HBC7.cpp index 2a889d8..132f86e 100644 --- a/Kits/DirectXTex/BC6HBC7.cpp +++ b/Kits/DirectXTex/BC6HBC7.cpp @@ -452,7 +452,6 @@ namespace public: INTColor() = default; INTColor(int nr, int ng, int nb) noexcept : r(nr), g(ng), b(nb), pad(0) {} - INTColor(const INTColor& c) noexcept : r(c.r), g(c.g), b(c.b), pad(0) {} INTColor& operator += (_In_ const INTColor& c) noexcept { @@ -751,9 +750,12 @@ namespace float RoughMSE(_Inout_ EncodeParams* pEP) const noexcept; private: - static const ModeDescriptor ms_aDesc[][82]; - static const ModeInfo ms_aInfo[]; - static const int ms_aModeToInfo[]; + static constexpr uint8_t c_NumModes = 14; + static constexpr uint8_t c_NumModeInfo = 32; + + static const ModeDescriptor ms_aDesc[c_NumModes][82]; + static const ModeInfo ms_aInfo[c_NumModes]; + static const int ms_aModeToInfo[c_NumModeInfo]; }; // BC67 compression (16b bits per texel) @@ -856,12 +858,14 @@ namespace static float RoughMSE(_Inout_ EncodeParams* pEP, _In_ size_t uShape, _In_ size_t uIndexMode) noexcept; private: - static const ModeInfo ms_aInfo[]; + static constexpr uint8_t c_NumModes = 8; + + static const ModeInfo ms_aInfo[c_NumModes]; }; } // BC6H Compression -const D3DX_BC6H::ModeDescriptor D3DX_BC6H::ms_aDesc[14][82] = +const D3DX_BC6H::ModeDescriptor D3DX_BC6H::ms_aDesc[D3DX_BC6H::c_NumModes][82] = { { // Mode 1 (0x00) - 10 5 5 5 { M, 0}, { M, 1}, {GY, 4}, {BY, 4}, {BZ, 4}, {RW, 0}, {RW, 1}, {RW, 2}, {RW, 3}, {RW, 4}, @@ -1033,7 +1037,7 @@ const D3DX_BC6H::ModeDescriptor D3DX_BC6H::ms_aDesc[14][82] = }; // Mode, Partitions, Transformed, IndexPrec, RGBAPrec -const D3DX_BC6H::ModeInfo D3DX_BC6H::ms_aInfo[] = +const D3DX_BC6H::ModeInfo D3DX_BC6H::ms_aInfo[D3DX_BC6H::c_NumModes] = { {0x00, 1, true, 3, { { LDRColorA(10,10,10,0), LDRColorA(5, 5, 5,0) }, { LDRColorA(5,5,5,0), LDRColorA(5,5,5,0) } } }, // Mode 1 {0x01, 1, true, 3, { { LDRColorA(7, 7, 7,0), LDRColorA(6, 6, 6,0) }, { LDRColorA(6,6,6,0), LDRColorA(6,6,6,0) } } }, // Mode 2 @@ -1051,7 +1055,7 @@ const D3DX_BC6H::ModeInfo D3DX_BC6H::ms_aInfo[] = {0x0f, 0, true, 4, { { LDRColorA(16,16,16,0), LDRColorA(4, 4, 4,0) }, { LDRColorA(0,0,0,0), LDRColorA(0,0,0,0) } } }, // Mode 14 }; -const int D3DX_BC6H::ms_aModeToInfo[] = +const int D3DX_BC6H::ms_aModeToInfo[D3DX_BC6H::c_NumModeInfo] = { 0, // Mode 1 - 0x00 1, // Mode 2 - 0x01 @@ -1088,7 +1092,7 @@ const int D3DX_BC6H::ms_aModeToInfo[] = }; // BC7 compression: uPartitions, uPartitionBits, uPBits, uRotationBits, uIndexModeBits, uIndexPrec, uIndexPrec2, RGBAPrec, RGBAPrecWithP -const D3DX_BC7::ModeInfo D3DX_BC7::ms_aInfo[] = +const D3DX_BC7::ModeInfo D3DX_BC7::ms_aInfo[D3DX_BC7::c_NumModes] = { {2, 4, 6, 0, 0, 3, 0, LDRColorA(4,4,4,0), LDRColorA(5,5,5,0)}, // Mode 0: Color only, 3 Subsets, RGBP 4441 (unique P-bit), 3-bit indecies, 16 partitions @@ -1655,17 +1659,14 @@ void D3DX_BC6H::Decode(bool bSigned, HDRColorA* pOut) const noexcept uMode = static_cast((unsigned(GetBits(uStartBit, 3)) << 2) | uMode); } - assert(uMode < 32); - _Analysis_assume_(uMode < 32); + assert(uMode < c_NumModeInfo); + _Analysis_assume_(uMode < c_NumModeInfo); if (ms_aModeToInfo[uMode] >= 0) { - assert(static_cast(ms_aModeToInfo[uMode]) < std::size(ms_aInfo)); - _Analysis_assume_(ms_aModeToInfo[uMode] < std::size(ms_aInfo)); + assert(static_cast(ms_aModeToInfo[uMode]) < c_NumModes); + _Analysis_assume_(ms_aModeToInfo[uMode] < c_NumModes); const ModeDescriptor* desc = ms_aDesc[ms_aModeToInfo[uMode]]; - - assert(static_cast(ms_aModeToInfo[uMode]) < std::size(ms_aDesc)); - _Analysis_assume_(ms_aModeToInfo[uMode] < std::size(ms_aDesc)); const ModeInfo& info = ms_aInfo[ms_aModeToInfo[uMode]]; INTEndPntPair aEndPts[BC6H_MAX_REGIONS] = {}; @@ -1811,7 +1812,7 @@ void D3DX_BC6H::Encode(bool bSigned, const HDRColorA* const pIn) noexcept EncodeParams EP(pIn, bSigned); - for (EP.uMode = 0; EP.uMode < std::size(ms_aInfo) && EP.fBestErr > 0; ++EP.uMode) + for (EP.uMode = 0; EP.uMode < c_NumModes && EP.fBestErr > 0; ++EP.uMode) { const uint8_t uShapes = ms_aInfo[EP.uMode].uPartitions ? 32u : 1u; // Number of rough cases to look at. reasonable values of this are 1, uShapes/4, and uShapes @@ -1936,6 +1937,9 @@ _Use_decl_annotations_ bool D3DX_BC6H::EndPointsFit(const EncodeParams* pEP, const INTEndPntPair aEndPts[]) noexcept { assert(pEP); + assert(pEP->uMode < c_NumModes); + _Analysis_assume_(pEP->uMode < c_NumModes); + const bool bTransformed = ms_aInfo[pEP->uMode].bTransformed; const bool bIsSigned = pEP->bSigned; const LDRColorA& Prec0 = ms_aInfo[pEP->uMode].RGBAPrec[0][0]; @@ -1978,6 +1982,9 @@ _Use_decl_annotations_ void D3DX_BC6H::GeneratePaletteQuantized(const EncodeParams* pEP, const INTEndPntPair& endPts, INTColor aPalette[]) const noexcept { assert(pEP); + assert(pEP->uMode < c_NumModes); + _Analysis_assume_(pEP->uMode < c_NumModes); + const size_t uIndexPrec = ms_aInfo[pEP->uMode].uIndexPrec; const size_t uNumIndices = size_t(1) << uIndexPrec; assert(uNumIndices > 0); @@ -2029,6 +2036,8 @@ _Use_decl_annotations_ float D3DX_BC6H::MapColorsQuantized(const EncodeParams* pEP, const INTColor aColors[], size_t np, const INTEndPntPair &endPts) const noexcept { assert(pEP); + assert(pEP->uMode < c_NumModes); + _Analysis_assume_(pEP->uMode < c_NumModes); const uint8_t uIndexPrec = ms_aInfo[pEP->uMode].uIndexPrec; auto const uNumIndices = static_cast(1u << uIndexPrec); @@ -2065,6 +2074,9 @@ float D3DX_BC6H::PerturbOne(const EncodeParams* pEP, const INTColor aColors[], s const INTEndPntPair& oldEndPts, INTEndPntPair& newEndPts, float fOldErr, int do_b) const noexcept { assert(pEP); + assert(pEP->uMode < c_NumModes); + _Analysis_assume_(pEP->uMode < c_NumModes); + uint8_t uPrec; switch (ch) { @@ -2178,6 +2190,9 @@ _Use_decl_annotations_ void D3DX_BC6H::OptimizeEndPoints(const EncodeParams* pEP, const float aOrgErr[], const INTEndPntPair aOrgEndPts[], INTEndPntPair aOptEndPts[]) const noexcept { assert(pEP); + assert(pEP->uMode < c_NumModes); + _Analysis_assume_(pEP->uMode < c_NumModes); + const uint8_t uPartitions = ms_aInfo[pEP->uMode].uPartitions; assert(uPartitions < BC6H_MAX_REGIONS); _Analysis_assume_(uPartitions < BC6H_MAX_REGIONS); @@ -2205,6 +2220,9 @@ _Use_decl_annotations_ void D3DX_BC6H::SwapIndices(const EncodeParams* pEP, INTEndPntPair aEndPts[], size_t aIndices[]) noexcept { assert(pEP); + assert(pEP->uMode < c_NumModes); + _Analysis_assume_(pEP->uMode < c_NumModes); + const size_t uPartitions = ms_aInfo[pEP->uMode].uPartitions; const size_t uNumIndices = size_t(1) << ms_aInfo[pEP->uMode].uIndexPrec; const size_t uHighIndexBit = uNumIndices >> 1; @@ -2234,6 +2252,9 @@ _Use_decl_annotations_ void D3DX_BC6H::AssignIndices(const EncodeParams* pEP, const INTEndPntPair aEndPts[], size_t aIndices[], float aTotErr[]) const noexcept { assert(pEP); + assert(pEP->uMode < c_NumModes); + _Analysis_assume_(pEP->uMode < c_NumModes); + const uint8_t uPartitions = ms_aInfo[pEP->uMode].uPartitions; auto const uNumIndices = static_cast(1u << ms_aInfo[pEP->uMode].uIndexPrec); @@ -2276,6 +2297,9 @@ _Use_decl_annotations_ void D3DX_BC6H::QuantizeEndPts(const EncodeParams* pEP, INTEndPntPair* aQntEndPts) const noexcept { assert(pEP && aQntEndPts); + assert(pEP->uMode < c_NumModes); + _Analysis_assume_(pEP->uMode < c_NumModes); + const INTEndPntPair* aUnqEndPts = pEP->aUnqEndPts[pEP->uShape]; const LDRColorA& Prec = ms_aInfo[pEP->uMode].RGBAPrec[0][0]; const uint8_t uPartitions = ms_aInfo[pEP->uMode].uPartitions; @@ -2298,6 +2322,9 @@ _Use_decl_annotations_ void D3DX_BC6H::EmitBlock(const EncodeParams* pEP, const INTEndPntPair aEndPts[], const size_t aIndices[]) noexcept { assert(pEP); + assert(pEP->uMode < c_NumModes); + _Analysis_assume_(pEP->uMode < c_NumModes); + const uint8_t uRealMode = ms_aInfo[pEP->uMode].uMode; const uint8_t uPartitions = ms_aInfo[pEP->uMode].uPartitions; const uint8_t uIndexPrec = ms_aInfo[pEP->uMode].uIndexPrec; @@ -2342,6 +2369,9 @@ _Use_decl_annotations_ void D3DX_BC6H::Refine(EncodeParams* pEP) noexcept { assert(pEP); + assert(pEP->uMode < c_NumModes); + _Analysis_assume_(pEP->uMode < c_NumModes); + const uint8_t uPartitions = ms_aInfo[pEP->uMode].uPartitions; assert(uPartitions < BC6H_MAX_REGIONS); _Analysis_assume_(uPartitions < BC6H_MAX_REGIONS); @@ -2394,6 +2424,9 @@ void D3DX_BC6H::GeneratePaletteUnquantized(const EncodeParams* pEP, size_t uRegi assert(pEP); assert(uRegion < BC6H_MAX_REGIONS && pEP->uShape < BC6H_MAX_SHAPES); _Analysis_assume_(uRegion < BC6H_MAX_REGIONS && pEP->uShape < BC6H_MAX_SHAPES); + assert(pEP->uMode < c_NumModes); + _Analysis_assume_(pEP->uMode < c_NumModes); + const INTEndPntPair& endPts = pEP->aUnqEndPts[pEP->uShape][uRegion]; const uint8_t uIndexPrec = ms_aInfo[pEP->uMode].uIndexPrec; auto const uNumIndices = static_cast(1u << uIndexPrec); @@ -2428,6 +2461,9 @@ _Use_decl_annotations_ float D3DX_BC6H::MapColors(const EncodeParams* pEP, size_t uRegion, size_t np, const size_t* auIndex) const noexcept { assert(pEP); + assert(pEP->uMode < c_NumModes); + _Analysis_assume_(pEP->uMode < c_NumModes); + const uint8_t uIndexPrec = ms_aInfo[pEP->uMode].uIndexPrec; auto const uNumIndices = static_cast(1u << uIndexPrec); INTColor aPalette[BC6H_MAX_INDICES]; @@ -2455,6 +2491,8 @@ float D3DX_BC6H::RoughMSE(EncodeParams* pEP) const noexcept assert(pEP); assert(pEP->uShape < BC6H_MAX_SHAPES); _Analysis_assume_(pEP->uShape < BC6H_MAX_SHAPES); + assert(pEP->uMode < c_NumModes); + _Analysis_assume_(pEP->uMode < c_NumModes); INTEndPntPair* aEndPts = pEP->aUnqEndPts[pEP->uShape]; @@ -2838,6 +2876,9 @@ _Use_decl_annotations_ void D3DX_BC7::GeneratePaletteQuantized(const EncodeParams* pEP, size_t uIndexMode, const LDREndPntPair& endPts, LDRColorA aPalette[]) const noexcept { assert(pEP); + assert(pEP->uMode < c_NumModes); + _Analysis_assume_(pEP->uMode < c_NumModes); + const size_t uIndexPrec = uIndexMode ? ms_aInfo[pEP->uMode].uIndexPrec2 : ms_aInfo[pEP->uMode].uIndexPrec; const size_t uIndexPrec2 = uIndexMode ? ms_aInfo[pEP->uMode].uIndexPrec : ms_aInfo[pEP->uMode].uIndexPrec2; const size_t uNumIndices = size_t(1) << uIndexPrec; @@ -2868,6 +2909,9 @@ float D3DX_BC7::PerturbOne(const EncodeParams* pEP, const LDRColorA aColors[], s const LDREndPntPair &oldEndPts, LDREndPntPair &newEndPts, float fOldErr, uint8_t do_b) const noexcept { assert(pEP); + assert(pEP->uMode < c_NumModes); + _Analysis_assume_(pEP->uMode < c_NumModes); + const int prec = ms_aInfo[pEP->uMode].RGBAPrecWithP[ch]; LDREndPntPair tmp_endPts = newEndPts = oldEndPts; float fMinErr = fOldErr; @@ -2910,6 +2954,9 @@ void D3DX_BC7::Exhaustive(const EncodeParams* pEP, const LDRColorA aColors[], si float& fOrgErr, LDREndPntPair& optEndPt) const noexcept { assert(pEP); + assert(pEP->uMode < c_NumModes); + _Analysis_assume_(pEP->uMode < c_NumModes); + const uint8_t uPrec = ms_aInfo[pEP->uMode].RGBAPrecWithP[ch]; LDREndPntPair tmpEndPt; if (fOrgErr == 0) @@ -2981,6 +3028,8 @@ void D3DX_BC7::OptimizeOne(const EncodeParams* pEP, const LDRColorA aColors[], s float fOrgErr, const LDREndPntPair& org, LDREndPntPair& opt) const noexcept { assert(pEP); + assert(pEP->uMode < c_NumModes); + _Analysis_assume_(pEP->uMode < c_NumModes); float fOptErr = fOrgErr; opt = org; @@ -3047,6 +3096,9 @@ void D3DX_BC7::OptimizeEndPoints(const EncodeParams* pEP, size_t uShape, size_t const LDREndPntPair aOrgEndPts[], LDREndPntPair aOptEndPts[]) const noexcept { assert(pEP); + assert(pEP->uMode < c_NumModes); + _Analysis_assume_(pEP->uMode < c_NumModes); + const uint8_t uPartitions = ms_aInfo[pEP->uMode].uPartitions; assert(uPartitions < BC7_MAX_REGIONS && uShape < BC7_MAX_SHAPES); _Analysis_assume_(uPartitions < BC7_MAX_REGIONS && uShape < BC7_MAX_SHAPES); @@ -3072,6 +3124,8 @@ void D3DX_BC7::AssignIndices(const EncodeParams* pEP, size_t uShape, size_t uInd assert(pEP); assert(uShape < BC7_MAX_SHAPES); _Analysis_assume_(uShape < BC7_MAX_SHAPES); + assert(pEP->uMode < c_NumModes); + _Analysis_assume_(pEP->uMode < c_NumModes); const uint8_t uPartitions = ms_aInfo[pEP->uMode].uPartitions; assert(uPartitions < BC7_MAX_REGIONS); @@ -3149,6 +3203,9 @@ _Use_decl_annotations_ void D3DX_BC7::EmitBlock(const EncodeParams* pEP, size_t uShape, size_t uRotation, size_t uIndexMode, const LDREndPntPair aEndPts[], const size_t aIndex[], const size_t aIndex2[]) noexcept { assert(pEP); + assert(pEP->uMode < c_NumModes); + _Analysis_assume_(pEP->uMode < c_NumModes); + const uint8_t uPartitions = ms_aInfo[pEP->uMode].uPartitions; assert(uPartitions < BC7_MAX_REGIONS); _Analysis_assume_(uPartitions < BC7_MAX_REGIONS); @@ -3236,6 +3293,9 @@ _Use_decl_annotations_ void D3DX_BC7::FixEndpointPBits(const EncodeParams* pEP, const LDREndPntPair *pOrigEndpoints, LDREndPntPair *pFixedEndpoints) noexcept { assert(pEP); + assert(pEP->uMode < c_NumModes); + _Analysis_assume_(pEP->uMode < c_NumModes); + const size_t uPartitions = ms_aInfo[pEP->uMode].uPartitions; assert(uPartitions < BC7_MAX_REGIONS); _Analysis_assume_(uPartitions < BC7_MAX_REGIONS); @@ -3323,6 +3383,8 @@ float D3DX_BC7::Refine(const EncodeParams* pEP, size_t uShape, size_t uRotation, assert(pEP); assert(uShape < BC7_MAX_SHAPES); _Analysis_assume_(uShape < BC7_MAX_SHAPES); + assert(pEP->uMode < c_NumModes); + _Analysis_assume_(pEP->uMode < c_NumModes); const size_t uPartitions = ms_aInfo[pEP->uMode].uPartitions; assert(uPartitions < BC7_MAX_REGIONS); @@ -3379,6 +3441,9 @@ _Use_decl_annotations_ float D3DX_BC7::MapColors(const EncodeParams* pEP, const LDRColorA aColors[], size_t np, size_t uIndexMode, const LDREndPntPair& endPts, float fMinErr) const noexcept { assert(pEP); + assert(pEP->uMode < c_NumModes); + _Analysis_assume_(pEP->uMode < c_NumModes); + const uint8_t uIndexPrec = uIndexMode ? ms_aInfo[pEP->uMode].uIndexPrec2 : ms_aInfo[pEP->uMode].uIndexPrec; const uint8_t uIndexPrec2 = uIndexMode ? ms_aInfo[pEP->uMode].uIndexPrec : ms_aInfo[pEP->uMode].uIndexPrec2; LDRColorA aPalette[BC7_MAX_INDICES]; @@ -3404,6 +3469,9 @@ float D3DX_BC7::RoughMSE(EncodeParams* pEP, size_t uShape, size_t uIndexMode) no assert(pEP); assert(uShape < BC7_MAX_SHAPES); _Analysis_assume_(uShape < BC7_MAX_SHAPES); + assert(pEP->uMode < c_NumModes); + _Analysis_assume_(pEP->uMode < c_NumModes); + LDREndPntPair* aEndPts = pEP->aEndPts[uShape]; const uint8_t uPartitions = ms_aInfo[pEP->uMode].uPartitions; diff --git a/Kits/DirectXTex/DirectXTex.h b/Kits/DirectXTex/DirectXTex.h index ba04068..6ab0a45 100644 --- a/Kits/DirectXTex/DirectXTex.h +++ b/Kits/DirectXTex/DirectXTex.h @@ -124,6 +124,7 @@ namespace DirectX size_t __cdecl ComputeScanlines(_In_ DXGI_FORMAT fmt, _In_ size_t height) noexcept; DXGI_FORMAT __cdecl MakeSRGB(_In_ DXGI_FORMAT fmt) noexcept; + DXGI_FORMAT __cdecl MakeLinear(_In_ DXGI_FORMAT fmt) noexcept; DXGI_FORMAT __cdecl MakeTypeless(_In_ DXGI_FORMAT fmt) noexcept; DXGI_FORMAT __cdecl MakeTypelessUNORM(_In_ DXGI_FORMAT fmt) noexcept; DXGI_FORMAT __cdecl MakeTypelessFLOAT(_In_ DXGI_FORMAT fmt) noexcept; @@ -856,6 +857,15 @@ namespace DirectX _Out_ size_t& required) noexcept; //--------------------------------------------------------------------------------- + // Direct3D interop + + enum CREATETEX_FLAGS : uint32_t + { + CREATETEX_DEFAULT = 0, + CREATETEX_FORCE_SRGB = 0x1, + CREATETEX_IGNORE_SRGB = 0x2, + }; + // Direct3D 11 functions #if defined(__d3d11_h__) || defined(__d3d11_x_h__) bool __cdecl IsSupportedTexture(_In_ ID3D11Device* pDevice, _In_ const TexMetadata& metadata) noexcept; @@ -870,18 +880,17 @@ namespace DirectX HRESULT __cdecl CreateTextureEx( _In_ ID3D11Device* pDevice, _In_reads_(nimages) const Image* srcImages, _In_ size_t nimages, _In_ const TexMetadata& metadata, - _In_ D3D11_USAGE usage, _In_ unsigned int bindFlags, _In_ unsigned int cpuAccessFlags, _In_ unsigned int miscFlags, _In_ bool forceSRGB, + _In_ D3D11_USAGE usage, _In_ unsigned int bindFlags, _In_ unsigned int cpuAccessFlags, _In_ unsigned int miscFlags, _In_ CREATETEX_FLAGS flags, _Outptr_ ID3D11Resource** ppResource) noexcept; HRESULT __cdecl CreateShaderResourceViewEx( _In_ ID3D11Device* pDevice, _In_reads_(nimages) const Image* srcImages, _In_ size_t nimages, _In_ const TexMetadata& metadata, - _In_ D3D11_USAGE usage, _In_ unsigned int bindFlags, _In_ unsigned int cpuAccessFlags, _In_ unsigned int miscFlags, _In_ bool forceSRGB, + _In_ D3D11_USAGE usage, _In_ unsigned int bindFlags, _In_ unsigned int cpuAccessFlags, _In_ unsigned int miscFlags, _In_ CREATETEX_FLAGS flags, _Outptr_ ID3D11ShaderResourceView** ppSRV) noexcept; HRESULT __cdecl CaptureTexture(_In_ ID3D11Device* pDevice, _In_ ID3D11DeviceContext* pContext, _In_ ID3D11Resource* pSource, _Out_ ScratchImage& result) noexcept; #endif - //--------------------------------------------------------------------------------- // Direct3D 12 functions #if defined(__d3d12_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) bool __cdecl IsSupportedTexture(_In_ ID3D12Device* pDevice, _In_ const TexMetadata& metadata) noexcept; @@ -892,7 +901,7 @@ namespace DirectX HRESULT __cdecl CreateTextureEx( _In_ ID3D12Device* pDevice, _In_ const TexMetadata& metadata, - _In_ D3D12_RESOURCE_FLAGS resFlags, _In_ bool forceSRGB, + _In_ D3D12_RESOURCE_FLAGS resFlags, _In_ CREATETEX_FLAGS flags, _Outptr_ ID3D12Resource** ppResource) noexcept; HRESULT __cdecl PrepareUpload( diff --git a/Kits/DirectXTex/DirectXTex.inl b/Kits/DirectXTex/DirectXTex.inl index 1ca81f9..7fef321 100644 --- a/Kits/DirectXTex/DirectXTex.inl +++ b/Kits/DirectXTex/DirectXTex.inl @@ -24,6 +24,7 @@ DEFINE_ENUM_FLAG_OPERATORS(TEX_PMALPHA_FLAGS); DEFINE_ENUM_FLAG_OPERATORS(TEX_COMPRESS_FLAGS); DEFINE_ENUM_FLAG_OPERATORS(CNMAP_FLAGS); DEFINE_ENUM_FLAG_OPERATORS(CMSE_FLAGS); +DEFINE_ENUM_FLAG_OPERATORS(CREATETEX_FLAGS); // WIC_FILTER modes match TEX_FILTER modes constexpr WIC_FLAGS operator|(WIC_FLAGS a, TEX_FILTER_FLAGS b) { return static_cast(static_cast(a) | static_cast(b & TEX_FILTER_MODE_MASK)); } diff --git a/Kits/DirectXTex/DirectXTexP.h b/Kits/DirectXTex/DirectXTexP.h index 7a55e0d..38fdc44 100644 --- a/Kits/DirectXTex/DirectXTexP.h +++ b/Kits/DirectXTex/DirectXTexP.h @@ -12,7 +12,7 @@ #pragma once // Off by default warnings -#pragma warning(disable : 4619 4616 4061 4265 4365 4571 4623 4625 4626 4628 4668 4710 4711 4746 4774 4820 4987 5026 5027 5031 5032 5039 5045 5219 5246 26812) +#pragma warning(disable : 4619 4616 4061 4265 4365 4571 4623 4625 4626 4628 4668 4710 4711 4746 4774 4820 4987 5026 5027 5031 5032 5039 5045 5219 5246 5264 26812) // C4619/4616 #pragma warning warnings // 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 @@ -36,6 +36,7 @@ // 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). // Windows 8.1 SDK related Off by default warnings @@ -153,9 +154,9 @@ #include "DirectXTex.h" +#ifdef _WIN32 #include -#ifdef _WIN32 #if defined(NTDDI_WIN10_FE) || defined(__MINGW32__) #include #else diff --git a/Kits/DirectXTex/DirectXTexUtil.cpp b/Kits/DirectXTex/DirectXTexUtil.cpp index 02a00fd..ea24728 100644 --- a/Kits/DirectXTex/DirectXTexUtil.cpp +++ b/Kits/DirectXTex/DirectXTexUtil.cpp @@ -1183,6 +1183,41 @@ DXGI_FORMAT DirectX::MakeSRGB(DXGI_FORMAT fmt) noexcept } +//------------------------------------------------------------------------------------- +// Converts to an non-SRGB equivalent type +//------------------------------------------------------------------------------------- +_Use_decl_annotations_ +DXGI_FORMAT DirectX::MakeLinear(DXGI_FORMAT fmt) noexcept +{ + switch (fmt) + { + 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 fmt; + } +} + + //------------------------------------------------------------------------------------- // Converts to a format to an equivalent TYPELESS format if available //------------------------------------------------------------------------------------- diff --git a/Kits/DirectXTex/DirectXTex_GXDK_PC_2019.vcxproj b/Kits/DirectXTex/DirectXTex_GXDK_PC_2019.vcxproj index fab1fa2..e3987fc 100644 --- a/Kits/DirectXTex/DirectXTex_GXDK_PC_2019.vcxproj +++ b/Kits/DirectXTex/DirectXTex_GXDK_PC_2019.vcxproj @@ -169,6 +169,7 @@ true /Zc:twoPhase- /Zc:__cplusplus /ZH:SHA_256 %(AdditionalOptions) Level4 + true true @@ -196,6 +197,7 @@ true /Zc:twoPhase- /Zc:__cplusplus /ZH:SHA_256 %(AdditionalOptions) Level4 + true true diff --git a/Kits/DirectXTex/README.md b/Kits/DirectXTex/README.md index 76349ba..750cf31 100644 --- a/Kits/DirectXTex/README.md +++ b/Kits/DirectXTex/README.md @@ -6,11 +6,11 @@ http://go.microsoft.com/fwlink/?LinkId=248926 Copyright (c) Microsoft Corporation. -**May 9, 2022** +**October 17, 2022** This package contains DirectXTex, a shared source library for reading and writing ``.DDS`` files, and performing various texture content processing operations including resizing, format conversion, mip-map generation, block compression for Direct3D runtime texture resources, and height-map to normal-map conversion. This library makes use of the Windows Image Component (WIC) APIs. It also includes ``.TGA`` and ``.HDR`` readers and writers since these image file formats are commonly used for texture content processing pipelines, but are not currently supported by a built-in WIC codec. -This code is designed to build with Visual Studio 2019 (16.9 or later), Visual Studio 2022, or clang for Windows v11 or later. Use of the Windows 10 May 2020 Update SDK ([19041](https://walbourn.github.io/windows-10-may-2020-update-sdk/)) or later is required. +This code is designed to build with Visual Studio 2019 (16.11), Visual Studio 2022, or clang for Windows v11 or later. Use of the Windows 10 May 2020 Update SDK ([19041](https://walbourn.github.io/windows-10-may-2020-update-sdk/)) or later is required. 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). @@ -71,6 +71,8 @@ For the latest version of DirectXTex, bug reports, etc. please visit the project ## Release Notes +* Starting with the July 2022 release, the ``bool forceSRGB`` parameter for the CreateTextureEx and CreateShaderResourceViewEx functions is now a ``CREATETEX_FLAGS`` typed enum bitmask flag parameter. This may have a *breaking change* impact to client code. Replace ``true`` with ``CREATETEX_FORCE_SRGB`` and ``false`` with ``CREATETEX_DEFAULT``. + * Starting with the June 2020 release, this library makes use of typed enum bitmask flags per the recommendation of the _C++ Standard_ section *17.5.2.1.3 Bitmask types*. This is consistent with Direct3D 12's use of the ``DEFINE_ENUM_FLAG_OPERATORS`` macro. 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: ``CP_FLAGS_NONE``, ``DDS_FLAGS_NONE``, ``WIC_FLAGS_NONE``, ``TEX_FR_ROTATE0``, ``TEX_FILTER_DEFAULT``, ``TEX_FILTER_DEFAULT``, ``TEX_FILTER_DEFAULT``, ``CNMAP_DEFAULT``, or ``CNMAP_DEFAULT``. diff --git a/Kits/DirectXTex/Shaders/CompileShaders.cmd b/Kits/DirectXTex/Shaders/CompileShaders.cmd index 6d953bb..13418e0 100644 --- a/Kits/DirectXTex/Shaders/CompileShaders.cmd +++ b/Kits/DirectXTex/Shaders/CompileShaders.cmd @@ -44,7 +44,7 @@ endlocal exit /b 0 :CompileShader -set fxc=%PCFXC% %1.hlsl %FXCOPTS% /Tcs_4_0 /E%2 "/Fh%CompileShadersOutput%\%1_%2.inc" "/Fd%CompileShadersOutput%\%1_%2.pdb" /Vn%1_%2 +set fxc=%PCFXC% "%1.hlsl" %FXCOPTS% /Tcs_4_0 /E%2 "/Fh%CompileShadersOutput%\%1_%2.inc" "/Fd%CompileShadersOutput%\%1_%2.pdb" /Vn%1_%2 echo. echo %fxc% %fxc% || set error=1 diff --git a/Kits/DirectXTex/d3dx12.h b/Kits/DirectXTex/d3dx12.h index 42f26a6..e873d96 100644 --- a/Kits/DirectXTex/d3dx12.h +++ b/Kits/DirectXTex/d3dx12.h @@ -76,7 +76,12 @@ struct CD3DX12_VIEWPORT : public D3D12_VIEWPORT 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) @@ -2040,7 +2045,12 @@ inline UINT64 GetRequiredIntermediateSize( _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; @@ -2066,8 +2076,14 @@ inline UINT64 UpdateSubresources( _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) || @@ -2125,8 +2141,14 @@ inline UINT64 UpdateSubresources( _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) || @@ -2194,7 +2216,12 @@ inline UINT64 UpdateSubresources( 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); @@ -2232,7 +2259,12 @@ inline UINT64 UpdateSubresources( 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); @@ -2260,7 +2292,12 @@ inline UINT64 UpdateSubresources( 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); @@ -2287,7 +2324,12 @@ inline UINT64 UpdateSubresources( 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); @@ -4071,20 +4113,20 @@ public: 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 : + 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 + firstMipLevel, + numMips, + firstArraySlice, + numArraySlices, + firstPlane, + numPlanes } {} }; diff --git a/Kits/LiveTK/LiveResources.cpp b/Kits/LiveTK/LiveResources.cpp index 541a5a0..34fbf29 100644 --- a/Kits/LiveTK/LiveResources.cpp +++ b/Kits/LiveTK/LiveResources.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #ifdef __clang__ @@ -41,9 +42,7 @@ namespace _Use_decl_annotations_ LiveResources::LiveResources(XTaskQueueHandle queue, bool autoManageUser, bool isGuestUserAllowed) noexcept(false): -#ifdef _GAMING_XBOX - m_networkConnectivityChangedHandle{}, -#endif + m_networkConnectivityChangedToken{}, m_isNetworkAvailable(false), m_autoManageUser(autoManageUser), m_isGuestUserAllowed(isGuestUserAllowed), @@ -74,59 +73,57 @@ LiveResources::LiveResources(XTaskQueueHandle queue, bool autoManageUser, bool i char scidBuffer[64] = {}; sprintf_s(scidBuffer, "00000000-0000-0000-0000-0000%08x", m_titleId); - + XblInitArgs xblInit = { m_asyncQueue, scidBuffer }; hr = XblInitialize(&xblInit); DX::ThrowIfFailed(hr); m_scid = scidBuffer; -#ifdef _GAMING_XBOX - if (XGameRuntimeIsFeatureAvailable(XGameRuntimeFeature::XNetworking)) - { - // Listen for network connectivity changes - NotifyNetworkConnectivityHintChange( - [](void* context, NL_NETWORK_CONNECTIVITY_HINT connectivityHint) - { - auto liveResources = static_cast(context); - - liveResources->m_isNetworkAvailable = - connectivityHint.ConnectivityLevel != NL_NETWORK_CONNECTIVITY_LEVEL_HINT::NetworkConnectivityLevelHintUnknown; - }, // Callback function - this, // Context object - true, // Notify immediately with the current status - &m_networkConnectivityChangedHandle // Notification handle - ); - } - else -#endif - { - // Assume network stack is ready on desktop - m_isNetworkAvailable = true; - } + // Listen for network connectivity changes + hr = XNetworkingRegisterConnectivityHintChanged( + m_asyncQueue, + this, + [](void* context, const XNetworkingConnectivityHint* connectivityHint) + { + auto liveResources = static_cast(context); + + liveResources->m_isNetworkAvailable = + connectivityHint->connectivityLevel != XNetworkingConnectivityLevelHint::Unknown; + }, + &m_networkConnectivityChangedToken); + DX::ThrowIfFailed(hr); + + XNetworkingConnectivityHint hint{}; + XNetworkingGetConnectivityHint(&hint); + m_isNetworkAvailable = hint.connectivityLevel != XNetworkingConnectivityLevelHint::Unknown; } LiveResources::~LiveResources() { -#ifdef _GAMING_XBOX - if (m_networkConnectivityChangedHandle) + if (m_xboxLiveContext) { - CancelMibChangeNotify2(m_networkConnectivityChangedHandle); + XblContextCloseHandle(m_xboxLiveContext); + } + if (m_xboxLiveUser) + { + XUserCloseHandle(m_xboxLiveUser); } -#endif - if (m_asyncQueue) - { - XTaskQueueCloseHandle(m_asyncQueue); - m_asyncQueue = nullptr; - } + XUserUnregisterForChangeEvent(m_userChangedEventToken, false); + XNetworkingUnregisterConnectivityHintChanged(m_networkConnectivityChangedToken, false); + + auto async = new XAsyncBlock{}; + XblCleanupAsync(async); + + XTaskQueueCloseHandle(m_asyncQueue); } void LiveResources::Initialize() { - XUserRegisterForChangeEvent(m_asyncQueue, this, [](void *context, const XUserLocalId userLocalId, XUserChangeEvent event) + XUserRegisterForChangeEvent(m_asyncQueue, this, [](void *context, const XUserLocalId userLocalId, XUserChangeEvent event) { - auto pThis = reinterpret_cast(context); + auto pThis = static_cast(context); switch (event) { @@ -281,7 +278,7 @@ void LiveResources::SignInSilently() pThis->HandleError(result); } } - + delete async; }; diff --git a/Kits/LiveTK/LiveResources.h b/Kits/LiveTK/LiveResources.h index 4323ff6..6882873 100644 --- a/Kits/LiveTK/LiveResources.h +++ b/Kits/LiveTK/LiveResources.h @@ -74,9 +74,7 @@ namespace ATG void HandleError(HRESULT error); void InitializeXboxServices(); -#ifdef _GAMING_XBOX - HANDLE m_networkConnectivityChangedHandle; -#endif + XTaskQueueRegistrationToken m_networkConnectivityChangedToken; bool m_isNetworkAvailable; bool m_autoManageUser; diff --git a/README.md b/README.md index c9cdd6b..5b71264 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ This repo contains game development samples written by the Microsoft Xbox Advanc * [Kits](/Kits) contains support code used by the samples * [Media](/Media) contains media files used by the samples -* [Samples](#Samples-by-category) contains samples - * [Audio](#Audio) - * [IntroGraphics](#Intro-Graphics) - * [Graphics](#Graphics) - * [Live](#Live) - * [System](#System) - * [Tools](#Tools) - * [xCloud](#xCloud) +* [Samples](../../../wiki/SampleList) contains samples + * [Audio](../../../wiki/SampleList#Audio) + * [IntroGraphics](../../../wiki/SampleList#Intro-Graphics) + * [Graphics](../../../wiki/SampleList#Graphics) + * [Live](../../../wiki/SampleList#Live) + * [System](../../../wiki/SampleList#System) + * [Tools](../../../wiki/SampleList#Tools) + * [xCloud](../../../wiki/SampleList#xCloud) # Requirements @@ -36,130 +36,6 @@ This project may contain trademarks or logos for projects, products, or services For more ATG samples, see [DirectML Samples](https://github.com/microsoft/DirectML), [PlayFab-Samples](https://github.com/PlayFab/PlayFab-Samples), [Xbox-ATG-Samples](https://github.com/microsoft/Xbox-ATG-Samples), and [Xbox-LIVE-Samples](https://github.com/microsoft/xbox-live-samples). -## Samples by category +## Samples list by category - -### Audio - -| Path| Xbox| PC| Tool| -|----------------------------------------| ------| ------| ------| -| AdvancedSpatialSounds| [Xbox](Samples/Audio/AdvancedSpatialSounds)| | | -| InGameChat| [Xbox](Samples/Audio/InGameChat)| [PC](Samples/Audio/InGameChat)| | -| SimplePlay3DSound| [Xbox](Samples/Audio/SimplePlay3DSound)| | | -| SimplePlaySound| [Xbox](Samples/Audio/SimplePlaySound)| | | -| SimplePlaySoundStream| [Xbox](Samples/Audio/SimplePlaySoundStream)| | | -| SimpleSpatialPlaySound| [Xbox](Samples/Audio/SimpleSpatialPlaySound)| | | -| SimpleWASAPICapture| [Xbox](Samples/Audio/SimpleWASAPICapture)| | | -| SimpleWASAPIPlaySound| [Xbox](Samples/Audio/SimpleWASAPIPlaySound)| | | - -### Graphics - -| Path| Xbox| PC| Tool| -|----------------------------------------| ------| ------| ------| -| AdvancedESRAM| [Xbox](Samples/Graphics/AdvancedESRAM)| | | -| Antialiasing| [Xbox](Samples/Graphics/Antialiasing)| | | -| ComputeParticles| [Xbox](Samples/Graphics/ComputeParticles)| | | -| DeferredParticles| [Xbox](Samples/Graphics/DeferredParticles)| | | -| DXRTriangle| [Xbox](Samples/Graphics/DXRTriangle)| [PC](Samples/Graphics/DXRTriangle)| | -| DynamicLOD| [Xbox](Samples/Graphics/DynamicLOD)| [PC](Samples/Graphics/DynamicLOD)| | -| ExecuteIndirect| [Xbox](Samples/Graphics/ExecuteIndirect)| | | -| FastBlockCompress| [Xbox](Samples/Graphics/FastBlockCompress)| | | -| GeometricExpansion| [Xbox](Samples/Graphics/GeometricExpansion)| [PC](Samples/Graphics/GeometricExpansion)| | -| HDR10| [Xbox](Samples/Graphics/HDR10)| | | -| HistogramCS| [Xbox](Samples/Graphics/HistogramCS)| | | -| HlslCompile| [Xbox](Samples/Graphics/HlslCompile)| | [Tool](Samples/Graphics/HlslCompile)| -| MeshletCull| [Xbox](Samples/Graphics/MeshletCull)| [PC](Samples/Graphics/MeshletCull)| | -| MeshletInstancing| [Xbox](Samples/Graphics/MeshletInstancing)| [PC](Samples/Graphics/MeshletInstancing)| | -| PointSprites| [Xbox](Samples/Graphics/PointSprites)| | | -| SimpleHDR| [Xbox](Samples/Graphics/SimpleHDR)| | | -| SimplePBR| [Xbox](Samples/Graphics/SimplePBR)| [PC](Samples/Graphics/SimplePBR)| | -| SimpleMeshlet| [Xbox](Samples/Graphics/SimpleMeshlet)| [PC](Samples/Graphics/SimpleMeshlet)| | -| SmokeSimulation| [Xbox](Samples/Graphics/SmokeSimulation)| | | -| VisibilityBuffer|[Xbox](Samples/Graphics/VisibilityBuffer/VisibilityBuffer)|[PC](Samples/Graphics/VisibilityBuffer/VisibilityBuffer)| | - -### IntroGraphics - -| Path| Xbox| PC| Tool| -|----------------------------------------| ------| ------| ------| -| SimpleBezier| [Xbox](Samples/IntroGraphics/SimpleBezier)| | | -| SimpleCompute| [Xbox](Samples/IntroGraphics/SimpleCompute)| | | -| SimpleDeviceAndSwapChain| [Xbox](Samples/IntroGraphics/SimpleDeviceAndSwapChain)| | | -| SimpleDynamicResources| [Xbox](Samples/IntroGraphics/SimpleDynamicResources)| [PC](Samples/IntroGraphics/SimpleDynamicResources)| | -| SimpleInstancing| [Xbox](Samples/IntroGraphics/SimpleInstancing)| | | -| SimpleLighting| [Xbox](Samples/IntroGraphics/SimpleLighting)| | | -| SimpleMeshShader| [Xbox](Samples/IntroGraphics/SimpleMeshShader)| [PC](Samples/IntroGraphics/SimpleMeshShader)| | -| SimpleMSAA| [Xbox](Samples/IntroGraphics/SimpleMSAA)| | | -| SimpleSamplerFeedback| [Xbox](Samples/IntroGraphics/SimpleSamplerFeedback)| | | -| SimpleTexture| [Xbox](Samples/IntroGraphics/SimpleTexture)| | | -| SimpleTriangle| [Xbox](Samples/IntroGraphics/SimpleTriangle)| [PC](Samples/IntroGraphics/SimpleTriangleDesktop)| | - -### Live - -| Path| Xbox| PC| Tool| -|----------------------------------------| ------| ------| ------| -| Achievements| [Xbox](Samples/Live/Achievements)| [PC](Samples/Live/Achievements)| | -| DownloadableContent| [Xbox](Samples/Live/DownloadableContent)| [PC](Samples/Live/DownloadableContent)| | -| Fundamentals| | [PC](Samples/Live/Fundamentals_Desktop)| | -| InGameStore| [Xbox](Samples/Live/InGameStore)| [PC](Samples/Live/InGameStore)| | -| LeaderboardsEventManaged| [Xbox](Samples/Live/LeaderboardsEventManaged)| [PC](Samples/Live/LeaderboardsEventManaged)| | -| LeaderboardsTitleManaged| [Xbox](Samples/Live/LeaderboardsTitleManaged)| [PC](Samples/Live/LeaderboardsTitleManaged_Desktop)| | -| mDNS| [Xbox](Samples/Live/mDNS)| [PC](Samples/Live/mDNS_Desktop)| | -| MicrosoftStoreServicesClient| [Xbox](Samples/Live/MicrosoftStoreServicesClient)| [PC](Samples/Live/MicrosoftStoreServicesClient)| | -| SimpleCrossGenMPSD| [Xbox](Samples/Live/SimpleCrossGenMPSD)| [PC](Samples/Live/SimpleCrossGenMPSD)| | -| SimpleHttp| [Xbox](Samples/Live/SimpleHttp)| [PC](Samples/Live/SimpleHttp)| | -| SimpleWebSockets| [Xbox](Samples/Live/SimpleWebSockets)| [PC](Samples/Live/SimpleWebSockets)| | -| SocialManager| [Xbox](Samples/Live/SocialManager)| [PC](Samples/Live/SocialManager)| | -| TitleStorage| [Xbox](Samples/Live/TitleStorage)| | | - -### System - -| Path| Xbox| PC| Tool| -|----------------------------------------| ------| ------| ------| -| AdvancedExceptionHandling| [Xbox](Samples/System/AdvancedExceptionHandling)| [PC](Samples/System/AdvancedExceptionHandling)| | -| OutOfProcDumpTool| [Xbox](Samples/System/AdvancedExceptionHandling)| | | -| AsynchronousProgramming| [Xbox](Samples/System/AsynchronousProgramming)| [PC](Samples/System/AsynchronousProgramming)| | -| Collision| [Xbox](Samples/System/Collision)| [PC](Samples/System/Collision)| | -| CustomEventProvider| [Xbox](Samples/System/CustomEventProvider)| | | -| DataBreakPoints| [Xbox](Samples/System/DataBreakPoints)| [PC](Samples/System/DataBreakPoints)| | -| FrontPanelDemo| [Xbox](Samples/System/FrontPanelDemo)| | | -| FrontPanelDolphin| [Xbox](Samples/System/FrontPanelDolphin)| | | -| FrontPanelGame| [Xbox](Samples/System/FrontPanelGame)| | | -| FrontPanelLogo| [Xbox](Samples/System/FrontPanelLogo)| | | -| FrontPanelText| [Xbox](Samples/System/FrontPanelText)| | | -| GameInputInterfacing| [Xbox](Samples/System/GameInputInterfacing)| | | -| GameInputSequential| [Xbox](Samples/System/GameInputSequential)| | | -| Gamepad| [Xbox](Samples/System/Gamepad)| [PC](Samples/System/Gamepad | | -| GamepadVibration| [Xbox](Samples/System/GamepadVibration)| [PC](Samples/System/GamepadVibration)| | -| GameSave| [Xbox](Samples/System/GameSave)| [PC](Samples/System/GameSave_Desktop)| | -| IntelligentDelivery| [Xbox](Samples/System/IntelligentDelivery)| [PC](Samples/System/IntelligentDelivery)| | -| LocalStorage| [Xbox](Samples/System/LocalStorage)| [PC](Samples/System/LocalStorage)| | -| MouseInput| [Xbox](Samples/System/MouseInput)| | | -| NLSAndLocalization| [Xbox](Samples/System/NLSAndLocalization)| [PC](Samples/System/NLSAndLocalization)| | -| SimpleDirectStorage| [Xbox](Samples/System/SimpleDirectStorage)| | | -| SimpleDirectStorageCombo| [Xbox](Samples/System/SimpleDirectStorageCombo)| [PC](Samples/System/SimpleDirectStorageCombo)| | -| SimpleExceptionHandling| [Xbox](Samples/System/SimpleExceptionHandling)| [PC](Samples/System/SimpleExceptionHandling)| | -| SimpleFFBWheel| [Xbox](Samples/System/SimpleFFBWheel)| | | -| SimpleFrontPanel| [Xbox](Samples/System/SimpleFrontPanel)| | | -| SimplePLM| [Xbox](Samples/System/SimplePLM)| | | -| SimpleUserModel| [Xbox](Samples/System/SimpleUserModel)| | | -| SystemInfo| [Xbox](Samples/System/SystemInfo)| [PC](Samples/System/SystemInfo)| | -| UserManagement| [Xbox](Samples/System/UserManagement)| | | - -### Tools - -| Path| Xbox| PC| Tool| -|----------------------------------------| ------| ------| ------| -| BWOIExample| [Xbox](Samples/Tools/BWOIExample)| [PC](Samples/Tools/BWOIExample)| | -| CacheTestCombo| [Xbox](Samples/Tools/CacheTestCombo)| [PC](Samples/Tools/CacheTestCombo)| | -| DumpTool| [Xbox](Samples/Tools/DumpTool)| | | -| ConverterApp| | | [Tool](Samples/Tools/MeshletConverter)| -| Runtime| | | [Tool](Samples/Tools/MeshletConverter)| -| OSPrimitiveTestCombo| [Xbox](Samples/Tools/OSPrimitiveTestCombo)| [PC](Samples/Tools/OSPrimitiveTestCombo)| | -| xbgamepad| | | [Tool](Samples/Tools/xbgamepad)| -| xtexconv| | | [Tool](Samples/Tools/xtexconv)| - -### xCloud - -| Path| Xbox| PC| Tool| -|----------------------------------------| ------| ------| ------| -| SimpleCloudAwareSample| [Xbox](Samples/xCloud/SimpleCloudAwareSample)| | | +[Samples List Wiki](../../../wiki/SampleList) diff --git a/Samples/Audio/InGameChat/InGameChat.vcxproj b/Samples/Audio/InGameChat/InGameChat.vcxproj index c134ee8..7c2b63f 100644 --- a/Samples/Audio/InGameChat/InGameChat.vcxproj +++ b/Samples/Audio/InGameChat/InGameChat.vcxproj @@ -508,7 +508,7 @@ - + diff --git a/Samples/Audio/InGameChat/InGameChat.vcxproj.filters b/Samples/Audio/InGameChat/InGameChat.vcxproj.filters index 0be37fe..47bcd50 100644 --- a/Samples/Audio/InGameChat/InGameChat.vcxproj.filters +++ b/Samples/Audio/InGameChat/InGameChat.vcxproj.filters @@ -1,4 +1,4 @@ - + @@ -118,7 +118,7 @@ - + diff --git a/Samples/Audio/InGameChat/Main.cpp b/Samples/Audio/InGameChat/Main.cpp index 9e09b43..6057d38 100644 --- a/Samples/Audio/InGameChat/Main.cpp +++ b/Samples/Audio/InGameChat/Main.cpp @@ -384,7 +384,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) } else { - SetWindowLongPtr(hWnd, GWL_STYLE, 0); + SetWindowLongPtr(hWnd, GWL_STYLE, WS_POPUP); SetWindowLongPtr(hWnd, GWL_EXSTYLE, WS_EX_TOPMOST); SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); diff --git a/Samples/Audio/InGameChat/MicrosoftGame.Config b/Samples/Audio/InGameChat/MicrosoftGameConfig.mgc similarity index 93% rename from Samples/Audio/InGameChat/MicrosoftGame.Config rename to Samples/Audio/InGameChat/MicrosoftGameConfig.mgc index 67f69bf..b154b3f 100644 --- a/Samples/Audio/InGameChat/MicrosoftGame.Config +++ b/Samples/Audio/InGameChat/MicrosoftGameConfig.mgc @@ -1,5 +1,5 @@ - + @@ -10,6 +10,7 @@ 6B2BEA70 + 000000004C26F118 cXxNU;h%fotM~o&rBc;N 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

(5ajPd}`b+QN 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 LGs64tr@;OXf$8S< diff --git a/Samples/Audio/InGameChat/pch.h b/Samples/Audio/InGameChat/pch.h index 01b4322..da1bf5a 100644 --- a/Samples/Audio/InGameChat/pch.h +++ b/Samples/Audio/InGameChat/pch.h @@ -42,8 +42,8 @@ #include -#if _GRDK_VER < 0x4A610D2B /* GXDK Edition 200600 */ -#error This sample requires the June 2020 GDK or later +#if _GRDK_VER < 0x55F00C58 /* GDK Edition 220300 */ +#error This sample requires the March 2022 GDK or later #endif #ifdef _GAMING_XBOX_SCARLETT diff --git a/Samples/Graphics/AdvancedESRAM/AdvancedESRAM.cpp b/Samples/Graphics/AdvancedESRAM/AdvancedESRAM.cpp index f0494ac..40ebd42 100644 --- a/Samples/Graphics/AdvancedESRAM/AdvancedESRAM.cpp +++ b/Samples/Graphics/AdvancedESRAM/AdvancedESRAM.cpp @@ -8,7 +8,7 @@ #include "pch.h" #include "AdvancedESRAM.h" -extern void ExitSample(); +extern void ExitSample() noexcept; using namespace DirectX; using namespace ATG; @@ -107,36 +107,37 @@ namespace const std::vector s_triIndex = { 0, 1, 2 }; // Sample Constants - const DXGI_FORMAT c_colorFormat = DXGI_FORMAT_R11G11B10_FLOAT; - const DXGI_FORMAT c_depthFormat = DXGI_FORMAT_D32_FLOAT; + constexpr DXGI_FORMAT c_colorFormat = DXGI_FORMAT_R11G11B10_FLOAT; + constexpr DXGI_FORMAT c_depthFormat = DXGI_FORMAT_D32_FLOAT; - const float c_defaultPhi = XM_2PI / 6.0f; - const float c_defaultRadius = 3.3f; + constexpr float c_defaultPhi = XM_2PI / 6.0f; + constexpr float c_defaultRadius = 3.3f; //----------------------------------- // Helper Functions template - void IncrMod(T& value, T mod) { + void IncrMod(T& value, T mod) noexcept + { T res = (value + 1) % mod; value = res < 0 ? mod - 1 : res; } template - void DecrMod(T& value, T mod) { + void DecrMod(T& value, T mod) noexcept + { int res = int(value) - 1; value = res < 0 ? mod - 1 : res; } - void Saturate(float& value) { value = std::max(0.0f, std::min(1.0f, value)); } + void Saturate(float& value) noexcept { value = std::max(0.0f, std::min(1.0f, value)); } } #pragma endregion #pragma region Construction -Sample::Sample() - : m_deviceResources(new DX::DeviceResources(DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN)) - , m_displayWidth(0) +Sample::Sample() noexcept(false) + : m_displayWidth(0) , m_displayHeight(0) , m_frame(0) , m_theta(0.0f) @@ -148,7 +149,9 @@ Sample::Sample() , m_updateStats(true) , m_visData{} { - std::fill_n(m_esramRatios, _countof(m_esramRatios), 1.0f); + m_deviceResources = std::make_unique(DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN); + + std::fill_n(m_esramRatios, std::size(m_esramRatios), 1.0f); } Sample::~Sample() @@ -188,6 +191,8 @@ void Sample::Tick() { PIXBeginEvent(PIX_COLOR_DEFAULT, L"Frame %llu", m_frame); + m_deviceResources->WaitForOrigin(); + m_timer.Tick([&]() { Update(m_timer); @@ -196,10 +201,11 @@ void Sample::Tick() Render(); PIXEndEvent(); - ++m_frame; + m_frame++; } -void Sample::Update(const DX::StepTimer& timer) +// Updates the world. +void Sample::Update(DX::StepTimer const& timer) { using ButtonState = DirectX::GamePad::ButtonStateTracker::ButtonState; @@ -326,6 +332,7 @@ void Sample::Update(const DX::StepTimer& timer) #pragma region Frame Render +// Draws the scene. void Sample::Render() { // Don't try to render anything before the first Update. @@ -360,14 +367,14 @@ void Sample::Render() // Set descriptor heaps ID3D12DescriptorHeap* heaps[] = { m_srvPile->Heap(), m_commonStates->Heap() }; - commandList->SetDescriptorHeaps(UINT(_countof(heaps)), heaps); + commandList->SetDescriptorHeaps(static_cast(std::size(heaps)), heaps); { ScopedPixEvent Clear(commandList, PIX_COLOR_DEFAULT, L"Clear"); // Set the viewport and scissor rect. - auto viewport = m_deviceResources->GetScreenViewport(); - auto scissorRect = m_deviceResources->GetScissorRect(); + auto const viewport = m_deviceResources->GetScreenViewport(); + auto const scissorRect = m_deviceResources->GetScissorRect(); commandList->RSSetViewports(1, &viewport); commandList->RSSetScissorRects(1, &scissorRect); @@ -412,10 +419,11 @@ void Sample::Render() TransientResource outlineTex[2]; D3D12_GPU_DESCRIPTOR_HANDLE srvHandles[2]; - for (uint32_t i = 0; i < _countof(outlineTex); ++i) + for (size_t i = 0; i < std::size(outlineTex); ++i) { outlineTex[i] = AcquireTransientTexture(commandList, m_outlineDesc, D3D12_RESOURCE_STATE_RENDER_TARGET, SceneTexture(ST_Outline0 + i)); - srvHandles[i] = m_srvPile->WriteDescriptors(m_deviceResources->GetD3DDevice(), SRV_Outline0 + i, &outlineTex[i].SRV, 1); + srvHandles[i] = m_srvPile->WriteDescriptors(m_deviceResources->GetD3DDevice(), + static_cast(SRV_Outline0 + i), &outlineTex[i].SRV, 1); handles[ST_Outline0 + i] = outlineTex[i].handle; } @@ -454,7 +462,7 @@ void Sample::Render() obj.model->DrawOpaque(commandList, obj.effects.begin()); // Release the outline textures' memory pages back to the allocator. - for (uint32_t i = 0; i < _countof(outlineTex); ++i) + for (size_t i = 0; i < std::size(outlineTex); ++i) { m_allocator->Release(commandList, outlineTex[i], D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); } @@ -476,10 +484,11 @@ void Sample::Render() TransientResource bloomTex[2]; D3D12_GPU_DESCRIPTOR_HANDLE srvHandles[2]; - for (uint32_t i = 0; i < _countof(bloomTex); ++i) + for (size_t i = 0; i < std::size(bloomTex); ++i) { bloomTex[i] = AcquireTransientTexture(commandList, m_bloomDesc, D3D12_RESOURCE_STATE_RENDER_TARGET, SceneTexture(ST_Bloom0 + i)); - srvHandles[i] = m_srvPile->WriteDescriptors(m_deviceResources->GetD3DDevice(), SRV_Bloom0 + i, &bloomTex[i].SRV, 1); + srvHandles[i] = m_srvPile->WriteDescriptors(m_deviceResources->GetD3DDevice(), + static_cast(SRV_Bloom0 + i), &bloomTex[i].SRV, 1); handles[ST_Bloom0 + i] = bloomTex[i].handle; } @@ -540,7 +549,7 @@ void Sample::Render() commandList->CopyResource(colorTex.resource, bloomTex[1].resource); // We're finished with the bloom textures - release their memory pages back to the allocator. - for (uint32_t i = 0; i < _countof(bloomTex); ++i) + for (size_t i = 0; i < std::size(bloomTex); ++i) { m_allocator->Release(commandList, bloomTex[i], D3D12_RESOURCE_STATE_GENERIC_READ); } @@ -613,7 +622,7 @@ void Sample::DrawHUD(ID3D12GraphicsCommandList* commandList) { m_hudBatch->Begin(commandList); - auto safe = SimpleMath::Viewport::ComputeTitleSafeArea(m_displayWidth, m_displayHeight); + auto const safe = SimpleMath::Viewport::ComputeTitleSafeArea(m_displayWidth, m_displayHeight); wchar_t textBuffer[128] = {}; XMFLOAT2 textPos = XMFLOAT2(float(safe.left), float(safe.top)); @@ -748,13 +757,11 @@ void Sample::CreateDeviceDependentResources() // Create heap m_srvPile = std::make_unique(device, - D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, - D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, 128, DescriptorHeapIndex::SRV_Count); // Load models from disk. - m_models.resize(_countof(s_modelPaths)); + m_models.resize(std::size(s_modelPaths)); for (size_t i = 0; i < m_models.size(); ++i) { m_models[i] = Model::CreateFromSDKMESH(device, s_modelPaths[i]); @@ -782,8 +789,8 @@ void Sample::CreateDeviceDependentResources() } // HUD - auto backBufferRts = RenderTargetState(m_deviceResources->GetBackBufferFormat(), m_deviceResources->GetDepthBufferFormat()); - auto spritePSD = SpriteBatchPipelineStateDescription(backBufferRts, &CommonStates::AlphaBlend); + const RenderTargetState backBufferRts(m_deviceResources->GetBackBufferFormat(), m_deviceResources->GetDepthBufferFormat()); + const SpriteBatchPipelineStateDescription spritePSD(backBufferRts, &CommonStates::AlphaBlend); m_hudBatch = std::make_unique(device, resourceUpload, spritePSD); auto finished = resourceUpload.End(m_deviceResources->GetCommandQueue()); @@ -795,8 +802,8 @@ void Sample::CreateDeviceDependentResources() auto effectFactory = EffectFactory(m_srvPile->Heap(), m_commonStates->Heap()); - auto objectRTState = RenderTargetState(c_colorFormat, c_depthFormat); - auto objectPSD = EffectPipelineStateDescription( + const RenderTargetState objectRTState(c_colorFormat, c_depthFormat); + const EffectPipelineStateDescription objectPSD( nullptr, CommonStates::Opaque, CommonStates::DepthDefault, @@ -804,7 +811,7 @@ void Sample::CreateDeviceDependentResources() objectRTState, D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE); - m_scene.resize(_countof(s_sceneDefinition)); + m_scene.resize(std::size(s_sceneDefinition)); for (size_t i = 0; i < m_scene.size(); i++) { size_t index = s_sceneDefinition[i].modelIndex; @@ -825,8 +832,8 @@ void Sample::CreateDeviceDependentResources() }); } - auto outlineRtState = RenderTargetState(c_colorFormat, DXGI_FORMAT_UNKNOWN); - auto outlinePSD = EffectPipelineStateDescription( + const RenderTargetState outlineRtState(c_colorFormat, DXGI_FORMAT_UNKNOWN); + const EffectPipelineStateDescription outlinePSD( &VertexPositionNormalTexture::InputLayout, CommonStates::Opaque, CommonStates::DepthNone, @@ -842,11 +849,11 @@ void Sample::CreateDeviceDependentResources() m_fullScreenTri = GeometricPrimitive::CreateCustom(s_triVertex, s_triIndex); - auto postRtState = RenderTargetState(c_colorFormat, DXGI_FORMAT_UNKNOWN); + const RenderTargetState postRtState(c_colorFormat, DXGI_FORMAT_UNKNOWN); m_blurEffect = std::make_unique(device, postRtState, BasicPostProcess::GaussianBlur_5x5); m_blurEffect->SetGaussianParameter(12.0f); - auto combinePSD = EffectPipelineStateDescription( + const EffectPipelineStateDescription combinePSD( &VertexPositionNormalTexture::InputLayout, CommonStates::NonPremultiplied, CommonStates::DepthNone, @@ -859,7 +866,7 @@ void Sample::CreateDeviceDependentResources() m_alphaCompositeEffect->SetDiffuseColor(XMVectorSet(1.0f, 1.0f, 1.0f, 1.0f)); m_alphaCompositeEffect->SetAlpha(0.6f); - auto backBufferRtState = RenderTargetState(DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN); + const RenderTargetState backBufferRtState(DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN); m_bloomExtractEffect = std::make_unique(device, postRtState, BasicPostProcess::BloomExtract); m_bloomExtractEffect->SetBloomExtractParameter(0.9f); @@ -988,7 +995,7 @@ void Sample::UpdateVisualizerRanges(const ResourceHandle(&resources)[ST_Count]) // Calculate ESRAM page ranges for each texture std::vector ranges; - for (size_t i = 0; i < _countof(resources); ++i) + for (size_t i = 0; i < std::size(resources); ++i) { #ifdef _GAMING_XBOX_XBOXONE m_allocator->GetEsramRanges(resources[i], ranges); diff --git a/Samples/Graphics/AdvancedESRAM/AdvancedESRAM.h b/Samples/Graphics/AdvancedESRAM/AdvancedESRAM.h index de59a45..8b8c53b 100644 --- a/Samples/Graphics/AdvancedESRAM/AdvancedESRAM.h +++ b/Samples/Graphics/AdvancedESRAM/AdvancedESRAM.h @@ -46,25 +46,33 @@ enum SceneTexture : uint32_t class Sample { public: - Sample(); + Sample() noexcept(false); ~Sample(); + Sample(Sample&&) = default; + Sample& operator= (Sample&&) = default; + + Sample(Sample const&) = delete; + Sample& operator= (Sample const&) = delete; + // Initialization and management void Initialize(HWND window); void Uninitialize(); - // Basic Sample loop + // Basic render loop void Tick(); // Messages void OnSuspending(); void OnResuming(); + void OnConstrained() {} + void OnUnConstrained() {} // Properties - bool RequestHDRMode() const { return m_deviceResources ? (m_deviceResources->GetDeviceOptions() & DX::DeviceResources::c_EnableHDR) != 0 : false; } + bool RequestHDRMode() const noexcept { return m_deviceResources ? (m_deviceResources->GetDeviceOptions() & DX::DeviceResources::c_EnableHDR) != 0 : false; } private: - void Update(const DX::StepTimer& timer); + void Update(DX::StepTimer const& timer); void Render(); void DrawHUD(ID3D12GraphicsCommandList* commandList); diff --git a/Samples/Graphics/AdvancedESRAM/DeviceResources.cpp b/Samples/Graphics/AdvancedESRAM/DeviceResources.cpp index d76fd63..fa67816 100644 --- a/Samples/Graphics/AdvancedESRAM/DeviceResources.cpp +++ b/Samples/Graphics/AdvancedESRAM/DeviceResources.cpp @@ -31,14 +31,13 @@ DeviceResources::DeviceResources( m_rtvDescriptorSize(0), m_screenViewport{}, m_scissorRect{}, - m_backBufferFormat((flags & c_EnableHDR) ? DXGI_FORMAT_R10G10B10A2_UNORM : backBufferFormat), + m_backBufferFormat(backBufferFormat), m_depthBufferFormat(depthBufferFormat), m_backBufferCount(backBufferCount), m_window(nullptr), m_d3dFeatureLevel(D3D_FEATURE_LEVEL_12_0), m_outputSize{0, 0, 1920, 1080}, - m_options(flags), - m_gameDVRFormat((flags & c_EnableHDR) ? backBufferFormat : DXGI_FORMAT_UNKNOWN) + m_options(flags) { if (backBufferCount < 2 || backBufferCount > MAX_BACK_BUFFER_COUNT) { @@ -55,12 +54,16 @@ DeviceResources::~DeviceResources() // Ensure we present a blank screen before cleaning up resources. if (m_commandQueue) { - (void)m_commandQueue->PresentX(0, nullptr, nullptr); + std::ignore = m_commandQueue->PresentX(0, nullptr, nullptr); } } // Configures the Direct3D device, and stores handles to it and the device context. +#ifdef _GAMING_XBOX_SCARLETT +void DeviceResources::CreateDeviceResources(D3D12XBOX_CREATE_DEVICE_FLAGS createDeviceFlags) +#else void DeviceResources::CreateDeviceResources() +#endif { // Create the DX12 API device object. D3D12XBOX_CREATE_DEVICE_PARAMETERS params = {}; @@ -77,6 +80,17 @@ void DeviceResources::CreateDeviceResources() params.GraphicsCommandQueueRingSizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); params.GraphicsScratchMemorySizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); params.ComputeScratchMemorySizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); +#ifdef _GAMING_XBOX_SCARLETT + params.CreateDeviceFlags = createDeviceFlags; + +#if (_GXDK_VER >= 0x585D070E /* GXDK Edition 221000 */) + if (m_options & c_AmplificationShaders) + { + params.AmplificationShaderIndirectArgsBufferSize = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); + params.AmplificationShaderPayloadBufferSize = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); + } +#endif +#endif HRESULT hr = D3D12XboxCreateDevice( nullptr, @@ -107,7 +121,7 @@ void DeviceResources::CreateDeviceResources() // Create descriptor heaps for render target views and depth stencil views. D3D12_DESCRIPTOR_HEAP_DESC rtvDescriptorHeapDesc = {}; - rtvDescriptorHeapDesc.NumDescriptors = (m_options & c_EnableHDR) ? (m_backBufferCount * 2) : m_backBufferCount; + rtvDescriptorHeapDesc.NumDescriptors = m_backBufferCount; rtvDescriptorHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; ThrowIfFailed(m_d3dDevice->CreateDescriptorHeap(&rtvDescriptorHeapDesc, IID_GRAPHICS_PPV_ARGS(m_rtvDescriptorHeap.ReleaseAndGetAddressOf()))); @@ -152,31 +166,46 @@ void DeviceResources::CreateDeviceResources() m_fenceEvent.Attach(CreateEventEx(nullptr, nullptr, 0, EVENT_MODIFY_STATE | SYNCHRONIZE)); if (!m_fenceEvent.IsValid()) { - throw std::exception("CreateEvent"); + throw std::system_error(std::error_code(static_cast(GetLastError()), std::system_category()), "CreateEventEx"); } - if (m_options & c_Enable4K_UHD) + if (m_options & (c_Enable4K_UHD | c_EnableQHD)) { switch (XSystemGetDeviceType()) { case XSystemDeviceType::XboxOne: case XSystemDeviceType::XboxOneS: - case XSystemDeviceType::XboxScarlettLockhart /* Xbox Series S */: - m_options &= ~c_Enable4K_UHD; -#ifdef _DEBUG - OutputDebugStringA("INFO: Swapchain using 1080p (1920 x 1080)\n"); -#endif + m_options &= ~(c_Enable4K_UHD | c_EnableQHD); break; + case XSystemDeviceType::XboxScarlettLockhart /* Xbox Series S */: + m_options &= ~c_Enable4K_UHD; + if (m_options & c_EnableQHD) + { + m_outputSize = { 0, 0, 2560, 1440 }; + } + break; + + case XSystemDeviceType::XboxScarlettAnaconda /* Xbox Series X */: + case XSystemDeviceType::XboxOneXDevkit: + case XSystemDeviceType::XboxScarlettDevkit: default: - m_outputSize = { 0, 0, 3840, 2160 }; -#ifdef _DEBUG - OutputDebugStringA("INFO: Swapchain using 4k (3840 x 2160)\n"); -#endif + m_outputSize = (m_options & c_Enable4K_UHD) ? RECT{ 0, 0, 3840, 2160 } : RECT{ 0, 0, 2560, 1440 }; break; } } +#ifdef _DEBUG + const char* info = nullptr; + switch (m_outputSize.bottom) + { + case 2160: info = "INFO: Swapchain using 4k (3840 x 2160)\n"; break; + case 1440: info = "INFO: Swapchain using 1440p (2560 x 1440)\n"; break; + default: info = "INFO: Swapchain using 1080p (1920 x 1080)\n"; break; + } + OutputDebugStringA(info); +#endif + RegisterFrameEvents(); } @@ -185,7 +214,7 @@ void DeviceResources::CreateWindowSizeDependentResources() { if (!m_window) { - throw std::exception("Call SetWindow with a valid window handle"); + throw std::logic_error("Call SetWindow with a valid Win32 window handle"); } // Wait until all previous GPU work is complete. @@ -198,7 +227,6 @@ void DeviceResources::CreateWindowSizeDependentResources() for (UINT n = 0; n < m_backBufferCount; n++) { m_renderTargets[n].Reset(); - m_renderTargetsGameDVR[n].Reset(); m_fenceValues[n] = m_fenceValues[m_backBufferIndex]; } @@ -208,7 +236,7 @@ void DeviceResources::CreateWindowSizeDependentResources() // Obtain the back buffers for this window which will be the final render targets // and create render target views for each of them. - CD3DX12_HEAP_PROPERTIES swapChainHeapProperties(D3D12_HEAP_TYPE_DEFAULT); + const CD3DX12_HEAP_PROPERTIES swapChainHeapProperties(D3D12_HEAP_TYPE_DEFAULT); D3D12_RESOURCE_DESC swapChainBufferDesc = CD3DX12_RESOURCE_DESC::Tex2D( m_backBufferFormat, @@ -219,6 +247,13 @@ void DeviceResources::CreateWindowSizeDependentResources() ); swapChainBufferDesc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; +#ifdef _GAMING_XBOX_XBOXONE + if (m_options & c_EnableHDR) + { + swapChainBufferDesc.Flags |= D3D12XBOX_RESOURCE_FLAG_ALLOW_AUTOMATIC_GAMEDVR_TONE_MAP; + } +#endif + D3D12_CLEAR_VALUE swapChainOptimizedClearValue = {}; swapChainOptimizedClearValue.Format = m_backBufferFormat; @@ -240,41 +275,12 @@ void DeviceResources::CreateWindowSizeDependentResources() rtvDesc.Format = m_backBufferFormat; rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; - CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( + const CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( m_rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), static_cast(n), m_rtvDescriptorSize); m_d3dDevice->CreateRenderTargetView(m_renderTargets[n].Get(), &rtvDesc, rtvDescriptor); } - if (m_options & c_EnableHDR) - { - swapChainBufferDesc.Format = swapChainOptimizedClearValue.Format = m_gameDVRFormat; - - for (UINT n = 0; n < m_backBufferCount; n++) - { - ThrowIfFailed(m_d3dDevice->CreateCommittedResource( - &swapChainHeapProperties, - D3D12_HEAP_FLAG_ALLOW_DISPLAY, - &swapChainBufferDesc, - D3D12_RESOURCE_STATE_PRESENT, - &swapChainOptimizedClearValue, - IID_GRAPHICS_PPV_ARGS(m_renderTargetsGameDVR[n].GetAddressOf()))); - - wchar_t name[25] = {}; - swprintf_s(name, L"GameDVR Render target %u", n); - m_renderTargetsGameDVR[n]->SetName(name); - - D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {}; - rtvDesc.Format = m_gameDVRFormat; - rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; - - CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptorGameDVR( - m_rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), - static_cast(m_backBufferCount + n), m_rtvDescriptorSize); - m_d3dDevice->CreateRenderTargetView(m_renderTargetsGameDVR[n].Get(), &rtvDesc, rtvDescriptorGameDVR); - } - } - // Reset the index to the current back buffer. m_backBufferIndex = 0; @@ -282,7 +288,7 @@ void DeviceResources::CreateWindowSizeDependentResources() { // Allocate a 2-D surface as the depth/stencil buffer and create a depth/stencil view // on this surface. - CD3DX12_HEAP_PROPERTIES depthHeapProperties(D3D12_HEAP_TYPE_DEFAULT); + const CD3DX12_HEAP_PROPERTIES depthHeapProperties(D3D12_HEAP_TYPE_DEFAULT); D3D12_RESOURCE_DESC depthStencilDesc = CD3DX12_RESOURCE_DESC::Tex2D( m_depthBufferFormat, @@ -295,7 +301,7 @@ void DeviceResources::CreateWindowSizeDependentResources() D3D12_CLEAR_VALUE depthOptimizedClearValue = {}; depthOptimizedClearValue.Format = m_depthBufferFormat; - depthOptimizedClearValue.DepthStencil.Depth = 1.0f; + depthOptimizedClearValue.DepthStencil.Depth = (m_options & c_ReverseDepth) ? 0.0f : 1.0f; depthOptimizedClearValue.DepthStencil.Stencil = 0; ThrowIfFailed(m_d3dDevice->CreateCommittedResource( @@ -331,10 +337,6 @@ void DeviceResources::CreateWindowSizeDependentResources() // Prepare the command list and render target for rendering. void DeviceResources::Prepare(D3D12_RESOURCE_STATES beforeState, D3D12_RESOURCE_STATES afterState) { - // Wait until frame start is signaled - m_framePipelineToken = D3D12XBOX_FRAME_PIPELINE_TOKEN_NULL; - ThrowIfFailed(m_d3dDevice->WaitFrameEventX(D3D12XBOX_FRAME_EVENT_ORIGIN, INFINITE, nullptr, D3D12XBOX_WAIT_FRAME_EVENT_FLAG_NONE, &m_framePipelineToken)); - // Reset command list and allocator. ThrowIfFailed(m_commandAllocators[m_backBufferIndex]->Reset()); ThrowIfFailed(m_commandList->Reset(m_commandAllocators[m_backBufferIndex].Get(), nullptr)); @@ -342,23 +344,10 @@ void DeviceResources::Prepare(D3D12_RESOURCE_STATES beforeState, D3D12_RESOURCE_ if (beforeState != afterState) { // Transition the render target into the correct state to allow for drawing into it. - if (m_options & c_EnableHDR) - { - D3D12_RESOURCE_BARRIER barriers[2] = - { - CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), - beforeState, afterState), - CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargetsGameDVR[m_backBufferIndex].Get(), - beforeState, afterState), - }; - m_commandList->ResourceBarrier(_countof(barriers), barriers); - } - else - { - D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), - beforeState, afterState); - m_commandList->ResourceBarrier(1, &barrier); - } + const D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition( + m_renderTargets[m_backBufferIndex].Get(), + beforeState, afterState); + m_commandList->ResourceBarrier(1, &barrier); } } @@ -368,20 +357,10 @@ void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState, _In_opt_ const if (beforeState != D3D12_RESOURCE_STATE_PRESENT) { // Transition the render target to the state that allows it to be presented to the display. - if (m_options & c_EnableHDR) - { - D3D12_RESOURCE_BARRIER barriers[2] = - { - CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), beforeState, D3D12_RESOURCE_STATE_PRESENT), - CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargetsGameDVR[m_backBufferIndex].Get(), beforeState, D3D12_RESOURCE_STATE_PRESENT), - }; - m_commandList->ResourceBarrier(_countof(barriers), barriers); - } - else - { - D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), beforeState, D3D12_RESOURCE_STATE_PRESENT); - m_commandList->ResourceBarrier(1, &barrier); - } + const D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition( + m_renderTargets[m_backBufferIndex].Get(), + beforeState, D3D12_RESOURCE_STATE_PRESENT); + m_commandList->ResourceBarrier(1, &barrier); } // Send the command list off to the GPU for processing. @@ -389,32 +368,24 @@ void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState, _In_opt_ const m_commandQueue->ExecuteCommandLists(1, CommandListCast(m_commandList.GetAddressOf())); // Present the backbuffer using the PresentX API. - D3D12XBOX_PRESENT_PLANE_PARAMETERS planeParameters[2] = {}; - planeParameters[0].Token = planeParameters[1].Token = m_framePipelineToken; - planeParameters[0].ResourceCount = planeParameters[1].ResourceCount = 1; - planeParameters[0].ppResources = m_renderTargets[m_backBufferIndex].GetAddressOf(); + D3D12XBOX_PRESENT_PLANE_PARAMETERS planeParameters = {}; + planeParameters.Token = m_framePipelineToken; + planeParameters.ResourceCount = 1; + planeParameters.ppResources = m_renderTargets[m_backBufferIndex].GetAddressOf(); if (m_options & c_EnableHDR) { - planeParameters[0].pSrcViewRects = planeParameters[1].pSrcViewRects = &m_outputSize; - planeParameters[0].ColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020; - planeParameters[1].ppResources = m_renderTargetsGameDVR[m_backBufferIndex].GetAddressOf(); - planeParameters[1].ColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; - - ThrowIfFailed( - m_commandQueue->PresentX(2, planeParameters, params) - ); - } - else - { - ThrowIfFailed( - m_commandQueue->PresentX(1, planeParameters, params) - ); + planeParameters.ColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020; } - // Xbox One apps do not need to handle DXGI_ERROR_DEVICE_REMOVED or DXGI_ERROR_DEVICE_RESET. + ThrowIfFailed( + m_commandQueue->PresentX(1, &planeParameters, params) + ); - MoveToNextFrame(); + // Xbox apps do not need to handle DXGI_ERROR_DEVICE_REMOVED or DXGI_ERROR_DEVICE_RESET. + + // Update the back buffer index. + m_backBufferIndex = (m_backBufferIndex + 1) % m_backBufferCount; } // Handle GPU suspend/resume @@ -436,13 +407,13 @@ void DeviceResources::WaitForGpu() noexcept if (m_commandQueue && m_fence && m_fenceEvent.IsValid()) { // Schedule a Signal command in the GPU queue. - UINT64 fenceValue = m_fenceValues[m_backBufferIndex]; + const UINT64 fenceValue = m_fenceValues[m_backBufferIndex]; if (SUCCEEDED(m_commandQueue->Signal(m_fence.Get(), fenceValue))) { // Wait until the Signal has been processed. if (SUCCEEDED(m_fence->SetEventOnCompletion(fenceValue, m_fenceEvent.Get()))) { - WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); + std::ignore = WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); // Increment the fence value for the current frame. m_fenceValues[m_backBufferIndex]++; @@ -451,25 +422,17 @@ void DeviceResources::WaitForGpu() noexcept } } -// Prepare to render the next frame. -void DeviceResources::MoveToNextFrame() +// For PresentX rendering, we should wait for the origin event just before processing input. +void DeviceResources::WaitForOrigin() { - // Schedule a Signal command in the queue. - const UINT64 currentFenceValue = m_fenceValues[m_backBufferIndex]; - ThrowIfFailed(m_commandQueue->Signal(m_fence.Get(), currentFenceValue)); - - // Update the back buffer index. - m_backBufferIndex = (m_backBufferIndex + 1) % m_backBufferCount; - - // If the next frame is not ready to be rendered yet, wait until it is ready. - if (m_fence->GetCompletedValue() < m_fenceValues[m_backBufferIndex]) - { - ThrowIfFailed(m_fence->SetEventOnCompletion(m_fenceValues[m_backBufferIndex], m_fenceEvent.Get())); - WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); - } - - // Set the fence value for the next frame. - m_fenceValues[m_backBufferIndex] = currentFenceValue + 1; + // Wait until frame start is signaled + m_framePipelineToken = D3D12XBOX_FRAME_PIPELINE_TOKEN_NULL; + ThrowIfFailed(m_d3dDevice->WaitFrameEventX( + D3D12XBOX_FRAME_EVENT_ORIGIN, + INFINITE, + nullptr, + D3D12XBOX_WAIT_FRAME_EVENT_FLAG_NONE, + &m_framePipelineToken)); } // Set frame interval and register for frame events diff --git a/Samples/Graphics/AdvancedESRAM/DeviceResources.h b/Samples/Graphics/AdvancedESRAM/DeviceResources.h index 00b0984..4924894 100644 --- a/Samples/Graphics/AdvancedESRAM/DeviceResources.h +++ b/Samples/Graphics/AdvancedESRAM/DeviceResources.h @@ -10,8 +10,11 @@ namespace DX class DeviceResources { public: - static constexpr unsigned int c_Enable4K_UHD = 0x1; - static constexpr unsigned int c_EnableHDR = 0x2; + static constexpr unsigned int c_Enable4K_UHD = 0x1; + static constexpr unsigned int c_EnableQHD = 0x2; + static constexpr unsigned int c_EnableHDR = 0x4; + static constexpr unsigned int c_ReverseDepth = 0x8; + static constexpr unsigned int c_AmplificationShaders = 0x10; DeviceResources(DXGI_FORMAT backBufferFormat = DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT depthBufferFormat = DXGI_FORMAT_D32_FLOAT, @@ -25,7 +28,11 @@ namespace DX DeviceResources(DeviceResources const&) = delete; DeviceResources& operator= (DeviceResources const&) = delete; +#ifdef _GAMING_XBOX_SCARLETT + void CreateDeviceResources(D3D12XBOX_CREATE_DEVICE_FLAGS createDeviceFlags = D3D12XBOX_CREATE_DEVICE_FLAG_NONE); +#else void CreateDeviceResources(); +#endif void CreateWindowSizeDependentResources(); void SetWindow(HWND window) noexcept { m_window = window; } void Prepare(D3D12_RESOURCE_STATES beforeState = D3D12_RESOURCE_STATE_PRESENT, @@ -35,6 +42,7 @@ namespace DX void Suspend(); void Resume(); void WaitForGpu() noexcept; + void WaitForOrigin(); // Device Accessors. RECT GetOutputSize() const noexcept { return m_outputSize; } @@ -67,19 +75,7 @@ namespace DX return CD3DX12_CPU_DESCRIPTOR_HANDLE(m_dsvDescriptorHeap->GetCPUDescriptorHandleForHeapStart()); } - // Direct3D HDR Game DVR support for Xbox One. - ID3D12Resource* GetGameDVRRenderTarget() const noexcept { return m_renderTargetsGameDVR[m_backBufferIndex].Get(); } - DXGI_FORMAT GetGameDVRFormat() const noexcept { return m_gameDVRFormat; } - - CD3DX12_CPU_DESCRIPTOR_HANDLE GetGameDVRRenderTargetView() const noexcept - { - return CD3DX12_CPU_DESCRIPTOR_HANDLE( - m_rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), - static_cast(m_backBufferCount + m_backBufferIndex), m_rtvDescriptorSize); - } - private: - void MoveToNextFrame(); void RegisterFrameEvents(); static constexpr size_t MAX_BACK_BUFFER_COUNT = 3; @@ -87,8 +83,13 @@ namespace DX UINT m_backBufferIndex; // Direct3D objects. - Microsoft::WRL::ComPtr m_d3dDevice; +#ifdef _GAMING_XBOX_SCARLETT + Microsoft::WRL::ComPtr m_d3dDevice; + Microsoft::WRL::ComPtr m_commandList; +#else + Microsoft::WRL::ComPtr m_d3dDevice; Microsoft::WRL::ComPtr m_commandList; +#endif Microsoft::WRL::ComPtr m_commandQueue; Microsoft::WRL::ComPtr m_commandAllocators[MAX_BACK_BUFFER_COUNT]; @@ -121,9 +122,5 @@ namespace DX // DeviceResources options (see flags above) unsigned int m_options; - - // Direct3D HDR Game DVR support for Xbox One. - Microsoft::WRL::ComPtr m_renderTargetsGameDVR[MAX_BACK_BUFFER_COUNT]; - DXGI_FORMAT m_gameDVRFormat; }; } diff --git a/Samples/Graphics/AdvancedESRAM/Main.cpp b/Samples/Graphics/AdvancedESRAM/Main.cpp index 64b3364..2437de3 100644 --- a/Samples/Graphics/AdvancedESRAM/Main.cpp +++ b/Samples/Graphics/AdvancedESRAM/Main.cpp @@ -1,7 +1,7 @@ //-------------------------------------------------------------------------------------- // Main.cpp // -// Entry point for Microsoft GDK with Xbox extensions. +// Entry point for Microsoft GDK with Xbox extensions // // Advanced Technology Group (ATG) // Copyright (C) Microsoft Corporation. All rights reserved. @@ -18,16 +18,25 @@ using namespace DirectX; +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wcovered-switch-default" +#pragma clang diagnostic ignored "-Wswitch-enum" +#endif + +#pragma warning(disable : 4061) + namespace { std::unique_ptr g_sample; HANDLE g_plmSuspendComplete = nullptr; HANDLE g_plmSignalResume = nullptr; -}; +} bool g_HDRMode = false; LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); +void SetDisplayMode() noexcept; +void ExitSample() noexcept; // Entry point int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) @@ -58,6 +67,8 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp // Register class and create window PAPPSTATE_REGISTRATION hPLM = {}; + PAPPCONSTRAIN_REGISTRATION hPLM2 = {}; + { // Register class WNDCLASSEXA wcex = {}; @@ -66,7 +77,7 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp wcex.lpfnWndProc = WndProc; wcex.hInstance = hInstance; wcex.lpszClassName = u8"PointSpritesWindowClass"; - wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); + wcex.hbrBackground = reinterpret_cast(COLOR_WINDOW + 1); if (!RegisterClassExA(&wcex)) return 1; @@ -79,17 +90,7 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp ShowWindow(hwnd, nCmdShow); - if (g_sample->RequestHDRMode()) - { - // Request HDR mode. - auto result = XDisplayTryEnableHdrMode(XDisplayHdrModePreference::PreferHdr, nullptr); - - g_HDRMode = (result == XDisplayHdrModeResult::Enabled); - -#ifdef _DEBUG - OutputDebugStringA((g_HDRMode) ? "INFO: Display in HDR Mode\n" : "INFO: Display in SDR Mode\n"); -#endif - } + SetDisplayMode(); SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast(g_sample.get())); @@ -116,7 +117,7 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp PostMessage(reinterpret_cast(context), WM_USER, 0, 0); // To defer suspend, you must wait to exit this callback - (void)WaitForSingleObject(g_plmSuspendComplete, INFINITE); + std::ignore = WaitForSingleObject(g_plmSuspendComplete, INFINITE); } else { @@ -124,6 +125,13 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp } }, hwnd, &hPLM)) return 1; + + if (RegisterAppConstrainedChangeNotification([](BOOLEAN constrained, PVOID context) + { + // To ensure we use the main UI thread to process the notification, we self-post a message + SendMessage(reinterpret_cast(context), WM_USER + 1, (constrained) ? 1u : 0u, 0); + }, hwnd, &hPLM2)) + return 1; } // Main message loop @@ -144,13 +152,14 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp g_sample.reset(); UnregisterAppStateChangeNotification(hPLM); + UnregisterAppConstrainedChangeNotification(hPLM2); CloseHandle(g_plmSuspendComplete); CloseHandle(g_plmSignalResume); XGameRuntimeUninitialize(); - return (int)msg.wParam; + return static_cast(msg.wParam); } // Windows procedure @@ -168,18 +177,52 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) // Complete deferral SetEvent(g_plmSuspendComplete); - (void)WaitForSingleObject(g_plmSignalResume, INFINITE); + std::ignore = WaitForSingleObject(g_plmSignalResume, INFINITE); + + SetDisplayMode(); sample->OnResuming(); } break; + + case WM_USER + 1: + if (sample) + { + if (wParam) + { + sample->OnConstrained(); + } + else + { + SetDisplayMode(); + + sample->OnUnConstrained(); + } + } + break; } return DefWindowProc(hWnd, message, wParam, lParam); } +// HDR helper +void SetDisplayMode() noexcept +{ + if (g_sample && g_sample->RequestHDRMode()) + { + // Request HDR mode. + auto result = XDisplayTryEnableHdrMode(XDisplayHdrModePreference::PreferHdr, nullptr); + + g_HDRMode = (result == XDisplayHdrModeResult::Enabled); + +#ifdef _DEBUG + OutputDebugStringA((g_HDRMode) ? "INFO: Display in HDR Mode\n" : "INFO: Display in SDR Mode\n"); +#endif + } +} + // Exit helper -void ExitSample() +void ExitSample() noexcept { PostQuitMessage(0); } diff --git a/Samples/Graphics/AdvancedESRAM/ReadMe.docx b/Samples/Graphics/AdvancedESRAM/ReadMe.docx index ed3758825ab57e1f6b866e933990af2cdb827baf..6cf09c9de1ccb351135559ba1f95de4b0f5c09b3 100644 GIT binary patch delta 33050 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 delta 32487 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 diff --git a/Samples/Graphics/AdvancedESRAM/StepTimer.h b/Samples/Graphics/AdvancedESRAM/StepTimer.h index bb15fff..2dbefcb 100644 --- a/Samples/Graphics/AdvancedESRAM/StepTimer.h +++ b/Samples/Graphics/AdvancedESRAM/StepTimer.h @@ -111,7 +111,7 @@ namespace DX timeDelta *= TicksPerSecond; timeDelta /= static_cast(m_qpcFrequency.QuadPart); - uint32_t lastFrameCount = m_frameCount; + const uint32_t lastFrameCount = m_frameCount; if (m_isFixedTimeStep) { diff --git a/Samples/Graphics/AdvancedESRAM/pch.h b/Samples/Graphics/AdvancedESRAM/pch.h index 790527c..5a030a4 100644 --- a/Samples/Graphics/AdvancedESRAM/pch.h +++ b/Samples/Graphics/AdvancedESRAM/pch.h @@ -37,8 +37,8 @@ #include -#if _GXDK_VER < 0x4A610D2B /* GXDK Edition 200600 */ -#error This sample requires the June 2020 GDK or later +#if _GXDK_VER < 0x55F007B0 /* GDK Edition 211000 */ +#error This sample requires the October 2021 GDK or later #endif #ifdef _GAMING_XBOX_SCARLETT @@ -59,25 +59,30 @@ #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 "ATGColors.h" #include "CommonStates.h" -#include "ControllerFont.h" #include "DescriptorHeap.h" #include "DirectXHelpers.h" #include "Effects.h" @@ -85,9 +90,7 @@ #include "GeometricPrimitive.h" #include "GraphicsMemory.h" #include "Model.h" -#include "PerformanceTimersXbox.h" #include "PostProcess.h" -#include "ReadData.h" #include "RenderTargetState.h" #include "ResourceUploadBatch.h" #include "SimpleMath.h" @@ -95,6 +98,11 @@ #include "SpriteFont.h" #include "VertexTypes.h" +#include "ATGColors.h" +#include "ControllerFont.h" +#include "PerformanceTimersXbox.h" +#include "ReadData.h" + #include "SharedDefinitions.h" namespace DX @@ -103,7 +111,7 @@ namespace DX class com_exception : public std::exception { public: - com_exception(HRESULT hr) : result(hr) {} + com_exception(HRESULT hr) noexcept : result(hr) {} const char* what() const override { @@ -121,6 +129,12 @@ namespace DX { if (FAILED(hr)) { +#ifdef _DEBUG + char str[64] = {}; + sprintf_s(str, "**ERROR** Fatal Error with HRESULT of %08X\n", static_cast(hr)); + OutputDebugStringA(str); + __debugbreak(); +#endif throw com_exception(hr); } } diff --git a/Samples/Graphics/Antialiasing/Antialiasing.cpp b/Samples/Graphics/Antialiasing/Antialiasing.cpp index 5813532..947bfba 100644 --- a/Samples/Graphics/Antialiasing/Antialiasing.cpp +++ b/Samples/Graphics/Antialiasing/Antialiasing.cpp @@ -137,6 +137,8 @@ void Sample::Tick() { PIXBeginEvent(PIX_COLOR_DEFAULT, L"Frame %I64u", m_frame); + m_deviceResources->WaitForOrigin(); + m_timer.Tick([&]() { Update(m_timer); @@ -226,8 +228,8 @@ void Sample::Update(DX::StepTimer const&) } // Limit to avoid looking directly up or down - const float limit = XM_PI / 2.0f - 0.01f; - m_pitch = std::max(-limit, std::min(+limit, m_pitch)); + constexpr float c_limit = XM_PI / 2.0f - 0.01f; + m_pitch = std::max(-c_limit, std::min(+c_limit, m_pitch)); if (m_yaw > XM_PI) { @@ -386,7 +388,7 @@ void Sample::Render() { case AntialiasMethods::SMAA2X: { - auto size = m_deviceResources->GetOutputSize(); + auto const size = m_deviceResources->GetOutputSize(); SMAAConstantBuffer cb = { { 1.f, 1.f, 1.f, 0.f }, 1.f / float(size.right), 1.f / float(size.bottom) }; auto cbHandle = m_graphicsMemory->AllocateConstant(cb); RenderSMAA(commandList, Descriptors::SceneTex, Descriptors::DepthTex, 1.f, cbHandle.GpuAddress()); @@ -406,7 +408,7 @@ void Sample::Render() commandList->ResourceBarrier(1, &barrier); } - auto size = m_deviceResources->GetOutputSize(); + auto const size = m_deviceResources->GetOutputSize(); SMAAConstantBuffer cb = { { 0.f, 0.f, 0.f, 0.f }, 1.f / float(size.right), 1.f / float(size.bottom) }; auto cbHandle = m_graphicsMemory->AllocateConstant(cb); RenderSMAA(commandList, Descriptors::SceneTex, m_hardwareAA ? Descriptors::DepthTex : Descriptors::DepthStencilSRV, 1.f, cbHandle.GpuAddress()); @@ -425,13 +427,13 @@ void Sample::Render() { PIXBeginEvent(commandList, PIX_COLOR_DEFAULT, L"Render FXAA"); - auto rtvDescriptor = m_renderDescriptors->GetCpuHandle(RTDescriptors::FinalRTV); + auto const rtvDescriptor = m_renderDescriptors->GetCpuHandle(RTDescriptors::FinalRTV); commandList->OMSetRenderTargets(1, &rtvDescriptor, FALSE, nullptr); m_scene->TransitionTo(commandList, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); m_final->BeginScene(commandList); - auto size = m_deviceResources->GetOutputSize(); + auto const size = m_deviceResources->GetOutputSize(); FXAAConstantBuffer cb = { 1.f / float(size.right), 1.f / float(size.bottom) }; auto cbHandle = m_graphicsMemory->AllocateConstant(cb); @@ -451,7 +453,7 @@ void Sample::Render() } // Render final scene - auto rtvDescriptor = m_deviceResources->GetRenderTargetView(); + auto const rtvDescriptor = m_deviceResources->GetRenderTargetView(); commandList->OMSetRenderTargets(1, &rtvDescriptor, FALSE, nullptr); @@ -517,10 +519,10 @@ void Sample::RenderZoom(ID3D12GraphicsCommandList* commandList) m_fullScreenQuad->Draw(commandList, m_zoomPSO.Get(), m_resourceDescriptors->GetGpuHandle(Descriptors::FinalTex)); - auto vp = m_deviceResources->GetScreenViewport(); + auto const vp = m_deviceResources->GetScreenViewport(); commandList->RSSetViewports(1, &vp); - auto scissors = m_deviceResources->GetScissorRect(); + auto const scissors = m_deviceResources->GetScissorRect(); commandList->RSSetScissorRects(1, &scissors); m_lineEFfect->Apply(commandList); @@ -554,8 +556,8 @@ void Sample::RenderUI(ID3D12GraphicsCommandList* commandList) }; commandList->SetDescriptorHeaps(_countof(descriptorHeaps), descriptorHeaps); - auto size = m_deviceResources->GetOutputSize(); - auto safe = SimpleMath::Viewport::ComputeTitleSafeArea(UINT(size.right), UINT(size.bottom)); + auto const size = m_deviceResources->GetOutputSize(); + auto const safe = SimpleMath::Viewport::ComputeTitleSafeArea(UINT(size.right), UINT(size.bottom)); m_batch->Begin(commandList); @@ -683,7 +685,7 @@ void Sample::RenderSMAA(ID3D12GraphicsCommandList* commandList, size_t renderTar commandList->OMSetRenderTargets(1, &rtvDescriptor, FALSE, nullptr); - float factor[4] = { blendFactor, blendFactor, blendFactor, blendFactor }; + const float factor[4] = { blendFactor, blendFactor, blendFactor, blendFactor }; commandList->OMSetBlendFactor(factor); m_fullScreenQuad->Draw(commandList, m_neighborBlendPSO.Get(), colorHandle, @@ -730,8 +732,8 @@ void Sample::Clear() commandList->ClearDepthStencilView(dsvDescriptor, D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr); // Set the viewport and scissor rect. - auto viewport = m_deviceResources->GetScreenViewport(); - auto scissorRect = m_deviceResources->GetScissorRect(); + auto const viewport = m_deviceResources->GetScreenViewport(); + auto const scissorRect = m_deviceResources->GetScissorRect(); commandList->RSSetViewports(1, &viewport); commandList->RSSetScissorRects(1, &scissorRect); @@ -789,7 +791,7 @@ void Sample::CreateDeviceDependentResources() m_model->LoadStaticBuffers(device, upload); - RenderTargetState rtStateUI(m_deviceResources->GetBackBufferFormat(), m_deviceResources->GetDepthBufferFormat()); + const RenderTargetState rtStateUI(m_deviceResources->GetBackBufferFormat(), m_deviceResources->GetDepthBufferFormat()); { SpriteBatchPipelineStateDescription pd(rtStateUI, &CommonStates::AlphaBlend); @@ -1111,7 +1113,7 @@ void Sample::CreateWindowSizeDependentResources() { m_batch->SetViewport(m_deviceResources->GetScreenViewport()); - auto size = m_deviceResources->GetOutputSize(); + auto const size = m_deviceResources->GetOutputSize(); auto device = m_deviceResources->GetD3DDevice(); ResourceUploadBatch resourceUpload(device); diff --git a/Samples/Graphics/Antialiasing/Antialiasing.h b/Samples/Graphics/Antialiasing/Antialiasing.h index 7388a84..d61b4d6 100644 --- a/Samples/Graphics/Antialiasing/Antialiasing.h +++ b/Samples/Graphics/Antialiasing/Antialiasing.h @@ -37,6 +37,11 @@ public: // Messages void OnSuspending(); void OnResuming(); + void OnConstrained() {} + void OnUnConstrained() {} + + // Properties + bool RequestHDRMode() const noexcept { return m_deviceResources ? (m_deviceResources->GetDeviceOptions() & DX::DeviceResources::c_EnableHDR) != 0 : false; } private: diff --git a/Samples/Graphics/Antialiasing/DeviceResources.cpp b/Samples/Graphics/Antialiasing/DeviceResources.cpp index 1b11747..c7045e9 100644 --- a/Samples/Graphics/Antialiasing/DeviceResources.cpp +++ b/Samples/Graphics/Antialiasing/DeviceResources.cpp @@ -33,7 +33,7 @@ namespace default: return fmt; } } -}; +} // Constructor for DeviceResources. DeviceResources::DeviceResources( @@ -70,12 +70,16 @@ DeviceResources::~DeviceResources() // Ensure we present a blank screen before cleaning up resources. if (m_commandQueue) { - (void)m_commandQueue->PresentX(0, nullptr, nullptr); + std::ignore = m_commandQueue->PresentX(0, nullptr, nullptr); } } // Configures the Direct3D device, and stores handles to it and the device context. +#ifdef _GAMING_XBOX_SCARLETT +void DeviceResources::CreateDeviceResources(D3D12XBOX_CREATE_DEVICE_FLAGS createDeviceFlags) +#else void DeviceResources::CreateDeviceResources() +#endif { // Create the DX12 API device object. D3D12XBOX_CREATE_DEVICE_PARAMETERS params = {}; @@ -92,6 +96,17 @@ void DeviceResources::CreateDeviceResources() params.GraphicsCommandQueueRingSizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); params.GraphicsScratchMemorySizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); params.ComputeScratchMemorySizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); +#ifdef _GAMING_XBOX_SCARLETT + params.CreateDeviceFlags = createDeviceFlags; + +#if (_GXDK_VER >= 0x585D070E /* GXDK Edition 221000 */) + if (m_options & c_AmplificationShaders) + { + params.AmplificationShaderIndirectArgsBufferSize = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); + params.AmplificationShaderPayloadBufferSize = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); + } +#endif +#endif HRESULT hr = D3D12XboxCreateDevice( nullptr, @@ -167,31 +182,46 @@ void DeviceResources::CreateDeviceResources() m_fenceEvent.Attach(CreateEventEx(nullptr, nullptr, 0, EVENT_MODIFY_STATE | SYNCHRONIZE)); if (!m_fenceEvent.IsValid()) { - throw std::exception("CreateEvent"); + throw std::system_error(std::error_code(static_cast(GetLastError()), std::system_category()), "CreateEventEx"); } - if (m_options & c_Enable4K_UHD) + if (m_options & (c_Enable4K_UHD | c_EnableQHD)) { switch (XSystemGetDeviceType()) { case XSystemDeviceType::XboxOne: case XSystemDeviceType::XboxOneS: - case XSystemDeviceType::XboxScarlettLockhart /* Xbox Series S */: - m_options &= ~c_Enable4K_UHD; -#ifdef _DEBUG - OutputDebugStringA("INFO: Swapchain using 1080p (1920 x 1080)\n"); -#endif + m_options &= ~(c_Enable4K_UHD | c_EnableQHD); break; + case XSystemDeviceType::XboxScarlettLockhart /* Xbox Series S */: + m_options &= ~c_Enable4K_UHD; + if (m_options & c_EnableQHD) + { + m_outputSize = { 0, 0, 2560, 1440 }; + } + break; + + case XSystemDeviceType::XboxScarlettAnaconda /* Xbox Series X */: + case XSystemDeviceType::XboxOneXDevkit: + case XSystemDeviceType::XboxScarlettDevkit: default: - m_outputSize = { 0, 0, 3840, 2160 }; -#ifdef _DEBUG - OutputDebugStringA("INFO: Swapchain using 4k (3840 x 2160)\n"); -#endif + m_outputSize = (m_options & c_Enable4K_UHD) ? RECT{ 0, 0, 3840, 2160 } : RECT{ 0, 0, 2560, 1440 }; break; } } +#ifdef _DEBUG + const char* info = nullptr; + switch (m_outputSize.bottom) + { + case 2160: info = "INFO: Swapchain using 4k (3840 x 2160)\n"; break; + case 1440: info = "INFO: Swapchain using 1440p (2560 x 1440)\n"; break; + default: info = "INFO: Swapchain using 1080p (1920 x 1080)\n"; break; + } + OutputDebugStringA(info); +#endif + RegisterFrameEvents(); } @@ -200,7 +230,7 @@ void DeviceResources::CreateWindowSizeDependentResources() { if (!m_window) { - throw std::exception("Call SetWindow with a valid window handle"); + throw std::logic_error("Call SetWindow with a valid Win32 window handle"); } // Wait until all previous GPU work is complete. @@ -222,7 +252,7 @@ void DeviceResources::CreateWindowSizeDependentResources() // Obtain the back buffers for this window which will be the final render targets // and create render target views for each of them. - CD3DX12_HEAP_PROPERTIES swapChainHeapProperties(D3D12_HEAP_TYPE_DEFAULT); + const CD3DX12_HEAP_PROPERTIES swapChainHeapProperties(D3D12_HEAP_TYPE_DEFAULT); D3D12_RESOURCE_DESC swapChainBufferDesc = CD3DX12_RESOURCE_DESC::Tex2D( m_backBufferFormat, @@ -233,6 +263,13 @@ void DeviceResources::CreateWindowSizeDependentResources() ); swapChainBufferDesc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; +#ifdef _GAMING_XBOX_XBOXONE + if (m_options & c_EnableHDR) + { + swapChainBufferDesc.Flags |= D3D12XBOX_RESOURCE_FLAG_ALLOW_AUTOMATIC_GAMEDVR_TONE_MAP; + } +#endif + D3D12_CLEAR_VALUE swapChainOptimizedClearValue = {}; swapChainOptimizedClearValue.Format = m_backBufferFormat; @@ -254,7 +291,7 @@ void DeviceResources::CreateWindowSizeDependentResources() rtvDesc.Format = m_backBufferFormat; rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; - CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( + const CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( m_rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), static_cast(n), m_rtvDescriptorSize); m_d3dDevice->CreateRenderTargetView(m_renderTargets[n].Get(), &rtvDesc, rtvDescriptor); @@ -267,7 +304,7 @@ void DeviceResources::CreateWindowSizeDependentResources() { // Allocate a 2-D surface as the depth/stencil buffer and create a depth/stencil view // on this surface. - CD3DX12_HEAP_PROPERTIES depthHeapProperties(D3D12_HEAP_TYPE_DEFAULT); + const CD3DX12_HEAP_PROPERTIES depthHeapProperties(D3D12_HEAP_TYPE_DEFAULT); D3D12_RESOURCE_DESC depthStencilDesc = CD3DX12_RESOURCE_DESC::Tex2D( MakeDepthTypeless(m_depthBufferFormat), @@ -280,7 +317,7 @@ void DeviceResources::CreateWindowSizeDependentResources() D3D12_CLEAR_VALUE depthOptimizedClearValue = {}; depthOptimizedClearValue.Format = m_depthBufferFormat; - depthOptimizedClearValue.DepthStencil.Depth = 1.0f; + depthOptimizedClearValue.DepthStencil.Depth = (m_options & c_ReverseDepth) ? 0.0f : 1.0f; depthOptimizedClearValue.DepthStencil.Stencil = 0; ThrowIfFailed(m_d3dDevice->CreateCommittedResource( @@ -316,29 +353,29 @@ void DeviceResources::CreateWindowSizeDependentResources() // Prepare the command list and render target for rendering. void DeviceResources::Prepare(D3D12_RESOURCE_STATES beforeState, D3D12_RESOURCE_STATES afterState) { - // Wait until frame start is signaled - m_framePipelineToken = D3D12XBOX_FRAME_PIPELINE_TOKEN_NULL; - ThrowIfFailed(m_d3dDevice->WaitFrameEventX(D3D12XBOX_FRAME_EVENT_ORIGIN, INFINITE, nullptr, D3D12XBOX_WAIT_FRAME_EVENT_FLAG_NONE, &m_framePipelineToken)); - // Reset command list and allocator. ThrowIfFailed(m_commandAllocators[m_backBufferIndex]->Reset()); ThrowIfFailed(m_commandList->Reset(m_commandAllocators[m_backBufferIndex].Get(), nullptr)); if (beforeState != afterState) { - D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), + // Transition the render target into the correct state to allow for drawing into it. + const D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition( + m_renderTargets[m_backBufferIndex].Get(), beforeState, afterState); m_commandList->ResourceBarrier(1, &barrier); } } // Present the contents of the swap chain to the screen. -void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState) +void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState, _In_opt_ const D3D12XBOX_PRESENT_PARAMETERS* params) { if (beforeState != D3D12_RESOURCE_STATE_PRESENT) { // Transition the render target to the state that allows it to be presented to the display. - D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), beforeState, D3D12_RESOURCE_STATE_PRESENT); + const D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition( + m_renderTargets[m_backBufferIndex].Get(), + beforeState, D3D12_RESOURCE_STATE_PRESENT); m_commandList->ResourceBarrier(1, &barrier); } @@ -347,18 +384,24 @@ void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState) m_commandQueue->ExecuteCommandLists(1, CommandListCast(m_commandList.GetAddressOf())); // Present the backbuffer using the PresentX API. - D3D12XBOX_PRESENT_PLANE_PARAMETERS planeParameters[2] = {}; - planeParameters[0].Token = planeParameters[1].Token = m_framePipelineToken; - planeParameters[0].ResourceCount = planeParameters[1].ResourceCount = 1; - planeParameters[0].ppResources = m_renderTargets[m_backBufferIndex].GetAddressOf(); + D3D12XBOX_PRESENT_PLANE_PARAMETERS planeParameters = {}; + planeParameters.Token = m_framePipelineToken; + planeParameters.ResourceCount = 1; + planeParameters.ppResources = m_renderTargets[m_backBufferIndex].GetAddressOf(); + + if (m_options & c_EnableHDR) + { + planeParameters.ColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020; + } ThrowIfFailed( - m_commandQueue->PresentX(1, planeParameters, nullptr) + m_commandQueue->PresentX(1, &planeParameters, params) ); - // Xbox One apps do not need to handle DXGI_ERROR_DEVICE_REMOVED or DXGI_ERROR_DEVICE_RESET. + // Xbox apps do not need to handle DXGI_ERROR_DEVICE_REMOVED or DXGI_ERROR_DEVICE_RESET. - MoveToNextFrame(); + // Update the back buffer index. + m_backBufferIndex = (m_backBufferIndex + 1) % m_backBufferCount; } // Handle GPU suspend/resume @@ -380,13 +423,13 @@ void DeviceResources::WaitForGpu() noexcept if (m_commandQueue && m_fence && m_fenceEvent.IsValid()) { // Schedule a Signal command in the GPU queue. - UINT64 fenceValue = m_fenceValues[m_backBufferIndex]; + const UINT64 fenceValue = m_fenceValues[m_backBufferIndex]; if (SUCCEEDED(m_commandQueue->Signal(m_fence.Get(), fenceValue))) { // Wait until the Signal has been processed. if (SUCCEEDED(m_fence->SetEventOnCompletion(fenceValue, m_fenceEvent.Get()))) { - WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); + std::ignore = WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); // Increment the fence value for the current frame. m_fenceValues[m_backBufferIndex]++; @@ -395,25 +438,17 @@ void DeviceResources::WaitForGpu() noexcept } } -// Prepare to render the next frame. -void DeviceResources::MoveToNextFrame() +// For PresentX rendering, we should wait for the origin event just before processing input. +void DeviceResources::WaitForOrigin() { - // Schedule a Signal command in the queue. - const UINT64 currentFenceValue = m_fenceValues[m_backBufferIndex]; - ThrowIfFailed(m_commandQueue->Signal(m_fence.Get(), currentFenceValue)); - - // Update the back buffer index. - m_backBufferIndex = (m_backBufferIndex + 1) % m_backBufferCount; - - // If the next frame is not ready to be rendered yet, wait until it is ready. - if (m_fence->GetCompletedValue() < m_fenceValues[m_backBufferIndex]) - { - ThrowIfFailed(m_fence->SetEventOnCompletion(m_fenceValues[m_backBufferIndex], m_fenceEvent.Get())); - WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); - } - - // Set the fence value for the next frame. - m_fenceValues[m_backBufferIndex] = currentFenceValue + 1; + // Wait until frame start is signaled + m_framePipelineToken = D3D12XBOX_FRAME_PIPELINE_TOKEN_NULL; + ThrowIfFailed(m_d3dDevice->WaitFrameEventX( + D3D12XBOX_FRAME_EVENT_ORIGIN, + INFINITE, + nullptr, + D3D12XBOX_WAIT_FRAME_EVENT_FLAG_NONE, + &m_framePipelineToken)); } // Set frame interval and register for frame events diff --git a/Samples/Graphics/Antialiasing/DeviceResources.h b/Samples/Graphics/Antialiasing/DeviceResources.h index f32d1b7..eef98b3 100644 --- a/Samples/Graphics/Antialiasing/DeviceResources.h +++ b/Samples/Graphics/Antialiasing/DeviceResources.h @@ -1,6 +1,8 @@ // // DeviceResources.h - A wrapper for the Direct3D 12.X device and swapchain // +// Modified to use a typeless format for depth +// #pragma once @@ -10,7 +12,11 @@ namespace DX class DeviceResources { public: - static constexpr unsigned int c_Enable4K_UHD = 0x1; + static constexpr unsigned int c_Enable4K_UHD = 0x1; + static constexpr unsigned int c_EnableQHD = 0x2; + static constexpr unsigned int c_EnableHDR = 0x4; + static constexpr unsigned int c_ReverseDepth = 0x8; + static constexpr unsigned int c_AmplificationShaders = 0x10; DeviceResources(DXGI_FORMAT backBufferFormat = DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT depthBufferFormat = DXGI_FORMAT_D32_FLOAT, @@ -24,15 +30,21 @@ namespace DX DeviceResources(DeviceResources const&) = delete; DeviceResources& operator= (DeviceResources const&) = delete; +#ifdef _GAMING_XBOX_SCARLETT + void CreateDeviceResources(D3D12XBOX_CREATE_DEVICE_FLAGS createDeviceFlags = D3D12XBOX_CREATE_DEVICE_FLAG_NONE); +#else void CreateDeviceResources(); +#endif void CreateWindowSizeDependentResources(); void SetWindow(HWND window) noexcept { m_window = window; } void Prepare(D3D12_RESOURCE_STATES beforeState = D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATES afterState = D3D12_RESOURCE_STATE_RENDER_TARGET); - void Present(D3D12_RESOURCE_STATES beforeState = D3D12_RESOURCE_STATE_RENDER_TARGET); + void Present(D3D12_RESOURCE_STATES beforeState = D3D12_RESOURCE_STATE_RENDER_TARGET, + _In_opt_ const D3D12XBOX_PRESENT_PARAMETERS* params = nullptr); void Suspend(); void Resume(); void WaitForGpu() noexcept; + void WaitForOrigin(); // Device Accessors. RECT GetOutputSize() const noexcept { return m_outputSize; } @@ -66,7 +78,6 @@ namespace DX } private: - void MoveToNextFrame(); void RegisterFrameEvents(); static constexpr size_t MAX_BACK_BUFFER_COUNT = 3; diff --git a/Samples/Graphics/Antialiasing/Main.cpp b/Samples/Graphics/Antialiasing/Main.cpp index df7111c..59fddbb 100644 --- a/Samples/Graphics/Antialiasing/Main.cpp +++ b/Samples/Graphics/Antialiasing/Main.cpp @@ -18,16 +18,25 @@ using namespace DirectX; +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wcovered-switch-default" +#pragma clang diagnostic ignored "-Wswitch-enum" +#endif + +#pragma warning(disable : 4061) + namespace { std::unique_ptr g_sample; HANDLE g_plmSuspendComplete = nullptr; HANDLE g_plmSignalResume = nullptr; -}; +} bool g_HDRMode = false; LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); +void SetDisplayMode() noexcept; +void ExitSample() noexcept; // Entry point int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) @@ -58,6 +67,8 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp // Register class and create window PAPPSTATE_REGISTRATION hPLM = {}; + PAPPCONSTRAIN_REGISTRATION hPLM2 = {}; + { // Register class WNDCLASSEXA wcex = {}; @@ -79,6 +90,8 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp ShowWindow(hwnd, nCmdShow); + SetDisplayMode(); + SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast(g_sample.get())); // Sample Usage Telemetry @@ -104,7 +117,7 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp PostMessage(reinterpret_cast(context), WM_USER, 0, 0); // To defer suspend, you must wait to exit this callback - (void)WaitForSingleObject(g_plmSuspendComplete, INFINITE); + std::ignore = WaitForSingleObject(g_plmSuspendComplete, INFINITE); } else { @@ -112,6 +125,13 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp } }, hwnd, &hPLM)) return 1; + + if (RegisterAppConstrainedChangeNotification([](BOOLEAN constrained, PVOID context) + { + // To ensure we use the main UI thread to process the notification, we self-post a message + SendMessage(reinterpret_cast(context), WM_USER + 1, (constrained) ? 1u : 0u, 0); + }, hwnd, &hPLM2)) + return 1; } // Main message loop @@ -132,7 +152,8 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp g_sample.reset(); UnregisterAppStateChangeNotification(hPLM); - + UnregisterAppConstrainedChangeNotification(hPLM2); + CloseHandle(g_plmSuspendComplete); CloseHandle(g_plmSignalResume); @@ -156,16 +177,50 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) // Complete deferral SetEvent(g_plmSuspendComplete); - (void)WaitForSingleObject(g_plmSignalResume, INFINITE); + std::ignore = WaitForSingleObject(g_plmSignalResume, INFINITE); + + SetDisplayMode(); sample->OnResuming(); } break; + + case WM_USER + 1: + if (sample) + { + if (wParam) + { + sample->OnConstrained(); + } + else + { + SetDisplayMode(); + + sample->OnUnConstrained(); + } + } + break; } return DefWindowProc(hWnd, message, wParam, lParam); } +// HDR helper +void SetDisplayMode() noexcept +{ + if (g_sample && g_sample->RequestHDRMode()) + { + // Request HDR mode. + auto result = XDisplayTryEnableHdrMode(XDisplayHdrModePreference::PreferHdr, nullptr); + + g_HDRMode = (result == XDisplayHdrModeResult::Enabled); + +#ifdef _DEBUG + OutputDebugStringA((g_HDRMode) ? "INFO: Display in HDR Mode\n" : "INFO: Display in SDR Mode\n"); +#endif + } +} + // Exit helper void ExitSample() noexcept { diff --git a/Samples/Graphics/Antialiasing/Readme.docx b/Samples/Graphics/Antialiasing/Readme.docx index 78a58d16fcc35dd6d04e927fa07e26cb2f3972c9..14593ba264ab59a229aee83028c8f15ed2324085 100644 GIT binary patch delta 28983 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±0I(_&C znjTSUg_oA|)_L{wuJDbQ{#>Fk{PH!9%Lh-UHQSI~?z%HSylFm}d8%K~_@PmQS9iFy z!KllzR(^38R7uC>uI%K`Z+8@4*wAeJ_z(}Sqw)XV&_!YIZSL&GuKtsuLKO-+4J3k| z0ssIz3zM=)B!6^o3-F0Bu`_MlbaK~e+6PdCY?z?KNV2itzAMbZ4Xy{1sV@Xxv7dge zb{Fm+4m|hvOq)!KB?`tq@|d79$;5Vve*bzJMaVOjh=k-)uq8S&gC16&KiwBG6N!+P z84nP`#D$_ulv=A|j15g0CuYnus-=+|JEoFjx!Git;eSHv1c!d$mv>4_%8Y^a9*Ni! zgDM<7^rI!36yWTDQ%qB$EjzS~;AY4S-{GhW3I~8-hhfk~O>Rcbu~XFnO$UKMRfjYi zM!K-M`7l!0B`t=LCS6iAjMPad-c4|+n1IQR)SOuOtGAdF{kBsh04rj%*DTNM5t#nG z#*j?B4S!GsUJZdylA8$&%w@vzNm5hf6795zEB{gB@1(~k|LW3su)N>0u-qMH2b&vP z`|3WiJGrkV-Ek&t88xg~4sn#iq?u~E10PC*$+U*-uLWSw^ZL0$1wBjcU#)F@I))b5 zM}Bp7JU=PFP7M4Wr|A%lM;{4)uURYMkiqT3gMUJE7Tlm`^Qi%02Qa6s=gXpIL=^+m zwz?fB>seMc=X5HLleK{jdiKEgV$J|2u}OLpC9LC$oiDL%eFanb0`kQ?Eu9oQd|#Oin|so?(Po79=-3s@4M@+KeKj{HQC9^%1mbagod_6!9&in zIXa8Cgt}7;S>L3u3DLd^(IG0scF!HS8Yv4T zI1;`-iJQj0P)=b$X;;uyI;mp2u>IsMK_lR2c?T6(4i=^tXSz=@u50JY9Vc~?@giry z>5MQ|fB%9c(bGN@vjJqK1HL6EbGH;Q6($bL4+t zQN*ukTU`7I5D+sdE=q*JW2fyF+*gzQbBYHWNI86^bW4Z&G)F;fibc-klbcB>Sk`GU zks=PmQhf6&{E_|5u%G$%$MGTNyh2)3|z!0d5* z(*ncRpsGA_Imen}ToNlzDxO|Y6)&5Cxbe%Qs(7WJ<^)gvM9dnHDS^$y%=^RiN9n<+uJY$$yX79Q&iFh*Mjx3`mFXNR z@?W+|Td_)2hbY)n)Z;O}$qS2=qWGwE`?y0_8Bpnr-{-5`fY*I$!l_FUBPggFj3*$l zQuNldK%rf>sWPOxW_C}{T`n#?qmN6&rGELMTD7nF)pv+W(O{=ARS^~@l$yS&BDywM z6*qG|ZGjOcD0({_``ip@@61IA26M8>^)Pd7zd2aeE!+5*rD<5IAYx4w)MgcakJQOE ze1wHqwktSRpfCB7atfAiA)7C>4x!pt3FD50%t*lzO3f1WbL({V2s#*nkc+9VUS2!M zOBgv!_Qk$Qw@<$fumwvzVPSFe60dlzt+t()6vHYk2-GsVhlJpFM7CIeeB$T|TcK>q z-UBq*^V)CQe4ED-*##`|dE+4zRTDvW6jPzi8nU%?0pMfGlq`$w#K!?qzPrA|efZ7g(ZY+~J(N)H?jZR?MHl z5icsu5@iSlU9hcRXgVWLQ6|p_;_1E8q%7#*q$=#&T{=UX`~}Sq;Cx(-Mw?i~wTjea zTzJc0flR@2GV89Q%i4Phvkg1qOpeaN?8{XK#_lNfI_EDyqDWz&c;xz-pZ`YfL8rCrSyEC@`u4rhjA+ko{b(>^}056CKM3W$vU zxWQN)>%THZuSA3q{wxcH>UrjHpa6==8$&nSfuRx&PEn<{5-rjfLQ*D25@WIbiQ9tl z#i_ZVF_8=2?lcx^ML}!D$+xI-bwUU$76vB1LPGYivBVE#7qRB2`@~I>fPz91&E=jj zXh?R6$`L3x_aD|qqQ+JXMHaG$H^7lkux2B_7p-;Uv`?!2tQ4TfJ0k0C(NOvny2*=8 zfH68homOK|pk>+G@0FY7e`RlQvhEmYld)aWv#>xDxs5q3)QVihA?DKSqul0wlDEAH z(;lI)RmOj&!;V%gA&^Ee3XEVk4RK3zryvE9C=T9KRtp}Xj3ScIGi3!%dJD-iWHX%4 z1=vlxftq}f(sq7i<3*R5iO-$H6ND4-0h1M!lX|lzXY4*dYPVZFr9%UjNh;frY>;n9 z(aI4@38fwb@P7+LJj$>|WP}>%W1Fm3FYpRe$iakU#Gfvw@%RY!W1`O~Ce0aUA#l}( z2dEIW>UQ;SlZe>D(P!I2i?rbGUPsalgm0;SjAhiHv@Jm#XQF0Ib9;bT)}>eA0zS)s zb)9j4ei0nK5I$45j&vC{T{f>HWPnXc?7)*% zD8vkfrJ_(%&H7H#3JcF#&fdW-hM}dOJKarU#Z7`ENbp?@JK_9o&4@ogLd_q53^rN( zftJP`2T9cE=R1zqxE7br+D#^D4V;G5J~2SPTwF)YnU2J9gS5sM3@|(#2S_mAw7~OH zDu{*5DBFH$=$oL4$_| z&R#k)H$MGD1Pt*h2!&SWEL6K5;V%p{|5X2~u(?{uYi(F*FmB1vYv@UT1?(8j?;-6f z=*dT$nN4Jp(+)nF(hh7@m`US`s#fa-^Rci8H!L4D!^d;lE=Lf9q1U~Zt1;WaX8wC! zBw9;)I81Re=e!Cq0L&Ao;p@`YK8qeic8Gd zwy29JN$P#oQax$s>M@#S-f&kh;%+-q6B@TvL9RzF=QF*)61%41@3P6JO_goq~P>s(7E9 zzE#;LpLg%U;_Xrl!jGRt#5>-r;+)*S25;!5o9VABcWtq}4)olrWvtD?gYhs5hlz@& zMjjuwV7|OvS_U-`zp*8gwFL+;Ec}c|0=u4GQChb6A)E~~Z*rz5tnvoB7~aG$6*^0P zUtCnC1YB$rFbw&G0OdpaiuclaF2;5n+IX}lldIc=Uf7&ege5jSo}@OvedRChbl$Q*}*-) z2vOzxkLGxyOAU+NgeKwEmYLhwX(zU|m)-N?PaSz)?>b#U@S|tBqB-vuP1CcKd`UdS zPOL4M#CLweqhV0f2ol&xE&t2eAY&qZNGcP@iaY&wvzq8^#w9bk5~orjmZs~S63S#Z zFH(}mz{P!mv%JNDtl+WxQ{~=jQ%0)f7-SWJYD#`-zg(<9`#2NDb!4WDHXVOJ-E)^6 zN5klIVZ9|W>oJt(PnQSxVG)``@|Sy^Ek`Ah6cYdRxa(G~*&ND-md>WYWrSX<4cbad zfi?#&qwDxRt&Qm%-0knctY%-T`E@sF;oV+`i&G&x=bQd+Hj$JHW;&KP|f*mH;Ia*L!ocv}1W>B=APO+Tj(0&FWqtdP6l>)%Z4K`+5VgyjldLtOMwL0V z)#F8>^ z?1GC?m^BXlOekpFBvP2tdS;bc;ffe9$G!wC+$_z%LgG*zP!W2o6DlzuF5^{U{|C%O z`BNXh9Ca0gt$msJ#1rDTR=7i?usCj#=LL`UAhpJ=Lq8v`x2d#SYqq|^!%mMDFHror zq~2^Raac8sh2DQX$(Sqn;}8<~ zE=+<4^y$}0)4ERs=mm-9YhehqmAG9_YL#ClH}F(dgmo;N+twcnzmB^52gvRi)*n)k zEsHB(#z((lgM$zggu>?Vj`b8DfSa0R>{vJGH{NsFxcc0)-lylKdW{|y;;UMjM~3kY zs|u5)pvk#PP4ht)%i(2~sKgPp#^&~@B2l3)dj)7A(FVyfB$y0q;#NUB00Rp9^X2Du zMZ%fy#ceZ?hi9nF#i~tq7zgxs!{;9v}`8mHke>Rg=Zneg5-uhMt%B9zf8a6a4X#&dI<2A)KjTH+ow0qHZ zMwirW%Zophv^4(6E7d={sZW(`17sca;F-*2s*o;sRH9Zzmy{&1uua3Z<`o-s?i0U#=7&AKgenOS*`DRx=HbO!P8R-}d z*>G2$XiX7%1jYqlzBl;fz@5=@Ur}vO_|~}s;{5QpPl1*KCVggI5MSKG=&x1A@rl;( z$MLzL#ahb5+=t4VfnAH)0y1b>;C)`z$+s1KVKp(fOWWZ))e%*xUEJ1SxMGF17}#Q) zA}eMN(uK@*2;3&K>1wk%Zq(&2?Tw64gGJYMw69D92YwPRT0DD;vY5?=Q0ug=WPJ)f zHXFN`UlVWQgP}0U>+{K1-jvkmi$@@UOzEMCRV})?@g2;lz|0xu!TB-fPCR@0220hu z*HJksHCE)aV1di)mdl9Fyb%wvCCMiIm=1MPe&o+t6&UK}sT=ipEYCpCif37Pa3x^8i!~A0HwKl^$_nNJJ5s#pk!QZbXu3zKtJbkY|atxnnw#JJ5)%P1Hv9YZL}f0$r#eRwcTs~*2&b|nd2+) zTEOn?5xI@X%LB$6Z{_bOcMh8fDu_5IFftt=eOnL?(w1vGMfaicTW#_ly?WNrFJ9fs z#%qxZscr>two9Qmd5d$`Jq1}_ZL3z^eizmz@$7P+1tu&GUG>M52KL0@FSg{N1b!ol zc>xKp2#<6`ph%(ZEFe|t+H~&MmH**Rx%_;t2u`_-W=}$cbkMj|*HvRtmB|v@D1Hiu z*<`N%DL%jRGOxGVKw`oP-%;Ryd}s1=L^yog84r99{j3pK&~i=m+fAGK;;CbGvEVgT zR3+(6!JzgvS;AXVTYLWW!?EYa6>BGk^+4#}GeC4W&3z zz)s6vdzy)ElWBR3=tg_0iKmF1XQ(&x&qJc>5&>uxct5s4aVbWH=kb%szl0IE6Fy=@ z^OrEvLO@{rr}omyjf>64(P2mbUHIn1`w!t8?7(yQCksa2I)obuoX`cV-md` z79!iDbdI!0)1zgw^sKQP=5p?~P+J-@nL49ch1f8j^gL3_!6iqdULq|9b-uG4Hx*9c zCySOEIK&e$=^2!-9K`3a+;-$SA6~5)SzxtS@N-dca0ThN4S{bqVfJK^JEv4l@lt>j zs_;R|(-zIP;|-Bg!jCE~^3fsH8B2_q=+_-+U4rVv0%p+$kR>xLX7b6RB;A;5+6Z>k zLA;WqD!w1}soB&Y>>SzZRU}Mm4w7!;3)sbQVXYz^k(Zks|CD8D(N+madq)Ix6#;z} zxr>{ukw-oxh@>>o)YMD)u@HTnP)zI>gJsdssW#j@`&@9R0U##eu7pH4`&nx=g0laF zX|xYvPbDqmih#WApPv0~qx(26L1R3^Z52926>;Mff`{5~!%W>g3^szas)*P%8M?Q; zk5fk?+p9)L3Dn2{9rHHul6lV^=3;Z_^SB;6|pILBf5^z$h+divw%QL ziPJ*S{Bg_h_XOfg1a??U1(2)_Q{bS{@FfTz&gf?fwgg*JgK;}Ck6a6mY(b2gz!KAI zD#U$O(5%~YzT+5q=6c9mS?v#U5$C5ecrk?Vr==T%$%BMgs?X2RQJ&QFn5;N>t^;P< zQ{6v_@Sbz-#t$;2(KQifSmxUnG&hzxWkNJWzdnuVe>x-PRO}l-vjkSRa57rR)WC2+ z?HLNW6UUw9pNmcF_oC;h_ttoL1(S|#1gi{XGiF~(dT#(C2QrCCka{RC3=9r^yT@`2 zI1NU)wmzww-8}ni#UH&ZRJzroF(*@{Gbul>Of#0wPlBw&=Oq<c(4jO`$Q zv>MeJKbEOW*Br+|sRBpufP+5|0Njg4^-dmvZrKAYkEX8`H-sHj8wz)Nk5j zgY@i+SN8Dx5mc|lDv!T7!6!JnMz(vT$uiu(*;Kx@E!-l3{j`sfm{k)C4%VvMxfI-G zom*z@#~ZpsjMcQz90*&+^G|sT2#99%ll!5`4#Q*R&}UPVV=Es%8CmOf9sNq-7y#wr zYjGrhT|ykW2?0tO)uI@N77&}-g<$ijc=ThT5U4+&E*ntMw(Bmx8vQT*W+ZYV%3)H%%^7@^Lq?cXh zxx8Fxi_~9#RgX#J9;ws;-tL(+i>#1q;YVUg z)nv?ATN;6QdsKiw#M_#$#JxVk!wrl~)t}-T%LYlyIF+t2SJ2d;e}knXODtmL5IAnQ zzi;IC?|>{7j}fyk70U~)cy904Y8Ox=SIvI#Bei*oJKPE&FBeFXMUl~`bRa&3b-A={K1Qr;SD$W3s9CdN5)=kV3rq!eG#|GP1Sw*PW zdoFZ8i837`nk_E0SGF47Hkp`OLi<5+0GXm{Y_LSgrh%h1UY9ZR*x^ zf3wrIW>PDY56|g;N<$~F5nD1D!%KiXXPyU+_D>>9K3mAeI7e(Yw4dFM&(&JI+{T+a zS_FnrceUk*ygGid3CNrJxm%I`$f`EdT=n*)w5rQ{vwoUn_D&3*?7Fe($0u<|T&b)t zA(>UBRmezOKTqxKpV>Mi(6nl=Dup^}jT3^N9_g$em;5J{?*nZ0=WE3E7J8SjHxI6r zPd)EnyKM3jEIVq%ZX1bNTTXu{X59ZUtl;*3bM*-mT+qyDnnvH(`#die`{TJ8d-E4{XB ziO(tx?)f*_+$5C*3XJ z+S}Xgj_W;cO=kz=S?`7Gb>{|Vpcv3z*l544v8gqeTv_;Sw^#h_^0n4Md7-=33y$u{ zOW(12zrET%{fkWdVy0sAxq}4&qIq7s?cLJWT-MmW&aX>s$lJy|FQe}<2zx6cD4toj zaQ&kKM31ejUFQmJTCUdBUpfW<&X}3}wrR|LUY7iOypzk5f2_r&1cuyU^$ZwOl2fTI z4nXZ%wcOdP!MjvTn`wc*G_G3-^?WQEuwOaGC9>f!aio;c78KN*-H(3sjv(6)f_KkL z7s!C}+(K0a<$h@<%wH!=B)&d%A=Col$Y4io1uUUqx zxEkHGwao~xiq)4j+pg`U`T%*nDj2}{AkHley(iLV zT2@{My*O`3;jK6UCvMG4hXXW4n|Hnb?GFCkb|qeSwLI%xjw$%0H{B&e%&6JLxdeNk zOzF@@tD1})g|Fv~IzN+(s@4n%+;d-Ao`0|YetmA7yXec5GbW-=^7Zu5j+f6#?Yk!| zVc@S=X9u*L+uD`(ty|#iF>V9}w(AJ25a!NoD;0mUXo}8i9S`A-t@iU`>Em_hDN?OK z?S`|@g)Uk~Ti&DY#_MSvXXf-O+3$-KI${J+nvA=qfX!;DP$braT?+1Ul`Dq?q>MZC zN0w|+6xUZ<4>YU0l)csFJ#+$;s>oHz+f_O6vBT}?L};d!_(HashG5H?>qFrd|;< ziP3|spZ3xPCTe4Qo@SSlNS)TVPmPX=9Pf)4{w}t}G)vjkwV**MQ-mYrI9L&OpTS>y zfSNpKuyhL;9@rgu7jiBxhRr3pu!rUb5qvP#qQJKE& zlm0{4mYacumroK0{rha%AW90+%=**rKwffPRWI<P7Gc6-uKwrK$vM$L>8qM}g>0Oj zuD$NTqB2mp?Le+_HBf67yOYoGc#O7wsfPXncm#l1QUqDS4UQrb9O$rK7dm1!kmimO zpPo&4sZx_LjgQ}57gfEQkF)@V+nr@^Haxh==1VDeq%Q#av#X%Otew-KA6bon(|G); z&Iw?ki&XZ+rXY*3z%v?%%;d%;ifkb3&(Xa$@PxPRv~c*CHh&l1 zSJG z$65&CVMh~dax?OZnp$pCc@l?F5_�@EBK2N2z6PyE%@4TW4xG)%(gkoG&d9KX?@ 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 Z^fxfc;CC>X$=rwl6c~8OPdOg!{{VyC8(sha diff --git a/Samples/Graphics/Antialiasing/StepTimer.h b/Samples/Graphics/Antialiasing/StepTimer.h index bb15fff..2dbefcb 100644 --- a/Samples/Graphics/Antialiasing/StepTimer.h +++ b/Samples/Graphics/Antialiasing/StepTimer.h @@ -111,7 +111,7 @@ namespace DX timeDelta *= TicksPerSecond; timeDelta /= static_cast(m_qpcFrequency.QuadPart); - uint32_t lastFrameCount = m_frameCount; + const uint32_t lastFrameCount = m_frameCount; if (m_isFixedTimeStep) { diff --git a/Samples/Graphics/Antialiasing/pch.h b/Samples/Graphics/Antialiasing/pch.h index 13bfd59..7b9841f 100644 --- a/Samples/Graphics/Antialiasing/pch.h +++ b/Samples/Graphics/Antialiasing/pch.h @@ -37,8 +37,8 @@ #include -#if _GXDK_VER < 0x4A610D2B /* GXDK Edition 200600 */ -#error This sample requires the June 2020 GDK or later +#if _GXDK_VER < 0x55F007B0 /* GDK Edition 211000 */ +#error This sample requires the October 2021 GDK or later #endif #ifdef _GAMING_XBOX_SCARLETT @@ -56,15 +56,23 @@ #include #include +#include #include +#include #include +#include +#include +#include #include +#include #include #include +#include +#include +#include -#include -#include #include + #include #include diff --git a/Samples/Graphics/ComputeParticles/ComputeParticles.cpp b/Samples/Graphics/ComputeParticles/ComputeParticles.cpp index 1290d33..3cc2de9 100644 --- a/Samples/Graphics/ComputeParticles/ComputeParticles.cpp +++ b/Samples/Graphics/ComputeParticles/ComputeParticles.cpp @@ -10,7 +10,7 @@ #include "ATGColors.h" -extern void ExitSample(); +extern void ExitSample() noexcept; using namespace DirectX; using namespace DirectX::SimpleMath; @@ -63,7 +63,7 @@ void Sample::Initialize(HWND window) m_deviceResources->SetWindow(window); - m_deviceResources->CreateDeviceResources(); + m_deviceResources->CreateDeviceResources(); CreateDeviceDependentResources(); m_deviceResources->CreateWindowSizeDependentResources(); @@ -76,6 +76,8 @@ void Sample::Tick() { PIXBeginEvent(PIX_COLOR_DEFAULT, L"Frame %llu", m_frame); + m_deviceResources->WaitForOrigin(); + m_timer.Tick([&]() { Update(m_timer); @@ -250,16 +252,16 @@ void Sample::Clear() PIXBeginEvent(commandList, PIX_COLOR_DEFAULT, L"Clear"); // Clear the views. - auto rtvDescriptor = m_deviceResources->GetRenderTargetView(); - auto dsvDescriptor = m_deviceResources->GetDepthStencilView(); + auto const rtvDescriptor = m_deviceResources->GetRenderTargetView(); + auto const dsvDescriptor = m_deviceResources->GetDepthStencilView(); commandList->OMSetRenderTargets(1, &rtvDescriptor, FALSE, &dsvDescriptor); commandList->ClearRenderTargetView(rtvDescriptor, ATG::Colors::Background, 0, nullptr); commandList->ClearDepthStencilView(dsvDescriptor, D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr); // Set the viewport and scissor rect. - auto viewport = m_deviceResources->GetScreenViewport(); - auto scissorRect = m_deviceResources->GetScissorRect(); + auto const viewport = m_deviceResources->GetScreenViewport(); + auto const scissorRect = m_deviceResources->GetScissorRect(); commandList->RSSetViewports(1, &viewport); commandList->RSSetScissorRects(1, &scissorRect); @@ -292,8 +294,6 @@ void Sample::CreateDeviceDependentResources() m_commonStates = std::make_unique(device); m_srvPile = std::make_unique(device, - D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, - D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, 128, SRV_Count); @@ -329,8 +329,8 @@ void Sample::CreateDeviceDependentResources() m_particles.GenerateGeometry(*m_models[0]); // HUD - auto backBufferRts = RenderTargetState(m_deviceResources->GetBackBufferFormat(), m_deviceResources->GetDepthBufferFormat()); - auto spritePSD = SpriteBatchPipelineStateDescription(backBufferRts, &CommonStates::AlphaBlend); + const RenderTargetState backBufferRts(m_deviceResources->GetBackBufferFormat(), m_deviceResources->GetDepthBufferFormat()); + const SpriteBatchPipelineStateDescription spritePSD(backBufferRts, &CommonStates::AlphaBlend); m_hudBatch = std::make_unique(device, resourceUpload, spritePSD); auto finished = resourceUpload.End(m_deviceResources->GetCommandQueue()); @@ -339,7 +339,7 @@ void Sample::CreateDeviceDependentResources() // Instantiate objects from basic scene definition. auto effectFactory = EffectFactory(m_srvPile->Heap(), m_commonStates->Heap()); - auto objectRTState = RenderTargetState(m_deviceResources->GetBackBufferFormat(), m_deviceResources->GetDepthBufferFormat()); + const RenderTargetState objectRTState(m_deviceResources->GetBackBufferFormat(), m_deviceResources->GetDepthBufferFormat()); auto objectPSD = EffectPipelineStateDescription( nullptr, CommonStates::Opaque, diff --git a/Samples/Graphics/ComputeParticles/ComputeParticles.h b/Samples/Graphics/ComputeParticles/ComputeParticles.h index 37aa4a7..38f67e6 100644 --- a/Samples/Graphics/ComputeParticles/ComputeParticles.h +++ b/Samples/Graphics/ComputeParticles/ComputeParticles.h @@ -21,6 +21,12 @@ public: Sample() noexcept(false); ~Sample(); + Sample(Sample&&) = default; + Sample& operator= (Sample&&) = default; + + Sample(Sample const&) = delete; + Sample& operator= (Sample const&) = delete; + // Initialization and management void Initialize(HWND window); @@ -30,9 +36,11 @@ public: // Messages void OnSuspending(); void OnResuming(); + void OnConstrained() {} + void OnUnConstrained() {} // Properties - bool RequestHDRMode() const { return m_deviceResources ? (m_deviceResources->GetDeviceOptions() & DX::DeviceResources::c_EnableHDR) != 0 : false; } + bool RequestHDRMode() const noexcept { return m_deviceResources ? (m_deviceResources->GetDeviceOptions() & DX::DeviceResources::c_EnableHDR) != 0 : false; } private: void Update(DX::StepTimer const& timer); diff --git a/Samples/Graphics/ComputeParticles/DeviceResources.cpp b/Samples/Graphics/ComputeParticles/DeviceResources.cpp index d76fd63..fa67816 100644 --- a/Samples/Graphics/ComputeParticles/DeviceResources.cpp +++ b/Samples/Graphics/ComputeParticles/DeviceResources.cpp @@ -31,14 +31,13 @@ DeviceResources::DeviceResources( m_rtvDescriptorSize(0), m_screenViewport{}, m_scissorRect{}, - m_backBufferFormat((flags & c_EnableHDR) ? DXGI_FORMAT_R10G10B10A2_UNORM : backBufferFormat), + m_backBufferFormat(backBufferFormat), m_depthBufferFormat(depthBufferFormat), m_backBufferCount(backBufferCount), m_window(nullptr), m_d3dFeatureLevel(D3D_FEATURE_LEVEL_12_0), m_outputSize{0, 0, 1920, 1080}, - m_options(flags), - m_gameDVRFormat((flags & c_EnableHDR) ? backBufferFormat : DXGI_FORMAT_UNKNOWN) + m_options(flags) { if (backBufferCount < 2 || backBufferCount > MAX_BACK_BUFFER_COUNT) { @@ -55,12 +54,16 @@ DeviceResources::~DeviceResources() // Ensure we present a blank screen before cleaning up resources. if (m_commandQueue) { - (void)m_commandQueue->PresentX(0, nullptr, nullptr); + std::ignore = m_commandQueue->PresentX(0, nullptr, nullptr); } } // Configures the Direct3D device, and stores handles to it and the device context. +#ifdef _GAMING_XBOX_SCARLETT +void DeviceResources::CreateDeviceResources(D3D12XBOX_CREATE_DEVICE_FLAGS createDeviceFlags) +#else void DeviceResources::CreateDeviceResources() +#endif { // Create the DX12 API device object. D3D12XBOX_CREATE_DEVICE_PARAMETERS params = {}; @@ -77,6 +80,17 @@ void DeviceResources::CreateDeviceResources() params.GraphicsCommandQueueRingSizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); params.GraphicsScratchMemorySizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); params.ComputeScratchMemorySizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); +#ifdef _GAMING_XBOX_SCARLETT + params.CreateDeviceFlags = createDeviceFlags; + +#if (_GXDK_VER >= 0x585D070E /* GXDK Edition 221000 */) + if (m_options & c_AmplificationShaders) + { + params.AmplificationShaderIndirectArgsBufferSize = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); + params.AmplificationShaderPayloadBufferSize = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); + } +#endif +#endif HRESULT hr = D3D12XboxCreateDevice( nullptr, @@ -107,7 +121,7 @@ void DeviceResources::CreateDeviceResources() // Create descriptor heaps for render target views and depth stencil views. D3D12_DESCRIPTOR_HEAP_DESC rtvDescriptorHeapDesc = {}; - rtvDescriptorHeapDesc.NumDescriptors = (m_options & c_EnableHDR) ? (m_backBufferCount * 2) : m_backBufferCount; + rtvDescriptorHeapDesc.NumDescriptors = m_backBufferCount; rtvDescriptorHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; ThrowIfFailed(m_d3dDevice->CreateDescriptorHeap(&rtvDescriptorHeapDesc, IID_GRAPHICS_PPV_ARGS(m_rtvDescriptorHeap.ReleaseAndGetAddressOf()))); @@ -152,31 +166,46 @@ void DeviceResources::CreateDeviceResources() m_fenceEvent.Attach(CreateEventEx(nullptr, nullptr, 0, EVENT_MODIFY_STATE | SYNCHRONIZE)); if (!m_fenceEvent.IsValid()) { - throw std::exception("CreateEvent"); + throw std::system_error(std::error_code(static_cast(GetLastError()), std::system_category()), "CreateEventEx"); } - if (m_options & c_Enable4K_UHD) + if (m_options & (c_Enable4K_UHD | c_EnableQHD)) { switch (XSystemGetDeviceType()) { case XSystemDeviceType::XboxOne: case XSystemDeviceType::XboxOneS: - case XSystemDeviceType::XboxScarlettLockhart /* Xbox Series S */: - m_options &= ~c_Enable4K_UHD; -#ifdef _DEBUG - OutputDebugStringA("INFO: Swapchain using 1080p (1920 x 1080)\n"); -#endif + m_options &= ~(c_Enable4K_UHD | c_EnableQHD); break; + case XSystemDeviceType::XboxScarlettLockhart /* Xbox Series S */: + m_options &= ~c_Enable4K_UHD; + if (m_options & c_EnableQHD) + { + m_outputSize = { 0, 0, 2560, 1440 }; + } + break; + + case XSystemDeviceType::XboxScarlettAnaconda /* Xbox Series X */: + case XSystemDeviceType::XboxOneXDevkit: + case XSystemDeviceType::XboxScarlettDevkit: default: - m_outputSize = { 0, 0, 3840, 2160 }; -#ifdef _DEBUG - OutputDebugStringA("INFO: Swapchain using 4k (3840 x 2160)\n"); -#endif + m_outputSize = (m_options & c_Enable4K_UHD) ? RECT{ 0, 0, 3840, 2160 } : RECT{ 0, 0, 2560, 1440 }; break; } } +#ifdef _DEBUG + const char* info = nullptr; + switch (m_outputSize.bottom) + { + case 2160: info = "INFO: Swapchain using 4k (3840 x 2160)\n"; break; + case 1440: info = "INFO: Swapchain using 1440p (2560 x 1440)\n"; break; + default: info = "INFO: Swapchain using 1080p (1920 x 1080)\n"; break; + } + OutputDebugStringA(info); +#endif + RegisterFrameEvents(); } @@ -185,7 +214,7 @@ void DeviceResources::CreateWindowSizeDependentResources() { if (!m_window) { - throw std::exception("Call SetWindow with a valid window handle"); + throw std::logic_error("Call SetWindow with a valid Win32 window handle"); } // Wait until all previous GPU work is complete. @@ -198,7 +227,6 @@ void DeviceResources::CreateWindowSizeDependentResources() for (UINT n = 0; n < m_backBufferCount; n++) { m_renderTargets[n].Reset(); - m_renderTargetsGameDVR[n].Reset(); m_fenceValues[n] = m_fenceValues[m_backBufferIndex]; } @@ -208,7 +236,7 @@ void DeviceResources::CreateWindowSizeDependentResources() // Obtain the back buffers for this window which will be the final render targets // and create render target views for each of them. - CD3DX12_HEAP_PROPERTIES swapChainHeapProperties(D3D12_HEAP_TYPE_DEFAULT); + const CD3DX12_HEAP_PROPERTIES swapChainHeapProperties(D3D12_HEAP_TYPE_DEFAULT); D3D12_RESOURCE_DESC swapChainBufferDesc = CD3DX12_RESOURCE_DESC::Tex2D( m_backBufferFormat, @@ -219,6 +247,13 @@ void DeviceResources::CreateWindowSizeDependentResources() ); swapChainBufferDesc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; +#ifdef _GAMING_XBOX_XBOXONE + if (m_options & c_EnableHDR) + { + swapChainBufferDesc.Flags |= D3D12XBOX_RESOURCE_FLAG_ALLOW_AUTOMATIC_GAMEDVR_TONE_MAP; + } +#endif + D3D12_CLEAR_VALUE swapChainOptimizedClearValue = {}; swapChainOptimizedClearValue.Format = m_backBufferFormat; @@ -240,41 +275,12 @@ void DeviceResources::CreateWindowSizeDependentResources() rtvDesc.Format = m_backBufferFormat; rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; - CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( + const CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( m_rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), static_cast(n), m_rtvDescriptorSize); m_d3dDevice->CreateRenderTargetView(m_renderTargets[n].Get(), &rtvDesc, rtvDescriptor); } - if (m_options & c_EnableHDR) - { - swapChainBufferDesc.Format = swapChainOptimizedClearValue.Format = m_gameDVRFormat; - - for (UINT n = 0; n < m_backBufferCount; n++) - { - ThrowIfFailed(m_d3dDevice->CreateCommittedResource( - &swapChainHeapProperties, - D3D12_HEAP_FLAG_ALLOW_DISPLAY, - &swapChainBufferDesc, - D3D12_RESOURCE_STATE_PRESENT, - &swapChainOptimizedClearValue, - IID_GRAPHICS_PPV_ARGS(m_renderTargetsGameDVR[n].GetAddressOf()))); - - wchar_t name[25] = {}; - swprintf_s(name, L"GameDVR Render target %u", n); - m_renderTargetsGameDVR[n]->SetName(name); - - D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {}; - rtvDesc.Format = m_gameDVRFormat; - rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; - - CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptorGameDVR( - m_rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), - static_cast(m_backBufferCount + n), m_rtvDescriptorSize); - m_d3dDevice->CreateRenderTargetView(m_renderTargetsGameDVR[n].Get(), &rtvDesc, rtvDescriptorGameDVR); - } - } - // Reset the index to the current back buffer. m_backBufferIndex = 0; @@ -282,7 +288,7 @@ void DeviceResources::CreateWindowSizeDependentResources() { // Allocate a 2-D surface as the depth/stencil buffer and create a depth/stencil view // on this surface. - CD3DX12_HEAP_PROPERTIES depthHeapProperties(D3D12_HEAP_TYPE_DEFAULT); + const CD3DX12_HEAP_PROPERTIES depthHeapProperties(D3D12_HEAP_TYPE_DEFAULT); D3D12_RESOURCE_DESC depthStencilDesc = CD3DX12_RESOURCE_DESC::Tex2D( m_depthBufferFormat, @@ -295,7 +301,7 @@ void DeviceResources::CreateWindowSizeDependentResources() D3D12_CLEAR_VALUE depthOptimizedClearValue = {}; depthOptimizedClearValue.Format = m_depthBufferFormat; - depthOptimizedClearValue.DepthStencil.Depth = 1.0f; + depthOptimizedClearValue.DepthStencil.Depth = (m_options & c_ReverseDepth) ? 0.0f : 1.0f; depthOptimizedClearValue.DepthStencil.Stencil = 0; ThrowIfFailed(m_d3dDevice->CreateCommittedResource( @@ -331,10 +337,6 @@ void DeviceResources::CreateWindowSizeDependentResources() // Prepare the command list and render target for rendering. void DeviceResources::Prepare(D3D12_RESOURCE_STATES beforeState, D3D12_RESOURCE_STATES afterState) { - // Wait until frame start is signaled - m_framePipelineToken = D3D12XBOX_FRAME_PIPELINE_TOKEN_NULL; - ThrowIfFailed(m_d3dDevice->WaitFrameEventX(D3D12XBOX_FRAME_EVENT_ORIGIN, INFINITE, nullptr, D3D12XBOX_WAIT_FRAME_EVENT_FLAG_NONE, &m_framePipelineToken)); - // Reset command list and allocator. ThrowIfFailed(m_commandAllocators[m_backBufferIndex]->Reset()); ThrowIfFailed(m_commandList->Reset(m_commandAllocators[m_backBufferIndex].Get(), nullptr)); @@ -342,23 +344,10 @@ void DeviceResources::Prepare(D3D12_RESOURCE_STATES beforeState, D3D12_RESOURCE_ if (beforeState != afterState) { // Transition the render target into the correct state to allow for drawing into it. - if (m_options & c_EnableHDR) - { - D3D12_RESOURCE_BARRIER barriers[2] = - { - CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), - beforeState, afterState), - CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargetsGameDVR[m_backBufferIndex].Get(), - beforeState, afterState), - }; - m_commandList->ResourceBarrier(_countof(barriers), barriers); - } - else - { - D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), - beforeState, afterState); - m_commandList->ResourceBarrier(1, &barrier); - } + const D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition( + m_renderTargets[m_backBufferIndex].Get(), + beforeState, afterState); + m_commandList->ResourceBarrier(1, &barrier); } } @@ -368,20 +357,10 @@ void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState, _In_opt_ const if (beforeState != D3D12_RESOURCE_STATE_PRESENT) { // Transition the render target to the state that allows it to be presented to the display. - if (m_options & c_EnableHDR) - { - D3D12_RESOURCE_BARRIER barriers[2] = - { - CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), beforeState, D3D12_RESOURCE_STATE_PRESENT), - CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargetsGameDVR[m_backBufferIndex].Get(), beforeState, D3D12_RESOURCE_STATE_PRESENT), - }; - m_commandList->ResourceBarrier(_countof(barriers), barriers); - } - else - { - D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), beforeState, D3D12_RESOURCE_STATE_PRESENT); - m_commandList->ResourceBarrier(1, &barrier); - } + const D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition( + m_renderTargets[m_backBufferIndex].Get(), + beforeState, D3D12_RESOURCE_STATE_PRESENT); + m_commandList->ResourceBarrier(1, &barrier); } // Send the command list off to the GPU for processing. @@ -389,32 +368,24 @@ void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState, _In_opt_ const m_commandQueue->ExecuteCommandLists(1, CommandListCast(m_commandList.GetAddressOf())); // Present the backbuffer using the PresentX API. - D3D12XBOX_PRESENT_PLANE_PARAMETERS planeParameters[2] = {}; - planeParameters[0].Token = planeParameters[1].Token = m_framePipelineToken; - planeParameters[0].ResourceCount = planeParameters[1].ResourceCount = 1; - planeParameters[0].ppResources = m_renderTargets[m_backBufferIndex].GetAddressOf(); + D3D12XBOX_PRESENT_PLANE_PARAMETERS planeParameters = {}; + planeParameters.Token = m_framePipelineToken; + planeParameters.ResourceCount = 1; + planeParameters.ppResources = m_renderTargets[m_backBufferIndex].GetAddressOf(); if (m_options & c_EnableHDR) { - planeParameters[0].pSrcViewRects = planeParameters[1].pSrcViewRects = &m_outputSize; - planeParameters[0].ColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020; - planeParameters[1].ppResources = m_renderTargetsGameDVR[m_backBufferIndex].GetAddressOf(); - planeParameters[1].ColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; - - ThrowIfFailed( - m_commandQueue->PresentX(2, planeParameters, params) - ); - } - else - { - ThrowIfFailed( - m_commandQueue->PresentX(1, planeParameters, params) - ); + planeParameters.ColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020; } - // Xbox One apps do not need to handle DXGI_ERROR_DEVICE_REMOVED or DXGI_ERROR_DEVICE_RESET. + ThrowIfFailed( + m_commandQueue->PresentX(1, &planeParameters, params) + ); - MoveToNextFrame(); + // Xbox apps do not need to handle DXGI_ERROR_DEVICE_REMOVED or DXGI_ERROR_DEVICE_RESET. + + // Update the back buffer index. + m_backBufferIndex = (m_backBufferIndex + 1) % m_backBufferCount; } // Handle GPU suspend/resume @@ -436,13 +407,13 @@ void DeviceResources::WaitForGpu() noexcept if (m_commandQueue && m_fence && m_fenceEvent.IsValid()) { // Schedule a Signal command in the GPU queue. - UINT64 fenceValue = m_fenceValues[m_backBufferIndex]; + const UINT64 fenceValue = m_fenceValues[m_backBufferIndex]; if (SUCCEEDED(m_commandQueue->Signal(m_fence.Get(), fenceValue))) { // Wait until the Signal has been processed. if (SUCCEEDED(m_fence->SetEventOnCompletion(fenceValue, m_fenceEvent.Get()))) { - WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); + std::ignore = WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); // Increment the fence value for the current frame. m_fenceValues[m_backBufferIndex]++; @@ -451,25 +422,17 @@ void DeviceResources::WaitForGpu() noexcept } } -// Prepare to render the next frame. -void DeviceResources::MoveToNextFrame() +// For PresentX rendering, we should wait for the origin event just before processing input. +void DeviceResources::WaitForOrigin() { - // Schedule a Signal command in the queue. - const UINT64 currentFenceValue = m_fenceValues[m_backBufferIndex]; - ThrowIfFailed(m_commandQueue->Signal(m_fence.Get(), currentFenceValue)); - - // Update the back buffer index. - m_backBufferIndex = (m_backBufferIndex + 1) % m_backBufferCount; - - // If the next frame is not ready to be rendered yet, wait until it is ready. - if (m_fence->GetCompletedValue() < m_fenceValues[m_backBufferIndex]) - { - ThrowIfFailed(m_fence->SetEventOnCompletion(m_fenceValues[m_backBufferIndex], m_fenceEvent.Get())); - WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); - } - - // Set the fence value for the next frame. - m_fenceValues[m_backBufferIndex] = currentFenceValue + 1; + // Wait until frame start is signaled + m_framePipelineToken = D3D12XBOX_FRAME_PIPELINE_TOKEN_NULL; + ThrowIfFailed(m_d3dDevice->WaitFrameEventX( + D3D12XBOX_FRAME_EVENT_ORIGIN, + INFINITE, + nullptr, + D3D12XBOX_WAIT_FRAME_EVENT_FLAG_NONE, + &m_framePipelineToken)); } // Set frame interval and register for frame events diff --git a/Samples/Graphics/ComputeParticles/DeviceResources.h b/Samples/Graphics/ComputeParticles/DeviceResources.h index 1d26b17..4924894 100644 --- a/Samples/Graphics/ComputeParticles/DeviceResources.h +++ b/Samples/Graphics/ComputeParticles/DeviceResources.h @@ -10,8 +10,11 @@ namespace DX class DeviceResources { public: - static constexpr unsigned int c_Enable4K_UHD = 0x1; - static constexpr unsigned int c_EnableHDR = 0x2; + static constexpr unsigned int c_Enable4K_UHD = 0x1; + static constexpr unsigned int c_EnableQHD = 0x2; + static constexpr unsigned int c_EnableHDR = 0x4; + static constexpr unsigned int c_ReverseDepth = 0x8; + static constexpr unsigned int c_AmplificationShaders = 0x10; DeviceResources(DXGI_FORMAT backBufferFormat = DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT depthBufferFormat = DXGI_FORMAT_D32_FLOAT, @@ -25,7 +28,11 @@ namespace DX DeviceResources(DeviceResources const&) = delete; DeviceResources& operator= (DeviceResources const&) = delete; +#ifdef _GAMING_XBOX_SCARLETT + void CreateDeviceResources(D3D12XBOX_CREATE_DEVICE_FLAGS createDeviceFlags = D3D12XBOX_CREATE_DEVICE_FLAG_NONE); +#else void CreateDeviceResources(); +#endif void CreateWindowSizeDependentResources(); void SetWindow(HWND window) noexcept { m_window = window; } void Prepare(D3D12_RESOURCE_STATES beforeState = D3D12_RESOURCE_STATE_PRESENT, @@ -35,6 +42,7 @@ namespace DX void Suspend(); void Resume(); void WaitForGpu() noexcept; + void WaitForOrigin(); // Device Accessors. RECT GetOutputSize() const noexcept { return m_outputSize; } @@ -67,19 +75,7 @@ namespace DX return CD3DX12_CPU_DESCRIPTOR_HANDLE(m_dsvDescriptorHeap->GetCPUDescriptorHandleForHeapStart()); } - // Direct3D HDR Game DVR support for Xbox One. - ID3D12Resource* GetGameDVRRenderTarget() const noexcept { return m_renderTargetsGameDVR[m_backBufferIndex].Get(); } - DXGI_FORMAT GetGameDVRFormat() const noexcept { return m_gameDVRFormat; } - - CD3DX12_CPU_DESCRIPTOR_HANDLE GetGameDVRRenderTargetView() const noexcept - { - return CD3DX12_CPU_DESCRIPTOR_HANDLE( - m_rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), - static_cast(m_backBufferCount + m_backBufferIndex), m_rtvDescriptorSize); - } - private: - void MoveToNextFrame(); void RegisterFrameEvents(); static constexpr size_t MAX_BACK_BUFFER_COUNT = 3; @@ -126,9 +122,5 @@ namespace DX // DeviceResources options (see flags above) unsigned int m_options; - - // Direct3D HDR Game DVR support for Xbox One. - Microsoft::WRL::ComPtr m_renderTargetsGameDVR[MAX_BACK_BUFFER_COUNT]; - DXGI_FORMAT m_gameDVRFormat; }; } diff --git a/Samples/Graphics/ComputeParticles/Main.cpp b/Samples/Graphics/ComputeParticles/Main.cpp index 6f6c0c2..1fdf8c0 100644 --- a/Samples/Graphics/ComputeParticles/Main.cpp +++ b/Samples/Graphics/ComputeParticles/Main.cpp @@ -1,7 +1,7 @@ //-------------------------------------------------------------------------------------- // Main.cpp // -// Entry point for Microsoft GDK with Xbox extensions. +// Entry point for Microsoft GDK with Xbox extensions // // Advanced Technology Group (ATG) // Copyright (C) Microsoft Corporation. All rights reserved. @@ -18,16 +18,25 @@ using namespace DirectX; +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wcovered-switch-default" +#pragma clang diagnostic ignored "-Wswitch-enum" +#endif + +#pragma warning(disable : 4061) + namespace { std::unique_ptr g_sample; HANDLE g_plmSuspendComplete = nullptr; HANDLE g_plmSignalResume = nullptr; -}; +} bool g_HDRMode = false; LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); +void SetDisplayMode() noexcept; +void ExitSample() noexcept; // Entry point int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) @@ -58,6 +67,8 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp // Register class and create window PAPPSTATE_REGISTRATION hPLM = {}; + PAPPCONSTRAIN_REGISTRATION hPLM2 = {}; + { // Register class WNDCLASSEXA wcex = {}; @@ -66,7 +77,7 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp wcex.lpfnWndProc = WndProc; wcex.hInstance = hInstance; wcex.lpszClassName = u8"ComputeParticlesWindowClass"; - wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); + wcex.hbrBackground = reinterpret_cast(COLOR_WINDOW + 1); if (!RegisterClassExA(&wcex)) return 1; @@ -79,17 +90,7 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp ShowWindow(hwnd, nCmdShow); - if (g_sample->RequestHDRMode()) - { - // Request HDR mode. - auto result = XDisplayTryEnableHdrMode(XDisplayHdrModePreference::PreferHdr, nullptr); - - g_HDRMode = (result == XDisplayHdrModeResult::Enabled); - - #ifdef _DEBUG - OutputDebugStringA((g_HDRMode) ? "INFO: Display in HDR Mode\n" : "INFO: Display in SDR Mode\n"); - #endif - } + SetDisplayMode(); SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast(g_sample.get())); @@ -116,7 +117,7 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp PostMessage(reinterpret_cast(context), WM_USER, 0, 0); // To defer suspend, you must wait to exit this callback - (void)WaitForSingleObject(g_plmSuspendComplete, INFINITE); + std::ignore = WaitForSingleObject(g_plmSuspendComplete, INFINITE); } else { @@ -124,6 +125,13 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp } }, hwnd, &hPLM)) return 1; + + if (RegisterAppConstrainedChangeNotification([](BOOLEAN constrained, PVOID context) + { + // To ensure we use the main UI thread to process the notification, we self-post a message + SendMessage(reinterpret_cast(context), WM_USER + 1, (constrained) ? 1u : 0u, 0); + }, hwnd, &hPLM2)) + return 1; } // Main message loop @@ -144,13 +152,14 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp g_sample.reset(); UnregisterAppStateChangeNotification(hPLM); - + UnregisterAppConstrainedChangeNotification(hPLM2); + CloseHandle(g_plmSuspendComplete); CloseHandle(g_plmSignalResume); XGameRuntimeUninitialize(); - return (int) msg.wParam; + return static_cast(msg.wParam); } // Windows procedure @@ -168,18 +177,52 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) // Complete deferral SetEvent(g_plmSuspendComplete); - (void)WaitForSingleObject(g_plmSignalResume, INFINITE); + std::ignore = WaitForSingleObject(g_plmSignalResume, INFINITE); + + SetDisplayMode(); sample->OnResuming(); } break; + + case WM_USER + 1: + if (sample) + { + if (wParam) + { + sample->OnConstrained(); + } + else + { + SetDisplayMode(); + + sample->OnUnConstrained(); + } + } + break; } return DefWindowProc(hWnd, message, wParam, lParam); } +// HDR helper +void SetDisplayMode() noexcept +{ + if (g_sample && g_sample->RequestHDRMode()) + { + // Request HDR mode. + auto result = XDisplayTryEnableHdrMode(XDisplayHdrModePreference::PreferHdr, nullptr); + + g_HDRMode = (result == XDisplayHdrModeResult::Enabled); + +#ifdef _DEBUG + OutputDebugStringA((g_HDRMode) ? "INFO: Display in HDR Mode\n" : "INFO: Display in SDR Mode\n"); +#endif + } +} + // Exit helper -void ExitSample() +void ExitSample() noexcept { PostQuitMessage(0); } diff --git a/Samples/Graphics/ComputeParticles/StepTimer.h b/Samples/Graphics/ComputeParticles/StepTimer.h index bb15fff..2dbefcb 100644 --- a/Samples/Graphics/ComputeParticles/StepTimer.h +++ b/Samples/Graphics/ComputeParticles/StepTimer.h @@ -111,7 +111,7 @@ namespace DX timeDelta *= TicksPerSecond; timeDelta /= static_cast(m_qpcFrequency.QuadPart); - uint32_t lastFrameCount = m_frameCount; + const uint32_t lastFrameCount = m_frameCount; if (m_isFixedTimeStep) { diff --git a/Samples/Graphics/ComputeParticles/pch.h b/Samples/Graphics/ComputeParticles/pch.h index 02f8a02..1dfc644 100644 --- a/Samples/Graphics/ComputeParticles/pch.h +++ b/Samples/Graphics/ComputeParticles/pch.h @@ -37,8 +37,8 @@ #include -#if _GXDK_VER < 0x4A610D2B /* GXDK Edition 200600 */ -#error This sample requires the June 2020 GDK or later +#if _GXDK_VER < 0x55F007B0 /* GDK Edition 211000 */ +#error This sample requires the October 2021 GDK or later #endif #ifdef _GAMING_XBOX_SCARLETT @@ -56,14 +56,21 @@ #include #include +#include #include +#include #include +#include +#include +#include #include +#include #include #include +#include +#include +#include -#include -#include #include #include @@ -93,7 +100,7 @@ namespace DX class com_exception : public std::exception { public: - com_exception(HRESULT hr) : result(hr) {} + com_exception(HRESULT hr) noexcept : result(hr) {} const char* what() const override { diff --git a/Samples/Graphics/ComputeParticles/readme.docx b/Samples/Graphics/ComputeParticles/readme.docx index 3d56565e8571d9f1ff855172d366f4c67c546a43..c1384221b756254e911ec73bf855dab2fa88fb22 100644 GIT binary patch delta 31813 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

vfI-ZX+oK@{8_n#?NXXXx1us}iQ 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 yn2Eiyf|I?2Gn1i%0|*rcfB_

iw&T!X&D0z@G+Za`tM04it>$j|%_*#85&#jr~N delta 31242 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, DXGI_FORMAT_UNKNOWN, 2); + m_deviceResources = std::make_unique(DXGI_FORMAT_R10G10B10A2_UNORM, DXGI_FORMAT_UNKNOWN); m_deviceResources->RegisterDeviceNotify(this); } @@ -50,7 +50,7 @@ void Sample::Initialize(HWND window, int width, int height) m_deviceResources->SetWindow(window, width, height); - m_deviceResources->CreateDeviceResources(); + m_deviceResources->CreateDeviceResources(); CreateDeviceDependentResources(); m_deviceResources->CreateWindowSizeDependentResources(); @@ -63,6 +63,10 @@ void Sample::Tick() { PIXBeginEvent(PIX_COLOR_DEFAULT, L"Frame %llu", m_frame); +#ifdef _GAMING_XBOX + m_deviceResources->WaitForOrigin(); +#endif + m_timer.Tick([&]() { Update(m_timer); @@ -86,7 +90,7 @@ void Sample::Update(DX::StepTimer const& timer) m_keyboardButtons.Update(kb); - const float SPEED_OF_CHANGE = 0.25f; + constexpr float SPEED_OF_CHANGE = 0.25f; if (pad.IsConnected()) { @@ -223,8 +227,8 @@ void Sample::RenderHUD(ID3D12GraphicsCommandList* commandList) commandList->RSSetViewports(1, &viewport); commandList->RSSetScissorRects(1, &scissorRect); - auto size = m_deviceResources->GetOutputSize(); - auto safe = SimpleMath::Viewport::ComputeTitleSafeArea((UINT)size.right, (UINT)size.bottom); + auto const size = m_deviceResources->GetOutputSize(); + auto const safe = SimpleMath::Viewport::ComputeTitleSafeArea((UINT)size.right, (UINT)size.bottom); wchar_t textBuffer[128] = {}; XMFLOAT2 textPos = XMFLOAT2(float(safe.left), float(safe.top)); @@ -265,14 +269,6 @@ void Sample::RenderHUD(ID3D12GraphicsCommandList* commandList) #pragma region Message Handlers // Message handlers -void Sample::OnActivated() -{ -} - -void Sample::OnDeactivated() -{ -} - void Sample::OnSuspending() { m_deviceResources->Suspend(); @@ -288,7 +284,7 @@ void Sample::OnResuming() void Sample::OnWindowMoved() { - auto r = m_deviceResources->GetOutputSize(); + auto const r = m_deviceResources->GetOutputSize(); m_deviceResources->WindowSizeChanged(r.right, r.bottom); } @@ -314,6 +310,11 @@ void Sample::CreateRaytracingPipeline() { auto device = m_deviceResources->GetD3DDevice(); +#if defined(_GAMING_XBOX_SCARLETT) && (_GRDK_VER >= 0x55F00C58 /* GDK Edition 220300 */) + // Save the RtPso PDB on the scratch drive + device->SetCompileTimeShaderPdbPathX(L"D:\\"); +#endif + CD3DX12_STATE_OBJECT_DESC raytracingPipeline{ D3D12_STATE_OBJECT_TYPE_RAYTRACING_PIPELINE }; auto raytracingLibrary = raytracingPipeline.CreateSubobject(); @@ -387,10 +388,10 @@ void Sample::BuildBottomLevelAccelerationStructure(bool buildEveryFrame) auto device = m_deviceResources->GetD3DDevice(); auto commandList = m_deviceResources->GetCommandList(); - const UINT vertexCount = 3; - const UINT vertexSize = sizeof(XMFLOAT3); - const UINT indexCount = 3; - const UINT indexSize = sizeof(UINT); + constexpr UINT vertexCount = 3; + constexpr UINT vertexSize = sizeof(XMFLOAT3); + constexpr UINT indexCount = 3; + constexpr UINT indexSize = sizeof(UINT); // Build a BLAS for one triangle... D3D12_RAYTRACING_GEOMETRY_DESC geometryDesc = { D3D12_RAYTRACING_GEOMETRY_TYPE_TRIANGLES, D3D12_RAYTRACING_GEOMETRY_FLAG_NONE, {} }; @@ -556,8 +557,8 @@ void Sample::CreateDeviceDependentResources() resourceUpload.Begin(); // HUD - auto backBufferRts = RenderTargetState(m_deviceResources->GetBackBufferFormat(), m_deviceResources->GetDepthBufferFormat()); - auto spritePSD = SpriteBatchPipelineStateDescription(backBufferRts, &CommonStates::AlphaBlend); + const RenderTargetState backBufferRts(m_deviceResources->GetBackBufferFormat(), m_deviceResources->GetDepthBufferFormat()); + const SpriteBatchPipelineStateDescription spritePSD(backBufferRts, &CommonStates::AlphaBlend); m_hudBatch = std::make_unique(device, resourceUpload, spritePSD); wchar_t strFilePath[MAX_PATH] = {}; @@ -583,7 +584,7 @@ void Sample::CreateDeviceDependentResources() void Sample::CreateWindowSizeDependentResources() { auto device = m_deviceResources->GetD3DDevice(); - RECT outputSize = m_deviceResources->GetOutputSize(); + auto const outputSize = m_deviceResources->GetOutputSize(); D3D12_HEAP_PROPERTIES defaultHeapProps = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT); diff --git a/Samples/Graphics/DXRTriangle/DXRTriangle/DXRTriangle.h b/Samples/Graphics/DXRTriangle/DXRTriangle/DXRTriangle.h index e2967d3..81d0dd5 100644 --- a/Samples/Graphics/DXRTriangle/DXRTriangle/DXRTriangle.h +++ b/Samples/Graphics/DXRTriangle/DXRTriangle/DXRTriangle.h @@ -13,10 +13,7 @@ struct SimpleTriangleRecord : public ShaderRecord { - SimpleTriangleRecord() : ShaderRecord() - { - - } + SimpleTriangleRecord() = default; SimpleTriangleRecord(ID3D12StateObjectProperties* props, LPCWSTR exportName) { @@ -64,8 +61,8 @@ public: void OnDeviceRestored() override; // Messages - void OnActivated(); - void OnDeactivated(); + void OnActivated() {} + void OnDeactivated() {} void OnSuspending(); void OnResuming(); void OnWindowMoved(); diff --git a/Samples/Graphics/DXRTriangle/DXRTriangle/DXRTriangle.vcxproj b/Samples/Graphics/DXRTriangle/DXRTriangle/DXRTriangle.vcxproj index 471425b..d5d4520 100644 --- a/Samples/Graphics/DXRTriangle/DXRTriangle/DXRTriangle.vcxproj +++ b/Samples/Graphics/DXRTriangle/DXRTriangle/DXRTriangle.vcxproj @@ -438,6 +438,9 @@ + /Qembed_debug %(AdditionalOptions) + /Qembed_debug %(AdditionalOptions) + /Qembed_debug %(AdditionalOptions) diff --git a/Samples/Graphics/DXRTriangle/DXRTriangle/DeviceResources.cpp b/Samples/Graphics/DXRTriangle/DXRTriangle/DeviceResources.cpp index ef5bdf1..7df6750 100644 --- a/Samples/Graphics/DXRTriangle/DXRTriangle/DeviceResources.cpp +++ b/Samples/Graphics/DXRTriangle/DXRTriangle/DeviceResources.cpp @@ -1,7 +1,7 @@ // // DeviceResources.cpp - A wrapper for the Direct3D 12/12.X device and swapchain // -// Modified to use ID3D12Device6/ID3D12GraphicsCommandList5 for DXR access +// Modified to use ID3D12Device8/ID3D12GraphicsCommandList5 for DXR access // #include "pch.h" @@ -12,7 +12,6 @@ using namespace DX; using Microsoft::WRL::ComPtr; -#ifdef _GAMING_DESKTOP #ifdef __clang__ #pragma clang diagnostic ignored "-Wcovered-switch-default" #pragma clang diagnostic ignored "-Wswitch-enum" @@ -20,6 +19,7 @@ using Microsoft::WRL::ComPtr; #pragma warning(disable : 4061) +#ifdef _GAMING_DESKTOP namespace { inline DXGI_FORMAT NoSRGB(DXGI_FORMAT fmt) noexcept @@ -39,7 +39,8 @@ namespace DeviceResources::DeviceResources( DXGI_FORMAT backBufferFormat, DXGI_FORMAT depthBufferFormat, - UINT backBufferCount) noexcept(false) : + UINT backBufferCount, + unsigned int flags) noexcept(false) : m_backBufferIndex(0), m_fenceValues{}, #ifdef _GAMING_XBOX @@ -54,6 +55,7 @@ DeviceResources::DeviceResources( m_window(nullptr), m_d3dFeatureLevel(D3D_FEATURE_LEVEL_11_0), m_outputSize{ 0, 0, 1, 1 }, + m_options(flags), m_deviceNotify(nullptr) { if (backBufferCount < 2 || backBufferCount > MAX_BACK_BUFFER_COUNT) @@ -72,7 +74,7 @@ DeviceResources::~DeviceResources() // Ensure we present a blank screen before cleaning up resources. if (m_commandQueue) { - (void)m_commandQueue->PresentX(0, nullptr, nullptr); + std::ignore = m_commandQueue->PresentX(0, nullptr, nullptr); } #endif } @@ -98,6 +100,14 @@ void DeviceResources::CreateDeviceResources() params.GraphicsScratchMemorySizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); params.ComputeScratchMemorySizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); +#if defined(_GAMING_XBOX_SCARLETT) && (_GRDK_VER >= 0x585D070E /* GXDK Edition 221000 */) + if (m_options & c_AmplificationShaders) + { + params.AmplificationShaderIndirectArgsBufferSize = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); + params.AmplificationShaderPayloadBufferSize = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); + } +#endif + HRESULT hr = D3D12XboxCreateDevice( nullptr, ¶ms, @@ -171,7 +181,7 @@ void DeviceResources::CreateDeviceResources() #ifdef _DEBUG if (hr == E_NOINTERFACE) { - OutputDebugStringA("ERROR: Requires Windows 10 (Version 1903, Build 18362) or later\n"); + OutputDebugStringA("ERROR: Requires Windows 10 (Version 2004, Build 19041) or later\n"); } #endif ThrowIfFailed(hr); @@ -326,7 +336,7 @@ void DeviceResources::CreateWindowSizeDependentResources() // Obtain the back buffers for this window which will be the final render targets // and create render target views for each of them. - CD3DX12_HEAP_PROPERTIES swapChainHeapProperties(D3D12_HEAP_TYPE_DEFAULT); + const CD3DX12_HEAP_PROPERTIES swapChainHeapProperties(D3D12_HEAP_TYPE_DEFAULT); D3D12_RESOURCE_DESC swapChainBufferDesc = CD3DX12_RESOURCE_DESC::Tex2D( m_backBufferFormat, @@ -358,7 +368,7 @@ void DeviceResources::CreateWindowSizeDependentResources() rtvDesc.Format = m_backBufferFormat; rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; - CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( + const CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( m_rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), static_cast(n), m_rtvDescriptorSize); m_d3dDevice->CreateRenderTargetView(m_renderTargets[n].Get(), &rtvDesc, rtvDescriptor); @@ -369,7 +379,7 @@ void DeviceResources::CreateWindowSizeDependentResources() #else // _GAMING_DESKTOP - DXGI_FORMAT backBufferFormat = NoSRGB(m_backBufferFormat); + const DXGI_FORMAT backBufferFormat = NoSRGB(m_backBufferFormat); // If the swap chain already exists, resize it, otherwise create one. if (m_swapChain) @@ -452,7 +462,7 @@ void DeviceResources::CreateWindowSizeDependentResources() rtvDesc.Format = m_backBufferFormat; rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; - CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( + const CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( m_rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), static_cast(n), m_rtvDescriptorSize); m_d3dDevice->CreateRenderTargetView(m_renderTargets[n].Get(), &rtvDesc, rtvDescriptor); @@ -467,7 +477,7 @@ void DeviceResources::CreateWindowSizeDependentResources() { // Allocate a 2-D surface as the depth/stencil buffer and create a depth/stencil view // on this surface. - CD3DX12_HEAP_PROPERTIES depthHeapProperties(D3D12_HEAP_TYPE_DEFAULT); + const CD3DX12_HEAP_PROPERTIES depthHeapProperties(D3D12_HEAP_TYPE_DEFAULT); D3D12_RESOURCE_DESC depthStencilDesc = CD3DX12_RESOURCE_DESC::Tex2D( m_depthBufferFormat, @@ -480,7 +490,7 @@ void DeviceResources::CreateWindowSizeDependentResources() D3D12_CLEAR_VALUE depthOptimizedClearValue = {}; depthOptimizedClearValue.Format = m_depthBufferFormat; - depthOptimizedClearValue.DepthStencil.Depth = 1.0f; + depthOptimizedClearValue.DepthStencil.Depth = (m_options & c_ReverseDepth) ? 0.0f : 1.0f; depthOptimizedClearValue.DepthStencil.Stencil = 0; ThrowIfFailed(m_d3dDevice->CreateCommittedResource( @@ -591,12 +601,6 @@ void DeviceResources::HandleDeviceLost() // Prepare the command list and render target for rendering. void DeviceResources::Prepare(D3D12_RESOURCE_STATES beforeState, D3D12_RESOURCE_STATES afterState) { -#ifdef _GAMING_XBOX - // Wait until frame start is signaled - m_framePipelineToken = D3D12XBOX_FRAME_PIPELINE_TOKEN_NULL; - ThrowIfFailed(m_d3dDevice->WaitFrameEventX(D3D12XBOX_FRAME_EVENT_ORIGIN, INFINITE, nullptr, D3D12XBOX_WAIT_FRAME_EVENT_FLAG_NONE, &m_framePipelineToken)); -#endif - // Reset command list and allocator. ThrowIfFailed(m_commandAllocators[m_backBufferIndex]->Reset()); ThrowIfFailed(m_commandList->Reset(m_commandAllocators[m_backBufferIndex].Get(), nullptr)); @@ -604,7 +608,8 @@ void DeviceResources::Prepare(D3D12_RESOURCE_STATES beforeState, D3D12_RESOURCE_ if (beforeState != afterState) { // Transition the render target into the correct state to allow for drawing into it. - D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), + const D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition( + m_renderTargets[m_backBufferIndex].Get(), beforeState, afterState); m_commandList->ResourceBarrier(1, &barrier); } @@ -616,7 +621,9 @@ void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState) if (beforeState != D3D12_RESOURCE_STATE_PRESENT) { // Transition the render target to the state that allows it to be presented to the display. - D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), beforeState, D3D12_RESOURCE_STATE_PRESENT); + const D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition( + m_renderTargets[m_backBufferIndex].Get(), + beforeState, D3D12_RESOURCE_STATE_PRESENT); m_commandList->ResourceBarrier(1, &barrier); } @@ -636,9 +643,12 @@ void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState) m_commandQueue->PresentX(1, &planeParameters, nullptr) ); - // Xbox One apps do not need to handle DXGI_ERROR_DEVICE_REMOVED or DXGI_ERROR_DEVICE_RESET. + // Xbox apps do not need to handle DXGI_ERROR_DEVICE_REMOVED or DXGI_ERROR_DEVICE_RESET. -#else + // Update the back buffer index. + m_backBufferIndex = (m_backBufferIndex + 1) % m_backBufferCount; + +#else // _GAMING_DESKTOP // The first argument instructs DXGI to block until VSync, putting the application // to sleep until the next VSync. This ensures we don't waste any cycles rendering @@ -661,9 +671,8 @@ void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState) ThrowIfFailed(hr); } -#endif - MoveToNextFrame(); +#endif } // Handle GPU suspend/resume @@ -689,13 +698,13 @@ void DeviceResources::WaitForGpu() noexcept if (m_commandQueue && m_fence && m_fenceEvent.IsValid()) { // Schedule a Signal command in the GPU queue. - UINT64 fenceValue = m_fenceValues[m_backBufferIndex]; + const UINT64 fenceValue = m_fenceValues[m_backBufferIndex]; if (SUCCEEDED(m_commandQueue->Signal(m_fence.Get(), fenceValue))) { // Wait until the Signal has been processed. if (SUCCEEDED(m_fence->SetEventOnCompletion(fenceValue, m_fenceEvent.Get()))) { - WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); + std::ignore = WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); // Increment the fence value for the current frame. m_fenceValues[m_backBufferIndex]++; @@ -704,32 +713,20 @@ void DeviceResources::WaitForGpu() noexcept } } -// Prepare to render the next frame. -void DeviceResources::MoveToNextFrame() -{ - // Schedule a Signal command in the queue. - const UINT64 currentFenceValue = m_fenceValues[m_backBufferIndex]; - ThrowIfFailed(m_commandQueue->Signal(m_fence.Get(), currentFenceValue)); - - // Update the back buffer index. #ifdef _GAMING_XBOX - m_backBufferIndex = (m_backBufferIndex + 1) % m_backBufferCount; -#else - m_backBufferIndex = m_swapChain->GetCurrentBackBufferIndex(); -#endif - - // If the next frame is not ready to be rendered yet, wait until it is ready. - if (m_fence->GetCompletedValue() < m_fenceValues[m_backBufferIndex]) - { - ThrowIfFailed(m_fence->SetEventOnCompletion(m_fenceValues[m_backBufferIndex], m_fenceEvent.Get())); - WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); - } - - // Set the fence value for the next frame. - m_fenceValues[m_backBufferIndex] = currentFenceValue + 1; +// For PresentX rendering, we should wait for the origin event just before processing input. +void DeviceResources::WaitForOrigin() +{ + // Wait until frame start is signaled + m_framePipelineToken = D3D12XBOX_FRAME_PIPELINE_TOKEN_NULL; + ThrowIfFailed(m_d3dDevice->WaitFrameEventX( + D3D12XBOX_FRAME_EVENT_ORIGIN, + INFINITE, + nullptr, + D3D12XBOX_WAIT_FRAME_EVENT_FLAG_NONE, + &m_framePipelineToken)); } -#ifdef _GAMING_XBOX // Set frame interval and register for frame events void DeviceResources::RegisterFrameEvents() { @@ -758,7 +755,30 @@ void DeviceResources::RegisterFrameEvents() nullptr, D3D12XBOX_SCHEDULE_FRAME_EVENT_FLAG_NONE)); } -#else + +#else // _GAMING_DESKTOP + +// Prepare to render the next frame. +void DeviceResources::MoveToNextFrame() +{ + // Schedule a Signal command in the queue. + const UINT64 currentFenceValue = m_fenceValues[m_backBufferIndex]; + ThrowIfFailed(m_commandQueue->Signal(m_fence.Get(), currentFenceValue)); + + // Update the back buffer index. + m_backBufferIndex = m_swapChain->GetCurrentBackBufferIndex(); + + // If the next frame is not ready to be rendered yet, wait until it is ready. + if (m_fence->GetCompletedValue() < m_fenceValues[m_backBufferIndex]) + { + ThrowIfFailed(m_fence->SetEventOnCompletion(m_fenceValues[m_backBufferIndex], m_fenceEvent.Get())); + std::ignore = WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); + } + + // Set the fence value for the next frame. + m_fenceValues[m_backBufferIndex] = currentFenceValue + 1; +} + // This method acquires the first available hardware adapter that supports Direct3D 12. // If no such adapter can be found, try WARP. Otherwise throw an exception. void DeviceResources::GetAdapter(IDXGIAdapter1** ppAdapter) diff --git a/Samples/Graphics/DXRTriangle/DXRTriangle/DeviceResources.h b/Samples/Graphics/DXRTriangle/DXRTriangle/DeviceResources.h index d3c3894..f7718ae 100644 --- a/Samples/Graphics/DXRTriangle/DXRTriangle/DeviceResources.h +++ b/Samples/Graphics/DXRTriangle/DXRTriangle/DeviceResources.h @@ -1,7 +1,7 @@ // // DeviceResources.h - A wrapper for the Direct3D 12/12.X device and swapchain // -// Modified to use ID3D12Device6/ID3D12GraphicsCommandList5 for DXR access +// Modified to use ID3D12Device8/ID3D12GraphicsCommandList5 for DXR access // #pragma once @@ -22,9 +22,13 @@ namespace DX class DeviceResources { public: + static constexpr unsigned int c_ReverseDepth = 0x1; + static constexpr unsigned int c_AmplificationShaders = 0x2; + DeviceResources(DXGI_FORMAT backBufferFormat = DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT depthBufferFormat = DXGI_FORMAT_D32_FLOAT, - UINT backBufferCount = 2) noexcept(false); + UINT backBufferCount = 2, + unsigned int flags = 0) noexcept(false); ~DeviceResources(); DeviceResources(DeviceResources&&) = default; @@ -45,6 +49,9 @@ namespace DX void Suspend(); void Resume(); void WaitForGpu() noexcept; +#ifdef _GAMING_XBOX + void WaitForOrigin(); +#endif // Device Accessors. RECT GetOutputSize() const noexcept { return m_outputSize; } @@ -68,6 +75,7 @@ namespace DX D3D12_RECT GetScissorRect() const noexcept { return m_scissorRect; } UINT GetCurrentFrameIndex() const noexcept { return m_backBufferIndex; } UINT GetBackBufferCount() const noexcept { return m_backBufferCount; } + unsigned int GetDeviceOptions() const noexcept { return m_options; } CD3DX12_CPU_DESCRIPTOR_HANDLE GetRenderTargetView() const noexcept { @@ -81,10 +89,10 @@ namespace DX } private: - void MoveToNextFrame(); #ifdef _GAMING_XBOX void RegisterFrameEvents(); #else + void MoveToNextFrame(); void GetAdapter(IDXGIAdapter1** ppAdapter); #endif @@ -93,7 +101,7 @@ namespace DX UINT m_backBufferIndex; // Direct3D objects. - Microsoft::WRL::ComPtr m_d3dDevice; + Microsoft::WRL::ComPtr m_d3dDevice; Microsoft::WRL::ComPtr m_commandList; Microsoft::WRL::ComPtr m_commandQueue; Microsoft::WRL::ComPtr m_commandAllocators[MAX_BACK_BUFFER_COUNT]; @@ -131,6 +139,9 @@ namespace DX D3D_FEATURE_LEVEL m_d3dFeatureLevel; RECT m_outputSize; + // DeviceResources options (see flags above) + unsigned int m_options; + // The IDeviceNotify can be held directly as it owns the DeviceResources. IDeviceNotify* m_deviceNotify; }; diff --git a/Samples/Graphics/DXRTriangle/DXRTriangle/Main.cpp b/Samples/Graphics/DXRTriangle/DXRTriangle/Main.cpp index 7ba6839..1905a93 100644 --- a/Samples/Graphics/DXRTriangle/DXRTriangle/Main.cpp +++ b/Samples/Graphics/DXRTriangle/DXRTriangle/Main.cpp @@ -417,7 +417,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) } else { - SetWindowLongPtr(hWnd, GWL_STYLE, 0); + SetWindowLongPtr(hWnd, GWL_STYLE, WS_POPUP); SetWindowLongPtr(hWnd, GWL_EXSTYLE, WS_EX_TOPMOST); SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); diff --git a/Samples/Graphics/DXRTriangle/DXRTriangle/StepTimer.h b/Samples/Graphics/DXRTriangle/DXRTriangle/StepTimer.h index bb15fff..2dbefcb 100644 --- a/Samples/Graphics/DXRTriangle/DXRTriangle/StepTimer.h +++ b/Samples/Graphics/DXRTriangle/DXRTriangle/StepTimer.h @@ -111,7 +111,7 @@ namespace DX timeDelta *= TicksPerSecond; timeDelta /= static_cast(m_qpcFrequency.QuadPart); - uint32_t lastFrameCount = m_frameCount; + const uint32_t lastFrameCount = m_frameCount; if (m_isFixedTimeStep) { diff --git a/Samples/Graphics/DXRTriangle/DXRTriangle/pch.h b/Samples/Graphics/DXRTriangle/DXRTriangle/pch.h index 038fc6d..cd100f4 100644 --- a/Samples/Graphics/DXRTriangle/DXRTriangle/pch.h +++ b/Samples/Graphics/DXRTriangle/DXRTriangle/pch.h @@ -41,8 +41,8 @@ #include -#if _GRDK_VER < 0x4A610D2B /* GXDK Edition 200600 */ -#error This sample requires the June 2020 GDK or later +#if _GRDK_VER < 0x55F007B0 /* GDK Edition 211000 */ +#error This sample requires the October 2021 GDK or later #endif #ifdef _GAMING_XBOX_SCARLETT diff --git a/Samples/Graphics/DeferredParticles/DeferredParticles.cpp b/Samples/Graphics/DeferredParticles/DeferredParticles.cpp index bc6d42e..4bc30b1 100644 --- a/Samples/Graphics/DeferredParticles/DeferredParticles.cpp +++ b/Samples/Graphics/DeferredParticles/DeferredParticles.cpp @@ -10,10 +10,7 @@ #include "pch.h" #include "DeferredParticles.h" -// C4238: nonstandard extension used: class rvalue used as lvalue -#pragma warning(disable : 4238) - -extern void ExitSample(); +extern void ExitSample() noexcept; using namespace DirectX; @@ -98,7 +95,12 @@ void Sample::Tick() { PIXBeginEvent(PIX_COLOR_DEFAULT, L"Frame %llu", m_frame); - m_timer.Tick([&]() { Update(m_timer); }); + m_deviceResources->WaitForOrigin(); + + m_timer.Tick([&]() + { + Update(m_timer); + }); Render(); @@ -196,7 +198,7 @@ void Sample::Render() } // Restore the viewport, as the sky changed the min depth bounds. - auto vp = m_deviceResources->GetScreenViewport(); + auto const vp = m_deviceResources->GetScreenViewport(); commandList->RSSetViewports(1, &vp); // Render the particle systems. @@ -206,7 +208,7 @@ void Sample::Render() { ScopedPixEvent hud(commandList, PIX_COLOR_DEFAULT, L"HUD"); - auto safe = DirectX::SimpleMath::Viewport::ComputeTitleSafeArea(UINT(m_displayWidth), UINT(m_displayHeight)); + auto const safe = DirectX::SimpleMath::Viewport::ComputeTitleSafeArea(UINT(m_displayWidth), UINT(m_displayHeight)); wchar_t textBuffer[1024] = {}; XMFLOAT2 textPos = XMFLOAT2(float(safe.left), float(safe.top)); @@ -247,16 +249,16 @@ void Sample::Clear() PIXBeginEvent(commandList, PIX_COLOR_DEFAULT, L"Clear"); // Clear the views. - auto rtvDescriptor = m_deviceResources->GetRenderTargetView(); - auto dsvDescriptor = m_deviceResources->GetDepthStencilView(); + auto const rtvDescriptor = m_deviceResources->GetRenderTargetView(); + auto const dsvDescriptor = m_deviceResources->GetDepthStencilView(); commandList->OMSetRenderTargets(1, &rtvDescriptor, FALSE, &dsvDescriptor); commandList->ClearRenderTargetView(rtvDescriptor, ATG::Colors::Background, 0, nullptr); commandList->ClearDepthStencilView(dsvDescriptor, D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr); // Set the viewport and scissor rect. - auto viewport = m_deviceResources->GetScreenViewport(); - auto scissorRect = m_deviceResources->GetScissorRect(); + auto const viewport = m_deviceResources->GetScreenViewport(); + auto const scissorRect = m_deviceResources->GetScissorRect(); commandList->RSSetViewports(1, &viewport); commandList->RSSetScissorRects(1, &scissorRect); @@ -289,8 +291,6 @@ void Sample::CreateDeviceDependentResources() m_commonStates = std::make_unique(device); m_srvPile = std::make_unique(device, - D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, - D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, 128, SRV_Count); @@ -305,8 +305,8 @@ void Sample::CreateDeviceDependentResources() resourceUpload.Begin(); // HUD - auto backBufferRts = RenderTargetState(m_deviceResources->GetBackBufferFormat(), m_deviceResources->GetDepthBufferFormat()); - auto spritePSD = SpriteBatchPipelineStateDescription(backBufferRts, &CommonStates::AlphaBlend); + const RenderTargetState backBufferRts(m_deviceResources->GetBackBufferFormat(), m_deviceResources->GetDepthBufferFormat()); + const SpriteBatchPipelineStateDescription spritePSD(backBufferRts, &CommonStates::AlphaBlend); m_hudBatch = std::make_unique(device, resourceUpload, spritePSD); m_particleWorld.CreateDeviceDependentResources(m_deviceResources.get(), resourceUpload, *m_models[0].get()); @@ -399,7 +399,7 @@ void Sample::CreateDeviceDependentResources() void Sample::CreateWindowSizeDependentResources() { auto device = m_deviceResources->GetD3DDevice(); - const auto size = m_deviceResources->GetOutputSize(); + auto const size = m_deviceResources->GetOutputSize(); // Calculate display dimensions. m_displayWidth = size.right - size.left; @@ -468,7 +468,7 @@ void Sample::SetupPipelineSky(ID3D12GraphicsCommandList* commandList, const Mode SetupPipelineCommon(commandList, part, instIndex, texOffset, skyView); - D3D12_VIEWPORT vpSky = m_deviceResources->GetScreenViewport(); + auto vpSky = m_deviceResources->GetScreenViewport(); vpSky.MinDepth = 0.999f; commandList->RSSetViewports(1, &vpSky); commandList->SetPipelineState(m_skyPSO.Get()); diff --git a/Samples/Graphics/DeferredParticles/DeferredParticles.h b/Samples/Graphics/DeferredParticles/DeferredParticles.h index fe666de..632f976 100644 --- a/Samples/Graphics/DeferredParticles/DeferredParticles.h +++ b/Samples/Graphics/DeferredParticles/DeferredParticles.h @@ -20,18 +20,26 @@ public: Sample() noexcept(false); ~Sample(); + Sample(Sample&&) = default; + Sample& operator= (Sample&&) = default; + + Sample(Sample const&) = delete; + Sample& operator= (Sample const&) = delete; + // Initialization and management void Initialize(HWND window); - // Basic Sample loop + // Basic render loop void Tick(); // Messages void OnSuspending(); void OnResuming(); + void OnConstrained() {} + void OnUnConstrained() {} // Properties - bool RequestHDRMode() const { return m_deviceResources ? (m_deviceResources->GetDeviceOptions() & DX::DeviceResources::c_EnableHDR) != 0 : false; } + bool RequestHDRMode() const noexcept { return m_deviceResources ? (m_deviceResources->GetDeviceOptions() & DX::DeviceResources::c_EnableHDR) != 0 : false; } // Helper callback functions that setup the pipeline state for the scene & sky mesh types. void SetupPipelineScene(ID3D12GraphicsCommandList* commandList, const DirectX::ModelMeshPart& part, int instIndex, int texOffset); diff --git a/Samples/Graphics/DeferredParticles/DeviceResources.cpp b/Samples/Graphics/DeferredParticles/DeviceResources.cpp index d76fd63..fa67816 100644 --- a/Samples/Graphics/DeferredParticles/DeviceResources.cpp +++ b/Samples/Graphics/DeferredParticles/DeviceResources.cpp @@ -31,14 +31,13 @@ DeviceResources::DeviceResources( m_rtvDescriptorSize(0), m_screenViewport{}, m_scissorRect{}, - m_backBufferFormat((flags & c_EnableHDR) ? DXGI_FORMAT_R10G10B10A2_UNORM : backBufferFormat), + m_backBufferFormat(backBufferFormat), m_depthBufferFormat(depthBufferFormat), m_backBufferCount(backBufferCount), m_window(nullptr), m_d3dFeatureLevel(D3D_FEATURE_LEVEL_12_0), m_outputSize{0, 0, 1920, 1080}, - m_options(flags), - m_gameDVRFormat((flags & c_EnableHDR) ? backBufferFormat : DXGI_FORMAT_UNKNOWN) + m_options(flags) { if (backBufferCount < 2 || backBufferCount > MAX_BACK_BUFFER_COUNT) { @@ -55,12 +54,16 @@ DeviceResources::~DeviceResources() // Ensure we present a blank screen before cleaning up resources. if (m_commandQueue) { - (void)m_commandQueue->PresentX(0, nullptr, nullptr); + std::ignore = m_commandQueue->PresentX(0, nullptr, nullptr); } } // Configures the Direct3D device, and stores handles to it and the device context. +#ifdef _GAMING_XBOX_SCARLETT +void DeviceResources::CreateDeviceResources(D3D12XBOX_CREATE_DEVICE_FLAGS createDeviceFlags) +#else void DeviceResources::CreateDeviceResources() +#endif { // Create the DX12 API device object. D3D12XBOX_CREATE_DEVICE_PARAMETERS params = {}; @@ -77,6 +80,17 @@ void DeviceResources::CreateDeviceResources() params.GraphicsCommandQueueRingSizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); params.GraphicsScratchMemorySizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); params.ComputeScratchMemorySizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); +#ifdef _GAMING_XBOX_SCARLETT + params.CreateDeviceFlags = createDeviceFlags; + +#if (_GXDK_VER >= 0x585D070E /* GXDK Edition 221000 */) + if (m_options & c_AmplificationShaders) + { + params.AmplificationShaderIndirectArgsBufferSize = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); + params.AmplificationShaderPayloadBufferSize = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); + } +#endif +#endif HRESULT hr = D3D12XboxCreateDevice( nullptr, @@ -107,7 +121,7 @@ void DeviceResources::CreateDeviceResources() // Create descriptor heaps for render target views and depth stencil views. D3D12_DESCRIPTOR_HEAP_DESC rtvDescriptorHeapDesc = {}; - rtvDescriptorHeapDesc.NumDescriptors = (m_options & c_EnableHDR) ? (m_backBufferCount * 2) : m_backBufferCount; + rtvDescriptorHeapDesc.NumDescriptors = m_backBufferCount; rtvDescriptorHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; ThrowIfFailed(m_d3dDevice->CreateDescriptorHeap(&rtvDescriptorHeapDesc, IID_GRAPHICS_PPV_ARGS(m_rtvDescriptorHeap.ReleaseAndGetAddressOf()))); @@ -152,31 +166,46 @@ void DeviceResources::CreateDeviceResources() m_fenceEvent.Attach(CreateEventEx(nullptr, nullptr, 0, EVENT_MODIFY_STATE | SYNCHRONIZE)); if (!m_fenceEvent.IsValid()) { - throw std::exception("CreateEvent"); + throw std::system_error(std::error_code(static_cast(GetLastError()), std::system_category()), "CreateEventEx"); } - if (m_options & c_Enable4K_UHD) + if (m_options & (c_Enable4K_UHD | c_EnableQHD)) { switch (XSystemGetDeviceType()) { case XSystemDeviceType::XboxOne: case XSystemDeviceType::XboxOneS: - case XSystemDeviceType::XboxScarlettLockhart /* Xbox Series S */: - m_options &= ~c_Enable4K_UHD; -#ifdef _DEBUG - OutputDebugStringA("INFO: Swapchain using 1080p (1920 x 1080)\n"); -#endif + m_options &= ~(c_Enable4K_UHD | c_EnableQHD); break; + case XSystemDeviceType::XboxScarlettLockhart /* Xbox Series S */: + m_options &= ~c_Enable4K_UHD; + if (m_options & c_EnableQHD) + { + m_outputSize = { 0, 0, 2560, 1440 }; + } + break; + + case XSystemDeviceType::XboxScarlettAnaconda /* Xbox Series X */: + case XSystemDeviceType::XboxOneXDevkit: + case XSystemDeviceType::XboxScarlettDevkit: default: - m_outputSize = { 0, 0, 3840, 2160 }; -#ifdef _DEBUG - OutputDebugStringA("INFO: Swapchain using 4k (3840 x 2160)\n"); -#endif + m_outputSize = (m_options & c_Enable4K_UHD) ? RECT{ 0, 0, 3840, 2160 } : RECT{ 0, 0, 2560, 1440 }; break; } } +#ifdef _DEBUG + const char* info = nullptr; + switch (m_outputSize.bottom) + { + case 2160: info = "INFO: Swapchain using 4k (3840 x 2160)\n"; break; + case 1440: info = "INFO: Swapchain using 1440p (2560 x 1440)\n"; break; + default: info = "INFO: Swapchain using 1080p (1920 x 1080)\n"; break; + } + OutputDebugStringA(info); +#endif + RegisterFrameEvents(); } @@ -185,7 +214,7 @@ void DeviceResources::CreateWindowSizeDependentResources() { if (!m_window) { - throw std::exception("Call SetWindow with a valid window handle"); + throw std::logic_error("Call SetWindow with a valid Win32 window handle"); } // Wait until all previous GPU work is complete. @@ -198,7 +227,6 @@ void DeviceResources::CreateWindowSizeDependentResources() for (UINT n = 0; n < m_backBufferCount; n++) { m_renderTargets[n].Reset(); - m_renderTargetsGameDVR[n].Reset(); m_fenceValues[n] = m_fenceValues[m_backBufferIndex]; } @@ -208,7 +236,7 @@ void DeviceResources::CreateWindowSizeDependentResources() // Obtain the back buffers for this window which will be the final render targets // and create render target views for each of them. - CD3DX12_HEAP_PROPERTIES swapChainHeapProperties(D3D12_HEAP_TYPE_DEFAULT); + const CD3DX12_HEAP_PROPERTIES swapChainHeapProperties(D3D12_HEAP_TYPE_DEFAULT); D3D12_RESOURCE_DESC swapChainBufferDesc = CD3DX12_RESOURCE_DESC::Tex2D( m_backBufferFormat, @@ -219,6 +247,13 @@ void DeviceResources::CreateWindowSizeDependentResources() ); swapChainBufferDesc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; +#ifdef _GAMING_XBOX_XBOXONE + if (m_options & c_EnableHDR) + { + swapChainBufferDesc.Flags |= D3D12XBOX_RESOURCE_FLAG_ALLOW_AUTOMATIC_GAMEDVR_TONE_MAP; + } +#endif + D3D12_CLEAR_VALUE swapChainOptimizedClearValue = {}; swapChainOptimizedClearValue.Format = m_backBufferFormat; @@ -240,41 +275,12 @@ void DeviceResources::CreateWindowSizeDependentResources() rtvDesc.Format = m_backBufferFormat; rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; - CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( + const CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( m_rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), static_cast(n), m_rtvDescriptorSize); m_d3dDevice->CreateRenderTargetView(m_renderTargets[n].Get(), &rtvDesc, rtvDescriptor); } - if (m_options & c_EnableHDR) - { - swapChainBufferDesc.Format = swapChainOptimizedClearValue.Format = m_gameDVRFormat; - - for (UINT n = 0; n < m_backBufferCount; n++) - { - ThrowIfFailed(m_d3dDevice->CreateCommittedResource( - &swapChainHeapProperties, - D3D12_HEAP_FLAG_ALLOW_DISPLAY, - &swapChainBufferDesc, - D3D12_RESOURCE_STATE_PRESENT, - &swapChainOptimizedClearValue, - IID_GRAPHICS_PPV_ARGS(m_renderTargetsGameDVR[n].GetAddressOf()))); - - wchar_t name[25] = {}; - swprintf_s(name, L"GameDVR Render target %u", n); - m_renderTargetsGameDVR[n]->SetName(name); - - D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {}; - rtvDesc.Format = m_gameDVRFormat; - rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; - - CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptorGameDVR( - m_rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), - static_cast(m_backBufferCount + n), m_rtvDescriptorSize); - m_d3dDevice->CreateRenderTargetView(m_renderTargetsGameDVR[n].Get(), &rtvDesc, rtvDescriptorGameDVR); - } - } - // Reset the index to the current back buffer. m_backBufferIndex = 0; @@ -282,7 +288,7 @@ void DeviceResources::CreateWindowSizeDependentResources() { // Allocate a 2-D surface as the depth/stencil buffer and create a depth/stencil view // on this surface. - CD3DX12_HEAP_PROPERTIES depthHeapProperties(D3D12_HEAP_TYPE_DEFAULT); + const CD3DX12_HEAP_PROPERTIES depthHeapProperties(D3D12_HEAP_TYPE_DEFAULT); D3D12_RESOURCE_DESC depthStencilDesc = CD3DX12_RESOURCE_DESC::Tex2D( m_depthBufferFormat, @@ -295,7 +301,7 @@ void DeviceResources::CreateWindowSizeDependentResources() D3D12_CLEAR_VALUE depthOptimizedClearValue = {}; depthOptimizedClearValue.Format = m_depthBufferFormat; - depthOptimizedClearValue.DepthStencil.Depth = 1.0f; + depthOptimizedClearValue.DepthStencil.Depth = (m_options & c_ReverseDepth) ? 0.0f : 1.0f; depthOptimizedClearValue.DepthStencil.Stencil = 0; ThrowIfFailed(m_d3dDevice->CreateCommittedResource( @@ -331,10 +337,6 @@ void DeviceResources::CreateWindowSizeDependentResources() // Prepare the command list and render target for rendering. void DeviceResources::Prepare(D3D12_RESOURCE_STATES beforeState, D3D12_RESOURCE_STATES afterState) { - // Wait until frame start is signaled - m_framePipelineToken = D3D12XBOX_FRAME_PIPELINE_TOKEN_NULL; - ThrowIfFailed(m_d3dDevice->WaitFrameEventX(D3D12XBOX_FRAME_EVENT_ORIGIN, INFINITE, nullptr, D3D12XBOX_WAIT_FRAME_EVENT_FLAG_NONE, &m_framePipelineToken)); - // Reset command list and allocator. ThrowIfFailed(m_commandAllocators[m_backBufferIndex]->Reset()); ThrowIfFailed(m_commandList->Reset(m_commandAllocators[m_backBufferIndex].Get(), nullptr)); @@ -342,23 +344,10 @@ void DeviceResources::Prepare(D3D12_RESOURCE_STATES beforeState, D3D12_RESOURCE_ if (beforeState != afterState) { // Transition the render target into the correct state to allow for drawing into it. - if (m_options & c_EnableHDR) - { - D3D12_RESOURCE_BARRIER barriers[2] = - { - CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), - beforeState, afterState), - CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargetsGameDVR[m_backBufferIndex].Get(), - beforeState, afterState), - }; - m_commandList->ResourceBarrier(_countof(barriers), barriers); - } - else - { - D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), - beforeState, afterState); - m_commandList->ResourceBarrier(1, &barrier); - } + const D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition( + m_renderTargets[m_backBufferIndex].Get(), + beforeState, afterState); + m_commandList->ResourceBarrier(1, &barrier); } } @@ -368,20 +357,10 @@ void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState, _In_opt_ const if (beforeState != D3D12_RESOURCE_STATE_PRESENT) { // Transition the render target to the state that allows it to be presented to the display. - if (m_options & c_EnableHDR) - { - D3D12_RESOURCE_BARRIER barriers[2] = - { - CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), beforeState, D3D12_RESOURCE_STATE_PRESENT), - CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargetsGameDVR[m_backBufferIndex].Get(), beforeState, D3D12_RESOURCE_STATE_PRESENT), - }; - m_commandList->ResourceBarrier(_countof(barriers), barriers); - } - else - { - D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), beforeState, D3D12_RESOURCE_STATE_PRESENT); - m_commandList->ResourceBarrier(1, &barrier); - } + const D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition( + m_renderTargets[m_backBufferIndex].Get(), + beforeState, D3D12_RESOURCE_STATE_PRESENT); + m_commandList->ResourceBarrier(1, &barrier); } // Send the command list off to the GPU for processing. @@ -389,32 +368,24 @@ void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState, _In_opt_ const m_commandQueue->ExecuteCommandLists(1, CommandListCast(m_commandList.GetAddressOf())); // Present the backbuffer using the PresentX API. - D3D12XBOX_PRESENT_PLANE_PARAMETERS planeParameters[2] = {}; - planeParameters[0].Token = planeParameters[1].Token = m_framePipelineToken; - planeParameters[0].ResourceCount = planeParameters[1].ResourceCount = 1; - planeParameters[0].ppResources = m_renderTargets[m_backBufferIndex].GetAddressOf(); + D3D12XBOX_PRESENT_PLANE_PARAMETERS planeParameters = {}; + planeParameters.Token = m_framePipelineToken; + planeParameters.ResourceCount = 1; + planeParameters.ppResources = m_renderTargets[m_backBufferIndex].GetAddressOf(); if (m_options & c_EnableHDR) { - planeParameters[0].pSrcViewRects = planeParameters[1].pSrcViewRects = &m_outputSize; - planeParameters[0].ColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020; - planeParameters[1].ppResources = m_renderTargetsGameDVR[m_backBufferIndex].GetAddressOf(); - planeParameters[1].ColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; - - ThrowIfFailed( - m_commandQueue->PresentX(2, planeParameters, params) - ); - } - else - { - ThrowIfFailed( - m_commandQueue->PresentX(1, planeParameters, params) - ); + planeParameters.ColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020; } - // Xbox One apps do not need to handle DXGI_ERROR_DEVICE_REMOVED or DXGI_ERROR_DEVICE_RESET. + ThrowIfFailed( + m_commandQueue->PresentX(1, &planeParameters, params) + ); - MoveToNextFrame(); + // Xbox apps do not need to handle DXGI_ERROR_DEVICE_REMOVED or DXGI_ERROR_DEVICE_RESET. + + // Update the back buffer index. + m_backBufferIndex = (m_backBufferIndex + 1) % m_backBufferCount; } // Handle GPU suspend/resume @@ -436,13 +407,13 @@ void DeviceResources::WaitForGpu() noexcept if (m_commandQueue && m_fence && m_fenceEvent.IsValid()) { // Schedule a Signal command in the GPU queue. - UINT64 fenceValue = m_fenceValues[m_backBufferIndex]; + const UINT64 fenceValue = m_fenceValues[m_backBufferIndex]; if (SUCCEEDED(m_commandQueue->Signal(m_fence.Get(), fenceValue))) { // Wait until the Signal has been processed. if (SUCCEEDED(m_fence->SetEventOnCompletion(fenceValue, m_fenceEvent.Get()))) { - WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); + std::ignore = WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); // Increment the fence value for the current frame. m_fenceValues[m_backBufferIndex]++; @@ -451,25 +422,17 @@ void DeviceResources::WaitForGpu() noexcept } } -// Prepare to render the next frame. -void DeviceResources::MoveToNextFrame() +// For PresentX rendering, we should wait for the origin event just before processing input. +void DeviceResources::WaitForOrigin() { - // Schedule a Signal command in the queue. - const UINT64 currentFenceValue = m_fenceValues[m_backBufferIndex]; - ThrowIfFailed(m_commandQueue->Signal(m_fence.Get(), currentFenceValue)); - - // Update the back buffer index. - m_backBufferIndex = (m_backBufferIndex + 1) % m_backBufferCount; - - // If the next frame is not ready to be rendered yet, wait until it is ready. - if (m_fence->GetCompletedValue() < m_fenceValues[m_backBufferIndex]) - { - ThrowIfFailed(m_fence->SetEventOnCompletion(m_fenceValues[m_backBufferIndex], m_fenceEvent.Get())); - WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); - } - - // Set the fence value for the next frame. - m_fenceValues[m_backBufferIndex] = currentFenceValue + 1; + // Wait until frame start is signaled + m_framePipelineToken = D3D12XBOX_FRAME_PIPELINE_TOKEN_NULL; + ThrowIfFailed(m_d3dDevice->WaitFrameEventX( + D3D12XBOX_FRAME_EVENT_ORIGIN, + INFINITE, + nullptr, + D3D12XBOX_WAIT_FRAME_EVENT_FLAG_NONE, + &m_framePipelineToken)); } // Set frame interval and register for frame events diff --git a/Samples/Graphics/DeferredParticles/DeviceResources.h b/Samples/Graphics/DeferredParticles/DeviceResources.h index 1d26b17..4924894 100644 --- a/Samples/Graphics/DeferredParticles/DeviceResources.h +++ b/Samples/Graphics/DeferredParticles/DeviceResources.h @@ -10,8 +10,11 @@ namespace DX class DeviceResources { public: - static constexpr unsigned int c_Enable4K_UHD = 0x1; - static constexpr unsigned int c_EnableHDR = 0x2; + static constexpr unsigned int c_Enable4K_UHD = 0x1; + static constexpr unsigned int c_EnableQHD = 0x2; + static constexpr unsigned int c_EnableHDR = 0x4; + static constexpr unsigned int c_ReverseDepth = 0x8; + static constexpr unsigned int c_AmplificationShaders = 0x10; DeviceResources(DXGI_FORMAT backBufferFormat = DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT depthBufferFormat = DXGI_FORMAT_D32_FLOAT, @@ -25,7 +28,11 @@ namespace DX DeviceResources(DeviceResources const&) = delete; DeviceResources& operator= (DeviceResources const&) = delete; +#ifdef _GAMING_XBOX_SCARLETT + void CreateDeviceResources(D3D12XBOX_CREATE_DEVICE_FLAGS createDeviceFlags = D3D12XBOX_CREATE_DEVICE_FLAG_NONE); +#else void CreateDeviceResources(); +#endif void CreateWindowSizeDependentResources(); void SetWindow(HWND window) noexcept { m_window = window; } void Prepare(D3D12_RESOURCE_STATES beforeState = D3D12_RESOURCE_STATE_PRESENT, @@ -35,6 +42,7 @@ namespace DX void Suspend(); void Resume(); void WaitForGpu() noexcept; + void WaitForOrigin(); // Device Accessors. RECT GetOutputSize() const noexcept { return m_outputSize; } @@ -67,19 +75,7 @@ namespace DX return CD3DX12_CPU_DESCRIPTOR_HANDLE(m_dsvDescriptorHeap->GetCPUDescriptorHandleForHeapStart()); } - // Direct3D HDR Game DVR support for Xbox One. - ID3D12Resource* GetGameDVRRenderTarget() const noexcept { return m_renderTargetsGameDVR[m_backBufferIndex].Get(); } - DXGI_FORMAT GetGameDVRFormat() const noexcept { return m_gameDVRFormat; } - - CD3DX12_CPU_DESCRIPTOR_HANDLE GetGameDVRRenderTargetView() const noexcept - { - return CD3DX12_CPU_DESCRIPTOR_HANDLE( - m_rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), - static_cast(m_backBufferCount + m_backBufferIndex), m_rtvDescriptorSize); - } - private: - void MoveToNextFrame(); void RegisterFrameEvents(); static constexpr size_t MAX_BACK_BUFFER_COUNT = 3; @@ -126,9 +122,5 @@ namespace DX // DeviceResources options (see flags above) unsigned int m_options; - - // Direct3D HDR Game DVR support for Xbox One. - Microsoft::WRL::ComPtr m_renderTargetsGameDVR[MAX_BACK_BUFFER_COUNT]; - DXGI_FORMAT m_gameDVRFormat; }; } diff --git a/Samples/Graphics/DeferredParticles/Main.cpp b/Samples/Graphics/DeferredParticles/Main.cpp index 19c385d..b7c0677 100644 --- a/Samples/Graphics/DeferredParticles/Main.cpp +++ b/Samples/Graphics/DeferredParticles/Main.cpp @@ -1,7 +1,7 @@ //-------------------------------------------------------------------------------------- // Main.cpp // -// Entry point for Microsoft GDK with Xbox extensions. +// Entry point for Microsoft GDK with Xbox extensions // // Advanced Technology Group (ATG) // Copyright (C) Microsoft Corporation. All rights reserved. @@ -18,16 +18,25 @@ using namespace DirectX; +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wcovered-switch-default" +#pragma clang diagnostic ignored "-Wswitch-enum" +#endif + +#pragma warning(disable : 4061) + namespace { std::unique_ptr g_sample; HANDLE g_plmSuspendComplete = nullptr; HANDLE g_plmSignalResume = nullptr; -}; +} bool g_HDRMode = false; LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); +void SetDisplayMode() noexcept; +void ExitSample() noexcept; // Entry point int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) @@ -58,6 +67,8 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp // Register class and create window PAPPSTATE_REGISTRATION hPLM = {}; + PAPPCONSTRAIN_REGISTRATION hPLM2 = {}; + { // Register class WNDCLASSEXA wcex = {}; @@ -66,7 +77,7 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp wcex.lpfnWndProc = WndProc; wcex.hInstance = hInstance; wcex.lpszClassName = u8"DeferredParticlesWindowClass"; - wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); + wcex.hbrBackground = reinterpret_cast(COLOR_WINDOW + 1); if (!RegisterClassExA(&wcex)) return 1; @@ -79,17 +90,7 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp ShowWindow(hwnd, nCmdShow); - if (g_sample->RequestHDRMode()) - { - // Request HDR mode. - auto result = XDisplayTryEnableHdrMode(XDisplayHdrModePreference::PreferHdr, nullptr); - - g_HDRMode = (result == XDisplayHdrModeResult::Enabled); - - #ifdef _DEBUG - OutputDebugStringA((g_HDRMode) ? "INFO: Display in HDR Mode\n" : "INFO: Display in SDR Mode\n"); - #endif - } + SetDisplayMode(); SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast(g_sample.get())); @@ -116,7 +117,7 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp PostMessage(reinterpret_cast(context), WM_USER, 0, 0); // To defer suspend, you must wait to exit this callback - (void)WaitForSingleObject(g_plmSuspendComplete, INFINITE); + std::ignore = WaitForSingleObject(g_plmSuspendComplete, INFINITE); } else { @@ -124,6 +125,13 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp } }, hwnd, &hPLM)) return 1; + + if (RegisterAppConstrainedChangeNotification([](BOOLEAN constrained, PVOID context) + { + // To ensure we use the main UI thread to process the notification, we self-post a message + SendMessage(reinterpret_cast(context), WM_USER + 1, (constrained) ? 1u : 0u, 0); + }, hwnd, &hPLM2)) + return 1; } // Main message loop @@ -144,13 +152,14 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp g_sample.reset(); UnregisterAppStateChangeNotification(hPLM); - + UnregisterAppConstrainedChangeNotification(hPLM2); + CloseHandle(g_plmSuspendComplete); CloseHandle(g_plmSignalResume); XGameRuntimeUninitialize(); - return (int) msg.wParam; + return static_cast(msg.wParam); } // Windows procedure @@ -168,18 +177,52 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) // Complete deferral SetEvent(g_plmSuspendComplete); - (void)WaitForSingleObject(g_plmSignalResume, INFINITE); + std::ignore = WaitForSingleObject(g_plmSignalResume, INFINITE); + + SetDisplayMode(); sample->OnResuming(); } break; + + case WM_USER + 1: + if (sample) + { + if (wParam) + { + sample->OnConstrained(); + } + else + { + SetDisplayMode(); + + sample->OnUnConstrained(); + } + } + break; } return DefWindowProc(hWnd, message, wParam, lParam); } +// HDR helper +void SetDisplayMode() noexcept +{ + if (g_sample && g_sample->RequestHDRMode()) + { + // Request HDR mode. + auto result = XDisplayTryEnableHdrMode(XDisplayHdrModePreference::PreferHdr, nullptr); + + g_HDRMode = (result == XDisplayHdrModeResult::Enabled); + +#ifdef _DEBUG + OutputDebugStringA((g_HDRMode) ? "INFO: Display in HDR Mode\n" : "INFO: Display in SDR Mode\n"); +#endif + } +} + // Exit helper -void ExitSample() +void ExitSample() noexcept { PostQuitMessage(0); } diff --git a/Samples/Graphics/DeferredParticles/StepTimer.h b/Samples/Graphics/DeferredParticles/StepTimer.h index bb15fff..2dbefcb 100644 --- a/Samples/Graphics/DeferredParticles/StepTimer.h +++ b/Samples/Graphics/DeferredParticles/StepTimer.h @@ -111,7 +111,7 @@ namespace DX timeDelta *= TicksPerSecond; timeDelta /= static_cast(m_qpcFrequency.QuadPart); - uint32_t lastFrameCount = m_frameCount; + const uint32_t lastFrameCount = m_frameCount; if (m_isFixedTimeStep) { diff --git a/Samples/Graphics/DeferredParticles/pch.h b/Samples/Graphics/DeferredParticles/pch.h index 5e20ee5..5da7101 100644 --- a/Samples/Graphics/DeferredParticles/pch.h +++ b/Samples/Graphics/DeferredParticles/pch.h @@ -37,8 +37,8 @@ #include -#if _GXDK_VER < 0x4A610D2B /* GXDK Edition 200600 */ -#error This sample requires the June 2020 GDK or later +#if _GXDK_VER < 0x55F007B0 /* GDK Edition 211000 */ +#error This sample requires the October 2021 GDK or later #endif #ifdef _GAMING_XBOX_SCARLETT @@ -56,14 +56,21 @@ #include #include +#include #include +#include #include +#include +#include +#include #include +#include #include #include +#include +#include +#include -#include -#include #include #include @@ -94,7 +101,7 @@ namespace DX class com_exception : public std::exception { public: - com_exception(HRESULT hr) : result(hr) {} + com_exception(HRESULT hr) noexcept : result(hr) {} const char* what() const override { diff --git a/Samples/Graphics/DynamicLOD/DeviceResources.cpp b/Samples/Graphics/DynamicLOD/DeviceResources.cpp index 7d88350..acc2c8a 100644 --- a/Samples/Graphics/DynamicLOD/DeviceResources.cpp +++ b/Samples/Graphics/DynamicLOD/DeviceResources.cpp @@ -12,7 +12,6 @@ using namespace DX; using Microsoft::WRL::ComPtr; -#ifdef _GAMING_DESKTOP #ifdef __clang__ #pragma clang diagnostic ignored "-Wcovered-switch-default" #pragma clang diagnostic ignored "-Wswitch-enum" @@ -20,6 +19,7 @@ using Microsoft::WRL::ComPtr; #pragma warning(disable : 4061) +#ifdef _GAMING_DESKTOP namespace { inline DXGI_FORMAT NoSRGB(DXGI_FORMAT fmt) noexcept @@ -39,7 +39,8 @@ namespace DeviceResources::DeviceResources( DXGI_FORMAT backBufferFormat, DXGI_FORMAT depthBufferFormat, - UINT backBufferCount) noexcept(false) : + UINT backBufferCount, + unsigned int flags) noexcept(false) : m_backBufferIndex(0), m_fenceValues{}, #ifdef _GAMING_XBOX @@ -54,6 +55,7 @@ DeviceResources::DeviceResources( m_window(nullptr), m_d3dFeatureLevel(D3D_FEATURE_LEVEL_11_0), m_outputSize{ 0, 0, 1, 1 }, + m_options(flags), m_deviceNotify(nullptr) { if (backBufferCount < 2 || backBufferCount > MAX_BACK_BUFFER_COUNT) @@ -72,7 +74,7 @@ DeviceResources::~DeviceResources() // Ensure we present a blank screen before cleaning up resources. if (m_commandQueue) { - (void)m_commandQueue->PresentX(0, nullptr, nullptr); + std::ignore = m_commandQueue->PresentX(0, nullptr, nullptr); } #endif } @@ -98,6 +100,14 @@ void DeviceResources::CreateDeviceResources() params.GraphicsScratchMemorySizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); params.ComputeScratchMemorySizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); +#if defined(_GAMING_XBOX_SCARLETT) && (_GRDK_VER >= 0x585D070E /* GXDK Edition 221000 */) + if (m_options & c_AmplificationShaders) + { + params.AmplificationShaderIndirectArgsBufferSize = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); + params.AmplificationShaderPayloadBufferSize = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); + } +#endif + HRESULT hr = D3D12XboxCreateDevice( nullptr, ¶ms, @@ -150,7 +160,7 @@ void DeviceResources::CreateDeviceResources() 80 /* IDXGISwapChain::GetContainingOutput: The swapchain's adapter does not control the output on which the swapchain's window resides. */, }; DXGI_INFO_QUEUE_FILTER filter = {}; - filter.DenyList.NumIDs = _countof(hide); + filter.DenyList.NumIDs = static_cast(std::size(hide)); filter.DenyList.pIDList = hide; dxgiInfoQueue->AddStorageFilterEntries(DXGI_DEBUG_DXGI, &filter); } @@ -196,7 +206,7 @@ void DeviceResources::CreateDeviceResources() D3D12_MESSAGE_ID_RESOURCE_BARRIER_MISMATCHING_COMMAND_LIST_TYPE, }; D3D12_INFO_QUEUE_FILTER filter = {}; - filter.DenyList.NumIDs = _countof(hide); + filter.DenyList.NumIDs = static_cast(std::size(hide)); filter.DenyList.pIDList = hide; d3dInfoQueue->AddStorageFilterEntries(&filter); } @@ -205,6 +215,9 @@ void DeviceResources::CreateDeviceResources() // Determine maximum supported feature level for this device static const D3D_FEATURE_LEVEL s_featureLevels[] = { +#if defined(NTDDI_WIN10_FE) && (NTDDI_VERSION >= NTDDI_WIN10_FE) + D3D_FEATURE_LEVEL_12_2, +#endif D3D_FEATURE_LEVEL_12_1, D3D_FEATURE_LEVEL_12_0, D3D_FEATURE_LEVEL_11_1, @@ -213,7 +226,7 @@ void DeviceResources::CreateDeviceResources() D3D12_FEATURE_DATA_FEATURE_LEVELS featLevels = { - _countof(s_featureLevels), s_featureLevels, D3D_FEATURE_LEVEL_11_0 + static_cast(std::size(s_featureLevels)), s_featureLevels, D3D_FEATURE_LEVEL_11_0 }; hr = m_d3dDevice->CheckFeatureSupport(D3D12_FEATURE_FEATURE_LEVELS, &featLevels, sizeof(featLevels)); @@ -284,7 +297,7 @@ void DeviceResources::CreateDeviceResources() m_fenceEvent.Attach(CreateEventEx(nullptr, nullptr, 0, EVENT_MODIFY_STATE | SYNCHRONIZE)); if (!m_fenceEvent.IsValid()) { - throw std::exception("CreateEvent"); + throw std::system_error(std::error_code(static_cast(GetLastError()), std::system_category()), "CreateEventEx"); } #ifdef _GAMING_XBOX @@ -297,7 +310,7 @@ void DeviceResources::CreateWindowSizeDependentResources() { if (!m_window) { - throw std::exception("Call SetWindow with a valid window handle"); + throw std::logic_error("Call SetWindow with a valid Win32 window handle"); } // Wait until all previous GPU work is complete. @@ -323,7 +336,7 @@ void DeviceResources::CreateWindowSizeDependentResources() // Obtain the back buffers for this window which will be the final render targets // and create render target views for each of them. - CD3DX12_HEAP_PROPERTIES swapChainHeapProperties(D3D12_HEAP_TYPE_DEFAULT); + const CD3DX12_HEAP_PROPERTIES swapChainHeapProperties(D3D12_HEAP_TYPE_DEFAULT); D3D12_RESOURCE_DESC swapChainBufferDesc = CD3DX12_RESOURCE_DESC::Tex2D( m_backBufferFormat, @@ -355,7 +368,7 @@ void DeviceResources::CreateWindowSizeDependentResources() rtvDesc.Format = m_backBufferFormat; rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; - CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( + const CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( m_rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), static_cast(n), m_rtvDescriptorSize); m_d3dDevice->CreateRenderTargetView(m_renderTargets[n].Get(), &rtvDesc, rtvDescriptor); @@ -366,7 +379,7 @@ void DeviceResources::CreateWindowSizeDependentResources() #else // _GAMING_DESKTOP - DXGI_FORMAT backBufferFormat = NoSRGB(m_backBufferFormat); + const DXGI_FORMAT backBufferFormat = NoSRGB(m_backBufferFormat); // If the swap chain already exists, resize it, otherwise create one. if (m_swapChain) @@ -449,7 +462,7 @@ void DeviceResources::CreateWindowSizeDependentResources() rtvDesc.Format = m_backBufferFormat; rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; - CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( + const CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( m_rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), static_cast(n), m_rtvDescriptorSize); m_d3dDevice->CreateRenderTargetView(m_renderTargets[n].Get(), &rtvDesc, rtvDescriptor); @@ -464,7 +477,7 @@ void DeviceResources::CreateWindowSizeDependentResources() { // Allocate a 2-D surface as the depth/stencil buffer and create a depth/stencil view // on this surface. - CD3DX12_HEAP_PROPERTIES depthHeapProperties(D3D12_HEAP_TYPE_DEFAULT); + const CD3DX12_HEAP_PROPERTIES depthHeapProperties(D3D12_HEAP_TYPE_DEFAULT); D3D12_RESOURCE_DESC depthStencilDesc = CD3DX12_RESOURCE_DESC::Tex2D( m_depthBufferFormat, @@ -477,7 +490,7 @@ void DeviceResources::CreateWindowSizeDependentResources() D3D12_CLEAR_VALUE depthOptimizedClearValue = {}; depthOptimizedClearValue.Format = m_depthBufferFormat; - depthOptimizedClearValue.DepthStencil.Depth = 1.0f; + depthOptimizedClearValue.DepthStencil.Depth = (m_options & c_ReverseDepth) ? 0.0f : 1.0f; depthOptimizedClearValue.DepthStencil.Stencil = 0; ThrowIfFailed(m_d3dDevice->CreateCommittedResource( @@ -588,12 +601,6 @@ void DeviceResources::HandleDeviceLost() // Prepare the command list and render target for rendering. void DeviceResources::Prepare(D3D12_RESOURCE_STATES beforeState, D3D12_RESOURCE_STATES afterState) { -#ifdef _GAMING_XBOX - // Wait until frame start is signaled - m_framePipelineToken = D3D12XBOX_FRAME_PIPELINE_TOKEN_NULL; - ThrowIfFailed(m_d3dDevice->WaitFrameEventX(D3D12XBOX_FRAME_EVENT_ORIGIN, INFINITE, nullptr, D3D12XBOX_WAIT_FRAME_EVENT_FLAG_NONE, &m_framePipelineToken)); -#endif - // Reset command list and allocator. ThrowIfFailed(m_commandAllocators[m_backBufferIndex]->Reset()); ThrowIfFailed(m_commandList->Reset(m_commandAllocators[m_backBufferIndex].Get(), nullptr)); @@ -601,7 +608,8 @@ void DeviceResources::Prepare(D3D12_RESOURCE_STATES beforeState, D3D12_RESOURCE_ if (beforeState != afterState) { // Transition the render target into the correct state to allow for drawing into it. - D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), + const D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition( + m_renderTargets[m_backBufferIndex].Get(), beforeState, afterState); m_commandList->ResourceBarrier(1, &barrier); } @@ -613,7 +621,9 @@ void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState) if (beforeState != D3D12_RESOURCE_STATE_PRESENT) { // Transition the render target to the state that allows it to be presented to the display. - D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), beforeState, D3D12_RESOURCE_STATE_PRESENT); + const D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition( + m_renderTargets[m_backBufferIndex].Get(), + beforeState, D3D12_RESOURCE_STATE_PRESENT); m_commandList->ResourceBarrier(1, &barrier); } @@ -633,9 +643,12 @@ void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState) m_commandQueue->PresentX(1, &planeParameters, nullptr) ); - // Xbox One apps do not need to handle DXGI_ERROR_DEVICE_REMOVED or DXGI_ERROR_DEVICE_RESET. + // Xbox apps do not need to handle DXGI_ERROR_DEVICE_REMOVED or DXGI_ERROR_DEVICE_RESET. -#else + // Update the back buffer index. + m_backBufferIndex = (m_backBufferIndex + 1) % m_backBufferCount; + +#else // _GAMING_DESKTOP // The first argument instructs DXGI to block until VSync, putting the application // to sleep until the next VSync. This ensures we don't waste any cycles rendering @@ -658,9 +671,8 @@ void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState) ThrowIfFailed(hr); } -#endif - MoveToNextFrame(); +#endif } // Handle GPU suspend/resume @@ -686,13 +698,13 @@ void DeviceResources::WaitForGpu() noexcept if (m_commandQueue && m_fence && m_fenceEvent.IsValid()) { // Schedule a Signal command in the GPU queue. - UINT64 fenceValue = m_fenceValues[m_backBufferIndex]; + const UINT64 fenceValue = m_fenceValues[m_backBufferIndex]; if (SUCCEEDED(m_commandQueue->Signal(m_fence.Get(), fenceValue))) { // Wait until the Signal has been processed. if (SUCCEEDED(m_fence->SetEventOnCompletion(fenceValue, m_fenceEvent.Get()))) { - WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); + std::ignore = WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); // Increment the fence value for the current frame. m_fenceValues[m_backBufferIndex]++; @@ -701,32 +713,20 @@ void DeviceResources::WaitForGpu() noexcept } } -// Prepare to render the next frame. -void DeviceResources::MoveToNextFrame() -{ - // Schedule a Signal command in the queue. - const UINT64 currentFenceValue = m_fenceValues[m_backBufferIndex]; - ThrowIfFailed(m_commandQueue->Signal(m_fence.Get(), currentFenceValue)); - - // Update the back buffer index. #ifdef _GAMING_XBOX - m_backBufferIndex = (m_backBufferIndex + 1) % m_backBufferCount; -#else - m_backBufferIndex = m_swapChain->GetCurrentBackBufferIndex(); -#endif - - // If the next frame is not ready to be rendered yet, wait until it is ready. - if (m_fence->GetCompletedValue() < m_fenceValues[m_backBufferIndex]) - { - ThrowIfFailed(m_fence->SetEventOnCompletion(m_fenceValues[m_backBufferIndex], m_fenceEvent.Get())); - WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); - } - - // Set the fence value for the next frame. - m_fenceValues[m_backBufferIndex] = currentFenceValue + 1; +// For PresentX rendering, we should wait for the origin event just before processing input. +void DeviceResources::WaitForOrigin() +{ + // Wait until frame start is signaled + m_framePipelineToken = D3D12XBOX_FRAME_PIPELINE_TOKEN_NULL; + ThrowIfFailed(m_d3dDevice->WaitFrameEventX( + D3D12XBOX_FRAME_EVENT_ORIGIN, + INFINITE, + nullptr, + D3D12XBOX_WAIT_FRAME_EVENT_FLAG_NONE, + &m_framePipelineToken)); } -#ifdef _GAMING_XBOX // Set frame interval and register for frame events void DeviceResources::RegisterFrameEvents() { @@ -755,7 +755,30 @@ void DeviceResources::RegisterFrameEvents() nullptr, D3D12XBOX_SCHEDULE_FRAME_EVENT_FLAG_NONE)); } -#else + +#else // _GAMING_DESKTOP + +// Prepare to render the next frame. +void DeviceResources::MoveToNextFrame() +{ + // Schedule a Signal command in the queue. + const UINT64 currentFenceValue = m_fenceValues[m_backBufferIndex]; + ThrowIfFailed(m_commandQueue->Signal(m_fence.Get(), currentFenceValue)); + + // Update the back buffer index. + m_backBufferIndex = m_swapChain->GetCurrentBackBufferIndex(); + + // If the next frame is not ready to be rendered yet, wait until it is ready. + if (m_fence->GetCompletedValue() < m_fenceValues[m_backBufferIndex]) + { + ThrowIfFailed(m_fence->SetEventOnCompletion(m_fenceValues[m_backBufferIndex], m_fenceEvent.Get())); + std::ignore = WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); + } + + // Set the fence value for the next frame. + m_fenceValues[m_backBufferIndex] = currentFenceValue + 1; +} + // This method acquires the first available hardware adapter that supports Direct3D 12. // If no such adapter can be found, try WARP. Otherwise throw an exception. void DeviceResources::GetAdapter(IDXGIAdapter1** ppAdapter) @@ -797,7 +820,7 @@ void DeviceResources::GetAdapter(IDXGIAdapter1** ppAdapter) // Try WARP12 instead if (FAILED(m_dxgiFactory->EnumWarpAdapter(IID_PPV_ARGS(adapter.ReleaseAndGetAddressOf())))) { - throw std::exception("WARP12 not available. Enable the 'Graphics Tools' optional feature"); + throw std::runtime_error("WARP12 not available. Enable the 'Graphics Tools' optional feature"); } OutputDebugStringA("Direct3D Adapter - WARP12\n"); @@ -806,7 +829,7 @@ void DeviceResources::GetAdapter(IDXGIAdapter1** ppAdapter) if (!adapter) { - throw std::exception("No Direct3D 12 device found"); + throw std::runtime_error("No Direct3D 12 device found"); } *ppAdapter = adapter.Detach(); diff --git a/Samples/Graphics/DynamicLOD/DeviceResources.h b/Samples/Graphics/DynamicLOD/DeviceResources.h index a920f13..0e0aeb0 100644 --- a/Samples/Graphics/DynamicLOD/DeviceResources.h +++ b/Samples/Graphics/DynamicLOD/DeviceResources.h @@ -22,9 +22,13 @@ namespace DX class DeviceResources { public: + static constexpr unsigned int c_ReverseDepth = 0x1; + static constexpr unsigned int c_AmplificationShaders = 0x2; + DeviceResources(DXGI_FORMAT backBufferFormat = DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT depthBufferFormat = DXGI_FORMAT_D32_FLOAT, - UINT backBufferCount = 2) noexcept(false); + UINT backBufferCount = 2, + unsigned int flags = 0) noexcept(false); ~DeviceResources(); DeviceResources(DeviceResources&&) = default; @@ -45,6 +49,9 @@ namespace DX void Suspend(); void Resume(); void WaitForGpu() noexcept; +#ifdef _GAMING_XBOX + void WaitForOrigin(); +#endif // Device Accessors. RECT GetOutputSize() const noexcept { return m_outputSize; } @@ -68,6 +75,7 @@ namespace DX D3D12_RECT GetScissorRect() const noexcept { return m_scissorRect; } UINT GetCurrentFrameIndex() const noexcept { return m_backBufferIndex; } UINT GetBackBufferCount() const noexcept { return m_backBufferCount; } + unsigned int GetDeviceOptions() const noexcept { return m_options; } CD3DX12_CPU_DESCRIPTOR_HANDLE GetRenderTargetView() const noexcept { @@ -81,10 +89,10 @@ namespace DX } private: - void MoveToNextFrame(); #ifdef _GAMING_XBOX void RegisterFrameEvents(); #else + void MoveToNextFrame(); void GetAdapter(IDXGIAdapter1** ppAdapter); #endif @@ -131,6 +139,9 @@ namespace DX D3D_FEATURE_LEVEL m_d3dFeatureLevel; RECT m_outputSize; + // DeviceResources options (see flags above) + unsigned int m_options; + // The IDeviceNotify can be held directly as it owns the DeviceResources. IDeviceNotify* m_deviceNotify; }; diff --git a/Samples/Graphics/DynamicLOD/DynamicLOD.cpp b/Samples/Graphics/DynamicLOD/DynamicLOD.cpp index e42c2ba..279a971 100644 --- a/Samples/Graphics/DynamicLOD/DynamicLOD.cpp +++ b/Samples/Graphics/DynamicLOD/DynamicLOD.cpp @@ -14,7 +14,7 @@ #pragma warning( disable : 4324 4365 ) -extern void ExitSample(); +extern void ExitSample() noexcept; using namespace ATG; using namespace DirectX; @@ -27,15 +27,15 @@ namespace //-------------------------------------- // Definitions - static const wchar_t* s_sampleTitle = L"Dynamic LOD"; + const wchar_t* s_sampleTitle = L"Dynamic LOD"; - static const wchar_t* s_ampShaderFilename = L"DynamicLodAS.cso"; - static const wchar_t* s_meshShaderFilename = L"InstancedLodMS.cso"; - static const wchar_t* s_pixelShaderFilename = L"BasicMeshletPS.cso"; + const wchar_t* s_ampShaderFilename = L"DynamicLodAS.cso"; + const wchar_t* s_meshShaderFilename = L"InstancedLodMS.cso"; + const wchar_t* s_pixelShaderFilename = L"BasicMeshletPS.cso"; - static const wchar_t* s_lodFilenames[] = + const wchar_t* s_lodFilenames[] = { -#if _GAMING_DESKTOP +#ifdef _GAMING_DESKTOP L"ATGDragon\\Dragon_LOD0.sdkmesh", L"ATGDragon\\Dragon_LOD1.sdkmesh", L"ATGDragon\\Dragon_LOD2.sdkmesh", @@ -53,9 +53,9 @@ namespace }; #ifdef _GAMING_XBOX_SCARLETT - const uint32_t c_textureDataPitchAlign = D3D12XBOX_TEXTURE_DATA_PITCH_ALIGNMENT; + constexpr uint32_t c_textureDataPitchAlign = D3D12XBOX_TEXTURE_DATA_PITCH_ALIGNMENT; #else - const uint32_t c_textureDataPitchAlign = D3D12_TEXTURE_DATA_PITCH_ALIGNMENT; + constexpr uint32_t c_textureDataPitchAlign = D3D12_TEXTURE_DATA_PITCH_ALIGNMENT; #endif const HelpButtonAssignment c_buttonAssignment[] = @@ -72,9 +72,9 @@ namespace { HelpID::VIEW_BUTTON, L"Exit Sample" }, }; - constexpr float s_fovy = XM_PIDIV4; - constexpr uint32_t s_maxGroupsPerDispatch = 65536; - constexpr uint32_t s_maxInstances = AS_GROUP_SIZE * s_maxGroupsPerDispatch; + constexpr float c_fovy = XM_PIDIV4; + constexpr uint32_t c_maxGroupsPerDispatch = 65536; + constexpr uint32_t c_maxInstances = AS_GROUP_SIZE * c_maxGroupsPerDispatch; // Resource view offsets into the descriptor heap // The mesh & meshlet SRVs are laid out as they are referenced in shader code @@ -117,8 +117,7 @@ namespace } Sample::Sample() noexcept(false) - : m_deviceResources(std::make_unique()) - , m_displayWidth(0) + : m_displayWidth(0) , m_displayHeight(0) , m_frame(0) , m_uploadInstances(false) @@ -129,6 +128,9 @@ Sample::Sample() noexcept(false) , m_forceLod0(false) , m_renderHelp(false) { + m_deviceResources = std::make_unique( + DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_D32_FLOAT, 2, + DX::DeviceResources::c_AmplificationShaders); m_deviceResources->RegisterDeviceNotify(this); } @@ -163,6 +165,10 @@ void Sample::Tick() { PIXBeginEvent(PIX_COLOR_DEFAULT, L"Frame %llu", m_frame); +#ifdef _GAMING_XBOX + m_deviceResources->WaitForOrigin(); +#endif + m_timer.Tick([&]() { Update(m_timer); @@ -348,16 +354,16 @@ void Sample::Clear() PIXBeginEvent(commandList, PIX_COLOR_DEFAULT, L"Clear"); // Clear the views. - auto rtvDescriptor = m_deviceResources->GetRenderTargetView(); - auto dsvDescriptor = m_deviceResources->GetDepthStencilView(); + auto const rtvDescriptor = m_deviceResources->GetRenderTargetView(); + auto const dsvDescriptor = m_deviceResources->GetDepthStencilView(); commandList->OMSetRenderTargets(1, &rtvDescriptor, FALSE, &dsvDescriptor); commandList->ClearRenderTargetView(rtvDescriptor, ATG::Colors::Background, 0, nullptr); commandList->ClearDepthStencilView(dsvDescriptor, D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr); // Set the viewport and scissor rect. - auto viewport = m_deviceResources->GetScreenViewport(); - auto scissorRect = m_deviceResources->GetScissorRect(); + auto const viewport = m_deviceResources->GetScreenViewport(); + auto const scissorRect = m_deviceResources->GetScissorRect(); commandList->RSSetViewports(1, &viewport); commandList->RSSetScissorRects(1, &scissorRect); @@ -378,7 +384,7 @@ void Sample::UpdateConstants(ID3D12GraphicsCommandList* commandList) XMStoreFloat4x4(&constants.View, XMMatrixTranspose(view)); XMStoreFloat4x4(&constants.ViewProj, XMMatrixTranspose(viewProj)); constants.RenderMode = m_renderMode; - constants.RecipTanHalfFovy = 1.0f / std::tanf(s_fovy * 0.5f); + constants.RecipTanHalfFovy = 1.0f / std::tanf(c_fovy * 0.5f); constants.LODCount = static_cast(m_lods.size()); constants.ForceLOD0 = m_forceLod0; constants.ForceVisible = m_forceVisible; @@ -568,7 +574,7 @@ void Sample::OnResuming() void Sample::OnWindowMoved() { - auto r = m_deviceResources->GetOutputSize(); + auto const r = m_deviceResources->GetOutputSize(); m_deviceResources->WindowSizeChanged(r.right, r.bottom); } @@ -581,7 +587,7 @@ void Sample::OnWindowSizeChanged(int width, int height) } // Properties -void Sample::GetDefaultSize(int& width, int& height) const +void Sample::GetDefaultSize(int& width, int& height) const noexcept { width = 1280; height = 720; @@ -592,7 +598,7 @@ void Sample::GetDefaultSize(int& width, int& height) const // These are the resources that depend on the device. void Sample::CreateDeviceDependentResources() { - auto device = static_cast(m_deviceResources->GetD3DDevice()); + auto device = m_deviceResources->GetD3DDevice(); // Check for Shader Model 6.5 and Mesh Shader feature support #ifdef _GAMING_DESKTOP @@ -722,7 +728,7 @@ void Sample::CreateDeviceDependentResources() } // Create our HUD objects - auto backBufferRts = RenderTargetState(m_deviceResources->GetBackBufferFormat(), m_deviceResources->GetDepthBufferFormat()); + const RenderTargetState backBufferRts(m_deviceResources->GetBackBufferFormat(), m_deviceResources->GetDepthBufferFormat()); m_controlHelp->RestoreDevice(device, resourceUpload, backBufferRts); auto spritePSD = SpriteBatchPipelineStateDescription(backBufferRts, &CommonStates::AlphaBlend); @@ -842,7 +848,7 @@ void Sample::CreateWindowSizeDependentResources() m_hudBatch->SetViewport(m_deviceResources->GetScreenViewport()); m_camera.SetWindow(m_displayWidth, m_displayHeight); - m_camera.SetProjectionParameters(s_fovy, 1.0f, 10000.0f, true); + m_camera.SetProjectionParameters(c_fovy, 1.0f, 10000.0f, true); m_camera.SetLookAt(Vector3(-200.0f, 200.0f, -200.0f), Vector3::Up * 40.0f); m_camera.SetSensitivity(500.0f, 100.0f, 1000.0f, 10.0f); @@ -964,9 +970,9 @@ void Sample::RegenerateInstances() } // Limit instance count to the number of instances - if (m_instances.size() > s_maxInstances) + if (m_instances.size() > c_maxInstances) { - m_instances.resize(s_maxInstances); + m_instances.resize(c_maxInstances); } // Only recreate instance-sized buffers if necessary. diff --git a/Samples/Graphics/DynamicLOD/DynamicLOD.h b/Samples/Graphics/DynamicLOD/DynamicLOD.h index 43c9472..9dae17f 100644 --- a/Samples/Graphics/DynamicLOD/DynamicLOD.h +++ b/Samples/Graphics/DynamicLOD/DynamicLOD.h @@ -38,7 +38,7 @@ public: void OnWindowSizeChanged(int width, int height); // Properties - void GetDefaultSize(int& width, int& height) const; + void GetDefaultSize(int& width, int& height) const noexcept; private: void Update(DX::StepTimer const& timer); diff --git a/Samples/Graphics/DynamicLOD/Main.cpp b/Samples/Graphics/DynamicLOD/Main.cpp index e22ea65..9eed737 100644 --- a/Samples/Graphics/DynamicLOD/Main.cpp +++ b/Samples/Graphics/DynamicLOD/Main.cpp @@ -20,6 +20,13 @@ using namespace DirectX; +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wcovered-switch-default" +#pragma clang diagnostic ignored "-Wswitch-enum" +#endif + +#pragma warning(disable : 4061) + namespace { std::unique_ptr g_sample; @@ -27,11 +34,12 @@ namespace HANDLE g_plmSuspendComplete = nullptr; HANDLE g_plmSignalResume = nullptr; #endif -}; +} LPCWSTR g_szAppName = L"Dynamic LOD"; LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); +void ExitSample() noexcept; // Entry point int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) @@ -55,7 +63,7 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp if (hr == E_GAMERUNTIME_DLL_NOT_FOUND || hr == E_GAMERUNTIME_VERSION_MISMATCH) { #ifdef _GAMING_DESKTOP - (void)MessageBoxW(nullptr, L"Game Runtime is not installed on this system or needs updating.", g_szAppName, MB_ICONERROR | MB_OK); + std::ignore = MessageBoxW(nullptr, L"Game Runtime is not installed on this system or needs updating.", g_szAppName, MB_ICONERROR | MB_OK); #endif } return 1; @@ -80,7 +88,7 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp wcex.lpfnWndProc = WndProc; wcex.hInstance = hInstance; wcex.hCursor = LoadCursorW(nullptr, IDC_ARROW); - wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); + wcex.hbrBackground = reinterpret_cast(COLOR_WINDOW + 1); wcex.lpszClassName = L"DynamicLODWindowClass"; if (!RegisterClassExW(&wcex)) return 1; @@ -134,7 +142,7 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp PostMessage(reinterpret_cast(context), WM_USER, 0, 0); // To defer suspend, you must wait to exit this callback - (void)WaitForSingleObject(g_plmSuspendComplete, INFINITE); + std::ignore = WaitForSingleObject(g_plmSuspendComplete, INFINITE); } else { @@ -164,14 +172,14 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp #ifdef _GAMING_XBOX UnregisterAppStateChangeNotification(hPLM); - + CloseHandle(g_plmSuspendComplete); CloseHandle(g_plmSignalResume); #endif XGameRuntimeUninitialize(); - return (int) msg.wParam; + return static_cast(msg.wParam); } // Windows procedure @@ -232,7 +240,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) // Complete deferral SetEvent(g_plmSuspendComplete); - (void)WaitForSingleObject(g_plmSignalResume, INFINITE); + std::ignore = WaitForSingleObject(g_plmSignalResume, INFINITE); sample->OnResuming(); } @@ -246,7 +254,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) else { PAINTSTRUCT ps; - (void)BeginPaint(hWnd, &ps); + std::ignore = BeginPaint(hWnd, &ps); EndPaint(hWnd, &ps); } break; @@ -298,12 +306,13 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) break; case WM_GETMINMAXINFO: - { - auto info = reinterpret_cast(lParam); - info->ptMinTrackSize.x = 320; - info->ptMinTrackSize.y = 200; - } - break; + if (lParam) + { + auto info = reinterpret_cast(lParam); + info->ptMinTrackSize.x = 320; + info->ptMinTrackSize.y = 200; + } + break; case WM_POWERBROADCAST: switch (wParam) @@ -360,7 +369,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) } else { - SetWindowLongPtr(hWnd, GWL_STYLE, 0); + SetWindowLongPtr(hWnd, GWL_STYLE, WS_POPUP); SetWindowLongPtr(hWnd, GWL_EXSTYLE, WS_EX_TOPMOST); SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); @@ -388,7 +397,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) } // Exit helper -void ExitSample() +void ExitSample() noexcept { PostQuitMessage(0); } diff --git a/Samples/Graphics/DynamicLOD/StepTimer.h b/Samples/Graphics/DynamicLOD/StepTimer.h index bb15fff..2dbefcb 100644 --- a/Samples/Graphics/DynamicLOD/StepTimer.h +++ b/Samples/Graphics/DynamicLOD/StepTimer.h @@ -111,7 +111,7 @@ namespace DX timeDelta *= TicksPerSecond; timeDelta /= static_cast(m_qpcFrequency.QuadPart); - uint32_t lastFrameCount = m_frameCount; + const uint32_t lastFrameCount = m_frameCount; if (m_isFixedTimeStep) { diff --git a/Samples/Graphics/DynamicLOD/pch.h b/Samples/Graphics/DynamicLOD/pch.h index 6f655b9..9c637d7 100644 --- a/Samples/Graphics/DynamicLOD/pch.h +++ b/Samples/Graphics/DynamicLOD/pch.h @@ -41,14 +41,13 @@ #include -#if _GRDK_VER < 0x4A610D2B /* GXDK Edition 200600 */ -#error This sample requires the June 2020 GDK or later +#if _GRDK_VER < 0x55F007B0 /* GDK Edition 211000 */ +#error This sample requires the October 2021 GDK or later #endif #ifdef _GAMING_XBOX_SCARLETT #include #include - #elif defined(_GAMING_XBOX) #error This sample does not support Xbox One #else @@ -69,17 +68,23 @@ #include #include +#include #include +#include #include +#include +#include +#include #include +#include #include #include #include #include #include - -#include -#include +#include +#include +#include #ifdef _GAMING_XBOX #include @@ -122,9 +127,9 @@ namespace DX class com_exception : public std::exception { public: - com_exception(HRESULT hr) : result(hr) {} + com_exception(HRESULT hr) noexcept : result(hr) {} - virtual const char* what() const override + const char* what() const override { static char s_str[64] = {}; sprintf_s(s_str, "Failure with HRESULT of %08X", static_cast(result)); diff --git a/Samples/Graphics/ExecuteIndirect/DeviceResources.cpp b/Samples/Graphics/ExecuteIndirect/DeviceResources.cpp index d76fd63..fa67816 100644 --- a/Samples/Graphics/ExecuteIndirect/DeviceResources.cpp +++ b/Samples/Graphics/ExecuteIndirect/DeviceResources.cpp @@ -31,14 +31,13 @@ DeviceResources::DeviceResources( m_rtvDescriptorSize(0), m_screenViewport{}, m_scissorRect{}, - m_backBufferFormat((flags & c_EnableHDR) ? DXGI_FORMAT_R10G10B10A2_UNORM : backBufferFormat), + m_backBufferFormat(backBufferFormat), m_depthBufferFormat(depthBufferFormat), m_backBufferCount(backBufferCount), m_window(nullptr), m_d3dFeatureLevel(D3D_FEATURE_LEVEL_12_0), m_outputSize{0, 0, 1920, 1080}, - m_options(flags), - m_gameDVRFormat((flags & c_EnableHDR) ? backBufferFormat : DXGI_FORMAT_UNKNOWN) + m_options(flags) { if (backBufferCount < 2 || backBufferCount > MAX_BACK_BUFFER_COUNT) { @@ -55,12 +54,16 @@ DeviceResources::~DeviceResources() // Ensure we present a blank screen before cleaning up resources. if (m_commandQueue) { - (void)m_commandQueue->PresentX(0, nullptr, nullptr); + std::ignore = m_commandQueue->PresentX(0, nullptr, nullptr); } } // Configures the Direct3D device, and stores handles to it and the device context. +#ifdef _GAMING_XBOX_SCARLETT +void DeviceResources::CreateDeviceResources(D3D12XBOX_CREATE_DEVICE_FLAGS createDeviceFlags) +#else void DeviceResources::CreateDeviceResources() +#endif { // Create the DX12 API device object. D3D12XBOX_CREATE_DEVICE_PARAMETERS params = {}; @@ -77,6 +80,17 @@ void DeviceResources::CreateDeviceResources() params.GraphicsCommandQueueRingSizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); params.GraphicsScratchMemorySizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); params.ComputeScratchMemorySizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); +#ifdef _GAMING_XBOX_SCARLETT + params.CreateDeviceFlags = createDeviceFlags; + +#if (_GXDK_VER >= 0x585D070E /* GXDK Edition 221000 */) + if (m_options & c_AmplificationShaders) + { + params.AmplificationShaderIndirectArgsBufferSize = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); + params.AmplificationShaderPayloadBufferSize = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); + } +#endif +#endif HRESULT hr = D3D12XboxCreateDevice( nullptr, @@ -107,7 +121,7 @@ void DeviceResources::CreateDeviceResources() // Create descriptor heaps for render target views and depth stencil views. D3D12_DESCRIPTOR_HEAP_DESC rtvDescriptorHeapDesc = {}; - rtvDescriptorHeapDesc.NumDescriptors = (m_options & c_EnableHDR) ? (m_backBufferCount * 2) : m_backBufferCount; + rtvDescriptorHeapDesc.NumDescriptors = m_backBufferCount; rtvDescriptorHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; ThrowIfFailed(m_d3dDevice->CreateDescriptorHeap(&rtvDescriptorHeapDesc, IID_GRAPHICS_PPV_ARGS(m_rtvDescriptorHeap.ReleaseAndGetAddressOf()))); @@ -152,31 +166,46 @@ void DeviceResources::CreateDeviceResources() m_fenceEvent.Attach(CreateEventEx(nullptr, nullptr, 0, EVENT_MODIFY_STATE | SYNCHRONIZE)); if (!m_fenceEvent.IsValid()) { - throw std::exception("CreateEvent"); + throw std::system_error(std::error_code(static_cast(GetLastError()), std::system_category()), "CreateEventEx"); } - if (m_options & c_Enable4K_UHD) + if (m_options & (c_Enable4K_UHD | c_EnableQHD)) { switch (XSystemGetDeviceType()) { case XSystemDeviceType::XboxOne: case XSystemDeviceType::XboxOneS: - case XSystemDeviceType::XboxScarlettLockhart /* Xbox Series S */: - m_options &= ~c_Enable4K_UHD; -#ifdef _DEBUG - OutputDebugStringA("INFO: Swapchain using 1080p (1920 x 1080)\n"); -#endif + m_options &= ~(c_Enable4K_UHD | c_EnableQHD); break; + case XSystemDeviceType::XboxScarlettLockhart /* Xbox Series S */: + m_options &= ~c_Enable4K_UHD; + if (m_options & c_EnableQHD) + { + m_outputSize = { 0, 0, 2560, 1440 }; + } + break; + + case XSystemDeviceType::XboxScarlettAnaconda /* Xbox Series X */: + case XSystemDeviceType::XboxOneXDevkit: + case XSystemDeviceType::XboxScarlettDevkit: default: - m_outputSize = { 0, 0, 3840, 2160 }; -#ifdef _DEBUG - OutputDebugStringA("INFO: Swapchain using 4k (3840 x 2160)\n"); -#endif + m_outputSize = (m_options & c_Enable4K_UHD) ? RECT{ 0, 0, 3840, 2160 } : RECT{ 0, 0, 2560, 1440 }; break; } } +#ifdef _DEBUG + const char* info = nullptr; + switch (m_outputSize.bottom) + { + case 2160: info = "INFO: Swapchain using 4k (3840 x 2160)\n"; break; + case 1440: info = "INFO: Swapchain using 1440p (2560 x 1440)\n"; break; + default: info = "INFO: Swapchain using 1080p (1920 x 1080)\n"; break; + } + OutputDebugStringA(info); +#endif + RegisterFrameEvents(); } @@ -185,7 +214,7 @@ void DeviceResources::CreateWindowSizeDependentResources() { if (!m_window) { - throw std::exception("Call SetWindow with a valid window handle"); + throw std::logic_error("Call SetWindow with a valid Win32 window handle"); } // Wait until all previous GPU work is complete. @@ -198,7 +227,6 @@ void DeviceResources::CreateWindowSizeDependentResources() for (UINT n = 0; n < m_backBufferCount; n++) { m_renderTargets[n].Reset(); - m_renderTargetsGameDVR[n].Reset(); m_fenceValues[n] = m_fenceValues[m_backBufferIndex]; } @@ -208,7 +236,7 @@ void DeviceResources::CreateWindowSizeDependentResources() // Obtain the back buffers for this window which will be the final render targets // and create render target views for each of them. - CD3DX12_HEAP_PROPERTIES swapChainHeapProperties(D3D12_HEAP_TYPE_DEFAULT); + const CD3DX12_HEAP_PROPERTIES swapChainHeapProperties(D3D12_HEAP_TYPE_DEFAULT); D3D12_RESOURCE_DESC swapChainBufferDesc = CD3DX12_RESOURCE_DESC::Tex2D( m_backBufferFormat, @@ -219,6 +247,13 @@ void DeviceResources::CreateWindowSizeDependentResources() ); swapChainBufferDesc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; +#ifdef _GAMING_XBOX_XBOXONE + if (m_options & c_EnableHDR) + { + swapChainBufferDesc.Flags |= D3D12XBOX_RESOURCE_FLAG_ALLOW_AUTOMATIC_GAMEDVR_TONE_MAP; + } +#endif + D3D12_CLEAR_VALUE swapChainOptimizedClearValue = {}; swapChainOptimizedClearValue.Format = m_backBufferFormat; @@ -240,41 +275,12 @@ void DeviceResources::CreateWindowSizeDependentResources() rtvDesc.Format = m_backBufferFormat; rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; - CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( + const CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( m_rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), static_cast(n), m_rtvDescriptorSize); m_d3dDevice->CreateRenderTargetView(m_renderTargets[n].Get(), &rtvDesc, rtvDescriptor); } - if (m_options & c_EnableHDR) - { - swapChainBufferDesc.Format = swapChainOptimizedClearValue.Format = m_gameDVRFormat; - - for (UINT n = 0; n < m_backBufferCount; n++) - { - ThrowIfFailed(m_d3dDevice->CreateCommittedResource( - &swapChainHeapProperties, - D3D12_HEAP_FLAG_ALLOW_DISPLAY, - &swapChainBufferDesc, - D3D12_RESOURCE_STATE_PRESENT, - &swapChainOptimizedClearValue, - IID_GRAPHICS_PPV_ARGS(m_renderTargetsGameDVR[n].GetAddressOf()))); - - wchar_t name[25] = {}; - swprintf_s(name, L"GameDVR Render target %u", n); - m_renderTargetsGameDVR[n]->SetName(name); - - D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {}; - rtvDesc.Format = m_gameDVRFormat; - rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; - - CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptorGameDVR( - m_rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), - static_cast(m_backBufferCount + n), m_rtvDescriptorSize); - m_d3dDevice->CreateRenderTargetView(m_renderTargetsGameDVR[n].Get(), &rtvDesc, rtvDescriptorGameDVR); - } - } - // Reset the index to the current back buffer. m_backBufferIndex = 0; @@ -282,7 +288,7 @@ void DeviceResources::CreateWindowSizeDependentResources() { // Allocate a 2-D surface as the depth/stencil buffer and create a depth/stencil view // on this surface. - CD3DX12_HEAP_PROPERTIES depthHeapProperties(D3D12_HEAP_TYPE_DEFAULT); + const CD3DX12_HEAP_PROPERTIES depthHeapProperties(D3D12_HEAP_TYPE_DEFAULT); D3D12_RESOURCE_DESC depthStencilDesc = CD3DX12_RESOURCE_DESC::Tex2D( m_depthBufferFormat, @@ -295,7 +301,7 @@ void DeviceResources::CreateWindowSizeDependentResources() D3D12_CLEAR_VALUE depthOptimizedClearValue = {}; depthOptimizedClearValue.Format = m_depthBufferFormat; - depthOptimizedClearValue.DepthStencil.Depth = 1.0f; + depthOptimizedClearValue.DepthStencil.Depth = (m_options & c_ReverseDepth) ? 0.0f : 1.0f; depthOptimizedClearValue.DepthStencil.Stencil = 0; ThrowIfFailed(m_d3dDevice->CreateCommittedResource( @@ -331,10 +337,6 @@ void DeviceResources::CreateWindowSizeDependentResources() // Prepare the command list and render target for rendering. void DeviceResources::Prepare(D3D12_RESOURCE_STATES beforeState, D3D12_RESOURCE_STATES afterState) { - // Wait until frame start is signaled - m_framePipelineToken = D3D12XBOX_FRAME_PIPELINE_TOKEN_NULL; - ThrowIfFailed(m_d3dDevice->WaitFrameEventX(D3D12XBOX_FRAME_EVENT_ORIGIN, INFINITE, nullptr, D3D12XBOX_WAIT_FRAME_EVENT_FLAG_NONE, &m_framePipelineToken)); - // Reset command list and allocator. ThrowIfFailed(m_commandAllocators[m_backBufferIndex]->Reset()); ThrowIfFailed(m_commandList->Reset(m_commandAllocators[m_backBufferIndex].Get(), nullptr)); @@ -342,23 +344,10 @@ void DeviceResources::Prepare(D3D12_RESOURCE_STATES beforeState, D3D12_RESOURCE_ if (beforeState != afterState) { // Transition the render target into the correct state to allow for drawing into it. - if (m_options & c_EnableHDR) - { - D3D12_RESOURCE_BARRIER barriers[2] = - { - CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), - beforeState, afterState), - CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargetsGameDVR[m_backBufferIndex].Get(), - beforeState, afterState), - }; - m_commandList->ResourceBarrier(_countof(barriers), barriers); - } - else - { - D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), - beforeState, afterState); - m_commandList->ResourceBarrier(1, &barrier); - } + const D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition( + m_renderTargets[m_backBufferIndex].Get(), + beforeState, afterState); + m_commandList->ResourceBarrier(1, &barrier); } } @@ -368,20 +357,10 @@ void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState, _In_opt_ const if (beforeState != D3D12_RESOURCE_STATE_PRESENT) { // Transition the render target to the state that allows it to be presented to the display. - if (m_options & c_EnableHDR) - { - D3D12_RESOURCE_BARRIER barriers[2] = - { - CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), beforeState, D3D12_RESOURCE_STATE_PRESENT), - CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargetsGameDVR[m_backBufferIndex].Get(), beforeState, D3D12_RESOURCE_STATE_PRESENT), - }; - m_commandList->ResourceBarrier(_countof(barriers), barriers); - } - else - { - D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), beforeState, D3D12_RESOURCE_STATE_PRESENT); - m_commandList->ResourceBarrier(1, &barrier); - } + const D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition( + m_renderTargets[m_backBufferIndex].Get(), + beforeState, D3D12_RESOURCE_STATE_PRESENT); + m_commandList->ResourceBarrier(1, &barrier); } // Send the command list off to the GPU for processing. @@ -389,32 +368,24 @@ void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState, _In_opt_ const m_commandQueue->ExecuteCommandLists(1, CommandListCast(m_commandList.GetAddressOf())); // Present the backbuffer using the PresentX API. - D3D12XBOX_PRESENT_PLANE_PARAMETERS planeParameters[2] = {}; - planeParameters[0].Token = planeParameters[1].Token = m_framePipelineToken; - planeParameters[0].ResourceCount = planeParameters[1].ResourceCount = 1; - planeParameters[0].ppResources = m_renderTargets[m_backBufferIndex].GetAddressOf(); + D3D12XBOX_PRESENT_PLANE_PARAMETERS planeParameters = {}; + planeParameters.Token = m_framePipelineToken; + planeParameters.ResourceCount = 1; + planeParameters.ppResources = m_renderTargets[m_backBufferIndex].GetAddressOf(); if (m_options & c_EnableHDR) { - planeParameters[0].pSrcViewRects = planeParameters[1].pSrcViewRects = &m_outputSize; - planeParameters[0].ColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020; - planeParameters[1].ppResources = m_renderTargetsGameDVR[m_backBufferIndex].GetAddressOf(); - planeParameters[1].ColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; - - ThrowIfFailed( - m_commandQueue->PresentX(2, planeParameters, params) - ); - } - else - { - ThrowIfFailed( - m_commandQueue->PresentX(1, planeParameters, params) - ); + planeParameters.ColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020; } - // Xbox One apps do not need to handle DXGI_ERROR_DEVICE_REMOVED or DXGI_ERROR_DEVICE_RESET. + ThrowIfFailed( + m_commandQueue->PresentX(1, &planeParameters, params) + ); - MoveToNextFrame(); + // Xbox apps do not need to handle DXGI_ERROR_DEVICE_REMOVED or DXGI_ERROR_DEVICE_RESET. + + // Update the back buffer index. + m_backBufferIndex = (m_backBufferIndex + 1) % m_backBufferCount; } // Handle GPU suspend/resume @@ -436,13 +407,13 @@ void DeviceResources::WaitForGpu() noexcept if (m_commandQueue && m_fence && m_fenceEvent.IsValid()) { // Schedule a Signal command in the GPU queue. - UINT64 fenceValue = m_fenceValues[m_backBufferIndex]; + const UINT64 fenceValue = m_fenceValues[m_backBufferIndex]; if (SUCCEEDED(m_commandQueue->Signal(m_fence.Get(), fenceValue))) { // Wait until the Signal has been processed. if (SUCCEEDED(m_fence->SetEventOnCompletion(fenceValue, m_fenceEvent.Get()))) { - WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); + std::ignore = WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); // Increment the fence value for the current frame. m_fenceValues[m_backBufferIndex]++; @@ -451,25 +422,17 @@ void DeviceResources::WaitForGpu() noexcept } } -// Prepare to render the next frame. -void DeviceResources::MoveToNextFrame() +// For PresentX rendering, we should wait for the origin event just before processing input. +void DeviceResources::WaitForOrigin() { - // Schedule a Signal command in the queue. - const UINT64 currentFenceValue = m_fenceValues[m_backBufferIndex]; - ThrowIfFailed(m_commandQueue->Signal(m_fence.Get(), currentFenceValue)); - - // Update the back buffer index. - m_backBufferIndex = (m_backBufferIndex + 1) % m_backBufferCount; - - // If the next frame is not ready to be rendered yet, wait until it is ready. - if (m_fence->GetCompletedValue() < m_fenceValues[m_backBufferIndex]) - { - ThrowIfFailed(m_fence->SetEventOnCompletion(m_fenceValues[m_backBufferIndex], m_fenceEvent.Get())); - WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); - } - - // Set the fence value for the next frame. - m_fenceValues[m_backBufferIndex] = currentFenceValue + 1; + // Wait until frame start is signaled + m_framePipelineToken = D3D12XBOX_FRAME_PIPELINE_TOKEN_NULL; + ThrowIfFailed(m_d3dDevice->WaitFrameEventX( + D3D12XBOX_FRAME_EVENT_ORIGIN, + INFINITE, + nullptr, + D3D12XBOX_WAIT_FRAME_EVENT_FLAG_NONE, + &m_framePipelineToken)); } // Set frame interval and register for frame events diff --git a/Samples/Graphics/ExecuteIndirect/DeviceResources.h b/Samples/Graphics/ExecuteIndirect/DeviceResources.h index 1d26b17..4924894 100644 --- a/Samples/Graphics/ExecuteIndirect/DeviceResources.h +++ b/Samples/Graphics/ExecuteIndirect/DeviceResources.h @@ -10,8 +10,11 @@ namespace DX class DeviceResources { public: - static constexpr unsigned int c_Enable4K_UHD = 0x1; - static constexpr unsigned int c_EnableHDR = 0x2; + static constexpr unsigned int c_Enable4K_UHD = 0x1; + static constexpr unsigned int c_EnableQHD = 0x2; + static constexpr unsigned int c_EnableHDR = 0x4; + static constexpr unsigned int c_ReverseDepth = 0x8; + static constexpr unsigned int c_AmplificationShaders = 0x10; DeviceResources(DXGI_FORMAT backBufferFormat = DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT depthBufferFormat = DXGI_FORMAT_D32_FLOAT, @@ -25,7 +28,11 @@ namespace DX DeviceResources(DeviceResources const&) = delete; DeviceResources& operator= (DeviceResources const&) = delete; +#ifdef _GAMING_XBOX_SCARLETT + void CreateDeviceResources(D3D12XBOX_CREATE_DEVICE_FLAGS createDeviceFlags = D3D12XBOX_CREATE_DEVICE_FLAG_NONE); +#else void CreateDeviceResources(); +#endif void CreateWindowSizeDependentResources(); void SetWindow(HWND window) noexcept { m_window = window; } void Prepare(D3D12_RESOURCE_STATES beforeState = D3D12_RESOURCE_STATE_PRESENT, @@ -35,6 +42,7 @@ namespace DX void Suspend(); void Resume(); void WaitForGpu() noexcept; + void WaitForOrigin(); // Device Accessors. RECT GetOutputSize() const noexcept { return m_outputSize; } @@ -67,19 +75,7 @@ namespace DX return CD3DX12_CPU_DESCRIPTOR_HANDLE(m_dsvDescriptorHeap->GetCPUDescriptorHandleForHeapStart()); } - // Direct3D HDR Game DVR support for Xbox One. - ID3D12Resource* GetGameDVRRenderTarget() const noexcept { return m_renderTargetsGameDVR[m_backBufferIndex].Get(); } - DXGI_FORMAT GetGameDVRFormat() const noexcept { return m_gameDVRFormat; } - - CD3DX12_CPU_DESCRIPTOR_HANDLE GetGameDVRRenderTargetView() const noexcept - { - return CD3DX12_CPU_DESCRIPTOR_HANDLE( - m_rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), - static_cast(m_backBufferCount + m_backBufferIndex), m_rtvDescriptorSize); - } - private: - void MoveToNextFrame(); void RegisterFrameEvents(); static constexpr size_t MAX_BACK_BUFFER_COUNT = 3; @@ -126,9 +122,5 @@ namespace DX // DeviceResources options (see flags above) unsigned int m_options; - - // Direct3D HDR Game DVR support for Xbox One. - Microsoft::WRL::ComPtr m_renderTargetsGameDVR[MAX_BACK_BUFFER_COUNT]; - DXGI_FORMAT m_gameDVRFormat; }; } diff --git a/Samples/Graphics/ExecuteIndirect/ExecuteIndirect.cpp b/Samples/Graphics/ExecuteIndirect/ExecuteIndirect.cpp index 6fdbbc3..24dcfbf 100644 --- a/Samples/Graphics/ExecuteIndirect/ExecuteIndirect.cpp +++ b/Samples/Graphics/ExecuteIndirect/ExecuteIndirect.cpp @@ -10,7 +10,7 @@ #include "ATGColors.h" -extern void ExitSample(); +extern void ExitSample() noexcept; using namespace DirectX; @@ -68,7 +68,7 @@ void Sample::Initialize(HWND window) m_deviceResources->SetWindow(window); - m_deviceResources->CreateDeviceResources(); + m_deviceResources->CreateDeviceResources(); CreateDeviceDependentResources(); m_deviceResources->CreateWindowSizeDependentResources(); @@ -81,6 +81,8 @@ void Sample::Tick() { PIXBeginEvent(PIX_COLOR_DEFAULT, L"Frame %llu", m_frame); + m_deviceResources->WaitForOrigin(); + m_timer.Tick([&]() { Update(m_timer); @@ -267,16 +269,16 @@ void Sample::Clear() PIXBeginEvent(commandList, PIX_COLOR_DEFAULT, L"Clear"); // Clear the views. - auto rtvDescriptor = m_deviceResources->GetRenderTargetView(); - auto dsvDescriptor = m_deviceResources->GetDepthStencilView(); + auto const rtvDescriptor = m_deviceResources->GetRenderTargetView(); + auto const dsvDescriptor = m_deviceResources->GetDepthStencilView(); commandList->OMSetRenderTargets(1, &rtvDescriptor, FALSE, &dsvDescriptor); commandList->ClearRenderTargetView(rtvDescriptor, ATG::Colors::Background, 0, nullptr); commandList->ClearDepthStencilView(dsvDescriptor, D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr); // Set the viewport and scissor rect. - auto viewport = m_deviceResources->GetScreenViewport(); - auto scissorRect = m_deviceResources->GetScissorRect(); + auto const viewport = m_deviceResources->GetScreenViewport(); + auto const scissorRect = m_deviceResources->GetScissorRect(); commandList->RSSetViewports(1, &viewport); commandList->RSSetScissorRects(1, &scissorRect); @@ -499,7 +501,7 @@ void Sample::ComputeCulling() m_constantBufferFrustum->Unmap(0, &constantBufferFrustumRange); // Initialize Count to 0 - UINT clearValues[4] = {}; + const UINT clearValues[4] = {}; commandList->ClearUnorderedAccessViewUint( m_countDescriptorGPU, m_countDescriptorCPU, @@ -628,7 +630,7 @@ void Sample::CreateDeviceDependentResources() m_resourceDescriptorHeap->GetCpuHandle(ResourceDescriptors::FontController), m_resourceDescriptorHeap->GetGpuHandle(ResourceDescriptors::FontController)); - RenderTargetState renderTargetState(m_deviceResources->GetBackBufferFormat(), m_deviceResources->GetDepthBufferFormat()); + const RenderTargetState renderTargetState(m_deviceResources->GetBackBufferFormat(), m_deviceResources->GetDepthBufferFormat()); SpriteBatchPipelineStateDescription pipelineDescription(renderTargetState); m_spriteBatch = std::make_unique(device, resourceUpload, pipelineDescription); @@ -638,7 +640,7 @@ void Sample::CreateDeviceDependentResources() // Allocate all memory resources that change on a window SizeChanged event. void Sample::CreateWindowSizeDependentResources() { - auto outputSize = m_deviceResources->GetOutputSize(); + auto const outputSize = m_deviceResources->GetOutputSize(); // Initial camera values m_cameraPosition = XMVectorSet(0.0f, 0.0f, -10.0f, 1.0f); @@ -732,9 +734,9 @@ void Sample::InitializeMeshes() Index faceIndices[faces][indicesPerFace] = { { 0, 1, 2, }, - { 0, 2, 3, }, - { 0, 3, 1, }, - { 3, 2, 1, }, + { 0, 2, 3, }, + { 0, 3, 1, }, + { 3, 2, 1, }, }; for (uint32_t iFace = 0; iFace < faces; ++iFace) { @@ -767,11 +769,11 @@ void Sample::InitializeMeshes() Index faceIndices[faces][indicesPerFace] = { { 0, 1, 3, 2, }, - { 0, 4, 5, 1, }, - { 0, 2, 6, 4, }, - { 4, 6, 7, 5, }, - { 2, 3, 7, 6, }, - { 1, 5, 7, 3, }, + { 0, 4, 5, 1, }, + { 0, 2, 6, 4, }, + { 4, 6, 7, 5, }, + { 2, 3, 7, 6, }, + { 1, 5, 7, 3, }, }; for (uint32_t iFace = 0; iFace < faces; ++iFace) { @@ -806,13 +808,13 @@ void Sample::InitializeMeshes() Index faceIndices[faces][indicesPerFace] = { { 0, 3, 4 }, - { 3, 1, 4 }, - { 1, 2, 4 }, - { 2, 0, 4 }, - { 3, 0, 5 }, - { 1, 3, 5 }, - { 2, 1, 5 }, - { 0, 2, 5 }, + { 3, 1, 4 }, + { 1, 2, 4 }, + { 2, 0, 4 }, + { 3, 0, 5 }, + { 1, 3, 5 }, + { 2, 1, 5 }, + { 0, 2, 5 }, }; for (uint32_t iFace = 0; iFace < faces; ++iFace) { @@ -860,17 +862,17 @@ void Sample::InitializeMeshes() Index faceIndices[faces][indicesPerFace] = { { 0, 12, 13, 1, 8, }, - { 0, 8, 9, 2, 16, }, - { 0, 16, 17, 4, 12, }, - { 1, 18, 3, 9, 8, }, - { 1, 13, 5, 19, 18, }, - { 2, 9, 3, 15, 14, }, - { 2, 14, 6, 17, 16, }, - { 3, 18, 19, 7, 15, }, - { 4, 10, 5, 13, 12, }, - { 4, 17, 6, 11, 10, }, - { 5, 10, 11, 7, 19, }, - { 6, 14, 15, 7, 11, }, + { 0, 8, 9, 2, 16, }, + { 0, 16, 17, 4, 12, }, + { 1, 18, 3, 9, 8, }, + { 1, 13, 5, 19, 18, }, + { 2, 9, 3, 15, 14, }, + { 2, 14, 6, 17, 16, }, + { 3, 18, 19, 7, 15, }, + { 4, 10, 5, 13, 12, }, + { 4, 17, 6, 11, 10, }, + { 5, 10, 11, 7, 19, }, + { 6, 14, 15, 7, 11, }, }; for (uint32_t iFace = 0; iFace < faces; ++iFace) { @@ -917,25 +919,25 @@ void Sample::InitializeMeshes() Index faceIndices[faces][indicesPerFace] = { { 0, 4, 2, }, - { 0, 2, 5, }, - { 0, 5, 10, }, - { 0, 10, 8, }, - { 0, 8, 4, }, - { 3, 1, 7, }, - { 3, 7, 11, }, - { 3, 11, 9, }, - { 3, 9, 6, }, - { 3, 6, 1, }, - { 4, 9, 2, }, - { 2, 9, 11, }, - { 5, 2, 11, }, - { 5, 11, 7, }, - { 5, 7, 10, }, - { 10, 7, 1, }, - { 10, 1, 8, }, - { 8, 1, 6, }, - { 8, 6, 4, }, - { 4, 6, 9, }, + { 0, 2, 5, }, + { 0, 5, 10, }, + { 0, 10, 8, }, + { 0, 8, 4, }, + { 3, 1, 7, }, + { 3, 7, 11, }, + { 3, 11, 9, }, + { 3, 9, 6, }, + { 3, 6, 1, }, + { 4, 9, 2, }, + { 2, 9, 11, }, + { 5, 2, 11, }, + { 5, 11, 7, }, + { 5, 7, 10, }, + { 10, 7, 1, }, + { 10, 1, 8, }, + { 8, 1, 6, }, + { 8, 6, 4, }, + { 4, 6, 9, }, }; for (uint32_t iFace = 0; iFace < faces; ++iFace) { diff --git a/Samples/Graphics/ExecuteIndirect/ExecuteIndirect.h b/Samples/Graphics/ExecuteIndirect/ExecuteIndirect.h index 27953d4..695608d 100644 --- a/Samples/Graphics/ExecuteIndirect/ExecuteIndirect.h +++ b/Samples/Graphics/ExecuteIndirect/ExecuteIndirect.h @@ -25,6 +25,13 @@ class Sample public: Sample() noexcept(false); + ~Sample() = default; + + Sample(Sample&&) = default; + Sample& operator= (Sample&&) = default; + + Sample(Sample const&) = delete; + Sample& operator= (Sample const&) = delete; // Initialization and management void Initialize(HWND window); @@ -35,9 +42,11 @@ public: // Messages void OnSuspending(); void OnResuming(); + void OnConstrained() {} + void OnUnConstrained() {} // Properties - bool RequestHDRMode() const { return m_deviceResources ? (m_deviceResources->GetDeviceOptions() & DX::DeviceResources::c_EnableHDR) != 0 : false; } + bool RequestHDRMode() const noexcept { return m_deviceResources ? (m_deviceResources->GetDeviceOptions() & DX::DeviceResources::c_EnableHDR) != 0 : false; } private: diff --git a/Samples/Graphics/ExecuteIndirect/Main.cpp b/Samples/Graphics/ExecuteIndirect/Main.cpp index a60b9d8..3b87bcf 100644 --- a/Samples/Graphics/ExecuteIndirect/Main.cpp +++ b/Samples/Graphics/ExecuteIndirect/Main.cpp @@ -18,16 +18,25 @@ using namespace DirectX; +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wcovered-switch-default" +#pragma clang diagnostic ignored "-Wswitch-enum" +#endif + +#pragma warning(disable : 4061) + namespace { std::unique_ptr g_sample; HANDLE g_plmSuspendComplete = nullptr; HANDLE g_plmSignalResume = nullptr; -}; +} bool g_HDRMode = false; LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); +void SetDisplayMode() noexcept; +void ExitSample() noexcept; // Entry point int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) @@ -58,6 +67,8 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp // Register class and create window PAPPSTATE_REGISTRATION hPLM = {}; + PAPPCONSTRAIN_REGISTRATION hPLM2 = {}; + { // Register class WNDCLASSEXA wcex = {}; @@ -66,7 +77,7 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp wcex.lpfnWndProc = WndProc; wcex.hInstance = hInstance; wcex.lpszClassName = u8"ExecuteIndirectWindowClass"; - wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); + wcex.hbrBackground = reinterpret_cast(COLOR_WINDOW + 1); if (!RegisterClassExA(&wcex)) return 1; @@ -79,17 +90,7 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp ShowWindow(hwnd, nCmdShow); - if (g_sample->RequestHDRMode()) - { - // Request HDR mode. - auto result = XDisplayTryEnableHdrMode(XDisplayHdrModePreference::PreferHdr, nullptr); - - g_HDRMode = (result == XDisplayHdrModeResult::Enabled); - - #ifdef _DEBUG - OutputDebugStringA((g_HDRMode) ? "INFO: Display in HDR Mode\n" : "INFO: Display in SDR Mode\n"); - #endif - } + SetDisplayMode(); SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast(g_sample.get())); @@ -116,7 +117,7 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp PostMessage(reinterpret_cast(context), WM_USER, 0, 0); // To defer suspend, you must wait to exit this callback - (void)WaitForSingleObject(g_plmSuspendComplete, INFINITE); + std::ignore = WaitForSingleObject(g_plmSuspendComplete, INFINITE); } else { @@ -124,6 +125,13 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp } }, hwnd, &hPLM)) return 1; + + if (RegisterAppConstrainedChangeNotification([](BOOLEAN constrained, PVOID context) + { + // To ensure we use the main UI thread to process the notification, we self-post a message + SendMessage(reinterpret_cast(context), WM_USER + 1, (constrained) ? 1u : 0u, 0); + }, hwnd, &hPLM2)) + return 1; } // Main message loop @@ -144,13 +152,14 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp g_sample.reset(); UnregisterAppStateChangeNotification(hPLM); - + UnregisterAppConstrainedChangeNotification(hPLM2); + CloseHandle(g_plmSuspendComplete); CloseHandle(g_plmSignalResume); XGameRuntimeUninitialize(); - return (int) msg.wParam; + return static_cast(msg.wParam); } // Windows procedure @@ -168,18 +177,52 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) // Complete deferral SetEvent(g_plmSuspendComplete); - (void)WaitForSingleObject(g_plmSignalResume, INFINITE); + std::ignore = WaitForSingleObject(g_plmSignalResume, INFINITE); + + SetDisplayMode(); sample->OnResuming(); } break; + + case WM_USER + 1: + if (sample) + { + if (wParam) + { + sample->OnConstrained(); + } + else + { + SetDisplayMode(); + + sample->OnUnConstrained(); + } + } + break; } return DefWindowProc(hWnd, message, wParam, lParam); } +// HDR helper +void SetDisplayMode() noexcept +{ + if (g_sample && g_sample->RequestHDRMode()) + { + // Request HDR mode. + auto result = XDisplayTryEnableHdrMode(XDisplayHdrModePreference::PreferHdr, nullptr); + + g_HDRMode = (result == XDisplayHdrModeResult::Enabled); + +#ifdef _DEBUG + OutputDebugStringA((g_HDRMode) ? "INFO: Display in HDR Mode\n" : "INFO: Display in SDR Mode\n"); +#endif + } +} + // Exit helper -void ExitSample() +void ExitSample() noexcept { PostQuitMessage(0); } diff --git a/Samples/Graphics/ExecuteIndirect/Readme.docx b/Samples/Graphics/ExecuteIndirect/Readme.docx index 241e8f80913f17ce98ad76566924d42b38fd1372..38e99ccbe903379bbd04799980f9c4dbd0ee7717 100644 GIT binary patch delta 22585 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

bFeyWUBvp56h{sRC2|Nocu zLIx0jDR{6+ln4L-bUOe55C8xGcW-iJFKA_9WMy(OE_iKhm*sdK zF$qRz<#<_yx1)Jg6=PX0!#Pb!`7Vt^mY4aodKc!YoKL55NabbDA{i@45v79VAuUUo z$|Kn$DMt-l_%sYHVq^(}IiMzobHXb6w8B6qgXpp?TiZbR3?PsYqirblWGF-CTx|h= zX=eceS6fK>c}P2iGtNW8+eqelNa{9{bso}|JEhwWo)J-+#L zBWN;gTA0q3=~I=IRXdgq(Oo^<&%;H3O0!C6O=dI!J?2?CkBcrlr01eR=ez}ZJfwif zG->IU1sWJ?`%;@Q!zEz>{y#Vh|A-=e67Ag963;QC_pqU=?vv(o$C|>|>N$bI z<3|GN_ZtWPz`)<+ru$#e1<{OuFxFt>22IZe%p5(Dj^a}m)igIq)AZdmpY!~ookP(a zfi#U-S$)YlIIkw8sjWJ_JXxfC&)D|24T2=gKf17M>}vn4Ylu%Ougl0K;>8(!0i!Qc zN8Hj>47y>n8hrs|#S2su+k67ibjQ{jkv1*}BcEVe?5Un@Yi8XF>w3k1Tg~QA3-C>h zS6su_C$&LX!zBAi;- zAJ*nB3;A?P7D*-4EdF$VKDg=ljsAMN6KuBR9!-+ph#+n>wNBhAy4B6j?%KC{SU+np zAYE8wv)SI!2>H$0(QN2OqwIBRu-6V?Qx&}JCO#CAP4Ki$`y*0Cif-wSb!J3zzMN@A z`Q*Y>jeeGGW2=c>CO;CgT-n=>bjbix13OTC(;E;#8w%YHpr+@4uBT`*VKeXA0z#@; z6;}yuLZc=rf8Wqy`MX!HHOURF+`#hhwotmO7#6mUSaiRaoFr*OEcjh#1FN26>OpX6 z-5!|HM!tnL?MDjUGx5K}&M)Do4rPtSdX^G?c*x^Lg8q*FZg|MmkjiCzyZ(Mmig98_>y+3r>{;?w3nHYl9sS)dVlPE+(cDEKG|_M}k0T1jhr{M~ zupjvDRi!&MFtKL`&9ZhaLLt}1NHwlXC=&ba>$`?xiOhXHLSffH4pyA& zvXpCM)p+BJ-=KmW5TD zb96=7&+%-mdDrEBCbBixy|Ul$CYsphwJp9_^(^1huS+PybsZFVXB;^E%!y)RAPas| zU6)XSXDe8oC!Q$O#>wfHq#iaclOJ;Za7m%MlgYC{m*a|G!i~#ve{Yx$gq4`use=8Ojy^)rVo_vR#KeoMNn8YRlJFULSz=?F-ct|&;sXiwgZqCjtEN;N;16s$ieh8;#?uw=MpMkmjbm%( zjj2pbP0=jmA@5H<8iHz3at9=dU&O6941Fqp(|fd~G+rq3Mt$8Z7;3+QMY$G&^IEd- z3#dmH<%-cNobw_-MQ+;VUWZW3N*BCVLNb!>m%rsvd)vy#KTTQ6zrdjMB&_`0uv4q& zK$4JGqYGA6A84LRya1{Oh6Fl!1TkvTmV`HFIcH1Ekz{KPa@``>=L|PR_$T^$k<+h# z*I`>{PLWr;gxX(E_zH9AgrDDkN|(4O`6*BGADtwgb=MR%bPY1(j9+tn1+xB-HvuQE zy@U32FWk2^mgk?^D%}?6IK%yoxvwH*c}EVA20t^ghdwg-0XNp}L{}&7ci70Dw`J#x z6;Jz=l^5B#9}Rs4?~47)UGDAGyrDUNs-m8A)O8)2A9$^*{>Ws!<6X&mGTt@5pNjW9 zOsVk7K(dMbARZ(ZRv*Wd4J@?mSN%75J&{@D*rE`6GCIr}ePayKyS@_m#~#q`67_YY+A@`EBf~U5E^=Y? z8vGlYYQhfeBInqz!M_$5$e#pnmVeW7)yc$rv;1S*_guxf$itr3;2&WVSrdH5-RRGi zYUdGq|33hi@j?a^2{Q=|J|74G08W$2S0{g$NS17DvbAM736RVLNp|+fZn2;x+Eycp z0!iD+Zu>xejXv3)LrSt8CmzRX)5+F@v3WZm&z*ez+U8gO(vADH4*gf}XS@gRg@CO>QY?sQRdDgcerO3NOCUx-*NkU` zOZu?FuwM=0nO!s3267Gn0tp>^8|wJgP#qJw+5&P90s^kKki5f?o)NBd7?QJ% zkRKOlGe_acaGqNJ}J{5z#n0BGT6}DvO~ zjI(*ULQCI3;^8CP)C)u;lLzJ_~e;5aIDeYeR%W$(VT%wFj6`U)c?~&ps98dUW zSc0)TAM6hN-*$&9?wcfW&WH>GY_nw3ykR_-SD3%5e_}S3QnqZ0IJ~~)+w0BVKU3=& z#`Kz_Jq_YLMzZM>5-Hyz^w7VtU!}M=n;U(ShRm&z-h0{w%m=KV9iapgV4uI=c@LYS z6^zALH=;94NbRfBH69ZHP(32M)2sS^AoLY(L$szvuMwxPWBxMKos|U330Zc)X|LMu zo~hF_??VS3u2ZcoL(HN8yS(II{mLeeIwLp-&7^OL-#PH%QsqNdp7?vHu-d{@GqGr- zA&1JyG2thEp=;HZGLhHeG&=Dg|IgV)g?R|BfESCazCoQngb11=w>k?>M2Ks1erJf((g;$A#o~j`l+18$sO_I4MEBmstNM5 z1BQ*QPRerSi@G#Ys#NJrVa)z`=ABxAV)xZwo~)F=*p0g@L~Y*PgScRFm)ke|8;NAY z%84z|-2D=EjfZfG0-;T$B>b6rzrpUcT7~NE+F=)TqRg(cWnQj!FNFct?W4*8`H{t; zStaZMgc{rTs9L;sHw2|@+9_40Yda&Uy2D_4Uol*E7HKMXy|a%gW=w!g@?@ov6mbW| z6;y)9Lz~CBT}}qG=}e;w$0uRO@o6pEVHs`f1iJ*hvALEG_@!7Gc_k8|4){0S)sfiT z0eBVz;XS^6?)s{WoDVZ_`i(Y-)tf$(;OP;p!QLG66F&h$eOSY-&FGPg>u&gKqYanr zV81Y6Dtl1u&XqW7XVK5(Do1*}KnPND4JjkYuY&4-a3#x(ou%Qx#n>*O7iOJ~8P(;C z?LS5yNWVRo%PYz*T^)un@5#OrJgiu8q`(~RcCVN*o)8uG)<|o=m|2J5I@mi(tXHAX zM9Y+SP&Q-8$6krT(0|22Z`AUf{ zmF?cGBU@oYBw4^kmiw9w)N256z4N3cjyOi@P(slkA?$fz@ z<+>IdIPZlnUe^nx0f&al*I1=@fEc4Xrk&Y^EUt`9#=f#Bndji<3Bas~dBa}N6%{zL zq?MMnp$EvVxTqXh^?+|MpTqL>%Q43EP&;1C`;DBmd}#UhiE}*_$4|+`%@W<^!C^UP z!1?X4K=sEU|5z-f&it*bgMpYOq&tGv-sWynIQ3phSp1M;>Y}iCxReY{Gg}jN>mA1XLXtNQe`SSW&$}m=Z_3lpo zz?d{_J=fZPgJw4~Y|K4ikg_wbG6&tDOrkW={q1}>t$&BANyk|ecttu6xA)?bci>^_ zN|qqCW`;aC3*5L=83;H7Z3$;~9w-7~ORZlz(7o?Fi1tpLYCS8yCp{~@(>!B3QgVBN zAa5GlO)0)sOS$BqY>Q_leQPD z7nYGaje&wv1?6M}@IoreM_lWxTE4LGe=oIQSPre}3?9_f$R00Ws<_Ip-%9th7l2WG zD&_qW-{51+!EiB&!xLB;>Bk|^^FsdM+H3BaFLyrS7Xngh{C0H1Rp_qtebW*vwlkhn z2_}gdGZ&4&Y57UsjLr_3U>=V`qG*USJ;ij@`%h2gkBf(vi!C9d)3%CiQ_-5+*VRT- zF*!ymFntnNC0+J3V`rPx7-U9X)72K&57V0uEP5$qxL{XFAEp9=@b6CQUOyKWlowIH ziKVVoN?8vU%`88qg~ApfR=iP`5LPU3H+W&9VYH`0xW; z#S+@irNRzLWiuh`Tj!p*9EneiVaLgPK@vwP(O=)6UB740aM?Sf1+O;hsVff`b(;Q2$v%Yq_LTMzKDBC^(6P(u2D$kTj zr=;5K2ODO?+wVYZ#Hqkh(j@Um10rB?-GaSWwW28OLm%QwjcMdDhgM0w`k`PT$3#rj zV*0yy`l1ua&JkQ*y|-T zU#cj&*B!wKFz|TZwZ+i8iGr=VYF;tW#Ui?G@M>!NF+xTy^{@Qmjz}qOiEu$+GHrp- zFPwZVzNL}NgvY_8D4PQ(M%&Pz^n-%&-l(&OsuM1d?s2x^Ed@3D17CpaiQ~kOe*&`z z-J~wKNP(e$b@P%8jte|_cSDAKk2Ato(W1WHQCK|*tXh`uh%~!jRc!%Qj6OAgp1#jl zdjZJu(vNhc=!fbEC#9uZKRHtF7NHhmsAc;&@N($B7SmH2hWwQ5wm^0Hj)e&u?)Z&d zRtNz>vL*!kd%9B#)gZ&~zSpGePbU?;fGg!u4YWHF3s7Djy<}W=m`-rda4op~WHP(G zH6()FVrApQk|XUpfn?glzp7=i#jbE`Nl=-@M|*Rf$p{xYqq-flKl({m=2TL`^x-2E zmY(`^Bpy!2$;}c|&Lmsj?1$qMKcFU+fb|A)f!!+^t(Br!?Pb{tLW`wWdo@Y^q)N7y ziIF3kv$tZ0didqIspF6myP3whG7lKf=CYAQ(L6Wc?XlOG$a}h24)q=+x|L={>}-#T zr!PaM4Ca@PUwPwtS+Jno9M}D(V3~&7a-Plo1Du&Ea&}2w47~Px?d!YhF^JmF9&d37 z3@*VNJ>W!)E2;L}l!jaouN5~~abetv*wEOax~gvvkiTgo+Y0?hXvDeVtc%Cm^!I%PbVZu z;UyB`>9k3Mb{m+I*JLwiav|XRsCk<_55or7b+O+q@jsx${O6z5rRYB4eBJg=j1Zvt}*9$-gU%4cw#5!U1sXrnf2&p2m z;&o+naeALQ(0Wn&+`RDF{84K`f=ts|jZ-t04At967i&M-eZ*nFhcpZwlVX5K4@6B_ zN%6ze{e2k?M<+gKy3%X27F=DMXss)$)RoPeX z!-{U)8Yaf&VkJ=D@Gd1=WLD)8p-t2_P zvo^6r`qTvlST8r|{nY-s5H;^PA|#dE05-Rdw^a|+b&t2-zJ|KH-XEyW#7$YhXuo*3 z%Tjp7|N8tZWvc-TMc(L0hkp*Fd@IaHU`-f5UM;7eRJB6$`xbY)pjqT9$4a7@S4kH; zP$qxuiJ^|n`vrC8%tRvdS@^0Hg0w--e_(i|DyK!H!<}V}hSM+cVw)I9TXd59f^g++ zuRd731k=58>73Uh8nG#slkNs7UA&TJ^UPlIT;mHx$a|Yv-iOMB<4iyjazBGS?@SCc zr~fMBtf;6wDHT~0Z0lKTS?>`nZ|Zv9HnLW+KJd!KvwPvN;_fpCkJwBsPEarB&1o#U z@-M^6qY~sSrpUroRL8+m#BJ$L2Jic_L5Z51ZIdh7_{E=m(h3vJq(cpk>#don|pb zxkk+S0;;7%H*#hbl``SusNiSM=+&l8C`dCIJ{Z|5D5_F)%7ksJZW*M;=U6jD^K6qu zXDLf~TsZYvIL{a-C@2-SQyzU->iVep#1gNMBF7lW>g&}0T(FWMm)3dI73D2TuvA)@ z6N7!81%!1vTJ-w1`+K4aK7&=OU!9WefwIC_Ids=WD{qiB%rYn4C7Tc}R8n4X=CRXM zw!}a#_F#jy^&$_&J4pD-I)3){BP)u_a<)~?vMn=arW*59 z&sv=ZVXuEO8|uwdtvK*WW9M^MYc3*U zu0XceHm9}F{b+Gja%M=ZzbYQHgM-aZ;Px8JU{64ON56i4n`ZNV!MV$_@^KtdM>nvM zmT7e@_@-`HGh=c@r&kjxZ8{dve-OR4SZrj)dXKuNPx7Pt#XtQ8^WrS9*1gd|2Ec?0 z2~5?|3g;sjTzh%*B=2AnZNde^IM-f_hu>3a!_?K?lqRiUf8gSbY~ka_{>Ikp`vYjJ z^UP&6R~xzmBS(4;AKei8FbB^M7J2v5NNGh_wZ0cnpGe?qi0sMD8H*i<)%&QYAofYa zkRxiI$8Kc<83YdlKmEFpA242zayNMz(J5>+?Ln}QKh!(%06^?y-b!>yzKP0{UNZ4Z zqWx@$lcvms9VCDpwk7Q!w*5eGyqGA2=tLQvLAfVp{5*DZ=%$#<6hGJ}n7!tBLwfQb zC=SCP!%wmiOCG?23VG>H%umqHctmU1@nQl1%pLNK)XjMoqBPPViNCto*W+y3KqY+R zKhSFH8A2T3e+TI_PK@`h{S02e6`w+Sin0$F@*lRazv zo?%B2V?(zeuBuI$O3#3>c-{}AOy$>I&(`A;xwE`^PLtVi10cUzG&$SAhxrcS4@d*&7RtC- zCF#tCCCNy%PAWBD$c#Pi-$X&x&)popy2@~S8ga4RK7~z$GzgyJXxeVkm&CO%<)ZUV zXDAAxDSN7jJ+^}H9G5G3mG)@4pnax9Bue4H*BzLriPQxNbsvPIIDv{w~B;~ z_R*6|5Oh4O1wcS6R>FPT>jKK4k%%|M5zntZp@I~-ndu#L>xN%O6m_p{z}>%r%o8mn z+nC~-rFXp-{IvbVM*U6PWIUOC`mEI%v!VnD*I{apS)^s&>_1xuPpmz9_-B5=!eH)2 zt!Pj$QI_aZ#13t$H0s|WZ6mP~Adv9?ED8%vHv(v(MnHFr0K8QH1<@bC-rw==pJEOb zGzKuU{G0s!Bj8r}!vRC3jR6!?|3)nTXfL5P#sEg@f1?wB1X^0qBVzyq^?&mc%>Td@ ze-wnqP+k*&1e&?!J3>nuXsii<1I^d!9jDdb`vlMp696TetM$W0Vki+1AcPtXRR#j& z&}_lbI3Pe4^)+<-_aW{gl+Y9)k0!edH8cgtqLE=k(;v2|QP5>mfC8#7l+FymM)hB3 z8i29QB9KmbbADJU%n zz)Jn^0O8LKKvGa^5P*r}?|~5&1!ew$yZ-;rkR%I0v30l6_Hy?C^V+z3K|MhLQtID* tdEr67^bmIZ23-FXVFl=4podo{uMDL%2PmT1t3d6|0jvOdW59#xe*kapSGWKG delta 22232 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

wVvpitzNc>Qi$Wfi ziz~!LR5FaoR$2Sdv|er3tZQM9)Y|1Axw6R;g|_HOOQ`=ZGtw1RLMn-SrsVJ2kW1~RRI6hmBk@5v zW*5goFKEN5vSXK>E3|h@{$^drg7e+_{aThI9v;edcDv$i!$KRLVfzPax^z?zESDS* zACop@t2=#hD5oT|VgJt~wTSGez++5d<6RoVsfMtIKG4ObA&z20S%lhbPeL4g^_jJu zgGPjy#{#V%di0+stfdCf$`Tl>WW84drzx+p10Q>g9B;FqcT#@7OI;$we;9B1x;l1C zj-h)yu*oYGGwx?(O=>4mzR^=7ESu79jHafFaZ=Aap6)R|;rBW~VNc?mp7FVIeq0wr zJHbST8ChJN+}uJvd3Nz4x`}q5zo%6-pS{eRb4I@_*@TitOqs@}Y|ianjl#!r?;Xr_ zma;nUq@>kdwkviIx5}GK7ie6S`kmr{Ub~$(8~640KW?}$ZZ2!#h$+cOwAheMx0(C5 zHvg|Ki6+^5SLZww0_o;a*nlpSQ8Q@MZR>aHd(?DmsfSpBUPPA9QEph1*B|dGk_i0s zhmnax=IlI$+>GP6&4MNLS*He=0=fxn41%u8k$F|>anhy}%tB<`AWRd%Yy67 z)U()cyC>X}>~BBa{5_-j>|vcBDVp!}DCK>P*!RuyW@@#n5N~c=yBucz0Cq$2zCLf> zBy%ZeVzO1(#9AAYazfwJ59bumv`KQ)gYjP`AuJ)XtAyrQgCc*=pg6OY#J=$Lp$D#3 zdDlh8a5m;aq0J2A4bcnb-;c!^IuDfQ0|=g0F+f4W&ByS@t_~uv5C{}3tOMMj!{D!V zfPvCq9wH(ahL1ntJB0_DZ~#rE&utoZ08Eh10W_3%WbdD-+dBZF56I;JSls7&wH-tU z`QN?|AJuDM8wc2{{5cbR2h5BF57q+~&{2?D4>-bB;f|2~uy zky+JMx2L)~(jo*nfCGoa{kTK)!Yu~eXRfK`}(Kq-`?v#K0a6U zf8_k{CHx0DyiN}Gak<-~sfR3YzejlWXVLb->=R`AXNQ=o@^jwY!hbMa{yTc&-=_Ql zfA~k#|FC~mzBT{nh%odAZsZB^eZ?P9;n#b9YEtc9_+#TgWtv|Y7yg3;@(FE| zFno9PCu01sDTRNBTdQB#s7?R9KsfZxf1~|@mj8Qb@()^@p9RLhfb|hjzYOx>-&5wC z1?|puRrF0x>!uIFa@S}Nk1xB2e|g=&^Y88`&yQC#US9I&U4MCU%-6r~l~LH-*5_xt zk73{1pbzBTDIr+IcpT-wzNC9z8J|+}<7bzC+pDmfwNHd-loU|ryNHjI;8Bjxe-VO0 zc^dm1F`gnuzzI@(ZQL6D>*GAG<*w?tDJXybddbH!{tEv6Z{YU{??3N$-57n-B=7N; zY(G9zj&WZHW6U-6xD)S7$YsrLyf0SVJ@P)@O})F)YugW&+nepTppLSp|8mQR@v>|8 z_0?j(w@!PJ31cYq(Y1bbhrDXbf7ob-*W7o;m|`i;(`-LEg2M!XqQpKF##vt9_LT6N zST<1+KdrkrgSB~#a?Qo%er)*bm!K@0S>LPMGH73d=M8;m__RGHpcsV-!pA^-%U@T5 zi9AOlF#bGS`|Z14UzXP{>+>)88fu#%f2K9*ySifE>}f4(%12edm+y1*eLv=7?sH^?{%L+4IYz|2Mhm&2&%2 z({FRyz8kM2wCDDv`%{pb-z0lC-#aR0#2X(sh1pRTDuX*1OHhdfY{ZoBce=hje_QQCHo9fH6 z@$=Vy-~M%D!E_qN_teiRPw~){`Ay8r^3{3XUd)yKmlvar3;bHTN158(-ZF9zfDfH^ z@zatoG4d$}e}VGu{c?k{e*UeSXa7CU*{oC%ZZKb0aTB{z1PWiuTxf z>o{b=Eq;ris{Fn#e=or2>}22nJN5^_?_zAFEx8IXL=kKAskM306KgoRP9znmS>Qb)vgz;k5ou*zLe+(y)CtLk;B>d$_{iwL+ zogsGZUd`wHvHWZ%JKefF6EM$R_x?L$?7TkqpB--O-&31DiW%nBen0t6_4)Zt$oul1 z9(`Hdpl?6i{+af*=3Qs}j`zRkzI~B-j7^fi27lP~qw|XFHjlow-)VDi{R!gSSB;PU zsml|so{T(te{S;Z{2&eTlQWF>>@Vcg2|nk%R|HQ5PJ1#QzSIqu?aUH0L%aLe(-hA$ z{CfJU`>XB3GH*Y_@!9^;e~YW|ncDYUO}>n^+ithkC+)oG{r?~ov)4-q`xA#l+)o%p z3O_M4M*c)X0*OHw1#>X>f1j)O1pgS~UBjT{+x#=vfBey>&o3U*(8uHH6%e2Cyesti z`BD*lAL124V;_%E_9MKbj|=}k@G{1a^FMq44foOzjMDUrfKil!_GJ7k7DlmMy=RT| zWMKp)u{X8ofBpS$(~j}17@jrqk4g<@P@bV6_Up?ZKWmCF&92>3@L$W5TK{W+A%u8-U%5W( z_RrGiTM+JC{}|)HRzaWU^-pD-jNg8O^ZHXTHtn~cFuWcAI_I}B`$oX3sm{jmc(!JnO1edYk7t)TC{xzF3^$B=X%d=8O$ertds6RwbJd ziB%=6jKBhqcmWnXB3q7k<6?Ve20zEsEk21;gX@uxcwM;G!{xrg^&NcRlhRj9(Z?6y73iBAhlh`qQdJQO&^#RY;Cg?wIXD`{ zfnj(MoK@zxm&ah>Spwm7?;K5b|9g4?J_kMYScZ~1o$BYOUKFa>%^Ra;4}hP1f5S?= zLohG-{gMNB=rFa?!25Zn1}@~UNYW!JX0U4Ahwn=XL6gQxFaa|)hdb-&$jG3NgndkJ zz%}R7Bso!WA(rAfoE)fWFd^i!?XX}8eXwpJf=lY)XI;t7Z@0nA{?yT7ek~SP&pRJM z{QS}fNa3pwxP(WOM|P zt+6+Eh#+M)8OCUiyGVb^uhMZlA@&+V*5t1^7P@QUj1L{zd^0(NX0q_me>+lodsuOQ z>{$du(}4?*ny3|S9QfUui%fB*6;zy1bZfcubeiI}kXHX#d6+ZnSBjj05m|)WAwEP8 z=LP7g=XaZt)WW6w`U+_13d6vIC)>0OI!8HHM^mn# z#TF)PY;ez9r59j127{bjf0f{T%?4PhWRs|__hH~K6+`bg1Fh#PpMpxMHO<pJ~l18kF3+SX2$={vdMonAZg; z5w00n%LQqB<9IrhR=YXT7TJqC(f|N(v$~A}iMBPLHRtt+EyEpUR0oqCf@GTEsPXz2;&zZVdC*P9kEiX9t1Gdc>WyWa)#nqGoFhju?+g=IK`hZd9?R{kQtS+X2n5oU7WUf(4C+Inx!_Ag=Wm5OO z=h_+4G3X^Fe;Cak{b{Q2S?XGVLT7w;y_2%~xQWJ4RBsLE!K6^SpiulkUOOng+gFA> z_JkZYBxFK;gE!NxHvP!7p(QHwoDPFR|+ap^vgsO!_|e$gMNe;dgN)Yxxs!A|(q_C1R|7+~@S z)Hxej4`$;FFiN<0IS%_vN^_I6@vIlsC^e+l+EU1j!mXHO)@w;p4NmxMtXcQEvh(P2 zN!g<2v3H~ZqjRu3xR<#c8L|#zGD+N|p(VR$+76HaFYt(+QjI^5y%T9TgHPsmz-;v5 zm=ht8e}^@YQzB;jutMHxl(WTP3kKG}2@mu8EFDBH zxz|&|m*I9|!D4lh_;@RaktIfu5`-7v!S%%LuE-^phMO}ce&TV+mV`d|FgY3ZG>~O9 z7Coki2Xhcvo>*m7uuc$s>3KX{Nbk&-V`D2*4r1xyVK2%m)a29>l{Tx@9zd zxCJl3;bvK0xv5PlV+x!pxkI0?Jz_5Oe=3~CHPp{%qrH;HwYd{n8y1a=80sC`d@0NF zp=i!Wqcz19aa9z215zrbLrC376cu^Zy-;!QHaq!Hns&^~x`arcb@I~;CIEvEqo=+8 zJG;IBMetN;58)|NpOg8go!@pXLiG07ZHv4rRbe$u8{|xu6Uj9MG79ly*|;f{e=?%h zk%L(^djSgeEbo=f4QawhsA)H3Prj0k697zBcm$J#t3dZSUntVP^Ec5>ZC>#l@zTX& zO>cWDO|rD(IZdXm`2}d1z=<@lhi2CqJN1Vo8=#5(t30n&A? zFD`p^5~aDTd`6E4BQM~R4()}df4UbS1Ss7+t<>bCnun>^asVxhq<|U@y&1sE0j8zS z@b2@@4(uY%yhK+I&8vx)U0OmXZCw-_WY4&+|IW+DJ4Y*i5LzL6h!$3A%beZB6P`d9 zgqweL0m{?Rub=$_vcr%|U1RK>?rC?C`L$z@SJ!I&Gu?)+SYI!~BRb%9f9|llHABiF zZJsdN+0F#}S+OSOvw!FI8{Xd@c{SzjQ}A|d&k|k9x`fCTx*_6vHCgt0p!4gQJ-)!5 zW9hpV9z|ujtHbcna*> zs{k|}YThOvS}4(%DH)U{O2l?;4K17X_1ZO!>#fNl~xKS(koDLJ5R-4RG3DKc}OGG zdT7<%tu%0I90J-Ix#FFd%P2h*iX7_pb=el`TQ{2MkYVz7H_xgVx8*f1(<>KkpledC6)7)O~OU3M7lZ1 z)U7}w=bFYoa8+yPLC+FF8D{Kdt|S;c_u}EO)z@QgT4P~4e+7+cjVIrKCT&XF87 zQaXe)FNRovy09N*WZ2C;58hEMQS{z3d)nfXZM)E;CS*-PDj{2Kq;0eu%4_K~yZ~V+ z&XR#d8$XCy$X=>QB@)U2rnA#3TQ2oAY9%*f12BYvI9*PJjzbO1C~MlVc2>vJ)zUoV zFiA@L0(2j&e?l;wh}|t;EdR?fk^CM9hl)#4HE*=@ z=EvhIO}VZ#8#HS;m=KOIFympVPt|s9+?Hzz=6z(TFLiWrS)3lvnC$l~82iYHN5JaH z<=uLZF)(GNq}lZK;gfJpF{L&iy~df>X1Y>W>TGsS-fbu2!M*@Jx0Q(=_B*m;Hob~W z`NWDmf0iof%?uP**eh|+r;^>6TidEd!o+`!-h7x(@7Vk z%srJ8&Rseci6F}FM31?CQuR5Or{+MMk0%BtPym8WB4F##V!PvBNii5L8gtqdAfsyH zu9gN|8yu!`U*;I(MkPGES5M^j0-V`Pd-Mv5)Ds35hBKt&dL!XiMPZsH z2@UB4k$$#W41MUuOhGrq7^pn7xMQ<1`NTGID!Zg-ON0=N7dLD@3$W5#qhze z>WyvyNGrmH8Nw5uqPrsJ9tE?bN4bLy*2iv7FEnQKp*UO2M9U5DQXr=y_${Wil*k=& zc>%IhYypsa;ZEI!T+*vp)-7#kV(M%*F0~1VJg_;OHOhe5BC#4X0?oM~gh;2hf0bM= zutZ`~6bo1?zTHOgO12G-HC1th>a{HVI1)CB(RCPz^h$~s2J#^|U?A}19 zd>e4`*h^krr`s*j)E0qkaacHQe1s`_nSuP1aa(v*S)l_CVG z*y&<41Q<8okphodbl}%9e@+3bqb_R@0RBBA6@D3uIXSfUlERoN?q#MR-A6%Q&Z*WO z@p>)~-X)qsLNpF%@nlxO+Ydo2c*KB$;VYN}S3zM^Dvj^_1Jf8p>5K&6bv`?7ka0|I zkfi2NR%s1{hD^l_;KkhH;816S(a-AePNPSRz`=$etC+#d3@-zHe-Q0!Ok%CoBpM5d zaIHgyr?OFQa`xa)G}SnYC31~&UkxWd(OQG|P34FAn{Da`vT9|<>=hH;0@Tf2#!IPR zx%|qGUJT)6RiI2mbdb$;fr#-$rl#g$+#uCNCyY)eP4n~hnOARQpkz-aIko_E6XZ&#H; zGD=_hyOq9kN=cqL^>&025?wa-j8)AfR7|3bKp0;&B2lNke;C#ZGU`x%i1T>sE_te0 zax_m7W$%Srwi;o5NGu?KSFl^4l2f`0@G`HNbvZQtRD1Q#nnXyCcjPcLq0|qDhXz~} z!!SL(!`-}88#IgC;|dK9&mGPh>})3VevKl#&miF4k=^VppD(27Y_)JU423+Hs@DN` zHED>g*6$mlf3GGv2g7|MHAByiqJPF2EOu7HGRPsx^f%i~S8Kqjbh|Eo)e~iENsrZ3 z`f@#`{8_Ka$_AGiyvQtWcG_er~eAf%JIu8TNZrdYX(Rb)|B}a{@6BonE zw=&+kxUO*fgMuW77%Pw~?s@#ZC{?Kiv(pi3y~lVYf3O@frdo4E{=#U31r#u0P{cb@ z=pO0oi&RwQxwr^tOV3;5a3hU!P!$iYWeLh2#|Q4XXYT-0S5VT3YgQPnBANmNOypEc zeKoElPe<>(43W!yqx3#)*t^x+-8I9+D-&rs9Q8`aV6a_$1c#+kY|7>BF9cp2J2gXtWu z6`{EXc710qebHIVHX3xmwEayoimMO2zq*%=k;coDHGr-ptp1}BV9@D*oYYjYDaI!?tli~uI za8VFOE{f6zhFnFC@Cy*VndCf zIFKhzcVmZlYYKR@tFvZ0gOs?z6z|Ye@8}HjV=OGI{rnr;aO*yqAf5)U6--|X2f4IkE zhK`$)k<#4Qt8D_|I@joRPjxU%oci3U)3e>&A_VhJ4go5n(6{gJu_LRUoAWhM?*a;k zi`X^e1K2l6sq*3>^5DXx0Sd6<+2#?xotWEx82LCa@-6j}^FYAso}j1;+LgEWe}7D5 zzN(Ys2Xg~D&1zqm|Hs~!8tg`+L6MY1i6UhV zn!fHX9vbLgP4DEbq%$%}Tasm4$Hz53y8xAxBy)&4G9!ZKayh7vm3DBaUTM2Q!?6vn zo=vZ$sOu=#=F+dTvNR`^hHNw^C1GdYHfVc|F5 z<*k27yN$-B+ON5pI9yz`S!C>l{PLnD-wY?+tZLTS&2rsuMm52@i93s#7MGeM->M|D z-fd~Vbix@i=S;gfj~gqge;jO-YbPz4t97}xHJe?@zHCNSdemjMw5PXklI3;UzHBy; zez=aK)_BO5SCM@^H*OaG?d4L+^3w|4m`(=Kaz4LwwW|&1@pXs3=rot%Fj^zWyV6+? zI$kiEO>1{U$Jr=?L?^bA)YG1+4#r6-)>&@QGWB_#EV&%@T#KKse`mvNSK3&u$#CQL z#Ol1>Mh;_d?NxVra+mBq}($OV%t}#FvwWEVruNU5jfq8ZPc|E?syj zyK>t0dd6^@>H2m&5^2A+xS7m&mv7GEf46rk7r8z+PSR@ycRvNA;F+QMs+%B$;enFO^z2CAQ6Qe|;GSrHirIDG5BYxZyjS z%pZjp?dXm+S8=)RatRUx@f|Y1D2>! z5}LP+u9~fNpKmiaQES-U3=+C!R=hZ2>!VTUO7d@&+Nx3O$JeE<7Vr&ifW$fDZ-%{& z&2r_P$q;$Cf3zy)q%GCU$#^xonDHh3qRDof{l=o^O_iuIz7=mKb@xi|v(sjcU9+Mc zM)j4lsEX}s+Tw4NxZf1DR()b!;0|5ByI}=QHMbX8qj7WHD-SAnn{~QbdD$Ip%q}Lq zzOx!kYB%fdMb@9i16D2t^|5u;zKPcey>cloH=3;*f1^Y=rMD_CGN%%lLG{jWZkD~R zKds69?fSx6-;CT5BCGMm-Kxr5&OG(7Yesh zuN>bj#@ADOKC|^Z_Of<0&FXhurDCZ>jq-s#uzfZjS7_P{(#_gf&)J?nZ}G`hC1_*I zcC)EA+S_ZIzZo!#%c?KYtaHovYa>LHb97zzfBW>*tCDT!?TDFjR{4sR;&z9Vu9jwP zni+;%lT}p;M|xPf8--(hZlZR3{br{o=r@&WySp~^dRu*b7LXvTJVln03Q;Q)RW-@8 zkN%#FW`RBKb|qD{HtF!W$cjh38VPFG^1~b%4jvMRdAh@s(F2DaMrd`~+zA`2+w1D8 zB2!Xk`?JIyLeG1_J9pKIc!3Y2bQ5NI`>4~ZSt>d$LPCm@lqoHL`S>8F0J4hr|M4H1 zxoK}J-rWRsR)Yft!0ypswNfEV&Zj>b^yl`#T-gI=ddv1U^-#-h5x})J93ZFwmXcat z15&aQ>EaLE!3PN9g1|Byt91}&d@5i|dM03tgk$00sv;N*VyynP@{>HX?hF1Jc>h`? z(I*HE{e(OFLgGUL|FyG1Dl7pJz!+9c*RAXlet_M>uP1Tzp^Wd0xyig^1-9 z-UH!tJJrT%2m?d*wmj>2Fd z1LDD6x{0LLothAu(us$~Cq|Z@9~t*4=l2{g^u+T|O^TuSd$>FVJYW{#9BKMEgF+V3 z9OH2k+ph10DJCUoOMx<+C=8KJ0;H9H$G@Wvqc4z0NUflcKR!Sr;0Maa=?gI=uEV2b z*n6_Pr%_Cw4~Y?{OdRcR2p5b6If2uz1K|J)FQ@P4K+gjh}z&NhVJ zZJu|V=fAVfqb}Hz7i{3i750msws|UwX3Nz)`mJ)knvTV?(CLeJn{-5Ln%mxgPWn>K zYL7Z4xizY{s@0{kxDc|dmLR2DjW3r*ixO*lhIrGjap9Lvam;--7DIsDVs zmD4ef)`Rz}u959i2P?|JAW{#!Uw>gwJP@{2`~`GDTyb$?PztH|e|#Y74Qnksj-xfv zO?wIkf5UlDID3j@MnUS-L%tOJdYq*XDf~FM|3ozbXvf8xkb<%u&^)o1c=5{n@RfoG z?;nQ|AD1s+WmVv1NIu(tFJC~b62}PqL#+ZBP!f4EkFZ|$t6Y$|*4`3Wc(Hu$` zVZfn&fP87_kYs@VuW~Tw8G&I1nPiQhd;m%<96i7wdu0y~C*}YUx}N$8m0*xj=vz@l z2@vliZb_#e)hN<4sS+B1GQ5=+L`z(VtmdcGKdM>iQBX|fpGSx-CW=sbzpk8uPG81G z@G+W1O<`n1ffc@gSjEE(^5McMK>0iB^==;Fvjmaw!dRIx81`W5`OR-L^?N=wyu^w9C78x%aE~bQy2W6jv$r-;F=W%A z<%6|ZnjlN!>9y^z`t8jXA}g>y{%rw`P6st_&L2LseE?@acW(U+A`tZ~3=QC^qhGmieCmne#=h!<3pe`EEG88bR zjvObAVQI+#ZhO(N7bMQQH?B^Mhf3P*|F>(XZa-k;0`U^l3>KPd#zDvEK`x<2hwlKKy!sYVN0hAF0Gk@nUQT&eJJ;QVC|-GO$zZks(X%$Z&n+ ziX!G(Jl>Bj*~n0xe1;M62O9PB=A5n`oLbV6$bUWq$Q-Z9n!%m3&H5T3pR)f<&f*#C z9Q*H^q7_+qGl32hFG2XYtDz6!=%$w(!`fr3)QREqo#e41F6sxCyH_A_= z+CpM_5g~iY4~YqekO^3Rv=5fUwv&Y+PzI4ukM={J9{%e`Gz2D{l2N0I7lGjM&cPOI z3M$W=`Wdi0+-`nxBN{j10so>jPrvpsle{3X(@~KBmqTI{9E^|)FhVfsBG^bsRe0kR zN;qHR=n_UI7>6j1N3BrlRL#qOjRIhx4eFj2ZKPr$v_kJUe|(VhQx)_xuXJ6^CG=J# zN(o;apyoBHL5rUzs+UB+;2X3kM!GD)zoPq204NYL3#uKvQN$kcuxXc@1^{|o+7|l7 zhDCpaP6217K4_iX;>*1n~E1>5saQ&bmjLMoF5=Rey^~tZjIoxwsDfV6X zIzj)VJ}6AYoFXd(3J1*sC+>AoJekJSo;=}LPSYO_f5=9U&a)PsOY(;UB~xZh9&UX` zMqQKV$+is{jZEDM#|MIcfgzy3hyULNVdT0w)bLNl>f!!xba@}daC!fCF*>fye9&)k z15*|2%!T@+kF~_m`MMI_z+B1 zltc`f*NBf}*5O4g@LtI}Ph-_IOp!KWBVvEYLXjj*)*rC-HdxJ@p2sRwIX^PbjyORF zfaRRfioKN|WeJ6U4Brs8mvG?KTXDwIVB{r_h7sDE?R$~oSJU-J=dDak2+|Q4sR8wOF zjT|gDE9ReP{kA{7YM$C?Og4 zO8)&$x|G)#UbT)9#Bs>;k&T}VLAOz~fFlVcVx#PT8GGlANLS(%WDLniRt77Z8mn7B z+muqO7UB#J-SB`=gZ- ztH`=#X~%ntqRi7A^8;)Jf&p{me|tv<0ZKJktadD^g#Sn+*U^@QhE_`pFPvZD<(iDB;+W1yD?7YGR>mgEv-(~8C@ym81KuSuwS znAT}|ejlkkwG&F9N*Wc7sVwxzzUNX4ZxO{?%3j%i0K@~CpOm+8f-Hf6S9`fkK2qzc z?@XcEfK9ov4N;R0<%h^Gu@etEV`!XzPMYAoF@a&JhPKyZo^*+)!HM)|st2)`X$ECE zg6&7D(xgU6>*E4WIN2>pqu8E!Xl|}=Q;rSM{pl7>A5AEXQtB@AQ;(W^%anq(lDs-C z$UepPHh*aCBhP6X1un%aXvO;z>X1!>;)2n zDM9HT(5TUt@qRV>Yu4{w1&9fj~MCrzWa_zpUJ~y z5oti3gs_+$qUoDnKc66MrxN+OCtpB7MN33`c7W+F#tASG>Q7b1IMo-bE~LOLgrGkX zlEj6Nh};T2Gz=O!_KIPDA0~8^y&r{Gior^-k9`7+IZxqG^j42bvL!H0at>J|YmqSo z^PF^>w<2p^rB&_N+!Z-NvrGm2`fsZ$_p4?gG!cCOP(z@;7fMKBJjTmQl@#*iZzQZ- zL~IYK?7$Bmkx&IcU_>5BKCJoB)DVaH4ic7IY6M$?X%taO5UnwPtz@9r5Xsm9&Kf5Z zz+$Z!>VXdSS4h|;@h4yqI3P>&{BbgaXyLQe{XJz3iYVT;o*XOmrL|dBY*ueU^#r0tFK$w2ug&rj8;={!(jzRa` zBkp4^Gb3~r2@21DnTGyd<|SBiAOr$AP*`z+xN@exM+_w-?Qm;>P#^HQ@*oiW< zE%!|-4*kUIJEth(uT7f!`Zk%`U|Hr$FV~h}gQnBi^Qe^_`&g_bUy$$w6YSL_{c~5W z`Mc3@zZ3x!t~|1wsC4~uTJNum)7RGX{I!va%wdFBOl*9Gso zpwI>HKEQ9L3}Ps9_^t^?q{|y)(XRzPL1VmZ7%5Qg-@UJ*A{^mZIkKb(swIPt3#q&9oej20zJbwrG%1H4`OFIl3Igu)cTQLaaSLV;Z>a-3|5`rJ}(h68D){sh8pO05pQ z8yPfIme`ZC;z6O(F3o+jc+fLt+53LdPqm-)e)RbJo-TkaVZ{IUpNxpU#!)tzQVja_ zu8e*od-wES6H-t2TZ*oa9!{Mp1wp!?f)X$Akmf=r4CF|9III(5h`^jdDrOzUb4ZVW zdARsaFTV}FoaG5|3~$N`&J)}>6gWXM)xG!EzI#HysbNqAg@a&OK>kv;pw`GCLcR`? z28qAJFZmVuLTcdPv?2hU!U6qzsVN?W*Z$^q#(yI@LR5yt@^v!s(3Gj*r}r|m=khAq6Jfs zi!G6}99!MAc!A-M^wrO>bN8;9-jr}iLIY<|Zsd`y#-$f8{FEB`WGi3OKjmqV56OuM z)l>UONhMM7j_LpU_y11GaR8Dt&?6E-kfn5#t_TT_#daQS^VwLfWodDuaPaPbR3KK^ z^nvtyLdAiPW=1l7VBawC?IOt(XRP6z@bt)Lhe;I~?|2m?gD-(()qFP+jAf{25=3<*iZgoOuz)H*pg!3z5~m9q!}M|Ez0Z$bNT&_&XQ^E}S{l;jlA zoFQ)|mQ!>^JoXRX+mP}$wv$o|Y`hh( zjmGBNI=UCiNB~o*A3)FKh0!2)3X^|B9e+i!kbJ6G&ck?+Aq{D$3G!cp)DAs#Ka%tC zVpcap^aHIvNN#vZD3sbfFgf!C^$6(~kemnmA!q@3yCormlDzn*#PnGl#fyli=;K$A zGO{Yu%o`QNcxRq(s8%Xro(GMr0OAkl7A_{DBJh9^$w`fz=SvsF=|V|`dx}bZTz?pZ z6uMaxR||K1bOc#Y<1-dPM4`47+3`b6PEGBdjb{v&Q8LB;8v;&-^ZES{iGdW#?ZnX% zvI~(};)RIr&oRnrS`ZBD_|=Xgt7wv9JOs+Xjhx7`+{v0uhgV2VCennFax@~Nn1b}^ zWe=9BXsmivF7*{7VrqLvRyOlW34i!wKXG%ftOA?~`b82rLJX3lN5;q$Ss*d$Vot?h zd>6mH8oz-33#qiQg))RE7-N5qIb$3$deQAMa+Or+lX9EBR?T8-JZ4_D;|LpU=jF z@(N>Fq$uJO&wqOK{4+O`8bAfp?w|1%+Z@zVqowf{F(; zXG~_Crt%D7fi@(L)wft>8RL?s3xv^zMua2GIo={RM-uY|k$>oV@?Jn62@M2IXpA}f zuHi-CcDw&2dbpH|*r24nme>7-mC~H3@WNS`>n$f`;g~$@N7r z@=!)I64R83E`R#4q%lEqNl>ziB|Qh#k>ofPA&YVoiHw9&)J4oVWAiarlFDrcvn@DI zreHqV$qn$FIAG^v3Qu@VWj-ky1S|z1HkVk0lwNf_mI?++b)aZDOvPc*MKNyCQ4YE@ zEU0KB{y2?F;lH0aM;z4X%mH{B%>jD}gNKzM^HmI(R)6oa!f7}~VLlJMP#F^dEtOxt z{7Wcg1ICw|+`G^x0WeF3$Qs8hEb}77(({Nym8r$OQDT!UIOUwO?1_9 z0%PQvn%~JwCHW*(sb$%HD;OC{{YTu|hU1&wW06nh7y$OPW^z!JPgt6`_*y|Lkol@3 zK*X3FT7OeXxch!8scE}?P1pKWNj2Lsb=~VdCu!mIZxlb9$}L#F6#j2i$O#ArD9e@w z#?n<%w50Jm-nG*n>u#c5neLWKKlW&EnjM^8?4TU~adhuB;jnBlYXju-W+WfsffP+R z`v=eqnueIi1bBMORwx8mC-@cm7*dQ3b%KI6B3zP$bkUR)jQT3Uo6SAA2L!J;@%Gcbp-KWLSxj0HfZMaIvEkMT zpf@&xz#Ue?O7B}kW7HHu)tWql4A=Jh{_zOo_AJM?+ z`+uX}FsN$al>eU7pdbc+Xz76>z&2`?MY*hv!xReK(vUO3<`NB~H3mf@bWg%%%3`+I zqHE4pSr-M6ZJ<%i!UwcN!On5$@4fp3~)t!k5= zZ@QXpTNRsFhVAA2|3v+2&7t}^0BbXj?SG+Z8+vU<*B)xRu1NdaW;}%_-eLa0HZ=VF znt&Rdq2KxEt@te2yo`KCpPcz>qhN(*=pqPu`se!+};mcYhTc z?JoN1m2{ph-jMJNy?<^P?$51)V?6BZ<3N$yON8-8!Qr@C&vXp4I-_A}e$V!+;dm0s zwqv7alikCfAY9T2pP}EdVCfU2Ex}y<19UCX(l!dm$&PK?Hg{~>$&PJf#kOtRHg{}m z$F}|FocI0y``tTwRIl!ywPsb%QKNg#r_hkN>}UvK`bvnYJ0hMrW^_cahTbsglQ1mT z3Fp+e*CExVG=~8izi=`Nd=$FXE#!T2TkQ4Z?rVa+XE=m45E4XxI9NB32wB=fGCiMSU^GkcD8Ow z!i3R0_W<4ABBnI`LqojA&s+O;2S7cp?6t2E=sG7#<(*R%5#ltTdyA7^5D>&LPypDv zW1_qwV{>g-HMab+RcOT6r+T(mmVEC+i|yg!O+2;!xf`+V8q!;D2okWT14Pxl$|vm4 zbBMCU{sxL*d|&roz;4kh;VLa#R*$U=UpFqZ!gR+@wX(ceeB8LjdX$=q+Oa;q;@l!B z%ybIaeq<6w{LL7G^&|owD_R#ZO8}6vxI=2$ug?l0JfXDk)Zoc|+H1|e@r^pXH-kTa zcl3Wm(p2ae0t9L}^Mc*ZR@p+Tn7k^N-rbvUtzX$%W$Q4FrZCf8Gj(NWuA>8e=70$+ z*|-QN3cLpr5gfP8;<>Zv`4-3zbO|2tmf)RaRvbZcEma5L@+NrJ->(uP3TTlLN(oUTuf-UQF#5$74ghft+=lE!?XmU_c}a%^0-4Hy0_1pkQXNTN<(5*1C{qPc zsS2Ld&t3d3TFI>P_htp%&I3Mm5C%fOYpg%%yk<0-Q8M5G8hwN+xQPlN78eMkT#~CD zoPTl-kNsy!jFA*qyM|dWLo`GF@PhsoJCm{0ntafk3cAW?B}%YcasVje6!cFzbHaVGku&d|x}1WMiQB1jFqzuhlgXz2mwq={NORBAQWfMp@&{W9NFWU$ zs5Ic<4Y6(41!}Gp?U;O4Id=*(gKx$#qUF*}m70RCF&hejShz;sT<%e#F(IX8b6nHl z0FMYv#OR@esU0H_EC;ASy{mWd095)S{eIc^0bV~ORV0!;?xQ)Jl+K$i!FL)LE%xa( z2Uv%AdKGq^PoOW%%$kFau)!*et{o!h(6zd&8CEkYwsa4VLnWA~XZNIV{By1cqjPCA z7cf$4F#g6*d=<3Ghg}uvMok#NUZ2s&pm`hwQ%#)#?<#!hjv|9Mqp%$su##IkEPDIw z3mUgCN51ZQ`g2_R;V@EQ^97`<1|1Zb_|FNw@lfs`=r6hhSpNhNARzF>X(b%MmhCzt z>gYAaB_8pP@+dGeI$DKovGR3^ixEG8{@;M09Na zMo@_<5wwD~)lx*Pq@}o-IMZQ(6fxTixOm+0sSqS+>2em#V9Khiq?91+ee{ozVSF5g z{lA8obPH|H)A*c$q@(Ne!lS>{g^zAvbRGofpyDDKg?tttELC8;EAY+Pp$}9ZCGH)H zVETvmB@}a$qDW-li-tLst~qbNfKwx&IojhhjLz{N(H*Pw-7NR|ptYX>1JI_iF<2vT z5G=RcLh<*Ozq~VaWhDqxvFJ)Ph2y2`7Kp1qDXM!@b8Ej(1|QdYMcbpPKh3%YrPmlIT%x3#X_JHhx(VNyGi5loFcy20KKfFUU`vSKfK$p zWuvg5-WBU4dpGFLVDVzW5pBD<0jLF#A_5``cik>Wl<1USQKiC+A*giWZkFq?lSZlr zYh7$NniB+L`qW(|rDCG`iBJ777yG5)vxsMQnMWkgT_bM!Uf109(7SNJWd%S&=Bv z8%^1TsL<6Te*Oq|@dbc%R$TS!&*n>8N>y|MLxGzltDI)fDz#8h#Uk`{{gf9x@G=o_hZIN*#*vCs)< z1f514t8aN&Wg5sNV(Ji&kCI&|9=Bb2FSn$0`u%4R*xe70@SWi?%My?K#u^# z?Yn&t^FRgo2688TAv=udY@qQqLU^HYw9z3>+i0VPx_<`!eJ;AwVEo3cQ+7ncGrEY6 zBk88=Ef6|L<~Kx!>8^$t9E)6YX@Ur}u2jX1atrxfY^t3JFgfbZ0y=0N@41*}xYTAZ zIVE#lg)Bd`{D>(L5aim_Fa29G5JyBiE%@p8Z!f4B@QkQ{a z*)x(Fe5HMEDIM(;j6%E)4|Ex?$!Oa=%bJOA$2z+-bnl1wQGulCq16_`t?v4(Uz74? zQl|He?p5fEw&qKDkI1U*n5t~c1GU4yi|S-9D%rn2_;dbAaY`bOK1T*G-08y%wo_jO z&USb;et{;=$)Q41f|;kYK>-1cQY2bv5CXaahiBr$t_@~M8x3T*l^XEK(zgXjs-Zi9 z?8fOUtqCs<{h!#L#-6gM^$AJA{>s>ubZXn2x%O~BbE|$X%*g**NKrXplAqB&N}roI z$G)SKGdr%<#~-n&Ej|SQL5$p`X0)v`T77$7cI5@C-xh?-e`g9_jiLr~bL=rY8=DAZe0Q-^v8nL_3MI8Gm4 zf0DckBxD|L?EFd!-h#;(YE<8Ct*|Ej`Bp1WMtaLa(Uh0BZMM|||bvz46!6VXIJUpaR2bp%ZP42Z>MRqTJLLKOyzt~MLKIZDO zTH1y-4q$3n3YmgAy|L--DYVp^6N zN1N#LD5=S-KHAYt_G{On2bb7jDQgUjvwCAR&8OPPF2`n%FiQdEkUZp+u?wW{=L{S(~}X*tFqaQHR=s!$YWz zo(^EaEd#F1MB-@v4ZG+tU`E9YVg^g)_08G7mV&q|Jh-knb&P-HzomSH7GL5$n{0#% z-w0Cn;4Db<6$&s%fV~R%X|OXR&np=cnc=cO!G6#f_V1L}icyeyX9=d2#4mPdf$YRk zBCDLOZF1y_FZB==idbL#Mh90XH>tW%1(NEsmew%C8?5W9SGR1KPyfZ;$|H>HGYBtM zd;T8iX)jW0+z*{~>i$$l@#x3W%6Z^fV?|YLM#l0yoG~Yr!gLIdCN%>WJxTvU)*i!AKwa80^^MQsT?09`xKdD&Ldj6aPEul;~;m&FGX4e zI1=selBiMz#-YZHQ|vZXH;X*w_*(s<|oCNGBJZVlr6V(AqySDX5&s6$HmhT;Ut}nN%Y7DBj?)^Hw+)Tq=-ENUX_!E6)3GJzRz-M zl_FC4tuNDI!O5%BDP^v)6LP9IL>poQJ#y+dRa#LLCn+o3#HgOMv?K_UBTneBkV3V3 zKqx>{uRw9np4I>u0_;?pC7ER9U!I4HG|z58-uPY8yHmpS3E>Q&PvSAX4eF zu&%5fwl#ibQzl~pJDayOW7(4TXxrrc>*;rTJ!plyEuGTj*T_rM4n-VH8Rx`5+hUH& zc=X3MP&Pvc_K*cQHa{EL2McCFjF2HisbRkv{bSKJrHa>MMojoTUa0lKdLDBgy-_;^ z-W?mro*kSGzENmE)^2Y+c7trzB%zgAvlXuA{(4(4PIEfyHk+n${cy#S3?~sW8(gnQ+m8BKG_(Yot(n88x#x^)Q`8c^jVHvUax*yYoa;zmbu<w1plY= z+~#HMuco4EtAy+RBd%e`y1d#9Jjbh5B|8LyZxBTtjSv%G4q)kp6C9Y8m4Sy6{<>yh zql_vh$Lg%uY52Mq0{NrDt(R}&Q>$2n+VRKQ?f@GAZ>KjD7X1|@N9#&If68+=n~%Nr zL+%Fg-~%3%u~;|R09$;$SC?6(Yzt|t&&t^&e_Z{DJt)?mbj%D&60?9rSDsv9`iEL< z)tUAmb@}R@Vd+X9$1aC|M7>^mr8^H_miGI_E?({47IdMc7-THk4Ar@me?NLsm&ep^ ze>>;dCM07l-Nw7~?IPd!n|M@?_u}b5WR!fq6}qBq>O$<#xN!uAazuU#@lw_>d9VtRoq%vC3h)@Iv^at6}oqgpegYTl zsh|BaBdHC+7=B36H3`>X+|wr2hMT<-;9r)@G}M&BaD{V?X&66kBo2g59(B9?e39p$ zv_4A%g#-cEe__#IH-<1TJzpYvAq_fPRkUI-k;`Tv;21xM8M%G zSkp8_?NSco&VqN2@u)UIX2NqC#|6Ned=x{}(l{M1z+NE}^$(~Lse^#J%d_T0^oL{~D_Li`0>mrn+lAa-A zfh5%f4hhkJ3}<*-CS)MZ@v=$w?$emH+e)hn3;%pI{C@d8 z*07pK#vuIOBxz;U)5-~2314O#2S{ZwIywhIqunr>J6PbQK;0^hB0Coyn!75kge|gp6bwG6_#aL zJ&I)GL$f}5L25?Lz0;WA{5{Bsu;5L8ke?2US@S!Ctf{I85q`Ros=_K$4vgsEAsowzcT6ESDr9-Tj)kraT60wNP!HQ6 zZ_6@hr06%Q&z?+L2YQf^Y)eCP`(Cx{XD#px`3UF?^kg|JRCRh0fdjL?N_0YA&u%qQ z_`!9F>o{}#C~ufewrDUa9KbQBD1mUe(Y2%HHb(_*>?y+{wY4L;aXGtC$6f#tRWSnT zkXMwHOvbpWDy+$J+|wocy9rS#=Z|)#o&lF<;l9y2uW>%m%q(*?H(xZu6}NA7Z_E`~ zCR}^$!UG0rn3b#zN^69zvs0b+n;PVKH3TSYu&8UM)uRnt!^aV>IiN*Gy6u9Vt#Jj| zKpUeAkKC7@`23$mMvQn7FcA>_L6I2(7K{Ig+>qZRx#LkCf93BFGG~0&LuG@$nR~8~C7lAV*k_+dP;Eht z_?rc(lO>tD=hJg_5I;BS!f8yq{4#-5o?4{qVQVkA;BcYSZQVR?Si}q(S?sJ)*Ii)o zo@lI*ro?=)2ULV#exX$>W7`V@JatnVHtSDMr(J9kfi_;ba=wiKB&$vWobZPK;1Dg3 zCr)t^l2j3xxuBf+%3p{!i)a;s?8L!`$x3K=J-@%dZN4eJCCDK07g~T}>Fo1wrPh7J z#d!9e9L0a7+fbGFAoN8Hla4Krj!I=EEwyTY0f(h_0a_l~=^O;b_naL9?#udA@@@Hpjy5*~iHu4zZi98!_XIC0jZ`ld>)jLhzeYs{hS>p9U9#v)+}{Fg2azRwAIHMUp~O4gOSoNc1Q@WC_O~KU$vfgTc7V5SIG@ zA3t8DgCV!@(!2loV90I;MazSxlI{Q~`C?bZjhmQPu~KQZXq*n4zec3TOia?qpfwxz z*BCysz-{zN+7EdoyKc4)mV_%c=oz4CC37?=As0!0>=yM?v$ruRAyKtg|?#T%{6`cNR{3F%QKirXXhfq zi6n85x3f^DQ_@JLTCr8uh*ytcS45xmA6$q_wNnjgSW5p^+6Z0NPT)_wke5=qq zDBO|v9@K8KrniowB5MUaypGs~d+@~PLtXb}V zsi~Ld$KI>!TWYPe0RP}P zB|Xod^n1rSg7@{&Tb@Bbi0mk@Xd_D4*y(y_`tM@S!L>h_gLOK6X9-do-s$f7{x5>@YyIqn3G2VplWYqdz9?!41Nia#*C-GK|S!A2ad=dYmOZN?a1^q(#WhV*AmVF&u zNLh*vtf=AhQK#eGbOK-RziG{RLM6Ncwr%2ag9RRwiLAN!9KIbmY=a?WXk#b??s7jF z#ndNzWVEZ<6Wa}8fLI)s;)ooZ33o9T;@AdObe+3!?g$Ul`!Uk!^6mrT zdcIK?=SoE05~Tq}&;*9CAL#UU(APYiP&4dwt32n^Tr zieBY>O2G!Fd>!}>0R*JNH7;NZmq^2a365Q$hAvM z0^RY)(oAG?CXyToL!@{=sn`-cO|CP5$if!@BwAM~_E&2x6N%e(1Z7-HGI$^s$0`1K z{Bz1vAYq&3eL}uj_+qek&2fC+L(Hj|s}}F2k!^RXoNPtMMjB?Qaw)z z?kw%(#mJG1rd;uRkJBFq2pEOjr6TE?Aw(03bQ=*Mx6D*gj`cyWKoXEI?VIw77j3J31&IdX}HaBKBc9>0i~B2Kar!>CR9dyg%h(7 zTM3Jcys;MqVB03!{4GoCnL(Nc%f+y2dhjvFIIn9N+T4TFC>FK>aJPFCFtxHM{cf7r z+a6{;4v4~10J}2u57yQKeSrxHAlNEG@WgeoF^sav`mIznQuUz|*>Ky~EaC=-3z)q7zDkDV8 z-Hceajw_hK0CQb<=N?X2dvHt5D_X%Oh!pK5#ljJElq-M7F#u71Nq7YWGTYXSG0b50 z(F+{u0K~!}B%2ZQpAu%U%*R|fnWoNQzirCdbgs9S-WJ@f$x1gx2l7`I`Aj|QDw(* zDRb@zUzJzF0kF+#94Mgz`_9etG02!&Er-pAC&HXWgX8aa_AFI+exJgarefaeNFHE; z{H4owwET27R5Pbi;Xz1Zudw&K!5*q)%|mFlnzzDX3pf$qro3UbBM>(M-?gMzFWHDv zbp{0y+Zrc_5hO!{S?7Fx?xnTCGd(pkwjM|THvTfd2b8#!^0~^C#<3*R2$1Mo&?mQn z#(HZxEL%8pH2Jlj4d^QhF=|ni@c8eku>V|a0MJK3YFX~fVI0wfx<&k;06Rz89pmd zvVtWg1w#b16fN&}q@HZ*V;+;*OC&LsCwMB=GW)cZqj-SXXmk`*7efV`J_U;8l4Rq8 zV;9g+3zvoq+%)=Gt9TiB`uu2`@3Cl~ohR3jeFhE8L`62xJX72c0g_GbMV-Z8H)*hivx_kx z?&>i!E)+JSX90`7ux7`?^`Gn+kM9ZmrXX2NyT~ZfMU%u11JXp-9UMjo{rwVlV{}Bk z+F#i62(wkdQ0p!nqvg{g%DT$(vOixr0Z-90BNCRcf0Q`U5)|;h3Y3UTB_|q~u6XbB z<>Qf%go~JmsUfH{1giNxF-+bPZFNf0P};r=%GW6x4u9v`s5^K;@~PcJNCQRyPmhn$ zqfv^6<7(rVqcme{eNP{nLAJ8scZVp;GR1?P>JoD=nUw0r zTQ8+edv)G^GcN5%w*owGQB!lbPHM|Ik*uiXq?*Nf7RbysjZL(`(DJOGlu{1r=(EUq ze{-(UbwcXO8Q;p>+H6CW{+F>XoZWOtVJ(OH+*XcB?o3gax-{RGn%Sx+4hzA1iyB4k zqAR?@=%{0zyp7p;MSm<7%{RxTA}zm*ck5x(Hh!erV2v1ndZgz2JrDX1TQ~~gl>pFx zJQ#t;4xz^mz&`}?L!hn44q$%3zcgl)e{vmwfQVAzI09n@ZC6RnjMdseq6^jM zACB#xlFO-1-+ozp3 z&6H13XVJvgxzw#iUGsdaW^(C)!zyiM6;Bm8o4|5uwSRBC4BC5Ewv}PS*0i!W8LsbB z#_`!MvaPbxV>_O+R^v)Bb5C%e?dlP_danJ9do>Txk%32>nP_;8H5Yj4X5gi@_Z$3d z2utiZr?C}j)(nhSU4MIIS00yUmiL${vi0@`GUpCeW1x2`oh6{AZum=<);Z?9VkKGa z%H%mtl&{b;Q~ARGs)v8LtWqzLjpU(y6Wd}WBTK1ddL`Eu+vcDJ#EsAez3x4-zw-+t zVD%dSC;Vs<_w1nI>xX}i{jKsjrGYu0`STxjW2`sFuLh@h^;2?qN>d6 zYr2FICp_jtNa11m^wy$-X8c;L@ul3xRO zzt~uPmoljn&%CqB#qP!D)&|*+>ptAc*Q>eKPNuRzh=bc1TaB~VRBcf& zW?J{Qgr>~AHm%ycOacZaOIk!T{$ugr5Oyz4217Y@ZTvi2-fPI*MDvVYam_xzeD=2Q z(;2Gqjlk2+Ew9hHhAJHV7$}ghbW{!bC#)EA@+&pL6-*kC5kV=0wBmp=Ba9>! zmbSwrkH`_h711WQXUZu=SVB2~xyy-D`T(AtHU|`4mf2=O&Nx&U1#Vu|?PrX2=8q9h zD%hCHC3I8Ho=YeKMjxZF2jzT>c~~EK#lDYE${qnle$xh=(9XHXA~ttY_g7kowSd+o z5m6VRl(?TI9)|wcALrw?YjWTWyHtBOFjPp>0(+5&XT;Pe@uPPk-Yi-IBH&x4E_4uLOyNdqd(ntd!W&IS{QVpf;s?drz&JA`TZ%wjK{xuK zE1bM+2IEb%1^?4u$a9FTC?i=;tlWml+U+U2}8slf)oyIflsZZ zCSTq}9#KUxiJ~XAr9q)s8FuB;#2?++Lg#}!P8Jdpek4?fFZ9Ovr05xT>Gr%Koo%rZD+lsRc~0@PB_U^ zQFdsXY)f1XT-Ihcdw;+>wA54~LKm`F|1W)Z`iGW@u&05Pom5-M&eX6QQV{4IshPFF(Q9B-f3y1T-iQgO& z4!-erEv%`0U}DsCL!-wPt&=>nU5W<3(C>a|7Szgp4YCtG!`bXt#H1r4)hc z?QM_%O0*y*{mqVNVXmqAogTqOVLRWlye$FC!;Xi-QRO(vA<=N|%RFGA&g0K~7*Ws-Qr$ZzXr7V0W!Bs->Eu4nGw6xa)j37|Wo@>Hfz8o%D7p&Kr;t3W_eDsEH`B4cogd%bz|d znDTx*iIAAK1*@;g{&72@|9v-jPpK#NOnXQH-xkY>!^cpNS3o{dLJA*0fGRO3(V3izIX$OItFuZNRsCbXYR0CVoCm&NI-TUx1ikZLvmIAL*b)h^xV4RW4zvf%HFEosm`r9d=UYl zO<~%q_{1*D>w!d~V^P6p)|X`MSo90-O%2~FYkes-=u4t9k}4mDE4{RssZ zp;N{H>ji@+`CCm& zewvx7LeVyUBO$o-)e3cAWd3jjkS`^6%OnP&FBl5twFHxNR2$0mHg<|)Jjh;26#!nB zehAS7O|08R@!QGF&X+pgw?zj(c0=gWX(XU{k1TB1Bw~<5Sd~GG`FVd7L~z*4w?-v9 zt45DpGRus(ZH@IG$xZ6Y3I8t`<;+o=^WTV0qbyJV?*CS0q3IPB819G%T!;wK#oXu< ze4#uI4Pl9klUv|m5NFo)agH&=X#lui()sK}jynq%te=Nm6_s5JO1c4yUmusf)%&L7 zb#0%axpZoRYhsHnvCS|FTwyKXPr$&Ibey8f)76~olEdZDmrZ+8J}&YO72sI|JK7GO z;%MS7|5UzwBUrf0Iio?2&rqnQFUt0uL9zy7)w7yJTlaV@=hUI<_}-9X6#%jGcW?P8 zR+uGR4iwR|>U@L$IQ)Fv3+i`dsju8;xnYJ;!`Rh9hYv0$W^IM)|G0W#t!q*)^EAL- z(xO(H>4&(YMjYBQ2zNpcXE?N&m`hWlC(YtV=vpeqMopZD3f0$Cvb$t?Qj%9(Si}^j z+L*7K06yNlHt09{k^SAGj{rbVX?6W8+J=)Kw5^SOy_t<+2tAZxi4WHW7#yWx(f7e1 z>^u5mEtnaPU5<v0ku&JRjXCP@Bmay~n*EqKsXR{&V5dFxyjoUyweL#F zFe4S0v7^vJ-7938e(uk}dctU1zd;YY^-PU6>4g1FXy5UY%Fx_r4A30M;4bNMOJ#VH zak{QNFk8I%KUYo0Se{N&c6sHpK6LxcIa^OUd3M z+?xU7`tptC6a?Wt5p40Q+9j&?m9|@=`m{(_ztD`r8B?GDL-MRCPz2DhDNsh?H{z7= z(m@Dd5Yj=05$E#Zb#caClsRq8&E=|k0=N-)mfE&RX_vOcQen1<3~E9KWJBU7a5O~l zU=TD!1`&f&P*p*GRPg^fK9?s8!gQ-1BTv;DBHtg2(9AzL$8Hgd3fKJ^qh;N@rRn+T z+07%bnM@4?2T7n*K|e4|z)G$s6jJ{a&;^%~(O&xDqcHGll1cCxeu?jUXiu9qBkgl! zCpbVM;(l)q!wn|n(AS~$AA*#F9`DRxX8z$ z^Ef(PKz($D8&xgWOmO(^;W@tmU2N-U)!f>!U!8=1(lOrx0Tl570#GnNO~XlOeH3gX zHFtmkBMq!Fu4ypS|NQ)CMAXv4TwBbH0_9NE^=zJDOpKOXWrXW3(6%b{I}^~aj^L3> zb0OdUb{x<8l&(GDf}BxKY=uf~ji6FK9wEt)AE@(PoSa zO@R6y+bXOoJJi4P_E@$L`vBD*^{CIr$`C1gPmSKYTf%|GZ4^ql|2fYgsFl!Tu)NhXM#_txJusE8Z4YH=>4Rn``8#ThmLvrRK!FOSbHJ}pj>`hIqWl74*T z);Eu8m>lcHhWp+`{!`BqBmdbTL4%CD1Q;boe#BsrU=5;%8pA5<#Qw#6_{%<+C_A-e zjs--Ch2y-ZuvI}}q^Rgizw^ylOwUKgl&x|;twcjRj8X*At>vz3h0ZJ`==YEE82T}_rfdUfj&Mu2(t z>vZE;1Pn~^*=f*GAt$Q_O^2UGAtxo;J=d&Cm)4e|QabYDI_8wj)LFF7AYjU|=v)F5 z7o$@;dO8OHkJX6OE$8pdF-r=* zMle4)qlYWFJ#sMT#%M=1%Wrm?vK1On{%5oaRDxX-mq(@rJc}j)K!Rf?Yoq~yKce~M zlv5U0)_5fBC(Fd?efX1WBtY273NKk=5vrH6T>QMJ7x+ZxaUPxWz%LTpIncSInz%(h zSzt%6D3KEEAZ+~-pzEpiAnWE!YUsR~3K#4KMQeIAu<3|Ba6CBh7`7Fcg(Ed!MIS?5Bi;xXkfOyzLG@8%tee4xa1n=izCPsT5p>FT1+oJi~Hjj+}5Xbe@!zZr5clg4|sbB^-f2z}a#_6creSKR_wJZa%DNC?5L< z7w~{M;xB>VA0xqdwoiW;=zdjOOoi*sn}6qM2gLmJI#A)G0=*$G0;T+L*o>T;+)h;qg>QKl`mAn{_K|_ zf?t*)g8YB!u1pDMN9<;myu->Id5jW&c9o{3 zTKo$;@Z!K8L*-YdqRKhsk2w1g6IMicp11F1rM;Nenq2>B{}=&3sV@fgb! zQ;mvCJk?mgVr*g!ML2_t@(_h(6=6!A3PXb-B{-xyl?_NUz5ui(pphI3ODGdrwxEVW zYf8Uk|_oGyX3b9m${wSeN=b~tHUFHc!yVPbWDa}@+byoay@lh$dGUY)9uwPM3 zU*WK%gt7G0uNnIz{r}zm|1`H*mPek&gEpX#adF7uzI?6^=rFB*QmoXvb z|1|zr%4zxk-S{Kq|EmHZg`lH@2eQKM;eC){mNZ$ zW7hKG9Z%xehpVd-S!c1=e$rb{`Td#j^Lq&^!)3u4Q7qMM&o&dl;Y+CID4(#G*CXtS z^qD{2>{`0x8D)<0P*rC$I;qvn0S@-0YCb{dAqRwSsWyCXW#c6&M<28LcjPZGOv6zJ z)3w9fJLbyl-0;TcndF^#MN#?jwO2>(PK&rZk-xhr-TVS0{0hP(?XwEQD%ZKo;nQ~V;IyoV@|c5B64GCLhTS`8byr$CwP+A6xiH*DbD0bR&NqVD5qXZ5cvBQ$Ek+QM{M@$}xxZmju$6Nsjrf zzh5bqxA9)+8a_L8lTIM#tO?u4YTXi85~Dy|?5{=G&^w~-CE4~5q-`6odEQfPBN#{X zve5_Ih1u3l14&K!Opp0xo|wokIjgF9txzqAjmGd`yg7j@WYf_ZM?VZYxZZb=h#dWR z&aab5yHZHNr@B#t3BCA?9K`^A#xcj9V5!z?R4THT--4;p(5NpD5(8_z*JhIi%0w#9 z;uMEm%n~d6UA@)vb9^h#Fw6Vfg92>O6#cb`-AK)vH$R=*(KscFL#9Dg17AV3bw7EU zvb@c+Xiug^8N#KzY{Ry>4R%fHapbAU2XCz&)<~SAe?=AGhRJF@8()ro-3WeK5OJ|Q|V=wBy zxHioEHGhNjxeCUfZBRm+-Idj~1@6H0Ftt%6zV&TnPkppg_ZBs@6Gc90Hr7jn*btSjTi8%IuHf#>xC=jvc&8({2^vkDGP4EYS$fYCP#UIC!XUk3pxB%#1FD z>t&bF-%%*BX8C@m(x%*t^{nmB1Mre zEk`Ch5{YRFwup))21kIZTT(OHPLFQ1ezqy1%Cjoy)zgKl%thwKSVRZmp-`jElta73 zM=vspB%GCA%^E_YikYzn%lT_sC}lpGZo2lK_^oZTCEEI8Q9Yb+aEe_XmP%O#!NwKf zFV9y~H;5a#>)IFwa?v}Vq|ohr)f_bDBv!5Main9;)N)4dowYxpRu|{b99K3S9jyDXO!)#?ooX+c$;J2eRg>eLGB7WaM9ssea5 z!ZG9}WUouBm86F-`d@jP=-+|l=GGsFWXNVUTsP|X39dl2*Qu8ZW>fU&Iw`3adnmoD z*Mc5Inm5uxVu!OR`#38uNs&M$UeQ+8a`TM)z?t(K@%xU;68r29H5qrcRury)piA@6 zW|W=AetDO2bzQvK(`ufzkS`8LWekCKX+XF`VEkQC#vLXPRf#Y8YisMWoHR zurq3h8Qa#uw5S&MK0IBQ2%Jg)OCQZ^xF$Zma*`BLM`WqLD~j*#B;gBFbKz9B&qYzh z^%ivrsA#)N9NTkYr%_|)+RM}R!K>CgX#V1x>tx-%a7C4`#Ho$cT#Z!N=wD6EZ}{T5 z6%;Xv2b2vP|GLk3JF>vtl%+sGDECl^e!}|tpnH#}s~fwq0@4ZDX=XP7q&)|sf!6t& z@M09X_V)KqS<32P_L&Z;w}lJIBqy7Rh&autJN>#H*!r2Du&mUU} z{JJg_pjI7IQ1%Vf`~3|ENDL6|qJ}xzZsnNBOm86x>>t5a%RFE4IclUD*lxL4dQ_3# zENB8v;KtQd>_Ymqk!cp&D%6SeD@eK0a6l3D+?CEIN)m87ReeHkh8;pri^i_-DJL+-(@0M>A_)>|h^iG(0V1Z4O zS&po!hLxcD5IImN&61w&@MF;yV~MK!=oDMCAff33mHI9)?qS4=9#0RP ziKLD{E}dBNX{Rul;Lx*XIt?ZsR&1iE(Fk5VT2l8re`5&!V%54Fmh)c>P2jM20dXcu*?uV};yV$K**M*^v}s`NH+`r8fQO9C3-3qSLf7#P9%~5R zk@G~yj5pdY7U3C)M#YmW^ zl$EKcziKYh%@ALy(E)Rc>l# z9Rp~lGdZ`>>UJxU=Z&Vwn|Z#>`jg`#2O9WE^k%4f-JLE90xCt`B@)^gc(Hg6#Ywx` zfK2WTy6I`tsjTAs!eL`y9(_q`qt1-&D!Z2}Uv0da1x~qK$+A|8muNr=v|Vk!L&J^M*NnUcM*{})DYYG>=LV))0} z5 zyb5AJ?gqhAD8j*$?I5(7Xi-5x>{i}>C;YAkX_A@I^Q(m~i#=X3lDq;DCjhHcyI`|( zY=xoqT#>p>4gTTSa_DAts+nFqr_^2!QwdKI89uo@4%VVADSb5VqnXyO&VX#*SrDe8 zRqy1zUfbw^4K=<5HPH@)0c&^t>6w(ukFsstqTIFVk*%pYPLc|oT&h1yQ}GtFz&rL= z0YsQ0Z7_2ARiE)0k|wG%0>E4Q&oJ<;6(kgnA7-K~&q$uB`|N$1aH4CL`%L~A(y>n< z7f6{1g$IpP+x=O3k3=DE`A3?>I$TBTs7<=V(9H?azEFWEFsb_)JTK>Hz!byePL_4N zwe_Yodri;0U(pxczs&1>e)FMIO5XU1`<-?0XQ2L+upt@4lDdwIV1Ov0Im zId=9EGo6;$zqo7O!xL|uq~BcMRlIlN$_Ov7C51%?-Q;bK2`}JJX)l z^AD4!tE}hx8M2b8zTCC(+fxsT|U;J+6(M3#(61N+-J-OnrP*ZpB%1dg2 z$+i=wSp55as_fAf)#dlf%F^2JPW*J#Gwh<^8sR8W4PTBf2g9?ix5QrL?(KQ5Q8Z7> zPkZeG2VRRU8t>m1r5P>L-4l^}po&qC%kNa`8Ig?Vdruma<*-lMrJ@m{V1CQ1{uT3< z=p2pPmvtX-DjMq?4ED1&`Cci<*udxE4^7RPemGyY z{`0nVTI=GBdWV^1U5wA3Tl}tVa93|}R) zs@vyq=gPPT_xr?~|1J9%u2TGq6J?b*EJ%y)^;*>yE6=MPr$cqX6eICL!a-jXNlCAv2q3_HTvtLv*J9yM_V zuW51UQq^^H?`+Euo-KNG&ck}Wg`q3_{y5C|-V-kWa{rQ3C;j4UbidF0VKL=Km9o&M z<9U~Trg-aaU~Ijln&@>`NuNVbszS`vt&VMt{)4luqCdAp=rnvaT_Yvf;v=Fs|NOJ* z2izF$ROD|F>AdumKZ;E}cIu|(%}>@SGnm;ir6e~ONtfIz_d2F~C)-=-T>61uSzSk1 z1KA%+2bD~)*s@{bp$A<`52hLjHvVPUP_$Nj8pq85!H3l~AD2d#XV$F!!{%;xza+~n z&f`)+VO5<~nT^!Jwt$e7#0v%t1@8YZ$*nB<((pqWxFIDu zaKaS|yrw5_Wl>c_Y>H=K5QNErX`WE%5X#GVmqBs^|*tW=xOY#$v;$GJVcA77r%7qRITlveS2MV-cAAtylo) zd>AmEuD2biz_(ZSxe>;mK(<6=PPCHnf8O5i!?O<_Y z2HrA0eP0xd*z|`xShSd85~uU-WN~Kp%VC+WkP8&e+{q#!17D>ih8_${il$H933NI7 zyfw@@3=B4v)35AgQBg;X%rY?WBcy?>?j{z%jUMHNy7Jo_={3i?S9xR{~X_MpNiK76#yz u$pJ(m_qpcFrequency.QuadPart); - uint32_t lastFrameCount = m_frameCount; + const uint32_t lastFrameCount = m_frameCount; if (m_isFixedTimeStep) { diff --git a/Samples/Graphics/FastBlockCompress/pch.h b/Samples/Graphics/FastBlockCompress/pch.h index 9a3626f..fe01fea 100644 --- a/Samples/Graphics/FastBlockCompress/pch.h +++ b/Samples/Graphics/FastBlockCompress/pch.h @@ -37,8 +37,8 @@ #include -#if _GXDK_VER < 0x4A610D2B /* GXDK Edition 200600 */ -#error This sample requires the June 2020 GDK or later +#if _GXDK_VER < 0x55F007B0 /* GDK Edition 211000 */ +#error This sample requires the October 2021 GDK or later #endif #ifdef _GAMING_XBOX_SCARLETT @@ -58,18 +58,26 @@ #include #include +#include #include +#include #include +#include +#include +#include #include +#include #include #include +#include +#include +#include #include #include -#include -#include #include + #include #include @@ -93,7 +101,7 @@ namespace DX class com_exception : public std::exception { public: - com_exception(HRESULT hr) : result(hr) {} + com_exception(HRESULT hr) noexcept : result(hr) {} const char* what() const override { diff --git a/Samples/Graphics/GeometricExpansion/DeviceResources.cpp b/Samples/Graphics/GeometricExpansion/DeviceResources.cpp index 7d88350..2b40774 100644 --- a/Samples/Graphics/GeometricExpansion/DeviceResources.cpp +++ b/Samples/Graphics/GeometricExpansion/DeviceResources.cpp @@ -12,7 +12,6 @@ using namespace DX; using Microsoft::WRL::ComPtr; -#ifdef _GAMING_DESKTOP #ifdef __clang__ #pragma clang diagnostic ignored "-Wcovered-switch-default" #pragma clang diagnostic ignored "-Wswitch-enum" @@ -20,6 +19,7 @@ using Microsoft::WRL::ComPtr; #pragma warning(disable : 4061) +#ifdef _GAMING_DESKTOP namespace { inline DXGI_FORMAT NoSRGB(DXGI_FORMAT fmt) noexcept @@ -39,7 +39,8 @@ namespace DeviceResources::DeviceResources( DXGI_FORMAT backBufferFormat, DXGI_FORMAT depthBufferFormat, - UINT backBufferCount) noexcept(false) : + UINT backBufferCount, + unsigned int flags) noexcept(false) : m_backBufferIndex(0), m_fenceValues{}, #ifdef _GAMING_XBOX @@ -54,6 +55,7 @@ DeviceResources::DeviceResources( m_window(nullptr), m_d3dFeatureLevel(D3D_FEATURE_LEVEL_11_0), m_outputSize{ 0, 0, 1, 1 }, + m_options(flags), m_deviceNotify(nullptr) { if (backBufferCount < 2 || backBufferCount > MAX_BACK_BUFFER_COUNT) @@ -72,7 +74,7 @@ DeviceResources::~DeviceResources() // Ensure we present a blank screen before cleaning up resources. if (m_commandQueue) { - (void)m_commandQueue->PresentX(0, nullptr, nullptr); + std::ignore = m_commandQueue->PresentX(0, nullptr, nullptr); } #endif } @@ -98,6 +100,14 @@ void DeviceResources::CreateDeviceResources() params.GraphicsScratchMemorySizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); params.ComputeScratchMemorySizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); +#if defined(_GAMING_XBOX_SCARLETT) && (_GRDK_VER >= 0x585D070E /* GXDK Edition 221000 */) + if (m_options & c_AmplificationShaders) + { + params.AmplificationShaderIndirectArgsBufferSize = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); + params.AmplificationShaderPayloadBufferSize = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); + } +#endif + HRESULT hr = D3D12XboxCreateDevice( nullptr, ¶ms, @@ -150,7 +160,7 @@ void DeviceResources::CreateDeviceResources() 80 /* IDXGISwapChain::GetContainingOutput: The swapchain's adapter does not control the output on which the swapchain's window resides. */, }; DXGI_INFO_QUEUE_FILTER filter = {}; - filter.DenyList.NumIDs = _countof(hide); + filter.DenyList.NumIDs = static_cast(std::size(hide)); filter.DenyList.pIDList = hide; dxgiInfoQueue->AddStorageFilterEntries(DXGI_DEBUG_DXGI, &filter); } @@ -194,9 +204,11 @@ void DeviceResources::CreateDeviceResources() // Workarounds for debug layer issues on hybrid-graphics systems D3D12_MESSAGE_ID_EXECUTECOMMANDLISTS_WRONGSWAPCHAINBUFFERREFERENCE, D3D12_MESSAGE_ID_RESOURCE_BARRIER_MISMATCHING_COMMAND_LIST_TYPE, + // Workaround for debug layer issues with mesh shader validation. + static_cast(1323) /* D3D12_MESSAGE_ID_CREATEMESHSHADER_TOPOLOGY_MISMATCH */, }; D3D12_INFO_QUEUE_FILTER filter = {}; - filter.DenyList.NumIDs = _countof(hide); + filter.DenyList.NumIDs = static_cast(std::size(hide)); filter.DenyList.pIDList = hide; d3dInfoQueue->AddStorageFilterEntries(&filter); } @@ -205,6 +217,9 @@ void DeviceResources::CreateDeviceResources() // Determine maximum supported feature level for this device static const D3D_FEATURE_LEVEL s_featureLevels[] = { +#if defined(NTDDI_WIN10_FE) && (NTDDI_VERSION >= NTDDI_WIN10_FE) + D3D_FEATURE_LEVEL_12_2, +#endif D3D_FEATURE_LEVEL_12_1, D3D_FEATURE_LEVEL_12_0, D3D_FEATURE_LEVEL_11_1, @@ -213,7 +228,7 @@ void DeviceResources::CreateDeviceResources() D3D12_FEATURE_DATA_FEATURE_LEVELS featLevels = { - _countof(s_featureLevels), s_featureLevels, D3D_FEATURE_LEVEL_11_0 + static_cast(std::size(s_featureLevels)), s_featureLevels, D3D_FEATURE_LEVEL_11_0 }; hr = m_d3dDevice->CheckFeatureSupport(D3D12_FEATURE_FEATURE_LEVELS, &featLevels, sizeof(featLevels)); @@ -284,7 +299,7 @@ void DeviceResources::CreateDeviceResources() m_fenceEvent.Attach(CreateEventEx(nullptr, nullptr, 0, EVENT_MODIFY_STATE | SYNCHRONIZE)); if (!m_fenceEvent.IsValid()) { - throw std::exception("CreateEvent"); + throw std::system_error(std::error_code(static_cast(GetLastError()), std::system_category()), "CreateEventEx"); } #ifdef _GAMING_XBOX @@ -297,7 +312,7 @@ void DeviceResources::CreateWindowSizeDependentResources() { if (!m_window) { - throw std::exception("Call SetWindow with a valid window handle"); + throw std::logic_error("Call SetWindow with a valid Win32 window handle"); } // Wait until all previous GPU work is complete. @@ -323,7 +338,7 @@ void DeviceResources::CreateWindowSizeDependentResources() // Obtain the back buffers for this window which will be the final render targets // and create render target views for each of them. - CD3DX12_HEAP_PROPERTIES swapChainHeapProperties(D3D12_HEAP_TYPE_DEFAULT); + const CD3DX12_HEAP_PROPERTIES swapChainHeapProperties(D3D12_HEAP_TYPE_DEFAULT); D3D12_RESOURCE_DESC swapChainBufferDesc = CD3DX12_RESOURCE_DESC::Tex2D( m_backBufferFormat, @@ -355,7 +370,7 @@ void DeviceResources::CreateWindowSizeDependentResources() rtvDesc.Format = m_backBufferFormat; rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; - CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( + const CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( m_rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), static_cast(n), m_rtvDescriptorSize); m_d3dDevice->CreateRenderTargetView(m_renderTargets[n].Get(), &rtvDesc, rtvDescriptor); @@ -366,7 +381,7 @@ void DeviceResources::CreateWindowSizeDependentResources() #else // _GAMING_DESKTOP - DXGI_FORMAT backBufferFormat = NoSRGB(m_backBufferFormat); + const DXGI_FORMAT backBufferFormat = NoSRGB(m_backBufferFormat); // If the swap chain already exists, resize it, otherwise create one. if (m_swapChain) @@ -449,7 +464,7 @@ void DeviceResources::CreateWindowSizeDependentResources() rtvDesc.Format = m_backBufferFormat; rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; - CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( + const CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( m_rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), static_cast(n), m_rtvDescriptorSize); m_d3dDevice->CreateRenderTargetView(m_renderTargets[n].Get(), &rtvDesc, rtvDescriptor); @@ -464,7 +479,7 @@ void DeviceResources::CreateWindowSizeDependentResources() { // Allocate a 2-D surface as the depth/stencil buffer and create a depth/stencil view // on this surface. - CD3DX12_HEAP_PROPERTIES depthHeapProperties(D3D12_HEAP_TYPE_DEFAULT); + const CD3DX12_HEAP_PROPERTIES depthHeapProperties(D3D12_HEAP_TYPE_DEFAULT); D3D12_RESOURCE_DESC depthStencilDesc = CD3DX12_RESOURCE_DESC::Tex2D( m_depthBufferFormat, @@ -477,7 +492,7 @@ void DeviceResources::CreateWindowSizeDependentResources() D3D12_CLEAR_VALUE depthOptimizedClearValue = {}; depthOptimizedClearValue.Format = m_depthBufferFormat; - depthOptimizedClearValue.DepthStencil.Depth = 1.0f; + depthOptimizedClearValue.DepthStencil.Depth = (m_options & c_ReverseDepth) ? 0.0f : 1.0f; depthOptimizedClearValue.DepthStencil.Stencil = 0; ThrowIfFailed(m_d3dDevice->CreateCommittedResource( @@ -588,12 +603,6 @@ void DeviceResources::HandleDeviceLost() // Prepare the command list and render target for rendering. void DeviceResources::Prepare(D3D12_RESOURCE_STATES beforeState, D3D12_RESOURCE_STATES afterState) { -#ifdef _GAMING_XBOX - // Wait until frame start is signaled - m_framePipelineToken = D3D12XBOX_FRAME_PIPELINE_TOKEN_NULL; - ThrowIfFailed(m_d3dDevice->WaitFrameEventX(D3D12XBOX_FRAME_EVENT_ORIGIN, INFINITE, nullptr, D3D12XBOX_WAIT_FRAME_EVENT_FLAG_NONE, &m_framePipelineToken)); -#endif - // Reset command list and allocator. ThrowIfFailed(m_commandAllocators[m_backBufferIndex]->Reset()); ThrowIfFailed(m_commandList->Reset(m_commandAllocators[m_backBufferIndex].Get(), nullptr)); @@ -601,7 +610,8 @@ void DeviceResources::Prepare(D3D12_RESOURCE_STATES beforeState, D3D12_RESOURCE_ if (beforeState != afterState) { // Transition the render target into the correct state to allow for drawing into it. - D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), + const D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition( + m_renderTargets[m_backBufferIndex].Get(), beforeState, afterState); m_commandList->ResourceBarrier(1, &barrier); } @@ -613,7 +623,9 @@ void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState) if (beforeState != D3D12_RESOURCE_STATE_PRESENT) { // Transition the render target to the state that allows it to be presented to the display. - D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), beforeState, D3D12_RESOURCE_STATE_PRESENT); + const D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition( + m_renderTargets[m_backBufferIndex].Get(), + beforeState, D3D12_RESOURCE_STATE_PRESENT); m_commandList->ResourceBarrier(1, &barrier); } @@ -633,9 +645,12 @@ void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState) m_commandQueue->PresentX(1, &planeParameters, nullptr) ); - // Xbox One apps do not need to handle DXGI_ERROR_DEVICE_REMOVED or DXGI_ERROR_DEVICE_RESET. + // Xbox apps do not need to handle DXGI_ERROR_DEVICE_REMOVED or DXGI_ERROR_DEVICE_RESET. -#else + // Update the back buffer index. + m_backBufferIndex = (m_backBufferIndex + 1) % m_backBufferCount; + +#else // _GAMING_DESKTOP // The first argument instructs DXGI to block until VSync, putting the application // to sleep until the next VSync. This ensures we don't waste any cycles rendering @@ -658,9 +673,8 @@ void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState) ThrowIfFailed(hr); } -#endif - MoveToNextFrame(); +#endif } // Handle GPU suspend/resume @@ -686,13 +700,13 @@ void DeviceResources::WaitForGpu() noexcept if (m_commandQueue && m_fence && m_fenceEvent.IsValid()) { // Schedule a Signal command in the GPU queue. - UINT64 fenceValue = m_fenceValues[m_backBufferIndex]; + const UINT64 fenceValue = m_fenceValues[m_backBufferIndex]; if (SUCCEEDED(m_commandQueue->Signal(m_fence.Get(), fenceValue))) { // Wait until the Signal has been processed. if (SUCCEEDED(m_fence->SetEventOnCompletion(fenceValue, m_fenceEvent.Get()))) { - WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); + std::ignore = WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); // Increment the fence value for the current frame. m_fenceValues[m_backBufferIndex]++; @@ -701,32 +715,20 @@ void DeviceResources::WaitForGpu() noexcept } } -// Prepare to render the next frame. -void DeviceResources::MoveToNextFrame() -{ - // Schedule a Signal command in the queue. - const UINT64 currentFenceValue = m_fenceValues[m_backBufferIndex]; - ThrowIfFailed(m_commandQueue->Signal(m_fence.Get(), currentFenceValue)); - - // Update the back buffer index. #ifdef _GAMING_XBOX - m_backBufferIndex = (m_backBufferIndex + 1) % m_backBufferCount; -#else - m_backBufferIndex = m_swapChain->GetCurrentBackBufferIndex(); -#endif - - // If the next frame is not ready to be rendered yet, wait until it is ready. - if (m_fence->GetCompletedValue() < m_fenceValues[m_backBufferIndex]) - { - ThrowIfFailed(m_fence->SetEventOnCompletion(m_fenceValues[m_backBufferIndex], m_fenceEvent.Get())); - WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); - } - - // Set the fence value for the next frame. - m_fenceValues[m_backBufferIndex] = currentFenceValue + 1; +// For PresentX rendering, we should wait for the origin event just before processing input. +void DeviceResources::WaitForOrigin() +{ + // Wait until frame start is signaled + m_framePipelineToken = D3D12XBOX_FRAME_PIPELINE_TOKEN_NULL; + ThrowIfFailed(m_d3dDevice->WaitFrameEventX( + D3D12XBOX_FRAME_EVENT_ORIGIN, + INFINITE, + nullptr, + D3D12XBOX_WAIT_FRAME_EVENT_FLAG_NONE, + &m_framePipelineToken)); } -#ifdef _GAMING_XBOX // Set frame interval and register for frame events void DeviceResources::RegisterFrameEvents() { @@ -755,7 +757,30 @@ void DeviceResources::RegisterFrameEvents() nullptr, D3D12XBOX_SCHEDULE_FRAME_EVENT_FLAG_NONE)); } -#else + +#else // _GAMING_DESKTOP + +// Prepare to render the next frame. +void DeviceResources::MoveToNextFrame() +{ + // Schedule a Signal command in the queue. + const UINT64 currentFenceValue = m_fenceValues[m_backBufferIndex]; + ThrowIfFailed(m_commandQueue->Signal(m_fence.Get(), currentFenceValue)); + + // Update the back buffer index. + m_backBufferIndex = m_swapChain->GetCurrentBackBufferIndex(); + + // If the next frame is not ready to be rendered yet, wait until it is ready. + if (m_fence->GetCompletedValue() < m_fenceValues[m_backBufferIndex]) + { + ThrowIfFailed(m_fence->SetEventOnCompletion(m_fenceValues[m_backBufferIndex], m_fenceEvent.Get())); + std::ignore = WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); + } + + // Set the fence value for the next frame. + m_fenceValues[m_backBufferIndex] = currentFenceValue + 1; +} + // This method acquires the first available hardware adapter that supports Direct3D 12. // If no such adapter can be found, try WARP. Otherwise throw an exception. void DeviceResources::GetAdapter(IDXGIAdapter1** ppAdapter) @@ -797,7 +822,7 @@ void DeviceResources::GetAdapter(IDXGIAdapter1** ppAdapter) // Try WARP12 instead if (FAILED(m_dxgiFactory->EnumWarpAdapter(IID_PPV_ARGS(adapter.ReleaseAndGetAddressOf())))) { - throw std::exception("WARP12 not available. Enable the 'Graphics Tools' optional feature"); + throw std::runtime_error("WARP12 not available. Enable the 'Graphics Tools' optional feature"); } OutputDebugStringA("Direct3D Adapter - WARP12\n"); @@ -806,7 +831,7 @@ void DeviceResources::GetAdapter(IDXGIAdapter1** ppAdapter) if (!adapter) { - throw std::exception("No Direct3D 12 device found"); + throw std::runtime_error("No Direct3D 12 device found"); } *ppAdapter = adapter.Detach(); diff --git a/Samples/Graphics/GeometricExpansion/DeviceResources.h b/Samples/Graphics/GeometricExpansion/DeviceResources.h index a920f13..0e0aeb0 100644 --- a/Samples/Graphics/GeometricExpansion/DeviceResources.h +++ b/Samples/Graphics/GeometricExpansion/DeviceResources.h @@ -22,9 +22,13 @@ namespace DX class DeviceResources { public: + static constexpr unsigned int c_ReverseDepth = 0x1; + static constexpr unsigned int c_AmplificationShaders = 0x2; + DeviceResources(DXGI_FORMAT backBufferFormat = DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT depthBufferFormat = DXGI_FORMAT_D32_FLOAT, - UINT backBufferCount = 2) noexcept(false); + UINT backBufferCount = 2, + unsigned int flags = 0) noexcept(false); ~DeviceResources(); DeviceResources(DeviceResources&&) = default; @@ -45,6 +49,9 @@ namespace DX void Suspend(); void Resume(); void WaitForGpu() noexcept; +#ifdef _GAMING_XBOX + void WaitForOrigin(); +#endif // Device Accessors. RECT GetOutputSize() const noexcept { return m_outputSize; } @@ -68,6 +75,7 @@ namespace DX D3D12_RECT GetScissorRect() const noexcept { return m_scissorRect; } UINT GetCurrentFrameIndex() const noexcept { return m_backBufferIndex; } UINT GetBackBufferCount() const noexcept { return m_backBufferCount; } + unsigned int GetDeviceOptions() const noexcept { return m_options; } CD3DX12_CPU_DESCRIPTOR_HANDLE GetRenderTargetView() const noexcept { @@ -81,10 +89,10 @@ namespace DX } private: - void MoveToNextFrame(); #ifdef _GAMING_XBOX void RegisterFrameEvents(); #else + void MoveToNextFrame(); void GetAdapter(IDXGIAdapter1** ppAdapter); #endif @@ -131,6 +139,9 @@ namespace DX D3D_FEATURE_LEVEL m_d3dFeatureLevel; RECT m_outputSize; + // DeviceResources options (see flags above) + unsigned int m_options; + // The IDeviceNotify can be held directly as it owns the DeviceResources. IDeviceNotify* m_deviceNotify; }; diff --git a/Samples/Graphics/GeometricExpansion/GeometricExpansion.cpp b/Samples/Graphics/GeometricExpansion/GeometricExpansion.cpp index a9e346a..8cbe26a 100644 --- a/Samples/Graphics/GeometricExpansion/GeometricExpansion.cpp +++ b/Samples/Graphics/GeometricExpansion/GeometricExpansion.cpp @@ -15,7 +15,7 @@ #pragma warning( disable : 4324 4365 ) -extern void ExitSample(); +extern void ExitSample() noexcept; using namespace ATG; using namespace DirectX; @@ -47,20 +47,10 @@ namespace constexpr float c_springCoeff = 4.0f; constexpr float c_dragFactor = 1.0f; constexpr float c_initialSpeed = 3.0f; - -#ifdef _GAMING_DESKTOP - static const GUID D3D12ExperimentalShaderModelsID = { /* 76f5573e-f13a-40f5-b297-81ce9e18933f */ - 0x76f5573e, - 0xf13a, - 0x40f5, - { 0xb2, 0x97, 0x81, 0xce, 0x9e, 0x18, 0x93, 0x3f } - }; -#endif } Sample::Sample() noexcept(false) - : m_deviceResources(std::make_unique()) - , m_displayWidth(0) + : m_displayWidth(0) , m_displayHeight(0) , m_frame(0) , m_spawnRate(c_spawnRate) @@ -76,6 +66,7 @@ Sample::Sample() noexcept(false) ValueRange(c_minLifetime, c_maxLifetime), ValueRange(c_minSize, c_maxSize)) { + m_deviceResources = std::make_unique(); m_deviceResources->RegisterDeviceNotify(this); } @@ -90,12 +81,10 @@ Sample::~Sample() // Initialize the Direct3D resources required to run. void Sample::Initialize(HWND window, int width, int height) { -#ifdef _GAMING_DESKTOP - D3D12EnableExperimentalFeatures(1, &D3D12ExperimentalShaderModelsID, nullptr, nullptr); -#endif - m_gamePad = std::make_unique(); + m_keyboard = std::make_unique(); + m_mouse = std::make_unique(); m_mouse->SetWindow(window); @@ -114,6 +103,10 @@ void Sample::Tick() { PIXBeginEvent(PIX_COLOR_DEFAULT, L"Frame %llu", m_frame); +#ifdef _GAMING_XBOX + m_deviceResources->WaitForOrigin(); +#endif + m_timer.Tick([&]() { Update(m_timer); @@ -283,8 +276,8 @@ void Sample::Render() auto commandList = m_deviceResources->GetCommandList(); PIXBeginEvent(commandList, PIX_COLOR_DEFAULT, L"Render"); - ID3D12DescriptorHeap* heaps[] = { m_srvPile->Heap() }; - commandList->SetDescriptorHeaps(1, heaps); + auto heaps = m_srvPile->Heap(); + commandList->SetDescriptorHeaps(1, &heaps); m_particleSystem.Draw(commandList, m_graphicsMemory.get()); @@ -306,16 +299,16 @@ void Sample::Clear() PIXBeginEvent(commandList, PIX_COLOR_DEFAULT, L"Clear"); // Clear the views. - auto rtvDescriptor = m_deviceResources->GetRenderTargetView(); - auto dsvDescriptor = m_deviceResources->GetDepthStencilView(); + auto const rtvDescriptor = m_deviceResources->GetRenderTargetView(); + auto const dsvDescriptor = m_deviceResources->GetDepthStencilView(); commandList->OMSetRenderTargets(1, &rtvDescriptor, FALSE, &dsvDescriptor); commandList->ClearRenderTargetView(rtvDescriptor, ATG::Colors::Background, 0, nullptr); commandList->ClearDepthStencilView(dsvDescriptor, D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr); // Set the viewport and scissor rect. - auto viewport = m_deviceResources->GetScreenViewport(); - auto scissorRect = m_deviceResources->GetScissorRect(); + auto const viewport = m_deviceResources->GetScreenViewport(); + auto const scissorRect = m_deviceResources->GetScissorRect(); commandList->RSSetViewports(1, &viewport); commandList->RSSetScissorRects(1, &scissorRect); @@ -326,7 +319,7 @@ void Sample::DrawHUD(ID3D12GraphicsCommandList* commandList) { m_hudBatch->Begin(commandList); - auto safe = SimpleMath::Viewport::ComputeTitleSafeArea(m_displayWidth, m_displayHeight); + auto const safe = SimpleMath::Viewport::ComputeTitleSafeArea(m_displayWidth, m_displayHeight); wchar_t textBuffer[128] = {}; XMFLOAT2 textPos = XMFLOAT2(float(safe.left), float(safe.top)); @@ -398,14 +391,6 @@ void Sample::DrawHUD(ID3D12GraphicsCommandList* commandList) #pragma region Message Handlers // Message handlers -void Sample::OnActivated() -{ -} - -void Sample::OnDeactivated() -{ -} - void Sample::OnSuspending() { m_deviceResources->Suspend(); @@ -421,7 +406,7 @@ void Sample::OnResuming() void Sample::OnWindowMoved() { - auto r = m_deviceResources->GetOutputSize(); + auto const r = m_deviceResources->GetOutputSize(); m_deviceResources->WindowSizeChanged(r.right, r.bottom); } @@ -434,7 +419,7 @@ void Sample::OnWindowSizeChanged(int width, int height) } // Properties -void Sample::GetDefaultSize(int& width, int& height) const +void Sample::GetDefaultSize(int& width, int& height) const noexcept { width = 1280; height = 720; @@ -469,8 +454,6 @@ void Sample::CreateDeviceDependentResources() // Create heap m_srvPile = std::make_unique(device, - D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, - D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, 128, DescriptorHeapIndex::SRV_Count); @@ -480,7 +463,7 @@ void Sample::CreateDeviceDependentResources() resourceUpload.Begin(); // HUD - auto backBufferRts = RenderTargetState(m_deviceResources->GetBackBufferFormat(), m_deviceResources->GetDepthBufferFormat()); + const RenderTargetState backBufferRts(m_deviceResources->GetBackBufferFormat(), m_deviceResources->GetDepthBufferFormat()); auto spritePSD = SpriteBatchPipelineStateDescription(backBufferRts, &CommonStates::AlphaBlend); m_hudBatch = std::make_unique(device, resourceUpload, spritePSD); @@ -503,7 +486,7 @@ void Sample::CreateDeviceDependentResources() // Allocate all memory resources that change on a window SizeChanged event. void Sample::CreateWindowSizeDependentResources() { - RECT size = m_deviceResources->GetOutputSize(); + auto const size = m_deviceResources->GetOutputSize(); m_displayWidth = size.right - size.left; m_displayHeight = size.bottom - size.top; @@ -524,6 +507,11 @@ void Sample::CreateWindowSizeDependentResources() void Sample::OnDeviceLost() { + m_particleSystem.ReleaseDevice(); + m_hudBatch.reset(); + m_smallFont.reset(); + m_ctrlFont.reset(); + m_srvPile.reset(); m_graphicsMemory.reset(); } diff --git a/Samples/Graphics/GeometricExpansion/GeometricExpansion.h b/Samples/Graphics/GeometricExpansion/GeometricExpansion.h index 9d212bd..e49ec0f 100644 --- a/Samples/Graphics/GeometricExpansion/GeometricExpansion.h +++ b/Samples/Graphics/GeometricExpansion/GeometricExpansion.h @@ -20,6 +20,12 @@ public: Sample() noexcept(false); ~Sample(); + Sample(Sample&&) = default; + Sample& operator= (Sample&&) = default; + + Sample(Sample const&) = delete; + Sample& operator= (Sample const&) = delete; + // Initialization and management void Initialize(HWND window, int width, int height); @@ -31,15 +37,15 @@ public: void OnDeviceRestored() override; // Messages - void OnActivated(); - void OnDeactivated(); + void OnActivated() {} + void OnDeactivated() {} void OnSuspending(); void OnResuming(); void OnWindowMoved(); void OnWindowSizeChanged(int width, int height); // Properties - void GetDefaultSize(int& width, int& height) const; + void GetDefaultSize(int& width, int& height) const noexcept; private: void Update(DX::StepTimer const& timer); diff --git a/Samples/Graphics/GeometricExpansion/Main.cpp b/Samples/Graphics/GeometricExpansion/Main.cpp index 4f935eb..a3e3743 100644 --- a/Samples/Graphics/GeometricExpansion/Main.cpp +++ b/Samples/Graphics/GeometricExpansion/Main.cpp @@ -20,6 +20,13 @@ using namespace DirectX; +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wcovered-switch-default" +#pragma clang diagnostic ignored "-Wswitch-enum" +#endif + +#pragma warning(disable : 4061) + namespace { std::unique_ptr g_sample; @@ -27,11 +34,12 @@ namespace HANDLE g_plmSuspendComplete = nullptr; HANDLE g_plmSignalResume = nullptr; #endif -}; +} LPCWSTR g_szAppName = L"GeometricExpansion"; LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); +void ExitSample() noexcept; // Entry point int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) @@ -55,7 +63,7 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp if (hr == E_GAMERUNTIME_DLL_NOT_FOUND || hr == E_GAMERUNTIME_VERSION_MISMATCH) { #ifdef _GAMING_DESKTOP - (void)MessageBoxW(nullptr, L"Game Runtime is not installed on this system or needs updating.", g_szAppName, MB_ICONERROR | MB_OK); + std::ignore = MessageBoxW(nullptr, L"Game Runtime is not installed on this system or needs updating.", g_szAppName, MB_ICONERROR | MB_OK); #endif } return 1; @@ -80,7 +88,7 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp wcex.lpfnWndProc = WndProc; wcex.hInstance = hInstance; wcex.hCursor = LoadCursorW(nullptr, IDC_ARROW); - wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); + wcex.hbrBackground = reinterpret_cast(COLOR_WINDOW + 1); wcex.lpszClassName = L"GeometricExpansionWindowClass"; if (!RegisterClassExW(&wcex)) return 1; @@ -134,7 +142,7 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp PostMessage(reinterpret_cast(context), WM_USER, 0, 0); // To defer suspend, you must wait to exit this callback - (void)WaitForSingleObject(g_plmSuspendComplete, INFINITE); + std::ignore = WaitForSingleObject(g_plmSuspendComplete, INFINITE); } else { @@ -164,14 +172,14 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp #ifdef _GAMING_XBOX UnregisterAppStateChangeNotification(hPLM); - + CloseHandle(g_plmSuspendComplete); CloseHandle(g_plmSignalResume); #endif XGameRuntimeUninitialize(); - return (int) msg.wParam; + return static_cast(msg.wParam); } // Windows procedure @@ -232,7 +240,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) // Complete deferral SetEvent(g_plmSuspendComplete); - (void)WaitForSingleObject(g_plmSignalResume, INFINITE); + std::ignore = WaitForSingleObject(g_plmSignalResume, INFINITE); sample->OnResuming(); } @@ -246,7 +254,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) else { PAINTSTRUCT ps; - (void)BeginPaint(hWnd, &ps); + std::ignore = BeginPaint(hWnd, &ps); EndPaint(hWnd, &ps); } break; @@ -298,12 +306,13 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) break; case WM_GETMINMAXINFO: - { - auto info = reinterpret_cast(lParam); - info->ptMinTrackSize.x = 320; - info->ptMinTrackSize.y = 200; - } - break; + if (lParam) + { + auto info = reinterpret_cast(lParam); + info->ptMinTrackSize.x = 320; + info->ptMinTrackSize.y = 200; + } + break; case WM_POWERBROADCAST: switch (wParam) @@ -360,7 +369,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) } else { - SetWindowLongPtr(hWnd, GWL_STYLE, 0); + SetWindowLongPtr(hWnd, GWL_STYLE, WS_POPUP); SetWindowLongPtr(hWnd, GWL_EXSTYLE, WS_EX_TOPMOST); SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); @@ -388,7 +397,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) } // Exit helper -void ExitSample() +void ExitSample() noexcept { PostQuitMessage(0); } diff --git a/Samples/Graphics/GeometricExpansion/Particles.cpp b/Samples/Graphics/GeometricExpansion/Particles.cpp index 4701627..dd4ccf1 100644 --- a/Samples/Graphics/GeometricExpansion/Particles.cpp +++ b/Samples/Graphics/GeometricExpansion/Particles.cpp @@ -10,6 +10,7 @@ #pragma warning( disable : 4324 4365 ) +using namespace ATG; using namespace DirectX; using namespace DirectX::SimpleMath; @@ -45,220 +46,228 @@ namespace } } -namespace ATG +ParticleSystem::ParticleSystem( + FXMVECTOR position, + float spawnRate, + float springCoeff, + float dragFactor, + float speed, + ValueRange lifetime, + ValueRange size) + : m_position{} + , m_spawnFrequency(1.0f / spawnRate) + , m_springCoeff(springCoeff) + , m_dragFactor(dragFactor) + , m_simulationTime(0.0f) + , m_speed(speed) + , m_angle(XM_2PI) + , m_lifetime(lifetime) + , m_size(size) + , m_particles{} { - ParticleSystem::ParticleSystem( - FXMVECTOR position, - float spawnRate, - float springCoeff, - float dragFactor, - float speed, - ValueRange lifetime, - ValueRange size) - : m_position{} - , m_spawnFrequency(1.0f / spawnRate) - , m_springCoeff(springCoeff) - , m_dragFactor(dragFactor) - , m_simulationTime(0.0f) - , m_speed(speed) - , m_angle(XM_2PI) - , m_lifetime(lifetime) - , m_size(size) - , m_particles{} - { - assert(spawnRate > 0.0f); + assert(spawnRate > 0.0f); - XMStoreFloat3(&m_position, position); - m_particles.reserve(c_particleBufferInitSize); // Prime particle list with arbitrarily large allocation - } - - void ParticleSystem::CreateResources(DX::DeviceResources& deviceResources) - { - m_device = deviceResources.GetD3DDevice(); - - auto heapProps = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT); - auto cbDesc = CD3DX12_RESOURCE_DESC::Buffer(GetAlignedSize(sizeof(Constants))); - - DX::ThrowIfFailed(m_device->CreateCommittedResource(&heapProps, D3D12_HEAP_FLAG_NONE, &cbDesc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_GRAPHICS_PPV_ARGS(m_constantBuffer.ReleaseAndGetAddressOf()))); - - ReloadPipelineState(deviceResources); - } - - void ParticleSystem::Update(float dt, const Matrix& view, const Matrix& proj) - { - m_simulationTime += dt; - - // Kill dead particles - for (size_t i = 0; i < m_particles.size(); ++i) - { - auto& p = m_particles[i]; - - if (m_simulationTime - p.InitTime > p.Lifetime) - { - if (i != m_particles.size() - 1) - { - // Just overwrite it with the last particle and continue. - p = m_particles.back(); - --i; - } - - m_particles.pop_back(); - } - } - - // Spawn new particles - float lastSpawnTime = floorf((m_simulationTime - dt) / m_spawnFrequency) * m_spawnFrequency; - - size_t newCount = static_cast(floorf((m_simulationTime - lastSpawnTime) / m_spawnFrequency)); - size_t oldCount = m_particles.size(); - - m_particles.resize(oldCount + newCount); - - // Initialize new particles - for (size_t i = oldCount; i < m_particles.size(); ++i) - { - float angle = m_angle.Gen(); // Set off in random direction in xz plane - Vector3 direction(cosf(angle), 0, sinf(angle)); - - Vector3 velocity; - velocity = direction * m_speed; // Random speed in xz - velocity.y = m_speed; // Initial kick in y to break equilibrium - - auto& p = m_particles[i]; - p.Position = m_position; - p.Velocity = velocity; - p.InitTime = m_simulationTime; - p.Lifetime = m_lifetime.Gen(); - p.Size = m_size.Gen(); - } - - // Perform integration at fixed time intervals for stability - for (auto& p : m_particles) - { - // Simulate particles over dt using a fixed timestep - for (float accum = dt; accum >= 0; accum -= c_fixedStep) - { - // Only compute drag for horizontal velocity - Vector3 velXZ = p.Velocity; - velXZ.y = 0; - - float length = velXZ.Length(); - Vector3 direction = velXZ / length; - - // Compute forces - Vector3 force; - force.y = -m_springCoeff * (p.Position.y - m_position.y); // Spring force proportional to displacement in y from rest position - force.x = -m_dragFactor * c_dragCoeff * length * direction.x; // Drag force proportional to velocity in xz - force.z = -m_dragFactor * c_dragCoeff * length * direction.z; - - Vector3 acceleration = force * c_particleMassInv; - - // Perform integration - float stepSize = std::min(accum, c_fixedStep); - - p.Velocity = p.Velocity + acceleration * stepSize; - p.Position = p.Position + p.Velocity * stepSize; - } - } - - // Update constants - m_constants.ViewProj = (view * proj).Transpose(); - m_constants.ViewPosition = Vector3::Transform(Vector3::Zero, view.Invert()); - m_constants.ParticleCount = static_cast(m_particles.size()); - m_constants.CameraUp = Vector3::TransformNormal(Vector3::Up, view.Invert()); - m_constants.SimulationTime = m_simulationTime; - XMStoreFloat3(&m_constants.Color, DirectX::Colors::Red); - - // Update GPU Buffers - TryResizeBuffers(); - } - - void ParticleSystem::Draw(ID3D12GraphicsCommandList6* commandList, GraphicsMemory* graphicsMemory) - { - // Copy cpu memory to upload buffer - auto upload = graphicsMemory->Allocate(GetAlignedSize(sizeof(Constants)) + m_particles.size() * sizeof(Particle)); - - std::memcpy(static_cast(upload.Memory()), &m_constants, sizeof(Constants)); - std::memcpy(static_cast(upload.Memory()) + GetAlignedSize(sizeof(Constants)), m_particles.data(), m_particles.size() * sizeof(Particle)); - - // Copy from upload buffer to resource - DirectX::TransitionResource(commandList, m_particleBuffer.Get(), D3D12_RESOURCE_STATE_GENERIC_READ, D3D12_RESOURCE_STATE_COPY_DEST); - DirectX::TransitionResource(commandList, m_constantBuffer.Get(), D3D12_RESOURCE_STATE_GENERIC_READ, D3D12_RESOURCE_STATE_COPY_DEST); - - commandList->CopyBufferRegion(m_constantBuffer.Get(), 0, upload.Resource(), 0, sizeof(Constants)); - commandList->CopyBufferRegion(m_particleBuffer.Get(), 0, upload.Resource(), GetAlignedSize(sizeof(Constants)), m_particles.size() * sizeof(Particle)); - - DirectX::TransitionResource(commandList, m_particleBuffer.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_GENERIC_READ); - DirectX::TransitionResource(commandList, m_constantBuffer.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_GENERIC_READ); - - // Draw - commandList->SetGraphicsRootSignature(m_rootSignature.Get()); - commandList->SetPipelineState(m_particlePSO.Get()); - - commandList->SetGraphicsRootConstantBufferView(0, m_constantBuffer->GetGPUVirtualAddress()); - commandList->SetGraphicsRootShaderResourceView(1, m_particleBuffer->GetGPUVirtualAddress()); - - auto count = RoundUpDiv((uint32_t)m_particles.size(), GROUP_SIZE / 4u); - commandList->DispatchMesh(count, 1, 1); - } - - void ParticleSystem::ReloadPipelineState(DX::DeviceResources& deviceResources) - { - auto device = deviceResources.GetD3DDevice(); - - auto meshShader = DX::ReadData(s_meshShaderFilename); - auto pixelShader = DX::ReadData(s_pixelShaderFilename); - - DX::ThrowIfFailed(device->CreateRootSignature(0, meshShader.data(), meshShader.size(), IID_GRAPHICS_PPV_ARGS(m_rootSignature.ReleaseAndGetAddressOf()))); - - D3DX12_MESH_SHADER_PIPELINE_STATE_DESC psoDesc = {}; - psoDesc.pRootSignature = m_rootSignature.Get(); - psoDesc.MS = { meshShader.data(), meshShader.size() }; - psoDesc.PS = { pixelShader.data(), pixelShader.size() }; - psoDesc.NumRenderTargets = 1; - psoDesc.RTVFormats[0] = deviceResources.GetBackBufferFormat(); - psoDesc.DSVFormat = deviceResources.GetDepthBufferFormat(); - psoDesc.RasterizerState = CommonStates::CullNone; - psoDesc.BlendState = CommonStates::AlphaBlend; - psoDesc.DepthStencilState = CommonStates::DepthRead; - psoDesc.SampleMask = UINT_MAX; - psoDesc.SampleDesc = DefaultSampleDesc(); - - auto meshStreamDesc = CD3DX12_PIPELINE_MESH_STATE_STREAM(psoDesc); - - D3D12_PIPELINE_STATE_STREAM_DESC streamDesc = {}; - streamDesc.SizeInBytes = sizeof(meshStreamDesc); - streamDesc.pPipelineStateSubobjectStream = &meshStreamDesc; - - DX::ThrowIfFailed(device->CreatePipelineState(&streamDesc, IID_GRAPHICS_PPV_ARGS(m_particlePSO.ReleaseAndGetAddressOf()))); - } - - void ParticleSystem::TryResizeBuffers() - { - assert(m_device != nullptr); - - const size_t minSize = sizeof(Particle) * c_particleBufferInitSize; - - size_t newSize = m_particles.size() * sizeof(Particle) * 2; // Over-allocate by two to reduce number of allocations - newSize = std::max(newSize, minSize); - - m_particleBufferTemp = nullptr; // Release the old particle buffer, which we cached for a frame to avoid read after release - - // Don't resize if it's beneath our high watermark - if (m_particleBuffer != nullptr) - { - if (newSize < m_particleBuffer->GetDesc().Width) - { - return; - } - } - - m_particleBufferTemp = m_particleBuffer; // Temporarily cache the old particle buffer to avoid releasing before its last usage is complete. - - // Reallocate particle buffer - auto heapProps = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT); - auto particleDesc = CD3DX12_RESOURCE_DESC::Buffer(GetAlignedSize(newSize)); - - DX::ThrowIfFailed(m_device->CreateCommittedResource(&heapProps, D3D12_HEAP_FLAG_NONE, &particleDesc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_GRAPHICS_PPV_ARGS(m_particleBuffer.ReleaseAndGetAddressOf()))); - } + XMStoreFloat3(&m_position, position); + m_particles.reserve(c_particleBufferInitSize); // Prime particle list with arbitrarily large allocation +} + +void ParticleSystem::CreateResources(DX::DeviceResources& deviceResources) +{ + m_device = deviceResources.GetD3DDevice(); + + const CD3DX12_HEAP_PROPERTIES heapProps(D3D12_HEAP_TYPE_DEFAULT); + auto cbDesc = CD3DX12_RESOURCE_DESC::Buffer(GetAlignedSize(sizeof(Constants))); + + DX::ThrowIfFailed(m_device->CreateCommittedResource(&heapProps, D3D12_HEAP_FLAG_NONE, &cbDesc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_GRAPHICS_PPV_ARGS(m_constantBuffer.ReleaseAndGetAddressOf()))); + + ReloadPipelineState(deviceResources); +} + +void ParticleSystem::ReleaseDevice() +{ + m_rootSignature.Reset(); + m_particlePSO.Reset(); + m_constantBuffer.Reset(); + m_particleBuffer.Reset(); + m_particleBufferTemp.Reset(); + m_device.Reset(); +} + + +void ParticleSystem::Update(float dt, const Matrix& view, const Matrix& proj) +{ + m_simulationTime += dt; + + // Kill dead particles + for (size_t i = 0; i < m_particles.size(); ++i) + { + auto& p = m_particles[i]; + + if (m_simulationTime - p.InitTime > p.Lifetime) + { + if (i != m_particles.size() - 1) + { + // Just overwrite it with the last particle and continue. + p = m_particles.back(); + --i; + } + + m_particles.pop_back(); + } + } + + // Spawn new particles + float lastSpawnTime = floorf((m_simulationTime - dt) / m_spawnFrequency) * m_spawnFrequency; + + size_t newCount = static_cast(floorf((m_simulationTime - lastSpawnTime) / m_spawnFrequency)); + size_t oldCount = m_particles.size(); + + m_particles.resize(oldCount + newCount); + + // Initialize new particles + for (size_t i = oldCount; i < m_particles.size(); ++i) + { + float angle = m_angle.Gen(); // Set off in random direction in xz plane + Vector3 direction(cosf(angle), 0, sinf(angle)); + + Vector3 velocity; + velocity = direction * m_speed; // Random speed in xz + velocity.y = m_speed; // Initial kick in y to break equilibrium + + auto& p = m_particles[i]; + p.Position = m_position; + p.Velocity = velocity; + p.InitTime = m_simulationTime; + p.Lifetime = m_lifetime.Gen(); + p.Size = m_size.Gen(); + } + + // Perform integration at fixed time intervals for stability + for (auto& p : m_particles) + { + // Simulate particles over dt using a fixed timestep + for (float accum = dt; accum >= 0; accum -= c_fixedStep) + { + // Only compute drag for horizontal velocity + Vector3 velXZ = p.Velocity; + velXZ.y = 0; + + float length = velXZ.Length(); + Vector3 direction = velXZ / length; + + // Compute forces + Vector3 force; + force.y = -m_springCoeff * (p.Position.y - m_position.y); // Spring force proportional to displacement in y from rest position + force.x = -m_dragFactor * c_dragCoeff * length * direction.x; // Drag force proportional to velocity in xz + force.z = -m_dragFactor * c_dragCoeff * length * direction.z; + + Vector3 acceleration = force * c_particleMassInv; + + // Perform integration + float stepSize = std::min(accum, c_fixedStep); + + p.Velocity = p.Velocity + acceleration * stepSize; + p.Position = p.Position + p.Velocity * stepSize; + } + } + + // Update constants + m_constants.ViewProj = (view * proj).Transpose(); + m_constants.ViewPosition = Vector3::Transform(Vector3::Zero, view.Invert()); + m_constants.ParticleCount = static_cast(m_particles.size()); + m_constants.CameraUp = Vector3::TransformNormal(Vector3::Up, view.Invert()); + m_constants.SimulationTime = m_simulationTime; + XMStoreFloat3(&m_constants.Color, DirectX::Colors::Red); + + // Update GPU Buffers + TryResizeBuffers(); +} + +void ParticleSystem::Draw(ID3D12GraphicsCommandList6* commandList, GraphicsMemory* graphicsMemory) +{ + // Copy cpu memory to upload buffer + auto upload = graphicsMemory->Allocate(GetAlignedSize(sizeof(Constants)) + m_particles.size() * sizeof(Particle)); + + std::memcpy(static_cast(upload.Memory()), &m_constants, sizeof(Constants)); + std::memcpy(static_cast(upload.Memory()) + GetAlignedSize(sizeof(Constants)), m_particles.data(), m_particles.size() * sizeof(Particle)); + + // Copy from upload buffer to resource + DirectX::TransitionResource(commandList, m_particleBuffer.Get(), D3D12_RESOURCE_STATE_GENERIC_READ, D3D12_RESOURCE_STATE_COPY_DEST); + DirectX::TransitionResource(commandList, m_constantBuffer.Get(), D3D12_RESOURCE_STATE_GENERIC_READ, D3D12_RESOURCE_STATE_COPY_DEST); + + commandList->CopyBufferRegion(m_constantBuffer.Get(), 0, upload.Resource(), 0, sizeof(Constants)); + commandList->CopyBufferRegion(m_particleBuffer.Get(), 0, upload.Resource(), GetAlignedSize(sizeof(Constants)), m_particles.size() * sizeof(Particle)); + + DirectX::TransitionResource(commandList, m_particleBuffer.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_GENERIC_READ); + DirectX::TransitionResource(commandList, m_constantBuffer.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_GENERIC_READ); + + // Draw + commandList->SetGraphicsRootSignature(m_rootSignature.Get()); + commandList->SetPipelineState(m_particlePSO.Get()); + + commandList->SetGraphicsRootConstantBufferView(0, m_constantBuffer->GetGPUVirtualAddress()); + commandList->SetGraphicsRootShaderResourceView(1, m_particleBuffer->GetGPUVirtualAddress()); + + auto count = RoundUpDiv((uint32_t)m_particles.size(), GROUP_SIZE / 4u); + commandList->DispatchMesh(count, 1, 1); +} + +void ParticleSystem::ReloadPipelineState(DX::DeviceResources& deviceResources) +{ + auto device = deviceResources.GetD3DDevice(); + + auto meshShader = DX::ReadData(s_meshShaderFilename); + auto pixelShader = DX::ReadData(s_pixelShaderFilename); + + DX::ThrowIfFailed(device->CreateRootSignature(0, meshShader.data(), meshShader.size(), IID_GRAPHICS_PPV_ARGS(m_rootSignature.ReleaseAndGetAddressOf()))); + + D3DX12_MESH_SHADER_PIPELINE_STATE_DESC psoDesc = {}; + psoDesc.pRootSignature = m_rootSignature.Get(); + psoDesc.MS = { meshShader.data(), meshShader.size() }; + psoDesc.PS = { pixelShader.data(), pixelShader.size() }; + psoDesc.NumRenderTargets = 1; + psoDesc.RTVFormats[0] = deviceResources.GetBackBufferFormat(); + psoDesc.DSVFormat = deviceResources.GetDepthBufferFormat(); + psoDesc.RasterizerState = CommonStates::CullNone; + psoDesc.BlendState = CommonStates::AlphaBlend; + psoDesc.DepthStencilState = CommonStates::DepthRead; + psoDesc.SampleMask = UINT_MAX; + psoDesc.SampleDesc = DefaultSampleDesc(); + + auto meshStreamDesc = CD3DX12_PIPELINE_MESH_STATE_STREAM(psoDesc); + + D3D12_PIPELINE_STATE_STREAM_DESC streamDesc = {}; + streamDesc.SizeInBytes = sizeof(meshStreamDesc); + streamDesc.pPipelineStateSubobjectStream = &meshStreamDesc; + + DX::ThrowIfFailed(device->CreatePipelineState(&streamDesc, IID_GRAPHICS_PPV_ARGS(m_particlePSO.ReleaseAndGetAddressOf()))); +} + +void ParticleSystem::TryResizeBuffers() +{ + assert(m_device != nullptr); + + constexpr size_t minSize = sizeof(Particle) * c_particleBufferInitSize; + + size_t newSize = m_particles.size() * sizeof(Particle) * 2; // Over-allocate by two to reduce number of allocations + newSize = std::max(newSize, minSize); + + m_particleBufferTemp = nullptr; // Release the old particle buffer, which we cached for a frame to avoid read after release + + // Don't resize if it's beneath our high watermark + if (m_particleBuffer != nullptr) + { + if (newSize < m_particleBuffer->GetDesc().Width) + { + return; + } + } + + m_particleBufferTemp = m_particleBuffer; // Temporarily cache the old particle buffer to avoid releasing before its last usage is complete. + + // Reallocate particle buffer + const CD3DX12_HEAP_PROPERTIES heapProps(D3D12_HEAP_TYPE_DEFAULT); + auto particleDesc = CD3DX12_RESOURCE_DESC::Buffer(GetAlignedSize(newSize)); + + DX::ThrowIfFailed(m_device->CreateCommittedResource(&heapProps, D3D12_HEAP_FLAG_NONE, &particleDesc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_GRAPHICS_PPV_ARGS(m_particleBuffer.ReleaseAndGetAddressOf()))); } diff --git a/Samples/Graphics/GeometricExpansion/Particles.h b/Samples/Graphics/GeometricExpansion/Particles.h index 54cfc44..bd3a620 100644 --- a/Samples/Graphics/GeometricExpansion/Particles.h +++ b/Samples/Graphics/GeometricExpansion/Particles.h @@ -42,6 +42,17 @@ namespace ATG ValueRange size ); + ~ParticleSystem() + { + ReleaseDevice(); + } + + ParticleSystem(ParticleSystem&&) = default; + ParticleSystem& operator= (ParticleSystem&&) = default; + + ParticleSystem(ParticleSystem const&) = delete; + ParticleSystem& operator= (ParticleSystem const&) = delete; + void SetSpawnRate(float spawnRate) { m_spawnFrequency = 1.0f / std::max(1e-4f, spawnRate); } void SetSpringCoefficent(float coeff) { m_springCoeff = std::max(1e-4f, coeff); } void SetDragFactor(float factor) { m_dragFactor = std::max(0.0f, factor); } @@ -49,6 +60,7 @@ namespace ATG void CreateResources(DX::DeviceResources& deviceResources); void ReloadPipelineState(DX::DeviceResources& deviceResources); + void ReleaseDevice(); void Update(float deltaTime, const DirectX::SimpleMath::Matrix& view, const DirectX::SimpleMath::Matrix& proj); void Draw(ID3D12GraphicsCommandList6* commandList, DirectX::GraphicsMemory* graphicsMemory); @@ -73,7 +85,7 @@ namespace ATG std::vector m_particles; // D3D12 API Objects - ID3D12Device* m_device; + Microsoft::WRL::ComPtr m_device; Microsoft::WRL::ComPtr m_rootSignature; Microsoft::WRL::ComPtr m_particlePSO; diff --git a/Samples/Graphics/GeometricExpansion/ReadMe.docx b/Samples/Graphics/GeometricExpansion/ReadMe.docx index ff1ddda705a7ee7427400e560f84d69b2c89c063..15c11b98c55f8c354e75edf1cb46a3e669a753eb 100644 GIT binary patch delta 29641 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 XKrbI%C|)KIwIqNF4oBz%>4o?oNU&hy delta 29312 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 diff --git a/Samples/Graphics/GeometricExpansion/StepTimer.h b/Samples/Graphics/GeometricExpansion/StepTimer.h index bb15fff..2dbefcb 100644 --- a/Samples/Graphics/GeometricExpansion/StepTimer.h +++ b/Samples/Graphics/GeometricExpansion/StepTimer.h @@ -111,7 +111,7 @@ namespace DX timeDelta *= TicksPerSecond; timeDelta /= static_cast(m_qpcFrequency.QuadPart); - uint32_t lastFrameCount = m_frameCount; + const uint32_t lastFrameCount = m_frameCount; if (m_isFixedTimeStep) { diff --git a/Samples/Graphics/GeometricExpansion/pch.h b/Samples/Graphics/GeometricExpansion/pch.h index a80e9b3..638798f 100644 --- a/Samples/Graphics/GeometricExpansion/pch.h +++ b/Samples/Graphics/GeometricExpansion/pch.h @@ -41,8 +41,8 @@ #include -#if _GRDK_VER < 0x4A610D2B /* GXDK Edition 200600 */ -#error This sample requires the June 2020 GDK or later +#if _GRDK_VER < 0x55F007B0 /* GDK Edition 211000 */ +#error This sample requires the October 2021 GDK or later #endif #ifdef _GAMING_XBOX_SCARLETT @@ -69,14 +69,20 @@ #include #include +#include #include +#include #include +#include +#include +#include #include +#include #include #include - -#include -#include +#include +#include +#include #ifdef _GAMING_XBOX #include @@ -89,7 +95,6 @@ #include #include "CommonStates.h" -#include "ControllerFont.h" #include "DescriptorHeap.h" #include "DirectXHelpers.h" #include "GamePad.h" @@ -97,21 +102,23 @@ #include "Keyboard.h" #include "Model.h" #include "Mouse.h" -#include "OrbitCamera.h" -#include "ReadData.h" #include "RenderTargetState.h" #include "ResourceUploadBatch.h" #include "SimpleMath.h" #include "SpriteBatch.h" #include "SpriteFont.h" +#include "ControllerFont.h" +#include "OrbitCamera.h" +#include "ReadData.h" + namespace DX { // Helper class for COM exceptions class com_exception : public std::exception { public: - com_exception(HRESULT hr) : result(hr) {} + com_exception(HRESULT hr) noexcept : result(hr) {} const char* what() const override { diff --git a/Samples/Graphics/HDR10/DeviceResources.cpp b/Samples/Graphics/HDR10/DeviceResources.cpp index 233948e..836bf26 100644 --- a/Samples/Graphics/HDR10/DeviceResources.cpp +++ b/Samples/Graphics/HDR10/DeviceResources.cpp @@ -1,6 +1,8 @@ // // DeviceResources.cpp - A wrapper for the Direct3D 12.X device and swapchain // +// Explicit HDR10+GameDVR rendering variant. +// #include "pch.h" #include "DeviceResources.h" @@ -55,12 +57,16 @@ DeviceResources::~DeviceResources() // Ensure we present a blank screen before cleaning up resources. if (m_commandQueue) { - (void)m_commandQueue->PresentX(0, nullptr, nullptr); + std::ignore = m_commandQueue->PresentX(0, nullptr, nullptr); } } // Configures the Direct3D device, and stores handles to it and the device context. +#ifdef _GAMING_XBOX_SCARLETT +void DeviceResources::CreateDeviceResources(D3D12XBOX_CREATE_DEVICE_FLAGS createDeviceFlags) +#else void DeviceResources::CreateDeviceResources() +#endif { // Create the DX12 API device object. D3D12XBOX_CREATE_DEVICE_PARAMETERS params = {}; @@ -77,6 +83,17 @@ void DeviceResources::CreateDeviceResources() params.GraphicsCommandQueueRingSizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); params.GraphicsScratchMemorySizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); params.ComputeScratchMemorySizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); +#ifdef _GAMING_XBOX_SCARLETT + params.CreateDeviceFlags = createDeviceFlags; + +#if (_GXDK_VER >= 0x585D070E /* GXDK Edition 221000 */) + if (m_options & c_AmplificationShaders) + { + params.AmplificationShaderIndirectArgsBufferSize = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); + params.AmplificationShaderPayloadBufferSize = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); + } +#endif +#endif HRESULT hr = D3D12XboxCreateDevice( nullptr, @@ -223,7 +240,7 @@ void DeviceResources::CreateWindowSizeDependentResources() // Obtain the back buffers for this window which will be the final render targets // and create render target views for each of them. - CD3DX12_HEAP_PROPERTIES swapChainHeapProperties(D3D12_HEAP_TYPE_DEFAULT); + const CD3DX12_HEAP_PROPERTIES swapChainHeapProperties(D3D12_HEAP_TYPE_DEFAULT); D3D12_RESOURCE_DESC swapChainBufferDesc = CD3DX12_RESOURCE_DESC::Tex2D( m_backBufferFormat, @@ -255,7 +272,7 @@ void DeviceResources::CreateWindowSizeDependentResources() rtvDesc.Format = m_backBufferFormat; rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; - CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( + const CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( m_rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), static_cast(n), m_rtvDescriptorSize); m_d3dDevice->CreateRenderTargetView(m_renderTargets[n].Get(), &rtvDesc, rtvDescriptor); @@ -283,7 +300,7 @@ void DeviceResources::CreateWindowSizeDependentResources() rtvDesc.Format = m_gameDVRFormat; rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; - CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptorGameDVR( + const CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptorGameDVR( m_rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), static_cast(m_backBufferCount + n), m_rtvDescriptorSize); m_d3dDevice->CreateRenderTargetView(m_renderTargetsGameDVR[n].Get(), &rtvDesc, rtvDescriptorGameDVR); @@ -297,7 +314,7 @@ void DeviceResources::CreateWindowSizeDependentResources() { // Allocate a 2-D surface as the depth/stencil buffer and create a depth/stencil view // on this surface. - CD3DX12_HEAP_PROPERTIES depthHeapProperties(D3D12_HEAP_TYPE_DEFAULT); + const CD3DX12_HEAP_PROPERTIES depthHeapProperties(D3D12_HEAP_TYPE_DEFAULT); D3D12_RESOURCE_DESC depthStencilDesc = CD3DX12_RESOURCE_DESC::Tex2D( m_depthBufferFormat, @@ -310,7 +327,7 @@ void DeviceResources::CreateWindowSizeDependentResources() D3D12_CLEAR_VALUE depthOptimizedClearValue = {}; depthOptimizedClearValue.Format = m_depthBufferFormat; - depthOptimizedClearValue.DepthStencil.Depth = 1.0f; + depthOptimizedClearValue.DepthStencil.Depth = (m_options & c_ReverseDepth) ? 0.0f : 1.0f; depthOptimizedClearValue.DepthStencil.Stencil = 0; ThrowIfFailed(m_d3dDevice->CreateCommittedResource( @@ -346,10 +363,6 @@ void DeviceResources::CreateWindowSizeDependentResources() // Prepare the command list and render target for rendering. void DeviceResources::Prepare(D3D12_RESOURCE_STATES beforeState, D3D12_RESOURCE_STATES afterState) { - // Wait until frame start is signaled - m_framePipelineToken = D3D12XBOX_FRAME_PIPELINE_TOKEN_NULL; - ThrowIfFailed(m_d3dDevice->WaitFrameEventX(D3D12XBOX_FRAME_EVENT_ORIGIN, INFINITE, nullptr, D3D12XBOX_WAIT_FRAME_EVENT_FLAG_NONE, &m_framePipelineToken)); - // Reset command list and allocator. ThrowIfFailed(m_commandAllocators[m_backBufferIndex]->Reset()); ThrowIfFailed(m_commandList->Reset(m_commandAllocators[m_backBufferIndex].Get(), nullptr)); @@ -359,7 +372,7 @@ void DeviceResources::Prepare(D3D12_RESOURCE_STATES beforeState, D3D12_RESOURCE_ // Transition the render target into the correct state to allow for drawing into it. if (m_options & c_EnableHDR) { - D3D12_RESOURCE_BARRIER barriers[2] = + const D3D12_RESOURCE_BARRIER barriers[2] = { CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), beforeState, afterState), @@ -370,7 +383,8 @@ void DeviceResources::Prepare(D3D12_RESOURCE_STATES beforeState, D3D12_RESOURCE_ } else { - D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), + const D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition( + m_renderTargets[m_backBufferIndex].Get(), beforeState, afterState); m_commandList->ResourceBarrier(1, &barrier); } @@ -385,7 +399,7 @@ void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState, _In_opt_ const // Transition the render target to the state that allows it to be presented to the display. if (m_options & c_EnableHDR) { - D3D12_RESOURCE_BARRIER barriers[2] = + const D3D12_RESOURCE_BARRIER barriers[2] = { CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), beforeState, D3D12_RESOURCE_STATE_PRESENT), CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargetsGameDVR[m_backBufferIndex].Get(), beforeState, D3D12_RESOURCE_STATE_PRESENT), @@ -394,7 +408,9 @@ void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState, _In_opt_ const } else { - D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), beforeState, D3D12_RESOURCE_STATE_PRESENT); + const D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition( + m_renderTargets[m_backBufferIndex].Get(), + beforeState, D3D12_RESOURCE_STATE_PRESENT); m_commandList->ResourceBarrier(1, &barrier); } } @@ -427,9 +443,10 @@ void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState, _In_opt_ const ); } - // Xbox One apps do not need to handle DXGI_ERROR_DEVICE_REMOVED or DXGI_ERROR_DEVICE_RESET. + // Xbox apps do not need to handle DXGI_ERROR_DEVICE_REMOVED or DXGI_ERROR_DEVICE_RESET. - MoveToNextFrame(); + // Update the back buffer index. + m_backBufferIndex = (m_backBufferIndex + 1) % m_backBufferCount; } // Handle GPU suspend/resume @@ -451,13 +468,13 @@ void DeviceResources::WaitForGpu() noexcept if (m_commandQueue && m_fence && m_fenceEvent.IsValid()) { // Schedule a Signal command in the GPU queue. - UINT64 fenceValue = m_fenceValues[m_backBufferIndex]; + const UINT64 fenceValue = m_fenceValues[m_backBufferIndex]; if (SUCCEEDED(m_commandQueue->Signal(m_fence.Get(), fenceValue))) { // Wait until the Signal has been processed. if (SUCCEEDED(m_fence->SetEventOnCompletion(fenceValue, m_fenceEvent.Get()))) { - WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); + std::ignore = WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); // Increment the fence value for the current frame. m_fenceValues[m_backBufferIndex]++; @@ -466,25 +483,17 @@ void DeviceResources::WaitForGpu() noexcept } } -// Prepare to render the next frame. -void DeviceResources::MoveToNextFrame() +// For PresentX rendering, we should wait for the origin event just before processing input. +void DeviceResources::WaitForOrigin() { - // Schedule a Signal command in the queue. - const UINT64 currentFenceValue = m_fenceValues[m_backBufferIndex]; - ThrowIfFailed(m_commandQueue->Signal(m_fence.Get(), currentFenceValue)); - - // Update the back buffer index. - m_backBufferIndex = (m_backBufferIndex + 1) % m_backBufferCount; - - // If the next frame is not ready to be rendered yet, wait until it is ready. - if (m_fence->GetCompletedValue() < m_fenceValues[m_backBufferIndex]) - { - ThrowIfFailed(m_fence->SetEventOnCompletion(m_fenceValues[m_backBufferIndex], m_fenceEvent.Get())); - WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); - } - - // Set the fence value for the next frame. - m_fenceValues[m_backBufferIndex] = currentFenceValue + 1; + // Wait until frame start is signaled + m_framePipelineToken = D3D12XBOX_FRAME_PIPELINE_TOKEN_NULL; + ThrowIfFailed(m_d3dDevice->WaitFrameEventX( + D3D12XBOX_FRAME_EVENT_ORIGIN, + INFINITE, + nullptr, + D3D12XBOX_WAIT_FRAME_EVENT_FLAG_NONE, + &m_framePipelineToken)); } // Set frame interval and register for frame events diff --git a/Samples/Graphics/HDR10/DeviceResources.h b/Samples/Graphics/HDR10/DeviceResources.h index e6d4603..57241bb 100644 --- a/Samples/Graphics/HDR10/DeviceResources.h +++ b/Samples/Graphics/HDR10/DeviceResources.h @@ -1,6 +1,8 @@ // // DeviceResources.h - A wrapper for the Direct3D 12.X device and swapchain // +// Explicit HDR10+GameDVR rendering variant. +// #pragma once @@ -10,9 +12,11 @@ namespace DX class DeviceResources { public: - static constexpr unsigned int c_Enable4K_UHD = 0x1; - static constexpr unsigned int c_EnableQHD = 0x2; - static constexpr unsigned int c_EnableHDR = 0x4; + static constexpr unsigned int c_Enable4K_UHD = 0x1; + static constexpr unsigned int c_EnableQHD = 0x2; + static constexpr unsigned int c_EnableHDR = 0x4; + static constexpr unsigned int c_ReverseDepth = 0x8; + static constexpr unsigned int c_AmplificationShaders = 0x10; DeviceResources(DXGI_FORMAT backBufferFormat = DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT depthBufferFormat = DXGI_FORMAT_D32_FLOAT, @@ -26,7 +30,11 @@ namespace DX DeviceResources(DeviceResources const&) = delete; DeviceResources& operator= (DeviceResources const&) = delete; +#ifdef _GAMING_XBOX_SCARLETT + void CreateDeviceResources(D3D12XBOX_CREATE_DEVICE_FLAGS createDeviceFlags = D3D12XBOX_CREATE_DEVICE_FLAG_NONE); +#else void CreateDeviceResources(); +#endif void CreateWindowSizeDependentResources(); void SetWindow(HWND window) noexcept { m_window = window; } void Prepare(D3D12_RESOURCE_STATES beforeState = D3D12_RESOURCE_STATE_PRESENT, @@ -36,6 +44,7 @@ namespace DX void Suspend(); void Resume(); void WaitForGpu() noexcept; + void WaitForOrigin(); // Device Accessors. RECT GetOutputSize() const noexcept { return m_outputSize; } @@ -69,8 +78,8 @@ namespace DX } // Direct3D HDR Game DVR support for Xbox One. - ID3D12Resource* GetGameDVRRenderTarget() const noexcept { return m_renderTargetsGameDVR[m_backBufferIndex].Get(); } - DXGI_FORMAT GetGameDVRFormat() const noexcept { return m_gameDVRFormat; } + ID3D12Resource* GetGameDVRRenderTarget() const noexcept { return m_renderTargetsGameDVR[m_backBufferIndex].Get(); } + DXGI_FORMAT GetGameDVRFormat() const noexcept { return m_gameDVRFormat; } CD3DX12_CPU_DESCRIPTOR_HANDLE GetGameDVRRenderTargetView() const noexcept { @@ -80,7 +89,6 @@ namespace DX } private: - void MoveToNextFrame(); void RegisterFrameEvents(); static constexpr size_t MAX_BACK_BUFFER_COUNT = 3; diff --git a/Samples/Graphics/HDR10/HDR10.cpp b/Samples/Graphics/HDR10/HDR10.cpp index c699dee..b84dfe0 100644 --- a/Samples/Graphics/HDR10/HDR10.cpp +++ b/Samples/Graphics/HDR10/HDR10.cpp @@ -66,23 +66,27 @@ using DirectX::Colors::White; namespace { - inline DirectX::XMVECTOR MakeColor(float value) { DirectX::XMVECTORF32 color = { value, value, value, 1.0f }; return color; } + inline XMVECTOR MakeColor(float value) noexcept + { + XMVECTORF32 color = { value, value, value, 1.0f }; + return color; + } // Clamp value between 0 and 1 - inline float Clamp(float value) + inline float Clamp(float value) noexcept { return std::min(std::max(value, 0.0f), 1.0f); } // Applies the sRGB gamma curve to a linear value. This function is only used to output UI values - float LinearToSRGB(float hdrSceneValue) + float LinearToSRGB(float hdrSceneValue) noexcept { - const float Cutoff = 0.0031308f; - const float Linear = 12.92f; - const float Scale = 1.055f; - const float Bias = 0.055f; - const float Gamma = 2.4f; - const float InvGamma = 1.0f / Gamma; + constexpr float Cutoff = 0.0031308f; + constexpr float Linear = 12.92f; + constexpr float Scale = 1.055f; + constexpr float Bias = 0.055f; + constexpr float Gamma = 2.4f; + constexpr float InvGamma = 1.0f / Gamma; // clamp values [0..1] hdrSceneValue = Clamp(hdrSceneValue); @@ -151,6 +155,8 @@ void Sample::Tick() { PIXBeginEvent(PIX_COLOR_DEFAULT, L"Frame %llu", m_frame); + m_deviceResources->WaitForOrigin(); + m_timer.Tick([&]() { Update(m_timer); @@ -210,10 +216,10 @@ void Sample::Update(DX::StepTimer const& timer) m_HDR10Data.PaperWhiteNits = std::min(m_HDR10Data.PaperWhiteNits, DX::c_MaxNitsFor2084); } - const float c_fastNitsDelta = 25.0f; - const float c_slowNitsDelta = 1.0f; - const float c_fastSceneValueDelta = 0.05f; - const float c_slowSceneValueDelta = 0.005f; + constexpr float c_fastNitsDelta = 25.0f; + constexpr float c_slowNitsDelta = 1.0f; + constexpr float c_fastSceneValueDelta = 0.05f; + constexpr float c_slowSceneValueDelta = 0.005f; if (gamepad.IsLeftThumbStickDown() || gamepad.IsLeftThumbStickLeft()) { @@ -296,7 +302,7 @@ void Sample::Render() PIXBeginEvent(commandList, PIX_COLOR_DEFAULT, L"Render"); ID3D12DescriptorHeap* heaps[] = { m_resourceDescriptorHeap->Heap() }; - commandList->SetDescriptorHeaps(_countof(heaps), heaps); + commandList->SetDescriptorHeaps(static_cast(std::size(heaps)), heaps); if (m_bRender2084Curve) { @@ -488,10 +494,10 @@ void Sample::Render2084Curve() m_primitiveBatch->DrawLine(VertexPositionColor(Vector3(viewportWidth, 0.5f, 0), White), VertexPositionColor(Vector3(viewportWidth, viewportHeight, 0), White)); // Render horizontal tick marks - float numSteps = 16; - for (int i = 0; i < numSteps; i++) + constexpr int c_numSteps = 16; + for (int i = 0; i < c_numSteps; i++) { - float x = (i * (viewportWidth / numSteps)) + 0.5f; + float x = (i * (viewportWidth / float(c_numSteps))) + 0.5f; float y = viewportHeight; m_primitiveBatch->DrawLine(VertexPositionColor(Vector3(x, y, 0), White), VertexPositionColor(Vector3(x, y - 10, 0), White)); } @@ -590,7 +596,7 @@ void Sample::Render2084Curve() m_fontBatch->End(); // Render blocks - const long size = 150; + constexpr long size = 150; RECT position = {}; position.left = 1920 - size * 4; position.top = 50; @@ -701,7 +707,7 @@ void Sample::Clear() m_hdrScene->TransitionTo(commandList, D3D12_RESOURCE_STATE_RENDER_TARGET); // Clear the views. - auto rtvDescriptor = m_rtvDescriptorHeap->GetCpuHandle(static_cast(ResourceDescriptors::HDRScene)); + auto const rtvDescriptor = m_rtvDescriptorHeap->GetCpuHandle(static_cast(ResourceDescriptors::HDRScene)); commandList->OMSetRenderTargets(1, &rtvDescriptor, FALSE, nullptr); @@ -710,8 +716,8 @@ void Sample::Clear() commandList->ClearRenderTargetView(rtvDescriptor, Black, 0, nullptr); // Set the viewport and scissor rect. - auto viewport = m_deviceResources->GetScreenViewport(); - auto scissorRect = m_deviceResources->GetScissorRect(); + auto const viewport = m_deviceResources->GetScreenViewport(); + auto const scissorRect = m_deviceResources->GetScissorRect(); commandList->RSSetViewports(1, &viewport); commandList->RSSetScissorRects(1, &scissorRect); @@ -781,7 +787,7 @@ void Sample::CreateDeviceDependentResources() m_resourceDescriptorHeap->GetCpuHandle(static_cast(ResourceDescriptors::HDRScene)), m_rtvDescriptorHeap->GetCpuHandle(static_cast(RTVDescriptors::HDRScene))); - auto size = m_deviceResources->GetOutputSize(); + auto const size = m_deviceResources->GetOutputSize(); m_hdrScene->SetWindow(size); // Init fonts diff --git a/Samples/Graphics/HDR10/Main.cpp b/Samples/Graphics/HDR10/Main.cpp index bd79f01..d30f2b8 100644 --- a/Samples/Graphics/HDR10/Main.cpp +++ b/Samples/Graphics/HDR10/Main.cpp @@ -29,7 +29,6 @@ namespace std::unique_ptr g_sample; HANDLE g_plmSuspendComplete = nullptr; HANDLE g_plmSignalResume = nullptr; - } LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); @@ -112,7 +111,7 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp PostMessage(reinterpret_cast(context), WM_USER, 0, 0); // To defer suspend, you must wait to exit this callback - (void)WaitForSingleObject(g_plmSuspendComplete, INFINITE); + std::ignore = WaitForSingleObject(g_plmSuspendComplete, INFINITE); } else { @@ -172,7 +171,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) // Complete deferral SetEvent(g_plmSuspendComplete); - (void)WaitForSingleObject(g_plmSignalResume, INFINITE); + std::ignore = WaitForSingleObject(g_plmSignalResume, INFINITE); sample->OnResuming(); } diff --git a/Samples/Graphics/HDR10/Readme.docx b/Samples/Graphics/HDR10/Readme.docx index e36ce2a9ae7d51443514c55a78a7d98d6dd1a8da..21217c0d9181e33fa54bae699c84c40d6aa9d4bf 100644 GIT binary patch delta 22186 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 RfDtuK0;DG1JH=m;;C~2{j6MJW diff --git a/Samples/Graphics/HDR10/StepTimer.h b/Samples/Graphics/HDR10/StepTimer.h index bb15fff..2dbefcb 100644 --- a/Samples/Graphics/HDR10/StepTimer.h +++ b/Samples/Graphics/HDR10/StepTimer.h @@ -111,7 +111,7 @@ namespace DX timeDelta *= TicksPerSecond; timeDelta /= static_cast(m_qpcFrequency.QuadPart); - uint32_t lastFrameCount = m_frameCount; + const uint32_t lastFrameCount = m_frameCount; if (m_isFixedTimeStep) { diff --git a/Samples/Graphics/HDR10/pch.h b/Samples/Graphics/HDR10/pch.h index 1b96692..0ff0ef3 100644 --- a/Samples/Graphics/HDR10/pch.h +++ b/Samples/Graphics/HDR10/pch.h @@ -37,8 +37,8 @@ #include -#if _GXDK_VER < 0x4A610D2B /* GXDK Edition 200600 */ -#error This sample requires the June 2020 GDK or later +#if _GXDK_VER < 0x55F007B0 /* GDK Edition 211000 */ +#error This sample requires the October 2021 GDK or later #endif #ifdef _GAMING_XBOX_SCARLETT @@ -69,6 +69,7 @@ #include #include #include +#include #include diff --git a/Samples/Graphics/HistogramCS/DeviceResources.cpp b/Samples/Graphics/HistogramCS/DeviceResources.cpp index e7d3590..fa67816 100644 --- a/Samples/Graphics/HistogramCS/DeviceResources.cpp +++ b/Samples/Graphics/HistogramCS/DeviceResources.cpp @@ -31,7 +31,7 @@ DeviceResources::DeviceResources( m_rtvDescriptorSize(0), m_screenViewport{}, m_scissorRect{}, - m_backBufferFormat((flags & c_EnableHDR) ? DXGI_FORMAT_R10G10B10A2_UNORM : backBufferFormat), + m_backBufferFormat(backBufferFormat), m_depthBufferFormat(depthBufferFormat), m_backBufferCount(backBufferCount), m_window(nullptr), @@ -54,7 +54,7 @@ DeviceResources::~DeviceResources() // Ensure we present a blank screen before cleaning up resources. if (m_commandQueue) { - (void)m_commandQueue->PresentX(0, nullptr, nullptr); + std::ignore = m_commandQueue->PresentX(0, nullptr, nullptr); } } @@ -82,6 +82,14 @@ void DeviceResources::CreateDeviceResources() params.ComputeScratchMemorySizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); #ifdef _GAMING_XBOX_SCARLETT params.CreateDeviceFlags = createDeviceFlags; + +#if (_GXDK_VER >= 0x585D070E /* GXDK Edition 221000 */) + if (m_options & c_AmplificationShaders) + { + params.AmplificationShaderIndirectArgsBufferSize = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); + params.AmplificationShaderPayloadBufferSize = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); + } +#endif #endif HRESULT hr = D3D12XboxCreateDevice( @@ -178,6 +186,9 @@ void DeviceResources::CreateDeviceResources() } break; + case XSystemDeviceType::XboxScarlettAnaconda /* Xbox Series X */: + case XSystemDeviceType::XboxOneXDevkit: + case XSystemDeviceType::XboxScarlettDevkit: default: m_outputSize = (m_options & c_Enable4K_UHD) ? RECT{ 0, 0, 3840, 2160 } : RECT{ 0, 0, 2560, 1440 }; break; @@ -225,7 +236,7 @@ void DeviceResources::CreateWindowSizeDependentResources() // Obtain the back buffers for this window which will be the final render targets // and create render target views for each of them. - CD3DX12_HEAP_PROPERTIES swapChainHeapProperties(D3D12_HEAP_TYPE_DEFAULT); + const CD3DX12_HEAP_PROPERTIES swapChainHeapProperties(D3D12_HEAP_TYPE_DEFAULT); D3D12_RESOURCE_DESC swapChainBufferDesc = CD3DX12_RESOURCE_DESC::Tex2D( m_backBufferFormat, @@ -264,7 +275,7 @@ void DeviceResources::CreateWindowSizeDependentResources() rtvDesc.Format = m_backBufferFormat; rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; - CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( + const CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( m_rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), static_cast(n), m_rtvDescriptorSize); m_d3dDevice->CreateRenderTargetView(m_renderTargets[n].Get(), &rtvDesc, rtvDescriptor); @@ -277,7 +288,7 @@ void DeviceResources::CreateWindowSizeDependentResources() { // Allocate a 2-D surface as the depth/stencil buffer and create a depth/stencil view // on this surface. - CD3DX12_HEAP_PROPERTIES depthHeapProperties(D3D12_HEAP_TYPE_DEFAULT); + const CD3DX12_HEAP_PROPERTIES depthHeapProperties(D3D12_HEAP_TYPE_DEFAULT); D3D12_RESOURCE_DESC depthStencilDesc = CD3DX12_RESOURCE_DESC::Tex2D( m_depthBufferFormat, @@ -290,7 +301,7 @@ void DeviceResources::CreateWindowSizeDependentResources() D3D12_CLEAR_VALUE depthOptimizedClearValue = {}; depthOptimizedClearValue.Format = m_depthBufferFormat; - depthOptimizedClearValue.DepthStencil.Depth = 1.0f; + depthOptimizedClearValue.DepthStencil.Depth = (m_options & c_ReverseDepth) ? 0.0f : 1.0f; depthOptimizedClearValue.DepthStencil.Stencil = 0; ThrowIfFailed(m_d3dDevice->CreateCommittedResource( @@ -326,10 +337,6 @@ void DeviceResources::CreateWindowSizeDependentResources() // Prepare the command list and render target for rendering. void DeviceResources::Prepare(D3D12_RESOURCE_STATES beforeState, D3D12_RESOURCE_STATES afterState) { - // Wait until frame start is signaled - m_framePipelineToken = D3D12XBOX_FRAME_PIPELINE_TOKEN_NULL; - ThrowIfFailed(m_d3dDevice->WaitFrameEventX(D3D12XBOX_FRAME_EVENT_ORIGIN, INFINITE, nullptr, D3D12XBOX_WAIT_FRAME_EVENT_FLAG_NONE, &m_framePipelineToken)); - // Reset command list and allocator. ThrowIfFailed(m_commandAllocators[m_backBufferIndex]->Reset()); ThrowIfFailed(m_commandList->Reset(m_commandAllocators[m_backBufferIndex].Get(), nullptr)); @@ -337,7 +344,8 @@ void DeviceResources::Prepare(D3D12_RESOURCE_STATES beforeState, D3D12_RESOURCE_ if (beforeState != afterState) { // Transition the render target into the correct state to allow for drawing into it. - D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), + const D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition( + m_renderTargets[m_backBufferIndex].Get(), beforeState, afterState); m_commandList->ResourceBarrier(1, &barrier); } @@ -349,7 +357,9 @@ void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState, _In_opt_ const if (beforeState != D3D12_RESOURCE_STATE_PRESENT) { // Transition the render target to the state that allows it to be presented to the display. - D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), beforeState, D3D12_RESOURCE_STATE_PRESENT); + const D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition( + m_renderTargets[m_backBufferIndex].Get(), + beforeState, D3D12_RESOURCE_STATE_PRESENT); m_commandList->ResourceBarrier(1, &barrier); } @@ -372,9 +382,10 @@ void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState, _In_opt_ const m_commandQueue->PresentX(1, &planeParameters, params) ); - // Xbox One apps do not need to handle DXGI_ERROR_DEVICE_REMOVED or DXGI_ERROR_DEVICE_RESET. + // Xbox apps do not need to handle DXGI_ERROR_DEVICE_REMOVED or DXGI_ERROR_DEVICE_RESET. - MoveToNextFrame(); + // Update the back buffer index. + m_backBufferIndex = (m_backBufferIndex + 1) % m_backBufferCount; } // Handle GPU suspend/resume @@ -396,13 +407,13 @@ void DeviceResources::WaitForGpu() noexcept if (m_commandQueue && m_fence && m_fenceEvent.IsValid()) { // Schedule a Signal command in the GPU queue. - UINT64 fenceValue = m_fenceValues[m_backBufferIndex]; + const UINT64 fenceValue = m_fenceValues[m_backBufferIndex]; if (SUCCEEDED(m_commandQueue->Signal(m_fence.Get(), fenceValue))) { // Wait until the Signal has been processed. if (SUCCEEDED(m_fence->SetEventOnCompletion(fenceValue, m_fenceEvent.Get()))) { - WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); + std::ignore = WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); // Increment the fence value for the current frame. m_fenceValues[m_backBufferIndex]++; @@ -411,25 +422,17 @@ void DeviceResources::WaitForGpu() noexcept } } -// Prepare to render the next frame. -void DeviceResources::MoveToNextFrame() +// For PresentX rendering, we should wait for the origin event just before processing input. +void DeviceResources::WaitForOrigin() { - // Schedule a Signal command in the queue. - const UINT64 currentFenceValue = m_fenceValues[m_backBufferIndex]; - ThrowIfFailed(m_commandQueue->Signal(m_fence.Get(), currentFenceValue)); - - // Update the back buffer index. - m_backBufferIndex = (m_backBufferIndex + 1) % m_backBufferCount; - - // If the next frame is not ready to be rendered yet, wait until it is ready. - if (m_fence->GetCompletedValue() < m_fenceValues[m_backBufferIndex]) - { - ThrowIfFailed(m_fence->SetEventOnCompletion(m_fenceValues[m_backBufferIndex], m_fenceEvent.Get())); - WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); - } - - // Set the fence value for the next frame. - m_fenceValues[m_backBufferIndex] = currentFenceValue + 1; + // Wait until frame start is signaled + m_framePipelineToken = D3D12XBOX_FRAME_PIPELINE_TOKEN_NULL; + ThrowIfFailed(m_d3dDevice->WaitFrameEventX( + D3D12XBOX_FRAME_EVENT_ORIGIN, + INFINITE, + nullptr, + D3D12XBOX_WAIT_FRAME_EVENT_FLAG_NONE, + &m_framePipelineToken)); } // Set frame interval and register for frame events diff --git a/Samples/Graphics/HistogramCS/DeviceResources.h b/Samples/Graphics/HistogramCS/DeviceResources.h index 012841d..10484d2 100644 --- a/Samples/Graphics/HistogramCS/DeviceResources.h +++ b/Samples/Graphics/HistogramCS/DeviceResources.h @@ -12,9 +12,11 @@ namespace DX class DeviceResources { public: - static constexpr unsigned int c_Enable4K_UHD = 0x1; - static constexpr unsigned int c_EnableQHD = 0x2; - static constexpr unsigned int c_EnableHDR = 0x4; + static constexpr unsigned int c_Enable4K_UHD = 0x1; + static constexpr unsigned int c_EnableQHD = 0x2; + static constexpr unsigned int c_EnableHDR = 0x4; + static constexpr unsigned int c_ReverseDepth = 0x8; + static constexpr unsigned int c_AmplificationShaders = 0x10; DeviceResources(DXGI_FORMAT backBufferFormat = DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT depthBufferFormat = DXGI_FORMAT_D32_FLOAT, @@ -42,6 +44,7 @@ namespace DX void Suspend(); void Resume(); void WaitForGpu() noexcept; + void WaitForOrigin(); // Device Accessors. RECT GetOutputSize() const noexcept { return m_outputSize; } @@ -77,7 +80,6 @@ namespace DX } private: - void MoveToNextFrame(); void RegisterFrameEvents(); static constexpr size_t MAX_BACK_BUFFER_COUNT = 3; diff --git a/Samples/Graphics/HistogramCS/HistogramCS.cpp b/Samples/Graphics/HistogramCS/HistogramCS.cpp index f7a6ac5..2d762b6 100644 --- a/Samples/Graphics/HistogramCS/HistogramCS.cpp +++ b/Samples/Graphics/HistogramCS/HistogramCS.cpp @@ -70,7 +70,10 @@ Sample::Sample() noexcept(false) : m_technique(HistTGSM) { // Use gamma-correct rendering. - m_deviceResources = std::make_unique(DXGI_FORMAT_B8G8R8A8_UNORM_SRGB, DXGI_FORMAT_D32_FLOAT, 2, + m_deviceResources = std::make_unique( + DXGI_FORMAT_B8G8R8A8_UNORM_SRGB, + DXGI_FORMAT_D32_FLOAT, + 2, DX::DeviceResources::c_Enable4K_UHD | DX::DeviceResources::c_EnableQHD); } @@ -102,6 +105,8 @@ void Sample::Tick() { PIXBeginEvent(PIX_COLOR_DEFAULT, L"Frame %llu", m_frame); + m_deviceResources->WaitForOrigin(); + m_timer.Tick([&]() { Update(m_timer); @@ -154,7 +159,7 @@ void Sample::Update(DX::StepTimer const&) } // Limit to avoid looking directly up or down - const float limit = XM_PI / 2.0f - 0.01f; + constexpr float limit = XM_PI / 2.0f - 0.01f; m_pitch = std::max(-limit, std::min(+limit, m_pitch)); if (m_yaw > XM_PI) @@ -198,7 +203,7 @@ void Sample::Render() // Compute histogram { - auto backBuffer = m_deviceResources->GetRenderTarget(); + auto const backBuffer = m_deviceResources->GetRenderTarget(); ScopedBarrier scopeBarrier(commandList, { @@ -212,9 +217,9 @@ void Sample::Render() m_gpuTimer.Start(commandList); - auto vp = m_deviceResources->GetScreenViewport(); + auto const vp = m_deviceResources->GetScreenViewport(); - uint32_t height = static_cast(vp.Height); + auto height = static_cast(vp.Height); CSConstantBuffer cb = { static_cast(vp.Width), height, 0, 0 }; auto cbHandle = m_graphicsMemory->AllocateConstant(cb); @@ -234,7 +239,7 @@ void Sample::Render() } // Visualize the histogram - auto vp = m_deviceResources->GetScreenViewport(); + auto const vp = m_deviceResources->GetScreenViewport(); { D3D12_VIEWPORT vizvp = @@ -281,7 +286,7 @@ void Sample::RenderScene(ID3D12GraphicsCommandList* commandList) m_modelResources->Heap(), m_states->Heap() }; - commandList->SetDescriptorHeaps(_countof(descriptorHeaps), descriptorHeaps); + commandList->SetDescriptorHeaps(static_cast(std::size(descriptorHeaps)), descriptorHeaps); Model::UpdateEffectMatrices(m_modelEffects, SimpleMath::Matrix::Identity, m_view, m_proj); @@ -300,8 +305,8 @@ void Sample::RenderUI(ID3D12GraphicsCommandList* commandList) }; commandList->SetDescriptorHeaps(_countof(descriptorHeaps), descriptorHeaps); - auto size = m_deviceResources->GetOutputSize(); - auto safe = SimpleMath::Viewport::ComputeTitleSafeArea(UINT(size.right), UINT(size.bottom)); + auto const size = m_deviceResources->GetOutputSize(); + auto const safe = SimpleMath::Viewport::ComputeTitleSafeArea(UINT(size.right), UINT(size.bottom)); m_batch->Begin(commandList); @@ -342,8 +347,8 @@ void Sample::Clear() PIXBeginEvent(commandList, PIX_COLOR_DEFAULT, L"Clear"); // Clear the views. - auto rtvDescriptor = m_deviceResources->GetRenderTargetView(); - auto dsvDescriptor = m_deviceResources->GetDepthStencilView(); + auto const rtvDescriptor = m_deviceResources->GetRenderTargetView(); + auto const dsvDescriptor = m_deviceResources->GetDepthStencilView(); commandList->OMSetRenderTargets(1, &rtvDescriptor, FALSE, &dsvDescriptor); // Use linear clear color for gamma-correct rendering. @@ -351,8 +356,8 @@ void Sample::Clear() commandList->ClearDepthStencilView(dsvDescriptor, D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr); // Set the viewport and scissor rect. - auto viewport = m_deviceResources->GetScreenViewport(); - auto scissorRect = m_deviceResources->GetScissorRect(); + auto const viewport = m_deviceResources->GetScreenViewport(); + auto const scissorRect = m_deviceResources->GetScissorRect(); commandList->RSSetViewports(1, &viewport); commandList->RSSetScissorRects(1, &scissorRect); @@ -419,7 +424,7 @@ void Sample::CreateDeviceDependentResources() m_model->LoadStaticBuffers(device, upload); { - RenderTargetState rtStateUI(m_deviceResources->GetBackBufferFormat(), m_deviceResources->GetDepthBufferFormat()); + const RenderTargetState rtStateUI(m_deviceResources->GetBackBufferFormat(), m_deviceResources->GetDepthBufferFormat()); SpriteBatchPipelineStateDescription pd(rtStateUI, &CommonStates::AlphaBlend); m_batch = std::make_unique(device, upload, pd); } @@ -428,7 +433,7 @@ void Sample::CreateDeviceDependentResources() m_deviceResources->WaitForGpu(); finish.wait(); - RenderTargetState rtState(m_deviceResources->GetBackBufferFormat(), m_deviceResources->GetDepthBufferFormat()); + const RenderTargetState rtState(m_deviceResources->GetBackBufferFormat(), m_deviceResources->GetDepthBufferFormat()); // Create scene effects. { @@ -564,7 +569,7 @@ void Sample::CreateWindowSizeDependentResources() { m_batch->SetViewport(m_deviceResources->GetScreenViewport()); - auto size = m_deviceResources->GetOutputSize(); + auto const size = m_deviceResources->GetOutputSize(); auto device = m_deviceResources->GetD3DDevice(); diff --git a/Samples/Graphics/HistogramCS/Main.cpp b/Samples/Graphics/HistogramCS/Main.cpp index ae606f3..18e116b 100644 --- a/Samples/Graphics/HistogramCS/Main.cpp +++ b/Samples/Graphics/HistogramCS/Main.cpp @@ -117,7 +117,7 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp PostMessage(reinterpret_cast(context), WM_USER, 0, 0); // To defer suspend, you must wait to exit this callback - (void)WaitForSingleObject(g_plmSuspendComplete, INFINITE); + std::ignore = WaitForSingleObject(g_plmSuspendComplete, INFINITE); } else { @@ -177,7 +177,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) // Complete deferral SetEvent(g_plmSuspendComplete); - (void)WaitForSingleObject(g_plmSignalResume, INFINITE); + std::ignore = WaitForSingleObject(g_plmSignalResume, INFINITE); SetDisplayMode(); diff --git a/Samples/Graphics/HistogramCS/Readme.docx b/Samples/Graphics/HistogramCS/Readme.docx index 6b5157548ebe8e081a2e92959001e8b8a024004f..637833634e6f6f070bd92c5dd305dd741a106d6e 100644 GIT binary patch delta 28857 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 delta 28207 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 diff --git a/Samples/Graphics/HistogramCS/StepTimer.h b/Samples/Graphics/HistogramCS/StepTimer.h index bb15fff..2dbefcb 100644 --- a/Samples/Graphics/HistogramCS/StepTimer.h +++ b/Samples/Graphics/HistogramCS/StepTimer.h @@ -111,7 +111,7 @@ namespace DX timeDelta *= TicksPerSecond; timeDelta /= static_cast(m_qpcFrequency.QuadPart); - uint32_t lastFrameCount = m_frameCount; + const uint32_t lastFrameCount = m_frameCount; if (m_isFixedTimeStep) { diff --git a/Samples/Graphics/HistogramCS/pch.h b/Samples/Graphics/HistogramCS/pch.h index 43d021d..e8a282c 100644 --- a/Samples/Graphics/HistogramCS/pch.h +++ b/Samples/Graphics/HistogramCS/pch.h @@ -37,8 +37,8 @@ #include -#if _GXDK_VER < 0x4A610F3C /* GDK Edition 200800 */ -#error This sample requires the August 2020 GDK or later +#if _GXDK_VER < 0x55F007B0 /* GDK Edition 211000 */ +#error This sample requires the October 2021 GDK or later #endif #ifdef _GAMING_XBOX_SCARLETT @@ -69,6 +69,7 @@ #include #include #include +#include #include diff --git a/Samples/Graphics/HlslCompile/DeviceResources.cpp b/Samples/Graphics/HlslCompile/DeviceResources.cpp index d76fd63..fa67816 100644 --- a/Samples/Graphics/HlslCompile/DeviceResources.cpp +++ b/Samples/Graphics/HlslCompile/DeviceResources.cpp @@ -31,14 +31,13 @@ DeviceResources::DeviceResources( m_rtvDescriptorSize(0), m_screenViewport{}, m_scissorRect{}, - m_backBufferFormat((flags & c_EnableHDR) ? DXGI_FORMAT_R10G10B10A2_UNORM : backBufferFormat), + m_backBufferFormat(backBufferFormat), m_depthBufferFormat(depthBufferFormat), m_backBufferCount(backBufferCount), m_window(nullptr), m_d3dFeatureLevel(D3D_FEATURE_LEVEL_12_0), m_outputSize{0, 0, 1920, 1080}, - m_options(flags), - m_gameDVRFormat((flags & c_EnableHDR) ? backBufferFormat : DXGI_FORMAT_UNKNOWN) + m_options(flags) { if (backBufferCount < 2 || backBufferCount > MAX_BACK_BUFFER_COUNT) { @@ -55,12 +54,16 @@ DeviceResources::~DeviceResources() // Ensure we present a blank screen before cleaning up resources. if (m_commandQueue) { - (void)m_commandQueue->PresentX(0, nullptr, nullptr); + std::ignore = m_commandQueue->PresentX(0, nullptr, nullptr); } } // Configures the Direct3D device, and stores handles to it and the device context. +#ifdef _GAMING_XBOX_SCARLETT +void DeviceResources::CreateDeviceResources(D3D12XBOX_CREATE_DEVICE_FLAGS createDeviceFlags) +#else void DeviceResources::CreateDeviceResources() +#endif { // Create the DX12 API device object. D3D12XBOX_CREATE_DEVICE_PARAMETERS params = {}; @@ -77,6 +80,17 @@ void DeviceResources::CreateDeviceResources() params.GraphicsCommandQueueRingSizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); params.GraphicsScratchMemorySizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); params.ComputeScratchMemorySizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); +#ifdef _GAMING_XBOX_SCARLETT + params.CreateDeviceFlags = createDeviceFlags; + +#if (_GXDK_VER >= 0x585D070E /* GXDK Edition 221000 */) + if (m_options & c_AmplificationShaders) + { + params.AmplificationShaderIndirectArgsBufferSize = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); + params.AmplificationShaderPayloadBufferSize = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); + } +#endif +#endif HRESULT hr = D3D12XboxCreateDevice( nullptr, @@ -107,7 +121,7 @@ void DeviceResources::CreateDeviceResources() // Create descriptor heaps for render target views and depth stencil views. D3D12_DESCRIPTOR_HEAP_DESC rtvDescriptorHeapDesc = {}; - rtvDescriptorHeapDesc.NumDescriptors = (m_options & c_EnableHDR) ? (m_backBufferCount * 2) : m_backBufferCount; + rtvDescriptorHeapDesc.NumDescriptors = m_backBufferCount; rtvDescriptorHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; ThrowIfFailed(m_d3dDevice->CreateDescriptorHeap(&rtvDescriptorHeapDesc, IID_GRAPHICS_PPV_ARGS(m_rtvDescriptorHeap.ReleaseAndGetAddressOf()))); @@ -152,31 +166,46 @@ void DeviceResources::CreateDeviceResources() m_fenceEvent.Attach(CreateEventEx(nullptr, nullptr, 0, EVENT_MODIFY_STATE | SYNCHRONIZE)); if (!m_fenceEvent.IsValid()) { - throw std::exception("CreateEvent"); + throw std::system_error(std::error_code(static_cast(GetLastError()), std::system_category()), "CreateEventEx"); } - if (m_options & c_Enable4K_UHD) + if (m_options & (c_Enable4K_UHD | c_EnableQHD)) { switch (XSystemGetDeviceType()) { case XSystemDeviceType::XboxOne: case XSystemDeviceType::XboxOneS: - case XSystemDeviceType::XboxScarlettLockhart /* Xbox Series S */: - m_options &= ~c_Enable4K_UHD; -#ifdef _DEBUG - OutputDebugStringA("INFO: Swapchain using 1080p (1920 x 1080)\n"); -#endif + m_options &= ~(c_Enable4K_UHD | c_EnableQHD); break; + case XSystemDeviceType::XboxScarlettLockhart /* Xbox Series S */: + m_options &= ~c_Enable4K_UHD; + if (m_options & c_EnableQHD) + { + m_outputSize = { 0, 0, 2560, 1440 }; + } + break; + + case XSystemDeviceType::XboxScarlettAnaconda /* Xbox Series X */: + case XSystemDeviceType::XboxOneXDevkit: + case XSystemDeviceType::XboxScarlettDevkit: default: - m_outputSize = { 0, 0, 3840, 2160 }; -#ifdef _DEBUG - OutputDebugStringA("INFO: Swapchain using 4k (3840 x 2160)\n"); -#endif + m_outputSize = (m_options & c_Enable4K_UHD) ? RECT{ 0, 0, 3840, 2160 } : RECT{ 0, 0, 2560, 1440 }; break; } } +#ifdef _DEBUG + const char* info = nullptr; + switch (m_outputSize.bottom) + { + case 2160: info = "INFO: Swapchain using 4k (3840 x 2160)\n"; break; + case 1440: info = "INFO: Swapchain using 1440p (2560 x 1440)\n"; break; + default: info = "INFO: Swapchain using 1080p (1920 x 1080)\n"; break; + } + OutputDebugStringA(info); +#endif + RegisterFrameEvents(); } @@ -185,7 +214,7 @@ void DeviceResources::CreateWindowSizeDependentResources() { if (!m_window) { - throw std::exception("Call SetWindow with a valid window handle"); + throw std::logic_error("Call SetWindow with a valid Win32 window handle"); } // Wait until all previous GPU work is complete. @@ -198,7 +227,6 @@ void DeviceResources::CreateWindowSizeDependentResources() for (UINT n = 0; n < m_backBufferCount; n++) { m_renderTargets[n].Reset(); - m_renderTargetsGameDVR[n].Reset(); m_fenceValues[n] = m_fenceValues[m_backBufferIndex]; } @@ -208,7 +236,7 @@ void DeviceResources::CreateWindowSizeDependentResources() // Obtain the back buffers for this window which will be the final render targets // and create render target views for each of them. - CD3DX12_HEAP_PROPERTIES swapChainHeapProperties(D3D12_HEAP_TYPE_DEFAULT); + const CD3DX12_HEAP_PROPERTIES swapChainHeapProperties(D3D12_HEAP_TYPE_DEFAULT); D3D12_RESOURCE_DESC swapChainBufferDesc = CD3DX12_RESOURCE_DESC::Tex2D( m_backBufferFormat, @@ -219,6 +247,13 @@ void DeviceResources::CreateWindowSizeDependentResources() ); swapChainBufferDesc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; +#ifdef _GAMING_XBOX_XBOXONE + if (m_options & c_EnableHDR) + { + swapChainBufferDesc.Flags |= D3D12XBOX_RESOURCE_FLAG_ALLOW_AUTOMATIC_GAMEDVR_TONE_MAP; + } +#endif + D3D12_CLEAR_VALUE swapChainOptimizedClearValue = {}; swapChainOptimizedClearValue.Format = m_backBufferFormat; @@ -240,41 +275,12 @@ void DeviceResources::CreateWindowSizeDependentResources() rtvDesc.Format = m_backBufferFormat; rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; - CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( + const CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( m_rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), static_cast(n), m_rtvDescriptorSize); m_d3dDevice->CreateRenderTargetView(m_renderTargets[n].Get(), &rtvDesc, rtvDescriptor); } - if (m_options & c_EnableHDR) - { - swapChainBufferDesc.Format = swapChainOptimizedClearValue.Format = m_gameDVRFormat; - - for (UINT n = 0; n < m_backBufferCount; n++) - { - ThrowIfFailed(m_d3dDevice->CreateCommittedResource( - &swapChainHeapProperties, - D3D12_HEAP_FLAG_ALLOW_DISPLAY, - &swapChainBufferDesc, - D3D12_RESOURCE_STATE_PRESENT, - &swapChainOptimizedClearValue, - IID_GRAPHICS_PPV_ARGS(m_renderTargetsGameDVR[n].GetAddressOf()))); - - wchar_t name[25] = {}; - swprintf_s(name, L"GameDVR Render target %u", n); - m_renderTargetsGameDVR[n]->SetName(name); - - D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {}; - rtvDesc.Format = m_gameDVRFormat; - rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; - - CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptorGameDVR( - m_rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), - static_cast(m_backBufferCount + n), m_rtvDescriptorSize); - m_d3dDevice->CreateRenderTargetView(m_renderTargetsGameDVR[n].Get(), &rtvDesc, rtvDescriptorGameDVR); - } - } - // Reset the index to the current back buffer. m_backBufferIndex = 0; @@ -282,7 +288,7 @@ void DeviceResources::CreateWindowSizeDependentResources() { // Allocate a 2-D surface as the depth/stencil buffer and create a depth/stencil view // on this surface. - CD3DX12_HEAP_PROPERTIES depthHeapProperties(D3D12_HEAP_TYPE_DEFAULT); + const CD3DX12_HEAP_PROPERTIES depthHeapProperties(D3D12_HEAP_TYPE_DEFAULT); D3D12_RESOURCE_DESC depthStencilDesc = CD3DX12_RESOURCE_DESC::Tex2D( m_depthBufferFormat, @@ -295,7 +301,7 @@ void DeviceResources::CreateWindowSizeDependentResources() D3D12_CLEAR_VALUE depthOptimizedClearValue = {}; depthOptimizedClearValue.Format = m_depthBufferFormat; - depthOptimizedClearValue.DepthStencil.Depth = 1.0f; + depthOptimizedClearValue.DepthStencil.Depth = (m_options & c_ReverseDepth) ? 0.0f : 1.0f; depthOptimizedClearValue.DepthStencil.Stencil = 0; ThrowIfFailed(m_d3dDevice->CreateCommittedResource( @@ -331,10 +337,6 @@ void DeviceResources::CreateWindowSizeDependentResources() // Prepare the command list and render target for rendering. void DeviceResources::Prepare(D3D12_RESOURCE_STATES beforeState, D3D12_RESOURCE_STATES afterState) { - // Wait until frame start is signaled - m_framePipelineToken = D3D12XBOX_FRAME_PIPELINE_TOKEN_NULL; - ThrowIfFailed(m_d3dDevice->WaitFrameEventX(D3D12XBOX_FRAME_EVENT_ORIGIN, INFINITE, nullptr, D3D12XBOX_WAIT_FRAME_EVENT_FLAG_NONE, &m_framePipelineToken)); - // Reset command list and allocator. ThrowIfFailed(m_commandAllocators[m_backBufferIndex]->Reset()); ThrowIfFailed(m_commandList->Reset(m_commandAllocators[m_backBufferIndex].Get(), nullptr)); @@ -342,23 +344,10 @@ void DeviceResources::Prepare(D3D12_RESOURCE_STATES beforeState, D3D12_RESOURCE_ if (beforeState != afterState) { // Transition the render target into the correct state to allow for drawing into it. - if (m_options & c_EnableHDR) - { - D3D12_RESOURCE_BARRIER barriers[2] = - { - CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), - beforeState, afterState), - CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargetsGameDVR[m_backBufferIndex].Get(), - beforeState, afterState), - }; - m_commandList->ResourceBarrier(_countof(barriers), barriers); - } - else - { - D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), - beforeState, afterState); - m_commandList->ResourceBarrier(1, &barrier); - } + const D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition( + m_renderTargets[m_backBufferIndex].Get(), + beforeState, afterState); + m_commandList->ResourceBarrier(1, &barrier); } } @@ -368,20 +357,10 @@ void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState, _In_opt_ const if (beforeState != D3D12_RESOURCE_STATE_PRESENT) { // Transition the render target to the state that allows it to be presented to the display. - if (m_options & c_EnableHDR) - { - D3D12_RESOURCE_BARRIER barriers[2] = - { - CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), beforeState, D3D12_RESOURCE_STATE_PRESENT), - CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargetsGameDVR[m_backBufferIndex].Get(), beforeState, D3D12_RESOURCE_STATE_PRESENT), - }; - m_commandList->ResourceBarrier(_countof(barriers), barriers); - } - else - { - D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), beforeState, D3D12_RESOURCE_STATE_PRESENT); - m_commandList->ResourceBarrier(1, &barrier); - } + const D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition( + m_renderTargets[m_backBufferIndex].Get(), + beforeState, D3D12_RESOURCE_STATE_PRESENT); + m_commandList->ResourceBarrier(1, &barrier); } // Send the command list off to the GPU for processing. @@ -389,32 +368,24 @@ void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState, _In_opt_ const m_commandQueue->ExecuteCommandLists(1, CommandListCast(m_commandList.GetAddressOf())); // Present the backbuffer using the PresentX API. - D3D12XBOX_PRESENT_PLANE_PARAMETERS planeParameters[2] = {}; - planeParameters[0].Token = planeParameters[1].Token = m_framePipelineToken; - planeParameters[0].ResourceCount = planeParameters[1].ResourceCount = 1; - planeParameters[0].ppResources = m_renderTargets[m_backBufferIndex].GetAddressOf(); + D3D12XBOX_PRESENT_PLANE_PARAMETERS planeParameters = {}; + planeParameters.Token = m_framePipelineToken; + planeParameters.ResourceCount = 1; + planeParameters.ppResources = m_renderTargets[m_backBufferIndex].GetAddressOf(); if (m_options & c_EnableHDR) { - planeParameters[0].pSrcViewRects = planeParameters[1].pSrcViewRects = &m_outputSize; - planeParameters[0].ColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020; - planeParameters[1].ppResources = m_renderTargetsGameDVR[m_backBufferIndex].GetAddressOf(); - planeParameters[1].ColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; - - ThrowIfFailed( - m_commandQueue->PresentX(2, planeParameters, params) - ); - } - else - { - ThrowIfFailed( - m_commandQueue->PresentX(1, planeParameters, params) - ); + planeParameters.ColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020; } - // Xbox One apps do not need to handle DXGI_ERROR_DEVICE_REMOVED or DXGI_ERROR_DEVICE_RESET. + ThrowIfFailed( + m_commandQueue->PresentX(1, &planeParameters, params) + ); - MoveToNextFrame(); + // Xbox apps do not need to handle DXGI_ERROR_DEVICE_REMOVED or DXGI_ERROR_DEVICE_RESET. + + // Update the back buffer index. + m_backBufferIndex = (m_backBufferIndex + 1) % m_backBufferCount; } // Handle GPU suspend/resume @@ -436,13 +407,13 @@ void DeviceResources::WaitForGpu() noexcept if (m_commandQueue && m_fence && m_fenceEvent.IsValid()) { // Schedule a Signal command in the GPU queue. - UINT64 fenceValue = m_fenceValues[m_backBufferIndex]; + const UINT64 fenceValue = m_fenceValues[m_backBufferIndex]; if (SUCCEEDED(m_commandQueue->Signal(m_fence.Get(), fenceValue))) { // Wait until the Signal has been processed. if (SUCCEEDED(m_fence->SetEventOnCompletion(fenceValue, m_fenceEvent.Get()))) { - WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); + std::ignore = WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); // Increment the fence value for the current frame. m_fenceValues[m_backBufferIndex]++; @@ -451,25 +422,17 @@ void DeviceResources::WaitForGpu() noexcept } } -// Prepare to render the next frame. -void DeviceResources::MoveToNextFrame() +// For PresentX rendering, we should wait for the origin event just before processing input. +void DeviceResources::WaitForOrigin() { - // Schedule a Signal command in the queue. - const UINT64 currentFenceValue = m_fenceValues[m_backBufferIndex]; - ThrowIfFailed(m_commandQueue->Signal(m_fence.Get(), currentFenceValue)); - - // Update the back buffer index. - m_backBufferIndex = (m_backBufferIndex + 1) % m_backBufferCount; - - // If the next frame is not ready to be rendered yet, wait until it is ready. - if (m_fence->GetCompletedValue() < m_fenceValues[m_backBufferIndex]) - { - ThrowIfFailed(m_fence->SetEventOnCompletion(m_fenceValues[m_backBufferIndex], m_fenceEvent.Get())); - WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); - } - - // Set the fence value for the next frame. - m_fenceValues[m_backBufferIndex] = currentFenceValue + 1; + // Wait until frame start is signaled + m_framePipelineToken = D3D12XBOX_FRAME_PIPELINE_TOKEN_NULL; + ThrowIfFailed(m_d3dDevice->WaitFrameEventX( + D3D12XBOX_FRAME_EVENT_ORIGIN, + INFINITE, + nullptr, + D3D12XBOX_WAIT_FRAME_EVENT_FLAG_NONE, + &m_framePipelineToken)); } // Set frame interval and register for frame events diff --git a/Samples/Graphics/HlslCompile/DeviceResources.h b/Samples/Graphics/HlslCompile/DeviceResources.h index 1d26b17..4924894 100644 --- a/Samples/Graphics/HlslCompile/DeviceResources.h +++ b/Samples/Graphics/HlslCompile/DeviceResources.h @@ -10,8 +10,11 @@ namespace DX class DeviceResources { public: - static constexpr unsigned int c_Enable4K_UHD = 0x1; - static constexpr unsigned int c_EnableHDR = 0x2; + static constexpr unsigned int c_Enable4K_UHD = 0x1; + static constexpr unsigned int c_EnableQHD = 0x2; + static constexpr unsigned int c_EnableHDR = 0x4; + static constexpr unsigned int c_ReverseDepth = 0x8; + static constexpr unsigned int c_AmplificationShaders = 0x10; DeviceResources(DXGI_FORMAT backBufferFormat = DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT depthBufferFormat = DXGI_FORMAT_D32_FLOAT, @@ -25,7 +28,11 @@ namespace DX DeviceResources(DeviceResources const&) = delete; DeviceResources& operator= (DeviceResources const&) = delete; +#ifdef _GAMING_XBOX_SCARLETT + void CreateDeviceResources(D3D12XBOX_CREATE_DEVICE_FLAGS createDeviceFlags = D3D12XBOX_CREATE_DEVICE_FLAG_NONE); +#else void CreateDeviceResources(); +#endif void CreateWindowSizeDependentResources(); void SetWindow(HWND window) noexcept { m_window = window; } void Prepare(D3D12_RESOURCE_STATES beforeState = D3D12_RESOURCE_STATE_PRESENT, @@ -35,6 +42,7 @@ namespace DX void Suspend(); void Resume(); void WaitForGpu() noexcept; + void WaitForOrigin(); // Device Accessors. RECT GetOutputSize() const noexcept { return m_outputSize; } @@ -67,19 +75,7 @@ namespace DX return CD3DX12_CPU_DESCRIPTOR_HANDLE(m_dsvDescriptorHeap->GetCPUDescriptorHandleForHeapStart()); } - // Direct3D HDR Game DVR support for Xbox One. - ID3D12Resource* GetGameDVRRenderTarget() const noexcept { return m_renderTargetsGameDVR[m_backBufferIndex].Get(); } - DXGI_FORMAT GetGameDVRFormat() const noexcept { return m_gameDVRFormat; } - - CD3DX12_CPU_DESCRIPTOR_HANDLE GetGameDVRRenderTargetView() const noexcept - { - return CD3DX12_CPU_DESCRIPTOR_HANDLE( - m_rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), - static_cast(m_backBufferCount + m_backBufferIndex), m_rtvDescriptorSize); - } - private: - void MoveToNextFrame(); void RegisterFrameEvents(); static constexpr size_t MAX_BACK_BUFFER_COUNT = 3; @@ -126,9 +122,5 @@ namespace DX // DeviceResources options (see flags above) unsigned int m_options; - - // Direct3D HDR Game DVR support for Xbox One. - Microsoft::WRL::ComPtr m_renderTargetsGameDVR[MAX_BACK_BUFFER_COUNT]; - DXGI_FORMAT m_gameDVRFormat; }; } diff --git a/Samples/Graphics/HlslCompile/HlslCompile.cpp b/Samples/Graphics/HlslCompile/HlslCompile.cpp index 36b6120..4a5e846 100644 --- a/Samples/Graphics/HlslCompile/HlslCompile.cpp +++ b/Samples/Graphics/HlslCompile/HlslCompile.cpp @@ -30,7 +30,9 @@ Sample::Sample() noexcept(false) : m_frame(0) { // Use gamma-correct rendering. - m_deviceResources = std::make_unique(DXGI_FORMAT_B8G8R8A8_UNORM_SRGB, DXGI_FORMAT_D32_FLOAT, 2, + m_deviceResources = std::make_unique( + DXGI_FORMAT_B8G8R8A8_UNORM_SRGB, DXGI_FORMAT_D32_FLOAT, + 2, DX::DeviceResources::c_Enable4K_UHD); } @@ -41,7 +43,7 @@ void Sample::Initialize(HWND window) m_deviceResources->SetWindow(window); - m_deviceResources->CreateDeviceResources(); + m_deviceResources->CreateDeviceResources(); CreateDeviceDependentResources(); m_deviceResources->CreateWindowSizeDependentResources(); @@ -54,6 +56,8 @@ void Sample::Tick() { PIXBeginEvent(PIX_COLOR_DEFAULT, L"Frame %llu", m_frame); + m_deviceResources->WaitForOrigin(); + m_timer.Tick([&]() { Update(m_timer); @@ -85,137 +89,137 @@ void Sample::Update(DX::StepTimer const&) // Draws the scene. void Sample::Render() { - // Don't try to render anything before the first Update. - if (m_timer.GetFrameCount() == 0) - { - return; - } + // Don't try to render anything before the first Update. + if (m_timer.GetFrameCount() == 0) + { + return; + } - // Prepare the command list to render a new frame. - m_deviceResources->Prepare(); - Clear(); + // Prepare the command list to render a new frame. + m_deviceResources->Prepare(); + Clear(); - auto commandList = m_deviceResources->GetCommandList(); - PIXBeginEvent(commandList, PIX_COLOR_DEFAULT, L"Render"); + auto commandList = m_deviceResources->GetCommandList(); + PIXBeginEvent(commandList, PIX_COLOR_DEFAULT, L"Render"); - auto size = m_deviceResources->GetOutputSize(); - RECT safeRect = SimpleMath::Viewport::ComputeTitleSafeArea(UINT(size.right), UINT(size.bottom)); + auto const size = m_deviceResources->GetOutputSize(); + auto const safeRect = SimpleMath::Viewport::ComputeTitleSafeArea(UINT(size.right), UINT(size.bottom)); - XMFLOAT2 positionTitle = { (float)safeRect.left, (float)safeRect.top, }; - m_spriteBatch->Begin(commandList); - m_spriteBatch->SetViewport(m_deviceResources->GetScreenViewport()); - m_fontDescription->DrawString(m_spriteBatch.get(), L"HlslCompile sample - To test HLSL symbols, take a PIX GPU capture and examine the pixel shaders for each triangle below", positionTitle); - m_spriteBatch->End(); + XMFLOAT2 positionTitle = { (float)safeRect.left, (float)safeRect.top, }; + m_spriteBatch->Begin(commandList); + m_spriteBatch->SetViewport(m_deviceResources->GetScreenViewport()); + m_fontDescription->DrawString(m_spriteBatch.get(), L"HlslCompile sample - To test HLSL symbols, take a PIX GPU capture and examine the pixel shaders for each triangle below", positionTitle); + m_spriteBatch->End(); - auto width = size.right - size.left; - auto height = size.bottom - size.top; - auto yMargin = 0.02f * height; + auto width = size.right - size.left; + auto height = size.bottom - size.top; + auto yMargin = 0.02f * height; - D3D12_VIEWPORT viewportExample; - viewportExample.TopLeftX = positionTitle.x; - viewportExample.TopLeftY = positionTitle.y + 4.0f * yMargin; - viewportExample.Width = - viewportExample.Height = 0.05f * height; - viewportExample.MinDepth = 0.0f; - viewportExample.MaxDepth = 1.0; + D3D12_VIEWPORT viewportExample; + viewportExample.TopLeftX = positionTitle.x; + viewportExample.TopLeftY = positionTitle.y + 4.0f * yMargin; + viewportExample.Width = + viewportExample.Height = 0.05f * height; + viewportExample.MinDepth = 0.0f; + viewportExample.MaxDepth = 1.0; - auto DrawExample = [commandList, this, &viewportExample](const wchar_t* name, - ID3D12PipelineState* pso)-> void - { - static const VertexPositionColor c_vertexData[3] = - { - { - XMFLOAT3(0.0f, 1.0f, 1.0f), - SimpleMath::Vector4(Colors::Red), - }, // Top / Red - { - XMFLOAT3(1.0f, -1.0f, 1.0f), - SimpleMath::Vector4(Colors::Green), - }, // Right / Green - { - XMFLOAT3(-1.0f, -1.0f, 1.0f), - SimpleMath::Vector4(Colors::Blue), - } // Left / Blue - }; + auto DrawExample = [commandList, this, &viewportExample](const wchar_t* name, + ID3D12PipelineState* pso)-> void + { + static const VertexPositionColor c_vertexData[3] = + { + { + XMFLOAT3(0.0f, 1.0f, 1.0f), + SimpleMath::Vector4(Colors::Red), + }, // Top / Red + { + XMFLOAT3(1.0f, -1.0f, 1.0f), + SimpleMath::Vector4(Colors::Green), + }, // Right / Green + { + XMFLOAT3(-1.0f, -1.0f, 1.0f), + SimpleMath::Vector4(Colors::Blue), + } // Left / Blue + }; - PIXBeginEvent(commandList, PIX_COLOR_DEFAULT, name); - commandList->SetGraphicsRootSignature(m_rootSignature.Get()); - commandList->SetPipelineState(pso); - commandList->RSSetViewports(1, &viewportExample); - m_primitiveBatch->Begin(commandList); - m_primitiveBatch->DrawTriangle(c_vertexData[0], c_vertexData[1], c_vertexData[2]); - m_primitiveBatch->End(); - auto vp = m_deviceResources->GetScreenViewport(); - commandList->RSSetViewports(1, &vp); - PIXEndEvent(commandList); - }; + PIXBeginEvent(commandList, PIX_COLOR_DEFAULT, name); + commandList->SetGraphicsRootSignature(m_rootSignature.Get()); + commandList->SetPipelineState(pso); + commandList->RSSetViewports(1, &viewportExample); + m_primitiveBatch->Begin(commandList); + m_primitiveBatch->DrawTriangle(c_vertexData[0], c_vertexData[1], c_vertexData[2]); + m_primitiveBatch->End(); + auto vp = m_deviceResources->GetScreenViewport(); + commandList->RSSetViewports(1, &vp); + PIXEndEvent(commandList); + }; - auto DrawDescription = [this, commandList, &viewportExample, width, yMargin](const wchar_t* description, size_t size)->void - { - auto xTab1 = 0.005f * width; - XMFLOAT2 positionSize = { - viewportExample.TopLeftX + viewportExample.Width + xTab1, - viewportExample.TopLeftY + viewportExample.Height / 2.0f - yMargin / 2.0f, - }; - auto xTab2 = 0.080f * width; - XMFLOAT2 positionDescription = { - positionSize.x + xTab2, - positionSize.y, - }; + auto DrawDescription = [this, commandList, &viewportExample, width, yMargin](const wchar_t* description, size_t size)->void + { + auto xTab1 = 0.005f * width; + XMFLOAT2 positionSize = { + viewportExample.TopLeftX + viewportExample.Width + xTab1, + viewportExample.TopLeftY + viewportExample.Height / 2.0f - yMargin / 2.0f, + }; + auto xTab2 = 0.080f * width; + XMFLOAT2 positionDescription = { + positionSize.x + xTab2, + positionSize.y, + }; - wchar_t buffer[2048] = L""; - swprintf_s(buffer, L"(%6zd bytes)", size); + wchar_t buffer[2048] = L""; + swprintf_s(buffer, L"(%6zd bytes)", size); - m_spriteBatch->Begin(commandList); - m_spriteBatch->SetViewport(m_deviceResources->GetScreenViewport()); - m_fontDescription->DrawString(m_spriteBatch.get(), buffer, positionSize, Colors::Cyan); - m_fontDescription->DrawString(m_spriteBatch.get(), description, positionDescription); - m_spriteBatch->End(); - }; + m_spriteBatch->Begin(commandList); + m_spriteBatch->SetViewport(m_deviceResources->GetScreenViewport()); + m_fontDescription->DrawString(m_spriteBatch.get(), buffer, positionSize, Colors::Cyan); + m_fontDescription->DrawString(m_spriteBatch.get(), description, positionDescription); + m_spriteBatch->End(); + }; - // Draw triangle with each variant of the pixel shader. - // See comments at the CreateGraphicsPipelineState calls later in this file - DrawExample(L"DxcEmbeddedPdb", m_pipelineStateDxcEmbeddedPdb.Get()); - DrawDescription(L"Dxc.exe - Symbols are embedded in the shader binary and automatically found by PIX.", m_sizeDxcEmbeddedPdb); - viewportExample.TopLeftY += viewportExample.Height + yMargin; - DrawExample(L"DxcManualPdb", m_pipelineStateDxcManualPdb.Get()); - DrawDescription(L"Dxc.exe - Symbols are saved to a user-specified pdb file and automatically found by PIX.", m_sizeDxcManualPdb); - viewportExample.TopLeftY += viewportExample.Height + yMargin; - DrawExample(L"DxcAutoPdb", m_pipelineStateDxcAutoPdb.Get()); - DrawDescription(L"Dxc.exe - Symbols are saved to a pdb file with an auto-generated filename, and the user must set a pdb path in PIX.", m_sizeDxcAutoPdb); - viewportExample.TopLeftY += viewportExample.Height + yMargin; + // Draw triangle with each variant of the pixel shader. + // See comments at the CreateGraphicsPipelineState calls later in this file + DrawExample(L"DxcEmbeddedPdb", m_pipelineStateDxcEmbeddedPdb.Get()); + DrawDescription(L"Dxc.exe - Symbols are embedded in the shader binary and automatically found by PIX.", m_sizeDxcEmbeddedPdb); + viewportExample.TopLeftY += viewportExample.Height + yMargin; + DrawExample(L"DxcManualPdb", m_pipelineStateDxcManualPdb.Get()); + DrawDescription(L"Dxc.exe - Symbols are saved to a user-specified pdb file and automatically found by PIX.", m_sizeDxcManualPdb); + viewportExample.TopLeftY += viewportExample.Height + yMargin; + DrawExample(L"DxcAutoPdb", m_pipelineStateDxcAutoPdb.Get()); + DrawDescription(L"Dxc.exe - Symbols are saved to a pdb file with an auto-generated filename, and the user must set a pdb path in PIX.", m_sizeDxcAutoPdb); + viewportExample.TopLeftY += viewportExample.Height + yMargin; - DrawExample(L"DxCompilerEmbeddedPdb", m_pipelineStateDxCompilerEmbeddedPdb.Get()); + DrawExample(L"DxCompilerEmbeddedPdb", m_pipelineStateDxCompilerEmbeddedPdb.Get()); #if _GAMING_XBOX_SCARLETT DrawDescription(L"DxCompiler_xs.dll - Symbols are embedded in the shader binary and automatically found by PIX.", m_sizeDxCompilerEmbeddedPdb); #else DrawDescription(L"DxCompiler_x.dll - Symbols are embedded in the shader binary and automatically found by PIX.", m_sizeDxCompilerEmbeddedPdb); #endif viewportExample.TopLeftY += viewportExample.Height + yMargin; - DrawExample(L"DxCompilerManualPdb", m_pipelineStateDxCompilerManualPdb.Get()); + DrawExample(L"DxCompilerManualPdb", m_pipelineStateDxCompilerManualPdb.Get()); #if _GAMING_XBOX_SCARLETT DrawDescription(L"DxCompiler_xs.dll - Symbols are saved to a user-specified pdb file and automatically found by PIX.", m_sizeDxCompilerManualPdb); #else DrawDescription(L"DxCompiler_x.dll - Symbols are saved to a user-specified pdb file and automatically found by PIX.", m_sizeDxCompilerManualPdb); #endif - viewportExample.TopLeftY += viewportExample.Height + yMargin; - DrawExample(L"DxCompilerAutoPdb", m_pipelineStateDxCompilerAutoPdb.Get()); + viewportExample.TopLeftY += viewportExample.Height + yMargin; + DrawExample(L"DxCompilerAutoPdb", m_pipelineStateDxCompilerAutoPdb.Get()); #if _GAMING_XBOX_SCARLETT DrawDescription(L"DxCompiler_xs.dll - Symbols are saved to a pdb file with an auto-generated filename, and the user must set a pdb path in PIX.", m_sizeDxCompilerAutoPdb); #else DrawDescription(L"DxCompiler_x.dll - Symbols are saved to a pdb file with an auto-generated filename, and the user must set a pdb path in PIX.", m_sizeDxCompilerAutoPdb); #endif - viewportExample.TopLeftY += viewportExample.Height + yMargin; + viewportExample.TopLeftY += viewportExample.Height + yMargin; - PIXEndEvent(commandList); + PIXEndEvent(commandList); - // Show the new frame. - PIXBeginEvent(PIX_COLOR_DEFAULT, L"Present"); - m_deviceResources->Present(); - PIXBeginEvent(m_deviceResources->GetCommandQueue(), PIX_COLOR_DEFAULT, L"CommitGraphicsMemory"); - m_graphicsMemory->Commit(m_deviceResources->GetCommandQueue()); - PIXEndEvent(m_deviceResources->GetCommandQueue()); - PIXEndEvent(); + // Show the new frame. + PIXBeginEvent(PIX_COLOR_DEFAULT, L"Present"); + m_deviceResources->Present(); + PIXBeginEvent(m_deviceResources->GetCommandQueue(), PIX_COLOR_DEFAULT, L"CommitGraphicsMemory"); + m_graphicsMemory->Commit(m_deviceResources->GetCommandQueue()); + PIXEndEvent(m_deviceResources->GetCommandQueue()); + PIXEndEvent(); } // Helper method to clear the back buffers. @@ -225,8 +229,8 @@ void Sample::Clear() PIXBeginEvent(commandList, PIX_COLOR_DEFAULT, L"Clear"); // Clear the views. - auto rtvDescriptor = m_deviceResources->GetRenderTargetView(); - auto dsvDescriptor = m_deviceResources->GetDepthStencilView(); + auto const rtvDescriptor = m_deviceResources->GetRenderTargetView(); + auto const dsvDescriptor = m_deviceResources->GetDepthStencilView(); commandList->OMSetRenderTargets(1, &rtvDescriptor, FALSE, &dsvDescriptor); @@ -236,8 +240,8 @@ void Sample::Clear() commandList->ClearDepthStencilView(dsvDescriptor, D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr); // Set the viewport and scissor rect. - auto viewport = m_deviceResources->GetScreenViewport(); - auto scissorRect = m_deviceResources->GetScissorRect(); + auto const viewport = m_deviceResources->GetScreenViewport(); + auto const scissorRect = m_deviceResources->GetScissorRect(); commandList->RSSetViewports(1, &viewport); commandList->RSSetScissorRects(1, &scissorRect); @@ -263,96 +267,96 @@ void Sample::OnResuming() // These are the resources that depend on the device. void Sample::CreateDeviceDependentResources() { - auto device = m_deviceResources->GetD3DDevice(); + auto device = m_deviceResources->GetD3DDevice(); - auto size = m_deviceResources->GetOutputSize(); + auto const size = m_deviceResources->GetOutputSize(); - m_graphicsMemory = std::make_unique(device); + m_graphicsMemory = std::make_unique(device); - // Create root signature. - auto vertexShaderBlob = DX::ReadData(L"VertexShader.cso"); + // Create root signature. + auto vertexShaderBlob = DX::ReadData(L"VertexShader.cso"); - // Xbox One best practice is to use HLSL-based root signatures to support shader precompilation. + // Xbox One best practice is to use HLSL-based root signatures to support shader precompilation. - DX::ThrowIfFailed( - device->CreateRootSignature(0, vertexShaderBlob.data(), vertexShaderBlob.size(), - IID_GRAPHICS_PPV_ARGS(m_rootSignature.ReleaseAndGetAddressOf()))); + DX::ThrowIfFailed( + device->CreateRootSignature(0, vertexShaderBlob.data(), vertexShaderBlob.size(), + IID_GRAPHICS_PPV_ARGS(m_rootSignature.ReleaseAndGetAddressOf()))); - // Create the pipeline state, which includes loading shaders. - // Describe and create the graphics pipeline state object (PSO). - D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {}; - psoDesc.InputLayout = VertexPositionColor::InputLayout; - psoDesc.pRootSignature = m_rootSignature.Get(); - psoDesc.VS = { vertexShaderBlob.data(), vertexShaderBlob.size() }; - psoDesc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT); - psoDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT); - psoDesc.DepthStencilState.DepthEnable = FALSE; - psoDesc.DepthStencilState.StencilEnable = FALSE; - psoDesc.DSVFormat = m_deviceResources->GetDepthBufferFormat(); - psoDesc.SampleMask = UINT_MAX; - psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; - psoDesc.NumRenderTargets = 1; - psoDesc.RTVFormats[0] = m_deviceResources->GetBackBufferFormat(); - psoDesc.SampleDesc.Count = 1; + // Create the pipeline state, which includes loading shaders. + // Describe and create the graphics pipeline state object (PSO). + D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {}; + psoDesc.InputLayout = VertexPositionColor::InputLayout; + psoDesc.pRootSignature = m_rootSignature.Get(); + psoDesc.VS = { vertexShaderBlob.data(), vertexShaderBlob.size() }; + psoDesc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT); + psoDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT); + psoDesc.DepthStencilState.DepthEnable = FALSE; + psoDesc.DepthStencilState.StencilEnable = FALSE; + psoDesc.DSVFormat = m_deviceResources->GetDepthBufferFormat(); + psoDesc.SampleMask = UINT_MAX; + psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; + psoDesc.NumRenderTargets = 1; + psoDesc.RTVFormats[0] = m_deviceResources->GetBackBufferFormat(); + psoDesc.SampleDesc.Count = 1; - auto LoadPixelShader = [device](const wchar_t *name, D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc, const IID& riid, void** pso) -> size_t - { - auto pixelShaderBlob = DX::ReadData(name); - psoDesc.PS = { pixelShaderBlob.data(), pixelShaderBlob.size() }; + auto LoadPixelShader = [device](const wchar_t *name, D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc, const IID& riid, void** pso) -> size_t + { + auto pixelShaderBlob = DX::ReadData(name); + psoDesc.PS = { pixelShaderBlob.data(), pixelShaderBlob.size() }; DX::ThrowIfFailed(device->CreateGraphicsPipelineState(&psoDesc, riid, pso)); - return pixelShaderBlob.size(); - }; + return pixelShaderBlob.size(); + }; - // The standard pixel shader, compiled with dxc /Zi. - m_sizeDxcEmbeddedPdb = LoadPixelShader(L"PixelShader_Dxc_EmbeddedPdb.cso", psoDesc, IID_GRAPHICS_PPV_ARGS(m_pipelineStateDxcEmbeddedPdb.ReleaseAndGetAddressOf())); + // The standard pixel shader, compiled with dxc /Zi. + m_sizeDxcEmbeddedPdb = LoadPixelShader(L"PixelShader_Dxc_EmbeddedPdb.cso", psoDesc, IID_GRAPHICS_PPV_ARGS(m_pipelineStateDxcEmbeddedPdb.ReleaseAndGetAddressOf())); - // The standard pixel shader, compiled with dxc /Zi /Qstrip_debug /Fd . - m_sizeDxcManualPdb = LoadPixelShader(L"PixelShader_Dxc_ManualPdb.cso", psoDesc, IID_GRAPHICS_PPV_ARGS(m_pipelineStateDxcManualPdb.ReleaseAndGetAddressOf())); + // The standard pixel shader, compiled with dxc /Zi /Qstrip_debug /Fd . + m_sizeDxcManualPdb = LoadPixelShader(L"PixelShader_Dxc_ManualPdb.cso", psoDesc, IID_GRAPHICS_PPV_ARGS(m_pipelineStateDxcManualPdb.ReleaseAndGetAddressOf())); - // The standard pixel shader, compiled with dxc /Zi /Qstrip_debug /Fd \. - // The trailing backslash tells the compiler to automatically choose a filename equal to the shader hash - m_sizeDxcAutoPdb = LoadPixelShader(L"PixelShader_Dxc_AutoPdb.cso", psoDesc, IID_GRAPHICS_PPV_ARGS(m_pipelineStateDxcAutoPdb.ReleaseAndGetAddressOf())); + // The standard pixel shader, compiled with dxc /Zi /Qstrip_debug /Fd \. + // The trailing backslash tells the compiler to automatically choose a filename equal to the shader hash + m_sizeDxcAutoPdb = LoadPixelShader(L"PixelShader_Dxc_AutoPdb.cso", psoDesc, IID_GRAPHICS_PPV_ARGS(m_pipelineStateDxcAutoPdb.ReleaseAndGetAddressOf())); - // The pixel shader compiled with IDxcCompiler and /Zi. - m_sizeDxCompilerEmbeddedPdb = LoadPixelShader(L"PixelShader_DxCompiler_EmbeddedPdb.cso", psoDesc, IID_GRAPHICS_PPV_ARGS(m_pipelineStateDxCompilerEmbeddedPdb.ReleaseAndGetAddressOf())); + // The pixel shader compiled with IDxcCompiler and /Zi. + m_sizeDxCompilerEmbeddedPdb = LoadPixelShader(L"PixelShader_DxCompiler_EmbeddedPdb.cso", psoDesc, IID_GRAPHICS_PPV_ARGS(m_pipelineStateDxCompilerEmbeddedPdb.ReleaseAndGetAddressOf())); - // The pixel shader compiled with IDxcCompiler and /Zi /Qstrip_debug, with the symbols - // saved to a file with a name chosen manually. - m_sizeDxCompilerManualPdb = LoadPixelShader(L"PixelShader_DxCompiler_ManualPdb.cso", psoDesc, IID_GRAPHICS_PPV_ARGS(m_pipelineStateDxCompilerManualPdb.ReleaseAndGetAddressOf())); + // The pixel shader compiled with IDxcCompiler and /Zi /Qstrip_debug, with the symbols + // saved to a file with a name chosen manually. + m_sizeDxCompilerManualPdb = LoadPixelShader(L"PixelShader_DxCompiler_ManualPdb.cso", psoDesc, IID_GRAPHICS_PPV_ARGS(m_pipelineStateDxCompilerManualPdb.ReleaseAndGetAddressOf())); - // The pixel shader compiled with IDxcCompiler and /Zi /Qstrip_debug, with the symbols - // saved to a file named after the shader hash. - m_sizeDxCompilerAutoPdb = LoadPixelShader(L"PixelShader_DxCompiler_AutoPdb.cso", psoDesc, IID_GRAPHICS_PPV_ARGS(m_pipelineStateDxCompilerAutoPdb.ReleaseAndGetAddressOf())); + // The pixel shader compiled with IDxcCompiler and /Zi /Qstrip_debug, with the symbols + // saved to a file named after the shader hash. + m_sizeDxCompilerAutoPdb = LoadPixelShader(L"PixelShader_DxCompiler_AutoPdb.cso", psoDesc, IID_GRAPHICS_PPV_ARGS(m_pipelineStateDxCompilerAutoPdb.ReleaseAndGetAddressOf())); - { - m_primitiveBatch = std::make_unique>(device); + { + m_primitiveBatch = std::make_unique>(device); - m_resourceDescriptorHeap = std::make_unique(device, ResourceDescriptors::Count); + m_resourceDescriptorHeap = std::make_unique(device, ResourceDescriptors::Count); - ResourceUploadBatch resourceUpload(device); - resourceUpload.Begin(); + ResourceUploadBatch resourceUpload(device); + resourceUpload.Begin(); - RenderTargetState renderTargetState(m_deviceResources->GetBackBufferFormat(), m_deviceResources->GetDepthBufferFormat()); - SpriteBatchPipelineStateDescription pipelineDescription(renderTargetState); - m_spriteBatch = std::make_unique(device, resourceUpload, pipelineDescription); + const RenderTargetState renderTargetState(m_deviceResources->GetBackBufferFormat(), m_deviceResources->GetDepthBufferFormat()); + SpriteBatchPipelineStateDescription pipelineDescription(renderTargetState); + m_spriteBatch = std::make_unique(device, resourceUpload, pipelineDescription); - bool uhd = (size.right - size.left) > 3000; + const bool uhd = (size.right - size.left) > 3000; - m_fontDescription = std::make_unique(device, - resourceUpload, - uhd ? L"SegoeUI_36.spritefont" : L"SegoeUI_18.spritefont", - m_resourceDescriptorHeap->GetCpuHandle(ResourceDescriptors::FontDescription), - m_resourceDescriptorHeap->GetGpuHandle(ResourceDescriptors::FontDescription)); + m_fontDescription = std::make_unique(device, + resourceUpload, + uhd ? L"SegoeUI_36.spritefont" : L"SegoeUI_18.spritefont", + m_resourceDescriptorHeap->GetCpuHandle(ResourceDescriptors::FontDescription), + m_resourceDescriptorHeap->GetGpuHandle(ResourceDescriptors::FontDescription)); - auto uploadResourcesFinished = resourceUpload.End(m_deviceResources->GetCommandQueue()); - uploadResourcesFinished.wait(); // Wait for resources to upload - } + auto uploadResourcesFinished = resourceUpload.End(m_deviceResources->GetCommandQueue()); + uploadResourcesFinished.wait(); // Wait for resources to upload + } - // Wait until assets have been uploaded to the GPU. - m_deviceResources->WaitForGpu(); + // Wait until assets have been uploaded to the GPU. + m_deviceResources->WaitForGpu(); } // Allocate all memory resources that change on a window SizeChanged event. diff --git a/Samples/Graphics/HlslCompile/HlslCompile.h b/Samples/Graphics/HlslCompile/HlslCompile.h index d859638..7d19ca5 100644 --- a/Samples/Graphics/HlslCompile/HlslCompile.h +++ b/Samples/Graphics/HlslCompile/HlslCompile.h @@ -18,6 +18,13 @@ class Sample public: Sample() noexcept(false); + ~Sample() = default; + + Sample(Sample&&) = default; + Sample& operator= (Sample&&) = default; + + Sample(Sample const&) = delete; + Sample& operator= (Sample const&) = delete; // Initialization and management void Initialize(HWND window); @@ -28,9 +35,11 @@ public: // Messages void OnSuspending(); void OnResuming(); + void OnConstrained() {} + void OnUnConstrained() {} - // Properties - bool RequestHDRMode() const { return m_deviceResources ? (m_deviceResources->GetDeviceOptions() & DX::DeviceResources::c_EnableHDR) != 0 : false; } + // Properties + bool RequestHDRMode() const noexcept { return m_deviceResources ? (m_deviceResources->GetDeviceOptions() & DX::DeviceResources::c_EnableHDR) != 0 : false; } private: @@ -54,46 +63,46 @@ private: // DirectXTK objects. std::unique_ptr m_graphicsMemory; - std::unique_ptr m_resourceDescriptorHeap; + std::unique_ptr m_resourceDescriptorHeap; // Direct3D 12 objects Microsoft::WRL::ComPtr m_rootSignature; Microsoft::WRL::ComPtr m_vertexBuffer; - // Pipeline states for each version of the pixel shader. - // These differ only in compiler arguments pertaining to symbols. + // Pipeline states for each version of the pixel shader. + // These differ only in compiler arguments pertaining to symbols. - Microsoft::WRL::ComPtr m_pipelineStateDxcEmbeddedPdb; - Microsoft::WRL::ComPtr m_pipelineStateDxcManualPdb; - Microsoft::WRL::ComPtr m_pipelineStateDxcAutoPdb; + Microsoft::WRL::ComPtr m_pipelineStateDxcEmbeddedPdb; + Microsoft::WRL::ComPtr m_pipelineStateDxcManualPdb; + Microsoft::WRL::ComPtr m_pipelineStateDxcAutoPdb; - Microsoft::WRL::ComPtr m_pipelineStateDxCompilerEmbeddedPdb; - Microsoft::WRL::ComPtr m_pipelineStateDxCompilerManualPdb; - Microsoft::WRL::ComPtr m_pipelineStateDxCompilerAutoPdb; + Microsoft::WRL::ComPtr m_pipelineStateDxCompilerEmbeddedPdb; + Microsoft::WRL::ComPtr m_pipelineStateDxCompilerManualPdb; + Microsoft::WRL::ComPtr m_pipelineStateDxCompilerAutoPdb; - // Binary sizes - size_t m_sizeDxcEmbeddedPdb; - size_t m_sizeDxcManualPdb; - size_t m_sizeDxcAutoPdb; + // Binary sizes + size_t m_sizeDxcEmbeddedPdb; + size_t m_sizeDxcManualPdb; + size_t m_sizeDxcAutoPdb; - size_t m_sizeDxCompilerEmbeddedPdb; - size_t m_sizeDxCompilerManualPdb; - size_t m_sizeDxCompilerAutoPdb; + size_t m_sizeDxCompilerEmbeddedPdb; + size_t m_sizeDxCompilerManualPdb; + size_t m_sizeDxCompilerAutoPdb; - // Draw helper - std::unique_ptr> - m_primitiveBatch; + // Draw helper + std::unique_ptr> + m_primitiveBatch; - // Fonts - struct ResourceDescriptors - { - enum : uint32_t - { - FontDescription, - Count - }; - }; + // Fonts + struct ResourceDescriptors + { + enum : uint32_t + { + FontDescription, + Count + }; + }; - std::unique_ptr m_spriteBatch; - std::unique_ptr m_fontDescription; + std::unique_ptr m_spriteBatch; + std::unique_ptr m_fontDescription; }; diff --git a/Samples/Graphics/HlslCompile/Main.cpp b/Samples/Graphics/HlslCompile/Main.cpp index 527a0f0..ac29dcd 100644 --- a/Samples/Graphics/HlslCompile/Main.cpp +++ b/Samples/Graphics/HlslCompile/Main.cpp @@ -1,7 +1,7 @@ //-------------------------------------------------------------------------------------- // Main.cpp // -// Entry point for Microsoft GDK with Xbox extensions. +// Entry point for Microsoft GDK with Xbox extensions // // Advanced Technology Group (ATG) // Copyright (C) Microsoft Corporation. All rights reserved. @@ -18,18 +18,27 @@ using namespace DirectX; +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wcovered-switch-default" +#pragma clang diagnostic ignored "-Wswitch-enum" +#endif + +#pragma warning(disable : 4061) + namespace { std::unique_ptr g_sample; HANDLE g_plmSuspendComplete = nullptr; HANDLE g_plmSignalResume = nullptr; -}; +} bool g_HDRMode = false; LPCWSTR g_szAppName = L"HLSLSymbols"; LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); +void SetDisplayMode() noexcept; +void ExitSample() noexcept; // Entry point int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) @@ -57,6 +66,8 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp // Register class and create window PAPPSTATE_REGISTRATION hPLM = {}; + PAPPCONSTRAIN_REGISTRATION hPLM2 = {}; + { // Register class WNDCLASSEX wcex = {}; @@ -66,7 +77,7 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp wcex.hInstance = hInstance; wcex.lpszClassName = L"HLSLSymbolsWindowClass"; wcex.hCursor = LoadCursor(nullptr, IDC_ARROW); - wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); + wcex.hbrBackground = reinterpret_cast(COLOR_WINDOW + 1); if (!RegisterClassEx(&wcex)) return 1; @@ -79,17 +90,7 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp ShowWindow(hwnd, nCmdShow); - if (g_sample->RequestHDRMode()) - { - // Request HDR mode. - auto result = XDisplayTryEnableHdrMode(XDisplayHdrModePreference::PreferHdr, nullptr); - - g_HDRMode = (result == XDisplayHdrModeResult::Enabled); - - #ifdef _DEBUG - OutputDebugStringA((g_HDRMode) ? "INFO: Display in HDR Mode\n" : "INFO: Display in SDR Mode\n"); - #endif - } + SetDisplayMode(); SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast(g_sample.get())); @@ -116,7 +117,7 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp PostMessage(reinterpret_cast(context), WM_USER, 0, 0); // To defer suspend, you must wait to exit this callback - (void)WaitForSingleObject(g_plmSuspendComplete, INFINITE); + std::ignore = WaitForSingleObject(g_plmSuspendComplete, INFINITE); } else { @@ -124,6 +125,13 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp } }, hwnd, &hPLM)) return 1; + + if (RegisterAppConstrainedChangeNotification([](BOOLEAN constrained, PVOID context) + { + // To ensure we use the main UI thread to process the notification, we self-post a message + SendMessage(reinterpret_cast(context), WM_USER + 1, (constrained) ? 1u : 0u, 0); + }, hwnd, &hPLM2)) + return 1; } // Main message loop @@ -144,13 +152,14 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp g_sample.reset(); UnregisterAppStateChangeNotification(hPLM); - + UnregisterAppConstrainedChangeNotification(hPLM2); + CloseHandle(g_plmSuspendComplete); CloseHandle(g_plmSignalResume); XGameRuntimeUninitialize(); - return (int) msg.wParam; + return static_cast(msg.wParam); } // Windows procedure @@ -168,18 +177,52 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) // Complete deferral SetEvent(g_plmSuspendComplete); - (void)WaitForSingleObject(g_plmSignalResume, INFINITE); + std::ignore = WaitForSingleObject(g_plmSignalResume, INFINITE); + + SetDisplayMode(); sample->OnResuming(); } break; + + case WM_USER + 1: + if (sample) + { + if (wParam) + { + sample->OnConstrained(); + } + else + { + SetDisplayMode(); + + sample->OnUnConstrained(); + } + } + break; } return DefWindowProc(hWnd, message, wParam, lParam); } +// HDR helper +void SetDisplayMode() noexcept +{ + if (g_sample && g_sample->RequestHDRMode()) + { + // Request HDR mode. + auto result = XDisplayTryEnableHdrMode(XDisplayHdrModePreference::PreferHdr, nullptr); + + g_HDRMode = (result == XDisplayHdrModeResult::Enabled); + +#ifdef _DEBUG + OutputDebugStringA((g_HDRMode) ? "INFO: Display in HDR Mode\n" : "INFO: Display in SDR Mode\n"); +#endif + } +} + // Exit helper -void ExitSample() +void ExitSample() noexcept { PostQuitMessage(0); } diff --git a/Samples/Graphics/HlslCompile/ReadMe.docx b/Samples/Graphics/HlslCompile/ReadMe.docx index 2428306de5b23d68a636d4dfebdc06064a478ce4..2f051f2fe1efa5d0e43f1e5ffcb4a4472d86567b 100644 GIT binary patch delta 28137 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 P4gh)t4J#-?2^v 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 diff --git a/Samples/Graphics/HlslCompile/StepTimer.h b/Samples/Graphics/HlslCompile/StepTimer.h index bb15fff..2dbefcb 100644 --- a/Samples/Graphics/HlslCompile/StepTimer.h +++ b/Samples/Graphics/HlslCompile/StepTimer.h @@ -111,7 +111,7 @@ namespace DX timeDelta *= TicksPerSecond; timeDelta /= static_cast(m_qpcFrequency.QuadPart); - uint32_t lastFrameCount = m_frameCount; + const uint32_t lastFrameCount = m_frameCount; if (m_isFixedTimeStep) { diff --git a/Samples/Graphics/HlslCompile/pch.h b/Samples/Graphics/HlslCompile/pch.h index 771b4d6..5be6a7b 100644 --- a/Samples/Graphics/HlslCompile/pch.h +++ b/Samples/Graphics/HlslCompile/pch.h @@ -37,8 +37,8 @@ #include -#if _GXDK_VER < 0x4A610D2B /* GXDK Edition 200600 */ -#error This sample requires the June 2020 GDK or later +#if _GXDK_VER < 0x55F007B0 /* GDK Edition 211000 */ +#error This sample requires the October 2021 GDK or later #endif #ifdef _GAMING_XBOX_SCARLETT @@ -56,13 +56,21 @@ #include #include +#include #include +#include #include +#include +#include +#include #include +#include #include #include +#include +#include +#include -#include #include #include @@ -86,7 +94,7 @@ namespace DX class com_exception : public std::exception { public: - com_exception(HRESULT hr) : result(hr) {} + com_exception(HRESULT hr) noexcept : result(hr) {} const char* what() const override { diff --git a/Samples/Graphics/MeshletCull/DeviceResources.cpp b/Samples/Graphics/MeshletCull/DeviceResources.cpp index dd465d2..2b40774 100644 --- a/Samples/Graphics/MeshletCull/DeviceResources.cpp +++ b/Samples/Graphics/MeshletCull/DeviceResources.cpp @@ -12,7 +12,6 @@ using namespace DX; using Microsoft::WRL::ComPtr; -#ifdef _GAMING_DESKTOP #ifdef __clang__ #pragma clang diagnostic ignored "-Wcovered-switch-default" #pragma clang diagnostic ignored "-Wswitch-enum" @@ -20,6 +19,7 @@ using Microsoft::WRL::ComPtr; #pragma warning(disable : 4061) +#ifdef _GAMING_DESKTOP namespace { inline DXGI_FORMAT NoSRGB(DXGI_FORMAT fmt) noexcept @@ -39,7 +39,8 @@ namespace DeviceResources::DeviceResources( DXGI_FORMAT backBufferFormat, DXGI_FORMAT depthBufferFormat, - UINT backBufferCount) noexcept(false) : + UINT backBufferCount, + unsigned int flags) noexcept(false) : m_backBufferIndex(0), m_fenceValues{}, #ifdef _GAMING_XBOX @@ -54,6 +55,7 @@ DeviceResources::DeviceResources( m_window(nullptr), m_d3dFeatureLevel(D3D_FEATURE_LEVEL_11_0), m_outputSize{ 0, 0, 1, 1 }, + m_options(flags), m_deviceNotify(nullptr) { if (backBufferCount < 2 || backBufferCount > MAX_BACK_BUFFER_COUNT) @@ -72,7 +74,7 @@ DeviceResources::~DeviceResources() // Ensure we present a blank screen before cleaning up resources. if (m_commandQueue) { - (void)m_commandQueue->PresentX(0, nullptr, nullptr); + std::ignore = m_commandQueue->PresentX(0, nullptr, nullptr); } #endif } @@ -98,6 +100,14 @@ void DeviceResources::CreateDeviceResources() params.GraphicsScratchMemorySizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); params.ComputeScratchMemorySizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); +#if defined(_GAMING_XBOX_SCARLETT) && (_GRDK_VER >= 0x585D070E /* GXDK Edition 221000 */) + if (m_options & c_AmplificationShaders) + { + params.AmplificationShaderIndirectArgsBufferSize = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); + params.AmplificationShaderPayloadBufferSize = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); + } +#endif + HRESULT hr = D3D12XboxCreateDevice( nullptr, ¶ms, @@ -150,7 +160,7 @@ void DeviceResources::CreateDeviceResources() 80 /* IDXGISwapChain::GetContainingOutput: The swapchain's adapter does not control the output on which the swapchain's window resides. */, }; DXGI_INFO_QUEUE_FILTER filter = {}; - filter.DenyList.NumIDs = _countof(hide); + filter.DenyList.NumIDs = static_cast(std::size(hide)); filter.DenyList.pIDList = hide; dxgiInfoQueue->AddStorageFilterEntries(DXGI_DEBUG_DXGI, &filter); } @@ -198,7 +208,7 @@ void DeviceResources::CreateDeviceResources() static_cast(1323) /* D3D12_MESSAGE_ID_CREATEMESHSHADER_TOPOLOGY_MISMATCH */, }; D3D12_INFO_QUEUE_FILTER filter = {}; - filter.DenyList.NumIDs = _countof(hide); + filter.DenyList.NumIDs = static_cast(std::size(hide)); filter.DenyList.pIDList = hide; d3dInfoQueue->AddStorageFilterEntries(&filter); } @@ -207,6 +217,9 @@ void DeviceResources::CreateDeviceResources() // Determine maximum supported feature level for this device static const D3D_FEATURE_LEVEL s_featureLevels[] = { +#if defined(NTDDI_WIN10_FE) && (NTDDI_VERSION >= NTDDI_WIN10_FE) + D3D_FEATURE_LEVEL_12_2, +#endif D3D_FEATURE_LEVEL_12_1, D3D_FEATURE_LEVEL_12_0, D3D_FEATURE_LEVEL_11_1, @@ -215,7 +228,7 @@ void DeviceResources::CreateDeviceResources() D3D12_FEATURE_DATA_FEATURE_LEVELS featLevels = { - _countof(s_featureLevels), s_featureLevels, D3D_FEATURE_LEVEL_11_0 + static_cast(std::size(s_featureLevels)), s_featureLevels, D3D_FEATURE_LEVEL_11_0 }; hr = m_d3dDevice->CheckFeatureSupport(D3D12_FEATURE_FEATURE_LEVELS, &featLevels, sizeof(featLevels)); @@ -286,7 +299,7 @@ void DeviceResources::CreateDeviceResources() m_fenceEvent.Attach(CreateEventEx(nullptr, nullptr, 0, EVENT_MODIFY_STATE | SYNCHRONIZE)); if (!m_fenceEvent.IsValid()) { - throw std::exception("CreateEvent"); + throw std::system_error(std::error_code(static_cast(GetLastError()), std::system_category()), "CreateEventEx"); } #ifdef _GAMING_XBOX @@ -299,7 +312,7 @@ void DeviceResources::CreateWindowSizeDependentResources() { if (!m_window) { - throw std::exception("Call SetWindow with a valid window handle"); + throw std::logic_error("Call SetWindow with a valid Win32 window handle"); } // Wait until all previous GPU work is complete. @@ -325,7 +338,7 @@ void DeviceResources::CreateWindowSizeDependentResources() // Obtain the back buffers for this window which will be the final render targets // and create render target views for each of them. - CD3DX12_HEAP_PROPERTIES swapChainHeapProperties(D3D12_HEAP_TYPE_DEFAULT); + const CD3DX12_HEAP_PROPERTIES swapChainHeapProperties(D3D12_HEAP_TYPE_DEFAULT); D3D12_RESOURCE_DESC swapChainBufferDesc = CD3DX12_RESOURCE_DESC::Tex2D( m_backBufferFormat, @@ -357,7 +370,7 @@ void DeviceResources::CreateWindowSizeDependentResources() rtvDesc.Format = m_backBufferFormat; rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; - CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( + const CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( m_rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), static_cast(n), m_rtvDescriptorSize); m_d3dDevice->CreateRenderTargetView(m_renderTargets[n].Get(), &rtvDesc, rtvDescriptor); @@ -368,7 +381,7 @@ void DeviceResources::CreateWindowSizeDependentResources() #else // _GAMING_DESKTOP - DXGI_FORMAT backBufferFormat = NoSRGB(m_backBufferFormat); + const DXGI_FORMAT backBufferFormat = NoSRGB(m_backBufferFormat); // If the swap chain already exists, resize it, otherwise create one. if (m_swapChain) @@ -451,7 +464,7 @@ void DeviceResources::CreateWindowSizeDependentResources() rtvDesc.Format = m_backBufferFormat; rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; - CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( + const CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( m_rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), static_cast(n), m_rtvDescriptorSize); m_d3dDevice->CreateRenderTargetView(m_renderTargets[n].Get(), &rtvDesc, rtvDescriptor); @@ -466,7 +479,7 @@ void DeviceResources::CreateWindowSizeDependentResources() { // Allocate a 2-D surface as the depth/stencil buffer and create a depth/stencil view // on this surface. - CD3DX12_HEAP_PROPERTIES depthHeapProperties(D3D12_HEAP_TYPE_DEFAULT); + const CD3DX12_HEAP_PROPERTIES depthHeapProperties(D3D12_HEAP_TYPE_DEFAULT); D3D12_RESOURCE_DESC depthStencilDesc = CD3DX12_RESOURCE_DESC::Tex2D( m_depthBufferFormat, @@ -479,7 +492,7 @@ void DeviceResources::CreateWindowSizeDependentResources() D3D12_CLEAR_VALUE depthOptimizedClearValue = {}; depthOptimizedClearValue.Format = m_depthBufferFormat; - depthOptimizedClearValue.DepthStencil.Depth = 1.0f; + depthOptimizedClearValue.DepthStencil.Depth = (m_options & c_ReverseDepth) ? 0.0f : 1.0f; depthOptimizedClearValue.DepthStencil.Stencil = 0; ThrowIfFailed(m_d3dDevice->CreateCommittedResource( @@ -590,12 +603,6 @@ void DeviceResources::HandleDeviceLost() // Prepare the command list and render target for rendering. void DeviceResources::Prepare(D3D12_RESOURCE_STATES beforeState, D3D12_RESOURCE_STATES afterState) { -#ifdef _GAMING_XBOX - // Wait until frame start is signaled - m_framePipelineToken = D3D12XBOX_FRAME_PIPELINE_TOKEN_NULL; - ThrowIfFailed(m_d3dDevice->WaitFrameEventX(D3D12XBOX_FRAME_EVENT_ORIGIN, INFINITE, nullptr, D3D12XBOX_WAIT_FRAME_EVENT_FLAG_NONE, &m_framePipelineToken)); -#endif - // Reset command list and allocator. ThrowIfFailed(m_commandAllocators[m_backBufferIndex]->Reset()); ThrowIfFailed(m_commandList->Reset(m_commandAllocators[m_backBufferIndex].Get(), nullptr)); @@ -603,7 +610,8 @@ void DeviceResources::Prepare(D3D12_RESOURCE_STATES beforeState, D3D12_RESOURCE_ if (beforeState != afterState) { // Transition the render target into the correct state to allow for drawing into it. - D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), + const D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition( + m_renderTargets[m_backBufferIndex].Get(), beforeState, afterState); m_commandList->ResourceBarrier(1, &barrier); } @@ -615,7 +623,9 @@ void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState) if (beforeState != D3D12_RESOURCE_STATE_PRESENT) { // Transition the render target to the state that allows it to be presented to the display. - D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), beforeState, D3D12_RESOURCE_STATE_PRESENT); + const D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition( + m_renderTargets[m_backBufferIndex].Get(), + beforeState, D3D12_RESOURCE_STATE_PRESENT); m_commandList->ResourceBarrier(1, &barrier); } @@ -635,9 +645,12 @@ void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState) m_commandQueue->PresentX(1, &planeParameters, nullptr) ); - // Xbox One apps do not need to handle DXGI_ERROR_DEVICE_REMOVED or DXGI_ERROR_DEVICE_RESET. + // Xbox apps do not need to handle DXGI_ERROR_DEVICE_REMOVED or DXGI_ERROR_DEVICE_RESET. -#else + // Update the back buffer index. + m_backBufferIndex = (m_backBufferIndex + 1) % m_backBufferCount; + +#else // _GAMING_DESKTOP // The first argument instructs DXGI to block until VSync, putting the application // to sleep until the next VSync. This ensures we don't waste any cycles rendering @@ -660,9 +673,8 @@ void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState) ThrowIfFailed(hr); } -#endif - MoveToNextFrame(); +#endif } // Handle GPU suspend/resume @@ -688,13 +700,13 @@ void DeviceResources::WaitForGpu() noexcept if (m_commandQueue && m_fence && m_fenceEvent.IsValid()) { // Schedule a Signal command in the GPU queue. - UINT64 fenceValue = m_fenceValues[m_backBufferIndex]; + const UINT64 fenceValue = m_fenceValues[m_backBufferIndex]; if (SUCCEEDED(m_commandQueue->Signal(m_fence.Get(), fenceValue))) { // Wait until the Signal has been processed. if (SUCCEEDED(m_fence->SetEventOnCompletion(fenceValue, m_fenceEvent.Get()))) { - WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); + std::ignore = WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); // Increment the fence value for the current frame. m_fenceValues[m_backBufferIndex]++; @@ -703,32 +715,20 @@ void DeviceResources::WaitForGpu() noexcept } } -// Prepare to render the next frame. -void DeviceResources::MoveToNextFrame() -{ - // Schedule a Signal command in the queue. - const UINT64 currentFenceValue = m_fenceValues[m_backBufferIndex]; - ThrowIfFailed(m_commandQueue->Signal(m_fence.Get(), currentFenceValue)); - - // Update the back buffer index. #ifdef _GAMING_XBOX - m_backBufferIndex = (m_backBufferIndex + 1) % m_backBufferCount; -#else - m_backBufferIndex = m_swapChain->GetCurrentBackBufferIndex(); -#endif - - // If the next frame is not ready to be rendered yet, wait until it is ready. - if (m_fence->GetCompletedValue() < m_fenceValues[m_backBufferIndex]) - { - ThrowIfFailed(m_fence->SetEventOnCompletion(m_fenceValues[m_backBufferIndex], m_fenceEvent.Get())); - WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); - } - - // Set the fence value for the next frame. - m_fenceValues[m_backBufferIndex] = currentFenceValue + 1; +// For PresentX rendering, we should wait for the origin event just before processing input. +void DeviceResources::WaitForOrigin() +{ + // Wait until frame start is signaled + m_framePipelineToken = D3D12XBOX_FRAME_PIPELINE_TOKEN_NULL; + ThrowIfFailed(m_d3dDevice->WaitFrameEventX( + D3D12XBOX_FRAME_EVENT_ORIGIN, + INFINITE, + nullptr, + D3D12XBOX_WAIT_FRAME_EVENT_FLAG_NONE, + &m_framePipelineToken)); } -#ifdef _GAMING_XBOX // Set frame interval and register for frame events void DeviceResources::RegisterFrameEvents() { @@ -757,7 +757,30 @@ void DeviceResources::RegisterFrameEvents() nullptr, D3D12XBOX_SCHEDULE_FRAME_EVENT_FLAG_NONE)); } -#else + +#else // _GAMING_DESKTOP + +// Prepare to render the next frame. +void DeviceResources::MoveToNextFrame() +{ + // Schedule a Signal command in the queue. + const UINT64 currentFenceValue = m_fenceValues[m_backBufferIndex]; + ThrowIfFailed(m_commandQueue->Signal(m_fence.Get(), currentFenceValue)); + + // Update the back buffer index. + m_backBufferIndex = m_swapChain->GetCurrentBackBufferIndex(); + + // If the next frame is not ready to be rendered yet, wait until it is ready. + if (m_fence->GetCompletedValue() < m_fenceValues[m_backBufferIndex]) + { + ThrowIfFailed(m_fence->SetEventOnCompletion(m_fenceValues[m_backBufferIndex], m_fenceEvent.Get())); + std::ignore = WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); + } + + // Set the fence value for the next frame. + m_fenceValues[m_backBufferIndex] = currentFenceValue + 1; +} + // This method acquires the first available hardware adapter that supports Direct3D 12. // If no such adapter can be found, try WARP. Otherwise throw an exception. void DeviceResources::GetAdapter(IDXGIAdapter1** ppAdapter) @@ -799,7 +822,7 @@ void DeviceResources::GetAdapter(IDXGIAdapter1** ppAdapter) // Try WARP12 instead if (FAILED(m_dxgiFactory->EnumWarpAdapter(IID_PPV_ARGS(adapter.ReleaseAndGetAddressOf())))) { - throw std::exception("WARP12 not available. Enable the 'Graphics Tools' optional feature"); + throw std::runtime_error("WARP12 not available. Enable the 'Graphics Tools' optional feature"); } OutputDebugStringA("Direct3D Adapter - WARP12\n"); @@ -808,7 +831,7 @@ void DeviceResources::GetAdapter(IDXGIAdapter1** ppAdapter) if (!adapter) { - throw std::exception("No Direct3D 12 device found"); + throw std::runtime_error("No Direct3D 12 device found"); } *ppAdapter = adapter.Detach(); diff --git a/Samples/Graphics/MeshletCull/DeviceResources.h b/Samples/Graphics/MeshletCull/DeviceResources.h index a920f13..0e0aeb0 100644 --- a/Samples/Graphics/MeshletCull/DeviceResources.h +++ b/Samples/Graphics/MeshletCull/DeviceResources.h @@ -22,9 +22,13 @@ namespace DX class DeviceResources { public: + static constexpr unsigned int c_ReverseDepth = 0x1; + static constexpr unsigned int c_AmplificationShaders = 0x2; + DeviceResources(DXGI_FORMAT backBufferFormat = DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT depthBufferFormat = DXGI_FORMAT_D32_FLOAT, - UINT backBufferCount = 2) noexcept(false); + UINT backBufferCount = 2, + unsigned int flags = 0) noexcept(false); ~DeviceResources(); DeviceResources(DeviceResources&&) = default; @@ -45,6 +49,9 @@ namespace DX void Suspend(); void Resume(); void WaitForGpu() noexcept; +#ifdef _GAMING_XBOX + void WaitForOrigin(); +#endif // Device Accessors. RECT GetOutputSize() const noexcept { return m_outputSize; } @@ -68,6 +75,7 @@ namespace DX D3D12_RECT GetScissorRect() const noexcept { return m_scissorRect; } UINT GetCurrentFrameIndex() const noexcept { return m_backBufferIndex; } UINT GetBackBufferCount() const noexcept { return m_backBufferCount; } + unsigned int GetDeviceOptions() const noexcept { return m_options; } CD3DX12_CPU_DESCRIPTOR_HANDLE GetRenderTargetView() const noexcept { @@ -81,10 +89,10 @@ namespace DX } private: - void MoveToNextFrame(); #ifdef _GAMING_XBOX void RegisterFrameEvents(); #else + void MoveToNextFrame(); void GetAdapter(IDXGIAdapter1** ppAdapter); #endif @@ -131,6 +139,9 @@ namespace DX D3D_FEATURE_LEVEL m_d3dFeatureLevel; RECT m_outputSize; + // DeviceResources options (see flags above) + unsigned int m_options; + // The IDeviceNotify can be held directly as it owns the DeviceResources. IDeviceNotify* m_deviceNotify; }; diff --git a/Samples/Graphics/MeshletCull/Main.cpp b/Samples/Graphics/MeshletCull/Main.cpp index 17d0897..93d2a4c 100644 --- a/Samples/Graphics/MeshletCull/Main.cpp +++ b/Samples/Graphics/MeshletCull/Main.cpp @@ -20,6 +20,13 @@ using namespace DirectX; +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wcovered-switch-default" +#pragma clang diagnostic ignored "-Wswitch-enum" +#endif + +#pragma warning(disable : 4061) + namespace { std::unique_ptr g_sample; @@ -27,11 +34,12 @@ namespace HANDLE g_plmSuspendComplete = nullptr; HANDLE g_plmSignalResume = nullptr; #endif -}; +} LPCWSTR g_szAppName = L"Meshlet Culling"; LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); +void ExitSample() noexcept; // Entry point int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) @@ -55,9 +63,10 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp if (hr == E_GAMERUNTIME_DLL_NOT_FOUND || hr == E_GAMERUNTIME_VERSION_MISMATCH) { #ifdef _GAMING_DESKTOP - (void)MessageBoxW(nullptr, L"Game Runtime is not installed on this system or needs updating.", g_szAppName, MB_ICONERROR | MB_OK); + std::ignore = MessageBoxW(nullptr, L"Game Runtime is not installed on this system or needs updating.", g_szAppName, MB_ICONERROR | MB_OK); #endif } + return 1; } #ifdef _GAMING_XBOX @@ -79,7 +88,7 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp wcex.lpfnWndProc = WndProc; wcex.hInstance = hInstance; wcex.hCursor = LoadCursorW(nullptr, IDC_ARROW); - wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); + wcex.hbrBackground = reinterpret_cast(COLOR_WINDOW + 1); wcex.lpszClassName = L"SimpleMeshShaderWindowClass"; if (!RegisterClassExW(&wcex)) return 1; @@ -133,7 +142,7 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp PostMessage(reinterpret_cast(context), WM_USER, 0, 0); // To defer suspend, you must wait to exit this callback - (void)WaitForSingleObject(g_plmSuspendComplete, INFINITE); + std::ignore = WaitForSingleObject(g_plmSuspendComplete, INFINITE); } else { @@ -163,20 +172,20 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp #ifdef _GAMING_XBOX UnregisterAppStateChangeNotification(hPLM); - + CloseHandle(g_plmSuspendComplete); CloseHandle(g_plmSignalResume); #endif XGameRuntimeUninitialize(); - return (int) msg.wParam; + return static_cast(msg.wParam); } // Windows procedure LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { -#ifndef _GAMING_XBOX +#ifdef _GAMING_DESKTOP static bool s_in_sizemove = false; static bool s_in_suspend = false; static bool s_minimized = false; @@ -231,7 +240,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) // Complete deferral SetEvent(g_plmSuspendComplete); - (void)WaitForSingleObject(g_plmSignalResume, INFINITE); + std::ignore = WaitForSingleObject(g_plmSignalResume, INFINITE); sample->OnResuming(); } @@ -245,7 +254,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) else { PAINTSTRUCT ps; - (void)BeginPaint(hWnd, &ps); + std::ignore = BeginPaint(hWnd, &ps); EndPaint(hWnd, &ps); } break; @@ -297,12 +306,13 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) break; case WM_GETMINMAXINFO: - { - auto info = reinterpret_cast(lParam); - info->ptMinTrackSize.x = 320; - info->ptMinTrackSize.y = 200; - } - break; + if (lParam) + { + auto info = reinterpret_cast(lParam); + info->ptMinTrackSize.x = 320; + info->ptMinTrackSize.y = 200; + } + break; case WM_POWERBROADCAST: switch (wParam) @@ -359,7 +369,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) } else { - SetWindowLongPtr(hWnd, GWL_STYLE, 0); + SetWindowLongPtr(hWnd, GWL_STYLE, WS_POPUP); SetWindowLongPtr(hWnd, GWL_EXSTYLE, WS_EX_TOPMOST); SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); @@ -387,7 +397,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) } // Exit helper -void ExitSample() +void ExitSample() noexcept { PostQuitMessage(0); } diff --git a/Samples/Graphics/MeshletCull/MeshletCull.cpp b/Samples/Graphics/MeshletCull/MeshletCull.cpp index 6cdc9d4..eb6b7b2 100644 --- a/Samples/Graphics/MeshletCull/MeshletCull.cpp +++ b/Samples/Graphics/MeshletCull/MeshletCull.cpp @@ -14,7 +14,7 @@ #include "Shared.h" -extern void ExitSample(); +extern void ExitSample() noexcept; using namespace ATG; using namespace DirectX; @@ -27,15 +27,15 @@ namespace //-------------------------------------- // Definitions - static const wchar_t* s_sampleTitle = L"Meshlet Cull"; + const wchar_t* s_sampleTitle = L"Meshlet Cull"; - static const wchar_t* s_meshletCullAsFilename = L"MeshletCullAS.cso"; - static const wchar_t* s_meshletDrawMsFilename = L"BasicMeshletMS.cso"; - static const wchar_t* s_basicdrawPsFilename = L"BasicMeshletPS.cso"; + const wchar_t* s_meshletCullAsFilename = L"MeshletCullAS.cso"; + const wchar_t* s_meshletDrawMsFilename = L"BasicMeshletMS.cso"; + const wchar_t* s_basicdrawPsFilename = L"BasicMeshletPS.cso"; - static const wchar_t* s_meshFilenames[] = + const wchar_t* s_meshFilenames[] = { -#if _GAMING_DESKTOP +#ifdef _GAMING_DESKTOP L"ATGDragon\\Dragon_LOD0.sdkmesh", L"ATGDragon\\Dragon_LOD1.sdkmesh", L"ATGDragon\\Dragon_LOD2.sdkmesh", @@ -184,9 +184,8 @@ namespace } -Sample::Sample() noexcept(false) - : m_deviceResources(std::make_unique()) - , m_displayWidth(0) +Sample::Sample() noexcept(false) + : m_displayWidth(0) , m_displayHeight(0) , m_frame(0) , m_countsData(nullptr) @@ -198,6 +197,9 @@ Sample::Sample() noexcept(false) , m_highlightedIndex(uint32_t(-1)) , m_selectedIndex(uint32_t(-1)) { + m_deviceResources = std::make_unique( + DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_D32_FLOAT, 2, + DX::DeviceResources::c_AmplificationShaders); m_deviceResources->RegisterDeviceNotify(this); } @@ -248,6 +250,10 @@ void Sample::Update(DX::StepTimer const& timer) { PIXBeginEvent(PIX_COLOR_DEFAULT, L"Update"); +#ifdef _GAMING_XBOX + m_deviceResources->WaitForOrigin(); +#endif + // Input-agnostic app controls struct AppControls { @@ -487,16 +493,16 @@ void Sample::Clear() PIXBeginEvent(commandList, PIX_COLOR_DEFAULT, L"Clear"); // Clear the views. - auto rtvDescriptor = m_deviceResources->GetRenderTargetView(); - auto dsvDescriptor = m_deviceResources->GetDepthStencilView(); + auto const rtvDescriptor = m_deviceResources->GetRenderTargetView(); + auto const dsvDescriptor = m_deviceResources->GetDepthStencilView(); commandList->OMSetRenderTargets(1, &rtvDescriptor, FALSE, &dsvDescriptor); commandList->ClearRenderTargetView(rtvDescriptor, ATG::Colors::Background, 0, nullptr); commandList->ClearDepthStencilView(dsvDescriptor, D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr); // Set the viewport and scissor rect. - auto viewport = m_deviceResources->GetScreenViewport(); - auto scissorRect = m_deviceResources->GetScissorRect(); + auto const viewport = m_deviceResources->GetScreenViewport(); + auto const scissorRect = m_deviceResources->GetScissorRect(); commandList->RSSetViewports(1, &viewport); commandList->RSSetScissorRects(1, &scissorRect); @@ -713,7 +719,7 @@ void Sample::OnResuming() void Sample::OnWindowMoved() { - auto r = m_deviceResources->GetOutputSize(); + auto const r = m_deviceResources->GetOutputSize(); m_deviceResources->WindowSizeChanged(r.right, r.bottom); } @@ -737,7 +743,7 @@ void Sample::GetDefaultSize(int& width, int& height) const // These are the resources that depend on the device. void Sample::CreateDeviceDependentResources() { - auto device = static_cast(m_deviceResources->GetD3DDevice()); + auto device = m_deviceResources->GetD3DDevice(); // Check for Shader Model 6.5 and Mesh Shader feature support #ifdef _GAMING_DESKTOP @@ -973,7 +979,7 @@ void Sample::CreateDeviceDependentResources() // Allocate all memory resources that change on a window SizeChanged event. void Sample::CreateWindowSizeDependentResources() { - RECT size = m_deviceResources->GetOutputSize(); + auto const size = m_deviceResources->GetOutputSize(); m_displayWidth = static_cast(size.right - size.left); m_displayHeight = static_cast(size.bottom - size.top); @@ -1069,7 +1075,7 @@ void Sample::Pick() auto& mesh = model.Model->meshes[0]->opaqueMeshParts[0]; assert(mesh->vbDecl != nullptr); - uint8_t* vbMem = static_cast(mesh->vertexBuffer.Memory()); + auto vbMem = static_cast(mesh->vertexBuffer.Memory()); uint32_t stride = mesh->vertexStride; uint32_t offset = ComputeSemanticByteOffset(*mesh->vbDecl, "SV_Position"); assert(offset != uint32_t(-1)); diff --git a/Samples/Graphics/MeshletCull/StepTimer.h b/Samples/Graphics/MeshletCull/StepTimer.h index bb15fff..2dbefcb 100644 --- a/Samples/Graphics/MeshletCull/StepTimer.h +++ b/Samples/Graphics/MeshletCull/StepTimer.h @@ -111,7 +111,7 @@ namespace DX timeDelta *= TicksPerSecond; timeDelta /= static_cast(m_qpcFrequency.QuadPart); - uint32_t lastFrameCount = m_frameCount; + const uint32_t lastFrameCount = m_frameCount; if (m_isFixedTimeStep) { diff --git a/Samples/Graphics/MeshletCull/pch.h b/Samples/Graphics/MeshletCull/pch.h index a424700..487f495 100644 --- a/Samples/Graphics/MeshletCull/pch.h +++ b/Samples/Graphics/MeshletCull/pch.h @@ -41,8 +41,8 @@ #include -#if _GRDK_VER < 0x4A610D2B /* GXDK Edition 200600 */ -#error This sample requires the June 2020 GDK or later +#if _GRDK_VER < 0x55F007B0 /* GDK Edition 211000 */ +#error This sample requires the October 2021 GDK or later #endif #ifdef _GAMING_XBOX_SCARLETT @@ -68,14 +68,20 @@ #include #include +#include #include +#include #include +#include +#include +#include #include +#include #include #include - -#include -#include +#include +#include +#include #ifdef _GAMING_XBOX #include @@ -159,9 +165,9 @@ namespace DX class com_exception : public std::exception { public: - com_exception(HRESULT hr) : result(hr) {} + com_exception(HRESULT hr) noexcept : result(hr) {} - virtual const char* what() const override + const char* what() const override { static char s_str[64] = {}; sprintf_s(s_str, "Failure with HRESULT of %08X", static_cast(result)); @@ -177,6 +183,12 @@ namespace DX { if (FAILED(hr)) { +#ifdef _DEBUG + char str[64] = {}; + sprintf_s(str, "**ERROR** Fatal Error with HRESULT of %08X\n", static_cast(hr)); + OutputDebugStringA(str); + __debugbreak(); +#endif throw com_exception(hr); } } diff --git a/Samples/Graphics/MeshletInstancing/DeviceResources.cpp b/Samples/Graphics/MeshletInstancing/DeviceResources.cpp index 7d88350..2b40774 100644 --- a/Samples/Graphics/MeshletInstancing/DeviceResources.cpp +++ b/Samples/Graphics/MeshletInstancing/DeviceResources.cpp @@ -12,7 +12,6 @@ using namespace DX; using Microsoft::WRL::ComPtr; -#ifdef _GAMING_DESKTOP #ifdef __clang__ #pragma clang diagnostic ignored "-Wcovered-switch-default" #pragma clang diagnostic ignored "-Wswitch-enum" @@ -20,6 +19,7 @@ using Microsoft::WRL::ComPtr; #pragma warning(disable : 4061) +#ifdef _GAMING_DESKTOP namespace { inline DXGI_FORMAT NoSRGB(DXGI_FORMAT fmt) noexcept @@ -39,7 +39,8 @@ namespace DeviceResources::DeviceResources( DXGI_FORMAT backBufferFormat, DXGI_FORMAT depthBufferFormat, - UINT backBufferCount) noexcept(false) : + UINT backBufferCount, + unsigned int flags) noexcept(false) : m_backBufferIndex(0), m_fenceValues{}, #ifdef _GAMING_XBOX @@ -54,6 +55,7 @@ DeviceResources::DeviceResources( m_window(nullptr), m_d3dFeatureLevel(D3D_FEATURE_LEVEL_11_0), m_outputSize{ 0, 0, 1, 1 }, + m_options(flags), m_deviceNotify(nullptr) { if (backBufferCount < 2 || backBufferCount > MAX_BACK_BUFFER_COUNT) @@ -72,7 +74,7 @@ DeviceResources::~DeviceResources() // Ensure we present a blank screen before cleaning up resources. if (m_commandQueue) { - (void)m_commandQueue->PresentX(0, nullptr, nullptr); + std::ignore = m_commandQueue->PresentX(0, nullptr, nullptr); } #endif } @@ -98,6 +100,14 @@ void DeviceResources::CreateDeviceResources() params.GraphicsScratchMemorySizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); params.ComputeScratchMemorySizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); +#if defined(_GAMING_XBOX_SCARLETT) && (_GRDK_VER >= 0x585D070E /* GXDK Edition 221000 */) + if (m_options & c_AmplificationShaders) + { + params.AmplificationShaderIndirectArgsBufferSize = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); + params.AmplificationShaderPayloadBufferSize = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); + } +#endif + HRESULT hr = D3D12XboxCreateDevice( nullptr, ¶ms, @@ -150,7 +160,7 @@ void DeviceResources::CreateDeviceResources() 80 /* IDXGISwapChain::GetContainingOutput: The swapchain's adapter does not control the output on which the swapchain's window resides. */, }; DXGI_INFO_QUEUE_FILTER filter = {}; - filter.DenyList.NumIDs = _countof(hide); + filter.DenyList.NumIDs = static_cast(std::size(hide)); filter.DenyList.pIDList = hide; dxgiInfoQueue->AddStorageFilterEntries(DXGI_DEBUG_DXGI, &filter); } @@ -194,9 +204,11 @@ void DeviceResources::CreateDeviceResources() // Workarounds for debug layer issues on hybrid-graphics systems D3D12_MESSAGE_ID_EXECUTECOMMANDLISTS_WRONGSWAPCHAINBUFFERREFERENCE, D3D12_MESSAGE_ID_RESOURCE_BARRIER_MISMATCHING_COMMAND_LIST_TYPE, + // Workaround for debug layer issues with mesh shader validation. + static_cast(1323) /* D3D12_MESSAGE_ID_CREATEMESHSHADER_TOPOLOGY_MISMATCH */, }; D3D12_INFO_QUEUE_FILTER filter = {}; - filter.DenyList.NumIDs = _countof(hide); + filter.DenyList.NumIDs = static_cast(std::size(hide)); filter.DenyList.pIDList = hide; d3dInfoQueue->AddStorageFilterEntries(&filter); } @@ -205,6 +217,9 @@ void DeviceResources::CreateDeviceResources() // Determine maximum supported feature level for this device static const D3D_FEATURE_LEVEL s_featureLevels[] = { +#if defined(NTDDI_WIN10_FE) && (NTDDI_VERSION >= NTDDI_WIN10_FE) + D3D_FEATURE_LEVEL_12_2, +#endif D3D_FEATURE_LEVEL_12_1, D3D_FEATURE_LEVEL_12_0, D3D_FEATURE_LEVEL_11_1, @@ -213,7 +228,7 @@ void DeviceResources::CreateDeviceResources() D3D12_FEATURE_DATA_FEATURE_LEVELS featLevels = { - _countof(s_featureLevels), s_featureLevels, D3D_FEATURE_LEVEL_11_0 + static_cast(std::size(s_featureLevels)), s_featureLevels, D3D_FEATURE_LEVEL_11_0 }; hr = m_d3dDevice->CheckFeatureSupport(D3D12_FEATURE_FEATURE_LEVELS, &featLevels, sizeof(featLevels)); @@ -284,7 +299,7 @@ void DeviceResources::CreateDeviceResources() m_fenceEvent.Attach(CreateEventEx(nullptr, nullptr, 0, EVENT_MODIFY_STATE | SYNCHRONIZE)); if (!m_fenceEvent.IsValid()) { - throw std::exception("CreateEvent"); + throw std::system_error(std::error_code(static_cast(GetLastError()), std::system_category()), "CreateEventEx"); } #ifdef _GAMING_XBOX @@ -297,7 +312,7 @@ void DeviceResources::CreateWindowSizeDependentResources() { if (!m_window) { - throw std::exception("Call SetWindow with a valid window handle"); + throw std::logic_error("Call SetWindow with a valid Win32 window handle"); } // Wait until all previous GPU work is complete. @@ -323,7 +338,7 @@ void DeviceResources::CreateWindowSizeDependentResources() // Obtain the back buffers for this window which will be the final render targets // and create render target views for each of them. - CD3DX12_HEAP_PROPERTIES swapChainHeapProperties(D3D12_HEAP_TYPE_DEFAULT); + const CD3DX12_HEAP_PROPERTIES swapChainHeapProperties(D3D12_HEAP_TYPE_DEFAULT); D3D12_RESOURCE_DESC swapChainBufferDesc = CD3DX12_RESOURCE_DESC::Tex2D( m_backBufferFormat, @@ -355,7 +370,7 @@ void DeviceResources::CreateWindowSizeDependentResources() rtvDesc.Format = m_backBufferFormat; rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; - CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( + const CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( m_rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), static_cast(n), m_rtvDescriptorSize); m_d3dDevice->CreateRenderTargetView(m_renderTargets[n].Get(), &rtvDesc, rtvDescriptor); @@ -366,7 +381,7 @@ void DeviceResources::CreateWindowSizeDependentResources() #else // _GAMING_DESKTOP - DXGI_FORMAT backBufferFormat = NoSRGB(m_backBufferFormat); + const DXGI_FORMAT backBufferFormat = NoSRGB(m_backBufferFormat); // If the swap chain already exists, resize it, otherwise create one. if (m_swapChain) @@ -449,7 +464,7 @@ void DeviceResources::CreateWindowSizeDependentResources() rtvDesc.Format = m_backBufferFormat; rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; - CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( + const CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( m_rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), static_cast(n), m_rtvDescriptorSize); m_d3dDevice->CreateRenderTargetView(m_renderTargets[n].Get(), &rtvDesc, rtvDescriptor); @@ -464,7 +479,7 @@ void DeviceResources::CreateWindowSizeDependentResources() { // Allocate a 2-D surface as the depth/stencil buffer and create a depth/stencil view // on this surface. - CD3DX12_HEAP_PROPERTIES depthHeapProperties(D3D12_HEAP_TYPE_DEFAULT); + const CD3DX12_HEAP_PROPERTIES depthHeapProperties(D3D12_HEAP_TYPE_DEFAULT); D3D12_RESOURCE_DESC depthStencilDesc = CD3DX12_RESOURCE_DESC::Tex2D( m_depthBufferFormat, @@ -477,7 +492,7 @@ void DeviceResources::CreateWindowSizeDependentResources() D3D12_CLEAR_VALUE depthOptimizedClearValue = {}; depthOptimizedClearValue.Format = m_depthBufferFormat; - depthOptimizedClearValue.DepthStencil.Depth = 1.0f; + depthOptimizedClearValue.DepthStencil.Depth = (m_options & c_ReverseDepth) ? 0.0f : 1.0f; depthOptimizedClearValue.DepthStencil.Stencil = 0; ThrowIfFailed(m_d3dDevice->CreateCommittedResource( @@ -588,12 +603,6 @@ void DeviceResources::HandleDeviceLost() // Prepare the command list and render target for rendering. void DeviceResources::Prepare(D3D12_RESOURCE_STATES beforeState, D3D12_RESOURCE_STATES afterState) { -#ifdef _GAMING_XBOX - // Wait until frame start is signaled - m_framePipelineToken = D3D12XBOX_FRAME_PIPELINE_TOKEN_NULL; - ThrowIfFailed(m_d3dDevice->WaitFrameEventX(D3D12XBOX_FRAME_EVENT_ORIGIN, INFINITE, nullptr, D3D12XBOX_WAIT_FRAME_EVENT_FLAG_NONE, &m_framePipelineToken)); -#endif - // Reset command list and allocator. ThrowIfFailed(m_commandAllocators[m_backBufferIndex]->Reset()); ThrowIfFailed(m_commandList->Reset(m_commandAllocators[m_backBufferIndex].Get(), nullptr)); @@ -601,7 +610,8 @@ void DeviceResources::Prepare(D3D12_RESOURCE_STATES beforeState, D3D12_RESOURCE_ if (beforeState != afterState) { // Transition the render target into the correct state to allow for drawing into it. - D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), + const D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition( + m_renderTargets[m_backBufferIndex].Get(), beforeState, afterState); m_commandList->ResourceBarrier(1, &barrier); } @@ -613,7 +623,9 @@ void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState) if (beforeState != D3D12_RESOURCE_STATE_PRESENT) { // Transition the render target to the state that allows it to be presented to the display. - D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), beforeState, D3D12_RESOURCE_STATE_PRESENT); + const D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition( + m_renderTargets[m_backBufferIndex].Get(), + beforeState, D3D12_RESOURCE_STATE_PRESENT); m_commandList->ResourceBarrier(1, &barrier); } @@ -633,9 +645,12 @@ void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState) m_commandQueue->PresentX(1, &planeParameters, nullptr) ); - // Xbox One apps do not need to handle DXGI_ERROR_DEVICE_REMOVED or DXGI_ERROR_DEVICE_RESET. + // Xbox apps do not need to handle DXGI_ERROR_DEVICE_REMOVED or DXGI_ERROR_DEVICE_RESET. -#else + // Update the back buffer index. + m_backBufferIndex = (m_backBufferIndex + 1) % m_backBufferCount; + +#else // _GAMING_DESKTOP // The first argument instructs DXGI to block until VSync, putting the application // to sleep until the next VSync. This ensures we don't waste any cycles rendering @@ -658,9 +673,8 @@ void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState) ThrowIfFailed(hr); } -#endif - MoveToNextFrame(); +#endif } // Handle GPU suspend/resume @@ -686,13 +700,13 @@ void DeviceResources::WaitForGpu() noexcept if (m_commandQueue && m_fence && m_fenceEvent.IsValid()) { // Schedule a Signal command in the GPU queue. - UINT64 fenceValue = m_fenceValues[m_backBufferIndex]; + const UINT64 fenceValue = m_fenceValues[m_backBufferIndex]; if (SUCCEEDED(m_commandQueue->Signal(m_fence.Get(), fenceValue))) { // Wait until the Signal has been processed. if (SUCCEEDED(m_fence->SetEventOnCompletion(fenceValue, m_fenceEvent.Get()))) { - WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); + std::ignore = WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); // Increment the fence value for the current frame. m_fenceValues[m_backBufferIndex]++; @@ -701,32 +715,20 @@ void DeviceResources::WaitForGpu() noexcept } } -// Prepare to render the next frame. -void DeviceResources::MoveToNextFrame() -{ - // Schedule a Signal command in the queue. - const UINT64 currentFenceValue = m_fenceValues[m_backBufferIndex]; - ThrowIfFailed(m_commandQueue->Signal(m_fence.Get(), currentFenceValue)); - - // Update the back buffer index. #ifdef _GAMING_XBOX - m_backBufferIndex = (m_backBufferIndex + 1) % m_backBufferCount; -#else - m_backBufferIndex = m_swapChain->GetCurrentBackBufferIndex(); -#endif - - // If the next frame is not ready to be rendered yet, wait until it is ready. - if (m_fence->GetCompletedValue() < m_fenceValues[m_backBufferIndex]) - { - ThrowIfFailed(m_fence->SetEventOnCompletion(m_fenceValues[m_backBufferIndex], m_fenceEvent.Get())); - WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); - } - - // Set the fence value for the next frame. - m_fenceValues[m_backBufferIndex] = currentFenceValue + 1; +// For PresentX rendering, we should wait for the origin event just before processing input. +void DeviceResources::WaitForOrigin() +{ + // Wait until frame start is signaled + m_framePipelineToken = D3D12XBOX_FRAME_PIPELINE_TOKEN_NULL; + ThrowIfFailed(m_d3dDevice->WaitFrameEventX( + D3D12XBOX_FRAME_EVENT_ORIGIN, + INFINITE, + nullptr, + D3D12XBOX_WAIT_FRAME_EVENT_FLAG_NONE, + &m_framePipelineToken)); } -#ifdef _GAMING_XBOX // Set frame interval and register for frame events void DeviceResources::RegisterFrameEvents() { @@ -755,7 +757,30 @@ void DeviceResources::RegisterFrameEvents() nullptr, D3D12XBOX_SCHEDULE_FRAME_EVENT_FLAG_NONE)); } -#else + +#else // _GAMING_DESKTOP + +// Prepare to render the next frame. +void DeviceResources::MoveToNextFrame() +{ + // Schedule a Signal command in the queue. + const UINT64 currentFenceValue = m_fenceValues[m_backBufferIndex]; + ThrowIfFailed(m_commandQueue->Signal(m_fence.Get(), currentFenceValue)); + + // Update the back buffer index. + m_backBufferIndex = m_swapChain->GetCurrentBackBufferIndex(); + + // If the next frame is not ready to be rendered yet, wait until it is ready. + if (m_fence->GetCompletedValue() < m_fenceValues[m_backBufferIndex]) + { + ThrowIfFailed(m_fence->SetEventOnCompletion(m_fenceValues[m_backBufferIndex], m_fenceEvent.Get())); + std::ignore = WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); + } + + // Set the fence value for the next frame. + m_fenceValues[m_backBufferIndex] = currentFenceValue + 1; +} + // This method acquires the first available hardware adapter that supports Direct3D 12. // If no such adapter can be found, try WARP. Otherwise throw an exception. void DeviceResources::GetAdapter(IDXGIAdapter1** ppAdapter) @@ -797,7 +822,7 @@ void DeviceResources::GetAdapter(IDXGIAdapter1** ppAdapter) // Try WARP12 instead if (FAILED(m_dxgiFactory->EnumWarpAdapter(IID_PPV_ARGS(adapter.ReleaseAndGetAddressOf())))) { - throw std::exception("WARP12 not available. Enable the 'Graphics Tools' optional feature"); + throw std::runtime_error("WARP12 not available. Enable the 'Graphics Tools' optional feature"); } OutputDebugStringA("Direct3D Adapter - WARP12\n"); @@ -806,7 +831,7 @@ void DeviceResources::GetAdapter(IDXGIAdapter1** ppAdapter) if (!adapter) { - throw std::exception("No Direct3D 12 device found"); + throw std::runtime_error("No Direct3D 12 device found"); } *ppAdapter = adapter.Detach(); diff --git a/Samples/Graphics/MeshletInstancing/DeviceResources.h b/Samples/Graphics/MeshletInstancing/DeviceResources.h index a920f13..0e0aeb0 100644 --- a/Samples/Graphics/MeshletInstancing/DeviceResources.h +++ b/Samples/Graphics/MeshletInstancing/DeviceResources.h @@ -22,9 +22,13 @@ namespace DX class DeviceResources { public: + static constexpr unsigned int c_ReverseDepth = 0x1; + static constexpr unsigned int c_AmplificationShaders = 0x2; + DeviceResources(DXGI_FORMAT backBufferFormat = DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT depthBufferFormat = DXGI_FORMAT_D32_FLOAT, - UINT backBufferCount = 2) noexcept(false); + UINT backBufferCount = 2, + unsigned int flags = 0) noexcept(false); ~DeviceResources(); DeviceResources(DeviceResources&&) = default; @@ -45,6 +49,9 @@ namespace DX void Suspend(); void Resume(); void WaitForGpu() noexcept; +#ifdef _GAMING_XBOX + void WaitForOrigin(); +#endif // Device Accessors. RECT GetOutputSize() const noexcept { return m_outputSize; } @@ -68,6 +75,7 @@ namespace DX D3D12_RECT GetScissorRect() const noexcept { return m_scissorRect; } UINT GetCurrentFrameIndex() const noexcept { return m_backBufferIndex; } UINT GetBackBufferCount() const noexcept { return m_backBufferCount; } + unsigned int GetDeviceOptions() const noexcept { return m_options; } CD3DX12_CPU_DESCRIPTOR_HANDLE GetRenderTargetView() const noexcept { @@ -81,10 +89,10 @@ namespace DX } private: - void MoveToNextFrame(); #ifdef _GAMING_XBOX void RegisterFrameEvents(); #else + void MoveToNextFrame(); void GetAdapter(IDXGIAdapter1** ppAdapter); #endif @@ -131,6 +139,9 @@ namespace DX D3D_FEATURE_LEVEL m_d3dFeatureLevel; RECT m_outputSize; + // DeviceResources options (see flags above) + unsigned int m_options; + // The IDeviceNotify can be held directly as it owns the DeviceResources. IDeviceNotify* m_deviceNotify; }; diff --git a/Samples/Graphics/MeshletInstancing/Main.cpp b/Samples/Graphics/MeshletInstancing/Main.cpp index 390eddd..89278e8 100644 --- a/Samples/Graphics/MeshletInstancing/Main.cpp +++ b/Samples/Graphics/MeshletInstancing/Main.cpp @@ -20,6 +20,13 @@ using namespace DirectX; +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wcovered-switch-default" +#pragma clang diagnostic ignored "-Wswitch-enum" +#endif + +#pragma warning(disable : 4061) + namespace { std::unique_ptr g_sample; @@ -27,11 +34,12 @@ namespace HANDLE g_plmSuspendComplete = nullptr; HANDLE g_plmSignalResume = nullptr; #endif -}; +} LPCWSTR g_szAppName = L"Meshlet Instancing"; LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); +void ExitSample() noexcept; // Entry point int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) @@ -55,7 +63,7 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp if (hr == E_GAMERUNTIME_DLL_NOT_FOUND || hr == E_GAMERUNTIME_VERSION_MISMATCH) { #ifdef _GAMING_DESKTOP - (void)MessageBoxW(nullptr, L"Game Runtime is not installed on this system or needs updating.", g_szAppName, MB_ICONERROR | MB_OK); + std::ignore = MessageBoxW(nullptr, L"Game Runtime is not installed on this system or needs updating.", g_szAppName, MB_ICONERROR | MB_OK); #endif } return 1; @@ -80,7 +88,7 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp wcex.lpfnWndProc = WndProc; wcex.hInstance = hInstance; wcex.hCursor = LoadCursorW(nullptr, IDC_ARROW); - wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); + wcex.hbrBackground = reinterpret_cast(COLOR_WINDOW + 1); wcex.lpszClassName = L"MeshletInstancingWindowClass"; if (!RegisterClassExW(&wcex)) return 1; @@ -134,7 +142,7 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp PostMessage(reinterpret_cast(context), WM_USER, 0, 0); // To defer suspend, you must wait to exit this callback - (void)WaitForSingleObject(g_plmSuspendComplete, INFINITE); + std::ignore = WaitForSingleObject(g_plmSuspendComplete, INFINITE); } else { @@ -164,14 +172,14 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp #ifdef _GAMING_XBOX UnregisterAppStateChangeNotification(hPLM); - + CloseHandle(g_plmSuspendComplete); CloseHandle(g_plmSignalResume); #endif XGameRuntimeUninitialize(); - return (int) msg.wParam; + return static_cast(msg.wParam); } // Windows procedure @@ -232,7 +240,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) // Complete deferral SetEvent(g_plmSuspendComplete); - (void)WaitForSingleObject(g_plmSignalResume, INFINITE); + std::ignore = WaitForSingleObject(g_plmSignalResume, INFINITE); sample->OnResuming(); } @@ -246,7 +254,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) else { PAINTSTRUCT ps; - (void)BeginPaint(hWnd, &ps); + std::ignore = BeginPaint(hWnd, &ps); EndPaint(hWnd, &ps); } break; @@ -298,12 +306,13 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) break; case WM_GETMINMAXINFO: - { - auto info = reinterpret_cast(lParam); - info->ptMinTrackSize.x = 320; - info->ptMinTrackSize.y = 200; - } - break; + if (lParam) + { + auto info = reinterpret_cast(lParam); + info->ptMinTrackSize.x = 320; + info->ptMinTrackSize.y = 200; + } + break; case WM_POWERBROADCAST: switch (wParam) @@ -360,7 +369,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) } else { - SetWindowLongPtr(hWnd, GWL_STYLE, 0); + SetWindowLongPtr(hWnd, GWL_STYLE, WS_POPUP); SetWindowLongPtr(hWnd, GWL_EXSTYLE, WS_EX_TOPMOST); SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); @@ -388,7 +397,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) } // Exit helper -void ExitSample() +void ExitSample() noexcept { PostQuitMessage(0); } diff --git a/Samples/Graphics/MeshletInstancing/MeshletInstancing.cpp b/Samples/Graphics/MeshletInstancing/MeshletInstancing.cpp index 9f70c57..00c012a 100644 --- a/Samples/Graphics/MeshletInstancing/MeshletInstancing.cpp +++ b/Samples/Graphics/MeshletInstancing/MeshletInstancing.cpp @@ -14,7 +14,7 @@ #pragma warning( disable : 4324 4365 ) -extern void ExitSample(); +extern void ExitSample() noexcept; using namespace ATG; using namespace DirectX; @@ -33,9 +33,9 @@ namespace const wchar_t* s_meshShaderFilename = L"InstancedMeshletMS.cso"; const wchar_t* s_pixelShaderFilename = L"BasicMeshletPS.cso"; - static const wchar_t* s_lodFilenames[] = + const wchar_t* s_lodFilenames[] = { -#if _GAMING_DESKTOP +#ifdef _GAMING_DESKTOP L"ATGDragon\\Dragon_LOD0.sdkmesh", L"ATGDragon\\Dragon_LOD1.sdkmesh", L"ATGDragon\\Dragon_LOD2.sdkmesh", @@ -51,7 +51,7 @@ namespace L"Dragon_LOD5.sdkmesh", #endif }; - const uint32_t c_lodCount = _countof(s_lodFilenames); + constexpr uint32_t c_lodCount = _countof(s_lodFilenames); #ifdef _GAMING_XBOX_SCARLETT const uint32_t c_textureDataPitchAlign = D3D12XBOX_TEXTURE_DATA_PITCH_ALIGNMENT; @@ -85,7 +85,7 @@ namespace template size_t GetAlignedSize(T size) { - const size_t alignment = D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT; + constexpr size_t alignment = D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT; const size_t alignedSize = (size + alignment - 1) & ~(alignment - 1); return alignedSize; } @@ -109,8 +109,7 @@ namespace } Sample::Sample() noexcept(false) - : m_deviceResources(std::make_unique()) - , m_displayWidth(0) + : m_displayWidth(0) , m_displayHeight(0) , m_frame(0) , m_instMode(InstanceMode::IM_Line) @@ -120,6 +119,7 @@ Sample::Sample() noexcept(false) , m_updateInstances(false) , m_renderHelp(false) { + m_deviceResources = std::make_unique(); m_deviceResources->RegisterDeviceNotify(this); } @@ -135,13 +135,15 @@ Sample::~Sample() void Sample::Initialize(HWND window, int width, int height) { m_gamePad = std::make_unique(); + m_keyboard = std::make_unique(); + m_mouse = std::make_unique(); m_mouse->SetWindow(window); m_deviceResources->SetWindow(window, width, height); - m_deviceResources->CreateDeviceResources(); + m_deviceResources->CreateDeviceResources(); CreateDeviceDependentResources(); m_deviceResources->CreateWindowSizeDependentResources(); @@ -154,6 +156,10 @@ void Sample::Tick() { PIXBeginEvent(PIX_COLOR_DEFAULT, L"Frame %llu", m_frame); +#ifdef _GAMING_XBOX + m_deviceResources->WaitForOrigin(); +#endif + m_timer.Tick([&]() { Update(m_timer); @@ -168,7 +174,6 @@ void Sample::Tick() // Updates the world. void Sample::Update(DX::StepTimer const& timer) { - PIXBeginEvent(PIX_COLOR_DEFAULT, L"Update"); float elapsedTime = float(timer.GetElapsedSeconds()); @@ -348,16 +353,16 @@ void Sample::Clear() PIXBeginEvent(commandList, PIX_COLOR_DEFAULT, L"Clear"); // Clear the views. - auto rtvDescriptor = m_deviceResources->GetRenderTargetView(); - auto dsvDescriptor = m_deviceResources->GetDepthStencilView(); + auto const rtvDescriptor = m_deviceResources->GetRenderTargetView(); + auto const dsvDescriptor = m_deviceResources->GetDepthStencilView(); commandList->OMSetRenderTargets(1, &rtvDescriptor, FALSE, &dsvDescriptor); commandList->ClearRenderTargetView(rtvDescriptor, ATG::Colors::Background, 0, nullptr); commandList->ClearDepthStencilView(dsvDescriptor, D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr); // Set the viewport and scissor rect. - auto viewport = m_deviceResources->GetScreenViewport(); - auto scissorRect = m_deviceResources->GetScissorRect(); + auto const viewport = m_deviceResources->GetScreenViewport(); + auto const scissorRect = m_deviceResources->GetScissorRect(); commandList->RSSetViewports(1, &viewport); commandList->RSSetScissorRects(1, &scissorRect); @@ -397,7 +402,7 @@ void Sample::UpdateConstants(ID3D12GraphicsCommandList* commandList) void Sample::DrawHUD(ID3D12GraphicsCommandList* commandList) { - const wchar_t* c_renderModeNames[] = + static const wchar_t* c_renderModeNames[] = { L"Flat", L"Meshlets" @@ -407,7 +412,7 @@ void Sample::DrawHUD(ID3D12GraphicsCommandList* commandList) m_hudBatch->Begin(commandList); - auto safe = SimpleMath::Viewport::ComputeTitleSafeArea(m_displayWidth, m_displayHeight); + auto const safe = SimpleMath::Viewport::ComputeTitleSafeArea(m_displayWidth, m_displayHeight); // Draw a text underlay box m_hudBatch->Draw(m_srvPile->GetGpuHandle(SRV_WhiteTexture), { 1, 1 }, RECT{ safe.left - 10, safe.top - 10, safe.left + 335, safe.top + 270 }); @@ -508,7 +513,7 @@ void Sample::OnResuming() void Sample::OnWindowMoved() { - auto r = m_deviceResources->GetOutputSize(); + auto const r = m_deviceResources->GetOutputSize(); m_deviceResources->WindowSizeChanged(r.right, r.bottom); } @@ -521,7 +526,7 @@ void Sample::OnWindowSizeChanged(int width, int height) } // Properties -void Sample::GetDefaultSize(int& width, int& height) const +void Sample::GetDefaultSize(int& width, int& height) const noexcept { width = 1280; height = 720; @@ -556,11 +561,9 @@ void Sample::CreateDeviceDependentResources() // Instantiate manager objects m_graphicsMemory = std::make_unique(device); m_gpuTimer = std::make_unique(device, m_deviceResources->GetCommandQueue()); - m_controlHelp = std::make_unique(L"Meshlet Instancing", nullptr, c_buttonAssignment, _countof(c_buttonAssignment)); + m_controlHelp = std::make_unique(L"Meshlet Instancing", nullptr, c_buttonAssignment, std::size(c_buttonAssignment)); m_srvPile = std::make_unique(device, - D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, - D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, 128, DescriptorHeapIndex::SRV_Count); @@ -599,7 +602,7 @@ void Sample::CreateDeviceDependentResources() // Create GPU resources for various purposes { // Create constant resources - auto defaultHeap = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT); + const CD3DX12_HEAP_PROPERTIES defaultHeap(D3D12_HEAP_TYPE_DEFAULT); auto cbDesc = CD3DX12_RESOURCE_DESC::Buffer(GetAlignedSize(sizeof(Constants))); DX::ThrowIfFailed(device->CreateCommittedResource( @@ -626,13 +629,13 @@ void Sample::CreateDeviceDependentResources() } // Load the model LODs and mesh data - m_lods.resize(_countof(s_lodFilenames)); - m_primCounts.resize(_countof(s_lodFilenames)); + m_lods.resize(std::size(s_lodFilenames)); + m_primCounts.resize(std::size(s_lodFilenames)); for (size_t i = 0; i < m_lods.size(); ++i) { - wchar_t filepath[256]; - DX::FindMediaFile(filepath, _countof(filepath), s_lodFilenames[i]); + wchar_t filepath[_MAX_PATH] = {}; + DX::FindMediaFile(filepath, _MAX_PATH, s_lodFilenames[i]); m_lods[i].Model = Model::CreateFromSDKMESH(device, filepath); @@ -675,7 +678,7 @@ void Sample::CreateDeviceDependentResources() } // Create our HUD objects - auto backBufferRts = RenderTargetState(m_deviceResources->GetBackBufferFormat(), m_deviceResources->GetDepthBufferFormat()); + const RenderTargetState backBufferRts(m_deviceResources->GetBackBufferFormat(), m_deviceResources->GetDepthBufferFormat()); m_controlHelp->RestoreDevice(device, resourceUpload, backBufferRts); auto spritePSD = SpriteBatchPipelineStateDescription(backBufferRts, &CommonStates::AlphaBlend); @@ -715,7 +718,7 @@ void Sample::CreateDeviceDependentResources() // Allocate all memory resources that change on a window SizeChanged event. void Sample::CreateWindowSizeDependentResources() { - RECT size = m_deviceResources->GetOutputSize(); + auto const size = m_deviceResources->GetOutputSize(); m_displayWidth = size.right - size.left; m_displayHeight = size.bottom - size.top; @@ -766,7 +769,7 @@ void Sample::RegenerateInstances() m_updateInstances = true; const float radius = m_lods[m_lodIndex].Model->meshes[0]->boundingSphere.Radius; - const float padding = 0.5f; + constexpr float padding = 0.5f; const float spacing = (1.0f + padding) * radius; // Determine our instancing mode diff --git a/Samples/Graphics/MeshletInstancing/MeshletInstancing.h b/Samples/Graphics/MeshletInstancing/MeshletInstancing.h index 15a0651..0c9c75a 100644 --- a/Samples/Graphics/MeshletInstancing/MeshletInstancing.h +++ b/Samples/Graphics/MeshletInstancing/MeshletInstancing.h @@ -25,6 +25,12 @@ public: Sample() noexcept(false); ~Sample(); + Sample(Sample&&) = default; + Sample& operator= (Sample&&) = default; + + Sample(Sample const&) = delete; + Sample& operator= (Sample const&) = delete; + // Initialization and management void Initialize(HWND window, int width, int height); @@ -44,7 +50,7 @@ public: void OnWindowSizeChanged(int width, int height); // Properties - void GetDefaultSize(int& width, int& height) const; + void GetDefaultSize(int& width, int& height) const noexcept; private: void Update(DX::StepTimer const& timer); diff --git a/Samples/Graphics/MeshletInstancing/ReadMe.docx b/Samples/Graphics/MeshletInstancing/ReadMe.docx index cdb14351365c92d6ba2a7766cfcb559e9107737c..aadf172d046b3cb9d42fd61d77c52ba6c08b5fc4 100644 GIT binary patch delta 31179 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 j{(m_qpcFrequency.QuadPart); - uint32_t lastFrameCount = m_frameCount; + const uint32_t lastFrameCount = m_frameCount; if (m_isFixedTimeStep) { diff --git a/Samples/Graphics/MeshletInstancing/pch.h b/Samples/Graphics/MeshletInstancing/pch.h index ced9b03..7c19e00 100644 --- a/Samples/Graphics/MeshletInstancing/pch.h +++ b/Samples/Graphics/MeshletInstancing/pch.h @@ -41,8 +41,8 @@ #include -#if _GRDK_VER < 0x4A610D2B /* GXDK Edition 200600 */ -#error This sample requires the June 2020 GDK or later +#if _GRDK_VER < 0x55F007B0 /* GDK Edition 211000 */ +#error This sample requires the October 2021 GDK or later #endif #ifdef _GAMING_XBOX_SCARLETT @@ -68,17 +68,23 @@ #include #include +#include #include +#include #include +#include +#include +#include #include +#include #include #include #include #include #include - -#include -#include +#include +#include +#include #ifdef _GAMING_XBOX #include @@ -91,28 +97,29 @@ #include #include "CommonStates.h" -#include "ControllerFont.h" -#include "ControllerHelp.h" #include "DescriptorHeap.h" #include "DirectXHelpers.h" -#include "FlyCamera.h" #include "GamePad.h" #include "GraphicsMemory.h" #include "Keyboard.h" -#include "Meshlet.h" #include "Model.h" #include "Mouse.h" +#include "RenderTargetState.h" +#include "ResourceUploadBatch.h" +#include "SimpleMath.h" +#include "SpriteBatch.h" +#include "SpriteFont.h" + +#include "ControllerFont.h" +#include "ControllerHelp.h" +#include "FlyCamera.h" +#include "Meshlet.h" #ifdef _GAMING_XBOX_SCARLETT #include "PerformanceTimersXbox.h" #else #include "PerformanceTimers.h" #endif #include "ReadData.h" -#include "RenderTargetState.h" -#include "ResourceUploadBatch.h" -#include "SimpleMath.h" -#include "SpriteBatch.h" -#include "SpriteFont.h" namespace DX { @@ -120,7 +127,7 @@ namespace DX class com_exception : public std::exception { public: - com_exception(HRESULT hr) : result(hr) {} + com_exception(HRESULT hr) noexcept : result(hr) {} const char* what() const override { diff --git a/Samples/Graphics/PointSprites/DeviceResources.cpp b/Samples/Graphics/PointSprites/DeviceResources.cpp index 12f2b99..fa67816 100644 --- a/Samples/Graphics/PointSprites/DeviceResources.cpp +++ b/Samples/Graphics/PointSprites/DeviceResources.cpp @@ -25,19 +25,19 @@ DeviceResources::DeviceResources( DXGI_FORMAT depthBufferFormat, UINT backBufferCount, unsigned int flags) noexcept(false) : - m_backBufferIndex(0), - m_fenceValues{}, - m_framePipelineToken{}, - m_rtvDescriptorSize(0), - m_screenViewport{}, - m_scissorRect{}, - m_backBufferFormat(backBufferFormat), - m_depthBufferFormat(depthBufferFormat), - m_backBufferCount(backBufferCount), - m_window(nullptr), - m_d3dFeatureLevel(D3D_FEATURE_LEVEL_12_0), - m_outputSize{ 0, 0, 1920, 1080 }, - m_options(flags) + m_backBufferIndex(0), + m_fenceValues{}, + m_framePipelineToken{}, + m_rtvDescriptorSize(0), + m_screenViewport{}, + m_scissorRect{}, + m_backBufferFormat(backBufferFormat), + m_depthBufferFormat(depthBufferFormat), + m_backBufferCount(backBufferCount), + m_window(nullptr), + m_d3dFeatureLevel(D3D_FEATURE_LEVEL_12_0), + m_outputSize{0, 0, 1920, 1080}, + m_options(flags) { if (backBufferCount < 2 || backBufferCount > MAX_BACK_BUFFER_COUNT) { @@ -54,12 +54,16 @@ DeviceResources::~DeviceResources() // Ensure we present a blank screen before cleaning up resources. if (m_commandQueue) { - (void)m_commandQueue->PresentX(0, nullptr, nullptr); + std::ignore = m_commandQueue->PresentX(0, nullptr, nullptr); } } // Configures the Direct3D device, and stores handles to it and the device context. +#ifdef _GAMING_XBOX_SCARLETT +void DeviceResources::CreateDeviceResources(D3D12XBOX_CREATE_DEVICE_FLAGS createDeviceFlags) +#else void DeviceResources::CreateDeviceResources() +#endif { // Create the DX12 API device object. D3D12XBOX_CREATE_DEVICE_PARAMETERS params = {}; @@ -76,6 +80,17 @@ void DeviceResources::CreateDeviceResources() params.GraphicsCommandQueueRingSizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); params.GraphicsScratchMemorySizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); params.ComputeScratchMemorySizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); +#ifdef _GAMING_XBOX_SCARLETT + params.CreateDeviceFlags = createDeviceFlags; + +#if (_GXDK_VER >= 0x585D070E /* GXDK Edition 221000 */) + if (m_options & c_AmplificationShaders) + { + params.AmplificationShaderIndirectArgsBufferSize = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); + params.AmplificationShaderPayloadBufferSize = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); + } +#endif +#endif HRESULT hr = D3D12XboxCreateDevice( nullptr, @@ -151,31 +166,46 @@ void DeviceResources::CreateDeviceResources() m_fenceEvent.Attach(CreateEventEx(nullptr, nullptr, 0, EVENT_MODIFY_STATE | SYNCHRONIZE)); if (!m_fenceEvent.IsValid()) { - throw std::exception("CreateEvent"); + throw std::system_error(std::error_code(static_cast(GetLastError()), std::system_category()), "CreateEventEx"); } - if (m_options & c_Enable4K_UHD) + if (m_options & (c_Enable4K_UHD | c_EnableQHD)) { switch (XSystemGetDeviceType()) { case XSystemDeviceType::XboxOne: case XSystemDeviceType::XboxOneS: - case XSystemDeviceType::XboxScarlettLockhart /* Xbox Series S */: - m_options &= ~c_Enable4K_UHD; -#ifdef _DEBUG - OutputDebugStringA("INFO: Swapchain using 1080p (1920 x 1080)\n"); -#endif + m_options &= ~(c_Enable4K_UHD | c_EnableQHD); break; + case XSystemDeviceType::XboxScarlettLockhart /* Xbox Series S */: + m_options &= ~c_Enable4K_UHD; + if (m_options & c_EnableQHD) + { + m_outputSize = { 0, 0, 2560, 1440 }; + } + break; + + case XSystemDeviceType::XboxScarlettAnaconda /* Xbox Series X */: + case XSystemDeviceType::XboxOneXDevkit: + case XSystemDeviceType::XboxScarlettDevkit: default: - m_outputSize = { 0, 0, 3840, 2160 }; -#ifdef _DEBUG - OutputDebugStringA("INFO: Swapchain using 4k (3840 x 2160)\n"); -#endif + m_outputSize = (m_options & c_Enable4K_UHD) ? RECT{ 0, 0, 3840, 2160 } : RECT{ 0, 0, 2560, 1440 }; break; } } +#ifdef _DEBUG + const char* info = nullptr; + switch (m_outputSize.bottom) + { + case 2160: info = "INFO: Swapchain using 4k (3840 x 2160)\n"; break; + case 1440: info = "INFO: Swapchain using 1440p (2560 x 1440)\n"; break; + default: info = "INFO: Swapchain using 1080p (1920 x 1080)\n"; break; + } + OutputDebugStringA(info); +#endif + RegisterFrameEvents(); } @@ -184,7 +214,7 @@ void DeviceResources::CreateWindowSizeDependentResources() { if (!m_window) { - throw std::exception("Call SetWindow with a valid window handle"); + throw std::logic_error("Call SetWindow with a valid Win32 window handle"); } // Wait until all previous GPU work is complete. @@ -206,7 +236,7 @@ void DeviceResources::CreateWindowSizeDependentResources() // Obtain the back buffers for this window which will be the final render targets // and create render target views for each of them. - CD3DX12_HEAP_PROPERTIES swapChainHeapProperties(D3D12_HEAP_TYPE_DEFAULT); + const CD3DX12_HEAP_PROPERTIES swapChainHeapProperties(D3D12_HEAP_TYPE_DEFAULT); D3D12_RESOURCE_DESC swapChainBufferDesc = CD3DX12_RESOURCE_DESC::Tex2D( m_backBufferFormat, @@ -217,6 +247,13 @@ void DeviceResources::CreateWindowSizeDependentResources() ); swapChainBufferDesc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; +#ifdef _GAMING_XBOX_XBOXONE + if (m_options & c_EnableHDR) + { + swapChainBufferDesc.Flags |= D3D12XBOX_RESOURCE_FLAG_ALLOW_AUTOMATIC_GAMEDVR_TONE_MAP; + } +#endif + D3D12_CLEAR_VALUE swapChainOptimizedClearValue = {}; swapChainOptimizedClearValue.Format = m_backBufferFormat; @@ -238,7 +275,7 @@ void DeviceResources::CreateWindowSizeDependentResources() rtvDesc.Format = m_backBufferFormat; rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; - CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( + const CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( m_rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), static_cast(n), m_rtvDescriptorSize); m_d3dDevice->CreateRenderTargetView(m_renderTargets[n].Get(), &rtvDesc, rtvDescriptor); @@ -251,7 +288,7 @@ void DeviceResources::CreateWindowSizeDependentResources() { // Allocate a 2-D surface as the depth/stencil buffer and create a depth/stencil view // on this surface. - CD3DX12_HEAP_PROPERTIES depthHeapProperties(D3D12_HEAP_TYPE_DEFAULT); + const CD3DX12_HEAP_PROPERTIES depthHeapProperties(D3D12_HEAP_TYPE_DEFAULT); D3D12_RESOURCE_DESC depthStencilDesc = CD3DX12_RESOURCE_DESC::Tex2D( m_depthBufferFormat, @@ -259,12 +296,12 @@ void DeviceResources::CreateWindowSizeDependentResources() backBufferHeight, 1, // This depth stencil view has only one texture. 1 // Use a single mipmap level. - ); + ); depthStencilDesc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL; D3D12_CLEAR_VALUE depthOptimizedClearValue = {}; depthOptimizedClearValue.Format = m_depthBufferFormat; - depthOptimizedClearValue.DepthStencil.Depth = 1.0f; + depthOptimizedClearValue.DepthStencil.Depth = (m_options & c_ReverseDepth) ? 0.0f : 1.0f; depthOptimizedClearValue.DepthStencil.Stencil = 0; ThrowIfFailed(m_d3dDevice->CreateCommittedResource( @@ -274,7 +311,7 @@ void DeviceResources::CreateWindowSizeDependentResources() D3D12_RESOURCE_STATE_DEPTH_WRITE, &depthOptimizedClearValue, IID_GRAPHICS_PPV_ARGS(m_depthStencil.ReleaseAndGetAddressOf()) - )); + )); m_depthStencil->SetName(L"Depth stencil"); @@ -300,10 +337,6 @@ void DeviceResources::CreateWindowSizeDependentResources() // Prepare the command list and render target for rendering. void DeviceResources::Prepare(D3D12_RESOURCE_STATES beforeState, D3D12_RESOURCE_STATES afterState) { - // Wait until frame start is signaled - m_framePipelineToken = D3D12XBOX_FRAME_PIPELINE_TOKEN_NULL; - ThrowIfFailed(m_d3dDevice->WaitFrameEventX(D3D12XBOX_FRAME_EVENT_ORIGIN, INFINITE, nullptr, D3D12XBOX_WAIT_FRAME_EVENT_FLAG_NONE, &m_framePipelineToken)); - // Reset command list and allocator. ThrowIfFailed(m_commandAllocators[m_backBufferIndex]->Reset()); ThrowIfFailed(m_commandList->Reset(m_commandAllocators[m_backBufferIndex].Get(), nullptr)); @@ -311,7 +344,8 @@ void DeviceResources::Prepare(D3D12_RESOURCE_STATES beforeState, D3D12_RESOURCE_ if (beforeState != afterState) { // Transition the render target into the correct state to allow for drawing into it. - D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), + const D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition( + m_renderTargets[m_backBufferIndex].Get(), beforeState, afterState); m_commandList->ResourceBarrier(1, &barrier); } @@ -323,7 +357,9 @@ void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState, _In_opt_ const if (beforeState != D3D12_RESOURCE_STATE_PRESENT) { // Transition the render target to the state that allows it to be presented to the display. - D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), beforeState, D3D12_RESOURCE_STATE_PRESENT); + const D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition( + m_renderTargets[m_backBufferIndex].Get(), + beforeState, D3D12_RESOURCE_STATE_PRESENT); m_commandList->ResourceBarrier(1, &barrier); } @@ -337,13 +373,19 @@ void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState, _In_opt_ const planeParameters.ResourceCount = 1; planeParameters.ppResources = m_renderTargets[m_backBufferIndex].GetAddressOf(); + if (m_options & c_EnableHDR) + { + planeParameters.ColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020; + } + ThrowIfFailed( m_commandQueue->PresentX(1, &planeParameters, params) ); - // Xbox One apps do not need to handle DXGI_ERROR_DEVICE_REMOVED or DXGI_ERROR_DEVICE_RESET. + // Xbox apps do not need to handle DXGI_ERROR_DEVICE_REMOVED or DXGI_ERROR_DEVICE_RESET. - MoveToNextFrame(); + // Update the back buffer index. + m_backBufferIndex = (m_backBufferIndex + 1) % m_backBufferCount; } // Handle GPU suspend/resume @@ -365,13 +407,13 @@ void DeviceResources::WaitForGpu() noexcept if (m_commandQueue && m_fence && m_fenceEvent.IsValid()) { // Schedule a Signal command in the GPU queue. - UINT64 fenceValue = m_fenceValues[m_backBufferIndex]; + const UINT64 fenceValue = m_fenceValues[m_backBufferIndex]; if (SUCCEEDED(m_commandQueue->Signal(m_fence.Get(), fenceValue))) { // Wait until the Signal has been processed. if (SUCCEEDED(m_fence->SetEventOnCompletion(fenceValue, m_fenceEvent.Get()))) { - WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); + std::ignore = WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); // Increment the fence value for the current frame. m_fenceValues[m_backBufferIndex]++; @@ -380,25 +422,17 @@ void DeviceResources::WaitForGpu() noexcept } } -// Prepare to render the next frame. -void DeviceResources::MoveToNextFrame() +// For PresentX rendering, we should wait for the origin event just before processing input. +void DeviceResources::WaitForOrigin() { - // Schedule a Signal command in the queue. - const UINT64 currentFenceValue = m_fenceValues[m_backBufferIndex]; - ThrowIfFailed(m_commandQueue->Signal(m_fence.Get(), currentFenceValue)); - - // Update the back buffer index. - m_backBufferIndex = (m_backBufferIndex + 1) % m_backBufferCount; - - // If the next frame is not ready to be rendered yet, wait until it is ready. - if (m_fence->GetCompletedValue() < m_fenceValues[m_backBufferIndex]) - { - ThrowIfFailed(m_fence->SetEventOnCompletion(m_fenceValues[m_backBufferIndex], m_fenceEvent.Get())); - WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); - } - - // Set the fence value for the next frame. - m_fenceValues[m_backBufferIndex] = currentFenceValue + 1; + // Wait until frame start is signaled + m_framePipelineToken = D3D12XBOX_FRAME_PIPELINE_TOKEN_NULL; + ThrowIfFailed(m_d3dDevice->WaitFrameEventX( + D3D12XBOX_FRAME_EVENT_ORIGIN, + INFINITE, + nullptr, + D3D12XBOX_WAIT_FRAME_EVENT_FLAG_NONE, + &m_framePipelineToken)); } // Set frame interval and register for frame events diff --git a/Samples/Graphics/PointSprites/DeviceResources.h b/Samples/Graphics/PointSprites/DeviceResources.h index 5088b68..4924894 100644 --- a/Samples/Graphics/PointSprites/DeviceResources.h +++ b/Samples/Graphics/PointSprites/DeviceResources.h @@ -10,12 +10,16 @@ namespace DX class DeviceResources { public: - static constexpr unsigned int c_Enable4K_UHD = 0x1; + static constexpr unsigned int c_Enable4K_UHD = 0x1; + static constexpr unsigned int c_EnableQHD = 0x2; + static constexpr unsigned int c_EnableHDR = 0x4; + static constexpr unsigned int c_ReverseDepth = 0x8; + static constexpr unsigned int c_AmplificationShaders = 0x10; DeviceResources(DXGI_FORMAT backBufferFormat = DXGI_FORMAT_B8G8R8A8_UNORM, - DXGI_FORMAT depthBufferFormat = DXGI_FORMAT_D32_FLOAT, - UINT backBufferCount = 2, - unsigned int flags = 0) noexcept(false); + DXGI_FORMAT depthBufferFormat = DXGI_FORMAT_D32_FLOAT, + UINT backBufferCount = 2, + unsigned int flags = 0) noexcept(false); ~DeviceResources(); DeviceResources(DeviceResources&&) = default; @@ -24,36 +28,41 @@ namespace DX DeviceResources(DeviceResources const&) = delete; DeviceResources& operator= (DeviceResources const&) = delete; +#ifdef _GAMING_XBOX_SCARLETT + void CreateDeviceResources(D3D12XBOX_CREATE_DEVICE_FLAGS createDeviceFlags = D3D12XBOX_CREATE_DEVICE_FLAG_NONE); +#else void CreateDeviceResources(); +#endif void CreateWindowSizeDependentResources(); void SetWindow(HWND window) noexcept { m_window = window; } void Prepare(D3D12_RESOURCE_STATES beforeState = D3D12_RESOURCE_STATE_PRESENT, - D3D12_RESOURCE_STATES afterState = D3D12_RESOURCE_STATE_RENDER_TARGET); + D3D12_RESOURCE_STATES afterState = D3D12_RESOURCE_STATE_RENDER_TARGET); void Present(D3D12_RESOURCE_STATES beforeState = D3D12_RESOURCE_STATE_RENDER_TARGET, - _In_opt_ const D3D12XBOX_PRESENT_PARAMETERS* params = nullptr); + _In_opt_ const D3D12XBOX_PRESENT_PARAMETERS* params = nullptr); void Suspend(); void Resume(); void WaitForGpu() noexcept; + void WaitForOrigin(); // Device Accessors. RECT GetOutputSize() const noexcept { return m_outputSize; } // Direct3D Accessors. - auto GetD3DDevice() const noexcept { return m_d3dDevice.Get(); } - HWND GetWindow() const noexcept { return m_window; } + auto GetD3DDevice() const noexcept { return m_d3dDevice.Get(); } + HWND GetWindow() const noexcept { return m_window; } D3D_FEATURE_LEVEL GetDeviceFeatureLevel() const noexcept { return m_d3dFeatureLevel; } - ID3D12Resource* GetRenderTarget() const noexcept { return m_renderTargets[m_backBufferIndex].Get(); } - ID3D12Resource* GetDepthStencil() const noexcept { return m_depthStencil.Get(); } - ID3D12CommandQueue* GetCommandQueue() const noexcept { return m_commandQueue.Get(); } - ID3D12CommandAllocator* GetCommandAllocator() const noexcept { return m_commandAllocators[m_backBufferIndex].Get(); } - auto GetCommandList() const noexcept { return m_commandList.Get(); } - DXGI_FORMAT GetBackBufferFormat() const noexcept { return m_backBufferFormat; } - DXGI_FORMAT GetDepthBufferFormat() const noexcept { return m_depthBufferFormat; } - D3D12_VIEWPORT GetScreenViewport() const noexcept { return m_screenViewport; } - D3D12_RECT GetScissorRect() const noexcept { return m_scissorRect; } - UINT GetCurrentFrameIndex() const noexcept { return m_backBufferIndex; } - UINT GetBackBufferCount() const noexcept { return m_backBufferCount; } - unsigned int GetDeviceOptions() const noexcept { return m_options; } + ID3D12Resource* GetRenderTarget() const noexcept { return m_renderTargets[m_backBufferIndex].Get(); } + ID3D12Resource* GetDepthStencil() const noexcept { return m_depthStencil.Get(); } + ID3D12CommandQueue* GetCommandQueue() const noexcept { return m_commandQueue.Get(); } + ID3D12CommandAllocator* GetCommandAllocator() const noexcept { return m_commandAllocators[m_backBufferIndex].Get(); } + auto GetCommandList() const noexcept { return m_commandList.Get(); } + DXGI_FORMAT GetBackBufferFormat() const noexcept { return m_backBufferFormat; } + DXGI_FORMAT GetDepthBufferFormat() const noexcept { return m_depthBufferFormat; } + D3D12_VIEWPORT GetScreenViewport() const noexcept { return m_screenViewport; } + D3D12_RECT GetScissorRect() const noexcept { return m_scissorRect; } + UINT GetCurrentFrameIndex() const noexcept { return m_backBufferIndex; } + UINT GetBackBufferCount() const noexcept { return m_backBufferCount; } + unsigned int GetDeviceOptions() const noexcept { return m_options; } CD3DX12_CPU_DESCRIPTOR_HANDLE GetRenderTargetView() const noexcept { @@ -67,7 +76,6 @@ namespace DX } private: - void MoveToNextFrame(); void RegisterFrameEvents(); static constexpr size_t MAX_BACK_BUFFER_COUNT = 3; diff --git a/Samples/Graphics/PointSprites/Main.cpp b/Samples/Graphics/PointSprites/Main.cpp index d5f79c4..0f753f1 100644 --- a/Samples/Graphics/PointSprites/Main.cpp +++ b/Samples/Graphics/PointSprites/Main.cpp @@ -13,18 +13,30 @@ #include "ATGTelemetry.h" #include +#include #include using namespace DirectX; +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wcovered-switch-default" +#pragma clang diagnostic ignored "-Wswitch-enum" +#endif + +#pragma warning(disable : 4061) + namespace { std::unique_ptr g_sample; HANDLE g_plmSuspendComplete = nullptr; HANDLE g_plmSignalResume = nullptr; -}; +} + +bool g_HDRMode = false; LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); +void SetDisplayMode() noexcept; +void ExitSample() noexcept; // Entry point int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) @@ -55,6 +67,8 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp // Register class and create window PAPPSTATE_REGISTRATION hPLM = {}; + PAPPCONSTRAIN_REGISTRATION hPLM2 = {}; + { // Register class WNDCLASSEXA wcex = {}; @@ -63,7 +77,7 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp wcex.lpfnWndProc = WndProc; wcex.hInstance = hInstance; wcex.lpszClassName = u8"PointSpritesWindowClass"; - wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); + wcex.hbrBackground = reinterpret_cast(COLOR_WINDOW + 1); if (!RegisterClassExA(&wcex)) return 1; @@ -76,6 +90,8 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp ShowWindow(hwnd, nCmdShow); + SetDisplayMode(); + SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast(g_sample.get())); // Sample Usage Telemetry @@ -91,23 +107,30 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp return 1; if (RegisterAppStateChangeNotification([](BOOLEAN quiesced, PVOID context) + { + if (quiesced) { - if (quiesced) - { - ResetEvent(g_plmSuspendComplete); - ResetEvent(g_plmSignalResume); + ResetEvent(g_plmSuspendComplete); + ResetEvent(g_plmSignalResume); - // To ensure we use the main UI thread to process the notification, we self-post a message - PostMessage(reinterpret_cast(context), WM_USER, 0, 0); + // To ensure we use the main UI thread to process the notification, we self-post a message + PostMessage(reinterpret_cast(context), WM_USER, 0, 0); - // To defer suspend, you must wait to exit this callback - (void)WaitForSingleObject(g_plmSuspendComplete, INFINITE); - } - else - { - SetEvent(g_plmSignalResume); - } - }, hwnd, &hPLM)) + // To defer suspend, you must wait to exit this callback + std::ignore = WaitForSingleObject(g_plmSuspendComplete, INFINITE); + } + else + { + SetEvent(g_plmSignalResume); + } + }, hwnd, &hPLM)) + return 1; + + if (RegisterAppConstrainedChangeNotification([](BOOLEAN constrained, PVOID context) + { + // To ensure we use the main UI thread to process the notification, we self-post a message + SendMessage(reinterpret_cast(context), WM_USER + 1, (constrained) ? 1u : 0u, 0); + }, hwnd, &hPLM2)) return 1; } @@ -129,6 +152,7 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp g_sample.reset(); UnregisterAppStateChangeNotification(hPLM); + UnregisterAppConstrainedChangeNotification(hPLM2); CloseHandle(g_plmSuspendComplete); CloseHandle(g_plmSignalResume); @@ -153,16 +177,50 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) // Complete deferral SetEvent(g_plmSuspendComplete); - (void)WaitForSingleObject(g_plmSignalResume, INFINITE); + std::ignore = WaitForSingleObject(g_plmSignalResume, INFINITE); + + SetDisplayMode(); sample->OnResuming(); } break; + + case WM_USER + 1: + if (sample) + { + if (wParam) + { + sample->OnConstrained(); + } + else + { + SetDisplayMode(); + + sample->OnUnConstrained(); + } + } + break; } return DefWindowProc(hWnd, message, wParam, lParam); } +// HDR helper +void SetDisplayMode() noexcept +{ + if (g_sample && g_sample->RequestHDRMode()) + { + // Request HDR mode. + auto result = XDisplayTryEnableHdrMode(XDisplayHdrModePreference::PreferHdr, nullptr); + + g_HDRMode = (result == XDisplayHdrModeResult::Enabled); + +#ifdef _DEBUG + OutputDebugStringA((g_HDRMode) ? "INFO: Display in HDR Mode\n" : "INFO: Display in SDR Mode\n"); +#endif + } +} + // Exit helper void ExitSample() noexcept { diff --git a/Samples/Graphics/PointSprites/PointSprites.cpp b/Samples/Graphics/PointSprites/PointSprites.cpp index dea49e8..16994d5 100644 --- a/Samples/Graphics/PointSprites/PointSprites.cpp +++ b/Samples/Graphics/PointSprites/PointSprites.cpp @@ -10,7 +10,7 @@ #include "ATGColors.h" -extern void ExitSample(); +extern void ExitSample() noexcept; using namespace DirectX; @@ -21,23 +21,25 @@ namespace //------------------------------------- // Sample Constants - const wchar_t* g_sampleTitle = L"Point Sprites"; + const wchar_t* g_sampleTitle = L"Point Sprites"; - const int s_vertexStride = (3 + 4) * 4; - const int s_numParticles = 32 * 1024; - const float s_maxParticleColor = 0.2f; - const float s_maxParticleSize = 30.0f; - const float s_defaultParticleSize = 4.0f; - const int s_runsPerTest = 3; - const int s_runsPerSequence = 2; + constexpr int c_vertexStride = (3 + 4) * 4; + constexpr int c_numParticles = 32 * 1024; + constexpr float c_maxParticleColor = 0.2f; + constexpr float c_maxParticleSize = 30.0f; + constexpr float c_defaultParticleSize = 4.0f; + constexpr int c_runsPerTest = 3; + constexpr int c_runsPerSequence = 2; - const D3D12_INPUT_ELEMENT_DESC s_elementDesc[] = + constexpr uint32_t c_elementDescNum = 2; + + const D3D12_INPUT_ELEMENT_DESC s_elementDesc[c_elementDescNum] = { { "TEXCOORD", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, }; - const D3D12_INPUT_ELEMENT_DESC s_instanceElementDesc[] = + const D3D12_INPUT_ELEMENT_DESC s_instanceElementDesc[c_elementDescNum] = { { "TEXCOORD", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA, 1 }, { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA, 1 }, @@ -50,7 +52,7 @@ Sample::Sample() noexcept(false) , m_displayHeight(0) , m_frame(0) , m_vbView{} - , m_particleSize(s_defaultParticleSize) + , m_particleSize(c_defaultParticleSize) , m_zeroViewport(false) , m_refreshParticles(true) , m_selectorIdx(0) @@ -90,6 +92,8 @@ void Sample::Tick() { PIXBeginEvent(PIX_COLOR_DEFAULT, L"Frame %llu", m_frame); + m_deviceResources->WaitForOrigin(); + m_timer.Tick([&]() { Update(m_timer); @@ -118,7 +122,7 @@ void Sample::Update(DX::StepTimer const&) if (pad.IsAPressed()) { - m_particleSize = std::min(m_particleSize + 0.5f, s_maxParticleSize); + m_particleSize = std::min(m_particleSize + 0.5f, c_maxParticleSize); m_refreshParticles = true; } @@ -189,13 +193,13 @@ void Sample::Render() // Set the common resource state to the pipeline. ID3D12DescriptorHeap* heaps[] = { m_srvPile->Heap() }; - commandList->SetDescriptorHeaps(_countof(heaps), heaps); + commandList->SetDescriptorHeaps(static_cast(std::size(heaps)), heaps); commandList->SetGraphicsRootSignature(m_commonRS.Get()); float cbData[] = { 1.f / m_displayWidth, 1.f / m_displayHeight, }; commandList->SetGraphicsRoot32BitConstants(RS_ConstantBuffer, 2, &cbData, 0); - commandList->SetGraphicsRoot32BitConstant(RS_ConstantBuffer, UINT(s_numParticles), 2); + commandList->SetGraphicsRoot32BitConstant(RS_ConstantBuffer, UINT(c_numParticles), 2); commandList->SetGraphicsRootShaderResourceView(RS_VertexBuffer, m_vertices->GetGPUVirtualAddress()); commandList->SetGraphicsRootDescriptorTable(RS_ParticleTex, m_srvPile->GetGpuHandle(SRV_ParticleTex)); @@ -203,21 +207,21 @@ void Sample::Render() ClearTestResults(); - // Run the forward sequence test s_runsPerSequence times - for (int i = 0; i < s_runsPerSequence; ++i) + // Run the forward sequence test c_runsPerSequence times + for (int i = 0; i < c_runsPerSequence; ++i) { RunTests(m_forwardSequence, commandList); } - // Run the random sequence test s_runsPerSequence times - for (int i = 0; i < s_runsPerSequence; ++i) + // Run the random sequence test c_runsPerSequence times + for (int i = 0; i < c_runsPerSequence; ++i) { CreateRandomTestSequence(m_randomSequence); RunTests(m_randomSequence, commandList); } - // Run the reverse sequence test s_runsPerSequence times - for (int i = 0; i < s_runsPerSequence; ++i) + // Run the reverse sequence test c_runsPerSequence times + for (int i = 0; i < c_runsPerSequence; ++i) { RunTests(m_reverseSequence, commandList); } @@ -239,11 +243,11 @@ void Sample::Render() m_hudBatch->Begin(commandList); - const float ParticleCountPerTest = float(s_numParticles * s_runsPerTest) * 0.000001f; // In millions - const float TotalParticleCount = float(s_numParticles * s_runsPerTest * s_runsPerSequence * 3 * enabledTestCount) * 0.000001f; // In millions + const float ParticleCountPerTest = float(c_numParticles * c_runsPerTest) * 0.000001f; // In millions + const float TotalParticleCount = float(c_numParticles * c_runsPerTest * c_runsPerSequence * 3 * enabledTestCount) * 0.000001f; // In millions wchar_t textBuffer[1024] = {}; - swprintf_s(textBuffer, _countof(textBuffer), + swprintf_s(textBuffer, L"Particles per Test: %.2f mln \n" "Total Rendered Particles: %.2f mln\n" "ParticleSize: %.1f\n" @@ -288,10 +292,10 @@ void Sample::Render() Test& test = *it; auto testColor = test.Enabled ? DirectX::Colors::Green : DirectX::Colors::Red; - swprintf_s(textBuffer, _countof(textBuffer), L"%s", test.Name); + swprintf_s(textBuffer, L"%s", test.Name); m_smallFont->DrawString(m_hudBatch.get(), textBuffer, XMFLOAT2(xPos - 460, yPos), testColor); - swprintf_s(textBuffer, _countof(textBuffer), L"%.2f", test.AverageTimeResult); + swprintf_s(textBuffer, L"%.2f", test.AverageTimeResult); float xOffset = XMVectorGetX(m_smallFont->MeasureString(textBuffer)); m_smallFont->DrawString(m_hudBatch.get(), textBuffer, XMFLOAT2(xPos - xOffset, yPos), testColor); @@ -322,16 +326,16 @@ void Sample::Clear() PIXBeginEvent(commandList, PIX_COLOR_DEFAULT, L"Clear"); // Clear the views. - auto rtvDescriptor = m_deviceResources->GetRenderTargetView(); - auto dsvDescriptor = m_deviceResources->GetDepthStencilView(); + auto const rtvDescriptor = m_deviceResources->GetRenderTargetView(); + auto const dsvDescriptor = m_deviceResources->GetDepthStencilView(); commandList->OMSetRenderTargets(1, &rtvDescriptor, FALSE, &dsvDescriptor); commandList->ClearRenderTargetView(rtvDescriptor, DirectX::Colors::Black, 0, nullptr); commandList->ClearDepthStencilView(dsvDescriptor, D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr); // Set the viewport and scissor rect. - auto viewport = m_deviceResources->GetScreenViewport(); - auto scissorRect = m_deviceResources->GetScissorRect(); + auto const viewport = m_deviceResources->GetScreenViewport(); + auto const scissorRect = m_deviceResources->GetScissorRect(); commandList->RSSetViewports(1, &viewport); commandList->RSSetScissorRects(1, &scissorRect); @@ -364,15 +368,13 @@ void Sample::CreateDeviceDependentResources() m_gpuTimer = std::make_unique(device, m_deviceResources->GetCommandQueue()); m_commonStates = std::make_unique(device); m_srvPile = std::make_unique(device, - D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, - D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, 128, SRV_Count); auto resourceUpload = ResourceUploadBatch(device); resourceUpload.Begin(); - auto backBufferRts = RenderTargetState(m_deviceResources->GetBackBufferFormat(), m_deviceResources->GetDepthBufferFormat()); + const RenderTargetState backBufferRts(m_deviceResources->GetBackBufferFormat(), m_deviceResources->GetDepthBufferFormat()); auto spritePSD = SpriteBatchPipelineStateDescription(backBufferRts, &CommonStates::AlphaBlend); m_hudBatch = std::make_unique(device, resourceUpload, spritePSD); @@ -383,8 +385,8 @@ void Sample::CreateDeviceDependentResources() finished.wait(); // Create our vertex buffer resource. - auto defaultHeapProps = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT); - auto desc = CD3DX12_RESOURCE_DESC::Buffer(s_vertexStride * s_numParticles); + const CD3DX12_HEAP_PROPERTIES defaultHeapProps(D3D12_HEAP_TYPE_DEFAULT); + auto desc = CD3DX12_RESOURCE_DESC::Buffer(c_vertexStride * c_numParticles); DX::ThrowIfFailed(device->CreateCommittedResource( &defaultHeapProps, @@ -397,8 +399,8 @@ void Sample::CreateDeviceDependentResources() m_vertices->SetName(L"Vertex Buffer"); m_vbView.BufferLocation = m_vertices->GetGPUVirtualAddress(); - m_vbView.SizeInBytes = s_vertexStride * s_numParticles; - m_vbView.StrideInBytes = s_vertexStride; + m_vbView.SizeInBytes = c_vertexStride * c_numParticles; + m_vbView.StrideInBytes = c_vertexStride; CreateTestPSOs(); } @@ -481,7 +483,7 @@ void Sample::CreateTestPSOs() auto nativeQuadInstVS = DX::ReadData(L"RenderNativeQuadInstancingVS.cso"); psoDesc.InputLayout.pInputElementDescs = s_instanceElementDesc; - psoDesc.InputLayout.NumElements = _countof(s_instanceElementDesc); + psoDesc.InputLayout.NumElements = c_elementDescNum; psoDesc.VS = { nativeQuadInstVS.data(), nativeQuadInstVS.size() }; psoDesc.PrimitiveTopologyType = D3D12XBOX_PRIMITIVE_TOPOLOGY_TYPE_QUAD; @@ -493,7 +495,7 @@ void Sample::CreateTestPSOs() auto triStripQuadInstVS = DX::ReadData(L"RenderTriangleStripQuadInstancingVS.cso"); psoDesc.InputLayout.pInputElementDescs = s_instanceElementDesc; - psoDesc.InputLayout.NumElements = _countof(s_instanceElementDesc); + psoDesc.InputLayout.NumElements = c_elementDescNum; psoDesc.VS = { triStripQuadInstVS.data(), triStripQuadInstVS.size() }; psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; @@ -538,7 +540,7 @@ void Sample::CreateTestPSOs() auto render3InstVS = DX::ReadData(L"Render3InstancingVS.cso"); psoDesc.InputLayout.pInputElementDescs = s_instanceElementDesc; - psoDesc.InputLayout.NumElements = _countof(s_instanceElementDesc); + psoDesc.InputLayout.NumElements = c_elementDescNum; psoDesc.VS = { render3InstVS.data(), render3InstVS.size() }; psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; @@ -550,7 +552,7 @@ void Sample::CreateTestPSOs() auto render4InstVS = DX::ReadData(L"Render4InstancingVS.cso"); psoDesc.InputLayout.pInputElementDescs = s_instanceElementDesc; - psoDesc.InputLayout.NumElements = _countof(s_instanceElementDesc); + psoDesc.InputLayout.NumElements = c_elementDescNum; psoDesc.VS = { render4InstVS.data(), render4InstVS.size() }; psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; @@ -563,7 +565,7 @@ void Sample::CreateTestPSOs() // OffChip psoDesc.InputLayout.pInputElementDescs = s_elementDesc; - psoDesc.InputLayout.NumElements = _countof(s_elementDesc); + psoDesc.InputLayout.NumElements = c_elementDescNum; psoDesc.VS = { passVS.data(), passVS.size() }; psoDesc.GS = { expand3GS.data(), expand3GS.size() }; psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_POINT; @@ -571,7 +573,7 @@ void Sample::CreateTestPSOs() DX::ThrowIfFailed(device->CreateGraphicsPipelineState(&psoDesc, IID_GRAPHICS_PPV_ARGS(m_triGeoOffChipPSO.ReleaseAndGetAddressOf()))); } -#ifdef _GAMING_XBOX_XBOXONE // Xbox Scarlett doesn't have on/off-chip specification +#ifdef _GAMING_XBOX_XBOXONE // Xbox Series X|S doesn't have on/off-chip specification { auto expand3OnChipGS = DX::ReadData(L"Expand3OnChipGS.cso"); @@ -590,7 +592,7 @@ void Sample::CreateTestPSOs() // OffChip psoDesc.InputLayout.pInputElementDescs = s_elementDesc; - psoDesc.InputLayout.NumElements = _countof(s_elementDesc); + psoDesc.InputLayout.NumElements = c_elementDescNum; psoDesc.VS = { passVS.data(), passVS.size() }; psoDesc.GS = { expand4GS.data(), expand4GS.size() }; psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_POINT; @@ -599,7 +601,7 @@ void Sample::CreateTestPSOs() DX::ThrowIfFailed(device->CreateGraphicsPipelineState(&psoDesc, IID_GRAPHICS_PPV_ARGS(m_quadGeoOffChipPSO.ReleaseAndGetAddressOf()))); } -#ifdef _GAMING_XBOX_XBOXONE // Xbox Scarlett doesn't have on/off-chip specification +#ifdef _GAMING_XBOX_XBOXONE // Xbox Series X|S doesn't have on/off-chip specification { auto expand4OnChipGS = DX::ReadData(L"Expand4OnChipGS.cso"); @@ -626,7 +628,7 @@ void Sample::CreateTestPSOs() DX::ThrowIfFailed(device->CreateGraphicsPipelineState(&psoDesc, IID_GRAPHICS_PPV_ARGS(m_onlyTriGeoOffChipPSO.ReleaseAndGetAddressOf()))); } -#ifdef _GAMING_XBOX_XBOXONE // Xbox Scarlett doesn't have on/off-chip specification +#ifdef _GAMING_XBOX_XBOXONE // Xbox Series X|S doesn't have on/off-chip specification { auto onlyExpand3OnChipGS = DX::ReadData(L"OnlyExpand3OnChipGS.cso"); @@ -653,7 +655,7 @@ void Sample::CreateTestPSOs() DX::ThrowIfFailed(device->CreateGraphicsPipelineState(&psoDesc, IID_GRAPHICS_PPV_ARGS(m_onlyQuadGeoOffChipPSO.ReleaseAndGetAddressOf()))); } -#ifdef _GAMING_XBOX_XBOXONE // Xbox Scarlett doesn't have on/off-chip specification +#ifdef _GAMING_XBOX_XBOXONE // Xbox Series X|S doesn't have on/off-chip specification { auto onlyExpand4OnChipGS = DX::ReadData(L"OnlyExpand4OnChipGS.cso"); @@ -672,7 +674,7 @@ void Sample::CreateTestPSOs() auto render3DS = DX::ReadData(L"Render3DS.cso"); psoDesc.InputLayout.pInputElementDescs = s_elementDesc; - psoDesc.InputLayout.NumElements = _countof(s_elementDesc); + psoDesc.InputLayout.NumElements = c_elementDescNum; psoDesc.VS = { passVS.data(), passVS.size() }; psoDesc.HS = { render3HS.data(), render3HS.size() }; psoDesc.DS = { render3DS.data(), render3DS.size() }; @@ -689,7 +691,7 @@ void Sample::CreateTestPSOs() auto render4DS = DX::ReadData(L"Render4DS.cso"); psoDesc.InputLayout.pInputElementDescs = s_elementDesc; - psoDesc.InputLayout.NumElements = _countof(s_elementDesc); + psoDesc.InputLayout.NumElements = c_elementDescNum; psoDesc.VS = { passVS.data(), passVS.size() }; psoDesc.HS = { render4HS.data(), render4HS.size() }; psoDesc.DS = { render4DS.data(), render4DS.size() }; @@ -740,8 +742,8 @@ void Sample::UpdateVertexData(ID3D12GraphicsCommandList* commandList) srand(0); // Generate the vertex data - static float vbData[7 * s_numParticles]; - for (int i = 0; i < s_numParticles; ++i) + static float vbData[7 * c_numParticles]; + for (int i = 0; i < c_numParticles; ++i) { float* p = &vbData[i * 7]; @@ -749,9 +751,9 @@ void Sample::UpdateVertexData(ID3D12GraphicsCommandList* commandList) p[1] = m_displayHeight * (float)rand() / (float)RAND_MAX; p[2] = m_particleSize * (float)rand() / (float)RAND_MAX; - p[3] = s_maxParticleColor * (float)rand() / (float)RAND_MAX; - p[4] = s_maxParticleColor * (float)rand() / (float)RAND_MAX; - p[5] = s_maxParticleColor * (float)rand() / (float)RAND_MAX; + p[3] = c_maxParticleColor * (float)rand() / (float)RAND_MAX; + p[4] = c_maxParticleColor * (float)rand() / (float)RAND_MAX; + p[5] = c_maxParticleColor * (float)rand() / (float)RAND_MAX; p[6] = (float)rand() / (float)RAND_MAX; } @@ -785,7 +787,7 @@ void Sample::RegisterTests() RegisterTest(true, L"VS instanced triangles", &Sample::TestVSInstancedTriangles); RegisterTest(true, L"VS instanced quads", &Sample::TestVSInstancedQuads); -#ifdef _GAMING_XBOX_XBOXONE // Xbox Scarlett doesn't have on/off-chip specification +#ifdef _GAMING_XBOX_XBOXONE // Xbox Series X|S doesn't have on/off-chip specification RegisterTest(true, L"GS triangles (off chip - default)", &Sample::TestGSTriangles); RegisterTest(true, L"GS quads (off chip - default)", &Sample::TestGSQuads); RegisterTest(true, L"GS only triangles (off chip - default)", &Sample::TestGSOnlyTriangles); @@ -934,10 +936,10 @@ void Sample::TestVSNativeQuads(Test& test, ID3D12GraphicsCommandList* commandLis commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_QUADLIST); m_gpuTimer->Start(commandList, test.Id); - for (int i = 0; i < s_runsPerTest; ++i) + for (int i = 0; i < c_runsPerTest; ++i) { // multiplying the number of verts by 4 because each 4 verts will make a quad - commandList->DrawInstanced(s_numParticles * 4, 1, 0, 0); + commandList->DrawInstanced(c_numParticles * 4, 1, 0, 0); } m_gpuTimer->Stop(commandList, test.Id); } @@ -948,9 +950,9 @@ void Sample::TestVSNativeInstancedQuads(Test& test, ID3D12GraphicsCommandList* c commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_QUADLIST); m_gpuTimer->Start(commandList, test.Id); - for (int i = 0; i < s_runsPerTest; ++i) + for (int i = 0; i < c_runsPerTest; ++i) { - commandList->DrawInstanced(4, s_numParticles, 0, 0); + commandList->DrawInstanced(4, c_numParticles, 0, 0); } m_gpuTimer->Stop(commandList, test.Id); } @@ -961,10 +963,10 @@ void Sample::TestVSTriangleStripInstancedQuads(Test& test, ID3D12GraphicsCommand commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); m_gpuTimer->Start(commandList, test.Id); - for (int i = 0; i < s_runsPerTest; ++i) + for (int i = 0; i < c_runsPerTest; ++i) { // multiplying the number of verts by 4 because each 4 verts will make a quad - commandList->DrawInstanced(4, s_numParticles, 0, 0); + commandList->DrawInstanced(4, c_numParticles, 0, 0); } m_gpuTimer->Stop(commandList, test.Id); } @@ -975,10 +977,10 @@ void Sample::TestVSRectListQuads(Test& test, ID3D12GraphicsCommandList* commandL commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_RECTLIST); m_gpuTimer->Start(commandList, test.Id); - for (int i = 0; i < s_runsPerTest; ++i) + for (int i = 0; i < c_runsPerTest; ++i) { // multiplying the number of verts by 3 because each 3 verts will make a quad - commandList->DrawInstanced(s_numParticles * 3, 1, 0, 0); + commandList->DrawInstanced(c_numParticles * 3, 1, 0, 0); } m_gpuTimer->Stop(commandList, test.Id); } @@ -989,10 +991,10 @@ void Sample::TestVSTriangles(Test& test, ID3D12GraphicsCommandList* commandList) commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); m_gpuTimer->Start(commandList, test.Id); - for (int i = 0; i < s_runsPerTest; ++i) + for (int i = 0; i < c_runsPerTest; ++i) { // multiplying the number of verts by 3 because each 3 verts will make a triangle - commandList->DrawInstanced(s_numParticles * 3, 1, 0, 0); + commandList->DrawInstanced(c_numParticles * 3, 1, 0, 0); } m_gpuTimer->Stop(commandList, test.Id); } @@ -1003,10 +1005,10 @@ void Sample::TestVSQuads(Test& test, ID3D12GraphicsCommandList* commandList) commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); m_gpuTimer->Start(commandList, test.Id); - for (int i = 0; i < s_runsPerTest; ++i) + for (int i = 0; i < c_runsPerTest; ++i) { // multiplying the number of verts by 6 because each 6 verts will make two triangles - commandList->DrawInstanced(s_numParticles * 6, 1, 0, 0); + commandList->DrawInstanced(c_numParticles * 6, 1, 0, 0); } m_gpuTimer->Stop(commandList, test.Id); } @@ -1017,9 +1019,9 @@ void Sample::TestVSInstancedTriangles(Test& test, ID3D12GraphicsCommandList* com commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); m_gpuTimer->Start(commandList, test.Id); - for (int i = 0; i < s_runsPerTest; ++i) + for (int i = 0; i < c_runsPerTest; ++i) { - commandList->DrawInstanced(3, s_numParticles, 0, 0); + commandList->DrawInstanced(3, c_numParticles, 0, 0); } m_gpuTimer->Stop(commandList, test.Id); } @@ -1030,9 +1032,9 @@ void Sample::TestVSInstancedQuads(Test& test, ID3D12GraphicsCommandList* command commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); m_gpuTimer->Start(commandList, test.Id); - for (int i = 0; i < s_runsPerTest; ++i) + for (int i = 0; i < c_runsPerTest; ++i) { - commandList->DrawInstanced(6, s_numParticles, 0, 0); + commandList->DrawInstanced(6, c_numParticles, 0, 0); } m_gpuTimer->Stop(commandList, test.Id); } @@ -1048,9 +1050,9 @@ void Sample::TestGSTriangles(Test& test, ID3D12GraphicsCommandList* commandList) commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_POINTLIST); m_gpuTimer->Start(commandList, test.Id); - for (int i = 0; i < s_runsPerTest; ++i) + for (int i = 0; i < c_runsPerTest; ++i) { - commandList->DrawInstanced(s_numParticles, 1, 0, 0); + commandList->DrawInstanced(c_numParticles, 1, 0, 0); } m_gpuTimer->Stop(commandList, test.Id); } @@ -1062,9 +1064,9 @@ void Sample::TestGSQuads(Test& test, ID3D12GraphicsCommandList* commandList) commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_POINTLIST); m_gpuTimer->Start(commandList, test.Id); - for (int i = 0; i < s_runsPerTest; ++i) + for (int i = 0; i < c_runsPerTest; ++i) { - commandList->DrawInstanced(s_numParticles, 1, 0, 0); + commandList->DrawInstanced(c_numParticles, 1, 0, 0); } m_gpuTimer->Stop(commandList, test.Id); } @@ -1076,9 +1078,9 @@ void Sample::TestGSOnlyTriangles(Test& test, ID3D12GraphicsCommandList* commandL commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_POINTLIST); m_gpuTimer->Start(commandList, test.Id); - for (int i = 0; i < s_runsPerTest; ++i) + for (int i = 0; i < c_runsPerTest; ++i) { - commandList->DrawInstanced(s_numParticles, 1, 0, 0); + commandList->DrawInstanced(c_numParticles, 1, 0, 0); } m_gpuTimer->Stop(commandList, test.Id); } @@ -1090,9 +1092,9 @@ void Sample::TestGSOnlyQuads(Test& test, ID3D12GraphicsCommandList* commandList) commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_POINTLIST); m_gpuTimer->Start(commandList, test.Id); - for (int i = 0; i < s_runsPerTest; ++i) + for (int i = 0; i < c_runsPerTest; ++i) { - commandList->DrawInstanced(s_numParticles, 1, 0, 0); + commandList->DrawInstanced(c_numParticles, 1, 0, 0); } m_gpuTimer->Stop(commandList, test.Id); @@ -1107,9 +1109,9 @@ void Sample::TestDSTriangles(Test& test, ID3D12GraphicsCommandList* commandList) commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_1_CONTROL_POINT_PATCHLIST); m_gpuTimer->Start(commandList, test.Id); - for (int i = 0; i < s_runsPerTest; ++i) + for (int i = 0; i < c_runsPerTest; ++i) { - commandList->DrawInstanced(s_numParticles, 1, 0, 0); + commandList->DrawInstanced(c_numParticles, 1, 0, 0); } m_gpuTimer->Stop(commandList, test.Id); @@ -1121,9 +1123,9 @@ void Sample::TestDSQuads(Test& test, ID3D12GraphicsCommandList* commandList) commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_1_CONTROL_POINT_PATCHLIST); m_gpuTimer->Start(commandList, test.Id); - for (int i = 0; i < s_runsPerTest; ++i) + for (int i = 0; i < c_runsPerTest; ++i) { - commandList->DrawInstanced(s_numParticles, 1, 0, 0); + commandList->DrawInstanced(c_numParticles, 1, 0, 0); } m_gpuTimer->Stop(commandList, test.Id); @@ -1140,11 +1142,11 @@ void Sample::TestMSTriangles(Test& test, ID3D12GraphicsCommandList* commandList) commandList->SetPipelineState(m_msPSO.Get()); commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); - uint32_t totalVertCount = (s_numParticles * 4); + uint32_t totalVertCount = (c_numParticles * 4); uint32_t groupCount = (totalVertCount + 31) / 32; m_gpuTimer->Start(commandList, test.Id); - for (int i = 0; i < s_runsPerTest; ++i) + for (int i = 0; i < c_runsPerTest; ++i) { cmdList->DispatchMesh(groupCount, 1, 1); } diff --git a/Samples/Graphics/PointSprites/PointSprites.h b/Samples/Graphics/PointSprites/PointSprites.h index 68a1876..c7e3a10 100644 --- a/Samples/Graphics/PointSprites/PointSprites.h +++ b/Samples/Graphics/PointSprites/PointSprites.h @@ -35,6 +35,11 @@ public: // Messages void OnSuspending(); void OnResuming(); + void OnConstrained() {} + void OnUnConstrained() {} + + // Properties + bool RequestHDRMode() const noexcept { return m_deviceResources ? (m_deviceResources->GetDeviceOptions() & DX::DeviceResources::c_EnableHDR) != 0 : false; } private: diff --git a/Samples/Graphics/PointSprites/StepTimer.h b/Samples/Graphics/PointSprites/StepTimer.h index bb15fff..2dbefcb 100644 --- a/Samples/Graphics/PointSprites/StepTimer.h +++ b/Samples/Graphics/PointSprites/StepTimer.h @@ -111,7 +111,7 @@ namespace DX timeDelta *= TicksPerSecond; timeDelta /= static_cast(m_qpcFrequency.QuadPart); - uint32_t lastFrameCount = m_frameCount; + const uint32_t lastFrameCount = m_frameCount; if (m_isFixedTimeStep) { diff --git a/Samples/Graphics/PointSprites/pch.h b/Samples/Graphics/PointSprites/pch.h index aa900ed..7408827 100644 --- a/Samples/Graphics/PointSprites/pch.h +++ b/Samples/Graphics/PointSprites/pch.h @@ -37,8 +37,8 @@ #include -#if _GXDK_VER < 0x4A610D2B /* GXDK Edition 200600 */ -#error This sample requires the June 2020 GDK or later +#if _GXDK_VER < 0x55F007B0 /* GDK Edition 211000 */ +#error This sample requires the October 2021 GDK or later #endif #ifdef _GAMING_XBOX_SCARLETT @@ -53,19 +53,27 @@ #include #include + #include #include #include +#include #include +#include #include +#include +#include +#include #include +#include #include #include +#include +#include +#include #include -#include -#include #include #include @@ -73,27 +81,28 @@ #include #include "CommonStates.h" -#include "ControllerFont.h" #include "DDSTextureLoader.h" #include "DescriptorHeap.h" #include "DirectXHelpers.h" #include "GamePad.h" #include "GraphicsMemory.h" -#include "PerformanceTimersXbox.h" -#include "ReadData.h" #include "RenderTargetState.h" #include "ResourceUploadBatch.h" #include "SimpleMath.h" #include "SpriteBatch.h" #include "SpriteFont.h" +#include "ControllerFont.h" +#include "PerformanceTimersXbox.h" +#include "ReadData.h" + namespace DX { // Helper class for COM exceptions class com_exception : public std::exception { public: - com_exception(HRESULT hr) : result(hr) {} + com_exception(HRESULT hr) noexcept : result(hr) {} const char* what() const override { @@ -111,6 +120,12 @@ namespace DX { if (FAILED(hr)) { +#ifdef _DEBUG + char str[64] = {}; + sprintf_s(str, "**ERROR** Fatal Error with HRESULT of %08X\n", static_cast(hr)); + OutputDebugStringA(str); + __debugbreak(); +#endif throw com_exception(hr); } } diff --git a/Samples/Graphics/PointSprites/readme.docx b/Samples/Graphics/PointSprites/readme.docx index ed285d2743b0ebec3a97475a0db5e0e0c7a95eba..34a76446ecf20b4e401cf14c02db80f655490575 100644 GIT binary patch delta 35295 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 delta 34631 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⪚J42{fAUk6tttYG{f2r8gR^w z;e=p;?V=NMtOj<#>7z6m5oqKuD}p)qhEN1>3Nsb1)MK z*&pRH8F5^dOgw56#Pej8RF2LGloD(vz3SL1rRt7I1-IF`h z%pjMVCo=m0NHsu?G$jaf2d(~5YlmC0rfONrAi+E3vi4&|uu+N7;Ej`2W&c(shagF4 zD!NN!FLQxwV82|DHZA7e2fi0w<*FKR*-45s91!%jQEJcal(b>cIeQYDq?!&A`SC>BiHtI0KoaVMz#U#ep}l$;jidHdwt1 ztSS%+;J^V>lR#xa30hv7*4tqr8Gj?qYSNqrDh!TTBx;+$2ob^rVIICT1}&y8L>!84 zSh)v$Z|c&N#%?t+FL6>Fwhw&ly`;w64#sA_&j6ueda>SAk`5aj=jVoQ&4_-T$W4`Q zkLzM|1mtptdHbt#i_zv@nxLTOn}ZIzY8LMwKzyY+n{c}ix1?-`*P9(39CJ2dUkOP_ za>%E`G1O?69%BzT)f%J${17{aiLpChc>JV65JafQ z_!t+@e7;h^tABON0OM@zuzd?cEqPwF=a5j4?sY4qyWC7EI3X!vgop=ak6{aQ9+7zSjFZWDbGQ*-_ratHnlc}2{)3O!q)KwB$$aEA+zlnOR1S zB;^RUBml3;9%MjK&X$NC_dzOwPbUSC@!gUR>_1Xi5{C>Ef%bOc!8y5a#)zh*101Z; zs3tPA*Lw*IcT~tdOYKz<3-S`ciNnh*d1O`RjA)IKL7D=EV15;`d2N$Lub;=A|9OOn zK@AU2Z)0RETel}*+;Z*-n?sVMkjOzTDgqzoW|9~WV%}r98rgBUaF2>YI=tZ%apc*{ zemiIwbb~_XWZ!a-jc*0J82d&jIY?QTcH%vZlHCEgbw8y8?VZCvEzk38wU@fsS2`OHE@Eb?>n zR2L^M(<@dc(!u`GfggKoIFa0VH!!)UX8fe;cy6gH28UC+pW7|t3YFYX1Ei6_pgUzR zOms=fuI){5n-$gkJ(^%07DqzE(Xp+SHuCbMv70>n_FG#@dmK^mrb>CYsIOS{ zfRtIGV#M-DxmH|KIj<9KkeLsOGD8w&!oO$`YP@GA!vENaG`XEwyoZ8qu%4rUpqgpR ziBdtt8>VnVQ)P50T;kU;1i<|JZPAp`x0P|rlaD8QIZ}=-6u)*$`_abd&h?{d-`jh? z3_!4UiPUXb)!yY1uDzv$*IQo?TC=@y?s9s5h?ItYJZZRix2I%PEd)b_S?SeZu z>wJFX{XKY~$$1~NU$-tgU*=z&ciB4KH1&E~KXeCfE`1K%Zhjm*1)S9r060egtylEJ z>+O^4p3U6aAYE++7(WS&j$HMPn*Dt%&%Qn%_Rm824ZZtbfHQp;#vSS&p7#5laQK@# z-Q{rC2b?l_nnsV=04~-_(>8A_MZ-<&KkDS_^~?9j`QqW)&o-^<_IK0Ae&#Qcc|pke zXJEinp#O05@}saDyZ7BySTy}L-#-RlQO6FbPl@BvGyZ*(4#7`n@0Y8d zm$prW)XA!=^Stxe6@K!l(ZMpUE~4Z7;!+)z6s^Y=0VY--3@t`!j1GzGZnovTE%x%j zb3A@LuTQ=Io|J?>XAqzj{u!$*u%>o9AmRWV3pmh2ubVBmdk@PxFW8Xx^epF;{0y3# zO^cg~yezB3Wem&UT6!e7n(vw3A>Y;Sp{f04F4$6bIbwSG*e}^cAx=*|k`Nr?eBWNZ zcnElcHSqt9UdDMVhXgXD7tT|9wBUVxEq?36L%cGH7xnVU^sVnor6K&tf|t8L9=rjG zJVR~KzVe6puTVU zCinD5yG;=aI7Yg7>G-_g-w~AE<)5*eyeLcM@`&m;)@blf3U<1RWEnM^%~u4t!!`~9 zvMQr_MzL>ErBWUxFK}{({!#0}v6X&}ecyIev~Cb9U_XR0C?uOJ247lLSs~w3 zdUrC6u;NUS!9m0|0=F3n{0)MG_-KT4GBINDP${WFVNDuZlU=3mlYKZ-pR>n(uVupd zb$9B>Wn~gv^~r^75@^Bzb4LZ32_CG``PVSEA=R;4Fxc-P^x~DmN2gmUGOY3b!BxQv z(`RmAMOGpce>|*mG)|*_GfS7V;q5CvN7IsdkL}mGytXFUu(xDe7w4ox4xfT*vQ6JVG1>>%$>b1GmvD5* z&MemdSr%NDj}^j?8?LuE{L1u3<_&$Ur-#1#mbE&rgD&$;=9?M0J?4~pYuLrtZx$kC zy+{p6=MN_!{!&mqGi^<~@3x0lq7hLLR|w6IIo~U!8Sb5qEe~{yz4vITpz($4R^$JC z9sVxc|IVt2o$7yIJdFeZ%=Snm|1b~kuIH6uu%?5L3j62oar3(=XJ=LNeXwX8^|ii| z?jgM#l66TPizsBi+nfs|+4s-i@a^PiU{O~@MMQj_K-KL-HFETQzuoW~$mMbDn@<n0xgUoXBziv&xat;g>&|a&SpmLox0e9E%xG!bU;WeQZi25N z%T2M3PV!iP)bOr^_BC<)=(f{;rvrs zE?4hi05vPQlcIZMz;aLC19Jv*zJD(WGaftb9Osah7IW| z&oiX_(D_hiOuDMdis+qjqy4Gvjx08$+|+qjW(0-by_Ov$9@A7}ymc{^U#DD0$e}Wg zdR_9TfhsiovAx6?%yoDns;zVE|JwdfonhMEoA97vp`FZNk~yw9nrah)JV-3CsNWSdjHHwvC-&LffWh$poW$rG7K90pYkIGnvi=9?;CcKQXW6uI)ZPY zt@T-#Bsb4#~m0rL$-lB6<8IscYb?Fq+D>1ONc=!V$0M`_!TxCeng%d z%yJI&Y^F;{c!`-8)zdI^eYy%4knA8`eWk;0`lavXSWwLgn>f26`i({?_yow+@3)^K z{;)negS0~$xc7))8o2f~<^)K{?<#(c9spRwEp9 z1xNUvzDq3u*PY#g(Rz$EZ)bQZeA=0xdHb?p+L`<{rnHOSZ+Im$L_-hA*y%n`L@#v#Q)u{6Yj9-H$!tU{U{Luu1@2WCrA)GZXJ& z7<>%#6G>~N(4TgJ-DZ}85P(`yMjdlTxW|4Q?wWykSk74i^05W5UcmVH0&agk1pt@f za5KkG%Y9ut$Ilqk|FzRp*MYTLrp+ow15uVK!{3xYgjPOybpkBw(Fd4gk`>AYOo>G>RCLA>l#9{~8H1O5{(^x14j zIi~YkeQ^QvBsM|oSipY-50miq6jSX>@8HKe2KyjW?Ry>6(heNg&`P-fr>q@&q$9{( zW)WjhQq|~*2HP>sfVW;XtyV!-G+)n!>bs2qW!6}vP2x-ST?anZ)pqm0e*d{vsI+0yOTPB^aY)XfIZJ7 zRNyri88_+SnagB~ybCLwfqI#Mi^%_P??(~BPa;pjzu{Lgidyh%>CM+!H(glH)J0E$ zDBpknN@lU*GG0;^folN6wS+$ zxgN(H4!WQ}Tev;PA%3P+xYS4)iEoOW^5&4QLP~;XJx7Q2)HEXkxn`t}*oq<>W^$#5<(^f-^k^r;o9QnJ4>9ef*ys2ug4qhg`>JdhG zhoYz0+n|I@Ylme2mu|n<5Cm>eY}9kxF9NK6l6~q7wx6nZ+`!l<`j2=<@Jn42hNDtw zgMXm8>J_C%T7X5~SFeX;>@EJcypj*u-dOdh|1nr4Tth}f#vkV~`17P8BY&F4u%TH+ z*#h4koM&KbTd(!&ym1z-D2*r$-)UU!CE>tcXO-a3)_>bi^A=NwUAr8+K~=)Bs&EFv zjr2)9RsD~P*z7S(^n|{5XPstb0PKuEKKcqi1xLe-bdCJSNxk=>hW!J6-m!n25Vc%@n`dJhQ;~6UNXKe*dphzl7G|8!`Defd3=T$|PrQ%84^jS$CO<^s zFxE@k^Bm~P-E4xjvo4qBzdQ7lgd1d%mJaQ4h8!^bkEQJ)0wRBY^mEETuaepyV#pde zDDK$TY3eoJ45Qv{gauFgFgsx6M=^Y9iOl<^o{S2GML^dQ$e zVyKX3450{3ZfLxV2q7jJW0%ahZX$V(F*90eLQ63>k}P6~yjG>FJ2xa3-63Tyzkhyz z{Jv|Sb$)A|z4qDX``M>*w_VUAZ}#fq^!AR91YT%fWMOI1o{C_Gaw|PDeImX-a%v^A zt@A1U)MS?19B{=>;^T;}1m`J7jZ82Wea6q&tEru5KbBU7bt5>->-R+FU3R(ArgcL! zdp0F6!ysr9BWE4>E7QBJid(kV-r4>+o;W)seMZc-~g&-gP1LkvFA2t|wJv4On91nAgpe{JTEEA>&15 zN(63l)gPq#FTx6rJTl< zASQLmca;2^WRC{b8j^T=!C2qQxA*6CWoNEXuP{Oj@7*GKSR`dz6uhV%zF*?@j1XZN zr*kl>hnicyAivl?!{sJFBY4KTcu*|SEJ~${p?l$bNoZc>rlOskt zu`wN0gvEXzduVHz(*of$7MT#Y1dkgsPTy}vV-}0sjJ~k^?+%UnrDNQcVHtNQ?;wVm zJA_Jgj*(y;g(vx* z=c{kKRNU~*U+`wsro!lO>RE>aa{+{%ZDGaQT=GkO^*=nAevhvW#|H9Umq{~k7sF@7 zKTfgw+hFE6B0KN_&K_<%XnSkCz_iz|)|{eY6QEL3oxI!FO5JE_T%7%-FZsIEp=5c@ zbM~k^d}ozF@kbKUriQUS-XqsCN-UCG_-u#PYO0>uO}uB2*YVgKf0dA$Azi#?TPHNK zYw*OarL<>@6VBI}D)HrqMOJn$S18jYJ@`0Mb^Iu{=R))Rm_)LkG7Xkc&uwXJs!^ip z^P^g&l^F?5I-W$^eJPhB9CvAJ7#1W6{|4*NKQVOu_iLEu5PEA}3bGwlJC>wv++jU` zOt~REXEwFF2xX_|bCulJF@~tL4^# z8oWRkQA!W%P;Zjg%+n5&vaRyXp!e&X?WK>)O@$hJO!q#M^wA`@yn_qwJ`|*fkDLk< zSeyY523|f1Jiw%wn2ZR(2luXdN2QPYG`Z-7;Mj21qhr&8z9&casxDEohFc^vt-rc? zxN{K$awh81GF_w{yrXp{!sGf$@D(DtyXeA-8UD!JK{F3-l_EBAT#;rxl=^kMVC4JT zJcnNlU6hkLRHuKS&H)C)*?|BCUFcT$!5jyDez!bvDvJ(F-8Fqe5r>_ z5rY>fm!j0bZ6*X!GNU2kF2#?SC4odHWQTYkMahm)0~1V02Z4+MPy=Lz7)=MRt65AD z6bLhtrVIfqRY(c^+aOf+>;gMj!fLq#2oPo@%4Myp02*149zxO=yk|jL=$~J34Zov^ zKduXd`JD#3Y+(mB_D$exlbf&-Z%)o2j-PzPresentX(0, nullptr, nullptr); + std::ignore = m_commandQueue->PresentX(0, nullptr, nullptr); } } @@ -83,6 +85,14 @@ void DeviceResources::CreateDeviceResources() params.ComputeScratchMemorySizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); #ifdef _GAMING_XBOX_SCARLETT params.CreateDeviceFlags = createDeviceFlags; + +#if (_GXDK_VER >= 0x585D070E /* GXDK Edition 221000 */) + if (m_options & c_AmplificationShaders) + { + params.AmplificationShaderIndirectArgsBufferSize = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); + params.AmplificationShaderPayloadBufferSize = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); + } +#endif #endif HRESULT hr = D3D12XboxCreateDevice( @@ -118,9 +128,12 @@ void DeviceResources::CreateDeviceResources() rtvDescriptorHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; ThrowIfFailed(m_d3dDevice->CreateDescriptorHeap(&rtvDescriptorHeapDesc, IID_GRAPHICS_PPV_ARGS(m_rtvDescriptorHeap.ReleaseAndGetAddressOf()))); + m_rtvDescriptorHeap->SetName(L"DeviceResources"); + m_rtvDescriptorSize = m_d3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV); + // Create UAV for backbuffer. D3D12_DESCRIPTOR_HEAP_DESC uavDescriptorHeapDesc = {}; uavDescriptorHeapDesc.NumDescriptors = m_backBufferCount; uavDescriptorHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; @@ -135,6 +148,7 @@ void DeviceResources::CreateDeviceResources() dsvDescriptorHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV; ThrowIfFailed(m_d3dDevice->CreateDescriptorHeap(&dsvDescriptorHeapDesc, IID_GRAPHICS_PPV_ARGS(m_dsvDescriptorHeap.ReleaseAndGetAddressOf()))); + m_dsvDescriptorHeap->SetName(L"DeviceResources"); } @@ -151,11 +165,13 @@ void DeviceResources::CreateDeviceResources() // Create a command list for recording graphics commands. ThrowIfFailed(m_d3dDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, m_commandAllocators[0].Get(), nullptr, IID_GRAPHICS_PPV_ARGS(m_commandList.ReleaseAndGetAddressOf()))); ThrowIfFailed(m_commandList->Close()); + m_commandList->SetName(L"DeviceResources"); // Create a fence for tracking GPU execution progress. ThrowIfFailed(m_d3dDevice->CreateFence(m_fenceValues[m_backBufferIndex], D3D12_FENCE_FLAG_NONE, IID_GRAPHICS_PPV_ARGS(m_fence.ReleaseAndGetAddressOf()))); m_fenceValues[m_backBufferIndex]++; + m_fence->SetName(L"DeviceResources"); m_fenceEvent.Attach(CreateEventEx(nullptr, nullptr, 0, EVENT_MODIFY_STATE | SYNCHRONIZE)); @@ -231,7 +247,7 @@ void DeviceResources::CreateWindowSizeDependentResources() // Obtain the back buffers for this window which will be the final render targets // and create render target views for each of them. - CD3DX12_HEAP_PROPERTIES swapChainHeapProperties(D3D12_HEAP_TYPE_DEFAULT); + const CD3DX12_HEAP_PROPERTIES swapChainHeapProperties(D3D12_HEAP_TYPE_DEFAULT); D3D12_RESOURCE_DESC swapChainBufferDesc = CD3DX12_RESOURCE_DESC::Tex2D( m_backBufferFormat, @@ -245,8 +261,11 @@ void DeviceResources::CreateWindowSizeDependentResources() swapChainBufferDesc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; #ifdef _GAMING_XBOX_XBOXONE - // For Xbox One, a title needs to specify the auto tone mapping flag, but not on Scarlett - swapChainBufferDesc.Flags |= D3D12XBOX_RESOURCE_FLAG_ALLOW_AUTOMATIC_GAMEDVR_TONE_MAP; + if (m_options & c_EnableHDR) + { + // For Xbox One, a title needs to specify the auto tone mapping flag, but not on Xbox Series X|S + swapChainBufferDesc.Flags |= D3D12XBOX_RESOURCE_FLAG_ALLOW_AUTOMATIC_GAMEDVR_TONE_MAP; + } #endif D3D12_CLEAR_VALUE swapChainOptimizedClearValue = {}; @@ -260,22 +279,22 @@ void DeviceResources::CreateWindowSizeDependentResources() &swapChainBufferDesc, D3D12_RESOURCE_STATE_PRESENT, &swapChainOptimizedClearValue, - IID_GRAPHICS_PPV_ARGS(m_renderTargets[n].ReleaseAndGetAddressOf()))); + IID_GRAPHICS_PPV_ARGS(m_renderTargets[n].GetAddressOf()))); wchar_t name[25] = {}; - swprintf_s(name, L"SwapBuffer %u", n); + swprintf_s(name, L"Render target %u", n); m_renderTargets[n]->SetName(name); D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {}; rtvDesc.Format = m_backBufferFormat; rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; - CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( + const CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( m_rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), static_cast(n), m_rtvDescriptorSize); m_d3dDevice->CreateRenderTargetView(m_renderTargets[n].Get(), &rtvDesc, rtvDescriptor); - CD3DX12_CPU_DESCRIPTOR_HANDLE uavDescriptor( + const CD3DX12_CPU_DESCRIPTOR_HANDLE uavDescriptor( m_uavDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), static_cast(n), m_uavDescriptorSize); @@ -289,7 +308,7 @@ void DeviceResources::CreateWindowSizeDependentResources() { // Allocate a 2-D surface as the depth/stencil buffer and create a depth/stencil view // on this surface. - CD3DX12_HEAP_PROPERTIES depthHeapProperties(D3D12_HEAP_TYPE_DEFAULT); + const CD3DX12_HEAP_PROPERTIES depthHeapProperties(D3D12_HEAP_TYPE_DEFAULT); D3D12_RESOURCE_DESC depthStencilDesc = CD3DX12_RESOURCE_DESC::Tex2D( m_depthBufferFormat, @@ -302,7 +321,7 @@ void DeviceResources::CreateWindowSizeDependentResources() D3D12_CLEAR_VALUE depthOptimizedClearValue = {}; depthOptimizedClearValue.Format = m_depthBufferFormat; - depthOptimizedClearValue.DepthStencil.Depth = 1.0f; + depthOptimizedClearValue.DepthStencil.Depth = (m_options & c_ReverseDepth) ? 0.0f : 1.0f; depthOptimizedClearValue.DepthStencil.Stencil = 0; ThrowIfFailed(m_d3dDevice->CreateCommittedResource( @@ -338,10 +357,6 @@ void DeviceResources::CreateWindowSizeDependentResources() // Prepare the command list and render target for rendering. void DeviceResources::Prepare(D3D12_RESOURCE_STATES beforeState, D3D12_RESOURCE_STATES afterState) { - // Wait until frame start is signaled - m_framePipelineToken = D3D12XBOX_FRAME_PIPELINE_TOKEN_NULL; - ThrowIfFailed(m_d3dDevice->WaitFrameEventX(D3D12XBOX_FRAME_EVENT_ORIGIN, INFINITE, nullptr, D3D12XBOX_WAIT_FRAME_EVENT_FLAG_NONE, &m_framePipelineToken)); - // Reset command list and allocator. ThrowIfFailed(m_commandAllocators[m_backBufferIndex]->Reset()); ThrowIfFailed(m_commandList->Reset(m_commandAllocators[m_backBufferIndex].Get(), nullptr)); @@ -349,7 +364,8 @@ void DeviceResources::Prepare(D3D12_RESOURCE_STATES beforeState, D3D12_RESOURCE_ if (beforeState != afterState) { // Transition the render target into the correct state to allow for drawing into it. - D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), + const D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition( + m_renderTargets[m_backBufferIndex].Get(), beforeState, afterState); m_commandList->ResourceBarrier(1, &barrier); } @@ -361,7 +377,9 @@ void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState, _In_opt_ const if (beforeState != D3D12_RESOURCE_STATE_PRESENT) { // Transition the render target to the state that allows it to be presented to the display. - D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), beforeState, D3D12_RESOURCE_STATE_PRESENT); + const D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition( + m_renderTargets[m_backBufferIndex].Get(), + beforeState, D3D12_RESOURCE_STATE_PRESENT); m_commandList->ResourceBarrier(1, &barrier); } @@ -392,9 +410,10 @@ void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState, _In_opt_ const m_commandQueue->PresentX(1, &planeParameters, params) ); - // Xbox One apps do not need to handle DXGI_ERROR_DEVICE_REMOVED or DXGI_ERROR_DEVICE_RESET. + // Xbox apps do not need to handle DXGI_ERROR_DEVICE_REMOVED or DXGI_ERROR_DEVICE_RESET. - MoveToNextFrame(); + // Update the back buffer index. + m_backBufferIndex = (m_backBufferIndex + 1) % m_backBufferCount; } // Handle GPU suspend/resume @@ -416,13 +435,13 @@ void DeviceResources::WaitForGpu() noexcept if (m_commandQueue && m_fence && m_fenceEvent.IsValid()) { // Schedule a Signal command in the GPU queue. - UINT64 fenceValue = m_fenceValues[m_backBufferIndex]; + const UINT64 fenceValue = m_fenceValues[m_backBufferIndex]; if (SUCCEEDED(m_commandQueue->Signal(m_fence.Get(), fenceValue))) { // Wait until the Signal has been processed. if (SUCCEEDED(m_fence->SetEventOnCompletion(fenceValue, m_fenceEvent.Get()))) { - WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); + std::ignore = WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); // Increment the fence value for the current frame. m_fenceValues[m_backBufferIndex]++; @@ -431,25 +450,17 @@ void DeviceResources::WaitForGpu() noexcept } } -// Prepare to render the next frame. -void DeviceResources::MoveToNextFrame() +// For PresentX rendering, we should wait for the origin event just before processing input. +void DeviceResources::WaitForOrigin() { - // Schedule a Signal command in the queue. - const UINT64 currentFenceValue = m_fenceValues[m_backBufferIndex]; - ThrowIfFailed(m_commandQueue->Signal(m_fence.Get(), currentFenceValue)); - - // Update the back buffer index. - m_backBufferIndex = (m_backBufferIndex + 1) % m_backBufferCount; - - // If the next frame is not ready to be rendered yet, wait until it is ready. - if (m_fence->GetCompletedValue() < m_fenceValues[m_backBufferIndex]) - { - ThrowIfFailed(m_fence->SetEventOnCompletion(m_fenceValues[m_backBufferIndex], m_fenceEvent.Get())); - WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); - } - - // Set the fence value for the next frame. - m_fenceValues[m_backBufferIndex] = currentFenceValue + 1; + // Wait until frame start is signaled + m_framePipelineToken = D3D12XBOX_FRAME_PIPELINE_TOKEN_NULL; + ThrowIfFailed(m_d3dDevice->WaitFrameEventX( + D3D12XBOX_FRAME_EVENT_ORIGIN, + INFINITE, + nullptr, + D3D12XBOX_WAIT_FRAME_EVENT_FLAG_NONE, + &m_framePipelineToken)); } // Set frame interval and register for frame events diff --git a/Samples/Graphics/SimpleHDR/DeviceResources.h b/Samples/Graphics/SimpleHDR/DeviceResources.h index 4f56d78..223fe56 100644 --- a/Samples/Graphics/SimpleHDR/DeviceResources.h +++ b/Samples/Graphics/SimpleHDR/DeviceResources.h @@ -1,6 +1,8 @@ // // DeviceResources.h - A wrapper for the Direct3D 12.X device and swapchain // +// Modified to expose a UAV for the swap buffer. +// #pragma once @@ -10,9 +12,11 @@ namespace DX class DeviceResources { public: - static constexpr unsigned int c_Enable4K_UHD = 0x1; - static constexpr unsigned int c_EnableQHD = 0x2; - static constexpr unsigned int c_EnableHDR = 0x4; + static constexpr unsigned int c_Enable4K_UHD = 0x1; + static constexpr unsigned int c_EnableQHD = 0x2; + static constexpr unsigned int c_EnableHDR = 0x4; + static constexpr unsigned int c_ReverseDepth = 0x8; + static constexpr unsigned int c_AmplificationShaders = 0x10; DeviceResources(DXGI_FORMAT backBufferFormat = DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT depthBufferFormat = DXGI_FORMAT_D32_FLOAT, @@ -40,6 +44,7 @@ namespace DX void Suspend(); void Resume(); void WaitForGpu() noexcept; + void WaitForOrigin(); // Device Accessors. RECT GetOutputSize() const noexcept { return m_outputSize; } @@ -80,7 +85,6 @@ namespace DX } private: - void MoveToNextFrame(); void RegisterFrameEvents(); static constexpr size_t MAX_BACK_BUFFER_COUNT = 3; diff --git a/Samples/Graphics/SimpleHDR/Main.cpp b/Samples/Graphics/SimpleHDR/Main.cpp index 9cc14da..9958296 100644 --- a/Samples/Graphics/SimpleHDR/Main.cpp +++ b/Samples/Graphics/SimpleHDR/Main.cpp @@ -1,7 +1,7 @@ //-------------------------------------------------------------------------------------- // Main.cpp // -// Entry point for Microsoft Game Core on Xbox +// Entry point for Microsoft GDK with Xbox extensions // // Advanced Technology Group (ATG) // Copyright (C) Microsoft Corporation. All rights reserved. @@ -56,7 +56,7 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp // Default main thread to CPU 0 SetThreadAffinityMask(GetCurrentThread(), 0x1); - // Microsoft Game Core on Xbox supports UTF-8 everywhere + // Microsoft GDKX supports UTF-8 everywhere assert(GetACP() == CP_UTF8); g_sample = std::make_unique(); @@ -111,7 +111,7 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp PostMessage(reinterpret_cast(context), WM_USER, 0, 0); // To defer suspend, you must wait to exit this callback - (void)WaitForSingleObject(g_plmSuspendComplete, INFINITE); + std::ignore = WaitForSingleObject(g_plmSuspendComplete, INFINITE); } else { @@ -171,7 +171,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) // Complete deferral SetEvent(g_plmSuspendComplete); - (void)WaitForSingleObject(g_plmSignalResume, INFINITE); + std::ignore = WaitForSingleObject(g_plmSignalResume, INFINITE); sample->OnResuming(); } diff --git a/Samples/Graphics/SimpleHDR/SimpleHDR.cpp b/Samples/Graphics/SimpleHDR/SimpleHDR.cpp index 7147e0d..8feb539 100644 --- a/Samples/Graphics/SimpleHDR/SimpleHDR.cpp +++ b/Samples/Graphics/SimpleHDR/SimpleHDR.cpp @@ -50,7 +50,9 @@ Sample::Sample() noexcept(false) : // On Xbox One, 10:10:10:2 is required for HDR m_deviceResources = std::make_unique(DXGI_FORMAT_R10G10B10A2_UNORM, #endif - DXGI_FORMAT_UNKNOWN, 2, DX::DeviceResources::c_Enable4K_UHD | DX::DeviceResources::c_EnableQHD | DX::DeviceResources::c_EnableHDR); + DXGI_FORMAT_UNKNOWN, + 2, + DX::DeviceResources::c_Enable4K_UHD | DX::DeviceResources::c_EnableQHD | DX::DeviceResources::c_EnableHDR); } // Initialize the Direct3D resources required to run. @@ -67,7 +69,7 @@ void Sample::Initialize(HWND window) CreateWindowSizeDependentResources(); // Try to switch the display into HDR mode - TryEnableHDR(); + SetDisplayMode(); // Render all UI at 1080p so that it's easy to switch between 1080p/1440p/4K auto viewportUI = m_deviceResources->GetScreenViewport(); @@ -82,6 +84,8 @@ void Sample::Tick() { PIXBeginEvent(PIX_COLOR_DEFAULT, L"Frame %llu", m_frame); + m_deviceResources->WaitForOrigin(); + m_timer.Tick([&]() { Update(m_timer); @@ -164,12 +168,12 @@ void Sample::Clear() TransitionResource(commandList, m_hdrScene.Get(), D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_RENDER_TARGET); - auto rtvDescriptor = m_rtvPile->GetCpuHandle(HDRSceneRTV); + auto const rtvDescriptor = m_rtvPile->GetCpuHandle(HDRSceneRTV); commandList->OMSetRenderTargets(1, &rtvDescriptor, FALSE, nullptr); commandList->ClearRenderTargetView(rtvDescriptor, ATG::Colors::Background, 0, nullptr); - auto viewport = m_deviceResources->GetScreenViewport(); - auto scissorRect = m_deviceResources->GetScissorRect(); + auto const viewport = m_deviceResources->GetScreenViewport(); + auto const scissorRect = m_deviceResources->GetScissorRect(); commandList->RSSetViewports(1, &viewport); commandList->RSSetScissorRects(1, &scissorRect); @@ -180,11 +184,11 @@ void Sample::RenderUI(ID3D12GraphicsCommandList* commandList) { PIXBeginEvent(commandList, PIX_COLOR_DEFAULT, L"RenderUI"); - auto rtvDescriptor = m_rtvPile->GetCpuHandle(HDRSceneRTV); + auto const rtvDescriptor = m_rtvPile->GetCpuHandle(HDRSceneRTV); commandList->OMSetRenderTargets(1, &rtvDescriptor, FALSE, nullptr); - auto viewport = m_deviceResources->GetScreenViewport(); - auto scissorRect = m_deviceResources->GetScissorRect(); + auto const viewport = m_deviceResources->GetScreenViewport(); + auto const scissorRect = m_deviceResources->GetScissorRect(); commandList->RSSetViewports(1, &viewport); commandList->RSSetScissorRects(1, &scissorRect); @@ -229,7 +233,7 @@ void Sample::FinalHDRShader(ID3D12GraphicsCommandList* commandList) PIXEndEvent(commandList); } -void Sample::TryEnableHDR() +void Sample::SetDisplayMode() { if ((m_deviceResources->GetDeviceOptions() & DX::DeviceResources::c_EnableHDR) != 0 ) { @@ -256,7 +260,7 @@ void Sample::OnSuspending() void Sample::OnResuming() { // While a title is suspended, the console TV settings could have changed, so we need to call the display APIs when resuming - TryEnableHDR(); + SetDisplayMode(); m_deviceResources->Resume(); m_timer.ResetElapsedTime(); @@ -270,7 +274,7 @@ void Sample::OnConstrained() void Sample::OnUnConstrained() { // While a title is constrained, the console TV settings could have changed, so we need to call the display APIs when unconstraining - TryEnableHDR(); + SetDisplayMode(); } #pragma endregion @@ -285,7 +289,7 @@ void Sample::CreateDeviceDependentResources() // Use a primitive batch to draw a simple triangle { - RenderTargetState rtState(g_hdrBackBufferFormat, m_deviceResources->GetDepthBufferFormat()); + const RenderTargetState rtState(g_hdrBackBufferFormat, m_deviceResources->GetDepthBufferFormat()); m_batch = std::make_unique>(device); EffectPipelineStateDescription pd(&VertexPositionColor::InputLayout, CommonStates::Opaque, diff --git a/Samples/Graphics/SimpleHDR/SimpleHDR.h b/Samples/Graphics/SimpleHDR/SimpleHDR.h index 82d8781..e4a4c6c 100644 --- a/Samples/Graphics/SimpleHDR/SimpleHDR.h +++ b/Samples/Graphics/SimpleHDR/SimpleHDR.h @@ -52,7 +52,7 @@ private: void CreateDeviceDependentResources(); void CreateWindowSizeDependentResources(); - void TryEnableHDR(); + void SetDisplayMode(); // Device resources. std::unique_ptr m_deviceResources; diff --git a/Samples/Graphics/SimpleHDR/StepTimer.h b/Samples/Graphics/SimpleHDR/StepTimer.h index bb15fff..2dbefcb 100644 --- a/Samples/Graphics/SimpleHDR/StepTimer.h +++ b/Samples/Graphics/SimpleHDR/StepTimer.h @@ -111,7 +111,7 @@ namespace DX timeDelta *= TicksPerSecond; timeDelta /= static_cast(m_qpcFrequency.QuadPart); - uint32_t lastFrameCount = m_frameCount; + const uint32_t lastFrameCount = m_frameCount; if (m_isFixedTimeStep) { diff --git a/Samples/Graphics/SimpleHDR/pch.h b/Samples/Graphics/SimpleHDR/pch.h index 8ba561c..28f8814 100644 --- a/Samples/Graphics/SimpleHDR/pch.h +++ b/Samples/Graphics/SimpleHDR/pch.h @@ -37,8 +37,8 @@ #include -#if _GXDK_VER < 0x4A610F3C /* GDK Edition 200800 */ -#error This sample requires the August 2020 GDK or later +#if _GXDK_VER < 0x55F007B0 /* GDK Edition 211000 */ +#error This sample requires the October 2021 GDK or later #endif #ifdef _GAMING_XBOX_SCARLETT @@ -69,6 +69,7 @@ #include #include #include +#include #include #include diff --git a/Samples/Graphics/SimpleMeshlet/DeviceResources.cpp b/Samples/Graphics/SimpleMeshlet/DeviceResources.cpp index 81b00e1..2b40774 100644 --- a/Samples/Graphics/SimpleMeshlet/DeviceResources.cpp +++ b/Samples/Graphics/SimpleMeshlet/DeviceResources.cpp @@ -12,7 +12,6 @@ using namespace DX; using Microsoft::WRL::ComPtr; -#ifdef _GAMING_DESKTOP #ifdef __clang__ #pragma clang diagnostic ignored "-Wcovered-switch-default" #pragma clang diagnostic ignored "-Wswitch-enum" @@ -20,6 +19,7 @@ using Microsoft::WRL::ComPtr; #pragma warning(disable : 4061) +#ifdef _GAMING_DESKTOP namespace { inline DXGI_FORMAT NoSRGB(DXGI_FORMAT fmt) noexcept @@ -39,7 +39,8 @@ namespace DeviceResources::DeviceResources( DXGI_FORMAT backBufferFormat, DXGI_FORMAT depthBufferFormat, - UINT backBufferCount) noexcept(false) : + UINT backBufferCount, + unsigned int flags) noexcept(false) : m_backBufferIndex(0), m_fenceValues{}, #ifdef _GAMING_XBOX @@ -54,6 +55,7 @@ DeviceResources::DeviceResources( m_window(nullptr), m_d3dFeatureLevel(D3D_FEATURE_LEVEL_11_0), m_outputSize{ 0, 0, 1, 1 }, + m_options(flags), m_deviceNotify(nullptr) { if (backBufferCount < 2 || backBufferCount > MAX_BACK_BUFFER_COUNT) @@ -72,7 +74,7 @@ DeviceResources::~DeviceResources() // Ensure we present a blank screen before cleaning up resources. if (m_commandQueue) { - (void)m_commandQueue->PresentX(0, nullptr, nullptr); + std::ignore = m_commandQueue->PresentX(0, nullptr, nullptr); } #endif } @@ -98,6 +100,14 @@ void DeviceResources::CreateDeviceResources() params.GraphicsScratchMemorySizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); params.ComputeScratchMemorySizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); +#if defined(_GAMING_XBOX_SCARLETT) && (_GRDK_VER >= 0x585D070E /* GXDK Edition 221000 */) + if (m_options & c_AmplificationShaders) + { + params.AmplificationShaderIndirectArgsBufferSize = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); + params.AmplificationShaderPayloadBufferSize = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); + } +#endif + HRESULT hr = D3D12XboxCreateDevice( nullptr, ¶ms, @@ -328,7 +338,7 @@ void DeviceResources::CreateWindowSizeDependentResources() // Obtain the back buffers for this window which will be the final render targets // and create render target views for each of them. - CD3DX12_HEAP_PROPERTIES swapChainHeapProperties(D3D12_HEAP_TYPE_DEFAULT); + const CD3DX12_HEAP_PROPERTIES swapChainHeapProperties(D3D12_HEAP_TYPE_DEFAULT); D3D12_RESOURCE_DESC swapChainBufferDesc = CD3DX12_RESOURCE_DESC::Tex2D( m_backBufferFormat, @@ -360,7 +370,7 @@ void DeviceResources::CreateWindowSizeDependentResources() rtvDesc.Format = m_backBufferFormat; rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; - CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( + const CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( m_rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), static_cast(n), m_rtvDescriptorSize); m_d3dDevice->CreateRenderTargetView(m_renderTargets[n].Get(), &rtvDesc, rtvDescriptor); @@ -371,7 +381,7 @@ void DeviceResources::CreateWindowSizeDependentResources() #else // _GAMING_DESKTOP - DXGI_FORMAT backBufferFormat = NoSRGB(m_backBufferFormat); + const DXGI_FORMAT backBufferFormat = NoSRGB(m_backBufferFormat); // If the swap chain already exists, resize it, otherwise create one. if (m_swapChain) @@ -454,7 +464,7 @@ void DeviceResources::CreateWindowSizeDependentResources() rtvDesc.Format = m_backBufferFormat; rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; - CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( + const CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( m_rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), static_cast(n), m_rtvDescriptorSize); m_d3dDevice->CreateRenderTargetView(m_renderTargets[n].Get(), &rtvDesc, rtvDescriptor); @@ -469,7 +479,7 @@ void DeviceResources::CreateWindowSizeDependentResources() { // Allocate a 2-D surface as the depth/stencil buffer and create a depth/stencil view // on this surface. - CD3DX12_HEAP_PROPERTIES depthHeapProperties(D3D12_HEAP_TYPE_DEFAULT); + const CD3DX12_HEAP_PROPERTIES depthHeapProperties(D3D12_HEAP_TYPE_DEFAULT); D3D12_RESOURCE_DESC depthStencilDesc = CD3DX12_RESOURCE_DESC::Tex2D( m_depthBufferFormat, @@ -482,7 +492,7 @@ void DeviceResources::CreateWindowSizeDependentResources() D3D12_CLEAR_VALUE depthOptimizedClearValue = {}; depthOptimizedClearValue.Format = m_depthBufferFormat; - depthOptimizedClearValue.DepthStencil.Depth = 1.0f; + depthOptimizedClearValue.DepthStencil.Depth = (m_options & c_ReverseDepth) ? 0.0f : 1.0f; depthOptimizedClearValue.DepthStencil.Stencil = 0; ThrowIfFailed(m_d3dDevice->CreateCommittedResource( @@ -593,12 +603,6 @@ void DeviceResources::HandleDeviceLost() // Prepare the command list and render target for rendering. void DeviceResources::Prepare(D3D12_RESOURCE_STATES beforeState, D3D12_RESOURCE_STATES afterState) { -#ifdef _GAMING_XBOX - // Wait until frame start is signaled - m_framePipelineToken = D3D12XBOX_FRAME_PIPELINE_TOKEN_NULL; - ThrowIfFailed(m_d3dDevice->WaitFrameEventX(D3D12XBOX_FRAME_EVENT_ORIGIN, INFINITE, nullptr, D3D12XBOX_WAIT_FRAME_EVENT_FLAG_NONE, &m_framePipelineToken)); -#endif - // Reset command list and allocator. ThrowIfFailed(m_commandAllocators[m_backBufferIndex]->Reset()); ThrowIfFailed(m_commandList->Reset(m_commandAllocators[m_backBufferIndex].Get(), nullptr)); @@ -606,7 +610,8 @@ void DeviceResources::Prepare(D3D12_RESOURCE_STATES beforeState, D3D12_RESOURCE_ if (beforeState != afterState) { // Transition the render target into the correct state to allow for drawing into it. - D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), + const D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition( + m_renderTargets[m_backBufferIndex].Get(), beforeState, afterState); m_commandList->ResourceBarrier(1, &barrier); } @@ -618,7 +623,9 @@ void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState) if (beforeState != D3D12_RESOURCE_STATE_PRESENT) { // Transition the render target to the state that allows it to be presented to the display. - D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), beforeState, D3D12_RESOURCE_STATE_PRESENT); + const D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition( + m_renderTargets[m_backBufferIndex].Get(), + beforeState, D3D12_RESOURCE_STATE_PRESENT); m_commandList->ResourceBarrier(1, &barrier); } @@ -638,9 +645,12 @@ void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState) m_commandQueue->PresentX(1, &planeParameters, nullptr) ); - // Xbox One apps do not need to handle DXGI_ERROR_DEVICE_REMOVED or DXGI_ERROR_DEVICE_RESET. + // Xbox apps do not need to handle DXGI_ERROR_DEVICE_REMOVED or DXGI_ERROR_DEVICE_RESET. -#else + // Update the back buffer index. + m_backBufferIndex = (m_backBufferIndex + 1) % m_backBufferCount; + +#else // _GAMING_DESKTOP // The first argument instructs DXGI to block until VSync, putting the application // to sleep until the next VSync. This ensures we don't waste any cycles rendering @@ -663,9 +673,8 @@ void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState) ThrowIfFailed(hr); } -#endif - MoveToNextFrame(); +#endif } // Handle GPU suspend/resume @@ -691,13 +700,13 @@ void DeviceResources::WaitForGpu() noexcept if (m_commandQueue && m_fence && m_fenceEvent.IsValid()) { // Schedule a Signal command in the GPU queue. - UINT64 fenceValue = m_fenceValues[m_backBufferIndex]; + const UINT64 fenceValue = m_fenceValues[m_backBufferIndex]; if (SUCCEEDED(m_commandQueue->Signal(m_fence.Get(), fenceValue))) { // Wait until the Signal has been processed. if (SUCCEEDED(m_fence->SetEventOnCompletion(fenceValue, m_fenceEvent.Get()))) { - WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); + std::ignore = WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); // Increment the fence value for the current frame. m_fenceValues[m_backBufferIndex]++; @@ -706,32 +715,20 @@ void DeviceResources::WaitForGpu() noexcept } } -// Prepare to render the next frame. -void DeviceResources::MoveToNextFrame() -{ - // Schedule a Signal command in the queue. - const UINT64 currentFenceValue = m_fenceValues[m_backBufferIndex]; - ThrowIfFailed(m_commandQueue->Signal(m_fence.Get(), currentFenceValue)); - - // Update the back buffer index. #ifdef _GAMING_XBOX - m_backBufferIndex = (m_backBufferIndex + 1) % m_backBufferCount; -#else - m_backBufferIndex = m_swapChain->GetCurrentBackBufferIndex(); -#endif - - // If the next frame is not ready to be rendered yet, wait until it is ready. - if (m_fence->GetCompletedValue() < m_fenceValues[m_backBufferIndex]) - { - ThrowIfFailed(m_fence->SetEventOnCompletion(m_fenceValues[m_backBufferIndex], m_fenceEvent.Get())); - WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); - } - - // Set the fence value for the next frame. - m_fenceValues[m_backBufferIndex] = currentFenceValue + 1; +// For PresentX rendering, we should wait for the origin event just before processing input. +void DeviceResources::WaitForOrigin() +{ + // Wait until frame start is signaled + m_framePipelineToken = D3D12XBOX_FRAME_PIPELINE_TOKEN_NULL; + ThrowIfFailed(m_d3dDevice->WaitFrameEventX( + D3D12XBOX_FRAME_EVENT_ORIGIN, + INFINITE, + nullptr, + D3D12XBOX_WAIT_FRAME_EVENT_FLAG_NONE, + &m_framePipelineToken)); } -#ifdef _GAMING_XBOX // Set frame interval and register for frame events void DeviceResources::RegisterFrameEvents() { @@ -760,7 +757,30 @@ void DeviceResources::RegisterFrameEvents() nullptr, D3D12XBOX_SCHEDULE_FRAME_EVENT_FLAG_NONE)); } -#else + +#else // _GAMING_DESKTOP + +// Prepare to render the next frame. +void DeviceResources::MoveToNextFrame() +{ + // Schedule a Signal command in the queue. + const UINT64 currentFenceValue = m_fenceValues[m_backBufferIndex]; + ThrowIfFailed(m_commandQueue->Signal(m_fence.Get(), currentFenceValue)); + + // Update the back buffer index. + m_backBufferIndex = m_swapChain->GetCurrentBackBufferIndex(); + + // If the next frame is not ready to be rendered yet, wait until it is ready. + if (m_fence->GetCompletedValue() < m_fenceValues[m_backBufferIndex]) + { + ThrowIfFailed(m_fence->SetEventOnCompletion(m_fenceValues[m_backBufferIndex], m_fenceEvent.Get())); + std::ignore = WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); + } + + // Set the fence value for the next frame. + m_fenceValues[m_backBufferIndex] = currentFenceValue + 1; +} + // This method acquires the first available hardware adapter that supports Direct3D 12. // If no such adapter can be found, try WARP. Otherwise throw an exception. void DeviceResources::GetAdapter(IDXGIAdapter1** ppAdapter) diff --git a/Samples/Graphics/SimpleMeshlet/DeviceResources.h b/Samples/Graphics/SimpleMeshlet/DeviceResources.h index a920f13..0e0aeb0 100644 --- a/Samples/Graphics/SimpleMeshlet/DeviceResources.h +++ b/Samples/Graphics/SimpleMeshlet/DeviceResources.h @@ -22,9 +22,13 @@ namespace DX class DeviceResources { public: + static constexpr unsigned int c_ReverseDepth = 0x1; + static constexpr unsigned int c_AmplificationShaders = 0x2; + DeviceResources(DXGI_FORMAT backBufferFormat = DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT depthBufferFormat = DXGI_FORMAT_D32_FLOAT, - UINT backBufferCount = 2) noexcept(false); + UINT backBufferCount = 2, + unsigned int flags = 0) noexcept(false); ~DeviceResources(); DeviceResources(DeviceResources&&) = default; @@ -45,6 +49,9 @@ namespace DX void Suspend(); void Resume(); void WaitForGpu() noexcept; +#ifdef _GAMING_XBOX + void WaitForOrigin(); +#endif // Device Accessors. RECT GetOutputSize() const noexcept { return m_outputSize; } @@ -68,6 +75,7 @@ namespace DX D3D12_RECT GetScissorRect() const noexcept { return m_scissorRect; } UINT GetCurrentFrameIndex() const noexcept { return m_backBufferIndex; } UINT GetBackBufferCount() const noexcept { return m_backBufferCount; } + unsigned int GetDeviceOptions() const noexcept { return m_options; } CD3DX12_CPU_DESCRIPTOR_HANDLE GetRenderTargetView() const noexcept { @@ -81,10 +89,10 @@ namespace DX } private: - void MoveToNextFrame(); #ifdef _GAMING_XBOX void RegisterFrameEvents(); #else + void MoveToNextFrame(); void GetAdapter(IDXGIAdapter1** ppAdapter); #endif @@ -131,6 +139,9 @@ namespace DX D3D_FEATURE_LEVEL m_d3dFeatureLevel; RECT m_outputSize; + // DeviceResources options (see flags above) + unsigned int m_options; + // The IDeviceNotify can be held directly as it owns the DeviceResources. IDeviceNotify* m_deviceNotify; }; diff --git a/Samples/Graphics/SimpleMeshlet/Main.cpp b/Samples/Graphics/SimpleMeshlet/Main.cpp index 30a03d1..76e9d21 100644 --- a/Samples/Graphics/SimpleMeshlet/Main.cpp +++ b/Samples/Graphics/SimpleMeshlet/Main.cpp @@ -63,7 +63,7 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp if (hr == E_GAMERUNTIME_DLL_NOT_FOUND || hr == E_GAMERUNTIME_VERSION_MISMATCH) { #ifdef _GAMING_DESKTOP - (void)MessageBoxW(nullptr, L"Game Runtime is not installed on this system or needs updating.", g_szAppName, MB_ICONERROR | MB_OK); + std::ignore = MessageBoxW(nullptr, L"Game Runtime is not installed on this system or needs updating.", g_szAppName, MB_ICONERROR | MB_OK); #endif } return 1; @@ -142,7 +142,7 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp PostMessage(reinterpret_cast(context), WM_USER, 0, 0); // To defer suspend, you must wait to exit this callback - (void)WaitForSingleObject(g_plmSuspendComplete, INFINITE); + std::ignore = WaitForSingleObject(g_plmSuspendComplete, INFINITE); } else { @@ -240,7 +240,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) // Complete deferral SetEvent(g_plmSuspendComplete); - (void)WaitForSingleObject(g_plmSignalResume, INFINITE); + std::ignore = WaitForSingleObject(g_plmSignalResume, INFINITE); sample->OnResuming(); } @@ -254,7 +254,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) else { PAINTSTRUCT ps; - (void)BeginPaint(hWnd, &ps); + std::ignore = BeginPaint(hWnd, &ps); EndPaint(hWnd, &ps); } break; @@ -369,7 +369,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) } else { - SetWindowLongPtr(hWnd, GWL_STYLE, 0); + SetWindowLongPtr(hWnd, GWL_STYLE, WS_POPUP); SetWindowLongPtr(hWnd, GWL_EXSTYLE, WS_EX_TOPMOST); SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); diff --git a/Samples/Graphics/SimpleMeshlet/ReadMe.docx b/Samples/Graphics/SimpleMeshlet/ReadMe.docx index 3bae7845f7bf40d44159bae516a3c9b112fd4683..6afa80205c8bc07c788a247128dd538433037ee8 100644 GIT binary patch delta 33899 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 F007QZWrqL& diff --git a/Samples/Graphics/SimpleMeshlet/SimpleMeshlet.cpp b/Samples/Graphics/SimpleMeshlet/SimpleMeshlet.cpp index 7fbedae..3ce0fd8 100644 --- a/Samples/Graphics/SimpleMeshlet/SimpleMeshlet.cpp +++ b/Samples/Graphics/SimpleMeshlet/SimpleMeshlet.cpp @@ -44,7 +44,7 @@ namespace const wchar_t* s_meshFilenames[] = { -#if _GAMING_DESKTOP +#ifdef _GAMING_DESKTOP L"ATGDragon\\Dragon_LOD0.sdkmesh", L"ATGDragon\\Dragon_LOD1.sdkmesh", L"ATGDragon\\Dragon_LOD2.sdkmesh", @@ -63,8 +63,8 @@ namespace #endif }; - const uint32_t s_dragonLODStart = 0; - const uint32_t s_dragonLODCount = 6; + constexpr uint32_t s_dragonLODStart = 0; + constexpr uint32_t s_dragonLODCount = 6; struct ObjectDefinition { @@ -88,8 +88,7 @@ namespace } Sample::Sample() noexcept(false) - : m_deviceResources(std::make_unique()) - , m_displayWidth(0) + : m_displayWidth(0) , m_displayHeight(0) , m_frame(0) , m_cull(false) @@ -97,6 +96,7 @@ Sample::Sample() noexcept(false) , m_drawMeshlets(true) , m_lodIndex(s_dragonLODStart) { + m_deviceResources = std::make_unique(); m_deviceResources->RegisterDeviceNotify(this); } @@ -112,7 +112,9 @@ Sample::~Sample() void Sample::Initialize(HWND window, int width, int height) { m_gamePad = std::make_unique(); + m_keyboard = std::make_unique(); + m_mouse = std::make_unique(); m_mouse->SetWindow(window); @@ -131,6 +133,10 @@ void Sample::Tick() { PIXBeginEvent(PIX_COLOR_DEFAULT, L"Frame %llu", m_frame); +#ifdef _GAMING_XBOX + m_deviceResources->WaitForOrigin(); +#endif + m_timer.Tick([&]() { Update(m_timer); @@ -279,8 +285,8 @@ void Sample::Render() UpdateConstants(commandList); // Set up the root signature & pipeline state - ID3D12DescriptorHeap* descriptorHeaps[] = { m_srvPile->Heap() }; - commandList->SetDescriptorHeaps(1, descriptorHeaps); + auto descriptorHeaps = m_srvPile->Heap(); + commandList->SetDescriptorHeaps(1, &descriptorHeaps); commandList->SetGraphicsRootSignature(m_rootSignature.Get()); commandList->SetPipelineState(m_cull ? m_cullMeshletPso.Get() : m_basicMeshletPso.Get()); @@ -381,16 +387,16 @@ void Sample::Clear() PIXBeginEvent(commandList, PIX_COLOR_DEFAULT, L"Clear"); // Clear the views. - auto rtvDescriptor = m_deviceResources->GetRenderTargetView(); - auto dsvDescriptor = m_deviceResources->GetDepthStencilView(); + auto const rtvDescriptor = m_deviceResources->GetRenderTargetView(); + auto const dsvDescriptor = m_deviceResources->GetDepthStencilView(); commandList->OMSetRenderTargets(1, &rtvDescriptor, FALSE, &dsvDescriptor); commandList->ClearRenderTargetView(rtvDescriptor, ATG::Colors::Background, 0, nullptr); commandList->ClearDepthStencilView(dsvDescriptor, D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr); // Set the viewport and scissor rect. - auto viewport = m_deviceResources->GetScreenViewport(); - auto scissorRect = m_deviceResources->GetScissorRect(); + auto const viewport = m_deviceResources->GetScreenViewport(); + auto const scissorRect = m_deviceResources->GetScissorRect(); commandList->RSSetViewports(1, &viewport); commandList->RSSetScissorRects(1, &scissorRect); @@ -401,7 +407,7 @@ void Sample::DrawHUD(ID3D12GraphicsCommandList* commandList) { m_hudBatch->Begin(commandList); - auto safe = SimpleMath::Viewport::ComputeTitleSafeArea(m_displayWidth, m_displayHeight); + auto const safe = SimpleMath::Viewport::ComputeTitleSafeArea(m_displayWidth, m_displayHeight); wchar_t textBuffer[128] = {}; XMFLOAT2 textPos = XMFLOAT2(float(safe.left), float(safe.top)); @@ -482,14 +488,6 @@ void Sample::DrawHUD(ID3D12GraphicsCommandList* commandList) #pragma region Message Handlers // Message handlers -void Sample::OnActivated() -{ -} - -void Sample::OnDeactivated() -{ -} - void Sample::OnSuspending() { m_deviceResources->Suspend(); @@ -505,7 +503,7 @@ void Sample::OnResuming() void Sample::OnWindowMoved() { - auto r = m_deviceResources->GetOutputSize(); + auto const r = m_deviceResources->GetOutputSize(); m_deviceResources->WindowSizeChanged(r.right, r.bottom); } @@ -554,15 +552,13 @@ void Sample::CreateDeviceDependentResources() // Create heap m_srvPile = std::make_unique(device, - D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, - D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, 128, DescriptorHeapIndex::SRV_Count); m_frustumDraw.CreateDeviceResources(*m_deviceResources, *m_commonStates); { - auto defaultHeapProps = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT); + const CD3DX12_HEAP_PROPERTIES defaultHeapProps(D3D12_HEAP_TYPE_DEFAULT); auto cbDesc = CD3DX12_RESOURCE_DESC::Buffer(GetAlignedSize(sizeof(Constants))); DX::ThrowIfFailed(device->CreateCommittedResource( @@ -619,12 +615,12 @@ void Sample::CreateDeviceDependentResources() for (size_t i = 0; i < m_models.size(); ++i) { - wchar_t filepath[256]; - DX::FindMediaFile(filepath, _countof(filepath), s_meshFilenames[i]); + wchar_t filepath[_MAX_PATH] = {}; + DX::FindMediaFile(filepath, _MAX_PATH, s_meshFilenames[i]); m_models[i].Model = Model::CreateFromSDKMESH(device, filepath); - wchar_t meshletFilepath[256] = {}; + wchar_t meshletFilepath[_MAX_PATH] = {}; swprintf_s(meshletFilepath, L"%s.bin", filepath); m_models[i].MeshletData = MeshletSet::Read(meshletFilepath); @@ -661,7 +657,7 @@ void Sample::CreateDeviceDependentResources() auto effectFactory = EffectFactory(m_srvPile->Heap(), m_commonStates->Heap()); - auto objectRTState = RenderTargetState(m_deviceResources->GetBackBufferFormat(), m_deviceResources->GetDepthBufferFormat()); + const RenderTargetState objectRTState(m_deviceResources->GetBackBufferFormat(), m_deviceResources->GetDepthBufferFormat()); auto objectPSD = EffectPipelineStateDescription( nullptr, CommonStates::Opaque, @@ -680,7 +676,7 @@ void Sample::CreateDeviceDependentResources() m_scene[i].ModelIndex = index; m_scene[i].Effects = m_models[index].Model->CreateEffects(effectFactory, objectPSD, objectPSD, int(texOffsets[index])); - auto defaultHeapProps = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT); + const CD3DX12_HEAP_PROPERTIES defaultHeapProps(D3D12_HEAP_TYPE_DEFAULT); auto cbDesc = CD3DX12_RESOURCE_DESC::Buffer(GetAlignedSize(sizeof(Instance))); DX::ThrowIfFailed(device->CreateCommittedResource( &defaultHeapProps, @@ -693,7 +689,7 @@ void Sample::CreateDeviceDependentResources() } // HUD - auto backBufferRts = RenderTargetState(m_deviceResources->GetBackBufferFormat(), m_deviceResources->GetDepthBufferFormat()); + const RenderTargetState backBufferRts(m_deviceResources->GetBackBufferFormat(), m_deviceResources->GetDepthBufferFormat()); auto spritePSD = SpriteBatchPipelineStateDescription(backBufferRts, &CommonStates::AlphaBlend); m_hudBatch = std::make_unique(device, resourceUpload, spritePSD); @@ -716,7 +712,7 @@ void Sample::CreateDeviceDependentResources() // Allocate all memory resources that change on a window SizeChanged event. void Sample::CreateWindowSizeDependentResources() { - RECT size = m_deviceResources->GetOutputSize(); + auto const size = m_deviceResources->GetOutputSize(); m_displayWidth = size.right - size.left; m_displayHeight = size.bottom - size.top; @@ -747,16 +743,19 @@ void Sample::CreateWindowSizeDependentResources() void Sample::OnDeviceLost() { - m_graphicsMemory.reset(); + m_commonStates.reset(); m_srvPile.reset(); m_rootSignature.Reset(); m_basicMeshletPso.Reset(); m_cullMeshletPso.Reset(); + m_viewCB.Reset(); m_hudBatch.reset(); m_smallFont.reset(); - m_models.resize(0); + m_models.clear(); m_textureFactory.reset(); - m_scene.resize(0); + m_scene.clear(); + m_frustumDraw.ReleaseResources(); + m_graphicsMemory.reset(); } void Sample::OnDeviceRestored() diff --git a/Samples/Graphics/SimpleMeshlet/SimpleMeshlet.h b/Samples/Graphics/SimpleMeshlet/SimpleMeshlet.h index df699fd..40e677a 100644 --- a/Samples/Graphics/SimpleMeshlet/SimpleMeshlet.h +++ b/Samples/Graphics/SimpleMeshlet/SimpleMeshlet.h @@ -54,8 +54,8 @@ public: void OnDeviceRestored() override; // Messages - void OnActivated(); - void OnDeactivated(); + void OnActivated() {} + void OnDeactivated() {} void OnSuspending(); void OnResuming(); void OnWindowMoved(); diff --git a/Samples/Graphics/SimpleMeshlet/StepTimer.h b/Samples/Graphics/SimpleMeshlet/StepTimer.h index bb15fff..2dbefcb 100644 --- a/Samples/Graphics/SimpleMeshlet/StepTimer.h +++ b/Samples/Graphics/SimpleMeshlet/StepTimer.h @@ -111,7 +111,7 @@ namespace DX timeDelta *= TicksPerSecond; timeDelta /= static_cast(m_qpcFrequency.QuadPart); - uint32_t lastFrameCount = m_frameCount; + const uint32_t lastFrameCount = m_frameCount; if (m_isFixedTimeStep) { diff --git a/Samples/Graphics/SimpleMeshlet/pch.h b/Samples/Graphics/SimpleMeshlet/pch.h index 69c1d20..fb109bc 100644 --- a/Samples/Graphics/SimpleMeshlet/pch.h +++ b/Samples/Graphics/SimpleMeshlet/pch.h @@ -41,8 +41,8 @@ #include -#if _GRDK_VER < 0x4A610D2B /* GXDK Edition 200600 */ -#error This sample requires the June 2020 GDK or later +#if _GRDK_VER < 0x55F007B0 /* GDK Edition 211000 */ +#error This sample requires the October 2021 GDK or later #endif #ifdef _GAMING_XBOX_SCARLETT @@ -81,6 +81,7 @@ #include #include #include +#include #ifdef _GAMING_XBOX #include @@ -93,23 +94,24 @@ #include #include "CommonStates.h" -#include "ControllerFont.h" #include "DescriptorHeap.h" #include "DirectXHelpers.h" #include "GamePad.h" #include "GraphicsMemory.h" #include "Keyboard.h" -#include "Meshlet.h" #include "Model.h" #include "Mouse.h" -#include "OrbitCamera.h" -#include "ReadData.h" #include "RenderTargetState.h" #include "ResourceUploadBatch.h" #include "SimpleMath.h" #include "SpriteBatch.h" #include "SpriteFont.h" +#include "ControllerFont.h" +#include "Meshlet.h" +#include "OrbitCamera.h" +#include "ReadData.h" + namespace DX { // Helper class for COM exceptions diff --git a/Samples/Graphics/SimplePBR/DeviceResources.cpp b/Samples/Graphics/SimplePBR/DeviceResources.cpp index 0bf5d85..ec2ddaa 100644 --- a/Samples/Graphics/SimplePBR/DeviceResources.cpp +++ b/Samples/Graphics/SimplePBR/DeviceResources.cpp @@ -21,7 +21,6 @@ using Microsoft::WRL::ComPtr; #pragma warning(disable : 4061) #ifdef _GAMING_DESKTOP - namespace { inline DXGI_FORMAT NoSRGB(DXGI_FORMAT fmt) noexcept @@ -48,7 +47,8 @@ namespace DeviceResources::DeviceResources( DXGI_FORMAT backBufferFormat, DXGI_FORMAT depthBufferFormat, - UINT backBufferCount) noexcept(false) : + UINT backBufferCount, + unsigned int flags) noexcept(false) : m_backBufferIndex(0), m_fenceValues{}, #ifdef _GAMING_XBOX @@ -67,7 +67,8 @@ DeviceResources::DeviceResources( #endif m_window(nullptr), m_d3dFeatureLevel(D3D_FEATURE_LEVEL_11_0), - m_outputSize{ 0, 0, 1920, 1080 }, + m_outputSize{ 0, 0, 1, 1 }, + m_options(flags), m_deviceNotify(nullptr) { if (backBufferCount < 2 || backBufferCount > MAX_BACK_BUFFER_COUNT) @@ -86,13 +87,12 @@ DeviceResources::~DeviceResources() // Ensure we present a blank screen before cleaning up resources. if (m_commandQueue) { - (void)m_commandQueue->PresentX(0, nullptr, nullptr); + std::ignore = m_commandQueue->PresentX(0, nullptr, nullptr); } #endif } // Configures the Direct3D device, and stores handles to it and the device context. - void DeviceResources::CreateDeviceResources() { #ifdef _GAMING_XBOX @@ -113,6 +113,14 @@ void DeviceResources::CreateDeviceResources() params.GraphicsScratchMemorySizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); params.ComputeScratchMemorySizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); +#if defined(_GAMING_XBOX_SCARLETT) && (_GRDK_VER >= 0x585D070E /* GXDK Edition 221000 */) + if (m_options & c_AmplificationShaders) + { + params.AmplificationShaderIndirectArgsBufferSize = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); + params.AmplificationShaderPayloadBufferSize = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); + } +#endif + HRESULT hr = D3D12XboxCreateDevice( nullptr, ¶ms, @@ -133,7 +141,7 @@ void DeviceResources::CreateDeviceResources() m_d3dFeatureLevel = D3D_FEATURE_LEVEL_12_0; -#else +#else // _GAMING_DESKTOP #if defined(_DEBUG) // Enable the debug layer (requires the Graphics Tools "optional feature"). @@ -212,6 +220,9 @@ void DeviceResources::CreateDeviceResources() // Determine maximum supported feature level for this device static const D3D_FEATURE_LEVEL s_featureLevels[] = { +#if defined(NTDDI_WIN10_FE) && (NTDDI_VERSION >= NTDDI_WIN10_FE) + D3D_FEATURE_LEVEL_12_2, +#endif D3D_FEATURE_LEVEL_12_1, D3D_FEATURE_LEVEL_12_0, D3D_FEATURE_LEVEL_11_1, @@ -297,7 +308,7 @@ void DeviceResources::CreateDeviceResources() { throw std::system_error(std::error_code(static_cast(GetLastError()), std::system_category()), "CreateEventEx"); } - + #ifdef _GAMING_XBOX RegisterFrameEvents(); #endif @@ -337,7 +348,7 @@ void DeviceResources::CreateWindowSizeDependentResources() // Obtain the back buffers for this window which will be the final render targets // and create render target views for each of them. - CD3DX12_HEAP_PROPERTIES swapChainHeapProperties(D3D12_HEAP_TYPE_DEFAULT); + const CD3DX12_HEAP_PROPERTIES swapChainHeapProperties(D3D12_HEAP_TYPE_DEFAULT); D3D12_RESOURCE_DESC swapChainBufferDesc = CD3DX12_RESOURCE_DESC::Tex2D( m_backBufferFormat, @@ -369,7 +380,7 @@ void DeviceResources::CreateWindowSizeDependentResources() rtvDesc.Format = m_backBufferFormat; rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; - CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( + const CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( m_rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), static_cast(n), m_rtvDescriptorSize); m_d3dDevice->CreateRenderTargetView(m_renderTargets[n].Get(), &rtvDesc, rtvDescriptor); @@ -395,7 +406,7 @@ void DeviceResources::CreateWindowSizeDependentResources() rtvDesc.Format = m_gameDVRFormat; rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; - CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptorGameDVR( + const CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptorGameDVR( m_rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), static_cast(m_backBufferCount + n), m_rtvDescriptorSize); m_d3dDevice->CreateRenderTargetView(m_renderTargetsGameDVR[n].Get(), &rtvDesc, rtvDescriptorGameDVR); @@ -404,9 +415,9 @@ void DeviceResources::CreateWindowSizeDependentResources() // Reset the index to the current back buffer. m_backBufferIndex = 0; -#else +#else // _GAMING_DESKTOP - DXGI_FORMAT backBufferFormat = NoSRGB(m_backBufferFormat); + const DXGI_FORMAT backBufferFormat = NoSRGB(m_backBufferFormat); // If the swap chain already exists, resize it, otherwise create one. if (m_swapChain) @@ -492,7 +503,7 @@ void DeviceResources::CreateWindowSizeDependentResources() rtvDesc.Format = m_backBufferFormat; rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; - CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( + const CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( m_rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), static_cast(n), m_rtvDescriptorSize); m_d3dDevice->CreateRenderTargetView(m_renderTargets[n].Get(), &rtvDesc, rtvDescriptor); @@ -507,7 +518,7 @@ void DeviceResources::CreateWindowSizeDependentResources() { // Allocate a 2-D surface as the depth/stencil buffer and create a depth/stencil view // on this surface. - CD3DX12_HEAP_PROPERTIES depthHeapProperties(D3D12_HEAP_TYPE_DEFAULT); + const CD3DX12_HEAP_PROPERTIES depthHeapProperties(D3D12_HEAP_TYPE_DEFAULT); D3D12_RESOURCE_DESC depthStencilDesc = CD3DX12_RESOURCE_DESC::Tex2D( m_depthBufferFormat, @@ -520,7 +531,7 @@ void DeviceResources::CreateWindowSizeDependentResources() D3D12_CLEAR_VALUE depthOptimizedClearValue = {}; depthOptimizedClearValue.Format = m_depthBufferFormat; - depthOptimizedClearValue.DepthStencil.Depth = 1.0f; + depthOptimizedClearValue.DepthStencil.Depth = (m_options & c_ReverseDepth) ? 0.0f : 1.0f; depthOptimizedClearValue.DepthStencil.Stencil = 0; ThrowIfFailed(m_d3dDevice->CreateCommittedResource( @@ -566,7 +577,7 @@ void DeviceResources::SetWindow(HWND window, int width, int height) noexcept // This method is called when the Win32 window changes size. bool DeviceResources::WindowSizeChanged(int width, int height) { - RECT newRc = {0,0,0,0}; + RECT newRc; newRc.left = newRc.top = 0; newRc.right = width; newRc.bottom = height; @@ -636,30 +647,25 @@ void DeviceResources::HandleDeviceLost() // Prepare the command list and render target for rendering. void DeviceResources::Prepare(D3D12_RESOURCE_STATES beforeState, D3D12_RESOURCE_STATES afterState) { -#ifdef _GAMING_XBOX - // Wait until frame start is signaled - m_framePipelineToken = D3D12XBOX_FRAME_PIPELINE_TOKEN_NULL; - ThrowIfFailed(m_d3dDevice->WaitFrameEventX(D3D12XBOX_FRAME_EVENT_ORIGIN, INFINITE, nullptr, D3D12XBOX_WAIT_FRAME_EVENT_FLAG_NONE, &m_framePipelineToken)); -#endif - // Reset command list and allocator. ThrowIfFailed(m_commandAllocators[m_backBufferIndex]->Reset()); ThrowIfFailed(m_commandList->Reset(m_commandAllocators[m_backBufferIndex].Get(), nullptr)); if (beforeState != afterState) { + // Transition the render target into the correct state to allow for drawing into it. #ifdef _GAMING_XBOX - - D3D12_RESOURCE_BARRIER barriers[2] = - { - CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), - beforeState, afterState), - CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargetsGameDVR[m_backBufferIndex].Get(), - beforeState, afterState), - }; - m_commandList->ResourceBarrier(static_cast(std::size(barriers)), barriers); + const D3D12_RESOURCE_BARRIER barriers[2] = + { + CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), + beforeState, afterState), + CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargetsGameDVR[m_backBufferIndex].Get(), + beforeState, afterState), + }; + m_commandList->ResourceBarrier(static_cast(std::size(barriers)), barriers); #else - D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), + const D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition( + m_renderTargets[m_backBufferIndex].Get(), beforeState, afterState); m_commandList->ResourceBarrier(1, &barrier); #endif @@ -673,7 +679,7 @@ void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState) { // Transition the render target to the state that allows it to be presented to the display. #ifdef _GAMING_XBOX - D3D12_RESOURCE_BARRIER barriers[2] = + const D3D12_RESOURCE_BARRIER barriers[2] = { CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), beforeState, D3D12_RESOURCE_STATE_PRESENT), CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargetsGameDVR[m_backBufferIndex].Get(), beforeState, D3D12_RESOURCE_STATE_PRESENT), @@ -681,7 +687,9 @@ void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState) m_commandList->ResourceBarrier(_countof(barriers), barriers); #else // Transition the render target to the state that allows it to be presented to the display. - D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), beforeState, D3D12_RESOURCE_STATE_PRESENT); + const D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition( + m_renderTargets[m_backBufferIndex].Get(), + beforeState, D3D12_RESOURCE_STATE_PRESENT); m_commandList->ResourceBarrier(1, &barrier); #endif } @@ -691,6 +699,7 @@ void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState) m_commandQueue->ExecuteCommandLists(1, CommandListCast(m_commandList.GetAddressOf())); #ifdef _GAMING_XBOX + // Present the backbuffer using the PresentX API. D3D12XBOX_PRESENT_PLANE_PARAMETERS planeParameters[2] = {}; planeParameters[0].Token = planeParameters[1].Token = m_framePipelineToken; @@ -705,7 +714,14 @@ void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState) ThrowIfFailed( m_commandQueue->PresentX(2, planeParameters, nullptr) ); -#else + + // Xbox apps do not need to handle DXGI_ERROR_DEVICE_REMOVED or DXGI_ERROR_DEVICE_RESET. + + // Update the back buffer index. + m_backBufferIndex = (m_backBufferIndex + 1) % m_backBufferCount; + +#else // _GAMING_DESKTOP + HRESULT hr = m_swapChain->Present(1, 0); // If the device was reset we must completely reinitialize the renderer. if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) @@ -723,11 +739,8 @@ void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState) ThrowIfFailed(hr); } -#endif - MoveToNextFrame(); -#ifdef _GAMING_DESKTOP if (!m_dxgiFactory->IsCurrent()) { UpdateColorSpace(); @@ -758,13 +771,13 @@ void DeviceResources::WaitForGpu() noexcept if (m_commandQueue && m_fence && m_fenceEvent.IsValid()) { // Schedule a Signal command in the GPU queue. - UINT64 fenceValue = m_fenceValues[m_backBufferIndex]; + const UINT64 fenceValue = m_fenceValues[m_backBufferIndex]; if (SUCCEEDED(m_commandQueue->Signal(m_fence.Get(), fenceValue))) { // Wait until the Signal has been processed. if (SUCCEEDED(m_fence->SetEventOnCompletion(fenceValue, m_fenceEvent.Get()))) { - WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); + std::ignore = WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); // Increment the fence value for the current frame. m_fenceValues[m_backBufferIndex]++; @@ -773,32 +786,20 @@ void DeviceResources::WaitForGpu() noexcept } } -// Prepare to render the next frame. -void DeviceResources::MoveToNextFrame() -{ - // Schedule a Signal command in the queue. - const UINT64 currentFenceValue = m_fenceValues[m_backBufferIndex]; - ThrowIfFailed(m_commandQueue->Signal(m_fence.Get(), currentFenceValue)); - - // Update the back buffer index. #ifdef _GAMING_XBOX - m_backBufferIndex = (m_backBufferIndex + 1) % m_backBufferCount; -#else - m_backBufferIndex = m_swapChain->GetCurrentBackBufferIndex(); -#endif - - // If the next frame is not ready to be rendered yet, wait until it is ready. - if (m_fence->GetCompletedValue() < m_fenceValues[m_backBufferIndex]) - { - ThrowIfFailed(m_fence->SetEventOnCompletion(m_fenceValues[m_backBufferIndex], m_fenceEvent.Get())); - WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); - } - - // Set the fence value for the next frame. - m_fenceValues[m_backBufferIndex] = currentFenceValue + 1; +// For PresentX rendering, we should wait for the origin event just before processing input. +void DeviceResources::WaitForOrigin() +{ + // Wait until frame start is signaled + m_framePipelineToken = D3D12XBOX_FRAME_PIPELINE_TOKEN_NULL; + ThrowIfFailed(m_d3dDevice->WaitFrameEventX( + D3D12XBOX_FRAME_EVENT_ORIGIN, + INFINITE, + nullptr, + D3D12XBOX_WAIT_FRAME_EVENT_FLAG_NONE, + &m_framePipelineToken)); } -#ifdef _GAMING_XBOX // Set frame interval and register for frame events void DeviceResources::RegisterFrameEvents() { @@ -827,7 +828,30 @@ void DeviceResources::RegisterFrameEvents() nullptr, D3D12XBOX_SCHEDULE_FRAME_EVENT_FLAG_NONE)); } -#else + +#else // _GAMING_DESKTOP + +// Prepare to render the next frame. +void DeviceResources::MoveToNextFrame() +{ + // Schedule a Signal command in the queue. + const UINT64 currentFenceValue = m_fenceValues[m_backBufferIndex]; + ThrowIfFailed(m_commandQueue->Signal(m_fence.Get(), currentFenceValue)); + + // Update the back buffer index. + m_backBufferIndex = m_swapChain->GetCurrentBackBufferIndex(); + + // If the next frame is not ready to be rendered yet, wait until it is ready. + if (m_fence->GetCompletedValue() < m_fenceValues[m_backBufferIndex]) + { + ThrowIfFailed(m_fence->SetEventOnCompletion(m_fenceValues[m_backBufferIndex], m_fenceEvent.Get())); + std::ignore = WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); + } + + // Set the fence value for the next frame. + m_fenceValues[m_backBufferIndex] = currentFenceValue + 1; +} + // This method acquires the first available hardware adapter that supports Direct3D 12. // If no such adapter can be found, try WARP. Otherwise throw an exception. void DeviceResources::GetAdapter(IDXGIAdapter1** ppAdapter) @@ -883,9 +907,6 @@ void DeviceResources::GetAdapter(IDXGIAdapter1** ppAdapter) *ppAdapter = adapter.Detach(); } -#endif - -#ifdef _GAMING_DESKTOP // Sets the color space for the swap chain in order to handle HDR output. void DeviceResources::UpdateColorSpace() @@ -994,5 +1015,4 @@ void DeviceResources::UpdateColorSpace() } } -#endif // _GAMING_DESKTOP - +#endif diff --git a/Samples/Graphics/SimplePBR/DeviceResources.h b/Samples/Graphics/SimplePBR/DeviceResources.h index e3c80e7..9809b06 100644 --- a/Samples/Graphics/SimplePBR/DeviceResources.h +++ b/Samples/Graphics/SimplePBR/DeviceResources.h @@ -21,9 +21,13 @@ namespace DX class DeviceResources { public: + static constexpr unsigned int c_ReverseDepth = 0x1; + static constexpr unsigned int c_AmplificationShaders = 0x2; + DeviceResources(DXGI_FORMAT backBufferFormat = DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT depthBufferFormat = DXGI_FORMAT_D32_FLOAT, - UINT backBufferCount = 2) noexcept(false); + UINT backBufferCount = 2, + unsigned int flags = 0) noexcept(false); ~DeviceResources(); DeviceResources(DeviceResources&&) = default; @@ -44,8 +48,9 @@ namespace DX void Suspend(); void Resume(); void WaitForGpu() noexcept; - -#ifdef _GAMING_DESKTOP +#ifdef _GAMING_XBOX + void WaitForOrigin(); +#else void UpdateColorSpace(); #endif @@ -73,6 +78,7 @@ namespace DX D3D12_RECT GetScissorRect() const noexcept { return m_scissorRect; } UINT GetCurrentFrameIndex() const noexcept { return m_backBufferIndex; } UINT GetBackBufferCount() const noexcept { return m_backBufferCount; } + unsigned int GetDeviceOptions() const noexcept { return m_options; } CD3DX12_CPU_DESCRIPTOR_HANDLE GetRenderTargetView() const noexcept { @@ -98,12 +104,13 @@ namespace DX } #endif private: - void MoveToNextFrame(); #ifdef _GAMING_XBOX void RegisterFrameEvents(); #else + void MoveToNextFrame(); void GetAdapter(IDXGIAdapter1** ppAdapter); #endif + static constexpr size_t MAX_BACK_BUFFER_COUNT = 3; UINT m_backBufferIndex; @@ -161,6 +168,9 @@ namespace DX D3D_FEATURE_LEVEL m_d3dFeatureLevel; RECT m_outputSize; + // DeviceResources options (see flags above) + unsigned int m_options; + // The IDeviceNotify can be held directly as it owns the DeviceResources. IDeviceNotify* m_deviceNotify; }; diff --git a/Samples/Graphics/SimplePBR/Main.cpp b/Samples/Graphics/SimplePBR/Main.cpp index ee7ace0..a2cd7d4 100644 --- a/Samples/Graphics/SimplePBR/Main.cpp +++ b/Samples/Graphics/SimplePBR/Main.cpp @@ -137,8 +137,8 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp #endif break; - case XSystemDeviceType::XboxOneXDevkit: case XSystemDeviceType::XboxScarlettAnaconda /* Xbox Series X */: + case XSystemDeviceType::XboxOneXDevkit: case XSystemDeviceType::XboxScarlettDevkit: default: rc = { 0, 0, 3840, 2160 }; @@ -163,9 +163,11 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp return 1; ShowWindow(hwnd, nCmdShow); + #ifdef _GAMING_XBOX SetDisplayMode(); #endif + SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast(g_sample.get())); // Sample Usage Telemetry @@ -482,13 +484,13 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) // HDR helper void SetDisplayMode() noexcept { - // Request HDR mode. - auto result = XDisplayTryEnableHdrMode(XDisplayHdrModePreference::PreferHdr, nullptr); + // Request HDR mode. + auto result = XDisplayTryEnableHdrMode(XDisplayHdrModePreference::PreferHdr, nullptr); - g_HDRMode = (result == XDisplayHdrModeResult::Enabled); + g_HDRMode = (result == XDisplayHdrModeResult::Enabled); #ifdef _DEBUG - OutputDebugStringA((g_HDRMode) ? "INFO: Display in HDR Mode\n" : "INFO: Display in SDR Mode\n"); + OutputDebugStringA((g_HDRMode) ? "INFO: Display in HDR Mode\n" : "INFO: Display in SDR Mode\n"); #endif } #endif diff --git a/Samples/Graphics/SimplePBR/Readme.docx b/Samples/Graphics/SimplePBR/Readme.docx index 23a4942b2d4ac89f8e94f0238b3baf361f322be5..cac3901bf04305e5391fce8ef81a4a6bd2382a39 100644 GIT binary patch delta 34763 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�)YaJApeuZt2 zuEw^lXk<59`UO#_O5OL}!eRh;w0bB}y>WS29dMPyq_f$~q(VHv-hp|eP^y)u^JbS@ ze<*jOeVJwsg+pXi_^n7yxI@$D*L*|jRA&m)h#GxEog8}2NReu6mkxZoP?uS@Rz0wL zF^Lgg%wkj6Qf|3Jyt@W*C_{dbBv!RHQyMU*!4L(>Te?&;w?(Xl=oNBsx5%kR;US<~ zBY)cIm-}+NH3))-*%a7D{VucG_e#?lf5W)l`eG-}guw$>VE8cDO`G}JM&`?18IVdI z>+6ENpDcrTkT>n_s?E^zi7UIUa@b?VMti@D;)c!)%uVP3Ew&sP@+wzrnm+Te0L0pE z*~P^;)_F~@EOh~=&Dq+dBni}Ane`cAYyf$>*m`+NoAzdsrMjH$=NP(egnUdDf2Mp; zXQn*2mD)p&pYFn~+A}AI9J|ah%;I5)B`6bc#ib+%46CH@`J8FjHVZ()F}i@XsZ=pT zOns6;fz%smW11sJeU2PZEpC$U9R_Wf$+kNydi$Fu&{vTZ=)nj)j2LGt#Ge_wnN35i zovqMPXRJ7Z7upzoU#PGEdv}g3e*g*$1@#<=D7h*}Xzn&q{XhsLQhfJhW1VZ59{CA-#G_-QPl4e{^E8PhBgBcfBKCeL6^kR zv)x2ct?kGPFqkm4J_d>=`b}65?$;0YjBsq%f~$L7!nb#pQvsKKD9Q+wj{W##7_W)n zNrF04OoG4PQR4h#u^)I-;&&Q6PM;}f2!saQAZqNThw#$6a{vL5&b-WDXj!=4GOANd zp2>Ff%=?#sqR4a&xh&%0f5?I{2a-OB;U*Z~3XS7DFsHWVMu?PPSp+F#;<+x+Ng!z@ z`R|}Y?+aW6}{iXB#rStrM)_LSLf1S6S9lWR^yXdmc zQE2%0_eC%8QM0rBN#R()x~zYYk3__6l3b zkJowHv^0L$D>3D}(A>=%x{oW=OVfUh+WL15YP5N1&S$#Trwwndv}XI2Dq4j*b7&4W z>)MEmYk*xf4ap%+jJ^)xaW0?F0z8fEFW-cY_aPP+7`1d)bne{luA`!&*xdT zQX7oPfm7p{Mk_z>-3fz!foH91B{vf`qEL*qbce_^!a&*wNAPu8!z1-efKYEc?Ok({x9?X@|# zAhRWcFHjZ)#VJ}C6v3tVUq4{+hNPAm1l|_Qrj&p|ztB1;w0a7!jDkz2p4LmjtLLlq z;Szqdw*Sm-e*&-^ZS({LlsSQtC$K^**jOL#3HajU>#)V=+ZS-0%xJm>_a1m@`vQSy zmDKhH4Y*YDxJ|*CJq>_h*EH>$>}e3)xAAg)rIqQ@2}TToWI5?7M%pH<=LWoxfR0#M zg&0WTg_UGR27)Ss7m?v~_AClGmZBuMLBSIWl7}y%f1qHQaEC9XsclgQz!kVTwt#A6 z9arcdcX)>w1%wz6IG0ZP@rEgs5FxAyKooM9W*7F0RE!-$f5%F^MlxCHc~+~SMM|UKUOmqhh|Hav z6o{Lo=TTUl2c1%N!H^j82GCCwq8|u)LQ2uSW`&c$_MxZYCZ&coctz*4edBqV*L5x1 zH{GCANk1XXlfqPo6^>Ws#~M(FlZT&xeOU%it+nTd&^ZHW_}a7)45(V6x}$r4u@CJD ze^8=2ns3AL0 z>1yu>V{b>a0YWUO;<%ybSmDPTx^=nAH%Q$V!8~6lg1zoqPk<9p7&g>f$m0r5Vz?0$ zq1n(=I3OtcJAh7XY!gZCyjFf6uvw0KfATs{AI6an8 zXcME{;~Nmc8Hp)7V{7VVN8z=kaABT7>GO=nuDsC7EAJ%9M5Tj->3W0%BM7Hr?>vPu z3{M%FcA^QK9omG!oEh5e9W=Ble{S?6hxQ7JYG()J9`2px5Np%_Lc}gK{K2R|-GLz{ z3)6Ki2O#wL2=U91!10`o_3K5+iQ9fHJ3c==9K|sz$Ex3dcx)Hq7sjRlQSi6u$DGQl z>`nUd31~0zruV}8G{wO)rc1P{G8(<@!k#P$81}PgKnJ>o0W+d3P#g`}e^#oKq@l5@ z%t5Q>%?wy2N!EBY`2Kiy*z)4}!UeSdT{z;kI@8kp{%>%b(<_fMil!xxe!jWV&!4Hk zpP&6#)!(;vwuTu7+yFPQ$1$|YkjAu!ngS%h*f9fQ0hlym)C5d+K-w${4typahXVQ{ zLtxMVc4ST_UI6VvBJvU?e+eiZz7=G#aGr_o;Q&`a|=1J8cQ)GGBwg64MtRTiF~qeAmo(7(ERP3f13oh3X%vv3603> zG0YGlylX?I(TplH39Fb)9F${n?S%v>SBCQoA;bM6;|p5gySQ9Z3|JblLCgp4?v;$n9IVjhErs6|0%S>#E4 z*5Vn{;+fM4nXgY~e;Sv zfH+F$6*7s4-=kptC*%-(9~4)cG;~Jf;HJzMUq2dU#4pH9e*-t5wGl_w({x2Tz;PY} zjWzuBX2hY;&=k3`z+4B~2Wl?7Wzy4<0<1vh7AM2yDVV+U(xe9sa)xwER3JOGUp?uK ztDHYQX!ii)UI1Osk!b@t3?%BAEQ2fK3#7DVA-B-`=;YD2hK|(9p+r~9oj?|k2lsYm z8b}8S4^U-be|IA2U_YJ+b~9RTp&*JfhOL9?pF^VWR95NB$h#$ z*>E>%2B<_psYFP+e#Qjct{n^&p9C4xr%F(#8M!_LI9eN zP-FHyye}F0N`=A*7~NchiG-s-*K(ESINHO#mgZ>b*lojikfL|x!u?A3OmB2DrNESu+N^CpW zDFnp)%_)rx&uJ1PpZNS4-QX#Ted-Z}o8lQ+e|QpPsZM6^U^z(v+~K2up5;<#BE#H- z)>pNd6r~it;*7u{UDG8VW5!c!v$7+opkajwx>Yv7q=4%&AcOE7GlFaj`quL18od4) zzKg!V`=56_dy^s!gJe2)k}CSmdU*z~EGdcxO+!T{)gni7oW@C6{o`_rw7_u)=(Bho ze|z2vo|oB<%pd~KKo*TWJeW!Uqu{J)ju*h_B4^CwKmeQVnvR)j;gY$P9*OqE3>+(p z;846mCx>!kX3t}^%EyK7)EPXy%<>%%Ib*?ir;{SC0h~&kyOg5vU+oKqIG{hkt#2~M zIKU=&-Z=4xoTVY4mKx-fizMrTN!@vYe};h84TzRKnVT@|L<`cbRC}F<5;VxuDOw5z z#qdWo5tDR+37D*7iwI&DrpYu^9LS8`O+h!jek{UfLAuWSH2vKQ2+YG;MV|k@mS0Qd zm-yK`p*TyfM}DBA_#5U1=ctNQ44Fk9ydT61X;M%XML97Ed0CW%wBZFSIZ5QXe@Uu6 z4nksg!Bk3!8GLnNWDGCyD%dVoi?KJ~y5tvb#V*om@w_AK5#2 zu?ElSs(hB}M2(|3P@H@VTYo3~31@Rml>B^5cmMifX?`j-l5U`9j@>x|9F3jiU&0&5P6ic;QjaGy zxCve+a3TpjhP7kC72=eLe}kCNhyJV-o>nL)M+QO+z%qDt;js*#ItEK&?Sr;&O}>7B z2Qym1v2X}jL?Xn%WgnDo@%fSH0koFf$1B&-0~H{1kKmImjh>ppQx{wC97V)skd+9p zo^Gn>nA)TS-p>@cUO@rm{dm>GS4Rp-qc0!WQ}}ZZ{)0L&v@FFWe?7<))7)|KK(?5d zkYR{xK2{iIrs)a6V2wm&X{^eod9DoQ3i1_SA;J!NpV2v0O6Gn-QWJNUAqWbt*-t*- z0<+$9njjNhS{utvUxV9e%){I1>tpNaRLxAi$2;(-&1VpteIvP%eE}0yfOldz;(!*9 z%R7!(iYEB`c$E+-e@){QCv0L$DI!7IhE2nBDzD+6pb zQ930cZw;lu)yiD$=^58cw)qT zFB*pmFVv?mEzdr?;_ku>xaAc{3@s1XcUCS6OgD7kGTuauhYKC%#KyL^#0F^9Uhu8B z(Pajts5GT>e3nlO@$!RZ4*_*YrL_tH!8hrUlOG~Rd$<_DN(h%4BW3;sI#9VoS6%|c}u8AK{ zq<=Uct*3k--|;@_wj=kBC;RI76qOZe*|9r=5CPpp`qY8Q=(E;h`aBt zbP0-~a2k5_Csw)yh5=faaLeMOe?z{|FkM3ZL0m!OX^|9f2^{~x>3|nS<80E#4=~6C zUYBG{pg$27`f^)?Db_OF$%_EC**Q1qb++*6-cJuX#fgTj{G}abA$*T^#PIL&VO=Im zoduklfB(s3mA=@iA#s-nKZnex^sDJSX@!-}_as ze}r6TpQN0P6A?um06(^teGR2Wy|0^u?fP+)Uh4J>-sfVgikAz%*9}cdso60nX(g~6+0pK#eo0iOFy88JBra`j|$%x7=X?>TOM)ppc zX`ErXylAL0Dc_RP^OhD@gyqKTn>Jt2 zWku42lP+*FEy*HEL3m>qQlpnh1Ze-YArWc%QFJ6q=F^?p>7D5^2C6C4K#Pd^rf((+ z=;L;MW;n6!uOC#jd6gMLa29a4zHAZPx9{;9)swMOlMpqwh#xoWqoiZWQ-lqde?i<{ zHh{l9Y}gC&I@ll)tp9=63!M`ZKNTV`^V}Z@y7*>1fBg%=w}EaLZ$oexJzHY@ZaDuB z00960>|EP!+c+A26(Sdl>7uc9K4i85bgNNJ33NVFFg0ezY#Co~Tu{OTw# zFvZ0UK01Ag_CkCs*$tBLnjWmex0Bv?_!BFOA4+`T0ZSieD4GV)JWJ?vlH-J=c*sJw z9}&fiW;_9(Iq%5POx8~Ff3OJ8;#WtygCDWFcO+Frn zWy0?ET<|72FjR9;k=Y#4bR0>xO|Qd%qhx^QdrVls+a`9AY*si!sPKeGH9v)6g~2Ao#1A7RRVq&~~9dTXNf4O=!@mWsb9J4SW`=+p^p?;*uj9bo<=| zBRUCVTp-Yxf1D(kqLA{PLWZ(2V$i+K(y#1Sv(pcRmSKX1tc*#9@*?|v!$u7|eJS=7 z2}PqK`-oFi=7^*$gYgQC(U_Cbfm?>8$&#sYsK16?6QQqc-PRY20V@H@hisnUg?POs zlwAM~*C73qim7Qit*#`=O|lf&W#8E{;gYRdH0wL~f6P{rot3($s}Do23@l6;`ttd| zoJ|Fe(G4zOrZv+#fTY@Y|~PlZ;e;o)M7GK$5lPwQl4nU71+Q!^704%KcQfqW*K_( zcK8--e^r!}aVHxk5B$LO?Jgc=GqH9c4P;*1&`RvL7pH~${JJ=uhitNHou>TvxJ)BP zKodnNiF@e>2(zfy0WHZgEnm{sig8pQEWKoL5$ocAThJN=vS(M`@Oz@=iJ(;Ab(v)h zQF5JbbFZE$>AEY(-r&DOuD#o9$`oIP7^8Z~f9b+L(qyO+J6ERS8sVoU2X8?^Iu)*IPD@ovhTYCUF)(0t4c~RxD}}t`DykD`Kg;)jFD^x zrtRxpN}ZZ=`4x$+n>I@98yv=fu2RchQ{n`~(AxzuS$DNNi`H5IAbLj7pubsi)?MQ& zf1=vuXsH{BmU*5PkS&ZdFd?xhs0XQmfRYQl#pq7jgLnm#qlF0d?CQS zt%I8bO_S7ma2`!ZVUmCsotCtkQ!+>4)(7x#!`?~>eC5G;$+pjFWp!cGlStE}Z<|X2 z@)f2LUW5pb9Qh~i3Su%ImlPwM-jE_of2SA%wFS&C>N3OiajQFNj-xUOi}qW4+t=Go zO}Fax_AO~pJst%EmX%42M%z;#Ur{}$2CMy{zCT1!Il|L07d4jQBZ++DUv{SGbj)|y zSQndV8rHxyb*|W7vtTuiEf%2J({2IosD}uuIA_UtE>cbKpd;vn#Pj^@0wwqcefQE@_GBE?xOi!fmVK0YhvVDc)O7PGi0*5F?(UNr6P z;<0yDL(YBVRb0M?OqL64KABU(Cpt&M5TOF65KOliE=LgVRl>HpA^;xifBBZ$WjV8c$fBt# z)>vzNT=zYqK;7=+jcs(ZL?$B?%4iv-_P@Y=RN0-S=oD!j8S`x`i*(5nzi=2 zenVI#0MxOTR4+8HSdHk5sopKo1+w7b{p=_2TkF8Tb>))Fva zGA!=|VwD$!*212Nrb`0h2`tUcD{?EObFuaCp?31EY+ zPxv{ob3qhmk$si-HYOq59!<`!q2b=B7#-%F!yaO{kuXIdc=KJR_gIC$hxMjoB7%A^ z1^*a40S5#HJ; zmj2%CJy`_wIndix6y>5Q?(BzOw?pQm9D_uH2^7-3R>jIvilP zxX1&Bv!-nXkedU%h34(&#*YiE@u==SNRqjwu?aSk)z{w-C|bXUU!qm6t18#ImEUhC z8Qal*SVUt}j9DuE;p9UqtFO5r9H7)|Nawl){!t`z#OaLrJ1$TA^cL5F%@19NrLjuDw>aPnKSpXe|^;A}qIL_KqQe zJ4P|>$>@4uov$kR?;pup(BdL0jLCrpc^(?!h2>z1y^LWyB*DQ_y#&gbCcZ6){Nz?l zW6p_zWDYWKdQUlxyyA+3c5FbLTUS0h7~NFu=yTy;X8ur%ig5`r#|zkt0$DRMKt7VxOdgF0I~-D zsA^7ahoGw}M1GeKCi)D{n}>9Swuk_AS0kIjA^D6-yX!z zKpQ`>3fiz!tV7wvm35MJ(d?Hel*QbE8&nB)Q(6HFjpd%_&I(tI!;xogHUfM%;f+rb z&T&XysOCYY+;%%aeXA91yG+&4i_od4h92l*xJ8;MZoOOEjAXb zIu3miEVqq6*_P`7GIGHT-&S(HQG(5@PN2jNZL01fonTaemk|*X*T-nZ8_9kVZ{9DR z_0xFc8&Q~P0478q1J{jhKRQ});j=Q&pw$0p>PZgz9 ziI15}Cr#eYbysg`;sl001tm#GnhIoiG%pw?P8WW;~^o^PO89J07 z0}Sk5sz_qa=soWX5Ou~_&$YNM1vP>~HkrLOQ)V;A0N-tDBM5n*FHd*o!5IE+_ah)% z;Hoo~?w5jm`i2C|S&QgV1pHVm0CfXp)F+P;;o?t~G5KQ-UErlGzUlY;oP2?1hFS+h zDz}<4|29UacODidocJbH)=d7(R;6Q(W?;CjhdXC~=gqfuOSHlFq?CQN)eUX0s^MP? z3l>S5yIvFr;U6m>jGW3@< zgv;qh0|058H&`o5$P^FEzI!hgX|aa302eEoXY%>^(j*6si7=Y~5m60~a&Q*u5^O8q zeV1z@%2%kr6`wh06LZ=FoBxA`0r)kD$Gph;V@9;7IA|FUkPn+%mB?BFVP87I$o`bqe{AxI9KH&d7Nl zg$}Qt!aOj@W))}Ko$5`J==(K;D3FR8vub*ahi`v~t5{OQfX$%cJ>?`<)SYR}5_(1K zF|+BUDIW$A1vf+-9}1pM57mTGP!q56aB~wV0;}uGtD28t8!J9!quHuP<0T~W&Y}6} zlqO}Rh~Cr)koCAuPt+h#*-Mugw13mAKWXhUZaEuz1aHba_nx(%hXWlZOPQya3;gcNnR- zYRUrJ^_TpjHnPbSXv6;47 z`mYRVuTl_4lgjH4rz2X zVvi1OsVFP4X7`-6ixDJ?bcJ1JcUr3XDy#Y21+XMeOZ63vzUoRpX`{5|6tlO3cTa!1 z@vwXp`yFB+xB2CVouhB3e4Gna}tv#lHygzWJ;5Yg8-FLD|IOW3Uk&68;l|@X421xFK z?{BQy&)KzFrY*7lMITAHpH{Ee{fC+RX%9WRsrwbur$_$+>NHlH<|Ns`?qpvq?nM6R zB}vWuJ?l4+8am)c!AhotrDooOUSatOBUc4sB0m^XuYA(XJ%EU?wB!hd>pYE-L+$!@F48w8e`m@&fr3nfh8QUt#pb1{3 zJWW@cM99N0=OvUN8q0N>8q8i0<~1c>fHtFi@d+fk(&kL95!W+sNHyb-DEd(T0yKyH zA3$%bgNFhz-;;w57DEkejcm`$Uk5G@yAt+CN^8vKV~ZdLlwlLt7JfnrCPOB8AnjffsapvG6yMVxndil~@ z50x1c&uTR9)nuEgI|JHxxhoKCm+PTE7!DDCux1GZ5aZx6x+tkz)e7fVrO!6rx5)NMmVU)xWD4dHV zzU``5Y5=vp-brCYd*_2*%yqLvuF8)LDp;F0_|cZ9hUfqaxg?DzOl=E&4WSxJ2g`Ba z0$=0-NA?KUC@ZPQC#~i7wD=GGl>UfL%v>rSqvPW(>dBIa8P|X^s&y@c9rxyUgnxkp z+DPTO+58I}2naF=2#A}#lPQy>nUSfP6Dy;;oozw#X#Bq`d+8Wo%g?WwXOMcSJb~$f zy0i)Wza%l2ITlsomp26&_JRgR6YaRQiVi;R+7~t!U!MEmE(aP$3#FBq@8gEQC0Q{d z!o1wyn$?MC*NPVu#QIYlkHLEaH@!a^1c$ZD1ghyJlk9$@sm4ArK*Luu-- z(a!lyt*3INRJ3nV4h<2apy*y0;KnPN+n4+PW0w;?bSH6aUv_CU0sD_#j;qfRJZa&|nRD#l zDT63Vz#?ANBjUPr1QqOrIRh|s{hhXK6edoxmfIW~*3+@=ogS0&Ty z5M0MA-qym)p!C#d&$T5uPn9i07A6ebQFlJ z_&IksWR-+wFmw?Q_t+hSR`3hhu-)LBWPRE8HJhn>>;^yZB(ODl4eT6D&L%gTSO5Fd+TC8DVGq3W~=X`6#G z_gI;fSDuJ`4Rc};L-3f$SZ{@$Qb1Sy$ zYSz*6u*M@ReOz*uH`X8HGr2LEB5`LEMeqxmwX4!EdWL7WjlN)4v7fIf2DL==JCRZ}fowhrPf5us07%4|DzxduQQ((*F;8!T-Zvt;`pD3yu<# zSh|@;|FAb}l!y5r_M*So<6=V0NZSF=r5lbTosG`ps>Q~af z#kunlO=U^_K@t)y3~MUr2-SAoWSxpkmaLDJaOC8(e}7{JcrjEWlSfGRv3hRm%;nwh z4d#5_^_K}@=z*VB99Q1}uR1ueNH5PkQ6{Zq!|T}*dFYnaGK1(BR(|4!s2Q>lo+KOL z)+IS(&gT89^p0ncnW5e3EAF2rRnQQQ{MAG_34?W4+RGdR>3ElYaSozT&+zPkA zgIiWkrpYJ#AS2wffEe$LI}6w6q9|798n-?d%iAu!X(i2fk$AxiX zNB>CkBaL#QO&vf8#I~+3Y>HzQ19yjhEj*tUckCjXVMnZ?6rD#8sj2C-7?gn}T(|X( zx8_W=Ax!nA1+jt3#hV33ih%8>W4Wsd0}O;A!I#5IX+t>fO4c6w@m%q+;ekky=RGS^ zYZPh{ScMTgtIS77hu(1)W#R&|0LRoholMA#Ji7Y`I&Xr&hH^@Zsv1TfuyXj431X(r z;Xm)0`>`~0H9(GWY^sEB-2PY7J^dV3px*xZ+uu1)67Z#TZBk8iu#}X-WK_BU9xG27 zO??h7@Zhj)*xE^ij*EQI}4Su*^|6rP7 zxVPtYl`-@3$GNWpcPp3yuUIi3ySEx?q$mc7!#o2JNc#&)V#`rrP5lsQp?+V8-p-zn zX-yE{A4zm)Gu7Y}=bh!Zw9*^wKwVryY%(mIcHa+^sUAZZxDN#%4qlR-J@`KMh=Pj) z^e0#GxLlQ~!>&R8vIhh-*{2~yQ<79xtN6rDSYen{`;&?@-Xbo~DzxL+!$~(>T zA9s3NK%)5oGG2-Z@ol-Kvq(=BByQ&jv2C!bwK&_E5bPVR_e4zri9YxXb=n>QK>8rjCOrc%NVorD7sjDV5ML`mV>BM zk~1+u3Vt3OFJSo5pQVjhVVXKQ+v@Re6;BiR&%6%9+d_d+{lzEor2Y3y43@>39BbF% zOrQt?-q@GV8%-RYe06x|ZPa=tgIllRSG2)ez}=6tslgZHP(8o#)YKx~M(Dw_KsD9{ z8*WuC1x?#nkw6>s65CK9WGPrtgEgYodw%aH2G<=qDv0xf(BuEp8({9xXN0%iTbY`= zHH^Z54&-(mkH3#J!?o7HkttQ{fm*KO1txIkBURvV2^2T;S>SX=driODw3yY{)h@#) z`pb-kiipnDc>ir|h%7)RX?CQ>%(U)A<$tsC2$Kx!X9a?J2*6yx+Jqs3 zE~B4H6{1>+L=4A|RLco_eFR=s;VF@o2sA=$RsP)!9!nYmLC|^cFo)B*lpIg~0Ypl~ z;mZv_S|l}ocnq9a_JM8vbyJVuRe5L5F`Gx|mP;v<8ME_K4B?0Fo=>{MbP$PSstU{Y z?NSi6t}MqVK4iZi7QFw1!(aH-rltEe@F}-dzstv=tZX}ohbyjvU|Y0ewvknjbKDSL zM?3FVyv8*2O%83Z}U^SjHuzei;g_I0-eHw6A zWrVfWnhwy+jm4UCzTmAU1TIS?&!#oksGdfIH8!8Q>V{fsW+iFXvAst+aaHxwc+u;^ z&DkGqtTZ<|&d66IGl_%wfa9FClC+^0h z{0XUGSoJ0;9>oSNQbA|HSqgwB*@FxvkT-;i*eoCl9*j$Br-@k zGxUOR<{z$|*Ix>W-P!eZaP}hjYvQq`KL^z;0jy@=tm+XAWthhpy8;3tY}^lTwDqIQ zEM(O5<9xBW3bKA2yZjNEUiB(}D_YVBz~d@d8$Up>;vsLPPFrasc^4NseB6jt=nA*_ z4W3ldQ4$?9a0WB@3YC0NdHUw+fMc2dFmBk8v3eAFM_`HsMbj1L)NIp)} z>cQN8mJB{p9BGbUBrr`}yHIc!3~lmSUlk6G0mlGG#{KsjxoZSOIT3DiRpLOJVb0H> zvUshDUG&sfrhiHZ7|26q=(w*E;%CB?g(@&~FNA0d2=a}%`|TyRa5lb%_4o4xv_2hK z*gi^Bg10N%%kbmX7@>?=&@FZ`I~7;HluESk`_)qSTOIC{38lzo2Yz+g;Uf02C9;^SsTY>C04TJ1NuECSxh1vgp~^YP2^FZH8lUu#m9!V7MRst( z1*vv1iB-Gw!&e<}n0SuM%^;1Kq&mi`Cz3p|4`2T(;Rw+KQ5D}#E?*|qqxJ_AlIr;# z234dQkz{T+ABCPLpGBszMdCMCJ_K-lSpKuTN(jqG@6ldRA1NLY4>Q==^hT-BY~jI7 ztfjmiie1N)y!x9BD;dp|^sQ4iy8h1*1&M$$54bw-=wD{ZP3IaAxX0FtK~1?D{kRUH zPf-_j4asjF;sx;2lp)8S@F+&kU;P5`^zH&zGV^yM65s)faJd&CsVF!tS`9#K*8mbJ zo6AG)QQ>B5N9#(5OjCJ!Tx{^>pcBwUM~tZ$QDt2NvrQ~b_vrD-X&zD@1?v0&uRosn z81*SUeNvKGgSZSHIIBl4(t6iEqbOQYV)t1c-f7HRhb_OP(N8orQ~brZ2~o9L z$k}^7A;orx;>eNmGS75&S68@}T`Gu^^)vvdGufP? zh{#KvwU;Jg_R$k6B-beYiyRlkYmtKqo*SI&efX|lGdU_tEq1CbCBeW6>o<&8454}Y zg&pwFJl!Vc&^t#zoDcYZo!=H#T{}yKHC38<4bRmOPmC{g**Mq;!5HRqT(It?Qp@rwv5tmnaJu$PwVGq%O4_hvS-t(NaBV?52^ zGo-RG=6Xe;Md(G5SQx;{#&prjq8lLsae*A;0Qvo(=u@xEW{c(Z;@`jIu1PCBIVuqV zc)tCHF?=NEYwJ{2FL;5tpM4R5Da&AY4JXZ6H1!f)OxWv&O#bfrJTIa&AdV_#vxV{>8M&kp5Nkk6X z3J+BzS#y3r3vfo7c@#tNEhDD0!R;2;A3`y~Irjkc#4YLSGD-169H*2Am%8}L-LD6d zaqA69cv>GDS&6vZ!A5P~Q9Mp6jY;?%b4Ai4?KHtgCDFy$y{)Z2p5ha1qDVCBmcFY4 zUbG)>^D7i@^CZBE87)QCKE#}Qca&UUZf6E`n)$Kk-p&9IGEr3vw ziKtw^cp!QwjKaQ?Sd@BAb?clo+jd5m{gDG%jy?WQLaOl12hY7tn>{4sEF@`9mS)zADm+eMp%fF|0$d@L3%^pTB*Z2nH^k#YPjvFF-l zOTgjgVbfc7M((L~oNEiK;fZ%~p!sfmHWAe`yqNFMOnyw+du@j4tT+U*Y?gkjgWN~#h2B9NAPGT6Vf3B#AbXj}Z7bd^+gQwFhQGrX^dJ|l@4x7p&eP290q3*@EAL*C-*qMr98+y*M^g3g- zBjE(io^46lDTK7Rhk?khcx*vxcz#}3sv-;Ip)}Ay=8Yh^h<8GrV@Y}fR0m#yKS%&YA@ z-$P)mDlqyM$8j9Ye05`qU+Ckz)VT;mOW&;j`DP+fFs-!{D1ypf9vYkqljL|xF0Y_P zHYhFbAnjWCLbRGKoMwOtqfO(#rj@)iSLSN|ML*K3Ui=xdJ+%wlXv3f_F~clMTI zQg8`pLQ2f)^iLPFc1r2Wngo#o=9eZ!b06r$6VGHEw7+BeKzN$QRbiLr@hIM??icA@ zs6!hs>Rsu<^h&qszDw>sG$lO(Q|lsuBElykmd~{)#YIPH#3a>TSQQn5sOd8NplZMX zVK4$FBV3ocCK#qk8m#Y;aff&iPWq$+M_^nsPlogN{KYxd8%@!fq*bnBKB zzP_I{S$lc=^4a@L8|-N=OXwUw5m@&l4X+sn?+0+7s}?sACTIUTHAy$MHf6( z=7ijd1aE%^a9&3ChqttJi-=ZNX{2;agv`mtvFHdNVCbgm9EFZw!rP`Er`%c}$#0>9 z(1wvd?bC?dE6d*h{OCB+A2nwdyh61i>fuEfvVt|o)>6REb~yGWQFsbJyNpqP?(w>V zf%W0y-yyxNJWJTJ$2GsJm3Sxd!6*Gp`0<{p;P#^|{m`O%7O&J zuJB>A?0o&XrXDwKK)6T&e+6=NWdmw;X_;!x`7gP!RcoSkdvkqfTiE&2oA<}-^4rs# z*iZ!o2J6AIW50kCF6M+ucER&q%k}L>$%>tpN*q5)&RDuKP|AwFW@0e@+xQuHcS7Up{pxgqij z_0Dd{`!uRZ@>e!t$Cr)r`?w#r--jdA{_*bxXRI3tvh;E5no^~vn2YVcHC%}gGO|Ns z&^}S{U8F-+e{6?c!8UWjBB#nDa{+@JaIA?S6N^10291$5FPE;!RdV3H!xGFvM2%NL zyNnQY?nI55eBu)6-*BQLsRNg!hAmrZQI;0ncRe+t!*G`CpK!wd-*95Z_iHYMK!zYS zrMd*&!%P;5(7`o-e(S7GJ%m}VRQ9;G&io1qA3B2IByH>~Eu!7r0!-p_zlOej}UR~|OpX2uPmyObXZo@R&!?Fq6Ipsq{H)p=l0&zkq57t+J zrZLeRkUI)WBXBQo9R+7`#>t|-U2%E0=!$7o;a>wKo;|UJ@&|SfPNwQR`6At7etSSH z7MLcLQz2PEkV5~s-kG)g4+8I_P!mPG{;kCX&?e z4LsB(pO}RK1JilPXs0URlb!yF4`Jdsb>LAIdX9glZ7HXx87=%9<&`aF= zZa!OZ4YLvPX}b3#mK5Wm&Jsa0x0j9_W#GAy5MmUP{FbC!fE!R1oVCh-Y|b)P~y&_f+UUo060i7>nRh zeSV}AcJ>41-b4N6wNU#?L47tGE`AO~pP*Y8IUPzR(oGbix*^9+;Z5=qtV=9b18Z?( zu9l`XDr7!>E+(S0CFf1yqC>z1C~*J1pwXJFhB!+2)ThrDem;KZ*^53Lm5Odl$vZD& zl&b_aN#0B>Q1?}gHmEyT;+*7=c;m{vDYUa)VTu8^P1C=S$5_-t7j2xYk z4uff=WCyZzy>;aJ$MwxCC>5pPpML!=^wU!r4i4^3I6+O5-hc`pA=B@nBG2&FOu#Zy z${Uy`8}8d{7Vd_3#L-UHTfZkM`RxL0A#*Artl+c&rvZNos<9GDLqDM2@8tl-TBPWX z#++{%=1JnK>Qitw+exzBhO#4KM+-pDeKeS6kocVd6Qyoy&dNZoVekW*zcqI_vKOnZ zQ}(Wqc#tYvb?!SidLSlYHy;8W70%Oy3ofv{H8&Y1A&&^ClLBpH*8n;;jm251*1I5C7B zgZ$&zLZw$SVw7njX%UTkuTafw-179Y-;(=SGN6P9|2e?CeGMif0C3Ee&-^z^r?UyG zG2~O+8hVyJL)4niT5;tO%;ubSp|sNcigA<-125C$IE9Fe0K159F^#uaCAek^ntM^l z{J3T2CjtLfetb5vhEL~M8L&L4hw;=%mB7Fagk}SeQ)~++!;UI6J-~p^5>f%ZA4T}NDU;d>vJ$#1Lx0IHM9 zAoeiN)O^qSEYtswefuhIcSQ9+sRVy{w0+o8z@=}OjCPmnma_nN1pWBSJ=7U(8&8B} zj(ruRo0q|>H~F5=eOd7mY~JEopvP8yBd?>IeF#+Qo)5(`-4VdTq`Wr;hYl#e_Gb|A zm6P_>y!9S(w7T=1Ti+cy{XIPjpbcA~SNPSMP-B4^(B&S|D!kez6ALt zs4qc(3Fb?%UxND*{Fe~Eg!m<-<}P<+9SIm<@xFrTR|%q*Lehnd1Ju#AUmL;jWt#r{ zaoifsIV(p*KicIer==^g>MX1khadr^f2TsKeo*v&h;EgC6rs$GhZT4V6_czo>c^wDx{ zy0p@96QNHBXxo4NV|&*YQ*~9_#N?vKW6{Q1aF5;DX-Bc9o16O4#jO#r)#}V=Gv%y~ z_H~%y1sd#XgQNj{N*kX|0He!hlCN z4$*Gze9VW-xg3~?=hvP#`fkXu+f{Ywf%NA!_&~6s%02H1hGoIIO_ezhyRT%hAz*0vY5UrCdPMHH5-2X zE{5hdQbhA^@S?rtYNbjO+-~!zy~8U!&oBR7uy7u&efY&Rzv?BqBrP}QVD$hnHfl05 zpKeDxFST zK_)2+PNuJc6keV>@J*IM)o1n zI`=Waq=^x!@|&sN2_i&&L)2jw*bk_$-gnS7_;_Ica<|X=+MmOBzq}nP7(8&s^09aM z6TP!dE{MV&MKldS^J3hu`1r$M(%PTr{gy|Db_Po6@8qo^lOGwBswXJTi9R{`O_ysg zOT+Kcw(r2J{e|1BLdHUDHe2_nELOAZQx8WVB|K7eV<8^xY~(Z&9FL@20fjhs3XEjr zZ~``&Xb2=JDAIu5qX7wJv{-1MUd{~hY?v%be5uwGw2`O;O#`T^LHZ9|idG<`vS{Bu zGlOV8iLut#9ziw2UZX5S1|)IY%R=<#{$flu;?R(l{itY?S_)-kNS=OXrabgibA7@< zo^`aYV7ddjuieQ|tWtYIiv~sU9LTi6d}Kp&j1eMLV27w5wfmU4wgORCe?hUDqKJzU ze$zS&q(J?O4S7wn5;L%?sW_AaTg^mo_ss85Q34C6*ZR=H0(Iah!SSK|*Uyb`CsX~j zNx5StiType1A^2@Lg+BUC2&0zsEs;+@MPgusl}uyR*+=k1%ERUSV_1SdFNRD*2#l9 zP_UJ0f1SmNTI$gh3f!1+8L zQQ{RQKLou9jI>Ze6tKXK@UbKi(}j$-S@o5+i=-9``e0FE(~E;oV-H+p(UAnR`SBBT z@BS%!NFSF3W)n}ISfwK+RoD|1_%$C7`cCtR)Dw# zEGcMy6W9y;k3%^s@zK;>ceoO*olH3Ii;!tJziKcTP24BSfp-_=_;#Z^AY?7<6v>O+ z5)L_i|EpfC2hGKn=qdX32bv01ph;rlBv=Wk&zJ+uIi_q9uH4?4mPkY}IrJ+#e8?su zDNPWQIB8KIvM1lJ4jnaByn%nWAZek=8-g^0P6bP|uoS)xa($#&y97(|`!^C64tyLv zE4lkmc!YdZC}&1<%0br%V5z!Ujmp>VLZB_P%g5Y(xPf;k@NC@CrQZWw?%uF~eG>Sh z?aDAaS_*kqOn!p$@Z(p2WNas{JJ($s9XfGQ5}t}M8QXaHIr7akL6&>d-TtH^xe%NN zmobSk7OoSC6`TR)j%+oSuU%^j=3#dP=V z>;$r?l5)5`?%&xkp|qMKY3U0_TjY+&W;8?ZMDh93b_tsh?VS20aKz;Xai%vmItJT5wIc$=+Rj=IZ8d?u9no2L!EV~!plg-Rd$33#*vgBFYG$; z0SCdVS0`57fUXiMU^sX6Qq>AG!6KWx2M?%zmZtjQ z%>;ZT?EHA%--ob`=&1$yiwgzs7&d%-hQAN}Xw^Go18u;zhnk#esFcfMlYI}^ggFtt zPWp@w6YZ(J4WrKV()4u!!2m@0>$Oh2^Pj9!{dHR_p8V0Dz|@%G_C38dy^mU4L{q!X zdn3)mrHIe{6e2req*bL>mBx?qgspG0e(sa6DPuiVit~=(z(>^;XCFguXSC`q=}=m!MQ!=F1zwF zpPIpHs;_nvyHV&ppP3i~Vgd`zXA`eu!#@NyMmHZ6Av@u*BT|>0#Dw`DNsRqbD@6s+ z`T~n*a}vuLGS_8)Uy?SC7%z3XOg^z$C{rZbJrqYJ6jd{o=L!RbFo=qV+M9g&uc*2| z0nN&rE5@>&&s$)d?^yq7RD{;s>l6;sk}V@RI!dHp9@>Nw6h|!1nZ6NI=wMQ$ z+iGK)-H%)T87G-Op5wP=AIb6cg$;;+Vv>31ZabZ+E^ylcO3b+*O1Wuv0u2?rMm8nV zTW9_t?2fdb3V2yv9@zsw<`iR6W_;ZDC{BCef=~)mVj@n|?X%4y#(jmdCOfC)yMsU2 z!{(kiUwNbYeZ9TT6gxjmGXK0pD#d@=#|@}5$zx1TM-i?!GzVq~a)@P6T;%g=gWr7o zyr4{byz>_U>IS6xv@?|p!-Eufj#y%hSt6L-yOh%k<9{pAQ5Rt~lGf$5=#ACFkm-qy zq>T?GQD3L2;C1h~B5l0oR1oD-1yZ`bPs{tRGj&mK2W32S`EFZG!6sP%7-wqT#g0a}!E7~$Q9<%F+Cp3;I zjNw1MyjquIMDAsB*@;e@64hBd$h?Z*g&V+Fg zNecEm@C2@%BYG=VgD%rpjXnC)SZkFT62NJWK%8Y(??H#&RA*zDb7p?AF8^I>Db%1{ zGL2&0xy3wxaKl&p$p&?c$zb~JeY&eVa6qb3LFWepOryfh&+ef&PB$!CmEgJYOE=}r z7A`;QxV+(@o2v-#Z+G2e%6Ez%hL<#3Rl_f$z~R(QE{gK?4EFEqRu5-B?$@W)mt$N> z{Hm5IVa4p)d0d#Ve_AIxOk>?IkbFWV!&H2-#-TJJY(?RXVOdV@@3XH2N8SuOpqN^G z4cyC34yU-ngkD27v5tKPYqeXTHKgQPi04^`2D7(CF0U38XG+FxQA*m$b_OT|r#lcl zfOwk4bG!T$xKu{1*N1}89Tnmo8g7vbRYJa>hOO_Ha|??-?fQgWPZoK=H)%Ij`>sT6fQWBd{@(}d03w?0G`BO_ar&>- z)_1c;{`+(|MWlB`+iQvLazJs+@*1lnP`6!))F>Ed=rQb)0;}6I#%6J4nOD9Xs;M5g zTx-G`SpI(f3e;VbM@`-M$d6uBR60ptFtE3=$n6U=K>G^w_}QK_cYw5 z^DK)>zRzSO8*09jA32Qgbch?4UNMn&0PD(0~w`pKBs>-mUt`pv0TvE}#23L&KZr;&3l;acs%=xKM}25WOoe<`sQzd^&PE z&0@AEEU$jFKm<)P$JY}l9f-nvD~HRi{F5<_-J$Pz&SEwf=eKi?!RCBz)k0b6=5&=C@i0zd=aiWL#6giF9MvO%44mj-N2r5MM0mhvxU*t zr8p6+!p!dva2+rE^%mVdG73G)xDJ@ZjfH)-9zAvA@exNLkuB} zbYpIxqq|e3-j2Iin3xW`SX917k^NzY*n9u`Sixdf^HtyLgpZRzkkU)m&|EQe2iX9( z8GjL2b~orUNR=1vsLxVGCpf@>yM%Gc3SUJx={59I}n4A^MjYUPH78J0uBN3kK8#|DR(w z-X!EUhxW#sgG?m&2&C{2v7}3BWB^Z*D)671D7zWtI_;Qo)MHh%9i$MmcZEuUKi~P; zVha8B7nR7O#x&G=`oXie2$4W8nTN5(zyTX0LDI9=*7Er=$!EHC`q6i??`3oEm)Opbk?{0v{c$A&*`&=7zO;4PCE$`&auk%;NdMVrmez5JaD z9+B~2IhFFYF;@S+lB<@N@kFm{`;Q4wdd9DpMf@)aIG~t^jQ-bgmePcuYscd15-y?~ zXU(G?aO%6=7`zb^9oFJ7MkAB8;e1z#kMe7B9Q4=8mKZZ)<0WE$?n4mEMPLKdaXXd& zZLJ736FkU5R}qU7yiG@io)*zk*~4HAXm&XLkcuAtpum7U?srEw>_hZ%9K>xUt#B{O z1H?-JF8$y;@lci$&$r`zYGb(q9@fXiL4QSH@Oqu|cn9GUhc^96>J`hL6byU|yj{D# z(re!?*gvayYrG~I77GGLy6Ul63m@o)5AJ`Q$EWNjTVAj-buAr zMLQDc+Ccq)Hm7a7lIIRl2=!Gx=Cvm4^|GqZXVn$T5+y-1)$cXw$%h!He8Le(I(B(Z zbIhq`*n_H@8uM+}OkF5SC*5X1IMrpjMXkok&j>CUO(pr?BeG@$)`UB*zX639g=J}w zXbnT!XX|C|3Evkg5}6M)I1rVN)CW;lWK~-$gAH44uem!g8X3XRBZz1n2OGkaX3B=D zZ>E_4f^AIFRY`t#Qq^f!zJ2RuoUiPtYYA8P564x~IRn)VlPI)WF0z`Y!bP0sMcc=9 zj)0*rCAC2?&jMX`HI^v@EAN$vs_zO19kh>|e}4G)@yzDAX=DjZ;PR0*874Rg2*KBQ zod3UKHn#`-R5LQRHT$Px-cM0fTw}%_x=eT=$Xh1?N+&esw)dD?oxK17}0x5@hFykE- zS+eBU*n!M&9^%V2CfQjJoasd*Yy+f;4#9kf=--{)j_%9P9ld(ADG`pROf!NB*e2^5 z?6g@UStOhiic>}Qe!+#aa$|@^kX}0`0s=FOsL6cdV{NTlo6a+WBDJJ~kRZtiUitBr z|FCXsp>l&tOq$B6@Mz*y=W`yC%0eL3CNf*3?*l#2QJP{MJ19tGMSCfTOf#6%jKy_e z+xVIU^F}L$^Rs`olW&K(t0?AcTWU?$SBT|#@_vYhh*M+@BNUMn=hKJVl(slmC7(vH zqLbmIU^sS)a9}#AmSNA-_;8!*<@$ojUd?}P1{#d1(aj8OoC|T!+p~r}R4{@tSvLYHrW54WYRSMUS4HK3m>H9k)1S&M%ls5)e z0c9LdB|hky-#7Z3a=>H;IgV(*^n=|r4JKO*_x4pgirYzgWqX{`8T+MV<}8PUA{|uC zx7|b~`Y02A&dsy9YSKKya(jDF7$i=t=nmk5_+8dV<_Kuyioo$bAnYdzDm$LncX6FZ zl$m0Ms(QgVMj{yhAY&B~G7vBZd5M`T>zQ2sK4Xk%(6nt!AI!}~)tV=e1C35|(;(I4 zWLyQ2{750P{GZCAFaBx;;)=!oIs(#&%^I>JEOB+&A|2{7JD$A>*!$m*csM5!>6Czf zF{(zmN-#e`2mP@AkbR;L&V*ejTd=@zz$KavBWEMM9GCBoJ1q3iw*gpkThG)lVB&~PTcigv9Z zZznI~OJ!d(!&Y^8&D7LOU#z{7)eE^}`MUq}Bpw8ge?y2}^Uet}IVjLOzE`3D|C<3% z+k1~a&Qq&rFOlY0wQVxP+aoF}Z4P&w9_1$HN~zx5wa+$lcd3Z)ot*#s?Qi;By17uN z$yP#Jd#a+8ph&}`sS~p1F5LdqW{-1YsSjsZ2$#{72G`AherY|mdN{RFJUG+9uZh>j z^JdFyp4>T?Igd@tUyx$N?W*;zA+`fb5I7>0l_5%U@E+sBp zb_!q5iO4z>@UTuJ-Q8 zsTqOKa+!3@gRK8o)ZSn5;4bUtyod3{s*W}$w!i_H={#>ze=`@|vTQT4ows>DyUT;~ z6TY}xYrda+eECzEr-q%G9`6p%&^eh~sH0wze|t*67lfwlG>FZ z+Vp2$e$+vCrz>((*ZQ&-d3}-SSbNKPlJP-yg?TkOlVZ-mBZ|Z0-sw)`sph*{lV#-!OIdpD!cP8KbTMx8t9Gvm(c9A8 zMSF`*v~5v*v5}W^@fzN!OLC9pre&&{eZF&yF-Gm18k0G%EeO71OU1a~wlyiEU|GDhX%ZI9wJpWEUNEDy4Y++4|j@VN! z5%(l_9TStwg&N9=?^d*Ad}c1W{&u0|^FQ?yroSru5&ksCaOS1!`4?v{_LZnt`YF8~ zxY>1s2&a~>`qLyg28P$utylyDSor-I7$)2M$xL6xA}AmYKVEig9425oFOwCJ~{*XmbQW~+Z7P8F|q5RzR5LQ8DY4m-N2nA`=CxH|o zHm0GQ@PGORkOIU8B6J1Qr}MD^6~LDVTcay@KivnU0KU#g60NAswoHtblh0Jh zf`e!BjYPmc!CS~b&`unRgevG+`W4y2ix3I(R0D-aNy?#Lmi z#W-_%4u_x%$v2A({r=W&3e9BJ&J$R$1@8=X$ zkwzbpL%8PM^naX!_Dmv|rrUD~S~H10n_j~us3wEnAVFw|d@+3=m!OIaxX=aW{p-LT zsi;vp|Mm2LT!P9n@X`U;umSB?MF8h_(`~s0ZJ2^SPOs($+FtZ!`W|jUSEi|7r!(^i LsZ$n?$gfM6iqp3E)xgP;^EB09dn?2YCU1&lTMtIsd+d{~(9=$-yovHe1wH zpJvVX2rvIE+BTSef{g#@5Mx<<&YK$e4~ENsM^F52${&D#MEwu@m&LC6KSqS1KX4;2 zi0>=@hzh^m^HY;@^THn+|0&b_WL)?U638b2@;d+`jQ$|ue_~J{7-0A#LSH!99}JC8 zoc&e`UO3qg!@_@%$NrP12{jg4n_dORKY{fTP(KHG|F0=?PW@(MyOZ$=C<4kTlN$;d z46h6?Df#iUNxyAX*pomCE`MMA`t_0xMf4T?+h4)&6ZSuQyQ+<@uH$|DE!&RI6hqWi z{t$6>HEhKD7IJC58Sje~wa=`JHdAk|^xpP^<+f(?&9B0=?!Mf!ezmVS13 zpx?k={|oHPk&@1D(tqC}<0Op#W8~|5$>wvfg8fxnlO%y6_*RsJ+kprQr=ZVMMR6R3 zgRoC6-e-Qf-gYOyex-2)-)`WC38qaSZF?E)z3oY0bYET!ygeH#zucnd%@$v%kLjT{ z9$qKNLb2P1jcybB-`vO~XFW%z6Z2yl8?|%zZ{qHObQ{f*k1V6V0f1}LCh2N(Bt#kj< z`Qu(_=<{N%?UMZ}*?5BfxXkyw?+5F@_5b>*zANW{Yr1|oM0NS))%f*mw{8EvF@HSu z!#?$M%1hkWMSpe|v!Zx+Ubh!Zs$czm@=F5#u)@3bV%2@N6~&u5w{yQp;&=M_VYtQ4u4AA2_h=WtRQdb5 zya8Xc<8A-%*dG9YAA5)SnE$?sci4Vk4E_HXe_!$5d4D7N?~BW9%YJ_kp!l)#KL685 zyJ=p{SMuAOt*dv^=$lZ?wtGGK{a|ba_7-lGJiK^0-uU^~uk}ak>z9p}*S{x!&EG)rDMb3FFPI8%@1C7)~NDw)%M__<5vymR!AOh)uh9^EH1cKAZ7I zx6Z}{%ztawt-oiCjn{|nv%?MDKDF+`h+*FC{mFN#&aZDm_RD*D^ksJZuK95LSK8O= zz0UYM-oMY?eUW(%b)3Bif7tb-^N#E$3%@nrX>)7+3F6#V4bSeS%L}a723kH$GFAR;5zmSkXVh~2b z9L)XS*Xk|7Kl^CY&@cEV`^+_e^y%}{LmK*cJiPkL{Cd9q`qdVCo@GsjO&FQkeq@uIlQKfML|_Z6sqR$p*Tz_NvVHs1(`U%sut{JJ4O^C#z5>`rJzDv9Ta~_ec zt9$1ndt!Po!;>{SiDQlHp@+C_uvGo!vBK2@eBz_hm2>FN|%A&xKM|5%SS2=z0J4cs3b082N!=co3Xa=J%J!VBlE- z;biL^O?LZxdIP=&UG!K4k~*HM*QcK6s@P5|qoz-QA3ej0-Ge{P+2fJ{59ly9QqOx? zsrnA&ElAuUDrT^1)dl;d1b?7OV+9z4shYv9b-c<*uMdQMOzyxfMyyBTOX-}nSn3aze}b`jy*bALLV0UB(Z43>~y>4)&h zdNZI0)Ef{6A0yj=Jbtm8p$_VDFdROXdG~P^&MWV?WSu^8lbjaX)^3x3yShc!rrW=^ z)eknn)@fOs76l)O`VCmc{v607e;vb#3C?3^H0cmucyJJfQCx*kJe)O&716SWOeE2x z;>pY>)VhG3HGeQ;psHH^z^@I(g4r7|ld%XERYQ3^iA+6aRSrsoWBS&7L7L7uo(_f8 ztk-Z2?b#V<006j2T?c_gn~G2C^K!)I{sBnPRnvAqGz`;$&dKZ~x?))jkeqK;WH~+a za15S}x;j3Jsb>OEsR9LitUVZ}p$QgD%;`;OH3m=ydw+rG8DQ72G82EWyNRl?6L!9P zm%}t%PG)5@!BaPm((IGCd%LEm8%nQJX5j;7>* z83>-(bc&Bw2aGcB`;pPJI-3GwCN_tXnMNtCpl5If*K5`man*IMW2Z>VpqGSTG<)#I zv3jJ5V*v`C^6l+G%Ifnj8hu{w8cw}Qp>$56_-nsIM>Fl_%Qn za#g5h#FA`@3g?N0ZI_IXi~c;_Nk*WCZgp~Y#Ba9eTI|69;~h}vY-Bl@wI{$R;XLFh z=zlH=&5hE^vu;?SRG-``OCeJVw<3~RZUsrzIN`C8W<9FX&ce$jVe^W|_DBJS>%nf} zPUbSCPg{&hC2^JdhHS%eJwO6H!vl6oH2y$#*HFV5d^FbsW}}y@IT8YSSbRAlBDM=k zWKW}m@f_z z9q&ayu*3jTg75}BIIg%p6uH2XV7(5p7rPv?#(@VuO-@E#4P+UOMUTnh$s9zMCl*;1 ztP=!ZI$l!X`M_twkgj2Xru&gsB+1ATv;;jk`uq~lvk1+gh!w4Q8M$yKhU~7aaiqyM)-OSvk1y&6 zw?Rf^Pt0W64C67c>WSe5rp-Kk?50;QWwvZ_*naoCe6W^qq2sW;_SN7gP z{YRb_9?&~an^qi&s356ECqw_0bEIN$}fjk@+8x=FuVmu{0Fxyi!ha;;D9|IyW{R{Q{9UvYn^)YccxfZCqSq~z#%a>>oF>!O z^aiv{;6xhOQ?sj-9lJxE4$5?va)HoOt`)V$u<-OAV6F4C0O__=7l*xFW2Jtmd`b@o zBg^4}4(yqw+BYBsDD5;Z)Obxa7gKLV4_X#U0Tmp$6Mz>zOiQidK7Xc-9oSi%c!@5b znp+VKJ2!+*+PWy#$d+*(Z_mre2S+QOA6Nl;2xnGmii};w6COhsgqwbI0m{qJ@1N}g z(nFs~ZEb9w_GNdG`K@CQH^*wcGu;G^SluqdGd$o`c4c*If|Ns2zhJbpoeK7=VvWsL z|G{lHyuCfLa?F~SV1IXP*AgAcx&+7#x+CIpGg%`jL>7hM*fiovJDd~k%Cf1$ABYS4t z+g)V?^Nv{^U|y+y?N8j~^+$ZAOP&Y&Rb8L0wI}-H37NCR&VM?CqX95RK>y(f;URc9 z^BM8c;SM|ZVx$0L3u#!qD6EO<$yrgBi*R&jzwVx7&3n8b$NMYbUIJ*;SG-L=wLqdT zW85oq7>mu)7+N~%%dM?z$7P||4A~|)7}SYa43;|1cav`FBUltDr}-maj>k7(t++xT ziW@f%j}K_)CM}sM6K9yY$&_&`JRfg-j^^@7^gu$1 zI|gVDGiic|?*PjlDU`!Y{kgQTvVA6WH!@V$bXIs6%70i!ZcZo=LVZ9ULqrg#m5(^` zNh7-I@Y!LojZ}V44c@Hj#X42t8!!dwBL#T3OCo0r8waymigbODse6uu*IN>Kz)h{L zPkNFF$}l50btJ*qxEBxlwYnWM(;9N~n$ws@z5zjpLr*<%J(7b)N{3+L#Q^hB2lm30 z4BDyV!G8ye#fsj!W=C6Gysj5|(1f(kNhM&*m9&k9LwPM3`!^sAMQPlVXzlqC3)yoS zszgi~z<9nk%9=@Cg`N{|)vVUes}~K6G-kR|uh697 zV1Gb9E}$TkSMlLooZ1rLNG?*dIAgNcv0&sOCmsRIBbT+yBf`LhmEwBU zm#0U<6~z?Vbad0Ady=LutGM?-k&~+M_=wPoUTV~bE(3DTC$YY6uKA(K1 z&&s)W%jwAK$UVNt40ZE-F_l~wMgVFZ9)H#a?_5_P?LI=$H(-dv{3!nu2RIuB|ij_R<{PoFes@!G-<|>9}5to2y9mrL@Z=;?!{%XIVDc$l@9xfu}a$psw-P7nPT7P3;W{J5u8#!^mVez#NGg48QdX57_IzgnDt`Dm$@1bdAgrP{>1WJ(A?+;PF`k~ZPOY@6Q(=+=Yq z%&m3h`pVI(VW%HDiyYEX_O@a)jgsl+o?DityRaO(e1GeaZUi5^vRdgHfPb_+n3+B} z;R(7aa_&$tJ-U?B+hBEUxAa0|Mi+>a#f-FE^9}`aDuUl5N=u0BO3rUUc8n|laxUDd zy^wQq6N{>$?Nm&ztBp%d%pp%~3MP#*U^b7f+6+N+$_XLVsdXXe3oMbC6h;D;h`ZY; zZoxMGv7$ox5S3`T)E$TXJjk zL<&U1w~l+Hm}zrG81GyQm$MFx`J-~zcJN&g%_y$6SW>H-p$)6zyd~cn99{O3mACPJ zk2SSHAY1HbPMe7CNgZ}RB>Oq-gT4psWuVTQx7A4J>p8I_NWlc#sDIhJmtbyMNslh? z6_wP_ITq8ZKs+2dT!@W$R-7sgND}7T9?L}AOCVL$GMnG7#@HLsZRoBVD^m_`ZpBD& zl66Xf4O)XK_~>DmrHLFF7V@B6*9S6or61~jh@*tN+t9h97a>nhw7`&rt0v+D69vKX zwH$dugQL#6ejvn(x_^r^$@qpui))r|Z*+G?bPQjQ_B@l}_C^Ek{rkfjV!6i=vCasR zfRYJdaW{kwK>9LBw;N-ql_bX(iV;rWM4GC8pO)JRo$@51U_qq_LCQC}7z_c%jXhG} zAq@}wGDHbrwbW(teZYI9q{7cbKE;Q|o)Z`|#jVT~q{kr0^M5(fnj>CK#lgLVV?c<; z;Vhoa64?C^wESlTC>Xwg8E_L6Mx~PI!9OvLL6p{r0bb|R(+U~K_zp>G2BoD|LukO1 zOb=enHS!Nt+8f=Z_InyVVgwG>{7^;=UZi;8>%C~-A`)w?I@VY~gex7&U6l1DMss}!UI1R59(o`Um6Sl<1nGDu443;(c^J*O1p ziBs=K7$MPlWzSeyk3z}B$^Zn>O(SA;+=^i>C&L!y`zVXn_L3!vC5O`(QubD;rHc_% zhu8wLy?=sTeU%)OMS$mNNv+GF_QuMsHr6BpdUzm*i3y~xKRh+yq8Nth;w|oEg<7L& z)EpP6zw+GStijjSg!b1cviS@GKCZHpp5^m}6t8O~oDD-E52ot2z(b8|Vy*PYis*|; zPJaJbNzKsHqv)M+28*r>VHso}r~1w|b3OR}`V zc>*soiyN+UOw*Wt>LE?61Oh+w+$_(1kFx9fj23hcy|&<}0lh|fzwnKWHx8~V-1eX# z$v(nzLTT3an3Ko z*?-cr#yH$bqv%z|MJri?(&zDsU!AjifTT}4Opn?D&EPY6RAkJ=)8JMtl@|^C;KpAr+R9*?l^|= zm9VM8b@r^6c&MBuqKeJoF_RpVb2w)PVSnRHJ!*`@)lR(hjjzFU23Lwu-+a4zFqbZG zt$7^`+Gm>XE*bgF1NN`(Wn-Z6;$-!pBT0)vAjrwMrV!1>JlO*=x|M!B+O6`MpgI2# zU=yt!0}-tpV99ziEK-U`^-wy%FxzjyWVBft1FDBUQIO<6QN1#))F2d9_8*xG2Y=|K z34x1R7|1+4S6yI27TRv*N|#9thrYSqW4-kycxd()y#%*WMDe5+vH%qU6~SH2LRC+W z?`LW&cg;G%Pk`<(Sl#2(C|LERkq8~oe0aqm4(!NW{t%7R|+@Eo>K+mJ%0I6_M5Jt}P!UOtD zMULidRj}oPwv09wLsO zD{_0S%7@6+iz5L<%0ZTMx!EXY2Mn3~L9Tb@)C~LslPY{G+AQD^4JkUT*OZi|+FooE z2$rcrZ(FK^VdB(f*D5*N^?yA?F!yvNKt&X~X8#^Lw3?|t-(vM4pl~pYZ9P1JZG(g= zE-oVX4on)L04uI-9^w0mxo?M&kJBvQ6E{Bh1iWksin^d}ao_*@V0ZsxswKN!v^pGE zK)IeXU{-Y#XqwYvRe#j#(>%A=@2aG^0epNjZaTBA;IOE{J~^G)?bV%~BgfThPVSY* z5kFYoG-;shxa{htA>IzA?Wm-e>FsLMt9xb6xb<7hx#H*RL)$0@^X^@Ku^PE^tj_6X zec@NvLc!Teol%(A*PB9Pr`OwpnX7vxa@eMJq@^}*gH8-Gs9yUGe^{(>?0mVR!!6valVy>Bp$YITenTnIN-NUujtb2F!y?QC;5 z8Sgt3p+ciqtty}J0RNA%g`*N?>bcNzwvE_9z*%Vol zifTMJnUFL0^3EQJPI)?IEoZRG%Z@b1?Je3qF_y|a&x+^4k{covvXy3%(wFOvRm)0?1?zCS{uU)_p_YU=+m_h z>#q&^ZdhqZH^sa1Z4ilCXC;;08P;tEo1E+9Zzg&x&#~0=`L?|*^%UCbNIzAXE?W>5DfIrm4QP@1aqqUg8oqbZhpolSkjNv?iJsj}YK^w=hK z>oo@LZ9gCzdeQO&x-uNLYJz`x9lf3HWj=!B&#(y=nN6+eIdPDQ3>s8j$vcxw_VS~Mu z{9c_`8kMPWgBl#O?JdnIvc9{Cs@2<0x6m)%Z#Us~ZAJH_Houv6d!u!KTE5-1Z=&AZ z@6%%5sZ5Mo^VZ*h=@s*Sp;~X;YI(d;y6eI+8WkPgDc#%k?W()8XJwJS+uRtN+u?W! z$bV{bbH6T8xw$3hI^}JdsZfnQE&6ZX<}KFWx*Y)Zh!H9 z=uVK|L~eI`^;VfvZ;Pd7d!wtBru^6!5CE+_1e%fzqgJM}tmAJV{alP@fqUG|Vkj$3 z+~Bpyibt(#0l2Tk&Kwas9)b~dxxus11L+1mFgj&1a;xjRPHElBOY}|6JJpSA7Bs{3@k&jT7Ri9<8uL9 z!j*t69FB#8s}8|fFvjYSwH?Hnbzjh*j`hbf@IJxN&`+qbFE~E*kM$=QaQO)bl6_g) z8@FJvvQb1Qy!6ZMJnP3 zdxO3|x5heH|0Lt7v~WK!%y-MxmZ2h-+}NFF<(?h3*Px3DhxtXp5WzOgKE+x^TpTVbI;VakiuWS`sRJ+6II z5SZEGKHAO3bDwF6OWK)}Qm}@+q>;m3OnIXKE>zW13&ZfqiV@}D1+?a5IeG%qXdNGp*!%TXjdz^X;8vQLeT03-eWlZIG_U zwyp5aZnSnaSM{T{(rw(1ZdYtLP;-TFBjVlCzC+fBQLkz&`Nc??0sZWD@0&cmQ2FhB z-YNH{Va2z)o$iEEcYU`o8vq`y?n_i}Se{o0TxCD%tk)B%5PuEu?!dHBDAmg2X|v1C zl$*OAFja%Hzw4tlKEXz#XR)X<_bG=MyvZM;0CjemO+$x>a;Uga3NZ3KKo6~??@ zXU06Yk=nN$Ki&o#wP%j@Id-07nA!a;mY|H!73Y%dGpv%p=VPW_Th9OqN9Y35rc%WW zG4T^m3Z&jp8`2zk*XPIq)#66^-hR-Ak!;(uqPM?p0(})pfgbe0LyvK~LHsj;57S|Y zwX+Rc>VJe4NAN-$qTd%9EWpv7BMX26LqQ`4B1*2xAzHgl)IJabu@oR+M8wE*VBq|} zu_XLQ{0Gg_(tkpR?|Ud~YjXH|6X$tRs}M#kuVLQA$_jTY3rUe!UW5Xd3cIB#o+Lk_ z-&nSUFcAd)x$=Qd?;rA~zJ3Tl5GymBef_ZflYjk_bHFc(`cuNcxUGeuO+keIMv$OO zV(!^)EU1=tX!#gS7+N0zMHBr^SoiMN5B7v`Y}bO>y)Ln`x0X`@mwhP82o#Qed^3oa z#GfQVow-ede}AID`Qz=C?~RE+Y4AFI<~~CpG~fPd0xKEtS?}w@^i^aDQXo znuGPSc1O})3n)HwPE!Wm;-GCh6*}1Qx#l zvuvd{7?K00#xadne%iYc2K@rhTGdK!BCJKB7;4MhDx@ln4q4sGTfXHtrx?fK=YK?_P=-xfipKd~G&ZEJHBg)pMIJPY$4b>1;y+bU z&`)6l9vTudsVloHrnNe}z2gTcmXSucj%1VmQ8vZ%F^tLl zX-+N3YzhAhlm&iqj1q%Fn2P`P0~T*cYMH+8ZLn-g2pIGi%7a4LQ#dmUrhiU77_4;Tr)zJbn&)d|bYO<77tDHCTJ#sqzH^ z&nk)X1r3-gc_>qGrltW9?3$*1mzoCQw>D1KS6ZndonXWeNS2eXVx(=tb~50F1a!p8 zD#SnnFN`j0hGIO77j`Q$oPW-qMFGcBlmrVDJfI+c`63DmmI>+2!HU`p|}&A4I8ZD zOSFb5Uld3~(a}u4zwv$xXBN@7-Lba`2%u%xN1->gd;%7=A-XHT#4Mt%m5JXdxDEIF zwN*vyw4mZu^|Q?D#hFWtCq0?(dXV2tcTB(utsZiiiM+YA2+dksj<-V9`pR}l{B~{F zx=Jgo@?J++^VTpe9Dfoh4J)4fE6v{3)pa5~eunXU&LBPoj+JK;+qVwso z@x09Ix|SZBZcwVEpAhEB&BTBej#uSl3uwUc%MZXlEriF`(tmRUxH|)A_}a1&45(Y7 zw!?3$*)Fh0K!@HDLo=|(gm1ZH%LnMY$`BvGyCg<_0}AKW)tdz4LK~iYbs)S-OY~Kp z6*B$|Eu<$Z+3dH$(AyGifDjAnIBwuMR`4;yur4?G0u-9Gd0dN9J!-krL zJg(p*f|Z~M&3}elg#&`3-vD%CZJS7J=e6?dfK7AUlg~Mt<4Ihh?j`f`^fCaV=BOmY z-%#JUzB;e*4m={j(l(|){)AU5ShAWOxYq7d5MFDS)+jC3VJ^T5`6*bmgc%TKqQWtO z>NWYV1R5Cya6%^@EI|he2r|~C1?=@8D~`W?Nxhjn%YUb7Mpr0N=F)untPFZ4pH4qX z@@XV?PQ(SkH?48dh<_#+Ny$L-Jg203jBlY1&xyi{jjKxGvUmtEH(KI1ZD;Kq-iy^0 z@T@pK+@nwxqulWeh~SjOl%1h9_R_QPTvE8O&Y<*pL}OPzXyuhxl4PvXLBe!B!htS? zQ?YlR!haZsrwmOy(FD#;ZOmZKOl|rJn%V?6`jJz61x2;96Y>of&vJ;Rx%xsxF0}l? ztU%p?Ax1OPbu9-V^zaJt+bw||I&16qi;@%f{aki@etI~HV^oe+-+p>*7vdK>rvOp# zS@dI0WmPtletZJjQ@rW5@IEYY@Q&#ct*VS>Z+|nhM>7KY{p<6>xof7(ts1X-qq)DM0dz9MdOefJq}pO~7aeq|GAlz-Qt( z6@PFuG5`h*U`OU?Wog&SvL279)hh}Au#pq(N%g) zKzPx(>jn|B8A$?&Q0gOK6~KOUfI|An{*B4`QTTna(eS3=2p`7qq}vak=<5U}?YxF+dbV-3RG^w2^QI$A}IeHCIkAH&B zvS=sqQIBU#i$|MI$b5Y;)8HzLw7)aDJVFp*`7f5N#Lfb;Gqm$#_8ah)ahkqhnEq4T2 zJf7U_$~2G;5FVh)z~O{&gZ*%k5CeF-94NO7ZYWs3aIV1-a~*9$B6jNGJdwdW_vh zN0N(uZjTT1NT1GOyD&8Q+JEUB@m~<452xp4(5%i$vUoU1<8WRE&B~Ij>&Uy2I%9&9 zSO$e=!*bLNP>Fz2iI8;tkO^3?9ZVG;I~kM5N>Hbj$ia;!d{`N&_IbY2Cll^qwkMt^s3#q;g!4z9RR zG5v}DU)yVYpf5 zk2xaZmk&kiuShalRCw9QWB@%YULnEIpLF^|$P!kpV*-91yT=+pl~sJpTp(E#ep4db z!J9%r%x8{iWOz=K7=QU>&!5o^o}$=?5ka^qo{@zIL6#U~`U#ek6u=!m3+PcSg(fmg zCbT}Q$M{x4;VVuE?9w$|;^;FTTkC}#LIn*gL~vVW4NMA{kO3LQ$}vO8w&31c-duvu zKf`a)FYx{6Ezf3Bq@j~c=T2fpzuPF!;FTps(V%Il$Rv8C1%FmEC_3v*(gMdJpwHrU z=y?lx=w&-J{SdqZSv1<=!A$zEg0rAGUI4R;He-$x0c^HwI%cAWOXgN`CfXy@cdRgk zQ}GO)9LkA_J&jPBj|;a`C-C$#Yvp)oGZxHuGAIJj3E--kj3y2&XP!4s_ASoNHBcW7 z@?k3@y;G8S@_)o!1ML_P7kjdXpnHfGq^#6+eXirFlgDF}vjoNEN3>vKxBmz%s-Z>r zkqg6F8Y*@;hOeIK44)rvVf8)P=C>rBWCaAqOD#fg^}3#)OO2QK&MTofORk4?t0O-d zUbW|_ic}1lMLTUjh-cBHpel-TV!H9NC<#de2{v*PaDS6Y`Wz=AwvS-QBg728x-c__ z7hC_fi`7@W>uz1zzimY>(mnCIIJ@rh7aIElyIMK`{>`ih7KJcxvgx9n52lQ!oN1$bk;htkpHw_7oZ?gz0+` zSh`r5@%*6;v}z!6ee?R3F-xa4dsnm#vf!;X`=KydxbmISF-Z*qpsECzsyqLj5@G*e{KHzz&9Sf#% zQ-30MHp0E`M|a_|f^vMeAH)E>2M-xM+{4F%!Mm{aL3?G5zJ7owC|dsE<}F|mu@D27 zeQRDjGqfKReCdTRoYM{L0Z4-uC^)*QTgc&MWDWaB&VdFqDi z6%;_;$EzN`I#5UweR&*+>g#cMw%sgXNL5d;O3>*JsAfmv@nj**Ejt+nMQ zufgLa=HcVy^`Uok{AH@$!xMNW<|7Esei1)Ne}IX~$0ZXEIH1Kt8pZ)j;RydeTz|zx zO4B%$_=HGj_SmWdtuSsQ{^}%JM3WVP(ockQ7T$3}3RxIRV&2a(#17=!Gt3oZ`x3u4 zd`L|>1b?O2OhUFCbY@Z(4^BZDiLo<`6k+8GUMYq}D0l&08DOi43UUkl+<3((j0OHn zaLD3arW6h@qK(&Oppc#Js!$3{D}RRN#a=4Po{OPJ2q+&Ej(-O#+#f_0Y9g1hftD;@ z(%EM^sWNMdNYbZ1w#>}*l#xIg`Eo{bak4nWP)G*J-xtw%Y{b14jYEYO>eH9D3pIuuOpym;sS+pDmp*>o>Ig|-sN?w&o zgVVpe{roz9&_YD^1KFa$lYfH1tKWSa`7R(@$mX$W7V9WnfC_OLFKrOpCQ&40f$S*367bgb>0 z@Fks*1@&(d02hbegQl$#h2hIK+MdOeI9BB)4t?>SCO;UNtEbfy_#bwLoD7O$XjM^P zu$l5wFDWPL62ppF%wR@ibmQSxGJ-nw1fvn(x`@WBZm(i0AReZboVW!sPEW@|auN)% z$wCq;MGElzg2!W#DSuXasL6pN8w{QiaFW5f*eLWej*Tj6v|iOPo*#!}#07rvATN83 z8%j$@(bU8Uu-Jfr#eW0jnoTDPK$mO^E1R@)H6G)Y-p-{iTzMTQ3( zKlGOV424C#ZkUAauA(rRlJ*-eN%1lBxXw2Wag-IbhqUjH#37+JGgdzma{YBUxSrpX z>I{Vw#Hsr*-7ZsVTWYAEGcXOBWk^O;vV`?rW*X@yNv3gzu3USl}sG$1TDT4pMepv#J+2`59~WLlC%6oT-=AtYum zmI%=PYeOQ^^n>V#mCT1HwbLi#c?48bsDTy{^G!aC70_c@Ju?{D_SX+8Dl%mT5S$sT zs+Yb6>+2n_Q9CIsHFi;Bi}+YnkCF~|9z1OL9>ncs1AqAY!-jVuo(CHwg5_U0ujibQ zcvpzL%yWMr=;Dj<{Ph=tvw?0HFGFw;K1v0Ck0}23mHv>}?Uk>KdTv&fau6-Xp zQifND!Wv?ulw%dk*Q$Gbw z|DM(-a%atCPE=V&(6WSIeW$$zL6%8M%H|iqH9yYu$V&idCe>r2K*(XWcsRKhFPg-r z)<+^hrSHHkSkKPIsr>dT^n9F*X_=$h@jJM6Wq-ygF@Bb<2VORGnWnPro}832OQ!Xt zo+L+j2B$11shV_J5%QThO+Pus>95U>|C^f~FW)iv1hv8+2I*^T#kLi;BYy&DHx{rz zhthKONEJ<>3_Z@Eet%?~V7Na8s(&6@&Rb!A6%~NcI!Su{X{h*Jk}URu6=5CJp8~5V zm4CpYV1o}%97`I@(zNzERqU7AfUx@`)Q0c7D}M6xOPKr;CZ~}85+-j#vbYxeznUq@-A2AFJbaanEYhIZOtW42Y7b_IE45;j@x^e;GPxRKN{C> zD#-qW zEsK~yFfz;*PqGv#uvs#3(inQebsN8oI>ZE&&Wm}~;Yf!I>tTy)oa7WfRZC% zwgs3b7#a@ir&-&0(lQ{4kyMfrl`P3a&+>1-6C^d#NE#h3j=pYUWp6DfJH9AMBsE>m zl1KY<;w$#>jS;?%83!-Mmtg3UAb-;Eob~UDulUv(mdVHpqiW*oQpm0+HpFP_z;c!Z z%yBWDz(Yw?g-W{uc-6>>#+i5&rE7+wNm(SyWnd+P#K(V~1@`-s(?NVbO-@xaoF%!_ z5?)Ut7uPsNp(IL6GYBgjea?U_QWQjuWKV)|7!7cGQiLRyMgE?_R^kYvzkd^3DpE=j zzFM9KkC>dg*TkzTklgii5>2>zug8?7&aFG}$LlA|Ki%)8v(4~vnUE5W2WuUu0Sx=U z0V@8gzz1=(!9V};KUv|(cI1(!DLRLew4YjS3`raM14$(_Wm8XZrYyd7h8a&VKs&`hL5>a$>ySe#H!m`wIXA^CD*q>waHrGDWpM_@%yr?SzIyU<$4i>|Bh6ALZoaX?KmIqR*aQ5&HnVLX? z4#7iUcvg!I!m-vCI)8c>Jv*g~&qUjTh0y=|1SPZG%JLoCT@XG>==Y0b3aJS*AmcrM z2Us44=`adhXDWgI!{$Yr-yWKMv>Q^@2o=xr6j-v5SXZ-M5FcxW7*Skf{tQDt0-L}R zkcp_3^tp%b;e;$GQWFdT9Y~4Z2dp&9baN`DXq>LT(R82F*MDhURzx*RZ0_YA&%`?? zrYwVB^nU>W0RR8&TH9{h$Qgc>fi8-sh$S9H(yarCqUkk|Yz-@EFO3m7lBS_J#LUpL z_A2kts{-4X%ainELCj<0cc(lI){cYj+P zrYd^f)$lL<+r8kE(PCJ=UTLDNfh zy63q5X}|N+$yQN(^rxx(>q`>v`%fo+MC5t+{v)}OC4VR4wW{FH8-y!^SK|d*n9gy5 zIX+KMI-WT+%)sk+^r!@m^=SQ31U6Y+4Mk*v%S_&%xoF6xb~9Yc8Z=ICnX5tLcIE&l zd$I*|GtZ8NlJQBW0Fx&CnsnZwwNKN?_sYf7LGdA9jQqaWAMV!|pGtX3M8aty8NUZ7 zd!+a2;eV$UzV}Ay_h9uIk!MBu3(nJwJgX7^7+C&!U5(=uB47lD3)o9;UWIJmyxl40 z1yMM34l(Z#^InT1Qk*BG&PNaNLmfb?ze91K?(d8y3h`b?AbHb$AqjtvJ%6CZdvZ;( z=gfniF5_L|i4!`*p&Jel@#GLs4mH%FhI+ObxPL+Ep@#YiU5#@7$E=~;&>0LI*V-ZG z#izFK8i$y7hvk};4#RlV3MPi;-MrWMzE65S)UjtaTf_HHJ$v zA)Mq%1A^NHX@p)J*zqnI#&!<>D%kBv(Gwv?+d`K)c7uq{VK=S;@!|}?50RtM!{-lN^V3H>CrKuKchU!WRyO-l1{E2qQ+f=7CY{N znu-jVLA4~tOpg0Ais3eByNa9iQ_=%-=U_oyT+#jkG0|zlIz}dSx5LOWt-2>pu*v4! z&w*EAS)zsE$9a5cI&G9K!e>Q$2hBNs=H6EK6{Y|zPo6>KODzR$Uwvh0O>^m^R)iZ@ z%P6)Xvg*=eoRF2eou%5J`|;eyES>ouCw7B?LT&!qzrC7`)3tP?b|R>Bt~uc61SRTx z5qSX#TG5+(MqEGjX|N~>p2+6VV=rg}f8ARs2Hz2%CfjR`oAxpp>S-6?$}^TEfS6Z% z1Z^oIw~RCLDg=4=tNLtYjJQ3THr&7E=y~V~(pBQZk1u!bNmHOkQ@!CJILn1ytY#tu zt_0~Wo91VSZ>byZL2XQz!m_}`6uaN;%h6->WmfQxggogzMm&i{Yo(|>+9d;ui5}FsC%( zG;>MOR9((?(wS1OWdrdce{PVve;DhU37HHF=Nt4FQPa-3OP7FIc_kZx!bqDKqcdi6 z>>UnO%4{mk_C{Jm68cHIAUAwP+7FlW+Ey_&fptddNfVB{P zOs4A!yDey#qNcr|)H8%~&dF-X6*w27baaq`rpiTQB$pOuuwb$LghQmJWWZUc!2nVr zP%I()3|h!iIFhBgEZxc6kh2pX(A08p816jgZaP6nh>Swd(J?rJ0=*!W9_M+dQ{Obu zIQIQ(}k?-da+7*^_=Ppucf$H{M*3t1}OjUlE%}wxO_HT6cK3wP-xeZ&yzSev{%3kXN>2|!x+HD4%kFj zxW?*2A?2$)ac(jhSF}n_~kg{6X!6PAP;P);sp~hG|Fc~ z_j;D}1*J4^tHfAbO4Tn@-AgqBttzd`PyNeE$ft2ubj(Hbq%PqoSbrdHXM55}Rjo{C zy&LL{_-eG=rnjcp@c*O!lk-kNx- z96j6O*>L9v^wZ**HyfoVm%>#F#x`N%fvHkPPs5q&5;YX%Qpu8cw**xzIzPLc;s&gL05Uutp?6Lwiug4Q_T(k~)9li`P7y?iAqgxCJ5D+*l5D*Gb zQVs+%8lXOL&3cy+DdcdO7`yP)4?&dg;l(G64#75$mhJs5ukYtR%1}3nJ}~A-(O2 z>hs63)NG~ueX*2wk85!07D*vZAo284k*ro~^uX8!2cHS1R6P- zF-+%S!Adpq?RaMsw#h%l^wNG_h~hMg*LmkJ+U;I|(xam?wj`vY!bZ6VbIx3OumV*R zvjMy$>*h0kPGqU!#0NYOg}0SapBXz-7zeZlX>kY9h3TjNgcaWt&VS0dJ3cZnBm`fg ze61LfoMf`-g$>sjWA6*V#O0S(+HNL6#EFA*dSdkZnk;yx?$N+@U zHngU5wdIXJ9+gAc2LnYhNurPWKzhl7p@d9uRx`Oj^8FDi%s= zJ5fViXZ7!#s?RcE0J`Wa-p#(TvekM$&DtqTkHaDsY0~ejsE887Yq;( zGEmYE1TtzIqcR+TcuR2z7$m}{lgI6NDDP|g$P&*yrcJb z-XR_e^>ut#g7cE^nprYFb|R?aAIwJjZD3sh?uf%5j2K~PM+(zq915V9Dmq=Xp#h;v zVv^F^NWG+i#FEkKWhhYaBa}mjK`gliF}NOrq`~%edI5l5fm1XQBABsX1!Osc%?|`M z%Lbs-c%1in1vr?KE1(q7hH9Gbrr5{k!)TkmZkTBBKrK)t^TH8TA_LQ9r#|HVd@{E+uc{Uj7Q#iQ8d-d2#`A!k=`u-wq;2*f*U%D~~Q?oI5dO<&0~ z2(pXlk!lCP%k8)0fDjg`q8dk!#UJj~7Y>sxo5d)7<_&I1%F28$6g*CQ?eeyildafM zTViw0|8!}I?lqKRMV}0wT{lz_d?W;~Idc2Z%8{G0e^K?&$WmAVc^j^}ow|#=jrvP@ zUPD4K<9Z)+{gm2(aw((NcAY(+PKNnl`GfVxDUd!uz=Q2Qm8l9Vx*q+Mv)sgNK7Fpz z6~O{E%>A+ENq-WEyur*Fovic@rh}o8qpy(jI8&?&mDS2t)H{)+;X{kabIHC)1LqCz zqQ%N0@T`aoLYm92!G6So;{c#`r@(#eELY%6f$*|;-pLLbBl>d_~neCBX5@P?CIvOMNN~1 zskYXt`uii4fSo?8a{?FRP zp_`Okis3F+o(^x9)Z#EpVo1QgLN%6l3jfqv6d{{%Jozf9jb5(63|7HnDU^Yr;^5zu z+i-KU{T`W;eKi6EY@_CAuAUqK;qyVpY%H_EwsO{A4pe_?ypH=kvQ)I~a~}sO#az0! z`fy!FL*K0HR#+HOAKgH`Y2gWoyu{^_Q=h{;zU)##jc}3=1G_Q1{Ax*!2$G4`{09yN ziVRowo&tRkazq6O{hcZveVO|;acQ9PkcmHazw1;(J&5qU>Mh#oUfr4_jK!v6&pGjarqYbcUKXm3WeH|wfA!FIU!aUf5?0dqau z1hfQL53)MH+@!AxfPEI~Yo?hK8TUXNk?OF9J~Xo04Z;1kyrq8$L|!A#6@s#8H!zu` z7K<`R+DcNZmx}>^IeMj^9~p(-t4b$ZT3{44^Jt|>v%aDaR7PF=M$8`d*G>GdpY@Pg zJughjhy_Au_V3@rwpHuEE`dUJf^K8<1sfL)NA7T|y*XmOGFAjsxg!Lzb}uJpJ5{=+ z^}SF1TxX@PzN~)!s<+=xZkJj>o$XSx^EsbPOGg^j$(9pm`?3XsdoIg}!F@zbfLTBj zt`_&s;4O@3{e{hc3~P=r)cZdS^VYxtr2U(pOGbamwXW&ku*Kit?WXoODUrJYMMT51 zno@~xZt~EL?*!7{X?&XFe(^Y_gEo1F_=I7p9|rjkZrzGvr{ zSOJ*CY#Nqhx4W0QS3HxF$8OE!=Wf zft>%#G~H4Lq4W@b^vFN2GYZm^-%MkXV)?&Ji_E$%{a>aD`^&?gD>?Di$^VdBAetD8 z`K)Ok;80j5qZ^9Jr8i@ZoEN9ql(sEN_6hbV;jFn!rm7(oZsW-!zX#d>8|o!E-L)r(x>CGx+(CV^zBZCrF~1E@0NimUhd5? z$<7p~1sPs#b%v{w>7^g;{XgjgoC9IRKUhAq8YnX{TG7ng-DVcZJ6N~va=76xXPXed zX^!J}=d(>f$oFzpF2A5iGV7-bxd^dI&A0bxzi5Zg@6-r9mmbJB)W0*WR$5t@xHMvQ zm%^NO4o?{9|6$3^UbDlwLJ%;&Rq-LFL_}m>r*Y}lCX!#5+pLu|bMd%PX(8}uv!ztf zO&bU`t@SqyZ`>K1qlS?)3%vU4v}p7IY5@mXbua4Y3E)A@vt3JWw3Lku{4S%I?K-n} zZCC=i@25~8J;o)WnuD@gYiutB_f8F4uL1Qvzys6%N%Oi6~qNR8Cof9&h|$G)_GI;UGw^}p;xk)G83r*r?y zKHsiwCrq?RpdN(2?66dL5xN@11>gx$Uq0pFdMwe`p~J*IF}%T>R20Q#w=2wjRqmI0a@%!V-`4V<&bFb1UW@ zuFDMA-7ePfPjNjqCI9jfw~U3)4QSA*sUu4??EmR|8Dg1SEbQNQ`-`xo&*1O1_E?^| z*^5E_QDb?=#>ThKNff`4e(M~-Z6w6ce8}wH6GkcKW50;gMnQ{d~hDU5|bsJYIX3=w@91Gc}MlwC=_2tmw^d z&B843hq@3-;>_J*5Jm+d!{>Vu;o?WiHjbQ$Fd!osa8wb9HZ>>K>dp5^;TK_+F)OmlmvrbF%_DsWzr0!Rad%31wCn0}?%_<{`YsJ5PNXc|}D{qbsNiw%i&& z;7@y0N*nkH(OQAtmw6_WOd+%_*iMdbu*EFMGni;klI5%@XJq3d<4`?)HYLhjzNfV7 z%CowrK{Hnn@Bn(ar9I!I;LPk8IFEEJZf;_oR)uqgvDLlU{A$2tKq{N`QOXMys$T&l zkn3+9Jih6tr4<7#Mp@J4e_x?92@&JVciS zLDWcp--QNkj=Y}Lk91pm@OH-_85h&%Vj-+R3BK)O%}*tur_jzl_r|hu!cvnF06=YZ z(>b%rivx@>V7HB_W1i*JKp-aj84_tsL`Fb^n${57KY|++Kt}ug>tW$IJ^;}HYK}Ix zHA!)M4O!Svf_v=^BdmCpU5au?^H+3%?r6Ta!Xq^hnQmuZUFBKw=5fhnc~3X|dW_>x zpEIL-|me0pGeKURGW#*L=Ap6>cK}U!-f21Z4X5$>D5trNKMd(Qr)jZ zIP+yWa#mpk3!W)saCFQIK;%Xpp4{4_?t(JMuVS6dY<-Ri&Jj#m-w45Eoqhcf&f)~t zcK)_MOXog#L{R!%KP<@LGbT(aK^jo%+$H;dfNBmO)yH_iYppYM`ybl&>fvtpR3xjd z4`yG-f|o*IR-=@}<;( zmb#kL5g&GuTTWgo0EWTivkq*VX44kL!ra&WQ_1akg67~^eXJs$%(#RxgZlL(27IP; zFmo-b7Tz7O;2{4nLap}Iu$Zp>S+YU=y=4a3xrOWE-iW^32OwcJOykH9T%5F6;&>cv za00NKvrx5L)aG~wE0?9f8M>BTDrLH0D>d`rQRAI5O2e1ff44jwMr|!+U^i=3fH=>^ zac$1YNo+h}rY`VblwZLsOc`ej2Gf>yfjy8-Rx>5}(EL9*2+?DJo z=y5bs*l$2e54H7`E;q8~mlIZ$6l_diYsd$rqDY!qGA)`KpDzF zg?6+I6bB4B3uqgLaaEQqBO_xm_gD$wZPBL@X?mmPFW!?OSbE@$5N#@_YP4V&V-Yo0 zj4WBT;HVx%aGzPC4c}DofO1SKg`4E0qtxMde!c?yRFC#nteuR7k^x?Cu-q8)=!>81 zIT=gX3MsaOAPR9BxI0`B@qgocJE+pI0Oh4KJ4@a7093p2*WjVu3)ih}R9d)-!aRED z{OAufQaf}(t^OI-S!3%(C1rC8s)9q3PWto&^>k<=bz?!Mt)!TQ3lb(b9_mkMCdq1G$K4QJ_)%Y zi)QG(1-WsedPC)y9h+dXa1-Ooy;FJPYvgvp%!I84ky`k1i+Mrb+yOV3Wn%;JRJBR< z={o4~FuRRgIm7)pn;q~1(85PBI@rj6*G%3!qm{57PtZLo?n%O-`3i1N3xi*B@waw) zf7EKvawdCq;B0&;QcvhN$1v$$|IaRh_RrI-^#6_@IN~5o6#-E1Ks@gzd_cEjRfy`; zbkH=YsaxUG4a~7(1T!2XChU0tPsFP87$p^sC_4!Udlq-ngX1Mcz{eOv+0Acbl<>*1 zo6-<#k-Jjr!CEa5huEE4E=gh$Nv?`YlGG7LE@@)oD#|b;W&Th~^l*`UT0LsQBT21D zoaSRaERFGN7yyI=pLXP7vhu!Ogl{ASlc8v%i|;(D51#<~g@Bo;U<&h*IiwixzaW60 zSw?V`B;5@swl&N!BPjoZc%_KwqFJ%=Y`gvk=T8}`FO+nHVhuWtJwBVY&Fmq zE?yu&DCRd5S5rg3!}u|%WoT(z{mXi7;dO;OKuYi0_3vETj0?hhjBnS7^j%jrbS`#D zDMA{<4{i)bhj*Vgf~>pdONT(mjpeN)*n+1EywFRSe6!xsQ<{VA_SD_NM(caC*5T*Q zDk~f4TuB*Tx9Yny^RIG?5Ls%Q(P6UP0c=m;JO+iOmJ3BD(;#@0cl2m<-g(-E9aN}U z0NvUTVlOS-Al|-um1Ff(n`c(^#X||Xv@Z>xEB*G}6knzi_+A|H6^hvLL*+8k{0cXV zHlc?ttBYtOV#ME2jgz?oVHljUv#o*>g&U6eH<2pn6VAisQ<{PY?Ont#Lz%VTS!c+5R=FA}vEVyAPrrCmpnV0R_4erN- zBgL|1+Z(AgpfA@UuVi8}BUf|d+^W*6Eciji9~irrcx_+A3s-2^=8>S*EVvMd;ambfzNWvpC6GVBpuGPaT-J=q*wcg<2&O5};2}Zm8$t!)G`S%Oo z$s3y23Cedlk)F<tTujEt<^5H zpixF`3pV{BhMyUcC?z_2rD&Ka|tU*5TO4f^BHZR>1OWj{Fn-?agz$vjRY zE!@&lcVHCtO!jCFWC;bS0r?rBVa0HM2hu9i&qN7De8W4M^5YmpK%;a)r-+Zt9M#`a z@eNjV2RrQu(m?>h56bl$^9|0z8M-)>&M|Cp&dKh@CUGS?tQa4pIA%Y2d6cL)xT%ob z;w538w=_QxGQLzN?io1DTh}m*53iS)9sNupB2d~&lHZvRlscx4I*nC-WL$0Kc^!sJ zc!yrXn~i{Dou62z08?MH>K<~ymDTmSZR`iZIiVUYJ`e?VGd17^n0W`1jQB|@s(k1X zdI?s2=HJ^xfn_~RM-7d8mH3OKgU9AFwpxvcD?-Q%@IhCz`A~5`4_K~i`v*zeNPYnI zL!z8QK7A>uNSCm66P8{GFspXZY{|XTXxEUdR_1A{CNkGJ0)(4lqTYF5Orc7JNAQt~ z_+#of8ShJ^u67b;U2}$UADrrRi$7p64m6-9Ek<&yvI*4VX+%m*C&S*+T5`rZAaar$ z=v^xU3`sXCyz}fl)<%bTrPK$zy0hGqfpPuu z!*W_`grzu&2h@=rirvBcX(@uerSB9%eT6mt*}p$?T3$?1++U#2d0ay6?EUT1J9Wid z;Gh-XJ`(p{kVam2nVK6^$sd${L(^H%oT)~#wXoWz=k{4rF>7Uqz_PCB_ubC0ECmw5 z2$Gk@E{KQu<%WGR@g%s>d30tuYGwF#AhgJ?(@&rQ72k8pezL z_YU!0#Twv&+5|>6i`McI^0~-6r`gN!Ah5I$-?fkYyr$q)@R1|`T~^@i2(A@0*E}_^ zTPOjF4~sh3guRoKBwXqJx-tE^OXp=!d`Yc?6B1=zKA;K^XUS4f&bZoBb%jv))RU zm^F|S(d!s9kM%@8?OP$$F80Js9{=;B*yr~3k$ zC*ce02vyh-CtZH*`up*mD$5L zBfcek-bp!pGUr-d=phG~Qa*ln0G~<)#NoBucHHonuE;+ zCEsufAX99x{4~e2Lhlz}3T=2AH;77E0YdsKWo#p0XWo%2CP|utJj207Jln){z-n9O zDJXhy@8xSdxYDkbS8yfRrdHHIwuoWVT?%5(GCc5>+>#N4Qmaky#83#nXP6EQ5Je(@ zaBa?^^^vtj^DH6vlG`)20CkW$0}CbQoGe{pt-|5Om4s8Bs?Bcpi&2IiHlo!uZupU} z?ks$7uX4K3S@$RHH&O>%9%@OE)tZOC_jtbH3oOv@jLAQIj4sRACQ0ENeXZalk(!CW zPv@+N5x0}Bsk#m@Hr>po`K8Be+=$g9kT1`8Ui?ZMn9%Mx(VGt-*{f4(VM+?xO_8!W6 znT;5NZ--*HSm$E_$R*T!U$b&)?l9`1ZkM;x^2%LyjfcPwQQ)S7tz(u2@M<6u!}ku+ zUn(D)tO66JKB&9b-3x%hc+UuOca++FqY5yKq1-5=y1ob?=C8<{@EV{kd5U0>cpgMB zq)%ZpOH}T_;e%DPJ{hkO`BS{mmcznu54vx1GyFS_V-M`Hqyi7eq|$B} z_V?6H!t7XRie}5GDcx8IfJwY%@osC^T?w2I9X2&IN{tA76b^bnQ3jjUyLFl-9>aVT zv>}2V4*8ESM+5l$p>d!4UY-VWpD)WT9@*NEox0#Mbx7mOq50jpK-hj%zk$)kaL3`9A#w3WaG|2ttoxcPcSIV@Sh`UiC*uW?(oG%6DeKOOt zz;v0gj=h4n9{55>06)L!_D$OwGfb@imR3NYg3y&TW06ndi>>bse!>76x@wh(#{3?_Jcr~Mayo(>=rWsY)}cYT zC=}y83(<~X*a!n%{wylTUO}|7q{Zn~pDBO?DwltrAhMl*4Zc;+11aFoFPOf~;yH(m zVf;_j5+boVPnePl%il&@u;meFa%DBCgKeZVqfHz`(^cdo=vb}4R8m^A1p=-!c5KU8 z@2Bm`tTbIaMaj5{%iqkCoK&HGwdea9xzcrks5Y=01*c%vDi1_2_@^G$I?-P$4k>f~Aa^Gsg+%6@HY z`l}IP+2=hsA`O_Gr}*we!+y|rR2KDz-neYcCkp-SW%mf1=(C1oz~#Fg&&BrXEAuHX zpoL6r4NGSmRX1Hx=BsspWmC4;#H*|z3thsi=>cHZU?d#FdMqTj(e8-vI=lc_e6)9J zP==?Xr#5b^Y@DPTe(x-+xCws7;gTB|jXQnLbme}&JZg(SXn4y!>rpqv8e{-4SLUYG znchDfn%g!VHB6Z>Z zZ$Nwl@*7a!fc^%|H(C`b!c9#n2mFyZ8A@5SwfC zD=UpL>${aOhrh_V#gmTDo>_nc-ry+F=E?-dH*5 z;qK(3>U7D2|Cp54xOwE&LSI%3Xy45&!W3n@c-Y-eJ%|}F(ze!GYgqICu3uiWYN;8@ zO6m2ASpQ3E#vZ)N_Ca|wx)SPW3P`h7y=+U-{ODMRTAz@*{IW)Va$=qG(A`v}eadkf zKpj{rW=A^LZmZE2MJO#(@2Ta4PSjy^kNpTTBuwI{CAL3|key;x-5T@(^zhl7AKC2f z5V~n0A2{$B-B@|vqh{S2sugz0rkKFB<}x^M^#@Klci@=b@oXJzD5AmR0WO-T3Nn|u z7b$R^7M6r2z`SUe=v@?L2m}i^X?fRKE5?@4O9XQq_&2+-8gM`3kb|>sX1oc8Ab-(r z%11ocS)GLiA8fY5lfj^4O=)Vu`_wsk*Z>N8=h)mHa#GN83_XL$-E8^_o9QfmMPfLo z5te?k=~TTs%UWJJXe9550us}-FK?x7Hq5hZC@=RKPqZ~B?m`~{R)mThcJUaf1C_RA z+MP`!{mmXvjoQwA-SP@9c3t(~J?Ay!xfgo8)aN+|XrJaO?_g?N)vVVVZB{owPcyq> z2n5sr)QUfr?WAl;YOJB@w72ix**hBF`Pou878NZhHUG^YvA8`B1L%9sVJN8+CPJ!u zdmY1r<;A3My@J3HXK|@wJUWNuD}Sl2gnQOgf8nja-{qW1?sACqeC70PQR$}f-!h=R zHAQ)(t+eyX{e1tSXV7h=+|osrVX+Zhn2q+V*E@Q#$b}-OFuN{Cnpo+Db@9G=2wt#) z80&F-PWUrLBC5II9PozL;I*}QMfmiq_JMbMr0i%%Qd@$_c=`Go*J$+j!riVE76G1x zB)d{Rb`CFweR#5%WQZ+2YB*LD1(Qes0=y6iK}gGGhkz7bFcNeNPac0hYJvc%V$C&j zZ$PMmDQIE)&z_$W_Fr(h0Rd-v8UZ4L{nd?AgEHKh5fuwm+N)K7D1b1lockP zg%b%8Tj9ht6tFFKDl`IV5r=UjVa!zmS|SOYZdMQzW<{QZgSwBJUv-tUFl%RVqxKqP z4X4IVA@!9fGhlcmiqIGEn1&5yVer{h(K5rckuW9nkO7Dfe%G!QG;iA$1W@`PP$F-; z=B@VHNh@j0vVkJ6V(OQt$wLfL62)&|<$yqXmA>p97nhzS&@9V)ys`m?f= z2eD0eirGrf`!Yh0gX5R7e9pw=IQocO>+e`e%ChCe?+fh$$P_m z7~~r_69cfB&7dj_>chbnyN_uH&u+LQ+I|Y4w6mx3<-0O4`QDR;gQB{32HY46IFs*6 zQx`yTo7561BJVn!z?!n_6f5ZqDX7AoMKS~i*Y&HWLLY+*z-`KURVb|Yz1F`8F=Be^ z6!F~qUE$t2id>_JgTdB7{GCJ-0uiwSzc=e1PXWk;#4-)sV93?e6=%uaBN7c%M?qBBmDf)bis_VZAGGo@)1lB3}k-h>|J;Tn_X#eg|ch zE}oh-d2p%54+CBt>Cp@ep#xt?0Y)c8NGg!%-nqX)MNWpa0}S9LBr)`i8?8~VW?=0b z$pKjWVT>K@9HuYx1}&g(&cWO~n|%6(jUz&aaHusd6?%c8ST?F#5CYE%Vq0J*MmWqU zyO-?Fq((OdSfbGCC#pw4hlxBOO?8GlZt1UvyHfv4R3wl^-pPjJ02{bpeM?p&&MXz@ zYSP*>ANLngHI*6Pc)ExRlWJ%^AJfPLsSj}Nt}vhm(GjiC=kHQDUvwOpKn!1y#dn5; zva>8santYEr1Cd9EXcpG)+%VK*nX5hl zen}6B3EBV8Fr3$MI=^8!chXD!O7A7r-ANn@NlwTOf>?x_Pgi)YQ2HF5ywc8(SQx<9 zNr;zEtBcLqfo&gKx~Z&e-^`6Lw|u`_hqIBl9m{%;HayFBdmkk>md?ht#g(Js)9TEg zu`%f5)Amy4BpYL0rR+Z#MoqHk&YqRl_AV~F9_c&Vrg}}hyf-+Nyyb6I7Km@qUbZjZ zTqv1EjGXwT3uCn>yonFLH}MzCQc3|M8+$Cr(`a4mZ8I_ycfV=hGAf4h9->}cm3~b& zOdMR5jVz~)RsNk^`G`RgfpN*NbnLM%A45DH7hrdp`gKm2wVbRZV1^b9I(Rm8 zJ5tc9Mk{qzye@A$omd{TkPYnB>(db9#qWQDqdQ4TJS%eRnYWXk4aK?NzS%hA9%RrJ z{!Y8P+xh_iFE0Z~P7)LhEiPRL24EfEhx(m|YrXa_XZ-(!Al6P+`bPHOLCC)tw=ZfS2pH{MEofUp!>v)_%FFYaGI2Ii*#`a4KH$$B0utA2Z7`yHu zfn^Z=AY0>^aR;bUOIvH{hrBjC@5eg|gg*s?_ys;LjKCEbP9 z$Di6Vvkt#I+=k!jesX)7cl$ZL66?oVnYG_`St*RF zVxo0{X@;9)X$ZuCav}T_lDtYW2ugkKcya9n;&KVx83a6=X$%U!E0-YeElK%9|@brrx8=p_@+o83NL@nIHDz-bad~G7WpC_Ee*EirS zMR@~h^ygE<=P+X~W=Hk9UV@K$R*-e#SH>O}#(yb{+!byC&~6WgG>FaSz61sRcTl%UG$Rd-&xiE9Lnb)J|hB;QKwr5z?cBKqeZ zOyK;T*B5FImB^JwZR~Q`h>dpJS;O>~s?*b&wFgnBiMD{LPg%0>)j4C7z|ULJ46x_+ z9-CG>qY?=OoZW1}Noq_QT;bN`{Kzo53yj8avsywM##vlMa z-0}AWd+r*Se-{1Jxgz789CGhIO5R})61ntBCA*{ryg8vGG=9ELdd3l9%sI!ohL@pu zS7%e65#7bw?Y8hflW9rD-(I!2*@HR@DUd2J74;$WqE`Bie+_ut`n4?;G<6!^xMw}z z0j?c@c~!O20>^Z{2I5aqYS4-}P=DW`^1M;vIK|O{KEhJSkHnMy-M1QEYsR1m!~^Xz zQ^Riu=z#ksDdSwVC!Nj{c70WGeLk(cnr?H)Coz1bTqE-CZtMr!1cxRz8SaQgar)hM ztu*AF?&iNI56UOn?-jCSxoz82?Reqs=VH{YEdulEhaPSEkI^bpk2Qqf26Z%nm8c7h zL{;k$KB7ME-_USrl4CT$#=s@(ox3JH|6zUxEO=~IPqyXN)qCe8FYKo-%uh9~eh)-- z**XZUZVj-!@J>&;QN8-e!~}{iQ;)9;$rx?hJyPm<8j{x$&LLdb5!p=K`HE>53Z34| z`)jv8Qv`3pNAcxIIDK<0M3Kt2l1HH*n>&Id0;uTv5}AE9SMw7;;&=>H4$teaV6h;; zYz9F2dfKWdMKPyxT$bMb;#GIMC2ot*y8R-YwDi~G?De?f<4 z!QJxjg@&RF{(=fk^501sw6fO`YeDdwWJNy?G+FY{pD_u!UviXemT4M|^0^O()r&c< ztM*t&o8yQZw_}?Tdpg6aGj?bIk@iYmNvZ5Fh?}~k_gy2)WoMT^7?)S*}D+$Ox_XL0{LS}L$o#fI%+UAclm0SaOKynjpT{wyq1KaMA%j!w1{5dno zFCAH1O%EKMRJcOsPI^i1kx}dS)A~3wh&Brly(=|)hkDW{(OkG9I@53fj~p34=&wuU zurNb}luwh59$`aa4L0(XSn5a77Wgc;{c?~soAAp_`+#k*vq!R;aKWaeS!fTG|{H1J3h-(%_Ez!|IMw<qX7RB^6^#ke_=zzNG@HznHXjNs<6zU> zQP>j}X=^b(b&okuzw}2GVk6$3ZiV6cGA^u&#Q&13<&lf|TZAkCBi_C)Sz7LouaNE=WbnN37jh%#k-dx@sHqZ#X!cb4G(>gQRXxd- z!rwyebZ&^ebM15hH#~jYo%-LWLqhtz20Y;W-$M0}U3lKUJCG0LxN^l53s2;J*-|!^ z7|Gc8St8P?2;8T6)}VmU5FsGjs6p2ZF{(iWhjEk#bW1>~_3M#=(e6$bq4;51!^SZb zIF_3_YTXk`bfRkKrWR=Y`wbOw3_S8j{*il$IlGkB*nrpSU(H6v{&PEtMC;7;h&+|L zV@Nv^18zN}7}&C7c$n#!v?<4PD(%^g_3uaegpPFgbys!O-nX$R#%jE7#pv?oS^WX( zH2Qr3DOjL?ta{2xftV@_(jSzIDosSKy_O%O^@{iyh z2?EM=f(iA^W8g+Q8`W@DQf5XQ=2~yPozGgG8_qqg%**XQPDc1~fTgicHPs*|qu>yZ za+)<0ta6&R`&Q=ZH8rISnS;OcH%0@YM!2n(L~k&)S2st#4u%_w{a#gol}9w-dBsF- zZ?FuKwoGC__leS?knGIblN*v_JoS4tWJi&6Xm`R;uEk<=87>~Q)oQwx>Ii~5ibcHh zM%YEX?nCf$&hAvu@Be-Rn0B}N5Xb^S7de;}vO45fUMlFNYwvm4ih(jg8vfHAO5AX$ zbz%Z++Fs~+*#8`WbIl2%;m+aC?_nEmaR>1~1`4lZ*DxG+P;eDQ5+iG10gZN+GS{F@ zP|;wr4K+-5J~MYa^yt$L3@EG%zKa=9T@v)A8*ugke8dvrq>E zb)X5mkP2oLnhOe~Z#&v)FKBoj?;vYeksuLHO6cVYXJx=muRL>48XEv7gzkYhL(#1pM1TYa*US62^d;hxDh7|IRk=f=jYHM#2UpYTKkSq6J-1 zT=4$5#N`nf4o+1vPZUci)Gn`ZzraE**TY?r5&QgzgnvO1$K|S+Q$p%#Z`3&-I!a#O zv{Jh)|Er{QU4!fBxkQ8)7I1s8?B$*My>~Ba){hzwD>HbRNcm)a`!Ht3DFMDT>iek` zfkn`kvN$k&hc*c%SV{tb>CItAk0pYSvOi%W1yqFM(yICX)Sga1t-yTsK3`eS9Jv>5;chm=U;GAGKJQAc_-!k&5@@tZTnD2)XT$Mq&-e8c?8&IYp2v zB#3G7yY#h@q~*oBU&Ri9_s5Ep{uRZ4vBN)QK$u#8_xFkCC+97|ZA&*2PANC8JVCN~ z?=+=TEDx^ad^x5bSINev$tVc9FE5x36NS#+)=$|*cL&eH(p&HI&^XM{z=;LiMWM(~ zF_J@n`p#|)gSg4vw^@$Vr<8ScsB+7n<$HyC|ABSM-F4+&NHiI()bFYU-zOo1ocH2f zB+-LZMGaGG*Ek1Q5>sF~E|r)eQ!1bDh0J=jr6Kcy<5lhzNxLlZtC-qWf{tA6{?!4) z2ilKnB*e)pcJf|N!YkTOYjN)4D+HzrRdDmWOs@YSDc{So z{YY|mXy^w@IAhjj0%*v#6;fXl-#j?$3;aLt7Cek32*I+c_6&&>7!VLEFWJY4G_(cY zB{<@dmTw>^x#l~Xi3g>Ewxw^sw!8Y1bfwkXqt5xP9L}???d!{XT|0IXQ5pNZM@_v9 zZYCrf2(2nm=?ty|J?t6*e36MvO`T>~4i;nLZY?eTEMN)Zk5oku2Q=F?sS#==OTC>8 z1(s%~Z|KVKuXs$=20%&jU5UEE#B*W=@KC_GEqEmKAr=paAUDzsG}W`u{4YgkpdO}p zIbVB>xsctkCEt?sg&>)igYtX}R;`B2fgZ1Fdqy^CRMxo3)1#tigO~T!3LFWic67-2 zB0mb5R+L*b9Rb3XjU{&5rKI5gp>-gKhLb>|8G zU(u&HNq(i$K=0;UKm#l~4Rsl!cz+aS7>LcyalX0nfqlY>1M2H;dE7jyF({+Nr3CE> z2lIdYKiiz*CG2F^uiX1f|G+Dg1NAOmPFA`nVx1lve&l&kE>(Z@4?8H}Igjm=?|%S1 z-sHjdsCNR2jP(ceZX59I<-cJsvCq8gvJMAR=wolzy>yBFCfbUzg8BwcDl(pO$XF zWU`ChP`!%lho786SM)iz1Mx8#izD>I=k!jF*jz2dkrM*rHJRF?lQ_FnW-G7Yb} zsI}vTm5v~K8Zb1^r-Jyv})2}%_r5W zyg4hG7fn`UO5e}x=R}J)BiKd%eT7N7YFSm(KKa zp2_jgA)G@^*5Usx<}K4_?HA5mIqx{T_NuEne)l)Hiuai%R(U>B2UiLS9|h)!a6Slp z7f|BHzz{K=l}#{!MbVFeVY05D%=BLzf&$X0Th@W&4{+c-eF2-Gsx<0eVYn=i!7}{; zo1ls`Vz(w_=>|f{;py`1g38i}&35Pt{HDi(6rk^sLYQ!K`fQK_#0D006OyOj1Sx>8 zP_;%^uw%M72T%d}`Vxc{nbSi#1Z|l%R7{qwlm&;$WX&o89)yBZmDBHX2)Z-vubQk= zEj!(rQ&5ZPX7%)ZPC-YeZQav10cqxq1p?FM3kAfc^Kb!WJxZn@;1U#>?!zUh&saOX zhD*?i>1{7a%$e!_jOnc0f{u)))7`lRHKfrepb*w_P4D6chVDYN;UR>Ars*fS1?`!R zUYO3sBWTTZ?8$U@9ziu3L=ylqo`lfA^?Z6KkD!VSxby`U0@r~J1<<4y0-Sp}{Unc| zvJAYK0B#}zZCXQs!Z*`7cm-{k+TTxi;}x`LUj9j7djA&zsp*S&fgV5gdHOkCL3Os} H%ph|C75yFd diff --git a/Samples/Graphics/SimplePBR/SimplePBR.cpp b/Samples/Graphics/SimplePBR/SimplePBR.cpp index eb4acfe..172f49e 100644 --- a/Samples/Graphics/SimplePBR/SimplePBR.cpp +++ b/Samples/Graphics/SimplePBR/SimplePBR.cpp @@ -1,7 +1,7 @@ //-------------------------------------------------------------------------------------- // SimplePBR.cpp // -// Demonstrates PBRModel and PBREffect in DirectX 12 on Xbox One, Scarlett and PC devices +// Demonstrates PBRModel and PBREffect in DirectX 12 on Xbox One, Xbox Series X|S, and PC devices // // Advanced Technology Group (ATG) // Copyright (C) Microsoft Corporation. All rights reserved. @@ -9,7 +9,8 @@ #include "pch.h" #include "SimplePBR.h" -#include + +#include "ATGColors.h" #include "FindMedia.h" #include "ControllerFont.h" @@ -21,6 +22,10 @@ using Microsoft::WRL::ComPtr; namespace { + constexpr DXGI_FORMAT c_HDRFormat = DXGI_FORMAT_R11G11B10_FLOAT; + constexpr DXGI_FORMAT c_backBufferFormat = DXGI_FORMAT_R10G10B10A2_UNORM; + constexpr DXGI_FORMAT c_depthFormat = DXGI_FORMAT_D32_FLOAT; + // PBR Assest paths. const wchar_t* s_modelPaths[] = { @@ -50,7 +55,7 @@ namespace D3D12_GPU_DESCRIPTOR_HANDLE irradianceTex, D3D12_GPU_DESCRIPTOR_HANDLE sampler) { - RenderTargetState hdrBufferRts(Sample::GetHDRRenderFormat(), Sample::GetDepthFormat()); + const RenderTargetState hdrBufferRts(c_HDRFormat, c_depthFormat); m_sphere = DirectX::GeometricPrimitive::CreateSphere(1.5); @@ -83,8 +88,8 @@ namespace void XM_CALLCONV Render(ID3D12GraphicsCommandList* commandList, FXMMATRIX camView, CXMMATRIX camProj) { - const size_t numSpheres = 3; - const float step = 15.f; + constexpr size_t numSpheres = 3; + constexpr float step = 15.f; Vector3 modelPos((-step * (numSpheres - 1)) / 2.f, 0, 0); @@ -134,10 +139,10 @@ Sample::Sample() : m_gamepadConnected(false) { m_deviceResources = std::make_unique( - GetBackBufferFormat(), GetDepthFormat(), 2); + c_backBufferFormat, c_depthFormat, 2); m_deviceResources->RegisterDeviceNotify(this); m_gamePad = std::make_unique(); - m_hdrScene = std::make_unique(Sample::GetHDRRenderFormat()); + m_hdrScene = std::make_unique(c_HDRFormat); } Sample::~Sample() @@ -151,16 +156,6 @@ Sample::~Sample() // Initialize the Direct3D resources required to run. void Sample::Initialize(HWND window, int width, int height) { -#ifdef _GAMING_XBOX - // Determine if attached display is HDR or SDR, if HDR, also set the TV in HDR mode. - auto result = XDisplayTryEnableHdrMode(XDisplayHdrModePreference::PreferHdr, nullptr); - - m_bIsTVInHDRMode = (result == XDisplayHdrModeResult::Enabled); -#ifdef _DEBUG - OutputDebugStringA((m_bIsTVInHDRMode) ? "INFO: Display in HDR Mode\n" : "INFO: Display in SDR Mode\n"); -#endif -#endif - m_deviceResources->SetWindow(window, width, height); m_deviceResources->CreateDeviceResources(); @@ -169,7 +164,7 @@ void Sample::Initialize(HWND window, int width, int height) // Initialize Camera { const auto size = m_deviceResources->GetOutputSize(); - const float fovAngleY = 70.0f * XM_PI / 180.0f; + constexpr float fovAngleY = 70.0f * XM_PI / 180.0f; m_camera = std::make_unique(); m_camera->SetWindow(size.right, size.bottom); @@ -196,6 +191,10 @@ void Sample::Tick() { PIXBeginEvent(PIX_COLOR_DEFAULT, L"Frame %llu", m_frame); +#ifdef _GAMING_XBOX + m_deviceResources->WaitForOrigin(); +#endif + m_timer.Tick([&]() { Update(m_timer); @@ -268,8 +267,7 @@ void Sample::Update(DX::StepTimer const& timer) void Sample::RenderHUD(ID3D12GraphicsCommandList* commandList) { - DX::DeviceResources* deviceResources = m_deviceResources.get(); - auto size = deviceResources->GetOutputSize(); + auto const size = m_deviceResources->GetOutputSize(); // Safe area dimensions are in screenspace, with y-axis inverted. auto safe = SimpleMath::Viewport::ComputeTitleSafeArea((UINT)size.right, (UINT)size.bottom); @@ -296,13 +294,13 @@ void Sample::RenderHUD(ID3D12GraphicsCommandList* commandList) float padding = 0.02f; // Convert screenspace coordinates to NDC. - float minX = 2.0f * (safe.left / (FLOAT)size.right) - 1.0f - padding; - float maxX = 2.0f * ((XMVectorGetX(LegendStringBounds) + (FLOAT)safe.left) / (FLOAT)size.right) - 1.0f + padding; - float minYTitle = (2.0f * ((XMVectorGetY(TitleStringBounds) + (FLOAT)safe.top) / (FLOAT)size.bottom) - 1.0f) * -1.0f - padding; - float maxYTitle = (2.0f * (safe.top / (FLOAT)size.bottom) - 1.0f) * -1.0f + padding; + float minX = 2.0f * (safe.left / (float)size.right) - 1.0f - padding; + float maxX = 2.0f * ((XMVectorGetX(LegendStringBounds) + (float)safe.left) / (float)size.right) - 1.0f + padding; + float minYTitle = (2.0f * ((XMVectorGetY(TitleStringBounds) + (float)safe.top) / (float)size.bottom) - 1.0f) * -1.0f - padding; + float maxYTitle = (2.0f * (safe.top / (float)size.bottom) - 1.0f) * -1.0f + padding; - float minYControls = (2.0f * (safe.bottom / (FLOAT)size.bottom) - 1.0f) * -1.0f - padding; - float maxYControls = (2.0f * (((FLOAT)safe.bottom - XMVectorGetY(LegendStringBounds)) / (FLOAT)size.bottom) - 1.0f) * -1.0f + padding; + float minYControls = (2.0f * (safe.bottom / (float)size.bottom) - 1.0f) * -1.0f - padding; + float maxYControls = (2.0f * (((float)safe.bottom - XMVectorGetY(LegendStringBounds)) / (float)size.bottom) - 1.0f) * -1.0f + padding; // Sample Title UI Rectangle VertexPositionColor vTitle0(Vector3(minX, maxYTitle, UIRectangleSceneDepth), UIBackground); @@ -325,9 +323,9 @@ void Sample::RenderHUD(ID3D12GraphicsCommandList* commandList) m_hudBatch->Begin(commandList); #ifdef _GAMING_XBOX - auto fontColor = ATG::ColorsHDR::White; + auto fontColor = ATG::ColorsHDR::White; #else - auto fontColor = ATG::ColorsHDR::LightGrey; + auto fontColor = ATG::ColorsHDR::LightGrey; #endif m_smallFont->DrawString(m_hudBatch.get(), L"SimplePBR Sample", @@ -358,12 +356,11 @@ void Sample::Render() m_deviceResources->Prepare(); Clear(); - DX::DeviceResources* deviceResources = m_deviceResources.get(); - auto commandList = deviceResources->GetCommandList(); + auto commandList = m_deviceResources->GetCommandList(); // Set descriptor heaps ID3D12DescriptorHeap* heaps[] = { m_srvPile->Heap(), m_commonStates->Heap() }; - commandList->SetDescriptorHeaps(_countof(heaps), heaps); + commandList->SetDescriptorHeaps(static_cast(std::size(heaps)), heaps); PIXBeginEvent(commandList, PIX_COLOR_DEFAULT, L"Render"); @@ -372,7 +369,7 @@ void Sample::Render() PIXBeginEvent(commandList, PIX_COLOR_DEFAULT, L"Render HDR"); - auto depthStencilDescriptor = deviceResources->GetDepthStencilView(); + auto depthStencilDescriptor = m_deviceResources->GetDepthStencilView(); auto toneMapRTVDescriptor = m_rtvHeap->GetFirstCpuHandle(); commandList->OMSetRenderTargets(1, &toneMapRTVDescriptor, FALSE, &depthStencilDescriptor); @@ -406,7 +403,7 @@ void Sample::Render() #if defined(_GAMING_XBOX) // Generate both HDR10 and tonemapped SDR signal - D3D12_CPU_DESCRIPTOR_HANDLE rtvDescriptors[2] = { deviceResources->GetRenderTargetView(), deviceResources->GetGameDVRRenderTargetView() }; + D3D12_CPU_DESCRIPTOR_HANDLE rtvDescriptors[2] = { m_deviceResources->GetRenderTargetView(), m_deviceResources->GetGameDVRRenderTargetView() }; commandList->OMSetRenderTargets(2, rtvDescriptors, FALSE, nullptr); m_HDR10->SetHDRSourceTexture(m_srvPile->GetGpuHandle(static_cast(StaticDescriptors::SceneTex))); @@ -414,10 +411,10 @@ void Sample::Render() #else { - auto rtv = static_cast(deviceResources->GetRenderTargetView()); + auto rtv = static_cast(m_deviceResources->GetRenderTargetView()); commandList->OMSetRenderTargets(1, &rtv, FALSE, NULL); - if (deviceResources->GetColorSpace() == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020) + if (m_deviceResources->GetColorSpace() == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020) { // HDR10 signal m_HDR10->SetHDRSourceTexture(m_srvPile->GetGpuHandle(static_cast(StaticDescriptors::SceneTex))); @@ -470,14 +467,6 @@ void Sample::Clear() #pragma region Message Handlers // Message handlers -void Sample::OnActivated() -{ -} - -void Sample::OnDeactivated() -{ -} - void Sample::OnSuspending() { m_deviceResources->Suspend(); @@ -485,11 +474,6 @@ void Sample::OnSuspending() void Sample::OnResuming() { -#ifdef _GAMING_XBOX - // While a title is suspended, the console TV settings could have changed, so we need to call the display APIs when resuming - auto result = XDisplayTryEnableHdrMode(XDisplayHdrModePreference::PreferHdr, nullptr); - m_bIsTVInHDRMode = (result == XDisplayHdrModeResult::Enabled); -#endif m_deviceResources->Resume(); m_timer.ResetElapsedTime(); #ifdef _GAMING_DESKTOP @@ -499,7 +483,7 @@ void Sample::OnResuming() void Sample::OnWindowMoved() { - auto r = m_deviceResources->GetOutputSize(); + auto const r = m_deviceResources->GetOutputSize(); m_deviceResources->WindowSizeChanged(r.right, r.bottom); } @@ -562,7 +546,7 @@ void Sample::CreateDeviceDependentResources() // UI Geometry Setup m_vertexBatch = std::make_unique>(device); - RenderTargetState UIRectRtState(GetHDRRenderFormat(), GetDepthFormat()); + const RenderTargetState UIRectRtState(c_HDRFormat, c_depthFormat); EffectPipelineStateDescription UIRectPd( &VertexType::InputLayout, @@ -573,7 +557,7 @@ void Sample::CreateDeviceDependentResources() m_basicEffect = std::make_unique(device, EffectFlags::VertexColor, UIRectPd); - RenderTargetState rtState(m_deviceResources->GetBackBufferFormat(), + const RenderTargetState rtState(m_deviceResources->GetBackBufferFormat(), m_deviceResources->GetDepthBufferFormat()); // Begin uploading texture resources @@ -608,7 +592,7 @@ void Sample::CreateDeviceDependentResources() // Pipeline state - for rendering direct to back buffer { - RenderTargetState backBufferRts(Sample::GetBackBufferFormat(), Sample::GetDepthFormat()); + RenderTargetState backBufferRts(c_backBufferFormat, c_depthFormat); // Create HDR10 color space effect #if defined(_GAMING_XBOX) @@ -630,7 +614,7 @@ void Sample::CreateDeviceDependentResources() // Pipeline state - for rendering to HDR buffer { - RenderTargetState hdrBufferRts(Sample::GetHDRRenderFormat(), Sample::GetDepthFormat()); + const RenderTargetState hdrBufferRts(c_HDRFormat, c_depthFormat); // HUD DirectX::SpriteBatchPipelineStateDescription hudpd( diff --git a/Samples/Graphics/SimplePBR/SimplePBR.h b/Samples/Graphics/SimplePBR/SimplePBR.h index 5f8c45f..bf6c663 100644 --- a/Samples/Graphics/SimplePBR/SimplePBR.h +++ b/Samples/Graphics/SimplePBR/SimplePBR.h @@ -44,8 +44,8 @@ public: void OnDeviceRestored() override; // Messages - void OnActivated(); - void OnDeactivated(); + void OnActivated() {} + void OnDeactivated() {} void OnSuspending(); void OnResuming(); void OnConstrained() {} @@ -57,10 +57,6 @@ public: // Properties void GetDefaultSize(int& width, int& height) const noexcept; - inline static DXGI_FORMAT GetHDRRenderFormat() { return DXGI_FORMAT_R11G11B10_FLOAT; } - inline static DXGI_FORMAT GetBackBufferFormat() { return DXGI_FORMAT_R10G10B10A2_UNORM; } - inline static DXGI_FORMAT GetDepthFormat() { return DXGI_FORMAT_D32_FLOAT; } - private: void Update(DX::StepTimer const& timer); @@ -102,9 +98,6 @@ private: std::unique_ptr m_keyboard; DirectX::Keyboard::KeyboardStateTracker m_keyboardButtons; std::unique_ptr m_mouse; -#else - // HDR Support - bool m_bIsTVInHDRMode; #endif // Render states diff --git a/Samples/Graphics/SimplePBR/StepTimer.h b/Samples/Graphics/SimplePBR/StepTimer.h index bb15fff..2dbefcb 100644 --- a/Samples/Graphics/SimplePBR/StepTimer.h +++ b/Samples/Graphics/SimplePBR/StepTimer.h @@ -111,7 +111,7 @@ namespace DX timeDelta *= TicksPerSecond; timeDelta /= static_cast(m_qpcFrequency.QuadPart); - uint32_t lastFrameCount = m_frameCount; + const uint32_t lastFrameCount = m_frameCount; if (m_isFixedTimeStep) { diff --git a/Samples/Graphics/SimplePBR/pch.h b/Samples/Graphics/SimplePBR/pch.h index 49a6c88..f4a3fa8 100644 --- a/Samples/Graphics/SimplePBR/pch.h +++ b/Samples/Graphics/SimplePBR/pch.h @@ -41,8 +41,8 @@ #include -#if _GRDK_VER < 0x4A610D2B /* GXDK Edition 200600 */ -#error This sample requires the June 2020 GDK or later +#if _GRDK_VER < 0x55F007B0 /* GDK Edition 211000 */ +#error This sample requires the October 2021 GDK or later #endif #ifdef _GAMING_XBOX_SCARLETT @@ -91,6 +91,7 @@ // then add the NuGet package WinPixEventRuntime to the project. #include #endif + #include "CommonStates.h" #include "DDSTextureLoader.h" #include "DescriptorHeap.h" @@ -111,6 +112,7 @@ #include "SpriteFont.h" #include "VertexTypes.h" #include "WICTextureLoader.h" + #include namespace DX diff --git a/Samples/Graphics/SmokeSimulation/DeviceResources.cpp b/Samples/Graphics/SmokeSimulation/DeviceResources.cpp index d76fd63..fa67816 100644 --- a/Samples/Graphics/SmokeSimulation/DeviceResources.cpp +++ b/Samples/Graphics/SmokeSimulation/DeviceResources.cpp @@ -31,14 +31,13 @@ DeviceResources::DeviceResources( m_rtvDescriptorSize(0), m_screenViewport{}, m_scissorRect{}, - m_backBufferFormat((flags & c_EnableHDR) ? DXGI_FORMAT_R10G10B10A2_UNORM : backBufferFormat), + m_backBufferFormat(backBufferFormat), m_depthBufferFormat(depthBufferFormat), m_backBufferCount(backBufferCount), m_window(nullptr), m_d3dFeatureLevel(D3D_FEATURE_LEVEL_12_0), m_outputSize{0, 0, 1920, 1080}, - m_options(flags), - m_gameDVRFormat((flags & c_EnableHDR) ? backBufferFormat : DXGI_FORMAT_UNKNOWN) + m_options(flags) { if (backBufferCount < 2 || backBufferCount > MAX_BACK_BUFFER_COUNT) { @@ -55,12 +54,16 @@ DeviceResources::~DeviceResources() // Ensure we present a blank screen before cleaning up resources. if (m_commandQueue) { - (void)m_commandQueue->PresentX(0, nullptr, nullptr); + std::ignore = m_commandQueue->PresentX(0, nullptr, nullptr); } } // Configures the Direct3D device, and stores handles to it and the device context. +#ifdef _GAMING_XBOX_SCARLETT +void DeviceResources::CreateDeviceResources(D3D12XBOX_CREATE_DEVICE_FLAGS createDeviceFlags) +#else void DeviceResources::CreateDeviceResources() +#endif { // Create the DX12 API device object. D3D12XBOX_CREATE_DEVICE_PARAMETERS params = {}; @@ -77,6 +80,17 @@ void DeviceResources::CreateDeviceResources() params.GraphicsCommandQueueRingSizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); params.GraphicsScratchMemorySizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); params.ComputeScratchMemorySizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); +#ifdef _GAMING_XBOX_SCARLETT + params.CreateDeviceFlags = createDeviceFlags; + +#if (_GXDK_VER >= 0x585D070E /* GXDK Edition 221000 */) + if (m_options & c_AmplificationShaders) + { + params.AmplificationShaderIndirectArgsBufferSize = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); + params.AmplificationShaderPayloadBufferSize = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); + } +#endif +#endif HRESULT hr = D3D12XboxCreateDevice( nullptr, @@ -107,7 +121,7 @@ void DeviceResources::CreateDeviceResources() // Create descriptor heaps for render target views and depth stencil views. D3D12_DESCRIPTOR_HEAP_DESC rtvDescriptorHeapDesc = {}; - rtvDescriptorHeapDesc.NumDescriptors = (m_options & c_EnableHDR) ? (m_backBufferCount * 2) : m_backBufferCount; + rtvDescriptorHeapDesc.NumDescriptors = m_backBufferCount; rtvDescriptorHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; ThrowIfFailed(m_d3dDevice->CreateDescriptorHeap(&rtvDescriptorHeapDesc, IID_GRAPHICS_PPV_ARGS(m_rtvDescriptorHeap.ReleaseAndGetAddressOf()))); @@ -152,31 +166,46 @@ void DeviceResources::CreateDeviceResources() m_fenceEvent.Attach(CreateEventEx(nullptr, nullptr, 0, EVENT_MODIFY_STATE | SYNCHRONIZE)); if (!m_fenceEvent.IsValid()) { - throw std::exception("CreateEvent"); + throw std::system_error(std::error_code(static_cast(GetLastError()), std::system_category()), "CreateEventEx"); } - if (m_options & c_Enable4K_UHD) + if (m_options & (c_Enable4K_UHD | c_EnableQHD)) { switch (XSystemGetDeviceType()) { case XSystemDeviceType::XboxOne: case XSystemDeviceType::XboxOneS: - case XSystemDeviceType::XboxScarlettLockhart /* Xbox Series S */: - m_options &= ~c_Enable4K_UHD; -#ifdef _DEBUG - OutputDebugStringA("INFO: Swapchain using 1080p (1920 x 1080)\n"); -#endif + m_options &= ~(c_Enable4K_UHD | c_EnableQHD); break; + case XSystemDeviceType::XboxScarlettLockhart /* Xbox Series S */: + m_options &= ~c_Enable4K_UHD; + if (m_options & c_EnableQHD) + { + m_outputSize = { 0, 0, 2560, 1440 }; + } + break; + + case XSystemDeviceType::XboxScarlettAnaconda /* Xbox Series X */: + case XSystemDeviceType::XboxOneXDevkit: + case XSystemDeviceType::XboxScarlettDevkit: default: - m_outputSize = { 0, 0, 3840, 2160 }; -#ifdef _DEBUG - OutputDebugStringA("INFO: Swapchain using 4k (3840 x 2160)\n"); -#endif + m_outputSize = (m_options & c_Enable4K_UHD) ? RECT{ 0, 0, 3840, 2160 } : RECT{ 0, 0, 2560, 1440 }; break; } } +#ifdef _DEBUG + const char* info = nullptr; + switch (m_outputSize.bottom) + { + case 2160: info = "INFO: Swapchain using 4k (3840 x 2160)\n"; break; + case 1440: info = "INFO: Swapchain using 1440p (2560 x 1440)\n"; break; + default: info = "INFO: Swapchain using 1080p (1920 x 1080)\n"; break; + } + OutputDebugStringA(info); +#endif + RegisterFrameEvents(); } @@ -185,7 +214,7 @@ void DeviceResources::CreateWindowSizeDependentResources() { if (!m_window) { - throw std::exception("Call SetWindow with a valid window handle"); + throw std::logic_error("Call SetWindow with a valid Win32 window handle"); } // Wait until all previous GPU work is complete. @@ -198,7 +227,6 @@ void DeviceResources::CreateWindowSizeDependentResources() for (UINT n = 0; n < m_backBufferCount; n++) { m_renderTargets[n].Reset(); - m_renderTargetsGameDVR[n].Reset(); m_fenceValues[n] = m_fenceValues[m_backBufferIndex]; } @@ -208,7 +236,7 @@ void DeviceResources::CreateWindowSizeDependentResources() // Obtain the back buffers for this window which will be the final render targets // and create render target views for each of them. - CD3DX12_HEAP_PROPERTIES swapChainHeapProperties(D3D12_HEAP_TYPE_DEFAULT); + const CD3DX12_HEAP_PROPERTIES swapChainHeapProperties(D3D12_HEAP_TYPE_DEFAULT); D3D12_RESOURCE_DESC swapChainBufferDesc = CD3DX12_RESOURCE_DESC::Tex2D( m_backBufferFormat, @@ -219,6 +247,13 @@ void DeviceResources::CreateWindowSizeDependentResources() ); swapChainBufferDesc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; +#ifdef _GAMING_XBOX_XBOXONE + if (m_options & c_EnableHDR) + { + swapChainBufferDesc.Flags |= D3D12XBOX_RESOURCE_FLAG_ALLOW_AUTOMATIC_GAMEDVR_TONE_MAP; + } +#endif + D3D12_CLEAR_VALUE swapChainOptimizedClearValue = {}; swapChainOptimizedClearValue.Format = m_backBufferFormat; @@ -240,41 +275,12 @@ void DeviceResources::CreateWindowSizeDependentResources() rtvDesc.Format = m_backBufferFormat; rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; - CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( + const CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( m_rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), static_cast(n), m_rtvDescriptorSize); m_d3dDevice->CreateRenderTargetView(m_renderTargets[n].Get(), &rtvDesc, rtvDescriptor); } - if (m_options & c_EnableHDR) - { - swapChainBufferDesc.Format = swapChainOptimizedClearValue.Format = m_gameDVRFormat; - - for (UINT n = 0; n < m_backBufferCount; n++) - { - ThrowIfFailed(m_d3dDevice->CreateCommittedResource( - &swapChainHeapProperties, - D3D12_HEAP_FLAG_ALLOW_DISPLAY, - &swapChainBufferDesc, - D3D12_RESOURCE_STATE_PRESENT, - &swapChainOptimizedClearValue, - IID_GRAPHICS_PPV_ARGS(m_renderTargetsGameDVR[n].GetAddressOf()))); - - wchar_t name[25] = {}; - swprintf_s(name, L"GameDVR Render target %u", n); - m_renderTargetsGameDVR[n]->SetName(name); - - D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {}; - rtvDesc.Format = m_gameDVRFormat; - rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; - - CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptorGameDVR( - m_rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), - static_cast(m_backBufferCount + n), m_rtvDescriptorSize); - m_d3dDevice->CreateRenderTargetView(m_renderTargetsGameDVR[n].Get(), &rtvDesc, rtvDescriptorGameDVR); - } - } - // Reset the index to the current back buffer. m_backBufferIndex = 0; @@ -282,7 +288,7 @@ void DeviceResources::CreateWindowSizeDependentResources() { // Allocate a 2-D surface as the depth/stencil buffer and create a depth/stencil view // on this surface. - CD3DX12_HEAP_PROPERTIES depthHeapProperties(D3D12_HEAP_TYPE_DEFAULT); + const CD3DX12_HEAP_PROPERTIES depthHeapProperties(D3D12_HEAP_TYPE_DEFAULT); D3D12_RESOURCE_DESC depthStencilDesc = CD3DX12_RESOURCE_DESC::Tex2D( m_depthBufferFormat, @@ -295,7 +301,7 @@ void DeviceResources::CreateWindowSizeDependentResources() D3D12_CLEAR_VALUE depthOptimizedClearValue = {}; depthOptimizedClearValue.Format = m_depthBufferFormat; - depthOptimizedClearValue.DepthStencil.Depth = 1.0f; + depthOptimizedClearValue.DepthStencil.Depth = (m_options & c_ReverseDepth) ? 0.0f : 1.0f; depthOptimizedClearValue.DepthStencil.Stencil = 0; ThrowIfFailed(m_d3dDevice->CreateCommittedResource( @@ -331,10 +337,6 @@ void DeviceResources::CreateWindowSizeDependentResources() // Prepare the command list and render target for rendering. void DeviceResources::Prepare(D3D12_RESOURCE_STATES beforeState, D3D12_RESOURCE_STATES afterState) { - // Wait until frame start is signaled - m_framePipelineToken = D3D12XBOX_FRAME_PIPELINE_TOKEN_NULL; - ThrowIfFailed(m_d3dDevice->WaitFrameEventX(D3D12XBOX_FRAME_EVENT_ORIGIN, INFINITE, nullptr, D3D12XBOX_WAIT_FRAME_EVENT_FLAG_NONE, &m_framePipelineToken)); - // Reset command list and allocator. ThrowIfFailed(m_commandAllocators[m_backBufferIndex]->Reset()); ThrowIfFailed(m_commandList->Reset(m_commandAllocators[m_backBufferIndex].Get(), nullptr)); @@ -342,23 +344,10 @@ void DeviceResources::Prepare(D3D12_RESOURCE_STATES beforeState, D3D12_RESOURCE_ if (beforeState != afterState) { // Transition the render target into the correct state to allow for drawing into it. - if (m_options & c_EnableHDR) - { - D3D12_RESOURCE_BARRIER barriers[2] = - { - CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), - beforeState, afterState), - CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargetsGameDVR[m_backBufferIndex].Get(), - beforeState, afterState), - }; - m_commandList->ResourceBarrier(_countof(barriers), barriers); - } - else - { - D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), - beforeState, afterState); - m_commandList->ResourceBarrier(1, &barrier); - } + const D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition( + m_renderTargets[m_backBufferIndex].Get(), + beforeState, afterState); + m_commandList->ResourceBarrier(1, &barrier); } } @@ -368,20 +357,10 @@ void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState, _In_opt_ const if (beforeState != D3D12_RESOURCE_STATE_PRESENT) { // Transition the render target to the state that allows it to be presented to the display. - if (m_options & c_EnableHDR) - { - D3D12_RESOURCE_BARRIER barriers[2] = - { - CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), beforeState, D3D12_RESOURCE_STATE_PRESENT), - CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargetsGameDVR[m_backBufferIndex].Get(), beforeState, D3D12_RESOURCE_STATE_PRESENT), - }; - m_commandList->ResourceBarrier(_countof(barriers), barriers); - } - else - { - D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), beforeState, D3D12_RESOURCE_STATE_PRESENT); - m_commandList->ResourceBarrier(1, &barrier); - } + const D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition( + m_renderTargets[m_backBufferIndex].Get(), + beforeState, D3D12_RESOURCE_STATE_PRESENT); + m_commandList->ResourceBarrier(1, &barrier); } // Send the command list off to the GPU for processing. @@ -389,32 +368,24 @@ void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState, _In_opt_ const m_commandQueue->ExecuteCommandLists(1, CommandListCast(m_commandList.GetAddressOf())); // Present the backbuffer using the PresentX API. - D3D12XBOX_PRESENT_PLANE_PARAMETERS planeParameters[2] = {}; - planeParameters[0].Token = planeParameters[1].Token = m_framePipelineToken; - planeParameters[0].ResourceCount = planeParameters[1].ResourceCount = 1; - planeParameters[0].ppResources = m_renderTargets[m_backBufferIndex].GetAddressOf(); + D3D12XBOX_PRESENT_PLANE_PARAMETERS planeParameters = {}; + planeParameters.Token = m_framePipelineToken; + planeParameters.ResourceCount = 1; + planeParameters.ppResources = m_renderTargets[m_backBufferIndex].GetAddressOf(); if (m_options & c_EnableHDR) { - planeParameters[0].pSrcViewRects = planeParameters[1].pSrcViewRects = &m_outputSize; - planeParameters[0].ColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020; - planeParameters[1].ppResources = m_renderTargetsGameDVR[m_backBufferIndex].GetAddressOf(); - planeParameters[1].ColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; - - ThrowIfFailed( - m_commandQueue->PresentX(2, planeParameters, params) - ); - } - else - { - ThrowIfFailed( - m_commandQueue->PresentX(1, planeParameters, params) - ); + planeParameters.ColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020; } - // Xbox One apps do not need to handle DXGI_ERROR_DEVICE_REMOVED or DXGI_ERROR_DEVICE_RESET. + ThrowIfFailed( + m_commandQueue->PresentX(1, &planeParameters, params) + ); - MoveToNextFrame(); + // Xbox apps do not need to handle DXGI_ERROR_DEVICE_REMOVED or DXGI_ERROR_DEVICE_RESET. + + // Update the back buffer index. + m_backBufferIndex = (m_backBufferIndex + 1) % m_backBufferCount; } // Handle GPU suspend/resume @@ -436,13 +407,13 @@ void DeviceResources::WaitForGpu() noexcept if (m_commandQueue && m_fence && m_fenceEvent.IsValid()) { // Schedule a Signal command in the GPU queue. - UINT64 fenceValue = m_fenceValues[m_backBufferIndex]; + const UINT64 fenceValue = m_fenceValues[m_backBufferIndex]; if (SUCCEEDED(m_commandQueue->Signal(m_fence.Get(), fenceValue))) { // Wait until the Signal has been processed. if (SUCCEEDED(m_fence->SetEventOnCompletion(fenceValue, m_fenceEvent.Get()))) { - WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); + std::ignore = WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); // Increment the fence value for the current frame. m_fenceValues[m_backBufferIndex]++; @@ -451,25 +422,17 @@ void DeviceResources::WaitForGpu() noexcept } } -// Prepare to render the next frame. -void DeviceResources::MoveToNextFrame() +// For PresentX rendering, we should wait for the origin event just before processing input. +void DeviceResources::WaitForOrigin() { - // Schedule a Signal command in the queue. - const UINT64 currentFenceValue = m_fenceValues[m_backBufferIndex]; - ThrowIfFailed(m_commandQueue->Signal(m_fence.Get(), currentFenceValue)); - - // Update the back buffer index. - m_backBufferIndex = (m_backBufferIndex + 1) % m_backBufferCount; - - // If the next frame is not ready to be rendered yet, wait until it is ready. - if (m_fence->GetCompletedValue() < m_fenceValues[m_backBufferIndex]) - { - ThrowIfFailed(m_fence->SetEventOnCompletion(m_fenceValues[m_backBufferIndex], m_fenceEvent.Get())); - WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); - } - - // Set the fence value for the next frame. - m_fenceValues[m_backBufferIndex] = currentFenceValue + 1; + // Wait until frame start is signaled + m_framePipelineToken = D3D12XBOX_FRAME_PIPELINE_TOKEN_NULL; + ThrowIfFailed(m_d3dDevice->WaitFrameEventX( + D3D12XBOX_FRAME_EVENT_ORIGIN, + INFINITE, + nullptr, + D3D12XBOX_WAIT_FRAME_EVENT_FLAG_NONE, + &m_framePipelineToken)); } // Set frame interval and register for frame events diff --git a/Samples/Graphics/SmokeSimulation/DeviceResources.h b/Samples/Graphics/SmokeSimulation/DeviceResources.h index 1d26b17..4924894 100644 --- a/Samples/Graphics/SmokeSimulation/DeviceResources.h +++ b/Samples/Graphics/SmokeSimulation/DeviceResources.h @@ -10,8 +10,11 @@ namespace DX class DeviceResources { public: - static constexpr unsigned int c_Enable4K_UHD = 0x1; - static constexpr unsigned int c_EnableHDR = 0x2; + static constexpr unsigned int c_Enable4K_UHD = 0x1; + static constexpr unsigned int c_EnableQHD = 0x2; + static constexpr unsigned int c_EnableHDR = 0x4; + static constexpr unsigned int c_ReverseDepth = 0x8; + static constexpr unsigned int c_AmplificationShaders = 0x10; DeviceResources(DXGI_FORMAT backBufferFormat = DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT depthBufferFormat = DXGI_FORMAT_D32_FLOAT, @@ -25,7 +28,11 @@ namespace DX DeviceResources(DeviceResources const&) = delete; DeviceResources& operator= (DeviceResources const&) = delete; +#ifdef _GAMING_XBOX_SCARLETT + void CreateDeviceResources(D3D12XBOX_CREATE_DEVICE_FLAGS createDeviceFlags = D3D12XBOX_CREATE_DEVICE_FLAG_NONE); +#else void CreateDeviceResources(); +#endif void CreateWindowSizeDependentResources(); void SetWindow(HWND window) noexcept { m_window = window; } void Prepare(D3D12_RESOURCE_STATES beforeState = D3D12_RESOURCE_STATE_PRESENT, @@ -35,6 +42,7 @@ namespace DX void Suspend(); void Resume(); void WaitForGpu() noexcept; + void WaitForOrigin(); // Device Accessors. RECT GetOutputSize() const noexcept { return m_outputSize; } @@ -67,19 +75,7 @@ namespace DX return CD3DX12_CPU_DESCRIPTOR_HANDLE(m_dsvDescriptorHeap->GetCPUDescriptorHandleForHeapStart()); } - // Direct3D HDR Game DVR support for Xbox One. - ID3D12Resource* GetGameDVRRenderTarget() const noexcept { return m_renderTargetsGameDVR[m_backBufferIndex].Get(); } - DXGI_FORMAT GetGameDVRFormat() const noexcept { return m_gameDVRFormat; } - - CD3DX12_CPU_DESCRIPTOR_HANDLE GetGameDVRRenderTargetView() const noexcept - { - return CD3DX12_CPU_DESCRIPTOR_HANDLE( - m_rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), - static_cast(m_backBufferCount + m_backBufferIndex), m_rtvDescriptorSize); - } - private: - void MoveToNextFrame(); void RegisterFrameEvents(); static constexpr size_t MAX_BACK_BUFFER_COUNT = 3; @@ -126,9 +122,5 @@ namespace DX // DeviceResources options (see flags above) unsigned int m_options; - - // Direct3D HDR Game DVR support for Xbox One. - Microsoft::WRL::ComPtr m_renderTargetsGameDVR[MAX_BACK_BUFFER_COUNT]; - DXGI_FORMAT m_gameDVRFormat; }; } diff --git a/Samples/Graphics/SmokeSimulation/Main.cpp b/Samples/Graphics/SmokeSimulation/Main.cpp index 1da6ae1..40f8e64 100644 --- a/Samples/Graphics/SmokeSimulation/Main.cpp +++ b/Samples/Graphics/SmokeSimulation/Main.cpp @@ -1,7 +1,7 @@ //-------------------------------------------------------------------------------------- // Main.cpp // -// Entry point for Microsoft GDK with Xbox extensions. +// Entry point for Microsoft GDK with Xbox extensions // // Advanced Technology Group (ATG) // Copyright (C) Microsoft Corporation. All rights reserved. @@ -18,16 +18,25 @@ using namespace DirectX; +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wcovered-switch-default" +#pragma clang diagnostic ignored "-Wswitch-enum" +#endif + +#pragma warning(disable : 4061) + namespace { std::unique_ptr g_sample; HANDLE g_plmSuspendComplete = nullptr; HANDLE g_plmSignalResume = nullptr; -}; +} bool g_HDRMode = false; LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); +void SetDisplayMode() noexcept; +void ExitSample() noexcept; // Entry point int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) @@ -58,6 +67,8 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp // Register class and create window PAPPSTATE_REGISTRATION hPLM = {}; + PAPPCONSTRAIN_REGISTRATION hPLM2 = {}; + { // Register class WNDCLASSEXA wcex = {}; @@ -66,7 +77,7 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp wcex.lpfnWndProc = WndProc; wcex.hInstance = hInstance; wcex.lpszClassName = u8"SmokeSimulationWindowClass"; - wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); + wcex.hbrBackground = reinterpret_cast(COLOR_WINDOW + 1); if (!RegisterClassExA(&wcex)) return 1; @@ -79,17 +90,7 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp ShowWindow(hwnd, nCmdShow); - if (g_sample->RequestHDRMode()) - { - // Request HDR mode. - auto result = XDisplayTryEnableHdrMode(XDisplayHdrModePreference::PreferHdr, nullptr); - - g_HDRMode = (result == XDisplayHdrModeResult::Enabled); - - #ifdef _DEBUG - OutputDebugStringA((g_HDRMode) ? "INFO: Display in HDR Mode\n" : "INFO: Display in SDR Mode\n"); - #endif - } + SetDisplayMode(); SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast(g_sample.get())); @@ -116,7 +117,7 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp PostMessage(reinterpret_cast(context), WM_USER, 0, 0); // To defer suspend, you must wait to exit this callback - (void)WaitForSingleObject(g_plmSuspendComplete, INFINITE); + std::ignore = WaitForSingleObject(g_plmSuspendComplete, INFINITE); } else { @@ -124,6 +125,13 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp } }, hwnd, &hPLM)) return 1; + + if (RegisterAppConstrainedChangeNotification([](BOOLEAN constrained, PVOID context) + { + // To ensure we use the main UI thread to process the notification, we self-post a message + SendMessage(reinterpret_cast(context), WM_USER + 1, (constrained) ? 1u : 0u, 0); + }, hwnd, &hPLM2)) + return 1; } // Main message loop @@ -144,13 +152,14 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp g_sample.reset(); UnregisterAppStateChangeNotification(hPLM); - + UnregisterAppConstrainedChangeNotification(hPLM2); + CloseHandle(g_plmSuspendComplete); CloseHandle(g_plmSignalResume); XGameRuntimeUninitialize(); - return (int) msg.wParam; + return static_cast(msg.wParam); } // Windows procedure @@ -168,18 +177,52 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) // Complete deferral SetEvent(g_plmSuspendComplete); - (void)WaitForSingleObject(g_plmSignalResume, INFINITE); + std::ignore = WaitForSingleObject(g_plmSignalResume, INFINITE); + + SetDisplayMode(); sample->OnResuming(); } break; + + case WM_USER + 1: + if (sample) + { + if (wParam) + { + sample->OnConstrained(); + } + else + { + SetDisplayMode(); + + sample->OnUnConstrained(); + } + } + break; } return DefWindowProc(hWnd, message, wParam, lParam); } +// HDR helper +void SetDisplayMode() noexcept +{ + if (g_sample && g_sample->RequestHDRMode()) + { + // Request HDR mode. + auto result = XDisplayTryEnableHdrMode(XDisplayHdrModePreference::PreferHdr, nullptr); + + g_HDRMode = (result == XDisplayHdrModeResult::Enabled); + +#ifdef _DEBUG + OutputDebugStringA((g_HDRMode) ? "INFO: Display in HDR Mode\n" : "INFO: Display in SDR Mode\n"); +#endif + } +} + // Exit helper -void ExitSample() +void ExitSample() noexcept { PostQuitMessage(0); } diff --git a/Samples/Graphics/SmokeSimulation/SmokeSimulation.cpp b/Samples/Graphics/SmokeSimulation/SmokeSimulation.cpp index 264f5b5..525adf8 100644 --- a/Samples/Graphics/SmokeSimulation/SmokeSimulation.cpp +++ b/Samples/Graphics/SmokeSimulation/SmokeSimulation.cpp @@ -8,7 +8,7 @@ #include "pch.h" #include "SmokeSimulation.h" -extern void ExitSample(); +extern void ExitSample() noexcept; using namespace DirectX; @@ -57,6 +57,8 @@ void Sample::Tick() { PIXBeginEvent(PIX_COLOR_DEFAULT, L"Frame %llu", m_frame); + m_deviceResources->WaitForOrigin(); + m_timer.Tick([&]() { Update(m_timer); @@ -184,16 +186,16 @@ void Sample::Clear() PIXBeginEvent(commandList, PIX_COLOR_DEFAULT, L"Clear"); // Clear the views. - auto rtvDescriptor = m_deviceResources->GetRenderTargetView(); - auto dsvDescriptor = m_deviceResources->GetDepthStencilView(); + auto const rtvDescriptor = m_deviceResources->GetRenderTargetView(); + auto const dsvDescriptor = m_deviceResources->GetDepthStencilView(); commandList->OMSetRenderTargets(1, &rtvDescriptor, FALSE, &dsvDescriptor); commandList->ClearRenderTargetView(rtvDescriptor, DirectX::Colors::Black, 0, nullptr); commandList->ClearDepthStencilView(dsvDescriptor, D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr); // Set the viewport and scissor rect. - auto viewport = m_deviceResources->GetScreenViewport(); - auto scissorRect = m_deviceResources->GetScissorRect(); + auto const viewport = m_deviceResources->GetScreenViewport(); + auto const scissorRect = m_deviceResources->GetScissorRect(); commandList->RSSetViewports(1, &viewport); commandList->RSSetScissorRects(1, &scissorRect); @@ -226,8 +228,6 @@ void Sample::CreateDeviceDependentResources() m_commonStates = std::make_unique(device); m_srvPile = std::make_unique( device, - D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, - D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, 128, SRV_Count); @@ -237,7 +237,7 @@ void Sample::CreateDeviceDependentResources() resourceUpload.Begin(); // HUD - auto backBufferRts = RenderTargetState(m_deviceResources->GetBackBufferFormat(), m_deviceResources->GetDepthBufferFormat()); + const RenderTargetState backBufferRts(m_deviceResources->GetBackBufferFormat(), m_deviceResources->GetDepthBufferFormat()); auto spritePSD = SpriteBatchPipelineStateDescription(backBufferRts, &CommonStates::AlphaBlend); m_hudBatch = std::make_unique(device, resourceUpload, spritePSD); @@ -249,7 +249,7 @@ void Sample::CreateDeviceDependentResources() void Sample::CreateWindowSizeDependentResources() { auto device = m_deviceResources->GetD3DDevice(); - auto size = m_deviceResources->GetOutputSize(); + auto const size = m_deviceResources->GetOutputSize(); // Calculate display dimensions. m_displayWidth = size.right - size.left; diff --git a/Samples/Graphics/SmokeSimulation/SmokeSimulation.h b/Samples/Graphics/SmokeSimulation/SmokeSimulation.h index 33075ba..76bdaff 100644 --- a/Samples/Graphics/SmokeSimulation/SmokeSimulation.h +++ b/Samples/Graphics/SmokeSimulation/SmokeSimulation.h @@ -20,18 +20,26 @@ public: Sample() noexcept(false); ~Sample(); + Sample(Sample&&) = default; + Sample& operator= (Sample&&) = default; + + Sample(Sample const&) = delete; + Sample& operator= (Sample const&) = delete; + // Initialization and management void Initialize(HWND window); - // Basic Sample loop + // Basic render loop void Tick(); // Messages void OnSuspending(); void OnResuming(); + void OnConstrained() {} + void OnUnConstrained() {} // Properties - bool RequestHDRMode() const { return m_deviceResources ? (m_deviceResources->GetDeviceOptions() & DX::DeviceResources::c_EnableHDR) != 0 : false; } + bool RequestHDRMode() const noexcept { return m_deviceResources ? (m_deviceResources->GetDeviceOptions() & DX::DeviceResources::c_EnableHDR) != 0 : false; } private: void Update(const DX::StepTimer& timer); diff --git a/Samples/Graphics/SmokeSimulation/StepTimer.h b/Samples/Graphics/SmokeSimulation/StepTimer.h index bb15fff..2dbefcb 100644 --- a/Samples/Graphics/SmokeSimulation/StepTimer.h +++ b/Samples/Graphics/SmokeSimulation/StepTimer.h @@ -111,7 +111,7 @@ namespace DX timeDelta *= TicksPerSecond; timeDelta /= static_cast(m_qpcFrequency.QuadPart); - uint32_t lastFrameCount = m_frameCount; + const uint32_t lastFrameCount = m_frameCount; if (m_isFixedTimeStep) { diff --git a/Samples/Graphics/SmokeSimulation/pch.h b/Samples/Graphics/SmokeSimulation/pch.h index a6168ba..28dd6f8 100644 --- a/Samples/Graphics/SmokeSimulation/pch.h +++ b/Samples/Graphics/SmokeSimulation/pch.h @@ -37,8 +37,8 @@ #include -#if _GXDK_VER < 0x4A610D2B /* GXDK Edition 200600 */ -#error This sample requires the June 2020 GDK or later +#if _GXDK_VER < 0x55F007B0 /* GDK Edition 211000 */ +#error This sample requires the October 2021 GDK or later #endif #ifdef _GAMING_XBOX_SCARLETT @@ -56,14 +56,21 @@ #include #include +#include #include +#include #include +#include +#include +#include #include +#include #include #include +#include +#include +#include -#include -#include #include #include @@ -94,7 +101,7 @@ namespace DX class com_exception : public std::exception { public: - com_exception(HRESULT hr) : result(hr) {} + com_exception(HRESULT hr) noexcept : result(hr) {} const char* what() const override { diff --git a/Samples/Graphics/SmokeSimulation/readme.docx b/Samples/Graphics/SmokeSimulation/readme.docx index 2cee66b2f1dfbaa13a193622107cbfbff2a44088..3da31d2cb533d1dc433d2f70fdd99f93bad624e5 100644 GIT binary patch delta 28613 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 delta 28354 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 64353034 + 0000000040272D21 -#if _GRDK_VER < 0x4A610D2B /* GXDK Edition 200600 */ -#error This sample requires the June 2020 GDK or later +#if _GRDK_VER < 0x55F00C58 /* GDK Edition 220300 */ +#error This sample requires the March 2022 GDK or later #endif #ifdef _GAMING_XBOX_SCARLETT diff --git a/Samples/Live/DownloadableContent/DLCPackage/Package_Scarlett/MicrosoftGame.config b/Samples/Live/DownloadableContent/DLCPackage/Package_Scarlett/MicrosoftGame.config deleted file mode 100644 index 2a37524..0000000 --- a/Samples/Live/DownloadableContent/DLCPackage/Package_Scarlett/MicrosoftGame.config +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - 6F9201BF - 9P96RFVJQ562 - - 9NQWJKKNHF1L - - Scarlett - - - - diff --git a/Samples/Live/DownloadableContent/DLCPackage/Package_XboxOne/MicrosoftGame.config b/Samples/Live/DownloadableContent/DLCPackage/Package_XboxOne/MicrosoftGame.config deleted file mode 100644 index a752536..0000000 --- a/Samples/Live/DownloadableContent/DLCPackage/Package_XboxOne/MicrosoftGame.config +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - 6F9201BF - 9P96RFVJQ562 - - 9NQWJKKNHF1L - - XboxOne - - - - diff --git a/Samples/Live/DownloadableContent/DLCPackagePC/Package/MicrosoftGame.config b/Samples/Live/DownloadableContent/DLCPackagePC/Package/MicrosoftGame.config deleted file mode 100644 index 2e464e9..0000000 --- a/Samples/Live/DownloadableContent/DLCPackagePC/Package/MicrosoftGame.config +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - 6F9201BF - 9P96RFVJQ562 - - 9NQWJKKNHF1L - - PC - - - - x64 - - - - - diff --git a/Samples/Live/DownloadableContent/DownloadableContent.cpp b/Samples/Live/DownloadableContent/DownloadableContent.cpp index 156035b..86bbc2d 100644 --- a/Samples/Live/DownloadableContent/DownloadableContent.cpp +++ b/Samples/Live/DownloadableContent/DownloadableContent.cpp @@ -75,6 +75,13 @@ Sample::Sample() noexcept(false) : Sample::~Sample() { + if (m_storeContext) + { + XStoreCloseContextHandle(m_storeContext); + } + + XPackageUnregisterPackageInstalled(m_packageInstallToken, true); + if (m_deviceResources) { m_deviceResources->WaitForGpu(); @@ -130,7 +137,7 @@ void Sample::Initialize(HWND window, int width, int height) m_liveResources->SetErrorHandler([this](HRESULT error) { - if (error == E_GAMEUSER_RESOLVE_USER_ISSUE_REQUIRED) + if (error == E_GAMEUSER_RESOLVE_USER_ISSUE_REQUIRED || error == E_GAMEUSER_NO_DEFAULT_USER) { m_liveResources->SignInWithUI(); } @@ -138,10 +145,6 @@ void Sample::Initialize(HWND window, int width, int height) { ErrorMessage("LiveResource error : E_GAMEUSER_NO_PACKAGE_IDENTITY\n"); } - else if (error == E_GAMEUSER_NO_DEFAULT_USER) - { - ErrorMessage("LiveResource error : E_GAMEUSER_NO_DEFAULT_USER\n"); - } else // Handle other error cases. { ErrorMessage("LiveResource error : 0x%08X\n", error); @@ -182,13 +185,15 @@ void Sample::RefreshStoreProducts() XStoreProductQueryHandle queryHandle = nullptr; + // This call occurs following output, this will be fixed by future update (bug:40153923) + // XERROR: HR:80070006 threadID:1060 Handle: already exists (00000000d5d1e1a0:StoreProductQuery) HRESULT hr = XStoreQueryAssociatedProductsResult( async, &queryHandle); if (static_cast(hr) == 0x803F6107) /* IAP_E_UNEXPECTED */ { - pThis->ErrorMessage("Config is invalid (Sandbox / ContentIdOverride / EKBIDOverride) : 0x%08X\n", hr); + pThis->ErrorMessage("User has no entitlement or configs are invalid (Sandbox / ContentIdOverride / EKBIDOverride) : 0x%08X\n", hr); delete async; pThis->m_isStoreEnumerating = false; return; @@ -208,8 +213,7 @@ void Sample::RefreshStoreProducts() { Sample* pThis = reinterpret_cast(context); - // BUG 30095480 : Currently hasDigitalDownload doesn't work. - //if (product->hasDigitalDownload) + if (product->hasDigitalDownload) { StoreProductDetails storeProduct(product); @@ -304,6 +308,7 @@ void Sample::RefreshStoreProducts() } + XStoreCloseProductsQueryHandle(queryHandle); delete async; pThis->m_isStoreEnumerating = false; }; @@ -376,7 +381,7 @@ void Sample::PurchaseStoreProduct(StoreProductDetails &package) if (FAILED(hr)) { - ErrorMessage("Failed to purchase: 0x%x\n", hr); + ErrorMessage("Failed to purchase : 0x%x\n", hr); delete reinterpret_cast(async->context); delete async; return; @@ -625,13 +630,16 @@ void Sample::RefreshInstalledPackages() { UnmountSelectedPackage(*package); button->GetTypedSubElementById(ID("DLC_Status"))->SetStyleId(ID("Unchecked")); + + SetBackgroundImage("Assets\\Unmounted.png"); } } }); - dlcButton->ButtonState().AddListenerWhen(UIButton::State::Focused, [this](UIButton* ) + dlcButton->ButtonState().AddListenerWhen(UIButton::State::Focused, [this](UIButton* button) { - m_legendText->SetDisplayText(UIDisplayString("[A] to Mount or Unmount Package, [Y] to Refresh, [LB] + [RB] to Toggle filter, [VIEW] to Close Sample")); + m_legendText->SetDisplayText(UIDisplayString("[A] to Mount or Unmount Package, [X] to Uninstall, [Y] to Refresh, [LB] + [RB] to Toggle filter, [VIEW] to Close Sample")); + m_currentFocusStoreId = button->GetTypedSubElementById(ID("DLC_StoreID"))->GetDisplayText(); }); } } @@ -649,91 +657,134 @@ PackageDetails* Sample::GetPackageDetail(const std::string &storeId) return nullptr; } -void Sample::MountSelectedPackage(PackageDetails &package) +HRESULT Sample::MountPackage(const char* packageIdentifier, XPackageMountHandle* mountHandle) { - struct Context - { - Sample *pThis; - PackageDetails *package; - }; - auto async = new XAsyncBlock{}; - async->queue = m_asyncQueue; - async->context = new Context{ this, &package }; - async->callback = [](XAsyncBlock *async) + + HRESULT hr = XPackageMountWithUiAsync(packageIdentifier, async); + + if (SUCCEEDED(hr)) { - auto &[pThis, package] = *reinterpret_cast(async->context); + // Wait for XPackageMountWithUiAsync. + hr = XAsyncGetStatus(async, true); - XStoreLicenseHandle license = {}; - auto result = XStoreAcquireLicenseForPackageResult(async, &license); - - if (SUCCEEDED(result)) + if (SUCCEEDED(hr)) { - bool isLicense = XStoreIsLicenseValid(license); + hr = XPackageMountWithUiResult(async, mountHandle); - debugPrint("%s %s\n", package->displayName.data(), isLicense ? "Licensed" : "No license"); - - if (isLicense) + if (FAILED(hr)) { - PackageEventContext *context = new PackageEventContext{ pThis, package->storeId }; - - auto token = pThis->RegisterPackageEvents(license, context); - - XPackageMountHandle mountHandle = {}; - result = XPackageMount(package->packageIdentifier.data(), &mountHandle); - - if (SUCCEEDED(result)) - { - package->isMounted = true; - package->button->GetTypedSubElementById(ID("DLC_Status"))->SetStyleId(ID("Checked")); - - pThis->AddNewMountedPackage(package->storeId, license, token, mountHandle, context); - } - else if (result == E_ACCESSDENIED) - { - XStoreCloseLicenseHandle(license); - pThis->ErrorMessage("Mounting failed. Cannot mount an app (%s): E_ACCESSDENIED\n", package->displayName.data()); - } - else if (result == E_GAMEPACKAGE_DLC_NOT_SUPPORTED) - { - XStoreCloseLicenseHandle(license); - pThis->ErrorMessage("Mounting failed. This package may target another device %s: 0x%08X\n", package->displayName.data(), result); - } - else - { - XStoreCloseLicenseHandle(license); - pThis->ErrorMessage("Error mounting package %s: 0x%08X\n", package->displayName.data(), result); - } + ErrorMessage("Mounting failed : %s : 0x%08X\n", packageIdentifier, hr); } - else - { - pThis->ErrorMessage("You don't have a license for %s.\n", package->displayName.data()); - } - pThis->RefreshInstalledPackages(); - pThis->m_needSetFocus = EnumFocusArea::InstalledPackages; - } - else if (static_cast(result) == 0x87E10BC6) /* LM_E_CONTENT_NOT_IN_CATALOG */ - { - pThis->ErrorMessage("XStoreAcquireLicenseForPackageResult failed: %s : LM_E_CONTENT_NOT_IN_CATALOG.\n", package->displayName.data()); } else { - pThis->ErrorMessage("XStoreAcquireLicenseForPackageResult failed: %s : 0x%08X\n", package->displayName.data(), result); + if (hr == E_ACCESSDENIED) + { + ErrorMessage("Mounting failed. Cannot access package : %s : E_ACCESSDENIED\n", packageIdentifier); + } + else if (hr == E_GAMEPACKAGE_DLC_NOT_SUPPORTED) + { + ErrorMessage("Mounting failed. This package may target another device : %s : E_GAMEPACKAGE_DLC_NOT_SUPPORTED\n", packageIdentifier); + } + else if (hr == E_ABORT) + { + ErrorMessage("Mounting failed. User canceled : %s : E_ABORT.\n", packageIdentifier); + } + else if (static_cast(hr) == 0x87DE2729 /* LM_E_OWNER_NOT_SIGNED_IN */) + { + ErrorMessage("Mounting failed. User has no entitlement : %s", packageIdentifier); + } + else + { + ErrorMessage("Mounting failed : %s : 0x%08X\n", packageIdentifier, hr); + } } - - package->isBusy = false; - - delete reinterpret_cast(async->context); - delete async; - }; - - if (FAILED(XStoreAcquireLicenseForPackageAsync(m_storeContext, package.packageIdentifier.data(), async))) - { - package.isBusy = false; - - delete reinterpret_cast(async->context); - delete async; } + + delete async; + + return hr; +} + +HRESULT Sample::AcquireLicense(const char* packageIdentifier, XStoreLicenseHandle* licenseHandle) +{ + auto async = new XAsyncBlock{}; + + HRESULT hr = XStoreAcquireLicenseForPackageAsync(m_storeContext, packageIdentifier, async); + + if (SUCCEEDED(hr)) + { + // Wait for XStoreAcquireLicenseForPackageAsync. + hr = XAsyncGetStatus(async, true); + + if (SUCCEEDED(hr)) + { + hr = XStoreAcquireLicenseForPackageResult(async, licenseHandle); + } + else + { + if (static_cast(hr) == 0x87E10BC6) /* LM_E_CONTENT_NOT_IN_CATALOG */ + { + ErrorMessage("AcquireLicense failed: %s : LM_E_CONTENT_NOT_IN_CATALOG.\n", packageIdentifier); + } + if (static_cast(hr) == 0x803F9006) /* LM_E_ENTITLED_USER_SIGNED_OUT */ + { + ErrorMessage("AcquireLicense failed: %s : LM_E_ENTITLED_USER_SIGNED_OUT.\n", packageIdentifier); + } + else + { + ErrorMessage("AcquireLicense failed: %s : 0x%08X\n", packageIdentifier, hr); + } + } + } + + delete async; + + return hr; +} + +void Sample::MountSelectedPackage(PackageDetails &package) +{ + XStoreLicenseHandle license = {}; + + HRESULT hr = AcquireLicense(package.packageIdentifier.data(), &license); + + if (SUCCEEDED(hr)) + { + bool isLicense = XStoreIsLicenseValid(license); + + debugPrint("%s %s\n", package.displayName.data(), isLicense ? "Licensed" : "No license"); + + if (isLicense) + { + PackageEventContext* pec = new PackageEventContext{ this, package.storeId }; + + auto token = RegisterPackageEvents(license, pec); + + XPackageMountHandle mountHandle = {}; + + hr = MountPackage(package.packageIdentifier.data(), &mountHandle); + + if (SUCCEEDED(hr)) + { + package.isMounted = true; + package.button->GetTypedSubElementById(ID("DLC_Status"))->SetStyleId(ID("Checked")); + + AddNewMountedPackage(package.storeId, license, token, mountHandle, pec); + } + else + { + delete pec; + XStoreCloseLicenseHandle(license); + } + + RefreshInstalledPackages(); + m_needSetFocus = EnumFocusArea::InstalledPackages; + } + } + + package.isBusy = false; } void Sample::UnmountSelectedPackage(PackageDetails &package) @@ -833,44 +884,9 @@ void Sample::AddNewMountedPackage(std::string &storeId, XStoreLicenseHandle lice debugPrint("DLC path : %s\n", path); - HANDLE hFile = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); - - if (hFile == INVALID_HANDLE_VALUE) - { - ErrorMessage("Failed to open %s\n", path); - delete[] path; - return; - } - - // Get file size - FILE_STANDARD_INFO fileInfo; - if (GetFileInformationByHandleEx(hFile, FileStandardInfo, &fileInfo, sizeof(fileInfo)) == FALSE) - { - ErrorMessage("Failed to get the file size %s\n", path); - delete[] path; - CloseHandle(hFile); - return; - } - - LARGE_INTEGER fileSize = fileInfo.EndOfFile; - - // Allocate the buffer - std::unique_ptr buffer; - buffer.reset(new uint8_t[fileSize.LowPart]); - - DWORD bytesRead; - if (ReadFile(hFile, buffer.get(), fileSize.LowPart, &bytesRead, nullptr) == FALSE) - { - ErrorMessage("ReadFile Failed %s\n", path); - delete[] path; - CloseHandle(hFile); - return; - } + SetBackgroundImage(path); delete[] path; - CloseHandle(hFile); - - m_backgroundImage->UseTextureData(buffer.get(), fileSize.LowPart); } PackageMountInfo* Sample::GetPackageMountInfo(const std::string &storeId) @@ -884,6 +900,22 @@ PackageMountInfo* Sample::GetPackageMountInfo(const std::string &storeId) return nullptr; } + +void Sample::UninstallPackage(PackageDetails& package) +{ + if (XPackageUninstallPackage(package.packageIdentifier.data())) + { + debugPrint("Package %s uninstalled.\n", package.packageIdentifier.data()); + } + else + { + ErrorMessage("XPackageUninstallPackage failed : %s\n", package.packageIdentifier.data()); + } + + RefreshInstalledPackages(); + RefreshStoreProducts(); +} + #pragma endregion #pragma region UI Methods @@ -915,6 +947,44 @@ void Sample::ResetStoreButton(std::shared_ptr button) SetDisplayText(UIDisplayString("")); } +void Sample::SetBackgroundImage(const char* filename) +{ + HANDLE hFile = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); + + if (hFile == INVALID_HANDLE_VALUE) + { + ErrorMessage("Failed to open %s\n", filename); + return; + } + + // Get file size + FILE_STANDARD_INFO fileInfo; + if (GetFileInformationByHandleEx(hFile, FileStandardInfo, &fileInfo, sizeof(fileInfo)) == FALSE) + { + ErrorMessage("Failed to get the file size %s\n", filename); + CloseHandle(hFile); + return; + } + + LARGE_INTEGER fileSize = fileInfo.EndOfFile; + + // Allocate the buffer + std::unique_ptr buffer; + buffer.reset(new uint8_t[fileSize.LowPart]); + + DWORD bytesRead; + if (ReadFile(hFile, buffer.get(), fileSize.LowPart, &bytesRead, nullptr) == FALSE) + { + ErrorMessage("ReadFile Failed %s\n", filename); + CloseHandle(hFile); + return; + } + + CloseHandle(hFile); + + m_backgroundImage->UseTextureData(buffer.get(), fileSize.LowPart); +} + void Sample::ErrorMessage(std::string_view format, ...) { const size_t bufferSize = 2048; @@ -994,6 +1064,17 @@ void Sample::Update(DX::StepTimer const& timer) m_twistMenu->DecrementSelectedItem(); } + if (buttons.x == GamePad::ButtonStateTracker::PRESSED || + keys.IsKeyPressed(DirectX::Keyboard::Keys::X)) + { + if (m_currentFocusStoreId != "") + { + PackageDetails* package = GetPackageDetail(m_currentFocusStoreId); + UninstallPackage(*package); + m_currentFocusStoreId = ""; + } + } + if (buttons.y == GamePad::ButtonStateTracker::PRESSED || keys.IsKeyPressed(DirectX::Keyboard::Keys::Y)) { diff --git a/Samples/Live/DownloadableContent/DownloadableContent.h b/Samples/Live/DownloadableContent/DownloadableContent.h index 00f67c7..56f7a79 100644 --- a/Samples/Live/DownloadableContent/DownloadableContent.h +++ b/Samples/Live/DownloadableContent/DownloadableContent.h @@ -175,6 +175,8 @@ public: // DLC Methods void RefreshInstalledPackages(); + HRESULT MountPackage(const char* packageIdentifier, XPackageMountHandle* mountHandle); + HRESULT AcquireLicense(const char* packageIdentifier, XStoreLicenseHandle* licenseHandle); void MountSelectedPackage(PackageDetails &package); void UnmountSelectedPackage(PackageDetails &package); XTaskQueueRegistrationToken RegisterPackageEvents(XStoreLicenseHandle license, PackageEventContext *context); @@ -182,6 +184,7 @@ public: void AddNewMountedPackage(std::string &storeId, XStoreLicenseHandle license, XTaskQueueRegistrationToken token, XPackageMountHandle mountHandle, PackageEventContext *context); PackageMountInfo* GetPackageMountInfo(const std::string &storeId); PackageDetails* GetPackageDetail(const std::string &storeId); + void UninstallPackage(PackageDetails& package); private: @@ -195,6 +198,7 @@ private: void InitializeUI(); void ResetStoreButton(std::shared_ptr button); + void SetBackgroundImage(const char* filename); void ErrorMessage(std::string_view format, ...); // UIStyleManager::D3DResourcesProvider interface methods @@ -247,6 +251,7 @@ private: std::vector m_storeDetailList; bool m_isStoreEnumerating; + std::string m_currentFocusStoreId; std::string m_lastSelectStoreId; EnumFocusArea m_needSetFocus; diff --git a/Samples/Live/DownloadableContent/DownloadableContent.vcxproj b/Samples/Live/DownloadableContent/DownloadableContent.vcxproj index 57b08ab..02e8660 100644 --- a/Samples/Live/DownloadableContent/DownloadableContent.vcxproj +++ b/Samples/Live/DownloadableContent/DownloadableContent.vcxproj @@ -495,14 +495,11 @@ - - - + + + Document - - Designer - @@ -579,11 +576,11 @@ true false - + true false - + true false @@ -676,6 +673,32 @@ + + + true + true + true + true + true + true + + + true + true + true + true + true + true + + + true + true + true + true + true + true + + diff --git a/Samples/Live/DownloadableContent/DownloadableContent.vcxproj.filters b/Samples/Live/DownloadableContent/DownloadableContent.vcxproj.filters index 948f084..0f975fb 100644 --- a/Samples/Live/DownloadableContent/DownloadableContent.vcxproj.filters +++ b/Samples/Live/DownloadableContent/DownloadableContent.vcxproj.filters @@ -1,4 +1,4 @@ - + @@ -164,7 +164,7 @@ DLCPackage\Package_XboxOne - + DLCPackage\Package_XboxOne @@ -185,7 +185,7 @@ DLCPackage\Package_Scarlett - + DLCPackage\Package_Scarlett @@ -195,7 +195,7 @@ DLCPackagePC - + DLCPackagePC\Package @@ -220,9 +220,6 @@ DLCPackagePC\Package\Assets - - - Assets @@ -314,4 +311,9 @@ Assets + + + + + \ No newline at end of file diff --git a/Samples/Live/DownloadableContent/Main.cpp b/Samples/Live/DownloadableContent/Main.cpp index 9376010..1f614c2 100644 --- a/Samples/Live/DownloadableContent/Main.cpp +++ b/Samples/Live/DownloadableContent/Main.cpp @@ -378,7 +378,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) } else { - SetWindowLongPtr(hWnd, GWL_STYLE, 0); + SetWindowLongPtr(hWnd, GWL_STYLE, WS_POPUP); SetWindowLongPtr(hWnd, GWL_EXSTYLE, WS_EX_TOPMOST); SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); diff --git a/Samples/Live/DownloadableContent/MicrosoftGame.Config b/Samples/Live/DownloadableContent/MicrosoftGameConfig_PC.mgc similarity index 82% rename from Samples/Live/DownloadableContent/MicrosoftGame.Config rename to Samples/Live/DownloadableContent/MicrosoftGameConfig_PC.mgc index 1e4ade5..b03fef5 100644 --- a/Samples/Live/DownloadableContent/MicrosoftGame.Config +++ b/Samples/Live/DownloadableContent/MicrosoftGameConfig_PC.mgc @@ -1,16 +1,18 @@ - + - + 6F9201BF 9NQWJKKNHF1L + 0000000040380102 @@ -18,7 +20,7 @@ - d730ab4d-8c24-4963-ad8f-4a292b448997 + DD755A56-F89D-443F-81C9-47412CA0E048 00000000-0000-0000-0000-000000000001 diff --git a/Samples/Live/DownloadableContent/MicrosoftGameConfig_Scarlett.mgc b/Samples/Live/DownloadableContent/MicrosoftGameConfig_Scarlett.mgc new file mode 100644 index 0000000..3b65e3f --- /dev/null +++ b/Samples/Live/DownloadableContent/MicrosoftGameConfig_Scarlett.mgc @@ -0,0 +1,38 @@ + + + + + + + + + + + 6F9201BF + 9NQWJKKNHF1L + 0000000040380102 + + + + XboxOne + + + + D730AB4D-8C24-4963-AD8F-4A292B448997 + 00000000-0000-0000-0000-000000000001 + + + + + diff --git a/Samples/Live/DownloadableContent/MicrosoftGameConfig_XboxOne.mgc b/Samples/Live/DownloadableContent/MicrosoftGameConfig_XboxOne.mgc new file mode 100644 index 0000000..f1f11db --- /dev/null +++ b/Samples/Live/DownloadableContent/MicrosoftGameConfig_XboxOne.mgc @@ -0,0 +1,38 @@ + + + + + + + + + + + 6F9201BF + 9NQWJKKNHF1L + 0000000040380102 + + + + XboxOne + + + + D730AB4D-8C24-4963-AD8F-4A292B448997 + 00000000-0000-0000-0000-000000000001 + + + + + diff --git a/Samples/Live/DownloadableContent/ReadMe.docx b/Samples/Live/DownloadableContent/ReadMe.docx index 05509f26a9bf53bc202610b7779e48d43c29cf3c..841c02138199d424283b08276ac18deeddf42c9b 100644 GIT binary patch delta 41584 zcmV(*K;FNikT%VWHW*M#0|XQR1^@^E001EXy$(|f>j3}�jw&BLaU-Yui8&hVO;` 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

V1RJ@L0Bq)zI zSzo3*L<{{i&|BznYlh1R#&0dU7P1|;&+Qu@em7QizEe7 z&%xyo&Or*$-cCK-3mS$gXfC%{HuUQvNGSh*=*iJp`enbL`gy_413F1?S`j<>a$pTo zZz4knq8*&@aj^FSwLg#5>Rksj2BFb>oNn=E!(+X&k5QLM2?LGLa4o1c^Yen9KWU3+2~#E1*mEc(BJEkn3giEz4~r{ z`^x#u43N$bwKp?YEwXguULmGKp5fafV3=E7!wIs*maV7|vem;GR=QBUkuSisRB}@k zyIBof+C)-aI4I?PcXS<|@_8}_dU!6$u1Z0q@xW~MyqMNTMjt@FV@4e1-cH>Puzyyzf@0&@J>jqC$v8}W{VzQKzn|t1lnagdL>46IA z>F&TFSy_afb>C9JX{LwDqVAH_aV{y+y9Z8UOTn1ZRo zpy^ajqq;XA&-)vantsdOxMd^Q6Mw=hC)kg`z|2cqrcDV%Dssp8;wl!GiWo0{Motb% zzKheg0(&#yf50g3Yb0ABnj8-2rQ{O^MCfsB#?zMe`9#3Yxzv-T9QecWwU3Xl$ z>IE3X*~mNMNJ{Ey&j)tfN)uGt?huN~?wB|Ub-uCG1I0T$Lu+cLr`bY(%x-Qxbs}U^ ztweaxEHLKGonmw2PEjE`~qSPNi30RpzC^ZioJ{Hfd}&hWUh zLO{yQqqn7=IZ1@u-%U{DX(bXf+P@Y19f)A~vKCK`dsVg{Bn|FL>|_0 zM*RzMUL-Y3i`>nyO3FKT=qEBU?`7_Ls(QA@pag`rJm&!cl}&Pg)B(VQw;wUAlfodA zNSJm7E{%u`&YFXRvvWBZiXjWcTCCsE7PyXM9R^+$p7&Tm=9jzdx%O029$FWBof4`G zqi;Ed0DHg-dIvo~8ay@TuwS37M9)S0)4n^`g&1ARF>pDdZ&C6%;noH1xz?UAL&21( zVE0g2WS#EvQJ-#qjGZ0Yf}Uu*Hs_0fC)m_W4ve*=yRne`J0ss@R|GBkIIC%N%;xr@ z&UuB}$nok`=Qyv=fx@nATZ`gWia;P-aYYAtfRU%zEdo%qu)6(1-@H%)8~4`IS8=T; zDXm{{(6{lM77R+N@`0`@Ye^BrH}H1Zo0#~nAw!F;4^X3jb9V`JcjjnaL{lPANPf}% z2k8+$yBEN!8smp5hnFE_AfpR?F{QXu>@7#Tiq4NgIov&N`~8r1;oMKOQ!%@gQwZU; z#>r7@;ZYv+|jH+WcN2{Gfa=vRT=OE7G?O zZ@d8WiW-h^Z&?CN$rlrC{gh2_)LN?Dk1S{=yIZmsapQi4f@6J#WHwYen<{vuA~G;< z#Im>j168|(U~E6#H#a6ZpD!sfOUtdavr{f#dWzbAm5)NVaxyFIF1V6fSTxNAc<$pf@P! z5r5?E#eGz`T?B>BbUj9TckGDn!(n}`Mt9sJwn!-(9II|oq9@tXQ@fEnYr_eu`T|n5 zC9l z_9Yj~IW;Th>LbGSksu^_f*$;QIfb`ma_ttROsn<{ro2Eci@=y3mAnumtiL6x!_wFi zFz}NWJT6cG_D5-^FLA@A^fi?Yg*g}btpj0y?piUBc)CqfrFs2YM)tA1z{=F)8mIkXcYg4ScI=+Hy?n@vBWOV5La|njkaghV{4Ls_!=%K2 zz$p&c1D5HHyDwRbh??w@bbCkHx3?`x^^%p$CnIyt`5c@Wqax1jycX%U2%l!?b_N*_ zlyv;qH53t*Mt8K21}3w~oU1TTVZ@pr)IOr=Fxq}10ODHJPl+R!K-m)qPg3`+lN4u@ zh6Ggf$*5aQTp$WRNICzC#Ugv(5tm+n6&|%eEh1VD=cz%)9@cgC1$YNWr#-swSg79p3{#ENX9<(GS9MJpn}(db zj8uM9K#v+H#2jz}UGqNMaGD+NMK-60<}3mCgwtX)BGkSVL|IV18Pa9&_K;A2OFPhe zzrad)dsfsp_HyCEHqPRGtaxc^UNN*OcGS+y=GefIdm)Fj_2BrJxCGgap>qU@Wn3(e z!*LmfwPr*P_n5{g0Db|Y1dHAbXL5U-UjPHA(hjGwn&XAZ8_HmRq?#D5g&X$paJ7)Z zX~IS3nxLvkW3+pNwWO!)d)~HxAiDjI9cOhiljBJ(p+_KeArAzWi&KW+mg)7L#^Rok z`w-`nMn40s&!WM+(m-FWZW`U}P^q|9|9|X#X^-RBcGzDb+JG^W>M3zwdeD{ODybw@ zX(^Rdl@|;sijt^>RFTwHv4J5NBjjj?&@h)dS_nvd^c}Y2Q8{w|mit;96sWM#o`5RWzsv>i1lSbWc z7R6dlY7dl4t1ooRntog3YXf$6d*%B+;FD7@0DduT`IYNpzTIDF*S)~{nzzbYx30Mz zqX$$(W7O=MOU3id<&iLdXRH0IGR<8Vnda(h$`;7}Qn06%=;QclTUUXNp>(D5-XMVX}+%fVT#du6!=naT?+>({T3A#8P zN@bR5o%@~6LT~zflaQ={Y{bE62q@85&_chWpEY<>XBJko(VX0W6uJEPqEu4d+kvgn zZE4^WGeA~3_J+6HM`tuJNnF)6=FMEyoEfWj;7J)b7w3JByt%wt=Fh!pyV@2SSB7@$ zSzSvK%DLO~hB|Wb#%xxqG!#Nvk=|f#8C1J?+H2Mt%gU80Ol56M_;fEYmKMF1+bJ~c z^Wws3F2{v)<5C`f`-{d^4!65>&m41ggK^lE#+77`FlLRi!Af$oIBxXDwqG-S>smP1 zZW?D!xQ^Z79WqbPre(a}lnd77^!lc(`~2{t)@V@$u_|9&1E$Pb3*5Stk!4#@M@!|j zdNCMuS{;5;o?l!qZD6_wWCIAZURu^lr|pJD+qb9lR>5e08dPWIONE|Cj%qh~x>8-Z z3lSLWUdgbgvu+M8HcO}VCZoEe(|&&}mBmFZ&?%yLCBl7kQC3AQS00_sRCrYpYoe$u z;0khmWvqlzhw9FZ$t8)l(Sa_jHFI)Jk~MDCIU4Zfm07F?j!$V)@~Qf%Oh}c1+FTTU zw%H|}&a88PcGe~5<$0mWutx+pVGM#}TP1vGT1%d54wbIf>^GHC`%=5OF*RGbodl~Q zwcenAqsV7Pp}qkAYniLkGoTIz(%jAW27YPLD2!D0d?4RwXVYq(8FV}JO>0&^DqOGZ ziM z!gOp&w_2I$;TNO~;F-(uil_w$h{FEHx;mc&w1r3!2M_`I+C2h#0QesGJFZLwwL?~B zW}fhW!rgIoh@;){TD@m#lv=1%r+T;E7;4_o3=}<`CyL5YDqaz`n(vQK%e}T=WHP@icvZPc%dK=z#XK2_z!AO8r=5~mnb3T5=gr_?XyR7cpujorh?!n6w0&?eS#59U(YQs%!_-2K90?=C!X^ajL-ZEHTEWd2-5s| z{#6ihg`&CMOssBqY(5YVrF7_Cr_8}w7Ml=c`{SVstJ*D&;-P|_5EKt}1mmG9bg_7$ zb0cLSi`JRMi4C8oTIXZcSj{`+^lU~K&lRV7E97iqITz;VBh7KGs?uGwFZI4vBq`M3 z&@u9q+pR9^xYIEDty4Y+bb_jXp3SIny@LyqRJz1hi?g9T(M;gF4wc4mtk5+E2QC1; zn2xDJO*HUxNhk&lUbb8A(Cn&q@dl?HndEMUh2d&4T~fr%#uwH_t$agw8nicXMOD7G zEBVWdc}EdyK&cOuqCLFzRx?X#w`$eEh;ZpT!4G?VyD&d>Y5RO+yV~S`lv&;mT)plW zT0N>Xn)YsUtGvTBjtU$#7~RZPgYM9!>imQp4{^bpLq%E51zXMHLaH?u6*a~nnv0jU z8PddBZ7n0DP^j~e4?Kgstu9(FK*l0ZSAAgENWlo(%*Cu((Tg}S8xTVAs9(-sF69+D z%r{noN!aGZEexk+x{3pT+o@st*H*Q@I!E8}WP`)xT_@E3fr@7fyU=bhPQ zMVqiALmKqj^wq7av*bu-mpA!w>9X&aU9)-7>=E*+WfunRQs+XxJtZzXrD3Jbly8lT z`Mf6p2P*Kz`_;nfS*hEvHJO2QbiFE()NSF`*UIdICk*DcHf1M&Y*o0Z^dze4S6iBF z++G5QLpY)QF=G;anj0=5yu~vlXW@o#A_3FN(oJom$4*M}wTK6>eOzem(78v`Q_pQE$8MO|NTJ$X1gYtu9ZyJ&LlM=Yu8R z56ih z;!qG>ijGDgYzyzR`2djc3|&Cllq!KljQnh5t>!BolEJTA4BjT{jFG>*Z8u=t*b)%E zDwk_8dSPSMwB|Lh#UX8Q3Dq<-f#MAYKRx&`9`tvX+Mp@-Xx@MqN(cSk(O?12E{!ZA zT0ntzu8(+sw}#r^XaNjbLWVPj(d)lQ#G z4wDzAroWn+NQ;E}!snP+kSj^BIQ|;_4s?qTvoYYuscZI3cb&xa_Lct%o9h1H?W@T* z^f$~H{33{Ngy0vmG=tH`Mua}2k)TU#=va2Bs3w+wZ@R%SVSM{+tb6D)X5QFuUs*kD zY}qEvueGtMwKT_NaM^;Y3_~%|_uuqp6YLutBT{bT;NLeWdj0Rfj2#qqBT|C*d`KCFW81(cfut1%p@RmUH!ztGo5MEVzJl3!uP`0u5V$aP z9KITKdry}4G>YhRlbw)e;_$Dqpu69T(((RSaTsp@H4FoX!Skib7R?=TCzMwaUd@>f{vOshj-?eKT!Naiw|X z3EfVsn(G#X=}7g++y$i*r*n?zl&UrQwz8a0wC2%7>lmx5o%fuQt=Dd3)Znb#VCkkS zvAsUqx&oT_62^&+nz>WC&{GJumCsK)#!;`U%+$uHRy8)F#a+gcKtx=Yh zGv=f{ZnCz@cTTVRVxJmRbGL2OAT{wcBWDTf3iC&_%idm5gh8raL!ZonY|A``2NQ+qD8{ zUZFXak>a%i7=jT6Rik3H0yvhIBDDglFg0;q6CkC404;HZLO#8KfPbPm3w!K;=v`PP z0SkeGeW8@)FJlu-z(?b7ni2NGr=i27`wUQGX@U?b4#Fb>B}(8Kl4Ld@L35;Oo@dek zLejKI!}0(TfCOKT036qThhyZ&`NNS$J4geuG^MED!V0*4M6f)M%Q%tH zN1Vv1sW{;G8$~UG>KAq*PJ-E2VF(L0|RG)`3{Ckx1i#N z`-oal8Ili(dPhIgnQIJxbkBT^bwiE!dS{E;ICSkpQqzZ~S=Vaq4G^2Qr zBoHAI2@~mKJ@SOgD&(el*oeZ=I4h_1h*PAthl|m#K+f2X$@BYo*yFa@7{;L|rKfC+xS3mykKm72& z{_5_#zy7B``t1*Y@)tk;yI`_XDiVMKJP8xb?N=acEjoY|F z5fu@Cf>cC*cnfv7(|X-9Ql*(SM#d6!Spripld-Cbnkr@~wD{OL?6VY#J!}RG$N@74 zTl;8$>*piOe~o!2G}}x}H{h-qhV~nvWjGz{K9n@jaDB*V0SKN%Eu#Qdd3^#pJ%&A^ zU!d3HWtOHA=FbUBXR%&znc-MgOSoo?sL?7BS$TSYaUkzA$s*2n=q_)(WK1#VSjqBJ zjmPvp-xu~8hVG6{-+xK;QMfFrB%a`N3I{?OAU1|RT%&jz;(6N?j6K=G=b_;@){#?c z>Ygup7BB;b`d6pbkn8uXdOZBzv(d=Yd(`)>u|1*P%_`!y>lt7@3uT5h20Z_f?@W)E zES-UWPzao0Rb0xlobebwPw`S1UgBAiPe_*M7vG1hTxyPsO@Kd%S$1F#0@y-vVD3Oi zeP-K`@&cW;1L`jeTQjuA%NhkI9)TS$6C$TZmcJo5cKehH z#j@o6CIf<9WJTiu^}U9|VcJ{cXpZF)+M7{-IGP~1`^v|MyTy?y?8Efd+Ns^M`oPHV zLJnFu?r4plwo+;w6OR=4o^E%6sNW};B$2~`a9vYHXO>h|4`_p3ZS7dza6?>KelS4< z1vE%_JqE_M?ExUb^9LYYY{!RDI=}%MuP`?#3f;{@p)By)HCaq7tkXCl@|p_O(Pq?t zyhICl>Ka7+Nff;)@`M&CmS=HD{gED7f)ON<-K^U`w#OK-gZ01g%X_KJ#9)vK23|7Z zQXpax!!=DCTL5nZ^UxyZU|YUKhZ|dRng%O(9(YUlF&)V&;Htrxs$Fetk2bMzwVze6 zD`$ooz{<@$#!9-hbY`}X<*=qbL7(7%w8PnCZF!f$wg8UM003g{jDc1Jo}@8?L=I#) z>Ln!W;~i2Q7!a#)iqAYU%$pgo&U9}!9XdfNb z#9dE(i$V?*G)TbqX0`!!nhpb}f{7s63iqd+nU8hM6ua#~*}7AFuEp2Oi%~Rxl~5kS z^7Q(6&4$4a0AO{cEU#hdVPLY*LGOYYkDWyp0Sb4%1WwIjkUf(CB~?n5WVGuU?U7gxL9s|p`ZrWWFsFEsxj^vv92#p7^VubPm1p$2p6;r5%Lx&slO%KRE z!w9(*2tx2xU_imB52V3iBWP!T0E3~AGsDNefL0uW{+EEe_7V5PVjVQ@!qC7#tZxK) zI&bMf{(#{ElF;Y6HAcHY$_$-4f$81u_I}V#`rf-S@$j&B$-jm06y(<=fmA1!Na|*Zz z?qmbc7#ZJ@rx3`qaW{Z97?u-QXaRJ?&p_Eei{zR(piyJ&fPq892R*iZsCAL^HPa#c z`v9F7tX>Wj5TItn^5EU|PGUC@s2f^@8)Ek&A*Q;6Z_3J9Cefe}xGJj9?tMr!s2ocW z8%B-oTc&Vt{Oeot!tacK!#|-L(;3IygK$0S{>#8k0FNL};Ncb*DCCz2r32)jt-G)W zyciZ@hx3B>U`6=#y`X3_ybD{udi#n;Rm#z2cm~LNEVelhLdVg=7P!3#s`I0_kp`Fs zA<5_qlH&q_?)rjdzRAy9hSk!MmuLFz6me8i(zFqC)z?T{!-5vmTj% z&)a0j@qIus&)aAq)SkCZK;l417G9*)%V%vBGG3ezI^(kAjMkmtG(1>f@umllI;g|L z3y@cV)ET_mJe1MdFyS5eJa)r&3KEb6_g9y}Rb&kJ+?98&F8tUBe?c10wMNf+amWaMudhPcMdI-2l`Yvd0DMrBOtDq|i@TwmKAQ5ugO~FPW>3qr34%fG0p8BW2;V zBB?So8KR4SE;K3A8nv-IK77gwfSA`O=2YJUL&T;58)u(e)O1q?hT*ga_AGY_&@JIX ztz)(yBRqCumF75=l;y-IXEKc7DpWy^Md8LaQWlKg$quo8l{O44N>7DGAu_X zHlPy7c~6qI3>O8UIH^NYeR<78vI2z@2_FEL8D68P`1aUtc>FYU8F|mu9JmSxjZ+(p zX#IZBb~gG{VxW#UpREO?3$mhbhdQ{|P*KT$&k{V(qs{NCQmvB}*m~O)?6upk$v2 za7dEiSQ$pu{%p9c`eZI!?>7LPu2WDCMCu{k?ZSVsB)XdbLN0$MAx%-DN^;2<22P`Y z!M#WT)PCMd*@Al-Z>hF#x-UL=oK*-~q!M!{SPfTYKAsEx9_5M%x03yR3rp}%@Z~f* zjH6d1ctL(BXYjLWe}HNKLQ0Ml1fJxXMEDY+@&qdoace2gZZFA*J7SESJ}ml|kckf` z+6DBz&IDA$^hGngPHr_nC8cg;0W%4Ino6GGOkaMDg;r>dRg%GxoGfdagm~|#z}*Rq zbikU^9QylSmRC55Py|^`mQ7G-T;n5|yYCqeWhqye4wj~p!m@bCjT?_fR3$LO1fD-M zbD3r|fszxFgc1}DCxm#Y?)@aUun>Rp3|MH|Vdz1sN%a!cND>lFsw%`w6b=%9EekAC z`tJh{ix0puj^{DI1gHs~;|Wnq_>sW8VhKE zH$K8At@aWqTtkNL#@kEGoTNon;Q0jaGK5Gf1oP5yM>}VJX_xM*q9%cka`}-J7>4B&!ttP$`^YwG<~5m@sA+Vct)j!y8CG|l z6a}*@P{=Dxn^@=T>=VhdNRgZr2)>iom=25yFcA1xpH9aslny&J4XbAv_i2@3SuE6c zYL={ja*WksA@Ozc##UN?Eat%k0vP5WBzgSqm;WKdDC4A};<%J{bFkQntP7zdFh7zX=DlKG5O@E9mNRd7ysnIl<6oQn0B2tPVqImWUJ;pJc zls@}_gVL@nZ=KT9b!?P4H8tJwYO55%VdNee!7~tVw6%T0F3ivKXYT&A)$Jgcdk0DP z49j}^N(K_k0nR4`>p--<#4W>%T%++CFs%TK`QyO@?{{Rh2JPjCvz&;6Xp$^mRlk|!Ng)pl+O#Ta!tzhkp*)+pk zfKjE23SzscpCr-1{M6!}enOZ&3p2u`@gQHUsDvrZFsUqm%wq%}cacLN%EM|cA0$Z& zXSLR8sdQG*h;pjz&Qwtm7i5u>_$;ZhkHJrhc%})SW;FSM$ebrp_g;!{fGpEthC4uG znAWQI&DR*p8q6vk!s0_PyK;gu5y8vTLkc5dGcU2vn2sM>5+n_$lTi%M1+5hPFapJE z0;&ZbRi8Y6qcCzrSEi`XfM<3OBW<~3OC;^!v+~Tax;m_x4tNP=r-j^>c(Bc5ftC=&WjF8A|6^0fO{9{%X2n)~N}p`~VZ-|jSb()A`M$fT@pEkEO8 zaMnl^B+uk_(h{k1B%aumX9-Hv1UkOxb&#gcXz2oo4`<$1PS&TQyB;mtelBwCQx#gD zfP~sNn_ad`vVovG$Hv0CmRWo|;9`rMh{Q>BpFXVV|U;E=?{PYH+SFt11e?yS9jn2>hAkL`SI6(|HHd~y(xwM zxkCO&Tp|B`;s1%X?}^W~?-99vvbFDlSO&^}giOYVr!7xCM&PHY7yu#>r;=G=xI}9T zp~PcfA8lsGwe`ok2>eVohXLjaI0cq#bIq{Io(H$6Q3+NH49>OE_9s(tA2j%SQv+_j zgc{VT>sU70o<>ctBiPWY{ifL8x;VG@CW8S)F`7oRRKfsy`r_$s>SRP3*acK#Ih|#H zL{*{CBJLx5Bp4jXK?K0mts5Q1+XIVeHVGbE<2fu^wUKV+8chNN)t5ivhzq(peSm zL{#Mrj6qYt9+#2{>NA;tJ_DInPfz7mRc^K7#|LC3)99%vYF$jNr<{&x zMq)`u;F9r;l*$ma91qVuNI!o@bULE*r}c89+Bl;wpNwe8<{_d{7*Hn0CRB#i2r^ln zPE{q9R^s@6E|*^+d7KiHwJ=0gqbO9%>}eL@26kvKKP+avuDlQIhV{uW;c-EK>$PJAfV+3h6 z`GYBao4B1ds&o0ADYf1VIETw)ESo~@TMs@fVjtcodZb)D6dleO#@5eG0Hfesh|T#1S!jcQQAnH+#e^|T z6OzKQRKn}oiK+OLAc!frg*r?#hWDTJgwB@_D!Z)-J;PiGnUzU)%Y$PCQexypjOZih zvJW?{fdxfz zU2+idPBgeZJU;{)*=Uo01%i6KLRtr=aG+>bKvSUCg}(Aa{Q^%-uuxv5=1S5y#0x(&4-_gX?#{{7n{S0;)#kG;|nLbft?R zahhTvS?-)*RP4o`AOh5QhR0J6xuXb-Z=P z&=cft2bK!!gE@cs*h)APePm9J=_t7<`~)-o5vdcKu`GcR@7U&tGaxVYNuPB@fuuBC z6H*}{D$51x6K$t|!t*Wft*E_{^Wkj*WN;w*_d=@zo;Vyg_@M%THw?#xn^aE=!Hf4^S zvaxO3wz;uw+jjEAw(X5Kwr!gm+jjEi_petUPSs3T-Ktyno|@_I={_R_$;auhK#0n; z7OZ+*Z-JIcfu(1PBIH6!KZjawYm@%T%y0T>6{nT}r3HvjdXE65)ezqy-HiCB z@sNPOFJH)42rohl0r2a$LeWD00|5uq4P6E+Gy4Ipg)!?9qD4G$>sKWnotGdC7)fdvY?4ZIux6UVD8t)w{C zk3%Xufegr^wHMjRq*R_XtAD3T0W-t#?m>#6WczRmR@%Rcx^Pik)d{BE_lMhnC9Her zheED?^)VsA(cxU z*<>QZ-QQk6nuG}5ZP|9~D(|{LkjcHb{Rqn0*8}{ESdbg#!NI5PAV4 zU6Lbt(wuA*@m+@5y3Fb}nhqGKI7P8O7hEaT=ws)#_hi1`NhotD?{g_AHxtT9s7JZo zAqB9s8|+HnA`-=AOpM(Mc%L+(LR^97N_PD56->o;CM?+t_zBs>2q z=olgQYp%0g-!Y}Oea65nlt7G03`s7q%A%51!gOEosodNNf$^%0c_SjS zHg^4t(kgjuAdHL_fP_MSY7WnjdO$!0QF?b1HeGma+d6}A^UUrd>y3*yu$u#Xe*!S} zza86n<{lpjyk8Oro=9f%H+FvYJKgal%&tDs2q9u4^32^c|LsoMDWLTm(_Qcjn>Q<6#ZbNuO<^ZP*ocAP(?15REN9RU#e0dY#wzjQMpraVY6!CLu{G%f?{!V}H#FUEHI-TCu!hP8 zPPLuVD}(RN=8CIkGJ~yV_2;yuDrDb}H@@oB;IM;Oa?)YzOuk4vJ$+Jos!BkihG+_A zv_R^?S#%2NL?Q3rJo*~TT(zO!(?c5RWFyvOE|L3|TDp(PFO5oaLthsSQe0>g8=&e~ zyB`P=^5CbHHK`x2;z}>tk=@8!mBXeHG(JgsIaslOHbO6R$JFHTTWwq~39GajXv7xW zg)x^@Y(&=BLQDcUumI~U)@*=JJ78gi^NYk3PT+Pbntlma@#$)$H|qTw{g}N{?O2+x za;)(MRb{r)Ho*tMM7WXrXdiJ+f#zZq!Zn|=B&I}`K*e=j_G)Lckg2P zk=Rp8H98B1NEtF0ks6M|!*nkj8bk9z$Xt}GEG-f@?{$(ZTBlxHV}Ah02R)~_&+l2% z*w;-c2%PTJO(erudFj z`?6TbH1vZKJMLJVYlWZE?`P;&g!?_m2j@&`Merq(gmZrLx}U<@k0f01%&=hA687&sB<;z`nsWgCq?&IT&rKMQ9T%Uqs^#e3caLsd zzGVsj^?*6;IInUiG)<*pN%s^JkMLts&<;UW2FS|6Xxmelo^(jp47PoF)&&W=0J?hO0B9C4>TZo#BGn-{-vy;;`k@Rf)U!w;Gh_(5*N<@{ z>2*M#raMqELo%w8EBQ(1a>zlO|js-TjLc8KRz3bvv!zE`e zZ5`}eTR^;FO!F6lLgVqgHk>$R$thtl#sh8Dupg$TqC1@si-F#*yjm61I;;qPdk<;4 zW1OOo>l4DqBS(XnxTmcA%G@*FgU@KPY##vhQNr9vkicZ|TkWT}UycC@<|v+5Q#TgN z%t3q)a!dOje=>7zT0heZNax7)zHY0nLxjFg;IaoyE|~))=H@`Ssvjfk5SmHJ`Jlzq zF|P<`m_1m?m^e~H6Iftm{_?XSjgZF57zi1*-Hb8rsPc=DB9MtFMMo}h-b&6{GaV3@ z8EGxGL!C;ztiB#~pNTErZnGtVNS#RlSKaR7ZQ{cw3lSk=+j+tQy5(x?Kn=$qD&4ip zBqNX7)zhTA6jWUDIKa#5zX0W7p#cLj?xXJdw)7p39%^5?n=7sT>t`|Sm0h?G^-YB5 zPcGNs4s_B(r6q|rpsB0$aEp6}8v(EZ>DD>8biRDrz-A1qZXnTDv^NuM9V9Df7r2vx zG{p1k^W)^{Y0kk~MxMng{9BY6iTPl{FY~Ctxu$TYJH*>|dE6R;u;C#4YOiG*XvA;c z$kUs{C0*lhgnx1S!^`d`fJ=NNf-Hk2%6*bADc?IChH7!r?+`r)6uWRlR}Dy%PqAYC z?VZ8DFM7rQ07TQf$lZu0RDEeR`{cU7_{4Up{A>>)#$jS&z$)w1dm7+~?bF3d)}wpJ z=YC*{`%m1?&UdfuMs;d_6T^G%Mk0z>dTinCN(BMX?ZjJ7PNgYgIC97jD}$IA87@CU zP3}{mXVQv^6kK80o>272eg=>kaQYBj#xbs6IEL&;xv}tJZF~SO)Rf^#gljDaKd`HT1-Z|h=iosV{ZYS$qFSEoWZfsoFwke1O zrc^`k5Z~ zGAs(b&NF~Z(=$2wEz#~ASU==7FQ_sNA%)#}RH{;FtIWdVMhU7W?_D)@D@x3`Gy z_C@U-XV@nJ!=9vSpNo99*q8;sRn+{=<1G6d%tes3XQt8MI3bIRH|eabGR(kv zJ3h;OLqw#NOIf7{jd;2}U&lrREeAy09=gQ+b!*};z@i#&-;p+ldS&zq;9|IN&q}u^@b@elXV9q$?Jl~tQb^^xm zXR!@L$vI|Mm+=C-ZKb?Vdo}gjj@+w1=G2h4KBgQkU~ehEX*I97jgy~-;|wtu#`*Gs zW@a%pCNJ%`7>d+EdE6VMom=U*5Q~`XAhA#vcBylslas9gQB0mHhcfu$~Ra8ktz}h zXdG7)AcQUC?X3*-?<1tnf$305NL#TVl#=fzb%A`1ot-xLpm)SRxdJJ ztvvH54-(9;5Xpom(OuoN!K6A%JQ|SLr?+H^UX-MmlXdvh7CaWi9LU|urcGPsxpj)< z&*r)&4M;V=f}C_vGqxbthoJmb6G}KdwiL{TPQCleSx#jY;U+em@?CcOJing=cwF(g z>D;cb-K0B$NYhq-&*ZP;X{$lofTGvMV*4pybX1uy$*^k=!juXcYYe^EhUwKicytaS z=aatF|7+{Z9g4mx?&s=8EI#o1KM9+I#ZP=nl#SwBrp^^Ja7Y#!_flAlGQB3Rtv zjriO|!8}fSsn04%ri@T~xfY}Q z3H+b_9T=RMOJIHyxHci+{}Q#;J-Bus1cfJwRg%Izr^FZN_ox_(3#KId(}sHpS8Lyt z0rO)>FU`|J&C*r>)TQ1*Uo6%{icaVYH8co$xm4RG?Z9?VuGl?eZR}|BfmFSzhXH%1 zuJ5OlGO0|75|phOmGCjG!9R}P_xK=)lW5VI5m#Z00s`Eb9nq|CIHjk)|oJfs5iWkB${v=;GE_F+sM>y=$1&Wi6#3i#+U`ztC4-U5RFJtgVjICG>7vIlP< zr8$%Kdn#QWR&?X+3HM(^mDTjE3U63Tz|ap-y_qP0vf5cY!jV@TfB#G4Oho#RsPz+~ zx*V-RoDm8Ce5w(n_JB7{gYdO{c%+-Li6&!jU|Hrqz3`9(5&@%KWd%t zIpYHf0OD2 z6T5F%g;`|V$^y}V!b^{J&-`w&ysG1PtqV>^+|n-D5-4Iv9)g)^k;5#Q3S2DE=(O`<9bNZNM^>y8ZE-zMKIh`4xzXY7b_?u}S#) zG{8hjQudbiXXZl8W>>0G?=@;Qd29q8^!tHu{!3w2j19b?@)@G9c` z<{(}G0gF3KqELxU|JM@lVW13_1L&@ye;CgAuNTmt(nhO!akkiIcE%A_`uzMvWwlQ7 zLW_?f4dYg?RIm`NXd7n1Y2h`-C@^5ti_nup-i1mYxh{4uQZKq9mhw_tz=pS(-}s+{ zF<>C184L)-Nj|rdi_CinjTT}DH;w8V(exqWv=&^&+6jrdN(zwaR4DYKLm#gE)Z&i` zpz*niXW8eA%rGg>G~(D=t}qS*i+>%}`&I#Nq27PpVib^t17Mtv9bI5HG>u#KTfAE< zu`?lYak*?;K#qAdI`WW&V<#d(J0p&V*XE*-5`u`KSx-ZXCr z(3{=6j%^9xdm#!OYGi93qQ6Edkq>%))mfR47Kii;PjlLAHr=TA1@G?Q zz51r36;Hm$aLlI~OfO{?_ayS%oZ~7To>BJ-nHQAwEG(Jx0q&h0)j#I~y zdKGoxe~)8U7hVmiZeCk5ChIu&11L@lvYaQe>HvW#W>0t@Caq$t{I#B>w}a8e?`U!5 zh)ar|gPK{=(#5Vm!H7%+hVyjmu;tQit(H3_mLnxnku-WH^$=M;Uu-|Vxv@ER}*6sx-%S7WT*=w=ay7=J_4A1+!q1q8{fNG{KK z;2VFw{V{T<Okg~|cUG~YDa-wF)q-#0$$Zx*vtFLr)Q|RFn8dAuFNu}^K{mosHSDegsP_7EBX4Bo6TV@dan-P)?}4>7gt zGQi#&r0v+&8by+6{pDcP4_GssRt90Kj44*7-o0z8kwY(i$X(fYTRFd+;Zmzv;CLn93j$%x_E-NvnyXI%_@Z(3r5@PPv~n}VswCw z%gSKyPN4(uBAI(p`E&1(8j_d=1BkvTY z);}?}Tou7W8O}h3dbA3Z1Pn3WF$(RmBt1a}#^U9@6fDqWL?hPn&MkDZFNe3X#1$jn zQbFYwRTXd@&8N%Z+e3Xq7ZNJEO?kd0Gn*ZWRvZZUTu?|kK1Q<{CLX) ze(IrrI}9=An!aYavcUE|D)XjVx2xD+xWQssjL5?o@^N`uOwE(;9r$rSD_e2)^vViyMmr(KAiW((^5Cyke`Vr_MEgua6yNKS$wbneB%G zA#zNm;i-m35V4XZeTYCcVSRWz*n^7|dOML9oi%rPXz}s*G~6RlFZCre?A^qS-?+8g z`blp1#f9^DTY&m0MdcI7)m;NRlHAWT0^9wdaT_S-Dymz}}M@ zQt(BoRL=o2`~~-)a|a+WO{ekx*)wH%{=Yrb!qm{@|LmFCYfk&^NWMR!s{w!9vv%Yv zs*gj@H_J1#oTg+F&C`QPhe`W>QH^z%e@J(e>8FCXhHKG|)ptfSS2NY&yX4ui3i^K? zu7vR>p$Dr{(bLXD}mB@;i%BeBO zM}~f>7opSpal+FXdO+oH<BaqaK@9{Q~=t7 zZ<2z9OX!J?*z@p*lMC^Swxs8*q_#L?{BV{NId`@YrDRn3PQse8)}!_1*~V@H3^hQl zgRWj_W|p6j>7y404qBUegh&r(NKSN~?15y*tV9{9E|d>utmZ?lmw=_`uUP_!MUy_V*Q2C=)(aA@xSOx(h2$7slNW4FYEoxaKd`9d?nYA2JM zQ=mmUAQp0QdUU+dEu+y}D>vuthf&L=T~@VS&eKSZ!%@sus!5=HsWX!&S^#R(zUq;$ zV~lz08D0)a;xUxzgG1b_f05H$eZ)=1oM)S`w0`K5DH?K`kNzEOm76deh36x)VfTy! zR04C{h<*u}=?gtbgn^gD&Te8Y&^KIcKLN2Mz9?5q)1dL>35reGSx1%cWWHnC5hR>geZV>Tyns{=m z$}{`qpnIFpjm?Vr(ly@WtfA^|8bn#>w=9T_A^zuQ^3ofjL)Sz82G-p%DqdjYGNtWS zYh{+QP+hA#xOl~4?dsDTV9`0c{Sd!mO=<>}2geAkuU`Xa)kbFtC;;|9wfQTZi&o#R zKmwwGgNy(q_Edwv@fRG4a^|k$iG_bn&NBz6uIb%Jnd=~`LEdc>3QWUviZvGqNs|0p zyKiEb*``_|*A$wYlcU&ii!vPr5b5@!4Teo4EyaXPd21|!4U-Z}Dk+RET>7Px67*?2X!Z2u@6XcsK3y>dyh>CJ1sa>{ND3;%;-V%=&cJ_luz!)KZw zkZ^`?z4JsPNPo$5yiU7`716M+3-}XEaCPs&G)VK-Q65@Rnw1@ux3Y~;XMuC%)@n(- z^o)#=6~v#X#}80zk&D0^K#~4uB^0dCT-3e&n|IBq*5b;FFx`SI;}2B8!6*NH&?5;! zV{4g%IhPpXI*~p#y*QQXqtDP?+Zclc6@`;nq{YHzU>R$S0w3HZiKf1}oY0tOjcY;S+`{|1b zXan9-y*ywoIXC1ojgk9Ae^y)U)Fh_(Nd!nDb-!_RK2SnZYe~TF1sGA@)A!6eZ!a=- zuxSqy<2IVPPb}ZM;@mOr0F6`lKaxPmYl&Ua zIR+?(90hsBaDf%Y*s)p+c|TQw&~ccNUFgKlO6QeexWs&g;+>PxB}B_ z$dXyknU@T*e8rUM6GrJ5DrKfCo>gd6=g~ae>|PH&Ka0-vJg1TW5Lf^HY5oCC%ys3J zat2CwqeZtpWtqUhP;#p(ROZt&+xrOMv<##SXhkEl;gaTVK*3amK3~X6=gCOuY{+8h zmyE=S>k(FdAsg*FYt@s?fSUsx=TSYo+h+4XML1J?Hd3}qj_0zXsGICg2Q6I@>%p4`Ct)>-ZkCt*_8YlKs%2+*$~)_Dq?hT$Kf^Wshzgx zcNo;fD+*{besBtbwj;LJ6DXADQiqA^XU43~9#lRyDDsI1uobIp2YF{}z78#um|7J8 zZB#Y_F%M>we7C0zXENKcJEK|l0M)Sxp={tWxQ6jv?&bBf)Fn155y3OD$DzSxU0<1X z7H*Wu{G+5iwA$;0|9Ss!mv!oK+%WYg`f+Wq2y*9atZiAY5YGAR zbIh#3F5j6ed%aKHo?%|?87|}jLCY}lNMmO&pyqi)z47}_cN#U9 zUsGcn;YR#EWN$cEyoGk@p-{bHWZ~faINJeYk@aD8j`)*y#i-`*oZ4t3$##U$Vngip;T5oLrH~sa^qfp$&C6l;YsKLx3ME4uL+SUBlgEiuVeb}r-zVtSb}g0C z5X_mlstA|IasCroZ9Cy1$3iS$&x)>dSI!;bX?B0g8dKi2M_k7@;_6a~$XlY+uLhFH z5Mh(-a64dV5kW{p2oMw8jy)Eoe5)OH(1$}_#p78AyNd<-1;yu%QSoS1)*PdA3G^xs z46M#0(dQ^J1G&NvKeq83I_@W!C;mM`blIxhe7@6}&m|+*HX0sSoxX;J^c+}k{;hCO~q(me*UCKi=-goN4fR?9Zo={t{!Q<@5O+5$ zYBcuRF;p}Z1(1c})fC}4&Vvk8^fRdmWxM>;~u+=YER$l$t`#V{>< zd!U^xpkd(FTPxU-y10EeJWy9`{GXRo?;7!dQDbHe_(Nhg;7o)|>$y8Kx8tZl?O&S& zip}&65e#PN^XRh=$?UR2>5?9aO0dFC;3l)IodJ*eMOc>`gnQD6BQYQkdPi;{PGkgu zd-$X7mF_i8dgHzCzB%rFCrF|7C#&%IKUsxQeSZxOXygSYLnd=v_E_AHO2fYG)81$~ z2_JzV#wnD)-h5!)ISQm@iw$ix*%V9NOhuwL7lP=KI7!TEPt-sp26cZLQqNty>}ozm z&HV&!c;L^T6?oG707N;7V9Fg6HI9_wfxR$I^HGTz+K;0aWH58xwP#;+)nbkJX^sYz zgXzaog`M%dcBj$G=V34n!q)kPyh;le-N60Uh&A)w|2C`Fsy`Pf3%2iv14<9Q1n6lX zTU6G`x6P{da1a^y)xIf)O0_XbNq{cRUOPz&|)=hi`G-`y@`H>Wl*ck4jV!v z)*?$R1^2A-p}fm5SaH|o%c8rMC>== z>jHbBGK#=S&QQ?-MoVNmfK`Bbdo!Q|$!GZWu9lr8&Y z$dRd2mO0TW-^&3m!^{kOwT5NqtIr$R(J<0e_F48A7vQmwN z?#vOws(;=Hb_Hk=sF`l=&l>1H(l6Lg8EQ^driDLk^IpHqJCiGHuw*~i2_C)Y$~df^ zMucik#V>^#FyI1@EErFswehxbUOxeZY@n8zQ-n=uO{KVRN=>(BXNzhq9%%;&k#y6y zpa3cgac)K#_re7yxj(qQ1;jgFRv85gVhgS}#Bowy!Wxf{^yk&+zdFjoY+XGR5tQ0j z?BRcz&pzZhEMB;N)M9ZL&ANY#;v9WZa{syv@iTzQ+I>sudwgBACGjDUVyq07Bx68=`3Q9o)CEyu6> zV)$1ZD7W}LJY8G&ymc+`5Q-)wgnM#!AHq>}mvTSKQ8m`_g2gRAHr`P2lIp?tmT;Vv zeAt5TR_*=MOI`2xmgLIT&P5Q@vni?bcm?mX{VFl+{Um$Lmfdqrrj^t7KGQ|w^4*P$ zduP<(TCy_|o$@Wvoa6GT2Ec`X4_cwv+WM%N%abipl{Fe4i0qO9TT~$5iT*uuZMDY?b@gQQ;QOZES}Qou3(WlOa81#;$6 z0loVt=boyzon)BV%9h&u746@zhIzF&Nwz476HDX~2?tApVHoZ{s`}+~11s)5E35ndjFigx$ynYKOGVD6@axA1~b{k<5Ga;C!RHTj|=x(czFMu9c+(BE8Gvq6AvBk&{7fg;Sb&0S=_gx9&r;#om7dz z>avI$hSbdGsdikm`&qSZd<~H>$Kh6fSz6q1%#G$HK>G*h;8-DGH8p|UNAZ(C{^eL? zMV-{~-~~|7M?j@;6`BAj82x&wWiZx!fGAwnXI&lr*ZTMAnkMqk)1{p(@K+j~nk~Dj zG>8gn-gQKV>c(cAA3ykuJ{vvLh5iFGnc8cD@T>CrgRk=ULv0V4{x^cz-5FO-IkI3}nl@)6%Mp`wyJA^QZc zD6jn9*u6esITZIuxGGBO(J*7QXo3VSom43~VooU%QMTpO%=wD6sFrm;pAJi<1HshY z4kNKs+`EZRBrM6eS?R+A7;?IsL$l{W#t^nB0Lvy4DT}Kt%U~XB6r@jO@ScRTj=~_A zhSymblZsG0#AI6d8ZpsxRFufJKi@srh&hCxP?QT(Qy8k|hEvKD42wAV5cypY=Bn`dH#z`kSlBlL`XA@4}H++PHb>`8}Mx~Yp~ z074+4k)fzHZVd4%Vr}DQ2?}h4*pbFMqCmWT;#fXmC25N!I7=d_HElKlG?J7tibSf6 zgz~Zx9BJuEXn3J1xOF}_gMla+)qvNfpHL4~fotH1Vjx-kiWEyxE$d%6Ge{!#FnrZm8idc1>`6G2PA9KI zWF(aJ6?fG4aoIR&1AyD4TM{LX7#(jp}igMYt6zRA% zjohVntMfX{p~n;y#Cxw1Qaij>0L&RUlW?Lh{#>grQH!De^JXM+X~S&nF_KuZLhYMb zB@tG{*_Gk3_#J+|VtB6+4>~Y03kdSM<%}KhYRzB|7+>2!Ln<*R5=WaXiF5KvRPwX< z0nOUvN(Wujj44MnSwrd@zYV9Po%R)}T;na;%L+D=nv~Sv(Ub#N2eEio01TTrMHPF& zLG1Nep~1QcuMuwky7yW+#!1EV4CPe_DpTpQfTFPhT-U@Q*yjZ1F+%t3#AFO2VQG}7 zrXnN0kcP`L>_ZKZ38hXJr|E;%3dd=!Y$_e%1zfRO>F`dJ@FkBF$8ssmY~?hixrj@s z;r+?uz()@XA&K7GeWz3pz^Is zNDmUS5+@8)3--0f=p0wmvE!-c2IXoY|?Yh@4#c^5$TcV-`!+C+%uWY{Wz-!yE22CEA@Fz46Dv zT=v68JK?{|>-c*MHNbtYMpmL`gd$2s!r3Rr$PrD=qFgpqWS)~jtVCA({7~*5e)EGz z9?i=3Sp1H1Ecjpt06FZJK`cM)=!_e-L+i;wR`p3?Z;OSpoAG34 zNxS3pkX&`<(87P9t?-JO(s_i+agPV{(FT^h7`gX8&}os702{_^1Dafsu?FOnJckFz7~F$K7Ox}%$d5i9Zt z3u!~pMJ)i?eYlA@hGZtHc5oxAa722-p$Dl>kj=>zaJXb#bWblPJo3LIpZz_D$-7++ zlRX^JC!Dn!0JY1F`y zMuVOE!IQ>;=(t9vKK*>7e)5Wq^fDLWmO<{$m1sT7Yv6-B+Y;ZQ`khB z(Q{1JzgZ{}oXcP8;$`0V?YRig_n|nXBc;1m!@qqm010T1^?xIyo(JH%D|cf|->Lq( znfqwkWj6c7(!x)$czAk(wzsd^8EaQf_ez|5byv6hQgZCyKl#r6;~nGcn(JS7ZF_xH zTz2rRu=$>25Bo&wvXA<_9Upo{tB%bxJo9Wi-&TEW>*ArsIM(YZ^z{J8b350+TA!1b zziP{S2DF&+>FIi^N2?PJyH9K#+DGl)32VA^U03XRJzekJ9V3W;*EC-mwZ1BJGd#WT zgGC;~_2k-CW^LpmYNjs#J)e3#y>K?#12Qk0+Za{^_;&T#@NyXSO5b`mH@Vqt zMk*B;z_Du$i$5oJ!?HqIpPVg6rj18^3`ZSiY*mrEFc2Q^PT$rmi>e?i5KjIq?f0s#T1z4E}30S+s4rpvzbaaO#V<3Y!Y*|SvKnw?_EY?Nt& zp~s$H-1N!g;z!oiB{(EB*dYgmn&VyV#Ekv;+h^qO9p&<_2>d?qXQrI=S1`YEe;r23&1$`v$LEvE-KZGS%vN6q39W)ip(uoE{SMr~XJ%_1|>!Cdl+@ z*ygB{*FlG`*mieaSnI5C_zzRy3b5oYLhaB!0*158gv-5HWz2{mK&SZ3TA_P?3oZgI zRiPyO{aq@hB+y~Y3!T|vBT^Db0k9Ei38=8;CUTBzy-B}0S14+dSmA%J(0njMggCW# zkCMmsK&mRNv_Suh&qiLy!6?M~4L3{ds~&)OZ)pw66`o|txxWA><>cv?HqEg744}T^ zknI`0*8fIz4{k7vTZ(fA-+f-PXLi&UR+ghbz^4LpIJK z45S70&vz^h-p<7H0-i4?K&psy^mqq2=1N#C$Tzn~QM2_Y?J1UyJG! z+Kl4AI+TixebI>>tk4S$c>`f|r=6VKnyCyn6l`jVnN5Ilz~}8dKzl+KDSh*+sD@o% z;U_-g6IhlLY(wI6ujN2gunsdu=V=^?(DTAIFI>S-dsqMFjpgYj^4Z2We_q@VhQl3y zK6b26!#9W8(fE|ZFZDWeWCw%a-Qu&miQdXIUsTfH_rEM>cr3dK9(_mn)2&2(822f* zm`2e1LZ%P~xHftJG#TQ<(6X1T15(OLA{u(w$t(CI9g=x`XK8FHUcDMKx3HIF5_mDU zke6gSeKxnym#ia0c*0swM8!#+%|-1HX3-kI{|b_D#t;<>>z$!Ev{&2dY|zW zMk7T0&#Mk%u^1}(;OwGX(JAZq6kwO`l12tS4(OfSGCVkI^EDrJ>t@ioV!OGd@&=#E z?@Cpcw>MO|n(LD^!da5z{nBMTpYKWHOjmynSAV38v9r^5kgdRWDVKruHAu6XQfyqF zwFXSq5@jriEenabikUZE&0A+z6D@eL6g6z%=tkJFF}E{wc%c*}{bhT==CT?!|2KW2 z@=o6+Y>k~5!@I+uyBu-$MtdWMbVoIMvFcYfLzFS#eZdt_@d ze!}0xhUoxH+bP-Kpx=|3HDJ)@l%{xd$qtBa`a3wsEBS4P`44?*qx)a&{$eHtTR6Iammel|nsOc%6kP*1+7V23>DvtpNr9_MDL)IcmYFN{B!-Us;~XG^ zp*8kw(OgV;qUvAHD|VXAluui&ct4(7^tYEtf!HD*T0h<5I>u2H3rXc+ppgD*t-p{y z>{~xCd{8%I+(149xUV68n2>(@2%h}x%BrsSne}TRckw>4Vl`$5iTwfk&*PEG1o3Dj zG-7JeRjmOA@n{3Q@S05ZM(HC&G=Nag&D&ZdlD?;^)kjP`l$2+J@yL`3EO*nQ$0E5h zJxH@24S{5Wp|=9{j-W>oylD9Iuv$M0j91QpH=?4(Cl^EBC%P5wu%3Ju!?H~zob^J) z-vyAxFcKK?^qtj2|9fzOJRj0h1heShd<>I&2p#8=Z9j~g=iuH@1{iB5ANK)HE9Flt z#-W#oqv`KGEZ?T2ikMI~o`w^a*`C^W@I1>YJhhyL%Z%oIB#ti9%*_mBx840phg9cm z1KO$bAk{feQT2!n*h2&fQp6FB(E#|&8Umlk#U;NPecr_3umKx>IyM>9 zeqkOtjwt_xMA{9I0BueckMj&8g)^FA9Z!7i8Lf@w^E%RaoOx9p7wm>!wyi!|Mza7W zn2$o~bTGXxUG&xHkW2kHc-PZeeesHXhR-?|tdwr;(ZX5L*FW}b$sc}WIIMr#u4~Au z>OUk58LH}iDhJ8cG>uj(k&Fjw)p_7O>i@F_|9p29O(no=H%&hd+@EV#x(3o z>sv>Ci?h2$_{9azt9S2NreRxzU7@a6zyf}MkQesP zjblFt7@QYGyeHv%)%l-tRRwgk=9_h2Ix#vWVihBsDErk*PJOL3HafknG{d?-lyg*1 zA2HlS+pqp%VJpq#ek|pc6>JUPGoJP&@fRZokVlO`6w0>inR{DmE)6f2TgN#Phfv>X z+sN+}N3Sc#P}gUA?Ce~?yBvPlf^!@TY^VF&&cmkrT`LJRN|2jSU6_nxA9rX3UR;dL zpI8r!7j7S(Vat?>Dv*dzF&Cor}hwSk!@wi0F1|)bn^W=U!HHm7dC$5s|&w7ER3jV(u`^uoWmZ)3Yhu|*3H8=!!_rZe& zm%$-Of(-5wbb=+gy9Rf6XCMQ?Ay}|L&`0jA_vL-}*E>~bcTexN*Xiy$)n}@@d$ki- zg;p%DqL#rOUb3`~;8Af|oiTmy11xVe`n{}AYkC9;$!!bY#o8;1mQ#^9%whk;yXTRo zh4Y!{j`lk2J%#)8z;hLSH_1CG@)mLp!U1XI9SmG@0IFo4Lb) z;qe>QmGLCm=UTRfok&++vK#fWW|p<)7EYp{hsmorF{Bj^Nol>dn5lT@NbK0?0kno@ zshILwE#>KbfNamEedsQ$1y=~8Z|`^%7Gbb3ES&C=%?l@3ix|hO8X{m zg7D3GYhOLQ{HkTsH0WP zE#>?QQ0uXUkXEZ(kpx_7m^lRQx~Kj#dynDcAAV^@ z2nScu3fWKu5Q9}bwujw1MS7p$d**&{jXD2{$->c>5>R?^oc;zh483Y3E3vUe1vJ24 ze*BiaBeefr;U;HEN_c;BT8`<<(K6c8rzn##rU&FDOvzk)WF5k2ER{9${*Qon0VSiH zyyZOQaQ8nIt`CQO6Xw`eH@7-AibKR+3R}$!sKXgyni}B#5FppJ%zR+m^6rKY ztGC1d!_1L``?*+j{53XqG@Zvvm{LQ}1R|^s=yHQ`!5Wjb+Ec@rY!-G>4 z{JIN_${HAhL_J}nE(r;_wGUOvVRv@4EBDbeB<}f~Mw%p3v=1Kslp{eBOzR}6?u7%l z^q#F^p)QW?s!)va@!TjrJn}Nz!S}vK~%_9D`>pZQ~!@s0q(_2F!pG8wY6lr+oLbXly zf0lCen^9dzn^Szene2SQX=$cIp|IgLD2NfkGYIra;EwOrjddgVY!;i(_0EzajFNl{ z%sf61$E4wxN~k<2&V%hm(CkZ5(|3%0=k*oFy^h^1@_lu~Ysg#V+li}l#42USv1?mM zqY%dz_4kneiOu$A$g5=fgk%a|q0ZBUaEssp9^h-Achh~P$?C#^8;NRI{T(xY{o>w6 zXdYU3XQIXE*%0;)Yo9hXx5c&%!GMH(2dfY<0wkm<8nfsNr?TnDIJIJ*C~bPU+hFEJ(H$kes@Padbg@$ zeNJqi0T$xDw=qB!xXVyCV+rE0Fj*LV1eDj@ip)AwxUY!9ALOJ0H@iIWecp;Xx+>$< zdUqspBrz+WxPHez6V||YcBo5+l&7iD?j25GSksI}fTTkNyor3#+x6|{+Gg}|F)Uwy z%aSn4=Q{s3fob}!{lfu_o;*WzHa(8Kdyal88QjRL3S7V2R%yQp%0&76ab6(6b$%6J zL5>l^dL96r|&uT+apf2N^$oa&hmeZ_d#elh)> zB6w~T<8CBt#LNKu#-UVgm>_&|bP&|*jPTc8MC(%2UcuVMZvk6fIe{SS*^}?L8pt5m z2XHZbtQwD~=AGv>l+5^Pa`C&drx#T&J7y|o{-RG+tI_wC6PKqH_3sS8B#!1p5oDsk zNj?KOVQq6frzN-Dx*@D7^9a(3i+I1K)Tzc+my5)bXP1j!u$%ViU16t2%Ogx4W^Eq#6tZ zaN`xQWhTFb0r=eFmon+)4npXLFVNO$QROt_y;2!fKD<@HyEtCLerdlW0*@v6{mxj| ztX=h6#H#chgKQkLURUW`%e@UxZz5D}u}Z92>Tl&-F1WBaQoAO^?mcd`V{Qjn2+tDR z5Sidpd1LDHMrCZvYU7!Dth{rrXtES{LA)}_WTg*Qq02Y)vXp@)u)Cko`-(rb% zS+tQWbltLE#VSp4j(jI-EO2N5`RX~{GQ zEtB7i7tpmSOHV%E01ekA^NPZgy(nQzhOQTsTg#Gaq8{5QV>`YJ)j;;!_%GSE+ zL|Pn`;Pfs7X+r;^Kth@N(MCe7vfN%eXy*U=p;)yM`O@!s6~fL1;xT}Z6oh#sU=h|( zuD_6fDd-fCH}#$ov6O_;B>w^e#&SCT8~gtm+9bN%2h43xe-0;C2xI>9=)c6^|EV2K zXktEs8{0GD-}VzqH$aOGQgoaOsejB1rQZF#f)va0t_{C7AJfbi*vO*<9SAUdW*iZz zK5Oa*W^StY$q_JCe629Rfg~>5pyV@*P3;|DuMh8wQ3unXSeTV(!}(RPp@ZnQ1S-dp zUcMo3W-(1e0@n|@84;v1^tcK*V}2g*iaw^dGJx-O+K|ShV(nketD?u&{YK=#AXT!_ zgvN$0jogfkq~y9e^RkUiY-WD0;TtIi&dNAY`co70o{GC zkgv%j>t5CdJRUq*D)4j04eyGl1Rdl|Iqi3A|JZw&{6;*D#z$Z$IX!BwaI} z_EH~t&jy;Lnn5N75#tZ$hyx+fiKlYy_Lt?xlIqDoflg_!_<>HRB_Jg2vWYqdh6Nj_ z7TfSsG6ssb8kq=`jPLZ8dc{^?ChP zN2Y5W?!G*jMCzT}?eFs}C*t1i{q%b&KQfgtfW;u)~QuAD>FezK^-cFUe z*+4?p>8l_gfvL%F9XEMdK;!O8&i!?Qn|bT*yaX?GAC6rYM85B*&z)1cipMePq87~s*#-3{qBWn7ZQP)sgN@d( zQ`)1Ztp|+G2oxAxJT;DU+yy>m6Te7q$bCL_9rq;#Z_9F_+L2>P$6P|(T2n(@N<-8! zv0g(QyIr}qryv!8u~!kMrEBosh$Q8`R%eA9q<>|M%pu7zUbvm)%fvFB@-hUP637fu zLt7^4#h!!NAlZ4ri?)vNE!R5Ffq&}v3;3O3$t-m9!=>f;FzuSBtFV^Tt2JXVaGIO*s zfnCDz2F!RBdSm7-hwXSsiO8+w(9qLgX)+$@O|0ZNf6I2W6lMGjodpP;)7FNlO-S1A zXU7%y1x0e^Mf6`S`$6tGIu-ixUXWzc=L>D;Mt+_3{^XYSR*3AZa%~=s)U-JX6g0AK z(@(g(O}@N^RC2r(@z($TCwOBkqU-5qgZwo4G~jH`1^hHd&F4@%d}GCrA);9htmGZ& z{}7+&npk%xIbL2!7j55oToQjXW^e5u`8xP{QM@AmPRIO)lHL2Gv);2>a@f`{%TAtU~Eiorv9U#I^ zE?z|=l~#fpCoEXK-l%*gRP&FRtGm;cHl$!-4L%R`%1b}A6A@(}y5DH-y8E*Jm>gD4 z>;@c(%>V%I7L>)kj_)+$;%UaYAAh<$-5hH?U2JIt8~`SSaB~&Y!yez7Zzp03wEmE= z(|!tSxs{7udOjI5>pj@;iqp#zmOrXB=SgTwBw62fZCuNil$cf0*uKA$(J37jkSxxw zNoZ+;%CY@4^**l9rl214R)%560@=w%x9lsrWfBhIh0ukRZmu$QNnMppn7Q-- zVdH@Mz{rOhr(f{w>VYDA8LHm_zml)WhTy>ospc<4_AHq8lI_7jl{$X>6LE8N*4W|^ z$6dtRsah!{*7Y)HWIk;pw=d?!=2i{_avlx#`v=>hkNbkd=wr)Hd!JBi@U+3_#+FOJ zXR21(O5k2Im-to*kx&wOuSY|-c^I79^DtWJI$r9lCOc6~Pqs@$J?Q3AncG|;zSuG7 z6atWtahBDV&*+1>qrJ2I)OJ~pUhoX$_K}ZKkZ@kPHCB{C;ip;Z>>`ZsgtBd2MkP+8WiYmHxK zHzDY%43)9af)AL{xW^Q6Gi*zmpOR=jI|8oK2UwjzN2PLROn0o?PW2bPP>zWf`%NA{ zin#T%p;S$dItB4D{hIBcb`%z;%x+LfF@Iyez*eK%Y)7ElyKFp-R^ZiO@~wT{mr`41 zRaHSb&Nmf|!GB+1RU@;PP^!F(lPxzPPPW*%hySIB4I*TU_;9XGB~olZN0^bSJ0{}B zN=JO>J}+-Nkj7P&iiyQT&i(3xs&2i#_&CSF1`)s;G#yYSCX^?!xhddrjM42_1=E>c z%nAzOd2R4!rYe3IRt>Bcnwu~Dyb!T_$w0bFK>`Qnq$0GvTMkOU)yeo_BEhpnmsR^B zH=j?uP_@=pO~{v%%ECr`(XjH}g3OeUJ72}TUgla&X2ABr1WV=`L0Ne*;`rbiy>~G) zc2!@s3Lwgqc>N`@CRm24@cXHGaQ)UrJrt@4O`J5d0F-CN>HFJwVmj-n+$MSEG1w@M z*2;sa>?@c}e?Bfo$LEB>K}yq z;{o$%wWXXwJ8geWCBNBiC@_g#A)3lyhGc}=Qg1C#S6lc=q;VI!ZsB;+fg8)&T zsN8xmJ1QY%@@$xf!Ir@;HS?hIwY|K-FOy0ZJ14z&f;kdx-1b5D-p!61@B{%AuXI2R z194%fEZhQYVn=lsXexd%meVa=haV_KO~#G089z|t81D;iJ#2Elek=jVn7byRRgEU# z2?!I}af4FoMa<0eImde5D+bjY*ftjy`C1F$PWaL_FicGe<$=lEexljjR?YCEhC~?0 zuFNf>;|BWZ2S-nbM5Gsz06$nvHV7E>!Ay`^U4!Et5w_i{D)y%(A{3CV&OjS%CCd^P znORKmgJts9;z#%W?K9cfY*Eud^K&5ha)o4rW4Wsd&2~iT&9eQ*mJ9L7v&tcs(dAbK z4ZgbR_n99v2)t!fSBLTk`w;cnxME5j*Ea+1nG&yQ0bXp_pDYSb6(z+ClQ6+0J zRsr3%%RGb=-p*8Y@qnzl zuf%|7#YCk{{oL7Aj+m9m(p5vEdZI#Z=&NmHzq-gk86(PI)Q*&jMXCS8h4$^7m;VH* zEB}T;@F!id>MR0IR=d$6l;i_DY*x|o5A{t0&0*xAFn1Wcr08$> z#nUl-ud;*Ak%}WmHC2C$8E-!sp(MYxsbu4I)do>qDmPEQ_uP zhn=hm#t@6SQulZsiW;+ViO)D4)qk-mn*!NSXL$ z#o5eiRN^>0r4>(Beq0%73=KLaR8zOQH25c3EsA-}Eqzk!%l2h?F?~?5neqA&PWs72_V|0mmU+Cq2IH@TAjP6wyi{#e;RibgN!1K*R140CjN zVD>W>(mn6O^T0bzVE~zfO98$s3oKVR?}|F{7oe^?geswQkJ+FooOOK6Z3yJL2`h%u z(5BfI22ZOI?YvmI~nxJT1z}RxD(+ zs5SYx$5HVxf){Brs` zm#Te;OTKup%cL z*6&m?;AU0a(9!F^N7*D{zG9y2wb51Dp9(ggQr8MzNhu;%C6Ah=pZfKB>P!3ekE0U} zyAl%%4z!q+)<&lvV6u48-DJDvMb61KqpGSXBsp=6H=(Py1epHd)2nirn?rY=tR0s) z-m4#tS1*+|)6CI0y9uJ4h_=wkP-C@Ix?fR~5uClq_ZhR~lSe;D{z$LgCos|adE}?3 z&a#=LsNZlq?f@`vkenhSXP6fEqro<4%X5A(_Kw?@iU?s>72KzjK#MnF&2B3diI>3J zgEAtZ%6Oru)Z;p)qtDk)T%wC#LyYu;>a)(AD=z?1p2#!uty;185>0@VEw3#GLa)Ys zPEvHX2miJ~N)B99PDy0BkZ$IfTB((6$Kn~61;v$&W^i*=Pt06O&3)MKvETrrCoArH?dxv9jw(*^YDCG03Ek(-xeU(D!y{De-sH z7KFFmym18G2$3GrwbPS74Sb75-^$B&F1EHH(o+aJeg(Uy;R7{p{b?1x*Za>Oui+mA z7H%wJ6Gigr;{u!Y%Id`vvB}+nTdcIC^Wl*)qN58obn>Lq6MpkhAoBD)-m+(9@0p!Z zjaJ@g;xeSy)#wfr@F_1#q%ez#S{bwnazsUiuoKN9x)(^RMsnlk8HNjvJSc z;1DE6&O-DSi=xvr4$}1WnzgJ=fXb?Ek!6oXbNf_YF(o?g2EXV^%pMlLVJ#f}7&`i~ zcXa!~>#7oI*(eREpAFF~Cy<}V%zC4dVwrK^`qUYBY*tl-rtc+-nn1b*Cmr-0qaANp zrL526r5M(n0XhTf`IC$gX^#Tjtnkn5k?uBKIoT>3?Ux!Y!P-|nUYLUO?cd7A(k|D^ z+A~rqrq7X$u`TLvYk~Zg#9Pv2t=TO*RH0Ws#-fI5^_aESnE%Xvbd?9oI>W=kEg>}X zSOKWv!J$b&jb1LCp>?%;Y?bw9FGP5V6FtZ$sb4mBuz7__6WUtzD%Rh(4G_*t-wA^~ zfxEt$WR54uolr`W14jwM;H9V(_1tM*vwSdmbx(R8TbMN0p7Jq|3@gL7`8B#WHyeNZ zzE)5ZLhPR6Y`l>W^ z0{#s8$eRQm-|S|%Nv>_pzy_2EsYp&!%vLUaa>E@+Jx-IJ>%s>j-|LykH3MB;%{asI zOo@K#vArcP-M7QLdRUriu0%%W9>aKk=q6Ub-mZ5$>Yv~f^8)76TJdlDv)`k7>VllJ z?0JT>;zG;~nGdO|56KC#M2ZJFU=CHtKO%96buXl~$k1FM>I^s#umqSW`9gXE=*{#gO zJ3qpxoQO0+;x=S;5c2L&=FdXiHCeAHev}Tyl z-vbo|^xXTt4ygp9X5LUHmMrjD5js>YB5MF4rGSN?x)$YZ-)dPQiLZdVP(E}dHp!GG zYm0p7m?&yZuAipBp@)#Y7P(?$6p^7Cr2~a(sBM!6@1)lcm6inZ{x>12=wvR!w_}0!w}1ey)O}arx6gB2nclwmKW39k1o2mnsAjw9d(52FKH8{_c#{pd%B=z#&R$?wvw6{w$oe@B3W z!+ehWd(lHmzyK<8@esN~F*P_i6P{8FTw?49Rv_VXn3BQ3<4-1OnO6(|D;_$$sF$~AtMn0Rs`sK z2sjuZh0qiQ>HU*#XF-rd0MhVS5XC=9J|rdtAd9g43o;V|kb+l);D-Wa;e{avp#W*b zk1qgeH~=CxO%-^AS8xPyC~(i4xZ!Bv{_gtvZ=#p1*Nr&qTi1z`XN^1rjqaBwgFw)8pI3)v0>h{9_?Xu<(Z))Z}zukIi1Awz~vD9>TarNLdcXfrFfuGy*{ML~n p(1IWq5db*^IR{8-1V9-<#R+m10iedFdG3~k=iXuV2Rzs0{{X4#_6z_3 delta 41882 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 G -#if _GRDK_VER < 0x4A6110CC /* GDK Edition 201100 */ -#error This sample requires the November 2020 GDK or later +#if _GRDK_VER < 0x55F00C58 /* GDK Edition 220300 */ +#error This sample requires the March 2022 GDK or later #endif #ifdef _GAMING_XBOX_SCARLETT diff --git a/Samples/Live/Fundamentals_Desktop/Fundamentals_Desktop.vcxproj b/Samples/Live/Fundamentals_Desktop/Fundamentals_Desktop.vcxproj index abfadbb..a7b7096 100644 --- a/Samples/Live/Fundamentals_Desktop/Fundamentals_Desktop.vcxproj +++ b/Samples/Live/Fundamentals_Desktop/Fundamentals_Desktop.vcxproj @@ -213,7 +213,7 @@ xcopy /Y /I /E "..\..\..\Media\Fonts\*.spritefont" "$(TargetDir)" - + diff --git a/Samples/Live/Fundamentals_Desktop/Fundamentals_Desktop.vcxproj.filters b/Samples/Live/Fundamentals_Desktop/Fundamentals_Desktop.vcxproj.filters index d366335..d0091f9 100644 --- a/Samples/Live/Fundamentals_Desktop/Fundamentals_Desktop.vcxproj.filters +++ b/Samples/Live/Fundamentals_Desktop/Fundamentals_Desktop.vcxproj.filters @@ -1,4 +1,4 @@ - + @@ -81,7 +81,7 @@ - + diff --git a/Samples/Live/Fundamentals_Desktop/Main.cpp b/Samples/Live/Fundamentals_Desktop/Main.cpp index 3568389..e4057f2 100644 --- a/Samples/Live/Fundamentals_Desktop/Main.cpp +++ b/Samples/Live/Fundamentals_Desktop/Main.cpp @@ -294,7 +294,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) } else { - SetWindowLongPtr(hWnd, GWL_STYLE, 0); + SetWindowLongPtr(hWnd, GWL_STYLE, WS_POPUP); SetWindowLongPtr(hWnd, GWL_EXSTYLE, WS_EX_TOPMOST); SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); diff --git a/Samples/Live/Fundamentals_Desktop/MicrosoftGame.Config b/Samples/Live/Fundamentals_Desktop/MicrosoftGameConfig.mgc similarity index 93% rename from Samples/Live/Fundamentals_Desktop/MicrosoftGame.Config rename to Samples/Live/Fundamentals_Desktop/MicrosoftGameConfig.mgc index 9beb526..b828fbd 100644 --- a/Samples/Live/Fundamentals_Desktop/MicrosoftGame.Config +++ b/Samples/Live/Fundamentals_Desktop/MicrosoftGameConfig.mgc @@ -1,5 +1,5 @@ - + 7325F784 + 00000000442675A9 %{wv&$0vDL9{TlYWb+=uVIM%AoUbFDG!vxWO# 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 M76_yr01hhqA3-jaM*si- delta 21133 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 -#if _GRDK_VER < 0x47BB2070 /* GDK Edition 191102 */ -#error This sample requires the November 2020 GDK QFE2 or later +#if _GRDK_VER < 0x55F00C58 /* GDK Edition 220300 */ +#error This sample requires the March 2022 GDK or later #endif #include diff --git a/Samples/Live/InGameStore/InGameStore.cpp b/Samples/Live/InGameStore/InGameStore.cpp index b62c066..cf560b7 100644 --- a/Samples/Live/InGameStore/InGameStore.cpp +++ b/Samples/Live/InGameStore/InGameStore.cpp @@ -677,7 +677,8 @@ void Sample::QueryCatalog() XStoreProductKind::Consumable | XStoreProductKind::Durable | XStoreProductKind::Game | - XStoreProductKind::UnmanagedConsumable; + XStoreProductKind::UnmanagedConsumable | + XStoreProductKind::Pass; HRESULT hr = XStoreQueryAssociatedProductsAsync( m_xStoreContext, @@ -762,7 +763,8 @@ void Sample::QueryCollections() XStoreProductKind::Consumable | XStoreProductKind::Durable | XStoreProductKind::Game | - XStoreProductKind::UnmanagedConsumable; + XStoreProductKind::UnmanagedConsumable | + XStoreProductKind::Pass; HRESULT hr = XStoreQueryEntitledProductsAsync( m_xStoreContext, diff --git a/Samples/Live/InGameStore/InGameStore.vcxproj b/Samples/Live/InGameStore/InGameStore.vcxproj index 1b4bbba..0844137 100644 --- a/Samples/Live/InGameStore/InGameStore.vcxproj +++ b/Samples/Live/InGameStore/InGameStore.vcxproj @@ -1,4 +1,4 @@ - + @@ -627,7 +627,7 @@ - + Designer diff --git a/Samples/Live/InGameStore/InGameStore.vcxproj.filters b/Samples/Live/InGameStore/InGameStore.vcxproj.filters index 856c7b1..a06cacf 100644 --- a/Samples/Live/InGameStore/InGameStore.vcxproj.filters +++ b/Samples/Live/InGameStore/InGameStore.vcxproj.filters @@ -1,4 +1,4 @@ - + @@ -103,7 +103,7 @@ - + diff --git a/Samples/Live/InGameStore/Main.cpp b/Samples/Live/InGameStore/Main.cpp index 280e2eb..c0aeb2c 100644 --- a/Samples/Live/InGameStore/Main.cpp +++ b/Samples/Live/InGameStore/Main.cpp @@ -401,7 +401,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) } else { - SetWindowLongPtr(hWnd, GWL_STYLE, 0); + SetWindowLongPtr(hWnd, GWL_STYLE, WS_POPUP); SetWindowLongPtr(hWnd, GWL_EXSTYLE, WS_EX_TOPMOST); SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); diff --git a/Samples/Live/InGameStore/MicrosoftGame.Config b/Samples/Live/InGameStore/MicrosoftGameConfig.mgc similarity index 94% rename from Samples/Live/InGameStore/MicrosoftGame.Config rename to Samples/Live/InGameStore/MicrosoftGameConfig.mgc index 6738ee6..e7267a4 100644 --- a/Samples/Live/InGameStore/MicrosoftGame.Config +++ b/Samples/Live/InGameStore/MicrosoftGameConfig.mgc @@ -1,5 +1,5 @@ - + 62ab3c24 + 000000004C2690C8 9NTL0QDWZ4FS diff --git a/Samples/Live/InGameStore/ReadMe.docx b/Samples/Live/InGameStore/ReadMe.docx index 25695170a3b186e5e9f6b0586408be6b9253039d..2aa63e5c96737f7bdedcda7870f0a56deef3d708 100644 GIT binary patch delta 29780 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 diff --git a/Samples/Live/InGameStore/pch.h b/Samples/Live/InGameStore/pch.h index 413a993..bfa70fc 100644 --- a/Samples/Live/InGameStore/pch.h +++ b/Samples/Live/InGameStore/pch.h @@ -41,8 +41,8 @@ #include -#if _GRDK_VER < 0x4A610D2B /* GXDK Edition 200600 */ -#error This sample requires the June 2020 GDK or later +#if _GRDK_VER < 0x55F00C58 /* GDK Edition 220300 */ +#error This sample requires the March 2022 GDK or later #endif #ifdef _GAMING_XBOX_SCARLETT diff --git a/Samples/Live/LeaderboardsEventBased/Assets/SampleUI.csv b/Samples/Live/LeaderboardsEventBased/Assets/SampleUI.csv index 68a92f2..14e55d2 100644 --- a/Samples/Live/LeaderboardsEventBased/Assets/SampleUI.csv +++ b/Samples/Live/LeaderboardsEventBased/Assets/SampleUI.csv @@ -1,6 +1,6 @@ #ITEM,ID,X,Y,DX,DY,PARAMETERS CUSTOM_OVERLAY,2000,0,0,1920,1080 -LEGEND,2001,35,830,625,350,[DPad] or Up/Down Arrows - Select a scenario|[A] or Enter - Run the query|[X] or 'E' - Send game play event|[Y] or 'T' - Toggle Leaderboard type|[LB][RB] or Left/Right Arrows - Leaderboard / Stats Query|[View] or Esc Exit,SMALL +LEGEND,2001,35,830,625,350,[DPad] or UP/DOWN Arrows - Select a scenario|[A] or ENTER - Run the query|[X] or SPACE - Send game play event|[Y] or F1 - Toggle Leaderboard type|[LB][RB] or F2 - Leaderboard / Stats Query|[View] or ESC - Exit,SMALL # Console Labels LABEL,2002,525,150,300,50,Log,LEFT;SMALL diff --git a/Samples/Live/LeaderboardsEventBased/LeaderboardsEventBased.cpp b/Samples/Live/LeaderboardsEventBased/LeaderboardsEventBased.cpp index 7a1c036..2047a3d 100644 --- a/Samples/Live/LeaderboardsEventBased/LeaderboardsEventBased.cpp +++ b/Samples/Live/LeaderboardsEventBased/LeaderboardsEventBased.cpp @@ -31,6 +31,13 @@ void Sample::QueryLeaderboards( { XblSocialGroupType queryGroupType = isGlobalLeaderboard ? XblSocialGroupType::None : XblSocialGroupType::People; + if (!m_liveResources->GetLiveContext()) + { + m_resultConsole->Clear(); + m_resultConsole->WriteLine(L"No user is signed in."); + return; + } + m_logConsole->Clear(); m_logConsole->Format(L"** Query Leaderboard: %S / Stat: %S / Global: %lu\n", leaderboardName.c_str(), statName.c_str(), isGlobalLeaderboard); @@ -135,47 +142,6 @@ void Sample::ProcessLeaderboardResults(XAsyncBlock *async) } } - -void Sample::RenderLeaderboardsResults(LeaderboardsQueryContext *ctx) -{ - std::stringstream ss; - - if (ctx->page == 0) - { - // render header columns - ss << std::setw(8) << "Rank"; ss << std::setw(0) << " |"; - ss << std::setw(16) << "Gamer Tag"; - - for (size_t i = 0; i < ctx->result->columnsCount; i++) - { - ss << std::setw(0) << " |"; - ss << std::setw(i == 0 ? 32 : 18) << ctx->result->columns[i].statName; - } - ss << std::endl; - } - - // render all rows - for (size_t i = 0; i < ctx->result->rowsCount; i++) - { - ss << std::setw(8) << ctx->result->rows[i].rank; ss << std::setw(0) << " |"; - ss << std::setw(16) << ctx->result->rows[i].gamertag; - for (size_t j = 0; j < ctx->result->rows[i].columnValuesCount; j++) - { - ss << std::setw(0) << " |"; - ss << std::setw(j == 0 ? 32 : 18) << ctx->result->rows[i].columnValues[j]; - } - - if (i + 1 < ctx->result->rowsCount) { ss << std::endl; } - } - - if(ctx->page == 0) - { - m_resultConsole->Clear(); - } - - m_resultConsole->WriteLine(DX::Utf8ToWide(ss.str()).c_str()); -} - // Queries an individual statistic for the current user void Sample::QueryStatistics(const std::string &statName) { @@ -206,7 +172,7 @@ void Sample::QueryStatistics(const std::string &statName) hr = XblUserStatisticsGetSingleUserStatisticResultSize(async, &size); if (SUCCEEDED(hr)) { - buffer.reserve(size); + buffer.resize(size); hr = XblUserStatisticsGetSingleUserStatisticResult(async, size, buffer.data(), &result, &size); if (SUCCEEDED(hr)) { @@ -239,24 +205,6 @@ void Sample::QueryStatistics(const std::string &statName) } } -void Sample::RenderStatisticsResult(XblUserStatisticsResult *res) -{ - std::stringstream ss; - - for (uint32_t i = 0; i < res->serviceConfigStatisticsCount; i++) - { - for (uint32_t j = 0; j < res->serviceConfigStatistics[i].statisticsCount; j++) - { - ss << std::setw(20) << res->serviceConfigStatistics[i].statistics[j].statisticName; - ss << std::setw(12) << res->serviceConfigStatistics[i].statistics[j].statisticType; - ss << std::setw(12) << res->serviceConfigStatistics[i].statistics[j].value; - ss << std::endl; - } - } - - m_resultConsole->Clear(); - m_resultConsole->WriteLine(DX::Utf8ToWide(ss.str()).c_str()); -} // Simulate a play session void Sample::PlayGame() @@ -276,100 +224,7 @@ void Sample::PlayGame() } } -void Sample::SetupUI() -{ - using namespace ATG; - - wchar_t result[MAX_PATH]; - DX::FindMediaFile(result, MAX_PATH, L".\\Assets\\SampleUI.csv"); - m_ui->LoadLayout(result, L".\\Assets\\"); - - s_mainPanel = m_ui->FindPanel(c_sampleUIPanel); - - s_labels.emplace(c_pageTitleText, m_ui->FindControl(c_sampleUIPanel, c_pageTitleLabel)); - s_labels.emplace(c_pageDescText, m_ui->FindControl(c_sampleUIPanel, c_modeLabel)); - s_labels.emplace(c_leaderboardType, m_ui->FindControl(c_sampleUIPanel, c_leaderboardType)); - - auto statsMaze = m_ui->FindControl

+)xk$^?&R3Pyg+r#sK941xa9>n4?OeAVu^?s3Ryy2^|w9VSK-%K_QGmvKUvM ziJ@NPsAXdi8#*2e*W~_C?hH!91Ox%h>;O?_yFfBjsR@V;qx=DC+5{wn{si^d6eM@= z%*+(TLiOL&AOCNh_n+~Rpgx#_z|{W^vHvl+Vn+Qi1wBKjMA4anB+#FtOwB;d)c;y> z|1n3al0;>j-Fy7&)rXFTHhsS?{~?I~5j|(m_qpcFrequency.QuadPart); - uint32_t lastFrameCount = m_frameCount; + const uint32_t lastFrameCount = m_frameCount; if (m_isFixedTimeStep) { diff --git a/Samples/Graphics/ExecuteIndirect/pch.h b/Samples/Graphics/ExecuteIndirect/pch.h index 5810e25..478f4e1 100644 --- a/Samples/Graphics/ExecuteIndirect/pch.h +++ b/Samples/Graphics/ExecuteIndirect/pch.h @@ -37,8 +37,8 @@ #include -#if _GXDK_VER < 0x4A610D2B /* GXDK Edition 200600 */ -#error This sample requires the June 2020 GDK or later +#if _GXDK_VER < 0x55F007B0 /* GDK Edition 211000 */ +#error This sample requires the October 2021 GDK or later #endif #ifdef _GAMING_XBOX_SCARLETT @@ -56,40 +56,48 @@ #include #include +#include #include +#include #include +#include +#include +#include #include +#include #include #include #include +#include +#include +#include -#include -#include #include + #include #include +#include "DescriptorHeap.h" #include "GamePad.h" #include "GraphicsMemory.h" #include "RenderTargetState.h" - -#include "ControllerFont.h" -#include "DescriptorHeap.h" -#include "PerformanceTimersXbox.h" -#include "ReadData.h" #include "ResourceUploadBatch.h" #include "SpriteBatch.h" #include "SpriteFont.h" +#include "ControllerFont.h" +#include "PerformanceTimersXbox.h" +#include "ReadData.h" + namespace DX { // Helper class for COM exceptions class com_exception : public std::exception { public: - com_exception(HRESULT hr) : result(hr) {} + com_exception(HRESULT hr) noexcept : result(hr) {} - virtual const char* what() const override + const char* what() const override { static char s_str[64] = {}; sprintf_s(s_str, "Failure with HRESULT of %08X", static_cast(result)); diff --git a/Samples/Graphics/FastBlockCompress/DeviceResources.cpp b/Samples/Graphics/FastBlockCompress/DeviceResources.cpp index d76fd63..fa67816 100644 --- a/Samples/Graphics/FastBlockCompress/DeviceResources.cpp +++ b/Samples/Graphics/FastBlockCompress/DeviceResources.cpp @@ -31,14 +31,13 @@ DeviceResources::DeviceResources( m_rtvDescriptorSize(0), m_screenViewport{}, m_scissorRect{}, - m_backBufferFormat((flags & c_EnableHDR) ? DXGI_FORMAT_R10G10B10A2_UNORM : backBufferFormat), + m_backBufferFormat(backBufferFormat), m_depthBufferFormat(depthBufferFormat), m_backBufferCount(backBufferCount), m_window(nullptr), m_d3dFeatureLevel(D3D_FEATURE_LEVEL_12_0), m_outputSize{0, 0, 1920, 1080}, - m_options(flags), - m_gameDVRFormat((flags & c_EnableHDR) ? backBufferFormat : DXGI_FORMAT_UNKNOWN) + m_options(flags) { if (backBufferCount < 2 || backBufferCount > MAX_BACK_BUFFER_COUNT) { @@ -55,12 +54,16 @@ DeviceResources::~DeviceResources() // Ensure we present a blank screen before cleaning up resources. if (m_commandQueue) { - (void)m_commandQueue->PresentX(0, nullptr, nullptr); + std::ignore = m_commandQueue->PresentX(0, nullptr, nullptr); } } // Configures the Direct3D device, and stores handles to it and the device context. +#ifdef _GAMING_XBOX_SCARLETT +void DeviceResources::CreateDeviceResources(D3D12XBOX_CREATE_DEVICE_FLAGS createDeviceFlags) +#else void DeviceResources::CreateDeviceResources() +#endif { // Create the DX12 API device object. D3D12XBOX_CREATE_DEVICE_PARAMETERS params = {}; @@ -77,6 +80,17 @@ void DeviceResources::CreateDeviceResources() params.GraphicsCommandQueueRingSizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); params.GraphicsScratchMemorySizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); params.ComputeScratchMemorySizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); +#ifdef _GAMING_XBOX_SCARLETT + params.CreateDeviceFlags = createDeviceFlags; + +#if (_GXDK_VER >= 0x585D070E /* GXDK Edition 221000 */) + if (m_options & c_AmplificationShaders) + { + params.AmplificationShaderIndirectArgsBufferSize = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); + params.AmplificationShaderPayloadBufferSize = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); + } +#endif +#endif HRESULT hr = D3D12XboxCreateDevice( nullptr, @@ -107,7 +121,7 @@ void DeviceResources::CreateDeviceResources() // Create descriptor heaps for render target views and depth stencil views. D3D12_DESCRIPTOR_HEAP_DESC rtvDescriptorHeapDesc = {}; - rtvDescriptorHeapDesc.NumDescriptors = (m_options & c_EnableHDR) ? (m_backBufferCount * 2) : m_backBufferCount; + rtvDescriptorHeapDesc.NumDescriptors = m_backBufferCount; rtvDescriptorHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; ThrowIfFailed(m_d3dDevice->CreateDescriptorHeap(&rtvDescriptorHeapDesc, IID_GRAPHICS_PPV_ARGS(m_rtvDescriptorHeap.ReleaseAndGetAddressOf()))); @@ -152,31 +166,46 @@ void DeviceResources::CreateDeviceResources() m_fenceEvent.Attach(CreateEventEx(nullptr, nullptr, 0, EVENT_MODIFY_STATE | SYNCHRONIZE)); if (!m_fenceEvent.IsValid()) { - throw std::exception("CreateEvent"); + throw std::system_error(std::error_code(static_cast(GetLastError()), std::system_category()), "CreateEventEx"); } - if (m_options & c_Enable4K_UHD) + if (m_options & (c_Enable4K_UHD | c_EnableQHD)) { switch (XSystemGetDeviceType()) { case XSystemDeviceType::XboxOne: case XSystemDeviceType::XboxOneS: - case XSystemDeviceType::XboxScarlettLockhart /* Xbox Series S */: - m_options &= ~c_Enable4K_UHD; -#ifdef _DEBUG - OutputDebugStringA("INFO: Swapchain using 1080p (1920 x 1080)\n"); -#endif + m_options &= ~(c_Enable4K_UHD | c_EnableQHD); break; + case XSystemDeviceType::XboxScarlettLockhart /* Xbox Series S */: + m_options &= ~c_Enable4K_UHD; + if (m_options & c_EnableQHD) + { + m_outputSize = { 0, 0, 2560, 1440 }; + } + break; + + case XSystemDeviceType::XboxScarlettAnaconda /* Xbox Series X */: + case XSystemDeviceType::XboxOneXDevkit: + case XSystemDeviceType::XboxScarlettDevkit: default: - m_outputSize = { 0, 0, 3840, 2160 }; -#ifdef _DEBUG - OutputDebugStringA("INFO: Swapchain using 4k (3840 x 2160)\n"); -#endif + m_outputSize = (m_options & c_Enable4K_UHD) ? RECT{ 0, 0, 3840, 2160 } : RECT{ 0, 0, 2560, 1440 }; break; } } +#ifdef _DEBUG + const char* info = nullptr; + switch (m_outputSize.bottom) + { + case 2160: info = "INFO: Swapchain using 4k (3840 x 2160)\n"; break; + case 1440: info = "INFO: Swapchain using 1440p (2560 x 1440)\n"; break; + default: info = "INFO: Swapchain using 1080p (1920 x 1080)\n"; break; + } + OutputDebugStringA(info); +#endif + RegisterFrameEvents(); } @@ -185,7 +214,7 @@ void DeviceResources::CreateWindowSizeDependentResources() { if (!m_window) { - throw std::exception("Call SetWindow with a valid window handle"); + throw std::logic_error("Call SetWindow with a valid Win32 window handle"); } // Wait until all previous GPU work is complete. @@ -198,7 +227,6 @@ void DeviceResources::CreateWindowSizeDependentResources() for (UINT n = 0; n < m_backBufferCount; n++) { m_renderTargets[n].Reset(); - m_renderTargetsGameDVR[n].Reset(); m_fenceValues[n] = m_fenceValues[m_backBufferIndex]; } @@ -208,7 +236,7 @@ void DeviceResources::CreateWindowSizeDependentResources() // Obtain the back buffers for this window which will be the final render targets // and create render target views for each of them. - CD3DX12_HEAP_PROPERTIES swapChainHeapProperties(D3D12_HEAP_TYPE_DEFAULT); + const CD3DX12_HEAP_PROPERTIES swapChainHeapProperties(D3D12_HEAP_TYPE_DEFAULT); D3D12_RESOURCE_DESC swapChainBufferDesc = CD3DX12_RESOURCE_DESC::Tex2D( m_backBufferFormat, @@ -219,6 +247,13 @@ void DeviceResources::CreateWindowSizeDependentResources() ); swapChainBufferDesc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; +#ifdef _GAMING_XBOX_XBOXONE + if (m_options & c_EnableHDR) + { + swapChainBufferDesc.Flags |= D3D12XBOX_RESOURCE_FLAG_ALLOW_AUTOMATIC_GAMEDVR_TONE_MAP; + } +#endif + D3D12_CLEAR_VALUE swapChainOptimizedClearValue = {}; swapChainOptimizedClearValue.Format = m_backBufferFormat; @@ -240,41 +275,12 @@ void DeviceResources::CreateWindowSizeDependentResources() rtvDesc.Format = m_backBufferFormat; rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; - CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( + const CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( m_rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), static_cast(n), m_rtvDescriptorSize); m_d3dDevice->CreateRenderTargetView(m_renderTargets[n].Get(), &rtvDesc, rtvDescriptor); } - if (m_options & c_EnableHDR) - { - swapChainBufferDesc.Format = swapChainOptimizedClearValue.Format = m_gameDVRFormat; - - for (UINT n = 0; n < m_backBufferCount; n++) - { - ThrowIfFailed(m_d3dDevice->CreateCommittedResource( - &swapChainHeapProperties, - D3D12_HEAP_FLAG_ALLOW_DISPLAY, - &swapChainBufferDesc, - D3D12_RESOURCE_STATE_PRESENT, - &swapChainOptimizedClearValue, - IID_GRAPHICS_PPV_ARGS(m_renderTargetsGameDVR[n].GetAddressOf()))); - - wchar_t name[25] = {}; - swprintf_s(name, L"GameDVR Render target %u", n); - m_renderTargetsGameDVR[n]->SetName(name); - - D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {}; - rtvDesc.Format = m_gameDVRFormat; - rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; - - CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptorGameDVR( - m_rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), - static_cast(m_backBufferCount + n), m_rtvDescriptorSize); - m_d3dDevice->CreateRenderTargetView(m_renderTargetsGameDVR[n].Get(), &rtvDesc, rtvDescriptorGameDVR); - } - } - // Reset the index to the current back buffer. m_backBufferIndex = 0; @@ -282,7 +288,7 @@ void DeviceResources::CreateWindowSizeDependentResources() { // Allocate a 2-D surface as the depth/stencil buffer and create a depth/stencil view // on this surface. - CD3DX12_HEAP_PROPERTIES depthHeapProperties(D3D12_HEAP_TYPE_DEFAULT); + const CD3DX12_HEAP_PROPERTIES depthHeapProperties(D3D12_HEAP_TYPE_DEFAULT); D3D12_RESOURCE_DESC depthStencilDesc = CD3DX12_RESOURCE_DESC::Tex2D( m_depthBufferFormat, @@ -295,7 +301,7 @@ void DeviceResources::CreateWindowSizeDependentResources() D3D12_CLEAR_VALUE depthOptimizedClearValue = {}; depthOptimizedClearValue.Format = m_depthBufferFormat; - depthOptimizedClearValue.DepthStencil.Depth = 1.0f; + depthOptimizedClearValue.DepthStencil.Depth = (m_options & c_ReverseDepth) ? 0.0f : 1.0f; depthOptimizedClearValue.DepthStencil.Stencil = 0; ThrowIfFailed(m_d3dDevice->CreateCommittedResource( @@ -331,10 +337,6 @@ void DeviceResources::CreateWindowSizeDependentResources() // Prepare the command list and render target for rendering. void DeviceResources::Prepare(D3D12_RESOURCE_STATES beforeState, D3D12_RESOURCE_STATES afterState) { - // Wait until frame start is signaled - m_framePipelineToken = D3D12XBOX_FRAME_PIPELINE_TOKEN_NULL; - ThrowIfFailed(m_d3dDevice->WaitFrameEventX(D3D12XBOX_FRAME_EVENT_ORIGIN, INFINITE, nullptr, D3D12XBOX_WAIT_FRAME_EVENT_FLAG_NONE, &m_framePipelineToken)); - // Reset command list and allocator. ThrowIfFailed(m_commandAllocators[m_backBufferIndex]->Reset()); ThrowIfFailed(m_commandList->Reset(m_commandAllocators[m_backBufferIndex].Get(), nullptr)); @@ -342,23 +344,10 @@ void DeviceResources::Prepare(D3D12_RESOURCE_STATES beforeState, D3D12_RESOURCE_ if (beforeState != afterState) { // Transition the render target into the correct state to allow for drawing into it. - if (m_options & c_EnableHDR) - { - D3D12_RESOURCE_BARRIER barriers[2] = - { - CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), - beforeState, afterState), - CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargetsGameDVR[m_backBufferIndex].Get(), - beforeState, afterState), - }; - m_commandList->ResourceBarrier(_countof(barriers), barriers); - } - else - { - D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), - beforeState, afterState); - m_commandList->ResourceBarrier(1, &barrier); - } + const D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition( + m_renderTargets[m_backBufferIndex].Get(), + beforeState, afterState); + m_commandList->ResourceBarrier(1, &barrier); } } @@ -368,20 +357,10 @@ void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState, _In_opt_ const if (beforeState != D3D12_RESOURCE_STATE_PRESENT) { // Transition the render target to the state that allows it to be presented to the display. - if (m_options & c_EnableHDR) - { - D3D12_RESOURCE_BARRIER barriers[2] = - { - CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), beforeState, D3D12_RESOURCE_STATE_PRESENT), - CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargetsGameDVR[m_backBufferIndex].Get(), beforeState, D3D12_RESOURCE_STATE_PRESENT), - }; - m_commandList->ResourceBarrier(_countof(barriers), barriers); - } - else - { - D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), beforeState, D3D12_RESOURCE_STATE_PRESENT); - m_commandList->ResourceBarrier(1, &barrier); - } + const D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition( + m_renderTargets[m_backBufferIndex].Get(), + beforeState, D3D12_RESOURCE_STATE_PRESENT); + m_commandList->ResourceBarrier(1, &barrier); } // Send the command list off to the GPU for processing. @@ -389,32 +368,24 @@ void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState, _In_opt_ const m_commandQueue->ExecuteCommandLists(1, CommandListCast(m_commandList.GetAddressOf())); // Present the backbuffer using the PresentX API. - D3D12XBOX_PRESENT_PLANE_PARAMETERS planeParameters[2] = {}; - planeParameters[0].Token = planeParameters[1].Token = m_framePipelineToken; - planeParameters[0].ResourceCount = planeParameters[1].ResourceCount = 1; - planeParameters[0].ppResources = m_renderTargets[m_backBufferIndex].GetAddressOf(); + D3D12XBOX_PRESENT_PLANE_PARAMETERS planeParameters = {}; + planeParameters.Token = m_framePipelineToken; + planeParameters.ResourceCount = 1; + planeParameters.ppResources = m_renderTargets[m_backBufferIndex].GetAddressOf(); if (m_options & c_EnableHDR) { - planeParameters[0].pSrcViewRects = planeParameters[1].pSrcViewRects = &m_outputSize; - planeParameters[0].ColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020; - planeParameters[1].ppResources = m_renderTargetsGameDVR[m_backBufferIndex].GetAddressOf(); - planeParameters[1].ColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; - - ThrowIfFailed( - m_commandQueue->PresentX(2, planeParameters, params) - ); - } - else - { - ThrowIfFailed( - m_commandQueue->PresentX(1, planeParameters, params) - ); + planeParameters.ColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020; } - // Xbox One apps do not need to handle DXGI_ERROR_DEVICE_REMOVED or DXGI_ERROR_DEVICE_RESET. + ThrowIfFailed( + m_commandQueue->PresentX(1, &planeParameters, params) + ); - MoveToNextFrame(); + // Xbox apps do not need to handle DXGI_ERROR_DEVICE_REMOVED or DXGI_ERROR_DEVICE_RESET. + + // Update the back buffer index. + m_backBufferIndex = (m_backBufferIndex + 1) % m_backBufferCount; } // Handle GPU suspend/resume @@ -436,13 +407,13 @@ void DeviceResources::WaitForGpu() noexcept if (m_commandQueue && m_fence && m_fenceEvent.IsValid()) { // Schedule a Signal command in the GPU queue. - UINT64 fenceValue = m_fenceValues[m_backBufferIndex]; + const UINT64 fenceValue = m_fenceValues[m_backBufferIndex]; if (SUCCEEDED(m_commandQueue->Signal(m_fence.Get(), fenceValue))) { // Wait until the Signal has been processed. if (SUCCEEDED(m_fence->SetEventOnCompletion(fenceValue, m_fenceEvent.Get()))) { - WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); + std::ignore = WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); // Increment the fence value for the current frame. m_fenceValues[m_backBufferIndex]++; @@ -451,25 +422,17 @@ void DeviceResources::WaitForGpu() noexcept } } -// Prepare to render the next frame. -void DeviceResources::MoveToNextFrame() +// For PresentX rendering, we should wait for the origin event just before processing input. +void DeviceResources::WaitForOrigin() { - // Schedule a Signal command in the queue. - const UINT64 currentFenceValue = m_fenceValues[m_backBufferIndex]; - ThrowIfFailed(m_commandQueue->Signal(m_fence.Get(), currentFenceValue)); - - // Update the back buffer index. - m_backBufferIndex = (m_backBufferIndex + 1) % m_backBufferCount; - - // If the next frame is not ready to be rendered yet, wait until it is ready. - if (m_fence->GetCompletedValue() < m_fenceValues[m_backBufferIndex]) - { - ThrowIfFailed(m_fence->SetEventOnCompletion(m_fenceValues[m_backBufferIndex], m_fenceEvent.Get())); - WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); - } - - // Set the fence value for the next frame. - m_fenceValues[m_backBufferIndex] = currentFenceValue + 1; + // Wait until frame start is signaled + m_framePipelineToken = D3D12XBOX_FRAME_PIPELINE_TOKEN_NULL; + ThrowIfFailed(m_d3dDevice->WaitFrameEventX( + D3D12XBOX_FRAME_EVENT_ORIGIN, + INFINITE, + nullptr, + D3D12XBOX_WAIT_FRAME_EVENT_FLAG_NONE, + &m_framePipelineToken)); } // Set frame interval and register for frame events diff --git a/Samples/Graphics/FastBlockCompress/DeviceResources.h b/Samples/Graphics/FastBlockCompress/DeviceResources.h index 1d26b17..4924894 100644 --- a/Samples/Graphics/FastBlockCompress/DeviceResources.h +++ b/Samples/Graphics/FastBlockCompress/DeviceResources.h @@ -10,8 +10,11 @@ namespace DX class DeviceResources { public: - static constexpr unsigned int c_Enable4K_UHD = 0x1; - static constexpr unsigned int c_EnableHDR = 0x2; + static constexpr unsigned int c_Enable4K_UHD = 0x1; + static constexpr unsigned int c_EnableQHD = 0x2; + static constexpr unsigned int c_EnableHDR = 0x4; + static constexpr unsigned int c_ReverseDepth = 0x8; + static constexpr unsigned int c_AmplificationShaders = 0x10; DeviceResources(DXGI_FORMAT backBufferFormat = DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT depthBufferFormat = DXGI_FORMAT_D32_FLOAT, @@ -25,7 +28,11 @@ namespace DX DeviceResources(DeviceResources const&) = delete; DeviceResources& operator= (DeviceResources const&) = delete; +#ifdef _GAMING_XBOX_SCARLETT + void CreateDeviceResources(D3D12XBOX_CREATE_DEVICE_FLAGS createDeviceFlags = D3D12XBOX_CREATE_DEVICE_FLAG_NONE); +#else void CreateDeviceResources(); +#endif void CreateWindowSizeDependentResources(); void SetWindow(HWND window) noexcept { m_window = window; } void Prepare(D3D12_RESOURCE_STATES beforeState = D3D12_RESOURCE_STATE_PRESENT, @@ -35,6 +42,7 @@ namespace DX void Suspend(); void Resume(); void WaitForGpu() noexcept; + void WaitForOrigin(); // Device Accessors. RECT GetOutputSize() const noexcept { return m_outputSize; } @@ -67,19 +75,7 @@ namespace DX return CD3DX12_CPU_DESCRIPTOR_HANDLE(m_dsvDescriptorHeap->GetCPUDescriptorHandleForHeapStart()); } - // Direct3D HDR Game DVR support for Xbox One. - ID3D12Resource* GetGameDVRRenderTarget() const noexcept { return m_renderTargetsGameDVR[m_backBufferIndex].Get(); } - DXGI_FORMAT GetGameDVRFormat() const noexcept { return m_gameDVRFormat; } - - CD3DX12_CPU_DESCRIPTOR_HANDLE GetGameDVRRenderTargetView() const noexcept - { - return CD3DX12_CPU_DESCRIPTOR_HANDLE( - m_rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), - static_cast(m_backBufferCount + m_backBufferIndex), m_rtvDescriptorSize); - } - private: - void MoveToNextFrame(); void RegisterFrameEvents(); static constexpr size_t MAX_BACK_BUFFER_COUNT = 3; @@ -126,9 +122,5 @@ namespace DX // DeviceResources options (see flags above) unsigned int m_options; - - // Direct3D HDR Game DVR support for Xbox One. - Microsoft::WRL::ComPtr m_renderTargetsGameDVR[MAX_BACK_BUFFER_COUNT]; - DXGI_FORMAT m_gameDVRFormat; }; } diff --git a/Samples/Graphics/FastBlockCompress/FastBlockCompress.cpp b/Samples/Graphics/FastBlockCompress/FastBlockCompress.cpp index 0544a48..ddea082 100644 --- a/Samples/Graphics/FastBlockCompress/FastBlockCompress.cpp +++ b/Samples/Graphics/FastBlockCompress/FastBlockCompress.cpp @@ -12,7 +12,7 @@ #include "ControllerFont.h" #include "ReadData.h" -extern void ExitSample(); +extern void ExitSample() noexcept; using namespace DirectX; @@ -20,9 +20,9 @@ using Microsoft::WRL::ComPtr; namespace { - const uint32_t MAX_TEXTURE_WIDTH = 2048; // 2048x2048 + constexpr uint32_t MAX_TEXTURE_WIDTH = 2048; // 2048x2048 - const uint32_t RMS_THREADGROUP_WIDTH = 64; + constexpr uint32_t RMS_THREADGROUP_WIDTH = 64; enum RMSRootParameterIndex { @@ -61,8 +61,8 @@ namespace static_assert((sizeof(ConstantBufferQuad) % 16) == 0, "CB size not padded correctly"); - const float c_zoomSpeed = 0.5f; - const float c_panSpeed = 0.5f; + constexpr float c_zoomSpeed = 0.5f; + constexpr float c_panSpeed = 0.5f; inline XMFLOAT2 RMSComputeResult(ID3D12Resource* buffer, float width) { @@ -114,7 +114,7 @@ void Sample::Initialize(HWND window) m_deviceResources->SetWindow(window); - m_deviceResources->CreateDeviceResources(); + m_deviceResources->CreateDeviceResources(); CreateDeviceDependentResources(); m_deviceResources->CreateWindowSizeDependentResources(); @@ -127,6 +127,8 @@ void Sample::Tick() { PIXBeginEvent(PIX_COLOR_DEFAULT, L"Frame %llu", m_frame); + m_deviceResources->WaitForOrigin(); + m_timer.Tick([&]() { Update(m_timer); @@ -310,7 +312,7 @@ void Sample::Update(DX::StepTimer const& timer) m_gpuTimer.BeginFrame(m_computeCommandList.Get()); ID3D12DescriptorHeap* pHeaps[] = { m_resourceDescriptors->Heap() }; - m_computeCommandList->SetDescriptorHeaps(_countof(pHeaps), pHeaps); + m_computeCommandList->SetDescriptorHeaps(static_cast(std::size(pHeaps)), pHeaps); auto img = m_images[m_currentImage].get(); @@ -506,9 +508,9 @@ void Sample::Render() m_deviceResources->Prepare(); Clear(); - auto size = m_deviceResources->GetOutputSize(); + auto const size = m_deviceResources->GetOutputSize(); - auto safe = SimpleMath::Viewport::ComputeTitleSafeArea(UINT(size.right), UINT(size.bottom)); + auto const safe = SimpleMath::Viewport::ComputeTitleSafeArea(UINT(size.right), UINT(size.bottom)); auto commandList = m_deviceResources->GetCommandList(); PIXBeginEvent(commandList, PIX_COLOR_DEFAULT, L"Render"); @@ -518,7 +520,7 @@ void Sample::Render() { m_resourceDescriptors->Heap() }; - commandList->SetDescriptorHeaps(_countof(descriptorHeaps), descriptorHeaps); + commandList->SetDescriptorHeaps(static_cast(std::size(descriptorHeaps)), descriptorHeaps); // Draw images auto img = m_images[m_currentImage].get(); @@ -599,7 +601,7 @@ void Sample::Render() viewPort.MaxDepth = 1; commandList->RSSetViewports(1, &viewPort); - RECT rct = { long(viewPort.TopLeftX), long(viewPort.TopLeftY), long(viewPort.TopLeftX + viewPort.Width), long(viewPort.TopLeftY + viewPort.Height) }; + const RECT rct = { long(viewPort.TopLeftX), long(viewPort.TopLeftY), long(viewPort.TopLeftX + viewPort.Width), long(viewPort.TopLeftY + viewPort.Height) }; commandList->RSSetScissorRects(1, &rct); if (m_toggleOriginal) @@ -770,14 +772,14 @@ void Sample::Clear() PIXBeginEvent(commandList, PIX_COLOR_DEFAULT, L"Clear"); // Clear the views. - auto rtvDescriptor = m_deviceResources->GetRenderTargetView(); + auto const rtvDescriptor = m_deviceResources->GetRenderTargetView(); commandList->OMSetRenderTargets(1, &rtvDescriptor, FALSE, nullptr); commandList->ClearRenderTargetView(rtvDescriptor, ATG::Colors::Background, 0, nullptr); // Set the viewport and scissor rect. - auto viewport = m_deviceResources->GetScreenViewport(); - auto scissorRect = m_deviceResources->GetScissorRect(); + auto const viewport = m_deviceResources->GetScreenViewport(); + auto const scissorRect = m_deviceResources->GetScissorRect(); commandList->RSSetViewports(1, &viewport); commandList->RSSetScissorRects(1, &scissorRect); @@ -809,11 +811,9 @@ void Sample::CreateDeviceDependentResources() m_graphicsMemory = std::make_unique(device); - RenderTargetState rtState(m_deviceResources->GetBackBufferFormat(), m_deviceResources->GetDepthBufferFormat()); + const RenderTargetState rtState(m_deviceResources->GetBackBufferFormat(), m_deviceResources->GetDepthBufferFormat()); m_resourceDescriptors = std::make_unique(device, - D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, - D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, 256, Descriptors::Count); diff --git a/Samples/Graphics/FastBlockCompress/FastBlockCompress.h b/Samples/Graphics/FastBlockCompress/FastBlockCompress.h index 30ba68e..a446e81 100644 --- a/Samples/Graphics/FastBlockCompress/FastBlockCompress.h +++ b/Samples/Graphics/FastBlockCompress/FastBlockCompress.h @@ -51,6 +51,13 @@ class Sample public: Sample() noexcept(false); + ~Sample() = default; + + Sample(Sample&&) = default; + Sample& operator= (Sample&&) = default; + + Sample(Sample const&) = delete; + Sample& operator= (Sample const&) = delete; // Initialization and management void Initialize(HWND window); @@ -61,9 +68,11 @@ public: // Messages void OnSuspending(); void OnResuming(); + void OnConstrained() {} + void OnUnConstrained() {} // Properties - bool RequestHDRMode() const { return m_deviceResources ? (m_deviceResources->GetDeviceOptions() & DX::DeviceResources::c_EnableHDR) != 0 : false; } + bool RequestHDRMode() const noexcept { return m_deviceResources ? (m_deviceResources->GetDeviceOptions() & DX::DeviceResources::c_EnableHDR) != 0 : false; } private: @@ -140,10 +149,10 @@ private: Microsoft::WRL::ComPtr m_fbcTexture; void* m_fbcTextureMem; - std::vector m_srvFBC; + std::vector m_srvFBC; - DX::GPUComputeTimer m_gpuTimer; - DX::CPUTimer m_cpuTimer; + DX::GPUComputeTimer m_gpuTimer; + DX::CPUTimer m_cpuTimer; Microsoft::WRL::ComPtr m_computeAllocator; Microsoft::WRL::ComPtr m_computeCommandQueue; diff --git a/Samples/Graphics/FastBlockCompress/Main.cpp b/Samples/Graphics/FastBlockCompress/Main.cpp index ff1207e..ccec991 100644 --- a/Samples/Graphics/FastBlockCompress/Main.cpp +++ b/Samples/Graphics/FastBlockCompress/Main.cpp @@ -18,16 +18,25 @@ using namespace DirectX; +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wcovered-switch-default" +#pragma clang diagnostic ignored "-Wswitch-enum" +#endif + +#pragma warning(disable : 4061) + namespace { std::unique_ptr g_sample; HANDLE g_plmSuspendComplete = nullptr; HANDLE g_plmSignalResume = nullptr; -}; +} bool g_HDRMode = false; LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); +void SetDisplayMode() noexcept; +void ExitSample() noexcept; // Entry point int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) @@ -58,6 +67,8 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp // Register class and create window PAPPSTATE_REGISTRATION hPLM = {}; + PAPPCONSTRAIN_REGISTRATION hPLM2 = {}; + { // Register class WNDCLASSEXA wcex = {}; @@ -66,7 +77,7 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp wcex.lpfnWndProc = WndProc; wcex.hInstance = hInstance; wcex.lpszClassName = u8"FastBlockCompressWindowClass"; - wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); + wcex.hbrBackground = reinterpret_cast(COLOR_WINDOW + 1); if (!RegisterClassExA(&wcex)) return 1; @@ -79,17 +90,7 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp ShowWindow(hwnd, nCmdShow); - if (g_sample->RequestHDRMode()) - { - // Request HDR mode. - auto result = XDisplayTryEnableHdrMode(XDisplayHdrModePreference::PreferHdr, nullptr); - - g_HDRMode = (result == XDisplayHdrModeResult::Enabled); - - #ifdef _DEBUG - OutputDebugStringA((g_HDRMode) ? "INFO: Display in HDR Mode\n" : "INFO: Display in SDR Mode\n"); - #endif - } + SetDisplayMode(); SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast(g_sample.get())); @@ -116,7 +117,7 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp PostMessage(reinterpret_cast(context), WM_USER, 0, 0); // To defer suspend, you must wait to exit this callback - (void)WaitForSingleObject(g_plmSuspendComplete, INFINITE); + std::ignore = WaitForSingleObject(g_plmSuspendComplete, INFINITE); } else { @@ -124,6 +125,13 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp } }, hwnd, &hPLM)) return 1; + + if (RegisterAppConstrainedChangeNotification([](BOOLEAN constrained, PVOID context) + { + // To ensure we use the main UI thread to process the notification, we self-post a message + SendMessage(reinterpret_cast(context), WM_USER + 1, (constrained) ? 1u : 0u, 0); + }, hwnd, &hPLM2)) + return 1; } // Main message loop @@ -144,13 +152,14 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR lp g_sample.reset(); UnregisterAppStateChangeNotification(hPLM); - + UnregisterAppConstrainedChangeNotification(hPLM2); + CloseHandle(g_plmSuspendComplete); CloseHandle(g_plmSignalResume); XGameRuntimeUninitialize(); - return (int) msg.wParam; + return static_cast(msg.wParam); } // Windows procedure @@ -168,18 +177,52 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) // Complete deferral SetEvent(g_plmSuspendComplete); - (void)WaitForSingleObject(g_plmSignalResume, INFINITE); + std::ignore = WaitForSingleObject(g_plmSignalResume, INFINITE); + + SetDisplayMode(); sample->OnResuming(); } break; + + case WM_USER + 1: + if (sample) + { + if (wParam) + { + sample->OnConstrained(); + } + else + { + SetDisplayMode(); + + sample->OnUnConstrained(); + } + } + break; } return DefWindowProc(hWnd, message, wParam, lParam); } +// HDR helper +void SetDisplayMode() noexcept +{ + if (g_sample && g_sample->RequestHDRMode()) + { + // Request HDR mode. + auto result = XDisplayTryEnableHdrMode(XDisplayHdrModePreference::PreferHdr, nullptr); + + g_HDRMode = (result == XDisplayHdrModeResult::Enabled); + +#ifdef _DEBUG + OutputDebugStringA((g_HDRMode) ? "INFO: Display in HDR Mode\n" : "INFO: Display in SDR Mode\n"); +#endif + } +} + // Exit helper -void ExitSample() +void ExitSample() noexcept { PostQuitMessage(0); } diff --git a/Samples/Graphics/FastBlockCompress/Readme.docx b/Samples/Graphics/FastBlockCompress/Readme.docx index 99b125c96d912677cbc02f14147c1a77a6fbade1..2e080bbfe2e866a8d0102e3ef35c29fe44c41c4d 100644 GIT binary patch delta 35505 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