Cocos2dx源码赏析(2)之渲染

Cocos2dx源码赏析(2)之渲染

随即篇,继续于源码的角度来跟下Cocos2dx引擎底渲染过程,以之来梳理下Cocos2dx引擎凡是安用相机行事等要素显示在屏幕上之。

从达成一致首对Cocos2dx启动流程的梳理中唯独领略,Cocos2dx依赖通过各平台的输入启动发动机,并当循环中调用Director::mainLoop方法来保障引擎的各样逻辑:

void Director::mainLoop()
{
    if (_purgeDirectorInNextLoop)
    {
        _purgeDirectorInNextLoop = false;
        purgeDirector();
    }
    else if (_restartDirectorInNextLoop)
    {
        _restartDirectorInNextLoop = false;
        restartDirector();
    }
    else if (! _invalid)
    {
        drawScene();

        // release the objects
        PoolManager::getInstance()->getCurrentPool()->clear();
    }
}

void Director::end()
{
    _purgeDirectorInNextLoop = true;
}

void Director::restart()
{
    _restartDirectorInNextLoop = true;
}

void Director::stopAnimation()
{
    _invalid = true;
}

当调用了Director::end()方法时,_purgeDirectorInNextLoop变量才会叫置为true,并履行了purgeDirector()方法:

void Director::purgeDirector()
{
    reset();

    CHECK_GL_ERROR_DEBUG();

    // OpenGL view
    if (_openGLView)
    {
        _openGLView->end();
        _openGLView = nullptr;
    }

    // delete Director
    release();
}

可观察,这里进行了一些重置和清理工作。即以需要了游戏的时,能够调用Director::end()方法,让引擎跳出主循环,执行关闭。

调用了Director::restart()方法时,_restartDirectorInNextLoop变量会吃置为true,即会举行restartDirector()方法:

void Director::restartDirector()
{
    reset();

    // RenderState need to be reinitialized
    RenderState::initialize();

    // Texture cache need to be reinitialized
    initTextureCache();

    // Reschedule for action manager
    getScheduler()->scheduleUpdate(getActionManager(), Scheduler::PRIORITY_SYSTEM, false);

    // release the objects
    PoolManager::getInstance()->getCurrentPool()->clear();

    // Restart animation
    startAnimation();

    // Real restart in script level
#if CC_ENABLE_SCRIPT_BINDING
    ScriptEvent scriptEvent(kRestartGame, nullptr);
    ScriptEngineManager::getInstance()->getScriptEngine()->sendEvent(&scriptEvent);
#endif
}

可观察,在restartDirector方法被,先举办了重置reset方法,然后还要随着将渲染状态、纹理缓存、定时器、内存管理、动画等以更开始化了。以这来贯彻休闲游还开的方案。

_invalid变量默认是true,刚开以Director::init中会面被置为false,在调用Director::stopAnimation()时,会用_invalid置为true,此时无满足条件,即不会师调用drawScene()绘制场景的办法,当然在调用Director::startAnimation()又谋面拿_invalid置为false,由是能够精通,当_invalid置为true时,引擎在做空循环。

下,才终于真正进入正题,即当_invalid为false时,会调用drawScene方法来绘制场景,设置定时器,动画,事件循环等一律多元处理:

void Director::drawScene()
{
    // calculate "global" dt
    calculateDeltaTime();

    if (_openGLView)
    {
        _openGLView->pollEvents();
    }

    //tick before glClear: issue #533
    if (! _paused)
    {
        _eventDispatcher->dispatchEvent(_eventBeforeUpdate);
        _scheduler->update(_deltaTime);
        _eventDispatcher->dispatchEvent(_eventAfterUpdate);
    }

    _renderer->clear();
    experimental::FrameBuffer::clearAllFBOs();
    /* to avoid flickr, nextScene MUST be here: after tick and before draw.
     * FIXME: Which bug is this one. It seems that it can't be reproduced with v0.9
     */
    if (_nextScene)
    {
        setNextScene();
    }

    pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);

    if (_runningScene)
    {
#if (CC_USE_PHYSICS || (CC_USE_3D_PHYSICS && CC_ENABLE_BULLET_INTEGRATION) || CC_USE_NAVMESH)
        _runningScene->stepPhysicsAndNavigation(_deltaTime);
#endif
        //clear draw stats
        _renderer->clearDrawStats();

        //render the scene
        _openGLView->renderScene(_runningScene, _renderer);

        _eventDispatcher->dispatchEvent(_eventAfterVisit);
    }

    // draw the notifications node
    if (_notificationNode)
    {
        _notificationNode->visit(_renderer, Mat4::IDENTITY, 0);
    }

    if (_displayStats)
    {
        showStats();
    }
    _renderer->render();

    _eventDispatcher->dispatchEvent(_eventAfterDraw);

    popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);

    _totalFrames++;

    // swap buffers
    if (_openGLView)
    {
        _openGLView->swapBuffers();
    }

    if (_displayStats)
    {
        calculateMPF();
    }
}

第一以drawScene方法被,会先调用calculateDelta提姆e方法来计量每帧的工夫间隔_delta提姆(Tim)e,即每帧执行同一多重逻辑操作所花费的时光。

接里判断了_openGLView,该目标是故来用OpenGL绘制的情节上现在异平台对应的视图上,那里不同之平台有差的凡促成。而_openGLView的赋值是以调用了Director::setOpenGLView方法里举行的,而setOpenGLView方法的调用,我们是于AppDelegate::applicationDidFinishLaunching()方法中调用的。所以,这里_openGLView正常处境下是无会晤为空的。那么,也不怕会合举行_openGLView->poll伊芙nts()方法,这多少个法子是独空实现,只当一定的平台才做相应的拍卖。一般会在该办法被,检查来没有接触发什么风波(键盘输入、鼠标移动等)。

还接着爆发只_paused的判断,而_paused为购置否true,即未满意条件是以调用了Director::pause方法中安装的,那么非满意条件时,就非会见履此的代码:

    if (! _paused)
    {
        _eventDispatcher->dispatchEvent(_eventBeforeUpdate);
        _scheduler->update(_deltaTime);
        _eventDispatcher->dispatchEvent(_eventAfterUpdate);
    }

