Advertisement Jump to content
Sign in to follow this  
Juliean

Geometry-Shader for sprites?

This topic is 1730 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello,

 

has somebody every tried to use a geometry-shader to speed up 2d-sprite rendering? I wondered if this could yield some performance benefit. Right now, I'm uploading a full quad of vertices with 3 floats position, 2 floats texcoord and 4 floats color each, making for a complete of 9x4x4 = 144 bytes for each sprite uploaded. That is how I create those vertices:

size_t Sprite::Draw(const math::Vector2& vTextureSize, const math::Vector3& vPosition, const RECT& rSrcRect, const math::Vector2f& vScale)
{
    const math::Vector2 vSrcSize(rSrcRect.right - rSrcRect.left, rSrcRect.bottom - rSrcRect.top);
    const math::Vector2f vSize(vSrcSize.x*vScale.x, vSrcSize.y*vScale.y);

    const float leftVertex = vPosition.x  * m_vInvHalfScreenSize.x - 1.0f;
    const float rightVertex = leftVertex + vSize.x * m_vInvHalfScreenSize.x;
    const float topVertex = -vPosition.y * m_vInvHalfScreenSize.y + 1.0f;
    const float bottomVertex = topVertex - vSize.y * m_vInvHalfScreenSize.y;

    const float leftCoord = rSrcRect.left / (float)vTextureSize.x;
    const float rightCoord = rSrcRect.right / (float)vTextureSize.x;
    const float topCoord = rSrcRect.top / (float)vTextureSize.y;
    const float bottomCoord = rSrcRect.bottom / (float)vTextureSize.y;

    SpriteVertex Vertices[] =
    {
            { leftVertex,    topVertex,        vPosition.z, leftCoord,    topCoord, 1.0f, 1.0f, 1.0f, 1.0f},
            { rightVertex, topVertex, vPosition.z, rightCoord, topCoord, 1.0f, 1.0f, 1.0f, 1.0f },
            { rightVertex, bottomVertex, vPosition.z, rightCoord, bottomCoord, 1.0f, 1.0f, 1.0f, 1.0f },
            { leftVertex, bottomVertex, vPosition.z, leftCoord, bottomCoord, 1.0f, 1.0f, 1.0f, 1.0f }
    };
                
    SpriteVertex* pBuffer = static_cast<SpriteVertex*>(m_mapped.pData);
    memcpy(pBuffer + m_nextFreeId * 4, Vertices, sizeof(SpriteVertex)* 4);

    return m_nextFreeId++;
}

With a geometry-shader, I quess I could upload a point containing exactly those parameters from the methods (thus, 11 float = 44 bytes in size) and have a geometry-shader expand that to the full sprite.

 

Now, I've read that geometry shaders can be pretty slow and should be avoided, so my question is, has anybody experience with this kind of technique, and whether it can potentially yield better performance due to less data being transfered and less CPU-work?

Edited by Juliean

Share this post


Link to post
Share on other sites
Advertisement

I'm sure you could do it. It would help if you're spending a lot of time setting up vertices. You can also achieve the same thing without geometry shaders, if you want. In the vertex shader you just need to figure out which sprite you're rendering by doing VertexID % 4, and then you can apply the correct data by pulling it out of a buffer containing 1 full set of parameters for each sprite.

Edited by MJP

Share this post


Link to post
Share on other sites

It's do-able but it's unlikely to provide a performance benefit.  The overhead of having the GS stage active will hurt some, and modern hardware has such high bandwidth that you'd really need to be drawing a huge (and I mean HUGE) amount of sprites to get any measurable gain from the reduced vertex count.  Vertex counts for this kind of object are not really a bottleneck - trust me.  You'll choke far far more on fillrate and ROP.

 

Where the GS stage is most useful is not really point-to-quad expansion.  It's great for operations on an entire primitive.  If you had an extremely expensive per-triangle calculation in your VS, adding a GS and only doing it once instead of 3 times can be benefit.  If you want to generate per-triangle normals and don't want to bloat your vertex format, it's great.  Use it to frustum-cull individual triangles even (although the GPU is going to do that later on in the pipeline anyway, so only do it in a GS if it means you can skip a significant amount of calculation).  Generate extra sets of texture coords on the fly for detail texturing.  Stuff like that.

 

You can easily do point-to-quad expansion with instancing instead.  Generate the verts for a single quad and make that per-vertex input, then add the data required to complete the sprite as per-instance data, issuing a DrawInstanced call.  But like I said, the number of verts is so unlikely to be a bottleneck that you'll most likely measure no benefit.

Edited by mhagain

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!