static void DrawVertexBuffer(Texture depthForParticlesRT, List<MyBillboard> billboards)
        {
            //  This is important for optimalization (although I don't know when it can happen to have zero billboards), but
            //  also that loop below needs it - as it assumes we are rendering at least one billboard.
            if (billboards.Count == 0)
                return;

            Device device = MyRender.GraphicsDevice;
            Surface oldTargets = null;

            DepthStencilState previousState = null;
            if (MyRender.Settings.VisualizeOverdraw)
            {
                oldTargets = device.GetRenderTarget(0);

                //We borrow lod0normals to render stencil
                MyRender.SetRenderTarget(MyRender.GetRenderTarget(MyRenderTargets.Auxiliary0), null);
                device.Clear(ClearFlags.Target | ClearFlags.Stencil, new ColorBGRA(0), 1.0f, 0);

                previousState = MyStateObjects.StencilMask_AlwaysIncrement_DepthStencilState;
                MyStateObjects.StencilMask_AlwaysIncrement_DepthStencilState.Apply();
            }
            else
            {
                previousState = DepthStencilState.None;
                DepthStencilState.None.Apply();
            }

            //  Draw particles without culling. It's because how we calculate left/up vector, we can have problems in back camera. (yes, that can be solved, but why bother...)
            //  Also I guess that drawing without culling may be faster - as GPU doesn't have to check it
            // No it's not, correct culling is faster: http://msdn.microsoft.com/en-us/library/windows/desktop/bb204882(v=vs.85).aspx
            RasterizerState.CullNone.Apply();

            MyEffectTransparentGeometry effect = MyRender.GetEffect(MyEffects.TransparentGeometry) as MyEffectTransparentGeometry;

            effect.SetWorldMatrix(Matrix.Identity);

            Matrix viewMatrix = MyRenderCamera.ViewMatrixAtZero;
            effect.SetViewMatrix(ref viewMatrix);

            effect.SetProjectionMatrix(ref MyRenderCamera.ProjectionMatrix);

            Viewport originalViewport = MyRender.GraphicsDevice.Viewport;

            effect.SetDepthsRT(depthForParticlesRT);
            effect.SetHalfPixel(depthForParticlesRT.GetLevelDescription(0).Width, depthForParticlesRT.GetLevelDescription(0).Height);
            effect.SetScale(MyRender.GetScaleForViewport(depthForParticlesRT));

            // Later we can interpolate between Main and Aux
            effect.SetEnvironmentMap(MyRender.GetRenderTargetCube(MyRenderTargets.EnvironmentCube));

            //For struct size checks
            //int stride = MyVertexFormatTransparentGeometry.VertexDeclaration.VertexStride;
            //int s = Marshal.SizeOf(new MyVertexFormatTransparentGeometry());


            //  We iterate over all sorted billboards, and seach for when texture/shader has changed.
            //  We try to draw as many billboards as possible (using the same texture), but because we are rendering billboards
            //  sorted by depth, we still need to switch sometimes. Btw: I have observed, that most time consuming when drawing particles
            //  is device.DrawUserPrimitives(), even if I call it for the whole list of billboards (without this optimization). I think, it's
            //  because particles are pixel-bound (I do a lot of light calculation + there is blending, which is always slow).
            MyTransparentMaterial lastMaterial = MyTransparentMaterials.GetMaterial(billboards[0].Material);
            MyBillboard lastBillboard = billboards[0];
            MyTransparentMaterial lastBlendMaterial = lastBillboard.BlendMaterial != null ? MyTransparentMaterials.GetMaterial(lastBillboard.BlendMaterial) : null;


            bool ignoreDepth = false;
            Matrix projectionMatrix = MyRenderCamera.ProjectionMatrix;
            Matrix invProjectionMatrix = Matrix.Invert(projectionMatrix);
            effect.SetInverseDefaultProjectionMatrix(ref invProjectionMatrix);

            if (lastBillboard.CustomViewProjection != -1)
            {
                SetupCustomViewProjection(effect, ref originalViewport, lastBillboard, ref ignoreDepth, ref projectionMatrix);
            }

            // 0.05% of billboard is blended
            const float softColorizeSize = 0.05f;

            device.VertexDeclaration = MyVertexFormatTransparentGeometry.VertexDeclaration;
            device.SetStreamSource(0, m_vertexBuffer, 0, MyVertexFormatTransparentGeometry.Stride);
            device.Indices = m_indexBuffer;

            MyRender.GetShadowRenderer().SetupShadowBaseEffect(effect);

            MyEffectTransparentGeometry effect2 = MyRender.GetEffect(MyEffects.TransparentGeometry) as MyEffectTransparentGeometry;
            effect2.SetShadowBias(0.001f);

            MyLights.UpdateEffectReflector(effect2.Reflector, false);
            MyLights.UpdateEffect(effect2, false);


            int geomCount = billboards.Count;
            int it = 0;
            int cnt = 0;

            while (geomCount > 0)
            {
                if (geomCount > RENDER_BUFFER_SIZE)
                {
                    geomCount -= RENDER_BUFFER_SIZE;
                    cnt = RENDER_BUFFER_SIZE;
                }
                else
                {
                    cnt = geomCount;
                    geomCount = 0;
                }

                int indexFrom = it * RENDER_BUFFER_SIZE + 1;
                cnt = cnt + indexFrom - 1;
                for (int i = indexFrom; i <= cnt; i++)
                {
                    //  We need texture from billboard that's before the current billboard (because we always render "what was")
                    MyBillboard billboard = billboards[i - 1];
                    MyTransparentMaterial blendMaterialProperties = billboard.BlendMaterial != null ? MyTransparentMaterials.GetMaterial(billboard.BlendMaterial) : MyTransparentMaterials.GetMaterial(billboard.Material);
                    MyTransparentMaterial lastBlendMaterialProperties = lastBlendMaterial == null ? blendMaterialProperties : lastBlendMaterial;

                    bool colorizeChanged = EnableColorize && lastBillboard.EnableColorize != billboard.EnableColorize;
                    bool nearChanged = lastBillboard.Near != billboard.Near;
                    bool sizeChanged = EnableColorize && billboard.EnableColorize && lastBillboard.Size != billboard.Size;
                    bool blendTextureChanged = false;
                    bool projectionChanged = lastBillboard.CustomViewProjection != billboard.CustomViewProjection;
                    bool cullStencilChanges = lastBillboard.CullWithStencil != billboard.CullWithStencil;

                    if (lastBlendMaterial != (billboard.BlendMaterial != null ? MyTransparentMaterials.GetMaterial(billboard.BlendMaterial) : null) && billboard.BlendTextureRatio > 0)
                    {
                        if ((lastBlendMaterialProperties.UseAtlas) && (blendMaterialProperties.UseAtlas))
                            blendTextureChanged = false;
                        else
                            blendTextureChanged = true;
                    }

                    //bool blendTextureChanged = lastBlendTexture != billboard.BlendTexture;
                    bool billboardChanged = colorizeChanged || sizeChanged || blendTextureChanged || nearChanged || projectionChanged || cullStencilChanges;

                    MyTransparentMaterial actMaterialProperties = MyTransparentMaterials.GetMaterial(billboard.Material);
                    MyTransparentMaterial lastMaterialProperties = lastMaterial;

                    billboardChanged |= (actMaterialProperties.CanBeAffectedByOtherLights != lastMaterialProperties.CanBeAffectedByOtherLights)
                                    || (actMaterialProperties.IgnoreDepth != lastMaterialProperties.IgnoreDepth);


                    if (projectionChanged)
                    {
                        SetupCustomViewProjection(effect, ref originalViewport, lastBillboard, ref ignoreDepth, ref projectionMatrix);
                    }

                    if (!billboardChanged)
                    {
                        if (MyTransparentMaterials.GetMaterial(billboard.Material) != lastMaterial)
                        {
                            if (actMaterialProperties.UseAtlas && lastMaterialProperties.UseAtlas)
                                billboardChanged = false;
                            else
                                billboardChanged = true;
                        }
                    }

                    //  If texture is different than the last one, or if we reached end of billboards
                    if ((i == cnt) || billboardChanged)
                    {
                        //  We don't need to do this when we reach end of billboards - it's needed only if we do next iteration of possible billboards
                        if ((i != cnt) || billboardChanged)
                        {
                            if ((i - indexFrom) > 0)
                            {
                                int firstIndex = (indexFrom - 1) * MyTransparentGeometryConstants.INDICES_PER_TRANSPARENT_GEOMETRY; //MyTransparentGeometryConstants.VERTICES_PER_TRANSPARENT_GEOMETRY;

                                SetupCustomViewProjection(effect, ref originalViewport, lastBillboard, ref ignoreDepth, ref projectionMatrix);

                                SetupEffect(ref lastMaterial, lastBlendMaterialProperties, EnableColorize && lastBillboard.EnableColorize, lastBillboard.Size * softColorizeSize, lastBillboard.Near, ignoreDepth, ref projectionMatrix);

                                effect.Begin();
                                DrawBuffer(firstIndex, (i - indexFrom) * MyTransparentGeometryConstants.TRIANGLES_PER_TRANSPARENT_GEOMETRY);
                                effect.End();

                                MyPerformanceCounter.PerCameraDrawWrite.BillboardsDrawCalls++;
                            }

                            lastMaterial = MyTransparentMaterials.GetMaterial(billboard.Material);
                            lastBillboard = billboard;
                            lastBlendMaterial = billboard.BlendMaterial != null ? MyTransparentMaterials.GetMaterial(billboard.BlendMaterial) : null;
                            indexFrom = i;
                        }


                        if ((i == cnt) && (i - indexFrom + 1 != 0))
                        {
                            lastMaterial = MyTransparentMaterials.GetMaterial(lastBillboard.Material);
                            blendMaterialProperties = lastBillboard.BlendMaterial == null ? MyTransparentMaterials.GetMaterial(lastBillboard.Material) : MyTransparentMaterials.GetMaterial(lastBillboard.BlendMaterial);
                            int firstIndex = (indexFrom - 1) * MyTransparentGeometryConstants.INDICES_PER_TRANSPARENT_GEOMETRY;

                            SetupCustomViewProjection(effect, ref originalViewport, lastBillboard, ref ignoreDepth, ref projectionMatrix);

                            SetupEffect(ref lastMaterial, blendMaterialProperties, EnableColorize && billboard.EnableColorize, lastBillboard.Size * softColorizeSize, lastBillboard.Near, ignoreDepth, ref projectionMatrix);

                            if (billboard.CullWithStencil)
                            {
                                DepthStencilState.BackgroundObjects.Apply();
                            }
                            effect.Begin();
                            DrawBuffer(firstIndex, (i - indexFrom + 1) * MyTransparentGeometryConstants.TRIANGLES_PER_TRANSPARENT_GEOMETRY);
                            effect.End();

                            if (billboard.CullWithStencil)
                            {
                                previousState.Apply();
                            }
                            MyPerformanceCounter.PerCameraDrawWrite.BillboardsDrawCalls++;
                        }
                    }
                }

                it++;
            }

            device.SetStreamSource(0, null, 0, 0);

            MyPerformanceCounter.PerCameraDrawWrite.BillboardsInFrustum += billboards.Count;

            // Visualize overdraw of particles. More overdraws = bigger performance issue.
            if (MyRender.Settings.VisualizeOverdraw)
            {
                if (m_overDrawColorsAnim == null)
                {
                    m_overDrawColorsAnim = new MyAnimatedPropertyVector4();
                    m_overDrawColorsAnim.AddKey(0.0f, new Vector4(0.0f, 1.0f, 0.0f, 1.0f));
                    m_overDrawColorsAnim.AddKey(0.25f, new Vector4(1.0f, 1.0f, 0.0f, 1.0f));
                    m_overDrawColorsAnim.AddKey(0.75f, new Vector4(0.0f, 0.0f, 1.0f, 1.0f));
                    m_overDrawColorsAnim.AddKey(1.0f, new Vector4(1.0f, 0.0f, 0.0f, 1.0f));
                }

                //Space without particles is black
                device.Clear(ClearFlags.Target, new ColorBGRA(0), 1.0f, 0);


                for (int referenceStencil = 1; referenceStencil < PARTICLES_OVERDRAW_MAX; referenceStencil++)
                {
                    DepthStencilState ds = new DepthStencilState()
                    {
                        StencilEnable = true,
                        ReferenceStencil = referenceStencil,
                        StencilFunction = Compare.LessEqual,
                    };

                    ds.Apply(false);


                    float diff = (float)(referenceStencil - 1) / (PARTICLES_OVERDRAW_MAX - 1);
                    Vector4 referenceColorV4;
                    m_overDrawColorsAnim.GetInterpolatedValue<Vector4>(diff, out referenceColorV4);
                    Color referenceColor = new Color(referenceColorV4);

                    MyRender.BeginSpriteBatch(BlendState.Opaque, null, null);
                    MyRender.DrawSprite(MyRender.BlankTexture, new Rectangle(0, 0, MyRenderCamera.Viewport.Width, MyRenderCamera.Viewport.Height), referenceColor);
                    MyRender.EndSpriteBatch();
                }

                DepthStencilState.None.Apply();

                int leftStart = MyRenderCamera.Viewport.Width / 4;
                int topStart = (int)(MyRenderCamera.Viewport.Height * 0.75f);

                int size = MyRenderCamera.Viewport.Width - 2 * leftStart;
                int sizeY = (int)(MyRenderCamera.Viewport.Width / 32.0f);
                int sizeStep = size / PARTICLES_OVERDRAW_MAX;

                MyRender.BeginSpriteBatch(null, null, null);

                for (int i = 0; i < PARTICLES_OVERDRAW_MAX; i++)
                {
                    float diff = (float)(i - 1) / (PARTICLES_OVERDRAW_MAX - 1);
                    Vector4 referenceColorV4;
                    m_overDrawColorsAnim.GetInterpolatedValue<Vector4>(diff, out referenceColorV4);
                    Color referenceColor = new Color(referenceColorV4);

                    MyRender.DrawSprite(MyRender.BlankTexture, new Rectangle(leftStart + i * sizeStep, topStart, sizeStep, sizeY), referenceColor);
                }

                MyDebugDraw.DrawText(new Vector2((float)leftStart, (float)(topStart + sizeY)), new System.Text.StringBuilder("1"), Color.White, 1.0f, false);
                MyDebugDraw.DrawText(new Vector2((float)leftStart + size, (float)(topStart + sizeY)), new System.Text.StringBuilder(">" + PARTICLES_OVERDRAW_MAX.ToString()), Color.White, 1.0f, false);

                MyRender.EndSpriteBatch();

                device.SetRenderTarget(0, oldTargets);
                oldTargets.Dispose();

                MyRender.Blit(MyRender.GetRenderTarget(MyRenderTargets.Auxiliary0), false);
            }

            //  Restore to 'opaque', because that's the usual blend state
            BlendState.Opaque.Apply();
        }
 internal static void BeginSpriteBatch(BlendState blendState, DepthStencilState depthState, RasterizerState rasterizerState)
 {
     if (m_spriteBatchUsageCount == 0)
     {
         //  Deferred means that draw call will be send to GPU not on every Draw(), but only at the End() or if we change
         //  a texture between Begin/End. It's faster than Immediate mode.
         m_spriteBatch.Begin(SpriteSortMode.Deferred,
                             blendState,
                             VRageRender.Graphics.SamplerState.LinearClamp,
                             depthState,
                             rasterizerState);
     }
     m_spriteBatchUsageCount++;
 }