diff --git a/cocos/renderer/CCMeshCommand.cpp b/cocos/renderer/CCMeshCommand.cpp index 3f1e8faa87..70bf6dce06 100644 --- a/cocos/renderer/CCMeshCommand.cpp +++ b/cocos/renderer/CCMeshCommand.cpp @@ -252,6 +252,10 @@ void MeshCommand::postBatchDraw() glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glBindBuffer(GL_ARRAY_BUFFER, 0); } + + // restore the default state since we don't know + // if the next command will need the default state or not + RenderState::StateBlock::restore(0); } } diff --git a/cocos/renderer/CCRenderState.h b/cocos/renderer/CCRenderState.h index 12b42c32ab..6e875ed52d 100644 --- a/cocos/renderer/CCRenderState.h +++ b/cocos/renderer/CCRenderState.h @@ -380,6 +380,21 @@ public: */ static void invalidate(long stateBits); + /** + * Restores the global Render State to the default state + * + * The difference between `invalidate()` and `restore()`, is that `restore()` will + * restore the global Render State based on its current state. Only the + * states that were changed will be restored. + * + * Rule of thumb: + + - call `restore()` if you want to restore to the default state after using `StateBlock`. + - call `invalidate()` if you want to restore to the default state after calling manual GL calls. + + */ + static void restore(long stateOverrideBits); + static StateBlock* _defaultState; protected: @@ -387,7 +402,6 @@ public: ~StateBlock(); void bindNoRestore(); - static void restore(long stateOverrideBits); static void enableDepthWrite(); void cloneInto(StateBlock* renderState) const; diff --git a/cocos/renderer/CCRenderer.cpp b/cocos/renderer/CCRenderer.cpp index f39bbba933..f84b0bd990 100644 --- a/cocos/renderer/CCRenderer.cpp +++ b/cocos/renderer/CCRenderer.cpp @@ -482,6 +482,9 @@ void Renderer::processRenderCommand(RenderCommand* 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 diff --git a/tests/cpp-tests/Classes/MaterialSystemTest/MaterialSystemTest.cpp b/tests/cpp-tests/Classes/MaterialSystemTest/MaterialSystemTest.cpp index a6b9b87533..b915eaeeef 100644 --- a/tests/cpp-tests/Classes/MaterialSystemTest/MaterialSystemTest.cpp +++ b/tests/cpp-tests/Classes/MaterialSystemTest/MaterialSystemTest.cpp @@ -49,6 +49,7 @@ MaterialSystemTest::MaterialSystemTest() ADD_TEST_CASE(Material_Sprite3DTest); ADD_TEST_CASE(Material_parsePerformance); ADD_TEST_CASE(Material_invalidate); + ADD_TEST_CASE(Material_renderState); } std::string MaterialSystemBaseTest::title() const @@ -382,6 +383,7 @@ std::string Material_parsePerformance::subtitle() const { return "Testing parsing performance"; } + // // // @@ -451,6 +453,71 @@ void Material_invalidate::draw(cocos2d::Renderer *renderer, const cocos2d::Mat4 renderer->addCommand(&_customCommand); } +// +// +// +void Material_renderState::onEnter() +{ + MaterialSystemBaseTest::onEnter(); + + // ORC + auto sprite = Sprite3D::create("Sprite3DTest/orc.c3b"); + sprite->setScale(5); + sprite->setRotation3D(Vec3(0,180,0)); + addChild(sprite); + sprite->setNormalizedPosition(Vec2(0.3,0.3)); + + auto rotate = RotateBy::create(5, Vec3(0,360,0)); + auto repeat = RepeatForever::create(rotate); + sprite->runAction(repeat); + + // SPINE + auto skeletonNode = spine::SkeletonAnimation::createWithFile("spine/goblins-ffd.json", "spine/goblins-ffd.atlas", 1.5f); + skeletonNode->setAnimation(0, "walk", true); + skeletonNode->setSkin("goblin"); + + skeletonNode->setScale(0.25); + skeletonNode->setNormalizedPosition(Vec2(0.6,0.3)); + this->addChild(skeletonNode); + + _stateBlock = RenderState::StateBlock::create(); + _stateBlock->retain(); + + _stateBlock->setDepthTest(false); + _stateBlock->setDepthWrite(false); + _stateBlock->setCullFace(true); + _stateBlock->setCullFaceSide(RenderState::CULL_FACE_SIDE_FRONT); + _stateBlock->setFrontFace(RenderState::FRONT_FACE_CW); + _stateBlock->setBlend(false); +} + +void Material_renderState::onExit() +{ + MaterialSystemBaseTest::onExit(); + _stateBlock->release(); +} + +std::string Material_renderState::subtitle() const +{ + return "You should see a Spine animation on the right"; +} + +void Material_renderState::draw(cocos2d::Renderer *renderer, const cocos2d::Mat4 &transform, uint32_t flags) +{ + _customCommand.init(_globalZOrder, transform, flags); + _customCommand.func = [this]() { + + this->_stateBlock->bind(); + + // should do something... + // and after that, restore + + this->_stateBlock->restore(0); + }; + + renderer->addCommand(&_customCommand); +} + // MARK: Helper functions static void printProperties(Properties* properties, int indent) diff --git a/tests/cpp-tests/Classes/MaterialSystemTest/MaterialSystemTest.h b/tests/cpp-tests/Classes/MaterialSystemTest/MaterialSystemTest.h index c6ec9d629f..b64f93eeec 100644 --- a/tests/cpp-tests/Classes/MaterialSystemTest/MaterialSystemTest.h +++ b/tests/cpp-tests/Classes/MaterialSystemTest/MaterialSystemTest.h @@ -124,4 +124,19 @@ public: cocos2d::CustomCommand _customCommand; }; +class Material_renderState : public MaterialSystemBaseTest +{ +public: + CREATE_FUNC(Material_renderState); + + virtual void onEnter() override; + virtual void onExit() override; + virtual std::string subtitle() const override; + + virtual void draw(cocos2d::Renderer *renderer, const cocos2d::Mat4 &transform, uint32_t flags) override; + + cocos2d::RenderState::StateBlock* _stateBlock; + cocos2d::CustomCommand _customCommand; +}; +