axmol/cocos/2d/renderer/Renderer.cpp

335 lines
9.3 KiB
C++
Raw Normal View History

//
// Created by NiTe Luo on 10/31/13.
//
#include "Renderer.h"
#include "CCShaderCache.h"
#include "ccGLStateCache.h"
2013-11-09 04:06:39 +08:00
#include "CustomCommand.h"
2013-11-12 03:54:08 +08:00
#include "QuadCommand.h"
2013-11-14 09:31:12 +08:00
#include "GroupCommand.h"
2013-11-09 04:06:39 +08:00
NS_CC_BEGIN
using namespace std;
2013-11-15 08:39:03 +08:00
static Renderer* s_instance;
Renderer *Renderer::getInstance()
{
2013-11-08 08:50:53 +08:00
if(!s_instance)
{
2013-11-08 08:50:53 +08:00
s_instance = new Renderer();
if(!s_instance->init())
{
CC_SAFE_DELETE(s_instance);
}
}
2013-11-08 08:50:53 +08:00
return s_instance;
}
void Renderer::destroyInstance()
{
CC_SAFE_RELEASE_NULL(s_instance);
}
Renderer::Renderer()
:_lastMaterialID(0)
2013-11-08 07:48:37 +08:00
,_numQuads(0)
2013-11-11 16:14:29 +08:00
,_firstCommand(0)
,_lastCommand(0)
,_glViewAssigned(false)
2013-11-08 07:48:37 +08:00
{
_commandGroupStack.push(DEFAULT_RENDER_QUEUE);
2013-11-14 09:31:12 +08:00
RenderQueue defaultRenderQueue;
_renderGroups.push_back(defaultRenderQueue);
_renderStack.push({DEFAULT_RENDER_QUEUE, 0});
2013-11-08 07:48:37 +08:00
}
Renderer::~Renderer()
{
2013-11-14 09:31:12 +08:00
_renderGroups.clear();
glDeleteBuffers(2, _buffersVBO);
// if (Configuration::getInstance()->supportsShareableVAO())
// {
2013-11-23 02:24:52 +08:00
glDeleteVertexArrays(1, &_quadVAO);
GL::bindVAO(0);
// }
2013-11-08 08:50:53 +08:00
}
bool Renderer::init()
{
return true;
}
2013-11-08 08:50:53 +08:00
void Renderer::initGLView()
{
setupIndices();
2013-11-08 08:50:53 +08:00
setupVBOAndVAO();
_glViewAssigned = true;
2013-11-08 08:50:53 +08:00
}
void Renderer::setupIndices()
{
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);
}
}
void Renderer::setupVBOAndVAO()
{
2013-11-23 02:24:52 +08:00
glGenVertexArrays(1, &_quadVAO);
GL::bindVAO(_quadVAO);
2013-11-08 08:50:53 +08:00
glGenBuffers(2, &_buffersVBO[0]);
glBindBuffer(GL_ARRAY_BUFFER, _buffersVBO[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(_quads[0]) * VBO_SIZE, _quads, GL_DYNAMIC_DRAW);
// vertices
glEnableVertexAttribArray(GLProgram::VERTEX_ATTRIB_POSITION);
glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION, 3, GL_FLOAT, GL_FALSE, sizeof(V3F_C4B_T2F), (GLvoid*) offsetof( V3F_C4B_T2F, vertices));
// colors
glEnableVertexAttribArray(GLProgram::VERTEX_ATTRIB_COLOR);
glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(V3F_C4B_T2F), (GLvoid*) offsetof( V3F_C4B_T2F, colors));
// tex coords
glEnableVertexAttribArray(GLProgram::VERTEX_ATTRIB_TEX_COORDS);
glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_TEX_COORDS, 2, GL_FLOAT, GL_FALSE, sizeof(V3F_C4B_T2F), (GLvoid*) offsetof( V3F_C4B_T2F, texCoords));
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _buffersVBO[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(_indices[0]) * VBO_SIZE * 6, _indices, GL_STATIC_DRAW);
// Must unbind the VAO before changing the element buffer.
GL::bindVAO(0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
CHECK_GL_ERROR_DEBUG();
}
2013-11-16 09:32:29 +08:00
void Renderer::addCommand(RenderCommand* command)
{
command->generateID();
_renderGroups[_commandGroupStack.top()].push_back(command);
2013-11-16 09:32:29 +08:00
}
void Renderer::addCommand(RenderCommand* command, int renderQueue)
{
2013-11-06 09:00:34 +08:00
command->generateID();
2013-11-14 09:31:12 +08:00
_renderGroups[renderQueue].push_back(command);
}
void Renderer::pushGroup(int renderQueueID)
{
_commandGroupStack.push(renderQueueID);
}
void Renderer::popGroup()
{
_commandGroupStack.pop();
}
2013-11-16 03:29:11 +08:00
int Renderer::createRenderQueue()
{
RenderQueue newRenderQueue;
_renderGroups.push_back(newRenderQueue);
return (int)_renderGroups.size() - 1;
2013-11-16 03:29:11 +08:00
}
2013-11-06 09:00:34 +08:00
bool compareRenderCommand(RenderCommand* a, RenderCommand* b)
{
return a->getID() < b->getID();
}
void Renderer::render()
{
2013-11-07 06:57:42 +08:00
//Uncomment this once everything is rendered by new renderer
//glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//TODO setup camera or MVP
if (_glViewAssigned)
2013-11-14 09:31:12 +08:00
{
//Process render commands
//1. Sort render commands based on ID
for (auto it = _renderGroups.begin(); it != _renderGroups.end(); ++it)
2013-11-14 09:31:12 +08:00
{
stable_sort((*it).begin(), (*it).end(), compareRenderCommand);
}
while(!_renderStack.empty())
{
RenderQueue currRenderQueue = _renderGroups[_renderStack.top().renderQueueID];
size_t len = currRenderQueue.size();
//Refresh the batch command index in case the renderStack has changed.
_firstCommand = _lastCommand = _renderStack.top().currentIndex;
//Process RenderQueue
for(size_t i = _renderStack.top().currentIndex; i < len; i++)
2013-11-14 09:31:12 +08:00
{
_renderStack.top().currentIndex = _lastCommand = i;
auto command = currRenderQueue[i];
if(command->getType() == QUAD_COMMAND)
2013-11-14 09:31:12 +08:00
{
QuadCommand* cmd = static_cast<QuadCommand*>(command);
CCASSERT(cmd->getQuadCount()<VBO_SIZE, "VBO is not big enough for quad data, please break the quad data down or use customized render command");
//Batch quads
if(_numQuads + cmd->getQuadCount() < VBO_SIZE)
{
memcpy(_quads + _numQuads, cmd->getQuad(), sizeof(V3F_C4B_T2F_Quad) * cmd->getQuadCount());
_numQuads += cmd->getQuadCount();
}
else
{
//Draw batched quads if VBO is full
drawBatchedQuads();
}
}
else if(command->getType() == CUSTOM_COMMAND)
{
flush();
CustomCommand* cmd = static_cast<CustomCommand*>(command);
cmd->execute();
}
else if(command->getType() == GROUP_COMMAND)
{
flush();
GroupCommand* cmd = static_cast<GroupCommand*>(command);
_renderStack.top().currentIndex = i + 1;
//push new renderQueue to renderStack
_renderStack.push({cmd->getRenderQueueID(), 0});
//Exit current loop
break;
2013-11-14 09:31:12 +08:00
}
else
{
flush();
2013-11-14 09:31:12 +08:00
}
}
//Draw the batched quads
drawBatchedQuads();
currRenderQueue = _renderGroups[_renderStack.top().renderQueueID];
len = currRenderQueue.size();
//If pop the render stack if we already processed all the commands
if(_renderStack.top().currentIndex + 1 >= len)
2013-11-14 09:31:12 +08:00
{
_renderStack.pop();
2013-11-14 09:31:12 +08:00
}
}
}
2013-11-15 02:35:28 +08:00
//TODO give command back to command pool
for (size_t j = 0 ; j < _renderGroups.size(); j++)
{
2013-11-15 02:35:28 +08:00
for_each(_renderGroups[j].begin(), _renderGroups[j].end(), [](RenderCommand* cmd){delete cmd;});
_renderGroups[j].clear();
2013-11-11 16:14:29 +08:00
}
//Clear the stack incase gl view hasn't been initialized yet
while(!_renderStack.empty())
{
_renderStack.pop();
}
2013-11-15 04:17:54 +08:00
_renderStack.push({DEFAULT_RENDER_QUEUE, 0});
2013-11-11 16:14:29 +08:00
_firstCommand = _lastCommand = 0;
_lastMaterialID = 0;
}
2013-11-09 04:06:39 +08:00
2013-11-11 16:14:29 +08:00
void Renderer::drawBatchedQuads()
{
2013-11-19 07:52:47 +08:00
//TODO we can improve the draw performance by insert material switching command before hand.
2013-11-14 02:26:26 +08:00
2013-11-11 16:14:29 +08:00
int quadsToDraw = 0;
int startQuad = 0;
2013-11-11 16:14:29 +08:00
//Upload buffer to VBO
if(_numQuads <= 0)
2013-11-19 07:52:47 +08:00
{
_firstCommand = _lastCommand;
2013-11-11 16:14:29 +08:00
return;
2013-11-19 07:52:47 +08:00
}
2013-11-11 16:14:29 +08:00
//Set VBO data
glBindBuffer(GL_ARRAY_BUFFER, _buffersVBO[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(_quads[0]) * (_numQuads), NULL, GL_DYNAMIC_DRAW);
void *buf = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
memcpy(buf, _quads, sizeof(_quads[0])* (_numQuads));
glUnmapBuffer(GL_ARRAY_BUFFER);
glBindBuffer(GL_ARRAY_BUFFER, 0);
//Bind VAO
2013-11-23 02:24:52 +08:00
GL::bindVAO(_quadVAO);
2013-11-11 16:14:29 +08:00
2013-11-12 03:54:08 +08:00
//Start drawing verties in batch
2013-11-11 16:14:29 +08:00
for(size_t i = _firstCommand; i <= _lastCommand; i++)
{
2013-11-15 04:17:54 +08:00
RenderCommand* command = _renderGroups[_renderStack.top().renderQueueID][i];
2013-11-11 16:14:29 +08:00
if (command->getType() == QUAD_COMMAND)
{
QuadCommand* cmd = static_cast<QuadCommand*>(command);
if(_lastMaterialID != cmd->getMaterialID())
2013-11-09 04:06:39 +08:00
{
2013-11-11 16:14:29 +08:00
//Draw quads
if(quadsToDraw > 0)
{
glDrawElements(GL_TRIANGLES, (GLsizei) quadsToDraw*6, GL_UNSIGNED_SHORT, (GLvoid*) (startQuad*6*sizeof(_indices[0])) );
2013-11-11 16:14:29 +08:00
startQuad += quadsToDraw;
quadsToDraw = 0;
}
2013-11-09 04:06:39 +08:00
2013-11-11 16:14:29 +08:00
//Use new material
cmd->useMaterial();
_lastMaterialID = cmd->getMaterialID();
2013-11-09 04:06:39 +08:00
}
2013-11-11 16:14:29 +08:00
quadsToDraw += cmd->getQuadCount();
}
}
2013-11-12 03:54:08 +08:00
//Draw any remaining quad
if(quadsToDraw > 0)
{
glDrawElements(GL_TRIANGLES, (GLsizei) quadsToDraw*6, GL_UNSIGNED_SHORT, (GLvoid*) (startQuad*6*sizeof(_indices[0])) );
}
2013-11-08 07:48:37 +08:00
//Unbind VAO
2013-11-21 12:39:32 +08:00
GL::bindVAO(0);
2013-11-12 03:54:08 +08:00
_firstCommand = _lastCommand;
2013-11-08 07:48:37 +08:00
_numQuads = 0;
}
2013-11-09 04:06:39 +08:00
void Renderer::flush()
{
2013-11-12 03:54:08 +08:00
drawBatchedQuads();
2013-11-09 04:06:39 +08:00
_lastMaterialID = 0;
}
NS_CC_END