UE4 APK内置资源包加载流程
UE4支持多种安卓资源包格式如 ATC,DXT,ETC1,ETC2,PVRTC,ASTC等,资源加载时需根据具体手机型号加载具体的资源格式,本文主要介绍了UE4在安卓平台的资源加载流程。
游戏启动首先会调LanchAndroid.cpp中的AndroidMain(struct android_app* stat)函数,该函数中会执行一些java环境加载,资源包挂载,引擎初始化等操作。在引擎初始化中会创建并初始化渲染硬件接口层(RHI)并在RHI的初始化中设置该系统所支持的资源格式,并在后面的资源加载中根据所支持的格式加载合适的资源。
挂载资源
挂载资源会将obb(main.obb.png)中的资源建立文件名与文件位置及其他相关信息的映射关系,将该映射关系存到FZipUnionFile中的Entries中, Entries是获取磁盘文件的桥梁。
主要代码如下:
AndroidFile.cpp
Entries.Add(
FileName,
MakeShareable(new Entry(
MakeShareable(new FFileHandleAndroid(
*file, EntryOffset, UncompressedLength)),
FileName,
WhenModified)));
可通过AndroidFile.cpp中提供的IFileHandle* OpenRead(const TCHAR* Filename, bool bAllowWrite = false)函数获取文件访问句柄
IFileHandle* OpenRead(const TCHAR* Filename, bool AllowLocal, bool bAllowWrite)
{
...
return new FFileHandleAndroid(
*ZipResource.GetEntry(AssetPath).File,
0, ZipResource.GetEntry(AssetPath).File->Size());
...
}
设置平台所支持的资源格式
在创建RHI(渲染硬件接口)时应用程序便初始化了平台所支持的资源压缩格式,资源打包时也存储了资源的格式,在加载资源时可判断该平台是否支持该资源格式
设置资源格式流程
主要代码如下:
AndroidPlatform:OpenGlDevice.cpp
IosPlatform:MetalRHI.cpp
if ( FOpenGL::SupportsPVRTC() )
{
SetupTextureFormat( PF_PVRTC2, FOpenGLTextureFormat(GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG, GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG, GL_RGBA, GL_UNSIGNED_BYTE, true, false));
SetupTextureFormat( PF_PVRTC4, FOpenGLTextureFormat(GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG, GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG, GL_RGBA, GL_UNSIGNED_BYTE, true, false));
}
if ( FOpenGL::SupportsATITC() )
{
SetupTextureFormat( PF_ATC_RGB, FOpenGLTextureFormat(GL_ATC_RGB_AMD, GL_ATC_RGB_AMD, GL_RGBA, GL_UNSIGNED_BYTE, true, false));
SetupTextureFormat( PF_ATC_RGBA_E, FOpenGLTextureFormat(GL_ATC_RGBA_EXPLICIT_ALPHA_AMD, GL_ATC_RGBA_EXPLICIT_ALPHA_AMD, GL_RGBA, GL_UNSIGNED_BYTE, true, false));
SetupTextureFormat( PF_ATC_RGBA_I, FOpenGLTextureFormat(GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD, GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD, GL_RGBA, GL_UNSIGNED_BYTE, true, false));
}
if ( FOpenGL::SupportsETC1() )
{
SetupTextureFormat( PF_ETC1, FOpenGLTextureFormat(GL_ETC1_RGB8_OES, GL_ETC1_RGB8_OES, GL_RGBA, GL_UNSIGNED_BYTE, true, false));
}
SetupTextureFormat()函数实际上设置了GPixelFormats
static inline void SetupTextureFormat( EPixelFormat Format, const FOpenGLTextureFormat& GLFormat)
{
GOpenGLTextureFormats[Format] = GLFormat;
GPixelFormats[Format].Supported = (GLFormat.Format != GL_NONE && (GLFormat.InternalFormat[0] != GL_NONE || GLFormat.InternalFormat[1] != GL_NONE));
}
后面加载资源时都将根据GPixelFormats[Format].Supported来判断是否是支持的格式。
加载游戏对象
初始化渲染硬件接口后程序会加载游戏对象并根据所支持的资源格式加载资源其流程大致如下
在序列化Texture资源的时候会根据平台所支持的格式加载资源, 如下代码所示:
void UTexture::SerializeCookedPlatformData(FArchive& Ar)
{
if (IsTemplate() )
{
return;
}
DECLARE_SCOPE_CYCLE_COUNTER( TEXT("UTexture::SerializeCookedPlatformData"), STAT_Texture_SerializeCookedData, STATGROUP_LoadTime );
UEnum* PixelFormatEnum = UTexture::GetPixelFormatEnum();
{
FTexturePlatformData** RunningPlatformDataPtr = GetRunningPlatformData();
if ( RunningPlatformDataPtr == NULL )
return;
FTexturePlatformData*& RunningPlatformData = *RunningPlatformDataPtr;
FName PixelFormatName = NAME_None;
CleanupCachedRunningPlatformData();
check( RunningPlatformData == NULL );
RunningPlatformData = new FTexturePlatformData();
Ar << PixelFormatName;
while (PixelFormatName != NAME_None)
{
EPixelFormat PixelFormat = (EPixelFormat)PixelFormatEnum->GetValueByName(PixelFormatName);
int32 SkipOffset = 0;
Ar << SkipOffset;
bool bFormatSupported = GPixelFormats[PixelFormat].Supported;
if (RunningPlatformData->PixelFormat == PF_Unknown && bFormatSupported)
{
// Extra arg is unused here because we're loading
const bool bStreamable = false;
RunningPlatformData->SerializeCooked(Ar, this, bStreamable);
}
else
{
Ar.Seek(SkipOffset);
}
Ar << PixelFormatName;
}
}
if( Ar.IsLoading() )
{
LODBias = 0;
}
}
其中这段代码为根据具体平台加载资源
RunningPlatformData = new FTexturePlatformData();
Ar << PixelFormatName;
while (PixelFormatName != NAME_None)
{
EPixelFormat PixelFormat = (EPixelFormat)PixelFormatEnum->GetValueByName(PixelFormatName);
int32 SkipOffset = 0;
Ar << SkipOffset;
bool bFormatSupported = GPixelFormats[PixelFormat].Supported;
if (RunningPlatformData->PixelFormat == PF_Unknown && bFormatSupported)
{
// Extra arg is unused here because we're loading
const bool bStreamable = false;
RunningPlatformData->SerializeCooked(Ar, this, bStreamable);
}
else
{
Ar.Seek(SkipOffset);
}
Ar << PixelFormatName;
}