Merge pull request #12324 from ricardoquesada/state_buffer_fixes

State buffer fixes
This commit is contained in:
Ricardo Quesada 2015-06-12 22:02:58 -07:00
commit 97c1d29af9
13 changed files with 264 additions and 23 deletions

View File

@ -33,6 +33,7 @@
#include "renderer/CCGLProgramCache.h"
#include "renderer/ccGLStateCache.h"
#include "renderer/CCFrameBuffer.h"
#include "renderer/CCRenderState.h"
NS_CC_BEGIN
@ -409,14 +410,22 @@ void Camera::clearBackground(float depth)
{
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glStencilMask(0);
RenderState::StateBlock::_defaultState->setStencilWrite(0);
oldDepthTest = glIsEnabled(GL_DEPTH_TEST);
glGetIntegerv(GL_DEPTH_FUNC, &oldDepthFunc);
glGetBooleanv(GL_DEPTH_WRITEMASK, &oldDepthMask);
glDepthMask(GL_TRUE);
RenderState::StateBlock::_defaultState->setDepthWrite(true);
glEnable(GL_DEPTH_TEST);
RenderState::StateBlock::_defaultState->setDepthTest(true);
glDepthFunc(GL_ALWAYS);
RenderState::StateBlock::_defaultState->setDepthFunction(RenderState::DEPTH_ALWAYS);
}
//draw
static V3F_C4B_T2F_Quad quad;
quad.bl.vertices = Vec3(-1,-1,0);
@ -461,14 +470,18 @@ void Camera::clearBackground(float depth)
if(GL_FALSE == oldDepthTest)
{
glDisable(GL_DEPTH_TEST);
RenderState::StateBlock::_defaultState->setDepthTest(false);
}
glDepthFunc(oldDepthFunc);
if(GL_FALSE == oldDepthMask)
{
glDepthMask(GL_FALSE);
RenderState::StateBlock::_defaultState->setDepthWrite(false);
}
glStencilMask(0xFFFFF);
RenderState::StateBlock::_defaultState->setStencilWrite(0xFFFFF);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
}
}

View File

