Monday, April 23, 2012

Rendering Strategy

I recently optimized the rendering in Shoot, and came out with something faster yet with a very simple implementation.

My goal was to minimize the graphic driver state changes. This typically involves grouping the entities per material and geometry, so as the ones that share the same properties get rendered using the same draw call. This gives a much better performance when the entity count is up. I could push it further using geometry instancing, whereas the world transforms of several entities are "pushed" along a single draw call, but for now, I do without it.

Without further talking here is roughly how it works:
//! vertex info
struct VertexInfo
{
    VertexBuffer* pVertexBuffer;
    std::vector<Matrix44> aTransforms;
};

typedef std::map< u32, VertexInfo > VertexMap;

//! render info
struct RenderInfo
{
    Material* pMaterial;
    VertexMap m_VertexMap;
};

typedef std::map< u32, RenderInfo > RenderMap;

//! adds an entity to a render map
void EntityRenderer::AddToRenderMap(RenderMap& renderMap, RenderableEntity* pEntity)
{
    Material* pMaterial = pEntity->GetMaterial();
    VertexBuffer* pVertexBuffer = pEntity->GetVertexBuffer();
    renderMap[pMaterial->GetID()].pMaterial = pMaterial;
    renderMap[pMaterial->GetID()].m_VertexMap[pVertexBuffer->GetID()].pVertexBuffer = pVertexBuffer;
    renderMap[pMaterial->GetID()].m_VertexMap[pVertexBuffer->GetID()].aTransforms.push_back(pEntity->GetTransformationMatrix());
}

//! renders from a render map
void EntityRenderer::Render(RenderMap& renderMap)
{
    for(RenderMap::iterator it = renderMap.begin(); it != renderMap.end(); ++it)
    {
        Material* pMaterial = (*it).second.pMaterial;
        pMaterial->Begin();

        for(VertexMap::iterator it2 = (*it).second.m_VertexMap.begin();
            it2 != (*it).second.m_VertexMap.end();
            ++it2)
        {
            VertexInfo& vertexInfo = (*it2).second;
            vertexInfo.pVertexBuffer->Begin();

            std::vector<Matrix44>& aTransforms = vertexInfo.aTransforms;
            for(u32 i=0; i<aTransforms.size(); ++i)
            {
                GraphicsDriver::Instance()->SetTransform(GraphicsDriver::TS_World, aTransforms[i]);
                vertexInfo.pVertexBuffer->Draw();
            }
            vertexInfo.pVertexBuffer->End();
        }    

       pMaterial->End();
    }
}

//! renders the entities (Pseudo code)
void EntityRenderer::Render()
{
    ...Setup 3D view

    Render(m_SkyBoxMap);

    Render(m_Solid3DMap);

    Render(m_Transparent3DMap);

    ...Setup 2D View

    Render(m_Solid2DMap);

    Render(m_Transparent2DMap);
}

No comments:

Post a Comment