HLSL:在编译时强制执行常量寄存器限制

时间:2010-05-19 07:26:15

标签: xna shader hlsl vertex-shader

在HLSL中,有没有办法限制编译器使用的常量寄存器的数量?

具体来说,如果我有类似的东西:

float4 foobar[300];

在vs_2_0顶点着色器中,编译器将使用超过256个常量寄存器快速生成效果。但是2.0顶点着色器只能保证能够访问256个常量寄存器,因此当我尝试使用该效果时,它会在运行时以一种模糊且依赖于GPU的方式失败。我宁愿在编译时失败。

这个问题特别烦人,因为编译器本身在我要求的场景之后在幕后分配常量寄存器。我必须检查装配,看看我是否超过限制。

理想情况下,我想在HLSL中执行此操作(我正在使用XNA内容管道),但是如果有一个可以传递给编译器的标志也很有趣。

1 个答案:

答案 0 :(得分:1)

基于Stringer Bell指出Disassemble方法,我已经启动了一个小的post-build实用程序来解析并检查效果。请注意,这不是很漂亮。它专为XNA 3.1设计,需要the XNA WinForms sample中的ServiceContainerGraphicsDeviceService类。在命令行上传递内容目录路径,不带斜杠。

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;
    }
}