├── .gitignore ├── CMakeLists.txt ├── LICENSE.md ├── README.md ├── examples ├── 00-hellotriangle │ ├── CMakeLists.txt │ ├── main.cpp │ ├── triangle.frag │ └── triangle.vert ├── 01-rtao │ ├── CMakeLists.txt │ ├── data │ │ ├── sponza.mtl │ │ ├── sponza.obj │ │ └── textures │ │ │ ├── 00_skap.JPG │ │ │ ├── 01_STUB.JPG │ │ │ ├── 01_S_ba.JPG │ │ │ ├── 01_St_kp.JPG │ │ │ ├── KAMEN-stup.JPG │ │ │ ├── KAMEN.JPG │ │ │ ├── prozor1.JPG │ │ │ ├── reljef.JPG │ │ │ ├── sp_luk.JPG │ │ │ ├── vrata_ko.JPG │ │ │ ├── vrata_kr.JPG │ │ │ └── x01_st.JPG │ ├── main.cpp │ ├── rtao.frag │ ├── rtao.vert │ └── samplerBlueNoiseErrorDistribution_128x128_OptimizedFor_2d2d2d2d_1spp.cpp ├── 02-pbr │ ├── CMakeLists.txt │ ├── data │ │ ├── SciFiHelmet │ │ │ ├── README.md │ │ │ ├── glTF │ │ │ │ ├── SciFiHelmet.bin │ │ │ │ ├── SciFiHelmet.gltf │ │ │ │ ├── SciFiHelmet_AmbientOcclusion.png │ │ │ │ ├── SciFiHelmet_BaseColor.png │ │ │ │ ├── SciFiHelmet_MetallicRoughness.png │ │ │ │ └── SciFiHelmet_Normal.png │ │ │ └── screenshot │ │ │ │ └── screenshot.jpg │ │ └── kiara_1_dawn_4k.hdr │ ├── ibl.comp │ ├── ibl.cpp │ ├── ibl.h │ ├── main.cpp │ ├── pbr.frag │ ├── pbr.vert │ ├── sky.frag │ ├── sky.vert │ ├── taa.frag │ └── taa.vert ├── 03-mesh │ ├── CMakeLists.txt │ ├── main.cpp │ ├── mesh.frag │ └── mesh.mesh ├── CMakeLists.txt └── common │ ├── CMakeLists.txt │ ├── fly_camera.cpp │ ├── fly_camera.h │ ├── gpu_scene.cpp │ ├── gpu_scene.h │ └── gpu_scene.hlsli ├── gfx.cpp ├── gfx.h ├── gfx_core.cpp ├── gfx_core.h ├── gfx_imgui.cpp ├── gfx_imgui.h ├── gfx_internal_types.cpp ├── gfx_internal_types.h ├── gfx_scene.cpp ├── gfx_scene.h ├── gfx_window.cpp ├── gfx_window.h └── third_party ├── .gitignore └── AmdDxExt ├── AmdExtD3D.h ├── AmdExtD3DCommandListMarkerApi.h ├── AmdExtD3DDeviceApi.h └── AmdPix3.h /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | .DS_Store 3 | imgui.ini 4 | .vscode 5 | .vs 6 | out/ 7 | cache/ 8 | .cache/ 9 | shader_cache/ 10 | shader_pdb/ 11 | compile_commands.json 12 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Guillaume Boissé 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gfx 2 | 3 | **gfx** is a minimalist and easy to use graphics API built on top of **Direct3D12**/**HLSL** intended for rapid prototyping. 4 | 5 | It supports: 6 | 7 | - Full shader reflection; no need for root signatures, pipeline states, descriptor tables, register indexing, etc. 8 | - Internal management of descriptor heaps, resource views, memory allocation, etc. 9 | - "Garbage collection" defers the release of freed resources to ensure the GPU is done with it; you can create and destroy anything at any time without worrying about synchronization. 10 | - Automatic placement of pipeline barriers and resource transitions. 11 | - Runtime shader reloading; simply call the `gfxKernelReloadAll()` function. 12 | - Basic GPU-based parallel primitives (min/max/sum scans and reductions as well as key-only/key-value pair sorting for various data types). 13 | - DXR-1.1 raytracing (i.e. using `RayQuery<>` object); also known as [inline raytracing](https://microsoft.github.io/DirectX-Specs/d3d/Raytracing.html#inline-raytracing). 14 | 15 | On top of `gfx.h`, three optional layers are available: 16 | 17 | - `gfx_imgui.h`: adds [Dear ImGui](https://github.com/ocornut/imgui) support. 18 | - `gfx_scene.h`: supports loading `.obj` files (`.gltf` coming soon). 19 | - `gfx_window.h`: window creation and basic event management. 20 | 21 | ## Usage 22 | 23 | Include the header corresponding to the desired level of functionality. 24 | 25 | In one `.cpp` file, define `#define GFX_IMPLEMENTATION_DEFINE` and include said header to include the implementation details. 26 | 27 | ## Motivation 28 | 29 | This project was born mostly out of frustration while researching rendering techniques using explicit APIs such as **Direct3D12** or **Vulkan**. 30 | 31 | These APIs intend to deliver higher performance for complex workloads on both the CPU and the GPU by giving more responsibility to the developer; unfortunately this is to the detriment of productivity especially during the initial prototyping/research phase due to their high level of complexity. 32 | 33 | **gfx** aims to provide an efficient API that's easy to learn, pleasant to use and quick to get things set up, while offering rapid iterations; it allows to get that "start from scratch" efficiency while still offering access to the basic necessary functionality (low-level access to GPU work scheduling, drawing UI overlays, etc.). 34 | 35 | ## Drawing a triangle 36 | 37 | Some example code for drawing a simple colored triangle: 38 | 39 | ```cpp 40 | // main.cpp 41 | 42 | #define GFX_IMPLEMENTATION_DEFINE 43 | #include "gfx_window.h" 44 | 45 | int main() 46 | { 47 | auto window = gfxCreateWindow(1280, 720); 48 | auto gfx = gfxCreateContext(window); 49 | 50 | float vertices[] = { 0.5f, -0.5f, 0.0f, 51 | 0.0f, 0.7f, 0.0f, 52 | -0.5f, -0.5f, 0.0f }; 53 | auto vertex_buffer = gfxCreateBuffer(gfx, sizeof(vertices), vertices); 54 | 55 | auto program = gfxCreateProgram(gfx, "triangle"); 56 | auto kernel = gfxCreateGraphicsKernel(gfx, program); 57 | 58 | for (auto time = 0.0f; !gfxWindowIsCloseRequested(window); time += 0.1f) 59 | { 60 | gfxWindowPumpEvents(window); 61 | 62 | float color[] = { 0.5f * cosf(time) + 0.5f, 63 | 0.5f * sinf(time) + 0.5f, 64 | 1.0f }; 65 | gfxProgramSetParameter(gfx, program, "Color", color); 66 | 67 | gfxCommandBindKernel(gfx, kernel); 68 | gfxCommandBindVertexBuffer(gfx, vertex_buffer); 69 | 70 | gfxCommandDraw(gfx, 3); 71 | 72 | gfxFrame(gfx); 73 | } 74 | 75 | gfxDestroyContext(gfx); 76 | gfxDestroyWindow(window); 77 | 78 | return 0; 79 | } 80 | ``` 81 | 82 | Here's the corresponding vertex shader: 83 | 84 | ```cpp 85 | // triangle.vert 86 | 87 | float4 main(float3 pos : Position) : SV_Position 88 | { 89 | return float4(pos, 1.0f); 90 | } 91 | ``` 92 | 93 | And pixel shader: 94 | 95 | ```cpp 96 | // triangle.frag 97 | 98 | float3 Color; 99 | 100 | float4 main() : SV_Target 101 | { 102 | return float4(Color, 1.0f); 103 | } 104 | ``` 105 | -------------------------------------------------------------------------------- /examples/00-hellotriangle/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(00-hellotriangle ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp) 2 | 3 | target_link_libraries(00-hellotriangle PUBLIC gfx) 4 | 5 | set_target_properties(00-hellotriangle PROPERTIES FOLDER "examples") 6 | 7 | set_target_properties(00-hellotriangle PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) 8 | 9 | file(GLOB SHADER_FILES CONFIGURE_DEPENDS 10 | ${CMAKE_CURRENT_SOURCE_DIR}/*.frag 11 | ${CMAKE_CURRENT_SOURCE_DIR}/*.vert) 12 | 13 | target_sources(00-hellotriangle PRIVATE ${SHADER_FILES}) 14 | 15 | source_group("Media Files\\Shaders" FILES ${SHADER_FILES}) 16 | 17 | set_source_files_properties(${SHADER_FILES} PROPERTIES VS_TOOL_OVERRIDE "None") 18 | 19 | add_custom_command(TARGET 00-hellotriangle POST_BUILD 20 | COMMAND ${CMAKE_COMMAND} -E copy $ $ 21 | COMMAND_EXPAND_LISTS 22 | ) 23 | -------------------------------------------------------------------------------- /examples/00-hellotriangle/main.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | MIT License 3 | 4 | Copyright (c) 2024 Guillaume Boissé 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | ****************************************************************************/ 24 | #include "gfx_window.h" 25 | 26 | int main() 27 | { 28 | auto window = gfxCreateWindow(1280, 720, "gfx - Hello, triangle!"); 29 | auto gfx = gfxCreateContext(window); 30 | 31 | float vertices[] = { 0.5f, -0.5f, 0.0f, 32 | 0.0f, 0.7f, 0.0f, 33 | -0.5f, -0.5f, 0.0f }; 34 | auto vertex_buffer = gfxCreateBuffer(gfx, sizeof(vertices), vertices); 35 | 36 | auto program = gfxCreateProgram(gfx, "triangle"); 37 | auto kernel = gfxCreateGraphicsKernel(gfx, program); 38 | 39 | for(float time = 0.0f; !gfxWindowIsCloseRequested(window); time += 0.1f) 40 | { 41 | gfxWindowPumpEvents(window); 42 | 43 | float color[] = { 0.5f * cosf(time) + 0.5f, 44 | 0.5f * sinf(time) + 0.5f, 45 | 1.0f }; 46 | gfxProgramSetParameter(gfx, program, "Color", color); 47 | 48 | gfxCommandBindKernel(gfx, kernel); 49 | gfxCommandBindVertexBuffer(gfx, vertex_buffer); 50 | 51 | gfxCommandDraw(gfx, 3); 52 | 53 | gfxFrame(gfx); 54 | } 55 | 56 | gfxDestroyContext(gfx); 57 | gfxDestroyWindow(window); 58 | 59 | return 0; 60 | } 61 | -------------------------------------------------------------------------------- /examples/00-hellotriangle/triangle.frag: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | MIT License 3 | 4 | Copyright (c) 2024 Guillaume Boissé 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | ****************************************************************************/ 24 | 25 | float3 Color; 26 | 27 | float4 main() : SV_Target 28 | { 29 | return float4(Color, 1.0f); 30 | } 31 | -------------------------------------------------------------------------------- /examples/00-hellotriangle/triangle.vert: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | MIT License 3 | 4 | Copyright (c) 2024 Guillaume Boissé 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | ****************************************************************************/ 24 | 25 | float4 main(float3 pos : Position) : SV_Position 26 | { 27 | return float4(pos, 1.0f); 28 | } 29 | -------------------------------------------------------------------------------- /examples/01-rtao/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(01-rtao ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp) 2 | 3 | target_link_libraries(01-rtao PUBLIC gfx) 4 | 5 | set_target_properties(01-rtao PROPERTIES FOLDER "examples") 6 | 7 | set_target_properties(01-rtao PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) 8 | 9 | file(GLOB SHADER_FILES CONFIGURE_DEPENDS 10 | ${CMAKE_CURRENT_SOURCE_DIR}/*.frag 11 | ${CMAKE_CURRENT_SOURCE_DIR}/*.vert) 12 | 13 | target_sources(01-rtao PRIVATE ${SHADER_FILES}) 14 | 15 | source_group("Media Files\\Shaders" FILES ${SHADER_FILES}) 16 | 17 | set_source_files_properties(${SHADER_FILES} PROPERTIES VS_TOOL_OVERRIDE "None") 18 | 19 | add_custom_command(TARGET 01-rtao POST_BUILD 20 | COMMAND ${CMAKE_COMMAND} -E copy $ $ 21 | COMMAND_EXPAND_LISTS 22 | ) 23 | -------------------------------------------------------------------------------- /examples/01-rtao/data/sponza.mtl: -------------------------------------------------------------------------------- 1 | # Blender MTL File: 'None' 2 | # Material Count: 20 3 | 4 | newmtl sp_00_luk_mal1 5 | Ns 225.000000 6 | Ka 1.000000 1.000000 1.000000 7 | Kd 0.800000 0.800000 0.800000 8 | Ks 0.500000 0.500000 0.500000 9 | Ke 0.000000 0.000000 0.000000 10 | Ni 1.000000 11 | d 1.000000 12 | illum 2 13 | map_Kd textures/01_St_kp.JPG 14 | 15 | newmtl sp_00_luk_mali 16 | Ns 225.000000 17 | Ka 1.000000 1.000000 1.000000 18 | Kd 0.800000 0.800000 0.800000 19 | Ks 0.500000 0.500000 0.500000 20 | Ke 0.000000 0.000000 0.000000 21 | Ni 1.000000 22 | d 1.000000 23 | illum 2 24 | map_Kd textures/sp_luk.JPG 25 | 26 | newmtl sp_00_pod 27 | Ns 225.000000 28 | Ka 1.000000 1.000000 1.000000 29 | Kd 0.800000 0.800000 0.800000 30 | Ks 0.500000 0.500000 0.500000 31 | Ke 0.000000 0.000000 0.000000 32 | Ni 1.000000 33 | d 1.000000 34 | illum 2 35 | map_Kd textures/KAMEN.JPG 36 | 37 | newmtl sp_00_prozor 38 | Ns 225.000000 39 | Ka 1.000000 1.000000 1.000000 40 | Kd 0.800000 0.800000 0.800000 41 | Ks 0.500000 0.500000 0.500000 42 | Ke 0.000000 0.000000 0.000000 43 | Ni 1.000000 44 | d 1.000000 45 | illum 2 46 | map_Kd textures/prozor1.JPG 47 | 48 | newmtl sp_00_stup 49 | Ns 225.000000 50 | Ka 1.000000 1.000000 1.000000 51 | Kd 0.800000 0.800000 0.800000 52 | Ks 0.500000 0.500000 0.500000 53 | Ke 0.000000 0.000000 0.000000 54 | Ni 1.000000 55 | d 1.000000 56 | illum 2 57 | map_Kd textures/01_STUB.JPG 58 | 59 | newmtl sp_00_svod 60 | Ns 225.000000 61 | Ka 1.000000 1.000000 1.000000 62 | Kd 0.800000 0.800000 0.800000 63 | Ks 0.500000 0.500000 0.500000 64 | Ke 0.000000 0.000000 0.000000 65 | Ni 1.000000 66 | d 1.000000 67 | illum 2 68 | map_Kd textures/KAMEN-stup.JPG 69 | 70 | newmtl sp_00_vrata_kock 71 | Ns 225.000000 72 | Ka 1.000000 1.000000 1.000000 73 | Kd 0.800000 0.800000 0.800000 74 | Ks 0.500000 0.500000 0.500000 75 | Ke 0.000000 0.000000 0.000000 76 | Ni 1.000000 77 | d 1.000000 78 | illum 2 79 | map_Kd textures/vrata_ko.JPG 80 | 81 | newmtl sp_00_vrata_krug 82 | Ns 225.000000 83 | Ka 1.000000 1.000000 1.000000 84 | Kd 0.800000 0.800000 0.800000 85 | Ks 0.500000 0.500000 0.500000 86 | Ke 0.000000 0.000000 0.000000 87 | Ni 1.000000 88 | d 1.000000 89 | illum 2 90 | map_Kd textures/vrata_kr.JPG 91 | 92 | newmtl sp_00_zid 93 | Ns 225.000000 94 | Ka 1.000000 1.000000 1.000000 95 | Kd 0.800000 0.800000 0.800000 96 | Ks 0.500000 0.500000 0.500000 97 | Ke 0.000000 0.000000 0.000000 98 | Ni 1.000000 99 | d 1.000000 100 | illum 2 101 | map_Kd textures/KAMEN.JPG 102 | 103 | newmtl sp_01_luk_a 104 | Ns 225.000000 105 | Ka 1.000000 1.000000 1.000000 106 | Kd 0.800000 0.800000 0.800000 107 | Ks 0.500000 0.500000 0.500000 108 | Ke 0.000000 0.000000 0.000000 109 | Ni 1.000000 110 | d 1.000000 111 | illum 2 112 | map_Kd textures/sp_luk.JPG 113 | 114 | newmtl sp_01_stub 115 | Ns 225.000000 116 | Ka 1.000000 1.000000 1.000000 117 | Kd 0.800000 0.800000 0.800000 118 | Ks 0.500000 0.500000 0.500000 119 | Ke 0.000000 0.000000 0.000000 120 | Ni 1.000000 121 | d 1.000000 122 | illum 2 123 | map_Kd textures/01_STUB.JPG 124 | 125 | newmtl sp_01_stub_baza 126 | Ns 225.000000 127 | Ka 1.000000 1.000000 1.000000 128 | Kd 0.800000 0.800000 0.800000 129 | Ks 0.500000 0.500000 0.500000 130 | Ke 0.000000 0.000000 0.000000 131 | Ni 1.000000 132 | d 1.000000 133 | illum 2 134 | map_Kd textures/01_S_ba.JPG 135 | 136 | newmtl sp_01_stub_baza_ 137 | Ns 225.000000 138 | Ka 1.000000 1.000000 1.000000 139 | Kd 0.784314 0.784314 0.784314 140 | Ks 0.500000 0.500000 0.500000 141 | Ke 0.000000 0.000000 0.000000 142 | Ni 1.000000 143 | d 1.000000 144 | illum 2 145 | 146 | newmtl sp_01_stub_kut 147 | Ns 225.000000 148 | Ka 1.000000 1.000000 1.000000 149 | Kd 0.800000 0.800000 0.800000 150 | Ks 0.500000 0.500000 0.500000 151 | Ke 0.000000 0.000000 0.000000 152 | Ni 1.000000 153 | d 1.000000 154 | illum 2 155 | map_Kd textures/01_STUB.JPG 156 | 157 | newmtl sp_01_stup 158 | Ns 225.000000 159 | Ka 1.000000 1.000000 1.000000 160 | Kd 0.800000 0.800000 0.800000 161 | Ks 0.500000 0.500000 0.500000 162 | Ke 0.000000 0.000000 0.000000 163 | Ni 1.000000 164 | d 1.000000 165 | illum 2 166 | map_Kd textures/x01_st.JPG 167 | 168 | newmtl sp_01_stup_baza 169 | Ns 225.000000 170 | Ka 1.000000 1.000000 1.000000 171 | Kd 0.800000 0.800000 0.800000 172 | Ks 0.500000 0.500000 0.500000 173 | Ke 0.000000 0.000000 0.000000 174 | Ni 1.000000 175 | d 1.000000 176 | illum 2 177 | map_Kd textures/01_S_ba.JPG 178 | 179 | newmtl sp_02_reljef 180 | Ns 225.000000 181 | Ka 1.000000 1.000000 1.000000 182 | Kd 0.800000 0.800000 0.800000 183 | Ks 0.500000 0.500000 0.500000 184 | Ke 0.000000 0.000000 0.000000 185 | Ni 1.000000 186 | d 1.000000 187 | illum 2 188 | map_Kd textures/reljef.JPG 189 | 190 | newmtl sp_svod_kapitel 191 | Ns 225.000000 192 | Ka 1.000000 1.000000 1.000000 193 | Kd 0.800000 0.800000 0.800000 194 | Ks 0.500000 0.500000 0.500000 195 | Ke 0.000000 0.000000 0.000000 196 | Ni 1.000000 197 | d 1.000000 198 | illum 2 199 | map_Kd textures/00_skap.JPG 200 | 201 | newmtl sp_vijenac 202 | Ns 225.000000 203 | Ka 1.000000 1.000000 1.000000 204 | Kd 0.800000 0.800000 0.800000 205 | Ks 0.500000 0.500000 0.500000 206 | Ke 0.000000 0.000000 0.000000 207 | Ni 1.000000 208 | d 1.000000 209 | illum 2 210 | map_Kd textures/00_skap.JPG 211 | 212 | newmtl sp_zid_vani 213 | Ns 225.000000 214 | Ka 1.000000 1.000000 1.000000 215 | Kd 0.800000 0.800000 0.800000 216 | Ks 0.500000 0.500000 0.500000 217 | Ke 0.000000 0.000000 0.000000 218 | Ni 1.000000 219 | d 1.000000 220 | illum 2 221 | map_Kd textures/KAMEN.JPG 222 | -------------------------------------------------------------------------------- /examples/01-rtao/data/textures/00_skap.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gboisse/gfx/fa1d2264bc131db933f840dd6ac54bf80fe7c540/examples/01-rtao/data/textures/00_skap.JPG -------------------------------------------------------------------------------- /examples/01-rtao/data/textures/01_STUB.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gboisse/gfx/fa1d2264bc131db933f840dd6ac54bf80fe7c540/examples/01-rtao/data/textures/01_STUB.JPG -------------------------------------------------------------------------------- /examples/01-rtao/data/textures/01_S_ba.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gboisse/gfx/fa1d2264bc131db933f840dd6ac54bf80fe7c540/examples/01-rtao/data/textures/01_S_ba.JPG -------------------------------------------------------------------------------- /examples/01-rtao/data/textures/01_St_kp.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gboisse/gfx/fa1d2264bc131db933f840dd6ac54bf80fe7c540/examples/01-rtao/data/textures/01_St_kp.JPG -------------------------------------------------------------------------------- /examples/01-rtao/data/textures/KAMEN-stup.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gboisse/gfx/fa1d2264bc131db933f840dd6ac54bf80fe7c540/examples/01-rtao/data/textures/KAMEN-stup.JPG -------------------------------------------------------------------------------- /examples/01-rtao/data/textures/KAMEN.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gboisse/gfx/fa1d2264bc131db933f840dd6ac54bf80fe7c540/examples/01-rtao/data/textures/KAMEN.JPG -------------------------------------------------------------------------------- /examples/01-rtao/data/textures/prozor1.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gboisse/gfx/fa1d2264bc131db933f840dd6ac54bf80fe7c540/examples/01-rtao/data/textures/prozor1.JPG -------------------------------------------------------------------------------- /examples/01-rtao/data/textures/reljef.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gboisse/gfx/fa1d2264bc131db933f840dd6ac54bf80fe7c540/examples/01-rtao/data/textures/reljef.JPG -------------------------------------------------------------------------------- /examples/01-rtao/data/textures/sp_luk.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gboisse/gfx/fa1d2264bc131db933f840dd6ac54bf80fe7c540/examples/01-rtao/data/textures/sp_luk.JPG -------------------------------------------------------------------------------- /examples/01-rtao/data/textures/vrata_ko.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gboisse/gfx/fa1d2264bc131db933f840dd6ac54bf80fe7c540/examples/01-rtao/data/textures/vrata_ko.JPG -------------------------------------------------------------------------------- /examples/01-rtao/data/textures/vrata_kr.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gboisse/gfx/fa1d2264bc131db933f840dd6ac54bf80fe7c540/examples/01-rtao/data/textures/vrata_kr.JPG -------------------------------------------------------------------------------- /examples/01-rtao/data/textures/x01_st.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gboisse/gfx/fa1d2264bc131db933f840dd6ac54bf80fe7c540/examples/01-rtao/data/textures/x01_st.JPG -------------------------------------------------------------------------------- /examples/01-rtao/main.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | MIT License 3 | 4 | Copyright (c) 2024 Guillaume Boissé 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | ****************************************************************************/ 24 | 25 | #include "gfx_window.h" 26 | #include "gfx_scene.h" 27 | #include "gfx_imgui.h" 28 | 29 | #include "glm/gtc/matrix_transform.hpp" 30 | #include "samplerBlueNoiseErrorDistribution_128x128_OptimizedFor_2d2d2d2d_1spp.cpp" 31 | 32 | // Need to match shader-side structure 33 | struct Vertex 34 | { 35 | glm::vec4 position; 36 | glm::vec4 normal; 37 | glm::vec2 uv; 38 | }; 39 | 40 | float CalculateHaltonNumber(uint32_t index, uint32_t base) 41 | { 42 | float f = 1.0f; 43 | float result = 0.0f; 44 | 45 | for(uint32_t i = index; i > 0;) 46 | { 47 | f /= base; 48 | result = result + f * (i % base); 49 | i = (uint32_t)(i / (float)base); 50 | } 51 | 52 | return result; 53 | } 54 | 55 | int32_t main() 56 | { 57 | GfxWindow window = gfxCreateWindow(1280, 720, "gfx - RTAO"); 58 | GfxContext gfx = gfxCreateContext(window); 59 | GfxScene scene = gfxCreateScene(); 60 | gfxImGuiInitialize(gfx); 61 | 62 | // Upload the scene to GPU memory 63 | std::vector index_buffers; 64 | std::vector vertex_buffers; 65 | std::vector albedo_buffers; 66 | 67 | gfxSceneImport(scene, "data/sponza.obj"); // import our scene 68 | 69 | uint32_t const mesh_count = gfxSceneGetMeshCount(scene); 70 | uint32_t const material_count = gfxSceneGetMaterialCount(scene); 71 | uint32_t const instance_count = gfxSceneGetInstanceCount(scene); 72 | 73 | for(uint32_t i = 0; i < mesh_count; ++i) 74 | { 75 | GfxConstRef mesh_ref = gfxSceneGetMeshHandle(scene, i); 76 | 77 | uint32_t const index_count = (uint32_t)mesh_ref->indices.size(); 78 | uint32_t const vertex_count = (uint32_t)mesh_ref->vertices.size(); 79 | 80 | std::vector vertices(vertex_count); 81 | 82 | for(uint32_t v = 0; v < vertex_count; ++v) 83 | { 84 | vertices[v].position = glm::vec4(mesh_ref->vertices[v].position, 1.0f); 85 | vertices[v].normal = glm::vec4(mesh_ref->vertices[v].normal, 0.0f); 86 | vertices[v].uv = glm::vec2(mesh_ref->vertices[v].uv); 87 | } 88 | 89 | GfxBuffer index_buffer = gfxCreateBuffer(gfx, index_count, mesh_ref->indices.data()); 90 | GfxBuffer vertex_buffer = gfxCreateBuffer(gfx, vertex_count, vertices.data()); 91 | 92 | uint32_t const mesh_id = (uint32_t)mesh_ref; 93 | if(mesh_id >= index_buffers.size()) 94 | { 95 | index_buffers.resize(mesh_id + 1); 96 | vertex_buffers.resize(mesh_id + 1); 97 | } 98 | index_buffers[mesh_id] = index_buffer; 99 | vertex_buffers[mesh_id] = vertex_buffer; 100 | } 101 | 102 | for(uint32_t i = 0; i < material_count; ++i) 103 | { 104 | GfxConstRef material_ref = gfxSceneGetMaterialHandle(scene, i); 105 | 106 | GfxTexture albedo_buffer; 107 | 108 | if(material_ref->albedo_map) 109 | { 110 | GfxImage const &albedo_map = *material_ref->albedo_map; 111 | uint32_t const mip_count = gfxCalculateMipCount(albedo_map.width, albedo_map.height); 112 | 113 | albedo_buffer = gfxCreateTexture2D(gfx, albedo_map.width, albedo_map.height, albedo_map.format, mip_count); 114 | 115 | GfxBuffer upload_buffer = gfxCreateBuffer(gfx, albedo_map.width * albedo_map.height * albedo_map.channel_count, albedo_map.data.data()); 116 | gfxCommandCopyBufferToTexture(gfx, albedo_buffer, upload_buffer); 117 | gfxCommandGenerateMips(gfx, albedo_buffer); 118 | gfxDestroyBuffer(gfx, upload_buffer); 119 | } 120 | 121 | uint32_t const material_id = (uint32_t)material_ref; 122 | if(material_id >= albedo_buffers.size()) 123 | { 124 | albedo_buffers.resize(material_id + 1); 125 | } 126 | albedo_buffers[material_id] = albedo_buffer; 127 | } 128 | 129 | GfxAccelerationStructure rt_scene = gfxCreateAccelerationStructure(gfx); 130 | std::vector rt_meshes(instance_count); 131 | 132 | for(uint32_t i = 0; i < instance_count; ++i) 133 | { 134 | rt_meshes[i] = gfxCreateRaytracingPrimitive(gfx, rt_scene); 135 | 136 | GfxConstRef instance_ref = gfxSceneGetInstanceHandle(scene, i); 137 | 138 | uint32_t const mesh_id = (uint32_t)instance_ref->mesh; 139 | gfxRaytracingPrimitiveBuild(gfx, rt_meshes[i], index_buffers[mesh_id], vertex_buffers[mesh_id]); 140 | } 141 | 142 | gfxAccelerationStructureUpdate(gfx, rt_scene); 143 | 144 | // Create our raytracing render targets 145 | GfxTexture color_buffer = gfxCreateTexture2D(gfx, DXGI_FORMAT_R16G16B16A16_FLOAT); 146 | GfxTexture accum_buffer = gfxCreateTexture2D(gfx, DXGI_FORMAT_R32G32B32A32_FLOAT); 147 | GfxTexture depth_buffer = gfxCreateTexture2D(gfx, DXGI_FORMAT_D32_FLOAT); 148 | 149 | // And our blue noise sampler 150 | GfxBuffer sobol_buffer = gfxCreateBuffer(gfx, sizeof(sobol_256spp_256d), sobol_256spp_256d); 151 | GfxBuffer ranking_buffer = gfxCreateBuffer(gfx, sizeof(rankingTile), rankingTile); 152 | GfxBuffer scrambling_buffer = gfxCreateBuffer(gfx, sizeof(scramblingTile), scramblingTile); 153 | 154 | // Compile our ambient occlusion shaders 155 | GfxDrawState trace_draw_state; 156 | gfxDrawStateSetColorTarget(trace_draw_state, 0, color_buffer.getFormat()); 157 | gfxDrawStateSetDepthStencilTarget(trace_draw_state, depth_buffer.getFormat()); 158 | 159 | GfxDrawState accumulate_draw_state; 160 | gfxDrawStateSetColorTarget(accumulate_draw_state, 0, accum_buffer.getFormat()); 161 | gfxDrawStateSetBlendMode(accumulate_draw_state, D3D12_BLEND_ONE, D3D12_BLEND_ONE, D3D12_BLEND_OP_ADD, D3D12_BLEND_ONE, D3D12_BLEND_ONE, D3D12_BLEND_OP_ADD); 162 | 163 | GfxProgram rtao_program = gfxCreateProgram(gfx, "rtao"); 164 | GfxKernel trace_kernel = gfxCreateGraphicsKernel(gfx, rtao_program, trace_draw_state, "Trace"); 165 | GfxKernel accumulate_kernel = gfxCreateGraphicsKernel(gfx, rtao_program, accumulate_draw_state, "Accumulate"); 166 | GfxKernel resolve_kernel = gfxCreateGraphicsKernel(gfx, rtao_program, "Resolve"); 167 | 168 | gfxProgramSetParameter(gfx, rtao_program, "ColorBuffer", color_buffer); 169 | gfxProgramSetParameter(gfx, rtao_program, "AccumBuffer", accum_buffer); 170 | 171 | gfxProgramSetParameter(gfx, rtao_program, "SobolBuffer", sobol_buffer); 172 | gfxProgramSetParameter(gfx, rtao_program, "RankingBuffer", ranking_buffer); 173 | gfxProgramSetParameter(gfx, rtao_program, "ScramblingBuffer", scrambling_buffer); 174 | 175 | gfxProgramSetParameter(gfx, rtao_program, "Scene", rt_scene); 176 | 177 | // Enable anisotropic texture filtering 178 | GfxSamplerState texture_sampler = gfxCreateSamplerState(gfx, D3D12_FILTER_ANISOTROPIC, D3D12_TEXTURE_ADDRESS_MODE_WRAP, D3D12_TEXTURE_ADDRESS_MODE_WRAP); 179 | 180 | gfxProgramSetParameter(gfx, rtao_program, "TextureSampler", texture_sampler); 181 | 182 | // Run application loop 183 | glm::mat4 previous_view_projection_matrix; 184 | 185 | float previous_ao_radius = 0.0f, ao_radius = 5.0f; 186 | 187 | for(uint32_t frame_index = 0; !gfxWindowIsCloseRequested(window); ++frame_index) 188 | { 189 | gfxWindowPumpEvents(window); 190 | 191 | // Show the GUI options 192 | if(ImGui::Begin("gfx - rtao", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings)) 193 | { 194 | if(gfxIsRaytracingSupported(gfx)) 195 | { 196 | ImGui::DragFloat("AO radius", &ao_radius, 1e-2f, 0.0f, 10.0f, "%.2f"); 197 | } 198 | else 199 | { 200 | ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.2f, 0.0f, 1.0f)); 201 | ImGui::Text("Raytracing isn't supported on device `%s'", gfx.getName()); 202 | ImGui::PopStyleColor(); 203 | } 204 | } 205 | ImGui::End(); 206 | 207 | // Calculate our camera matrices 208 | float const aspect_ratio = gfxGetBackBufferWidth(gfx) / (float)gfxGetBackBufferHeight(gfx); 209 | 210 | glm::mat4 view_matrix = glm::lookAt(glm::vec3(15.0f, 2.5f, 0.0f), glm::vec3(0.0f, 2.5f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f)); 211 | glm::mat4 projection_matrix = glm::perspective(0.6f, aspect_ratio, 1e-1f, 1e4f); 212 | 213 | if(ao_radius != previous_ao_radius || projection_matrix * view_matrix != previous_view_projection_matrix) 214 | { 215 | gfxCommandClearTexture(gfx, accum_buffer); 216 | 217 | previous_ao_radius = ao_radius; 218 | 219 | previous_view_projection_matrix = projection_matrix * view_matrix; 220 | } 221 | 222 | // Apply anti-aliasing jitter 223 | projection_matrix[2][0] = (2.0f * CalculateHaltonNumber(frame_index & 255, 2) - 1.0f) / gfxGetBackBufferWidth(gfx); 224 | projection_matrix[2][1] = (2.0f * CalculateHaltonNumber(frame_index & 255, 3) - 1.0f) / gfxGetBackBufferHeight(gfx); 225 | 226 | glm::mat4 view_projection_matrix = projection_matrix * view_matrix; 227 | 228 | gfxProgramSetParameter(gfx, rtao_program, "AoRadius", ao_radius); 229 | gfxProgramSetParameter(gfx, rtao_program, "FrameIndex", frame_index); 230 | 231 | gfxProgramSetParameter(gfx, rtao_program, "ViewProjectionMatrix", view_projection_matrix); 232 | 233 | // Perform our occlusion tracing 234 | gfxCommandBindColorTarget(gfx, 0, color_buffer); 235 | gfxCommandBindDepthStencilTarget(gfx, depth_buffer); 236 | gfxCommandBindKernel(gfx, trace_kernel); 237 | 238 | gfxCommandClearTexture(gfx, color_buffer); 239 | gfxCommandClearTexture(gfx, depth_buffer); 240 | 241 | for(uint32_t i = 0; i < instance_count; ++i) 242 | { 243 | GfxInstance const &instance = gfxSceneGetInstances(scene)[i]; 244 | 245 | if(instance.material) 246 | gfxProgramSetParameter(gfx, rtao_program, "AlbedoBuffer", albedo_buffers[(uint32_t)instance.material]); 247 | else 248 | gfxProgramSetParameter(gfx, rtao_program, "AlbedoBuffer", GfxTexture()); // will return black 249 | 250 | gfxCommandBindIndexBuffer(gfx, index_buffers[(uint32_t)instance.mesh]); 251 | gfxCommandBindVertexBuffer(gfx, vertex_buffers[(uint32_t)instance.mesh]); 252 | 253 | gfxCommandDrawIndexed(gfx, (uint32_t)instance.mesh->indices.size()); 254 | } 255 | 256 | // Accumulate the occlusion values 257 | gfxCommandBindColorTarget(gfx, 0, accum_buffer); 258 | gfxCommandBindKernel(gfx, accumulate_kernel); 259 | 260 | gfxCommandDraw(gfx, 3); 261 | 262 | // And resolve into the back buffer 263 | gfxCommandBindKernel(gfx, resolve_kernel); 264 | 265 | gfxCommandDraw(gfx, 3); 266 | 267 | // Finally, we submit the frame 268 | gfxImGuiRender(); 269 | gfxFrame(gfx); 270 | } 271 | 272 | // Release resources on termination 273 | gfxDestroyTexture(gfx, color_buffer); 274 | gfxDestroyTexture(gfx, accum_buffer); 275 | gfxDestroyTexture(gfx, depth_buffer); 276 | 277 | gfxDestroyBuffer(gfx, sobol_buffer); 278 | gfxDestroyBuffer(gfx, ranking_buffer); 279 | gfxDestroyBuffer(gfx, scrambling_buffer); 280 | 281 | gfxDestroyKernel(gfx, trace_kernel); 282 | gfxDestroyKernel(gfx, accumulate_kernel); 283 | gfxDestroyKernel(gfx, resolve_kernel); 284 | gfxDestroyProgram(gfx, rtao_program); 285 | 286 | gfxDestroySamplerState(gfx, texture_sampler); 287 | gfxDestroyAccelerationStructure(gfx, rt_scene); 288 | for(GfxRaytracingPrimitive &rt_mesh : rt_meshes) 289 | gfxDestroyRaytracingPrimitive(gfx, rt_mesh); 290 | 291 | for(uint32_t i = 0; i < index_buffers.size(); ++i) 292 | gfxDestroyBuffer(gfx, index_buffers.data()[i]); 293 | for(uint32_t i = 0; i < vertex_buffers.size(); ++i) 294 | gfxDestroyBuffer(gfx, vertex_buffers.data()[i]); 295 | for(uint32_t i = 0; i < albedo_buffers.size(); ++i) 296 | gfxDestroyTexture(gfx, albedo_buffers.data()[i]); 297 | 298 | gfxImGuiTerminate(); 299 | gfxDestroyScene(scene); 300 | gfxDestroyContext(gfx); 301 | gfxDestroyWindow(window); 302 | 303 | return 0; 304 | } 305 | -------------------------------------------------------------------------------- /examples/01-rtao/rtao.frag: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | MIT License 3 | 4 | Copyright (c) 2024 Guillaume Boissé 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | ****************************************************************************/ 24 | 25 | #define GOLDEN_RATIO 1.61803398874989484820f 26 | #define PI 3.14159265358979323846f 27 | 28 | float AoRadius; 29 | uint FrameIndex; 30 | 31 | Texture2D ColorBuffer; 32 | Texture2D AccumBuffer; 33 | Texture2D AlbedoBuffer; 34 | 35 | StructuredBuffer SobolBuffer; 36 | StructuredBuffer RankingBuffer; 37 | StructuredBuffer ScramblingBuffer; 38 | 39 | RaytracingAccelerationStructure Scene; 40 | 41 | SamplerState TextureSampler; 42 | 43 | struct Params 44 | { 45 | float4 position : SV_Position; 46 | float3 normal : NORMAL; 47 | float3 world : POSITION; 48 | float2 uv : TEXCOORDS; 49 | }; 50 | 51 | // https://perso.liris.cnrs.fr/david.coeurjolly/publications/heitz19.html 52 | float samplerBlueNoiseErrorDistribution_128x128_OptimizedFor_2d2d2d2d_1spp(in int pixel_i, in int pixel_j, in int sample_index, in int sample_dimension) 53 | { 54 | // wrap arguments 55 | pixel_i = (pixel_i & 127); 56 | pixel_j = (pixel_j & 127); 57 | sample_index = (sample_index & 255); 58 | sample_dimension = (sample_dimension & 255); 59 | 60 | // xor index based on optimized ranking 61 | int ranked_sample_index = sample_index ^ RankingBuffer[sample_dimension + (pixel_i + pixel_j * 128) * 8]; 62 | 63 | // fetch value in sequence 64 | int value = SobolBuffer[sample_dimension + ranked_sample_index * 256]; 65 | 66 | // If the dimension is optimized, xor sequence value based on optimized scrambling 67 | value = value ^ ScramblingBuffer[(sample_dimension % 8) + (pixel_i + pixel_j * 128) * 8]; 68 | 69 | // convert to float and return 70 | return (0.5f + value) / 256.0f; 71 | } 72 | 73 | // https://blog.demofox.org/2017/10/31/animating-noise-for-integration-over-time/ 74 | float2 BlueNoise_Sample2D(in uint2 pixel, in uint sample_index, in uint dimension_offset) 75 | { 76 | float2 s = float2(samplerBlueNoiseErrorDistribution_128x128_OptimizedFor_2d2d2d2d_1spp(pixel.x, pixel.y, 0, dimension_offset + 0), 77 | samplerBlueNoiseErrorDistribution_128x128_OptimizedFor_2d2d2d2d_1spp(pixel.x, pixel.y, 0, dimension_offset + 1)); 78 | 79 | return fmod(s + (sample_index & 255) * GOLDEN_RATIO, 1.0f); 80 | } 81 | 82 | // https://graphics.pixar.com/library/OrthonormalB/paper.pdf 83 | void GetOrthoVectors(in float3 n, out float3 b1, out float3 b2) 84 | { 85 | float sign = (n.z >= 0.0f ? 1.0f : -1.0f); 86 | float a = -1.0f / (sign + n.z); 87 | float b = n.x * n.y * a; 88 | 89 | b1 = float3(1.0f + sign * n.x * n.x * a, sign * b, -sign * n.x); 90 | b2 = float3(b, sign + n.y * n.y * a, -n.y); 91 | } 92 | 93 | float3 MapToHemisphere(in float2 s, in float3 n, in float e) 94 | { 95 | float3 u, v; 96 | GetOrthoVectors(n, u, v); 97 | 98 | float r1 = s.x; 99 | float r2 = s.y; 100 | 101 | float sinpsi = sin(2.0f * PI * r1); 102 | float cospsi = cos(2.0f * PI * r1); 103 | float costheta = pow(1.0f - r2, 1.0f / (e + 1.0f)); 104 | float sintheta = sqrt(1.0f - costheta * costheta); 105 | 106 | return normalize(u * sintheta * cospsi + v * sintheta * sinpsi + n * costheta); 107 | } 108 | 109 | float4 Trace(in Params params) : SV_Target 110 | { 111 | float2 s = BlueNoise_Sample2D(params.position.xy, FrameIndex, 0); 112 | 113 | RayDesc ray_desc; 114 | ray_desc.Direction = MapToHemisphere(s, params.normal, 1.0f); 115 | ray_desc.Origin = params.world; 116 | ray_desc.TMin = 1e-3f; 117 | ray_desc.TMax = AoRadius; 118 | 119 | RayQuery ray_query; 120 | ray_query.TraceRayInline(Scene, RAY_FLAG_NONE, 0xFFu, ray_desc); 121 | ray_query.Proceed(); 122 | 123 | float ao_value = (ray_query.CommittedStatus() == COMMITTED_TRIANGLE_HIT ? 0.0f : 1.0f); 124 | float3 albedo = AlbedoBuffer.Sample(TextureSampler, params.uv).xyz; 125 | 126 | return float4(albedo * ao_value, 1.0f); 127 | } 128 | 129 | float4 Accumulate(in float4 pos : SV_Position) : SV_Target 130 | { 131 | return ColorBuffer.Load(int3(pos.xy, 0)); 132 | } 133 | 134 | float4 Resolve(in float4 pos : SV_Position) : SV_Target 135 | { 136 | float4 color_sample = AccumBuffer.Load(int3(pos.xy, 0)); 137 | float3 color_value = color_sample.xyz / max(color_sample.w - 1.0f, 1.0f); 138 | 139 | return float4(sqrt(color_value), 1.0f); 140 | } 141 | -------------------------------------------------------------------------------- /examples/01-rtao/rtao.vert: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | MIT License 3 | 4 | Copyright (c) 2024 Guillaume Boissé 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | ****************************************************************************/ 24 | 25 | float4x4 ViewProjectionMatrix; 26 | 27 | // Need to match host-side structure 28 | struct Vertex 29 | { 30 | float4 position : POSITION; 31 | float4 normal : NORMAL; 32 | float2 uv : TEXCOORDS; 33 | }; 34 | 35 | struct Params 36 | { 37 | float4 position : SV_Position; 38 | float3 normal : NORMAL; 39 | float3 world : POSITION; 40 | float2 uv : TEXCOORDS; 41 | }; 42 | 43 | Params Trace(in Vertex vertex) 44 | { 45 | Params params; 46 | params.position = mul(ViewProjectionMatrix, vertex.position); 47 | params.normal = vertex.normal.xyz; 48 | params.world = vertex.position.xyz; 49 | params.uv = vertex.uv; 50 | return params; 51 | } 52 | 53 | float4 Accumulate(in uint idx : SV_VertexID) : SV_Position 54 | { 55 | return 1.0f - float4(4.0f * (idx & 1), 4.0f * (idx >> 1), 1.0f, 0.0f); 56 | } 57 | 58 | float4 Resolve(in uint idx : SV_VertexID) : SV_Position 59 | { 60 | return 1.0f - float4(4.0f * (idx & 1), 4.0f * (idx >> 1), 1.0f, 0.0f); 61 | } 62 | -------------------------------------------------------------------------------- /examples/02-pbr/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(02-pbr 2 | ${CMAKE_CURRENT_SOURCE_DIR}/ibl.cpp 3 | ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp) 4 | 5 | target_sources(02-pbr PRIVATE 6 | ${CMAKE_CURRENT_SOURCE_DIR}/ibl.h) 7 | 8 | target_link_libraries(02-pbr PUBLIC common) 9 | 10 | set_target_properties(02-pbr PROPERTIES FOLDER "examples") 11 | 12 | set_target_properties(02-pbr PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) 13 | 14 | file(GLOB SHADER_FILES CONFIGURE_DEPENDS 15 | ${CMAKE_CURRENT_SOURCE_DIR}/*.comp 16 | ${CMAKE_CURRENT_SOURCE_DIR}/*.frag 17 | ${CMAKE_CURRENT_SOURCE_DIR}/*.vert) 18 | 19 | target_sources(02-pbr PRIVATE ${SHADER_FILES}) 20 | 21 | source_group("Media Files\\Shaders" FILES ${SHADER_FILES}) 22 | 23 | set_source_files_properties(${SHADER_FILES} PROPERTIES VS_TOOL_OVERRIDE "None") 24 | 25 | add_custom_command(TARGET 02-pbr POST_BUILD 26 | COMMAND ${CMAKE_COMMAND} -E copy $ $ 27 | COMMAND_EXPAND_LISTS 28 | ) 29 | -------------------------------------------------------------------------------- /examples/02-pbr/data/SciFiHelmet/README.md: -------------------------------------------------------------------------------- 1 | # Sci Fi Helmet 2 | ## Screenshot 3 | 4 | ![screenshot](screenshot/screenshot.jpg) 5 | 6 | 7 | ## License Information 8 | 9 | ["Sci Fi Helmet" by Michael Pavlovic](http://quixel.se/usermanual/quixelsuite/doku.php?id=ddo_samples) 10 | 11 | [Attribution 4.0 International (CC BY 4.0)](https://creativecommons.org/licenses/by/4.0/) 12 | 13 | Converted by Norbert Nopper for glTF testing. 14 | -------------------------------------------------------------------------------- /examples/02-pbr/data/SciFiHelmet/glTF/SciFiHelmet.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gboisse/gfx/fa1d2264bc131db933f840dd6ac54bf80fe7c540/examples/02-pbr/data/SciFiHelmet/glTF/SciFiHelmet.bin -------------------------------------------------------------------------------- /examples/02-pbr/data/SciFiHelmet/glTF/SciFiHelmet.gltf: -------------------------------------------------------------------------------- 1 | { 2 | "accessors" : [ 3 | { 4 | "bufferView" : 0, 5 | "byteOffset" : 0, 6 | "componentType" : 5125, 7 | "count" : 70074, 8 | "max" : [ 9 | 70073 10 | ], 11 | "min" : [ 12 | 0 13 | ], 14 | "type" : "SCALAR" 15 | }, 16 | { 17 | "bufferView" : 1, 18 | "byteOffset" : 0, 19 | "componentType" : 5126, 20 | "count" : 70074, 21 | "max" : [ 22 | 1.1511525, 23 | 1.4587184, 24 | 1.2511277 25 | ], 26 | "min" : [ 27 | -1.1511525, 28 | -1.4587183, 29 | -1.2511287 30 | ], 31 | "type" : "VEC3" 32 | }, 33 | { 34 | "bufferView" : 2, 35 | "byteOffset" : 0, 36 | "componentType" : 5126, 37 | "count" : 70074, 38 | "max" : [ 39 | 0.99993396, 40 | 0.9998779, 41 | 0.9999757 42 | ], 43 | "min" : [ 44 | -0.99993396, 45 | -0.9999633, 46 | -0.9999864 47 | ], 48 | "type" : "VEC3" 49 | }, 50 | { 51 | "bufferView" : 3, 52 | "byteOffset" : 0, 53 | "componentType" : 5126, 54 | "count" : 70074, 55 | "max" : [ 56 | 0.9999959, 57 | 1.000000, 58 | 0.9999942, 59 | 1.000000 60 | ], 61 | "min" : [ 62 | -0.99999845, 63 | -0.99996936, 64 | -0.9999994, 65 | 1.000000 66 | ], 67 | "type" : "VEC4" 68 | }, 69 | { 70 | "bufferView" : 4, 71 | "byteOffset" : 0, 72 | "componentType" : 5126, 73 | "count" : 70074, 74 | "max" : [ 75 | 0.995369, 76 | 0.990894 77 | ], 78 | "min" : [ 79 | 0.003625, 80 | 0.0064439773 81 | ], 82 | "type" : "VEC2" 83 | } 84 | ], 85 | "asset" : { 86 | "generator" : "VKTS glTF 2.0 exporter", 87 | "version" : "2.0" 88 | }, 89 | "bufferViews" : [ 90 | { 91 | "buffer" : 0, 92 | "byteLength" : 280296, 93 | "byteOffset" : 0, 94 | "target" : 34963 95 | }, 96 | { 97 | "buffer" : 0, 98 | "byteLength" : 840888, 99 | "byteOffset" : 280296, 100 | "target" : 34962 101 | }, 102 | { 103 | "buffer" : 0, 104 | "byteLength" : 840888, 105 | "byteOffset" : 1121184, 106 | "target" : 34962 107 | }, 108 | { 109 | "buffer" : 0, 110 | "byteLength" : 1121184, 111 | "byteOffset" : 1962072, 112 | "target" : 34962 113 | }, 114 | { 115 | "buffer" : 0, 116 | "byteLength" : 560592, 117 | "byteOffset" : 3083256, 118 | "target" : 34962 119 | } 120 | ], 121 | "buffers" : [ 122 | { 123 | "byteLength" : 3643848, 124 | "uri" : "SciFiHelmet.bin" 125 | } 126 | ], 127 | "images" : [ 128 | { 129 | "uri" : "SciFiHelmet_BaseColor.png" 130 | }, 131 | { 132 | "uri" : "SciFiHelmet_MetallicRoughness.png" 133 | }, 134 | { 135 | "uri" : "SciFiHelmet_Normal.png" 136 | }, 137 | { 138 | "uri" : "SciFiHelmet_AmbientOcclusion.png" 139 | } 140 | ], 141 | "materials" : [ 142 | { 143 | "name" : "SciFiHelmet", 144 | "normalTexture" : { 145 | "index" : 2 146 | }, 147 | "occlusionTexture" : { 148 | "index" : 3 149 | }, 150 | "pbrMetallicRoughness" : { 151 | "baseColorTexture" : { 152 | "index" : 0 153 | }, 154 | "metallicRoughnessTexture" : { 155 | "index" : 1 156 | } 157 | } 158 | } 159 | ], 160 | "meshes" : [ 161 | { 162 | "name" : "SciFiHelmet", 163 | "primitives" : [ 164 | { 165 | "attributes" : { 166 | "NORMAL" : 2, 167 | "POSITION" : 1, 168 | "TANGENT" : 3, 169 | "TEXCOORD_0" : 4 170 | }, 171 | "indices" : 0, 172 | "material" : 0, 173 | "mode" : 4 174 | } 175 | ] 176 | } 177 | ], 178 | "nodes" : [ 179 | { 180 | "name" : "Camera", 181 | "rotation" : [ 182 | 0.483536, 183 | 0.336872, 184 | -0.208704, 185 | 0.780483 186 | ], 187 | "translation" : [ 188 | 7.481132, 189 | 5.343665, 190 | 6.507640 191 | ] 192 | }, 193 | { 194 | "mesh" : 0, 195 | "name" : "SciFiHelmet" 196 | } 197 | ], 198 | "samplers" : [ 199 | {} 200 | ], 201 | "scene" : 0, 202 | "scenes" : [ 203 | { 204 | "nodes" : [ 205 | 0, 206 | 1 207 | ] 208 | } 209 | ], 210 | "textures" : [ 211 | { 212 | "sampler" : 0, 213 | "source" : 0 214 | }, 215 | { 216 | "sampler" : 0, 217 | "source" : 1 218 | }, 219 | { 220 | "sampler" : 0, 221 | "source" : 2 222 | }, 223 | { 224 | "sampler" : 0, 225 | "source" : 3 226 | } 227 | ] 228 | } 229 | -------------------------------------------------------------------------------- /examples/02-pbr/data/SciFiHelmet/glTF/SciFiHelmet_AmbientOcclusion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gboisse/gfx/fa1d2264bc131db933f840dd6ac54bf80fe7c540/examples/02-pbr/data/SciFiHelmet/glTF/SciFiHelmet_AmbientOcclusion.png -------------------------------------------------------------------------------- /examples/02-pbr/data/SciFiHelmet/glTF/SciFiHelmet_BaseColor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gboisse/gfx/fa1d2264bc131db933f840dd6ac54bf80fe7c540/examples/02-pbr/data/SciFiHelmet/glTF/SciFiHelmet_BaseColor.png -------------------------------------------------------------------------------- /examples/02-pbr/data/SciFiHelmet/glTF/SciFiHelmet_MetallicRoughness.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gboisse/gfx/fa1d2264bc131db933f840dd6ac54bf80fe7c540/examples/02-pbr/data/SciFiHelmet/glTF/SciFiHelmet_MetallicRoughness.png -------------------------------------------------------------------------------- /examples/02-pbr/data/SciFiHelmet/glTF/SciFiHelmet_Normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gboisse/gfx/fa1d2264bc131db933f840dd6ac54bf80fe7c540/examples/02-pbr/data/SciFiHelmet/glTF/SciFiHelmet_Normal.png -------------------------------------------------------------------------------- /examples/02-pbr/data/SciFiHelmet/screenshot/screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gboisse/gfx/fa1d2264bc131db933f840dd6ac54bf80fe7c540/examples/02-pbr/data/SciFiHelmet/screenshot/screenshot.jpg -------------------------------------------------------------------------------- /examples/02-pbr/data/kiara_1_dawn_4k.hdr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gboisse/gfx/fa1d2264bc131db933f840dd6ac54bf80fe7c540/examples/02-pbr/data/kiara_1_dawn_4k.hdr -------------------------------------------------------------------------------- /examples/02-pbr/ibl.comp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | MIT License 3 | 4 | Copyright (c) 2024 Guillaume Boissé 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | ****************************************************************************/ 24 | #define PI 3.141592653589793238463f 25 | 26 | uint g_MipLevel; 27 | uint g_FaceIndex; 28 | float4x4 g_ViewProjectionInverse; 29 | 30 | TextureCube g_Cubemap; 31 | Texture2D g_EnvironmentMap; 32 | 33 | RWTexture2DArray g_InCubemap; 34 | RWTexture2DArray g_OutCubemap; 35 | RWTexture2D g_BrdfBuffer; 36 | RWTexture2DArray g_IrradianceBuffer; 37 | RWTexture2DArray g_EnvironmentBuffer; 38 | 39 | SamplerState g_LinearSampler; 40 | 41 | // https://en.wikipedia.org/wiki/Equirectangular_projection 42 | float2 SampleSphericalMap(in float3 rd) 43 | { 44 | return float2(atan2(rd.z, rd.x) / (2.0f * PI) + 0.5f, 1.0f - acos(rd.y) / PI); 45 | } 46 | 47 | [numthreads(8, 8, 1)] 48 | void DrawCubemap(in uint2 did : SV_DispatchThreadID) 49 | { 50 | uint3 dimensions; 51 | g_OutCubemap.GetDimensions(dimensions.x, dimensions.y, dimensions.z); 52 | 53 | float2 uv = (did + 0.5f) / dimensions.xy; 54 | float2 ndc = 2.0f * uv - 1.0f; 55 | 56 | float4 world = mul(g_ViewProjectionInverse, float4(ndc, 1.0f, 1.0f)); 57 | world /= world.w; // perspective divide 58 | 59 | uv = SampleSphericalMap(normalize(world.xyz)); 60 | 61 | float3 color = g_EnvironmentMap.SampleLevel(g_LinearSampler, uv, 0.0f).xyz; 62 | 63 | g_OutCubemap[int3(did, g_FaceIndex)] = float4(color, 1.0f); 64 | } 65 | 66 | [numthreads(8, 8, 1)] 67 | void BlurCubemap(in uint3 did : SV_DispatchThreadID) 68 | { 69 | uint3 dimensions; 70 | g_InCubemap.GetDimensions(dimensions.x, dimensions.y, dimensions.z); 71 | 72 | if(any(did.xy >= max(dimensions.xy >> 1, 1))) 73 | { 74 | return; // out of bounds 75 | } 76 | 77 | float4 result = float4(0.0f, 0.0f, 0.0f, 0.0f); 78 | float sample_count = 0.0f; 79 | 80 | for(uint y = 0; y < 2; ++y) 81 | { 82 | for(uint x = 0; x < 2; ++x) 83 | { 84 | uint2 pix = (did.xy << 1) + uint2(x, y); 85 | 86 | if(any(pix >= dimensions.xy)) 87 | { 88 | break; // out of bounds 89 | } 90 | 91 | result += g_InCubemap[int3(pix, did.z)]; 92 | 93 | sample_count += 1.0f; 94 | } 95 | } 96 | 97 | g_OutCubemap[did] = result / sample_count; 98 | } 99 | 100 | //! 101 | //! For more info, see: https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf 102 | //! 103 | 104 | [numthreads(8, 8, 1)] 105 | void DrawIrradiance(in uint2 did : SV_DispatchThreadID) 106 | { 107 | uint3 dimensions; 108 | g_IrradianceBuffer.GetDimensions(dimensions.x, dimensions.y, dimensions.z); 109 | 110 | float2 uv = (did + 0.5f) / dimensions.xy; 111 | float2 ndc = 2.0f * uv - 1.0f; 112 | 113 | float4 world = mul(g_ViewProjectionInverse, float4(ndc, 1.0f, 1.0f)); 114 | world /= world.w; // perspective divide 115 | 116 | float3 forward = normalize(world.xyz); 117 | float3 up = float3(0.0f, 1.0f, 0.0f); 118 | float3 right = cross(up, forward); 119 | up = cross(forward, right); 120 | 121 | float sample_count = 0.0f; 122 | float sample_delta = 0.025f; 123 | 124 | float3 irradiance = float3(0.0f, 0.0f, 0.0f); 125 | 126 | for(float phi = 0.0f; phi < 2.0f * PI; phi += sample_delta) 127 | { 128 | for(float theta = 0.0f; theta < 0.5f * PI; theta += sample_delta) 129 | { 130 | // Spherical to cartesian (in tangent space) 131 | float3 tangent_sample = float3(sin(theta) * cos(phi), sin(theta) * sin(phi), cos(theta)); 132 | // Tangent space to world 133 | float3 sample_vector = tangent_sample.x * right + tangent_sample.y * up + tangent_sample.z * forward; 134 | // Accumulate irradiance contribution 135 | irradiance += g_Cubemap.SampleLevel(g_LinearSampler, sample_vector, 0.0f).xyz * cos(theta) * sin(theta); 136 | // Increment sample count 137 | sample_count += 1.0f; 138 | } 139 | } 140 | 141 | g_IrradianceBuffer[int3(did, g_FaceIndex)] = float4(PI * irradiance / sample_count, 1.0f); 142 | } 143 | 144 | float VanDerCorput(in uint bits) 145 | { 146 | bits = (bits << 16u) | (bits >> 16u); 147 | bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); 148 | bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); 149 | bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); 150 | bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); 151 | return float(double(bits) * 2.3283064365386963e-10); // / 0x100000000 152 | } 153 | 154 | float2 Hammersley(in uint i, in uint N) 155 | { 156 | return float2(i / float(N), VanDerCorput(i)); 157 | } 158 | 159 | float3 ImportanceSampleGGX(in float2 Xi, in float3 N, in float roughness) 160 | { 161 | float a = roughness * roughness; 162 | 163 | float phi = 2.0f * PI * Xi.x; 164 | float cos_theta = sqrt((1.0f - Xi.y) / (1.0f + (a * a - 1.0f) * Xi.y)); 165 | float sin_theta = sqrt(1.0f - cos_theta * cos_theta); 166 | 167 | // From spherical coordinates to cartesian coordinates 168 | float3 H; 169 | H.x = cos(phi) * sin_theta; 170 | H.y = sin(phi) * sin_theta; 171 | H.z = cos_theta; 172 | 173 | // From tangent-space vector to world-space sample vector 174 | float3 up = (abs(N.z) < 0.999f ? float3(0.0f, 0.0f, 1.0f) : float3(1.0f, 0.0f, 0.0f)); 175 | float3 tangent = normalize(cross(up, N)); 176 | float3 bitangent = cross(N, tangent); 177 | 178 | return normalize(tangent * H.x + bitangent * H.y + N * H.z); 179 | } 180 | 181 | float DistributionGGX(in float n_dot_h, in float roughness) 182 | { 183 | float a = roughness * roughness; 184 | float a_squared = a * a; 185 | float n_dot_h_squared = n_dot_h * n_dot_h; 186 | 187 | float denom = (n_dot_h_squared * (a_squared - 1.0f) + 1.0f); 188 | 189 | denom = PI * denom * denom; 190 | 191 | return a_squared / denom; 192 | } 193 | 194 | [numthreads(8, 8, 1)] 195 | void DrawEnvironment(in uint2 did : SV_DispatchThreadID) 196 | { 197 | uint3 dimensions; 198 | g_EnvironmentBuffer.GetDimensions(dimensions.x, dimensions.y, dimensions.z); 199 | 200 | float2 uv = (did + 0.5f) / dimensions.xy; 201 | float2 ndc = 2.0f * uv - 1.0f; 202 | 203 | float4 world = mul(g_ViewProjectionInverse, float4(ndc, 1.0f, 1.0f)); 204 | world /= world.w; // perspective divide 205 | 206 | // In split-sum approximation, reflection and view vector are the same 207 | float3 N = normalize(world.xyz); 208 | float3 R = N; 209 | float3 V = R; 210 | 211 | float total_weight = 0.0f; 212 | float3 environment = float3(0.0f, 0.0f, 0.0f); 213 | 214 | const uint sample_count = 4096; 215 | const float roughness = g_MipLevel / 4.0f; 216 | 217 | for(uint i = 0; i < sample_count; ++i) 218 | { 219 | float2 Xi = Hammersley(i, sample_count); 220 | float3 H = ImportanceSampleGGX(Xi, N, roughness); 221 | float3 L = normalize(2.0f * dot(V, H) * H - V); 222 | 223 | float n_dot_l = max(dot(N, L), 0.0f); 224 | 225 | if(n_dot_l > 0.0f) 226 | { 227 | // Sample from the environment's mip level based on roughness/pdf 228 | float nDotH = max(dot(N, H), 0.0f); 229 | float hDotV = max(dot(H, V), 0.0f); 230 | float D = DistributionGGX(nDotH, roughness); 231 | float pdf = D * nDotH / (4.0f * hDotV) + 0.0001f; 232 | 233 | // Compute mip level to be sampled 234 | float sa_texel = 4.0f * PI / (6.0f * dimensions.x * dimensions.y); 235 | float sa_sample = 1.0f / (sample_count * pdf + 0.0001f); 236 | float mip_level = (roughness == 0.0f ? 0.0f : 0.5f * log2(sa_sample / sa_texel)); 237 | 238 | // Add contribution to convolution 239 | environment += g_Cubemap.SampleLevel(g_LinearSampler, L, mip_level).xyz * n_dot_l; 240 | total_weight += n_dot_l; 241 | } 242 | } 243 | 244 | g_EnvironmentBuffer[int3(did, g_FaceIndex)] = float4(environment / total_weight, 1.0f); 245 | } 246 | 247 | float GeometrySchlickGGX(in float n_dot_v, in float roughness) 248 | { 249 | float a = roughness * roughness; 250 | float k = a / 2.0f; 251 | 252 | float denom = n_dot_v * (1.0f - k) + k; 253 | 254 | return n_dot_v / denom; 255 | } 256 | 257 | float GeometrySmith(in float n_dot_v, in float n_dot_l, in float roughness) 258 | { 259 | float ggx2 = GeometrySchlickGGX(n_dot_v, roughness); 260 | float ggx1 = GeometrySchlickGGX(n_dot_l, roughness); 261 | 262 | return ggx1 * ggx2; 263 | } 264 | 265 | [numthreads(8, 8, 1)] 266 | void DrawBRDF(in uint2 did : SV_DispatchThreadID) 267 | { 268 | uint2 dimensions; 269 | g_BrdfBuffer.GetDimensions(dimensions.x, dimensions.y); 270 | 271 | float2 uv = (did + 0.5f) / dimensions; 272 | 273 | float n_dot_v = uv.x; 274 | float roughness = uv.y; 275 | 276 | float3 N = float3(0.0f, 0.0f, 1.0f); 277 | float3 V = float3(sqrt(1.0f - n_dot_v * n_dot_v), 0.0f, n_dot_v); 278 | 279 | float A = 0.0f; 280 | float B = 0.0f; 281 | 282 | const uint sample_count = 1024; 283 | 284 | for(uint i = 0; i < sample_count; ++i) 285 | { 286 | float2 Xi = Hammersley(i, sample_count); 287 | float3 H = ImportanceSampleGGX(Xi, N, roughness); 288 | float3 L = normalize(2.0f * dot(V, H) * H - V); 289 | 290 | float n_dot_l = max(L.z, 0.0f); 291 | float n_dot_h = max(H.z, 0.0f); 292 | float v_dot_h = max(dot(V, H), 0.0f); 293 | 294 | if(n_dot_l > 0.0f) 295 | { 296 | float G = GeometrySmith(n_dot_v, n_dot_l, roughness); 297 | float G_Vis = (G * v_dot_h) / (n_dot_h * n_dot_v); 298 | float Fc = pow(1.0f - v_dot_h, 5.0f); 299 | 300 | A += (1.0f - Fc) * G_Vis; 301 | B += Fc * G_Vis; 302 | } 303 | } 304 | 305 | g_BrdfBuffer[did] = float2(A, B) / sample_count; 306 | } 307 | -------------------------------------------------------------------------------- /examples/02-pbr/ibl.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | MIT License 3 | 4 | Copyright (c) 2024 Guillaume Boissé 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | ****************************************************************************/ 24 | #include "ibl.h" 25 | 26 | #define _USE_MATH_DEFINES 27 | #include "math.h" 28 | #include "glm/gtc/matrix_transform.hpp" 29 | 30 | namespace 31 | { 32 | 33 | glm::vec3 const forward_vectors[] = 34 | { 35 | glm::vec3(-1.0f, 0.0f, 0.0f), 36 | glm::vec3( 1.0f, 0.0f, 0.0f), 37 | glm::vec3( 0.0f, 1.0f, 0.0f), 38 | glm::vec3( 0.0f, -1.0f, 0.0f), 39 | glm::vec3( 0.0f, 0.0f, -1.0f), 40 | glm::vec3( 0.0f, 0.0f, 1.0f) 41 | }; 42 | 43 | glm::vec3 const up_vectors[] = 44 | { 45 | glm::vec3( 0.0f, -1.0f, 0.0f), 46 | glm::vec3( 0.0f, -1.0f, 0.0f), 47 | glm::vec3( 0.0f, 0.0f, -1.0f), 48 | glm::vec3( 0.0f, 0.0f, 1.0f), 49 | glm::vec3( 0.0f, -1.0f, 0.0f), 50 | glm::vec3( 0.0f, -1.0f, 0.0f) 51 | }; 52 | 53 | } //! unnamed namespace 54 | 55 | IBL ConvolveIBL(GfxContext gfx, GfxTexture environment_map) 56 | { 57 | IBL ibl = {}; 58 | 59 | if(environment_map) 60 | { 61 | uint32_t const cubemap_size = 512; 62 | uint32_t const mip_level_count = gfxCalculateMipCount(cubemap_size); 63 | 64 | GfxTexture cubemap = gfxCreateTextureCube(gfx, cubemap_size, DXGI_FORMAT_R16G16B16A16_FLOAT, mip_level_count); 65 | GfxProgram ibl_program = gfxCreateProgram(gfx, "ibl"); 66 | GfxSamplerState linear_sampler = gfxCreateSamplerState(gfx, D3D12_FILTER_MIN_MAG_MIP_LINEAR); 67 | 68 | ibl.brdf_buffer = gfxCreateTexture2D(gfx, 512, 512, DXGI_FORMAT_R16G16_FLOAT); 69 | ibl.irradiance_buffer = gfxCreateTextureCube(gfx, 32, DXGI_FORMAT_R16G16B16A16_FLOAT); 70 | ibl.environment_buffer = gfxCreateTextureCube(gfx, 128, DXGI_FORMAT_R16G16B16A16_FLOAT, 5); 71 | 72 | gfxProgramSetParameter(gfx, ibl_program, "g_Cubemap", cubemap); 73 | gfxProgramSetParameter(gfx, ibl_program, "g_EnvironmentMap", environment_map); 74 | 75 | gfxProgramSetParameter(gfx, ibl_program, "g_LinearSampler", linear_sampler); 76 | 77 | // Draw the environment buffer into a cubemap 78 | GfxKernel draw_cubemap_kernel = gfxCreateComputeKernel(gfx, ibl_program, "DrawCubemap"); 79 | { 80 | gfxCommandBindKernel(gfx, draw_cubemap_kernel); 81 | 82 | uint32_t const *num_threads = gfxKernelGetNumThreads(gfx, draw_cubemap_kernel); 83 | uint32_t const num_groups_x = (cubemap_size + num_threads[0] - 1) / num_threads[0]; 84 | uint32_t const num_groups_y = (cubemap_size + num_threads[1] - 1) / num_threads[1]; 85 | 86 | for(uint32_t face_index = 0; face_index < 6; ++face_index) 87 | { 88 | glm::mat4 const view = glm::lookAt(glm::vec3(0.0f), forward_vectors[face_index], up_vectors[face_index]); 89 | glm::mat4 const proj = glm::perspective((float)M_PI / 2.0f, 1.0f, 0.1f, 1e4f); 90 | glm::mat4 const view_proj_inv = glm::inverse(proj * view); 91 | 92 | gfxProgramSetParameter(gfx, ibl_program, "g_FaceIndex", face_index); 93 | gfxProgramSetParameter(gfx, ibl_program, "g_ViewProjectionInverse", view_proj_inv); 94 | 95 | gfxProgramSetParameter(gfx, ibl_program, "g_OutCubemap", cubemap); 96 | 97 | gfxCommandDispatch(gfx, num_groups_x, num_groups_y, 1); 98 | } 99 | 100 | gfxDestroyKernel(gfx, draw_cubemap_kernel); 101 | } 102 | 103 | // Blur the cubemap to generate its mip levels 104 | GfxKernel blur_cubemap_kernel = gfxCreateComputeKernel(gfx, ibl_program, "BlurCubemap"); 105 | { 106 | gfxCommandBindKernel(gfx, blur_cubemap_kernel); 107 | 108 | uint32_t const *num_threads = gfxKernelGetNumThreads(gfx, blur_cubemap_kernel); 109 | uint32_t mip_level_size = GFX_MAX(cubemap_size >> 1, 1u); 110 | 111 | for(uint32_t mip_level = 1; mip_level < mip_level_count; ++mip_level) 112 | { 113 | uint32_t const num_groups_x = (mip_level_size + num_threads[0] - 1) / num_threads[0]; 114 | uint32_t const num_groups_y = (mip_level_size + num_threads[1] - 1) / num_threads[1]; 115 | 116 | gfxProgramSetParameter(gfx, ibl_program, "g_InCubemap", cubemap, mip_level - 1); 117 | gfxProgramSetParameter(gfx, ibl_program, "g_OutCubemap", cubemap, mip_level); 118 | 119 | gfxCommandDispatch(gfx, num_groups_x, num_groups_y, 6); 120 | 121 | mip_level_size = GFX_MAX(mip_level_size >> 1, 1u); 122 | } 123 | 124 | gfxDestroyKernel(gfx, blur_cubemap_kernel); 125 | } 126 | 127 | // Convolve the cubemap to pre-calculate irradiance 128 | GfxKernel draw_irradiance_kernel = gfxCreateComputeKernel(gfx, ibl_program, "DrawIrradiance"); 129 | { 130 | gfxCommandBindKernel(gfx, draw_irradiance_kernel); 131 | 132 | uint32_t const *num_threads = gfxKernelGetNumThreads(gfx, draw_irradiance_kernel); 133 | uint32_t const num_groups_x = (ibl.irradiance_buffer.getWidth() + num_threads[0] - 1) / num_threads[0]; 134 | uint32_t const num_groups_y = (ibl.irradiance_buffer.getHeight() + num_threads[1] - 1) / num_threads[1]; 135 | 136 | for(uint32_t face_index = 0; face_index < 6; ++face_index) 137 | { 138 | glm::mat4 const view = glm::lookAt(glm::vec3(0.0f), forward_vectors[face_index], up_vectors[face_index]); 139 | glm::mat4 const proj = glm::perspective((float)M_PI / 2.0f, 1.0f, 0.1f, 1e4f); 140 | glm::mat4 const view_proj_inv = glm::inverse(proj * view); 141 | 142 | gfxProgramSetParameter(gfx, ibl_program, "g_FaceIndex", face_index); 143 | gfxProgramSetParameter(gfx, ibl_program, "g_ViewProjectionInverse", view_proj_inv); 144 | 145 | gfxProgramSetParameter(gfx, ibl_program, "g_IrradianceBuffer", ibl.irradiance_buffer); 146 | 147 | gfxCommandDispatch(gfx, num_groups_x, num_groups_y, 1); 148 | } 149 | 150 | gfxDestroyKernel(gfx, draw_irradiance_kernel); 151 | } 152 | 153 | // Convolve the cubemap to pre-filter the environment lighting for reflections 154 | GfxKernel draw_environment_kernel = gfxCreateComputeKernel(gfx, ibl_program, "DrawEnvironment"); 155 | { 156 | gfxCommandBindKernel(gfx, draw_environment_kernel); 157 | 158 | uint32_t const *num_threads = gfxKernelGetNumThreads(gfx, draw_environment_kernel); 159 | uint32_t mip_level_width = ibl.environment_buffer.getWidth(); 160 | uint32_t mip_level_height = ibl.environment_buffer.getHeight(); 161 | 162 | for(uint32_t mip_level = 0; mip_level < ibl.environment_buffer.getMipLevels(); ++mip_level) 163 | { 164 | uint32_t const num_groups_x = (mip_level_width + num_threads[0] - 1) / num_threads[0]; 165 | uint32_t const num_groups_y = (mip_level_height + num_threads[1] - 1) / num_threads[1]; 166 | 167 | for(uint32_t face_index = 0; face_index < 6; ++face_index) 168 | { 169 | glm::mat4 const view = glm::lookAt(glm::vec3(0.0f), forward_vectors[face_index], up_vectors[face_index]); 170 | glm::mat4 const proj = glm::perspective((float)M_PI / 2.0f, 1.0f, 0.1f, 1e4f); 171 | glm::mat4 const view_proj_inv = glm::inverse(proj * view); 172 | 173 | gfxProgramSetParameter(gfx, ibl_program, "g_MipLevel", mip_level); 174 | gfxProgramSetParameter(gfx, ibl_program, "g_FaceIndex", face_index); 175 | gfxProgramSetParameter(gfx, ibl_program, "g_ViewProjectionInverse", view_proj_inv); 176 | 177 | gfxProgramSetParameter(gfx, ibl_program, "g_EnvironmentBuffer", ibl.environment_buffer, mip_level); 178 | 179 | gfxCommandDispatch(gfx, num_groups_x, num_groups_y, 1); 180 | } 181 | 182 | mip_level_width = GFX_MAX(mip_level_width >> 1, 1u); 183 | mip_level_height = GFX_MAX(mip_level_height >> 1, 1u); 184 | } 185 | 186 | gfxDestroyKernel(gfx, draw_environment_kernel); 187 | } 188 | 189 | // Finally, we pre-calculate the BRDF for various roughness and incident directions values 190 | GfxKernel draw_brdf_kernel = gfxCreateComputeKernel(gfx, ibl_program, "DrawBRDF"); 191 | { 192 | uint32_t const *num_threads = gfxKernelGetNumThreads(gfx, draw_brdf_kernel); 193 | uint32_t const num_groups_x = (ibl.brdf_buffer.getWidth() + num_threads[0] - 1) / num_threads[0]; 194 | uint32_t const num_groups_y = (ibl.brdf_buffer.getHeight() + num_threads[1] - 1) / num_threads[1]; 195 | 196 | gfxProgramSetParameter(gfx, ibl_program, "g_BrdfBuffer", ibl.brdf_buffer); 197 | 198 | gfxCommandBindKernel(gfx, draw_brdf_kernel); 199 | gfxCommandDispatch(gfx, num_groups_x, num_groups_y, 1); 200 | 201 | gfxDestroyKernel(gfx, draw_brdf_kernel); 202 | } 203 | 204 | gfxDestroyTexture(gfx, cubemap); 205 | gfxDestroyProgram(gfx, ibl_program); 206 | gfxDestroySamplerState(gfx, linear_sampler); 207 | } 208 | 209 | return ibl; 210 | } 211 | 212 | void ReleaseIBL(GfxContext gfx, IBL const &ibl) 213 | { 214 | gfxDestroyTexture(gfx, ibl.brdf_buffer); 215 | gfxDestroyTexture(gfx, ibl.irradiance_buffer); 216 | gfxDestroyTexture(gfx, ibl.environment_buffer); 217 | } 218 | -------------------------------------------------------------------------------- /examples/02-pbr/ibl.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | MIT License 3 | 4 | Copyright (c) 2024 Guillaume Boissé 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | ****************************************************************************/ 24 | #pragma once 25 | 26 | #include "gfx.h" 27 | 28 | struct IBL 29 | { 30 | GfxTexture brdf_buffer; 31 | GfxTexture irradiance_buffer; 32 | GfxTexture environment_buffer; 33 | }; 34 | 35 | IBL ConvolveIBL(GfxContext gfx, GfxTexture environment_map); 36 | void ReleaseIBL(GfxContext gfx, IBL const &ibl); 37 | -------------------------------------------------------------------------------- /examples/02-pbr/main.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | MIT License 3 | 4 | Copyright (c) 2024 Guillaume Boissé 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | ****************************************************************************/ 24 | #include "gfx_imgui.h" 25 | 26 | #include "ibl.h" 27 | #include "gpu_scene.h" 28 | #include "fly_camera.h" 29 | #include "glm/gtc/matrix_transform.hpp" 30 | 31 | namespace 32 | { 33 | 34 | char const *environment_map_path = "data/kiara_1_dawn_4k.hdr"; 35 | char const *scene_path = "data/SciFiHelmet/glTF/SciFiHelmet.gltf"; 36 | 37 | } //! unnamed namespace 38 | 39 | int32_t main() 40 | { 41 | GfxWindow window = gfxCreateWindow(1280, 720, "gfx - PBR"); 42 | GfxContext gfx = gfxCreateContext(window); 43 | GfxScene scene = gfxCreateScene(); 44 | gfxImGuiInitialize(gfx); 45 | 46 | // Import the scene data 47 | gfxSceneImport(scene, scene_path); 48 | gfxSceneImport(scene, environment_map_path); 49 | GpuScene gpu_scene = UploadSceneToGpuMemory(gfx, scene); 50 | 51 | // Process the environment for image-based lighting (i.e., IBL) 52 | GfxConstRef environment_image = gfxSceneFindObjectByAssetFile(scene, environment_map_path); 53 | 54 | GfxTexture environment_map = (environment_image ? gpu_scene.textures[(uint32_t)environment_image] : GfxTexture()); 55 | 56 | IBL const ibl = ConvolveIBL(gfx, environment_map); 57 | 58 | // Create our color (i.e., HDR) and depth buffers 59 | GfxTexture color_buffer = gfxCreateTexture2D(gfx, DXGI_FORMAT_R16G16B16A16_FLOAT); 60 | GfxTexture history_buffer = gfxCreateTexture2D(gfx, DXGI_FORMAT_R16G16B16A16_FLOAT); 61 | GfxTexture resolve_buffer = gfxCreateTexture2D(gfx, DXGI_FORMAT_R16G16B16A16_FLOAT); 62 | GfxTexture velocity_buffer = gfxCreateTexture2D(gfx, DXGI_FORMAT_R16G16_FLOAT); 63 | GfxTexture depth_buffer = gfxCreateTexture2D(gfx, DXGI_FORMAT_D32_FLOAT); 64 | 65 | // Create our PBR programs and kernels 66 | GfxProgram pbr_program = gfxCreateProgram(gfx, "pbr"); 67 | GfxProgram sky_program = gfxCreateProgram(gfx, "sky"); 68 | GfxProgram taa_program = gfxCreateProgram(gfx, "taa"); 69 | 70 | GfxDrawState pbr_draw_state; 71 | gfxDrawStateSetColorTarget(pbr_draw_state, 0, color_buffer.getFormat()); 72 | gfxDrawStateSetColorTarget(pbr_draw_state, 1, velocity_buffer.getFormat()); 73 | gfxDrawStateSetDepthStencilTarget(pbr_draw_state, depth_buffer.getFormat()); 74 | 75 | GfxKernel pbr_kernel = gfxCreateGraphicsKernel(gfx, pbr_program, pbr_draw_state); 76 | 77 | GfxDrawState sky_draw_state; 78 | gfxDrawStateSetColorTarget(sky_draw_state, 0, color_buffer.getFormat()); 79 | gfxDrawStateSetDepthStencilTarget(sky_draw_state, depth_buffer.getFormat()); 80 | 81 | GfxKernel sky_kernel = gfxCreateGraphicsKernel(gfx, sky_program, sky_draw_state); 82 | 83 | GfxDrawState reproject_draw_state; 84 | gfxDrawStateSetColorTarget(reproject_draw_state, 0, resolve_buffer.getFormat()); 85 | 86 | GfxKernel reproject_kernel = gfxCreateGraphicsKernel(gfx, taa_program, reproject_draw_state, "Reproject"); 87 | GfxKernel resolve_kernel = gfxCreateGraphicsKernel(gfx, taa_program, "Resolve"); 88 | 89 | // Create our sampler states 90 | GfxSamplerState linear_sampler = gfxCreateSamplerState(gfx, D3D12_FILTER_MIN_MAG_MIP_LINEAR); 91 | GfxSamplerState nearest_sampler = gfxCreateSamplerState(gfx, D3D12_FILTER_MIN_MAG_MIP_POINT); 92 | 93 | gfxProgramSetParameter(gfx, pbr_program, "g_LinearSampler", linear_sampler); 94 | gfxProgramSetParameter(gfx, sky_program, "g_LinearSampler", linear_sampler); 95 | gfxProgramSetParameter(gfx, taa_program, "g_LinearSampler", linear_sampler); 96 | gfxProgramSetParameter(gfx, taa_program, "g_NearestSampler", nearest_sampler); 97 | 98 | // Bind a bunch of shader parameters 99 | BindGpuScene(gfx, pbr_program, gpu_scene); 100 | 101 | gfxProgramSetParameter(gfx, pbr_program, "g_BrdfBuffer", ibl.brdf_buffer); 102 | gfxProgramSetParameter(gfx, pbr_program, "g_IrradianceBuffer", ibl.irradiance_buffer); 103 | gfxProgramSetParameter(gfx, pbr_program, "g_EnvironmentBuffer", ibl.environment_buffer); 104 | 105 | gfxProgramSetParameter(gfx, sky_program, "g_EnvironmentBuffer", ibl.environment_buffer); 106 | 107 | gfxProgramSetParameter(gfx, taa_program, "g_ColorBuffer", color_buffer); 108 | gfxProgramSetParameter(gfx, taa_program, "g_HistoryBuffer", history_buffer); 109 | gfxProgramSetParameter(gfx, taa_program, "g_ResolveBuffer", resolve_buffer); 110 | gfxProgramSetParameter(gfx, taa_program, "g_VelocityBuffer", velocity_buffer); 111 | gfxProgramSetParameter(gfx, taa_program, "g_DepthBuffer", depth_buffer); 112 | 113 | // Run the application loop 114 | FlyCamera fly_camera = CreateFlyCamera(gfx, glm::vec3(0.0f, 0.0f, 5.0f), glm::vec3(0.0f, 0.0f, 0.0f)); 115 | 116 | while(!gfxWindowIsCloseRequested(window)) 117 | { 118 | gfxWindowPumpEvents(window); 119 | 120 | // Update our GPU scene and camera 121 | UpdateGpuScene(gfx, scene, gpu_scene); 122 | UpdateFlyCamera(gfx, window, fly_camera); 123 | 124 | gfxProgramSetParameter(gfx, pbr_program, "g_Eye", fly_camera.eye); 125 | gfxProgramSetParameter(gfx, sky_program, "g_Eye", fly_camera.eye); 126 | 127 | gfxProgramSetParameter(gfx, sky_program, "g_ViewProjectionInverse", glm::inverse(fly_camera.view_proj)); 128 | 129 | // Update texel size (can change if window is resized) 130 | float const texel_size[] = 131 | { 132 | 1.0f / gfxGetBackBufferWidth(gfx), 133 | 1.0f / gfxGetBackBufferHeight(gfx) 134 | }; 135 | 136 | gfxProgramSetParameter(gfx, sky_program, "g_TexelSize", texel_size); 137 | gfxProgramSetParameter(gfx, taa_program, "g_TexelSize", texel_size); 138 | 139 | // Clear our render targets 140 | gfxCommandClearTexture(gfx, color_buffer); 141 | gfxCommandClearTexture(gfx, velocity_buffer); 142 | gfxCommandClearTexture(gfx, depth_buffer); 143 | 144 | // Draw all the meshes in the scene 145 | uint32_t const instance_count = gfxSceneGetInstanceCount(scene); 146 | 147 | gfxCommandBindColorTarget(gfx, 0, color_buffer); 148 | gfxCommandBindColorTarget(gfx, 1, velocity_buffer); 149 | gfxCommandBindDepthStencilTarget(gfx, depth_buffer); 150 | gfxCommandBindKernel(gfx, pbr_kernel); 151 | gfxCommandBindIndexBuffer(gfx, gpu_scene.index_buffer); 152 | gfxCommandBindVertexBuffer(gfx, gpu_scene.vertex_buffer); 153 | 154 | for(uint32_t i = 0; i < instance_count; ++i) 155 | { 156 | GfxConstRef const instance_ref = gfxSceneGetInstanceHandle(scene, i); 157 | 158 | uint32_t const instance_id = (uint32_t)instance_ref; 159 | uint32_t const mesh_id = (uint32_t)instance_ref->mesh; 160 | 161 | Mesh const mesh = gpu_scene.meshes[mesh_id]; 162 | 163 | gfxProgramSetParameter(gfx, pbr_program, "g_InstanceId", instance_id); 164 | gfxProgramSetParameter(gfx, pbr_program, "g_ViewProjection", fly_camera.view_proj); 165 | gfxProgramSetParameter(gfx, pbr_program, "g_PreviousViewProjection", fly_camera.prev_view_proj); 166 | 167 | gfxCommandDrawIndexed(gfx, mesh.count, 1, mesh.first_index, mesh.base_vertex); 168 | } 169 | 170 | // Draw our skybox 171 | gfxCommandBindColorTarget(gfx, 0, color_buffer); 172 | gfxCommandBindDepthStencilTarget(gfx, depth_buffer); 173 | gfxCommandBindKernel(gfx, sky_kernel); 174 | gfxCommandDraw(gfx, 3); 175 | 176 | // Reproject the temporal history (a.k.a., TAA) 177 | gfxCommandBindColorTarget(gfx, 0, resolve_buffer); 178 | gfxCommandBindKernel(gfx, reproject_kernel); 179 | gfxCommandDraw(gfx, 3); 180 | 181 | // Update the temporal history with the new anti-aliased frame 182 | gfxCommandCopyTexture(gfx, history_buffer, resolve_buffer); 183 | 184 | // Resolve into the backbuffer 185 | gfxCommandBindKernel(gfx, resolve_kernel); 186 | gfxCommandDraw(gfx, 3); 187 | 188 | // And submit the frame 189 | gfxImGuiRender(); 190 | gfxFrame(gfx); 191 | } 192 | 193 | // Release our resources 194 | gfxDestroyTexture(gfx, color_buffer); 195 | gfxDestroyTexture(gfx, depth_buffer); 196 | gfxDestroyTexture(gfx, history_buffer); 197 | gfxDestroyTexture(gfx, resolve_buffer); 198 | gfxDestroyTexture(gfx, velocity_buffer); 199 | 200 | ReleaseIBL(gfx, ibl); 201 | gfxDestroySamplerState(gfx, linear_sampler); 202 | gfxDestroySamplerState(gfx, nearest_sampler); 203 | 204 | gfxDestroyKernel(gfx, pbr_kernel); 205 | gfxDestroyProgram(gfx, pbr_program); 206 | gfxDestroyKernel(gfx, sky_kernel); 207 | gfxDestroyProgram(gfx, sky_program); 208 | gfxDestroyKernel(gfx, resolve_kernel); 209 | gfxDestroyKernel(gfx, reproject_kernel); 210 | gfxDestroyProgram(gfx, taa_program); 211 | 212 | gfxImGuiTerminate(); 213 | gfxDestroyScene(scene); 214 | ReleaseGpuScene(gfx, gpu_scene); 215 | 216 | gfxDestroyContext(gfx); 217 | gfxDestroyWindow(window); 218 | 219 | return 0; 220 | } 221 | -------------------------------------------------------------------------------- /examples/02-pbr/pbr.frag: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | MIT License 3 | 4 | Copyright (c) 2024 Guillaume Boissé 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | ****************************************************************************/ 24 | #include "../common/gpu_scene.hlsli" 25 | 26 | float3 g_Eye; 27 | uint g_InstanceId; 28 | 29 | Texture2D g_BrdfBuffer; 30 | TextureCube g_IrradianceBuffer; 31 | TextureCube g_EnvironmentBuffer; 32 | 33 | SamplerState g_LinearSampler; 34 | 35 | struct Params 36 | { 37 | float4 position : SV_Position; 38 | float3 normal : NORMAL; 39 | float2 uv : TEXCOORD0; 40 | float3 world : POSITION0; 41 | float4 current : POSITION1; 42 | float4 previous : POSITION2; 43 | }; 44 | 45 | struct Result 46 | { 47 | float4 color : SV_Target0; 48 | float2 velocity : SV_Target1; 49 | }; 50 | 51 | // https://seblagarde.wordpress.com/2011/08/17/hello-world/ 52 | float3 FresnelSchlickRoughness(in float n_dot_v, in float3 F0, in float roughness) 53 | { 54 | return F0 + (max(F0, 1.0f - roughness) - F0) * pow(1.0f - n_dot_v, 5.0f); 55 | } 56 | 57 | // Calculates motion vectors in UV-space (i.e., normalized [0, 1] coordinates) 58 | float2 CalculateVelocity(in Params params) 59 | { 60 | float2 ndc_velocity = params.current.xy / params.current.w 61 | - params.previous.xy / params.previous.w; 62 | float2 uv_velocity = ndc_velocity * float2(0.5f, -0.5f); 63 | 64 | return uv_velocity; 65 | } 66 | 67 | Result main(in Params params) 68 | { 69 | // Load our material 70 | Instance instance = g_InstanceBuffer[g_InstanceId]; 71 | Material material = g_MaterialBuffer[instance.material_id]; 72 | 73 | // Load and sample our texture maps 74 | uint albedo_map = asuint(material.albedo.w); 75 | uint roughness_map = asuint(material.metallicity_roughness.w); 76 | uint metallicity_map = asuint(material.metallicity_roughness.y); 77 | uint normal_map = asuint(material.ao_normal_emissivity.y); 78 | uint ao_map = asuint(material.ao_normal_emissivity.x); 79 | 80 | if(albedo_map != uint(-1)) 81 | { 82 | material.albedo.xyz *= g_Textures[albedo_map].Sample(g_TextureSampler, params.uv).xyz; 83 | } 84 | 85 | if(roughness_map != uint(-1)) 86 | { 87 | material.metallicity_roughness.z *= g_Textures[roughness_map].Sample(g_TextureSampler, params.uv).x; 88 | } 89 | 90 | if(metallicity_map != uint(-1)) 91 | { 92 | material.metallicity_roughness.x *= g_Textures[metallicity_map].Sample(g_TextureSampler, params.uv).x; 93 | } 94 | 95 | if(normal_map != uint(-1)) 96 | { 97 | float3 normal = normalize(params.normal); 98 | float3 view_direction = normalize(g_Eye - params.world); 99 | 100 | float3 dp1 = ddx(-view_direction); 101 | float3 dp2 = ddy(-view_direction); 102 | float2 duv1 = ddx(params.uv); 103 | float2 duv2 = ddy(params.uv); 104 | 105 | float3 dp2perp = normalize(cross(dp2, normal)); 106 | float3 dp1perp = normalize(cross(normal, dp1)); 107 | float3 tangent = dp2perp * duv1.x + dp1perp * duv2.x; 108 | float3 bitangent = dp2perp * duv1.y + dp1perp * duv2.y; 109 | 110 | float invmax = rsqrt(max(dot(tangent, tangent), dot(bitangent, bitangent))); 111 | float3x3 tbn = transpose(float3x3(tangent * invmax, bitangent * invmax, normal)); 112 | float3 disturb = 2.0f * g_Textures[normal_map].Sample(g_TextureSampler, params.uv).xyz - 1.0f; 113 | 114 | params.normal = mul(tbn, disturb); 115 | } 116 | 117 | if(ao_map != uint(-1)) 118 | { 119 | material.ao_normal_emissivity.x = g_Textures[ao_map].Sample(g_TextureSampler, params.uv).x; 120 | } 121 | else 122 | { 123 | material.ao_normal_emissivity.x = 1.0f; 124 | } 125 | 126 | // Post-process our material properties 127 | material.albedo.xyz = sqrt(material.albedo.xyz); 128 | material.metallicity_roughness.x = saturate(material.metallicity_roughness.x); 129 | material.metallicity_roughness.z = clamp(material.metallicity_roughness.z * material.metallicity_roughness.z, 0.01f, 1.0f); 130 | 131 | // And shade :) 132 | float3 albedo = material.albedo.xyz; 133 | float3 normal = normalize(params.normal); 134 | float roughness = material.metallicity_roughness.z; 135 | float metallicity = material.metallicity_roughness.x; 136 | float ao = material.ao_normal_emissivity.x; 137 | 138 | float3 view_direction = normalize(g_Eye - params.world); 139 | float n_dot_v = max(dot(normal, view_direction), 0.0f); 140 | float2 brdf_uv = float2(max(dot(normal, view_direction), 0.0f), roughness); 141 | float3 reflection_direction = reflect(-view_direction, normal); 142 | 143 | float3 F0 = lerp(0.04f, albedo, metallicity); 144 | float3 F = FresnelSchlickRoughness(n_dot_v, F0, roughness); 145 | float3 kD = (1.0f - F) * (1.0f - metallicity * (1.0f - roughness)); 146 | 147 | float2 brdf = g_BrdfBuffer.SampleLevel(g_LinearSampler, brdf_uv, 0.0f).xy; 148 | float3 irradiance = g_IrradianceBuffer.SampleLevel(g_LinearSampler, normal, 0.0f).xyz; 149 | float3 environment = g_EnvironmentBuffer.SampleLevel(g_LinearSampler, reflection_direction, roughness * 4.0f).xyz; 150 | 151 | float so = saturate(ao + (ao + n_dot_v) * (ao + n_dot_v) - 1.0f); 152 | float3 diffuse = kD * ao * albedo * irradiance; 153 | float3 specular = (F * brdf.x + brdf.y) * lerp(ao * (F * brdf.x + brdf.y) * irradiance, environment, so); 154 | 155 | float3 color = diffuse + specular; 156 | 157 | // Tonemap the color output 158 | color *= 0.75f; 159 | color /= 1.0f + color; 160 | color = saturate(color); 161 | color = sqrt(color); 162 | color = color * color * (3.0f - 2.0f * color); 163 | 164 | // Populate our multiple render targets (i.e., MRT) 165 | Result result; 166 | result.color = float4(color, 1.0f); 167 | result.velocity = CalculateVelocity(params); 168 | 169 | return result; 170 | } 171 | -------------------------------------------------------------------------------- /examples/02-pbr/pbr.vert: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | MIT License 3 | 4 | Copyright (c) 2024 Guillaume Boissé 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | ****************************************************************************/ 24 | #include "../common/gpu_scene.hlsli" 25 | 26 | uint g_InstanceId; 27 | float4x4 g_ViewProjection; 28 | float4x4 g_PreviousViewProjection; 29 | 30 | struct Params 31 | { 32 | float4 position : SV_Position; 33 | float3 normal : NORMAL; 34 | float2 uv : TEXCOORD0; 35 | float3 world : POSITION0; 36 | float4 current : POSITION1; 37 | float4 previous : POSITION2; 38 | }; 39 | 40 | Params main(in Vertex vertex) 41 | { 42 | float4x4 transform = g_TransformBuffer[g_InstanceId]; 43 | float4x4 previous_transform = g_PreviousTransformBuffer[g_InstanceId]; 44 | float4 position = mul(transform, vertex.position); 45 | float4 previous_position = mul(previous_transform, vertex.position); 46 | float3 normal = TransformDirection(transform, vertex.normal.xyz); 47 | 48 | Params params; 49 | params.position = mul(g_ViewProjection, position); 50 | params.normal = normal; 51 | params.uv = vertex.uv; 52 | params.world = position.xyz; 53 | params.current = params.position; 54 | params.previous = mul(g_PreviousViewProjection, previous_position); 55 | 56 | return params; 57 | } 58 | -------------------------------------------------------------------------------- /examples/02-pbr/sky.frag: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | MIT License 3 | 4 | Copyright (c) 2024 Guillaume Boissé 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | ****************************************************************************/ 24 | 25 | float3 g_Eye; 26 | float2 g_TexelSize; 27 | float4x4 g_ViewProjectionInverse; 28 | 29 | TextureCube g_EnvironmentBuffer; 30 | 31 | SamplerState g_LinearSampler; 32 | 33 | float4 main(in float4 pos : SV_Position) : SV_Target 34 | { 35 | float2 uv = pos.xy * g_TexelSize; 36 | float2 ndc = 2.0f * float2(uv.x, 1.0f - uv.y) - 1.0f; 37 | 38 | // Retrieve the ray direction and sample the environment 39 | float4 world = mul(g_ViewProjectionInverse, float4(ndc, 1.0f, 1.0f)); 40 | world /= world.w; // perspective divide 41 | 42 | float3 color = g_EnvironmentBuffer.SampleLevel(g_LinearSampler, world.xyz - g_Eye, 1.0f).xyz; 43 | 44 | // Tonemap the color output 45 | color *= 0.75f; 46 | color /= 1.0f + color; 47 | color = saturate(color); 48 | color = sqrt(color); 49 | color = color * color * (3.0f - 2.0f * color); 50 | 51 | return float4(color, 1.0f); 52 | } 53 | -------------------------------------------------------------------------------- /examples/02-pbr/sky.vert: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | MIT License 3 | 4 | Copyright (c) 2024 Guillaume Boissé 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | ****************************************************************************/ 24 | 25 | float4 main(in uint idx : SV_VertexID) : SV_Position 26 | { 27 | return 1.0f - float4(4.0f * (idx & 1), 4.0f * (idx >> 1), 1e-6f, 0.0f); 28 | } 29 | -------------------------------------------------------------------------------- /examples/02-pbr/taa.frag: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | MIT License 3 | 4 | Copyright (c) 2024 Guillaume Boissé 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | ****************************************************************************/ 24 | 25 | float2 g_TexelSize; 26 | 27 | Texture2D g_ColorBuffer; 28 | Texture2D g_HistoryBuffer; 29 | Texture2D g_ResolveBuffer; 30 | Texture2D g_VelocityBuffer; 31 | Texture2D g_DepthBuffer; 32 | 33 | SamplerState g_LinearSampler; 34 | SamplerState g_NearestSampler; 35 | 36 | float SquareLength(in float2 value) 37 | { 38 | return (value.x * value.x + value.y * value.y); 39 | } 40 | 41 | // https://www.gdcvault.com/play/1022970/Temporal-Reprojection-Anti-Aliasing-in 42 | float2 FindClosestVelocity(in float2 uv, out bool is_sky_pixel) 43 | { 44 | float2 velocity; 45 | float closest_depth = 9.9f; 46 | for(int y = -1; y <= 1; ++y) 47 | for(int x = -1; x <= 1; ++x) 48 | { 49 | float2 tex = uv + float2(x, y) * g_TexelSize; 50 | float depth = g_DepthBuffer.Sample(g_NearestSampler, tex).x; 51 | if(depth < closest_depth) 52 | { 53 | velocity = g_VelocityBuffer.Sample(g_NearestSampler, tex).xy; 54 | closest_depth = depth; 55 | } 56 | } 57 | is_sky_pixel = (closest_depth == 1.0f); 58 | return velocity; 59 | } 60 | 61 | float4 Reproject(in float4 pos : SV_Position) : SV_Target 62 | { 63 | float2 uv = pos.xy * g_TexelSize; 64 | 65 | float3 vsum = g_ColorBuffer.Sample(g_NearestSampler, uv).xyz; 66 | float3 vsum2 = vsum * vsum; 67 | float wsum = 1.0f; 68 | 69 | float3 nmin = vsum; 70 | float3 nmax = vsum; 71 | 72 | for(float y = -1.0f; y <= 1.0f; ++y) 73 | { 74 | for(float x = -1.0f; x <= 1.0f; ++x) 75 | { 76 | if(x != 0.0f || y != 0.0f) 77 | { 78 | float3 v = g_ColorBuffer.Sample(g_NearestSampler, uv + float2(x, y) * g_TexelSize).xyz; 79 | float w = exp(-3.0f * SquareLength(float2(x, y)) / 4.0f); 80 | 81 | vsum2 += v * v * w; 82 | vsum += v * w; 83 | wsum += w; 84 | 85 | nmin = min(v, nmin); 86 | nmax = max(v, nmax); 87 | } 88 | } 89 | } 90 | 91 | // https://developer.download.nvidia.com/gameworks/events/GDC2016/msalvi_temporal_supersampling.pdf 92 | float3 ex = vsum / wsum; 93 | float3 ex2 = vsum2 / wsum; 94 | float3 dev = sqrt(max(ex2 - ex * ex, 0.0f)); 95 | 96 | bool is_sky_pixel; 97 | float2 velocity = FindClosestVelocity(uv, is_sky_pixel); 98 | float box_size = lerp(0.5f, 2.5f, is_sky_pixel ? 0.0f : smoothstep(0.02f, 0.0f, length(velocity))); 99 | 100 | nmin = max(ex - dev * box_size, nmin); 101 | nmax = min(ex + dev * box_size, nmax); 102 | 103 | // http://advances.realtimerendering.com/s2014/#_HIGH-QUALITY_TEMPORAL_SUPERSAMPLING 104 | float3 history = g_HistoryBuffer.Sample(g_LinearSampler, uv - velocity).xyz; 105 | float3 clamped_history = clamp(history, nmin, nmax); 106 | float3 center = g_ColorBuffer.Sample(g_NearestSampler, uv).xyz; 107 | float3 result = lerp(clamped_history, center, 1.0f / 16.0f); 108 | 109 | return float4(result, 1.0f); 110 | } 111 | 112 | float3 RGBToYCoCg(in float3 rgb) 113 | { 114 | return float3( 115 | 0.25f * rgb.r + 0.5f * rgb.g + 0.25f * rgb.b, 116 | 0.5f * rgb.r - 0.5f * rgb.b, 117 | -0.25f * rgb.r + 0.5f * rgb.g - 0.25f * rgb.b); 118 | } 119 | 120 | float3 YCoCgToRGB(in float3 yCoCg) 121 | { 122 | return float3( 123 | yCoCg.x + yCoCg.y - yCoCg.z, 124 | yCoCg.x + yCoCg.z, 125 | yCoCg.x - yCoCg.y - yCoCg.z); 126 | } 127 | 128 | // https://en.wikipedia.org/wiki/Unsharp_masking 129 | float3 ApplySharpening(in float3 center, in float3 top, in float3 left, in float3 right, in float3 bottom) 130 | { 131 | float3 result = RGBToYCoCg(center); 132 | 133 | float unsharpenMask; 134 | unsharpenMask = 4.0f * result.x; 135 | unsharpenMask -= RGBToYCoCg(top).x; 136 | unsharpenMask -= RGBToYCoCg(bottom).x; 137 | unsharpenMask -= RGBToYCoCg(left).x; 138 | unsharpenMask -= RGBToYCoCg(right).x; 139 | 140 | result.x = clamp(result.x + 0.25f * unsharpenMask, 0.9f * result.x, 1.1f * result.x); 141 | 142 | return YCoCgToRGB(result); 143 | } 144 | 145 | float4 Resolve(in float4 pos : SV_Position) : SV_Target 146 | { 147 | float2 uv = pos.xy * g_TexelSize; 148 | 149 | float3 color = ApplySharpening(g_ResolveBuffer.Sample(g_NearestSampler, uv).xyz, 150 | g_ResolveBuffer.Sample(g_NearestSampler, uv + float2( 0.0f, 1.0f) * g_TexelSize).xyz, 151 | g_ResolveBuffer.Sample(g_NearestSampler, uv + float2( 1.0f, 0.0f) * g_TexelSize).xyz, 152 | g_ResolveBuffer.Sample(g_NearestSampler, uv + float2(-1.0f, 0.0f) * g_TexelSize).xyz, 153 | g_ResolveBuffer.Sample(g_NearestSampler, uv + float2( 0.0f, -1.0f) * g_TexelSize).xyz); 154 | 155 | return float4(color, 1.0f); 156 | } 157 | -------------------------------------------------------------------------------- /examples/02-pbr/taa.vert: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | MIT License 3 | 4 | Copyright (c) 2024 Guillaume Boissé 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | ****************************************************************************/ 24 | 25 | float4 Reproject(in uint idx : SV_VertexID) : SV_POSITION 26 | { 27 | return 1.0f - float4(4.0f * (idx & 1), 4.0f * (idx >> 1), 1.0f, 0.0f); 28 | } 29 | 30 | float4 Resolve(in uint idx : SV_VertexID) : SV_POSITION 31 | { 32 | return 1.0f - float4(4.0f * (idx & 1), 4.0f * (idx >> 1), 1.0f, 0.0f); 33 | } 34 | -------------------------------------------------------------------------------- /examples/03-mesh/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(03-mesh ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp) 2 | 3 | target_link_libraries(03-mesh PUBLIC gfx) 4 | 5 | set_target_properties(03-mesh PROPERTIES FOLDER "examples") 6 | 7 | set_target_properties(03-mesh PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) 8 | 9 | file(GLOB SHADER_FILES CONFIGURE_DEPENDS 10 | ${CMAKE_CURRENT_SOURCE_DIR}/*.task 11 | ${CMAKE_CURRENT_SOURCE_DIR}/*.mesh 12 | ${CMAKE_CURRENT_SOURCE_DIR}/*.frag) 13 | 14 | target_sources(03-mesh PRIVATE ${SHADER_FILES}) 15 | 16 | source_group("Media Files\\Shaders" FILES ${SHADER_FILES}) 17 | 18 | set_source_files_properties(${SHADER_FILES} PROPERTIES VS_TOOL_OVERRIDE "None") 19 | 20 | add_custom_command(TARGET 03-mesh POST_BUILD 21 | COMMAND ${CMAKE_COMMAND} -E copy $ $ 22 | COMMAND_EXPAND_LISTS 23 | ) 24 | -------------------------------------------------------------------------------- /examples/03-mesh/main.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | MIT License 3 | 4 | Copyright (c) 2024 Guillaume Boissé 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | ****************************************************************************/ 24 | #include "gfx_window.h" 25 | 26 | int32_t main() 27 | { 28 | GfxWindow window = gfxCreateWindow(1280, 720, "gfx - Mesh shaders"); 29 | GfxContext gfx = gfxCreateContext(window); 30 | 31 | GfxProgram program = gfxCreateProgram(gfx, "mesh"); 32 | GfxKernel kernel = gfxCreateMeshKernel(gfx, program); 33 | 34 | for(float time = 0.0f; !gfxWindowIsCloseRequested(window); time += 0.1f) 35 | { 36 | gfxWindowPumpEvents(window); 37 | 38 | float color[] = { 0.5f * cosf(time) + 0.5f, 39 | 0.5f * sinf(time) + 0.5f, 40 | 1.0f }; 41 | gfxProgramSetParameter(gfx, program, "Color", color); 42 | 43 | gfxCommandBindKernel(gfx, kernel); 44 | gfxCommandDrawMesh(gfx, 1, 1, 1); 45 | 46 | gfxFrame(gfx); 47 | } 48 | 49 | gfxDestroyKernel(gfx, kernel); 50 | gfxDestroyProgram(gfx, program); 51 | 52 | gfxDestroyContext(gfx); 53 | gfxDestroyWindow(window); 54 | 55 | return 0; 56 | } 57 | -------------------------------------------------------------------------------- /examples/03-mesh/mesh.frag: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | MIT License 3 | 4 | Copyright (c) 2024 Guillaume Boissé 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | ****************************************************************************/ 24 | 25 | float3 Color; 26 | 27 | float4 main() : SV_Target 28 | { 29 | return float4(Color, 1.0f); 30 | } 31 | -------------------------------------------------------------------------------- /examples/03-mesh/mesh.mesh: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | MIT License 3 | 4 | Copyright (c) 2024 Guillaume Boissé 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | ****************************************************************************/ 24 | 25 | struct Vertex 26 | { 27 | float4 position : SV_Position; 28 | }; 29 | 30 | [outputtopology("triangle")] 31 | [numthreads(1, 1, 1)] 32 | void main(out vertices Vertex verts[3], out indices uint3 triangles[1]) 33 | { 34 | SetMeshOutputCounts(3, 1); 35 | 36 | verts[0].position = float4( 0.5f, -0.5f, 0.0f, 1.0f); 37 | verts[1].position = float4( 0.0f, 0.7f, 0.0f, 1.0f); 38 | verts[2].position = float4(-0.5f, -0.5f, 0.0f, 1.0f); 39 | 40 | triangles[0] = uint3(0, 1, 2); 41 | } 42 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/00-hellotriangle) 3 | 4 | if(GFX_ENABLE_GUI AND GFX_ENABLE_SCENE) 5 | add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/common) 6 | add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/01-rtao) 7 | add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/02-pbr) 8 | endif() 9 | 10 | add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/03-mesh) 11 | -------------------------------------------------------------------------------- /examples/common/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | add_library(common STATIC 3 | ${CMAKE_CURRENT_SOURCE_DIR}/fly_camera.cpp 4 | ${CMAKE_CURRENT_SOURCE_DIR}/gpu_scene.cpp 5 | ) 6 | 7 | target_sources(common PRIVATE 8 | ${CMAKE_CURRENT_SOURCE_DIR}/fly_camera.h 9 | ${CMAKE_CURRENT_SOURCE_DIR}/gpu_scene.h 10 | ) 11 | 12 | file(GLOB SHADER_FILES CONFIGURE_DEPENDS${CMAKE_CURRENT_SOURCE_DIR}/*.hlsli) 13 | 14 | target_sources(common PRIVATE ${SHADER_FILES}) 15 | 16 | source_group("Media Files\\Shaders" FILES ${SHADER_FILES}) 17 | 18 | set_source_files_properties(${SHADER_FILES} PROPERTIES VS_TOOL_OVERRIDE "None") 19 | 20 | target_link_libraries(common PUBLIC gfx) 21 | 22 | set_target_properties(common PROPERTIES FOLDER "examples") 23 | 24 | target_include_directories(gfx PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) 25 | -------------------------------------------------------------------------------- /examples/common/fly_camera.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | MIT License 3 | 4 | Copyright (c) 2024 Guillaume Boissé 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | ****************************************************************************/ 24 | #include "fly_camera.h" 25 | 26 | #include "glm/gtc/matrix_transform.hpp" 27 | 28 | namespace 29 | { 30 | 31 | float CalculateHaltonNumber(uint32_t index, uint32_t base) 32 | { 33 | float f = 1.0f, result = 0.0f; 34 | for(uint32_t i = index; i > 0;) 35 | { 36 | f /= base; 37 | result = result + f * (i % base); 38 | i = (uint32_t)(i / (float)base); 39 | } 40 | return result; 41 | } 42 | 43 | } //! unnamed namespace 44 | 45 | FlyCamera CreateFlyCamera(GfxContext gfx, glm::vec3 const &eye, glm::vec3 const ¢er) 46 | { 47 | FlyCamera fly_camera = {}; 48 | 49 | float const aspect_ratio = gfxGetBackBufferWidth(gfx) / (float)gfxGetBackBufferHeight(gfx); 50 | 51 | fly_camera.eye = eye; 52 | fly_camera.center = center; 53 | fly_camera.up = glm::vec3(0.0f, 1.0f, 0.0f); 54 | 55 | fly_camera.view = glm::lookAt(eye, center, fly_camera.up); 56 | fly_camera.proj = glm::perspective(0.6f, aspect_ratio, 1e-1f, 1e4f); 57 | fly_camera.view_proj = fly_camera.proj * fly_camera.view; 58 | 59 | fly_camera.prev_view = fly_camera.view; 60 | fly_camera.prev_proj = fly_camera.proj; 61 | fly_camera.prev_view_proj = fly_camera.view_proj; 62 | 63 | return fly_camera; 64 | } 65 | 66 | void UpdateFlyCamera(GfxContext gfx, GfxWindow window, FlyCamera &fly_camera) 67 | { 68 | // Update camera history 69 | fly_camera.prev_view = fly_camera.view; 70 | fly_camera.prev_proj = fly_camera.proj; 71 | 72 | // TODO: animate the camera... (gboisse) 73 | 74 | // Update projection aspect ratio 75 | float const aspect_ratio = gfxGetBackBufferWidth(gfx) / (float)gfxGetBackBufferHeight(gfx); 76 | 77 | fly_camera.proj = glm::perspective(0.6f, aspect_ratio, 1e-1f, 1e4f); 78 | 79 | // Update projection jitter for anti-aliasing 80 | static uint32_t jitter_index; 81 | 82 | jitter_index = (jitter_index + 1) & 15; // 16 samples TAA 83 | 84 | float const jitter_x = (2.0f * CalculateHaltonNumber(jitter_index + 1, 2) - 1.0f) / gfxGetBackBufferWidth(gfx); 85 | float const jitter_y = (2.0f * CalculateHaltonNumber(jitter_index + 1, 3) - 1.0f) / gfxGetBackBufferHeight(gfx); 86 | 87 | fly_camera.proj[2][0] = jitter_x; 88 | fly_camera.proj[2][1] = jitter_y; 89 | fly_camera.prev_proj[2][0] = jitter_x; // patch previous projection matrix so subpixel jitter doesn't generate velocity values 90 | fly_camera.prev_proj[2][1] = jitter_y; 91 | 92 | fly_camera.view_proj = fly_camera.proj * fly_camera.view; 93 | fly_camera.prev_view_proj = fly_camera.prev_proj * fly_camera.prev_view; 94 | } 95 | -------------------------------------------------------------------------------- /examples/common/fly_camera.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | MIT License 3 | 4 | Copyright (c) 2024 Guillaume Boissé 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | ****************************************************************************/ 24 | #pragma once 25 | 26 | #include "glm/glm.hpp" 27 | #include "gfx_window.h" 28 | 29 | struct FlyCamera 30 | { 31 | glm::vec3 eye; 32 | glm::vec3 center; 33 | glm::vec3 up; 34 | 35 | glm::mat4 view; 36 | glm::mat4 proj; 37 | glm::mat4 view_proj; 38 | 39 | glm::mat4 prev_view; 40 | glm::mat4 prev_proj; 41 | glm::mat4 prev_view_proj; 42 | }; 43 | 44 | FlyCamera CreateFlyCamera(GfxContext gfx, glm::vec3 const &eye, glm::vec3 const ¢er); 45 | void UpdateFlyCamera(GfxContext gfx, GfxWindow window, FlyCamera &fly_camera); 46 | -------------------------------------------------------------------------------- /examples/common/gpu_scene.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | MIT License 3 | 4 | Copyright (c) 2024 Guillaume Boissé 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | ****************************************************************************/ 24 | #include "gpu_scene.h" 25 | 26 | namespace 27 | { 28 | 29 | struct Material 30 | { 31 | glm::vec4 albedo; 32 | glm::vec4 metallicity_roughness; 33 | glm::vec4 ao_normal_emissivity; 34 | }; 35 | 36 | struct Instance 37 | { 38 | uint32_t mesh_id; 39 | uint32_t material_id; 40 | }; 41 | 42 | struct Vertex 43 | { 44 | glm::vec4 position; 45 | glm::vec4 normal; 46 | glm::vec2 uv; 47 | }; 48 | 49 | } //! unnamed namespace 50 | 51 | GpuScene UploadSceneToGpuMemory(GfxContext gfx, GfxScene scene) 52 | { 53 | GpuScene gpu_scene = {}; 54 | 55 | // Load our materials 56 | std::vector materials; 57 | 58 | for(uint32_t i = 0; i < gfxSceneGetMaterialCount(scene); ++i) 59 | { 60 | GfxConstRef material_ref = gfxSceneGetMaterialHandle(scene, i); 61 | 62 | Material material = {}; 63 | material.albedo = glm::vec4(glm::vec3(material_ref->albedo), glm::uintBitsToFloat((uint32_t)material_ref->albedo_map)); 64 | material.metallicity_roughness = glm::vec4( material_ref->metallicity, glm::uintBitsToFloat((uint32_t)material_ref->metallicity_map), 65 | material_ref->roughness, glm::uintBitsToFloat((uint32_t)material_ref->roughness_map)); 66 | material.ao_normal_emissivity = glm::vec4(glm::uintBitsToFloat((uint32_t)material_ref->ao_map), 67 | glm::uintBitsToFloat((uint32_t)material_ref->normal_map), 68 | glm::uintBitsToFloat((uint32_t)material_ref->emissivity_map), 0.0f); 69 | 70 | uint32_t const material_id = (uint32_t)material_ref; 71 | 72 | if(material_id >= materials.size()) 73 | { 74 | materials.resize(material_id + 1); 75 | } 76 | 77 | materials[material_id] = material; 78 | } 79 | 80 | gpu_scene.material_buffer = gfxCreateBuffer(gfx, (uint32_t)materials.size(), materials.data()); 81 | 82 | // Load our meshes 83 | uint32_t first_index = 0; 84 | uint32_t base_vertex = 0; 85 | 86 | for(uint32_t i = 0; i < gfxSceneGetMeshCount(scene); ++i) 87 | { 88 | GfxConstRef mesh_ref = gfxSceneGetMeshHandle(scene, i); 89 | 90 | Mesh mesh = {}; 91 | mesh.count = (uint32_t)mesh_ref->indices.size(); 92 | mesh.first_index = first_index; 93 | mesh.base_vertex = base_vertex; 94 | 95 | uint32_t const mesh_id = (uint32_t)mesh_ref; 96 | 97 | if(mesh_id >= gpu_scene.meshes.size()) 98 | { 99 | gpu_scene.meshes.resize(mesh_id + 1); 100 | } 101 | 102 | gpu_scene.meshes[mesh_id] = mesh; 103 | 104 | first_index += (uint32_t)mesh_ref->indices.size(); 105 | base_vertex += (uint32_t)mesh_ref->vertices.size(); 106 | } 107 | 108 | gpu_scene.mesh_buffer = gfxCreateBuffer(gfx, (uint32_t)gpu_scene.meshes.size(), gpu_scene.meshes.data()); 109 | 110 | // Load our vertices 111 | std::vector indices; 112 | std::vector vertices; 113 | 114 | for(uint32_t i = 0; i < gfxSceneGetMeshCount(scene); ++i) 115 | { 116 | GfxConstRef mesh_ref = gfxSceneGetMeshHandle(scene, i); 117 | 118 | std::vector const &index_buffer = mesh_ref->indices; 119 | 120 | for(uint32_t index : index_buffer) 121 | { 122 | indices.push_back(index); 123 | } 124 | 125 | std::vector const &vertex_buffer = mesh_ref->vertices; 126 | 127 | for(GfxVertex vertex : vertex_buffer) 128 | { 129 | Vertex gpu_vertex = {}; 130 | 131 | gpu_vertex.position = glm::vec4(vertex.position, 1.0f); 132 | gpu_vertex.normal = glm::vec4(vertex.normal, 0.0f); 133 | gpu_vertex.uv = glm::vec2(vertex.uv); 134 | 135 | vertices.push_back(gpu_vertex); 136 | } 137 | } 138 | 139 | gpu_scene.index_buffer = gfxCreateBuffer(gfx, (uint32_t)indices.size(), indices.data()); 140 | gpu_scene.vertex_buffer = gfxCreateBuffer(gfx, (uint32_t)vertices.size(), vertices.data()); 141 | 142 | // Load our instances 143 | std::vector instances; 144 | std::vector transforms; 145 | 146 | for(uint32_t i = 0; i < gfxSceneGetInstanceCount(scene); ++i) 147 | { 148 | GfxConstRef const instance_ref = gfxSceneGetInstanceHandle(scene, i); 149 | 150 | Instance instance = {}; 151 | instance.mesh_id = (uint32_t)instance_ref->mesh; 152 | instance.material_id = (uint32_t)instance_ref->material; 153 | 154 | uint32_t const instance_id = (uint32_t)instance_ref; 155 | 156 | if(instance_id >= instances.size()) 157 | { 158 | instances.resize(instance_id + 1); 159 | transforms.resize(instance_id + 1); 160 | } 161 | 162 | instances[instance_id] = instance; 163 | transforms[instance_id] = instance_ref->transform; 164 | } 165 | 166 | gpu_scene.instance_buffer = gfxCreateBuffer(gfx, (uint32_t)instances.size(), instances.data()); 167 | gpu_scene.transform_buffer = gfxCreateBuffer(gfx, (uint32_t)transforms.size(), transforms.data()); 168 | gpu_scene.previous_transform_buffer = gfxCreateBuffer(gfx, (uint32_t)transforms.size(), transforms.data()); 169 | 170 | for(GfxBuffer &upload_transform_buffer : gpu_scene.upload_transform_buffers) 171 | { 172 | upload_transform_buffer = gfxCreateBuffer(gfx, (uint32_t)transforms.size(), nullptr, kGfxCpuAccess_Write); 173 | } 174 | 175 | for(uint32_t i = 0; i < gfxSceneGetImageCount(scene); ++i) 176 | { 177 | GfxConstRef const image_ref = gfxSceneGetImageHandle(scene, i); 178 | 179 | GfxTexture texture = gfxCreateTexture2D(gfx, image_ref->width, image_ref->height, image_ref->format, gfxCalculateMipCount(image_ref->width, image_ref->height)); 180 | 181 | uint32_t const texture_size = image_ref->width * image_ref->height * image_ref->channel_count * image_ref->bytes_per_channel; 182 | 183 | GfxBuffer upload_texture_buffer = gfxCreateBuffer(gfx, texture_size, image_ref->data.data(), kGfxCpuAccess_Write); 184 | 185 | gfxCommandCopyBufferToTexture(gfx, texture, upload_texture_buffer); 186 | gfxDestroyBuffer(gfx, upload_texture_buffer); 187 | gfxCommandGenerateMips(gfx, texture); 188 | 189 | uint32_t const image_id = (uint32_t)image_ref; 190 | 191 | if(image_id >= gpu_scene.textures.size()) 192 | { 193 | gpu_scene.textures.resize(image_id + 1); 194 | } 195 | 196 | gpu_scene.textures[image_id] = texture; 197 | } 198 | 199 | gpu_scene.texture_sampler = gfxCreateSamplerState(gfx, D3D12_FILTER_ANISOTROPIC, D3D12_TEXTURE_ADDRESS_MODE_WRAP, D3D12_TEXTURE_ADDRESS_MODE_WRAP); 200 | 201 | return gpu_scene; 202 | } 203 | 204 | void ReleaseGpuScene(GfxContext gfx, GpuScene const &gpu_scene) 205 | { 206 | gfxDestroyBuffer(gfx, gpu_scene.mesh_buffer); 207 | gfxDestroyBuffer(gfx, gpu_scene.index_buffer); 208 | gfxDestroyBuffer(gfx, gpu_scene.vertex_buffer); 209 | gfxDestroyBuffer(gfx, gpu_scene.instance_buffer); 210 | gfxDestroyBuffer(gfx, gpu_scene.material_buffer); 211 | gfxDestroyBuffer(gfx, gpu_scene.transform_buffer); 212 | gfxDestroyBuffer(gfx, gpu_scene.previous_transform_buffer); 213 | 214 | for(GfxBuffer upload_transform_buffer : gpu_scene.upload_transform_buffers) 215 | { 216 | gfxDestroyBuffer(gfx, upload_transform_buffer); 217 | } 218 | 219 | for(GfxTexture texture : gpu_scene.textures) 220 | { 221 | gfxDestroyTexture(gfx, texture); 222 | } 223 | 224 | gfxDestroySamplerState(gfx, gpu_scene.texture_sampler); 225 | } 226 | 227 | void UpdateGpuScene(GfxContext gfx, GfxScene scene, GpuScene &gpu_scene) 228 | { 229 | GfxBuffer upload_transform_buffer = gpu_scene.upload_transform_buffers[gfxGetBackBufferIndex(gfx)]; 230 | 231 | glm::mat4 *transforms = gfxBufferGetData(gfx, upload_transform_buffer); 232 | 233 | uint32_t const instance_count = gfxSceneGetInstanceCount(scene); 234 | 235 | for(uint32_t i = 0; i < instance_count; ++i) 236 | { 237 | GfxConstRef const instance_ref = gfxSceneGetInstanceHandle(scene, i); 238 | 239 | uint32_t const instance_id = (uint32_t)instance_ref; 240 | 241 | transforms[instance_id] = instance_ref->transform; 242 | } 243 | 244 | gfxCommandCopyBuffer(gfx, gpu_scene.previous_transform_buffer, gpu_scene.transform_buffer); 245 | 246 | gfxCommandCopyBuffer(gfx, gpu_scene.transform_buffer, upload_transform_buffer); 247 | } 248 | 249 | void BindGpuScene(GfxContext gfx, GfxProgram program, GpuScene const &gpu_scene) 250 | { 251 | gfxProgramSetParameter(gfx, program, "g_MeshBuffer", gpu_scene.mesh_buffer); 252 | gfxProgramSetParameter(gfx, program, "g_IndexBuffer", gpu_scene.index_buffer); 253 | gfxProgramSetParameter(gfx, program, "g_VertexBuffer", gpu_scene.vertex_buffer); 254 | gfxProgramSetParameter(gfx, program, "g_InstanceBuffer", gpu_scene.instance_buffer); 255 | gfxProgramSetParameter(gfx, program, "g_MaterialBuffer", gpu_scene.material_buffer); 256 | gfxProgramSetParameter(gfx, program, "g_TransformBuffer", gpu_scene.transform_buffer); 257 | gfxProgramSetParameter(gfx, program, "g_PreviousTransformBuffer", gpu_scene.previous_transform_buffer); 258 | 259 | gfxProgramSetParameter(gfx, program, "g_Textures", gpu_scene.textures.data(), (uint32_t)gpu_scene.textures.size()); 260 | 261 | gfxProgramSetParameter(gfx, program, "g_TextureSampler", gpu_scene.texture_sampler); 262 | } 263 | -------------------------------------------------------------------------------- /examples/common/gpu_scene.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | MIT License 3 | 4 | Copyright (c) 2024 Guillaume Boissé 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | ****************************************************************************/ 24 | #pragma once 25 | 26 | #include "gfx_scene.h" 27 | 28 | struct Mesh 29 | { 30 | uint32_t count; 31 | uint32_t first_index; 32 | uint32_t base_vertex; 33 | uint32_t padding; 34 | }; 35 | 36 | struct GpuScene 37 | { 38 | std::vector meshes; 39 | 40 | GfxBuffer mesh_buffer; 41 | GfxBuffer index_buffer; 42 | GfxBuffer vertex_buffer; 43 | GfxBuffer instance_buffer; 44 | GfxBuffer material_buffer; 45 | GfxBuffer transform_buffer; 46 | GfxBuffer previous_transform_buffer; 47 | GfxBuffer upload_transform_buffers[kGfxConstant_BackBufferCount]; 48 | 49 | std::vector textures; 50 | 51 | GfxSamplerState texture_sampler; 52 | }; 53 | 54 | GpuScene UploadSceneToGpuMemory(GfxContext gfx, GfxScene scene); 55 | void ReleaseGpuScene(GfxContext gfx, GpuScene const &gpu_scene); 56 | 57 | void UpdateGpuScene(GfxContext gfx, GfxScene scene, GpuScene &gpu_scene); 58 | void BindGpuScene(GfxContext gfx, GfxProgram program, GpuScene const &gpu_scene); 59 | -------------------------------------------------------------------------------- /examples/common/gpu_scene.hlsli: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | MIT License 3 | 4 | Copyright (c) 2024 Guillaume Boissé 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | ****************************************************************************/ 24 | #ifndef GPU_SCENE_HLSLI 25 | #define GPU_SCENE_HLSLI 26 | 27 | struct Material 28 | { 29 | float4 albedo; 30 | float4 metallicity_roughness; 31 | float4 ao_normal_emissivity; 32 | }; 33 | 34 | struct Mesh 35 | { 36 | uint count; 37 | uint first_index; 38 | uint base_vertex; 39 | uint padding; 40 | }; 41 | 42 | struct Instance 43 | { 44 | uint mesh_id; 45 | uint material_id; 46 | }; 47 | 48 | struct Vertex 49 | { 50 | float4 position : POSITION; 51 | float4 normal : NORMAL; 52 | float2 uv : TEXCOORD; 53 | }; 54 | 55 | StructuredBuffer g_MeshBuffer; 56 | StructuredBuffer g_IndexBuffer; 57 | StructuredBuffer g_VertexBuffer; 58 | StructuredBuffer g_InstanceBuffer; 59 | StructuredBuffer g_MaterialBuffer; 60 | StructuredBuffer g_TransformBuffer; 61 | StructuredBuffer g_PreviousTransformBuffer; 62 | 63 | Texture2D g_Textures[] : register(space99); // different space to avoid issues with bindless allocating all available texture registers... 64 | 65 | SamplerState g_TextureSampler; 66 | 67 | // https://github.com/graphitemaster/normals_revisited 68 | float3 TransformDirection(in float4x4 transform, in float3 direction) 69 | { 70 | float4x4 result; 71 | #define minor(m, r0, r1, r2, c0, c1, c2) \ 72 | (m[c0][r0] * (m[c1][r1] * m[c2][r2] - m[c1][r2] * m[c2][r1]) - \ 73 | m[c1][r0] * (m[c0][r1] * m[c2][r2] - m[c0][r2] * m[c2][r1]) + \ 74 | m[c2][r0] * (m[c0][r1] * m[c1][r2] - m[c0][r2] * m[c1][r1])) 75 | result[0][0] = minor(transform, 1, 2, 3, 1, 2, 3); 76 | result[1][0] = -minor(transform, 1, 2, 3, 0, 2, 3); 77 | result[2][0] = minor(transform, 1, 2, 3, 0, 1, 3); 78 | result[3][0] = -minor(transform, 1, 2, 3, 0, 1, 2); 79 | result[0][1] = -minor(transform, 0, 2, 3, 1, 2, 3); 80 | result[1][1] = minor(transform, 0, 2, 3, 0, 2, 3); 81 | result[2][1] = -minor(transform, 0, 2, 3, 0, 1, 3); 82 | result[3][1] = minor(transform, 0, 2, 3, 0, 1, 2); 83 | result[0][2] = minor(transform, 0, 1, 3, 1, 2, 3); 84 | result[1][2] = -minor(transform, 0, 1, 3, 0, 2, 3); 85 | result[2][2] = minor(transform, 0, 1, 3, 0, 1, 3); 86 | result[3][2] = -minor(transform, 0, 1, 3, 0, 1, 2); 87 | result[0][3] = -minor(transform, 0, 1, 2, 1, 2, 3); 88 | result[1][3] = minor(transform, 0, 1, 2, 0, 2, 3); 89 | result[2][3] = -minor(transform, 0, 1, 2, 0, 1, 3); 90 | result[3][3] = minor(transform, 0, 1, 2, 0, 1, 2); 91 | return mul(result, float4(direction, 0.0f)).xyz; 92 | #undef minor // cleanup 93 | } 94 | 95 | #endif //! GPU_SCENE_HLSLI 96 | -------------------------------------------------------------------------------- /gfx_core.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | MIT License 3 | 4 | Copyright (c) 2024 Guillaume Boissé 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | ****************************************************************************/ 24 | 25 | #include "gfx_core.h" 26 | 27 | #include 28 | #include 29 | #define WIN32_LEAN_AND_MEAN 30 | #include 31 | 32 | char const *gfxResultGetString(GfxResult result) 33 | { 34 | switch(result) 35 | { 36 | case kGfxResult_NoError: 37 | return "No error"; 38 | case kGfxResult_InvalidParameter: 39 | return "Invalid parameter"; 40 | case kGfxResult_InvalidOperation: 41 | return "Invalid operation"; 42 | case kGfxResult_OutOfMemory: 43 | return "Out of memory"; 44 | case kGfxResult_InternalError: 45 | return "Internal error"; 46 | case kGfxResult_DeviceError: 47 | return "Device error"; 48 | default: 49 | GFX_ASSERT(0); 50 | break; // unknown error 51 | } 52 | return "Unknown error"; 53 | } 54 | 55 | void GFX_PRINTLN_IMPL(char const *file_name, uint32_t line_number, char const *format, ...) 56 | { 57 | va_list args; 58 | va_start(args, format); 59 | GFX_ASSERT(file_name != nullptr); 60 | int32_t size = snprintf(nullptr, 0, "%s(%-4u): %s", file_name, line_number, format); 61 | std::vector body(size + 1); 62 | snprintf(body.data(), body.size(), "%s(%-4u): %s", file_name, line_number, format); 63 | size = vsnprintf(nullptr, 0, body.data(), args); 64 | std::vector message(size + 2); 65 | vsnprintf(message.data(), message.size(), body.data(), args); 66 | va_end(args); 67 | message[message.size() - 2] = '\n'; 68 | OutputDebugStringA(message.data()); 69 | puts(message.data()); 70 | } 71 | 72 | void GFX_PRINT_ERROR_IMPL(GfxResult result, char const *file_name, uint32_t line_number, char const *format, ...) 73 | { 74 | va_list args; 75 | va_start(args, format); 76 | GFX_ASSERT(file_name != nullptr); 77 | int32_t size = snprintf(nullptr, 0, "%s(%-4u): error: %s (0x%x: %s)", file_name, line_number, format, (uint32_t)result, gfxResultGetString(result)); 78 | std::vector body(size + 1); 79 | snprintf(body.data(), body.size(), "%s(%-4u): error: %s (0x%x: %s)", file_name, line_number, format, (uint32_t)result, gfxResultGetString(result)); 80 | size = vsnprintf(nullptr, 0, body.data(), args); 81 | std::vector message(size + 2); 82 | vsnprintf(message.data(), message.size(), body.data(), args); 83 | va_end(args); 84 | message[message.size() - 2] = '\n'; 85 | OutputDebugStringA(message.data()); 86 | puts(message.data()); 87 | } 88 | 89 | GfxResult GFX_SET_ERROR_IMPL(GfxResult result, char const *file_name, uint32_t line_number, char const *format, ...) 90 | { 91 | va_list args; 92 | va_start(args, format); 93 | GFX_ASSERT(file_name != nullptr); 94 | int32_t size = snprintf(nullptr, 0, "%s(%-4u): error: %s (0x%x: %s)", file_name, line_number, format, (uint32_t)result, gfxResultGetString(result)); 95 | std::vector body(size + 1); 96 | snprintf(body.data(), body.size(), "%s(%-4u): error: %s (0x%x: %s)", file_name, line_number, format, (uint32_t)result, gfxResultGetString(result)); 97 | size = vsnprintf(nullptr, 0, body.data(), args); 98 | std::vector message(size + 2); 99 | vsnprintf(message.data(), message.size(), body.data(), args); 100 | va_end(args); 101 | message[message.size() - 2] = '\n'; 102 | OutputDebugStringA(message.data()); 103 | puts(message.data()); 104 | return result; 105 | } 106 | -------------------------------------------------------------------------------- /gfx_core.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | MIT License 3 | 4 | Copyright (c) 2024 Guillaume Boissé 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | ****************************************************************************/ 24 | 25 | #ifndef GFX_INCLUDE_GFX_CORE_H 26 | #define GFX_INCLUDE_GFX_CORE_H 27 | 28 | #include // va_list, etc. 29 | #include // uint32_t, etc. 30 | #include // std::min(), std::max() 31 | 32 | //! 33 | //! Constants. 34 | //! 35 | 36 | enum GfxResult 37 | { 38 | kGfxResult_NoError = 0, 39 | kGfxResult_InvalidParameter, 40 | kGfxResult_InvalidOperation, 41 | kGfxResult_OutOfMemory, 42 | kGfxResult_InternalError, 43 | kGfxResult_DeviceError, 44 | 45 | kGfxResult_Count 46 | }; 47 | 48 | enum GfxConstant 49 | { 50 | kGfxConstant_BackBufferCount = 3, 51 | kGfxConstant_MaxRenderTarget = 8, 52 | kGfxConstant_MaxAnisotropy = 8, 53 | kGfxConstant_MaxNameLength = 64, 54 | kGfxConstant_NumBindlessSlots = 1024 55 | }; 56 | 57 | //! 58 | //! Public macros. 59 | //! 60 | 61 | #define GFX_ASSERT(X) // skip it 62 | 63 | #define GFX_ASSERTMSG(X, ...) 64 | 65 | #define GFX_MIN(X, Y) ((std::min)(X, Y)) 66 | 67 | #define GFX_MAX(X, Y) ((std::max)(X, Y)) 68 | 69 | #define GFX_SNPRINTF(BUFFER, SIZE, FORMAT, ...) \ 70 | _snprintf_s(BUFFER, SIZE, _TRUNCATE, FORMAT, __VA_ARGS__) 71 | 72 | #define GFX_NON_COPYABLE(TYPE) \ 73 | TYPE(TYPE const &) = delete; TYPE &operator =(TYPE const &) = delete 74 | 75 | #define GFX_ALIGN(VAL, ALIGN) \ 76 | (((VAL) + (static_cast(ALIGN) - 1)) & ~(static_cast(ALIGN) - 1)) 77 | 78 | #define GFX_BREAKPOINT \ 79 | GFX_MULTI_LINE_MACRO_BEGIN \ 80 | if(IsDebuggerPresent()) \ 81 | DebugBreak(); \ 82 | GFX_MULTI_LINE_MACRO_END 83 | 84 | #define GFX_TRY(X) \ 85 | GFX_MULTI_LINE_MACRO_BEGIN \ 86 | GfxResult const _res = (GfxResult)(X); \ 87 | if(_res != kGfxResult_NoError) \ 88 | return GFX_SET_ERROR(_res, "`%s' failed", GFX_STRINGIFY(X)); \ 89 | GFX_MULTI_LINE_MACRO_END 90 | 91 | #define GFX_PRINTLN(...) \ 92 | GFX_MULTI_LINE_MACRO_BEGIN \ 93 | GFX_PRINTLN_IMPL(__FILE__, __LINE__, __VA_ARGS__); \ 94 | GFX_MULTI_LINE_MACRO_END 95 | 96 | #define GFX_SET_ERROR(RESULT, ...) \ 97 | GFX_SET_ERROR_IMPL(RESULT, __FILE__, __LINE__, __VA_ARGS__) 98 | 99 | #define GFX_PRINT_ERROR(RESULT, ...) \ 100 | GFX_MULTI_LINE_MACRO_BEGIN \ 101 | GFX_PRINT_ERROR_IMPL(RESULT, __FILE__, __LINE__, __VA_ARGS__); \ 102 | GFX_MULTI_LINE_MACRO_END 103 | 104 | //! 105 | //! Debug macros. 106 | //! 107 | 108 | #ifdef _DEBUG 109 | 110 | #undef GFX_ASSERT 111 | #define GFX_ASSERT(X) \ 112 | GFX_MULTI_LINE_MACRO_BEGIN \ 113 | if(!(X)) \ 114 | { \ 115 | GFX_BREAKPOINT; \ 116 | exit(kGfxResult_InternalError); \ 117 | } \ 118 | GFX_MULTI_LINE_MACRO_END 119 | 120 | #undef GFX_ASSERTMSG 121 | #define GFX_ASSERTMSG(X, ...) \ 122 | GFX_MULTI_LINE_MACRO_BEGIN \ 123 | if(!(X)) \ 124 | { \ 125 | GFX_PRINTLN_IMPL(__FILE__, __LINE__, __VA_ARGS__); \ 126 | GFX_BREAKPOINT; \ 127 | } \ 128 | GFX_MULTI_LINE_MACRO_END 129 | 130 | #endif //! _DEBUG 131 | 132 | //! 133 | //! Internal macros. 134 | //! 135 | 136 | #define GFX_MULTI_LINE_MACRO_BEGIN \ 137 | __pragma(warning(push)) \ 138 | __pragma(warning(disable:4127)) /* conditional expression is constant */ \ 139 | __pragma(warning(disable:4390)) /* empty controlled statement found */ \ 140 | do \ 141 | { 142 | 143 | #define GFX_MULTI_LINE_MACRO_END \ 144 | } \ 145 | while(0) \ 146 | __pragma(warning(pop)) 147 | 148 | #define GFX_STRINGIFY(X) GFX_STRINGIFY2(X) 149 | 150 | #define GFX_STRINGIFY2(X) #X 151 | 152 | #define GFX_INTERNAL_HANDLE(TYPE) friend class GfxInternal; public: inline TYPE() { memset(this, 0, sizeof(*this)); } \ 153 | inline bool operator ==(TYPE const &other) const { return handle == other.handle; } \ 154 | inline bool operator !=(TYPE const &other) const { return handle != other.handle; } \ 155 | inline uint32_t getIndex() const { return (uint32_t)(handle ? handle & 0xFFFFFFFFull \ 156 | : 0xFFFFFFFFull); } \ 157 | inline operator uint32_t() const { return getIndex(); } \ 158 | inline operator bool() const { return !!handle; } \ 159 | private: uint64_t handle 160 | 161 | #define GFX_INTERNAL_NAMED_HANDLE(TYPE) friend class GfxInternal; public: inline TYPE() { memset(this, 0, sizeof(*this)); } \ 162 | inline bool operator ==(TYPE const &other) const { return handle == other.handle; } \ 163 | inline bool operator !=(TYPE const &other) const { return handle != other.handle; } \ 164 | inline uint32_t getIndex() const { return (uint32_t)(handle ? handle & 0xFFFFFFFFull \ 165 | : 0xFFFFFFFFull); } \ 166 | inline operator uint32_t() const { return getIndex(); } \ 167 | inline char const *getName() const { return name; } \ 168 | inline void setName(char const *n) { uint32_t i = 0; \ 169 | if(n) \ 170 | for(; n[i] && i < kGfxConstant_MaxNameLength; ++i) \ 171 | name[i] = n[i]; name[i] = '\0'; } \ 172 | inline operator bool() const { return !!handle; } \ 173 | private: uint64_t handle; char name[kGfxConstant_MaxNameLength + 1] 174 | 175 | char const *gfxResultGetString(GfxResult result); 176 | 177 | void GFX_PRINTLN_IMPL(char const *file_name, uint32_t line_number, char const *format, ...); 178 | 179 | void GFX_PRINT_ERROR_IMPL( 180 | GfxResult result, char const *file_name, uint32_t line_number, char const *format, ...); 181 | 182 | GfxResult GFX_SET_ERROR_IMPL( 183 | GfxResult result, char const *file_name, uint32_t line_number, char const *format, ...); 184 | 185 | #endif //! GFX_INCLUDE_GFX_CORE_H 186 | -------------------------------------------------------------------------------- /gfx_imgui.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | MIT License 3 | 4 | Copyright (c) 2024 Guillaume Boissé 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | ****************************************************************************/ 24 | #if !defined(GFX_INCLUDE_GFX_IMGUI_H) && defined(GFX_ENABLE_GUI) 25 | #define GFX_INCLUDE_GFX_IMGUI_H 26 | 27 | #ifndef IMGUI_USE_WCHAR32 28 | // Required to support unicode values above 0x10000 29 | #define IMGUI_USE_WCHAR32 30 | #endif 31 | #define IMGUI_DEFINE_MATH_OPERATORS 32 | 33 | #include "gfx.h" 34 | #include "imgui.h" 35 | 36 | //! 37 | //! ImGui initialization/termination. 38 | //! 39 | 40 | GfxResult gfxImGuiInitialize(GfxContext gfx, char const **font_filenames = nullptr, uint32_t font_count = 0, 41 | ImFontConfig const *font_configs = nullptr, ImGuiConfigFlags flags = 0); 42 | GfxResult gfxImGuiTerminate(); 43 | GfxResult gfxImGuiRender(); 44 | 45 | bool gfxImGuiIsInitialized(); 46 | 47 | #endif //! GFX_INCLUDE_GFX_IMGUI_H 48 | 49 | //! 50 | //! Implementation details. 51 | //! 52 | 53 | #ifdef GFX_IMPLEMENTATION_DEFINE 54 | 55 | #pragma once 56 | #include "gfx_imgui.cpp" 57 | 58 | #endif //! GFX_IMPLEMENTATION_DEFINE 59 | -------------------------------------------------------------------------------- /gfx_internal_types.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | MIT License 3 | 4 | Copyright (c) 2024 Guillaume Boissé 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | ****************************************************************************/ 24 | 25 | #include "gfx_core.h" 26 | #include "gfx_internal_types.h" 27 | 28 | void *gfxMalloc(size_t size) 29 | { 30 | #ifdef _MSC_VER 31 | _Notnull_ /* Silence msvc warnings about not checking allocation failure */ 32 | #endif 33 | void *ret = malloc(size); 34 | GFX_ASSERT(ret != nullptr); // out of memory 35 | return ret; 36 | } 37 | 38 | void gfxFree(void *pointer) 39 | { 40 | free(pointer); 41 | } 42 | 43 | //! 44 | //! Freelist allocator. 45 | //! 46 | 47 | GfxFreelist::GfxFreelist() 48 | : name_(nullptr) 49 | , slots_(nullptr) 50 | , slot_counts_(nullptr) 51 | , next_slot_(0xFFFFFFFFu) 52 | , size_(0) 53 | {} 54 | 55 | GfxFreelist::GfxFreelist(char const *name) 56 | : name_(name ? (char *)gfxMalloc(strlen(name) + 1) : nullptr) 57 | , slots_(nullptr) 58 | , slot_counts_(nullptr) 59 | , next_slot_(0xFFFFFFFFu) 60 | , size_(0) 61 | { 62 | if (name_) 63 | { 64 | for (uint32_t i = 0; !i || name[i - 1]; ++i) 65 | { 66 | name_[i] = name[i]; 67 | } 68 | } 69 | } 70 | 71 | GfxFreelist::~GfxFreelist() 72 | { 73 | #ifdef _DEBUG 74 | uint32_t const free_slot_count = calculate_free_slot_count(); 75 | if (free_slot_count < size_) 76 | { 77 | GFX_PRINTLN("Warning: %u %s(s) not freed properly; detected memory leak", size_ - free_slot_count, 78 | name_ ? name_ : "freelist slot"); 79 | } 80 | #endif //! _DEBUG 81 | free(name_); 82 | free(slots_); 83 | free(slot_counts_); 84 | } 85 | 86 | bool GfxFreelist::empty() const 87 | { 88 | return calculate_free_slot_count() == size_; 89 | } 90 | 91 | uint32_t GfxFreelist::allocate_slot() 92 | { 93 | if (next_slot_ == 0xFFFFFFFFu) 94 | { 95 | grow(); 96 | } 97 | GFX_ASSERT(next_slot_ != 0xFFFFFFFFu); 98 | uint32_t const slot = next_slot_; 99 | next_slot_ = slots_[next_slot_]; 100 | slots_[slot] = 0xFFFFFFFEu; 101 | slot_counts_[slot] = 1; 102 | return slot; 103 | } 104 | 105 | uint32_t GfxFreelist::allocate_slots(uint32_t slot_count) 106 | { 107 | uint32_t slot = 0xFFFFFFFFu; 108 | if (slot_count == 0) 109 | { 110 | return 0xFFFFFFFFu; 111 | } 112 | if (slot_count == 1) 113 | { 114 | return allocate_slot(); 115 | } 116 | for (uint32_t next_slot = next_slot_; next_slot != 0xFFFFFFFFu; next_slot = slots_[next_slot]) 117 | { 118 | bool is_block_available = true; 119 | if (next_slot + slot_count > size_) 120 | { 121 | continue; 122 | } 123 | for (uint32_t i = 1; i < slot_count; ++i) 124 | { 125 | if (slots_[next_slot + i] == 0xFFFFFFFEu) 126 | { 127 | is_block_available = false; 128 | break; // block isn't available 129 | } 130 | } 131 | if (is_block_available) 132 | { 133 | slot = next_slot; 134 | break; // found a suitable block 135 | } 136 | } 137 | if (slot == 0xFFFFFFFFu) 138 | { 139 | slot = size_; 140 | grow(slot_count); 141 | } 142 | GFX_ASSERT(slot != 0xFFFFFFFFu); 143 | GFX_ASSERT(slot + slot_count <= size_); 144 | uint32_t previous_slot = 0xFFFFFFFFu; 145 | uint32_t next_slot = next_slot_; 146 | next_slot_ = 0xFFFFFFFFu; 147 | for (; next_slot != 0xFFFFFFFFu;) 148 | { 149 | if (next_slot < slot || next_slot >= slot + slot_count) 150 | { 151 | if (previous_slot == 0xFFFFFFFFu) 152 | { 153 | next_slot_ = next_slot; 154 | } 155 | previous_slot = next_slot; 156 | next_slot = slots_[next_slot]; 157 | } 158 | else 159 | { 160 | if (previous_slot != 0xFFFFFFFFu) 161 | { 162 | slots_[previous_slot] = slots_[next_slot]; 163 | } 164 | uint32_t const previous_next_slot = slots_[next_slot]; 165 | slots_[next_slot] = 0xFFFFFFFEu; 166 | next_slot = previous_next_slot; 167 | } 168 | } 169 | for (uint32_t i = 0; i < slot_count; ++i) 170 | { 171 | slot_counts_[i + slot] = (i == 0 ? slot_count : 0); 172 | } 173 | return slot; 174 | } 175 | 176 | void GfxFreelist::free_slot(uint32_t slot) 177 | { 178 | GFX_ASSERT(slot < size_); 179 | if (slot >= size_) 180 | { 181 | return; 182 | } 183 | GFX_ASSERT(slots_[slot] == 0xFFFFFFFEu); 184 | if (slots_[slot] != 0xFFFFFFFEu) 185 | { 186 | return; // already freed 187 | } 188 | GFX_ASSERT(slot_counts_[slot] > 0); 189 | if (slot_counts_[slot] == 0) 190 | { 191 | return; // cannot free within a block 192 | } 193 | uint32_t i = slot + slot_counts_[slot]; 194 | while (i-- > slot) 195 | { 196 | GFX_ASSERT(i == slot || slot_counts_[i] == 0); 197 | GFX_ASSERT(slots_[i] == 0xFFFFFFFEu); 198 | slots_[i] = next_slot_; 199 | slot_counts_[i] = 0; 200 | next_slot_ = i; 201 | } 202 | } 203 | 204 | uint32_t GfxFreelist::size() const 205 | { 206 | return size_; 207 | } 208 | 209 | void GfxFreelist::clear() 210 | { 211 | free(slots_); 212 | slots_ = nullptr; 213 | free(slot_counts_); 214 | slot_counts_ = nullptr; 215 | next_slot_ = 0xFFFFFFFFu; 216 | size_ = 0; 217 | } 218 | 219 | uint32_t GfxFreelist::calculate_free_slot_count() const 220 | { 221 | uint32_t free_slot_count = 0; 222 | for (uint32_t next_slot = next_slot_; next_slot != 0xFFFFFFFFu; next_slot = slots_[next_slot]) 223 | { 224 | ++free_slot_count; // found an available slot 225 | } 226 | return free_slot_count; 227 | } 228 | 229 | void GfxFreelist::grow(uint32_t slot_count) 230 | { 231 | uint32_t size = size_ + slot_count; 232 | size += ((size + 2) >> 1); // grow by half capacity 233 | uint32_t *slots = (uint32_t *)gfxMalloc(size * sizeof(uint32_t)); 234 | uint32_t *slot_counts = (uint32_t *)gfxMalloc(size * sizeof(uint32_t)); 235 | for (uint32_t i = 0; i < size; ++i) 236 | { 237 | if (i < size_) 238 | { 239 | slots[i] = (slots_[i] != 0xFFFFFFFFu ? slots_[i] : size_); 240 | slot_counts[i] = slot_counts_[i]; 241 | } 242 | else 243 | { 244 | slots[i] = (i + 1 < size ? i + 1 : 0xFFFFFFFFu); 245 | slot_counts[i] = 0; 246 | } 247 | } 248 | next_slot_ = (next_slot_ != 0xFFFFFFFFu ? next_slot_ : size_); 249 | free(slot_counts_); 250 | slot_counts_ = slot_counts; 251 | free(slots_); 252 | slots_ = slots; 253 | size_ = size; 254 | } 255 | 256 | //! 257 | //! Handle dispenser. 258 | //! 259 | 260 | GfxHandles::GfxHandles() 261 | : name_(nullptr) 262 | , handles_(nullptr) 263 | , next_handle_(0xFFFFFFFFu) 264 | , capacity_(0) 265 | {} 266 | 267 | GfxHandles::GfxHandles(char const *name) 268 | : name_(name ? (char *)gfxMalloc(strlen(name) + 1) : nullptr) 269 | , handles_(nullptr) 270 | , next_handle_(0xFFFFFFFFu) 271 | , capacity_(0) 272 | { 273 | if (name_) 274 | { 275 | for (uint32_t i = 0; !i || name[i - 1]; ++i) 276 | { 277 | name_[i] = name[i]; 278 | } 279 | } 280 | } 281 | 282 | GfxHandles::~GfxHandles() 283 | { 284 | #ifdef _DEBUG 285 | uint32_t const free_handle_count = calculate_free_handle_count(); 286 | if (free_handle_count < capacity_) 287 | { 288 | GFX_PRINTLN("Warning: %u %s(s) not freed properly; detected memory leak", 289 | capacity_ - free_handle_count, name_ ? name_ : "handle"); 290 | } 291 | #endif //! _DEBUG 292 | free(name_); 293 | free(handles_); 294 | } 295 | 296 | bool GfxHandles::empty() const 297 | { 298 | return calculate_free_handle_count() == capacity_; 299 | } 300 | 301 | uint64_t GfxHandles::allocate_handle() 302 | { 303 | if (next_handle_ == 0xFFFFFFFFu) 304 | { 305 | grow(); 306 | } 307 | uint64_t const next_handle = handles_[next_handle_]; 308 | uint64_t const handle = ((next_handle >> 32) << 32) | static_cast(next_handle_); 309 | next_handle_ = static_cast(next_handle & 0xFFFFFFFFull); 310 | GFX_ASSERT(handle != 0); // should never happen 311 | return handle; 312 | } 313 | 314 | bool GfxHandles::acquire_handle(uint64_t handle) 315 | { 316 | if (!handle || !(handle >> 32)) 317 | { 318 | return false; // invalid handle 319 | } 320 | uint32_t const target_handle = static_cast(handle & 0xFFFFFFFFull); 321 | if (target_handle >= capacity_) 322 | { 323 | grow(target_handle - capacity_ + 1); // grow our capacity 324 | } 325 | for (uint32_t previous_handle = 0xFFFFFFFFu, next_handle = next_handle_; next_handle != 0xFFFFFFFFu; 326 | previous_handle = next_handle, 327 | next_handle = static_cast(handles_[next_handle] & 0xFFFFFFFFull)) 328 | { 329 | if (next_handle == target_handle) 330 | { 331 | uint32_t const free_handle = static_cast(handles_[next_handle] & 0xFFFFFFFFull); 332 | handles_[next_handle] = ((handle >> 32) << 32) | static_cast(free_handle); 333 | if (previous_handle == 0xFFFFFFFFu) 334 | { 335 | next_handle_ = free_handle; 336 | } 337 | else 338 | { 339 | handles_[previous_handle] = 340 | ((handles_[previous_handle] >> 32) << 32) | static_cast(free_handle); 341 | } 342 | return true; 343 | } 344 | } 345 | return false; 346 | } 347 | 348 | uint64_t GfxHandles::get_handle(uint32_t index) const 349 | { 350 | GFX_ASSERT(index < capacity_); 351 | if (index >= capacity_) 352 | { 353 | return 0; 354 | } 355 | uint32_t const handle_age = static_cast(handles_[index] >> 32); 356 | return (static_cast(handle_age) << 32) | static_cast(index); 357 | } 358 | 359 | bool GfxHandles::has_handle(uint64_t handle) const 360 | { 361 | if (!handle) 362 | { 363 | return false; // invalid handle 364 | } 365 | uint32_t const next_handle = static_cast(handle & 0xFFFFFFFFull); 366 | GFX_ASSERT(next_handle < capacity_); 367 | if (next_handle >= capacity_) 368 | { 369 | return false; 370 | } 371 | uint32_t const handle_age = static_cast(handles_[next_handle] >> 32); 372 | return handle_age == static_cast(handle >> 32); 373 | } 374 | 375 | bool GfxHandles::free_handle(uint64_t handle) 376 | { 377 | if (!handle) 378 | { 379 | return false; // invalid handle 380 | } 381 | uint32_t const next_handle = static_cast(handle & 0xFFFFFFFFull); 382 | GFX_ASSERT(next_handle < capacity_); 383 | if (next_handle >= capacity_) 384 | { 385 | return false; 386 | } 387 | uint32_t handle_age = static_cast(handles_[next_handle] >> 32); 388 | if (handle_age != static_cast(handle >> 32)) 389 | { 390 | return false; 391 | } 392 | handle_age = (handle_age < 0xFFFFFFFFu ? handle_age + 1 : 1); 393 | handles_[next_handle] = (static_cast(handle_age) << 32) | static_cast(next_handle_); 394 | next_handle_ = next_handle; // insert back into freelist 395 | return true; 396 | } 397 | 398 | uint32_t GfxHandles::calculate_free_handle_count() const 399 | { 400 | uint32_t free_handle_count = 0; 401 | for (uint32_t next_handle = next_handle_; next_handle != 0xFFFFFFFFu; 402 | next_handle = static_cast(handles_[next_handle] & 0xFFFFFFFFull)) 403 | { 404 | ++free_handle_count; // found an available handle 405 | } 406 | return free_handle_count; 407 | } 408 | 409 | void GfxHandles::grow(uint32_t handle_count) 410 | { 411 | uint32_t previous_handle = 0xFFFFFFFFu; 412 | uint32_t capacity = capacity_ + handle_count; 413 | capacity += ((capacity + 2) >> 1); // grow by half capacity 414 | uint64_t *handles = (uint64_t *)gfxMalloc(capacity * sizeof(uint64_t)); 415 | memcpy(handles, handles_, capacity_ * sizeof(uint64_t)); 416 | for (uint32_t i = capacity_; i < capacity; ++i) 417 | { 418 | handles[i] = (1ull << 32) | static_cast(i + 1 < capacity ? i + 1 : 0xFFFFFFFFu); 419 | } 420 | for (uint32_t next_handle = next_handle_; next_handle != 0xFFFFFFFFu; 421 | next_handle = static_cast(handles[next_handle] & 0xFFFFFFFFull)) 422 | { 423 | previous_handle = next_handle; 424 | } 425 | if (previous_handle == 0xFFFFFFFFu) 426 | { 427 | next_handle_ = capacity_; 428 | } 429 | else 430 | { 431 | GFX_ASSERT(previous_handle < capacity); 432 | handles[previous_handle] = ((handles[previous_handle] >> 32) << 32) | capacity_; 433 | } 434 | free(handles_); 435 | handles_ = handles; 436 | capacity_ = capacity; 437 | } 438 | -------------------------------------------------------------------------------- /gfx_internal_types.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | MIT License 3 | 4 | Copyright (c) 2024 Guillaume Boissé 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | ****************************************************************************/ 24 | 25 | #ifndef GFX_INCLUDE_GFX_TYPES_H 26 | #define GFX_INCLUDE_GFX_TYPES_H 27 | 28 | #include "gfx_core.h" 29 | 30 | #include // std::vector 31 | #define WIN32_LEAN_AND_MEAN 32 | #include 33 | 34 | void *gfxMalloc(size_t size); 35 | 36 | void gfxFree(void *pointer); 37 | 38 | //! 39 | //! Sparse array container. 40 | //! 41 | 42 | template 43 | class GfxArray 44 | { 45 | GFX_NON_COPYABLE(GfxArray); 46 | 47 | public: 48 | GfxArray(); 49 | ~GfxArray(); 50 | 51 | TYPE *at(uint32_t index); 52 | TYPE &operator[](uint32_t index); 53 | TYPE const *at(uint32_t index) const; 54 | TYPE const &operator[](uint32_t index) const; 55 | bool has(uint32_t index) const; 56 | bool empty() const; 57 | 58 | TYPE &insert(uint32_t index); 59 | TYPE &insert(uint32_t index, TYPE const &object); 60 | bool erase(uint32_t index); 61 | void clear(); 62 | 63 | TYPE *data(); 64 | uint32_t size() const; 65 | TYPE const *data() const; 66 | uint32_t capacity() const; 67 | 68 | uint32_t get_index(uint32_t packed_index) const; 69 | uint32_t get_packed_index(uint32_t index) const; 70 | 71 | protected: 72 | void reserve(uint32_t capacity); 73 | 74 | TYPE *data_; 75 | uint32_t size_; 76 | uint32_t capacity_; 77 | uint32_t *indices_; 78 | uint32_t *packed_indices_; 79 | }; 80 | 81 | template 82 | GfxArray::GfxArray() 83 | : data_(nullptr) 84 | , size_(0) 85 | , capacity_(0) 86 | , indices_(nullptr) 87 | , packed_indices_(nullptr) 88 | {} 89 | 90 | template 91 | GfxArray::~GfxArray() 92 | { 93 | for (uint32_t i = 0; i < size_; ++i) 94 | { 95 | data_[i].~TYPE(); 96 | } 97 | gfxFree(data_); 98 | gfxFree(indices_); 99 | gfxFree(packed_indices_); 100 | } 101 | 102 | template 103 | TYPE *GfxArray::at(uint32_t index) 104 | { 105 | if (index >= capacity_) 106 | { 107 | return nullptr; // out of bounds 108 | } 109 | uint32_t const packed_index = packed_indices_[index]; 110 | if (packed_index == 0xFFFFFFFFu) 111 | { 112 | return nullptr; // not found 113 | } 114 | return &data_[packed_index]; 115 | } 116 | 117 | template 118 | TYPE &GfxArray::operator[](uint32_t index) 119 | { 120 | TYPE *object = at(index); 121 | GFX_ASSERT(object != nullptr); 122 | return *object; 123 | } 124 | 125 | template 126 | TYPE const *GfxArray::at(uint32_t index) const 127 | { 128 | if (index >= capacity_) 129 | { 130 | return nullptr; // out of bounds 131 | } 132 | uint32_t const packed_index = packed_indices_[index]; 133 | if (packed_index == 0xFFFFFFFFu) 134 | { 135 | return nullptr; // not found 136 | } 137 | return &data_[packed_index]; 138 | } 139 | 140 | template 141 | TYPE const &GfxArray::operator[](uint32_t index) const 142 | { 143 | TYPE const *object = at(index); 144 | GFX_ASSERT(object != nullptr); 145 | return *object; 146 | } 147 | 148 | template 149 | bool GfxArray::has(uint32_t index) const 150 | { 151 | if (index >= capacity_) 152 | { 153 | return false; // out of bounds 154 | } 155 | return packed_indices_[index] != 0xFFFFFFFFu; 156 | } 157 | 158 | template 159 | bool GfxArray::empty() const 160 | { 161 | return size_ == 0; 162 | } 163 | 164 | template 165 | TYPE &GfxArray::insert(uint32_t index) 166 | { 167 | GFX_ASSERT(index < 0xFFFFFFFFu); 168 | if (index >= capacity_) 169 | { 170 | reserve(index + 1); 171 | } 172 | uint32_t const packed_index = packed_indices_[index]; 173 | if (packed_index != 0xFFFFFFFFu) 174 | { 175 | data_[packed_index].~TYPE(); 176 | return *new (&data_[packed_index]) TYPE(); 177 | } 178 | GFX_ASSERT(size_ < capacity_); 179 | indices_[size_] = index; 180 | packed_indices_[index] = size_; 181 | return *new (&data_[size_++]) TYPE(); 182 | } 183 | 184 | template 185 | TYPE &GfxArray::insert(uint32_t index, TYPE const &object) 186 | { 187 | GFX_ASSERT(index < 0xFFFFFFFFu); 188 | if (index >= capacity_) 189 | { 190 | reserve(index + 1); 191 | } 192 | uint32_t const packed_index = packed_indices_[index]; 193 | if (packed_index != 0xFFFFFFFFu) 194 | { 195 | data_[packed_index].~TYPE(); 196 | return *new (&data_[packed_index]) TYPE(object); 197 | } 198 | GFX_ASSERT(size_ < capacity_); 199 | indices_[size_] = index; 200 | packed_indices_[index] = size_; 201 | return *new (&data_[size_++]) TYPE(object); 202 | } 203 | 204 | template 205 | bool GfxArray::erase(uint32_t index) 206 | { 207 | GFX_ASSERT(index < capacity_); 208 | uint32_t const packed_index = packed_indices_[index]; 209 | if (packed_index == 0xFFFFFFFFu) 210 | { 211 | return false; 212 | } 213 | GFX_ASSERT(size_ > 0); // should never happen 214 | if (packed_index != size_ - 1) 215 | { 216 | std::swap(data_[packed_index], data_[size_ - 1]); 217 | indices_[packed_index] = indices_[size_ - 1]; 218 | packed_indices_[indices_[packed_index]] = packed_index; 219 | } 220 | data_[--size_].~TYPE(); 221 | packed_indices_[index] = 0xFFFFFFFFu; 222 | indices_[size_] = 0; 223 | return true; 224 | } 225 | 226 | template 227 | void GfxArray::clear() 228 | { 229 | for (uint32_t i = 0; i < size_; ++i) 230 | { 231 | packed_indices_[indices_[i]] = 0xFFFFFFFFu; 232 | indices_[i] = 0; 233 | data_[i].~TYPE(); 234 | } 235 | size_ = 0; 236 | } 237 | 238 | template 239 | TYPE *GfxArray::data() 240 | { 241 | return data_; 242 | } 243 | 244 | template 245 | uint32_t GfxArray::size() const 246 | { 247 | return size_; 248 | } 249 | 250 | template 251 | TYPE const *GfxArray::data() const 252 | { 253 | return data_; 254 | } 255 | 256 | template 257 | uint32_t GfxArray::capacity() const 258 | { 259 | return capacity_; 260 | } 261 | 262 | template 263 | uint32_t GfxArray::get_index(uint32_t packed_index) const 264 | { 265 | GFX_ASSERT(packed_index < size_); 266 | return indices_[packed_index]; 267 | } 268 | 269 | template 270 | uint32_t GfxArray::get_packed_index(uint32_t index) const 271 | { 272 | GFX_ASSERT(index < capacity_); 273 | return packed_indices_[index]; 274 | } 275 | 276 | template 277 | void GfxArray::reserve(uint32_t capacity) 278 | { 279 | uint32_t const new_capacity = GFX_MAX(capacity_ + ((capacity_ + 2) >> 1), capacity); 280 | TYPE *data = (TYPE *)gfxMalloc(new_capacity * sizeof(TYPE)); 281 | uint32_t *indices = (uint32_t *)gfxMalloc(new_capacity * sizeof(uint32_t)); 282 | uint32_t *packed_indices = (uint32_t *)gfxMalloc(new_capacity * sizeof(uint32_t)); 283 | for (uint32_t i = 0; i < capacity_; ++i) 284 | { 285 | if (i < size_) 286 | { 287 | new (&data[i]) TYPE(std::move(data_[i])); 288 | data_[i].~TYPE(); 289 | } 290 | indices[i] = indices_[i]; 291 | packed_indices[i] = packed_indices_[i]; 292 | } 293 | for (uint32_t i = capacity_; i < new_capacity; ++i) 294 | { 295 | indices[i] = 0; 296 | packed_indices[i] = 0xFFFFFFFFu; 297 | } 298 | gfxFree(data_); 299 | gfxFree(indices_); 300 | gfxFree(packed_indices_); 301 | data_ = data; 302 | indices_ = indices; 303 | packed_indices_ = packed_indices; 304 | capacity_ = new_capacity; 305 | } 306 | 307 | //! 308 | //! Freelist allocator. 309 | //! 310 | 311 | class GfxFreelist 312 | { 313 | GFX_NON_COPYABLE(GfxFreelist); 314 | 315 | public: 316 | GfxFreelist(); 317 | explicit GfxFreelist(char const *name); 318 | ~GfxFreelist(); 319 | 320 | bool empty() const; 321 | 322 | uint32_t allocate_slot(); 323 | uint32_t allocate_slots(uint32_t slot_count); 324 | void free_slot(uint32_t slot); 325 | uint32_t size() const; 326 | void clear(); 327 | 328 | uint32_t calculate_free_slot_count() const; 329 | 330 | protected: 331 | void grow(uint32_t slot_count = 1); 332 | 333 | char *name_; 334 | uint32_t *slots_; 335 | uint32_t *slot_counts_; 336 | uint32_t next_slot_; 337 | uint32_t size_; 338 | }; 339 | 340 | //! 341 | //! Handle dispenser. 342 | //! 343 | 344 | class GfxHandles 345 | { 346 | GFX_NON_COPYABLE(GfxHandles); 347 | 348 | public: 349 | GfxHandles(); 350 | explicit GfxHandles(char const *name); 351 | ~GfxHandles(); 352 | 353 | bool empty() const; 354 | 355 | uint64_t allocate_handle(); 356 | bool acquire_handle(uint64_t handle); 357 | uint64_t get_handle(uint32_t index) const; 358 | bool has_handle(uint64_t handle) const; 359 | bool free_handle(uint64_t handle); 360 | 361 | uint32_t calculate_free_handle_count() const; 362 | 363 | protected: 364 | void grow(uint32_t handle_count = 1); 365 | 366 | char *name_; 367 | uint64_t *handles_; 368 | uint32_t next_handle_; 369 | uint32_t capacity_; 370 | }; 371 | 372 | #endif //! GFX_INCLUDE_GFX_TYPES_H 373 | -------------------------------------------------------------------------------- /gfx_window.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | MIT License 3 | 4 | Copyright (c) 2024 Guillaume Boissé 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | ****************************************************************************/ 24 | 25 | #include "gfx_window.h" 26 | 27 | # ifdef GFX_ENABLE_GUI 28 | #include "gfx_imgui.h" // for (optional) ImGui integration 29 | # ifdef GFX_IMGUI_SOURCE 30 | #include "backends/imgui_impl_win32.h" 31 | # else 32 | #include "imgui_impl_win32.h" 33 | # endif 34 | extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); 35 | # endif 36 | 37 | class GfxWindowInternal 38 | { 39 | GFX_NON_COPYABLE(GfxWindowInternal); 40 | 41 | HWND window_ = {}; 42 | bool is_minimized_ = false; 43 | bool is_maximized_ = false; 44 | bool is_close_requested_ = false; 45 | bool is_key_down_[VK_OEM_CLEAR] = {}; 46 | bool is_previous_key_down_[VK_OEM_CLEAR] = {}; 47 | void (*drop_callback_)(char const *, uint32_t, void *) = nullptr; 48 | void *callback_data_ = nullptr; 49 | 50 | public: 51 | GfxWindowInternal(GfxWindow &window) { window.handle = reinterpret_cast(this); } 52 | ~GfxWindowInternal() { terminate(); } 53 | 54 | GfxResult initialize(GfxWindow &window, uint32_t window_width, uint32_t window_height, char const *window_title, GfxCreateWindowFlags flags) 55 | { 56 | window_title = (!window_title ? "gfx" : window_title); 57 | 58 | WNDCLASSEX 59 | window_class = {}; 60 | window_class.cbSize = sizeof(window_class); 61 | window_class.style = CS_HREDRAW | CS_VREDRAW; 62 | window_class.lpfnWndProc = WindowProc; 63 | window_class.hInstance = GetModuleHandle(nullptr); 64 | window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); 65 | window_class.lpszClassName = window_title; 66 | 67 | RegisterClassEx(&window_class); 68 | 69 | RECT window_rect = { 0, 0, (LONG)window_width, (LONG)window_height }; 70 | 71 | DWORD const window_style = WS_OVERLAPPEDWINDOW & ~((flags & kGfxCreateWindowFlag_NoResizeWindow) != 0 ? 72 | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX : 0); 73 | 74 | AdjustWindowRect(&window_rect, window_style, FALSE); 75 | 76 | window_ = CreateWindowEx((flags & kGfxCreateWindowFlag_AcceptDrop) != 0 ? WS_EX_ACCEPTFILES : 0, 77 | window_title, 78 | window_title, 79 | window_style, 80 | CW_USEDEFAULT, 81 | CW_USEDEFAULT, 82 | window_rect.right - window_rect.left, 83 | window_rect.bottom - window_rect.top, 84 | nullptr, 85 | nullptr, 86 | GetModuleHandle(nullptr), 87 | nullptr); 88 | 89 | if((flags & kGfxCreateWindowFlag_FullscreenWindow) != 0) 90 | { 91 | WINDOWPLACEMENT g_wpPrev; 92 | memset(&g_wpPrev, 0, sizeof(g_wpPrev)); 93 | g_wpPrev.length = sizeof(g_wpPrev); 94 | DWORD dwStyle = GetWindowLong(window_, GWL_STYLE); 95 | 96 | if((dwStyle & WS_OVERLAPPEDWINDOW) != 0) 97 | { 98 | MONITORINFO mi; 99 | memset(&mi, 0, sizeof(mi)); 100 | mi.cbSize = sizeof(mi); 101 | if(GetWindowPlacement(window_, &g_wpPrev) && 102 | GetMonitorInfo(MonitorFromWindow(window_, MONITOR_DEFAULTTOPRIMARY), &mi)) 103 | { 104 | SetWindowLong(window_, GWL_STYLE, dwStyle & ~WS_OVERLAPPEDWINDOW); 105 | SetWindowPos(window_, HWND_TOP, mi.rcMonitor.left, mi.rcMonitor.top, 106 | mi.rcMonitor.right - mi.rcMonitor.left, mi.rcMonitor.bottom - mi.rcMonitor.top, 107 | SWP_NOOWNERZORDER | SWP_FRAMECHANGED); 108 | } 109 | } 110 | else 111 | { 112 | SetWindowLong(window_, GWL_STYLE, dwStyle | WS_OVERLAPPEDWINDOW); 113 | SetWindowPlacement(window_, &g_wpPrev); 114 | SetWindowPos(window_, NULL, 0, 0, 0, 0, 115 | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_FRAMECHANGED); 116 | } 117 | } 118 | 119 | SetWindowLongPtrA(window_, GWLP_USERDATA, (LONG_PTR)this); 120 | 121 | ShowWindow(window_, (flags & kGfxCreateWindowFlag_MaximizeWindow) != 0 ? SW_SHOWMAXIMIZED : 122 | (flags & kGfxCreateWindowFlag_HideWindow ) != 0 ? SW_HIDE : 123 | SW_SHOWDEFAULT); 124 | 125 | window.hwnd = window_; 126 | 127 | return kGfxResult_NoError; 128 | } 129 | 130 | GfxResult terminate() 131 | { 132 | if(window_) 133 | DestroyWindow(window_); 134 | # ifdef GFX_ENABLE_GUI 135 | if(ImGui::GetCurrentContext() != nullptr && ImGui::GetIO().BackendPlatformUserData != nullptr) 136 | ImGui_ImplWin32_Shutdown(); 137 | # endif 138 | 139 | return kGfxResult_NoError; 140 | } 141 | 142 | GfxResult pumpEvents() 143 | { 144 | MSG msg = {}; 145 | memcpy(is_previous_key_down_, is_key_down_, sizeof(is_key_down_)); 146 | while(PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) 147 | { 148 | TranslateMessage(&msg); 149 | DispatchMessage(&msg); 150 | } 151 | return kGfxResult_NoError; 152 | } 153 | 154 | inline bool getIsKeyDown(uint32_t key_code) const 155 | { 156 | return (key_code < ARRAYSIZE(is_key_down_) ? is_key_down_[key_code] : false); 157 | } 158 | 159 | inline bool getIsKeyPressed(uint32_t key_code) const 160 | { 161 | return (key_code < ARRAYSIZE(is_key_down_) ? is_key_down_[key_code] && !is_previous_key_down_[key_code] : false); 162 | } 163 | 164 | inline bool getIsKeyReleased(uint32_t key_code) const 165 | { 166 | return (key_code < ARRAYSIZE(is_key_down_) ? !is_key_down_[key_code] && is_previous_key_down_[key_code] : false); 167 | } 168 | 169 | inline bool getIsCloseRequested() const 170 | { 171 | return is_close_requested_; 172 | } 173 | 174 | inline bool getIsMinimized() const 175 | { 176 | return is_minimized_; 177 | } 178 | 179 | inline bool getIsMaximized() const 180 | { 181 | return is_maximized_; 182 | } 183 | 184 | inline void registerDropCallback(void (*callback)(char const *, uint32_t, void *), void *data) 185 | { 186 | drop_callback_ = callback; 187 | callback_data_ = data; 188 | } 189 | 190 | inline void unregisterDropCallback() 191 | { 192 | drop_callback_ = nullptr; 193 | callback_data_ = nullptr; 194 | } 195 | 196 | static inline GfxWindowInternal *GetGfxWindow(GfxWindow window) { return reinterpret_cast(window.handle); } 197 | 198 | private: 199 | inline void updateKeyBinding(uint32_t message, uint32_t key_code) 200 | { 201 | bool is_down; 202 | switch(message) 203 | { 204 | case WM_KEYUP: case WM_SYSKEYUP: 205 | is_down = false; 206 | break; 207 | case WM_KEYDOWN: case WM_SYSKEYDOWN: 208 | is_down = true; 209 | break; 210 | default: 211 | return; 212 | } 213 | is_key_down_[key_code] = is_down; 214 | } 215 | 216 | inline void resetKeyBindings() 217 | { 218 | for(uint32_t i = 0; i < ARRAYSIZE(is_key_down_); ++i) 219 | is_key_down_[i] = false; 220 | } 221 | 222 | static LRESULT CALLBACK WindowProc(HWND window, UINT message, WPARAM w_param, LPARAM l_param) 223 | { 224 | GfxWindowInternal *gfx_window = (GfxWindowInternal *)GetWindowLongPtrA(window, GWLP_USERDATA); 225 | if(gfx_window != nullptr) 226 | { 227 | # ifdef GFX_ENABLE_GUI 228 | if(ImGui::GetCurrentContext() != nullptr && ImGui::GetIO().BackendPlatformUserData == nullptr && gfxImGuiIsInitialized()) 229 | ImGui_ImplWin32_Init(gfx_window->window_); 230 | # endif 231 | switch(message) 232 | { 233 | case WM_SIZE: 234 | { 235 | gfx_window->is_minimized_ = IsIconic(gfx_window->window_); 236 | gfx_window->is_maximized_ = IsZoomed(gfx_window->window_); 237 | } 238 | break; 239 | case WM_DESTROY: 240 | { 241 | gfx_window->is_close_requested_ = true; 242 | } 243 | return 0; 244 | case WM_KILLFOCUS: 245 | { 246 | gfx_window->resetKeyBindings(); 247 | } 248 | break; 249 | case WM_DROPFILES: 250 | { 251 | HDROP hdrop = (HDROP)w_param; 252 | if(gfx_window->drop_callback_ != nullptr) 253 | { 254 | char file_name[MAX_PATH]; 255 | // Get the number of files dropped onto window 256 | uint32_t const file_count = DragQueryFileA(hdrop, 0xFFFFFFFFu, file_name, MAX_PATH); 257 | for(uint32_t i = 0; i < file_count; i++) 258 | { 259 | // Get i'th filename 260 | DragQueryFileA(hdrop, i, file_name, MAX_PATH); 261 | 262 | // Pass to callback function 263 | (*gfx_window->drop_callback_)(file_name, i, gfx_window->callback_data_); 264 | } 265 | } 266 | DragFinish(hdrop); 267 | } 268 | break; 269 | case WM_CHAR: 270 | case WM_SETCURSOR: 271 | case WM_DEVICECHANGE: 272 | case WM_KEYUP: case WM_SYSKEYUP: 273 | case WM_KEYDOWN: case WM_SYSKEYDOWN: 274 | case WM_LBUTTONUP: case WM_RBUTTONUP: 275 | case WM_MBUTTONUP: case WM_XBUTTONUP: 276 | case WM_MOUSEWHEEL: case WM_MOUSEHWHEEL: 277 | case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK: 278 | case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK: 279 | case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK: 280 | case WM_XBUTTONDOWN: case WM_XBUTTONDBLCLK: 281 | if(w_param < ARRAYSIZE(is_key_down_)) 282 | gfx_window->updateKeyBinding(message, (uint32_t)w_param); 283 | # ifdef GFX_ENABLE_GUI 284 | if(ImGui::GetCurrentContext() != nullptr && ImGui::GetIO().BackendPlatformUserData != nullptr) 285 | ImGui_ImplWin32_WndProcHandler(gfx_window->window_, message, w_param, l_param); 286 | # endif 287 | break; 288 | default: 289 | break; 290 | } 291 | } 292 | return DefWindowProc(window, message, w_param, l_param); 293 | } 294 | }; 295 | 296 | GfxWindow gfxCreateWindow(uint32_t window_width, uint32_t window_height, char const *window_title, GfxCreateWindowFlags flags) 297 | { 298 | GfxResult result; 299 | GfxWindow window = {}; 300 | GfxWindowInternal *gfx_window = new GfxWindowInternal(window); 301 | if(!gfx_window) return window; // out of memory 302 | result = gfx_window->initialize(window, window_width, window_height, window_title, flags); 303 | if(result != kGfxResult_NoError) 304 | { 305 | window = {}; 306 | delete gfx_window; 307 | GFX_PRINT_ERROR(result, "Failed to create new window"); 308 | } 309 | return window; 310 | } 311 | 312 | GfxResult gfxDestroyWindow(GfxWindow window) 313 | { 314 | delete GfxWindowInternal::GetGfxWindow(window); 315 | return kGfxResult_NoError; 316 | } 317 | 318 | GfxResult gfxWindowPumpEvents(GfxWindow window) 319 | { 320 | GfxWindowInternal *gfx_window = GfxWindowInternal::GetGfxWindow(window); 321 | if(!gfx_window) return kGfxResult_InvalidParameter; 322 | return gfx_window->pumpEvents(); 323 | } 324 | 325 | bool gfxWindowIsKeyDown(GfxWindow window, uint32_t key_code) 326 | { 327 | GfxWindowInternal *gfx_window = GfxWindowInternal::GetGfxWindow(window); 328 | if(!gfx_window) return false; // invalid window handle 329 | return gfx_window->getIsKeyDown(key_code); 330 | } 331 | 332 | bool gfxWindowIsKeyPressed(GfxWindow window, uint32_t key_code) 333 | { 334 | GfxWindowInternal *gfx_window = GfxWindowInternal::GetGfxWindow(window); 335 | if(!gfx_window) return false; // invalid window handle 336 | return gfx_window->getIsKeyPressed(key_code); 337 | } 338 | 339 | bool gfxWindowIsKeyReleased(GfxWindow window, uint32_t key_code) 340 | { 341 | GfxWindowInternal *gfx_window = GfxWindowInternal::GetGfxWindow(window); 342 | if(!gfx_window) return false; // invalid window handle 343 | return gfx_window->getIsKeyReleased(key_code); 344 | } 345 | 346 | bool gfxWindowIsCloseRequested(GfxWindow window) 347 | { 348 | GfxWindowInternal *gfx_window = GfxWindowInternal::GetGfxWindow(window); 349 | if(!gfx_window) return false; // invalid window handle 350 | return gfx_window->getIsCloseRequested(); 351 | } 352 | 353 | bool gfxWindowIsMinimized(GfxWindow window) 354 | { 355 | GfxWindowInternal *gfx_window = GfxWindowInternal::GetGfxWindow(window); 356 | if(!gfx_window) return false; // invalid window handle 357 | return gfx_window->getIsMinimized(); 358 | } 359 | 360 | bool gfxWindowIsMaximized(GfxWindow window) 361 | { 362 | GfxWindowInternal *gfx_window = GfxWindowInternal::GetGfxWindow(window); 363 | if(!gfx_window) return false; // invalid window handle 364 | return gfx_window->getIsMaximized(); 365 | } 366 | 367 | bool gfxWindowRegisterDropCallback(GfxWindow window, void (*callback)(char const *, uint32_t, void *), void *data) 368 | { 369 | GfxWindowInternal *gfx_window = GfxWindowInternal::GetGfxWindow(window); 370 | if(!gfx_window) return false; // invalid window handle 371 | gfx_window->registerDropCallback(callback, data); 372 | return true; 373 | } 374 | 375 | bool gfxWindowUnregisterDropCallback(GfxWindow window) 376 | { 377 | GfxWindowInternal *gfx_window = GfxWindowInternal::GetGfxWindow(window); 378 | if(!gfx_window) return false; // invalid window handle 379 | gfx_window->unregisterDropCallback(); 380 | return true; 381 | } 382 | -------------------------------------------------------------------------------- /gfx_window.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | MIT License 3 | 4 | Copyright (c) 2024 Guillaume Boissé 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | ****************************************************************************/ 24 | #ifndef GFX_INCLUDE_GFX_WINDOW_H 25 | #define GFX_INCLUDE_GFX_WINDOW_H 26 | 27 | #include "gfx.h" 28 | 29 | //! 30 | //! Window management. 31 | //! 32 | 33 | class GfxWindow { friend class GfxWindowInternal; uint64_t handle; HWND hwnd; public: 34 | inline bool operator ==(GfxWindow const &other) const { return handle == other.handle; } 35 | inline bool operator !=(GfxWindow const &other) const { return handle != other.handle; } 36 | inline GfxWindow() { memset(this, 0, sizeof(*this)); } 37 | inline operator bool() const { return !!handle; } 38 | inline HWND getHWND() const { return hwnd; } 39 | inline operator HWND() const { return hwnd; } }; 40 | 41 | enum GfxCreateWindowFlag 42 | { 43 | kGfxCreateWindowFlag_MaximizeWindow = 1 << 0, 44 | kGfxCreateWindowFlag_NoResizeWindow = 1 << 1, 45 | kGfxCreateWindowFlag_HideWindow = 1 << 2, 46 | kGfxCreateWindowFlag_FullscreenWindow = 1 << 3, 47 | kGfxCreateWindowFlag_AcceptDrop = 1 << 4 48 | }; 49 | typedef uint32_t GfxCreateWindowFlags; 50 | 51 | GfxWindow gfxCreateWindow(uint32_t window_width, uint32_t window_height, char const *window_title = nullptr, GfxCreateWindowFlags flags = 0); 52 | GfxResult gfxDestroyWindow(GfxWindow window); 53 | 54 | GfxResult gfxWindowPumpEvents(GfxWindow window); 55 | 56 | bool gfxWindowIsKeyDown(GfxWindow window, uint32_t key_code); // https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes 57 | bool gfxWindowIsKeyPressed(GfxWindow window, uint32_t key_code); 58 | bool gfxWindowIsKeyReleased(GfxWindow window, uint32_t key_code); 59 | bool gfxWindowIsCloseRequested(GfxWindow window); 60 | bool gfxWindowIsMinimized(GfxWindow window); 61 | bool gfxWindowIsMaximized(GfxWindow window); 62 | bool gfxWindowRegisterDropCallback(GfxWindow window, void (*callback)(char const *, uint32_t, void *), void *data = nullptr); 63 | bool gfxWindowUnregisterDropCallback(GfxWindow window); 64 | 65 | #endif //! GFX_INCLUDE_GFX_WINDOW_H 66 | 67 | //! 68 | //! Implementation details. 69 | //! 70 | 71 | #ifdef GFX_IMPLEMENTATION_DEFINE 72 | 73 | #pragma once 74 | #include "gfx_window.cpp" 75 | 76 | #endif //! GFX_IMPLEMENTATION_DEFINE 77 | -------------------------------------------------------------------------------- /third_party/.gitignore: -------------------------------------------------------------------------------- 1 | /imgui/ 2 | /stb/ 3 | /KTX-Software/ 4 | /Vulkan-Headers/ 5 | /glm/ 6 | /tinyobjloader/ 7 | /cgltf/ 8 | /tinyexr/ 9 | /D3D12MemoryAllocator/ 10 | /directx12-agility/ 11 | /directx-dxc/ 12 | /WinPixEventRuntime/ 13 | -------------------------------------------------------------------------------- /third_party/AmdDxExt/AmdExtD3D.h: -------------------------------------------------------------------------------- 1 | /* 2 | *********************************************************************************************************************** 3 | * 4 | * Copyright (c) 2016 Advanced Micro Devices, Inc. All rights reserved. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | * 24 | *********************************************************************************************************************** 25 | */ 26 | /** 27 | *********************************************************************************************************************** 28 | * @file AmdExtD3D.h 29 | * @brief AMD D3D Exension API factory include file. 30 | *********************************************************************************************************************** 31 | */ 32 | #pragma once 33 | 34 | #include 35 | 36 | /* All AMD extensions contain the standard IUnknown interface: 37 | * virtual HRESULT STDMETHODCALLTYPE QueryInterface( 38 | * REFIID riid, 39 | * _COM_Outptr_ void __RPC_FAR *__RPC_FAR *ppvObject) = 0; 40 | * virtual ULONG STDMETHODCALLTYPE AddRef( void) = 0; 41 | * virtual ULONG STDMETHODCALLTYPE Release( void) = 0; 42 | */ 43 | 44 | 45 | // The app must use GetProcAddress, etc. to retrive this exported function 46 | // The associated typedef provides a convenient way to define the function pointer 47 | HRESULT __cdecl AmdExtD3DCreateInterface( 48 | IUnknown* pOuter, ///< [in] object on which to base this new interface; usually a D3D device 49 | REFIID riid, ///< ID of the requested interface 50 | void** ppvObject); ///< [out] The result interface object 51 | typedef HRESULT (__cdecl *PFNAmdExtD3DCreateInterface)(IUnknown* pOuter, REFIID riid, void** ppvObject); 52 | 53 | /** 54 | *********************************************************************************************************************** 55 | * @brief Abstract factory for extension interfaces 56 | * 57 | * Each extension interface (e.g. tessellation) will derive from this class 58 | *********************************************************************************************************************** 59 | */ 60 | interface __declspec (uuid("014937EC-9288-446F-A9AC-D75A8E3A984F")) 61 | IAmdExtD3DFactory : public IUnknown 62 | { 63 | public: 64 | virtual HRESULT CreateInterface( 65 | IUnknown* pOuter, ///< [in] An object on which to base this new interface; the required object type 66 | ///< is usually a device object but not always 67 | REFIID riid, ///< The ID of the requested interface 68 | void** ppvObject) = 0; ///< [out] The result interface object 69 | }; 70 | -------------------------------------------------------------------------------- /third_party/AmdDxExt/AmdExtD3DCommandListMarkerApi.h: -------------------------------------------------------------------------------- 1 | /* 2 | *********************************************************************************************************************** 3 | * 4 | * Copyright (c) 2017 Advanced Micro Devices, Inc. All rights reserved. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | * 24 | *********************************************************************************************************************** 25 | */ 26 | 27 | /** 28 | *********************************************************************************************************************** 29 | * @file AmdExtD3DCommandListMarkerApi.h 30 | * @brief 31 | * AMD D3D Command List Marker API include file. 32 | *********************************************************************************************************************** 33 | */ 34 | 35 | #pragma once 36 | 37 | #include 38 | 39 | /** 40 | *********************************************************************************************************************** 41 | * @brief D3D Command List Marker extension API object 42 | *********************************************************************************************************************** 43 | */ 44 | interface __declspec(uuid("735F1F3A-555D-4F70-AB92-7DB4A3AB1D28")) 45 | IAmdExtD3DCommandListMarker : public IUnknown 46 | { 47 | public: 48 | /// Set a command list marker to indicate the beginning of a rendering pass 49 | virtual VOID PushMarker(const char* pMarker) = 0; 50 | /// Set a command list marker to indicate the end of the current rendering pass 51 | virtual VOID PopMarker() = 0; 52 | /// Set a command list marker to indicate a rendering activity 53 | virtual VOID SetMarker(const char* pMarker) = 0; 54 | }; -------------------------------------------------------------------------------- /third_party/AmdDxExt/AmdExtD3DDeviceApi.h: -------------------------------------------------------------------------------- 1 | /* 2 | *********************************************************************************************************************** 3 | * 4 | * Copyright (c) 2016 Advanced Micro Devices, Inc. All rights reserved. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | * 24 | *********************************************************************************************************************** 25 | */ 26 | 27 | #pragma once 28 | #include "unknwn.h" 29 | #include 30 | #include "AmdExtD3DDeviceApi.h" 31 | 32 | /** 33 | *********************************************************************************************************************** 34 | * @brief AMD extension structure enumeration supported by the driver 35 | *********************************************************************************************************************** 36 | */ 37 | enum AmdExtD3DStructType : UINT 38 | { 39 | AmdExtD3DStructUnknown, ///< Unsupported 40 | AmdExtD3DStructPipelineState, ///< Pipeline state extension structure (AmdExtD3DPipelineCreateInfo) 41 | }; 42 | 43 | /** 44 | *********************************************************************************************************************** 45 | * @brief Extension create info base structure 46 | *********************************************************************************************************************** 47 | */ 48 | struct AmdExtD3DCreateInfo 49 | { 50 | AmdExtD3DStructType type; ///< AMD create info structure. Must be one of the supported types. 51 | void* pNext; ///< Pointer to a valid AMD structure. Must be nullptr if using base version. 52 | /// Structures defined by multiple extensions (or versions) may be chained 53 | /// together using this field. When chaining, the driver will process the chain 54 | /// starting from the base parameter onwards. 55 | }; 56 | 57 | /** 58 | *********************************************************************************************************************** 59 | * @brief Extended pipeline flags 60 | *********************************************************************************************************************** 61 | */ 62 | struct AmdExtD3DPipelineFlags 63 | { 64 | unsigned int depthBoundsTestEnable : 1; ///< Enable depth bounds testing 65 | unsigned int reserved : 31; ///< Reserved bits (must be 0) 66 | }; 67 | 68 | /** 69 | *********************************************************************************************************************** 70 | * @brief Extended pipeline state create info structure 71 | *********************************************************************************************************************** 72 | */ 73 | struct AmdExtD3DPipelineCreateInfo : AmdExtD3DCreateInfo 74 | { 75 | AmdExtD3DPipelineFlags flags; ///< Pipeline flags 76 | }; 77 | 78 | /** 79 | *********************************************************************************************************************** 80 | * @brief Extension device API object 81 | *********************************************************************************************************************** 82 | */ 83 | interface __declspec(uuid("8104C0FC-7413-410F-8E83-AA617E908648")) 84 | IAmdExtD3DDevice : public IUnknown 85 | { 86 | public: 87 | virtual HRESULT CreateGraphicsPipelineState( 88 | const AmdExtD3DCreateInfo* pAmdExtCreateInfo, 89 | const D3D12_GRAPHICS_PIPELINE_STATE_DESC* pDesc, 90 | REFIID riid, 91 | void** ppPipelineState) = 0; 92 | }; 93 | 94 | /** 95 | *********************************************************************************************************************** 96 | * @brief Version 1 extension device API object 97 | *********************************************************************************************************************** 98 | */ 99 | interface __declspec(uuid("4BBCAF68-EAF7-4FA4-B653-CB458C334A4E")) 100 | IAmdExtD3DDevice1 : public IAmdExtD3DDevice 101 | { 102 | public: 103 | virtual VOID PushMarker(ID3D12GraphicsCommandList* pGfxCmdList, const char* pMarker) = 0; 104 | virtual VOID PopMarker(ID3D12GraphicsCommandList* pGfxCmdList) = 0; 105 | virtual VOID SetMarker(ID3D12GraphicsCommandList* pGfxCmdList, const char* pMarker) = 0; 106 | }; -------------------------------------------------------------------------------- /third_party/AmdDxExt/AmdPix3.h: -------------------------------------------------------------------------------- 1 | //============================================================================== 2 | // Copyright (c) 2017-2023 Advanced Micro Devices, Inc. All rights reserved. 3 | /// @author AMD Developer Tools Team 4 | /// @file 5 | /// @brief This file contains macro definitions to wrap existing PIX3 6 | /// functions with AMD RGP marker support. 7 | /// 8 | /// This requires a WinPixEventRuntime version of at least 9 | /// 1.0.200127001. 10 | /// 11 | /// To use: Update the PIXEvents.h file to #include this header file. 12 | /// Then prefix the existing calls to PIXBeginEventOnContextCpu, 13 | /// PIXEndEventOnContextCpu and PIXSetMarkerOnContextCpu within 14 | /// that file to add an "Rgp" prefix (so the calls become 15 | /// RgpPIXBeginEventOnContextCpu, RgpPIXEndEventOnContextCpu and 16 | /// RgpPIXSetMarkerOnContextCpu). When using a version of 17 | /// WinPixEventRuntime prior to v1.0.231030001, you should also 18 | /// add a "Legacy" suffix to the aforementioned calls. More 19 | /// information, including a complete user guide, can be found 20 | /// in the RGP user documentation. 21 | //============================================================================== 22 | 23 | #ifndef _AMD_PIX3_H_ 24 | #define _AMD_PIX3_H_ 25 | 26 | #include 27 | #include 28 | 29 | #include "AmdExtD3D.h" 30 | #include "AmdExtD3DDeviceApi.h" 31 | 32 | #define MAX_MARKER_STRING_LENGTH 1024 33 | 34 | // Per-thread AMD extension device object using TLS. 35 | static __declspec(thread) IAmdExtD3DDevice1* tls_pAmdExtDeviceObject = nullptr; 36 | static __declspec(thread) bool tls_checkAmdDriver = true; 37 | 38 | // This function will initialize the tls_pAmdExtDeviceObject per thread 39 | // tls_pAmdExtDeviceObject contains the marker API. 40 | inline void InitializeAmdExtDeviceObject(ID3D12GraphicsCommandList* pCommandList) 41 | { 42 | // Return immediately if the device extension object has been created for the thread. 43 | if (nullptr != tls_pAmdExtDeviceObject) 44 | { 45 | return; 46 | } 47 | 48 | // Return immediately if on non-AMD system. 49 | if (!tls_checkAmdDriver) 50 | { 51 | return; 52 | } 53 | 54 | HMODULE hpAmdD3dDl2 = nullptr; 55 | BOOL loaded = FALSE; 56 | 57 | if (tls_checkAmdDriver) 58 | { 59 | loaded = GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, L"amdxc64.dll", &hpAmdD3dDl2); 60 | } 61 | 62 | if (FALSE != loaded && nullptr != hpAmdD3dDl2) 63 | { 64 | PFNAmdExtD3DCreateInterface pAmdExtD3dCreateFunc = (PFNAmdExtD3DCreateInterface)GetProcAddress(hpAmdD3dDl2, "AmdExtD3DCreateInterface"); 65 | 66 | if (nullptr != pAmdExtD3dCreateFunc) 67 | { 68 | ID3D12Device* pDevice = nullptr; 69 | 70 | if (nullptr != pCommandList) 71 | { 72 | pCommandList->GetDevice(__uuidof(ID3D12Device), reinterpret_cast(&pDevice)); 73 | } 74 | 75 | if (nullptr != pDevice) 76 | { 77 | // Create the extension object factory. 78 | IAmdExtD3DFactory* pAmdExtObject = nullptr; 79 | pAmdExtD3dCreateFunc(pDevice, __uuidof(IAmdExtD3DFactory), reinterpret_cast(&pAmdExtObject)); 80 | 81 | if (nullptr != pAmdExtObject) 82 | { 83 | // Use the extension factory object to create a device extension object that contains the marker API. 84 | pAmdExtObject->CreateInterface(pDevice, __uuidof(IAmdExtD3DDevice1), reinterpret_cast(&tls_pAmdExtDeviceObject)); 85 | pAmdExtObject->Release(); 86 | } 87 | pDevice->Release(); 88 | } 89 | } 90 | } 91 | else 92 | { 93 | // Running on non-AMD hardware or missing AMD driver install. 94 | tls_checkAmdDriver = false; 95 | } 96 | } 97 | 98 | inline void RgpSetMarker(ID3D12GraphicsCommandList* pCommandList, PCWSTR formatString, ...) 99 | { 100 | InitializeAmdExtDeviceObject(pCommandList); 101 | 102 | if (nullptr != tls_pAmdExtDeviceObject) 103 | { 104 | // Create a new marker string that includes all the variadic args. 105 | wchar_t markerString[MAX_MARKER_STRING_LENGTH]; 106 | va_list args; 107 | va_start(args, formatString); 108 | vswprintf_s(markerString, formatString, args); 109 | va_end(args); 110 | 111 | // Convert from wchar_t to char string. 112 | char markerStringInChar[MAX_MARKER_STRING_LENGTH]; 113 | size_t retValue = 0; 114 | wcstombs_s(&retValue, markerStringInChar, MAX_MARKER_STRING_LENGTH, markerString, MAX_MARKER_STRING_LENGTH); 115 | 116 | // Set the RGP marker. 117 | tls_pAmdExtDeviceObject->SetMarker(pCommandList, markerStringInChar); 118 | } 119 | } 120 | 121 | inline void RgpSetMarker(ID3D12GraphicsCommandList* pCommandList, PCSTR formatString, ...) 122 | { 123 | InitializeAmdExtDeviceObject(pCommandList); 124 | 125 | if (nullptr != tls_pAmdExtDeviceObject) 126 | { 127 | // Create a new marker string that includes all the variadic args. 128 | char markerString[MAX_MARKER_STRING_LENGTH]; 129 | va_list args; 130 | va_start(args, formatString); 131 | vsprintf_s(markerString, formatString, args); 132 | va_end(args); 133 | 134 | // Set the RGP marker. 135 | tls_pAmdExtDeviceObject->SetMarker(pCommandList, markerString); 136 | } 137 | } 138 | 139 | inline void RgpPushMarker(ID3D12GraphicsCommandList* pCommandList, PCWSTR formatString, ...) 140 | { 141 | InitializeAmdExtDeviceObject(pCommandList); 142 | 143 | if (nullptr != tls_pAmdExtDeviceObject) 144 | { 145 | // Create a new marker string that includes all the variadic args. 146 | wchar_t markerString[MAX_MARKER_STRING_LENGTH]; 147 | va_list args; 148 | va_start(args, formatString); 149 | vswprintf_s(markerString, formatString, args); 150 | va_end(args); 151 | 152 | // Convert from wchar_t to char string. 153 | char markerStringInChar[MAX_MARKER_STRING_LENGTH]; 154 | size_t retValue = 0; 155 | wcstombs_s(&retValue, markerStringInChar, MAX_MARKER_STRING_LENGTH, markerString, MAX_MARKER_STRING_LENGTH); 156 | 157 | // Push the RGP marker. 158 | tls_pAmdExtDeviceObject->PushMarker(pCommandList, markerStringInChar); 159 | } 160 | } 161 | 162 | inline void RgpPushMarker(ID3D12GraphicsCommandList* pCommandList, PCSTR formatString, ...) 163 | { 164 | InitializeAmdExtDeviceObject(pCommandList); 165 | 166 | if (nullptr != tls_pAmdExtDeviceObject) 167 | { 168 | // Create a new marker string that includes all the variadic args. 169 | char markerString[MAX_MARKER_STRING_LENGTH]; 170 | va_list args; 171 | va_start(args, formatString); 172 | vsprintf_s(markerString, formatString, args); 173 | va_end(args); 174 | 175 | // Push the RGP marker. 176 | tls_pAmdExtDeviceObject->PushMarker(pCommandList, markerString); 177 | } 178 | } 179 | 180 | inline void RgpPopMarker(ID3D12GraphicsCommandList* pCommandList) 181 | { 182 | InitializeAmdExtDeviceObject(pCommandList); 183 | 184 | if (nullptr != tls_pAmdExtDeviceObject) 185 | { 186 | tls_pAmdExtDeviceObject->PopMarker(pCommandList); 187 | } 188 | } 189 | 190 | inline void RgpSetMarker(ID3D12CommandQueue*, PCWSTR, ...) 191 | { 192 | // There is no queue-based marker yet. 193 | } 194 | 195 | inline void RgpSetMarker(ID3D12CommandQueue*, PCSTR, ...) 196 | { 197 | // There is no queue-based marker yet. 198 | } 199 | 200 | inline void RgpPushMarker(ID3D12CommandQueue*, PCWSTR, ...) 201 | { 202 | // There is no queue-based marker yet. 203 | } 204 | 205 | inline void RgpPushMarker(ID3D12CommandQueue*, PCSTR, ...) 206 | { 207 | // There is no queue-based marker yet. 208 | } 209 | 210 | inline void RgpPopMarker(ID3D12CommandQueue*) 211 | { 212 | // There is no queue-based marker yet. 213 | } 214 | 215 | // Define three macros to wrap existing PIX3 functions when using WinPixEventRuntime v1.0.231030001 or newer. 216 | #define RgpPIXBeginEventOnContextCpu(destination, eventSize, context, color, formatString, ...) \ 217 | RgpPushMarker(context, formatString, args...); \ 218 | PIXBeginEventOnContextCpu(destination, eventSize, context, color, formatString, args...); 219 | 220 | #define RgpPIXEndEventOnContextCpu(destination, context) \ 221 | RgpPopMarker(context); \ 222 | destination = PIXEndEventOnContextCpu(context); 223 | 224 | #define RgpPIXSetMarkerOnContextCpu(destination, eventSize, context, color, formatString, ...) \ 225 | RgpSetMarker(context, formatString, args...); \ 226 | PIXSetMarkerOnContextCpu(destination, eventSize, context, color, formatString, args...); 227 | 228 | // Define three macros to wrap existing PIX3 functions when using a legacy version of WinPixEventRuntime (prior to v1.0.231030001). 229 | #define RgpPIXBeginEventOnContextCpuLegacy(context, color, formatString, ...) \ 230 | RgpPushMarker(context, formatString, args...); \ 231 | PIXBeginEventOnContextCpu(context, color, formatString, args...); 232 | 233 | #define RgpPIXEndEventOnContextCpuLegacy(context) \ 234 | RgpPopMarker(context); \ 235 | PIXEndEventOnContextCpu(context); 236 | 237 | #define RgpPIXSetMarkerOnContextCpuLegacy(context, color, formatString, ...) \ 238 | RgpSetMarker(context, formatString, args...); \ 239 | PIXSetMarkerOnContextCpu(context, color, formatString, args...); 240 | 241 | #endif //_AMD_PIX3_H_ 242 | --------------------------------------------------------------------------------