gpt4 book ai didi

vertex-shader - 为什么这个计算着色器比顶点着色器慢得多?

转载 作者:行者123 更新时间:2023-12-04 05:39:07 24 4
gpt4 key购买 nike

我正在探索使用计算着色器将骨骼变形应用于网格顶点,而不是使用流输出的顶点着色器。我发现计算着色器的执行速度比顶点着色器慢得多,但在我把它写下来之前,我想确定我没有做错什么。

使用我的 100,000 个顶点的测试数据和 300 个骨骼的 1,000 帧动画数据,顶点着色器的运行时间约为 0.22 毫秒,而计算着色器的运行时间是 0.85 毫秒的 4 倍。计时是通过 D3D API 计时器查询(而不是 cpu 计时器)完成的。

变形结构体.hlsl

struct Vertex {
float3 position : POSITION;
float3 normal : NORMAL;
float2 texcoord : TEXCOORD;
float3 tangent : TANGENT;
float4 color : COLOR;
};

struct BoneWeights {
uint index;
float weight;
};

StructuredBuffer<matrix> g_bone_array : register(t0);
Buffer<uint> g_bone_offsets : register(t1);
Buffer<uint> g_bone_counts : register(t2);
StructuredBuffer<BoneWeights> g_bone_weights : register(t3);

bone_deform_cs.hlsl
#include "deform_structs.hlsl"

StructuredBuffer<Vertex> g_input_vertex : register(t4);
RWStructuredBuffer<Vertex> g_output_vertex : register(u0);

[numthreads(64,1,1)]
void BoneDeformCS(uint id : SV_DispatchThreadID) {
Vertex vert = g_input_vertex[id.x];
uint offset = g_bone_offsets[id.x];
uint count = g_bone_counts[id.x];

matrix bone_matrix = 0;
for (uint i = offset; i < (offset + count); ++i) {
BoneWeights weight_info = g_bone_weights[i];
bone_matrix += weight_info.weight * g_bone_array[weight_info.index];
}

vert.position = mul(float4(vert.position,1), bone_matrix).xyz;
vert.normal = normalize(mul(vert.normal, (float3x3)bone_matrix));
vert.tangent = normalize(mul(vert.tangent, (float3x3)bone_matrix));
g_output_vertex[id.x] = vert;
}

bone_deform_vs.hlsl
#include "deform_structs.hlsl"

void BoneDeformVS(uint id : SV_VertexID, Vertex vsin, out Vertex vsout) {
uint offset = g_bone_offsets[id];
uint count = g_bone_counts[id];

matrix bone_matrix = 0;
for (uint i = offset; i < (offset + count); ++i) {
BoneWeights bone_info = g_bone_weights[i];
bone_matrix += bone_info.weight * g_bone_array[bone_info.index];
}

vsout.position = mul(float4(vsin.position,1), bone_matrix).xyz;
vsout.normal = normalize(mul(vsin.normal, (float3x3)bone_matrix));
vsout.tangent = normalize(mul(vsin.tangent, (float3x3)bone_matrix));
vsout.texcoord = vsin.texcoord;
vsout.color = vsin.color;
}

缓冲区运行后比较它们的内容,它们是相同的并且包含预期值。

我怀疑我可能错误地执行了计算着色器,产生了太多线程?我有我传递给 Dispatch 的号码吗?错误的?由于它是一维数据行,因此使用 [numthreads(64,1,1)] 对我来说是有意义的。 .我尝试了 32-1024 的各种值。 64 似乎是最佳选择,因为它是高效使用 AMD GPU 所需的最低要求。反正。当我调用 Dispatch ,我要求它执行 (vertex_count / 64) + (vertex_count % 64 != 0) ? 1 : 0 .对于 100,000 个顶点,调用最终为 Dispatch(1563,1,1) .
ID3D11ShaderResourceView * srvs[] = {bone_array_srv, bone_offset_srv,
bone_count_srv, bone_weights_srv,
cs_vertices_srv};
ID3D11UnorderedAccessView * uavs[] = {cs_output_uav};
UINT srv_count = sizeof(srvs) / sizeof(srvs[0]);
UINT uav_count = sizeof(uavs) / sizeof(uavs[0]);
UINT thread_group_count = vertex_count / 64 + (vertex_count % 64 != 0) ? 1 : 0;

context->CSSetShader(cs, nullptr, 0);
context->CSSetShaderResources(0, srv_count, srvs);
context->CSSetUnorderedAccessViews(0, uav_count, uavs);
context->Dispatch(thread_group_count, 1, 1);

