GhostEngine Render Graph: major refactor & Unity RG ref
- Major architectural refactor for performance, extensibility, and feature completeness: resource pooling, pass culling, aliasing, and compilation caching. - Introduces type-safe builder and context APIs, blackboard pattern, and unified resource management. - Adds detailed documentation and cleans up obsolete files and APIs. - Includes (commented) Unity Render Graph source for reference; not compiled, for parity and future extension.
This commit is contained in:
2220
Ghost.RenderGraph.Concept/Unity/NativePassCompiler.cs
Normal file
2220
Ghost.RenderGraph.Concept/Unity/NativePassCompiler.cs
Normal file
File diff suppressed because it is too large
Load Diff
1692
Ghost.RenderGraph.Concept/Unity/RenderGraph.cs
Normal file
1692
Ghost.RenderGraph.Concept/Unity/RenderGraph.cs
Normal file
File diff suppressed because it is too large
Load Diff
598
Ghost.RenderGraph.Concept/Unity/RenderGraphBuilders.cs
Normal file
598
Ghost.RenderGraph.Concept/Unity/RenderGraphBuilders.cs
Normal file
@@ -0,0 +1,598 @@
|
||||
#if false
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using UnityEngine.Experimental.Rendering;
|
||||
using static UnityEngine.Rendering.RenderGraphModule.RenderGraph;
|
||||
|
||||
namespace UnityEngine.Rendering.RenderGraphModule
|
||||
{
|
||||
// This is a class making it a struct wouldn't help as we pas it around as an interface which means it would be boxed/unboxed anyway
|
||||
// Publicly this class has different faces to help the users with different pass types through type safety but internally
|
||||
// we just have a single implementation for all builders
|
||||
internal class RenderGraphBuilders : IBaseRenderGraphBuilder, IComputeRenderGraphBuilder, IRasterRenderGraphBuilder, IUnsafeRenderGraphBuilder
|
||||
{
|
||||
RenderGraphPass m_RenderPass;
|
||||
RenderGraphResourceRegistry m_Resources;
|
||||
RenderGraph m_RenderGraph;
|
||||
bool m_Disposed;
|
||||
|
||||
public RenderGraphBuilders()
|
||||
{
|
||||
m_RenderPass = null;
|
||||
m_Resources = null;
|
||||
m_RenderGraph = null;
|
||||
m_Disposed = true;
|
||||
}
|
||||
|
||||
public void Setup(RenderGraphPass renderPass, RenderGraphResourceRegistry resources, RenderGraph renderGraph)
|
||||
{
|
||||
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
||||
// If the object is not disposed yet this is an error as the pass is not finished (only in the dispose we register it with the rendergraph)
|
||||
// This is likely cause by a user not doing a clean using and then forgetting to manually dispose the object.
|
||||
if (m_Disposed != true)
|
||||
{
|
||||
throw new Exception(RenderGraph.RenderGraphExceptionMessages.k_UndisposedBuilderPreviousPass);
|
||||
}
|
||||
#endif
|
||||
m_RenderPass = renderPass;
|
||||
m_Resources = resources;
|
||||
m_RenderGraph = renderGraph;
|
||||
m_Disposed = false;
|
||||
|
||||
renderPass.useAllGlobalTextures = false;
|
||||
|
||||
if (renderPass.type == RenderGraphPassType.Raster)
|
||||
{
|
||||
CommandBuffer.ThrowOnSetRenderTarget = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void EnableAsyncCompute(bool value)
|
||||
{
|
||||
m_RenderPass.EnableAsyncCompute(value);
|
||||
}
|
||||
|
||||
public void AllowPassCulling(bool value)
|
||||
{
|
||||
// This pass cannot be culled if it allows global state modifications
|
||||
if (value && m_RenderPass.allowGlobalState)
|
||||
return;
|
||||
|
||||
m_RenderPass.AllowPassCulling(value);
|
||||
}
|
||||
|
||||
public void AllowGlobalStateModification(bool value)
|
||||
{
|
||||
m_RenderPass.AllowGlobalState(value);
|
||||
|
||||
// This pass cannot be culled if it allows global state modifications
|
||||
if (value)
|
||||
{
|
||||
AllowPassCulling(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enable foveated rendering for this pass.
|
||||
/// </summary>
|
||||
/// <param name="value">True to enable foveated rendering.</param>
|
||||
public void EnableFoveatedRasterization(bool value)
|
||||
{
|
||||
m_RenderPass.EnableFoveatedRasterization(value);
|
||||
}
|
||||
|
||||
public BufferHandle CreateTransientBuffer(in BufferDesc desc)
|
||||
{
|
||||
var result = m_Resources.CreateBuffer(desc, m_RenderPass.index);
|
||||
UseTransientResource(result.handle);
|
||||
return result;
|
||||
}
|
||||
|
||||
public BufferHandle CreateTransientBuffer(in BufferHandle computebuffer)
|
||||
{
|
||||
ref readonly var desc = ref m_Resources.GetBufferResourceDesc(computebuffer.handle);
|
||||
return CreateTransientBuffer(desc);
|
||||
}
|
||||
|
||||
public TextureHandle CreateTransientTexture(in TextureDesc desc)
|
||||
{
|
||||
var result = m_Resources.CreateTexture(desc, m_RenderPass.index);
|
||||
UseTransientResource(result.handle);
|
||||
return result;
|
||||
}
|
||||
|
||||
public TextureHandle CreateTransientTexture(in TextureHandle texture)
|
||||
{
|
||||
ref readonly var desc = ref m_Resources.GetTextureResourceDesc(texture.handle);
|
||||
return CreateTransientTexture(desc);
|
||||
}
|
||||
|
||||
public void GenerateDebugData(bool value)
|
||||
{
|
||||
m_RenderPass.GenerateDebugData(value);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (m_Disposed)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
m_RenderGraph.RenderGraphState = RenderGraphState.RecordingGraph;
|
||||
|
||||
// Use all globals simply means this... we do a UseTexture on all globals so the pass has the correct dependencies.
|
||||
// This of course goes to show how bad an idea shader-system wide globals really are dependency/lifetime tracking wise :-)
|
||||
if (m_RenderPass.useAllGlobalTextures)
|
||||
{
|
||||
foreach (var texture in m_RenderGraph.AllGlobals())
|
||||
{
|
||||
if (texture.IsValid())
|
||||
this.UseTexture(texture, AccessFlags.Read);
|
||||
}
|
||||
}
|
||||
|
||||
// Set globals on the graph fronted side so subsequent passes can have pass dependencies on these global texture handles
|
||||
foreach (var t in m_RenderPass.setGlobalsList)
|
||||
{
|
||||
m_RenderGraph.SetGlobal(t.Item1, t.Item2);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (m_RenderPass.type == RenderGraphPassType.Raster)
|
||||
{
|
||||
CommandBuffer.ThrowOnSetRenderTarget = false;
|
||||
}
|
||||
|
||||
m_RenderPass = null;
|
||||
m_Resources = null;
|
||||
m_RenderGraph = null;
|
||||
m_Disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
[Conditional("DEVELOPMENT_BUILD"), Conditional("UNITY_EDITOR")]
|
||||
private void CheckWriteTo(in ResourceHandle handle)
|
||||
{
|
||||
if (RenderGraph.enableValidityChecks)
|
||||
{
|
||||
// Write by design generates a new version of the resource. However
|
||||
// you could in theory write to v2 of the resource while there is already
|
||||
// a v3 so this write would then introduce a new v4 of the resource.
|
||||
// This would mean a divergence in the versioning history subsequent versions based on v2 and other subsequent versions based on v3
|
||||
// this would be very confusing as they are all still refered to by the same texture just different versions
|
||||
// so we decide to disallow this. It can always be (at zero cost) handled by using a "Move" pass to move the divergent version
|
||||
// so it's own texture resource which again has it's own single history of versions.
|
||||
if (handle.IsVersioned)
|
||||
{
|
||||
var name = m_Resources.GetRenderGraphResourceName(handle);
|
||||
throw new InvalidOperationException($"In pass '{m_RenderPass.name}' when trying to use resource '{name}' of type {handle.type} at index {handle.index} - " + RenderGraph.RenderGraphExceptionMessages.k_WriteToVersionedResource);
|
||||
}
|
||||
|
||||
if (m_RenderPass.IsWritten(handle))
|
||||
{
|
||||
// We bump the version and write count if you call USeResource with writing flags. So calling this several times
|
||||
// would lead to the side effect of new versions for every call to UseResource leading to incorrect versions.
|
||||
// In theory we could detect and ignore the second UseResource but we decided to just disallow is as it might also
|
||||
// Stem from user confusion.
|
||||
// It seems the most likely cause of such a situation would be something like:
|
||||
// TextureHandle b = a;
|
||||
// ...
|
||||
// much much code in between, user lost track that a=b
|
||||
// ...
|
||||
// builder.WriteTexture(a)
|
||||
// builder.WriteTexture(b)
|
||||
// > Get this error they were probably thinking they were writing two separate outputs... but they are just two versions of resource 'a'
|
||||
// where they can only differ between by careful management of versioned resources.
|
||||
var name = m_Resources.GetRenderGraphResourceName(handle);
|
||||
throw new InvalidOperationException($"In pass '{m_RenderPass.name}' when trying to use resource '{name}' of type {handle.type} at index {handle.index} - " + RenderGraph.RenderGraphExceptionMessages.k_WriteToResourceTwice);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Lifetime of a transient resource is one render graph pass
|
||||
private ResourceHandle UseTransientResource(in ResourceHandle inputHandle)
|
||||
{
|
||||
CheckResource(inputHandle);
|
||||
|
||||
ResourceHandle versionedHandle = inputHandle.IsVersioned ? inputHandle : m_Resources.GetLatestVersionHandle(inputHandle);
|
||||
|
||||
// Transient resources are always considered written and read in the render graph pass where they are used
|
||||
// Compiler will take it into account later, no need to add them to read and write lists
|
||||
m_RenderPass.AddTransientResource(versionedHandle);
|
||||
|
||||
return versionedHandle;
|
||||
}
|
||||
|
||||
private ResourceHandle UseResource(in ResourceHandle inputHandle, AccessFlags flags)
|
||||
{
|
||||
CheckResource(inputHandle);
|
||||
|
||||
bool discard = (flags & AccessFlags.Discard) != 0;
|
||||
bool read = (flags & AccessFlags.Read) != 0;
|
||||
bool write = (flags & AccessFlags.Write) != 0;
|
||||
|
||||
ResourceHandle versionedHandle = inputHandle.IsVersioned ? inputHandle : m_Resources.GetLatestVersionHandle(inputHandle);
|
||||
|
||||
// If we are not discarding the current version and its data, add a "read" dependency on it
|
||||
// this is a bit of a misnomer it really means more like "Preserve existing content or read"
|
||||
if (!discard)
|
||||
{
|
||||
m_Resources.IncrementReadCount(versionedHandle);
|
||||
m_RenderPass.AddResourceRead(versionedHandle);
|
||||
|
||||
// Implicit read - not user-specified
|
||||
if (!read)
|
||||
{
|
||||
m_RenderPass.implicitReadsList.Add(versionedHandle);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// We are discarding it but we still read it, so we add a dependency on version "0" of this resource
|
||||
if (read)
|
||||
{
|
||||
var zeroVersionHandle = m_Resources.GetZeroVersionHandle(versionedHandle);
|
||||
m_Resources.IncrementReadCount(zeroVersionHandle);
|
||||
m_RenderPass.AddResourceRead(zeroVersionHandle);
|
||||
}
|
||||
}
|
||||
|
||||
if (write)
|
||||
{
|
||||
CheckWriteTo(inputHandle);
|
||||
// New versioned written by this render graph pass
|
||||
versionedHandle = m_Resources.IncrementWriteCount(inputHandle);
|
||||
m_RenderPass.AddResourceWrite(versionedHandle);
|
||||
}
|
||||
|
||||
return versionedHandle;
|
||||
}
|
||||
|
||||
public BufferHandle UseBuffer(in BufferHandle input, AccessFlags flags)
|
||||
{
|
||||
UseResource(input.handle, flags);
|
||||
return input;
|
||||
}
|
||||
|
||||
// UseTexture and SetRenderAttachment are currently forced to be mutually exclusive in the same pass
|
||||
// check this.
|
||||
// We currently ignore the version. In theory there might be some cases that are actually allowed with versioning
|
||||
// for ample UseTexture(myTexV1, read) UseFragment(myTexV2, ReadWrite) as they are different versions
|
||||
// but for now we don't allow any of that.
|
||||
[Conditional("DEVELOPMENT_BUILD"), Conditional("UNITY_EDITOR")]
|
||||
private void CheckNotUseFragment(in TextureHandle tex)
|
||||
{
|
||||
if (RenderGraph.enableValidityChecks)
|
||||
{
|
||||
bool usedAsFragment = (m_RenderPass.depthAccess.textureHandle.IsValid() && m_RenderPass.depthAccess.textureHandle.handle.index == tex.handle.index);
|
||||
if (!usedAsFragment)
|
||||
{
|
||||
for (int i = 0; i <= m_RenderPass.colorBufferMaxIndex; i++)
|
||||
{
|
||||
if (m_RenderPass.colorBufferAccess[i].textureHandle.IsValid() && m_RenderPass.colorBufferAccess[i].textureHandle.handle.index == tex.handle.index)
|
||||
{
|
||||
usedAsFragment = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (usedAsFragment)
|
||||
{
|
||||
var name = m_Resources.GetRenderGraphResourceName(tex.handle);
|
||||
throw new ArgumentException($"In pass '{m_RenderPass.name}' when trying to use resource '{name}' of type {tex.handle.type} at index {tex.handle.index} - " + RenderGraph.RenderGraphExceptionMessages.k_TextureAlreadyBeingUsedThroughSetAttachment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Conditional("DEVELOPMENT_BUILD"), Conditional("UNITY_EDITOR")]
|
||||
private void CheckTextureUVOriginIsValid(in ResourceHandle handle, TextureResource texRes)
|
||||
{
|
||||
if (texRes.textureUVOrigin == TextureUVOriginSelection.TopLeft)
|
||||
{
|
||||
var name = m_Resources.GetRenderGraphResourceName(handle);
|
||||
throw new ArgumentException($"In pass '{m_RenderPass.name}' when trying to use resource '{name}' of type `{handle.type}` at index `{handle.index}` - " + RenderGraph.RenderGraphExceptionMessages.IncompatibleTextureUVOriginUseTexture(texRes.textureUVOrigin));
|
||||
}
|
||||
}
|
||||
|
||||
public void UseTexture(in TextureHandle input, AccessFlags flags)
|
||||
{
|
||||
CheckNotUseFragment(input);
|
||||
UseResource(input.handle, flags);
|
||||
|
||||
if ((flags & AccessFlags.Read) == AccessFlags.Read)
|
||||
{
|
||||
if (m_RenderGraph.renderTextureUVOriginStrategy == RenderTextureUVOriginStrategy.PropagateAttachmentOrientation)
|
||||
{
|
||||
TextureResource texRes = m_Resources.GetTextureResource(input.handle);
|
||||
CheckTextureUVOriginIsValid(input.handle, texRes);
|
||||
texRes.textureUVOrigin = TextureUVOriginSelection.BottomLeft;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void UseGlobalTexture(int propertyId, AccessFlags flags)
|
||||
{
|
||||
var h = m_RenderGraph.GetGlobal(propertyId);
|
||||
if (h.IsValid())
|
||||
{
|
||||
UseTexture(h, flags);
|
||||
}
|
||||
else
|
||||
{
|
||||
// rose test this path
|
||||
var name = m_Resources.GetRenderGraphResourceName(h.handle);
|
||||
throw new ArgumentException($"In pass '{m_RenderPass.name}' when trying to use resource '{name}' of type {h.handle.type} at index {h.handle.index} - " + RenderGraph.RenderGraphExceptionMessages.NoGlobalTextureAtPropertyID(propertyId));
|
||||
}
|
||||
}
|
||||
|
||||
public void UseAllGlobalTextures(bool enable)
|
||||
{
|
||||
m_RenderPass.useAllGlobalTextures = enable;
|
||||
}
|
||||
|
||||
public void SetGlobalTextureAfterPass(in TextureHandle input, int propertyId)
|
||||
{
|
||||
m_RenderPass.setGlobalsList.Add(ValueTuple.Create(input, propertyId));
|
||||
}
|
||||
|
||||
// Shared validation between SetRenderAttachment/SetRenderAttachmentDepth
|
||||
[Conditional("DEVELOPMENT_BUILD"), Conditional("UNITY_EDITOR")]
|
||||
private void CheckUseFragment(in TextureHandle tex, bool isDepth)
|
||||
{
|
||||
if (RenderGraph.enableValidityChecks)
|
||||
{
|
||||
// We ignore the version as we don't allow mixing UseTexture/UseFragment between different versions
|
||||
// even though it should theoretically work (and we might do so in the future) for now we're overly strict.
|
||||
bool alreadyUsed = false;
|
||||
|
||||
//TODO: Check grab textures here and allow if it's grabbed. For now
|
||||
// SetRenderAttachment()
|
||||
// UseTexture(grab)
|
||||
// will work but not the other way around
|
||||
for (int i = 0; i < m_RenderPass.resourceReadLists[tex.handle.iType].Count; i++)
|
||||
{
|
||||
if (m_RenderPass.resourceReadLists[tex.handle.iType][i].index == tex.handle.index)
|
||||
{
|
||||
alreadyUsed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < m_RenderPass.resourceWriteLists[tex.handle.iType].Count; i++)
|
||||
{
|
||||
if (m_RenderPass.resourceWriteLists[tex.handle.iType][i].index == tex.handle.index)
|
||||
{
|
||||
alreadyUsed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (alreadyUsed)
|
||||
{
|
||||
var name = m_Resources.GetRenderGraphResourceName(tex.handle);
|
||||
throw new InvalidOperationException($"In pass '{m_RenderPass.name}' when trying to use resource '{name}' of type {tex.handle.type} at index {tex.handle.index} - " + RenderGraph.RenderGraphExceptionMessages.k_SetRenderAttachmentTextureAlreadyUsed);
|
||||
}
|
||||
|
||||
m_Resources.GetRenderTargetInfo(tex.handle, out var info);
|
||||
|
||||
bool isDepthFormat = GraphicsFormatUtility.IsDepthFormat(info.format);
|
||||
bool formatMismatch = isDepth != isDepthFormat;
|
||||
|
||||
if (formatMismatch)
|
||||
{
|
||||
var name = m_Resources.GetRenderGraphResourceName(tex.handle);
|
||||
var errorMsg = isDepth
|
||||
? RenderGraph.RenderGraphExceptionMessages.UseDepthWithColorFormat(info.format)
|
||||
: RenderGraph.RenderGraphExceptionMessages.k_SetRenderAttachmentOnDepthTexture;
|
||||
|
||||
throw new InvalidOperationException(
|
||||
$"In pass '{m_RenderPass.name}' when trying to use resource '{name}' " +
|
||||
$"of type {tex.handle.type} at index {tex.handle.index} - {errorMsg}");
|
||||
}
|
||||
|
||||
if (m_RenderGraph.renderTextureUVOriginStrategy == RenderTextureUVOriginStrategy.PropagateAttachmentOrientation)
|
||||
{
|
||||
var texResource = m_Resources.GetTextureResource(tex.handle);
|
||||
TextureResource checkTexResource = null;
|
||||
for (int i = 0; i < m_RenderPass.fragmentInputMaxIndex + 1; ++i)
|
||||
{
|
||||
if (m_RenderPass.fragmentInputAccess[i].textureHandle.IsValid())
|
||||
{
|
||||
ref readonly TextureHandle texCheck = ref m_RenderPass.fragmentInputAccess[i].textureHandle;
|
||||
checkTexResource = m_Resources.GetTextureResource(texCheck.handle);
|
||||
if (texResource.textureUVOrigin != TextureUVOriginSelection.Unknown && checkTexResource.textureUVOrigin != TextureUVOriginSelection.Unknown && texResource.textureUVOrigin != checkTexResource.textureUVOrigin)
|
||||
{
|
||||
var name = m_Resources.GetRenderGraphResourceName(tex.handle);
|
||||
var checkName = m_Resources.GetRenderGraphResourceName(texCheck.handle);
|
||||
throw new InvalidOperationException($"In pass '{m_RenderPass.name}' when trying to use resource '{name}' of type {tex.handle.type} at index {tex.handle.index} - " + RenderGraph.RenderGraphExceptionMessages.IncompatibleTextureUVOrigin(texResource.textureUVOrigin, "input", checkName, texCheck.handle.type, texCheck.handle.index, checkTexResource.textureUVOrigin));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < m_RenderPass.colorBufferMaxIndex + 1; ++i)
|
||||
{
|
||||
if (m_RenderPass.colorBufferAccess[i].textureHandle.IsValid())
|
||||
{
|
||||
ref readonly TextureHandle texCheck = ref m_RenderPass.colorBufferAccess[i].textureHandle;
|
||||
checkTexResource = m_Resources.GetTextureResource(texCheck.handle);
|
||||
if (texResource.textureUVOrigin != TextureUVOriginSelection.Unknown && checkTexResource.textureUVOrigin != TextureUVOriginSelection.Unknown && texResource.textureUVOrigin != checkTexResource.textureUVOrigin)
|
||||
{
|
||||
var name = m_Resources.GetRenderGraphResourceName(tex.handle);
|
||||
var checkName = m_Resources.GetRenderGraphResourceName(texCheck.handle);
|
||||
throw new InvalidOperationException($"In pass '{m_RenderPass.name}' when trying to use resource '{name}' of type {tex.handle.type} at index {tex.handle.index} - " + RenderGraph.RenderGraphExceptionMessages.IncompatibleTextureUVOrigin(texResource.textureUVOrigin, "render", checkName, texCheck.handle.type, texCheck.handle.index, checkTexResource.textureUVOrigin));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isDepth && m_RenderPass.depthAccess.textureHandle.IsValid())
|
||||
{
|
||||
TextureHandle texCheck = m_RenderPass.depthAccess.textureHandle;
|
||||
checkTexResource = m_Resources.GetTextureResource(texCheck.handle);
|
||||
if (texResource.textureUVOrigin != TextureUVOriginSelection.Unknown && checkTexResource.textureUVOrigin != TextureUVOriginSelection.Unknown && texResource.textureUVOrigin != checkTexResource.textureUVOrigin)
|
||||
{
|
||||
var name = m_Resources.GetRenderGraphResourceName(tex.handle);
|
||||
var checkName = m_Resources.GetRenderGraphResourceName(texCheck.handle);
|
||||
throw new InvalidOperationException($"In pass '{m_RenderPass.name}' when trying to use resource '{name}' of type {tex.handle.type} at index {tex.handle.index} - " + RenderGraph.RenderGraphExceptionMessages.IncompatibleTextureUVOrigin(texResource.textureUVOrigin, "depth", checkName, texCheck.handle.type, texCheck.handle.index, checkTexResource.textureUVOrigin));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var globalTex in m_RenderPass.setGlobalsList)
|
||||
{
|
||||
if (globalTex.Item1.handle.index == tex.handle.index)
|
||||
{
|
||||
var name = m_Resources.GetRenderGraphResourceName(tex.handle);
|
||||
throw new InvalidOperationException($"In pass '{m_RenderPass.name}' when trying to use resource '{name}' of type {tex.handle.type} at index {tex.handle.index} - " + RenderGraph.RenderGraphExceptionMessages.k_SetRenderAttachmentOnGlobalTexture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetRenderAttachment(TextureHandle tex, int index, AccessFlags flags, int mipLevel, int depthSlice)
|
||||
{
|
||||
CheckUseFragment(tex, false);
|
||||
var versionedTextureHandle = new TextureHandle(UseResource(tex.handle, flags));
|
||||
m_RenderPass.SetColorBufferRaw(versionedTextureHandle, index, flags, mipLevel, depthSlice);
|
||||
}
|
||||
|
||||
public void SetInputAttachment(TextureHandle tex, int index, AccessFlags flags, int mipLevel, int depthSlice)
|
||||
{
|
||||
CheckFrameBufferFetchEmulationIsSupported(tex);
|
||||
|
||||
CheckUseFragment(tex, false);
|
||||
var versionedTextureHandle = new TextureHandle(UseResource(tex.handle, flags));
|
||||
m_RenderPass.SetFragmentInputRaw(versionedTextureHandle, index, flags, mipLevel, depthSlice);
|
||||
}
|
||||
|
||||
public void SetRenderAttachmentDepth(TextureHandle tex, AccessFlags flags, int mipLevel, int depthSlice)
|
||||
{
|
||||
CheckUseFragment(tex, true);
|
||||
var versionedTextureHandle = new TextureHandle(UseResource(tex.handle, flags));
|
||||
m_RenderPass.SetDepthBufferRaw(versionedTextureHandle, flags, mipLevel, depthSlice);
|
||||
}
|
||||
|
||||
public TextureHandle SetRandomAccessAttachment(TextureHandle input, int index, AccessFlags flags = AccessFlags.Read)
|
||||
{
|
||||
CheckNotUseFragment(input);
|
||||
ResourceHandle result = UseResource(input.handle, flags);
|
||||
m_RenderPass.SetRandomWriteResourceRaw(result, index, false, flags);
|
||||
return input;
|
||||
}
|
||||
|
||||
public void SetShadingRateImageAttachment(in TextureHandle tex)
|
||||
{
|
||||
CheckNotUseFragment(tex);
|
||||
var versionedTextureHandle = new TextureHandle(UseResource(tex.handle, AccessFlags.Read));
|
||||
m_RenderPass.SetShadingRateImageRaw(versionedTextureHandle);
|
||||
}
|
||||
|
||||
public BufferHandle UseBufferRandomAccess(BufferHandle input, int index, AccessFlags flags = AccessFlags.Read)
|
||||
{
|
||||
var h = UseBuffer(input, flags);
|
||||
m_RenderPass.SetRandomWriteResourceRaw(h.handle, index, true, flags);
|
||||
return input;
|
||||
}
|
||||
|
||||
public BufferHandle UseBufferRandomAccess(BufferHandle input, int index, bool preserveCounterValue, AccessFlags flags = AccessFlags.Read)
|
||||
{
|
||||
var h = UseBuffer(input, flags);
|
||||
m_RenderPass.SetRandomWriteResourceRaw(h.handle, index, preserveCounterValue, flags);
|
||||
return input;
|
||||
}
|
||||
|
||||
public void SetRenderFunc<PassData>(BaseRenderFunc<PassData, ComputeGraphContext> renderFunc) where PassData : class, new()
|
||||
{
|
||||
((ComputeRenderGraphPass<PassData>)m_RenderPass).renderFunc = renderFunc;
|
||||
}
|
||||
|
||||
public void SetRenderFunc<PassData>(BaseRenderFunc<PassData, RasterGraphContext> renderFunc) where PassData : class, new()
|
||||
{
|
||||
((RasterRenderGraphPass<PassData>)m_RenderPass).renderFunc = renderFunc;
|
||||
}
|
||||
|
||||
public void SetRenderFunc<PassData>(BaseRenderFunc<PassData, UnsafeGraphContext> renderFunc) where PassData : class, new()
|
||||
{
|
||||
((UnsafeRenderGraphPass<PassData>)m_RenderPass).renderFunc = renderFunc;
|
||||
}
|
||||
|
||||
public void UseRendererList(in RendererListHandle input)
|
||||
{
|
||||
m_RenderPass.UseRendererList(input);
|
||||
}
|
||||
|
||||
[Conditional("DEVELOPMENT_BUILD"), Conditional("UNITY_EDITOR")]
|
||||
void CheckResource(in ResourceHandle res, bool checkTransientReadWrite = false)
|
||||
{
|
||||
if (RenderGraph.enableValidityChecks)
|
||||
{
|
||||
if (res.IsValid())
|
||||
{
|
||||
int transientIndex = m_Resources.GetRenderGraphResourceTransientIndex(res);
|
||||
// We have dontCheckTransientReadWrite here because users may want to use UseColorBuffer/UseDepthBuffer API to benefit from render target auto binding. In this case we don't want to raise the error.
|
||||
if (transientIndex == m_RenderPass.index && checkTransientReadWrite)
|
||||
{
|
||||
var name = m_Resources.GetRenderGraphResourceName(res);
|
||||
Debug.LogError($"In pass '{m_RenderPass.name}' when trying to use resource '{name}' of type {res.type} at index {res.index} - " + RenderGraph.RenderGraphExceptionMessages.k_ReadWriteTransient);
|
||||
}
|
||||
|
||||
if (transientIndex != -1 && transientIndex != m_RenderPass.index)
|
||||
{
|
||||
var name = m_Resources.GetRenderGraphResourceName(res);
|
||||
throw new ArgumentException(
|
||||
$"In pass '{m_RenderPass.name}' when trying to use resource '{name}' of type {res.type} at index {res.index} - " +
|
||||
RenderGraph.RenderGraphExceptionMessages.UseTransientTextureInWrongPass(transientIndex));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var name = m_Resources.GetRenderGraphResourceName(res);
|
||||
throw new Exception($"In pass '{m_RenderPass.name}' when trying to use resource '{name}' of type {res.type} at index {res.index} - " + RenderGraph.RenderGraphExceptionMessages.k_InvalidResource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Conditional("DEVELOPMENT_BUILD"), Conditional("UNITY_EDITOR")]
|
||||
void CheckFrameBufferFetchEmulationIsSupported(in TextureHandle tex)
|
||||
{
|
||||
if (enableValidityChecks)
|
||||
{
|
||||
if (!Util.RenderGraphUtils.IsFramebufferFetchEmulationMSAASupportedOnCurrentPlatform())
|
||||
{
|
||||
var sourceInfo = m_RenderGraph.GetRenderTargetInfo(tex);
|
||||
if (sourceInfo.bindMS)
|
||||
throw new InvalidOperationException($"This API is not supported with MSAA attachments on the current platform: {SystemInfo.graphicsDeviceType}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetShadingRateFragmentSize(ShadingRateFragmentSize shadingRateFragmentSize)
|
||||
{
|
||||
m_RenderPass.SetShadingRateFragmentSize(shadingRateFragmentSize);
|
||||
}
|
||||
|
||||
public void SetShadingRateCombiner(ShadingRateCombinerStage stage, ShadingRateCombiner combiner)
|
||||
{
|
||||
m_RenderPass.SetShadingRateCombiner(stage, combiner);
|
||||
}
|
||||
|
||||
public void SetExtendedFeatureFlags(ExtendedFeatureFlags extendedFeatureFlags)
|
||||
{
|
||||
m_RenderPass.SetExtendedFeatureFlags(extendedFeatureFlags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
752
Ghost.RenderGraph.Concept/Unity/RenderGraphPass.cs
Normal file
752
Ghost.RenderGraph.Concept/Unity/RenderGraphPass.cs
Normal file
@@ -0,0 +1,752 @@
|
||||
#if false
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace UnityEngine.Rendering.RenderGraphModule
|
||||
{
|
||||
[DebuggerDisplay("RenderPass: {name} (Index:{index} Async:{enableAsyncCompute})")]
|
||||
abstract class RenderGraphPass
|
||||
{
|
||||
public abstract void Execute(InternalRenderGraphContext renderGraphContext);
|
||||
public abstract void Release(RenderGraphObjectPool pool);
|
||||
public abstract bool HasRenderFunc();
|
||||
public abstract int GetRenderFuncHash();
|
||||
|
||||
public string name { get; protected set; }
|
||||
public int index { get; protected set; }
|
||||
public RenderGraphPassType type { get; internal set; }
|
||||
public ProfilingSampler customSampler { get; protected set; }
|
||||
public bool enableAsyncCompute { get; protected set; }
|
||||
public bool allowPassCulling { get; protected set; }
|
||||
public bool allowGlobalState { get; protected set; }
|
||||
public bool enableFoveatedRasterization { get; protected set; }
|
||||
public ExtendedFeatureFlags extendedFeatureFlags { get; protected set; }
|
||||
|
||||
// Before using the AccessFlags use resourceHandle.isValid()
|
||||
// to make sure that the data in the colorBuffer/fragmentInput/randomAccessResource buffers are up to date
|
||||
public TextureAccess depthAccess { get; protected set; }
|
||||
|
||||
public TextureAccess[] colorBufferAccess { get; protected set; } = new TextureAccess[RenderGraph.kMaxMRTCount];
|
||||
public int colorBufferMaxIndex { get; protected set; } = -1;
|
||||
|
||||
public bool hasShadingRateImage { get; protected set; }
|
||||
public TextureAccess shadingRateAccess { get; protected set; }
|
||||
|
||||
public bool hasShadingRateStates { get; protected set; }
|
||||
public ShadingRateFragmentSize shadingRateFragmentSize { get; protected set; }
|
||||
public ShadingRateCombiner primitiveShadingRateCombiner { get; protected set; }
|
||||
public ShadingRateCombiner fragmentShadingRateCombiner { get; protected set; }
|
||||
|
||||
// Used by native pass compiler only
|
||||
public TextureAccess[] fragmentInputAccess { get; protected set; } = new TextureAccess[RenderGraph.kMaxMRTCount];
|
||||
public int fragmentInputMaxIndex { get; protected set; } = -1;
|
||||
|
||||
public struct RandomWriteResourceInfo
|
||||
{
|
||||
public ResourceHandle h;
|
||||
public bool preserveCounterValue;
|
||||
}
|
||||
|
||||
// This list can contain both texture and buffer resources based on their binding index.
|
||||
public RandomWriteResourceInfo[] randomAccessResource { get; protected set; } = new RandomWriteResourceInfo[RenderGraph.kMaxMRTCount];
|
||||
public int randomAccessResourceMaxIndex { get; protected set; } = -1;
|
||||
|
||||
public bool generateDebugData { get; protected set; }
|
||||
|
||||
public bool allowRendererListCulling { get; protected set; }
|
||||
|
||||
public List<ResourceHandle>[] resourceReadLists = new List<ResourceHandle>[(int)RenderGraphResourceType.Count];
|
||||
public List<ResourceHandle>[] resourceWriteLists = new List<ResourceHandle>[(int)RenderGraphResourceType.Count];
|
||||
public List<ResourceHandle>[] transientResourceList = new List<ResourceHandle>[(int)RenderGraphResourceType.Count];
|
||||
|
||||
public List<RendererListHandle> usedRendererListList = new List<RendererListHandle>();
|
||||
|
||||
public List<ValueTuple<TextureHandle, int>> setGlobalsList = new List<ValueTuple<TextureHandle, int>>();
|
||||
public bool useAllGlobalTextures;
|
||||
|
||||
public List<ResourceHandle> implicitReadsList = new List<ResourceHandle>();
|
||||
|
||||
#if UNITY_EDITOR || DEVELOPMENT_BUILD
|
||||
public RenderGraph.DebugData.PassScriptInfo debugScriptInfo { get; set; }
|
||||
#endif
|
||||
|
||||
public RenderGraphPass()
|
||||
{
|
||||
for (int i = 0; i < (int)RenderGraphResourceType.Count; ++i)
|
||||
{
|
||||
resourceReadLists[i] = new List<ResourceHandle>();
|
||||
resourceWriteLists[i] = new List<ResourceHandle>();
|
||||
transientResourceList[i] = new List<ResourceHandle>();
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
name = "";
|
||||
index = -1;
|
||||
customSampler = null;
|
||||
for (int i = 0; i < (int)RenderGraphResourceType.Count; ++i)
|
||||
{
|
||||
resourceReadLists[i].Clear();
|
||||
resourceWriteLists[i].Clear();
|
||||
transientResourceList[i].Clear();
|
||||
}
|
||||
|
||||
usedRendererListList.Clear();
|
||||
setGlobalsList.Clear();
|
||||
useAllGlobalTextures = false;
|
||||
implicitReadsList.Clear();
|
||||
enableAsyncCompute = false;
|
||||
allowPassCulling = true;
|
||||
allowRendererListCulling = true;
|
||||
allowGlobalState = false;
|
||||
enableFoveatedRasterization = false;
|
||||
generateDebugData = true;
|
||||
|
||||
// Invalidate the buffers without clearing them, as it is too costly
|
||||
// Use IsValid() to make sure that the data in the colorBuffer/fragmentInput/randomAccessResource buffers are up to date
|
||||
colorBufferMaxIndex = -1;
|
||||
fragmentInputMaxIndex = -1;
|
||||
randomAccessResourceMaxIndex = -1;
|
||||
|
||||
// We do not need to clear colorBufferAccess and fragmentInputAccess as we have the colorBufferMaxIndex and fragmentInputMaxIndex
|
||||
// which are reset above so we only clear depthAccess here.
|
||||
depthAccess = default(TextureAccess);
|
||||
|
||||
hasShadingRateImage = false;
|
||||
hasShadingRateStates = false;
|
||||
shadingRateFragmentSize = ShadingRateFragmentSize.FragmentSize1x1;
|
||||
primitiveShadingRateCombiner = ShadingRateCombiner.Keep;
|
||||
fragmentShadingRateCombiner = ShadingRateCombiner.Keep;
|
||||
|
||||
// Invalidate ExtendedFeatureFlags
|
||||
extendedFeatureFlags = ExtendedFeatureFlags.None;
|
||||
}
|
||||
|
||||
// Check if the pass has any render targets set-up
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool HasRenderAttachments()
|
||||
{
|
||||
return depthAccess.textureHandle.IsValid() || colorBufferAccess[0].textureHandle.IsValid() || colorBufferMaxIndex > 0;
|
||||
}
|
||||
|
||||
// Checks if the resource is involved in this pass
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool IsTransient(in ResourceHandle res)
|
||||
{
|
||||
// Versioning doesn't matter much for transient resources as they are only used within a single pass
|
||||
for (int i = 0; i < transientResourceList[res.iType].Count; i++)
|
||||
{
|
||||
if (transientResourceList[res.iType][i].index == res.index)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool IsWritten(in ResourceHandle res)
|
||||
{
|
||||
// You can only ever write to the latest version so we ignore it when looking in the list
|
||||
for (int i = 0; i < resourceWriteLists[res.iType].Count; i++)
|
||||
{
|
||||
if (resourceWriteLists[res.iType][i].index == res.index)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool IsRead(in ResourceHandle res)
|
||||
{
|
||||
if (res.IsVersioned)
|
||||
{
|
||||
return resourceReadLists[res.iType].Contains(res);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Just look if we are reading any version of this texture.
|
||||
// Note that in theory this pass could read from several versions of the same texture
|
||||
// e.g. ColorBuffer,v3 and ColorBuffer,v5 so this check is always conservative
|
||||
for (int i = 0; i < resourceReadLists[res.iType].Count; i++)
|
||||
{
|
||||
if (resourceReadLists[res.iType][i].index == res.index)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool IsAttachment(in TextureHandle res)
|
||||
{
|
||||
// We ignore the version when checking, if any version is used it is considered a match
|
||||
|
||||
if (depthAccess.textureHandle.IsValid() && depthAccess.textureHandle.handle.index == res.handle.index) return true;
|
||||
for (int i = 0; i < colorBufferAccess.Length; i++)
|
||||
{
|
||||
if (colorBufferAccess[i].textureHandle.IsValid() && colorBufferAccess[i].textureHandle.handle.index == res.handle.index) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void AddResourceWrite(in ResourceHandle res)
|
||||
{
|
||||
resourceWriteLists[res.iType].Add(res);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void AddResourceRead(in ResourceHandle res)
|
||||
{
|
||||
resourceReadLists[res.iType].Add(res);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void AddTransientResource(in ResourceHandle res)
|
||||
{
|
||||
transientResourceList[res.iType].Add(res);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void UseRendererList(in RendererListHandle rendererList)
|
||||
{
|
||||
usedRendererListList.Add(rendererList);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void EnableAsyncCompute(bool value)
|
||||
{
|
||||
enableAsyncCompute = value;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void AllowPassCulling(bool value)
|
||||
{
|
||||
allowPassCulling = value;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void EnableFoveatedRasterization(bool value)
|
||||
{
|
||||
enableFoveatedRasterization = value;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void AllowRendererListCulling(bool value)
|
||||
{
|
||||
allowRendererListCulling = value;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void AllowGlobalState(bool value)
|
||||
{
|
||||
allowGlobalState = value;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void GenerateDebugData(bool value)
|
||||
{
|
||||
generateDebugData = value;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetColorBuffer(in TextureHandle resource, int index)
|
||||
{
|
||||
Debug.Assert(index < RenderGraph.kMaxMRTCount && index >= 0);
|
||||
colorBufferMaxIndex = Math.Max(colorBufferMaxIndex, index);
|
||||
colorBufferAccess[index] = new TextureAccess(colorBufferAccess[index], resource);
|
||||
AddResourceWrite(resource.handle);
|
||||
}
|
||||
|
||||
// Sets up the color buffer for this pass but not any resource Read/Writes for it
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetColorBufferRaw(in TextureHandle resource, int index, AccessFlags accessFlags, int mipLevel, int depthSlice)
|
||||
{
|
||||
Debug.Assert(index < RenderGraph.kMaxMRTCount && index >= 0);
|
||||
if (colorBufferAccess[index].textureHandle.handle.Equals(resource.handle) || !colorBufferAccess[index].textureHandle.IsValid())
|
||||
{
|
||||
colorBufferMaxIndex = Math.Max(colorBufferMaxIndex, index);
|
||||
colorBufferAccess[index] = new TextureAccess(resource, accessFlags, mipLevel, depthSlice);
|
||||
}
|
||||
else
|
||||
{
|
||||
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
||||
// You tried to do SetRenderAttachment(tex1, 1, ..); SetRenderAttachment(tex2, 1, ..); that is not valid for different textures on the same index
|
||||
throw new InvalidOperationException(
|
||||
$"In pass '{name}' when trying to call SetRenderAttachment with resource of type {resource.handle.type} at index {index} - " +
|
||||
RenderGraph.RenderGraphExceptionMessages.k_MoreThanOneResourceForMRTIndex);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// Sets up the color buffer for this pass but not any resource Read/Writes for it
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetFragmentInputRaw(in TextureHandle resource, int index, AccessFlags accessFlags, int mipLevel, int depthSlice)
|
||||
{
|
||||
Debug.Assert(index < RenderGraph.kMaxMRTCount && index >= 0);
|
||||
if (fragmentInputAccess[index].textureHandle.handle.Equals(resource.handle) || !fragmentInputAccess[index].textureHandle.IsValid())
|
||||
{
|
||||
fragmentInputMaxIndex = Math.Max(fragmentInputMaxIndex, index);
|
||||
fragmentInputAccess[index] = new TextureAccess(resource, accessFlags, mipLevel, depthSlice);
|
||||
}
|
||||
else
|
||||
{
|
||||
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
||||
// You tried to do SetRenderAttachment(tex1, 1, ..); SetRenderAttachment(tex2, 1, ..); that is not valid for different textures on the same index
|
||||
throw new InvalidOperationException(
|
||||
$"In pass '{name}' when trying to call SetInputAttachment with resource of type {resource.handle.type} at index {index} - " +
|
||||
RenderGraph.RenderGraphExceptionMessages.k_MoreThanOneTextureForFragInputIndex);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// Sets up the color buffer for this pass but not any resource Read/Writes for it
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetRandomWriteResourceRaw(in ResourceHandle resource, int index, bool preserveCounterValue, AccessFlags accessFlags)
|
||||
{
|
||||
Debug.Assert(index < RenderGraph.kMaxMRTCount && index >= 0);
|
||||
if (randomAccessResource[index].h.Equals(resource) || !randomAccessResource[index].h.IsValid())
|
||||
{
|
||||
randomAccessResourceMaxIndex = Math.Max(randomAccessResourceMaxIndex, index);
|
||||
ref var info = ref randomAccessResource[index];
|
||||
info.h = resource;
|
||||
info.preserveCounterValue = preserveCounterValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
// You tried to do SetRenderAttachment(tex1, 1, ..); SetRenderAttachment(tex2, 1, ..); that is not valid for different textures on the same index
|
||||
throw new InvalidOperationException(
|
||||
$"In pass '{name}' when trying to call SetRandomAccessAttachment/UseBufferRandomAccess with resource of type {resource.type} at index {index} - " +
|
||||
RenderGraph.RenderGraphExceptionMessages.k_MoreThanOneTextureRandomWriteInputIndex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetDepthBuffer(in TextureHandle resource, DepthAccess flags)
|
||||
{
|
||||
depthAccess = new TextureAccess(resource, (AccessFlags)flags, 0, 0);
|
||||
if ((flags & DepthAccess.Read) != 0)
|
||||
AddResourceRead(resource.handle);
|
||||
if ((flags & DepthAccess.Write) != 0)
|
||||
AddResourceWrite(resource.handle);
|
||||
}
|
||||
|
||||
// Sets up the depth buffer for this pass but not any resource Read/Writes for it
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetDepthBufferRaw(in TextureHandle resource, AccessFlags accessFlags, int mipLevel, int depthSlice)
|
||||
{
|
||||
// If no depth buffer yet or if it is the same one as the previous one, allow the call otherwise log an error.
|
||||
if (depthAccess.textureHandle.handle.Equals(resource.handle) || !depthAccess.textureHandle.IsValid())
|
||||
{
|
||||
depthAccess = new TextureAccess(resource, accessFlags, mipLevel, depthSlice);
|
||||
}
|
||||
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"In pass '{name}' when trying to call SetRenderAttachmentDepth with resource of type {resource.handle.type} at index {index} - " +
|
||||
RenderGraph.RenderGraphExceptionMessages.k_MultipleDepthTextures);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Here we want to keep computation to a minimum and only hash what will influence NRP compiler: Pass merging, load/store actions etc.
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
void ComputeTextureHash(ref HashFNV1A32 generator, in ResourceHandle handle, RenderGraphResourceRegistry resources)
|
||||
{
|
||||
if (handle.index == 0)
|
||||
return;
|
||||
|
||||
if (resources.IsRenderGraphResourceImported(handle))
|
||||
{
|
||||
var res = resources.GetTextureResource(handle);
|
||||
var graphicsResource = res.graphicsResource;
|
||||
ref readonly var desc = ref res.desc;
|
||||
|
||||
var externalTexture = graphicsResource.externalTexture;
|
||||
if (externalTexture != null) // External texture
|
||||
{
|
||||
generator.Append((int) externalTexture.graphicsFormat);
|
||||
generator.Append((int) externalTexture.dimension);
|
||||
generator.Append(externalTexture.width);
|
||||
generator.Append(externalTexture.height);
|
||||
if (externalTexture is RenderTexture externalRT)
|
||||
generator.Append(externalRT.antiAliasing);
|
||||
}
|
||||
else if (graphicsResource.rt != null) // Regular RTHandle
|
||||
{
|
||||
var rt = graphicsResource.rt;
|
||||
generator.Append((int) rt.graphicsFormat);
|
||||
generator.Append((int) rt.dimension);
|
||||
generator.Append(rt.antiAliasing);
|
||||
if (graphicsResource.useScaling)
|
||||
if (graphicsResource.scaleFunc != null)
|
||||
generator.Append(DelegateHashCodeUtils.GetFuncHashCode(graphicsResource.scaleFunc));
|
||||
else
|
||||
generator.Append(graphicsResource.scaleFactor);
|
||||
else
|
||||
{
|
||||
generator.Append(rt.width);
|
||||
generator.Append(rt.height);
|
||||
}
|
||||
}
|
||||
else if (graphicsResource.nameID != default) // External RTI
|
||||
{
|
||||
// The only info we have is from the provided desc upon importing.
|
||||
generator.Append((int) desc.format);
|
||||
generator.Append((int) desc.dimension);
|
||||
generator.Append((int) desc.msaaSamples);
|
||||
generator.Append(desc.width);
|
||||
generator.Append(desc.height);
|
||||
}
|
||||
|
||||
// Add the clear/discard buffer flags to the hash (used in all the cases above)
|
||||
generator.Append(desc.clearBuffer);
|
||||
generator.Append(desc.discardBuffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
ref readonly var desc = ref resources.GetTextureResourceDesc(handle);
|
||||
generator.Append((int) desc.format);
|
||||
generator.Append((int) desc.dimension);
|
||||
generator.Append((int) desc.msaaSamples);
|
||||
generator.Append(desc.clearBuffer);
|
||||
generator.Append(desc.discardBuffer);
|
||||
switch (desc.sizeMode)
|
||||
{
|
||||
case TextureSizeMode.Explicit:
|
||||
generator.Append(desc.width);
|
||||
generator.Append(desc.height);
|
||||
break;
|
||||
case TextureSizeMode.Scale:
|
||||
generator.Append(desc.scale);
|
||||
break;
|
||||
case TextureSizeMode.Functor:
|
||||
generator.Append(DelegateHashCodeUtils.GetFuncHashCode(desc.func));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
static void ComputeHashForTextureAccess(ref HashFNV1A32 generator, in ResourceHandle handle, in TextureAccess textureAccess)
|
||||
{
|
||||
generator.Append(handle.index);
|
||||
generator.Append((int) textureAccess.flags);
|
||||
generator.Append(textureAccess.mipLevel);
|
||||
generator.Append(textureAccess.depthSlice);
|
||||
}
|
||||
|
||||
// This function is performance sensitive.
|
||||
// Avoid mass function calls to get the hashCode and compute locally instead.
|
||||
public void ComputeHash(ref HashFNV1A32 generator, RenderGraphResourceRegistry resources)
|
||||
{
|
||||
generator.Append((int) type);
|
||||
generator.Append(enableAsyncCompute);
|
||||
generator.Append(allowPassCulling);
|
||||
generator.Append(allowGlobalState);
|
||||
generator.Append(enableFoveatedRasterization);
|
||||
generator.Append(extendedFeatureFlags);
|
||||
|
||||
var depthHandle = depthAccess.textureHandle.handle;
|
||||
if (depthHandle.IsValid())
|
||||
{
|
||||
ComputeTextureHash(ref generator, depthHandle, resources);
|
||||
ComputeHashForTextureAccess(ref generator, depthHandle, depthAccess);
|
||||
}
|
||||
|
||||
for (int i = 0; i < colorBufferMaxIndex + 1; ++i)
|
||||
{
|
||||
var colorBufferAccessElement = colorBufferAccess[i];
|
||||
var handle = colorBufferAccessElement.textureHandle.handle;
|
||||
if (!handle.IsValid())
|
||||
continue;
|
||||
|
||||
ComputeTextureHash(ref generator, handle, resources);
|
||||
ComputeHashForTextureAccess(ref generator, handle, colorBufferAccessElement);
|
||||
}
|
||||
|
||||
generator.Append(colorBufferMaxIndex);
|
||||
|
||||
generator.Append(hasShadingRateImage);
|
||||
if (hasShadingRateImage)
|
||||
{
|
||||
var handle = shadingRateAccess.textureHandle.handle;
|
||||
if (handle.IsValid())
|
||||
{
|
||||
ComputeTextureHash(ref generator, handle, resources);
|
||||
ComputeHashForTextureAccess(ref generator, handle, shadingRateAccess);
|
||||
}
|
||||
}
|
||||
|
||||
generator.Append(hasShadingRateStates);
|
||||
generator.Append((int)shadingRateFragmentSize);
|
||||
generator.Append((int)primitiveShadingRateCombiner);
|
||||
generator.Append((int)fragmentShadingRateCombiner);
|
||||
|
||||
for (int i = 0; i < fragmentInputMaxIndex + 1; ++i)
|
||||
{
|
||||
var fragmentInputAccessElement = fragmentInputAccess[i];
|
||||
var handle = fragmentInputAccessElement.textureHandle.handle;
|
||||
if (!handle.IsValid())
|
||||
continue;
|
||||
|
||||
ComputeTextureHash(ref generator, handle, resources);
|
||||
ComputeHashForTextureAccess(ref generator, handle, fragmentInputAccessElement);
|
||||
}
|
||||
|
||||
for (int i = 0; i < randomAccessResourceMaxIndex + 1; ++i)
|
||||
{
|
||||
var rar = randomAccessResource[i];
|
||||
if (!rar.h.IsValid())
|
||||
continue;
|
||||
|
||||
generator.Append(rar.h.index);
|
||||
generator.Append(rar.preserveCounterValue);
|
||||
}
|
||||
generator.Append(randomAccessResourceMaxIndex);
|
||||
generator.Append(fragmentInputMaxIndex);
|
||||
generator.Append(generateDebugData);
|
||||
generator.Append(allowRendererListCulling);
|
||||
|
||||
for (int resType = 0; resType < (int)RenderGraphResourceType.Count; resType++)
|
||||
{
|
||||
var resourceReads = resourceReadLists[resType];
|
||||
var resourceReadsCount = resourceReads.Count;
|
||||
for (int i = 0; i < resourceReadsCount; ++i)
|
||||
generator.Append(resourceReads[i].index);
|
||||
|
||||
var resourceWrites = resourceWriteLists[resType];
|
||||
var resourceWritesCount = resourceWrites.Count;
|
||||
for (int i = 0; i < resourceWritesCount; ++i)
|
||||
generator.Append(resourceWrites[i].index);
|
||||
|
||||
var resourceTransient = transientResourceList[resType];
|
||||
var resourceTransientCount = resourceTransient.Count;
|
||||
for (int i = 0; i < resourceTransientCount; ++i)
|
||||
generator.Append(resourceTransient[i].index);
|
||||
}
|
||||
|
||||
var usedRendererListListCount = usedRendererListList.Count;
|
||||
for (int i = 0; i < usedRendererListListCount; ++i)
|
||||
generator.Append(usedRendererListList[i].handle);
|
||||
|
||||
var setGlobalsListCount = setGlobalsList.Count;
|
||||
for (int i = 0; i < setGlobalsListCount; ++i)
|
||||
{
|
||||
var global = setGlobalsList[i];
|
||||
generator.Append(global.Item1.handle.index);
|
||||
generator.Append(global.Item2);
|
||||
}
|
||||
generator.Append(useAllGlobalTextures);
|
||||
|
||||
var implicitReadsListCount = implicitReadsList.Count;
|
||||
for (int i = 0; i < implicitReadsListCount; ++i)
|
||||
generator.Append(implicitReadsList[i].index);
|
||||
|
||||
generator.Append(GetRenderFuncHash());
|
||||
}
|
||||
|
||||
public void SetShadingRateImageRaw(in TextureHandle shadingRateImage)
|
||||
{
|
||||
if (ShadingRateInfo.supportsPerImageTile)
|
||||
{
|
||||
hasShadingRateImage = true;
|
||||
// shading rate image access flag is always read, only 1 mip and 1 slice
|
||||
shadingRateAccess = new TextureAccess(shadingRateImage, AccessFlags.Read, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetShadingRateImage(in TextureHandle shadingRateImage, AccessFlags accessFlags, int mipLevel, int depthSlice)
|
||||
{
|
||||
if (ShadingRateInfo.supportsPerImageTile)
|
||||
{
|
||||
hasShadingRateImage = true;
|
||||
shadingRateAccess = new TextureAccess(shadingRateImage, accessFlags, mipLevel, depthSlice);
|
||||
AddResourceRead(shadingRateAccess.textureHandle.handle);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetShadingRateFragmentSize(ShadingRateFragmentSize shadingRateFragmentSize)
|
||||
{
|
||||
if (ShadingRateInfo.supportsPerDrawCall)
|
||||
{
|
||||
hasShadingRateStates = true;
|
||||
this.shadingRateFragmentSize = shadingRateFragmentSize;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetShadingRateCombiner(ShadingRateCombinerStage stage, ShadingRateCombiner combiner)
|
||||
{
|
||||
if (ShadingRateInfo.supportsPerImageTile)
|
||||
{
|
||||
switch (stage)
|
||||
{
|
||||
case ShadingRateCombinerStage.Primitive:
|
||||
hasShadingRateStates = true;
|
||||
primitiveShadingRateCombiner = combiner;
|
||||
break;
|
||||
|
||||
case ShadingRateCombinerStage.Fragment:
|
||||
hasShadingRateStates = true;
|
||||
fragmentShadingRateCombiner = combiner;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetExtendedFeatureFlags(ExtendedFeatureFlags value)
|
||||
{
|
||||
extendedFeatureFlags |= value;
|
||||
}
|
||||
}
|
||||
|
||||
// This used to have an extra generic argument 'RenderGraphContext' abstracting the context and avoiding
|
||||
// the RenderGraphPass/ComputeRenderGraphPass/RasterRenderGraphPass/UnsafeRenderGraphPass classes below
|
||||
// but this confuses IL2CPP and causes garbage when boxing the context created (even though they are structs)
|
||||
[DebuggerDisplay("RenderPass: {name} (Index:{index} Async:{enableAsyncCompute})")]
|
||||
internal abstract class BaseRenderGraphPass<PassData, TRenderGraphContext> : RenderGraphPass
|
||||
where PassData : class, new()
|
||||
{
|
||||
internal PassData data;
|
||||
internal BaseRenderFunc<PassData, TRenderGraphContext> renderFunc;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Initialize(int passIndex, PassData passData, string passName, RenderGraphPassType passType, ProfilingSampler sampler)
|
||||
{
|
||||
Clear();
|
||||
index = passIndex;
|
||||
data = passData;
|
||||
name = passName;
|
||||
type = passType;
|
||||
customSampler = sampler;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override void Release(RenderGraphObjectPool pool)
|
||||
{
|
||||
pool.Release(data);
|
||||
data = null;
|
||||
renderFunc = null;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override bool HasRenderFunc()
|
||||
{
|
||||
return renderFunc != null;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override int GetRenderFuncHash()
|
||||
{
|
||||
return renderFunc != null ? DelegateHashCodeUtils.GetFuncHashCode(renderFunc) : 0;
|
||||
}
|
||||
}
|
||||
|
||||
[DebuggerDisplay("RenderPass: {name} (Index:{index} Async:{enableAsyncCompute})")]
|
||||
[Obsolete("RenderGraphPass is deprecated, use RasterRenderGraphPass/ComputeRenderGraphPass/UnsafeRenderGraphPass instead.")]
|
||||
internal sealed class RenderGraphPass<PassData> : BaseRenderGraphPass<PassData, RenderGraphContext>
|
||||
where PassData : class, new()
|
||||
{
|
||||
internal static RenderGraphContext c = new RenderGraphContext();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override void Execute(InternalRenderGraphContext renderGraphContext)
|
||||
{
|
||||
c.FromInternalContext(renderGraphContext);
|
||||
renderFunc(data, c);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override void Release(RenderGraphObjectPool pool)
|
||||
{
|
||||
base.Release(pool);
|
||||
|
||||
// We need to do the release from here because we need the final type.
|
||||
pool.Release(this);
|
||||
}
|
||||
}
|
||||
|
||||
[DebuggerDisplay("RenderPass: {name} (Index:{index} Async:{enableAsyncCompute})")]
|
||||
internal sealed class ComputeRenderGraphPass<PassData> : BaseRenderGraphPass<PassData, ComputeGraphContext>
|
||||
where PassData : class, new()
|
||||
{
|
||||
internal static ComputeGraphContext c = new ComputeGraphContext();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override void Execute(InternalRenderGraphContext renderGraphContext)
|
||||
{
|
||||
c.FromInternalContext(renderGraphContext);
|
||||
renderFunc(data, c);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override void Release(RenderGraphObjectPool pool)
|
||||
{
|
||||
base.Release(pool);
|
||||
|
||||
// We need to do the release from here because we need the final type.
|
||||
pool.Release(this);
|
||||
}
|
||||
}
|
||||
|
||||
[DebuggerDisplay("RenderPass: {name} (Index:{index} Async:{enableAsyncCompute})")]
|
||||
internal sealed class RasterRenderGraphPass<PassData> : BaseRenderGraphPass<PassData, RasterGraphContext>
|
||||
where PassData : class, new()
|
||||
{
|
||||
internal static RasterGraphContext c = new RasterGraphContext();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override void Execute(InternalRenderGraphContext renderGraphContext)
|
||||
{
|
||||
c.FromInternalContext(renderGraphContext);
|
||||
renderFunc(data, c);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override void Release(RenderGraphObjectPool pool)
|
||||
{
|
||||
base.Release(pool);
|
||||
|
||||
// We need to do the release from here because we need the final type.
|
||||
pool.Release(this);
|
||||
}
|
||||
}
|
||||
|
||||
[DebuggerDisplay("RenderPass: {name} (Index:{index} Async:{enableAsyncCompute})")]
|
||||
internal sealed class UnsafeRenderGraphPass<PassData> : BaseRenderGraphPass<PassData, UnsafeGraphContext>
|
||||
where PassData : class, new()
|
||||
{
|
||||
internal static UnsafeGraphContext c = new UnsafeGraphContext();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override void Execute(InternalRenderGraphContext renderGraphContext)
|
||||
{
|
||||
c.FromInternalContext(renderGraphContext);
|
||||
renderFunc(data, c);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override void Release(RenderGraphObjectPool pool)
|
||||
{
|
||||
base.Release(pool);
|
||||
|
||||
// We need to do the release from here because we need the final type.
|
||||
pool.Release(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user