Modify RoiPooling to use OperationResolver and support float16.

Bug: 118608492
Test: NeuralNetworksTest_static
Change-Id: I164c58c8105ef8ceed23144423b472d586faa8c9
Merged-In: I164c58c8105ef8ceed23144423b472d586faa8c9
(cherry picked from commit b14b6166078db0092eff2d46ee4a7ee0c63c7d8c)
diff --git a/common/Android.bp b/common/Android.bp
index 2c5efc9..7eb03eb 100644
--- a/common/Android.bp
+++ b/common/Android.bp
@@ -34,6 +34,7 @@
         "operations/LogSoftmax.cpp",
         "operations/PRelu.cpp",
         "operations/Reduce.cpp",
+        "operations/RoiPooling.cpp",
         "operations/Select.cpp",
     ],
 }
@@ -115,7 +116,6 @@
         "operations/Reshape.cpp",
         "operations/RNN.cpp",
         "operations/RoiAlign.cpp",
-        "operations/RoiPooling.cpp",
         "operations/SimpleMath.cpp",
         "operations/Slice.cpp",
         "operations/Split.cpp",
diff --git a/common/CpuExecutor.cpp b/common/CpuExecutor.cpp
index a1138b2..1e3a5db 100644
--- a/common/CpuExecutor.cpp
+++ b/common/CpuExecutor.cpp
@@ -2252,47 +2252,6 @@
                 break;
             }
         } break;
-        case OperationType::ROI_POOLING: {
-            if (!allParametersPresent(5, 1)) {
-                return ANEURALNETWORKS_BAD_DATA;
-            }
-            const RunTimeOperandInfo& input = mOperands[ins[0]];
-            const RunTimeOperandInfo& roi = mOperands[ins[1]];
-            const RunTimeOperandInfo& outputShape = mOperands[ins[2]];
-            const float spatialScale = getScalarData<float>(mOperands[ins[3]]);
-            const bool data_layout = getScalarData<bool>(mOperands[ins[4]]);
-
-            RunTimeOperandInfo& out = mOperands[outs[0]];
-            Shape outShape = out.shape();
-
-            RunTimeOperandInfo input_tmp, out_tmp;
-            std::unique_ptr<uint8_t[]> input_tmp_guard, out_tmp_guard;
-            if (!convertToNhwc(input_tmp, input, input_tmp_guard, data_layout)) {
-                success = false;
-                break;
-            }
-            out_tmp.lifetime = OperandLifeTime::TEMPORARY_VARIABLE;
-            out_tmp.buffer = data_layout ? nullptr : out.buffer;
-
-            if (!roiAlignPrepare(input_tmp.shape(), reinterpret_cast<const float*>(roi.buffer),
-                                 roi.shape(), reinterpret_cast<const int32_t*>(outputShape.buffer),
-                                 outputShape.shape(), spatialScale, &outShape) ||
-                !setInfoAndAllocateIfNeeded(&out_tmp, outShape)) {
-                success = false;
-                break;
-            }
-
-            success = roiPoolingGeneric(input_tmp.buffer, input_tmp.shape(), roi.buffer,
-                                        roi.shape(), spatialScale, out_tmp.buffer, outShape);
-
-            if (data_layout) {
-                out_tmp_guard.reset(out_tmp.buffer);
-            }
-            if (!success || !convertFromNhwc(out, out_tmp, data_layout)) {
-                success = false;
-                break;
-            }
-        } break;
         case OperationType::MAXIMUM:
         case OperationType::MINIMUM: {
             if (!allParametersPresent(2, 1)) {
diff --git a/common/OperationResolver.cpp b/common/OperationResolver.cpp
index 8e612eb..54ff386 100644
--- a/common/OperationResolver.cpp
+++ b/common/OperationResolver.cpp
@@ -45,6 +45,7 @@
 const OperationRegistration* register_REDUCE_MIN();
 const OperationRegistration* register_REDUCE_PROD();
 const OperationRegistration* register_REDUCE_SUM();
+const OperationRegistration* register_ROI_POOLING();
 const OperationRegistration* register_SELECT();
 
 OperationResolver::OperationResolver() {
@@ -69,6 +70,7 @@
     registerOperation(register_REDUCE_MIN());
     registerOperation(register_REDUCE_PROD());
     registerOperation(register_REDUCE_SUM());
+    registerOperation(register_ROI_POOLING());
     registerOperation(register_SELECT());
 }
 
diff --git a/common/Utils.cpp b/common/Utils.cpp
index 0a1ddaf..da3dcf6 100644
--- a/common/Utils.cpp
+++ b/common/Utils.cpp
@@ -2189,30 +2189,6 @@
                                                  inExpectedTypes, outputCount, outputIndexes,
                                                  outExpectedTypes);
         }
-        case ANEURALNETWORKS_ROI_POOLING: {
-            if (inputCount != 5 || outputCount != 1) {
-                logInvalidInOutNumber(5, 1);
-                return ANEURALNETWORKS_BAD_DATA;
-            }
-            std::vector<OperandType> inExpectedTypes;
-            std::vector<OperandType> outExpectedTypes;
-            auto inputType = operands[inputIndexes[0]].type;
-            if (inputType == OperandType::TENSOR_FLOAT32 ||
-                inputType == OperandType::TENSOR_QUANT8_ASYMM) {
-                inExpectedTypes = {inputType, OperandType::TENSOR_FLOAT32,
-                                   OperandType::TENSOR_INT32, OperandType::FLOAT32,
-                                   OperandType::BOOL};
-                outExpectedTypes = {inputType};
-            } else {
-                LOG(ERROR) << "Unsupported input tensor type for operation "
-                           << kOperationNames[opType];
-                return ANEURALNETWORKS_BAD_DATA;
-            }
-            NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2));
-            return validateOperationOperandTypes(operands, inputCount, inputIndexes,
-                                                 inExpectedTypes, outputCount, outputIndexes,
-                                                 outExpectedTypes);
-        }
         case ANEURALNETWORKS_MAXIMUM:
         case ANEURALNETWORKS_MINIMUM: {
             if (inputCount != 2 || outputCount != 1) {
diff --git a/common/include/CpuOperationUtils.h b/common/include/CpuOperationUtils.h
index 2976b4e..fb1996a 100644
--- a/common/include/CpuOperationUtils.h
+++ b/common/include/CpuOperationUtils.h
@@ -91,6 +91,81 @@
     return true;
 }
 
+template <typename T>
+inline bool convertNhwcToNchw(const std::vector<T>& nhwc, const Shape& nhwcShape, T* nchw) {
+    NN_RET_CHECK_EQ(getNumberOfDimensions(nhwcShape), 4)
+            << "Error converting a non-4-D tensor to NCHW layout";
+    const auto& fromDim = nhwcShape.dimensions;
+    const auto from = nhwc.data();
+    uint32_t spatialSize = fromDim[1] * fromDim[2];
+    for (uint32_t n = 0; n < fromDim[0]; n++) {
+        for (uint32_t c = 0; c < fromDim[3]; c++) {
+            for (uint32_t hw = 0; hw < spatialSize; hw++) {
+                uint32_t fromIndex = n * spatialSize * fromDim[3] + hw * fromDim[3] + c;
+                *nchw++ = from[fromIndex];
+            }
+        }
+    }
+    return true;
+}
+
+template <typename T>
+class InputWithLayout {
+   public:
+    InputWithLayout(bool useNchw) : mDataOriginal(nullptr), mUseNchw(useNchw) {}
+
+    bool initialize(const T* data, const Shape& shape) {
+        mDataOriginal = data;
+        mShape = shape;
+        if (mUseNchw) {
+            return convertNchwToNhwc(mDataOriginal, shape, &mDataNhwc, &mShape);
+        }
+        return true;
+    }
+
+    const T* getNhwcBuffer() { return mUseNchw ? mDataNhwc.data() : mDataOriginal; }
+    const Shape& getNhwcShape() { return mShape; }
+
+   private:
+    const T* mDataOriginal;
+    std::vector<T> mDataNhwc;
+    Shape mShape;
+    bool mUseNchw;
+};
+
+template <typename T>
+class OutputWithLayout {
+   public:
+    OutputWithLayout(bool useNchw) : mDataOriginal(nullptr), mUseNchw(useNchw) {}
+
+    bool initialize(T* data, const Shape& shape) {
+        NN_RET_CHECK_EQ(getNumberOfDimensions(shape), 4);
+        mDataOriginal = data;
+        mShape = shape;
+        if (mUseNchw) {
+            const auto& dim = shape.dimensions;
+            mShape.dimensions = {dim[0], dim[2], dim[3], dim[1]};
+            mDataNhwc.resize(getNumberOfElements(shape));
+        }
+        return true;
+    }
+
+    T* getNhwcBuffer() { return mUseNchw ? mDataNhwc.data() : mDataOriginal; }
+    const Shape& getNhwcShape() { return mShape; }
+    bool commit() {
+        if (mUseNchw) {
+            return convertNhwcToNchw(mDataNhwc, mShape, mDataOriginal);
+        }
+        return true;
+    }
+
+   private:
+    T* mDataOriginal;
+    std::vector<T> mDataNhwc;
+    Shape mShape;
+    bool mUseNchw;
+};
+
 } // nn
 } // android
 
diff --git a/common/include/Operations.h b/common/include/Operations.h
index e0f878a..4e0c59c 100644
--- a/common/include/Operations.h
+++ b/common/include/Operations.h
@@ -286,10 +286,6 @@
                     const Shape& roiShape, float spatialScale, int32_t samplingRatio,
                     uint8_t* outputData, const Shape& outputShape);
 
