Unity DOTS中的baking(二)Baker的触发

Unity DOTS中的baking(二)Baker的触发

我们知道,当传入Baker的authoring component的值发生变化时,就会触发baking。不过在有些情况下,component所引用的对象没有变化,而是对象自身内部的一些属性发生了变化。这种情况下,是否会触发baking呢?我们来动手验证一下。

首先定义一个继承自ScriptableObject的ImageGeneratorInfo类:

[CreateAssetMenu(menuName = "ImageGeneratorInfo")]
public class ImageGeneratorInfo : ScriptableObject
{
    [Range(0.0f, 1.0f)]
    public float Spacing;
    public Mesh Mesh;
    public Material Material;
}

这样,我们就可以利用这个脚本创建asset了,随便给它塞点东西:

Unity DOTS中的baking(二)1

然后我们修改下baker脚本,在ECS Component中新增一个spacing,读取assets中的Spacing值:

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);

            if(authoring.info == null) return;

            var entity = GetEntity(TransformUsageFlags.None);
            AddComponent(entity, new MyComponent {
                value = authoring.bakeIntData,
                spacing = authoring.info.Spacing
            });
        }
    }
}

public struct MyComponent : IComponentData
{
    public int value;
    public float spacing;
}

我们在SubScene中新建一个空的GameObject,挂上MyAuthoring脚本,并在Info中设置上刚刚创建的ImageGeneratorInfo asset,随即就会触发baking:

Unity DOTS中的baking(二)2

Unity DOTS中的baking(二)3

此时,如果直接去修改asset本身,例如将asset的spacing参数设置为其他的值,并不会触发baking。这就意味着,转换后的Entity所拥有的MyComponent数据是错误的。对此,Unity官方文档给出了解释:

However, Unity doesn’t automatically track data from other sources, such as authoring components or assets. You need to add a dependency to the baker so it can track this kind of data. To do this, use the methods that the Baker class provides to access other components and GameObjects instead of the methods provided by the GameObject:

Unity只会自动地追踪依赖authoring component自身的变化,至于component所引用的资源,则需要使用DependsOn来显式定义依赖:

public override void Bake(MyAuthoring authoring)
{
    Debug.Log("==========================Bake Invoked!========================== " + authoring.name);

    DependsOn(authoring.info);

    if(authoring.info == null) return;

    var entity = GetEntity(TransformUsageFlags.None);
    AddComponent(entity, new MyComponent {
        value = authoring.bakeIntData,
        spacing = authoring.info.Spacing
    });
}

此时再修改Spacing参数,就会触发baking了:

Unity DOTS中的baking(二)4

这个DependsOn背后究竟做了什么呢?查看一下DependsOn的源码:

/// <summary>
/// This will take a dependency on Object of type T.
/// </summary>
/// <param name="dependency">The Object to take a dependency on.</param>
/// <typeparam name="T">The type of the object. Must be derived from UnityEngine.Object.</typeparam>
/// <returns>The Object of type T if a dependency was taken, null otherwise.</returns>
public T DependsOn<T>(T dependency) where T : UnityEngine.Object
{
    _State.Dependencies->DependResolveReference(_State.AuthoringSource.GetInstanceID(), dependency);

    // Transform component takes an implicit dependency on the entire parent hierarchy
    // since transform.position and friends returns a value calculated from all parents
    var transform = dependency as Transform;
    if (transform != null)
        _State.Dependencies->DependOnParentTransformHierarchy(transform);

    return dependency;
}

这里最关键的函数就是这个DependResolveReference了,再来看下它的源码:

