Skip to content

Commit bcbc497

Browse files
authored
Even KISS-er access to intrinsics (#214)
An even simpler implementation of the concept from #212, thanks to the review feedback by @DJMcNab Alternative to #108 and #196 Part of #166
1 parent 5c29a11 commit bcbc497

4 files changed

Lines changed: 345 additions & 49 deletions

File tree

fearless_simd/examples/play.rs

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -30,26 +30,10 @@ fn foo<S: Simd>(simd: S, x: f32) -> f32 {
3030
simd.splat_f32x4(x).sqrt()[0]
3131
}
3232

33-
// currently requires `safe_wrappers` feature
34-
fn do_something_on_neon(_level: Level) -> f32 {
35-
#[cfg(all(feature = "safe_wrappers", target_arch = "aarch64"))]
36-
if let Some(neon) = _level.as_neon() {
37-
return neon.vectorize(
38-
#[inline(always)]
39-
|| {
40-
let v = neon.neon.vdupq_n_f32(42.0);
41-
neon.neon.vgetq_lane_f32::<0>(v)
42-
},
43-
);
44-
}
45-
0.0
46-
}
47-
4833
fn main() {
4934
let level = Level::new();
5035
let x = level.dispatch(Foo);
5136
let y = dispatch!(level, simd => foo(simd, 42.0));
52-
let z = do_something_on_neon(level);
5337

54-
println!("level = {level:?}, x = {x}, y = {y}, z = {z}");
38+
println!("level = {level:?}, x = {x}, y = {y}");
5539
}

fearless_simd/examples/srgb.rs

Lines changed: 47 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,68 @@
11
// Copyright 2024 the Fearless_SIMD Authors
22
// SPDX-License-Identifier: Apache-2.0 OR MIT
33

4-
#![expect(
5-
missing_docs,
6-
reason = "TODO: https://github.com/linebender/fearless_simd/issues/40"
7-
)]
4+
//! Converts a single RGBA pixel from linear RGB to sRGB.
5+
//!
6+
//! This example demonstrates the usual Fearless SIMD structure:
7+
//!
8+
//! - write the main computation as an `#[inline(always)]` function generic over
9+
//! [`Simd`];
10+
//! - use [`dispatch!`] at the non-SIMD boundary to run it with the best
11+
//! available target features;
12+
//! - drop down to [`kernel!`](fearless_simd::kernel) when a small part of the
13+
//! computation needs a target-specific intrinsic.
14+
//!
15+
//! The RGB channels are converted with portable SIMD operations. The alpha
16+
//! channel is copied unchanged, using an architecture-specific lane-copy
17+
//! intrinsic if one is available and a scalar fallback otherwise.
818
919
use fearless_simd::{Level, dispatch, f32x4, prelude::*};
1020

11-
// This block shows how to use safe wrappers for compile-time enforcement
12-
// of using valid SIMD intrinsics.
13-
#[cfg(feature = "safe_wrappers")]
14-
#[inline(always)]
15-
fn copy_alpha<S: Simd>(a: f32x4<S>, b: f32x4<S>) -> f32x4<S> {
16-
// #[cfg(target_arch = "x86_64")]
17-
// if let Some(avx2) = a.simd.level().as_avx2() {
18-
// return avx2
19-
// .sse4_1
20-
// ._mm_blend_ps::<8>(a.into(), b.into())
21-
// .simd_into(a.simd);
22-
// }
23-
#[cfg(target_arch = "aarch64")]
24-
if let Some(neon) = a.simd.level().as_neon() {
25-
return neon
26-
.neon
27-
.vcopyq_laneq_f32::<3, 3>(a.into(), b.into())
28-
.simd_into(a.simd);
21+
#[cfg(target_arch = "aarch64")]
22+
use core::arch::aarch64::{float32x4_t, vcopyq_laneq_f32};
23+
#[cfg(target_arch = "x86")]
24+
use core::arch::x86::{__m128, _mm_blend_ps};
25+
#[cfg(target_arch = "x86_64")]
26+
use core::arch::x86_64::{__m128, _mm_blend_ps};
27+
28+
fearless_simd::kernel! {
29+
/// Copy the alpha lane on AArch64 using a NEON lane-copy intrinsic.
30+
#[inline]
31+
fn copy_alpha_neon(neon: Neon, a: float32x4_t, b: float32x4_t) -> float32x4_t {
32+
vcopyq_laneq_f32::<3, 3>(a, b)
2933
}
30-
let mut result = a;
31-
result[3] = b[3];
32-
result
3334
}
3435

35-
// This block lets the example compile without safe wrappers.
36-
#[cfg(not(feature = "safe_wrappers"))]
36+
fearless_simd::kernel! {
37+
/// Copy the alpha lane on x86 using the SSE4.2 token to enable SSE4.1 blend instructions.
38+
#[inline]
39+
fn copy_alpha_sse4_2(sse4_2: Sse4_2, a: __m128, b: __m128) -> __m128 {
40+
_mm_blend_ps::<8>(a, b)
41+
}
42+
}
43+
44+
/// Return `a` with its alpha channel replaced by `b`'s alpha channel.
45+
///
46+
/// This helper shows how portable SIMD code can opportunistically call
47+
/// target-specific kernels while still providing a fallback for every backend.
3748
#[inline(always)]
3849
fn copy_alpha<S: Simd>(a: f32x4<S>, b: f32x4<S>) -> f32x4<S> {
50+
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
51+
if let Some(sse4_2) = a.simd.level().as_sse4_2() {
52+
return copy_alpha_sse4_2(sse4_2, a.into(), b.into()).simd_into(a.simd);
53+
}
54+
3955
#[cfg(target_arch = "aarch64")]
40-
if let Some(_neon) = a.simd.level().as_neon() {
41-
unsafe {
42-
return core::arch::aarch64::vcopyq_laneq_f32::<3, 3>(a.into(), b.into())
43-
.simd_into(a.simd);
44-
}
56+
if let Some(neon) = a.simd.level().as_neon() {
57+
return copy_alpha_neon(neon, a.into(), b.into()).simd_into(a.simd);
4558
}
59+
4660
let mut result = a;
4761
result[3] = b[3];
4862
result
4963
}
5064

65+
/// Approximate the linear-RGB to sRGB transfer curve for RGB, preserving alpha.
5166
#[inline(always)]
5267
fn to_srgb<S: Simd>(simd: S, rgba: [f32; 4]) -> [f32; 4] {
5368
let v: f32x4<S> = rgba.simd_into(simd);

0 commit comments

Comments
 (0)