[Bf-blender-cvs] [57d4b869be3] master: NLA: rewrite evaluation channel data structures.

Alexander Gavrilov noreply at git.blender.org
Sat Jan 5 08:52:48 CET 2019


Commit: 57d4b869be387767be51fe1f9d819904c342d692
Author: Alexander Gavrilov
Date:   Sat Dec 15 16:09:27 2018 +0300
Branches: master
https://developer.blender.org/rB57d4b869be387767be51fe1f9d819904c342d692

NLA: rewrite evaluation channel data structures.

Implementing a new intelligent mixing mode that combines quaternions
via multiplication requires rewriting the NLA code to recombine array
properties from separate scalar channels during evaluation.

In addition, stable evaluation of NLA stack requires that any channel
that is touched by any of the actions in the stack should always be
set to a definite value by evaluation, even if no strip affects it
at this point of the timeline. The obvious choice for the fallback
is the default value of the property.

To make scanning all actions reasonably efficient, mapping paths to
channels should be done using hash tables.

Differential Revision: https://developer.blender.org/D4120

===================================================================

M	source/blender/blenkernel/intern/anim_sys.c
M	source/blender/blenkernel/nla_private.h

===================================================================

diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c
index 579509608c3..1fe51b95ae4 100644
--- a/source/blender/blenkernel/intern/anim_sys.c
+++ b/source/blender/blenkernel/intern/anim_sys.c
@@ -2020,131 +2020,363 @@ NlaEvalStrip *nlastrips_ctime_get_strip(Depsgraph *depsgraph, ListBase *list, Li
 
 /* ---------------------- */
 
-/* find an NlaEvalChannel that matches the given criteria
- * - ptr and prop are the RNA data to find a match for
- */
-static NlaEvalChannel *nlaevalchan_find_match(ListBase *channels, const PathResolvedRNA *prna)
+/* Initialize a valid mask, allocating memory if necessary. */
+static void nlavalidmask_init(NlaValidMask *mask, int bits)
 {
-	NlaEvalChannel *nec;
+	if (BLI_BITMAP_SIZE(bits) > sizeof(mask->buffer)) {
+		mask->ptr = BLI_BITMAP_NEW(bits, "NlaValidMask");
+	}
+	else {
+		mask->ptr = mask->buffer;
+	}
+}
 
-	/* sanity check */
-	if (channels == NULL)
-		return NULL;
+/* Free allocated memory for the mask. */
+static void nlavalidmask_free(NlaValidMask *mask)
+{
+	if (mask->ptr != mask->buffer) {
+		MEM_freeN(mask->ptr);
+	}
+}
 
-	/* loop through existing channels, checking for a channel which affects the same property */
-	for (nec = channels->first; nec; nec = nec->next) {
-		/* - comparing the PointerRNA's is done by comparing the pointers
-		 *   to the actual struct the property resides in, since that all the
-		 *   other data stored in PointerRNA cannot allow us to definitively
-		 *   identify the data
-		 */
-		if ((nec->rna.ptr.data == prna->ptr.data) && (nec->rna.prop == prna->prop) && ELEM(nec->rna.prop_index, -1, prna->prop_index))
-			return nec;
+/* ---------------------- */
+
+/* Hashing functions for NlaEvalChannelKey. */
+static uint nlaevalchan_keyhash(const void *ptr)
+{
+	const NlaEvalChannelKey *key = ptr;
+	uint hash = BLI_ghashutil_ptrhash(key->ptr.data);
+	return hash ^ BLI_ghashutil_ptrhash(key->prop);
+}
+
+static bool nlaevalchan_keycmp(const void *a, const void *b)
+{
+	const NlaEvalChannelKey *A = a;
+	const NlaEvalChannelKey *B = b;
+
+	return (BLI_ghashutil_ptrcmp(A->ptr.data, B->ptr.data) ||
+	        BLI_ghashutil_ptrcmp(A->prop, B->prop));
+}
+
+/* ---------------------- */
+
+/* Allocate a new blending value snapshot for the channel. */
+static NlaEvalChannelSnapshot *nlaevalchan_snapshot_new(NlaEvalChannel *nec)
+{
+	int length = nec->base_snapshot.length;
+
+	size_t byte_size = sizeof(NlaEvalChannelSnapshot) + sizeof(float) * length;
+	NlaEvalChannelSnapshot *nec_snapshot = MEM_callocN(byte_size, "NlaEvalChannelSnapshot");
+
+	nec_snapshot->channel = nec;
+	nec_snapshot->length = length;
+
+	return nec_snapshot;
+}
+
+/* Free a channel's blending value snapshot. */
+static void nlaevalchan_snapshot_free(NlaEvalChannelSnapshot *nec_snapshot)
+{
+	BLI_assert(!nec_snapshot->is_base);
+
+	MEM_freeN(nec_snapshot);
+}
+
+/* Copy all data in the snapshot. */
+static void nlaevalchan_snapshot_copy(NlaEvalChannelSnapshot *dst, const NlaEvalChannelSnapshot *src)
+{
+	BLI_assert(dst->channel == src->channel);
+
+	memcpy(dst->values, src->values, sizeof(float) * dst->length);
+}
+
+/* ---------------------- */
+
+/* Initialize a blending state snapshot structure. */
+static void nlaeval_snapshot_init(NlaEvalSnapshot *snapshot, NlaEvalData *nlaeval, NlaEvalSnapshot *base)
+{
+	snapshot->base = base;
+	snapshot->size = MAX2(16, nlaeval->num_channels);
+	snapshot->channels = MEM_callocN(sizeof(*snapshot->channels) * snapshot->size, "NlaEvalSnapshot::channels");
+}
+
+/* Retrieve the individual channel snapshot. */
+static NlaEvalChannelSnapshot *nlaeval_snapshot_get(NlaEvalSnapshot *snapshot, int index)
+{
+	return (index < snapshot->size) ? snapshot->channels[index] : NULL;
+}
+
+/* Ensure at least this number of slots exists. */
+static void nlaeval_snapshot_ensure_size(NlaEvalSnapshot *snapshot, int size)
+{
+	if (size > snapshot->size) {
+		snapshot->size *= 2;
+		CLAMP_MIN(snapshot->size, size);
+		CLAMP_MIN(snapshot->size, 16);
+
+		size_t byte_size = sizeof(*snapshot->channels) * snapshot->size;
+		snapshot->channels = MEM_recallocN_id(snapshot->channels, byte_size, "NlaEvalSnapshot::channels");
 	}
+}
 
-	/* not found */
-	return NULL;
+/* Retrieve the address of a slot in the blending state snapshot for this channel (may realloc). */
+static NlaEvalChannelSnapshot **nlaeval_snapshot_ensure_slot(NlaEvalSnapshot *snapshot, NlaEvalChannel *nec)
+{
+	nlaeval_snapshot_ensure_size(snapshot, nec->owner->num_channels);
+	return &snapshot->channels[nec->index];
 }
 
-/* initialise default value for NlaEvalChannel, so that it doesn't blend things wrong */
-static float nlaevalchan_init_value(PathResolvedRNA *rna)
+/* Retrieve the blending snapshot for the specified channel, with fallback to base. */
+static NlaEvalChannelSnapshot *nlaeval_snapshot_find_channel(NlaEvalSnapshot *snapshot, NlaEvalChannel *nec)
 {
-	PointerRNA *ptr = &rna->ptr;
-	PropertyRNA *prop = rna->prop;
-	int index = rna->prop_index;
+	while (snapshot != NULL) {
+		NlaEvalChannelSnapshot *nec_snapshot = nlaeval_snapshot_get(snapshot, nec->index);
+		if (nec_snapshot != NULL) {
+			return nec_snapshot;
+		}
+		snapshot = snapshot->base;
+	}
+
+	return &nec->base_snapshot;
+}
+
+/* Retrieve or create the channel value snapshot, copying from the other snapshot (or default values) */
+static NlaEvalChannelSnapshot *nlaeval_snapshot_ensure_channel(NlaEvalSnapshot *snapshot, NlaEvalChannel *nec)
+{
+	NlaEvalChannelSnapshot **slot = nlaeval_snapshot_ensure_slot(snapshot, nec);
+
+	if (*slot == NULL) {
+		NlaEvalChannelSnapshot *base_snapshot, *nec_snapshot;
+
+		nec_snapshot = nlaevalchan_snapshot_new(nec);
+		base_snapshot = nlaeval_snapshot_find_channel(snapshot->base, nec);
+
+		nlaevalchan_snapshot_copy(nec_snapshot, base_snapshot);
+
+		*slot = nec_snapshot;
+	}
+
+	return *slot;
+}
+
+/* Free all memory owned by this blending snapshot structure. */
+static void nlaeval_snapshot_free_data(NlaEvalSnapshot *snapshot)
+{
+	if (snapshot->channels != NULL) {
+		for (int i = 0; i < snapshot->size; i++) {
+			NlaEvalChannelSnapshot *nec_snapshot = snapshot->channels[i];
+			if (nec_snapshot != NULL) {
+				nlaevalchan_snapshot_free(nec_snapshot);
+			}
+		}
+
+		MEM_freeN(snapshot->channels);
+	}
+
+	snapshot->base = NULL;
+	snapshot->size = 0;
+	snapshot->channels = NULL;
+}
+
+/* ---------------------- */
+
+/* Free memory owned by this evaluation channel. */
+static void nlaevalchan_free_data(NlaEvalChannel *nec)
+{
+	nlavalidmask_free(&nec->valid);
+}
+
+/* Initialize a full NLA evaluation state structure. */
+static void nlaeval_init(NlaEvalData *nlaeval)
+{
+	memset(nlaeval, 0, sizeof(*nlaeval));
+
+	nlaeval->path_hash = BLI_ghash_str_new("NlaEvalData::path_hash");
+	nlaeval->key_hash = BLI_ghash_new(nlaevalchan_keyhash, nlaevalchan_keycmp, "NlaEvalData::key_hash");
+}
+
+static void nlaeval_free(NlaEvalData *nlaeval)
+{
+	/* Delete base snapshot - its channels are part of NlaEvalChannel and shouldn't be freed. */
+	MEM_SAFE_FREE(nlaeval->base_snapshot.channels);
+
+	/* Delete result snapshot. */
+	nlaeval_snapshot_free_data(&nlaeval->eval_snapshot);
+
+	/* Delete channels. */
+	for (NlaEvalChannel *nec = nlaeval->channels.first; nec; nec = nec->next) {
+		nlaevalchan_free_data(nec);
+	}
+
+	BLI_freelistN(&nlaeval->channels);
+	BLI_ghash_free(nlaeval->path_hash, NULL, NULL);
+	BLI_ghash_free(nlaeval->key_hash, NULL, NULL);
+}
+
+/* ---------------------- */
+
+static int nlaevalchan_validate_index(NlaEvalChannel *nec, int index)
+{
+	if (nec->is_array) {
+		if (index >= 0 && index < nec->base_snapshot.length) {
+			return index;
+		}
+
+		return -1;
+	}
+	else {
+		return 0;
+	}
+}
+
+/* Initialise default values for NlaEvalChannel from the property data. */
+static void nlaevalchan_get_default_values(NlaEvalChannel *nec, float *r_values)
+{
+	PointerRNA *ptr = &nec->key.ptr;
+	PropertyRNA *prop = nec->key.prop;
+	int length = nec->base_snapshot.length;
 
 	/* NOTE: while this doesn't work for all RNA properties as default values aren't in fact
 	 * set properly for most of them, at least the common ones (which also happen to get used
 	 * in NLA strips a lot, e.g. scale) are set correctly.
 	 */
-	switch (RNA_property_type(prop)) {
-		case PROP_BOOLEAN:
-			if (RNA_property_array_check(prop))
-				return (float)RNA_property_boolean_get_default_index(ptr, prop, index);
-			else
-				return (float)RNA_property_boolean_get_default(ptr, prop);
-		case PROP_INT:
-			if (RNA_property_array_check(prop))
-				return (float)RNA_property_int_get_default_index(ptr, prop, index);
-			else
-				return (float)RNA_property_int_get_default(ptr, prop);
-		case PROP_FLOAT:
-			if (RNA_property_array_check(prop))
-				return RNA_property_float_get_default_index(ptr, prop, index);
-			else
-				return RNA_property_float_get_default(ptr, prop);
-		case PROP_ENUM:
-			return (float)RNA_property_enum_get_default(ptr, prop);
-		default:
-			return 0.0f;
+	if (RNA_property_array_check(prop)) {
+		BLI_assert(length == RNA_property_array_length(ptr, prop));
+		bool *tmp_bool;
+		int *tmp_int;
+
+		switch (RNA_property_type(prop)) {
+			case PROP_BOOLEAN:
+				tmp_bool = MEM_malloc_arrayN(sizeof(*tmp_bool), length, __func__);
+				RNA_property_boolean_get_default_array(ptr, prop, tmp_bool);
+				for (int i = 0; i < length; i++) {
+					r_values[i] = (float)tmp_bool[i];
+				}
+				MEM_freeN(tmp_bool);
+				break;
+			case PROP_INT:
+				tmp_int = MEM_malloc_arrayN(sizeof(*tmp_int), length, __func__);
+				RNA_property_int_get_default_array(ptr, prop, tmp_int);
+				for (int i = 0; i < length; i++) {
+					r_values[i] = (float)tmp_int[i];
+				}
+				MEM_freeN(tmp_int);
+				break;
+			case PROP_FLOAT:
+				RNA_property_float_get_default_array(ptr, prop, r_values);
+				break;
+			default:
+				memset(r_values, 0, sizeof(float) * length);
+		}
+	}
+	else {
+		BLI_assert(length == 1);
+
+		switch (RNA_property_type(prop)) {
+			case PROP_BOOLEAN:
+				*r_values = (float)RNA_property_boolean_get_default(ptr, prop);
+				break;
+			case PROP_INT:
+				*r_values = (float)RNA_property_int_get_default(ptr, prop);
+				break;
+			case PROP_FLOAT:
+				*r_values = RNA_property_float_get_default(ptr, prop

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list