Original Post
Edited: Fixed EffectHandlerProxy Reset method, didn't work because of Hashtable synchronization issues. _____________________________________ I was just playing around with my cloud generator and I decided that I should be setting the effect parameters using EffectHandles instead of name strings. Everyone says these are more efficient, but there seem to be no solid figures on exactly how much more efficient. So, I thought my results might be interesting for other (M)DX developers. I also developed a little utility class to facilitate using EffectHandles, which you can find below. Analysis When I started replacing the SetParameter calls to use handles, I noticed quite an impressive speed gain as I went along. After I replaced all 26 SetParameter calls, which are called each frame, my average framerate went up from 950 to 1100 fps. That's a 16% gain, by only switching from strings to handles! My EffectHandleProxy class below uses a Hashtable to look up handles for parameter names, so you can keep setting the parameters using the hassle-free, descriptive name strings, while taking advantage of the efficiency of the handles. I'm a bit puzzled why the DirectX team doesn't incorporate this into the Effect.SetParameter method directly. At first I thought the overhead of the Hashtable lookups might be just as inefficient as calling SetParameter using the name string, but the 16% fps gain already includes this overhead. EffectHandleProxy utility class The EffectHandleProxy class allows you to easily use handles for setting effect parameters, with a minimum of recoding. You can create an instance of the EffectHandleProxy alongside an effect. When you create the Effect object during a device reset (or initialization), simply call the Reset(Effect) method on the proxy object. This will bind the proxy to the effect and it will recreate any handles that were used on the effect before. Note that you need one proxy per effect and that you can keep reusing this proxy for the same effect over a device reset, using the Reset(Effect) method. Actually setting the parameters is easy as well. You simply replace effect.SetParameter with proxyObject.SetParameter and that's that. The proxy supports all overloads of the Effect method (plus an extra one for structs) and it will automatically create a new EffectHandle for any parameter name that hasn't been used before. The code (or download it without layout quirks): [Edited by - remigius on December 8, 2005 1:18:01 PM]
[source lang=c#]
using System;
using System.Collections;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using System.Runtime.InteropServices;
namespace Clouds.Util
{
/// <summary>
/// The EffectHandleProxy caches effecthandles to efficiently set
/// effect parameters while using the name strings in the main app.
/// </summary>
public class EffectHandleProxy
{
private Hashtable effectHandles = new Hashtable();
private Effect effect;
public EffectHandleProxy()
{
}
public void Reset( Effect effect )
{
this.effect = effect;
effectHandles.Clear();
}
public void SetValue( string name, BaseTexture val )
{
effect.SetValue( GetHandle( name ), val );
}
public void SetValue( string name, bool val )
{
effect.SetValue( GetHandle( name ), val );
}
public void SetValue( string name, bool[] val )
{
effect.SetValue( GetHandle( name ), val );
}
public void SetValue( string name, ColorValue val )
{
effect.SetValue( GetHandle( name ), val );
}
public void SetValue( string name, ColorValue[] val )
{
effect.SetValue( GetHandle( name ), val );
}
public void SetValue( string name, GraphicsStream val )
{
effect.SetValue( GetHandle( name ), val );
}
public void SetValue( string name, int val )
{
effect.SetValue( GetHandle( name ), val );
}
public void SetValue( string name, int[] val )
{
effect.SetValue( GetHandle( name ), val );
}
public void SetValue( string name, Matrix val )
{
effect.SetValue( GetHandle( name ), val );
}
public void SetValue( string name, Matrix[] val )
{
effect.SetValue( GetHandle( name ), val );
}
public void SetValue( string name, float val )
{
effect.SetValue( GetHandle( name ), val );
}
public void SetValue( string name, float[] val )
{
effect.SetValue( GetHandle( name ), val );
}
public void SetValue( string name, string val )
{
effect.SetValue( GetHandle( name ), val );
}
public void SetValue( string name, Vector4 val )
{
effect.SetValue( GetHandle( name ), val );
}
public void SetValue( string name, Vector4[] val )
{
effect.SetValue( GetHandle( name ), val );
}
public unsafe void SetValue( string name, void* val, int len )
{
effect.SetValue( GetHandle( name ), val, len );
}
public unsafe void SetStructure(string name, object objStr)
{
int nSize = Marshal.SizeOf(objStr);
IntPtr unmanagedPointer = Marshal.AllocHGlobal(nSize);
Marshal.StructureToPtr(objStr, unmanagedPointer, true);
effect.SetValue(GetHandle(name), unmanagedPointer.ToPointer(), nSize);
Marshal.FreeHGlobal(unmanagedPointer);
}
private EffectHandle GetHandle( string name )
{
if (!effectHandles.ContainsKey(name))
{
effectHandles.Add( name, effect.GetParameter( null, name ) );
}
return (EffectHandle)effectHandles[name];
}
}
}