ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
Loading...
Searching...
No Matches
dispatch.h File Reference

Go to the source code of this file.

Data Structures

struct  simd_backend_t
 simd backend keeps the values that move through the subsystem: name, required_features, luminance. Some members are pointers into caller- or subsystem-owned memory, so lifetime follows the API that populated them rather than the struct copy itself. More...

Typedefs

typedef void(* simd_luminance_fn) (const uint8_t *rgb, uint8_t *out, size_t count)

Functions

const simd_backend_t * simd_select_backend (uint32_t features)
const simd_backend_t * simd_backend (void)
void simd_luminance_scalar (const uint8_t *rgb, uint8_t *out, size_t count)
void simd_luminance_sse2 (const uint8_t *rgb, uint8_t *out, size_t count)
void simd_luminance_ssse3 (const uint8_t *rgb, uint8_t *out, size_t count)
void simd_luminance_avx2 (const uint8_t *rgb, uint8_t *out, size_t count)
void simd_luminance_neon (const uint8_t *rgb, uint8_t *out, size_t count)
void simd_luminance_sve (const uint8_t *rgb, uint8_t *out, size_t count)

Typedef Documentation

◆ simd_luminance_fn

typedef void(* simd_luminance_fn) (const uint8_t *rgb, uint8_t *out, size_t count)

RGB24 to rounded BT.601 luminance. Exactly count pixels are read/written.

Definition at line 8 of file dispatch.h.

Function Documentation

◆ simd_backend()

const simd_backend_t * simd_backend ( void )

Immutable dispatch table cached per thread; no startup constructors required.

Definition at line 36 of file dispatch.c.

36 {
37 static _Thread_local const simd_backend_t *selected;
38 if (!selected) {
39 uint32_t features = platform_cpu_features();
40 selected = simd_select_backend(features);
41 const char *requested = simd_backend_override();
42 if (requested && requested[0] && strcmp(requested, "auto") != 0) {
43 // A diagnostic override can never bypass the CPU/OS safety check.
44 selected = simd_select_backend(0);
45 for (size_t i = 0; i < sizeof(backends) / sizeof(backends[0]); i++) {
46 if (strcmp(requested, backends[i].name) == 0 &&
47 (features & backends[i].required_features) == backends[i].required_features) {
48 selected = &backends[i];
49 break;
50 }
51 }
52 }
53 }
54 return selected;
55}
const simd_backend_t * simd_select_backend(uint32_t features)
Definition dispatch.c:28
unsigned int uint32_t
Definition common.h:62
simd backend keeps the values that move through the subsystem: name, required_features,...
Definition dispatch.h:12

References simd_select_backend().

Referenced by main(), and simd_cells_backend().

◆ simd_luminance_avx2()

void simd_luminance_avx2 ( const uint8_t * rgb,
uint8_t * out,
size_t count )

Definition at line 17 of file avx2/luminance.c.

17 {
18 const rgb_pixel_t *row = (const rgb_pixel_t *)rgb;
19 size_t x = 0;
20 for (; width - x >= 32; x += 32) {
21 avx2_load_rgb32_optimized(row + x, avx2_r_buffer, avx2_g_buffer, avx2_b_buffer);
22 avx2_compute_luminance_32(avx2_r_buffer, avx2_g_buffer, avx2_b_buffer, avx2_luminance_buffer);
23 memcpy(out + x, avx2_luminance_buffer, 32);
24 }
25 simd_luminance_scalar((const uint8_t *)(row + x), out + x, width - x);
26}
void simd_luminance_scalar(const uint8_t *rgb, uint8_t *out, size_t count)
Definition dispatch.c:57
unsigned int width
Definition fft.c:23
unsigned char uint8_t
Definition common.h:60
static bool size_t gotel_query_t * out
Definition queries.h:8

References out, simd_luminance_scalar(), and width.

◆ simd_luminance_neon()

void simd_luminance_neon ( const uint8_t * rgb,
uint8_t * out,
size_t count )

Definition at line 6 of file neon/luminance.c.

