如何在计算着色器中正确并行化纹理的重新缩放?

问题描述 投票:0回答:1

我有一个

RWTexture2D<float4>
,它由光线生成着色器填充。我需要按一个公共常数值缩放每个像素,该常数只有在光线生成着色器完成后才知道。所以,我正在计算着色器中进行重新缩放。 不幸的是,我对计算着色器不太熟悉。我显然希望缩放操作尽可能快。所以我想我想使用可用的最大并行化。我看到有诸如线程和组之类的东西以及相应的系统值

SV_GroupID

SV_GroupThreadID
SV_GroupIndex
SV_DispatchThreadID
。但我仍然不清楚
[numthreads(THREAD_COUNT_X, THREAD_COUNT_Y, 1)]
和命令列表
Dispatch
调用的最佳选择是什么。
为了实现,我尝试了以下方法:

uint const stride_size_x = texture_width / THREAD_COUNT_X, stride_size_y = texture_height / THREAD_COUNT_Y, offset_x = thread_id.x * stride_size_x, offset_y = thread_id.y * stride_size_y; for (uint v = offset_y; v < offset_y + stride_size_y; ++v) { for (uint u = offset_x; u < offset_x + stride_size_x; ++u) mytexture[uint2(u, v)] *= myscaling; }

但是,令我惊讶的是,这无法正常工作。图像的一小部分(底部)似乎没有被我的循环捕获。我在这里做错了什么和/或者我应该以不同的方式实现它?

备注

:在循环期间,我还将根据 mytexture[uint2(u, v)] 编写

(u, v)
到另一个纹理的变换。所以,如果这很重要,我想在这里做的不仅仅是重新调整。
    

hlsl compute-shader directx-12
1个回答
0
投票
numthreads

解析为 32,否则解析为 64(在 NVidia 上效率稍低,但几乎不会产生影响)。您可以使用

SV_DispatchThreadId
轻松将线程索引转换为像素坐标。然后您在着色器中要做的就是实际缩放。
[numthreads(8, 8, 1)] // 8 * 8 = 64
void main(uint3 id : SV_DispatchThreadID)
{
    mytexture[id.xy] *= myscaling;
}

在上面的内核中,每个组将产生 8x8 线程。在 NVidia 上,这意味着一半的线程将按顺序执行。每个组将在纹理中的二次 8x8 像素图块上运行。您当然可以将其更改为例如2x16 或 1x32。这不会影响性能,在这种情况下,最好使用较小的二次平铺大小,因为更容易使纹理尺寸为 8 的倍数。如果纹理尺寸不是 8 的倍数,则可能需要添加检查和仅当您在范围内时才应用缩放。纹理外部的有效线程将无操作:

[numthreads(8, 8, 1)] void main(uint3 id : SV_DispatchThreadID) { uint2 dimensions; result.GetDimensions(dimensions.x, dimensions.y); if (id.x < dimensions.x && id.y < dimensions.y) mytexture[id.xy] *= myscaling; }

语义值
SV_DispatchThreadID

指的是线程在整个调度内的3D索引,因此

xy
部分可以直接映射到像素位置。
docs
包含有关如何派生的更多信息。 对于调度大小,您必须提供要生成的组(不是线程)的数量:

commandList->Dispatch(texture->GetDesc().Width / 8, texture->GetDesc().Height / 8, 1);

© www.soinside.com 2019 - 2024. All rights reserved.