Следующее решение не является идеальным (одного из сортов можно полностью избежать в пользу перестановки; память можно повторно использовать вместо перераспределения в каждом следующем кадре; палитра может быть расширена), но вполне работоспособна. Он рисует в оттенках красного только верхние 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 мс.