否虽然是当调用了Director::pause的不二法门,然后上主循环,可是未会面应相应的轩然大波调度以及定时器的换代处理。

接轨朝着生实施,如下代码:

_renderer->clear();
experimental::FrameBuffer::clearAllFBOs();

此地,首假使在绘制前,执行相应的清理工作(例如:清除颜色缓冲区和深缓冲区,清除帧缓冲对象等)。

下一场,就推行这行代码了:

if (_nextScene)
{
    setNextScene();
}

追踪一下,能够找到,在调用了Director的replaceScene、pushScene或popScene等艺术时,会于_nextScene赋值,这几乎个法子的意分别是:
replaceScene:将要执行之情景压入场景栈中,并替换当前之光景,_nextScene指向如实施的场地。
pushScene:将要执行的景压入场景栈中,并以_nextScene指向设举行的现象。
popScene:在场景栈中弹奏有脚下情景,并拿_nextScene指向上一个底情景。

上述这五个措施仍然在生一帧绘制生效。在setNextScene会执行有景的状态切换,并将下一个而履行的景观指定为眼前运作的景色。

延续,再不怕行下的代码:

pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);

if (_runningScene)
{
#if (CC_USE_PHYSICS || (CC_USE_3D_PHYSICS && CC_ENABLE_BULLET_INTEGRATION) || CC_USE_NAVMESH)
    _runningScene->stepPhysicsAndNavigation(_deltaTime);
#endif
    //clear draw stats
    _renderer->clearDrawStats();

    //render the scene
    _openGLView->renderScene(_runningScene, _renderer);

    _eventDispatcher->dispatchEvent(_eventAfterVisit);
}

pushMatrix会将模型视图的矩阵压入相应的栈中。而相应之栈有存放模型视图矩阵的库房,投影矩阵的库房,纹理矩阵的堆栈。接下来,首要看renderScene方法的调用。

void GLView::renderScene(Scene* scene, Renderer* renderer)
{
    CCASSERT(scene, "Invalid Scene");
    CCASSERT(renderer, "Invalid Renderer");

    if (_vrImpl)
    {
        _vrImpl->render(scene, renderer);
    }
    else
    {
        scene->render(renderer, Mat4::IDENTITY, nullptr);
    }
}

这里,_vrImpl是关于VR的贯彻,这里先不关心。然后,就调用到了scene的render方法:

void Scene::render(Renderer* renderer, const Mat4* eyeTransforms, const Mat4* eyeProjections, unsigned int multiViewCount)
{
    auto director = Director::getInstance();
    Camera* defaultCamera = nullptr;
    const auto& transform = getNodeToParentTransform();

    for (const auto& camera : getCameras())
    {
        if (!camera->isVisible())
            continue;

        Camera::_visitingCamera = camera;
        if (Camera::_visitingCamera->getCameraFlag() == CameraFlag::DEFAULT)
        {
            defaultCamera = Camera::_visitingCamera;
        }

        // There are two ways to modify the "default camera" with the eye Transform:
        // a) modify the "nodeToParentTransform" matrix
        // b) modify the "additional transform" matrix
        // both alternatives are correct, if the user manually modifies the camera with a camera->setPosition()
        // then the "nodeToParent transform" will be lost.
        // And it is important that the change is "permanent", because the matrix might be used for calculate
        // culling and other stuff.
        for (unsigned int i = 0; i < multiViewCount; ++i) {
            if (eyeProjections)
                camera->setAdditionalProjection(eyeProjections[i] * camera->getProjectionMatrix().getInversed());
            if (eyeTransforms)
                camera->setAdditionalTransform(eyeTransforms[i].getInversed());
            director->pushProjectionMatrix(i);
            director->loadProjectionMatrix(Camera::_visitingCamera->getViewProjectionMatrix(), i);
        }

        camera->apply();
        //clear background with max depth
        camera->clearBackground();
        //visit the scene
        visit(renderer, transform, 0);
#if CC_USE_NAVMESH
        if (_navMesh && _navMeshDebugCamera == camera)
        {
            _navMesh->debugDraw(renderer);
        }
#endif

        renderer->render();
        camera->restore();

        for (unsigned int i = 0; i < multiViewCount; ++i)
            director->popProjectionMatrix(i);

        // we shouldn't restore the transform matrix since it could be used
        // from "update" or other parts of the game to calculate culling or something else.
//        camera->setNodeToParentTransform(eyeCopy);
    }

#if CC_USE_3D_PHYSICS && CC_ENABLE_BULLET_INTEGRATION
    if (_physics3DWorld && _physics3DWorld->isDebugDrawEnabled())
    {
        Camera *physics3dDebugCamera = _physics3dDebugCamera != nullptr ? _physics3dDebugCamera: defaultCamera;

        for (unsigned int i = 0; i < multiViewCount; ++i) {
            if (eyeProjections)
                physics3dDebugCamera->setAdditionalProjection(eyeProjections[i] * physics3dDebugCamera->getProjectionMatrix().getInversed());
            if (eyeTransforms)
                physics3dDebugCamera->setAdditionalTransform(eyeTransforms[i].getInversed());
            director->pushProjectionMatrix(i);
            director->loadProjectionMatrix(physics3dDebugCamera->getViewProjectionMatrix(), i);
        }

        physics3dDebugCamera->apply();
        physics3dDebugCamera->clearBackground();

        _physics3DWorld->debugDraw(renderer);
        renderer->render();

        physics3dDebugCamera->restore();

        for (unsigned int i = 0; i < multiViewCount; ++i)
            director->popProjectionMatrix(i);
    }
#endif

    Camera::_visitingCamera = nullptr;
//    experimental::FrameBuffer::applyDefaultFBO();
}

于这render方法中,重要关心六个措施的调用,即上边就简单尽代码:

visit(renderer, transform, 0);
renderer->render();

