使用CUDA的调色板热图

使用CUDA的调色板热图,cuda,Cuda,我使用CUDA使用光线跟踪器在屏幕上渲染场景,并想知道屏幕上最热的点是什么:我测量clock64返回的值之间的差异,以了解屏幕上每个像素的运行时间: float start = clock64(); frame[y * w + x] = TraceRay(x, y, w, h); counters[y * w + x] = clock64() - start;` 目前,我执行以下操作以获得热图: auto p = thrust::cuda::par.on(stream); thrust::de

我使用CUDA使用光线跟踪器在屏幕上渲染场景,并想知道屏幕上最热的点是什么:我测量clock64返回的值之间的差异,以了解屏幕上每个像素的运行时间:

float start = clock64();
frame[y * w + x] = TraceRay(x, y, w, h);
counters[y * w + x] = clock64() - start;`
目前,我执行以下操作以获得热图:

auto p = thrust::cuda::par.on(stream);
thrust::device_ptr< const float > c = thrust::device_pointer_cast(counters);
auto m = thrust::minmax_element(p, c, c + w * h);
thrust::device_ptr< Color > f = thrust::device_pointer_cast(frame);
#ifndef __CUDACC_EXTENDED_LAMBDA__
#error "nvcc --expt-extended-lambda"
#endif
auto l = [=] __device__ (float c) -> Color
{
    auto color = (c - *m.first) / float(*m.second - *m.first);
    return {color, 0.0f, 1.0f - color, 1.0f};
};
thrust::transform(p, c, c + w * h, f, l);
但结果的分布太广,大多数是蓝色的,最有趣的像素的一些小子集几乎变成了红色的热点,很难计算。几乎没有有价值的梯度

另外,我想通过调色板定义热图,在参考点百分位中为计数器值定义颜色。就像它可以在gnuplot中完成一样:

我想我可以对计数器中的所有值进行排序,并对其应用调色板:

首先,我需要对计数器中的所有值进行排序,这些值以前是通过空颜色字段和坐标x、y扩展的,或者仅仅是通过源线性数组中的索引来扩展的。只有来自计数器的值才能参和排序的比较器。 然后应用调色板。只需将调色板中给定的颜色的分段线性插值值分配给颜色字段,该值映射到[0;1]区间,然后映射到排序数组的线性索引

毕竟,我可以使用x和y坐标绘制所有颜色,或者通过线性索引字段对它们进行排序


CUDA在算法实现中是否有一席之地?据我所知,有基数排序,但它是否适用于键不跨整个结构的结构?

以下解决方案并不完美,因为排序可以完全避免,而有利于排列;内存可以重复使用,而不是每下一帧重新分配;调色板可以进行扩展,但相当可行。它只绘制大多数重块的上5%的红色阴影。所有其他人都被画成从灰色到蓝色的阴影

__global__
void drawHeatmap(unsigned int w, unsigned int h, const Color * heatmap, cudaSurfaceObject_t frame)
{
    unsigned int x = blockIdx.x * blockDim.x + threadIdx.x;
    unsigned int y = blockIdx.y * blockDim.y + threadIdx.y;
    if ((x >= w) || (y >= h)) {
        return;
    }
    surf2Dwrite< Color >(heatmap[w * y + x], frame, sizeof(Color) * x, y);
}

void CudaRaytracer::buildHeatmap(cudaStream_t stream,
                                 unsigned int w, unsigned int h,
                                 float * counters,
                                 cudaSurfaceObject_t frame)
{
    assert(counters);
    auto p = thrust::cuda::par.on(stream);
    thrust::device_ptr< float > c = thrust::device_pointer_cast(counters);
    const auto size = w * h;
    thrust::device_vector< unsigned int > indices(size);
    thrust::sequence(p, indices.begin(), indices.end());
    thrust::sort_by_key(p, c, c + size, indices.begin());
#ifndef __CUDACC_EXTENDED_LAMBDA__
#error "nvcc --expt-extended-lambda"
#endif
    auto make_palette = [=] __device__ (unsigned int index) -> Color
    {
        constexpr unsigned int palette_size = 3;
        static const float reference_points[palette_size] = {0.0f, 0.95f, 1.0f};
        float pos = index / float(size);
        unsigned int i = 0;
        for (; i < palette_size; ++i) {
            if (pos < reference_points[i]) {
                break;
            }
        }
        __syncwarp();
        static const Color palette[palette_size] = {{0.0f, 1.0f, 0.0f, 1.0f}, {0.0f, 0.0f, 1.0f, 1.0f}, {1.0f, 0.0f, 0.0f, 1.0f}};
        float weight = (pos - reference_points[i - 1]) / (reference_points[i] - reference_points[i - 1]);
        return weight * (palette[i - 1] - palette[i]) + palette[i];
    };
    thrust::device_vector< Color > heatmap(size);
    auto index = thrust::make_counting_iterator(0u);
    thrust::transform(p, index, index + size, heatmap.begin(), make_palette);
    thrust::sort_by_key(p, indices.begin(), indices.end(), heatmap.begin());
    auto gridSize = deriveGridSize(w, h);
    drawHeatmap<<< gridSize, blockSize, 0, stream >>>(w, h, heatmap.data().get(), frame);
    cudaStreamSynchronize(stream);
}

在GeForce RTX 2060上,渲染时间约为30毫秒。

太宽了?!我要求一个具体算法的实现。也许今天我可以自己回答。但我想从社区中得到直觉。