Cocos2dx和Mac应用程序混合开发阻塞主线程

因为公司的需要,我们需要做一个Cocos2dx 和 Mac原生混合的一个应用出来,但是期间出现了一些问题,因为我们原生APP已经做完在那边了,所以需要在原生APP里面插入Cocos2dx的内容,所以导致了一些问题。

我们这个版本是基于Cocos2dx 3.15.1,如果是2.x版本的朋友应该没有这个问题,原因是因为cocos2dx内部的loop形式不一样。

ok,进入正题。
首先我们可以看下 “CCApplication-mac.h” 这个文件中的 “run”方法

  int Application::run()
  {
      initGLContextAttrs();
      if(!applicationDidFinishLaunching())
      {
          return 1;
      }
  //    
  //    long lastTime = 0L;
   //    long curTime = 0L;

    auto director = Director::getInstance();
    auto glview = director->getOpenGLView();

    while (!glview->windowShouldClose()) {
        long lastTime = getCurrentMillSecond();
        director->mainLoop();
        glview->pollEvents();
        long curTime = getCurrentMillSecond();
        if (curTime - lastTime < _animationInterval)
        {
            usleep(static_cast((_animationInterval - curTime + lastTime)*1000));
        }
    }


    /* Only work on Desktop
    *  Director::mainLoop is really one frame logic
    *  when we want to close the window, we should call Director::end();
    *  then call Director::mainLoop to do release of internal resources
    */
    if (glview->isOpenGLReady())
    {
        director->end();
        director->mainLoop();
    }

    glview->release();
    return 0;
  }

其中我们应该可以看到

  while (!glview->windowShouldClose()) 

这一个while就是起了一个cocos内部使用的runloop,然后开始loop自己的一个消息循环。那么问题来了,他在主线程上面,他什么时候可以进入到下一个Mac的loop呢?就是当glView被close的时候。

那么问题来了,也就是游戏在跑的时候,我mac的主线程是被阻塞的。这很明显不合理。

然后有人可能会想一个办法,那就是在子线程调用run,像这样:

      dispatch_async(dispatch_queue_create("cocos", 0), ^{
        run();
      });

这又有一个问题,那就是画面的绘制必须在主线程。所以这个方法行不通。

那么我们可以插入到主线程的loop中,like this

dispatch_async(dispatch_queue_create("cocos", 0), ^{
    while (!glview->windowShouldClose())
    {
        long lastTime = getCurrentMillSecond();
        dispatch_semaphore_t tt = dispatch_semaphore_create(0);
        dispatch_async(dispatch_get_main_queue(), ^{
            director->mainLoop();
            glview->pollEvents();
            dispatch_semaphore_signal(tt);
        });
        dispatch_semaphore_wait(tt, DISPATCH_TIME_FOREVER);

        long curTime = getCurrentMillSecond();
        if (curTime - lastTime < _animationInterval)
        {
            usleep(static_cast((_animationInterval - curTime + lastTime)*1000));
        }
    }

});

以为这样就ok了?问题肯定不会着么简单,然后我发现系统崩溃在:

glfwPollEvents();

系统报错:

WechatIMG7.jpeg

一脸懵逼,说是autoReleasePool被干掉了。然后发现这个glfw是一个框架,cocos倒入的是一个.a文件,然后我们在去找了这个glfw的源码

void _glfwPlatformPollEvents(void)
{
    for (;;)
  {
      NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny
                                        untilDate:[NSDate distantPast]
                                           inMode:NSDefaultRunLoopMode
                                          dequeue:YES];
    if (event == nil)
        break;

       [NSApp sendEvent:event];
    }

[_glfw.ns.autoreleasePool drain];
_glfw.ns.autoreleasePool = [[NSAutoreleasePool alloc] init];
}

发现这里其实是监听了mac的原生事件的,所以我们点击屏幕的时候他还是会响应的,具体这个怎么响应的大家去看看mac的runloop
那么我们下面看到一句就是

[_glfw.ns.autoreleasePool drain];
_glfw.ns.autoreleasePool = [[NSAutoreleasePool alloc] init];

问题就是这两句造成的,所以很果断的把她注释掉。实际上,大家可能要问,为啥不把所有代码注释掉,因为反正不会阻塞主线程了,我还是觉得大家先去看看runloop。

然后我们重新编译 出一个 glfw.a 文件导入到工程

编译过了,主线程也ok了。喜大普泵。

没错一般情况下呢,现在这样做很多应用可以解决了,但是我们接入了基于WebRtc的音视频服务,然而这个视频呢又是通过opengl渲染的,你发现控制台一直报错

Cocos2dx和Mac应用程序混合开发阻塞主线程_第1张图片
WX20170817-170630.png

然后查资料发现这个错误就是 上下文指向的问题造成的,然后我们修改一下代码

dispatch_async(dispatch_queue_create("cocos", 0), ^{
        while (!glview->windowShouldClose())
        {
        long lastTime = getCurrentMillSecond();
        
        dispatch_semaphore_t tt = dispatch_semaphore_create(0);
        @autoreleasepool {
            dispatch_async(dispatch_get_main_queue(), ^{
                auto window = ((cocos2d::GLViewImpl*)glview)->getWindow();
                glfwMakeContextCurrent(window);
                
                director->mainLoop();
                glview->pollEvents();
                dispatch_semaphore_signal(tt);
            });
        }
        
        dispatch_semaphore_wait(tt, DISPATCH_TIME_FOREVER);
        
        long curTime = getCurrentMillSecond();
        if (curTime - lastTime < _animationInterval)
        {
            usleep(static_cast((_animationInterval - curTime + lastTime)*1000));
        }
    }
});

然后就发现不错可以用了。

文章写的很烂,也懒得排版了,哈哈哈

你可能感兴趣的:(Cocos2dx和Mac应用程序混合开发阻塞主线程)