此间的visit会调用到父类Node节点相应的visit方法:

void Node::visit(Renderer* renderer, const Mat4 &parentTransform, uint32_t parentFlags)
{
    // quick return if not visible. children won't be drawn.
    if (!_visible)
    {
        return;
    }

    uint32_t flags = processParentFlags(parentTransform, parentFlags);

    // IMPORTANT:
    // To ease the migration to v3.0, we still support the Mat4 stack,
    // but it is deprecated and your code should not rely on it
    _director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
    _director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, _modelViewTransform);

    bool visibleByCamera = isVisitableByVisitingCamera();

    int i = 0;

    if(!_children.empty())
    {
        sortAllChildren();
        // draw children zOrder < 0
        for(auto size = _children.size(); i < size; ++i)
        {
            auto node = _children.at(i);

            if (node && node->_localZOrder < 0)
                node->visit(renderer, _modelViewTransform, flags);
            else
                break;
        }
        // self draw
        if (visibleByCamera)
            this->draw(renderer, _modelViewTransform, flags);

        for(auto it=_children.cbegin()+i, itCend = _children.cend(); it != itCend; ++it)
            (*it)->visit(renderer, _modelViewTransform, flags);
    }
    else if (visibleByCamera)
    {
        this->draw(renderer, _modelViewTransform, flags);
    }

    _director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);

    // FIX ME: Why need to set _orderOfArrival to 0??
    // Please refer to https://github.com/cocos2d/cocos2d-x/pull/6920
    // reset for next frame
    // _orderOfArrival = 0;
}

该措施首先会对现阶段节点产的子节点举行遍历并排序,这里遍历会遍历整个Node节点树,然后在调用自身的绘图方法draw。例如,精灵百事可乐会调用精灵自身之draw方法:

void Sprite::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
    if (_texture == nullptr)
    {
        return;
    }

#if CC_USE_CULLING
    // Don't calculate the culling if the transform was not updated
    auto visitingCamera = Camera::getVisitingCamera();
    auto defaultCamera = Camera::getDefaultCamera();
    if (visitingCamera == defaultCamera) {
        _insideBounds = ((flags & FLAGS_TRANSFORM_DIRTY) || visitingCamera->isViewProjectionUpdated()) ? renderer->checkVisibility(transform, _contentSize) : _insideBounds;
    }
    else
    {
        // XXX: this always return true since
        _insideBounds = renderer->checkVisibility(transform, _contentSize);
    }

    if(_insideBounds)
#endif
    {
        _trianglesCommand.init(_globalZOrder,
                               _texture,
                               getGLProgramState(),
                               _blendFunc,
                               _polyInfo.triangles,
                               transform,
                               flags);

        renderer->addCommand(&_trianglesCommand);

#if CC_SPRITE_DEBUG_DRAW
        _debugDrawNode->clear();
        auto count = _polyInfo.triangles.indexCount/3;
        auto indices = _polyInfo.triangles.indices;
        auto verts = _polyInfo.triangles.verts;
        for(ssize_t i = 0; i < count; i++)
        {
            //draw 3 lines
            Vec3 from =verts[indices[i*3]].vertices;
            Vec3 to = verts[indices[i*3+1]].vertices;
            _debugDrawNode->drawLine(Vec2(from.x, from.y), Vec2(to.x,to.y), Color4F::WHITE);

            from =verts[indices[i*3+1]].vertices;
            to = verts[indices[i*3+2]].vertices;
            _debugDrawNode->drawLine(Vec2(from.x, from.y), Vec2(to.x,to.y), Color4F::WHITE);

            from =verts[indices[i*3+2]].vertices;
            to = verts[indices[i*3]].vertices;
            _debugDrawNode->drawLine(Vec2(from.x, from.y), Vec2(to.x,to.y), Color4F::WHITE);
        }
#endif //CC_SPRITE_DEBUG_DRAW
    }
}

在百事可乐的draw方法中,并无直绘制,而是叫renderer发送一个RenderCommand指令(这里是TrianglesCommand),renderer会将RenderCommand放入一个栈中,等Node节点元素都遍历完毕,才行RenderCommand指令。

论目的版本的引擎实现,就将绘制逻辑从Node节点树遍历中分别出来了。每一遍绘制就让renderer发送一个RenderCommand指令。

连片下看Renderer::render方法:

void Renderer::render()
{
    //Uncomment this once everything is rendered by new renderer
    //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    //TODO: setup camera or MVP
    _isRendering = true;

    if (_glViewAssigned)
    {
        //Process render commands
        //1. Sort render commands based on ID
        for (auto &renderqueue : _renderGroups)
        {
            renderqueue.sort();
        }
        visitRenderQueue(_renderGroups[0]);
    }
    clean();
    _isRendering = false;
}

此间取出下标为0的渲染队列,然后,进一步通过visitRenderQueue来获取队列中之渲染指令Command:

void Renderer::visitRenderQueue(RenderQueue& queue)
{
    queue.saveRenderState();

    //
    //Process Global-Z < 0 Objects
    //
    const auto& zNegQueue = queue.getSubQueue(RenderQueue::QUEUE_GROUP::GLOBALZ_NEG);
    if (zNegQueue.size() > 0)
    {
        if(_isDepthTestFor2D)
        {
            glEnable(GL_DEPTH_TEST);
            glDepthMask(true);
            glEnable(GL_BLEND);
            RenderState::StateBlock::_defaultState->setDepthTest(true);
            RenderState::StateBlock::_defaultState->setDepthWrite(true);
            RenderState::StateBlock::_defaultState->setBlend(true);
        }
        else
        {
            glDisable(GL_DEPTH_TEST);
            glDepthMask(false);
            glEnable(GL_BLEND);
            RenderState::StateBlock::_defaultState->setDepthTest(false);
            RenderState::StateBlock::_defaultState->setDepthWrite(false);
            RenderState::StateBlock::_defaultState->setBlend(true);
        }
        glDisable(GL_CULL_FACE);
        RenderState::StateBlock::_defaultState->setCullFace(false);

        for (const auto& zNegNext : zNegQueue)
        {
            processRenderCommand(zNegNext);
        }
        flush();
    }

    //
    //Process Opaque Object
    //
    const auto& opaqueQueue = queue.getSubQueue(RenderQueue::QUEUE_GROUP::OPAQUE_3D);
    if (opaqueQueue.size() > 0)
    {
        //Clear depth to achieve layered rendering
        glEnable(GL_DEPTH_TEST);
        glDepthMask(true);
        glDisable(GL_BLEND);
        glEnable(GL_CULL_FACE);
        RenderState::StateBlock::_defaultState->setDepthTest(true);
        RenderState::StateBlock::_defaultState->setDepthWrite(true);
        RenderState::StateBlock::_defaultState->setBlend(false);
        RenderState::StateBlock::_defaultState->setCullFace(true);

        for (const auto& opaqueNext : opaqueQueue)
        {
            processRenderCommand(opaqueNext);
        }
        flush();
    }

    //
    //Process 3D Transparent object
    //
    const auto& transQueue = queue.getSubQueue(RenderQueue::QUEUE_GROUP::TRANSPARENT_3D);
    if (transQueue.size() > 0)
    {
        glEnable(GL_DEPTH_TEST);
        glDepthMask(false);
        glEnable(GL_BLEND);
        glEnable(GL_CULL_FACE);

        RenderState::StateBlock::_defaultState->setDepthTest(true);
        RenderState::StateBlock::_defaultState->setDepthWrite(false);
        RenderState::StateBlock::_defaultState->setBlend(true);
        RenderState::StateBlock::_defaultState->setCullFace(true);


        for (const auto& transNext : transQueue)
        {
            processRenderCommand(transNext);
        }
        flush();
    }

    //
    //Process Global-Z = 0 Queue
    //
    const auto& zZeroQueue = queue.getSubQueue(RenderQueue::QUEUE_GROUP::GLOBALZ_ZERO);
    if (zZeroQueue.size() > 0)
    {
        if(_isDepthTestFor2D)
        {
            glEnable(GL_DEPTH_TEST);
            glDepthMask(true);
            glEnable(GL_BLEND);

            RenderState::StateBlock::_defaultState->setDepthTest(true);
            RenderState::StateBlock::_defaultState->setDepthWrite(true);
            RenderState::StateBlock::_defaultState->setBlend(true);
        }
        else
        {
            glDisable(GL_DEPTH_TEST);
            glDepthMask(false);
            glEnable(GL_BLEND);

            RenderState::StateBlock::_defaultState->setDepthTest(false);
            RenderState::StateBlock::_defaultState->setDepthWrite(false);
            RenderState::StateBlock::_defaultState->setBlend(true);
        }
        glDisable(GL_CULL_FACE);
        RenderState::StateBlock::_defaultState->setCullFace(false);

        for (const auto& zZeroNext : zZeroQueue)
        {
            processRenderCommand(zZeroNext);
        }
        flush();
    }

    //
    //Process Global-Z > 0 Queue
    //
    const auto& zPosQueue = queue.getSubQueue(RenderQueue::QUEUE_GROUP::GLOBALZ_POS);
    if (zPosQueue.size() > 0)
    {
        if(_isDepthTestFor2D)
        {
            glEnable(GL_DEPTH_TEST);
            glDepthMask(true);
            glEnable(GL_BLEND);

            RenderState::StateBlock::_defaultState->setDepthTest(true);
            RenderState::StateBlock::_defaultState->setDepthWrite(true);
            RenderState::StateBlock::_defaultState->setBlend(true);
        }
        else
        {
            glDisable(GL_DEPTH_TEST);
            glDepthMask(false);
            glEnable(GL_BLEND);

            RenderState::StateBlock::_defaultState->setDepthTest(false);
            RenderState::StateBlock::_defaultState->setDepthWrite(false);
            RenderState::StateBlock::_defaultState->setBlend(true);
        }
        glDisable(GL_CULL_FACE);
        RenderState::StateBlock::_defaultState->setCullFace(false);

        for (const auto& zPosNext : zPosQueue)
        {
            processRenderCommand(zPosNext);
        }
        flush();
    }

    queue.restoreRenderState();
}

下一场,取出队列中之Command,并推行processRenderCommand方法:

