游戏开发论坛

 找回密码
 立即注册
搜索
查看: 7982|回复: 4

3D API中,字体绘制的Batch优化

[复制链接]

89

主题

4036

帖子

4132

积分

论坛元老

Rank: 8Rank: 8

积分
4132
发表于 2012-9-29 23:39:00 | 显示全部楼层 |阅读模式
按:最近想起坛子里有个古老的问题。字体如何绘制。也想起很久以前给网友做的一个回答: 如何高效的绘制字符,那又是另外一个问题了。今天就特别来解释一下这个问题.
原文首发于我的blog:
http://blog.csdn.net/nhsoft/article/details/7788547

在3D引擎中绘制字符是很恶心的事情。一个个绘制吧又太慢。组成一个大mesh吧。
又太耗引擎的人品。而且还是得lock/unlock vb。
才绘制了一百多个字。 fps从600掉到80多。

经过跟别人的讨论。pos之类的数据用shader constant来传递会效率高一些。
于是做了这样个处理。
1. 做72个1x1大小的Quad。 (其实可以用一个,DrawInstance就好了)。
2. 将纹理相同的char集合起来。
3. 准备一个shader接受 72个字符的信息(pos , texture , color)。其实最多可以81个。 81x3 = 243 + 3 x 4 = 255(最多256个constant)
4. 将这些constant一次性传递过去。一次绘制72个字符。

这个好处是1 没有lock unlock vb。2. 一次绘制最多72个字符。效率更高。

这样处理后,绘制100个字符后, fps基本没有多大的变化。

BEGIN_NAMESPACE_XEVOL3D;
class xBatchFontDevice : public IFontRenderDevice
{
    IRenderApi*            m_pRenderApi;
    eFontFilter            m_filter;
    IBlenderState*         m_BlendState;
    ISamplerState*         m_pPointSampler;
    ISamplerState*         m_pLinearSampler;
    HGpuProgram            m_pDefFontShader;
    HGpuProgram            m_pUsrFontShader;
    IDepthStencilState*    m_pStencilState;


    IVertexStream*         m_pVertexStream;
    IInputBuffer*          m_pVertexBuffer;
    IInputBuffer*          m_pIdxBuffer   ;
    IInputAssembler*       m_pAss         ;
    int                    m_nQuad        ;


    float4                 m_FontColor[72];
    float4                 m_SrcRect  [72];
    float4                 m_DstRect  [72];
    int                    m_iQuadIndex;
    IBaseTexture*          m_pTexture;
    struct xBathFontVertex
    {
        xvec4       m_pos;
        xvec2       m_uv[2];
    };
public:
    IMPL_REFCOUNT_OBJECT_INTERFACE(xBatchFontDevice);
    xBatchFontDevice(IRenderApi* pRenderApi);
    ~xBatchFontDevice();
    IRenderApi*             renderApi(){return m_pRenderApi ; }
public:
    virtual bool            resetFontFilter() { return true ; }
    virtual bool            setUVLayer(int nUVLayer);
    virtual bool            setShaderProgram(HGpuProgram pProgram);
    virtual bool            init();
    virtual bool            setFontFilter(eFontFilter filter);
    virtual bool            beginFontRender();
    virtual bool            endFontRender();
    virtual bool            commit();
    virtual bool            drawRectf(IBaseTexture* pTexture, float vDestRect[4] , const xColor_4f& color);
    virtual bool            drawRectf(IBaseTexture* pTexture, float vDestRect[4] ,float vSrcRect[4]  , const xColor_4f& color);
    virtual IBaseTexture*   createTexture(int w , int h , ePIXEL_FORMAT fmt , bool bReadable , eResourceUsage usage , int nMipMap = 1, int nArraySize = 1 );
    virtual bool            isTextureSupport(ePIXEL_FORMAT fmt , bool lockable = true);
};




bool xBatchFontDevice::setUVLayer(int nUVLayer)
{


    return true;
}


bool xBatchFontDevice::setShaderProgram(HGpuProgram pProgram)
{
    m_pUsrFontShader = pProgram;
    return true;
}