6 {
7 const rgb_pixel_t *row = (const rgb_pixel_t *)rgb;
8 size_t x = 0;
9 for (; width - x >= 16; x += 16) {
10 // Load 16 RGB pixels (48 bytes)
11 uint8x16x3_t pixels = vld3q_u8((const uint8_t *)(row + x));
12
13 // Calculate luminance for all 16 pixels: (77*R + 150*G + 29*B + 128) >> 8
14 uint16x8_t luma_lo = vmull_u8(vget_low_u8(pixels.val[0]), vdup_n_u8(LUMA_RED)); // R * 77
15 luma_lo = vmlal_u8(luma_lo, vget_low_u8(pixels.val[1]), vdup_n_u8(LUMA_GREEN)); // + G * 150
16 luma_lo = vmlal_u8(luma_lo, vget_low_u8(pixels.val[2]), vdup_n_u8(LUMA_BLUE)); // + B * 29
17 luma_lo = vaddq_u16(luma_lo, vdupq_n_u16(128)); // + 128 (rounding)
18 luma_lo = vshrq_n_u16(luma_lo, 8); // >> 8
19
20 uint16x8_t luma_hi = vmull_u8(vget_high_u8(pixels.val[0]), vdup_n_u8(LUMA_RED));
21 luma_hi = vmlal_u8(luma_hi, vget_high_u8(pixels.val[1]), vdup_n_u8(LUMA_GREEN));
22 luma_hi = vmlal_u8(luma_hi, vget_high_u8(pixels.val[2]), vdup_n_u8(LUMA_BLUE));
23 luma_hi = vaddq_u16(luma_hi, vdupq_n_u16(128));
24 luma_hi = vshrq_n_u16(luma_hi, 8);
25
26 // Convert 16-bit luminance back to 8-bit
27 uint8x16_t luminance = vcombine_u8(vmovn_u16(luma_lo), vmovn_u16(luma_hi));
28
29 vst1q_u8(out + x, luminance);
30 }
31 simd_luminance_scalar((const uint8_t *)(row + x), out + x, width - x);
32}
#define LUMA_BLUE
Luminance blue coefficient (0.114 * 256 = 29).
#define LUMA_GREEN
Luminance green coefficient (0.587 * 256 = 150).
#define LUMA_RED
Luminance red coefficient (0.299 * 256 = 77).

References LUMA_BLUE, LUMA_GREEN, LUMA_RED, out, simd_luminance_scalar(), and width.

◆ simd_luminance_scalar()

void simd_luminance_scalar ( const uint8_t * rgb,
uint8_t * out,
size_t count )

Definition at line 57 of file dispatch.c.

57 {
58 for (size_t i = 0; i < count; i++)
59 out[i] = (uint8_t)((77u * rgb[3 * i] + 150u * rgb[3 * i + 1] + 29u * rgb[3 * i + 2] + 128u) >> 8);
60}

References out.

Referenced by simd_luminance_avx2(), simd_luminance_neon(), simd_luminance_sse2(), and simd_luminance_ssse3().

◆ simd_luminance_sse2()

void simd_luminance_sse2 ( const uint8_t * rgb,
uint8_t * out,
size_t count )

Definition at line 17 of file sse2/luminance.c.

