Skip to content

[VFX] Crash with subgraph #5104

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 51 commits into from
Aug 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
01e8e42
Add regression test on basic sanitize
PaulDemeulenaere Jun 17, 2021
43b4832
Move SanitizeForImport
PaulDemeulenaere Jun 24, 2021
e228f63
*Minor : Missing InvariantCultureIgnoreCase flag in EndsWith
PaulDemeulenaere Jun 24, 2021
4d22c94
*Update changelog.md
PaulDemeulenaere Jun 24, 2021
99ab5fb
*Fix HDRP warning
PaulDemeulenaere Jun 24, 2021
d0d9438
Revert "*Fix HDRP warning"
PaulDemeulenaere Jun 25, 2021
cbfa99c
More conservative way of workaround Preprocess issue
PaulDemeulenaere Jun 27, 2021
508d713
Remove DestroyImmediate
PaulDemeulenaere Jun 30, 2021
1fb0835
Revert "Remove DestroyImmediate "
PaulDemeulenaere Jun 30, 2021
ff51dcf
Add HDRPUserSettings
PaulDemeulenaere Jun 30, 2021
6c5f90b
Revert "Add HDRPUserSettings"
PaulDemeulenaere Jun 30, 2021
a569cbe
Force m_WizardPopupAtStart to false
PaulDemeulenaere Jun 30, 2021
4f90950
Merge branch 'master' into vfx/fix/1344645-sanitize-failure
PaulDemeulenaere Jun 30, 2021
ded88dc
Improvement of this workaround after discussion with @julienf
PaulDemeulenaere Jul 1, 2021
9e94d07
Minor : Editor Test cleanup
PaulDemeulenaere Jul 2, 2021
8d4cdec
Gather all ImportAsset after all Sanitize from the same batch
PaulDemeulenaere Jul 2, 2021
f5a6be5
Minor changes : Change log & Safe execption handling
PaulDemeulenaere Jul 7, 2021
d17ddef
Merge branch 'master' into vfx/fix/1344645-sanitize-failure
PaulDemeulenaere Jul 7, 2021
2d1fa12
Add regression test (which is expecting to crash on master)
PaulDemeulenaere Jul 8, 2021
c643c1e
Merge branch 'vfx/fix/1344645-sanitize-failure' into vfx/fix/1345426-…
PaulDemeulenaere Jul 8, 2021
5e6c3ea
Merge branch 'master' into vfx/fix/1344645-sanitize-failure
PaulDemeulenaere Jul 8, 2021
15059cf
Fix sanitize issue during the very first loading
PaulDemeulenaere Jul 9, 2021
8f944a6
Merge branch 'master' into vfx/fix/1344645-sanitize-failure
PaulDemeulenaere Jul 9, 2021
b0219a1
Merge branch 'vfx/fix/1344645-sanitize-failure' into vfx/fix/1345426-…
PaulDemeulenaere Jul 12, 2021
da1d40e
*Revert this* : OnSRPChanged & PrepareSubgraphs are public
PaulDemeulenaere Jul 12, 2021
a105d01
Not a valid fix : prepare subgraphs just before reimport
PaulDemeulenaere Jul 12, 2021
90ea420
Rename ToSubGraphOperator to ConvertToSubGraphOperator
PaulDemeulenaere Jul 12, 2021
ad447b1
Experiment : trying with a force double import
PaulDemeulenaere Jul 12, 2021
bbcf70d
Test : Don't know what PrepareSubgraph does better
PaulDemeulenaere Jul 12, 2021
2b8a971
Remove dirty PrepareSubgraphs in VFXViewWindow
PaulDemeulenaere Jul 12, 2021
2027002
*Add comment about reason behind PrepareSubgraphs requirement
PaulDemeulenaere Jul 12, 2021
0fb7316
*Update changelog
PaulDemeulenaere Jul 12, 2021
8e1c455
Remove unexpected public/private
PaulDemeulenaere Jul 12, 2021
8c442a8
*Minor* Replace `return` by `continue`
PaulDemeulenaere Jul 12, 2021
2c0bde7
Merge branch 'master' into vfx/fix/1344645-sanitize-failure
PaulDemeulenaere Jul 12, 2021
e19d404
Extend test & Add some notes
PaulDemeulenaere Jul 12, 2021
9ffde33
Remove ClearRuntimeData
PaulDemeulenaere Jul 12, 2021
6dc0d56
*Add note about reason of failure for playmodeXR
PaulDemeulenaere Jul 13, 2021
3aa94cc
Merge branch 'master' into vfx/fix/1344645-sanitize-failure
PaulDemeulenaere Jul 13, 2021
d726339
Merge branch 'vfx/fix/1344645-sanitize-failure' into vfx/fix/1345426-…
PaulDemeulenaere Jul 13, 2021
d2eb21c
Simpler solution : Use backup system instead of reimporting twice
PaulDemeulenaere Jul 13, 2021
da7d41e
Restore test which wasn't working before
PaulDemeulenaere Jul 13, 2021
f47bbd7
Minor : update comment
PaulDemeulenaere Jul 13, 2021
404df89
Merge branch 'master' into vfx/fix/1345426-crash-with-subgraph
PaulDemeulenaere Jul 13, 2021
dec84fa
*Fix bad conflict resolution
PaulDemeulenaere Jul 13, 2021
2421bc5
Restored previously skipped PrepareSubgraphs
PaulDemeulenaere Jul 13, 2021
31f756c
Revert "Restored previously skipped PrepareSubgraphs"
PaulDemeulenaere Jul 13, 2021
29c18b7
Fix missing ResyncSlots on the very first creation of the subgraph
PaulDemeulenaere Jul 13, 2021
ba445f8
Merge branch 'master' into vfx/fix/1345426-crash-with-subgraph
PaulDemeulenaere Jul 28, 2021
76db742
Merge branch 'master' into vfx/fix/1345426-crash-with-subgraph
julienf-unity Aug 2, 2021
c0a3e72
Merge branch 'master' into vfx/fix/1345426-crash-with-subgraph
julienf-unity Aug 2, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using System.IO;
using UnityEditor.VFX.Block.Test;
using UnityEngine.UIElements;
using UnityEngine.TestTools;

