先回顾第一篇引擎初始化时,最先执行的几个main函数:
wWinMain() => UnityMain() => UnityMainImpl()
int UnityMainImpl(HINSTANCE hInst, HINSTANCE hPrev, LPWSTR szCmdLine, int nCmdShow)
{
...
if (!PlayerInitEngineGraphics()) //初始化 图形引擎
winutils::DisplayErrorMessagesAndQuit("Failed to initialize player");
Assert(IsGfxDevice());
...
}
bool PlayerInitEngineGraphics(bool batchmode /*= false*/)
{
PROFILER_AUTO(kPlayerInitWithGraphics);
// Initialize Engine (eg. Mastercontext, Shaderlab, Setup RTTI, Call Static Initialize functions, Setup messages and Tags)
if (!InitializeEngineGraphics(batchmode)) //跳转
{
#if PLATFORM_WIN && !defined(UNITY_WIN_API_SUBSET)
winutils::AddErrorMessage("InitializeEngineGraphics failed");
#endif
printf_console("PlayerInitEngineGraphics: InitializeEngineGraphics failed\n");
return false;
}
...
INVOKE_GLOBAL_CALLBACK(doneInitializingEngineGraphics); //全局函数调用,通知其他模块做各自的处理
return true;
}
bool InitializeEngineGraphics(bool batch)
{
if (g_sIsEngineGraphicsInitialized == false)
{
...
#if PLATFORM_OSX || PLATFORM_LINUX || PLATFORM_WEBGL || (PLATFORM_WIN && !defined(UNITY_WIN_API_SUBSET)) && !PLATFORM_WINRT
if (!InitializeGfxDevice()) //初始化对应平台的图形设备
return false;
#endif
InitScalableBufferManager();
ShaderLab::InitShaderLab();
ShaderPassContextInitialize();
...
}
return true;
}
图形引擎的初始化主要是为了实例化出当前平台所选择的图形API的包装对象,即 GfxDevice 也叫做图形中间抽象层。虚幻引擎称作RHI全称是Render Hardware Interface(渲染硬件接口)
GfxDevice* InitializeGfxDevice()
{
...
GfxCreateDeviceFlags deviceFlags = kGfxCreateDeviceFlagNone;
GfxDevice* device = NULL;
INVOKE_GLOBAL_CALLBACK(beforeInitializeEngineGraphics);
...
# if ENABLE_FORCE_GFX_RENDERER //如果我们在引擎设置里强制使用特定的图形设备
if (g_ForcedGfxRenderer >= 0 && g_ForcedGfxRenderer < kGfxRendererCount)
{
printf_console("Forcing GfxDevice: %s\n", GetGfxDeviceTypeName(g_ForcedGfxRenderer));
...
device = CreateGfxDevice(g_ForcedGfxRenderer, deviceFlags);
}
# endif // if ENABLE_FORCE_GFX_RENDERER
...
#if (PLATFORM_WIN) && !defined(UNITY_WIN_API_SUBSET) // Windows & WinRT
#if GFX_SUPPORTS_D3D11 // try DX11
if (!device)
device = CreateGfxDevice(kGfxRendererD3D11, deviceFlags);
#endif
...
#elif UNITY_GFX_EXTERNAL_SELECT_RENDERING_API // iOS / Android / MacOS
#if GFX_SUPPORTS_VULKAN
if (!device)
{
...
if (useVulkan)
{
device = CreateGfxDevice(kGfxRendererVulkan, deviceFlags);
}
}
}
#endif // GFX_SUPPORTS_VULKAN
if (!device)
{
GfxDeviceRenderer renderer = ExternalSelectRenderingAPI();
device = CreateGfxDevice(renderer, deviceFlags);
}
#elif PLATFORM_LINUX && GFX_SUPPORTS_OPENGL_UNIFIED
if (!device)
device = CreateGfxDevice(kGfxRendererOpenGLCore, deviceFlags);
#elif PLATFORM_LINUX && !ENABLE_SDL2_WINDOWING && GFX_SUPPORTS_NULL
if (!device)
{
device = CreateNullGfxDevice();
}
#elif PLATFORM_PS4
if (!device)
{
GfxDevicePS4::SetGnmThreadingMode(GetGfxThreadingMode());
device = CreateGfxDevice(kGfxRendererPS4, deviceFlags);
}
#elif PLATFORM_XBOXONE
if (!device)
{
const dynamic_array<int>& apis = GetBuildSettings().GetGraphicsAPIs();
GfxDeviceRenderer renderer = static_cast<GfxDeviceRenderer>(apis[0]);
device = CreateGfxDevice(renderer, deviceFlags);
}
#elif GFX_SUPPORTS_VULKAN && !GFX_SUPPORTS_NULL
if (!device)
{
device = CreateGfxDevice(kGfxRendererVulkan, deviceFlags);
}
#elif GFX_SUPPORTS_NULL
if (!device)
{
extern GfxDevice* CreateNullGfxDevice();
device = CreateNullGfxDevice();
}
#else
if (!device)
{
extern GfxDevice* PlatformCreateClientGfxDevice(GfxCreateDeviceFlags deviceFlags);
device = PlatformCreateClientGfxDevice(deviceFlags);
}
#endif
if (!device) // Early out on failure
return NULL;
...
return device;
}
可以看到在构造 device 前做了很多的平台判断 :强制指定、Windows、iOS 、Android 、MacOS、LINUX、PS4、XBOXONE 等等。
我暂时选取 Windows 下 OpenGLES 的流程分析。
GfxDevice* CreateGfxDevice(GfxDeviceRenderer api, GfxCreateDeviceFlags /*flags*/)
{
return CreateRealGfxDevice(api);
}
GfxDevice* CreateRealGfxDevice(GfxDeviceRenderer renderer)
{
...
#if GFX_SUPPORTS_D3D12
if (IsD3D12Renderer(renderer))
{
GfxDevice* CreateD3D12GfxDevice(void);
return CreateD3D12GfxDevice();
}
#endif
#if GFX_SUPPORTS_D3D11 && !defined(UNITY_WIN_API_SUBSET)
if (renderer == kGfxRendererD3D11)
{
GfxDevice* CreateD3D11GfxDevice(void);
return CreateD3D11GfxDevice();
}
#endif
#if GFX_SUPPORTS_VULKAN
if (renderer == kGfxRendererVulkan)
{
GfxDevice* CreateVKGfxDevice(void);
return CreateVKGfxDevice();
}
#endif
#if GFX_SUPPORTS_NULL
if (renderer == kGfxRendererNull)
{
GfxDevice* CreateNullGfxDevice(void);
return CreateNullGfxDevice();
}
#endif
#if GFX_SUPPORTS_OPENGL_UNIFIED //OpenGLES 的创建
if (IsUnifiedGLRenderer(renderer))
{
extern GfxDevice* CreateGLESGfxDevice(GfxDeviceRenderer renderer);
return CreateGLESGfxDevice(renderer);
}
#endif
#if GFX_SUPPORTS_METAL
if (renderer == kGfxRendererMetal)
{
extern GfxDevice* CreateMetalGfxDevice();
return CreateMetalGfxDevice();
}
#endif
#if PLATFORM_SUPPORTS_PS4GFX
extern GfxDevice* CreateGNMGfxDevice(void);
extern GfxThreadingMode g_GfxThreadingMode;
GfxDevicePS4::SetGnmThreadingMode(g_GfxThreadingMode);
GfxDevice* device = CreateGNMGfxDevice();
SetGfxDevice(device);
SetRealGfxDevice(device);
((GfxDevicePS4*)device)->Init();
device->AcquireThreadOwnership();
return device;
#endif
#if GFX_SUPPORTS_PLATFORM
extern GfxDevice* PlatformCreateRealGfxDevice(void);
return PlatformCreateRealGfxDevice();
#endif
// we don't support the passed platform
return NULL;
}
再次根据所选的图形API做判断 DX11、DX12、VULKAN、OpenGL、METAL 等。
GfxDevice* CreateGLESGfxDevice(GfxDeviceRenderer renderer)
{
PROFILER_AUTO(kProfilerCreateGLESGfxDevice);
//根据上一步传进来的api类型做选择
GfxDeviceLevelGL requestedLevel = kGfxLevelUninitialized;
switch (renderer)
{
case kGfxRendererOpenGLES20:
requestedLevel = kGfxLevelES2;
break;
case kGfxRendererOpenGLES3x:
requestedLevel = kGfxLevelESLast;
break;
case kGfxRendererOpenGLCore:
requestedLevel = kGfxLevelCoreLast;
break;
default:
Assert(0);
break;
}
//其他强制设置
# if ENABLE_FORCE_GFX_RENDERER
requestedLevel = (g_ForcedGLLevel != kGfxLevelUninitialized ? g_ForcedGLLevel : requestedLevel);
# endif
#if (ENABLE_EGL || UNITY_APPLE_PVR) //是否开启 EGL
GfxDeviceLevelGL bestAllowedLevel = kGfxLevelESLast;
// This returns a specific level or the best available depending on PlayerSettings
switch (UnityGetSelectedGLESVersion())
{
case 2:
bestAllowedLevel = kGfxLevelES2;
break;
case 3:
bestAllowedLevel = kGfxLevelES3;
break;
case 4:
bestAllowedLevel = kGfxLevelES31; // Note: on iphone, 4 denotes Metal but in that case we'll never get here anyway.
break;
case 5:
bestAllowedLevel = kGfxLevelES31AEP;
break;
case 6:
bestAllowedLevel = kGfxLevelES32;
break;
}
// Set the retrieved best level only if no specific level has been forced with command-line option
if (requestedLevel == kGfxLevelESLast)
requestedLevel = bestAllowedLevel;
#elif PLATFORM_OSX
requestedLevel = std::min(requestedLevel, kGfxLevelCore41);
#elif PLATFORM_WEBGL
requestedLevel = std::min(requestedLevel, kGfxLevelES3);
#endif
//上面又经过一轮判断,选择一个特定版本 OpenGL 进行初始化
GfxDeviceGLES* device = UNITY_NEW_AS_ROOT(GfxDeviceGLES, kMemGfxDevice, "Rendering", "GfxDeviceGLES") ();
if (device->Init(requestedLevel))
return device;
DebugAssertMsg(device, "ERROR: Failed to initialized a GfxDeviceGLES instance");
UNITY_DELETE(device, kMemGfxDevice);
return NULL;
}
OpenGL 有多种版本,有主要支持PC的核心模式(Core-profile)版本,和 主要支持移动设备的ES(Embedded Systems 相当于核心版本的子集)版本。同时 OpenGL3.X 以上的版本是当前主流。而 EGL 又是 OpenGLES 和原生窗口系统之间的沟通桥梁。
class GfxDeviceGLES 是图形设备的 OpenGLES 的实现,他继承自class GfxThreadableDevice : public GfxDevice。class GfxDevice 是所有图形接口的基类,它是底层图形api的各种功能的包装,内部的实现是unity图形引擎精华所在, 比如 设置混合状态SetBlendState()、设置深度SetDepthState()、绘制相关的 DrawBuffers() 、DrawBuffersIndirect() 等等。
其他图形的API各种实现如下:
接下来看看设备初始化的细节:
bool GfxDeviceGLES::Init(GfxDeviceLevelGL deviceLevel)
{
void* masterContextPointer = NULL;
g_RequestedGLLevel = deviceLevel; //上一步传入的opengl等级 2.x 3.x 4.x ?
//1 桌面版本的opengl初始化,在windows中的opengl实现叫 WGL不细讲,可自行百度
#if UNITY_DESKTOP
#if PLATFORM_WIN
SetMasterContextClassName(L"WindowGLClassName");
GfxDeviceLevelGL CreateMasterGraphicsContext(GfxDeviceLevelGL level);
deviceLevel = CreateMasterGraphicsContext(deviceLevel);
if (deviceLevel == kGfxLevelUninitialized)
return false;
#endif
GraphicsContextHandle masterContext = GetMasterGraphicsContext();
if (!masterContext.IsValid())
return false;
SetMainGraphicsContext(masterContext);
ActivateGraphicsContext(masterContext, true, kGLContextSkipGfxDeviceMakeCurrent);
GraphicsContextGL* context = OBJECT_FROM_HANDLE(masterContext, GraphicsContextGL);
masterContextPointer = context;
//2 这里是移动端的opengl实现 带ES后缀的
#else
// Read in context version
ContextGLES::Create(contextLevelToGLESVersion(deviceLevel)); //下一步
masterContextPointer = gl::ContextHandle::DummyMaster().Get(); // masterContextPointer should not be null when calling SetActiveContext
#endif
GLES_ASSERT(&m_Api, masterContextPointer, "No master context created");
// Initialize context and states
g_DeviceStateGLES = &m_State;
if (IsGfxLevelES2(deviceLevel))
m_Renderer = kGfxRendererOpenGLES20;
else if (IsGfxLevelES(deviceLevel))
m_Renderer = kGfxRendererOpenGLES3x;
else if (IsGfxLevelCore(deviceLevel))
m_Renderer = kGfxRendererOpenGLCore;
else
GLES_ASSERT(&m_Api, 0, "OPENGL ERROR: Invalid device level");
...
return true;
}
//EGL的固定初始化步骤
bool ContextGLES::Create(int glesVersion)
{
...
//原生的窗口句柄
HWND hWnd = GetScreenManager().GetWindow();
...
EGLint numConfigs;
EGLint majorVersion;
EGLint minorVersion;
//自定义的属性
const EGLint configAttribs[] =
{
EGL_LEVEL, 0,
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RENDERABLE_TYPE, (glesVersion == 2) ? EGL_OPENGL_ES2_BIT : EGL_OPENGL_ES3_BIT_KHR,
EGL_NATIVE_RENDERABLE, EGL_FALSE,
EGL_DEPTH_SIZE, 16,
EGL_ALPHA_SIZE, 1,
EGL_STENCIL_SIZE, 1,
EGL_SAMPLES, 0,
EGL_NONE
};
//从 原生设备 获取 EGL的逻辑设备
sOpenGLESData.dsp = eglGetDisplay(hWnd ? GetDC(hWnd) : EGL_DEFAULT_DISPLAY);
if (sOpenGLESData.dsp == EGL_NO_DISPLAY)
{
printf_console("GLES30: eglGetDisplay failed\n");
return false;
}
bool last = ::gAlreadyClosing;
::gAlreadyClosing = true;
// 初始化 EGL
if (!eglInitialize(sOpenGLESData.dsp, &majorVersion, &minorVersion))
{
printf_console("GLES30: eglInitialize failed\n");
return false;
}
// 根据自定义属性 获取 相关配置
if (!eglChooseConfig(sOpenGLESData.dsp, configAttribs, &sOpenGLESData.cfg, 1, &numConfigs))
{
printf_console("GLES30: eglChooseConfig failed\n");
return false;
}
// 创建EGL的绘制表面
sOpenGLESData.surf = eglCreateWindowSurface(sOpenGLESData.dsp, sOpenGLESData.cfg, NativeWindowType(hWnd), NULL);
if (sOpenGLESData.surf == EGL_NO_SURFACE)
{
printf_console("GLES30: eglCreateWindowSurface failed\n");
return false;
}
//创建GL的上下文环境
EGLint ctxAttribList[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE};
sOpenGLESData.cxt = eglCreateContext(sOpenGLESData.dsp, sOpenGLESData.cfg, EGL_NO_CONTEXT, ctxAttribList);
if (sOpenGLESData.cxt == EGL_NO_CONTEXT)
{
printf_console("GLES30: eglCreateContext failed\n");
return false;
}
//设置上下文
if (!eglMakeCurrent(sOpenGLESData.dsp, sOpenGLESData.surf, sOpenGLESData.surf, sOpenGLESData.cxt))
{
printf_console("GLES30: eglMakeCurrent failed\n");
return false;
}
::gAlreadyClosing = last;
GLESAssert();
return true;
#endif
}
以上选取了OpenGLES的初始化步骤,接下来看一下另一个比较重要的api工具,类 GfxDeviceGLES 中的一个成员变量 ApiGLES m_Api;
class ApiGLES : public ApiFuncGLES, private NonCopyable {}
struct ApiFuncGLES{}
通过名字就可以看出来它就是最底层的OpenGL接口。ApiGLES 是一系列的简单封装的工具,而 ApiFuncGLES 是图形API的最小粒度函数集合。
void ApiGLES::Init(const GfxContextGLES& context, GfxDeviceLevelGL &deviceLevel)
{
...
// Load the OpenGL API pointers
Load(deviceLevel);
...
}
//看到了非常熟悉的api集合
void ApiGLES::Load(GfxDeviceLevelGL level)
{
...
ApiGLES& api = *this;
...
GLES_GET_FUNC(api, BindBuffer);
GLES_GET_FUNC(api, BindFramebuffer);
GLES_GET_FUNC(api, BindRenderbuffer);
GLES_GET_FUNC(api, Clear);
GLES_GET_FUNC(api, ClearColor);
GLES_GET_FUNC(api, Uniform1fv);
GLES_GET_FUNC(api, Uniform1i);
GLES_GET_FUNC(api, Uniform1iv);
GLES_GET_FUNC(api, Uniform1uiv);
GLES_GET_FUNC(api, DrawArrays);
GLES_GET_FUNC(api, DrawElements);
GLES_GET_FUNC(api, DrawArraysIndirect);
GLES_GET_FUNC(api, DrawElementsIndirect);
GLES_GET_FUNC(api, DrawArraysInstanced);
GLES_GET_FUNC(api, DrawElementsInstanced);
...
}
最后,看一段创建shader的简易封装的实现,都是最基本的步骤:创建shader、设置shader代码、编译shader。
GLuint ApiGLES::CreateShader(gl::ShaderStage stage, const char* source)
{
GLES_CHECK(this, -1);
GLES_ASSERT(this, source, "'source' is null, pass a valid GLSL shader source");
GLuint shaderName = 0;
//创建shader
GLES_CALL_RET(this, shaderName, glCreateShader, gl::GetShaderStage(stage));
GLES_ASSERT(this, shaderName, "Shader object failed to be created");
//设置shader源码
GLES_CALL(this, glShaderSource, shaderName, 1, &source, NULL);
//编译shader
GLES_CALL(this, glCompileShader, shaderName);
GLES_CHECK(this, shaderName);
return shaderName;
}