1 | //************************************ bs::framework - Copyright 2018 Marko Pintera **************************************// |
2 | //*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********// |
3 | #pragma once |
4 | |
5 | #include "BsCorePrerequisites.h" |
6 | #include "Utility/BsModule.h" |
7 | #include "Profiling/BsRenderStats.h" |
8 | #include "Allocators/BsPoolAlloc.h" |
9 | |
10 | namespace bs |
11 | { |
12 | /** @addtogroup Profiling |
13 | * @{ |
14 | */ |
15 | |
16 | /** Contains various profiler statistics about a single GPU profiling sample. */ |
17 | struct GPUProfileSample |
18 | { |
19 | String name; /**< Name of the sample for easier identification. */ |
20 | float timeMs; /**< Time in milliseconds it took to execute the sampled block. */ |
21 | |
22 | UINT32 numDrawCalls; /**< Number of draw calls that happened. */ |
23 | UINT32 numRenderTargetChanges; /**< How many times was render target changed. */ |
24 | UINT32 numPresents; /**< How many times did a buffer swap happen on a double buffered render target. */ |
25 | UINT32 numClears; /**< How many times was render target cleared. */ |
26 | |
27 | UINT32 numVertices; /**< Total number of vertices sent to the GPU. */ |
28 | UINT32 numPrimitives; /**< Total number of primitives sent to the GPU. */ |
29 | UINT32 numDrawnSamples; /**< Number of samples drawn by the GPU. */ |
30 | |
31 | UINT32 numPipelineStateChanges; /**< How many times did the pipeline state change. */ |
32 | |
33 | UINT32 numGpuParamBinds; /**< How many times were GPU parameters bound. */ |
34 | UINT32 numVertexBufferBinds; /**< How many times was a vertex buffer bound. */ |
35 | UINT32 numIndexBufferBinds; /**< How many times was an index buffer bound. */ |
36 | |
37 | UINT32 numResourceWrites; /**< How many times were GPU resources written to. */ |
38 | UINT32 numResourceReads; /**< How many times were GPU resources read from. */ |
39 | |
40 | UINT32 numObjectsCreated; /**< How many GPU objects were created. */ |
41 | UINT32 numObjectsDestroyed; /**< How many GPU objects were destroyed. */ |
42 | |
43 | Vector<GPUProfileSample> children; |
44 | }; |
45 | |
46 | /** Profiler report containing information about GPU sampling data from a single frame. */ |
47 | struct GPUProfilerReport |
48 | { |
49 | GPUProfileSample frameSample; /**< Sample containing data for entire frame. */ |
50 | }; |
51 | |
52 | /** |
53 | * Profiler that measures time and amount of various GPU operations. |
54 | * |
55 | * @note Core thread only except where noted otherwise. |
56 | */ |
57 | class BS_CORE_EXPORT ProfilerGPU : public Module<ProfilerGPU> |
58 | { |
59 | private: |
60 | struct ProfiledSample |
61 | { |
62 | ProfilerString name; |
63 | RenderStatsData startStats; |
64 | RenderStatsData endStats; |
65 | SPtr<ct::TimerQuery> activeTimeQuery; |
66 | SPtr<ct::OcclusionQuery> activeOcclusionQuery; |
67 | |
68 | Vector<ProfiledSample*> children; |
69 | }; |
70 | |
71 | public: |
72 | ProfilerGPU(); |
73 | ~ProfilerGPU(); |
74 | |
75 | /** |
76 | * Signals a start of a new frame. Every frame will generate a separate profiling report. This call must be followed |
77 | * by endFrame(), and any sampling operations must happen between beginFrame() and endFrame(). |
78 | */ |
79 | void beginFrame(); |
80 | |
81 | /** |
82 | * Signals an end of the currently sampled frame. Results of the sampling will be available once |
83 | * getNumAvailableReports increments. This may take a while as the sampling is scheduled on the core thread and |
84 | * on the GPU. |
85 | * |
86 | * @param[in] discard If true, the results of the frame will not be resolved and it will be discarded. |
87 | */ |
88 | void endFrame(bool discard = false); |
89 | |
90 | /** |
91 | * Begins sample measurement. Must be followed by endSample(). |
92 | * |
93 | * @param[in] name Unique name for the sample you can later use to find the sampling data. |
94 | * |
95 | * @note Must be called between beginFrame()/endFrame() calls. |
96 | */ |
97 | void beginSample(ProfilerString name); |
98 | |
99 | /** |
100 | * Ends sample measurement. |
101 | * |
102 | * @param[in] name Unique name for the sample. |
103 | * |
104 | * @note |
105 | * Unique name is primarily needed to more easily identify mismatched begin/end sample pairs. Otherwise the name in |
106 | * beginSample() would be enough. Must be called between beginFrame()/endFrame() calls. |
107 | */ |
108 | void endSample(const ProfilerString& name); |
109 | |
110 | /** |
111 | * Returns number of profiling reports that are ready but haven't been retrieved yet. |
112 | * |
113 | * @note |
114 | * There is an internal limit of maximum number of available reports, where oldest ones will get deleted so make |
115 | * sure to call this often if you don't want to miss some. |
116 | * @note |
117 | * Thread safe. |
118 | */ |
119 | UINT32 getNumAvailableReports(); |
120 | |
121 | /** |
122 | * Gets the oldest report available and removes it from the internal list. Throws an exception if no reports are |
123 | * available. |
124 | * |
125 | * @note Thread safe. |
126 | */ |
127 | GPUProfilerReport getNextReport(); |
128 | |
129 | public: |
130 | // ***** INTERNAL ****** |
131 | /** @name Internal |
132 | * @{ |
133 | */ |
134 | |
135 | /** |
136 | * To be called once per frame from the Core thread. |
137 | */ |
138 | void _update(); |
139 | |
140 | /** @} */ |
141 | |
142 | private: |
143 | /** Assigns start values for the provided sample. */ |
144 | void beginSampleInternal(ProfiledSample& sample, bool issueOcclusion); |
145 | |
146 | /** Assigns end values for the provided sample. */ |
147 | void endSampleInternal(ProfiledSample& sample); |
148 | |
149 | /** Creates a new timer query or returns an existing free query. */ |
150 | SPtr<ct::TimerQuery> getTimerQuery() const; |
151 | |
152 | /** Creates a new occlusion query or returns an existing free query. */ |
153 | SPtr<ct::OcclusionQuery> getOcclusionQuery() const; |
154 | |
155 | /** Frees the memory used by all the child samples. */ |
156 | void freeSample(ProfiledSample& sample); |
157 | |
158 | /** Resolves an active sample and converts it to report sample. */ |
159 | void resolveSample(const ProfiledSample& sample, GPUProfileSample& reportSample); |
160 | |
161 | private: |
162 | ProfiledSample mFrameSample; |
163 | bool mIsFrameActive = false; |
164 | Stack<ProfiledSample*> mActiveSamples; |
165 | |
166 | Queue<ProfiledSample> mUnresolvedFrames; |
167 | GPUProfilerReport* mReadyReports = nullptr; |
168 | |
169 | static const UINT32 MAX_QUEUE_ELEMENTS; |
170 | UINT32 mReportHeadPos = 0; |
171 | UINT32 mReportCount = 0; |
172 | |
173 | PoolAlloc<sizeof(ProfiledSample), 256> mSamplePool; |
174 | |
175 | mutable Stack<SPtr<ct::TimerQuery>> mFreeTimerQueries; |
176 | mutable Stack<SPtr<ct::OcclusionQuery>> mFreeOcclusionQueries; |
177 | |
178 | Mutex mMutex; |
179 | }; |
180 | |
181 | /** Provides global access to ProfilerGPU instance. */ |
182 | BS_CORE_EXPORT ProfilerGPU& gProfilerGPU(); |
183 | |
184 | /** Profiling macros that allow profiling functionality to be disabled at compile time. */ |
185 | #if BS_PROFILING_ENABLED |
186 | #define BS_GPU_PROFILE_BEGIN(name) gProfilerGPU().beginSample(name); |
187 | #define BS_GPU_PROFILE_END(name) gProfilerGPU().endSample(name); |
188 | #else |
189 | #define BS_GPU_PROFILE_BEGIN(name) |
190 | #define BS_GPU_PROFILE_END(name) |
191 | #endif |
192 | |
193 | /** |
194 | * Helper class that performs GPU profiling in the current block. Profiling sample is started when the class is |
195 | * constructed and ended upon destruction. |
196 | */ |
197 | struct ProfileGPUBlock |
198 | { |
199 | #if BS_PROFILING_ENABLED |
200 | ProfileGPUBlock(ProfilerString name) |
201 | { |
202 | mSampleName = std::move(name); |
203 | gProfilerGPU().beginSample(mSampleName); |
204 | } |
205 | #else |
206 | ProfileGPUBlock(const ProfilerString& name) |
207 | { } |
208 | #endif |
209 | |
210 | #if BS_PROFILING_ENABLED |
211 | ~ProfileGPUBlock() |
212 | { |
213 | gProfilerGPU().endSample(mSampleName); |
214 | } |
215 | #endif |
216 | |
217 | private: |
218 | #if BS_PROFILING_ENABLED |
219 | ProfilerString mSampleName; |
220 | #endif |
221 | }; |
222 | |
223 | /** @} */ |
224 | } |