├── .gitignore
├── Image.jpg
├── fontawesome-webfont.ttf
├── ImGuiProfiler.vcxproj.user
├── .clang-format
├── ImGui
├── LICENSE.txt
├── imgui_impl_win32.h
├── imgui_impl_dx12.h
├── imconfig.h
└── imstb_rectpack.h
├── ImGuiProfiler.sln
├── LICENSE
├── README.md
├── ImGuiProfiler.vcxproj.filters
├── ImGuiProfiler.vcxproj
├── Profiler.h
├── ProfilerWindow.cpp
├── main.cpp
├── Profiler.cpp
└── IconsFontAwesome4.h
/.gitignore:
--------------------------------------------------------------------------------
1 | .vs
2 | x64
3 | trace.json
4 | /.idea
5 | imgui.ini
6 |
--------------------------------------------------------------------------------
/Image.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simco50/TimelineProfiler/HEAD/Image.jpg
--------------------------------------------------------------------------------
/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simco50/TimelineProfiler/HEAD/fontawesome-webfont.ttf
--------------------------------------------------------------------------------
/ImGuiProfiler.vcxproj.user:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.clang-format:
--------------------------------------------------------------------------------
1 | ---
2 | BasedOnStyle: Microsoft
3 | AccessModifierOffset: '-4'
4 | AlignConsecutiveAssignments: Consecutive
5 | AlignConsecutiveDeclarations: Consecutive
6 | AlignConsecutiveMacros: Consecutive
7 | AlignConsecutiveBitFields: Consecutive
8 | AlignEscapedNewlines: Left
9 | AlignTrailingComments: 'true'
10 | Cpp11BracedListStyle: 'false'
11 | PointerAlignment: Left
12 | TabWidth: '4'
13 | UseTab: Always
14 | AllowShortFunctionsOnASingleLine: Inline
15 | ColumnLimit: 0
16 | ...
17 |
--------------------------------------------------------------------------------
/ImGui/LICENSE.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014-2025 Omar Cornut
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 |
--------------------------------------------------------------------------------
/ImGuiProfiler.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.6.33829.357
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ImGuiProfiler", "ImGuiProfiler.vcxproj", "{C89A478E-D4D0-4A65-8D3E-98AE7BD4929C}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|x64 = Debug|x64
11 | Debug|x86 = Debug|x86
12 | Release|x64 = Release|x64
13 | Release|x86 = Release|x86
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {C89A478E-D4D0-4A65-8D3E-98AE7BD4929C}.Debug|x64.ActiveCfg = Debug|x64
17 | {C89A478E-D4D0-4A65-8D3E-98AE7BD4929C}.Debug|x64.Build.0 = Debug|x64
18 | {C89A478E-D4D0-4A65-8D3E-98AE7BD4929C}.Debug|x86.ActiveCfg = Debug|Win32
19 | {C89A478E-D4D0-4A65-8D3E-98AE7BD4929C}.Debug|x86.Build.0 = Debug|Win32
20 | {C89A478E-D4D0-4A65-8D3E-98AE7BD4929C}.Release|x64.ActiveCfg = Release|x64
21 | {C89A478E-D4D0-4A65-8D3E-98AE7BD4929C}.Release|x64.Build.0 = Release|x64
22 | {C89A478E-D4D0-4A65-8D3E-98AE7BD4929C}.Release|x86.ActiveCfg = Release|Win32
23 | {C89A478E-D4D0-4A65-8D3E-98AE7BD4929C}.Release|x86.Build.0 = Release|Win32
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | GlobalSection(ExtensibilityGlobals) = postSolution
29 | SolutionGuid = {77272787-8C32-4F17-B9A0-C7FA5D1D7D25}
30 | EndGlobalSection
31 | EndGlobal
32 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Simon Coenen
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 |
23 | --------------------------------------------------------------------------------------------
24 |
25 |
26 | FontAwesomeCpp License
27 |
28 | Copyright (c) 2017 Juliette Foucaut and Doug Binks
29 |
30 | This software is provided 'as-is', without any express or implied
31 | warranty. In no event will the authors be held liable for any damages
32 | arising from the use of this software.
33 |
34 | Permission is granted to anyone to use this software for any purpose,
35 | including commercial applications, and to alter it and redistribute it
36 | freely, subject to the following restrictions:
37 |
38 | 1. The origin of this software must not be misrepresented; you must not
39 | claim that you wrote the original software. If you use this software
40 | in a product, an acknowledgment in the product documentation would be
41 | appreciated but is not required.
42 | 2. Altered source versions must be plainly marked as such, and must not be
43 | misrepresented as being the original software.
44 | 3. This notice may not be removed or altered from any source distribution.{\rtf1}
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # TimelineProfiler
2 | Simple CPU/GPU profiler with ImGui HUD
3 | GPU profiler only supports D3D12
4 |
5 | 
6 |
7 | ## Usage
8 |
9 | ### Setup
10 |
11 | Add files to project:
12 | - Profiler.h
13 | - Profiler.cpp
14 | - ProfilerWindow.cpp
15 | - IconsFontAwesome4.h
16 | - fontawesome-webfont.ttf
17 |
18 | Add the icon font and merge it with your main font
19 | ```c++
20 | ImFontConfig fontConfig;
21 | fontConfig.MergeMode = true;
22 | fontConfig.GlyphMinAdvanceX = 15.0f
23 | static const ImWchar icon_ranges[] = { ICON_MIN_FA, ICON_MAX_FA, 0 };
24 | io.Fonts->AddFontFromFileTTF(FONT_ICON_FILE_NAME_FA, 15.0f, &fontConfig, icon_ranges);
25 | ```
26 |
27 | ### CPU Profiler
28 |
29 | #### Initialize
30 |
31 | ```c++
32 | // Initialize
33 | gCPUProfiler.Initialize(historySize, maxNumEvents);
34 | ```
35 |
36 | #### Shutdown
37 |
38 | ```c++
39 | // Shutdown
40 | gCPUProfiler.Shutdown();
41 | ```
42 |
43 | #### Each frame
44 | ```c++
45 | // Call at the start of each frame
46 | PROFILE_FRAME()
47 | ```
48 |
49 | **CPU Event**
50 |
51 | `PROFILE_CPU_SCOPE()` to add a CPU event. Optionally specify a custom name
52 |
53 |
54 | **Registering a thread (optional)**
55 |
56 | `PROFILE_REGISTER_THREAD(name)` to register a thread.
57 |
58 | The registration order of threads will define the order in the timeline
59 | If a thread is not registered, it will lazy-register when an event is created first.
60 |
61 |
62 | ### GPU Profiler
63 |
64 | If you want to use the GPU profiler, initialize and shutdown as following, providing your command queues.
65 |
66 | #### Initialize
67 |
68 | ```c++
69 | // Initialize
70 | Span queues;
71 | gGPUProfiler.Initialize(d3dDevice, queues, historySize, frameLatency, maxNumEvents, maxActiveCommandLists);
72 | ```
73 |
74 | #### Shutdown
75 |
76 | ```c++
77 | // Shutdown
78 | gGPUProfiler.Shutdown();
79 | ```
80 |
81 | #### Each frame
82 | ```c++
83 | // Call at the start of each frame
84 | PROFILE_FRAME()
85 | ```
86 |
87 | #### Each `ExecuteCommandLists` (not optional!)
88 |
89 | ```c++
90 | ID3D12CommandQueue* queue = ...;
91 | Span cmdlists = ...;
92 | PROFILE_EXECUTE_COMMANDLISTS(queue, cmdlists);
93 | queue->ExecuteCommandLists(cmdlists.data(), cmdlists.size());
94 | ```
95 |
96 | #### Adding events
97 |
98 | `PROFILE_GPU_SCOPE(commandlist, name)` to add a GPU event.
99 |
100 | Specify the ID3D12GraphicsCommandList, and optionally a name.
--------------------------------------------------------------------------------
/ImGuiProfiler.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx
7 |
8 |
9 | {93995380-89BD-4b04-88EB-625FBE52EBFB}
10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd
11 |
12 |
13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
15 |
16 |
17 |
18 |
19 | Header Files
20 |
21 |
22 | Header Files
23 |
24 |
25 | Header Files
26 |
27 |
28 | Header Files
29 |
30 |
31 | Header Files
32 |
33 |
34 | Header Files
35 |
36 |
37 | Header Files
38 |
39 |
40 | Header Files
41 |
42 |
43 | Header Files
44 |
45 |
46 | Header Files
47 |
48 |
49 | Header Files
50 |
51 |
52 |
53 |
54 | Source Files
55 |
56 |
57 | Source Files
58 |
59 |
60 | Source Files
61 |
62 |
63 | Source Files
64 |
65 |
66 | Source Files
67 |
68 |
69 | Source Files
70 |
71 |
72 | Source Files
73 |
74 |
75 | Source Files
76 |
77 |
78 | Source Files
79 |
80 |
81 | Source Files
82 |
83 |
84 |
--------------------------------------------------------------------------------
/ImGui/imgui_impl_win32.h:
--------------------------------------------------------------------------------
1 | // dear imgui: Platform Backend for Windows (standard windows API for 32-bits AND 64-bits applications)
2 | // This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..)
3 |
4 | // Implemented features:
5 | // [X] Platform: Clipboard support (for Win32 this is actually part of core dear imgui)
6 | // [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen.
7 | // [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy VK_* values are obsolete since 1.87 and not supported since 1.91.5]
8 | // [X] Platform: Gamepad support.
9 | // [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
10 | // [X] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
11 |
12 | // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
13 | // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
14 | // Learn about Dear ImGui:
15 | // - FAQ https://dearimgui.com/faq
16 | // - Getting Started https://dearimgui.com/getting-started
17 | // - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
18 | // - Introduction, links and more at the top of imgui.cpp
19 |
20 | #pragma once
21 | #include "imgui.h" // IMGUI_IMPL_API
22 | #ifndef IMGUI_DISABLE
23 |
24 | // Follow "Getting Started" link and check examples/ folder to learn about using backends!
25 | IMGUI_IMPL_API bool ImGui_ImplWin32_Init(void* hwnd);
26 | IMGUI_IMPL_API bool ImGui_ImplWin32_InitForOpenGL(void* hwnd);
27 | IMGUI_IMPL_API void ImGui_ImplWin32_Shutdown();
28 | IMGUI_IMPL_API void ImGui_ImplWin32_NewFrame();
29 |
30 | // Win32 message handler your application need to call.
31 | // - Intentionally commented out in a '#if 0' block to avoid dragging dependencies on from this helper.
32 | // - You should COPY the line below into your .cpp code to forward declare the function and then you can call it.
33 | // - Call from your application's message handler. Keep calling your message handler unless this function returns TRUE.
34 |
35 | #if 0
36 | extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
37 | #endif
38 |
39 | // DPI-related helpers (optional)
40 | // - Use to enable DPI awareness without having to create an application manifest.
41 | // - Your own app may already do this via a manifest or explicit calls. This is mostly useful for our examples/ apps.
42 | // - In theory we could call simple functions from Windows SDK such as SetProcessDPIAware(), SetProcessDpiAwareness(), etc.
43 | // but most of the functions provided by Microsoft require Windows 8.1/10+ SDK at compile time and Windows 8/10+ at runtime,
44 | // neither we want to require the user to have. So we dynamically select and load those functions to avoid dependencies.
45 | IMGUI_IMPL_API void ImGui_ImplWin32_EnableDpiAwareness();
46 | IMGUI_IMPL_API float ImGui_ImplWin32_GetDpiScaleForHwnd(void* hwnd); // HWND hwnd
47 | IMGUI_IMPL_API float ImGui_ImplWin32_GetDpiScaleForMonitor(void* monitor); // HMONITOR monitor
48 |
49 | // Transparency related helpers (optional) [experimental]
50 | // - Use to enable alpha compositing transparency with the desktop.
51 | // - Use together with e.g. clearing your framebuffer with zero-alpha.
52 | IMGUI_IMPL_API void ImGui_ImplWin32_EnableAlphaCompositing(void* hwnd); // HWND hwnd
53 |
54 | #endif // #ifndef IMGUI_DISABLE
55 |
--------------------------------------------------------------------------------
/ImGui/imgui_impl_dx12.h:
--------------------------------------------------------------------------------
1 | // dear imgui: Renderer Backend for DirectX12
2 | // This needs to be used along with a Platform Backend (e.g. Win32)
3 |
4 | // Implemented features:
5 | // [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef!
6 | // [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
7 | // [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures).
8 | // [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'.
9 | // [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
10 |
11 | // The aim of imgui_impl_dx12.h/.cpp is to be usable in your engine without any modification.
12 | // IF YOU FEEL YOU NEED TO MAKE ANY CHANGE TO THIS CODE, please share them and your feedback at https://github.com/ocornut/imgui/
13 |
14 | // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
15 | // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
16 | // Learn about Dear ImGui:
17 | // - FAQ https://dearimgui.com/faq
18 | // - Getting Started https://dearimgui.com/getting-started
19 | // - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
20 | // - Introduction, links and more at the top of imgui.cpp
21 |
22 | #pragma once
23 | #include "imgui.h" // IMGUI_IMPL_API
24 | #ifndef IMGUI_DISABLE
25 | #include // DXGI_FORMAT
26 | #include // D3D12_CPU_DESCRIPTOR_HANDLE
27 |
28 | // Initialization data, for ImGui_ImplDX12_Init()
29 | struct ImGui_ImplDX12_InitInfo
30 | {
31 | ID3D12Device* Device;
32 | ID3D12CommandQueue* CommandQueue; // Command queue used for queuing texture uploads.
33 | int NumFramesInFlight;
34 | DXGI_FORMAT RTVFormat; // RenderTarget format.
35 | DXGI_FORMAT DSVFormat; // DepthStencilView format.
36 | void* UserData;
37 |
38 | // Allocating SRV descriptors for textures is up to the application, so we provide callbacks.
39 | // (current version of the backend will only allocate one descriptor, from 1.92 the backend will need to allocate more)
40 | ID3D12DescriptorHeap* SrvDescriptorHeap;
41 | void (*SrvDescriptorAllocFn)(ImGui_ImplDX12_InitInfo* info, D3D12_CPU_DESCRIPTOR_HANDLE* out_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE* out_gpu_desc_handle);
42 | void (*SrvDescriptorFreeFn)(ImGui_ImplDX12_InitInfo* info, D3D12_CPU_DESCRIPTOR_HANDLE cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE gpu_desc_handle);
43 | #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
44 | D3D12_CPU_DESCRIPTOR_HANDLE LegacySingleSrvCpuDescriptor; // To facilitate transition from single descriptor to allocator callback, you may use those.
45 | D3D12_GPU_DESCRIPTOR_HANDLE LegacySingleSrvGpuDescriptor;
46 | #endif
47 |
48 | ImGui_ImplDX12_InitInfo() { memset((void*)this, 0, sizeof(*this)); }
49 | };
50 |
51 | // Follow "Getting Started" link and check examples/ folder to learn about using backends!
52 | IMGUI_IMPL_API bool ImGui_ImplDX12_Init(ImGui_ImplDX12_InitInfo* info);
53 | IMGUI_IMPL_API void ImGui_ImplDX12_Shutdown();
54 | IMGUI_IMPL_API void ImGui_ImplDX12_NewFrame();
55 | IMGUI_IMPL_API void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandList* graphics_command_list);
56 |
57 | #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
58 | // Legacy initialization API Obsoleted in 1.91.5
59 | // - font_srv_cpu_desc_handle and font_srv_gpu_desc_handle are handles to a single SRV descriptor to use for the internal font texture, they must be in 'srv_descriptor_heap'
60 | // - When we introduced the ImGui_ImplDX12_InitInfo struct we also added a 'ID3D12CommandQueue* CommandQueue' field.
61 | IMGUI_IMPL_API bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FORMAT rtv_format, ID3D12DescriptorHeap* srv_descriptor_heap, D3D12_CPU_DESCRIPTOR_HANDLE font_srv_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE font_srv_gpu_desc_handle);
62 | #endif
63 |
64 | // Use if you want to reset your rendering device without losing Dear ImGui state.
65 | IMGUI_IMPL_API bool ImGui_ImplDX12_CreateDeviceObjects();
66 | IMGUI_IMPL_API void ImGui_ImplDX12_InvalidateDeviceObjects();
67 |
68 | // (Advanced) Use e.g. if you need to precisely control the timing of texture updates (e.g. for staged rendering), by setting ImDrawData::Textures = NULL to handle this manually.
69 | IMGUI_IMPL_API void ImGui_ImplDX12_UpdateTexture(ImTextureData* tex);
70 |
71 | // [BETA] Selected render state data shared with callbacks.
72 | // This is temporarily stored in GetPlatformIO().Renderer_RenderState during the ImGui_ImplDX12_RenderDrawData() call.
73 | // (Please open an issue if you feel you need access to more data)
74 | struct ImGui_ImplDX12_RenderState
75 | {
76 | ID3D12Device* Device;
77 | ID3D12GraphicsCommandList* CommandList;
78 | };
79 |
80 | #endif // #ifndef IMGUI_DISABLE
81 |
--------------------------------------------------------------------------------
/ImGuiProfiler.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | Win32
7 |
8 |
9 | Release
10 | Win32
11 |
12 |
13 | Debug
14 | x64
15 |
16 |
17 | Release
18 | x64
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | 16.0
48 | Win32Proj
49 | {c89a478e-d4d0-4a65-8d3e-98ae7bd4929c}
50 | ImGuiProfiler
51 | 10.0
52 |
53 |
54 |
55 | Application
56 | true
57 | v143
58 | Unicode
59 |
60 |
61 | Application
62 | false
63 | v143
64 | true
65 | Unicode
66 |
67 |
68 | Application
69 | true
70 | v143
71 | Unicode
72 |
73 |
74 | Application
75 | false
76 | v143
77 | true
78 | Unicode
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 | Level3
101 | true
102 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
103 | true
104 | stdcpp20
105 |
106 |
107 | Console
108 | true
109 | dxgi.lib;d3d12.lib;%(AdditionalDependencies)
110 |
111 |
112 |
113 |
114 | Level3
115 | true
116 | true
117 | true
118 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
119 | true
120 | stdcpp20
121 |
122 |
123 | Console
124 | true
125 | true
126 | true
127 | dxgi.lib;d3d12.lib;%(AdditionalDependencies)
128 |
129 |
130 |
131 |
132 | Level3
133 | true
134 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions)
135 | true
136 | stdcpp20
137 | $(PROJECT_DIR)imgui;C:\Program Files\Superluminal\Performance\API\include\Superluminal;%(AdditionalIncludeDirectories)
138 |
139 |
140 | Console
141 | true
142 | dxgi.lib;d3d12.lib;%(AdditionalDependencies)
143 |
144 |
145 |
146 |
147 | Level3
148 | true
149 | true
150 | true
151 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
152 | true
153 | $(PROJECT_DIR)imgui;C:\Program Files\Superluminal\Performance\API\include\Superluminal;%(AdditionalIncludeDirectories)
154 | stdcpp20
155 |
156 |
157 | Console
158 | true
159 | true
160 | true
161 | dxgi.lib;d3d12.lib;%(AdditionalDependencies)
162 |
163 |
164 |
165 |
166 |
167 |
--------------------------------------------------------------------------------
/ImGui/imconfig.h:
--------------------------------------------------------------------------------
1 | //-----------------------------------------------------------------------------
2 | // DEAR IMGUI COMPILE-TIME OPTIONS
3 | // Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure.
4 | // You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions.
5 | //-----------------------------------------------------------------------------
6 | // A) You may edit imconfig.h (and not overwrite it when updating Dear ImGui, or maintain a patch/rebased branch with your modifications to it)
7 | // B) or '#define IMGUI_USER_CONFIG "my_imgui_config.h"' in your project and then add directives in your own file without touching this template.
8 | //-----------------------------------------------------------------------------
9 | // You need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include the imgui*.cpp
10 | // files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures.
11 | // Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts.
12 | // Call IMGUI_CHECKVERSION() from your .cpp file to verify that the data structures your files are using are matching the ones imgui.cpp is using.
13 | //-----------------------------------------------------------------------------
14 |
15 | #pragma once
16 |
17 | //---- Define assertion handler. Defaults to calling assert().
18 | // If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement.
19 | //#define IM_ASSERT(_EXPR) MyAssert(_EXPR)
20 | //#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts
21 |
22 | //---- Define attributes of all API symbols declarations, e.g. for DLL under Windows
23 | // Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility.
24 | // DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions()
25 | // for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details.
26 | //#define IMGUI_API __declspec( dllexport )
27 | //#define IMGUI_API __declspec( dllimport )
28 |
29 | //---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to clean your code of obsolete function/names.
30 | //#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
31 | //#define IMGUI_DISABLE_OBSOLETE_KEYIO // 1.87: disable legacy io.KeyMap[]+io.KeysDown[] in favor io.AddKeyEvent(). This will be folded into IMGUI_DISABLE_OBSOLETE_FUNCTIONS in a few versions.
32 |
33 | //---- Disable all of Dear ImGui or don't implement standard windows/tools.
34 | // It is very strongly recommended to NOT disable the demo windows and debug tool during development. They are extremely useful in day to day work. Please read comments in imgui_demo.cpp.
35 | //#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty.
36 | //#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty.
37 | //#define IMGUI_DISABLE_DEBUG_TOOLS // Disable metrics/debugger and other debug tools: ShowMetricsWindow(), ShowDebugLogWindow() and ShowStackToolWindow() will be empty (this was called IMGUI_DISABLE_METRICS_WINDOW before 1.88).
38 |
39 | //---- Don't implement some functions to reduce linkage requirements.
40 | //#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. (user32.lib/.a, kernel32.lib/.a)
41 | //#define IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with Visual Studio] Implement default IME handler (require imm32.lib/.a, auto-link for Visual Studio, -limm32 on command-line for MinGW)
42 | //#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a)
43 | //#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, IME).
44 | //#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default).
45 | //#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf)
46 | //#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself.
47 | //#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies)
48 | //#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function.
49 | //#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions().
50 | //#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available
51 |
52 | //---- Include imgui_user.h at the end of imgui.h as a convenience
53 | //#define IMGUI_INCLUDE_IMGUI_USER_H
54 |
55 | //---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another)
56 | //#define IMGUI_USE_BGRA_PACKED_COLOR
57 |
58 | //---- Use 32-bit for ImWchar (default is 16-bit) to support unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...)
59 | //#define IMGUI_USE_WCHAR32
60 |
61 | //---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version
62 | // By default the embedded implementations are declared static and not available outside of Dear ImGui sources files.
63 | //#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h"
64 | //#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h"
65 | //#define IMGUI_STB_SPRINTF_FILENAME "my_folder/stb_sprintf.h" // only used if IMGUI_USE_STB_SPRINTF is defined.
66 | //#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION
67 | //#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
68 | //#define IMGUI_DISABLE_STB_SPRINTF_IMPLEMENTATION // only disabled if IMGUI_USE_STB_SPRINTF is defined.
69 |
70 | //---- Use stb_sprintf.h for a faster implementation of vsnprintf instead of the one from libc (unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined)
71 | // Compatibility checks of arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by stb_sprintf.h.
72 | //#define IMGUI_USE_STB_SPRINTF
73 |
74 | //---- Use FreeType to build and rasterize the font atlas (instead of stb_truetype which is embedded by default in Dear ImGui)
75 | // Requires FreeType headers to be available in the include path. Requires program to be compiled with 'misc/freetype/imgui_freetype.cpp' (in this repository) + the FreeType library (not provided).
76 | // On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'.
77 | //#define IMGUI_ENABLE_FREETYPE
78 |
79 | //---- Use FreeType+lunasvg library to render OpenType SVG fonts (SVGinOT)
80 | // Requires lunasvg headers to be available in the include path + program to be linked with the lunasvg library (not provided).
81 | // Only works in combination with IMGUI_ENABLE_FREETYPE.
82 | // (implementation is based on Freetype's rsvg-port.c which is licensed under CeCILL-C Free Software License Agreement)
83 | //#define IMGUI_ENABLE_FREETYPE_LUNASVG
84 |
85 | //---- Use stb_truetype to build and rasterize the font atlas (default)
86 | // The only purpose of this define is if you want force compilation of the stb_truetype backend ALONG with the FreeType backend.
87 | //#define IMGUI_ENABLE_STB_TRUETYPE
88 |
89 | //---- Define constructor and implicit cast operators to convert back<>forth between your math types and ImVec2/ImVec4.
90 | // This will be inlined as part of ImVec2 and ImVec4 class declarations.
91 | /*
92 | #define IM_VEC2_CLASS_EXTRA \
93 | constexpr ImVec2(const MyVec2& f) : x(f.x), y(f.y) {} \
94 | operator MyVec2() const { return MyVec2(x,y); }
95 |
96 | #define IM_VEC4_CLASS_EXTRA \
97 | constexpr ImVec4(const MyVec4& f) : x(f.x), y(f.y), z(f.z), w(f.w) {} \
98 | operator MyVec4() const { return MyVec4(x,y,z,w); }
99 | */
100 | //---- ...Or use Dear ImGui's own very basic math operators.
101 | #define IMGUI_DEFINE_MATH_OPERATORS
102 |
103 | //---- Use 32-bit vertex indices (default is 16-bit) is one way to allow large meshes with more than 64K vertices.
104 | // Your renderer backend will need to support it (most example renderer backends support both 16/32-bit indices).
105 | // Another way to allow large meshes while keeping 16-bit indices is to handle ImDrawCmd::VtxOffset in your renderer.
106 | // Read about ImGuiBackendFlags_RendererHasVtxOffset for details.
107 | //#define ImDrawIdx unsigned int
108 |
109 | //---- Override ImDrawCallback signature (will need to modify renderer backends accordingly)
110 | //struct ImDrawList;
111 | //struct ImDrawCmd;
112 | //typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data);
113 | //#define ImDrawCallback MyImDrawCallback
114 |
115 | //---- Debug Tools: Macro to break in Debugger (we provide a default implementation of this in the codebase)
116 | // (use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.)
117 | //#define IM_DEBUG_BREAK IM_ASSERT(0)
118 | //#define IM_DEBUG_BREAK __debugbreak()
119 |
120 | //---- Debug Tools: Enable slower asserts
121 | //#define IMGUI_DEBUG_PARANOID
122 |
123 | //---- Tip: You can add extra functions within the ImGui:: namespace from anywhere (e.g. your own sources/header files)
124 | /*
125 | namespace ImGui
126 | {
127 | void MyFunction(const char* name, MyMatrix44* mtx);
128 | }
129 | */
130 |
--------------------------------------------------------------------------------
/Profiler.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #ifndef WITH_PROFILING
4 | #define WITH_PROFILING 1
5 | #endif
6 |
7 | #if !WITH_PROFILING
8 |
9 | #define PROFILE_REGISTER_THREAD(...)
10 | #define PROFILE_FRAME()
11 | #define PROFILE_EXECUTE_COMMANDLISTS(...)
12 |
13 | #define PROFILE_CPU_SCOPE(...)
14 | #define PROFILE_CPU_BEGIN(...)
15 | #define PROFILE_CPU_END()
16 |
17 | #define PROFILE_GPU_SCOPE(...)
18 | #define PROFILE_GPU_BEGIN(...)
19 | #define PROFILE_GPU_END()
20 |
21 | #define PROFILE_PRESENT(...)
22 |
23 | #else
24 |
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 | #include
32 | #include
33 |
34 | void DrawProfilerHUD();
35 |
36 | void HandleAssertMessage(const char* pMessage);
37 |
38 | template
39 | static void HandleAssertMessage(const char* pExpression, const char* pFileName, int line, const char* pFmt = nullptr, Args... args)
40 | {
41 | char message[1024]{};
42 | if (pFmt)
43 | sprintf_s(message, pFmt, std::forward(args)...);
44 | char finalMessage[1024]{};
45 | sprintf_s(finalMessage, "ASSERT FAILED:\nExpression: %s\nFile: %s:%d\n%s\n", pExpression, __FILE__, __LINE__, message);
46 | HandleAssertMessage(finalMessage);
47 | }
48 |
49 | #define gAssert(op, ...) \
50 | do \
51 | { \
52 | bool result = (op); \
53 | if (result == false) \
54 | { \
55 | HandleAssertMessage(#op, __FILE__, __LINE__, __VA_ARGS__); \
56 | __debugbreak(); \
57 | } \
58 | } while (false)
59 |
60 | #define gVerify(op, expected, ...) \
61 | do \
62 | { \
63 | auto r = (op); \
64 | gAssert(r expected, __VA_ARGS__); \
65 | } while (false)
66 |
67 | #define gVerifyHR(op) gVerify(op, == S_OK, "HRESULT Failed")
68 | #define gBoundCheck(val, minV, maxV) gAssert(val >= minV && val < maxV, "Value out of bounds")
69 |
70 | #define _STRINGIFY(a) #a
71 | #define STRINGIFY(a) _STRINGIFY(a)
72 | #define CONCAT_IMPL(x, y) x##y
73 | #define MACRO_CONCAT(x, y) CONCAT_IMPL(x, y)
74 |
75 | // Basic types
76 | using uint64 = uint64_t;
77 | using uint32 = uint32_t;
78 | using uint16 = uint16_t;
79 | using uint8 = uint8_t;
80 | template
81 | using Span = std::span;
82 | template
83 | using Array = std::vector;
84 | template
85 | using StaticArray = std::array;
86 | template
87 | using HashMap = std::unordered_map;
88 | using Mutex = std::mutex;
89 |
90 | struct URange
91 | {
92 | uint32 Begin = 0;
93 | uint32 End = 0;
94 | uint32 GetLength() const { return End - Begin; }
95 | };
96 |
97 | // Forward declare D3D12 types
98 | struct ID3D12CommandList;
99 | struct ID3D12GraphicsCommandList;
100 | struct ID3D12CommandQueue;
101 | struct ID3D12Device;
102 | struct ID3D12Resource;
103 | struct ID3D12CommandAllocator;
104 | struct ID3D12QueryHeap;
105 | struct ID3D12Fence;
106 | struct IDXGISwapChain;
107 | using WinHandle = void*;
108 |
109 |
110 | /*
111 | General
112 | */
113 |
114 | // Usage:
115 | // PROFILE_REGISTER_THREAD(const char* pName)
116 | // PROFILE_REGISTER_THREAD()
117 | #define PROFILE_REGISTER_THREAD(...) gProfiler.RegisterCurrentThread(__VA_ARGS__)
118 |
119 | /// Usage:
120 | // PROFILE_FRAME()
121 | #define PROFILE_FRAME() \
122 | gProfiler.Tick(); \
123 | gGPUProfiler.Tick()
124 |
125 | /// Usage:
126 | /// PROFILE_EXECUTE_COMMANDLISTS(ID3D12CommandQueue* pQueue, Span commandLists)
127 | #define PROFILE_EXECUTE_COMMANDLISTS(queue, cmdlists) gGPUProfiler.ExecuteCommandLists(queue, cmdlists)
128 |
129 | /*
130 | CPU Profiling
131 | */
132 |
133 | // Usage:
134 | // PROFILE_CPU_SCOPE(const char* pName)
135 | // PROFILE_CPU_SCOPE()
136 | #define PROFILE_CPU_SCOPE(...) CPUProfileScope MACRO_CONCAT(profiler, __COUNTER__)(__FUNCTION__, __FILE__, __LINE__, __VA_ARGS__)
137 |
138 | // Usage:
139 | // PROFILE_CPU_BEGIN(const char* pName)
140 | // PROFILE_CPU_BEGIN()
141 | #define PROFILE_CPU_BEGIN(...) gProfiler.BeginEvent(__VA_ARGS__)
142 | // Usage:
143 | // PROFILE_CPU_END()
144 | #define PROFILE_CPU_END() gProfiler.EndEvent()
145 |
146 |
147 | #define PROFILE_PRESENT(swapchain) gProfiler.Present(swapchain)
148 |
149 |
150 | /*
151 | GPU Profiling
152 | */
153 |
154 | // Usage:
155 | // PROFILE_GPU_SCOPE(ID3D12GraphicsCommandList* pCommandList, const char* pName)
156 | // PROFILE_GPU_SCOPE(ID3D12GraphicsCommandList* pCommandList)
157 | #define PROFILE_GPU_SCOPE(cmdlist, ...) GPUProfileScope MACRO_CONCAT(gpu_profiler, __COUNTER__)(__FUNCTION__, __FILE__, __LINE__, cmdlist, __VA_ARGS__)
158 |
159 | // Usage:
160 | // PROFILE_GPU_BEGIN(const char* pName, ID3D12GraphicsCommandList* pCommandList)
161 | #define PROFILE_GPU_BEGIN(cmdlist, name) gGPUProfiler.BeginEvent(cmdlist, name, __FILE__, __LINE__)
162 |
163 | // Usage:
164 | // PROFILE_GPU_END(ID3D12GraphicsCommandList* pCommandList)
165 | #define PROFILE_GPU_END(cmdlist) gGPUProfiler.EndEvent(cmdlist)
166 |
167 |
168 |
169 | template
170 | struct FixedArray
171 | {
172 | public:
173 | T& Pop()
174 | {
175 | gAssert(Length > 0);
176 | --Length;
177 | return Data[Length];
178 | }
179 |
180 | T& Push()
181 | {
182 | Length++;
183 | gAssert(Length < N);
184 | return Data[Length - 1];
185 | }
186 |
187 | T& Top()
188 | {
189 | gAssert(Length > 0);
190 | return Data[Length - 1];
191 | }
192 |
193 | uint32 GetSize() const { return Length; }
194 |
195 | private:
196 | uint32 Length = 0;
197 | StaticArray Data{};
198 | };
199 |
200 |
201 |
202 | // Single event
203 | struct ProfilerEvent
204 | {
205 | const char* pName = nullptr; ///< Name of event
206 | const char* pFilePath = nullptr; ///< File path of location where event was started
207 | uint32 Color : 24 = 0xFFFFFF; ///< Color
208 | uint32 Depth : 8 = 0; ///< Stack depth of event
209 | uint32 LineNumber : 18 = 0; ///< Line number of file where event was started
210 | uint32 ThreadIndex : 10 = 0; ///< Index of thread this event is started on
211 | uint32 QueueIndex : 4 = 0; ///< GPU Queue Index (GPU-specific)
212 | uint64 TicksBegin = 0; ///< Begin CPU ticks
213 | uint64 TicksEnd = 0; ///< End CPU ticks
214 |
215 | bool IsValid() const { return TicksBegin != 0 && TicksEnd != 0; }
216 | uint32 GetColor() const { return Color | (0xFF << 24); }
217 | };
218 | static_assert(std::has_unique_object_representations_v);
219 |
220 |
221 | // Data for a single frame of profiling events
222 | using ProfilerEventData = Array;
223 |
224 |
225 | //-----------------------------------------------------------------------------
226 | // [SECTION] GPU Profiler
227 | //-----------------------------------------------------------------------------
228 |
229 | extern class GPUProfiler gGPUProfiler;
230 |
231 | struct GPUProfilerCallbacks
232 | {
233 | using EventBeginFn = void (*)(const char* /*pName*/, ID3D12GraphicsCommandList* /*CommandList*/, void* /*pUserData*/);
234 | using EventEndFn = void (*)(ID3D12GraphicsCommandList* /*CommandList*/, void* /*pUserData*/);
235 |
236 | EventBeginFn OnEventBegin = nullptr;
237 | EventEndFn OnEventEnd = nullptr;
238 | void* pUserData = nullptr;
239 | };
240 |
241 | class GPUProfiler
242 | {
243 | public:
244 | void Initialize(ID3D12Device* pDevice, Span queues, uint32 frameLatency);
245 |
246 | void Shutdown();
247 |
248 | // Allocate and record a GPU event on the commandlist
249 | void BeginEvent(ID3D12GraphicsCommandList* pCmd, const char* pName, uint32 color, const char* pFilePath, uint32 lineNumber);
250 |
251 | // Allocate and record a GPU event on the commandlist
252 | void BeginEvent(ID3D12GraphicsCommandList* pCmd, const char* pName, uint32 color = 0) { BeginEvent(pCmd, pName, color, "", 0); }
253 |
254 | // Record a GPU event end on the commandlist
255 | void EndEvent(ID3D12GraphicsCommandList* pCmd);
256 |
257 | // Resolve the last frame and advance to the next frame.
258 | // Call at the START of the frame.
259 | void Tick();
260 |
261 | // Notify profiler that these commandlists are executed on a particular queue
262 | void ExecuteCommandLists(const ID3D12CommandQueue* pQueue, Span commandLists);
263 |
264 | void SetPaused(bool paused) { m_PauseQueued = paused; }
265 |
266 | // Data of a single GPU queue. Allows converting GPU timestamps to CPU timestamps
267 | struct QueueInfo
268 | {
269 | char Name[128]{}; ///< Name of the queue
270 | ID3D12CommandQueue* pQueue = nullptr; ///< The D3D queue object
271 | uint64 GPUCalibrationTicks = 0; ///< The number of GPU ticks when the calibration was done
272 | uint64 CPUCalibrationTicks = 0; ///< The number of CPU ticks when the calibration was done
273 | uint64 GPUFrequency = 0; ///< The GPU tick frequency
274 | uint32 Index = 0; ///< Index of queue
275 | uint32 QueryHeapIndex = 0; ///< Query Heap index (Copy vs. Other queues)
276 | uint32 TrackIndex = 0; ///< The index in the tracks of the profiler
277 | };
278 |
279 | Span GetQueues() const { return m_Queues; }
280 |
281 | void SetEventCallback(const GPUProfilerCallbacks& inCallbacks) { m_EventCallback = inCallbacks; }
282 |
283 | private:
284 | struct QueryHeap
285 | {
286 | public:
287 | void Initialize(ID3D12Device* pDevice, ID3D12CommandQueue* pResolveQueue, uint32 maxNumQueries, uint32 frameLatency);
288 | void Shutdown();
289 |
290 | uint32 RecordQuery(ID3D12GraphicsCommandList* pCmd);
291 | uint32 Resolve(uint32 frameIndex);
292 | void Reset(uint32 frameIndex);
293 | bool IsFrameComplete(uint64 frameIndex);
294 | uint32 GetQueryCapacity() const { return m_MaxNumQueries; }
295 |
296 | Span GetQueryData(uint32 frameIndex) const
297 | {
298 | if (!IsInitialized())
299 | return {};
300 |
301 | uint32 frameBit = frameIndex % m_FrameLatency;
302 | return m_ReadbackData.subspan(frameBit * m_MaxNumQueries, m_MaxNumQueries);
303 | }
304 |
305 | bool IsInitialized() const { return m_pQueryHeap != nullptr; }
306 | ID3D12QueryHeap* GetHeap() const { return m_pQueryHeap; }
307 |
308 | private:
309 | Array m_CommandAllocators; ///< CommandAlloctors to resolve queries. 1 per frame
310 | uint32 m_MaxNumQueries = 0; ///< Max number of event queries
311 | uint32 m_FrameLatency = 0; ///< Number of GPU frame latency
312 | std::atomic m_QueryIndex = 0; ///< Current index of queries
313 | ID3D12GraphicsCommandList* m_pCommandList = nullptr; ///< CommandList to resolve queries
314 | ID3D12QueryHeap* m_pQueryHeap = nullptr; ///< Heap containing MaxNumQueries * FrameLatency queries
315 | ID3D12Resource* m_pReadbackResource = nullptr; ///< Readback resource storing resolved query dara
316 | Span m_ReadbackData = {}; ///< Mapped readback resource pointer
317 | ID3D12CommandQueue* m_pResolveQueue = nullptr; ///< Queue to resolve queries on
318 | ID3D12Fence* m_pResolveFence = nullptr; ///< Fence for tracking when queries are finished resolving
319 | uint64 m_LastCompletedFence = 0; ///< Last finish fence value
320 | };
321 |
322 | // Data for a single frame of GPU queries. One for each frame latency
323 | struct QueryData
324 | {
325 | constexpr static uint32 cQueryIndexBits = 16u;
326 | constexpr static uint32 cMaxNumQueries = (1u << cQueryIndexBits) - 1u;
327 |
328 | struct QueryPair
329 | {
330 | uint32 QueryIndexBegin : cQueryIndexBits = 0xFFFF;
331 | uint32 QueryIndexEnd : cQueryIndexBits = 0xFFFF;
332 |
333 | bool IsValid() const { return QueryIndexBegin != 0xFFFF && QueryIndexEnd != 0xFFFF; }
334 | };
335 | static_assert(sizeof(QueryPair) == sizeof(uint32));
336 |
337 | struct Query
338 | {
339 | static constexpr uint32 EndEventFlag = 0xFFFE;
340 | static constexpr uint32 InvalidEventFlag = 0xFFFF;
341 |
342 | uint32 QueryIndex : QueryData::cQueryIndexBits = InvalidEventFlag; ///< The index into the query heap
343 | uint32 EventIndex : 16 = InvalidEventFlag; ///< The ProfilerEvent index. 0xFFFE is it is an "EndEvent"
344 | };
345 | static_assert(sizeof(Query) == sizeof(uint32));
346 | static_assert(std::has_unique_object_representations_v);
347 |
348 | Array Pairs;
349 | ProfilerEventData Events;
350 | uint32 NumEvents = 0;
351 | };
352 | QueryData& GetQueryData(uint32 frameIndex) { return m_QueryData[frameIndex % m_FrameLatency]; }
353 | QueryData& GetQueryData() { return GetQueryData(m_FrameIndex); }
354 |
355 | // Contains the state for a tracked commandlist
356 | struct CommandListState
357 | {
358 | CommandListState(GPUProfiler* profiler, ID3D12CommandList* pCmd);
359 | ~CommandListState();
360 |
361 | GPUProfiler* pProfiler = nullptr;
362 | ID3D12CommandList* pCommandList = nullptr;
363 | uint32 DestructionEventID = 0;
364 | Array Queries;
365 | };
366 |
367 | CommandListState* GetState(ID3D12CommandList* pCmd, bool createIfNotFound);
368 |
369 | uint64 ConvertToCPUTicks(const QueueInfo& queue, uint64 gpuTicks) const
370 | {
371 | gAssert(gpuTicks >= queue.GPUCalibrationTicks);
372 | return queue.CPUCalibrationTicks + (gpuTicks - queue.GPUCalibrationTicks) * m_CPUTickFrequency / queue.GPUFrequency;
373 | }
374 |
375 | QueryHeap& GetHeap(bool isCopyQueue) { return isCopyQueue ? m_QueryHeaps[1] : m_QueryHeaps[0]; }
376 |
377 | bool m_IsInitialized = false;
378 | bool m_IsPaused = false;
379 | bool m_PauseQueued = false;
380 |
381 | Array m_QueryData; ///< Data containing all intermediate query event data. 1 per frame latency
382 | std::atomic m_EventIndex = 0; ///< Current event index
383 | uint32 m_FrameLatency = 0; ///< Max number of in-flight GPU frames
384 | uint32 m_FrameToReadback = 0; ///< Next frame to readback from
385 | uint32 m_FrameIndex = 0; ///< Current frame index
386 | StaticArray m_QueryHeaps; ///< GPU Query Heaps
387 | uint64 m_CPUTickFrequency = 0; ///< Tick frequency of CPU for QPC
388 | Mutex m_QueryRangeLock;
389 |
390 | WinHandle m_CommandListMapLock{}; ///< Lock for accessing commandlist state hashmap
391 | HashMap m_CommandListMap; ///< Maps commandlist to index
392 |
393 | static constexpr uint32 MAX_EVENT_DEPTH = 32;
394 | using ActiveEventStack = FixedArray;
395 | Array m_QueueEventStack; ///< Stack of active events for each command queue
396 | Array m_Queues; ///< All registered queues
397 | HashMap m_QueueIndexMap; ///< Map from command queue to index
398 | GPUProfilerCallbacks m_EventCallback;
399 | };
400 |
401 | // Helper RAII-style structure to push and pop a GPU sample event
402 | struct GPUProfileScope
403 | {
404 | GPUProfileScope(const char* pFunction, const char* pFilePath, uint32 lineNumber, ID3D12GraphicsCommandList* pCmd, const char* pName)
405 | : pCmd(pCmd)
406 | {
407 | gGPUProfiler.BeginEvent(pCmd, pName, 0, pFilePath, lineNumber);
408 | }
409 |
410 | GPUProfileScope(const char* pFunction, const char* pFilePath, uint32 lineNumber, ID3D12GraphicsCommandList* pCmd)
411 | : pCmd(pCmd)
412 | {
413 | gGPUProfiler.BeginEvent(pCmd, pFunction, 0, pFilePath, lineNumber);
414 | }
415 |
416 | ~GPUProfileScope()
417 | {
418 | gGPUProfiler.EndEvent(pCmd);
419 | }
420 |
421 | GPUProfileScope(const GPUProfileScope&) = delete;
422 | GPUProfileScope& operator=(const GPUProfileScope&) = delete;
423 |
424 | private:
425 | ID3D12GraphicsCommandList* pCmd;
426 | };
427 |
428 |
429 |
430 | //-----------------------------------------------------------------------------
431 | // [SECTION] CPU Profiler
432 | //-----------------------------------------------------------------------------
433 |
434 | // Global CPU Profiler
435 | extern class Profiler gProfiler;
436 |
437 | struct CPUProfilerCallbacks
438 | {
439 | using EventBeginFn = void (*)(const char* /*pName*/, void* /*pUserData*/);
440 | using EventEndFn = void (*)(void* /*pUserData*/);
441 |
442 | EventBeginFn OnEventBegin = nullptr;
443 | EventEndFn OnEventEnd = nullptr;
444 | void* pUserData = nullptr;
445 | };
446 |
447 | // CPU Profiler
448 | // Also responsible for updating GPU profiler
449 | // Also responsible for drawing HUD
450 | class Profiler
451 | {
452 | public:
453 | void Initialize(uint32 historySize);
454 |
455 | void Shutdown();
456 |
457 | // Start and push an event on the current thread
458 | void BeginEvent(const char* pName, uint32 color, const char* pFilePath, uint32 lineNumber);
459 |
460 | // Start and push an event on the current thread
461 | void BeginEvent(const char* pName, uint32 color = 0) { BeginEvent(pName, color, "", 0); }
462 |
463 | // End and pop the last pushed event on the current thread
464 | void EndEvent();
465 |
466 | void AddEvent(uint32 trackIndex, const ProfilerEvent& event, uint32 frameIndex);
467 |
468 | void Present(IDXGISwapChain* pSwapChain);
469 |
470 | bool IsInitialized() const { return m_IsInitialized; }
471 |
472 | // Resolve the last frame and advance to the next frame.
473 | // Call at the START of the frame.
474 | void Tick();
475 |
476 | // Initialize a thread with an optional name
477 | int RegisterCurrentThread(const char* pName = nullptr);
478 |
479 | struct EventTrack
480 | {
481 | enum class EType
482 | {
483 | CPU,
484 | GPU,
485 | Present,
486 | };
487 |
488 | ProfilerEventData& GetFrameData(int frameIndex) { return Events[frameIndex % Events.size()]; }
489 | const ProfilerEventData& GetFrameData(int frameIndex) const { return Events[frameIndex % Events.size()]; }
490 |
491 | char Name[128]{}; ///< Name
492 | uint32 ID = 0; ///< ThreadID (or generic identifier)
493 | uint32 Index = 0; ///< Index in Tracks Array
494 | EType Type = EType::CPU;
495 |
496 | static constexpr int MAX_STACK_DEPTH = 32;
497 | FixedArray EventStack;
498 | Array Events;
499 | };
500 |
501 | // Register a new track
502 | int RegisterTrack(const char* pName, EventTrack::EType type, uint32 id);
503 |
504 | URange GetFrameRange() const
505 | {
506 | uint32 begin = m_FrameIndex - std::min(m_FrameIndex, m_HistorySize) + 1;
507 | uint32 end = m_FrameIndex;
508 | return { .Begin = begin, .End = end };
509 | }
510 |
511 | uint64 GetFirstFrameAnchorTicks() const { return m_BeginFrameTicks[(m_FrameIndex + m_BeginFrameTicks.size() + 1) % m_BeginFrameTicks.size()]; }
512 |
513 | Span GetTracks() const { return m_Tracks; }
514 |
515 | void SetEventCallback(const CPUProfilerCallbacks& inCallbacks) { m_EventCallback = inCallbacks; }
516 | void SetPaused(bool paused) { m_QueuedPaused = paused; }
517 | bool IsPaused() const { return m_Paused; }
518 |
519 | private:
520 | struct PresentEntry
521 | {
522 | constexpr static uint64 sQPCDroppedFrame = ~0ull; ///< QPC when frame is dropped (due to tearing)
523 | constexpr static uint64 sQPCMissedFrame = ~0ull - 1; ///< QPC when frame was missed on CPU (rare)
524 |
525 | uint64 PresentQPC = sQPCDroppedFrame;
526 | uint64 DisplayQPC = sQPCDroppedFrame;
527 | uint32 PresentID = ~0u;
528 | uint32 FrameIndex = 0;
529 | };
530 |
531 | int& GetCurrentThreadTrackIndex()
532 | {
533 | static thread_local int index = -1;
534 | return index;
535 | }
536 |
537 | EventTrack& GetCurrentThreadTrack()
538 | {
539 | int index = GetCurrentThreadTrackIndex();
540 | if (index == -1)
541 | index = RegisterCurrentThread();
542 | return m_Tracks[index];
543 | }
544 |
545 | PresentEntry* GetPresentEntry(uint32 presentID, bool isNewEntry)
546 | {
547 | PresentEntry& entry = m_PresentQueue[presentID % m_PresentQueue.size()];
548 | if (entry.PresentID != presentID && !isNewEntry)
549 | return nullptr;
550 | return &entry;
551 | }
552 |
553 | IDXGISwapChain* m_pPresentSwapChain = nullptr; ///< The swapchain that was last presented with
554 | int m_PresentTrackIndex = -1; ///< The track index for the present timeline
555 | StaticArray m_PresentQueue = {}; ///< Queue to register information whenever Present is called
556 | uint32 m_LastQueuedPresentID = 0; ///< PresentID after the last Present was called
557 | uint32 m_LastQueriedPresentID = 0; ///< The last PresentID which GetFrameStatistics has provided data for
558 | uint32 m_LastProcessedValidPresentID = 0; ///< The last PresentID which GetFrameStatistics has provided data for
559 | uint32 m_LastSyncRefreshCount = 0; ///< The SyncRefreshCount of the last queried frame statistics
560 | uint32 m_LastProcessedPresentID = 0; ///< The last PresentID which was processed to an even
561 | uint32 m_MsToTicks = 0; ///< The amount of ticks in 1 ms
562 |
563 | CPUProfilerCallbacks m_EventCallback;
564 | Mutex m_ThreadDataLock; ///< Mutex for accessing thread data
565 | Array m_Tracks;
566 | Array m_BeginFrameTicks;
567 | uint32 m_HistorySize = 0; ///< History size
568 | uint32 m_FrameIndex = 0; ///< The current frame index
569 | bool m_Paused = false; ///< The current pause state
570 | bool m_QueuedPaused = false; ///< The queued pause state
571 | bool m_IsInitialized = false;
572 | };
573 |
574 | // Helper RAII-style structure to push and pop a CPU sample region
575 | struct CPUProfileScope
576 | {
577 | CPUProfileScope(const char* pFunctionName, const char* pFilePath, uint32 lineNumber, const char* pName, uint32 color = 0)
578 | {
579 | gProfiler.BeginEvent(pName, color, pFilePath, lineNumber);
580 | }
581 |
582 | CPUProfileScope(const char* pFunctionName, const char* pFilePath, uint32 lineNumber, uint32 color = 0)
583 | {
584 | gProfiler.BeginEvent(pFunctionName, color, pFilePath, lineNumber);
585 | }
586 |
587 | ~CPUProfileScope()
588 | {
589 | gProfiler.EndEvent();
590 | }
591 |
592 | CPUProfileScope(const CPUProfileScope&) = delete;
593 | CPUProfileScope& operator=(const CPUProfileScope&) = delete;
594 | };
595 |
596 | #endif
--------------------------------------------------------------------------------
/ImGui/imstb_rectpack.h:
--------------------------------------------------------------------------------
1 | // [DEAR IMGUI]
2 | // This is a slightly modified version of stb_rect_pack.h 1.01.
3 | // Grep for [DEAR IMGUI] to find the changes.
4 | //
5 | // stb_rect_pack.h - v1.01 - public domain - rectangle packing
6 | // Sean Barrett 2014
7 | //
8 | // Useful for e.g. packing rectangular textures into an atlas.
9 | // Does not do rotation.
10 | //
11 | // Before #including,
12 | //
13 | // #define STB_RECT_PACK_IMPLEMENTATION
14 | //
15 | // in the file that you want to have the implementation.
16 | //
17 | // Not necessarily the awesomest packing method, but better than
18 | // the totally naive one in stb_truetype (which is primarily what
19 | // this is meant to replace).
20 | //
21 | // Has only had a few tests run, may have issues.
22 | //
23 | // More docs to come.
24 | //
25 | // No memory allocations; uses qsort() and assert() from stdlib.
26 | // Can override those by defining STBRP_SORT and STBRP_ASSERT.
27 | //
28 | // This library currently uses the Skyline Bottom-Left algorithm.
29 | //
30 | // Please note: better rectangle packers are welcome! Please
31 | // implement them to the same API, but with a different init
32 | // function.
33 | //
34 | // Credits
35 | //
36 | // Library
37 | // Sean Barrett
38 | // Minor features
39 | // Martins Mozeiko
40 | // github:IntellectualKitty
41 | //
42 | // Bugfixes / warning fixes
43 | // Jeremy Jaussaud
44 | // Fabian Giesen
45 | //
46 | // Version history:
47 | //
48 | // 1.01 (2021-07-11) always use large rect mode, expose STBRP__MAXVAL in public section
49 | // 1.00 (2019-02-25) avoid small space waste; gracefully fail too-wide rectangles
50 | // 0.99 (2019-02-07) warning fixes
51 | // 0.11 (2017-03-03) return packing success/fail result
52 | // 0.10 (2016-10-25) remove cast-away-const to avoid warnings
53 | // 0.09 (2016-08-27) fix compiler warnings
54 | // 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0)
55 | // 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0)
56 | // 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort
57 | // 0.05: added STBRP_ASSERT to allow replacing assert
58 | // 0.04: fixed minor bug in STBRP_LARGE_RECTS support
59 | // 0.01: initial release
60 | //
61 | // LICENSE
62 | //
63 | // See end of file for license information.
64 |
65 | //////////////////////////////////////////////////////////////////////////////
66 | //
67 | // INCLUDE SECTION
68 | //
69 |
70 | #ifndef STB_INCLUDE_STB_RECT_PACK_H
71 | #define STB_INCLUDE_STB_RECT_PACK_H
72 |
73 | #define STB_RECT_PACK_VERSION 1
74 |
75 | #ifdef STBRP_STATIC
76 | #define STBRP_DEF static
77 | #else
78 | #define STBRP_DEF extern
79 | #endif
80 |
81 | #ifdef __cplusplus
82 | extern "C" {
83 | #endif
84 |
85 | typedef struct stbrp_context stbrp_context;
86 | typedef struct stbrp_node stbrp_node;
87 | typedef struct stbrp_rect stbrp_rect;
88 |
89 | typedef int stbrp_coord;
90 |
91 | #define STBRP__MAXVAL 0x7fffffff
92 | // Mostly for internal use, but this is the maximum supported coordinate value.
93 |
94 | STBRP_DEF int stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects);
95 | // Assign packed locations to rectangles. The rectangles are of type
96 | // 'stbrp_rect' defined below, stored in the array 'rects', and there
97 | // are 'num_rects' many of them.
98 | //
99 | // Rectangles which are successfully packed have the 'was_packed' flag
100 | // set to a non-zero value and 'x' and 'y' store the minimum location
101 | // on each axis (i.e. bottom-left in cartesian coordinates, top-left
102 | // if you imagine y increasing downwards). Rectangles which do not fit
103 | // have the 'was_packed' flag set to 0.
104 | //
105 | // You should not try to access the 'rects' array from another thread
106 | // while this function is running, as the function temporarily reorders
107 | // the array while it executes.
108 | //
109 | // To pack into another rectangle, you need to call stbrp_init_target
110 | // again. To continue packing into the same rectangle, you can call
111 | // this function again. Calling this multiple times with multiple rect
112 | // arrays will probably produce worse packing results than calling it
113 | // a single time with the full rectangle array, but the option is
114 | // available.
115 | //
116 | // The function returns 1 if all of the rectangles were successfully
117 | // packed and 0 otherwise.
118 |
119 | struct stbrp_rect
120 | {
121 | // reserved for your use:
122 | int id;
123 |
124 | // input:
125 | stbrp_coord w, h;
126 |
127 | // output:
128 | stbrp_coord x, y;
129 | int was_packed; // non-zero if valid packing
130 |
131 | }; // 16 bytes, nominally
132 |
133 |
134 | STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes);
135 | // Initialize a rectangle packer to:
136 | // pack a rectangle that is 'width' by 'height' in dimensions
137 | // using temporary storage provided by the array 'nodes', which is 'num_nodes' long
138 | //
139 | // You must call this function every time you start packing into a new target.
140 | //
141 | // There is no "shutdown" function. The 'nodes' memory must stay valid for
142 | // the following stbrp_pack_rects() call (or calls), but can be freed after
143 | // the call (or calls) finish.
144 | //
145 | // Note: to guarantee best results, either:
146 | // 1. make sure 'num_nodes' >= 'width'
147 | // or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1'
148 | //
149 | // If you don't do either of the above things, widths will be quantized to multiples
150 | // of small integers to guarantee the algorithm doesn't run out of temporary storage.
151 | //
152 | // If you do #2, then the non-quantized algorithm will be used, but the algorithm
153 | // may run out of temporary storage and be unable to pack some rectangles.
154 |
155 | STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem);
156 | // Optionally call this function after init but before doing any packing to
157 | // change the handling of the out-of-temp-memory scenario, described above.
158 | // If you call init again, this will be reset to the default (false).
159 |
160 |
161 | STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic);
162 | // Optionally select which packing heuristic the library should use. Different
163 | // heuristics will produce better/worse results for different data sets.
164 | // If you call init again, this will be reset to the default.
165 |
166 | enum
167 | {
168 | STBRP_HEURISTIC_Skyline_default=0,
169 | STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default,
170 | STBRP_HEURISTIC_Skyline_BF_sortHeight
171 | };
172 |
173 |
174 | //////////////////////////////////////////////////////////////////////////////
175 | //
176 | // the details of the following structures don't matter to you, but they must
177 | // be visible so you can handle the memory allocations for them
178 |
179 | struct stbrp_node
180 | {
181 | stbrp_coord x,y;
182 | stbrp_node *next;
183 | };
184 |
185 | struct stbrp_context
186 | {
187 | int width;
188 | int height;
189 | int align;
190 | int init_mode;
191 | int heuristic;
192 | int num_nodes;
193 | stbrp_node *active_head;
194 | stbrp_node *free_head;
195 | stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2'
196 | };
197 |
198 | #ifdef __cplusplus
199 | }
200 | #endif
201 |
202 | #endif
203 |
204 | //////////////////////////////////////////////////////////////////////////////
205 | //
206 | // IMPLEMENTATION SECTION
207 | //
208 |
209 | #ifdef STB_RECT_PACK_IMPLEMENTATION
210 | #ifndef STBRP_SORT
211 | #include
212 | #define STBRP_SORT qsort
213 | #endif
214 |
215 | #ifndef STBRP_ASSERT
216 | #include
217 | #define STBRP_ASSERT assert
218 | #endif
219 |
220 | #ifdef _MSC_VER
221 | #define STBRP__NOTUSED(v) (void)(v)
222 | #define STBRP__CDECL __cdecl
223 | #else
224 | #define STBRP__NOTUSED(v) (void)sizeof(v)
225 | #define STBRP__CDECL
226 | #endif
227 |
228 | enum
229 | {
230 | STBRP__INIT_skyline = 1
231 | };
232 |
233 | STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic)
234 | {
235 | switch (context->init_mode) {
236 | case STBRP__INIT_skyline:
237 | STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight);
238 | context->heuristic = heuristic;
239 | break;
240 | default:
241 | STBRP_ASSERT(0);
242 | }
243 | }
244 |
245 | STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem)
246 | {
247 | if (allow_out_of_mem)
248 | // if it's ok to run out of memory, then don't bother aligning them;
249 | // this gives better packing, but may fail due to OOM (even though
250 | // the rectangles easily fit). @TODO a smarter approach would be to only
251 | // quantize once we've hit OOM, then we could get rid of this parameter.
252 | context->align = 1;
253 | else {
254 | // if it's not ok to run out of memory, then quantize the widths
255 | // so that num_nodes is always enough nodes.
256 | //
257 | // I.e. num_nodes * align >= width
258 | // align >= width / num_nodes
259 | // align = ceil(width/num_nodes)
260 |
261 | context->align = (context->width + context->num_nodes-1) / context->num_nodes;
262 | }
263 | }
264 |
265 | STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes)
266 | {
267 | int i;
268 |
269 | for (i=0; i < num_nodes-1; ++i)
270 | nodes[i].next = &nodes[i+1];
271 | nodes[i].next = NULL;
272 | context->init_mode = STBRP__INIT_skyline;
273 | context->heuristic = STBRP_HEURISTIC_Skyline_default;
274 | context->free_head = &nodes[0];
275 | context->active_head = &context->extra[0];
276 | context->width = width;
277 | context->height = height;
278 | context->num_nodes = num_nodes;
279 | stbrp_setup_allow_out_of_mem(context, 0);
280 |
281 | // node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly)
282 | context->extra[0].x = 0;
283 | context->extra[0].y = 0;
284 | context->extra[0].next = &context->extra[1];
285 | context->extra[1].x = (stbrp_coord) width;
286 | context->extra[1].y = (1<<30);
287 | context->extra[1].next = NULL;
288 | }
289 |
290 | // find minimum y position if it starts at x1
291 | static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste)
292 | {
293 | stbrp_node *node = first;
294 | int x1 = x0 + width;
295 | int min_y, visited_width, waste_area;
296 |
297 | STBRP__NOTUSED(c);
298 |
299 | STBRP_ASSERT(first->x <= x0);
300 |
301 | #if 0
302 | // skip in case we're past the node
303 | while (node->next->x <= x0)
304 | ++node;
305 | #else
306 | STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency
307 | #endif
308 |
309 | STBRP_ASSERT(node->x <= x0);
310 |
311 | min_y = 0;
312 | waste_area = 0;
313 | visited_width = 0;
314 | while (node->x < x1) {
315 | if (node->y > min_y) {
316 | // raise min_y higher.
317 | // we've accounted for all waste up to min_y,
318 | // but we'll now add more waste for everything we've visted
319 | waste_area += visited_width * (node->y - min_y);
320 | min_y = node->y;
321 | // the first time through, visited_width might be reduced
322 | if (node->x < x0)
323 | visited_width += node->next->x - x0;
324 | else
325 | visited_width += node->next->x - node->x;
326 | } else {
327 | // add waste area
328 | int under_width = node->next->x - node->x;
329 | if (under_width + visited_width > width)
330 | under_width = width - visited_width;
331 | waste_area += under_width * (min_y - node->y);
332 | visited_width += under_width;
333 | }
334 | node = node->next;
335 | }
336 |
337 | *pwaste = waste_area;
338 | return min_y;
339 | }
340 |
341 | typedef struct
342 | {
343 | int x,y;
344 | stbrp_node **prev_link;
345 | } stbrp__findresult;
346 |
347 | static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height)
348 | {
349 | int best_waste = (1<<30), best_x, best_y = (1 << 30);
350 | stbrp__findresult fr;
351 | stbrp_node **prev, *node, *tail, **best = NULL;
352 |
353 | // align to multiple of c->align
354 | width = (width + c->align - 1);
355 | width -= width % c->align;
356 | STBRP_ASSERT(width % c->align == 0);
357 |
358 | // if it can't possibly fit, bail immediately
359 | if (width > c->width || height > c->height) {
360 | fr.prev_link = NULL;
361 | fr.x = fr.y = 0;
362 | return fr;
363 | }
364 |
365 | node = c->active_head;
366 | prev = &c->active_head;
367 | while (node->x + width <= c->width) {
368 | int y,waste;
369 | y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste);
370 | if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL
371 | // bottom left
372 | if (y < best_y) {
373 | best_y = y;
374 | best = prev;
375 | }
376 | } else {
377 | // best-fit
378 | if (y + height <= c->height) {
379 | // can only use it if it first vertically
380 | if (y < best_y || (y == best_y && waste < best_waste)) {
381 | best_y = y;
382 | best_waste = waste;
383 | best = prev;
384 | }
385 | }
386 | }
387 | prev = &node->next;
388 | node = node->next;
389 | }
390 |
391 | best_x = (best == NULL) ? 0 : (*best)->x;
392 |
393 | // if doing best-fit (BF), we also have to try aligning right edge to each node position
394 | //
395 | // e.g, if fitting
396 | //
397 | // ____________________
398 | // |____________________|
399 | //
400 | // into
401 | //
402 | // | |
403 | // | ____________|
404 | // |____________|
405 | //
406 | // then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned
407 | //
408 | // This makes BF take about 2x the time
409 |
410 | if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) {
411 | tail = c->active_head;
412 | node = c->active_head;
413 | prev = &c->active_head;
414 | // find first node that's admissible
415 | while (tail->x < width)
416 | tail = tail->next;
417 | while (tail) {
418 | int xpos = tail->x - width;
419 | int y,waste;
420 | STBRP_ASSERT(xpos >= 0);
421 | // find the left position that matches this
422 | while (node->next->x <= xpos) {
423 | prev = &node->next;
424 | node = node->next;
425 | }
426 | STBRP_ASSERT(node->next->x > xpos && node->x <= xpos);
427 | y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste);
428 | if (y + height <= c->height) {
429 | if (y <= best_y) {
430 | if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) {
431 | best_x = xpos;
432 | //STBRP_ASSERT(y <= best_y); [DEAR IMGUI]
433 | best_y = y;
434 | best_waste = waste;
435 | best = prev;
436 | }
437 | }
438 | }
439 | tail = tail->next;
440 | }
441 | }
442 |
443 | fr.prev_link = best;
444 | fr.x = best_x;
445 | fr.y = best_y;
446 | return fr;
447 | }
448 |
449 | static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height)
450 | {
451 | // find best position according to heuristic
452 | stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height);
453 | stbrp_node *node, *cur;
454 |
455 | // bail if:
456 | // 1. it failed
457 | // 2. the best node doesn't fit (we don't always check this)
458 | // 3. we're out of memory
459 | if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) {
460 | res.prev_link = NULL;
461 | return res;
462 | }
463 |
464 | // on success, create new node
465 | node = context->free_head;
466 | node->x = (stbrp_coord) res.x;
467 | node->y = (stbrp_coord) (res.y + height);
468 |
469 | context->free_head = node->next;
470 |
471 | // insert the new node into the right starting point, and
472 | // let 'cur' point to the remaining nodes needing to be
473 | // stiched back in
474 |
475 | cur = *res.prev_link;
476 | if (cur->x < res.x) {
477 | // preserve the existing one, so start testing with the next one
478 | stbrp_node *next = cur->next;
479 | cur->next = node;
480 | cur = next;
481 | } else {
482 | *res.prev_link = node;
483 | }
484 |
485 | // from here, traverse cur and free the nodes, until we get to one
486 | // that shouldn't be freed
487 | while (cur->next && cur->next->x <= res.x + width) {
488 | stbrp_node *next = cur->next;
489 | // move the current node to the free list
490 | cur->next = context->free_head;
491 | context->free_head = cur;
492 | cur = next;
493 | }
494 |
495 | // stitch the list back in
496 | node->next = cur;
497 |
498 | if (cur->x < res.x + width)
499 | cur->x = (stbrp_coord) (res.x + width);
500 |
501 | #ifdef _DEBUG
502 | cur = context->active_head;
503 | while (cur->x < context->width) {
504 | STBRP_ASSERT(cur->x < cur->next->x);
505 | cur = cur->next;
506 | }
507 | STBRP_ASSERT(cur->next == NULL);
508 |
509 | {
510 | int count=0;
511 | cur = context->active_head;
512 | while (cur) {
513 | cur = cur->next;
514 | ++count;
515 | }
516 | cur = context->free_head;
517 | while (cur) {
518 | cur = cur->next;
519 | ++count;
520 | }
521 | STBRP_ASSERT(count == context->num_nodes+2);
522 | }
523 | #endif
524 |
525 | return res;
526 | }
527 |
528 | static int STBRP__CDECL rect_height_compare(const void *a, const void *b)
529 | {
530 | const stbrp_rect *p = (const stbrp_rect *) a;
531 | const stbrp_rect *q = (const stbrp_rect *) b;
532 | if (p->h > q->h)
533 | return -1;
534 | if (p->h < q->h)
535 | return 1;
536 | return (p->w > q->w) ? -1 : (p->w < q->w);
537 | }
538 |
539 | static int STBRP__CDECL rect_original_order(const void *a, const void *b)
540 | {
541 | const stbrp_rect *p = (const stbrp_rect *) a;
542 | const stbrp_rect *q = (const stbrp_rect *) b;
543 | return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed);
544 | }
545 |
546 | STBRP_DEF int stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects)
547 | {
548 | int i, all_rects_packed = 1;
549 |
550 | // we use the 'was_packed' field internally to allow sorting/unsorting
551 | for (i=0; i < num_rects; ++i) {
552 | rects[i].was_packed = i;
553 | }
554 |
555 | // sort according to heuristic
556 | STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare);
557 |
558 | for (i=0; i < num_rects; ++i) {
559 | if (rects[i].w == 0 || rects[i].h == 0) {
560 | rects[i].x = rects[i].y = 0; // empty rect needs no space
561 | } else {
562 | stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h);
563 | if (fr.prev_link) {
564 | rects[i].x = (stbrp_coord) fr.x;
565 | rects[i].y = (stbrp_coord) fr.y;
566 | } else {
567 | rects[i].x = rects[i].y = STBRP__MAXVAL;
568 | }
569 | }
570 | }
571 |
572 | // unsort
573 | STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order);
574 |
575 | // set was_packed flags and all_rects_packed status
576 | for (i=0; i < num_rects; ++i) {
577 | rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL);
578 | if (!rects[i].was_packed)
579 | all_rects_packed = 0;
580 | }
581 |
582 | // return the all_rects_packed status
583 | return all_rects_packed;
584 | }
585 | #endif
586 |
587 | /*
588 | ------------------------------------------------------------------------------
589 | This software is available under 2 licenses -- choose whichever you prefer.
590 | ------------------------------------------------------------------------------
591 | ALTERNATIVE A - MIT License
592 | Copyright (c) 2017 Sean Barrett
593 | Permission is hereby granted, free of charge, to any person obtaining a copy of
594 | this software and associated documentation files (the "Software"), to deal in
595 | the Software without restriction, including without limitation the rights to
596 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
597 | of the Software, and to permit persons to whom the Software is furnished to do
598 | so, subject to the following conditions:
599 | The above copyright notice and this permission notice shall be included in all
600 | copies or substantial portions of the Software.
601 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
602 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
603 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
604 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
605 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
606 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
607 | SOFTWARE.
608 | ------------------------------------------------------------------------------
609 | ALTERNATIVE B - Public Domain (www.unlicense.org)
610 | This is free and unencumbered software released into the public domain.
611 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
612 | software, either in source code form or as a compiled binary, for any purpose,
613 | commercial or non-commercial, and by any means.
614 | In jurisdictions that recognize copyright laws, the author or authors of this
615 | software dedicate any and all copyright interest in the software to the public
616 | domain. We make this dedication for the benefit of the public at large and to
617 | the detriment of our heirs and successors. We intend this dedication to be an
618 | overt act of relinquishment in perpetuity of all present and future rights to
619 | this software under copyright law.
620 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
621 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
622 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
623 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
624 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
625 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
626 | ------------------------------------------------------------------------------
627 | */
628 |
--------------------------------------------------------------------------------
/ProfilerWindow.cpp:
--------------------------------------------------------------------------------
1 |
2 | #include "Profiler.h"
3 |
4 | #if WITH_PROFILING
5 |
6 | #include "IconsFontAwesome4.h"
7 | #include "IconsFontAwesome4_data.h"
8 | #include "Roboto_data.h"
9 | #include
10 | #include
11 | #include
12 |
13 | #define NOMINMAX
14 | #include
15 |
16 | void HandleAssertMessage(const char* pMessage)
17 | {
18 | printf("%s", pMessage);
19 | OutputDebugStringA(pMessage);
20 | }
21 |
22 | class StringHash
23 | {
24 | private:
25 | static constexpr uint32 val_const{ 0x811c9dc5 };
26 | static constexpr uint32 prime_const{ 0x1000193 };
27 |
28 | static inline constexpr uint32 Hash_Internal(const char* const str, const uint32 value) noexcept
29 | {
30 | if (!str)
31 | return 0;
32 | return (str[0] == '\0') ? value : Hash_Internal(&str[1], (value ^ uint64(str[0])) * prime_const);
33 | }
34 |
35 | public:
36 | static inline constexpr StringHash Hash(const char* const str) noexcept
37 | {
38 | return StringHash(Hash_Internal(str, val_const));
39 | }
40 |
41 | constexpr StringHash(uint32 value = 0) noexcept
42 | : m_Hash(value)
43 | {
44 | }
45 |
46 | constexpr StringHash(const char* const pText) noexcept
47 | : m_Hash(Hash_Internal(pText, val_const))
48 | {
49 | }
50 |
51 | constexpr void Combine(uint32 other)
52 | {
53 | m_Hash ^= other + 0x9e3779b9 + (m_Hash << 6) + (m_Hash >> 2);
54 | }
55 |
56 | inline constexpr operator uint32() const { return m_Hash; }
57 |
58 | inline bool operator==(const StringHash& rhs) const = default;
59 | inline bool operator!=(const StringHash& rhs) const = default;
60 | inline bool operator<(const StringHash& rhs) const = default;
61 | inline bool operator>(const StringHash& rhs) const = default;
62 |
63 | uint32 m_Hash;
64 | };
65 |
66 | struct StyleOptions
67 | {
68 | int MaxDepth = 10;
69 | float MaxTime = 200;
70 |
71 | float BarHeight = 1.5f;
72 | float BarPadding = 2;
73 | float ScrollBarSize = 15.0f;
74 |
75 | ImVec4 BarColorMultiplier = ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
76 | ImVec4 BGTextColor = ImVec4(0.5f, 0.5f, 0.5f, 1.0f);
77 | ImVec4 FGTextColor = ImVec4(0.9f, 0.9f, 0.9f, 1.0f);
78 | ImVec4 BarHighlightColor = ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
79 |
80 | bool DebugMode = false;
81 |
82 | float GetBarHeight() const
83 | {
84 | return BarHeight * ImGui::GetTextLineHeight();
85 | }
86 | };
87 |
88 | struct HUDContext
89 | {
90 | StyleOptions Style;
91 | ImFont* TextFont = nullptr;
92 | ImFont* IconFont = nullptr;
93 |
94 | float TimelineScale = 5.0f;
95 | ImVec2 TimelineOffset = ImVec2(0.0f, 0.0f);
96 |
97 | bool IsSelectingRange = false;
98 | float RangeSelectionStart = 0.0f;
99 | char SearchString[128]{};
100 | bool IsPaused = false;
101 |
102 | struct SelectedStatData
103 | {
104 | StringHash Hash = {};
105 | uint32 NumSamples = 0;
106 |
107 | float MovingAverageTime = 0;
108 | float MinTime = FLT_MAX;
109 | float MaxTime = 0.0f;
110 |
111 | void Set(uint32 hash)
112 | {
113 | Hash = hash;
114 | NumSamples = 0;
115 | MovingAverageTime = 0;
116 | MinTime = FLT_MAX;
117 | MaxTime = 0.0f;
118 | }
119 |
120 | void AddSample(float newSample)
121 | {
122 | ++NumSamples;
123 | MinTime = ImMin(newSample, MinTime);
124 | MaxTime = ImMax(newSample, MaxTime);
125 | MovingAverageTime = MovingAverageTime + (newSample - MovingAverageTime) / NumSamples;
126 | NumSamples %= 4096;
127 | }
128 |
129 | } SelectedEvent;
130 | };
131 |
132 | static HUDContext gHUDContext;
133 | static HUDContext& Context()
134 | {
135 | return gHUDContext;
136 | }
137 |
138 | static void EditStyle(StyleOptions& style)
139 | {
140 | ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x * 0.7f);
141 | ImGui::SliderInt("Depth", &style.MaxDepth, 1, 12);
142 | ImGui::SliderFloat("Max Time", &style.MaxTime, 8, 500, "%.1f");
143 | ImGui::SliderFloat("Bar Height", &style.BarHeight, 1, 4);
144 | ImGui::SliderFloat("Bar Padding", &style.BarPadding, 0, 5);
145 | ImGui::SliderFloat("Scroll Bar Size", &style.ScrollBarSize, 1.0f, 40.0f);
146 | ImGui::ColorEdit4("Bar Color Multiplier", &style.BarColorMultiplier.x);
147 | ImGui::ColorEdit4("Background Text Color", &style.BGTextColor.x);
148 | ImGui::ColorEdit4("Foreground Text Color", &style.FGTextColor.x);
149 | ImGui::ColorEdit4("Bar Highlight Color", &style.BarHighlightColor.x);
150 | ImGui::Separator();
151 | ImGui::Checkbox("Debug Mode", &style.DebugMode);
152 | ImGui::PopItemWidth();
153 | }
154 |
155 | static StringHash GetEventHash(const ProfilerEvent& event)
156 | {
157 | StringHash hash;
158 | hash.Combine(StringHash(event.pName));
159 | hash.Combine(StringHash(event.pFilePath));
160 | hash.Combine(event.LineNumber);
161 | hash.Combine(event.QueueIndex);
162 | return hash;
163 | }
164 |
165 | template
166 | static std::string Sprintf(const char* pFormat, Args... args)
167 | {
168 | static char buffer[1024];
169 | sprintf_s(buffer, pFormat, std::forward(args)...);
170 | return buffer;
171 | }
172 |
173 | struct TraceContext
174 | {
175 | TraceContext()
176 | {
177 | QueryPerformanceCounter((LARGE_INTEGER*)&BaseTime);
178 | }
179 |
180 | std::ofstream TraceStream;
181 | uint64 BaseTime = 0;
182 | };
183 |
184 | void BeginTrace(const char* pPath, TraceContext& context)
185 | {
186 | if (context.TraceStream.is_open())
187 | return;
188 |
189 | context.TraceStream.open(pPath);
190 | context.TraceStream << "{\n\"traceEvents\": [\n";
191 |
192 | context.TraceStream << Sprintf("{\"name\":\"process_name\",\"ph\":\"M\",\"pid\":0,\"args\":{\"name\":\"Track\"}},\n");
193 | for (const Profiler::EventTrack& track : gProfiler.GetTracks())
194 | {
195 | context.TraceStream << Sprintf("{\"name\":\"thread_name\",\"ph\":\"M\",\"pid\":0,\"tid\":%d,\"args\":{\"name\":\"%s\"}},\n", track.Index, track.Name);
196 | }
197 | }
198 |
199 | void UpdateTrace(TraceContext& context)
200 | {
201 | if (!context.TraceStream.is_open())
202 | return;
203 |
204 | uint64 frequency = 0;
205 | QueryPerformanceFrequency((LARGE_INTEGER*)&frequency);
206 | const float TicksToMs = 1000.0f / frequency;
207 |
208 | URange cpuRange = gProfiler.GetFrameRange();
209 | for (const Profiler::EventTrack& track : gProfiler.GetTracks())
210 | {
211 | for (const ProfilerEvent& event : track.GetFrameData(cpuRange.Begin))
212 | context.TraceStream << Sprintf("{\"pid\":0,\"tid\":%d,\"ts\":%d,\"dur\":%d,\"ph\":\"X\",\"name\":\"%s\"},\n", track.Index, (int)(1000 * TicksToMs * (event.TicksBegin - context.BaseTime)), (int)(1000 * TicksToMs * (event.TicksEnd - event.TicksBegin)), event.pName);
213 | }
214 | }
215 |
216 | void EndTrace(TraceContext& context)
217 | {
218 | if (!context.TraceStream.is_open())
219 | return;
220 |
221 | context.TraceStream << "{}]\n}";
222 | context.TraceStream.close();
223 | }
224 |
225 | static void DrawProfilerTimeline(const ImVec2& size = ImVec2(0, 0))
226 | {
227 | PROFILE_CPU_SCOPE();
228 |
229 | HUDContext& context = gHUDContext;
230 | StyleOptions& style = context.Style;
231 |
232 | static TraceContext traceContext;
233 |
234 | UpdateTrace(traceContext);
235 |
236 | ImVec2 sizeActual = ImGui::CalcItemSize(size, ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y);
237 |
238 | ImRect timelineRect(ImGui::GetCursorScreenPos(), ImGui::GetCursorScreenPos() + sizeActual - ImVec2(200, 0));
239 | ImGui::ItemSize(timelineRect.GetSize());
240 |
241 | // The current (scaled) size of the timeline
242 | float timelineWidth = timelineRect.GetWidth() * context.TimelineScale;
243 |
244 | ImVec2 cursor = timelineRect.Min + context.TimelineOffset;
245 | ImVec2 cursorStart = cursor;
246 | ImDrawList* pDraw = ImGui::GetWindowDrawList();
247 |
248 | ImGuiID timelineID = ImGui::GetID("Timeline");
249 | timelineRect.Max -= ImVec2(style.ScrollBarSize, style.ScrollBarSize);
250 | if (ImGui::ItemAdd(timelineRect, timelineID))
251 | {
252 | ImGui::PushClipRect(timelineRect.Min, timelineRect.Max, true);
253 |
254 | // How many ticks per ms
255 | uint64 frequency = 0;
256 | QueryPerformanceFrequency((LARGE_INTEGER*)&frequency);
257 | const float MsToTicks = (float)frequency / 1000.0f;
258 | const float TicksToMs = 1000.0f / frequency;
259 |
260 | // How many ticks are in the timeline
261 | float ticksInTimeline = MsToTicks * style.MaxTime;
262 |
263 | URange cpuRange = gProfiler.GetFrameRange();
264 | uint64 beginAnchor = gProfiler.GetFirstFrameAnchorTicks();
265 |
266 | // How many pixels is one tick
267 | const float TicksToPixels = timelineWidth / ticksInTimeline;
268 |
269 | // Add vertical bars for each ms interval
270 | /*
271 | 0 1 2 3
272 | | | | |
273 | | | | |
274 | | | | |
275 | */
276 | pDraw->AddRectFilled(timelineRect.Min, ImVec2(timelineRect.Max.x, timelineRect.Min.y + style.GetBarHeight()), ImColor(0.0f, 0.0f, 0.0f, 0.1f));
277 | pDraw->AddRect(timelineRect.Min - ImVec2(10, 0), ImVec2(timelineRect.Max.x + 10, timelineRect.Min.y + style.GetBarHeight()), ImColor(1.0f, 1.0f, 1.0f, 0.4f));
278 |
279 | float minIntervalDistance = 80.0f;
280 | float msWidth = 1.0f * MsToTicks * TicksToPixels;
281 | float intervalSize = ceil(minIntervalDistance / msWidth * 2.0f) / 2.0f;
282 |
283 | int markerIdx = 0;
284 | for (float intervalTime = 0; intervalTime < style.MaxTime; intervalTime += intervalSize, ++markerIdx)
285 | {
286 | float x0 = intervalTime * MsToTicks * TicksToPixels;
287 | ImVec2 tickPos = ImVec2(cursor.x + x0, timelineRect.Min.y);
288 | pDraw->AddLine(tickPos + ImVec2(0, style.GetBarHeight() * 0.5f), tickPos + ImVec2(0, style.GetBarHeight()), ImColor(style.BGTextColor));
289 |
290 | const char* pBarText;
291 | ImFormatStringToTempBuffer(&pBarText, nullptr, "%.1f ms", intervalTime);
292 | pDraw->AddText(tickPos + ImVec2(5, 0), ImColor(style.BGTextColor), pBarText);
293 |
294 | if (markerIdx % 2 == 0)
295 | pDraw->AddRectFilled(tickPos + ImVec2(0, style.GetBarHeight()), tickPos + ImVec2(intervalSize * MsToTicks * TicksToPixels, timelineRect.Max.y), ImColor(1.0f, 1.0f, 1.0f, 0.02f));
296 | }
297 |
298 | cursor.y += style.GetBarHeight();
299 |
300 | ImGui::PushClipRect(timelineRect.Min + ImVec2(0, style.GetBarHeight()), timelineRect.Max, true);
301 |
302 | ImRect clipRect = ImGui::GetCurrentWindow()->ClipRect;
303 |
304 | // Draw the bars for a list of profiling events
305 | /*
306 | [=== SomeFunction (1.2 ms) ===]
307 | */
308 | bool anyHovered = false;
309 | auto DrawTrack = [&](Span events, uint32 frameIndex, uint32& outTrackDepth) {
310 | for (const ProfilerEvent& event : events)
311 | {
312 | // Skip events above the max depth
313 | if (!event.IsValid() || event.Depth >= (uint32)style.MaxDepth)
314 | continue;
315 |
316 | outTrackDepth = ImMax(outTrackDepth, (uint32)event.Depth + 1);
317 |
318 | bool hovered = false;
319 | bool clicked = false;
320 | if (event.TicksEnd > beginAnchor)
321 | {
322 | float startPos = (event.TicksBegin < beginAnchor ? 0 : event.TicksBegin - beginAnchor) * TicksToPixels;
323 | float endPos = (event.TicksEnd - beginAnchor) * TicksToPixels;
324 | float y = event.Depth * style.GetBarHeight();
325 | ImRect itemRect = ImRect(cursor + ImVec2(startPos, y), cursor + ImVec2(endPos, y + style.GetBarHeight()));
326 |
327 | // Ensure a bar always has a width
328 | itemRect.Max.x = ImMax(itemRect.Max.x, itemRect.Min.x + 1);
329 |
330 | if (clipRect.Overlaps(itemRect))
331 | {
332 | float ms = TicksToMs * (float)(event.TicksEnd - event.TicksBegin);
333 |
334 | ImColor color = ImColor(event.GetColor()) * style.BarColorMultiplier;
335 | ImColor textColor = style.FGTextColor;
336 | // Fade out the bars that don't match the filter
337 | if (context.SearchString[0] != 0 && !strstr(event.pName, context.SearchString))
338 | {
339 | color.Value.w *= 0.3f;
340 | textColor.Value.w *= 0.5f;
341 | }
342 |
343 | // Darken the bottom
344 | ImColor colorBottom = color.Value * ImVec4(0.8f, 0.8f, 0.8f, 1.0f);
345 |
346 | if (!anyHovered && ImGui::IsMouseHoveringRect(itemRect.Min, itemRect.Max))
347 | {
348 | hovered = true;
349 | anyHovered = true;
350 |
351 | if (ImGui::IsMouseClicked(ImGuiMouseButton_Left))
352 | {
353 | clicked = true;
354 | }
355 |
356 | // If the bar is double-clicked, zoom in to make the bar fill the entire window
357 | if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
358 | {
359 | // Zoom ration to make the bar fit the entire window
360 | float zoom = timelineWidth / itemRect.GetWidth();
361 | context.TimelineScale = zoom;
362 |
363 | // Recompute the timeline size with new zoom
364 | float newTimelineWidth = timelineRect.GetWidth() * context.TimelineScale;
365 | float newTickScale = newTimelineWidth / ticksInTimeline;
366 | float newStartPos = newTickScale * (event.TicksBegin - beginAnchor);
367 |
368 | context.TimelineOffset.x = -newStartPos;
369 | }
370 | }
371 |
372 | // Draw the bar rect and outline if hovered
373 |
374 | // Only pad if bar is large enough
375 | float maxPaddingX = ImMax(itemRect.GetWidth() * 0.5f - 1.0f, 0.0f);
376 | const ImVec2 padding(ImMin(style.BarPadding, maxPaddingX), style.BarPadding);
377 | if (hovered)
378 | {
379 | ImColor highlightColor = color.Value * ImVec4(1.5f, 1.5f, 1.5f, 1.0f);
380 | color.Value = color.Value * ImVec4(1.2f, 1.2f, 1.2f, 1.0f);
381 | colorBottom.Value = colorBottom.Value * ImVec4(1.2f, 1.2f, 1.2f, 1.0f);
382 | pDraw->AddRectFilledMultiColor(itemRect.Min + padding, itemRect.Max - padding, color, color, colorBottom, colorBottom);
383 | pDraw->AddRect(itemRect.Min, itemRect.Max, highlightColor, 0.0f, ImDrawFlags_None, 3.0f);
384 | }
385 | else
386 | {
387 | pDraw->AddRectFilledMultiColor(itemRect.Min + padding, itemRect.Max - padding, color, color, colorBottom, colorBottom);
388 | }
389 |
390 | // If the bar size is large enough, draw the name of the bar on top
391 | if (itemRect.GetWidth() > 10.0f)
392 | {
393 | const char* pBarText = event.pName;
394 | ImVec2 textSize = ImGui::CalcTextSize(pBarText);
395 | if (textSize.x * 2 < itemRect.GetWidth())
396 | {
397 | ImFormatStringToTempBuffer(&pBarText, nullptr, "%s (%.2f ms)", event.pName, ms);
398 | textSize = ImGui::CalcTextSize(pBarText);
399 | }
400 |
401 | itemRect.Expand(ImVec2(-2.0f, 0.0f));
402 |
403 | ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.f, 0.f, 0.f, 0.5f));
404 | itemRect.Translate(ImVec2(2.0f, 2.0f));
405 | ImGui::RenderTextEllipsis(pDraw, itemRect.Min + ImMax(ImVec2(), ImVec2(itemRect.GetWidth(), style.GetBarHeight()) - textSize) * 0.5f, itemRect.Max, itemRect.Max.x, pBarText, nullptr, &textSize);
406 | ImGui::PopStyleColor();
407 |
408 | itemRect.Translate(ImVec2(-2.0f, -2.0f));
409 | ImGui::RenderTextEllipsis(pDraw, itemRect.Min + ImMax(ImVec2(), ImVec2(itemRect.GetWidth(), style.GetBarHeight()) - textSize) * 0.5f, itemRect.Max, itemRect.Max.x, pBarText, nullptr, &textSize);
410 | }
411 | }
412 | }
413 |
414 | if (hovered)
415 | {
416 | if (ImGui::BeginTooltip())
417 | {
418 | ImGui::TextColored(ImVec4(1.0f, 0.7f, 0.4f, 1.0f), "%s | %.3f ms", event.pName, TicksToMs * (float)(event.TicksEnd - event.TicksBegin));
419 | ImGui::Text("Frame %d", frameIndex);
420 | if (event.pFilePath && *event.pFilePath != 0)
421 | ImGui::Text("%s:%d", event.pFilePath, event.LineNumber);
422 | ImGui::EndTooltip();
423 | }
424 | }
425 | if (clicked)
426 | {
427 | StringHash eventHash = GetEventHash(event);
428 | context.SelectedEvent.Set(GetEventHash(event));
429 | }
430 | }
431 | };
432 |
433 | // Add track name and expander
434 | /*
435 | (>) Main Thread [1234]
436 | */
437 | auto TrackHeader = [&](const char* pName, uint32 id) {
438 | pDraw->AddRectFilled(ImVec2(timelineRect.Min.x, cursor.y), ImVec2(timelineRect.Max.x, cursor.y + style.GetBarHeight()), ImColor(0.0f, 0.0f, 0.0f, 0.3f));
439 |
440 | bool isOpen = ImGui::GetCurrentWindow()->StateStorage.GetBool(id, true);
441 | ImVec2 trackTextCursor = ImVec2(timelineRect.Min.x, cursor.y);
442 |
443 | float caretSize = ImGui::GetTextLineHeight();
444 | if (ImGui::ItemAdd(ImRect(trackTextCursor, trackTextCursor + ImVec2(caretSize, caretSize)), id))
445 | {
446 | if (ImGui::IsItemHovered())
447 | pDraw->AddRect(ImGui::GetItemRectMin() + ImVec2(2, 2), ImGui::GetItemRectMax() - ImVec2(2, 2), ImColor(style.BGTextColor), 3.0f);
448 | pDraw->AddText(ImGui::GetItemRectMin() + ImVec2(2, 2), ImColor(style.BGTextColor), isOpen ? ICON_FA_CARET_DOWN : ICON_FA_CARET_RIGHT);
449 | if (ImGui::ButtonBehavior(ImRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax()), id, nullptr, nullptr, ImGuiButtonFlags_MouseButtonLeft))
450 | {
451 | isOpen = !isOpen;
452 | ImGui::GetCurrentWindow()->StateStorage.SetBool(id, isOpen);
453 | }
454 | }
455 |
456 | trackTextCursor.x += caretSize;
457 | pDraw->AddText(trackTextCursor, ImColor(style.BGTextColor), pName);
458 | cursor.y += style.GetBarHeight();
459 | return isOpen;
460 | };
461 |
462 | // Draw each track
463 | Span tracks = gProfiler.GetTracks();
464 |
465 | // Sort by track type
466 | Array sorted_tracks;
467 | for (const Profiler::EventTrack& track : tracks)
468 | sorted_tracks.push_back(&track);
469 | std::sort(sorted_tracks.begin(), sorted_tracks.end(), [](const Profiler::EventTrack* a, const Profiler::EventTrack* b) {
470 | return (int)a->Type > (int)b->Type;
471 | });
472 |
473 | for (const Profiler::EventTrack* pTrack : sorted_tracks)
474 | {
475 | PROFILE_CPU_SCOPE("Timeline Track");
476 |
477 | // Add thread name for track
478 | const char* pHeaderText;
479 | ImFormatStringToTempBuffer(&pHeaderText, nullptr, "%s [%d]", pTrack->Name, pTrack->ID);
480 |
481 | if (TrackHeader(pHeaderText, ImGui::GetID(pTrack)))
482 | {
483 | uint32 trackDepth = 0;
484 |
485 | // Add a bar in the right place for each event
486 | /*
487 | |[=============] |
488 | | [======] |
489 | */
490 | for (uint32 frameIndex = cpuRange.Begin; frameIndex < cpuRange.End; ++frameIndex)
491 | {
492 | DrawTrack(pTrack->GetFrameData(frameIndex), frameIndex, trackDepth);
493 | }
494 | cursor.y += trackDepth * style.GetBarHeight();
495 | }
496 |
497 | // Add vertical line to end track
498 | pDraw->AddLine(ImVec2(timelineRect.Min.x, cursor.y), ImVec2(timelineRect.Max.x, cursor.y), ImColor(style.BGTextColor));
499 | }
500 |
501 | // The final height of the timeline
502 | float timelineHeight = cursor.y - cursorStart.y;
503 |
504 | if (ImGui::IsWindowFocused())
505 | {
506 | // Profile range
507 | // If not currently in selection, start selection when left mouse button is pressed
508 | if (!context.IsSelectingRange && ImGui::IsMouseHoveringRect(timelineRect.Min, timelineRect.Max))
509 | {
510 | if (ImGui::IsMouseClicked(ImGuiMouseButton_Left))
511 | {
512 | context.RangeSelectionStart = ImGui::GetMousePos().x;
513 | context.IsSelectingRange = true;
514 | }
515 | }
516 | else if (context.IsSelectingRange)
517 | {
518 | // If mouse button is released, exit measuring mode
519 | if (ImGui::IsMouseReleased(ImGuiMouseButton_Left))
520 | {
521 | context.IsSelectingRange = false;
522 | }
523 | else
524 | {
525 | // Distance between mouse cursor and measuring start
526 | float distance = fabs(ImGui::GetMousePos().x - context.RangeSelectionStart);
527 |
528 | // Fade in based on distance
529 | float opacity = ImClamp(distance / 30.0f, 0.0f, 1.0f);
530 | if (opacity > 0.0f)
531 | {
532 | float time = (distance / TicksToPixels) * TicksToMs;
533 |
534 | // Draw measure region
535 | pDraw->AddRectFilled(ImVec2(context.RangeSelectionStart, timelineRect.Min.y), ImVec2(ImGui::GetMousePos().x, timelineRect.Max.y), ImColor(1.0f, 1.0f, 1.0f, 0.1f));
536 | pDraw->AddLine(ImVec2(context.RangeSelectionStart, timelineRect.Min.y), ImVec2(context.RangeSelectionStart, timelineRect.Max.y), ImColor(1.0f, 1.0f, 1.0f, 0.3f), 3.0f);
537 | pDraw->AddLine(ImVec2(ImGui::GetMousePos().x, timelineRect.Min.y), ImVec2(ImGui::GetMousePos().x, timelineRect.Max.y), ImColor(1.0f, 1.0f, 1.0f, 0.3f), 3.0f);
538 |
539 | // Add line and arrows
540 | ImColor measureColor = style.FGTextColor;
541 | measureColor.Value.w *= opacity;
542 | ImVec2 lineStart = ImVec2(context.RangeSelectionStart, ImGui::GetMousePos().y);
543 | ImVec2 lineEnd = ImGui::GetMousePos();
544 | if (lineStart.x > lineEnd.x)
545 | std::swap(lineStart.x, lineEnd.x);
546 | pDraw->AddLine(lineStart, lineEnd, measureColor);
547 | pDraw->AddLine(lineStart, lineStart + ImVec2(5, 5), measureColor);
548 | pDraw->AddLine(lineStart, lineStart + ImVec2(5, -5), measureColor);
549 | pDraw->AddLine(lineEnd, lineEnd + ImVec2(-5, 5), measureColor);
550 | pDraw->AddLine(lineEnd, lineEnd + ImVec2(-5, -5), measureColor);
551 |
552 | // Add text in the middle
553 | const char* pTimeText;
554 | ImFormatStringToTempBuffer(&pTimeText, nullptr, "Time: %.3f ms", time);
555 | ImVec2 textSize = ImGui::CalcTextSize(pTimeText);
556 | pDraw->AddText((lineEnd + lineStart) / 2 - ImVec2(textSize.x * 0.5f, textSize.y), measureColor, pTimeText);
557 | }
558 | }
559 | }
560 |
561 | // Zoom behavior
562 | float zoomDelta = 0.0f;
563 | if (ImGui::IsKeyDown(ImGuiKey_LeftCtrl) || ImGui::IsKeyDown(ImGuiKey_RightCtrl))
564 | zoomDelta += ImGui::GetIO().MouseWheel / 5.0f;
565 |
566 | if (zoomDelta != 0)
567 | {
568 | // Logarithmic scale
569 | float logScale = logf(context.TimelineScale);
570 | logScale += zoomDelta;
571 | float newScale = ImClamp(expf(logScale), 1.0f, 100.0f);
572 |
573 | float scaleFactor = newScale / context.TimelineScale;
574 | context.TimelineScale *= scaleFactor;
575 | ImVec2 mousePos = ImGui::GetMousePos() - timelineRect.Min;
576 | context.TimelineOffset.x = mousePos.x - (mousePos.x - context.TimelineOffset.x) * scaleFactor;
577 | }
578 | }
579 |
580 | // Panning behavior
581 | bool held;
582 | ImGui::ButtonBehavior(timelineRect, timelineID, nullptr, &held, ImGuiButtonFlags_MouseButtonRight);
583 | if (held)
584 | context.TimelineOffset += ImGui::GetIO().MouseDelta;
585 |
586 | // Compute the new timeline size to correctly clamp the offset
587 | timelineWidth = timelineRect.GetWidth() * context.TimelineScale;
588 | context.TimelineOffset = ImClamp(context.TimelineOffset, ImMin(ImVec2(0.0f, 0.0f), timelineRect.GetSize() - ImVec2(timelineWidth, timelineHeight)), ImVec2(0.0f, 0.0f));
589 |
590 | ImGui::PopClipRect();
591 | ImGui::PopClipRect();
592 |
593 | // Draw a debug rect around the timeline item and the whole (unclipped) timeline rect
594 | if (style.DebugMode)
595 | {
596 | pDraw->PushClipRectFullScreen();
597 | pDraw->AddRect(cursorStart, cursorStart + ImVec2(timelineWidth, timelineHeight), ImColor(1.0f, 0.0f, 0.0f), 0.0f, ImDrawFlags_None, 3.0f);
598 | pDraw->AddRect(timelineRect.Min, timelineRect.Max, ImColor(0.0f, 1.0f, 0.0f), 0.0f, ImDrawFlags_None, 2.0f);
599 | pDraw->PopClipRect();
600 | }
601 |
602 | // Add extra data to tooltip
603 | ImGui::SameLine();
604 |
605 | ImGui::BeginGroup();
606 |
607 | const char* pTracePath = "trace.json";
608 | if (!traceContext.TraceStream.is_open())
609 | {
610 | if (ImGui::Button("Begin Trace", ImVec2(ImGui::GetContentRegionAvail().x, 0)))
611 | {
612 | BeginTrace(pTracePath, traceContext);
613 | }
614 | }
615 | else
616 | {
617 | if (ImGui::Button("End Trace", ImVec2(ImGui::GetContentRegionAvail().x, 0)))
618 | {
619 | EndTrace(traceContext);
620 | }
621 | }
622 |
623 | HUDContext::SelectedStatData& selectedEvent = context.SelectedEvent;
624 | if ((uint32)selectedEvent.Hash != 0)
625 | {
626 | const char* pName = "";
627 | float eventTime = 0;
628 | uint32 n = 0;
629 | for (uint32 i = cpuRange.Begin; i < cpuRange.End; ++i)
630 | {
631 | for (const Profiler::EventTrack& track : gProfiler.GetTracks())
632 | {
633 | const ProfilerEventData& eventData = track.GetFrameData(i);
634 | for (const ProfilerEvent& event : eventData)
635 | {
636 | if (GetEventHash(event) == selectedEvent.Hash)
637 | {
638 | float time = TicksToMs * (float)(event.TicksEnd - event.TicksBegin);
639 | selectedEvent.AddSample(time);
640 | pName = event.pName;
641 | eventTime = time;
642 | ++n;
643 | }
644 | }
645 | }
646 | }
647 |
648 | if (eventTime)
649 | {
650 | ImGui::Text(pName);
651 | if (ImGui::BeginTable("TooltipTable", 2))
652 | {
653 | ImGui::TableNextColumn();
654 | ImGui::Text("Time:");
655 | ImGui::TableNextColumn();
656 | ImGui::Text("%.2f ms", eventTime);
657 |
658 | ImGui::TableNextColumn();
659 | ImGui::Text("Occurances:");
660 | ImGui::TableNextColumn();
661 | ImGui::Text("%d", n);
662 |
663 | ImGui::TableNextColumn();
664 | ImGui::Text("Moving Average:");
665 | ImGui::TableNextColumn();
666 | ImGui::Text("%.2f ms", selectedEvent.MovingAverageTime);
667 |
668 | ImGui::TableNextColumn();
669 | ImGui::Text("Min/Max:");
670 | ImGui::TableNextColumn();
671 | ImGui::Text("%.2f/%.2f ms", selectedEvent.MinTime, selectedEvent.MaxTime);
672 |
673 | ImGui::EndTable();
674 | }
675 | }
676 | }
677 | ImGui::EndGroup();
678 |
679 | // Horizontal scroll bar
680 | ImS64 scrollH = -(ImS64)context.TimelineOffset.x;
681 | ImGui::ScrollbarEx(ImRect(ImVec2(timelineRect.Min.x, timelineRect.Max.y), ImVec2(timelineRect.Max.x + style.ScrollBarSize, timelineRect.Max.y + style.ScrollBarSize)), ImGui::GetID("ScrollH"), ImGuiAxis_X, &scrollH, (ImS64)timelineRect.GetSize().x, (ImS64)timelineWidth, ImDrawFlags_None);
682 | context.TimelineOffset.x = -(float)scrollH;
683 |
684 | // Vertical scroll bar
685 | ImS64 scrollV = -(ImS64)context.TimelineOffset.y;
686 | ImGui::ScrollbarEx(ImRect(ImVec2(timelineRect.Max.x, timelineRect.Min.y), ImVec2(timelineRect.Max.x + style.ScrollBarSize, timelineRect.Max.y)), ImGui::GetID("ScrollV"), ImGuiAxis_Y, &scrollV, (ImS64)timelineRect.GetSize().y, (ImS64)timelineHeight, ImDrawFlags_None);
687 | context.TimelineOffset.y = -(float)scrollV;
688 | }
689 | }
690 |
691 |
692 | void DrawProfilerHUD()
693 | {
694 | HUDContext& context = Context();
695 | StyleOptions& style = context.Style;
696 |
697 | if (!context.IconFont)
698 | {
699 | {
700 | ImFontConfig fontConfig;
701 | fontConfig.MergeMode = false;
702 | strcpy_s(fontConfig.Name, "Roboto-Regular");
703 | context.TextFont = ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(roboto_regular_compressed_data, roboto_regular_compressed_size, 0.0f, &fontConfig);
704 | }
705 |
706 | {
707 | ImFontConfig fontConfig;
708 | fontConfig.MergeMode = true;
709 | strcpy_s(fontConfig.Name, "FontAwesome");
710 | static const ImWchar icon_ranges[] = { ICON_MIN_FA, ICON_MAX_FA, 0 };
711 | context.IconFont = ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(font_awesome_compressed_data, font_awesome_compressed_size, 0.0f, &fontConfig, icon_ranges);
712 | }
713 | }
714 |
715 | ImGui::PushFont(context.TextFont);
716 | ImGui::PushStyleColor(ImGuiCol_Text, style.FGTextColor);
717 |
718 | if (gProfiler.IsPaused())
719 | ImGui::Text("Paused");
720 | else
721 | ImGui::Text("Press Space to pause");
722 |
723 | ImGui::SameLine(100);
724 |
725 | ImGui::Dummy(ImVec2(30, 0));
726 | ImGui::SameLine();
727 |
728 | ImGui::Text("Filter");
729 | ImGui::SetNextItemWidth(150);
730 | ImGui::SameLine();
731 | ImGui::InputText("##Search", context.SearchString, ARRAYSIZE(context.SearchString));
732 | ImGui::SameLine();
733 | if (ImGui::Button(ICON_FA_TIMES "##clearfilter"))
734 | context.SearchString[0] = 0;
735 | ImGui::SameLine();
736 | if (ImGui::Button(ICON_FA_PAINT_BRUSH "##styleeditor"))
737 | ImGui::OpenPopup("Style Editor");
738 |
739 | if (ImGui::BeginPopup("Style Editor"))
740 | {
741 | EditStyle(style);
742 | ImGui::EndPopup();
743 | }
744 |
745 | if (ImGui::IsWindowFocused() && ImGui::IsKeyPressed(ImGuiKey_Space))
746 | context.IsPaused = !context.IsPaused;
747 |
748 | if (ImGui::IsWindowHovered() && (ImGui::IsMouseClicked(ImGuiMouseButton_Right) || ImGui::IsMouseClicked(ImGuiMouseButton_Left)))
749 | ImGui::SetWindowFocus();
750 |
751 | gProfiler.SetPaused(context.IsPaused);
752 | gGPUProfiler.SetPaused(context.IsPaused);
753 |
754 | DrawProfilerTimeline(ImVec2(0, 0));
755 |
756 | ImGui::PopStyleColor();
757 | ImGui::PopFont();
758 | }
759 |
760 | #endif
761 |
--------------------------------------------------------------------------------
/main.cpp:
--------------------------------------------------------------------------------
1 | // Dear ImGui: standalone example application for Windows API + DirectX 12
2 |
3 | // Learn about Dear ImGui:
4 | // - FAQ https://dearimgui.com/faq
5 | // - Getting Started https://dearimgui.com/getting-started
6 | // - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
7 | // - Introduction, links and more at the top of imgui.cpp
8 |
9 | #define NOMINMAX
10 |
11 | #include "imgui.h"
12 | #include "imgui_impl_win32.h"
13 | #include "imgui_impl_dx12.h"
14 | #include
15 | #include
16 | #include
17 |
18 | #include "Profiler.h"
19 |
20 | #ifdef _DEBUG
21 | #define DX12_ENABLE_DEBUG_LAYER
22 | #endif
23 |
24 | #ifdef DX12_ENABLE_DEBUG_LAYER
25 | #include
26 | #pragma comment(lib, "dxguid.lib")
27 | #endif
28 |
29 | //#define SUPERLUMINAL_API
30 | #ifdef SUPERLUMINAL_API
31 | #include
32 | static PerformanceAPI_Functions sSuperluminal{};
33 | PerformanceAPI_ModuleHandle sSuperluminalHandle = nullptr;
34 | #endif
35 |
36 | // Config for example app
37 | static const int APP_NUM_FRAMES_IN_FLIGHT = 2;
38 | static const int APP_NUM_BACK_BUFFERS = 2;
39 | static const int APP_SRV_HEAP_SIZE = 64;
40 |
41 | struct FrameContext
42 | {
43 | ID3D12CommandAllocator* CommandAllocator;
44 | ID3D12CommandAllocator* PresentCommandAllocator;
45 | UINT64 FenceValue;
46 | };
47 |
48 | // Simple free list based allocator
49 | struct ExampleDescriptorHeapAllocator
50 | {
51 | ID3D12DescriptorHeap* Heap = nullptr;
52 | D3D12_DESCRIPTOR_HEAP_TYPE HeapType = D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES;
53 | D3D12_CPU_DESCRIPTOR_HANDLE HeapStartCpu;
54 | D3D12_GPU_DESCRIPTOR_HANDLE HeapStartGpu;
55 | UINT HeapHandleIncrement;
56 | ImVector FreeIndices;
57 |
58 | void Create(ID3D12Device* device, ID3D12DescriptorHeap* heap)
59 | {
60 | IM_ASSERT(Heap == nullptr && FreeIndices.empty());
61 | Heap = heap;
62 | D3D12_DESCRIPTOR_HEAP_DESC desc = heap->GetDesc();
63 | HeapType = desc.Type;
64 | HeapStartCpu = Heap->GetCPUDescriptorHandleForHeapStart();
65 | HeapStartGpu = Heap->GetGPUDescriptorHandleForHeapStart();
66 | HeapHandleIncrement = device->GetDescriptorHandleIncrementSize(HeapType);
67 | FreeIndices.reserve((int)desc.NumDescriptors);
68 | for (int n = desc.NumDescriptors; n > 0; n--)
69 | FreeIndices.push_back(n - 1);
70 | }
71 | void Destroy()
72 | {
73 | Heap = nullptr;
74 | FreeIndices.clear();
75 | }
76 | void Alloc(D3D12_CPU_DESCRIPTOR_HANDLE* out_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE* out_gpu_desc_handle)
77 | {
78 | IM_ASSERT(FreeIndices.Size > 0);
79 | int idx = FreeIndices.back();
80 | FreeIndices.pop_back();
81 | out_cpu_desc_handle->ptr = HeapStartCpu.ptr + (idx * HeapHandleIncrement);
82 | out_gpu_desc_handle->ptr = HeapStartGpu.ptr + (idx * HeapHandleIncrement);
83 | }
84 | void Free(D3D12_CPU_DESCRIPTOR_HANDLE out_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE out_gpu_desc_handle)
85 | {
86 | int cpu_idx = (int)((out_cpu_desc_handle.ptr - HeapStartCpu.ptr) / HeapHandleIncrement);
87 | int gpu_idx = (int)((out_gpu_desc_handle.ptr - HeapStartGpu.ptr) / HeapHandleIncrement);
88 | IM_ASSERT(cpu_idx == gpu_idx);
89 | FreeIndices.push_back(cpu_idx);
90 | }
91 | };
92 |
93 | // Data
94 | static FrameContext g_frameContext[APP_NUM_FRAMES_IN_FLIGHT] = {};
95 | static UINT g_frameIndex = 0;
96 |
97 | static ID3D12Device* g_pd3dDevice = nullptr;
98 | static ID3D12DescriptorHeap* g_pd3dRtvDescHeap = nullptr;
99 | static ID3D12DescriptorHeap* g_pd3dSrvDescHeap = nullptr;
100 | static ExampleDescriptorHeapAllocator g_pd3dSrvDescHeapAlloc = {};
101 | static ID3D12CommandQueue* g_pd3dCommandQueue = nullptr;
102 | static ID3D12GraphicsCommandList* g_pd3dCommandList = nullptr;
103 | static ID3D12Fence* g_fence = nullptr;
104 | static HANDLE g_fenceEvent = nullptr;
105 | static UINT64 g_fenceLastSignaledValue = 0;
106 | static IDXGISwapChain3* g_pSwapChain = nullptr;
107 | static bool g_SwapChainTearingSupport = false;
108 | static bool g_SwapChainOccluded = false;
109 | static HANDLE g_hSwapChainWaitableObject = nullptr;
110 | static bool g_SwapChainEnableTearing = true;
111 | static bool g_SwapChainEnableVSync = true;
112 | static bool g_SwapChainEnableWaitableObject = true;
113 | static int g_SwapChainMaximumFrameLatency = 2;
114 | static ID3D12Resource* g_mainRenderTargetResource[APP_NUM_BACK_BUFFERS] = {};
115 | static D3D12_CPU_DESCRIPTOR_HANDLE g_mainRenderTargetDescriptor[APP_NUM_BACK_BUFFERS] = {};
116 | static HWND g_MainWindow = nullptr;
117 |
118 | static ID3D12Resource* g_GPUWorkloadResources[2];
119 | static float g_CPUWorkload = 0.0f;
120 | static float g_CPUWorkloadVariance = 0.0f;
121 | static int g_GPUWorkload = 0;
122 | static float g_GPUWorkloadVariance = 0.0f;
123 |
124 |
125 | // Forward declarations of helper functions
126 | bool CreateDeviceD3D(HWND hWnd);
127 | void CleanupDeviceD3D();
128 | bool CreateSwapchain(HWND hWnd);
129 | void CleanupRenderTarget();
130 | void WaitForPendingOperations();
131 | void WaitForSwapchain();
132 | FrameContext* WaitForNextFrameContext();
133 | LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
134 |
135 | // Main code
136 | int main(int, char**)
137 | {
138 | // Make process DPI aware and obtain main monitor scale
139 | ImGui_ImplWin32_EnableDpiAwareness();
140 | float main_scale = ImGui_ImplWin32_GetDpiScaleForMonitor(::MonitorFromPoint(POINT{ 0, 0 }, MONITOR_DEFAULTTOPRIMARY));
141 |
142 | // Create application window
143 | WNDCLASSEXW wc = { sizeof(wc), CS_CLASSDC, WndProc, 0L, 0L, GetModuleHandle(nullptr), nullptr, nullptr, nullptr, nullptr, L"ImGui Example", nullptr };
144 | ::RegisterClassExW(&wc);
145 | HWND hwnd = ::CreateWindowW(wc.lpszClassName, L"Dear ImGui DirectX12 Example", WS_OVERLAPPEDWINDOW, 100, 100, (int)(1280 * main_scale), (int)(800 * main_scale), nullptr, nullptr, wc.hInstance, nullptr);
146 | g_MainWindow = hwnd;
147 |
148 | // Initialize Direct3D
149 | if (!CreateDeviceD3D(hwnd))
150 | {
151 | CleanupDeviceD3D();
152 | ::UnregisterClassW(wc.lpszClassName, wc.hInstance);
153 | return 1;
154 | }
155 |
156 | // Show the window
157 | ::ShowWindow(hwnd, SW_SHOWDEFAULT);
158 | ::UpdateWindow(hwnd);
159 |
160 | // Setup Dear ImGui context
161 | IMGUI_CHECKVERSION();
162 | ImGui::CreateContext();
163 | ImGuiIO& io = ImGui::GetIO(); (void)io;
164 | io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
165 | io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
166 | io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking
167 | io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows
168 | //io.ConfigViewportsNoAutoMerge = true;
169 | //io.ConfigViewportsNoTaskBarIcon = true;
170 |
171 | // Setup Dear ImGui style
172 | ImGui::StyleColorsDark();
173 | //ImGui::StyleColorsLight();
174 |
175 | // Setup scaling
176 | ImGuiStyle& style = ImGui::GetStyle();
177 | style.ScaleAllSizes(main_scale); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this requires resetting Style + calling this again)
178 | style.FontScaleDpi = main_scale; // Set initial font scale. (using io.ConfigDpiScaleFonts=true makes this unnecessary. We leave both here for documentation purpose)
179 | io.ConfigDpiScaleFonts = true; // [Experimental] Automatically overwrite style.FontScaleDpi in Begin() when Monitor DPI changes. This will scale fonts but _NOT_ scale sizes/padding for now.
180 | io.ConfigDpiScaleViewports = true; // [Experimental] Scale Dear ImGui and Platform Windows when Monitor DPI changes.
181 |
182 | // When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones.
183 | if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
184 | {
185 | style.WindowRounding = 0.0f;
186 | style.Colors[ImGuiCol_WindowBg].w = 1.0f;
187 | }
188 |
189 | // Setup Platform/Renderer backends
190 | ImGui_ImplWin32_Init(hwnd);
191 |
192 | ImGui_ImplDX12_InitInfo init_info = {};
193 | init_info.Device = g_pd3dDevice;
194 | init_info.CommandQueue = g_pd3dCommandQueue;
195 | init_info.NumFramesInFlight = APP_NUM_FRAMES_IN_FLIGHT;
196 | init_info.RTVFormat = DXGI_FORMAT_R8G8B8A8_UNORM;
197 | init_info.DSVFormat = DXGI_FORMAT_UNKNOWN;
198 | // Allocating SRV descriptors (for textures) is up to the application, so we provide callbacks.
199 | // (current version of the backend will only allocate one descriptor, future versions will need to allocate more)
200 | init_info.SrvDescriptorHeap = g_pd3dSrvDescHeap;
201 | init_info.SrvDescriptorAllocFn = [](ImGui_ImplDX12_InitInfo*, D3D12_CPU_DESCRIPTOR_HANDLE* out_cpu_handle, D3D12_GPU_DESCRIPTOR_HANDLE* out_gpu_handle) { return g_pd3dSrvDescHeapAlloc.Alloc(out_cpu_handle, out_gpu_handle); };
202 | init_info.SrvDescriptorFreeFn = [](ImGui_ImplDX12_InitInfo*, D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle, D3D12_GPU_DESCRIPTOR_HANDLE gpu_handle) { return g_pd3dSrvDescHeapAlloc.Free(cpu_handle, gpu_handle); };
203 | ImGui_ImplDX12_Init(&init_info);
204 |
205 | gProfiler.Initialize(25);
206 | Span queues(&g_pd3dCommandQueue, 1);
207 | gGPUProfiler.Initialize(g_pd3dDevice, queues, 4);
208 |
209 | #ifdef SUPERLUMINAL_API
210 | sSuperluminalHandle = PerformanceAPI_LoadFrom(L"C:\\Program Files\\Superluminal\\Performance\\API\\dll\\x64\\PerformanceAPI.dll", &sSuperluminal);
211 | if (sSuperluminalHandle)
212 | {
213 | CPUProfilerCallbacks callbacks{
214 | .OnEventBegin = [](const char* pName, void* /*pUserData*/) { sSuperluminal.BeginEvent(pName, nullptr, PERFORMANCEAPI_DEFAULT_COLOR); },
215 | .OnEventEnd = [](void* /*pUserData*/) { sSuperluminal.EndEvent(); },
216 | .pUserData = nullptr
217 | };
218 | gProfiler.SetEventCallback(callbacks);
219 | }
220 | #endif
221 |
222 | // Before 1.91.6: our signature was using a single descriptor. From 1.92, specifying SrvDescriptorAllocFn/SrvDescriptorFreeFn will be required to benefit from new features.
223 | //ImGui_ImplDX12_Init(g_pd3dDevice, APP_NUM_FRAMES_IN_FLIGHT, DXGI_FORMAT_R8G8B8A8_UNORM, g_pd3dSrvDescHeap, g_pd3dSrvDescHeap->GetCPUDescriptorHandleForHeapStart(), g_pd3dSrvDescHeap->GetGPUDescriptorHandleForHeapStart());
224 |
225 | // Load Fonts
226 | // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them.
227 | // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple.
228 | // - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit).
229 | // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering.
230 | // - Read 'docs/FONTS.md' for more instructions and details. If you like the default font but want it to scale better, consider using the 'ProggyVector' from the same author!
231 | // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ !
232 | style.FontSizeBase = 14.0f;
233 | //io.Fonts->AddFontDefault();
234 | //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf");
235 | //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf");
236 | //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf");
237 | //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf");
238 | //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf");
239 | //IM_ASSERT(font != nullptr);
240 |
241 | // Our state
242 | bool show_demo_window = true;
243 | bool show_another_window = false;
244 | ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
245 |
246 | PROFILE_REGISTER_THREAD("Main Thread");
247 |
248 | // Main loop
249 | bool done = false;
250 | while (!done)
251 | {
252 | PROFILE_FRAME();
253 |
254 | {
255 | PROFILE_CPU_SCOPE("WndProc");
256 |
257 | // Poll and handle messages (inputs, window resize, etc.)
258 | // See the WndProc() function below for our to dispatch events to the Win32 backend.
259 | MSG msg;
260 | while (::PeekMessage(&msg, nullptr, 0U, 0U, PM_REMOVE))
261 | {
262 | ::TranslateMessage(&msg);
263 | ::DispatchMessage(&msg);
264 | if (msg.message == WM_QUIT)
265 | done = true;
266 | }
267 | }
268 |
269 | if (done)
270 | break;
271 |
272 | // Handle window screen locked
273 | if ((g_SwapChainOccluded && g_pSwapChain->Present(0, DXGI_PRESENT_TEST) == DXGI_STATUS_OCCLUDED) || ::IsIconic(hwnd))
274 | {
275 | ::Sleep(10);
276 | continue;
277 | }
278 | g_SwapChainOccluded = false;
279 |
280 | WaitForSwapchain();
281 | FrameContext* frameCtx = WaitForNextFrameContext();
282 |
283 | // Start the Dear ImGui frame
284 | {
285 | PROFILE_CPU_SCOPE("NewFrame");
286 |
287 | ImGui_ImplDX12_NewFrame();
288 | ImGui_ImplWin32_NewFrame();
289 | ImGui::NewFrame();
290 | }
291 |
292 | {
293 | PROFILE_CPU_SCOPE("ProfilerHUD");
294 | DrawProfilerHUD();
295 | }
296 |
297 | if (show_demo_window)
298 | ImGui::ShowDemoWindow(&show_demo_window);
299 |
300 | {
301 | if (ImGui::Begin("Options"))
302 | {
303 | PROFILE_CPU_SCOPE("Options");
304 |
305 | if (ImGui::SliderInt("Maximum Frame Latency", &g_SwapChainMaximumFrameLatency, 1, 4))
306 | CreateSwapchain(g_MainWindow);
307 | if (ImGui::Checkbox("Waitable SwapChain", &g_SwapChainEnableWaitableObject))
308 | CreateSwapchain(g_MainWindow);
309 | if(ImGui::Checkbox("VSync", &g_SwapChainEnableVSync))
310 | CreateSwapchain(g_MainWindow);
311 | if(ImGui::Checkbox("Allow Tearing", &g_SwapChainEnableTearing))
312 | CreateSwapchain(g_MainWindow);
313 |
314 | ImGui::SliderFloat("CPU Load", &g_CPUWorkload, 0.0f, 33.66f, "%.2f");
315 | ImGui::SliderFloat("CPU Load Variance", &g_CPUWorkloadVariance, 0.0f, 1.0f, "%.2f");
316 | ImGui::SliderInt("GPU Load", &g_GPUWorkload, 0, 500);
317 | ImGui::SliderFloat("GPU Load Variance", &g_GPUWorkloadVariance, 0.0f, 1.0f, "%.2f");
318 |
319 | ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
320 | }
321 | ImGui::End();
322 | }
323 |
324 | float randomV_CPU = ((float)rand() / RAND_MAX) * 2 - 1;
325 | float cpu_workload = g_CPUWorkload + g_CPUWorkload * randomV_CPU * g_CPUWorkloadVariance;
326 |
327 | float randomV_GPU = ((float)rand() / RAND_MAX) * 2 - 1;
328 | int gpu_workload = (int)(g_GPUWorkload + g_GPUWorkload * randomV_GPU * g_GPUWorkloadVariance);
329 |
330 | if (cpu_workload > 0.0f)
331 | {
332 | PROFILE_CPU_SCOPE("CPU Load");
333 |
334 | LARGE_INTEGER freq, start, now;
335 | QueryPerformanceFrequency(&freq);
336 | QueryPerformanceCounter(&start);
337 |
338 | double target = cpu_workload * 0.001f * freq.QuadPart; // 2 ms in ticks
339 | do
340 | {
341 | QueryPerformanceCounter(&now);
342 | } while ((now.QuadPart - start.QuadPart) < target);
343 | }
344 |
345 | // Rendering
346 | {
347 | PROFILE_CPU_SCOPE("ImGui Render");
348 | ImGui::Render();
349 | }
350 |
351 | UINT backBufferIdx = g_pSwapChain->GetCurrentBackBufferIndex();
352 |
353 | frameCtx->CommandAllocator->Reset();
354 | g_pd3dCommandList->Reset(frameCtx->CommandAllocator, nullptr);
355 |
356 | {
357 | PROFILE_GPU_SCOPE(g_pd3dCommandList, "GPU Workload");
358 | for (int i = 0; i < gpu_workload; ++i)
359 | g_pd3dCommandList->CopyResource(g_GPUWorkloadResources[0], g_GPUWorkloadResources[1]);
360 | }
361 |
362 | g_pd3dCommandList->Close();
363 |
364 | {
365 | Span cmdlists((ID3D12CommandList**)&g_pd3dCommandList, 1);
366 | PROFILE_EXECUTE_COMMANDLISTS(g_pd3dCommandQueue, cmdlists);
367 | g_pd3dCommandQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)&g_pd3dCommandList);
368 | }
369 |
370 | frameCtx->PresentCommandAllocator->Reset();
371 | g_pd3dCommandList->Reset(frameCtx->CommandAllocator, nullptr);
372 |
373 |
374 | {
375 | PROFILE_GPU_SCOPE(g_pd3dCommandList, "Render");
376 |
377 | D3D12_RESOURCE_BARRIER barrier = {};
378 | barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
379 | barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
380 | barrier.Transition.pResource = g_mainRenderTargetResource[backBufferIdx];
381 | barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
382 | barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
383 | barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
384 |
385 | g_pd3dCommandList->ResourceBarrier(1, &barrier);
386 |
387 | // Render Dear ImGui graphics
388 | const float clear_color_with_alpha[4] = { clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w };
389 | g_pd3dCommandList->ClearRenderTargetView(g_mainRenderTargetDescriptor[backBufferIdx], clear_color_with_alpha, 0, nullptr);
390 | g_pd3dCommandList->OMSetRenderTargets(1, &g_mainRenderTargetDescriptor[backBufferIdx], FALSE, nullptr);
391 | g_pd3dCommandList->SetDescriptorHeaps(1, &g_pd3dSrvDescHeap);
392 | ImGui_ImplDX12_RenderDrawData(ImGui::GetDrawData(), g_pd3dCommandList);
393 | barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
394 | barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
395 | g_pd3dCommandList->ResourceBarrier(1, &barrier);
396 | }
397 |
398 | g_pd3dCommandList->Close();
399 |
400 | {
401 | Span cmdlists((ID3D12CommandList**)&g_pd3dCommandList, 1);
402 | PROFILE_EXECUTE_COMMANDLISTS(g_pd3dCommandQueue, cmdlists);
403 | g_pd3dCommandQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)&g_pd3dCommandList);
404 | }
405 |
406 | // Update and Render additional Platform Windows
407 | if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
408 | {
409 | PROFILE_CPU_SCOPE("Update Viewports");
410 | ImGui::UpdatePlatformWindows();
411 | ImGui::RenderPlatformWindowsDefault();
412 | }
413 |
414 | g_pd3dCommandQueue->Signal(g_fence, ++g_fenceLastSignaledValue);
415 | frameCtx->FenceValue = g_fenceLastSignaledValue;
416 |
417 | // Present
418 | {
419 | PROFILE_CPU_SCOPE("Present");
420 | HRESULT hr = g_pSwapChain->Present(g_SwapChainEnableVSync ? 1 : 0, !g_SwapChainEnableVSync && g_SwapChainTearingSupport ? DXGI_PRESENT_ALLOW_TEARING : 0); // Present without vsync
421 | PROFILE_PRESENT(g_pSwapChain);
422 | g_SwapChainOccluded = (hr == DXGI_STATUS_OCCLUDED);
423 | g_frameIndex++;
424 | }
425 | }
426 |
427 | WaitForPendingOperations();
428 |
429 | // Cleanup
430 | ImGui_ImplDX12_Shutdown();
431 | ImGui_ImplWin32_Shutdown();
432 | ImGui::DestroyContext();
433 |
434 | gProfiler.Shutdown();
435 | gGPUProfiler.Shutdown();
436 |
437 | CleanupDeviceD3D();
438 | ::DestroyWindow(hwnd);
439 | ::UnregisterClassW(wc.lpszClassName, wc.hInstance);
440 |
441 | return 0;
442 | }
443 |
444 | // Helper functions
445 | bool CreateDeviceD3D(HWND hWnd)
446 | {
447 | // [DEBUG] Enable debug interface
448 | #ifdef DX12_ENABLE_DEBUG_LAYER
449 | ID3D12Debug* pdx12Debug = nullptr;
450 | if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&pdx12Debug))))
451 | pdx12Debug->EnableDebugLayer();
452 | #endif
453 |
454 | // Create device
455 | D3D_FEATURE_LEVEL featureLevel = D3D_FEATURE_LEVEL_11_0;
456 | if (D3D12CreateDevice(nullptr, featureLevel, IID_PPV_ARGS(&g_pd3dDevice)) != S_OK)
457 | return false;
458 |
459 | // [DEBUG] Setup debug interface to break on any warnings/errors
460 | #ifdef DX12_ENABLE_DEBUG_LAYER
461 | if (pdx12Debug != nullptr)
462 | {
463 | ID3D12InfoQueue* pInfoQueue = nullptr;
464 | g_pd3dDevice->QueryInterface(IID_PPV_ARGS(&pInfoQueue));
465 | pInfoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, true);
466 | pInfoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_CORRUPTION, true);
467 | pInfoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_WARNING, true);
468 | pInfoQueue->Release();
469 | pdx12Debug->Release();
470 | }
471 | #endif
472 |
473 | {
474 | D3D12_DESCRIPTOR_HEAP_DESC desc = {};
475 | desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
476 | desc.NumDescriptors = APP_NUM_BACK_BUFFERS;
477 | desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
478 | desc.NodeMask = 1;
479 | if (g_pd3dDevice->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&g_pd3dRtvDescHeap)) != S_OK)
480 | return false;
481 |
482 | SIZE_T rtvDescriptorSize = g_pd3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
483 | D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = g_pd3dRtvDescHeap->GetCPUDescriptorHandleForHeapStart();
484 | for (UINT i = 0; i < APP_NUM_BACK_BUFFERS; i++)
485 | {
486 | g_mainRenderTargetDescriptor[i] = rtvHandle;
487 | rtvHandle.ptr += rtvDescriptorSize;
488 | }
489 | }
490 |
491 | {
492 | D3D12_DESCRIPTOR_HEAP_DESC desc = {};
493 | desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
494 | desc.NumDescriptors = APP_SRV_HEAP_SIZE;
495 | desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
496 | if (g_pd3dDevice->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&g_pd3dSrvDescHeap)) != S_OK)
497 | return false;
498 | g_pd3dSrvDescHeapAlloc.Create(g_pd3dDevice, g_pd3dSrvDescHeap);
499 | }
500 |
501 | {
502 | D3D12_COMMAND_QUEUE_DESC desc = {};
503 | desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
504 | desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
505 | desc.NodeMask = 1;
506 | if (g_pd3dDevice->CreateCommandQueue(&desc, IID_PPV_ARGS(&g_pd3dCommandQueue)) != S_OK)
507 | return false;
508 | }
509 |
510 | for (UINT i = 0; i < APP_NUM_FRAMES_IN_FLIGHT; i++)
511 | {
512 | if (g_pd3dDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&g_frameContext[i].CommandAllocator)) != S_OK)
513 | return false;
514 | if (g_pd3dDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&g_frameContext[i].PresentCommandAllocator)) != S_OK)
515 | return false;
516 | }
517 |
518 | if (g_pd3dDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, g_frameContext[0].CommandAllocator, nullptr, IID_PPV_ARGS(&g_pd3dCommandList)) != S_OK ||
519 | g_pd3dCommandList->Close() != S_OK)
520 | return false;
521 |
522 | if (g_pd3dDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&g_fence)) != S_OK)
523 | return false;
524 |
525 | g_fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
526 | if (g_fenceEvent == nullptr)
527 | return false;
528 |
529 |
530 | D3D12_RESOURCE_DESC bufferDesc{
531 | .Dimension = D3D12_RESOURCE_DIMENSION_BUFFER,
532 | .Alignment = 0,
533 | .Width = 32 * 1024 * 1024,
534 | .Height = 1,
535 | .DepthOrArraySize = 1,
536 | .MipLevels = 1,
537 | .Format = DXGI_FORMAT_UNKNOWN,
538 | .SampleDesc = {
539 | .Count = 1,
540 | .Quality = 0,
541 | },
542 | .Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
543 | .Flags = D3D12_RESOURCE_FLAG_NONE,
544 | };
545 |
546 | D3D12_HEAP_PROPERTIES heapProps{
547 | .Type = D3D12_HEAP_TYPE_DEFAULT,
548 | .CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN,
549 | .MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN,
550 | .CreationNodeMask = 0,
551 | .VisibleNodeMask = 0,
552 | };
553 |
554 | for (int i = 0; i < 2; ++i)
555 | g_pd3dDevice->CreateCommittedResource(&heapProps, D3D12_HEAP_FLAG_ALLOW_ALL_BUFFERS_AND_TEXTURES, &bufferDesc, D3D12_RESOURCE_STATE_COMMON, nullptr, IID_PPV_ARGS(&g_GPUWorkloadResources[i]));
556 |
557 | return true;
558 | }
559 |
560 | void CleanupDeviceD3D()
561 | {
562 | CleanupRenderTarget();
563 |
564 | for (int i = 0; i < 2; ++i)
565 | g_GPUWorkloadResources[i]->Release();
566 |
567 | if (g_pSwapChain) { g_pSwapChain->SetFullscreenState(false, nullptr); g_pSwapChain->Release(); g_pSwapChain = nullptr; }
568 | if (g_hSwapChainWaitableObject != nullptr) { CloseHandle(g_hSwapChainWaitableObject); }
569 | for (UINT i = 0; i < APP_NUM_FRAMES_IN_FLIGHT; i++)
570 | {
571 | if (g_frameContext[i].CommandAllocator)
572 | {
573 | g_frameContext[i].CommandAllocator->Release();
574 | g_frameContext[i].CommandAllocator = nullptr;
575 | }
576 | if (g_frameContext[i].PresentCommandAllocator)
577 | {
578 | g_frameContext[i].PresentCommandAllocator->Release();
579 | g_frameContext[i].PresentCommandAllocator = nullptr;
580 | }
581 | }
582 | if (g_pd3dCommandQueue) { g_pd3dCommandQueue->Release(); g_pd3dCommandQueue = nullptr; }
583 | if (g_pd3dCommandList) { g_pd3dCommandList->Release(); g_pd3dCommandList = nullptr; }
584 | if (g_pd3dRtvDescHeap) { g_pd3dRtvDescHeap->Release(); g_pd3dRtvDescHeap = nullptr; }
585 | if (g_pd3dSrvDescHeap) { g_pd3dSrvDescHeap->Release(); g_pd3dSrvDescHeap = nullptr; }
586 | if (g_fence) { g_fence->Release(); g_fence = nullptr; }
587 | if (g_fenceEvent) { CloseHandle(g_fenceEvent); g_fenceEvent = nullptr; }
588 | if (g_pd3dDevice) { g_pd3dDevice->Release(); g_pd3dDevice = nullptr; }
589 |
590 | #ifdef DX12_ENABLE_DEBUG_LAYER
591 | IDXGIDebug1* pDebug = nullptr;
592 | if (SUCCEEDED(DXGIGetDebugInterface1(0, IID_PPV_ARGS(&pDebug))))
593 | {
594 | pDebug->ReportLiveObjects(DXGI_DEBUG_ALL, DXGI_DEBUG_RLO_SUMMARY);
595 | pDebug->Release();
596 | }
597 | #endif
598 | }
599 |
600 | bool CreateSwapchain(HWND hWnd)
601 | {
602 | CleanupRenderTarget();
603 |
604 | if (g_pSwapChain)
605 | {
606 | g_pSwapChain->Release();
607 | g_pSwapChain = nullptr;
608 | }
609 |
610 | // Setup swap chain
611 | DXGI_SWAP_CHAIN_DESC1 sd;
612 | ZeroMemory(&sd, sizeof(sd));
613 | sd.BufferCount = APP_NUM_BACK_BUFFERS;
614 | sd.Width = 0;
615 | sd.Height = 0;
616 | sd.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
617 | sd.Flags = g_SwapChainEnableWaitableObject ? DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT : 0;
618 | sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
619 | sd.SampleDesc.Count = 1;
620 | sd.SampleDesc.Quality = 0;
621 | sd.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
622 | sd.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
623 | sd.Scaling = DXGI_SCALING_STRETCH;
624 | sd.Stereo = FALSE;
625 |
626 | IDXGIFactory5* dxgiFactory = nullptr;
627 | IDXGISwapChain1* swapChain1 = nullptr;
628 | if (CreateDXGIFactory1(IID_PPV_ARGS(&dxgiFactory)) != S_OK)
629 | return false;
630 |
631 | BOOL allow_tearing = FALSE;
632 | dxgiFactory->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allow_tearing, sizeof(allow_tearing));
633 | g_SwapChainTearingSupport = allow_tearing == TRUE && g_SwapChainEnableTearing;
634 | if (g_SwapChainTearingSupport)
635 | sd.Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
636 |
637 | if (dxgiFactory->CreateSwapChainForHwnd(g_pd3dCommandQueue, hWnd, &sd, nullptr, nullptr, &swapChain1) != S_OK)
638 | return false;
639 | if (swapChain1->QueryInterface(IID_PPV_ARGS(&g_pSwapChain)) != S_OK)
640 | return false;
641 | if (g_SwapChainTearingSupport)
642 | dxgiFactory->MakeWindowAssociation(hWnd, DXGI_MWA_NO_ALT_ENTER);
643 |
644 | swapChain1->Release();
645 | dxgiFactory->Release();
646 |
647 | if (g_hSwapChainWaitableObject)
648 | CloseHandle(g_hSwapChainWaitableObject);
649 |
650 | if (g_SwapChainEnableWaitableObject)
651 | {
652 | g_pSwapChain->SetMaximumFrameLatency(g_SwapChainMaximumFrameLatency);
653 | g_hSwapChainWaitableObject = g_pSwapChain->GetFrameLatencyWaitableObject();
654 | }
655 | else
656 | {
657 | g_hSwapChainWaitableObject = nullptr;
658 | }
659 |
660 | for (UINT i = 0; i < APP_NUM_BACK_BUFFERS; i++)
661 | {
662 | ID3D12Resource* pBackBuffer = nullptr;
663 | g_pSwapChain->GetBuffer(i, IID_PPV_ARGS(&pBackBuffer));
664 | g_pd3dDevice->CreateRenderTargetView(pBackBuffer, nullptr, g_mainRenderTargetDescriptor[i]);
665 | g_mainRenderTargetResource[i] = pBackBuffer;
666 | }
667 | return true;
668 | }
669 |
670 | void WaitForSwapchain()
671 | {
672 | PROFILE_CPU_SCOPE();
673 | if (g_hSwapChainWaitableObject)
674 | {
675 | ::WaitForSingleObject(g_hSwapChainWaitableObject, INFINITE);
676 | }
677 | }
678 |
679 | void CleanupRenderTarget()
680 | {
681 | WaitForPendingOperations();
682 |
683 | for (UINT i = 0; i < APP_NUM_BACK_BUFFERS; i++)
684 | if (g_mainRenderTargetResource[i]) { g_mainRenderTargetResource[i]->Release(); g_mainRenderTargetResource[i] = nullptr; }
685 | }
686 |
687 | void WaitForPendingOperations()
688 | {
689 | g_pd3dCommandQueue->Signal(g_fence, ++g_fenceLastSignaledValue);
690 |
691 | g_fence->SetEventOnCompletion(g_fenceLastSignaledValue, g_fenceEvent);
692 | ::WaitForSingleObject(g_fenceEvent, INFINITE);
693 | }
694 |
695 | FrameContext* WaitForNextFrameContext()
696 | {
697 | PROFILE_CPU_SCOPE();
698 |
699 | FrameContext* frame_context = &g_frameContext[g_frameIndex % APP_NUM_FRAMES_IN_FLIGHT];
700 | if (g_fence->GetCompletedValue() < frame_context->FenceValue)
701 | {
702 | g_fence->SetEventOnCompletion(frame_context->FenceValue, g_fenceEvent);
703 | ::WaitForSingleObject(g_fenceEvent, INFINITE);
704 | }
705 |
706 | return frame_context;
707 | }
708 |
709 | // Forward declare message handler from imgui_impl_win32.cpp
710 | extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
711 |
712 | // Win32 message handler
713 | // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
714 | // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data.
715 | // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.
716 | // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
717 | LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
718 | {
719 | if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam))
720 | return true;
721 |
722 | switch (msg)
723 | {
724 | case WM_SIZE:
725 | if (g_pd3dDevice != nullptr && wParam != SIZE_MINIMIZED)
726 | {
727 | CreateSwapchain(hWnd);
728 | }
729 | return 0;
730 | case WM_SYSCOMMAND:
731 | if ((wParam & 0xfff0) == SC_KEYMENU) // Disable ALT application menu
732 | return 0;
733 | break;
734 | case WM_DESTROY:
735 | ::PostQuitMessage(0);
736 | return 0;
737 | }
738 | return ::DefWindowProcW(hWnd, msg, wParam, lParam);
739 | }
740 |
--------------------------------------------------------------------------------
/Profiler.cpp:
--------------------------------------------------------------------------------
1 |
2 | #include "Profiler.h"
3 |
4 | #if WITH_PROFILING
5 |
6 | #define NOMINMAX
7 | #include
8 | #include
9 |
10 | Profiler gProfiler;
11 | GPUProfiler gGPUProfiler;
12 |
13 |
14 | // Thread-safe page allocator that recycles pages based on ID
15 | struct ProfilerAllocator
16 | {
17 | public:
18 | static constexpr uint32 cPageSize = 2 * 1024;
19 |
20 | struct Page
21 | {
22 | uint32 ID = 0;
23 | uint32 Size = 0;
24 |
25 | void* GetData()
26 | {
27 | return static_cast(this + 1);
28 | }
29 |
30 | static Page* Create(uint32 size)
31 | {
32 | void* pData = new char[sizeof(Page) + size];
33 | Page* pPage = new (pData) Page;
34 | pPage->Size = size;
35 | return pPage;
36 | }
37 |
38 | static void Release(Page* pPage)
39 | {
40 | char* pData = reinterpret_cast(pPage);
41 | delete[] pData;
42 | }
43 | };
44 | static_assert(std::is_trivially_destructible_v);
45 |
46 | void Release()
47 | {
48 | while (!AllocatedPages.empty())
49 | {
50 | Page* pPage = AllocatedPages.front();
51 | Page::Release(pPage);
52 | AllocatedPages.pop();
53 | }
54 | while (FreePages.empty())
55 | {
56 | Page* pPage = FreePages.back();
57 | Page::Release(pPage);
58 | FreePages.pop_back();
59 | }
60 | }
61 |
62 | Page* AllocatePage(uint32 id)
63 | {
64 | std::lock_guard m(PageLock);
65 |
66 | Page* pPage = nullptr;
67 | if (FreePages.empty())
68 | {
69 | pPage = Page::Create(cPageSize);
70 | ++NumPages;
71 | }
72 | else
73 | {
74 | pPage = FreePages.back();
75 | FreePages.pop_back();
76 | }
77 | pPage->ID = id;
78 | AllocatedPages.push(pPage);
79 | return pPage;
80 | }
81 |
82 | bool IsValidPage(uint32 id)
83 | {
84 | return id >= MinValidID;
85 | }
86 |
87 | void Evict(uint32 id)
88 | {
89 | std::lock_guard m(PageLock);
90 |
91 | gAssert(NumPages == FreePages.size() + AllocatedPages.size());
92 |
93 | while (!AllocatedPages.empty())
94 | {
95 | Page* pPage = AllocatedPages.front();
96 | if (id >= pPage->ID)
97 | {
98 | FreePages.push_back(pPage);
99 | AllocatedPages.pop();
100 | }
101 | else
102 | {
103 | break;
104 | }
105 | }
106 |
107 | MinValidID = id + 1;
108 | }
109 |
110 | private:
111 | std::mutex PageLock;
112 | Array FreePages;
113 | std::queue AllocatedPages;
114 | uint32 MinValidID = 0;
115 | int NumPages = 0;
116 | };
117 |
118 | static ProfilerAllocator sProfilerAllocator;
119 |
120 | class SubAllocator
121 | {
122 | public:
123 | const char* String(const char* pStr, uint32 id)
124 | {
125 | uint32 len = (uint32)strlen(pStr) + 1;
126 | char* pData = (char*)Allocate(len, id);
127 | strcpy_s(pData, len, pStr);
128 | return pData;
129 | }
130 |
131 | void* Allocate(uint32 size, uint32 id)
132 | {
133 | if (pPage == nullptr || id > ID || !sProfilerAllocator.IsValidPage(ID) || Offset + size > pPage->Size)
134 | {
135 | ID = std::max(ID, id);
136 | pPage = sProfilerAllocator.AllocatePage(ID);
137 | Offset = 0;
138 | }
139 | void* pData = static_cast(pPage->GetData()) + Offset;
140 | Offset += size;
141 | return pData;
142 | }
143 |
144 | private:
145 | uint32 Offset = 0;
146 | uint32 ID = 0;
147 | ProfilerAllocator::Page* pPage = nullptr;
148 | };
149 | static thread_local SubAllocator gAllocator;
150 |
151 | static uint32 HSV2RGB(float hue, float saturation, float value)
152 | {
153 | float R = std::max(std::min(fabs(hue * 6 - 3) - 1, 1.0f), 0.0f);
154 | float G = std::max(std::min(2 - fabs(hue * 6 - 2), 1.0f), 0.0f);
155 | float B = std::max(std::min(2 - fabs(hue * 6 - 4), 1.0f), 0.0f);
156 |
157 | R = ((R - 1) * saturation + 1) * value;
158 | G = ((G - 1) * saturation + 1) * value;
159 | B = ((B - 1) * saturation + 1) * value;
160 |
161 | return ((uint8)roundf(R * 255.0f) << 0) |
162 | ((uint8)roundf(G * 255.0f) << 8) |
163 | ((uint8)roundf(B * 255.0f) << 16) |
164 | 255 << 24;
165 | }
166 |
167 | static uint32 GetFrameColor(uint32 frameIndex)
168 | {
169 | return HSV2RGB(frameIndex % 10 / 10.0f, 0.5f, 0.5f);
170 | }
171 |
172 | static uint32 ColorFromString(const char* pStr)
173 | {
174 | const float saturation = 0.5f;
175 | const float value = 0.6f;
176 | float hue = (float)std::hash{}(pStr) / std::numeric_limits::max();
177 | return HSV2RGB(hue, saturation, value);
178 | }
179 |
180 |
181 | //-----------------------------------------------------------------------------
182 | // [SECTION] GPU Profiler
183 | //-----------------------------------------------------------------------------
184 |
185 | static constexpr const char* GetCommandQueueName(D3D12_COMMAND_LIST_TYPE type)
186 | {
187 | switch (type)
188 | {
189 | case D3D12_COMMAND_LIST_TYPE_DIRECT: return "Direct Queue";
190 | case D3D12_COMMAND_LIST_TYPE_COMPUTE: return "Compute Queue";
191 | case D3D12_COMMAND_LIST_TYPE_COPY: return "Copy Queue";
192 | case D3D12_COMMAND_LIST_TYPE_VIDEO_DECODE: return "Video Decode Queue";
193 | case D3D12_COMMAND_LIST_TYPE_VIDEO_ENCODE: return "Video Encode Queue";
194 | case D3D12_COMMAND_LIST_TYPE_VIDEO_PROCESS: return "Video Process Queue";
195 | default: return "Unknown Queue";
196 | }
197 | }
198 |
199 | void GPUProfiler::Initialize(ID3D12Device* pDevice, Span queues, uint32 frameLatency)
200 | {
201 | gAssert(frameLatency >= 1, "Frame Latency must be at least 1");
202 |
203 | m_FrameLatency = frameLatency;
204 |
205 | InitializeSRWLock((PSRWLOCK)&m_CommandListMapLock);
206 | QueryPerformanceFrequency((LARGE_INTEGER*)&m_CPUTickFrequency);
207 |
208 | m_QueueEventStack.resize(queues.size());
209 | for (uint32 queueIndex = 0; queueIndex < queues.size(); ++queueIndex)
210 | {
211 | ID3D12CommandQueue* pQueue = queues[queueIndex];
212 | D3D12_COMMAND_QUEUE_DESC desc = pQueue->GetDesc();
213 |
214 | m_QueueIndexMap[pQueue] = (uint32)m_Queues.size();
215 | QueueInfo& queueInfo = m_Queues.emplace_back();
216 | uint32 size = ARRAYSIZE(queueInfo.Name);
217 | constexpr GUID ID_D3DDebugObjectName = { 0x429b8c22, 0x9188, 0x4b0c, 0x87, 0x42, 0xac, 0xb0, 0xbf, 0x85, 0xc2, 0x00 };
218 | if (FAILED(pQueue->GetPrivateData(ID_D3DDebugObjectName, &size, queueInfo.Name)))
219 | strcpy_s(queueInfo.Name, GetCommandQueueName(desc.Type));
220 | queueInfo.pQueue = pQueue;
221 | queueInfo.Index = queueIndex;
222 | queueInfo.QueryHeapIndex = desc.Type == D3D12_COMMAND_LIST_TYPE_COPY ? 1 : 0;
223 | gVerifyHR(pQueue->GetClockCalibration(&queueInfo.GPUCalibrationTicks, &queueInfo.CPUCalibrationTicks));
224 | gVerifyHR(pQueue->GetTimestampFrequency(&queueInfo.GPUFrequency));
225 |
226 | if (!GetHeap(queueInfo.QueryHeapIndex).IsInitialized())
227 | GetHeap(queueInfo.QueryHeapIndex).Initialize(pDevice, pQueue, QueryData::cMaxNumQueries, frameLatency);
228 |
229 | queueInfo.TrackIndex = gProfiler.RegisterTrack(queueInfo.Name, Profiler::EventTrack::EType::GPU, queueInfo.Index);
230 | }
231 |
232 | // Maximum number of events is the number of queries is the total capacity of all query heaps divided by 2 (a pair of queries per event)
233 | int queryCapacity = 0;
234 | for (QueryHeap& heap : m_QueryHeaps)
235 | queryCapacity += heap.GetQueryCapacity();
236 |
237 |
238 | m_QueryData.resize(frameLatency);
239 | for (uint32 i = 0; i < frameLatency; ++i)
240 | {
241 | m_QueryData[i].Pairs.resize(queryCapacity / 2);
242 | m_QueryData[i].Events.resize(queryCapacity / 2);
243 | }
244 |
245 | m_IsInitialized = true;
246 | }
247 |
248 | void GPUProfiler::Shutdown()
249 | {
250 | for (QueryHeap& heap : m_QueryHeaps)
251 | heap.Shutdown();
252 |
253 | for (auto& commandListState : m_CommandListMap)
254 | delete commandListState.second;
255 | m_CommandListMap.clear();
256 |
257 | m_QueryData.clear();
258 |
259 | m_Queues.clear();
260 | m_QueueIndexMap.clear();
261 | }
262 |
263 | void GPUProfiler::BeginEvent(ID3D12GraphicsCommandList* pCmd, const char* pName, uint32 color, const char* pFilePath, uint32 lineNumber)
264 | {
265 | if (!m_IsInitialized)
266 | return;
267 |
268 | if (m_EventCallback.OnEventBegin)
269 | m_EventCallback.OnEventBegin(pName, pCmd, m_EventCallback.pUserData);
270 |
271 | if (m_IsPaused)
272 | return;
273 |
274 | // Register a query on the commandlist
275 | CommandListState& cmdListState = *GetState(pCmd, true);
276 | QueryData::Query& cmdListQuery = cmdListState.Queries.emplace_back();
277 |
278 | QueryData& queryData = GetQueryData();
279 |
280 | // Allocate a query range. This stores a begin/end query index pair. (Also event index)
281 | uint32 eventIndex = m_EventIndex.fetch_add(1);
282 | if (eventIndex >= queryData.Events.size())
283 | return;
284 |
285 | // Record a timestamp query and assign to the commandlist
286 | cmdListQuery.QueryIndex = GetHeap(pCmd->GetType()).RecordQuery(pCmd);
287 | cmdListQuery.EventIndex = eventIndex;
288 |
289 | // Allocate an event in the sample history
290 | ProfilerEvent& event = queryData.Events[eventIndex];
291 | event.pName = gAllocator.String(pName, m_FrameIndex);
292 | event.pFilePath = pFilePath;
293 | event.LineNumber = lineNumber;
294 | event.Color = color == 0 ? ColorFromString(pName) : color;
295 | }
296 |
297 | void GPUProfiler::EndEvent(ID3D12GraphicsCommandList* pCmd)
298 | {
299 | if (!m_IsInitialized)
300 | return;
301 |
302 | if (m_EventCallback.OnEventEnd)
303 | m_EventCallback.OnEventEnd(pCmd, m_EventCallback.pUserData);
304 |
305 | if (m_IsPaused)
306 | return;
307 |
308 | // Record a timestamp query and assign to the commandlist
309 | CommandListState& cmdListState = *GetState(pCmd, true);
310 | QueryData::Query& query = cmdListState.Queries.emplace_back();
311 | query.QueryIndex = GetHeap(pCmd->GetType()).RecordQuery(pCmd);
312 | query.EventIndex = QueryData::Query::EndEventFlag; // Range index to indicate this is an 'End' query
313 | }
314 |
315 | // Process the last frame and advance to the next
316 | void GPUProfiler::Tick()
317 | {
318 | if (!m_IsInitialized)
319 | return;
320 |
321 | for (ActiveEventStack& stack : m_QueueEventStack)
322 | gAssert(stack.GetSize() == 0, "EventStack for the CommandQueue should be empty. Forgot to `End()` %d Events", stack.GetSize());
323 |
324 | // Poll query heap and populate event timings
325 | while (m_FrameToReadback < m_FrameIndex)
326 | {
327 | // Wait for all query heaps to have finished resolving the queries for the readback frame
328 | bool allHeapsReady = true;
329 | for (QueryHeap& heap : m_QueryHeaps)
330 | allHeapsReady &= heap.IsFrameComplete(m_FrameToReadback);
331 | if (!allHeapsReady)
332 | break;
333 |
334 | std::scoped_lock lock(m_QueryRangeLock);
335 |
336 | QueryData& queryData = GetQueryData(m_FrameToReadback);
337 | for (uint32 i = 0; i < queryData.NumEvents; ++i)
338 | {
339 | ProfilerEvent& event = queryData.Events[i];
340 | QueryData::QueryPair& queryRange = queryData.Pairs[i];
341 | gAssert(queryRange.IsValid());
342 |
343 | const QueueInfo& queue = m_Queues[event.QueueIndex];
344 | Span queries = m_QueryHeaps[queue.QueryHeapIndex].GetQueryData(m_FrameToReadback);
345 |
346 | // Convert to CPU ticks and assign to event
347 | event.TicksBegin = ConvertToCPUTicks(queue, queries[queryRange.QueryIndexBegin]);
348 | event.TicksEnd = ConvertToCPUTicks(queue, queries[queryRange.QueryIndexEnd]);
349 |
350 | // Invalidate
351 | queryRange = {};
352 |
353 | gProfiler.AddEvent(queue.TrackIndex, event, m_FrameToReadback);
354 | }
355 | queryData.NumEvents = 0;
356 |
357 | ++m_FrameToReadback;
358 | }
359 |
360 | m_IsPaused = m_PauseQueued;
361 | if (m_IsPaused)
362 | return;
363 |
364 | for (const auto& data: m_CommandListMap)
365 | gAssert(data.second->Queries.empty(), "The Queries inside the commandlist is not empty. This is because ExecuteCommandLists was not called with this commandlist.");
366 |
367 | for (QueryHeap& heap : m_QueryHeaps)
368 | heap.Resolve(m_FrameIndex);
369 |
370 | ++m_FrameIndex;
371 |
372 | {
373 | for (QueryHeap& heap : m_QueryHeaps)
374 | heap.Reset(m_FrameIndex);
375 |
376 | m_EventIndex = 0;
377 | }
378 | }
379 |
380 | void GPUProfiler::ExecuteCommandLists(const ID3D12CommandQueue* pQueue, Span commandLists)
381 | {
382 | if (!m_IsInitialized)
383 | return;
384 |
385 | if (m_IsPaused)
386 | return;
387 |
388 | auto it = m_QueueIndexMap.find(pQueue);
389 | if (it == m_QueueIndexMap.end())
390 | return;
391 |
392 | std::scoped_lock lock(m_QueryRangeLock);
393 |
394 | uint32 queueIndex = it->second;
395 | ActiveEventStack& eventStack = m_QueueEventStack[queueIndex];
396 | QueryData& queryData = GetQueryData();
397 | queryData.NumEvents = m_EventIndex;
398 |
399 | for (ID3D12CommandList* pCmd : commandLists)
400 | {
401 | CommandListState* pCommandListQueries = GetState(pCmd, false);
402 | if (pCommandListQueries)
403 | {
404 | for (QueryData::Query& query : pCommandListQueries->Queries)
405 | {
406 | if (query.EventIndex != QueryData::Query::EndEventFlag)
407 | {
408 | // If it's a "BeginEvent", add to the stack
409 | eventStack.Push() = query;
410 | if (query.EventIndex == QueryData::Query::InvalidEventFlag)
411 | continue;
412 |
413 | ProfilerEvent& sampleEvent = queryData.Events[query.EventIndex];
414 | sampleEvent.QueueIndex = queueIndex;
415 | }
416 | else
417 | {
418 | // If it's an "EndEvent", pop top query from the stack and pair up
419 | gAssert(eventStack.GetSize() > 0, "Event Begin/End mismatch");
420 | QueryData::Query beginEventQuery = eventStack.Pop();
421 | if (beginEventQuery.EventIndex == QueryData::Query::InvalidEventFlag)
422 | continue;
423 |
424 | // Pair up Begin/End query indices
425 | QueryData::QueryPair& pair = queryData.Pairs[beginEventQuery.EventIndex];
426 | pair.QueryIndexBegin = beginEventQuery.QueryIndex;
427 | pair.QueryIndexEnd = query.QueryIndex;
428 |
429 | // Compute event depth
430 | ProfilerEvent& sampleEvent = queryData.Events[beginEventQuery.EventIndex];
431 | sampleEvent.Depth = eventStack.GetSize();
432 | gAssert(sampleEvent.QueueIndex == queueIndex, "Begin/EndEvent must be recorded on the same queue");
433 | }
434 | }
435 | pCommandListQueries->Queries.clear();
436 | }
437 | }
438 | }
439 |
440 |
441 | GPUProfiler::CommandListState* GPUProfiler::GetState(ID3D12CommandList* pCmd, bool createIfNotFound)
442 | {
443 | // See if it's already tracked
444 | AcquireSRWLockShared((PSRWLOCK)&m_CommandListMapLock);
445 | auto it = m_CommandListMap.find(pCmd);
446 | CommandListState* pState = it != m_CommandListMap.end() ? it->second : nullptr;
447 | ReleaseSRWLockShared((PSRWLOCK)&m_CommandListMapLock);
448 |
449 | if (!pState && createIfNotFound)
450 | {
451 | // If not, register new commandlist
452 | // TODO: Pool CommandListStates in case ID3D12CommandLists are often recreated
453 | pState = new CommandListState(this, pCmd);
454 |
455 | AcquireSRWLockExclusive((PSRWLOCK)&m_CommandListMapLock);
456 | m_CommandListMap[pCmd] = pState;
457 | ReleaseSRWLockExclusive((PSRWLOCK)&m_CommandListMapLock);
458 | }
459 | return pState;
460 | }
461 |
462 |
463 |
464 | GPUProfiler::CommandListState::CommandListState(GPUProfiler* profiler, ID3D12CommandList* pCmd)
465 | : pProfiler(profiler), pCommandList(pCmd)
466 | {
467 | ID3DDestructionNotifier* destruction_notifier = nullptr;
468 | gVerifyHR(pCmd->QueryInterface(&destruction_notifier));
469 | gVerifyHR(destruction_notifier->RegisterDestructionCallback([](void* pContext) {
470 | GPUProfiler::CommandListState* pState = (GPUProfiler::CommandListState*)pContext;
471 |
472 | AcquireSRWLockExclusive((PSRWLOCK)&pState->pProfiler->m_CommandListMapLock);
473 | pState->pProfiler->m_CommandListMap.erase(pState->pCommandList);
474 | ReleaseSRWLockExclusive((PSRWLOCK)&pState->pProfiler->m_CommandListMapLock);
475 |
476 | delete pState;
477 | }, this, &DestructionEventID));
478 |
479 | destruction_notifier->Release();
480 | }
481 |
482 |
483 |
484 | GPUProfiler::CommandListState::~CommandListState()
485 | {
486 | ID3DDestructionNotifier* destruction_notifier = nullptr;
487 | gVerifyHR(pCommandList->QueryInterface(&destruction_notifier));
488 | gVerifyHR(destruction_notifier->UnregisterDestructionCallback(DestructionEventID));
489 | destruction_notifier->Release();
490 | }
491 |
492 |
493 |
494 | void GPUProfiler::QueryHeap::Initialize(ID3D12Device* pDevice, ID3D12CommandQueue* pResolveQueue, uint32 maxNumQueries, uint32 frameLatency)
495 | {
496 | gAssert(gProfiler.IsInitialized());
497 |
498 | m_pResolveQueue = pResolveQueue;
499 | m_FrameLatency = frameLatency;
500 | m_MaxNumQueries = maxNumQueries;
501 |
502 | D3D12_COMMAND_QUEUE_DESC queueDesc = pResolveQueue->GetDesc();
503 |
504 | D3D12_QUERY_HEAP_DESC heapDesc{
505 | .Type = queueDesc.Type == D3D12_COMMAND_LIST_TYPE_COPY ? D3D12_QUERY_HEAP_TYPE_COPY_QUEUE_TIMESTAMP : D3D12_QUERY_HEAP_TYPE_TIMESTAMP,
506 | .Count = maxNumQueries,
507 | .NodeMask = 0x1,
508 | };
509 | gVerifyHR(pDevice->CreateQueryHeap(&heapDesc, IID_PPV_ARGS(&m_pQueryHeap)));
510 |
511 | for (uint32 i = 0; i < frameLatency; ++i)
512 | gVerifyHR(pDevice->CreateCommandAllocator(queueDesc.Type, IID_PPV_ARGS(&m_CommandAllocators.emplace_back())));
513 | gVerifyHR(pDevice->CreateCommandList(0x1, queueDesc.Type, m_CommandAllocators[0], nullptr, IID_PPV_ARGS(&m_pCommandList)));
514 |
515 | D3D12_RESOURCE_DESC readbackDesc{
516 | .Dimension = D3D12_RESOURCE_DIMENSION_BUFFER,
517 | .Alignment = 0,
518 | .Width = (uint64)maxNumQueries * sizeof(uint64) * frameLatency,
519 | .Height = 1,
520 | .DepthOrArraySize = 1,
521 | .MipLevels = 1,
522 | .Format = DXGI_FORMAT_UNKNOWN,
523 | .SampleDesc = {
524 | .Count = 1,
525 | .Quality = 0,
526 | },
527 | .Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
528 | .Flags = D3D12_RESOURCE_FLAG_NONE,
529 | };
530 |
531 | D3D12_HEAP_PROPERTIES heapProps{
532 | .Type = D3D12_HEAP_TYPE_READBACK,
533 | .CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN,
534 | .MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN,
535 | .CreationNodeMask = 0,
536 | .VisibleNodeMask = 0,
537 | };
538 |
539 | gVerifyHR(pDevice->CreateCommittedResource(&heapProps, D3D12_HEAP_FLAG_NONE, &readbackDesc, D3D12_RESOURCE_STATE_COMMON, nullptr, IID_PPV_ARGS(&m_pReadbackResource)));
540 | void* pReadbackData = nullptr;
541 | gVerifyHR(m_pReadbackResource->Map(0, nullptr, &pReadbackData));
542 | m_ReadbackData = Span(static_cast(pReadbackData), maxNumQueries * frameLatency);
543 |
544 | gVerifyHR(pDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&m_pResolveFence)));
545 | }
546 |
547 | void GPUProfiler::QueryHeap::Shutdown()
548 | {
549 | if (!IsInitialized())
550 | return;
551 |
552 | for (ID3D12CommandAllocator* pAllocator : m_CommandAllocators)
553 | pAllocator->Release();
554 | m_pCommandList->Release();
555 | m_pQueryHeap->Release();
556 | m_pReadbackResource->Release();
557 | m_pResolveFence->Release();
558 | }
559 |
560 | uint32 GPUProfiler::QueryHeap::RecordQuery(ID3D12GraphicsCommandList* pCmd)
561 | {
562 | uint32 index = m_QueryIndex.fetch_add(1);
563 | if (index >= m_MaxNumQueries)
564 | return 0xFFFFFFFF;
565 |
566 | pCmd->EndQuery(m_pQueryHeap, D3D12_QUERY_TYPE_TIMESTAMP, index);
567 | return index;
568 | }
569 |
570 | uint32 GPUProfiler::QueryHeap::Resolve(uint32 frameIndex)
571 | {
572 | if (!IsInitialized())
573 | return 0;
574 |
575 | uint32 frameBit = frameIndex % m_FrameLatency;
576 | uint32 queryStart = frameBit * m_MaxNumQueries;
577 | uint32 numQueries = std::min(m_MaxNumQueries, (uint32)m_QueryIndex);
578 | m_pCommandList->ResolveQueryData(m_pQueryHeap, D3D12_QUERY_TYPE_TIMESTAMP, 0, numQueries, m_pReadbackResource, queryStart * sizeof(uint64));
579 | gVerifyHR(m_pCommandList->Close());
580 | ID3D12CommandList* pCmdLists[] = { m_pCommandList };
581 | m_pResolveQueue->ExecuteCommandLists(1, pCmdLists);
582 | gVerifyHR(m_pResolveQueue->Signal(m_pResolveFence, frameIndex));
583 | return numQueries;
584 | }
585 |
586 | void GPUProfiler::QueryHeap::Reset(uint32 frameIndex)
587 | {
588 | if (!IsInitialized())
589 | return;
590 |
591 | // Don't advance to the next frame until the GPU has caught up until at least the frame latency
592 | if (frameIndex >= m_FrameLatency)
593 | {
594 | uint32 wait_frame = frameIndex - m_FrameLatency;
595 | if (!IsFrameComplete(wait_frame))
596 | m_pResolveFence->SetEventOnCompletion(wait_frame, nullptr);
597 | }
598 |
599 | m_QueryIndex = 0;
600 | ID3D12CommandAllocator* pAllocator = m_CommandAllocators[frameIndex % m_FrameLatency];
601 | gVerifyHR(pAllocator->Reset());
602 | gVerifyHR(m_pCommandList->Reset(pAllocator, nullptr));
603 | }
604 |
605 | bool GPUProfiler::QueryHeap::IsFrameComplete(uint64 frameIndex)
606 | {
607 | if (!IsInitialized())
608 | return true;
609 |
610 | uint64 fenceValue = frameIndex;
611 | if (fenceValue <= m_LastCompletedFence && m_LastCompletedFence > 0)
612 | return true;
613 | m_LastCompletedFence = std::max(m_pResolveFence->GetCompletedValue(), m_LastCompletedFence);
614 | return fenceValue <= m_LastCompletedFence;
615 | }
616 |
617 | //-----------------------------------------------------------------------------
618 | // [SECTION] CPU Profiler
619 | //-----------------------------------------------------------------------------
620 |
621 | void Profiler::Initialize(uint32 historySize)
622 | {
623 | m_HistorySize = historySize;
624 | m_IsInitialized = true;
625 | m_BeginFrameTicks.resize(historySize);
626 |
627 | uint64 frequency = 0;
628 | QueryPerformanceFrequency((LARGE_INTEGER*)&frequency);
629 | m_MsToTicks = (uint32)(frequency / 1000);
630 | }
631 |
632 | void Profiler::Shutdown()
633 | {
634 | m_Tracks.clear();
635 | m_BeginFrameTicks.clear();
636 |
637 | sProfilerAllocator.Release();
638 | }
639 |
640 | // Begin a new CPU event on the current thread
641 | void Profiler::BeginEvent(const char* pName, uint32 color, const char* pFilePath, uint32 lineNumber)
642 | {
643 | if (!m_IsInitialized)
644 | return;
645 |
646 | if (m_EventCallback.OnEventBegin)
647 | m_EventCallback.OnEventBegin(pName, m_EventCallback.pUserData);
648 |
649 | if (m_Paused)
650 | return;
651 |
652 | // Record new event
653 | EventTrack& track = GetCurrentThreadTrack();
654 | ProfilerEventData& eventData = track.GetFrameData(m_FrameIndex);
655 | track.EventStack.Push() = (uint32)eventData.size();
656 |
657 | ProfilerEvent& newEvent = eventData.emplace_back();
658 | newEvent.Depth = track.EventStack.GetSize() - 1;
659 | newEvent.ThreadIndex = track.Index;
660 | newEvent.pName = gAllocator.String(pName, m_FrameIndex);
661 | newEvent.pFilePath = pFilePath;
662 | newEvent.LineNumber = lineNumber;
663 | newEvent.Color = color == 0 ? ColorFromString(pName) : color;
664 | QueryPerformanceCounter((LARGE_INTEGER*)(&newEvent.TicksBegin));
665 | }
666 |
667 | // End the last pushed event on the current thread
668 | void Profiler::EndEvent()
669 | {
670 | if (!m_IsInitialized)
671 | return;
672 |
673 | if (m_EventCallback.OnEventEnd)
674 | m_EventCallback.OnEventEnd(m_EventCallback.pUserData);
675 |
676 | if (m_Paused)
677 | return;
678 |
679 | // End and pop an event of the stack
680 | EventTrack& track = GetCurrentThreadTrack();
681 |
682 | gAssert(track.EventStack.GetSize() > 0, "Event mismatch. Called EndEvent more than BeginEvent");
683 | uint32 eventIndex = track.EventStack.Pop();
684 | ProfilerEvent& event = track.GetFrameData(m_FrameIndex)[eventIndex];
685 | QueryPerformanceCounter((LARGE_INTEGER*)(&event.TicksEnd));
686 | }
687 |
688 |
689 | void Profiler::AddEvent(uint32 trackIndex, const ProfilerEvent& event, uint32 frameIndex)
690 | {
691 | EventTrack& track = m_Tracks[trackIndex];
692 | ProfilerEventData& events = track.GetFrameData(frameIndex);
693 |
694 | // Name must be copied
695 | ProfilerEvent newEvent = event;
696 | newEvent.pName = gAllocator.String(newEvent.pName, frameIndex);
697 |
698 | events.push_back(newEvent);
699 | }
700 |
701 |
702 | void Profiler::Present(IDXGISwapChain* pSwapChain)
703 | {
704 | if (m_PresentTrackIndex == -1)
705 | m_PresentTrackIndex = RegisterTrack("Present", EventTrack::EType::Present, 0);
706 |
707 | if (!m_Paused)
708 | {
709 | // Add an entry for the current present
710 | uint32 presentID;
711 | if (SUCCEEDED(pSwapChain->GetLastPresentCount(&presentID)))
712 | {
713 | // If the last queried present is larger than the current present,
714 | // that means the swapchain may have been recreated and we need to reset.
715 | if (m_LastQueuedPresentID > presentID || m_pPresentSwapChain != pSwapChain)
716 | {
717 | m_LastQueriedPresentID = 0;
718 | m_LastProcessedPresentID = 0;
719 | m_LastProcessedValidPresentID = 0;
720 | m_PresentQueue = {};
721 | m_LastSyncRefreshCount = 0;
722 | m_pPresentSwapChain = pSwapChain;
723 | }
724 |
725 | PresentEntry* pEntry = GetPresentEntry(presentID, true);
726 | *pEntry = {};
727 | QueryPerformanceCounter((LARGE_INTEGER*)&pEntry->PresentQPC);
728 | pEntry->PresentID = presentID;
729 | pEntry->FrameIndex = m_FrameIndex;
730 |
731 | m_LastQueuedPresentID = presentID;
732 | }
733 |
734 | DXGI_FRAME_STATISTICS frameStats{};
735 | while (SUCCEEDED(pSwapChain->GetFrameStatistics(&frameStats)) && frameStats.PresentCount > m_LastQueriedPresentID)
736 | {
737 | gAssert(frameStats.SyncQPCTime.QuadPart != 0);
738 |
739 | // Update the entry that was presented. All the ones before that were not queried are considered dropped.
740 | PresentEntry* pEntry = GetPresentEntry(frameStats.PresentCount, false);
741 | if (pEntry)
742 | pEntry->DisplayQPC = frameStats.SyncQPCTime.QuadPart;
743 |
744 | // It's possible for the CPU to miss a refresh in the time multiple presents have been processed (and not dropped).
745 | // In this case, there isn't enough information to resolve it. Try to estimate, otherwise ignore the frame.
746 | uint32 numRefreshes = frameStats.SyncRefreshCount - m_LastSyncRefreshCount;
747 | if (frameStats.SyncRefreshCount > 0 && numRefreshes > 1)
748 | {
749 | // If the previous entry was not processed, it means we missed it in GetFrameStatistics
750 | PresentEntry* pPreviousEntry = GetPresentEntry(frameStats.PresentCount - 1, false);
751 | if (pPreviousEntry && pPreviousEntry->DisplayQPC == PresentEntry::sQPCDroppedFrame)
752 | {
753 | // If there was a valid present before the previous one, average the time to at least get some estimate
754 | PresentEntry* pPreviousPreviousEntry = GetPresentEntry(frameStats.PresentCount - 2, false);
755 | if (pPreviousPreviousEntry && pEntry)
756 | {
757 | // printf("MISSED FRAME: %d - ID %d\n", pPreviousEntry->FrameIndex, pPreviousEntry->PresentID);
758 |
759 | // Taking a guess: the sync happened one sync after the one before it (most likely timing to have been missed)
760 | uint64 duration = pEntry->DisplayQPC - pPreviousPreviousEntry->DisplayQPC;
761 | uint64 estimatedSyncTime = pPreviousPreviousEntry->DisplayQPC + duration / numRefreshes;
762 |
763 | pPreviousEntry->DisplayQPC = estimatedSyncTime;
764 | }
765 | else
766 | {
767 | // If there's no way to estimate, just drop it
768 | pPreviousEntry->DisplayQPC = PresentEntry::sQPCMissedFrame;
769 | }
770 | }
771 | }
772 |
773 | m_LastSyncRefreshCount = frameStats.SyncRefreshCount;
774 | m_LastQueriedPresentID = frameStats.PresentCount;
775 | }
776 | }
777 |
778 | uint32 stackSize = 0;
779 |
780 | // Process all entries up until the last present
781 | for (uint32 presentID = m_LastProcessedPresentID + 1; presentID < m_LastQueriedPresentID; ++presentID)
782 | {
783 | const PresentEntry* pToProcessEntry = GetPresentEntry(presentID, false);
784 |
785 | // Search from the last processed valid entry where the next one is
786 | const PresentEntry* pNextValidPresentEntry = nullptr;
787 | for (uint32 nextValidPresent = m_LastProcessedValidPresentID; nextValidPresent <= m_LastQueriedPresentID; ++nextValidPresent)
788 | {
789 | const PresentEntry* pEntry = GetPresentEntry(nextValidPresent, false);
790 | if (pEntry && pEntry->DisplayQPC != PresentEntry::sQPCDroppedFrame)
791 | {
792 | pNextValidPresentEntry = pEntry;
793 | m_LastProcessedValidPresentID = pEntry->PresentID;
794 | }
795 | }
796 |
797 | // No valid present yet, try again next frame
798 | if (!pNextValidPresentEntry)
799 | break;
800 |
801 | if (pToProcessEntry)
802 | {
803 | if (pToProcessEntry->DisplayQPC == PresentEntry::sQPCMissedFrame)
804 | {
805 | // Ignored frame (rare, see above)
806 | }
807 | else if (pToProcessEntry->DisplayQPC == PresentEntry::sQPCDroppedFrame)
808 | {
809 | ProfilerEvent event{
810 | .pName = "Discarded",
811 | .Color = GetFrameColor(pToProcessEntry->FrameIndex),
812 | .Depth = 1,
813 | .TicksBegin = pNextValidPresentEntry->DisplayQPC,
814 | .TicksEnd = pNextValidPresentEntry->DisplayQPC + m_MsToTicks,
815 | };
816 | AddEvent(m_PresentTrackIndex, event, pToProcessEntry->FrameIndex);
817 | }
818 | else
819 | {
820 | // If the timing of the refresh is the same as the current frame, stack the events.
821 | // Wait for the next frame with a different refresh time
822 | if (pNextValidPresentEntry->DisplayQPC == pToProcessEntry->DisplayQPC)
823 | break;
824 |
825 | ProfilerEvent event{
826 | .pName = "Present",
827 | .Color = GetFrameColor(pToProcessEntry->FrameIndex),
828 | .Depth = stackSize,
829 | .TicksBegin = pToProcessEntry->DisplayQPC,
830 | .TicksEnd = pNextValidPresentEntry->DisplayQPC,
831 | };
832 | ++stackSize;
833 | AddEvent(m_PresentTrackIndex, event, pToProcessEntry->FrameIndex);
834 | }
835 | }
836 |
837 | m_LastProcessedPresentID = presentID;
838 | }
839 | }
840 |
841 |
842 |
843 | // Process the last frame and advance
844 | void Profiler::Tick()
845 | {
846 | if (!m_IsInitialized)
847 | return;
848 |
849 | m_Paused = m_QueuedPaused;
850 | if (m_Paused)
851 | return;
852 |
853 | // End the "CPU Frame" event (except on frame 0)
854 | if (m_FrameIndex)
855 | EndEvent();
856 |
857 | // Advance the frame and reset its data
858 | ++m_FrameIndex;
859 |
860 | std::scoped_lock lock(m_ThreadDataLock);
861 | for (EventTrack& track : m_Tracks)
862 | {
863 | ProfilerEventData& events = track.GetFrameData(m_FrameIndex);
864 | events.clear();
865 | }
866 |
867 | QueryPerformanceCounter((LARGE_INTEGER*)&m_BeginFrameTicks[m_FrameIndex % m_BeginFrameTicks.size()]);
868 |
869 | // Begin a "CPU Frame" event
870 | BeginEvent("CPU Frame", GetFrameColor(m_FrameIndex));
871 |
872 | if (m_FrameIndex >= m_HistorySize)
873 | sProfilerAllocator.Evict(m_FrameIndex - m_HistorySize);
874 | }
875 |
876 |
877 |
878 | // Register a new thread
879 | int Profiler::RegisterCurrentThread(const char* pName)
880 | {
881 | int& threadIndex = GetCurrentThreadTrackIndex();
882 |
883 | const char* pLocalName = pName;
884 | char name[256]{};
885 | if (!pName)
886 | {
887 | // If the name is not provided, retrieve it using GetThreadDescription()
888 | PWSTR pDescription = nullptr;
889 | if (SUCCEEDED(::GetThreadDescription(GetCurrentThread(), &pDescription)))
890 | WideCharToMultiByte(CP_UTF8, 0, pDescription, (int)wcslen(pDescription), name, ARRAYSIZE(name), nullptr, nullptr);
891 | pLocalName = name;
892 | }
893 |
894 | if (threadIndex == -1)
895 | {
896 | threadIndex = RegisterTrack(pLocalName, EventTrack::EType::CPU, GetCurrentThreadId());
897 | }
898 | else
899 | {
900 | strcpy_s(m_Tracks[threadIndex].Name, pLocalName);
901 | }
902 | return threadIndex;
903 | }
904 |
905 |
906 | // Register a new track
907 | int Profiler::RegisterTrack(const char* pName, EventTrack::EType type, uint32 id)
908 | {
909 | std::scoped_lock lock(m_ThreadDataLock);
910 |
911 | EventTrack& data = m_Tracks.emplace_back();
912 | strcpy_s(data.Name, ARRAYSIZE(data.Name), pName);
913 | data.ID = id;
914 | data.Index = (uint32)m_Tracks.size() - 1;
915 | data.Type = type;
916 | data.Events.resize(m_HistorySize);
917 |
918 | return data.Index;
919 | }
920 |
921 |
922 | #endif
923 |
--------------------------------------------------------------------------------
/IconsFontAwesome4.h:
--------------------------------------------------------------------------------
1 | // Generated by https://github.com/juliettef/IconFontCppHeaders script GenerateIconFontCppHeaders.py for languages C and C++
2 | // from https://github.com/FortAwesome/Font-Awesome/raw/4.x/src/icons.yml
3 | // for use with https://github.com/FortAwesome/Font-Awesome/blob/4.x/fonts/fontawesome-webfont.ttf
4 | #pragma once
5 |
6 | #define FONT_ICON_FILE_NAME_FA "fontawesome-webfont.ttf"
7 |
8 | #define ICON_MIN_FA 0xf000
9 | #define ICON_MAX_16_FA 0xf2e0
10 | #define ICON_MAX_FA 0xf2e0
11 | #define ICON_FA_GLASS "\xef\x80\x80" // U+f000
12 | #define ICON_FA_MUSIC "\xef\x80\x81" // U+f001
13 | #define ICON_FA_SEARCH "\xef\x80\x82" // U+f002
14 | #define ICON_FA_ENVELOPE_O "\xef\x80\x83" // U+f003
15 | #define ICON_FA_HEART "\xef\x80\x84" // U+f004
16 | #define ICON_FA_STAR "\xef\x80\x85" // U+f005
17 | #define ICON_FA_STAR_O "\xef\x80\x86" // U+f006
18 | #define ICON_FA_USER "\xef\x80\x87" // U+f007
19 | #define ICON_FA_FILM "\xef\x80\x88" // U+f008
20 | #define ICON_FA_TH_LARGE "\xef\x80\x89" // U+f009
21 | #define ICON_FA_TH "\xef\x80\x8a" // U+f00a
22 | #define ICON_FA_TH_LIST "\xef\x80\x8b" // U+f00b
23 | #define ICON_FA_CHECK "\xef\x80\x8c" // U+f00c
24 | #define ICON_FA_TIMES "\xef\x80\x8d" // U+f00d
25 | #define ICON_FA_SEARCH_PLUS "\xef\x80\x8e" // U+f00e
26 | #define ICON_FA_SEARCH_MINUS "\xef\x80\x90" // U+f010
27 | #define ICON_FA_POWER_OFF "\xef\x80\x91" // U+f011
28 | #define ICON_FA_SIGNAL "\xef\x80\x92" // U+f012
29 | #define ICON_FA_COG "\xef\x80\x93" // U+f013
30 | #define ICON_FA_TRASH_O "\xef\x80\x94" // U+f014
31 | #define ICON_FA_HOME "\xef\x80\x95" // U+f015
32 | #define ICON_FA_FILE_O "\xef\x80\x96" // U+f016
33 | #define ICON_FA_CLOCK_O "\xef\x80\x97" // U+f017
34 | #define ICON_FA_ROAD "\xef\x80\x98" // U+f018
35 | #define ICON_FA_DOWNLOAD "\xef\x80\x99" // U+f019
36 | #define ICON_FA_ARROW_CIRCLE_O_DOWN "\xef\x80\x9a" // U+f01a
37 | #define ICON_FA_ARROW_CIRCLE_O_UP "\xef\x80\x9b" // U+f01b
38 | #define ICON_FA_INBOX "\xef\x80\x9c" // U+f01c
39 | #define ICON_FA_PLAY_CIRCLE_O "\xef\x80\x9d" // U+f01d
40 | #define ICON_FA_REPEAT "\xef\x80\x9e" // U+f01e
41 | #define ICON_FA_REFRESH "\xef\x80\xa1" // U+f021
42 | #define ICON_FA_LIST_ALT "\xef\x80\xa2" // U+f022
43 | #define ICON_FA_LOCK "\xef\x80\xa3" // U+f023
44 | #define ICON_FA_FLAG "\xef\x80\xa4" // U+f024
45 | #define ICON_FA_HEADPHONES "\xef\x80\xa5" // U+f025
46 | #define ICON_FA_VOLUME_OFF "\xef\x80\xa6" // U+f026
47 | #define ICON_FA_VOLUME_DOWN "\xef\x80\xa7" // U+f027
48 | #define ICON_FA_VOLUME_UP "\xef\x80\xa8" // U+f028
49 | #define ICON_FA_QRCODE "\xef\x80\xa9" // U+f029
50 | #define ICON_FA_BARCODE "\xef\x80\xaa" // U+f02a
51 | #define ICON_FA_TAG "\xef\x80\xab" // U+f02b
52 | #define ICON_FA_TAGS "\xef\x80\xac" // U+f02c
53 | #define ICON_FA_BOOK "\xef\x80\xad" // U+f02d
54 | #define ICON_FA_BOOKMARK "\xef\x80\xae" // U+f02e
55 | #define ICON_FA_PRINT "\xef\x80\xaf" // U+f02f
56 | #define ICON_FA_CAMERA "\xef\x80\xb0" // U+f030
57 | #define ICON_FA_FONT "\xef\x80\xb1" // U+f031
58 | #define ICON_FA_BOLD "\xef\x80\xb2" // U+f032
59 | #define ICON_FA_ITALIC "\xef\x80\xb3" // U+f033
60 | #define ICON_FA_TEXT_HEIGHT "\xef\x80\xb4" // U+f034
61 | #define ICON_FA_TEXT_WIDTH "\xef\x80\xb5" // U+f035
62 | #define ICON_FA_ALIGN_LEFT "\xef\x80\xb6" // U+f036
63 | #define ICON_FA_ALIGN_CENTER "\xef\x80\xb7" // U+f037
64 | #define ICON_FA_ALIGN_RIGHT "\xef\x80\xb8" // U+f038
65 | #define ICON_FA_ALIGN_JUSTIFY "\xef\x80\xb9" // U+f039
66 | #define ICON_FA_LIST "\xef\x80\xba" // U+f03a
67 | #define ICON_FA_OUTDENT "\xef\x80\xbb" // U+f03b
68 | #define ICON_FA_INDENT "\xef\x80\xbc" // U+f03c
69 | #define ICON_FA_VIDEO_CAMERA "\xef\x80\xbd" // U+f03d
70 | #define ICON_FA_PICTURE_O "\xef\x80\xbe" // U+f03e
71 | #define ICON_FA_PENCIL "\xef\x81\x80" // U+f040
72 | #define ICON_FA_MAP_MARKER "\xef\x81\x81" // U+f041
73 | #define ICON_FA_ADJUST "\xef\x81\x82" // U+f042
74 | #define ICON_FA_TINT "\xef\x81\x83" // U+f043
75 | #define ICON_FA_PENCIL_SQUARE_O "\xef\x81\x84" // U+f044
76 | #define ICON_FA_SHARE_SQUARE_O "\xef\x81\x85" // U+f045
77 | #define ICON_FA_CHECK_SQUARE_O "\xef\x81\x86" // U+f046
78 | #define ICON_FA_ARROWS "\xef\x81\x87" // U+f047
79 | #define ICON_FA_STEP_BACKWARD "\xef\x81\x88" // U+f048
80 | #define ICON_FA_FAST_BACKWARD "\xef\x81\x89" // U+f049
81 | #define ICON_FA_BACKWARD "\xef\x81\x8a" // U+f04a
82 | #define ICON_FA_PLAY "\xef\x81\x8b" // U+f04b
83 | #define ICON_FA_PAUSE "\xef\x81\x8c" // U+f04c
84 | #define ICON_FA_STOP "\xef\x81\x8d" // U+f04d
85 | #define ICON_FA_FORWARD "\xef\x81\x8e" // U+f04e
86 | #define ICON_FA_FAST_FORWARD "\xef\x81\x90" // U+f050
87 | #define ICON_FA_STEP_FORWARD "\xef\x81\x91" // U+f051
88 | #define ICON_FA_EJECT "\xef\x81\x92" // U+f052
89 | #define ICON_FA_CHEVRON_LEFT "\xef\x81\x93" // U+f053
90 | #define ICON_FA_CHEVRON_RIGHT "\xef\x81\x94" // U+f054
91 | #define ICON_FA_PLUS_CIRCLE "\xef\x81\x95" // U+f055
92 | #define ICON_FA_MINUS_CIRCLE "\xef\x81\x96" // U+f056
93 | #define ICON_FA_TIMES_CIRCLE "\xef\x81\x97" // U+f057
94 | #define ICON_FA_CHECK_CIRCLE "\xef\x81\x98" // U+f058
95 | #define ICON_FA_QUESTION_CIRCLE "\xef\x81\x99" // U+f059
96 | #define ICON_FA_INFO_CIRCLE "\xef\x81\x9a" // U+f05a
97 | #define ICON_FA_CROSSHAIRS "\xef\x81\x9b" // U+f05b
98 | #define ICON_FA_TIMES_CIRCLE_O "\xef\x81\x9c" // U+f05c
99 | #define ICON_FA_CHECK_CIRCLE_O "\xef\x81\x9d" // U+f05d
100 | #define ICON_FA_BAN "\xef\x81\x9e" // U+f05e
101 | #define ICON_FA_ARROW_LEFT "\xef\x81\xa0" // U+f060
102 | #define ICON_FA_ARROW_RIGHT "\xef\x81\xa1" // U+f061
103 | #define ICON_FA_ARROW_UP "\xef\x81\xa2" // U+f062
104 | #define ICON_FA_ARROW_DOWN "\xef\x81\xa3" // U+f063
105 | #define ICON_FA_SHARE "\xef\x81\xa4" // U+f064
106 | #define ICON_FA_EXPAND "\xef\x81\xa5" // U+f065
107 | #define ICON_FA_COMPRESS "\xef\x81\xa6" // U+f066
108 | #define ICON_FA_PLUS "\xef\x81\xa7" // U+f067
109 | #define ICON_FA_MINUS "\xef\x81\xa8" // U+f068
110 | #define ICON_FA_ASTERISK "\xef\x81\xa9" // U+f069
111 | #define ICON_FA_EXCLAMATION_CIRCLE "\xef\x81\xaa" // U+f06a
112 | #define ICON_FA_GIFT "\xef\x81\xab" // U+f06b
113 | #define ICON_FA_LEAF "\xef\x81\xac" // U+f06c
114 | #define ICON_FA_FIRE "\xef\x81\xad" // U+f06d
115 | #define ICON_FA_EYE "\xef\x81\xae" // U+f06e
116 | #define ICON_FA_EYE_SLASH "\xef\x81\xb0" // U+f070
117 | #define ICON_FA_EXCLAMATION_TRIANGLE "\xef\x81\xb1" // U+f071
118 | #define ICON_FA_PLANE "\xef\x81\xb2" // U+f072
119 | #define ICON_FA_CALENDAR "\xef\x81\xb3" // U+f073
120 | #define ICON_FA_RANDOM "\xef\x81\xb4" // U+f074
121 | #define ICON_FA_COMMENT "\xef\x81\xb5" // U+f075
122 | #define ICON_FA_MAGNET "\xef\x81\xb6" // U+f076
123 | #define ICON_FA_CHEVRON_UP "\xef\x81\xb7" // U+f077
124 | #define ICON_FA_CHEVRON_DOWN "\xef\x81\xb8" // U+f078
125 | #define ICON_FA_RETWEET "\xef\x81\xb9" // U+f079
126 | #define ICON_FA_SHOPPING_CART "\xef\x81\xba" // U+f07a
127 | #define ICON_FA_FOLDER "\xef\x81\xbb" // U+f07b
128 | #define ICON_FA_FOLDER_OPEN "\xef\x81\xbc" // U+f07c
129 | #define ICON_FA_ARROWS_V "\xef\x81\xbd" // U+f07d
130 | #define ICON_FA_ARROWS_H "\xef\x81\xbe" // U+f07e
131 | #define ICON_FA_BAR_CHART "\xef\x82\x80" // U+f080
132 | #define ICON_FA_TWITTER_SQUARE "\xef\x82\x81" // U+f081
133 | #define ICON_FA_FACEBOOK_SQUARE "\xef\x82\x82" // U+f082
134 | #define ICON_FA_CAMERA_RETRO "\xef\x82\x83" // U+f083
135 | #define ICON_FA_KEY "\xef\x82\x84" // U+f084
136 | #define ICON_FA_COGS "\xef\x82\x85" // U+f085
137 | #define ICON_FA_COMMENTS "\xef\x82\x86" // U+f086
138 | #define ICON_FA_THUMBS_O_UP "\xef\x82\x87" // U+f087
139 | #define ICON_FA_THUMBS_O_DOWN "\xef\x82\x88" // U+f088
140 | #define ICON_FA_STAR_HALF "\xef\x82\x89" // U+f089
141 | #define ICON_FA_HEART_O "\xef\x82\x8a" // U+f08a
142 | #define ICON_FA_SIGN_OUT "\xef\x82\x8b" // U+f08b
143 | #define ICON_FA_LINKEDIN_SQUARE "\xef\x82\x8c" // U+f08c
144 | #define ICON_FA_THUMB_TACK "\xef\x82\x8d" // U+f08d
145 | #define ICON_FA_EXTERNAL_LINK "\xef\x82\x8e" // U+f08e
146 | #define ICON_FA_SIGN_IN "\xef\x82\x90" // U+f090
147 | #define ICON_FA_TROPHY "\xef\x82\x91" // U+f091
148 | #define ICON_FA_GITHUB_SQUARE "\xef\x82\x92" // U+f092
149 | #define ICON_FA_UPLOAD "\xef\x82\x93" // U+f093
150 | #define ICON_FA_LEMON_O "\xef\x82\x94" // U+f094
151 | #define ICON_FA_PHONE "\xef\x82\x95" // U+f095
152 | #define ICON_FA_SQUARE_O "\xef\x82\x96" // U+f096
153 | #define ICON_FA_BOOKMARK_O "\xef\x82\x97" // U+f097
154 | #define ICON_FA_PHONE_SQUARE "\xef\x82\x98" // U+f098
155 | #define ICON_FA_TWITTER "\xef\x82\x99" // U+f099
156 | #define ICON_FA_FACEBOOK "\xef\x82\x9a" // U+f09a
157 | #define ICON_FA_GITHUB "\xef\x82\x9b" // U+f09b
158 | #define ICON_FA_UNLOCK "\xef\x82\x9c" // U+f09c
159 | #define ICON_FA_CREDIT_CARD "\xef\x82\x9d" // U+f09d
160 | #define ICON_FA_RSS "\xef\x82\x9e" // U+f09e
161 | #define ICON_FA_HDD_O "\xef\x82\xa0" // U+f0a0
162 | #define ICON_FA_BULLHORN "\xef\x82\xa1" // U+f0a1
163 | #define ICON_FA_BELL "\xef\x83\xb3" // U+f0f3
164 | #define ICON_FA_CERTIFICATE "\xef\x82\xa3" // U+f0a3
165 | #define ICON_FA_HAND_O_RIGHT "\xef\x82\xa4" // U+f0a4
166 | #define ICON_FA_HAND_O_LEFT "\xef\x82\xa5" // U+f0a5
167 | #define ICON_FA_HAND_O_UP "\xef\x82\xa6" // U+f0a6
168 | #define ICON_FA_HAND_O_DOWN "\xef\x82\xa7" // U+f0a7
169 | #define ICON_FA_ARROW_CIRCLE_LEFT "\xef\x82\xa8" // U+f0a8
170 | #define ICON_FA_ARROW_CIRCLE_RIGHT "\xef\x82\xa9" // U+f0a9
171 | #define ICON_FA_ARROW_CIRCLE_UP "\xef\x82\xaa" // U+f0aa
172 | #define ICON_FA_ARROW_CIRCLE_DOWN "\xef\x82\xab" // U+f0ab
173 | #define ICON_FA_GLOBE "\xef\x82\xac" // U+f0ac
174 | #define ICON_FA_WRENCH "\xef\x82\xad" // U+f0ad
175 | #define ICON_FA_TASKS "\xef\x82\xae" // U+f0ae
176 | #define ICON_FA_FILTER "\xef\x82\xb0" // U+f0b0
177 | #define ICON_FA_BRIEFCASE "\xef\x82\xb1" // U+f0b1
178 | #define ICON_FA_ARROWS_ALT "\xef\x82\xb2" // U+f0b2
179 | #define ICON_FA_USERS "\xef\x83\x80" // U+f0c0
180 | #define ICON_FA_LINK "\xef\x83\x81" // U+f0c1
181 | #define ICON_FA_CLOUD "\xef\x83\x82" // U+f0c2
182 | #define ICON_FA_FLASK "\xef\x83\x83" // U+f0c3
183 | #define ICON_FA_SCISSORS "\xef\x83\x84" // U+f0c4
184 | #define ICON_FA_FILES_O "\xef\x83\x85" // U+f0c5
185 | #define ICON_FA_PAPERCLIP "\xef\x83\x86" // U+f0c6
186 | #define ICON_FA_FLOPPY_O "\xef\x83\x87" // U+f0c7
187 | #define ICON_FA_SQUARE "\xef\x83\x88" // U+f0c8
188 | #define ICON_FA_BARS "\xef\x83\x89" // U+f0c9
189 | #define ICON_FA_LIST_UL "\xef\x83\x8a" // U+f0ca
190 | #define ICON_FA_LIST_OL "\xef\x83\x8b" // U+f0cb
191 | #define ICON_FA_STRIKETHROUGH "\xef\x83\x8c" // U+f0cc
192 | #define ICON_FA_UNDERLINE "\xef\x83\x8d" // U+f0cd
193 | #define ICON_FA_TABLE "\xef\x83\x8e" // U+f0ce
194 | #define ICON_FA_MAGIC "\xef\x83\x90" // U+f0d0
195 | #define ICON_FA_TRUCK "\xef\x83\x91" // U+f0d1
196 | #define ICON_FA_PINTEREST "\xef\x83\x92" // U+f0d2
197 | #define ICON_FA_PINTEREST_SQUARE "\xef\x83\x93" // U+f0d3
198 | #define ICON_FA_GOOGLE_PLUS_SQUARE "\xef\x83\x94" // U+f0d4
199 | #define ICON_FA_GOOGLE_PLUS "\xef\x83\x95" // U+f0d5
200 | #define ICON_FA_MONEY "\xef\x83\x96" // U+f0d6
201 | #define ICON_FA_CARET_DOWN "\xef\x83\x97" // U+f0d7
202 | #define ICON_FA_CARET_UP "\xef\x83\x98" // U+f0d8
203 | #define ICON_FA_CARET_LEFT "\xef\x83\x99" // U+f0d9
204 | #define ICON_FA_CARET_RIGHT "\xef\x83\x9a" // U+f0da
205 | #define ICON_FA_COLUMNS "\xef\x83\x9b" // U+f0db
206 | #define ICON_FA_SORT "\xef\x83\x9c" // U+f0dc
207 | #define ICON_FA_SORT_DESC "\xef\x83\x9d" // U+f0dd
208 | #define ICON_FA_SORT_ASC "\xef\x83\x9e" // U+f0de
209 | #define ICON_FA_ENVELOPE "\xef\x83\xa0" // U+f0e0
210 | #define ICON_FA_LINKEDIN "\xef\x83\xa1" // U+f0e1
211 | #define ICON_FA_UNDO "\xef\x83\xa2" // U+f0e2
212 | #define ICON_FA_GAVEL "\xef\x83\xa3" // U+f0e3
213 | #define ICON_FA_TACHOMETER "\xef\x83\xa4" // U+f0e4
214 | #define ICON_FA_COMMENT_O "\xef\x83\xa5" // U+f0e5
215 | #define ICON_FA_COMMENTS_O "\xef\x83\xa6" // U+f0e6
216 | #define ICON_FA_BOLT "\xef\x83\xa7" // U+f0e7
217 | #define ICON_FA_SITEMAP "\xef\x83\xa8" // U+f0e8
218 | #define ICON_FA_UMBRELLA "\xef\x83\xa9" // U+f0e9
219 | #define ICON_FA_CLIPBOARD "\xef\x83\xaa" // U+f0ea
220 | #define ICON_FA_LIGHTBULB_O "\xef\x83\xab" // U+f0eb
221 | #define ICON_FA_EXCHANGE "\xef\x83\xac" // U+f0ec
222 | #define ICON_FA_CLOUD_DOWNLOAD "\xef\x83\xad" // U+f0ed
223 | #define ICON_FA_CLOUD_UPLOAD "\xef\x83\xae" // U+f0ee
224 | #define ICON_FA_USER_MD "\xef\x83\xb0" // U+f0f0
225 | #define ICON_FA_STETHOSCOPE "\xef\x83\xb1" // U+f0f1
226 | #define ICON_FA_SUITCASE "\xef\x83\xb2" // U+f0f2
227 | #define ICON_FA_BELL_O "\xef\x82\xa2" // U+f0a2
228 | #define ICON_FA_COFFEE "\xef\x83\xb4" // U+f0f4
229 | #define ICON_FA_CUTLERY "\xef\x83\xb5" // U+f0f5
230 | #define ICON_FA_FILE_TEXT_O "\xef\x83\xb6" // U+f0f6
231 | #define ICON_FA_BUILDING_O "\xef\x83\xb7" // U+f0f7
232 | #define ICON_FA_HOSPITAL_O "\xef\x83\xb8" // U+f0f8
233 | #define ICON_FA_AMBULANCE "\xef\x83\xb9" // U+f0f9
234 | #define ICON_FA_MEDKIT "\xef\x83\xba" // U+f0fa
235 | #define ICON_FA_FIGHTER_JET "\xef\x83\xbb" // U+f0fb
236 | #define ICON_FA_BEER "\xef\x83\xbc" // U+f0fc
237 | #define ICON_FA_H_SQUARE "\xef\x83\xbd" // U+f0fd
238 | #define ICON_FA_PLUS_SQUARE "\xef\x83\xbe" // U+f0fe
239 | #define ICON_FA_ANGLE_DOUBLE_LEFT "\xef\x84\x80" // U+f100
240 | #define ICON_FA_ANGLE_DOUBLE_RIGHT "\xef\x84\x81" // U+f101
241 | #define ICON_FA_ANGLE_DOUBLE_UP "\xef\x84\x82" // U+f102
242 | #define ICON_FA_ANGLE_DOUBLE_DOWN "\xef\x84\x83" // U+f103
243 | #define ICON_FA_ANGLE_LEFT "\xef\x84\x84" // U+f104
244 | #define ICON_FA_ANGLE_RIGHT "\xef\x84\x85" // U+f105
245 | #define ICON_FA_ANGLE_UP "\xef\x84\x86" // U+f106
246 | #define ICON_FA_ANGLE_DOWN "\xef\x84\x87" // U+f107
247 | #define ICON_FA_DESKTOP "\xef\x84\x88" // U+f108
248 | #define ICON_FA_LAPTOP "\xef\x84\x89" // U+f109
249 | #define ICON_FA_TABLET "\xef\x84\x8a" // U+f10a
250 | #define ICON_FA_MOBILE "\xef\x84\x8b" // U+f10b
251 | #define ICON_FA_CIRCLE_O "\xef\x84\x8c" // U+f10c
252 | #define ICON_FA_QUOTE_LEFT "\xef\x84\x8d" // U+f10d
253 | #define ICON_FA_QUOTE_RIGHT "\xef\x84\x8e" // U+f10e
254 | #define ICON_FA_SPINNER "\xef\x84\x90" // U+f110
255 | #define ICON_FA_CIRCLE "\xef\x84\x91" // U+f111
256 | #define ICON_FA_REPLY "\xef\x84\x92" // U+f112
257 | #define ICON_FA_GITHUB_ALT "\xef\x84\x93" // U+f113
258 | #define ICON_FA_FOLDER_O "\xef\x84\x94" // U+f114
259 | #define ICON_FA_FOLDER_OPEN_O "\xef\x84\x95" // U+f115
260 | #define ICON_FA_SMILE_O "\xef\x84\x98" // U+f118
261 | #define ICON_FA_FROWN_O "\xef\x84\x99" // U+f119
262 | #define ICON_FA_MEH_O "\xef\x84\x9a" // U+f11a
263 | #define ICON_FA_GAMEPAD "\xef\x84\x9b" // U+f11b
264 | #define ICON_FA_KEYBOARD_O "\xef\x84\x9c" // U+f11c
265 | #define ICON_FA_FLAG_O "\xef\x84\x9d" // U+f11d
266 | #define ICON_FA_FLAG_CHECKERED "\xef\x84\x9e" // U+f11e
267 | #define ICON_FA_TERMINAL "\xef\x84\xa0" // U+f120
268 | #define ICON_FA_CODE "\xef\x84\xa1" // U+f121
269 | #define ICON_FA_REPLY_ALL "\xef\x84\xa2" // U+f122
270 | #define ICON_FA_STAR_HALF_O "\xef\x84\xa3" // U+f123
271 | #define ICON_FA_LOCATION_ARROW "\xef\x84\xa4" // U+f124
272 | #define ICON_FA_CROP "\xef\x84\xa5" // U+f125
273 | #define ICON_FA_CODE_FORK "\xef\x84\xa6" // U+f126
274 | #define ICON_FA_CHAIN_BROKEN "\xef\x84\xa7" // U+f127
275 | #define ICON_FA_QUESTION "\xef\x84\xa8" // U+f128
276 | #define ICON_FA_INFO "\xef\x84\xa9" // U+f129
277 | #define ICON_FA_EXCLAMATION "\xef\x84\xaa" // U+f12a
278 | #define ICON_FA_SUPERSCRIPT "\xef\x84\xab" // U+f12b
279 | #define ICON_FA_SUBSCRIPT "\xef\x84\xac" // U+f12c
280 | #define ICON_FA_ERASER "\xef\x84\xad" // U+f12d
281 | #define ICON_FA_PUZZLE_PIECE "\xef\x84\xae" // U+f12e
282 | #define ICON_FA_MICROPHONE "\xef\x84\xb0" // U+f130
283 | #define ICON_FA_MICROPHONE_SLASH "\xef\x84\xb1" // U+f131
284 | #define ICON_FA_SHIELD "\xef\x84\xb2" // U+f132
285 | #define ICON_FA_CALENDAR_O "\xef\x84\xb3" // U+f133
286 | #define ICON_FA_FIRE_EXTINGUISHER "\xef\x84\xb4" // U+f134
287 | #define ICON_FA_ROCKET "\xef\x84\xb5" // U+f135
288 | #define ICON_FA_MAXCDN "\xef\x84\xb6" // U+f136
289 | #define ICON_FA_CHEVRON_CIRCLE_LEFT "\xef\x84\xb7" // U+f137
290 | #define ICON_FA_CHEVRON_CIRCLE_RIGHT "\xef\x84\xb8" // U+f138
291 | #define ICON_FA_CHEVRON_CIRCLE_UP "\xef\x84\xb9" // U+f139
292 | #define ICON_FA_CHEVRON_CIRCLE_DOWN "\xef\x84\xba" // U+f13a
293 | #define ICON_FA_HTML5 "\xef\x84\xbb" // U+f13b
294 | #define ICON_FA_CSS3 "\xef\x84\xbc" // U+f13c
295 | #define ICON_FA_ANCHOR "\xef\x84\xbd" // U+f13d
296 | #define ICON_FA_UNLOCK_ALT "\xef\x84\xbe" // U+f13e
297 | #define ICON_FA_BULLSEYE "\xef\x85\x80" // U+f140
298 | #define ICON_FA_ELLIPSIS_H "\xef\x85\x81" // U+f141
299 | #define ICON_FA_ELLIPSIS_V "\xef\x85\x82" // U+f142
300 | #define ICON_FA_RSS_SQUARE "\xef\x85\x83" // U+f143
301 | #define ICON_FA_PLAY_CIRCLE "\xef\x85\x84" // U+f144
302 | #define ICON_FA_TICKET "\xef\x85\x85" // U+f145
303 | #define ICON_FA_MINUS_SQUARE "\xef\x85\x86" // U+f146
304 | #define ICON_FA_MINUS_SQUARE_O "\xef\x85\x87" // U+f147
305 | #define ICON_FA_LEVEL_UP "\xef\x85\x88" // U+f148
306 | #define ICON_FA_LEVEL_DOWN "\xef\x85\x89" // U+f149
307 | #define ICON_FA_CHECK_SQUARE "\xef\x85\x8a" // U+f14a
308 | #define ICON_FA_PENCIL_SQUARE "\xef\x85\x8b" // U+f14b
309 | #define ICON_FA_EXTERNAL_LINK_SQUARE "\xef\x85\x8c" // U+f14c
310 | #define ICON_FA_SHARE_SQUARE "\xef\x85\x8d" // U+f14d
311 | #define ICON_FA_COMPASS "\xef\x85\x8e" // U+f14e
312 | #define ICON_FA_CARET_SQUARE_O_DOWN "\xef\x85\x90" // U+f150
313 | #define ICON_FA_CARET_SQUARE_O_UP "\xef\x85\x91" // U+f151
314 | #define ICON_FA_CARET_SQUARE_O_RIGHT "\xef\x85\x92" // U+f152
315 | #define ICON_FA_EUR "\xef\x85\x93" // U+f153
316 | #define ICON_FA_GBP "\xef\x85\x94" // U+f154
317 | #define ICON_FA_USD "\xef\x85\x95" // U+f155
318 | #define ICON_FA_INR "\xef\x85\x96" // U+f156
319 | #define ICON_FA_JPY "\xef\x85\x97" // U+f157
320 | #define ICON_FA_RUB "\xef\x85\x98" // U+f158
321 | #define ICON_FA_KRW "\xef\x85\x99" // U+f159
322 | #define ICON_FA_BTC "\xef\x85\x9a" // U+f15a
323 | #define ICON_FA_FILE "\xef\x85\x9b" // U+f15b
324 | #define ICON_FA_FILE_TEXT "\xef\x85\x9c" // U+f15c
325 | #define ICON_FA_SORT_ALPHA_ASC "\xef\x85\x9d" // U+f15d
326 | #define ICON_FA_SORT_ALPHA_DESC "\xef\x85\x9e" // U+f15e
327 | #define ICON_FA_SORT_AMOUNT_ASC "\xef\x85\xa0" // U+f160
328 | #define ICON_FA_SORT_AMOUNT_DESC "\xef\x85\xa1" // U+f161
329 | #define ICON_FA_SORT_NUMERIC_ASC "\xef\x85\xa2" // U+f162
330 | #define ICON_FA_SORT_NUMERIC_DESC "\xef\x85\xa3" // U+f163
331 | #define ICON_FA_THUMBS_UP "\xef\x85\xa4" // U+f164
332 | #define ICON_FA_THUMBS_DOWN "\xef\x85\xa5" // U+f165
333 | #define ICON_FA_YOUTUBE_SQUARE "\xef\x85\xa6" // U+f166
334 | #define ICON_FA_YOUTUBE "\xef\x85\xa7" // U+f167
335 | #define ICON_FA_XING "\xef\x85\xa8" // U+f168
336 | #define ICON_FA_XING_SQUARE "\xef\x85\xa9" // U+f169
337 | #define ICON_FA_YOUTUBE_PLAY "\xef\x85\xaa" // U+f16a
338 | #define ICON_FA_DROPBOX "\xef\x85\xab" // U+f16b
339 | #define ICON_FA_STACK_OVERFLOW "\xef\x85\xac" // U+f16c
340 | #define ICON_FA_INSTAGRAM "\xef\x85\xad" // U+f16d
341 | #define ICON_FA_FLICKR "\xef\x85\xae" // U+f16e
342 | #define ICON_FA_ADN "\xef\x85\xb0" // U+f170
343 | #define ICON_FA_BITBUCKET "\xef\x85\xb1" // U+f171
344 | #define ICON_FA_BITBUCKET_SQUARE "\xef\x85\xb2" // U+f172
345 | #define ICON_FA_TUMBLR "\xef\x85\xb3" // U+f173
346 | #define ICON_FA_TUMBLR_SQUARE "\xef\x85\xb4" // U+f174
347 | #define ICON_FA_LONG_ARROW_DOWN "\xef\x85\xb5" // U+f175
348 | #define ICON_FA_LONG_ARROW_UP "\xef\x85\xb6" // U+f176
349 | #define ICON_FA_LONG_ARROW_LEFT "\xef\x85\xb7" // U+f177
350 | #define ICON_FA_LONG_ARROW_RIGHT "\xef\x85\xb8" // U+f178
351 | #define ICON_FA_APPLE "\xef\x85\xb9" // U+f179
352 | #define ICON_FA_WINDOWS "\xef\x85\xba" // U+f17a
353 | #define ICON_FA_ANDROID "\xef\x85\xbb" // U+f17b
354 | #define ICON_FA_LINUX "\xef\x85\xbc" // U+f17c
355 | #define ICON_FA_DRIBBBLE "\xef\x85\xbd" // U+f17d
356 | #define ICON_FA_SKYPE "\xef\x85\xbe" // U+f17e
357 | #define ICON_FA_FOURSQUARE "\xef\x86\x80" // U+f180
358 | #define ICON_FA_TRELLO "\xef\x86\x81" // U+f181
359 | #define ICON_FA_FEMALE "\xef\x86\x82" // U+f182
360 | #define ICON_FA_MALE "\xef\x86\x83" // U+f183
361 | #define ICON_FA_GRATIPAY "\xef\x86\x84" // U+f184
362 | #define ICON_FA_SUN_O "\xef\x86\x85" // U+f185
363 | #define ICON_FA_MOON_O "\xef\x86\x86" // U+f186
364 | #define ICON_FA_ARCHIVE "\xef\x86\x87" // U+f187
365 | #define ICON_FA_BUG "\xef\x86\x88" // U+f188
366 | #define ICON_FA_VK "\xef\x86\x89" // U+f189
367 | #define ICON_FA_WEIBO "\xef\x86\x8a" // U+f18a
368 | #define ICON_FA_RENREN "\xef\x86\x8b" // U+f18b
369 | #define ICON_FA_PAGELINES "\xef\x86\x8c" // U+f18c
370 | #define ICON_FA_STACK_EXCHANGE "\xef\x86\x8d" // U+f18d
371 | #define ICON_FA_ARROW_CIRCLE_O_RIGHT "\xef\x86\x8e" // U+f18e
372 | #define ICON_FA_ARROW_CIRCLE_O_LEFT "\xef\x86\x90" // U+f190
373 | #define ICON_FA_CARET_SQUARE_O_LEFT "\xef\x86\x91" // U+f191
374 | #define ICON_FA_DOT_CIRCLE_O "\xef\x86\x92" // U+f192
375 | #define ICON_FA_WHEELCHAIR "\xef\x86\x93" // U+f193
376 | #define ICON_FA_VIMEO_SQUARE "\xef\x86\x94" // U+f194
377 | #define ICON_FA_TRY "\xef\x86\x95" // U+f195
378 | #define ICON_FA_PLUS_SQUARE_O "\xef\x86\x96" // U+f196
379 | #define ICON_FA_SPACE_SHUTTLE "\xef\x86\x97" // U+f197
380 | #define ICON_FA_SLACK "\xef\x86\x98" // U+f198
381 | #define ICON_FA_ENVELOPE_SQUARE "\xef\x86\x99" // U+f199
382 | #define ICON_FA_WORDPRESS "\xef\x86\x9a" // U+f19a
383 | #define ICON_FA_OPENID "\xef\x86\x9b" // U+f19b
384 | #define ICON_FA_UNIVERSITY "\xef\x86\x9c" // U+f19c
385 | #define ICON_FA_GRADUATION_CAP "\xef\x86\x9d" // U+f19d
386 | #define ICON_FA_YAHOO "\xef\x86\x9e" // U+f19e
387 | #define ICON_FA_GOOGLE "\xef\x86\xa0" // U+f1a0
388 | #define ICON_FA_REDDIT "\xef\x86\xa1" // U+f1a1
389 | #define ICON_FA_REDDIT_SQUARE "\xef\x86\xa2" // U+f1a2
390 | #define ICON_FA_STUMBLEUPON_CIRCLE "\xef\x86\xa3" // U+f1a3
391 | #define ICON_FA_STUMBLEUPON "\xef\x86\xa4" // U+f1a4
392 | #define ICON_FA_DELICIOUS "\xef\x86\xa5" // U+f1a5
393 | #define ICON_FA_DIGG "\xef\x86\xa6" // U+f1a6
394 | #define ICON_FA_PIED_PIPER_PP "\xef\x86\xa7" // U+f1a7
395 | #define ICON_FA_PIED_PIPER_ALT "\xef\x86\xa8" // U+f1a8
396 | #define ICON_FA_DRUPAL "\xef\x86\xa9" // U+f1a9
397 | #define ICON_FA_JOOMLA "\xef\x86\xaa" // U+f1aa
398 | #define ICON_FA_LANGUAGE "\xef\x86\xab" // U+f1ab
399 | #define ICON_FA_FAX "\xef\x86\xac" // U+f1ac
400 | #define ICON_FA_BUILDING "\xef\x86\xad" // U+f1ad
401 | #define ICON_FA_CHILD "\xef\x86\xae" // U+f1ae
402 | #define ICON_FA_PAW "\xef\x86\xb0" // U+f1b0
403 | #define ICON_FA_SPOON "\xef\x86\xb1" // U+f1b1
404 | #define ICON_FA_CUBE "\xef\x86\xb2" // U+f1b2
405 | #define ICON_FA_CUBES "\xef\x86\xb3" // U+f1b3
406 | #define ICON_FA_BEHANCE "\xef\x86\xb4" // U+f1b4
407 | #define ICON_FA_BEHANCE_SQUARE "\xef\x86\xb5" // U+f1b5
408 | #define ICON_FA_STEAM "\xef\x86\xb6" // U+f1b6
409 | #define ICON_FA_STEAM_SQUARE "\xef\x86\xb7" // U+f1b7
410 | #define ICON_FA_RECYCLE "\xef\x86\xb8" // U+f1b8
411 | #define ICON_FA_CAR "\xef\x86\xb9" // U+f1b9
412 | #define ICON_FA_TAXI "\xef\x86\xba" // U+f1ba
413 | #define ICON_FA_TREE "\xef\x86\xbb" // U+f1bb
414 | #define ICON_FA_SPOTIFY "\xef\x86\xbc" // U+f1bc
415 | #define ICON_FA_DEVIANTART "\xef\x86\xbd" // U+f1bd
416 | #define ICON_FA_SOUNDCLOUD "\xef\x86\xbe" // U+f1be
417 | #define ICON_FA_DATABASE "\xef\x87\x80" // U+f1c0
418 | #define ICON_FA_FILE_PDF_O "\xef\x87\x81" // U+f1c1
419 | #define ICON_FA_FILE_WORD_O "\xef\x87\x82" // U+f1c2
420 | #define ICON_FA_FILE_EXCEL_O "\xef\x87\x83" // U+f1c3
421 | #define ICON_FA_FILE_POWERPOINT_O "\xef\x87\x84" // U+f1c4
422 | #define ICON_FA_FILE_IMAGE_O "\xef\x87\x85" // U+f1c5
423 | #define ICON_FA_FILE_ARCHIVE_O "\xef\x87\x86" // U+f1c6
424 | #define ICON_FA_FILE_AUDIO_O "\xef\x87\x87" // U+f1c7
425 | #define ICON_FA_FILE_VIDEO_O "\xef\x87\x88" // U+f1c8
426 | #define ICON_FA_FILE_CODE_O "\xef\x87\x89" // U+f1c9
427 | #define ICON_FA_VINE "\xef\x87\x8a" // U+f1ca
428 | #define ICON_FA_CODEPEN "\xef\x87\x8b" // U+f1cb
429 | #define ICON_FA_JSFIDDLE "\xef\x87\x8c" // U+f1cc
430 | #define ICON_FA_LIFE_RING "\xef\x87\x8d" // U+f1cd
431 | #define ICON_FA_CIRCLE_O_NOTCH "\xef\x87\x8e" // U+f1ce
432 | #define ICON_FA_REBEL "\xef\x87\x90" // U+f1d0
433 | #define ICON_FA_EMPIRE "\xef\x87\x91" // U+f1d1
434 | #define ICON_FA_GIT_SQUARE "\xef\x87\x92" // U+f1d2
435 | #define ICON_FA_GIT "\xef\x87\x93" // U+f1d3
436 | #define ICON_FA_HACKER_NEWS "\xef\x87\x94" // U+f1d4
437 | #define ICON_FA_TENCENT_WEIBO "\xef\x87\x95" // U+f1d5
438 | #define ICON_FA_QQ "\xef\x87\x96" // U+f1d6
439 | #define ICON_FA_WEIXIN "\xef\x87\x97" // U+f1d7
440 | #define ICON_FA_PAPER_PLANE "\xef\x87\x98" // U+f1d8
441 | #define ICON_FA_PAPER_PLANE_O "\xef\x87\x99" // U+f1d9
442 | #define ICON_FA_HISTORY "\xef\x87\x9a" // U+f1da
443 | #define ICON_FA_CIRCLE_THIN "\xef\x87\x9b" // U+f1db
444 | #define ICON_FA_HEADER "\xef\x87\x9c" // U+f1dc
445 | #define ICON_FA_PARAGRAPH "\xef\x87\x9d" // U+f1dd
446 | #define ICON_FA_SLIDERS "\xef\x87\x9e" // U+f1de
447 | #define ICON_FA_SHARE_ALT "\xef\x87\xa0" // U+f1e0
448 | #define ICON_FA_SHARE_ALT_SQUARE "\xef\x87\xa1" // U+f1e1
449 | #define ICON_FA_BOMB "\xef\x87\xa2" // U+f1e2
450 | #define ICON_FA_FUTBOL_O "\xef\x87\xa3" // U+f1e3
451 | #define ICON_FA_TTY "\xef\x87\xa4" // U+f1e4
452 | #define ICON_FA_BINOCULARS "\xef\x87\xa5" // U+f1e5
453 | #define ICON_FA_PLUG "\xef\x87\xa6" // U+f1e6
454 | #define ICON_FA_SLIDESHARE "\xef\x87\xa7" // U+f1e7
455 | #define ICON_FA_TWITCH "\xef\x87\xa8" // U+f1e8
456 | #define ICON_FA_YELP "\xef\x87\xa9" // U+f1e9
457 | #define ICON_FA_NEWSPAPER_O "\xef\x87\xaa" // U+f1ea
458 | #define ICON_FA_WIFI "\xef\x87\xab" // U+f1eb
459 | #define ICON_FA_CALCULATOR "\xef\x87\xac" // U+f1ec
460 | #define ICON_FA_PAYPAL "\xef\x87\xad" // U+f1ed
461 | #define ICON_FA_GOOGLE_WALLET "\xef\x87\xae" // U+f1ee
462 | #define ICON_FA_CC_VISA "\xef\x87\xb0" // U+f1f0
463 | #define ICON_FA_CC_MASTERCARD "\xef\x87\xb1" // U+f1f1
464 | #define ICON_FA_CC_DISCOVER "\xef\x87\xb2" // U+f1f2
465 | #define ICON_FA_CC_AMEX "\xef\x87\xb3" // U+f1f3
466 | #define ICON_FA_CC_PAYPAL "\xef\x87\xb4" // U+f1f4
467 | #define ICON_FA_CC_STRIPE "\xef\x87\xb5" // U+f1f5
468 | #define ICON_FA_BELL_SLASH "\xef\x87\xb6" // U+f1f6
469 | #define ICON_FA_BELL_SLASH_O "\xef\x87\xb7" // U+f1f7
470 | #define ICON_FA_TRASH "\xef\x87\xb8" // U+f1f8
471 | #define ICON_FA_COPYRIGHT "\xef\x87\xb9" // U+f1f9
472 | #define ICON_FA_AT "\xef\x87\xba" // U+f1fa
473 | #define ICON_FA_EYEDROPPER "\xef\x87\xbb" // U+f1fb
474 | #define ICON_FA_PAINT_BRUSH "\xef\x87\xbc" // U+f1fc
475 | #define ICON_FA_BIRTHDAY_CAKE "\xef\x87\xbd" // U+f1fd
476 | #define ICON_FA_AREA_CHART "\xef\x87\xbe" // U+f1fe
477 | #define ICON_FA_PIE_CHART "\xef\x88\x80" // U+f200
478 | #define ICON_FA_LINE_CHART "\xef\x88\x81" // U+f201
479 | #define ICON_FA_LASTFM "\xef\x88\x82" // U+f202
480 | #define ICON_FA_LASTFM_SQUARE "\xef\x88\x83" // U+f203
481 | #define ICON_FA_TOGGLE_OFF "\xef\x88\x84" // U+f204
482 | #define ICON_FA_TOGGLE_ON "\xef\x88\x85" // U+f205
483 | #define ICON_FA_BICYCLE "\xef\x88\x86" // U+f206
484 | #define ICON_FA_BUS "\xef\x88\x87" // U+f207
485 | #define ICON_FA_IOXHOST "\xef\x88\x88" // U+f208
486 | #define ICON_FA_ANGELLIST "\xef\x88\x89" // U+f209
487 | #define ICON_FA_CC "\xef\x88\x8a" // U+f20a
488 | #define ICON_FA_ILS "\xef\x88\x8b" // U+f20b
489 | #define ICON_FA_MEANPATH "\xef\x88\x8c" // U+f20c
490 | #define ICON_FA_BUYSELLADS "\xef\x88\x8d" // U+f20d
491 | #define ICON_FA_CONNECTDEVELOP "\xef\x88\x8e" // U+f20e
492 | #define ICON_FA_DASHCUBE "\xef\x88\x90" // U+f210
493 | #define ICON_FA_FORUMBEE "\xef\x88\x91" // U+f211
494 | #define ICON_FA_LEANPUB "\xef\x88\x92" // U+f212
495 | #define ICON_FA_SELLSY "\xef\x88\x93" // U+f213
496 | #define ICON_FA_SHIRTSINBULK "\xef\x88\x94" // U+f214
497 | #define ICON_FA_SIMPLYBUILT "\xef\x88\x95" // U+f215
498 | #define ICON_FA_SKYATLAS "\xef\x88\x96" // U+f216
499 | #define ICON_FA_CART_PLUS "\xef\x88\x97" // U+f217
500 | #define ICON_FA_CART_ARROW_DOWN "\xef\x88\x98" // U+f218
501 | #define ICON_FA_DIAMOND "\xef\x88\x99" // U+f219
502 | #define ICON_FA_SHIP "\xef\x88\x9a" // U+f21a
503 | #define ICON_FA_USER_SECRET "\xef\x88\x9b" // U+f21b
504 | #define ICON_FA_MOTORCYCLE "\xef\x88\x9c" // U+f21c
505 | #define ICON_FA_STREET_VIEW "\xef\x88\x9d" // U+f21d
506 | #define ICON_FA_HEARTBEAT "\xef\x88\x9e" // U+f21e
507 | #define ICON_FA_VENUS "\xef\x88\xa1" // U+f221
508 | #define ICON_FA_MARS "\xef\x88\xa2" // U+f222
509 | #define ICON_FA_MERCURY "\xef\x88\xa3" // U+f223
510 | #define ICON_FA_TRANSGENDER "\xef\x88\xa4" // U+f224
511 | #define ICON_FA_TRANSGENDER_ALT "\xef\x88\xa5" // U+f225
512 | #define ICON_FA_VENUS_DOUBLE "\xef\x88\xa6" // U+f226
513 | #define ICON_FA_MARS_DOUBLE "\xef\x88\xa7" // U+f227
514 | #define ICON_FA_VENUS_MARS "\xef\x88\xa8" // U+f228
515 | #define ICON_FA_MARS_STROKE "\xef\x88\xa9" // U+f229
516 | #define ICON_FA_MARS_STROKE_V "\xef\x88\xaa" // U+f22a
517 | #define ICON_FA_MARS_STROKE_H "\xef\x88\xab" // U+f22b
518 | #define ICON_FA_NEUTER "\xef\x88\xac" // U+f22c
519 | #define ICON_FA_GENDERLESS "\xef\x88\xad" // U+f22d
520 | #define ICON_FA_FACEBOOK_OFFICIAL "\xef\x88\xb0" // U+f230
521 | #define ICON_FA_PINTEREST_P "\xef\x88\xb1" // U+f231
522 | #define ICON_FA_WHATSAPP "\xef\x88\xb2" // U+f232
523 | #define ICON_FA_SERVER "\xef\x88\xb3" // U+f233
524 | #define ICON_FA_USER_PLUS "\xef\x88\xb4" // U+f234
525 | #define ICON_FA_USER_TIMES "\xef\x88\xb5" // U+f235
526 | #define ICON_FA_BED "\xef\x88\xb6" // U+f236
527 | #define ICON_FA_VIACOIN "\xef\x88\xb7" // U+f237
528 | #define ICON_FA_TRAIN "\xef\x88\xb8" // U+f238
529 | #define ICON_FA_SUBWAY "\xef\x88\xb9" // U+f239
530 | #define ICON_FA_MEDIUM "\xef\x88\xba" // U+f23a
531 | #define ICON_FA_Y_COMBINATOR "\xef\x88\xbb" // U+f23b
532 | #define ICON_FA_OPTIN_MONSTER "\xef\x88\xbc" // U+f23c
533 | #define ICON_FA_OPENCART "\xef\x88\xbd" // U+f23d
534 | #define ICON_FA_EXPEDITEDSSL "\xef\x88\xbe" // U+f23e
535 | #define ICON_FA_BATTERY_FULL "\xef\x89\x80" // U+f240
536 | #define ICON_FA_BATTERY_THREE_QUARTERS "\xef\x89\x81" // U+f241
537 | #define ICON_FA_BATTERY_HALF "\xef\x89\x82" // U+f242
538 | #define ICON_FA_BATTERY_QUARTER "\xef\x89\x83" // U+f243
539 | #define ICON_FA_BATTERY_EMPTY "\xef\x89\x84" // U+f244
540 | #define ICON_FA_MOUSE_POINTER "\xef\x89\x85" // U+f245
541 | #define ICON_FA_I_CURSOR "\xef\x89\x86" // U+f246
542 | #define ICON_FA_OBJECT_GROUP "\xef\x89\x87" // U+f247
543 | #define ICON_FA_OBJECT_UNGROUP "\xef\x89\x88" // U+f248
544 | #define ICON_FA_STICKY_NOTE "\xef\x89\x89" // U+f249
545 | #define ICON_FA_STICKY_NOTE_O "\xef\x89\x8a" // U+f24a
546 | #define ICON_FA_CC_JCB "\xef\x89\x8b" // U+f24b
547 | #define ICON_FA_CC_DINERS_CLUB "\xef\x89\x8c" // U+f24c
548 | #define ICON_FA_CLONE "\xef\x89\x8d" // U+f24d
549 | #define ICON_FA_BALANCE_SCALE "\xef\x89\x8e" // U+f24e
550 | #define ICON_FA_HOURGLASS_O "\xef\x89\x90" // U+f250
551 | #define ICON_FA_HOURGLASS_START "\xef\x89\x91" // U+f251
552 | #define ICON_FA_HOURGLASS_HALF "\xef\x89\x92" // U+f252
553 | #define ICON_FA_HOURGLASS_END "\xef\x89\x93" // U+f253
554 | #define ICON_FA_HOURGLASS "\xef\x89\x94" // U+f254
555 | #define ICON_FA_HAND_ROCK_O "\xef\x89\x95" // U+f255
556 | #define ICON_FA_HAND_PAPER_O "\xef\x89\x96" // U+f256
557 | #define ICON_FA_HAND_SCISSORS_O "\xef\x89\x97" // U+f257
558 | #define ICON_FA_HAND_LIZARD_O "\xef\x89\x98" // U+f258
559 | #define ICON_FA_HAND_SPOCK_O "\xef\x89\x99" // U+f259
560 | #define ICON_FA_HAND_POINTER_O "\xef\x89\x9a" // U+f25a
561 | #define ICON_FA_HAND_PEACE_O "\xef\x89\x9b" // U+f25b
562 | #define ICON_FA_TRADEMARK "\xef\x89\x9c" // U+f25c
563 | #define ICON_FA_REGISTERED "\xef\x89\x9d" // U+f25d
564 | #define ICON_FA_CREATIVE_COMMONS "\xef\x89\x9e" // U+f25e
565 | #define ICON_FA_GG "\xef\x89\xa0" // U+f260
566 | #define ICON_FA_GG_CIRCLE "\xef\x89\xa1" // U+f261
567 | #define ICON_FA_TRIPADVISOR "\xef\x89\xa2" // U+f262
568 | #define ICON_FA_ODNOKLASSNIKI "\xef\x89\xa3" // U+f263
569 | #define ICON_FA_ODNOKLASSNIKI_SQUARE "\xef\x89\xa4" // U+f264
570 | #define ICON_FA_GET_POCKET "\xef\x89\xa5" // U+f265
571 | #define ICON_FA_WIKIPEDIA_W "\xef\x89\xa6" // U+f266
572 | #define ICON_FA_SAFARI "\xef\x89\xa7" // U+f267
573 | #define ICON_FA_CHROME "\xef\x89\xa8" // U+f268
574 | #define ICON_FA_FIREFOX "\xef\x89\xa9" // U+f269
575 | #define ICON_FA_OPERA "\xef\x89\xaa" // U+f26a
576 | #define ICON_FA_INTERNET_EXPLORER "\xef\x89\xab" // U+f26b
577 | #define ICON_FA_TELEVISION "\xef\x89\xac" // U+f26c
578 | #define ICON_FA_CONTAO "\xef\x89\xad" // U+f26d
579 | #define ICON_FA_500PX "\xef\x89\xae" // U+f26e
580 | #define ICON_FA_AMAZON "\xef\x89\xb0" // U+f270
581 | #define ICON_FA_CALENDAR_PLUS_O "\xef\x89\xb1" // U+f271
582 | #define ICON_FA_CALENDAR_MINUS_O "\xef\x89\xb2" // U+f272
583 | #define ICON_FA_CALENDAR_TIMES_O "\xef\x89\xb3" // U+f273
584 | #define ICON_FA_CALENDAR_CHECK_O "\xef\x89\xb4" // U+f274
585 | #define ICON_FA_INDUSTRY "\xef\x89\xb5" // U+f275
586 | #define ICON_FA_MAP_PIN "\xef\x89\xb6" // U+f276
587 | #define ICON_FA_MAP_SIGNS "\xef\x89\xb7" // U+f277
588 | #define ICON_FA_MAP_O "\xef\x89\xb8" // U+f278
589 | #define ICON_FA_MAP "\xef\x89\xb9" // U+f279
590 | #define ICON_FA_COMMENTING "\xef\x89\xba" // U+f27a
591 | #define ICON_FA_COMMENTING_O "\xef\x89\xbb" // U+f27b
592 | #define ICON_FA_HOUZZ "\xef\x89\xbc" // U+f27c
593 | #define ICON_FA_VIMEO "\xef\x89\xbd" // U+f27d
594 | #define ICON_FA_BLACK_TIE "\xef\x89\xbe" // U+f27e
595 | #define ICON_FA_FONTICONS "\xef\x8a\x80" // U+f280
596 | #define ICON_FA_REDDIT_ALIEN "\xef\x8a\x81" // U+f281
597 | #define ICON_FA_EDGE "\xef\x8a\x82" // U+f282
598 | #define ICON_FA_CREDIT_CARD_ALT "\xef\x8a\x83" // U+f283
599 | #define ICON_FA_CODIEPIE "\xef\x8a\x84" // U+f284
600 | #define ICON_FA_MODX "\xef\x8a\x85" // U+f285
601 | #define ICON_FA_FORT_AWESOME "\xef\x8a\x86" // U+f286
602 | #define ICON_FA_USB "\xef\x8a\x87" // U+f287
603 | #define ICON_FA_PRODUCT_HUNT "\xef\x8a\x88" // U+f288
604 | #define ICON_FA_MIXCLOUD "\xef\x8a\x89" // U+f289
605 | #define ICON_FA_SCRIBD "\xef\x8a\x8a" // U+f28a
606 | #define ICON_FA_PAUSE_CIRCLE "\xef\x8a\x8b" // U+f28b
607 | #define ICON_FA_PAUSE_CIRCLE_O "\xef\x8a\x8c" // U+f28c
608 | #define ICON_FA_STOP_CIRCLE "\xef\x8a\x8d" // U+f28d
609 | #define ICON_FA_STOP_CIRCLE_O "\xef\x8a\x8e" // U+f28e
610 | #define ICON_FA_SHOPPING_BAG "\xef\x8a\x90" // U+f290
611 | #define ICON_FA_SHOPPING_BASKET "\xef\x8a\x91" // U+f291
612 | #define ICON_FA_HASHTAG "\xef\x8a\x92" // U+f292
613 | #define ICON_FA_BLUETOOTH "\xef\x8a\x93" // U+f293
614 | #define ICON_FA_BLUETOOTH_B "\xef\x8a\x94" // U+f294
615 | #define ICON_FA_PERCENT "\xef\x8a\x95" // U+f295
616 | #define ICON_FA_GITLAB "\xef\x8a\x96" // U+f296
617 | #define ICON_FA_WPBEGINNER "\xef\x8a\x97" // U+f297
618 | #define ICON_FA_WPFORMS "\xef\x8a\x98" // U+f298
619 | #define ICON_FA_ENVIRA "\xef\x8a\x99" // U+f299
620 | #define ICON_FA_UNIVERSAL_ACCESS "\xef\x8a\x9a" // U+f29a
621 | #define ICON_FA_WHEELCHAIR_ALT "\xef\x8a\x9b" // U+f29b
622 | #define ICON_FA_QUESTION_CIRCLE_O "\xef\x8a\x9c" // U+f29c
623 | #define ICON_FA_BLIND "\xef\x8a\x9d" // U+f29d
624 | #define ICON_FA_AUDIO_DESCRIPTION "\xef\x8a\x9e" // U+f29e
625 | #define ICON_FA_VOLUME_CONTROL_PHONE "\xef\x8a\xa0" // U+f2a0
626 | #define ICON_FA_BRAILLE "\xef\x8a\xa1" // U+f2a1
627 | #define ICON_FA_ASSISTIVE_LISTENING_SYSTEMS "\xef\x8a\xa2" // U+f2a2
628 | #define ICON_FA_AMERICAN_SIGN_LANGUAGE_INTERPRETING "\xef\x8a\xa3" // U+f2a3
629 | #define ICON_FA_DEAF "\xef\x8a\xa4" // U+f2a4
630 | #define ICON_FA_GLIDE "\xef\x8a\xa5" // U+f2a5
631 | #define ICON_FA_GLIDE_G "\xef\x8a\xa6" // U+f2a6
632 | #define ICON_FA_SIGN_LANGUAGE "\xef\x8a\xa7" // U+f2a7
633 | #define ICON_FA_LOW_VISION "\xef\x8a\xa8" // U+f2a8
634 | #define ICON_FA_VIADEO "\xef\x8a\xa9" // U+f2a9
635 | #define ICON_FA_VIADEO_SQUARE "\xef\x8a\xaa" // U+f2aa
636 | #define ICON_FA_SNAPCHAT "\xef\x8a\xab" // U+f2ab
637 | #define ICON_FA_SNAPCHAT_GHOST "\xef\x8a\xac" // U+f2ac
638 | #define ICON_FA_SNAPCHAT_SQUARE "\xef\x8a\xad" // U+f2ad
639 | #define ICON_FA_PIED_PIPER "\xef\x8a\xae" // U+f2ae
640 | #define ICON_FA_FIRST_ORDER "\xef\x8a\xb0" // U+f2b0
641 | #define ICON_FA_YOAST "\xef\x8a\xb1" // U+f2b1
642 | #define ICON_FA_THEMEISLE "\xef\x8a\xb2" // U+f2b2
643 | #define ICON_FA_GOOGLE_PLUS_OFFICIAL "\xef\x8a\xb3" // U+f2b3
644 | #define ICON_FA_FONT_AWESOME "\xef\x8a\xb4" // U+f2b4
645 | #define ICON_FA_HANDSHAKE_O "\xef\x8a\xb5" // U+f2b5
646 | #define ICON_FA_ENVELOPE_OPEN "\xef\x8a\xb6" // U+f2b6
647 | #define ICON_FA_ENVELOPE_OPEN_O "\xef\x8a\xb7" // U+f2b7
648 | #define ICON_FA_LINODE "\xef\x8a\xb8" // U+f2b8
649 | #define ICON_FA_ADDRESS_BOOK "\xef\x8a\xb9" // U+f2b9
650 | #define ICON_FA_ADDRESS_BOOK_O "\xef\x8a\xba" // U+f2ba
651 | #define ICON_FA_ADDRESS_CARD "\xef\x8a\xbb" // U+f2bb
652 | #define ICON_FA_ADDRESS_CARD_O "\xef\x8a\xbc" // U+f2bc
653 | #define ICON_FA_USER_CIRCLE "\xef\x8a\xbd" // U+f2bd
654 | #define ICON_FA_USER_CIRCLE_O "\xef\x8a\xbe" // U+f2be
655 | #define ICON_FA_USER_O "\xef\x8b\x80" // U+f2c0
656 | #define ICON_FA_ID_BADGE "\xef\x8b\x81" // U+f2c1
657 | #define ICON_FA_ID_CARD "\xef\x8b\x82" // U+f2c2
658 | #define ICON_FA_ID_CARD_O "\xef\x8b\x83" // U+f2c3
659 | #define ICON_FA_QUORA "\xef\x8b\x84" // U+f2c4
660 | #define ICON_FA_FREE_CODE_CAMP "\xef\x8b\x85" // U+f2c5
661 | #define ICON_FA_TELEGRAM "\xef\x8b\x86" // U+f2c6
662 | #define ICON_FA_THERMOMETER_FULL "\xef\x8b\x87" // U+f2c7
663 | #define ICON_FA_THERMOMETER_THREE_QUARTERS "\xef\x8b\x88" // U+f2c8
664 | #define ICON_FA_THERMOMETER_HALF "\xef\x8b\x89" // U+f2c9
665 | #define ICON_FA_THERMOMETER_QUARTER "\xef\x8b\x8a" // U+f2ca
666 | #define ICON_FA_THERMOMETER_EMPTY "\xef\x8b\x8b" // U+f2cb
667 | #define ICON_FA_SHOWER "\xef\x8b\x8c" // U+f2cc
668 | #define ICON_FA_BATH "\xef\x8b\x8d" // U+f2cd
669 | #define ICON_FA_PODCAST "\xef\x8b\x8e" // U+f2ce
670 | #define ICON_FA_WINDOW_MAXIMIZE "\xef\x8b\x90" // U+f2d0
671 | #define ICON_FA_WINDOW_MINIMIZE "\xef\x8b\x91" // U+f2d1
672 | #define ICON_FA_WINDOW_RESTORE "\xef\x8b\x92" // U+f2d2
673 | #define ICON_FA_WINDOW_CLOSE "\xef\x8b\x93" // U+f2d3
674 | #define ICON_FA_WINDOW_CLOSE_O "\xef\x8b\x94" // U+f2d4
675 | #define ICON_FA_BANDCAMP "\xef\x8b\x95" // U+f2d5
676 | #define ICON_FA_GRAV "\xef\x8b\x96" // U+f2d6
677 | #define ICON_FA_ETSY "\xef\x8b\x97" // U+f2d7
678 | #define ICON_FA_IMDB "\xef\x8b\x98" // U+f2d8
679 | #define ICON_FA_RAVELRY "\xef\x8b\x99" // U+f2d9
680 | #define ICON_FA_EERCAST "\xef\x8b\x9a" // U+f2da
681 | #define ICON_FA_MICROCHIP "\xef\x8b\x9b" // U+f2db
682 | #define ICON_FA_SNOWFLAKE_O "\xef\x8b\x9c" // U+f2dc
683 | #define ICON_FA_SUPERPOWERS "\xef\x8b\x9d" // U+f2dd
684 | #define ICON_FA_WPEXPLORER "\xef\x8b\x9e" // U+f2de
685 | #define ICON_FA_MEETUP "\xef\x8b\xa0" // U+f2e0
686 |
--------------------------------------------------------------------------------