public void DependResolveReference(int authoringComponent, UnityEngine.Object referencedObject)
{
    // Tricky unity details ahead:
    // A UnityEngine.Object might be
    //  - actual null (ReferenceEquals(referencedObject, null)  -> instance ID zero)
    //  - currently unavailable (referencedObject == null)
    //      - If it is unavailable, it might still have an instanceID. The object might for example be brought back through undo or putting the asset in the same path / guid in the project
    //        In that case it will be re-established with the same instanceID and hence we need to have a dependency on when an object
    //        that previously didn't exist now starts existing at the instanceID that previously mapped to an invalid object.
    //  - valid (referencedObject != null) (instanceID non-zero)
    var referencedInstanceID = ReferenceEquals(referencedObject, null) ? 0 : referencedObject.GetInstanceID();
    if (referencedInstanceID != 0)
    {
        AddObjectReference(referencedInstanceID);

        var obj = Resources.InstanceIDToObject(referencedInstanceID);
        var objTypeId = TypeManager.GetTypeIndex(referencedObject.GetType());
        AddObjectExist(new ObjectExistDependency { InstanceID = referencedInstanceID, exists = (obj != null), Type = objTypeId });

#if UNITY_EDITOR
        //@todo: How do we handle creation / destruction of assets / components?
        if (EditorUtility.IsPersistent(referencedObject))
            AddPersistentAsset(referencedObject.GetInstanceID());
#endif
    }
}

从源码的注释得知,Unity需要处理依赖的object是fake null(例如object引用为missing)的情况。ObjectExistDependency这个类记录了所引用的object对应的asset是否存在。当调用DependsOn时,就会新建一个该类的对象保存起来。之后,再检测到asset变化时,会触发CalculateObjectExistDiffsJob这个job。该job会获取记录的所有objects对应的asset当前存在的状态,与之前ObjectExistDependency保存时的状态进行比较,如果不一致,说明asset发生了变化(从无到有或者从有到无),需要重新进行baking:

// Resolve the objectIds (Get Object)
// Check if they are null (If they are null)
NativeArray<bool> objectExists = new NativeArray<bool>(objectIds.Length, Allocator.TempJob);

InstanceIDsToValidArrayMarker.Begin();
Resources.InstanceIDsToValidArray(objectIds.AsArray(), objectExists);
InstanceIDsToValidArrayMarker.End();

var diffJob = new CalculateObjectExistDiffsJob()
{
    objectExistDependencies = _StructuralObjectExistDependency,
    objectExists = objectExists,
    deduplicatedObjIds = deduplicatedObjIds,
    changedComponentsPerThread = changedComponentsPerThread
};
var diffJobHandle = diffJob.Schedule(DependenciesHashMapHelper.GetBucketSize(_StructuralObjectExistDependency), 64);

传入job的四个参数,objectExistDependencies表示之前记录的dependency,objectExists就是当前asset的状态,deduplicatedObjIds代表去重过的object instance id,changedComponentsPerThread顾名思义就是记录需要重新baking的authoring component。job具体执行的代码如下:

public void ProcessEntry(int threadIndex, in UnsafeParallelMultiHashMap<int, ObjectExistDependency> hashMap, in int key, in ObjectExistDependency value)
{
    // Add them if the exist state has changed (State has changed)
    int existsID = deduplicatedObjIds[value.InstanceID];
    if (value.exists != objectExists[existsID])
    {
        changedComponentsPerThread.Add(key, m_ThreadIndex);
        IncrementalBakingLog.RecordComponentBake(key, ComponentBakeReason.ObjectExistStructuralChange, value.InstanceID, value.Type);
    }
}

传入的key和value取自于objectExistDependencies这个数据结构,分别表示authoring component的instance id,以及记录的ObjectExistDependency对象。在解释完这些数据结构的含义之后,这里job执行的逻辑就很清晰了。

哎,等一下,这里到目前为止所说的,都是关于asset是否存在的依赖,那么asset本身是否修改,这里的依赖是在哪里注册的呢?实际上,还是在这个DependResolveReference函数里:

AddObjectReference(referencedInstanceID);

这个函数会把authoring component和所依赖的object instance id关联,最终保存到一个UnsafeParallelMultiHashMap<int, int>类型的_PropertyChangeDependency变量里。然后,当components,gameobjects,或是assets发生变化时,都会触发相应的job,对该dependency进行扫描。

