Unity DOTS中的baking(三)过滤baking的输出

Unity DOTS中的baking(三)过滤baking的输出

默认情况下,在conversation world(baker和baking system运行的环境)下产生的所有entities和components,都会作为baking环节的输出。在baking结束时,Unity必须将自上次baking以来发生变化的任何数据,都要复制到main world。不过,有些数据其实只在编辑时有用,我们希望可以在baking输出的时候将其舍弃掉。为此,Unity也提供了一些过滤的手段进行支持。

首先是属性BakingType,它用来标记一个component,使其只会存在于conversation world,不会输出到main world中。我们来看一个例子:

public class MyAuthoring : MonoBehaviour
{public int bakeIntData = 0;public ImageGeneratorInfo info;class MyBaker : Baker<MyAuthoring>{public override void Bake(MyAuthoring authoring){Debug.Log("==========================Bake Invoked!========================== " + authoring.name);DependsOn(authoring.info);if(authoring.info == null) return;//var transform = authoring.transform;var transform = GetComponent<Transform>();var entity = GetEntity(TransformUsageFlags.None);AddComponent(entity, new MyComponent {value = authoring.bakeIntData,spacing = authoring.info.Spacing,position = transform.position});AddComponent(entity, new MyComponentBakingType {value = authoring.bakeIntData});}}
}public struct MyComponent : IComponentData
{public int value;public float spacing;public float3 position;
}[BakingType]
public struct MyComponentBakingType : IComponentData
{public int value;
}

这个例子中,我们在Baker里为entity添加了两个component,其中MyComponentBakingType是一个标记了BakingType属性的component。那么,在conversation world下,可以看到这两个component都存在于entity上:

Unity DOTS中的baking(三)1

而在editor world下,就只剩下MyComponent这一个component了:

Unity DOTS中的baking(三)2

接下来我们来研究研究Unity在其背后做了哪些事情。首先对于BakingType属性,Unity在TypeManager中定义了一个与之匹配的常量:

/// <summary>
/// Bitflag set for component types decorated with the <seealso cref="BakingTypeAttribute"/> attribute.
/// </summary>
public const int BakingOnlyTypeFlag = 1 << 20;

然后在add component时,会对component所定义的属性进行判断:

bool isBakingOnlyType = Attribute.IsDefined(type, typeof(BakingTypeAttribute));
if (isBakingOnlyType)typeIndex |= BakingOnlyTypeFlag;

TypeManager还对外暴露了IsBakingOnlyType这一get属性:

public bool IsBakingOnlyType 
{[MethodImpl(MethodImplOptions.AggressiveInlining)] get { return  (Value & TypeManager.BakingOnlyTypeFlag) != 0; } 
}

这一get属性会在GatherComponentChangesBuildPackedGatherComponentChangesJob两个job中使用,它们用来收集发生变化的components,但包含BakingType属性的component会被过滤掉。这两个job由EntityManagerDiffer类触发:

/// <summary>
/// Generates a detailed change set for the world.
/// All entities to be considered for diffing must have the <see cref="EntityGuid"/> component with a unique value.
/// </summary>
/// <remarks>
/// The resulting <see cref="EntityChanges"/> must be disposed when no longer needed.
/// </remarks>
/// <param name="options">A set of options which can be toggled.</param>
/// <param name="allocator">The allocator to use for the results object.</param>
/// <returns>A set of changes for the world since the last fast-forward.</returns>
public EntityChanges GetChanges(EntityManagerDifferOptions options, AllocatorManager.AllocatorHandle allocator)
{var changes = EntityDiffer.GetChanges(ref m_CachedComponentChanges,srcEntityManager: m_SourceEntityManager,dstEntityManager: m_ShadowEntityManager,options,m_EntityQueryDesc,m_BlobAssetCache,allocator);return changes;
}

从代码中可以猜测出,这里的变化指的是conversation world和shadow world之间的diff,conversation world就是baker和baking system运行的地方,而shadow world则是上一次baking环节输出的拷贝,Unity使用这个shadow world,与当前baking的输出进行对比,只把不同的components和entities拷贝到main world,然后再更新shadow world为当前的conversation world。那么,既然代码中调用的两个job会把包含BakingType属性的component过滤掉,很明显最后输出到main world的components就不包含它们了。

除了BakingType属性之外,Unity还提供了TemporaryBakingType属性标记一个component。这两者有什么区别呢?Unity官方文档中给出了一段说明:

You can also exclude components with the following attributes:

  • [BakingType]: Filters any components marked with this attribute from the baking output.
  • [TemporaryBakingType]: Destroys any components marked with this attribute from the baking output. This means that components marked with this attribute don’t remain from one baking pass to the next, and only exist during the time that a particular baker ran.

可以得知,TemporaryBakingType属于那种阅后即焚的操作,拥有该属性的component,会在触发相应的baking时add到entity上,然后在baking结束时component就会被销毁,这意味着该component也不会一直存在于conversation world。之后,如果不再触发相应的baking,那么该component在conversation world里也不复存在。

这么说有点枯燥,我们还是用代码进行实验,在前面例子的基础上,新增定义一个component,然后在authoring Bake函数中add一下:

[TemporaryBakingType]
public struct MyComponentTempBakingType : IComponentData
{public int value;
}public override void Bake(MyAuthoring authoring)
{AddComponent(entity, new MyComponentTempBakingType{value = authoring.bakeIntData});
}

由于它在conversation world中也是转瞬即逝,我们没法直接在编辑器观察到它。这里需要借助一下BakingSystem,BakingSystem是一类只存在baking过程中的system,它负责把各种baker产生的输出做进一步处理,而我们这里就是要观察baker中TemporaryBakingType属性的component。

[WorldSystemFilter(WorldSystemFilterFlags.BakingSystem)]
[BurstCompile]
public partial struct MyAuthoringBakingSystem : ISystem
{EntityQuery m_query;[BurstCompile]public void OnCreate(ref SystemState state){m_query = SystemAPI.QueryBuilder().WithAll<MyComponentTempBakingType>().Build();state.RequireForUpdate(m_query);}[BurstCompile]public void OnUpdate(ref SystemState state){Debug.Log("=================update my authoring baking system===================");}
}

OnCreate会在system创建的时候执行一次,代码中就是获取当前包含MyComponentTempBakingTypecomponent的entity,如果存在这样的entity,则system才会执行OnUpdate。那么,根据我们之前的假设,这里的OnUpdate只会执行一次。实际上也是如此:

Unity DOTS中的baking(三)3

类似地,暗地里Unity在TypeManager中定义了一个flag常量:

/// <summary>
/// Bitflag set for component types decorated with the <seealso cref="TemporaryBakingTypeAttribute"/> attribute.
/// </summary>
public const int TemporaryBakingTypeFlag = 1 << 21;

然后对外暴露名为TemporaryBakingType的get属性:

/// <summary>
/// <seealso cref="TypeIndex.IsTemporaryBakingType"/>
/// </summary>
public bool TemporaryBakingType => IsTemporaryBakingType(TypeIndex);

最终这一属性会被Unity内部的BakingStripSystem所使用,在OnCreate时会创建所有带有该attribute的entity query:

protected override void OnCreate()
{var allTypes = TypeManager.AllTypes.Where(t => t.TemporaryBakingType).ToArray();m_BakingComponentQueries = new NativeArray<(ComponentType, EntityQuery)>(allTypes.Length, Allocator.Persistent);for(int i = 0; i < allTypes.Length; i++){var componentType = ComponentType.FromTypeIndex(allTypes[i].TypeIndex);EntityQueryDesc desc = new EntityQueryDesc(){All = new ComponentType[] {componentType},Options = EntityQueryOptions.IncludeDisabledEntities | EntityQueryOptions.IncludePrefab};m_BakingComponentQueries[i] = (componentType, GetEntityQuery(desc));}
}

然后每次update时,对符合条件的entity移除掉带有MyComponentTempBakingType属性的component:

protected override void OnUpdate()
{using (s_stripping.Auto()){foreach(var (componentType, query) in m_BakingComponentQueries){EntityManager.RemoveComponent(query, componentType);}}
}

BakingTypeMyComponentTempBakingType都是用来过滤component的,Unity还提供了一种过滤entity的方式,即给需要过滤掉的entity上添加一个名为BakingOnlyEntity的component。

AddComponent<BakingOnlyEntity>(entity);

这样这个entity就只会存在于conversation world中:

Unity DOTS中的baking(三)4

Unity DOTS中的baking(三)5

背后的实现也很简单,就是有一个专门处理这个component的system,筛选出符合条件的entity,把它们一一销毁掉:

protected override void OnCreate()
{_DestroyRemoveEntityInBake = new EntityQueryBuilder(Allocator.Temp).WithAny<RemoveUnusedEntityInBake, BakingOnlyEntity>().WithOptions(EntityQueryOptions.IncludeDisabledEntities | EntityQueryOptions.IncludePrefab).Build(this);
}protected override void OnUpdate()
{EntityManager.DestroyEntity(_DestroyRemoveEntityInBake);
}

Reference

[1] Filter baking output

[2] Baking worlds overview

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/452176.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【SpringBoot】权限系统与RBAC模型

&#x1f4dd;个页人主&#xff1a;五敷有你 &#x1f525;系列专栏&#xff1a;SpringBoot⛺️稳重求进&#xff0c;晒太阳 权限系统与RBAC模型 权限 为了解决用户和资源的操作关系&#xff0c; 让指定的用户&#xff0c;只能操作指定的资源。 权限功能 菜单权限&a…

【Java 数据结构】优先级队列(堆)

优先级队列&#xff08;堆&#xff09; 1. 优先级队列1.1 概念 2. 优先级队列的模拟实现2.1 堆的概念2.2 堆的存储方式2.3 堆的创建2.3.1 堆向下调整2.3.2 堆的创建2.3.3 建堆的时间复杂度 2.4 堆的插入与删除2.4.1 堆的插入2.4.2 堆的删除 2.5 用堆模拟实现优先级队列 3.常用…

JavaWeb01-JDBC、Druid连接池

目录 一、JDBC 1.概述 2.本质 3.好处 4.使用步骤 5.JDBC_API &#xff08;1&#xff09;DriverManager&#xff08;驱动管理类&#xff09; &#xff08;2&#xff09;Connection&#xff08;数据库连接对象&#xff09; &#xff08;3&#xff09;Statement &#xf…

Python爬虫urllib详解

前言 学习爬虫&#xff0c;最初的操作便是模拟浏览器向服务器发出请求&#xff0c;那么我们需要从哪个地方做起呢&#xff1f;请求需要我们自己来构造吗&#xff1f;需要关心请求这个数据结构的实现吗&#xff1f;需要了解 HTTP、TCP、IP 层的网络传输通信吗&#xff1f;需要知…

【Python基础021】Python中的何如实现文件的读写

Python中文件的读写在程序运行过程中是一个非常重要的操作&#xff0c;我们通常会将一些大量的临时数据暂时存放到一个临时文件&#xff0c;这就需要用到文件的读取与写入功能。话不多说&#xff0c;我们直接上才艺。 1、文本文件和二进制文件 讲文件读写前&#xff0c;先说说…

Leetcode 热门百题斩(第三天)

介绍 针对leetcode的热门一百题&#xff0c;解决大多数实习生面试的基本算法题。通过我自己的思路和多种方法&#xff0c;供大家参考。 1.两数之和&#xff08;题号&#xff1a;1) 方法一 最先想到的就是两个for去遍历匹配。 class Solution {public int[] twoSum(int[]…

进程中线程使用率偏高问题排查

1. top命令查看CPU使用率高的进程 2. top -H -p 15931(进程PID) 查看进程下的线程 3. printf "%x\n" 17503(线程PID) 线程PID 10进制转16进制 0x445f 4. jstack -l 15931(JVM进程PID) 导出java进程栈信息&#xff0c;里面包含线程nid0x445f和所在的类&#xff0…

ansible的常用模块配置说明及批量部署服务

ansible的常用模块配置说明及批量部署服务 ansible的常用模块配置说明&#xff0c;在远程服务器批量配置清华大学的仓库文件&#xff0c;批量部署nginx&#xff0c;并启动服务。 ansible知识点&#xff1a; 一、Ansible特点&#xff1a; 1、部署简单&#xff0c;只需在主控端…

生物发酵展同期论坛|2024节能环保绿色低碳发展论坛

“十四五”规划中提出&#xff0c;提高工业、能源领城智能化与信息 化融合&#xff0c;明确“低碳经济”新的战略目标&#xff0c;热能产业是能源产 业和民生保障的重要组成部分&#xff0c;也是二氧化碳排放量大的行业 之一&#xff0c;产业高效、清洁、低碳、灵活、智能化水平…

Rust 第一个rust程序Hello Rust️

文章目录 前言一、vscode 安装rust相关插件二、Cargo New三、vscode调试rustLLDB 前言 Rust学习系列。今天就让我们掌握第一个rust程序。Hello Rust &#x1f980;️。 在上一篇文章我们在macOS成功安装了rust。 一、vscode 安装rust相关插件 以下是一些常用的 Rust 开发插件…

Docker基础与持续集成

docker 基础知识&#xff1a; docker与虚拟机 !左边为虚拟机&#xff0c;右边为docker环境 – Server :物理机服务器Host OS &#xff1a;构建的操作系统Hypervisor &#xff1a;一种虚拟机软件&#xff0c;装了之后才能虚拟化操作系统Guest OS &#xff1a;虚拟化的操作系统…

AI应用开发-git开源项目的一些问题及镜像解决办法

AI应用开发相关目录 本专栏包括AI应用开发相关内容分享&#xff0c;包括不限于AI算法部署实施细节、AI应用后端分析服务相关概念及开发技巧、AI应用后端应用服务相关概念及开发技巧、AI应用前端实现路径及开发技巧 适用于具备一定算法及Python使用基础的人群 AI应用开发流程概…