帧率控制及批量渲染
帧率fps很简单,关键在于时间的获取。
void Engine::mainLoop()
{
struct timeval tv;
gettimeofday (&tv, NULL);
unsigned int now = tv.tv_sec * 1000000 + tv.tv_usec;
if ( (now - _lastTime) > _animationInterval)
{
stepFrame();
_lastTime = now;
}
else
{
#if (TARGET_PLATFORM == PLATFORM_WIN32)
Sleep(0);
#else
sleep(0);
#endif
}
}
时间的获取使用函数gettimeofday,win32下我们利用getlocalTime来写一个。
#if (TARGET_PLATFORM == PLATFORM_WIN32)
int gettimeofday(struct timeval *tp, void *tzp)
{
time_t clock;
struct tm tm;
SYSTEMTIME wtm;
GetLocalTime(&wtm);
tm.tm_year = wtm.wYear - 1900;
tm.tm_mon = wtm.wMonth - 1;
tm.tm_mday = wtm.wDay;
tm.tm_hour = wtm.wHour;
tm.tm_min = wtm.wMinute;
tm.tm_sec = wtm.wSecond;
tm. tm_isdst = -1;
clock = mktime(&tm);
tp->tv_sec = clock;
tp->tv_usec = wtm.wMilliseconds * 1000;
return (0);
}
#endif
要注意的是,
gettimeofday本生是微秒级精度,现在只能达到毫秒级。不过这个对于帧率控制也够了。
批量渲染的本质是相同纹理的quad,我们一次性给它画出来。
std::vector<QuadCommand*> _batchedQuadCommands;
int _numQuads;
Quad _quads[VBO_SIZE];
GLushort _indices[6 * VBO_SIZE];
GLuint _buffersVBO[2];
static const int VBO_SIZE = 65536 / 6;
里面的相互关系是1个quad 对应6个索引 顶点,int 值0-0xffff也就是0-65535。
Renderer::Renderer(void):_isInit(false),_numQuads(0)
{
glGenBuffers(2,&_buffersVBO[0]);
for( int i=0; i < VBO_SIZE; i++)
{
_indices[i*6+0] = (GLushort) (i*4+0);
_indices[i*6+1] = (GLushort) (i*4+1);
_indices[i*6+2] = (GLushort) (i*4+2);
_indices[i*6+3] = (GLushort) (i*4+3);
_indices[i*6+4] = (GLushort) (i*4+2);
_indices[i*6+5] = (GLushort) (i*4+1);
}
}
Renderer::~Renderer(void)
{
glDeleteBuffers(2,&_buffersVBO[0]);
}
void Renderer::renderBatch()
{
if(_numQuads <= 0 || _batchedQuadCommands.empty())
{
return;
}
glBindBuffer(GL_ARRAY_BUFFER, _buffersVBO[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(_quads[0]) * _numQuads , _quads, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(_vertexPosition);
glVertexAttribPointer(
_vertexPosition, // attribute 0. No particular reason for 0, but must match the layout in the shader.
3, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
kQuadSize, // stride
(void*)(offsetof( Vertex, _position)) // array buffer offset
);
glEnableVertexAttribArray(_vertexUV);
glVertexAttribPointer(
_vertexUV, // attribute 0. No particular reason for 0, but must match the layout in the shader.
2, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
kQuadSize, // stride
(void*)(offsetof( Vertex, _texCoord)) // array buffer offset
);
glEnableVertexAttribArray(_vertexColor);
glVertexAttribPointer(
_vertexColor, // attribute 0. No particular reason for 0, but must match the layout in the shader.
4, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
kQuadSize, // stride
(void*)(offsetof( Vertex, _color)) // array buffer offset
);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _buffersVBO[1]);
int quadsToDraw = 0;
int startQuad = 0;
GLuint _lastTextureID = 0;
//Start drawing verties in batch
for(const auto& cmd : _batchedQuadCommands)
{
auto newTextureID = cmd->getTextureID();
if(_lastTextureID != newTextureID )
{
//Draw quads
if(quadsToDraw > 0)
{
glDrawElements(GL_TRIANGLES, (GLsizei) quadsToDraw*6, GL_UNSIGNED_SHORT, (GLvoid*) (startQuad*6*sizeof(_indices[0])) );
startQuad += quadsToDraw;
quadsToDraw = 0;
}
//Use new Texture
cmd->useTexture();
_lastTextureID = newTextureID;
}
quadsToDraw += cmd->getQuadCount();
}
if(quadsToDraw > 0)
{
glDrawElements(GL_TRIANGLES, (GLsizei) quadsToDraw*6, GL_UNSIGNED_SHORT, (GLvoid*) (startQuad*6*sizeof(_indices[0])) );
}
_batchedQuadCommands.clear();
_numQuads = 0;
}
_numQuads表示批量渲染集合的总个数。_batchedQuadCommands里是经过处理后的渲染集合,它把同纹理的quad给放在了一起。当检测到纹理不同时,就一次性把之前的quad给画出来。为了能够最大限度用到批量渲染。需要我们进行一个预处理操作,即modle*view*projection的计算不放到shader执行,放到cpu来执行,batch里的quad顶点是已经变换好之后的。