Ways to render a massive amount of sprites.

Started by
11 comments, last by Icebone1000 9 years, 2 months ago

Another potential method is to store the sprite info in a buffer resources, and then use an SRV in the vertex shader to grab the data out of it. The interesting thing that you can do, is to create the vertices completely in the vertex shader, with no vertex buffers required at all! The general idea is to use your draw call to specify how many vertices are generated, and then use the SV_VertexID semantic value to grab the appropriate data from the SRV. You would use 4 vertex shader invocations for each of the quads, and the method is relatively efficient. If you check out the particle storm demo from Hieroglyph 3, there is an example of creating vertices without vertex buffers (although I expand the vertices in the GS as mentioned above).

Regarding the use of instancing, you are probably not going to see an improvement for the use of 4 vertices as your instanced geometry. In general, unless you are using geometry with 100 or more vertices you won't see much improvement (at least that is general advice, but your particular situation may or may not reflect the general rule of thumb...).

Advertisement

3. Use one big vertex buffer for n quads. Since recreating such a big buffer from scratch every frame is not a good idea, let's use dynamic vertex buffer. The huge win is that we're calling DrawIndexed() only once. But again, updating such a big buffer will be slow, right?

I'm working on a GUI. And I want it to consume as less as possible.

I have used #3 for a GUI and did not find it slow.

Since you are working on a GUI you might want to consider what you specifically need for that use case. For example, specifying transforms for each quad sounds a bit like overkill to me. You can certainly do it, but do you need to for a GUI?

I think in many cases the quads of a GUI are relatively static. You might want some animation for hovering the cursor or sliding elements into view or whatever, but most things are just going to sit where you stick them. You could transform the verts of the quad on the CPU and then keep them around until there is a change.

Consider something like a text prompt/label, you are probably going to plop it on the screen somewhere, compute the quads for the letters, and from that point on it probably isn't going to move or rotate or scale. If you were to create the array of verts and hold onto it then all you have to do is copy the verts into the dynamic buffer every frame.

Assuming your GUI doesn't have an enormous amount of constantly changing elements, computing the new quads when the data changes is probably not going to be a big deal -- FPS counter, score, stats... Even then, those things are probably not changing values every frame.

You might want to take a look at the DirectX Took Kit which has a sprite batcher similar to what I'm describing.

So..Im the only one who have a single vertex buffer, normalized size (-0.5 to 0.5) that never leaves the slot?

I just scale it on the vertex shader, same thing with UVs.

Since size and uvs are commonly dynamic anyway, why bother with vertices?

Is that bad? Works fine to me. It makes more sense to use constant buffers for this kind of stuff to me. Codewise is also more clear.


//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
//Sprite HLSL V9
//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

//================================================================
//Shader Constant Buffers
//================================================================

cbuffer drawable : register(b0){
	float2 res	: packoffset(c0.x);
	float2 padding	: packoffset(c0.z);
	float4 uvRect	: packoffset(c1);
	matrix mWorld	: packoffset(c2);
	float4 color	: packoffset(c6);
}
cbuffer camera : register(b1){
	matrix mViewProjection;
}
//================================================================


//=================================================================
//Shader Types
//=================================================================
struct vs_out{
	float4 pos	: SV_POSITION;
	float4 color	: COLOR;
	float2 uv	: TEXTCOORD;
};
//=================================================================


//==================================================================
//VS
//==================================================================

vs_out vs_Sprite( float3 pos_p : POSITION, float2 uv_p : TEXCOORD ){

	vs_out output = (vs_out)0;

	//vertex scaling
	pos_p.xy *= res;
	pos_p.xy += padding.xy; // offset

	output.pos = mul( float4( pos_p, 1.0f ), mWorld );

	//camera(view) trafo:
	output.pos = mul( output.pos, mViewProjection ); 

	//uv offset:
	output.uv = uvRect.xy + (uv_p * uvRect.zw); //uv_p + uvOffset;

	// just pass color on:
	output.color = color;

	return output;	
}
//===================================================================

//=====================================================================
//Shader Resources
//=====================================================================
Texture2D tex2D : register(t0);
SamplerState samplerMode : register(s0);
//=====================================================================

//===================================================================
//PS
//===================================================================

float4 ps_Sprite( vs_out input_p ) : SV_TARGET{

	return tex2D.Sample( samplerMode, input_p.uv ) * input_p.color;
}
//==================================================================


And for Instancing:


...
//=================================================================
//Shader Types
//=================================================================
struct vs_out{
	...
};
struct vsInstance_In{
	float2 res	: INST_RES;
	float2 padding	: INST_PADD;
	float4 uvRect	: INST_TEXTCOORD;
	matrix mWorld	: INST_WORLD;
	float4 color	: INST_COLOR;
};
//=================================================================

//==================================================================
//VS
//==================================================================

vs_out vs_instancedSprite( float3 pos_p : POSITION, float2 uv_p : TEXCOORD, vsInstance_In instance_p ){

	vs_out output = (vs_out)0;
	
	//vertex scaling
	pos_p.xy *= instance_p.res;
	pos_p.xy += instance_p.padding.xy;
	
	//instance trafo
	output.pos = mul( float4( pos_p, 1.0f ), instance_p.mWorld );
	
	//camera(view) trafo:
	output.pos = mul( output.pos, mViewProjection );
	
	//uv offset:
	output.uv = instance_p.uvRect.xy + (uv_p * instance_p.uvRect.zw);
	
	// just pass stuff on:
	output.color = instance_p.color;
	
	return output;
}
//===================================================================
...

This topic is closed to new replies.

Advertisement