1
votes

In HLSL, is there any way to limit the number of constant registers that the compiler uses?

Specifically, if I have something like:

float4 foobar[300];

In a vs_2_0 vertex shader, the compiler will merrily generate the effect with more than 256 constant registers. But a 2.0 vertex shader is only guaranteed to have access to 256 constant registers, so when I try to use the effect, it fails in an obscure and GPU-dependent way at runtime. I would much rather have it fail at compile time.

This problem is especially annoying as the compiler itself allocates constant registers behind the scenes, on top of the ones I am asking for. I have to check the assembly to see if I'm over the limit.

Ideally I'd like to do this in HLSL (I'm using the XNA content pipeline), but if there's a flag that can be passed to the compiler that would also be interesting.

1
Good question. I would also post it on forums.xna.com/forums/27.aspx since the DirectX developers are hanging on there.Stringer
Thanks, Stringer Bell. I posted it to the XNA forum as you suggested: forums.xna.com/forums/p/53856/326864.aspxAndrew Russell
Also: if there's no way to do it in HLSL or with the compiler, is there any way to count the number of constant registers a compiled shader uses programmatically? (Ideally using XNA)Andrew Russell
Maybe look at ShaderCompiler.Disassemble method.Stringer

1 Answers

1
votes

Based on Stringer Bell's pointing out of the Disassemble method, I have whipped up a small post-build utility to parse and check the effect. Be warned that this is not very pretty. It is designed for XNA 3.1 and requires the ServiceContainer and GraphicsDeviceService classes from the XNA WinForms sample. Pass a content directory path on the command line with no trailing slash.

class Program
{
    const int maxRegisters = 256; // Sutiable for VS 2.0, not much else
    static int retval = 0;
    static string root;
    static ContentManager content;

    static void CheckFile(string path)
    {
        string name = path.Substring(root.Length+1, path.Length - (root.Length+1) - @".xnb".Length);
        Effect effect;
        try { effect = content.Load<Effect>(name); }
        catch { return; } // probably not an Effect
        string effectSource = effect.Disassemble(false);

        int highest = -1; // highest register allocated

        var matches = Regex.Matches(effectSource, @" c([0-9]+)"); // quick and dirty
        foreach(Match match in matches)
        {
            int register = Int32.Parse(match.Groups[1].ToString());
            if(register > highest)
                highest = register;
        }

        var parameters = Regex.Matches(effectSource, @"^ *// *[a-zA-Z_0-9]+ +c([0-9]+) +([0-9]+)", RegexOptions.Multiline);
        foreach(Match match in parameters)
        {
            int register = Int32.Parse(match.Groups[1].ToString()) + Int32.Parse(match.Groups[2].ToString()) - 1;
            if(register > highest)
                highest = register;
        }

        if(highest+1 > maxRegisters)
        {
            Console.WriteLine("Error: Shader \"" + name + "\" uses " + (highest+1).ToString() + " constant registers, which is TOO MANY!");
            retval = 1;
        }
        else
        {
            Console.WriteLine("Shader \"" + name + "\" uses " + (highest+1).ToString() + " constant registers (OK)");
        }
    }

    static void CheckDirectory(string path)
    {
        try
        {
            foreach(string file in Directory.GetFiles(path, @"*.xnb"))
                CheckFile(file);
            foreach(string dir in Directory.GetDirectories(path))
                CheckDirectory(dir);
        }
        catch { return; } // Don't care
    }

    static int Main(string[] args)
    {
        root = args[0];

        Form form = new Form(); // Dummy form for creating a graphics device
        GraphicsDeviceService gds = GraphicsDeviceService.AddRef(form.Handle,
                form.ClientSize.Width, form.ClientSize.Height);

        ServiceContainer services = new ServiceContainer();
        services.AddService<IGraphicsDeviceService>(gds);
        content = new ContentManager(services, root);

        CheckDirectory(root);

        return retval;
    }
}