aclnnRasterizer

๐Ÿ“„ View Source Code

Applicable Products

ProductSupported
Ascend 950PR/Ascend 950DTร—
Atlas A3 training products/Atlas A3 inference productsโˆš
Atlas A2 training products/Atlas A2 inference productsโˆš
Atlas 200I/500 A2 inference productsร—
Atlas inference productsร—
Atlas training productsร—

Function

  • Description: Implements rasterization computation. Based on given points and faces in 3D space, obtains the minimum depth of each pixel on the screen and its corresponding face index, and computes the perspective-corrected interpolation of barycentric coordinates for that face.

  • Formula: findicesfindices records the face index corresponding to the minimum depth of each pixel, and barycentricbarycentric records the perspective-corrected interpolation of barycentric coordinates of each vertex relative to the face recorded in findicesfindices. The Z-Buffer used in the computation records the minimum depth zminโก(x,y)z_{\min}(x, y) of each pixel (x,y)(x, y) and the triangle face index face_idx(x,y)\text{face\_idx}(x, y) corresponding to that depth.

    The computation process is as follows: For each triangle face ff in space:

    1. Convert the three vertex coordinates v0v_0, v1v_1, v2v_2 of ff to screen coordinates vs0v_{s0}, vs1v_{s1}, vs2v_{s2}

    2. Compute the bounding rectangle of ff based on vs0v_{s0}, vs1v_{s1}, vs2v_{s2}

    3. For each pixel vi=(xi,yi)v_i = (x_i, y_i) within the rectangle, perform the following operations:

      a. Compute the pixel center coordinate vcv_c b. Compute the barycentric coordinates (ฮฑ,ฮฒ,ฮณ)(\alpha, \beta, \gamma) of vcv_c relative to triangle ff c. Determine whether vcv_c lies inside the triangle based on (ฮฑ,ฮฒ,ฮณ)(\alpha, \beta, \gamma). If vcv_c is not inside the triangle, proceed to the next pixel within the rectangle; otherwise, perform the following steps d. Use (ฮฑ,ฮฒ,ฮณ)(\alpha, \beta, \gamma) and vs0v_{s0}, vs1v_{s1}, vs2v_{s2} to obtain the depth value depth of the current pixel e. If depth prior is enabled; otherwise, directly proceed to the next step "Z-Buffer Update"

      • Compute the depth threshold depth_thres using the depth prior map
      • If depth < depth_thres, process the next pixel in the rectangle; otherwise, perform the following steps.

      f. Z-Buffer update:

      • If depth<zminโก(xi,yi)depth < z_{\min}(x_i, y_i):
      zminโก(xi,yi)โ†depthface_idx(xi,yi)โ†f\quad z_{\min}(x_i, y_i) \gets \text{depth} \\ \quad \text{face\_idx}(x_i, y_i) \gets f
      • If depth=zminโก(xi,yi)depth = z_{\min}(x_i, y_i):
      face_idx(xi,yi)โ†minโก(face_idx(xi,yi),ย f)\quad \text{face\_idx}(x_i, y_i) \gets \min(\text{face\_idx}(x_i, y_i),\ f)

    After processing all triangular faces in space according to the above steps, for each pixel vi=(xi,yi)v_i = (x_i, y_i) on the screen of size heightโˆ—widthheight * width:

    1. Retrieve the face index fidxf_{idx} corresponding to viv_i in the Z-Buffer, findices(xi,yi)โ†fidxfindices (x_i, y_i) \gets f_{idx}
    2. Convert the three vertex coordinates v0v_0, v1v_1, v2v_2 of ff to screen coordinates vs0v_{s0}, vs1v_{s1}, vs2v_{s2}
    3. Compute the center coordinates vcv_c of viv_i
    4. Compute the barycentric coordinates (ฮฑ,ฮฒ,ฮณ)(\alpha, \beta, \gamma) of vcv_c relative to triangle ff
    5. Use (ฮฑ,ฮฒ,ฮณ)(\alpha, \beta, \gamma) to compute the perspective-corrected interpolation (ฮฑ~,ฮฒ~,ฮณ~)(\tilde{\alpha}, \tilde{\beta}, \tilde{\gamma})
    6. barycentric(xi,yi)โ†(ฮฑ~,ฮฒ~,ฮณ~)barycentric(x_i, y_i) \gets (\tilde{\alpha}, \tilde{\beta}, \tilde{\gamma})

    The specific computation methods involved are as follows:

    • Convert vertex v=(x,y,z,w)v = (x, y, z, w) to screen coordinates vs=(xs,ys,zs)v_s = (x_s, y_s, z_s)

      xs=(x/wโˆ—0.5+0.5)โˆ—(widthโˆ’1)+0.5ys=(0.5+0.5โˆ—y/w)โˆ—(heightโˆ’1)+0.5zs=z/wโˆ—0.49999+0.5x_s = (x / w * 0.5 + 0.5) * (width - 1) + 0.5\\ y_s = (0.5 + 0.5 * y / w) * (height - 1) + 0.5\\ z_s = z / w * 0.49999 + 0.5
    • The barycentric coordinates (ฮฑ,ฮฒ,ฮณ)(\alpha, \beta, \gamma) of point vv relative to triangle (v0,v1,v2)(v_0, v_1, v_2)

      1. Compute the signed areas areaarea, beta_tribeta\_tri, and gamma_trigamma\_tri of triangles (v0,v1,v2)(v_0, v_1, v_2), (v0,v,v2)(v_0, v, v_2), and (v0,v1,v)(v_0, v_1, v), respectively
      2. If areaarea is 0, then ฮฑ=ฮฒ=ฮณ=โˆ’1\alpha = \beta = \gamma = -1; otherwise
      ฮฒ=beta_tri/areaฮณ=gamma_tri/areaฮฑ=1โˆ’ฮฒโˆ’ฮณ\beta = beta\_tri / area\\ \gamma = gamma\_tri / area\\ \alpha = 1 - \beta - \gamma
    • The signed area of the triangle formed by vertices v0=(x0,y0,z0)v_0 = (x_0, y_0, z_0), v1=(x1,y1,z1)v_1 = (x_1, y_1, z_1), and v2=(x2,y2,z2)v_2 = (x_2, y_2, z_2)

      area=(x2โˆ’x0)โˆ—(y1โˆ’y0)โˆ’(x1โˆ’x0)โˆ—(y2โˆ’y0)area = (x_2 - x_0) * (y_1 - y_0) - (x_1 - x_0) * (y_2 - y_0)
    • Compute the depth depthdepth of pixel v=(x,y)v = (x, y) by combining the barycentric coordinates (ฮฑ,ฮฒ,ฮณ)(\alpha, \beta, \gamma) and the triangle screen coordinates v0=(x0,y0,z0)v_0 = (x_0, y_0, z_0), v1=(x1,y1,z1)v_1 = (x_1, y_1, z_1), and v2=(x2,y2,z2)v_2 = (x_2, y_2, z_2)

      depth=ฮฑโˆ—z0+ฮฒโˆ—z1+ฮณโˆ—z2depth = \alpha * z_0 + \beta * z_1 + \gamma * z_2
    • Compute the depth threshold depth_thresdepth\_thres for point v=(x,y)v = (x, y) by combining the depth map dd and the occlusion truncation occlusion_truncationocclusion\_truncation

      depth_thres=d(x,y)โˆ—0.49999+0.5+occlusion_truncationdepth\_thres = d(x, y) * 0.49999 + 0.5 + occlusion\_truncation
    • Determine whether the vertex lies inside the triangle based on the barycentric coordinates (ฮฑ,ฮฒ,ฮณ)(\alpha, \beta, \gamma) If ฮฑ>=0\alpha >= 0, ฮฒ>=0\beta >= 0, and ฮณ>=0\gamma >= 0, the point is inside the triangle (including on the triangle edges); otherwise, the point is outside the triangle.

    • Compute the perspective-corrected interpolation (ฮป0corrected,ฮป1corrected,ฮป2corrected)(\lambda_0^{corrected}, \lambda_1^{corrected}, \lambda_2^{corrected}) using the barycentric coordinates (ฮป0,ฮป1,ฮป2)(\lambda_0, \lambda_1, \lambda_2) and the three vertex coordinates of the triangle: v0=(x0,y0,z0,w0)v_0 = (x_0, y_0, z_0, w_0), v1=(x1,y1,z1,w1)v_1 = (x_1, y_1, z_1, w_1), and v2=(x2,y2,z2,w2)v_2 = (x_2, y_2, z_2, w_2)

      ฮปicorrected=ฮปi/wiโˆ‘(ฮปj/wj)\lambda_i^{corrected} = \frac{\lambda_i / w_i} { \sum (\lambda_j / w_j)}

