236 lines
9.1 KiB
C#
236 lines
9.1 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using Unity.Mathematics;
|
|
using Unity.Collections;
|
|
using Unity.Jobs;
|
|
using Unity.Collections.LowLevel.Unsafe;
|
|
using UnityEngine.U2D.Common;
|
|
using UnityEngine.Assertions;
|
|
|
|
namespace UnityEngine.U2D.Animation
|
|
{
|
|
internal class GpuDeformationSystem : BaseDeformationSystem
|
|
{
|
|
const string k_GpuSkinningShaderKeyword = "SKINNED_SPRITE";
|
|
const string k_GlobalSpriteBoneBufferId = "_SpriteBoneTransforms";
|
|
|
|
readonly Dictionary<int, Material> m_KeywordEnabledMaterials = new Dictionary<int, Material>();
|
|
|
|
NativeArray<int> m_BoneTransformBufferSizes;
|
|
ComputeBuffer m_BoneTransformsComputeBuffer;
|
|
static ComputeBuffer s_FallbackBuffer;
|
|
|
|
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
|
|
static void CreateFallbackBuffer()
|
|
{
|
|
if (s_FallbackBuffer == null)
|
|
s_FallbackBuffer = new ComputeBuffer(UnsafeUtility.SizeOf<float4x4>(), UnsafeUtility.SizeOf<float4x4>(), ComputeBufferType.Default);
|
|
|
|
Shader.SetGlobalBuffer(k_GlobalSpriteBoneBufferId, s_FallbackBuffer);
|
|
}
|
|
|
|
static void ClearFallbackBuffer()
|
|
{
|
|
if(s_FallbackBuffer != null)
|
|
s_FallbackBuffer.Release();
|
|
|
|
s_FallbackBuffer = null;
|
|
}
|
|
|
|
public override DeformationMethods deformationMethod => DeformationMethods.Gpu;
|
|
|
|
internal static bool DoesShaderSupportGpuDeformation(Material material)
|
|
{
|
|
if (material == null)
|
|
return false;
|
|
var shader = material.shader;
|
|
if (shader == null)
|
|
return false;
|
|
|
|
var supportedKeywords = shader.keywordSpace.keywords;
|
|
for (var i = 0; i < supportedKeywords.Length; ++i)
|
|
{
|
|
if (supportedKeywords[i].name == k_GpuSkinningShaderKeyword)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool IsComputeBufferValid(ComputeBuffer buffer) => buffer != null && buffer.IsValid();
|
|
|
|
protected override void InitializeArrays()
|
|
{
|
|
base.InitializeArrays();
|
|
|
|
const int startingCount = 0;
|
|
m_BoneTransformBuffers = new NativeArray<IntPtr>(startingCount, Allocator.Persistent);
|
|
m_BoneTransformBufferSizes = new NativeArray<int>(startingCount, Allocator.Persistent);
|
|
|
|
CreateFallbackBuffer();
|
|
}
|
|
|
|
internal override void Cleanup()
|
|
{
|
|
base.Cleanup();
|
|
|
|
m_BoneTransformBuffers.DisposeIfCreated();
|
|
m_BoneTransformBufferSizes.DisposeIfCreated();
|
|
|
|
CleanupComputeResources();
|
|
|
|
ClearFallbackBuffer();
|
|
}
|
|
|
|
protected override void ResizeAndCopyArrays(int updatedCount)
|
|
{
|
|
base.ResizeAndCopyArrays(updatedCount);
|
|
NativeArrayHelpers.ResizeAndCopyIfNeeded(ref m_BoneTransformBuffers, updatedCount);
|
|
NativeArrayHelpers.ResizeAndCopyIfNeeded(ref m_BoneTransformBufferSizes, updatedCount);
|
|
|
|
if (updatedCount == 0)
|
|
CleanupComputeResources();
|
|
}
|
|
|
|
void CleanupComputeResources()
|
|
{
|
|
if (IsComputeBufferValid(m_BoneTransformsComputeBuffer))
|
|
m_BoneTransformsComputeBuffer.Release();
|
|
m_BoneTransformsComputeBuffer = null;
|
|
|
|
foreach (var material in m_KeywordEnabledMaterials.Values)
|
|
material.DisableKeyword(k_GpuSkinningShaderKeyword);
|
|
m_KeywordEnabledMaterials.Clear();
|
|
Shader.SetGlobalBuffer(k_GlobalSpriteBoneBufferId, s_FallbackBuffer);
|
|
}
|
|
|
|
internal override void UpdateMaterial(SpriteSkin spriteSkin)
|
|
{
|
|
var sharedMaterial = spriteSkin.spriteRenderer.sharedMaterial;
|
|
if (!sharedMaterial.IsKeywordEnabled(k_GpuSkinningShaderKeyword))
|
|
sharedMaterial.EnableKeyword(k_GpuSkinningShaderKeyword);
|
|
}
|
|
|
|
internal override bool AddSpriteSkin(SpriteSkin spriteSkin)
|
|
{
|
|
var success = base.AddSpriteSkin(spriteSkin);
|
|
|
|
var sharedMaterial = spriteSkin.spriteRenderer.sharedMaterial;
|
|
if (!sharedMaterial.IsKeywordEnabled(k_GpuSkinningShaderKeyword))
|
|
{
|
|
sharedMaterial.EnableKeyword(k_GpuSkinningShaderKeyword);
|
|
m_KeywordEnabledMaterials.TryAdd(sharedMaterial.GetInstanceID(), sharedMaterial);
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
internal override void Update()
|
|
{
|
|
BatchRemoveSpriteSkins();
|
|
BatchAddSpriteSkins();
|
|
|
|
var count = m_SpriteSkins.Count;
|
|
if (count == 0)
|
|
return;
|
|
|
|
Assert.AreEqual(m_IsSpriteSkinActiveForDeform.Length, count);
|
|
Assert.AreEqual(m_PerSkinJobData.Length, count);
|
|
Assert.AreEqual(m_SpriteSkinData.Length, count);
|
|
Assert.AreEqual(m_BoundsData.Length, count);
|
|
Assert.AreEqual(m_SpriteRenderers.Length, count);
|
|
Assert.AreEqual(m_Buffers.Length, count);
|
|
Assert.AreEqual(m_BufferSizes.Length, count);
|
|
|
|
Assert.AreEqual(m_BoneTransformBuffers.Length, count);
|
|
Assert.AreEqual(m_BoneTransformBufferSizes.Length, count);
|
|
|
|
PrepareDataForDeformation(out var localToWorldJobHandle, out var worldToLocalJobHandle);
|
|
|
|
if (!GotVerticesToDeform(out var vertexBufferSize))
|
|
{
|
|
localToWorldJobHandle.Complete();
|
|
worldToLocalJobHandle.Complete();
|
|
DeactivateDeformableBuffers();
|
|
return;
|
|
}
|
|
|
|
var skinBatch = m_SkinBatchArray[0];
|
|
ResizeBuffers(vertexBufferSize, in skinBatch);
|
|
|
|
var batchCount = m_SpriteSkinData.Length;
|
|
var jobHandle = SchedulePrepareJob(batchCount);
|
|
|
|
Profiling.scheduleJobs.Begin();
|
|
jobHandle = JobHandle.CombineDependencies(localToWorldJobHandle, worldToLocalJobHandle, jobHandle);
|
|
jobHandle = ScheduleBoneJobBatched(jobHandle, skinBatch);
|
|
m_DeformJobHandle = ScheduleSkinDeformBatchedJob(jobHandle, skinBatch);
|
|
jobHandle = ScheduleCopySpriteRendererBuffersJob(m_DeformJobHandle, batchCount);
|
|
jobHandle = ScheduleCopySpriteRendererBoneTransformBuffersJob(jobHandle, batchCount);
|
|
jobHandle = ScheduleCalculateSpriteSkinAABBJob(jobHandle, batchCount);
|
|
Profiling.scheduleJobs.End();
|
|
|
|
JobHandle.ScheduleBatchedJobs();
|
|
jobHandle.Complete();
|
|
|
|
using (Profiling.setBoneTransformsArray.Auto())
|
|
{
|
|
InternalEngineBridge.SetBatchBoneTransformsAABBArray(m_SpriteRenderers, m_BoneTransformBuffers, m_BoneTransformBufferSizes, m_BoundsData);
|
|
}
|
|
|
|
SetComputeBuffer();
|
|
|
|
foreach (var spriteSkin in m_SpriteSkins)
|
|
{
|
|
var didDeform = m_IsSpriteSkinActiveForDeform[spriteSkin.dataIndex];
|
|
spriteSkin.PostDeform(didDeform);
|
|
}
|
|
|
|
DeactivateDeformableBuffers();
|
|
}
|
|
|
|
void ResizeBuffers(int vertexBufferSize, in PerSkinJobData skinBatch)
|
|
{
|
|
var noOfBones = skinBatch.bindPosesIndex.y;
|
|
var noOfVerticesInBatch = skinBatch.verticesIndex.y;
|
|
|
|
m_DeformedVerticesBuffer = BufferManager.instance.GetBuffer(m_ObjectId, vertexBufferSize);
|
|
NativeArrayHelpers.ResizeIfNeeded(ref m_FinalBoneTransforms, noOfBones);
|
|
NativeArrayHelpers.ResizeIfNeeded(ref m_BoneLookupData, noOfBones);
|
|
NativeArrayHelpers.ResizeIfNeeded(ref m_VertexLookupData, noOfVerticesInBatch);
|
|
|
|
if (!IsComputeBufferValid(m_BoneTransformsComputeBuffer) || m_BoneTransformsComputeBuffer.count < noOfBones)
|
|
CreateComputeBuffer(noOfBones);
|
|
}
|
|
|
|
void CreateComputeBuffer(int bufferSize)
|
|
{
|
|
if (IsComputeBufferValid(m_BoneTransformsComputeBuffer))
|
|
m_BoneTransformsComputeBuffer.Release();
|
|
|
|
m_BoneTransformsComputeBuffer = new ComputeBuffer(bufferSize, UnsafeUtility.SizeOf<float4x4>(), ComputeBufferType.Default);
|
|
SetComputeBuffer();
|
|
}
|
|
|
|
void SetComputeBuffer()
|
|
{
|
|
m_BoneTransformsComputeBuffer.SetData(m_FinalBoneTransforms, 0, 0, m_FinalBoneTransforms.Length);
|
|
Shader.SetGlobalBuffer(k_GlobalSpriteBoneBufferId, m_BoneTransformsComputeBuffer);
|
|
}
|
|
|
|
unsafe JobHandle ScheduleCopySpriteRendererBoneTransformBuffersJob(JobHandle jobHandle, int batchCount)
|
|
{
|
|
var copySpriteRendererBoneTransformBuffersJob = new CopySpriteRendererBoneTransformBuffersJob()
|
|
{
|
|
isSpriteSkinValidForDeformArray = m_IsSpriteSkinActiveForDeform,
|
|
spriteSkinData = m_SpriteSkinData,
|
|
ptrBoneTransforms = (IntPtr)NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(m_FinalBoneTransforms),
|
|
perSkinJobData = m_PerSkinJobData,
|
|
buffers = m_BoneTransformBuffers,
|
|
bufferSizes = m_BoneTransformBufferSizes,
|
|
};
|
|
return copySpriteRendererBoneTransformBuffersJob.Schedule(batchCount, 16, jobHandle);
|
|
}
|
|
}
|
|
}
|