├── .gitignore ├── Assets ├── LockScreenLogo.scale-200.png ├── SplashScreen.scale-200.png ├── Square150x150Logo.scale-200.png ├── Square44x44Logo.scale-200.png ├── Square44x44Logo.targetsize-24_altform-unplated.png ├── StoreLogo.png └── Wide310x150Logo.scale-200.png ├── FlipModelD3D12.sln ├── FlipModelD3D12.vcxproj ├── FlipModelD3D12.vcxproj.filters ├── FlipModelUniversal.vcxproj ├── FlipModelUniversal.vcxproj.filters ├── FlipModelUniversal_TemporaryKey.pfx ├── Package.appxmanifest ├── Readme.md ├── Source ├── App.cpp ├── App.h ├── DX12Helpers.cpp ├── DX12Helpers.hpp ├── EventViz.cpp ├── EventViz.hpp ├── PresentQueueStats.hpp ├── WindowsHelpers.cpp ├── WindowsHelpers.hpp ├── d3dx12.h ├── pixel_shader.hlsl ├── sample_cube.cpp ├── sample_cube.hpp ├── sample_dx12.cpp ├── sample_dx12.hpp ├── sample_game.cpp ├── sample_game.hpp ├── sample_main.cpp ├── sample_math.cpp ├── sample_math.hpp ├── timeline_multimap.hpp ├── vertex_shader.hlsl ├── wsi.cpp ├── wsi.hpp ├── wsi_utils.cpp └── wsi_utils.hpp ├── license.txt ├── thumbnail_320.png └── thumbnail_full.png /.gitignore: -------------------------------------------------------------------------------- 1 | /.vs/ 2 | /ARM/ 3 | /Debug/ 4 | /Release/ 5 | /Win32/ 6 | /x64/ 7 | /Generated Files/ 8 | /Source/pixel_shader.h 9 | /Source/vertex_shader.h 10 | /log.txt 11 | /FlipModelD3D12.VC.db 12 | /FlipModelD3D12.sdf 13 | /FlipModelD3D12.v11.suo 14 | -------------------------------------------------------------------------------- /Assets/LockScreenLogo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GameTechDev/FlipModelD3D12/f6f9233abd6957a77a1cf0d41050a3529352bf5a/Assets/LockScreenLogo.scale-200.png -------------------------------------------------------------------------------- /Assets/SplashScreen.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GameTechDev/FlipModelD3D12/f6f9233abd6957a77a1cf0d41050a3529352bf5a/Assets/SplashScreen.scale-200.png -------------------------------------------------------------------------------- /Assets/Square150x150Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GameTechDev/FlipModelD3D12/f6f9233abd6957a77a1cf0d41050a3529352bf5a/Assets/Square150x150Logo.scale-200.png -------------------------------------------------------------------------------- /Assets/Square44x44Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GameTechDev/FlipModelD3D12/f6f9233abd6957a77a1cf0d41050a3529352bf5a/Assets/Square44x44Logo.scale-200.png -------------------------------------------------------------------------------- /Assets/Square44x44Logo.targetsize-24_altform-unplated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GameTechDev/FlipModelD3D12/f6f9233abd6957a77a1cf0d41050a3529352bf5a/Assets/Square44x44Logo.targetsize-24_altform-unplated.png -------------------------------------------------------------------------------- /Assets/StoreLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GameTechDev/FlipModelD3D12/f6f9233abd6957a77a1cf0d41050a3529352bf5a/Assets/StoreLogo.png -------------------------------------------------------------------------------- /Assets/Wide310x150Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GameTechDev/FlipModelD3D12/f6f9233abd6957a77a1cf0d41050a3529352bf5a/Assets/Wide310x150Logo.scale-200.png -------------------------------------------------------------------------------- /FlipModelD3D12.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.24720.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FlipModelD3D12", "FlipModelD3D12.vcxproj", "{11EB5ED2-B797-47AF-825D-23DF434C97B5}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FlipModelUniversal", "FlipModelUniversal.vcxproj", "{525F2141-A251-47B0-8E92-E631ABF3A5FA}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|ARM = Debug|ARM 13 | Debug|x64 = Debug|x64 14 | Debug|x86 = Debug|x86 15 | Release|ARM = Release|ARM 16 | Release|x64 = Release|x64 17 | Release|x86 = Release|x86 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {11EB5ED2-B797-47AF-825D-23DF434C97B5}.Debug|ARM.ActiveCfg = Debug|Win32 21 | {11EB5ED2-B797-47AF-825D-23DF434C97B5}.Debug|x64.ActiveCfg = Debug|x64 22 | {11EB5ED2-B797-47AF-825D-23DF434C97B5}.Debug|x64.Build.0 = Debug|x64 23 | {11EB5ED2-B797-47AF-825D-23DF434C97B5}.Debug|x86.ActiveCfg = Debug|Win32 24 | {11EB5ED2-B797-47AF-825D-23DF434C97B5}.Debug|x86.Build.0 = Debug|Win32 25 | {11EB5ED2-B797-47AF-825D-23DF434C97B5}.Release|ARM.ActiveCfg = Release|Win32 26 | {11EB5ED2-B797-47AF-825D-23DF434C97B5}.Release|x64.ActiveCfg = Release|x64 27 | {11EB5ED2-B797-47AF-825D-23DF434C97B5}.Release|x64.Build.0 = Release|x64 28 | {11EB5ED2-B797-47AF-825D-23DF434C97B5}.Release|x86.ActiveCfg = Release|Win32 29 | {11EB5ED2-B797-47AF-825D-23DF434C97B5}.Release|x86.Build.0 = Release|Win32 30 | {525F2141-A251-47B0-8E92-E631ABF3A5FA}.Debug|ARM.ActiveCfg = Debug|ARM 31 | {525F2141-A251-47B0-8E92-E631ABF3A5FA}.Debug|ARM.Build.0 = Debug|ARM 32 | {525F2141-A251-47B0-8E92-E631ABF3A5FA}.Debug|ARM.Deploy.0 = Debug|ARM 33 | {525F2141-A251-47B0-8E92-E631ABF3A5FA}.Debug|x64.ActiveCfg = Debug|x64 34 | {525F2141-A251-47B0-8E92-E631ABF3A5FA}.Debug|x64.Build.0 = Debug|x64 35 | {525F2141-A251-47B0-8E92-E631ABF3A5FA}.Debug|x64.Deploy.0 = Debug|x64 36 | {525F2141-A251-47B0-8E92-E631ABF3A5FA}.Debug|x86.ActiveCfg = Debug|Win32 37 | {525F2141-A251-47B0-8E92-E631ABF3A5FA}.Debug|x86.Build.0 = Debug|Win32 38 | {525F2141-A251-47B0-8E92-E631ABF3A5FA}.Debug|x86.Deploy.0 = Debug|Win32 39 | {525F2141-A251-47B0-8E92-E631ABF3A5FA}.Release|ARM.ActiveCfg = Release|ARM 40 | {525F2141-A251-47B0-8E92-E631ABF3A5FA}.Release|ARM.Build.0 = Release|ARM 41 | {525F2141-A251-47B0-8E92-E631ABF3A5FA}.Release|ARM.Deploy.0 = Release|ARM 42 | {525F2141-A251-47B0-8E92-E631ABF3A5FA}.Release|x64.ActiveCfg = Release|x64 43 | {525F2141-A251-47B0-8E92-E631ABF3A5FA}.Release|x64.Build.0 = Release|x64 44 | {525F2141-A251-47B0-8E92-E631ABF3A5FA}.Release|x64.Deploy.0 = Release|x64 45 | {525F2141-A251-47B0-8E92-E631ABF3A5FA}.Release|x86.ActiveCfg = Release|Win32 46 | {525F2141-A251-47B0-8E92-E631ABF3A5FA}.Release|x86.Build.0 = Release|Win32 47 | {525F2141-A251-47B0-8E92-E631ABF3A5FA}.Release|x86.Deploy.0 = Release|Win32 48 | EndGlobalSection 49 | GlobalSection(SolutionProperties) = preSolution 50 | HideSolutionNode = FALSE 51 | EndGlobalSection 52 | EndGlobal 53 | -------------------------------------------------------------------------------- /FlipModelD3D12.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {11EB5ED2-B797-47AF-825D-23DF434C97B5} 23 | Win32Proj 24 | WSI 25 | 10.0.10240.0 26 | FlipModelD3D12 27 | 28 | 29 | 30 | Application 31 | true 32 | v140 33 | 34 | 35 | Application 36 | true 37 | v140 38 | 39 | 40 | Application 41 | false 42 | v140 43 | true 44 | 45 | 46 | Application 47 | false 48 | v140 49 | true 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | true 69 | $(SolutionDir)$(Platform)\$(ProjectName)\$(Configuration)\ 70 | $(Platform)\$(ProjectName)\$(Configuration)\ 71 | 72 | 73 | true 74 | $(SolutionDir)$(Platform)\$(ProjectName)\$(Configuration)\ 75 | $(Platform)\$(ProjectName)\$(Configuration)\ 76 | 77 | 78 | false 79 | $(SolutionDir)$(Platform)\$(ProjectName)\$(Configuration)\ 80 | $(Platform)\$(ProjectName)\$(Configuration)\ 81 | 82 | 83 | false 84 | $(SolutionDir)$(Platform)\$(ProjectName)\$(Configuration)\ 85 | $(Platform)\$(ProjectName)\$(Configuration)\ 86 | 87 | 88 | 89 | 90 | 91 | Disabled 92 | WIN32;_CRT_SECURE_NO_WARNINGS;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) 93 | . 94 | Level3 95 | MultiThreadedDebug 96 | 97 | 98 | Windows 99 | true 100 | %(AdditionalLibraryDirectories) 101 | 102 | 103 | 104 | 105 | 106 | 107 | Disabled 108 | WIN32;_CRT_SECURE_NO_WARNINGS;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) 109 | . 110 | Level3 111 | MultiThreadedDebug 112 | 113 | 114 | Windows 115 | true 116 | %(AdditionalLibraryDirectories) 117 | 118 | 119 | 120 | 121 | 122 | 123 | MaxSpeed 124 | true 125 | true 126 | WIN32;_CRT_SECURE_NO_WARNINGS;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) 127 | . 128 | Level3 129 | MultiThreaded 130 | 131 | 132 | Windows 133 | true 134 | true 135 | true 136 | %(AdditionalLibraryDirectories) 137 | 138 | 139 | 140 | 141 | 142 | 143 | MaxSpeed 144 | true 145 | true 146 | WIN32;_CRT_SECURE_NO_WARNINGS;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) 147 | . 148 | Level3 149 | MultiThreaded 150 | 151 | 152 | Windows 153 | true 154 | true 155 | true 156 | %(AdditionalLibraryDirectories) 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | pixel_shader 188 | 4.0 189 | 190 | 191 | Pixel 192 | %(RelativeDir)%(Filename).h 193 | 194 | 195 | vertex_shader 196 | 4.0 197 | 198 | 199 | Vertex 200 | %(RelativeDir)%(Filename).h 201 | 202 | 203 | 204 | 205 | 206 | 207 | -------------------------------------------------------------------------------- /FlipModelD3D12.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Source 6 | 7 | 8 | Source 9 | 10 | 11 | Source 12 | 13 | 14 | Source 15 | 16 | 17 | Source 18 | 19 | 20 | Source 21 | 22 | 23 | Helpers 24 | 25 | 26 | Helpers 27 | 28 | 29 | Helpers 30 | 31 | 32 | Helpers 33 | 34 | 35 | 36 | 37 | Source 38 | 39 | 40 | Source 41 | 42 | 43 | Source 44 | 45 | 46 | Source 47 | 48 | 49 | Source 50 | 51 | 52 | Source 53 | 54 | 55 | Helpers 56 | 57 | 58 | Helpers 59 | 60 | 61 | Helpers 62 | 63 | 64 | Helpers 65 | 66 | 67 | Helpers 68 | 69 | 70 | Source 71 | 72 | 73 | 74 | 75 | {d4b9a023-4208-435c-9013-45bc90cf9289} 76 | 77 | 78 | {2faa99ad-c882-482d-b216-2efa61450d86} 79 | 80 | 81 | {81ba097c-1074-4231-85ee-c8e2a326482f} 82 | 83 | 84 | 85 | 86 | Shaders 87 | 88 | 89 | Shaders 90 | 91 | 92 | -------------------------------------------------------------------------------- /FlipModelUniversal.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | Debug 22 | ARM 23 | 24 | 25 | Release 26 | ARM 27 | 28 | 29 | 30 | {525f2141-a251-47b0-8e92-e631abf3a5fa} 31 | DirectXApp 32 | FlipModelUniversal 33 | en-US 34 | 14.0 35 | true 36 | Windows Store 37 | 10.0.10240.0 38 | 10.0.10240.0 39 | 10.0 40 | true 41 | 42 | 43 | 44 | Application 45 | true 46 | v140 47 | 48 | 49 | Application 50 | true 51 | v140 52 | 53 | 54 | Application 55 | true 56 | v140 57 | 58 | 59 | Application 60 | false 61 | true 62 | v140 63 | true 64 | 65 | 66 | Application 67 | false 68 | true 69 | v140 70 | true 71 | 72 | 73 | Application 74 | false 75 | true 76 | v140 77 | true 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | False 106 | Always 107 | x86|x64|arm 108 | 109 | 110 | 111 | mincore.lib;d3d12.lib;dxgi.lib;windowscodecs.lib;%(AdditionalDependencies) 112 | %(AdditionalLibraryDirectories); $(VCInstallDir)\lib\store\arm; $(VCInstallDir)\lib\arm 113 | 114 | 115 | 116 | 117 | 118 | 119 | $(ProjectDir);$(IntermediateOutputPath);%(AdditionalIncludeDirectories) 120 | /bigobj %(AdditionalOptions) 121 | 4453;28204 122 | _DEBUG;%(PreprocessorDefinitions) 123 | NotUsing 124 | 125 | 126 | 127 | 128 | mincore.lib;d3d12.lib;dxgi.lib;windowscodecs.lib;%(AdditionalDependencies) 129 | %(AdditionalLibraryDirectories); $(VCInstallDir)\lib\store\arm; $(VCInstallDir)\lib\arm 130 | 131 | 132 | 133 | 134 | 135 | 136 | $(ProjectDir);$(IntermediateOutputPath);%(AdditionalIncludeDirectories) 137 | /bigobj %(AdditionalOptions) 138 | 4453;28204 139 | NDEBUG;%(PreprocessorDefinitions) 140 | NotUsing 141 | 142 | 143 | 144 | 145 | mincore.lib;d3d12.lib;dxgi.lib;windowscodecs.lib;%(AdditionalDependencies) 146 | %(AdditionalLibraryDirectories); $(VCInstallDir)\lib\store; $(VCInstallDir)\lib 147 | 148 | 149 | 150 | 151 | 152 | 153 | $(ProjectDir);$(IntermediateOutputPath);%(AdditionalIncludeDirectories) 154 | /bigobj %(AdditionalOptions) 155 | 4453;28204 156 | _DEBUG;%(PreprocessorDefinitions) 157 | NotUsing 158 | 159 | 160 | 161 | 162 | mincore.lib;d3d12.lib;dxgi.lib;windowscodecs.lib;%(AdditionalDependencies) 163 | %(AdditionalLibraryDirectories); $(VCInstallDir)\lib\store; $(VCInstallDir)\lib 164 | 165 | 166 | 167 | 168 | 169 | 170 | $(ProjectDir);$(IntermediateOutputPath);%(AdditionalIncludeDirectories) 171 | /bigobj %(AdditionalOptions) 172 | 4453;28204 173 | NDEBUG;%(PreprocessorDefinitions) 174 | NotUsing 175 | 176 | 177 | 178 | 179 | d3d12.lib;dxgi.lib;%(AdditionalDependencies) 180 | %(AdditionalLibraryDirectories); $(VCInstallDir)\lib\store\amd64; $(VCInstallDir)\lib\amd64 181 | 182 | 183 | 184 | 185 | 186 | 187 | $(ProjectDir);$(IntermediateOutputPath);%(AdditionalIncludeDirectories) 188 | /bigobj %(AdditionalOptions) 189 | 4453;28204 190 | _DEBUG;%(PreprocessorDefinitions) 191 | NotUsing 192 | 193 | 194 | 195 | 196 | mincore.lib;d3d12.lib;dxgi.lib;windowscodecs.lib;%(AdditionalDependencies) 197 | %(AdditionalLibraryDirectories); $(VCInstallDir)\lib\store\amd64; $(VCInstallDir)\lib\amd64 198 | 199 | 200 | 201 | 202 | 203 | 204 | $(ProjectDir);$(IntermediateOutputPath);%(AdditionalIncludeDirectories) 205 | /bigobj %(AdditionalOptions) 206 | 4453;28204 207 | NDEBUG;%(PreprocessorDefinitions) 208 | NotUsing 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | Designer 246 | 247 | 248 | 249 | 250 | 251 | pixel_shader 252 | %(RelativeDir)%(Filename).h 253 | 254 | 255 | pixel_shader 256 | %(RelativeDir)%(Filename).h 257 | 258 | 259 | pixel_shader 260 | %(RelativeDir)%(Filename).h 261 | 262 | 263 | pixel_shader 264 | %(RelativeDir)%(Filename).h 265 | 266 | 267 | pixel_shader 268 | %(RelativeDir)%(Filename).h 269 | 270 | 271 | pixel_shader 272 | %(RelativeDir)%(Filename).h 273 | 274 | 275 | 4.0 276 | 4.0 277 | 4.0 278 | 4.0 279 | 4.0 280 | 4.0 281 | Pixel 282 | Pixel 283 | Pixel 284 | Pixel 285 | Pixel 286 | Pixel 287 | 288 | 289 | vertex_shader 290 | vertex_shader 291 | vertex_shader 292 | vertex_shader 293 | vertex_shader 294 | vertex_shader 295 | %(RelativeDir)%(Filename).h 296 | %(RelativeDir)%(Filename).h 297 | %(RelativeDir)%(Filename).h 298 | %(RelativeDir)%(Filename).h 299 | %(RelativeDir)%(Filename).h 300 | %(RelativeDir)%(Filename).h 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 4.0 314 | 4.0 315 | 4.0 316 | 4.0 317 | 4.0 318 | 4.0 319 | Vertex 320 | Vertex 321 | Vertex 322 | Vertex 323 | Vertex 324 | Vertex 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | -------------------------------------------------------------------------------- /FlipModelUniversal.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | c0140414-759a-4040-abcb-ed452f5c87b7 6 | bmp;fbx;gif;jpg;jpeg;tga;tiff;tif;png 7 | 8 | 9 | Assets 10 | 11 | 12 | Assets 13 | 14 | 15 | Assets 16 | 17 | 18 | Assets 19 | 20 | 21 | Assets 22 | 23 | 24 | Assets 25 | 26 | 27 | Assets 28 | 29 | 30 | {1f5b3db8-5fa5-4b8c-92a2-ca3bfe05cdca} 31 | 32 | 33 | {5d881376-446a-4b56-a275-f402162c54a5} 34 | 35 | 36 | {e022284a-f824-413e-b20f-b850f1022727} 37 | 38 | 39 | 40 | 41 | Source 42 | 43 | 44 | Source 45 | 46 | 47 | Source 48 | 49 | 50 | Source 51 | 52 | 53 | Source 54 | 55 | 56 | Source 57 | 58 | 59 | Helpers 60 | 61 | 62 | Helpers 63 | 64 | 65 | 66 | 67 | Source 68 | 69 | 70 | Source 71 | 72 | 73 | Source 74 | 75 | 76 | Source 77 | 78 | 79 | Source 80 | 81 | 82 | Source 83 | 84 | 85 | Source 86 | 87 | 88 | Helpers 89 | 90 | 91 | Helpers 92 | 93 | 94 | Helpers 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | Source 105 | 106 | 107 | 108 | 109 | Shaders 110 | 111 | 112 | Shaders 113 | 114 | 115 | -------------------------------------------------------------------------------- /FlipModelUniversal_TemporaryKey.pfx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GameTechDev/FlipModelD3D12/f6f9233abd6957a77a1cf0d41050a3529352bf5a/FlipModelUniversal_TemporaryKey.pfx -------------------------------------------------------------------------------- /Package.appxmanifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | FlipModelD3D12 7 | dbregman 8 | Assets\StoreLogo.png 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # DISCONTINUATION OF PROJECT # 2 | This project will no longer be maintained by Intel. 3 | Intel has ceased development and contributions including, but not limited to, maintenance, bug fixes, new releases, or updates, to this project. 4 | Intel no longer accepts patches to this project. 5 | FlipModel D3D12 Sample Application 6 | ====================================================== 7 | An interactive visualization for understanding the Direct3D 12 Flip Model. 8 | 9 | For a detailed explanation, please see this [article](https://software.intel.com/en-us/articles/sample-application-for-direct3d-12-flip-model-swap-chains). 10 | 11 | Build Instructions 12 | ================== 13 | FlipModelD3D12.sln contains two projects: 14 | - FlipModelD3D12, for building a regular (win32) desktop application. 15 | - FlipModelUniversal, for building a Windows 10 Universal App. 16 | 17 | Choose your start up project, build and run. 18 | 19 | Requirements 20 | ============ 21 | - Windows 10 or greater 22 | - Visual Studio 2015 or higher 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Source/App.cpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2017 Intel Corporation 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | // use this file except in compliance with the License. You may obtain a copy 6 | // of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | //////////////////////////////////////////////////////////////////////////////// 16 | #include "App.h" 17 | 18 | using namespace FlipModelUniversal; 19 | 20 | using namespace Windows::ApplicationModel; 21 | using namespace Windows::ApplicationModel::Core; 22 | using namespace Windows::ApplicationModel::Activation; 23 | using namespace Windows::UI::Core; 24 | using namespace Windows::UI::Input; 25 | using namespace Windows::System; 26 | using namespace Windows::Foundation; 27 | using namespace Windows::Graphics::Display; 28 | 29 | // The DirectX 12 Application template is documented at http://go.microsoft.com/fwlink/?LinkID=613670&clcid=0x409 30 | 31 | ref struct Direct3DApplicationSource sealed : Windows::ApplicationModel::Core::IFrameworkViewSource 32 | { 33 | virtual Windows::ApplicationModel::Core::IFrameworkView^ CreateView() 34 | { 35 | return ref new App(); 36 | } 37 | }; 38 | 39 | // The main function is only used to initialize our IFrameworkView class. 40 | [Platform::MTAThread] 41 | int main(Platform::Array^) 42 | { 43 | 44 | auto direct3DApplicationSource = ref new Direct3DApplicationSource(); 45 | CoreApplication::Run(direct3DApplicationSource); 46 | return 0; 47 | } 48 | 49 | // The first method called when the IFrameworkView is being created. 50 | void App::Initialize(CoreApplicationView^ applicationView) 51 | { 52 | // Register event handlers for app lifecycle. This example includes Activated, so that we 53 | // can make the CoreWindow active and start rendering on the window. 54 | applicationView->Activated += 55 | ref new TypedEventHandler(this, &App::OnActivated); 56 | 57 | CoreApplication::Suspending += 58 | ref new EventHandler(this, &App::OnSuspending); 59 | 60 | CoreApplication::Resuming += 61 | ref new EventHandler(this, &App::OnResuming); 62 | } 63 | 64 | // Called when the CoreWindow object is created (or re-created). 65 | void App::SetWindow(CoreWindow^ window) 66 | { 67 | window->Closed += 68 | ref new TypedEventHandler(this, &App::OnWindowClosed); 69 | 70 | window->KeyDown += 71 | ref new TypedEventHandler(this, &App::OnKeyDown); 72 | 73 | window->KeyUp += 74 | ref new TypedEventHandler(this, &App::OnKeyUp); 75 | } 76 | 77 | // Initializes scene resources, or loads a previously saved app state. 78 | void App::Load(Platform::String^ entryPoint) 79 | { 80 | initialize_game(&m_game, GetTickCount64()); 81 | 82 | serialize_swapchain_options(false); 83 | } 84 | 85 | // This method is called after the window becomes active. 86 | void App::Run() 87 | { 88 | while (!m_windowClosed) 89 | { 90 | ZeroMemory(&m_action, sizeof(m_action)); 91 | 92 | CoreWindow^ window = CoreWindow::GetForCurrentThread(); 93 | window->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessAllIfPresent); 94 | 95 | m_windowDpi = DisplayInformation::GetForCurrentView()->LogicalDpi; 96 | m_windowWidthDips = window->Bounds.Width; 97 | m_windowHeightDips = window->Bounds.Height; 98 | m_windowVisible = window->Visible; 99 | 100 | if (window->GetAsyncKeyState(VirtualKey::Left) == CoreVirtualKeyStates::Down) 101 | { 102 | m_action.decrease_rotation = true; 103 | } 104 | 105 | if (window->GetAsyncKeyState(VirtualKey::Right) == CoreVirtualKeyStates::Down) 106 | { 107 | m_action.increase_rotation = true; 108 | } 109 | 110 | int64_t millisecond_clock_now = GetTickCount64(); 111 | 112 | unsigned ticks_elapsed = 0; 113 | float fractional_ticks = 0; 114 | calc_game_elapsed_time(&m_game, &ticks_elapsed, &fractional_ticks, millisecond_clock_now); 115 | 116 | update_game(&m_game, ticks_elapsed, &m_action, millisecond_clock_now); 117 | update_fps(); 118 | 119 | if (m_action.toggle_pause) 120 | { 121 | pause_eviz_dx12(m_game.paused); 122 | } 123 | 124 | if (m_windowVisible) 125 | { 126 | set_swapchain_options_dx12(NULL, reinterpret_cast(window), 127 | m_windowWidthDips, m_windowHeightDips, m_windowDpi, &m_swapchain_opts); 128 | 129 | WCHAR hud_string[4096]; 130 | 131 | { 132 | #define NEWLINE "\n" 133 | swprintf_s(hud_string, L"" 134 | "Controls:" NEWLINE 135 | "[%d] Pause: Space" NEWLINE 136 | "[%d] Fullscreen: F11" NEWLINE 137 | "[%d] Vsync: Ctrl+K" NEWLINE 138 | "[%d] Use Waitable Object: Ctrl+W" NEWLINE 139 | "[%d] MaximumFrameLatency: Ctrl+,Ctrl-" NEWLINE 140 | "[%d] BufferCount: +,-" NEWLINE 141 | "[%d] FrameCount: [,]" NEWLINE 142 | "[%.1f] GPU Workload up,down" NEWLINE 143 | "[%d] CPU Workload Ctrl+up, Ctrl+down" NEWLINE 144 | "Stats:" NEWLINE 145 | " DPI: %.2f, %.2fx%.2f" NEWLINE 146 | " Avg. Present Latency = %.2f ms" NEWLINE 147 | " Latency StdDev = %.2fms" NEWLINE 148 | " Latency MinMaxDev = %.2fms" NEWLINE 149 | " Fps = %.2f (%.2fms)" NEWLINE 150 | " GPU fps = %.2f (%.2fms)" NEWLINE 151 | " CPU fps = %.2f (%.2fms)" NEWLINE, 152 | m_game.paused, 153 | m_fullscreen, 154 | m_vsync, 155 | m_swapchain_opts.create_time.use_waitable_object, 156 | m_swapchain_opts.create_time.max_frame_latency, 157 | m_swapchain_opts.create_time.swapchain_buffer_count, 158 | m_swapchain_opts.create_time.gpu_frame_count, 159 | m_swapchain_opts.any_time.overdraw_factor, 160 | m_swapchain_opts.any_time.cpu_draw_ms, 161 | m_windowDpi, m_windowWidthDips, m_windowHeightDips, 162 | m_frame_latency, 163 | m_frame_latency_stddev, m_frame_latency_minmaxd, 164 | m_current_fps, 1000 / m_current_fps, 165 | m_current_fps_gpu, 1000 * m_current_frametime_gpu, 166 | m_current_fps_cpu, 1000 * m_current_frametime_cpu 167 | ); 168 | } 169 | 170 | dx12_render_stats stats = {0}; 171 | render_game_dx12(hud_string, &m_game, fractional_ticks, m_vsync, &stats); 172 | if (stats.latency) { 173 | const float alpha = 0.1f; 174 | m_frame_latency = (1 - alpha)*m_frame_latency + alpha*stats.latency; 175 | m_frame_latency_stddev = stats.stddev_jitter; 176 | m_frame_latency_minmaxd = stats.minmax_jitter; 177 | m_current_frametime_cpu = (1 - alpha)*m_current_frametime_cpu + alpha*stats.cpu_frame_time; 178 | m_current_frametime_gpu = (1 - alpha)*m_current_frametime_gpu + alpha*stats.gpu_frame_time; 179 | } 180 | } 181 | else 182 | { 183 | Sleep(1); 184 | } 185 | 186 | if (m_quit) 187 | { 188 | // FIXME: this isn't allowed.. ?? 189 | //window->Close(); 190 | //m_quit = false; 191 | } 192 | } 193 | } 194 | 195 | // Required for IFrameworkView. 196 | // Terminate events do not cause Uninitialize to be called. It will be called if your IFrameworkView 197 | // class is torn down while the app is in the foreground. 198 | void App::Uninitialize() 199 | { 200 | } 201 | 202 | // Application lifecycle event handlers. 203 | 204 | void App::OnActivated(CoreApplicationView^ applicationView, IActivatedEventArgs^ args) 205 | { 206 | // Run() won't start until the CoreWindow is activated. 207 | CoreWindow::GetForCurrentThread()->Activate(); 208 | } 209 | 210 | void App::OnSuspending(Platform::Object^ sender, SuspendingEventArgs^ args) 211 | { 212 | // Save app state asynchronously after requesting a deferral. Holding a deferral 213 | // indicates that the application is busy performing suspending operations. Be 214 | // aware that a deferral may not be held indefinitely. After about five seconds, 215 | // the app will be forced to exit. 216 | /*SuspendingDeferral^ deferral = args->SuspendingOperation->GetDeferral(); 217 | 218 | create_task([this, deferral]() 219 | { 220 | // TODO: Insert your code here. 221 | //m_sceneRenderer->SaveState(); 222 | 223 | deferral->Complete(); 224 | });*/ 225 | 226 | //ApplicationData.Current.LocalSettings 227 | 228 | serialize_swapchain_options(true); 229 | 230 | trim_dx12(); 231 | } 232 | 233 | void App::OnResuming(Platform::Object^ sender, Platform::Object^ args) 234 | { 235 | // Restore any data or state that was unloaded on suspend. By default, data 236 | // and state are persisted when resuming from suspend. Note that this event 237 | // does not occur if the app was previously terminated. 238 | 239 | // TODO: Replace this with your app's resuming logic. 240 | } 241 | 242 | // Window event handlers. 243 | 244 | void App::OnWindowClosed(CoreWindow^ sender, CoreWindowEventArgs^ args) 245 | { 246 | m_windowClosed = true; 247 | } 248 | 249 | void App::OnKeyDown(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::KeyEventArgs^ args) 250 | { 251 | bool controlDown = (sender->GetKeyState(VirtualKey::Control) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down; 252 | auto vkey = args->VirtualKey; 253 | 254 | auto status = args->KeyStatus; 255 | if (status.RepeatCount > 1) { 256 | return; 257 | } 258 | 259 | if (vkey == VirtualKey::F11) { 260 | auto applicationView = Windows::UI::ViewManagement::ApplicationView::GetForCurrentView(); 261 | if (applicationView->IsFullScreenMode) { 262 | applicationView->ExitFullScreenMode(); 263 | } else { 264 | applicationView->TryEnterFullScreenMode(); 265 | } 266 | m_fullscreen = applicationView->IsFullScreenMode; 267 | } 268 | else if (vkey == VirtualKey::K && controlDown) { 269 | m_vsync = !m_vsync; 270 | } 271 | else if (vkey == VirtualKey::W && controlDown) { 272 | m_swapchain_opts.create_time.use_waitable_object = !m_swapchain_opts.create_time.use_waitable_object; 273 | } 274 | if (vkey == VirtualKey::Up) { 275 | if(controlDown){ 276 | m_swapchain_opts.any_time.cpu_draw_ms = std::min(m_swapchain_opts.any_time.cpu_draw_ms + 1, 33); 277 | } 278 | else { 279 | m_swapchain_opts.any_time.overdraw_factor = std::min(m_swapchain_opts.any_time.overdraw_factor + 0.5f, 20); 280 | } 281 | } 282 | else if (vkey == VirtualKey::Down) { 283 | if(controlDown){ 284 | m_swapchain_opts.any_time.cpu_draw_ms = std::max(0, m_swapchain_opts.any_time.cpu_draw_ms - 1); 285 | } 286 | else { 287 | m_swapchain_opts.any_time.overdraw_factor = std::max(0, m_swapchain_opts.any_time.overdraw_factor - 0.5f); 288 | } 289 | } 290 | else if ((int)vkey == VK_OEM_MINUS) { 291 | if(controlDown){ 292 | m_swapchain_opts.create_time.max_frame_latency = std::max(1, m_swapchain_opts.create_time.max_frame_latency - 1); 293 | } 294 | else { 295 | m_swapchain_opts.create_time.swapchain_buffer_count = std::max(2, m_swapchain_opts.create_time.swapchain_buffer_count - 1); 296 | } 297 | } 298 | else if ((int)vkey == VK_OEM_PLUS) { 299 | if(controlDown){ 300 | m_swapchain_opts.create_time.max_frame_latency = std::min(8, m_swapchain_opts.create_time.max_frame_latency + 1); 301 | } 302 | else { 303 | m_swapchain_opts.create_time.swapchain_buffer_count = std::min(8, m_swapchain_opts.create_time.swapchain_buffer_count + 1); 304 | } 305 | } 306 | else if ((int)vkey == VK_OEM_4 /*[*/) { 307 | m_swapchain_opts.create_time.gpu_frame_count = std::max(1, m_swapchain_opts.create_time.gpu_frame_count - 1); 308 | } 309 | else if ((int)vkey == VK_OEM_6 /*]*/) { 310 | m_swapchain_opts.create_time.gpu_frame_count = std::min(8, m_swapchain_opts.create_time.gpu_frame_count + 1); 311 | } 312 | else if (vkey == VirtualKey::Tab) { 313 | m_swapchain_opts.inject.cpu_hiccup_count = 1; 314 | m_swapchain_opts.inject.cpu_hiccup_size = 10; 315 | } 316 | else if (vkey == VirtualKey::Escape) 317 | { 318 | m_quit = true; 319 | } 320 | else if (vkey == VirtualKey::Space) 321 | { 322 | m_action.toggle_pause = true; 323 | } 324 | } 325 | 326 | void App::OnKeyUp(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::KeyEventArgs^ args) 327 | { 328 | } 329 | 330 | void FlipModelUniversal::App::update_fps() 331 | { 332 | int64_t now = GetTickCount64(); 333 | int64_t then = m_fps_frame_timestamps[m_fps_frame_timestamp_write_index]; 334 | m_fps_frame_timestamps[m_fps_frame_timestamp_write_index] = now; 335 | m_fps_frame_timestamp_write_index = (m_fps_frame_timestamp_write_index + 1) & (FRAMETIME_BUFFER_LENGTH - 1); 336 | 337 | if (now > then) 338 | { 339 | if (FPS_UPDATES_PER_SECOND*(now - m_fps_last_update) > 1000) 340 | { 341 | float fps = (1000.0f*FRAMETIME_BUFFER_LENGTH) / (now - then); 342 | m_fps_last_update = now; 343 | m_current_fps = fps; 344 | m_current_fps_cpu = 1.0f / m_current_frametime_cpu; 345 | m_current_fps_gpu = 1.0f / m_current_frametime_gpu; 346 | } 347 | } 348 | else 349 | { 350 | m_current_fps = 0; 351 | } 352 | } 353 | 354 | static void serialize( 355 | Windows::Foundation::Collections::IPropertySet ^props, 356 | bool write, Platform::String^ key, 357 | int& value, int default_value) 358 | { 359 | if (write) { 360 | props->Insert(key, value); 361 | } 362 | else { 363 | value = default_value; 364 | auto entry = props->Lookup(key); 365 | if (entry) { 366 | auto propVal = dynamic_cast(entry); 367 | if (propVal) { 368 | value = propVal->GetInt32(); 369 | } 370 | } 371 | } 372 | }; 373 | 374 | static void serialize( 375 | Windows::Foundation::Collections::IPropertySet ^props, 376 | bool write, Platform::String^ key, 377 | float& value, float default_value) 378 | { 379 | if (write) { 380 | props->Insert(key, value); 381 | } 382 | else { 383 | value = default_value; 384 | auto entry = props->Lookup(key); 385 | if (entry) { 386 | auto propVal = dynamic_cast(entry); 387 | if (propVal) { 388 | value = (float)propVal->GetDouble(); 389 | } 390 | } 391 | } 392 | }; 393 | 394 | void App::serialize_swapchain_options(bool write) 395 | { 396 | auto settings = Windows::Storage::ApplicationData::Current->LocalSettings->Values; 397 | auto opts = &m_swapchain_opts; 398 | 399 | serialize(settings, write, "overdraw_factor", opts->any_time.overdraw_factor, 8.0f); 400 | serialize(settings, write, "cpu_draw_ms", opts->any_time.cpu_draw_ms, 8); 401 | serialize(settings, write, "use_waitable_object", opts->create_time.use_waitable_object, 1); 402 | serialize(settings, write, "max_frame_latency", opts->create_time.max_frame_latency, 2); 403 | serialize(settings, write, "swapchain_buffer_count", opts->create_time.swapchain_buffer_count, 3); 404 | serialize(settings, write, "gpu_frame_count", opts->create_time.gpu_frame_count, 2); 405 | } 406 | -------------------------------------------------------------------------------- /Source/App.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2017 Intel Corporation 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | // use this file except in compliance with the License. You may obtain a copy 6 | // of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | //////////////////////////////////////////////////////////////////////////////// 16 | #pragma once 17 | 18 | #define NOMINMAX 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include "sample_game.hpp" 26 | #include "sample_dx12.hpp" 27 | 28 | namespace FlipModelUniversal 29 | { 30 | // Main entry point for our app. Connects the app with the Windows shell and handles application lifecycle events. 31 | ref class App sealed : public Windows::ApplicationModel::Core::IFrameworkView 32 | { 33 | public: 34 | 35 | // IFrameworkView methods. 36 | virtual void Initialize(Windows::ApplicationModel::Core::CoreApplicationView^ applicationView); 37 | virtual void SetWindow(Windows::UI::Core::CoreWindow^ window); 38 | virtual void Load(Platform::String^ entryPoint); 39 | virtual void Run(); 40 | virtual void Uninitialize(); 41 | 42 | protected: 43 | // Application lifecycle event handlers. 44 | void OnActivated(Windows::ApplicationModel::Core::CoreApplicationView^ applicationView, Windows::ApplicationModel::Activation::IActivatedEventArgs^ args); 45 | void OnSuspending(Platform::Object^ sender, Windows::ApplicationModel::SuspendingEventArgs^ args); 46 | void OnResuming(Platform::Object^ sender, Platform::Object^ args); 47 | 48 | // Window event handlers. 49 | void OnWindowClosed(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::CoreWindowEventArgs^ args); 50 | 51 | // Input event handlers. 52 | void OnKeyDown(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::KeyEventArgs^ args); 53 | void OnKeyUp(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::KeyEventArgs^ args); 54 | 55 | private: 56 | 57 | void update_fps(); 58 | void serialize_swapchain_options(bool write); 59 | 60 | bool m_initialized = 0; 61 | bool m_quit = 0; 62 | bool m_fullscreen = 0; 63 | 64 | game_data m_game = { 0 }; 65 | game_command m_action = { 0 }; 66 | dx12_swapchain_options m_swapchain_opts = { 0 }; 67 | 68 | enum { 69 | FRAMETIME_BUFFER_LENGTH = 32, 70 | FPS_UPDATES_PER_SECOND = 4 71 | }; 72 | 73 | int64_t m_fps_frame_timestamps[FRAMETIME_BUFFER_LENGTH] = { 0 }; 74 | int64_t m_fps_frame_timestamp_write_index = 0; 75 | int64_t m_fps_last_update = 0; 76 | 77 | float m_current_fps = 0, m_current_fps_cpu = 0, m_current_fps_gpu = 0; 78 | float m_current_frametime_cpu = 0, m_current_frametime_gpu = 0; 79 | float m_frame_latency = 0; 80 | float m_frame_latency_stddev = 0, m_frame_latency_minmaxd = 0; 81 | 82 | bool m_vsync = 1; 83 | 84 | float m_windowWidthDips = 0; 85 | float m_windowHeightDips = 0; 86 | 87 | float m_windowDpi = 0; 88 | 89 | bool m_windowClosed = 0; 90 | bool m_windowVisible = 1; 91 | }; 92 | } 93 | -------------------------------------------------------------------------------- /Source/DX12Helpers.cpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2017 Intel Corporation 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | // use this file except in compliance with the License. You may obtain a copy 6 | // of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | //////////////////////////////////////////////////////////////////////////////// 16 | #include "DX12Helpers.hpp" 17 | 18 | #if D3D12_DYNAMIC_LINK 19 | 20 | HMODULE hModuleD3D12; 21 | D3D12DllExports D3D12; 22 | BOOL LoadD3D12Dll(BOOL Load) 23 | { 24 | // Already in desired state 25 | if(D3D12.loaded == Load) 26 | { 27 | return TRUE; 28 | } 29 | 30 | // Unloading 31 | if(!Load) 32 | { 33 | assert(hModuleD3D12); 34 | FreeLibrary(hModuleD3D12); 35 | ZeroMemory(&D3D12, sizeof(D3D12)); 36 | return TRUE; 37 | } 38 | 39 | // Loading 40 | hModuleD3D12 = LoadLibrary("D3D12"); 41 | if(!hModuleD3D12) 42 | { 43 | return FALSE; 44 | } 45 | 46 | *(FARPROC*)&D3D12.CreateDevice = GetProcAddress(hModuleD3D12, "D3D12CreateDevice"); 47 | *(FARPROC*)&D3D12.GetDebugInterface = GetProcAddress(hModuleD3D12, "D3D12GetDebugInterface"); 48 | *(FARPROC*)&D3D12.SerializeRootSignature = GetProcAddress(hModuleD3D12, "D3D12SerializeRootSignature"); 49 | *(FARPROC*)&D3D12.CreateRootSignatureDeserializer = GetProcAddress(hModuleD3D12, "D3D12CreateRootSignatureDeserializer"); 50 | 51 | if(!D3D12.CreateDevice || 52 | !D3D12.GetDebugInterface || 53 | !D3D12.SerializeRootSignature || 54 | !D3D12.CreateRootSignatureDeserializer) 55 | { 56 | FreeLibrary(hModuleD3D12); 57 | ZeroMemory(&D3D12, sizeof(D3D12)); 58 | return FALSE; 59 | } 60 | 61 | D3D12.loaded = TRUE; 62 | 63 | return TRUE; 64 | } 65 | 66 | #endif // #if D3D12_DYNAMIC_LINK 67 | 68 | void SetNameV(ID3D12Object *obj, const char *fmt, va_list args) 69 | { 70 | char buf[256]; 71 | int len = vsnprintf(buf, sizeof(buf), fmt, args); 72 | std::wstring temp(buf, buf+len); 73 | obj->SetName(temp.c_str()); 74 | } 75 | 76 | UINT64 WaitForFence(ID3D12Fence *Fence, HANDLE FenceEvent, UINT64 WaitValue) 77 | { 78 | UINT64 CompletedValue; 79 | while((CompletedValue = Fence->GetCompletedValue()) < WaitValue) 80 | { 81 | if(FenceEvent) 82 | { 83 | WaitForSingleObject(FenceEvent, INFINITE); 84 | } 85 | else 86 | { 87 | Sleep(1); 88 | } 89 | } 90 | return CompletedValue; 91 | } 92 | 93 | HRESULT UploadHeap::Initialize(ID3D12Device* device, UINT64 size) 94 | { 95 | this->~UploadHeap(); 96 | 97 | HRESULT hr; 98 | 99 | hr = device->CreateCommittedResource( 100 | &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD), 101 | D3D12_HEAP_FLAG_NONE, 102 | &CD3DX12_RESOURCE_DESC::Buffer(size), 103 | D3D12_RESOURCE_STATE_GENERIC_READ, 104 | nullptr, 105 | IID_PPV_ARGS(&mHeap)); 106 | if (FAILED(hr)) { 107 | return hr; 108 | } 109 | 110 | hr = mHeap->Map(0, nullptr, reinterpret_cast(&mHeapWO)); 111 | 112 | return hr; 113 | } 114 | 115 | void DescriptorArray::Initialize(ID3D12Device *Device, D3D12_DESCRIPTOR_HEAP_TYPE HeapType, 116 | D3D12_DESCRIPTOR_HEAP_FLAGS HeapFlags, UINT Size) 117 | { 118 | this->~DescriptorArray(); 119 | 120 | mArraySize = Size; 121 | D3D12_DESCRIPTOR_HEAP_DESC HeapDesc; 122 | ZeroMemory(&HeapDesc, sizeof(HeapDesc)); 123 | HeapDesc.NumDescriptors = Size; 124 | HeapDesc.Type = HeapType; 125 | HeapDesc.Flags = HeapFlags; 126 | Device->CreateDescriptorHeap(&HeapDesc, IID_PPV_ARGS(&mHeap)); 127 | mElementSize = Device->GetDescriptorHandleIncrementSize(HeapType); 128 | mCpuBase = mHeap->GetCPUDescriptorHandleForHeapStart(); 129 | mGpuBase = mHeap->GetGPUDescriptorHandleForHeapStart(); 130 | } 131 | 132 | bool FrameQueue::Initialize( 133 | const char *DebugName, 134 | ID3D12Device *Device, ID3D12CommandQueue *CommandQueue, 135 | ID3D12PipelineState *InitialPipelineState, 136 | void *FrameUserData, UINT SizeofStructFrame, UINT FrameCount, 137 | ID3D11On12Device *Device11On12, 138 | ID2D1DeviceContext2 *D2DDeviceContext) 139 | { 140 | this->~FrameQueue(); 141 | 142 | mNextFrameFence = 1; 143 | mNextFrameIndex = 0; 144 | 145 | mDebugName = DebugName; 146 | mDevice = Device; 147 | mDevice11On12 = Device11On12; 148 | mD2DDeviceContext = D2DDeviceContext; 149 | mCommandQueue = CommandQueue; 150 | 151 | CheckHresult(Device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&mFence))); 152 | mFenceEvent.Initialize(); 153 | 154 | mFrames.resize(FrameCount); 155 | for(UINT i = 0; i < FrameCount; ++i) 156 | { 157 | FrameContext *Frame = &mFrames[i]; 158 | Frame->mFrameFenceId = 0; 159 | CheckHresult(Device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, 160 | IID_PPV_ARGS(&Frame->mCommandAllocator))); 161 | SetName(Frame->mCommandAllocator, "%s.Frame%d:CmdAlloc", DebugName, i); 162 | Frame->mUserData = (char*)FrameUserData + i*SizeofStructFrame; 163 | } 164 | 165 | return true; 166 | } 167 | 168 | bool FrameQueue::SetSwapChain( 169 | IDXGISwapChain2 *SwapChain, 170 | DXGI_FORMAT RenderTargetViewFormat, 171 | float dpiX, float dpiY) 172 | { 173 | if (!SwapChain) { 174 | mBackBuffers.clear(); 175 | return true; 176 | } 177 | 178 | DXGI_SWAP_CHAIN_DESC1 Desc1; 179 | CheckHresult(SwapChain->GetDesc1(&Desc1)); 180 | UINT ChainLength = Desc1.BufferCount; 181 | assert(ChainLength); 182 | 183 | CheckHresult(SwapChain->QueryInterface(mSwapChain.ReleaseAndGetAddressOf())); 184 | mBackBuffers.resize(ChainLength); 185 | mRenderTargetViews.Initialize(mDevice.Get(), D3D12_DESCRIPTOR_HEAP_FLAG_NONE, ChainLength); 186 | D3D12_RENDER_TARGET_VIEW_DESC RtvDesc = {}; 187 | RtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; 188 | RtvDesc.Format = RenderTargetViewFormat == DXGI_FORMAT_UNKNOWN ? Desc1.Format : RenderTargetViewFormat; 189 | 190 | D2D1_BITMAP_PROPERTIES1 bitmapProperties = D2D1::BitmapProperties1( 191 | D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW, 192 | D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED), 193 | dpiX, 194 | dpiY 195 | ); 196 | 197 | for(UINT i = 0; i < ChainLength; ++i) 198 | { 199 | auto& BufferResources = mBackBuffers[i]; 200 | 201 | CheckHresult(mSwapChain->GetBuffer(i, IID_PPV_ARGS(&BufferResources.mBuffer))); 202 | SetName(BufferResources.mBuffer.Get(), "%s.BackBuffer%d", mDebugName, i); 203 | mDevice->CreateRenderTargetView(BufferResources.mBuffer.Get(), &RtvDesc, mRenderTargetViews[i].CpuHandle); 204 | if (mDevice11On12) 205 | { 206 | D3D11_RESOURCE_FLAGS d3d11Flags = { D3D11_BIND_RENDER_TARGET }; 207 | CheckHresult(mDevice11On12->CreateWrappedResource( 208 | BufferResources.mBuffer.Get(), &d3d11Flags, D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT, 209 | IID_PPV_ARGS(&BufferResources.mWrapped11Buffer))); 210 | ComPtr surface; 211 | CheckHresult(BufferResources.mWrapped11Buffer.As(&surface)); 212 | CheckHresult(mD2DDeviceContext->CreateBitmapFromDxgiSurface( 213 | surface.Get(), 214 | &bitmapProperties, 215 | &BufferResources.mD2DRenderTarget 216 | )); 217 | } 218 | } 219 | 220 | return true; 221 | } 222 | 223 | void FrameQueue::BeginFrame(FrameContext **OutFrame) 224 | { 225 | assert(mSwapChain); 226 | 227 | // Get/Increment the fence counter 228 | UINT64 FrameFence = mNextFrameFence; 229 | mNextFrameFence = mNextFrameFence + 1; 230 | 231 | // Get/Increment the frame ring-buffer index 232 | UINT FrameIndex = mNextFrameIndex; 233 | mNextFrameIndex = (mNextFrameIndex + 1) % (UINT)mFrames.size(); 234 | 235 | // Wait for the last frame occupying this slot to be complete 236 | FrameContext *Frame = &mFrames[FrameIndex]; 237 | WaitForFence(mFence.Get(), mFenceEvent.Get(), Frame->mFrameFenceId); 238 | Frame->mFrameFenceId = FrameFence; 239 | 240 | // Associate the frame with the swap chain backbuffer & RTV. 241 | UINT BackBufferIndex = mSwapChain->GetCurrentBackBufferIndex(); 242 | auto& Resources = mBackBuffers[BackBufferIndex]; 243 | Frame->mBackBufferIndex = BackBufferIndex; 244 | Frame->mBackBuffer = Resources.mBuffer.Get(); 245 | Frame->mWrapped11BackBuffer = Resources.mWrapped11Buffer.Get(); 246 | Frame->mD2DRenderTarget = Resources.mD2DRenderTarget.Get(); 247 | Frame->mBackBufferRTV = mRenderTargetViews[BackBufferIndex].CpuHandle; 248 | 249 | // Reset the command allocator and list 250 | ThrowIfFailed(Frame->mCommandAllocator->Reset()); 251 | 252 | *OutFrame = Frame; 253 | } 254 | 255 | void FrameQueue::EndFrame(FrameContext *Frame) 256 | { 257 | // Signal that the frame is complete 258 | ThrowIfFailed(mFence->SetEventOnCompletion(Frame->mFrameFenceId, mFenceEvent.Get())); 259 | ThrowIfFailed(mCommandQueue->Signal(mFence.Get(), Frame->mFrameFenceId)); 260 | } 261 | -------------------------------------------------------------------------------- /Source/DX12Helpers.hpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2017 Intel Corporation 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | // use this file except in compliance with the License. You may obtain a copy 6 | // of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | //////////////////////////////////////////////////////////////////////////////// 16 | #pragma once 17 | 18 | #include "WindowsHelpers.hpp" 19 | #include "d3dx12.h" 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #if D3D12_DYNAMIC_LINK 26 | 27 | struct D3D12DllExports 28 | { 29 | BOOL loaded; 30 | PFN_D3D12_CREATE_DEVICE CreateDevice; 31 | PFN_D3D12_GET_DEBUG_INTERFACE GetDebugInterface; 32 | PFN_D3D12_SERIALIZE_ROOT_SIGNATURE SerializeRootSignature; 33 | PFN_D3D12_CREATE_ROOT_SIGNATURE_DESERIALIZER CreateRootSignatureDeserializer; 34 | }; 35 | 36 | extern D3D12DllExports D3D12; 37 | BOOL LoadD3D12Dll(BOOL Load); 38 | 39 | #define D3D12CreateDevice D3D12.CreateDevice 40 | #define D3D12GetDebugInterface D3D12.GetDebugInterface 41 | #define D3D12SerializeRootSignature D3D12.SerializeRootSignature 42 | #define D3D12CreateRootSignatureDeserializer D3D12.CreateRootSignatureDeserializer 43 | 44 | #endif // D3D12_DYNAMIC_LINK 45 | 46 | inline UINT AlignTo(UINT offset, UINT align) 47 | { 48 | assert(!(align & (align-1))); 49 | return (offset + (align-1)) &~ (align-1); 50 | } 51 | 52 | template 53 | inline D3D12_GPU_VIRTUAL_ADDRESS GetGpuAddress(D3D12_GPU_VIRTUAL_ADDRESS base, T* member, S* structure) 54 | { 55 | return base + (reinterpret_cast(member) - reinterpret_cast(structure)); 56 | } 57 | 58 | template 59 | inline D3D12_VERTEX_BUFFER_VIEW MakeVertexBufferView(D3D12_GPU_VIRTUAL_ADDRESS base, T* member, UINT size, S* structure) 60 | { 61 | D3D12_VERTEX_BUFFER_VIEW view; 62 | view.BufferLocation = GetGpuAddress(base, member, structure); 63 | view.SizeInBytes = size; 64 | view.StrideInBytes = sizeof(T); 65 | return view; 66 | } 67 | 68 | template 69 | inline D3D12_INDEX_BUFFER_VIEW MakeIndexBufferView(D3D12_GPU_VIRTUAL_ADDRESS base, T* member, UINT size, S* structure, DXGI_FORMAT format) 70 | { 71 | D3D12_INDEX_BUFFER_VIEW view; 72 | view.BufferLocation = GetGpuAddress(base, member, structure); 73 | view.SizeInBytes = size; 74 | view.Format = format; 75 | return view; 76 | } 77 | 78 | void SetNameV(ID3D12Object *obj, const char *fmt, va_list args); 79 | 80 | static void SetName(ID3D12Object *obj, const char *fmt, ...) 81 | { 82 | va_list args; 83 | va_start(args, fmt); 84 | SetNameV(obj, fmt, args); 85 | va_end(args); 86 | } 87 | 88 | template 89 | static void SetName(ComPtr& obj, const char *fmt, ...) 90 | { 91 | va_list args; 92 | va_start(args, fmt); 93 | SetNameV(obj.Get(), fmt, args); 94 | va_end(args); 95 | } 96 | 97 | static void SetName(IDXGIObject *obj, const char *name) 98 | { 99 | obj->SetPrivateData(WKPDID_D3DDebugObjectName, (UINT)strlen(name), name); 100 | } 101 | 102 | UINT64 WaitForFence(ID3D12Fence *Fence, HANDLE FenceEvent, UINT64 WaitValue); 103 | 104 | // Upload Heap: Untyped version 105 | struct UploadHeap 106 | { 107 | HRESULT Initialize(ID3D12Device* device, UINT64 size); 108 | 109 | ID3D12Resource* Heap() { return mHeap.Get(); } 110 | 111 | // Write-only! 112 | void* DataWO() { return mHeapWO; } 113 | 114 | private: 115 | ComPtr mHeap; 116 | void* mHeapWO = nullptr; 117 | }; 118 | 119 | // Upload Heap: Structure/typed version 120 | template 121 | struct UploadHeapT: UploadHeap 122 | { 123 | HRESULT Initialize(ID3D12Device *device) 124 | { 125 | return UploadHeap::Initialize(device, sizeof(T)); 126 | } 127 | 128 | T* DataWO() { return (T*)UploadHeap::DataWO(); } 129 | }; 130 | 131 | // Untyped version 132 | struct DescriptorArray 133 | { 134 | struct IndexValue { 135 | const D3D12_CPU_DESCRIPTOR_HANDLE CpuHandle; 136 | const D3D12_GPU_DESCRIPTOR_HANDLE GpuHandle; 137 | }; 138 | UINT Size() const { return mArraySize; } 139 | IndexValue operator[](UINT i) { 140 | assert(i >= 0 && i < mArraySize); 141 | return { CpuHandle(i), GpuHandle(i) }; 142 | } 143 | D3D12_CPU_DESCRIPTOR_HANDLE CpuHandle(UINT i) { 144 | auto h = mCpuBase; 145 | h.ptr += i*mElementSize; 146 | return h; 147 | } 148 | D3D12_GPU_DESCRIPTOR_HANDLE GpuHandle(UINT i) { 149 | auto h = mGpuBase; 150 | h.ptr += i*mElementSize; 151 | return h; 152 | } 153 | ID3D12DescriptorHeap *GetHeap() { return mHeap.Get(); } 154 | 155 | protected: 156 | void Initialize(ID3D12Device *Device, D3D12_DESCRIPTOR_HEAP_TYPE HeapType, 157 | D3D12_DESCRIPTOR_HEAP_FLAGS HeapFlags, UINT Size); 158 | 159 | private: 160 | ComPtr mHeap; 161 | UINT mArraySize; 162 | UINT mElementSize; 163 | D3D12_CPU_DESCRIPTOR_HANDLE mCpuBase; 164 | D3D12_GPU_DESCRIPTOR_HANDLE mGpuBase; 165 | }; 166 | 167 | template 168 | struct DescriptorArrayT : DescriptorArray 169 | { 170 | void Initialize(ID3D12Device *Device, D3D12_DESCRIPTOR_HEAP_FLAGS HeapFlags, UINT Size) 171 | { 172 | DescriptorArray::Initialize(Device, HeapType, HeapFlags, Size); 173 | } 174 | }; 175 | 176 | /* How to use FrameQueue: 177 | 178 | init: 179 | 180 | FrameQueue frameQueue; 181 | MyFrameData myFrames[2]; 182 | frameQueue.Initialize(..., &frames, sizeof(MyFrameData), ARRAY_SIZE(myFrames)); 183 | 184 | render: 185 | 186 | FrameContext *Frame; 187 | frameQueue.BeginFrame(&frame); 188 | RenderFrame(Frame, FrameData); 189 | frameQueue.EndFrame(Frame); 190 | */ 191 | 192 | struct FrameQueue 193 | { 194 | bool Initialize( 195 | const char *DebugName, 196 | ID3D12Device *Device, ID3D12CommandQueue *CommandQueue, 197 | ID3D12PipelineState *InitialPipelineState, 198 | void *FrameUserData, UINT SizeofStructFrame, UINT FrameCount, 199 | ID3D11On12Device *Device11On12 = nullptr, 200 | ID2D1DeviceContext2 *D2DDeviceContext = nullptr); 201 | 202 | bool SetSwapChain(IDXGISwapChain2 *SwapChain, 203 | DXGI_FORMAT RenderTargetViewFormat = DXGI_FORMAT_UNKNOWN, 204 | float dpiX = 0, 205 | float dpiY = 0); 206 | 207 | struct FrameContext 208 | { 209 | friend struct FrameQueue; 210 | ID3D12Resource *mBackBuffer; 211 | ID3D11Resource *mWrapped11BackBuffer; 212 | ID2D1Bitmap1 *mD2DRenderTarget; 213 | D3D12_CPU_DESCRIPTOR_HANDLE mBackBufferRTV; 214 | ComPtr mCommandAllocator; 215 | UINT mBackBufferIndex; 216 | template T *CastUserDataAs() { 217 | return static_cast(mUserData); 218 | } 219 | void FlushCommandList() { 220 | 221 | } 222 | private: 223 | bool mCommandListOpen; 224 | void *mUserData; 225 | UINT64 mFrameFenceId; 226 | }; 227 | 228 | // Handle fencing 229 | void BeginFrame(FrameContext **Frame); 230 | void EndFrame(FrameContext *Frame); 231 | 232 | FrameContext *GetFrameContext(int i) { 233 | return &mFrames[i]; 234 | } 235 | 236 | private: 237 | const char *mDebugName; 238 | ComPtr mDevice; 239 | ComPtr mDevice11On12; 240 | ComPtr mD2DDeviceContext; 241 | ComPtr mSwapChain; 242 | ComPtr mCommandQueue; 243 | 244 | UINT64 mNextFrameFence; 245 | ComPtr mFence; 246 | WindowsEvent mFenceEvent; 247 | 248 | UINT mNextFrameIndex; 249 | std::vector mFrames; 250 | 251 | struct BackBufferResources { 252 | ComPtr mBuffer; 253 | ComPtr mWrapped11Buffer; 254 | ComPtr mD2DRenderTarget; 255 | }; 256 | 257 | std::vector mBackBuffers; 258 | DescriptorArrayT mRenderTargetViews; 259 | }; 260 | 261 | struct TimestampQueryHeap 262 | { 263 | bool Initialize(ID3D12Device *Device, UINT TimestampCount) 264 | { 265 | D3D12_QUERY_HEAP_DESC QueryHeapDesc; 266 | QueryHeapDesc.Count = TimestampCount; 267 | QueryHeapDesc.NodeMask = 0; 268 | QueryHeapDesc.Type = D3D12_QUERY_HEAP_TYPE_TIMESTAMP; 269 | CheckHresult(Device->CreateQueryHeap(&QueryHeapDesc, IID_PPV_ARGS(&mQueryHeap))); 270 | 271 | UINT BufferSize = sizeof(UINT64)*TimestampCount; 272 | D3D12_RESOURCE_DESC ReadbackBufferDesc = CD3DX12_RESOURCE_DESC::Buffer(BufferSize); 273 | D3D12_HEAP_PROPERTIES ReadbackBufferHeapProps = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_READBACK); 274 | CheckHresult(Device->CreateCommittedResource(&ReadbackBufferHeapProps, D3D12_HEAP_FLAG_NONE, 275 | &ReadbackBufferDesc, D3D12_RESOURCE_STATE_COPY_DEST, NULL, IID_PPV_ARGS(&mReadbackBuffer))); 276 | 277 | return true; 278 | } 279 | 280 | void QueryTimestampCommand(ID3D12GraphicsCommandList *CommandList, UINT TimestampIndex) 281 | { 282 | CommandList->EndQuery(mQueryHeap.Get(), D3D12_QUERY_TYPE_TIMESTAMP, TimestampIndex); 283 | } 284 | 285 | void ReadTimestampQueryCommand(ID3D12GraphicsCommandList *CommandList, UINT StartIndex, UINT NumQueries) 286 | { 287 | CommandList->ResolveQueryData(mQueryHeap.Get(), D3D12_QUERY_TYPE_TIMESTAMP, 288 | StartIndex, NumQueries, mReadbackBuffer.Get(), StartIndex*sizeof(UINT64)); 289 | } 290 | 291 | void Readback(void *Dest, UINT Size) 292 | { 293 | void *Source; 294 | D3D12_RANGE Range; 295 | Range.Begin = 0; 296 | Range.End = Size; 297 | ThrowIfFailed(mReadbackBuffer->Map(0, &Range, &Source)); 298 | memcpy(Dest, Source, Size); 299 | mReadbackBuffer->Unmap(0, NULL); 300 | } 301 | 302 | private: 303 | 304 | ComPtr mQueryHeap; 305 | ComPtr mReadbackBuffer; 306 | }; 307 | 308 | template 309 | struct TimestampQueryHeapT 310 | { 311 | enum { NumberOfTimestampsPerStruct = sizeof(FrameTimestamps) / sizeof(UINT64) }; 312 | 313 | struct QueryScope { 314 | QueryScope(TimestampQueryHeapT *Parent, 315 | ID3D12GraphicsCommandList *CommandList, 316 | const UINT64(&Timestamps)[2]) 317 | : mParent(Parent) 318 | , mCommandList(CommandList) 319 | , mTimestamps(Timestamps) 320 | { 321 | mParent->QueryTimestampCommand(mCommandList, &mTimestamps[0]); 322 | } 323 | 324 | ~QueryScope() 325 | { 326 | mParent->QueryTimestampCommand(mCommandList, &mTimestamps[1]); 327 | } 328 | 329 | TimestampQueryHeapT *mParent; 330 | ID3D12GraphicsCommandList *mCommandList; 331 | const FrameTimestamps *mTimestampStruct; 332 | const UINT64(&mTimestamps)[2]; 333 | }; 334 | 335 | bool Initialize(ID3D12Device *Device) 336 | { 337 | return mQueryHeap.Initialize(Device, NumberOfTimestampsPerStruct); 338 | } 339 | 340 | const FrameTimestamps *GetTimestampWriteStruct() 341 | { 342 | return 0; 343 | } 344 | 345 | const FrameTimestamps *GetTimestampReadStruct(bool refresh) 346 | { 347 | if (refresh) { 348 | mQueryHeap.Readback(&mLastReadback, sizeof(FrameTimestamps)); 349 | } 350 | return &mLastReadback; 351 | } 352 | 353 | void QueryTimestampCommand(ID3D12GraphicsCommandList *CommandList, const UINT64 *Timestamp) 354 | { 355 | UINT TimestampIndex = (UINT)(Timestamp - (UINT64*)0); 356 | assert(TimestampIndex*sizeof(UINT64) <= sizeof(FrameTimestamps)); 357 | mQueryHeap.QueryTimestampCommand(CommandList, TimestampIndex); 358 | } 359 | 360 | void ReadbackCommand(ID3D12GraphicsCommandList *CommandList) 361 | { 362 | mQueryHeap.ReadTimestampQueryCommand(CommandList, 0, NumberOfTimestampsPerStruct); 363 | } 364 | 365 | private: 366 | TimestampQueryHeap mQueryHeap; 367 | FrameTimestamps mLastReadback; 368 | }; 369 | -------------------------------------------------------------------------------- /Source/EventViz.cpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2017 Intel Corporation 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | // use this file except in compliance with the License. You may obtain a copy 6 | // of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | //////////////////////////////////////////////////////////////////////////////// 16 | #include "EventViz.hpp" 17 | #include 18 | #include 19 | 20 | namespace EventViz { 21 | 22 | const char *kVsyncQueue = "Vsync"; 23 | const char *kPresentQueue = "Present"; 24 | const char *kGpuQueue = "GPU"; 25 | const char *kCpuQueue = "CPU"; 26 | 27 | template 28 | inline bool IntervalsIntersect( 29 | T a, T b, T c, T d) 30 | { 31 | assert(a <= b); 32 | assert(c <= d); 33 | 34 | // ([a,b] & [c,d]) not empty 35 | return !(b < c || a > d); 36 | } 37 | 38 | EventStream::EventStream() 39 | { 40 | 41 | } 42 | 43 | EventStream::~EventStream() 44 | { 45 | 46 | } 47 | 48 | void EventStream::Trim(UINT64 MaxStartTime) 49 | { 50 | // Trim Vsyncs 51 | { 52 | auto Pred = [=](const EventData *e, const UINT64 Val) { return e->Start < Val; }; 53 | auto Begin = Vsyncs.begin(); 54 | auto End = std::lower_bound(Begin, Vsyncs.end(), MaxStartTime, Pred); // first element NOT < MaxStartTime 55 | Vsyncs.erase(Begin, End); // erase [Begin, End) 56 | } 57 | 58 | // Trim Events 59 | { 60 | auto Begin = AllEvents.begin(); 61 | auto End = AllEvents.lower_bound(MaxStartTime); // first element NOT < MaxStartTime 62 | AllEvents.erase(Begin, End); // erase [Begin, End) 63 | } 64 | } 65 | 66 | void EventStream::TrimToLastNSeconds(UINT Seconds) 67 | { 68 | UINT64 MaxStartTime = QpcNow() - Seconds*g_QpcFreq; 69 | Trim(MaxStartTime); 70 | } 71 | 72 | void EventStream::TrimToLastNVsyncs(UINT N) 73 | { 74 | UINT VsyncCount = GetVsyncCount(); 75 | if (VsyncCount <= N) { 76 | return; 77 | } 78 | Trim(Vsyncs[VsyncCount - N]->Start); 79 | } 80 | 81 | EventData *EventStream::Start(const char *Queue, const void *UserData, UINT64 UserID, UINT64 Time) 82 | { 83 | if (paused) return NULL; 84 | 85 | assert(Queue); 86 | 87 | Time = Time ? Time : QpcNow(); 88 | 89 | auto Event = new EventData{ Queue, UserData, UserID, Time, 0 }; 90 | AllEvents.emplace(Time, Event); 91 | 92 | return Event; 93 | } 94 | 95 | void EventStream::End(EventData *Data, UINT64 Time) 96 | { 97 | if (!Data || paused) return; 98 | 99 | Data->End = Time ? Time : QpcNow(); 100 | } 101 | 102 | void EventStream::InsertEvent(const char *Queue, UINT64 StartTime, UINT64 EndTime, const void *UserData, UINT64 UserID) 103 | { 104 | if (paused) return; 105 | 106 | assert(StartTime && EndTime >= StartTime); 107 | 108 | auto Event = new EventData{ Queue, UserData, UserID, StartTime, EndTime }; 109 | AllEvents.emplace(StartTime, Event); 110 | } 111 | 112 | void EventStream::Vsync(UINT64 Time) 113 | { 114 | if (paused) return; 115 | 116 | auto Event = EventStream::Start(kVsyncQueue, 0, 0, Time); 117 | EventStream::End(Event, Time); 118 | Vsyncs.push_back(Event); 119 | } 120 | 121 | UINT EventStream::GetVsyncCount() 122 | { 123 | return (UINT)Vsyncs.size(); 124 | } 125 | 126 | 127 | 128 | /// ---------------------------------------------------------------------- 129 | /// Drawing code 130 | /// ---------------------------------------------------------------------- 131 | 132 | enum { 133 | kQueueLineHeight = 33, 134 | kPaddingPixels = 33, 135 | }; 136 | 137 | void JoinTwoRectangles( 138 | std::vector& Lines, 139 | const EventViz::Rectangle& R1, 140 | const EventViz::Rectangle& R2) 141 | { 142 | float CX0 = (R1.Left + R1.Right) / 2; 143 | float CY0 = (R1.Top + R1.Bottom) / 2; 144 | float CX1 = (R2.Left + R2.Right) / 2; 145 | float CY1 = (R2.Top + R2.Bottom) / 2; 146 | 147 | if (1) 148 | { 149 | // Direct line between rectangle centers 150 | Lines.push_back({ CX0, CY0, CX1, CY1 }); 151 | } 152 | else 153 | { 154 | // Z-shaped line with 3 segments 155 | float CY = (CY0 + CY1) / 2; 156 | Lines.push_back({ CX0, CY0, CX0, CY }); 157 | Lines.push_back({ CX0, CY, CX1, CY }); 158 | Lines.push_back({ CX1, CY, CX1, CY1 }); 159 | } 160 | } 161 | 162 | void ConnectTheDots( 163 | EventViz::EventVisualization& Visualization, 164 | size_t Begin1, size_t End1, 165 | size_t Begin2, size_t End2, 166 | bool PrimaryOnly) 167 | { 168 | auto Rectangles = Visualization.Rectangles.data(); 169 | for (size_t i = Begin1; i < End1; ++i) { 170 | for (size_t j = std::max(i, Begin2); j < End2; ++j) { 171 | auto& R1 = Rectangles[i]; 172 | auto& R2 = Rectangles[j]; 173 | if (R1.Event->UserID == R2.Event->UserID && 174 | (!PrimaryOnly || (R1.Flags & R2.Flags & Rectangle::Primary)) && 175 | (!(R1.Sequence | R2.Sequence) || abs(R1.Sequence-R2.Sequence) == 1)) 176 | { 177 | // Create lines between the two primary rectangles for this event 178 | JoinTwoRectangles(Visualization.Lines, R1, R2); 179 | } 180 | } 181 | } 182 | } 183 | 184 | void LayoutLinearQueue(std::vector& Rectangles, 185 | EventViz::EventSet Events, UINT64 StartTime, UINT64 EndTime, float TimeToPixels, FloatRect Rect) 186 | { 187 | size_t EventCount = Events.size(); 188 | auto EventPtrs = Events.data(); 189 | for (size_t i = 0; i < EventCount; ++i) 190 | { 191 | EventData *Event = EventPtrs[i]; 192 | float X0 = Rect.Left + TimeToPixels * INT64(Event->Start - StartTime); 193 | float X1 = Rect.Left + TimeToPixels * INT64(Event->End - StartTime); 194 | Rectangles.push_back( { X0, Rect.Top, X1, Rect.Bottom, Event, true } ); 195 | } 196 | } 197 | 198 | void ComputeStackedQueueColumns( 199 | const std::vector& Events, // should be sorted by start time 200 | const std::vector& VsyncTimes, // should be sorted by time 201 | std::vector>& Stacks, 202 | UINT *MaxStackSize) 203 | { 204 | // N * (log(K)+C) 205 | // Each stack is in chronological (start time) order 206 | 207 | // Scatter each event into every interval that it intersects 208 | 209 | *MaxStackSize = 0; 210 | 211 | if (VsyncTimes.size() < 2) { 212 | return; 213 | } 214 | 215 | int K = (int)VsyncTimes.size(); 216 | auto Vsyncs = VsyncTimes.data(); 217 | 218 | Stacks.resize(K-1); 219 | 220 | UINT BiggestStack = 0; 221 | 222 | int EventCount = (int)Events.size(); 223 | for (int i = 0; i < EventCount; ++i) 224 | { 225 | auto Event = Events[i]; 226 | auto StartTime = Event->Start; 227 | auto EndTime = Event->End == UINT64_MAX ? Event->Start : Event->End; 228 | auto FirstVsyncPtr = std::lower_bound(Vsyncs, Vsyncs + K, StartTime); // First Vsync that is >= StartTime 229 | auto LastVsyncPtr = std::upper_bound(Vsyncs, Vsyncs + K, EndTime); // First Vysnc that is > EndTime 230 | 231 | int FirstVsyncIndex = std::max(0, int(FirstVsyncPtr - Vsyncs - 1)); 232 | int LastVsyncIndex = std::min(K-1, int(LastVsyncPtr - Vsyncs)); 233 | 234 | for (int i = FirstVsyncIndex; i < LastVsyncIndex; ++i) 235 | { 236 | if (Vsyncs[i] >= EndTime) { 237 | continue; 238 | } 239 | assert(IntervalsIntersect(StartTime, EndTime, Vsyncs[i], Vsyncs[i + 1])); 240 | Stacks[i].push_back(Event); 241 | UINT StackSize = (UINT)Stacks[i].size(); 242 | BiggestStack = std::max(BiggestStack, StackSize); 243 | } 244 | } 245 | 246 | *MaxStackSize = BiggestStack; 247 | } 248 | 249 | void LayoutStackedQueue( 250 | std::map& SequenceCounter, 251 | std::vector& Rectangles, 252 | const std::vector>& Stacks, 253 | const std::vector& VsyncTimes, 254 | UINT64 StartTime, UINT64 EndTime, 255 | float TimeToPixels, FloatRect Rect, UINT Rows) 256 | { 257 | // for each interval: 258 | // find all entries overlapping the interval 259 | // draw them from bottom to top, least recent to most recent 260 | 261 | size_t K = Stacks.size(); 262 | if (!K) return; 263 | 264 | assert(VsyncTimes.size() == K + 1); 265 | 266 | for (size_t i = 0; i < K; ++i) 267 | { 268 | auto& Stack = Stacks[i]; 269 | // from bottom to top 270 | UINT64 IntervalStart = VsyncTimes[i]; 271 | UINT64 IntervalEnd = VsyncTimes[i + 1]; 272 | UINT StackSize = (UINT)Stack.size(); 273 | UINT Lift = Rows - StackSize; 274 | for (UINT j = 0; j < StackSize; ++j) { 275 | // The END of the stack contains the most recent item, which needs to go at the bottom. 276 | auto Event = Stack[StackSize-1-j]; 277 | auto BlockStart = std::max(Event->Start, IntervalStart); 278 | auto BlockEnd = std::min(Event->End, IntervalEnd); 279 | float Y0 = Rect.Bottom-(j+1+Lift)*kQueueLineHeight; 280 | float Y1 = Rect.Bottom -(j+Lift)*kQueueLineHeight; 281 | float X0 = Rect.Left + TimeToPixels * INT64(BlockStart - StartTime); 282 | float X1 = Rect.Left + TimeToPixels * INT64(BlockEnd - StartTime); 283 | int Flags = 0; 284 | 285 | auto& SequenceIndex = SequenceCounter[Event->UserID]; 286 | SequenceIndex += 1; 287 | #if 0 288 | // last rectangle before presenting is Primary 289 | if (Event->End <= IntervalEnd && Event->End > IntervalStart) { 290 | Flags |= Rectangle::Primary; 291 | } 292 | #else 293 | // first rectangle entering the queue is Primary 294 | if (Event->Start >= IntervalStart && Event->Start <= IntervalEnd) { 295 | Flags |= Rectangle::Primary; 296 | } 297 | #endif 298 | if (Event->End == UINT64_MAX) { 299 | Flags |= Rectangle::Dropped; 300 | } 301 | Rectangles.push_back({ X0, Y0, X1, Y1, Event, Flags, SequenceIndex }); 302 | } 303 | } 304 | } 305 | 306 | void LayoutDisplayRectangles( 307 | std::map& SequenceCounter, 308 | std::vector& Rectangles, 309 | const std::vector>& PresentStacks, 310 | const std::vector& VsyncTimes, 311 | UINT64 StartTime, UINT64 EndTime, 312 | float TimeToPixels, FloatRect Rect) 313 | { 314 | size_t K = PresentStacks.size(); 315 | if (!K) return; 316 | 317 | assert(VsyncTimes.size() == K + 1); 318 | 319 | EventData *Display = 0; 320 | int Flags = 0; 321 | 322 | for (size_t i = 0; i < K; ++i) 323 | { 324 | if (Display) 325 | { 326 | UINT64 IntervalStart = VsyncTimes[i]; 327 | UINT64 IntervalEnd = VsyncTimes[i + 1]; 328 | float Y0 = Rect.Top; 329 | float Y1 = Rect.Bottom; 330 | float X0 = Rect.Left + TimeToPixels * INT64(IntervalStart - StartTime); 331 | float X1 = Rect.Left + TimeToPixels * INT64(IntervalEnd - StartTime); 332 | auto& SequenceIndex = SequenceCounter[Display->UserID]; 333 | SequenceIndex += 1; 334 | Rectangles.push_back({ X0, Y0, X1, Y1, Display, Flags, SequenceIndex }); 335 | Flags = 0; 336 | } 337 | 338 | auto& Stack = PresentStacks[i]; 339 | if (!Stack.empty()) 340 | { 341 | Display = Stack[0]; 342 | Flags = Rectangle::Primary; 343 | } 344 | } 345 | } 346 | 347 | void CreateVisualization(EventStream& Stream, UINT FirstVsync, UINT LastVsync, FloatRect ScreenRectInDips, EventVisualization& Visualization) 348 | { 349 | // We have three types of visualization: 350 | 351 | // Linear queues, Stacked queues, and Vsyncs. 352 | 353 | // The CPU & GPU queue are linear; The Present queue is stacked. 354 | // Vsyncs are represented as vertical lines. 355 | 356 | UINT VsyncCount = Stream.GetVsyncCount(); 357 | if (Stream.AllEvents.empty() || 358 | LastVsync <= FirstVsync || 359 | FirstVsync >= VsyncCount || 360 | LastVsync >= VsyncCount) 361 | { 362 | return; 363 | } 364 | 365 | UINT64 MaxSearchRadius = g_QpcFreq/4; // 1/4 second 366 | 367 | UINT64 StartTime = Stream.Vsyncs[FirstVsync]->Start; 368 | UINT64 EndTime = Stream.Vsyncs[LastVsync]->Start; 369 | 370 | auto First = Stream.AllEvents.lower_bound(StartTime - MaxSearchRadius); 371 | auto Last = Stream.AllEvents.upper_bound(EndTime + MaxSearchRadius); 372 | 373 | // FIXME: make these static? 374 | EventSet PresentQ, GpuQ, CpuQ; 375 | std::vector VsyncTimes; 376 | PresentQ.reserve(1000); 377 | GpuQ.reserve(1000); 378 | CpuQ.reserve(1000); 379 | for (auto it = First; it != Last; ++it) 380 | { 381 | EventData *Data = it->second.get(); 382 | if (Data->End && 383 | IntervalsIntersect( 384 | StartTime, EndTime, 385 | Data->Start, Data->End)) 386 | { 387 | if (Data->Queue == kPresentQueue) 388 | { 389 | PresentQ.push_back(Data); 390 | } 391 | else if (Data->Queue == kGpuQueue) 392 | { 393 | GpuQ.push_back(Data); 394 | } 395 | else if (Data->Queue == kCpuQueue) 396 | { 397 | CpuQ.push_back(Data); 398 | } 399 | else if (Data->Queue == kVsyncQueue) 400 | { 401 | VsyncTimes.push_back(Data->Start); 402 | } 403 | } 404 | } 405 | 406 | Visualization.Lines.clear(); 407 | Visualization.Rectangles.clear(); 408 | 409 | // Layout from the bottom up: CpuQ, GpuQ, PresentQ 410 | float y = ScreenRectInDips.Bottom; 411 | FloatRect Rect; 412 | 413 | Rect.Left = ScreenRectInDips.Left + kPaddingPixels; 414 | Rect.Right = ScreenRectInDips.Right - kPaddingPixels; 415 | 416 | UINT64 TimeWidth = EndTime - StartTime + 1; 417 | float PixelWidth = Rect.Right - Rect.Left + 1; 418 | float TimeToPixels = PixelWidth / TimeWidth; 419 | 420 | std::map SequenceCounter; 421 | 422 | // CpuQ 423 | size_t CpuRectBegin, CpuRectEnd; 424 | { 425 | Rect.Top = y - kQueueLineHeight; 426 | Rect.Bottom = y; 427 | CpuRectBegin = Visualization.Rectangles.size(); 428 | LayoutLinearQueue(Visualization.Rectangles, CpuQ, StartTime, EndTime, TimeToPixels, Rect); 429 | CpuRectEnd = Visualization.Rectangles.size(); 430 | y -= kQueueLineHeight + kPaddingPixels; 431 | } 432 | 433 | // GpuQ 434 | size_t GpuRectBegin, GpuRectEnd; 435 | { 436 | Rect.Top = y - kQueueLineHeight; 437 | Rect.Bottom = y; 438 | GpuRectBegin = Visualization.Rectangles.size(); 439 | LayoutLinearQueue(Visualization.Rectangles, GpuQ, StartTime, EndTime, TimeToPixels, Rect); 440 | GpuRectEnd = Visualization.Rectangles.size(); 441 | y -= kQueueLineHeight + kPaddingPixels; 442 | } 443 | 444 | // PresentQ 445 | size_t PresentRectBegin, PresentRectEnd; 446 | std::vector> PresentStacks; 447 | { 448 | UINT Rows; 449 | ComputeStackedQueueColumns(PresentQ, VsyncTimes, PresentStacks, &Rows); 450 | Rect.Top = y - Rows*kQueueLineHeight; 451 | Rect.Bottom = y; 452 | PresentRectBegin = Visualization.Rectangles.size(); 453 | LayoutStackedQueue(SequenceCounter, Visualization.Rectangles, PresentStacks, VsyncTimes, StartTime, EndTime, TimeToPixels, Rect, Rows); 454 | PresentRectEnd = Visualization.Rectangles.size(); 455 | y = Rect.Top - kPaddingPixels; 456 | } 457 | 458 | // DisplayQ: 459 | size_t DisplayRectBegin, DisplayRectEnd; 460 | { 461 | Rect.Top = y - kQueueLineHeight; 462 | Rect.Bottom = y; 463 | DisplayRectBegin = Visualization.Rectangles.size(); 464 | LayoutDisplayRectangles(SequenceCounter, Visualization.Rectangles, PresentStacks, VsyncTimes, StartTime, EndTime, TimeToPixels, Rect); 465 | DisplayRectEnd = Visualization.Rectangles.size(); 466 | } 467 | 468 | ConnectTheDots(Visualization, CpuRectBegin, CpuRectEnd, GpuRectBegin, GpuRectEnd, true); 469 | //ConnectTheDots(Visualization, GpuRectBegin, GpuRectEnd, PresentRectBegin, PresentRectEnd, true); 470 | 471 | ConnectTheDots(Visualization, CpuRectBegin, CpuRectEnd, PresentRectBegin, PresentRectEnd, true); 472 | ConnectTheDots(Visualization, PresentRectBegin, PresentRectEnd, PresentRectBegin, PresentRectEnd, false); 473 | ConnectTheDots(Visualization, PresentRectBegin, PresentRectEnd, DisplayRectBegin, DisplayRectEnd, false); 474 | 475 | // Vsyncs 476 | { 477 | float Y0 = Rect.Top; 478 | float Y1 = ScreenRectInDips.Bottom; 479 | for (size_t i = 0; i < VsyncTimes.size(); ++i) 480 | { 481 | float X0 = Rect.Left + TimeToPixels * (VsyncTimes[i] - StartTime); 482 | Visualization.Lines.push_back({ X0, Y0, X0, Y1 }); 483 | } 484 | } 485 | } 486 | 487 | } 488 | -------------------------------------------------------------------------------- /Source/EventViz.hpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2017 Intel Corporation 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | // use this file except in compliance with the License. You may obtain a copy 6 | // of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | //////////////////////////////////////////////////////////////////////////////// 16 | #pragma once 17 | 18 | #include "WindowsHelpers.hpp" 19 | #include 20 | #include 21 | #include 22 | 23 | #include "timeline_multimap.hpp" 24 | 25 | namespace EventViz 26 | { 27 | extern const char *kVsyncQueue; 28 | extern const char *kPresentQueue; 29 | extern const char *kGpuQueue; 30 | extern const char *kCpuQueue; 31 | 32 | struct EventData 33 | { 34 | const char *Queue; 35 | const void *UserData; 36 | UINT64 UserID; 37 | UINT64 Start; 38 | UINT64 End; 39 | }; 40 | 41 | typedef timeline_multimap> EventMapT; 42 | typedef std::vector EventSet; 43 | typedef std::pair VectorRange; 44 | typedef std::vector Partition; 45 | 46 | struct EventStream 47 | { 48 | EventStream(); 49 | ~EventStream(); 50 | 51 | void Pause(bool paused) { this->paused = paused; } 52 | 53 | void Trim(UINT64 MaxStartTime); 54 | void TrimToLastNSeconds(UINT Seconds); 55 | void TrimToLastNVsyncs(UINT Vsyncs); 56 | 57 | EventData *Start(const char *Queue, const void *UserData = 0, UINT64 UserID = 0, UINT64 Time = 0); 58 | void End(EventData *Data, UINT64 Time = 0); 59 | void InsertEvent(const char *Queue, UINT64 StartTime, UINT64 EndTime, const void *UserData = 0, UINT64 UserID = 0); 60 | void Vsync(UINT64 Time = 0); 61 | 62 | UINT GetVsyncCount(); 63 | 64 | EventMapT AllEvents; // keyed on/sorted by start time. 65 | std::deque Vsyncs; 66 | bool paused; 67 | }; 68 | 69 | struct Line { 70 | float X0, Y0, X1, Y1; 71 | }; 72 | 73 | struct FloatRect { 74 | float Left, Top, Right, Bottom; 75 | }; 76 | 77 | struct Rectangle { 78 | float Left, Top, Right, Bottom; 79 | EventData *Event; 80 | int Flags; 81 | int Sequence; 82 | 83 | enum FlagBits { 84 | Primary = 1, 85 | Dropped = 2, 86 | }; 87 | }; 88 | 89 | struct EventVisualization { 90 | std::vector Lines; 91 | std::vector Rectangles; 92 | }; 93 | 94 | void CreateVisualization(EventStream& Stream, UINT FirstVsync, 95 | UINT LastVsync, FloatRect Screen, EventVisualization& Visualization); 96 | } 97 | -------------------------------------------------------------------------------- /Source/PresentQueueStats.hpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2017 Intel Corporation 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | // use this file except in compliance with the License. You may obtain a copy 6 | // of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | //////////////////////////////////////////////////////////////////////////////// 16 | #pragma once 17 | 18 | #define NOMINMAX 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | struct PresentQueueStats 27 | { 28 | struct QueueEntry 29 | { 30 | UINT64 FrameBeginTime; 31 | UINT64 QueueEnteredTime; 32 | UINT64 PreRenderEstimatedSyncTime; 33 | UINT64 PresentTimeEstimatedSyncTime; 34 | UINT64 QueueExitedTime; 35 | void *UserData; 36 | UINT PresentID; 37 | BOOL Dropped; 38 | }; 39 | 40 | PresentQueueStats() 41 | { 42 | memset(this, 0, sizeof(*this)); 43 | } 44 | 45 | template 46 | HRESULT RetrieveStats( 47 | IDXGISwapChain1 *pSwapChain, 48 | DequeueEntry dequeue) 49 | { 50 | HRESULT hr; 51 | 52 | DXGI_FRAME_STATISTICS stats = { 0 }; 53 | while (SUCCEEDED(hr = pSwapChain->GetFrameStatistics(&stats)) && 54 | (stats.PresentCount > LastRetrievedID)) 55 | { 56 | //assert(stats.PresentCount - LastRetrievedID < MAX_QUEUE_LENGTH); 57 | 58 | UpdateEntry(stats); 59 | 60 | for (UINT i = LastRetrievedID + 1; i <= stats.PresentCount; ++i) 61 | { 62 | UINT EntryIndex = i % MAX_QUEUE_LENGTH; 63 | auto& Entry = Entries[EntryIndex]; 64 | QueueEntry e = Entry; 65 | 66 | if (Entry.PresentID != i) { 67 | // We overflowed the queue 68 | continue; 69 | } 70 | 71 | if (Entry.QueueExitedTime < Entry.QueueEnteredTime) { 72 | // Something got very, very confused. 73 | // This seems to happen when hitting f12 to force enter the debugger. 74 | continue; 75 | } 76 | 77 | e.Dropped = Entry.QueueExitedTime == TIME_STILL_IN_QUEUE; 78 | dequeue(e); 79 | } 80 | 81 | LastRetrievedID = stats.PresentCount; 82 | } 83 | 84 | return hr; 85 | } 86 | 87 | // Call this function after you call pSwapChain->Present() 88 | HRESULT PostPresent( 89 | IDXGISwapChain1 *pSwapChain, 90 | UINT SyncInterval, 91 | UINT64 FrameBeginTime, 92 | void *UserData) 93 | { 94 | HRESULT hr; 95 | 96 | UINT64 QpcTime = QpcNow(); 97 | UINT PresentID; 98 | 99 | hr = pSwapChain->GetLastPresentCount(&PresentID); 100 | if (FAILED(hr)) return hr; 101 | 102 | NewEntry(PresentID, FrameBeginTime, QpcTime, UserData); 103 | 104 | return hr; 105 | } 106 | 107 | private: 108 | 109 | enum : UINT { 110 | MAX_QUEUE_LENGTH = 32, 111 | INTERVAL_DURATION_WINDOW_SIZE = 7, 112 | }; 113 | 114 | enum : UINT64 { 115 | TIME_STILL_IN_QUEUE = ~(0ULL), 116 | }; 117 | 118 | UINT LastNewID; 119 | UINT LastUpdatedID; 120 | UINT LastRetrievedID; 121 | QueueEntry Entries[MAX_QUEUE_LENGTH]; 122 | UINT DurationHistoryWriteIndex; 123 | 124 | void NewEntry(UINT PresentID, 125 | UINT64 FrameBeginTime, 126 | UINT64 QpcTime, 127 | void *UserData) 128 | { 129 | UINT EntryIndex = PresentID % MAX_QUEUE_LENGTH; 130 | 131 | auto& Entry = Entries[EntryIndex]; 132 | Entry.FrameBeginTime = FrameBeginTime; 133 | Entry.PresentID = PresentID; 134 | Entry.UserData = UserData; 135 | Entry.QueueEnteredTime = QpcTime; 136 | Entry.QueueExitedTime = TIME_STILL_IN_QUEUE; 137 | 138 | LastNewID = PresentID; 139 | } 140 | 141 | void UpdateEntry(DXGI_FRAME_STATISTICS& stats) 142 | { 143 | UINT PresentID = stats.PresentCount; 144 | UINT EntryIndex = PresentID % MAX_QUEUE_LENGTH; 145 | 146 | auto& Entry = Entries[EntryIndex]; 147 | if (Entry.PresentID == PresentID) 148 | { 149 | Entry.QueueExitedTime = stats.SyncQPCTime.QuadPart; 150 | if (PresentID > 0) 151 | { 152 | UINT PreviousPresentID = PresentID - 1; 153 | UINT PreviousEntryIndex = PreviousPresentID % MAX_QUEUE_LENGTH; 154 | auto& PreviousEntry = Entries[PreviousEntryIndex]; 155 | if (PreviousEntry.PresentID == PreviousPresentID && 156 | PreviousEntry.QueueExitedTime != TIME_STILL_IN_QUEUE) 157 | { 158 | assert(Entry.QueueExitedTime >= PreviousEntry.QueueExitedTime); 159 | } 160 | } 161 | LastUpdatedID = PresentID; 162 | } 163 | } 164 | }; 165 | 166 | struct LatencyStatistics 167 | { 168 | void Sample(double Latency) 169 | { 170 | mLatencyHistory.push_back(Latency); 171 | if (mLatencyHistory.size() > mMaxHistoryLength) { 172 | mLatencyHistory.pop_front(); 173 | } 174 | } 175 | 176 | void SetHistoryLength(UINT Length) 177 | { 178 | mMaxHistoryLength = Length; 179 | } 180 | 181 | // absolute temporal distortion: Max(latency) - Min(latency) 182 | double EvaluateMinMaxMetric() 183 | { 184 | if (mLatencyHistory.empty()) { 185 | return 0; 186 | } 187 | 188 | auto minmax = std::minmax_element(std::begin(mLatencyHistory), std::end(mLatencyHistory)); 189 | return (*minmax.second) - (*minmax.first); 190 | } 191 | 192 | // std. dev of latency 193 | double EvaluateStdDevMetric() 194 | { 195 | if (mLatencyHistory.empty()) { 196 | return 0; 197 | } 198 | 199 | auto n = mLatencyHistory.size(); 200 | double sum = 0; 201 | double sq_sum = 0; 202 | 203 | for (double L : mLatencyHistory) { 204 | sum += L; 205 | sq_sum += L*L; 206 | } 207 | 208 | double mean = sum / n; 209 | double variance = sq_sum / n - mean * mean; 210 | return sqrt(variance); 211 | } 212 | 213 | private: 214 | 215 | std::deque mLatencyHistory; 216 | UINT mMaxHistoryLength; 217 | }; 218 | -------------------------------------------------------------------------------- /Source/WindowsHelpers.cpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2017 Intel Corporation 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | // use this file except in compliance with the License. You may obtain a copy 6 | // of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | //////////////////////////////////////////////////////////////////////////////// 16 | #include "WindowsHelpers.hpp" 17 | 18 | #include 19 | #include 20 | 21 | #if USING_WSI 22 | #include "wsi.hpp" 23 | #endif 24 | 25 | bool hresult_succeeded(HRESULT hresult, const char *message, const char *file, int line) 26 | { 27 | if (!SUCCEEDED(hresult)) { 28 | _com_error err(hresult, nullptr); 29 | char buf[1024]; 30 | #ifdef UNICODE 31 | sprintf_s(buf, "%s\n\tHRESULT Failure %08x: %ws", message, hresult, err.ErrorMessage()); 32 | #else 33 | sprintf_s(buf, "%s\n\tHRESULT Failure %08x: %s", message, hresult, err.ErrorMessage()); 34 | #endif 35 | OutputDebugStringA(buf); 36 | return false; 37 | } 38 | return true; 39 | } 40 | 41 | static UINT64 GetQpcFreq() 42 | { 43 | UINT64 QpcFreq; 44 | QueryPerformanceFrequency((LARGE_INTEGER*)&QpcFreq); 45 | return QpcFreq; 46 | } 47 | 48 | UINT64 g_QpcFreq = GetQpcFreq(); 49 | 50 | UINT64 SecondsToQpcTime(double Seconds) 51 | { 52 | return (UINT64)(g_QpcFreq*Seconds); 53 | } 54 | 55 | double QpcTimeToSeconds(UINT64 QpcTime) 56 | { 57 | return (double)QpcTime / g_QpcFreq; 58 | } 59 | 60 | UINT64 QpcNow() 61 | { 62 | UINT64 Now; 63 | QueryPerformanceCounter((LARGE_INTEGER*)&Now); 64 | return Now; 65 | } 66 | 67 | void SleepUntil(UINT64 QpcTime) 68 | { 69 | UINT64 Now = QpcNow(); 70 | if (QpcTime > Now) { 71 | UINT64 Duration = QpcTime - Now; 72 | UINT Millis = UINT(1000 * Duration / g_QpcFreq); 73 | if (Millis > 0) { 74 | Sleep(Millis); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Source/WindowsHelpers.hpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2017 Intel Corporation 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | // use this file except in compliance with the License. You may obtain a copy 6 | // of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | //////////////////////////////////////////////////////////////////////////////// 16 | #pragma once 17 | 18 | #define NOMINMAX 19 | #include 20 | #include 21 | 22 | using Microsoft::WRL::ComPtr; 23 | 24 | bool hresult_succeeded(HRESULT hresult, const char *message, const char *file, int line); 25 | 26 | #define ThrowIfFailed(...) \ 27 | if(!hresult_succeeded(__VA_ARGS__, #__VA_ARGS__, __FILE__, __LINE__)) { \ 28 | assert(false); \ 29 | throw; \ 30 | } 31 | 32 | #define CheckHresult(...) \ 33 | if(!hresult_succeeded(__VA_ARGS__, #__VA_ARGS__, __FILE__, __LINE__)) { \ 34 | return false; \ 35 | } 36 | 37 | #define AssertSucceeded(...) \ 38 | if(!hresult_succeeded(__VA_ARGS__, #__VA_ARGS__, __FILE__, __LINE__)) { \ 39 | assert(false); \ 40 | } 41 | 42 | struct WindowsEvent 43 | { 44 | WindowsEvent(): mEvent(0) { 45 | } 46 | ~WindowsEvent() { 47 | if(mEvent) { 48 | CloseHandle(mEvent); 49 | mEvent = 0; 50 | } 51 | } 52 | void Initialize() { 53 | mEvent = CreateEvent(0, 0, 0, 0); 54 | } 55 | WindowsEvent& operator = (HANDLE Event) { 56 | if(mEvent) { 57 | assert(Event != mEvent); 58 | CloseHandle(mEvent); 59 | } 60 | mEvent = Event; 61 | return *this; 62 | } 63 | operator bool() { return mEvent != 0; } 64 | HANDLE Get() { return mEvent; } 65 | HANDLE mEvent; 66 | }; 67 | 68 | extern UINT64 g_QpcFreq; 69 | 70 | UINT64 SecondsToQpcTime(double Seconds); 71 | double QpcTimeToSeconds(UINT64 QpcTime); 72 | UINT64 QpcNow(); 73 | void SleepUntil(UINT64 QpcTime); 74 | -------------------------------------------------------------------------------- /Source/pixel_shader.hlsl: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2017 Intel Corporation 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | // use this file except in compliance with the License. You may obtain a copy 6 | // of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | //////////////////////////////////////////////////////////////////////////////// 16 | #include "vertex_shader.hlsl" 17 | 18 | float4 pixel_shader(vs_out input) : SV_TARGET 19 | { 20 | return input.color; 21 | } 22 | -------------------------------------------------------------------------------- /Source/sample_cube.cpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2017 Intel Corporation 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | // use this file except in compliance with the License. You may obtain a copy 6 | // of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | //////////////////////////////////////////////////////////////////////////////// 16 | #include "sample_cube.hpp" 17 | 18 | #define FACE1_COLOR 0,255,0,255 19 | #define FACE2_COLOR 255,128,0,255 20 | #define FACE3_COLOR 255,0,0,255 21 | #define FACE4_COLOR 255,255,0,255 22 | #define FACE5_COLOR 0,0,255,255 23 | #define FACE6_COLOR 255,0,255,255 24 | 25 | color_vertex cube_vertices[6][4] = 26 | { 27 | { 28 | { 1.0f, 1.0f, -1.0f, FACE1_COLOR }, 29 | { -1.0f, 1.0f, -1.0f, FACE1_COLOR }, 30 | { -1.0f, 1.0f, 1.0f, FACE1_COLOR }, 31 | { 1.0f, 1.0f, 1.0f, FACE1_COLOR }, 32 | }, 33 | { 34 | { 1.0f, -1.0f, 1.0f, FACE2_COLOR }, 35 | { -1.0f, -1.0f, 1.0f, FACE2_COLOR }, 36 | { -1.0f, -1.0f, -1.0f, FACE2_COLOR }, 37 | { 1.0f, -1.0f, -1.0f, FACE2_COLOR }, 38 | }, 39 | { 40 | { 1.0f, 1.0f, 1.0f, FACE3_COLOR }, 41 | { -1.0f, 1.0f, 1.0f, FACE3_COLOR }, 42 | { -1.0f, -1.0f, 1.0f, FACE3_COLOR }, 43 | { 1.0f, -1.0f, 1.0f, FACE3_COLOR }, 44 | }, 45 | { 46 | { 1.0f, -1.0f, -1.0f, FACE4_COLOR }, 47 | { -1.0f, -1.0f, -1.0f, FACE4_COLOR }, 48 | { -1.0f, 1.0f, -1.0f, FACE4_COLOR }, 49 | { 1.0f, 1.0f, -1.0f, FACE4_COLOR }, 50 | }, 51 | { 52 | { -1.0f, 1.0f, 1.0f, FACE5_COLOR }, 53 | { -1.0f, 1.0f, -1.0f, FACE5_COLOR }, 54 | { -1.0f, -1.0f, -1.0f, FACE5_COLOR }, 55 | { -1.0f, -1.0f, 1.0f, FACE5_COLOR }, 56 | }, 57 | { 58 | { 1.0f, 1.0f, -1.0f, FACE6_COLOR }, 59 | { 1.0f, 1.0f, 1.0f, FACE6_COLOR }, 60 | { 1.0f, -1.0f, 1.0f, FACE6_COLOR }, 61 | { 1.0f, -1.0f, -1.0f, FACE6_COLOR }, 62 | } 63 | }; 64 | 65 | #define CUBE_INDS(face) {4*face,4*face+1,4*face+2,4*face,4*face+2,4*face+3} 66 | unsigned short cube_indices[6][6] = { 67 | CUBE_INDS(0), 68 | CUBE_INDS(1), 69 | CUBE_INDS(2), 70 | CUBE_INDS(3), 71 | CUBE_INDS(4), 72 | CUBE_INDS(5), 73 | }; 74 | -------------------------------------------------------------------------------- /Source/sample_cube.hpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2017 Intel Corporation 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | // use this file except in compliance with the License. You may obtain a copy 6 | // of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | //////////////////////////////////////////////////////////////////////////////// 16 | #pragma once 17 | 18 | struct color_vertex 19 | { 20 | float x, y, z; 21 | union { 22 | struct { 23 | unsigned char r, g, b, a; 24 | }; 25 | unsigned int rgba; 26 | }; 27 | }; 28 | 29 | extern color_vertex cube_vertices[6][4]; 30 | extern unsigned short cube_indices[6][6]; 31 | -------------------------------------------------------------------------------- /Source/sample_dx12.hpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2017 Intel Corporation 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | // use this file except in compliance with the License. You may obtain a copy 6 | // of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | //////////////////////////////////////////////////////////////////////////////// 16 | #pragma once 17 | 18 | #include 19 | 20 | struct game_data; 21 | struct text_rectangle; 22 | 23 | struct dx12_swapchain_options 24 | { 25 | // these can be altered freely 26 | struct { 27 | float overdraw_factor; 28 | int cpu_draw_ms; 29 | } any_time; 30 | 31 | // changing these will cause the device to be recreated 32 | struct { 33 | int use_waitable_object; 34 | int max_frame_latency; 35 | int swapchain_buffer_count; 36 | int gpu_frame_count; 37 | } create_time; 38 | 39 | struct { 40 | int cpu_hiccup_size; 41 | int cpu_hiccup_count; 42 | } inject; 43 | }; 44 | 45 | struct dx12_render_stats 46 | { 47 | float cpu_frame_time; 48 | float gpu_frame_time; 49 | float latency; 50 | float minmax_jitter; 51 | float stddev_jitter; 52 | }; 53 | 54 | bool initialize_dx12(dx12_swapchain_options *opts); 55 | void trim_dx12(); 56 | void shutdown_dx12(); 57 | 58 | bool set_swapchain_options_dx12(void *pHWND, void *pCoreWindow,float x_dips, float y_dips, float dpi, dx12_swapchain_options *opts); 59 | 60 | void render_game_dx12(wchar_t *hud_text, game_data *game, float fractional_ticks, int vsync_interval, dx12_render_stats *stats); 61 | 62 | void pause_eviz_dx12(bool pause); 63 | -------------------------------------------------------------------------------- /Source/sample_game.cpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2017 Intel Corporation 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | // use this file except in compliance with the License. You may obtain a copy 6 | // of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | //////////////////////////////////////////////////////////////////////////////// 16 | #include "sample_game.hpp" 17 | #include 18 | 19 | static void game_tick(game_data *game, const game_command *action); 20 | 21 | void initialize_game(game_data *game, int64_t millisecond_clock_now) 22 | { 23 | *game = { 0 }; 24 | 25 | game->last_unpause_millis = millisecond_clock_now; 26 | game->time = 0; 27 | game->cube_rotation_speed = INITIAL_CUBE_ROTATION_SPEED; 28 | 29 | cube_object& cube0 = game->cubes[0]; 30 | //cube0.position.x = -WORLD_ONE * 2; 31 | 32 | cube_object& cube1 = game->cubes[1]; 33 | //cube1.position.x = +WORLD_ONE * 2; 34 | 35 | cube0.position.y = WORLD_ONE * 4; 36 | cube1.position.y = cube0.position.y; 37 | } 38 | 39 | void calc_game_elapsed_time(game_data *game, unsigned *ticks_elapsed, float *fractional_ticks_elapsed, int64_t millisecond_clock_now) 40 | { 41 | if (game->paused) 42 | { 43 | *ticks_elapsed = 0; 44 | *fractional_ticks_elapsed = game->paused_fractional_ticks; 45 | } 46 | else 47 | { 48 | int64_t millisecs_since_pause = millisecond_clock_now - game->last_unpause_millis; 49 | 50 | // e.g. game fps = 30, milliseconds elapsed = 1010 51 | // game time = 1010/30 = 30.3 52 | // (30, 0.3 fractional time) 53 | 54 | unsigned long long temp = GAME_TICKS_PER_SECOND*millisecs_since_pause; 55 | 56 | game_time_t current_game_time = game->time_at_last_pause + (temp / 1000); 57 | unsigned fractional_game_time = temp % 1000; 58 | 59 | *ticks_elapsed = unsigned(current_game_time - game->time); 60 | *fractional_ticks_elapsed = fractional_game_time / 1000.0f; 61 | 62 | game->paused_fractional_ticks = *fractional_ticks_elapsed; 63 | } 64 | } 65 | 66 | void update_game(game_data *game, unsigned ticks_elapsed, const game_command *action, int64_t millisecond_clock_now) 67 | { 68 | for (unsigned i = 0; i < ticks_elapsed; ++i) 69 | { 70 | game_tick(game, action); 71 | 72 | game->time += 1; 73 | } 74 | 75 | // handle pausing 76 | if (action->toggle_pause || (game->paused && action->force_unpause)) 77 | { 78 | if (action->force_unpause) 79 | { 80 | game->paused = false; 81 | } 82 | else if (action->toggle_pause) 83 | { 84 | game->paused = !game->paused; 85 | } 86 | 87 | if (game->paused) 88 | { 89 | game->time_at_last_pause = game->time; 90 | } 91 | else 92 | { 93 | game->last_unpause_millis = millisecond_clock_now; 94 | } 95 | } 96 | } 97 | 98 | static void game_tick(game_data *game, const game_command *action) 99 | { 100 | if (action) 101 | { 102 | game->cube_rotation_speed += 100 * (action->increase_rotation - action->decrease_rotation); 103 | if (abs(game->cube_rotation_speed) < 300) { 104 | game->cube_rotation_speed = int(0.9*game->cube_rotation_speed); 105 | } 106 | } 107 | 108 | for (int cube_index = 0; cube_index < 2; ++cube_index) 109 | { 110 | cube_object& cube = game->cubes[cube_index]; 111 | 112 | #if 0 113 | if (cube.position.y < 0) 114 | { 115 | // Cube is on or below the ground: Reset, and give the cube some upwards velocity. 116 | cube.position.y = 0; 117 | //cube.rotation = 0; 118 | cube.velocity.y = -GRAVITY * 15; 119 | } 120 | else 121 | { 122 | // Integrate: 123 | cube.position.x += cube.velocity.x; 124 | cube.position.y += cube.velocity.y; 125 | cube.position.z += cube.velocity.z; 126 | 127 | // Apply gravity: 128 | cube.velocity.y += GRAVITY; 129 | 130 | // Apply rotation: 131 | cube.rotation = (angle)(cube.rotation + game->cube_rotation_speed); 132 | } 133 | #else 134 | cube.rotation = (angle)(cube.rotation + game->cube_rotation_speed); 135 | #endif 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /Source/sample_game.hpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2017 Intel Corporation 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | // use this file except in compliance with the License. You may obtain a copy 6 | // of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | //////////////////////////////////////////////////////////////////////////////// 16 | #pragma once 17 | 18 | #include 19 | 20 | enum 21 | { 22 | GAME_TICKS_PER_SECOND = 24, 23 | WORLD_ONE = 512, 24 | GRAVITY = -int(WORLD_ONE / GAME_TICKS_PER_SECOND), // world-unit delta-v per tick 25 | FULL_CIRCLE = 0x10000, 26 | 27 | INITIAL_CUBE_ROTATION_SPEED = FULL_CIRCLE / (4 * GAME_TICKS_PER_SECOND) 28 | }; 29 | 30 | typedef unsigned long long game_time_t; 31 | typedef int coordinate; 32 | typedef unsigned short angle; 33 | 34 | struct world_vector 35 | { 36 | coordinate x, y, z; 37 | }; 38 | 39 | struct cube_object 40 | { 41 | world_vector position, velocity; 42 | angle rotation; 43 | bool initialized; 44 | }; 45 | 46 | struct game_command 47 | { 48 | bool decrease_rotation; 49 | bool increase_rotation; 50 | 51 | bool toggle_pause; 52 | bool force_unpause; 53 | }; 54 | 55 | struct game_data 56 | { 57 | bool paused; 58 | float paused_fractional_ticks; 59 | 60 | int64_t startup_millis, last_unpause_millis; 61 | game_time_t time, time_at_last_pause; 62 | 63 | // The game world consists of two cubes. 64 | int cube_rotation_speed; 65 | cube_object cubes[2]; 66 | }; 67 | 68 | void initialize_game(game_data *game, int64_t millisecond_clock_now); 69 | void calc_game_elapsed_time(game_data *game, unsigned *ticks_elapsed, float *fractional_ticks_elapsed, int64_t millisecond_clock_now); 70 | void update_game(game_data *game, unsigned ticks_elapsed, const game_command *input, int64_t millisecond_clock_now); 71 | void dispose_game(game_data *game); 72 | -------------------------------------------------------------------------------- /Source/sample_main.cpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2017 Intel Corporation 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | // use this file except in compliance with the License. You may obtain a copy 6 | // of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | //////////////////////////////////////////////////////////////////////////////// 16 | #include "wsi.hpp" 17 | #include "sample_dx12.hpp" 18 | #include "sample_game.hpp" 19 | 20 | static wsi::ScreenState screen; 21 | static wsi::ScreenMode mode; 22 | static unsigned max_fps; 23 | static float current_fps, current_fps_cpu, current_fps_gpu; 24 | static float current_frametime_cpu, current_frametime_gpu; 25 | static bool caption_changed; 26 | static bool running; 27 | static float paused_fractional_ticks; 28 | static float frame_latency; 29 | static float frame_latency_stddev, frame_latency_minmaxd; 30 | 31 | static dx12_swapchain_options swapchain_opts; 32 | 33 | static WCHAR hud_string[4096]; 34 | 35 | static void rebuild_hud_string(game_data *game) 36 | { 37 | if (!caption_changed) 38 | { 39 | return; 40 | } 41 | 42 | caption_changed = false; 43 | 44 | #define NEWLINE "\n" 45 | swprintf_s(hud_string, L"" 46 | "Controls:" NEWLINE 47 | "[%d] Pause: Space" NEWLINE 48 | "[%d] Fullscreen: F11" NEWLINE 49 | "[%d] Vsync: Ctrl+K" NEWLINE 50 | "[%d] Use Waitable Object: Ctrl+W" NEWLINE 51 | "[%d] MaximumFrameLatency: Ctrl+,Ctrl-" NEWLINE 52 | "[%d] BufferCount: +,-" NEWLINE 53 | "[%d] FrameCount: [,]" NEWLINE 54 | "[%.1f] GPU Workload up,down" NEWLINE 55 | "[%d] CPU Workload Ctrl+up, Ctrl+down" NEWLINE 56 | "Stats:" NEWLINE 57 | " Avg. Present Latency = %.2f ms" NEWLINE 58 | " Latency StdDev = %.2fms" NEWLINE 59 | " Latency MinMaxDev = %.2fms" NEWLINE 60 | " Fps = %.2f (%.2fms)" NEWLINE 61 | " GPU fps = %.2f (%.2fms)" NEWLINE 62 | " CPU fps = %.2f (%.2fms)" NEWLINE, 63 | game->paused, 64 | screen.prefs.windowed==0, 65 | screen.prefs.vsync, 66 | swapchain_opts.create_time.use_waitable_object, 67 | swapchain_opts.create_time.max_frame_latency, 68 | swapchain_opts.create_time.swapchain_buffer_count, 69 | swapchain_opts.create_time.gpu_frame_count, 70 | swapchain_opts.any_time.overdraw_factor, 71 | swapchain_opts.any_time.cpu_draw_ms, 72 | frame_latency, 73 | frame_latency_stddev, frame_latency_minmaxd, 74 | current_fps, 1000 / current_fps, 75 | current_fps_gpu, 1000*current_frametime_gpu, 76 | current_fps_cpu, 1000*current_frametime_cpu 77 | ); 78 | } 79 | 80 | static void update_fps() 81 | { 82 | enum { 83 | FRAMETIME_BUFFER_LENGTH = 32, 84 | FPS_UPDATES_PER_SECOND = 4 85 | }; 86 | static wsi::millisec64_t frame_timestamps[FRAMETIME_BUFFER_LENGTH]; 87 | static wsi::millisec64_t frame_timestamp_write_index; 88 | static wsi::millisec64_t last_update; 89 | 90 | wsi::millisec64_t now = wsi::milliseconds(); 91 | wsi::millisec64_t then = frame_timestamps[frame_timestamp_write_index]; 92 | frame_timestamps[frame_timestamp_write_index] = now; 93 | frame_timestamp_write_index = (frame_timestamp_write_index + 1) & (FRAMETIME_BUFFER_LENGTH - 1); 94 | 95 | if (now > then) 96 | { 97 | if (FPS_UPDATES_PER_SECOND*(now - last_update) > 1000) 98 | { 99 | float fps = (1000.0f*FRAMETIME_BUFFER_LENGTH) / (now - then); 100 | last_update = now; 101 | caption_changed = true; 102 | current_fps = fps; 103 | current_fps_cpu = 1.0f / current_frametime_cpu; 104 | current_fps_gpu = 1.0f / current_frametime_gpu; 105 | } 106 | } 107 | else 108 | { 109 | current_fps = 0; 110 | } 111 | } 112 | 113 | void process_inputs(game_command *out_action) 114 | { 115 | // Process windows thread messages - this may or may not be needed for your game, 116 | // depending on what you run in the main thread. 117 | { 118 | MSG msg; 119 | while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) 120 | { 121 | DispatchMessage(&msg); 122 | } 123 | } 124 | 125 | // Process queued state updates from the UI thread 126 | if (wsi::read_state(&screen, (unsigned)-1)) 127 | { 128 | if (screen.changed_fields & wsi::QuitRaised) 129 | { 130 | running = false; 131 | } 132 | 133 | unsigned ideal_graphics_fps = screen.dm.dmDisplayFrequency; 134 | 135 | // Compute the desired framerate based on window and power state. 136 | if (screen.window_status == wsi::Hidden) 137 | { 138 | ideal_graphics_fps = 0; 139 | } 140 | else 141 | { 142 | if (screen.window_status == wsi::Background) 143 | { 144 | 145 | } 146 | else if (screen.window_status == wsi::Normal) 147 | { 148 | ideal_graphics_fps *= 2; 149 | } 150 | 151 | if (screen.power_status == wsi::OnBattery) 152 | { 153 | ideal_graphics_fps /= 2; 154 | } 155 | } 156 | 157 | max_fps = std::max((unsigned)GAME_TICKS_PER_SECOND, ideal_graphics_fps); 158 | 159 | wsi::enable_timer_boost(screen.power_status == wsi::PluggedIn && screen.window_status == wsi::Normal); 160 | 161 | caption_changed = true; 162 | } 163 | 164 | // Process queued input events from the UI thread 165 | wsi::WindowMessage message; 166 | auto old_opts = swapchain_opts; 167 | while (wsi::read_message(&message)) 168 | { 169 | switch (message.type) 170 | { 171 | case wsi::Character: 172 | // process text input 173 | break; 174 | case wsi::Keystroke: 175 | { 176 | if (message.keystroke.modkeys & wsi::modRepeat) { 177 | break; 178 | } 179 | if (message.keystroke.code == 'W' && (message.keystroke.modkeys & wsi::modControl)) { 180 | swapchain_opts.create_time.use_waitable_object = !swapchain_opts.create_time.use_waitable_object; 181 | } 182 | if (message.keystroke.code == VK_F11) { 183 | wsi::toggle_fullscreen(); 184 | } 185 | else if (message.keystroke.code == VK_UP) { 186 | if (message.keystroke.modkeys & wsi::modControl) { 187 | swapchain_opts.any_time.cpu_draw_ms = std::min(swapchain_opts.any_time.cpu_draw_ms + 1, 33); 188 | } else { 189 | swapchain_opts.any_time.overdraw_factor = std::min(swapchain_opts.any_time.overdraw_factor + 0.5f, 20); 190 | } 191 | } 192 | else if (message.keystroke.code == VK_DOWN) { 193 | if (message.keystroke.modkeys & wsi::modControl) { 194 | swapchain_opts.any_time.cpu_draw_ms = std::max(0, swapchain_opts.any_time.cpu_draw_ms - 1); 195 | } else { 196 | swapchain_opts.any_time.overdraw_factor = std::max(0, swapchain_opts.any_time.overdraw_factor - 0.5f); 197 | } 198 | } 199 | else if (message.keystroke.code == VK_OEM_MINUS) { 200 | if (message.keystroke.modkeys & wsi::modControl) { 201 | swapchain_opts.create_time.max_frame_latency = std::max(1, swapchain_opts.create_time.max_frame_latency - 1); 202 | } 203 | else { 204 | swapchain_opts.create_time.swapchain_buffer_count = std::max(2, swapchain_opts.create_time.swapchain_buffer_count - 1); 205 | } 206 | } 207 | else if (message.keystroke.code == VK_OEM_PLUS) { 208 | if (message.keystroke.modkeys & wsi::modControl) { 209 | swapchain_opts.create_time.max_frame_latency = std::min(8, swapchain_opts.create_time.max_frame_latency + 1); 210 | } 211 | else { 212 | swapchain_opts.create_time.swapchain_buffer_count = std::min(8, swapchain_opts.create_time.swapchain_buffer_count + 1); 213 | } 214 | } 215 | else if (message.keystroke.code == VK_OEM_4 /*[*/) { 216 | swapchain_opts.create_time.gpu_frame_count = std::max(1, swapchain_opts.create_time.gpu_frame_count - 1); 217 | } 218 | else if (message.keystroke.code == VK_OEM_6 /*]*/) { 219 | swapchain_opts.create_time.gpu_frame_count = std::min(8, swapchain_opts.create_time.gpu_frame_count + 1); 220 | } 221 | else if (message.keystroke.code == VK_TAB) { 222 | swapchain_opts.inject.cpu_hiccup_count = 1; 223 | swapchain_opts.inject.cpu_hiccup_size = 10; 224 | } 225 | else if (message.keystroke.code == VK_ESCAPE) 226 | { 227 | running = false; 228 | } 229 | else if (message.keystroke.code == VK_SPACE) 230 | { 231 | out_action->toggle_pause = true; 232 | } 233 | } 234 | break; 235 | case wsi::Mouse: 236 | // process mouse input 237 | break; 238 | } 239 | } 240 | 241 | if (memcmp(&swapchain_opts, &old_opts, sizeof(old_opts))) 242 | { 243 | out_action->force_unpause = true; 244 | out_action->toggle_pause = false; 245 | } 246 | 247 | // Process continuous/time-based (not event based) input: 248 | if (screen.window_status == wsi::Normal) 249 | { 250 | out_action->decrease_rotation = wsi::keyboard_state[VK_LEFT]; 251 | out_action->increase_rotation = wsi::keyboard_state[VK_RIGHT]; 252 | } 253 | 254 | // Process queued input from the network 255 | 256 | // etc. 257 | } 258 | 259 | static void run_game() 260 | { 261 | game_data game; 262 | 263 | initialize_game(&game, wsi::milliseconds()); 264 | 265 | running = true; 266 | 267 | for (;;) 268 | { 269 | wsi::update(&screen); 270 | 271 | bool rendering = wsi::should_render(); 272 | 273 | int64_t millisecond_clock_now = wsi::milliseconds(); 274 | 275 | unsigned ticks_elapsed = 0; 276 | float fractional_ticks = 0; 277 | calc_game_elapsed_time(&game, &ticks_elapsed, &fractional_ticks, millisecond_clock_now); 278 | 279 | game_command action = { 0 }; 280 | process_inputs(&action); 281 | 282 | if (!running) break; 283 | 284 | update_game(&game, ticks_elapsed, &action, millisecond_clock_now); 285 | update_fps(); 286 | 287 | if (action.toggle_pause) 288 | { 289 | pause_eviz_dx12(game.paused); 290 | } 291 | 292 | if (rendering) 293 | { 294 | float dpi = (float)screen.dpi.cx; 295 | float x_dips = screen.dimensions.cx * 96.0f / dpi; 296 | float y_dips = screen.dimensions.cy * 96.0f / dpi; 297 | set_swapchain_options_dx12(&wsi::screen_window, NULL, x_dips, y_dips, dpi, &swapchain_opts); 298 | rebuild_hud_string(&game); 299 | dx12_render_stats stats; 300 | render_game_dx12(hud_string, &game, fractional_ticks, screen.prefs.vsync, &stats); 301 | if (stats.latency) { 302 | const float alpha = 0.1f; 303 | frame_latency = (1-alpha)*frame_latency + alpha*stats.latency; 304 | frame_latency_stddev = stats.stddev_jitter; 305 | frame_latency_minmaxd = stats.minmax_jitter; 306 | current_frametime_cpu = (1 - alpha)*current_frametime_cpu + alpha*stats.cpu_frame_time; 307 | current_frametime_gpu = (1 - alpha)*current_frametime_gpu + alpha*stats.gpu_frame_time; 308 | } 309 | } 310 | 311 | //wsi::limit_fps(max_fps); 312 | } 313 | } 314 | 315 | int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) 316 | { 317 | #ifdef _DEBUG 318 | struct leakcheck { 319 | static void dump() { 320 | _CrtDumpMemoryLeaks(); 321 | } 322 | }; 323 | atexit(leakcheck::dump); 324 | #endif 325 | 326 | UNREFERENCED_PARAMETER(hInstance); 327 | UNREFERENCED_PARAMETER(hPrevInstance); 328 | UNREFERENCED_PARAMETER(lpszCmdLine); 329 | UNREFERENCED_PARAMETER(nCmdShow); 330 | 331 | wsi::initialize(TEXT("FlipModelD3D12"), NULL, NULL, "log.txt"); 332 | 333 | wsi::set_thread_name(GetCurrentThreadId(), "Main Thread"); 334 | 335 | // Set the screen mode (and prefs which is optional) 336 | { 337 | wsi::ScreenModePrefs prefs; 338 | 339 | mode.dm.dmPelsWidth = 1024; 340 | mode.dm.dmPelsHeight = 768; 341 | mode.dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT; 342 | 343 | mode.min_width = 1024; 344 | mode.min_height = 768; 345 | 346 | mode.allow_windowed = true; 347 | mode.allow_fullscreen = true; 348 | mode.allow_borderless = true; 349 | mode.allow_resize = true; 350 | 351 | prefs.windowed = true; 352 | prefs.top = false; 353 | prefs.clip_cursor = false; 354 | prefs.vsync = true; 355 | 356 | wsi::set_mode(mode, &prefs); 357 | } 358 | 359 | swapchain_opts.any_time.overdraw_factor = 8; 360 | swapchain_opts.any_time.cpu_draw_ms = 8; 361 | swapchain_opts.create_time.gpu_frame_count = 2; 362 | swapchain_opts.create_time.swapchain_buffer_count = 3; 363 | swapchain_opts.create_time.use_waitable_object = 1; 364 | swapchain_opts.create_time.max_frame_latency = 2; 365 | 366 | if (initialize_dx12(&swapchain_opts)) 367 | { 368 | run_game(); 369 | } 370 | else 371 | { 372 | MessageBox(wsi::screen_window, "Could not initialize D3D12; the sample cannot run.", "Error", MB_ICONEXCLAMATION); 373 | wsi::log_message(0, 0, "Could not initialize D3D12; the sample cannot run."); 374 | } 375 | 376 | shutdown_dx12(); 377 | wsi::shutdown(); 378 | 379 | return 0; 380 | } 381 | -------------------------------------------------------------------------------- /Source/sample_math.cpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2017 Intel Corporation 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | // use this file except in compliance with the License. You may obtain a copy 6 | // of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | //////////////////////////////////////////////////////////////////////////////// 16 | #include "sample_math.hpp" 17 | 18 | #define _USE_MATH_DEFINES 19 | #include 20 | #include 21 | 22 | void load_identity(float4x4 *m) 23 | { 24 | memset(m, 0, sizeof(*m)); 25 | m->_11 = 1.0f; 26 | m->_22 = 1.0f; 27 | m->_33 = 1.0f; 28 | m->_44 = 1.0f; 29 | } 30 | 31 | void translate(float4x4 *m, float x, float y, float z) 32 | { 33 | m->_14 += m->_11*x + m->_12*y + m->_13*z; 34 | m->_24 += m->_21*x + m->_22*y + m->_23*z; 35 | m->_34 += m->_31*x + m->_32*y + m->_33*z; 36 | m->_44 += m->_41*x + m->_42*y + m->_43*z; 37 | } 38 | 39 | void mult_matrix(float4x4 *m, const float4x4 *op) 40 | { 41 | float4x4 temp; 42 | for (int c = 0; c < 4; ++c) 43 | { 44 | for (int r = 0; r < 4; ++r) 45 | { 46 | // column major ordering: mat[row, col] = m[col][row] 47 | // matrix multiplication: out[row r, col c] = dot(row [r] of m, col [c] of op) 48 | temp.m[c][r] = 49 | m->m[0][r] * op->m[c][0] + 50 | m->m[1][r] * op->m[c][1] + 51 | m->m[2][r] * op->m[c][2] + 52 | m->m[3][r] * op->m[c][3]; 53 | } 54 | } 55 | *m = temp; 56 | } 57 | 58 | void rotate(float4x4 *m, float degrees, float x, float y, float z) 59 | { 60 | float mag = sqrtf(x*x + y*y + z*z); 61 | if (mag <= 0) return; 62 | 63 | const float _deg2rad = float(M_PI / 180.0f); 64 | float theta = degrees * _deg2rad; 65 | float s = sinf(theta); 66 | float c = cosf(theta); 67 | 68 | float scale = 1.0f / mag; 69 | x *= scale; y *= scale; z *= scale; 70 | 71 | float xx = x*x, yy = y*y, zz = z*z; 72 | float xy = x*y, yz = y*z, xz = x*z; 73 | float xs = x*s, ys = y*s, zs = z*s; 74 | float d = 1.0f - c; 75 | 76 | float4x4 r; 77 | r._11 = xx*d + c; 78 | r._12 = xy*d - zs; 79 | r._13 = xz*d + ys; 80 | r._14 = 0.0f; 81 | 82 | r._21 = xy*d + zs; 83 | r._22 = yy*d + c; 84 | r._23 = yz*d - xs; 85 | r._24 = 0.0f; 86 | 87 | r._31 = xz*d - ys; 88 | r._32 = yz*d + xs; 89 | r._33 = zz*d + c; 90 | r._34 = 0.0f; 91 | 92 | r._41 = 0.0f; 93 | r._42 = 0.0f; 94 | r._43 = 0.0f; 95 | r._44 = 1.0f; 96 | 97 | mult_matrix(m, &r); 98 | } 99 | 100 | void load_frustum(float4x4 *m, 101 | float left, float right, float bottom, float top, 102 | float near, float far, float near_depth, float far_depth) 103 | { 104 | float r_l, t_b, f_n; 105 | memset(m, 0, sizeof(*m)); 106 | 107 | r_l = right - left; 108 | m->_11 = 2 * near / r_l; 109 | m->_13 = (right + left) / r_l; 110 | 111 | t_b = top - bottom; 112 | m->_22 = 2 * near / t_b; 113 | m->_23 = (top + bottom) / t_b; 114 | 115 | f_n = far - near; 116 | m->_33 = (near*near_depth - far*far_depth) / f_n; 117 | m->_34 = far*near*(near_depth - far_depth) / f_n; 118 | 119 | m->_43 = -1.0f; 120 | } 121 | 122 | // simplified ortho: 123 | // left = 0, right = width 124 | // top = 0, bottom = height 125 | // near = 0, far = depth 126 | void load_simple_ortho(float4x4 *m, float width, float height, float depth, float near_depth, float far_depth) 127 | { 128 | memset(m, 0, sizeof(*m)); 129 | 130 | m->_11 = 2.0f / width; 131 | m->_22 = -2.0f / height; 132 | m->_33 = (near_depth - far_depth) / depth; 133 | 134 | m->_14 = -1.0f; 135 | m->_24 = 1.0f; 136 | m->_34 = near_depth; 137 | m->_44 = 1.0f; 138 | } 139 | 140 | // simplified frustum: 141 | // square, centered on zero 142 | void load_simple_perspective(float4x4 *m, float fov_y, float aspect, float near, float far, float near_depth, float far_depth) 143 | { 144 | const float _half_deg2rad_ = float(M_PI / 360.0); 145 | float hh = tanf(fov_y * _half_deg2rad_) * near; 146 | float hw = hh * aspect; 147 | load_frustum(m, -hw, hw, -hh, hh, near, far, near_depth, far_depth); 148 | } 149 | -------------------------------------------------------------------------------- /Source/sample_math.hpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2017 Intel Corporation 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | // use this file except in compliance with the License. You may obtain a copy 6 | // of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | //////////////////////////////////////////////////////////////////////////////// 16 | #pragma once 17 | 18 | struct float4x4 19 | { 20 | // "column major" layout matches OpenGL 21 | union { 22 | float m[4][4]; 23 | struct { 24 | float _11, _21, _31, _41; 25 | float _12, _22, _32, _42; 26 | float _13, _23, _33, _43; 27 | float _14, _24, _34, _44; 28 | }; 29 | }; 30 | }; 31 | 32 | void load_identity(float4x4 *m); 33 | void mult_matrix(float4x4 *m, const float4x4 *op); 34 | void translate(float4x4 *m, float x, float y, float z); 35 | void rotate(float4x4 *m, float degrees, float x, float y, float z); 36 | 37 | // NOTE: the near_depth and far_depth parameters allow building projection matrices compatible with both GL and DX: 38 | // DX's depth range is [0,1] where GL's is [-1,1] 39 | void load_frustum(float4x4 *m, float left, float right, float bottom, float top, float near, float far, float near_depth, float far_depth); 40 | void load_simple_ortho(float4x4 *m, float width, float height, float depth, float near_depth, float far_depth); 41 | void load_simple_perspective(float4x4 *m, float fov_y, float aspect, float near, float far, float near_depth, float far_depth); 42 | -------------------------------------------------------------------------------- /Source/timeline_multimap.hpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2017 Intel Corporation 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | // use this file except in compliance with the License. You may obtain a copy 6 | // of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | //////////////////////////////////////////////////////////////////////////////// 16 | #pragma once 17 | 18 | #include 19 | 20 | // Timeline_multimap: 21 | // A multimap that represents a timeline of events. 22 | // The assumption is that events are inserted "roughly" in chronological order, and in batches. 23 | // Under these conditions, the performance will be much better than a regular multimap. 24 | // In order to achieve this extra performance, we accept certain tradeoffs: 25 | // -Improve average case operations at the cost of worst case. 26 | // -Sacrifice stable iterators (pretty much any operation invalidates). 27 | // -Sacrifice stable references (values are not allocated on the heap). 28 | template< 29 | typename K, // the time index, should be integral 30 | typename V> // typically std::unique_ptr 31 | struct timeline_multimap 32 | { 33 | struct value_type 34 | { 35 | K first; 36 | V second; 37 | template value_type(K k, Args&&... args) 38 | : first(k), second(std::forward(args)...) { } 39 | friend bool operator < (const value_type& lhs, const value_type& rhs) { 40 | return lhs.first < rhs.first; 41 | } 42 | friend bool operator < (const value_type& lhs, const K& rhs) { 43 | return lhs.first < rhs; 44 | } 45 | friend bool operator < (const K& lhs, const value_type& rhs) { 46 | return lhs < rhs.first; 47 | } 48 | }; 49 | 50 | typedef typename std::vector::iterator iterator; 51 | 52 | bool empty() 53 | { 54 | return vals.empty(); 55 | } 56 | 57 | size_t size() 58 | { 59 | return vals.size(); 60 | } 61 | 62 | void clear() 63 | { 64 | vals.clear(); 65 | unsorted_begin = 0; 66 | } 67 | 68 | template 69 | iterator emplace(K k, Args&&... args) 70 | { 71 | auto pos = vals.size(); 72 | vals.emplace_back(k, std::forward(args)...); 73 | return vals.begin() + pos; 74 | } 75 | 76 | iterator lower_bound(const K& k) 77 | { 78 | sort(); 79 | return std::lower_bound(vals.begin(), vals.end(), k); 80 | } 81 | 82 | iterator upper_bound(const K& k) 83 | { 84 | sort(); 85 | return std::upper_bound(vals.begin(), vals.end(), k); 86 | } 87 | 88 | iterator begin() 89 | { 90 | sort(); 91 | return vals.begin(); 92 | } 93 | 94 | iterator end() 95 | { 96 | sort(); 97 | return vals.end(); 98 | } 99 | 100 | iterator erase(iterator first, iterator last) { 101 | assert(size() == unsorted_begin); 102 | auto it = vals.erase(first, last); 103 | unsorted_begin = size(); 104 | return it; 105 | } 106 | 107 | iterator erase(iterator position) { 108 | assert(size() == unsorted_begin); 109 | auto it = vals.erase(position); 110 | unsorted_begin = size(); 111 | return it; 112 | } 113 | 114 | private: 115 | 116 | void sort() 117 | { 118 | auto N = size(); 119 | if (unsorted_begin != N) 120 | { 121 | auto first = &vals[0]; 122 | auto mid = first + unsorted_begin; 123 | auto last = first + N; 124 | unsorted_begin = N; 125 | std::sort(mid, last); 126 | auto merge_start = std::upper_bound(first, mid, *mid); 127 | std::inplace_merge(merge_start, mid, last); 128 | } 129 | } 130 | 131 | std::vector vals; 132 | size_t unsorted_begin = 0; // == size() when sorted 133 | }; 134 | -------------------------------------------------------------------------------- /Source/vertex_shader.hlsl: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2017 Intel Corporation 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | // use this file except in compliance with the License. You may obtain a copy 6 | // of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | //////////////////////////////////////////////////////////////////////////////// 16 | cbuffer dx11_cbuffer : register(b0) 17 | { 18 | float4x4 projection; 19 | }; 20 | 21 | cbuffer flags_cbuffer : register(b1) 22 | { 23 | uint flags; 24 | }; 25 | 26 | struct vs_in 27 | { 28 | float3 position : position; 29 | float4 color : color; 30 | float4x4 modelview : modelview; // per-instance 31 | }; 32 | 33 | struct vs_out 34 | { 35 | float4 position: SV_Position; 36 | float4 color : color; 37 | }; 38 | 39 | vs_out vertex_shader( vs_in input ) 40 | { 41 | vs_out output; 42 | 43 | float4 viewspace = mul(input.modelview, float4(input.position, 1.0)); 44 | float4 clipspace = mul(projection, viewspace); 45 | output.position = clipspace; 46 | 47 | output.color = input.color; 48 | 49 | return output; 50 | } 51 | -------------------------------------------------------------------------------- /Source/wsi.hpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2017 Intel Corporation 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | // use this file except in compliance with the License. You may obtain a copy 6 | // of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | //////////////////////////////////////////////////////////////////////////////// 16 | #pragma once 17 | 18 | #include "wsi_utils.hpp" 19 | 20 | namespace wsi 21 | { 22 | // global variables 23 | 24 | extern HWND screen_window; 25 | extern bool keyboard_state[256]; 26 | 27 | // enums 28 | 29 | enum { 30 | WM_SETMODE = WM_USER + 1, 31 | WM_SETPREFS, 32 | WM_DESTROYSELF, 33 | WM_SHOWCURSOR, 34 | WM_RELCURSOR, 35 | WM_SETTITLE, 36 | WM_TOGGLEFULLSCREEN, 37 | WM_TOGGLEONTOP, 38 | WM_TOGGLEGRABMODE, 39 | WM_TOGGLEVSYNC 40 | }; 41 | 42 | enum WindowStatus { 43 | Hidden, // Minimized, etc. 44 | Background, // Another app has focus 45 | Normal 46 | }; 47 | 48 | enum PowerStatus { 49 | PluggedIn, 50 | OnBattery 51 | }; 52 | 53 | // Fullscreen: 54 | // resolution == desktop: use borderless if allowed. 55 | // resolution > desktop: must change resolution (stretch only). 56 | // resolution < desktop: must change resolution, can stretch or center. 57 | 58 | struct ScreenMode 59 | { 60 | ScreenMode() { 61 | ZeroMemory(this, sizeof(*this)); 62 | dm.dmSize = sizeof(dm); 63 | } 64 | 65 | DEVMODE dm; 66 | unsigned min_width, min_height; // when resizable 67 | 68 | // flags which control which kinds of window manipulations are permissible. 69 | bool allow_windowed; 70 | bool allow_fullscreen; // i.e., exclusive mode. 71 | bool allow_borderless; // i.e., use borderless when applicable. 72 | bool allow_resize; 73 | }; 74 | 75 | // This is the user-state associated with the mode, 76 | // which you might choose to persist across sessions. 77 | struct ScreenModePrefs 78 | { 79 | ScreenModePrefs() { ZeroMemory(this, sizeof(*this)); } 80 | 81 | bool windowed; 82 | bool top; 83 | bool clip_cursor; 84 | bool vsync; 85 | WINDOWPLACEMENT placement; 86 | }; 87 | 88 | enum ScreenStateFields 89 | { 90 | QuitRaisedBit, 91 | RedrawRaisedBit, 92 | PrefsBit, 93 | DevmodeBit, 94 | DimensionsBit, 95 | DpiBit, 96 | StatusBit, 97 | PowerBit, 98 | CursorClippedBit, 99 | 100 | NUMBER_OF_SCREEN_STATE_FIELDS, 101 | 102 | QuitRaised = 1 << QuitRaisedBit, 103 | RedrawRaised = 1 << RedrawRaisedBit, 104 | Prefs = 1 << PrefsBit, 105 | Dimensions = 1 << DimensionsBit, 106 | Dpi = 1 << DpiBit, 107 | Devmode = 1 << DevmodeBit, 108 | Status = 1 << StatusBit, 109 | Power = 1 << PowerBit, 110 | CursorClipped = 1 << CursorClippedBit 111 | }; 112 | 113 | struct ScreenState 114 | { 115 | unsigned changed_fields; 116 | ScreenModePrefs prefs; 117 | DEVMODE dm; // actual, from ENUM_CURRENT_SETTINGS 118 | SIZE dimensions; // actual, current values for the client area. 119 | SIZE dpi; 120 | WindowStatus window_status; 121 | PowerStatus power_status; 122 | bool cursor_clipped_now; 123 | bool cursor_relative_now; 124 | }; 125 | 126 | enum WindowMessageType 127 | { 128 | Character, 129 | Keystroke, 130 | Mouse 131 | }; 132 | 133 | enum ClickType 134 | { 135 | MouseMove, 136 | MouseDelta, 137 | MouseDown, 138 | MouseUp, 139 | MouseWheel 140 | }; 141 | 142 | enum ModkeyFlags 143 | { 144 | modControl=1, 145 | modAlt=2, 146 | modShift=4, 147 | modRepeat=8, 148 | modLMouse=16, 149 | modRMouse=32, 150 | modMMouse=64 151 | }; 152 | 153 | struct WindowMessage 154 | { 155 | WindowMessageType type; 156 | 157 | WindowMessage() { } 158 | WindowMessage(WindowMessageType type) 159 | { 160 | memset(this, 0, sizeof(*this)); 161 | this->type = type; 162 | } 163 | 164 | union 165 | { 166 | struct { 167 | unsigned char code; 168 | char padding; 169 | unsigned short modkeys; 170 | } character; 171 | 172 | struct { 173 | unsigned char code; 174 | char padding; 175 | unsigned short modkeys; 176 | } keystroke; 177 | 178 | struct { 179 | unsigned char button; 180 | char type; 181 | unsigned short modkeys; 182 | short x, y; 183 | } mouse; 184 | }; 185 | }; 186 | 187 | // If a custom WndProc is provided, call wsi::WndProc instead of DefWndProc. 188 | LRESULT WINAPI WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); 189 | 190 | bool initialize(const TCHAR *title, HICON icon, WNDPROC wndproc, const char *logfile_name = 0); 191 | void shutdown(); 192 | 193 | void log_message(const char *file, int line, const char *format, ...); 194 | 195 | bool should_render(); 196 | void update(const ScreenState *main_thread_state); 197 | void limit_fps(int max_fps); 198 | 199 | void set_mode(const ScreenMode& mode, const ScreenModePrefs *prefs = 0, bool recreate_window = false); // set mode and prefs 200 | void set_prefs(const ScreenModePrefs *prefs); // set prefs only 201 | 202 | // The "state queue" contains all information where only the latest update is important. 203 | bool read_state(ScreenState *state, unsigned remove_mask); 204 | 205 | // The "message queue" contanis all information where sequence is important. 206 | // This is a regular queue, finite in size, so messages will get lost if it becomes overfull. 207 | bool read_message(WindowMessage *message); 208 | 209 | bool get_mouse_position(int *x, int *y); // coords relative to the game window; returns false if the cursor is not in the game window 210 | void set_mouse_position(int x, int y); // coords relative to the game window. 211 | 212 | void set_title(const TCHAR *title); 213 | inline void show_cursor(bool show) { PostMessage(screen_window, WM_SHOWCURSOR, (WPARAM)show, 0); } 214 | inline void enable_relative_cursor(bool enable) { PostMessage(screen_window, WM_RELCURSOR, (WPARAM)enable, 0); } 215 | 216 | // convenience functions for toggling preferences 217 | inline void toggle_fullscreen() { PostMessage(screen_window, WM_TOGGLEFULLSCREEN, 0, 0); } 218 | inline void toggle_on_top() { PostMessage(screen_window, WM_TOGGLEONTOP, 0, 0); } 219 | inline void toggle_clip() { PostMessage(screen_window, WM_TOGGLEGRABMODE, 0, 0); } 220 | inline void toggle_vsync() { PostMessage(screen_window, WM_TOGGLEVSYNC, 0, 0); } 221 | } 222 | -------------------------------------------------------------------------------- /Source/wsi_utils.cpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2017 Intel Corporation 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | // use this file except in compliance with the License. You may obtain a copy 6 | // of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | //////////////////////////////////////////////////////////////////////////////// 16 | #include "wsi.hpp" 17 | #include "wsi_utils.hpp" 18 | 19 | #if 1 // enable this for older windows versions without multimonitor support 20 | #pragma warning(disable:4996) // function marked deprecated 21 | #define COMPILE_MULTIMON_STUBS 22 | #include 23 | #endif 24 | 25 | #include 26 | #include 27 | 28 | #pragma comment(lib, "winmm.lib") 29 | 30 | using namespace wsi; 31 | 32 | static HMODULE shcore = (HMODULE)-1; 33 | 34 | typedef enum PROCESS_DPI_AWARENESS { 35 | PROCESS_DPI_UNAWARE = 0, 36 | PROCESS_SYSTEM_DPI_AWARE = 1, 37 | PROCESS_PER_MONITOR_DPI_AWARE = 2 38 | } PROCESS_DPI_AWARENESS; 39 | 40 | typedef enum MONITOR_DPI_TYPE { 41 | MDT_EFFECTIVE_DPI = 0, 42 | MDT_ANGULAR_DPI = 1, 43 | MDT_RAW_DPI = 2, 44 | MDT_DEFAULT = MDT_EFFECTIVE_DPI 45 | } MONITOR_DPI_TYPE; 46 | 47 | typedef HRESULT(STDAPICALLTYPE *SETPROCESSDPIAWARENESSPROC)(_In_ PROCESS_DPI_AWARENESS value); 48 | static SETPROCESSDPIAWARENESSPROC SetProcessDpiAwareness; 49 | 50 | typedef HRESULT (STDAPICALLTYPE *GETDPIFORMONITORPROC)( _In_ HMONITOR hmonitor, 51 | _In_ MONITOR_DPI_TYPE dpiType, _Out_ UINT *dpiX, _Out_ UINT *dpiY); 52 | static GETDPIFORMONITORPROC GetDpiForMonitor; 53 | 54 | static bool init_shcore() 55 | { 56 | if (shcore == (HMODULE)-1) 57 | { 58 | shcore = LoadLibraryA("shcore"); 59 | SetProcessDpiAwareness = (SETPROCESSDPIAWARENESSPROC)GetProcAddress(shcore, "SetProcessDpiAwareness"); 60 | GetDpiForMonitor = (GETDPIFORMONITORPROC)GetProcAddress(shcore, "GetDpiForMonitor"); 61 | } 62 | return shcore != 0 && shcore != (HMODULE)-1; 63 | } 64 | 65 | static DWORD WINAPI thread_startup(LPVOID lpParameter) 66 | { 67 | THREAD *thread = (THREAD*)lpParameter; 68 | 69 | if (thread->name) 70 | { 71 | set_thread_name(GetCurrentThreadId(), thread->name); 72 | } 73 | 74 | if (!thread->init_proc || 75 | thread->init_proc(thread, thread->user_data)) 76 | { 77 | thread->running = true; 78 | thread->init_ok = true; 79 | SetEvent(thread->init_event); 80 | thread->exit_code = thread->thread_proc(thread, thread->user_data); 81 | } 82 | else 83 | { 84 | SetEvent(thread->init_event); 85 | thread->exit_code = -1; 86 | } 87 | thread->running = false; 88 | thread->exited = true; 89 | return thread->exit_code; 90 | } 91 | 92 | 93 | void wsi::set_thread_name(DWORD thread_id, const char* thread_name) 94 | { 95 | enum { MS_VC_EXCEPTION = 0x406D1388 }; 96 | 97 | struct THREADNAME_INFO 98 | { 99 | DWORD dwType; // Must be 0x1000. 100 | LPCSTR szName; // Pointer to name (in user addr space). 101 | DWORD dwThreadID; // Thread ID (-1=caller thread). 102 | DWORD dwFlags; // Reserved for future use, must be zero. 103 | } info; 104 | 105 | info.dwType = 0x1000; 106 | info.szName = thread_name; 107 | info.dwThreadID = thread_id; 108 | info.dwFlags = 0; 109 | 110 | __try 111 | { 112 | RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info); 113 | } 114 | __except (EXCEPTION_EXECUTE_HANDLER) 115 | { 116 | } 117 | } 118 | 119 | bool wsi::create_thread(THREAD *thread, const char *name, int(*thread_proc)(THREAD*, void*), int(*init_proc)(THREAD*, void*), void *user_data) 120 | { 121 | memset(thread, 0, sizeof(*thread)); 122 | thread->name = name; 123 | thread->thread_proc = thread_proc; 124 | thread->init_proc = init_proc; 125 | thread->user_data = user_data; 126 | 127 | thread->init_event = CreateEvent(NULL, FALSE, FALSE, NULL); 128 | if(!thread->init_event) 129 | { 130 | return false; 131 | } 132 | 133 | thread->thread_handle = CreateThread(NULL, 0, thread_startup, thread, 0, &thread->thread_id); 134 | if (!thread->thread_handle) 135 | { 136 | return false; 137 | } 138 | 139 | WaitForSingleObject(thread->init_event, INFINITE); 140 | CloseHandle(thread->init_event); 141 | thread->init_event = NULL; 142 | 143 | return (1==thread->init_ok); 144 | } 145 | 146 | int wsi::wait_thread(THREAD *thread) 147 | { 148 | if (thread->thread_handle) 149 | { 150 | WaitForSingleObject(thread->thread_handle, INFINITE); 151 | assert(thread->exited && !thread->running); 152 | CloseHandle(thread->thread_handle); 153 | thread->thread_handle = NULL; 154 | } 155 | return thread->exit_code; 156 | } 157 | 158 | // In some cases, timeBeginPeriod(1) is necessary to get acceptable timing performance. 159 | // Decreasing the timer period can increase power consumption, so it must be used responsibly. 160 | void wsi::enable_timer_boost(bool boost) 161 | { 162 | static bool currently_boosted = false; 163 | if (currently_boosted != boost) 164 | { 165 | if (!currently_boosted) 166 | { 167 | timeBeginPeriod(1); 168 | } 169 | else 170 | { 171 | timeEndPeriod(1); 172 | } 173 | } 174 | } 175 | 176 | // https://randomascii.wordpress.com/2012/07/05/when-even-crashing-doesnt-work/ 177 | void wsi::disable_exception_swallowing(void) 178 | { 179 | typedef BOOL(WINAPI *tGetPolicy)(LPDWORD lpFlags); 180 | typedef BOOL(WINAPI *tSetPolicy)(DWORD dwFlags); 181 | const DWORD EXCEPTION_SWALLOWING = 0x1; 182 | 183 | HMODULE kernel32 = GetModuleHandleA("kernel32"); 184 | tGetPolicy pGetPolicy = (tGetPolicy)GetProcAddress(kernel32, "GetProcessUserModeExceptionPolicy"); 185 | tSetPolicy pSetPolicy = (tSetPolicy)GetProcAddress(kernel32, "SetProcessUserModeExceptionPolicy"); 186 | 187 | if (pGetPolicy && pSetPolicy) 188 | { 189 | DWORD dwFlags; 190 | if (pGetPolicy(&dwFlags)) 191 | { 192 | // Turn off the filter 193 | pSetPolicy(dwFlags & ~EXCEPTION_SWALLOWING); 194 | } 195 | } 196 | } 197 | 198 | 199 | void wsi::fit_and_center_rect(RECT& rect, const RECT& container, bool center) 200 | { 201 | int rect_width = rectangle_width(rect); 202 | int rect_height = rectangle_height(rect); 203 | int container_width = rectangle_width(container); 204 | int container_height = rectangle_height(container); 205 | 206 | rect_width = std::min(rect_width, container_width); 207 | rect_height = std::min(rect_height, container_height); 208 | 209 | if (center) 210 | { 211 | rect.left = container.left + (container_width - rect_width) / 2; 212 | rect.right = rect.left + rect_width; 213 | rect.top = container.top + (container_height - rect_height) / 2; 214 | rect.bottom = rect.top + rect_height; 215 | } 216 | else 217 | { 218 | rect.left = std::max(rect.left, container.left); 219 | rect.left = std::min(rect.left, container.right - rect_width); 220 | rect.top = std::max(rect.top, container.top); 221 | rect.top = std::min(rect.top, container.bottom - rect_height); 222 | rect.right = rect.left + rect_width; 223 | rect.bottom = rect.top + rect_height; 224 | } 225 | } 226 | 227 | bool wsi::get_monitor_rect(HMONITOR monitor, RECT *rect, bool workarea) 228 | { 229 | MONITORINFOEX info; 230 | 231 | ZeroMemory(&info, sizeof(info)); 232 | info.cbSize = sizeof(info); 233 | 234 | ZeroMemory(rect, sizeof(RECT)); 235 | 236 | if (GetMonitorInfo(monitor, (LPMONITORINFO)&info)) 237 | { 238 | DEVMODE dm; 239 | ZeroMemory(&dm, sizeof(DEVMODE)); 240 | dm.dmSize = sizeof(DEVMODE); 241 | 242 | // What we are really interested in is the registry settings for the monitor, not the current settings. 243 | if (!workarea && EnumDisplaySettings(info.szDevice, ENUM_REGISTRY_SETTINGS, &dm)) 244 | { 245 | // Sometimes the device mode can report 0x0 (e.g. on remote desktop) 246 | if (dm.dmPelsHeight && dm.dmPelsWidth) 247 | { 248 | rect->top = info.rcMonitor.top; 249 | rect->left = info.rcMonitor.left; 250 | rect->right = rect->left + dm.dmPelsWidth; 251 | rect->bottom = rect->top + dm.dmPelsHeight; 252 | } 253 | else 254 | { 255 | *rect = info.rcMonitor; 256 | } 257 | } 258 | else 259 | { 260 | *rect = workarea ? info.rcWork : info.rcMonitor; 261 | } 262 | 263 | return true; 264 | } 265 | 266 | return false; 267 | } 268 | 269 | bool wsi::get_monitor_dpi(HMONITOR monitor, SIZE *dpi) 270 | { 271 | if (init_shcore() && GetDpiForMonitor) 272 | { 273 | UINT x, y; 274 | HRESULT hr = GetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &x, &y); 275 | 276 | if (SUCCEEDED(hr)) 277 | { 278 | dpi->cx = x; 279 | dpi->cy = y; 280 | 281 | return true; 282 | } 283 | } 284 | 285 | // windows default DPI 286 | dpi->cx = 96; 287 | dpi->cy = 96; 288 | 289 | return false; 290 | } 291 | 292 | wsi::millisec64_t wsi::milliseconds() 293 | { 294 | static unsigned int last; 295 | static millisec64_t offset; 296 | static bool init = false; 297 | 298 | unsigned now = timeGetTime(); 299 | 300 | if (!init) 301 | { 302 | last = now; 303 | init = true; 304 | } 305 | else if (now < last) 306 | { 307 | // wrapped around. 308 | offset += 1ull << 32; 309 | } 310 | 311 | last = now; 312 | 313 | return now + offset; 314 | } 315 | 316 | bool wsi::enable_dpi_awareness() 317 | { 318 | static int succeeded = -1; 319 | 320 | if (succeeded == -1) 321 | { 322 | succeeded = 0; 323 | 324 | // Try to call the most modern API (SetProcessDpiAwareness) 325 | if (init_shcore() && SetProcessDpiAwareness) 326 | { 327 | SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); 328 | succeeded = 1; 329 | } 330 | 331 | // Fallback to SetProcessDPIAware() 332 | if (!succeeded) 333 | { 334 | HMODULE user32 = GetModuleHandleA("user32"); 335 | if (user32) 336 | { 337 | typedef BOOL(WINAPI *SETPROCESSDPIAWAREPROC)(VOID); 338 | SETPROCESSDPIAWAREPROC SetProcessDPIAware = (SETPROCESSDPIAWAREPROC)GetProcAddress(user32, "SetProcessDPIAware"); 339 | if (SetProcessDPIAware) 340 | { 341 | SetProcessDPIAware(); 342 | succeeded = 1; 343 | } 344 | } 345 | } 346 | } 347 | 348 | return succeeded == 1; 349 | } 350 | 351 | HRESULT wsi::make_dxgi_window_association(HWND hWnd, IUnknown *pD3dDevice) 352 | { 353 | HRESULT hr; 354 | 355 | IDXGIDevice * pDXGIDevice = 0; 356 | hr = pD3dDevice->QueryInterface(__uuidof(IDXGIDevice), (void **)&pDXGIDevice); 357 | if (SUCCEEDED(hr)) 358 | { 359 | IDXGIAdapter * pDXGIAdapter = 0; 360 | hr = pDXGIDevice->GetParent(__uuidof(IDXGIAdapter), (void **)&pDXGIAdapter); 361 | pDXGIDevice->Release(); 362 | if (SUCCEEDED(hr)) 363 | { 364 | IDXGIFactory * pIDXGIFactory = 0; 365 | hr = pDXGIAdapter->GetParent(__uuidof(IDXGIFactory), (void **)&pIDXGIFactory); 366 | pDXGIAdapter->Release(); 367 | if (SUCCEEDED(hr)) 368 | { 369 | hr = pIDXGIFactory->MakeWindowAssociation(hWnd, DXGI_MWA_NO_WINDOW_CHANGES | DXGI_MWA_NO_ALT_ENTER); 370 | pIDXGIFactory->Release(); 371 | } 372 | } 373 | } 374 | 375 | return hr; 376 | } 377 | 378 | bool wsi::hresult_succeeded(HRESULT hresult, const char *message, const char *file, int line) 379 | { 380 | if(!SUCCEEDED(hresult)) { 381 | _com_error err(hresult); 382 | wsi::log_message(file, line, "%s\n\tHRESULT Failure %08x: %s", message, hresult, err.ErrorMessage()); 383 | return false; 384 | } 385 | return true; 386 | } 387 | 388 | void wsi::queue::init(void *mem, int q_element_size, int q_diameter) 389 | { 390 | this->element_size = q_element_size; 391 | this->diameter = q_diameter; 392 | this->read_index = this->write_index = 0; 393 | 394 | if (mem) 395 | { 396 | buffer = mem; 397 | user_mem = true; 398 | } 399 | else 400 | { 401 | buffer = calloc(diameter, element_size); 402 | user_mem = false; 403 | } 404 | } 405 | 406 | wsi::queue::~queue() 407 | { 408 | if (!user_mem) 409 | { 410 | free(buffer); 411 | } 412 | } 413 | 414 | void *wsi::queue::read() 415 | { 416 | if (size() > 0) 417 | { 418 | void *src = element(read_index); 419 | increment(&read_index); 420 | return src; 421 | } 422 | return 0; 423 | } 424 | 425 | void *wsi::queue::write() 426 | { 427 | if (size() < diameter - 1) 428 | { 429 | void *dest = element(write_index); 430 | increment(&write_index); 431 | return dest; 432 | } 433 | return 0; 434 | } 435 | 436 | void wsi::queue::clear(bool dealloc) 437 | { 438 | if (dealloc) 439 | { 440 | free(buffer); 441 | memset(this, 0, sizeof(queue)); 442 | } 443 | else 444 | { 445 | read_index = write_index = 0; 446 | } 447 | } 448 | 449 | int wsi::queue::size() 450 | { 451 | int d = write_index - read_index; 452 | return d >= 0 ? d : diameter + d; 453 | } 454 | 455 | void wsi::queue::increment(int *index) 456 | { 457 | int j = *index + 1; 458 | *index = j >= diameter ? 0 : j; 459 | } 460 | 461 | void *wsi::queue::element(int index) 462 | { 463 | return (char*)buffer + index*element_size; 464 | } 465 | -------------------------------------------------------------------------------- /Source/wsi_utils.hpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2017 Intel Corporation 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | // use this file except in compliance with the License. You may obtain a copy 6 | // of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | // License for the specific language governing permissions and limitations 14 | // under the License. 15 | //////////////////////////////////////////////////////////////////////////////// 16 | #pragma once 17 | 18 | #define NOMINMAX 19 | #include 20 | 21 | #include 22 | #include 23 | 24 | namespace wsi 25 | { 26 | // basic threads 27 | 28 | typedef struct tagTHREAD 29 | { 30 | bool running, init_ok, exited, quit_signal; 31 | HANDLE init_event; 32 | HANDLE thread_handle; 33 | DWORD thread_id; 34 | int exit_code; 35 | void *user_data; 36 | const char *name; 37 | int(*init_proc)(struct tagTHREAD*, void*); 38 | int(*thread_proc)(struct tagTHREAD*, void*); 39 | } THREAD, *PTHREAD; 40 | 41 | void set_thread_name(DWORD thread_id, const char* thread_name); 42 | bool create_thread(THREAD *thread, const char *name, int(*thread_proc)(THREAD*, void*), int(*init_proc)(THREAD*, void*), void *user_data); 43 | int wait_thread(THREAD *thread); 44 | 45 | // misc windows stuff 46 | void enable_timer_boost(bool boost); 47 | void disable_exception_swallowing(); 48 | 49 | // rects 50 | void fit_and_center_rect(RECT& rect, const RECT& container, bool center); 51 | inline int rectangle_width(const RECT& rect) { return rect.right - rect.left; } 52 | inline int rectangle_height(const RECT& rect) { return rect.bottom - rect.top; } 53 | 54 | // monitors 55 | bool get_monitor_rect(HMONITOR monitor, RECT *rect, bool workarea); 56 | bool get_monitor_dpi(HMONITOR monitor, SIZE *dpi); 57 | 58 | // time 59 | typedef unsigned long long millisec64_t; 60 | // NB: not thread safe! 61 | // recommended that this only be used from the main thread. 62 | millisec64_t milliseconds(); 63 | 64 | // dpi awareness 65 | bool enable_dpi_awareness(); 66 | 67 | // DXGI 68 | HRESULT make_dxgi_window_association(HWND hWnd, IUnknown *pD3dDevice); 69 | 70 | // HRESULTs 71 | bool hresult_succeeded(HRESULT hresult, const char *message, const char *file, int line); 72 | 73 | // basic message queue (lockless single reader / single writer) 74 | // NB: This is not thread safe with more than one reader or writer! 75 | struct queue 76 | { 77 | queue() { } 78 | ~queue(); 79 | void init(void *mem, int element_size, int diameter); 80 | void *read(); 81 | void *write(); 82 | void clear(bool dealloc); 83 | int size(); 84 | private: 85 | void increment(int *index); 86 | void *element(int index); 87 | private: 88 | void *buffer; 89 | int read_index, write_index; 90 | int element_size; 91 | int diameter; 92 | bool user_mem; 93 | private: 94 | queue(const queue&); 95 | const queue& operator=(const queue&); 96 | }; 97 | } 98 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | 5 | http://www.apache.org/licenses/ 6 | 7 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 8 | 9 | 1. Definitions. 10 | 11 | "License" shall mean the terms and conditions for use, reproduction, and 12 | distribution as defined by Sections 1 through 9 of this document. 13 | 14 | "Licensor" shall mean the copyright owner or entity authorized by the copyright 15 | owner that is granting the License. 16 | 17 | "Legal Entity" shall mean the union of the acting entity and all other entities 18 | that control, are controlled by, or are under common control with that entity. 19 | For the purposes of this definition, "control" means (i) the power, direct or 20 | indirect, to cause the direction or management of such entity, whether by 21 | contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity exercising 25 | permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, including 28 | but not limited to software source code, documentation source, and configuration 29 | files. 30 | 31 | "Object" form shall mean any form resulting from mechanical transformation or 32 | translation of a Source form, including but not limited to compiled object code, 33 | generated documentation, and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or Object form, made 36 | available under the License, as indicated by a copyright notice that is included 37 | in or attached to the work (an example is provided in the Appendix below). 38 | 39 | "Derivative Works" shall mean any work, whether in Source or Object form, that 40 | is based on (or derived from) the Work and for which the editorial revisions, 41 | annotations, elaborations, or other modifications represent, as a whole, an 42 | original work of authorship. For the purposes of this License, Derivative Works 43 | shall not include works that remain separable from, or merely link (or bind by 44 | name) to the interfaces of, the Work and Derivative Works thereof. 45 | 46 | "Contribution" shall mean any work of authorship, including the original version 47 | of the Work and any modifications or additions to that Work or Derivative Works 48 | thereof, that is intentionally submitted to Licensor for inclusion in the Work 49 | by the copyright owner or by an individual or Legal Entity authorized to submit 50 | on behalf of the copyright owner. For the purposes of this definition, 51 | "submitted" means any form of electronic, verbal, or written communication sent 52 | to the Licensor or its representatives, including but not limited to 53 | communication on electronic mailing lists, source code control systems, and 54 | issue tracking systems that are managed by, or on behalf of, the Licensor for 55 | the purpose of discussing and improving the Work, but excluding communication 56 | that is conspicuously marked or otherwise designated in writing by the copyright 57 | owner as "Not a Contribution." 58 | 59 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf 60 | of whom a Contribution has been received by Licensor and subsequently 61 | incorporated within the Work. 62 | 63 | 2. Grant of Copyright License. Subject to the terms and conditions of this 64 | License, each Contributor hereby grants to You a perpetual, worldwide, 65 | non-exclusive, no-charge, royalty-free, irrevocable copyright license to 66 | reproduce, prepare Derivative Works of, publicly display, publicly perform, 67 | sublicense, and distribute the Work and such Derivative Works in Source or 68 | Object form. 69 | 70 | 3. Grant of Patent License. Subject to the terms and conditions of this License, 71 | each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, 72 | no-charge, royalty-free, irrevocable (except as stated in this section) patent 73 | license to make, have made, use, offer to sell, sell, import, and otherwise 74 | transfer the Work, where such license applies only to those patent claims 75 | licensable by such Contributor that are necessarily infringed by their 76 | Contribution(s) alone or by combination of their Contribution(s) with the Work 77 | to which such Contribution(s) was submitted. If You institute patent litigation 78 | against any entity (including a cross-claim or counterclaim in a lawsuit) 79 | alleging that the Work or a Contribution incorporated within the Work 80 | constitutes direct or contributory patent infringement, then any patent licenses 81 | granted to You under this License for that Work shall terminate as of the date 82 | such litigation is filed. 83 | 84 | 4. Redistribution. You may reproduce and distribute copies of the Work or 85 | Derivative Works thereof in any medium, with or without modifications, and in 86 | Source or Object form, provided that You meet the following conditions: 87 | You must give any other recipients of the Work or Derivative Works a copy of 88 | this License; and 89 | 90 | 91 | You must cause any modified files to carry prominent notices stating that You 92 | changed the files; and 93 | 94 | 95 | You must retain, in the Source form of any Derivative Works that You 96 | distribute, all copyright, patent, trademark, and attribution notices from the 97 | Source form of the Work, excluding those notices that do not pertain to any 98 | part of the Derivative Works; and 99 | 100 | 101 | If the Work includes a "NOTICE" text file as part of its distribution, then 102 | any Derivative Works that You distribute must include a readable copy of the 103 | attribution notices contained within such NOTICE file, excluding those notices 104 | that do not pertain to any part of the Derivative Works, in at least one of 105 | the following places: within a NOTICE text file distributed as part of the 106 | Derivative Works; within the Source form or documentation, if provided along 107 | with the Derivative Works; or, within a display generated by the Derivative 108 | Works, if and wherever such third-party notices normally appear. The contents 109 | of the NOTICE file are for informational purposes only and do not modify the 110 | License. You may add Your own attribution notices within Derivative Works that 111 | You distribute, alongside or as an addendum to the NOTICE text from the Work, 112 | provided that such additional attribution notices cannot be construed as 113 | modifying the License. 114 | You may add Your own copyright statement to Your modifications and may provide 115 | additional or different license terms and conditions for use, reproduction, or 116 | distribution of Your modifications, or for any such Derivative Works as a whole, 117 | provided Your use, reproduction, and distribution of the Work otherwise complies 118 | with the conditions stated in this License. 119 | 120 | 5. Submission of Contributions. Unless You explicitly state otherwise, any 121 | Contribution intentionally submitted for inclusion in the Work by You to the 122 | Licensor shall be under the terms and conditions of this License, without any 123 | additional terms or conditions. Notwithstanding the above, nothing herein shall 124 | supersede or modify the terms of any separate license agreement you may have 125 | executed with Licensor regarding such Contributions. 126 | 127 | 6. Trademarks. This License does not grant permission to use the trade names, 128 | trademarks, service marks, or product names of the Licensor, except as required 129 | for reasonable and customary use in describing the origin of the Work and 130 | reproducing the content of the NOTICE file. 131 | 132 | 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in 133 | writing, Licensor provides the Work (and each Contributor provides its 134 | Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 135 | KIND, either express or implied, including, without limitation, any warranties 136 | or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 137 | PARTICULAR PURPOSE. You are solely responsible for determining the 138 | appropriateness of using or redistributing the Work and assume any risks 139 | associated with Your exercise of permissions under this License. 140 | 141 | 8. Limitation of Liability. In no event and under no legal theory, whether in 142 | tort (including negligence), contract, or otherwise, unless required by 143 | applicable law (such as deliberate and grossly negligent acts) or agreed to in 144 | writing, shall any Contributor be liable to You for damages, including any 145 | direct, indirect, special, incidental, or consequential damages of any character 146 | arising as a result of this License or out of the use or inability to use the 147 | Work (including but not limited to damages for loss of goodwill, work stoppage, 148 | computer failure or malfunction, or any and all other commercial damages or 149 | losses), even if such Contributor has been advised of the possibility of such 150 | damages. 151 | 152 | 9. Accepting Warranty or Additional Liability. While redistributing the Work or 153 | Derivative Works thereof, You may choose to offer, and charge a fee for, 154 | acceptance of support, warranty, indemnity, or other liability obligations 155 | and/or rights consistent with this License. However, in accepting such 156 | obligations, You may act only on Your own behalf and on Your sole 157 | responsibility, not on behalf of any other Contributor, and only if You agree to 158 | indemnify, defend, and hold each Contributor harmless for any liability incurred 159 | by, or claims asserted against, such Contributor by reason of your accepting any 160 | such warranty or additional liability. 161 | 162 | END OF TERMS AND CONDITIONS 163 | 164 | APPENDIX: How to apply the Apache License to your work 165 | 166 | To apply the Apache License to your work, attach the following boilerplate 167 | notice, with the fields enclosed by brackets "[]" replaced with your own 168 | identifying information. (Don't include the brackets!) The text should be 169 | enclosed in the appropriate comment syntax for the file format. We also 170 | recommend that a file or class name and description of purpose be included on 171 | the same "printed page" as the copyright notice for easier identification within 172 | third-party archives. 173 | 174 | Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, 175 | Version 2.0 (the "License"); you may not use this file except in compliance with 176 | the License. You may obtain a copy of the License at 177 | http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or 178 | agreed to in writing, software distributed under the License is distributed on 179 | an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 180 | or implied. See the License for the specific language governing permissions and 181 | limitations under the License. 182 | -------------------------------------------------------------------------------- /thumbnail_320.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GameTechDev/FlipModelD3D12/f6f9233abd6957a77a1cf0d41050a3529352bf5a/thumbnail_320.png -------------------------------------------------------------------------------- /thumbnail_full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GameTechDev/FlipModelD3D12/f6f9233abd6957a77a1cf0d41050a3529352bf5a/thumbnail_full.png --------------------------------------------------------------------------------