供應商擴展

Android 10 中引入的神經網路 API (NNAPI) 供應商擴充功能是供應商定義的操作和資料類型的集合。在運行 NN HAL 1.2 或更高版本的裝置上,驅動程式可以透過支援相應的供應商擴充功能來提供自訂硬體加速操作。供應商擴展不會修改現有操作的行為。

供應商擴展為 OEM 操作和資料類型提供了更結構化的替代方案,後者在 Android 10 中已棄用。有關詳細信息,請參閱OEM 操作和資料類型

擴充功能使用許可名單

供應商擴充只能由/product/vendor/odm/data分區上明確指定的 Android 應用程式和本機二進位使用。位於/system分區的應用程式和本機二進位檔案無法使用供應商擴充。

允許使用 NNAPI 供應商擴充功能的 Android 應用程式和二進位檔案的清單儲存在/vendor/etc/nnapi_extensions_app_allowlist中。文件的每一行都包含一個新條目。條目可以是以斜線 (/) 為前綴的本機二進位路徑(例如/data/foo ),也可以是 Android 應用程式套件的名稱(例如com.foo.bar )。

允許清單是從 NNAPI 運行時共享庫強制執行的。該庫可以防止意外使用,但不能防止直接使用 NNAPI 驅動程式 HAL 介面的應用程式故意規避。

供應商擴展定義

供應商創建並維護帶有擴展定義的頭文件。擴充定義的完整範例可以在example/fibonacci/FibonacciExtension.h中找到。

每個擴充功能必須有一個以供應商的反向網域開頭的唯一名稱。

const char EXAMPLE_EXTENSION_NAME[] = "com.example.my_extension";

該名稱充當操作和資料類型的命名空間。 NNAPI 使用此名稱來區分供應商擴充。

操作和資料類型的聲明方式與runtime/include/NeuralNetworks.h中的方式類似。

enum {
    /**
     * A custom scalar type.
     */
    EXAMPLE_SCALAR = 0,

    /**
     * A custom tensor type.
     *
     * Attached to this tensor is {@link ExampleTensorParams}.
     */
    EXAMPLE_TENSOR = 1,
};

enum {
    /**
     * Computes example function.
     *
     * Inputs:
     * * 0: A scalar of {@link EXAMPLE_SCALAR}.
     *
     * Outputs:
     * * 0: A tensor of {@link EXAMPLE_TENSOR}.
     */
    EXAMPLE_FUNCTION = 0,
};

擴充操作可以使用任何操作數類型,包括非擴充操作數類型和來自其他擴充的操作數類型。當使用另一個擴展的操作數類型時,驅動程式必須支援另一個擴展。

擴充功能還可以聲明自訂結構來伴隨擴展操作數。

/**
 * Quantization parameters for {@link EXAMPLE_TENSOR}.
 */
typedef struct ExampleTensorParams {
    double scale;
    int64_t zeroPoint;
} ExampleTensorParams;

在 NNAPI 用戶端中使用擴充

runtime/include/NeuralNetworksExtensions.h (C API) 檔案提供執行時間擴充支援。本節提供 C API 的概述。

若要檢查設備是否支援擴展,請使用ANeuralNetworksDevice_getExtensionSupport

bool isExtensionSupported;
CHECK_EQ(ANeuralNetworksDevice_getExtensionSupport(device, EXAMPLE_EXTENSION_NAME,
                                                   &isExtensionSupported),
         ANEURALNETWORKS_NO_ERROR);
if (isExtensionSupported) {
    // The device supports the extension.
    ...
}

若要建立具有擴充操作數的模型,請使用ANeuralNetworksModel_getExtensionOperandType取得運算元類型並呼叫ANeuralNetworksModel_addOperand

int32_t type;
CHECK_EQ(ANeuralNetworksModel_getExtensionOperandType(model, EXAMPLE_EXTENSION_NAME, EXAMPLE_TENSOR, &type),
         ANEURALNETWORKS_NO_ERROR);
ANeuralNetworksOperandType operandType{
        .type = type,
        .dimensionCount = dimensionCount,
        .dimensions = dimensions,
};
CHECK_EQ(ANeuralNetworksModel_addOperand(model, &operandType), ANEURALNETWORKS_NO_ERROR);

(可選)使用ANeuralNetworksModel_setOperandExtensionData將附加資料與擴充操作數關聯。

ExampleTensorParams params{
        .scale = 0.5,
        .zeroPoint = 128,
};
CHECK_EQ(ANeuralNetworksModel_setOperandExtensionData(model, operandIndex, &params, sizeof(params)),
         ANEURALNETWORKS_NO_ERROR);

若要建立具有擴充操作的模型,請使用ANeuralNetworksModel_getExtensionOperationType取得操作類型並呼叫ANeuralNetworksModel_addOperation

ANeuralNetworksOperationType type;
CHECK_EQ(ANeuralNetworksModel_getExtensionOperationType(model, EXAMPLE_EXTENSION_NAME, EXAMPLE_FUNCTION,
                                                        &type),
         ANEURALNETWORKS_NO_ERROR);
CHECK_EQ(ANeuralNetworksModel_addOperation(model, type, inputCount, inputs, outputCount, outputs),
         ANEURALNETWORKS_NO_ERROR);

向 NNAPI 驅動程式新增擴充支援

驅動程式透過IDevice::getSupportedExtensions方法報告支援的擴充。傳回的清單必須包含描述每個支援的擴充功能的條目。

Extension {
    .name = EXAMPLE_EXTENSION_NAME,
    .operandTypes = {
        {
            .type = EXAMPLE_SCALAR,
            .isTensor = false,
            .byteSize = 8,
        },
        {
            .type = EXAMPLE_TENSOR,
            .isTensor = true,
            .byteSize = 8,
        },
    },
}

在用於標識類型和操作的 32 位元中,高Model::ExtensionTypeEncoding::HIGH_BITS_PREFIX位元是擴充前綴,低Model::ExtensionTypeEncoding::LOW_BITS_TYPE位元表示擴充的類型或操作。

處理操作或操作數類型時,驅動程式必須檢查擴充前綴。如果擴充前綴具有非零值,則運算或操作數類型是擴充類型。如果值為0 ,則運算或操作數類型不是擴充類型。

若要將前綴對應到副檔名,請在model.extensionNameToPrefix中尋找。對於給定模型,從前綴到擴展名稱的映射是一對一對應(雙射)。不同的前綴值可能對應不同型號中的相同擴展名。

驅動程式必須驗證擴充操作和資料類型,因為 NNAPI 運行時無法驗證特定的擴充操作和資料類型。

擴展操作數可以在operand.extraParams.extension中具有關聯數據,運行時將其視為任意大小的原始數據 blob。

OEM操作和資料類型

NNAPI 具有 OEM 操作和 OEM 資料類型,可讓裝置製造商提供自訂的、特定於驅動程式的功能。這些操作和資料類型僅由 OEM 應用程式使用。 OEM 操作和資料類型的語意是 OEM 特定的,並且可以隨時變更。 OEM 操作和資料類型使用OperationType::OEM_OPERATIONOperandType::OEMOperandType::TENSOR_OEM_BYTE進行編碼。