Merge pull request #5075 from ricardoquesada/renderer_perf_fixes

Renderer perf fixes
This commit is contained in:
Ricardo Quesada 2014-01-15 16:10:51 -08:00
commit e8bd5cb4a3
17 changed files with 307 additions and 104 deletions

View File

@ -2,11 +2,14 @@ cocos2d-x-3.0final ?.? ?
[All] [All]
[NEW] Console: added the 'textures', 'fileutils dump' and 'config' commands [NEW] Console: added the 'textures', 'fileutils dump' and 'config' commands
[NEW] DrawNode supports to draw triangle, quad bezier, cubic bezier. [NEW] DrawNode supports to draw triangle, quad bezier, cubic bezier.
[NEW] Renderer: Added BatchCommand. This command is not "batchable" with other commands, but improves performance in about 10%
[FIX] Console: log(format, va_args) is private to prevent possible resolution errors [FIX] Console: log(format, va_args) is private to prevent possible resolution errors
[FIX] Configuration: dumpInfo() -> getInfo() [FIX] Configuration: dumpInfo() -> getInfo()
[FIX] ControlSlider doesn't support to set selected thumb sprite. [FIX] ControlSlider doesn't support to set selected thumb sprite.
[FIX] ControlButton doesn't support to set scale ratio of touchdown state. [FIX] ControlButton doesn't support to set scale ratio of touchdown state.
[FIX] Particles: Crash was triggered if there is not `textureFileName`section in particle plist file. [FIX] Particles: Crash was triggered if there is not `textureFileName`section in particle plist file.
[FIX] Renderer: QuadCommand::init() does not copy the Quads, it only store a reference making the code faster
[FIX] Renderer: Performance improved in Sprite and SpriteBatchNode (and subclasses) sprites in about 20%
[FIX] Tests: TestCpp works with CMake on Windows. [FIX] Tests: TestCpp works with CMake on Windows.
[FIX] Tests: Sprites Performance Test has 3 new tests [FIX] Tests: Sprites Performance Test has 3 new tests
[FIX] TextureCache: getTextureForKey and removeTextureForKey work as expected [FIX] TextureCache: getTextureForKey and removeTextureForKey work as expected

View File

@ -1 +1 @@
3d6ada05d55194dd8e4c10ed8316add7c0d8705c 88c095bbe123ab56df3f7870692c6631f4464c8d

View File

@ -122,6 +122,7 @@ renderer/CCFrustum.cpp \
renderer/CCGroupCommand.cpp \ renderer/CCGroupCommand.cpp \
renderer/CCMaterialManager.cpp \ renderer/CCMaterialManager.cpp \
renderer/CCQuadCommand.cpp \ renderer/CCQuadCommand.cpp \
renderer/CCBatchCommand.cpp \
renderer/CCRenderCommand.cpp \ renderer/CCRenderCommand.cpp \
renderer/CCRenderer.cpp \ renderer/CCRenderer.cpp \
renderer/CCRenderMaterial.cpp \ renderer/CCRenderMaterial.cpp \

View File

@ -382,26 +382,14 @@ void ParticleBatchNode::draw(void)
return; return;
} }
// CC_NODE_DRAW_SETUP(); _batchCommand.init(0,
//
// GL::blendFunc( _blendFunc.src, _blendFunc.dst );
//
// _textureAtlas->drawQuads();
auto shader = ShaderCache::getInstance()->getProgram(GLProgram::SHADER_NAME_POSITION_TEXTURE_COLOR_NO_MVP);
kmMat4 mv;
kmGLGetMatrix(KM_GL_MODELVIEW, &mv);
_quadCommand.init(0,
_vertexZ, _vertexZ,
_textureAtlas->getTexture()->getName(), _textureAtlas->getTexture()->getName(),
shader, _shaderProgram,
_blendFunc, _blendFunc,
_textureAtlas->getQuads(), _textureAtlas,
_textureAtlas->getTotalQuads(), _modelViewTransform);
mv); Director::getInstance()->getRenderer()->addCommand(&_batchCommand);
Director::getInstance()->getRenderer()->addCommand(&_quadCommand);
CC_PROFILER_STOP("CCParticleBatchNode - draw"); CC_PROFILER_STOP("CCParticleBatchNode - draw");
} }