bool xBatchFontDevice::init()
{   
    xInputLayoutDesc InputDesc;
    InputDesc.addElement(SHADER_SEMANTIC_POSITION     , SHADERVARTYPE_FLOAT4    );
    InputDesc.addElement(SHADER_SEMANTIC_TEXCOORD     , SHADERVARTYPE_FLOAT2 , 0);
    InputDesc.addElement(SHADER_SEMANTIC_TEXCOORD     , SHADERVARTYPE_FLOAT2 , 1);
    InputDesc.addBufferDesc(RESOURCE_USAGE_DEFAULT    , RESOURCE_ACCESS_NONE);


    m_pAss  = m_pRenderApi->createInputAssembler(L"BatchFont", InputDesc);
    m_pVertexStream = m_pAss->createVertexStream();


    m_nQuad = m_pRenderApi->intCapsValue(L"FontBatchCount" , 72 );
    if(m_nQuad > 72) m_nQuad = 72;
    xFaceIndex16_U indices[ 72 * 2 ];
    for(int i = 0 ; i < m_nQuad ; i ++)
    {
        int baseIndex = i * 4;
        indices[i * 2    ].v1 = 0 + baseIndex;
        indices[i * 2    ].v2 = 2 + baseIndex;
        indices[i * 2    ].v3 = 1 + baseIndex;


        indices[i * 2 + 1].v1 = 2 + baseIndex;
        indices[i * 2 + 1].v2 = 0 + baseIndex;
        indices[i * 2 + 1].v3 = 3 + baseIndex;
    }
    xInputBufferDesc idxBufDesc;
    idxBufDesc.m_usage       = RESOURCE_USAGE_DEFAULT;
    idxBufDesc.m_accessFlage = RESOURCE_ACCESS_NONE;
    idxBufDesc.m_bindtype    = BIND_AS_INDEX_BUFFER;
    m_pIdxBuffer = m_pRenderApi->createInputBuffer(72 * 6  , 2 , &idxBufDesc,indices);
    //////////////////////////////////////////////////////////////////////////
    xInputBufferDesc vBufDesc;
    vBufDesc.m_usage       = RESOURCE_USAGE_DEFAULT;
    vBufDesc.m_accessFlage = RESOURCE_ACCESS_NONE;
    vBufDesc.m_bindtype    = BIND_AS_VERTEX_BUFFER;


    xBathFontVertex batchVertex[72 * 4];
    float _zValue = m_pRenderApi->get2DZValue();
    for(int i = 0 ; i < 72 ; i ++)
    {
        xBathFontVertex* Vertex = batchVertex + i * 4;
        Vertex[0].m_uv[0] = Vertex[0].m_uv[1] = xvec2(0.0f  , 1.0f);
        Vertex[1].m_uv[0] = Vertex[1].m_uv[1] = xvec2(0.0f  , 0.0f);
        Vertex[2].m_uv[0] = Vertex[2].m_uv[1] = xvec2(1.0f  , 0.0f);
        Vertex[3].m_uv[0] = Vertex[3].m_uv[1] = xvec2(1.0f  , 1.0f);
        
        Vertex[0].m_pos   = xvec4(0.0f  , 1.0f , _zValue , i);
        Vertex[1].m_pos   = xvec4(0.0f  , 0.0f , _zValue , i);
        Vertex[2].m_pos   = xvec4(1.0f  , 0.0f , _zValue , i);
        Vertex[3].m_pos   = xvec4(1.0f  , 1.0f , _zValue , i);
    }


    m_pVertexStream->createInputBuffer( 0 , 4 * m_nQuad , &batchVertex , &vBufDesc );
    m_pVertexBuffer = m_pVertexStream->getInputBuffer(0);
    m_pVertexBuffer->update(eLock_WriteDiscard , batchVertex , sizeof(xBathFontVertex) * 4 * 72);




    m_iQuadIndex = 0;


    m_filter = eFontFilter_Point;


    if(m_pDefFontShader.isHandle() == false)
    {
        HGpuProgram hGpuProgram = m_pRenderApi->gpuProgramManager()->load(L"batchFont.vertex", L"font.pixel");
        m_pDefFontShader = hGpuProgram;
    }


    if(m_pStencilState == NULL)         { m_pStencilState  = m_pRenderApi->createDepthStencilState(L"Overlay");}
    if(m_pPointSampler == NULL)         { m_pPointSampler  = m_pRenderApi->createSamplerState(L"FontPoint");        }
    if(m_pLinearSampler == NULL) { m_pLinearSampler = m_pRenderApi->createSamplerState(L"FontLinear");        }
    if(m_BlendState == NULL)     { m_BlendState     = m_pRenderApi->createBlendState(L"Font.Blend"); }
    return true;
}