namespace UnityEditor.VFX.Test
{
Expand All @@ -20,9 +21,13 @@ public class VFXControllersTests
VFXViewController m_ViewController;

const string testAssetName = "Assets/TmpTests/VFXGraph1.vfx";

const string testSubgraphAssetName = "Assets/TmpTests/VFXGraphSub.vfx";
const string testSubgraphSubAssetName = "Assets/TmpTests/VFXGraphSub_Subgraph.vfx";

const string testAssetMainSubgraph = "Assets/TmpTests/VFXGraphSubGraph_Main.vfx";
const string testSubgraphSubOperatorAssetName = "Assets/TmpTests/VFXGraphSub_Subgraph.vfxoperator";

private int m_StartUndoGroupId;

[SetUp]
Expand Down Expand Up @@ -53,7 +58,10 @@ public void DestroyTestAsset()
m_ViewController.useCount--;
Undo.RevertAllDownToGroup(m_StartUndoGroupId);
AssetDatabase.DeleteAsset(testAssetName);
AssetDatabase.DeleteAsset(testAssetName);
AssetDatabase.DeleteAsset(testAssetMainSubgraph);
AssetDatabase.DeleteAsset(testSubgraphAssetName);
AssetDatabase.DeleteAsset(testSubgraphSubAssetName);
AssetDatabase.DeleteAsset(testSubgraphSubOperatorAssetName);
}

#pragma warning disable 0414
Expand Down Expand Up @@ -979,6 +987,175 @@ public void Subgraph_Event_Link_To_Spawn()

window.graphView.controller = null;
}


