diff options
Diffstat (limited to 'intern/cycles/render')
37 files changed, 1910 insertions, 1142 deletions
diff --git a/intern/cycles/render/CMakeLists.txt b/intern/cycles/render/CMakeLists.txt index c67919b375a..feead27c5ca 100644 --- a/intern/cycles/render/CMakeLists.txt +++ b/intern/cycles/render/CMakeLists.txt @@ -24,6 +24,7 @@ set(INC_SYS set(SRC alembic.cpp + alembic_read.cpp attribute.cpp background.cpp bake.cpp @@ -67,6 +68,7 @@ set(SRC set(SRC_HEADERS alembic.h + alembic_read.h attribute.h bake.h background.h diff --git a/intern/cycles/render/alembic.cpp b/intern/cycles/render/alembic.cpp index 1336f81896a..dcb456dc1ce 100644 --- a/intern/cycles/render/alembic.cpp +++ b/intern/cycles/render/alembic.cpp @@ -16,6 +16,7 @@ #include "render/alembic.h" +#include "render/alembic_read.h" #include "render/camera.h" #include "render/curves.h" #include "render/mesh.h" @@ -34,7 +35,48 @@ using namespace Alembic::AbcGeom; CCL_NAMESPACE_BEGIN -/* TODO(@kevindietrich): motion blur support */ +/* TODO(kevindietrich): motion blur support. */ + +template<typename SchemaType> +static vector<FaceSetShaderIndexPair> parse_face_sets_for_shader_assignment( + SchemaType &schema, const array<Node *> &used_shaders) +{ + vector<FaceSetShaderIndexPair> result; + + std::vector<std::string> face_set_names; + schema.getFaceSetNames(face_set_names); + + if (face_set_names.empty()) { + return result; + } + + for (const std::string &face_set_name : face_set_names) { + int shader_index = 0; + + for (Node *node : used_shaders) { + if (node->name == face_set_name) { + break; + } + + ++shader_index; + } + + if (shader_index >= used_shaders.size()) { + /* use the first shader instead if none was found */ + shader_index = 0; + } + + const Alembic::AbcGeom::IFaceSet face_set = schema.getFaceSet(face_set_name); + + if (!face_set.valid()) { + continue; + } + + result.push_back({face_set, shader_index}); + } + + return result; +} void CachedData::clear() { @@ -54,7 +96,7 @@ void CachedData::clear() subd_start_corner.clear(); transforms.clear(); triangles.clear(); - triangles_loops.clear(); + uv_loops.clear(); vertices.clear(); for (CachedAttribute &attr : attributes) { @@ -101,7 +143,7 @@ bool CachedData::is_constant() const CHECK_IF_CONSTANT(subd_start_corner) CHECK_IF_CONSTANT(transforms) CHECK_IF_CONSTANT(triangles) - CHECK_IF_CONSTANT(triangles_loops) + CHECK_IF_CONSTANT(uv_loops) CHECK_IF_CONSTANT(vertices) for (const CachedAttribute &attr : attributes) { @@ -140,7 +182,7 @@ void CachedData::invalidate_last_loaded_time(bool attributes_only) subd_start_corner.invalidate_last_loaded_time(); transforms.invalidate_last_loaded_time(); triangles.invalidate_last_loaded_time(); - triangles_loops.invalidate_last_loaded_time(); + uv_loops.invalidate_last_loaded_time(); vertices.invalidate_last_loaded_time(); } @@ -161,7 +203,7 @@ void CachedData::set_time_sampling(TimeSampling 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); + uv_loops.set_time_sampling(time_sampling); vertices.set_time_sampling(time_sampling); for (CachedAttribute &attr : attributes) { @@ -169,36 +211,6 @@ void CachedData::set_time_sampling(TimeSampling 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; @@ -366,249 +378,6 @@ static Transform make_transform(const M44d &a, float scale) 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()); - - /* Keys used to determine if the UVs do actually change over time. */ - ArraySample::Key previous_indices_key; - ArraySample::Key previous_values_key; - - foreach (chrono_t time, times) { - if (progress.get_cancel()) { - return; - } - - const ISampleSelector iss = ISampleSelector(time); - const IV2fGeomParam::Sample uvsample = uvs.getIndexedValue(iss); - - if (!uvsample.valid()) { - continue; - } - - const array<int3> *triangles = - cached_data.triangles.data_for_time_no_check(time).get_data_or_null(); - const array<int3> *triangles_loops = - cached_data.triangles_loops.data_for_time_no_check(time).get_data_or_null(); - - if (!triangles || !triangles_loops) { - continue; - } - - array<char> data; - data.resize(triangles->size() * 3 * sizeof(float2)); - - float2 *data_float2 = reinterpret_cast<float2 *>(data.data()); - - const ArraySample::Key indices_key = uvsample.getIndices()->getKey(); - const ArraySample::Key values_key = uvsample.getVals()->getKey(); - - if (indices_key == previous_indices_key && values_key == previous_values_key) { - attr.data.reuse_data_for_last_time(time); - } - else { - 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); - } - - previous_indices_key = indices_key; - previous_values_key = values_key; - } -} - -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).get_data_or_null(); - - 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).get_data_or_null(); - - 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); @@ -648,398 +417,137 @@ 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, - Progress &progress) +void AlembicObject::load_data_in_cache(CachedData &cached_data, + AlembicProcedural *proc, + IPolyMeshSchema &schema, + Progress &progress) { - cached_data.clear(); - /* Only load data for the original Geometry. */ if (instance_of) { return; } - 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()); - - /* Key used to determine if the triangles change over time, if the key is the same as the - * last one, we can avoid creating a new entry in the cache and simply point to the last - * frame. */ - ArraySample::Key previous_key; - - /* 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. */ - if (schema.getTopologyVariance() != kHomogenousTopology || cached_data.triangles.size() == 0) { - const ArraySample::Key key = sample.getFaceIndices()->getKey(); - - if (key == previous_key) { - cached_data.triangles.reuse_data_for_last_time(time); - cached_data.triangles_loops.reuse_data_for_last_time(time); - cached_data.shader.reuse_data_for_last_time(time); - } - else { - /* 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); - } + cached_data.clear(); - previous_key = key; - } + PolyMeshSchemaData data; + data.topology_variance = schema.getTopologyVariance(); + data.time_sampling = schema.getTimeSampling(); + data.positions = schema.getPositionsProperty(); + data.face_counts = schema.getFaceCountsProperty(); + data.face_indices = schema.getFaceIndicesProperty(); + data.normals = schema.getNormalsParam(); + data.num_samples = schema.getNumSamples(); + data.shader_face_sets = parse_face_sets_for_shader_assignment(schema, get_used_shaders()); - if (normals.valid()) { - add_normals(sample.getFaceIndices(), normals, time, cached_data); - } - } + read_geometry_data(proc, cached_data, data, progress); if (progress.get_cancel()) { return; } - update_shader_attributes(schema.getArbGeomParams(), progress); + /* Use the schema as the base compound property to also be able to look for top level properties. + */ + read_attributes( + proc, cached_data, schema, schema.getUVsParam(), get_requested_attributes(), progress); if (progress.get_cancel()) { return; } - const IV2fGeomParam &uvs = schema.getUVsParam(); - - if (uvs.valid()) { - add_uvs(proc, uvs, cached_data, progress); - } - + cached_data.invalidate_last_loaded_time(true); data_loaded = true; } -void AlembicObject::load_all_data(AlembicProcedural *proc, ISubDSchema &schema, Progress &progress) +void AlembicObject::load_data_in_cache(CachedData &cached_data, + AlembicProcedural *proc, + ISubDSchema &schema, + Progress &progress) { - cached_data.clear(); - /* Only load data for the original Geometry. */ if (instance_of) { return; } - 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); - } - } + cached_data.clear(); - /* TODO(@kevindietrich) : attributes, need test files */ + SubDSchemaData data; + data.time_sampling = schema.getTimeSampling(); + data.num_samples = schema.getNumSamples(); + data.topology_variance = schema.getTopologyVariance(); + data.face_counts = schema.getFaceCountsProperty(); + data.face_indices = schema.getFaceIndicesProperty(); + data.positions = schema.getPositionsProperty(); + data.face_varying_interpolate_boundary = schema.getFaceVaryingInterpolateBoundaryProperty(); + data.face_varying_propagate_corners = schema.getFaceVaryingPropagateCornersProperty(); + data.interpolate_boundary = schema.getInterpolateBoundaryProperty(); + data.crease_indices = schema.getCreaseIndicesProperty(); + data.crease_lengths = schema.getCreaseLengthsProperty(); + data.crease_sharpnesses = schema.getCreaseSharpnessesProperty(); + data.corner_indices = schema.getCornerIndicesProperty(); + data.corner_sharpnesses = schema.getCornerSharpnessesProperty(); + data.holes = schema.getHolesProperty(); + data.subdivision_scheme = schema.getSubdivisionSchemeProperty(); + data.velocities = schema.getVelocitiesProperty(); + data.shader_face_sets = parse_face_sets_for_shader_assignment(schema, get_used_shaders()); + + read_geometry_data(proc, cached_data, data, progress); if (progress.get_cancel()) { return; } + /* Use the schema as the base compound property to also be able to look for top level properties. + */ + read_attributes( + proc, cached_data, schema, schema.getUVsParam(), get_requested_attributes(), progress); + + cached_data.invalidate_last_loaded_time(true); data_loaded = true; } -void AlembicObject::load_all_data(AlembicProcedural *proc, - const ICurvesSchema &schema, - Progress &progress, - float default_radius) +void AlembicObject::load_data_in_cache(CachedData &cached_data, + AlembicProcedural *proc, + const ICurvesSchema &schema, + Progress &progress) { - cached_data.clear(); - /* Only load data for the original Geometry. */ if (instance_of) { return; } - 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; - - const bool is_homogenous = schema.getTopologyVariance() == kHomogenousTopology; - - 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); - } - - if (!is_homogenous || cached_data.curve_first_key.size() == 0) { - curve_first_key.push_back_reserved(offset); - curve_shader.push_back_reserved(0); - } - - offset += num_vertices; - } + cached_data.clear(); - cached_data.curve_keys.add_data(curve_keys, time); - cached_data.curve_radius.add_data(curve_radius, time); + CurvesSchemaData data; + data.positions = schema.getPositionsProperty(); + data.position_weights = schema.getPositionWeightsProperty(); + data.normals = schema.getNormalsParam(); + data.knots = schema.getKnotsProperty(); + data.orders = schema.getOrdersProperty(); + data.widths = schema.getWidthsParam(); + data.velocities = schema.getVelocitiesProperty(); + data.time_sampling = schema.getTimeSampling(); + data.topology_variance = schema.getTopologyVariance(); + data.num_samples = schema.getNumSamples(); + data.num_vertices = schema.getNumVerticesProperty(); + data.default_radius = proc->get_default_radius(); + data.radius_scale = get_radius_scale(); + + read_geometry_data(proc, cached_data, data, progress); - if (!is_homogenous || cached_data.curve_first_key.size() == 0) { - cached_data.curve_first_key.add_data(curve_first_key, time); - cached_data.curve_shader.add_data(curve_shader, time); - } + if (progress.get_cancel()) { + return; } - // TODO(@kevindietrich): attributes, need example files + /* Use the schema as the base compound property to also be able to look for top level properties. + */ + read_attributes( + proc, cached_data, schema, schema.getUVsParam(), get_requested_attributes(), progress); + cached_data.invalidate_last_loaded_time(true); data_loaded = true; } -void AlembicObject::setup_transform_cache(float scale) +void AlembicObject::setup_transform_cache(CachedData &cached_data, float scale) { cached_data.transforms.clear(); cached_data.transforms.invalidate_last_loaded_time(); @@ -1102,188 +610,6 @@ AttributeRequestSet AlembicObject::get_requested_attributes() 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).get_data_or_null(); - const array<int3> *triangles_loops = - cached_data.triangles_loops.data_for_time_no_check(time).get_data_or_null(); - - 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).get_data_or_null(); - - 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).get_data_or_null(); - - 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) @@ -1291,7 +617,11 @@ static void update_attributes(AttributeSet &attributes, CachedData &cached_data, set<Attribute *> cached_attributes; for (CachedData::CachedAttribute &attribute : cached_data.attributes) { - const array<char> *attr_data = attribute.data.data_for_time(frame_time).get_data_or_null(); + const CacheLookupResult<array<char>> result = attribute.data.data_for_time(frame_time); + + if (result.has_no_data_for_time()) { + continue; + } Attribute *attr = nullptr; if (attribute.std != ATTR_STD_NONE) { @@ -1304,18 +634,19 @@ static void update_attributes(AttributeSet &attributes, CachedData &cached_data, cached_attributes.insert(attr); - if (!attr_data) { - /* no new data */ + if (!result.has_new_data()) { continue; } + const ccl::array<char> &attr_data = result.get_data(); + /* 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()); + if (attr->buffer.size() != attr_data.size()) { + attr->buffer.resize(attr_data.size()); } - memcpy(attr->data(), attr_data->data(), attr_data->size()); + memcpy(attr->data(), attr_data.data(), attr_data.size()); attr->modified = true; } @@ -1323,8 +654,7 @@ static void update_attributes(AttributeSet &attributes, CachedData &cached_data, 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++); - attributes.modified = true; + attributes.remove(it++); continue; } @@ -1407,6 +737,15 @@ void AlembicProcedural::generate(Scene *scene, Progress &progress) need_data_updates = true; } + /* Check if the shaders were modified. */ + if (object->used_shaders_is_modified() && object->get_object() && + object->get_object()->get_geometry()) { + Geometry *geometry = object->get_object()->get_geometry(); + array<Node *> used_shaders = object->get_used_shaders(); + geometry->set_used_shaders(used_shaders); + need_shader_updates = true; + } + /* Check for changes in shaders (e.g. newly requested attributes). */ foreach (Node *shader_node, object->get_used_shaders()) { Shader *shader = static_cast<Shader *>(shader_node); @@ -1467,6 +806,7 @@ void AlembicProcedural::generate(Scene *scene, Progress &progress) read_subd(object, frame_time); } + object->need_shader_update = false; object->clear_modified(); } @@ -1586,6 +926,11 @@ void AlembicProcedural::read_mesh(AlembicObject *abc_object, Abc::chrono_t frame Mesh *mesh = static_cast<Mesh *>(object->get_geometry()); + /* Make sure shader ids are also updated. */ + if (mesh->used_shaders_is_modified()) { + mesh->tag_shader_modified(); + } + 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()); @@ -1653,6 +998,11 @@ void AlembicProcedural::read_subd(AlembicObject *abc_object, Abc::chrono_t frame Mesh *mesh = static_cast<Mesh *>(object->get_geometry()); + /* Make sure shader ids are also updated. */ + if (mesh->used_shaders_is_modified()) { + mesh->tag_shader_modified(); + } + /* 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()) { @@ -1743,6 +1093,11 @@ void AlembicProcedural::read_curves(AlembicObject *abc_object, Abc::chrono_t fra Hair *hair = static_cast<Hair *>(object->get_geometry()); + /* Make sure shader ids are also updated. */ + if (hair->used_shaders_is_modified()) { + hair->tag_curve_shader_modified(); + } + 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()); @@ -1936,12 +1291,17 @@ void AlembicProcedural::build_caches(Progress &progress) if (!object->has_data_loaded()) { IPolyMesh polymesh(object->iobject, Alembic::Abc::kWrapExisting); IPolyMeshSchema schema = polymesh.getSchema(); - object->load_all_data(this, schema, progress); + object->load_data_in_cache(object->get_cached_data(), this, schema, progress); } else if (object->need_shader_update) { IPolyMesh polymesh(object->iobject, Alembic::Abc::kWrapExisting); IPolyMeshSchema schema = polymesh.getSchema(); - object->update_shader_attributes(schema.getArbGeomParams(), progress); + read_attributes(this, + object->get_cached_data(), + schema, + schema.getUVsParam(), + object->get_requested_attributes(), + progress); } } else if (object->schema_type == AlembicObject::CURVES) { @@ -1949,24 +1309,29 @@ void AlembicProcedural::build_caches(Progress &progress) object->radius_scale_is_modified()) { ICurves curves(object->iobject, Alembic::Abc::kWrapExisting); ICurvesSchema schema = curves.getSchema(); - object->load_all_data(this, schema, progress, default_radius); + object->load_data_in_cache(object->get_cached_data(), this, schema, progress); } } else if (object->schema_type == AlembicObject::SUBD) { if (!object->has_data_loaded()) { ISubD subd_mesh(object->iobject, Alembic::Abc::kWrapExisting); ISubDSchema schema = subd_mesh.getSchema(); - object->load_all_data(this, schema, progress); + object->load_data_in_cache(object->get_cached_data(), this, schema, progress); } else if (object->need_shader_update) { ISubD subd_mesh(object->iobject, Alembic::Abc::kWrapExisting); ISubDSchema schema = subd_mesh.getSchema(); - object->update_shader_attributes(schema.getArbGeomParams(), progress); + read_attributes(this, + object->get_cached_data(), + schema, + schema.getUVsParam(), + object->get_requested_attributes(), + progress); } } if (scale_is_modified() || object->get_cached_data().transforms.size() == 0) { - object->setup_transform_cache(scale); + object->setup_transform_cache(object->get_cached_data(), scale); } } } diff --git a/intern/cycles/render/alembic.h b/intern/cycles/render/alembic.h index d0c5856a353..61c0e40fe4a 100644 --- a/intern/cycles/render/alembic.h +++ b/intern/cycles/render/alembic.h @@ -152,6 +152,10 @@ template<typename T> class DataStore { double last_loaded_time = std::numeric_limits<double>::max(); public: + /* Keys used to compare values. */ + Alembic::AbcCoreAbstract::ArraySample::Key key1; + Alembic::AbcCoreAbstract::ArraySample::Key key2; + void set_time_sampling(Alembic::AbcCoreAbstract::TimeSampling time_sampling_) { time_sampling = time_sampling_; @@ -225,6 +229,11 @@ template<typename T> class DataStore { index_data_map.push_back({time, data_index.source_time, data_index.index}); } + void add_no_data(double time) + { + index_data_map.push_back({time, time, -1ul}); + } + bool is_constant() const { return data.size() <= 1; @@ -239,6 +248,7 @@ template<typename T> class DataStore { { invalidate_last_loaded_time(); data.clear(); + index_data_map.clear(); } void invalidate_last_loaded_time() @@ -283,7 +293,7 @@ struct CachedData { 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>> uv_loops{}; DataStore<array<int>> shader{}; /* subd data */ @@ -361,16 +371,18 @@ class AlembicObject : public Node { void set_object(Object *object); Object *get_object(); - void load_all_data(AlembicProcedural *proc, - Alembic::AbcGeom::IPolyMeshSchema &schema, - Progress &progress); - void load_all_data(AlembicProcedural *proc, - Alembic::AbcGeom::ISubDSchema &schema, - Progress &progress); - void load_all_data(AlembicProcedural *proc, - const Alembic::AbcGeom::ICurvesSchema &schema, - Progress &progress, - float default_radius); + void load_data_in_cache(CachedData &cached_data, + AlembicProcedural *proc, + Alembic::AbcGeom::IPolyMeshSchema &schema, + Progress &progress); + void load_data_in_cache(CachedData &cached_data, + AlembicProcedural *proc, + Alembic::AbcGeom::ISubDSchema &schema, + Progress &progress); + void load_data_in_cache(CachedData &cached_data, + AlembicProcedural *proc, + const Alembic::AbcGeom::ICurvesSchema &schema, + Progress &progress); bool has_data_loaded() const; @@ -396,33 +408,21 @@ class AlembicObject : public Node { CachedData &get_cached_data() { - return cached_data; + return cached_data_; } bool is_constant() const { - return cached_data.is_constant(); + 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); + CachedData cached_data_; - void setup_transform_cache(float scale); + void setup_transform_cache(CachedData &cached_data, float scale); AttributeRequestSet get_requested_attributes(); }; @@ -480,16 +480,23 @@ class AlembicProcedural : public Procedural { * 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 existing or a newly created AlembicObject for the given path. */ + /* This should be called by scene exporters to request the rendering of an object located + * in the Alembic archive at the given path. + * + * Since we lazily load object, the function does not validate the existence of the object + * in the archive. If no objects with such path if found in the archive during the next call + * to `generate`, it will be ignored. + * + * Returns a pointer to an existing or a newly created AlembicObject for the given path. */ AlembicObject *get_or_create_object(const ustring &path); private: + /* Add an object to our list of objects, and tag the socket as modified. */ + void add_object(AlembicObject *object); + /* Load the data for all the objects whose data has not yet been loaded. */ void load_objects(Progress &progress); diff --git a/intern/cycles/render/alembic_read.cpp b/intern/cycles/render/alembic_read.cpp new file mode 100644 index 00000000000..33ce3502879 --- /dev/null +++ b/intern/cycles/render/alembic_read.cpp @@ -0,0 +1,1008 @@ +/* + * Copyright 2021 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_read.h" + +#include "render/alembic.h" +#include "render/mesh.h" + +#include "util/util_progress.h" + +#ifdef WITH_ALEMBIC + +using namespace Alembic::AbcGeom; + +CCL_NAMESPACE_BEGIN + +static float3 make_float3_from_yup(const V3f &v) +{ + return make_float3(v.x, -v.z, v.y); +} + +/* 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; + } + + // load the data for the entire animation + const double start_frame = static_cast<double>(proc->get_start_frame()); + const double end_frame = static_cast<double>(proc->get_end_frame()); + + const double frame_rate = static_cast<double>(proc->get_frame_rate()); + const double start_time = start_frame / frame_rate; + const double end_time = (end_frame + 1) / frame_rate; + + const size_t start_index = time_sampling.getFloorIndex(start_time, num_samples).first; + const size_t end_index = time_sampling.getCeilIndex(end_time, num_samples).first; + + for (size_t i = start_index; i < end_index; ++i) { + result.insert(time_sampling.getSampleTime(i)); + } + + return result; +} + +/* Main function to read data, this will iterate over all the relevant sample times for the + * duration of the requested animation, and call the DataReadingFunc for each of those sample time. + */ +template<typename Params, typename DataReadingFunc> +static void read_data_loop(AlembicProcedural *proc, + CachedData &cached_data, + const Params ¶ms, + DataReadingFunc &&func, + Progress &progress) +{ + const std::set<chrono_t> times = get_relevant_sample_times( + proc, *params.time_sampling, params.num_samples); + + cached_data.set_time_sampling(*params.time_sampling); + + for (chrono_t time : times) { + if (progress.get_cancel()) { + return; + } + + func(cached_data, params, time); + } +} + +/* Polygon Mesh Geometries. */ + +/* Compute the vertex normals in case none are present in the IPolyMeshSchema, this is mostly used + * to avoid computing them in the GeometryManager in order to speed up data updates. */ +static void compute_vertex_normals(CachedData &cache, double current_time) +{ + if (cache.vertices.size() == 0) { + return; + } + + CachedData::CachedAttribute &attr_normal = cache.add_attribute( + ustring("N"), cache.vertices.get_time_sampling()); + attr_normal.std = ATTR_STD_VERTEX_NORMAL; + attr_normal.element = ATTR_ELEMENT_VERTEX; + attr_normal.type_desc = TypeNormal; + + const array<float3> *vertices = + cache.vertices.data_for_time_no_check(current_time).get_data_or_null(); + const array<int3> *triangles = + cache.triangles.data_for_time_no_check(current_time).get_data_or_null(); + + if (!vertices || !triangles) { + attr_normal.data.add_no_data(current_time); + return; + } + + array<char> attr_data(vertices->size() * sizeof(float3)); + float3 *attr_ptr = reinterpret_cast<float3 *>(attr_data.data()); + memset(attr_ptr, 0, vertices->size() * sizeof(float3)); + + for (size_t t = 0; t < triangles->size(); ++t) { + const int3 tri_int3 = triangles->data()[t]; + Mesh::Triangle tri{}; + tri.v[0] = tri_int3[0]; + tri.v[1] = tri_int3[1]; + tri.v[2] = tri_int3[2]; + + const float3 tri_N = tri.compute_normal(vertices->data()); + + for (int v = 0; v < 3; ++v) { + attr_ptr[tri_int3[v]] += tri_N; + } + } + + for (size_t v = 0; v < vertices->size(); ++v) { + attr_ptr[v] = normalize(attr_ptr[v]); + } + + attr_normal.data.add_data(attr_data, current_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).get_data_or_null(); + + 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).get_data_or_null(); + + 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<int> uv_loops; + shader.reserve(num_triangles); + triangles.reserve(num_triangles); + uv_loops.reserve(num_triangles * 3); + 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)); + uv_loops.push_back_reserved(index_offset + j + 2); + uv_loops.push_back_reserved(index_offset + j + 1); + uv_loops.push_back_reserved(index_offset); + } + + index_offset += face_counts_array[i]; + } + + cached_data.triangles.add_data(triangles, time); + cached_data.uv_loops.add_data(uv_loops, time); + cached_data.shader.add_data(shader, time); +} + +static array<int> compute_polygon_to_shader_map( + const Int32ArraySamplePtr &face_counts, + const vector<FaceSetShaderIndexPair> &face_set_shader_index, + ISampleSelector sample_sel) +{ + if (face_set_shader_index.empty()) { + return {}; + } + + if (!face_counts) { + return {}; + } + + if (face_counts->size() == 0) { + return {}; + } + + array<int> polygon_to_shader(face_counts->size()); + + for (const FaceSetShaderIndexPair &pair : face_set_shader_index) { + const IFaceSet &face_set = pair.face_set; + 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] = pair.shader_index; + } + } + + return polygon_to_shader; +} + +static void read_poly_mesh_geometry(CachedData &cached_data, + const PolyMeshSchemaData &data, + chrono_t time) +{ + const ISampleSelector iss = ISampleSelector(time); + + add_positions(data.positions.getValue(iss), time, cached_data); + + const Int32ArraySamplePtr face_counts = data.face_counts.getValue(iss); + const Int32ArraySamplePtr face_indices = data.face_indices.getValue(iss); + + /* Only copy triangles for other frames if the topology is changing over time as well. */ + if (data.topology_variance != kHomogeneousTopology || cached_data.triangles.size() == 0) { + bool do_triangles = true; + + /* Compare key with last one to check whether the topology changed. */ + if (cached_data.triangles.size() > 0) { + const ArraySample::Key key = face_indices->getKey(); + + if (key == cached_data.triangles.key1) { + do_triangles = false; + } + + cached_data.triangles.key1 = key; + } + + if (do_triangles) { + const array<int> polygon_to_shader = compute_polygon_to_shader_map( + face_counts, data.shader_face_sets, iss); + add_triangles(face_counts, face_indices, time, cached_data, polygon_to_shader); + } + else { + cached_data.triangles.reuse_data_for_last_time(time); + cached_data.uv_loops.reuse_data_for_last_time(time); + cached_data.shader.reuse_data_for_last_time(time); + } + + /* Initialize the first key. */ + if (data.topology_variance != kHomogeneousTopology && cached_data.triangles.size() == 1) { + cached_data.triangles.key1 = face_indices->getKey(); + } + } + + if (data.normals.valid()) { + add_normals(face_indices, data.normals, time, cached_data); + } + else { + compute_vertex_normals(cached_data, time); + } +} + +void read_geometry_data(AlembicProcedural *proc, + CachedData &cached_data, + const PolyMeshSchemaData &data, + Progress &progress) +{ + read_data_loop(proc, cached_data, data, read_poly_mesh_geometry, progress); +} + +/* Subdivision Geometries */ + +static void add_subd_polygons(CachedData &cached_data, const SubDSchemaData &data, chrono_t time) +{ + const ISampleSelector iss = ISampleSelector(time); + + const Int32ArraySamplePtr face_counts = data.face_counts.getValue(iss); + const Int32ArraySamplePtr face_indices = data.face_indices.getValue(iss); + + 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; + array<int> uv_loops; + + 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); + uv_loops.reserve(num_corners); + + int start_corner = 0; + int current_shader = 0; + int ptex_offset = 0; + + const array<int> polygon_to_shader = compute_polygon_to_shader_map( + face_counts, data.shader_face_sets, iss); + + 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]); + uv_loops.push_back_reserved(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); + cached_data.uv_loops.add_data(uv_loops, time); +} + +static void add_subd_creases(CachedData &cached_data, const SubDSchemaData &data, chrono_t time) +{ + if (!(data.crease_indices.valid() && data.crease_indices.valid() && + data.crease_sharpnesses.valid())) { + return; + } + + const ISampleSelector iss = ISampleSelector(time); + + const Int32ArraySamplePtr creases_length = data.crease_lengths.getValue(iss); + const Int32ArraySamplePtr creases_indices = data.crease_indices.getValue(iss); + const FloatArraySamplePtr creases_sharpnesses = data.crease_sharpnesses.getValue(iss); + + 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); + } +} + +static void read_subd_geometry(CachedData &cached_data, const SubDSchemaData &data, chrono_t time) +{ + const ISampleSelector iss = ISampleSelector(time); + + add_positions(data.positions.getValue(iss), time, cached_data); + + if (data.topology_variance != kHomogenousTopology || cached_data.shader.size() == 0) { + add_subd_polygons(cached_data, data, time); + add_subd_creases(cached_data, data, time); + } +} + +void read_geometry_data(AlembicProcedural *proc, + CachedData &cached_data, + const SubDSchemaData &data, + Progress &progress) +{ + read_data_loop(proc, cached_data, data, read_subd_geometry, progress); +} + +/* Curve Geometries. */ + +static void read_curves_data(CachedData &cached_data, const CurvesSchemaData &data, chrono_t time) +{ + const ISampleSelector iss = ISampleSelector(time); + + const Int32ArraySamplePtr curves_num_vertices = data.num_vertices.getValue(iss); + const P3fArraySamplePtr position = data.positions.getValue(iss); + + FloatArraySamplePtr radiuses; + + if (data.widths.valid()) { + IFloatGeomParam::Sample wsample = data.widths.getExpandedValue(iss); + radiuses = wsample.getVals(); + } + + const bool do_radius = (radiuses != nullptr) && (radiuses->size() > 1); + float radius = (radiuses && radiuses->size() == 1) ? (*radiuses)[0] : data.default_radius; + + array<float3> curve_keys; + array<float> curve_radius; + array<int> curve_first_key; + array<int> curve_shader; + + const bool is_homogenous = data.topology_variance == kHomogenousTopology; + + 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]; + // todo(@kevindietrich): we are reading too much data? + curve_keys.push_back_slow(make_float3_from_yup(f)); + + if (do_radius) { + radius = (*radiuses)[offset + j]; + } + + curve_radius.push_back_slow(radius * data.radius_scale); + } + + if (!is_homogenous || cached_data.curve_first_key.size() == 0) { + 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); + + if (!is_homogenous || cached_data.curve_first_key.size() == 0) { + cached_data.curve_first_key.add_data(curve_first_key, time); + cached_data.curve_shader.add_data(curve_shader, time); + } +} + +void read_geometry_data(AlembicProcedural *proc, + CachedData &cached_data, + const CurvesSchemaData &data, + Progress &progress) +{ + read_data_loop(proc, cached_data, data, read_curves_data, progress); +} + +/* Attributes conversions. */ + +/* Type traits for converting between Alembic and Cycles types. + */ + +template<typename T> struct value_type_converter { + using cycles_type = float; + /* Use `TypeDesc::FLOAT` instead of `TypeFloat` to work around a compiler bug in gcc 11. */ + static constexpr TypeDesc type_desc = TypeDesc::FLOAT; + static constexpr const char *type_name = "float (default)"; + + static cycles_type convert_value(T value) + { + return static_cast<float>(value); + } +}; + +template<> struct value_type_converter<Imath::V2f> { + using cycles_type = float2; + static constexpr TypeDesc type_desc = TypeFloat2; + static constexpr const char *type_name = "float2"; + + static cycles_type convert_value(Imath::V2f value) + { + return make_float2(value.x, value.y); + } +}; + +template<> struct value_type_converter<Imath::V3f> { + using cycles_type = float3; + static constexpr TypeDesc type_desc = TypeVector; + static constexpr const char *type_name = "float3"; + + static cycles_type convert_value(Imath::V3f value) + { + return make_float3_from_yup(value); + } +}; + +template<> struct value_type_converter<Imath::C3f> { + using cycles_type = uchar4; + static constexpr TypeDesc type_desc = TypeRGBA; + static constexpr const char *type_name = "rgb"; + + static cycles_type convert_value(Imath::C3f value) + { + return color_float_to_byte(make_float3(value.x, value.y, value.z)); + } +}; + +template<> struct value_type_converter<Imath::C4f> { + using cycles_type = uchar4; + static constexpr TypeDesc type_desc = TypeRGBA; + static constexpr const char *type_name = "rgba"; + + static cycles_type convert_value(Imath::C4f value) + { + return color_float4_to_uchar4(make_float4(value.r, value.g, value.b, value.a)); + } +}; + +/* Main function used to read attributes of any type. */ +template<typename TRAIT> +static void process_attribute(CachedData &cache, + CachedData::CachedAttribute &attribute, + GeometryScope scope, + const typename ITypedGeomParam<TRAIT>::Sample &sample, + double time) +{ + using abc_type = typename TRAIT::value_type; + using cycles_type = typename value_type_converter<abc_type>::cycles_type; + + const TypedArraySample<TRAIT> &values = *sample.getVals(); + + switch (scope) { + case kConstantScope: + case kVertexScope: { + const array<float3> *vertices = + cache.vertices.data_for_time_no_check(time).get_data_or_null(); + + if (!vertices) { + attribute.data.add_no_data(time); + return; + } + + if (vertices->size() != values.size()) { + attribute.data.add_no_data(time); + return; + } + + array<char> data(vertices->size() * sizeof(cycles_type)); + + cycles_type *pod_typed_data = reinterpret_cast<cycles_type *>(data.data()); + + for (size_t i = 0; i < values.size(); ++i) { + *pod_typed_data++ = value_type_converter<abc_type>::convert_value(values[i]); + } + + attribute.data.add_data(data, time); + break; + } + case kVaryingScope: { + const array<int3> *triangles = + cache.triangles.data_for_time_no_check(time).get_data_or_null(); + + if (!triangles) { + attribute.data.add_no_data(time); + return; + } + + array<char> data(triangles->size() * 3 * sizeof(cycles_type)); + + cycles_type *pod_typed_data = reinterpret_cast<cycles_type *>(data.data()); + + for (const int3 &tri : *triangles) { + *pod_typed_data++ = value_type_converter<abc_type>::convert_value(values[tri.x]); + *pod_typed_data++ = value_type_converter<abc_type>::convert_value(values[tri.y]); + *pod_typed_data++ = value_type_converter<abc_type>::convert_value(values[tri.z]); + } + + attribute.data.add_data(data, time); + break; + } + default: { + break; + } + } +} + +/* UVs are processed separately as their indexing is based on loops, instead of vertices or + * corners. */ +static void process_uvs(CachedData &cache, + CachedData::CachedAttribute &attribute, + GeometryScope scope, + const IV2fGeomParam::Sample &sample, + double time) +{ + if (scope != kFacevaryingScope) { + return; + } + + const array<int> *uv_loops = cache.uv_loops.data_for_time_no_check(time).get_data_or_null(); + + if (!uv_loops) { + return; + } + + const array<int3> *triangles = cache.triangles.data_for_time_no_check(time).get_data_or_null(); + const array<int> *corners = + cache.subd_face_corners.data_for_time_no_check(time).get_data_or_null(); + + array<char> data; + if (triangles) { + data.resize(triangles->size() * 3 * sizeof(float2)); + } + else if (corners) { + data.resize(corners->size() * sizeof(float2)); + } + else { + return; + } + + float2 *data_float2 = reinterpret_cast<float2 *>(data.data()); + + const uint32_t *indices = sample.getIndices()->get(); + const V2f *values = sample.getVals()->get(); + + for (const int uv_loop_index : *uv_loops) { + const uint32_t index = indices[uv_loop_index]; + *data_float2++ = make_float2(values[index][0], values[index][1]); + } + + attribute.data.add_data(data, time); +} + +/* Type of the function used to parse one time worth of data, either process_uvs or + * process_attribute_generic. */ +template<typename TRAIT> +using process_callback_type = void (*)(CachedData &, + CachedData::CachedAttribute &, + GeometryScope, + const typename ITypedGeomParam<TRAIT>::Sample &, + double); + +/* Main loop to process the attributes, this will look at the given param's TimeSampling and + * extract data based on which frame time is requested by the procedural and execute the callback + * for each of those requested time. */ +template<typename TRAIT> +static void read_attribute_loop(AlembicProcedural *proc, + CachedData &cache, + const ITypedGeomParam<TRAIT> ¶m, + process_callback_type<TRAIT> callback, + Progress &progress, + AttributeStandard std = ATTR_STD_NONE) +{ + const std::set<chrono_t> times = get_relevant_sample_times( + proc, *param.getTimeSampling(), param.getNumSamples()); + + if (times.empty()) { + return; + } + + std::string name = param.getName(); + + if (std == ATTR_STD_UV) { + std::string uv_source_name = Alembic::Abc::GetSourceName(param.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 (!uv_source_name.empty()) { + name = uv_source_name; + } + } + + CachedData::CachedAttribute &attribute = cache.add_attribute(ustring(name), + *param.getTimeSampling()); + + using abc_type = typename TRAIT::value_type; + + attribute.data.set_time_sampling(*param.getTimeSampling()); + attribute.std = std; + attribute.type_desc = value_type_converter<abc_type>::type_desc; + + if (attribute.type_desc == TypeRGBA) { + attribute.element = ATTR_ELEMENT_CORNER_BYTE; + } + else { + if (param.getScope() == kVaryingScope || param.getScope() == kFacevaryingScope) { + attribute.element = ATTR_ELEMENT_CORNER; + } + else { + attribute.element = ATTR_ELEMENT_VERTEX; + } + } + + for (const chrono_t time : times) { + if (progress.get_cancel()) { + return; + } + + ISampleSelector iss = ISampleSelector(time); + typename ITypedGeomParam<TRAIT>::Sample sample; + param.getIndexed(sample, iss); + + if (!sample.valid()) { + continue; + } + + if (!sample.getVals()) { + attribute.data.add_no_data(time); + continue; + } + + /* Check whether we already loaded constant data. */ + if (attribute.data.size() != 0) { + if (param.isConstant()) { + return; + } + + const ArraySample::Key indices_key = sample.getIndices()->getKey(); + const ArraySample::Key values_key = sample.getVals()->getKey(); + + const bool is_same_as_last_time = (indices_key == attribute.data.key1 && + values_key == attribute.data.key2); + + attribute.data.key1 = indices_key; + attribute.data.key2 = values_key; + + if (is_same_as_last_time) { + attribute.data.reuse_data_for_last_time(time); + continue; + } + } + + callback(cache, attribute, param.getScope(), sample, time); + } +} + +/* Attributes requests. */ + +/* This structure is used to tell which ICoumpoundProperty the PropertyHeader comes from, as we + * need the parent when downcasting to the proper type. */ +struct PropHeaderAndParent { + const PropertyHeader *prop; + ICompoundProperty parent; +}; + +/* Parse the ICompoundProperty to look for properties whose names appear in the + * AttributeRequestSet. This also looks into any child ICompoundProperty of the given + * ICompoundProperty. If no property of the given name is found, let it be that way, Cycles will + * use a zero value for the missing attribute. */ +static void parse_requested_attributes_recursive(const AttributeRequestSet &requested_attributes, + const ICompoundProperty &arb_geom_params, + vector<PropHeaderAndParent> &requested_properties) +{ + if (!arb_geom_params.valid()) { + return; + } + + for (const AttributeRequest &req : requested_attributes.requests) { + const PropertyHeader *property_header = arb_geom_params.getPropertyHeader(req.name.c_str()); + + if (!property_header) { + continue; + } + + requested_properties.push_back({property_header, arb_geom_params}); + } + + /* Look into children compound properties. */ + for (size_t i = 0; i < arb_geom_params.getNumProperties(); ++i) { + const PropertyHeader &property_header = arb_geom_params.getPropertyHeader(i); + + if (property_header.isCompound()) { + ICompoundProperty compound_property = ICompoundProperty(arb_geom_params, + property_header.getName()); + parse_requested_attributes_recursive( + requested_attributes, compound_property, requested_properties); + } + } +} + +/* Main entry point for parsing requested attributes from an ICompoundProperty, this exists so that + * we can simply return the list of properties instead of allocating it on the stack and passing it + * as a parameter. */ +static vector<PropHeaderAndParent> parse_requested_attributes( + const AttributeRequestSet &requested_attributes, const ICompoundProperty &arb_geom_params) +{ + vector<PropHeaderAndParent> requested_properties; + parse_requested_attributes_recursive( + requested_attributes, arb_geom_params, requested_properties); + return requested_properties; +} + +/* Read the attributes requested by the shaders from the archive. This will recursively find named + * attributes from the AttributeRequestSet in the ICompoundProperty and any of its compound child. + * The attributes are added to the CachedData's attribute list. For each attribute we will try to + * deduplicate data across consecutive frames. */ +void read_attributes(AlembicProcedural *proc, + CachedData &cache, + const ICompoundProperty &arb_geom_params, + const IV2fGeomParam &default_uvs_param, + const AttributeRequestSet &requested_attributes, + Progress &progress) +{ + if (default_uvs_param.valid()) { + /* Only the default UVs should be treated as the standard UV attribute. */ + read_attribute_loop(proc, cache, default_uvs_param, process_uvs, progress, ATTR_STD_UV); + } + + vector<PropHeaderAndParent> requested_properties = parse_requested_attributes( + requested_attributes, arb_geom_params); + + for (const PropHeaderAndParent &prop_and_parent : requested_properties) { + if (progress.get_cancel()) { + return; + } + + const PropertyHeader *prop = prop_and_parent.prop; + const ICompoundProperty &parent = prop_and_parent.parent; + + if (IBoolGeomParam::matches(*prop)) { + const IBoolGeomParam ¶m = IBoolGeomParam(parent, prop->getName()); + read_attribute_loop(proc, cache, param, process_attribute<BooleanTPTraits>, progress); + } + else if (IInt32GeomParam::matches(*prop)) { + const IInt32GeomParam ¶m = IInt32GeomParam(parent, prop->getName()); + read_attribute_loop(proc, cache, param, process_attribute<Int32TPTraits>, progress); + } + else if (IFloatGeomParam::matches(*prop)) { + const IFloatGeomParam ¶m = IFloatGeomParam(parent, prop->getName()); + read_attribute_loop(proc, cache, param, process_attribute<Float32TPTraits>, progress); + } + else if (IV2fGeomParam::matches(*prop)) { + const IV2fGeomParam ¶m = IV2fGeomParam(parent, prop->getName()); + if (Alembic::AbcGeom::isUV(*prop)) { + read_attribute_loop(proc, cache, param, process_uvs, progress); + } + else { + read_attribute_loop(proc, cache, param, process_attribute<V2fTPTraits>, progress); + } + } + else if (IV3fGeomParam::matches(*prop)) { + const IV3fGeomParam ¶m = IV3fGeomParam(parent, prop->getName()); + read_attribute_loop(proc, cache, param, process_attribute<V3fTPTraits>, progress); + } + else if (IN3fGeomParam::matches(*prop)) { + const IN3fGeomParam ¶m = IN3fGeomParam(parent, prop->getName()); + read_attribute_loop(proc, cache, param, process_attribute<N3fTPTraits>, progress); + } + else if (IC3fGeomParam::matches(*prop)) { + const IC3fGeomParam ¶m = IC3fGeomParam(parent, prop->getName()); + read_attribute_loop(proc, cache, param, process_attribute<C3fTPTraits>, progress); + } + else if (IC4fGeomParam::matches(*prop)) { + const IC4fGeomParam ¶m = IC4fGeomParam(parent, prop->getName()); + read_attribute_loop(proc, cache, param, process_attribute<C4fTPTraits>, progress); + } + } + + cache.invalidate_last_loaded_time(true); +} + +CCL_NAMESPACE_END + +#endif diff --git a/intern/cycles/render/alembic_read.h b/intern/cycles/render/alembic_read.h new file mode 100644 index 00000000000..9cc8622a1ba --- /dev/null +++ b/intern/cycles/render/alembic_read.h @@ -0,0 +1,134 @@ +/* + * Copyright 2021 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 + +#ifdef WITH_ALEMBIC + +# include <Alembic/AbcCoreFactory/All.h> +# include <Alembic/AbcGeom/All.h> + +# include "util/util_vector.h" + +CCL_NAMESPACE_BEGIN + +class AlembicProcedural; +class AttributeRequestSet; +class Progress; +struct CachedData; + +/* Maps a FaceSet whose name matches that of a Shader to the index of said shader in the Geometry's + * used_shaders list. */ +struct FaceSetShaderIndexPair { + Alembic::AbcGeom::IFaceSet face_set; + int shader_index; +}; + +/* Data of an IPolyMeshSchema that we need to read. */ +struct PolyMeshSchemaData { + Alembic::AbcGeom::TimeSamplingPtr time_sampling; + size_t num_samples; + Alembic::AbcGeom::MeshTopologyVariance topology_variance; + + Alembic::AbcGeom::IP3fArrayProperty positions; + Alembic::AbcGeom::IInt32ArrayProperty face_indices; + Alembic::AbcGeom::IInt32ArrayProperty face_counts; + + Alembic::AbcGeom::IN3fGeomParam normals; + + vector<FaceSetShaderIndexPair> shader_face_sets; + + // Unsupported for now. + Alembic::AbcGeom::IV3fArrayProperty velocities; +}; + +void read_geometry_data(AlembicProcedural *proc, + CachedData &cached_data, + const PolyMeshSchemaData &data, + Progress &progress); + +/* Data of an ISubDSchema that we need to read. */ +struct SubDSchemaData { + Alembic::AbcGeom::TimeSamplingPtr time_sampling; + size_t num_samples; + Alembic::AbcGeom::MeshTopologyVariance topology_variance; + + Alembic::AbcGeom::IInt32ArrayProperty face_counts; + Alembic::AbcGeom::IInt32ArrayProperty face_indices; + Alembic::AbcGeom::IP3fArrayProperty positions; + + Alembic::AbcGeom::IInt32ArrayProperty crease_indices; + Alembic::AbcGeom::IInt32ArrayProperty crease_lengths; + Alembic::AbcGeom::IFloatArrayProperty crease_sharpnesses; + + vector<FaceSetShaderIndexPair> shader_face_sets; + + // Those are unsupported for now. + Alembic::AbcGeom::IInt32ArrayProperty corner_indices; + Alembic::AbcGeom::IFloatArrayProperty corner_sharpnesses; + Alembic::AbcGeom::IInt32Property face_varying_interpolate_boundary; + Alembic::AbcGeom::IInt32Property face_varying_propagate_corners; + Alembic::AbcGeom::IInt32Property interpolate_boundary; + Alembic::AbcGeom::IInt32ArrayProperty holes; + Alembic::AbcGeom::IStringProperty subdivision_scheme; + Alembic::AbcGeom::IV3fArrayProperty velocities; +}; + +void read_geometry_data(AlembicProcedural *proc, + CachedData &cached_data, + const SubDSchemaData &data, + Progress &progress); + +/* Data of a ICurvesSchema that we need to read. */ +struct CurvesSchemaData { + Alembic::AbcGeom::TimeSamplingPtr time_sampling; + size_t num_samples; + Alembic::AbcGeom::MeshTopologyVariance topology_variance; + + Alembic::AbcGeom::IP3fArrayProperty positions; + + Alembic::AbcGeom::IInt32ArrayProperty num_vertices; + + float default_radius; + float radius_scale; + + // Those are unsupported for now. + Alembic::AbcGeom::IV3fArrayProperty velocities; + // if this property is invalid then the weight for every point is 1 + Alembic::AbcGeom::IFloatArrayProperty position_weights; + Alembic::AbcGeom::IN3fGeomParam normals; + Alembic::AbcGeom::IFloatGeomParam widths; + Alembic::AbcGeom::IUcharArrayProperty orders; + Alembic::AbcGeom::IFloatArrayProperty knots; + + // TODO(@kevindietrich): type, basis, wrap +}; + +void read_geometry_data(AlembicProcedural *proc, + CachedData &cached_data, + const CurvesSchemaData &data, + Progress &progress); + +void read_attributes(AlembicProcedural *proc, + CachedData &cache, + const Alembic::AbcGeom::ICompoundProperty &arb_geom_params, + const Alembic::AbcGeom::IV2fGeomParam &default_uvs_param, + const AttributeRequestSet &requested_attributes, + Progress &progress); + +CCL_NAMESPACE_END + +#endif diff --git a/intern/cycles/render/attribute.cpp b/intern/cycles/render/attribute.cpp index 331d30802f1..bf9d69cb47e 100644 --- a/intern/cycles/render/attribute.cpp +++ b/intern/cycles/render/attribute.cpp @@ -383,6 +383,23 @@ AttributeStandard Attribute::name_standard(const char *name) return ATTR_STD_NONE; } +AttrKernelDataType Attribute::kernel_type(const Attribute &attr) +{ + if (attr.element == ATTR_ELEMENT_CORNER) { + return AttrKernelDataType::UCHAR4; + } + + if (attr.type == TypeDesc::TypeFloat) { + return AttrKernelDataType::FLOAT; + } + + if (attr.type == TypeFloat2) { + return AttrKernelDataType::FLOAT2; + } + + return AttrKernelDataType::FLOAT3; +} + void Attribute::get_uv_tiles(Geometry *geom, AttributePrimitive prim, unordered_set<int> &tiles) const @@ -417,7 +434,7 @@ void Attribute::get_uv_tiles(Geometry *geom, /* Attribute Set */ AttributeSet::AttributeSet(Geometry *geometry, AttributePrimitive prim) - : geometry(geometry), prim(prim) + : modified_flag(~0u), geometry(geometry), prim(prim) { } @@ -440,7 +457,7 @@ Attribute *AttributeSet::add(ustring name, TypeDesc type, AttributeElement eleme Attribute new_attr(name, type, element, geometry, prim); attributes.emplace_back(std::move(new_attr)); - modified = true; + tag_modified(attributes.back()); return &attributes.back(); } @@ -462,8 +479,7 @@ void AttributeSet::remove(ustring name) for (it = attributes.begin(); it != attributes.end(); it++) { if (&*it == attr) { - modified = true; - attributes.erase(it); + remove(it); return; } } @@ -608,8 +624,7 @@ void AttributeSet::remove(AttributeStandard std) for (it = attributes.begin(); it != attributes.end(); it++) { if (&*it == attr) { - modified = true; - attributes.erase(it); + remove(it); return; } } @@ -634,6 +649,12 @@ void AttributeSet::remove(Attribute *attribute) } } +void AttributeSet::remove(list<Attribute>::iterator it) +{ + tag_modified(*it); + attributes.erase(it); +} + void AttributeSet::resize(bool reserve_only) { foreach (Attribute &attr, attributes) { @@ -674,21 +695,22 @@ void AttributeSet::update(AttributeSet &&new_attributes) for (it = attributes.begin(); it != attributes.end();) { if (it->std != ATTR_STD_NONE) { if (new_attributes.find(it->std) == nullptr) { - modified = true; - attributes.erase(it++); + remove(it++); continue; } } else if (it->name != "") { if (new_attributes.find(it->name) == nullptr) { - modified = true; - attributes.erase(it++); + remove(it++); continue; } } it++; } + + /* If all attributes were replaced, transform is no longer applied. */ + geometry->transform_applied = false; } void AttributeSet::clear_modified() @@ -696,7 +718,27 @@ void AttributeSet::clear_modified() foreach (Attribute &attr, attributes) { attr.modified = false; } - modified = false; + + modified_flag = 0; +} + +void AttributeSet::tag_modified(const Attribute &attr) +{ + /* Some attributes are not stored in the various kernel attribute arrays + * (DeviceScene::attribute_*), so the modified flags are only set if the associated standard + * corresponds to an attribute which will be stored in the kernel's attribute arrays. */ + const bool modifies_device_array = (attr.std != ATTR_STD_FACE_NORMAL && + attr.std != ATTR_STD_VERTEX_NORMAL); + + if (modifies_device_array) { + AttrKernelDataType kernel_type = Attribute::kernel_type(attr); + modified_flag |= (1u << kernel_type); + } +} + +bool AttributeSet::modified(AttrKernelDataType kernel_type) const +{ + return (modified_flag & (1u << kernel_type)) != 0; } /* AttributeRequest */ diff --git a/intern/cycles/render/attribute.h b/intern/cycles/render/attribute.h index 18c9e5ab83a..004c267cabc 100644 --- a/intern/cycles/render/attribute.h +++ b/intern/cycles/render/attribute.h @@ -39,6 +39,21 @@ class Hair; class Mesh; struct Transform; +/* AttrKernelDataType. + * + * The data type of the device arrays storing the attribute's data. Those data types are different + * than the ones for attributes as some attribute types are stored in the same array, e.g. Point, + * Vector, and Transform are all stored as float3 in the kernel. + * + * The values of this enumeration are also used as flags to detect changes in AttributeSet. */ + +enum AttrKernelDataType { + FLOAT = 0, + FLOAT2 = 1, + FLOAT3 = 2, + UCHAR4 = 3, +}; + /* Attribute * * Arbitrary data layers on meshes. @@ -167,6 +182,8 @@ class Attribute { static const char *standard_name(AttributeStandard std); static AttributeStandard name_standard(const char *name); + static AttrKernelDataType kernel_type(const Attribute &attr); + void get_uv_tiles(Geometry *geom, AttributePrimitive prim, unordered_set<int> &tiles) const; }; @@ -175,11 +192,12 @@ class Attribute { * Set of attributes on a mesh. */ class AttributeSet { + uint32_t modified_flag; + public: Geometry *geometry; AttributePrimitive prim; list<Attribute> attributes; - bool modified = true; AttributeSet(Geometry *geometry, AttributePrimitive prim); AttributeSet(AttributeSet &&) = default; @@ -197,6 +215,8 @@ class AttributeSet { void remove(Attribute *attribute); + void remove(list<Attribute>::iterator it); + void resize(bool reserve_only = false); void clear(bool preserve_voxel_data = false); @@ -204,7 +224,18 @@ class AttributeSet { * and remove any attribute not found on the new set from this. */ void update(AttributeSet &&new_attributes); + /* Return whether the attributes of the given kernel_type are modified, where "modified" means + * that some attributes of the given type were added or removed from this AttributeSet. This does + * not mean that the data of the remaining attributes in this AttributeSet were also modified. To + * check this, use Attribute.modified. */ + bool modified(AttrKernelDataType kernel_type) const; + void clear_modified(); + + private: + /* Set the relevant modified flag for the attribute. Only attributes that are stored in device + * arrays will be considered for tagging this AttributeSet as modified. */ + void tag_modified(const Attribute &attr); }; /* AttributeRequest diff --git a/intern/cycles/render/background.cpp b/intern/cycles/render/background.cpp index b3d383afae4..b925e755434 100644 --- a/intern/cycles/render/background.cpp +++ b/intern/cycles/render/background.cpp @@ -59,6 +59,7 @@ Background::Background() : Node(get_node_type()) Background::~Background() { + dereference_all_used_nodes(); } void Background::device_update(Device *device, DeviceScene *dscene, Scene *scene) @@ -130,6 +131,14 @@ void Background::device_free(Device * /*device*/, DeviceScene * /*dscene*/) void Background::tag_update(Scene *scene) { + Shader *bg_shader = get_shader(scene); + if (bg_shader && bg_shader->is_modified()) { + /* Tag as modified to update the KernelBackground visibility information. + * We only tag the use_shader socket as modified as it is related to the shader + * and to avoid doing unnecessary updates anywhere else. */ + tag_use_shader_modified(); + } + if (ao_factor_is_modified() || use_ao_is_modified()) { scene->integrator->tag_update(scene, Integrator::BACKGROUND_AO_MODIFIED); } diff --git a/intern/cycles/render/geometry.cpp b/intern/cycles/render/geometry.cpp index 124a41db21e..ce76658acb6 100644 --- a/intern/cycles/render/geometry.cpp +++ b/intern/cycles/render/geometry.cpp @@ -46,6 +46,12 @@ CCL_NAMESPACE_BEGIN /* Geometry */ +PackFlags operator|=(PackFlags &pack_flags, uint32_t value) +{ + pack_flags = (PackFlags)((uint32_t)pack_flags | value); + return pack_flags; +} + NODE_ABSTRACT_DEFINE(Geometry) { NodeType *type = NodeType::add("geometry_base", NULL); @@ -79,6 +85,7 @@ Geometry::Geometry(const NodeType *node_type, const Type type) Geometry::~Geometry() { + dereference_all_used_nodes(); delete bvh; } @@ -823,10 +830,13 @@ 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(); + /* The order of those flags needs to match that of AttrKernelDataType. */ + const bool attributes_need_realloc[4] = { + 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; @@ -845,7 +855,7 @@ void GeometryManager::device_update_attributes(Device *device, if (attr) { /* force a copy if we need to reallocate all the data */ - attr->modified |= copy_all_data; + attr->modified |= attributes_need_realloc[Attribute::kernel_type(*attr)]; } update_attribute_element_offset(geom, @@ -868,7 +878,7 @@ void GeometryManager::device_update_attributes(Device *device, if (subd_attr) { /* force a copy if we need to reallocate all the data */ - subd_attr->modified |= copy_all_data; + subd_attr->modified |= attributes_need_realloc[Attribute::kernel_type(*subd_attr)]; } update_attribute_element_offset(mesh, @@ -899,6 +909,10 @@ void GeometryManager::device_update_attributes(Device *device, foreach (AttributeRequest &req, attributes.requests) { Attribute *attr = values.find(req); + if (attr) { + attr->modified |= attributes_need_realloc[Attribute::kernel_type(*attr)]; + } + update_attribute_element_offset(object->geometry, dscene->attributes_float, attr_float_offset, @@ -934,10 +948,10 @@ void GeometryManager::device_update_attributes(Device *device, /* copy to device */ progress.set_status("Updating Mesh", "Copying Attributes 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(); + dscene->attributes_float.copy_to_device_if_modified(); + dscene->attributes_float2.copy_to_device_if_modified(); + dscene->attributes_float3.copy_to_device_if_modified(); + dscene->attributes_uchar4.copy_to_device_if_modified(); if (progress.get_cancel()) return; @@ -1235,7 +1249,16 @@ void GeometryManager::device_update_bvh(Device *device, const bool can_refit = scene->bvh != nullptr && (bparams.bvh_layout == BVHLayout::BVH_LAYOUT_OPTIX); - const bool pack_all = scene->bvh == nullptr; + + PackFlags pack_flags = PackFlags::PACK_NONE; + + if (scene->bvh == nullptr) { + pack_flags |= PackFlags::PACK_ALL; + } + + if (dscene->prim_visibility.is_modified()) { + pack_flags |= PackFlags::PACK_VISIBILITY; + } BVH *bvh = scene->bvh; if (!scene->bvh) { @@ -1273,10 +1296,14 @@ void GeometryManager::device_update_bvh(Device *device, pack.root_index = -1; - if (!pack_all) { + if (pack_flags != PackFlags::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); + + if ((pack_flags & PackFlags::PACK_VISIBILITY) != 0) { + dscene->prim_visibility.give_data(pack.prim_visibility); + } } else { /* It is not strictly necessary to skip those resizes we if do not have to repack, as the OS @@ -1305,13 +1332,21 @@ void GeometryManager::device_update_bvh(Device *device, // 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()) { + /* Make a copy of the pack_flags so the current geometry's flags do not pollute the others'. + */ + PackFlags geom_pack_flags = pack_flags; + + if (geom->is_modified()) { + geom_pack_flags |= PackFlags::PACK_VERTICES; + } + + if (geom_pack_flags == PACK_NONE) { continue; } const pair<int, uint> &info = geometry_to_object_info[geom]; pool.push(function_bind( - &Geometry::pack_primitives, geom, &pack, info.first, info.second, pack_all)); + &Geometry::pack_primitives, geom, &pack, info.first, info.second, geom_pack_flags)); } pool.wait_work(); } @@ -1346,7 +1381,7 @@ void GeometryManager::device_update_bvh(Device *device, dscene->prim_type.steal_data(pack.prim_type); dscene->prim_type.copy_to_device(); } - if (pack.prim_visibility.size() && (dscene->prim_visibility.need_realloc() || has_bvh2_layout)) { + if (pack.prim_visibility.size() && (dscene->prim_visibility.is_modified() || has_bvh2_layout)) { dscene->prim_visibility.steal_data(pack.prim_visibility); dscene->prim_visibility.copy_to_device(); } @@ -1364,7 +1399,6 @@ void GeometryManager::device_update_bvh(Device *device, } dscene->data.bvh.root = pack.root_index; - dscene->data.bvh.bvh_layout = bparams.bvh_layout; dscene->data.bvh.use_bvh_steps = (scene->params.num_bvh_time_steps != 0); dscene->data.bvh.curve_subdivisions = scene->params.curve_subdivisions(); /* The scene handle is set in 'CPUDevice::const_copy_to' and 'OptiXDevice::const_copy_to' */ @@ -1404,24 +1438,46 @@ static void update_device_flags_attribute(uint32_t &device_update_flags, 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; + AttrKernelDataType kernel_type = Attribute::kernel_type(attr); + + switch (kernel_type) { + case AttrKernelDataType::FLOAT: { + device_update_flags |= ATTR_FLOAT_MODIFIED; + break; + } + case AttrKernelDataType::FLOAT2: { + device_update_flags |= ATTR_FLOAT2_MODIFIED; + break; + } + case AttrKernelDataType::FLOAT3: { + device_update_flags |= ATTR_FLOAT3_MODIFIED; + break; + } + case AttrKernelDataType::UCHAR4: { + device_update_flags |= ATTR_UCHAR4_MODIFIED; + break; + } } } } +static void update_attribute_realloc_flags(uint32_t &device_update_flags, + const AttributeSet &attributes) +{ + if (attributes.modified(AttrKernelDataType::FLOAT)) { + device_update_flags |= ATTR_FLOAT_NEEDS_REALLOC; + } + if (attributes.modified(AttrKernelDataType::FLOAT2)) { + device_update_flags |= ATTR_FLOAT2_NEEDS_REALLOC; + } + if (attributes.modified(AttrKernelDataType::FLOAT3)) { + device_update_flags |= ATTR_FLOAT3_NEEDS_REALLOC; + } + if (attributes.modified(AttrKernelDataType::UCHAR4)) { + device_update_flags |= ATTR_UCHAR4_NEEDS_REALLOC; + } +} + void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Progress &progress) { if (!need_update() && !need_flags_update) { @@ -1444,16 +1500,11 @@ void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Pro foreach (Geometry *geom, scene->geometry) { geom->has_volume = false; - if (geom->attributes.modified) { - device_update_flags |= ATTRS_NEED_REALLOC; - } + update_attribute_realloc_flags(device_update_flags, geom->attributes); if (geom->is_mesh()) { Mesh *mesh = static_cast<Mesh *>(geom); - - if (mesh->subd_attributes.modified) { - device_update_flags |= ATTRS_NEED_REALLOC; - } + update_attribute_realloc_flags(device_update_flags, mesh->subd_attributes); } foreach (Node *node, geom->get_used_shaders()) { @@ -1584,7 +1635,6 @@ void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Pro 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->tri_shader.tag_realloc(); dscene->patches.tag_realloc(); @@ -1596,6 +1646,10 @@ void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Pro } } + if ((update_flags & VISIBILITY_MODIFIED) != 0) { + dscene->prim_visibility.tag_modified(); + } + if (device_update_flags & ATTR_FLOAT_NEEDS_REALLOC) { dscene->attributes_map.tag_realloc(); dscene->attributes_float.tag_realloc(); @@ -1922,7 +1976,8 @@ void GeometryManager::device_update(Device *device, * Also update the BVH if the transformations change, we cannot rely on tagging the Geometry * as modified in this case, as we may accumulate displacement if the vertices do not also * change. */ - bool need_update_scene_bvh = (scene->bvh == nullptr || (update_flags & TRANSFORM_MODIFIED) != 0); + bool need_update_scene_bvh = (scene->bvh == nullptr || + (update_flags & (TRANSFORM_MODIFIED | VISIBILITY_MODIFIED)) != 0); { scoped_callback_timer timer([scene](double time) { if (scene->update_stats) { @@ -1985,6 +2040,11 @@ void GeometryManager::device_update(Device *device, } } + /* Always set BVH layout again after displacement where it was set to none, + * to avoid ray-tracing at that stage. */ + dscene->data.bvh.bvh_layout = BVHParams::best_bvh_layout(scene->params.bvh_layout, + device->get_bvh_layout_mask()); + { scoped_callback_timer timer([scene](double time) { if (scene->update_stats) { @@ -2003,7 +2063,7 @@ void GeometryManager::device_update(Device *device, * for meshes with correct bounding boxes. * * This wouldn't cause wrong results, just true - * displacement might be less optimal ot calculate. + * displacement might be less optimal to calculate. */ scene->object_manager->need_flags_update = old_need_object_flags_update; } diff --git a/intern/cycles/render/geometry.h b/intern/cycles/render/geometry.h index abdd851a089..7db122f69cb 100644 --- a/intern/cycles/render/geometry.h +++ b/intern/cycles/render/geometry.h @@ -43,6 +43,24 @@ class Shader; class Volume; struct PackedBVH; +/* Flags used to determine which geometry data need to be packed. */ +enum PackFlags : uint32_t { + PACK_NONE = 0u, + + /* Pack the geometry information (e.g. triangle or curve keys indices). */ + PACK_GEOMETRY = (1u << 0), + + /* Pack the vertices, for Meshes and Volumes' bounding meshes. */ + PACK_VERTICES = (1u << 1), + + /* Pack the visibility flags for each triangle or curve. */ + PACK_VISIBILITY = (1u << 2), + + PACK_ALL = (PACK_GEOMETRY | PACK_VERTICES | PACK_VISIBILITY), +}; + +PackFlags operator|=(PackFlags &pack_flags, uint32_t value); + /* Geometry * * Base class for geometric types like Mesh and Hair. */ @@ -126,7 +144,10 @@ class Geometry : public Node { int n, int total); - virtual void pack_primitives(PackedBVH *pack, int object, uint visibility, bool pack_all) = 0; + virtual void pack_primitives(PackedBVH *pack, + int object, + uint visibility, + PackFlags pack_flags) = 0; /* Check whether the geometry should have own BVH built separately. Briefly, * own BVH is needed for geometry, if: @@ -191,6 +212,8 @@ class GeometryManager { TRANSFORM_MODIFIED = (1 << 10), + VISIBILITY_MODIFIED = (1 << 11), + /* tag everything in the manager for an update */ UPDATE_ALL = ~0u, diff --git a/intern/cycles/render/hair.cpp b/intern/cycles/render/hair.cpp index dad235aa340..72fc612c0c0 100644 --- a/intern/cycles/render/hair.cpp +++ b/intern/cycles/render/hair.cpp @@ -494,38 +494,47 @@ void Hair::pack_curves(Scene *scene, } } -void Hair::pack_primitives(PackedBVH *pack, int object, uint visibility, bool pack_all) +void Hair::pack_primitives(PackedBVH *pack, int object, uint visibility, PackFlags pack_flags) { if (curve_first_key.empty()) return; - /* If the BVH does not have to be recreated, we can bail out. */ - if (!pack_all) { - return; + /* Separate loop as other arrays are not initialized if their packing is not required. */ + if ((pack_flags & PACK_VISIBILITY) != 0) { + unsigned int *prim_visibility = &pack->prim_visibility[optix_prim_offset]; + + 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, ++index) { + prim_visibility[index] = visibility; + } + } } - 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, ++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 - prim_index[index] = j + prim_offset; - prim_object[index] = object; + if ((pack_flags & PACK_GEOMETRY) != 0) { + unsigned int *prim_tri_index = &pack->prim_tri_index[optix_prim_offset]; + int *prim_type = &pack->prim_type[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, ++index) { + prim_tri_index[index] = -1; + prim_type[index] = PRIMITIVE_PACK_SEGMENT(type, k); + // Each curve segment points back to its curve index + 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 4b949f984e5..1a8f422e8c4 100644 --- a/intern/cycles/render/hair.h +++ b/intern/cycles/render/hair.h @@ -146,7 +146,10 @@ 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, bool pack_all) override; + void pack_primitives(PackedBVH *pack, + int object, + uint visibility, + PackFlags pack_flags) override; }; CCL_NAMESPACE_END diff --git a/intern/cycles/render/image.cpp b/intern/cycles/render/image.cpp index 29a95beaf7e..27f9b7df1dd 100644 --- a/intern/cycles/render/image.cpp +++ b/intern/cycles/render/image.cpp @@ -303,7 +303,8 @@ ImageManager::ImageManager(const DeviceInfo &info) animation_frame = 0; /* Set image limits */ - has_half_images = info.has_half_images; + features.has_half_float = info.has_half_images; + features.has_nanovdb = info.has_nanovdb; } ImageManager::~ImageManager() @@ -347,7 +348,7 @@ void ImageManager::load_image_metadata(Image *img) metadata = ImageMetaData(); metadata.colorspace = img->params.colorspace; - if (img->loader->load_metadata(metadata)) { + if (img->loader->load_metadata(features, metadata)) { assert(metadata.type != IMAGE_DATA_NUM_TYPES); } else { @@ -356,15 +357,10 @@ void ImageManager::load_image_metadata(Image *img) metadata.detect_colorspace(); - /* No half textures on OpenCL, use full float instead. */ - if (!has_half_images) { - if (metadata.type == IMAGE_DATA_TYPE_HALF4) { - metadata.type = IMAGE_DATA_TYPE_FLOAT4; - } - else if (metadata.type == IMAGE_DATA_TYPE_HALF) { - metadata.type = IMAGE_DATA_TYPE_FLOAT; - } - } + assert(features.has_half_float || + (metadata.type != IMAGE_DATA_TYPE_HALF4 && metadata.type != IMAGE_DATA_TYPE_HALF)); + assert(features.has_nanovdb || (metadata.type != IMAGE_DATA_TYPE_NANOVDB_FLOAT || + metadata.type != IMAGE_DATA_TYPE_NANOVDB_FLOAT3)); img->need_metadata = false; } diff --git a/intern/cycles/render/image.h b/intern/cycles/render/image.h index c802521db56..dede9513d5f 100644 --- a/intern/cycles/render/image.h +++ b/intern/cycles/render/image.h @@ -97,6 +97,13 @@ class ImageMetaData { void detect_colorspace(); }; +/* Information about supported features that Image loaders can use. */ +class ImageDeviceFeatures { + public: + bool has_half_float; + bool has_nanovdb; +}; + /* Image loader base class, that can be subclassed to load image data * from custom sources (file, memory, procedurally generated, etc). */ class ImageLoader { @@ -105,7 +112,7 @@ class ImageLoader { virtual ~ImageLoader(){}; /* Load metadata without actual image yet, should be fast. */ - virtual bool load_metadata(ImageMetaData &metadata) = 0; + virtual bool load_metadata(const ImageDeviceFeatures &features, ImageMetaData &metadata) = 0; /* Load actual image contents. */ virtual bool load_pixels(const ImageMetaData &metadata, @@ -212,7 +219,8 @@ class ImageManager { private: bool need_update_; - bool has_half_images; + + ImageDeviceFeatures features; thread_mutex device_mutex; thread_mutex images_mutex; diff --git a/intern/cycles/render/image_oiio.cpp b/intern/cycles/render/image_oiio.cpp index e9c87461822..4867efe6ac0 100644 --- a/intern/cycles/render/image_oiio.cpp +++ b/intern/cycles/render/image_oiio.cpp @@ -30,7 +30,7 @@ OIIOImageLoader::~OIIOImageLoader() { } -bool OIIOImageLoader::load_metadata(ImageMetaData &metadata) +bool OIIOImageLoader::load_metadata(const ImageDeviceFeatures &features, ImageMetaData &metadata) { /* Perform preliminary checks, with meaningful logging. */ if (!path_exists(filepath.string())) { @@ -76,7 +76,7 @@ bool OIIOImageLoader::load_metadata(ImageMetaData &metadata) } /* check if it's half float */ - if (spec.format == TypeDesc::HALF) { + if (spec.format == TypeDesc::HALF && features.has_half_float) { is_half = true; } diff --git a/intern/cycles/render/image_oiio.h b/intern/cycles/render/image_oiio.h index a234b968557..a6dbb168b65 100644 --- a/intern/cycles/render/image_oiio.h +++ b/intern/cycles/render/image_oiio.h @@ -26,7 +26,7 @@ class OIIOImageLoader : public ImageLoader { OIIOImageLoader(const string &filepath); ~OIIOImageLoader(); - bool load_metadata(ImageMetaData &metadata) override; + bool load_metadata(const ImageDeviceFeatures &features, ImageMetaData &metadata) override; bool load_pixels(const ImageMetaData &metadata, void *pixels, diff --git a/intern/cycles/render/image_sky.cpp b/intern/cycles/render/image_sky.cpp index 0560907c63e..7f9b85836f8 100644 --- a/intern/cycles/render/image_sky.cpp +++ b/intern/cycles/render/image_sky.cpp @@ -40,7 +40,7 @@ SkyLoader::SkyLoader(float sun_elevation, SkyLoader::~SkyLoader(){}; -bool SkyLoader::load_metadata(ImageMetaData &metadata) +bool SkyLoader::load_metadata(const ImageDeviceFeatures &, ImageMetaData &metadata) { metadata.width = 512; metadata.height = 128; diff --git a/intern/cycles/render/image_sky.h b/intern/cycles/render/image_sky.h index 686f4e5b885..89ff586e7fd 100644 --- a/intern/cycles/render/image_sky.h +++ b/intern/cycles/render/image_sky.h @@ -34,7 +34,7 @@ class SkyLoader : public ImageLoader { float ozone_density); ~SkyLoader(); - bool load_metadata(ImageMetaData &metadata) override; + bool load_metadata(const ImageDeviceFeatures &features, ImageMetaData &metadata) override; bool load_pixels(const ImageMetaData &metadata, void *pixels, diff --git a/intern/cycles/render/image_vdb.cpp b/intern/cycles/render/image_vdb.cpp index 70b3de5a939..fb6394e8917 100644 --- a/intern/cycles/render/image_vdb.cpp +++ b/intern/cycles/render/image_vdb.cpp @@ -34,7 +34,7 @@ VDBImageLoader::~VDBImageLoader() { } -bool VDBImageLoader::load_metadata(ImageMetaData &metadata) +bool VDBImageLoader::load_metadata(const ImageDeviceFeatures &features, ImageMetaData &metadata) { #ifdef WITH_OPENVDB if (!grid) { @@ -56,55 +56,71 @@ bool VDBImageLoader::load_metadata(ImageMetaData &metadata) if (grid->isType<openvdb::FloatGrid>()) { metadata.channels = 1; # ifdef WITH_NANOVDB - nanogrid = nanovdb::openToNanoVDB(*openvdb::gridConstPtrCast<openvdb::FloatGrid>(grid)); + if (features.has_nanovdb) { + nanogrid = nanovdb::openToNanoVDB(*openvdb::gridConstPtrCast<openvdb::FloatGrid>(grid)); + } # endif } else if (grid->isType<openvdb::Vec3fGrid>()) { metadata.channels = 3; # ifdef WITH_NANOVDB - nanogrid = nanovdb::openToNanoVDB(*openvdb::gridConstPtrCast<openvdb::Vec3fGrid>(grid)); + if (features.has_nanovdb) { + nanogrid = nanovdb::openToNanoVDB(*openvdb::gridConstPtrCast<openvdb::Vec3fGrid>(grid)); + } # endif } else if (grid->isType<openvdb::BoolGrid>()) { metadata.channels = 1; # ifdef WITH_NANOVDB - nanogrid = nanovdb::openToNanoVDB( - openvdb::FloatGrid(*openvdb::gridConstPtrCast<openvdb::BoolGrid>(grid))); + if (features.has_nanovdb) { + nanogrid = nanovdb::openToNanoVDB( + openvdb::FloatGrid(*openvdb::gridConstPtrCast<openvdb::BoolGrid>(grid))); + } # endif } else if (grid->isType<openvdb::DoubleGrid>()) { metadata.channels = 1; # ifdef WITH_NANOVDB - nanogrid = nanovdb::openToNanoVDB( - openvdb::FloatGrid(*openvdb::gridConstPtrCast<openvdb::DoubleGrid>(grid))); + if (features.has_nanovdb) { + nanogrid = nanovdb::openToNanoVDB( + openvdb::FloatGrid(*openvdb::gridConstPtrCast<openvdb::DoubleGrid>(grid))); + } # endif } else if (grid->isType<openvdb::Int32Grid>()) { metadata.channels = 1; # ifdef WITH_NANOVDB - nanogrid = nanovdb::openToNanoVDB( - openvdb::FloatGrid(*openvdb::gridConstPtrCast<openvdb::Int32Grid>(grid))); + if (features.has_nanovdb) { + nanogrid = nanovdb::openToNanoVDB( + openvdb::FloatGrid(*openvdb::gridConstPtrCast<openvdb::Int32Grid>(grid))); + } # endif } else if (grid->isType<openvdb::Int64Grid>()) { metadata.channels = 1; # ifdef WITH_NANOVDB - nanogrid = nanovdb::openToNanoVDB( - openvdb::FloatGrid(*openvdb::gridConstPtrCast<openvdb::Int64Grid>(grid))); + if (features.has_nanovdb) { + nanogrid = nanovdb::openToNanoVDB( + openvdb::FloatGrid(*openvdb::gridConstPtrCast<openvdb::Int64Grid>(grid))); + } # endif } else if (grid->isType<openvdb::Vec3IGrid>()) { metadata.channels = 3; # ifdef WITH_NANOVDB - nanogrid = nanovdb::openToNanoVDB( - openvdb::Vec3fGrid(*openvdb::gridConstPtrCast<openvdb::Vec3IGrid>(grid))); + if (features.has_nanovdb) { + nanogrid = nanovdb::openToNanoVDB( + openvdb::Vec3fGrid(*openvdb::gridConstPtrCast<openvdb::Vec3IGrid>(grid))); + } # endif } else if (grid->isType<openvdb::Vec3dGrid>()) { metadata.channels = 3; # ifdef WITH_NANOVDB - nanogrid = nanovdb::openToNanoVDB( - openvdb::Vec3fGrid(*openvdb::gridConstPtrCast<openvdb::Vec3dGrid>(grid))); + if (features.has_nanovdb) { + nanogrid = nanovdb::openToNanoVDB( + openvdb::Vec3fGrid(*openvdb::gridConstPtrCast<openvdb::Vec3dGrid>(grid))); + } # endif } else if (grid->isType<openvdb::MaskGrid>()) { @@ -118,21 +134,25 @@ bool VDBImageLoader::load_metadata(ImageMetaData &metadata) } # ifdef WITH_NANOVDB - metadata.byte_size = nanogrid.size(); - if (metadata.channels == 1) { - metadata.type = IMAGE_DATA_TYPE_NANOVDB_FLOAT; - } - else { - metadata.type = IMAGE_DATA_TYPE_NANOVDB_FLOAT3; - } -# else - if (metadata.channels == 1) { - metadata.type = IMAGE_DATA_TYPE_FLOAT; - } - else { - metadata.type = IMAGE_DATA_TYPE_FLOAT4; + if (nanogrid) { + metadata.byte_size = nanogrid.size(); + if (metadata.channels == 1) { + metadata.type = IMAGE_DATA_TYPE_NANOVDB_FLOAT; + } + else { + metadata.type = IMAGE_DATA_TYPE_NANOVDB_FLOAT3; + } } + else # endif + { + if (metadata.channels == 1) { + metadata.type = IMAGE_DATA_TYPE_FLOAT; + } + else { + metadata.type = IMAGE_DATA_TYPE_FLOAT4; + } + } /* Set transform from object space to voxel index. */ openvdb::math::Mat4f grid_matrix = grid->transform().baseMap()->getAffineMap()->getMat4(); @@ -143,20 +163,29 @@ bool VDBImageLoader::load_metadata(ImageMetaData &metadata) } } + Transform texture_to_index; # ifdef WITH_NANOVDB - Transform texture_to_index = transform_identity(); -# else - openvdb::Coord min = bbox.min(); - Transform texture_to_index = transform_translate(min.x(), min.y(), min.z()) * - transform_scale(dim.x(), dim.y(), dim.z()); + if (nanogrid) { + texture_to_index = transform_identity(); + } + else # endif + { + openvdb::Coord min = bbox.min(); + texture_to_index = transform_translate(min.x(), min.y(), min.z()) * + transform_scale(dim.x(), dim.y(), dim.z()); + } metadata.transform_3d = transform_inverse(index_to_object * texture_to_index); metadata.use_transform_3d = true; +# ifndef WITH_NANOVDB + (void)features; +# endif return true; #else (void)metadata; + (void)features; return false; #endif } @@ -165,48 +194,52 @@ bool VDBImageLoader::load_pixels(const ImageMetaData &, void *pixels, const size { #ifdef WITH_OPENVDB # ifdef WITH_NANOVDB - memcpy(pixels, nanogrid.data(), nanogrid.size()); -# else - if (grid->isType<openvdb::FloatGrid>()) { - openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, (float *)pixels); - openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::FloatGrid>(grid), dense); - } - else if (grid->isType<openvdb::Vec3fGrid>()) { - openvdb::tools::Dense<openvdb::Vec3f, openvdb::tools::LayoutXYZ> dense( - bbox, (openvdb::Vec3f *)pixels); - openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Vec3fGrid>(grid), dense); - } - else if (grid->isType<openvdb::BoolGrid>()) { - openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, (float *)pixels); - openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::BoolGrid>(grid), dense); - } - else if (grid->isType<openvdb::DoubleGrid>()) { - openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, (float *)pixels); - openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::DoubleGrid>(grid), dense); - } - else if (grid->isType<openvdb::Int32Grid>()) { - openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, (float *)pixels); - openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Int32Grid>(grid), dense); - } - else if (grid->isType<openvdb::Int64Grid>()) { - openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, (float *)pixels); - openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Int64Grid>(grid), dense); - } - else if (grid->isType<openvdb::Vec3IGrid>()) { - openvdb::tools::Dense<openvdb::Vec3f, openvdb::tools::LayoutXYZ> dense( - bbox, (openvdb::Vec3f *)pixels); - openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Vec3IGrid>(grid), dense); - } - else if (grid->isType<openvdb::Vec3dGrid>()) { - openvdb::tools::Dense<openvdb::Vec3f, openvdb::tools::LayoutXYZ> dense( - bbox, (openvdb::Vec3f *)pixels); - openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Vec3dGrid>(grid), dense); - } - else if (grid->isType<openvdb::MaskGrid>()) { - openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, (float *)pixels); - openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::MaskGrid>(grid), dense); + if (nanogrid) { + memcpy(pixels, nanogrid.data(), nanogrid.size()); } + else # endif + { + if (grid->isType<openvdb::FloatGrid>()) { + openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, (float *)pixels); + openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::FloatGrid>(grid), dense); + } + else if (grid->isType<openvdb::Vec3fGrid>()) { + openvdb::tools::Dense<openvdb::Vec3f, openvdb::tools::LayoutXYZ> dense( + bbox, (openvdb::Vec3f *)pixels); + openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Vec3fGrid>(grid), dense); + } + else if (grid->isType<openvdb::BoolGrid>()) { + openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, (float *)pixels); + openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::BoolGrid>(grid), dense); + } + else if (grid->isType<openvdb::DoubleGrid>()) { + openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, (float *)pixels); + openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::DoubleGrid>(grid), dense); + } + else if (grid->isType<openvdb::Int32Grid>()) { + openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, (float *)pixels); + openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Int32Grid>(grid), dense); + } + else if (grid->isType<openvdb::Int64Grid>()) { + openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, (float *)pixels); + openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Int64Grid>(grid), dense); + } + else if (grid->isType<openvdb::Vec3IGrid>()) { + openvdb::tools::Dense<openvdb::Vec3f, openvdb::tools::LayoutXYZ> dense( + bbox, (openvdb::Vec3f *)pixels); + openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Vec3IGrid>(grid), dense); + } + else if (grid->isType<openvdb::Vec3dGrid>()) { + openvdb::tools::Dense<openvdb::Vec3f, openvdb::tools::LayoutXYZ> dense( + bbox, (openvdb::Vec3f *)pixels); + openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Vec3dGrid>(grid), dense); + } + else if (grid->isType<openvdb::MaskGrid>()) { + openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, (float *)pixels); + openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::MaskGrid>(grid), dense); + } + } return true; #else (void)pixels; diff --git a/intern/cycles/render/image_vdb.h b/intern/cycles/render/image_vdb.h index 71d10cc39f5..763196f2a15 100644 --- a/intern/cycles/render/image_vdb.h +++ b/intern/cycles/render/image_vdb.h @@ -33,7 +33,8 @@ class VDBImageLoader : public ImageLoader { VDBImageLoader(const string &grid_name); ~VDBImageLoader(); - virtual bool load_metadata(ImageMetaData &metadata) override; + virtual bool load_metadata(const ImageDeviceFeatures &features, + ImageMetaData &metadata) override; virtual bool load_pixels(const ImageMetaData &metadata, void *pixels, diff --git a/intern/cycles/render/light.cpp b/intern/cycles/render/light.cpp index 72450e2c546..5290d68e75a 100644 --- a/intern/cycles/render/light.cpp +++ b/intern/cycles/render/light.cpp @@ -129,6 +129,7 @@ NODE_DEFINE(Light) SOCKET_VECTOR(axisv, "Axis V", zero_float3()); SOCKET_FLOAT(sizev, "Size V", 1.0f); SOCKET_BOOLEAN(round, "Round", false); + SOCKET_FLOAT(spread, "Spread", M_PI_F); SOCKET_INT(map_resolution, "Map Resolution", 0); @@ -158,6 +159,7 @@ NODE_DEFINE(Light) Light::Light() : Node(get_node_type()) { + dereference_all_used_nodes(); } void Light::tag_update(Scene *scene) @@ -858,6 +860,15 @@ void LightManager::device_update_points(Device *, DeviceScene *dscene, Scene *sc float invarea = (area != 0.0f) ? 1.0f / area : 1.0f; float3 dir = light->dir; + /* Convert from spread angle 0..180 to 90..0, clamping to a minimum + * angle to avoid excessive noise. */ + const float min_spread_angle = 1.0f * M_PI_F / 180.0f; + const float spread_angle = 0.5f * (M_PI_F - max(light->spread, min_spread_angle)); + /* Normalization computed using: + * integrate cos(x) * (1 - tan(x) * tan(a)) * sin(x) from x = 0 to pi/2 - a. */ + const float tan_spread = tanf(spread_angle); + const float normalize_spread = 2.0f / (2.0f + (2.0f * spread_angle - M_PI_F) * tan_spread); + dir = safe_normalize(dir); if (light->use_mis && area != 0.0f) @@ -877,6 +888,8 @@ void LightManager::device_update_points(Device *, DeviceScene *dscene, Scene *sc klights[light_index].area.dir[0] = dir.x; klights[light_index].area.dir[1] = dir.y; klights[light_index].area.dir[2] = dir.z; + klights[light_index].area.tan_spread = tan_spread; + klights[light_index].area.normalize_spread = normalize_spread; } else if (light->light_type == LIGHT_SPOT) { shader_id &= ~SHADER_AREA_LIGHT; diff --git a/intern/cycles/render/light.h b/intern/cycles/render/light.h index 39014b5d667..fbd709125ff 100644 --- a/intern/cycles/render/light.h +++ b/intern/cycles/render/light.h @@ -58,6 +58,7 @@ class Light : public Node { NODE_SOCKET_API(float3, axisv) NODE_SOCKET_API(float, sizev) NODE_SOCKET_API(bool, round) + NODE_SOCKET_API(float, spread) NODE_SOCKET_API(Transform, tfm) diff --git a/intern/cycles/render/mesh.cpp b/intern/cycles/render/mesh.cpp index d5e5b960665..fd9879dd5dd 100644 --- a/intern/cycles/render/mesh.cpp +++ b/intern/cycles/render/mesh.cpp @@ -805,7 +805,7 @@ void Mesh::pack_patches(uint *patch_data, uint vert_offset, uint face_offset, ui } } -void Mesh::pack_primitives(ccl::PackedBVH *pack, int object, uint visibility, bool pack_all) +void Mesh::pack_primitives(ccl::PackedBVH *pack, int object, uint visibility, PackFlags pack_flags) { if (triangles.empty()) return; @@ -819,28 +819,38 @@ void Mesh::pack_primitives(ccl::PackedBVH *pack, int object, uint visibility, bo uint type = has_motion_blur() ? PRIMITIVE_MOTION_TRIANGLE : PRIMITIVE_TRIANGLE; - if (pack_all) { + /* Separate loop as other arrays are not initialized if their packing is not required. */ + if ((pack_flags & PackFlags::PACK_VISIBILITY) != 0) { + unsigned int *prim_visibility = &pack->prim_visibility[optix_prim_offset]; + for (size_t k = 0; k < num_prims; ++k) { + prim_visibility[k] = visibility; + } + } + + if ((pack_flags & PackFlags::PACK_GEOMETRY) != 0) { /* 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; + if ((pack_flags & PackFlags::PACK_GEOMETRY) != 0) { + prim_tri_index[k] = (prim_offset + k) * 3; + prim_type[k] = type; + prim_index[k] = prim_offset + k; + prim_object[k] = object; + } } } - for (size_t k = 0; k < num_prims; ++k) { - const Mesh::Triangle t = get_triangle(k); - 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]]); + if ((pack_flags & PackFlags::PACK_VERTICES) != 0) { + for (size_t k = 0; k < num_prims; ++k) { + const Mesh::Triangle t = get_triangle(k); + 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 2b0ff92ab62..e9e79f7f20d 100644 --- a/intern/cycles/render/mesh.h +++ b/intern/cycles/render/mesh.h @@ -232,7 +232,10 @@ 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, bool pack_all) override; + void pack_primitives(PackedBVH *pack, + int object, + uint visibility, + PackFlags pack_flags) override; void tessellate(DiagSplit *split); diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp index f3d420c6fcb..5792d2458d1 100644 --- a/intern/cycles/render/nodes.cpp +++ b/intern/cycles/render/nodes.cpp @@ -6093,6 +6093,7 @@ NODE_DEFINE(VectorMathNode) type_enum.insert("reflect", NODE_VECTOR_MATH_REFLECT); type_enum.insert("refract", NODE_VECTOR_MATH_REFRACT); type_enum.insert("faceforward", NODE_VECTOR_MATH_FACEFORWARD); + type_enum.insert("multiply_add", NODE_VECTOR_MATH_MULTIPLY_ADD); type_enum.insert("dot_product", NODE_VECTOR_MATH_DOT_PRODUCT); @@ -6165,7 +6166,8 @@ void VectorMathNode::compile(SVMCompiler &compiler) int vector_stack_offset = compiler.stack_assign_if_linked(vector_out); /* 3 Vector Operators */ - if (math_type == NODE_VECTOR_MATH_WRAP || math_type == NODE_VECTOR_MATH_FACEFORWARD) { + if (math_type == NODE_VECTOR_MATH_WRAP || math_type == NODE_VECTOR_MATH_FACEFORWARD || + math_type == NODE_VECTOR_MATH_MULTIPLY_ADD) { ShaderInput *vector3_in = input("Vector3"); int vector3_stack_offset = compiler.stack_assign(vector3_in); compiler.add_node( diff --git a/intern/cycles/render/nodes.h b/intern/cycles/render/nodes.h index fb9cf0c9836..99cb0b779b8 100644 --- a/intern/cycles/render/nodes.h +++ b/intern/cycles/render/nodes.h @@ -1600,11 +1600,23 @@ class SetNormalNode : public ShaderNode { NODE_SOCKET_API(float3, direction) }; -class OSLNode : public ShaderNode { +class OSLNode final : public ShaderNode { public: static OSLNode *create(ShaderGraph *graph, size_t num_inputs, const OSLNode *from = NULL); ~OSLNode(); + static void operator delete(void *ptr) + { + /* Override delete operator to silence new-delete-type-mismatch ASAN warnings + * regarding size mismatch in the destructor. This is intentional as we allocate + * extra space at the end of the node. */ + ::operator delete(ptr); + } + static void operator delete(void *, void *) + { + /* Deliberately empty placement delete operator, to avoid MSVC warning C4291. */ + } + ShaderNode *clone(ShaderGraph *graph) const; char *input_default_value(); diff --git a/intern/cycles/render/object.cpp b/intern/cycles/render/object.cpp index 52f63685aeb..f65f8bc6e90 100644 --- a/intern/cycles/render/object.cpp +++ b/intern/cycles/render/object.cpp @@ -153,10 +153,6 @@ 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()) { @@ -224,6 +220,10 @@ void Object::tag_update(Scene *scene) flag |= ObjectManager::TRANSFORM_MODIFIED; } + if (visibility_is_modified()) { + flag |= ObjectManager::VISIBILITY_MODIFIED; + } + foreach (Node *node, geometry->get_used_shaders()) { Shader *shader = static_cast<Shader *>(node); if (shader->get_use_mis() && shader->has_surface_emission) @@ -918,6 +918,10 @@ void ObjectManager::tag_update(Scene *scene, uint32_t flag) geometry_flag |= GeometryManager::TRANSFORM_MODIFIED; } + if ((flag & VISIBILITY_MODIFIED) != 0) { + geometry_flag |= GeometryManager::VISIBILITY_MODIFIED; + } + scene->geometry_manager->tag_update(scene, geometry_flag); } diff --git a/intern/cycles/render/object.h b/intern/cycles/render/object.h index 23682270fd1..e4bc7ac3d8e 100644 --- a/intern/cycles/render/object.h +++ b/intern/cycles/render/object.h @@ -134,6 +134,7 @@ class ObjectManager { OBJECT_MODIFIED = (1 << 5), HOLDOUT_MODIFIED = (1 << 6), TRANSFORM_MODIFIED = (1 << 7), + VISIBILITY_MODIFIED = (1 << 8), /* tag everything in the manager for an update */ UPDATE_ALL = ~0u, diff --git a/intern/cycles/render/osl.cpp b/intern/cycles/render/osl.cpp index 53c67049571..9bd6e3a5e2d 100644 --- a/intern/cycles/render/osl.cpp +++ b/intern/cycles/render/osl.cpp @@ -91,10 +91,10 @@ void OSLShaderManager::reset(Scene * /*scene*/) shading_system_init(); } -void OSLShaderManager::device_update(Device *device, - DeviceScene *dscene, - Scene *scene, - Progress &progress) +void OSLShaderManager::device_update_specific(Device *device, + DeviceScene *dscene, + Scene *scene, + Progress &progress) { if (!need_update()) return; @@ -1149,7 +1149,7 @@ void OSLCompiler::compile(OSLGlobals *og, Shader *shader) shader->has_integrator_dependency = false; /* generate surface shader */ - if (shader->used && graph && output->input("Surface")->link) { + if (shader->reference_count() && graph && output->input("Surface")->link) { shader->osl_surface_ref = compile_type(shader, shader->graph, SHADER_TYPE_SURFACE); if (has_bump) @@ -1165,7 +1165,7 @@ void OSLCompiler::compile(OSLGlobals *og, Shader *shader) } /* generate volume shader */ - if (shader->used && graph && output->input("Volume")->link) { + if (shader->reference_count() && graph && output->input("Volume")->link) { shader->osl_volume_ref = compile_type(shader, shader->graph, SHADER_TYPE_VOLUME); shader->has_volume = true; } @@ -1173,7 +1173,7 @@ void OSLCompiler::compile(OSLGlobals *og, Shader *shader) shader->osl_volume_ref = OSL::ShaderGroupRef(); /* generate displacement shader */ - if (shader->used && graph && output->input("Displacement")->link) { + if (shader->reference_count() && graph && output->input("Displacement")->link) { shader->osl_displacement_ref = compile_type(shader, shader->graph, SHADER_TYPE_DISPLACEMENT); shader->has_displacement = true; } diff --git a/intern/cycles/render/osl.h b/intern/cycles/render/osl.h index ea2d0ca492c..f6aa98d867a 100644 --- a/intern/cycles/render/osl.h +++ b/intern/cycles/render/osl.h @@ -72,15 +72,18 @@ class OSLShaderManager : public ShaderManager { static void free_memory(); - void reset(Scene *scene); + void reset(Scene *scene) override; - bool use_osl() + bool use_osl() override { return true; } - void device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress); - void device_free(Device *device, DeviceScene *dscene, Scene *scene); + void device_update_specific(Device *device, + DeviceScene *dscene, + Scene *scene, + Progress &progress) override; + void device_free(Device *device, DeviceScene *dscene, Scene *scene) override; /* osl compile and query */ static bool osl_compile(const string &inputfile, const string &outputfile); diff --git a/intern/cycles/render/scene.cpp b/intern/cycles/render/scene.cpp index d3dfe1c5be6..c4e7d2c79d6 100644 --- a/intern/cycles/render/scene.cpp +++ b/intern/cycles/render/scene.cpp @@ -143,21 +143,27 @@ void Scene::free_memory(bool final) delete bvh; bvh = NULL; - foreach (Shader *s, shaders) - delete s; - /* delete procedurals before other types as they may hold pointers to those types */ + /* The order of deletion is important to make sure data is freed based on possible dependencies + * as the Nodes' reference counts are decremented in the destructors: + * + * - Procedurals can create and hold pointers to any other types. + * - Objects can hold pointers to Geometries and ParticleSystems + * - Lights and Geometries can hold pointers to Shaders. + * + * Similarly, we first delete all nodes and their associated device data, and then the managers + * and their associated device data. + */ foreach (Procedural *p, procedurals) delete p; - foreach (Geometry *g, geometry) - delete g; foreach (Object *o, objects) delete o; - foreach (Light *l, lights) - delete l; + foreach (Geometry *g, geometry) + delete g; foreach (ParticleSystem *p, particle_systems) delete p; + foreach (Light *l, lights) + delete l; - shaders.clear(); geometry.clear(); objects.clear(); lights.clear(); @@ -169,7 +175,25 @@ void Scene::free_memory(bool final) film->device_free(device, &dscene, this); background->device_free(device, &dscene); integrator->device_free(device, &dscene, true); + } + if (final) { + delete camera; + delete dicing_camera; + delete film; + delete background; + delete integrator; + } + + /* Delete Shaders after every other nodes to ensure that we do not try to decrement the reference + * count on some dangling pointer. */ + foreach (Shader *s, shaders) + delete s; + + shaders.clear(); + + /* Now that all nodes have been deleted, we can safely delete managers and device data. */ + if (device) { object_manager->device_free(device, &dscene, true); geometry_manager->device_free(device, &dscene, true); shader_manager->device_free(device, &dscene, this); @@ -179,7 +203,7 @@ void Scene::free_memory(bool final) bake_manager->device_free(device, &dscene); - if (!params.persistent_data || final) + if (final) image_manager->device_free(device); else image_manager->device_free_builtin(device); @@ -189,11 +213,6 @@ void Scene::free_memory(bool final) if (final) { delete lookup_tables; - delete camera; - delete dicing_camera; - delete film; - delete background; - delete integrator; delete object_manager; delete geometry_manager; delete shader_manager; @@ -504,9 +523,6 @@ bool Scene::update(Progress &progress, bool &kernel_switch_needed) { /* update scene */ if (need_update()) { - /* Updated used shader tag so we know which features are need for the kernel. */ - shader_manager->update_shaders_used(this); - /* Update max_closures. */ KernelIntegrator *kintegrator = &dscene.data.integrator; if (params.background) { @@ -526,9 +542,6 @@ bool Scene::update(Progress &progress, bool &kernel_switch_needed) DeviceKernelStatus kernel_switch_status = device->get_active_kernel_switch_state(); kernel_switch_needed = kernel_switch_status == DEVICE_KERNEL_FEATURE_KERNEL_AVAILABLE || kernel_switch_status == DEVICE_KERNEL_FEATURE_KERNEL_INVALID; - if (kernel_switch_status == DEVICE_KERNEL_WAITING_FOR_FEATURE_KERNEL) { - progress.set_kernel_status("Compiling render kernels"); - } if (new_kernels_needed || kernel_switch_needed) { progress.set_kernel_status("Compiling render kernels"); device->wait_for_availability(loaded_kernel_features); @@ -566,9 +579,6 @@ bool Scene::load_kernels(Progress &progress, bool lock_scene) return false; } - progress.add_skip_time(timer, false); - VLOG(1) << "Total time spent loading kernels: " << time_dt() - timer.get_start(); - kernels_loaded = true; loaded_kernel_features = requested_features; return true; @@ -587,7 +597,7 @@ int Scene::get_max_closure_count() int max_closures = 0; for (int i = 0; i < shaders.size(); i++) { Shader *shader = shaders[i]; - if (shader->used) { + if (shader->reference_count()) { int num_closures = shader->graph->get_num_closures(); max_closures = max(max_closures, num_closures); } @@ -748,9 +758,10 @@ template<> void Scene::delete_node_impl(ParticleSystem *node) particle_system_manager->tag_update(this); } -template<> void Scene::delete_node_impl(Shader * /*node*/) +template<> void Scene::delete_node_impl(Shader *shader) { /* don't delete unused shaders, not supported */ + shader->clear_reference_count(); } template<> void Scene::delete_node_impl(Procedural *node) @@ -817,9 +828,12 @@ template<> void Scene::delete_nodes(const set<ParticleSystem *> &nodes, const No particle_system_manager->tag_update(this); } -template<> void Scene::delete_nodes(const set<Shader *> & /*nodes*/, const NodeOwner * /*owner*/) +template<> void Scene::delete_nodes(const set<Shader *> &nodes, const NodeOwner * /*owner*/) { /* don't delete unused shaders, not supported */ + for (Shader *shader : nodes) { + shader->clear_reference_count(); + } } template<> void Scene::delete_nodes(const set<Procedural *> &nodes, const NodeOwner *owner) diff --git a/intern/cycles/render/scene.h b/intern/cycles/render/scene.h index 61c753e411c..a6dab06f8f2 100644 --- a/intern/cycles/render/scene.h +++ b/intern/cycles/render/scene.h @@ -178,7 +178,6 @@ class SceneParams { int num_bvh_time_steps; int hair_subdivisions; CurveShapeType hair_shape; - bool persistent_data; int texture_limit; bool background; @@ -193,7 +192,6 @@ class SceneParams { num_bvh_time_steps = 0; hair_subdivisions = 3; hair_shape = CURVE_RIBBON; - persistent_data = false; texture_limit = 0; background = true; } @@ -206,7 +204,7 @@ class SceneParams { use_bvh_unaligned_nodes == params.use_bvh_unaligned_nodes && num_bvh_time_steps == params.num_bvh_time_steps && hair_subdivisions == params.hair_subdivisions && hair_shape == params.hair_shape && - persistent_data == params.persistent_data && texture_limit == params.texture_limit); + texture_limit == params.texture_limit); } int curve_subdivisions() diff --git a/intern/cycles/render/session.cpp b/intern/cycles/render/session.cpp index 3c601e18126..7830ca2293a 100644 --- a/intern/cycles/render/session.cpp +++ b/intern/cycles/render/session.cpp @@ -243,11 +243,6 @@ void Session::run_gpu() } } - /* Don't go in pause mode when image was rendered with preview kernels - * When feature kernels become available the session will be reset. */ - else if (no_tiles && kernel_state == DEVICE_KERNEL_WAITING_FOR_FEATURE_KERNEL) { - time_sleep(0.1); - } else if (no_tiles && kernel_state == DEVICE_KERNEL_FEATURE_KERNEL_AVAILABLE) { reset_gpu(tile_manager.params, params.samples); } @@ -762,11 +757,6 @@ void Session::run_cpu() } } - /* Don't go in pause mode when preview kernels are used - * When feature kernels become available the session will be reset. */ - else if (no_tiles && kernel_state == DEVICE_KERNEL_WAITING_FOR_FEATURE_KERNEL) { - time_sleep(0.1); - } else if (no_tiles && kernel_state == DEVICE_KERNEL_FEATURE_KERNEL_AVAILABLE) { reset_cpu(tile_manager.params, params.samples); } diff --git a/intern/cycles/render/shader.cpp b/intern/cycles/render/shader.cpp index 5ecbd92d96d..59b60904746 100644 --- a/intern/cycles/render/shader.cpp +++ b/intern/cycles/render/shader.cpp @@ -16,7 +16,6 @@ #include "device/device.h" -#include "render/alembic.h" #include "render/background.h" #include "render/camera.h" #include "render/colorspace.h" @@ -27,6 +26,7 @@ #include "render/nodes.h" #include "render/object.h" #include "render/osl.h" +#include "render/procedural.h" #include "render/scene.h" #include "render/shader.h" #include "render/svm.h" @@ -218,7 +218,6 @@ Shader::Shader() : Node(get_node_type()) displacement_method = DISPLACE_BUMP; id = -1; - used = false; need_update_uvs = true; need_update_attribute = true; @@ -382,8 +381,9 @@ void Shader::tag_used(Scene *scene) { /* if an unused shader suddenly gets used somewhere, it needs to be * recompiled because it was skipped for compilation before */ - if (!used) { + if (!reference_count()) { tag_modified(); + /* We do not reference here as the shader will be referenced when added to a socket. */ scene->shader_manager->tag_update(scene, ShaderManager::SHADER_MODIFIED); } } @@ -461,52 +461,28 @@ int ShaderManager::get_shader_id(Shader *shader, bool smooth) return id; } -void ShaderManager::update_shaders_used(Scene *scene) +void ShaderManager::device_update(Device *device, + DeviceScene *dscene, + Scene *scene, + Progress &progress) { if (!need_update()) { return; } - /* figure out which shaders are in use, so SVM/OSL can skip compiling them - * for speed and avoid loading image textures into memory */ uint id = 0; foreach (Shader *shader, scene->shaders) { - shader->used = false; shader->id = id++; } - scene->default_surface->used = true; - scene->default_light->used = true; - scene->default_background->used = true; - scene->default_empty->used = true; + /* Those shaders should always be compiled as they are used as fallback if a shader cannot be + * found, e.g. bad shader index for the triangle shaders on a Mesh. */ + assert(scene->default_surface->reference_count() != 0); + assert(scene->default_light->reference_count() != 0); + assert(scene->default_background->reference_count() != 0); + assert(scene->default_empty->reference_count() != 0); - 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); - shader->used = true; - } - - foreach (Light *light, scene->lights) - if (light->get_shader()) - const_cast<Shader *>(light->get_shader())->used = true; + device_update_specific(device, dscene, scene, progress); } void ShaderManager::device_update_common(Device *device, @@ -528,6 +504,8 @@ void ShaderManager::device_update_common(Device *device, if (shader->get_use_mis()) flag |= SD_USE_MIS; + if (shader->has_surface_emission) + flag |= SD_HAS_EMISSION; if (shader->has_surface_transparent && shader->get_use_transparent_shadow()) flag |= SD_HAS_TRANSPARENT_SHADOW; if (shader->has_volume) { @@ -637,6 +615,7 @@ void ShaderManager::add_default(Scene *scene) Shader *shader = scene->create_node<Shader>(); shader->name = "default_surface"; shader->set_graph(graph); + shader->reference(); scene->default_surface = shader; shader->tag_update(scene); } @@ -655,6 +634,8 @@ void ShaderManager::add_default(Scene *scene) shader->set_graph(graph); scene->default_volume = shader; shader->tag_update(scene); + /* No default reference for the volume to avoid compiling volume kernels if there are no actual + * volumes in the scene */ } /* default light */ @@ -671,6 +652,7 @@ void ShaderManager::add_default(Scene *scene) Shader *shader = scene->create_node<Shader>(); shader->name = "default_light"; shader->set_graph(graph); + shader->reference(); scene->default_light = shader; shader->tag_update(scene); } @@ -682,6 +664,7 @@ void ShaderManager::add_default(Scene *scene) Shader *shader = scene->create_node<Shader>(); shader->name = "default_background"; shader->set_graph(graph); + shader->reference(); scene->default_background = shader; shader->tag_update(scene); } @@ -693,6 +676,7 @@ void ShaderManager::add_default(Scene *scene) Shader *shader = scene->create_node<Shader>(); shader->name = "default_empty"; shader->set_graph(graph); + shader->reference(); scene->default_empty = shader; shader->tag_update(scene); } @@ -733,7 +717,7 @@ void ShaderManager::get_requested_features(Scene *scene, requested_features->nodes_features = 0; for (int i = 0; i < scene->shaders.size(); i++) { Shader *shader = scene->shaders[i]; - if (!shader->used) { + if (!shader->reference_count()) { continue; } diff --git a/intern/cycles/render/shader.h b/intern/cycles/render/shader.h index f47d64f346c..50c8bed4669 100644 --- a/intern/cycles/render/shader.h +++ b/intern/cycles/render/shader.h @@ -132,7 +132,6 @@ class Shader : public Node { /* determined before compiling */ uint id; - bool used; #ifdef WITH_OSL /* osl shading state references */ @@ -187,10 +186,11 @@ class ShaderManager { } /* device update */ - virtual void device_update(Device *device, - DeviceScene *dscene, - Scene *scene, - Progress &progress) = 0; + void device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress); + virtual void device_update_specific(Device *device, + DeviceScene *dscene, + Scene *scene, + Progress &progress) = 0; virtual void device_free(Device *device, DeviceScene *dscene, Scene *scene) = 0; void device_update_common(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress); @@ -208,7 +208,6 @@ class ShaderManager { static void add_default(Scene *scene); /* Selective nodes compilation. */ - void update_shaders_used(Scene *scene); void get_requested_features(Scene *scene, DeviceRequestedFeatures *requested_features); static void free_memory(); diff --git a/intern/cycles/render/svm.cpp b/intern/cycles/render/svm.cpp index fce604234f1..5c793c5c016 100644 --- a/intern/cycles/render/svm.cpp +++ b/intern/cycles/render/svm.cpp @@ -69,10 +69,10 @@ void SVMShaderManager::device_update_shader(Scene *scene, << summary.full_report(); } -void SVMShaderManager::device_update(Device *device, - DeviceScene *dscene, - Scene *scene, - Progress &progress) +void SVMShaderManager::device_update_specific(Device *device, + DeviceScene *dscene, + Scene *scene, + Progress &progress) { if (!need_update()) return; @@ -776,7 +776,7 @@ void SVMCompiler::compile_type(Shader *shader, ShaderGraph *graph, ShaderType ty add_node(NODE_ENTER_BUMP_EVAL, bump_state_offset); } - if (shader->used) { + if (shader->reference_count()) { CompilerState state(graph); if (clin->link) { bool generate = false; diff --git a/intern/cycles/render/svm.h b/intern/cycles/render/svm.h index a4ca68e1d8d..d23ff3e2a47 100644 --- a/intern/cycles/render/svm.h +++ b/intern/cycles/render/svm.h @@ -44,10 +44,13 @@ class SVMShaderManager : public ShaderManager { SVMShaderManager(); ~SVMShaderManager(); - void reset(Scene *scene); + void reset(Scene *scene) override; - void device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress); - void device_free(Device *device, DeviceScene *dscene, Scene *scene); + void device_update_specific(Device *device, + DeviceScene *dscene, + Scene *scene, + Progress &progress) override; + void device_free(Device *device, DeviceScene *dscene, Scene *scene) override; protected: void device_update_shader(Scene *scene, |