fix: TriangleCommands with custom uniforms can be batched (#16329)

* fix: TriangleCommands with custom uniforms can be batched

TriangleCommands with custom uniforms can be batched together.

This improves the performance when using custom uniforms without
adding any penalties when not using them

Github issue #16224

* better tests
This commit is contained in:
Ricardo Quesada 2016-08-07 14:51:02 -07:00 committed by GitHub
parent 0e65025bda
commit f7464f8de5
4 changed files with 210 additions and 19 deletions

View File

@ -36,7 +36,6 @@ TrianglesCommand::TrianglesCommand()
:_materialID(0) :_materialID(0)
,_textureID(0) ,_textureID(0)
,_glProgramState(nullptr) ,_glProgramState(nullptr)
,_glProgram(nullptr)
,_blendType(BlendFunc::DISABLE) ,_blendType(BlendFunc::DISABLE)
,_alphaTextureID(0) ,_alphaTextureID(0)
{ {
@ -60,14 +59,12 @@ void TrianglesCommand::init(float globalOrder, GLuint textureID, GLProgramState*
_mv = mv; _mv = mv;
if( _textureID != textureID || _blendType.src != blendType.src || _blendType.dst != blendType.dst || if( _textureID != textureID || _blendType.src != blendType.src || _blendType.dst != blendType.dst ||
_glProgramState != glProgramState || _glProgramState != glProgramState)
_glProgram != glProgramState->getGLProgram())
{ {
_textureID = textureID; _textureID = textureID;
_blendType = blendType; _blendType = blendType;
_glProgramState = glProgramState; _glProgramState = glProgramState;
_glProgram = glProgramState->getGLProgram();
generateMaterialID(); generateMaterialID();
} }
} }
@ -89,18 +86,25 @@ TrianglesCommand::~TrianglesCommand()
void TrianglesCommand::generateMaterialID() void TrianglesCommand::generateMaterialID()
{ {
// do not batch if using custom uniforms (since we cannot batch) it // glProgramState is hashed because it contains:
if(_glProgramState->getUniformCount() > 0) // * uniforms/values
{ // * glProgram
_materialID = Renderer::MATERIAL_ID_DO_NOT_BATCH; //
setSkipBatching(true); // we safely can when the same glProgramState is being used then they share those states
} // if they don't have the same glProgramState, they might still have the same
else // uniforms/values and glProgram, but it would be too expensive to check the uniforms.
{ struct {
int glProgram = (int)_glProgram->getProgram(); GLuint textureId;
int intArray[4] = { glProgram, (int)_textureID, (int)_blendType.src, (int)_blendType.dst}; GLenum blendSrc;
_materialID = XXH32((const void*)intArray, sizeof(intArray), 0); GLenum blendDst;
} void* glProgramState;
} hashMe;
hashMe.textureId = _textureID;
hashMe.blendSrc = _blendType.src;
hashMe.blendDst = _blendType.dst;
hashMe.glProgramState = _glProgramState;
_materialID = XXH32((const void*)&hashMe, sizeof(hashMe), 0);
} }
void TrianglesCommand::useMaterial() const void TrianglesCommand::useMaterial() const

View File

@ -105,8 +105,6 @@ protected:
GLuint _textureID; GLuint _textureID;
/**GLprogramstate for the command. encapsulate shaders and uniforms.*/ /**GLprogramstate for the command. encapsulate shaders and uniforms.*/
GLProgramState* _glProgramState; GLProgramState* _glProgramState;
/**The GLProgram used by GLProgramState*/
GLProgram* _glProgram;
/**Blend function when rendering the triangles.*/ /**Blend function when rendering the triangles.*/
BlendFunc _blendType; BlendFunc _blendType;
/**Rendered triangles.*/ /**Rendered triangles.*/

View File

@ -38,6 +38,8 @@ NewRendererTests::NewRendererTests()
ADD_TEST_CASE(CaptureNodeTest); ADD_TEST_CASE(CaptureNodeTest);
ADD_TEST_CASE(BugAutoCulling); ADD_TEST_CASE(BugAutoCulling);
ADD_TEST_CASE(RendererBatchQuadTri); ADD_TEST_CASE(RendererBatchQuadTri);
ADD_TEST_CASE(RendererUniformBatch);
ADD_TEST_CASE(RendererUniformBatch2);
}; };
std::string MultiSceneTest::title() const std::string MultiSceneTest::title() const
@ -610,3 +612,164 @@ std::string RendererBatchQuadTri::subtitle() const
{ {
return "QuadCommand and TriangleCommands are batched together"; return "QuadCommand and TriangleCommands are batched together";
} }
//
//
// RendererUniformBatch
//
RendererUniformBatch::RendererUniformBatch()
{
Size s = Director::getInstance()->getWinSize();
auto glBlurState = createBlurGLProgramState();
auto glSepiaState = createSepiaGLProgramState();
auto x_inc = s.width / 20;
auto y_inc = s.height / 6;
for (int y=0; y<6; ++y)
{
for (int x=0; x<20; ++x)
{
auto sprite = Sprite::create("Images/grossini.png");
sprite->setPosition(Vec2(x * x_inc, y * y_inc));
sprite->setScale(0.4);
addChild(sprite);
if (y>=4) {
sprite->setGLProgramState(glSepiaState);
} else if(y>=2) {
sprite->setGLProgramState(glBlurState);
}
}
}
}
GLProgramState* RendererUniformBatch::createBlurGLProgramState()
{
#if (CC_TARGET_PLATFORM != CC_PLATFORM_WINRT)
const std::string shaderName("Shaders/example_Blur.fsh");
#else
const std::string shaderName("Shaders/example_Blur_winrt.fsh");
#endif
// outline shader
auto fileUtiles = FileUtils::getInstance();
auto fragmentFullPath = fileUtiles->fullPathForFilename(shaderName);
auto fragSource = fileUtiles->getStringFromFile(fragmentFullPath);
auto glprogram = GLProgram::createWithByteArrays(ccPositionTextureColor_noMVP_vert, fragSource.c_str());
auto glprogramstate = (glprogram == nullptr ? nullptr : GLProgramState::getOrCreateWithGLProgram(glprogram));
glprogramstate->setUniformVec2("resolution", Vec2(85,121));
#if (CC_TARGET_PLATFORM != CC_PLATFORM_WINRT)
glprogramstate->setUniformFloat("blurRadius", 10);
glprogramstate->setUniformFloat("sampleNum", 5);
#endif
return glprogramstate;
}
GLProgramState* RendererUniformBatch::createSepiaGLProgramState()
{
const std::string shaderName("Shaders/example_Sepia.fsh");
// outline shader
auto fileUtiles = FileUtils::getInstance();
auto fragmentFullPath = fileUtiles->fullPathForFilename(shaderName);
auto fragSource = fileUtiles->getStringFromFile(fragmentFullPath);
auto glprogram = GLProgram::createWithByteArrays(ccPositionTextureColor_noMVP_vert, fragSource.c_str());
auto glprogramstate = (glprogram == nullptr ? nullptr : GLProgramState::getOrCreateWithGLProgram(glprogram));
return glprogramstate;
}
std::string RendererUniformBatch::title() const
{
return "RendererUniformBatch";
}
std::string RendererUniformBatch::subtitle() const
{
return "Only 9 draw calls should appear";
}
//
// RendererUniformBatch2
//
RendererUniformBatch2::RendererUniformBatch2()
{
Size s = Director::getInstance()->getWinSize();
auto glBlurState = createBlurGLProgramState();
auto glSepiaState = createSepiaGLProgramState();
auto x_inc = s.width / 20;
auto y_inc = s.height / 6;
for (int y=0; y<6; ++y)
{
for (int x=0; x<20; ++x)
{
auto sprite = Sprite::create("Images/grossini.png");
sprite->setPosition(Vec2(x * x_inc, y * y_inc));
sprite->setScale(0.4);
addChild(sprite);
auto r = CCRANDOM_0_1();
if (r < 0.33)
sprite->setGLProgramState(glSepiaState);
else if (r < 0.66)
sprite->setGLProgramState(glBlurState);
}
}
}
GLProgramState* RendererUniformBatch2::createBlurGLProgramState()
{
#if (CC_TARGET_PLATFORM != CC_PLATFORM_WINRT)
const std::string shaderName("Shaders/example_Blur.fsh");
#else
const std::string shaderName("Shaders/example_Blur_winrt.fsh");
#endif
// outline shader
auto fileUtiles = FileUtils::getInstance();
auto fragmentFullPath = fileUtiles->fullPathForFilename(shaderName);
auto fragSource = fileUtiles->getStringFromFile(fragmentFullPath);
auto glprogram = GLProgram::createWithByteArrays(ccPositionTextureColor_noMVP_vert, fragSource.c_str());
auto glprogramstate = (glprogram == nullptr ? nullptr : GLProgramState::getOrCreateWithGLProgram(glprogram));
glprogramstate->setUniformVec2("resolution", Vec2(85,121));
#if (CC_TARGET_PLATFORM != CC_PLATFORM_WINRT)
glprogramstate->setUniformFloat("blurRadius", 10);
glprogramstate->setUniformFloat("sampleNum", 5);
#endif
return glprogramstate;
}
GLProgramState* RendererUniformBatch2::createSepiaGLProgramState()
{
const std::string shaderName("Shaders/example_Sepia.fsh");
// outline shader
auto fileUtiles = FileUtils::getInstance();
auto fragmentFullPath = fileUtiles->fullPathForFilename(shaderName);
auto fragSource = fileUtiles->getStringFromFile(fragmentFullPath);
auto glprogram = GLProgram::createWithByteArrays(ccPositionTextureColor_noMVP_vert, fragSource.c_str());
auto glprogramstate = (glprogram == nullptr ? nullptr : GLProgramState::getOrCreateWithGLProgram(glprogram));
return glprogramstate;
}
std::string RendererUniformBatch2::title() const
{
return "RendererUniformBatch 2";
}
std::string RendererUniformBatch2::subtitle() const
{
return "Mixing different shader states should work ok";
}

View File

@ -166,4 +166,30 @@ protected:
RendererBatchQuadTri(); RendererBatchQuadTri();
}; };
class RendererUniformBatch : public MultiSceneTest
{
public:
CREATE_FUNC(RendererUniformBatch);
virtual std::string title() const override;
virtual std::string subtitle() const override;
protected:
RendererUniformBatch();
cocos2d::GLProgramState* createBlurGLProgramState();
cocos2d::GLProgramState* createSepiaGLProgramState();
};
class RendererUniformBatch2 : public MultiSceneTest
{
public:
CREATE_FUNC(RendererUniformBatch2);
virtual std::string title() const override;
virtual std::string subtitle() const override;
protected:
RendererUniformBatch2();
cocos2d::GLProgramState* createBlurGLProgramState();
cocos2d::GLProgramState* createSepiaGLProgramState();
};
#endif //__NewRendererTest_H_ #endif //__NewRendererTest_H_