diff options
Diffstat (limited to 'intern')
60 files changed, 3816 insertions, 951 deletions
diff --git a/intern/cycles/blender/blender_curves.cpp b/intern/cycles/blender/blender_curves.cpp index 964241e9904..4fb5c7f57d1 100644 --- a/intern/cycles/blender/blender_curves.cpp +++ b/intern/cycles/blender/blender_curves.cpp @@ -67,11 +67,10 @@ static bool ObtainCacheParticleData( Transform tfm = get_transform(b_ob->matrix_world()); Transform itfm = transform_quick_inverse(tfm); - BL::Object::modifiers_iterator b_mod; - for (b_ob->modifiers.begin(b_mod); b_mod != b_ob->modifiers.end(); ++b_mod) { - if ((b_mod->type() == b_mod->type_PARTICLE_SYSTEM) && - (background ? b_mod->show_render() : b_mod->show_viewport())) { - BL::ParticleSystemModifier psmd((const PointerRNA)b_mod->ptr); + for (BL::Modifier &b_mod : b_ob->modifiers) { + if ((b_mod.type() == b_mod.type_PARTICLE_SYSTEM) && + (background ? b_mod.show_render() : b_mod.show_viewport())) { + BL::ParticleSystemModifier psmd((const PointerRNA)b_mod.ptr); BL::ParticleSystem b_psys((const PointerRNA)psmd.particle_system().ptr); BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr); @@ -163,11 +162,10 @@ static bool ObtainCacheParticleUV(Hair *hair, CData->curve_uv.clear(); - BL::Object::modifiers_iterator b_mod; - for (b_ob->modifiers.begin(b_mod); b_mod != b_ob->modifiers.end(); ++b_mod) { - if ((b_mod->type() == b_mod->type_PARTICLE_SYSTEM) && - (background ? b_mod->show_render() : b_mod->show_viewport())) { - BL::ParticleSystemModifier psmd((const PointerRNA)b_mod->ptr); + for (BL::Modifier &b_mod : b_ob->modifiers) { + if ((b_mod.type() == b_mod.type_PARTICLE_SYSTEM) && + (background ? b_mod.show_render() : b_mod.show_viewport())) { + BL::ParticleSystemModifier psmd((const PointerRNA)b_mod.ptr); BL::ParticleSystem b_psys((const PointerRNA)psmd.particle_system().ptr); BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr); @@ -226,11 +224,10 @@ static bool ObtainCacheParticleVcol(Hair *hair, CData->curve_vcol.clear(); - BL::Object::modifiers_iterator b_mod; - for (b_ob->modifiers.begin(b_mod); b_mod != b_ob->modifiers.end(); ++b_mod) { - if ((b_mod->type() == b_mod->type_PARTICLE_SYSTEM) && - (background ? b_mod->show_render() : b_mod->show_viewport())) { - BL::ParticleSystemModifier psmd((const PointerRNA)b_mod->ptr); + for (BL::Modifier &b_mod : b_ob->modifiers) { + if ((b_mod.type() == b_mod.type_PARTICLE_SYSTEM) && + (background ? b_mod.show_render() : b_mod.show_viewport())) { + BL::ParticleSystemModifier psmd((const PointerRNA)b_mod.ptr); BL::ParticleSystem b_psys((const PointerRNA)psmd.particle_system().ptr); BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr); @@ -510,11 +507,10 @@ static void ExportCurveSegmentsMotion(Hair *hair, ParticleCurveData *CData, int bool BlenderSync::object_has_particle_hair(BL::Object b_ob) { /* Test if the object has a particle modifier with hair. */ - BL::Object::modifiers_iterator b_mod; - for (b_ob.modifiers.begin(b_mod); b_mod != b_ob.modifiers.end(); ++b_mod) { - if ((b_mod->type() == b_mod->type_PARTICLE_SYSTEM) && - (preview ? b_mod->show_viewport() : b_mod->show_render())) { - BL::ParticleSystemModifier psmd((const PointerRNA)b_mod->ptr); + for (BL::Modifier &b_mod : b_ob.modifiers) { + if ((b_mod.type() == b_mod.type_PARTICLE_SYSTEM) && + (preview ? b_mod.show_viewport() : b_mod.show_render())) { + BL::ParticleSystemModifier psmd((const PointerRNA)b_mod.ptr); BL::ParticleSystem b_psys((const PointerRNA)psmd.particle_system().ptr); BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr); @@ -678,9 +674,7 @@ static void export_hair_curves(Scene *scene, Hair *hair, BL::Hair b_hair) /* Export curves and points. */ vector<float> points_length; - BL::Hair::curves_iterator b_curve_iter; - for (b_hair.curves.begin(b_curve_iter); b_curve_iter != b_hair.curves.end(); ++b_curve_iter) { - BL::HairCurve b_curve = *b_curve_iter; + for (BL::HairCurve &b_curve : b_hair.curves) { const int first_point_index = b_curve.first_point_index(); const int num_points = b_curve.num_points(); @@ -748,9 +742,7 @@ static void export_hair_curves_motion(Hair *hair, BL::Hair b_hair, int motion_st int num_motion_keys = 0; int curve_index = 0; - BL::Hair::curves_iterator b_curve_iter; - for (b_hair.curves.begin(b_curve_iter); b_curve_iter != b_hair.curves.end(); ++b_curve_iter) { - BL::HairCurve b_curve = *b_curve_iter; + for (BL::HairCurve &b_curve : b_hair.curves) { const int first_point_index = b_curve.first_point_index(); const int num_points = b_curve.num_points(); @@ -855,10 +847,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 */ diff --git a/intern/cycles/blender/blender_device.cpp b/intern/cycles/blender/blender_device.cpp index 3ec77719bea..d51b31de638 100644 --- a/intern/cycles/blender/blender_device.cpp +++ b/intern/cycles/blender/blender_device.cpp @@ -47,11 +47,9 @@ DeviceInfo blender_device_info(BL::Preferences &b_preferences, BL::Scene &b_scen /* Find cycles preferences. */ PointerRNA cpreferences; - BL::Preferences::addons_iterator b_addon_iter; - for (b_preferences.addons.begin(b_addon_iter); b_addon_iter != b_preferences.addons.end(); - ++b_addon_iter) { - if (b_addon_iter->module() == "cycles") { - cpreferences = b_addon_iter->preferences().ptr; + for (BL::Addon &b_addon : b_preferences.addons) { + if (b_addon.module() == "cycles") { + cpreferences = b_addon.preferences().ptr; break; } } diff --git a/intern/cycles/blender/blender_geometry.cpp b/intern/cycles/blender/blender_geometry.cpp index bd2f0731030..a009018f357 100644 --- a/intern/cycles/blender/blender_geometry.cpp +++ b/intern/cycles/blender/blender_geometry.cpp @@ -42,32 +42,20 @@ static Geometry::Type determine_geom_type(BL::Object &b_ob, bool use_particle_ha return Geometry::MESH; } -Geometry *BlenderSync::sync_geometry(BL::Depsgraph &b_depsgraph, - BL::Object &b_ob, - BL::Object &b_ob_instance, - bool object_updated, - bool use_particle_hair, - TaskPool *task_pool) +array<Node *> BlenderSync::find_used_shaders(BL::Object &b_ob) { - /* Test if we can instance or if the object is modified. */ - BL::ID b_ob_data = b_ob.data(); - BL::ID b_key_id = (BKE_object_is_modified(b_ob)) ? b_ob_instance : b_ob_data; BL::Material material_override = view_layer.material_override; Shader *default_shader = (b_ob.type() == BL::Object::type_VOLUME) ? scene->default_volume : scene->default_surface; - Geometry::Type geom_type = determine_geom_type(b_ob, use_particle_hair); - GeometryKey key(b_key_id.ptr.data, geom_type); - /* Find shader indices. */ array<Node *> used_shaders; - BL::Object::material_slots_iterator slot; - for (b_ob.material_slots.begin(slot); slot != b_ob.material_slots.end(); ++slot) { + for (BL::MaterialSlot &b_slot : b_ob.material_slots) { if (material_override) { find_shader(material_override, used_shaders, default_shader); } else { - BL::ID b_material(slot->material()); + BL::ID b_material(b_slot.material()); find_shader(b_material, used_shaders, default_shader); } } @@ -79,6 +67,25 @@ Geometry *BlenderSync::sync_geometry(BL::Depsgraph &b_depsgraph, used_shaders.push_back_slow(default_shader); } + return used_shaders; +} + +Geometry *BlenderSync::sync_geometry(BL::Depsgraph &b_depsgraph, + BL::Object &b_ob, + BL::Object &b_ob_instance, + bool object_updated, + bool use_particle_hair, + TaskPool *task_pool) +{ + /* Test if we can instance or if the object is modified. */ + BL::ID b_ob_data = b_ob.data(); + BL::ID b_key_id = (BKE_object_is_modified(b_ob)) ? b_ob_instance : b_ob_data; + Geometry::Type geom_type = determine_geom_type(b_ob, use_particle_hair); + GeometryKey key(b_key_id.ptr.data, geom_type); + + /* Find shader indices. */ + array<Node *> used_shaders = find_used_shaders(b_ob); + /* Ensure we only sync instanced geometry once. */ Geometry *geom = geometry_map.find(key); if (geom) { @@ -124,7 +131,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; } } diff --git a/intern/cycles/blender/blender_mesh.cpp b/intern/cycles/blender/blender_mesh.cpp index 3420025f472..7edf797e096 100644 --- a/intern/cycles/blender/blender_mesh.cpp +++ b/intern/cycles/blender/blender_mesh.cpp @@ -285,12 +285,10 @@ static void attr_create_sculpt_vertex_color(Scene *scene, BL::Mesh &b_mesh, bool subdivision) { - BL::Mesh::sculpt_vertex_colors_iterator l; - - for (b_mesh.sculpt_vertex_colors.begin(l); l != b_mesh.sculpt_vertex_colors.end(); ++l) { - const bool active_render = l->active_render(); + for (BL::MeshVertColorLayer &l : b_mesh.sculpt_vertex_colors) { + const bool active_render = l.active_render(); AttributeStandard vcol_std = (active_render) ? ATTR_STD_VERTEX_COLOR : ATTR_STD_NONE; - ustring vcol_name = ustring(l->name().c_str()); + ustring vcol_name = ustring(l.name().c_str()); const bool need_vcol = mesh->need_attribute(scene, vcol_name) || mesh->need_attribute(scene, vcol_std); @@ -307,7 +305,7 @@ static void attr_create_sculpt_vertex_color(Scene *scene, int numverts = b_mesh.vertices.length(); for (int i = 0; i < numverts; i++) { - *(cdata++) = get_float4(l->data[i].color()); + *(cdata++) = get_float4(l.data[i].color()); } } } @@ -315,12 +313,10 @@ static void attr_create_sculpt_vertex_color(Scene *scene, /* Create vertex color attributes. */ static void attr_create_vertex_color(Scene *scene, Mesh *mesh, BL::Mesh &b_mesh, bool subdivision) { - BL::Mesh::vertex_colors_iterator l; - - for (b_mesh.vertex_colors.begin(l); l != b_mesh.vertex_colors.end(); ++l) { - const bool active_render = l->active_render(); + for (BL::MeshLoopColorLayer &l : b_mesh.vertex_colors) { + const bool active_render = l.active_render(); AttributeStandard vcol_std = (active_render) ? ATTR_STD_VERTEX_COLOR : ATTR_STD_NONE; - ustring vcol_name = ustring(l->name().c_str()); + ustring vcol_name = ustring(l.name().c_str()); const bool need_vcol = mesh->need_attribute(scene, vcol_name) || mesh->need_attribute(scene, vcol_std); @@ -339,13 +335,12 @@ static void attr_create_vertex_color(Scene *scene, Mesh *mesh, BL::Mesh &b_mesh, vcol_attr = mesh->subd_attributes.add(vcol_name, TypeRGBA, ATTR_ELEMENT_CORNER_BYTE); } - BL::Mesh::polygons_iterator p; uchar4 *cdata = vcol_attr->data_uchar4(); - for (b_mesh.polygons.begin(p); p != b_mesh.polygons.end(); ++p) { - int n = p->loop_total(); + for (BL::MeshPolygon &p : b_mesh.polygons) { + int n = p.loop_total(); for (int i = 0; i < n; i++) { - float4 color = get_float4(l->data[p->loop_start() + i].color()); + float4 color = get_float4(l.data[p.loop_start() + i].color()); /* Compress/encode vertex color using the sRGB curve. */ *(cdata++) = color_float4_to_uchar4(color); } @@ -359,14 +354,13 @@ static void attr_create_vertex_color(Scene *scene, Mesh *mesh, BL::Mesh &b_mesh, vcol_attr = mesh->attributes.add(vcol_name, TypeRGBA, ATTR_ELEMENT_CORNER_BYTE); } - BL::Mesh::loop_triangles_iterator t; uchar4 *cdata = vcol_attr->data_uchar4(); - for (b_mesh.loop_triangles.begin(t); t != b_mesh.loop_triangles.end(); ++t) { - int3 li = get_int3(t->loops()); - float4 c1 = get_float4(l->data[li[0]].color()); - float4 c2 = get_float4(l->data[li[1]].color()); - float4 c3 = get_float4(l->data[li[2]].color()); + for (BL::MeshLoopTriangle &t : b_mesh.loop_triangles) { + int3 li = get_int3(t.loops()); + float4 c1 = get_float4(l.data[li[0]].color()); + float4 c2 = get_float4(l.data[li[1]].color()); + float4 c3 = get_float4(l.data[li[2]].color()); /* Compress/encode vertex color using the sRGB curve. */ cdata[0] = color_float4_to_uchar4(c1); @@ -383,14 +377,12 @@ static void attr_create_vertex_color(Scene *scene, Mesh *mesh, BL::Mesh &b_mesh, static void attr_create_uv_map(Scene *scene, Mesh *mesh, BL::Mesh &b_mesh) { if (b_mesh.uv_layers.length() != 0) { - BL::Mesh::uv_layers_iterator l; - - for (b_mesh.uv_layers.begin(l); l != b_mesh.uv_layers.end(); ++l) { - const bool active_render = l->active_render(); + for (BL::MeshUVLoopLayer &l : b_mesh.uv_layers) { + const bool active_render = l.active_render(); AttributeStandard uv_std = (active_render) ? ATTR_STD_UV : ATTR_STD_NONE; - ustring uv_name = ustring(l->name().c_str()); + ustring uv_name = ustring(l.name().c_str()); AttributeStandard tangent_std = (active_render) ? ATTR_STD_UV_TANGENT : ATTR_STD_NONE; - ustring tangent_name = ustring((string(l->name().c_str()) + ".tangent").c_str()); + ustring tangent_name = ustring((string(l.name().c_str()) + ".tangent").c_str()); /* Denotes whether UV map was requested directly. */ const bool need_uv = mesh->need_attribute(scene, uv_name) || @@ -412,14 +404,13 @@ static void attr_create_uv_map(Scene *scene, Mesh *mesh, BL::Mesh &b_mesh) uv_attr = mesh->attributes.add(uv_name, TypeFloat2, ATTR_ELEMENT_CORNER); } - BL::Mesh::loop_triangles_iterator t; float2 *fdata = uv_attr->data_float2(); - for (b_mesh.loop_triangles.begin(t); t != b_mesh.loop_triangles.end(); ++t) { - int3 li = get_int3(t->loops()); - fdata[0] = get_float2(l->data[li[0]].uv()); - fdata[1] = get_float2(l->data[li[1]].uv()); - fdata[2] = get_float2(l->data[li[2]].uv()); + for (BL::MeshLoopTriangle &t : b_mesh.loop_triangles) { + int3 li = get_int3(t.loops()); + fdata[0] = get_float2(l.data[li[0]].uv()); + fdata[1] = get_float2(l.data[li[1]].uv()); + fdata[2] = get_float2(l.data[li[2]].uv()); fdata += 3; } } @@ -427,10 +418,10 @@ static void attr_create_uv_map(Scene *scene, Mesh *mesh, BL::Mesh &b_mesh) /* UV tangent */ if (need_tangent) { AttributeStandard sign_std = (active_render) ? ATTR_STD_UV_TANGENT_SIGN : ATTR_STD_NONE; - ustring sign_name = ustring((string(l->name().c_str()) + ".tangent_sign").c_str()); + ustring sign_name = ustring((string(l.name().c_str()) + ".tangent_sign").c_str()); bool need_sign = (mesh->need_attribute(scene, sign_name) || mesh->need_attribute(scene, sign_std)); - mikk_compute_tangents(b_mesh, l->name().c_str(), mesh, need_sign, active_render); + mikk_compute_tangents(b_mesh, l.name().c_str(), mesh, need_sign, active_render); } /* Remove temporarily created UV attribute. */ if (!need_uv && uv_attr != NULL) { @@ -480,13 +471,12 @@ static void attr_create_subd_uv_map(Scene *scene, Mesh *mesh, BL::Mesh &b_mesh, uv_attr->flags |= ATTR_SUBDIVIDED; } - BL::Mesh::polygons_iterator p; float2 *fdata = uv_attr->data_float2(); - for (b_mesh.polygons.begin(p); p != b_mesh.polygons.end(); ++p) { - int n = p->loop_total(); + for (BL::MeshPolygon &p : b_mesh.polygons) { + int n = p.loop_total(); for (int j = 0; j < n; j++) { - *(fdata++) = get_float2(l->data[p->loop_start() + j].uv()); + *(fdata++) = get_float2(l->data[p.loop_start() + j].uv()); } } } @@ -706,9 +696,8 @@ static void attr_create_random_per_island(Scene *scene, DisjointSet vertices_sets(number_of_vertices); - BL::Mesh::edges_iterator e; - for (b_mesh.edges.begin(e); e != b_mesh.edges.end(); ++e) { - vertices_sets.join(e->vertices()[0], e->vertices()[1]); + for (BL::MeshEdge &e : b_mesh.edges) { + vertices_sets.join(e.vertices()[0], e.vertices()[1]); } AttributeSet &attributes = (subdivision) ? mesh->subd_attributes : mesh->attributes; @@ -716,15 +705,13 @@ static void attr_create_random_per_island(Scene *scene, float *data = attribute->data_float(); if (!subdivision) { - BL::Mesh::loop_triangles_iterator t; - for (b_mesh.loop_triangles.begin(t); t != b_mesh.loop_triangles.end(); ++t) { - data[t->index()] = hash_uint_to_float(vertices_sets.find(t->vertices()[0])); + for (BL::MeshLoopTriangle &t : b_mesh.loop_triangles) { + data[t.index()] = hash_uint_to_float(vertices_sets.find(t.vertices()[0])); } } else { - BL::Mesh::polygons_iterator p; - for (b_mesh.polygons.begin(p); p != b_mesh.polygons.end(); ++p) { - data[p->index()] = hash_uint_to_float(vertices_sets.find(p->vertices()[0])); + for (BL::MeshPolygon &p : b_mesh.polygons) { + data[p.index()] = hash_uint_to_float(vertices_sets.find(p.vertices()[0])); } } } @@ -756,10 +743,9 @@ static void create_mesh(Scene *scene, numtris = numfaces; } else { - BL::Mesh::polygons_iterator p; - for (b_mesh.polygons.begin(p); p != b_mesh.polygons.end(); ++p) { - numngons += (p->loop_total() == 4) ? 0 : 1; - numcorners += p->loop_total(); + for (BL::MeshPolygon &p : b_mesh.polygons) { + numngons += (p.loop_total() == 4) ? 0 : 1; + numcorners += p.loop_total(); } } @@ -803,17 +789,15 @@ static void create_mesh(Scene *scene, /* create faces */ if (!subdivision) { - BL::Mesh::loop_triangles_iterator t; - - for (b_mesh.loop_triangles.begin(t); t != b_mesh.loop_triangles.end(); ++t) { - BL::MeshPolygon p = b_mesh.polygons[t->polygon_index()]; - int3 vi = get_int3(t->vertices()); + for (BL::MeshLoopTriangle &t : b_mesh.loop_triangles) { + BL::MeshPolygon p = b_mesh.polygons[t.polygon_index()]; + int3 vi = get_int3(t.vertices()); int shader = clamp(p.material_index(), 0, used_shaders.size() - 1); bool smooth = p.use_smooth() || use_loop_normals; if (use_loop_normals) { - BL::Array<float, 9> loop_normals = t->split_normals(); + BL::Array<float, 9> loop_normals = t.split_normals(); for (int i = 0; i < 3; i++) { N[vi[i]] = make_float3( loop_normals[i * 3], loop_normals[i * 3 + 1], loop_normals[i * 3 + 2]); @@ -828,18 +812,17 @@ static void create_mesh(Scene *scene, } } else { - BL::Mesh::polygons_iterator p; vector<int> vi; - for (b_mesh.polygons.begin(p); p != b_mesh.polygons.end(); ++p) { - int n = p->loop_total(); - int shader = clamp(p->material_index(), 0, used_shaders.size() - 1); - bool smooth = p->use_smooth() || use_loop_normals; + for (BL::MeshPolygon &p : b_mesh.polygons) { + int n = p.loop_total(); + int shader = clamp(p.material_index(), 0, used_shaders.size() - 1); + bool smooth = p.use_smooth() || use_loop_normals; vi.resize(n); for (int i = 0; i < n; i++) { /* NOTE: Autosmooth is already taken care about. */ - vi[i] = b_mesh.loops[p->loop_start() + i].vertex_index(); + vi[i] = b_mesh.loops[p.loop_start() + i].vertex_index(); } /* create subd faces */ @@ -891,19 +874,18 @@ static void create_subd_mesh(Scene *scene, /* export creases */ size_t num_creases = 0; - BL::Mesh::edges_iterator e; - for (b_mesh.edges.begin(e); e != b_mesh.edges.end(); ++e) { - if (e->crease() != 0.0f) { + for (BL::MeshEdge &e : b_mesh.edges) { + if (e.crease() != 0.0f) { num_creases++; } } mesh->reserve_subd_creases(num_creases); - for (b_mesh.edges.begin(e); e != b_mesh.edges.end(); ++e) { - if (e->crease() != 0.0f) { - mesh->add_crease(e->vertices()[0], e->vertices()[1], e->crease()); + for (BL::MeshEdge &e : b_mesh.edges) { + if (e.crease() != 0.0f) { + mesh->add_crease(e.vertices()[0], e.vertices()[1], e.crease()); } } @@ -1075,15 +1057,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()); diff --git a/intern/cycles/blender/blender_object.cpp b/intern/cycles/blender/blender_object.cpp index 5261fbcee07..f0460b129c2 100644 --- a/intern/cycles/blender/blender_object.cpp +++ b/intern/cycles/blender/blender_object.cpp @@ -51,10 +51,11 @@ bool BlenderSync::BKE_object_is_modified(BL::Object &b_ob) } else { /* object level material links */ - BL::Object::material_slots_iterator slot; - for (b_ob.material_slots.begin(slot); slot != b_ob.material_slots.end(); ++slot) - if (slot->link() == BL::MaterialSlot::link_OBJECT) + for (BL::MaterialSlot &b_slot : b_ob.material_slots) { + if (b_slot.link() == BL::MaterialSlot::link_OBJECT) { return true; + } + } } return false; @@ -243,9 +244,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); diff --git a/intern/cycles/blender/blender_particles.cpp b/intern/cycles/blender/blender_particles.cpp index ca221b229b4..d5dd7215c47 100644 --- a/intern/cycles/blender/blender_particles.cpp +++ b/intern/cycles/blender/blender_particles.cpp @@ -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; diff --git a/intern/cycles/blender/blender_python.cpp b/intern/cycles/blender/blender_python.cpp index 525525e1047..5646bd5b2eb 100644 --- a/intern/cycles/blender/blender_python.cpp +++ b/intern/cycles/blender/blender_python.cpp @@ -597,22 +597,19 @@ static PyObject *osl_update_node_func(PyObject * /*self*/, PyObject *args) bool removed; do { - BL::Node::inputs_iterator b_input; - BL::Node::outputs_iterator b_output; - removed = false; - for (b_node.inputs.begin(b_input); b_input != b_node.inputs.end(); ++b_input) { - if (used_sockets.find(b_input->ptr.data) == used_sockets.end()) { - b_node.inputs.remove(b_data, *b_input); + for (BL::NodeSocket &b_input : b_node.inputs) { + if (used_sockets.find(b_input.ptr.data) == used_sockets.end()) { + b_node.inputs.remove(b_data, b_input); removed = true; break; } } - for (b_node.outputs.begin(b_output); b_output != b_node.outputs.end(); ++b_output) { - if (used_sockets.find(b_output->ptr.data) == used_sockets.end()) { - b_node.outputs.remove(b_data, *b_output); + for (BL::NodeSocket &b_output : b_node.outputs) { + if (used_sockets.find(b_output.ptr.data) == used_sockets.end()) { + b_node.outputs.remove(b_data, b_output); removed = true; break; } diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp index 8566d0e7ed5..ae13310789e 100644 --- a/intern/cycles/blender/blender_session.cpp +++ b/intern/cycles/blender/blender_session.cpp @@ -358,11 +358,7 @@ void BlenderSession::do_write_update_render_tile(RenderTile &rtile, if (do_read_only) { /* copy each pass */ - BL::RenderLayer::passes_iterator b_iter; - - for (b_rlay.passes.begin(b_iter); b_iter != b_rlay.passes.end(); ++b_iter) { - BL::RenderPass b_pass(*b_iter); - + for (BL::RenderPass &b_pass : b_rlay.passes) { /* find matching pass type */ PassType pass_type = BlenderSync::get_pass_type(b_pass); int components = b_pass.channels(); @@ -552,7 +548,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. */ @@ -736,10 +731,7 @@ void BlenderSession::do_write_update_render_result(BL::RenderLayer &b_rlay, if (!do_update_only) { /* copy each pass */ - BL::RenderLayer::passes_iterator b_iter; - - for (b_rlay.passes.begin(b_iter); b_iter != b_rlay.passes.end(); ++b_iter) { - BL::RenderPass b_pass(*b_iter); + for (BL::RenderPass &b_pass : b_rlay.passes) { int components = b_pass.channels(); /* Copy pixels from regular render passes. */ @@ -1116,10 +1108,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; } diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp index ac86cf3345c..64e2b172451 100644 --- a/intern/cycles/blender/blender_shader.cpp +++ b/intern/cycles/blender/blender_shader.cpp @@ -148,15 +148,13 @@ BlenderAttributeType blender_attribute_name_split_type(ustring name, string *r_r static BL::NodeSocket get_node_output(BL::Node &b_node, const string &name) { - BL::Node::outputs_iterator b_out; - - for (b_node.outputs.begin(b_out); b_out != b_node.outputs.end(); ++b_out) - if (b_out->name() == name) - return *b_out; - + for (BL::NodeSocket &b_out : b_node.outputs) { + if (b_out.name() == name) { + return b_out; + } + } assert(0); - - return *b_out; + return *b_node.outputs.begin(); } static float3 get_node_output_rgba(BL::Node &b_node, const string &name) @@ -723,9 +721,8 @@ static ShaderNode *add_node(Scene *scene, image->set_alpha_type(get_image_alpha_type(b_image)); array<int> tiles; - BL::Image::tiles_iterator b_iter; - for (b_image.tiles.begin(b_iter); b_iter != b_image.tiles.end(); ++b_iter) { - tiles.push_back_slow(b_iter->number()); + for (BL::UDIMTile &b_tile : b_image.tiles) { + tiles.push_back_slow(b_tile.number()); } image->set_tiles(tiles); @@ -1012,18 +1009,18 @@ static ShaderInput *node_find_input_by_name(ShaderNode *node, string name = b_socket.name(); if (node_use_modified_socket_name(node)) { - BL::Node::inputs_iterator b_input; bool found = false; int counter = 0, total = 0; - for (b_node.inputs.begin(b_input); b_input != b_node.inputs.end(); ++b_input) { - if (b_input->name() == name) { - if (!found) + for (BL::NodeSocket &b_input : b_node.inputs) { + if (b_input.name() == name) { + if (!found) { counter++; + } total++; } - if (b_input->ptr.data == b_socket.ptr.data) + if (b_input.ptr.data == b_socket.ptr.data) found = true; } @@ -1045,19 +1042,20 @@ static ShaderOutput *node_find_output_by_name(ShaderNode *node, string name = b_socket.name(); if (node_use_modified_socket_name(node)) { - BL::Node::outputs_iterator b_output; bool found = false; int counter = 0, total = 0; - for (b_node.outputs.begin(b_output); b_output != b_node.outputs.end(); ++b_output) { - if (b_output->name() == name) { - if (!found) + for (BL::NodeSocket &b_output : b_node.outputs) { + if (b_output.name() == name) { + if (!found) { counter++; + } total++; } - if (b_output->ptr.data == b_socket.ptr.data) + if (b_output.ptr.data == b_socket.ptr.data) { found = true; + } } /* rename if needed */ @@ -1082,25 +1080,19 @@ static void add_nodes(Scene *scene, const ProxyMap &proxy_output_map) { /* add nodes */ - BL::ShaderNodeTree::nodes_iterator b_node; PtrInputMap input_map; PtrOutputMap output_map; - BL::Node::inputs_iterator b_input; - BL::Node::outputs_iterator b_output; - /* find the node to use for output if there are multiple */ BL::ShaderNode output_node = b_ntree.get_output_node( BL::ShaderNodeOutputMaterial::target_CYCLES); /* add nodes */ - for (b_ntree.nodes.begin(b_node); b_node != b_ntree.nodes.end(); ++b_node) { - if (b_node->mute() || b_node->is_a(&RNA_NodeReroute)) { + for (BL::Node &b_node : b_ntree.nodes) { + if (b_node.mute() || b_node.is_a(&RNA_NodeReroute)) { /* replace muted node with internal links */ - BL::Node::internal_links_iterator b_link; - for (b_node->internal_links.begin(b_link); b_link != b_node->internal_links.end(); - ++b_link) { - BL::NodeSocket to_socket(b_link->to_socket()); + for (BL::NodeLink &b_link : b_node.internal_links) { + BL::NodeSocket to_socket(b_link.to_socket()); SocketType::Type to_socket_type = convert_socket_type(to_socket); if (to_socket_type == SocketType::UNDEFINED) { continue; @@ -1108,22 +1100,22 @@ static void add_nodes(Scene *scene, ConvertNode *proxy = graph->create_node<ConvertNode>(to_socket_type, to_socket_type, true); - input_map[b_link->from_socket().ptr.data] = proxy->inputs[0]; - output_map[b_link->to_socket().ptr.data] = proxy->outputs[0]; + input_map[b_link.from_socket().ptr.data] = proxy->inputs[0]; + output_map[b_link.to_socket().ptr.data] = proxy->outputs[0]; graph->add(proxy); } } - else if (b_node->is_a(&RNA_ShaderNodeGroup) || b_node->is_a(&RNA_NodeCustomGroup) || - b_node->is_a(&RNA_ShaderNodeCustomGroup)) { + else if (b_node.is_a(&RNA_ShaderNodeGroup) || b_node.is_a(&RNA_NodeCustomGroup) || + b_node.is_a(&RNA_ShaderNodeCustomGroup)) { BL::ShaderNodeTree b_group_ntree(PointerRNA_NULL); - if (b_node->is_a(&RNA_ShaderNodeGroup)) - b_group_ntree = BL::ShaderNodeTree(((BL::NodeGroup)(*b_node)).node_tree()); - else if (b_node->is_a(&RNA_NodeCustomGroup)) - b_group_ntree = BL::ShaderNodeTree(((BL::NodeCustomGroup)(*b_node)).node_tree()); + if (b_node.is_a(&RNA_ShaderNodeGroup)) + b_group_ntree = BL::ShaderNodeTree(((BL::NodeGroup)(b_node)).node_tree()); + else if (b_node.is_a(&RNA_NodeCustomGroup)) + b_group_ntree = BL::ShaderNodeTree(((BL::NodeCustomGroup)(b_node)).node_tree()); else - b_group_ntree = BL::ShaderNodeTree(((BL::ShaderNodeCustomGroup)(*b_node)).node_tree()); + b_group_ntree = BL::ShaderNodeTree(((BL::ShaderNodeCustomGroup)(b_node)).node_tree()); ProxyMap group_proxy_input_map, group_proxy_output_map; @@ -1131,8 +1123,8 @@ static void add_nodes(Scene *scene, * Do this even if the node group has no internal tree, * so that links have something to connect to and assert won't fail. */ - for (b_node->inputs.begin(b_input); b_input != b_node->inputs.end(); ++b_input) { - SocketType::Type input_type = convert_socket_type(*b_input); + for (BL::NodeSocket &b_input : b_node.inputs) { + SocketType::Type input_type = convert_socket_type(b_input); if (input_type == SocketType::UNDEFINED) { continue; } @@ -1141,14 +1133,14 @@ static void add_nodes(Scene *scene, graph->add(proxy); /* register the proxy node for internal binding */ - group_proxy_input_map[b_input->identifier()] = proxy; + group_proxy_input_map[b_input.identifier()] = proxy; - input_map[b_input->ptr.data] = proxy->inputs[0]; + input_map[b_input.ptr.data] = proxy->inputs[0]; - set_default_value(proxy->inputs[0], *b_input, b_data, b_ntree); + set_default_value(proxy->inputs[0], b_input, b_data, b_ntree); } - for (b_node->outputs.begin(b_output); b_output != b_node->outputs.end(); ++b_output) { - SocketType::Type output_type = convert_socket_type(*b_output); + for (BL::NodeSocket &b_output : b_node.outputs) { + SocketType::Type output_type = convert_socket_type(b_output); if (output_type == SocketType::UNDEFINED) { continue; } @@ -1157,9 +1149,9 @@ static void add_nodes(Scene *scene, graph->add(proxy); /* register the proxy node for internal binding */ - group_proxy_output_map[b_output->identifier()] = proxy; + group_proxy_output_map[b_output.identifier()] = proxy; - output_map[b_output->ptr.data] = proxy->outputs[0]; + output_map[b_output.ptr.data] = proxy->outputs[0]; } if (b_group_ntree) { @@ -1174,30 +1166,30 @@ static void add_nodes(Scene *scene, group_proxy_output_map); } } - else if (b_node->is_a(&RNA_NodeGroupInput)) { + else if (b_node.is_a(&RNA_NodeGroupInput)) { /* map each socket to a proxy node */ - for (b_node->outputs.begin(b_output); b_output != b_node->outputs.end(); ++b_output) { - ProxyMap::const_iterator proxy_it = proxy_input_map.find(b_output->identifier()); + for (BL::NodeSocket &b_output : b_node.outputs) { + ProxyMap::const_iterator proxy_it = proxy_input_map.find(b_output.identifier()); if (proxy_it != proxy_input_map.end()) { ConvertNode *proxy = proxy_it->second; - output_map[b_output->ptr.data] = proxy->outputs[0]; + output_map[b_output.ptr.data] = proxy->outputs[0]; } } } - else if (b_node->is_a(&RNA_NodeGroupOutput)) { - BL::NodeGroupOutput b_output_node(*b_node); + else if (b_node.is_a(&RNA_NodeGroupOutput)) { + BL::NodeGroupOutput b_output_node(b_node); /* only the active group output is used */ if (b_output_node.is_active_output()) { /* map each socket to a proxy node */ - for (b_node->inputs.begin(b_input); b_input != b_node->inputs.end(); ++b_input) { - ProxyMap::const_iterator proxy_it = proxy_output_map.find(b_input->identifier()); + for (BL::NodeSocket &b_input : b_node.inputs) { + ProxyMap::const_iterator proxy_it = proxy_output_map.find(b_input.identifier()); if (proxy_it != proxy_output_map.end()) { ConvertNode *proxy = proxy_it->second; - input_map[b_input->ptr.data] = proxy->inputs[0]; + input_map[b_input.ptr.data] = proxy->inputs[0]; - set_default_value(proxy->inputs[0], *b_input, b_data, b_ntree); + set_default_value(proxy->inputs[0], b_input, b_data, b_ntree); } } } @@ -1205,52 +1197,49 @@ static void add_nodes(Scene *scene, else { ShaderNode *node = NULL; - if (b_node->ptr.data == output_node.ptr.data) { + if (b_node.ptr.data == output_node.ptr.data) { node = graph->output(); } else { - BL::ShaderNode b_shader_node(*b_node); + BL::ShaderNode b_shader_node(b_node); node = add_node( scene, b_engine, b_data, b_depsgraph, b_scene, graph, b_ntree, b_shader_node); } if (node) { /* map node sockets for linking */ - for (b_node->inputs.begin(b_input); b_input != b_node->inputs.end(); ++b_input) { - ShaderInput *input = node_find_input_by_name(node, *b_node, *b_input); + for (BL::NodeSocket &b_input : b_node.inputs) { + ShaderInput *input = node_find_input_by_name(node, b_node, b_input); if (!input) { /* XXX should not happen, report error? */ continue; } - input_map[b_input->ptr.data] = input; + input_map[b_input.ptr.data] = input; - set_default_value(input, *b_input, b_data, b_ntree); + set_default_value(input, b_input, b_data, b_ntree); } - for (b_node->outputs.begin(b_output); b_output != b_node->outputs.end(); ++b_output) { - ShaderOutput *output = node_find_output_by_name(node, *b_node, *b_output); + for (BL::NodeSocket &b_output : b_node.outputs) { + ShaderOutput *output = node_find_output_by_name(node, b_node, b_output); if (!output) { /* XXX should not happen, report error? */ continue; } - output_map[b_output->ptr.data] = output; + output_map[b_output.ptr.data] = output; } } } } /* connect nodes */ - BL::NodeTree::links_iterator b_link; - - for (b_ntree.links.begin(b_link); b_link != b_ntree.links.end(); ++b_link) { + for (BL::NodeLink &b_link : b_ntree.links) { /* Ignore invalid links to avoid unwanted cycles created in graph. * Also ignore links with unavailable sockets. */ - if (!(b_link->is_valid() && b_link->from_socket().enabled() && - b_link->to_socket().enabled())) { + if (!(b_link.is_valid() && b_link.from_socket().enabled() && b_link.to_socket().enabled())) { continue; } /* get blender link data */ - BL::NodeSocket b_from_sock = b_link->from_socket(); - BL::NodeSocket b_to_sock = b_link->to_socket(); + BL::NodeSocket b_from_sock = b_link.from_socket(); + BL::NodeSocket b_to_sock = b_link.to_socket(); ShaderOutput *output = 0; ShaderInput *input = 0; @@ -1298,13 +1287,12 @@ void BlenderSync::sync_materials(BL::Depsgraph &b_depsgraph, bool update_all) TaskPool pool; set<Shader *> updated_shaders; - BL::Depsgraph::ids_iterator b_id; - for (b_depsgraph.ids.begin(b_id); b_id != b_depsgraph.ids.end(); ++b_id) { - if (!b_id->is_a(&RNA_Material)) { + for (BL::ID &b_id : b_depsgraph.ids) { + if (!b_id.is_a(&RNA_Material)) { continue; } - BL::Material b_mat(*b_id); + BL::Material b_mat(b_id); Shader *shader; /* test if we need to sync */ @@ -1497,7 +1485,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 +1504,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 */ @@ -1527,13 +1513,12 @@ void BlenderSync::sync_lights(BL::Depsgraph &b_depsgraph, bool update_all) { shader_map.set_default(scene->default_light); - BL::Depsgraph::ids_iterator b_id; - for (b_depsgraph.ids.begin(b_id); b_id != b_depsgraph.ids.end(); ++b_id) { - if (!b_id->is_a(&RNA_Light)) { + for (BL::ID &b_id : b_depsgraph.ids) { + if (!b_id.is_a(&RNA_Light)) { continue; } - BL::Light b_light(*b_id); + BL::Light b_light(b_id); Shader *shader; /* test if we need to sync */ diff --git a/intern/cycles/blender/blender_sync.cpp b/intern/cycles/blender/blender_sync.cpp index e27daa2488d..4bcadab35a1 100644 --- a/intern/cycles/blender/blender_sync.cpp +++ b/intern/cycles/blender/blender_sync.cpp @@ -24,6 +24,7 @@ #include "render/mesh.h" #include "render/nodes.h" #include "render/object.h" +#include "render/procedural.h" #include "render/scene.h" #include "render/shader.h" @@ -131,9 +132,8 @@ void BlenderSync::sync_recalc(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d } /* Iterate over all IDs in this depsgraph. */ - BL::Depsgraph::updates_iterator b_update; - for (b_depsgraph.updates.begin(b_update); b_update != b_depsgraph.updates.end(); ++b_update) { - BL::ID b_id(b_update->id()); + for (BL::DepsgraphUpdate &b_update : b_depsgraph.updates) { + BL::ID b_id(b_update.id()); /* Material */ if (b_id.is_a(&RNA_Material)) { @@ -151,17 +151,17 @@ void BlenderSync::sync_recalc(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d const bool is_geometry = object_is_geometry(b_ob); const bool is_light = !is_geometry && object_is_light(b_ob); - if (b_ob.is_instancer() && b_update->is_updated_shading()) { + if (b_ob.is_instancer() && b_update.is_updated_shading()) { /* Needed for e.g. object color updates on instancer. */ object_map.set_recalc(b_ob); } if (is_geometry || is_light) { - const bool updated_geometry = b_update->is_updated_geometry(); + const bool updated_geometry = b_update.is_updated_geometry(); /* Geometry (mesh, hair, volume). */ if (is_geometry) { - if (b_update->is_updated_transform() || b_update->is_updated_shading()) { + if (b_update.is_updated_transform() || b_update.is_updated_shading()) { object_map.set_recalc(b_ob); } @@ -181,7 +181,7 @@ void BlenderSync::sync_recalc(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d } /* Light */ else if (is_light) { - if (b_update->is_updated_transform() || b_update->is_updated_shading()) { + if (b_update.is_updated_transform() || b_update.is_updated_shading()) { object_map.set_recalc(b_ob); light_map.set_recalc(b_ob); } @@ -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 */ @@ -471,16 +466,15 @@ void BlenderSync::sync_images() return; } /* Free buffers used by images which are not needed for render. */ - BL::BlendData::images_iterator b_image; - for (b_data.images.begin(b_image); b_image != b_data.images.end(); ++b_image) { + for (BL::Image &b_image : b_data.images) { /* TODO(sergey): Consider making it an utility function to check * whether image is considered builtin. */ - const bool is_builtin = b_image->packed_file() || - b_image->source() == BL::Image::source_GENERATED || - b_image->source() == BL::Image::source_MOVIE || b_engine.is_preview(); + const bool is_builtin = b_image.packed_file() || + b_image.source() == BL::Image::source_GENERATED || + b_image.source() == BL::Image::source_MOVIE || b_engine.is_preview(); if (is_builtin == false) { - b_image->buffers_free(); + b_image.buffers_free(); } /* TODO(sergey): Free builtin images not used by any shader. */ } @@ -581,10 +575,7 @@ vector<Pass> BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay, vector<Pass> passes; /* loop over passes */ - BL::RenderLayer::passes_iterator b_pass_iter; - - for (b_rlay.passes.begin(b_pass_iter); b_pass_iter != b_rlay.passes.end(); ++b_pass_iter) { - BL::RenderPass b_pass(*b_pass_iter); + for (BL::RenderPass &b_pass : b_rlay.passes) { PassType pass_type = get_pass_type(b_pass); if (pass_type == PASS_MOTION && scene->integrator->get_motion_blur()) @@ -729,7 +720,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; } @@ -752,9 +743,8 @@ void BlenderSync::free_data_after_sync(BL::Depsgraph &b_depsgraph) /* TODO(sergey): We can actually remove the whole dependency graph, * but that will need some API support first. */ - BL::Depsgraph::objects_iterator b_ob; - for (b_depsgraph.objects.begin(b_ob); b_ob != b_depsgraph.objects.end(); ++b_ob) { - b_ob->cache_release(); + for (BL::Object &b_ob : b_depsgraph.objects) { + b_ob.cache_release(); } } diff --git a/intern/cycles/blender/blender_sync.h b/intern/cycles/blender/blender_sync.h index ccf059d7704..1c43522a57e 100644 --- a/intern/cycles/blender/blender_sync.h +++ b/intern/cycles/blender/blender_sync.h @@ -134,6 +134,7 @@ class BlenderSync { void sync_view(); /* Shader */ + array<Node *> find_used_shaders(BL::Object &b_ob); void sync_world(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d, bool update_all); void sync_shaders(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d); void sync_nodes(Shader *shader, BL::ShaderNodeTree &b_ntree); diff --git a/intern/cycles/blender/blender_util.h b/intern/cycles/blender/blender_util.h index 5185ebae789..43dbb4105df 100644 --- a/intern/cycles/blender/blender_util.h +++ b/intern/cycles/blender/blender_util.h @@ -538,11 +538,9 @@ static inline bool object_use_deform_motion(BL::Object &b_parent, BL::Object &b_ static inline BL::FluidDomainSettings object_fluid_liquid_domain_find(BL::Object &b_ob) { - BL::Object::modifiers_iterator b_mod; - - for (b_ob.modifiers.begin(b_mod); b_mod != b_ob.modifiers.end(); ++b_mod) { - if (b_mod->is_a(&RNA_FluidModifier)) { - BL::FluidModifier b_mmd(*b_mod); + for (BL::Modifier &b_mod : b_ob.modifiers) { + if (b_mod.is_a(&RNA_FluidModifier)) { + BL::FluidModifier b_mmd(b_mod); if (b_mmd.fluid_type() == BL::FluidModifier::fluid_type_DOMAIN && b_mmd.domain_settings().domain_type() == BL::FluidDomainSettings::domain_type_LIQUID) { @@ -556,11 +554,9 @@ static inline BL::FluidDomainSettings object_fluid_liquid_domain_find(BL::Object static inline BL::FluidDomainSettings object_fluid_gas_domain_find(BL::Object &b_ob) { - BL::Object::modifiers_iterator b_mod; - - for (b_ob.modifiers.begin(b_mod); b_mod != b_ob.modifiers.end(); ++b_mod) { - if (b_mod->is_a(&RNA_FluidModifier)) { - BL::FluidModifier b_mmd(*b_mod); + for (BL::Modifier &b_mod : b_ob.modifiers) { + if (b_mod.is_a(&RNA_FluidModifier)) { + BL::FluidModifier b_mmd(b_mod); if (b_mmd.fluid_type() == BL::FluidModifier::fluid_type_DOMAIN && b_mmd.domain_settings().domain_type() == BL::FluidDomainSettings::domain_type_GAS) { diff --git a/intern/cycles/blender/blender_volume.cpp b/intern/cycles/blender/blender_volume.cpp index 094d4bbc08c..410f7a72cf5 100644 --- a/intern/cycles/blender/blender_volume.cpp +++ b/intern/cycles/blender/blender_volume.cpp @@ -222,9 +222,7 @@ class BlenderVolumeLoader : public VDBImageLoader { b_volume.grids.load(b_data.ptr.data); #ifdef WITH_OPENVDB - BL::Volume::grids_iterator b_grid_iter; - for (b_volume.grids.begin(b_grid_iter); b_grid_iter != b_volume.grids.end(); ++b_grid_iter) { - BL::VolumeGrid b_volume_grid(*b_grid_iter); + for (BL::VolumeGrid &b_volume_grid : b_volume.grids) { if (b_volume_grid.name() == grid_name) { const bool unload = !b_volume_grid.is_loaded(); @@ -260,9 +258,7 @@ static void sync_volume_object(BL::BlendData &b_data, volume->set_object_space((b_render.space() == BL::VolumeRender::space_OBJECT)); /* Find grid with matching name. */ - BL::Volume::grids_iterator b_grid_iter; - for (b_volume.grids.begin(b_grid_iter); b_grid_iter != b_volume.grids.end(); ++b_grid_iter) { - BL::VolumeGrid b_grid = *b_grid_iter; + for (BL::VolumeGrid &b_grid : b_volume.grids) { ustring name = ustring(b_grid.name()); AttributeStandard std = ATTR_STD_NONE; diff --git a/intern/cycles/device/device_memory.h b/intern/cycles/device/device_memory.h index 00b2aa864aa..1f63a152458 100644 --- a/intern/cycles/device/device_memory.h +++ b/intern/cycles/device/device_memory.h @@ -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() diff --git a/intern/cycles/kernel/kernels/opencl/kernel_opencl_image.h b/intern/cycles/kernel/kernels/opencl/kernel_opencl_image.h index d87e5f193ad..bb6b8a40e8e 100644 --- a/intern/cycles/kernel/kernels/opencl/kernel_opencl_image.h +++ b/intern/cycles/kernel/kernels/opencl/kernel_opencl_image.h @@ -18,7 +18,7 @@ /* Data type to replace `double` used in the NanoVDB headers. Cycles don't need doubles, and is * safer and more portable to never use double datatype on GPU. * Use a special structure, so that the following is true: - * - No unnoticed implicit cast or mathermatical operations used on scalar 64bit type + * - No unnoticed implicit cast or mathematical operations used on scalar 64bit type * (which rules out trick like using `uint64_t` as a drop-in replacement for double). * - Padding rules are matching exactly `double` * (which rules out array of `uint8_t`). */ diff --git a/intern/cycles/kernel/osl/osl_globals.h b/intern/cycles/kernel/osl/osl_globals.h index caca3c28c8d..f1789f0d7eb 100644 --- a/intern/cycles/kernel/osl/osl_globals.h +++ b/intern/cycles/kernel/osl/osl_globals.h @@ -43,7 +43,7 @@ class ColorSpaceProcessor; * * Data needed by OSL render services, that is global to a rendering session. * This includes all OSL shaders, name to attribute mapping and texture handles. - * */ + */ struct OSLGlobals { OSLGlobals() diff --git a/intern/cycles/render/CMakeLists.txt b/intern/cycles/render/CMakeLists.txt index 9663e25cd93..389f913b145 100644 --- a/intern/cycles/render/CMakeLists.txt +++ b/intern/cycles/render/CMakeLists.txt @@ -23,6 +23,7 @@ set(INC_SYS ) set(SRC + alembic.cpp attribute.cpp background.cpp bake.cpp @@ -48,6 +49,7 @@ set(SRC mesh_displace.cpp mesh_subdivision.cpp nodes.cpp + procedural.cpp object.cpp osl.cpp particles.cpp @@ -64,6 +66,7 @@ set(SRC ) set(SRC_HEADERS + alembic.h attribute.h bake.h background.h @@ -90,6 +93,7 @@ set(SRC_HEADERS object.h osl.h particles.h + procedural.h curves.h scene.h session.h @@ -144,6 +148,16 @@ if(WITH_OPENVDB) ) endif() +if(WITH_ALEMBIC) + add_definitions(-DWITH_ALEMBIC) + list(APPEND INC_SYS + ${ALEMBIC_INCLUDE_DIRS} + ) + list(APPEND LIB + ${ALEMBIC_LIBRARIES} + ) +endif() + if(WITH_NANOVDB) list(APPEND INC_SYS ${NANOVDB_INCLUDE_DIRS} diff --git a/intern/cycles/render/alembic.cpp b/intern/cycles/render/alembic.cpp new file mode 100644 index 00000000000..e0e50d99f84 --- /dev/null +++ b/intern/cycles/render/alembic.cpp @@ -0,0 +1,1869 @@ +/* + * 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/alembic.h" + +#include "render/camera.h" +#include "render/curves.h" +#include "render/mesh.h" +#include "render/object.h" +#include "render/scene.h" +#include "render/shader.h" + +#include "util/util_foreach.h" +#include "util/util_progress.h" +#include "util/util_transform.h" +#include "util/util_vector.h" + +#ifdef WITH_ALEMBIC + +using namespace Alembic::AbcGeom; + +CCL_NAMESPACE_BEGIN + +/* TODO(@kevindietrich): motion blur support */ + +void CachedData::clear() +{ + attributes.clear(); + curve_first_key.clear(); + curve_keys.clear(); + curve_radius.clear(); + curve_shader.clear(); + num_ngons.clear(); + shader.clear(); + subd_creases_edge.clear(); + subd_creases_weight.clear(); + subd_face_corners.clear(); + subd_num_corners.clear(); + subd_ptex_offset.clear(); + subd_smooth.clear(); + subd_start_corner.clear(); + transforms.clear(); + triangles.clear(); + triangles_loops.clear(); + vertices.clear(); + + for (CachedAttribute &attr : attributes) { + attr.data.clear(); + } + + attributes.clear(); +} + +CachedData::CachedAttribute &CachedData::add_attribute(const ustring &name, + const TimeSampling &time_sampling) +{ + for (auto &attr : attributes) { + if (attr.name == name) { + return attr; + } + } + + CachedAttribute &attr = attributes.emplace_back(); + attr.name = name; + attr.data.set_time_sampling(time_sampling); + return attr; +} + +bool CachedData::is_constant() const +{ +# define CHECK_IF_CONSTANT(data) \ + if (!data.is_constant()) { \ + return false; \ + } + + CHECK_IF_CONSTANT(curve_first_key) + CHECK_IF_CONSTANT(curve_keys) + CHECK_IF_CONSTANT(curve_radius) + CHECK_IF_CONSTANT(curve_shader) + CHECK_IF_CONSTANT(num_ngons) + CHECK_IF_CONSTANT(shader) + CHECK_IF_CONSTANT(subd_creases_edge) + CHECK_IF_CONSTANT(subd_creases_weight) + CHECK_IF_CONSTANT(subd_face_corners) + CHECK_IF_CONSTANT(subd_num_corners) + CHECK_IF_CONSTANT(subd_ptex_offset) + CHECK_IF_CONSTANT(subd_smooth) + CHECK_IF_CONSTANT(subd_start_corner) + CHECK_IF_CONSTANT(transforms) + CHECK_IF_CONSTANT(triangles) + CHECK_IF_CONSTANT(triangles_loops) + CHECK_IF_CONSTANT(vertices) + + for (const CachedAttribute &attr : attributes) { + if (!attr.data.is_constant()) { + return false; + } + } + + return true; + +# undef CHECK_IF_CONSTANT +} + +void CachedData::invalidate_last_loaded_time(bool attributes_only) +{ + if (attributes_only) { + for (CachedAttribute &attr : attributes) { + attr.data.invalidate_last_loaded_time(); + } + + return; + } + + curve_first_key.invalidate_last_loaded_time(); + curve_keys.invalidate_last_loaded_time(); + curve_radius.invalidate_last_loaded_time(); + curve_shader.invalidate_last_loaded_time(); + num_ngons.invalidate_last_loaded_time(); + shader.invalidate_last_loaded_time(); + subd_creases_edge.invalidate_last_loaded_time(); + subd_creases_weight.invalidate_last_loaded_time(); + subd_face_corners.invalidate_last_loaded_time(); + subd_num_corners.invalidate_last_loaded_time(); + subd_ptex_offset.invalidate_last_loaded_time(); + subd_smooth.invalidate_last_loaded_time(); + subd_start_corner.invalidate_last_loaded_time(); + transforms.invalidate_last_loaded_time(); + triangles.invalidate_last_loaded_time(); + triangles_loops.invalidate_last_loaded_time(); + vertices.invalidate_last_loaded_time(); +} + +void CachedData::set_time_sampling(TimeSampling time_sampling) +{ + curve_first_key.set_time_sampling(time_sampling); + curve_keys.set_time_sampling(time_sampling); + curve_radius.set_time_sampling(time_sampling); + curve_shader.set_time_sampling(time_sampling); + num_ngons.set_time_sampling(time_sampling); + shader.set_time_sampling(time_sampling); + subd_creases_edge.set_time_sampling(time_sampling); + subd_creases_weight.set_time_sampling(time_sampling); + subd_face_corners.set_time_sampling(time_sampling); + subd_num_corners.set_time_sampling(time_sampling); + subd_ptex_offset.set_time_sampling(time_sampling); + subd_smooth.set_time_sampling(time_sampling); + subd_start_corner.set_time_sampling(time_sampling); + transforms.set_time_sampling(time_sampling); + triangles.set_time_sampling(time_sampling); + triangles_loops.set_time_sampling(time_sampling); + vertices.set_time_sampling(time_sampling); + + for (CachedAttribute &attr : attributes) { + attr.data.set_time_sampling(time_sampling); + } +} + +/* get the sample times to load data for the given the start and end frame of the procedural */ +static set<chrono_t> get_relevant_sample_times(AlembicProcedural *proc, + const TimeSampling &time_sampling, + size_t num_samples) +{ + set<chrono_t> result; + + if (num_samples < 2) { + result.insert(0.0); + return result; + } + + double start_frame = (double)(proc->get_start_frame() / proc->get_frame_rate()); + double end_frame = (double)((proc->get_end_frame() + 1) / proc->get_frame_rate()); + + size_t start_index = time_sampling.getFloorIndex(start_frame, num_samples).first; + size_t end_index = time_sampling.getCeilIndex(end_frame, num_samples).first; + + for (size_t i = start_index; i < end_index; ++i) { + result.insert(time_sampling.getSampleTime(i)); + } + + return result; +} + +static float3 make_float3_from_yup(const V3f &v) +{ + return make_float3(v.x, -v.z, v.y); +} + +static M44d convert_yup_zup(const M44d &mtx, float scale_mult) +{ + V3d scale, shear, rotation, translation; + extractSHRT(mtx, + scale, + shear, + rotation, + translation, + true, + IMATH_INTERNAL_NAMESPACE::Euler<double>::XZY); + + M44d rot_mat, scale_mat, trans_mat; + rot_mat.setEulerAngles(V3d(rotation.x, -rotation.z, rotation.y)); + scale_mat.setScale(V3d(scale.x, scale.z, scale.y)); + trans_mat.setTranslation(V3d(translation.x, -translation.z, translation.y)); + + M44d temp_mat = scale_mat * rot_mat * trans_mat; + + scale_mat.setScale(static_cast<double>(scale_mult)); + + return temp_mat * scale_mat; +} + +static void transform_decompose( + const M44d &mat, V3d &scale, V3d &shear, Quatd &rotation, V3d &translation) +{ + M44d mat_remainder(mat); + + /* extract scale and shear */ + Imath::extractAndRemoveScalingAndShear(mat_remainder, scale, shear); + + /* extract translation */ + translation.x = mat_remainder[3][0]; + translation.y = mat_remainder[3][1]; + translation.z = mat_remainder[3][2]; + + /* extract rotation */ + rotation = extractQuat(mat_remainder); +} + +static M44d transform_compose(const V3d &scale, + const V3d &shear, + const Quatd &rotation, + const V3d &translation) +{ + M44d scale_mat, shear_mat, rot_mat, trans_mat; + + scale_mat.setScale(scale); + shear_mat.setShear(shear); + rot_mat = rotation.toMatrix44(); + trans_mat.setTranslation(translation); + + return scale_mat * shear_mat * rot_mat * trans_mat; +} + +/* get the matrix for the specified time, or return the identity matrix if there is no exact match + */ +static M44d get_matrix_for_time(const MatrixSampleMap &samples, chrono_t time) +{ + MatrixSampleMap::const_iterator iter = samples.find(time); + if (iter != samples.end()) { + return iter->second; + } + + return M44d(); +} + +/* get the matrix for the specified time, or interpolate between samples if there is no exact match + */ +static M44d get_interpolated_matrix_for_time(const MatrixSampleMap &samples, chrono_t time) +{ + if (samples.empty()) { + return M44d(); + } + + /* see if exact match */ + MatrixSampleMap::const_iterator iter = samples.find(time); + if (iter != samples.end()) { + return iter->second; + } + + if (samples.size() == 1) { + return samples.begin()->second; + } + + if (time <= samples.begin()->first) { + return samples.begin()->second; + } + + if (time >= samples.rbegin()->first) { + return samples.rbegin()->second; + } + + /* find previous and next time sample to interpolate */ + chrono_t prev_time = samples.begin()->first; + chrono_t next_time = samples.rbegin()->first; + + for (MatrixSampleMap::const_iterator I = samples.begin(); I != samples.end(); ++I) { + chrono_t current_time = (*I).first; + + if (current_time > prev_time && current_time <= time) { + prev_time = current_time; + } + + if (current_time > next_time && current_time >= time) { + next_time = current_time; + } + } + + const M44d prev_mat = get_matrix_for_time(samples, prev_time); + const M44d next_mat = get_matrix_for_time(samples, next_time); + + V3d prev_scale, next_scale; + V3d prev_shear, next_shear; + V3d prev_translation, next_translation; + Quatd prev_rotation, next_rotation; + + transform_decompose(prev_mat, prev_scale, prev_shear, prev_rotation, prev_translation); + transform_decompose(next_mat, next_scale, next_shear, next_rotation, next_translation); + + chrono_t t = (time - prev_time) / (next_time - prev_time); + + /* ensure rotation around the shortest angle */ + if ((prev_rotation ^ next_rotation) < 0) { + next_rotation = -next_rotation; + } + + return transform_compose(Imath::lerp(prev_scale, next_scale, t), + Imath::lerp(prev_shear, next_shear, t), + Imath::slerp(prev_rotation, next_rotation, t), + Imath::lerp(prev_translation, next_translation, t)); +} + +static void concatenate_xform_samples(const MatrixSampleMap &parent_samples, + const MatrixSampleMap &local_samples, + MatrixSampleMap &output_samples) +{ + set<chrono_t> union_of_samples; + + for (const std::pair<chrono_t, M44d> pair : parent_samples) { + union_of_samples.insert(pair.first); + } + + for (const std::pair<chrono_t, M44d> pair : local_samples) { + union_of_samples.insert(pair.first); + } + + foreach (chrono_t time, union_of_samples) { + M44d parent_matrix = get_interpolated_matrix_for_time(parent_samples, time); + M44d local_matrix = get_interpolated_matrix_for_time(local_samples, time); + + output_samples[time] = local_matrix * parent_matrix; + } +} + +static Transform make_transform(const M44d &a, float scale) +{ + M44d m = convert_yup_zup(a, scale); + Transform trans; + for (int j = 0; j < 3; j++) { + for (int i = 0; i < 4; i++) { + trans[j][i] = static_cast<float>(m[i][j]); + } + } + return trans; +} + +static void add_uvs(AlembicProcedural *proc, + const IV2fGeomParam &uvs, + CachedData &cached_data, + Progress &progress) +{ + if (uvs.getScope() != kFacevaryingScope) { + return; + } + + const TimeSamplingPtr time_sampling_ptr = uvs.getTimeSampling(); + + TimeSampling time_sampling; + if (time_sampling_ptr) { + time_sampling = *time_sampling_ptr; + } + + std::string name = Alembic::Abc::GetSourceName(uvs.getMetaData()); + + /* According to the convention, primary UVs should have had their name + * set using Alembic::Abc::SetSourceName, but you can't expect everyone + * to follow it! :) */ + if (name.empty()) { + name = uvs.getName(); + } + + CachedData::CachedAttribute &attr = cached_data.add_attribute(ustring(name), time_sampling); + attr.std = ATTR_STD_UV; + + ccl::set<chrono_t> times = get_relevant_sample_times(proc, time_sampling, uvs.getNumSamples()); + + foreach (chrono_t time, times) { + if (progress.get_cancel()) { + return; + } + + const ISampleSelector iss = ISampleSelector(time); + const IV2fGeomParam::Sample sample = uvs.getExpandedValue(iss); + + const IV2fGeomParam::Sample uvsample = uvs.getIndexedValue(iss); + + if (!uvsample.valid()) { + continue; + } + + const array<int3> *triangles = cached_data.triangles.data_for_time_no_check(time); + const array<int3> *triangles_loops = cached_data.triangles_loops.data_for_time_no_check(time); + + if (!triangles || !triangles_loops) { + continue; + } + + array<char> data; + data.resize(triangles->size() * 3 * sizeof(float2)); + + float2 *data_float2 = reinterpret_cast<float2 *>(data.data()); + + const unsigned int *indices = uvsample.getIndices()->get(); + const V2f *values = uvsample.getVals()->get(); + + for (const int3 &loop : *triangles_loops) { + unsigned int v0 = indices[loop.x]; + unsigned int v1 = indices[loop.y]; + unsigned int v2 = indices[loop.z]; + + data_float2[0] = make_float2(values[v0][0], values[v0][1]); + data_float2[1] = make_float2(values[v1][0], values[v1][1]); + data_float2[2] = make_float2(values[v2][0], values[v2][1]); + data_float2 += 3; + } + + attr.data.add_data(data, time); + } +} + +static void add_normals(const Int32ArraySamplePtr face_indices, + const IN3fGeomParam &normals, + double time, + CachedData &cached_data) +{ + switch (normals.getScope()) { + case kFacevaryingScope: { + const ISampleSelector iss = ISampleSelector(time); + const IN3fGeomParam::Sample sample = normals.getExpandedValue(iss); + + if (!sample.valid()) { + return; + } + + CachedData::CachedAttribute &attr = cached_data.add_attribute(ustring(normals.getName()), + *normals.getTimeSampling()); + attr.std = ATTR_STD_VERTEX_NORMAL; + + const array<float3> *vertices = cached_data.vertices.data_for_time_no_check(time); + + if (!vertices) { + return; + } + + array<char> data; + data.resize(vertices->size() * sizeof(float3)); + + float3 *data_float3 = reinterpret_cast<float3 *>(data.data()); + + const int *face_indices_array = face_indices->get(); + const N3fArraySamplePtr values = sample.getVals(); + + for (size_t i = 0; i < face_indices->size(); ++i) { + int point_index = face_indices_array[i]; + data_float3[point_index] = make_float3_from_yup(values->get()[i]); + } + + attr.data.add_data(data, time); + break; + } + case kVaryingScope: + case kVertexScope: { + const ISampleSelector iss = ISampleSelector(time); + const IN3fGeomParam::Sample sample = normals.getExpandedValue(iss); + + if (!sample.valid()) { + return; + } + + CachedData::CachedAttribute &attr = cached_data.add_attribute(ustring(normals.getName()), + *normals.getTimeSampling()); + attr.std = ATTR_STD_VERTEX_NORMAL; + + const array<float3> *vertices = cached_data.vertices.data_for_time_no_check(time); + + if (!vertices) { + return; + } + + array<char> data; + data.resize(vertices->size() * sizeof(float3)); + + float3 *data_float3 = reinterpret_cast<float3 *>(data.data()); + + const Imath::V3f *values = sample.getVals()->get(); + + for (size_t i = 0; i < vertices->size(); ++i) { + data_float3[i] = make_float3_from_yup(values[i]); + } + + attr.data.add_data(data, time); + + break; + } + default: { + break; + } + } +} + +static void add_positions(const P3fArraySamplePtr positions, double time, CachedData &cached_data) +{ + if (!positions) { + return; + } + + array<float3> vertices; + vertices.reserve(positions->size()); + + for (size_t i = 0; i < positions->size(); i++) { + V3f f = positions->get()[i]; + vertices.push_back_reserved(make_float3_from_yup(f)); + } + + cached_data.vertices.add_data(vertices, time); +} + +static void add_triangles(const Int32ArraySamplePtr face_counts, + const Int32ArraySamplePtr face_indices, + double time, + CachedData &cached_data, + const array<int> &polygon_to_shader) +{ + if (!face_counts || !face_indices) { + return; + } + + const size_t num_faces = face_counts->size(); + const int *face_counts_array = face_counts->get(); + const int *face_indices_array = face_indices->get(); + + size_t num_triangles = 0; + for (size_t i = 0; i < face_counts->size(); i++) { + num_triangles += face_counts_array[i] - 2; + } + + array<int> shader; + array<int3> triangles; + array<int3> triangles_loops; + shader.reserve(num_triangles); + triangles.reserve(num_triangles); + triangles_loops.reserve(num_triangles); + int index_offset = 0; + + for (size_t i = 0; i < num_faces; i++) { + int current_shader = 0; + + if (!polygon_to_shader.empty()) { + current_shader = polygon_to_shader[i]; + } + + for (int j = 0; j < face_counts_array[i] - 2; j++) { + int v0 = face_indices_array[index_offset]; + int v1 = face_indices_array[index_offset + j + 1]; + int v2 = face_indices_array[index_offset + j + 2]; + + shader.push_back_reserved(current_shader); + + /* Alembic orders the loops following the RenderMan convention, so need to go in reverse. */ + triangles.push_back_reserved(make_int3(v2, v1, v0)); + triangles_loops.push_back_reserved( + make_int3(index_offset + j + 2, index_offset + j + 1, index_offset)); + } + + index_offset += face_counts_array[i]; + } + + cached_data.triangles.add_data(triangles, time); + cached_data.triangles_loops.add_data(triangles_loops, time); + cached_data.shader.add_data(shader, time); +} + +NODE_DEFINE(AlembicObject) +{ + NodeType *type = NodeType::add("alembic_object", create); + + SOCKET_STRING(path, "Alembic Path", ustring()); + SOCKET_NODE_ARRAY(used_shaders, "Used Shaders", &Shader::node_type); + + SOCKET_INT(subd_max_level, "Max Subdivision Level", 1); + SOCKET_FLOAT(subd_dicing_rate, "Subdivision Dicing Rate", 1.0f); + + SOCKET_FLOAT(radius_scale, "Radius Scale", 1.0f); + + return type; +} + +AlembicObject::AlembicObject() : Node(node_type) +{ +} + +AlembicObject::~AlembicObject() +{ +} + +void AlembicObject::set_object(Object *object_) +{ + object = object_; +} + +Object *AlembicObject::get_object() +{ + return object; +} + +bool AlembicObject::has_data_loaded() const +{ + return data_loaded; +} + +void AlembicObject::update_shader_attributes(const ICompoundProperty &arb_geom_params, + Progress &progress) +{ + AttributeRequestSet requested_attributes = get_requested_attributes(); + + foreach (const AttributeRequest &attr, requested_attributes.requests) { + if (progress.get_cancel()) { + return; + } + + bool attr_exists = false; + foreach (CachedData::CachedAttribute &cached_attr, cached_data.attributes) { + if (cached_attr.name == attr.name) { + attr_exists = true; + break; + } + } + + if (attr_exists) { + continue; + } + + read_attribute(arb_geom_params, attr.name, progress); + } + + cached_data.invalidate_last_loaded_time(true); + need_shader_update = false; +} + +template<typename SchemaType> +void AlembicObject::read_face_sets(SchemaType &schema, + array<int> &polygon_to_shader, + ISampleSelector sample_sel) +{ + std::vector<std::string> face_sets; + schema.getFaceSetNames(face_sets); + + if (face_sets.empty()) { + return; + } + + const Int32ArraySamplePtr face_counts = schema.getFaceCountsProperty().getValue(); + + polygon_to_shader.resize(face_counts->size()); + + foreach (const std::string &face_set_name, face_sets) { + int shader_index = 0; + + foreach (Node *node, get_used_shaders()) { + if (node->name == face_set_name) { + break; + } + + ++shader_index; + } + + if (shader_index >= get_used_shaders().size()) { + /* use the first shader instead if none was found */ + shader_index = 0; + } + + const IFaceSet face_set = schema.getFaceSet(face_set_name); + + if (!face_set.valid()) { + continue; + } + + const IFaceSetSchema face_schem = face_set.getSchema(); + const IFaceSetSchema::Sample face_sample = face_schem.getValue(sample_sel); + const Int32ArraySamplePtr group_faces = face_sample.getFaces(); + const size_t num_group_faces = group_faces->size(); + + for (size_t l = 0; l < num_group_faces; l++) { + size_t pos = (*group_faces)[l]; + + if (pos >= polygon_to_shader.size()) { + continue; + } + + polygon_to_shader[pos] = shader_index; + } + } +} + +void AlembicObject::load_all_data(AlembicProcedural *proc, + IPolyMeshSchema &schema, + float scale, + Progress &progress) +{ + cached_data.clear(); + + const TimeSamplingPtr time_sampling = schema.getTimeSampling(); + cached_data.set_time_sampling(*time_sampling); + + const IN3fGeomParam &normals = schema.getNormalsParam(); + + ccl::set<chrono_t> times = get_relevant_sample_times( + proc, *time_sampling, schema.getNumSamples()); + + /* read topology */ + foreach (chrono_t time, times) { + if (progress.get_cancel()) { + return; + } + + const ISampleSelector iss = ISampleSelector(time); + const IPolyMeshSchema::Sample sample = schema.getValue(iss); + + add_positions(sample.getPositions(), time, cached_data); + + /* Only copy triangles for other frames if the topology is changing over time as well. + * + * TODO(@kevindietrich): even for dynamic simulations, this is a waste of memory and + * processing time if only the positions are changing in a subsequence of frames but we + * cannot optimize in this current system if the attributes are changing over time as well, + * as we need valid data for each time point. This can be solved by using reference counting + * on the ccl::array and simply share the array across frames. */ + if (schema.getTopologyVariance() != kHomogenousTopology || cached_data.triangles.size() == 0) { + /* start by reading the face sets (per face shader), as we directly split polygons to + * triangles + */ + array<int> polygon_to_shader; + read_face_sets(schema, polygon_to_shader, iss); + + add_triangles( + sample.getFaceCounts(), sample.getFaceIndices(), time, cached_data, polygon_to_shader); + } + + if (normals.valid()) { + add_normals(sample.getFaceIndices(), normals, time, cached_data); + } + } + + if (progress.get_cancel()) { + return; + } + + update_shader_attributes(schema.getArbGeomParams(), progress); + + if (progress.get_cancel()) { + return; + } + + const IV2fGeomParam &uvs = schema.getUVsParam(); + + if (uvs.valid()) { + add_uvs(proc, uvs, cached_data, progress); + } + + if (progress.get_cancel()) { + return; + } + + setup_transform_cache(scale); + + data_loaded = true; +} + +void AlembicObject::load_all_data(AlembicProcedural *proc, + ISubDSchema &schema, + float scale, + Progress &progress) +{ + cached_data.clear(); + + AttributeRequestSet requested_attributes = get_requested_attributes(); + + const TimeSamplingPtr time_sampling = schema.getTimeSampling(); + cached_data.set_time_sampling(*time_sampling); + + ccl::set<chrono_t> times = get_relevant_sample_times( + proc, *time_sampling, schema.getNumSamples()); + + /* read topology */ + foreach (chrono_t time, times) { + if (progress.get_cancel()) { + return; + } + + const ISampleSelector iss = ISampleSelector(time); + const ISubDSchema::Sample sample = schema.getValue(iss); + + add_positions(sample.getPositions(), time, cached_data); + + const Int32ArraySamplePtr face_counts = sample.getFaceCounts(); + const Int32ArraySamplePtr face_indices = sample.getFaceIndices(); + + /* start by reading the face sets (per face shader) */ + array<int> polygon_to_shader; + read_face_sets(schema, polygon_to_shader, iss); + + /* read faces */ + array<int> subd_start_corner; + array<int> shader; + array<int> subd_num_corners; + array<bool> subd_smooth; + array<int> subd_ptex_offset; + array<int> subd_face_corners; + + const size_t num_faces = face_counts->size(); + const int *face_counts_array = face_counts->get(); + const int *face_indices_array = face_indices->get(); + + int num_ngons = 0; + int num_corners = 0; + for (size_t i = 0; i < face_counts->size(); i++) { + num_ngons += (face_counts_array[i] == 4 ? 0 : 1); + num_corners += face_counts_array[i]; + } + + subd_start_corner.reserve(num_faces); + subd_num_corners.reserve(num_faces); + subd_smooth.reserve(num_faces); + subd_ptex_offset.reserve(num_faces); + shader.reserve(num_faces); + subd_face_corners.reserve(num_corners); + + int start_corner = 0; + int current_shader = 0; + int ptex_offset = 0; + + for (size_t i = 0; i < face_counts->size(); i++) { + num_corners = face_counts_array[i]; + + if (!polygon_to_shader.empty()) { + current_shader = polygon_to_shader[i]; + } + + subd_start_corner.push_back_reserved(start_corner); + subd_num_corners.push_back_reserved(num_corners); + + for (int j = 0; j < num_corners; ++j) { + subd_face_corners.push_back_reserved(face_indices_array[start_corner + j]); + } + + shader.push_back_reserved(current_shader); + subd_smooth.push_back_reserved(1); + subd_ptex_offset.push_back_reserved(ptex_offset); + + ptex_offset += (num_corners == 4 ? 1 : num_corners); + + start_corner += num_corners; + } + + cached_data.shader.add_data(shader, time); + cached_data.subd_start_corner.add_data(subd_start_corner, time); + cached_data.subd_num_corners.add_data(subd_num_corners, time); + cached_data.subd_smooth.add_data(subd_smooth, time); + cached_data.subd_ptex_offset.add_data(subd_ptex_offset, time); + cached_data.subd_face_corners.add_data(subd_face_corners, time); + cached_data.num_ngons.add_data(num_ngons, time); + + /* read creases */ + Int32ArraySamplePtr creases_length = sample.getCreaseLengths(); + Int32ArraySamplePtr creases_indices = sample.getCreaseIndices(); + FloatArraySamplePtr creases_sharpnesses = sample.getCreaseSharpnesses(); + + if (creases_length && creases_indices && creases_sharpnesses) { + array<int> creases_edge; + array<float> creases_weight; + + creases_edge.reserve(creases_sharpnesses->size() * 2); + creases_weight.reserve(creases_sharpnesses->size()); + + int length_offset = 0; + int weight_offset = 0; + for (size_t c = 0; c < creases_length->size(); ++c) { + const int crease_length = creases_length->get()[c]; + + for (size_t j = 0; j < crease_length - 1; ++j) { + creases_edge.push_back_reserved(creases_indices->get()[length_offset + j]); + creases_edge.push_back_reserved(creases_indices->get()[length_offset + j + 1]); + creases_weight.push_back_reserved(creases_sharpnesses->get()[weight_offset++]); + } + + length_offset += crease_length; + } + + cached_data.subd_creases_edge.add_data(creases_edge, time); + cached_data.subd_creases_weight.add_data(creases_weight, time); + } + } + + /* TODO(@kevindietrich) : attributes, need test files */ + + if (progress.get_cancel()) { + return; + } + + setup_transform_cache(scale); + + data_loaded = true; +} + +void AlembicObject::load_all_data(AlembicProcedural *proc, + const ICurvesSchema &schema, + float scale, + Progress &progress, + float default_radius) +{ + cached_data.clear(); + + const TimeSamplingPtr time_sampling = schema.getTimeSampling(); + cached_data.set_time_sampling(*time_sampling); + + ccl::set<chrono_t> times = get_relevant_sample_times( + proc, *time_sampling, schema.getNumSamples()); + + foreach (chrono_t time, times) { + if (progress.get_cancel()) { + return; + } + + const ISampleSelector iss = ISampleSelector(time); + const ICurvesSchema::Sample sample = schema.getValue(iss); + + const Int32ArraySamplePtr curves_num_vertices = sample.getCurvesNumVertices(); + const P3fArraySamplePtr position = sample.getPositions(); + + const IFloatGeomParam widths_param = schema.getWidthsParam(); + FloatArraySamplePtr radiuses; + + if (widths_param.valid()) { + IFloatGeomParam::Sample wsample = widths_param.getExpandedValue(iss); + radiuses = wsample.getVals(); + } + + const bool do_radius = (radiuses != nullptr) && (radiuses->size() > 1); + float radius = (radiuses && radiuses->size() == 1) ? (*radiuses)[0] : default_radius; + + array<float3> curve_keys; + array<float> curve_radius; + array<int> curve_first_key; + array<int> curve_shader; + + curve_keys.reserve(position->size()); + curve_radius.reserve(position->size()); + curve_first_key.reserve(curves_num_vertices->size()); + curve_shader.reserve(curves_num_vertices->size()); + + int offset = 0; + for (size_t i = 0; i < curves_num_vertices->size(); i++) { + const int num_vertices = curves_num_vertices->get()[i]; + + for (int j = 0; j < num_vertices; j++) { + const V3f &f = position->get()[offset + j]; + curve_keys.push_back_reserved(make_float3_from_yup(f)); + + if (do_radius) { + radius = (*radiuses)[offset + j]; + } + + curve_radius.push_back_reserved(radius * radius_scale); + } + + curve_first_key.push_back_reserved(offset); + curve_shader.push_back_reserved(0); + + offset += num_vertices; + } + + cached_data.curve_keys.add_data(curve_keys, time); + cached_data.curve_radius.add_data(curve_radius, time); + cached_data.curve_first_key.add_data(curve_first_key, time); + cached_data.curve_shader.add_data(curve_shader, time); + } + + // TODO(@kevindietrich): attributes, need example files + + setup_transform_cache(scale); + + data_loaded = true; +} + +void AlembicObject::setup_transform_cache(float scale) +{ + cached_data.transforms.clear(); + cached_data.transforms.invalidate_last_loaded_time(); + + if (xform_samples.size() == 0) { + Transform tfm = transform_scale(make_float3(scale)); + cached_data.transforms.add_data(tfm, 0.0); + } + else { + /* It is possible for a leaf node of the hierarchy to have multiple samples for its transforms + * if a sibling has animated transforms. So check if we indeed have animated transformations. + */ + M44d first_matrix = xform_samples.begin()->first; + bool has_animation = false; + for (const std::pair<chrono_t, M44d> pair : xform_samples) { + if (pair.second != first_matrix) { + has_animation = true; + break; + } + } + + if (!has_animation) { + Transform tfm = make_transform(first_matrix, scale); + cached_data.transforms.add_data(tfm, 0.0); + } + else { + for (const std::pair<chrono_t, M44d> pair : xform_samples) { + Transform tfm = make_transform(pair.second, scale); + cached_data.transforms.add_data(tfm, pair.first); + } + } + } +} + +AttributeRequestSet AlembicObject::get_requested_attributes() +{ + AttributeRequestSet requested_attributes; + + Geometry *geometry = object->get_geometry(); + assert(geometry); + + foreach (Node *node, geometry->get_used_shaders()) { + Shader *shader = static_cast<Shader *>(node); + + foreach (const AttributeRequest &attr, shader->attributes.requests) { + if (attr.name != "") { + requested_attributes.add(attr.name); + } + } + } + + return requested_attributes; +} + +void AlembicObject::read_attribute(const ICompoundProperty &arb_geom_params, + const ustring &attr_name, + Progress &progress) +{ + const PropertyHeader *prop = arb_geom_params.getPropertyHeader(attr_name.c_str()); + + if (prop == nullptr) { + return; + } + + if (IV2fProperty::matches(prop->getMetaData()) && Alembic::AbcGeom::isUV(*prop)) { + const IV2fGeomParam ¶m = IV2fGeomParam(arb_geom_params, prop->getName()); + + CachedData::CachedAttribute &attribute = cached_data.add_attribute(attr_name, + *param.getTimeSampling()); + + for (size_t i = 0; i < param.getNumSamples(); ++i) { + if (progress.get_cancel()) { + return; + } + + ISampleSelector iss = ISampleSelector(index_t(i)); + + IV2fGeomParam::Sample sample; + param.getIndexed(sample, iss); + + const chrono_t time = param.getTimeSampling()->getSampleTime(index_t(i)); + + if (param.getScope() == kFacevaryingScope) { + V2fArraySamplePtr values = sample.getVals(); + UInt32ArraySamplePtr indices = sample.getIndices(); + + attribute.std = ATTR_STD_NONE; + attribute.element = ATTR_ELEMENT_CORNER; + attribute.type_desc = TypeFloat2; + + const array<int3> *triangles = cached_data.triangles.data_for_time_no_check(time); + const array<int3> *triangles_loops = cached_data.triangles_loops.data_for_time_no_check( + time); + + if (!triangles || !triangles_loops) { + return; + } + + array<char> data; + data.resize(triangles->size() * 3 * sizeof(float2)); + + float2 *data_float2 = reinterpret_cast<float2 *>(data.data()); + + for (const int3 &loop : *triangles_loops) { + unsigned int v0 = (*indices)[loop.x]; + unsigned int v1 = (*indices)[loop.y]; + unsigned int v2 = (*indices)[loop.z]; + + data_float2[0] = make_float2((*values)[v0][0], (*values)[v0][1]); + data_float2[1] = make_float2((*values)[v1][0], (*values)[v1][1]); + data_float2[2] = make_float2((*values)[v2][0], (*values)[v2][1]); + data_float2 += 3; + } + + attribute.data.set_time_sampling(*param.getTimeSampling()); + attribute.data.add_data(data, time); + } + } + } + else if (IC3fProperty::matches(prop->getMetaData())) { + const IC3fGeomParam ¶m = IC3fGeomParam(arb_geom_params, prop->getName()); + + CachedData::CachedAttribute &attribute = cached_data.add_attribute(attr_name, + *param.getTimeSampling()); + + for (size_t i = 0; i < param.getNumSamples(); ++i) { + if (progress.get_cancel()) { + return; + } + + ISampleSelector iss = ISampleSelector(index_t(i)); + + IC3fGeomParam::Sample sample; + param.getIndexed(sample, iss); + + const chrono_t time = param.getTimeSampling()->getSampleTime(index_t(i)); + + C3fArraySamplePtr values = sample.getVals(); + + attribute.std = ATTR_STD_NONE; + + if (param.getScope() == kVaryingScope) { + attribute.element = ATTR_ELEMENT_CORNER_BYTE; + attribute.type_desc = TypeRGBA; + + const array<int3> *triangles = cached_data.triangles.data_for_time_no_check(time); + + if (!triangles) { + return; + } + + array<char> data; + data.resize(triangles->size() * 3 * sizeof(uchar4)); + + uchar4 *data_uchar4 = reinterpret_cast<uchar4 *>(data.data()); + + int offset = 0; + for (const int3 &tri : *triangles) { + Imath::C3f v = (*values)[tri.x]; + data_uchar4[offset + 0] = color_float_to_byte(make_float3(v.x, v.y, v.z)); + + v = (*values)[tri.y]; + data_uchar4[offset + 1] = color_float_to_byte(make_float3(v.x, v.y, v.z)); + + v = (*values)[tri.z]; + data_uchar4[offset + 2] = color_float_to_byte(make_float3(v.x, v.y, v.z)); + + offset += 3; + } + + attribute.data.set_time_sampling(*param.getTimeSampling()); + attribute.data.add_data(data, time); + } + } + } + else if (IC4fProperty::matches(prop->getMetaData())) { + const IC4fGeomParam ¶m = IC4fGeomParam(arb_geom_params, prop->getName()); + + CachedData::CachedAttribute &attribute = cached_data.add_attribute(attr_name, + *param.getTimeSampling()); + + for (size_t i = 0; i < param.getNumSamples(); ++i) { + if (progress.get_cancel()) { + return; + } + + ISampleSelector iss = ISampleSelector(index_t(i)); + + IC4fGeomParam::Sample sample; + param.getIndexed(sample, iss); + + const chrono_t time = param.getTimeSampling()->getSampleTime(index_t(i)); + + C4fArraySamplePtr values = sample.getVals(); + + attribute.std = ATTR_STD_NONE; + + if (param.getScope() == kVaryingScope) { + attribute.element = ATTR_ELEMENT_CORNER_BYTE; + attribute.type_desc = TypeRGBA; + + const array<int3> *triangles = cached_data.triangles.data_for_time_no_check(time); + + if (!triangles) { + return; + } + + array<char> data; + data.resize(triangles->size() * 3 * sizeof(uchar4)); + + uchar4 *data_uchar4 = reinterpret_cast<uchar4 *>(data.data()); + + int offset = 0; + for (const int3 &tri : *triangles) { + Imath::C4f v = (*values)[tri.x]; + data_uchar4[offset + 0] = color_float4_to_uchar4(make_float4(v.r, v.g, v.b, v.a)); + + v = (*values)[tri.y]; + data_uchar4[offset + 1] = color_float4_to_uchar4(make_float4(v.r, v.g, v.b, v.a)); + + v = (*values)[tri.z]; + data_uchar4[offset + 2] = color_float4_to_uchar4(make_float4(v.r, v.g, v.b, v.a)); + + offset += 3; + } + + attribute.data.set_time_sampling(*param.getTimeSampling()); + attribute.data.add_data(data, time); + } + } + } +} + +/* Update existing attributes and remove any attribute not in the cached_data, those attributes + * were added by Cycles (e.g. face normals) */ +static void update_attributes(AttributeSet &attributes, CachedData &cached_data, double frame_time) +{ + set<Attribute *> cached_attributes; + + for (CachedData::CachedAttribute &attribute : cached_data.attributes) { + const array<char> *attr_data = attribute.data.data_for_time(frame_time); + + Attribute *attr = nullptr; + if (attribute.std != ATTR_STD_NONE) { + attr = attributes.add(attribute.std, attribute.name); + } + else { + attr = attributes.add(attribute.name, attribute.type_desc, attribute.element); + } + assert(attr); + + cached_attributes.insert(attr); + + if (!attr_data) { + /* no new data */ + continue; + } + + /* weak way of detecting if the topology has changed + * todo: reuse code from device_update patch */ + if (attr->buffer.size() != attr_data->size()) { + attr->buffer.resize(attr_data->size()); + } + + memcpy(attr->data(), attr_data->data(), attr_data->size()); + } + + /* remove any attributes not in cached_attributes */ + list<Attribute>::iterator it; + for (it = attributes.attributes.begin(); it != attributes.attributes.end();) { + if (cached_attributes.find(&(*it)) == cached_attributes.end()) { + attributes.attributes.erase(it++); + continue; + } + + it++; + } +} + +NODE_DEFINE(AlembicProcedural) +{ + NodeType *type = NodeType::add("alembic", create); + + SOCKET_STRING(filepath, "Filename", ustring()); + SOCKET_FLOAT(frame, "Frame", 1.0f); + SOCKET_FLOAT(start_frame, "Start Frame", 1.0f); + SOCKET_FLOAT(end_frame, "End Frame", 1.0f); + SOCKET_FLOAT(frame_rate, "Frame Rate", 24.0f); + SOCKET_FLOAT(frame_offset, "Frame Offset", 0.0f); + SOCKET_FLOAT(default_radius, "Default Radius", 0.01f); + SOCKET_FLOAT(scale, "Scale", 1.0f); + + SOCKET_NODE_ARRAY(objects, "Objects", &AlembicObject::node_type); + + return type; +} + +AlembicProcedural::AlembicProcedural() : Procedural(node_type) +{ + objects_loaded = false; + scene_ = nullptr; +} + +AlembicProcedural::~AlembicProcedural() +{ + ccl::set<Geometry *> geometries_set; + ccl::set<Object *> objects_set; + ccl::set<AlembicObject *> abc_objects_set; + + foreach (Node *node, objects) { + AlembicObject *abc_object = static_cast<AlembicObject *>(node); + + objects_set.insert(abc_object->get_object()); + geometries_set.insert(abc_object->get_object()->get_geometry()); + + delete_node(abc_object); + } + + scene_->delete_nodes(geometries_set, this); + scene_->delete_nodes(objects_set, this); +} + +void AlembicProcedural::generate(Scene *scene, Progress &progress) +{ + assert(scene_ == nullptr || scene_ == scene); + scene_ = scene; + + bool need_shader_updates = false; + + /* check for changes in shaders (newly requested atttributes) */ + foreach (Node *object_node, objects) { + AlembicObject *object = static_cast<AlembicObject *>(object_node); + + foreach (Node *shader_node, object->get_used_shaders()) { + Shader *shader = static_cast<Shader *>(shader_node); + + if (shader->need_update_geometry()) { + object->need_shader_update = true; + need_shader_updates = true; + } + } + } + + if (!is_modified() && !need_shader_updates) { + return; + } + + if (!archive.valid()) { + Alembic::AbcCoreFactory::IFactory factory; + factory.setPolicy(Alembic::Abc::ErrorHandler::kQuietNoopPolicy); + archive = factory.getArchive(filepath.c_str()); + + if (!archive.valid()) { + /* avoid potential infinite update loops in viewport synchronization */ + filepath.clear(); + clear_modified(); + return; + } + } + + if (!objects_loaded || objects_is_modified()) { + load_objects(progress); + objects_loaded = true; + } + + const chrono_t frame_time = (chrono_t)((frame - frame_offset) / frame_rate); + + foreach (Node *node, objects) { + AlembicObject *object = static_cast<AlembicObject *>(node); + + if (progress.get_cancel()) { + return; + } + + /* skip constant objects */ + if (object->has_data_loaded() && object->is_constant() && !object->is_modified() && + !object->need_shader_update && !scale_is_modified()) { + continue; + } + + if (IPolyMesh::matches(object->iobject.getHeader())) { + read_mesh(scene, object, frame_time, progress); + } + else if (ICurves::matches(object->iobject.getHeader())) { + read_curves(scene, object, frame_time, progress); + } + else if (ISubD::matches(object->iobject.getHeader())) { + read_subd(scene, object, frame_time, progress); + } + + object->clear_modified(); + } + + clear_modified(); +} + +void AlembicProcedural::add_object(AlembicObject *object) +{ + objects.push_back_slow(object); + tag_objects_modified(); +} + +void AlembicProcedural::tag_update(Scene *scene) +{ + scene->procedural_manager->tag_update(); +} + +AlembicObject *AlembicProcedural::get_or_create_object(const ustring &path) +{ + foreach (Node *node, objects) { + AlembicObject *object = static_cast<AlembicObject *>(node); + + if (object->get_path() == path) { + return object; + } + } + + AlembicObject *object = create_node<AlembicObject>(); + object->set_path(path); + + add_object(object); + + return object; +} + +void AlembicProcedural::load_objects(Progress &progress) +{ + unordered_map<string, AlembicObject *> object_map; + + foreach (Node *node, objects) { + AlembicObject *object = static_cast<AlembicObject *>(node); + + /* only consider newly added objects */ + if (object->get_object() == nullptr) { + object_map.insert({object->get_path().c_str(), object}); + } + } + + IObject root = archive.getTop(); + + for (size_t i = 0; i < root.getNumChildren(); ++i) { + walk_hierarchy(root, root.getChildHeader(i), nullptr, object_map, progress); + } +} + +void AlembicProcedural::read_mesh(Scene *scene, + AlembicObject *abc_object, + Abc::chrono_t frame_time, + Progress &progress) +{ + IPolyMesh polymesh(abc_object->iobject, Alembic::Abc::kWrapExisting); + + Mesh *mesh = nullptr; + + /* create a mesh node in the scene if not already done */ + if (!abc_object->get_object()) { + mesh = scene->create_node<Mesh>(); + mesh->set_owner(this); + mesh->name = abc_object->iobject.getName(); + + array<Node *> used_shaders = abc_object->get_used_shaders(); + mesh->set_used_shaders(used_shaders); + + /* create object*/ + Object *object = scene->create_node<Object>(); + object->set_owner(this); + object->set_geometry(mesh); + object->set_tfm(abc_object->xform); + object->name = abc_object->iobject.getName(); + + abc_object->set_object(object); + } + else { + mesh = static_cast<Mesh *>(abc_object->get_object()->get_geometry()); + } + + CachedData &cached_data = abc_object->get_cached_data(); + IPolyMeshSchema schema = polymesh.getSchema(); + + if (!abc_object->has_data_loaded()) { + abc_object->load_all_data(this, schema, scale, progress); + } + else { + if (abc_object->need_shader_update) { + abc_object->update_shader_attributes(schema.getArbGeomParams(), progress); + } + + if (scale_is_modified()) { + abc_object->setup_transform_cache(scale); + } + } + + /* update sockets */ + + Object *object = abc_object->get_object(); + cached_data.transforms.copy_to_socket(frame_time, object, object->get_tfm_socket()); + + cached_data.vertices.copy_to_socket(frame_time, mesh, mesh->get_verts_socket()); + + cached_data.shader.copy_to_socket(frame_time, mesh, mesh->get_shader_socket()); + + array<int3> *triangle_data = cached_data.triangles.data_for_time(frame_time); + if (triangle_data) { + array<int> triangles; + array<bool> smooth; + + triangles.reserve(triangle_data->size() * 3); + smooth.reserve(triangle_data->size()); + + for (size_t i = 0; i < triangle_data->size(); ++i) { + int3 tri = (*triangle_data)[i]; + triangles.push_back_reserved(tri.x); + triangles.push_back_reserved(tri.y); + triangles.push_back_reserved(tri.z); + smooth.push_back_reserved(1); + } + + mesh->set_triangles(triangles); + mesh->set_smooth(smooth); + } + + /* update attributes */ + + update_attributes(mesh->attributes, cached_data, frame_time); + + /* we don't yet support arbitrary attributes, for now add vertex + * coordinates as generated coordinates if requested */ + if (mesh->need_attribute(scene, ATTR_STD_GENERATED)) { + Attribute *attr = mesh->attributes.add(ATTR_STD_GENERATED); + memcpy( + attr->data_float3(), mesh->get_verts().data(), sizeof(float3) * mesh->get_verts().size()); + } + + if (mesh->is_modified()) { + bool need_rebuild = mesh->triangles_is_modified(); + mesh->tag_update(scene, need_rebuild); + } +} + +void AlembicProcedural::read_subd(Scene *scene, + AlembicObject *abc_object, + Abc::chrono_t frame_time, + Progress &progress) +{ + ISubD subd_mesh(abc_object->iobject, Alembic::Abc::kWrapExisting); + ISubDSchema schema = subd_mesh.getSchema(); + + Mesh *mesh = nullptr; + + /* create a mesh node in the scene if not already done */ + if (!abc_object->get_object()) { + mesh = scene->create_node<Mesh>(); + mesh->set_owner(this); + mesh->name = abc_object->iobject.getName(); + + array<Node *> used_shaders = abc_object->get_used_shaders(); + mesh->set_used_shaders(used_shaders); + + /* Alembic is OpenSubDiv compliant, there is no option to set another subdivision type. */ + mesh->set_subdivision_type(Mesh::SubdivisionType::SUBDIVISION_CATMULL_CLARK); + + /* create object*/ + Object *object = scene->create_node<Object>(); + object->set_owner(this); + object->set_geometry(mesh); + object->set_tfm(abc_object->xform); + object->name = abc_object->iobject.getName(); + + abc_object->set_object(object); + } + else { + mesh = static_cast<Mesh *>(abc_object->get_object()->get_geometry()); + } + + if (!abc_object->has_data_loaded()) { + abc_object->load_all_data(this, schema, scale, progress); + } + else { + if (abc_object->need_shader_update) { + abc_object->update_shader_attributes(schema.getArbGeomParams(), progress); + } + + if (scale_is_modified()) { + abc_object->setup_transform_cache(scale); + } + } + + mesh->set_subd_max_level(abc_object->get_subd_max_level()); + mesh->set_subd_dicing_rate(abc_object->get_subd_dicing_rate()); + + CachedData &cached_data = abc_object->get_cached_data(); + + if (abc_object->subd_max_level_is_modified() || abc_object->subd_dicing_rate_is_modified()) { + /* need to reset the current data is something changed */ + cached_data.invalidate_last_loaded_time(); + } + + /* Cycles overwrites the original triangles when computing displacement, so we always have to + * repass the data if something is animated (vertices most likely) to avoid buffer overflows. */ + if (!cached_data.is_constant()) { + cached_data.invalidate_last_loaded_time(); + + /* remove previous triangles, if any */ + array<int> triangles; + mesh->set_triangles(triangles); + } + + mesh->clear_non_sockets(); + + /* udpate sockets */ + + Object *object = abc_object->get_object(); + cached_data.transforms.copy_to_socket(frame_time, object, object->get_tfm_socket()); + + cached_data.vertices.copy_to_socket(frame_time, mesh, mesh->get_verts_socket()); + + /* cached_data.shader is also used for subd_shader */ + cached_data.shader.copy_to_socket(frame_time, mesh, mesh->get_subd_shader_socket()); + + cached_data.subd_start_corner.copy_to_socket( + frame_time, mesh, mesh->get_subd_start_corner_socket()); + + cached_data.subd_num_corners.copy_to_socket( + frame_time, mesh, mesh->get_subd_num_corners_socket()); + + cached_data.subd_smooth.copy_to_socket(frame_time, mesh, mesh->get_subd_smooth_socket()); + + cached_data.subd_ptex_offset.copy_to_socket( + frame_time, mesh, mesh->get_subd_ptex_offset_socket()); + + cached_data.subd_face_corners.copy_to_socket( + frame_time, mesh, mesh->get_subd_face_corners_socket()); + + cached_data.num_ngons.copy_to_socket(frame_time, mesh, mesh->get_num_ngons_socket()); + + cached_data.subd_creases_edge.copy_to_socket( + frame_time, mesh, mesh->get_subd_creases_edge_socket()); + + cached_data.subd_creases_weight.copy_to_socket( + frame_time, mesh, mesh->get_subd_creases_weight_socket()); + + mesh->set_num_subd_faces(mesh->get_subd_shader().size()); + + /* udpate attributes */ + + update_attributes(mesh->subd_attributes, cached_data, frame_time); + + /* we don't yet support arbitrary attributes, for now add vertex + * coordinates as generated coordinates if requested */ + if (mesh->need_attribute(scene, ATTR_STD_GENERATED)) { + Attribute *attr = mesh->attributes.add(ATTR_STD_GENERATED); + memcpy( + attr->data_float3(), mesh->get_verts().data(), sizeof(float3) * mesh->get_verts().size()); + } + + if (mesh->is_modified()) { + bool need_rebuild = (mesh->triangles_is_modified()) || + (mesh->subd_num_corners_is_modified()) || + (mesh->subd_shader_is_modified()) || (mesh->subd_smooth_is_modified()) || + (mesh->subd_ptex_offset_is_modified()) || + (mesh->subd_start_corner_is_modified()) || + (mesh->subd_face_corners_is_modified()); + + mesh->tag_update(scene, need_rebuild); + } +} + +void AlembicProcedural::read_curves(Scene *scene, + AlembicObject *abc_object, + Abc::chrono_t frame_time, + Progress &progress) +{ + ICurves curves(abc_object->iobject, Alembic::Abc::kWrapExisting); + Hair *hair; + + /* create a hair node in the scene if not already done */ + if (!abc_object->get_object()) { + hair = scene->create_node<Hair>(); + hair->set_owner(this); + hair->name = abc_object->iobject.getName(); + + array<Node *> used_shaders = abc_object->get_used_shaders(); + hair->set_used_shaders(used_shaders); + + /* create object*/ + Object *object = scene->create_node<Object>(); + object->set_owner(this); + object->set_geometry(hair); + object->set_tfm(abc_object->xform); + object->name = abc_object->iobject.getName(); + + abc_object->set_object(object); + } + else { + hair = static_cast<Hair *>(abc_object->get_object()->get_geometry()); + } + + ICurvesSchema schema = curves.getSchema(); + + if (!abc_object->has_data_loaded() || default_radius_is_modified() || + abc_object->radius_scale_is_modified()) { + abc_object->load_all_data(this, schema, scale, progress, default_radius); + } + else { + if (scale_is_modified()) { + abc_object->setup_transform_cache(scale); + } + } + + CachedData &cached_data = abc_object->get_cached_data(); + + /* update sockets */ + + Object *object = abc_object->get_object(); + cached_data.transforms.copy_to_socket(frame_time, object, object->get_tfm_socket()); + + cached_data.curve_keys.copy_to_socket(frame_time, hair, hair->get_curve_keys_socket()); + + cached_data.curve_radius.copy_to_socket(frame_time, hair, hair->get_curve_radius_socket()); + + cached_data.curve_shader.copy_to_socket(frame_time, hair, hair->get_curve_shader_socket()); + + cached_data.curve_first_key.copy_to_socket(frame_time, hair, hair->get_curve_first_key_socket()); + + /* update attributes */ + + update_attributes(hair->attributes, cached_data, frame_time); + + /* we don't yet support arbitrary attributes, for now add first keys as generated coordinates if + * requested */ + if (hair->need_attribute(scene, ATTR_STD_GENERATED)) { + Attribute *attr_generated = hair->attributes.add(ATTR_STD_GENERATED); + float3 *generated = attr_generated->data_float3(); + + for (size_t i = 0; i < hair->num_curves(); i++) { + generated[i] = hair->get_curve_keys()[hair->get_curve(i).first_key]; + } + } + + const bool rebuild = (hair->curve_keys_is_modified() || hair->curve_radius_is_modified()); + hair->tag_update(scene, rebuild); +} + +void AlembicProcedural::walk_hierarchy( + IObject parent, + const ObjectHeader &header, + MatrixSampleMap *xform_samples, + const unordered_map<std::string, AlembicObject *> &object_map, + Progress &progress) +{ + if (progress.get_cancel()) { + return; + } + + IObject next_object; + + MatrixSampleMap concatenated_xform_samples; + + if (IXform::matches(header)) { + IXform xform(parent, header.getName()); + + IXformSchema &xs = xform.getSchema(); + + if (xs.getNumOps() > 0) { + TimeSamplingPtr ts = xs.getTimeSampling(); + MatrixSampleMap local_xform_samples; + + MatrixSampleMap *temp_xform_samples = nullptr; + if (xform_samples == nullptr) { + /* If there is no parent transforms, fill the map directly. */ + temp_xform_samples = &concatenated_xform_samples; + } + else { + /* use a temporary map */ + temp_xform_samples = &local_xform_samples; + } + + for (size_t i = 0; i < xs.getNumSamples(); ++i) { + chrono_t sample_time = ts->getSampleTime(index_t(i)); + XformSample sample = xs.getValue(ISampleSelector(sample_time)); + temp_xform_samples->insert({sample_time, sample.getMatrix()}); + } + + if (xform_samples != nullptr) { + concatenate_xform_samples(*xform_samples, local_xform_samples, concatenated_xform_samples); + } + + xform_samples = &concatenated_xform_samples; + } + + next_object = xform; + } + else if (ISubD::matches(header)) { + ISubD subd(parent, header.getName()); + + unordered_map<std::string, AlembicObject *>::const_iterator iter; + iter = object_map.find(subd.getFullName()); + + if (iter != object_map.end()) { + AlembicObject *abc_object = iter->second; + abc_object->iobject = subd; + + if (xform_samples) { + abc_object->xform_samples = *xform_samples; + } + } + + next_object = subd; + } + else if (IPolyMesh::matches(header)) { + IPolyMesh mesh(parent, header.getName()); + + unordered_map<std::string, AlembicObject *>::const_iterator iter; + iter = object_map.find(mesh.getFullName()); + + if (iter != object_map.end()) { + AlembicObject *abc_object = iter->second; + abc_object->iobject = mesh; + + if (xform_samples) { + abc_object->xform_samples = *xform_samples; + } + } + + next_object = mesh; + } + else if (ICurves::matches(header)) { + ICurves curves(parent, header.getName()); + + unordered_map<std::string, AlembicObject *>::const_iterator iter; + iter = object_map.find(curves.getFullName()); + + if (iter != object_map.end()) { + AlembicObject *abc_object = iter->second; + abc_object->iobject = curves; + + if (xform_samples) { + abc_object->xform_samples = *xform_samples; + } + } + + next_object = curves; + } + else if (IFaceSet::matches(header)) { + // ignore the face set, it will be read along with the data + } + else { + // unsupported type for now (Points, NuPatch) + next_object = parent.getChild(header.getName()); + } + + if (next_object.valid()) { + for (size_t i = 0; i < next_object.getNumChildren(); ++i) { + walk_hierarchy( + next_object, next_object.getChildHeader(i), xform_samples, object_map, progress); + } + } +} + +CCL_NAMESPACE_END + +#endif diff --git a/intern/cycles/render/alembic.h b/intern/cycles/render/alembic.h new file mode 100644 index 00000000000..77bdac09ba2 --- /dev/null +++ b/intern/cycles/render/alembic.h @@ -0,0 +1,404 @@ +/* + * 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. + */ + +#pragma once + +#include "graph/node.h" +#include "render/attribute.h" +#include "render/procedural.h" +#include "util/util_set.h" +#include "util/util_transform.h" +#include "util/util_vector.h" + +#ifdef WITH_ALEMBIC + +# include <Alembic/AbcCoreFactory/All.h> +# include <Alembic/AbcGeom/All.h> + +CCL_NAMESPACE_BEGIN + +class AlembicProcedural; +class Geometry; +class Object; +class Progress; +class Shader; + +using MatrixSampleMap = std::map<Alembic::Abc::chrono_t, Alembic::Abc::M44d>; + +/* Helpers to detect if some type is a ccl::array. */ +template<typename> struct is_array : public std::false_type { +}; + +template<typename T> struct is_array<array<T>> : public std::true_type { +}; + +/* Store the data set for an animation at every time points, or at the beginning of the animation + * for constant data. + * + * The data is supposed to be stored in chronological order, and is looked up using the current + * animation time in seconds using the TimeSampling from the Alembic property. */ +template<typename T> class DataStore { + struct DataTimePair { + double time = 0; + T data{}; + }; + + vector<DataTimePair> data{}; + Alembic::AbcCoreAbstract::TimeSampling time_sampling{}; + + double last_loaded_time = std::numeric_limits<double>::max(); + + public: + void set_time_sampling(Alembic::AbcCoreAbstract::TimeSampling time_sampling_) + { + time_sampling = time_sampling_; + } + + Alembic::AbcCoreAbstract::TimeSampling get_time_sampling() const + { + return time_sampling; + } + + /* Get the data for the specified time. + * Return nullptr if there is no data or if the data for this time was already loaded. */ + T *data_for_time(double time) + { + if (size() == 0) { + return nullptr; + } + + std::pair<size_t, Alembic::Abc::chrono_t> index_pair; + index_pair = time_sampling.getNearIndex(time, data.size()); + DataTimePair &data_pair = data[index_pair.first]; + + if (last_loaded_time == data_pair.time) { + return nullptr; + } + + last_loaded_time = data_pair.time; + + return &data_pair.data; + } + + /* get the data for the specified time, but do not check if the data was already loaded for this + * time return nullptr if there is no data */ + T *data_for_time_no_check(double time) + { + if (size() == 0) { + return nullptr; + } + + std::pair<size_t, Alembic::Abc::chrono_t> index_pair; + index_pair = time_sampling.getNearIndex(time, data.size()); + DataTimePair &data_pair = data[index_pair.first]; + return &data_pair.data; + } + + void add_data(T &data_, double time) + { + if constexpr (is_array<T>::value) { + data.emplace_back(); + data.back().data.steal_data(data_); + data.back().time = time; + return; + } + + data.push_back({time, data_}); + } + + bool is_constant() const + { + return data.size() <= 1; + } + + size_t size() const + { + return data.size(); + } + + void clear() + { + invalidate_last_loaded_time(); + data.clear(); + } + + void invalidate_last_loaded_time() + { + last_loaded_time = std::numeric_limits<double>::max(); + } + + /* Copy the data for the specified time to the node's socket. If there is no + * data for this time or it was already loaded, do nothing. */ + void copy_to_socket(double time, Node *node, const SocketType *socket) + { + T *data_ = data_for_time(time); + + if (data_ == nullptr) { + return; + } + + /* TODO(kevindietrich): arrays are emptied when passed to the sockets, so for now we copy the + * arrays to avoid reloading the data */ + T value = *data_; + node->set(*socket, value); + } +}; + +/* Actual cache for the stored data. + * This caches the topological, transformation, and attribute data for a Mesh node or a Hair node + * inside of DataStores. + */ +struct CachedData { + DataStore<Transform> transforms{}; + + /* mesh data */ + DataStore<array<float3>> vertices; + DataStore<array<int3>> triangles{}; + /* triangle "loops" are the polygons' vertices indices used for indexing face varying attributes + * (like UVs) */ + DataStore<array<int3>> triangles_loops{}; + DataStore<array<int>> shader{}; + + /* subd data */ + DataStore<array<int>> subd_start_corner; + DataStore<array<int>> subd_num_corners; + DataStore<array<bool>> subd_smooth; + DataStore<array<int>> subd_ptex_offset; + DataStore<array<int>> subd_face_corners; + DataStore<int> num_ngons; + DataStore<array<int>> subd_creases_edge; + DataStore<array<float>> subd_creases_weight; + + /* hair data */ + DataStore<array<float3>> curve_keys; + DataStore<array<float>> curve_radius; + DataStore<array<int>> curve_first_key; + DataStore<array<int>> curve_shader; + + struct CachedAttribute { + AttributeStandard std; + AttributeElement element; + TypeDesc type_desc; + ustring name; + DataStore<array<char>> data{}; + }; + + vector<CachedAttribute> attributes{}; + + void clear(); + + CachedAttribute &add_attribute(const ustring &name, + const Alembic::Abc::TimeSampling &time_sampling); + + bool is_constant() const; + + void invalidate_last_loaded_time(bool attributes_only = false); + + void set_time_sampling(Alembic::AbcCoreAbstract::TimeSampling time_sampling); +}; + +/* Representation of an Alembic object for the AlembicProcedural. + * + * The AlembicObject holds the path to the Alembic IObject inside of the archive that is desired + * for rendering, as well as the list of shaders that it is using. + * + * The names of the shaders should correspond to the names of the FaceSets inside of the Alembic + * archive for per-triangle shader association. If there is no FaceSets, or the names do not + * match, the first shader is used for rendering for all triangles. + */ +class AlembicObject : public Node { + public: + NODE_DECLARE + + /* Path to the IObject inside of the archive. */ + NODE_SOCKET_API(ustring, path) + + /* Shaders used for rendering. */ + NODE_SOCKET_API_ARRAY(array<Node *>, used_shaders) + + /* Maximum number of subdivisions for ISubD objects. */ + NODE_SOCKET_API(int, subd_max_level) + + /* Finest level of detail (in pixels) for the subdivision. */ + NODE_SOCKET_API(float, subd_dicing_rate) + + /* Scale the radius of points and curves. */ + NODE_SOCKET_API(float, radius_scale) + + AlembicObject(); + ~AlembicObject(); + + private: + friend class AlembicProcedural; + + void set_object(Object *object); + Object *get_object(); + + void load_all_data(AlembicProcedural *proc, + Alembic::AbcGeom::IPolyMeshSchema &schema, + float scale, + Progress &progress); + void load_all_data(AlembicProcedural *proc, + Alembic::AbcGeom::ISubDSchema &schema, + float scale, + Progress &progress); + void load_all_data(AlembicProcedural *proc, + const Alembic::AbcGeom::ICurvesSchema &schema, + float scale, + Progress &progress, + float default_radius); + + bool has_data_loaded() const; + + bool need_shader_update = true; + + MatrixSampleMap xform_samples; + Alembic::AbcGeom::IObject iobject; + Transform xform; + + CachedData &get_cached_data() + { + return cached_data; + } + + bool is_constant() const + { + return cached_data.is_constant(); + } + + Object *object = nullptr; + + bool data_loaded = false; + + CachedData cached_data; + + void update_shader_attributes(const Alembic::AbcGeom::ICompoundProperty &arb_geom_params, + Progress &progress); + + void read_attribute(const Alembic::AbcGeom::ICompoundProperty &arb_geom_params, + const ustring &attr_name, + Progress &progress); + + template<typename SchemaType> + void read_face_sets(SchemaType &schema, + array<int> &polygon_to_shader, + Alembic::AbcGeom::ISampleSelector sample_sel); + + void setup_transform_cache(float scale); + + AttributeRequestSet get_requested_attributes(); +}; + +/* Procedural to render objects from a single Alembic archive. + * + * Every object desired to be rendered should be passed as an AlembicObject through the objects + * socket. + * + * This procedural will load the data set for the entire animation in memory on the first frame, + * and directly set the data for the new frames on the created Nodes if needed. This allows for + * faster updates between frames as it avoids reseeking the data on disk. + */ +class AlembicProcedural : public Procedural { + Alembic::AbcGeom::IArchive archive; + bool objects_loaded; + Scene *scene_; + + public: + NODE_DECLARE + + /* The file path to the Alembic archive */ + NODE_SOCKET_API(ustring, filepath) + + /* The current frame to render. */ + NODE_SOCKET_API(float, frame) + + /* The first frame to load data for. */ + NODE_SOCKET_API(float, start_frame) + + /* The last frame to load data for. */ + NODE_SOCKET_API(float, end_frame) + + /* Subtracted to the current frame. */ + NODE_SOCKET_API(float, frame_offset) + + /* The frame rate used for rendering in units of frames per second. */ + NODE_SOCKET_API(float, frame_rate) + + /* List of AlembicObjects to render. */ + NODE_SOCKET_API_ARRAY(array<Node *>, objects) + + /* Set the default radius to use for curves when the Alembic Curves Schemas do not have radius + * information. */ + NODE_SOCKET_API(float, default_radius) + + /* Multiplier to account for differences in default units for measuring objects in various + * software. */ + NODE_SOCKET_API(float, scale) + + AlembicProcedural(); + ~AlembicProcedural(); + + /* Populates the Cycles scene with Nodes for every contained AlembicObject on the first + * invocation, and updates the data on subsequent invocations if the frame changed. */ + void generate(Scene *scene, Progress &progress); + + /* Add an object to our list of objects, and tag the socket as modified. */ + void add_object(AlembicObject *object); + + /* Tag for an update only if something was modified. */ + void tag_update(Scene *scene); + + /* Returns a pointer to an exisiting or a newly created AlembicObject for the given path. */ + AlembicObject *get_or_create_object(const ustring &path); + + private: + /* Load the data for all the objects whose data has not yet been loaded. */ + void load_objects(Progress &progress); + + /* Traverse the Alembic hierarchy to lookup the IObjects for the AlembicObjects that were + * specified in our objects socket, and accumulate all of the transformations samples along the + * way for each IObject. */ + void walk_hierarchy(Alembic::AbcGeom::IObject parent, + const Alembic::AbcGeom::ObjectHeader &ohead, + MatrixSampleMap *xform_samples, + const unordered_map<string, AlembicObject *> &object_map, + Progress &progress); + + /* Read the data for an IPolyMesh at the specified frame_time. Creates corresponding Geometry and + * Object Nodes in the Cycles scene if none exist yet. */ + void read_mesh(Scene *scene, + AlembicObject *abc_object, + Alembic::AbcGeom::Abc::chrono_t frame_time, + Progress &progress); + + /* Read the data for an ICurves at the specified frame_time. Creates corresponding Geometry and + * Object Nodes in the Cycles scene if none exist yet. */ + void read_curves(Scene *scene, + AlembicObject *abc_object, + Alembic::AbcGeom::Abc::chrono_t frame_time, + Progress &progress); + + /* Read the data for an ISubD at the specified frame_time. Creates corresponding Geometry and + * Object Nodes in the Cycles scene if none exist yet. */ + void read_subd(Scene *scene, + AlembicObject *abc_object, + Alembic::AbcGeom::Abc::chrono_t frame_time, + Progress &progress); +}; + +CCL_NAMESPACE_END + +#endif diff --git a/intern/cycles/render/attribute.cpp b/intern/cycles/render/attribute.cpp index b478aae9ae2..ce4ae6e4295 100644 --- a/intern/cycles/render/attribute.cpp +++ b/intern/cycles/render/attribute.cpp @@ -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,42 @@ 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 = add(attr.name, attr.type, attr.element); + nattr->std = attr.std; + 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_) diff --git a/intern/cycles/render/attribute.h b/intern/cycles/render/attribute.h index 9990a1325a7..f9997d3c422 100644 --- a/intern/cycles/render/attribute.h +++ b/intern/cycles/render/attribute.h @@ -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 diff --git a/intern/cycles/render/background.cpp b/intern/cycles/render/background.cpp index 7bdcb1578c3..1303f894912 100644 --- a/intern/cycles/render/background.cpp +++ b/intern/cycles/render/background.cpp @@ -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) diff --git a/intern/cycles/render/bake.cpp b/intern/cycles/render/bake.cpp index 439ebdedb8e..317a3937cab 100644 --- a/intern/cycles/render/bake.cpp +++ b/intern/cycles/render/bake.cpp @@ -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 diff --git a/intern/cycles/render/bake.h b/intern/cycles/render/bake.h index 93e664c2ab1..655b9b1cf7e 100644 --- a/intern/cycles/render/bake.h +++ b/intern/cycles/render/bake.h @@ -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; diff --git a/intern/cycles/render/film.cpp b/intern/cycles/render/film.cpp index 5c3778f6ae5..9b7657802d6 100644 --- a/intern/cycles/render/film.cpp +++ b/intern/cycles/render/film.cpp @@ -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) { diff --git a/intern/cycles/render/geometry.cpp b/intern/cycles/render/geometry.cpp index 6fc217f2d76..b6fe8a66b87 100644 --- a/intern/cycles/render/geometry.cpp +++ b/intern/cycles/render/geometry.cpp @@ -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 strictly 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,119 @@ 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->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 or shaders are modified, we would need to reallocate, so + * these are the only arrays that can be updated */ + dscene->tri_vnormal.tag_modified(); + dscene->tri_shader.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 +1705,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 +1721,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 +1817,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 +1890,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 +1898,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 +1912,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 +1927,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 +1952,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 +1977,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 +1986,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 +2068,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) +{ + 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 { - need_update = true; - scene->object_manager->need_update = true; + return update_flags != UPDATE_NONE; } void GeometryManager::collect_statistics(const Scene *scene, RenderStats *stats) diff --git a/intern/cycles/render/geometry.h b/intern/cycles/render/geometry.h index b124e950ad2..88388f31a9b 100644 --- a/intern/cycles/render/geometry.h +++ b/intern/cycles/render/geometry.h @@ -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); diff --git a/intern/cycles/render/hair.cpp b/intern/cycles/render/hair.cpp index 896e798b6f9..e94cad6b32e 100644 --- a/intern/cycles/render/hair.cpp +++ b/intern/cycles/render/hair.cpp @@ -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; } } } diff --git a/intern/cycles/render/hair.h b/intern/cycles/render/hair.h index c7be08d679c..4b949f984e5 100644 --- a/intern/cycles/render/hair.h +++ b/intern/cycles/render/hair.h @@ -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 diff --git a/intern/cycles/render/image.cpp b/intern/cycles/render/image.cpp index 30858c4f68b..a09a680f93f 100644 --- a/intern/cycles/render/image.cpp +++ b/intern/cycles/render/image.cpp @@ -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 diff --git a/intern/cycles/render/image.h b/intern/cycles/render/image.h index 6ac1db9ed63..c802521db56 100644 --- a/intern/cycles/render/image.h +++ b/intern/cycles/render/image.h @@ -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; diff --git a/intern/cycles/render/integrator.cpp b/intern/cycles/render/integrator.cpp index e5b9e6bfabf..c97a6549653 100644 --- a/intern/cycles/render/integrator.cpp +++ b/intern/cycles/render/integrator.cpp @@ -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 diff --git a/intern/cycles/render/integrator.h b/intern/cycles/render/integrator.h index 9fe46ad591c..57dcaf95360 100644 --- a/intern/cycles/render/integrator.h +++ b/intern/cycles/render/integrator.h @@ -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 diff --git a/intern/cycles/render/light.cpp b/intern/cycles/render/light.cpp index 2bde3242b26..6e9dea564c2 100644 --- a/intern/cycles/render/light.cpp +++ b/intern/cycles/render/light.cpp @@ -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) +{ + update_flags |= flag; +} + +bool LightManager::need_update() const { - need_update = true; + 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) diff --git a/intern/cycles/render/light.h b/intern/cycles/render/light.h index e590e13b489..39014b5d667 100644 --- a/intern/cycles/render/light.h +++ b/intern/cycles/render/light.h @@ -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 diff --git a/intern/cycles/render/mesh.cpp b/intern/cycles/render/mesh.cpp index a0afdd3b841..5f62da8f18b 100644 --- a/intern/cycles/render/mesh.cpp +++ b/intern/cycles/render/mesh.cpp @@ -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; - for (size_t k = 0; k < num_prims; ++k) { - pack.prim_tri_index.push_back_reserved(pack.prim_tri_verts.size()); + 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) { 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]]); } } diff --git a/intern/cycles/render/mesh.h b/intern/cycles/render/mesh.h index b0a16fdfd8f..2b0ff92ab62 100644 --- a/intern/cycles/render/mesh.h +++ b/intern/cycles/render/mesh.h @@ -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); diff --git a/intern/cycles/render/object.cpp b/intern/cycles/render/object.cpp index 406982e3ef9..0f16a4fc12c 100644 --- a/intern/cycles/render/object.cpp +++ b/intern/cycles/render/object.cpp @@ -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) +{ + 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 { - need_update = true; - scene->geometry_manager->need_update = true; - scene->light_manager->need_update = true; + return update_flags != UPDATE_NONE; } string ObjectManager::get_cryptomatte_objects(Scene *scene) diff --git a/intern/cycles/render/object.h b/intern/cycles/render/object.h index 76c9c30abb0..cf1b9ca510a 100644 --- a/intern/cycles/render/object.h +++ b/intern/cycles/render/object.h @@ -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, diff --git a/intern/cycles/render/osl.cpp b/intern/cycles/render/osl.cpp index 35b62746f1b..889636fe0f3 100644 --- a/intern/cycles/render/osl.cpp +++ b/intern/cycles/render/osl.cpp @@ -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)); diff --git a/intern/cycles/render/particles.cpp b/intern/cycles/render/particles.cpp index faad731d413..0e168050281 100644 --- a/intern/cycles/render/particles.cpp +++ b/intern/cycles/render/particles.cpp @@ -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 diff --git a/intern/cycles/render/particles.h b/intern/cycles/render/particles.h index 0b0408184fe..8b59756f148 100644 --- a/intern/cycles/render/particles.h +++ b/intern/cycles/render/particles.h @@ -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 diff --git a/intern/cycles/render/procedural.cpp b/intern/cycles/render/procedural.cpp new file mode 100644 index 00000000000..1307a35dcf2 --- /dev/null +++ b/intern/cycles/render/procedural.cpp @@ -0,0 +1,89 @@ +/* + * 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 "procedural.h" + +#include "render/scene.h" +#include "render/stats.h" + +#include "util/util_foreach.h" +#include "util/util_progress.h" + +CCL_NAMESPACE_BEGIN + +NODE_ABSTRACT_DEFINE(Procedural) +{ + NodeType *type = NodeType::add("procedural_base", NULL); + return type; +} + +Procedural::Procedural(const NodeType *type) : Node(type) +{ +} + +Procedural::~Procedural() +{ +} + +ProceduralManager::ProceduralManager() +{ + need_update_ = true; +} + +ProceduralManager::~ProceduralManager() +{ +} + +void ProceduralManager::update(Scene *scene, Progress &progress) +{ + if (!need_update()) { + return; + } + + progress.set_status("Updating Procedurals"); + + scoped_callback_timer timer([scene](double time) { + if (scene->update_stats) { + scene->update_stats->procedurals.times.add_entry({"update", time}); + } + }); + + foreach (Procedural *procedural, scene->procedurals) { + if (progress.get_cancel()) { + return; + } + + procedural->generate(scene, progress); + } + + if (progress.get_cancel()) { + return; + } + + need_update_ = false; +} + +void ProceduralManager::tag_update() +{ + need_update_ = true; +} + +bool ProceduralManager::need_update() const +{ + return need_update_; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/render/procedural.h b/intern/cycles/render/procedural.h new file mode 100644 index 00000000000..985b8d69979 --- /dev/null +++ b/intern/cycles/render/procedural.h @@ -0,0 +1,73 @@ +/* + * 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. + */ + +#pragma once + +#include "graph/node.h" + +CCL_NAMESPACE_BEGIN + +class Progress; +class Scene; + +/* A Procedural is a Node which can create other Nodes before rendering starts. + * + * The Procedural is supposed to be the owner of any nodes that it creates. It can also create + * Nodes directly in the Scene (through Scene.create_node), it should still be set as the owner of + * those Nodes. + */ +class Procedural : public Node, public NodeOwner { + public: + NODE_ABSTRACT_DECLARE + + explicit Procedural(const NodeType *type); + virtual ~Procedural(); + + /* Called each time the ProceduralManager is tagged for an update, this function is the entry + * point for the data generated by this Procedural. */ + virtual void generate(Scene *scene, Progress &progress) = 0; + + /* Create a node and set this Procedural as the owner. */ + template<typename T> T *create_node() + { + T *node = new T(); + node->set_owner(this); + return node; + } + + /* Delete a Node created and owned by this Procedural. */ + template<typename T> void delete_node(T *node) + { + assert(node->get_owner() == this); + delete node; + } +}; + +class ProceduralManager { + bool need_update_; + + public: + ProceduralManager(); + ~ProceduralManager(); + + void update(Scene *scene, Progress &progress); + + void tag_update(); + + bool need_update() const; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/render/scene.cpp b/intern/cycles/render/scene.cpp index b7720b7aa99..6bb25677965 100644 --- a/intern/cycles/render/scene.cpp +++ b/intern/cycles/render/scene.cpp @@ -18,6 +18,7 @@ #include "bvh/bvh.h" #include "device/device.h" +#include "render/alembic.h" #include "render/background.h" #include "render/bake.h" #include "render/camera.h" @@ -29,6 +30,7 @@ #include "render/object.h" #include "render/osl.h" #include "render/particles.h" +#include "render/procedural.h" #include "render/scene.h" #include "render/session.h" #include "render/shader.h" @@ -114,6 +116,7 @@ Scene::Scene(const SceneParams ¶ms_, Device *device) image_manager = new ImageManager(device->info); particle_system_manager = new ParticleSystemManager(); bake_manager = new BakeManager(); + procedural_manager = new ProceduralManager(); kernels_loaded = false; /* TODO(sergey): Check if it's indeed optimal value for the split kernel. */ @@ -142,6 +145,9 @@ void Scene::free_memory(bool final) foreach (Shader *s, shaders) delete s; + /* delete procedurals before other types as they may hold pointers to those types */ + foreach (Procedural *p, procedurals) + delete p; foreach (Geometry *g, geometry) delete g; foreach (Object *o, objects) @@ -156,15 +162,16 @@ void Scene::free_memory(bool final) objects.clear(); lights.clear(); particle_systems.clear(); + procedurals.clear(); if (device) { 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); @@ -195,6 +202,7 @@ void Scene::free_memory(bool final) delete image_manager; delete bake_manager; delete update_stats; + delete procedural_manager; } } @@ -236,6 +244,11 @@ void Scene::device_update(Device *device_, Progress &progress) if (progress.get_cancel() || device->have_error()) return; + procedural_manager->update(this, progress); + + if (progress.get_cancel()) + return; + progress.set_status("Updating Background"); background->device_update(device, &dscene, this); @@ -386,11 +399,12 @@ 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 || - film->is_modified()); + 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() || procedural_manager->need_update()); } bool Scene::need_reset() @@ -407,12 +421,15 @@ 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); + procedural_manager->tag_update(); } void Scene::device_free() @@ -596,7 +613,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 +622,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 +631,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 +640,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 +649,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,10 +667,23 @@ 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; } +template<> AlembicProcedural *Scene::create_node<AlembicProcedural>() +{ +#ifdef WITH_ALEMBIC + AlembicProcedural *node = new AlembicProcedural(); + node->set_owner(this); + procedurals.push_back(node); + procedural_manager->tag_update(); + return node; +#else + return nullptr; +#endif +} + template<typename T> void delete_node_from_array(vector<T> &nodes, T node) { for (size_t i = 0; i < nodes.size(); ++i) { @@ -664,43 +694,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) @@ -714,6 +753,21 @@ template<> void Scene::delete_node_impl(Shader * /*node*/) /* don't delete unused shaders, not supported */ } +template<> void Scene::delete_node_impl(Procedural *node) +{ + delete_node_from_array(procedurals, node); + procedural_manager->tag_update(); +} + +template<> void Scene::delete_node_impl(AlembicProcedural *node) +{ +#ifdef WITH_ALEMBIC + delete_node_impl(static_cast<Procedural *>(node)); +#else + (void)node; +#endif +} + template<typename T> static void remove_nodes_in_set(const set<T *> &nodes_set, vector<T *> &nodes_array, @@ -742,19 +796,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) @@ -768,4 +822,10 @@ template<> void Scene::delete_nodes(const set<Shader *> & /*nodes*/, const NodeO /* don't delete unused shaders, not supported */ } +template<> void Scene::delete_nodes(const set<Procedural *> &nodes, const NodeOwner *owner) +{ + remove_nodes_in_set(nodes, procedurals, owner); + procedural_manager->tag_update(); +} + CCL_NAMESPACE_END diff --git a/intern/cycles/render/scene.h b/intern/cycles/render/scene.h index 27e9a131bbd..7595817226c 100644 --- a/intern/cycles/render/scene.h +++ b/intern/cycles/render/scene.h @@ -36,6 +36,7 @@ CCL_NAMESPACE_BEGIN +class AlembicProcedural; class AttributeRequestSet; class Background; class BVH; @@ -53,6 +54,8 @@ class Object; class ObjectManager; class ParticleSystemManager; class ParticleSystem; +class Procedural; +class ProceduralManager; class CurveSystemManager; class Shader; class ShaderManager; @@ -236,6 +239,7 @@ class Scene : public NodeOwner { vector<Light *> lights; vector<ParticleSystem *> particle_systems; vector<Pass> passes; + vector<Procedural *> procedurals; /* data managers */ ImageManager *image_manager; @@ -245,6 +249,7 @@ class Scene : public NodeOwner { ObjectManager *object_manager; ParticleSystemManager *particle_system_manager; BakeManager *bake_manager; + ProceduralManager *procedural_manager; /* default shaders */ Shader *default_surface; @@ -379,6 +384,8 @@ template<> ParticleSystem *Scene::create_node<ParticleSystem>(); template<> Shader *Scene::create_node<Shader>(); +template<> AlembicProcedural *Scene::create_node<AlembicProcedural>(); + template<> void Scene::delete_node_impl(Light *node); template<> void Scene::delete_node_impl(Mesh *node); @@ -395,6 +402,10 @@ template<> void Scene::delete_node_impl(ParticleSystem *node); template<> void Scene::delete_node_impl(Shader *node); +template<> void Scene::delete_node_impl(Procedural *node); + +template<> void Scene::delete_node_impl(AlembicProcedural *node); + template<> void Scene::delete_nodes(const set<Light *> &nodes, const NodeOwner *owner); template<> void Scene::delete_nodes(const set<Geometry *> &nodes, const NodeOwner *owner); @@ -405,6 +416,8 @@ template<> void Scene::delete_nodes(const set<ParticleSystem *> &nodes, const No template<> void Scene::delete_nodes(const set<Shader *> &nodes, const NodeOwner *owner); +template<> void Scene::delete_nodes(const set<Procedural *> &nodes, const NodeOwner *owner); + CCL_NAMESPACE_END #endif /* __SCENE_H__ */ diff --git a/intern/cycles/render/session.cpp b/intern/cycles/render/session.cpp index 7a30ecb24f7..3c601e18126 100644 --- a/intern/cycles/render/session.cpp +++ b/intern/cycles/render/session.cpp @@ -1040,13 +1040,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; diff --git a/intern/cycles/render/shader.cpp b/intern/cycles/render/shader.cpp index 7e06b427e4d..332599be708 100644 --- a/intern/cycles/render/shader.cpp +++ b/intern/cycles/render/shader.cpp @@ -16,6 +16,7 @@ #include "device/device.h" +#include "render/alembic.h" #include "render/background.h" #include "render/camera.h" #include "render/colorspace.h" @@ -218,7 +219,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 +294,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 +311,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 +327,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 +356,18 @@ 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); + scene->procedural_manager->tag_update(); } if (has_volume != prev_has_volume || volume_step_rate != prev_volume_step_rate) { @@ -378,15 +383,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 +494,7 @@ int ShaderManager::get_shader_id(Shader *shader, bool smooth) void ShaderManager::update_shaders_used(Scene *scene) { - if (!need_update) { + if (!need_update()) { return; } @@ -504,6 +514,21 @@ void ShaderManager::update_shaders_used(Scene *scene) if (scene->background->get_shader()) scene->background->get_shader()->used = true; +#ifdef WITH_ALEMBIC + foreach (Procedural *procedural, scene->procedurals) { + AlembicProcedural *abc_proc = static_cast<AlembicProcedural *>(procedural); + + foreach (Node *abc_node, abc_proc->get_objects()) { + AlembicObject *abc_object = static_cast<AlembicObject *>(abc_node); + + foreach (Node *node, abc_object->get_used_shaders()) { + Shader *shader = static_cast<Shader *>(node); + shader->used = true; + } + } + } +#endif + foreach (Geometry *geom, scene->geometry) foreach (Node *node, geom->get_used_shaders()) { Shader *shader = static_cast<Shader *>(node); @@ -793,4 +818,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 diff --git a/intern/cycles/render/shader.h b/intern/cycles/render/shader.h index de19048d8e1..4375ef9e978 100644 --- a/intern/cycles/render/shader.h +++ b/intern/cycles/render/shader.h @@ -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; diff --git a/intern/cycles/render/stats.cpp b/intern/cycles/render/stats.cpp index 1a840a906a5..2c6273842e2 100644 --- a/intern/cycles/render/stats.cpp +++ b/intern/cycles/render/stats.cpp @@ -375,6 +375,7 @@ string SceneUpdateStats::full_report() result += "Particles:\n" + particles.full_report(1); result += "SVM:\n" + svm.full_report(1); result += "Tables:\n" + tables.full_report(1); + result += "Procedurals:\n" + procedurals.full_report(1); return result; } @@ -394,6 +395,7 @@ void SceneUpdateStats::clear() scene.times.clear(); svm.times.clear(); tables.times.clear(); + procedurals.times.clear(); } CCL_NAMESPACE_END diff --git a/intern/cycles/render/stats.h b/intern/cycles/render/stats.h index a6be27db4c2..93d029bba61 100644 --- a/intern/cycles/render/stats.h +++ b/intern/cycles/render/stats.h @@ -219,6 +219,7 @@ class SceneUpdateStats { UpdateTimeStats scene; UpdateTimeStats svm; UpdateTimeStats tables; + UpdateTimeStats procedurals; string full_report(); diff --git a/intern/cycles/render/svm.cpp b/intern/cycles/render/svm.cpp index 6f5a03124f2..fce604234f1 100644 --- a/intern/cycles/render/svm.cpp +++ b/intern/cycles/render/svm.cpp @@ -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."; diff --git a/intern/cycles/render/tables.cpp b/intern/cycles/render/tables.cpp index b581537c852..a0813400b1c 100644 --- a/intern/cycles/render/tables.cpp +++ b/intern/cycles/render/tables.cpp @@ -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; diff --git a/intern/cycles/render/tables.h b/intern/cycles/render/tables.h index e912d9c01f4..de538e2af78 100644 --- a/intern/cycles/render/tables.h +++ b/intern/cycles/render/tables.h @@ -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); }; diff --git a/intern/cycles/util/util_array.h b/intern/cycles/util/util_array.h index ea481787018..73f7d6cf7f8 100644 --- a/intern/cycles/util/util_array.h +++ b/intern/cycles/util/util_array.h @@ -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_; diff --git a/intern/ghost/intern/GHOST_DropTargetX11.h b/intern/ghost/intern/GHOST_DropTargetX11.h index 69f6fec7ac1..7d777270317 100644 --- a/intern/ghost/intern/GHOST_DropTargetX11.h +++ b/intern/ghost/intern/GHOST_DropTargetX11.h @@ -109,7 +109,7 @@ class GHOST_DropTargetX11 { /* class holding internal stiff of xdnd library */ static DndClass m_dndClass; - /* list of supported types to eb draggeg into */ + /* list of supported types to be dragged into */ static Atom *m_dndTypes; /* list of supported dran'n'drop actions */ diff --git a/intern/guardedalloc/CMakeLists.txt b/intern/guardedalloc/CMakeLists.txt index 0d46e81cd87..b47565b8ef9 100644 --- a/intern/guardedalloc/CMakeLists.txt +++ b/intern/guardedalloc/CMakeLists.txt @@ -49,12 +49,6 @@ set(LIB ) if(WIN32 AND NOT UNIX) - list(APPEND SRC - intern/mmap_win.c - - mmap_win.h - ) - list(APPEND INC_SYS ${PTHREADS_INC} ) diff --git a/intern/guardedalloc/MEM_guardedalloc.h b/intern/guardedalloc/MEM_guardedalloc.h index a0174e30aff..62f28c9e97e 100644 --- a/intern/guardedalloc/MEM_guardedalloc.h +++ b/intern/guardedalloc/MEM_guardedalloc.h @@ -122,7 +122,7 @@ extern void *(*MEM_calloc_arrayN)(size_t len, /** * Allocate a block of memory of size len, with tag name str. The * name must be a static, because only a pointer to it is stored ! - * */ + */ extern void *(*MEM_mallocN)(size_t len, const char *str) /* ATTR_MALLOC */ ATTR_WARN_UNUSED_RESULT ATTR_ALLOC_SIZE(1) ATTR_NONNULL(2); @@ -130,7 +130,7 @@ extern void *(*MEM_mallocN)(size_t len, const char *str) /* ATTR_MALLOC */ ATTR_ * Allocate a block of memory of size (len * size), with tag name str, * aborting in case of integer overflow to prevent vulnerabilities. The * name must be a static, because only a pointer to it is stored ! - * */ + */ extern void *(*MEM_malloc_arrayN)(size_t len, size_t size, const char *str) /* ATTR_MALLOC */ ATTR_WARN_UNUSED_RESULT @@ -139,7 +139,7 @@ extern void *(*MEM_malloc_arrayN)(size_t len, /** * Allocate an aligned block of memory of size len, with tag name str. The * name must be a static, because only a pointer to it is stored ! - * */ + */ extern void *(*MEM_mallocN_aligned)(size_t len, size_t alignment, const char *str) /* ATTR_MALLOC */ ATTR_WARN_UNUSED_RESULT diff --git a/intern/guardedalloc/intern/mmap_win.c b/intern/guardedalloc/intern/mmap_win.c deleted file mode 100644 index a02a0f88fa9..00000000000 --- a/intern/guardedalloc/intern/mmap_win.c +++ /dev/null @@ -1,294 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2008 Blender Foundation. - * All rights reserved. - */ - -/** \file - * \ingroup MEM - */ - -#ifdef WIN32 - -# include <errno.h> -# include <io.h> -# include <stdio.h> -# include <sys/types.h> -# include <windows.h> - -# include "mmap_win.h" - -# ifndef FILE_MAP_EXECUTE -// not defined in earlier versions of the Platform SDK (before February 2003) -# define FILE_MAP_EXECUTE 0x0020 -# endif - -/* copied from BLI_utildefines.h, ugh */ -# ifdef __GNUC__ -# define UNUSED(x) UNUSED_##x __attribute__((__unused__)) -# else -# define UNUSED(x) x -# endif - -/* --------------------------------------------------------------------- */ -/* local storage definitions */ -/* --------------------------------------------------------------------- */ -/* all memory mapped chunks are put in linked lists */ -typedef struct mmapLink { - struct mmapLink *next, *prev; -} mmapLink; - -typedef struct mmapListBase { - void *first, *last; -} mmapListBase; - -typedef struct MemMap { - struct MemMap *next, *prev; - void *mmap; - HANDLE fhandle; - HANDLE maphandle; -} MemMap; - -/* --------------------------------------------------------------------- */ -/* local functions */ -/* --------------------------------------------------------------------- */ - -static void mmap_addtail(volatile mmapListBase *listbase, void *vlink); -static void mmap_remlink(volatile mmapListBase *listbase, void *vlink); -static void *mmap_findlink(volatile mmapListBase *listbase, void *ptr); - -static int mmap_get_prot_flags(int flags); -static int mmap_get_access_flags(int flags); - -/* --------------------------------------------------------------------- */ -/* vars */ -/* --------------------------------------------------------------------- */ -volatile static struct mmapListBase _mmapbase; -volatile static struct mmapListBase *mmapbase = &_mmapbase; - -/* --------------------------------------------------------------------- */ -/* implementation */ -/* --------------------------------------------------------------------- */ - -/* mmap for windows */ -void *mmap(void *UNUSED(start), size_t len, int prot, int flags, int fd, off_t offset) -{ - HANDLE fhandle = INVALID_HANDLE_VALUE; - HANDLE maphandle; - int prot_flags = mmap_get_prot_flags(prot); - int access_flags = mmap_get_access_flags(prot); - MemMap *mm = NULL; - void *ptr = NULL; - - if (flags & MAP_FIXED) { - return MAP_FAILED; - } - -# if 0 - if (fd == -1) { - _set_errno(EBADF); - return MAP_FAILED; - } -# endif - - if (fd != -1) { - fhandle = (HANDLE)_get_osfhandle(fd); - } - if (fhandle == INVALID_HANDLE_VALUE) { - if (!(flags & MAP_ANONYMOUS)) { - errno = EBADF; - return MAP_FAILED; - } - } - else { - if (!DuplicateHandle(GetCurrentProcess(), - fhandle, - GetCurrentProcess(), - &fhandle, - 0, - FALSE, - DUPLICATE_SAME_ACCESS)) { - return MAP_FAILED; - } - } - - /* Split 64 bit size into low and high bits. */ - DWORD len_bits_high = len >> 32; - DWORD len_bits_low = len & 0xFFFFFFFF; - - maphandle = CreateFileMapping(fhandle, NULL, prot_flags, len_bits_high, len_bits_low, NULL); - if (maphandle == 0) { - errno = EBADF; - return MAP_FAILED; - } - - ptr = MapViewOfFile(maphandle, access_flags, 0, offset, 0); - if (ptr == NULL) { - DWORD dwLastErr = GetLastError(); - if (dwLastErr == ERROR_MAPPED_ALIGNMENT) { - errno = EINVAL; - } - else { - errno = EACCES; - } - CloseHandle(maphandle); - return MAP_FAILED; - } - - mm = (MemMap *)malloc(sizeof(MemMap)); - if (!mm) { - errno = ENOMEM; - } - mm->fhandle = fhandle; - mm->maphandle = maphandle; - mm->mmap = ptr; - mmap_addtail(mmapbase, mm); - - return ptr; -} - -/* munmap for windows */ -intptr_t munmap(void *ptr, size_t UNUSED(size)) -{ - MemMap *mm = mmap_findlink(mmapbase, ptr); - if (!mm) { - errno = EINVAL; - return -1; - } - UnmapViewOfFile(mm->mmap); - CloseHandle(mm->maphandle); - CloseHandle(mm->fhandle); - mmap_remlink(mmapbase, mm); - free(mm); - return 0; -} - -/* --------------------------------------------------------------------- */ -/* local functions */ -/* --------------------------------------------------------------------- */ - -static void mmap_addtail(volatile mmapListBase *listbase, void *vlink) -{ - struct mmapLink *link = vlink; - - if (link == NULL) { - return; - } - if (listbase == NULL) { - return; - } - - link->next = 0; - link->prev = listbase->last; - - if (listbase->last) { - ((struct mmapLink *)listbase->last)->next = link; - } - if (listbase->first == NULL) { - listbase->first = link; - } - listbase->last = link; -} - -static void mmap_remlink(volatile mmapListBase *listbase, void *vlink) -{ - struct mmapLink *link = vlink; - - if (link == NULL) { - return; - } - if (listbase == NULL) { - return; - } - if (link->next) { - link->next->prev = link->prev; - } - if (link->prev) { - link->prev->next = link->next; - } - - if (listbase->last == link) { - listbase->last = link->prev; - } - if (listbase->first == link) { - listbase->first = link->next; - } -} - -static void *mmap_findlink(volatile mmapListBase *listbase, void *ptr) -{ - MemMap *mm; - - if (ptr == NULL) { - return NULL; - } - if (listbase == NULL) { - return NULL; - } - - mm = (MemMap *)listbase->first; - while (mm) { - if (mm->mmap == ptr) { - return mm; - } - mm = mm->next; - } - return NULL; -} - -static int mmap_get_prot_flags(int flags) -{ - int prot = PAGE_NOACCESS; - - if ((flags & PROT_READ) == PROT_READ) { - if ((flags & PROT_WRITE) == PROT_WRITE) { - prot = (flags & PROT_EXEC) ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE; - } - else { - prot = (flags & PROT_EXEC) ? PAGE_EXECUTE_READ : PAGE_READONLY; - } - } - else if ((flags & PROT_WRITE) == PROT_WRITE) { - prot = (flags & PROT_EXEC) ? PAGE_EXECUTE_READ : PAGE_WRITECOPY; - } - else if ((flags & PROT_EXEC) == PROT_EXEC) { - prot = PAGE_EXECUTE_READ; - } - return prot; -} - -static int mmap_get_access_flags(int flags) -{ - int access = 0; - - if ((flags & PROT_READ) == PROT_READ) { - if ((flags & PROT_WRITE) == PROT_WRITE) { - access = FILE_MAP_WRITE; - } - else { - access = (flags & PROT_EXEC) ? FILE_MAP_EXECUTE : FILE_MAP_READ; - } - } - else if ((flags & PROT_WRITE) == PROT_WRITE) { - access = FILE_MAP_COPY; - } - else if ((flags & PROT_EXEC) == PROT_EXEC) { - access = FILE_MAP_EXECUTE; - } - return access; -} - -#endif // WIN32 diff --git a/intern/guardedalloc/mmap_win.h b/intern/guardedalloc/mmap_win.h deleted file mode 100644 index c0cbaa0e512..00000000000 --- a/intern/guardedalloc/mmap_win.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2008 Blender Foundation. - * All rights reserved. - */ - -/** \file - * \ingroup MEM - */ - -#ifndef __MMAP_WIN_H__ -#define __MMAP_WIN_H__ - -#define PROT_NONE 0 -#define PROT_READ 1 -#define PROT_WRITE 2 -#define PROT_EXEC 4 - -#define MAP_FILE 0 -#define MAP_SHARED 1 -#define MAP_PRIVATE 2 -#define MAP_TYPE 0xF -#define MAP_FIXED 0x10 -#define MAP_ANONYMOUS 0x20 -#define MAP_ANON MAP_ANONYMOUS - -#define MAP_FAILED ((void *)-1) - -/* needed for uintptr_t, exception, dont use BLI anywhere else in MEM_* */ -#include "../../source/blender/blenlib/BLI_sys_types.h" - -#include <sys/types.h> - -void *mmap(void *start, size_t len, int prot, int flags, int fd, off_t offset); -intptr_t munmap(void *ptr, size_t size); - -#endif |