@ -30,6 +30,7 @@
#include "renderer/CCGLProgramCache.h"
#include "renderer/ccGLStateCache.h"
#include "renderer/CCRenderer.h"
#include "renderer/CCRenderState.h"
#include "base/CCDirector.h"
#if (CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32 || CC_TARGET_PLATFORM == CC_PLATFORM_LINUX)
@ -395,12 +396,15 @@ void ClippingNode::onBeforeVisit()
// enable stencil use
glEnable(GL_STENCIL_TEST);
RenderState::StateBlock::_defaultState->setStencilTest(true);
// check for OpenGL error while enabling stencil test
CHECK_GL_ERROR_DEBUG();
// all bits on the stencil buffer are readonly, except the current layer bit,
// this means that operation like glClear or glStencilOp will be masked with this value
glStencilMask(mask_layer);
RenderState::StateBlock::_defaultState->setStencilWrite(mask_layer);
// manually save the depth test state
@ -413,6 +417,7 @@ void ClippingNode::onBeforeVisit()
// it should never prevent something else to be drawn,
// only disabling depth buffer update should do
glDepthMask(GL_FALSE);
RenderState::StateBlock::_defaultState->setDepthWrite(false);
///////////////////////////////////
// CLEAR STENCIL BUFFER
@ -424,7 +429,13 @@ void ClippingNode::onBeforeVisit()
// if not in inverted mode: set the current layer value to 0 in the stencil buffer
// if in inverted mode: set the current layer value to 1 in the stencil buffer
glStencilFunc(GL_NEVER, mask_layer, mask_layer);
RenderState::StateBlock::_defaultState->setStencilFunction(RenderState::STENCIL_NEVER, mask_layer, mask_layer);
glStencilOp(!_inverted ? GL_ZERO : GL_REPLACE, GL_KEEP, GL_KEEP);
RenderState::StateBlock::_defaultState->setStencilOperation(
!_inverted ? RenderState::STENCIL_OP_ZERO : RenderState::STENCIL_OP_REPLACE,
RenderState::STENCIL_OP_KEEP,
RenderState::STENCIL_OP_KEEP);
// draw a fullscreen solid rectangle to clear the stencil buffer
//ccDrawSolidRect(Vec2::ZERO, ccpFromSize([[Director sharedDirector] winSize]), Color4F(1, 1, 1, 1));
@ -439,7 +450,14 @@ void ClippingNode::onBeforeVisit()
// if not in inverted mode: set the current layer value to 1 in the stencil buffer
// if in inverted mode: set the current layer value to 0 in the stencil buffer
glStencilFunc(GL_NEVER, mask_layer, mask_layer);
RenderState::StateBlock::_defaultState->setStencilFunction(RenderState::STENCIL_NEVER, mask_layer, mask_layer);
glStencilOp(!_inverted ? GL_REPLACE : GL_ZERO, GL_KEEP, GL_KEEP);
RenderState::StateBlock::_defaultState->setStencilOperation(
!_inverted ? RenderState::STENCIL_OP_REPLACE : RenderState::STENCIL_OP_ZERO,
RenderState::STENCIL_OP_KEEP,
RenderState::STENCIL_OP_KEEP);
// enable alpha test only if the alpha threshold < 1,
// indeed if alpha threshold == 1, every pixel will be drawn anyways
@ -480,6 +498,8 @@ void ClippingNode::onAfterDrawStencil()
// restore the depth test state
glDepthMask(_currentDepthWriteMask);
RenderState::StateBlock::_defaultState->setDepthWrite(_currentDepthWriteMask);
//if (currentDepthTestEnabled) {
// glEnable(GL_DEPTH_TEST);
//}
@ -494,7 +514,10 @@ void ClippingNode::onAfterDrawStencil()
// else
// do not draw the pixel but keep the current layer in the stencil buffer
glStencilFunc(GL_EQUAL, _mask_layer_le, _mask_layer_le);
RenderState::StateBlock::_defaultState->setStencilFunction(RenderState::STENCIL_EQUAL, _mask_layer_le, _mask_layer_le);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
RenderState::StateBlock::_defaultState->setStencilOperation(RenderState::STENCIL_OP_KEEP, RenderState::STENCIL_OP_KEEP, RenderState::STENCIL_OP_KEEP);
// draw (according to the stencil test func) this node and its childs
}
@ -507,11 +530,18 @@ void ClippingNode::onAfterVisit()
// manually restore the stencil state
glStencilFunc(_currentStencilFunc, _currentStencilRef, _currentStencilValueMask);
RenderState::StateBlock::_defaultState->setStencilFunction((RenderState::StencilFunction)_currentStencilFunc, _currentStencilRef, _currentStencilValueMask);
glStencilOp(_currentStencilFail, _currentStencilPassDepthFail, _currentStencilPassDepthPass);
RenderState::StateBlock::_defaultState->setStencilOperation((RenderState::StencilOperation)_currentStencilFail,
(RenderState::StencilOperation)_currentStencilPassDepthFail,
(RenderState::StencilOperation)_currentStencilPassDepthPass);
glStencilMask(_currentStencilWriteMask);
if (!_currentStencilEnabled)
{
glDisable(GL_STENCIL_TEST);
RenderState::StateBlock::_defaultState->setStencilTest(false);
}
// we are done using this layer, decrement

View File