Function Prototype

Each operator is divided into a two-phase API. You must first call the "aclnnRasterizerGetWorkspaceSize" API to obtain the workspace size required for computation and the executor that contains the operator computation process, and then call the "aclnnRasterizer" API to execute the computation.

aclnnStatus aclnnRasterizerGetWorkspaceSize(
    const aclTensor *v,
    const aclTensor *f,
    const aclTensor *dOptional,
    int64_t          width,
    int64_t          height,
    double           occlusionTruncation,
    int64_t          useDepthPrior,
    const aclTensor *findicesOut,
    const aclTensor *barycentricOut,
    uint64_t        *workspaceSize,
    aclOpExecutor   **executor)
aclnnStatus aclnnRasterizer(
    void          *workspace,
    uint64_t       workspaceSize,
    aclOpExecutor *executor,
    aclrtStream    stream)

aclnnRasterizerGetWorkspaceSize

  • Parameters

    Parameter Name Input/Output Description Usage Description Data Type Data Format Dimension (shape) Non-contiguous Tensor
    v (aclTensor*) Input Input tensor representing vertex coordinates in space.
    • Empty tensors are not supported.
    • Shape: (numVertices, 4), where numVertices is a positive integer indicating the number of vertices. Each vertex coordinate is represented as (x, y, z, w).
    FLOAT32 ND 2 ร—
    f (aclTensor*) Input Input tensor representing faces in space.
    • Empty tensors are not supported.
    • The shape is (numFaces, 3), where numFaces represents the number of faces in space and is a positive integer. Each face is a triangle, and each vertex of the triangle is represented as an index of the vertex in v. Therefore, the element values in f should be valid indices into v, i.e., the value range is [0, numVertices-1]. The caller is responsible for ensuring that the elements in f are valid.
    INT32 ND 2 ร—
    dOptional (aclTensor*) Input Represents the input tensor of the depth map, used to compute the depth threshold.
    • Optional input. An empty Tensor is supported.
    • This parameter does not take effect.
    FLOAT32 ND 2 ร—
    width (int64_t) Input Screen width. Value range: [1, 4096]. - - - -
    height (int64_t) Input Screen height. Value range: [1, 4096]. - - - -
    occlusionTruncation (double) Input Occlusion truncation, used to calculate the depth threshold. This parameter does not take effect. - - - -
    useDepthPrior (int64_t) Input Indicates whether to apply depth prior.
    • The value is 0 or 1. 1 indicates that depth prior is applied, and 0 indicates that depth prior is not applied.
    • This operator does not support applying depth prior, so the value is fixed at 0.
    - - - -
    findicesOut (aclTensor*) Output Indicates the index of the face corresponding to the minimum depth of each pixel on the screen.
    • Empty tensors are not supported.
    • Shape: (height, width), where height is the screen height and width is the screen width.
    INT32 ND 2 ร—
    barycentricOut (aclTensor*) Output The output tensor representing the perspective-corrected interpolation of barycentric coordinates for each pixel on the screen relative to the face corresponding to the minimum depth.
    • Empty tensors are not supported.
    • The shape is (height, width, 3), where height is the screen height and width is the screen width.
    FLOAT32 ND 3 ร—
    workspaceSize (uint64_t*) Output Returns the workspace size to be allocated on the Device side. - - - - -
    executor (aclOpExecutor**) Output Returns the op executor, which contains the operator compute flow. - - - - -
  • Return Value

    aclnnStatus: return code. For details, see aclnn Return Codes.

    The first-phase API performs input parameter validation and reports an error in the following scenarios:

    Return Code Error Code Description
    ACLNN_ERR_PARAM_NULLPTR 161001 The input v, f, findicesOut, or barycentricOut is a null pointer.
    ACLNN_ERR_PARAM_INVALID 161002 The data type of v, f, findicesOut, or barycentricOut is not within the supported range.
    ACLNN_ERR_INNER_TILING_ERROR 561002 The shape of v, f, findicesOut, or barycentricOut is not within the supported range.
    The value of useDepthPrior, height, or width is not within the supported range.

