Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
## [1.4.2] - 2024-10-04

### Changed

* Updated Burst dependency to version 1.8.18
* Updated entities packages dependencies

### Added

* The new `ProceduralMotion_Tag` tag component data type informs Entities Graphics that an Entity needs to participate in the object motion vector pass. The tag is automatically added for entities that go through the builtin MeshRendererBaker path.
* Enable entities motion vector pass participation for URP.
* PruneUploadBufferPool API to request pruning of the upload buffer pool.

### Fixed

* GameObjects that depend on vertex shader logic to generate procedural motion vectors through object motion vector passes will now also do the same when baked to Entities and rendered through Entities Graphics.
  • Loading branch information
Unity Technologies committed Oct 4, 2024
1 parent be43339 commit bdc7d9e
Show file tree
Hide file tree
Showing 12 changed files with 221 additions and 31 deletions.
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,24 @@ uid: changelog

# Changelog

## [1.4.2] - 2024-10-04

### Changed

* Updated Burst dependency to version 1.8.18
* Updated entities packages dependencies

### Added

* The new `ProceduralMotion_Tag` tag component data type informs Entities Graphics that an Entity needs to participate in the object motion vector pass. The tag is automatically added for entities that go through the builtin MeshRendererBaker path.
* Enable entities motion vector pass participation for URP.
* PruneUploadBufferPool API to request pruning of the upload buffer pool.


### Fixed

* GameObjects that depend on vertex shader logic to generate procedural motion vectors through object motion vector passes will now also do the same when baked to Entities and rendered through Entities Graphics.


## [1.3.2] - 2024-09-06

Expand Down
4 changes: 3 additions & 1 deletion Unity.Entities.Graphics/DrawCommandGeneration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1014,6 +1014,7 @@ internal unsafe struct EmitDrawCommandsJob : IJobParallelForDefer
[ReadOnly] public ComponentTypeHandle<MaterialMeshInfo> MaterialMeshInfo;
[ReadOnly] public ComponentTypeHandle<LocalToWorld> LocalToWorld;
[ReadOnly] public ComponentTypeHandle<DepthSorted_Tag> DepthSorted;
[ReadOnly] public ComponentTypeHandle<PerVertexMotionVectors_Tag> ProceduralMotion;
[ReadOnly] public ComponentTypeHandle<DeformedMeshIndex> DeformedMeshIndex;
[ReadOnly] public SharedComponentTypeHandle<RenderMeshArray> RenderMeshArray;
[ReadOnly] public SharedComponentTypeHandle<RenderFilterSettings> RenderFilterSettings;
Expand Down Expand Up @@ -1101,7 +1102,8 @@ public void Execute(int index)
#else
bool isDeformed = false;
#endif
hasMotion = orderChanged || transformChanged || isDeformed;
bool hasProceduralMotion = chunk.Has(ref ProceduralMotion);
hasMotion = orderChanged || transformChanged || isDeformed || hasProceduralMotion;
}

int chunkStartIndex = entitiesGraphicsChunkInfo.CullingData.ChunkOffsetInBatch;
Expand Down
5 changes: 5 additions & 0 deletions Unity.Entities.Graphics/EntitiesGraphicsComponents.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,4 +105,9 @@ public struct WorldToLocal_Tag : IComponentData {}
/// A tag component that enables depth sorting for the entity.
/// </summary>
public struct DepthSorted_Tag : IComponentData {}

/// <summary>
/// A tag component that enables motion vectors generated by vertex shader logic for the entity.
/// </summary>
public struct PerVertexMotionVectors_Tag : IComponentData {}
}
11 changes: 11 additions & 0 deletions Unity.Entities.Graphics/EntitiesGraphicsSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1193,6 +1193,16 @@ private void InitializeMaterialProperties()
}
}


/// <summary>
/// Prune sparse uploader gpu buffer pool.
/// </summary>
/// <param name="maxMemoryToRetainInUploadPoolBytes">Maximum memory target to keep alive in upload buffer pool. Only buffers marked as free will be pruned, so the memory retained might be more than requested.</param>
public void PruneUploadBufferPool(int maxMemoryToRetainInUploadPoolBytes)
{
m_GPUUploader.PruneUploadBufferPoolOnFrameCleanup(maxMemoryToRetainInUploadPoolBytes);
}

