fix(vector): batched Dot in the scalar VectorF32 fallback #2

Closed
jorijnvdgraaf wants to merge 1 commit from claude/scalar-fallback-dot into master
Showing only changes of commit ed188f207b - Show all commits

fix(vector): batched Dot in the scalar VectorF32 fallback

MatrixRowMajor calls VectorF32<Len, 1>::Dot(a0, b0, a1, b1, ...) for
its matrix-vector products and for LookAt. The x86, wasm and RISC-V
backends each define it; the scalar fallback, which every other target
gets, did not. Anything that imports Crafter.Math on such a target
failed to compile with "no member named 'Dot' in VectorF32", aarch64
included: cross-building Crafter.Graphics for a phone stopped there.

The fallback gains the same batched Dot, written like the RISC-V one:
one lane per (a, b) pair, summing only the first Len lanes of each
input.

No test exercises it: the suite builds the x86 and RISC-V tiers only.
Checked by cross-building Crafter.Graphics for aarch64-alpine-linux-musl
and running its UI on the device.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Jorijn van der Graaf 2026-10-06 16:52:24 +02:00
Signed by: jorijnvdgraaf
GPG key ID: 2937E59CDCC1BCFB

View file

@ -2136,6 +2136,28 @@ namespace Crafter {
return sq;
}
// Batched dot product over BatchSize (a, b) pairs: Dot(a0, b0, a1, b1,
// ...) returns one lane per pair. MatrixRowMajor relies on it, so
// without it this fallback (every target that has no intrinsic
// backend above) does not compile. Only the first Len lanes of each
// input are summed, as in the other backends.
template<typename... Rest>
requires((std::is_same_v<Rest, VectorF32<Len, Packing>> && ...) &&
(1 + sizeof...(Rest) == 2 * VectorBase<Len, Packing, float>::BatchSize))
constexpr static auto Dot(VectorF32<Len, Packing> first, Rest... rest) {
constexpr std::uint8_t N = VectorBase<Len, Packing, float>::BatchSize;
VectorF32<1, static_cast<std::uint8_t>(Packing * N)> r;
std::array<VectorF32<Len, Packing>, 2 * N> args{ first, rest... };
for (std::uint8_t i = 0; i < N; ++i)
for (std::uint8_t p = 0; p < Packing; ++p) {
float acc = 0.0f;
for (std::uint8_t k = 0; k < Len; ++k)
acc += args[2 * i].v[p * Len + k] * args[2 * i + 1].v[p * Len + k];
r.v[i * Packing + p] = acc;
}
return r;
}
template<typename... Rest>
requires((std::is_same_v<Rest, VectorF32<Len, Packing>> && ...) &&
(1 + sizeof...(Rest) == VectorBase<Len, Packing, float>::BatchSize))