void Renderer::processRenderCommand(RenderCommand* command)
{
    auto commandType = command->getType();
    if( RenderCommand::Type::TRIANGLES_COMMAND == commandType)
    {
        // flush other queues
        flush3D();

        auto cmd = static_cast<TrianglesCommand*>(command);

        // flush own queue when buffer is full
        if(_filledVertex + cmd->getVertexCount() > VBO_SIZE || _filledIndex + cmd->getIndexCount() > INDEX_VBO_SIZE)
        {
            CCASSERT(cmd->getVertexCount()>= 0 && cmd->getVertexCount() < VBO_SIZE, "VBO for vertex is not big enough, please break the data down or use customized render command");
            CCASSERT(cmd->getIndexCount()>= 0 && cmd->getIndexCount() < INDEX_VBO_SIZE, "VBO for index is not big enough, please break the data down or use customized render command");
            drawBatchedTriangles();
        }

        // queue it
        _queuedTriangleCommands.push_back(cmd);
        _filledIndex += cmd->getIndexCount();
        _filledVertex += cmd->getVertexCount();
    }
    else if (RenderCommand::Type::MESH_COMMAND == commandType)
    {
        flush2D();
        auto cmd = static_cast<MeshCommand*>(command);

        if (cmd->isSkipBatching() || _lastBatchedMeshCommand == nullptr || _lastBatchedMeshCommand->getMaterialID() != cmd->getMaterialID())
        {
            flush3D();

            CCGL_DEBUG_INSERT_EVENT_MARKER("RENDERER_MESH_COMMAND");

            if(cmd->isSkipBatching())
            {
                // XXX: execute() will call bind() and unbind()
                // but unbind() shouldn't be call if the next command is a MESH_COMMAND with Material.
                // Once most of cocos2d-x moves to Pass/StateBlock, only bind() should be used.
                cmd->execute();
            }
            else
            {
                cmd->preBatchDraw();
                cmd->batchDraw();
                _lastBatchedMeshCommand = cmd;
            }
        }
        else
        {
            CCGL_DEBUG_INSERT_EVENT_MARKER("RENDERER_MESH_COMMAND");
            cmd->batchDraw();
        }
    }
    else if(RenderCommand::Type::GROUP_COMMAND == commandType)
    {
        flush();
        int renderQueueID = ((GroupCommand*) command)->getRenderQueueID();
        CCGL_DEBUG_PUSH_GROUP_MARKER("RENDERER_GROUP_COMMAND");
        visitRenderQueue(_renderGroups[renderQueueID]);
        CCGL_DEBUG_POP_GROUP_MARKER();
    }
    else if(RenderCommand::Type::CUSTOM_COMMAND == commandType)
    {
        flush();
        auto cmd = static_cast<CustomCommand*>(command);
        CCGL_DEBUG_INSERT_EVENT_MARKER("RENDERER_CUSTOM_COMMAND");
        cmd->execute();
    }
    else if(RenderCommand::Type::BATCH_COMMAND == commandType)
    {
        flush();
        auto cmd = static_cast<BatchCommand*>(command);
        CCGL_DEBUG_INSERT_EVENT_MARKER("RENDERER_BATCH_COMMAND");
        cmd->execute();
    }
    else if(RenderCommand::Type::PRIMITIVE_COMMAND == commandType)
    {
        flush();
        auto cmd = static_cast<PrimitiveCommand*>(command);
        CCGL_DEBUG_INSERT_EVENT_MARKER("RENDERER_PRIMITIVE_COMMAND");
        cmd->execute();
    }
    else
    {
        CCLOGERROR("Unknown commands in renderQueue");
    }
}

可以看在processRenderCommand中即是各类类型的Command的推行及对应的拍卖了。而当百事可乐的绘图发之是TRIANGLES_COMMAND类型的命令,所以,直接看这drawBatchedTriangles:

void Renderer::drawBatchedTriangles()
{
    if(_queuedTriangleCommands.empty())
        return;

    CCGL_DEBUG_INSERT_EVENT_MARKER("RENDERER_BATCH_TRIANGLES");

    _filledVertex = 0;
    _filledIndex = 0;

    /************** 1: Setup up vertices/indices *************/

    _triBatchesToDraw[0].offset = 0;
    _triBatchesToDraw[0].indicesToDraw = 0;
    _triBatchesToDraw[0].cmd = nullptr;

    int batchesTotal = 0;
    int prevMaterialID = -1;
    bool firstCommand = true;

    for(const auto& cmd : _queuedTriangleCommands)
    {
        auto currentMaterialID = cmd->getMaterialID();
        const bool batchable = !cmd->isSkipBatching();

        fillVerticesAndIndices(cmd);

        // in the same batch ?
        if (batchable && (prevMaterialID == currentMaterialID || firstCommand))
        {
            CC_ASSERT(firstCommand || _triBatchesToDraw[batchesTotal].cmd->getMaterialID() == cmd->getMaterialID() && "argh... error in logic");
            _triBatchesToDraw[batchesTotal].indicesToDraw += cmd->getIndexCount();
            _triBatchesToDraw[batchesTotal].cmd = cmd;
        }
        else
        {
            // is this the first one?
            if (!firstCommand) {
                batchesTotal++;
                _triBatchesToDraw[batchesTotal].offset = _triBatchesToDraw[batchesTotal-1].offset + _triBatchesToDraw[batchesTotal-1].indicesToDraw;
            }

            _triBatchesToDraw[batchesTotal].cmd = cmd;
            _triBatchesToDraw[batchesTotal].indicesToDraw = (int) cmd->getIndexCount();

            // is this a single batch ? Prevent creating a batch group then
            if (!batchable)
                currentMaterialID = -1;
        }

        // capacity full ?
        if (batchesTotal + 1 >= _triBatchesToDrawCapacity) {
            _triBatchesToDrawCapacity *= 1.4;
            _triBatchesToDraw = (TriBatchToDraw*) realloc(_triBatchesToDraw, sizeof(_triBatchesToDraw[0]) * _triBatchesToDrawCapacity);
        }

        prevMaterialID = currentMaterialID;
        firstCommand = false;
    }
    batchesTotal++;

    /************** 2: Copy vertices/indices to GL objects *************/
    auto conf = Configuration::getInstance();
    if (conf->supportsShareableVAO() && conf->supportsMapBuffer())
    {
        //Bind VAO
        GL::bindVAO(_buffersVAO);
        //Set VBO data
        glBindBuffer(GL_ARRAY_BUFFER, _buffersVBO[0]);

        // option 1: subdata
//        glBufferSubData(GL_ARRAY_BUFFER, sizeof(_quads[0])*start, sizeof(_quads[0]) * n , &_quads[start] );

        // option 2: data
//        glBufferData(GL_ARRAY_BUFFER, sizeof(_verts[0]) * _filledVertex, _verts, GL_STATIC_DRAW);

        // option 3: orphaning + glMapBuffer
        // FIXME: in order to work as fast as possible, it must "and the exact same size and usage hints it had before."
        //  source: https://www.opengl.org/wiki/Buffer_Object_Streaming#Explicit_multiple_buffering
        // so most probably we won't have any benefit of using it
        glBufferData(GL_ARRAY_BUFFER, sizeof(_verts[0]) * _filledVertex, nullptr, GL_STATIC_DRAW);
        void *buf = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
        memcpy(buf, _verts, sizeof(_verts[0]) * _filledVertex);
        glUnmapBuffer(GL_ARRAY_BUFFER);

        glBindBuffer(GL_ARRAY_BUFFER, 0);

        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _buffersVBO[1]);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(_indices[0]) * _filledIndex, _indices, GL_STATIC_DRAW);
    }
    else
    {
        // Client Side Arrays
#define kQuadSize sizeof(_verts[0])
        glBindBuffer(GL_ARRAY_BUFFER, _buffersVBO[0]);

        glBufferData(GL_ARRAY_BUFFER, sizeof(_verts[0]) * _filledVertex , _verts, GL_DYNAMIC_DRAW);

        GL::enableVertexAttribs(GL::VERTEX_ATTRIB_FLAG_POS_COLOR_TEX);

        // vertices
        glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION, 3, GL_FLOAT, GL_FALSE, kQuadSize, (GLvoid*) offsetof(V3F_C4B_T2F, vertices));

        // colors
        glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, kQuadSize, (GLvoid*) offsetof(V3F_C4B_T2F, colors));

        // tex coords
        glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_TEX_COORD, 2, GL_FLOAT, GL_FALSE, kQuadSize, (GLvoid*) offsetof(V3F_C4B_T2F, texCoords));

        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _buffersVBO[1]);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(_indices[0]) * _filledIndex, _indices, GL_STATIC_DRAW);
    }

    /************** 3: Draw *************/
    for (int i=0; i<batchesTotal; ++i)
    {
        CC_ASSERT(_triBatchesToDraw[i].cmd && "Invalid batch");
        _triBatchesToDraw[i].cmd->useMaterial();
        glDrawElements(GL_TRIANGLES, (GLsizei) _triBatchesToDraw[i].indicesToDraw, GL_UNSIGNED_SHORT, (GLvoid*) (_triBatchesToDraw[i].offset*sizeof(_indices[0])) );
        _drawnBatches++;
        _drawnVertices += _triBatchesToDraw[i].indicesToDraw;
    }

    /************** 4: Cleanup *************/
    if (Configuration::getInstance()->supportsShareableVAO())
    {
        //Unbind VAO
        GL::bindVAO(0);
    }
    else
    {
        glBindBuffer(GL_ARRAY_BUFFER, 0);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    }

    _queuedTriangleCommands.clear();
    _filledVertex = 0;
    _filledIndex = 0;
}

