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)