//Regression test for case 1345426
[UnityTest]
public IEnumerator ConvertToSubGraphOperator()
{
var window = VFXViewWindow.GetWindow<VFXViewWindow>();

VisualEffectAsset asset = VisualEffectAssetEditorUtility.CreateNewAsset(testAssetMainSubgraph);
VisualEffectResource resource = asset.GetResource();
window.LoadAsset(AssetDatabase.LoadAssetAtPath<VisualEffectAsset>(testAssetMainSubgraph), null);

var viewController = VFXViewController.GetController(VisualEffectResource.GetResourceAtPath(testAssetMainSubgraph));

var graph = viewController.graph;
var add_A = ScriptableObject.CreateInstance<Operator.Add>();
graph.AddChild(add_A);
var add_B = ScriptableObject.CreateInstance<Operator.Add>();
graph.AddChild(add_B);
Assert.IsTrue(add_A.outputSlots[0].Link(add_B.inputSlots[1]));

//Simple compilable system
{
var spawnerContext = ScriptableObject.CreateInstance<VFXBasicSpawner>();

var spawnerInit = ScriptableObject.CreateInstance<VFXBasicInitialize>();
var spawnerOutput = ScriptableObject.CreateInstance<VFXPlanarPrimitiveOutput>();

var blockAttributeDesc = VFXLibrary.GetBlocks().FirstOrDefault(o => o.modelType == typeof(Block.SetAttribute));
var blockAttribute = blockAttributeDesc.CreateInstance();
blockAttribute.SetSettingValue("attribute", "position");
spawnerInit.AddChild(blockAttribute);

graph.AddChild(spawnerContext);
graph.AddChild(spawnerInit);
graph.AddChild(spawnerOutput);

spawnerInit.LinkFrom(spawnerContext);
spawnerOutput.LinkFrom(spawnerInit);
}

var initialize = viewController.graph.children.OfType<VFXBasicInitialize>().FirstOrDefault();
Assert.IsNotNull(initialize);
Assert.IsTrue(initialize.children.Any());

var allSlot = initialize.children.SelectMany(o => o.inputSlots);
var firstVector3 = allSlot.FirstOrDefault(o => o.property.type == typeof(Position));
Assert.IsNotNull(firstVector3);
Assert.IsTrue(add_B.outputSlots[0].Link(firstVector3[0][0]));
Assert.IsTrue(firstVector3.HasLink(true));
viewController.LightApplyChanges();

yield return null;

var controller = window.graphView.Query<VFXOperatorUI>().ToList().Where(t => t.controller.model is Operator.Add).Select(o => o.controller).Cast<Controller>();
Assert.AreEqual(2, controller.Count());
VFXConvertSubgraph.ConvertToSubgraphOperator(window.graphView, controller, Rect.zero, testSubgraphSubOperatorAssetName);

for (int i = 0; i < 32; ++i)
yield return null;

//Check the status of the newly integrated subgraph, expecting one output
var subgraph = viewController.graph.children.OfType<VFXSubgraphOperator>().FirstOrDefault();
Assert.IsNotNull(subgraph);
Assert.AreEqual(1, subgraph.outputSlots.Count);
Assert.IsFalse(subgraph.outputSlots.Any(s => s == null));

//If we reach here without any error or crash, the bug has been fixed
window.graphView.controller = null;
}

