Cycles: Initial implementation of detailed statistics

Gathers information about object geometry and textures. Very basic at
this moment, but need to start somewhere.

Things which needs to be included still:

- "Runtime" information, like BVH. While it is not directly controllable
  by artists, it's still important to know.

- Device array sizes. Again, not under artists control, but is added to
  the overall size.

- Memory peak at different synchronization stages.

At this point it simply prints info to the stdout after F12 is done,
need better control over that too.

Reviewers: brecht

Differential Revision: https://developer.blender.org/D3566
This commit is contained in:
Sergey Sharybin
2018-07-27 15:46:13 +02:00
parent 709b36e43b
commit 84d47e3685
13 changed files with 287 additions and 0 deletions

View File

@@ -67,6 +67,9 @@ def _configure_argument_parser():
parser.add_argument("--cycles-resumable-end-chunk", parser.add_argument("--cycles-resumable-end-chunk",
help="End chunk to render", help="End chunk to render",
default=None) default=None)
parser.add_argument("--cycles-print-stats",
help="Print rendering statistics to stderr",
action='store_true')
return parser return parser
@@ -95,6 +98,9 @@ def _parse_command_line():
int(args.cycles_resumable_start_chunk), int(args.cycles_resumable_start_chunk),
int(args.cycles_resumable_end_chunk), int(args.cycles_resumable_end_chunk),
) )
if args.cycles_print_stats:
import _cycles
_cycles.enable_print_stats()
def init(): def init():

View File