bool xBatchFontDevice::setFontFilter(eFontFilter filter)
{
    m_filter = filter;
    if(m_filter == eFontFilter_Point)
    {
        return m_pRenderApi->setSamplerState(eShader_PixelShader , 0 , m_pPointSampler );
    }
    else if(m_filter == eFontFilter_Linear)
    {
        return m_pRenderApi->setSamplerState(eShader_PixelShader , 0 , m_pLinearSampler );
    }
    return false;
}


xBatchFontDevice::xBatchFontDevice(IRenderApi* pRenderApi)
{
    m_filter         = eFontFilter_Point;
    m_pStencilState  = NULL;
    m_pDefFontShader.setNULL();
    m_pRenderApi     = pRenderApi;
    m_pPointSampler  = NULL;
    m_pLinearSampler = NULL;
    m_BlendState     = NULL;
    m_pUsrFontShader.setNULL();
    m_pTexture       = NULL;


    m_pVertexStream= NULL;
    m_pVertexBuffer= NULL;
    m_pIdxBuffer   = NULL;
    m_pAss         = NULL;
    m_nQuad        = 0;
    m_iQuadIndex = 0;


}


xBatchFontDevice::~xBatchFontDevice()
{


}


bool xBatchFontDevice::beginFontRender()
{
    if(m_pDefFontShader.isHandle()  == false )
        return false;
    m_pRenderApi->setDepthStencilState(m_pStencilState);
    m_pRenderApi->setBlendState(m_BlendState);
    IGpuProgramParamTable* pShaderTable = NULL;
    if(m_pUsrFontShader)
    {
        m_pRenderApi->setGpuProgram(m_pUsrFontShader);// pushGpuProgram(m_pUsrFontShader);
        pShaderTable = m_pUsrFontShader->getParamTable();// pushGpuProgram(m_pUsrFontShader);
    }
    else
    {
        m_pRenderApi->setGpuProgram(m_pDefFontShader) ; // pushGpuProgram(m_pDefFontShader);
        pShaderTable = m_pDefFontShader->getParamTable(); // pushGpuProgram(m_pDefFontShader);
    }


    float4x4 matWorld;
    float4x4 matProject;
    float4x4 matView;
    m_pRenderApi->getCamera()->toMatrix(matView , matProject);
    m_pRenderApi->getMatrix(matWorld.data       , MATRIXMODE_World );


    pShaderTable->setParamater(L"_matWorld"   , matWorld   , eShader_VertexShader);
    pShaderTable->setParamater(L"_matView"    , matView    , eShader_VertexShader);
    pShaderTable->setParamater(L"_matProject" , matProject , eShader_VertexShader);


    m_iQuadIndex = 0;
    return true;
}


bool xBatchFontDevice::endFontRender()
{
    commit();
    return true;
}


bool xBatchFontDevice::drawRectf(IBaseTexture* pTexture, float vDestRect[4] , const xColor_4f& color)
{
    if(m_pRenderApi)
    {
        assert(0);
        return false;
    }
    return false;
}


bool xBatchFontDevice::commit()
{
    if(m_iQuadIndex == 0)
        return true ;


    IGpuProgramParamTable* pShaderTable = NULL;
    if(m_pUsrFontShader)
    {
        pShaderTable = m_pUsrFontShader->getParamTable();// pushGpuProgram(m_pUsrFontShader);
    }
    else
    {
        pShaderTable = m_pDefFontShader->getParamTable(); // pushGpuProgram(m_pDefFontShader);
    }
   
    m_pRenderApi->setTextureByLayer(0 , m_pTexture);
    pShaderTable->setParamater(L"SrcRect"   , m_SrcRect   , m_iQuadIndex , 0 ,  eShader_VertexShader);
    pShaderTable->setParamater(L"DstRect"   , m_DstRect   , m_iQuadIndex , 0 ,  eShader_VertexShader);
    pShaderTable->setParamater(L"RectColor" , m_FontColor , m_iQuadIndex , 0 ,  eShader_VertexShader);


    m_pRenderApi->setVertexBuffer(m_pVertexBuffer , 0 , sizeof(xBathFontVertex) );
    m_pRenderApi->setInputAssembler( m_pAss );
    m_pRenderApi->draw(m_pIdxBuffer , m_iQuadIndex * 6 );


    m_iQuadIndex = 0;
    return true;
}


