|
1 | 1 | // Copyright 2024 the Fearless_SIMD Authors |
2 | 2 | // SPDX-License-Identifier: Apache-2.0 OR MIT |
3 | 3 |
|
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. |
8 | 18 |
|
9 | 19 | use fearless_simd::{Level, dispatch, f32x4, prelude::*}; |
10 | 20 |
|
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) |
29 | 33 | } |
30 | | - let mut result = a; |
31 | | - result[3] = b[3]; |
32 | | - result |
33 | 34 | } |
34 | 35 |
|
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. |
37 | 48 | #[inline(always)] |
38 | 49 | 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 | + |
39 | 55 | #[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); |
45 | 58 | } |
| 59 | + |
46 | 60 | let mut result = a; |
47 | 61 | result[3] = b[3]; |
48 | 62 | result |
49 | 63 | } |
50 | 64 |
|
| 65 | +/// Approximate the linear-RGB to sRGB transfer curve for RGB, preserving alpha. |
51 | 66 | #[inline(always)] |
52 | 67 | fn to_srgb<S: Simd>(simd: S, rgba: [f32; 4]) -> [f32; 4] { |
53 | 68 | let v: f32x4<S> = rgba.simd_into(simd); |
|
0 commit comments