numthreads(1,1,1)的计算着色器运行速度极慢

Sie*_*han 2 directx hlsl compute-shader directcompute

我刚开始学习DirectX编程,使用F#和SharpDX作为.NET包装器.作为测试用例,我渲染了Mandelbrot集.使用2个计算着色器完成计算.

第一个着色器计算每个像素的深度(函数"CalcMandel"),结果存储在RWStructuredBuffer中.这种计算需要大量的单次或双次乘法,但它在我的GPU(AMD 7790)上的速度非常快."CalcMandel"具有该属性

[numthreads(16, 16, 1)]
Run Code Online (Sandbox Code Playgroud)

并通过发送

context.Dispatch (imageWidth / 16, imageHeight / 16, 1)
Run Code Online (Sandbox Code Playgroud)

这里没有问题 - "核心"Mandelbrot集的1000 x 800像素图像以超过1000 fps的速度运行(在GPU上使用单精度).


第二个着色器几乎没有做任何事情:它计算先前计算的最小值,最大值和平均值(函数"CalcMinMax")."CalcMinMax"具有该属性

[numthreads(1, 1, 1)]
Run Code Online (Sandbox Code Playgroud)

并被称为通过

context.Dispatch (1,1,1)
Run Code Online (Sandbox Code Playgroud)

对于当时给定的图像大小,单个GPU线程必须遍历超过800.000个整数的缓冲区以计算最小值,最大值和平均值.我使用单个线程,因为我不知道如何以并行方式实现此计算.

问题:"CalcMinMax"非常慢:帧速率从1000 fps下降到5 fps!

我的问题:这里有什么问题?我使用了错误的设置/参数(numthreads)吗?如何加快min-max计算?

我的想法:我的第一个假设是访问RWBuffer可能会很慢 - 事实并非如此.当我用常量替换缓冲区访问时,帧速率没有增加.

我的GPU有appr.900个着色器核心并使用数千个线程来计算Mandelbrot集合,而"CalcMinMax"仅使用一个线程.然而,我仍然不明白为什么事情变得如此缓慢.

我很感激任何建议!

================================================

// HLSL CONTENT(省略Mandelbrot集计算):

cbuffer cbCSMandel : register( b0 )
{

double  a0, b0, da, db;
double  ja0, jb0;   
int max_iterations;
bool julia;     int  cycle;
int width;      int height;
double colorFactor;
int algoIndex;
int step;
};


struct statistics
{
  int   minDepth;
  int     axDepth;
  float avgDepth;
  int   loops;
};

RWStructuredBuffer<float4> colorOutputTable :   register (u0);
StructuredBuffer<float4> output2 :          register (t0);
RWStructuredBuffer<int> counterTable :          register (u1);
RWStructuredBuffer<float4> colorTable :     register (u2);

RWStructuredBuffer<statistics>statsTable :      register (u3);


// Mandelbrot calculations….
// Results are written to counterTable and colorOutputTable


// I limit the samples to 10000 pixels because calcMinMax is too slow
#define NUM_SAMPLES 10000;

void calcMinMax() 
{
    int minDepth = 64000;
    int maxDepth = 0;
    int len = width * height;
    int crit = len / NUM_SAMPLES;
    int steps = max (crit, 1);
    int index = 0;          
    int sumCount = 0;
    float sum = 0.0;

while (index < len)
{
    int cnt = counterTable[index];
    minDepth = cnt < minDepth & cnt > 0 ? cnt : minDepth;
    maxDepth = cnt > maxDepth ? cnt : maxDepth;
    sum += cnt > 0 ? cnt : 0.0f;
sumCount += cnt > 0 ? 1 : 0; 
    index += steps;
}

statsTable[0].minDepth = minDepth;
statsTable[0].maxDepth = maxDepth; 
statsTable[0].avgDepth = sum / sumCount;
statsTable[0].loops += 1; 
}


[numthreads(1, 1, 1)]
void CalcMinMax ( uint3 Gid : SV_GroupID, uint3 DTid : SV_DispatchThreadID, uint3 GTid :    SV_GroupThreadID, uint GI : SV_GroupIndex )

{
    switch (GI)     // this switch is used to verify GI number (always 0)
{
        case 0: calcMinMax();
    break;

        default: ;
    break;

}
}
Run Code Online (Sandbox Code Playgroud)

// ******************* F#程序(最小 - 最大部分) *************

着色器设置:

use minMaxShaderCode = ShaderBytecode.CompileFromFile(shaderPath, "CalcMinMax", "cs_5_0")                                                                
minMaxShader <- new ComputeShader(device, minMaxShaderCode.Bytecode.Data  )  
Run Code Online (Sandbox Code Playgroud)

着色器用法:

// ---------- CONNECT MinMap Shader
context.ComputeShader.Set(minMaxShader)    
context.ComputeShader.SetUnorderedAccessView(STATS_SLOT, statsBuffer.BufferView) 

context.ComputeShader.SetConstantBuffer(CONSTANT_SLOT, constantBuffer)
context.ComputeShader.SetUnorderedAccessView (COUNTER_SLOT, dataBuffer.BufferView)  
context.Dispatch (1,1,1)

// ---------- DISCONNECT MinMap Shader            
context.ComputeShader.SetConstantBuffer(CONSTANT_SLOT, null)
context.ComputeShader.SetUnorderedAccessView (STATS_SLOT, null) 
context.ComputeShader.SetUnorderedAccessView (COUNTER_SLOT, null) 
context.ComputeShader.Set (null) 
Run Code Online (Sandbox Code Playgroud)

阅读统计:

context.CopyResource(statsBuffer.DataBuffer, statsBuffer.StagingBuffer)
let boxer, stream = context.MapSubresource(statsBuffer.StagingBuffer, MapMode.Read, MapFlags.None)                                                                                                                                    
calcStatistics <- stream.Read<CalcStatistics>()
context.UnmapSubresource(statsBuffer.DataBuffer, 0)
Run Code Online (Sandbox Code Playgroud)

akh*_*bis 7

如果您只调度1个线程,那么GPU上的每个着色器单元都会空闲,等待该线程完成.你需要并行化你的minmax算法,并且考虑到你必须计算一个值数组以得出一个单独的值,这是一个典型的减少问题.更有效的方法是递归地计算局部最小值/最大值.这里可以看到用于对数组的值求和的示例的详细说明(从幻灯片19开始).