惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

P
Palo Alto Networks Blog
Recent Commits to openclaw:main
Recent Commits to openclaw:main
C
CERT Recently Published Vulnerability Notes
C
Cybersecurity and Infrastructure Security Agency CISA
S
Schneier on Security
S
Securelist
酷 壳 – CoolShell
酷 壳 – CoolShell
C
CXSECURITY Database RSS Feed - CXSecurity.com
Cyberwarzone
Cyberwarzone
Apple Machine Learning Research
Apple Machine Learning Research
S
SegmentFault 最新的问题
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
GbyAI
GbyAI
Security Latest
Security Latest
Last Week in AI
Last Week in AI
Microsoft Security Blog
Microsoft Security Blog
云风的 BLOG
云风的 BLOG
Recorded Future
Recorded Future
Webroot Blog
Webroot Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
TaoSecurity Blog
TaoSecurity Blog
C
Cisco Blogs
博客园 - 【当耐特】
Blog — PlanetScale
Blog — PlanetScale
Hugging Face - Blog
Hugging Face - Blog
B
Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Attack and Defense Labs
Attack and Defense Labs
The Last Watchdog
The Last Watchdog
U
Unit 42
阮一峰的网络日志
阮一峰的网络日志
Project Zero
Project Zero
WordPress大学
WordPress大学
L
LINUX DO - 最新话题
F
Fortinet All Blogs
L
LINUX DO - 热门话题
PCI Perspectives
PCI Perspectives
Simon Willison's Weblog
Simon Willison's Weblog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
MongoDB | Blog
MongoDB | Blog
Latest news
Latest news
P
Proofpoint News Feed
T
Threat Research - Cisco Blogs
The Hacker News
The Hacker News
爱范儿
爱范儿
O
OpenAI News
J
Java Code Geeks
T
The Exploit Database - CXSecurity.com
H
Hackread – Cybersecurity News, Data Breaches, AI and More

重归混沌的BLOG

给silly实现了一个ernro模块 | 重归混沌的BLOG 给silly实现了一个ernro模块 | 重归混沌的BLOG 第一次在生产环境使用 Vibe Coding | 重归混沌的BLOG 第一次在生产环境使用 Vibe Coding | 重归混沌的BLOG API 设计的艰难抉择 | 重归混沌的BLOG API 设计的艰难抉择 | 重归混沌的BLOG 十年 | 重归混沌的BLOG 十年 | 重归混沌的BLOG 在Go语言中如何使XML加载内存无限趋近于0 | 重归混沌的BLOG 在Go语言中如何使XML加载内存无限趋近于0 | 重归混沌的BLOG 对跨服玩法中的分布式一致性问题进行简单抽象 | 重归混沌的BLOG 对跨服玩法中的分布式一致性问题进行简单抽象 | 重归混沌的BLOG Go语言逃逸分析之slice和map | 重归混沌的BLOG Go语言逃逸分析之slice和map | 重归混沌的BLOG 谈谈观测 | 重归混沌的BLOG 谈谈观测 | 重归混沌的BLOG 写了个AI Agent服务端 | 重归混沌的BLOG 写了个AI Agent服务端 | 重归混沌的BLOG 谈谈代码设计中“严丝合缝” | 重归混沌的BLOG 谈谈代码设计中“严丝合缝” | 重归混沌的BLOG 一次艰难的线上游戏服务器内存排查经历 | 重归混沌的BLOG 一次艰难的线上游戏服务器内存排查经历 如何基于LanguageServerProtocol来编写lint工具 谈谈游戏服务器中RPC模块的设计 谈谈游戏服务器代码抽象 最近碰到的一个分布式一致性问题 谈谈游戏服务器的自动化测试 对Raft协议的一点理解 使用mmap来学习/proc/pid/smaps 2023(完) 再次实现了一个Lua性能分析器 终于给Silly的定时器增加了取消功能 一次虚拟内存排查经历 游戏服务器分布式数据的一种同步的思路 为silly增加了互斥锁 2022(完) Go语言之闭包篇 一例误用unsafe包引起的内存问题 Go语言之内存篇 初识Go语言 重新抽象图形API 给Lua实现了一个数学库 谈谈跨平台图形API的抽象 寻路和Flocking算法的结合 行为树的一种高效实现 内测过程中Shader出现的问题 彻底解决多国语言 谈谈数据库的选型 再谈Lua热更新(终) 初窥Rust 关于游戏服务器的服务拆分 ECS的初步实现 ECS初探 屏幕空间(SreenSpace)的想象力 一些对辐射度量学的理解 深度缓冲和半透明渲染 Mysql的间隙锁 更新一些GPU相关知识 2020 地形渲染之爬过的坑 Lua5.3 GC源码阅读(5) 实现一个数据库存储队列 再学计算机图形学入门 再谈分布式服务架构 游戏上线一个月后的反思 一次并发Bug 双向链表的三种实现 谈谈随机数的使用 再谈性能优化 2019 Lua中的函数式编程 重构登录逻辑 Unity资源管理(续) 一次关于Cache的性能分析 历史之2018 DC3算法 移动平台native代码遭遇的坑 从CPU层面谈谈优化 开卷有益(UNIX编程艺术篇) GC竞争问题 通过Mesh投影来实现贴花系统 谈谈我对数据同步的理解 又一个类型提升引起的Bug Lua5.3 GC源码阅读(4) Lua5.3 GC源码阅读(3) Lua5.3 GC源码阅读(2) Lua5.3 GC源码阅读(1) 三角形光栅化时遇到的坑 一次git事故 再见2017 又一个lua调试器 客户端缓存落地方案 Paxos算法 HTTP服务器的特点 一次性能优化经历 关于CPU分支预测 C程序中让两个不同版本的库共存 实现了一个AOI模块 一个高可伸缩的游戏服务器架构 关于网络协议封装的一些新想法
谈谈Unity的资源管理
重归混沌 · 2019-07-27 · via 重归混沌的BLOG