aclnnRasterizer

  • Parameters

    Parameter Name Input/Output Description
    workspace Input The workspace memory address allocated on the Device side.
    workspaceSize Input Size of the workspace requested on the Device side, obtained through the first-phase API aclnnRasterizerGetWorkspaceSize.
    executor Input Operator executor, which encapsulates the operator computation flow.
    stream Input Specifies the stream on which the task is executed.
  • Return Value

    aclnnStatus: return code. For details, see aclnn Return Codes.

Constraints

  • Only the scenario where useDepthPrior is 0 is supported. The parameters dOptional, occlusionTruncation, and useDepthPrior do not take effect in actual computation.

  • Deterministic computation:

    • aclnnRasterizer uses deterministic implementation by default.

Example

The following sample code is for reference only. For details about compilation and execution, see Compile and Run Samples.

#include <iostream>
#include <vector>
#include "acl/acl.h"
#include "aclnnop/aclnn_rasterizer.h"

#define CHECK_RET(cond, return_expr) \
    do {                             \
        if (!(cond)) {               \
            return_expr;             \
        }                            \
    } while (0)

#define LOG_PRINT(message, ...)         \
    do {                                \
        printf(message, ##__VA_ARGS__); \
    } while (0)

int64_t GetShapeSize(const std::vector<int64_t> &shape)
{
    int64_t shapeSize = 1;
    for (auto i : shape) {
        shapeSize *= i;
    }
    return shapeSize;
}

int Init(int32_t deviceId, aclrtStream *stream)
{
    // Boilerplate: resource initialization.
    auto ret = aclInit(nullptr);
    CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclInit failed. ERROR: %d\n", ret); return ret);
    ret = aclrtSetDevice(deviceId);
    CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSetDevice failed. ERROR: %d\n", ret); return ret);
    ret = aclrtCreateStream(stream);
    CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtCreateStream failed. ERROR: %d\n", ret); return ret);
    return 0;
}

template <typename T>
int CreateAclTensor(const std::vector<T> &hostData, const std::vector<int64_t> &shape, void **deviceAddr,
    aclDataType dataType, aclTensor **tensor)
{
    auto size = GetShapeSize(shape) * sizeof(T);
    // Call aclrtMalloc to allocate device-side memory.
    auto ret = aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST);
    CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMalloc failed. ERROR: %d\n", ret); return ret);
    // Call aclrtMemcpy to copy host-side data to the device-side memory.
    ret = aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE);
    CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMemcpy failed. ERROR: %d\n", ret); return ret);

    // Compute the strides of a contiguous tensor.
    std::vector<int64_t> strides(shape.size(), 1);
    for (int64_t i = shape.size() - 2; i >= 0; i--) {
        strides[i] = shape[i + 1] * strides[i + 1];
    }

    // Call the aclCreateTensor API to create an aclTensor.
    *tensor = aclCreateTensor(shape.data(),
        shape.size(),
        dataType,
        strides.data(),
        0,
        aclFormat::ACL_FORMAT_ND,
        shape.data(),
        shape.size(),
        *deviceAddr);
    return 0;
}

