├── .clang-format ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── assets ├── triangle.frag.dxbc ├── triangle.frag.hlsl ├── triangle.vert.dxbc └── triangle.vert.hlsl ├── license.md ├── readme.md └── src ├── Main.cpp ├── Renderer.cpp └── Renderer.h /.clang-format: -------------------------------------------------------------------------------- 1 | # Run manually to reformat a file: 2 | # clang-format -i --style=file 3 | BasedOnStyle: llvm 4 | DerivePointerAlignment: false 5 | UseTab: Never 6 | AllowShortIfStatementsOnASingleLine: true 7 | IndentWidth: 4 8 | TabWidth: 4 9 | BreakBeforeBraces: Allman 10 | AlignTrailingComments: true 11 | AllowAllParametersOfDeclarationOnNextLine: true 12 | Cpp11BracedListStyle: true 13 | IndentWrappedFunctionNames: true 14 | PointerAlignment: Left 15 | FixNamespaceComments: false -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # CMake 2 | CMakeCache.txt 3 | CMakeFiles 4 | CMakeScripts 5 | Testing 6 | Makefile 7 | cmake_install.cmake 8 | install_manifest.txt 9 | compile_commands.json 10 | CTestTestfile.cmake 11 | *.tcl 12 | *_autogen 13 | *.dir 14 | build/ 15 | visualstudio/ 16 | xcode/ 17 | androidstudio/ 18 | make/ 19 | webassembly/ 20 | 21 | ## Visual Studio 22 | # User-specific files 23 | *.vs 24 | *.vscode 25 | *.sln 26 | *.vcxproj 27 | *.filters 28 | *.suo 29 | *.user 30 | *.userosscache 31 | *.sln.docstates 32 | 33 | 34 | # User-specific files (MonoDevelop/Xamarin Studio) 35 | *.userprefs 36 | 37 | # Build results 38 | [Dd]ebug/ 39 | [Dd]ebugPublic/ 40 | [Rr]elease/ 41 | [Rr]eleases/ 42 | x64/ 43 | x86/ 44 | bld/ 45 | [Bb]in/ 46 | [Oo]bj/ 47 | [Ll]og/ 48 | 49 | # Windows 50 | 51 | *.DS_Store 52 | 53 | # Visual Studio 2015 cache/options directory 54 | .vs/ 55 | # Uncomment if you have tasks that create the project's static files in wwwroot 56 | #wwwroot/ 57 | 58 | # MSTest test Results 59 | [Tt]est[Rr]esult*/ 60 | [Bb]uild[Ll]og.* 61 | 62 | # NUNIT 63 | *.VisualState.xml 64 | TestResult.xml 65 | 66 | # Build Results of an ATL Project 67 | [Dd]ebugPS/ 68 | [Rr]eleasePS/ 69 | dlldata.c 70 | 71 | # Benchmark Results 72 | BenchmarkDotNet.Artifacts/ 73 | 74 | # .NET Core 75 | project.lock.json 76 | project.fragment.lock.json 77 | artifacts/ 78 | **/Properties/launchSettings.json 79 | 80 | *_i.c 81 | *_p.c 82 | *_i.h 83 | *.ilk 84 | *.meta 85 | *.obj 86 | *.pch 87 | *.pdb 88 | *.pgc 89 | *.pgd 90 | *.rsp 91 | *.sbr 92 | *.tlb 93 | *.tli 94 | *.tlh 95 | *.tmp 96 | *.tmp_proj 97 | *.log 98 | *.vspscc 99 | *.vssscc 100 | .builds 101 | *.pidb 102 | *.svclog 103 | *.scc 104 | 105 | # Chutzpah Test files 106 | _Chutzpah* 107 | 108 | # Visual C++ cache files 109 | ipch/ 110 | *.aps 111 | *.ncb 112 | *.opendb 113 | *.opensdf 114 | *.sdf 115 | *.cachefile 116 | *.VC.db 117 | *.VC.VC.opendb 118 | 119 | # Visual Studio profiler 120 | *.psess 121 | *.vsp 122 | *.vspx 123 | *.sap 124 | 125 | # TFS 2012 Local Workspace 126 | $tf/ 127 | 128 | # Guidance Automation Toolkit 129 | *.gpState 130 | 131 | # ReSharper is a .NET coding add-in 132 | _ReSharper*/ 133 | *.[Rr]e[Ss]harper 134 | *.DotSettings.user 135 | 136 | # JustCode is a .NET coding add-in 137 | .JustCode 138 | 139 | # TeamCity is a build add-in 140 | _TeamCity* 141 | 142 | # DotCover is a Code Coverage Tool 143 | *.dotCover 144 | 145 | # Visual Studio code coverage results 146 | *.coverage 147 | *.coveragexml 148 | 149 | # NCrunch 150 | _NCrunch_* 151 | .*crunch*.local.xml 152 | nCrunchTemp_* 153 | 154 | # MightyMoose 155 | *.mm.* 156 | AutoTest.Net/ 157 | 158 | # Web workbench (sass) 159 | .sass-cache/ 160 | 161 | # Installshield output folder 162 | [Ee]xpress/ 163 | 164 | # DocProject is a documentation generator add-in 165 | DocProject/buildhelp/ 166 | DocProject/Help/*.HxT 167 | DocProject/Help/*.HxC 168 | DocProject/Help/*.hhc 169 | DocProject/Help/*.hhk 170 | DocProject/Help/*.hhp 171 | DocProject/Help/Html2 172 | DocProject/Help/html 173 | 174 | # Click-Once directory 175 | publish/ 176 | 177 | # Publish Web Output 178 | *.[Pp]ublish.xml 179 | *.azurePubxml 180 | # Note: Comment the next line if you want to checkin your web deploy settings, 181 | # but database connection strings (with potential passwords) will be unencrypted 182 | *.pubxml 183 | *.publishproj 184 | 185 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 186 | # checkin your Azure Web App publish settings, but sensitive information contained 187 | # in these scripts will be unencrypted 188 | PublishScripts/ 189 | 190 | # NuGet Packages 191 | *.nupkg 192 | # The packages folder can be ignored because of Package Restore 193 | **/packages/* 194 | # except build/, which is used as an MSBuild target. 195 | !**/packages/build/ 196 | # Uncomment if necessary however generally it will be regenerated when needed 197 | #!**/packages/repositories.config 198 | # NuGet v3's project.json files produces more ignorable files 199 | *.nuget.props 200 | *.nuget.targets 201 | 202 | # Microsoft Azure Build Output 203 | csx/ 204 | *.build.csdef 205 | 206 | # Microsoft Azure Emulator 207 | ecf/ 208 | rcf/ 209 | 210 | # Windows Store app package directories and files 211 | AppPackages/ 212 | BundleArtifacts/ 213 | Package.StoreAssociation.xml 214 | _pkginfo.txt 215 | *.appx 216 | 217 | # Visual Studio cache files 218 | # files ending in .cache can be ignored 219 | *.[Cc]ache 220 | # but keep track of directories ending in .cache 221 | !*.[Cc]ache/ 222 | 223 | # Others 224 | ClientBin/ 225 | ~$* 226 | *~ 227 | *.dbmdl 228 | *.dbproj.schemaview 229 | *.jfm 230 | *.pfx 231 | *.publishsettings 232 | orleans.codegen.cs 233 | 234 | # Since there are multiple workflows, uncomment next line to ignore bower_components 235 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 236 | #bower_components/ 237 | 238 | # RIA/Silverlight projects 239 | Generated_Code/ 240 | 241 | # Backup & report files from converting an old project file 242 | # to a newer Visual Studio version. Backup files are not needed, 243 | # because we have git 244 | _UpgradeReport_Files/ 245 | Backup*/ 246 | UpgradeLog*.XML 247 | UpgradeLog*.htm 248 | 249 | # SQL Server files 250 | *.mdf 251 | *.ldf 252 | *.ndf 253 | 254 | # Business Intelligence projects 255 | *.rdl.data 256 | *.bim.layout 257 | *.bim_*.settings 258 | 259 | # Microsoft Fakes 260 | FakesAssemblies/ 261 | 262 | # GhostDoc plugin setting file 263 | *.GhostDoc.xml 264 | 265 | # Node.js Tools for Visual Studio 266 | .ntvs_analysis.dat 267 | node_modules/ 268 | 269 | # Typescript v1 declaration files 270 | typings/ 271 | 272 | # Visual Studio 6 build log 273 | *.plg 274 | 275 | # Visual Studio 6 workspace options file 276 | *.opt 277 | 278 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 279 | *.vbw 280 | 281 | # Visual Studio LightSwitch build output 282 | **/*.HTMLClient/GeneratedArtifacts 283 | **/*.DesktopClient/GeneratedArtifacts 284 | **/*.DesktopClient/ModelManifest.xml 285 | **/*.Server/GeneratedArtifacts 286 | **/*.Server/ModelManifest.xml 287 | _Pvt_Extensions 288 | 289 | # Paket dependency manager 290 | .paket/paket.exe 291 | paket-files/ 292 | 293 | # FAKE - F# Make 294 | .fake/ 295 | 296 | # JetBrains Rider 297 | .idea/ 298 | *.sln.iml 299 | 300 | # CodeRush 301 | .cr/ 302 | 303 | # Python Tools for Visual Studio (PTVS) 304 | __pycache__/ 305 | *.pyc 306 | 307 | # Cake - Uncomment if you are using it 308 | # tools/** 309 | # !tools/packages.config 310 | 311 | # Tabs Studio 312 | *.tss 313 | 314 | # Telerik's JustMock configuration file 315 | *.jmconfig 316 | 317 | # BizTalk build output 318 | *.btp.cs 319 | *.btm.cs 320 | *.odx.cs 321 | *.xsd.cs 322 | 323 | # Xcode 324 | # 325 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 326 | 327 | ## Build generated 328 | build/ 329 | DerivedData/ 330 | 331 | ## Various settings 332 | *.pbxuser 333 | !default.pbxuser 334 | *.mode1v3 335 | !default.mode1v3 336 | *.mode2v3 337 | !default.mode2v3 338 | *.perspectivev3 339 | !default.perspectivev3 340 | xcuserdata/ 341 | 342 | ## Other 343 | *.moved-aside 344 | *.xccheckout 345 | *.xcscmblueprint 346 | *.db 347 | 348 | ## Xcode 349 | *.xcworkspace 350 | *.xcodeproj -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "external/crosswindow"] 2 | path = external/crosswindow 3 | url = https://github.com/alaingalvan/crosswindow.git 4 | [submodule "external/crosswindow-graphics"] 5 | path = external/crosswindow-graphics 6 | url = https://github.com/alaingalvan/crosswindow-graphics.git 7 | [submodule "external/glm"] 8 | path = external/glm 9 | url = https://github.com/g-truc/glm.git 10 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Project Info 2 | 3 | cmake_minimum_required(VERSION 3.6 FATAL_ERROR) 4 | cmake_policy(VERSION 3.6) 5 | project(DirectX12Seed 6 | VERSION 1.0.0.0 7 | LANGUAGES C CXX 8 | ) 9 | 10 | # ============================================================= 11 | 12 | # CMake Settings 13 | 14 | set_property(GLOBAL PROPERTY USE_FOLDERS ON) 15 | set(CMAKE_SUPPRESS_REGENERATION true) 16 | set(DCMAKE_GENERATOR_PLATFORM "x64") 17 | set(CMAKE_CXX_STANDARD 14) 18 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 19 | set(CMAKE_CXX_EXTENSIONS OFF) 20 | SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin) 21 | SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/bin) 22 | SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/bin) 23 | SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/bin) 24 | SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/bin) 25 | SET(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/bin) 26 | SET(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/bin) 27 | if(NOT CMAKE_DEBUG_POSTFIX) 28 | set(CMAKE_DEBUG_POSTFIX d) 29 | endif() 30 | 31 | # ============================================================= 32 | 33 | # Options 34 | 35 | set(XGFX_API DIRECTX12 CACHE STRING "Which graphics API to use, defaults to AUTO, can be NOOP, VULKAN, OPENGL, DIRECTX12, or METAL.") 36 | set_property( 37 | CACHE 38 | XGFX_API PROPERTY 39 | STRINGS DIRECTX12 40 | ) 41 | 42 | # ============================================================= 43 | 44 | # Dependencies 45 | 46 | # CrossWindow 47 | message(STATUS "Installing crosswindow via submodule") 48 | add_subdirectory(external/crosswindow) 49 | set_property(TARGET CrossWindow PROPERTY FOLDER "Dependencies") 50 | 51 | # CrossWindow-Graphics 52 | message(STATUS "Installing crosswindow-graphics via submodule") 53 | add_subdirectory(external/crosswindow-graphics) 54 | 55 | # GLM 56 | message(STATUS "Installing glm via submodule") 57 | set(BUILD_STATIC_LIBS ON) 58 | add_subdirectory(external/glm/glm) 59 | set_property(TARGET glm_static PROPERTY FOLDER "Dependencies") 60 | 61 | # ============================================================= 62 | 63 | # Sources 64 | 65 | file(GLOB_RECURSE FILE_SOURCES RELATIVE 66 | ${CMAKE_CURRENT_SOURCE_DIR} 67 | ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp 68 | ${CMAKE_CURRENT_SOURCE_DIR}/src/*.h 69 | ) 70 | 71 | # Solution Filters 72 | foreach(source IN LISTS FILE_SOURCES) 73 | get_filename_component(source_path "${source}" PATH) 74 | string(REPLACE "/" "\\" source_path_msvc "${source_path}") 75 | string(REPLACE "src" "" source_path_final "${source_path_msvc}") 76 | source_group("${source_path_final}" FILES "${source}") 77 | endforeach() 78 | 79 | # ============================================================= 80 | 81 | # Finalize App 82 | 83 | xwin_add_executable( 84 | ${PROJECT_NAME} 85 | "${FILE_SOURCES}" 86 | ) 87 | 88 | # ============================================================= 89 | 90 | # Finish Dependencies 91 | 92 | target_link_libraries( 93 | ${PROJECT_NAME} 94 | CrossWindowGraphics 95 | CrossWindow 96 | glm_static 97 | ) 98 | 99 | target_include_directories( 100 | ${PROJECT_NAME} 101 | PUBLIC external/glm 102 | ) 103 | 104 | add_dependencies( 105 | ${PROJECT_NAME} 106 | CrossWindowGraphics 107 | CrossWindow 108 | glm_static 109 | 110 | ) 111 | 112 | target_compile_definitions( 113 | ${PROJECT_NAME} 114 | PUBLIC XGFX_${XGFX_API}=1 115 | ) 116 | 117 | # ============================================================= 118 | 119 | # Finish Settings 120 | 121 | # Change output dir to bin 122 | set_target_properties(${PROJECT_NAME} PROPERTIES 123 | RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin 124 | ) 125 | # Change working directory to top dir to access `assets/shaders/` folder 126 | set_property(TARGET ${PROJECT_NAME} PROPERTY VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/..) 127 | set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ${PROJECT_NAME}) 128 | -------------------------------------------------------------------------------- /assets/triangle.frag.dxbc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alaingalvan/directx12-seed/c8711b02ca8f4b51c827ad2bc5fbd3f9286ee237/assets/triangle.frag.dxbc -------------------------------------------------------------------------------- /assets/triangle.frag.hlsl: -------------------------------------------------------------------------------- 1 | static float4 outFragColor; 2 | static float3 inColor; 3 | 4 | struct SPIRV_Cross_Input 5 | { 6 | float3 inColor : COLOR; 7 | }; 8 | 9 | struct SPIRV_Cross_Output 10 | { 11 | float4 outFragColor : SV_Target0; 12 | }; 13 | 14 | void frag_main() 15 | { 16 | outFragColor = float4(inColor, 1.0f); 17 | } 18 | 19 | SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) 20 | { 21 | inColor = stage_input.inColor; 22 | frag_main(); 23 | SPIRV_Cross_Output stage_output; 24 | stage_output.outFragColor = outFragColor; 25 | return stage_output; 26 | } 27 | -------------------------------------------------------------------------------- /assets/triangle.vert.dxbc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alaingalvan/directx12-seed/c8711b02ca8f4b51c827ad2bc5fbd3f9286ee237/assets/triangle.vert.dxbc -------------------------------------------------------------------------------- /assets/triangle.vert.hlsl: -------------------------------------------------------------------------------- 1 | cbuffer ubo : register(b0) 2 | { 3 | row_major float4x4 ubo_projectionMatrix : packoffset(c0); 4 | row_major float4x4 ubo_modelMatrix : packoffset(c4); 5 | row_major float4x4 ubo_viewMatrix : packoffset(c8); 6 | }; 7 | 8 | static float4 gl_Position; 9 | static float3 outColor; 10 | static float3 inColor; 11 | static float3 inPos; 12 | 13 | struct SPIRV_Cross_Input 14 | { 15 | float3 inPos : POSITION; 16 | float3 inColor : COLOR; 17 | }; 18 | 19 | struct SPIRV_Cross_Output 20 | { 21 | float3 outColor : COLOR; 22 | float4 gl_Position : SV_Position; 23 | }; 24 | 25 | void vert_main() 26 | { 27 | outColor = inColor; 28 | gl_Position = mul(float4(inPos, 1.0f), mul(ubo_modelMatrix, mul(ubo_viewMatrix, ubo_projectionMatrix))); 29 | } 30 | 31 | SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) 32 | { 33 | inColor = stage_input.inColor; 34 | inPos = stage_input.inPos; 35 | vert_main(); 36 | SPIRV_Cross_Output stage_output; 37 | stage_output.gl_Position = gl_Position; 38 | stage_output.outColor = outColor; 39 | return stage_output; 40 | } 41 | -------------------------------------------------------------------------------- /license.md: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. 4 | 5 | In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | 9 | For more information, please refer to -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ![Cover Art](https://alain.xyz/blog/raw-directx12/assets/cover.jpg) 2 | 3 | # DirectX 12 Seed 4 | 5 | [![cmake-img]][cmake-url] 6 | [![License][license-img]][license-url] 7 | 8 | A DirectX 12 repo you can use to get started with your own renderer. 9 | 10 | ## Setup 11 | 12 | First install: 13 | 14 | - [Git](https://git-scm.com/) 15 | 16 | - [CMake](https://cmake.org) 17 | 18 | - [Visual Studio](https://visualstudio.microsoft.com/downloads/) 19 | 20 | Then type the following in your [terminal](https://hyper.is/). 21 | 22 | ```bash 23 | # 🐑 Clone the repo 24 | git clone https://github.com/alaingalvan/directx12-seed --recurse-submodules 25 | 26 | # 💿 go inside the folder 27 | cd directx12-seed 28 | 29 | # 👯 If you forget to `recurse-submodules` you can always run: 30 | git submodule update --init 31 | 32 | # 👷 Make a build folder 33 | mkdir build 34 | cd build 35 | 36 | # 🖼️ To build your Visual Studio solution on Windows x64 37 | cmake .. -A x64 38 | 39 | # 🔨 Build project 40 | cmake --build . 41 | ``` 42 | 43 | > Refer to [this blog post on designing C++ libraries and apps](https://alain.xyz/blog/designing-a-cpp-library) for more details on CMake, Git Submodules, etc. 44 | 45 | ## Project Layout 46 | 47 | As your project becomes more complex, you'll want to separate files and organize your application to something more akin to a game or renderer, check out this post on [game engine architecture](https://alain.xyz/blog/game-engine-architecture) and this one on [real time renderer architecture](https://alain.xyz/blog/realtime-renderer-architectures) for more details. 48 | 49 | ```bash 50 | ├─ 📂 external/ # 👶 Dependencies 51 | │ ├─ 📁 crosswindow/ # 🖼️ OS Windows 52 | │ ├─ 📁 crosswindow-graphics/ # 🎨 DirectX 12 Swapchain Creation 53 | │ └─ 📁 glm/ # ➕ Linear Algebra 54 | ├─ 📂 src/ # 🌟 Source Files 55 | │ ├─ 📄 Utils.h # ⚙️ Utilities (Load Files, Check Shaders, etc.) 56 | │ ├─ 📄 Renderer.h # 🔺 Triangle Draw Code 57 | │ ├─ 📄 Renderer.cpp # - 58 | │ └─ 📄 Main.cpp # 🏁 Application Main 59 | ├─ 📄 .gitignore # 👁️ Ignore certain files in git repo 60 | ├─ 📄 CMakeLists.txt # 🔨 Build Script 61 | ├─ 📄 license.md # ⚖️ Your License (Unlicense) 62 | └─ 📃readme.md # 📖 Read Me! 63 | ``` 64 | 65 | [cmake-img]: https://img.shields.io/badge/cmake-3.6-1f9948.svg?style=flat-square 66 | [cmake-url]: https://cmake.org/ 67 | [license-img]: https://img.shields.io/:license-mit-blue.svg?style=flat-square 68 | [license-url]: https://opensource.org/licenses/MIT 69 | -------------------------------------------------------------------------------- /src/Main.cpp: -------------------------------------------------------------------------------- 1 | #include "CrossWindow/CrossWindow.h" 2 | #include "Renderer.h" 3 | 4 | void xmain(int argc, const char** argv) 5 | { 6 | // 🖼️ Create a window 7 | xwin::EventQueue eventQueue; 8 | xwin::Window window; 9 | 10 | xwin::WindowDesc windowDesc; 11 | windowDesc.name = "MainWindow"; 12 | windowDesc.title = "Hello Triangle"; 13 | windowDesc.visible = true; 14 | windowDesc.width = 1280; 15 | windowDesc.height = 720; 16 | //windowDesc.fullscreen = true; 17 | window.create(windowDesc, eventQueue); 18 | 19 | // 📸 Create a renderer 20 | Renderer renderer(window); 21 | 22 | // 🏁 Engine loop 23 | bool isRunning = true; 24 | while (isRunning) 25 | { 26 | bool shouldRender = true; 27 | 28 | // ♻️ Update the event queue 29 | eventQueue.update(); 30 | 31 | // 🎈 Iterate through that queue: 32 | while (!eventQueue.empty()) 33 | { 34 | //Update Events 35 | const xwin::Event& event = eventQueue.front(); 36 | 37 | if (event.type == xwin::EventType::Resize) 38 | { 39 | const xwin::ResizeData data = event.data.resize; 40 | renderer.resize(data.width, data.height); 41 | shouldRender = false; 42 | } 43 | 44 | if (event.type == xwin::EventType::Close) 45 | { 46 | window.close(); 47 | shouldRender = false; 48 | isRunning = false; 49 | } 50 | 51 | eventQueue.pop(); 52 | } 53 | 54 | // ✨ Update Visuals 55 | if (shouldRender) 56 | { 57 | renderer.render(); 58 | } 59 | } 60 | 61 | } -------------------------------------------------------------------------------- /src/Renderer.cpp: -------------------------------------------------------------------------------- 1 | #include "Renderer.h" 2 | 3 | using namespace glm; 4 | 5 | // Helper functions 6 | 7 | inline void ThrowIfFailed(HRESULT hr) 8 | { 9 | if (FAILED(hr)) 10 | { 11 | throw std::exception(); 12 | } 13 | } 14 | 15 | // Renderer 16 | 17 | Renderer::Renderer(xwin::Window& window) 18 | { 19 | mWindow; 20 | 21 | // Initialization 22 | mFactory = nullptr; 23 | mAdapter = nullptr; 24 | #if defined(_DEBUG) 25 | mDebugController = nullptr; 26 | #endif 27 | mDevice = nullptr; 28 | mCommandQueue = nullptr; 29 | mCommandAllocator = nullptr; 30 | mCommandList = nullptr; 31 | mSwapchain = nullptr; 32 | 33 | // Resources 34 | 35 | mVertexBuffer = nullptr; 36 | mIndexBuffer = nullptr; 37 | 38 | mUniformBuffer = nullptr; 39 | mUniformBufferHeap = nullptr; 40 | mMappedUniformBuffer = nullptr; 41 | 42 | mRootSignature = nullptr; 43 | mPipelineState = nullptr; 44 | 45 | // Current Frame 46 | mRtvHeap = nullptr; 47 | for (size_t i = 0; i < backbufferCount; ++i) 48 | { 49 | mRenderTargets[i] = nullptr; 50 | } 51 | // Sync 52 | mFence = nullptr; 53 | 54 | initializeAPI(window); 55 | initializeResources(); 56 | setupCommands(); 57 | tStart = std::chrono::high_resolution_clock::now(); 58 | } 59 | 60 | Renderer::~Renderer() 61 | { 62 | if (mSwapchain != nullptr) 63 | { 64 | mSwapchain->SetFullscreenState(false, nullptr); 65 | mSwapchain->Release(); 66 | mSwapchain = nullptr; 67 | } 68 | 69 | destroyCommands(); 70 | destroyFrameBuffer(); 71 | destroyResources(); 72 | destroyAPI(); 73 | } 74 | 75 | void Renderer::initializeAPI(xwin::Window& window) 76 | { 77 | // The renderer needs the window when resizing the swapchain 78 | mWindow = &window; 79 | 80 | // Create Factory 81 | 82 | UINT dxgiFactoryFlags = 0; 83 | #if defined(_DEBUG) 84 | ID3D12Debug* debugController; 85 | ThrowIfFailed(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController))); 86 | ThrowIfFailed( 87 | debugController->QueryInterface(IID_PPV_ARGS(&mDebugController))); 88 | mDebugController->EnableDebugLayer(); 89 | mDebugController->SetEnableGPUBasedValidation(true); 90 | 91 | dxgiFactoryFlags |= DXGI_CREATE_FACTORY_DEBUG; 92 | 93 | debugController->Release(); 94 | debugController = nullptr; 95 | 96 | #endif 97 | ThrowIfFailed( 98 | CreateDXGIFactory2(dxgiFactoryFlags, IID_PPV_ARGS(&mFactory))); 99 | 100 | // Create Adapter 101 | 102 | for (UINT adapterIndex = 0; 103 | DXGI_ERROR_NOT_FOUND != 104 | mFactory->EnumAdapters1(adapterIndex, &mAdapter); 105 | ++adapterIndex) 106 | { 107 | DXGI_ADAPTER_DESC1 desc; 108 | mAdapter->GetDesc1(&desc); 109 | 110 | if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) 111 | { 112 | // Don't select the Basic Render Driver adapter. 113 | continue; 114 | } 115 | 116 | // Check to see if the adapter supports Direct3D 12, but don't create 117 | // the actual device yet. 118 | if (SUCCEEDED(D3D12CreateDevice(mAdapter, D3D_FEATURE_LEVEL_12_0, 119 | _uuidof(ID3D12Device), nullptr))) 120 | { 121 | break; 122 | } 123 | 124 | // We won't use this adapter, so release it 125 | mAdapter->Release(); 126 | } 127 | 128 | // Create Device 129 | ID3D12Device* pDev = nullptr; 130 | ThrowIfFailed(D3D12CreateDevice(mAdapter, D3D_FEATURE_LEVEL_12_0, 131 | IID_PPV_ARGS(&mDevice))); 132 | 133 | mDevice->SetName(L"Hello Triangle Device"); 134 | 135 | #if defined(_DEBUG) 136 | // Get debug device 137 | ThrowIfFailed(mDevice->QueryInterface(&mDebugDevice)); 138 | #endif 139 | 140 | // Create Command Queue 141 | D3D12_COMMAND_QUEUE_DESC queueDesc = {}; 142 | queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; 143 | queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; 144 | 145 | ThrowIfFailed( 146 | mDevice->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&mCommandQueue))); 147 | 148 | // Create Command Allocator 149 | ThrowIfFailed(mDevice->CreateCommandAllocator( 150 | D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&mCommandAllocator))); 151 | 152 | // Sync 153 | ThrowIfFailed( 154 | mDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&mFence))); 155 | 156 | // Create Swapchain 157 | const xwin::WindowDesc wdesc = window.getDesc(); 158 | resize(wdesc.width, wdesc.height); 159 | } 160 | 161 | void Renderer::destroyAPI() 162 | { 163 | if (mFence) 164 | { 165 | mFence->Release(); 166 | mFence = nullptr; 167 | } 168 | 169 | if (mCommandAllocator) 170 | { 171 | ThrowIfFailed(mCommandAllocator->Reset()); 172 | mCommandAllocator->Release(); 173 | mCommandAllocator = nullptr; 174 | } 175 | 176 | if (mCommandQueue) 177 | { 178 | mCommandQueue->Release(); 179 | mCommandQueue = nullptr; 180 | } 181 | 182 | if (mDevice) 183 | { 184 | mDevice->Release(); 185 | mDevice = nullptr; 186 | } 187 | 188 | if (mAdapter) 189 | { 190 | mAdapter->Release(); 191 | mAdapter = nullptr; 192 | } 193 | 194 | if (mFactory) 195 | { 196 | mFactory->Release(); 197 | mFactory = nullptr; 198 | } 199 | 200 | #if defined(_DEBUG) 201 | if (mDebugController) 202 | { 203 | mDebugController->Release(); 204 | mDebugController = nullptr; 205 | } 206 | 207 | D3D12_RLDO_FLAGS flags = 208 | D3D12_RLDO_SUMMARY | D3D12_RLDO_DETAIL | D3D12_RLDO_IGNORE_INTERNAL; 209 | 210 | mDebugDevice->ReportLiveDeviceObjects(flags); 211 | 212 | if (mDebugDevice) 213 | { 214 | mDebugDevice->Release(); 215 | mDebugDevice = nullptr; 216 | } 217 | #endif 218 | } 219 | 220 | void Renderer::initFrameBuffer() 221 | { 222 | mCurrentBuffer = mSwapchain->GetCurrentBackBufferIndex(); 223 | 224 | // Create descriptor heaps. 225 | { 226 | // Describe and create a render target view (RTV) descriptor heap. 227 | D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc = {}; 228 | rtvHeapDesc.NumDescriptors = backbufferCount; 229 | rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; 230 | rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; 231 | ThrowIfFailed(mDevice->CreateDescriptorHeap(&rtvHeapDesc, 232 | IID_PPV_ARGS(&mRtvHeap))); 233 | 234 | mRtvDescriptorSize = mDevice->GetDescriptorHandleIncrementSize( 235 | D3D12_DESCRIPTOR_HEAP_TYPE_RTV); 236 | } 237 | 238 | // Create frame resources. 239 | { 240 | D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle( 241 | mRtvHeap->GetCPUDescriptorHandleForHeapStart()); 242 | 243 | // Create a RTV for each frame. 244 | for (UINT n = 0; n < backbufferCount; n++) 245 | { 246 | ThrowIfFailed( 247 | mSwapchain->GetBuffer(n, IID_PPV_ARGS(&mRenderTargets[n]))); 248 | mDevice->CreateRenderTargetView(mRenderTargets[n], nullptr, 249 | rtvHandle); 250 | rtvHandle.ptr += (1 * mRtvDescriptorSize); 251 | } 252 | } 253 | } 254 | 255 | void Renderer::destroyFrameBuffer() 256 | { 257 | for (size_t i = 0; i < backbufferCount; ++i) 258 | { 259 | if (mRenderTargets[i]) 260 | { 261 | mRenderTargets[i]->Release(); 262 | mRenderTargets[i] = 0; 263 | } 264 | } 265 | if (mRtvHeap) 266 | { 267 | mRtvHeap->Release(); 268 | mRtvHeap = nullptr; 269 | } 270 | } 271 | 272 | void Renderer::initializeResources() 273 | { 274 | // Create the root signature. 275 | { 276 | D3D12_FEATURE_DATA_ROOT_SIGNATURE featureData = {}; 277 | 278 | // This is the highest version the sample supports. If 279 | // CheckFeatureSupport succeeds, the HighestVersion returned will not be 280 | // greater than this. 281 | featureData.HighestVersion = D3D_ROOT_SIGNATURE_VERSION_1_1; 282 | 283 | if (FAILED(mDevice->CheckFeatureSupport(D3D12_FEATURE_ROOT_SIGNATURE, 284 | &featureData, 285 | sizeof(featureData)))) 286 | { 287 | featureData.HighestVersion = D3D_ROOT_SIGNATURE_VERSION_1_0; 288 | } 289 | 290 | D3D12_DESCRIPTOR_RANGE1 ranges[1]; 291 | ranges[0].BaseShaderRegister = 0; 292 | ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_CBV; 293 | ranges[0].NumDescriptors = 1; 294 | ranges[0].RegisterSpace = 0; 295 | ranges[0].OffsetInDescriptorsFromTableStart = 0; 296 | ranges[0].Flags = D3D12_DESCRIPTOR_RANGE_FLAG_NONE; 297 | 298 | D3D12_ROOT_PARAMETER1 rootParameters[1]; 299 | rootParameters[0].ParameterType = 300 | D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; 301 | rootParameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX; 302 | 303 | rootParameters[0].DescriptorTable.NumDescriptorRanges = 1; 304 | rootParameters[0].DescriptorTable.pDescriptorRanges = ranges; 305 | 306 | D3D12_VERSIONED_ROOT_SIGNATURE_DESC rootSignatureDesc; 307 | rootSignatureDesc.Version = D3D_ROOT_SIGNATURE_VERSION_1_1; 308 | rootSignatureDesc.Desc_1_1.Flags = 309 | D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT; 310 | rootSignatureDesc.Desc_1_1.NumParameters = 1; 311 | rootSignatureDesc.Desc_1_1.pParameters = rootParameters; 312 | rootSignatureDesc.Desc_1_1.NumStaticSamplers = 0; 313 | rootSignatureDesc.Desc_1_1.pStaticSamplers = nullptr; 314 | 315 | ID3DBlob* signature; 316 | ID3DBlob* error; 317 | try 318 | { 319 | ThrowIfFailed(D3D12SerializeVersionedRootSignature( 320 | &rootSignatureDesc, &signature, &error)); 321 | ThrowIfFailed(mDevice->CreateRootSignature( 322 | 0, signature->GetBufferPointer(), signature->GetBufferSize(), 323 | IID_PPV_ARGS(&mRootSignature))); 324 | mRootSignature->SetName(L"Hello Triangle Root Signature"); 325 | } 326 | catch (std::exception e) 327 | { 328 | const char* errStr = (const char*)error->GetBufferPointer(); 329 | std::cout << errStr; 330 | error->Release(); 331 | error = nullptr; 332 | } 333 | 334 | if (signature) 335 | { 336 | signature->Release(); 337 | signature = nullptr; 338 | } 339 | } 340 | 341 | // Create the pipeline state, which includes compiling and loading shaders. 342 | { 343 | ID3DBlob* vertexShader = nullptr; 344 | ID3DBlob* pixelShader = nullptr; 345 | ID3DBlob* errors = nullptr; 346 | 347 | #if defined(_DEBUG) 348 | // Enable better shader debugging with the graphics debugging tools. 349 | UINT compileFlags = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION; 350 | #else 351 | UINT compileFlags = 0; 352 | #endif 353 | 354 | std::string path = ""; 355 | char pBuf[1024]; 356 | 357 | _getcwd(pBuf, 1024); 358 | path = pBuf; 359 | path += "\\"; 360 | std::wstring wpath = std::wstring(path.begin(), path.end()); 361 | 362 | std::string vertCompiledPath = path, fragCompiledPath = path; 363 | vertCompiledPath += "assets\\triangle.vert.dxbc"; 364 | fragCompiledPath += "assets\\triangle.frag.dxbc"; 365 | 366 | #define COMPILESHADERS 367 | #ifdef COMPILESHADERS 368 | std::wstring vertPath = wpath + L"assets\\triangle.vert.hlsl"; 369 | std::wstring fragPath = wpath + L"assets\\triangle.frag.hlsl"; 370 | 371 | try 372 | { 373 | ThrowIfFailed(D3DCompileFromFile(vertPath.c_str(), nullptr, nullptr, 374 | "main", "vs_5_0", compileFlags, 0, 375 | &vertexShader, &errors)); 376 | ThrowIfFailed(D3DCompileFromFile(fragPath.c_str(), nullptr, nullptr, 377 | "main", "ps_5_0", compileFlags, 0, 378 | &pixelShader, &errors)); 379 | } 380 | catch (std::exception e) 381 | { 382 | const char* errStr = (const char*)errors->GetBufferPointer(); 383 | std::cout << errStr; 384 | errors->Release(); 385 | errors = nullptr; 386 | } 387 | 388 | std::ofstream vsOut(vertCompiledPath, std::ios::out | std::ios::binary), 389 | fsOut(fragCompiledPath, std::ios::out | std::ios::binary); 390 | 391 | vsOut.write((const char*)vertexShader->GetBufferPointer(), 392 | vertexShader->GetBufferSize()); 393 | fsOut.write((const char*)pixelShader->GetBufferPointer(), 394 | pixelShader->GetBufferSize()); 395 | 396 | #else 397 | std::vector vsBytecodeData = readFile(vertCompiledPath); 398 | std::vector fsBytecodeData = readFile(fragCompiledPath); 399 | 400 | #endif 401 | // Define the vertex input layout. 402 | D3D12_INPUT_ELEMENT_DESC inputElementDescs[] = { 403 | {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, 404 | D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}, 405 | {"COLOR", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, 406 | D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}}; 407 | 408 | // Create the UBO. 409 | { 410 | // Note: using upload heaps to transfer static data like vert 411 | // buffers is not recommended. Every time the GPU needs it, the 412 | // upload heap will be marshalled over. Please read up on Default 413 | // Heap usage. An upload heap is used here for code simplicity and 414 | // because there are very few verts to actually transfer. 415 | D3D12_HEAP_PROPERTIES heapProps; 416 | heapProps.Type = D3D12_HEAP_TYPE_UPLOAD; 417 | heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; 418 | heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; 419 | heapProps.CreationNodeMask = 1; 420 | heapProps.VisibleNodeMask = 1; 421 | 422 | D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {}; 423 | heapDesc.NumDescriptors = 1; 424 | heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; 425 | heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; 426 | ThrowIfFailed(mDevice->CreateDescriptorHeap( 427 | &heapDesc, IID_PPV_ARGS(&mUniformBufferHeap))); 428 | 429 | D3D12_RESOURCE_DESC uboResourceDesc; 430 | uboResourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; 431 | uboResourceDesc.Alignment = 0; 432 | uboResourceDesc.Width = (sizeof(uboVS) + 255) & ~255; 433 | uboResourceDesc.Height = 1; 434 | uboResourceDesc.DepthOrArraySize = 1; 435 | uboResourceDesc.MipLevels = 1; 436 | uboResourceDesc.Format = DXGI_FORMAT_UNKNOWN; 437 | uboResourceDesc.SampleDesc.Count = 1; 438 | uboResourceDesc.SampleDesc.Quality = 0; 439 | uboResourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; 440 | uboResourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE; 441 | 442 | ThrowIfFailed(mDevice->CreateCommittedResource( 443 | &heapProps, D3D12_HEAP_FLAG_NONE, &uboResourceDesc, 444 | D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, 445 | IID_PPV_ARGS(&mUniformBuffer))); 446 | mUniformBufferHeap->SetName( 447 | L"Constant Buffer Upload Resource Heap"); 448 | 449 | D3D12_CONSTANT_BUFFER_VIEW_DESC cbvDesc = {}; 450 | cbvDesc.BufferLocation = mUniformBuffer->GetGPUVirtualAddress(); 451 | cbvDesc.SizeInBytes = 452 | (sizeof(uboVS) + 255) & 453 | ~255; // CB size is required to be 256-byte aligned. 454 | 455 | D3D12_CPU_DESCRIPTOR_HANDLE cbvHandle( 456 | mUniformBufferHeap->GetCPUDescriptorHandleForHeapStart()); 457 | cbvHandle.ptr = 458 | cbvHandle.ptr + mDevice->GetDescriptorHandleIncrementSize( 459 | D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV) * 460 | 0; 461 | 462 | mDevice->CreateConstantBufferView(&cbvDesc, cbvHandle); 463 | 464 | // We do not intend to read from this resource on the CPU. (End is 465 | // less than or equal to begin) 466 | D3D12_RANGE readRange; 467 | readRange.Begin = 0; 468 | readRange.End = 0; 469 | 470 | ThrowIfFailed(mUniformBuffer->Map( 471 | 0, &readRange, 472 | reinterpret_cast(&mMappedUniformBuffer))); 473 | memcpy(mMappedUniformBuffer, &uboVS, sizeof(uboVS)); 474 | mUniformBuffer->Unmap(0, &readRange); 475 | } 476 | 477 | // Describe and create the graphics pipeline state object (PSO). 478 | D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {}; 479 | psoDesc.InputLayout = {inputElementDescs, _countof(inputElementDescs)}; 480 | psoDesc.pRootSignature = mRootSignature; 481 | 482 | D3D12_SHADER_BYTECODE vsBytecode; 483 | D3D12_SHADER_BYTECODE psBytecode; 484 | 485 | #ifdef COMPILESHADERS 486 | vsBytecode.pShaderBytecode = vertexShader->GetBufferPointer(); 487 | vsBytecode.BytecodeLength = vertexShader->GetBufferSize(); 488 | 489 | psBytecode.pShaderBytecode = pixelShader->GetBufferPointer(); 490 | psBytecode.BytecodeLength = pixelShader->GetBufferSize(); 491 | #else 492 | vsBytecode.pShaderBytecode = vsBytecodeData.data(); 493 | vsBytecode.BytecodeLength = vsBytecodeData.size(); 494 | 495 | psBytecode.pShaderBytecode = fsBytecodeData.data(); 496 | psBytecode.BytecodeLength = fsBytecodeData.size(); 497 | #endif 498 | 499 | psoDesc.VS = vsBytecode; 500 | psoDesc.PS = psBytecode; 501 | 502 | D3D12_RASTERIZER_DESC rasterDesc; 503 | rasterDesc.FillMode = D3D12_FILL_MODE_SOLID; 504 | rasterDesc.CullMode = D3D12_CULL_MODE_NONE; 505 | rasterDesc.FrontCounterClockwise = FALSE; 506 | rasterDesc.DepthBias = D3D12_DEFAULT_DEPTH_BIAS; 507 | rasterDesc.DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP; 508 | rasterDesc.SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS; 509 | rasterDesc.DepthClipEnable = TRUE; 510 | rasterDesc.MultisampleEnable = FALSE; 511 | rasterDesc.AntialiasedLineEnable = FALSE; 512 | rasterDesc.ForcedSampleCount = 0; 513 | rasterDesc.ConservativeRaster = 514 | D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF; 515 | 516 | psoDesc.RasterizerState = rasterDesc; 517 | 518 | D3D12_BLEND_DESC blendDesc; 519 | blendDesc.AlphaToCoverageEnable = FALSE; 520 | blendDesc.IndependentBlendEnable = FALSE; 521 | const D3D12_RENDER_TARGET_BLEND_DESC defaultRenderTargetBlendDesc = { 522 | FALSE, 523 | FALSE, 524 | D3D12_BLEND_ONE, 525 | D3D12_BLEND_ZERO, 526 | D3D12_BLEND_OP_ADD, 527 | D3D12_BLEND_ONE, 528 | D3D12_BLEND_ZERO, 529 | D3D12_BLEND_OP_ADD, 530 | D3D12_LOGIC_OP_NOOP, 531 | D3D12_COLOR_WRITE_ENABLE_ALL, 532 | }; 533 | for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i) 534 | blendDesc.RenderTarget[i] = defaultRenderTargetBlendDesc; 535 | 536 | psoDesc.BlendState = blendDesc; 537 | psoDesc.DepthStencilState.DepthEnable = FALSE; 538 | psoDesc.DepthStencilState.StencilEnable = FALSE; 539 | psoDesc.SampleMask = UINT_MAX; 540 | psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; 541 | psoDesc.NumRenderTargets = 1; 542 | psoDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM; 543 | psoDesc.SampleDesc.Count = 1; 544 | try 545 | { 546 | ThrowIfFailed(mDevice->CreateGraphicsPipelineState( 547 | &psoDesc, IID_PPV_ARGS(&mPipelineState))); 548 | } 549 | catch (std::exception e) 550 | { 551 | std::cout << "Failed to create Graphics Pipeline!"; 552 | } 553 | 554 | if (vertexShader) 555 | { 556 | vertexShader->Release(); 557 | vertexShader = nullptr; 558 | } 559 | 560 | if (pixelShader) 561 | { 562 | pixelShader->Release(); 563 | pixelShader = nullptr; 564 | } 565 | } 566 | 567 | createCommands(); 568 | 569 | // Command lists are created in the recording state, but there is nothing 570 | // to record yet. The main loop expects it to be closed, so close it now. 571 | ThrowIfFailed(mCommandList->Close()); 572 | 573 | // Create the vertex buffer. 574 | { 575 | const UINT vertexBufferSize = sizeof(mVertexBufferData); 576 | 577 | // Note: using upload heaps to transfer static data like vert buffers is 578 | // not recommended. Every time the GPU needs it, the upload heap will be 579 | // marshalled over. Please read up on Default Heap usage. An upload heap 580 | // is used here for code simplicity and because there are very few verts 581 | // to actually transfer. 582 | D3D12_HEAP_PROPERTIES heapProps; 583 | heapProps.Type = D3D12_HEAP_TYPE_UPLOAD; 584 | heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; 585 | heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; 586 | heapProps.CreationNodeMask = 1; 587 | heapProps.VisibleNodeMask = 1; 588 | 589 | D3D12_RESOURCE_DESC vertexBufferResourceDesc; 590 | vertexBufferResourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; 591 | vertexBufferResourceDesc.Alignment = 0; 592 | vertexBufferResourceDesc.Width = vertexBufferSize; 593 | vertexBufferResourceDesc.Height = 1; 594 | vertexBufferResourceDesc.DepthOrArraySize = 1; 595 | vertexBufferResourceDesc.MipLevels = 1; 596 | vertexBufferResourceDesc.Format = DXGI_FORMAT_UNKNOWN; 597 | vertexBufferResourceDesc.SampleDesc.Count = 1; 598 | vertexBufferResourceDesc.SampleDesc.Quality = 0; 599 | vertexBufferResourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; 600 | vertexBufferResourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE; 601 | 602 | ThrowIfFailed(mDevice->CreateCommittedResource( 603 | &heapProps, D3D12_HEAP_FLAG_NONE, &vertexBufferResourceDesc, 604 | D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, 605 | IID_PPV_ARGS(&mVertexBuffer))); 606 | 607 | // Copy the triangle data to the vertex buffer. 608 | UINT8* pVertexDataBegin; 609 | 610 | // We do not intend to read from this resource on the CPU. 611 | D3D12_RANGE readRange; 612 | readRange.Begin = 0; 613 | readRange.End = 0; 614 | 615 | ThrowIfFailed(mVertexBuffer->Map( 616 | 0, &readRange, reinterpret_cast(&pVertexDataBegin))); 617 | memcpy(pVertexDataBegin, mVertexBufferData, sizeof(mVertexBufferData)); 618 | mVertexBuffer->Unmap(0, nullptr); 619 | 620 | // Initialize the vertex buffer view. 621 | mVertexBufferView.BufferLocation = 622 | mVertexBuffer->GetGPUVirtualAddress(); 623 | mVertexBufferView.StrideInBytes = sizeof(Vertex); 624 | mVertexBufferView.SizeInBytes = vertexBufferSize; 625 | } 626 | 627 | // Create the index buffer. 628 | { 629 | const UINT indexBufferSize = sizeof(mIndexBufferData); 630 | 631 | // Note: using upload heaps to transfer static data like vert buffers is 632 | // not recommended. Every time the GPU needs it, the upload heap will be 633 | // marshalled over. Please read up on Default Heap usage. An upload heap 634 | // is used here for code simplicity and because there are very few verts 635 | // to actually transfer. 636 | D3D12_HEAP_PROPERTIES heapProps; 637 | heapProps.Type = D3D12_HEAP_TYPE_UPLOAD; 638 | heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; 639 | heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; 640 | heapProps.CreationNodeMask = 1; 641 | heapProps.VisibleNodeMask = 1; 642 | 643 | D3D12_RESOURCE_DESC vertexBufferResourceDesc; 644 | vertexBufferResourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; 645 | vertexBufferResourceDesc.Alignment = 0; 646 | vertexBufferResourceDesc.Width = indexBufferSize; 647 | vertexBufferResourceDesc.Height = 1; 648 | vertexBufferResourceDesc.DepthOrArraySize = 1; 649 | vertexBufferResourceDesc.MipLevels = 1; 650 | vertexBufferResourceDesc.Format = DXGI_FORMAT_UNKNOWN; 651 | vertexBufferResourceDesc.SampleDesc.Count = 1; 652 | vertexBufferResourceDesc.SampleDesc.Quality = 0; 653 | vertexBufferResourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; 654 | vertexBufferResourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE; 655 | 656 | ThrowIfFailed(mDevice->CreateCommittedResource( 657 | &heapProps, D3D12_HEAP_FLAG_NONE, &vertexBufferResourceDesc, 658 | D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, 659 | IID_PPV_ARGS(&mIndexBuffer))); 660 | 661 | // Copy the triangle data to the vertex buffer. 662 | UINT8* pVertexDataBegin; 663 | 664 | // We do not intend to read from this resource on the CPU. 665 | D3D12_RANGE readRange; 666 | readRange.Begin = 0; 667 | readRange.End = 0; 668 | 669 | ThrowIfFailed(mIndexBuffer->Map( 670 | 0, &readRange, reinterpret_cast(&pVertexDataBegin))); 671 | memcpy(pVertexDataBegin, mIndexBufferData, sizeof(mIndexBufferData)); 672 | mIndexBuffer->Unmap(0, nullptr); 673 | 674 | // Initialize the vertex buffer view. 675 | mIndexBufferView.BufferLocation = mIndexBuffer->GetGPUVirtualAddress(); 676 | mIndexBufferView.Format = DXGI_FORMAT_R32_UINT; 677 | mIndexBufferView.SizeInBytes = indexBufferSize; 678 | } 679 | 680 | // Create synchronization objects and wait until assets have been uploaded 681 | // to the GPU. 682 | { 683 | mFenceValue = 1; 684 | 685 | // Create an event handle to use for frame synchronization. 686 | mFenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); 687 | if (mFenceEvent == nullptr) 688 | { 689 | ThrowIfFailed(HRESULT_FROM_WIN32(GetLastError())); 690 | } 691 | 692 | // Wait for the command list to execute; we are reusing the same command 693 | // list in our main loop but for now, we just want to wait for setup to 694 | // complete before continuing. 695 | // Signal and increment the fence value. 696 | const UINT64 fence = mFenceValue; 697 | ThrowIfFailed(mCommandQueue->Signal(mFence, fence)); 698 | mFenceValue++; 699 | 700 | // Wait until the previous frame is finished. 701 | if (mFence->GetCompletedValue() < fence) 702 | { 703 | ThrowIfFailed(mFence->SetEventOnCompletion(fence, mFenceEvent)); 704 | WaitForSingleObject(mFenceEvent, INFINITE); 705 | } 706 | 707 | mFrameIndex = mSwapchain->GetCurrentBackBufferIndex(); 708 | } 709 | } 710 | 711 | void Renderer::destroyResources() 712 | { 713 | // Sync 714 | CloseHandle(mFenceEvent); 715 | 716 | if (mPipelineState) 717 | { 718 | mPipelineState->Release(); 719 | mPipelineState = nullptr; 720 | } 721 | 722 | if (mRootSignature) 723 | { 724 | mRootSignature->Release(); 725 | mRootSignature = nullptr; 726 | } 727 | 728 | if (mVertexBuffer) 729 | { 730 | mVertexBuffer->Release(); 731 | mVertexBuffer = nullptr; 732 | } 733 | 734 | if (mIndexBuffer) 735 | { 736 | mIndexBuffer->Release(); 737 | mIndexBuffer = nullptr; 738 | } 739 | 740 | if (mUniformBuffer) 741 | { 742 | mUniformBuffer->Release(); 743 | mUniformBuffer = nullptr; 744 | } 745 | 746 | if (mUniformBufferHeap) 747 | { 748 | mUniformBufferHeap->Release(); 749 | mUniformBufferHeap = nullptr; 750 | } 751 | } 752 | 753 | void Renderer::createCommands() 754 | { 755 | // Create the command list. 756 | ThrowIfFailed(mDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, 757 | mCommandAllocator, mPipelineState, 758 | IID_PPV_ARGS(&mCommandList))); 759 | mCommandList->SetName(L"Hello Triangle Command List"); 760 | } 761 | 762 | void Renderer::setupCommands() 763 | { 764 | // Command list allocators can only be reset when the associated 765 | // command lists have finished execution on the GPU; apps should use 766 | // fences to determine GPU execution progress. 767 | ThrowIfFailed(mCommandAllocator->Reset()); 768 | 769 | // However, when ExecuteCommandList() is called on a particular command 770 | // list, that command list can then be reset at any time and must be before 771 | // re-recording. 772 | ThrowIfFailed(mCommandList->Reset(mCommandAllocator, mPipelineState)); 773 | 774 | // Set necessary state. 775 | mCommandList->SetGraphicsRootSignature(mRootSignature); 776 | mCommandList->RSSetViewports(1, &mViewport); 777 | mCommandList->RSSetScissorRects(1, &mSurfaceSize); 778 | 779 | ID3D12DescriptorHeap* pDescriptorHeaps[] = {mUniformBufferHeap}; 780 | mCommandList->SetDescriptorHeaps(_countof(pDescriptorHeaps), 781 | pDescriptorHeaps); 782 | 783 | D3D12_GPU_DESCRIPTOR_HANDLE srvHandle( 784 | mUniformBufferHeap->GetGPUDescriptorHandleForHeapStart()); 785 | mCommandList->SetGraphicsRootDescriptorTable(0, srvHandle); 786 | 787 | // Indicate that the back buffer will be used as a render target. 788 | D3D12_RESOURCE_BARRIER renderTargetBarrier; 789 | renderTargetBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; 790 | renderTargetBarrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; 791 | renderTargetBarrier.Transition.pResource = mRenderTargets[mFrameIndex]; 792 | renderTargetBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT; 793 | renderTargetBarrier.Transition.StateAfter = 794 | D3D12_RESOURCE_STATE_RENDER_TARGET; 795 | renderTargetBarrier.Transition.Subresource = 796 | D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; 797 | 798 | mCommandList->ResourceBarrier(1, &renderTargetBarrier); 799 | 800 | D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle( 801 | mRtvHeap->GetCPUDescriptorHandleForHeapStart()); 802 | rtvHandle.ptr = rtvHandle.ptr + (mFrameIndex * mRtvDescriptorSize); 803 | mCommandList->OMSetRenderTargets(1, &rtvHandle, FALSE, nullptr); 804 | 805 | // Record commands. 806 | const float clearColor[] = {0.2f, 0.2f, 0.2f, 1.0f}; 807 | mCommandList->ClearRenderTargetView(rtvHandle, clearColor, 0, nullptr); 808 | mCommandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); 809 | mCommandList->IASetVertexBuffers(0, 1, &mVertexBufferView); 810 | mCommandList->IASetIndexBuffer(&mIndexBufferView); 811 | 812 | mCommandList->DrawIndexedInstanced(3, 1, 0, 0, 0); 813 | 814 | // Indicate that the back buffer will now be used to present. 815 | D3D12_RESOURCE_BARRIER presentBarrier; 816 | presentBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; 817 | presentBarrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; 818 | presentBarrier.Transition.pResource = mRenderTargets[mFrameIndex]; 819 | presentBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; 820 | presentBarrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT; 821 | presentBarrier.Transition.Subresource = 822 | D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; 823 | 824 | mCommandList->ResourceBarrier(1, &presentBarrier); 825 | 826 | ThrowIfFailed(mCommandList->Close()); 827 | } 828 | 829 | void Renderer::destroyCommands() 830 | { 831 | if (mCommandList) 832 | { 833 | mCommandList->Reset(mCommandAllocator, mPipelineState); 834 | mCommandList->ClearState(mPipelineState); 835 | ThrowIfFailed(mCommandList->Close()); 836 | ID3D12CommandList* ppCommandLists[] = {mCommandList}; 837 | mCommandQueue->ExecuteCommandLists(_countof(ppCommandLists), 838 | ppCommandLists); 839 | 840 | // Wait for GPU to finish work 841 | const UINT64 fence = mFenceValue; 842 | ThrowIfFailed(mCommandQueue->Signal(mFence, fence)); 843 | mFenceValue++; 844 | if (mFence->GetCompletedValue() < fence) 845 | { 846 | ThrowIfFailed(mFence->SetEventOnCompletion(fence, mFenceEvent)); 847 | WaitForSingleObject(mFenceEvent, INFINITE); 848 | } 849 | 850 | mCommandList->Release(); 851 | mCommandList = nullptr; 852 | } 853 | } 854 | 855 | void Renderer::setupSwapchain(unsigned width, unsigned height) 856 | { 857 | 858 | mSurfaceSize.left = 0; 859 | mSurfaceSize.top = 0; 860 | mSurfaceSize.right = static_cast(mWidth); 861 | mSurfaceSize.bottom = static_cast(mHeight); 862 | 863 | mViewport.TopLeftX = 0.0f; 864 | mViewport.TopLeftY = 0.0f; 865 | mViewport.Width = static_cast(mWidth); 866 | mViewport.Height = static_cast(mHeight); 867 | mViewport.MinDepth = .1f; 868 | mViewport.MaxDepth = 1000.f; 869 | 870 | // Update Uniforms 871 | float zoom = 2.5f; 872 | 873 | // Update matrices 874 | uboVS.projectionMatrix = 875 | glm::perspective(45.0f, (float)mWidth / (float)mHeight, 0.01f, 1024.0f); 876 | 877 | uboVS.viewMatrix = 878 | glm::translate(glm::identity(), vec3(0.0f, 0.0f, zoom)); 879 | 880 | uboVS.modelMatrix = glm::identity(); 881 | 882 | if (mSwapchain != nullptr) 883 | { 884 | mSwapchain->ResizeBuffers(backbufferCount, mWidth, mHeight, 885 | DXGI_FORMAT_R8G8B8A8_UNORM, 0); 886 | } 887 | else 888 | { 889 | DXGI_SWAP_CHAIN_DESC1 swapchainDesc = {}; 890 | swapchainDesc.BufferCount = backbufferCount; 891 | swapchainDesc.Width = width; 892 | swapchainDesc.Height = height; 893 | swapchainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; 894 | swapchainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; 895 | swapchainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; 896 | swapchainDesc.SampleDesc.Count = 1; 897 | 898 | IDXGISwapChain1* swapchain = xgfx::createSwapchain( 899 | mWindow, mFactory, mCommandQueue, &swapchainDesc); 900 | HRESULT swapchainSupport = swapchain->QueryInterface( 901 | __uuidof(IDXGISwapChain3), (void**)&swapchain); 902 | if (SUCCEEDED(swapchainSupport)) 903 | { 904 | mSwapchain = (IDXGISwapChain3*)swapchain; 905 | } 906 | } 907 | mFrameIndex = mSwapchain->GetCurrentBackBufferIndex(); 908 | } 909 | 910 | void Renderer::resize(unsigned width, unsigned height) 911 | { 912 | mWidth = clamp(width, 1u, 0xffffu); 913 | mHeight = clamp(height, 1u, 0xffffu); 914 | 915 | // WAITING FOR THE FRAME TO COMPLETE BEFORE CONTINUING IS NOT BEST PRACTICE. 916 | // This is code implemented as such for simplicity. The 917 | // D3D12HelloFrameBuffering sample illustrates how to use fences for 918 | // efficient resource usage and to maximize GPU utilization. 919 | 920 | // Signal and increment the fence value. 921 | const UINT64 fence = mFenceValue; 922 | ThrowIfFailed(mCommandQueue->Signal(mFence, fence)); 923 | mFenceValue++; 924 | 925 | // Wait until the previous frame is finished. 926 | if (mFence->GetCompletedValue() < fence) 927 | { 928 | ThrowIfFailed(mFence->SetEventOnCompletion(fence, mFenceEvent)); 929 | WaitForSingleObjectEx(mFenceEvent, INFINITE, false); 930 | } 931 | 932 | destroyFrameBuffer(); 933 | setupSwapchain(width, height); 934 | initFrameBuffer(); 935 | } 936 | 937 | void Renderer::render() 938 | { 939 | // Framelimit set to 60 fps 940 | tEnd = std::chrono::high_resolution_clock::now(); 941 | float time = 942 | std::chrono::duration(tEnd - tStart).count(); 943 | if (time < (1000.0f / 60.0f)) 944 | { 945 | return; 946 | } 947 | tStart = std::chrono::high_resolution_clock::now(); 948 | 949 | { 950 | // Update Uniforms 951 | mElapsedTime += 0.001f * time; 952 | mElapsedTime = fmodf(mElapsedTime, 6.283185307179586f); 953 | 954 | uboVS.modelMatrix = glm::rotate(uboVS.modelMatrix, 0.001f * time, 955 | vec3(0.0f, 1.0f, 0.0f)); 956 | 957 | D3D12_RANGE readRange; 958 | readRange.Begin = 0; 959 | readRange.End = 0; 960 | 961 | ThrowIfFailed(mUniformBuffer->Map( 962 | 0, &readRange, reinterpret_cast(&mMappedUniformBuffer))); 963 | memcpy(mMappedUniformBuffer, &uboVS, sizeof(uboVS)); 964 | mUniformBuffer->Unmap(0, &readRange); 965 | } 966 | 967 | // Record all the commands we need to render the scene into the command 968 | // list. 969 | setupCommands(); 970 | 971 | // Execute the command list. 972 | ID3D12CommandList* ppCommandLists[] = {mCommandList}; 973 | mCommandQueue->ExecuteCommandLists(_countof(ppCommandLists), 974 | ppCommandLists); 975 | mSwapchain->Present(1, 0); 976 | 977 | // WAITING FOR THE FRAME TO COMPLETE BEFORE CONTINUING IS NOT BEST PRACTICE. 978 | 979 | // Signal and increment the fence value. 980 | const UINT64 fence = mFenceValue; 981 | ThrowIfFailed(mCommandQueue->Signal(mFence, fence)); 982 | mFenceValue++; 983 | 984 | // Wait until the previous frame is finished. 985 | if (mFence->GetCompletedValue() < fence) 986 | { 987 | ThrowIfFailed(mFence->SetEventOnCompletion(fence, mFenceEvent)); 988 | WaitForSingleObject(mFenceEvent, INFINITE); 989 | } 990 | 991 | mFrameIndex = mSwapchain->GetCurrentBackBufferIndex(); 992 | } -------------------------------------------------------------------------------- /src/Renderer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CrossWindow/CrossWindow.h" 4 | #include "CrossWindow/Graphics.h" 5 | 6 | #define GLM_FORCE_SSE42 1 7 | #define GLM_FORCE_DEFAULT_ALIGNED_GENTYPES 1 8 | #define GLM_FORCE_LEFT_HANDED 9 | #include "glm/glm.hpp" 10 | #include "glm/gtc/matrix_transform.hpp" 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | 20 | // Common Utils 21 | 22 | inline std::vector readFile(const std::string& filename) 23 | { 24 | std::ifstream file(filename, std::ios::ate | std::ios::binary); 25 | bool exists = (bool)file; 26 | 27 | if (!exists || !file.is_open()) 28 | { 29 | throw std::runtime_error("failed to open file!"); 30 | } 31 | 32 | size_t fileSize = (size_t)file.tellg(); 33 | std::vector buffer(fileSize); 34 | 35 | file.seekg(0); 36 | file.read(buffer.data(), fileSize); 37 | 38 | file.close(); 39 | 40 | return buffer; 41 | }; 42 | 43 | // Renderer 44 | 45 | class Renderer 46 | { 47 | public: 48 | Renderer(xwin::Window& window); 49 | 50 | ~Renderer(); 51 | 52 | // Render onto the render target 53 | void render(); 54 | 55 | // Resize the window and internal data structures 56 | void resize(unsigned width, unsigned height); 57 | 58 | protected: 59 | // Initialize your Graphics API 60 | void initializeAPI(xwin::Window& window); 61 | 62 | // Destroy any Graphics API data structures used in this example 63 | void destroyAPI(); 64 | 65 | // Initialize any resources such as VBOs, IBOs, used in this example 66 | void initializeResources(); 67 | 68 | // Destroy any resources used in this example 69 | void destroyResources(); 70 | 71 | // Create graphics API specific data structures to send commands to the GPU 72 | void createCommands(); 73 | 74 | // Set up commands used when rendering frame by this app 75 | void setupCommands(); 76 | 77 | // Destroy all commands 78 | void destroyCommands(); 79 | 80 | // Set up the FrameBuffer 81 | void initFrameBuffer(); 82 | 83 | void destroyFrameBuffer(); 84 | 85 | // Set up the RenderPass 86 | void createRenderPass(); 87 | 88 | void createSynchronization(); 89 | 90 | // Set up the swapchain 91 | void setupSwapchain(unsigned width, unsigned height); 92 | 93 | struct Vertex 94 | { 95 | float position[3]; 96 | float color[3]; 97 | }; 98 | 99 | Vertex mVertexBufferData[3] = {{{1.0f, -1.0f, 0.0f}, {1.0f, 0.0f, 0.0f}}, 100 | {{-1.0f, -1.0f, 0.0f}, {0.0f, 1.0f, 0.0f}}, 101 | {{0.0f, 1.0f, 0.0f}, {0.0f, 0.0f, 1.0f}}}; 102 | 103 | uint32_t mIndexBufferData[3] = {0, 1, 2}; 104 | 105 | std::chrono::time_point tStart, tEnd; 106 | float mElapsedTime = 0.0f; 107 | 108 | // Uniform data 109 | struct 110 | { 111 | glm::mat4 projectionMatrix; 112 | glm::mat4 modelMatrix; 113 | glm::mat4 viewMatrix; 114 | } uboVS; 115 | 116 | static const UINT backbufferCount = 2; 117 | 118 | xwin::Window* mWindow; 119 | unsigned mWidth, mHeight; 120 | 121 | // Initialization 122 | IDXGIFactory4* mFactory; 123 | IDXGIAdapter1* mAdapter; 124 | #if defined(_DEBUG) 125 | ID3D12Debug1* mDebugController; 126 | ID3D12DebugDevice* mDebugDevice; 127 | #endif 128 | ID3D12Device* mDevice; 129 | ID3D12CommandQueue* mCommandQueue; 130 | ID3D12CommandAllocator* mCommandAllocator; 131 | ID3D12GraphicsCommandList* mCommandList; 132 | 133 | // Current Frame 134 | UINT mCurrentBuffer; 135 | ID3D12DescriptorHeap* mRtvHeap; 136 | ID3D12Resource* mRenderTargets[backbufferCount]; 137 | IDXGISwapChain3* mSwapchain; 138 | 139 | // Resources 140 | D3D12_VIEWPORT mViewport; 141 | D3D12_RECT mSurfaceSize; 142 | 143 | ID3D12Resource* mVertexBuffer; 144 | ID3D12Resource* mIndexBuffer; 145 | 146 | ID3D12Resource* mUniformBuffer; 147 | ID3D12DescriptorHeap* mUniformBufferHeap; 148 | UINT8* mMappedUniformBuffer; 149 | 150 | D3D12_VERTEX_BUFFER_VIEW mVertexBufferView; 151 | D3D12_INDEX_BUFFER_VIEW mIndexBufferView; 152 | 153 | UINT mRtvDescriptorSize; 154 | ID3D12RootSignature* mRootSignature; 155 | ID3D12PipelineState* mPipelineState; 156 | 157 | // Sync 158 | UINT mFrameIndex; 159 | HANDLE mFenceEvent; 160 | ID3D12Fence* mFence; 161 | UINT64 mFenceValue; 162 | }; --------------------------------------------------------------------------------