@ -41,7 +41,7 @@ void ClippingRectangleNode::onBeforeVisitScissor()
{
if (_clippingEnabled) {
glEnable(GL_SCISSOR_TEST);
float scaleX = _scaleX;
float scaleY = _scaleY;
Node *parent = this->getParent();

View File

@ -33,6 +33,7 @@ THE SOFTWARE.
#include "renderer/CCGLProgramCache.h"
#include "renderer/ccGLStateCache.h"
#include "renderer/CCRenderer.h"
#include "renderer/CCRenderState.h"
#include "renderer/CCTexture2D.h"
#include "platform/CCGL.h"
#include "2d/CCCamera.h"
@ -327,8 +328,12 @@ void Grid3D::beforeBlit()
glGetBooleanv(GL_DEPTH_WRITEMASK, &depthWriteMask);
_oldDepthWriteValue = depthWriteMask != GL_FALSE;
CHECK_GL_ERROR_DEBUG();
glEnable(GL_DEPTH_TEST);
RenderState::StateBlock::_defaultState->setDepthTest(true);
glDepthMask(true);
RenderState::StateBlock::_defaultState->setDepthWrite(true);
}
}
@ -340,8 +345,10 @@ void Grid3D::afterBlit()
glEnable(GL_DEPTH_TEST);
else
glDisable(GL_DEPTH_TEST);
RenderState::StateBlock::_defaultState->setDepthTest(_oldDepthTestValue);
glDepthMask(_oldDepthWriteValue);
RenderState::StateBlock::_defaultState->setDepthWrite(_oldDepthWriteValue);
}
}

View File

