ECS 四 Sync Point、Write Group、Version Number

一:Sync points:同步点

所有造成原型结构变化的点都是同步点,同步点就是需要在所有workthread 上面同步当前数据状态的地方,在所有job 完成之后,在main thread 中进行,这也就是在job 中,不能改变stuctural

1.1 Structural changes:结构型变化

下面的操作会造成structural 改变:

  • Creating entities
  • Deleting entities
  • Adding components to an entity
  • Removing components from an entity
  • Changing the value of shared components:改变sharecomponent的值,而不是改变普通组件的值

上面的这些操作,只能在main thread 中执行。

structural changes 不仅会造成一个sync point ,同时之前对组件的引用也无效了,因为数据改变了,包括 DynamicBuffer 和 之前获取的组件缓存,比如 ComponentSystemBase.GetComponentDataFromEntity.

1.2 Avoiding sync points

使用entity command buffers (ECBs) 缓存要进行的structural changes ,在一帧固定的时间统一执行,来减少sync point。

如果不适用ECB,你只能在主线程中执行  structural changes ,它其实要比在job 中记录,然后执行要快,是直接通过 EntityManager 类执行。

如果不使用 EntityCommandBufferSystem ,可以尝试自己定义一个group,然后在把 执行 structural changes 的system  放在一起执行,相邻的两个system 只会产生一次sync point 。

See Entity Command Buffers for more information about using command buffers and command buffer systems.

二:Write groups

write group 属性可以让一个system修改另一个system修改过后的组件。

write groups 可以排除那些含有该组件的实体,用额外的逻辑处理它们。

使用write groups, 必须使用 write group filter option 来在查询中排除那些包含该组件的实体.

2.1 Write groups example

 HealthComponent and ColorComponent:根据血量改变颜色:

public struct HealthComponent : IComponentData
{
   public int Value;
}

public struct ColorComponent : IComponentData
{
   public float4 Value;
}

除此之外,需要两个system:

  1. The ComputeColorFromHealthSystem, which reads from HealthComponent and writes to ColorComponent
  2. The RenderWithColorComponent, which reads from ColorComponent

当吃血包和变无敌的时候,实体需要挂上 InvincibleTagComponent 该组件,这时候应该变成一个独立的颜色,不受上面机制的影响。

[WriteGroup(typeof(ColorComponent))]
struct InvincibleTagComponent : IComponentData {}

 上面的意思为  声明一个ColorComponent 的write group,它包含所有拥有WriteGroup( typeof(ColorComponent) )属性的组件:

  1. The ComputeColorFromHealthSystem 必须明确的声明 write groups. 则需要在查询时使用 EntityQueryOptions.FilterWriteGroup.

 ComputeColorFromHealthSystem :

...
protected override void OnUpdate() {
   Entities
      .WithName("ComputeColor")
      .WithEntityQueryOptions(EntityQueryOptions.FilterWriteGroup) // support write groups
      .ForEach((ref ColorComponent color, in HealthComponent health) => {
         // compute color here
      }).ScheduleParallel();
}
...

When this executes, the following happens:

  1. ref  修饰ColorComponent 表示可读写,in 表示只读
  2. 查询 ColorComponent的write group 发现 InvincibleTagComponent 包含在里面
  3. 排除所有含 InvincibleTagComponent 组件的实体

它的作用是 排除掉那些含有该system 不知道的组件的实体

NOTE

For more examples, see the Unity.Transforms code, which uses write groups for every component it updates, including LocalToWorld.

2.2Creating write groups

添加write group 属性 ,参数是一个组件类型,表示组里的组件依赖于该组件进行更新,一个组可以包含多个组件。

比如你想在A或B组件有一个的时候写入W组件:

public struct W : IComponentData
{
   public int Value;
}

[WriteGroup(typeof(W))]
public struct A : IComponentData
{
   public int Value;
}

[WriteGroup(typeof(W))]
public struct B : IComponentData
{
   public int Value;
}

NOTE

You do not add the target of the write group (component W in the example above) to its own write group.

2.3 Enabling write group filtering

To enable write group filtering, set the FilterWriteGroups flag on your job:

public class AddingSystem : SystemBase
{
   protected override void OnUpdate() {
      Entities
          // support write groups by setting EntityQueryOptions
         .WithEntityQueryOptions(EntityQueryOptions.FilterWriteGroup) 
         .ForEach((ref W w, in B b) => {
            // perform computation here
         }).ScheduleParallel();}
}

For query description objects, set the flag when you create the query:

public class AddingSystem : SystemBase
{
   private EntityQuery m_Query;

   protected override void OnCreate()
   {
       var queryDescription = new EntityQueryDesc
       {
           All = new ComponentType[] {
              ComponentType.ReadWrite<W>(),
              ComponentType.ReadOnly<B>()
           },
           Options = EntityQueryOptions.FilterWriteGroup
       };
       m_Query = GetEntityQuery(queryDescription);
   }
   // Define IJobChunk struct and schedule...
}

