[.net] How to make my material tree more efficient?

Started by
4 comments, last by sjelkjd 18 years, 1 month ago
Hey there everyone. I've just been working on state management in my team's game engine and we're using .NET 2.0 as our framework (obviously). The system uses an abstract class to represent a state change, and then these are linked together into a tree to create the scene representation. Everything seems to be working well at the moment - it's doing whats expected, but I'm sure it can do it (the traversal mainly) faster than it is at the moment. So I present to you, my material system code. The first snippet is my abstract state change class. This is most of the meat on the state changes themselves.

    abstract class StateChange
    {
        protected StateChange parent;
        protected List<StateChange> children = new List<StateChange>();
        protected List<SceneEntity> entities = new List<SceneEntity>();
        protected StateManager manager;

        public StateManager StateManager
        {
            get { return manager; }
            set { manager = value; }
        }
        internal List<StateChange> Children
        {
            get { return children; }
        }
        internal List<SceneEntity> Entities
        {
            get { return entities; }
        }
        public StateChange Parent
        {
            get { return parent; }
            set
            {
                value.AddChild(this);
            }
        }

        public void AddChild(StateChange child)
        {
            // First, check that the child did not previously have a parent, and if so - remove the child from the parent
            if (child.parent != null)
            {
                child.parent.RemoveChild(child);
            }

            // Now add the child, and set its parent.
            if (!children.Contains(child))
            {
                children.Add(child);
                child.parent = this;
            }
        }
        private void RemoveChild(StateChange child)
        {
            if (children.Contains(child))
            {
                children.Remove(child);
                child.parent = null;
            }
            else
                throw new NullReferenceException("Unable to remove a state change that is not a child of this parent");
        }

        public void AddEntity(SceneEntity entity)
        {
            if(!entities.Contains(entity))
                entities.Add(entity);
        }

        public abstract void Apply();
    }

An example implemtation of this is:

    class ShaderTexture : StateChange
    {
        private string parameter;
        private Microsoft.DirectX.Direct3D.Texture texture;

        public Microsoft.DirectX.Direct3D.Texture Texture
        {
            get { return texture; }
            set { texture = value; }
        }
        public string Parameter
        {
            get { return parameter; }
            set { parameter = value; }
        }

        public ShaderTexture(string parameter, Microsoft.DirectX.Direct3D.Texture texture)
        {
            this.parameter = parameter;
            this.texture = texture;
        }

        public override void Apply()
        {
            manager.ActiveShader.SetVariable(parameter, texture);
        }
    }
}

And here is how I'm traversing the scene:

    internal class StateTraverser
    {
        RenderVisitor renderVisitor = new RenderVisitor();
        Matrix view, projection;

        internal void Enter(StateChange change)
        {
            view = Engine.Active.SceneManager.ActiveCamera.View;
            projection = Engine.Active.SceneManager.ActiveCamera.Projection;

            VisitNode(change);
        }

        void VisitNode(StateChange change)
        {
            change.Apply();

            foreach (StateChange sc in change.Children)
                VisitNode(sc);

            foreach (SceneEntity e in change.Entities)
                if (e.IsVisible)
                {
                    Engine.Active.GraphicsDevice.States.ActiveShader.Update(e.WorldMatrix * Engine.Active.SceneManager.RootWorld, view, projection);
                    Engine.Active.GraphicsDevice.States.ActiveShader.Direct3DEffect.CommitChanges();
                    e.Accept(renderVisitor);
                }
        }
    }

Now my main concerns are with copying data and stuff. I think when I do "foreach SceneEntity e in change.Entities" there's some type of copy happening. Sure I read something like that somewhere. Anyway, any ideas are appreciated!
Ollie"It is better to ask some of the questions than to know all the answers." ~ James Thurber[ mdxinfo | An iridescent tentacle | Game design patterns ]
Advertisement
As far as I can remember the foreach construct creates an instance of an enumerator, which contains an array of pointers to each of the items in the collection at the point the foreach block is commenced.

I suppose this construction of the enumerator could be said to be less efficient that using the indexers in a for(x) loop, but I would have thought the effect would be extremely marginal as it isn't copying the data, only references to the underlying data.

It could be worth just knocking up two different loops, one using foreach and one using indexers and see which is faster (or do they generate essentially identical IL ?).

Its the same approach I'd use, so it would be a thumbs-up from me, but I'm sure someone will now leap in and pour scorn over my mutterings 8-)

Phillip Hamlyn
Are you going to handle sorting by state changes in your tree, or will that be handled elsewhere?

You should group bits of geometry that uses the same state together.

From cheapest to most expensive to change:
Material
Transform
Texture
Vertex/Pixel Shader
There is no difference between using a foreach and for in C#. They both offer the same performance, as the compiler will optimize each statement appropriately. The only time when a for loop can offer better performance is when the number of elements is a fixed number, basically a hard coded conditional:

eg)

for (int index = 0; index < 15; index++)


~Graham
Quote:Original post by gwihlidal
There is no difference between using a foreach and for in C#. They both offer the same performance, as the compiler will optimize each statement appropriately.


No you're wrong that very much depends on the implementation on the IEnumerator returned by the IEnumerable class. Is posile to have very close perf for foreach and for constructs but it is also posilble to have a big diference

From the C# 'Language Specification 2.0.doc' :
"
In a foreach statement of the form
foreach (ElementType element in collection) statement
if the collection expression is a type that does not implement the collection pattern, but does implement the constructed interface System.Collections.Generic.IEnumerable<T> for exactly one type T, then the expansion of the foreach statement is:
IEnumerator<T> enumerator = ((IEnumerable<T>)(collection)).GetEnumerator();
try {
while (enumerator.MoveNext()) {
ElementType element = (ElementType)enumerator.Current;
statement;
}
}
finally {
enumerator.Dispose();
}
"

So you can see in this case we have a cast and a method call which might not be inlined if it's more 32 Bytes (in opcode size). So the oportunity for a perf degrade is definatly here.


Quote:Original post by gwihlidal
There is no difference between using a foreach and for in C#. They both offer the same performance, as the compiler will optimize each statement appropriately. The only time when a for loop can offer better performance is when the number of elements is a fixed number, basically a hard coded conditional:

eg)

for (int index = 0; index < 15; index++)


~Graham


Also, writing your for loops as:
for(int index = 0; index < array.Length; index++)

makes it easier for the compiler to hoist the bounds check out of the loop body.

This topic is closed to new replies.

Advertisement