【问题标题】:How to manually or automatically optimize HLSL (pixel) shader code?如何手动或自动优化 HLSL(像素)着色器代码?
【发布时间】:2013-01-20 18:15:40
【问题描述】:

在计算复杂度方面优化 HLSL 着色器代码的成功策略是什么(意思是:最小化着色器的运行时间)?

我想一种方法是尽量减少编译着色器产生的算术运算的数量。

如何做到 a) 手动和 b) 使用自动化工具(如果存在)?

手工技巧合集(更新)

  • 避免分支(但如何做到最好?)
  • 尽可能:预先计算外部着色器并作为参数传递。

示例代码如下:

float2 DisplacementScroll;

// Parameter that limit the water effect
float glowHeight;
float limitTop;
float limitTopWater; 
float limitLeft;
float limitRight;
float limitBottom;

sampler TextureSampler : register(s0); // Original color
sampler DisplacementSampler : register(s1); // Displacement

float fadeoutWidth = 0.05;

// External rumble displacement
int enableRumble;
float displacementX;
float displacementY;

float screenZoom;

float4 main(float4 color : COLOR0, float2 texCoord : TEXCOORD0) : COLOR0
{

// Calculate minimal distance to next border
float dx = min(texCoord.x - limitLeft, limitRight - texCoord.x);
float dy = min(texCoord.y - limitTop, limitBottom - texCoord.y);

///////////////////////////////////////////////////////////////////////////////////////
// RUMBLE                                                        //////////////////////
///////////////////////////////////////////////////////////////////////////////////////

    if (enableRumble!=0)
    {
    // Limit rumble strength by distance to HLSL-active region (think map)
    // The factor of 100 is chosen by hand and controls slope with which dimfactor goes to 1
    float dimfactor = clamp(100.0f * min(dx, dy), 0, 1); // Maximum is 1.0 (do not amplify)

    // Shift texture coordinates by rumble
    texCoord.x += displacementX * dimfactor * screenZoom;
    texCoord.y += displacementY * dimfactor * screenZoom;
    }

//////////////////////////////////////////////////////////////////////////////////////////
// Water refraction (optical distortion) and water like-color tint  //////////////////////
//////////////////////////////////////////////////////////////////////////////////////////

if (dx >= 0)
{
float dyWater = min(texCoord.y - limitTopWater, limitBottom - texCoord.y);

  if (dyWater >= 0)
  {
    // Look up the amount of displacement from texture
    float2 displacement = tex2D(DisplacementSampler, DisplacementScroll + texCoord / 3);

    float finalFactor = min(dx,dyWater) / fadeoutWidth;
    if (finalFactor > 1) finalFactor = 1;

    // Apply displacement by water refraction
    texCoord.x += (displacement.x * 0.2 - 0.15) * finalFactor * 0.15 * screenZoom; // Why these strange numbers ?
    texCoord.y += (displacement.y * 0.2 - 0.15) * finalFactor * 0.15 * screenZoom;

    // Look up the texture color of the original underwater pixel.
    color = tex2D(TextureSampler, texCoord);

    // Additional color transformation (blue shift)
    color.r = color.r - 0.1f;
    color.g = color.g - 0.1f;
    color.b = color.b + 0.3f;

  }
  else if (dyWater > -glowHeight)
  {
   // No water distortion...
   color = tex2D(TextureSampler, texCoord);

   // Scales from 0 (upper glow limit) ... 1 (near water surface)
   float glowFactor = 1 - (dyWater / -glowHeight); 

   // ... but bluish glow
   // Additional color transformation
   color.r = color.r - (glowFactor * 0.1); // 24 = 1/(30f/720f); // Prelim: depends on screen resolution, must fit to value in HLSL Update
   color.g = color.g - (glowFactor * 0.1);
   color.b = color.b + (glowFactor * 0.3);
  }
  else
  {
  // Return original color (no water distortion above and below)
  color = tex2D(TextureSampler, texCoord);
  }
}
else
{
// Return original color (no water distortion left or right)
color = tex2D(TextureSampler, texCoord);
}

   return color;
}

technique Refraction
{
    pass Pass0
    {
        PixelShader = compile ps_2_0 main();
    }
}

【问题讨论】:

    标签: optimization xna shader hlsl pixel-shader


    【解决方案1】:

    我对 HLSL 内部结构不是很熟悉,但我从 GLSL 中学到的是:永远不要分支。它可能会执行这两个部分,然后决定它们的哪个结果应该是有效的。

    也可以看看thisthis

    据我所知,除了编译器本身,没有自动工具。对于非常低级别的优化,您可以使用 fxc 和 /Fc 参数来获取程序集列表。可能的汇编指令在here 中列出。一个值得一提的低级优化是 MAD:乘法和加法。这可能没有针对 MAD 操作进行优化(我不确定,自己尝试一下):

    a *= b;
    a += c;
    

    但这应该优化为 MAD:

    a = (a * b) + c;
    

    【讨论】:

    • 我是否正确理解应该避免使用 if/else?但是,如果要做出决定,怎么能做到这一点呢?
    • 这取决于大小写。有时您可能无法减少分支,但通常您可以将着色器拆分为两个着色器,然后再决定使用哪个着色器。有时它也有助于用其他数学公式解决问题。通常有一个简单的数学公式,无需分支即可解决问题。但是您的着色器并不是最好的例子,因为您正在分支选择正确的纹理。如果您可以使用纹理数组或合并纹理会更容易。 if 块的内容也应该减少。
    • 一种解决方案是这样做:a = float(a > 0.5) * 2.0;取而代之的是: if(a > 0.5) a = 2.0;否则 a = 0.0;
    【解决方案2】:

    您可以使用涉及操作函数的数学技术优化您的代码,例如:

    // Shift texture coordinates by rumble
    texCoord.x += displacementX * dimfactor * screenZoom;
    texCoord.y += displacementY * dimfactor * screenZoom;
    

    这里将三个值相乘​​,但其中只有一个来自GPU的寄存器,另外两个是常量,可以预先相乘并存储在全局常量中。

    // Shift texture coordinates by rumble
    texCoord.x += dimfactor * pre_zoom_dispx; // displacementX * screenZoom
    texCoord.y += dimfactor * pre_zoom_dispy; // displacementY * screenZoom
    

    另一个例子:

    // Apply displacement by water refraction
    texCoord.x += (displacement.x * 0.2 - 0.15) * finalFactor * 0.15 * screenZoom; // Why     these strange numbers ?
    texCoord.y += (displacement.y * 0.2 - 0.15) * finalFactor * 0.15 * screenZoom;
    
     0.15 * screenZoom <- can be optimized by one global.
    

    Visual Studio 2012 的 HLSL 编译器有一个选项来启用优化。但是您可以进行的最佳优化是尽可能简单地编写 HLSL 代码并使用内部函数http://msdn.microsoft.com/en-us/library/windows/desktop/ff471376(v=vs.85).aspx 这些函数就像 C 的memcpy,在主体中使用汇编代码,使用系统资源,如 128 位寄存器(是的,CPU 有 128 位寄存器http://en.wikipedia.org/wiki/Streaming_SIMD_Extensions)和非常快速的操作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-27
      • 2020-07-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多