这就是顶点着色器的执行方式:
ID3D11ShaderResourceView * srvs[] = {bone_array_srv, bone_offset_srv,
bone_count_srv, bone_weights_srv};
UINT srv_count = sizeof(srvs) / sizeof(srvs[0]);
UINT stride = 0;
UINT offset = 0;

context->GSSetShader(streamout_gs, nullptr, 0);
context->VSSetShader(vs, nullptr, 0);
context->VSSetShaderResources(0, srv_count, srvs);
context->SOSetTargets(1, &vs_output_buf, &offset);
context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_POINTLIST);
context->IASetInputLayout(vs_input_layout);
context->IASetVertexBuffers(0, 1, &vs_vertices, &stride, &offset);
context->Draw(vertex_count, 0);

或者答案仅仅是从着色器资源 View 读取并写入无序访问 View 比从顶点缓冲区读取并写入流输出缓冲区要慢得多?

最佳答案

我只是在学习如何使用计算着色器,所以我不是专家。关于你的骨骼计算,我确信 CS 至少应该和 VS 一样快。直觉告诉我numthreads (64,1,1)效率低于 numthreads (16,16,1) .
所以你可以试试这种方法:

  • 将线性缓冲区视为二次布局,x 和 y 大小相同
  • 计算 x/y 大小为 size = ceil (sqrt (numvertices))
  • 使用 dispat ch(size / 16, size / 16)在你的程序和numthreads (16,16,1)在您的 hlsl 文件中
  • 在复制 size 的位置分配一个常量缓冲区和 numvertices
  • 而不是使用 id.x作为索引,您将自己的(线性)索引计算为 int index = id.y * size +id.x) ,(也许 id.xy 也可以作为索引)
  • 在大多数情况下 size * size将大于 numvertices ,所以你最终会得到比顶点更多的线程。您可以通过在 hlsl 函数中添加条件来阻止这些额外的线程:
    int index = id.y * size +id.x;
    if (index < numvertices) { .. // your code follows

  • 我希望这种方法可以加快您的 CS 计算速度。

    ================ 编辑==================

    我的建议是基于我自己的计时测试。为了验证我的情况,我用更多的 numthreads 参数变化重复了这些测试。
    我计算了超过 1034 x 827 = 855,118 像素的 mandelbrot 集。结果如下:
    numthreads       Dispatch      groups  threads/  total
    x y fps x y group threads

    4 4 240 259 207 53445 16 855118
    8 8 550 129 103 13361 64 855118
    16 16 600 65 52 3340 256 855118
    32 32 580 32 26 835 1024 855118
    64 1 550 16 827 13361 64 855118
    256 1 460 4 827 3340 256 855118
    512 1 370 2 827 1670 512 855118

    如您所见,最佳点 - numthreads(16,16,1) - 创建与 numthreads(256,1,1) 相同的 #of 线程组 (3340),但性能提高了 30%。
    请注意,总线程数(并且必须)始终为 相同 !
    我的 GPU 是 ATI 7790。

    ================ 编辑2 ==================

    为了更深入地调查您关于 CS 与 VS 速度的问题,我重新观看了一个非常有趣的第 9 channel 视频(PDC09 演示文稿,由 Microsoft 首席架构师 Chas Boyd 举办,关于直接计算,请参见下面的链接)。在此演示文稿中,Boyd 指出优化线程布局 (numthreads) 可以导致吞吐量增加两倍。

    然而,更有趣的是他的演讲部分(从第 40 分钟开始),他解释了 UAV 和 GPU 内存布局之间的相关性(“图形与计算 I/O”)。我不想从 Boyds 的陈述中得出错误的结论,但似乎至少有可能,计算着色器通过无人机绑定(bind) 有一个 更低的内存带宽比其他 GPU 着色器。如果这是真的,我们可能会解释无人机不能绑定(bind)到 VS 的事实,例如(至少在 11.0 版本中)。

    由于这些内存访问模式也取决于硬件设计,因此您应该将您的问题直接上报给 ATI/NVIDIA 工程师。

    结论

    我已经吸收了大量关于 CS 使用的信息,但没有丝毫迹象表明 CS 可以比 VS 运行相同的算法慢。如果情况确实如此,那么您已经检测到对所有使用直接计算的人来说都很重要的东西。

    链接: http://channel9.msdn.com/Events/PDC/PDC09/P09-16

    关于vertex-shader - 为什么这个计算着色器比顶点着色器慢得多?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20206093/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com