UE4源码剖析:MallocBinned(上)
近期着手UE4项目开发,对UnrealEngine已久仰慕,装箱源码终于得此机会深入探索。装箱源码鉴于项目内存性能问题,装箱源码决定从内存分配器着手,装箱源码深入研读UE4源码。装箱源码源码超市代码虽个人水平有限,装箱源码尚不能全面理解,装箱源码但愿借此机会揭开源码神秘面纱,装箱源码让新手朋友们不再感到陌生。装箱源码
UE4内存分配器位于硬件抽象层HAL(Hardware Abstraction Layer)中。装箱源码具体装箱内存分配器代码位于VS项目目录:UE4/Source/Runtime/Core/Private/HAL/MallocBinned。装箱源码
分析从ApplePlatformMemory::BaseAllocator开始,装箱源码可发现Mac平台的装箱源码默认分配器为MallocBinned,iOS的装箱源码默认分配器为MallocAnsi。以下将重点分析MallocBinned。iris源码
一、确定对齐方式
FScopeLock用于局部线程锁,确保线程同步。关于Alignment的确定,通常使用默认值。默认值取决于内存对齐方式,此处默认对齐为8字节。
二、确定有足够空间来内存对齐
代码中,SpareBytesCount用于确认空间足够。若分配内存小于8字节,则按Alignment大小匹配箱体;若大于8字节,则按Size + Alignment - sizeof(FFreeMem)匹配箱体。
三、确定箱体大小
根据Size的recvmsg源码大小,有三种不同的处理方式。k以下的内存分配采用装箱分配,PoolTable中包含个不同大小的池子。
四、初始化内存池
分析内存池初始化过程,主要工作包括:确定内存大小,分配内存块,设置内存池基本信息。
五、内存装箱
AllocateBlockFromPool从内存池中分配一个Block,实现内存装箱过程。
游戏引擎Unity | Lightmap Baking:Progressive GPU源码分析
在探索Unity的GI源码过程中,我专注于Lightmap Baking的Progressive GPU实现。Unity在没有Enlighten后,仅剩两种GPU烘焙选项:CPU和AMD RadeonRay+OpenCL。openfiler 源码核心代码位于Editor\Src\GI的PVRRuntimeManager.cpp的Update()函数中,以下是烘焙过程的主要步骤:
首先,实时更新geometry、instance和material到缓存,这是数据准备阶段。
接着,通过Packing Atlas,instance被映射到uv坐标并分配到lightmap,使用的是基于二叉树的装箱算法,可能是Guillotine算法的变种。
Unity为每个instance的material生成两张纹理,一张存储albedo,一张存储emissive,与lightmap大小一致,便于后续的dsd源码路径追踪计算。这限制了采样精度。
相机裁剪阶段,通过相机的视锥判断哪些lightmap texel可见。Prioritize View功能优先烘焙可见的texel,逐lightmap进行,而非一次烘焙所有。
渲染阶段,Unity根据设置自适应采样,计算path tracing时考虑直接光、环境光和间接光,采用正交基计算、八面体编码和Moller-Trumbore方法,优化光源处理和环境光采样。
收敛阶段统计已经converge的texel数量,用于判断烘焙是否完成,并决定后续步骤。最后,执行降噪、滤波、stitch seams和存储结果到项目文件。
除了核心功能,Unity还提供了选项如denoiser、filter(支持Optix、OpenImage和Radeon Pro),以及处理stitch seams的最小二乘方法。此外,还有Lightmap Parameters用于设定背面容忍度,以及使用Sobol序列和Cranley Patterson Rotation获取随机点,以及四面体化分布的probe和3阶SH函数计算。
public static <T> int binarySearch(List<? extends T> list, T key, Comparator<? super T> c)
é®é¢1ï¼2å ¶å®å¯ä»¥ä¸èµ·è®²ï¼å°±æ¯ï¼ä½ 没å®å ¨ç解==ï¼equalsåèªå¨è£ ç®±ï¼auto boxing)ãä»ç»çä½ çcomparatorçå®ç°return i<j?-1:(i==j?0:1);è¿å¥è¯ï¼æ³¨æï¼ä½ çåæ°æ¯Integerç±»åï¼ä¸æ¯åºæ¬ç±»åintï¼ä½ 对类ç对象å==æä½ï¼æ¯æ¯è¾å®ä»¬æ¯å¦æ¯åä¸ä¸ªå¼ç¨ï¼èä¸æ¯ä»ä»¬çå¼æ¯å¦ç¸çãæ以ï¼ä½ ç¨è¿ä¸ªæ¯è¾å¨åäºåæç´¢ï¼ç»æè¯å®æ¯ä¸å¯é¢ç¥çãå½<å¤æ失败çæ¶åï¼åé¢ç==åºæ¬ä¸å¯è½æ¯trueï¼è¿ä¹æ¯ä¸ºä»ä¹if(integers.get(1)==new Integer(1)){ System.out.println("<3>");}è¿å¥è¯ä¸è½æå°åº<3>æ¥ãä½ åé¢é£ä¸ªå®ç°æ¯æ£ç¡®çï¼å 为类ç对象没æ<æä½å>æä½ï¼æ以ç¼è¯å¨ä¼æInteger对象èªå¨è½¬æintåæ§è¡æ¯è¾ã
é®é¢3ï¼apiåçå¾æ¸ æ¥ï¼å½æ¾ä¸å°çæ¶åï¼ä¼è¿åã-ï¼æå ¥å¼ï¼-1ãï¼å¨è¿ä¸ªç¨åºéï¼å ä¸ºä½ çæ¯è¾å¨åçä¸å¯¹ï¼æ以æ索失败ï¼å¹¶ä¸å®è®¤ä¸ºæå ¥å¼æ¯1ï¼è¿æ²¡æé®é¢åãå ¶å®ï¼å 为ä¸é¢è¯´çé®é¢ï¼ä»è¿åä»»ä½å¥å¥æªæªçæ°é½æå¯è½ã
å¦æä½ æ³å¯¹ä¸¤ä¸ªIntegerç±»ç对象æ¯è¾å¼ï¼ä½ å¯ä»¥è°ç¨i.eqauls(j);æè i.intValue()==j.intValue();intValueæ¹æ³è¿åçæ¯Integer对象å è£ çåºæ¬ç±»åçintå¼
å¦å¤ï¼è¡¥å ä¸ç¹ï¼ä¸ç¥éæ¯ä½ æè¿°çä¸å¥½ï¼è¿æ¯æç解çä¸å¯¹ãä½ è¯´çâæ ¹æ®APIæ档解éï¼ Comparatoråªæ¯å¨searchä¹å对listè¿è¡æåºãâä¸å¯¹ï¼binarysearchä¸ä¼æåºï¼èæ¯apiè¦æ±ä½ å¨è°ç¨binarySearchä¹åï¼å¿ é¡»ä¿è¯ä¼ è¿å»çlistæ¯å·²æåºäºçï¼ä½ å¯ä»¥è°ç¨Collections.sortæ¥å®ç°ï¼ä¹å¯ä»¥èªå·±åæåºç®æ³
=========================================================
comparatorçä½ç¨å°±æ¯æ¯è¾å¨åï¼åè¯æåºæ¹æ³ææ ·æ¯è¾ä¸¤ä¸ªå¼è°å¤§è°å°æç¸çï¼å¯¹äºIntegerï¼å½ç¶æ¯è¾è§åå¾ç´è§ï¼ä½æ¯å¯¹å«çç±»å¢ï¼æ¯å¦ä½ æä¸ä¸ªä¸ä¸ªStudentç±»ï¼ä½ å¸ææ ¹æ®å¦å·æ¥æåºï¼æ¥æ¾ï¼ä½ å°±éè¦å®ä¹èªå·±çComparatorï¼å½ç¶ä½ ä¹å¯ä»¥è®©ä½ çç±»å®ç°Comparableæ¥å£ï¼è¿æ ·ä½ å°±å¯ä»¥ç´æ¥è°ç¨binarySearchçå¦å¤ä¸ä¸ªä¸éè¦æ¯è¾å¨ççæ¬
=========================================================
æç¹ä¸æç½ï¼ä½ æ°è¡¥å çé®é¢æ¯é对æåªå¥è¯è®²çï¼ä½ è´´çAPIæ¯æ²¡éå¦ï¼ä½ç解ä¸å¯¹ãbinarySearchä¸ä¼æåºï¼æåºçæ¯sortï¼binarySearchå®ç°ä¸ä¸ä¼è°ç¨sortï¼è¿ç¹ä½ èªå·±çæºä»£ç å°±ç¥éäºï¼äºå®ä¸api表达çä¹æ¯è¿ä¸ªææï¼æ³¨æï¼åè¯æ¯âå¨è¿è¡æ¤è°ç¨ä¹åâï¼æææ¯ç¨æ·èªå·±è°ç¨sortåè°ç¨binarysearchï¼ãä½ ç¥épre-condition说æ³åï¼å表已æåºç¶æå°±æ¯binarySearchæ¹æ³æåçpre-conditionï¼åç½®æ¡ä»¶ï¼ï¼ä½ å¯ä»¥ä¼ ä¸ä¸ªä¹±åºçlistè¿å»ï¼æ²¡é®é¢ï¼ç¼è¯å¨ä¸ä¼æ¥éï¼ä½æ¯ä½ å¾ä¸å°èªå·±æ³è¦çç»æï¼è¿ä¸ªä½ èªå·±è¯å°±ç¥éäºã
=========================================================
æ±ããä½ çææé¾éä¸è¿æ¯searchä¼è°ç¨sortä¹ï¼å¦åï¼å¦æä½ æ¿è®¤searchä¸è°ç¨sortï¼é£ä¸ºä»ä¹è¿æçcomparatorä¼å¦åä¸æç´¢è¿ç¨ï¼å¦æsearchæ¢ä¸æåºï¼åä¸ä½¿ç¨comparatorï¼é£è¿ä¸ªåæ°ä¸å°±æ²¡æç¨äºåï¼
æ以ï¼searchéè¦comparatorï¼åªç¨æ¥æç´¢ï¼åæåºæ¯«æ å ³ç³»ãï¼ä½ åºè¯¥ç¥éäºåæç´¢æ¯æä¹æ§è¡çå§ï¼é£å°±å¾æ¸ æ¥ä¸ºä»ä¹éè¦ä¸ä¸ªcomparatoräºï¼
i.tostring()åConvert.ToString(i) åºå«
C#ä¸æ²¡åºå« ToString()çå®ç°è¿åç¨Convertå®ç°ç¸åã
è³äºè£ ç®±ï¼ä¼°è®¡æ¥¼ä¸»å¤èäºã
1. 两个ä¸åçç±»å转æ¢ï¼ä¸å®åå¨å建ç®æ 对象çè¿ç¨ãå³å建ä¸ä¸ªå符串å¼ç¨ã
2. å¹³æå°çè£ ç®±ï¼ä¸è¬åå¨äº int å° Intæ¶ç转æ¢ï¼ä½C#ä¸å®è´¨Intæ¯ç»æä½ï¼å¹¶éJavaä¸çä¸ä¸ªæ¯å¼ç±»åï¼ä¸ä¸ªæ¯å è£ ç±»çå ³ç³»ãintåªæ¯C#ä¸ä¸ºIntæä¾ä¸ä¸ªç®æçå ³é®åå½¢å¼åºç°ï¼ç¼è¯ä¸ºILåè¿æ¯ä»¥Int表示ã
Intçå®ä¹ï¼
public struct Int : IComparable, IFormattable,
IConvertible, IComparable<int>, IEquatable<int>
tolua源码分析(五)lua使用C#的enum
探讨了C#枚举如何在Lua中注册以及与普通类的注册区别。以官方提供的例子为例,展示了如何将C#的UnityEngine.Space类型的枚举推送到Lua层,并在Lua层面测试了诸如tostring、ToInt、Equals等接口,验证了在Lua层可以进行枚举的相等判断,以及将int转换为枚举或将枚举转换为int的操作。
在Lua层面表示C#的枚举,例子中在第行和第行将枚举推送到Lua层。由于枚举是值类型,C#层使用了enumMap缓存装箱后的object与枚举的映射关系。注册到Lua层的枚举类使用了EnumMetatable。
具体来看C#枚举注册到Lua的方法,例如在System_EnumWrap.Register方法中。在Lua层表示C#枚举的方式与普通类相似,但需要注意一些区别。
例如,当使用__tostring方法时,ToLua.ToObject将Lua栈上的userdata转换为object,通过userdata的index查找C#的object缓存,不会产生垃圾收集(GC)。同样地,ToInt方法中的CheckObject同样在C#的object缓存中查找,执行类型检查,也不会产生GC。
当比较C#的枚举与int类型时,由于使用了==操作符,这会触发装箱,产生一次GC。因此,在实际使用中应尽量避免在Lua层对C#枚举与number进行比较。而在Lua层直接比较两个C#枚举时,它们在Lua层被视为同一份userdata,因为它们来自于同一个C#缓存,index相同。
在将Lua栈上的number转换为C#枚举的实例时,IntToEnum方法在C#的UnityEngine_SpaceWrap类中实现。这个方法直接将double转换为int,再转换为UnityEngine.Space类型,避免了GC。在C#层推送到Lua层的枚举时,是从C#的缓存中取到枚举对应的object,然后推送到Lua层,也不会产生GC。
总结,在Lua使用C#的枚举时,从C#到Lua层的传递不会产生GC,在Lua层进行number与枚举类型之间的转换以及直接比较枚举时不触发GC。然而,当比较枚举与number时,会触发一次GC。针对这一情况,可以进行针对性优化。
下一节将深入研究在开发中常见的C#委托/事件如何注册到Lua函数的实现。
2024-12-24 00:55
2024-12-24 00:23
2024-12-23 23:47
2024-12-23 23:23
2024-12-23 23:07