-bool roiPoolingGeneric(const uint8_t* inputData, const Shape& inputShape, const uint8_t* roiData,
-                       const Shape& roiShape, float spatialScale, uint8_t* outputData,
-                       const Shape& outputShape);
-
 bool groupedConvFloat16(const _Float16* inputData, const Shape& inputShape,
                         const _Float16* filterData, const Shape& filterShape,
                         const _Float16* biasData, const Shape& biasShape, int32_t numGroups,
diff --git a/common/operations/RoiPooling.cpp b/common/operations/RoiPooling.cpp
index 591617f..6050feb 100644
--- a/common/operations/RoiPooling.cpp
+++ b/common/operations/RoiPooling.cpp
@@ -15,7 +15,8 @@
  */
 
 #include "CpuOperationUtils.h"
-#include "Operations.h"
+#include "OperationResolver.h"
+#include "OperationsUtils.h"
 
 #include <cfloat>
 #include <cmath>
@@ -24,15 +25,29 @@
 
 namespace android {
 namespace nn {
+namespace roi_pooling {
 
-template <typename T_Input>
-bool roiPoolingImpl(const T_Input* inputData, const Shape& inputShape, const float* roiData,
-                    const Shape& roiShape, float spatialScale, T_Input* outputData,
-                    const Shape& outputShape) {
+constexpr char kOperationName[] = "ROI_POOLING";
+
+constexpr uint32_t kNumInputs = 5;
+constexpr uint32_t kInputTensor = 0;
+constexpr uint32_t kRoiTensor = 1;
+constexpr uint32_t kOutputShapeTensor = 2;
+constexpr uint32_t kSpacialScaleScalar = 3;
+constexpr uint32_t kLayoutScalar = 4;
+
+constexpr uint32_t kNumOutputs = 1;
+constexpr uint32_t kOutputTensor = 0;
+
+namespace {
+
+template <typename T_Input, typename T_Roi>
+inline bool roiPoolingNhwc(const T_Input* inputData, const Shape& inputShape, const T_Roi* roiData,
+                           const Shape& roiShape, T_Roi spatialScale, T_Input* outputData,
+                           const Shape& outputShape) {
     NNTRACE_TRANS("RoiPooling");
 
-    const uint32_t kRoiDim = 4;
-
+    uint32_t numBatches = getSizeOfDimension(inputShape, 0);
     uint32_t inHeight = getSizeOfDimension(inputShape, 1);
     uint32_t inWidth = getSizeOfDimension(inputShape, 2);
     uint32_t inDepth = getSizeOfDimension(inputShape, 3);
@@ -41,36 +56,54 @@
     uint32_t numRois = getSizeOfDimension(roiShape, 0);
     uint32_t roiInfoLength = getSizeOfDimension(roiShape, 1);
 
+    const uint32_t kRoiDim = 4;
     T_Input* outPtr = outputData;
-    const float* roiDataEnd = roiData + numRois * roiInfoLength;
-    for (const float* roiInfo = roiData; roiInfo < roiDataEnd; roiInfo += kRoiDim) {
+    const T_Roi* roiDataEnd = roiData + numRois * roiInfoLength;
+    for (const T_Roi* roiInfo = roiData; roiInfo < roiDataEnd; roiInfo += kRoiDim) {
         uint32_t batchId = 0;
         // get optional batch id
         if (roiInfoLength == kRoiDim + 1) {
-            batchId = std::round(roiInfo[0]);
+            batchId = std::round(static_cast<float>(roiInfo[0]));
             roiInfo++;
         }
-        const T_Input* batchBase = inputData + batchId * inHeight * inWidth * inDepth;
 
-        int32_t wRoiStart = std::round(roiInfo[0] * spatialScale);
-        int32_t hRoiStart = std::round(roiInfo[1] * spatialScale);
-        int32_t wRoiEnd = std::round(roiInfo[2] * spatialScale);
-        int32_t hRoiEnd = std::round(roiInfo[3] * spatialScale);
+        // Check for malformed data
+        // 1. invalid batch id
+        // 2. Region out of bound: x1|x2|y1|y2 < 0 || x1|x2 > inWidth || y1|y2 > inHeight
+        // 3. Invalid region: x2 <= x1 || y2 <= y1
+        NN_RET_CHECK_GE(batchId, 0);
+        NN_RET_CHECK_LT(batchId, numBatches);
+        NN_RET_CHECK(roiInfo[0] >= 0);
+        NN_RET_CHECK(roiInfo[1] >= 0);
+        NN_RET_CHECK(roiInfo[2] >= 0);
+        NN_RET_CHECK(roiInfo[3] >= 0);
+        NN_RET_CHECK(roiInfo[0] * spatialScale <= inWidth);
+        NN_RET_CHECK(roiInfo[1] * spatialScale <= inHeight);
+        NN_RET_CHECK(roiInfo[2] * spatialScale <= inWidth);
+        NN_RET_CHECK(roiInfo[3] * spatialScale <= inHeight);
+        NN_RET_CHECK(roiInfo[0] < roiInfo[2]);
+        NN_RET_CHECK(roiInfo[1] < roiInfo[3]);
+
+        int32_t wRoiStart = std::round(static_cast<float>(roiInfo[0] * spatialScale));
+        int32_t hRoiStart = std::round(static_cast<float>(roiInfo[1] * spatialScale));
+        int32_t wRoiEnd = std::round(static_cast<float>(roiInfo[2] * spatialScale));
+        int32_t hRoiEnd = std::round(static_cast<float>(roiInfo[3] * spatialScale));
 
         // Rois with width/height < 1 are considered malformed and are forced to be 1
-        float roiWidth = static_cast<float>(std::max(wRoiEnd - wRoiStart + 1, 1));
-        float roiHeight = static_cast<float>(std::max(hRoiEnd - hRoiStart + 1, 1));
-        float wStepSize = roiWidth / static_cast<float>(outWidth);
-        float hStepSize = roiHeight / static_cast<float>(outHeight);
+        T_Roi roiWidth = static_cast<T_Roi>(std::max(wRoiEnd - wRoiStart + 1, 1));
+        T_Roi roiHeight = static_cast<T_Roi>(std::max(hRoiEnd - hRoiStart + 1, 1));
+        T_Roi wStepSize = roiWidth / static_cast<T_Roi>(outWidth);
+        T_Roi hStepSize = roiHeight / static_cast<T_Roi>(outHeight);
 
+        const T_Input* batchBase = inputData + batchId * inHeight * inWidth * inDepth;
         for (uint32_t i = 0; i < outHeight; i++) {
             for (uint32_t j = 0; j < outWidth; j++) {
                 // Take floor on start, ceil on end, start included, end excluded, i.e. [start, end)
                 // end is guaranteed to larger than start by at least 1
-                uint32_t wStart = std::floor(wStepSize * j + wRoiStart);
-                uint32_t wEnd = std::ceil(wStepSize * (j + 1) + wRoiStart);
-                uint32_t hStart = std::floor(hStepSize * i + hRoiStart);
-                uint32_t hEnd = std::ceil(hStepSize * (i + 1) + hRoiStart);
+                uint32_t wStart = std::floor(static_cast<float>(wStepSize * j + wRoiStart));
+                uint32_t wEnd = std::ceil(static_cast<float>(wStepSize * (j + 1) + wRoiStart));
+                uint32_t hStart = std::floor(static_cast<float>(hStepSize * i + hRoiStart));
+                uint32_t hEnd = std::ceil(static_cast<float>(hStepSize * (i + 1) + hRoiStart));
 
                 wStart = std::min(wStart, inWidth);
                 wEnd = std::min(wEnd, inWidth);
@@ -98,25 +131,117 @@
     return true;
 }
 
-bool roiPoolingGeneric(const uint8_t* inputData, const Shape& inputShape, const uint8_t* roiData,
-                       const Shape& roiShape, float spatialScale, uint8_t* outputData,
+template <typename T_Input, typename T_Roi>
+inline bool roiPooling(const T_Input* inputData, const Shape& inputShape, const T_Roi* roiData,
+                       const Shape& roiShape, T_Roi spatialScale, bool useNchw, T_Input* outputData,
                        const Shape& outputShape) {
-    NNTRACE_TRANS("roiPoolingGeneric");
-    if (inputShape.type == OperandType::TENSOR_FLOAT32) {
-        return roiPoolingImpl<float>(reinterpret_cast<const float*>(inputData), inputShape,
-                                     reinterpret_cast<const float*>(roiData), roiShape,
-                                     spatialScale, reinterpret_cast<float*>(outputData),
-                                     outputShape);
-    } else if (inputShape.type == OperandType::TENSOR_QUANT8_ASYMM) {
-        return roiPoolingImpl<uint8_t>(reinterpret_cast<const uint8_t*>(inputData), inputShape,
-                                       reinterpret_cast<const float*>(roiData), roiShape,
-                                       spatialScale, reinterpret_cast<uint8_t*>(outputData),
-                                       outputShape);
+    InputWithLayout<T_Input> input(useNchw);
+    OutputWithLayout<T_Input> output(useNchw);
+    NN_RET_CHECK(input.initialize(inputData, inputShape));
+    NN_RET_CHECK(output.initialize(outputData, outputShape));
+    NN_RET_CHECK(roiPoolingNhwc(input.getNhwcBuffer(), input.getNhwcShape(), roiData, roiShape,
+                                spatialScale, output.getNhwcBuffer(), output.getNhwcShape()));
+    NN_RET_CHECK(output.commit());
+    return true;
+}
+
+}  // namespace
+
+bool validate(const IOperationValidationContext* context) {
+    NN_RET_CHECK_EQ(context->getNumInputs(), kNumInputs);
+    NN_RET_CHECK_EQ(context->getNumOutputs(), kNumOutputs);
+    std::vector<OperandType> inExpectedTypes;
+    auto inputType = context->getInputType(kInputTensor);
+    if (inputType == OperandType::TENSOR_FLOAT32) {
+        inExpectedTypes = {OperandType::TENSOR_FLOAT32, OperandType::TENSOR_FLOAT32,
+                           OperandType::TENSOR_INT32, OperandType::FLOAT32, OperandType::BOOL};
+    } else if (inputType == OperandType::TENSOR_FLOAT16) {
+        inExpectedTypes = {OperandType::TENSOR_FLOAT16, OperandType::TENSOR_FLOAT16,
+                           OperandType::TENSOR_INT32, OperandType::FLOAT16, OperandType::BOOL};
+    } else if (inputType == OperandType::TENSOR_QUANT8_ASYMM) {
+        inExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM, OperandType::TENSOR_FLOAT32,
+                           OperandType::TENSOR_INT32, OperandType::FLOAT32, OperandType::BOOL};
     } else {
-        LOG(ERROR) << "Unsupported data type";
+        LOG(ERROR) << "Unsupported input tensor type for operation " << kOperationName;
         return false;
     }
+    NN_RET_CHECK(validateInputTypes(context, inExpectedTypes));
+    NN_RET_CHECK(validateOutputTypes(context, {inputType}));
+    return validateHalVersion(context, HalVersion::V1_2);
 }
 
+bool prepare(IOperationExecutionContext* context) {
+    bool useNchw = context->getInputValue<bool>(kLayoutScalar);
+    Shape input = context->getInputShape(kInputTensor);
+    Shape roiShape = context->getInputShape(kRoiTensor);
+    auto outputShapeData = context->getInputBuffer<int32_t>(kOutputShapeTensor);
+    Shape outputShapeShape = context->getInputShape(kOutputShapeTensor);
+
+    NN_RET_CHECK_EQ(getNumberOfDimensions(input), 4);
+    NN_RET_CHECK_EQ(getNumberOfDimensions(roiShape), 2);
+    NN_RET_CHECK_EQ(getNumberOfDimensions(outputShapeShape), 1);
+
+    uint32_t numBatches = getSizeOfDimension(input, 0);
+    uint32_t inHeight = getSizeOfDimension(input, useNchw ? 2 : 1);
+    uint32_t inWidth = getSizeOfDimension(input, useNchw ? 3 : 2);
+    uint32_t inDepth = getSizeOfDimension(input, useNchw ? 1 : 3);
+    uint32_t numRois = getSizeOfDimension(roiShape, 0);
+    uint32_t roiInfoLength = getSizeOfDimension(roiShape, 1);
+
+    const uint32_t kRoiDim = 4;
+    NN_RET_CHECK(roiInfoLength == (kRoiDim + 1) || (roiInfoLength == kRoiDim && numBatches == 1));
+    NN_RET_CHECK_EQ(getSizeOfDimension(outputShapeShape, 0), 2);
+
+    Shape output = context->getOutputShape(kOutputTensor);
+    output.type = input.type;
+    if (useNchw) {
+        output.dimensions = {numRois, inDepth, static_cast<uint32_t>(outputShapeData[0]),
+                             static_cast<uint32_t>(outputShapeData[1])};
+    } else {
+        output.dimensions = {numRois, static_cast<uint32_t>(outputShapeData[0]),
+                             static_cast<uint32_t>(outputShapeData[1]), inDepth};
+    }
+    return context->setOutputShape(kOutputTensor, output);
+}
+
+bool execute(IOperationExecutionContext* context) {
+    switch (context->getInputType(kInputTensor)) {
+        case OperandType::TENSOR_FLOAT16:
+            return roiPooling(context->getInputBuffer<_Float16>(kInputTensor),
+                              context->getInputShape(kInputTensor),
+                              context->getInputBuffer<_Float16>(kRoiTensor),
+                              context->getInputShape(kRoiTensor),
+                              context->getInputValue<_Float16>(kSpacialScaleScalar),
+                              context->getInputValue<bool>(kLayoutScalar),
+                              context->getOutputBuffer<_Float16>(kOutputTensor),
+                              context->getOutputShape(kOutputTensor));
+        case OperandType::TENSOR_FLOAT32:
+            return roiPooling(context->getInputBuffer<float>(kInputTensor),
+                              context->getInputShape(kInputTensor),
+                              context->getInputBuffer<float>(kRoiTensor),
+                              context->getInputShape(kRoiTensor),
+                              context->getInputValue<float>(kSpacialScaleScalar),
+                              context->getInputValue<bool>(kLayoutScalar),
+                              context->getOutputBuffer<float>(kOutputTensor),
+                              context->getOutputShape(kOutputTensor));
+        case OperandType::TENSOR_QUANT8_ASYMM:
+            return roiPooling(context->getInputBuffer<uint8_t>(kInputTensor),
+                              context->getInputShape(kInputTensor),
+                              context->getInputBuffer<float>(kRoiTensor),
+                              context->getInputShape(kRoiTensor),
+                              context->getInputValue<float>(kSpacialScaleScalar),
+                              context->getInputValue<bool>(kLayoutScalar),
+                              context->getOutputBuffer<uint8_t>(kOutputTensor),
+                              context->getOutputShape(kOutputTensor));
+        default:
+            NN_RET_CHECK_FAIL() << "Unsupported tensor type for operation " << kOperationName;
+    }
+}
+
+}  // namespace roi_pooling
+
+NN_REGISTER_OPERATION(ROI_POOLING, roi_pooling::kOperationName, roi_pooling::validate,
+                      roi_pooling::prepare, roi_pooling::execute);
+
 }  // namespace nn
 }  // namespace android
diff --git a/runtime/include/NeuralNetworks.h b/runtime/include/NeuralNetworks.h
index e5debee..50ab0ae 100644
--- a/runtime/include/NeuralNetworks.h
+++ b/runtime/include/NeuralNetworks.h
@@ -3275,6 +3275,11 @@
      * Rounding is applied in this operation to ensure integer boundary for
      * regions of interest and pooling bins.
      *
+     * Supported tensor {@link OperandCode}:
+     * * {@link ANEURALNETWORKS_TENSOR_FLOAT16}
+     * * {@link ANEURALNETWORKS_TENSOR_FLOAT32}
+     * * {@link ANEURALNETWORKS_TENSOR_QUANT8_ASYMM}
+     *
      * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
      * With the default data layout NHWC, the data is stored in the order of:
      * [batch, height, width, channels]. Alternatively, the data layout could
diff --git a/runtime/test/TestValidateOperations.cpp b/runtime/test/TestValidateOperations.cpp
index 0727fb7..c9231e4 100644
--- a/runtime/test/TestValidateOperations.cpp
+++ b/runtime/test/TestValidateOperations.cpp
@@ -1551,15 +1551,14 @@
     roiAlignOpTest(ANEURALNETWORKS_TENSOR_QUANT8_ASYMM);
 }
 
-void roiPoolingOpTest(int32_t operandCode) {
+void roiPoolingOpTest(int32_t inputOperandCode, int32_t roiOperandCode, int32_t scalarOperandCode) {
     uint32_t inDim[] = {1, 4, 4, 1}, roiDim[] = {4, 4}, outShapeDim[] = {2};
     uint32_t outDim[] = {4, 2, 2, 1};
     OperationTestBase roiPoolingTest(
             ANEURALNETWORKS_ROI_POOLING,
-            {getOpType(operandCode, 4, inDim), getOpType(ANEURALNETWORKS_TENSOR_FLOAT32, 2, roiDim),
-             getOpType(ANEURALNETWORKS_TENSOR_INT32, 1, outShapeDim),
-             getOpType(ANEURALNETWORKS_FLOAT32)},
-            {getOpType(operandCode, 4, outDim)});
+            {getOpType(inputOperandCode, 4, inDim), getOpType(roiOperandCode, 2, roiDim),
+             getOpType(ANEURALNETWORKS_TENSOR_INT32, 1, outShapeDim), getOpType(scalarOperandCode)},
+            {getOpType(inputOperandCode, 4, outDim)});
 
     EXPECT_TRUE(roiPoolingTest.testMutatingInputOperandCode());
     EXPECT_TRUE(roiPoolingTest.testMutatingInputOperandCounts());
@@ -1567,12 +1566,19 @@
     EXPECT_TRUE(roiPoolingTest.testMutatingOutputOperandCounts());
 }
 
+TEST(OperationValidationTest, ROI_POOLING_float16) {
+    roiPoolingOpTest(ANEURALNETWORKS_TENSOR_FLOAT16, ANEURALNETWORKS_TENSOR_FLOAT16,
+                     ANEURALNETWORKS_FLOAT16);
+}
+
 TEST(OperationValidationTest, ROI_POOLING_float32) {
-    roiPoolingOpTest(ANEURALNETWORKS_TENSOR_FLOAT32);
+    roiPoolingOpTest(ANEURALNETWORKS_TENSOR_FLOAT32, ANEURALNETWORKS_TENSOR_FLOAT32,
+                     ANEURALNETWORKS_FLOAT32);
 }
 
 TEST(OperationValidationTest, ROI_POOLING_quant8) {
-    roiPoolingOpTest(ANEURALNETWORKS_TENSOR_QUANT8_ASYMM);
+    roiPoolingOpTest(ANEURALNETWORKS_TENSOR_QUANT8_ASYMM, ANEURALNETWORKS_TENSOR_FLOAT32,
+                     ANEURALNETWORKS_FLOAT32);
 }
 
 void heatmapMaxKeypointOpTest(int32_t operandCode) {
diff --git a/runtime/test/generated/all_generated_V1_2_vts_tests.cpp b/runtime/test/generated/all_generated_V1_2_vts_tests.cpp
index fd00248..42bd10e 100644
--- a/runtime/test/generated/all_generated_V1_2_vts_tests.cpp
+++ b/runtime/test/generated/all_generated_V1_2_vts_tests.cpp
@@ -18417,6 +18417,21 @@
 }
 
 
+TEST_F(NeuralnetworksHidlTest, roi_pooling_nhwc_float16) {
+  generated_tests::Execute(device,
+                           roi_pooling::createTestModel_nhwc_float16,
+                           roi_pooling::is_ignored_nhwc_float16,
+                           roi_pooling::get_examples_nhwc_float16());
+}
+
+TEST_F(ValidationTest, roi_pooling_nhwc_float16) {
+  const Model model = roi_pooling::createTestModel_nhwc_float16();
+  const std::vector<Request> requests = createRequests(roi_pooling::get_examples_nhwc_float16());
+  validateModel(model);
+  validateRequests(model, requests);
+}
+
+
 TEST_F(NeuralnetworksHidlTest, roi_pooling_nchw) {
   generated_tests::Execute(device,
                            roi_pooling::createTestModel_nchw,
@@ -18462,6 +18477,21 @@
 }
 
 
+TEST_F(NeuralnetworksHidlTest, roi_pooling_nchw_float16) {
+  generated_tests::Execute(device,
+                           roi_pooling::createTestModel_nchw_float16,
+                           roi_pooling::is_ignored_nchw_float16,
+                           roi_pooling::get_examples_nchw_float16());
+}
+
+TEST_F(ValidationTest, roi_pooling_nchw_float16) {
+  const Model model = roi_pooling::createTestModel_nchw_float16();
+  const std::vector<Request> requests = createRequests(roi_pooling::get_examples_nchw_float16());
+  validateModel(model);
+  validateRequests(model, requests);
+}
+
+
 TEST_F(NeuralnetworksHidlTest, roi_pooling_nhwc_2) {
   generated_tests::Execute(device,
                            roi_pooling::createTestModel_nhwc_2,
@@ -18507,6 +18537,21 @@
 }
 
 
+TEST_F(NeuralnetworksHidlTest, roi_pooling_nhwc_float16_2) {
+  generated_tests::Execute(device,
+                           roi_pooling::createTestModel_nhwc_float16_2,
+                           roi_pooling::is_ignored_nhwc_float16_2,
+                           roi_pooling::get_examples_nhwc_float16_2());
+}
+
+TEST_F(ValidationTest, roi_pooling_nhwc_float16_2) {
+  const Model model = roi_pooling::createTestModel_nhwc_float16_2();
+  const std::vector<Request> requests = createRequests(roi_pooling::get_examples_nhwc_float16_2());
+  validateModel(model);
+  validateRequests(model, requests);
+}
+
+
 TEST_F(NeuralnetworksHidlTest, roi_pooling_nchw_2) {
   generated_tests::Execute(device,
                            roi_pooling::createTestModel_nchw_2,
@@ -18552,6 +18597,21 @@
 }
 
 
+TEST_F(NeuralnetworksHidlTest, roi_pooling_nchw_float16_2) {
+  generated_tests::Execute(device,
+                           roi_pooling::createTestModel_nchw_float16_2,
+                           roi_pooling::is_ignored_nchw_float16_2,
+                           roi_pooling::get_examples_nchw_float16_2());
+}
+
+TEST_F(ValidationTest, roi_pooling_nchw_float16_2) {
+  const Model model = roi_pooling::createTestModel_nchw_float16_2();
+  const std::vector<Request> requests = createRequests(roi_pooling::get_examples_nchw_float16_2());
+  validateModel(model);
+  validateRequests(model, requests);
+}
+
+
 // Generated from: rotated_bbox_transform.mod.py.
 namespace rotated_bbox_transform {
 // Generated rotated_bbox_transform test
diff --git a/runtime/test/generated/examples/roi_pooling.example.cpp b/runtime/test/generated/examples/roi_pooling.example.cpp
index 41bd833..6de4cf5 100644
--- a/runtime/test/generated/examples/roi_pooling.example.cpp
+++ b/runtime/test/generated/examples/roi_pooling.example.cpp
@@ -123,6 +123,47 @@
 return examples_nhwc_quant8;
 };
 
+std::vector<MixedTypedExample>& get_examples_nhwc_float16() {
+static std::vector<MixedTypedExample> examples_nhwc_float16 = {
+// Begin of an example
+{
+.operands = {
+//Input(s)
+{ // See tools/test_generator/include/TestHarness.h:MixedTyped
+  // int -> FLOAT32 map
+  {},
+  // int -> INT32 map
+  {},
+  // int -> QUANT8_ASYMM map
+  {},
+  // int -> QUANT16_SYMM map
+  {},
+  // int -> FLOAT16 map
+  {{0, {-10.0f, -1.0f, 4.0f, -5.0f, -8.0f, -2.0f, 9.0f, 1.0f, 7.0f, -2.0f, 3.0f, -7.0f, -2.0f, 10.0f, -3.0f, 5.0f}}, {1, {2.0f, 2.0f, 4.0f, 4.0f, 0.0f, 0.0f, 6.0f, 6.0f, 2.0f, 0.0f, 4.0f, 6.0f, 0.0f, 2.0f, 6.0f, 4.0f}}},
+  // int -> BOOL8 map
+  {},
+},
+//Output(s)
+{ // See tools/test_generator/include/TestHarness.h:MixedTyped
+  // int -> FLOAT32 map
+  {},
+  // int -> INT32 map
+  {},
+  // int -> QUANT8_ASYMM map
+  {},
+  // int -> QUANT16_SYMM map
+  {},
+  // int -> FLOAT16 map
+  {{0, {-2.0f, 9.0f, -2.0f, 3.0f, -1.0f, 9.0f, 10.0f, 5.0f, -1.0f, 9.0f, 10.0f, 3.0f, -2.0f, 9.0f, 7.0f, 3.0f}}},
+  // int -> BOOL8 map
+  {},
+}
+},
+}, // End of an example
+};
+return examples_nhwc_float16;
+};
+
 std::vector<MixedTypedExample>& get_examples_nchw() {
 static std::vector<MixedTypedExample> examples_nchw = {
 // Begin of an example
@@ -246,6 +287,47 @@
 return examples_nchw_quant8;
 };
 
+std::vector<MixedTypedExample>& get_examples_nchw_float16() {
+static std::vector<MixedTypedExample> examples_nchw_float16 = {
+// Begin of an example
+{
+.operands = {
+//Input(s)
+{ // See tools/test_generator/include/TestHarness.h:MixedTyped
+  // int -> FLOAT32 map
+  {},
+  // int -> INT32 map
+  {},
+  // int -> QUANT8_ASYMM map
+  {},
+  // int -> QUANT16_SYMM map
+  {},
+  // int -> FLOAT16 map
+  {{0, {-10.0f, -1.0f, 4.0f, -5.0f, -8.0f, -2.0f, 9.0f, 1.0f, 7.0f, -2.0f, 3.0f, -7.0f, -2.0f, 10.0f, -3.0f, 5.0f}}, {1, {2.0f, 2.0f, 4.0f, 4.0f, 0.0f, 0.0f, 6.0f, 6.0f, 2.0f, 0.0f, 4.0f, 6.0f, 0.0f, 2.0f, 6.0f, 4.0f}}},
+  // int -> BOOL8 map
+  {},
+},
+//Output(s)
+{ // See tools/test_generator/include/TestHarness.h:MixedTyped
+  // int -> FLOAT32 map
+  {},
+  // int -> INT32 map
+  {},
+  // int -> QUANT8_ASYMM map
+  {},
+  // int -> QUANT16_SYMM map
+  {},
+  // int -> FLOAT16 map
+  {{0, {-2.0f, 9.0f, -2.0f, 3.0f, -1.0f, 9.0f, 10.0f, 5.0f, -1.0f, 9.0f, 10.0f, 3.0f, -2.0f, 9.0f, 7.0f, 3.0f}}},
+  // int -> BOOL8 map
+  {},
+}
+},
+}, // End of an example
+};
+return examples_nchw_float16;
+};
+
 std::vector<MixedTypedExample>& get_examples_nhwc_2() {
 static std::vector<MixedTypedExample> examples_nhwc_2 = {
 // Begin of an example
@@ -369,6 +451,47 @@
 return examples_nhwc_quant8_2;
 };
 
+std::vector<MixedTypedExample>& get_examples_nhwc_float16_2() {
+static std::vector<MixedTypedExample> examples_nhwc_float16_2 = {
+// Begin of an example
+{
+.operands = {
+//Input(s)
+{ // See tools/test_generator/include/TestHarness.h:MixedTyped
+  // int -> FLOAT32 map
+  {},
+  // int -> INT32 map
+  {},
+  // int -> QUANT8_ASYMM map
+  {},
+  // int -> QUANT16_SYMM map
+  {},
+  // int -> FLOAT16 map
+  {{0, {8.84000015258789f, 8.880000114440918f, 7.409999847412109f, 5.599999904632568f, 9.949999809265137f, 4.369999885559082f, 0.10000000149011612f, 7.639999866485596f, 6.5f, 9.470000267028809f, 7.550000190734863f, 3.0f, 0.8899999856948853f, 3.009999990463257f, 6.300000190734863f, 4.400000095367432f, 1.6399999856948853f, 6.739999771118164f, 6.159999847412109f, 8.600000381469727f, 5.849999904632568f, 3.1700000762939453f, 7.119999885559082f, 6.789999961853027f, 5.769999980926514f, 6.619999885559082f, 5.130000114440918f, 8.4399995803833f, 5.079999923706055f, 7.119999885559082f, 2.8399999141693115f, 1.190000057220459f, 8.369999885559082f, 0.8999999761581421f, 7.860000133514404f, 9.6899995803833f, 1.9700000286102295f, 1.309999942779541f, 4.420000076293945f, 9.890000343322754f, 0.18000000715255737f, 9.0f, 9.300000190734863f, 0.4399999976158142f, 5.050000190734863f, 6.46999979019165f, 1.090000033378601f, 9.5f, 1.2999999523162842f, 2.180000066757202f, 2.049999952316284f, 7.739999771118164f, 7.659999847412109f, 0.6499999761581421f, 4.179999828338623f, 7.139999866485596f, 5.349999904632568f, 7.900000095367432f, 1.0399999618530273f, 1.4700000286102295f, 9.010000228881836f, 0.949999988079071f, 4.070000171661377f, 0.6499999761581421f, 5.46999979019165f, 2.640000104904175f, 0.8600000143051147f, 4.860000133514404f, 2.380000114440918f, 2.450000047683716f, 8.770000457763672f, 0.05999999865889549f, 3.5999999046325684f, 9.279999732971191f, 5.840000152587891f, 8.970000267028809f, 6.889999866485596f, 1.4299999475479126f, 3.9000000953674316f, 5.909999847412109f, 7.400000095367432f, 9.25f, 3.119999885559082f, 4.920000076293945f, 1.8700000047683716f, 3.2200000286102295f, 9.5f, 6.730000019073486f, 2.069999933242798f, 7.300000190734863f, 3.069999933242798f, 4.96999979019165f, 0.23999999463558197f, 8.90999984741211f, 1.090000033378601f, 0.27000001072883606f, 7.289999961853027f, 6.940000057220459f, 2.309999942779541f, 6.880000114440918f, 4.329999923706055f, 1.3700000047683716f, 0.8600000143051147f, 0.46000000834465027f, 6.070000171661377f, 3.809999942779541f, 0.8600000143051147f, 6.989999771118164f, 4.360000133514404f, 1.9199999570846558f, 8.1899995803833f, 3.569999933242798f, 7.900000095367432f, 6.78000020980835f, 4.639999866485596f, 6.820000171661377f, 6.179999828338623f, 9.630000114440918f, 2.630000114440918f, 2.3299999237060547f, 1.3600000143051147f, 2.700000047683716f, 9.989999771118164f, 9.850000381469727f, 8.0600004196167f, 4.800000190734863f, 7.800000190734863f, 5.429999828338623f}}, {1, {0.0f, 4.0f, 4.0f, 24.0f, 8.0f, 0.0f, 4.0f, 4.0f, 28.0f, 12.0f, 1.0f, 7.0f, 1.0f, 25.0f, 11.0f, 1.0f, 1.0f, 7.0f, 5.0f, 11.0f}}},
+  // int -> BOOL8 map
+  {},
+},
+//Output(s)
+{ // See tools/test_generator/include/TestHarness.h:MixedTyped
+  // int -> FLOAT32 map
+  {},
+  // int -> INT32 map
+  {},
+  // int -> QUANT8_ASYMM map
+  {},
+  // int -> QUANT16_SYMM map
+  {},
+  // int -> FLOAT16 map
+  {{0, {6.159999847412109f, 8.600000381469727f, 7.119999885559082f, 6.789999961853027f, 5.130000114440918f, 8.4399995803833f, 7.860000133514404f, 9.6899995803833f, 4.420000076293945f, 9.890000343322754f, 9.300000190734863f, 6.46999979019165f, 7.860000133514404f, 9.890000343322754f, 9.300000190734863f, 9.890000343322754f, 9.300000190734863f, 9.5f, 7.860000133514404f, 9.890000343322754f, 9.300000190734863f, 9.890000343322754f, 9.300000190734863f, 9.5f, 9.5f, 6.730000019073486f, 9.5f, 9.279999732971191f, 6.889999866485596f, 8.970000267028809f, 6.179999828338623f, 9.630000114440918f, 9.989999771118164f, 9.850000381469727f, 9.989999771118164f, 9.850000381469727f, 7.289999961853027f, 6.940000057220459f, 7.289999961853027f, 6.940000057220459f, 2.309999942779541f, 6.880000114440918f, 7.900000095367432f, 6.78000020980835f, 7.900000095367432f, 6.820000171661377f, 4.639999866485596f, 6.820000171661377f}}},
+  // int -> BOOL8 map
+  {},
+}
+},
+}, // End of an example
+};
+return examples_nhwc_float16_2;
+};
+
 std::vector<MixedTypedExample>& get_examples_nchw_2() {
 static std::vector<MixedTypedExample> examples_nchw_2 = {
 // Begin of an example
@@ -492,3 +615,44 @@
 return examples_nchw_quant8_2;
 };
 
+std::vector<MixedTypedExample>& get_examples_nchw_float16_2() {
+static std::vector<MixedTypedExample> examples_nchw_float16_2 = {
+// Begin of an example
+{
+.operands = {
+//Input(s)
+{ // See tools/test_generator/include/TestHarness.h:MixedTyped
+  // int -> FLOAT32 map
+  {},
+  // int -> INT32 map
+  {},
+  // int -> QUANT8_ASYMM map
+  {},
+  // int -> QUANT16_SYMM map
+  {},
+  // int -> FLOAT16 map
+  {{0, {8.84000015258789f, 7.409999847412109f, 9.949999809265137f, 0.10000000149011612f, 6.5f, 7.550000190734863f, 0.8899999856948853f, 6.300000190734863f, 1.6399999856948853f, 6.159999847412109f, 5.849999904632568f, 7.119999885559082f, 5.769999980926514f, 5.130000114440918f, 5.079999923706055f, 2.8399999141693115f, 8.369999885559082f, 7.860000133514404f, 1.9700000286102295f, 4.420000076293945f, 0.18000000715255737f, 9.300000190734863f, 5.050000190734863f, 1.090000033378601f, 1.2999999523162842f, 2.049999952316284f, 7.659999847412109f, 4.179999828338623f, 5.349999904632568f, 1.0399999618530273f, 9.010000228881836f, 4.070000171661377f, 8.880000114440918f, 5.599999904632568f, 4.369999885559082f, 7.639999866485596f, 9.470000267028809f, 3.0f, 3.009999990463257f, 4.400000095367432f, 6.739999771118164f, 8.600000381469727f, 3.1700000762939453f, 6.789999961853027f, 6.619999885559082f, 8.4399995803833f, 7.119999885559082f, 1.190000057220459f, 0.8999999761581421f, 9.6899995803833f, 1.309999942779541f, 9.890000343322754f, 9.0f, 0.4399999976158142f, 6.46999979019165f, 9.5f, 2.180000066757202f, 7.739999771118164f, 0.6499999761581421f, 7.139999866485596f, 7.900000095367432f, 1.4700000286102295f, 0.949999988079071f, 0.6499999761581421f, 5.46999979019165f, 0.8600000143051147f, 2.380000114440918f, 8.770000457763672f, 3.5999999046325684f, 5.840000152587891f, 6.889999866485596f, 3.9000000953674316f, 7.400000095367432f, 3.119999885559082f, 1.8700000047683716f, 9.5f, 2.069999933242798f, 3.069999933242798f, 0.23999999463558197f, 1.090000033378601f, 7.289999961853027f, 2.309999942779541f, 4.329999923706055f, 0.8600000143051147f, 6.070000171661377f, 0.8600000143051147f, 4.360000133514404f, 8.1899995803833f, 7.900000095367432f, 4.639999866485596f, 6.179999828338623f, 2.630000114440918f, 1.3600000143051147f, 9.989999771118164f, 8.0600004196167f, 7.800000190734863f, 2.640000104904175f, 4.860000133514404f, 2.450000047683716f, 0.05999999865889549f, 9.279999732971191f, 8.970000267028809f, 1.4299999475479126f, 5.909999847412109f, 9.25f, 4.920000076293945f, 3.2200000286102295f, 6.730000019073486f, 7.300000190734863f, 4.96999979019165f, 8.90999984741211f, 0.27000001072883606f, 6.940000057220459f, 6.880000114440918f, 1.3700000047683716f, 0.46000000834465027f, 3.809999942779541f, 6.989999771118164f, 1.9199999570846558f, 3.569999933242798f, 6.78000020980835f, 6.820000171661377f, 9.630000114440918f, 2.3299999237060547f, 2.700000047683716f, 9.850000381469727f, 4.800000190734863f, 5.429999828338623f}}, {1, {0.0f, 4.0f, 4.0f, 24.0f, 8.0f, 0.0f, 4.0f, 4.0f, 28.0f, 12.0f, 1.0f, 7.0f, 1.0f, 25.0f, 11.0f, 1.0f, 1.0f, 7.0f, 5.0f, 11.0f}}},
+  // int -> BOOL8 map
+  {},
+},
+//Output(s)
+{ // See tools/test_generator/include/TestHarness.h:MixedTyped
+  // int -> FLOAT32 map
+  {},
+  // int -> INT32 map
+  {},
+  // int -> QUANT8_ASYMM map
+  {},
+  // int -> QUANT16_SYMM map
+  {},
+  // int -> FLOAT16 map
+  {{0, {6.159999847412109f, 7.119999885559082f, 5.130000114440918f, 7.860000133514404f, 4.420000076293945f, 9.300000190734863f, 8.600000381469727f, 6.789999961853027f, 8.4399995803833f, 9.6899995803833f, 9.890000343322754f, 6.46999979019165f, 7.860000133514404f, 9.300000190734863f, 9.300000190734863f, 7.860000133514404f, 9.300000190734863f, 9.300000190734863f, 9.890000343322754f, 9.890000343322754f, 9.5f, 9.890000343322754f, 9.890000343322754f, 9.5f, 9.5f, 9.5f, 6.889999866485596f, 6.179999828338623f, 9.989999771118164f, 9.989999771118164f, 6.730000019073486f, 9.279999732971191f, 8.970000267028809f, 9.630000114440918f, 9.850000381469727f, 9.850000381469727f, 7.289999961853027f, 7.289999961853027f, 2.309999942779541f, 7.900000095367432f, 7.900000095367432f, 4.639999866485596f, 6.940000057220459f, 6.940000057220459f, 6.880000114440918f, 6.78000020980835f, 6.820000171661377f, 6.820000171661377f}}},
+  // int -> BOOL8 map
+  {},
+}
+},
+}, // End of an example
+};
+return examples_nchw_float16_2;
+};
+
diff --git a/runtime/test/generated/models/roi_pooling.model.cpp b/runtime/test/generated/models/roi_pooling.model.cpp
index 6776185..895238c 100644
--- a/runtime/test/generated/models/roi_pooling.model.cpp
+++ b/runtime/test/generated/models/roi_pooling.model.cpp
@@ -104,20 +104,54 @@
   return ignore.find(i) != ignore.end();
 }
 
+void CreateModel_nhwc_float16(Model *model) {
+  OperandType type0(Type::BOOL, {});
+  OperandType type11(Type::TENSOR_FLOAT16, {1, 4, 4, 1});
+  OperandType type12(Type::TENSOR_FLOAT16, {4, 2, 2, 1});
+  OperandType type13(Type::FLOAT16, {});
+  OperandType type14(Type::TENSOR_FLOAT16, {4, 4});
+  OperandType type4(Type::TENSOR_INT32, {2});
+  // Phase 1, operands
+  auto in = model->addOperand(&type11);
+  auto roi = model->addOperand(&type14);
+  auto param = model->addOperand(&type4);
+  auto param1 = model->addOperand(&type13);
+  auto layout = model->addOperand(&type0);
+  auto out = model->addOperand(&type12);
+  // Phase 2, operations
+  static int32_t param_init[] = {2, 2};
+  model->setOperandValue(param, param_init, sizeof(int32_t) * 2);
+  static _Float16 param1_init[] = {0.5f};
+  model->setOperandValue(param1, param1_init, sizeof(_Float16) * 1);
+  static bool8 layout_init[] = {false};
+  model->setOperandValue(layout, layout_init, sizeof(bool8) * 1);
+  model->addOperation(ANEURALNETWORKS_ROI_POOLING, {in, roi, param, param1, layout}, {out});
+  // Phase 3, inputs and outputs
+  model->identifyInputsAndOutputs(
+    {in, roi},
+    {out});
+  assert(model->isValid());
+}
+
+inline bool is_ignored_nhwc_float16(int i) {
+  static std::set<int> ignore = {};
+  return ignore.find(i) != ignore.end();
+}
+
 void CreateModel_nchw(Model *model) {
   OperandType type0(Type::BOOL, {});
-  OperandType type11(Type::TENSOR_FLOAT32, {1, 1, 4, 4});
-  OperandType type12(Type::TENSOR_FLOAT32, {4, 1, 2, 2});
+  OperandType type15(Type::TENSOR_FLOAT32, {1, 1, 4, 4});
+  OperandType type16(Type::TENSOR_FLOAT32, {4, 1, 2, 2});
   OperandType type2(Type::TENSOR_FLOAT32, {4, 4});
   OperandType type4(Type::TENSOR_INT32, {2});
   OperandType type5(Type::FLOAT32, {});
   // Phase 1, operands
-  auto in = model->addOperand(&type11);
+  auto in = model->addOperand(&type15);
   auto roi = model->addOperand(&type2);
   auto param = model->addOperand(&type4);
   auto param1 = model->addOperand(&type5);
   auto layout = model->addOperand(&type0);
-  auto out = model->addOperand(&type12);
+  auto out = model->addOperand(&type16);
   // Phase 2, operations
   static int32_t param_init[] = {2, 2};
   model->setOperandValue(param, param_init, sizeof(int32_t) * 2);
@@ -140,18 +174,18 @@
 
 void CreateModel_nchw_relaxed(Model *model) {
   OperandType type0(Type::BOOL, {});
-  OperandType type11(Type::TENSOR_FLOAT32, {1, 1, 4, 4});
-  OperandType type12(Type::TENSOR_FLOAT32, {4, 1, 2, 2});
+  OperandType type15(Type::TENSOR_FLOAT32, {1, 1, 4, 4});
+  OperandType type16(Type::TENSOR_FLOAT32, {4, 1, 2, 2});
   OperandType type2(Type::TENSOR_FLOAT32, {4, 4});
   OperandType type4(Type::TENSOR_INT32, {2});
   OperandType type5(Type::FLOAT32, {});
   // Phase 1, operands
-  auto in = model->addOperand(&type11);
+  auto in = model->addOperand(&type15);
   auto roi = model->addOperand(&type2);
   auto param = model->addOperand(&type4);
   auto param1 = model->addOperand(&type5);
   auto layout = model->addOperand(&type0);
-  auto out = model->addOperand(&type12);
+  auto out = model->addOperand(&type16);
   // Phase 2, operations
   static int32_t param_init[] = {2, 2};
   model->setOperandValue(param, param_init, sizeof(int32_t) * 2);
@@ -176,18 +210,18 @@
 
 void CreateModel_nchw_quant8(Model *model) {
   OperandType type0(Type::BOOL, {});
-  OperandType type13(Type::TENSOR_QUANT8_ASYMM, {1, 1, 4, 4}, 0.25f, 128);
-  OperandType type14(Type::TENSOR_QUANT8_ASYMM, {4, 1, 2, 2}, 0.25f, 128);
+  OperandType type17(Type::TENSOR_QUANT8_ASYMM, {1, 1, 4, 4}, 0.25f, 128);
+  OperandType type18(Type::TENSOR_QUANT8_ASYMM, {4, 1, 2, 2}, 0.25f, 128);
   OperandType type2(Type::TENSOR_FLOAT32, {4, 4});
   OperandType type4(Type::TENSOR_INT32, {2});
   OperandType type5(Type::FLOAT32, {});
   // Phase 1, operands
-  auto in = model->addOperand(&type13);
+  auto in = model->addOperand(&type17);
   auto roi = model->addOperand(&type2);
   auto param = model->addOperand(&type4);
   auto param1 = model->addOperand(&type5);
   auto layout = model->addOperand(&type0);
-  auto out = model->addOperand(&type14);
+  auto out = model->addOperand(&type18);
   // Phase 2, operations
   static int32_t param_init[] = {2, 2};
   model->setOperandValue(param, param_init, sizeof(int32_t) * 2);
@@ -208,6 +242,40 @@
   return ignore.find(i) != ignore.end();
 }
 
+void CreateModel_nchw_float16(Model *model) {
+  OperandType type0(Type::BOOL, {});
+  OperandType type13(Type::FLOAT16, {});
+  OperandType type14(Type::TENSOR_FLOAT16, {4, 4});
+  OperandType type19(Type::TENSOR_FLOAT16, {1, 1, 4, 4});
+  OperandType type20(Type::TENSOR_FLOAT16, {4, 1, 2, 2});
+  OperandType type4(Type::TENSOR_INT32, {2});
+  // Phase 1, operands
+  auto in = model->addOperand(&type19);
+  auto roi = model->addOperand(&type14);
+  auto param = model->addOperand(&type4);
+  auto param1 = model->addOperand(&type13);
+  auto layout = model->addOperand(&type0);
+  auto out = model->addOperand(&type20);
+  // Phase 2, operations
+  static int32_t param_init[] = {2, 2};
+  model->setOperandValue(param, param_init, sizeof(int32_t) * 2);
+  static _Float16 param1_init[] = {0.5f};
+  model->setOperandValue(param1, param1_init, sizeof(_Float16) * 1);
+  static bool8 layout_init[] = {true};
+  model->setOperandValue(layout, layout_init, sizeof(bool8) * 1);
+  model->addOperation(ANEURALNETWORKS_ROI_POOLING, {in, roi, param, param1, layout}, {out});
+  // Phase 3, inputs and outputs
+  model->identifyInputsAndOutputs(
+    {in, roi},
+    {out});
+  assert(model->isValid());
+}
+
+inline bool is_ignored_nchw_float16(int i) {
+  static std::set<int> ignore = {};
+  return ignore.find(i) != ignore.end();
+}
+
 void CreateModel_nhwc_2(Model *model) {
   OperandType type0(Type::BOOL, {});
   OperandType type4(Type::TENSOR_INT32, {2});
@@ -280,18 +348,18 @@
 
 void CreateModel_nhwc_quant8_2(Model *model) {
   OperandType type0(Type::BOOL, {});
-  OperandType type15(Type::TENSOR_QUANT8_ASYMM, {2, 4, 8, 2}, 0.04f, 0);
-  OperandType type16(Type::TENSOR_QUANT8_ASYMM, {4, 2, 3, 2}, 0.04f, 0);
+  OperandType type21(Type::TENSOR_QUANT8_ASYMM, {2, 4, 8, 2}, 0.04f, 0);
+  OperandType type22(Type::TENSOR_QUANT8_ASYMM, {4, 2, 3, 2}, 0.04f, 0);
   OperandType type4(Type::TENSOR_INT32, {2});
   OperandType type5(Type::FLOAT32, {});
   OperandType type7(Type::TENSOR_FLOAT32, {4, 5});
   // Phase 1, operands
-  auto in1 = model->addOperand(&type15);
+  auto in1 = model->addOperand(&type21);
   auto roi1 = model->addOperand(&type7);
   auto param2 = model->addOperand(&type4);
   auto param3 = model->addOperand(&type5);
   auto layout = model->addOperand(&type0);
-  auto out1 = model->addOperand(&type16);
+  auto out1 = model->addOperand(&type22);
   // Phase 2, operations
   static int32_t param2_init[] = {2, 3};
   model->setOperandValue(param2, param2_init, sizeof(int32_t) * 2);
@@ -312,20 +380,54 @@
   return ignore.find(i) != ignore.end();
 }
 
+void CreateModel_nhwc_float16_2(Model *model) {
+  OperandType type0(Type::BOOL, {});
+  OperandType type13(Type::FLOAT16, {});
+  OperandType type23(Type::TENSOR_FLOAT16, {2, 4, 8, 2});
+  OperandType type24(Type::TENSOR_FLOAT16, {4, 2, 3, 2});
+  OperandType type25(Type::TENSOR_FLOAT16, {4, 5});
+  OperandType type4(Type::TENSOR_INT32, {2});
+  // Phase 1, operands
+  auto in1 = model->addOperand(&type23);
+  auto roi1 = model->addOperand(&type25);
+  auto param2 = model->addOperand(&type4);
+  auto param3 = model->addOperand(&type13);
+  auto layout = model->addOperand(&type0);
+  auto out1 = model->addOperand(&type24);
+  // Phase 2, operations
+  static int32_t param2_init[] = {2, 3};
+  model->setOperandValue(param2, param2_init, sizeof(int32_t) * 2);
+  static _Float16 param3_init[] = {0.25f};
+  model->setOperandValue(param3, param3_init, sizeof(_Float16) * 1);
+  static bool8 layout_init[] = {false};
+  model->setOperandValue(layout, layout_init, sizeof(bool8) * 1);
+  model->addOperation(ANEURALNETWORKS_ROI_POOLING, {in1, roi1, param2, param3, layout}, {out1});
+  // Phase 3, inputs and outputs
+  model->identifyInputsAndOutputs(
+    {in1, roi1},
+    {out1});
+  assert(model->isValid());
+}
+
+inline bool is_ignored_nhwc_float16_2(int i) {
+  static std::set<int> ignore = {};
+  return ignore.find(i) != ignore.end();
+}
+
 void CreateModel_nchw_2(Model *model) {
   OperandType type0(Type::BOOL, {});
-  OperandType type17(Type::TENSOR_FLOAT32, {2, 2, 4, 8});
-  OperandType type18(Type::TENSOR_FLOAT32, {4, 2, 2, 3});
+  OperandType type26(Type::TENSOR_FLOAT32, {2, 2, 4, 8});
+  OperandType type27(Type::TENSOR_FLOAT32, {4, 2, 2, 3});
   OperandType type4(Type::TENSOR_INT32, {2});
   OperandType type5(Type::FLOAT32, {});
   OperandType type7(Type::TENSOR_FLOAT32, {4, 5});
   // Phase 1, operands
-  auto in1 = model->addOperand(&type17);
+  auto in1 = model->addOperand(&type26);
   auto roi1 = model->addOperand(&type7);
   auto param2 = model->addOperand(&type4);
   auto param3 = model->addOperand(&type5);
   auto layout = model->addOperand(&type0);
-  auto out1 = model->addOperand(&type18);
+  auto out1 = model->addOperand(&type27);
   // Phase 2, operations
   static int32_t param2_init[] = {2, 3};
   model->setOperandValue(param2, param2_init, sizeof(int32_t) * 2);
@@ -348,18 +450,18 @@
 
 void CreateModel_nchw_relaxed_2(Model *model) {
   OperandType type0(Type::BOOL, {});
-  OperandType type17(Type::TENSOR_FLOAT32, {2, 2, 4, 8});
-  OperandType type18(Type::TENSOR_FLOAT32, {4, 2, 2, 3});
+  OperandType type26(Type::TENSOR_FLOAT32, {2, 2, 4, 8});
+  OperandType type27(Type::TENSOR_FLOAT32, {4, 2, 2, 3});
   OperandType type4(Type::TENSOR_INT32, {2});
   OperandType type5(Type::FLOAT32, {});
   OperandType type7(Type::TENSOR_FLOAT32, {4, 5});
   // Phase 1, operands
-  auto in1 = model->addOperand(&type17);
+  auto in1 = model->addOperand(&type26);
   auto roi1 = model->addOperand(&type7);
   auto param2 = model->addOperand(&type4);
   auto param3 = model->addOperand(&type5);
   auto layout = model->addOperand(&type0);
-  auto out1 = model->addOperand(&type18);
+  auto out1 = model->addOperand(&type27);
   // Phase 2, operations
   static int32_t param2_init[] = {2, 3};
   model->setOperandValue(param2, param2_init, sizeof(int32_t) * 2);
@@ -384,18 +486,18 @@
 
 void CreateModel_nchw_quant8_2(Model *model) {
   OperandType type0(Type::BOOL, {});
-  OperandType type19(Type::TENSOR_QUANT8_ASYMM, {2, 2, 4, 8}, 0.04f, 0);
-  OperandType type20(Type::TENSOR_QUANT8_ASYMM, {4, 2, 2, 3}, 0.04f, 0);
+  OperandType type28(Type::TENSOR_QUANT8_ASYMM, {2, 2, 4, 8}, 0.04f, 0);
+  OperandType type29(Type::TENSOR_QUANT8_ASYMM, {4, 2, 2, 3}, 0.04f, 0);
   OperandType type4(Type::TENSOR_INT32, {2});
   OperandType type5(Type::FLOAT32, {});
   OperandType type7(Type::TENSOR_FLOAT32, {4, 5});
   // Phase 1, operands
-  auto in1 = model->addOperand(&type19);
+  auto in1 = model->addOperand(&type28);
   auto roi1 = model->addOperand(&type7);
   auto param2 = model->addOperand(&type4);
   auto param3 = model->addOperand(&type5);
   auto layout = model->addOperand(&type0);
-  auto out1 = model->addOperand(&type20);
+  auto out1 = model->addOperand(&type29);
   // Phase 2, operations
   static int32_t param2_init[] = {2, 3};
   model->setOperandValue(param2, param2_init, sizeof(int32_t) * 2);
@@ -416,3 +518,37 @@
   return ignore.find(i) != ignore.end();
 }
 
+void CreateModel_nchw_float16_2(Model *model) {
+  OperandType type0(Type::BOOL, {});
+  OperandType type13(Type::FLOAT16, {});
+  OperandType type25(Type::TENSOR_FLOAT16, {4, 5});
+  OperandType type30(Type::TENSOR_FLOAT16, {2, 2, 4, 8});
+  OperandType type31(Type::TENSOR_FLOAT16, {4, 2, 2, 3});
+  OperandType type4(Type::TENSOR_INT32, {2});
+  // Phase 1, operands
+  auto in1 = model->addOperand(&type30);
+  auto roi1 = model->addOperand(&type25);
+  auto param2 = model->addOperand(&type4);
+  auto param3 = model->addOperand(&type13);
+  auto layout = model->addOperand(&type0);
+  auto out1 = model->addOperand(&type31);
+  // Phase 2, operations
+  static int32_t param2_init[] = {2, 3};
+  model->setOperandValue(param2, param2_init, sizeof(int32_t) * 2);
+  static _Float16 param3_init[] = {0.25f};
+  model->setOperandValue(param3, param3_init, sizeof(_Float16) * 1);
+  static bool8 layout_init[] = {true};
+  model->setOperandValue(layout, layout_init, sizeof(bool8) * 1);
+  model->addOperation(ANEURALNETWORKS_ROI_POOLING, {in1, roi1, param2, param3, layout}, {out1});
+  // Phase 3, inputs and outputs
+  model->identifyInputsAndOutputs(
+    {in1, roi1},
+    {out1});
+  assert(model->isValid());
+}
+
+inline bool is_ignored_nchw_float16_2(int i) {
+  static std::set<int> ignore = {};
+  return ignore.find(i) != ignore.end();
+}
+
diff --git a/runtime/test/generated/tests/roi_pooling.mod.py.cpp b/runtime/test/generated/tests/roi_pooling.mod.py.cpp
index 923e9ea..80a02a9 100644
--- a/runtime/test/generated/tests/roi_pooling.mod.py.cpp
+++ b/runtime/test/generated/tests/roi_pooling.mod.py.cpp
@@ -27,6 +27,12 @@
             roi_pooling::get_examples_nhwc_quant8());
 }
 
+TEST_F(GeneratedTests, roi_pooling_nhwc_float16) {
+    execute(roi_pooling::CreateModel_nhwc_float16,
+            roi_pooling::is_ignored_nhwc_float16,
+            roi_pooling::get_examples_nhwc_float16());
+}
+
 TEST_F(GeneratedTests, roi_pooling_nchw) {
     execute(roi_pooling::CreateModel_nchw,
             roi_pooling::is_ignored_nchw,
@@ -45,6 +51,12 @@
             roi_pooling::get_examples_nchw_quant8());
 }
 
+TEST_F(GeneratedTests, roi_pooling_nchw_float16) {
+    execute(roi_pooling::CreateModel_nchw_float16,
+            roi_pooling::is_ignored_nchw_float16,
+            roi_pooling::get_examples_nchw_float16());
+}
+
 TEST_F(GeneratedTests, roi_pooling_nhwc_2) {
     execute(roi_pooling::CreateModel_nhwc_2,
             roi_pooling::is_ignored_nhwc_2,
@@ -63,6 +75,12 @@
             roi_pooling::get_examples_nhwc_quant8_2());
 }
 
+TEST_F(GeneratedTests, roi_pooling_nhwc_float16_2) {
+    execute(roi_pooling::CreateModel_nhwc_float16_2,
+            roi_pooling::is_ignored_nhwc_float16_2,
+            roi_pooling::get_examples_nhwc_float16_2());
+}
+
 TEST_F(GeneratedTests, roi_pooling_nchw_2) {
     execute(roi_pooling::CreateModel_nchw_2,
             roi_pooling::is_ignored_nchw_2,
@@ -81,3 +99,9 @@
             roi_pooling::get_examples_nchw_quant8_2());
 }
 
+TEST_F(GeneratedTests, roi_pooling_nchw_float16_2) {
+    execute(roi_pooling::CreateModel_nchw_float16_2,
+            roi_pooling::is_ignored_nchw_float16_2,
+            roi_pooling::get_examples_nchw_float16_2());
+}
+
diff --git a/runtime/test/generated/vts_models/roi_pooling.model.cpp b/runtime/test/generated/vts_models/roi_pooling.model.cpp
index cec26cc..55dbdcf 100644
--- a/runtime/test/generated/vts_models/roi_pooling.model.cpp
+++ b/runtime/test/generated/vts_models/roi_pooling.model.cpp
@@ -269,6 +269,95 @@
 }
 
 // Create the model
+Model createTestModel_nhwc_float16() {
+    const std::vector<Operand> operands = {
+        {
+            .type = OperandType::TENSOR_FLOAT16,
+            .dimensions = {1, 4, 4, 1},
+            .numberOfConsumers = 1,
+            .scale = 0.0f,
+            .zeroPoint = 0,
+            .lifetime = OperandLifeTime::MODEL_INPUT,
+            .location = {.poolIndex = 0, .offset = 0, .length = 0},
+        },
+        {
+            .type = OperandType::TENSOR_FLOAT16,
+            .dimensions = {4, 4},
+            .numberOfConsumers = 1,
+            .scale = 0.0f,
+            .zeroPoint = 0,
+            .lifetime = OperandLifeTime::MODEL_INPUT,
+            .location = {.poolIndex = 0, .offset = 0, .length = 0},
+        },
+        {
+            .type = OperandType::TENSOR_INT32,
+            .dimensions = {2},
+            .numberOfConsumers = 1,
+            .scale = 0.0f,
+            .zeroPoint = 0,
+            .lifetime = OperandLifeTime::CONSTANT_COPY,
+            .location = {.poolIndex = 0, .offset = 0, .length = 8},
+        },
+        {
+            .type = OperandType::FLOAT16,
+            .dimensions = {},
+            .numberOfConsumers = 1,
+            .scale = 0.0f,
+            .zeroPoint = 0,
+            .lifetime = OperandLifeTime::CONSTANT_COPY,
+            .location = {.poolIndex = 0, .offset = 8, .length = 2},
+        },
+        {
+            .type = OperandType::BOOL,
+            .dimensions = {},
+            .numberOfConsumers = 1,
+            .scale = 0.0f,
+            .zeroPoint = 0,
+            .lifetime = OperandLifeTime::CONSTANT_COPY,
+            .location = {.poolIndex = 0, .offset = 10, .length = 1},
+        },
+        {
+            .type = OperandType::TENSOR_FLOAT16,
+            .dimensions = {4, 2, 2, 1},
+            .numberOfConsumers = 0,
+            .scale = 0.0f,
+            .zeroPoint = 0,
+            .lifetime = OperandLifeTime::MODEL_OUTPUT,
+            .location = {.poolIndex = 0, .offset = 0, .length = 0},
+        }
+    };
+
+    const std::vector<Operation> operations = {
+        {
+            .type = OperationType::ROI_POOLING,
+            .inputs = {0, 1, 2, 3, 4},
+            .outputs = {5},
+        }
+    };
+
+    const std::vector<uint32_t> inputIndexes = {0, 1};
+    const std::vector<uint32_t> outputIndexes = {5};
+    std::vector<uint8_t> operandValues = {
+      2, 0, 0, 0, 2, 0, 0, 0, 0, 56, 0
+    };
+    const std::vector<hidl_memory> pools = {};
+
+    return {
+        .operands = operands,
+        .operations = operations,
+        .inputIndexes = inputIndexes,
+        .outputIndexes = outputIndexes,
+        .operandValues = operandValues,
+        .pools = pools,
+    };
+}
+
+inline bool is_ignored_nhwc_float16(int i) {
+  static std::set<int> ignore = {};
+  return ignore.find(i) != ignore.end();
+}
+
+// Create the model
 Model createTestModel_nchw() {
     const std::vector<Operand> operands = {
         {
@@ -537,6 +626,95 @@
 }
 
 // Create the model
+Model createTestModel_nchw_float16() {
+    const std::vector<Operand> operands = {
+        {
+            .type = OperandType::TENSOR_FLOAT16,
+            .dimensions = {1, 1, 4, 4},
+            .numberOfConsumers = 1,
+            .scale = 0.0f,
+            .zeroPoint = 0,
+            .lifetime = OperandLifeTime::MODEL_INPUT,
+            .location = {.poolIndex = 0, .offset = 0, .length = 0},
+        },
+        {
+            .type = OperandType::TENSOR_FLOAT16,
+            .dimensions = {4, 4},
+            .numberOfConsumers = 1,
+            .scale = 0.0f,
+            .zeroPoint = 0,
+            .lifetime = OperandLifeTime::MODEL_INPUT,
+            .location = {.poolIndex = 0, .offset = 0, .length = 0},
+        },
+        {
+            .type = OperandType::TENSOR_INT32,
+            .dimensions = {2},
+            .numberOfConsumers = 1,
+            .scale = 0.0f,
+            .zeroPoint = 0,
+            .lifetime = OperandLifeTime::CONSTANT_COPY,
+            .location = {.poolIndex = 0, .offset = 0, .length = 8},
+        },
+        {
+            .type = OperandType::FLOAT16,
+            .dimensions = {},
+            .numberOfConsumers = 1,
+            .scale = 0.0f,
+            .zeroPoint = 0,
+            .lifetime = OperandLifeTime::CONSTANT_COPY,
+            .location = {.poolIndex = 0, .offset = 8, .length = 2},
+        },
+        {
+            .type = OperandType::BOOL,
+            .dimensions = {},
+            .numberOfConsumers = 1,
+            .scale = 0.0f,
+            .zeroPoint = 0,
+            .lifetime = OperandLifeTime::CONSTANT_COPY,
+            .location = {.poolIndex = 0, .offset = 10, .length = 1},
+        },
+        {
+            .type = OperandType::TENSOR_FLOAT16,
+            .dimensions = {4, 1, 2, 2},
+            .numberOfConsumers = 0,
+            .scale = 0.0f,
+            .zeroPoint = 0,
+            .lifetime = OperandLifeTime::MODEL_OUTPUT,
+            .location = {.poolIndex = 0, .offset = 0, .length = 0},
+        }
+    };
+
+    const std::vector<Operation> operations = {
+        {
+            .type = OperationType::ROI_POOLING,
+            .inputs = {0, 1, 2, 3, 4},
+            .outputs = {5},
+        }
+    };
+
+    const std::vector<uint32_t> inputIndexes = {0, 1};
+    const std::vector<uint32_t> outputIndexes = {5};
+    std::vector<uint8_t> operandValues = {
+      2, 0, 0, 0, 2, 0, 0, 0, 0, 56, 1
+    };
+    const std::vector<hidl_memory> pools = {};
+
+    return {
+        .operands = operands,
+        .operations = operations,
+        .inputIndexes = inputIndexes,
+        .outputIndexes = outputIndexes,
+        .operandValues = operandValues,
+        .pools = pools,
+    };
+}
+
+inline bool is_ignored_nchw_float16(int i) {
+  static std::set<int> ignore = {};
+  return ignore.find(i) != ignore.end();
+}
+
+// Create the model
 Model createTestModel_nhwc_2() {
     const std::vector<Operand> operands = {
         {
@@ -805,6 +983,95 @@
 }
 
 // Create the model
+Model createTestModel_nhwc_float16_2() {
+    const std::vector<Operand> operands = {
+        {
+            .type = OperandType::TENSOR_FLOAT16,
+            .dimensions = {2, 4, 8, 2},
+            .numberOfConsumers = 1,
+            .scale = 0.0f,
+            .zeroPoint = 0,
+            .lifetime = OperandLifeTime::MODEL_INPUT,
+            .location = {.poolIndex = 0, .offset = 0, .length = 0},
+        },
+        {
+            .type = OperandType::TENSOR_FLOAT16,
+            .dimensions = {4, 5},
+            .numberOfConsumers = 1,
+            .scale = 0.0f,
+            .zeroPoint = 0,
+            .lifetime = OperandLifeTime::MODEL_INPUT,
+            .location = {.poolIndex = 0, .offset = 0, .length = 0},
+        },
+        {
+            .type = OperandType::TENSOR_INT32,
+            .dimensions = {2},
+            .numberOfConsumers = 1,
+            .scale = 0.0f,
+            .zeroPoint = 0,
+            .lifetime = OperandLifeTime::CONSTANT_COPY,
+            .location = {.poolIndex = 0, .offset = 0, .length = 8},
+        },
+        {
+            .type = OperandType::FLOAT16,
+            .dimensions = {},
+            .numberOfConsumers = 1,
+            .scale = 0.0f,
+            .zeroPoint = 0,
+            .lifetime = OperandLifeTime::CONSTANT_COPY,
+            .location = {.poolIndex = 0, .offset = 8, .length = 2},
+        },
+        {
+            .type = OperandType::BOOL,
+            .dimensions = {},
+            .numberOfConsumers = 1,
+            .scale = 0.0f,
+            .zeroPoint = 0,
+            .lifetime = OperandLifeTime::CONSTANT_COPY,
+            .location = {.poolIndex = 0, .offset = 10, .length = 1},
+        },
+        {
+            .type = OperandType::TENSOR_FLOAT16,
+            .dimensions = {4, 2, 3, 2},
+            .numberOfConsumers = 0,
+            .scale = 0.0f,
+            .zeroPoint = 0,
+            .lifetime = OperandLifeTime::MODEL_OUTPUT,
+            .location = {.poolIndex = 0, .offset = 0, .length = 0},
+        }
+    };
+
+    const std::vector<Operation> operations = {
+        {
+            .type = OperationType::ROI_POOLING,
+            .inputs = {0, 1, 2, 3, 4},
+            .outputs = {5},
+        }
+    };
+
+    const std::vector<uint32_t> inputIndexes = {0, 1};
+    const std::vector<uint32_t> outputIndexes = {5};
+    std::vector<uint8_t> operandValues = {
+      2, 0, 0, 0, 3, 0, 0, 0, 0, 52, 0
+    };
+    const std::vector<hidl_memory> pools = {};
+
+    return {
+        .operands = operands,
+        .operations = operations,
+        .inputIndexes = inputIndexes,
+        .outputIndexes = outputIndexes,
+        .operandValues = operandValues,
+        .pools = pools,
+    };
+}
+
+inline bool is_ignored_nhwc_float16_2(int i) {
+  static std::set<int> ignore = {};
+  return ignore.find(i) != ignore.end();
+}
+
+// Create the model
 Model createTestModel_nchw_2() {
     const std::vector<Operand> operands = {
         {
@@ -1072,3 +1339,92 @@
   return ignore.find(i) != ignore.end();
 }
 
+// Create the model
+Model createTestModel_nchw_float16_2() {
+    const std::vector<Operand> operands = {
+        {
+            .type = OperandType::TENSOR_FLOAT16,
+            .dimensions = {2, 2, 4, 8},
+            .numberOfConsumers = 1,
+            .scale = 0.0f,
+            .zeroPoint = 0,
+            .lifetime = OperandLifeTime::MODEL_INPUT,
+            .location = {.poolIndex = 0, .offset = 0, .length = 0},
+        },
+        {
+            .type = OperandType::TENSOR_FLOAT16,
+            .dimensions = {4, 5},
+            .numberOfConsumers = 1,
+            .scale = 0.0f,
+            .zeroPoint = 0,
+            .lifetime = OperandLifeTime::MODEL_INPUT,
+            .location = {.poolIndex = 0, .offset = 0, .length = 0},
+        },
+        {
+            .type = OperandType::TENSOR_INT32,
+            .dimensions = {2},
+            .numberOfConsumers = 1,
+            .scale = 0.0f,
+            .zeroPoint = 0,
+            .lifetime = OperandLifeTime::CONSTANT_COPY,
+            .location = {.poolIndex = 0, .offset = 0, .length = 8},
+        },
+        {
+            .type = OperandType::FLOAT16,
+            .dimensions = {},
+            .numberOfConsumers = 1,
+            .scale = 0.0f,
+            .zeroPoint = 0,
+            .lifetime = OperandLifeTime::CONSTANT_COPY,
+            .location = {.poolIndex = 0, .offset = 8, .length = 2},
+        },
+        {
+            .type = OperandType::BOOL,
+            .dimensions = {},
+            .numberOfConsumers = 1,
+            .scale = 0.0f,
+            .zeroPoint = 0,
+            .lifetime = OperandLifeTime::CONSTANT_COPY,
+            .location = {.poolIndex = 0, .offset = 10, .length = 1},
+        },
+        {
+            .type = OperandType::TENSOR_FLOAT16,
+            .dimensions = {4, 2, 2, 3},
+            .numberOfConsumers = 0,
+            .scale = 0.0f,
+            .zeroPoint = 0,
+            .lifetime = OperandLifeTime::MODEL_OUTPUT,
+            .location = {.poolIndex = 0, .offset = 0, .length = 0},
+        }
+    };
+
+    const std::vector<Operation> operations = {
+        {
+            .type = OperationType::ROI_POOLING,
+            .inputs = {0, 1, 2, 3, 4},
+            .outputs = {5},
+        }
+    };
+
+    const std::vector<uint32_t> inputIndexes = {0, 1};
+    const std::vector<uint32_t> outputIndexes = {5};
+    std::vector<uint8_t> operandValues = {
+      2, 0, 0, 0, 3, 0, 0, 0, 0, 52, 1
+    };
+    const std::vector<hidl_memory> pools = {};
+
+    return {
+        .operands = operands,
+        .operations = operations,
+        .inputIndexes = inputIndexes,
+        .outputIndexes = outputIndexes,
+        .operandValues = operandValues,
+        .pools = pools,
+    };
+}
+
+inline bool is_ignored_nchw_float16_2(int i) {
+  static std::set<int> ignore = {};
+  return ignore.find(i) != ignore.end();
+}
+
diff --git a/runtime/test/specs/V1_2/roi_pooling.mod.py b/runtime/test/specs/V1_2/roi_pooling.mod.py
index 243bb76..af8cb88 100644
--- a/runtime/test/specs/V1_2/roi_pooling.mod.py
+++ b/runtime/test/specs/V1_2/roi_pooling.mod.py
@@ -47,7 +47,7 @@
         -1, 9, 10, 3,
         -2, 9,  7, 3
     ]
-}).AddNchw(i1, o1, layout).AddVariations("relaxed", quant8)
+}).AddNchw(i1, o1, layout).AddVariations("relaxed", quant8, "float16")
 
 
 # TEST 2: ROI_ALIGN_2, outputShape = [2, 3], spatialScale = 0.25, samplingRatio = 4
@@ -91,4 +91,4 @@
         9.50, 6.73, 9.50, 9.28, 6.89, 8.97, 6.18, 9.63, 9.99, 9.85, 9.99, 9.85,
         7.29, 6.94, 7.29, 6.94, 2.31, 6.88, 7.90, 6.78, 7.90, 6.82, 4.64, 6.82
     ]
-}).AddNchw(i2, o2, layout).AddVariations("relaxed", quant8)
+}).AddNchw(i2, o2, layout).AddVariations("relaxed", quant8, "float16")