Cycles: optimize device updates

This optimizes device updates (during user edits or frame changes in
the viewport) by avoiding unnecessary computations. To achieve this,
we use a combination of the sockets' update flags as well as some new
flags passed to the various managers when tagging for an update to tell
exactly what the tagging is for (e.g. shader was modified, object was
removed, etc.).

Besides avoiding recomputations, we also avoid resending to the devices
unmodified data arrays, thus reducing bandwidth usage. For OptiX and
Embree, BVH packing was also multithreaded.

The performance improvements may vary depending on the used device (CPU
or GPU), and the content of the scene. Simple scenes (e.g. with no adaptive
subdivision or volumes) rendered using OptiX will benefit from this work
the most.

On average, for a variety of animated scenes, this gives a 3x speedup.

Reviewed By: #cycles, brecht

Maniphest Tasks: T79174

Differential Revision: https://developer.blender.org/D9555
This commit is contained in:
Kévin Dietrich 2021-01-22 15:01:26 +01:00
parent 131a758b6f
commit bbe6d44928
Notes: blender-bot 2023-02-14 04:40:22 +01:00
Referenced by commit 88d94d89fa, Fix T87007: Cycles Background not updated if strength is initially null
Referenced by commit 42198e9eb0, Fix T86601: Cycles accumulates displacement when transforming an Object
Referenced by commit 96e60cc22c, Fix T86567: Cycles crashes when playing back animated volumes
Referenced by commit 349c17cf54, Fix T85462: crash in render mode while removing instances
Referenced by issue #87929, Toggling "Indirect Only" for the view layers properties of a collection does not result in a change in the viewport (2.93, 3.0 regression)
Referenced by issue #86567, Quick Smoke with Cycles crashes Blender 2.93 alpha
Referenced by issue #85926, Material not updated in Cycles viewport render when made single user (while rendering with OptiX?)
Referenced by issue #85462, Crash in render mode while removing instances
Referenced by issue #85144, Crash in Blender 2.93 when changing render properties with rendered viewport
Referenced by issue #85048, Cycles Sculpt Vertex Color crash or wrong render
Referenced by issue #85010, Cycles viewport not showing certain material changes when using GPU compute (2.93)
Referenced by issue #79174, Cycles Procedural API and faster scene update
40 changed files with 1084 additions and 338 deletions

View File

@ -855,10 +855,7 @@ void BlenderSync::sync_hair(BL::Depsgraph b_depsgraph, BL::Object b_ob, Hair *ha
hair->set_value(socket, new_hair, socket);
}
hair->attributes.clear();
foreach (Attribute &attr, new_hair.attributes.attributes) {
hair->attributes.attributes.push_back(std::move(attr));
}
hair->attributes.update(std::move(new_hair.attributes));
/* tag update */

View File

@ -124,7 +124,7 @@ Geometry *BlenderSync::sync_geometry(BL::Depsgraph &b_depsgraph,
foreach (Node *node, geom->get_used_shaders()) {
Shader *shader = static_cast<Shader *>(node);
if (shader->need_update_geometry) {
if (shader->need_update_geometry()) {
attribute_recalc = true;
}
}

View File

@ -1075,15 +1075,8 @@ void BlenderSync::sync_mesh(BL::Depsgraph b_depsgraph, BL::Object b_ob, Mesh *me
mesh->set_value(socket, new_mesh, socket);
}
mesh->attributes.clear();
foreach (Attribute &attr, new_mesh.attributes.attributes) {
mesh->attributes.attributes.push_back(std::move(attr));
}
mesh->subd_attributes.clear();
foreach (Attribute &attr, new_mesh.subd_attributes.attributes) {
mesh->subd_attributes.attributes.push_back(std::move(attr));
}
mesh->attributes.update(std::move(new_mesh.attributes));
mesh->subd_attributes.update(std::move(new_mesh.subd_attributes));
mesh->set_num_subd_faces(new_mesh.get_num_subd_faces());

View File

@ -243,9 +243,6 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph,
/* holdout */
object->set_use_holdout(use_holdout);
if (object->use_holdout_is_modified()) {
scene->object_manager->tag_update(scene);
}
object->set_visibility(visibility);

View File

@ -57,7 +57,7 @@ bool BlenderSync::sync_dupli_particle(BL::Object &b_ob,
/* no update needed? */
if (!need_update && !object->get_geometry()->is_modified() &&
!scene->object_manager->need_update)
!scene->object_manager->need_update())
return true;
/* first time used in this sync loop? clear and tag update */
@ -85,7 +85,7 @@ bool BlenderSync::sync_dupli_particle(BL::Object &b_ob,
object->set_particle_index(psys->particles.size() - 1);
if (object->particle_index_is_modified())
scene->object_manager->tag_update(scene);
scene->object_manager->tag_update(scene, ObjectManager::PARTICLE_MODIFIED);
/* return that this object has particle data */
return true;

View File

@ -552,7 +552,6 @@ void BlenderSession::render(BL::Depsgraph &b_depsgraph_)
int seed = scene->integrator->get_seed();
seed += hash_uint2(seed, hash_uint2(view_index * 0xdeadbeef, 0));
scene->integrator->set_seed(seed);
scene->integrator->tag_update(scene);
}
/* Update number of samples per layer. */
@ -1116,10 +1115,6 @@ void BlenderSession::update_resumable_tile_manager(int num_samples)
scene->integrator->set_start_sample(rounded_range_start_sample);
if (scene->integrator->is_modified()) {
scene->integrator->tag_update(scene);
}
session->tile_manager.range_start_sample = rounded_range_start_sample;
session->tile_manager.range_num_samples = rounded_range_num_samples;
}

View File

@ -1497,7 +1497,6 @@ void BlenderSync::sync_world(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d,
shader->set_graph(graph);
shader->tag_update(scene);
background->tag_update(scene);
}
PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
@ -1517,8 +1516,7 @@ void BlenderSync::sync_world(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d,
viewport_parameters.custom_viewport_parameters());
background->set_use_ao(background->get_use_ao() && view_layer.use_background_ao);
if (background->is_modified())
background->tag_update(scene);
background->tag_update(scene);
}
/* Sync Lights */

View File

@ -302,11 +302,6 @@ void BlenderSync::sync_integrator()
integrator->set_sample_clamp_direct(get_float(cscene, "sample_clamp_direct"));
integrator->set_sample_clamp_indirect(get_float(cscene, "sample_clamp_indirect"));
if (!preview) {
if (integrator->get_motion_blur() != r.use_motion_blur()) {
scene->object_manager->tag_update(scene);
scene->camera->tag_modified();
}
integrator->set_motion_blur(r.use_motion_blur());
}
@ -375,8 +370,8 @@ void BlenderSync::sync_integrator()
integrator->set_ao_bounces(0);
}
if (integrator->is_modified())
integrator->tag_update(scene);
/* UPDATE_NONE as we don't want to tag the integrator as modified, just tag dependent things */
integrator->tag_update(scene, Integrator::UPDATE_NONE);
}
/* Film */
@ -729,7 +724,7 @@ vector<Pass> BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay,
scene->film->set_pass_alpha_threshold(b_view_layer.pass_alpha_threshold());
scene->film->tag_passes_update(scene, passes);
scene->integrator->tag_update(scene);
scene->integrator->tag_update(scene, Integrator::UPDATE_ALL);
return passes;
}

View File

@ -259,6 +259,8 @@ class device_memory {
device_ptr original_device_ptr;
size_t original_device_size;
Device *original_device;
bool need_realloc_;
bool modified;
};
/* Device Only Memory
@ -329,6 +331,8 @@ template<typename T> class device_vector : public device_memory {
{
data_type = device_type_traits<T>::data_type;
data_elements = device_type_traits<T>::num_elements;
modified = true;
need_realloc_ = true;
assert(data_elements > 0);
}
@ -347,6 +351,7 @@ template<typename T> class device_vector : public device_memory {
device_free();
host_free();
host_pointer = host_alloc(sizeof(T) * new_size);
modified = true;
assert(device_pointer == 0);
}
@ -400,6 +405,19 @@ template<typename T> class device_vector : public device_memory {
assert(device_pointer == 0);
}
void give_data(array<T> &to)
{
device_free();
to.set_data((T *)host_pointer, data_size);
data_size = 0;
data_width = 0;
data_height = 0;
data_depth = 0;
host_pointer = 0;
assert(device_pointer == 0);
}
/* Free device and host memory. */
void free()
{
@ -411,10 +429,40 @@ template<typename T> class device_vector : public device_memory {
data_height = 0;
data_depth = 0;
host_pointer = 0;
modified = true;
need_realloc_ = true;
assert(device_pointer == 0);
}
size_t size()
void free_if_need_realloc(bool force_free)
{
if (need_realloc_ || force_free) {
free();
}
}
bool is_modified() const
{
return modified;
}
bool need_realloc()
{
return need_realloc_;
}
void tag_modified()
{
modified = true;
}
void tag_realloc()
{
need_realloc_ = true;
tag_modified();
}
size_t size() const
{
return data_size;
}
@ -432,7 +480,24 @@ template<typename T> class device_vector : public device_memory {
void copy_to_device()
{
device_copy_to();
if (data_size != 0) {
device_copy_to();
}
}
void copy_to_device_if_modified()
{
if (!modified) {
return;
}
copy_to_device();
}
void clear_modified()
{
modified = false;
need_realloc_ = false;
}
void copy_from_device()

View File

@ -28,7 +28,7 @@ CCL_NAMESPACE_BEGIN
Attribute::Attribute(
ustring name, TypeDesc type, AttributeElement element, Geometry *geom, AttributePrimitive prim)
: name(name), std(ATTR_STD_NONE), type(type), element(element), flags(0)
: name(name), std(ATTR_STD_NONE), type(type), element(element), flags(0), modified(true)
{
/* string and matrix not supported! */
assert(type == TypeDesc::TypeFloat || type == TypeDesc::TypeColor ||
@ -82,6 +82,8 @@ void Attribute::add(const float &f)
for (size_t i = 0; i < size; i++)
buffer.push_back(data[i]);
modified = true;
}
void Attribute::add(const uchar4 &f)
@ -93,6 +95,8 @@ void Attribute::add(const uchar4 &f)
for (size_t i = 0; i < size; i++)
buffer.push_back(data[i]);
modified = true;
}
void Attribute::add(const float2 &f)
@ -104,6 +108,8 @@ void Attribute::add(const float2 &f)
for (size_t i = 0; i < size; i++)
buffer.push_back(data[i]);
modified = true;
}
void Attribute::add(const float3 &f)
@ -115,6 +121,8 @@ void Attribute::add(const float3 &f)
for (size_t i = 0; i < size; i++)
buffer.push_back(data[i]);
modified = true;
}
void Attribute::add(const Transform &f)
@ -126,6 +134,8 @@ void Attribute::add(const Transform &f)
for (size_t i = 0; i < size; i++)
buffer.push_back(data[i]);
modified = true;
}
void Attribute::add(const char *data)
@ -134,6 +144,26 @@ void Attribute::add(const char *data)
for (size_t i = 0; i < size; i++)
buffer.push_back(data[i]);
modified = true;
}
void Attribute::set_data_from(Attribute &&other)
{
assert(other.std == std);
assert(other.type == type);
assert(other.element == element);
this->flags = other.flags;
if (this->buffer.size() != other.buffer.size()) {
this->buffer = std::move(other.buffer);
modified = true;
}
else if (memcmp(this->data(), other.data(), other.buffer.size()) != 0) {
this->buffer = std::move(other.buffer);
modified = true;
}
}
size_t Attribute::data_sizeof() const
@ -627,6 +657,49 @@ void AttributeSet::clear(bool preserve_voxel_data)
}
}
void AttributeSet::update(AttributeSet &&new_attributes)
{
/* add or update old_attributes based on the new_attributes */
foreach (Attribute &attr, new_attributes.attributes) {
Attribute *nattr = nullptr;
if (attr.std != ATTR_STD_NONE) {
nattr = add(attr.std, attr.name);
}
else {
nattr = add(attr.name, attr.type, attr.element);
}
nattr->set_data_from(std::move(attr));
}
/* remove any attributes not on new_attributes */
list<Attribute>::iterator it;
for (it = attributes.begin(); it != attributes.end();) {
if (it->std != ATTR_STD_NONE) {
if (new_attributes.find(it->std) == nullptr) {
attributes.erase(it++);
continue;
}
}
else if (it->name != "") {
if (new_attributes.find(it->name) == nullptr) {
attributes.erase(it++);
continue;
}
}
it++;
}
}
void AttributeSet::clear_modified()
{
foreach (Attribute &attr, attributes) {
attr.modified = false;
}
}
/* AttributeRequest */
AttributeRequest::AttributeRequest(ustring name_)