/// <summary>
/// Called when this system is destroyed.
/// </summary>
Expand Down Expand Up @@ -1596,6 +1606,7 @@ private JobHandle OnPerformCulling(BatchRendererGroup rendererGroup, BatchCullin
LocalToWorld = GetComponentTypeHandle<LocalToWorld>(true),
DepthSorted = GetComponentTypeHandle<DepthSorted_Tag>(true),
DeformedMeshIndex = GetComponentTypeHandle<DeformedMeshIndex>(true),
ProceduralMotion = GetComponentTypeHandle<PerVertexMotionVectors_Tag>(true),
RenderFilterSettings = GetSharedComponentTypeHandle<RenderFilterSettings>(),
FilterSettings = m_FilterSettings,
CullingLayerMask = cullingContext.cullingLayerMask,
Expand Down
3 changes: 3 additions & 0 deletions Unity.Entities.Graphics/MeshRendererBakingUtility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ static void ConvertToSingleEntity<T>(
// Add all components up front using as few calls as possible.
var componentFlag = RenderMeshUtility.EntitiesGraphicsComponentFlags.Baking;
componentFlag.AppendMotionAndProbeFlags(renderMeshDescription, baker.IsStatic());
componentFlag.AppendPerVertexMotionPassFlag(materials);
componentFlag.AppendDepthSortedFlag(materials);
lodComponent.AppendLODFlags(ref componentFlag);
baker.AddComponent(entity, RenderMeshUtility.ComputeComponentTypes(componentFlag));
Expand Down Expand Up @@ -264,6 +265,8 @@ static bool ValidateMeshAndMaterials(Renderer authoring, Mesh mesh, Material[] m
{
if (materials[i] == null)
errorMessage += $"Material ({i}) is null. ";
else if (materials[i].shader == null)
errorMessage += $"Material {materials[i].name} ({i}) has null shader. ";
}

if (!string.IsNullOrEmpty(errorMessage))
Expand Down
35 changes: 34 additions & 1 deletion Unity.Entities.Graphics/RenderMeshUtility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,10 @@ internal enum EntitiesGraphicsComponentFlags
Baking = 1 << 5,
UseRenderMeshArray = 1 << 6,
LODGroup = 1 << 7,
PerVertexMotionPass = 1 << 8,

// Count of unique flag combinations
PermutationCount = 1 << 8,
PermutationCount = 1 << 9,
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand Down Expand Up @@ -171,6 +172,8 @@ static ComponentTypeSet GenerateComponentTypes(EntitiesGraphicsComponentFlags fl
#if USE_HYBRID_MOTION_PASS
if (flags.HasFlagFast(EntitiesGraphicsComponentFlags.InMotionPass))
components.Add(ComponentType.ReadWrite<BuiltinMaterialPropertyUnity_MatrixPreviousM>());
if (flags.HasFlagFast(EntitiesGraphicsComponentFlags.PerVertexMotionPass))
components.Add(ComponentType.ReadWrite<PerVertexMotionVectors_Tag>());
#endif
if (flags.HasFlagFast(EntitiesGraphicsComponentFlags.LightProbesBlend))
components.Add(ComponentType.ReadWrite<BlendProbeTag>());
Expand Down Expand Up @@ -210,6 +213,12 @@ internal static void AppendMotionAndProbeFlags(this ref EntitiesGraphicsComponen
flags |= LightProbeFlags(renderMeshDescription.LightProbeUsage);
}

internal static void AppendPerVertexMotionPassFlag(this ref EntitiesGraphicsComponentFlags flags, ReadOnlySpan<Material> materials)
{
foreach (var material in materials)
flags.AppendPerVertexMotionPassFlag(material);
}

internal static void AppendDepthSortedFlag(this ref EntitiesGraphicsComponentFlags flags, ReadOnlySpan<Material> materials)
{
foreach (var material in materials)
Expand Down Expand Up @@ -303,6 +312,7 @@ public static void AddComponents(
// Add all components up front using as few calls as possible.
var componentFlags = EntitiesGraphicsComponentFlags.None;
componentFlags.AppendMotionAndProbeFlags(renderMeshDescription, entityManager.HasComponent<Static>(entity));
componentFlags.AppendPerVertexMotionPassFlag(material);
componentFlags.AppendDepthSortedFlag(material);
entityManager.AddComponent(entity, ComputeComponentTypes(componentFlags));

Expand All @@ -311,7 +321,30 @@ public static void AddComponents(
entityManager.SetComponentData(entity, new RenderBounds { Value = mesh.bounds.ToAABB() });
}

private const string kMotionVectorsShaderPass_HDRP_URP = "MotionVectors";
private const string kMotionVectorMaterialTagKey_HDRP = "MotionVector";
private const string kMotionVectorMaterialTagValue_HDRP = "User";
private static readonly ShaderTagId kMotionVectorShaderTagKey_URP = new("AlwaysRenderMotionVectors");
private static readonly ShaderTagId kMotionVectorShaderTagValue_URP = new("true");
/// <summary>
/// Adds the `PerVertexMotionPass` flag if the given material wants to write vertex shader generated motion
/// vectors. Works for materials that use standard HDRP or URP conventions for writing motion vectors.
/// </summary>
static void AppendPerVertexMotionPassFlag(this ref EntitiesGraphicsComponentFlags flags, Material material)
{
#if HDRP_10_0_0_OR_NEWER
// For HDRP check that the motion vectors pass is enabled and that the material enables user defined motion vectors.
if(material.GetShaderPassEnabled(kMotionVectorsShaderPass_HDRP_URP) && material.GetTag(kMotionVectorMaterialTagKey_HDRP, false) == kMotionVectorMaterialTagValue_HDRP)
flags |= EntitiesGraphicsComponentFlags.PerVertexMotionPass;
#elif URP_16_0_0_OR_NEWER
// For URP check that the motion vectors pass is enabled and that the shader has user defined motion vectors.
if(material.GetShaderPassEnabled(kMotionVectorsShaderPass_HDRP_URP) && material.shader.FindSubshaderTagValue(0, kMotionVectorShaderTagKey_URP) == kMotionVectorShaderTagValue_URP)
flags |= EntitiesGraphicsComponentFlags.PerVertexMotionPass;
#endif
}

#pragma warning restore CS0162

static void AppendDepthSortedFlag(this ref EntitiesGraphicsComponentFlags flags, Material material)
{
if (IsMaterialTransparent(material))
Expand Down
90 changes: 73 additions & 17 deletions Unity.Entities.Graphics/SparseUploader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,7 @@ internal class BufferPool : IDisposable
{
private List<GraphicsBuffer> m_Buffers;
private Stack<int> m_FreeBufferIds;
private Stack<int> m_BuffersReleased;

private int m_Count;
private int m_Stride;
Expand All @@ -377,6 +378,7 @@ public BufferPool(int count, int stride, GraphicsBuffer.Target target, GraphicsB
{
m_Buffers = new List<GraphicsBuffer>();
m_FreeBufferIds = new Stack<int>();
m_BuffersReleased = new Stack<int>();

m_Count = count;
m_Stride = stride;
Expand All @@ -388,16 +390,29 @@ public void Dispose()
{
for (int i = 0; i < m_Buffers.Count; ++i)
{
m_Buffers[i].Dispose();
if (m_Buffers[i].IsValid())
{
m_Buffers[i].Dispose();
}
}
}

private int AllocateBuffer()
{
var id = m_Buffers.Count;
var cb = new GraphicsBuffer(m_Target, m_UsageFlags, m_Count, m_Stride);
m_Buffers.Add(cb);
return id;
cb.name = "SparseUploaderBuffer";
if (m_BuffersReleased.Count > 0)
{
var id = m_BuffersReleased.Pop();
m_Buffers[id] = cb;
return id;
}
else
{
var id = m_Buffers.Count;
m_Buffers.Add(cb);
return id;
}
}

public int GetBufferId()
Expand All @@ -418,7 +433,26 @@ public void PutBufferId(int id)
m_FreeBufferIds.Push(id);
}

public int TotalBufferCount => m_Buffers.Count;
/*
* Prune free buffers to allow up to maxMemoryToRetainInBytes to remain.
* Note that this will only release buffers that are marked free, so the actual memory retained might be higher than requested
*/
public void PruneFreeBuffers(int maxMemoryToRetainInBytes)
{
int memoryToFree = TotalBufferSize - maxMemoryToRetainInBytes;
if (memoryToFree <= 0) return;

while (memoryToFree > 0 && m_FreeBufferIds.Count > 0)
{
var id = m_FreeBufferIds.Pop();
var buffer = GetBufferFromId(id);
buffer.Dispose();
m_BuffersReleased.Push(id);
memoryToFree -= m_Count * m_Stride;
}
}

public int TotalBufferCount => m_Buffers.Count - m_BuffersReleased.Count;
public int TotalBufferSize => TotalBufferCount * m_Count * m_Stride;
}

Expand Down Expand Up @@ -493,6 +527,9 @@ public FrameData()
int m_OperationsBaseID;
int m_ReplaceOperationSize;

int m_RequestedUploadBufferPoolMaxSizeBytes;
bool m_PruneUploadBufferPool;

/// <summary>
/// Constructs a new sparse uploader with the specified buffer as the target.
/// </summary>
Expand Down Expand Up @@ -526,6 +563,9 @@ public SparseUploader(GraphicsBuffer destinationBuffer, int bufferChunkSize = 16

m_CurrentFrameUploadSize = 0;
m_MaxUploadSize = 0;

m_RequestedUploadBufferPoolMaxSizeBytes = 0;
m_PruneUploadBufferPool = false;
}

/// <summary>
Expand Down Expand Up @@ -775,6 +815,16 @@ public void EndAndCommit(ThreadedSparseUploader tsu)
StepFrame();
}

/// <summary>
/// Requests pruning of upload buffers. The actual release will happen in FrameCleanup.
/// </summary>
/// <param name="requestedMaxSizeRetainedInBytes">Maximum memory target to keep alive in upload buffer pool. Only buffers marked as free will be pruned, so the memory retained might be more than requested.</param>
public void PruneUploadBufferPoolOnFrameCleanup(int requestedMaxSizeRetainedInBytes)
{
m_RequestedUploadBufferPoolMaxSizeBytes = requestedMaxSizeRetainedInBytes;
m_PruneUploadBufferPool = true;
}

/// <summary>
/// Cleans up internal data and recovers buffers into the free buffer pool.
/// </summary>
Expand All @@ -785,22 +835,28 @@ public void FrameCleanup()
{
var numBuffers = m_ThreadData->m_NumBuffers;

if (numBuffers == 0)
return;

// These buffers where never used, so they gets returned to the pool at once
for (int iBuf = 0; iBuf < numBuffers; ++iBuf)
if (numBuffers > 0)
{
var mappedBuffer = m_MappedBuffers[iBuf];
MappedBuffer.UnpackMarker(mappedBuffer.m_Marker, out var operationOffset, out var dataOffset);
var graphicsBufferID = mappedBuffer.m_BufferID;
var graphicsBuffer = m_UploadBufferPool.GetBufferFromId(graphicsBufferID);
// These buffers were never used, so they get returned to the pool at once
for (int iBuf = 0; iBuf < numBuffers; ++iBuf)
{
var mappedBuffer = m_MappedBuffers[iBuf];
MappedBuffer.UnpackMarker(mappedBuffer.m_Marker, out var operationOffset, out var dataOffset);
var graphicsBufferID = mappedBuffer.m_BufferID;
var graphicsBuffer = m_UploadBufferPool.GetBufferFromId(graphicsBufferID);

graphicsBuffer.UnlockBufferAfterWrite<byte>(0);
m_UploadBufferPool.PutBufferId(graphicsBufferID);
}

graphicsBuffer.UnlockBufferAfterWrite<byte>(0);
m_UploadBufferPool.PutBufferId(graphicsBufferID);
m_MappedBuffers.Dispose();
}

m_MappedBuffers.Dispose();
if (m_PruneUploadBufferPool)
{
m_UploadBufferPool.PruneFreeBuffers(m_RequestedUploadBufferPoolMaxSizeBytes);
m_PruneUploadBufferPool = false;
}

StepFrame();
}
Expand Down
7 changes: 6 additions & 1 deletion Unity.Entities.Graphics/Unity.Entities.Graphics.asmdef
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@
"expression": "9.9.9",
"define": "URP_10_0_0_OR_NEWER"
},
{
"name": "com.unity.render-pipelines.universal",
"expression": "15.9.9",
"define": "URP_16_0_0_OR_NEWER"
},
{
"name": "com.unity.render-pipelines.core",
"expression": "9.9.9",
Expand All @@ -48,4 +53,4 @@
}
],
"noEngineReferences": false
}
}
10 changes: 5 additions & 5 deletions ValidationExceptions.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,29 @@
{
"ValidationTest": "API Updater Configuration Validation",
"ExceptionMessage": "stdout:\nAPIUpdater Configuration Validation\n-----------------------------------\n\nConfiguration Validation Tests (Failed: 0, Total: 1, Ignored 0):\n----------------------------------------------------------------\n\n\nAuto Generated Tests (Failed: 1, Total: 1, Ignored 0):\n------------------------------------------------------\n1) Expected updates not applied for configuration:\n[*] System.SByte [*] Unity.Rendering.MaterialMeshInfo::Submesh -> * Unity.Rendering.MaterialMeshInfo::SubMesh\n\nInput : unsafe class Test : object { System.SByte Method(System.SByte memberValue, Unity.Rendering.MaterialMeshInfo obj) { System.SByte local = obj.Submesh; return Method(obj.Submesh, obj); } }\nExpected: unsafe class Test : object { System.UInt16 Method(System.UInt16 memberValue, Unity.Rendering.MaterialMeshInfo obj) { System.UInt16 local = obj.SubMesh; return Method(obj.SubMesh, obj); } }\nActual : unsafe class Test : object { System.SByte Method(System.SByte memberValue, Unity.Rendering.MaterialMeshInfo obj) { System.SByte local = obj.SubMesh; return Method(obj.SubMesh, obj); } }\n\n\nBase type validation (Failed: 0, Total: 0, Ignored 0):\n------------------------------------------------------\nstderr:\n",
"PackageVersion": "1.3.2"
"PackageVersion": "1.4.2"
},
{
"ValidationTest": "API Validation",
"ExceptionMessage": "Breaking changes require a new major version.",
"PackageVersion": "1.3.2"
"PackageVersion": "1.4.2"
}
],
"WarningExceptions": [
{
"ValidationTest": "Manifest Validation",
"ExceptionMessage": "Package dependency [email protected] must be promoted to production before this package is promoted to production. (Except for core packages)",
"PackageVersion": "1.3.2"
"PackageVersion": "1.4.2"
},
{
"ValidationTest": "Folder Structure Validation",
"ExceptionMessage": "The Resources Directory should not be used in packages. For more guidance, please visit https://docs.unity3d.com/Manual/BestPracticeUnderstandingPerformanceInUnity6.html",
"PackageVersion": "1.3.2"
"PackageVersion": "1.4.2"
},
{
"ValidationTest": "Package Lifecycle Validation",
"ExceptionMessage": "com.unity.entities.graphics has never been promoted to production before. Please contact Release Management through slack in #devs-pkg-promotion to promote the first version of your package before trying to use this automated pipeline. Read more about this error and potential solutions at https://docs.unity3d.com/Packages/com.unity.package-validation-suite@latest/index.html?preview=1&subfolder=/manual/lifecycle_validation_error.html#the-very-first-version-of-a-package-must-be-promoted-by-release-management",
"PackageVersion": "1.3.2"
"PackageVersion": "1.4.2"
}
]
}
Loading

0 comments on commit bdc7d9e

Please sign in to comment.