XBox:Framework:Faster Sprites

From MadoxLabs

Template:Downloads:XBox

Edit Downloads:XBox (http://www.madox.ca/mediawiki2/index.php?title=Template:Downloads:XBox&action=edit)

I seem to have found a way to render sprite significantly faster. I have been playing with different ways to handle UI textures when rendering UI widgets (which a just sprites really). If I have many UI skin textures, is it better to combine them all into one or use many textures and set each sprite to use the appropriate one?

I tried playing with the case of using many textures. In this setup, I render 2000 quads each using a 200x200 texture. I create 7 extra copies of the texture, and create 7 texture samplers in the shader.

In the vertex declaration, I hijack one of the bytes to store which texture to use. Since these sprites are 2D, the Z and W coordinates are free to use. This is where I stuck the texture index. Then to sample the right texture, I have a big if/else block.

Some code explaining this:

The vertex definition:

 struct CrapVertex
 {
   // Each vertex contains the following:
   public Vector4 Position;  // The W coordinate is the flag for which texture to use
   public Vector2 Texture;

   public static int SizeInBytes
   {
     get { return 6 * sizeof(float); }
   }

   public static readonly VertexElement[] VertexElements = new VertexElement[] {
           new VertexElement(0, 0, VertexElementFormat.Vector4, VertexElementMethod.Default, VertexElementUsage.Position, 0),
           new VertexElement(0, 16, VertexElementFormat.Vector2, VertexElementMethod.Default, VertexElementUsage.TextureCoordinate, 0),
           };
 }

A portion of the shader that uses this:

texture tex0;
texture tex1;
// continue tex2 - tex7
sampler2D sam0 = sampler_state { Texture = <tex0>; };
sampler2D sam1 = sampler_state { Texture = <tex1>; };
// continue sam2 - sam7

struct VertexShaderInput
{
  float4 Position : POSITION0;
  float2 Texture : TEXCOORD0;
};

struct VertexShaderOutput
{
  float4 Position : POSITION0;
  float2 Texture : TEXCOORD0;
  float3 Location : COLOR1;
};

VertexShaderOutput VS(VertexShaderInput vertex)
{
 VertexShaderOutput output;

 // snip - setting the output.position

 output.Texture = vertex.Texture; 

 output.Location.xy = float2(pos.x, pos.y);  // copy the untranslated position for use in PS
 output.Location.z = vertex.Position.w;      // pass the texture flag on

 return output;
}

float4 PS(VertexShaderOutput pixel) : COLOR0
{
 float4 ret;
  
 // get the color from the image in the flag
 if (pixel.Location.z == 0) 
   ret = tex2D(sam0, pixel.Texture);
 else 
   ret = tex2D(sam1, pixel.Texture);
 // continue if/else for 2 to 7
   
 return ret;
}

When I set up the vertex stream, I set Position.W to be a number from 0 to 7 for which texture to use in PS().

Before doing this, rendering 2000 quads all at once with a single texture, the time per frame was ~120ms. After adding 7 more identical textures and spreading out the quads evenly across textures, the time per frame was 16ms.

It seems to me, if you are render a large number of prims all using the same texture, you will get great speed increase by using this multi texture method. This kind of thing is great for things like particles, grass, or anything with lots of the same geometry on screen.

2010-06-06

Disregard this page. I had messed up the vertexbuffer so that it was rendering far fewer quads than I though, as I added textures. Once I fixed the error, the multi-texture method was running 3x slower on average. The reason for this is that the shader will sample all 8 textures for all pixels and then pick one from the 8 samples.

Personal tools
Toolbox