JobHandle nonStructuralChangedComponentJobHandle = default;
if (incrementalConversionDataCache.ChangedComponents.Length > 0)
{
    var nonStructuralChangedComponentJob = new NonStructuralChangedComponentJob()
    {
        changedComponents = incrementalConversionDataCache.ChangedComponents,
        reversePropertyChangeDependency = _ReversePropertyChangeDependency,
        changedComponentsPerThread = changedComponentsPerThread
    };
    nonStructuralChangedComponentJobHandle = nonStructuralChangedComponentJob.Schedule(incrementalConversionDataCache.ChangedComponents.Length, 64, calculateGlobalReversePropertyJobHandle);
}

JobHandle nonStructuralChangedGameObjectPropertiesJobHandle = default;
if (incrementalConversionDataCache.ChangedGameObjectProperties.Length > 0)
{
    var nonStructuralChangedGameObjectPropertiesJob = new NonStructuralChangedGameObjectPropertiesJob()
    {
        changedGameObjects = incrementalConversionDataCache.ChangedGameObjectProperties,
        reversePropertyChangeDependency = _ReversePropertyChangeDependency,
        reverseGameObjectPropertyChangeDependency = _ReverseObjectPropertyDependency,
        changedComponentsPerThread = changedComponentsPerThread
    };
    nonStructuralChangedGameObjectPropertiesJobHandle = nonStructuralChangedGameObjectPropertiesJob.Schedule(incrementalConversionDataCache.ChangedGameObjectProperties.Length, 64, JobHandle.CombineDependencies(calculateGameObjectPropertyReverseJobHandle, calculateGlobalReversePropertyJobHandle));
}

JobHandle nonStructuralChangedAssetsJobHandle = default;
#if UNITY_EDITOR
if (incrementalConversionDataCache.ChangedAssets.Length > 0)
{
    var nonStructuralChangedAssetsJob = new NonStructuralChangedAssetsJob()
    {
        changedAssets = incrementalConversionDataCache.ChangedAssets,
        reversePropertyChangeDependency = _ReversePropertyChangeDependency,
        changedComponentsPerThread = changedComponentsPerThread
    };
    nonStructuralChangedAssetsJobHandle = nonStructuralChangedAssetsJob.Schedule(incrementalConversionDataCache.ChangedAssets.Length, 64, calculateGlobalReversePropertyJobHandle);
}
#endif

NonStructuralChangedComponentJob这个job为例:

public void Execute(int i)
{
    var component = changedComponents[i];
    changedComponentsPerThread.Add(component.instanceID, m_ThreadIndex);

    IncrementalBakingLog.RecordComponentBake(component.instanceID, ComponentBakeReason.ComponentChanged, component.instanceID, component.unityTypeIndex);
    IncrementalBakingLog.RecordComponentChanged(component.instanceID);

    foreach (var dep in reversePropertyChangeDependency.GetValuesForKey(component.instanceID))
    {
        changedComponentsPerThread.Add(dep, m_ThreadIndex);

        IncrementalBakingLog.RecordComponentBake(dep, ComponentBakeReason.GetComponentChanged, component.instanceID, component.unityTypeIndex);
    }
}

首先,肯定是将change component自身记录进来,它是需要重新baking的;然后,对当前记录的dependency进行扫描,取出依赖该change component的authoring components,它可能存在多个,需要进行遍历,一一记录。这样,所有依赖change component的autoring components全部都会重新baking。

此外,我们的authoring component也有可能需要获取自身GameObject上的某些component,例如我们在ECS Component中新增一个float3的position,读取transform上的position:

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 entity = GetEntity(TransformUsageFlags.None);
    AddComponent(entity, new MyComponent {
        value = authoring.bakeIntData,
        spacing = authoring.info.Spacing,
        position = transform.position
    });
}

public struct MyComponent : IComponentData
{
    public int value;
    public float spacing;
    public float3 position;
}

然而,直接使用authoring.transform获取到的transform,如果发生了变化,并不会重新触发baking。也就是说我们也需要手动指定依赖。Unity官方推荐使用GetComponent函数来获取component。

It is important to access other authoring components by using the GetComponent methods, because doing so registers dependencies. If we would have used authoring.transform here instead, a dependency would not have been registered, and moving the authoring GameObject while live baking would not rerun the baker as it should.

那我们就换用GetComponent<Transform>()来测试一下:

Unity DOTS中的baking(二)5