@ -30,6 +30,7 @@
#include "renderer/CCGLProgramCache.h"
#include "renderer/CCGLProgramState.h"
#include "renderer/CCRenderer.h"
#include "renderer/CCRenderState.h"
#include "3d/CCSkybox.h"
#include "3d/CCTextureCube.h"
@ -183,14 +184,21 @@ void Skybox::onDraw(const Mat4& transform, uint32_t flags)
glGetIntegerv(GL_DEPTH_FUNC, &depthFunc);
glEnable(GL_DEPTH_TEST);
RenderState::StateBlock::_defaultState->setDepthTest(true);
glDepthFunc(GL_LEQUAL);
RenderState::StateBlock::_defaultState->setDepthFunction(RenderState::DEPTH_LEQUAL);
GLboolean cullFlag = glIsEnabled(GL_CULL_FACE);
GLint cullMode;
glGetIntegerv(GL_CULL_FACE_MODE, &cullMode);
glEnable(GL_CULL_FACE);
RenderState::StateBlock::_defaultState->setCullFace(true);
glCullFace(GL_BACK);
RenderState::StateBlock::_defaultState->setCullFaceSide(RenderState::CULL_FACE_SIDE_BACK);
if (Configuration::getInstance()->supportsShareableVAO())
{

View File

@ -28,6 +28,7 @@
#include "base/CCDirector.h"
#include "renderer/CCGLProgram.h"
#include "renderer/CCRenderer.h"
#include "renderer/CCRenderState.h"
#include "renderer/ccGLStateCache.h"
#include "renderer/CCGLProgramCache.h"
@ -131,6 +132,8 @@ void Physics3DDebugDrawer::drawImplementation( const Mat4 &transform, uint32_t f
_program->use();
_program->setUniformsForBuiltins(transform);
glEnable(GL_DEPTH_TEST);
RenderState::StateBlock::_defaultState->setDepthTest(true);
GL::blendFunc(_blendFunc.src, _blendFunc.dst);
if (_dirty)
@ -158,7 +161,9 @@ void Physics3DDebugDrawer::drawImplementation( const Mat4 &transform, uint32_t f
glBindBuffer(GL_ARRAY_BUFFER, 0);
CC_INCREMENT_GL_DRAWN_BATCHES_AND_VERTICES(1,_bufferCount);
glDisable(GL_DEPTH_TEST);
RenderState::StateBlock::_defaultState->setDepthTest(false);
}
void Physics3DDebugDrawer::init()

View File

@ -37,25 +37,6 @@ NS_CC_BEGIN
RenderState::StateBlock* RenderState::StateBlock::_defaultState = nullptr;
// Render state override bits
enum
{
RS_BLEND = (1 << 0),
RS_BLEND_FUNC = (1 << 1),
RS_CULL_FACE = (1 << 2),
RS_DEPTH_TEST = (1 << 3),
RS_DEPTH_WRITE = (1 << 4),
RS_DEPTH_FUNC = (1 << 5),
RS_CULL_FACE_SIDE = (1 << 6),
RS_STENCIL_TEST = (1 << 7),
RS_STENCIL_WRITE = (1 << 8),
RS_STENCIL_FUNC = (1 << 9),
RS_STENCIL_OP = (1 << 10),
RS_FRONT_FACE = (1 << 11),
RS_ALL_ONES = 0xFFFFFFFF,
};
RenderState::RenderState()
: _texture(nullptr)
@ -731,6 +712,14 @@ uint32_t RenderState::StateBlock::getHash() const
return 0x12345678;
}
void RenderState::StateBlock::invalidate(long stateBits)
{
CCASSERT(_defaultState, "_default state not created yet. Cannot be invalidated");
_defaultState->_bits = stateBits;
_defaultState->restore(0);
}
void RenderState::StateBlock::setBlend(bool enabled)
{
_blendEnabled = enabled;

View File

@ -188,7 +188,7 @@ public:
* Defines a block of fixed-function render states that can be applied to a
* RenderState object.
*/
class StateBlock : public Ref
class CC_DLL StateBlock : public Ref
{
friend class RenderState;
friend class Pass;
@ -349,6 +349,37 @@ public:
uint32_t getHash() const;
bool isDirty() const;
/** StateBlock bits to be used with invalidate */
enum
{
RS_BLEND = (1 << 0),
RS_BLEND_FUNC = (1 << 1),
RS_CULL_FACE = (1 << 2),
RS_DEPTH_TEST = (1 << 3),
RS_DEPTH_WRITE = (1 << 4),
RS_DEPTH_FUNC = (1 << 5),
RS_CULL_FACE_SIDE = (1 << 6),
RS_STENCIL_TEST = (1 << 7),
RS_STENCIL_WRITE = (1 << 8),
RS_STENCIL_FUNC = (1 << 9),
RS_STENCIL_OP = (1 << 10),
RS_FRONT_FACE = (1 << 11),
RS_ALL_ONES = 0xFFFFFFFF,
};
/**
* Invalidates the default StateBlock.
*
* Only call it if you are calling GL calls directly. Invoke this function
* at the end of your custom draw call.
* This function restores the default render state its defaults values.
* Since this function might call GL calls, it must be called in a GL context is present.
*
* @param stateBits Bitwise-OR of the states that needs to be invalidated
*/
static void invalidate(long stateBits);
static StateBlock* _defaultState;
protected:

View File

@ -28,6 +28,7 @@ THE SOFTWARE.
#include "renderer/CCGLProgram.h"
#include "renderer/CCGLProgramCache.h"
#include "renderer/ccGLStateCache.h"
#include "renderer/CCRenderState.h"
#include "base/CCDirector.h"
#include "2d/CCDrawingPrimitives.h"
#include "renderer/CCRenderer.h"
@ -335,17 +336,43 @@ void Layout::onBeforeVisitStencil()
glGetIntegerv(GL_STENCIL_PASS_DEPTH_PASS, (GLint *)&_currentStencilPassDepthPass);
glEnable(GL_STENCIL_TEST);
RenderState::StateBlock::_defaultState->setStencilTest(true);
CHECK_GL_ERROR_DEBUG();
glStencilMask(mask_layer);
RenderState::StateBlock::_defaultState->setStencilWrite(mask_layer);
glGetBooleanv(GL_DEPTH_WRITEMASK, &_currentDepthWriteMask);
glDepthMask(GL_FALSE);
RenderState::StateBlock::_defaultState->setDepthWrite(false);
glStencilFunc(GL_NEVER, mask_layer, mask_layer);
RenderState::StateBlock::_defaultState->setStencilFunction(
RenderState::STENCIL_NEVER,
mask_layer,
mask_layer);
glStencilOp(GL_ZERO, GL_KEEP, GL_KEEP);
RenderState::StateBlock::_defaultState->setStencilOperation(
RenderState::STENCIL_OP_ZERO,
RenderState::STENCIL_OP_KEEP,
RenderState::STENCIL_OP_KEEP);
this->drawFullScreenQuadClearStencil();
glStencilFunc(GL_NEVER, mask_layer, mask_layer);
RenderState::StateBlock::_defaultState->setStencilFunction(
RenderState::STENCIL_NEVER,
mask_layer,
mask_layer);
glStencilOp(GL_REPLACE, GL_KEEP, GL_KEEP);
RenderState::StateBlock::_defaultState->setStencilOperation(
RenderState::STENCIL_OP_REPLACE,
RenderState::STENCIL_OP_KEEP,
RenderState::STENCIL_OP_KEEP);
}
void Layout::drawFullScreenQuadClearStencil()
@ -392,19 +419,42 @@ void Layout::drawFullScreenQuadClearStencil()
void Layout::onAfterDrawStencil()
{
glDepthMask(_currentDepthWriteMask);
RenderState::StateBlock::_defaultState->setDepthWrite(_currentDepthWriteMask);
glStencilFunc(GL_EQUAL, _mask_layer_le, _mask_layer_le);
RenderState::StateBlock::_defaultState->setStencilFunction(
RenderState::STENCIL_EQUAL,
_mask_layer_le,
_mask_layer_le);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
RenderState::StateBlock::_defaultState->setStencilOperation(
RenderState::STENCIL_OP_KEEP,
RenderState::STENCIL_OP_KEEP,
RenderState::STENCIL_OP_KEEP);
}
void Layout::onAfterVisitStencil()
{
glStencilFunc(_currentStencilFunc, _currentStencilRef, _currentStencilValueMask);
RenderState::StateBlock::_defaultState->setStencilFunction(
(RenderState::StencilFunction)_currentStencilFunc,
_currentStencilRef,
_currentStencilValueMask);
glStencilOp(_currentStencilFail, _currentStencilPassDepthFail, _currentStencilPassDepthPass);
RenderState::StateBlock::_defaultState->setStencilOperation(
(RenderState::StencilOperation)_currentStencilFail,
(RenderState::StencilOperation)_currentStencilPassDepthFail,
(RenderState::StencilOperation)_currentStencilPassDepthPass);
glStencilMask(_currentStencilWriteMask);
if (!_currentStencilEnabled)
{
glDisable(GL_STENCIL_TEST);
RenderState::StateBlock::_defaultState->setStencilTest(false);
}
s_layer--;
}

View File

@ -26,6 +26,7 @@
#include "MaterialSystemTest.h"
#include <ctime>
#include <spine/spine-cocos2dx.h>
#include "../testResource.h"
#include "cocos2d.h"
@ -47,6 +48,7 @@ MaterialSystemTest::MaterialSystemTest()
ADD_TEST_CASE(Material_MultipleSprite3D);
ADD_TEST_CASE(Material_Sprite3DTest);
ADD_TEST_CASE(Material_parsePerformance);
ADD_TEST_CASE(Material_invalidate);
}
std::string MaterialSystemBaseTest::title() const
@ -380,6 +382,74 @@ std::string Material_parsePerformance::subtitle() const
{
return "Testing parsing performance";
}
//
//
//
void Material_invalidate::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);
}
std::string Material_invalidate::subtitle() const
{
return "Testing RenderState::StateBlock::invalidate()";
}
void Material_invalidate::draw(cocos2d::Renderer *renderer, const cocos2d::Mat4 &transform, uint32_t flags)
{
_customCommand.init(_globalZOrder, transform, flags);
_customCommand.func = []() {
glDisable(GL_DEPTH_TEST);
CHECK_GL_ERROR_DEBUG();
glDepthMask(false);
CHECK_GL_ERROR_DEBUG();
glEnable(GL_CULL_FACE);
CHECK_GL_ERROR_DEBUG();
glCullFace((GLenum)GL_FRONT);
CHECK_GL_ERROR_DEBUG();
glFrontFace((GLenum)GL_CW);
CHECK_GL_ERROR_DEBUG();
glDisable(GL_BLEND);
CHECK_GL_ERROR_DEBUG();
// a non-optimal way is to pass all bits, but that would be very inefficient
// RenderState::StateBlock::invalidate(RenderState::StateBlock::RS_ALL_ONES);
RenderState::StateBlock::invalidate(RenderState::StateBlock::RS_DEPTH_TEST |
RenderState::StateBlock::RS_DEPTH_WRITE |
RenderState::StateBlock::RS_CULL_FACE |
RenderState::StateBlock::RS_CULL_FACE_SIDE |
RenderState::StateBlock::RS_FRONT_FACE |
RenderState::StateBlock::RS_BLEND);
};
renderer->addCommand(&_customCommand);
}
// MARK: Helper functions

