Unity引擎源码解析(伪) - 7 引擎启动之图形初始化

先回顾第一篇引擎初始化时,最先执行的几个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 GfxDeviceclass 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;
}

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值