Il est possible de faire jusqu'à 16 triangles avec AVX512, 8 avec AVX2 et 4 avec SSE. L'astuce cependant, est de s'assurer que les données sont au format SOA. L'autre astuce consiste à ne pas "retourner faux" à tout moment (juste filtrer les résultats à la fin). Donc, vous entrez triangle ressemblerait à quelque chose comme:
struct Tri {
__m256 e1[3];
__m256 e2[3];
__m256 v0[3];
};
Et votre rayon ressemblerait à ceci:
struct Ray {
__m256 dir[3];
__m256 pos[3];
};
Le code mathématique commence alors à regarder bien plus agréable (attention les _mm_dp_ps n'est pas la fonction la plus rapide jamais écrit - et sachez également que l'accès à l'implémentation interne des types __m128/__ m256/__ m512 n'est pas portable).
#define or8f _mm256_or_ps
#define mul _mm256_mul_ps
#define fmsub _mm256_fmsub_ps
#define fmadd _mm256_fmadd_ps
void cross(__m256 result[3], const __m256 a[3], const __m256 b[3])
{
result[0] = fmsub(a[1], b[2], mul(b[1], a[2]));
result[1] = fmsub(a[2], b[0], mul(b[2], a[0]));
result[2] = fmsub(a[0], b[1], mul(b[0], a[1]));
}
__m256 dot(const __m256 a[3], const __m256 b[3])
{
return fmadd(a[2], b[2], fmadd(a[1], b[1], mul(a[0], b[0])));
}
Vous avez essentiellement 4 conditions dans la méthode:
if (a > negativeEpsilon && a < positiveEpsilon)
if (u < 0.0f)
if (v < 0.0f || (u + v > 1.0f))
if (t < 0.0f || t > m_length)
Si l'une de ces conditions sont vraies, alors il n'y a pas d'intersection. Cela exige essentiellement un peu refactoring (dans le code pseudo)
__m256 condition0 = (a > negativeEpsilon && a < positiveEpsilon);
__m256 condition1 = (u < 0.0f)
__m256 condition2 = (v < 0.0f || (u + v > 1.0f))
__m256 condition3 = (t < 0.0f || t > m_length)
// combine all conditions that can cause failure.
__m256 failed = or8f(or8f(condition0, condition1), or8f(condition2, condition3));
Donc finalement, si une intersection a eu lieu, le résultat sera t. Si une intersection N'A PAS se, alors nous devons mettre le résultat à quelque chose de mal (un nombre négatif est peut-être un bon choix dans ce cas!)
// if(failed) return -1;
// else return t;
return _mm256_blendv_ps(t, _mm256_set1_ps(-1.0f), failed);
Alors que le code final peut sembler un peu méchant, il finira par être beaucoup plus rapide que votre approche. Le diable est dans les détails cependant ....
Un problème majeur avec cette approche est que vous avez le choix entre tester 1 rayon contre 8 triangles, ou tester 8 rayons contre 1 triangle. Pour les rayons principalement ce n'est probablement pas une grosse affaire. Pour les rayons secondaires qui ont l'habitude de se disperser dans des directions différentes), les choses peuvent commencer à devenir un peu gênantes. Il y a de bonnes chances que la plus grande partie du code de suivi de rayon se termine par un modèle de: test -> sort -> batch -> test -> sort -> lot
Si vous ne suivez pas ce modèle, vous êtes à peu près jamais tirer le meilleur parti des unités vectorielles. (Heureusement, les instructions de compression/expansion dans AVX512 aider beaucoup à ce sujet!)
Avez-vous regardé embree? https://embree.github.io/ – Rem
Merci! Je vais regarder. – Cloyz