Search Unity

Official New Feature : Direct Link

Discussion in 'Visual Effect Graph' started by PaulDemeulenaere, Jul 7, 2021.

  1. PaulDemeulenaere

    PaulDemeulenaere

    Unity Technologies

    Joined:
    Sep 29, 2016
    Posts:
    154
    Hello,

    I would like to follow up on this thread and I think it’s more appropriate to centralize a message in a dedicated post.

    We have some updates about these limitations, we recently added the support of “Direct Link” event (see this merge request if you are interested about the implementation details).

    First thing first, this feature is mainly addressed to advanced users who want more control on spawn events. For instance, you can be a gameplay programmer who is triggering a really custom amount of particles or a technical artist feeding the Visual Effect with custom data (which can’t be expressed as a simple texture or a point cache). Some minimal scripts are required for these scenarios and it isn’t a common case for the Visual Effect Graph.

    This feature has been available since 2021.2.0b2 with the embedded graphics packages and. The “Direct Link” is bypassing the spawn context constraints.

    We are still working on a general rework of the spawning system but meanwhile, we are also unlocking the ability of sending multiple events within the same frame from a script when an event is directly linked to an Initialize Context.

    Basic Usage
    Let me introduce this concept with an example, here, we have a quite complex spawn context chaining :
    image1.gif
    Every 0.2 seconds, it triggers three Spawn Context emitting a random amount of particles between 2 and 8.

    Now, let’s imagine the amount shouldn’t be random, but instead driven by the gameplay. In that case, the direct link is more general purpose for this kind of effect :
    image2.gif
    In this example, there is a MonoBehavior which is feeding the Initialize Context :
    Code (CSharp):
    1. //Spawn N Red
    2. m_EventAttribute.SetFloat(s_SpawnCountID, Random.Range(2.0f, 8.0f));
    3. m_EventAttribute.SetVector3(s_ColorID, new Vector3(1, 0, 0));
    4. m_TargetVisualEffect.SendEvent(s_FireID, m_EventAttribute);
    5. //Spawn N Green
    6. m_EventAttribute.SetFloat(s_SpawnCountID, Random.Range(2.0f, 8.0f));
    7. m_EventAttribute.SetVector3(s_ColorID, new Vector3(0, 1, 0));
    8. m_TargetVisualEffect.SendEvent(s_FireID, m_EventAttribute);
    9. //Spawn N Blue
    10. m_EventAttribute.SetFloat(s_SpawnCountID, Random.Range(2.0f, 8.0f));
    11. m_EventAttribute.SetVector3(s_ColorID, new Vector3(0, 0, 1));
    12. m_TargetVisualEffect.SendEvent(s_FireID, m_EventAttribute);
    In the end, even if the exact same effect was possible with the spawn event chaining, this new approach allows you to completely control the spawning from a script.

    Best practice
    Internally, the list of events will be aggregated into an array and sent to the GPU to finally be processed into a single dispatch. So, the best practice is always to limit the amount of SendEvent invocations.

    Let’s imagine you want to spawn 32 particles on a sphere in different places controlled by script, this is a common scenario when you can reduce the workload of transfer from CPU to GPU keeping the same result.

    The first naive approach would be to completely control the spawn by script :
    image4.gif
    With the corresponding code snippet :

    Code (CSharp):
    1. m_EventAttribute.SetFloat(s_SpawnCountID, (float)1.0f);
    2. for (int source = 0; source < spawnSources.Length; ++source)
    3. {
    4.     m_EventAttribute.SetVector3(s_ColorID, spawnSources[source].color);
    5.     for (int hit = 0; hit < number_of_particle; ++hit)
    6.     {
    7.         var particlePosion = spawnSources[source].position + Random.onUnitSphere * 0.5f;
    8.         m_EventAttribute.SetVector3(s_PositionID, particlePosion);
    9.         m_TargetVisualEffect.SendEvent(s_Solution_AID, m_EventAttribute);
    10.     }
    11. }
    12.  
    This solution is valid but it implies one unique event for every particle attribute.
    We can improve this approach moving the Random.onUnitSphere to the VFX Asset:
    image3.gif
    With the corresponding code snippet :
    Code (CSharp):
    1. m_EventAttribute.SetFloat(s_SpawnCountID, (float)number_of_particle);
    2. for (int source = 0; source < spawnSources.Length; ++source)
    3. {
    4.     m_EventAttribute.SetVector3(s_ColorID, spawnSources[source].color);
    5.     m_EventAttribute.SetVector3(s_PositionID, spawnSources[source].position);
    6.     m_TargetVisualEffect.SendEvent(s_Solution_BID, m_EventAttribute);
    7. }
    8.  
    Aside from having a simpler script, this second solution has divided by number_of_particle the number of SendEvent invocations, it will lead to less copy on the CPU and less transfer of data to the GPU.

    What do you think about this new solution ? Does it solve some pain points ?
    Feel free to share your impressions and don’t hesitate if you have any additional questions or if something isn’t clear.

    You can find all sources and assets used in these samples in the attached archive "vfx-direct-link-sample-master.zip", in can be opened with Unity 2021.2.0b2 or greater.
     

    Attached Files:

  2. Qriva

    Qriva

    Joined:
    Jun 30, 2019
    Posts:
    1,313
    I didn't test this, but it sounds good on paper.

    Additional questions


    1. What happens when I change graph properties between sending events?
    For example there is float property "ParticleSpeed" used by the graph, what will happen in this situation:
    Code (CSharp):
    1. for (int source = 0; source < spawnSources.Length; ++source)
    2. {
    3.     m_EventAttribute.SetVector3(s_ColorID, spawnSources[source].color);
    4.     m_EventAttribute.SetVector3(s_PositionID, spawnSources[source].position);
    5.     // Some special property
    6.     m_TargetVisualEffect.SetFloat("ParticleSpeed", Random.Range(0f, 10f));
    7.     m_TargetVisualEffect.SendEvent(s_Solution_BID, m_EventAttribute);
    8. }
    2. If my effect is a bit more complex, for instance explosion consisting of two systems running at the same time - one for shockwave or flares and one for explosion smoke particles, how would I control how many flares/waves and how many smokes are spawned. I would connect event directly to two initialize contexts, but number of particles would be common for both of them. The thing is I don't want as many smokes as flares, so how to handle this? My first guess is I would need to make separate events aka 'OnPlayExplosionFlares', 'OnPlayExplosionSmokes' etc...
     
    Last edited: Jul 7, 2021
  3. PaulDemeulenaere

    PaulDemeulenaere

    Unity Technologies

    Joined:
    Sep 29, 2016
    Posts:
    154
    That's a good question, this behavior isn't really clear in documentation yet. In your case, only the last SetFloat will be taken into account, Unity isn't saving the whole property sheet between each event, and since all these events will be processed within the same dispatch, it's always a simple unique material property.

    Let me clarify this with an example :
    Code (CSharp):
    1. //Using exposed property (fire_A)
    2. m_TargetVisualEffect.SetVector3(s_ExposedColorID, new Vector3(1, 0, 0));
    3. m_TargetVisualEffect.SendEvent(s_FireAID, m_EventAttribute);
    4.  
    5. m_TargetVisualEffect.SetVector3(s_ExposedColorID, new Vector3(0, 1, 0));
    6. m_TargetVisualEffect.SendEvent(s_FireAID, m_EventAttribute);
    7.  
    8. m_TargetVisualEffect.SetVector3(s_ExposedColorID, new Vector3(0, 0, 1));
    9. m_TargetVisualEffect.SendEvent(s_FireAID, m_EventAttribute);
    10.  
    11. //Using event attribute data (fire_B)
    12. //Spawn 1 Red
    13. m_EventAttribute.SetVector3(s_ColorID, new Vector3(1, 0, 0));
    14. m_TargetVisualEffect.SendEvent(s_FireBID, m_EventAttribute);
    15.  
    16. //Spawn 1 Green
    17. m_EventAttribute.SetVector3(s_ColorID, new Vector3(0, 1, 0));
    18. m_TargetVisualEffect.SendEvent(s_FireBID, m_EventAttribute);
    19.  
    20. //Spawn 1 Blue
    21. m_EventAttribute.SetVector3(s_ColorID, new Vector3(0, 0, 1));
    22. m_TargetVisualEffect.SendEvent(s_FireBID, m_EventAttribute);
    _answer_for_event_property.gif

    As you can notice, on the left, only the last SetVector3 with the value corresponding to blue is used.
    On the right, we are using event attributes data which can be independent per event, there is no need to create a new VFXEventAttribute for each SendEvent, the actual content is copied.

    You can find a package of this sample attached to this post.
     

    Attached Files:

    Fressbrett likes this.
  4. PaulDemeulenaere

    PaulDemeulenaere

    Unity Technologies

    Joined:
    Sep 29, 2016
    Posts:
    154
    If you are using "Direct Link" event, there is no way to override the spawn count before receiving it in Initialize Context. Maybe you could discard one of two particles in Initialize changing the alive state but this isn't very convenient, or you can separate events as you suggested.

    If you are using several "Spawn Context" as usual, you can author the number of emitted particules with different burst but you are constrained by the Spawn Context limitation (which is only carrying one event state per update).

    We are going to improve the support of this common scenario in further release of Unity (possibly with the Effect Instancing).
     
    Qriva likes this.
  5. Qriva

    Qriva

    Joined:
    Jun 30, 2019
    Posts:
    1,313
    That's what i thought - it would be nice in theory if it changed per event, but I understand how vfx graph works more or less and it would be difficult to implement. Anyways I think event attributes (or custom) should cover almost any case.

    I think the most common scenario everyone wish it worked (and the root of the problem) is to have one Visual Effect component with some effects like explosion, smoke, flash, whatever and just send event "spawn explosion here". The problem is it was possible, but only one explosion per frame = not usable in real world.
     
    Last edited: Jul 7, 2021
    Joshdbb, PhilSA and PaulDemeulenaere like this.
  6. PhilSA

    PhilSA

    Joined:
    Jul 11, 2013
    Posts:
    1,926
    @PaulDemeulenaere

    This is amazing, I was just recently tackling a situation where I wished I had this!

    There is only one thing that I think would still be missing for me and I was wondering if it would still be an issue now: I've noticed that SendEvent() can only be actually processed by the graph on the next frame. This often makes my VFX feel very visibly offsetted from the fast-moving objects that spawn them in worldspace, and it has been a big enough problem to make me have to find alternatives to using VFXGraph. Would there be any way to manually tell the graph to "process events & spawn particles right now"? Or maybe to change the update orders of things so that this is done after late update?
     
    Last edited: Jul 9, 2021
  7. PaulDemeulenaere

    PaulDemeulenaere

    Unity Technologies

    Joined:
    Sep 29, 2016
    Posts:
    154
    Thanks, I'm glad to know it will be used.

    There isn't any way to synchronously trigger an update for a Visual Effect component, SendEvent and Simulate are always pushed to a queue in order to be processed afterwards.

    However, the VFX.Update is actually processed right after the LateUpdate (see ExecutionOrder). In some cases, the authoring of the VisualEffect could be more appropriate in LateUpdate. For instance, the PropertyBinder is using the LateUpdate message to prevent the frame delay.
     
    Last edited: Jul 9, 2021
  8. PhilSA

    PhilSA

    Joined:
    Jul 11, 2013
    Posts:
    1,926
    I was referring to this bit of the VFXGraph doc:
    I'm not sure how things work internally so it's hard for me to understand what exactly causes this 1 frame delay for events, but that's the problem that I would need to solve in order to make this work for my project. I need to send a VFXGraph event either on Update() or on LateUpdate(), and have the particles spawned & rendered on that exact same frame. Otherwise I get this very visible delay/offset when spawning VFX: https://forum.unity.com/threads/vfxgraph-sendevent-delay-issue.1127279/

    It isn't impossible that I also reached a wrong conclusion due to a flawed test, but after seeing the comment in the docs about the 1 frame delay, I assumed the problem was indeed real
     
    Last edited: Jul 9, 2021
  9. PaulDemeulenaere

    PaulDemeulenaere

    Unity Technologies

    Joined:
    Sep 29, 2016
    Posts:
    154
    Hello,

    Sorry for the late answer, and after discussing with the team, I confirm this note is very confusing, we are currently editing this VFXGraph doc mention with something more appropriate :
    We also did a quick experiment to double check the expected behavior with this graph, it forces a particle to be visible only one frame :
    upload_2021-7-19_9-46-11.png

    I have a simple setup with a sphere position is driven by animation and another is driven by physics. I'm sending an event every frame
    If you are sending an event during the Update to synchronize a position you can notice the delay on the animated sphere :
    _using_update.gif

    It's expected because the animation is applied after the Update (see ExecutionOrder), it can be easily fix using `LateUpdate` instead :
    _using_late_update.gif

    As mentioned before, the PropertyBinder is using the LateUpdate for this exact reason.

    Let us know if you encounter any unexpected behavior.

    Thanks for pointing this wrong documentation out.
     

    Attached Files:

    PhilSA, fherbst and Onigiri like this.
  10. Qriva

    Qriva

    Joined:
    Jun 30, 2019
    Posts:
    1,313
    I have already expressed my opinion, but after rethinking I would like to add a few words.

    This solution for sure allows to implement what is needed, but looks "ugly". What I do not like in this feature is it makes VFX graph less modular and self contained.
    One of great advantages of VFX graph over shuriken is that I can create super complicated effect and it's boxed inside single asset what results in good readability and order. This solution moves part of effect logic beyond the graph (to C#), so it becomes more complicated and less user friendly, especially in the case 2 I described above (explosion).

    In short, if I could choose how to solve this problem I would prefer to be able to trigger event multiple times per frame and keep whole logic inside graph.
     
  11. PaulDemeulenaere

    PaulDemeulenaere

    Unity Technologies

    Joined:
    Sep 29, 2016
    Posts:
    154
    The direct link addition is only a niche improvement which isn't dedicated to be a general purpose approach.
    The scenario you described about several explosions could be addressed with the Effect Instancing, this feature will allow you to share the same simulation for several Visual Effect instances in the scene, it's a far more ambitious improvement.

    Beside that, even if it doesn't appear on the productboard, we are still working on a general rework of the spawning system which will address some of the areas you mentioned.

    Thanks for your feedback.
     
  12. Qriva

    Qriva

    Joined:
    Jun 30, 2019
    Posts:
    1,313
    I wanted to make a new thread with question, but as you mentioned it here I would like to ask if is there is any chance for instancing during 2021 cycle? If not, then does that mean we must wait another year to be truly released?

    Also after reading your description I am a bit confused - is effect instancing just some kind of batching or its more like faster way to instantiate and simulate the same instances?

    Would it be intended to spawn the same graph multiple times and run OnPlay on all of them?
    In my understanding it would make sense to keep just single instance and trigger play multiple times instead of spawning the same graph X times (unless it's a temporary solution).
     
  13. Roy-Hu

    Roy-Hu

    Joined:
    Apr 13, 2021
    Posts:
    7
    1.Will "direct link" be avaliable in unity 2020.3.x ?
    2. "single instance and trigger play multiple times", in this way, if the spawned particles spreading widely, the vfx object must have a big enough AABB, right ?
     
  14. PaulDemeulenaere

    PaulDemeulenaere

    Unity Technologies

    Joined:
    Sep 29, 2016
    Posts:
    154
    This feature won't be backported to 2021.2, the Unity release process is conservative. By the way, this improvement is still in development, we cannot provide an ETA at this time.

    To elaborate on this, right now every visual effect instance is independent and triggers a simulation dispatch resulting in inefficient GPU and CPU resources usage. In order to improve this, we are working on Effect Instancing which allows to batch instances of the Visual Effect asset. It's mainly a performance improvement which doesn't require any intervention from the user perspective.

    We are still working on a general rework of the spawning system, and this kind of improvement is something we also want to adress but this is not related to the instancing card.

    No, it's only available since 2021.2.0b2 and there isn't backport planned so far.

    Yes, in that case, you always have to consider your bounding boxes while spawning particles but it isn't really related to the "direct link" support. However, we recently improved the bounds authoring, you can check out this documentation about this workflow improvement.
     
    Last edited: Sep 16, 2021
    Roy-Hu, valarnur and Qriva like this.
  15. jjejj87

    jjejj87

    Joined:
    Feb 2, 2013
    Posts:
    1,117
    Hi, I am using Direct Link approach and was able to do it except rotation. What is the shader keyword for this? I thought it would be "angle" Vector3 but doesn't seem like it is picking up

    Code (CSharp):
    1. private static readonly int posID = Shader.PropertyToID("position");
    2.     private static readonly int rotID = Shader.PropertyToID("angle");
    3.  
    4. public void Play(Transform _transform)
    5.         {
    6.         if (att == null) att = fx.CreateVFXEventAttribute();
    7.         att.SetVector3(posID, _transform.position);
    8.         if (setY) att.SetVector3(rotID, _transform.eulerAngles);
    9.         if (useMultiple) att.SetFloat(spawnID, numToSpawnPerEvent);
    10.         fx.SendEvent(s_Solution_AID, att);
    11.         }
    Can you kindly help?

    nvm, looked at the generated code to find that it is angleX, angleY, angleZ in float.
     
    Last edited: Jul 13, 2022
    nazaroth likes this.
  16. VladVNeykov

    VladVNeykov

    Unity Technologies

    Joined:
    Sep 16, 2016
    Posts:
    550
    Hi, glad you found it! :) To make this easier in the future, if you can enable this option in the Preferences tab:


    You can select any block and see what per-particle attributes, block-specific parameters, and source code the block is using / producing:
     

    Attached Files:

  17. jjejj87

    jjejj87

    Joined:
    Feb 2, 2013
    Posts:
    1,117
    That is nice! Didn't know that this was possible! Thanks!
     
    nazaroth and VladVNeykov like this.
  18. Fressbrett

    Fressbrett

    Joined:
    Apr 11, 2018
    Posts:
    97
    There seems to be a bug when firing multiple events with differing EventAttributes within the same frame:
    As soon as there is a Spawn block in between the Event block and the Initialize block, only the last particle will appear.
    As soon as the Spawn block is removed, i.e. the Event propagates directly into the Initialize block, both particles appear as they should.
    Code (CSharp):
    1.             _eventAttribute.SetVector3("position", position1);
    2.             _explosionVFX.SendEvent("ExplosionStart", _eventAttribute);
    3.  
    4.             _eventAttribute.SetVector3("position", position2);
    5.             _explosionVFX.SendEvent("ExplosionStart", _eventAttribute);
    This only causes the particle at "position2" to appear:


    This causes both particles, at "postion1" and "position2" to appear:



    I am using Unity 2021.2.12f1 and VisualEffectGraph 12.1.4.
     
  19. Qriva

    Qriva

    Joined:
    Jun 30, 2019
    Posts:
    1,313
    Direct link is supposed to be solution to that problem.

    In previous years the problem was that spawn system could process only single event and this is why they made direct link - to allow you to spawn particles directly. In short this is intended behaviour.
    However I hope they finally implement multiple events with spawn context.
     
    PaulDemeulenaere likes this.
  20. Fressbrett

    Fressbrett

    Joined:
    Apr 11, 2018
    Posts:
    97
    Okay, I understand. However, what if my Spawn block would usually instantiate say 300 particles? With direct link I now have to fire my event 300 times, which sounds like significant CPU to GPU transfer overhead. Thus I second your wish for multiple events with spawn context
     
  21. PaulDemeulenaere

    PaulDemeulenaere

    Unity Technologies

    Joined:
    Sep 29, 2016
    Posts:
    154
    Hello,
    Indeed, the direct link is way to avoid the current limitation from the spawn context behavior which can't handle multiple events within the same frame.

    Actually, as previously mentioned in the very first post (see "Best Practice"), you can send a single event with a larger spawn count than one:
    Code (CSharp):
    1. _eventAttribute.SetVector3("position", position1);
    2. _eventAttribute.SetFloat("spawnCount", 300.0f);
    3. _explosionVFX.SendEvent("ExplosionStart", _eventAttribute);
    I hope that helps.
     
    Fressbrett likes this.
  22. Fressbrett

    Fressbrett

    Joined:
    Apr 11, 2018
    Posts:
    97
    Got it. I was a bit confused as to what the variables you used within your Best Practices example meant (
    s_SpawnCountID
    in this case). But following through the responses and enabling the Debug mode for VFX revealed that these are just identifiers for specific keywords within the VFX Graph.

    I would love if this behaviour is documented somewhere. Maybe I just haven't found it, the only pointer to what I wanted to achieve was this thread.