Hardware Instancing / Shader

i need some help on my hardware instancing
i just copied the solution from this New to 3D - instancing models for drawing

what i get is this:

some code:

        foreach (InstancedStaticActor actor in actors.Values)
        {
            if (actor.InstanceBuffer == null) continue;

            Model model = actor.Model;
            Matrix[] modelBones = new Matrix[model.Bones.Count];
            model.CopyAbsoluteBoneTransformsTo(modelBones);

            foreach (var mesh in model.Meshes)
            {
                foreach (var meshPart in mesh.MeshParts)
                {
                    GraphicsDevice.SetVertexBuffers(new VertexBufferBinding(meshPart.VertexBuffer, meshPart.VertexOffset, 0),
                        new VertexBufferBinding(actor.InstanceBuffer, 0, actor.Count));

                    GraphicsDevice.Indices = meshPart.IndexBuffer;

                    Effect effect = instanceeffect;
                    effect.CurrentTechnique = effect.Techniques["Instancing"];

                    effect.Parameters["Texture"].SetValue(((BasicEffect)model.Meshes[0].Effects[0]).Texture);
                    effect.Parameters["World"].SetValue(world * modelBones[mesh.ParentBone.Index]);
                    effect.Parameters["View"].SetValue(Scen.Camera.View);
                    effect.Parameters["Projection"].SetValue(Scen.Camera.Projection);
                    effect.Parameters["Color"].SetValue(Vector4.One);

                    foreach (var pass in effect.CurrentTechnique.Passes)
                    {
                        pass.Apply();
                        GraphicsDevice.DrawInstancedPrimitives(PrimitiveType.TriangleList, 0, meshPart.StartIndex, meshPart.PrimitiveCount, actor.Count);
                    }
                }
            }
        }

srly i dont get it …

my shader code
#if OPENGL
#define SV_POSITION POSITION
#define VS_SHADERMODEL vs_3_0
#define PS_SHADERMODEL ps_3_0
#else
#define VS_SHADERMODEL vs_4_0_level_9_1
#define PS_SHADERMODEL ps_4_0_level_9_1
#endif

// Camera settings.
float4x4 WorldViewProjection;
float4x4 World;
float4x4 View;
float4x4 Projection;
float4x4 Model;

// This sample uses a simple Lambert lighting model.
float4 LightDirection;
float4 DiffuseLight;
float4 AmbientLight;
float4 Color;

Texture2D Texture;

sampler CustomSampler = sampler_state
{
Texture = (Texture);
};

struct VertexShaderInput
{
float4 Position : POSITION;
float3 Normal : NORMAL0;
float2 TextureCoordinate : TEXCOORD0;
};

struct VertexShaderOutput
{
float4 Position : SV_POSITION;
float4 Color : COLOR0;
float2 TextureCoordinate : TEXCOORD0;
};

VertexShaderOutput VertexShaderFunction(VertexShaderInput input, float4x4 transform : BLENDWEIGHT)
{
VertexShaderOutput output;

// Apply the world and camera matrices to compute the output position.
float4 position = input.Position;
position = mul(position, Model);
position = mul(position, transpose(transform));
position = mul(position, World);
position = mul(position, View);
position = mul(position, Projection);

output.Color = Color;
output.Position = position;
output.TextureCoordinate = input.TextureCoordinate;
return output;

}

float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0
{
//return tex2D(CustomSampler, input.TextureCoordinate) * input.Color;
return input.Color;
}

