Add move semantics to set_timesamples and bool array dedup support

- Fix set_timesamples move overloads in prim-types.hh to use std::move()
- Add std::move() at call sites in ascii-parser.cc and scene-access.cc
- Add _dedup_bool_array cache in crate-reader.hh for bool array dedup
- Add add_dedup_bool_array_sample_pod() method in timesamples.hh
- Update UnpackTimeSampleValue_BOOL to use dedup cache for bool arrays

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Syoyo Fujita
2025-12-14 03:31:23 +09:00
parent 0ba4ffa43a
commit e4af68d251
6 changed files with 101 additions and 22 deletions

View File

@@ -4720,17 +4720,17 @@ bool AsciiParser::ParsePrimProps(std::map<std::string, Property> *props,
PUSH_ERROR_AND_RETURN(fmt::format("Variability mismatch. Attribute `{}` already has variability `{}`, but timeSampled value has variability `{}`.", attr_name, to_string(pattr->variability()), to_string(variability)));
}
pattr->get_var().set_timesamples(ts);
pattr->get_var().set_timesamples(std::move(ts));
// Set PropType to Attrib(since previously created Property may have EmptyAttrib).
props->at(attr_name).set_property_type(Property::Type::Attrib);
} else {
// new Attribute
pattr = &attr;
pattr = &attr;
primvar::PrimVar var;
var.set_timesamples(ts);
var.set_timesamples(std::move(ts));
if (array_qual) {
pattr->set_type_name(type_name + "[]");
} else {

View File

@@ -869,20 +869,47 @@ bool CrateReader::UnpackTimeSampleValue_BOOL(double t,
return true;
}
if (!ReadArray(&v_uint8)) {
PUSH_ERROR_AND_RETURN_TAG(kTag, "Failed to read bool array.");
}
// Check deduplication cache for bool array
auto it = _dedup_bool_array.find(rep);
if (it != _dedup_bool_array.end()) {
// Reuse cached array via ref_index
size_t ref_index = it->second;
DCOUT("Reusing cached BOOL array at sample index " << ref_index);
// Convert uint8_t array to bool array
std::vector<bool> v_bool;
v_bool.reserve(v_uint8.size());
for (uint8_t val : v_uint8) {
v_bool.push_back(val != 0);
}
if (dst.is_using_pod()) {
if (!dst.add_dedup_bool_array_sample_pod(t, ref_index, &_err)) {
PUSH_ERROR_AND_RETURN_TAG(kTag, "Failed to add dedup sample to TimeSamples.");
}
} else {
PUSH_ERROR_AND_RETURN_TAG(kTag, "Non-POD path not supported for bool dedup.");
}
} else {
// First occurrence - read and cache array
if (!ReadArray(&v_uint8)) {
PUSH_ERROR_AND_RETURN_TAG(kTag, "Failed to read bool array.");
}
if (!add_array_sample_to_timesamples<bool>(&dst, t, v_bool, &_err,
expected_total_samples)) {
PUSH_ERROR_AND_RETURN_TAG(kTag, "Failed to add sample to TimeSamples.");
// Convert uint8_t array to bool array
std::vector<bool> v_bool;
v_bool.reserve(v_uint8.size());
for (uint8_t val : v_uint8) {
v_bool.push_back(val != 0);
}
if (dst.is_using_pod()) {
// Store current index before adding
size_t current_index = dst.size();
_dedup_bool_array[rep] = current_index;
DCOUT("Caching BOOL array at sample index " << current_index);
if (!dst.add_array_sample_pod<bool>(t, v_bool, &_err, expected_total_samples)) {
PUSH_ERROR_AND_RETURN_TAG(kTag, "Failed to add sample to TimeSamples.");
}
} else {
if (!dst.add_sample(t, value::Value(v_bool), &_err)) {
PUSH_ERROR_AND_RETURN_TAG(kTag, "Failed to add sample to TimeSamples.");
}
}
}
} else {

View File

@@ -549,6 +549,7 @@ class CrateReader {
// Integer types (scalar and array)
std::unordered_map<crate::ValueRep, bool, crate::ValueRep::Hash> _dedup_bool;
std::unordered_map<crate::ValueRep, size_t, crate::ValueRep::Hash> _dedup_bool_array;
std::unordered_map<crate::ValueRep, int32_t, crate::ValueRep::Hash> _dedup_int32;
std::unordered_map<crate::ValueRep, uint32_t, crate::ValueRep::Hash> _dedup_uint32;
std::unordered_map<crate::ValueRep, int64_t, crate::ValueRep::Hash> _dedup_int64;

View File

@@ -1278,7 +1278,7 @@ struct Animatable {
}
void set_timesamples(TypedTimeSamples<T> &&ts) {
return set(ts);
return set(std::move(ts));
}
void clear_scalar() {
@@ -2959,7 +2959,7 @@ struct XformOp {
void set_timesamples(const value::TimeSamples &v) { _var.set_timesamples(v); }
void set_timesamples(value::TimeSamples &&v) { _var.set_timesamples(v); }
void set_timesamples(value::TimeSamples &&v) { _var.set_timesamples(std::move(v)); }
bool is_timesamples() const { return _var.is_timesamples(); }
bool has_timesamples() const { return _var.has_timesamples(); }

View File

@@ -1597,6 +1597,57 @@ struct TimeSamples {
return false; // Not using POD storage
}
/// Add a deduplicated bool array sample - reuses data from an existing sample
/// Special handling for bool since std::vector<bool> doesn't satisfy POD requirements
/// @param t Time value for this sample
/// @param ref_index Index of the existing sample whose data/offset to reuse
/// @param err Optional error string
bool add_dedup_bool_array_sample_pod(double t, size_t ref_index, std::string *err = nullptr) {
if (_use_pod) {
// Bool arrays are stored internally as uint8_t, but with bool type_id
// The dedup mechanism works the same way - just reference the existing sample
size_t new_idx = _pod_samples._times.size();
// Validate reference
if (ref_index >= new_idx) {
if (err) {
(*err) += "Invalid ref_index in add_dedup_bool_array_sample_pod: " +
std::to_string(ref_index) + " >= " + std::to_string(new_idx) + ".\n";
}
return false;
}
if (PODTimeSamples::is_dedup(_pod_samples._offsets[ref_index])) {
if (err) {
(*err) += "Cannot deduplicate from deduplicated sample.\n";
}
return false;
}
_pod_samples._times.push_back(t);
_pod_samples._blocked.push_back(0); // false = 0
// Copy array count from the referenced sample
size_t ref_array_count = (ref_index < _pod_samples._array_counts.size())
? _pod_samples._array_counts[ref_index]
: _pod_samples._array_size;
_pod_samples._array_counts.push_back(ref_array_count);
// Create dedup offset: bit 63=1 (dedup), bit 62=1 (array), bits 61-0=ref_index
uint64_t dedup_offset = PODTimeSamples::make_dedup_offset(ref_index, true);
_pod_samples._offsets.push_back(dedup_offset);
_pod_samples._dirty = true;
_dirty = true;
return true;
}
if (err) {
(*err) += "Not using POD storage for dedup bool array.\n";
}
return false;
}
/// Typed add blocked sample for POD types (optimization path)
template<typename T>
bool add_blocked_sample_pod(double t, std::string *err = nullptr, size_t expected_total_samples = 0) {

View File

@@ -726,7 +726,7 @@ bool ToProperty(const TypedAttribute<Animatable<T>> &input, Property &output, st
if (aval.value().has_timesamples()) {
value::TimeSamples ts = ToTypelessTimeSamples(aval.value().get_timesamples());
pvar.set_timesamples(ts);
pvar.set_timesamples(std::move(ts));
}
if (aval.value().has_value() || aval.value().has_timesamples()) {
@@ -802,7 +802,7 @@ bool ToProperty(const TypedAttributeWithFallback<Animatable<T>> &input,
if (v.is_timesamples()) {
value::TimeSamples ts = ToTypelessTimeSamples(v.get_timesamples());
pvar.set_timesamples(ts);
pvar.set_timesamples(std::move(ts));
} else if (v.is_scalar()) {
T a;
if (v.get_scalar(&a)) {
@@ -855,7 +855,7 @@ bool ToProperty(const TypedAttributeWithFallback<Animatable<T>> &input,
if (v.has_timesamples()) {
value::TimeSamples ts = ToTypelessTimeSamples(v.get_timesamples());
pvar.set_timesamples(ts);
pvar.set_timesamples(std::move(ts));
}
if (v.has_value()) {
@@ -934,7 +934,7 @@ bool ToTokenProperty(const TypedAttributeWithFallback<Animatable<T>> &input,
if (v.is_timesamples()) {
value::TimeSamples ts =
EnumTimeSamplesToTypelessTimeSamples(v.get_timesamples());
pvar.set_timesamples(ts);
pvar.set_timesamples(std::move(ts));
} else if (v.is_scalar()) {
T a;
if (v.get_scalar(&a)) {
@@ -983,7 +983,7 @@ bool ToTokenProperty(const TypedAttributeWithFallback<Animatable<T>> &input,
if (v.has_timesamples()) {
value::TimeSamples ts =
EnumTimeSamplesToTypelessTimeSamples(v.get_timesamples());
pvar.set_timesamples(ts);
pvar.set_timesamples(std::move(ts));
}
if (v.has_default()) {