DonaldW's github pages
Unreal Source Explained (USE) is an Unreal source code analysis, based on profilers.
For more infomation, see the repo in github.
See Table of Contents for the complete content list. Some important contents are listed below,
As you can see in the image above, Unreal is initialized by two main steps: FEngineLoop::PreInit()
(link) and FEngineLoop::Init()
(link). They are called in FAppEntry::Init()
(link) in iOS, and AndroidMain()
(link) in Android.
You may think PreInit()
is the low-level initializaiton and Init()
is the high-level.
Note in Unreal there are two ways to manage submodules: Module and Plugins. Module conatains only code, while Plugin can contain assets and/or Modules.
To name a few things get initialized in PreInit()
, in order:
LoadCoreModules()
(link) for “CoreUObject”;LoadPreInitModules()
(link) for “Engine”, “Renderer”, “AnimGraphRuntime”, “Landscape”, “RenderCore”;FEngineLoop::AppInit()
(link)
InitScalabilitySystem()
(link). Scalability adjusts the quality of various features in order to maintain the best performance for your game on different platforms and hardware;InitGamePhys()
(link)FSlateApplication::Create()
(link)RHIInit()
(link)CompileGlobalShaderMap()
(link)StartRenderingThread()
(link);UObject
s’ reflection data(link): ProcessNewlyLoadedUObjects()
(link);LoadStartupCoreModules()
(link): “Core”, “Networking”, “Messaging”, “Slate”, “UMG”;and Init()
initializes these in order:
UGameEngine::Init()
(link)
UEngine::Init()
(link), UEngine
is abstract base class of UGameEngine
and UEdtiorEngine
, and is responsible for management of systems critical to editor or game systems.;UGameUserSettings::LoadSettings()
(link);UGameInstance
(link), UGameInstance
is high-level manager object for an instance of the running game
UWorld
in UGameInstance::InitializeStandalone()
(link), UWorld
is the top level object representing a map or a sandbox in which Actors and Components will exist and be rendered;UGameViewportClient
(link)
UGameEngine::Start()
(link)To manage UObject
s, Unreal uses UObjectBase::ObjectFlags
(link) to record a UObject
instance’s states.
A UObject
instance is called Archetype object if it has RF_ArchetypeObject
flag, and it’s called Class Default Object (CDO) if it has RF_ClassDefaultObject
.
Unreal eventually calls StaticConstructObject_Internal()
(link) to create every UObject
instance, no matter it’s via user’s NewObject<T>()
call (link) or via Unreal’s internal call. Here the most important 3 parameters of StaticConstructObject_Internal()
are its
UClass* Class
: The class of the object to create. The return value of Class->GetDefaultObject()
is indeed the CDO;UObject* Template
: If specified, the property values from this object will be copied to the new object, and the new object’s ObjectArchetype value will be set to this object. If nullptr, the class default object is used instead.EObjectFlags InFlags
: The ObjectFlags to assign to the new object.Parameter Template
has higher priority than the CDO to copy its property value to the new object(link).
FObjectInitializer::~FObjectInitializer()
{
//...
UObject* Defaults = ObjectArchetype ? ObjectArchetype : BaseClass->GetDefaultObject(false);
InitProperties(Obj, BaseClass, Defaults, bCopyTransientsFromClassDefaults);
//...
}
The new object’s flag is set by the InFlags
argument. Usually it doesn’t have RF_ArchetypeObject
nor RF_ClassDefaultObject
flag, therefore, the new object is just a normal instance.
However that’s not the case when the new object is a new Archetype object or a new CDO.
So what’s archetype and CDO anyway, what’s their similarity and difference?
As the above image shows, either an Archetype object, a CDO, or any UObject
instance can be a template object to copy its properties’ values into a new object.
Any UObject
instance can be a template object, with or without RF_ArchetypeObject
or RF_ClassDefaultObject
flag.
Archetype object iteself is initialized from asset data (e.g. uasset) in the disk. Its RF_ArchetypeObject
flag is set when loading the asset in FLinkerLoad::CreateExport()
(link):
UObject* FLinkerLoad::CreateExport( int32 Index )
{
FObjectExport& Export = ExportMap[ Index ];
...
// RF_ArchetypeObject and other flags
EObjectFlags ObjectLoadFlags = Export.ObjectFlags;
...
Export.Object = StaticConstructObject_Internal
(
LoadClass,
ThisParent,
NewName,
ObjectLoadFlags,
EInternalObjectFlags::None,
Template
);
}
CDO itself is initialized by
SuperClass
(link),UClass::SerializeDefaultObject()
(link).CDOs have both the RF_ClassDefaultObject
and RF_ArchetypeObject
flags, they are set and explained when creating the CDO (link):
UObject* UClass::CreateDefaultObject()
{
...
// RF_ArchetypeObject flag is often redundant to RF_ClassDefaultObject, but we need to tag
// the CDO as RF_ArchetypeObject in order to propagate that flag to any default sub objects.
ClassDefaultObject = StaticAllocateObject(this, GetOuter(), NAME_None, EObjectFlags(RF_Public|RF_ClassDefaultObject|RF_ArchetypeObject));
...
}
For the same reason, all Default Subobjects have both RF_DefaultSubObject
and RF_ArchetypeObject
flags.
Most (near all) Z_Construct_UClass_XXX()
fuctions are called only in the initialization stage via ProcessNewlyLoadedUObjects()
(link).
Z_Construct_UClass_XXX()
are functions that construct the Unreal intrinsic “class reflection data”. These functions’ code are generated by macro in IMPLEMENT_INTRINSIC_CLASS
(link):
#define IMPLEMENT_INTRINSIC_CLASS(TClass, TRequiredAPI, TSuperClass, TSuperRequiredAPI, TPackage, InitCode) \
IMPLEMENT_CLASS(TClass, 0) \
TRequiredAPI UClass* Z_Construct_UClass_##TClass(); \
struct Z_Construct_UClass_##TClass##_Statics \
{ \
static UClass* Construct() \
{ \
extern TSuperRequiredAPI UClass* Z_Construct_UClass_##TSuperClass(); \
UClass* SuperClass = Z_Construct_UClass_##TSuperClass(); \
UClass* Class = TClass::StaticClass(); \
UObjectForceRegistration(Class); \
check(Class->GetSuperClass() == SuperClass); \
InitCode \
Class->StaticLink(); \
return Class; \
} \
}; \
UClass* Z_Construct_UClass_##TClass() \
{ \
static UClass* Class = NULL; \
if (!Class) \
{ \
Class = Z_Construct_UClass_##TClass##_Statics::Construct();\
} \
check(Class->GetClass()); \
return Class; \
} \
...