Skip to content

Commit

Permalink
[VFX] Activation slots in blocks
Browse files Browse the repository at this point in the history
This PR implements the activation ports in block to allow a better/more convenient way to enable/disable blocks with graph.
Activation expressions can be evaluated on both CPU and GPU and works for spawner blocks. Also for compile time constant, an optimized path is used.

![activationslot](https://user-images.githubusercontent.com/22473360/137947511-7dbeb592-6c4f-4534-bf6b-93afc5c16902.png         )

In addition to the core feature, this PR also:

- Fixes some useless recompilation triggered when dragging an edge out of an output slot connected to some compilable system
- Add bool <> uint, int, float casts
- Optimize the reduction of logical expressions

Fixed https://fogbugz.unity3d.com/f/cases/1428639/
  • Loading branch information
julienf-unity committed May 23, 2022
1 parent 90dc5e8 commit cabd676
Show file tree
Hide file tree
Showing 54 changed files with 13,099 additions and 229 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -827,6 +827,12 @@ private static StringBuilder TryBuildFromShaderGraph(VFXShaderGraphParticleOutpu

private static void BuildBlock(VFXContextCompiledData contextData, List<VFXSlot> linkedEventOut, VFXShaderWriter blockFunction, VFXShaderWriter blockCallFunction, HashSet<string> blockDeclared, Dictionary<VFXExpression, string> expressionToName, VFXBlock block, ref int blockIndex)
{
// Check enabled state
VFXExpression enabledExp = contextData.gpuMapper.FromNameAndId(VFXBlock.activationSlotName, blockIndex);
bool needsEnabledCheck = enabledExp != null && !enabledExp.Is(VFXExpression.Flags.Constant);
if (enabledExp != null && !needsEnabledCheck && !enabledExp.Get<bool>())
throw new ArgumentException("This method should not be called on a disabled block");

var parameters = block.mergedAttributes.Select(o =>
{
return new VFXShaderWriter.FunctionParameter
Expand Down Expand Up @@ -863,19 +869,34 @@ private static void BuildBlock(VFXContextCompiledData contextData, List<VFXSlot>
commentMethod);
}

//< Parameters (computed and/or extracted from uniform)
var expressionToNameLocal = expressionToName;
bool needScope = parameters.Any(o => !expressionToNameLocal.ContainsKey(o.expression));
if (needScope)
bool needsEnabledScope = needsEnabledCheck && !expressionToNameLocal.ContainsKey(enabledExp);
bool hasParameterTransformation = parameters.Any(o => !expressionToNameLocal.ContainsKey(o.expression));
bool needsParametersScope = needsEnabledCheck || hasParameterTransformation;

if (needsEnabledScope || hasParameterTransformation)
{
expressionToNameLocal = new Dictionary<VFXExpression, string>(expressionToNameLocal);
}

if (needsEnabledScope)
{
blockCallFunction.EnterScope();
blockCallFunction.WriteVariable(enabledExp, expressionToNameLocal);
}

if (needsEnabledCheck)
{
blockCallFunction.WriteLineFormat("if ({0})", expressionToNameLocal[enabledExp]);
}

if (needsParametersScope)
{
blockCallFunction.EnterScope();
foreach (var exp in parameters.Select(o => o.expression))
{
if (expressionToNameLocal.ContainsKey(exp))
{
continue;
}
blockCallFunction.WriteVariable(exp, expressionToNameLocal);
}
}
Expand All @@ -885,7 +906,7 @@ private static void BuildBlock(VFXContextCompiledData contextData, List<VFXSlot>
{
if ((parameters[indexEventCount].mode & VFXAttributeMode.Read) != 0)
throw new InvalidOperationException(string.Format("{0} isn't expected as read (special case)", VFXAttribute.EventCount.name));
blockCallFunction.WriteLine(string.Format("{0} = 0u;", VFXAttribute.EventCount.GetNameInCode(VFXAttributeLocation.Current)));
blockCallFunction.WriteLineFormat("{0} = 0u;", VFXAttribute.EventCount.GetNameInCode(VFXAttributeLocation.Current));
}

blockCallFunction.WriteCallFunction(methodName,
Expand All @@ -902,7 +923,11 @@ private static void BuildBlock(VFXContextCompiledData contextData, List<VFXSlot>
blockCallFunction.WriteLineFormat("{0}_{1} += {2};", VFXAttribute.EventCount.name, VFXCodeGeneratorHelper.GeneratePrefix((uint)eventIndex), VFXAttribute.EventCount.GetNameInCode(VFXAttributeLocation.Current));
}
}
if (needScope)

if (needsParametersScope)
blockCallFunction.ExitScope();

if (needsEnabledScope)
blockCallFunction.ExitScope();

blockIndex++;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ public VFXExpressionMapper()

public void AddExpressionsFromSlotContainer(IVFXSlotContainer slotContainer, int blockId)
{
if (slotContainer.activationSlot != null)
AddExpressionsFromSlot(slotContainer.activationSlot, blockId);
foreach (var master in slotContainer.inputSlots)
AddExpressionsFromSlot(master, blockId);
}
Expand All @@ -55,6 +57,7 @@ public static VFXExpressionMapper FromBlocks(IEnumerable<VFXBlock> blocks)
int cpt = 0;
foreach (var block in blocks)
{
mapper.AddExpression(block.activationExpression, VFXBlock.activationSlotName, cpt);
mapper.AddExpressions(block.parameters, cpt++);
}
return mapper;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1237,6 +1237,9 @@ public void Compile(VFXCompilationMode compilationMode, bool forceShaderValidati

public void UpdateValues()
{
if (m_ExpressionGraph == null)
return;

var flatGraph = m_ExpressionGraph.FlattenedExpressions;
var numFlattenedExpressions = flatGraph.Count;

Expand Down
5 changes: 5 additions & 0 deletions Packages/com.unity.visualeffectgraph/Editor/Data/VFXData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ interface ISpaceable
VFXCoordinateSpace space { get; set; }
}

interface IVFXDataGetter
{
VFXData GetData();
}

abstract class VFXData : VFXModel
{
public abstract VFXDataType type { get; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,4 +190,104 @@ sealed public override string GetCodeString(string[] parents)
return string.Format("(int){0}", parents[0]);
}
}

class VFXExpressionCastIntToBool : VFXExpression
{
public VFXExpressionCastIntToBool() : this(VFXValue<bool>.Default) {}
public VFXExpressionCastIntToBool(VFXExpression from) : base(Flags.None, new VFXExpression[1] { from }) {}
sealed public override VFXExpressionOperation operation => VFXExpressionOperation.CastIntToBool;

sealed protected override VFXExpression Evaluate(VFXExpression[] reducedParents)
{
return VFXValue.Constant(reducedParents[0].Get<int>() != 0);
}

sealed public override string GetCodeString(string[] parents)
{
return string.Format("(bool){0}", parents[0]);
}
}

class VFXExpressionCastUintToBool : VFXExpression
{
public VFXExpressionCastUintToBool() : this(VFXValue<bool>.Default) { }
public VFXExpressionCastUintToBool(VFXExpression from) : base(Flags.None, new VFXExpression[1] { from }) { }
sealed public override VFXExpressionOperation operation => VFXExpressionOperation.CastUintToBool;

sealed protected override VFXExpression Evaluate(VFXExpression[] reducedParents)
{
return VFXValue.Constant(reducedParents[0].Get<uint>() != 0u);
}

sealed public override string GetCodeString(string[] parents)
{
return string.Format("(bool){0}", parents[0]);
}
}

class VFXExpressionCastFloatToBool : VFXExpression
{
public VFXExpressionCastFloatToBool() : this(VFXValue<bool>.Default) { }
public VFXExpressionCastFloatToBool(VFXExpression from) : base(Flags.None, new VFXExpression[1] { from }) { }
sealed public override VFXExpressionOperation operation => VFXExpressionOperation.CastFloatToBool;

sealed protected override VFXExpression Evaluate(VFXExpression[] reducedParents)
{
return VFXValue.Constant(reducedParents[0].Get<float>() != 0.0f);
}

sealed public override string GetCodeString(string[] parents)
{
return string.Format("(bool){0}", parents[0]);
}
}

class VFXExpressionCastBoolToInt : VFXExpression
{
public VFXExpressionCastBoolToInt() : this(VFXValue<int>.Default) { }
public VFXExpressionCastBoolToInt(VFXExpression from) : base(Flags.None, new VFXExpression[1] { from }) { }
sealed public override VFXExpressionOperation operation => VFXExpressionOperation.CastBoolToInt;

sealed protected override VFXExpression Evaluate(VFXExpression[] reducedParents)
{
return VFXValue.Constant(reducedParents[0].Get<bool>() ? 1 : 0);
}

sealed public override string GetCodeString(string[] parents)
{
return string.Format("(int){0}", parents[0]);
}
}
class VFXExpressionCastBoolToUint : VFXExpression
{
public VFXExpressionCastBoolToUint() : this(VFXValue<uint>.Default) { }
public VFXExpressionCastBoolToUint(VFXExpression from) : base(Flags.None, new VFXExpression[1] { from }) { }
sealed public override VFXExpressionOperation operation => VFXExpressionOperation.CastBoolToUint;

sealed protected override VFXExpression Evaluate(VFXExpression[] reducedParents)
{
return VFXValue.Constant(reducedParents[0].Get<bool>() ? 1u : 0u);
}

sealed public override string GetCodeString(string[] parents)
{
return string.Format("(uint){0}", parents[0]);
}
}
class VFXExpressionCastBoolToFloat : VFXExpression
{
public VFXExpressionCastBoolToFloat() : this(VFXValue<float>.Default) { }
public VFXExpressionCastBoolToFloat(VFXExpression from) : base(Flags.None, new VFXExpression[1] { from }) { }
sealed public override VFXExpressionOperation operation => VFXExpressionOperation.CastBoolToFloat;

sealed protected override VFXExpression Evaluate(VFXExpression[] reducedParents)
{
return VFXValue.Constant(reducedParents[0].Get<bool>() ? 1.0f : 0.0f);
}

sealed public override string GetCodeString(string[] parents)
{
return string.Format("(float){0}", parents[0]);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -848,6 +848,26 @@ sealed protected override string GetBinaryOperationCode(string left, string righ
{
return string.Format("{0} && {1}", left, right);
}

protected override VFXExpression Reduce(VFXExpression[] reducedParents)
{
var trueExp = VFXValue.Constant(true);
var falseExp = VFXValue.Constant(false);

// a && false == false && b == false
if (reducedParents[0].Equals(falseExp) || reducedParents[1].Equals(falseExp))
return falseExp;

// a && true == a
if (reducedParents[1].Equals(trueExp))
return reducedParents[0];

// true && b == b
if (reducedParents[0].Equals(trueExp))
return reducedParents[1];

return base.Reduce(reducedParents);
}
}

class VFXExpressionLogicalOr : VFXExpressionBinaryBoolOperation
Expand All @@ -869,6 +889,26 @@ sealed protected override string GetBinaryOperationCode(string left, string righ
{
return string.Format("{0} || {1}", left, right);
}

protected override VFXExpression Reduce(VFXExpression[] reducedParents)
{
var trueExp = VFXValue.Constant(true);
var falseExp = VFXValue.Constant(false);

// a || true == b || true == true
if (reducedParents[0].Equals(trueExp) || reducedParents[1].Equals(trueExp))
return trueExp;

// a || false == a
if (reducedParents[1].Equals(falseExp))
return reducedParents[0];

// false || b == b
if (reducedParents[0].Equals(falseExp))
return reducedParents[1];

return base.Reduce(reducedParents);
}
}

class VFXExpressionLogicalNot : VFXExpressionUnaryBoolOperation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -228,73 +228,25 @@ void OnParamSystemModified(VFXModel model, VFXModel.InvalidationCause cause)
{
if (isRecording)
{
if (cause == VFXModel.InvalidationCause.kParamChanged)
if (cause == VFXModel.InvalidationCause.kParamChanged || cause == VFXModel.InvalidationCause.kExpressionValueInvalidated)
{
if (model is VFXSlot)
{
var slot = model as VFXSlot;
if (slot.name == "bounds" || slot.name == "boundsPadding")
return;
foreach (var data in GetAffectedData(slot))

if (slot.owner is IVFXDataGetter)
{
var particleData = data as VFXDataParticle;
if (particleData != null)
if (((IVFXDataGetter)slot.owner).GetData() is VFXDataParticle particleData)
{
string systemName = m_Graph.systemNames.GetUniqueSystemName(particleData);
m_FirstBound[systemName] = true;
}
}
}
}

if (cause == VFXModel.InvalidationCause.kEnableChanged)
{
if (model is VFXBlock)
{
var block = model as VFXBlock;
var particleData = block.GetData();
if (particleData != null)
{
string systemName = m_Graph.systemNames.GetUniqueSystemName(particleData);
m_FirstBound[systemName] = true;
}
}
}
}
}

IEnumerable<VFXData> GetAffectedData(VFXSlot slot)
{
var owner = slot.owner;
var block = owner as VFXBlock;
if (block != null && block.enabled)
{
yield return block.GetData();
yield break;
}
var ctx = owner as VFXContext;
if (ctx != null)
{
yield return ctx.GetData();
yield break;
}

var op = owner as VFXOperator;
if (op != null)
{
var outSlots = op.outputSlots;
foreach (var outSlot in outSlots)
{
foreach (var linkedSlot in outSlot.LinkedSlots)
{
foreach (var data in GetAffectedData(linkedSlot))
{
yield return data;
}
}
}
}
//otherwise the owner is a VFXParameter, and we do not want to reset the bounds in this case
}

internal void UpdateBounds()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ namespace UnityEditor.VFX.UI
class VFXBlockController : VFXNodeController
{
VFXContextController m_ContextController;

public VFXBlockController(VFXBlock model, VFXContextController contextController) : base(model, contextController.viewController)
{
m_ContextController = contextController;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ static VFXBlock DuplicateBlock(VFXBlock block)

VFXBlock result = duplicated.OfType<VFXBlock>().First();

block.activationSlot.UnlinkAll(true, false);
foreach (var slot in result.inputSlots)
{
slot.UnlinkAll(true, false);
Expand Down
Loading

0 comments on commit cabd676

Please sign in to comment.