Merge branch 'master' into blender2.8
[blender.git] / intern / cycles / render / stats.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 "render/stats.h"
18 #include "render/object.h"
19 #include "util/util_algorithm.h"
20 #include "util/util_foreach.h"
21 #include "util/util_string.h"
22
23 CCL_NAMESPACE_BEGIN
24
25 static int kIndentNumSpaces = 2;
26
27 /* Named size entry. */
28
29 namespace {
30
31 bool namedSizeEntryComparator(const NamedSizeEntry& a, const NamedSizeEntry& b)
32 {
33         /* We sort in descending order. */
34         return a.size > b.size;
35 }
36
37 bool namedTimeSampleEntryComparator(const NamedNestedSampleStats& a, const NamedNestedSampleStats& b)
38 {
39         return a.sum_samples > b.sum_samples;
40 }
41
42 bool namedSampleCountPairComparator(const NamedSampleCountPair& a, const NamedSampleCountPair& b)
43 {
44         return a.samples > b.samples;
45 }
46
47 }  // namespace
48
49 NamedSizeEntry::NamedSizeEntry()
50     : name(""),
51       size(0) {
52 }
53
54 NamedSizeEntry::NamedSizeEntry(const string& name, size_t size)
55     : name(name),
56       size(size) {
57 }
58
59 /* Named size statistics. */
60
61 NamedSizeStats::NamedSizeStats()
62     : total_size(0) {
63 }
64
65 void NamedSizeStats::add_entry(const NamedSizeEntry& entry) {
66         total_size += entry.size;
67         entries.push_back(entry);
68 }
69
70 string NamedSizeStats::full_report(int indent_level)
71 {
72         const string indent(indent_level * kIndentNumSpaces, ' ');
73         const string double_indent = indent + indent;
74         string result = "";
75         result += string_printf("%sTotal memory: %s (%s)\n",
76                                 indent.c_str(),
77                                 string_human_readable_size(total_size).c_str(),
78                                 string_human_readable_number(total_size).c_str());
79         sort(entries.begin(), entries.end(), namedSizeEntryComparator);
80         foreach(const NamedSizeEntry& entry, entries) {
81                 result += string_printf(
82                         "%s%-32s %s (%s)\n",
83                         double_indent.c_str(),
84                         entry.name.c_str(),
85                         string_human_readable_size(entry.size).c_str(),
86                         string_human_readable_number(entry.size).c_str());
87         }
88         return result;
89 }
90
91 /* Named time sample statistics. */
92
93 NamedNestedSampleStats::NamedNestedSampleStats()
94  : name(""), self_samples(0), sum_samples(0)
95 {}
96
97 NamedNestedSampleStats::NamedNestedSampleStats(const string& name, uint64_t samples)
98  : name(name), self_samples(samples), sum_samples(samples)
99 {}
100
101 NamedNestedSampleStats& NamedNestedSampleStats::add_entry(const string& name_, uint64_t samples_)
102 {
103         entries.push_back(NamedNestedSampleStats(name_, samples_));
104         return entries[entries.size()-1];
105 }
106
107 void NamedNestedSampleStats::update_sum()
108 {
109         sum_samples = self_samples;
110         foreach(NamedNestedSampleStats& entry, entries) {
111                 entry.update_sum();
112                 sum_samples += entry.sum_samples;
113         }
114 }
115
116 string NamedNestedSampleStats::full_report(int indent_level, uint64_t total_samples)
117 {
118         update_sum();
119
120         if(total_samples == 0) {
121                 total_samples = sum_samples;
122         }
123
124         const string indent(indent_level * kIndentNumSpaces, ' ');
125
126         const double sum_percent = 100*((double) sum_samples) / total_samples;
127         const double sum_seconds = sum_samples * 0.001;
128         const double self_percent = 100*((double) self_samples) / total_samples;
129         const double self_seconds = self_samples * 0.001;
130         string info = string_printf("%-32s: Total %3.2f%% (%.2fs), Self %3.2f%% (%.2fs)\n",
131                                     name.c_str(),
132                                     sum_percent,
133                                     sum_seconds,
134                                     self_percent,
135                                     self_seconds);
136         string result = indent + info;
137
138         sort(entries.begin(), entries.end(), namedTimeSampleEntryComparator);
139         foreach(NamedNestedSampleStats& entry, entries) {
140                 result += entry.full_report(indent_level + 1, total_samples);
141         }
142         return result;
143 }
144
145 /* Named sample count pairs. */
146
147 NamedSampleCountPair::NamedSampleCountPair(const ustring& name, uint64_t samples, uint64_t hits)
148  : name(name), samples(samples), hits(hits)
149 {}
150
151 NamedSampleCountStats::NamedSampleCountStats()
152 {}
153
154 void NamedSampleCountStats::add(const ustring& name, uint64_t samples, uint64_t hits)
155 {
156         entry_map::iterator entry = entries.find(name);
157         if(entry != entries.end()) {
158                 entry->second.samples += samples;
159                 entry->second.hits += hits;
160                 return;
161         }
162         entries.emplace(name, NamedSampleCountPair(name, samples, hits));
163 }
164
165 string NamedSampleCountStats::full_report(int indent_level)
166 {
167         const string indent(indent_level * kIndentNumSpaces, ' ');
168
169         vector<NamedSampleCountPair> sorted_entries;
170         sorted_entries.reserve(entries.size());
171
172         uint64_t total_hits = 0, total_samples = 0;
173         foreach(entry_map::const_reference entry, entries) {
174                 const NamedSampleCountPair &pair = entry.second;
175
176                 total_hits += pair.hits;
177                 total_samples += pair.samples;
178
179                 sorted_entries.push_back(pair);
180         }
181         const double avg_samples_per_hit = ((double) total_samples) / total_hits;
182
183         sort(sorted_entries.begin(), sorted_entries.end(), namedSampleCountPairComparator);
184
185         string result = "";
186         foreach(const NamedSampleCountPair& entry, sorted_entries) {
187                 const double seconds = entry.samples * 0.001;
188                 const double relative = ((double) entry.samples) / (entry.hits * avg_samples_per_hit);
189
190                 result += indent + string_printf("%-32s: %.2fs (Relative cost: %.2f)\n",
191                                                  entry.name.c_str(),
192                                                  seconds,
193                                                  relative);
194         }
195         return result;
196 }
197
198 /* Mesh statistics. */
199
200 MeshStats::MeshStats() {
201 }
202
203 string MeshStats::full_report(int indent_level)
204 {
205         const string indent(indent_level * kIndentNumSpaces, ' ');
206         string result = "";
207         result += indent + "Geometry:\n" + geometry.full_report(indent_level + 1);
208         return result;
209 }
210
211 /* Image statistics. */
212
213 ImageStats::ImageStats() {
214 }
215
216 string ImageStats::full_report(int indent_level)
217 {
218         const string indent(indent_level * kIndentNumSpaces, ' ');
219         string result = "";
220         result += indent + "Textures:\n" + textures.full_report(indent_level + 1);
221         return result;
222 }
223
224 /* Overall statistics. */
225
226 RenderStats::RenderStats() {
227         has_profiling = false;
228 }
229
230 void RenderStats::collect_profiling(Scene *scene, Profiler& prof)
231 {
232         has_profiling = true;
233
234         kernel = NamedNestedSampleStats("Total render time", prof.get_event(PROFILING_UNKNOWN));
235
236         kernel.add_entry("Ray setup", prof.get_event(PROFILING_RAY_SETUP));
237         kernel.add_entry("Result writing", prof.get_event(PROFILING_WRITE_RESULT));
238
239         NamedNestedSampleStats &integrator = kernel.add_entry("Path integration", prof.get_event(PROFILING_PATH_INTEGRATE));
240         integrator.add_entry("Scene intersection", prof.get_event(PROFILING_SCENE_INTERSECT));
241         integrator.add_entry("Indirect emission", prof.get_event(PROFILING_INDIRECT_EMISSION));
242         integrator.add_entry("Volumes", prof.get_event(PROFILING_VOLUME));
243
244         NamedNestedSampleStats &shading = integrator.add_entry("Shading", 0);
245         shading.add_entry("Shader Setup", prof.get_event(PROFILING_SHADER_SETUP));
246         shading.add_entry("Shader Eval", prof.get_event(PROFILING_SHADER_EVAL));
247         shading.add_entry("Shader Apply", prof.get_event(PROFILING_SHADER_APPLY));
248         shading.add_entry("Ambient Occlusion", prof.get_event(PROFILING_AO));
249         shading.add_entry("Subsurface", prof.get_event(PROFILING_SUBSURFACE));
250
251         integrator.add_entry("Connect Light", prof.get_event(PROFILING_CONNECT_LIGHT));
252         integrator.add_entry("Surface Bounce", prof.get_event(PROFILING_SURFACE_BOUNCE));
253
254         NamedNestedSampleStats &intersection = kernel.add_entry("Intersection", 0);
255         intersection.add_entry("Full Intersection", prof.get_event(PROFILING_INTERSECT));
256         intersection.add_entry("Local Intersection", prof.get_event(PROFILING_INTERSECT_LOCAL));
257         intersection.add_entry("Shadow All Intersection", prof.get_event(PROFILING_INTERSECT_SHADOW_ALL));
258         intersection.add_entry("Volume Intersection", prof.get_event(PROFILING_INTERSECT_VOLUME));
259         intersection.add_entry("Volume All Intersection", prof.get_event(PROFILING_INTERSECT_VOLUME_ALL));
260
261         NamedNestedSampleStats &closure = kernel.add_entry("Closures", 0);
262         closure.add_entry("Surface Closure Evaluation", prof.get_event(PROFILING_CLOSURE_EVAL));
263         closure.add_entry("Surface Closure Sampling", prof.get_event(PROFILING_CLOSURE_SAMPLE));
264         closure.add_entry("Volume Closure Evaluation", prof.get_event(PROFILING_CLOSURE_VOLUME_EVAL));
265         closure.add_entry("Volume Closure Sampling", prof.get_event(PROFILING_CLOSURE_VOLUME_SAMPLE));
266
267         NamedNestedSampleStats &denoising = kernel.add_entry("Denoising", prof.get_event(PROFILING_DENOISING));
268         denoising.add_entry("Construct Transform", prof.get_event(PROFILING_DENOISING_CONSTRUCT_TRANSFORM));
269         denoising.add_entry("Reconstruct", prof.get_event(PROFILING_DENOISING_RECONSTRUCT));
270
271         NamedNestedSampleStats &prefilter = denoising.add_entry("Prefiltering", 0);
272         prefilter.add_entry("Divide Shadow", prof.get_event(PROFILING_DENOISING_DIVIDE_SHADOW));
273         prefilter.add_entry("Non-Local means", prof.get_event(PROFILING_DENOISING_NON_LOCAL_MEANS));
274         prefilter.add_entry("Get Feature", prof.get_event(PROFILING_DENOISING_GET_FEATURE));
275         prefilter.add_entry("Detect Outliers", prof.get_event(PROFILING_DENOISING_DETECT_OUTLIERS));
276         prefilter.add_entry("Combine Halves", prof.get_event(PROFILING_DENOISING_COMBINE_HALVES));
277
278         shaders.entries.clear();
279         foreach(Shader *shader, scene->shaders) {
280                 uint64_t samples, hits;
281                 if(prof.get_shader(shader->id, samples, hits)) {
282                         shaders.add(shader->name, samples, hits);
283                 }
284         }
285
286         objects.entries.clear();
287         foreach(Object *object, scene->objects) {
288                 uint64_t samples, hits;
289                 if(prof.get_object(object->get_device_index(), samples, hits)) {
290                         objects.add(object->name, samples, hits);
291                 }
292         }
293 }
294
295 string RenderStats::full_report()
296 {
297         string result = "";
298         result += "Mesh statistics:\n" + mesh.full_report(1);
299         result += "Image statistics:\n" + image.full_report(1);
300         if(has_profiling) {
301                 result += "Kernel statistics:\n" + kernel.full_report(1);
302                 result += "Shader statistics:\n" + shaders.full_report(1);
303                 result += "Object statistics:\n" + objects.full_report(1);
304         }
305         else {
306                 result += "Profiling information not available (only works with CPU rendering)";
307         }
308         return result;
309 }
310
311 CCL_NAMESPACE_END