private void RegenerateUpdateChannels(List<AnimationBlender.Channel> channels) { var updateMemberInfos = new List<UpdateMemberInfo>(); foreach (var channel in channels) { updateMemberInfos.Add(new UpdateMemberInfo { Name = channel.PropertyName, DataOffset = channel.Offset }); } compiledUpdate = UpdateEngine.Compile(typeof(Entity), updateMemberInfos); }
/// <summary> /// Updates the specified <see cref="target"/> object with new data. /// </summary> /// <param name="target">The object to update.</param> /// <param name="compiledUpdate">The precompiled list of update operations, generated by <see cref="Compile"/>.</param> /// <param name="updateData">The data source for blittable struct.</param> /// <param name="updateObjects">The data source for objects and non-blittable struct</param> public static void Run(object target, CompiledUpdate compiledUpdate, IntPtr updateData, UpdateObjectData[] updateObjects) { var operations = compiledUpdate.UpdateOperations; var temporaryObjects = compiledUpdate.TemporaryObjects; var stack = new Stack <UpdateStackEntry>(); // Current object being processed object currentObj = target; object nextObject; // This object needs to be pinned since we will have a pointer to its memory // Note that the stack don't need to have each of its object pinned since we store entries as object + offset Interop.Pin(currentObj); // pinned test (this will need to be on a stack somehow) IntPtr currentPtr = UpdateEngineHelper.ObjectToPtr(currentObj); var operationCount = operations.Length; if (operationCount == 0) { return; } var operation = Interop.Pin(ref operations[0]); for (int index = 0; index < operationCount; index++) { // Adjust offset currentPtr += operation.AdjustOffset; switch (operation.Type) { case UpdateOperationType.EnterObjectProperty: { nextObject = ((UpdatableProperty)operation.Member).GetObject(currentPtr); if (nextObject == null && operation.SkipCountIfNull != -1) { index += operation.SkipCountIfNull; operation = Interop.AddPinned(operation, operation.SkipCountIfNull); break; } // Compute offset and push to stack stack.Push(new UpdateStackEntry( currentObj, (int)((byte *)currentPtr - (byte *)UpdateEngineHelper.ObjectToPtr(currentObj)) )); // Get object currentObj = nextObject; currentPtr = UpdateEngineHelper.ObjectToPtr(currentObj); break; } case UpdateOperationType.EnterStructPropertyBase: { // Compute offset and push to stack stack.Push(new UpdateStackEntry( currentObj, (int)((byte *)currentPtr - (byte *)UpdateEngineHelper.ObjectToPtr(currentObj)) )); currentObj = temporaryObjects[operation.DataOffset]; currentPtr = ((UpdatablePropertyBase)operation.Member).GetStructAndUnbox(currentPtr, currentObj); break; } case UpdateOperationType.EnterObjectField: { nextObject = ((UpdatableField)operation.Member).GetObject(currentPtr); if (nextObject == null && operation.SkipCountIfNull != -1) { index += operation.SkipCountIfNull; operation = Interop.AddPinned(operation, operation.SkipCountIfNull); break; } // Compute offset and push to stack stack.Push(new UpdateStackEntry( currentObj, (int)((byte *)currentPtr - (byte *)UpdateEngineHelper.ObjectToPtr(currentObj)) )); // Get object currentObj = nextObject; currentPtr = UpdateEngineHelper.ObjectToPtr(currentObj); break; } case UpdateOperationType.EnterObjectCustom: { nextObject = ((UpdatableCustomAccessor)operation.Member).GetObject(currentPtr); if (nextObject == null && operation.SkipCountIfNull != -1) { index += operation.SkipCountIfNull; operation = Interop.AddPinned(operation, operation.SkipCountIfNull); break; } // Compute offset and push to stack stack.Push(new UpdateStackEntry( currentObj, (int)((byte *)currentPtr - (byte *)UpdateEngineHelper.ObjectToPtr(currentObj)) )); // Get object currentObj = nextObject; currentPtr = UpdateEngineHelper.ObjectToPtr(currentObj); break; } case UpdateOperationType.LeaveAndCopyStructPropertyBase: { // Save back struct pointer var oldPtr = currentPtr; // Restore currentObj and currentPtr from stack var stackEntry = stack.Pop(); currentObj = stackEntry.Object; currentPtr = UpdateEngineHelper.ObjectToPtr(currentObj) + stackEntry.Offset; // Use setter to set back struct ((UpdatablePropertyBase)operation.Member).SetBlittable(currentPtr, oldPtr); break; } case UpdateOperationType.Leave: { // Restore currentObj and currentPtr from stack var stackEntry = stack.Pop(); currentObj = stackEntry.Object; currentPtr = UpdateEngineHelper.ObjectToPtr(currentObj) + stackEntry.Offset; break; } case UpdateOperationType.ConditionalSetObjectProperty: { var updateObject = updateObjects[operation.DataOffset]; if (updateObject.Condition != 0) // 0 is 0.0f in float { ((UpdatableProperty)operation.Member).SetObject(currentPtr, updateObject.Value); } break; } case UpdateOperationType.ConditionalSetBlittablePropertyBase: { // TODO: This case can happen quite often (i.e. a float property) and require an extra indirection // We could probably avoid it by having common types as non virtual methods (i.e. object, int, float, maybe even Vector3/4?) var data = (int *)((byte *)updateData + operation.DataOffset); if (*data++ != 0) // 0 is 0.0f in float { ((UpdatablePropertyBase)operation.Member).SetBlittable(currentPtr, (IntPtr)data); } break; } case UpdateOperationType.ConditionalSetStructPropertyBase: { // TODO: This case can happen quite often (i.e. a float property) and require an extra indirection // We could probably avoid it by having common types as non virtual methods (i.e. object, int, float, maybe even Vector3/4?) var updateObject = updateObjects[operation.DataOffset]; if (updateObject.Condition != 0) // 0 is 0.0f in float { ((UpdatablePropertyBase)operation.Member).SetStruct(currentPtr, updateObject.Value); } break; } case UpdateOperationType.ConditionalSetObjectField: { var updateObject = updateObjects[operation.DataOffset]; if (updateObject.Condition != 0) // 0 is 0.0f in float { ((UpdatableField)operation.Member).SetObject(currentPtr, updateObject.Value); } break; } case UpdateOperationType.ConditionalSetBlittableField: { var data = (int *)((byte *)updateData + operation.DataOffset); if (*data++ != 0) // 0 is 0.0f in float { ((UpdatableField)operation.Member).SetBlittable(currentPtr, (IntPtr)data); } break; } case UpdateOperationType.ConditionalSetBlittableField4: { var data = (int *)((byte *)updateData + operation.DataOffset); if (*data++ != 0) // 0 is 0.0f in float { *(int *)currentPtr = *data; } break; } case UpdateOperationType.ConditionalSetBlittableField8: { var data = (int *)((byte *)updateData + operation.DataOffset); if (*data++ != 0) // 0 is 0.0f in float { *(Blittable8 *)currentPtr = *(Blittable8 *)data; } break; } case UpdateOperationType.ConditionalSetBlittableField12: { var data = (int *)((byte *)updateData + operation.DataOffset); if (*data++ != 0) // 0 is 0.0f in float { *(Blittable12 *)currentPtr = *(Blittable12 *)data; } break; } case UpdateOperationType.ConditionalSetBlittableField16: { var data = (int *)((byte *)updateData + operation.DataOffset); if (*data++ != 0) // 0 is 0.0f in float { *(Blittable16 *)currentPtr = *(Blittable16 *)data; } break; } case UpdateOperationType.ConditionalSetStructField: { // Use setter to set back struct var updateObject = updateObjects[operation.DataOffset]; if (updateObject.Condition != 0) // 0 is 0.0f in float { ((UpdatableField)operation.Member).SetStruct(currentPtr, updateObject.Value); } break; } case UpdateOperationType.ConditionalSetObjectCustom: { var updateObject = updateObjects[operation.DataOffset]; if (updateObject.Condition != 0) // 0 is 0.0f in float { ((UpdatableCustomAccessor)operation.Member).SetObject(currentPtr, updateObject.Value); } break; } default: throw new ArgumentOutOfRangeException(); } operation = Interop.IncrementPinned(operation); } }
/// <summary> /// Updates the specified <see cref="target"/> object with new data. /// </summary> /// <param name="target">The object to update.</param> /// <param name="compiledUpdate">The precompiled list of update operations, generated by <see cref="Compile"/>.</param> /// <param name="updateData">The data source for blittable struct.</param> /// <param name="updateObjects">The data source for objects and non-blittable struct</param> public static void Run(object target, CompiledUpdate compiledUpdate, IntPtr updateData, UpdateObjectData[] updateObjects) { var operations = compiledUpdate.UpdateOperations; var temporaryObjects = compiledUpdate.TemporaryObjects; var stack = new Stack<UpdateStackEntry>(); // Current object being processed object currentObj = target; object nextObject; // This object needs to be pinned since we will have a pointer to its memory // Note that the stack don't need to have each of its object pinned since we store entries as object + offset Interop.Pin(currentObj); // pinned test (this will need to be on a stack somehow) IntPtr currentPtr = UpdateEngineHelper.ObjectToPtr(currentObj); var operationCount = operations.Length; if (operationCount == 0) return; var operation = Interop.Pin(ref operations[0]); for (int index = 0; index < operationCount; index++) { // Adjust offset currentPtr += operation.AdjustOffset; switch (operation.Type) { case UpdateOperationType.EnterObjectProperty: { nextObject = ((UpdatableProperty)operation.Member).GetObject(currentPtr); if (nextObject == null && operation.SkipCountIfNull != -1) { index += operation.SkipCountIfNull; operation = Interop.AddPinned(operation, operation.SkipCountIfNull); break; } // Compute offset and push to stack stack.Push(new UpdateStackEntry( currentObj, (int) ((byte*) currentPtr - (byte*) UpdateEngineHelper.ObjectToPtr(currentObj)) )); // Get object currentObj = nextObject; currentPtr = UpdateEngineHelper.ObjectToPtr(currentObj); break; } case UpdateOperationType.EnterStructPropertyBase: { // Compute offset and push to stack stack.Push(new UpdateStackEntry( currentObj, (int) ((byte*) currentPtr - (byte*) UpdateEngineHelper.ObjectToPtr(currentObj)) )); currentObj = temporaryObjects[operation.DataOffset]; currentPtr = ((UpdatablePropertyBase)operation.Member).GetStructAndUnbox(currentPtr, currentObj); break; } case UpdateOperationType.EnterObjectField: { nextObject = ((UpdatableField)operation.Member).GetObject(currentPtr); if (nextObject == null && operation.SkipCountIfNull != -1) { index += operation.SkipCountIfNull; operation = Interop.AddPinned(operation, operation.SkipCountIfNull); break; } // Compute offset and push to stack stack.Push(new UpdateStackEntry( currentObj, (int) ((byte*) currentPtr - (byte*) UpdateEngineHelper.ObjectToPtr(currentObj)) )); // Get object currentObj = nextObject; currentPtr = UpdateEngineHelper.ObjectToPtr(currentObj); break; } case UpdateOperationType.EnterObjectCustom: { nextObject = ((UpdatableCustomAccessor)operation.Member).GetObject(currentPtr); if (nextObject == null && operation.SkipCountIfNull != -1) { index += operation.SkipCountIfNull; operation = Interop.AddPinned(operation, operation.SkipCountIfNull); break; } // Compute offset and push to stack stack.Push(new UpdateStackEntry( currentObj, (int)((byte*)currentPtr - (byte*)UpdateEngineHelper.ObjectToPtr(currentObj)) )); // Get object currentObj = nextObject; currentPtr = UpdateEngineHelper.ObjectToPtr(currentObj); break; } case UpdateOperationType.LeaveAndCopyStructPropertyBase: { // Save back struct pointer var oldPtr = currentPtr; // Restore currentObj and currentPtr from stack var stackEntry = stack.Pop(); currentObj = stackEntry.Object; currentPtr = UpdateEngineHelper.ObjectToPtr(currentObj) + stackEntry.Offset; // Use setter to set back struct ((UpdatablePropertyBase)operation.Member).SetBlittable(currentPtr, oldPtr); break; } case UpdateOperationType.Leave: { // Restore currentObj and currentPtr from stack var stackEntry = stack.Pop(); currentObj = stackEntry.Object; currentPtr = UpdateEngineHelper.ObjectToPtr(currentObj) + stackEntry.Offset; break; } case UpdateOperationType.ConditionalSetObjectProperty: { var updateObject = updateObjects[operation.DataOffset]; if (updateObject.Condition != 0) // 0 is 0.0f in float ((UpdatableProperty)operation.Member).SetObject(currentPtr, updateObject.Value); break; } case UpdateOperationType.ConditionalSetBlittablePropertyBase: { // TODO: This case can happen quite often (i.e. a float property) and require an extra indirection // We could probably avoid it by having common types as non virtual methods (i.e. object, int, float, maybe even Vector3/4?) var data = (int*)((byte*)updateData + operation.DataOffset); if (*data++ != 0) // 0 is 0.0f in float ((UpdatablePropertyBase)operation.Member).SetBlittable(currentPtr, (IntPtr)data); break; } case UpdateOperationType.ConditionalSetStructPropertyBase: { // TODO: This case can happen quite often (i.e. a float property) and require an extra indirection // We could probably avoid it by having common types as non virtual methods (i.e. object, int, float, maybe even Vector3/4?) var updateObject = updateObjects[operation.DataOffset]; if (updateObject.Condition != 0) // 0 is 0.0f in float ((UpdatablePropertyBase)operation.Member).SetStruct(currentPtr, updateObject.Value); break; } case UpdateOperationType.ConditionalSetObjectField: { var updateObject = updateObjects[operation.DataOffset]; if (updateObject.Condition != 0) // 0 is 0.0f in float ((UpdatableField)operation.Member).SetObject(currentPtr, updateObject.Value); break; } case UpdateOperationType.ConditionalSetBlittableField: { var data = (int*)((byte*)updateData + operation.DataOffset); if (*data++ != 0) // 0 is 0.0f in float ((UpdatableField)operation.Member).SetBlittable(currentPtr, (IntPtr)data); break; } case UpdateOperationType.ConditionalSetBlittableField4: { var data = (int*)((byte*)updateData + operation.DataOffset); if (*data++ != 0) // 0 is 0.0f in float { *(int*)currentPtr = *data; } break; } case UpdateOperationType.ConditionalSetBlittableField8: { var data = (int*)((byte*)updateData + operation.DataOffset); if (*data++ != 0) // 0 is 0.0f in float { *(Blittable8*)currentPtr = *(Blittable8*)data; } break; } case UpdateOperationType.ConditionalSetBlittableField12: { var data = (int*)((byte*)updateData + operation.DataOffset); if (*data++ != 0) // 0 is 0.0f in float { *(Blittable12*)currentPtr = *(Blittable12*)data; } break; } case UpdateOperationType.ConditionalSetBlittableField16: { var data = (int*)((byte*)updateData + operation.DataOffset); if (*data++ != 0) // 0 is 0.0f in float { *(Blittable16*)currentPtr = *(Blittable16*)data; } break; } case UpdateOperationType.ConditionalSetStructField: { // Use setter to set back struct var updateObject = updateObjects[operation.DataOffset]; if (updateObject.Condition != 0) // 0 is 0.0f in float ((UpdatableField)operation.Member).SetStruct(currentPtr, updateObject.Value); break; } case UpdateOperationType.ConditionalSetObjectCustom: { var updateObject = updateObjects[operation.DataOffset]; if (updateObject.Condition != 0) // 0 is 0.0f in float ((UpdatableCustomAccessor)operation.Member).SetObject(currentPtr, updateObject.Value); break; } default: throw new ArgumentOutOfRangeException(); } operation = Interop.IncrementPinned(operation); } }