//Extension of previous test (related to 1345426) : create two outputs in subgraph (instead of one), revert and restore
[UnityTest]
public IEnumerator ConvertToSubGraphOperator_And_ModifySubgraph()
{
var previousTest = ConvertToSubGraphOperator();
while (previousTest.MoveNext())
yield return previousTest.Current;

var resource = VisualEffectResource.GetResourceAtPath(testSubgraphSubOperatorAssetName);
resource.WriteAsset();

var oneOutputState = File.ReadAllText(testSubgraphSubOperatorAssetName);
Assert.IsFalse(string.IsNullOrEmpty(oneOutputState));

resource = VisualEffectResource.GetResourceAtPath(testSubgraphSubOperatorAssetName);
Assert.IsNotNull(resource);
var window = VFXViewWindow.GetWindow<VFXViewWindow>();
window.LoadResource(resource, null);

var viewController = window.graphView.controller;
var graph = viewController.graph;
Assert.IsNotNull(graph);

var parameter = VFXLibrary.GetParameters().FirstOrDefault(o => o.model.type == typeof(Sphere));
Assert.IsNotNull(parameter);
var newParam = viewController.AddVFXParameter(Vector2.zero, (VFXModelDescriptorParameters)parameter);
newParam.isOutput = true;
var otherParamName = "programatically_new_name_test";
newParam.SetSettingValue("m_ExposedName", otherParamName);
viewController.ApplyChanges();

resource.WriteAsset();

yield return null;
window.Close();

var twoOutputState = File.ReadAllText(testSubgraphSubOperatorAssetName);
Assert.IsFalse(string.IsNullOrEmpty(twoOutputState));
Assert.AreNotEqual(oneOutputState, twoOutputState);
Assert.IsTrue(twoOutputState.Contains(otherParamName));

window.graphView.controller = null;
for (int i = 0; i < 16; ++i)
yield return null;

//Check the actual status, should have now two slots
{
AssetDatabase.ImportAsset(testAssetMainSubgraph);
var mainAsset = AssetDatabase.LoadAssetAtPath<VisualEffectAsset>(testAssetMainSubgraph);
var mainGraph = mainAsset.GetOrCreateResource().GetOrCreateGraph();

var subGraph = mainGraph.children.OfType<VFXSubgraphOperator>().FirstOrDefault();
Assert.IsNotNull(subGraph);
Assert.AreEqual(2, subGraph.outputSlots.Count);
Assert.IsFalse(subGraph.outputSlots.Any(o => o == null));
}
yield return null;

//Removing old slots, shouldn't get unexpected removed
{
File.WriteAllText(testSubgraphSubOperatorAssetName, oneOutputState);
AssetDatabase.Refresh();

AssetDatabase.ImportAsset(testAssetMainSubgraph);
var mainAsset = AssetDatabase.LoadAssetAtPath<VisualEffectAsset>(testAssetMainSubgraph);
var mainGraph = mainAsset.GetOrCreateResource().GetOrCreateGraph();

var subGraph = mainGraph.children.OfType<VFXSubgraphOperator>().FirstOrDefault();
Assert.IsNotNull(subGraph);
Assert.AreEqual(1, subGraph.outputSlots.Count);
Assert.IsFalse(subGraph.outputSlots.Any(o => o == null));
}
yield return null;

//Here, restore to the previous state, if one outputSlot is null, then we did a resync slot during compilation
{
File.WriteAllText(testSubgraphSubOperatorAssetName, twoOutputState);
AssetDatabase.Refresh();

AssetDatabase.ImportAsset(testAssetMainSubgraph);
var mainAsset = AssetDatabase.LoadAssetAtPath<VisualEffectAsset>(testAssetMainSubgraph);
var mainGraph = mainAsset.GetOrCreateResource().GetOrCreateGraph();

var subGraph = mainGraph.children.OfType<VFXSubgraphOperator>().FirstOrDefault();
Assert.IsNotNull(subGraph);
Assert.AreEqual(2, subGraph.outputSlots.Count);
Assert.IsFalse(subGraph.outputSlots.Any(o => o == null));
}
yield return null;

//Finally, open the source asset it will crash if the vfxasset is wrongly formed
resource = VisualEffectResource.GetResourceAtPath(testAssetMainSubgraph);
window = VFXViewWindow.GetWindow<VFXViewWindow>();
window.LoadResource(resource, null);

for (int i = 0; i < 16; ++i)
yield return null;
}
}
}
#endif
1 change: 1 addition & 0 deletions com.unity.visualeffectgraph/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Unexpected operator and block removal during migration [Case 1344645](https://issuetracker.unity3d.com/product/unity/issues/guid/1344645/)
- Inspector group headers now have a better indentation and alignment
- Zoom and warning icons were blurry in the "Play Controls" and "Visual Effect Model" scene overlays
- Random crash using subgraph [Case 1345426](https://issuetracker.unity3d.com/product/unity/issues/guid/1345426/)

## [11.0.0] - 2020-10-21
### Added
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ public static void ConvertToSubgraphContext(VFXView sourceView, IEnumerable<Cont
ctx.ConvertToSubgraphContext(sourceView, controllers, rect, path);
}

public static void ConvertToSubgraphOperator(VFXView sourceView, IEnumerable<Controller> controllers, Rect rect)
public static void ConvertToSubgraphOperator(VFXView sourceView, IEnumerable<Controller> controllers, Rect rect, string path = null)
{
var ctx = new Context();
ctx.ConvertToSubgraphOperator(sourceView, controllers, rect);
ctx.ConvertToSubgraphOperator(sourceView, controllers, rect, path);
}

public static void ConvertToSubgraphBlock(VFXView sourceView, IEnumerable<Controller> controllers, Rect rect)
Expand Down Expand Up @@ -251,19 +251,36 @@ public void ConvertToSubgraphContext(VFXView sourceView, IEnumerable<Controller>
UninitSmart();
}

public void ConvertToSubgraphOperator(VFXView sourceView, IEnumerable<Controller> controllers, Rect rect)
public void ConvertToSubgraphOperator(VFXView sourceView, IEnumerable<Controller> controllers, Rect rect, string path)
{
this.m_Rect = rect;
Init(sourceView, controllers);
if (!CreateUniqueSubgraph("SubgraphOperator", VisualEffectSubgraphOperator.Extension, VisualEffectAssetEditorUtility.CreateNew<VisualEffectSubgraphOperator>))
return;
if (path == null)
{
if (!CreateUniqueSubgraph("SubgraphOperator", VisualEffectSubgraphOperator.Extension, VisualEffectAssetEditorUtility.CreateNew<VisualEffectSubgraphOperator>))
return;
}
else
{
m_TargetSubgraph = VisualEffectAssetEditorUtility.CreateNew<VisualEffectSubgraphOperator>(path);
m_TargetController = VFXViewController.GetController(m_TargetSubgraph.GetResource());
m_TargetController.useCount++;
m_TargetControllers = new List<VFXNodeController>();
}
CopyPasteNodes();
m_SourceNode = ScriptableObject.CreateInstance<VFXSubgraphOperator>();
PostSetupNode();
m_SourceControllersWithBlocks = m_SourceControllers.Concat(m_SourceControllers.OfType<VFXContextController>().SelectMany(t => t.blockControllers));
TransferEdges();
TransfertOperatorOutputEdges();
Uninit();

//The PrepareSubgraphs was initially in compilation
//This change has been canceled to prevent creation of model in the wrong place
//Be sure the newly created operator has expected slot
var subGraphOperator = m_SourceNode as VFXSubgraphOperator;
subGraphOperator.RecreateCopy();
subGraphOperator.ResyncSlots(true);
}

List<VFXBlockController> m_SourceBlockControllers;
Expand Down
15 changes: 14 additions & 1 deletion com.unity.visualeffectgraph/Editor/Models/VFXGraph.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,20 @@ static void OnCompileResource(VisualEffectResource resource)
}
else
{
resource.GetOrCreateGraph().CompileForImport();
//Workaround, use backup system to prevent any modification of the graph during compilation
//The responsible of this unexpected change is PrepareSubgraphs => RecurseSubgraphRecreateCopy => ResyncSlots
//It will let the VFXGraph in a really bad state after compilation.
graph = resource.GetOrCreateGraph();
var dependencies = new HashSet<ScriptableObject>();
dependencies.Add(graph);
graph.CollectDependencies(dependencies);
var backup = VFXMemorySerializer.StoreObjectsToByteArray(dependencies.ToArray(), CompressionLevel.None);

graph.CompileForImport();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But still this means that CompileForImport can still trigger a SetDirty during compilation, which can potentially trigger, another useless import, or worse an infinite compile?

Copy link
Contributor Author

@PaulDemeulenaere PaulDemeulenaere Jul 15, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The dirty flag (which isn't serialized) about compilation is cleared at the end of compilation. See in CompileForImport :

m_ExpressionGraphDirty = false;
m_ExpressionValuesDirty = false;

The issue is due to preprocess we are doing before the actual compilation.
The backup doesn't restore these dirty flags.
Then, the SetDirty which can occurs (maybe ?) isn't detected/triggered, otherwise, I would have a infinite compilation in the editor test I introduced.


VFXMemorySerializer.ExtractObjects(backup, false);
//The backup during undo/redo is actually calling UnknownChange after ExtractObjects
//You have to avoid because it will call ResyncSlot
}
}
else
Expand Down