的确修改transform就会触发baking了。秉着知其然知其所以然的精神,我们来看看GetComponent背后的代码:

/// <summary>
/// Retrieves the component of Type T in the GameObject
/// </summary>
/// <param name="gameObject">The GameObject to get the component from</param>
/// <typeparam name="T">The type of component to retrieve</typeparam>
/// <returns>The component if a component matching the type is found, null otherwise</returns>
/// <remarks>This will take a dependency on the component</remarks>
private T GetComponentInternal<T>(GameObject gameObject) where T : Component
{
    var hasComponent = gameObject.TryGetComponent<T>(out var returnedComponent);

    _State.Dependencies->DependOnGetComponent(gameObject.GetInstanceID(), TypeManager.GetTypeIndex<T>(), hasComponent ? returnedComponent.GetInstanceID() : 0, BakeDependencies.GetComponentDependencyType.GetComponent);

    // Transform component takes an implicit dependency on the entire parent hierarchy
    // since transform.position and friends returns a value calculated from all parents
    var transform = returnedComponent as Transform;
    if (transform != null)
        _State.Dependencies->DependOnParentTransformHierarchy(transform);

    return returnedComponent;
}

不难发现关键代码就在DependOnGetComponent这个函数了:

public void DependOnGetComponent(int gameObject, TypeIndex type, int returnedComponent, GetComponentDependencyType dependencyType)
{
    if (returnedComponent != 0)
        AddObjectReference(returnedComponent);

    AddGetComponent(new GetComponentDependency {GameObject = gameObject, Type = type, ResultComponent = returnedComponent, DependencyType = dependencyType});
}

returnedComponent代表获取到的component的instance id,如果component存在,就需要追踪它本身是否发生变化,因此这里就和前面一样,使用AddObjectReference这个函数记录依赖。AddGetComponent会把要获取的GameObject,component,component的type index,以及依赖类型给记录下来,最终保存到_StructuralGetComponentDependency这个hashmap中。CalculateStructuralGetComponentDependencyJob会用到保存的数据,当检测到components,gameobjects,或是assets变化时,job就会触发。

public void ProcessEntry(int threadIndex, in UnsafeParallelMultiHashMap<int, GetComponentDependency> hashMap, in int key, in GetComponentDependency value)
{
    if (!value.IsValid(ref components, ref hierarchy))
    {
        changedComponentsPerThread.Add(key, m_ThreadIndex);
        IncrementalBakingLog.RecordComponentBake(key, ComponentBakeReason.GetComponentStructuralChange, value.ResultComponent, value.Type);
    }
}

从字面含义来看,只有GetComponentDependency类型的value判断为invalid时,才会把对应的key标记为需要重新baking。这里的key和value就是hashmap中的键值对,key表示authoring component,value里记录了当时使用GetComponent获取到的component依赖。那么,什么时候IsValid返回的是false呢?

public bool IsValid(ref GameObjectComponents components, ref SceneHierarchy hierarchy)
{
    switch (DependencyType)
    {
        case GetComponentDependencyType.GetComponentInParent:
            return GameObjectComponents.GetComponentInParent(ref components, ref hierarchy, GameObject, Type) == ResultComponent;
        case GetComponentDependencyType.GetComponent:
            return components.GetComponent(GameObject, Type) == ResultComponent;
        case GetComponentDependencyType.GetComponentInChildren:
            return GameObjectComponents.GetComponentInChildren(ref components, ref hierarchy, GameObject, Type) == ResultComponent;
    }
    return false;
}

这里的DependencyType为GetComponentDependencyType.GetComponent,可以看到这里会再去获取一下记录的GameObject身上最新的component,如果和之前记录的component instance id不同,说明component经历了从无到有或者从有到无的过程,它已经不是之前的那个对象了,此时就会返回false。总结一下,GetComponentInternal函数,不仅对component内部的值发生变化进行了依赖注册,还对component自身引用发生变化也进行了依赖注册。

如果DependsOn和GetComponent所依赖的component,它是个transform,Unity还会调用DependOnParentTransformHierarchy这个函数做一些额外的依赖工作。这是因为transform本身就不是独立的,它依赖整个parent hierarchy。这个事情是必要的,比如我们给当前挂有MyAuthoring脚本的GameObject建立一个父节点,如果父节点的transform发生变化,或者层级关系发生了变化,那么子节点的world transform必然也发生了变化,理应就要再次触发baking。