View File

@ -32,7 +32,7 @@
#include "CCNode.h" #include "CCNode.h"
#include "CCProtocols.h" #include "CCProtocols.h"
#include "renderer/CCQuadCommand.h" #include "renderer/CCBatchCommand.h"
NS_CC_BEGIN NS_CC_BEGIN
@ -146,7 +146,7 @@ private:
/** the blend function used for drawing the quads */ /** the blend function used for drawing the quads */
BlendFunc _blendFunc; BlendFunc _blendFunc;
// quad command // quad command
QuadCommand _quadCommand; BatchCommand _batchCommand;
}; };
// end of particle_nodes group // end of particle_nodes group

View File

@ -99,7 +99,7 @@ bool SpriteBatchNode::initWithTexture(Texture2D *tex, ssize_t capacity)
_descendants.reserve(capacity); _descendants.reserve(capacity);
setShaderProgram(ShaderCache::getInstance()->getProgram(GLProgram::SHADER_NAME_POSITION_TEXTURE_COLOR_NO_MVP)); setShaderProgram(ShaderCache::getInstance()->getProgram(GLProgram::SHADER_NAME_POSITION_TEXTURE_COLOR));
return true; return true;
} }
@ -356,18 +356,14 @@ void SpriteBatchNode::draw()
for(const auto &child: _children) for(const auto &child: _children)
child->updateTransform(); child->updateTransform();
kmMat4 mv; _batchCommand.init(0,
kmGLGetMatrix(KM_GL_MODELVIEW, &mv);
_quadCommand.init(0,
_vertexZ, _vertexZ,
_textureAtlas->getTexture()->getName(), _textureAtlas->getTexture()->getName(),
_shaderProgram, _shaderProgram,
_blendFunc, _blendFunc,
_textureAtlas->getQuads(), _textureAtlas,
_textureAtlas->getTotalQuads(), _modelViewTransform);
mv); Director::getInstance()->getRenderer()->addCommand(&_batchCommand);
Director::getInstance()->getRenderer()->addCommand(&_quadCommand);
} }
void SpriteBatchNode::increaseAtlasCapacity(void) void SpriteBatchNode::increaseAtlasCapacity(void)

View File

@ -35,7 +35,7 @@ THE SOFTWARE.
#include "CCProtocols.h" #include "CCProtocols.h"
#include "CCTextureAtlas.h" #include "CCTextureAtlas.h"
#include "ccMacros.h" #include "ccMacros.h"
#include "renderer/CCQuadCommand.h" #include "renderer/CCBatchCommand.h"
NS_CC_BEGIN NS_CC_BEGIN
@ -189,7 +189,7 @@ protected:
TextureAtlas *_textureAtlas; TextureAtlas *_textureAtlas;
BlendFunc _blendFunc; BlendFunc _blendFunc;
QuadCommand _quadCommand; // quad command BatchCommand _batchCommand; // render command
// all descendants: children, grand children, etc... // all descendants: children, grand children, etc...
// There is not need to retain/release these objects, since they are already retained by _children // There is not need to retain/release these objects, since they are already retained by _children

View File