int main()
{
    // 1. (Boilerplate) Initialize the device/stream. See the acl API manual.
    // Fill in the deviceId based on your actual device.
    int32_t deviceId = 0;
    aclrtStream stream;
    auto ret = Init(deviceId, &stream);
    CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("Init acl failed. ERROR: %d\n", ret); return ret);

    std::vector<int64_t> vShape = {3, 4};
    std::vector<int64_t> fShape = {1, 3};
    std::vector<int64_t> dShape = {10, 10};
    std::vector<int64_t> findicesShape = {10, 10};
    std::vector<int64_t> baryShape = {10, 10, 3};
    int64_t height = 10;
    int64_t width = 10;
    float occlusionTruncation = 0.0f;
    int64_t useDepthPrior = 0;
    std::vector<float> vData = {6.0f, 4.0f, 1.0f, 6.9f, 7.0928106f, 0.3491799f, 3.0046327f, 6.6574745f,
                                7.308903f, 7.6934705f, 0.1315008f, 3.9899914f};
    std::vector<int32_t> fData = {2, 1, 0};
    std::vector<float> dData(100, 0.0f);
    std::vector<int32_t> findicesData(100, 0);
    std::vector<float> baryData(10 * 10 * 3, 0.0f);

    void *vDeviceAddr = nullptr;
    void *fDeviceAddr = nullptr;
    void *dDeviceAddr = nullptr;
    void *findicesDeviceAddr = nullptr;
    void *baryDeviceAddr = nullptr;

    aclTensor *v = nullptr;
    aclTensor *f = nullptr;
    aclTensor *d = nullptr;
    aclTensor *findices = nullptr;
    aclTensor *barycentric = nullptr;

    ret = CreateAclTensor(vData, vShape, &vDeviceAddr, aclDataType::ACL_FLOAT, &v);
    CHECK_RET(ret == ACL_SUCCESS, return ret);

    ret = CreateAclTensor(fData, fShape, &fDeviceAddr, aclDataType::ACL_INT32, &f);
    CHECK_RET(ret == ACL_SUCCESS, return ret);

    ret = CreateAclTensor(dData, dShape, &dDeviceAddr, aclDataType::ACL_FLOAT, &d);
    CHECK_RET(ret == ACL_SUCCESS, return ret);

    ret = CreateAclTensor(findicesData, findicesShape, &findicesDeviceAddr, aclDataType::ACL_INT32, &findices);
    CHECK_RET(ret == ACL_SUCCESS, return ret);

    ret = CreateAclTensor(baryData, baryShape, &baryDeviceAddr, aclDataType::ACL_FLOAT, &barycentric);
    CHECK_RET(ret == ACL_SUCCESS, return ret);

    // 3. Call the CANN operator library API. Replace it with the specific API name.
    uint64_t workspaceSize = 0;
    aclOpExecutor *executor;
    // Call the first-phase API of aclnnRasterizer.
    ret = aclnnRasterizerGetWorkspaceSize(v, f, d, width, height, occlusionTruncation, useDepthPrior, findices,
                                            barycentric, &workspaceSize, &executor);
    CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnRasterizerGetWorkspaceSize failed. ERROR: %d\n", ret);
              return ret);
    // Allocate device memory based on the workspaceSize calculated by the first-phase API.
    void *workspaceAddr = nullptr;
    if (workspaceSize > 0) {
        ret = aclrtMalloc(&workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST);
        CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("allocate workspace failed. ERROR: %d\n", ret); return ret);
    }
    // Call the second-phase API of aclnnRasterizer.
    ret = aclnnRasterizer(workspaceAddr, workspaceSize, executor, stream);
    CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnRasterizer failed. ERROR: %d\n", ret); return ret);

    // 4. (Standard practice) Synchronize and wait for task execution to complete.
    ret = aclrtSynchronizeStream(stream);
    CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", ret); return ret);

    // 5. Obtain the output values and copy the results from device memory to host memory. Modify this based on the specific API definition.
    auto findicesSize = GetShapeSize(findicesShape);
    std::vector<int32_t> findicesOutData(findicesSize, 0);
    ret = aclrtMemcpy(findicesOutData.data(),
        findicesSize * sizeof(findicesOutData[0]),
        findicesDeviceAddr,
        findicesSize * sizeof(findicesOutData[0]),
        ACL_MEMCPY_DEVICE_TO_HOST);
    CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("copy result from device to host failed. ERROR: %d\n", ret); return ret);

    auto barycentricSize = GetShapeSize(baryShape);
    std::vector<float> baryOutData(barycentricSize, 0);
    ret = aclrtMemcpy(baryOutData.data(),
        barycentricSize * sizeof(baryOutData[0]),
        baryDeviceAddr,
        barycentricSize * sizeof(baryOutData[0]),
        ACL_MEMCPY_DEVICE_TO_HOST);
    CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("copy result from device to host failed. ERROR: %d\n", ret); return ret);

    for (int64_t i = 0; i < findicesSize; i++) {
        LOG_PRINT("findices[%ld] is: %d\n", i, findicesOutData[i]);
    }
    for (int64_t i = 0; i < barycentricSize; i++) {
        LOG_PRINT("barycentric[%ld] is: %f\n", i, baryOutData[i]);
    }

    // 6. Release the aclTensor. Modify based on the specific API definition.
    aclDestroyTensor(v);
    aclDestroyTensor(f);
    aclDestroyTensor(d);
    aclDestroyTensor(findices);
    aclDestroyTensor(barycentric);

    // 7. Release the device resources. Modify based on the specific API definition.
    if (workspaceSize > 0) {
        aclrtFree(workspaceAddr);
    }
    aclrtDestroyStream(stream);
    aclrtResetDevice(deviceId);
    aclFinalize();

    return 0;
}