[UE] mount of pak (with source code analysis)

The engine version used in this article is UE4.27.
For the convenience of understanding, the codes selected in this article are all partially intercepted, and only the parts related to the subsection are intercepted.

overview

Normal bulk file loading is to use FFileHelper::LoadFileToArraythe etc interface to read the file content. However, pak is a format similar to a compressed package, and the files in it cannot be read directly in this way. Therefore, you need to use mount to mount.

The mount operation tells the system which files can be read from the pak, and provides a virtual path so that the system can operate the files in the pak by operating ordinary files such as FPakPlatformFile::CopyFile, etc.FFileHelper::LoadFileToString

Several structures involved

IPlatformFile: The interface of file IO, which is the base class of the entire file system. This class and its subclasses are organized in a chain. Each instance has a reference to its lower layer. When accessing each layer, it first searches for itself, and then searches to the lower layer if it cannot find it.

FPakPlatformFile: inherited from IPlatformFile, responsible for the mount of pak. Its private member ExcludedNonPakExtensions records several file extensions that should only exist in the pak: uasset, umap, ubulk, uexp, uptnl, and ushaderbytecode. If these files are not found in the pak, it will no longer search for its lower layer.

FPakListEntry: A structure defined in FPakPlatformFile, which has two attributes, ReadOrder and PakFile. Several TArrays are used in FPakPlatformFile to record the mounted pak

FPakFile: The form of Pak stored in C++. There are members such as PakFilename, LastUseTime, Info, etc.
FPakInfo: Outline information of Pak, including version number, hash value, magic number, whether to encrypt, etc.
FPakEntry: Information of a single file in Pak, storage file size, offset in pak, whether to encrypt, etc.

Mount timing

Many places will be transferred FPakPlatformFile::Mountto mount for pak, here only the mount performed when the engine starts is analyzed

The initialization mount that occurs when the engine starts is located FPakPlatformFile::Initialize. If the PakFile singleton is specified using the command line, the singleton will be created through the ConditionallyCreateFileWrapper function and called FPakPlatformFile::Initialize during the engine PreInit stage. If not specified on the command line, the initialization function is performed during the EditorInit phase. The call stack for the latter is as follows:
insert image description here

pak read priority

As mentioned in the overview, the function of mount is to provide a method to read the pak file, so that the files in the pak can also use the operation method of manipulating bulk files. That is: mount is not to read the files in the pak . Therefore, the mount order actually has no effect on the read priority of pak

So what does the priority of pak reading depend on? How to ensure that the newly added pak can overwrite the resources in the original pak when the hot update is required? The answer depends on the ReadOrder of FPakListEntry : a large pak of ReadOrder will cover the resource content of a small pak

// IPlatformFilePak.h
struct FPakListEntry
{
    
    
	FPakListEntry(): ReadOrder(0), PakFile(nullptr){
    
    }

	uint32		ReadOrder;
	TRefCountPtr<FPakFile>	PakFile;
	
	// ReadOrder越大,优先级越高。故热更的时候只需要保证新pak的ReadOrder大于原来的pak即可
	FORCEINLINE bool operator < (const FPakListEntry& RHS) const
	{
    
    
		return ReadOrder > RHS.ReadOrder;
	}
};	

This ReadOrder is FPakPlatformFile::Mount(const TCHAR* InPakFilename, uint32 PakOrder, ...)specified, if not specified, use the lowest priority 0

Before entering the mount, the Initialize function will assign a ReadOrder to the pak (that is, PakOrder below) according to the path and name of the pak.

directory priority

// IPlatformFilePak.cpp
bool FPakPlatformFile::Initialize(IPlatformFile* Inner, const TCHAR* CmdLine)
{
    
    
	……
#if EXCLUDE_NONPAK_UE_EXTENSIONS && !WITH_EDITOR
	// Extensions for file types that should only ever be in a pak file. Used to stop unnecessary access to the lower level platform file
	ExcludedNonPakExtensions.Add(TEXT("uasset"));
	ExcludedNonPakExtensions.Add(TEXT("umap"));
	ExcludedNonPakExtensions.Add(TEXT("ubulk"));
	ExcludedNonPakExtensions.Add(TEXT("uexp"));
	ExcludedNonPakExtensions.Add(TEXT("uptnl"));
	ExcludedNonPakExtensions.Add(TEXT("ushaderbytecode"));
#endif
	……
	// Find and mount pak files from the specified directories.
	TArray<FString> PakFolders;
	GetPakFolders(FCommandLine::Get(), PakFolders);
	MountAllPakFiles(PakFolders, *StartupPaksWildcard);
	……
}

When FPakPlatformFile is initialized, in addition to initializing some member variables, it will also call MountAllPakFiles to mount the pak.