@ -144,6 +144,7 @@ set(COCOS2D_SRC
renderer/CCGroupCommand.cpp renderer/CCGroupCommand.cpp
renderer/CCMaterialManager.cpp renderer/CCMaterialManager.cpp
renderer/CCQuadCommand.cpp renderer/CCQuadCommand.cpp
renderer/CCBatchCommand.cpp
renderer/CCRenderCommand.cpp renderer/CCRenderCommand.cpp
renderer/CCRenderer.cpp renderer/CCRenderer.cpp
renderer/CCRenderMaterial.cpp renderer/CCRenderMaterial.cpp

View File

@ -0,0 +1,125 @@
/****************************************************************************
Copyright (c) 2013-2014 Chukong Technologies Inc.
http://www.cocos2d-x.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#include "renderer/CCBatchCommand.h"
#include "ccGLStateCache.h"
#include "CCTextureAtlas.h"
NS_CC_BEGIN
BatchCommand::BatchCommand()
: _viewport(0)
, _depth(0)
, _textureID(0)
, _blendType(BlendFunc::DISABLE)
, _textureAtlas(nullptr)
{
_type = RenderCommand::Type::BATCH_COMMAND;
_shader = nullptr;
}
void BatchCommand::init(int viewport, int32_t depth, GLuint textureID, GLProgram* shader, BlendFunc blendType, TextureAtlas *textureAtlas, const kmMat4& modelViewTransform)
{
_viewport = viewport;
_depth = depth;
_textureID = textureID;
_blendType = blendType;
_shader = shader;
_textureAtlas = textureAtlas;
_mv = modelViewTransform;
}
BatchCommand::~BatchCommand()
{
}
int64_t BatchCommand::generateID()
{
_id = 0;
//Generate Material ID
//TODO fix shader ID generation
CCASSERT(_shader->getProgram() < pow(2,10), "ShaderID is greater than 2^10");
//TODO fix texture ID generation
CCASSERT(_textureID < pow(2,18), "TextureID is greater than 2^18");
//TODO fix blend id generation
int blendID = 0;
if(_blendType == BlendFunc::DISABLE)
{
blendID = 0;
}
else if(_blendType == BlendFunc::ALPHA_PREMULTIPLIED)
{
blendID = 1;
}
else if(_blendType == BlendFunc::ALPHA_NON_PREMULTIPLIED)
{
blendID = 2;
}
else if(_blendType == BlendFunc::ADDITIVE)
{
blendID = 3;
}
else
{
blendID = 4;
}
//TODO Material ID should be part of the ID
//
// Temporal hack (later, these 32-bits should be packed in 24-bits
//
// +---------------------+-------------------+----------------------+
// | Shader ID (10 bits) | Blend ID (4 bits) | Texture ID (18 bits) |
// +---------------------+-------------------+----------------------+
_materialID = (int32_t)_shader->getProgram() << 22
| (int32_t)blendID << 18
| (int32_t)_textureID << 0;
//Generate RenderCommandID
_id = (int64_t)_viewport << 61
| (int64_t)1 << 60 //translucent
| (int64_t)_depth << 36;
return _id;
}
void BatchCommand::execute()
{
// Set material
_shader->use();
_shader->setUniformsForBuiltins(_mv);
GL::bindTexture2D(_textureID);
GL::blendFunc(_blendType.src, _blendType.dst);
// Draw
_textureAtlas->drawQuads();
}
NS_CC_END

View File

@ -0,0 +1,81 @@
/****************************************************************************
Copyright (c) 2013-2014 Chukong Technologies Inc.
http://www.cocos2d-x.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#ifndef _CC_BATCHCOMMAND_H_
#define _CC_BATCHCOMMAND_H_
#include "CCRenderCommand.h"
#include "CCGLProgram.h"
#include "CCRenderCommandPool.h"
#include "kazmath/kazmath.h"
NS_CC_BEGIN
class TextureAtlas;
#define CC_NO_TEXTURE 0
class BatchCommand : public RenderCommand
{
public:
BatchCommand();
~BatchCommand();
void init(int viewport, int32_t depth, GLuint texutreID, GLProgram* shader, BlendFunc blendType, TextureAtlas *textureAtlas, const kmMat4& modelViewTransform);
// +----------+----------+-----+-----------------------------------+
// | | | | | |
// | ViewPort | Transluc | | Depth | Material ID |
// | 3 bits | 1 bit | | 24 bits | 24 bit2 |
// +----------+----------+-----+----------------+------------------+
virtual int64_t generateID();
void execute();
protected:
int32_t _materialID;
//Key Data
int _viewport; /// Which view port it belongs to
//TODO use material to determine if it's translucent
int32_t _depth;
//Maternal
GLuint _textureID;
GLProgram* _shader;
// GLuint _shaderID;
BlendFunc _blendType;
TextureAtlas *_textureAtlas;
// ModelView transform
kmMat4 _mv;
};
NS_CC_END
#endif //_CC_BATCHCOMMAND_H_

View File

@ -27,8 +27,7 @@
NS_CC_BEGIN NS_CC_BEGIN
CustomCommand::CustomCommand() CustomCommand::CustomCommand()
:RenderCommand() : func(nullptr)
, func(nullptr)
, _viewport(0) , _viewport(0)
, _depth(0) , _depth(0)
{ {

View File

@ -86,8 +86,7 @@ void GroupCommandManager::releaseGroupID(int groupID)
} }
GroupCommand::GroupCommand() GroupCommand::GroupCommand()
:RenderCommand() : _viewport(0)
, _viewport(0)
, _depth(0) , _depth(0)
{ {
_type = RenderCommand::Type::GROUP_COMMAND; _type = RenderCommand::Type::GROUP_COMMAND;

View File

@ -29,17 +29,15 @@
NS_CC_BEGIN NS_CC_BEGIN
QuadCommand::QuadCommand() QuadCommand::QuadCommand()
:RenderCommand() :_viewport(0)
,_viewport(0)
,_depth(0) ,_depth(0)
,_textureID(0) ,_textureID(0)
,_blendType(BlendFunc::DISABLE) ,_blendType(BlendFunc::DISABLE)
,_quadCount(0) ,_quadsCount(0)
,_capacity(0)
{ {
_type = RenderCommand::Type::QUAD_COMMAND; _type = RenderCommand::Type::QUAD_COMMAND;
_shader = nullptr; _shader = nullptr;
_quad = nullptr; _quads = nullptr;
} }
void QuadCommand::init(int viewport, int32_t depth, GLuint textureID, GLProgram* shader, BlendFunc blendType, V3F_C4B_T2F_Quad* quad, ssize_t quadCount, const kmMat4 &mv) void QuadCommand::init(int viewport, int32_t depth, GLuint textureID, GLProgram* shader, BlendFunc blendType, V3F_C4B_T2F_Quad* quad, ssize_t quadCount, const kmMat4 &mv)
@ -48,63 +46,16 @@ void QuadCommand::init(int viewport, int32_t depth, GLuint textureID, GLProgram*
_depth = depth; _depth = depth;
_textureID = textureID; _textureID = textureID;
_blendType = blendType; _blendType = blendType;
_quadCount = quadCount;
_shader = shader; _shader = shader;
if(quadCount > _capacity ) { _quadsCount = quadCount;
//TODO find a better way to manage quads, current way will result in memory be wasted _quads = quad;
// _quad = (V3F_C4B_T2F_Quad*)malloc(sizeof(V3F_C4B_T2F_Quad) * quadCount);
_quad = (V3F_C4B_T2F_Quad*) realloc(_quad, sizeof(*quad) * quadCount );
_capacity = quadCount;
}
_quadCount = quadCount; _mv = mv;
memcpy(_quad, quad, sizeof(V3F_C4B_T2F_Quad) * quadCount);
for(int i=0; i<quadCount; ++i) {
V3F_C4B_T2F_Quad *q = &_quad[i];
kmVec3 vec1, out1;
vec1.x = q->bl.vertices.x;
vec1.y = q->bl.vertices.y;
vec1.z = q->bl.vertices.z;
kmVec3Transform(&out1, &vec1, &mv);
q->bl.vertices.x = out1.x;
q->bl.vertices.y = out1.y;
q->bl.vertices.z = out1.z;
kmVec3 vec2, out2;
vec2.x = q->br.vertices.x;
vec2.y = q->br.vertices.y;
vec2.z = q->br.vertices.z;
kmVec3Transform(&out2, &vec2, &mv);
q->br.vertices.x = out2.x;
q->br.vertices.y = out2.y;
q->br.vertices.z = out2.z;
kmVec3 vec3, out3;
vec3.x = q->tr.vertices.x;
vec3.y = q->tr.vertices.y;
vec3.z = q->tr.vertices.z;
kmVec3Transform(&out3, &vec3, &mv);
q->tr.vertices.x = out3.x;
q->tr.vertices.y = out3.y;
q->tr.vertices.z = out3.z;
kmVec3 vec4, out4;
vec4.x = q->tl.vertices.x;
vec4.y = q->tl.vertices.y;
vec4.z = q->tl.vertices.z;
kmVec3Transform(&out4, &vec4, &mv);
q->tl.vertices.x = out4.x;
q->tl.vertices.y = out4.y;
q->tl.vertices.z = out4.z;
}
} }
QuadCommand::~QuadCommand() QuadCommand::~QuadCommand()
{ {
free(_quad);
} }
int64_t QuadCommand::generateID() int64_t QuadCommand::generateID()

View File

@ -41,7 +41,7 @@ public:
QuadCommand(); QuadCommand();
~QuadCommand(); ~QuadCommand();
void init(int viewport, int32_t depth, GLuint texutreID, GLProgram* shader, BlendFunc blendType, V3F_C4B_T2F_Quad* quad, ssize_t quadCount, void init(int viewport, int32_t depth, GLuint texutreID, GLProgram* shader, BlendFunc blendType, V3F_C4B_T2F_Quad* quads, ssize_t quadCount,
const kmMat4& mv); const kmMat4& mv);
// +----------+----------+-----+-----------------------------------+ // +----------+----------+-----+-----------------------------------+
@ -60,14 +60,16 @@ public:
inline GLuint getTextureID() const { return _textureID; } inline GLuint getTextureID() const { return _textureID; }
inline V3F_C4B_T2F_Quad* getQuad() const { return _quad; } inline V3F_C4B_T2F_Quad* getQuads() const { return _quads; }
inline ssize_t getQuadCount() const { return _quadCount; } inline ssize_t getQuadCount() const { return _quadsCount; }
inline GLProgram* getShader() const { return _shader; } inline GLProgram* getShader() const { return _shader; }
inline BlendFunc getBlendType() const { return _blendType; } inline BlendFunc getBlendType() const { return _blendType; }
inline const kmMat4& getModelView() const { return _mv; }
protected: protected:
int32_t _materialID; int32_t _materialID;
@ -85,9 +87,10 @@ protected:
BlendFunc _blendType; BlendFunc _blendType;
V3F_C4B_T2F_Quad* _quad; V3F_C4B_T2F_Quad* _quads;
ssize_t _quadCount; ssize_t _quadsCount;
ssize_t _capacity;
kmMat4 _mv;
}; };
NS_CC_END NS_CC_END

View File

@ -42,6 +42,7 @@ public:
{ {
QUAD_COMMAND, QUAD_COMMAND,
CUSTOM_COMMAND, CUSTOM_COMMAND,
BATCH_COMMAND,
GROUP_COMMAND, GROUP_COMMAND,
UNKNOWN_COMMAND, UNKNOWN_COMMAND,
}; };

View File

@ -27,6 +27,7 @@
#include "ccGLStateCache.h" #include "ccGLStateCache.h"
#include "CCCustomCommand.h" #include "CCCustomCommand.h"
#include "renderer/CCQuadCommand.h" #include "renderer/CCQuadCommand.h"
#include "renderer/CCBatchCommand.h"
#include "CCGroupCommand.h" #include "CCGroupCommand.h"
#include "CCConfiguration.h" #include "CCConfiguration.h"
#include "CCDirector.h" #include "CCDirector.h"
@ -256,7 +257,9 @@ void Renderer::render()
_lastCommand ++; _lastCommand ++;
} }
memcpy(_quads + _numQuads, cmd->getQuad(), sizeof(V3F_C4B_T2F_Quad) * cmdQuadCount); memcpy(_quads + _numQuads, cmd->getQuads(), sizeof(V3F_C4B_T2F_Quad) * cmdQuadCount);
convertToWorldCoordiantes(_quads + _numQuads, cmdQuadCount, cmd->getModelView());
_numQuads += cmdQuadCount; _numQuads += cmdQuadCount;
} }
else if(commandType == RenderCommand::Type::CUSTOM_COMMAND) else if(commandType == RenderCommand::Type::CUSTOM_COMMAND)
@ -265,6 +268,12 @@ void Renderer::render()
CustomCommand* cmd = static_cast<CustomCommand*>(command); CustomCommand* cmd = static_cast<CustomCommand*>(command);
cmd->execute(); cmd->execute();
} }
else if(commandType == RenderCommand::Type::BATCH_COMMAND)
{
flush();
BatchCommand* cmd = static_cast<BatchCommand*>(command);
cmd->execute();
}
else if(commandType == RenderCommand::Type::GROUP_COMMAND) else if(commandType == RenderCommand::Type::GROUP_COMMAND)
{ {
flush(); flush();
@ -319,6 +328,49 @@ void Renderer::render()
_lastMaterialID = 0; _lastMaterialID = 0;
} }
void Renderer::convertToWorldCoordiantes(V3F_C4B_T2F_Quad* quads, ssize_t quantity, const kmMat4& modelView)
{
for(ssize_t i=0; i<quantity; ++i) {
V3F_C4B_T2F_Quad *q = &quads[i];
kmVec3 vec1, out1;
vec1.x = q->bl.vertices.x;
vec1.y = q->bl.vertices.y;
vec1.z = q->bl.vertices.z;
kmVec3Transform(&out1, &vec1, &modelView);
q->bl.vertices.x = out1.x;
q->bl.vertices.y = out1.y;
q->bl.vertices.z = out1.z;
kmVec3 vec2, out2;
vec2.x = q->br.vertices.x;
vec2.y = q->br.vertices.y;
vec2.z = q->br.vertices.z;
kmVec3Transform(&out2, &vec2, &modelView);
q->br.vertices.x = out2.x;
q->br.vertices.y = out2.y;
q->br.vertices.z = out2.z;
kmVec3 vec3, out3;
vec3.x = q->tr.vertices.x;
vec3.y = q->tr.vertices.y;
vec3.z = q->tr.vertices.z;
kmVec3Transform(&out3, &vec3, &modelView);
q->tr.vertices.x = out3.x;
q->tr.vertices.y = out3.y;
q->tr.vertices.z = out3.z;
kmVec3 vec4, out4;
vec4.x = q->tl.vertices.x;
vec4.y = q->tl.vertices.y;
vec4.z = q->tl.vertices.z;
kmVec3Transform(&out4, &vec4, &modelView);
q->tl.vertices.x = out4.x;
q->tl.vertices.y = out4.y;
q->tl.vertices.z = out4.z;
}
}
void Renderer::drawBatchedQuads() void Renderer::drawBatchedQuads()
{ {
//TODO we can improve the draw performance by insert material switching command before hand. //TODO we can improve the draw performance by insert material switching command before hand.

View File

@ -75,9 +75,12 @@ protected:
void mapBuffers(); void mapBuffers();
void drawBatchedQuads(); void drawBatchedQuads();
//Draw the previews queued quads and flush previous context //Draw the previews queued quads and flush previous context
void flush(); void flush();
void convertToWorldCoordiantes(V3F_C4B_T2F_Quad* quads, ssize_t quantity, const kmMat4& modelView);
std::stack<int> _commandGroupStack; std::stack<int> _commandGroupStack;
std::stack<RenderStackElement> _renderStack; std::stack<RenderStackElement> _renderStack;