/** * Copyright(c) Live2D Inc. All rights reserved. * * Use of this source code is governed by the Live2D Open Software license * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html. */ using Live2D.Cubism.Core.Unmanaged; using System.Threading; namespace Live2D.Cubism.Core { /// /// 'Atomic' update task. /// internal sealed class CubismTaskableModel : ICubismTask { #region Factory Methods /// /// Creates a from a . /// /// Moc source. /// Instance. public static CubismTaskableModel CreateTaskableModel(CubismMoc moc) { return new CubismTaskableModel(moc); } #endregion /// /// Handle to unmanaged model. /// public CubismUnmanagedModel UnmanagedModel { get; private set; } /// /// the model was instantiated from. /// public CubismMoc Moc { get; private set; } private CubismDynamicDrawableData[] _dynamicDrawableData; /// /// Buffer to write dynamic data to. /// public CubismDynamicDrawableData[] DynamicDrawableData { get { CubismDynamicDrawableData[] dynamicDrawableData = null; if (Monitor.TryEnter(Lock)) { dynamicDrawableData = _dynamicDrawableData; Monitor.Exit(Lock); } return dynamicDrawableData; } private set { _dynamicDrawableData = value; } } /// /// True if task is currently executing. /// public bool IsExecuting { get { var isExecuting = false; if (Monitor.TryEnter(Lock)) { isExecuting = (State == TaskState.Enqueued || State == TaskState.Executing); Monitor.Exit(Lock); } return isExecuting; } } /// /// True if did run to completion at least once. /// public bool DidExecute { get { var didExecute = false; if (Monitor.TryEnter(Lock)) { didExecute = (State == TaskState.Executed); Monitor.Exit(Lock); } return didExecute; } } /// /// True if unmanaged model and moc should be released. /// private bool ShouldReleaseUnmanaged { get; set; } #region Constructor /// /// Initializes instance. /// /// Moc unmanaged model was instantiated from. public CubismTaskableModel(CubismMoc moc) { Moc = moc; // Instantiate unmanaged model. var unmanagedMoc = moc.AcquireUnmanagedMoc(); UnmanagedModel = CubismUnmanagedModel.FromMoc(unmanagedMoc); Lock = new object(); State = TaskState.Idle; DynamicDrawableData = CubismDynamicDrawableData.CreateData(UnmanagedModel); ShouldReleaseUnmanaged = false; } #endregion /// /// Tries to read parameters into a buffer. /// /// Buffer to write to. /// on success; otherwise. public bool TryReadParameters(CubismParameter[] parameters) { var didRead = false; if (Monitor.TryEnter(Lock)) { try { if (State == TaskState.Executed) { parameters.ReadFrom(UnmanagedModel); didRead = true; } } finally { Monitor.Exit(Lock); } } return didRead; } /// /// Tries to write parameters to a buffer. /// /// Buffer to read from. /// Buffer to read from. /// on success; otherwise. public bool TryWriteParametersAndParts(CubismParameter[] parameters, CubismPart[] parts) { var didWrite = false; if (Monitor.TryEnter(Lock)) { try { if (State != TaskState.Executing) { parameters.WriteTo(UnmanagedModel); parts.WriteTo(UnmanagedModel); didWrite = true; } } finally { Monitor.Exit(Lock); } } return didWrite; } /// /// Dispatches the task for (maybe async) execution. /// public void Update() { // Validate state. lock (Lock) { if (State == TaskState.Enqueued || State == TaskState.Executing) { return; } // Update state. State = TaskState.Enqueued; } CubismTaskQueue.Enqueue(this); } /// /// Forces the task to run now to completion. /// public bool UpdateNow() { // Validate state. lock (Lock) { if (State == TaskState.Enqueued || State == TaskState.Executing) { return false; } // Update state. State = TaskState.Enqueued; } // Run execution directly. Execute(); return true; } /// /// Releases unmanaged resource. /// public void ReleaseUnmanaged() { ShouldReleaseUnmanaged = true; // Return if task is ongoing. lock (Lock) { if (State == TaskState.Enqueued || State == TaskState.Executing) { return; } } OnReleaseUnmanaged(); ShouldReleaseUnmanaged = false; } /// /// Runs the task. /// private void Execute() { // Validate state. lock (Lock) { State = TaskState.Executing; } // Update native backend. UnmanagedModel.Update(); // Get results. DynamicDrawableData.ReadFrom(UnmanagedModel); // Update state. lock (Lock) { State = TaskState.Executed; // Release native if requested. if (ShouldReleaseUnmanaged) { OnReleaseUnmanaged(); } } } /// /// Actually releases native resource(s). /// private void OnReleaseUnmanaged() { UnmanagedModel.Release(); Moc.ReleaseUnmanagedMoc(); UnmanagedModel = null; } #region Implementation of ICubismTask void ICubismTask.Execute() { Execute(); } #endregion #region Threading /// /// Task states. /// private enum TaskState { /// /// Idle state. /// Idle, /// /// Waiting-for-execution state. /// Enqueued, /// /// Executing state. /// Executing, /// /// Executed state. /// Executed } /// /// Lock. /// private object Lock { get; set; } /// /// Internal state. /// private TaskState State { get; set; } #endregion } }