/**
* 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
}
}