View File

@ -111,4 +111,17 @@ public:
virtual std::string subtitle() const override;
};
class Material_invalidate : public MaterialSystemBaseTest
{
public:
CREATE_FUNC(Material_invalidate);
virtual void onEnter() override;
virtual std::string subtitle() const override;
virtual void draw(cocos2d::Renderer *renderer, const cocos2d::Mat4 &transform, uint32_t flags) override;
cocos2d::CustomCommand _customCommand;
};

View File

@ -463,6 +463,10 @@ void RenderTextureTestDepthStencil::draw(Renderer *renderer, const Mat4 &transfo
void RenderTextureTestDepthStencil::onBeforeClear()
{
glStencilMask(0xFF);
// Since cocos2d-x v3.7, users should avoid calling GL directly because it will break the internal GL state
// But if users must call GL directly, they should update the state manually,
RenderState::StateBlock::_defaultState->setStencilWrite(0xFF);
}
void RenderTextureTestDepthStencil::onBeforeStencil()
@ -471,16 +475,30 @@ void RenderTextureTestDepthStencil::onBeforeStencil()
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_NEVER, 1, 0xFF);
glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
// Since cocos2d-x v3.7, users should avoid calling GL directly because it will break the internal GL state
// But if users must call GL directly, they should update the state manually,
RenderState::StateBlock::_defaultState->setStencilTest(true);
RenderState::StateBlock::_defaultState->setStencilFunction(RenderState::STENCIL_NEVER, 1, 0xFF);
RenderState::StateBlock::_defaultState->setStencilOperation(RenderState::STENCIL_OP_REPLACE, RenderState::STENCIL_OP_REPLACE, RenderState::STENCIL_OP_REPLACE);
}
void RenderTextureTestDepthStencil::onBeforDraw()
{
glStencilFunc(GL_NOTEQUAL, 1, 0xFF);
// Since cocos2d-x v3.7, users should avoid calling GL directly because it will break the internal GL state
// But if users must call GL directly, they should update the state manually,
RenderState::StateBlock::_defaultState->setStencilFunction(RenderState::STENCIL_NOTEQUAL, 1, 0xFF);
}
void RenderTextureTestDepthStencil::onAfterDraw()
{
glDisable(GL_STENCIL_TEST);
// Since cocos2d-x v3.7, users should avoid calling GL directly because it will break the internal GL state
// But if users must call GL directly, they should update the state manually,
RenderState::StateBlock::_defaultState->setStencilTest(false);
}
std::string RenderTextureTestDepthStencil::title() const

View File

@ -1054,9 +1054,16 @@ void Effect3DOutline::draw(const Mat4 &transform)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDisable(GL_DEPTH_TEST);
glCullFace(GL_BACK);
glDisable(GL_CULL_FACE);
// Since cocos2d-x v3.7, users should avoid calling GL directly because it will break the internal GL state
// But if users must call GL directly, they should update the state manually,
RenderState::StateBlock::_defaultState->setDepthTest(false);
RenderState::StateBlock::_defaultState->setCullFaceSide(RenderState::CULL_FACE_SIDE_BACK);
RenderState::StateBlock::_defaultState->setCullFace(false);
}
}