其一固然凡是必不可缺的绘图处理与举办相应的统一批次的处理。这里,就先勾勒到此,一句话来说根本是数OpenGL
api的调用,不过,笔者对这么些还尚未深远之知道,就非“误人子弟”,做过多的剖析了,后边等履了再来更新此篇,解释得还详尽些。因而,该篇只是勉强对渲染的代码执行流程作了简单的解析,谈不达标深远明白。但起码经过翻阅代码,可以解相应的拍卖是什么兑现之。

技术沟通QQ群:528655025
作者:AlphaGL
出处:http://www.cnblogs.com/alphagl/
版权所有,欢迎保留原来文链接举办转载 🙂

3,时间设置

查阅时日子
输入:

date

查服务器时间是否一律,若不均等则改
改变时间命令

date -s ‘MMDDhhmmYYYY.ss’

3.2.4 修改mapred-site.xml

倘没 mapred-site.xml
该文件,就复制mapred-site.xml.template文件不分畛域命名为mapred-site.xml。
输入:

vim mapred-site.xml

改者新建的mapred-site.xml文件,在节点内进入配置:

<property>
    <name>mapred.job.tracker</name>
    <value>master:9001</value>
</property>
<property>
      <name>mapred.local.dir</name>
       <value>/root/hadoop/var</value>
</property>
<property>
       <name>mapreduce.framework.name</name>
       <value>yarn</value>
</property>

2,配置选

JDK:1.8 (jdk-8u144-linux-x64.tar.gz)
Hadoop:2.8.2 (hadoop-2.8.2.tar.gz)
Hive: 2.1 (apache-hive-2.1.1-bin.tar.gz)

2,环境布置

编辑 /etc/profile 文件
输入:

vim /etc/profile

添加:

export HIVE_HOME=/opt/hive/hive2.1
export HIVE_CONF_DIR=${HIVE_HOME}/conf
export PATH=.:${JAVA_HOME}/bin:${SCALA_HOME}/bin:${SPARK_HOME}/bin:${HADOOP_HOME}/bin:${ZK_HOME}/bin:${HBASE_HOME}/bin:${HIVE_HOME}/bin:$PATH

图片 1
注:实际部署为自己之吧遵守!
输入:

source  /etc/profile

倘使配置生效

1,文件准备

拿下载下来的Hive 的安排文件举办解压
在linux上输入:

 tar -xvf  apache-hive-2.1.1-bin.tar.gz

下一场倒到/opt/hive 里面,将文件夹又命名为 hive2.1
输入

mv  apache-hive-2.1.1-bin  /opt/hive
mv apache-hive-2.1.1-bin hive2.1

1,yum安装

先是查看mysql 是否已安装
输入:

rpm -qa | grep mysql   

万一就装,想抹的语句
输入:
万般删除命令:

rpm -e mysql

暴力删除命令:

rpm -e --nodeps mysql    

凭文件呢会面删除

安装mysql
输入:

yum list mysql-server  

苟没有,则透过wget命令下载该包
输入:

wget http://repo.mysql.com/mysql-community-release-el7-5.noarch.rpm   

下载成功未来,再输入指令安装

yum install mysql-server

当设置过程被相遇选取输入y就举办了
图片 2
图片 3

安成功后,输入 service mysqld start 启动服务
输入:
mysqladmin -u root -p password ‘123456’
来安装密码
输入后平素回车(默认是不曾密码的)
接下来又输入
mysql -u root -p
图片 4
经过授权法又改长途连接权限
输入: grant all privileges on . to ‘root’@’%
identified by ‘123456’;
横流:第一单’root’是用户称,第二只’%’是有所的ip都足以长距离访问,第三独’123456’表示
用户密码 假设未常用 就关掉
输入:flush privileges; //刷新

在防火墙关闭之后,使用SQLYog之类的家伙测试是否能科学连接

图片 5

5.3.2 修改hive-site.xml

切换到 /opt/hive/hive2.1/conf 目录下
以hive-default.xml.template 拷贝一客,不分畛域命名为hive-site.xml
接下来编辑hive-site.xml文件