@@ -734,6 +734,12 @@ static PyObject *set_resumable_chunk_range_func(PyObject * /*self*/, PyObject *a
Py_RETURN_NONE; Py_RETURN_NONE;
} }
static PyObject *enable_print_stats_func(PyObject * /*self*/, PyObject * /*args*/)
{
BlenderSession::print_render_stats = true;
Py_RETURN_NONE;
}
static PyObject *get_device_types_func(PyObject * /*self*/, PyObject * /*args*/) static PyObject *get_device_types_func(PyObject * /*self*/, PyObject * /*args*/)
{ {
vector<DeviceInfo>& devices = Device::available_devices(); vector<DeviceInfo>& devices = Device::available_devices();
@@ -772,6 +778,9 @@ static PyMethodDef methods[] = {
{"debug_flags_update", debug_flags_update_func, METH_VARARGS, ""}, {"debug_flags_update", debug_flags_update_func, METH_VARARGS, ""},
{"debug_flags_reset", debug_flags_reset_func, METH_NOARGS, ""}, {"debug_flags_reset", debug_flags_reset_func, METH_NOARGS, ""},
/* Statistics. */
{"enable_print_stats", enable_print_stats_func, METH_NOARGS, ""},
/* Resumable render */ /* Resumable render */
{"set_resumable_chunk", set_resumable_chunk_func, METH_VARARGS, ""}, {"set_resumable_chunk", set_resumable_chunk_func, METH_VARARGS, ""},
{"set_resumable_chunk_range", set_resumable_chunk_range_func, METH_VARARGS, ""}, {"set_resumable_chunk_range", set_resumable_chunk_range_func, METH_VARARGS, ""},

View File

@@ -28,6 +28,7 @@
#include "render/scene.h" #include "render/scene.h"
#include "render/session.h" #include "render/session.h"
#include "render/shader.h" #include "render/shader.h"
#include "render/stats.h"
#include "util/util_color.h" #include "util/util_color.h"
#include "util/util_foreach.h" #include "util/util_foreach.h"
@@ -48,6 +49,7 @@ int BlenderSession::num_resumable_chunks = 0;
int BlenderSession::current_resumable_chunk = 0; int BlenderSession::current_resumable_chunk = 0;
int BlenderSession::start_resumable_chunk = 0; int BlenderSession::start_resumable_chunk = 0;
int BlenderSession::end_resumable_chunk = 0; int BlenderSession::end_resumable_chunk = 0;
bool BlenderSession::print_render_stats = false;
BlenderSession::BlenderSession(BL::RenderEngine& b_engine, BlenderSession::BlenderSession(BL::RenderEngine& b_engine,
BL::UserPreferences& b_userpref, BL::UserPreferences& b_userpref,
@@ -492,6 +494,12 @@ void BlenderSession::render()
/* free result without merging */ /* free result without merging */
end_render_result(b_engine, b_rr, true, true, false); end_render_result(b_engine, b_rr, true, true, false);
if(!b_engine.is_preview() && background && print_render_stats) {
RenderStats stats;
session->scene->collect_statistics(&stats);
printf("Render statistics:\n%s\n", stats.full_report().c_str());
}
if(session->progress.get_cancel()) if(session->progress.get_cancel())
break; break;
} }

View File

@@ -143,6 +143,8 @@ public:
static int start_resumable_chunk; static int start_resumable_chunk;
static int end_resumable_chunk; static int end_resumable_chunk;
static bool print_render_stats;
protected: protected:
void do_write_update_render_result(BL::RenderResult& b_rr, void do_write_update_render_result(BL::RenderResult& b_rr,
BL::RenderLayer& b_rlay, BL::RenderLayer& b_rlay,

View File

@@ -33,6 +33,7 @@ set(SRC
session.cpp session.cpp
shader.cpp shader.cpp
sobol.cpp sobol.cpp
stats.cpp
svm.cpp svm.cpp
tables.cpp tables.cpp
tile.cpp tile.cpp
@@ -60,6 +61,7 @@ set(SRC_HEADERS
session.h session.h
shader.h shader.h
sobol.h sobol.h
stats.h
svm.h svm.h
tables.h tables.h
tile.h tile.h

View File

@@ -17,6 +17,7 @@
#include "device/device.h" #include "device/device.h"
#include "render/image.h" #include "render/image.h"
#include "render/scene.h" #include "render/scene.h"
#include "render/stats.h"
#include "util/util_foreach.h" #include "util/util_foreach.h"
#include "util/util_logging.h" #include "util/util_logging.h"
@@ -1042,4 +1043,15 @@ void ImageManager::device_free(Device *device)
} }
} }
void ImageManager::collect_statistics(RenderStats *stats)
{
for(int type = 0; type < IMAGE_DATA_NUM_TYPES; type++) {
foreach(const Image *image, images[type]) {
stats->image.textures.add_entry(
NamedSizeEntry(path_filename(image->filename),
image->mem->memory_size()));
}
}
}
CCL_NAMESPACE_END CCL_NAMESPACE_END

View File

@@ -29,6 +29,7 @@ CCL_NAMESPACE_BEGIN
class Device; class Device;
class Progress; class Progress;
class RenderStats;
class Scene; class Scene;
class ImageMetaData { class ImageMetaData {
@@ -89,6 +90,8 @@ public:
device_memory *image_memory(int flat_slot); device_memory *image_memory(int flat_slot);
void collect_statistics(RenderStats *stats);
bool need_update; bool need_update;
/* NOTE: Here pixels_size is a size of storage, which equals to /* NOTE: Here pixels_size is a size of storage, which equals to

View File

@@ -27,6 +27,7 @@
#include "render/nodes.h" #include "render/nodes.h"
#include "render/object.h" #include "render/object.h"
#include "render/scene.h" #include "render/scene.h"
#include "render/stats.h"
#include "kernel/osl/osl_globals.h" #include "kernel/osl/osl_globals.h"
@@ -2276,6 +2277,15 @@ void MeshManager::tag_update(Scene *scene)
scene->object_manager->need_update = true; scene->object_manager->need_update = true;
} }
void MeshManager::collect_statistics(const Scene *scene, RenderStats *stats)
{
foreach(Mesh *mesh, scene->meshes) {
stats->mesh.geometry.add_entry(
NamedSizeEntry(string(mesh->name.c_str()),
mesh->get_total_size_in_bytes()));
}
}
bool Mesh::need_attribute(Scene *scene, AttributeStandard std) bool Mesh::need_attribute(Scene *scene, AttributeStandard std)
{ {
if(std == ATTR_STD_NONE) if(std == ATTR_STD_NONE)

View File

@@ -38,6 +38,7 @@ class Device;
class DeviceScene; class DeviceScene;
class Mesh; class Mesh;
class Progress; class Progress;
class RenderStats;
class Scene; class Scene;
class SceneParams; class SceneParams;
class AttributeRequest; class AttributeRequest;
@@ -351,6 +352,8 @@ public:
void create_volume_mesh(Scene *scene, Mesh *mesh, Progress &progress); void create_volume_mesh(Scene *scene, Mesh *mesh, Progress &progress);
void collect_statistics(const Scene *scene, RenderStats *stats);
protected: protected:
/* Calculate verts/triangles/curves offsets in global arrays. */ /* Calculate verts/triangles/curves offsets in global arrays. */
void mesh_calc_offset(Scene *scene); void mesh_calc_offset(Scene *scene);

View File

@@ -379,4 +379,10 @@ void Scene::device_free()
free_memory(false); free_memory(false);
} }
void Scene::collect_statistics(RenderStats *stats)
{
mesh_manager->collect_statistics(this, stats);
image_manager->collect_statistics(stats);
}
CCL_NAMESPACE_END CCL_NAMESPACE_END

View File

@@ -56,6 +56,7 @@ class ShaderManager;
class Progress; class Progress;
class BakeManager; class BakeManager;
class BakeData; class BakeData;
class RenderStats;
/* Scene Device Data */ /* Scene Device Data */
@@ -255,6 +256,8 @@ public:
void reset(); void reset();
void device_free(); void device_free();
void collect_statistics(RenderStats *stats);
protected: protected:
/* Check if some heavy data worth logging was updated. /* Check if some heavy data worth logging was updated.
* Mainly used to suppress extra annoying logging. * Mainly used to suppress extra annoying logging.

View File

@@ -0,0 +1,119 @@
/*
* Copyright 2011-2018 Blender Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "render/stats.h"
#include "util/util_algorithm.h"
#include "util/util_foreach.h"
#include "util/util_string.h"
CCL_NAMESPACE_BEGIN
static int kIndentNumSpaces = 2;
/* Named size entry. */
namespace {
bool namedSizeEntryComparator(const NamedSizeEntry& a, const NamedSizeEntry& b)
{
/* We sort in descending order. */
return a.size > b.size;
}
} // namespace
NamedSizeEntry::NamedSizeEntry()
: name(""),
size(0) {
}
NamedSizeEntry::NamedSizeEntry(const string& name, size_t size)
: name(name),
size(size) {
}
/* Named size statistics. */
NamedSizeStats::NamedSizeStats()
: total_size(0) {
}
void NamedSizeStats::add_entry(const NamedSizeEntry& entry) {
total_size += entry.size;
entries.push_back(entry);
}
string NamedSizeStats::full_report(int indent_level)
{
const string indent(indent_level * kIndentNumSpaces, ' ');
const string double_indent = indent + indent;
string result = "";
result += string_printf("%sTotal memory: %s (%s)\n",
indent.c_str(),
string_human_readable_size(total_size).c_str(),
string_human_readable_number(total_size).c_str());
sort(entries.begin(), entries.end(), namedSizeEntryComparator);
foreach(const NamedSizeEntry& entry, entries) {
result += string_printf(
"%s%-32s %s (%s)\n",
double_indent.c_str(),
entry.name.c_str(),
string_human_readable_size(entry.size).c_str(),
string_human_readable_number(entry.size).c_str());
}
return result;
}
/* Mesh statistics. */
MeshStats::MeshStats() {
}
string MeshStats::full_report(int indent_level)
{
const string indent(indent_level * kIndentNumSpaces, ' ');
string result = "";
result += indent + "Geometry:\n" + geometry.full_report(indent_level + 1);
return result;
}
/* Image statistics. */
ImageStats::ImageStats() {
}
string ImageStats::full_report(int indent_level)
{
const string indent(indent_level * kIndentNumSpaces, ' ');
string result = "";
result += indent + "Textures:\n" + textures.full_report(indent_level + 1);
return result;
}
/* Overall statistics. */
RenderStats::RenderStats() {
}
string RenderStats::full_report()
{
string result = "";
result += "Mesh statistics:\n" + mesh.full_report(1);
result += "Image statistics:\n" + image.full_report(1);
return result;
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,104 @@
/*
* Copyright 2011-2018 Blender Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __RENDER_STATS_H__
#define __RENDER_STATS_H__
#include "util/util_string.h"
#include "util/util_vector.h"
CCL_NAMESPACE_BEGIN
/* Named statistics entry, which corresponds to a size. There is no real
* semantic around the units of size, it just should be the same for all
* entries.
*
* This is a generic entry foi all size-related statistics, which helps
* avoiding duplicating code for things like sorting.
*/
class NamedSizeEntry {
public:
NamedSizeEntry();
NamedSizeEntry(const string& name, size_t size);
string name;
size_t size;
};
/* Container of named size entries. Used, for example, to store per-mesh memory
* usage statistics. But also keeps track of overall memory usage of the
* container.
*/
class NamedSizeStats {
public:
NamedSizeStats();
/* Add entry to the statistics. */
void add_entry(const NamedSizeEntry& entry);
/* Generate full human-readable report. */
string full_report(int indent_level = 0);
/* Total size of all entries. */
size_t total_size;
/* NOTE: Is fine to read directly, but for adding use add_entry(), which
* makes sure all accumulating values are properly updated.
*/
vector<NamedSizeEntry> entries;
};
/* Statistics about mesh in the render database. */
class MeshStats {
public:
MeshStats();
/* Generate full human-readable report. */
string full_report(int indent_level = 0);
/* Input geometry statistics, this is what is coming as an input to render
* from. say, Blender. This does not include runtime or engine specific
* memory like BVH.
*/
NamedSizeStats geometry;
};
/* Statistics about images held in memory. */
class ImageStats {
public:
ImageStats();
/* Generate full human-readable report. */
string full_report(int indent_level = 0);
NamedSizeStats textures;
};
/* Render process statistics. */
class RenderStats {
public:
RenderStats();
/* Return full report as string. */
string full_report();
MeshStats mesh;
ImageStats image;
};
CCL_NAMESPACE_END
#endif /* __RENDER_STATS_H__ */