JPL Spatial
Sound spatialization and propagation library
Loading...
Searching...
No Matches
SIMD.h
Go to the documentation of this file.
1//
2// ██╗██████╗ ██╗ ██╗██████╗ ███████╗
3// ██║██╔══██╗ ██║ ██║██╔══██╗██╔════╝ ** JPLSpatial **
4// ██║██████╔╝ ██║ ██║██████╔╝███████╗
5// ██ ██║██╔═══╝ ██║ ██║██╔══██╗╚════██║ https://github.com/Jaytheway/JPLSpatial
6// ╚█████╔╝██║ ███████╗██║██████╔╝███████║
7// ╚════╝ ╚═╝ ╚══════╝╚═╝╚═════╝ ╚══════╝
8//
9// Copyright 2025 Jaroslav Pevno, JPLSpatial is offered under the terms of the ISC license:
10//
11// Permission to use, copy, modify, and/or distribute this software for any purpose with or
12// without fee is hereby granted, provided that the above copyright notice and this permission
13// notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
14// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
15// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
16// CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
17// WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
18// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19
20#pragma once
21
22#include "JPLSpatial/Core.h"
25
26#include <bit>
27#include <limits>
28
29#if defined(JPL_USE_SSE)
30#include <immintrin.h>
31#include <emmintrin.h>
32#include <cstring> // std::memcpy
33#if defined (JPL_USE_SSE4_1)
34#include <smmintrin.h>
35#endif
36#elif defined(JPL_USE_NEON)
37#ifdef JPL_COMPILER_MSVC
38#include <intrin.h>
39#include <arm64_neon.h>
40#else
41#include <arm_neon.h>
42#endif
43#else
44#include <algorithm>
45#include <cmath>
46#endif
47
48#include <type_traits>
49#include <ostream>
50#include <span>
51
52namespace JPL
53{
54 // Forward declaration
55 struct simd_mask;
56
57 //==========================================================================
60 {
61 // Underlying native vector type
62#if defined(JPL_USE_SSE)
63 using Type = __m128;
64#elif defined(JPL_USE_NEON)
65 using Type = float32x4_t;
66#else
67 using Type = std::array<float, 4>;
68#endif
69
74 JPL_INLINE simd(float value) noexcept;
75 JPL_INLINE simd(float v0, float v1, float v2, float v3) noexcept;
76 JPL_INLINE simd(const float* mem);
77
79 template <int Scale>
80 static JPL_INLINE simd gather(const float* base, const simd_mask& offsets);
81
83 static JPL_INLINE simd zero() noexcept;
84
87
89
95
97 static constexpr std::size_t size() noexcept { return 4; }
98
100 JPL_INLINE void load(const float* mem);
101
103 JPL_INLINE void store(float* mem) const;
104
106 JPL_INLINE float operator [] (uint32 index) const noexcept;
107
109 template<uint32 LaneIndex> requires (LaneIndex < 4)
110 JPL_INLINE float get_lane() const noexcept;
111
113 JPL_INLINE simd operator * (const simd& other) const noexcept;
114
116 JPL_INLINE simd operator * (float value) const noexcept;
117
119 friend JPL_INLINE simd operator * (float value, const simd& other) noexcept;
120
122 JPL_INLINE simd operator / (float value) const noexcept;
123
125 JPL_INLINE simd& operator *= (float value) noexcept;
126
128 JPL_INLINE simd& operator *= (const simd& other) noexcept;
129
131 JPL_INLINE simd& operator /= (float value) noexcept;
132
134 JPL_INLINE simd& operator /= (const simd& other) noexcept;
135
137 JPL_INLINE simd operator + (const simd& other) const noexcept;
138
140 JPL_INLINE simd& operator += (const simd& other) noexcept;
141
143 JPL_INLINE simd operator - () const noexcept;
144
147
150
153
156
159
162
165 JPL_INLINE simd splat() const;
166
168 JPL_INLINE float reduce() const noexcept;
169
171 JPL_INLINE float reduce_max() const noexcept;
172
174 JPL_INLINE float reduce_min() const noexcept;
175
177 JPL_INLINE float reduce_mean() const noexcept { return reduce() * 0.25f; };
178
179 JPL_INLINE /*explicit*/ operator Type() const noexcept { return mNative; }
180
183
186
189
192
194 {
195 float data[4];
196 vec.store(data);
197 inStream << data[0] << ", " << data[1] << ", " << data[2] << ", " << data[3];
198 return inStream;
199 }
200
202 };
203 static_assert(std::is_trivial<simd>(), "simd supposed to be a trivial type.");
204
205 //==========================================================================
207 {
208 // Underlying vector type
209#if defined(JPL_USE_SSE)
210 using Type = __m128i;
211#elif defined(JPL_USE_NEON)
212 using Type = uint32x4_t;
213#else
214 using Type = std::array<uint32,4>;
215#endif
216
224
225 static JPL_INLINE simd_mask replicate(int value) noexcept;
226 static JPL_INLINE simd_mask replicate(uint32 value) noexcept;
227
228 static JPL_INLINE simd_mask zero() noexcept;
229
231 static constexpr std::size_t size() noexcept { return 4; }
232
234 inline static constexpr uint32 cTrueValue = 0xffffffffu;
235
237 JPL_INLINE void load(const uint32* mem);
238
240 JPL_INLINE void store(uint32* mem) const;
241
243 template<uint32 LaneIndex> requires (LaneIndex < 4)
244 JPL_INLINE uint32 get_lane() const noexcept;
245
247 JPL_INLINE bool all_of() const noexcept;
248
250 JPL_INLINE bool any_of() const noexcept;
251
253 JPL_INLINE bool none_of() const noexcept;
254
256 JPL_INLINE int reduce_count() const noexcept;
257
259 JPL_INLINE int reduce_min_index() const noexcept;
261 JPL_INLINE int reduce_max_index() const noexcept;
262
264 JPL_INLINE int GetTrues() const noexcept;
265
268
271
274
277
280
283
286
289
292
295
298
299 template<uint Count> requires(Count <= 31)
301
302 template<uint Count> requires(Count <= 31)
304
305 template<uint Count> requires(Count <= 31)
307
310
311 JPL_INLINE /*explicit*/ operator Type() const noexcept { return mNative; };
312
314 JPL_INLINE simd to_simd() const noexcept;
315
317 JPL_INLINE simd as_simd() const noexcept;
318
320 {
321 uint32 data[4];
322 vec.store(data);
323 inStream << data[0] << ", " << data[1] << ", " << data[2] << ", " << data[3];
324 return inStream;
325 }
326
328 };
329 static_assert(std::is_trivial<simd_mask>(), "simd_mask supposed to be a trivial type.");
330
331 //==========================================================================
333 JPL_INLINE simd operator | (const simd& a, const simd& b) noexcept;
335 JPL_INLINE simd operator ^ (const simd& a, const simd& b) noexcept;
337 JPL_INLINE simd operator & (const simd& a, const simd& b) noexcept;
338
340 JPL_INLINE simd_mask operator == (const simd& a, const simd& b) noexcept;
341 JPL_INLINE simd_mask operator != (const simd& a, const simd& b) noexcept { return ~(a == b); }
342
343 JPL_INLINE simd_mask operator < (const simd& a, const simd& b) noexcept;
344 JPL_INLINE simd_mask operator <= (const simd& a, const simd& b) noexcept;
345 JPL_INLINE simd_mask operator > (const simd& a, const simd& b) noexcept;
346 JPL_INLINE simd_mask operator >= (const simd& a, const simd& b) noexcept;
347
349 JPL_INLINE simd_mask operator == (const simd_mask& a, const simd_mask& b) noexcept;
350 JPL_INLINE simd_mask operator != (const simd_mask& a, const simd_mask& b) noexcept { return ~(a == b); }
351
353 JPL_INLINE simd_mask operator | (const simd_mask& a, const simd_mask& b) noexcept;
355 JPL_INLINE simd_mask operator ^ (const simd_mask& a, const simd_mask& b) noexcept;
357 JPL_INLINE simd_mask operator & (const simd_mask& a, const simd_mask& b) noexcept;
358
359 //==========================================================================
360 namespace Math
361 {
363 [[nodiscard]] JPL_INLINE simd Sqrt(const simd& vec) noexcept;
364
366 [[nodiscard]] JPL_INLINE simd InvSqrt(const simd& vec) noexcept;
367
368 // TODO: TEST
370 [[nodiscard]] JPL_INLINE simd Sign2(const simd& vec) noexcept;
371
372 // TODO: TEST
373 [[nodiscard]] JPL_INLINE simd_mask IsNearlyZero(const simd& vec, float tolerance = JPL_FLOAT_EPS) noexcept;
374 }
375
377 JPL_INLINE simd max(const simd& a, const simd& b) noexcept;
378
380 JPL_INLINE simd min(const simd& a, const simd& b) noexcept;
381
382 JPL_INLINE simd abs(const simd& vec) noexcept;
383
385 JPL_INLINE simd clamp(const simd& value, const simd& minV, const simd& maxV) noexcept;
386
388 JPL_INLINE simd fma(const simd& mul1, const simd& mul2, const simd& addV) noexcept;
389
390 namespace Math
391 {
393 JPL_INLINE simd FMA(const simd& mul1, const simd& mul2, const simd& addV) noexcept { return fma(mul1, mul2, addV); }
394 JPL_INLINE simd Lerp(const simd& a, const simd& b, const simd& t) noexcept { return fma(t, (b - a), a); }
395 }
396
398 JPL_INLINE simd floor(const simd& vec) noexcept;
399
401 JPL_INLINE simd ceil(const simd& vec) noexcept;
402
404 JPL_INLINE simd round(const simd& vec) noexcept;
405
407 JPL_INLINE simd interleave_lohi(const simd& vec) noexcept;
408
410 JPL_INLINE simd combine_lo(const simd& a, const simd& b) noexcept;
411
413 JPL_INLINE simd combine_hi(const simd& a, const simd& b) noexcept;
414
416 JPL_INLINE simd combine_lohi(const simd& a, const simd& b) noexcept;
417
419 JPL_INLINE simd reverse(const simd& vec) noexcept;
420
422 JPL_INLINE simd_mask max(const simd_mask& a, const simd_mask& b) noexcept;
423
425 JPL_INLINE simd_mask min(const simd_mask& a, const simd_mask& b) noexcept;
426
428 JPL_INLINE simd_mask clamp(const simd_mask& value, const simd_mask& minV, const simd_mask& maxV) noexcept;
429
431 JPL_INLINE simd_mask maxs(const simd_mask& a, const simd_mask& b) noexcept;
432
434 JPL_INLINE simd_mask mins(const simd_mask& a, const simd_mask& b) noexcept;
435
437 JPL_INLINE simd_mask clamps(const simd_mask& value, const simd_mask& minV, const simd_mask& maxV) noexcept;
438
439} // namespace JPL
440
441// Specializing std::equals_to to be able use with standart containers and algorithms
442template<>
443struct std::equal_to<JPL::simd>
444{
445 [[nodiscard]] JPL_INLINE bool operator()(const JPL::simd& a, const JPL::simd& b) const { return (a == b).all_of(); }
446};
447
448//==============================================================================
449//
450// Code beyond this point is implementation detail...
451//
452//==============================================================================
453namespace JPL
454{
455 //==========================================================================
457 {
458 return simd::zero();
459 }
460
462 {
463 return simd(1.0f);
464 }
465
467 {
468 return simd(0.5f);
469 }
470
472 {
473 return simd(JPL_PI);
474 }
475
476 //==========================================================================
478 {
479#if defined(JPL_USE_SSE)
480 mNative = _mm_set1_ps(value);
481#elif defined(JPL_USE_NEON)
482 mNative = vdupq_n_f32(value);
483#else
484 mNative[0] = value;
485 mNative[1] = value;
486 mNative[2] = value;
487 mNative[3] = value;
488#endif
489 }
490
491 JPL_INLINE simd::simd(float v0, float v1, float v2, float v3) noexcept
492 {
493#if defined(JPL_USE_SSE)
494 mNative = _mm_set_ps(v3, v2, v1, v0);
495#elif defined(JPL_USE_NEON)
496 mNative = vdupq_n_f32(0);
497 mNative = vsetq_lane_f32(v0, mNative, 0);
498 mNative = vsetq_lane_f32(v1, mNative, 1);
499 mNative = vsetq_lane_f32(v2, mNative, 2);
500 mNative = vsetq_lane_f32(v3, mNative, 3);
501#else
502 mNative[0] = v0;
503 mNative[1] = v1;
504 mNative[2] = v2;
505 mNative[3] = v3;
506#endif
507 }
508
510 {
511 load(mem);
512 }
513
514 template<int Scale>
516 {
517#if defined(JPL_USE_SSE)
518#ifdef JPL_USE_AVX2
520#else
521 const uint8* base = reinterpret_cast<const uint8*>(inBase);
522 Type x = _mm_load_ss(reinterpret_cast<const float*>(base + offsets.get_lane<0>() * Scale));
523 Type y = _mm_load_ss(reinterpret_cast<const float*>(base + offsets.get_lane<1>() * Scale));
525 Type z = _mm_load_ss(reinterpret_cast<const float*>(base + offsets.get_lane<2>() * Scale));
526 Type w = _mm_load_ss(reinterpret_cast<const float*>(base + offsets.get_lane<3>() * Scale));
528 return _mm_movelh_ps(xy, zw);
529#endif
530#else
531 const uint8* base = reinterpret_cast<const uint8*>(inBase);
532 float x = *reinterpret_cast<const float*>(base + offsets.get_lane<0>() * Scale);
533 float y = *reinterpret_cast<const float*>(base + offsets.get_lane<1>() * Scale);
534 float z = *reinterpret_cast<const float*>(base + offsets.get_lane<2>() * Scale);
535 float w = *reinterpret_cast<const float*>(base + offsets.get_lane<3>() * Scale);
536 return { x, y, z, w };
537#endif
538 }
539
540
542 {
543#if defined(JPL_USE_SSE)
544 return _mm_setzero_ps();
545#elif defined(JPL_USE_NEON)
546 return vdupq_n_f32(0);
547#else
548 return simd(0, 0, 0, 0);
549#endif
550 }
551
553 {
554 return simd(std::numeric_limits<float>::quiet_NaN());
555 }
556
558 {
559 return simd(std::numeric_limits<float>::infinity());
560 }
561
562 JPL_INLINE void simd::load(const float* mem)
563 {
564#if defined(JPL_USE_SSE)
566#elif defined(JPL_USE_NEON)
568#else
569 mNative = { mem[0], mem[1], mem[2], mem[3] };
570#endif
571 }
572
573 JPL_INLINE void simd::store(float* mem) const
574 {
575#if defined(JPL_USE_SSE)
577#elif defined(JPL_USE_NEON)
579#else
580 mem[0] = mNative[0];
581 mem[1] = mNative[1];
582 mem[2] = mNative[2];
583 mem[3] = mNative[3];
584#endif
585 }
586
587 JPL_INLINE float simd::operator[](uint32 index) const noexcept
588 {
589 JPL_ASSERT(index < 4);
590#if defined(JPL_USE_SSE)
591#if defined(JPL_USE_AVX)
592 __m128i sel = _mm_set1_epi32(static_cast<int>(index));
593 __m128 p = _mm_permutevar_ps(mNative, sel);
594 return _mm_cvtss_f32(p);
595#else
596 alignas(16) float temp[4];
597 _mm_store_ps(temp, mNative);
598 return temp[index];
599#endif
600#elif defined(JPL_USE_NEON)
601 alignas(16) float temp[4];
602 vst1q_f32(temp, mNative);
603 return temp[index];
604#else
605 return mNative[index];
606#endif
607 }
608
609 template<uint32 LaneIndex> requires (LaneIndex < 4)
610 JPL_INLINE float simd::get_lane() const noexcept
611 {
612#if defined(JPL_USE_SSE)
613 return _mm_cvtss_f32(_mm_shuffle_ps(mNative, mNative, _MM_SHUFFLE(0, 0, 0, LaneIndex)));
614#elif defined(JPL_USE_NEON)
615 return vgetq_lane_f32(mNative, LaneIndex);
616#else
617 return mNative[LaneIndex];
618#endif
619 }
620
622 {
623#if defined(JPL_USE_SSE)
624 return _mm_mul_ps(mNative, other.mNative);
625#elif defined(JPL_USE_NEON)
626 return vmulq_f32(mNative, other.mNative);
627#else
628 return simd(
629 mNative[0] * other.mNative[0],
630 mNative[1] * other.mNative[1],
631 mNative[2] * other.mNative[2],
632 mNative[3] * other.mNative[3]
633 );
634#endif
635 }
636
638 {
639#if defined(JPL_USE_SSE)
640 return _mm_mul_ps(mNative, _mm_set1_ps(value));
641#elif defined(JPL_USE_NEON)
642 return vmulq_n_f32(mNative, value);
643#else
644 return simd(mNative[0] * value, mNative[1] * value, mNative[2] * value, mNative[3] * value);
645#endif
646 }
647
648 JPL_INLINE simd operator * (float value, const simd& vec) noexcept
649 {
650#if defined(JPL_USE_SSE)
652#elif defined(JPL_USE_NEON)
653 return vmulq_n_f32(vec.mNative, value);
654#else
655 return simd(
656 value * vec.mNative[0],
657 value * vec.mNative[1],
658 value * vec.mNative[2],
659 value * vec.mNative[3]
660 );
661#endif
662 }
663
665 {
666#if defined(JPL_USE_SSE)
667 return _mm_div_ps(mNative, _mm_set1_ps(value));
668#elif defined(JPL_USE_NEON)
669 return vdivq_f32(mNative, vdupq_n_f32(value));
670#else
671 return simd(
672 mNative[0] / value,
673 mNative[1] / value,
674 mNative[2] / value,
675 mNative[3] / value
676 );
677#endif
678 }
679
681 {
682#if defined(JPL_USE_SSE)
683 mNative = _mm_mul_ps(mNative, _mm_set1_ps(value));
684#elif defined(JPL_USE_NEON)
685 mNative = vmulq_n_f32(mNative, value);
686#else
687 for (int i = 0; i < 4; ++i)
688 mNative[i] *= value;
689#endif
690 return *this;
691 }
692
694 {
695#if defined(JPL_USE_SSE)
696 mNative = _mm_mul_ps(mNative, other.mNative);
697#elif defined(JPL_USE_NEON)
698 mNative = vmulq_f32(mNative, other.mNative);
699#else
700 for (int i = 0; i < 4; ++i)
701 mNative[i] *= other.mNative[i];
702#endif
703 return *this;
704 }
705
707 {
708#if defined(JPL_USE_SSE)
709 mNative = _mm_div_ps(mNative, _mm_set1_ps(value));
710#elif defined(JPL_USE_NEON)
711 mNative = vdivq_f32(mNative, vdupq_n_f32(value));
712#else
713 for (int i = 0; i < 4; ++i)
714 mNative[i] /= value;
715#endif
716 return *this;
717 }
718
720 {
721#if defined(JPL_USE_SSE)
722 mNative = _mm_div_ps(mNative, other);
723#elif defined(JPL_USE_NEON)
724 mNative = vdivq_f32(mNative, other);
725#else
726 for (int i = 0; i < 4; ++i)
727 mNative[i] /= other.mNative[i];
728#endif
729 return *this;
730 }
731
733 {
734#if defined(JPL_USE_SSE)
735 return _mm_add_ps(mNative, other.mNative);
736#elif defined(JPL_USE_NEON)
737 return vaddq_f32(mNative, other.mNative);
738#else
739 return simd(
740 mNative[0] + other.mNative[0],
741 mNative[1] + other.mNative[1],
742 mNative[2] + other.mNative[2],
743 mNative[3] + other.mNative[3]
744 );
745#endif
746 }
747
749 {
750#if defined(JPL_USE_SSE)
751 mNative = _mm_add_ps(mNative, other.mNative);
752#elif defined(JPL_USE_NEON)
753 mNative = vaddq_f32(mNative, other.mNative);
754#else
755 for (int i = 0; i < 4; ++i)
756 mNative[i] += other.mNative[i];
757#endif
758 return *this;
759 }
760
762 {
763#if defined(JPL_USE_SSE)
765#elif defined(JPL_USE_NEON)
766#if 1 // ifdef JPL_CROSS_PLATFORM_DETERMINISTIC
767 return vsubq_f32(vdupq_n_f32(0), mNative);
768#else
769 return vnegq_f32(mNative);
770#endif
771#else
772#if 1 // ifdef JPL_CROSS_PLATFORM_DETERMINISTIC
773 return simd(
774 0.0f - mNative[0],
775 0.0f - mNative[1],
776 0.0f - mNative[2],
777 0.0f - mNative[3]
778 );
779#else
780 return simd(-mNative[0], -mNative[1], -mNative[2], -mNative[3]);
781#endif
782#endif
783 }
784
786 {
787#if defined(JPL_USE_SSE)
788 return _mm_sub_ps(mNative, other.mNative);
789#elif defined(JPL_USE_NEON)
790 return vsubq_f32(mNative, other.mNative);
791#else
792 return simd(
793 mNative[0] - other.mNative[0],
794 mNative[1] - other.mNative[1],
795 mNative[2] - other.mNative[2],
796 mNative[3] - other.mNative[3]
797 );
798#endif
799 }
800
802 {
803#if defined(JPL_USE_SSE)
804 mNative = _mm_sub_ps(mNative, other.mNative);
805#elif defined(JPL_USE_NEON)
806 mNative = vsubq_f32(mNative, other.mNative);
807#else
808 for (int i = 0; i < 4; ++i)
809 mNative[i] -= other.mNative[i];
810#endif
811 return *this;
812 }
813
815 {
816#if defined(JPL_USE_SSE)
817 return _mm_div_ps(mNative, other.mNative);
818#elif defined(JPL_USE_NEON)
819 return vdivq_f32(mNative, other.mNative);
820#else
821 return simd(
822 mNative[0] / other.mNative[0],
823 mNative[1] / other.mNative[1],
824 mNative[2] / other.mNative[2],
825 mNative[3] / other.mNative[3]
826 );
827#endif
828 }
829
830 JPL_INLINE void simd::operator|=(const simd& other) noexcept
831 {
832#if defined(JPL_USE_SSE)
833 mNative = _mm_or_ps(mNative, other.mNative);
834#elif defined(JPL_USE_NEON)
835 mNative = vreinterpretq_f32_u32(
836 vorrq_u32(
837 vreinterpretq_u32_f32(mNative),
839 )
840 );
841#else
842 *this = (as_mask() | other.as_mask()).as_simd();
843#endif
844 }
845
846 JPL_INLINE void simd::operator^=(const simd& other) noexcept
847 {
848#if defined(JPL_USE_SSE)
849 mNative = _mm_xor_ps(mNative, other.mNative);
850#elif defined(JPL_USE_NEON)
851 mNative = vreinterpretq_f32_u32(
852 veorq_u32(
853 vreinterpretq_u32_f32(mNative),
855 )
856 );
857#else
858 *this = (as_mask() ^ other.as_mask()).as_simd();
859#endif
860 }
861
862 JPL_INLINE void simd::operator&=(const simd& other) noexcept
863 {
864#if defined(JPL_USE_SSE)
865 mNative = _mm_and_ps(mNative, other.mNative);
866#elif defined(JPL_USE_NEON)
867 mNative = vreinterpretq_f32_u32(
868 vandq_u32(
869 vreinterpretq_u32_f32(mNative),
871 )
872 );
873#else
874 *this = (as_mask() & other.as_mask()).as_simd();
875#endif
876 }
877
878 JPL_INLINE simd operator|(const simd& a, const simd& b) noexcept
879 {
880#if defined(JPL_USE_SSE)
881 return _mm_or_ps(a.mNative, b.mNative);
882#elif defined(JPL_USE_NEON)
884 vorrq_u32(
885 vreinterpretq_u32_f32(a.mNative),
886 vreinterpretq_u32_f32(b.mNative)
887 )
888 );
889#else
890 return (a.as_mask() | b.as_mask()).as_simd();
891#endif
892 }
893
894 JPL_INLINE simd operator^(const simd& a, const simd& b) noexcept
895 {
896#if defined(JPL_USE_SSE)
897 return _mm_xor_ps(a.mNative, b.mNative);
898#elif defined(JPL_USE_NEON)
900 veorq_u32(
901 vreinterpretq_u32_f32(a.mNative),
902 vreinterpretq_u32_f32(b.mNative)
903 )
904 );
905#else
906 return (a.as_mask() ^ b.as_mask()).as_simd();
907#endif
908 }
909
910 JPL_INLINE simd operator&(const simd& a, const simd& b) noexcept
911 {
912#if defined(JPL_USE_SSE)
913 return _mm_and_ps(a.mNative, b.mNative);
914#elif defined(JPL_USE_NEON)
916 vandq_u32(
917 vreinterpretq_u32_f32(a.mNative),
918 vreinterpretq_u32_f32(b.mNative)
919 )
920 );
921#else
922 return (a.as_mask() & b.as_mask()).as_simd();
923#endif
924 }
925
926 template<uint32 LaneIndex> requires(LaneIndex < 4)
928 {
929#if defined(JPL_USE_SSE)
930 return _mm_shuffle_ps(mNative, mNative, _MM_SHUFFLE(LaneIndex, LaneIndex, LaneIndex, LaneIndex));
931#elif defined(JPL_USE_NEON)
932 return vdupq_laneq_f32(mNative, LaneIndex);
933#else
934 return { mNative[LaneIndex], mNative[LaneIndex], mNative[LaneIndex], mNative[LaneIndex] };
935#endif
936 }
937
939 {
940#if defined(JPL_USE_SSE)
945 return _mm_cvtss_f32(sums);
946#elif defined (JPL_USE_NEON)
947 return vaddvq_f32(mNative); // AArch64
948#else
949 return mNative[0] + mNative[1] + mNative[2] + mNative[3];
950#endif
951 }
952
954 {
955#if defined(JPL_USE_SSE)
958 Type shuf2 = _mm_shuffle_ps(max1, max1, _MM_SHUFFLE(1, 0, 3, 2));
960 return _mm_cvtss_f32(max1);
961#elif defined (JPL_USE_NEON)
964 return vget_lane_f32(max_pair, 0);
965#else
966 return std::max({ mNative[0], mNative[1], mNative[2], mNative[3] });
967#endif
968 }
969
971 {
972#if defined(JPL_USE_SSE)
975 Type shuf2 = _mm_shuffle_ps(min1, min1, _MM_SHUFFLE(1, 0, 3, 2));
977 return _mm_cvtss_f32(min1);
978#elif defined (JPL_USE_NEON)
981 return vget_lane_f32(min_pair, 0);
982#else
983 return std::min({ mNative[0], mNative[1], mNative[2], mNative[3] });
984#endif
985 }
986
988 {
989#if defined(JPL_USE_SSE)
991#elif defined(JPL_USE_NEON)
992 // Fixup from https://github.com/DLTcollab/sse2neon (MIT)
994 {
995 /* Detect values >= 2147483648.0f (out of INT32 range) */
996 float32x4_t max_f = vdupq_n_f32(2147483648.0f);
998
999 /* Detect NaN: x != x for NaN values */
1001
1002 /* Combine: any overflow or NaN should produce INT32_MIN */
1004
1005 /* Blend: select INT32_MIN where needed */
1008 };
1009
1012 //return vcvtq_u32_f32(mNative);
1013#else
1014 return {
1015 uint32(mNative[0]),
1016 uint32(mNative[1]),
1017 uint32(mNative[2]),
1018 uint32(mNative[3])
1019 };
1020#endif
1021 }
1022
1024 {
1025#if defined(JPL_USE_SSE)
1026 return _mm_castps_si128(mNative);
1027#elif defined(JPL_USE_NEON)
1029#else
1030 return std::bit_cast<simd_mask>(*this);
1031#endif
1032 }
1033
1034 JPL_INLINE simd simd::select(const simd_mask& mask, const simd& a, const simd& b) noexcept
1035 {
1036#if defined(JPL_USE_SSE4_1)
1037 return _mm_blendv_ps(b.mNative, a.mNative, _mm_castsi128_ps(mask.mNative));
1038#elif defined(JPL_USE_SSE)
1040 Type t0 = _mm_andnot_ps(mf, b.mNative); // (~m) & a
1041 Type t1 = _mm_and_ps(mf, a.mNative); // m & b
1042 return _mm_or_ps(t0, t1);
1043#elif defined(JPL_USE_NEON)
1044 return vbslq_f32(
1046 a.mNative,
1047 b.mNative);
1048#else
1049 return {
1050 mask.mNative[0] ? a.mNative[0] : b.mNative[0],
1051 mask.mNative[1] ? a.mNative[1] : b.mNative[1],
1052 mask.mNative[2] ? a.mNative[2] : b.mNative[2],
1053 mask.mNative[3] ? a.mNative[3] : b.mNative[3]
1054 };
1055#endif
1056 }
1057
1059 {
1060 return simd(1.0f) / vec;
1061 }
1062
1063 //==========================================================================
1065 {
1066#if defined(JPL_USE_SSE)
1067 mNative = _mm_set1_epi32(static_cast<int>(value));
1068#elif defined(JPL_USE_NEON)
1069 mNative = vdupq_n_u32(value);
1070#else
1071 mNative[0] = value;
1072 mNative[1] = value;
1073 mNative[2] = value;
1074 mNative[3] = value;
1075#endif
1076 }
1077
1079 {
1080#if defined(JPL_USE_SSE)
1081 mNative = _mm_set_epi32(
1082 static_cast<int>(v3),
1083 static_cast<int>(v2),
1084 static_cast<int>(v1),
1085 static_cast<int>(v0)
1086 );
1087#elif defined(JPL_USE_NEON)
1088 mNative = vdupq_n_u32(0);
1089 mNative = vsetq_lane_u32(v0, mNative, 0);
1090 mNative = vsetq_lane_u32(v1, mNative, 1);
1091 mNative = vsetq_lane_u32(v2, mNative, 2);
1092 mNative = vsetq_lane_u32(v3, mNative, 3);
1093#else
1094 mNative[0] = v0;
1095 mNative[1] = v1;
1096 mNative[2] = v2;
1097 mNative[3] = v3;
1098#endif
1099 }
1100
1102 {
1103 load(mem);
1104 }
1105
1107 {
1108#if defined(JPL_USE_NEON)
1110#else
1111 // for SSE/AVX it's the same as in constructor
1112 return simd_mask(static_cast<uint32>(value));
1113#endif
1114 }
1115
1117 {
1118 return simd_mask(value);
1119 }
1120
1122 {
1123#if defined(JPL_USE_SSE)
1124 return _mm_setzero_si128();
1125#elif defined(JPL_USE_NEON)
1126 return vdupq_n_u32(0);
1127#else
1128 return simd_mask(0, 0, 0, 0);
1129#endif
1130 }
1131
1133 {
1134#if defined(JPL_USE_SSE)
1135 std::memcpy(&mNative, mem, sizeof(mNative));
1136#elif defined(JPL_USE_NEON)
1138#else
1139 mNative = { mem[0], mem[1], mem[2], mem[3] };
1140#endif
1141 }
1142
1144 {
1145#if defined(JPL_USE_SSE)
1146 std::memcpy(mem, &mNative, sizeof(mNative));
1147#elif defined(JPL_USE_NEON)
1149#else
1150 mem[0] = mNative[0];
1151 mem[1] = mNative[1];
1152 mem[2] = mNative[2];
1153 mem[3] = mNative[3];
1154#endif
1155 }
1156
1157 template<uint32 LaneIndex> requires (LaneIndex < 4)
1159 {
1160#if defined(JPL_USE_SSE)
1161 return static_cast<uint32>(_mm_cvtsi128_si32(_mm_shuffle_epi32(mNative, _MM_SHUFFLE(0, 0, 0, LaneIndex))));
1162#elif defined(JPL_USE_NEON)
1163 return vgetq_lane_u32(mNative, LaneIndex);
1164#else
1165 return mNative[LaneIndex];
1166#endif
1167 }
1168
1170 {
1171 return GetTrues() == 0b1111;
1172 }
1173
1175 {
1176 return GetTrues() != 0;
1177 }
1178
1180 {
1181 return GetTrues() == 0;
1182 }
1183
1185 {
1186#if defined(JPL_USE_SSE)
1187 return std::popcount(static_cast<uint32>(_mm_movemask_ps(_mm_castsi128_ps(mNative))));
1188#elif defined(JPL_USE_NEON)
1189 return vaddvq_u32(vshrq_n_u32(mNative, 31));
1190#else
1191 return (mNative[0] >> 31) + (mNative[1] >> 31) + (mNative[2] >> 31) + (mNative[3] >> 31);
1192#endif
1193 }
1194
1196 {
1197 uint32 m = static_cast<uint32>(GetTrues());
1198 // std::countr_zero(0) == number of bits (32), so guard m==0 once.
1199 return m ? static_cast<int>(std::countr_zero(m)) : -1;
1200 }
1201
1203 {
1204 uint32 m = static_cast<uint32>(GetTrues());
1205 // For m==0, std::countl_zero(m) == 32 -> 31-32 == -1
1206 return 31 - static_cast<int>(std::countl_zero(m));
1207 }
1208
1210 {
1211#if defined(JPL_USE_SSE)
1213#elif defined(JPL_USE_NEON)
1214 static_assert(JPL_CPU_ADDRESS_BITS == 64 && "32-bit arm doesn't support this.");
1216#if defined(JPL_COMPILER_MSVC)
1217 const uint32x4_t w = { .n128_u32 = { 1u, 2u, 4u, 8u } };
1218#else
1219 const uint32x4_t w = { 1u, 2u, 4u, 8u };
1220#endif
1221 return static_cast<int>(vaddvq_u32(vmulq_u32(bits, w)));
1222#else
1223 return (mNative[0] >> 31)
1224 | ((mNative[1] >> 31) << 1)
1225 | ((mNative[2] >> 31) << 2)
1226 | ((mNative[3] >> 31) << 3);
1227#endif
1228 }
1229
1231 {
1232#if defined(JPL_USE_SSE4_1)
1233 return _mm_mullo_epi32(mNative, other.mNative);
1234#elif defined(JPL_USE_SSE)
1235 Type tmp1 = _mm_mul_epu32(mNative, other.mNative);
1237 return _mm_unpacklo_epi32(
1238 _mm_shuffle_epi32(tmp1, _MM_SHUFFLE(0, 0, 2, 0)),
1239 _mm_shuffle_epi32(tmp2, _MM_SHUFFLE(0, 0, 2, 0))
1240 );
1241#elif defined(JPL_USE_NEON)
1242 return vmulq_u32(mNative, other.mNative);
1243#else
1244 return {
1245 mNative[0] * other.mNative[0],
1246 mNative[1] * other.mNative[1],
1247 mNative[2] * other.mNative[2],
1248 mNative[3] * other.mNative[3]
1249 };
1250#endif
1251 }
1252
1254 {
1255#if defined(JPL_USE_SSE4_1)
1256 mNative = _mm_mullo_epi32(mNative, other.mNative);
1257#elif defined(JPL_USE_SSE)
1258 Type tmp1 = _mm_mul_epu32(mNative, other.mNative);
1260 mNative = _mm_unpacklo_epi32(
1261 _mm_shuffle_epi32(tmp1, _MM_SHUFFLE(0, 0, 2, 0)),
1262 _mm_shuffle_epi32(tmp2, _MM_SHUFFLE(0, 0, 2, 0))
1263 );
1264#elif defined(JPL_USE_NEON)
1265 mNative = vmulq_u32(mNative, other.mNative);
1266#else
1267 mNative[0] *= other.mNative[0];
1268 mNative[1] *= other.mNative[1];
1269 mNative[2] *= other.mNative[2];
1270 mNative[3] *= other.mNative[3];
1271#endif
1272 return *this;
1273 }
1274
1276 {
1277#if defined(JPL_USE_SSE)
1278 return _mm_add_epi32(mNative, other.mNative);
1279#elif defined(JPL_USE_NEON)
1280 return vaddq_u32(mNative, other.mNative);
1281#else
1282 return {
1283 mNative[0] + other.mNative[0],
1284 mNative[1] + other.mNative[1],
1285 mNative[2] + other.mNative[2],
1286 mNative[3] + other.mNative[3]
1287 };
1288#endif
1289 }
1290
1292 {
1293#if defined(JPL_USE_SSE)
1294 mNative = _mm_add_epi32(mNative, other.mNative);
1295#elif defined(JPL_USE_NEON)
1296 mNative = vaddq_u32(mNative, other.mNative);
1297#else
1298 mNative[0] += other.mNative[0];
1299 mNative[1] += other.mNative[1];
1300 mNative[2] += other.mNative[2];
1301 mNative[3] += other.mNative[3];
1302#endif
1303 return *this;
1304 }
1305
1307 {
1308#if defined(JPL_USE_SSE)
1309 return _mm_sub_epi32(mNative, other.mNative);
1310#elif defined(JPL_USE_NEON)
1311 return vsubq_u32(mNative, other.mNative);
1312#else
1313 return simd_mask(
1314 mNative[0] - other.mNative[0],
1315 mNative[1] - other.mNative[1],
1316 mNative[2] - other.mNative[2],
1317 mNative[3] - other.mNative[3]
1318 );
1319#endif
1320 }
1321
1323 {
1324#if defined(JPL_USE_SSE)
1325
1326 mNative = _mm_sub_epi32(mNative, other.mNative);
1327#elif defined(JPL_USE_NEON)
1328 mNative = vsubq_u32(mNative, other.mNative);
1329#else
1330 mNative[0] -= other.mNative[0];
1331 mNative[1] -= other.mNative[1];
1332 mNative[2] -= other.mNative[2];
1333 mNative[3] -= other.mNative[3];
1334#endif
1335 return *this;
1336 }
1337
1338 JPL_INLINE simd_mask operator==(const simd& a, const simd& b) noexcept
1339 {
1340#if defined(JPL_USE_SSE)
1341 return _mm_castps_si128(_mm_cmpeq_ps(a.mNative, b.mNative));
1342#elif defined(JPL_USE_NEON)
1343 return vceqq_f32(a.mNative, b.mNative);
1344#else
1345 return {
1346 a.mNative[0] == b.mNative[0] ? cTrueValue : 0,
1347 a.mNative[1] == b.mNative[1] ? cTrueValue : 0,
1348 a.mNative[2] == b.mNative[2] ? cTrueValue : 0,
1349 a.mNative[3] == b.mNative[3] ? cTrueValue : 0
1350 };
1351#endif
1352 }
1353
1354 JPL_INLINE simd_mask operator<(const simd& a, const simd& b) noexcept
1355 {
1356#if defined(JPL_USE_SSE)
1357 return _mm_castps_si128(_mm_cmplt_ps(a.mNative, b.mNative));
1358#elif defined(JPL_USE_NEON)
1359 return vcltq_f32(a.mNative, b.mNative);
1360#else
1361 return {
1362 a.mNative[0] < b.mNative[0] ? cTrueValue : 0,
1363 a.mNative[1] < b.mNative[1] ? cTrueValue : 0,
1364 a.mNative[2] < b.mNative[2] ? cTrueValue : 0,
1365 a.mNative[3] < b.mNative[3] ? cTrueValue : 0
1366 };
1367#endif
1368 }
1369
1370 JPL_INLINE simd_mask operator<=(const simd& a, const simd& b) noexcept
1371 {
1372#if defined(JPL_USE_SSE)
1373 return _mm_castps_si128(_mm_cmple_ps(a.mNative, b.mNative));
1374#elif defined(JPL_USE_NEON)
1375 return vcleq_f32(a.mNative, b.mNative);
1376#else
1377 return {
1378 a.mNative[0] <= b.mNative[0] ? cTrueValue : 0,
1379 a.mNative[1] <= b.mNative[1] ? cTrueValue : 0,
1380 a.mNative[2] <= b.mNative[2] ? cTrueValue : 0,
1381 a.mNative[3] <= b.mNative[3] ? cTrueValue : 0
1382 };
1383#endif
1384 }
1385
1386 JPL_INLINE simd_mask operator>(const simd& a, const simd& b) noexcept
1387 {
1388#if defined(JPL_USE_SSE)
1389 return _mm_castps_si128(_mm_cmpgt_ps(a.mNative, b.mNative));
1390#elif defined(JPL_USE_NEON)
1391 return vcgtq_f32(a.mNative, b.mNative);
1392#else
1393 return {
1394 a.mNative[0] > b.mNative[0] ? cTrueValue : 0,
1395 a.mNative[1] > b.mNative[1] ? cTrueValue : 0,
1396 a.mNative[2] > b.mNative[2] ? cTrueValue : 0,
1397 a.mNative[3] > b.mNative[3] ? cTrueValue : 0
1398 };
1399#endif
1400 }
1401
1402 JPL_INLINE simd_mask operator>=(const simd& a, const simd& b) noexcept
1403 {
1404#if defined(JPL_USE_SSE)
1405 return _mm_castps_si128(_mm_cmpge_ps(a.mNative, b.mNative));
1406#elif defined(JPL_USE_NEON)
1407 return vcgeq_f32(a.mNative, b.mNative);
1408#else
1409 return {
1410 a.mNative[0] >= b.mNative[0] ? cTrueValue : 0,
1411 a.mNative[1] >= b.mNative[1] ? cTrueValue : 0,
1412 a.mNative[2] >= b.mNative[2] ? cTrueValue : 0,
1413 a.mNative[3] >= b.mNative[3] ? cTrueValue : 0
1414 };
1415#endif
1416 }
1417
1419 {
1420#if defined(JPL_USE_SSE)
1421 return _mm_cmpeq_epi32(a.mNative, b.mNative);
1422#elif defined(JPL_USE_NEON)
1423 return vceqq_u32(a.mNative, b.mNative);
1424#else
1425 return {
1426 a.mNative[0] == b.mNative[0] ? cTrueValue : 0,
1427 a.mNative[1] == b.mNative[1] ? cTrueValue : 0,
1428 a.mNative[2] == b.mNative[2] ? cTrueValue : 0,
1429 a.mNative[3] == b.mNative[3] ? cTrueValue : 0
1430 };
1431#endif
1432 }
1433
1435 {
1436#if defined(JPL_USE_AVX512)
1437 return _mm_ternarylogic_epi32(mNative, mNative, mNative, 0b01010101);
1438#elif defined(JPL_USE_SSE)
1439 return *this ^ simd_mask(0xffffffff);
1440#elif defined(JPL_USE_NEON)
1441 return vmvnq_u32(mNative);
1442#else
1443 return { ~mNative[0], ~mNative[1], ~mNative[2], ~mNative[3] };
1444#endif
1445 }
1446
1448 {
1449#if defined(JPL_USE_SSE)
1450 mNative = _mm_or_si128(mNative, other.mNative);
1451#elif defined(JPL_USE_NEON)
1452 mNative = vorrq_u32(mNative, other.mNative);
1453#else
1454 mNative[0] |= other.mNative[0];
1455 mNative[1] |= other.mNative[1];
1456 mNative[2] |= other.mNative[2];
1457 mNative[3] |= other.mNative[3];
1458#endif
1459 }
1460
1462 {
1463#if defined(JPL_USE_SSE)
1464 mNative = _mm_xor_si128(mNative, other.mNative);
1465#elif defined(JPL_USE_NEON)
1466 mNative = veorq_u32(mNative, other.mNative);
1467#else
1468 mNative[0] ^= other.mNative[0];
1469 mNative[1] ^= other.mNative[1];
1470 mNative[2] ^= other.mNative[2];
1471 mNative[3] ^= other.mNative[3];
1472#endif
1473 }
1474
1476 {
1477#if defined(JPL_USE_SSE)
1478 mNative = _mm_and_si128(mNative, other.mNative);
1479#elif defined(JPL_USE_NEON)
1480 mNative = vandq_u32(mNative, other.mNative);
1481#else
1482 mNative[0] &= other.mNative[0];
1483 mNative[1] &= other.mNative[1];
1484 mNative[2] &= other.mNative[2];
1485 mNative[3] &= other.mNative[3];
1486#endif
1487 }
1488
1490 {
1491 JPL_ASSERT(count <= 31);
1492#if defined(JPL_USE_SSE)
1493 __m128i c = _mm_cvtsi32_si128(static_cast<int>(count));
1494 return _mm_srl_epi32(mNative, c);
1495#elif defined(JPL_USE_NEON)
1496 int32x4_t sh = vdupq_n_s32(-static_cast<int32_t>(count));
1497 return vshlq_u32(mNative, sh);
1498#else
1499 return simd_mask(
1500 mNative[0] >> count,
1501 mNative[1] >> count,
1502 mNative[2] >> count,
1503 mNative[3] >> count
1504 );
1505#endif
1506 }
1507
1509 {
1510 JPL_ASSERT(count <= 31);
1511#if defined(JPL_USE_SSE)
1512 __m128i c = _mm_cvtsi32_si128(static_cast<int>(count));
1513 return _mm_sll_epi32(mNative, c);
1514#elif defined(JPL_USE_NEON)
1515 int32x4_t sh = vdupq_n_s32(static_cast<int32_t>(count));
1516 return vshlq_u32(mNative, sh);
1517#else
1518 return simd_mask(
1519 mNative[0] << count,
1520 mNative[1] << count,
1521 mNative[2] << count,
1522 mNative[3] << count
1523 );
1524#endif
1525 }
1526
1527 template<uint Count> requires(Count <= 31)
1529 {
1530#if defined(JPL_USE_SSE)
1531 return _mm_srli_epi32(mNative, Count);
1532#elif defined(JPL_USE_NEON)
1533 return vshrq_n_u32(mNative, Count);
1534#else
1535 return simd_mask(
1536 mNative[0] >> Count,
1537 mNative[1] >> Count,
1538 mNative[2] >> Count,
1539 mNative[3] >> Count
1540 );
1541#endif
1542 }
1543
1544 template<uint Count> requires(Count <= 31)
1546 {
1547#if defined(JPL_USE_SSE)
1548 return _mm_slli_epi32(mNative, Count);
1549#elif defined(JPL_USE_NEON)
1550 return vshlq_n_u32(mNative, Count);
1551#else
1552 return simd_mask(
1553 mNative[0] << Count,
1554 mNative[1] << Count,
1555 mNative[2] << Count,
1556 mNative[3] << Count
1557 );
1558#endif
1559 }
1560
1561 template<uint Count> requires(Count <= 31)
1563 {
1564#if defined(JPL_USE_SSE)
1565 return _mm_srai_epi32(mNative, Count);
1566#elif defined(JPL_USE_NEON)
1568#else
1569 return {
1570 static_cast<uint32>(static_cast<int32>(mNative[0]) >> Count),
1571 static_cast<uint32>(static_cast<int32>(mNative[1]) >> Count),
1572 static_cast<uint32>(static_cast<int32>(mNative[2]) >> Count),
1573 static_cast<uint32>(static_cast<int32>(mNative[3]) >> Count)
1574 };
1575#endif
1576 }
1577
1579 {
1580#if defined(JPL_USE_SSE)
1581 return _mm_add_epi32(mNative, other.mNative);
1582#elif defined(JPL_USE_NEON)
1584 vreinterpretq_s32_u32(mNative),
1586#else
1587 return simd_mask(mNative[0] + other.mNative[0],
1588 mNative[1] + other.mNative[1],
1589 mNative[2] + other.mNative[2],
1590 mNative[3] + other.mNative[3]);
1591#endif
1592 }
1593
1595 {
1596#if defined(JPL_USE_SSE)
1597 return _mm_sub_epi32(mNative, other.mNative);
1598#elif defined(JPL_USE_NEON)
1600 vreinterpretq_s32_u32(mNative),
1602#else
1603 return simd_mask(mNative[0] - other.mNative[0],
1604 mNative[1] - other.mNative[1],
1605 mNative[2] - other.mNative[2],
1606 mNative[3] - other.mNative[3]);
1607#endif
1608 }
1609
1611 {
1612#if defined(JPL_USE_SSE)
1613 return _mm_cvtepi32_ps(mNative);
1614#elif defined(JPL_USE_NEON)
1616 //return vcvtq_f32_u32(mNative);
1617#else
1618 return {
1619 static_cast<float>(mU32[0]),
1620 static_cast<float>(mU32[1]),
1621 static_cast<float>(mU32[2]),
1622 static_cast<float>(mU32[3])
1623 };
1624#endif
1625 }
1626
1628 {
1629#if defined(JPL_USE_SSE)
1630 return _mm_castsi128_ps(mNative);
1631#elif defined(JPL_USE_NEON)
1633#else
1634 return std::bit_cast<simd>(*this);
1635#endif
1636 }
1637
1638 JPL_INLINE simd_mask operator|(const simd_mask& a, const simd_mask& b) noexcept
1639 {
1640#if defined(JPL_USE_SSE)
1641 return _mm_or_si128(a.mNative, b.mNative);
1642#elif defined(JPL_USE_NEON)
1643 return vorrq_u32(a.mNative, b.mNative);
1644#else
1645 return {
1646 a.mNative[0] | b.mNative[0],
1647 a.mNative[1] | b.mNative[1],
1648 a.mNative[2] | b.mNative[2],
1649 a.mNative[3] | b.mNative[3]
1650 };
1651#endif
1652 }
1653
1654 JPL_INLINE simd_mask operator^(const simd_mask& a, const simd_mask& b) noexcept
1655 {
1656#if defined(JPL_USE_SSE)
1657 return _mm_xor_si128(a.mNative, b.mNative);
1658#elif defined(JPL_USE_NEON)
1659 return veorq_u32(a.mNative, b.mNative);
1660#else
1661 return {
1662 a.mNative[0] ^ b.mNative[0],
1663 a.mNative[1] ^ b.mNative[1],
1664 a.mNative[2] ^ b.mNative[2],
1665 a.mNative[3] ^ b.mNative[3]
1666 };
1667#endif
1668 }
1669
1670 JPL_INLINE simd_mask operator&(const simd_mask& a, const simd_mask& b) noexcept
1671 {
1672#if defined(JPL_USE_SSE)
1673 return _mm_and_si128(a.mNative, b.mNative);
1674#elif defined(JPL_USE_NEON)
1675 return vandq_u32(a.mNative, b.mNative);
1676#else
1677 return {
1678 a.mNative[0] & b.mNative[0],
1679 a.mNative[1] & b.mNative[1],
1680 a.mNative[2] & b.mNative[2],
1681 a.mNative[3] & b.mNative[3]
1682 };
1683#endif
1684 }
1685
1686 //==========================================================================
1687 namespace Math
1688 {
1689 JPL_INLINE simd Sqrt(const simd& vec) noexcept
1690 {
1691#if defined(JPL_USE_SSE)
1692 return _mm_sqrt_ps(vec.mNative);
1693#elif defined(JPL_USE_NEON)
1694 return vsqrtq_f32(vec.mNative);
1695#else
1696 return simd(std::sqrt(mNative[0]), std::sqrt(mNative[1]), std::sqrt(mNative[2]), std::sqrt(mNative[3]));
1697#endif
1698 }
1699
1700 // @param NR : number of Newton's refinements.
1701 // 0: estimate, 1: ~16b, 2: ~23-24b (near float)
1702 template<int NR> requires(NR > 0 && NR <= 2)
1704 {
1705 using Type = typename simd::Type;
1706
1707#if defined(JPL_USE_SSE)
1708
1709 Type y = _mm_rsqrt_ps(vec); // approx 1/sqrt(x)
1710
1711 auto newton = [](const Type& x, const Type& y)
1712 {
1713 static const Type half = _mm_set1_ps(0.5f);
1714 static const Type thal = _mm_set1_ps(1.5f);
1715#if defined(JPL_USE_FMADD)
1716 // y = y * (1.5 - 0.5*x*y*y)
1717 Type xy = _mm_mul_ps(x, y);
1718 Type xy2 = _mm_mul_ps(xy, y);
1719 Type term = _mm_fnmadd_ps(half, xy2, thal);
1720 return _mm_mul_ps(y, term);
1721#else
1722 Type y2 = _mm_mul_ps(y, y);
1724 return _mm_mul_ps(y, term);
1725#endif
1726 };
1727
1728 if constexpr (NR >= 1)
1729 {
1730 y = newton(vec, y);
1731 }
1732 if constexpr (NR >= 2)
1733 {
1734 y = newton(vec, y);
1735 }
1736
1737 return y;
1738#elif defined (JPL_USE_NEON)
1739 Type y = vrsqrteq_f32(vec); // approx 1/sqrt(x)
1740
1741 auto newton = [](const Type& x, const Type& y)
1742 {
1743#if 0
1744 // vrsqrtsq_f32(a, b) ~= (3 - a * b) / 2, here a = x * y, b = y
1745 Type y2 = vmulq_f32(y, y);
1746 Type xy2 = vmulq_f32(x, y2);
1747 Type term = vrsqrtsq_f32(xy2, y);
1748#else
1749 static const Type half = vdupq_n_f32(0.5f);
1750 static const Type thal = vdupq_n_f32(1.5f);
1751 Type xy = vmulq_f32(x, y);
1752 Type xy2 = vmulq_f32(xy, y);
1753 Type term = vfmsq_f32(thal, half, xy2);
1754#endif
1755 return vmulq_f32(y, term);
1756 };
1757
1758 if constexpr (NR >= 1)
1759 {
1760 y = newton(vec, y);
1761 }
1762 if constexpr (NR >= 2)
1763 {
1764 y = newton(vec, y);
1765 }
1766
1767 return y;
1768#else
1769 return simd(
1770 1.0f / std::sqrt(vec.mNative[0]),
1771 1.0f / std::sqrt(vec.mNative[1]),
1772 1.0f / std::sqrt(vec.mNative[2]),
1773 1.0f / std::sqrt(vec.mNative[3])
1774 );
1775#endif
1776 }
1777
1778 JPL_INLINE simd InvSqrt(const simd& vec) noexcept
1779 {
1780 return InvSqrtImpl<2>(vec);
1781 }
1782
1784 {
1785 return InvSqrtImpl<1>(vec);
1786 }
1787
1788 JPL_INLINE simd Sign2(const simd& vec) noexcept
1789 {
1790 return simd::select(vec < simd::c_0(), -simd::c_1(), simd::c_1());
1791 }
1792
1794 {
1795 return abs(vec) <= tolerance;
1796 }
1797 }
1798
1799 JPL_INLINE simd max(const simd& a, const simd& b) noexcept
1800 {
1801#if defined(JPL_USE_SSE)
1802 return _mm_max_ps(a.mNative, b.mNative);
1803#elif defined(JPL_USE_NEON)
1804 return vmaxq_f32(a.mNative, b.mNative);
1805#else
1806 return simd(std::max(a.mNative[0], b.mNative[0]),
1807 std::max(a.mNative[1], b.mNative[1]),
1808 std::max(a.mNative[2], b.mNative[2]),
1809 std::max(a.mNative[3], b.mNative[3]));
1810#endif
1811 }
1812
1813 JPL_INLINE simd min(const simd& a, const simd& b) noexcept
1814 {
1815#if defined(JPL_USE_SSE)
1816 return _mm_min_ps(a.mNative, b.mNative);
1817#elif defined(JPL_USE_NEON)
1818 return vminq_f32(a.mNative, b.mNative);
1819#else
1820 return simd(std::min(a.mNative[0], b.mNative[0]),
1821 std::min(a.mNative[1], b.mNative[1]),
1822 std::min(a.mNative[2], b.mNative[2]),
1823 std::min(a.mNative[3], b.mNative[3]));
1824#endif
1825 }
1826
1827 JPL_INLINE simd abs(const simd& vec) noexcept
1828 {
1829#if defined(JPL_USE_SSE)
1830 return _mm_and_ps(vec.mNative, simd_mask(0x7fffffff).as_simd()); // v & (inv_sign_mask)
1831#elif defined(JPL_USE_NEON)
1832 return vabsq_f32(vec.mNative);
1833#else
1834 return { abs(vec.mNative[0]), abs(vec.mNative[1]), abs(vec.mNative[2]), abs(vec.mNative[3]) };
1835#endif
1836 }
1837
1838 JPL_INLINE simd clamp(const simd& value, const simd& minV, const simd& maxV) noexcept
1839 {
1840 return max(min(value, maxV), minV);
1841 }
1842
1843 JPL_INLINE simd fma(const simd& mul1, const simd& mul2, const simd& addV) noexcept
1844 {
1845#if defined(JPL_USE_SSE)
1846#ifdef JPL_USE_FMADD
1848#else
1850#endif
1851#elif defined(JPL_USE_NEON)
1853#else
1854 return Vec3(mul1.mNative[0] * mul2.mNative[0] + addV.mNative[0],
1855 mul1.mNative[1] * mul2.mNative[1] + addV.mNative[1],
1856 mul1.mNative[2] * mul2.mNative[2] + addV.mNative[2],
1857 mul1.mNative[3] * mul2.mNative[3] + addV.mNative[3]);
1858#endif
1859 }
1860
1861 JPL_INLINE simd floor(const simd& vec) noexcept
1862 {
1863 // Error handling...
1864 const simd vec_abs = abs(vec);
1865 const simd_mask is_nan = vec != vec;
1866 const simd_mask is_inf = vec_abs == simd::inf();
1867 const simd_mask is_zero = vec_abs == simd::zero();
1868
1869#if defined (JPL_USE_SSE)
1870#if defined (JPL_USE_SSE4_1)
1872#else
1873 // trunc toward 0
1875 // floor = trunc - (trunc > v ? 1 : 0)
1876 simd mask = (truncated > vec).as_simd() & simd(1.0f);
1878#endif
1880#elif defined (JPL_USE_NEON)
1881 auto floored = vrndmq_f32(vec.mNative);
1883#else
1884 return {
1885 std::floorf(vec.mNative[0]),
1886 std::floorf(vec.mNative[1]),
1887 std::floorf(vec.mNative[2]),
1888 std::floorf(vec.mNative[3])
1889 };
1890#endif
1891 }
1892
1893 JPL_INLINE simd ceil(const simd& vec) noexcept
1894 {
1895 // Error handling...
1896 const simd vec_abs = abs(vec);
1897 const simd_mask is_nan = vec != vec;
1898 const simd_mask is_inf = vec_abs == simd::inf();
1899 const simd_mask is_zero = vec_abs == simd::zero();
1900
1901#if defined (JPL_USE_SSE)
1902 const simd_mask requires_rounding = vec_abs < simd(8'388'608.0f);
1903#if defined (JPL_USE_SSE4_1)
1905#else
1906 // avoid converting NaN, infinity, and large finite values to int32
1908 // trunc toward 0
1910 // ceil = trunc + (trunc < v ? 1 : 0)
1911 const simd increment = (truncated < vec).as_simd() & simd(1.0f);
1913 // restore the sign bit
1914 ceiled = ceiled | (vec & simd(-0.0f));
1915#endif
1917#elif defined (JPL_USE_NEON)
1918 auto ceiled = vrndpq_f32(vec.mNative);
1920#else
1921 return {
1922 std::ceilf(vec.mNative[0]),
1923 std::ceilf(vec.mNative[1]),
1924 std::ceilf(vec.mNative[2]),
1925 std::ceilf(vec.mNative[3])
1926 };
1927#endif
1928 }
1929
1930 JPL_INLINE simd round(const simd& vec) noexcept
1931 {
1932#if defined(JPL_USE_SSE4_1)
1934#elif defined(JPL_USE_SSE)
1935 // trunc(x + copysign(0.5, x))
1936 simd_mask sign = vec.as_mask() & simd_mask(0x80000000);
1937 return (vec + (simd(0.5f) | sign.as_simd())).to_mask().to_simd();
1938#elif defined(JPL_USE_NEON)
1939 return vrndnq_f32(vec.mNative);
1940#else
1941 return {
1942 std::roundf(vec.mNative[0]),
1943 std::roundf(vec.mNative[1]),
1944 std::roundf(vec.mNative[2]),
1945 std::roundf(vec.mNative[3])
1946 };
1947#endif
1948 }
1949
1951 {
1952#if defined(JPL_USE_SSE)
1953 return _mm_shuffle_ps(vec.mNative, vec.mNative, _MM_SHUFFLE(3, 1, 2, 0));
1954#elif defined(JPL_USE_NEON)
1956 return vcombine_f32(vecs.val[0], vecs.val[1]);
1957#else
1958 return { vec.mNative[0], vec.mNative[2], vec.mNative[1], vec.mNative[3] };
1959#endif
1960 }
1961
1962 JPL_INLINE simd combine_lo(const simd& a, const simd& b) noexcept
1963 {
1964#if defined(JPL_USE_SSE)
1965 return _mm_castsi128_ps(
1967 _mm_castps_si128(a.mNative),
1968 _mm_castps_si128(b.mNative)
1969 )
1970 );
1971#elif defined(JPL_USE_NEON)
1972 return vcombine_f32(vget_low_f32(a.mNative), vget_low_f32(b.mNative));
1973#else
1974 return { a.mNative[0], a.mNative[1], b.mNative[0], b.mNative[1] };
1975#endif
1976 }
1977
1978 JPL_INLINE simd combine_hi(const simd& a, const simd& b) noexcept
1979 {
1980#if defined(JPL_USE_SSE)
1981 return _mm_castsi128_ps(
1983 _mm_castps_si128(a.mNative),
1984 _mm_castps_si128(b.mNative)
1985 )
1986 );
1987#elif defined(JPL_USE_NEON)
1988 return vcombine_f32(vget_high_f32(a.mNative), vget_high_f32(b.mNative));
1989#else
1990 return { a.mNative[2], a.mNative[3], b.mNative[2], b.mNative[3] };
1991#endif
1992 }
1993
1994 JPL_INLINE simd combine_lohi(const simd& a, const simd& b) noexcept
1995 {
1996#if defined(JPL_USE_SSE)
1997 return _mm_shuffle_ps(a.mNative, b.mNative, _MM_SHUFFLE(3, 2, 1, 0));
1998#elif defined(JPL_USE_NEON)
1999 return vcombine_f32(vget_low_f32(a.mNative), vget_high_f32(b.mNative));
2000#else
2001 return { a.mNative[0], a.mNative[1], b.mNative[2], b.mNative[3] };
2002#endif
2003 }
2004
2005 JPL_INLINE simd reverse(const simd& vec) noexcept
2006 {
2007#if defined(JPL_USE_SSE)
2008 return _mm_shuffle_ps(vec.mNative, vec.mNative, _MM_SHUFFLE(0, 1, 2, 3));
2009#elif defined(JPL_USE_NEON)
2010 // { 0, 1, 2, 3 } -> { 1, 0, 3, 2 }
2012 // combine high part { 3, 2 } and low part { 1, 0 } -> {3, 2, 1, 0}
2014#else
2015 return { vec.mNative[3], vec.mNative[2], vec.mNative[1], vec.mNative[0] };
2016#endif
2017 }
2018
2019 //==========================================================================
2020 JPL_INLINE simd_mask max(const simd_mask& a, const simd_mask& b) noexcept
2021 {
2022#if defined(JPL_USE_SSE4_1)
2023 return _mm_max_epu32(a.mNative, b.mNative);
2024#elif defined(JPL_USE_SSE)
2025 // Shift range from unsigned[0, 2 ^ 32 - 1] to signed[-2 ^ 31, 2 ^ 31 - 1]
2026 __m128i offset = _mm_set1_epi32(0x80000000);
2029 // Perform signed comparison: 0xFFFFFFFF where a > b
2031 // Select: (mask ? a : b)
2032 // Use bitwise logic to pick the maximum
2034#elif defined(JPL_USE_NEON)
2035 return vmaxq_u32(a.mNative, b.mNative);
2036#else
2037 return simd_mask(std::max(a.mNative[0], b.mNative[0]),
2038 std::max(a.mNative[1], b.mNative[1]),
2039 std::max(a.mNative[2], b.mNative[2]),
2040 std::max(a.mNative[3], b.mNative[3]));
2041#endif
2042 }
2043
2044 JPL_INLINE simd_mask min(const simd_mask& a, const simd_mask& b) noexcept
2045 {
2046#if defined(JPL_USE_SSE4_1)
2047 return _mm_min_epu32(a.mNative, b.mNative);
2048#elif defined(JPL_USE_SSE)
2049 // Shift range from [0, 2^32-1] to [-2^31, 2^31-1] for signed comparison
2050 __m128i offset = _mm_set1_epi32(0x80000000);
2053 // Perform signed comparison
2054 __m128i mask = _mm_cmpgt_epi32(a_signed, b_signed); // 0xFFFFFFFF if a > b
2055 // Select results: (mask ? b : a)
2057#elif defined(JPL_USE_NEON)
2058 return vminq_u32(a.mNative, b.mNative);
2059#else
2060 return simd_mask(std::min(a.mNative[0], b.mNative[0]),
2061 std::min(a.mNative[1], b.mNative[1]),
2062 std::min(a.mNative[2], b.mNative[2]),
2063 std::min(a.mNative[3], b.mNative[3]));
2064#endif
2065 }
2066
2068 {
2069 return max(min(value, maxV), minV);
2070 }
2071
2072 JPL_INLINE simd_mask maxs(const simd_mask& a, const simd_mask& b) noexcept
2073 {
2074#if defined(JPL_USE_SSE4_1)
2075 return _mm_max_epi32(a.mNative, b.mNative);
2076#elif defined(JPL_USE_SSE) // SSE2 fallback
2077 typename simd_mask::Type gt = _mm_cmpgt_epi32(a.mNative, b.mNative); // a > b ? 0xFFFFFFFF : 0
2078 // max = a where a > b, else b
2081#elif defined(JPL_USE_NEON)
2083 vreinterpretq_s32_u32(a.mNative),
2084 vreinterpretq_s32_u32(b.mNative)));
2085#else
2086 return simd_mask(
2087 static_cast<uint32>(std::max(static_cast<int32>(a.mNative[0]), static_cast<int32>(b.mNative[0]))),
2088 static_cast<uint32>(std::max(static_cast<int32>(a.mNative[1]), static_cast<int32>(b.mNative[1]))),
2089 static_cast<uint32>(std::max(static_cast<int32>(a.mNative[2]), static_cast<int32>(b.mNative[2]))),
2090 static_cast<uint32>(std::max(static_cast<int32>(a.mNative[3]), static_cast<int32>(b.mNative[3]))));
2091#endif
2092 }
2093
2094 JPL_INLINE simd_mask mins(const simd_mask& a, const simd_mask& b) noexcept
2095 {
2096#if defined(JPL_USE_SSE4_1)
2097 return _mm_min_epi32(a.mNative, b.mNative);
2098#elif defined(JPL_USE_SSE) // SSE2 fallback: select with cmp
2099 typename simd_mask::Type gt = _mm_cmpgt_epi32(a.mNative, b.mNative); // a > b ? 0xFFFFFFFF : 0
2100 // min = b where a > b, else a
2103#elif defined(JPL_USE_NEON)
2105 vreinterpretq_s32_u32(a.mNative),
2106 vreinterpretq_s32_u32(b.mNative)));
2107#else
2108 return simd_mask(
2109 static_cast<uint32>(std::min(static_cast<int32>(a.mNative[0]), static_cast<int32>(b.mNative[0]))),
2110 static_cast<uint32>(std::min(static_cast<int32>(a.mNative[1]), static_cast<int32>(b.mNative[1]))),
2111 static_cast<uint32>(std::min(static_cast<int32>(a.mNative[2]), static_cast<int32>(b.mNative[2]))),
2112 static_cast<uint32>(std::min(static_cast<int32>(a.mNative[3]), static_cast<int32>(b.mNative[3]))));
2113#endif
2114 }
2115
2117 {
2118 return maxs(mins(value, maxV), minV);
2119 }
2120
2121} // namespace JPL
#define JPL_ASSERT(inExpression,...)
Main assert macro, usage: JPL_ASSERT(condition, message) or JPL_ASSERT(condition)
Definition ErrorReporting.h:76
JPL_INLINE constexpr T FMA(T a, T b, T c) noexcept
Inlined fuse multiply-add. Compiler in some circumstances is more eager to optimize this than std::fm...
Definition Math.h:186
JPL_INLINE constexpr T InvSqrt(T x) noexcept
Definition Math.h:283
JPL_INLINE constexpr T Lerp(const T &v0, const T &v1, T t) noexcept
Linearly interpolate v0 towards v1.
Definition Math.h:172
JPL_INLINE constexpr T Sqrt(T x) noexcept
Definition Math.h:269
JPL_INLINE constexpr bool IsNearlyZero(T value, T errorTolerance=JPL_FLOAT_EPS_V< T >) noexcept
Definition Math.h:146
JPL_INLINE simd InvSqrtImpl(const simd &vec) noexcept
Definition SIMD.h:1703
JPL_INLINE simd InvSqrtFast(const simd &vec) noexcept
Definition SIMD.h:1783
JPL_INLINE constexpr T Sign2(T value) noexcept
Sign2 returns -1 for negative values, 1 otherwise.
Definition Math.h:131
Definition AcousticMaterial.h:36
JPL_INLINE simd clamp(const simd &value, const simd &minV, const simd &maxV) noexcept
Element-wise clamp.
Definition SIMD.h:1838
JPL_INLINE simd_mask clamps(const simd_mask &value, const simd_mask &minV, const simd_mask &maxV) noexcept
Element-wise clamp, signed.
Definition SIMD.h:2116
JPL_INLINE simd_mask operator!=(const simd &a, const simd &b) noexcept
Definition SIMD.h:341
JPL_INLINE simd_mask mins(const simd_mask &a, const simd_mask &b) noexcept
Element-wise min, signed.
Definition SIMD.h:2094
std::int32_t int32
Definition Core.h:316
JPL_INLINE simd_mask operator>=(const simd &a, const simd &b) noexcept
Definition SIMD.h:1402
JPL_INLINE simd operator|(const simd &a, const simd &b) noexcept
Component-wise logical OR.
Definition SIMD.h:878
JPL_INLINE simd_mask operator<=(const simd &a, const simd &b) noexcept
Definition SIMD.h:1370
JPL_INLINE simd_mask operator<(const simd &a, const simd &b) noexcept
Definition SIMD.h:1354
JPL_INLINE simd combine_hi(const simd &a, const simd &b) noexcept
Combine two high lanes of input a and b as { a2, a3, b2, b3 }.
Definition SIMD.h:1978
JPL_INLINE constexpr Vec2 operator/(T Scale, const JPL::Vec2 &V) noexcept
Definition MinimalVec2.h:64
unsigned int uint
Definition Core.h:308
JPL_INLINE simd_mask maxs(const simd_mask &a, const simd_mask &b) noexcept
Element-wise max, signed.
Definition SIMD.h:2072
JPL_INLINE simd reverse(const simd &vec) noexcept
Reverse the order of the lanes.
Definition SIMD.h:2005
std::uint32_t uint32
Definition Core.h:311
JPL_INLINE simd operator^(const simd &a, const simd &b) noexcept
Component-wise logical XOR.
Definition SIMD.h:894
JPL_INLINE simd combine_lohi(const simd &a, const simd &b) noexcept
Combine two low lanes of a and two high lanes from b as { a0, a1, b2, b3 }.
Definition SIMD.h:1994
JPL_INLINE constexpr bool operator==(const Vec2 &A, const Vec2 &B) noexcept
Definition MinimalVec2.h:59
JPL_INLINE simd operator&(const simd &a, const simd &b) noexcept
Component-wise logical AND.
Definition SIMD.h:910
JPL_INLINE simd_mask operator>(const simd &a, const simd &b) noexcept
Definition SIMD.h:1386
JPL_INLINE simd round(const simd &vec) noexcept
Element-wise round to nearest integer value.
Definition SIMD.h:1930
JPL_INLINE simd floor(const simd &vec) noexcept
Element-wise floor.
Definition SIMD.h:1861
JPL_INLINE simd max(const simd &a, const simd &b) noexcept
Element-wise max.
Definition SIMD.h:1799
JPL_INLINE simd ceil(const simd &vec) noexcept
Element-wise ceil.
Definition SIMD.h:1893
std::uint8_t uint8
Definition Core.h:309
JPL_INLINE simd fma(const simd &mul1, const simd &mul2, const simd &addV) noexcept
Element-wise fused multiply-add.
Definition SIMD.h:1843
JPL_INLINE constexpr Vec2 operator*(T Scale, const JPL::Vec2 &V) noexcept
Definition MinimalVec2.h:62
JPL_INLINE simd interleave_lohi(const simd &vec) noexcept
Interleave two low lanes with the two high lanes (e.g. { 0, 1, 2, 3 } -> { 0, 2, 1,...
Definition SIMD.h:1950
JPL_INLINE simd min(const simd &a, const simd &b) noexcept
Element-wise min.
Definition SIMD.h:1813
JPL_INLINE simd abs(const simd &vec) noexcept
Definition SIMD.h:1827
JPL_INLINE simd combine_lo(const simd &a, const simd &b) noexcept
Combine two low lanes of input a and b as { a0, a1, b0, b1 }.
Definition SIMD.h:1962
Definition ChannelMap.h:268
Definition SIMD.h:207
JPL_INLINE void store(uint32 *mem) const
Store values from simd to provided memory location.
Definition SIMD.h:1143
JPL_INLINE simd_mask & operator-=(const simd_mask &other) noexcept
Component-wise subtract two simd_mask vectors.
Definition SIMD.h:1322
JPL_INLINE bool all_of() const noexcept
Test if all of the components are true.
Definition SIMD.h:1169
JPL_INLINE simd_mask operator*(const simd_mask &other) const noexcept
Component-wise multiplies each of the 4 integer components with an integer (discards any overflow)
Definition SIMD.h:1230
Type mNative
Definition SIMD.h:327
JPL_INLINE bool any_of() const noexcept
Test if any of the components are true.
Definition SIMD.h:1174
JPL_INLINE simd_mask ashr() const noexcept
Definition SIMD.h:1562
JPL_INLINE simd_mask & operator*=(const simd_mask &other) noexcept
Component-wise multiplies each of the 4 integer components with an integer (discards any overflow)
Definition SIMD.h:1253
JPL_INLINE simd_mask operator-(const simd_mask &other) const noexcept
Component-wise subtract two simd_mask vectors.
Definition SIMD.h:1306
friend std::ostream & operator<<(std::ostream &inStream, const simd_mask &vec)
Definition SIMD.h:319
JPL_INLINE void operator^=(const simd_mask &other) noexcept
Component-wise logical XOR.
Definition SIMD.h:1461
JPL_INLINE int reduce_count() const noexcept
Count the number of components that are true.
Definition SIMD.h:1184
JPL_INLINE int reduce_max_index() const noexcept
Get index of the last component that is true.
Definition SIMD.h:1202
simd_mask() noexcept=default
JPL_INLINE simd_mask operator+(const simd_mask &other) const noexcept
Component-wise add an integer value to all integer components (discards any overflow)
Definition SIMD.h:1275
std::array< uint32, 4 > Type
Definition SIMD.h:214
JPL_INLINE simd as_simd() const noexcept
Reinterpret simd_mask as a simd (doesn't change the bits)
Definition SIMD.h:1627
JPL_INLINE uint32 get_lane() const noexcept
Get float component by index known at compile-time.
Definition SIMD.h:1158
JPL_INLINE simd_mask shr() const noexcept
Definition SIMD.h:1528
JPL_INLINE int reduce_min_index() const noexcept
Get index of the first component that is true.
Definition SIMD.h:1195
JPL_INLINE simd_mask & operator+=(const simd_mask &other) noexcept
Component-wise add two integer vectors (component wise)
Definition SIMD.h:1291
JPL_INLINE simd_mask operator>>(const uint32 count) const noexcept
Definition SIMD.h:1489
static JPL_INLINE simd_mask zero() noexcept
Definition SIMD.h:1121
JPL_INLINE simd_mask subs(const simd_mask &other) const noexcept
Definition SIMD.h:1594
JPL_INLINE int GetTrues() const noexcept
Store if [0] is true in bit 0, [1] in bit 1, [2] in bit 2 and [3] in bit 3.
Definition SIMD.h:1209
JPL_INLINE void operator&=(const simd_mask &other) noexcept
Component-wise logical AND.
Definition SIMD.h:1475
JPL_INLINE void load(const uint32 *mem)
Load values from memory into simd, 'mem' should point to memory with at least size() number of uint32...
Definition SIMD.h:1132
JPL_INLINE simd to_simd() const noexcept
Convert each component from an int to a float.
Definition SIMD.h:1610
JPL_INLINE void operator|=(const simd_mask &other) noexcept
Component-wise logical OR.
Definition SIMD.h:1447
JPL_INLINE bool none_of() const noexcept
Test if none of the components are true.
Definition SIMD.h:1179
JPL_INLINE simd_mask adds(const simd_mask &other) const noexcept
Definition SIMD.h:1578
JPL_INLINE simd_mask shl() const noexcept
Definition SIMD.h:1545
static JPL_INLINE simd_mask replicate(int value) noexcept
Definition SIMD.h:1106
JPL_INLINE simd_mask operator~() const noexcept
Component-wise logical NOT.
Definition SIMD.h:1434
Minimal 4-wide 32-bit float vector implementation for SIMD.
Definition SIMD.h:60
static JPL_INLINE simd c_0() noexcept
Frequently used constants.
Definition SIMD.h:456
friend JPL_INLINE simd operator*(float value, const simd &other) noexcept
Multiply vector with float.
Definition SIMD.h:648
JPL_INLINE float operator[](uint32 index) const noexcept
Get float component by index.
Definition SIMD.h:587
static JPL_INLINE simd c_1() noexcept
Definition SIMD.h:461
JPL_INLINE simd_mask to_mask() const noexcept
Convert each component from a float to an int.
Definition SIMD.h:987
JPL_INLINE simd operator/(float value) const noexcept
Divide vector by float.
Definition SIMD.h:664
JPL_INLINE simd & operator-=(const simd &other) noexcept
Subtract two float vectors (component wise)
Definition SIMD.h:801
JPL_INLINE simd_mask as_mask() const noexcept
Reinterpret simd as a simd_mask (doesn't change the bits)
Definition SIMD.h:1023
Type mNative
Definition SIMD.h:201
JPL_INLINE simd splat() const
Replicate component at LaneIndex to all components.
Definition SIMD.h:927
simd() noexcept=default
JPL_INLINE void operator|=(const simd &other) noexcept
Component-wise logical OR.
Definition SIMD.h:830
JPL_INLINE void load(const float *mem)
Load values from memory into simd, 'mem' should point to memory with at least size() number of floats...
Definition SIMD.h:562
static JPL_INLINE simd c_0p5() noexcept
Definition SIMD.h:466
static JPL_INLINE simd reciprocal(const simd &vec) noexcept
Reciprocal vector (1 / value) for each of the components.
Definition SIMD.h:1058
JPL_INLINE simd & operator+=(const simd &other) noexcept
Add two float vectors (component wise)
Definition SIMD.h:748
static JPL_INLINE simd zero() noexcept
Vector with all zeros.
Definition SIMD.h:541
std::array< float, 4 > Type
Definition SIMD.h:67
JPL_INLINE float reduce_min() const noexcept
Returns min component.
Definition SIMD.h:970
static JPL_INLINE simd c_pi() noexcept
Definition SIMD.h:471
JPL_INLINE simd & operator*=(float value) noexcept
Multiply vector with float.
Definition SIMD.h:680
JPL_INLINE void operator^=(const simd &other) noexcept
Component-wise logical XOR.
Definition SIMD.h:846
static JPL_INLINE simd inf() noexcept
Definition SIMD.h:557
JPL_INLINE float reduce_max() const noexcept
Returns max component.
Definition SIMD.h:953
static JPL_INLINE simd select(const simd_mask &mask, const simd &a, const simd &b) noexcept
Component-wise select, returns 'a' if mask is true, 'b' otherwise.
Definition SIMD.h:1034
JPL_INLINE void operator&=(const simd &other) noexcept
Component-wise logical AND.
Definition SIMD.h:862
JPL_INLINE float reduce() const noexcept
Returns sum of all components.
Definition SIMD.h:938
JPL_INLINE simd operator-() const noexcept
Negate.
Definition SIMD.h:761
JPL_INLINE simd operator+(const simd &other) const noexcept
Add two float vectors (component wise)
Definition SIMD.h:732
JPL_INLINE float get_lane() const noexcept
Get float component by index known at compile-time.
Definition SIMD.h:610
JPL_INLINE simd & operator/=(float value) noexcept
Divide vector by float.
Definition SIMD.h:706
JPL_INLINE void store(float *mem) const
Store values from simd to provided memory location.
Definition SIMD.h:573
static JPL_INLINE simd nan() noexcept
Vector with all NaN's.
Definition SIMD.h:552
static JPL_INLINE simd gather(const float *base, const simd_mask &offsets)
Gather 4 floats from memory at base + offsets[i] * Scale.
Definition SIMD.h:515
JPL_INLINE bool operator()(const JPL::simd &a, const JPL::simd &b) const
Definition SIMD.h:445