cp hive-default.xml.template hive-site.xml
vim hive-site.xml

编辑hive-site.xml文件,在 中添加:

<!-- 指定HDFS中的hive仓库地址 -->  
  <property>  
    <name>hive.metastore.warehouse.dir</name>  
    <value>/root/hive/warehouse</value>  
  </property>  

<property>
    <name>hive.exec.scratchdir</name>
    <value>/root/hive</value>
  </property>

  <!-- 该属性为空表示嵌入模式或本地模式,否则为远程模式 -->  
  <property>  
    <name>hive.metastore.uris</name>  
    <value></value>  
  </property>  

<!-- 指定mysql的连接 -->
 <property>
        <name>javax.jdo.option.ConnectionURL</name>
        <value>jdbc:mysql://master:3306/hive?createDatabaseIfNotExist=true</value>
    </property>
<!-- 指定驱动类 -->
    <property>
        <name>javax.jdo.option.ConnectionDriverName</name>
        <value>com.mysql.jdbc.Driver</value>
    </property>
   <!-- 指定用户名 -->
    <property>
        <name>javax.jdo.option.ConnectionUserName</name>
        <value>root</value>
    </property>
    <!-- 指定密码 -->
    <property>
        <name>javax.jdo.option.ConnectionPassword</name>
        <value>123456</value>
    </property>
    <property>
   <name>hive.metastore.schema.verification</name>
   <value>false</value>
    <description>
    </description>
 </property>

下一场以配备文件被颇具的

${system:java.io.tmpdir}

变动为 /opt/hive/tmp (假使无拖欠公文则创制),
连将以此文件夹赋予读写权限,将
${system:user.name}
更改为 root

例如:
更改在此以前的:
图片 6
改变后:
图片 7

配置图:
图片 8

横流: 由于hive-site.xml
文件被的配置了多,可以通过FTP将她下载下来举行编制。也足以直接配置好所用的,其他的好去除。
MySQL的接连地址被之master是主机的号,可以变换成ip。

3,关闭防火墙

关闭防火墙,方便访问。
CentOS 7版本以下输入:
闭馆防火墙

service   iptables stop

CentOS 7 以上的版本输入:

systemctl stop firewalld.service

五、Hive环境设置及布置

2,编译包安装

如出一辙、环境准备

安装mysql

切换到mysql的目录 /usr/local/mysql
输入:

 ./scripts/mysql_install_db --user=mysql

图片 9
马到成功安装mysql之后,输入
service mysql start 或 /etc/init.d/mysql start

图片 10
查看是否启动成功
输入:

 ps -ef|grep mysql

切换到 /usr/local/mysql/bin 目录下
安装密码
mysqladmin -u root password ‘123456’入mysql
输入:

mysql -u root -p

设置远程连接权限
输入:
grant all privileges on . to ‘root’@’%‘ identified
by ‘123456’;
接下来输入:
flush privileges;

征:
第一只’root’是用户称,第二独’%’是具的ip都得远程访问,第两个’123456’表示用户密码
假若不常用就关闭掉。

下本地连接工具连接测试

图片 11

1,环境变量设置

编辑 /etc/profile 文件 :

vim /etc/profile

部署文件:

export HADOOP_HOME=/opt/hadoop/hadoop2.8
export HADOOP_COMMON_LIB_NATIVE_DIR=$HADOOP_HOME/lib/native
export HADOOP_OPTS="-Djava.library.path=$HADOOP_HOME/lib"
export PATH=.:${JAVA_HOME}/bin:${HADOOP_HOME}/bin:$PATH

其它

重复多之hive使用要参见官方文档
https://cwiki.apache.org/confluence/display/Hive/LanguageManual
条件搭建参考:
http://blog.csdn.net/pucao_cug/article/details/71773665

版权注明:
作者:虚无境
天涯论坛出处:http://www.cnblogs.com/xuwujing
CSDN出处:http://blog.csdn.net/qazwsxpcm    
村办博客出处:http://www.panchengming.com
原创是,转载请标明出处,谢谢!

1,更改主机名

第一更改主机名,目的是为方便管理。
输入:

hostname 

查本机的名称
接下来转主机名吧master
输入:

hostnamectl set-hostname master

流动:主机名称改变后,要更开(reboot)才会收效。

Mysql文件准备

以下充斥好的mysql安装包上传到linux服务器
解压mysql解压包,并倒到/usr/local目录下,重命名也mysql。
命令:

tar -xvf mysql-5.6.21-linux-glibc2.5-x86_64.tar.gz
mv mysql-5.6.21-linux-glibc2.5-x86_64 /usr/local
cd /usr/local
mv mysql-5.6.21-linux-glibc2.5-x86_64 mysql

图片 12

注: mysql默认的门路是就是/usr/local/mysql
,倘使安之地点转移,需要改变相应的布局文件。

六、Hive Shell 测试

于功成名就启动Hadoop之后
切换到Hive目录下
输入:

cd /opt/hive/hive2.1/bin

首先开端化数据库
初叶化的时段注意要拿mysql启动
输入:

schematool  -initSchema -dbType mysql 

图片 13

推行成功后,可以望hive数据库与一堆表已经成立成功了

图片 14

切换到 cd /opt/hive/hive2.1/bin
上hive (确保hadoop以及成功启动)
输入:

hive

图片 15

进入hive 之后
举办片简约的操作
新建一个库房,然后在大兴土木平张表
基本操作和一般的涉嫌项目数据库差不多
创建库:

create database db_hiveTest;

创建表:

create  table  db_hiveTest.student(id int,name string)  row  format  delimited  fields   terminated  by  '\t';

图片 16

说明: terminated by ‘\t’
代表文本分隔符要使用Tab,行及履行直接不可知发出空格。

加载数据
初辟一个窗口
因为hive 不襄助写,所以添加数据选拔load加载文本获取。
新建一个文件

touch  /opt/hive/student.txt

编辑该文本添加数据
输入:

