public void CanUseGraphValueResolver_ToResolveValues_InAJob(NodeSet.RenderExecutionModel computeType) { using (var results = new NativeArray <float>(1, Allocator.Persistent)) using (var set = new RenderGraphTests.PotentiallyJobifiedNodeSet(computeType)) { var root = set.Create <RenderPipe>(); GraphValue <float> rootValue = set.CreateGraphValue(root, RenderPipe.KernelPorts.Output); for (int i = 0; i < 100; ++i) { set.SendMessage(root, RenderPipe.SimulationPorts.Input, i); set.Update(); GraphValueReadbackJob job; job.Value = rootValue; job.Result = results; job.Resolver = set.GetGraphValueResolver(out var valueResolverDependency); set.InjectDependencyFromConsumer(job.Schedule(valueResolverDependency)); // Automatically fences before CopyWorlds. Results is accessible now. set.Update(); Assert.AreEqual(i, results[0]); Assert.AreEqual(i, set.GetValueBlocking(rootValue)); } set.Destroy(root); set.ReleaseGraphValue(rootValue); } }
public void FeedbackTraversalOrder_IsCoherent([Values] NodeSet.RenderExecutionModel model) { using (var set = new NodeSet()) { set.RendererModel = model; var node1 = set.Create <KernelAdderNode>(); var node2 = set.Create <KernelAdderNode>(); GraphValue <int> node1GV = set.CreateGraphValue(node1, KernelAdderNode.KernelPorts.Output); GraphValue <int> node2GV = set.CreateGraphValue(node2, KernelAdderNode.KernelPorts.Output); set.Connect(node1, KernelAdderNode.KernelPorts.Output, node2, KernelAdderNode.KernelPorts.Input, NodeSet.ConnectionType.Feedback); for (int i = 0; i < 10; ++i) { set.SetData(node1, KernelAdderNode.KernelPorts.Input, i); set.Update(); Assert.AreEqual(i + 1, set.GetValueBlocking(node1GV)); Assert.AreEqual(i + 1, set.GetValueBlocking(node2GV)); } set.ReleaseGraphValue(node1GV); set.ReleaseGraphValue(node2GV); set.Destroy(node1, node2); } }
public void GraphAccumulatesData_OverLongChains( [Values(2, 10, 30)] int nodeChainLength, [Values(NodeSet.RenderExecutionModel.Synchronous, NodeSet.RenderExecutionModel.MaximallyParallel)] NodeSet.RenderExecutionModel meansOfComputation) { using (var set = new PotentiallyJobifiedNodeSet(meansOfComputation)) { var nodes = new List <NodeHandle <KernelAdderNode> >(nodeChainLength); var graphValues = new List <GraphValue <int> >(nodeChainLength); for (int i = 0; i < nodeChainLength; ++i) { var node = set.Create <KernelAdderNode>(); nodes.Add(node); graphValues.Add(set.CreateGraphValue(node, KernelAdderNode.KernelPorts.Output)); } for (int i = 0; i < nodeChainLength - 1; ++i) { set.Connect(nodes[i], KernelAdderNode.KernelPorts.Output, nodes[i + 1], KernelAdderNode.KernelPorts.Input); } set.Update(); for (int i = 0; i < nodeChainLength; ++i) { Assert.AreEqual(i + 1, set.GetValueBlocking(graphValues[i])); } for (int i = 0; i < nodeChainLength; ++i) { set.ReleaseGraphValue(graphValues[i]); set.Destroy(nodes[i]); } } }
public void TwoNodeFeedbackLoop_Works([Values] NodeSet.RenderExecutionModel model) { using (var set = new NodeSet()) { set.RendererModel = model; var node1 = set.Create <KernelAdderNode>(); var node2 = set.Create <KernelAdderNode>(); GraphValue <int> node1GV = set.CreateGraphValue(node1, KernelAdderNode.KernelPorts.Output); GraphValue <int> node2GV = set.CreateGraphValue(node2, KernelAdderNode.KernelPorts.Output); set.Connect(node1, KernelAdderNode.KernelPorts.Output, node2, KernelAdderNode.KernelPorts.Input); set.Connect(node2, KernelAdderNode.KernelPorts.Output, node1, KernelAdderNode.KernelPorts.Input, NodeSet.ConnectionType.Feedback); // After the first update, we expect GVs 1,2. On the next update, the 2 should be fedback into node1, so we expect 3,4. Next, 5,6... for (int i = 0; i < 10; ++i) { set.Update(); Assert.AreEqual(i * 2 + 1, set.GetValueBlocking(node1GV)); Assert.AreEqual(i * 2 + 2, set.GetValueBlocking(node2GV)); } set.ReleaseGraphValue(node1GV); set.ReleaseGraphValue(node2GV); set.Destroy(node1, node2); } }
public void TestUserNodes_DoRunInsideBurst( [Values] NodeSet.RenderExecutionModel model ) { if (!BurstConfig.IsBurstEnabled) { Assert.Ignore("Burst is not enabled"); } #if UNITY_EDITOR if (!TestRunConfig.EnableBurstCompileSynchronously) { Assert.Ignore("Burst is not compiling synchronously"); } #endif using (var set = new NodeSet()) { set.RendererModel = model; var node = set.Create <BurstedNode>(); var gv = set.CreateGraphValue(node, BurstedNode.KernelPorts.Result); set.Update(); Assert.IsTrue(set.GetValueBlocking(gv)); set.Destroy(node); set.ReleaseGraphValue(gv); } }
public void CanRead_ECSBuffer_InsideFromDFG( [Values] NodeSet.RenderExecutionModel model, [Values(1, 3, 13, 50)] int bufferSize) { const int k_Loops = 10; using (var f = new Fixture <UpdateSystem>()) { f.Set.RendererModel = model; var entity = f.EM.CreateEntity(typeof(SimpleBuffer)); var entityNode = f.Set.CreateComponentNode(entity); var dfgNode = f.Set.Create <BufferNode>(); var gv = f.Set.CreateGraphValue(dfgNode, BufferNode.KernelPorts.Output); f.Set.Connect(entityNode, ComponentNode.Output <SimpleBuffer>(), dfgNode, BufferNode.KernelPorts.Input); var rng = new Mathematics.Random(0x7f); f.Set.SetBufferSize(dfgNode, BufferNode.KernelPorts.Output, Buffer <SimpleBuffer> .SizeRequest(bufferSize)); for (int i = 0; i < k_Loops; ++i) { var ecsBuffer = f.EM.GetBuffer <SimpleBuffer>(entity); ecsBuffer.ResizeUninitialized(bufferSize); for (int n = 0; n < bufferSize; ++n) { ecsBuffer[n] = new SimpleBuffer { Values = rng.NextFloat3() }; } f.System.Update(); var resolver = f.Set.GetGraphValueResolver(out var dependency); dependency.Complete(); ecsBuffer = f.EM.GetBuffer <SimpleBuffer>(entity); var dfgBuffer = resolver.Resolve(gv); Assert.AreEqual(ecsBuffer.Length, dfgBuffer.Length); // TODO: can compare alias here for (int n = 0; n < bufferSize; ++n) { Assert.AreEqual(ecsBuffer[n], dfgBuffer[n]); } } f.Set.ReleaseGraphValue(gv); f.Set.Destroy(entityNode, dfgNode); } }
public void GraphCanUpdate_WithoutIssues([Values] NodeSet.RenderExecutionModel meansOfComputation) { using (var set = new PotentiallyJobifiedNodeSet(meansOfComputation)) { NodeHandle <KernelNode> a = set.Create <KernelNode>(), b = set.Create <KernelNode>(); set.Connect(a, KernelNode.KernelPorts.Output, b, KernelNode.KernelPorts.Input); set.Update(); set.DataGraph.SyncAnyRendering(); set.Destroy(a, b); } }
public void NthOrderFeedbackSystem_Works([Values] NodeSet.RenderExecutionModel model, [Values(2, 5, 100)] int numNodes) { using (var set = new NodeSet()) { set.RendererModel = model; var nodes = new List <NodeHandle <KernelSumNode> >(); var nodeGVs = new List <GraphValue <ECSInt> >(); var expected = new List <ECSInt>(); nodes.Add(set.Create <KernelSumNode>()); nodeGVs.Add(set.CreateGraphValue(nodes[0], KernelSumNode.KernelPorts.Output)); expected.Add(1); for (int i = 1; i < numNodes; ++i) { var node = set.Create <KernelSumNode>(); nodeGVs.Add(set.CreateGraphValue(node, KernelSumNode.KernelPorts.Output)); expected.Add(1); nodes.Add(node); set.SetPortArraySize(node, KernelSumNode.KernelPorts.Inputs, 1); set.Connect(nodes[i - 1], KernelSumNode.KernelPorts.Output, node, KernelSumNode.KernelPorts.Inputs, 0); set.SetPortArraySize(nodes[i - 1], KernelSumNode.KernelPorts.Inputs, 2); set.Connect(node, KernelSumNode.KernelPorts.Output, nodes[i - 1], KernelSumNode.KernelPorts.Inputs, 1, NodeSet.ConnectionType.Feedback); } set.SetData(nodes[0], KernelSumNode.KernelPorts.Inputs, 0, 1); for (int i = 0; i < 10; ++i) { set.Update(); for (int j = 0; j < numNodes; ++j) { Assert.AreEqual(expected[j], set.GetValueBlocking(nodeGVs[j])); } expected[0] = expected[1] + 1; for (int j = 1; j < numNodes - 1; ++j) { expected[j] = expected[j - 1] + expected[j + 1]; } expected[numNodes - 1] = expected[numNodes - 2]; } nodeGVs.ForEach(n => set.ReleaseGraphValue(n)); nodes.ForEach(n => set.Destroy(n)); } }
public void KernelNodeMemberMemory_IsPersistent_OverMultipleGraphEvaluations([Values] NodeSet.RenderExecutionModel meansOfComputation) { using (var set = new PotentiallyJobifiedNodeSet(meansOfComputation)) { var node = set.Create <PersistentKernelNode>(); var value = set.CreateGraphValue(node, PersistentKernelNode.KernelPorts.Output); for (int i = 0; i < 100; ++i) { set.Update(); Assert.AreEqual(i, set.GetValueBlocking(value)); } set.Destroy(node); set.ReleaseGraphValue(value); } }
[Test, Explicit] // Does not work due to issue #331. Do we even want it to work? public void SingleNodeFeedbackLoop_Works([Values] NodeSet.RenderExecutionModel model) { using (var set = new NodeSet()) { set.RendererModel = model; var node = set.Create <KernelAdderNode>(); GraphValue <int> nodeGV = set.CreateGraphValue(node, KernelAdderNode.KernelPorts.Output); set.Connect(node, KernelAdderNode.KernelPorts.Output, node, KernelAdderNode.KernelPorts.Input, NodeSet.ConnectionType.Feedback); for (int i = 0; i < 10; ++i) { set.Update(); Assert.AreEqual(i + 1, set.GetValueBlocking(nodeGV)); } set.ReleaseGraphValue(nodeGV); set.Destroy(node); } }
public void ComplexDAG_ProducesExpectedResults_InAllExecutionModels([Values] NodeSet.RenderExecutionModel model) { const int k_NumGraphs = 10; for (int k = 0; k < 10; ++k) { using (var set = new NodeSet()) { set.RendererModel = model; var tests = new List <DAGTest>(k_NumGraphs); for (int i = 0; i < k_NumGraphs; ++i) { tests.Add(new DAGTest(set)); } for (int i = 0; i < k_NumGraphs; ++i) { tests[i].SetLeafInputs(i); } set.Update(); /* A ---------------- B (1) * A -------- C ----- B (2) * / \ * A - B - B C = C (3) * \ / * A -------- C ----- B (4) * A (5) * * A = in + 1 * B = in * 3 * C = in1 + in2 * */ void CheckExpectedValueAtRoot(int expected, DAGTest graph, int root, int i) { var output = set.GetValueBlocking(graph.RootGVs[root]); ref var value = ref set.GetOutputValues()[graph.RootGVs[root].Handle.Index]; Assert.IsTrue(value.IsLinkedToGraph, // This happens locally inside CopyWorlds. $"Race condition on Root[{root}] from render graph in graph iteration {i}" ); if (expected != output) { System.Threading.Thread.Sleep(1000); var laterOutput = set.GetValueBlocking(graph.RootGVs[0]); Assert.AreEqual( output, laterOutput, $"Root[{root}] produced a race condition in graph iteration {i}" ); Assert.AreEqual( expected, output, $"Root[0] produced unexpected results in graph iteration {i}" ); } } for (int i = 0; i < k_NumGraphs; ++i) { var graph = tests[i]; const int b = 3; const int c = 2; var a = (i + 1); var abb = a * b * b; CheckExpectedValueAtRoot( a * b, graph, 0, i ); CheckExpectedValueAtRoot( (abb + a) * b, graph, 1, i ); CheckExpectedValueAtRoot( (abb + a) * c * c, graph, 2, i ); CheckExpectedValueAtRoot( (abb + a) * b, graph, 3, i ); CheckExpectedValueAtRoot( a, graph, 4, i ); } tests.ForEach(t => t.Dispose()); } }
public void CanReadAndWrite_ToSameECSBuffer_FromInsideDFG( [Values] NodeSet.RenderExecutionModel model, [Values(1, 3, 13, 50)] int bufferSize, [Values] bool feedbackAfterProcessing) { const int k_Loops = 10; var k_Offset = new float3(1.0f, 1.5f, 2.0f); using (var f = new Fixture <UpdateSystem>()) { f.Set.RendererModel = model; var entity = f.EM.CreateEntity(typeof(SimpleBuffer), typeof(SimpleData)); var entityNode = f.Set.CreateComponentNode(entity); var dfgNode = f.Set.Create <BufferNode>(); f.Set.SetData(dfgNode, BufferNode.KernelPorts.Offset, k_Offset); f.Set.Connect( entityNode, ComponentNode.Output <SimpleBuffer>(), dfgNode, BufferNode.KernelPorts.Input, !feedbackAfterProcessing ? NodeSet.ConnectionType.Feedback : NodeSet.ConnectionType.Normal ); f.Set.Connect( dfgNode, BufferNode.KernelPorts.Output, entityNode, ComponentNode.Input <SimpleBuffer>(), feedbackAfterProcessing ? NodeSet.ConnectionType.Feedback : NodeSet.ConnectionType.Normal ); var ecsBuffer = f.EM.GetBuffer <SimpleBuffer>(entity); // match all buffer sizes ecsBuffer.ResizeUninitialized(bufferSize); f.Set.SetBufferSize(dfgNode, BufferNode.KernelPorts.Output, Buffer <SimpleBuffer> .SizeRequest(bufferSize)); var expected = new List <SimpleBuffer>(); var rng = new Mathematics.Random(0x8f); for (int n = 0; n < bufferSize; ++n) { ecsBuffer[n] = new SimpleBuffer { Values = feedbackAfterProcessing ? -k_Offset : rng.NextFloat3() }; expected.Add(ecsBuffer[n]); } for (int i = 0; i < k_Loops; ++i) { f.System.Update(); // This should fence on all dependencies ecsBuffer = f.EM.GetBuffer <SimpleBuffer>(entity); for (int n = 0; n < bufferSize; ++n) { expected[n] = new SimpleBuffer { Values = expected[n].Values + k_Offset } } ; for (int n = 0; n < bufferSize; ++n) { Assert.AreEqual(expected[n], ecsBuffer[n]); } } f.Set.Destroy(entityNode, dfgNode); } } }
public void CanConnect_ECSBuffer_ToECSBuffer_UsingOnlyComponentNodes_AndTransferData( [Values] NodeSet.RenderExecutionModel model, [Values(1, 3, 13, 50)] int bufferSize, [Values] ConnectionMode strongNess) { const int k_Loops = 10; using (var f = new Fixture <UpdateSystem>()) { f.Set.RendererModel = model; var entitySource = f.EM.CreateEntity(typeof(SimpleBuffer)); var entityDestination = f.EM.CreateEntity(typeof(SimpleBuffer)); var entityNodeSource = f.Set.CreateComponentNode(entitySource); var entityNodeDest = f.Set.CreateComponentNode(entityDestination); if (strongNess == ConnectionMode.Strong) { f.Set.Connect( entityNodeSource, ComponentNode.Output <SimpleBuffer>(), entityNodeDest, ComponentNode.Input <SimpleBuffer>() ); } else { f.Set.Connect( entityNodeSource, (OutputPortID)ComponentNode.Output <SimpleBuffer>(), entityNodeDest, (InputPortID)ComponentNode.Input <SimpleBuffer>() ); } var rng = new Mathematics.Random(0x8f); var ecsSourceBuffer = f.EM.GetBuffer <SimpleBuffer>(entitySource); var ecsDestBuffer = f.EM.GetBuffer <SimpleBuffer>(entityDestination); // match all buffer sizes ecsSourceBuffer.ResizeUninitialized(bufferSize); ecsDestBuffer.ResizeUninitialized(bufferSize); for (int i = 0; i < k_Loops; ++i) { ecsSourceBuffer = f.EM.GetBuffer <SimpleBuffer>(entitySource); for (int n = 0; n < bufferSize; ++n) { ecsSourceBuffer[n] = new SimpleBuffer { Values = rng.NextFloat3() }; } f.System.Update(); // This should fence on all dependencies ecsDestBuffer = f.EM.GetBuffer <SimpleBuffer>(entityDestination); //f.Set.DataGraph.SyncAnyRendering(); // TODO: can compare alias here for (int n = 0; n < bufferSize; ++n) { Assert.AreEqual(ecsSourceBuffer[n], ecsDestBuffer[n]); } } f.Set.Destroy(entityNodeSource, entityNodeDest); } }
public void CanWrite_ToECSBuffer_InsideFromDFG_FromOriginalECS_Source( [Values] NodeSet.RenderExecutionModel model, [Values(1, 3, 13, 50)] int bufferSize, [Values] ConnectionMode strongNess) { const int k_Loops = 10; using (var f = new Fixture <UpdateSystem>()) { f.Set.RendererModel = model; var entitySource = f.EM.CreateEntity(typeof(SimpleBuffer)); var entityDestination = f.EM.CreateEntity(typeof(SimpleBuffer)); var entityNodeSource = f.Set.CreateComponentNode(entitySource); var entityNodeDest = f.Set.CreateComponentNode(entityDestination); var dfgNode = f.Set.Create <BufferNode>(); var gv = f.Set.CreateGraphValue(dfgNode, BufferNode.KernelPorts.Output); if (strongNess == ConnectionMode.Strong) { f.Set.Connect(entityNodeSource, ComponentNode.Output <SimpleBuffer>(), dfgNode, BufferNode.KernelPorts.Input); f.Set.Connect(dfgNode, BufferNode.KernelPorts.Output, entityNodeDest, ComponentNode.Input <SimpleBuffer>()); } else { f.Set.Connect( entityNodeSource, (OutputPortID)ComponentNode.Output <SimpleBuffer>(), dfgNode, (InputPortID)BufferNode.KernelPorts.Input ); f.Set.Connect( dfgNode, (OutputPortID)BufferNode.KernelPorts.Output, entityNodeDest, (InputPortID)ComponentNode.Input <SimpleBuffer>() ); } var rng = new Mathematics.Random(0x8f); var ecsSourceBuffer = f.EM.GetBuffer <SimpleBuffer>(entitySource); var ecsDestBuffer = f.EM.GetBuffer <SimpleBuffer>(entityDestination); // match all buffer sizes ecsSourceBuffer.ResizeUninitialized(bufferSize); ecsDestBuffer.ResizeUninitialized(bufferSize); f.Set.SetBufferSize(dfgNode, BufferNode.KernelPorts.Output, Buffer <SimpleBuffer> .SizeRequest(bufferSize)); for (int i = 0; i < k_Loops; ++i) { ecsSourceBuffer = f.EM.GetBuffer <SimpleBuffer>(entitySource); for (int n = 0; n < bufferSize; ++n) { ecsSourceBuffer[n] = new SimpleBuffer { Values = rng.NextFloat3() }; } f.System.Update(); // This should fence on all dependencies ecsDestBuffer = f.EM.GetBuffer <SimpleBuffer>(entityDestination); // TODO: can compare alias here for (int n = 0; n < bufferSize; ++n) { Assert.AreEqual(ecsSourceBuffer[n], ecsDestBuffer[n]); } } f.Set.ReleaseGraphValue(gv); f.Set.Destroy(entityNodeSource, entityNodeDest, dfgNode); } }