Unity最佳实践明确指出, 要使用AssetBundle而不是Resources目录来管理资源。

然而,事情并不像Unity官方描述的那么美好。因为使用AssetBundle我们甚至无法实现一个高效易用的,完全自动化资源管理方案。

据Unity官方说,一般有两种方案。

方案一,如果你的游戏是关卡性质的,可以在一个关卡里加载所有AssetBundle,然后在进入下一关卡时,卸载本关卡中加载的所有AssetBundle. 但这种机制似乎只对愤怒的小鸟这种小游戏才适用吧:D。

方案二,如果你的游戏不是关卡类的,那么Unity推荐做一个资源对AssetBundle引用计数。

如果一个对象(Asset或其他AssetBundle)引用此AssetBundle则其引用计数加1. 如果此AssetBundle首次加载(即加载前引用计数为0), 还需要递归对其依赖引用计数加1。

如果一个AssetBundle的引用计数为0则释放这个AssetBundle,同时还需要递归对其依赖引用计数减1.

除非,我们做像愤怒小鸟一样的通关游戏,不然似乎只有方案二给我们用。而且方案二乍一看是完备的,因为这正是GC算法的一种实现。

但是如果稍微仔细思考一下就会发现,这个方案只是AssetBundle的管理方案,是个半成品,要如何管理资源之间的依赖,Unity却只字未掉,看起来是让用户自己想办法,这似乎与其易学易用的宗旨不太相符。

下面来分析一下Unity中资源之间的关系。

在Unity中资源大约分为以下几种:

纹理(Texture)、网格(Mesh)、动画片段(AnimationClip)、音频片段(AudioClip)、材质(Material)、着色器(Shader)、字体资源(Font)以及文本资源(TextAsset)。

AssetBundle中还有一个极其特殊的存在,那就是Prefab, AssetBundle.LoadAsset时返回的是GameObject, 但是又必须经过Instantitate之后变成另外一个GameObject才能使用。此后所说的GameObject均是Instantitate之后的GameObject。

GameObject可以添加各种Component来引用上述资源,还可以通过代码动态增减某个GameObject上的Component或者修改Component对资源的引用。这种灵活性给资源管理带来了巨大麻烦,而没有这种灵活性,逻辑的实现就会更麻烦。


下面,举例来说明一下,要正确管理GameObject和资源之间的引用关系有多么艰难。

Prefab P能过Instantitate生成A,B,C,D四个GameObject.

执行如下代码之后,A引用{P,T1}, B引用{P,T1}, C引用{P,T3}。并且T2应该被Unload。

1: A.GetComponent<SpriteRender>().sprite = (Sprite)T1;
2: B.GetComponent<SpriteRender>().sprite = (Sprite)T1;
3: C.GetComponent<SpriteRender>().sprite = (Sprite)T2;
4: C.GetComponent<SpriteRender>().sprite = (Sprite)T3;

要想自动正确的管理GameObject和资源的引用关系,就必须要感知到对GameObject的赋值操作。

例如:所有的sprite赋值都必须使用类似SpriteAssign(SpriteRender sr, Sprite s)的接口。

SpriteAssign的执行流程通常是这样的。

  1. 检查sprite的值是不是T1相同,如果是相同则不做处理
  2. 检查sprite的值是不是从P中clone过来的,如果不是,将此sprite的引用计数减1
  3. 将T1的引用计数加1

如果P是一个树状态结构,即有P–(child)–>p1–(child)–>p2。

1: A.p1.p2.GetComponent<SpriteRender>().sprite = (Sprite)T1;
2: B.p1.p2.GetComponent<SpriteRender>().sprite = (Sprite)T1;
3: C.p1.p2.GetComponent<SpriteRender>().sprite = (Sprite)T2;
4: C.p1.p2.GetComponent<SpriteRender>().sprite = (Sprite)T3;

SpriteAssign接口中的步骤2就显得格外复杂,它必须修正引用关系如下:A引用{P,T1}, B引用{P,T1}, C引用{P,T3}。

同时Destory操作也要被感知,如果Destory(A)则需要释放A引用的资源,而如果Destory(A.p1.p2)则需要修正A对资源的引用情况。因为此时的引用关系是,A引用{P}。换句话说Destroy的开销也会变大。

而赋值和Destory都算不上低频操作,尤其是赋值操作。这样的开销已经足够让程序慢上好几倍了。如果不能承受这些开销,全自动化资源管理是不可能实现的。

我想这也是Unity不默认提供一套标准的全自动化资源管理方案,而是让用户根据实际情况来自己做trade-off的根本原因吧。


受方案一的启发,我觉得可以通过如下接口做一个半自动化的资源管理器。

void frame.record(asset);
void frame.dispose();
void stack.open(frame f);
frame stack.close();
void stack.push() {
frame l = new frame()
stack.open(l)
}
void stack.pop() {
frame f = stack.close()
f.dispose()
}

每一个frame对象都会记录在stack.open()和stack.close()之间所有加载过的资源,加过载多少次就记录多少次,这些资源会在执行frame.dispose()时如实的进行释放。

其中stack在管理栈式UI资源方面几乎已经达到了全自动化,当你打开一个UI时调用stack.push,在退出此UI时调用stack.pop会自动释放在此UI期间你所加载的全部资源。

而在其他不具有栈式加载资源特征的地方,frame类也提供了一种方便的半自动化管理方案。在加载资源之前调用stack.open(frame),在加载资源之后调用stack.close()。

最重要的是,此种方案的开销和复杂度,都要远低于全自动化管理方案。