vim /opt/hive/student.txt

添加数据:
高中档的空格符使用Tab建

1001    zhangsan
1002    lisi
1003    wangwu

图片 17
表明:
文本可以当Windows上边新建,然后经过ftp上传到linux中,需要专注文本的格式为unix
格式。

切换到hive shell
加载数据
输入:

load data local inpath '/opt/hive/student.txt'  into table db_hivetest.student;

然后查询该多少
输入:

select * from db_hiveTest.student;

图片 18

本文的Hadoop+Hive的布就到此截至了,谢谢阅读!

5.3.1新建文件夹

以修改配置文件前,需要事先以root目录下起有文书夹。

mkdir /root/hive
mkdir /root/hive/warehouse

新建了该文件之后,需要吃hadoop新建/root/hive/warehouse 和 /root/hive/
目录。
执行命令:

$HADOOP_HOME/bin/hadoop fs -mkdir -p /root/hive/
$HADOOP_HOME/bin/hadoop fs -mkdir -p /root/hive/warehouse

吃刚刚新建的目给读写权限,执行命令:

$HADOOP_HOME/bin/hadoop fs -chmod 777 /root/hive/
$HADOOP_HOME/bin/hadoop fs -chmod 777 /root/hive/warehouse 

检查及时点儿个目录是否成创建
输入:

$HADOOP_HOME/bin/hadoop fs -ls /root/
$HADOOP_HOME/bin/hadoop fs -ls /root/hive/

图片 19

得视曾经成功创设

其次、服务器的系安排

每当部署Hadoop+Hive从前,应该先行开一下布局。
做那么些部署为便于,使用root权限。

2,配置文件更改

预先切换至 /home/hadoop/hadoop2.8/etc/hadoop/ 目下

3.2.2修改 hadoop-env.sh

输入:

vim hadoop-env.sh

将${JAVA_HOME} 修改也投机之JDK路径

export   JAVA_HOME=${JAVA_HOME}

修改为:

export   JAVA_HOME=/home/java/jdk1.8

四、Mysql安装

盖Hive的默认元数据是Mysql,所以先要安装Mysql。
Mysql有少数栽安装格局,可活动选取。

3.2.1 修改 core-site.xml

输入:

vim core-site.xml

在添加:

<configuration>
<property>
        <name>hadoop.tmp.dir</name>
        <value>/root/hadoop/tmp</value>
        <description>Abase for other temporary directories.</description>
   </property>
   <property>
        <name>fs.default.name</name>
        <value>hdfs://master:9000</value>
   </property>
</configuration>

2,做IP和主机名之炫耀

修改hosts文件,做涉嫌映射
输入

vim /etc/hosts

添加
主机的ip 和 主机名称

192.168.238.128 master

3,配置更改

其三、Hadoop安装以及安排

Hadoop的现实配置于怪数据上连串之一 —–
Hadoop环境搭建(单机)

中介绍得特别详细了。所以本文就大概介绍一下。
流淌:具体部署为友好的啊仍。

3.2.3修改 hdfs-site.xml

输入:

vim hdfs-site.xml

在添加:

<property>
   <name>dfs.name.dir</name>
   <value>/root/hadoop/dfs/name</value>
   <description>Path on the local filesystem where theNameNode stores the namespace and transactions logs persistently.</description>
</property>
<property>
   <name>dfs.data.dir</name>
   <value>/root/hadoop/dfs/data</value>
   <description>Comma separated list of paths on the localfilesystem of a DataNode where it should store its blocks.</description>
</property>
<property>
   <name>dfs.replication</name>
   <value>2</value>
</property>
<property>
      <name>dfs.permissions</name>
      <value>false</value>
      <description>need not permissions</description>
</property>

3,Hadoop启动

启航前需要先格式化
切换到/home/hadoop/hadoop2.8/bin目录下
输入:

./hadoop  namenode  -format

格式化成功后,再切换来/home/hadoop/hadoop2.8/sbin目录下
启动hdfs和yarn
输入:

start-dfs.sh
start-yarn.sh

启航成功后,输入jsp查看是否启动成功
于浏览器输入 ip+8088 和ip +50070 界面查看是否会顾
可知对访问则启动成功

1,服务器采取

当地虚拟机
操作系统:linux CentOS 7
Cpu:2核
内存:2G
硬盘:40G

说明:因为用阿里云服务器每趟都使重新配置,而且还要考虑网络传输问题,于是自己于本土就搭建了一个虚拟机,方便文件之传和有关部署,缺点是本来卡的微机就变得再卡了。具体科目以及动用在上同样一体博文中。
地址: http://blog.csdn.net/qazwsxpcm/article/details/78816230

5.3.4 添加 数据令包

是因为Hive 默认自带的数据库是选拔mysql,所以这块就是之所以mysql
以mysql 的叫包 上传出 /opt/hive/hive2.1/lib

图片 20

3,下载地址

JDK:
http://www.oracle.com/technetwork/java/javase/downloads
Hadopp:
http://www.apache.org/dyn/closer.cgi/hadoop/common
Hive
http://mirror.bit.edu.cn/apache/hive/

百度云盘:
链接:https://pan.baidu.com/s/1slxBsHv 密码:x51i

5.3.3 修改 hive-env.sh

改hive-env.sh 文件,没有就复制 hive-env.sh.template
,一视同仁命名为hive-env.sh
图片 21
在那一个布局文件被补充加

export  HADOOP_HOME=/opt/hadoop/hadoop2.8
export  HIVE_CONF_DIR=/opt/hive/hive2.1/conf
export  HIVE_AUX_JARS_PATH=/opt/hive/hive2.1/lib

图片 22

引言

杀数额上序列之一 —–
Hadoop环境搭建(单机)

成功的搭建了Hadoop的条件,在非常数额上体系之二 —–
HBase环境搭建(单机)
打响搭建了HBase的环境和相关应用介绍。本文紧要讲解咋样搭建Hadoop+Hive的条件。

Post Author: admin

发表评论

电子邮件地址不会被公开。 必填项已用*标注