我有一个包含16位无符号整数(MTLPixelFormatR16Uint
)的MTLTexture
。这些值的范围大约在7000到20000之间,其中0被用作“无数据”值,因此在下面的代码中被跳过。我想找到最小和最大值,以便将这些值重新缩放为0-255。最终,我将基于数据的直方图来确定最小和最大值(它有一些离群值),但现在我只是卡在提取最小/最大值上。
我可以从GPU读取数据到CPU并获取最小/最大值,但更愿意在GPU上执行此任务。
第一次尝试
命令编码器使用每个线程组16x16个线程进行调度,线程组的数量基于纹理大小(例如:width = textureWidth / 16, height = textureHeight / 16)。
typedef struct {
atomic_uint min;
atomic_uint max;
} BandMinMax;
kernel void minMax(texture2d<ushort, access::read> band1 [[texture(0)]],
device BandMinMax &out [[buffer(0)]],
uint2 gid [[thread_position_in_grid]])
{
ushort value = band1.read(gid).r;
if (value != 0) {
uint currentMin = atomic_load_explicit(&out.min, memory_order_relaxed);
uint currentMax = atomic_load_explicit(&out.max, memory_order_relaxed);
if (value > currentMax) {
atomic_store_explicit(&out.max, value, memory_order_relaxed);
}
if (value < currentMin) {
atomic_store_explicit(&out.min, value, memory_order_relaxed);
}
}
}
从这里我得到了一个最小值和最大值,但是对于同样的数据集,最小值和最大值通常会返回不同的值。我相当确定这是在有多个线程运行时从单个线程中获得的最小值和最大值。
第二次尝试
基于之前的尝试,这一次我正在存储每个线程的单独最小/最大值,共256个(16x16)。
kernel void minMax(texture2d<ushort, access::read> band1 [[texture(0)]],
device BandMinMax *out [[buffer(0)]],
uint2 gid [[thread_position_in_grid]],
uint tid [[ thread_index_in_threadgroup ]])
{
ushort value = band1.read(gid).r;
if (value != 0) {
uint currentMin = atomic_load_explicit(&out[tid].min, memory_order_relaxed);
uint currentMax = atomic_load_explicit(&out[tid].max, memory_order_relaxed);
if (value > currentMax) {
atomic_store_explicit(&out[tid].max, value, memory_order_relaxed);
}
if (value < currentMin) {
atomic_store_explicit(&out[tid].min, value, memory_order_relaxed);
}
}
}
这将返回一个包含256组最小/最大值的数组。 我可以从中找到最小值中的最低值,但这似乎是一种不好的方法。 希望能指导正确的方向,谢谢!
atomicBuffer + ((tgpig[1] * tpt[0] + tgpig[0]) * 2)
。我的理解是原子操作逐线程组应用(顺便指正一下我的任何假设,如果错了的话)? 我使用通过tpt
变量中的threads_per_threadgroup
注释传递到内核中的16x16线程按线程组。 我不确定这是我的线程组网格的宽度吗?例如;纹理大小为192x160,线程组网格为12x10,并且偏移计算为atomicBuffer + ((tgpig[1] * 12 + tgpig[0]) * 2)
? - lockthreadgroups_per_grid
替换threads_per_threadgroup
是否可以解决它? - lock