Files
alibaba--mnn/source/backend/cpu/render/CPURasterAndInterpolate.cpp
2026-07-13 13:33:03 +08:00

455 lines
17 KiB
C++

#include "core/TensorUtils.hpp"
#include "core/Macro.h"
#include "../compute/CommonOptFunction.h"
#include "../CPUBackend.hpp"
#include <cmath>
#include <algorithm>
#include <array>
#include "half.hpp"
#define MNN_OPEN_TIME_TRACE
#include <MNN/AutoTime.hpp>
namespace MNN {
#ifdef MNN_SUPPORT_RENDER
struct Point {
float x;
float y;
float z;
float w;
};
class CPURasterAndInterpolate : public Execution {
public:
CPURasterAndInterpolate(Backend* bn, bool hasIndice, int primitiveType) : Execution(bn) {
mIndice = hasIndice;
mType = primitiveType;
}
virtual ~ CPURasterAndInterpolate() {
// Do nothing
}
virtual ErrorCode onResize(const std::vector<Tensor *> &inputs, const std::vector<Tensor *> &outputs) override {
return NO_ERROR;
}
void _rasterPointWithPointsize(const std::vector<Tensor *> &inputs, const std::vector<Tensor *> &outputs) {
auto rasterOutput = outputs[0];
auto width = rasterOutput->length(2);
auto height = rasterOutput->length(1);
auto rasterBufferPtr = rasterOutput->host<float>();
int pixelSize = height * width;
auto pointSize = inputs[1];
auto position = inputs[2];
auto pointSizePtr = pointSize->host<float>();
auto posPtr = position->host<float>();
auto numberPoint = pointSize->length(0);
auto tY = [height](float x) {
return ((x + 1.0f) * 0.5f * height) - 0.5f;
};
auto tX = [width](float x) {
return ((x + 1.0f) * 0.5f * width) - 0.5f;
};
for (int index=0; index<numberPoint; ++index) {
auto x = posPtr[4 * index + 0];
auto y = posPtr[4 * index + 1];
auto z = posPtr[4 * index + 2];
auto w = posPtr[4 * index + 3];
auto PSize = (int)pointSizePtr[index];
x = tX(x / w);
y = tY(y / w);
z = z / w;
int yMin = y - PSize;
yMin = ALIMIN(ALIMAX(yMin, 0), height-1);
int yMax = y + PSize;
yMax = ALIMIN(ALIMAX(yMax, 0), height-1);
int xMin = x - PSize;
xMin = ALIMIN(ALIMAX(xMin, 0), width-1);
int xMax = x + PSize;
xMax = ALIMIN(ALIMAX(xMax, 0), width-1);
for (int yi=yMin; yi<yMax; ++yi) {
for (int xi=xMin; xi<xMax; ++xi) {
auto dstPtr = rasterBufferPtr + 4 * (yi*width + xi);
auto curZ = z;
if (curZ > dstPtr[2]) {
continue;
}
dstPtr[2] = curZ;
dstPtr[3] = index;
dstPtr[0] = 0.0;
dstPtr[1] = 0.0;
for (int i=1; i<outputs.size(); ++i) {
auto unit = outputs[i]->length(3);
auto srcPtr = inputs[i+2]->host<float>();
auto ndstPtr = outputs[i]->host<float>() + (yi*width + xi) * unit;
auto src = srcPtr + index * unit;
for (int j=0; j<unit; ++j) {
ndstPtr[j] = src[j];
}
}
}
}
}
}
void _rasterTriangleWithIndice(const std::vector<Tensor *> &inputs, const std::vector<Tensor *> &outputs) {
auto rasterOutput = outputs[0];
auto width = rasterOutput->length(2);
auto height = rasterOutput->length(1);
auto rasterBufferPtr = rasterOutput->host<float>();
int pixelSize = height * width;
auto indice = inputs[1];
auto position = inputs[2];
auto indicePtr = indice->host<int>();
auto posPtr = position->host<float>();
auto numberTriangle = indice->elementSize() / 3;;
for (int triangleIndex=0; triangleIndex < numberTriangle; ++triangleIndex) {
auto i0 = indicePtr[3 * triangleIndex + 0];
auto i1 = indicePtr[3 * triangleIndex + 1];
auto i2 = indicePtr[3 * triangleIndex + 2];
auto x0 = posPtr[4 * i0 + 0];
auto x1 = posPtr[4 * i1 + 0];
auto x2 = posPtr[4 * i2 + 0];
auto y0 = posPtr[4 * i0 + 1];
auto y1 = posPtr[4 * i1 + 1];
auto y2 = posPtr[4 * i2 + 1];
auto z0 = posPtr[4 * i0 + 2];
auto z1 = posPtr[4 * i1 + 2];
auto z2 = posPtr[4 * i2 + 2];
auto w0 = posPtr[4 * i0 + 3];
auto w1 = posPtr[4 * i1 + 3];
auto w2 = posPtr[4 * i2 + 3];
std::vector<Point> points {
{x0, y0, z0, w0},
{x1, y1, z1, w1},
{x2, y2, z2, w2},
};
// TODO: Clip
auto tY = [height](float x) {
return ((x + 1.0f) * 0.5f * height);
};
auto rY = [height](int y) {
return (float)y / (float)height * 2.0f - 1.0f;
};
auto tX = [width](float x) {
return ((x + 1.0f) * 0.5f * width);
};
auto rX = [width](int y) {
return (float)y / (float)width * 2.0f - 1.0f;
};
for (int i=2; i<points.size(); ++i) {
auto& p0 = points[i-2];
auto& p1 = points[i-1];
auto& p2 = points[i-0];
x0 = p0.x / p0.w;
x1 = p1.x / p1.w;
x2 = p2.x / p2.w;
y0 = p0.y / p0.w;
y1 = p1.y / p1.w;
y2 = p2.y / p2.w;
z0 = p0.z / p0.w;
z1 = p1.z / p1.w;
z2 = p2.z / p2.w;
// MNN_PRINT("[%f,%f,%f], [%f,%f,%f], [%f,%f,%f]\n", x0, y0, z1, x1, y1, z1, x2, y2, z2);
// Reorder triangle, make mid y as y1
if (y1 < y0) {
std::swap(y1, y0);
std::swap(x1, x0);
}
if (y2 < y0) {
std::swap(y2, y0);
std::swap(x2, x0);
}
if (y2 < y1) {
std::swap(y2, y1);
std::swap(x2, x1);
}
// Split triangle by mid y
float c01 = (x1-x0) / (y1-y0);
if (std::fabs(y1-y0) < 0.0001f) {
c01 = 0.0f;
}
float c02 = (x2-x0) / (y2-y0);
if (std::fabs(y2-y0) < 0.0001f) {
c02 = 0.0f;
}
float c12 = (x2-x1) / (y2-y1);
if (std::fabs(y2-y1) < 0.0001f) {
c12 = 0.0f;
}
float my = y1;
// Compute Y Range and iter step
int y0i = floorf(tY(y0));
int y2i = ceilf(tY(y2));
int myil = floorf(tY(my));
int myir = ceilf(tY(my));
auto rasterTriangle = [width, height, tY, tX, rY, rX, rasterBufferPtr, &outputs, &inputs, i0, i1, i2, p0, p1, p2] (int y0i, int myi, float c01, float c02, float x0, float y0, float z0, float z1, float z2, int triangleIndex) {
if (y0i > myi) {
std::swap(y0i, myi);
}
if (y0i < 0) {
y0i = 0;
}
if (myi > height) {
myi = height;
}
for (int yi=y0i; yi<myi; ++yi) {
float yif = rY(yi);
float xli = tX(c01 * (yif - y0) + x0);
float xri = tX(c02 * (yif - y0) + x0);
if (xli > xri) {
std::swap(xli, xri);
}
int xs = floorf(xli);
int xe = ceilf(xri);
if (xs < 0) {
xs = 0;
}
if (xe > width) {
xe = width;
}
for (int xi=xs; xi<xe; ++xi) {
auto dstPtr = rasterBufferPtr + 4 * (yi*width + xi);
// Evaluate edge functions.
float fx = rX(xi);
float fy = yif;
float p0x = p0.x - fx * p0.w;
float p0y = p0.y - fy * p0.w;
float p1x = p1.x - fx * p1.w;
float p1y = p1.y - fy * p1.w;
float p2x = p2.x - fx * p2.w;
float p2y = p2.y - fy * p2.w;
float a0 = p1x*p2y - p1y*p2x;
float a1 = p2x*p0y - p2y*p0x;
float a2 = p0x*p1y - p0y*p1x;
// Perspective correct, normalized barycentrics.
float iw = 1.f / (a0 + a1 + a2);
float b0 = a0 * iw;
float b1 = a1 * iw;
// Compute z/w for depth buffer.
float z = p0.z * a0 + p1.z * a1 + p2.z * a2;
float w = p0.w * a0 + p1.w * a1 + p2.w * a2;
float zw = z / w;
float b2 = 1.0f - b0 - b1;
if (b2 < 0.0f || b2 > 1.0f) {
continue;
}
if (b1 < 0.0f || b1 > 1.0f) {
continue;
}
if (b0 < 0.0f || b0 > 1.0f) {
continue;
}
// Clamps to avoid NaNs.
b0 = fmaxf(fminf(b0, 1.0f), 0.0f); // Clamp to [+0.0, 1.0].
b1 = fmaxf(fminf(b1, 1.0f), 0.0f); // Clamp to [+0.0, 1.0].
zw = fmaxf(fminf(zw, 1.f), -1.f);
float curZ = zw;
if (curZ > dstPtr[2]) {
continue;
}
dstPtr[2] = curZ;
dstPtr[3] = triangleIndex;
dstPtr[0] = b0;
dstPtr[1] = b1;
for (int i=1; i<outputs.size(); ++i) {
auto unit = outputs[i]->length(3);
auto srcPtr = inputs[i+2]->host<float>();
auto ndstPtr = outputs[i]->host<float>() + (yi*width + xi) * unit;
auto src0 = srcPtr + i0 * unit;
auto src1 = srcPtr + i1 * unit;
auto src2 = srcPtr + i2 * unit;
for (int j=0; j<unit; ++j) {
ndstPtr[j] = b0 * src0[j] + b1 * src1[j] + b2 * src2[j];
}
}
}
}
};
// Up triangle
rasterTriangle(y0i, myir, c01, c02, x0, y0, z0, z1, z2, triangleIndex + 1);
// Down triangle
rasterTriangle(myil, y2i, c12, c02, x2, y2, z0, z1, z2, triangleIndex + 1);
}
}
}
virtual ErrorCode onExecute(const std::vector<Tensor *> &inputs, const std::vector<Tensor *> &outputs) override {
auto rasterOutput = outputs[0];
auto width = rasterOutput->length(2);
auto height = rasterOutput->length(1);
// Init Output, index + 1, w0, w1, z
int pixelSize = height * width;
auto rasterBufferPtr = rasterOutput->host<float>();
for (int i=0; i<pixelSize; ++i) {
rasterBufferPtr[4 * i + 0] = 0.0f;
rasterBufferPtr[4 * i + 1] = 0.0f;
rasterBufferPtr[4 * i + 2] = 1.0f;
rasterBufferPtr[4 * i + 3] = 0.0f;
}
for (int i=1; i<outputs.size(); ++i) {
auto ptr = outputs[i]->host<float>();
::memset(ptr, 0, pixelSize * outputs[i]->length(3) * sizeof(float));
}
if (mIndice && mType == 4) {
_rasterTriangleWithIndice(inputs, outputs);
} else if ((!mIndice) && (mType == 0)) {
_rasterPointWithPointsize(inputs, outputs);
}
return NO_ERROR;
}
private:
bool mIndice;
int mType;
};
class CPURasterSort : public Execution {
private:
std::vector<std::pair<uint32_t, uint32_t>> mKV;
std::array<uint32_t, 65536> mHistorm;
std::array<uint32_t, 65536> mHistormOffset;
public:
CPURasterSort(Backend* bn) : Execution(bn) {
}
virtual ~ CPURasterSort() {
// Do nothing
}
virtual ErrorCode onResize(const std::vector<Tensor *> &inputs, const std::vector<Tensor *> &outputs) override {
auto number = inputs[0]->length(0);
mKV.resize(number);
return NO_ERROR;
}
virtual ErrorCode onExecute(const std::vector<Tensor *> &inputs, const std::vector<Tensor *> &outputs) override {
auto number = inputs[0]->length(0);
auto attr = inputs[0];
auto viewProj = inputs[1]->host<float>();
auto numAttr = attr->length(1);
auto outputPtr = outputs[1]->host<float>();
for (int i=0; i<mHistorm.size(); ++i) {
mHistorm[i] = 0;
}
// for (int i=0; i<16; ++i) {
// printf("%f, ", viewProj[i]);
// }
// printf("\n");
// Make KV
int validNumber = 0;
float maxDepth = 1.0f;
const float minDepth = 0.0f;
float scale = 1.0f / (maxDepth - minDepth);
if (numAttr == 4) {
// Float
for (int i=0; i<number; ++i) {
auto srcPtr = (inputs[0]->host<float>() + numAttr * i);
auto x = srcPtr[0];
auto y = srcPtr[1];
auto z = srcPtr[2];
auto depth =
viewProj[2] * x +
viewProj[6] * y +
viewProj[10] * z + viewProj[14];
auto dw =
viewProj[3] * x +
viewProj[7] * y +
viewProj[11] * z + viewProj[15];
depth = depth / dw;
if (depth < minDepth || depth > maxDepth) {
continue;
}
uint32_t key = scale * (depth - minDepth) * 65535.0f;
mKV[validNumber].second = i;
mKV[validNumber].first = key;
validNumber++;
mHistorm[key]++;
}
} else {
for (int i=0; i<number; ++i) {
auto srcPtr = (half_float::half*)(inputs[0]->host<float>() + numAttr * i);
auto x = srcPtr[0];
auto y = srcPtr[1];
auto z = srcPtr[2];
auto depth =
viewProj[2] * x +
viewProj[6] * y +
viewProj[10] * z + viewProj[14];
auto dw =
viewProj[3] * x +
viewProj[7] * y +
viewProj[11] * z + viewProj[15];
depth = depth / dw;
if (depth < minDepth || depth > maxDepth) {
continue;
}
uint32_t key = scale * (depth - minDepth) * 65535.0f;
mKV[validNumber].second = i;
mKV[validNumber].first = key;
validNumber++;
mHistorm[key]++;
}
}
mHistormOffset[0] = 0;
for (int i=1; i<mHistormOffset.size(); ++i) {
mHistormOffset[i] = mHistormOffset[i-1] + mHistorm[i-1];
}
auto mKVMid = (std::pair<uint32_t, uint32_t>*)(outputs[1]->host<void>());
for (int i=0; i<validNumber; ++i) {
auto key = mKV[i].first;
auto offset = mHistormOffset[key];
mKVMid[offset] = mKV[i];
offset++;
mHistormOffset[key]=offset;
}
outputs[0]->host<int>()[0] = validNumber;
return NO_ERROR;
}
};
class CPURasterAndInterpolateCreator : public CPUBackend::Creator {
virtual Execution* onCreate(const std::vector<Tensor *> &inputs, const std::vector<Tensor *> &outputs,
const MNN::Op *op, Backend *backend) const override {
bool hasIndice = true;
int type = 4;
if (op->main_type() == OpParameter_Extra) {
auto extra = op->main_as_Extra();
if (nullptr != extra->attr()) {
for (int i=0; i<extra->attr()->size(); ++i) {
auto attr = extra->attr()->GetAs<Attribute>(i);
if (attr->key()->str() == "index") {
hasIndice = attr->b();
continue;
}
if (attr->key()->str() == "primitiveType") {
type = attr->i();
continue;
}
}
}
}
if (6 == type) {
return new CPURasterSort(backend);
}
return new CPURasterAndInterpolate(backend, hasIndice, type);
}
};
#endif
REGISTER_CPU_OP_CREATOR_RENDER(CPURasterAndInterpolateCreator, OpType_RasterAndInterpolate);
} // namespace MNN