// Hardware instancing technique.
technique Instancing
{
pass p0
{
VertexShader = compile VS_SHADERMODEL VertexShaderFunction();
PixelShader = compile PS_SHADERMODEL PixelShaderFunction();
}
}

        foreach (InstancedStaticActor actor in actors.Values)
        {
            if (actor.InstanceBuffer == null) continue;

            Model model = actor.Model;
            Matrix[] modelBones = new Matrix[model.Bones.Count];
            model.CopyAbsoluteBoneTransformsTo(modelBones);

            foreach (var mesh in model.Meshes)
            {
                foreach (var meshPart in mesh.MeshParts)
                {
                    GraphicsDevice.SetVertexBuffers(new VertexBufferBinding(meshPart.VertexBuffer, meshPart.VertexOffset, 0),
                        new VertexBufferBinding(actor.InstanceBuffer, 0, actor.Count));
                    GraphicsDevice.Indices = meshPart.IndexBuffer;

                    Effect effect = instanceeffect;
                    effect.Parameters["World"].SetValue(world);
                    effect.Parameters["View"].SetValue(Scen.Camera.View);
                    effect.Parameters["Projection"].SetValue(Scen.Camera.Projection);
                    effect.Parameters["Model"].SetValue(modelBones[mesh.ParentBone.Index]);
                    //effect.Parameters["WorldViewProjection"].SetValue(worldviewprojection);
                    //effect.Parameters["Texture"].SetValue(((BasicEffect)meshPart.Effect).Texture);
                    effect.Parameters["Color"].SetValue(Vector4.One);

                    foreach (var pass in effect.Techniques["Instancing"].Passes)
                    {
                        pass.Apply();
                        GraphicsDevice.DrawInstancedPrimitives(PrimitiveType.TriangleList, 0, meshPart.StartIndex, meshPart.PrimitiveCount, actor.Count);
                    }
                }
            }
        }

struct InstancedModelVertex : IVertexType
{
    private Matrix transform;
    public static VertexDeclaration Declaration { get; } = new VertexDeclaration(
        new VertexElement(0 * 4 * sizeof(float), VertexElementFormat.Vector4, VertexElementUsage.BlendWeight, 0),
        new VertexElement(1 * 4 * sizeof(float), VertexElementFormat.Vector4, VertexElementUsage.BlendWeight, 1),
        new VertexElement(2 * 4 * sizeof(float), VertexElementFormat.Vector4, VertexElementUsage.BlendWeight, 2),
        new VertexElement(3 * 4 * sizeof(float), VertexElementFormat.Vector4, VertexElementUsage.BlendWeight, 3));

    public VertexDeclaration VertexDeclaration => Declaration;
    public Matrix Transform { get => transform; set => transform = value; }

    public InstancedModelVertex(Matrix transform)
    {
        this.transform = transform;
    }
}

I think you tested that too?


(and copy’n’paste pastebin code and use big model).
It works and I did take some code of it.

On my little engine, I had a problem when I render my scene first and then instances, my instances were broken (long polys to every direction).
It works on FNA so might be MG bug (havent yet put issue to github).

This is from my benchmark test:
`

    public override void Render(GameTime gameTime)
    {
        Globals.GraphicsDevice.Clear(Color.Blue);

        //scene.Render(); // if MG, this here brokes instance renderes  (somemodel.Draw() brokes)
        if (mdlInstances != null)
            mdlInstances.Render(camera, scene.environment);
        scene.Render(); // if MG, one can render all models after instances

        string str = "Models: " + Globals.ModelCounter + "\nDrawcalls: " + Globals.DrawCallCounter;
        str += "\n\ncampos: " + camera.GetPosition().ToString();
        Globals.SpriteBatch.Begin();
        Globals.SpriteBatch.DrawString(Globals.Font, str, new Vector2(5, 5), Color.White);
        Globals.SpriteBatch.End();

        FrameRateCounter.Draw(gameTime);

        base.Render(gameTime);

    }

`

EDIT: somemodel.Draw(); I mean model’s meshpart.Draw(); xna/mg own Draw() method which maybe leaves some states on, dont know.

Some code from my engine’s here and there:

