如何手动或自动优化HLSL(像素)着色器代码?

时间:2013-01-20 18:15:41

标签: optimization xna shader hlsl pixel-shader

在计算复杂度方面优化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();
    }
}

2 个答案:

答案 0 :(得分:1)

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

另请查看thisthis

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

a *= b;
a += c;

但这应该针对MAD进行优化:

a = (a * b) + c;

答案 1 :(得分:1)

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

// 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编译器在poperties中有一个选项来启用Optimizations。但是你可以做的最好的优化是尽可能简单地编写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)和强大的快速操作。