bool xBatchFontDevice::drawRectf(IBaseTexture* pTexture, float vDestRect[4] ,float vSrcRect[4]  , const xColor_4f& color)
{
    if(m_pRenderApi)
    {
        if(m_pTexture != NULL && m_pTexture != pTexture)
            commit();


        m_pTexture = pTexture;
        const xTextureDesc& textDesc = pTexture->desc();
        xvec4& dstRect =  *((xvec4*)vDestRect);
        xvec4  srcRect =  *((xvec4*)vSrcRect);
        const xTextureDesc& desc = pTexture->desc();


        srcRect.rect.x /= desc.m_width;
        srcRect.rect.w /= desc.m_width;


        //////////////////////////////////////////////////////////////////////////
        srcRect.rect.y /= desc.m_height;
        srcRect.rect.h /= desc.m_height;


        m_SrcRect[m_iQuadIndex]   = srcRect;
        m_DstRect[m_iQuadIndex]   = dstRect;
        m_FontColor[m_iQuadIndex] = float4(color.r, color.g,color.b,color.a);
        m_iQuadIndex ++;
        if(m_iQuadIndex >= m_nQuad )
        {
            commit();
        }
    }
    return false;
}


IBaseTexture* xBatchFontDevice::createTexture(int w , int h , ePIXEL_FORMAT fmt , bool bReadable, eResourceUsage usage , int nMipMap, int nArraySize)
{
    if(m_pRenderApi)
    {
        xTextureInitDesc texInitDesc(w , h , fmt);
        texInitDesc.m_bReadable = bReadable;//;
        texInitDesc.m_TextureDesc.m_nMipmap = nMipMap;
        texInitDesc.m_TextureDesc.m_nArraySize = nArraySize;
        texInitDesc.m_usage  = usage;
        if(texInitDesc.m_usage == RESOURCE_USAGE_DEFAULT)
        {
            texInitDesc.m_access = 0;
        }
        return m_pRenderApi->createTexture(texInitDesc , NULL , 0);
    }
    return false;
}


bool xBatchFontDevice::isTextureSupport(ePIXEL_FORMAT fmt , bool lockable)
{
    if(m_pRenderApi)
    {
        return m_pRenderApi->isTextureSupport(fmt , lockable);
    }
    return false;
}


END_NAMESPACE_XEVOL3D;http://blog.csdn.net/nhsoft/article/details/7788547http://blog.csdn.net/nhsoft/article/details/7788547

50

主题

236

帖子

454

积分

中级会员

Rank: 3Rank: 3

积分
454
发表于 2012-10-13 13:42:00 | 显示全部楼层

Re:3D API中,字体绘制的Batch优化

绘制字体就是绘制图像,不管是2D还是3D
只要知道如何高效的绘制2D图像,字体绘制就解决了。
我一般把字体图像放到一个256×256或者128×128的纹理上.

0

主题

142

帖子

178

积分

注册会员

Rank: 2

积分
178
发表于 2012-11-14 13:25:00 | 显示全部楼层

Re:3D API中,字体绘制的Batch优化

啊啊啊

1

主题

21

帖子

204

积分

中级会员

Rank: 3Rank: 3

积分
204
发表于 2012-12-3 09:09:00 | 显示全部楼层

Re:3D API中,字体绘制的Batch优化

做一个背景缓冲区,在上面绘制文字,然后再转化成纹理.
纹理渲染就会快很多了.

32

主题

1259

帖子

1351

积分

金牌会员

Rank: 6Rank: 6

积分
1351
发表于 2012-12-3 15:31:00 | 显示全部楼层

Re:3D API中,字体绘制的Batch优化

把最近使用的文字cache到一张纹理中。然后每帧动态构造quad绘制出来。
其实批次就很低了。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

作品发布|文章投稿|广告合作|关于本站|游戏开发论坛 ( 闽ICP备17032699号-3 )

GMT+8, 2025-6-10 14:00

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表