`

when creating/updating instances:

   if (instanceBuffer == null)
        {
            VertexElement[] instanceStreamElements = new VertexElement[2];
            instanceStreamElements = new VertexElement[4];
            instanceStreamElements[0] = new VertexElement(0, VertexElementFormat.Vector4, VertexElementUsage.Position, 1);
            instanceStreamElements[1] = new VertexElement(sizeof(float) * 4, VertexElementFormat.Vector4, VertexElementUsage.Position, 2);
            instanceStreamElements[2] = new VertexElement(sizeof(float) * 8, VertexElementFormat.Vector4, VertexElementUsage.Position, 3);
            instanceStreamElements[3] = new VertexElement(sizeof(float) * 12, VertexElementFormat.Vector4, VertexElementUsage.Position, 4);
            instanceVertexDeclaration = new VertexDeclaration(instanceStreamElements);

        }
        else instanceBuffer.Dispose();

        instanceBuffer = new DynamicVertexBuffer(Globals.GraphicsDevice, instanceVertexDeclaration, numOfInstances, BufferUsage.WriteOnly);
        instanceBuffer.SetData(instances, 0, numOfInstances, SetDataOptions.Discard);
        instanceBufferBinding = new VertexBufferBinding(instanceBuffer, 0, 1);

render:

   public void Render(Camera camera, Environment environment)
    {
        if (numOfInstances > 0)
        {
            int meshIndex = -1;

            model.CopyAbsoluteBoneTransformsTo(transforms);
            Matrix worldPos = CalcWorldMatrix();
            foreach (ModelMesh mesh in model.Meshes)
            {
                meshIndex++;
                Matrix pos = transforms[mesh.ParentBone.Index] * worldPos;

                foreach (ModelMeshPart meshPart in mesh.MeshParts)
                {
                    BaseEffect effect = Material.GetMeshMaterial(meshIndex).effect;
                    effect.SetParameters(camera, environment, Material, pos, meshIndex);

                    Globals.GraphicsDevice.Indices = meshPart.IndexBuffer;
                    Globals.GraphicsDevice.SetVertexBuffers(
                        new VertexBufferBinding(meshPart.VertexBuffer, meshPart.VertexOffset, 0),
                        instanceBufferBinding
                    );

                    // Draw all the instance copies in a single call.
                    effect.GetEffect().CurrentTechnique.Passes[0].Apply();
                    Globals.GraphicsDevice.DrawInstancedPrimitives(PrimitiveType.TriangleList, 0,
                                                               0, meshPart.NumVertices,//xna/fna
                                                               meshPart.StartIndex,
                                                               meshPart.PrimitiveCount, numOfInstances);
                    Globals.DrawCallCounter++;
                }
            }

            Globals.ModelCounter += numOfInstances;
            return;
        }
    else 
        ...

in shader:

`

struct VS_INPUT_TEX
{
float4 Position : POSITION0;
float3 Normal : NORMAL;
float2 Texcoord : TEXCOORD0;
};
struct VS_OUTPUT_TEX
{
float4 Position : POSITION0;
float2 Texcoord : TEXCOORD0;
};

VS_OUTPUT_TEX VertexShaderNoShadedInstanced(VS_INPUT_TEX Input, float4x4 instanceTransform : POSITION1)
{
VS_OUTPUT_TEX Output;
float4x4 ins = mul(World, transpose(instanceTransform));
float4 worldPosition = mul(Input.Position, ins);
float4 viewPosition = mul(worldPosition, View);

Output.Position = mul(viewPosition, Projection);
Output.Texcoord = Input.Texcoord;
return Output;

}

`

yea u are right, that was my mistake.

That .Draw() was before instancing? Then it is indeed a bug (and not only my engine’s bug).

no i instanced and drawed like i showed it top but with many chunks so i was switching between normal draw calls and instanced draw calls. actually its not rendering artefacts anymore, but only one instance :smiley:

Good you got it working :slight_smile:

I will make minimal test to check if meshPart.Draw() before instancing brokes models, because it should not to do it (and if it still brokes models, I will make issue to github with that test).

i would not say i solved it or got it to work completly. its only drawing one instance out of 64 atm.
i have to correct myself… actually all 64 models drawn on the same spot! but why?

stupid thing again… leason learned :smiley:

changed:
GraphicsDevice.SetVertexBuffers(new VertexBufferBinding(meshPart.VertexBuffer, meshPart.VertexOffset, 0),
new VertexBufferBinding(actor.InstanceBuffer, 0, actor.Count));
to:
GraphicsDevice.SetVertexBuffers(new VertexBufferBinding(meshPart.VertexBuffer, meshPart.VertexOffset, 0),
new VertexBufferBinding(actor.InstanceBuffer, 0, 1));