mirror of
https://github.com/opencv/opencv.git
synced 2026-07-29 07:13:02 +04:00
Merge pull request #29304 from velonica0:dnn-rvv-block
dnn: fix new-engine block layout for RVV when VLEN > 128 #29304 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [x] The feature is well documented and sample code can be built with the project CMake <!-- Note!!! If you are an automated agent, we have a special process for you: add 🤖🤖🤖 to the end of the PR title. --> #### Problem Fixes #28852. The new DNN engine packs activations in a blocked NCHWc layout with a fixed channel-block size `C0 = 8`. The blocked-layout kernels (MaxPool, AveragePool, BatchNorm, depthwise Conv, ConvTranspose, generic Conv) load one block into a SIMD vector and assert: `C0 == nlanes || C0 == nlanes*2 || C0 % (nlanes*4) == 0` On RISC-V the universal intrinsics are **scalable with LMUL=2**, so `nlanes = (VLEN/32)*2` — **16 at VLEN=256, 64 at VLEN=1024** — which exceeds the fixed `C0=8`. The assertion fails with `-215` and the ONNX pooling/conv conformance tests abort. #29180 worked around it by disabling the RVV SIMD path in those kernels. #### Fix Make the block size track the hardware vector width on RVV instead of assuming 8: - `net_impl.cpp`: `defaultC0 = max(8, VTraits<v_float32>::vlanes())` (16/64 on RVV). - The fp32 blocked kernels read `C0` at runtime and size scratch buffers by `max_nlanes` (bounded by the compile-time `CV_RVV_MAX_VLEN`, default 1024). - All changes are guarded by `CV_SIMD_SCALABLE`, so **x86/NEON are unchanged**. - The int8 quantized kernels are hardwired to `C0=8` (VNNI/NEON packing + per-channel quant) and are scalar on RVV, so quantized graphs are pinned to `C0=8` in `prepareForInference()` (detected via a `CV_8S` arg); only fp32 graphs use the wider block. #### Validation (native RISC-V) I have already conducted tests on the Spacemit K3, covering both the X100 (VLEN=256) and the A100 (VLEN=1024). `opencv_test_dnn`, ENGINE_AUTO (new engine), no assertions: | VLEN | Test_ONNX_conformance | Test_ONNX_layers (conv/pool/depthwise/quantized/MobileNet_v4) | |------|-----------------------|--------------------------------------------------------------| | 256 | 1641 / 1641 | all pass (incl. `Quantized_Convolution`) | | 1024 | 1641 / 1641 | all pass (incl. `Quantized_Convolution`) |
This commit is contained in:
@@ -52,7 +52,7 @@ static void avgPool32f(const void* inp_, void* out_,
|
||||
float* out = (float*)out_ + nc0*planesize;
|
||||
float iksize = 1.f/ksize;
|
||||
|
||||
#if CV_SIMD
|
||||
#if CV_SIMD || CV_SIMD_SCALABLE
|
||||
int nlanes = VTraits<v_float32>::vlanes();
|
||||
CV_Assert(C0 == nlanes || C0 == nlanes*2 || C0 % (nlanes*4) == 0);
|
||||
v_float32 z = vx_setzero_f32();
|
||||
@@ -68,12 +68,12 @@ static void avgPool32f(const void* inp_, void* out_,
|
||||
y0 >= inner_y0 && y0 < inner_y1 ? inner_x0 : W;
|
||||
int yi_ = y0*SY - padY0;
|
||||
|
||||
#if !(CV_SIMD)
|
||||
#if !(CV_SIMD || CV_SIMD_SCALABLE)
|
||||
memset(out, 0, W*C0*sizeof(out[0]));
|
||||
#endif
|
||||
|
||||
for(;;) {
|
||||
#if CV_SIMD
|
||||
#if CV_SIMD || CV_SIMD_SCALABLE
|
||||
if (nlanes == C0) {
|
||||
for (; x0 < x1; x0++) {
|
||||
int xi_ = x0*SX - padX0;
|
||||
@@ -152,7 +152,7 @@ static void avgPool32f(const void* inp_, void* out_,
|
||||
break;
|
||||
x1 = inner_x1;
|
||||
|
||||
#if CV_SIMD
|
||||
#if CV_SIMD || CV_SIMD_SCALABLE
|
||||
if (nlanes == C0) {
|
||||
for (; x0 < x1; x0++) {
|
||||
int xi_ = x0*SX - padX0;
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace dnn {
|
||||
*/
|
||||
|
||||
#undef CV_SIMD_ONLY
|
||||
#if CV_SIMD
|
||||
#if CV_SIMD || CV_SIMD_SCALABLE
|
||||
#define CV_SIMD_ONLY(expr) expr
|
||||
#else
|
||||
#define CV_SIMD_ONLY(expr)
|
||||
@@ -135,7 +135,7 @@ static void batchnorm(const Mat& inp, Mat& out, const Mat& scale,
|
||||
}
|
||||
}
|
||||
}
|
||||
#if CV_SIMD
|
||||
#if CV_SIMD || CV_SIMD_SCALABLE
|
||||
/*
|
||||
[TODO] support C0 == vlanes/2, maybe C0 == vlanes/4.
|
||||
in this case, load everything into vsc0 and vb0, process
|
||||
@@ -153,7 +153,7 @@ static void batchnorm(const Mat& inp, Mat& out, const Mat& scale,
|
||||
scalebuf[c] = biasbuf[c] = 0.f;
|
||||
}
|
||||
v_float32 vsc0, vsc1, vsc2, vsc3;
|
||||
v_float32 vb0, vb1, vb2, vb3, vb4;
|
||||
v_float32 vb0, vb1, vb2, vb3;
|
||||
vsc0 = vx_load(scalebuf);
|
||||
vsc1 = vx_load(scalebuf + vlanes);
|
||||
vsc2 = vx_load(scalebuf + vlanes*2);
|
||||
|
||||
@@ -63,7 +63,7 @@ static void deconvBlock32f(const void* inp__, const void* /*residual*/,
|
||||
const float* wdata = (const float*)weights__;
|
||||
const float* bias = bias__;
|
||||
|
||||
#if (CV_SIMD)
|
||||
#if (CV_SIMD || CV_SIMD_SCALABLE)
|
||||
// SIMD path is safe when ngroups==1 or Kg%C0==0; repackDeconvWeights()
|
||||
// zero-fills padded lanes.
|
||||
const bool simd_ok =
|
||||
@@ -83,7 +83,7 @@ static void deconvBlock32f(const void* inp__, const void* /*residual*/,
|
||||
float* out_k1 = (float*)out__ + ((int64_t)n * K1 + k1) * ospatial * C0;
|
||||
const float* inp_n = (const float*)inp__ + (int64_t)n * C1 * ispatial * C0;
|
||||
|
||||
#if (CV_SIMD)
|
||||
#if (CV_SIMD || CV_SIMD_SCALABLE)
|
||||
// Precompute the shared (g, kblk, c1_abs_base) for the SIMD path.
|
||||
int simd_g = 0, simd_kblk = 0, simd_c1_abs_base = 0;
|
||||
if (simd_ok) {
|
||||
@@ -115,7 +115,7 @@ static void deconvBlock32f(const void* inp__, const void* /*residual*/,
|
||||
}
|
||||
}
|
||||
|
||||
#if (CV_SIMD)
|
||||
#if (CV_SIMD || CV_SIMD_SCALABLE)
|
||||
if (simd_ok) {
|
||||
const int VLANES = VTraits<v_float32>::vlanes();
|
||||
const int v_per_block = C0 / VLANES;
|
||||
|
||||
@@ -38,7 +38,14 @@ static void depthwiseConv32f(const void* inp__, const void* residual__,
|
||||
parallel_for_(Range(0, NC1), [&](const Range& range)
|
||||
{
|
||||
constexpr int MAX_CONV_DIMS = ConvState::MAX_CONV_DIMS;
|
||||
#if CV_SIMD_SCALABLE
|
||||
// RVV: universal intrinsics use LMUL=2, so the float vector width tracks
|
||||
// the hardware VLEN at runtime. The block size C0 == defaultC0 == vlanes()
|
||||
// (e.g. 16 at VLEN=256), so read it at runtime instead of fixing it to 8 (#28852).
|
||||
const int C0 = (int)cs.inpshape.back();
|
||||
#else
|
||||
constexpr int C0 = 8;
|
||||
#endif
|
||||
|
||||
CV_Assert(cs.nspatialdims <= MAX_CONV_DIMS && MAX_CONV_DIMS == 3);
|
||||
CV_Assert(C0 == cs.inpshape.back());
|
||||
@@ -73,7 +80,14 @@ static void depthwiseConv32f(const void* inp__, const void* residual__,
|
||||
const float* activParams = cs.activParams.data();
|
||||
ActivationFunc activation = cs.activation;
|
||||
float maxval = FLT_MAX, defaultAlpha = 0.f;
|
||||
#if CV_SIMD_SCALABLE
|
||||
// C0 is runtime on RVV; size scratch by the compile-time upper bound.
|
||||
float scalebuf[VTraits<v_float32>::max_nlanes];
|
||||
float biasbuf[VTraits<v_float32>::max_nlanes];
|
||||
float alphabuf[VTraits<v_float32>::max_nlanes];
|
||||
#else
|
||||
float scalebuf[C0], biasbuf[C0], alphabuf[C0];
|
||||
#endif
|
||||
if (fastActivation == FAST_ACTIV_CLIP) {
|
||||
CV_Assert(cs.activParams.size() == 2u);
|
||||
maxval = activParams[1];
|
||||
@@ -89,7 +103,7 @@ static void depthwiseConv32f(const void* inp__, const void* residual__,
|
||||
defaultAlpha = 1.f;
|
||||
}
|
||||
|
||||
#if CV_SIMD
|
||||
#if CV_SIMD || CV_SIMD_SCALABLE
|
||||
v_float32 v_maxval = vx_setall_f32(maxval);
|
||||
v_float32 z = vx_setzero_f32();
|
||||
const int nlanes = VTraits<v_float32>::vlanes();
|
||||
@@ -126,12 +140,12 @@ static void depthwiseConv32f(const void* inp__, const void* residual__,
|
||||
y0 >= inner_y0 && y0 < inner_y1 ? inner_x0 : W;
|
||||
int yi_ = y0*SY - padY0;
|
||||
|
||||
#if !(CV_SIMD)
|
||||
#if !(CV_SIMD || CV_SIMD_SCALABLE)
|
||||
memset(out, 0, W*C0*sizeof(out[0]));
|
||||
#endif
|
||||
|
||||
for(;;) {
|
||||
#if CV_SIMD
|
||||
#if CV_SIMD || CV_SIMD_SCALABLE
|
||||
if (nlanes == C0) {
|
||||
v_float32 sc0 = vx_load(scalebuf), b0 = vx_load(biasbuf);
|
||||
v_float32 alpha0 = vx_load(alphabuf);
|
||||
@@ -218,7 +232,7 @@ static void depthwiseConv32f(const void* inp__, const void* residual__,
|
||||
break;
|
||||
x1 = inner_x1;
|
||||
|
||||
#if CV_SIMD
|
||||
#if CV_SIMD || CV_SIMD_SCALABLE
|
||||
if (nlanes == C0) {
|
||||
v_float32 sc0 = vx_load(scalebuf), b0 = vx_load(biasbuf), alpha0 = vx_load(alphabuf);
|
||||
for (; x0 < x1; x0++) {
|
||||
@@ -335,7 +349,7 @@ static void depthwiseConv32f(const void* inp__, const void* residual__,
|
||||
x1 = W;
|
||||
}
|
||||
|
||||
#if !(CV_SIMD)
|
||||
#if !(CV_SIMD || CV_SIMD_SCALABLE)
|
||||
if (residual) {
|
||||
for (int x = 0; x < W*C0; x += C0) {
|
||||
for (int c = 0; c < C0; c++) {
|
||||
|
||||
@@ -432,14 +432,14 @@ static void setupActivation(const ConvState& cs, int K,
|
||||
static void fillCoeffBufs(FastActivation fastActivation, const float* activParams, float defaultAlpha,
|
||||
int k_count, int k_base,
|
||||
const float* scaleptr, const float* biasptr,
|
||||
float* scalebuf, float* biasbuf, float* alphabuf) {
|
||||
float* scalebuf, float* biasbuf, float* alphabuf, int K0) {
|
||||
int kk = 0;
|
||||
for (; kk < k_count; kk++) {
|
||||
scalebuf[kk] = scaleptr ? scaleptr[k_base + kk] : 1.f;
|
||||
biasbuf[kk] = biasptr ? biasptr[k_base + kk] : 0.f;
|
||||
alphabuf[kk] = fastActivation == FAST_ACTIV_PRELU ? activParams[k_base + kk] : defaultAlpha;
|
||||
}
|
||||
for (; kk < 8; kk++) {
|
||||
for (; kk < K0; kk++) {
|
||||
scalebuf[kk] = 0.f;
|
||||
biasbuf[kk] = 0.f;
|
||||
alphabuf[kk] = 0.f;
|
||||
@@ -570,6 +570,10 @@ static void scatterScalarOut(bool aligned_k, int k_base, int k_count, int K0shif
|
||||
}
|
||||
#endif
|
||||
|
||||
// RVV uses the generic runtime-C0 path below; the C0=8-specialized kernels are
|
||||
// unused on scalable backends, so do not compile them there (avoids -Wunused-function).
|
||||
#if !CV_SIMD_SCALABLE
|
||||
|
||||
// Specialized 1x1 convolution kernel with stride=1.
|
||||
static void conv32fC8_1x1(const void* inp__, const void* residual__, void* out__,
|
||||
const ConvState& cs, const void* weights__,
|
||||
@@ -655,7 +659,7 @@ static void conv32fC8_1x1(const void* inp__, const void* residual__, void* out__
|
||||
const float* inpbaseptr = (float*)inp__ + (n * C1 + c1_start) * iplanesize;
|
||||
const float* wbaseptr = (float*)weights__ + (g*Kblk + kblk)*(1*C1Max*C0*K0);
|
||||
|
||||
fillCoeffBufs(fastActivation, activParams, defaultAlpha, k_count, k_base, scaleptr, biasptr, scalebuf, biasbuf, alphabuf);
|
||||
fillCoeffBufs(fastActivation, activParams, defaultAlpha, k_count, k_base, scaleptr, biasptr, scalebuf, biasbuf, alphabuf, K0);
|
||||
|
||||
float* outptr = (float*)out__ + n*(K1*planesize) + p0*K0;
|
||||
const float* resptr = residual__ ? (float*)residual__ + n*(K1*planesize) + p0*K0 : nullptr;
|
||||
@@ -824,8 +828,8 @@ static void conv32fC8_1x1_kpair(const void* inp__, const void* residual__, void*
|
||||
const float* wbaseptrA = (float*)weights__ + (g*Kblk + kblkA)*(1*C1Max*C0*K0);
|
||||
const float* wbaseptrB = (float*)weights__ + (g*Kblk + kblkB)*(1*C1Max*C0*K0);
|
||||
|
||||
fillCoeffBufs(fastActivation, activParams, defaultAlpha, K0, k_baseA, scaleptr, biasptr, scalebufA, biasbufA, alphabufA);
|
||||
fillCoeffBufs(fastActivation, activParams, defaultAlpha, K0, k_baseB, scaleptr, biasptr, scalebufB, biasbufB, alphabufB);
|
||||
fillCoeffBufs(fastActivation, activParams, defaultAlpha, K0, k_baseA, scaleptr, biasptr, scalebufA, biasbufA, alphabufA, K0);
|
||||
fillCoeffBufs(fastActivation, activParams, defaultAlpha, K0, k_baseB, scaleptr, biasptr, scalebufB, biasbufB, alphabufB, K0);
|
||||
|
||||
float* outbaseA = (float*)out__ + n*(K1*planesize) + k_baseA*planeblocks;
|
||||
float* outbaseB = (float*)out__ + n*(K1*planesize) + k_baseB*planeblocks;
|
||||
@@ -1050,7 +1054,7 @@ static void conv32fC8_3x3s1(const void* inp__, const void* residual__, void* out
|
||||
const float* inpbaseptr = (float*)inp__ + (n * C1 + c1_start) * iplanesize;
|
||||
const float* wbaseptr = (float*)weights__ + (g*Kblk + kblk)*(9*C1Max*C0*K0);
|
||||
|
||||
fillCoeffBufs(fastActivation, activParams, defaultAlpha, k_count, k_base, scaleptr, biasptr, scalebuf, biasbuf, alphabuf);
|
||||
fillCoeffBufs(fastActivation, activParams, defaultAlpha, k_count, k_base, scaleptr, biasptr, scalebuf, biasbuf, alphabuf, K0);
|
||||
|
||||
float* outptr = (float*)out__ + n*(K1*planesize) + p0*K0;
|
||||
const float* resptr = residual__ ? (float*)residual__ + n*(K1*planesize) + p0*K0 : nullptr;
|
||||
@@ -1329,8 +1333,8 @@ static void conv32fC8_3x3s1_kpair(const void* inp__, const void* residual__, voi
|
||||
const float* wbaseptrA = (float*)weights__ + (g*Kblk + kblkA)*(9*C1Max*C0*K0);
|
||||
const float* wbaseptrB = (float*)weights__ + (g*Kblk + kblkB)*(9*C1Max*C0*K0);
|
||||
|
||||
fillCoeffBufs(fastActivation, activParams, defaultAlpha, K0, k_baseA, scaleptr, biasptr, scalebufA, biasbufA, alphabufA);
|
||||
fillCoeffBufs(fastActivation, activParams, defaultAlpha, K0, k_baseB, scaleptr, biasptr, scalebufB, biasbufB, alphabufB);
|
||||
fillCoeffBufs(fastActivation, activParams, defaultAlpha, K0, k_baseA, scaleptr, biasptr, scalebufA, biasbufA, alphabufA, K0);
|
||||
fillCoeffBufs(fastActivation, activParams, defaultAlpha, K0, k_baseB, scaleptr, biasptr, scalebufB, biasbufB, alphabufB, K0);
|
||||
|
||||
float* outbaseA = (float*)out__ + n*(K1*planesize) + k_baseA*planeblocks;
|
||||
float* outbaseB = (float*)out__ + n*(K1*planesize) + k_baseB*planeblocks;
|
||||
@@ -1635,7 +1639,7 @@ static void conv32fC8_1x1_strided(const void* inp__, const void* residual__, voi
|
||||
const float* inpbaseptr = (float*)inp__ + (n * C1 + c1_start) * iplanesize;
|
||||
const float* wbaseptr = (float*)weights__ + (g*Kblk + kblk)*(1*C1Max*C0*K0);
|
||||
|
||||
fillCoeffBufs(fastActivation, activParams, defaultAlpha, k_count, k_base, scaleptr, biasptr, scalebuf, biasbuf, alphabuf);
|
||||
fillCoeffBufs(fastActivation, activParams, defaultAlpha, k_count, k_base, scaleptr, biasptr, scalebuf, biasbuf, alphabuf, K0);
|
||||
|
||||
float* outptr = (float*)out__ + n*(K1*planesize) + p0*K0;
|
||||
const float* resptr = residual__ ? (float*)residual__ + n*(K1*planesize) + p0*K0 : nullptr;
|
||||
@@ -1831,7 +1835,7 @@ static void conv32fC8_3x3_strided(const void* inp__, const void* residual__, voi
|
||||
const float* inpbaseptr = (float*)inp__ + (n * C1 + c1_start) * iplanesize;
|
||||
const float* wbaseptr = (float*)weights__ + (g*Kblk + kblk)*(9*C1Max*C0*K0);
|
||||
|
||||
fillCoeffBufs(fastActivation, activParams, defaultAlpha, k_count, k_base, scaleptr, biasptr, scalebuf, biasbuf, alphabuf);
|
||||
fillCoeffBufs(fastActivation, activParams, defaultAlpha, k_count, k_base, scaleptr, biasptr, scalebuf, biasbuf, alphabuf, K0);
|
||||
|
||||
float* outptr = (float*)out__ + n*(K1*planesize) + p0*K0;
|
||||
const float* resptr = residual__ ? (float*)residual__ + n*(K1*planesize) + p0*K0 : nullptr;
|
||||
@@ -2019,10 +2023,13 @@ static void conv32fC8_3x3_strided(const void* inp__, const void* residual__, voi
|
||||
});
|
||||
}
|
||||
|
||||
#endif // !CV_SIMD_SCALABLE
|
||||
|
||||
static void conv32fC8(const void* inp__, const void* residual__, void* out__,
|
||||
const ConvState& cs, const void* weights__,
|
||||
const float* scale__, const float* bias__)
|
||||
{
|
||||
#if !CV_SIMD_SCALABLE // RVV: skip the C0=8-specialized kernels; use the generic runtime-C0 path
|
||||
int ksize = cs.wshape[2];
|
||||
if (ksize == 1 && cs.strides[0]*cs.strides[1]*cs.strides[2] == 1) {
|
||||
#if CV_SIMD256 && defined(__AVX2__)
|
||||
@@ -2063,6 +2070,7 @@ static void conv32fC8(const void* inp__, const void* residual__, void* out__,
|
||||
cs.outshape.dims <= 5) {
|
||||
return conv32fC8_3x3_strided(inp__, residual__, out__, cs, weights__, scale__, bias__);
|
||||
}
|
||||
#endif // !CV_SIMD_SCALABLE
|
||||
|
||||
const MatShape& inpshape = cs.inpshape;
|
||||
const MatShape& outshape = cs.outshape;
|
||||
@@ -2091,8 +2099,17 @@ static void conv32fC8(const void* inp__, const void* residual__, void* out__,
|
||||
|
||||
parallel_for_(Range(0, total_tasks_gen), [&](const Range& range) {
|
||||
constexpr int SPAT_BLOCK_SIZE = 10;
|
||||
#if CV_SIMD_SCALABLE
|
||||
// RVV: block size follows the runtime vector width (defaultC0 = vlanes(), LMUL=2).
|
||||
const int C0 = (int)inpshape.back();
|
||||
int C0shift = 0; while ((1 << C0shift) < C0) C0shift++;
|
||||
const int K0 = C0, K0shift = C0shift;
|
||||
constexpr int C0BUF = VTraits<v_float32>::max_nlanes; // compile-time scratch bound
|
||||
#else
|
||||
constexpr int C0shift = 3, K0shift = C0shift;
|
||||
constexpr int C0 = 1 << C0shift, K0 = C0;
|
||||
constexpr int C0BUF = K0;
|
||||
#endif
|
||||
|
||||
CV_Assert_N(inpshape.back() == C0, outshape.back() == K0);
|
||||
|
||||
@@ -2122,14 +2139,14 @@ static void conv32fC8(const void* inp__, const void* residual__, void* out__,
|
||||
int innerZ0 = cs.inner[0], innerZ1 = cs.inner[MAX_CONV_DIMS];
|
||||
int innerY0 = cs.inner[1], innerY1 = cs.inner[MAX_CONV_DIMS+1];
|
||||
int innerX0 = cs.inner[2], innerX1 = cs.inner[MAX_CONV_DIMS+2];
|
||||
float zbuf[C0] = {};
|
||||
float zbuf[C0BUF] = {};
|
||||
#endif
|
||||
|
||||
FastActivation fastActivation;
|
||||
const float* activParams;
|
||||
ActivationFunc activation;
|
||||
float maxval, defaultAlpha;
|
||||
float scalebuf[K0], biasbuf[K0], alphabuf[K0];
|
||||
float scalebuf[C0BUF], biasbuf[C0BUF], alphabuf[C0BUF];
|
||||
setupActivation(cs, K, fastActivation, activParams, activation, maxval, defaultAlpha);
|
||||
|
||||
// 1x1x1 convolution with (1,1,1) strides:
|
||||
@@ -2167,11 +2184,11 @@ static void conv32fC8(const void* inp__, const void* residual__, void* out__,
|
||||
const float* inpbaseptr = (float*)inp__ + (n * C1 + c1_start) * iplanesize;
|
||||
const float* wbaseptr = (float*)weights__ + (g*Kblk + kblk)*(ksize*C1Max*C0*K0);
|
||||
|
||||
fillCoeffBufs(fastActivation, activParams, defaultAlpha, k_count, k_base, scaleptr, biasptr, scalebuf, biasbuf, alphabuf);
|
||||
fillCoeffBufs(fastActivation, activParams, defaultAlpha, k_count, k_base, scaleptr, biasptr, scalebuf, biasbuf, alphabuf, K0);
|
||||
|
||||
float* outptr = (float*)out__ + n*(K1*planesize) + p0*K0;
|
||||
const float* resptr = residual__ ? (float*)residual__ + n*(K1*planesize) + p0*K0 : nullptr;
|
||||
float tmpbuf[SPAT_BLOCK_SIZE*K0] = {};
|
||||
float tmpbuf[SPAT_BLOCK_SIZE*C0BUF] = {};
|
||||
int p = p0;
|
||||
|
||||
#ifdef CONV_ENABLE_SIMD
|
||||
@@ -2343,7 +2360,7 @@ static void conv32fC8(const void* inp__, const void* residual__, void* out__,
|
||||
}
|
||||
#endif
|
||||
|
||||
float resbuf[K0] = {};
|
||||
float resbuf[C0BUF] = {};
|
||||
|
||||
for (; p < p1; p++, outptr += K0, resptr += (resptr ? K0 : 0))
|
||||
{
|
||||
@@ -2425,9 +2442,16 @@ static void conv32fC8(const void* inp__, const void* residual__, void* out__,
|
||||
cv::dnn::ConvFunc getConvFunc_(int depth, int C0)
|
||||
{
|
||||
ConvFunc func = nullptr;
|
||||
#if CV_SIMD_SCALABLE
|
||||
// RVV: block size follows the runtime vector width; accept the supported pow2 widths.
|
||||
if (depth == CV_32F && (C0 == 8 || C0 == 16 || C0 == 32 || C0 == 64)) {
|
||||
func = conv32fC8;
|
||||
}
|
||||
#else
|
||||
if (depth == CV_32F && C0 == 8) {
|
||||
func = conv32fC8;
|
||||
}
|
||||
#endif
|
||||
return func;
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ static void maxPool32f(const void* inp_, void* out_, const ConvState& cs)
|
||||
float* out = (float*)out_ + nc0*planesize;
|
||||
const float INITVAL = -FLT_MAX;
|
||||
|
||||
#if CV_SIMD
|
||||
#if CV_SIMD || CV_SIMD_SCALABLE
|
||||
int nlanes = VTraits<v_float32>::vlanes();
|
||||
v_float32 s_min = vx_setall_f32(INITVAL);
|
||||
CV_Assert(C0 == nlanes || C0 == nlanes*2 || C0 % (nlanes*4) == 0);
|
||||
@@ -65,13 +65,13 @@ static void maxPool32f(const void* inp_, void* out_, const ConvState& cs)
|
||||
y0 >= inner_y0 && y0 < inner_y1 ? inner_x0 : W;
|
||||
int yi_ = y0*SY - padY0;
|
||||
|
||||
#if !(CV_SIMD)
|
||||
#if !(CV_SIMD || CV_SIMD_SCALABLE)
|
||||
for (int c = 0; c < C0*W; c++)
|
||||
out[c] = INITVAL;
|
||||
#endif
|
||||
|
||||
for(;;) {
|
||||
#if CV_SIMD
|
||||
#if CV_SIMD || CV_SIMD_SCALABLE
|
||||
if (nlanes == C0) {
|
||||
for (; x0 < x1; x0++) {
|
||||
int xi_ = x0*SX - padX0;
|
||||
@@ -136,7 +136,7 @@ static void maxPool32f(const void* inp_, void* out_, const ConvState& cs)
|
||||
break;
|
||||
x1 = inner_x1;
|
||||
|
||||
#if CV_SIMD
|
||||
#if CV_SIMD || CV_SIMD_SCALABLE
|
||||
if (nlanes == C0) {
|
||||
for (; x0 < x1; x0++) {
|
||||
int xi_ = x0*SX - padX0;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "precomp.hpp"
|
||||
|
||||
#include "net_impl.hpp"
|
||||
#include "opencv2/core/hal/intrin.hpp"
|
||||
|
||||
#ifdef HAVE_ONNXRUNTIME
|
||||
#include <onnxruntime_cxx_api.h>
|
||||
@@ -74,6 +75,13 @@ Net::Impl::Impl()
|
||||
|
||||
accuracy = CV_32F;
|
||||
defaultC0 = DEFAULT_C0;
|
||||
#if CV_SIMD_SCALABLE
|
||||
// RVV: the universal intrinsics use LMUL=2, so the float vector width is
|
||||
// (VLEN/32)*2 (16 at VLEN=256). The blocked-layout kernels fill a full
|
||||
// vector per channel block, so the net-wide block size must match that
|
||||
// width rather than the fixed default of 8 (#28852).
|
||||
defaultC0 = std::max((int)DEFAULT_C0, VTraits<v_float32>::vlanes());
|
||||
#endif
|
||||
enableFP16 = haveFP16 = false;
|
||||
// FP16 is not ready yet in the new DNN engine
|
||||
// Ticket: https://github.com/opencv/opencv/issues/26196
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "precomp.hpp"
|
||||
|
||||
#include "net_impl.hpp"
|
||||
#include "opencv2/core/hal/intrin.hpp"
|
||||
|
||||
#include <limits>
|
||||
|
||||
@@ -545,6 +546,16 @@ void Net::Impl::prepareForInference()
|
||||
#endif
|
||||
|
||||
if (!prepared) {
|
||||
#if CV_SIMD_SCALABLE
|
||||
// RVV (#28852): keep quantized graphs at C0=8. The int8 kernels are hardwired to
|
||||
// C0=8 (VNNI/NEON weight packing + per-channel quantization) and run scalar on RVV,
|
||||
// so a wider block gives no benefit and breaks them. Only fp32 graphs use the wider
|
||||
// vlanes()-based defaultC0. Signed-int8 (CV_8S) args are the quantization signature
|
||||
// (uint8 image inputs are CV_8U, so they don't trigger this).
|
||||
for (const ArgData& a : args) {
|
||||
if (a.type == CV_8S) { defaultC0 = 8; break; }
|
||||
}
|
||||
#endif
|
||||
fuseQDQ();
|
||||
constFold();
|
||||
fuseBN();
|
||||
|
||||
Reference in New Issue
Block a user