TooLongLink Video Watcher AI
Watch:
العربية - Arabic
Bahasa Indonesia
বাংলা - Bengali
български - Bulgarian
简体中文 - Chinese Simplified
繁體中文 - Chinese Traditional
Hrvatski - Croatian
Čeština - Czech
Dansk - Danish
Nederlands - Dutch
English
Eesti - Estonian
فارسی - Farsi
Suomi - Finnish
Français - French
Deutsch - German
Ελληνικά - Greek
ગુજરાતી - Gujarati
עברית - Hebrew
हिन्दी - Hindi
Magyar - Hungarian
Italiano - Italian
日本語 - Japanese
ಕನ್ನಡ - Kannada
한국어 - Korean
Latviešu - Latvian
Lietuvių - Lithuanian
മലയാളം - Malayalam
मराठी - Marathi
Norsk - Norwegian
Polski - Polish
Português - Portuguese
Română - Romanian
Русский - Russian
Српски - Serbian
Slovenčina - Slovak
Slovenščina - Slovenian
Español - Spanish
Kiswahili - Swahili
Svenska - Swedish
தமிழ் - Tamil
తెలుగు - Telugu
ไทย - Thai
Türkçe - Turkish
Українська - Ukrainian
اردو - Urdu
Tiếng Việt - Vietnamese
Submit
超级马里奥64的内存管理:深入分析其资源浪费与优化潜力,并探讨理论上移植至PS1的可能性
Kaze Emanuar
总结:
1996年发行的Nintendo 64主机内存仅有4MB,而《超级马里奥64》的游戏在内存管理方面存在大量效率低下的问题。
视频深入剖析了游戏运行时内存(RAM)的各个组成部分,包括帧缓冲区、Z缓冲区、内存池、引擎代码、主游戏代码、变量、演员状态和音乐数据。
主要浪费包括:用于调试目的的代码编译、未使用的空闲空间、固定分配给每个角色的冗余数据、未动态加载的代码和变量(所有内容始终驻留内存),以及音乐数据中未被正确复用的样本缓冲区。
通过假设性的优化(如编译代码以减小大小、采用类似于《塞尔达传说:时之笛》的叠加层系统动态加载代码、优化数学函数表和改进演员数据结构),可以显著减少内存占用。
所有优化加起来,理论上可节省近2MB的RAM,这意味着《超级马里奥64》甚至可以理论上移植到只有2MB主RAM的PlayStation 1上(因为PS1拥有独立的VRAM和光盘音频流)。
开发者可能为了缩短开发时间而未进行极致优化,因为在4MB内存限制下,现有方案已经足够满足游戏需求。
Nintendo 64上的内存构成 [
00:00:20
]
N64内存背景与马里奥64内存管理简介 [
00:00:00
]
1996年发布的Nintendo 64只有4兆字节(MB)内存。
这大约只能存储1,350个《马里奥64》平均分辨率的纹理。
内存中除了纹理,还包含代码、游戏引擎、音乐、输出缓冲区和临时角色状态。
开发者必须将所有数据压缩到与现代智能手机一张照片大小差不多的内存中。
在《马里奥64》的一些关卡中,例如开场地图,内存空间非常紧张,仅多出两个32x64的纹理就会导致游戏内存不足。
N64的开发者常常使用技巧和优化,甚至做出牺牲以适应4MB的内存限制。
视频将探讨《马里奥64》中使用了哪些技巧以及做出了哪些牺牲,并揭示了由于两个独立的打字错误导致约93个纹理数据被浪费。
许多人认为现代游戏在资源方面极其浪费,普遍存在优化不良的问题,视频将探讨老游戏是否有所不同。
理论上是否可以将《马里奥64》适配到PlayStation 1更小的2MB内存中。
只要游戏能放入内存,进一步优化内存是没有意义的,除非需要做出牺牲。
减少内存使用可以为增加更多细节和纹理提供空间,或者启用内存分块(memory banking)技术以获得约5%的性能提升。
N64的游戏内存限制 [
00:00:40
]
N64游戏卡带ROM尺寸与RAM尺寸 [
00:01:00
]
马里奥64运行时内存布局分析 [
00:02:27
]
视频将从内存的末尾开始分析,逐步向上追溯。
N64内存包含的内容 [
00:03:00
]
帧缓冲区优化
[
00:03:16
]
帧缓冲区从十六进制地址8038F800到内存末尾,大约占用0.44MB。
《马里奥64》输出320x240的图像,每个像素2字节,并使用三缓冲机制(triple buffering),这是必要的。
屏幕顶部和底部的黑边是为了解决老式电视的过扫描(overscan)问题。
帧缓冲区在内存中是连续的,理论上可以重叠部分,节省两个8x32的黑条区域的内存,即节省十六进制2800字节,相当于3个《马里里奥64》平均纹理。
另一种方式是直接渲染224x320的图像,这将节省三倍的内存。
帧缓冲区数据详情 [
00:03:39
]
帧缓冲区节省内存的计算 [
00:04:19
]
手动内存分配造成的空闲空间
[
00:04:31
]
帧缓冲区后有十六进制910字节的空闲空间。
《马里奥64》的程序员手动指定了内存中每个数据块的位置,而非让编译器自动分配。
这种做法导致数据块之间存在大量空闲空间。
引擎代码和变量优化
[
00:06:26
]
引擎段负责解释关卡和角色脚本,并将ROM中压缩的骨骼和碰撞数据编译为内存中未压缩且更快的处理格式。
为尺寸而非速度编译代码
N64内存速度慢,为减小尺寸编译的代码通常运行更快。
然而,《马里奥64》的美国和日本发行版代码是为调试目的编译的,既不为速度也不为尺寸优化。
导致代码重复和无用注释残留。为尺寸编译可将代码段大小减半。
移除加载时不再需要的代码
许多引擎代码仅用于编译碰撞和骨骼,加载区域后就不再需要,但却一直占用内存。
移除这些代码可进一步减少50%的内存占用,总计减少75%。
优化数学函数表
正弦和余弦函数使用一个包含1.25个圆周旋转的大型表,余弦函数通过索引正弦函数四分之一旋转的数据来获取。
通常可以使用圆对称性将表大小减半、四分之一或八分之一。
采用四分之一交错的正弦表可节省十六进制3000字节,相当于4个纹理的内存,并略微提高性能。
巨大的空闲区域
引擎段之后有十六进制113A0字节,约23个纹理的空闲空间,这是游戏中最大的空闲区域。
这可能是由于输入错误,或者为了预留空间以增加更多的代码和变量。
编译代码优化的对比:左侧为针对尺寸优化,右侧为针对速度优化 [
00:06:48
]
游戏代码和变量优化
[
00:09:57
]
几乎所有变量存储区域都被临时演员状态存储占用。
角色数据结构的浪费
地图中每个角色,无论多么简单(如静态几何体或隐形对象),都固定分配了十六进制260字节的数据。
《时之笛》采用了更智能的系统,每个角色有一个小头,包含必要数据,然后根据需要将额外数据存储在内存较低的部分,类似于C++的类和子类。
角色数据中包含一个4x4的渲染矩阵,虽然有时使用,但不应该使用,因为它会减慢渲染速度。
平均每个角色浪费十六进制140字节,总计十六进制14500字节,约27个纹理。
未使用的角色对象池容量
对象池总是分配240个角色的空间,但游戏中的每个关卡使用的角色都少于这个数量(最多约180个,不考虑bug或漏洞)。
(此处作者选择不将其计入最终节省,因为它会影响特定玩法)
缺少叠加层(Overlays)机制
《马里奥64》没有叠加层系统,所有角色代码和所有角色变量始终加载在内存中。
例如,当玩家在城堡场地时,恐龙Dory的代码也闲置在内存中。
《时之笛》对每个角色都使用了叠加层功能,只加载必要的代码和变量。
动态加载相关数据而非整个代码库,可大幅减少内存使用。
由于通常只有约20%的代码库被使用,这将使整个区域(主区域减去角色状态,总计约1MB)的代码大小减少约80%,节省十六进制CB0000字节,约270个纹理。
这种改变虽然会略微增加进入关卡时的加载时间(约1-10毫秒),但却是最重要的内存节省。
残留的调试代码
主游戏代码中还存在大量残留的调试代码,并且也是以调试模式编译的。
移除这些可进一步减少约50%的代码大小,节省十六进制9500字节,约0.1MB。
演员对象最大容量 [
00:11:47
]
代码量计算 [
00:13:16
]
音乐数据优化
[
00:14:03
]
很少有N64游戏直接存储完全混合的音乐,而是存储音符和乐器数据,在运行时混合成背景音乐。
这需要RAM来加载音序数据和波形。
乐器数据量大,游戏不可能同时加载所有乐器到内存。
样本DMA缓冲区的低效使用
游戏使用“样本DMA缓冲区”将乐器动态加载到内存中。
游戏一次可播放60个音符,每个音符一个样本,每个音符有4个样本缓冲区。
理论上是为了最大程度减少ROM加载,但在实践中样本缓冲区并未被正确复用。
导致每四个缓冲区中有两到三个实际上是无用的,浪费十六进制CA80字节,约17个纹理。
未使用的零填充内存
音乐区域中存在许多从未被使用且始终保持为零的内存区域。
这部分浪费约为十六进制176C0字节,约31个纹理,占音乐区域总大小的约18%。
结合样本缓冲区浪费,总计浪费约为十六进制1DC00字节,约40个纹理。
音乐数据文件大小 [
00:14:16
]
未使用内存区域 [
00:16:25
]
内存池(角色/关卡数据)优化
[
00:16:54
]
内存池复杂,包含当前关卡中所有加载角色的几何体、纹理和碰撞数据,以及运行时分配的其他数据(如编译后的图像和碰撞数据)。
子区域数据始终加载
内存池中内存非常紧张,例如在城堡场地中,只剩下十六进制1AE字节,不足以存储3个平均大小的《马里奥64》纹理。
游戏中当前播放关卡及其所有子区域的数据都被加载。例如,当你在金字塔外时,金字塔内部和Boss战区域也已加载。
一次性读取后常驻内存的数据
许多数据只加载一次,之后再未被使用。
未编译的碰撞和骨骼数据在关卡加载第一帧后,仍保留在内存中,从未再次读取。
例如,马里奥的骨骼和树木等通用对象的骨骼数据在游戏启动时加载,之后在游戏运行期间成为“死内存”。
仅马里奥骨骼数据就占十六进制2E30字节,通用对象骨骼数据占十六进制1060字节。
内存分块加载冗余数据
《马里奥64》的内存分块机制导致在加载某个对象时,会加载过多的相关数据(例如,加载水怪会同时加载海草、水雷、水圈等)。
《时之笛》通过按房间加载对象数据解决了此问题,且玩家几乎未察觉到加载延迟。
纹理存储效率低下
纹理数据本身也存在浪费。许多纹理即使是纯灰度图像,也被赋予了完整的颜色通道(RGBA16),浪费了一半的纹理内存。
其他低效结构
行为脚本未压缩、编译骨骼节点中的指针、碰撞数据使用低效指针而非索引、碰撞法线不需要存储等。
估计内存池可减少约33%,即十六进制75B00字节,相当于143个纹理。
内存池占用内存图示 [
00:17:05
]
纹理存储效率低下的例子:灰度纹理存储为RGBA16 [
00:19:04
]
可缩减和可卸载的内存区域 [
00:19:24
]
Z缓冲区优化
[
00:19:39
]
Z缓冲区下方存在十六进制36800字节的空闲空间,约73个纹理,这可能是手动分配RAM时的一个打字错误。
由于游戏从未在屏幕底部8个像素渲染,Z缓冲区也无需包含这部分,可节省十六进制1400字节,约1.7个纹理。
内存优化总计与移植到PS1的可能性 [
00:20:34
]
将所有节省的内存加起来,总计为十六进制1E5620字节,略低于2MB。
移植至PlayStation 1
PlayStation 1虽然只有2MB的RAM,但它拥有独立的VRAM用于帧缓冲区,因此帧缓冲区内存无需计入主RAM。
这使得《马里奥64》在优化后可以轻松适应PS1的内存预算。
PS1游戏通常通过光盘存储音频,而非在内存中实时混合,因此音乐部分几乎可以全部移除。
理论上,优化后的游戏甚至可能只需要1MB的RAM。
总共节省的内存 [
00:20:34
]
PlayStation 1的硬件参数 [
00:21:23
]
优化后马里奥64所需的内存计算 [
00:21:33
]
开发者的选择与权衡 [
00:22:02
]
视频指出,虽然存在许多未被利用的内存优化机会,但开发者可能认为没有必要进一步优化RAM。
他们可能已经成功地使关卡达到预期的视觉效果。
虽然内存优化可以带来RAM分块等额外5%的性能提升,但可能存在其他更易实现且效益更高的优化途径。
开发者可能为了缩短开发时间而选择不进行这些复杂的优化,这在开发过程中是合理且可以理解的。
毕竟,如果使用所有4MB内存没有额外成本,为何不充分利用呢?
开发者的优化选择 [
00:22:53
]
Copied!
Why didn’t you like it?
Inaccurate / wrong facts
Missing important points
Took too much time to load
Article too long / verbose
Article too short / not enough detail
Article hard to read / bad formatting
Images irrelevant / broken
Language / translation issues
Offensive / inappropriate
Other
Submit
Thank you for your feedback!
×