/* * SPDX-FileCopyrightText: Copyright (c) 1993-2026 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 "regionPlugin.h" #include #include namespace nvinfer1::plugin { namespace { char const* const kREGION_PLUGIN_VERSION{"1"}; char const* const kREGION_PLUGIN_NAME{"Region_TRT"}; template void safeFree(T* ptr) { if (ptr) { free(ptr); ptr = nullptr; } } template void allocateChunk(T*& ptr, int32_t count) { ptr = static_cast(malloc(count * sizeof(T))); } struct SoftmaxTreeDeleter { void operator()(softmaxTree* smTree) const { if (smTree) { // free individual elements first safeFree(smTree->leaf); safeFree(smTree->parent); safeFree(smTree->child); safeFree(smTree->group); if (smTree->name) { for (int32_t i = 0; i < smTree->n; i++) { safeFree(smTree->name[i]); } safeFree(smTree->name); } safeFree(smTree->groupSize); safeFree(smTree->groupOffset); // free softmax tree safeFree(smTree); } } }; } // namespace Region::Region(RegionParameters params) : num(params.num) , coords(params.coords) , classes(params.classes) , smTree(params.smTree, SoftmaxTreeDeleter()) { } Region::Region(RegionParameters params, int32_t C, int32_t H, int32_t W) : num(params.num) , coords(params.coords) , classes(params.classes) , smTree(params.smTree, SoftmaxTreeDeleter()) , C(C) , H(H) , W(W) { } // NOLINTNEXTLINE(readability-function-cognitive-complexity) Region::Region(void const* buffer, size_t length) { char const *d = reinterpret_cast(buffer), *a = d; C = read(d); H = read(d); W = read(d); num = read(d); classes = read(d); coords = read(d); bool softmaxTreePresent = read(d); bool leafPresent = read(d); bool parentPresent = read(d); bool childPresent = read(d); bool groupPresent = read(d); bool namePresent = read(d); bool groupSizePresent = read(d); bool groupOffsetPresent = read(d); if (softmaxTreePresent) { softmaxTree* smTreeTemp; // need to read each element individually allocateChunk(smTreeTemp, 1); smTreeTemp->n = read(d); if (leafPresent) { allocateChunk(smTreeTemp->leaf, smTreeTemp->n); } else { smTreeTemp->leaf = nullptr; } if (parentPresent) { allocateChunk(smTreeTemp->parent, smTreeTemp->n); } else { smTreeTemp->parent = nullptr; } if (childPresent) { allocateChunk(smTreeTemp->child, smTreeTemp->n); } else { smTreeTemp->child = nullptr; } if (groupPresent) { allocateChunk(smTreeTemp->group, smTreeTemp->n); } else { smTreeTemp->group = nullptr; } for (int32_t i = 0; i < smTreeTemp->n; i++) { if (leafPresent) { smTreeTemp->leaf[i] = read(d); } if (parentPresent) { smTreeTemp->parent[i] = read(d); } if (childPresent) { smTreeTemp->child[i] = read(d); } if (groupPresent) { smTreeTemp->group[i] = read(d); } } if (namePresent) { allocateChunk(smTreeTemp->name, smTreeTemp->n); } else { smTreeTemp->name = nullptr; } if (namePresent) { for (int32_t i = 0; i < smTreeTemp->n; i++) { allocateChunk(smTreeTemp->name[i], 256); for (int32_t j = 0; j < 256; j++) { smTreeTemp->name[i][j] = read(d); } } } smTreeTemp->groups = read(d); if (groupSizePresent) { allocateChunk(smTreeTemp->groupSize, smTreeTemp->groups); } else { smTreeTemp->groupSize = nullptr; } if (groupOffsetPresent) { allocateChunk(smTreeTemp->groupOffset, smTreeTemp->groups); } else { smTreeTemp->groupOffset = nullptr; } for (int32_t i = 0; i < smTreeTemp->groups; i++) { if (groupSizePresent) { smTreeTemp->groupSize[i] = read(d); } if (groupOffsetPresent) { smTreeTemp->groupOffset[i] = read(d); } } smTree = std::shared_ptr(smTreeTemp, SoftmaxTreeDeleter()); } else { smTree.reset(); } PLUGIN_VALIDATE(d == a + length); } int32_t Region::getNbOutputs() const noexcept { return 1; } Dims Region::getOutputDimensions(int32_t index, Dims const* inputs, int32_t nbInputDims) noexcept { PLUGIN_ASSERT(nbInputDims == 1); PLUGIN_ASSERT(index == 0); return inputs[0]; } int32_t Region::enqueue( int32_t batchSize, void const* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept { void const* inputData = inputs[0]; void* outputData = outputs[0]; if (smTree.get()) { hasSoftmaxTree = true; } else { hasSoftmaxTree = false; } pluginStatus_t status = regionInference( stream, batchSize, C, H, W, num, coords, classes, hasSoftmaxTree, smTree.get(), inputData, outputData); return status; } size_t Region::getSerializationSize() const noexcept { // C, H, W, num, classes, coords, smTree !nullptr and other array members !nullptr, softmaxTree members size_t count = 6 * sizeof(int32_t) + 8 * sizeof(bool); if (smTree.get()) { count += 2 * sizeof(int32_t); if (smTree->leaf) { count += smTree->n * sizeof(int32_t); } if (smTree->parent) { count += smTree->n * sizeof(int32_t); } if (smTree->child) { count += smTree->n * sizeof(int32_t); } if (smTree->group) { count += smTree->n * sizeof(int32_t); } if (smTree->name) { count += smTree->n * 256 * sizeof(char); } if (smTree->groupSize) { count += smTree->groups * sizeof(int32_t); } if (smTree->groupOffset) { count += smTree->groups * sizeof(int32_t); } } return count; } // NOLINTNEXTLINE(readability-function-cognitive-complexity) void Region::serialize(void* buffer) const noexcept { char *d = reinterpret_cast(buffer), *a = d; write(d, C); write(d, H); write(d, W); write(d, num); write(d, classes); write(d, coords); write(d, smTree != nullptr); write(d, smTree != nullptr && smTree->leaf != nullptr); write(d, smTree != nullptr && smTree->parent != nullptr); write(d, smTree != nullptr && smTree->child != nullptr); write(d, smTree != nullptr && smTree->group != nullptr); write(d, smTree != nullptr && smTree->name != nullptr); write(d, smTree != nullptr && smTree->groupSize != nullptr); write(d, smTree != nullptr && smTree->groupOffset != nullptr); // need to do a deep copy if (smTree) { write(d, smTree->n); for (int32_t i = 0; i < smTree->n; i++) { if (smTree->leaf) { write(d, smTree->leaf[i]); } if (smTree->parent) { write(d, smTree->parent[i]); } if (smTree->child) { write(d, smTree->child[i]); } if (smTree->group) { write(d, smTree->group[i]); } } if (smTree->name) { for (int32_t i = 0; i < smTree->n; i++) { char const* str = smTree->name[i]; for (int32_t j = 0; j < 256; j++) { write(d, str[j]); } } } write(d, smTree->groups); for (int32_t i = 0; i < smTree->groups; i++) { if (smTree->groupSize) { write(d, smTree->groupSize[i]); } if (smTree->groupOffset) { write(d, smTree->groupOffset[i]); } } } PLUGIN_ASSERT(d == a + getSerializationSize()); } bool Region::supportsFormat(DataType type, PluginFormat format) const noexcept { return (type == DataType::kFLOAT && format == PluginFormat::kLINEAR); } int32_t Region::initialize() noexcept { return STATUS_SUCCESS; } void Region::terminate() noexcept {} char const* Region::getPluginType() const noexcept { return kREGION_PLUGIN_NAME; } char const* Region::getPluginVersion() const noexcept { return kREGION_PLUGIN_VERSION; } size_t Region::getWorkspaceSize(int32_t maxBatchSize) const noexcept { return 0; } void Region::destroy() noexcept { delete this; } IPluginV2Ext* Region::clone() const noexcept { try { RegionParameters params{num, coords, classes, nullptr}; auto plugin = std::make_unique(params, C, H, W); plugin->setPluginNamespace(mPluginNamespace.c_str()); plugin->setSoftmaxTree(smTree); return plugin.release(); } catch (std::exception const& e) { caughtError(e); } return nullptr; } // Set plugin namespace void Region::setPluginNamespace(char const* pluginNamespace) noexcept { mPluginNamespace = pluginNamespace; } char const* Region::getPluginNamespace() const noexcept { return mPluginNamespace.c_str(); } // Return the DataType of the plugin output at the requested index DataType Region::getOutputDataType(int32_t index, nvinfer1::DataType const* inputTypes, int32_t nbInputs) const noexcept { PLUGIN_ASSERT(index == 0); return DataType::kFLOAT; } // Configure the layer with input and output data types. void Region::configurePlugin(Dims const* inputDims, int32_t nbInputs, Dims const* outputDims, int32_t nbOutputs, DataType const* inputTypes, DataType const* outputTypes, bool const* inputIsBroadcast, bool const* outputIsBroadcast, PluginFormat floatFormat, int32_t maxBatchSize) noexcept { PLUGIN_ASSERT(*inputTypes == DataType::kFLOAT && floatFormat == PluginFormat::kLINEAR); PLUGIN_ASSERT(nbInputs == 1); PLUGIN_ASSERT(nbOutputs == 1); C = inputDims[0].d[0]; H = inputDims[0].d[1]; W = inputDims[0].d[2]; /* * In the below assertion, 1 stands for the objectness of the bounding box * We should also * PLUGIN_ASSERT(coords == 4); */ PLUGIN_ASSERT(C == num * (coords + 1 + classes)); } // Attach the plugin object to an execution context and grant the plugin the access to some context resource. void Region::attachToContext( cudnnContext* cudnnContext, cublasContext* cublasContext, IGpuAllocator* gpuAllocator) noexcept { } // Detach the plugin object from its execution context. void Region::detachFromContext() noexcept {} RegionPluginCreator::RegionPluginCreator() { mPluginAttributes.clear(); mPluginAttributes.emplace_back(PluginField("num", nullptr, PluginFieldType::kINT32, 1)); mPluginAttributes.emplace_back(PluginField("coords", nullptr, PluginFieldType::kINT32, 1)); mPluginAttributes.emplace_back(PluginField("classes", nullptr, PluginFieldType::kINT32, 1)); mPluginAttributes.emplace_back(PluginField("smTree", nullptr, PluginFieldType::kINT32, 1)); mFC.nbFields = mPluginAttributes.size(); mFC.fields = mPluginAttributes.data(); } char const* RegionPluginCreator::getPluginName() const noexcept { return kREGION_PLUGIN_NAME; } char const* RegionPluginCreator::getPluginVersion() const noexcept { return kREGION_PLUGIN_VERSION; } PluginFieldCollection const* RegionPluginCreator::getFieldNames() noexcept { return &mFC; } IPluginV2Ext* RegionPluginCreator::createPlugin(char const* name, PluginFieldCollection const* fc) noexcept { try { using namespace std::string_view_literals; PluginField const* fields = fc->fields; for (int32_t i = 0; i < fc->nbFields; ++i) { std::string_view const attrName = fields[i].name; if (attrName == "num"sv) { PLUGIN_VALIDATE(fields[i].type == PluginFieldType::kINT32); params.num = *(static_cast(fields[i].data)); } if (attrName == "coords"sv) { PLUGIN_VALIDATE(fields[i].type == PluginFieldType::kINT32); params.coords = *(static_cast(fields[i].data)); } if (attrName == "classes"sv) { PLUGIN_VALIDATE(fields[i].type == PluginFieldType::kINT32); params.classes = *(static_cast(fields[i].data)); } if (attrName == "smTree"sv) { // TODO not sure if this will work void* tmpData = const_cast(fields[i].data); params.smTree = static_cast(tmpData); } } auto obj = std::make_unique(params); obj->setPluginNamespace(mNamespace.c_str()); return obj.release(); } catch (std::exception const& e) { caughtError(e); } return nullptr; } IPluginV2Ext* RegionPluginCreator::deserializePlugin( char const* name, void const* serialData, size_t serialLength) noexcept { try { // This object will be deleted when the network is destroyed, which will // call Region::destroy() auto obj = std::make_unique(serialData, serialLength); obj->setPluginNamespace(mNamespace.c_str()); return obj.release(); } catch (std::exception const& e) { caughtError(e); } return nullptr; } } // namespace nvinfer1::plugin