int32 FPakPlatformFile::MountAllPakFiles(const TArray<FString>& PakFolders, const FString& WildCard)
{
    
    
	……
	if (bMountPaks)
	{
    
    
		TArray<FString> FoundPakFiles;
		FindAllPakFiles(LowerLevel, PakFolders, WildCard, FoundPakFiles);
		……
		for (int32 PakFileIndex = 0; PakFileIndex < FoundPakFiles.Num(); PakFileIndex++)
		{
    
    
			const FString& PakFilename = FoundPakFiles[PakFileIndex];
			……
			uint32 PakOrder = GetPakOrderFromPakFilePath(PakFilename);

			UE_LOG(LogPakFile, Display, TEXT("Mounting pak file %s."), *PakFilename);

			if (Mount(*PakFilename, PakOrder))
			{
    
    
				++NumPakFilesMounted;
			}
		}
	}
	return NumPakFilesMounted;
}

MountAllPakFiles actually reads the pak that needs to be mounted, traverses and mounts.
Among them, GetPakOrderFromPakFilePath obtains its priority according to the directory to which the pak belongs:

int32 FPakPlatformFile::GetPakOrderFromPakFilePath(const FString& PakFilePath)
{
    
    
	if (PakFilePath.StartsWith(FString::Printf(TEXT("%sPaks/%s-"), *FPaths::ProjectContentDir(), FApp::GetProjectName())))
	{
    
    
		return 4;
	}
	else if (PakFilePath.StartsWith(FPaths::ProjectContentDir()))
	{
    
    
		return 3;
	}
	else if (PakFilePath.StartsWith(FPaths::EngineContentDir()))
	{
    
    
		return 2;
	}
	else if (PakFilePath.StartsWith(FPaths::ProjectSavedDir()))
	{
    
    
		return 1;
	}

	return 0;
}

It can be seen that the basic strategy for establishing pak read priority is: Project/Content/Paks/ProjectName-*.pak > Project/Content/Paks/*.pak > Engine/Content/Paks/*.pak > Project/Saved/Paks/*.pak

Prioritize by filename

After the above directory priority processing, if the file name ends with _n_P, its priority will be raised to P ak Order + ( n + 1 ) × 100 ( n ≥ 1 ) PakOrder + (n + 1) × 100 \space\space\space(n \geq 1)P ak O r d er+(n+1)×100   (n1)

// IPlatformFilePak.cpp
bool FPakPlatformFile::Mount(const TCHAR* InPakFilename, uint32 PakOrder, const TCHAR* InPath /*= NULL*/, bool bLoadIndex /*= true*/)
{
    
    
	……
	if (PakFilename.EndsWith(TEXT("_P.pak")))
	{
    
    
		// Prioritize based on the chunk version number
		// Default to version 1 for single patch system
		uint32 ChunkVersionNumber = 1;
		FString StrippedPakFilename = PakFilename.LeftChop(6);
		int32 VersionEndIndex = PakFilename.Find("_", ESearchCase::CaseSensitive, ESearchDir::FromEnd);
		if (VersionEndIndex != INDEX_NONE && VersionEndIndex > 0)
		{
    
    
			int32 VersionStartIndex = PakFilename.Find("_", ESearchCase::CaseSensitive, ESearchDir::FromEnd, VersionEndIndex - 1);
			if (VersionStartIndex != INDEX_NONE)
			{
    
    
				VersionStartIndex++;
				FString VersionString = PakFilename.Mid(VersionStartIndex, VersionEndIndex - VersionStartIndex);
				if (VersionString.IsNumeric())
				{
    
    
					int32 ChunkVersionSigned = FCString::Atoi(*VersionString);
					if (ChunkVersionSigned >= 1)
					{
    
    
						// Increment by one so that the first patch file still gets more priority than the base pak file
						ChunkVersionNumber = (uint32)ChunkVersionSigned + 1;
					}
				}
			}
		}
		PakOrder += 100 * ChunkVersionNumber;
	}
	……
}

In summary

How to establish pak priority:

  1. The basic strategy for establishing pak read priority is: Project/Content/Paks/ProjectName-*.pak > Project/Content/Paks/*.pak > Engine/Content/Paks/*.pak > Project/Saved/Paks/*.pak

  2. After the rough processing of the above directory priority, if the file name ends with _n_P, its priority will be raised to P ak O rder + ( n + 1 ) × 100 ( n ≥ 1 ) PakOrder + (n + 1) × 100 \space\space\space(n \geq 1)P ak O r d er+(n+1)×100   (n1)

Supongo que te gusta

Origin blog.csdn.net/weixin_44559752/article/details/128695492
Recomendado
Clasificación