HLSL像素着色器照明性能(XNA)

HLSL像素着色器照明性能(XNA),xna,hlsl,pixel-shader,Xna,Hlsl,Pixel Shader,我有一个足够简单的着色器,支持多个点光源。 灯光存储为灯光结构数组(最大大小),当其更改时,我会传入活动灯光的数量。 问题出在PixelShader函数中: 这是基本的东西,从纹理中获得基本颜色,在灯光数组中循环0到NumacTivelills,并添加效果,效果很好,但性能很差 但是,如果我用一个相同值的常数替换对全局var numActiveLights的引用,性能就可以了。 我只是不明白为什么引用变量会产生30+fps的差异 谁能解释一下吗 完整着色器代码: #define MAX_POIN

我有一个足够简单的着色器,支持多个点光源。
灯光存储为灯光结构数组(最大大小),当其更改时,我会传入活动灯光的数量。
问题出在PixelShader函数中:
这是基本的东西,从纹理中获得基本颜色,在灯光数组中循环0到NumacTivelills,并添加效果,效果很好,但性能很差
但是,如果我用一个相同值的常数替换对全局var numActiveLights的引用,性能就可以了。
我只是不明白为什么引用变量会产生30+fps的差异

谁能解释一下吗

完整着色器代码:

#define MAX_POINT_LIGHTS 16

struct PointLight
{
    float3      Position;
    float4      Color;
    float       Radius;
};

float4x4    World;
float4x4    View;
float4x4    Projection;
float3  CameraPosition;

float4  SpecularColor;
float   SpecularPower;
float   SpecularIntensity;
float4      AmbientColor;
float   AmbientIntensity;
float   DiffuseIntensity;   

int     activeLights;
PointLight  lights[MAX_POINT_LIGHTS];

bool    IsLightingEnabled;
bool    IsAmbientLightingEnabled;
bool    IsDiffuseLightingEnabled;
bool    IsSpecularLightingEnabled;


Texture Texture;
sampler TextureSampler = sampler_state
{
    Texture = <Texture>;

    Magfilter = POINT;
    Minfilter = POINT;
    Mipfilter = POINT;

    AddressU = WRAP;
    AddressV = WRAP;
};

struct VS_INPUT
{
    float4 Position : POSITION0;
    float2 TexCoord : TEXCOORD0;
    float3 Normal : NORMAL0;
};

struct VS_OUTPUT
{
    float3 WorldPosition : TEXCOORD0;
    float4 Position : POSITION0;
    float3 Normal : TEXCOORD1;
    float2 TexCoord : TEXCOORD2;
    float3 ViewDir : TEXCOORD3;

};

VS_OUTPUT VS_PointLighting(VS_INPUT input)
{
    VS_OUTPUT output;

    float4 worldPosition = mul(input.Position, World);
    output.WorldPosition = worldPosition;

    float4 viewPosition = mul(worldPosition, View);
    output.Position = mul(viewPosition, Projection);

    output.Normal = normalize(mul(input.Normal, World));
    output.TexCoord = input.TexCoord;
    output.ViewDir = normalize(CameraPosition -  worldPosition);

    return output;
}

float4 PS_PointLighting(VS_OUTPUT IN) : COLOR
{
    if(!IsLightingEnabled) return tex2D(TextureSampler,IN.TexCoord);

    float4 color = float4(0.0f, 0.0f, 0.0f, 0.0f);

    float3 n = normalize(IN.Normal);
    float3 v = normalize(IN.ViewDir);
    float3 l = float3(0.0f, 0.0f, 0.0f);
    float3 h = float3(0.0f, 0.0f, 0.0f);

    float atten = 0.0f;
    float nDotL = 0.0f;
    float power = 0.0f;

    if(IsAmbientLightingEnabled) color += (AmbientColor*AmbientIntensity);

    if(IsDiffuseLightingEnabled || IsSpecularLightingEnabled)
    {
        //for (int i = 0; i < activeLights; ++i)//works but perfoemnce is terrible
        for (int i = 0; i < 7; ++i)//performance is fine but obviously isn't dynamic
        {
            l = (lights[i].Position - IN.WorldPosition) / lights[i].Radius;
            atten = saturate(1.0f - dot(l, l));

            l = normalize(l);

            nDotL = saturate(dot(n, l));

            if(IsDiffuseLightingEnabled) color += (lights[i].Color * nDotL * atten);
            if(IsSpecularLightingEnabled) color += (SpecularColor * SpecularPower * atten);
        }
    }

    return color * tex2D(TextureSampler, IN.TexCoord);
}

