美文网首页iOS开发cocos 2dx
Cocos2dx和Mac应用程序混合开发阻塞主线程

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

作者: 佛祖拿屠刀 | 来源:发表于2017-08-17 17:08 被阅读38次

    因为公司的需要,我们需要做一个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<useconds_t>((_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<useconds_t>((_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渲染的,你发现控制台一直报错

    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<useconds_t>((_animationInterval - curTime + lastTime)*1000));
            }
        }
    });
    

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

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

    相关文章

      网友评论

        本文标题:Cocos2dx和Mac应用程序混合开发阻塞主线程

        本文链接:https://www.haomeiwen.com/subject/lwhnrxtx.html