You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

892 lines
43 KiB

using NUnit.Framework;
using System.Collections.Generic;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering.RenderGraphModule;
namespace UnityEngine.Rendering.Tests
{
class NativePassCompilerRenderGraphTests
{
class RenderGraphTestPassData
{
public TextureHandle[] textures = new TextureHandle[8];
public BufferHandle[] buffers = new BufferHandle[8];
}
TextureDesc SimpleTextureDesc(string name, int w, int h, int samples)
{
TextureDesc result = new TextureDesc(w, h);
result.msaaSamples = (MSAASamples)samples;
result.colorFormat = GraphicsFormat.R8G8B8A8_UNorm;
result.name = name;
return result;
}
class TestBuffers
{
public TextureHandle backBuffer;
public TextureHandle depthBuffer;
public TextureHandle[] extraBuffers = new TextureHandle[10];
public TextureHandle extraDepthBuffer;
};
TestBuffers ImportAndCreateBuffers(RenderGraph g)
{
TestBuffers result = new TestBuffers();
var backBuffer = BuiltinRenderTextureType.CameraTarget;
var backBufferHandle = RTHandles.Alloc(backBuffer, "Backbuffer Color");
var depthBuffer = BuiltinRenderTextureType.Depth;
var depthBufferHandle = RTHandles.Alloc(depthBuffer, "Backbuffer Depth");
var extraDepthBufferHandle = RTHandles.Alloc(depthBuffer, "Extra Depth Buffer");
RenderTargetInfo importInfo = new RenderTargetInfo();
RenderTargetInfo importInfoDepth = new RenderTargetInfo();
importInfo.width = 1024;
importInfo.height = 768;
importInfo.volumeDepth = 1;
importInfo.msaaSamples = 1;
importInfo.format = GraphicsFormat.R16G16B16A16_SFloat;
result.backBuffer = g.ImportTexture(backBufferHandle, importInfo);
importInfoDepth = importInfo;
importInfoDepth.format = GraphicsFormat.D32_SFloat_S8_UInt;
result.depthBuffer = g.ImportTexture(depthBufferHandle, importInfoDepth);
importInfo.format = GraphicsFormat.D24_UNorm;
result.extraDepthBuffer = g.ImportTexture(extraDepthBufferHandle, importInfoDepth);
for (int i = 0; i < result.extraBuffers.Length; i++)
{
result.extraBuffers[i] = g.CreateTexture(SimpleTextureDesc("ExtraBuffer" + i, 1024, 768, 1));
}
return result;
}
RenderGraph AllocateRenderGraph()
{
RenderGraph g = new RenderGraph();
g.nativeRenderPassesEnabled = true;
return g;
}
[Test]
public void SimpleMergePasses()
{
var g = AllocateRenderGraph();
var buffers = ImportAndCreateBuffers(g);
// Render something to 0,1
{
var builder = g.AddRasterRenderPass<RenderGraphTestPassData>("TestPass0", out var passData);
builder.SetRenderAttachmentDepth(buffers.depthBuffer, AccessFlags.Write);
builder.SetRenderAttachment(buffers.extraBuffers[0], 0, AccessFlags.Write);
builder.SetRenderAttachment(buffers.extraBuffers[1], 1, AccessFlags.Write);
builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { });
builder.Dispose();
}
// Render extra bits to 1
{
var builder = g.AddRasterRenderPass<RenderGraphTestPassData>("TestPass1", out var passData);
builder.SetRenderAttachmentDepth(buffers.depthBuffer, AccessFlags.Write);
builder.SetRenderAttachment(buffers.extraBuffers[0], 0, AccessFlags.Write);
builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { });
builder.Dispose();
}
// Render to final buffer
{
var builder = g.AddRasterRenderPass<RenderGraphTestPassData>("TestPass2", out var passData);
builder.SetRenderAttachment(buffers.extraBuffers[0], 0, AccessFlags.Write);
builder.SetRenderAttachment(buffers.extraBuffers[1], 1, AccessFlags.Write);
builder.SetRenderAttachmentDepth(buffers.depthBuffer, AccessFlags.Write);
builder.SetRenderAttachment(buffers.backBuffer, 2, AccessFlags.Write);
builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { });
builder.Dispose();
}
var result = g.CompileNativeRenderGraph(g.ComputeGraphHash());
var passes = result.contextData.GetNativePasses();
Assert.AreEqual(1, passes.Count);
Assert.AreEqual(4, passes[0].attachments.size);
Assert.AreEqual(3, passes[0].numGraphPasses);
ref var firstAttachment = ref passes[0].attachments[0];
Assert.AreEqual(RenderBufferLoadAction.Load, firstAttachment.loadAction);
ref var secondAttachment = ref passes[0].attachments[1];
Assert.AreEqual(RenderBufferLoadAction.Clear, secondAttachment.loadAction);
ref var thirdAttachment = ref passes[0].attachments[2];
Assert.AreEqual(RenderBufferLoadAction.Clear, thirdAttachment.loadAction);
ref var fourthAttachment = ref passes[0].attachments[3];
Assert.AreEqual(RenderBufferLoadAction.Load, fourthAttachment.loadAction);
}
/*[Test]
public void MergeNonRenderPasses()
{
RenderGraph g = new RenderGraph();
var buffers = ImportAndCreateBuffers(g);
// Render something to 0,1
{
var builder = g.AddRasterRenderPass<RenderGraphTestPassData>("TestPass0", out var passData);
builder.SetRenderAttachmentDepth(buffers.depthBuffer, AccessFlags.Write);
builder.SetRenderAttachment(buffers.extraBuffers[0], 0, AccessFlags.Write);
builder.SetRenderAttachment(buffers.extraBuffers[1], 1, AccessFlags.Write);
builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { });
builder.Dispose();
}
// Render extra bits to 1
{
var builder = g.AddRasterRenderPass<RenderGraphTestPassData>("TestPass1", out var passData);
// This does something like CommandBufffer.SetGlobal or something that doesn't do any rendering
builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { });
builder.AllowPassCulling(false);
builder.Dispose();
}
// Render to final buffer
{
var builder = g.AddRasterRenderPass<RenderGraphTestPassData>("TestPass2", out var passData);
builder.SetRenderAttachment(buffers.extraBuffers[0], 0, AccessFlags.Write);
builder.SetRenderAttachment(buffers.extraBuffers[1], 1, AccessFlags.Write);
builder.SetRenderAttachmentDepth(buffers.depthBuffer, AccessFlags.Write);
builder.SetRenderAttachment(buffers.backBuffer, 2, AccessFlags.Write);
builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { });
builder.Dispose();
}
var result = g.CompileNativeRenderGraph(g.ComputeGraphHash());
var passes = result.contextData.GetNativePasses();
Assert.AreEqual(1, passes.Count);
Assert.AreEqual(4, passes[0].attachments.size);
Assert.AreEqual(3, passes[0].numGraphPasses);
Assert.AreEqual(2, passes[0].numNativeSubPasses);
}*/
[Test]
public void MergeDepthPassWithNoDepthPass()
{
var g = AllocateRenderGraph();
var buffers = ImportAndCreateBuffers(g);
// depth
{
var builder = g.AddRasterRenderPass<RenderGraphTestPassData>("TestPass0", out var passData);
builder.SetRenderAttachmentDepth(buffers.depthBuffer, AccessFlags.Write);
builder.SetRenderAttachment(buffers.extraBuffers[0], 0, AccessFlags.Write);
builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { });
builder.Dispose();
}
// with no depth
{
var builder = g.AddRasterRenderPass<RenderGraphTestPassData>("TestPass1", out var passData);
builder.SetRenderAttachment(buffers.extraBuffers[0], 0, AccessFlags.Write);
builder.SetRenderAttachment(buffers.backBuffer, 2, AccessFlags.Write);
builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { });
builder.Dispose();
}
var result = g.CompileNativeRenderGraph(g.ComputeGraphHash());
var passes = result.contextData.GetNativePasses();
Assert.AreEqual(1, passes.Count);
Assert.AreEqual(Rendering.RenderGraphModule.NativeRenderPassCompiler.PassBreakReason.EndOfGraph, passes[0].breakAudit.reason);
}
[Test]
public void MergeNoDepthPassWithDepthPass()
{
var g = AllocateRenderGraph();
var buffers = ImportAndCreateBuffers(g);
// no depth
{
var builder = g.AddRasterRenderPass<RenderGraphTestPassData>("TestPass0", out var passData);
builder.SetRenderAttachment(buffers.extraBuffers[0], 0, AccessFlags.Write);
builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { });
builder.Dispose();
}
// with depth
{
var builder = g.AddRasterRenderPass<RenderGraphTestPassData>("TestPass1", out var passData);
builder.SetRenderAttachmentDepth(buffers.depthBuffer, AccessFlags.Write);
builder.SetRenderAttachment(buffers.extraBuffers[0], 0, AccessFlags.Write);
builder.SetRenderAttachment(buffers.backBuffer, 2, AccessFlags.Write);
builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { });
builder.Dispose();
}
var result = g.CompileNativeRenderGraph(g.ComputeGraphHash());
var passes = result.contextData.GetNativePasses();
Assert.AreEqual(1, passes.Count);
Assert.AreEqual(Rendering.RenderGraphModule.NativeRenderPassCompiler.PassBreakReason.EndOfGraph, passes[0].breakAudit.reason);
}
[Test]
public void MergeMultiplePassesDifferentDepth()
{
var g = AllocateRenderGraph();
var buffers = ImportAndCreateBuffers(g);
// no depth
{
var builder = g.AddRasterRenderPass<RenderGraphTestPassData>("TestPass0", out var passData);
builder.SetRenderAttachment(buffers.extraBuffers[0], 0, AccessFlags.Write);
builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { });
builder.Dispose();
}
// with depth
{
var builder = g.AddRasterRenderPass<RenderGraphTestPassData>("TestPass1", out var passData);
builder.SetRenderAttachmentDepth(buffers.depthBuffer, AccessFlags.Write);
builder.SetRenderAttachment(buffers.extraBuffers[0], 0, AccessFlags.Write);
builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { });
builder.Dispose();
}
// with no depth
{
var builder = g.AddRasterRenderPass<RenderGraphTestPassData>("TestPass2", out var passData);
builder.SetRenderAttachment(buffers.extraBuffers[0], 0, AccessFlags.Write);
builder.SetRenderAttachment(buffers.backBuffer, 2, AccessFlags.Write);
builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { });
builder.Dispose();
}
var result = g.CompileNativeRenderGraph(g.ComputeGraphHash());
var passes = result.contextData.GetNativePasses();
Assert.AreEqual(1, passes.Count);
Assert.AreEqual(Rendering.RenderGraphModule.NativeRenderPassCompiler.PassBreakReason.EndOfGraph, passes[0].breakAudit.reason);
}
[Test]
public void MergeDifferentDepthFormatsBreaksPass()
{
var g = AllocateRenderGraph();
var buffers = ImportAndCreateBuffers(g);
// depth
{
var builder = g.AddRasterRenderPass<RenderGraphTestPassData>("TestPass0", out var passData);
builder.SetRenderAttachmentDepth(buffers.depthBuffer, AccessFlags.Write);
builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { });
builder.Dispose();
}
// with different depth
{
var builder = g.AddRasterRenderPass<RenderGraphTestPassData>("TestPass1", out var passData);
builder.SetRenderAttachmentDepth(buffers.extraDepthBuffer, AccessFlags.Write);
builder.SetRenderAttachment(buffers.extraBuffers[0], 0, AccessFlags.Write);
builder.SetRenderAttachment(buffers.backBuffer, 2, AccessFlags.Write);
builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { });
builder.Dispose();
}
var result = g.CompileNativeRenderGraph(g.ComputeGraphHash());
var passes = result.contextData.GetNativePasses();
Assert.AreEqual(2, passes.Count);
Assert.AreEqual(Rendering.RenderGraphModule.NativeRenderPassCompiler.PassBreakReason.DifferentDepthTextures, passes[0].breakAudit.reason);
}
[Test]
public void NonFragmentUseBreaksPass()
{
var g = AllocateRenderGraph();
var buffers = ImportAndCreateBuffers(g);
{
var builder = g.AddRasterRenderPass<RenderGraphTestPassData>("TestPass0", out var passData);
builder.SetRenderAttachmentDepth(buffers.depthBuffer, AccessFlags.Write);
builder.SetRenderAttachment(buffers.extraBuffers[0], 0, AccessFlags.Write);
builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { });
builder.Dispose();
}
{
var builder = g.AddRasterRenderPass<RenderGraphTestPassData>("TestPass1", out var passData);
builder.SetRenderAttachmentDepth(buffers.depthBuffer, AccessFlags.Write);
builder.UseTexture(buffers.extraBuffers[0], AccessFlags.Read);
builder.SetRenderAttachment(buffers.backBuffer, 2, AccessFlags.Write);
builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { });
builder.Dispose();
}
var result = g.CompileNativeRenderGraph(g.ComputeGraphHash());
var passes = result.contextData.GetNativePasses();
Assert.AreEqual(2, passes.Count);
Assert.AreEqual(Rendering.RenderGraphModule.NativeRenderPassCompiler.PassBreakReason.NextPassReadsTexture, passes[0].breakAudit.reason);
}
[Test]
public void NonRasterBreaksPass()
{
var g = AllocateRenderGraph();
var buffers = ImportAndCreateBuffers(g);
// No depth
{
var builder = g.AddRasterRenderPass<RenderGraphTestPassData>("TestPass0", out var passData);
builder.SetRenderAttachmentDepth(buffers.depthBuffer, AccessFlags.Write);
builder.SetRenderAttachment(buffers.extraBuffers[0], 0, AccessFlags.Write);
builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { });
builder.Dispose();
}
// Compute touches extraBuffers[0]
{
var builder = g.AddComputePass<RenderGraphTestPassData>("ComputePass", out var passData);
builder.UseTexture(buffers.extraBuffers[0], AccessFlags.ReadWrite);
builder.SetRenderFunc((RenderGraphTestPassData data, ComputeGraphContext context) => { });
builder.Dispose();
}
// With depth
{
var builder = g.AddRasterRenderPass<RenderGraphTestPassData>("TestPass1", out var passData);
builder.SetRenderAttachmentDepth(buffers.depthBuffer, AccessFlags.Write);
builder.SetRenderAttachment(buffers.extraBuffers[0], 1, AccessFlags.Read);
builder.SetRenderAttachment(buffers.backBuffer, 2, AccessFlags.Write);
builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { });
builder.Dispose();
}
var result = g.CompileNativeRenderGraph(g.ComputeGraphHash());
var passes = result.contextData.GetNativePasses();
Assert.AreEqual(2, passes.Count);
Assert.AreEqual(Rendering.RenderGraphModule.NativeRenderPassCompiler.PassBreakReason.NonRasterPass, passes[0].breakAudit.reason);
}
[Test]
public void TooManyAttachmentsBreaksPass()
{
var g = AllocateRenderGraph();
var buffers = ImportAndCreateBuffers(g);
// 8 attachments
{
var builder = g.AddRasterRenderPass<RenderGraphTestPassData>("TestPass0", out var passData);
builder.SetRenderAttachmentDepth(buffers.depthBuffer, AccessFlags.Write);
for (int i = 0; i < 6; i++)
{
builder.SetRenderAttachment(buffers.extraBuffers[i], i, AccessFlags.Write);
}
builder.SetRenderAttachment(buffers.backBuffer, 7, AccessFlags.Write);
builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { });
builder.Dispose();
}
// 2 additional attachments
{
var builder = g.AddRasterRenderPass<RenderGraphTestPassData>("TestPass0", out var passData);
builder.SetRenderAttachmentDepth(buffers.depthBuffer, AccessFlags.Write);
for (int i = 0; i < 2; i++)
{
builder.SetRenderAttachment(buffers.extraBuffers[i + 6], i, AccessFlags.Write);
}
builder.SetRenderAttachment(buffers.backBuffer, 2, AccessFlags.Write);
builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { });
builder.Dispose();
}
var result = g.CompileNativeRenderGraph(g.ComputeGraphHash());
var passes = result.contextData.GetNativePasses();
Assert.AreEqual(2, passes.Count);
Assert.AreEqual(Rendering.RenderGraphModule.NativeRenderPassCompiler.PassBreakReason.AttachmentLimitReached, passes[0].breakAudit.reason);
}
[Test]
public void NativeSubPassesLimitNotExceeded()
{
var g = AllocateRenderGraph();
var buffers = ImportAndCreateBuffers(g);
// Native subpasses limit is 8 so go above
for (int i = 0; i < Rendering.RenderGraphModule.NativeRenderPassCompiler.NativePassCompiler.k_MaxSubpass + 2; i++)
{
using var builder = g.AddRasterRenderPass<RenderGraphTestPassData>($"TestPass_{i}", out var passData);
builder.SetInputAttachment(buffers.extraBuffers[1 - i % 2], 0);
builder.SetRenderAttachmentDepth(buffers.depthBuffer);
builder.SetRenderAttachment(buffers.extraBuffers[i % 2], 1);
builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { });
}
var result = g.CompileNativeRenderGraph(g.ComputeGraphHash());
var passes = result.contextData.GetNativePasses();
Assert.AreEqual(2, passes.Count);
Assert.AreEqual(Rendering.RenderGraphModule.NativeRenderPassCompiler.NativePassCompiler.k_MaxSubpass, passes[0].numGraphPasses);
Assert.AreEqual(Rendering.RenderGraphModule.NativeRenderPassCompiler.PassBreakReason.SubPassLimitReached, passes[0].breakAudit.reason);
}
[Test]
public void AllocateFreeInMergedPassesWorks()
{
var g = AllocateRenderGraph();
var buffers = ImportAndCreateBuffers(g);
// Render something to extra 0
{
var builder = g.AddRasterRenderPass<RenderGraphTestPassData>("TestPass0", out var passData);
builder.SetRenderAttachmentDepth(buffers.depthBuffer, AccessFlags.Write);
builder.SetRenderAttachment(buffers.extraBuffers[0], 0, AccessFlags.Write);
builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { });
builder.Dispose();
}
// Render extra bits to extra 1, this causes 1 to be allocated in pass 1 which will be the first sub pass of the merged native pass
{
var builder = g.AddRasterRenderPass<RenderGraphTestPassData>("TestPass1", out var passData);
builder.SetRenderAttachmentDepth(buffers.depthBuffer, AccessFlags.Write);
builder.SetRenderAttachment(buffers.extraBuffers[1], 0, AccessFlags.Write);
builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { });
builder.Dispose();
}
// Render extra bits to extra 2, this causes 2 to be allocated in pass 2 which will be the second sub pass of the merged native pass
// It's also the last time extra 1 is used so it gets freed
{
var builder = g.AddRasterRenderPass<RenderGraphTestPassData>("TestPass2", out var passData);
builder.SetRenderAttachmentDepth(buffers.depthBuffer, AccessFlags.Write);
builder.SetRenderAttachment(buffers.extraBuffers[1], 0, AccessFlags.ReadWrite);
builder.SetRenderAttachment(buffers.extraBuffers[2], 1, AccessFlags.Write);
builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { });
builder.Dispose();
}
// Render to final buffer
{
var builder = g.AddRasterRenderPass<RenderGraphTestPassData>("TestPass2", out var passData);
builder.SetRenderAttachment(buffers.extraBuffers[0], 0, AccessFlags.Write);
builder.SetRenderAttachment(buffers.extraBuffers[2], 1, AccessFlags.Write);
builder.SetRenderAttachmentDepth(buffers.depthBuffer, AccessFlags.Write);
builder.SetRenderAttachment(buffers.backBuffer, 2, AccessFlags.Write);
builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { });
builder.Dispose();
}
var result = g.CompileNativeRenderGraph(g.ComputeGraphHash());
var passes = result.contextData.GetNativePasses();
Assert.AreEqual(1, passes.Count);
Assert.AreEqual(5, passes[0].attachments.size); //3 extra + color + depth
Assert.AreEqual(4, passes[0].numGraphPasses);
// Pass 1 first used = {extra 1}
List<ResourceHandle> firstUsed = new List<ResourceHandle>();
ref var pass1Data = ref result.contextData.passData.ElementAt(1);
foreach (ref readonly var res in pass1Data.FirstUsedResources(result.contextData))
firstUsed.Add(res);
Assert.AreEqual(1, firstUsed.Count);
Assert.AreEqual(buffers.extraBuffers[1].handle.index, firstUsed[0].index);
// Pass 2 last used = {
List<ResourceHandle> lastUsed = new List<ResourceHandle>();
ref var pass2Data = ref result.contextData.passData.ElementAt(2);
foreach (ref readonly var res in pass2Data.LastUsedResources(result.contextData))
lastUsed.Add(res);
Assert.AreEqual(1, lastUsed.Count);
Assert.AreEqual(buffers.extraBuffers[1].handle.index, lastUsed[0].index);
}
[Test]
public void MemorylessWorks()
{
var g = AllocateRenderGraph();
var buffers = ImportAndCreateBuffers(g);
// Render something to extra 0
{
var builder = g.AddRasterRenderPass<RenderGraphTestPassData>("TestPass0", out var passData);
builder.SetRenderAttachmentDepth(buffers.depthBuffer, AccessFlags.Write);
builder.SetRenderAttachment(buffers.extraBuffers[0], 0, AccessFlags.Write);
builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { });
builder.Dispose();
}
// Render to final buffer
{
var builder = g.AddRasterRenderPass<RenderGraphTestPassData>("TestPass2", out var passData);
builder.SetRenderAttachmentDepth(buffers.depthBuffer, AccessFlags.Write);
builder.SetRenderAttachment(buffers.extraBuffers[0], 0, AccessFlags.ReadWrite);
builder.SetRenderAttachment(buffers.backBuffer, 1, AccessFlags.Write);
builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { });
builder.Dispose();
}
var result = g.CompileNativeRenderGraph(g.ComputeGraphHash());
var passes = result.contextData.GetNativePasses();
Assert.AreEqual(1, passes.Count);
Assert.AreEqual(3, passes[0].attachments.size); //1 extra + color + depth
Assert.AreEqual(2, passes[0].numGraphPasses);
// Pass 0 : first used = {depthBuffer, extraBuffers[0]}
List<ResourceHandle> firstUsed = new List<ResourceHandle>();
ref var pass0Data = ref result.contextData.passData.ElementAt(0);
foreach (ref readonly var res in pass0Data.FirstUsedResources(result.contextData))
firstUsed.Add(res);
//Extra buffer 0 should be memoryless
Assert.AreEqual(2, firstUsed.Count);
Assert.AreEqual(buffers.extraBuffers[0].handle.index, firstUsed[1].index);
ref var info = ref result.contextData.UnversionedResourceData(firstUsed[1]);
Assert.AreEqual(true, info.memoryLess);
// Pass 1 : last used = {depthBuffer, extraBuffers[0], backBuffer}
List<ResourceHandle> lastUsed = new List<ResourceHandle>();
ref var pass1Data = ref result.contextData.passData.ElementAt(1);
foreach (var res in pass1Data.LastUsedResources(result.contextData))
lastUsed.Add(res);
Assert.AreEqual(3, lastUsed.Count);
Assert.AreEqual(buffers.extraBuffers[0].handle.index, lastUsed[1].index);
}
[Test]
public void InputAttachmentsWork()
{
var g = AllocateRenderGraph();
var buffers = ImportAndCreateBuffers(g);
// Render something to extra 0,1,2
{
var builder = g.AddRasterRenderPass<RenderGraphTestPassData>("TestPass0", out var passData);
builder.SetRenderAttachmentDepth(buffers.depthBuffer, AccessFlags.Write);
builder.SetRenderAttachment(buffers.extraBuffers[0], 0, AccessFlags.Write);
builder.SetRenderAttachment(buffers.extraBuffers[1], 1, AccessFlags.Write);
builder.SetRenderAttachment(buffers.extraBuffers[2], 2, AccessFlags.Write);
builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { });
builder.Dispose();
}
// Render to final buffer using extra 0 as attachment
{
var builder = g.AddRasterRenderPass<RenderGraphTestPassData>("TestPass2", out var passData);
builder.SetRenderAttachmentDepth(buffers.depthBuffer, AccessFlags.ReadWrite);
builder.SetRenderAttachment(buffers.backBuffer, 1, AccessFlags.Write);
builder.SetInputAttachment(buffers.extraBuffers[0], 0, AccessFlags.Read);
builder.SetInputAttachment(buffers.extraBuffers[1], 1, AccessFlags.Read);
builder.SetInputAttachment(buffers.extraBuffers[2], 2, AccessFlags.Read);
builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { });
builder.Dispose();
}
var result = g.CompileNativeRenderGraph(g.ComputeGraphHash());
var nativePasses = result.contextData.GetNativePasses();
Assert.AreEqual(1, nativePasses.Count);
Assert.AreEqual(5, nativePasses[0].attachments.size); //3 extra + color + depth
Assert.AreEqual(2, nativePasses[0].numGraphPasses);
// Validate attachments
Assert.AreEqual(buffers.depthBuffer.handle.index, nativePasses[0].attachments[0].handle.index);
Assert.AreEqual(buffers.extraBuffers[0].handle.index, nativePasses[0].attachments[1].handle.index);
Assert.AreEqual(buffers.extraBuffers[1].handle.index, nativePasses[0].attachments[2].handle.index);
Assert.AreEqual(buffers.extraBuffers[2].handle.index, nativePasses[0].attachments[3].handle.index);
Assert.AreEqual(buffers.backBuffer.handle.index, nativePasses[0].attachments[4].handle.index);
// Sub Pass 0
ref var subPass = ref result.contextData.nativeSubPassData.ElementAt(nativePasses[0].firstNativeSubPass);
Assert.AreEqual(0, subPass.inputs.Length);
Assert.AreEqual(3, subPass.colorOutputs.Length);
Assert.AreEqual(1, subPass.colorOutputs[0]);
Assert.AreEqual(2, subPass.colorOutputs[1]);
Assert.AreEqual(3, subPass.colorOutputs[2]);
// Sub Pass 1
ref var subPass2 = ref result.contextData.nativeSubPassData.ElementAt(nativePasses[0].firstNativeSubPass + 1);
Assert.AreEqual(3, subPass2.inputs.Length);
Assert.AreEqual(1, subPass2.inputs[0]);
Assert.AreEqual(2, subPass2.inputs[1]);
Assert.AreEqual(3, subPass2.inputs[2]);
Assert.AreEqual(1, subPass2.colorOutputs.Length);
Assert.AreEqual(4, subPass2.colorOutputs[0]);
}
[Test]
public void ImportParametersWork()
{
var g = AllocateRenderGraph();
var buffers = ImportAndCreateBuffers(g);
// Import with parameters
var backBuffer = BuiltinRenderTextureType.CameraTarget;
var backBufferHandle = RTHandles.Alloc(backBuffer, "Test Import");
RenderTargetInfo importInfo = new RenderTargetInfo();
importInfo.width = 1024;
importInfo.height = 768;
importInfo.msaaSamples = 1;
importInfo.volumeDepth = 1;
importInfo.format = GraphicsFormat.R16G16B16A16_SFloat;
ImportResourceParams importResourceParams = new ImportResourceParams();
importResourceParams.clearOnFirstUse = true;
importResourceParams.discardOnLastUse = true;
var importedTexture = g.ImportTexture(backBufferHandle, importInfo, importResourceParams);
// Render something to importedTexture
{
var builder = g.AddRasterRenderPass<RenderGraphTestPassData>("TestPass0", out var passData);
builder.SetRenderAttachment(importedTexture, 0, AccessFlags.Write);
builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { });
builder.Dispose();
}
// Compute does something or other
{
var builder = g.AddComputePass<RenderGraphTestPassData>("ComputePass", out var passData);
builder.UseTexture(buffers.extraBuffers[0], AccessFlags.ReadWrite);
builder.SetRenderFunc((RenderGraphTestPassData data, ComputeGraphContext context) => { });
builder.Dispose();
}
// Render to final buffer
{
var builder = g.AddRasterRenderPass<RenderGraphTestPassData>("TestPass2", out var passData);
builder.SetRenderAttachment(buffers.extraBuffers[0], 0, AccessFlags.Write);
builder.SetRenderAttachment(importedTexture, 1, AccessFlags.Write);
builder.SetRenderAttachment(buffers.backBuffer, 2, AccessFlags.Write);
builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { });
builder.Dispose();
}
var result = g.CompileNativeRenderGraph(g.ComputeGraphHash());
var passes = result.contextData.GetNativePasses();
// Validate nr pass 0
Assert.AreEqual(2, passes.Count);
Assert.AreEqual(1, passes[0].attachments.size);
Assert.AreEqual(1, passes[0].numGraphPasses);
// Clear on first use
ref var firstAttachment = ref passes[0].attachments[0];
Assert.AreEqual(RenderBufferLoadAction.Clear, firstAttachment.loadAction);
// Validate nr pass 1
Assert.AreEqual(3, passes[1].attachments.size);
Assert.AreEqual(1, passes[1].numGraphPasses);
// Discard on last use
Assert.AreEqual(RenderBufferStoreAction.DontCare, passes[1].attachments[1].storeAction);
// Regular imports do a full load/store
Assert.AreEqual(RenderBufferLoadAction.Load, passes[1].attachments[2].loadAction);
Assert.AreEqual(RenderBufferStoreAction.Store, passes[1].attachments[2].storeAction);
}
[Test]
public void FencesWork()
{
var g = AllocateRenderGraph();
var buffers = ImportAndCreateBuffers(g);
{ // Pass #1: Render pass writing to backbuffer
using var builder = g.AddRasterRenderPass<RenderGraphTestPassData>("#1 RenderPass", out _);
builder.UseTexture(buffers.backBuffer, AccessFlags.Write);
builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { });
}
{ // Pass #2: Async compute pass writing to back buffer
using var builder = g.AddComputePass<RenderGraphTestPassData>("#2 AsyncComputePass", out _);
builder.EnableAsyncCompute(true);
builder.UseTexture(buffers.backBuffer, AccessFlags.Write);
builder.SetRenderFunc((RenderGraphTestPassData data, ComputeGraphContext context) => { });
}
{ // Pass #3: Render pass writing to backbuffer
using var builder = g.AddRasterRenderPass<RenderGraphTestPassData>("#3 RenderPass", out _);
builder.UseTexture(buffers.backBuffer, AccessFlags.Write);
builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { });
}
{ // Pass #4: Render pass writing to backbuffer
using var builder = g.AddRasterRenderPass<RenderGraphTestPassData>("#4 RenderPass", out _);
builder.UseTexture(buffers.backBuffer, AccessFlags.Write);
builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { });
}
var result = g.CompileNativeRenderGraph(g.ComputeGraphHash());
var passData = result.contextData.passData;
// #1 waits for nothing, inserts a fence
Assert.AreEqual(-1, passData[0].waitOnGraphicsFencePassId);
Assert.True(passData[0].insertGraphicsFence);
// #2 (async compute) pass waits on #1, inserts a fence
Assert.AreEqual(0, passData[1].waitOnGraphicsFencePassId);
Assert.True(passData[1].insertGraphicsFence);
// #3 waits on #2 (async compute) pass, doesn't insert a fence
Assert.AreEqual(1, passData[2].waitOnGraphicsFencePassId);
Assert.False(passData[2].insertGraphicsFence);
// #4 waits for nothing, doesn't insert a fence
Assert.AreEqual(-1, passData[3].waitOnGraphicsFencePassId);
Assert.False(passData[3].insertGraphicsFence);
}
[Test]
public void BuffersWork()
{
var g = AllocateRenderGraph();
var rendertargets = ImportAndCreateBuffers(g);
var desc = new BufferDesc(1024, 16);
var buffer = g.CreateBuffer(desc);
// Render something to extra 0 and write uav
{
var builder = g.AddRasterRenderPass<RenderGraphTestPassData>("TestPass0", out var passData);
builder.SetRenderAttachmentDepth(rendertargets.depthBuffer, AccessFlags.Write);
builder.SetRenderAttachment(rendertargets.extraBuffers[0], 0, AccessFlags.Write);
builder.UseBufferRandomAccess(buffer, 1, AccessFlags.ReadWrite);
builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { });
builder.Dispose();
}
// Render extra bits to 0 reading from the uav
{
var builder = g.AddRasterRenderPass<RenderGraphTestPassData>("TestPass1", out var passData);
builder.SetRenderAttachmentDepth(rendertargets.depthBuffer, AccessFlags.Write);
builder.SetRenderAttachment(rendertargets.extraBuffers[0], 0, AccessFlags.Write);
builder.UseBuffer(buffer, AccessFlags.Read);
builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { });
builder.Dispose();
}
// Render to final buffer
{
var builder = g.AddRasterRenderPass<RenderGraphTestPassData>("TestPass2", out var passData);
builder.UseTexture(rendertargets.extraBuffers[0]);
builder.SetRenderAttachment(rendertargets.backBuffer, 2, AccessFlags.Write);
builder.SetRenderAttachmentDepth(rendertargets.depthBuffer, AccessFlags.Write);
builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { });
builder.Dispose();
}
var result = g.CompileNativeRenderGraph(g.ComputeGraphHash());
var passes = result.contextData.GetNativePasses();
// Validate Pass 0 : uav is first used and created
ref var pass0Data = ref result.contextData.passData.ElementAt(0);
var firstUsedList = pass0Data.FirstUsedResources(result.contextData).ToArray();
Assert.AreEqual(3, firstUsedList.Length);
Assert.AreEqual(rendertargets.depthBuffer.handle.index, firstUsedList[0].index);
Assert.AreEqual(RenderGraphResourceType.Texture, firstUsedList[0].type);
Assert.AreEqual(rendertargets.extraBuffers[0].handle.index, firstUsedList[1].index);
Assert.AreEqual(RenderGraphResourceType.Texture, firstUsedList[1].type);
Assert.AreEqual(buffer.handle.index, firstUsedList[2].index);
Assert.AreEqual(RenderGraphResourceType.Buffer, firstUsedList[2].type);
var randomAccessList = pass0Data.RandomWriteTextures(result.contextData).ToArray();
Assert.AreEqual(1, randomAccessList.Length);
Assert.AreEqual(buffer.handle.index, randomAccessList[0].resource.index);
Assert.AreEqual(RenderGraphResourceType.Buffer, randomAccessList[0].resource.type);
Assert.AreEqual(1, randomAccessList[0].index); // we asked for it to be at index 1 in the builder
Assert.AreEqual(true, randomAccessList[0].preserveCounterValue); // preserve is default
// Validate Pass 1 : uav buffer is last used and destroyed
ref var pass1Data = ref result.contextData.passData.ElementAt(1);
var lastUsedList = pass1Data.LastUsedResources(result.contextData).ToArray();
Assert.AreEqual(1, lastUsedList.Length);
Assert.AreEqual(buffer.handle.index, lastUsedList[0].index);
Assert.AreEqual(RenderGraphResourceType.Buffer, lastUsedList[0].type);
}
[Test]
public void ResolveMSAAImportColor()
{
var g = AllocateRenderGraph();
var buffers = ImportAndCreateBuffers(g);
// Import with parameters
// Depth
var depthBuffer = BuiltinRenderTextureType.Depth;
var depthBufferHandle = RTHandles.Alloc(depthBuffer, "Test Import Depth");
RenderTargetInfo importInfoDepth = new RenderTargetInfo();
importInfoDepth.width = 1024;
importInfoDepth.height = 768;
importInfoDepth.msaaSamples = 4;
importInfoDepth.volumeDepth = 1;
importInfoDepth.format = GraphicsFormat.D32_SFloat_S8_UInt;
ImportResourceParams importResourceParams = new ImportResourceParams();
importResourceParams.clearOnFirstUse = true;
importResourceParams.discardOnLastUse = true;
var importedDepth = g.ImportTexture(depthBufferHandle, importInfoDepth, importResourceParams);
// Color
var backBuffer = BuiltinRenderTextureType.CameraTarget;
var backBufferHandle = RTHandles.Alloc(backBuffer, "Test Import Color");
RenderTargetInfo importInfoColor = new RenderTargetInfo();
importInfoColor.width = 1024;
importInfoColor.height = 768;
importInfoColor.msaaSamples = 4;
importInfoColor.volumeDepth = 1;
importInfoColor.format = GraphicsFormat.R16G16B16A16_SFloat;
var importedColor = g.ImportTexture(backBufferHandle, importInfoColor, importResourceParams);
// Render something to importedColor and importedDepth
{
var builder = g.AddRasterRenderPass<RenderGraphTestPassData>("TestPass", out var passData);
builder.SetRenderAttachmentDepth(importedDepth, AccessFlags.Write);
builder.SetRenderAttachment(importedColor, 1, AccessFlags.Write);
builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { });
builder.Dispose();
}
var result = g.CompileNativeRenderGraph(g.ComputeGraphHash());
var passes = result.contextData.GetNativePasses();
// Validate nr pass
Assert.AreEqual(1, passes.Count);
Assert.AreEqual(2, passes[0].attachments.size);
Assert.AreEqual(1, passes[0].numGraphPasses);
// Clear on first use
ref var firstAttachment = ref passes[0].attachments[0];
Assert.AreEqual(RenderBufferLoadAction.Clear, firstAttachment.loadAction);
ref var secondAttachment = ref passes[0].attachments[1];
Assert.AreEqual(RenderBufferLoadAction.Clear, secondAttachment.loadAction);
// Discard on last use
Assert.AreEqual(RenderBufferStoreAction.DontCare, passes[0].attachments[0].storeAction);
// When discarding MSAA color, we only discard the MSAA buffers but keep the resolved texture
Assert.AreEqual(RenderBufferStoreAction.Resolve, passes[0].attachments[1].storeAction);
}
}
}