17 {
18 const rgb_pixel_t *row = (const rgb_pixel_t *)rgb;
19 size_t x = 0;
20 for (; width - x >= 16; x += 16) {
21 // Manual deinterleave RGB components (SSE2 limitation vs NEON's vld3q_u8)
22 uint8_t r_array[16], g_array[16], b_array[16];
23 for (int j = 0; j < 16; j++) {
24 r_array[j] = row[x + j].r;
25 g_array[j] = row[x + j].g;
26 b_array[j] = row[x + j].b;
27 }
28
29 // Load full 16 bytes into SSE2 registers (process in two 8-pixel batches)
30 __m128i r_vec_lo = _mm_loadl_epi64((__m128i *)(r_array + 0)); // First 8 pixels
31 __m128i r_vec_hi = _mm_loadl_epi64((__m128i *)(r_array + 8)); // Second 8 pixels
32 __m128i g_vec_lo = _mm_loadl_epi64((__m128i *)(g_array + 0));
33 __m128i g_vec_hi = _mm_loadl_epi64((__m128i *)(g_array + 8));
34 __m128i b_vec_lo = _mm_loadl_epi64((__m128i *)(b_array + 0));
35 __m128i b_vec_hi = _mm_loadl_epi64((__m128i *)(b_array + 8));
36
37 // Process first 8 pixels
38 __m128i r_16_lo = _mm_unpacklo_epi8(r_vec_lo, _mm_setzero_si128());
39 __m128i g_16_lo = _mm_unpacklo_epi8(g_vec_lo, _mm_setzero_si128());
40 __m128i b_16_lo = _mm_unpacklo_epi8(b_vec_lo, _mm_setzero_si128());
41
42 __m128i luma_r_lo = _mm_mullo_epi16(r_16_lo, _mm_set1_epi16(LUMA_RED));
43 __m128i luma_g_lo = _mm_mullo_epi16(g_16_lo, _mm_set1_epi16(150));
44 __m128i luma_b_lo = _mm_mullo_epi16(b_16_lo, _mm_set1_epi16(29));
45
46 __m128i luma_sum_lo = _mm_add_epi16(luma_r_lo, luma_g_lo);
47 luma_sum_lo = _mm_add_epi16(luma_sum_lo, luma_b_lo);
48 luma_sum_lo = _mm_add_epi16(luma_sum_lo, _mm_set1_epi16(128));
49 luma_sum_lo = _mm_srli_epi16(luma_sum_lo, 8);
50
51 // Process second 8 pixels
52 __m128i r_16_hi = _mm_unpacklo_epi8(r_vec_hi, _mm_setzero_si128());
53 __m128i g_16_hi = _mm_unpacklo_epi8(g_vec_hi, _mm_setzero_si128());
54 __m128i b_16_hi = _mm_unpacklo_epi8(b_vec_hi, _mm_setzero_si128());
55
56 __m128i luma_r_hi = _mm_mullo_epi16(r_16_hi, _mm_set1_epi16(LUMA_RED));
57 __m128i luma_g_hi = _mm_mullo_epi16(g_16_hi, _mm_set1_epi16(150));
58 __m128i luma_b_hi = _mm_mullo_epi16(b_16_hi, _mm_set1_epi16(29));
59
60 __m128i luma_sum_hi = _mm_add_epi16(luma_r_hi, luma_g_hi);
61 luma_sum_hi = _mm_add_epi16(luma_sum_hi, luma_b_hi);
62 luma_sum_hi = _mm_add_epi16(luma_sum_hi, _mm_set1_epi16(128));
63 luma_sum_hi = _mm_srli_epi16(luma_sum_hi, 8);
64
65 // Pack both halves to 8-bit
66 __m128i luminance_lo = _mm_packus_epi16(luma_sum_lo, _mm_setzero_si128());
67 __m128i luminance_hi = _mm_packus_epi16(luma_sum_hi, _mm_setzero_si128());
68
69 // Store and convert to ASCII characters
70 uint8_t luma_array[16];
71 _mm_storel_epi64((__m128i *)(luma_array + 0), luminance_lo);
72 _mm_storel_epi64((__m128i *)(luma_array + 8), luminance_hi);
73
74 memcpy(out + x, luma_array, 16);
75 }
76 simd_luminance_scalar((const uint8_t *)(row + x), out + x, width - x);
77}
uint8_t b
Blue color component (0-255).
uint8_t g
Green color component (0-255).
uint8_t r
Red color component (0-255).

References rgb_pixel_t::b, rgb_pixel_t::g, LUMA_RED, out, rgb_pixel_t::r, simd_luminance_scalar(), and width.

◆ simd_luminance_ssse3()

void simd_luminance_ssse3 ( const uint8_t * rgb,
uint8_t * out,
size_t count )

Definition at line 5 of file ssse3/luminance.c.

