270 lines
9.7 KiB
C#
270 lines
9.7 KiB
C#
|
using System;
|
||
|
using System.Diagnostics;
|
||
|
using System.Runtime.CompilerServices;
|
||
|
|
||
|
namespace UnityEngine.Rendering.RenderGraphModule
|
||
|
{
|
||
|
// RendererList is a different case so not represented here.
|
||
|
internal enum RenderGraphResourceType
|
||
|
{
|
||
|
Texture = 0,
|
||
|
Buffer,
|
||
|
AccelerationStructure,
|
||
|
Count
|
||
|
}
|
||
|
|
||
|
internal struct ResourceHandle : IEquatable<ResourceHandle>
|
||
|
{
|
||
|
// Note on handles validity.
|
||
|
// PassData classes used during render graph passes are pooled and because of that, when users don't fill them completely,
|
||
|
// they can contain stale handles from a previous render graph execution that could still be considered valid if we only checked the index.
|
||
|
// In order to avoid using those, we incorporate the execution index in a 16 bits hash to make sure the handle is coming from the current execution.
|
||
|
// If not, it's considered invalid.
|
||
|
// We store this validity mask in the upper 16 bits of the index.
|
||
|
const uint kValidityMask = 0xFFFF0000;
|
||
|
const uint kIndexMask = 0xFFFF;
|
||
|
|
||
|
uint m_Value;
|
||
|
int m_Version; // A freshly created resource always starts at version 0 the first write should bring it to v1
|
||
|
|
||
|
static uint s_CurrentValidBit = 1 << 16;
|
||
|
static uint s_SharedResourceValidBit = 0x7FFF << 16;
|
||
|
|
||
|
public int index
|
||
|
{
|
||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
get { return (int)(m_Value & kIndexMask); }
|
||
|
}
|
||
|
public int iType
|
||
|
{
|
||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
get { return (int)type; }
|
||
|
}
|
||
|
public int version
|
||
|
{
|
||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
get { return m_Version; }
|
||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
set { m_Version = value; }
|
||
|
}
|
||
|
public RenderGraphResourceType type { get; private set; }
|
||
|
|
||
|
internal ResourceHandle(int value, RenderGraphResourceType type, bool shared)
|
||
|
{
|
||
|
Debug.Assert(value <= 0xFFFF);
|
||
|
m_Value = ((uint)value & kIndexMask) | (shared ? s_SharedResourceValidBit : s_CurrentValidBit);
|
||
|
this.type = type;
|
||
|
this.m_Version = -1;
|
||
|
}
|
||
|
|
||
|
internal ResourceHandle(in ResourceHandle h, int version)
|
||
|
{
|
||
|
this.m_Value = h.m_Value;
|
||
|
this.type = h.type;
|
||
|
this.m_Version = version;
|
||
|
}
|
||
|
|
||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
public bool IsValid()
|
||
|
{
|
||
|
var validity = m_Value & kValidityMask;
|
||
|
return validity != 0 && (validity == s_CurrentValidBit || validity == s_SharedResourceValidBit);
|
||
|
}
|
||
|
|
||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
public bool IsNull()
|
||
|
{
|
||
|
if (index == 0)
|
||
|
{
|
||
|
// Make sure everything is zero
|
||
|
Debug.Assert(m_Value == 0);
|
||
|
Debug.Assert(m_Version == 0);
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
static public void NewFrame(int executionIndex)
|
||
|
{
|
||
|
uint previousValidBit = s_CurrentValidBit;
|
||
|
// Scramble frame count to avoid collision when wrapping around.
|
||
|
s_CurrentValidBit = (uint)(((executionIndex >> 16) ^ (executionIndex & 0xffff) * 58546883) << 16);
|
||
|
// In case the current valid bit is 0, even though perfectly valid, 0 represents an invalid handle, hence we'll
|
||
|
// trigger an invalid state incorrectly. To account for this, we actually skip 0 as a viable s_CurrentValidBit and
|
||
|
// start from 1 again.
|
||
|
// In the same spirit, s_SharedResourceValidBit is reserved for shared textures so we should never use it otherwise
|
||
|
// resources could be considered valid at frame N+1 (because shared) even though they aren't.
|
||
|
if (s_CurrentValidBit == 0 || s_CurrentValidBit == s_SharedResourceValidBit)
|
||
|
{
|
||
|
// We need to make sure we don't pick the same value twice.
|
||
|
uint value = 1;
|
||
|
while (previousValidBit == (value << 16))
|
||
|
value++;
|
||
|
s_CurrentValidBit = (value << 16);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public bool IsVersioned
|
||
|
{
|
||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
get
|
||
|
{
|
||
|
return m_Version >= 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
public bool Equals(ResourceHandle hdl)
|
||
|
{
|
||
|
return hdl.m_Value == this.m_Value && hdl.m_Version == this.m_Version && hdl.type == this.type;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class IRenderGraphResource
|
||
|
{
|
||
|
public bool imported;
|
||
|
public bool shared;
|
||
|
public bool sharedExplicitRelease;
|
||
|
public bool requestFallBack;
|
||
|
public bool forceRelease;
|
||
|
public uint writeCount;
|
||
|
public int cachedHash;
|
||
|
public int transientPassIndex;
|
||
|
public int sharedResourceLastFrameUsed;
|
||
|
public int version;
|
||
|
|
||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
public virtual void Reset(IRenderGraphResourcePool _ = null)
|
||
|
{
|
||
|
imported = false;
|
||
|
shared = false;
|
||
|
sharedExplicitRelease = false;
|
||
|
cachedHash = -1;
|
||
|
transientPassIndex = -1;
|
||
|
sharedResourceLastFrameUsed = -1;
|
||
|
requestFallBack = false;
|
||
|
forceRelease = false;
|
||
|
writeCount = 0;
|
||
|
version = 0;
|
||
|
}
|
||
|
|
||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
public virtual string GetName()
|
||
|
{
|
||
|
return "";
|
||
|
}
|
||
|
|
||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
public virtual bool IsCreated()
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
public virtual void IncrementWriteCount()
|
||
|
{
|
||
|
writeCount++;
|
||
|
}
|
||
|
|
||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
public virtual int NewVersion()
|
||
|
{
|
||
|
version++;
|
||
|
return version;
|
||
|
}
|
||
|
|
||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
public virtual bool NeedsFallBack()
|
||
|
{
|
||
|
return requestFallBack && writeCount == 0;
|
||
|
}
|
||
|
|
||
|
public virtual void CreatePooledGraphicsResource() { }
|
||
|
public virtual void CreateGraphicsResource() { }
|
||
|
public virtual void UpdateGraphicsResource() { }
|
||
|
public virtual void ReleasePooledGraphicsResource(int frameIndex) { }
|
||
|
public virtual void ReleaseGraphicsResource() { }
|
||
|
public virtual void LogCreation(RenderGraphLogger logger) { }
|
||
|
public virtual void LogRelease(RenderGraphLogger logger) { }
|
||
|
public virtual int GetSortIndex() { return 0; }
|
||
|
public virtual int GetDescHashCode() { return 0; }
|
||
|
}
|
||
|
|
||
|
[DebuggerDisplay("Resource ({GetType().Name}:{GetName()})")]
|
||
|
abstract class RenderGraphResource<DescType, ResType>
|
||
|
: IRenderGraphResource
|
||
|
where DescType : struct
|
||
|
where ResType : class
|
||
|
{
|
||
|
public DescType desc;
|
||
|
public bool validDesc; // Does the descriptor contain valid data (this is not always the case for imported resources)
|
||
|
public ResType graphicsResource;
|
||
|
|
||
|
protected RenderGraphResourcePool<ResType> m_Pool;
|
||
|
|
||
|
protected RenderGraphResource()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
public override void Reset(IRenderGraphResourcePool pool = null)
|
||
|
{
|
||
|
base.Reset();
|
||
|
m_Pool = pool as RenderGraphResourcePool<ResType>;
|
||
|
graphicsResource = null;
|
||
|
validDesc = false;
|
||
|
}
|
||
|
|
||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
public override bool IsCreated()
|
||
|
{
|
||
|
return graphicsResource != null;
|
||
|
}
|
||
|
|
||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
public override void ReleaseGraphicsResource()
|
||
|
{
|
||
|
graphicsResource = null;
|
||
|
}
|
||
|
|
||
|
public override void CreatePooledGraphicsResource()
|
||
|
{
|
||
|
Debug.Assert(m_Pool != null, "RenderGraphResource: CreatePooledGraphicsResource should only be called for regular pooled resources");
|
||
|
|
||
|
int hashCode = GetDescHashCode();
|
||
|
|
||
|
if (graphicsResource != null)
|
||
|
throw new InvalidOperationException($"RenderGraphResource: Trying to create an already created resource ({GetName()}). Resource was probably declared for writing more than once in the same pass.");
|
||
|
|
||
|
// If the pool doesn't have any available resource that we can use, we will create one
|
||
|
// In any case, we will update the graphicsResource name based on the RenderGraph resource name
|
||
|
if (!m_Pool.TryGetResource(hashCode, out graphicsResource))
|
||
|
{
|
||
|
CreateGraphicsResource();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
UpdateGraphicsResource();
|
||
|
}
|
||
|
|
||
|
cachedHash = hashCode;
|
||
|
m_Pool.RegisterFrameAllocation(cachedHash, graphicsResource);
|
||
|
}
|
||
|
|
||
|
public override void ReleasePooledGraphicsResource(int frameIndex)
|
||
|
{
|
||
|
if (graphicsResource == null)
|
||
|
throw new InvalidOperationException($"RenderGraphResource: Tried to release a resource ({GetName()}) that was never created. Check that there is at least one pass writing to it first.");
|
||
|
|
||
|
// Shared resources don't use the pool
|
||
|
if (m_Pool != null)
|
||
|
{
|
||
|
m_Pool.ReleaseResource(cachedHash, graphicsResource, frameIndex);
|
||
|
m_Pool.UnregisterFrameAllocation(cachedHash, graphicsResource);
|
||
|
}
|
||
|
|
||
|
Reset();
|
||
|
}
|
||
|
}
|
||
|
}
|