Unity DOTS中的baking(二)6

DependOnParentTransformHierarchy的实现如下:

public void DependOnParentTransformHierarchy(Transform transform)
{
    if (transform != null)
    {
        var hashGenerator = new xxHash3.StreamingState(false);
        GameObject go = transform.gameObject;
        int goInstanceID = go.GetInstanceID();

        // We take the dependency on the parent hierarchy.
        transform = transform.parent;
        while (transform != null)
        {
            hashGenerator.Update(transform.gameObject.GetInstanceID());

            AddObjectReference(transform.GetInstanceID());
            transform = transform.parent;
        }

        var hash = new Hash128(hashGenerator.DigestHash128());
        AddGetHierarchy(new GetHierarchyDependency {GameObject = goInstanceID, Hash = hash, DependencyType = GetHierarchyDependencyType.Parent});
    }
}

可以看到,Unity会对当前节点到根节点整个parent hierarchy进行扫描,对每个父节点的transform都建立了依赖关系,然后还记录了每个父节点GameObject的instance id,把它们汇总在一起生成一个128位的hash值。这个hash值就是用来标记当前的hierarchy。值得一提的是,Unity ECS 1.0.16版本之前的这段代码是有问题的,之前的版本也会把当前节点的instance id也统计进去,这点在Unity的changelog里有说明:

Changelog

[1.0.16] - 2023-09-11

Fixed

  • Fixed a hash mismatch on DependOnParentTransformHierarchy

之后,CalculateStructuralGetHierarchyDependencyJob这个job会使用之前记录的GetHierarchyDependency,判断hierarchy是否发生了变化(如果是1.0.16之前的版本,这里就必然会发生变化了,两边计算的方式压根就不一致)

public void ProcessEntry(int threadIndex, in UnsafeParallelMultiHashMap<int, GetHierarchyDependency> hashMap, in int key, in GetHierarchyDependency value)
{
    if (!value.IsValid(ref hierarchy))
    {
        changedComponentsPerThread.Add(key, m_ThreadIndex);
        IncrementalBakingLog.RecordComponentBake(key, ComponentBakeReason.GetHierarchyStructuralChange, 0, default);
    }
}

具体的判断逻辑位于IsValid

public Hash128 GetParentsHash(ref SceneHierarchy hierarchy, int instanceId)
{
    var hashGenerator = new xxHash3.StreamingState(false);

    if (hierarchy.TryGetIndexForInstanceId(instanceId, out int currentIndex))
    {
        while (currentIndex != -1)
        {
            int parentIndex = hierarchy.GetParentForIndex(currentIndex);
            if (parentIndex != -1)
            {
                int parentInstanceID = hierarchy.GetInstanceIdForIndex(parentIndex);
                hashGenerator.Update(parentInstanceID);
            }
            currentIndex = parentIndex;
        }
    }
    return new Hash128(hashGenerator.DigestHash128());
}

public bool IsValid(ref SceneHierarchy hierarchy)
{
    Hash128 returnValue = default;
    switch (DependencyType)
    {
        case GetHierarchyDependencyType.Parent:
            returnValue = GetParentsHash(ref hierarchy, GameObject);
            break;
        case GetHierarchyDependencyType.ImmediateChildren:
            returnValue = GetChildrenHash(ref hierarchy, GameObject, false);
            break;
        case GetHierarchyDependencyType.AllChildren:
            returnValue = GetChildrenHash(ref hierarchy, GameObject, true);
            break;
    }
    return (returnValue == Hash);
}

DependOnParentTransformHierarchy注册的dependency type为GetHierarchyDependencyType.Parent,因此这里计算出的hash值表示当前的parent hierarchy。如果两个hash值不同,说明两个hierarchy不同,那么authoring component需要重新运行baking。而且xxhash算法是一种非常快速的非加密哈希算法,它基本不可能发生碰撞,所以这里如果两个hash值相等,就可以认为两个hierarchy是完全相同的。

