Fused vertical separable filter over N planes.
Same contract as filter_horizontal_planes but operates vertically.
348{
349 constexpr size_t Unroll = 4;
350
351 const auto half = static_cast<int32_t>(kernel.size() / 2);
352 const auto ih =
static_cast<int32_t
>(
h);
353 const auto iw = static_cast<int32_t>(w);
354 const float* kdata = kernel.data();
355 const size_t n =
src.size();
356
357 std::for_each(std::execution::par_unseq,
358 std::views::iota(uint32_t { 0 },
h).begin(),
359 std::views::iota(uint32_t { 0 },
h).end(),
360 [&](uint32_t row) {
361 const auto py =
static_cast<int32_t
>(row);
362 const bool border = (py < half || py >= ih - half);
363 const size_t row_off = static_cast<size_t>(row) * w;
364
365 const float* s_ptr[32];
366 float* d_ptr[32];
367 for (size_t i = 0; i < n; ++i) {
369 d_ptr[i] =
dst[i] + row_off;
370 }
371
372#ifdef MAYAFLUX_ARCH_X64
374 if (!border) {
375 for (;
px + 8 <= iw;
px += 8) {
376 size_t i = 0;
377 for (; i + Unroll - 1 < n; i += Unroll) {
378 __m256 acc0 = _mm256_setzero_ps();
379 __m256 acc1 = _mm256_setzero_ps();
380 __m256 acc2 = _mm256_setzero_ps();
381 __m256 acc3 = _mm256_setzero_ps();
382 for (int32_t k = -half; k <= half; ++k) {
383 const size_t off =
static_cast<size_t>(
py + k) * w
384 +
static_cast<size_t>(
px);
385 const __m256 kv = _mm256_set1_ps(kdata[static_cast<size_t>(k + half)]);
386 acc0 = _mm256_fmadd_ps(_mm256_loadu_ps(s_ptr[i + 0] + off), kv, acc0);
387 acc1 = _mm256_fmadd_ps(_mm256_loadu_ps(s_ptr[i + 1] + off), kv, acc1);
388 acc2 = _mm256_fmadd_ps(_mm256_loadu_ps(s_ptr[i + 2] + off), kv, acc2);
389 acc3 = _mm256_fmadd_ps(_mm256_loadu_ps(s_ptr[i + 3] + off), kv, acc3);
390 }
391 _mm256_storeu_ps(d_ptr[i + 0] +
px, acc0);
392 _mm256_storeu_ps(d_ptr[i + 1] +
px, acc1);
393 _mm256_storeu_ps(d_ptr[i + 2] +
px, acc2);
394 _mm256_storeu_ps(d_ptr[i + 3] +
px, acc3);
395 }
396 for (; i < n; ++i) {
397 __m256 acc = _mm256_setzero_ps();
398 for (int32_t k = -half; k <= half; ++k) {
399 const size_t off =
static_cast<size_t>(
py + k) * w
400 +
static_cast<size_t>(
px);
401 acc = _mm256_fmadd_ps(
402 _mm256_loadu_ps(s_ptr[i] + off),
403 _mm256_set1_ps(kdata[static_cast<size_t>(k + half)]),
404 acc);
405 }
406 _mm256_storeu_ps(d_ptr[i] +
px, acc);
407 }
408 }
409 }
410 for (;
px < iw; ++
px) {
411 for (size_t i = 0; i < n; ++i) {
412 float acc = 0.0F;
413 for (int32_t k = -half; k <= half; ++k) {
414 const int32_t ny = border ? std::clamp(
py + k, 0, ih - 1) :
py + k;
415 const size_t off =
static_cast<size_t>(ny) * w +
static_cast<size_t>(
px);
416 acc += s_ptr[i][off] * kdata[static_cast<size_t>(k + half)];
417 }
419 }
420 }
421
422#elif defined(MAYAFLUX_ARCH_ARM64)
424 if (!border) {
425 for (;
px + 4 <= iw;
px += 4) {
426 size_t i = 0;
427 for (; i + Unroll - 1 < n; i += Unroll) {
428 float32x4_t acc0 = vdupq_n_f32(0.0F);
429 float32x4_t acc1 = vdupq_n_f32(0.0F);
430 float32x4_t acc2 = vdupq_n_f32(0.0F);
431 float32x4_t acc3 = vdupq_n_f32(0.0F);
432 for (int32_t k = -half; k <= half; ++k) {
433 const size_t off =
static_cast<size_t>(
py + k) * w
434 +
static_cast<size_t>(
px);
435 const float32x4_t kv = vdupq_n_f32(kdata[static_cast<size_t>(k + half)]);
436 acc0 = vmlaq_f32(acc0, vld1q_f32(s_ptr[i + 0] + off), kv);
437 acc1 = vmlaq_f32(acc1, vld1q_f32(s_ptr[i + 1] + off), kv);
438 acc2 = vmlaq_f32(acc2, vld1q_f32(s_ptr[i + 2] + off), kv);
439 acc3 = vmlaq_f32(acc3, vld1q_f32(s_ptr[i + 3] + off), kv);
440 }
441 vst1q_f32(d_ptr[i + 0] +
px, acc0);
442 vst1q_f32(d_ptr[i + 1] +
px, acc1);
443 vst1q_f32(d_ptr[i + 2] +
px, acc2);
444 vst1q_f32(d_ptr[i + 3] +
px, acc3);
445 }
446 for (; i < n; ++i) {
447 float32x4_t acc = vdupq_n_f32(0.0F);
448 for (int32_t k = -half; k <= half; ++k) {
449 const size_t off =
static_cast<size_t>(
py + k) * w
450 +
static_cast<size_t>(
px);
451 acc = vmlaq_f32(acc,
452 vld1q_f32(s_ptr[i] + off),
453 vdupq_n_f32(kdata[static_cast<size_t>(k + half)]));
454 }
455 vst1q_f32(d_ptr[i] +
px, acc);
456 }
457 }
458 }
459 for (;
px < iw; ++
px) {
460 for (size_t i = 0; i < n; ++i) {
461 float acc = 0.0F;
462 for (int32_t k = -half; k <= half; ++k) {
463 const int32_t ny = border ? std::clamp(
py + k, 0, ih - 1) :
py + k;
464 const size_t off =
static_cast<size_t>(ny) * w +
static_cast<size_t>(
px);
465 acc += s_ptr[i][off] * kdata[static_cast<size_t>(k + half)];
466 }
468 }
469 }
470
471#else
472 for (int32_t
px = 0;
px < iw; ++
px) {
473 for (size_t i = 0; i < n; ++i) {
474 float acc = 0.0F;
475 for (int32_t k = -half; k <= half; ++k) {
476 const int32_t ny = std::clamp(
py + k, 0, ih - 1);
477 const size_t off =
static_cast<size_t>(ny) * w +
static_cast<size_t>(
px);
478 acc += s_ptr[i][off] * kdata[static_cast<size_t>(k + half)];
479 }
481 }
482 }
483#endif
484 });
485}