ClangFormat: apply to source, most of intern
[blender.git] / intern / cycles / util / util_profiling.cpp
1 /*
2  * Copyright 2011-2018 Blender Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include "util/util_algorithm.h"
18 #include "util/util_profiling.h"
19 #include "util/util_set.h"
20
21 CCL_NAMESPACE_BEGIN
22
23 Profiler::Profiler() : do_stop_worker(true), worker(NULL)
24 {
25 }
26
27 Profiler::~Profiler()
28 {
29   assert(worker == NULL);
30 }
31
32 void Profiler::run()
33 {
34   uint64_t updates = 0;
35   auto start_time = std::chrono::system_clock::now();
36   while (!do_stop_worker) {
37     thread_scoped_lock lock(mutex);
38     foreach (ProfilingState *state, states) {
39       uint32_t cur_event = state->event;
40       int32_t cur_shader = state->shader;
41       int32_t cur_object = state->object;
42
43       /* The state reads/writes should be atomic, but just to be sure
44        * check the values for validity anyways. */
45       if (cur_event < PROFILING_NUM_EVENTS) {
46         event_samples[cur_event]++;
47       }
48
49       if (cur_shader >= 0 && cur_shader < shader_samples.size()) {
50         /* Only consider the active shader during events whose runtime significantly depends on it. */
51         if (((cur_event >= PROFILING_SHADER_EVAL) && (cur_event <= PROFILING_SUBSURFACE)) ||
52             ((cur_event >= PROFILING_CLOSURE_EVAL) &&
53              (cur_event <= PROFILING_CLOSURE_VOLUME_SAMPLE))) {
54           shader_samples[cur_shader]++;
55         }
56       }
57
58       if (cur_object >= 0 && cur_object < object_samples.size()) {
59         object_samples[cur_object]++;
60       }
61     }
62     lock.unlock();
63
64     /* Relative waits always overshoot a bit, so just waiting 1ms every
65      * time would cause the sampling to drift over time.
66      * By keeping track of the absolute time, the wait times correct themselves -
67      * if one wait overshoots a lot, the next one will be shorter to compensate. */
68     updates++;
69     std::this_thread::sleep_until(start_time + updates * std::chrono::milliseconds(1));
70   }
71 }
72
73 void Profiler::reset(int num_shaders, int num_objects)
74 {
75   bool running = (worker != NULL);
76   if (running) {
77     stop();
78   }
79
80   /* Resize and clear the accumulation vectors. */
81   shader_hits.assign(num_shaders, 0);
82   object_hits.assign(num_objects, 0);
83
84   event_samples.assign(PROFILING_NUM_EVENTS, 0);
85   shader_samples.assign(num_shaders, 0);
86   object_samples.assign(num_objects, 0);
87
88   if (running) {
89     start();
90   }
91 }
92
93 void Profiler::start()
94 {
95   assert(worker == NULL);
96   do_stop_worker = false;
97   worker = new thread(function_bind(&Profiler::run, this));
98 }
99
100 void Profiler::stop()
101 {
102   if (worker != NULL) {
103     do_stop_worker = true;
104
105     worker->join();
106     delete worker;
107     worker = NULL;
108   }
109 }
110
111 void Profiler::add_state(ProfilingState *state)
112 {
113   thread_scoped_lock lock(mutex);
114
115   /* Add the ProfilingState from the list of sampled states. */
116   assert(std::find(states.begin(), states.end(), state) == states.end());
117   states.push_back(state);
118
119   /* Resize thread-local hit counters. */
120   state->shader_hits.assign(shader_hits.size(), 0);
121   state->object_hits.assign(object_hits.size(), 0);
122
123   /* Initialize the state. */
124   state->event = PROFILING_UNKNOWN;
125   state->shader = -1;
126   state->object = -1;
127   state->active = true;
128 }
129
130 void Profiler::remove_state(ProfilingState *state)
131 {
132   thread_scoped_lock lock(mutex);
133
134   /* Remove the ProfilingState from the list of sampled states. */
135   states.erase(std::remove(states.begin(), states.end(), state), states.end());
136   state->active = false;
137
138   /* Merge thread-local hit counters. */
139   assert(shader_hits.size() == state->shader_hits.size());
140   for (int i = 0; i < shader_hits.size(); i++) {
141     shader_hits[i] += state->shader_hits[i];
142   }
143
144   assert(object_hits.size() == state->object_hits.size());
145   for (int i = 0; i < object_hits.size(); i++) {
146     object_hits[i] += state->object_hits[i];
147   }
148 }
149
150 uint64_t Profiler::get_event(ProfilingEvent event)
151 {
152   assert(worker == NULL);
153   return event_samples[event];
154 }
155
156 bool Profiler::get_shader(int shader, uint64_t &samples, uint64_t &hits)
157 {
158   assert(worker == NULL);
159   if (shader_samples[shader] == 0) {
160     return false;
161   }
162   samples = shader_samples[shader];
163   hits = shader_hits[shader];
164   return true;
165 }
166
167 bool Profiler::get_object(int object, uint64_t &samples, uint64_t &hits)
168 {
169   assert(worker == NULL);
170   if (object_samples[object] == 0) {
171     return false;
172   }
173   samples = object_samples[object];
174   hits = object_hits[object];
175   return true;
176 }
177
178 CCL_NAMESPACE_END