[WIP] Unreal Source Explained

DonaldW's github pages

[WIP] Unreal Source Explained

Unreal Source Explained (USE) is an Unreal source code analysis, based on profilers.
For more infomation, see the repo in github.

Contents

See Table of Contents for the complete content list. Some important contents are listed below,

Initialization

Engine Initialization Overview

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:

and Init() initializes these in order:

UObject Initialization

Archetype and CDO

To manage UObjects, 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

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

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.

AActor and UComponent initialization

Class reflection data

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; \
	} \

    ...