Alembic procedural: specific result type for cache lookups

This type, CacheLookupResult, holds the data for the current time, or an
explanation as to why no data is available (already loaded, or simply
nothing available). This is useful to document the behavior of the code
but also, in future changes, to respond appropriately for missing data.
This commit is contained in:
Kévin Dietrich 2021-03-12 01:57:41 +01:00
parent d72fc36ec5
commit 8922d177c1
2 changed files with 100 additions and 21 deletions

@ -408,8 +408,10 @@ static void add_uvs(AlembicProcedural *proc,
continue;
}
const array<int3> *triangles = cached_data.triangles.data_for_time_no_check(time);
const array<int3> *triangles_loops = cached_data.triangles_loops.data_for_time_no_check(time);
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;
@ -456,7 +458,8 @@ static void add_normals(const Int32ArraySamplePtr face_indices,
*normals.getTimeSampling());
attr.std = ATTR_STD_VERTEX_NORMAL;
const array<float3> *vertices = cached_data.vertices.data_for_time_no_check(time);
const array<float3> *vertices =
cached_data.vertices.data_for_time_no_check(time).get_data_or_null();
if (!vertices) {
return;
@ -491,7 +494,8 @@ static void add_normals(const Int32ArraySamplePtr face_indices,
*normals.getTimeSampling());
attr.std = ATTR_STD_VERTEX_NORMAL;
const array<float3> *vertices = cached_data.vertices.data_for_time_no_check(time);
const array<float3> *vertices =
cached_data.vertices.data_for_time_no_check(time).get_data_or_null();
if (!vertices) {
return;
@ -1109,9 +1113,10 @@ void AlembicObject::read_attribute(const ICompoundProperty &arb_geom_params,
attribute.element = ATTR_ELEMENT_CORNER;
attribute.type_desc = TypeFloat2;
const array<int3> *triangles = cached_data.triangles.data_for_time_no_check(time);
const array<int3> *triangles_loops = cached_data.triangles_loops.data_for_time_no_check(
time);
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;
@ -1164,7 +1169,8 @@ void AlembicObject::read_attribute(const ICompoundProperty &arb_geom_params,
attribute.element = ATTR_ELEMENT_CORNER_BYTE;
attribute.type_desc = TypeRGBA;
const array<int3> *triangles = cached_data.triangles.data_for_time_no_check(time);
const array<int3> *triangles =
cached_data.triangles.data_for_time_no_check(time).get_data_or_null();
if (!triangles) {
return;
@ -1220,7 +1226,8 @@ void AlembicObject::read_attribute(const ICompoundProperty &arb_geom_params,
attribute.element = ATTR_ELEMENT_CORNER_BYTE;
attribute.type_desc = TypeRGBA;
const array<int3> *triangles = cached_data.triangles.data_for_time_no_check(time);
const array<int3> *triangles =
cached_data.triangles.data_for_time_no_check(time).get_data_or_null();
if (!triangles) {
return;
@ -1259,7 +1266,7 @@ 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);
const array<char> *attr_data = attribute.data.data_for_time(frame_time).get_data_or_null();
Attribute *attr = nullptr;
if (attribute.std != ATTR_STD_NONE) {
@ -1558,7 +1565,7 @@ void AlembicProcedural::read_mesh(AlembicObject *abc_object, Abc::chrono_t frame
cached_data.shader.copy_to_socket(frame_time, mesh, mesh->get_shader_socket());
array<int3> *triangle_data = cached_data.triangles.data_for_time(frame_time);
array<int3> *triangle_data = cached_data.triangles.data_for_time(frame_time).get_data_or_null();
if (triangle_data) {
array<int> triangles;
array<bool> smooth;

@ -50,6 +50,78 @@ template<typename> struct is_array : public std::false_type {
template<typename T> struct is_array<array<T>> : public std::true_type {
};
/* Holds the data for a cache lookup at a given time, as well as informations to
* help disambiguate successes or failures to get data from the cache. */
template<typename T> class CacheLookupResult {
enum class State {
NEW_DATA,
ALREADY_LOADED,
NO_DATA_FOR_TIME,
};
T *data;
State state;
protected:
/* Prevent default construction outside of the class: for a valid result, we
* should use the static functions below. */
CacheLookupResult() = default;
public:
static CacheLookupResult new_data(T *data_)
{
CacheLookupResult result;
result.data = data_;
result.state = State::NEW_DATA;
return result;
}
static CacheLookupResult no_data_found_for_time()
{
CacheLookupResult result;
result.data = nullptr;
result.state = State::NO_DATA_FOR_TIME;
return result;
}
static CacheLookupResult already_loaded()
{
CacheLookupResult result;
result.data = nullptr;
result.state = State::ALREADY_LOADED;
return result;
}
/* This should only be call if new data is available. */
const T &get_data() const
{
assert(state == State::NEW_DATA);
assert(data != nullptr);
return *data;
}
T *get_data_or_null() const
{
// data_ should already be null if there is no new data so no need to check
return data;
}
bool has_new_data() const
{
return state == State::NEW_DATA;
}
bool has_already_loaded() const
{
return state == State::ALREADY_LOADED;
}
bool has_no_data_for_time() const
{
return state == State::NO_DATA_FOR_TIME;
}
};
/* Store the data set for an animation at every time points, or at the beginning of the animation
* for constant data.
*
@ -79,10 +151,10 @@ template<typename T> class DataStore {
/* Get the data for the specified time.
* Return nullptr if there is no data or if the data for this time was already loaded. */
T *data_for_time(double time)
CacheLookupResult<T> data_for_time(double time)
{
if (size() == 0) {
return nullptr;
return CacheLookupResult<T>::no_data_found_for_time();
}
std::pair<size_t, Alembic::Abc::chrono_t> index_pair;
@ -90,26 +162,26 @@ template<typename T> class DataStore {
DataTimePair &data_pair = data[index_pair.first];
if (last_loaded_time == data_pair.time) {
return nullptr;
return CacheLookupResult<T>::already_loaded();
}
last_loaded_time = data_pair.time;
return &data_pair.data;
return CacheLookupResult<T>::new_data(&data_pair.data);
}
/* get the data for the specified time, but do not check if the data was already loaded for this
* time return nullptr if there is no data */
T *data_for_time_no_check(double time)
CacheLookupResult<T> data_for_time_no_check(double time)
{
if (size() == 0) {
return nullptr;
return CacheLookupResult<T>::no_data_found_for_time();
}
std::pair<size_t, Alembic::Abc::chrono_t> index_pair;
index_pair = time_sampling.getNearIndex(time, data.size());
DataTimePair &data_pair = data[index_pair.first];
return &data_pair.data;
return CacheLookupResult<T>::new_data(&data_pair.data);
}
void add_data(T &data_, double time)
@ -149,15 +221,15 @@ template<typename T> class DataStore {
* data for this time or it was already loaded, do nothing. */
void copy_to_socket(double time, Node *node, const SocketType *socket)
{
T *data_ = data_for_time(time);
CacheLookupResult<T> result = data_for_time(time);
if (data_ == nullptr) {
if (!result.has_new_data()) {
return;
}
/* TODO(kevindietrich): arrays are emptied when passed to the sockets, so for now we copy the
* arrays to avoid reloading the data */
T value = *data_;
T value = result.get_data();
node->set(*socket, value);
}
};