View File

@ -54,6 +54,8 @@ class Attribute {
AttributeElement element;
uint flags; /* enum AttributeFlag */
bool modified;
Attribute(ustring name,
TypeDesc type,
AttributeElement element,
@ -159,6 +161,8 @@ class Attribute {
void add(const Transform &tfm);
void add(const char *data);
void set_data_from(Attribute &&other);
static bool same_storage(TypeDesc a, TypeDesc b);
static const char *standard_name(AttributeStandard std);
static AttributeStandard name_standard(const char *name);
@ -194,6 +198,12 @@ class AttributeSet {
void resize(bool reserve_only = false);
void clear(bool preserve_voxel_data = false);
/* Update the attributes in this AttributeSet with the ones from the new set,
* and remove any attribute not found on the new set from this. */
void update(AttributeSet &&new_attributes);
void clear_modified();
};
/* AttributeRequest

View File

@ -130,8 +130,9 @@ void Background::device_free(Device * /*device*/, DeviceScene * /*dscene*/)
void Background::tag_update(Scene *scene)
{
scene->integrator->tag_update(scene);
tag_modified();
if (ao_factor_is_modified() || use_ao_is_modified()) {
scene->integrator->tag_update(scene, Integrator::BACKGROUND_AO_MODIFIED);
}
}
Shader *Background::get_shader(const Scene *scene)

View File

@ -78,7 +78,7 @@ BakeManager::BakeManager()
type = SHADER_EVAL_BAKE;
pass_filter = 0;
need_update = true;
need_update_ = true;
}
BakeManager::~BakeManager()
@ -114,9 +114,9 @@ void BakeManager::set(Scene *scene,
/* create device and update scene */
scene->film->tag_modified();
scene->integrator->tag_update(scene);
scene->integrator->tag_update(scene, Integrator::UPDATE_ALL);
need_update = true;
need_update_ = true;
}
void BakeManager::device_update(Device * /*device*/,
@ -124,7 +124,7 @@ void BakeManager::device_update(Device * /*device*/,
Scene *scene,
Progress & /* progress */)
{
if (!need_update)
if (!need_update())
return;
scoped_callback_timer timer([scene](double time) {
@ -152,11 +152,21 @@ void BakeManager::device_update(Device * /*device*/,
object_index++;
}
need_update = false;
need_update_ = false;
}
void BakeManager::device_free(Device * /*device*/, DeviceScene * /*dscene*/)
{
}
void BakeManager::tag_update()
{
need_update_ = true;
}
bool BakeManager::need_update() const
{
return need_update_;
}
CCL_NAMESPACE_END

View File

@ -36,9 +36,12 @@ class BakeManager {
void device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress);
void device_free(Device *device, DeviceScene *dscene);
bool need_update;
void tag_update();
bool need_update() const;
private:
bool need_update_;
ShaderEvalType type;
int pass_filter;
std::string object_name;

View File

@ -688,16 +688,16 @@ void Film::device_free(Device * /*device*/, DeviceScene * /*dscene*/, Scene *sce
void Film::tag_passes_update(Scene *scene, const vector<Pass> &passes_, bool update_passes)
{
if (Pass::contains(scene->passes, PASS_UV) != Pass::contains(passes_, PASS_UV)) {
scene->geometry_manager->tag_update(scene);
scene->geometry_manager->tag_update(scene, GeometryManager::UV_PASS_NEEDED);
foreach (Shader *shader, scene->shaders)
shader->need_update_geometry = true;
shader->need_update_uvs = true;
}
else if (Pass::contains(scene->passes, PASS_MOTION) != Pass::contains(passes_, PASS_MOTION)) {
scene->geometry_manager->tag_update(scene);
scene->geometry_manager->tag_update(scene, GeometryManager::MOTION_PASS_NEEDED);
}
else if (Pass::contains(scene->passes, PASS_AO) != Pass::contains(passes_, PASS_AO)) {
scene->integrator->tag_update(scene);
scene->integrator->tag_update(scene, Integrator::AO_PASS_MODIFIED);
}
if (update_passes) {

View File

@ -240,7 +240,6 @@ void Geometry::compute_bvh(
}
}
clear_modified();
need_update_rebuild = false;
}
@ -262,22 +261,21 @@ bool Geometry::has_voxel_attributes() const
void Geometry::tag_update(Scene *scene, bool rebuild)
{
tag_modified();
if (rebuild) {
need_update_rebuild = true;
scene->light_manager->need_update = true;
scene->light_manager->tag_update(scene, LightManager::MESH_NEED_REBUILD);
}
else {
foreach (Node *node, used_shaders) {
Shader *shader = static_cast<Shader *>(node);
if (shader->has_surface_emission)
scene->light_manager->need_update = true;
if (shader->has_surface_emission) {
scene->light_manager->tag_update(scene, LightManager::EMISSIVE_MESH_MODIFIED);
break;
}
}
}
scene->geometry_manager->need_update = true;
scene->object_manager->need_update = true;
scene->geometry_manager->tag_update(scene, GeometryManager::GEOMETRY_MODIFIED);
}
void Geometry::tag_bvh_update(bool rebuild)
@ -293,7 +291,7 @@ void Geometry::tag_bvh_update(bool rebuild)
GeometryManager::GeometryManager()
{
need_update = true;
update_flags = UPDATE_ALL;
need_flags_update = true;
}
@ -494,6 +492,10 @@ void GeometryManager::update_svm_attributes(Device *,
if (attr_map_size == 0)
return;
if (!dscene->attributes_map.need_realloc()) {
return;
}
/* create attribute map */
uint4 *attr_map = dscene->attributes_map.alloc(attr_map_size);
memset(attr_map, 0, dscene->attributes_map.size() * sizeof(uint));
@ -602,8 +604,10 @@ void GeometryManager::update_attribute_element_offset(Geometry *geom,
offset = attr_uchar4_offset;
assert(attr_uchar4.size() >= offset + size);
for (size_t k = 0; k < size; k++) {
attr_uchar4[offset + k] = data[k];
if (mattr->modified) {
for (size_t k = 0; k < size; k++) {
attr_uchar4[offset + k] = data[k];
}
}
attr_uchar4_offset += size;
}
@ -612,8 +616,10 @@ void GeometryManager::update_attribute_element_offset(Geometry *geom,
offset = attr_float_offset;
assert(attr_float.size() >= offset + size);
for (size_t k = 0; k < size; k++) {
attr_float[offset + k] = data[k];
if (mattr->modified) {
for (size_t k = 0; k < size; k++) {
attr_float[offset + k] = data[k];
}
}
attr_float_offset += size;
}
@ -622,8 +628,10 @@ void GeometryManager::update_attribute_element_offset(Geometry *geom,
offset = attr_float2_offset;
assert(attr_float2.size() >= offset + size);
for (size_t k = 0; k < size; k++) {
attr_float2[offset + k] = data[k];
if (mattr->modified) {
for (size_t k = 0; k < size; k++) {
attr_float2[offset + k] = data[k];
}
}
attr_float2_offset += size;
}
@ -632,8 +640,10 @@ void GeometryManager::update_attribute_element_offset(Geometry *geom,
offset = attr_float3_offset;
assert(attr_float3.size() >= offset + size * 3);
for (size_t k = 0; k < size * 3; k++) {
attr_float3[offset + k] = (&tfm->x)[k];
if (mattr->modified) {
for (size_t k = 0; k < size * 3; k++) {
attr_float3[offset + k] = (&tfm->x)[k];
}
}
attr_float3_offset += size * 3;
}
@ -642,8 +652,10 @@ void GeometryManager::update_attribute_element_offset(Geometry *geom,
offset = attr_float3_offset;
assert(attr_float3.size() >= offset + size);
for (size_t k = 0; k < size; k++) {
attr_float3[offset + k] = data[k];
if (mattr->modified) {
for (size_t k = 0; k < size; k++) {
attr_float3[offset + k] = data[k];
}
}
attr_float3_offset += size;
}
@ -808,6 +820,11 @@ void GeometryManager::device_update_attributes(Device *device,
dscene->attributes_float3.alloc(attr_float3_size);
dscene->attributes_uchar4.alloc(attr_uchar4_size);
const bool copy_all_data = dscene->attributes_float.need_realloc() ||
dscene->attributes_float2.need_realloc() ||
dscene->attributes_float3.need_realloc() ||
dscene->attributes_uchar4.need_realloc();
size_t attr_float_offset = 0;
size_t attr_float2_offset = 0;
size_t attr_float3_offset = 0;
@ -822,6 +839,12 @@ void GeometryManager::device_update_attributes(Device *device,
* they actually refer to the same mesh attributes, optimize */
foreach (AttributeRequest &req, attributes.requests) {
Attribute *attr = geom->attributes.find(req);
if (attr) {
/* force a copy if we need to reallocate all the data */
attr->modified |= copy_all_data;
}
update_attribute_element_offset(geom,
dscene->attributes_float,
attr_float_offset,
@ -840,6 +863,11 @@ void GeometryManager::device_update_attributes(Device *device,
Mesh *mesh = static_cast<Mesh *>(geom);
Attribute *subd_attr = mesh->subd_attributes.find(req);
if (subd_attr) {
/* force a copy if we need to reallocate all the data */
subd_attr->modified |= copy_all_data;
}
update_attribute_element_offset(mesh,
dscene->attributes_float,
attr_float_offset,
@ -903,18 +931,10 @@ void GeometryManager::device_update_attributes(Device *device,
/* copy to device */
progress.set_status("Updating Mesh", "Copying Attributes to device");
if (dscene->attributes_float.size()) {
dscene->attributes_float.copy_to_device();
}
if (dscene->attributes_float2.size()) {
dscene->attributes_float2.copy_to_device();
}
if (dscene->attributes_float3.size()) {
dscene->attributes_float3.copy_to_device();
}
if (dscene->attributes_uchar4.size()) {
dscene->attributes_uchar4.copy_to_device();
}
dscene->attributes_float.copy_to_device();
dscene->attributes_float2.copy_to_device();
dscene->attributes_float3.copy_to_device();
dscene->attributes_uchar4.copy_to_device();
if (progress.get_cancel())
return;
@ -1066,17 +1086,34 @@ void GeometryManager::device_update_mesh(
uint *tri_patch = dscene->tri_patch.alloc(tri_size);
float2 *tri_patch_uv = dscene->tri_patch_uv.alloc(vert_size);
const bool copy_all_data = dscene->tri_shader.need_realloc() ||
dscene->tri_vindex.need_realloc() ||
dscene->tri_vnormal.need_realloc() ||
dscene->tri_patch.need_realloc() ||
dscene->tri_patch_uv.need_realloc();
foreach (Geometry *geom, scene->geometry) {
if (geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME) {
Mesh *mesh = static_cast<Mesh *>(geom);
mesh->pack_shaders(scene, &tri_shader[mesh->prim_offset]);
mesh->pack_normals(&vnormal[mesh->vert_offset]);
mesh->pack_verts(tri_prim_index,
&tri_vindex[mesh->prim_offset],
&tri_patch[mesh->prim_offset],
&tri_patch_uv[mesh->vert_offset],
mesh->vert_offset,
mesh->prim_offset);
if (mesh->shader_is_modified() || mesh->smooth_is_modified() ||
mesh->triangles_is_modified() || copy_all_data) {
mesh->pack_shaders(scene, &tri_shader[mesh->prim_offset]);
}
if (mesh->verts_is_modified() || copy_all_data) {
mesh->pack_normals(&vnormal[mesh->vert_offset]);
}
if (mesh->triangles_is_modified() || mesh->vert_patch_uv_is_modified() || copy_all_data) {
mesh->pack_verts(tri_prim_index,
&tri_vindex[mesh->prim_offset],
&tri_patch[mesh->prim_offset],
&tri_patch_uv[mesh->vert_offset],
mesh->vert_offset,
mesh->prim_offset);
}
if (progress.get_cancel())
return;
}
@ -1085,11 +1122,11 @@ void GeometryManager::device_update_mesh(
/* vertex coordinates */
progress.set_status("Updating Mesh", "Copying Mesh to device");
dscene->tri_shader.copy_to_device();
dscene->tri_vnormal.copy_to_device();
dscene->tri_vindex.copy_to_device();
dscene->tri_patch.copy_to_device();
dscene->tri_patch_uv.copy_to_device();
dscene->tri_shader.copy_to_device_if_modified();
dscene->tri_vnormal.copy_to_device_if_modified();
dscene->tri_vindex.copy_to_device_if_modified();
dscene->tri_patch.copy_to_device_if_modified();
dscene->tri_patch_uv.copy_to_device_if_modified();
}
if (curve_size != 0) {
@ -1098,9 +1135,21 @@ void GeometryManager::device_update_mesh(
float4 *curve_keys = dscene->curve_keys.alloc(curve_key_size);
float4 *curves = dscene->curves.alloc(curve_size);
const bool copy_all_data = dscene->curve_keys.need_realloc() || dscene->curves.need_realloc();
foreach (Geometry *geom, scene->geometry) {
if (geom->is_hair()) {
Hair *hair = static_cast<Hair *>(geom);
bool curve_keys_co_modified = hair->curve_radius_is_modified() ||
hair->curve_keys_is_modified();
bool curve_data_modified = hair->curve_shader_is_modified() ||
hair->curve_first_key_is_modified();
if (!curve_keys_co_modified && !curve_data_modified && !copy_all_data) {
continue;
}
hair->pack_curves(scene,
&curve_keys[hair->curvekey_offset],
&curves[hair->prim_offset],
@ -1110,11 +1159,11 @@ void GeometryManager::device_update_mesh(
}
}
dscene->curve_keys.copy_to_device();
dscene->curves.copy_to_device();
dscene->curve_keys.copy_to_device_if_modified();
dscene->curves.copy_to_device_if_modified();
}
if (patch_size != 0) {
if (patch_size != 0 && dscene->patches.need_realloc()) {
progress.set_status("Updating Mesh", "Copying Patches to device");
uint *patch_data = dscene->patches.alloc(patch_size);
@ -1180,16 +1229,25 @@ void GeometryManager::device_update_bvh(Device *device,
VLOG(1) << "Using " << bvh_layout_name(bparams.bvh_layout) << " layout.";
delete scene->bvh;
BVH *bvh = scene->bvh = BVH::create(bparams, scene->geometry, scene->objects, device);
device->build_bvh(bvh, progress, false);
const bool can_refit = scene->bvh != nullptr &&
(bparams.bvh_layout == BVHLayout::BVH_LAYOUT_OPTIX);
const bool pack_all = scene->bvh == nullptr;
BVH *bvh = scene->bvh;
if (!scene->bvh) {
bvh = scene->bvh = BVH::create(bparams, scene->geometry, scene->objects, device);
}
device->build_bvh(bvh, progress, can_refit);
if (progress.get_cancel()) {
return;
}
const bool has_bvh2_layout = (bparams.bvh_layout == BVH_LAYOUT_BVH2);
PackedBVH pack;
if (bparams.bvh_layout == BVH_LAYOUT_BVH2) {
if (has_bvh2_layout) {
pack = std::move(static_cast<BVH2 *>(bvh)->pack);
}
else {
@ -1210,12 +1268,22 @@ void GeometryManager::device_update_bvh(Device *device,
}
pack.root_index = -1;
pack.prim_tri_index.reserve(num_prims);
pack.prim_tri_verts.reserve(num_tri_verts);
pack.prim_type.reserve(num_prims);
pack.prim_index.reserve(num_prims);
pack.prim_object.reserve(num_prims);
pack.prim_visibility.reserve(num_prims);
if (!pack_all) {
/* if we do not need to recreate the BVH, then only the vertices are updated, so we can
* safely retake the memory */
dscene->prim_tri_verts.give_data(pack.prim_tri_verts);
}
else {
/* it is not stricly necessary to skip those resizes we if do not have to repack, as the OS
* will not allocate pages if we do not touch them, however it does help catching bugs */
pack.prim_tri_index.resize(num_prims);
pack.prim_tri_verts.resize(num_tri_verts);
pack.prim_type.resize(num_prims);
pack.prim_index.resize(num_prims);
pack.prim_object.resize(num_prims);
pack.prim_visibility.resize(num_prims);
}
// Merge visibility flags of all objects and find object index for non-instanced geometry
unordered_map<const Geometry *, pair<int, uint>> geometry_to_object_info;
@ -1229,17 +1297,27 @@ void GeometryManager::device_update_bvh(Device *device,
}
}
TaskPool pool;
// Iterate over scene mesh list instead of objects, since 'optix_prim_offset' was calculated
// based on that list, which may be ordered differently from the object list.
foreach (Geometry *geom, scene->geometry) {
if (!pack_all && !geom->is_modified()) {
continue;
}
const pair<int, uint> &info = geometry_to_object_info[geom];
geom->pack_primitives(pack, info.first, info.second);
pool.push(function_bind(
&Geometry::pack_primitives, geom, &pack, info.first, info.second, pack_all));
}
pool.wait_work();
}
/* copy to device */
progress.set_status("Updating Scene BVH", "Copying BVH to device");
/* When using BVH2, we always have to copy/update the data as its layout is dependent on the
* BVH's leaf nodes which may be different when the objects or vertices move. */
if (pack.nodes.size()) {
dscene->bvh_nodes.steal_data(pack.nodes);
dscene->bvh_nodes.copy_to_device();
@ -1252,7 +1330,7 @@ void GeometryManager::device_update_bvh(Device *device,
dscene->object_node.steal_data(pack.object_node);
dscene->object_node.copy_to_device();
}
if (pack.prim_tri_index.size()) {
if (pack.prim_tri_index.size() && (dscene->prim_tri_index.need_realloc() || has_bvh2_layout)) {
dscene->prim_tri_index.steal_data(pack.prim_tri_index);
dscene->prim_tri_index.copy_to_device();
}
@ -1260,23 +1338,23 @@ void GeometryManager::device_update_bvh(Device *device,
dscene->prim_tri_verts.steal_data(pack.prim_tri_verts);
dscene->prim_tri_verts.copy_to_device();
}
if (pack.prim_type.size()) {
if (pack.prim_type.size() && (dscene->prim_type.need_realloc() || has_bvh2_layout)) {
dscene->prim_type.steal_data(pack.prim_type);
dscene->prim_type.copy_to_device();
}
if (pack.prim_visibility.size()) {
if (pack.prim_visibility.size() && (dscene->prim_visibility.need_realloc() || has_bvh2_layout)) {
dscene->prim_visibility.steal_data(pack.prim_visibility);
dscene->prim_visibility.copy_to_device();
}
if (pack.prim_index.size()) {
if (pack.prim_index.size() && (dscene->prim_index.need_realloc() || has_bvh2_layout)) {
dscene->prim_index.steal_data(pack.prim_index);
dscene->prim_index.copy_to_device();
}
if (pack.prim_object.size()) {
if (pack.prim_object.size() && (dscene->prim_object.need_realloc() || has_bvh2_layout)) {
dscene->prim_object.steal_data(pack.prim_object);
dscene->prim_object.copy_to_device();
}
if (pack.prim_time.size()) {
if (pack.prim_time.size() && (dscene->prim_time.need_realloc() || has_bvh2_layout)) {
dscene->prim_time.steal_data(pack.prim_time);
dscene->prim_time.copy_to_device();
}
@ -1289,12 +1367,65 @@ void GeometryManager::device_update_bvh(Device *device,
dscene->data.bvh.scene = NULL;
}
/* Set of flags used to help determining what data has been modified or needs reallocation, so we
* can decide which device data to free or update. */
enum {
DEVICE_CURVE_DATA_MODIFIED = (1 << 0),
DEVICE_MESH_DATA_MODIFIED = (1 << 1),
ATTR_FLOAT_MODIFIED = (1 << 2),
ATTR_FLOAT2_MODIFIED = (1 << 3),
ATTR_FLOAT3_MODIFIED = (1 << 4),
ATTR_UCHAR4_MODIFIED = (1 << 5),
CURVE_DATA_NEED_REALLOC = (1 << 6),
MESH_DATA_NEED_REALLOC = (1 << 7),
ATTR_FLOAT_NEEDS_REALLOC = (1 << 8),
ATTR_FLOAT2_NEEDS_REALLOC = (1 << 9),
ATTR_FLOAT3_NEEDS_REALLOC = (1 << 10),
ATTR_UCHAR4_NEEDS_REALLOC = (1 << 11),
ATTRS_NEED_REALLOC = (ATTR_FLOAT_NEEDS_REALLOC | ATTR_FLOAT2_NEEDS_REALLOC |
ATTR_FLOAT3_NEEDS_REALLOC | ATTR_UCHAR4_NEEDS_REALLOC),
DEVICE_MESH_DATA_NEEDS_REALLOC = (CURVE_DATA_NEED_REALLOC | ATTRS_NEED_REALLOC),
DEVICE_CURVE_DATA_NEEDS_REALLOC = (MESH_DATA_NEED_REALLOC | ATTRS_NEED_REALLOC),
};
static void update_device_flags_attribute(uint32_t &device_update_flags,
const AttributeSet &attributes)
{
foreach (const Attribute &attr, attributes.attributes) {
if (!attr.modified) {
continue;
}
if (attr.element == ATTR_ELEMENT_CORNER) {
device_update_flags |= ATTR_UCHAR4_MODIFIED;
}
else if (attr.type == TypeDesc::TypeFloat) {
device_update_flags |= ATTR_FLOAT_MODIFIED;
}
else if (attr.type == TypeFloat2) {
device_update_flags |= ATTR_FLOAT2_MODIFIED;
}
else if (attr.type == TypeDesc::TypeMatrix) {
device_update_flags |= ATTR_FLOAT3_MODIFIED;
}
else if (attr.element != ATTR_ELEMENT_VOXEL) {
device_update_flags |= ATTR_FLOAT3_MODIFIED;
}
}
}
void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Progress &progress)
{
if (!need_update && !need_flags_update) {
if (!need_update() && !need_flags_update) {
return;
}
uint32_t device_update_flags = 0;
scoped_callback_timer timer([scene](double time) {
if (scene->update_stats) {
scene->update_stats->geometry.times.add_entry({"device_update_preprocess", time});
@ -1314,9 +1445,54 @@ void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Pro
if (shader->has_volume) {
geom->has_volume = true;
}
if (shader->has_surface_bssrdf) {
geom->has_surface_bssrdf = true;
}
if (shader->need_update_uvs) {
device_update_flags |= ATTR_FLOAT2_NEEDS_REALLOC;
/* Attributes might need to be tesselated if added. */
if (geom->is_mesh()) {
Mesh *mesh = static_cast<Mesh *>(geom);
if (mesh->need_tesselation()) {
mesh->tag_modified();
}
}
}
if (shader->need_update_attribute) {
device_update_flags |= ATTRS_NEED_REALLOC;
/* Attributes might need to be tesselated if added. */
if (geom->is_mesh()) {
Mesh *mesh = static_cast<Mesh *>(geom);
if (mesh->need_tesselation()) {
mesh->tag_modified();
}
}
}
if (shader->need_update_displacement) {
/* tag displacement related sockets as modified */
if (geom->is_mesh()) {
Mesh *mesh = static_cast<Mesh *>(geom);
mesh->tag_verts_modified();
mesh->tag_subd_dicing_rate_modified();
mesh->tag_subd_max_level_modified();
mesh->tag_subd_objecttoworld_modified();
device_update_flags |= ATTRS_NEED_REALLOC;
}
}
}
/* only check for modified attributes if we do not need to reallocate them already */
if ((device_update_flags & ATTRS_NEED_REALLOC) == 0) {
update_device_flags_attribute(device_update_flags, geom->attributes);
/* don't check for subd_attributes, as if they were modified, we would need to reallocate
* anyway */
}
/* Re-create volume mesh if we will rebuild or refit the BVH. Note we
@ -1332,13 +1508,118 @@ void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Pro
Volume *volume = static_cast<Volume *>(geom);
create_volume_mesh(volume, progress);
/* always reallocate when we have a volume, as we need to rebuild the BVH */
device_update_flags |= DEVICE_MESH_DATA_NEEDS_REALLOC;
}
if (geom->is_hair()) {
/* Set curve shape, still a global scene setting for now. */
Hair *hair = static_cast<Hair *>(geom);
hair->curve_shape = scene->params.hair_shape;
if (hair->need_update_rebuild) {
device_update_flags |= DEVICE_CURVE_DATA_NEEDS_REALLOC;
}
else if (hair->is_modified()) {
device_update_flags |= DEVICE_CURVE_DATA_MODIFIED;
}
}
if (geom->is_mesh()) {
Mesh *mesh = static_cast<Mesh *>(geom);
if (mesh->need_update_rebuild) {
device_update_flags |= DEVICE_MESH_DATA_NEEDS_REALLOC;
}
else if (mesh->verts_is_modified()) {
device_update_flags |= DEVICE_MESH_DATA_MODIFIED;
}
}
}
if (update_flags & (MESH_ADDED | MESH_REMOVED)) {
device_update_flags |= DEVICE_MESH_DATA_NEEDS_REALLOC;
}
if (update_flags & (HAIR_ADDED | HAIR_REMOVED)) {
device_update_flags |= DEVICE_CURVE_DATA_NEEDS_REALLOC;
}
/* tag the device arrays for reallocation or modification */
DeviceScene *dscene = &scene->dscene;
if (device_update_flags & (DEVICE_MESH_DATA_NEEDS_REALLOC | DEVICE_CURVE_DATA_NEEDS_REALLOC)) {
delete scene->bvh;
scene->bvh = nullptr;
dscene->bvh_nodes.tag_realloc();
dscene->bvh_leaf_nodes.tag_realloc();
dscene->object_node.tag_realloc();
dscene->prim_tri_verts.tag_realloc();
dscene->prim_tri_index.tag_realloc();
dscene->prim_type.tag_realloc();
dscene->prim_visibility.tag_realloc();
dscene->prim_index.tag_realloc();
dscene->prim_object.tag_realloc();
dscene->prim_time.tag_realloc();
if (device_update_flags & DEVICE_MESH_DATA_NEEDS_REALLOC) {
dscene->tri_vnormal.tag_realloc();
dscene->tri_vindex.tag_realloc();
dscene->tri_patch.tag_realloc();
dscene->tri_vnormal.tag_realloc();
dscene->tri_patch_uv.tag_realloc();
dscene->patches.tag_realloc();
}
if (device_update_flags & DEVICE_CURVE_DATA_NEEDS_REALLOC) {
dscene->curves.tag_realloc();
dscene->curve_keys.tag_realloc();
}
}
if (device_update_flags & ATTR_FLOAT_NEEDS_REALLOC) {
dscene->attributes_map.tag_realloc();
dscene->attributes_float.tag_realloc();
}
else if (device_update_flags & ATTR_FLOAT_MODIFIED) {
dscene->attributes_float.tag_modified();
}
if (device_update_flags & ATTR_FLOAT2_NEEDS_REALLOC) {
dscene->attributes_map.tag_realloc();
dscene->attributes_float2.tag_realloc();
}
else if (device_update_flags & ATTR_FLOAT_MODIFIED) {
dscene->attributes_float.tag_modified();
}
if (device_update_flags & ATTR_FLOAT3_NEEDS_REALLOC) {
dscene->attributes_map.tag_realloc();
dscene->attributes_float3.tag_realloc();
}
else if (device_update_flags & ATTR_FLOAT_MODIFIED) {
dscene->attributes_float.tag_modified();
}
if (device_update_flags & ATTR_UCHAR4_NEEDS_REALLOC) {
dscene->attributes_map.tag_realloc();
dscene->attributes_uchar4.tag_realloc();
}
else if (device_update_flags & ATTR_UCHAR4_MODIFIED) {
dscene->attributes_uchar4.tag_modified();
}
if (device_update_flags & DEVICE_MESH_DATA_MODIFIED) {
/* if anything else than vertices are modified, we would need to reallocate, so this is the
* only array that can be updated */
dscene->tri_vnormal.tag_modified();
}
if (device_update_flags & DEVICE_CURVE_DATA_MODIFIED) {
dscene->curve_keys.tag_modified();
dscene->curves.tag_modified();
}
need_flags_update = false;
@ -1423,7 +1704,7 @@ void GeometryManager::device_update(Device *device,
Scene *scene,
Progress &progress)
{
if (!need_update)
if (!need_update())
return;
VLOG(1) << "Total " << scene->geometry.size() << " meshes.";
@ -1439,12 +1720,6 @@ void GeometryManager::device_update(Device *device,
});
foreach (Geometry *geom, scene->geometry) {
foreach (Node *node, geom->get_used_shaders()) {
Shader *shader = static_cast<Shader *>(node);
if (shader->need_update_geometry)
geom->tag_modified();
}
if (geom->is_modified() &&
(geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME)) {
Mesh *mesh = static_cast<Mesh *>(geom);
@ -1541,7 +1816,7 @@ void GeometryManager::device_update(Device *device,
}
/* Device update. */
device_free(device, dscene);
device_free(device, dscene, false);
const BVHLayout bvh_layout = BVHParams::best_bvh_layout(scene->params.bvh_layout,
device->get_bvh_layout_mask());
@ -1614,7 +1889,7 @@ void GeometryManager::device_update(Device *device,
{"device_update (displacement: attributes)", time});
}
});
device_free(device, dscene);
device_free(device, dscene, false);
device_update_attributes(device, dscene, scene, progress);
if (progress.get_cancel()) {
@ -1622,6 +1897,9 @@ void GeometryManager::device_update(Device *device,
}
}
/* update the bvh even when there is no geometry so the kernel bvh data is still valid,
* especially when removing all of the objects during interactive renders */
bool need_update_scene_bvh = (scene->bvh == nullptr);
{
scoped_callback_timer timer([scene](double time) {
if (scene->update_stats) {
@ -1633,6 +1911,7 @@ void GeometryManager::device_update(Device *device,
size_t i = 0;
foreach (Geometry *geom, scene->geometry) {
if (geom->is_modified()) {
need_update_scene_bvh = true;
pool.push(function_bind(
&Geometry::compute_bvh, geom, device, dscene, &scene->params, &progress, i, num_bvh));
if (geom->need_build_bvh(bvh_layout)) {
@ -1647,7 +1926,9 @@ void GeometryManager::device_update(Device *device,
}
foreach (Shader *shader, scene->shaders) {
shader->need_update_geometry = false;
shader->need_update_uvs = false;
shader->need_update_attribute = false;
shader->need_update_displacement = false;
}
Scene::MotionType need_motion = scene->need_motion();
@ -1670,7 +1951,7 @@ void GeometryManager::device_update(Device *device,
return;
}
{
if (need_update_scene_bvh) {
scoped_callback_timer timer([scene](double time) {
if (scene->update_stats) {
scene->update_stats->geometry.times.add_entry({"device_update (build scene BVH)", time});
@ -1695,8 +1976,6 @@ void GeometryManager::device_update(Device *device,
}
}
need_update = false;
if (true_displacement_used) {
/* Re-tag flags for update, so they're re-evaluated
* for meshes with correct bounding boxes.
@ -1706,33 +1985,71 @@ void GeometryManager::device_update(Device *device,
*/
scene->object_manager->need_flags_update = old_need_object_flags_update;
}
/* unset flags */
foreach (Geometry *geom, scene->geometry) {
geom->clear_modified();
geom->attributes.clear_modified();
if (geom->is_mesh()) {
Mesh *mesh = static_cast<Mesh *>(geom);
mesh->subd_attributes.clear_modified();
}
}
update_flags = UPDATE_NONE;
dscene->bvh_nodes.clear_modified();
dscene->bvh_leaf_nodes.clear_modified();
dscene->object_node.clear_modified();
dscene->prim_tri_verts.clear_modified();
dscene->prim_tri_index.clear_modified();
dscene->prim_type.clear_modified();
dscene->prim_visibility.clear_modified();
dscene->prim_index.clear_modified();
dscene->prim_object.clear_modified();
dscene->prim_time.clear_modified();
dscene->tri_shader.clear_modified();
dscene->tri_vindex.clear_modified();
dscene->tri_patch.clear_modified();
dscene->tri_vnormal.clear_modified();
dscene->tri_patch_uv.clear_modified();
dscene->curves.clear_modified();
dscene->curve_keys.clear_modified();
dscene->patches.clear_modified();
dscene->attributes_map.clear_modified();
dscene->attributes_float.clear_modified();
dscene->attributes_float2.clear_modified();
dscene->attributes_float3.clear_modified();
dscene->attributes_uchar4.clear_modified();
}
void GeometryManager::device_free(Device *device, DeviceScene *dscene)
void GeometryManager::device_free(Device *device, DeviceScene *dscene, bool force_free)
{
dscene->bvh_nodes.free();
dscene->bvh_leaf_nodes.free();
dscene->object_node.free();
dscene->prim_tri_verts.free();
dscene->prim_tri_index.free();
dscene->prim_type.free();
dscene->prim_visibility.free();
dscene->prim_index.free();
dscene->prim_object.free();
dscene->prim_time.free();
dscene->tri_shader.free();
dscene->tri_vnormal.free();
dscene->tri_vindex.free();
dscene->tri_patch.free();
dscene->tri_patch_uv.free();
dscene->curves.free();
dscene->curve_keys.free();
dscene->patches.free();
dscene->attributes_map.free();
dscene->attributes_float.free();
dscene->attributes_float2.free();
dscene->attributes_float3.free();
dscene->attributes_uchar4.free();
dscene->bvh_nodes.free_if_need_realloc(force_free);
dscene->bvh_leaf_nodes.free_if_need_realloc(force_free);
dscene->object_node.free_if_need_realloc(force_free);
dscene->prim_tri_verts.free_if_need_realloc(force_free);
dscene->prim_tri_index.free_if_need_realloc(force_free);
dscene->prim_type.free_if_need_realloc(force_free);
dscene->prim_visibility.free_if_need_realloc(force_free);
dscene->prim_index.free_if_need_realloc(force_free);
dscene->prim_object.free_if_need_realloc(force_free);
dscene->prim_time.free_if_need_realloc(force_free);
dscene->tri_shader.free_if_need_realloc(force_free);
dscene->tri_vnormal.free_if_need_realloc(force_free);
dscene->tri_vindex.free_if_need_realloc(force_free);
dscene->tri_patch.free_if_need_realloc(force_free);
dscene->tri_patch_uv.free_if_need_realloc(force_free);
dscene->curves.free_if_need_realloc(force_free);
dscene->curve_keys.free_if_need_realloc(force_free);
dscene->patches.free_if_need_realloc(force_free);
dscene->attributes_map.free_if_need_realloc(force_free);
dscene->attributes_float.free_if_need_realloc(force_free);
dscene->attributes_float2.free_if_need_realloc(force_free);
dscene->attributes_float3.free_if_need_realloc(force_free);
dscene->attributes_uchar4.free_if_need_realloc(force_free);
/* Signal for shaders like displacement not to do ray tracing. */
dscene->data.bvh.bvh_layout = BVH_LAYOUT_NONE;
@ -1750,10 +2067,19 @@ void GeometryManager::device_free(Device *device, DeviceScene *dscene)
#endif
}
void GeometryManager::tag_update(Scene *scene)
void GeometryManager::tag_update(Scene *scene, uint32_t flag)
{
need_update = true;
scene->object_manager->need_update = true;
update_flags |= flag;
/* do not tag the object manager for an update if it is the one who tagged us */
if ((flag & OBJECT_MANAGER) == 0) {
scene->object_manager->tag_update(scene, ObjectManager::GEOMETRY_MANAGER);
}
}
bool GeometryManager::need_update() const
{
return update_flags != UPDATE_NONE;
}
void GeometryManager::collect_statistics(const Scene *scene, RenderStats *stats)

View File

@ -125,7 +125,7 @@ class Geometry : public Node {
int n,
int total);
virtual void pack_primitives(PackedBVH &pack, int object, uint visibility) = 0;
virtual void pack_primitives(PackedBVH *pack, int object, uint visibility, bool pack_all) = 0;
/* Check whether the geometry should have own BVH built separately. Briefly,
* own BVH is needed for geometry, if:
@ -155,6 +155,11 @@ class Geometry : public Node {
return geometry_type == HAIR;
}
bool is_volume() const
{
return geometry_type == VOLUME;
}
/* Updates */
void tag_update(Scene *scene, bool rebuild);
@ -164,9 +169,32 @@ class Geometry : public Node {
/* Geometry Manager */
class GeometryManager {
uint32_t update_flags;
public:
enum : uint32_t {
UV_PASS_NEEDED = (1 << 0),
MOTION_PASS_NEEDED = (1 << 1),
GEOMETRY_MODIFIED = (1 << 2),
OBJECT_MANAGER = (1 << 3),
MESH_ADDED = (1 << 4),
MESH_REMOVED = (1 << 5),
HAIR_ADDED = (1 << 6),
HAIR_REMOVED = (1 << 7),
SHADER_ATTRIBUTE_MODIFIED = (1 << 8),
SHADER_DISPLACEMENT_MODIFIED = (1 << 9),
GEOMETRY_ADDED = MESH_ADDED | HAIR_ADDED,
GEOMETRY_REMOVED = MESH_REMOVED | HAIR_REMOVED,
/* tag everything in the manager for an update */
UPDATE_ALL = ~0u,
UPDATE_NONE = 0u,
};
/* Update Flags */
bool need_update;
bool need_flags_update;
/* Constructor/Destructor */
@ -176,10 +204,12 @@ class GeometryManager {
/* Device Updates */
void device_update_preprocess(Device *device, Scene *scene, Progress &progress);
void device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress);
void device_free(Device *device, DeviceScene *dscene);
void device_free(Device *device, DeviceScene *dscene, bool force_free);
/* Updates */
void tag_update(Scene *scene);
void tag_update(Scene *scene, uint32_t flag);
bool need_update() const;
/* Statistics */
void collect_statistics(const Scene *scene, RenderStats *stats);

View File

@ -494,33 +494,38 @@ void Hair::pack_curves(Scene *scene,
}
}
void Hair::pack_primitives(PackedBVH &pack, int object, uint visibility)
void Hair::pack_primitives(PackedBVH *pack, int object, uint visibility, bool pack_all)
{
if (curve_first_key.empty())
return;
const size_t num_prims = num_segments();
pack.prim_tri_index.reserve(pack.prim_tri_index.size() + num_prims);
pack.prim_type.reserve(pack.prim_type.size() + num_prims);
pack.prim_visibility.reserve(pack.prim_visibility.size() + num_prims);
pack.prim_index.reserve(pack.prim_index.size() + num_prims);
pack.prim_object.reserve(pack.prim_object.size() + num_prims);
// 'pack.prim_time' is unused by Embree and OptiX
/* If the BVH does not have to be recreated, we can bail out. */
if (!pack_all) {
return;
}
unsigned int *prim_tri_index = &pack->prim_tri_index[optix_prim_offset];
int *prim_type = &pack->prim_type[optix_prim_offset];
unsigned int *prim_visibility = &pack->prim_visibility[optix_prim_offset];
int *prim_index = &pack->prim_index[optix_prim_offset];
int *prim_object = &pack->prim_object[optix_prim_offset];
// 'pack->prim_time' is unused by Embree and OptiX
uint type = has_motion_blur() ?
((curve_shape == CURVE_RIBBON) ? PRIMITIVE_MOTION_CURVE_RIBBON :
PRIMITIVE_MOTION_CURVE_THICK) :
((curve_shape == CURVE_RIBBON) ? PRIMITIVE_CURVE_RIBBON : PRIMITIVE_CURVE_THICK);
size_t index = 0;
for (size_t j = 0; j < num_curves(); ++j) {
Curve curve = get_curve(j);
for (size_t k = 0; k < curve.num_segments(); ++k) {
pack.prim_tri_index.push_back_reserved(-1);
pack.prim_type.push_back_reserved(PRIMITIVE_PACK_SEGMENT(type, k));
pack.prim_visibility.push_back_reserved(visibility);
for (size_t k = 0; k < curve.num_segments(); ++k, ++index) {
prim_tri_index[index] = -1;
prim_type[index] = PRIMITIVE_PACK_SEGMENT(type, k);
prim_visibility[index] = visibility;
// Each curve segment points back to its curve index
pack.prim_index.push_back_reserved(j + prim_offset);
pack.prim_object.push_back_reserved(object);
prim_index[index] = j + prim_offset;
prim_object[index] = object;
}
}
}

View File

@ -146,7 +146,7 @@ class Hair : public Geometry {
/* BVH */
void pack_curves(Scene *scene, float4 *curve_key_co, float4 *curve_data, size_t curvekey_offset);
void pack_primitives(PackedBVH &pack, int object, uint visibility) override;
void pack_primitives(PackedBVH *pack, int object, uint visibility, bool pack_all) override;
};
CCL_NAMESPACE_END

View File

@ -298,7 +298,7 @@ bool ImageLoader::is_vdb_loader() const
ImageManager::ImageManager(const DeviceInfo &info)
{
need_update = true;
need_update_ = true;
osl_texture_system = NULL;
animation_frame = 0;
@ -451,7 +451,7 @@ int ImageManager::add_image_slot(ImageLoader *loader,
images[slot] = img;
need_update = true;
need_update_ = true;
return slot;
}
@ -478,7 +478,7 @@ void ImageManager::remove_image_user(int slot)
* the reasons for this is that on shader changes we add and remove nodes
* that use them, but we do not want to reload the image all the time. */
if (image->users == 0)
need_update = true;
need_update_ = true;
}
static bool image_associate_alpha(ImageManager::Image *img)
@ -810,7 +810,7 @@ void ImageManager::device_free_image(Device *, int slot)
void ImageManager::device_update(Device *device, Scene *scene, Progress &progress)
{
if (!need_update) {
if (!need_update()) {
return;
}
@ -834,7 +834,7 @@ void ImageManager::device_update(Device *device, Scene *scene, Progress &progres
pool.wait_work();
need_update = false;
need_update_ = false;
}
void ImageManager::device_update_slot(Device *device, Scene *scene, int slot, Progress *progress)
@ -854,7 +854,7 @@ void ImageManager::device_load_builtin(Device *device, Scene *scene, Progress &p
{
/* Load only builtin images, Blender needs this to load evaluated
* scene data from depsgraph before it is freed. */
if (!need_update) {
if (!need_update()) {
return;
}
@ -896,4 +896,14 @@ void ImageManager::collect_statistics(RenderStats *stats)
}
}
void ImageManager::tag_update()
{
need_update_ = true;
}
bool ImageManager::need_update() const
{
return need_update_;
}
CCL_NAMESPACE_END

View File

@ -189,7 +189,9 @@ class ImageManager {
void collect_statistics(RenderStats *stats);
bool need_update;
void tag_update();
bool need_update() const;
struct Image {
ImageParams params;
@ -209,6 +211,7 @@ class ImageManager {
};
private:
bool need_update_;
bool has_half_images;
thread_mutex device_mutex;

View File

@ -17,9 +17,11 @@
#include "render/integrator.h"
#include "device/device.h"
#include "render/background.h"
#include "render/camera.h"
#include "render/film.h"
#include "render/jitter.h"
#include "render/light.h"
#include "render/object.h"
#include "render/scene.h"
#include "render/shader.h"
#include "render/sobol.h"
@ -113,6 +115,10 @@ void Integrator::device_update(Device *device, DeviceScene *dscene, Scene *scene
}
});
if (sampling_pattern_is_modified()) {
dscene->sample_pattern_lut.tag_realloc();
}
device_free(device, dscene);
KernelIntegrator *kintegrator = &dscene->data.integrator;
@ -242,45 +248,63 @@ void Integrator::device_update(Device *device, DeviceScene *dscene, Scene *scene
int dimensions = PRNG_BASE_NUM + max_samples * PRNG_BOUNCE_NUM;
dimensions = min(dimensions, SOBOL_MAX_DIMENSIONS);
if (sampling_pattern == SAMPLING_PATTERN_SOBOL) {
uint *directions = dscene->sample_pattern_lut.alloc(SOBOL_BITS * dimensions);
if (sampling_pattern_is_modified()) {
if (sampling_pattern == SAMPLING_PATTERN_SOBOL) {
uint *directions = dscene->sample_pattern_lut.alloc(SOBOL_BITS * dimensions);
sobol_generate_direction_vectors((uint(*)[SOBOL_BITS])directions, dimensions);
sobol_generate_direction_vectors((uint(*)[SOBOL_BITS])directions, dimensions);
dscene->sample_pattern_lut.copy_to_device();
}
else {
constexpr int sequence_size = NUM_PMJ_SAMPLES;
constexpr int num_sequences = NUM_PMJ_PATTERNS;
float2 *directions = (float2 *)dscene->sample_pattern_lut.alloc(sequence_size * num_sequences *
2);
TaskPool pool;
for (int j = 0; j < num_sequences; ++j) {
float2 *sequence = directions + j * sequence_size;
pool.push(
function_bind(&progressive_multi_jitter_02_generate_2D, sequence, sequence_size, j));
dscene->sample_pattern_lut.copy_to_device();
}
else {
constexpr int sequence_size = NUM_PMJ_SAMPLES;
constexpr int num_sequences = NUM_PMJ_PATTERNS;
float2 *directions = (float2 *)dscene->sample_pattern_lut.alloc(sequence_size *
num_sequences * 2);
TaskPool pool;
for (int j = 0; j < num_sequences; ++j) {
float2 *sequence = directions + j * sequence_size;
pool.push(
function_bind(&progressive_multi_jitter_02_generate_2D, sequence, sequence_size, j));
}
pool.wait_work();
dscene->sample_pattern_lut.copy_to_device();
}
pool.wait_work();
dscene->sample_pattern_lut.copy_to_device();
}
clear_modified();
}
void Integrator::device_free(Device *, DeviceScene *dscene)
void Integrator::device_free(Device *, DeviceScene *dscene, bool force_free)
{
dscene->sample_pattern_lut.free();
dscene->sample_pattern_lut.free_if_need_realloc(force_free);
}
void Integrator::tag_update(Scene *scene)
void Integrator::tag_update(Scene *scene, uint32_t flag)
{
foreach (Shader *shader, scene->shaders) {
if (shader->has_integrator_dependency) {
scene->shader_manager->need_update = true;
break;
if (flag & UPDATE_ALL) {
tag_modified();
}
if (flag & (AO_PASS_MODIFIED | BACKGROUND_AO_MODIFIED)) {
/* tag only the ao_bounces socket as modified so we avoid updating sample_pattern_lut
* unnecessarily */
tag_ao_bounces_modified();
}
if (filter_glossy_is_modified()) {
foreach (Shader *shader, scene->shaders) {
if (shader->has_integrator_dependency) {
scene->shader_manager->tag_update(scene, ShaderManager::INTEGRATOR_MODIFIED);
break;
}
}
}
tag_modified();
if (motion_blur_is_modified()) {
scene->object_manager->tag_update(scene, ObjectManager::MOTION_BLUR_MODIFIED);
scene->camera->tag_modified();
}
}
CCL_NAMESPACE_END

View File

@ -89,13 +89,23 @@ class Integrator : public Node {
NODE_SOCKET_API(SamplingPattern, sampling_pattern)
enum : uint32_t {
AO_PASS_MODIFIED = (1 << 0),
BACKGROUND_AO_MODIFIED = (1 << 1),
/* tag everything in the manager for an update */
UPDATE_ALL = ~0u,
UPDATE_NONE = 0u,
};
Integrator();
~Integrator();
void device_update(Device *device, DeviceScene *dscene, Scene *scene);
void device_free(Device *device, DeviceScene *dscene);
void device_free(Device *device, DeviceScene *dscene, bool force_free = false);
void tag_update(Scene *scene);
void tag_update(Scene *scene, uint32_t flag);
};
CCL_NAMESPACE_END

View File

@ -162,7 +162,9 @@ Light::Light() : Node(node_type)
void Light::tag_update(Scene *scene)
{
scene->light_manager->need_update = is_modified();
if (is_modified()) {
scene->light_manager->tag_update(scene, LightManager::LIGHT_MODIFIED);
}
}
bool Light::has_contribution(Scene *scene)
@ -183,7 +185,7 @@ bool Light::has_contribution(Scene *scene)
LightManager::LightManager()
{
need_update = true;
update_flags = UPDATE_ALL;
need_update_background = true;
use_light_visibility = false;
last_background_enabled = false;
@ -962,7 +964,7 @@ void LightManager::device_update(Device *device,
Scene *scene,
Progress &progress)
{
if (!need_update)
if (!need_update())
return;
scoped_callback_timer timer([scene](double time) {
@ -1000,7 +1002,7 @@ void LightManager::device_update(Device *device,
scene->film->set_use_light_visibility(use_light_visibility);
need_update = false;
update_flags = UPDATE_NONE;
need_update_background = false;
}
@ -1015,9 +1017,14 @@ void LightManager::device_free(Device *, DeviceScene *dscene, const bool free_ba
dscene->ies_lights.free();
}
void LightManager::tag_update(Scene * /*scene*/)
void LightManager::tag_update(Scene * /*scene*/, uint32_t flag)
{
need_update = true;
update_flags |= flag;
}
bool LightManager::need_update() const
{
return update_flags != UPDATE_NONE;
}
int LightManager::add_ies_from_file(const string &filename)
@ -1063,7 +1070,7 @@ int LightManager::add_ies(const string &content)
ies_slots[slot]->users = 1;
ies_slots[slot]->hash = hash;
need_update = true;
update_flags = UPDATE_ALL;
need_update_background = true;
return slot;
@ -1082,8 +1089,10 @@ void LightManager::remove_ies(int slot)
ies_slots[slot]->users--;
/* If the slot has no more users, update the device to remove it. */
need_update |= (ies_slots[slot]->users == 0);
need_update_background |= need_update;
if (ies_slots[slot]->users == 0) {
update_flags |= UPDATE_ALL;
need_update_background = true;
}
}
void LightManager::device_update_ies(DeviceScene *dscene)

View File

@ -91,8 +91,23 @@ class Light : public Node {
class LightManager {
public:
enum : uint32_t {
MESH_NEED_REBUILD = (1 << 0),
EMISSIVE_MESH_MODIFIED = (1 << 1),
LIGHT_MODIFIED = (1 << 2),
LIGHT_ADDED = (1 << 3),
LIGHT_REMOVED = (1 << 4),
OBJECT_MANAGER = (1 << 5),
SHADER_COMPILED = (1 << 6),
SHADER_MODIFIED = (1 << 7),
/* tag everything in the manager for an update */
UPDATE_ALL = ~0u,
UPDATE_NONE = 0u,
};
bool use_light_visibility;
bool need_update;
/* Need to update background (including multiple importance map) */
bool need_update_background;
@ -108,7 +123,9 @@ class LightManager {
void device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress);
void device_free(Device *device, DeviceScene *dscene, const bool free_background = true);
void tag_update(Scene *scene);
void tag_update(Scene *scene, uint32_t flag);
bool need_update() const;
/* Check whether there is a background light. */
bool has_background_light(Scene *scene);
@ -145,6 +162,8 @@ class LightManager {
bool last_background_enabled;
int last_background_resolution;
uint32_t update_flags;
};
CCL_NAMESPACE_END

View File

@ -805,34 +805,42 @@ void Mesh::pack_patches(uint *patch_data, uint vert_offset, uint face_offset, ui
}
}
void Mesh::pack_primitives(PackedBVH &pack, int object, uint visibility)
void Mesh::pack_primitives(ccl::PackedBVH *pack, int object, uint visibility, bool pack_all)
{
if (triangles.empty())
return;
const size_t num_prims = num_triangles();
pack.prim_tri_index.reserve(pack.prim_tri_index.size() + num_prims);
pack.prim_tri_verts.reserve(pack.prim_tri_verts.size() + num_prims * 3);
pack.prim_type.reserve(pack.prim_type.size() + num_prims);
pack.prim_visibility.reserve(pack.prim_visibility.size() + num_prims);
pack.prim_index.reserve(pack.prim_index.size() + num_prims);
pack.prim_object.reserve(pack.prim_object.size() + num_prims);
// 'pack.prim_time' is unused by Embree and OptiX
/* Use prim_offset for indexing as it is computed per geometry type, and prim_tri_verts does not
* contain data for Hair geometries. */
float4 *prim_tri_verts = &pack->prim_tri_verts[prim_offset * 3];
// 'pack->prim_time' is unused by Embree and OptiX
uint type = has_motion_blur() ? PRIMITIVE_MOTION_TRIANGLE : PRIMITIVE_TRIANGLE;
if (pack_all) {
/* Use optix_prim_offset for indexing as those arrays also contain data for Hair geometries. */
unsigned int *prim_tri_index = &pack->prim_tri_index[optix_prim_offset];
int *prim_type = &pack->prim_type[optix_prim_offset];
unsigned int *prim_visibility = &pack->prim_visibility[optix_prim_offset];
int *prim_index = &pack->prim_index[optix_prim_offset];
int *prim_object = &pack->prim_object[optix_prim_offset];
for (size_t k = 0; k < num_prims; ++k) {
prim_tri_index[k] = (prim_offset + k) * 3;
prim_type[k] = type;
prim_index[k] = prim_offset + k;
prim_object[k] = object;
prim_visibility[k] = visibility;
}
}
for (size_t k = 0; k < num_prims; ++k) {
pack.prim_tri_index.push_back_reserved(pack.prim_tri_verts.size());
const Mesh::Triangle t = get_triangle(k);
pack.prim_tri_verts.push_back_reserved(float3_to_float4(verts[t.v[0]]));
pack.prim_tri_verts.push_back_reserved(float3_to_float4(verts[t.v[1]]));
pack.prim_tri_verts.push_back_reserved(float3_to_float4(verts[t.v[2]]));
pack.prim_type.push_back_reserved(type);
pack.prim_visibility.push_back_reserved(visibility);
pack.prim_index.push_back_reserved(k + prim_offset);
pack.prim_object.push_back_reserved(object);
prim_tri_verts[k * 3] = float3_to_float4(verts[t.v[0]]);
prim_tri_verts[k * 3 + 1] = float3_to_float4(verts[t.v[1]]);
prim_tri_verts[k * 3 + 2] = float3_to_float4(verts[t.v[2]]);
}
}

View File

@ -232,7 +232,7 @@ class Mesh : public Geometry {
size_t tri_offset);
void pack_patches(uint *patch_data, uint vert_offset, uint face_offset, uint corner_offset);
void pack_primitives(PackedBVH &pack, int object, uint visibility) override;
void pack_primitives(PackedBVH *pack, int object, uint visibility, bool pack_all) override;
void tessellate(DiagSplit *split);

View File

@ -153,6 +153,10 @@ void Object::update_motion()
void Object::compute_bounds(bool motion_blur)
{
if (!is_modified() && !geometry->is_modified()) {
return;
}
BoundBox mbounds = geometry->bounds;
if (motion_blur && use_motion()) {
@ -205,20 +209,39 @@ void Object::apply_transform(bool apply_to_motion)
void Object::tag_update(Scene *scene)
{
uint32_t flag = ObjectManager::UPDATE_NONE;
if (is_modified()) {
flag |= ObjectManager::OBJECT_MODIFIED;
if (use_holdout_is_modified()) {
flag |= ObjectManager::HOLDOUT_MODIFIED;
}
}
if (geometry) {
if (geometry->transform_applied)
geometry->tag_modified();
if (tfm_is_modified()) {
/* tag the geometry as modified so the BVH is updated, but do not tag everything as modified
*/
if (geometry->is_mesh() || geometry->is_volume()) {
Mesh *mesh = static_cast<Mesh *>(geometry);
mesh->tag_verts_modified();
}
else if (geometry->is_hair()) {
Hair *hair = static_cast<Hair *>(geometry);
hair->tag_curve_keys_modified();
}
}
foreach (Node *node, geometry->get_used_shaders()) {
Shader *shader = static_cast<Shader *>(node);
if (shader->get_use_mis() && shader->has_surface_emission)
scene->light_manager->need_update = true;
scene->light_manager->tag_update(scene, LightManager::EMISSIVE_MESH_MODIFIED);
}
}
scene->camera->need_flags_update = true;
scene->geometry_manager->need_update = true;
scene->object_manager->need_update = true;
scene->object_manager->tag_update(scene, flag);
}
bool Object::use_motion() const
@ -361,7 +384,7 @@ int Object::get_device_index() const
ObjectManager::ObjectManager()
{
need_update = true;
update_flags = UPDATE_ALL;
need_flags_update = true;
}
@ -382,7 +405,9 @@ static float object_volume_density(const Transform &tfm, Geometry *geom)
return 1.0f;
}
void ObjectManager::device_update_object_transform(UpdateObjectTransformState *state, Object *ob)
void ObjectManager::device_update_object_transform(UpdateObjectTransformState *state,
Object *ob,
bool update_all)
{
KernelObject &kobject = state->objects[ob->index];
Transform *object_motion_pass = state->object_motion_pass;
@ -456,8 +481,11 @@ void ObjectManager::device_update_object_transform(UpdateObjectTransformState *s
kobject.motion_offset = state->motion_offset[ob->index];
/* Decompose transforms for interpolation. */
DecomposedTransform *decomp = state->object_motion + kobject.motion_offset;
transform_motion_decompose(decomp, ob->motion.data(), ob->motion.size());
if (ob->tfm_is_modified() || update_all) {
DecomposedTransform *decomp = state->object_motion + kobject.motion_offset;
transform_motion_decompose(decomp, ob->motion.data(), ob->motion.size());
}
flag |= SD_OBJECT_MOTION;
state->have_motion = true;
}
@ -480,10 +508,14 @@ void ObjectManager::device_update_object_transform(UpdateObjectTransformState *s
0;
kobject.patch_map_offset = 0;
kobject.attribute_map_offset = 0;
uint32_t hash_name = util_murmur_hash3(ob->name.c_str(), ob->name.length(), 0);
uint32_t hash_asset = util_murmur_hash3(ob->asset_name.c_str(), ob->asset_name.length(), 0);
kobject.cryptomatte_object = util_hash_to_float(hash_name);
kobject.cryptomatte_asset = util_hash_to_float(hash_asset);
if (ob->asset_name_is_modified() || update_all) {
uint32_t hash_name = util_murmur_hash3(ob->name.c_str(), ob->name.length(), 0);
uint32_t hash_asset = util_murmur_hash3(ob->asset_name.c_str(), ob->asset_name.length(), 0);
kobject.cryptomatte_object = util_hash_to_float(hash_name);
kobject.cryptomatte_asset = util_hash_to_float(hash_asset);
}
kobject.shadow_terminator_offset = 1.0f / (1.0f - 0.5f * ob->shadow_terminator_offset);
/* Object flag. */
@ -544,6 +576,9 @@ void ObjectManager::device_update_transforms(DeviceScene *dscene, Scene *scene,
numparticles += psys->particles.size();
}
/* as all the arrays are the same size, checking only dscene.objects is sufficient */
const bool update_all = dscene->objects.need_realloc();
/* Parallel object update, with grain size to avoid too much threading overhead
* for individual objects. */
static const int OBJECTS_PER_TASK = 32;
@ -551,7 +586,7 @@ void ObjectManager::device_update_transforms(DeviceScene *dscene, Scene *scene,
[&](const blocked_range<size_t> &r) {
for (size_t i = r.begin(); i != r.end(); i++) {
Object *ob = state.scene->objects[i];
device_update_object_transform(&state, ob);
device_update_object_transform(&state, ob, update_all);
}
});
@ -559,7 +594,7 @@ void ObjectManager::device_update_transforms(DeviceScene *dscene, Scene *scene,
return;
}
dscene->objects.copy_to_device();
dscene->objects.copy_to_device_if_modified();
if (state.need_motion == Scene::MOTION_PASS) {
dscene->object_motion_pass.copy_to_device();
}
@ -569,6 +604,10 @@ void ObjectManager::device_update_transforms(DeviceScene *dscene, Scene *scene,
dscene->data.bvh.have_motion = state.have_motion;
dscene->data.bvh.have_curves = state.have_curves;
dscene->objects.clear_modified();
dscene->object_motion_pass.clear_modified();
dscene->object_motion.clear_modified();
}
void ObjectManager::device_update(Device *device,
@ -576,12 +615,28 @@ void ObjectManager::device_update(Device *device,
Scene *scene,
Progress &progress)
{
if (!need_update)
if (!need_update())
return;
if (update_flags & (OBJECT_ADDED | OBJECT_REMOVED)) {
dscene->objects.tag_realloc();
dscene->object_motion_pass.tag_realloc();
dscene->object_motion.tag_realloc();
dscene->object_flag.tag_realloc();
dscene->object_volume_step.tag_realloc();
}
if (update_flags & HOLDOUT_MODIFIED) {
dscene->object_flag.tag_modified();
}
if (update_flags & PARTICLE_MODIFIED) {
dscene->objects.tag_modified();
}
VLOG(1) << "Total " << scene->objects.size() << " objects.";
device_free(device, dscene);
device_free(device, dscene, false);
if (scene->objects.size() == 0)
return;
@ -597,6 +652,16 @@ void ObjectManager::device_update(Device *device,
int index = 0;
foreach (Object *object, scene->objects) {
object->index = index++;
/* this is a bit too broad, however a bigger refactor might be needed to properly separate
* update each type of data (transform, flags, etc.) */
if (object->is_modified()) {
dscene->objects.tag_modified();
dscene->object_motion_pass.tag_modified();
dscene->object_motion.tag_modified();
dscene->object_flag.tag_modified();
dscene->object_volume_step.tag_modified();
}
}
}
@ -638,7 +703,7 @@ void ObjectManager::device_update(Device *device,
void ObjectManager::device_update_flags(
Device *, DeviceScene *dscene, Scene *scene, Progress & /*progress*/, bool bounds_valid)
{
if (!need_update && !need_flags_update)
if (!need_update() && !need_flags_update)
return;
scoped_callback_timer timer([scene](double time) {
@ -647,7 +712,7 @@ void ObjectManager::device_update_flags(
}
});
need_update = false;
update_flags = UPDATE_NONE;
need_flags_update = false;
if (scene->objects.size() == 0)
@ -717,6 +782,9 @@ void ObjectManager::device_update_flags(
/* Copy object flag. */
dscene->object_flag.copy_to_device();
dscene->object_volume_step.copy_to_device();
dscene->object_flag.clear_modified();
dscene->object_volume_step.clear_modified();
}
void ObjectManager::device_update_mesh_offsets(Device *, DeviceScene *dscene, Scene *scene)
@ -764,13 +832,13 @@ void ObjectManager::device_update_mesh_offsets(Device *, DeviceScene *dscene, Sc
}
}
void ObjectManager::device_free(Device *, DeviceScene *dscene)
void ObjectManager::device_free(Device *, DeviceScene *dscene, bool force_free)
{
dscene->objects.free();
dscene->object_motion_pass.free();
dscene->object_motion.free();
dscene->object_flag.free();
dscene->object_volume_step.free();
dscene->objects.free_if_need_realloc(force_free);
dscene->object_motion_pass.free_if_need_realloc(force_free);
dscene->object_motion.free_if_need_realloc(force_free);
dscene->object_flag.free_if_need_realloc(force_free);
dscene->object_volume_step.free_if_need_realloc(force_free);
}
void ObjectManager::apply_static_transforms(DeviceScene *dscene, Scene *scene, Progress &progress)
@ -841,11 +909,21 @@ void ObjectManager::apply_static_transforms(DeviceScene *dscene, Scene *scene, P
}
}
void ObjectManager::tag_update(Scene *scene)
void ObjectManager::tag_update(Scene *scene, uint32_t flag)
{
need_update = true;
scene->geometry_manager->need_update = true;
scene->light_manager->need_update = true;
update_flags |= flag;
/* avoid infinite loops if the geometry manager tagged us for an update */
if ((flag & GEOMETRY_MANAGER) == 0) {
scene->geometry_manager->tag_update(scene, GeometryManager::OBJECT_MANAGER);
}
scene->light_manager->tag_update(scene, LightManager::OBJECT_MANAGER);
}
bool ObjectManager::need_update() const
{
return update_flags != UPDATE_NONE;
}
string ObjectManager::get_cryptomatte_objects(Scene *scene)

View File

@ -122,8 +122,24 @@ class Object : public Node {
/* Object Manager */
class ObjectManager {
uint32_t update_flags;
public:
bool need_update;
enum : uint32_t {
PARTICLE_MODIFIED = (1 << 0),
GEOMETRY_MANAGER = (1 << 1),
MOTION_BLUR_MODIFIED = (1 << 2),
OBJECT_ADDED = (1 << 3),
OBJECT_REMOVED = (1 << 4),
OBJECT_MODIFIED = (1 << 5),
HOLDOUT_MODIFIED = (1 << 6),
/* tag everything in the manager for an update */
UPDATE_ALL = ~0u,
UPDATE_NONE = 0u,
};
bool need_flags_update;
ObjectManager();
@ -139,9 +155,11 @@ class ObjectManager {
bool bounds_valid = true);
void device_update_mesh_offsets(Device *device, DeviceScene *dscene, Scene *scene);
void device_free(Device *device, DeviceScene *dscene);
void device_free(Device *device, DeviceScene *dscene, bool force_free);
void tag_update(Scene *scene);
void tag_update(Scene *scene, uint32_t flag);
bool need_update() const;
void apply_static_transforms(DeviceScene *dscene, Scene *scene, Progress &progress);
@ -149,7 +167,9 @@ class ObjectManager {
string get_cryptomatte_assets(Scene *scene);
protected:
void device_update_object_transform(UpdateObjectTransformState *state, Object *ob);
void device_update_object_transform(UpdateObjectTransformState *state,
Object *ob,
bool update_all);
void device_update_object_transform_task(UpdateObjectTransformState *state);
bool device_update_object_transform_pop_work(UpdateObjectTransformState *state,
int *start_index,

View File

@ -96,7 +96,7 @@ void OSLShaderManager::device_update(Device *device,
Scene *scene,
Progress &progress)
{
if (!need_update)
if (!need_update())
return;
scoped_callback_timer timer([scene](double time) {
@ -132,7 +132,7 @@ void OSLShaderManager::device_update(Device *device,
compiler.compile(og, shader);
if (shader->get_use_mis() && shader->has_surface_emission)
scene->light_manager->need_update = true;
scene->light_manager->tag_update(scene, LightManager::SHADER_COMPILED);
}
/* setup shader engine */
@ -147,7 +147,7 @@ void OSLShaderManager::device_update(Device *device,
foreach (Shader *shader, scene->shaders)
shader->clear_modified();
need_update = false;
update_flags = UPDATE_NONE;
/* add special builtin texture types */
services->textures.insert(ustring("@ao"), new OSLTextureHandle(OSLTextureHandle::AO));

View File

@ -46,14 +46,14 @@ ParticleSystem::~ParticleSystem()
void ParticleSystem::tag_update(Scene *scene)
{
scene->particle_system_manager->need_update = true;
scene->particle_system_manager->tag_update(scene);
}
/* Particle System Manager */
ParticleSystemManager::ParticleSystemManager()
{
need_update = true;
need_update_ = true;
}
ParticleSystemManager::~ParticleSystemManager()
@ -109,7 +109,7 @@ void ParticleSystemManager::device_update(Device *device,
Scene *scene,
Progress &progress)
{
if (!need_update)
if (!need_update())
return;
scoped_callback_timer timer([scene](double time) {
@ -128,7 +128,7 @@ void ParticleSystemManager::device_update(Device *device,
if (progress.get_cancel())
return;
need_update = false;
need_update_ = false;
}
void ParticleSystemManager::device_free(Device *, DeviceScene *dscene)
@ -138,7 +138,12 @@ void ParticleSystemManager::device_free(Device *, DeviceScene *dscene)
void ParticleSystemManager::tag_update(Scene * /*scene*/)
{
need_update = true;
need_update_ = true;
}
bool ParticleSystemManager::need_update() const
{
return need_update_;
}
CCL_NAMESPACE_END

View File

@ -57,9 +57,9 @@ class ParticleSystem : public Node {
/* ParticleSystem Manager */
class ParticleSystemManager {
public:
bool need_update;
bool need_update_;
public:
ParticleSystemManager();
~ParticleSystemManager();
@ -71,6 +71,8 @@ class ParticleSystemManager {
void device_free(Device *device, DeviceScene *dscene);
void tag_update(Scene *scene);
bool need_update() const;
};
CCL_NAMESPACE_END

View File

@ -161,10 +161,10 @@ void Scene::free_memory(bool final)
camera->device_free(device, &dscene, this);
film->device_free(device, &dscene, this);
background->device_free(device, &dscene);
integrator->device_free(device, &dscene);
integrator->device_free(device, &dscene, true);
object_manager->device_free(device, &dscene);
geometry_manager->device_free(device, &dscene);
object_manager->device_free(device, &dscene, true);
geometry_manager->device_free(device, &dscene, true);
shader_manager->device_free(device, &dscene, this);
light_manager->device_free(device, &dscene);
@ -386,10 +386,11 @@ bool Scene::need_update()
bool Scene::need_data_update()
{
return (background->is_modified() || image_manager->need_update || object_manager->need_update ||
geometry_manager->need_update || light_manager->need_update ||
lookup_tables->need_update || integrator->is_modified() || shader_manager->need_update ||
particle_system_manager->need_update || bake_manager->need_update ||
return (background->is_modified() || image_manager->need_update() ||
object_manager->need_update() || geometry_manager->need_update() ||
light_manager->need_update() || lookup_tables->need_update() ||
integrator->is_modified() || shader_manager->need_update() ||
particle_system_manager->need_update() || bake_manager->need_update() ||
film->is_modified());
}
@ -407,11 +408,13 @@ void Scene::reset()
camera->tag_modified();
dicing_camera->tag_modified();
film->tag_modified();
background->tag_modified();
background->tag_update(this);
integrator->tag_update(this);
object_manager->tag_update(this);
geometry_manager->tag_update(this);
light_manager->tag_update(this);
integrator->tag_update(this, Integrator::UPDATE_ALL);
object_manager->tag_update(this, ObjectManager::UPDATE_ALL);
geometry_manager->tag_update(this, GeometryManager::UPDATE_ALL);
light_manager->tag_update(this, LightManager::UPDATE_ALL);
particle_system_manager->tag_update(this);
}
@ -596,7 +599,7 @@ template<> Light *Scene::create_node<Light>()
Light *node = new Light();
node->set_owner(this);
lights.push_back(node);
light_manager->tag_update(this);
light_manager->tag_update(this, LightManager::LIGHT_ADDED);
return node;
}
@ -605,7 +608,7 @@ template<> Mesh *Scene::create_node<Mesh>()
Mesh *node = new Mesh();
node->set_owner(this);
geometry.push_back(node);
geometry_manager->tag_update(this);
geometry_manager->tag_update(this, GeometryManager::MESH_ADDED);
return node;
}
@ -614,7 +617,7 @@ template<> Hair *Scene::create_node<Hair>()
Hair *node = new Hair();
node->set_owner(this);
geometry.push_back(node);
geometry_manager->tag_update(this);
geometry_manager->tag_update(this, GeometryManager::HAIR_ADDED);
return node;
}
@ -623,7 +626,7 @@ template<> Volume *Scene::create_node<Volume>()
Volume *node = new Volume();
node->set_owner(this);
geometry.push_back(node);
geometry_manager->tag_update(this);
geometry_manager->tag_update(this, GeometryManager::MESH_ADDED);
return node;
}
@ -632,7 +635,7 @@ template<> Object *Scene::create_node<Object>()
Object *node = new Object();
node->set_owner(this);
objects.push_back(node);
object_manager->tag_update(this);
object_manager->tag_update(this, ObjectManager::OBJECT_ADDED);
return node;
}
@ -650,7 +653,7 @@ template<> Shader *Scene::create_node<Shader>()
Shader *node = new Shader();
node->set_owner(this);
shaders.push_back(node);
shader_manager->need_update = true;
shader_manager->tag_update(this, ShaderManager::SHADER_ADDED);
return node;
}
@ -664,43 +667,52 @@ template<typename T> void delete_node_from_array(vector<T> &nodes, T node)
}
nodes.resize(nodes.size() - 1);
delete node;
}
template<> void Scene::delete_node_impl(Light *node)
{
delete_node_from_array(lights, node);
light_manager->tag_update(this);
light_manager->tag_update(this, LightManager::LIGHT_REMOVED);
}
template<> void Scene::delete_node_impl(Mesh *node)
{
delete_node_from_array(geometry, static_cast<Geometry *>(node));
geometry_manager->tag_update(this);
geometry_manager->tag_update(this, GeometryManager::MESH_REMOVED);
}
template<> void Scene::delete_node_impl(Hair *node)
{
delete_node_from_array(geometry, static_cast<Geometry *>(node));
geometry_manager->tag_update(this);
geometry_manager->tag_update(this, GeometryManager::HAIR_REMOVED);
}
template<> void Scene::delete_node_impl(Volume *node)
{
delete_node_from_array(geometry, static_cast<Geometry *>(node));
geometry_manager->tag_update(this);
geometry_manager->tag_update(this, GeometryManager::MESH_REMOVED);
}
template<> void Scene::delete_node_impl(Geometry *node)
{
uint flag;
if (node->is_hair()) {
flag = GeometryManager::HAIR_REMOVED;
}
else {
flag = GeometryManager::MESH_REMOVED;
}
delete_node_from_array(geometry, node);
geometry_manager->tag_update(this);
geometry_manager->tag_update(this, flag);
}
template<> void Scene::delete_node_impl(Object *node)
{
delete_node_from_array(objects, node);
object_manager->tag_update(this);
object_manager->tag_update(this, ObjectManager::OBJECT_REMOVED);
}
template<> void Scene::delete_node_impl(ParticleSystem *node)
@ -742,19 +754,19 @@ static void remove_nodes_in_set(const set<T *> &nodes_set,
template<> void Scene::delete_nodes(const set<Light *> &nodes, const NodeOwner *owner)
{
remove_nodes_in_set(nodes, lights, owner);
light_manager->tag_update(this);
light_manager->tag_update(this, LightManager::LIGHT_REMOVED);
}
template<> void Scene::delete_nodes(const set<Geometry *> &nodes, const NodeOwner *owner)
{
remove_nodes_in_set(nodes, geometry, owner);
geometry_manager->tag_update(this);
geometry_manager->tag_update(this, GeometryManager::GEOMETRY_REMOVED);
}
template<> void Scene::delete_nodes(const set<Object *> &nodes, const NodeOwner *owner)
{
remove_nodes_in_set(nodes, objects, owner);
object_manager->tag_update(this);
object_manager->tag_update(this, ObjectManager::OBJECT_REMOVED);
}
template<> void Scene::delete_nodes(const set<ParticleSystem *> &nodes, const NodeOwner *owner)

View File

@ -1038,13 +1038,7 @@ bool Session::update_scene()
BakeManager *bake_manager = scene->bake_manager;
if (integrator->get_sampling_pattern() != SAMPLING_PATTERN_SOBOL || bake_manager->get_baking()) {
int aa_samples = tile_manager.num_samples;
integrator->set_aa_samples(aa_samples);
if (integrator->is_modified()) {
integrator->tag_update(scene);
}
integrator->set_aa_samples(tile_manager.num_samples);
}
bool kernel_switch_needed = false;

View File

@ -218,7 +218,9 @@ Shader::Shader() : Node(node_type)
id = -1;
used = false;
need_update_geometry = true;
need_update_uvs = true;
need_update_attribute = true;
need_update_displacement = true;
}
Shader::~Shader()
@ -291,7 +293,7 @@ void Shader::set_graph(ShaderGraph *graph_)
const char *new_hash = (graph_) ? graph_->displacement_hash.c_str() : "";
if (strcmp(old_hash, new_hash) != 0) {
need_update_geometry = true;
need_update_displacement = true;
}
}
@ -308,13 +310,14 @@ void Shader::tag_update(Scene *scene)
{
/* update tag */
tag_modified();
scene->shader_manager->need_update = true;
scene->shader_manager->tag_update(scene, ShaderManager::SHADER_MODIFIED);
/* if the shader previously was emissive, update light distribution,
* if the new shader is emissive, a light manager update tag will be
* done in the shader manager device update. */
if (use_mis && has_surface_emission)
scene->light_manager->need_update = true;
scene->light_manager->tag_update(scene, LightManager::SHADER_MODIFIED);
/* Special handle of background MIS light for now: for some reason it
* has use_mis set to false. We are quite close to release now, so
@ -323,7 +326,7 @@ void Shader::tag_update(Scene *scene)
if (this == scene->background->get_shader(scene)) {
scene->light_manager->need_update_background = true;
if (scene->light_manager->has_background_light(scene)) {
scene->light_manager->need_update = true;
scene->light_manager->tag_update(scene, LightManager::SHADER_MODIFIED);
}
}
@ -352,17 +355,17 @@ void Shader::tag_update(Scene *scene)
attributes.add(ATTR_STD_POSITION_UNDISPLACED);
}
if (displacement_method_is_modified()) {
need_update_geometry = true;
scene->geometry_manager->need_update = true;
need_update_displacement = true;
scene->geometry_manager->tag_update(scene, GeometryManager::SHADER_DISPLACEMENT_MODIFIED);
scene->object_manager->need_flags_update = true;
}
}
/* compare if the attributes changed, mesh manager will check
* need_update_geometry, update the relevant meshes and clear it. */
* need_update_attribute, update the relevant meshes and clear it. */
if (attributes.modified(prev_attributes)) {
need_update_geometry = true;
scene->geometry_manager->need_update = true;
need_update_attribute = true;
scene->geometry_manager->tag_update(scene, GeometryManager::SHADER_ATTRIBUTE_MODIFIED);
}
if (has_volume != prev_has_volume || volume_step_rate != prev_volume_step_rate) {
@ -378,15 +381,20 @@ void Shader::tag_used(Scene *scene)
* recompiled because it was skipped for compilation before */
if (!used) {
tag_modified();
scene->shader_manager->need_update = true;
scene->shader_manager->tag_update(scene, ShaderManager::SHADER_MODIFIED);
}
}
bool Shader::need_update_geometry() const
{
return need_update_uvs || need_update_attribute || need_update_displacement;
}
/* Shader Manager */
ShaderManager::ShaderManager()
{
need_update = true;
update_flags = UPDATE_ALL;
beckmann_table_offset = TABLE_OFFSET_INVALID;
xyz_to_r = make_float3(3.2404542f, -1.5371385f, -0.4985314f);
@ -484,7 +492,7 @@ int ShaderManager::get_shader_id(Shader *shader, bool smooth)
void ShaderManager::update_shaders_used(Scene *scene)
{
if (!need_update) {
if (!need_update()) {
return;
}
@ -793,4 +801,15 @@ string ShaderManager::get_cryptomatte_materials(Scene *scene)
return manifest;
}
void ShaderManager::tag_update(Scene * /*scene*/, uint32_t /*flag*/)
{
/* update everything for now */
update_flags = ShaderManager::UPDATE_ALL;
}
bool ShaderManager::need_update() const
{
return update_flags != UPDATE_NONE;
}
CCL_NAMESPACE_END

View File

@ -100,7 +100,9 @@ class Shader : public Node {
float prev_volume_step_rate;
/* synchronization */
bool need_update_geometry;
bool need_update_uvs;
bool need_update_attribute;
bool need_update_displacement;
/* If the shader has only volume components, the surface is assumed to
* be transparent.
@ -152,6 +154,8 @@ class Shader : public Node {
void set_graph(ShaderGraph *graph);
void tag_update(Scene *scene);
void tag_used(Scene *scene);
bool need_update_geometry() const;
};
/* Shader Manager virtual base class
@ -161,7 +165,16 @@ class Shader : public Node {
class ShaderManager {
public:
bool need_update;
enum : uint32_t {
SHADER_ADDED = (1 << 0),
SHADER_MODIFIED = (1 << 2),
INTEGRATOR_MODIFIED = (1 << 3),
/* tag everything in the manager for an update */
UPDATE_ALL = ~0u,
UPDATE_NONE = 0u,
};
static ShaderManager *create(int shadingsystem);
virtual ~ShaderManager();
@ -204,9 +217,15 @@ class ShaderManager {
string get_cryptomatte_materials(Scene *scene);
void tag_update(Scene *scene, uint32_t flag);
bool need_update() const;
protected:
ShaderManager();
uint32_t update_flags;
typedef unordered_map<ustring, uint, ustringHash> AttributeIDMap;
AttributeIDMap unique_attribute_id;

View File

@ -74,7 +74,7 @@ void SVMShaderManager::device_update(Device *device,
Scene *scene,
Progress &progress)
{
if (!need_update)
if (!need_update())
return;
scoped_callback_timer timer([scene](double time) {
@ -125,7 +125,7 @@ void SVMShaderManager::device_update(Device *device,
shader->clear_modified();
if (shader->get_use_mis() && shader->has_surface_emission) {
scene->light_manager->need_update = true;
scene->light_manager->tag_update(scene, LightManager::SHADER_COMPILED);
}
/* Update the global jump table.
@ -159,7 +159,7 @@ void SVMShaderManager::device_update(Device *device,
device_update_common(device, dscene, scene, progress);
need_update = false;
update_flags = UPDATE_NONE;
VLOG(1) << "Shader manager updated " << num_shaders << " shaders in " << time_dt() - start_time
<< " seconds.";

View File

@ -28,7 +28,7 @@ CCL_NAMESPACE_BEGIN
LookupTables::LookupTables()
{
need_update = true;
need_update_ = true;
}
LookupTables::~LookupTables()
@ -38,7 +38,7 @@ LookupTables::~LookupTables()
void LookupTables::device_update(Device *, DeviceScene *dscene, Scene *scene)
{
if (!need_update)
if (!need_update())
return;
scoped_callback_timer timer([scene](double time) {
@ -52,7 +52,7 @@ void LookupTables::device_update(Device *, DeviceScene *dscene, Scene *scene)
if (lookup_tables.size() > 0)
dscene->lookup_table.copy_to_device();
need_update = false;
need_update_ = false;
}
void LookupTables::device_free(Device *, DeviceScene *dscene)
@ -60,6 +60,11 @@ void LookupTables::device_free(Device *, DeviceScene *dscene)
dscene->lookup_table.free();
}
bool LookupTables::need_update() const
{
return need_update_;
}
static size_t round_up_to_multiple(size_t size, size_t chunk)
{
return ((size + chunk - 1) / chunk) * chunk;
@ -69,7 +74,7 @@ size_t LookupTables::add_table(DeviceScene *dscene, vector<float> &data)
{
assert(data.size() > 0);
need_update = true;
need_update_ = true;
Table new_table;
new_table.offset = 0;
@ -107,7 +112,7 @@ void LookupTables::remove_table(size_t *offset)
return;
}
need_update = true;
need_update_ = true;
list<Table>::iterator table;

View File

@ -30,13 +30,14 @@ enum { TABLE_CHUNK_SIZE = 256 };
enum { TABLE_OFFSET_INVALID = -1 };
class LookupTables {
bool need_update_;
public:
struct Table {
size_t offset;
size_t size;
};
bool need_update;
list<Table> lookup_tables;
LookupTables();
@ -45,6 +46,8 @@ class LookupTables {
void device_update(Device *device, DeviceScene *dscene, Scene *scene);
void device_free(Device *device, DeviceScene *dscene);
bool need_update() const;
size_t add_table(DeviceScene *dscene, vector<float> &data);
void remove_table(size_t *offset);
};

View File

@ -131,6 +131,14 @@ template<typename T, size_t alignment = MIN_ALIGNMENT_CPU_DATA_TYPES> class arra
}
}
void set_data(T *ptr_, size_t datasize)
{
clear();
data_ = ptr_;
datasize_ = datasize;
capacity_ = datasize;
}
T *steal_pointer()
{
T *ptr = data_;