   [C#] [HLSL] Please criticise my quadrangle instancing technique.
  I've just put together my take on quadrangle instancing.  With this method I can batch draw 1000s of quadrangles providing they all have the same texture.The quadrangles are created as the following and stored in a resource pool.  There's more to the class that allows me to change parameters such as dimensions, uv coordinates and normal etc,   public Quadrangle()   {  // Create an array to store vertices in world space  vertices = new VertexQuadrangleInstancing[4];  position = Vector3.Zero;  width = 1.0f;  height = 1.0f;  // XNA uses the following from rotation matrix (Clockwise Winding)  // Right = (1,0,0)  // Up = (0,1,0)  // Backward = Normal = (0,0,1)  rotation = Matrix.Identity;   // Fill drawable vertex format with the new parameter info  Vector3 normal = rotation.Backward;  Vector4 instanceData0 = new Vector4();  instanceData0.X = position.X;  instanceData0.Y = position.Y;  instanceData0.Z = position.Z;  instanceData0.W = rotation.Right.X;  Vector4 instanceData1 = new Vector4();  instanceData1.X = rotation.Right.Y;  instanceData1.Y = rotation.Right.Z;  instanceData1.Z = rotation.Up.X;  instanceData1.W = rotation.Up.Y;  Vector4 instanceData2 = new Vector4();  instanceData2.X = rotation.Up.Z;  instanceData2.Y = rotation.Backward.X;  instanceData2.Z = rotation.Backward.Y;  instanceData2.W = rotation.Backward.Z;  Vector3 instanceData3 = new Vector3();  instanceData3.X = width;  instanceData3.Y = height;  vertices[0] = new VertexQuadrangleInstancing(corners[0], normal, Settings.UV_LL, instanceData0, instanceData1, instanceData2, instanceData3);  vertices[1] = new VertexQuadrangleInstancing(corners[1], normal, Settings.UV_UL, instanceData0, instanceData1, instanceData2, instanceData3);  vertices[2] = new VertexQuadrangleInstancing(corners[2], normal, Settings.UV_LR, instanceData0, instanceData1, instanceData2, instanceData3);  vertices[3] = new VertexQuadrangleInstancing(corners[3], normal, Settings.UV_UR, instanceData0, instanceData1, instanceData2, instanceData3);  recreateWorld = false;   }The HLSL code is as follows://=============================================//---[XNA to HLSL Variables]-------------------//=============================================shared float4x4 ViewProjection;texture Texture0;texture Texture1;//=============================================//---[HLSL Variables]--------------------------//=============================================float4x4 Rotation ={1, 0, 0, 0,0, 1, 0, 0,0, 0, 1, 0,0, 0, 0, 1};float4x4 Translation ={1, 0, 0, 0,0, 1, 0, 0,0, 0, 1, 0,0, 0, 0, 1};//=============================================//---[Texture Samplers]------------------------//=============================================sampler TextureSampler0 = sampler_state   {  Texture = <Texture0>;    MinFilter = None;  MagFilter = None;  MipFilter = None;    AddressU = Wrap; AddressV = Wrap;};sampler TextureSampler1 = sampler_state   {  Texture = <Texture1>;    MinFilter = None;  MagFilter = None;  MipFilter = None;    AddressU = Wrap;    AddressV = Wrap;};//=============================================//---[Structs]---------------------------------//=============================================struct VertexShaderInput{    float4 Position: POSITION0;    float2 TextureCoordinates: TEXCOORD0;    float4 IData0: TEXCOORD1;// Translation/Rotation    float4 IData1: TEXCOORD2;// Rotation    float4 IData2: TEXCOORD3;// Rotation    float3 IData3: TEXCOORD4;// Scale    };struct VertexShaderOutput{    float4 Position: POSITION0;   float2 TextureCoordinates: TEXCOORD0;};//=============================================//---[Vertex Shaders]--------------------------//=============================================VertexShaderOutput VertexShaderFunction(VertexShaderInput input){    VertexShaderOutput Output = (VertexShaderOutput)0;//The zero-based row-column position: //_m00, _m01, _m02, _m03//_m10, _m11, _m12, _m13//_m20, _m21, _m22, _m23//_m30, _m31, _m32, _m33//The one-based row-column position: //_11, _12, _13, _14//_21, _22, _23, _24//_31, _32, _33, _34//_41, _42, _43, _44//[0][0], [0][1], [0][2], [0][3]//[1][0], [1][1], [1][2], [1][3]//[2][0], [2][1], [2][2], [2][3]//[3][0], [3][1], [3][2], [3][3]Translation._41 = input.IData0.x;Translation._42 = input.IData0.y;Translation._43 = input.IData0.z;Rotation._11 = input.IData0.w * input.IData3.x;Rotation._12 = input.IData1.x * input.IData3.x;Rotation._13 = input.IData1.y * input.IData3.x;Rotation._21 = input.IData1.z * input.IData3.y;Rotation._22 = input.IData1.w * input.IData3.y;Rotation._23 = input.IData2.x * input.IData3.y;Rotation._31 = input.IData2.y;Rotation._32 = input.IData2.z;Rotation._33 = input.IData2.w;float4x4 world = mul(Rotation, Translation);float4x4 worldViewProj = mul(world, ViewProjection);    Output.Position = mul(input.Position, worldViewProj);    Output.TextureCoordinates = input.TextureCoordinates;    return Output;}//=============================================//---[Pixel Shaders]---------------------------//=============================================float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0{return tex2D(TextureSampler0, input.TextureCoordinates);}//=============================================//---[Techniques]------------------------------//=============================================technique Textured{    pass Pass0    {    AlphaBlendEnable = false;// No transparencyZEnable = true;ZWriteEnable = true;   VertexShader = compile vs_1_1 VertexShaderFunction();   PixelShader = compile ps_1_1 PixelShaderFunction();    }}technique Textured_Transparent{    pass Pass0    {AlphaBlendEnable = true;// Turn on Alpha BlendingZEnable = true;ZWriteEnable = true;SrcBlend = One;// Additive BlendingDestBlend = One;// Additive BlendingBlendOp = Add;// Additive Blending   VertexShader = compile vs_1_1 VertexShaderFunction();   PixelShader = compile ps_1_1 PixelShaderFunction();    }}Everything appears to work great, but can those with more experience in this area (i.e. most of you) please point out any pitfalls or inadequacies with this method?Thanks.   
  I fail to see how this allows you to do instancing. I'm probably totally missing something.How are you drawing these quads?Generally to perform instancing you use two vertex streams, one with your vertex data (4 vertices for a quad), the other with your instance data (whatever you want).   ;  Hmmm, perhaps instancing is the incorrect term.With this method I can batch draw 1000s of quads in one draw call as opposed to one per draw call.  Would this be faster or slower than instancing given that only one draw call is needed per texture?   ;  Instancing is the correct term, I'm just failing to see how it can be achieved with what you've posted, like I said, post up some of your drawing code and the code for when you fill your vertex buffer and I'm sure it will become clear.   ;  Here's the draw code:   private void DrawQuadrangleBatch()   {  int quad_Count = list_Quadrangle.Count;   if (quad_Count == 0)  { return;  }  // All quads use Matrix.Identity for world transform as they are already pre transformed  effect_QuadrangleBatch.Parameters["Texture0"].SetValue(texture_Wall_16x16);  // Quadrangles have 6 indices (TriangleList)  int[] quad_Indices = new int[quad_Count * 6];  // Quadrangles have 4 vertices  VertexQuadrangleInstancing[] quad_Vertices = new VertexQuadrangleInstancing[quad_Count * 4];  int index_Index = 0;  int vertex_Index = 0;  for (int i = 0; i < quad_Count; ++i)  { Quadrangle quad = list_Quadrangle; quad_Indices[index_Index++] = 0 + vertex_Index; quad_Indices[index_Index++] = 1 + vertex_Index; quad_Indices[index_Index++] = 2 + vertex_Index; quad_Indices[index_Index++] = 1 + vertex_Index; quad_Indices[index_Index++] = 3 + vertex_Index; quad_Indices[index_Index++] = 2 + vertex_Index; quad_Vertices[vertex_Index++] = quad.Vertices[0]; quad_Vertices[vertex_Index++] = quad.Vertices[1]; quad_Vertices[vertex_Index++] = quad.Vertices[2]; quad_Vertices[vertex_Index++] = quad.Vertices[3];  }  effect_QuadrangleBatch.Begin();  effect_QuadrangleBatch.CurrentTechnique.Passes[0].Begin();  // Divide the Index count by 3 because 3 Indices make 1 Triangle   // TriangleStrip can't be used as quads aren't always joined together  GraphicsDevice.VertexDeclaration = vd_VertexQuadrangleInstancing;  GraphicsDevice.DrawUserIndexedPrimitives<VertexQuadrangleInstancing>( PrimitiveType.TriangleList, quad_Vertices, 0, quad_Vertices.Length, quad_Indices, 0, quad_Indices.Length / 3 );  effect_QuadrangleBatch.CurrentTechnique.Passes[0].End();  effect_QuadrangleBatch.End();  // Clear the quadrilateral list for this frame now the quad has been drawn  //list_Quadrangle.Clear();   }Apologies, but it didn't occur to me to post it.   ;  That's more of a batching process than an instancing process, but it gets the job done the same way.If you are using DX10/11, use the DrawInstanced method instead, you can pass in the transforms as a seperate array to the quad (So the quad only exists as 4 verts, and the transforms only exist once per instance, not 4)The other one is, depending on your platform you can get the vertex index in as your vertex shader input and access the attribute streams directly by index - allowing you to do the standard (i % vertCount, i / vertCount) way of getting vertex number and instance number by having an index buffer that loop beyond the number of verts.   ;Quote:Original post by ExorcistIf you are using DX10/11, use the DrawInstanced method instead, you can pass in the transforms as a seperate array to the quad (So the quad only exists as 4 verts, and the transforms only exist once per instance, not 4)I'm stuck with DX9 because I'm using XNA. :(If I batch draw 8000 quads (using my method) then the FPS is around 127, which is way too low compared to another instancing method which has FPS of around 1175.I've tried using a vertex buffer and index buffer and changing the indices to shorts as opposed to ints:  // Quadrangles have 6 indices (TriangleList)  short[] quad_Indices = new short[quad_Count * 6];  // Quadrangles have 4 vertices  VertexQuadBatch[] quad_Vertices = new VertexQuadBatch[quad_Count * 4];  int index_Index = 0;  int vertex_Index = 0;  for (int i = 0; i < quad_Count; ++i)  { Quadrangle quad = list_Quadrangle; quad_Indices[index_Index++] = (short)(0 + vertex_Index); quad_Indices[index_Index++] = (short)(1 + vertex_Index); quad_Indices[index_Index++] = (short)(2 + vertex_Index); quad_Indices[index_Index++] = (short)(1 + vertex_Index); quad_Indices[index_Index++] = (short)(3 + vertex_Index); quad_Indices[index_Index++] = (short)(2 + vertex_Index); quad_Vertices[vertex_Index++] = quad.Vertices[0]; quad_Vertices[vertex_Index++] = quad.Vertices[1]; quad_Vertices[vertex_Index++] = quad.Vertices[2]; quad_Vertices[vertex_Index++] = quad.Vertices[3];  }  VertexBuffer vb = new VertexBuffer(GraphicsDevice, quad_Vertices.Length * VertexQuadBatch.SizeInBytes, BufferUsage.None);  vb.SetData<VertexQuadBatch>(quad_Vertices);  IndexBuffer ib = new IndexBuffer(GraphicsDevice, quad_Indices.Length * 2, BufferUsage.None, IndexElementSize.SixteenBits);  ib.SetData<short>(quad_Indices);  GraphicsDevice.VertexDeclaration = vd_VertexQuadrangleInstancing;  GraphicsDevice.Vertices[0].SetSource(vb, 0, VertexQuadBatch.SizeInBytes);  GraphicsDevice.Indices = ib;  effect_QuadrangleBatch.Begin();  effect_QuadrangleBatch.CurrentTechnique.Passes[0].Begin();  // Divide the Index count by 3 because 3 Indices make 1 Triangle   // TriangleStrip can't be used as quads aren't always joined together  GraphicsDevice.DrawIndexedPrimitives( PrimitiveType.TriangleList, 0, 0, quad_Vertices.Length, 0, quad_Indices.Length / 3 );  effect_QuadrangleBatch.CurrentTechnique.Passes[0].End();  effect_QuadrangleBatch.End();This gives me 3 more FPS but I not sure why this method isn't as fast?!  Can anyone suggest why?   ;  Actually it's slow because you refill the vertex buffer every frame and upload it to the video memory which is a slow process.what they do with instancing is, they have 1 object en that is rendered n times to the screen using different settings that are stored in for example a vertex stream.Some information about instancing and the techniques. I thing MSDN (DX9 instancing) will also be do-able in XNA...http://www.gamerendering.com/2008/10/21/instancing/   ;  To my knowledge, instancing has a certain per-instance overhead on any current graphics hardware. Therefore instancing is slower for very small objects (like a quad). Filling a dynamic vertex buffer with all the quads and is the standard way to render lots of quads. Using DX10 technology you can use alternative paths that might gain some performance. You can use a geometry shader to generate the quad, reducing the amount of dynamic vertex data to a single vertex per quad. You can use the vertex or primitive ID to fetch the instance data from a constant buffer / a texture, further reducing the amount of transferred data. But at the end you have to transfer some data. These are only different ways how to tell the GPU where you want your quads to show up and what segment of the texture should appear on them. And (usually) a bit of rotation information, scaling and colorization.   ;Quote:Original post by Spa8nkyQuote:Original post by ExorcistIf you are using DX10/11, use the DrawInstanced method instead, you can pass in the transforms as a seperate array to the quad (So the quad only exists as 4 verts, and the transforms only exist once per instance, not 4)I'm stuck with DX9 because I'm using XNA. :(If I batch draw 8000 quads (using my method) then the FPS is around 127, which is way too low compared to another instancing method which has FPS of around 1175.I've tried using a vertex buffer and index buffer and changing the indices to shorts as opposed to ints:*** Source Snippet Removed ***This gives me 3 more FPS but I not sure why this method isn't as fast?!  Can anyone suggest why?Here you're creating a new Vertex and IndexBuffer each frame, which is VERY bad for performance.What you need to do is create a DynamicVertexBuffer and DynamicIndexBuffer once (during LoadContent) and then update them once per frame, as opposed to recreating them.---As other people have already said, what you have here is not really instancing. You're not far off having shader instancing setup though. Although I'm not sure that would give you any extra performance.You could go for full on hardware instancing, but I think that ties you to SM3.0 hardware and above only.   