technique PerPixelPointLighting
{
    pass
    {
        VertexShader = compile vs_3_0 VS_PointLighting();
        PixelShader = compile ps_3_0 PS_PointLighting();
    }
}
#定义最大点灯16
结构点光源
{
浮动3位;
4色;
浮动半径;
};
四驱世界;
浮动4x4视图;
浮动4x4投影;
浮动3摄像机定位;
浮动4镜面颜色;
浮动镜面电源;
浮动镜面强度;
浮动4环境色;
漂浮环境强度;
漂浮扩散强度;
活动灯光;
点光源[最大点光源];
布尔孤岛亮起;
布尔是一个模糊的光源;
bool为DiffuseLightingEnabled;
布尔是特别明亮的;
纹理;
采样器纹理采样器=采样器状态
{
纹理=;
磁过滤器=点;
Minfilter=点;
Mipfilter=点;
地址u=包裹;
地址v=包裹;
};
结构与输入
{
浮动4位置:位置0;
浮动2 TexCoord:TEXCOORD0;
float3 Normal:NORMAL0;
};
结构与输出
{
浮动3世界位置:TEXCOORD0;
浮动4位置:位置0;
浮动3正常:TEXCOORD1;
浮动2 TexCoord:TEXCOORD2;
float3-ViewDir:TEXCOORD3;
};
VS_输出VS_点光源(VS_输入)
{
VS_输出;
float4 worldPosition=mul(input.Position,World);
output.WorldPosition=世界位置;
float4 viewPosition=mul(世界位置,视图);
输出位置=mul(视图位置、投影);
output.Normal=normalize(mul(input.Normal,World));
output.TexCoord=input.TexCoord;
output.ViewDir=规格化(CameraPosition-worldPosition);
返回输出;
}
float4 PS_点光源(VS_输出IN):颜色
{
如果(!IsLightingEnabled)返回tex2D(TextureSampler,IN.TexCoord);
float4 color=float4(0.0f,0.0f,0.0f,0.0f);
float3 n=正常化(IN.Normal);
float3 v=标准化(IN.ViewDir);
浮动3 l=浮动3(0.0f,0.0f,0.0f);
浮动3 h=浮动3(0.0f,0.0f,0.0f);
浮点数=0.0f;
浮动nDotL=0.0f;
浮动功率=0.0f;
如果(IsAmbientLightingEnabled)颜色+=(AmbientColor*AmbientIntensity);
如果(IsDiffuseLightingEnabled | | IsSpecialLightingEnabled)
{
//for(int i=0;i
我的猜测是,将循环约束更改为编译时常量将允许HLSL编译器展开循环。也就是说,与此相反:

for (int i = 0; i < 7; i++)
    doLoopyStuff();
循环和条件分支在着色器代码中可能会对性能造成重大影响,应尽可能避免

编辑

这是我脑子里想不出来的,但也许你可以试试这样的东西

for (int i = 0; i < MAX_LIGHTS; i++)
{
    color += step(i, activeLights) * lightingFunction();
}
for(int i=0;i

通过这种方式,您可以计算所有可能的灯光,但对于非活动灯光,始终会获得0的值。当然,好处将取决于照明功能的复杂性;您需要进行更多的评测。

尝试使用PIX对其进行评测

或者,读一读这篇杂乱无章的推测:

也许是因为使用常量,编译器可以分解和折叠循环的指令。当您将其替换为变量时,编译器将无法进行相同的假设

不过,与您的实际问题有些无关,我会将许多条件/计算推到软件级别

if(IsDiffuseLightingEnabled | | IsSpecialLightingEnabled)

^-像那样

另外,我认为在调用着色器程序之前,可以预先计算一些东西。Like
l=(lights[i].Position-IN.WorldPosition)/lights[i].Radius传递一个预先计算的数组,而不是每次在每个像素上计算

我可能对HLSL编译器所做的优化有错误的了解,但我认为在像素着色器上进行的每一次计算都会在屏幕上执行w*h次(尽管这是非常并行的),我模糊地记得着色器中的指令数量有一些限制(比如72?)。(尽管我认为在HLSL的更高版本中,这一限制被放宽了很多)。可能是因为你的着色器生成了这么多指令——可能是它破坏了你的程序,并在编译时将其转换为一个多通道像素着色器。如果是这样的话,这可能会增加大量的开销

实际上,还有一个想法可能很愚蠢:将变量传递给着色器,使其将数据传输到GPU。这种传输是在带宽有限的情况下进行的。也许编译器足够聪明,当您只需要
for (int i = 0; i < MAX_LIGHTS; i++)
{
    color += step(i, activeLights) * lightingFunction();
}