最后,我们来实际演示一下:

Unity DOTS中的baking(二)7

Reference

[1] Baker overview

[2] EntityComponentSystemSamples

[3] xxHash

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

七夕祭

title: 七夕祭 date: 2024-01-03 22:47:05 tags: 传送门 题目大意 解题思路 行的感兴趣的摊点或者列的感兴趣的摊点的数量能被行数或者列数整除&#xff0c;则能够实现要求。“均分”思想&#xff0c;设总感兴趣摊点数 T T T 和行数列数 n n n&#xff0c;当前感兴趣的摊点数…

Unity报错:InvalidOperationException: Insecure connection not allowed的解决方法

问题描述 在导入Steam VR 插件后报错&#xff1a; InvalidOperationException: Insecure connection not allowed UnityEngine.Networking.UnityWebRequest.SendWebRequest () (at <4139bb31c03640e7b650db6ec39d5754>:0) UnityEngine.WWW..ctor (System.String url) (…

13|代理(下):结构化工具对话、Self-Ask with Search以及Plan and execute代理

13&#xff5c;代理&#xff08;下&#xff09;&#xff1a;结构化工具对话、Self-Ask with Search以及Plan and execute代理 什么是结构化工具 LangChain 的第一个版本是在 2022 年 11 月推出的&#xff0c;当时的设计是基于 ReAct 论文构建的&#xff0c;主要围绕着代理和工…

xlrd.biffh.XLRDError: Can‘t find workbook in 0LE2 compound document

今天在运行之前可以正常运行的程序&#xff0c;解析excel文件&#xff0c;代码简单示例如下&#xff1a; import pandas as pddf pd.read_excel("F:\\1.xlsx")# 解析文件 不过&#xff0c;这次却遇到了一个问题&#xff0c;如下图&#xff1a; 第一次遇到这个错误…

Oracle VirtualBox中Linux系统基本使用方法——备赛笔记——2024全国职业院校技能大赛“大数据应用开发”赛项

前言 小北的这篇博客介绍的是关于用VirtualBox中下载好的ubuntu系统中&#xff0c;为搭建Hadoop平台所做的命令操作准备&#xff0c;希望能帮助不会的人有指引作用。 没有安装好VirtualBox中的ubuntu系统以及创建Hadoop账户的请参考小北之前的三篇博客&#xff1a; ubuntu18…

卷积神经网络|制作自己的Dataset

在编写代码训练神经网络之前&#xff0c;导入数据是必不可少的。PyTorch提供了许多预加载的数据集&#xff08;如FashionMNIST&#xff09;&#xff0c;这些数据集 子类并实现特定于特定数据的函数。 它们可用于对模型进行原型设计和基准测试&#xff0c;加载这些数据集是十分…

【PID精讲 13 】位置式PID和增量式PID

文章目录 位置式PID和增量式PID一、位置式PID控制算法二、增量式PID控制算法及仿真三、位置式与增量式优缺点 位置式PID和增量式PID 在计算机控制系统中&#xff0c;PID控制是通过计算机程序实现的&#xff0c;因此它的灵活性很大。一些原来在模拟PID控制器中无法实现的问题&a…

聚道云软件连接器带给服装行业客户的业务革新

【客户介绍】 某服装制作企业&#xff0c;专注于设计和生产各类服饰。追求时尚与品质的完美结合&#xff0c;以满足不同消费者的需求。凭借丰富的行业经验&#xff0c;该企业不断创新&#xff0c;致力于提供优质的服装产品和服务。我们的目标是成为消费者心目中的首选品牌&…

【Electron】快速建立Vue3+Vite+Electron Ts项目

git https://github.com/electron-vite/electron-vite-vue 创建项目 npm create electron-vite or pnpm create electron-vite 初始化 pnpm install or pnpm i 启动项目 pnpm dev 打包项目 pnpm build 项目创建成功后默认情况下 窗口是H800 W600 在createWindow 函数…

2.5 设计FMEA第五步:风险分析