5 {
6 const rgb_pixel_t *row = (const rgb_pixel_t *)rgb;
7 size_t x = 0;
8 for (; width - x >= 16; x += 16) {
9 // Manual deinterleave RGB components (SSSE3 limitation vs NEON's vld3q_u8)
10 uint8_t r_array[16], g_array[16], b_array[16];
11 for (int j = 0; j < 16; j++) {
12 r_array[j] = row[x + j].r;
13 g_array[j] = row[x + j].g;
14 b_array[j] = row[x + j].b;
15 }
16
17 // Process 16 pixels in two 8-pixel SSSE3 batches (same as SSE2 approach)
18 __m128i r_vec_lo = _mm_loadl_epi64((__m128i *)(r_array + 0));
19 __m128i r_vec_hi = _mm_loadl_epi64((__m128i *)(r_array + 8));
20 __m128i g_vec_lo = _mm_loadl_epi64((__m128i *)(g_array + 0));
21 __m128i g_vec_hi = _mm_loadl_epi64((__m128i *)(g_array + 8));
22 __m128i b_vec_lo = _mm_loadl_epi64((__m128i *)(b_array + 0));
23 __m128i b_vec_hi = _mm_loadl_epi64((__m128i *)(b_array + 8));
24
25 // Process first 8 pixels
26 __m128i r_16_lo = _mm_unpacklo_epi8(r_vec_lo, _mm_setzero_si128());
27 __m128i g_16_lo = _mm_unpacklo_epi8(g_vec_lo, _mm_setzero_si128());
28 __m128i b_16_lo = _mm_unpacklo_epi8(b_vec_lo, _mm_setzero_si128());
29
30 __m128i luma_r_lo = _mm_mullo_epi16(r_16_lo, _mm_set1_epi16(LUMA_RED));
31 __m128i luma_g_lo = _mm_mullo_epi16(g_16_lo, _mm_set1_epi16(LUMA_GREEN));
32 __m128i luma_b_lo = _mm_mullo_epi16(b_16_lo, _mm_set1_epi16(LUMA_BLUE));
33
34 __m128i luma_sum_lo = _mm_add_epi16(luma_r_lo, luma_g_lo);
35 luma_sum_lo = _mm_add_epi16(luma_sum_lo, luma_b_lo);
36 luma_sum_lo = _mm_add_epi16(luma_sum_lo, _mm_set1_epi16(LUMA_THRESHOLD));
37 luma_sum_lo = _mm_srli_epi16(luma_sum_lo, 8);
38
39 // Process second 8 pixels
40 __m128i r_16_hi = _mm_unpacklo_epi8(r_vec_hi, _mm_setzero_si128());
41 __m128i g_16_hi = _mm_unpacklo_epi8(g_vec_hi, _mm_setzero_si128());
42 __m128i b_16_hi = _mm_unpacklo_epi8(b_vec_hi, _mm_setzero_si128());
43
44 __m128i luma_r_hi = _mm_mullo_epi16(r_16_hi, _mm_set1_epi16(LUMA_RED));
45 __m128i luma_g_hi = _mm_mullo_epi16(g_16_hi, _mm_set1_epi16(LUMA_GREEN));
46 __m128i luma_b_hi = _mm_mullo_epi16(b_16_hi, _mm_set1_epi16(LUMA_BLUE));
47
48 __m128i luma_sum_hi = _mm_add_epi16(luma_r_hi, luma_g_hi);
49 luma_sum_hi = _mm_add_epi16(luma_sum_hi, luma_b_hi);
50 luma_sum_hi = _mm_add_epi16(luma_sum_hi, _mm_set1_epi16(LUMA_THRESHOLD));
51 luma_sum_hi = _mm_srli_epi16(luma_sum_hi, 8);
52
53 // Pack and store
54 __m128i luminance_lo = _mm_packus_epi16(luma_sum_lo, _mm_setzero_si128());
55 __m128i luminance_hi = _mm_packus_epi16(luma_sum_hi, _mm_setzero_si128());
56
57 uint8_t luma_array[16];
58 _mm_storel_epi64((__m128i *)(luma_array + 0), luminance_lo);
59 _mm_storel_epi64((__m128i *)(luma_array + 8), luminance_hi);
60 memcpy(out + x, luma_array, 16);
61 }
62 simd_luminance_scalar((const uint8_t *)(row + x), out + x, width - x);
63}
#define LUMA_THRESHOLD
Luminance threshold for rounding.