当开启option filter 的时候,默认把组里的组件,添加到none list 里面,除非在all 里面显示的声明它。

In the example code above, the query:

  • Excludes any entity that has component A, because W is writable and A is part of the write group of W.
  • Does not exclude any entity that has component B. Even though B is part of the write group of W, it is also explicitly specified in the All list.

2.4 Overriding another system that uses write groups

For example, if you want to set the orientation of your entities by specifying the angle and axis of rotation, you can create a component and a system to convert the angle and axis values into a quaternion and write that to the Unity.Transforms.Rotation component. To prevent the Unity.Transforms systems from updating Rotation, no matter what other components besides yours are present, you can put your component in the write group of Rotation:

using System;
using Unity.Collections;
using Unity.Entities;
using Unity.Transforms;
using Unity.Mathematics;

[Serializable]
[WriteGroup(typeof(Rotation))]
public struct RotationAngleAxis : IComponentData
{
   public float Angle;
   public float3 Axis;
}

You can then update any entities with the RotationAngleAxis component without contention:

using Unity.Burst;
using Unity.Entities;
using Unity.Jobs;
using Unity.Collections;
using Unity.Mathematics;
using Unity.Transforms;

public class RotationAngleAxisSystem : SystemBase
{
   protected override void OnUpdate()
   {
      Entities.ForEach((ref Rotation destination, in RotationAngleAxis source) =>
      {
         destination.Value 
             = quaternion.AxisAngle(math.normalize(source.Axis), source.Angle);
      }).ScheduleParallel();
   }
}

2.5 Extending another system that uses write groups

NOTE

You can use the Any clause of the query when appropriate.

var query = new EntityQueryDesc
{
    All = new ComponentType[] {
       ComponentType.ReadOnly<C>(), 
       ComponentType.ReadWrite<W>()
    },
    Any = new ComponentType[] {
       ComponentType.ReadOnly<A>(), 
       ComponentType.ReadOnly<B>()
    },
    Options = EntityQueryOptions.FilterWriteGroup
};

三: Version numbers

版本号(也称为generation)用来检测潜在的变化。可以使用它们来实现有效的优化策略,例如,当数据从最后一帧后没有更改时,可以跳过处理。对实体执行快速版本检查以提高性能。

本页面概述了ECS使用的version number,以及它们发生变化的条件。

所有的version numbers 都是32位的整数. 

例如,检查VersionB是否比VersionA更新的正确方法是:

bool VersionBIsMoreRecent = (VersionB - VersionA) > 0;

通常无法保证增加的版本号是多少。

3.1 EntityId.Version

EntityId 有一个索引和一个version number组成,因为ECS 重复利用索引值,version number 在EntityManager中实体每次销毁的时候都会增加 . 如果在EntityManager中查找EntityId时version number号不匹配,这意味着引用的实体不再存在。

比如通过EntityId来查找一个实体,可以使用 ComponentDataFromEntity.Exists.它就是通过version number 来检查实体是否还存在 。

3.2 World.Version

ECS increases the version number of a World every time it creates or destroys a manager (i.e. system).

3.3 EntityDataManager.GlobalVersion

EntityDataManager.GlobalVersion 在每一个 job component system 更新之前自增.

和 System.LastSystemVersion.结合使用

3.6 System.LastSystemVersion

System.LastSystemVersion 取 EntityDataManager.GlobalVersion 的值,在每一个 job component system 更新之后.

应该和 Chunk.ChangeVersion[].结合使用

3.7 Chunk.ChangeVersion

它保留了原型里面组件自从上次以可写入访问后的EntityDataManager.GlobalVersion 值,不管组件有没有真正的被改变。

你不能以可写入的状态访问shared components 。尽管它里面也存储了关于share component 的版本号。没有任何意义。

当在查询中使用 WithChangeFilter() 方法时,ECS 比较组件的 Chunk.ChangeVersion 和 System.LastSystemVersion,,它仅仅遍历那些自从system 最后一次运行后,组件类型以可写入状态访问的chunk 。

可以跳过那些没有改变的实体。

EntityManager.m_ComponentTypeOrderVersion[]

For each non-shared component type, ECS increases the version number every time an iterator involving that type becomes invalid. In other words, anything that might modify arrays of that type (not instances).

For example, if you have static objects that a particular component identifies, and a per-chunk bounding box, you only need to update those bounding boxes if the type order version changes for that component.

SharedComponentDataManager.m_SharedComponentVersion[]

These version numbers increase when any structural change happens to the entities stored in a chunk that reference the shared component.

For example, if you keep a count of entities per shared component, you can rely on that version number to only redo each count if the corresponding version number changes.

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TO_ZRG

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值