2.5.1 目的 DFMEA风险分析的目的是&#xff1a;通过评估严重度&#xff08;S&#xff09;、频度(O)和探测度(D)来估计风险&#xff0c;进而对采取的措施进行优先级排序&#xff08;AP&#xff09;。 其主要目标包括&#xff1a; 针对失效&#xff0c;分配控制措施&#xff0…

人机交互不是人机融合智能

一、人机交互和人机融合智能是两个不同的概念 人机交互是指人类与计算机之间的信息交流和操作方式&#xff0c;包括输入和输出界面、交互技术、用户体验等方面。人机交互的目标是提供用户友好的界面和自然的交互方式&#xff0c;使人类能够与计算机更加高效地进行沟通和协作。 …

增删改查语句实现了解不同的函数与特殊字符unionunion all区别

一、crud&#xff08;增删改查&#xff09; 1.1、查询 概念&#xff1a; 查询数据是指从数据库中根据需求&#xff0c;使用不同的查询方式来获取不同的数据&#xff0c;是使用频率最高、最重要的操作 注&#xff1a;在MySQL中&#xff0c;当执行一条SQL语句后&#xff0c;系…

Java中的序列化方法探索

.为什么要序列化 对象不序列化&#xff0c;能直接存储吗&#xff1f; 在 Java 中&#xff0c;序列化是将对象的状态信息转换为可以存储或传输的形式&#xff08;例如&#xff0c;转换为字节流&#xff09;的过程。在对象数据需要在网络上传输或需要在磁盘上持久化存储时&#…

初始数字孪生

文章目录 概念定义 架构框图 基本要求 功能要求 服务应用 参考文献 概念定义 数字孪生&#xff08;digital twin&#xff09;&#xff0c;是指具有保证物理状态和虚拟状态之间以适当速率和精度同步的数据连接的特定目标实体的数字化表达。 架构框图 数字孪生的整体架构&a…

C# halcon 工业产品尺寸测量

产品检测 这段代码是一个基于HalconDotNet的Windows窗体应用程序&#xff0c;主要用于图像处理和测量。以下是对代码的一些总结&#xff1a; 1. **图像显示与加载&#xff1a;** - 使用HalconDotNet库进行图像处理。 - 通过OpenFileDialog实现图像文件的选择和加载。 …

Vue v-html中内容图片过大自适应处理

之前图片如下&#xff0c;图片已经超出了页面的展示范围 对v-html增加样式处理 <div class"body padding-l scroll " v-html"docData.content"> </div><style scoped>.body >>> img {max-width: 100% ;} </style>…

[书生·浦语大模型实战营]——书生·浦语大模型全链路开源体系

大模型成为发展通用人工智能的重要途径 书生浦语大模型开源历程 书生浦语模型性能 从模型到应用 应用例子&#xff1a;智能客服/个人助手/行业应用 实现流程&#xff1a; 开源开放体系&#xff1a; 1.数据——书生万卷 价值观对齐这个挺有意思嗷&#xff01; 2.预训练工具…

redis复习笔记02(小滴课堂)

分布式缓存Redis6常见核心配置讲解 查看配置文件&#xff1a; 创建配置文件&#xff1a; 配置完我们去验证一下&#xff1a; 启动成功就没有问题了。 可以看到redis日志。 然后我们就可以连接我们的redis了&#xff1a; 设置了密码就需要密码登录了。 如果登录了错误的密码也无…

搜索Agent方案

为啥需要整体方案&#xff0c;直接调用搜索接口取Top1返回不成嘛&#xff1f;要是果真如此Simple&Naive&#xff0c;New Bing岂不是很容易复刻->.-> 我们先来看个例子&#xff0c;前一阵火爆全网的常温超导技术&#xff0c;如果想回答LK99哪些板块会涨&#xff0c;你…

勒索检测能力升级,亚信安全发布《勒索家族和勒索事件监控报告》

评论员简评 近期(12.08-12.14)共发生勒索事件119起&#xff0c;相较之前呈现持平趋势。 与上周相比&#xff0c;本周仍然流行的勒索家族为lockbit3和8base。在涉及的勒索家族中&#xff0c;活跃程度Top5的勒索家族分别是&#xff1a;lockbit3、siegedsec、dragonforce、8base和…