References rgb_pixel_t::b, rgb_pixel_t::g, LUMA_BLUE, LUMA_GREEN, LUMA_RED, LUMA_THRESHOLD, out, rgb_pixel_t::r, simd_luminance_scalar(), and width.

◆ simd_luminance_sve()

void simd_luminance_sve ( const uint8_t * rgb,
uint8_t * out,
size_t count )

Definition at line 17 of file sve/luminance.c.

17 {
18 const rgb_pixel_t *row = (const rgb_pixel_t *)rgb;
19 size_t x = 0;
20 while (x < width) {
21 // Widening consumes half the byte lanes; predicate the active halfword lanes.
22 size_t process_count = width - x < svcnth() ? width - x : svcnth();
23 svbool_t pg_active = svwhilelt_b8((uint64_t)0, (uint64_t)process_count);
24 // Retain the renderer's channel scratch layout before widening arithmetic.
25 uint8_t r_array[256], g_array[256], b_array[256]; // Max SVE vector size
26 for (int j = 0; j < process_count; j++) {
27 if (x + j < width) {
28 r_array[j] = row[x + j].r;
29 g_array[j] = row[x + j].g;
30 b_array[j] = row[x + j].b;
31 }
32 }
33
34 // Load into SVE vectors
35 svuint8_t r_vec = svld1_u8(pg_active, r_array);
36 svuint8_t g_vec = svld1_u8(pg_active, g_array);
37 svuint8_t b_vec = svld1_u8(pg_active, b_array);
38
39 // Convert to 16-bit for arithmetic
40 svuint16_t r_16 = svunpklo_u16(r_vec);
41 svuint16_t g_16 = svunpklo_u16(g_vec);
42 svuint16_t b_16 = svunpklo_u16(b_vec);
43
44 // Calculate luminance: (77*R + 150*G + 29*B + 128) >> 8
45 svuint16_t luma = svmul_n_u16_x(svptrue_b16(), r_16, LUMA_RED);
46 luma = svmla_n_u16_x(svptrue_b16(), luma, g_16, LUMA_GREEN);
47 luma = svmla_n_u16_x(svptrue_b16(), luma, b_16, LUMA_BLUE);
48 luma = svadd_n_u16_x(svptrue_b16(), luma, LUMA_THRESHOLD);
49 luma = svlsr_n_u16_x(svptrue_b16(), luma, 8);
50
51 // Store u16 luminance values (SVE1 compatible - no SVE2 narrowing intrinsics)
52 // After right-shift by 8, values are already in 0-255 range
53 uint16_t luma_temp[256];
54 svst1_u16(svptrue_b16(), luma_temp, luma);
55
56 // Convert to u8 array for ASCII lookup
57 uint8_t luma_array[256];
58 for (int j = 0; j < process_count; j++) {
59 luma_array[j] = (uint8_t)luma_temp[j];
60 }
61
62 memcpy(out + x, luma_array, process_count);
63 x += process_count;
64 }
65}
unsigned short uint16_t
Definition common.h:61
unsigned long long uint64_t
Definition common.h:63

References rgb_pixel_t::b, rgb_pixel_t::g, LUMA_BLUE, LUMA_GREEN, LUMA_RED, LUMA_THRESHOLD, out, rgb_pixel_t::r, and width.

◆ simd_select_backend()

const simd_backend_t * simd_select_backend ( uint32_t features)

Select only from compiled implementations whose requirements are satisfied.

Definition at line 28 of file dispatch.c.

28 {
29 for (size_t i = 0; i < sizeof(backends) / sizeof(backends[0]); i++) {
30 if ((features & backends[i].required_features) == backends[i].required_features)
31 return &backends[i];
32 }
33 return &backends[sizeof(backends) / sizeof(backends[0]) - 1];
34}

Referenced by simd_backend().