Files
nvidia--tensorrt/plugin/common/kernels/roiPooling.cu
T
wehub-resource-sync c8a779b1bb
Docker Image CI / build-ubuntu2004 (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:36:55 +08:00

354 lines
14 KiB
Plaintext

/*
* SPDX-FileCopyrightText: Copyright (c) 1993-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "common/kernels/kernel.h"
#include <algorithm>
#include <array>
#include <assert.h>
#include <cfloat>
#include <cstdio>
#include <math.h>
#include <stdio.h>
namespace nvinfer1
{
namespace plugin
{
// This macro is to control shared memory usage. If set to 1, kernel loads the whole feature map
// into shared memory for reuse; If set to 0, kernel loads data from global memory directly.
// Roi pooling performance is data dependent. You can test which value is better to your data.
// If all bboxes are very small, 0 is recommended, otherwise, shared memory will load many unused
// data; If bboxes have many overlaps, 1 is recommended to avoid duplicate loads.
// 1 requires larger shared memory size. It may fail if it is larger than CUDA allowed per-block
// shared memory size upper bound. Then you have to use 0.
#define ROIPOOLING_FEATURE_MAP_USE_SHMEM 1
template <typename T>
__device__ T getMax();
template <>
__device__ __forceinline__ int8_t getMax<int8_t>()
{
return INT8_MAX;
}
template <>
__device__ __forceinline__ float getMax<float>()
{
return FLT_MAX;
}
// ROI POOLING FORWARD KERNEL
template <typename DATA_T, typename ROI_T, bool INFER_ONLY, bool FM_IN_SMEM>
__global__ void ROIPoolingForwardKernelAligned(int32_t ROICount, const ROI_T* rois,
int32_t N, // feature map size
int32_t C, // feature map size
int32_t H, // feature map size
int32_t W, // feature map size
const DATA_T* featureMap, const int32_t poolingH, const int32_t poolingW, const float spatialScale, DATA_T* top,
int32_t* maxIds, int32_t fmapStep)
{
extern __shared__ float smem[];
DATA_T* feature_shr = (DATA_T*) &smem[0];
int* rois_shr = nullptr;
if (FM_IN_SMEM)
{
rois_shr = (int*) &feature_shr[H * W];
}
else
{
rois_shr = (int*) &feature_shr[0];
feature_shr = nullptr;
}
const int batch = blockIdx.x / C;
const int channel = blockIdx.x % C;
// load ROIs to shared memory
for (int j = threadIdx.x; j < ROICount; j += blockDim.x)
{
int offset = j << 2;
float4 roi = reinterpret_cast<float4*>(const_cast<float*>(rois))[batch * ROICount + j];
// spatialScale = 1.0 / featureStride
// Convert the coordinates to feature map scale
rois_shr[offset] = round(roi.x * spatialScale); //roi_start_w
rois_shr[offset + 1] = round(roi.y * spatialScale); //roi_start_h
rois_shr[offset + 2] = round(roi.z * spatialScale) - round(roi.x * spatialScale); //roi_length_w
rois_shr[offset + 3] = round(roi.w * spatialScale) - round(roi.y * spatialScale); // roi_length_h
}
// NC/xHW
int fmapOffset = blockIdx.x / fmapStep * H * W * fmapStep + blockIdx.x % fmapStep;
// Assumes #CTAs is just enough to cover all channels of all blocks
const DATA_T* bottom_data_offset = featureMap + fmapOffset;
if (FM_IN_SMEM)
{
// load the current channel to the shared memory
for (int j = threadIdx.x; j < H * W; j += blockDim.x)
{
feature_shr[j] = bottom_data_offset[j * fmapStep];
}
}
__syncthreads();
for (int j = threadIdx.x; j < ROICount; j += blockDim.x)
{
const int offset = j << 2;
// Force malformed ROIs to be 1x1
int roi_start_w = rois_shr[offset];
int roi_start_h = rois_shr[offset + 1];
int roi_width = max(rois_shr[offset + 2] + 1, 1);
int roi_height = max(rois_shr[offset + 3] + 1, 1);
float bin_size_h = static_cast<float>(roi_height) / static_cast<float>(poolingH);
float bin_size_w = static_cast<float>(roi_width) / static_cast<float>(poolingW);
for (int ph = 0; ph < poolingH; ++ph)
{
for (int pw = 0; pw < poolingW; ++pw)
{
int hstart = static_cast<int>(floor(static_cast<float>(ph) * bin_size_h));
int wstart = static_cast<int>(floor(static_cast<float>(pw) * bin_size_w));
int hend = static_cast<int>(ceil(static_cast<float>(ph + 1) * bin_size_h));
int wend = static_cast<int>(ceil(static_cast<float>(pw + 1) * bin_size_w));
// Add roi offsets and clip to input boundaries
// In fact, clipping should be done in the RPN, but just in case...
hstart = min(max(hstart + roi_start_h, 0), H);
hend = min(max(hend + roi_start_h, 0), H);
wstart = min(max(wstart + roi_start_w, 0), W);
wend = min(max(wend + roi_start_w, 0), W);
bool is_empty = (hend <= hstart) || (wend <= wstart);
// Define an empty pooling region to be zero
DATA_T maxval = is_empty ? 0 : -getMax<DATA_T>();
int maxId = -1;
DATA_T data = 0;
for (int h = hstart; h < hend; ++h)
{
for (int w = wstart; w < wend; ++w)
{
int index = h * W + w;
if (FM_IN_SMEM)
{
data = feature_shr[index];
}
else
{
data = bottom_data_offset[index * fmapStep];
}
if (data > maxval)
{
maxval = data;
maxId = index;
}
}
}
top[(((batch * ROICount + j) * C + channel) * poolingH + ph) * poolingW + pw] = maxval;
if (!INFER_ONLY)
{
maxIds[(((batch * ROICount + j) * C + channel) * poolingH + ph) * poolingW + pw] = maxId;
}
} //for:pw
} //for:ph
} // for:j
}
template <typename DATA_T, DLayout_t DATA_L, typename ROI_T, bool INFER_ONLY>
pluginStatus_t ROIPoolingForwardKernelAlignedLauncher(cudaStream_t stream,
const int R, // TOTAL number of rois -> ~nmsMaxOut * N
const int N, // Batch size
const int C, // Channels
const int H, // Input feature map H
const int W, // Input feature map W
const int poolingH, // Output feature map H
const int poolingW, // Output feature map W
const float spatialScale, const void* rois, const void* featureMap, void* top, int* maxIds, size_t deviceSmemSize)
{
size_t roiShmemSize = (R / N) * 4 * sizeof(ROI_T);
#if ROIPOOLING_FEATURE_MAP_USE_SHMEM
size_t shmemSize = H * W * sizeof(DATA_T) + roiShmemSize;
const bool fmap_in_shmem = true;
#else
size_t shmemSize = roiShmemSize;
const bool fmap_in_shmem = false;
#endif
if (shmemSize > deviceSmemSize)
{
return STATUS_BAD_PARAM;
}
// in the aligned version of ROI Pooling R should always be a multiple of N
PLUGIN_ASSERT(R % N == 0);
// NC/xHW
int32_t fmapStep = 1;
switch(DATA_L)
{
case NCHW: fmapStep = 1; break;
case NC4HW:
fmapStep = 4;
PLUGIN_ASSERT((N * C) % 4 == 0);
break;
case NC32HW:
fmapStep = 32;
PLUGIN_ASSERT((N * C) % 32 == 0);
break;
default: PLUGIN_ASSERT(false);
}
if (shmemSize > 48 * 1024)
{
PLUGIN_CHECK(cudaFuncSetAttribute(&ROIPoolingForwardKernelAligned<DATA_T, ROI_T, INFER_ONLY, true>,
cudaFuncAttributeMaxDynamicSharedMemorySize, shmemSize));
}
ROIPoolingForwardKernelAligned<DATA_T, ROI_T, INFER_ONLY, fmap_in_shmem><<<N * C, 256, shmemSize, stream>>>(R / N,
(const ROI_T*) rois,
N, // feature map size
C, // feature map size
H, // feature map size
W, // feature map size
(const DATA_T*) featureMap, poolingH, poolingW, spatialScale, (DATA_T*) top, maxIds, fmapStep);
CSC(cudaGetLastError(), STATUS_FAILURE);
return STATUS_SUCCESS;
}
// ROI POOLING LAUNCH CONFIG
typedef pluginStatus_t (*roiFwd)(cudaStream_t,
const int, //R, // TOTAL number of rois -> ~nmsMaxOut * N
const int, //N, // Batch size
const int, //C, // Channels
const int, //H, // Input feature map H
const int, //W, // Input feature map W
const int, //poolingH, // Output feature map H
const int, //poolingW, // Output feature map W
const float, //spatialScale,
const void*, //rois,
const void*, //featureMap,
void*, //top
int*, //maxIds
size_t); //device shmem size
// struct
struct roiFwdLaunchConfig
{
DataType t_rois;
DataType t_featureMap;
DLayout_t l_featureMap;
DataType t_top;
DLayout_t l_top;
bool inferOnly;
roiFwd function;
roiFwdLaunchConfig(
DataType t_rois, DataType t_featureMap, DLayout_t l_featureMap, DataType t_top, DLayout_t l_top, bool inferOnly)
: t_rois(t_rois)
, t_featureMap(t_featureMap)
, l_featureMap(l_featureMap)
, t_top(t_top)
, l_top(l_top)
, inferOnly(inferOnly)
, function(nullptr)
{
}
roiFwdLaunchConfig(DataType t_rois,
DataType t_featureMap,
DLayout_t l_featureMap,
DataType t_top,
DLayout_t l_top,
bool inferOnly,
roiFwd function)
: t_rois(t_rois)
, t_featureMap(t_featureMap)
, l_featureMap(l_featureMap)
, t_top(t_top)
, l_top(l_top)
, inferOnly(inferOnly)
, function(function)
{
}
bool operator==(roiFwdLaunchConfig const& other) const
{
return (t_rois == other.t_rois)
&& (t_featureMap == other.t_featureMap)
&& (l_featureMap == other.l_featureMap)
&& (t_top == other.t_top)
&& (l_top == other.l_top)
&& (inferOnly == other.inferOnly);
}
};
#define FLOAT32 nvinfer1::DataType::kFLOAT
#define INT8 nvinfer1::DataType::kINT8
static std::array<roiFwdLaunchConfig, 6> roiFwdLCOptions = {
roiFwdLaunchConfig(FLOAT32, FLOAT32, NCHW, FLOAT32, NCHW, true, ROIPoolingForwardKernelAlignedLauncher<float, NCHW, float, true>),
roiFwdLaunchConfig(FLOAT32, FLOAT32, NCHW, FLOAT32, NCHW, false, ROIPoolingForwardKernelAlignedLauncher<float, NCHW, float, false>),
roiFwdLaunchConfig(FLOAT32, INT8, NCHW, INT8, NCHW, true, ROIPoolingForwardKernelAlignedLauncher<int8_t, NCHW, float, true>),
roiFwdLaunchConfig(FLOAT32, INT8, NC4HW, INT8, NCHW, true, ROIPoolingForwardKernelAlignedLauncher<int8_t, NC4HW, float, true>),
roiFwdLaunchConfig(FLOAT32, INT8, NC32HW, INT8, NCHW, true, ROIPoolingForwardKernelAlignedLauncher<int8_t, NC32HW, float, true>),
roiFwdLaunchConfig(FLOAT32, FLOAT32, NC4HW, FLOAT32, NCHW, true, ROIPoolingForwardKernelAlignedLauncher<float, NC4HW, float, true>)};
// ROI INFERENCE
pluginStatus_t roiInference(cudaStream_t stream,
const int R, // TOTAL number of rois -> ~nmsMaxOut * N
const int N, // Batch size
const int C, // Channels
const int H, // Input feature map H
const int W, // Input feature map W
const int poolingH, // Output feature map H
const int poolingW, // Output feature map W
const float spatialScale,
const nvinfer1::DataType t_rois,
const void* rois,
const nvinfer1::DataType t_featureMap,
const DLayout_t l_featureMap,
const void* featureMap,
const nvinfer1::DataType t_top,
const DLayout_t l_top,
void* top,
size_t deviceSmemSize)
{
if (featureMap == NULL || rois == NULL || top == NULL)
{
return STATUS_BAD_PARAM;
}
DEBUG_PRINTF("&&&& ROIS %u\n", hash(rois, R * 4 * sizeof(float)));
DEBUG_PRINTF("&&&& FMAP %u\n", hash(featureMap, N * C * H * W * sizeof(float)));
roiFwdLaunchConfig rflc = roiFwdLaunchConfig(t_rois, t_featureMap, l_featureMap, t_top, l_top, true);
ASSERT_PARAM(R > 0);
for (unsigned i = 0; i < roiFwdLCOptions.size(); i++)
{
if (rflc == roiFwdLCOptions[i])
{
DEBUG_PRINTF("$$$$ ROI KERNEL %d\n", i);
return roiFwdLCOptions[i].function(stream,
R, N, C, H, W, poolingH, poolingW,
spatialScale, rois, featureMap, top, NULL, deviceSmemSize);
}
}
return STATUS_BAD_PARAM;
}
} // namespace plugin
} // namespace nvinfer1